Merge changes Idc4ff798,Ia1dd90fe

* changes:
  Update allwpilib and CTRE Phoenix6 libraries
  Squashed 'third_party/allwpilib/' changes from 83f1860047..f1a82828fe
diff --git a/WORKSPACE b/WORKSPACE
index e60524a..aa68099 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -478,9 +478,9 @@
 http_archive(
     name = "allwpilib_ni_libraries",
     build_file = "@//debian:ni-libraries.BUILD",
-    sha256 = "c5d03ce5ed3807d9c4a5d415d8123d9ab3479498428eb0f1d77e74891f107aa0",
-    strip_prefix = "ni-libraries-2023.3.0",
-    url = "https://github.com/wpilibsuite/ni-libraries/archive/refs/tags/v2023.3.0.zip",
+    sha256 = "b325099ed896d1325c9cd998f07053b4e60e28a65d991f86f28203bf6728eccd",
+    strip_prefix = "ni-libraries-2024.1.1",
+    url = "https://github.com/wpilibsuite/ni-libraries/archive/refs/tags/v2024.1.1.zip",
 )
 
 # For protobuf. Don't use these.
@@ -697,14 +697,14 @@
 cc_library(
     name = 'api-cpp',
     visibility = ['//visibility:public'],
-    hdrs = glob(['ctre/phoenix6/**/*.hpp', 'units/*.h']),
+    hdrs = glob(['ctre/phoenix6/**/*.hpp']),
     includes = ["."],
     deps = ["@//third_party/allwpilib/wpimath"],
 )
 """,
-    sha256 = "84b5b18ab6a7e10b88dd7b3c11a903830d90666dcfe99bb6eb7faeb0eb5645f6",
+    sha256 = "dd2de00f2ac6be5db36bf0d280c9548ac97deb872a078f9790fbfb7f0e2251ad",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/api-cpp/23.10.0-alpha-1/api-cpp-23.10.0-alpha-1-headers.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/api-cpp/24.0.0-beta-3/api-cpp-24.0.0-beta-3-headers.zip",
     ],
 )
 
@@ -726,9 +726,9 @@
     target_compatible_with = ['@//tools/platforms/hardware:roborio'],
 )
 """,
-    sha256 = "a7646f9d1078fcafef21ddd02a82bc5b5de74dab4c02e6081ec7bab379dc5cdd",
+    sha256 = "1f9cc3479ccb2747292522120fe9fc1c2505595ca16f8f32d4a77704e126183b",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/api-cpp/23.10.0-alpha-1/api-cpp-23.10.0-alpha-1-linuxathena.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/api-cpp/24.0.0-beta-3/api-cpp-24.0.0-beta-3-linuxathena.zip",
     ],
 )
 
@@ -741,9 +741,9 @@
     hdrs = glob(['ctre/**/*.h', 'ctre/**/*.hpp']),
 )
 """,
-    sha256 = "fe09ec0e093114dcf45795c497e35073c66ab4d2ecb427f5940ce05e24dcc498",
+    sha256 = "2b97f575210261566468c11ced919b86245927322bd8435f14582f2da18285ac",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/tools/23.10.0-alpha-1/tools-23.10.0-alpha-1-headers.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/tools/24.0.0-beta-3/tools-24.0.0-beta-3-headers.zip",
     ],
 )
 
@@ -765,9 +765,9 @@
     target_compatible_with = ['@//tools/platforms/hardware:roborio'],
 )
 """,
-    sha256 = "3a2fa4b4c6ce221d3f65daebaa9e8fc0e4ff025971fc6a2e8d386809ca0f11c7",
+    sha256 = "527ff6aa042b4e61c2d9511cc6908f9e85fb2418c20c8805ff62bffa0dc8336f",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/tools/23.10.0-alpha-1/tools-23.10.0-alpha-1-linuxathena.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/tools/24.0.0-beta-3/tools-24.0.0-beta-3-linuxathena.zip",
     ],
 )
 
@@ -780,9 +780,9 @@
     hdrs = glob(['ctre/phoenix/**/*.h']),
 )
 """,
-    sha256 = "0f38d570949a4e8833aa6ab5a9fa0caf232344d96674d1e4ae342c63a47bdf2a",
+    sha256 = "d902ccd756b49e5aa152904f98fa9a31bc7508be8bf0ec7f978d16e33c760828",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenix/api-cpp/5.30.4/api-cpp-5.30.4-headers.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix/api-cpp/5.32.0-beta-1/api-cpp-5.32.0-beta-1-headers.zip",
     ],
 )
 
@@ -804,9 +804,9 @@
     target_compatible_with = ['@//tools/platforms/hardware:roborio'],
 )
 """,
-    sha256 = "1ba6c3a17a644bb7f9643faf5ba6cc6d20e43991fbfffb58c8f0d3e780f3a2bc",
+    sha256 = "882741ba5cf28881425393be33f3b7d1f564995a87c614c3f3b189ac2941c2dc",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenix/api-cpp/5.30.4/api-cpp-5.30.4-linuxathena.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix/api-cpp/5.32.0-beta-1/api-cpp-5.32.0-beta-1-linuxathena.zip",
     ],
 )
 
@@ -819,9 +819,9 @@
     hdrs = glob(['ctre/phoenix/**/*.h']),
 )
 """,
-    sha256 = "c6be4d8472dabe57889ca14deee22487a6ae964f7e21ad4b7adfa2d524980614",
+    sha256 = "e751e319ebcc337d8ab538027fb424cda03e1a80e10c94de07c980e8a0ec0bee",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenix/cci/5.30.4/cci-5.30.4-headers.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix/cci/5.32.0-beta-1/cci-5.32.0-beta-1-headers.zip",
     ],
 )
 
@@ -843,9 +843,9 @@
     target_compatible_with = ['@//tools/platforms/hardware:roborio'],
 )
 """,
-    sha256 = "e4f31ac2a08360f2d5061cdf4d288f95379f2286fcd6736def384723d2d69f24",
+    sha256 = "b9c23b25ebeec0acb4063424ee7685b9e1ddecd3b31c84c353342d22228e33b5",
     urls = [
-        "https://maven.ctr-electronics.com/release/com/ctre/phoenix/cci/5.30.4/cci-5.30.4-linuxathena.zip",
+        "https://maven.ctr-electronics.com/release/com/ctre/phoenix/cci/5.32.0-beta-1/cci-5.32.0-beta-1-linuxathena.zip",
     ],
 )
 
diff --git a/debian/ni-libraries.BUILD b/debian/ni-libraries.BUILD
index e93895c..18e811f 100644
--- a/debian/ni-libraries.BUILD
+++ b/debian/ni-libraries.BUILD
@@ -8,7 +8,7 @@
 )
 
 cc_binary(
-    name = "libnirio_emb_can.so.21",
+    name = "libnirio_emb_can.so.23",
     srcs = [
         "src/shims/embcan/main.c",
     ],
@@ -20,10 +20,10 @@
     name = "ni-libraries",
     srcs = [
         "libNiFpgaLv.so.13",
-        "libnirio_emb_can.so.21",
-        "src/lib/chipobject/libRoboRIO_FRC_ChipObject.so.23.0.0",
-        "src/lib/netcomm/libFRC_NetworkCommunication.so.23.0.0",
-        "src/lib/visa/libvisa.so.22.5.0",
+        "libnirio_emb_can.so.23",
+        "src/lib/chipobject/libRoboRIO_FRC_ChipObject.so.24.0.0",
+        "src/lib/netcomm/libFRC_NetworkCommunication.so.24.0.0",
+        "src/lib/visa/libvisa.so.23.3.0",
     ],
     hdrs = glob(["src/include/**"]),
     includes = [
diff --git a/frc971/wpilib/ahal/PWM.cc b/frc971/wpilib/ahal/PWM.cc
index 3c42bc4..72219aa 100644
--- a/frc971/wpilib/ahal/PWM.cc
+++ b/frc971/wpilib/ahal/PWM.cc
@@ -103,8 +103,9 @@
 void PWM::SetBounds(double max, double deadbandMax, double center,
                     double deadbandMin, double min) {
   int32_t status = 0;
-  HAL_SetPWMConfig(m_handle, max, deadbandMax, center, deadbandMin, min,
-                   &status);
+  HAL_SetPWMConfigMicroseconds(m_handle, max * 1000.0, deadbandMax * 1000.0,
+                               center * 1000.0, deadbandMin * 1000.0,
+                               min * 1000.0, &status);
   HAL_CHECK_STATUS(status);
 }
 
@@ -124,8 +125,9 @@
 void PWM::SetRawBounds(int max, int deadbandMax, int center, int deadbandMin,
                        int min) {
   int32_t status = 0;
-  HAL_SetPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min,
-                      &status);
+  HAL_SetPWMConfigMicroseconds(m_handle, max * 1000, deadbandMax * 1000,
+                               center * 1000, deadbandMin * 1000, min * 1000,
+                               &status);
   HAL_CHECK_STATUS(status);
 }
 
@@ -136,6 +138,8 @@
  * controller. The values determine the upper and lower speeds as well as the
  * deadband bracket.
  *
+ * Values in microseconds.
+ *
  * @param max         The Minimum pwm value
  * @param deadbandMax The high end of the deadband range
  * @param center      The center speed (off)
@@ -145,8 +149,8 @@
 void PWM::GetRawBounds(int *max, int *deadbandMax, int *center,
                        int *deadbandMin, int *min) {
   int32_t status = 0;
-  HAL_GetPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min,
-                      &status);
+  HAL_GetPWMConfigMicroseconds(m_handle, max, deadbandMax, center, deadbandMin,
+                               min, &status);
   HAL_CHECK_STATUS(status);
 }
 
@@ -230,7 +234,7 @@
  */
 void PWM::SetRaw(uint16_t value) {
   int32_t status = 0;
-  HAL_SetPWMRaw(m_handle, value, &status);
+  HAL_SetPWMPulseTimeMicroseconds(m_handle, value, &status);
   HAL_CHECK_STATUS(status);
 }
 
@@ -243,7 +247,7 @@
  */
 uint16_t PWM::GetRaw() const {
   int32_t status = 0;
-  uint16_t value = HAL_GetPWMRaw(m_handle, &status);
+  uint16_t value = HAL_GetPWMPulseTimeMicroseconds(m_handle, &status);
   HAL_CHECK_STATUS(status);
 
   return value;
diff --git a/third_party/allwpilib/.clang-format b/third_party/allwpilib/.clang-format
index fef62d1..f69bef0 100644
--- a/third_party/allwpilib/.clang-format
+++ b/third_party/allwpilib/.clang-format
@@ -1,111 +1,171 @@
 ---
+Language:        Cpp
 BasedOnStyle:  Google
 AccessModifierOffset: -1
 AlignAfterOpenBracket: Align
-AlignConsecutiveMacros: false
-AlignConsecutiveAssignments: false
-AlignConsecutiveDeclarations: false
+AlignArrayOfStructures: None
+AlignConsecutiveAssignments:
+  Enabled:         false
+  AcrossEmptyLines: false
+  AcrossComments:  false
+  AlignCompound:   false
+  PadOperators:    true
+AlignConsecutiveBitFields:
+  Enabled:         false
+  AcrossEmptyLines: false
+  AcrossComments:  false
+  AlignCompound:   false
+  PadOperators:    false
+AlignConsecutiveDeclarations:
+  Enabled:         false
+  AcrossEmptyLines: false
+  AcrossComments:  false
+  AlignCompound:   false
+  PadOperators:    false
+AlignConsecutiveMacros:
+  Enabled:         false
+  AcrossEmptyLines: false
+  AcrossComments:  false
+  AlignCompound:   false
+  PadOperators:    false
 AlignEscapedNewlines: Left
-AlignOperands:   true
-AlignTrailingComments: true
+AlignOperands:   Align
+AlignTrailingComments:
+  Kind:            Always
+  OverEmptyLines:  0
 AllowAllArgumentsOnNextLine: true
-AllowAllConstructorInitializersOnNextLine: true
 AllowAllParametersOfDeclarationOnNextLine: true
 AllowShortBlocksOnASingleLine: Never
 AllowShortCaseLabelsOnASingleLine: false
+AllowShortEnumsOnASingleLine: true
 AllowShortFunctionsOnASingleLine: Inline
-AllowShortLambdasOnASingleLine: All
 AllowShortIfStatementsOnASingleLine: Never
+AllowShortLambdasOnASingleLine: All
 AllowShortLoopsOnASingleLine: false
 AlwaysBreakAfterDefinitionReturnType: None
 AlwaysBreakAfterReturnType: None
 AlwaysBreakBeforeMultilineStrings: true
 AlwaysBreakTemplateDeclarations: Yes
+AttributeMacros:
+  - __capability
 BinPackArguments: true
 BinPackParameters: true
+BitFieldColonSpacing: Both
 BraceWrapping:
   AfterCaseLabel:  false
   AfterClass:      false
-  AfterControlStatement: false
+  AfterControlStatement: Never
   AfterEnum:       false
+  AfterExternBlock: false
   AfterFunction:   false
   AfterNamespace:  false
   AfterObjCDeclaration: false
   AfterStruct:     false
   AfterUnion:      false
-  AfterExternBlock: false
   BeforeCatch:     false
   BeforeElse:      false
+  BeforeLambdaBody: false
+  BeforeWhile:     false
   IndentBraces:    false
   SplitEmptyFunction: true
   SplitEmptyRecord: true
   SplitEmptyNamespace: true
-BreakBeforeBinaryOperators: None
-BreakBeforeBraces: Attach
-BreakBeforeInheritanceComma: false
-BreakInheritanceList: BeforeColon
-BreakBeforeTernaryOperators: true
-BreakConstructorInitializersBeforeComma: false
-BreakConstructorInitializers: BeforeColon
+BreakAfterAttributes: Always
 BreakAfterJavaFieldAnnotations: false
+BreakArrays:     true
+BreakBeforeBinaryOperators: None
+BreakBeforeConceptDeclarations: Always
+BreakBeforeBraces: Attach
+BreakBeforeInlineASMColon: OnlyMultiline
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeColon
+BreakInheritanceList: BeforeColon
 BreakStringLiterals: true
 ColumnLimit:     80
 CommentPragmas:  '^ IWYU pragma:'
 CompactNamespaces: false
-ConstructorInitializerAllOnOneLineOrOnePerLine: true
 ConstructorInitializerIndentWidth: 4
 ContinuationIndentWidth: 4
 Cpp11BracedListStyle: true
-DeriveLineEnding: true
 DerivePointerAlignment: false
 DisableFormat:   false
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: LogicalBlock
 ExperimentalAutoDetectBinPacking: false
 FixNamespaceComments: true
 ForEachMacros:
   - foreach
   - Q_FOREACH
   - BOOST_FOREACH
+IfMacros:
+  - KJ_IF_MAYBE
 IncludeBlocks:   Regroup
 IncludeCategories:
   - Regex:           '^<ext/.*\.h>'
     Priority:        2
     SortPriority:    0
+    CaseSensitive:   false
   - Regex:           '^<.*\.h>'
     Priority:        1
     SortPriority:    0
+    CaseSensitive:   false
   - Regex:           '^<.*'
     Priority:        2
     SortPriority:    0
+    CaseSensitive:   false
   - Regex:           '.*'
     Priority:        3
     SortPriority:    0
+    CaseSensitive:   false
 IncludeIsMainRegex: '([-_](test|unittest))?$'
 IncludeIsMainSourceRegex: ''
+IndentAccessModifiers: false
+IndentCaseBlocks: false
 IndentCaseLabels: true
+IndentExternBlock: AfterExternBlock
 IndentGotoLabels: true
 IndentPPDirectives: None
+IndentRequiresClause: true
 IndentWidth:     2
 IndentWrappedFunctionNames: false
+InsertBraces:    false
+InsertNewlineAtEOF: false
+InsertTrailingCommas: None
+IntegerLiteralSeparator:
+  Binary:          0
+  BinaryMinDigits: 0
+  Decimal:         0
+  DecimalMinDigits: 0
+  Hex:             0
+  HexMinDigits:    0
 JavaScriptQuotes: Leave
 JavaScriptWrapImports: true
 KeepEmptyLinesAtTheStartOfBlocks: false
+LambdaBodyIndentation: Signature
+LineEnding:      DeriveLF
 MacroBlockBegin: ''
 MacroBlockEnd:   ''
 MaxEmptyLinesToKeep: 1
 NamespaceIndentation: None
 ObjCBinPackProtocolList: Never
 ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
 ObjCSpaceAfterProperty: false
 ObjCSpaceBeforeProtocolList: true
+PackConstructorInitializers: NextLine
 PenaltyBreakAssignment: 2
 PenaltyBreakBeforeFirstCallParameter: 1
 PenaltyBreakComment: 300
 PenaltyBreakFirstLessLess: 120
+PenaltyBreakOpenParenthesis: 0
 PenaltyBreakString: 1000
 PenaltyBreakTemplateDeclaration: 10
 PenaltyExcessCharacter: 1000000
+PenaltyIndentedWhitespace: 0
 PenaltyReturnTypeOnItsOwnLine: 200
 PointerAlignment: Left
+PPIndentWidth:   -1
+QualifierAlignment: Leave
 RawStringFormats:
   - Language:        Cpp
     Delimiters:
@@ -132,34 +192,65 @@
       - PARSE_TEXT_PROTO
       - ParseTextOrDie
       - ParseTextProtoOrDie
-    CanonicalDelimiter: ''
+      - ParseTestProto
+      - ParsePartialTestProto
+    CanonicalDelimiter: pb
     BasedOnStyle:    google
+ReferenceAlignment: Pointer
 ReflowComments:  true
+RemoveBracesLLVM: false
+RemoveSemicolon: false
+RequiresClausePosition: OwnLine
+RequiresExpressionIndentation: OuterScope
+SeparateDefinitionBlocks: Leave
+ShortNamespaceLines: 1
 SortIncludes:    false
-SortUsingDeclarations: true
+SortJavaStaticImport: Before
+SortUsingDeclarations: LexicographicNumeric
 SpaceAfterCStyleCast: false
 SpaceAfterLogicalNot: false
 SpaceAfterTemplateKeyword: true
+SpaceAroundPointerQualifiers: Default
 SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
 SpaceBeforeCpp11BracedList: false
 SpaceBeforeCtorInitializerColon: true
 SpaceBeforeInheritanceColon: true
 SpaceBeforeParens: ControlStatements
+SpaceBeforeParensOptions:
+  AfterControlStatements: true
+  AfterForeachMacros: true
+  AfterFunctionDefinitionName: false
+  AfterFunctionDeclarationName: false
+  AfterIfMacros:   true
+  AfterOverloadedOperator: false
+  AfterRequiresInClause: false
+  AfterRequiresInExpression: false
+  BeforeNonEmptyParentheses: false
 SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
 SpaceInEmptyBlock: false
 SpaceInEmptyParentheses: false
 SpacesBeforeTrailingComments: 2
-SpacesInAngles:  false
+SpacesInAngles:  Never
 SpacesInConditionalStatement: false
 SpacesInContainerLiterals: true
 SpacesInCStyleCastParentheses: false
+SpacesInLineCommentPrefix:
+  Minimum:         1
+  Maximum:         -1
 SpacesInParentheses: false
 SpacesInSquareBrackets: false
-SpaceBeforeSquareBrackets: false
-Standard:        c++17
+Standard:        c++20
 StatementMacros:
   - Q_UNUSED
   - QT_REQUIRE_VERSION
 TabWidth:        8
 UseTab:          Never
+WhitespaceSensitiveMacros:
+  - BOOST_PP_STRINGIZE
+  - CF_SWIFT_NAME
+  - NS_SWIFT_NAME
+  - PP_STRINGIZE
+  - STRINGIZE
 ...
diff --git a/third_party/allwpilib/.clang-tidy b/third_party/allwpilib/.clang-tidy
index 62783c0..6f1ae3a 100644
--- a/third_party/allwpilib/.clang-tidy
+++ b/third_party/allwpilib/.clang-tidy
@@ -35,7 +35,6 @@
   bugprone-unhandled-self-assignment,
   bugprone-unused-raii,
   bugprone-virtual-near-miss,
-  cert-dcl58-cpp,
   cert-err52-cpp,
   cert-err60-cpp,
   cert-mem57-cpp,
diff --git a/third_party/allwpilib/.gitattributes b/third_party/allwpilib/.gitattributes
index d8c3d83..ae2ea0e 100644
--- a/third_party/allwpilib/.gitattributes
+++ b/third_party/allwpilib/.gitattributes
@@ -1,4 +1,5 @@
 *.gradle text eol=lf
 *.java text eol=lf
+*.json text eol=lf
 *.md text eol=lf
 *.xml text eol=lf
diff --git a/third_party/allwpilib/.github/CODEOWNERS b/third_party/allwpilib/.github/CODEOWNERS
index 3b6f7a1..3413dae 100644
--- a/third_party/allwpilib/.github/CODEOWNERS
+++ b/third_party/allwpilib/.github/CODEOWNERS
@@ -29,8 +29,6 @@
 
 /wpilibNewCommands/  @wpilibsuite/commandbased
 
-/wpilibOldCommands/  @wpilibsuite/commandbased
-
 /wpilibcExamples/  @wpilibsuite/wpilib @wpilibsuite/documentation
 
 /wpilibjExamples/  @wpilibsuite/wpilib @wpilibsuite/documentation
diff --git a/third_party/allwpilib/.github/ISSUE_TEMPLATE/bug_report.md b/third_party/allwpilib/.github/ISSUE_TEMPLATE/bug_report.md
index bc502f2..de6ae41 100644
--- a/third_party/allwpilib/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/third_party/allwpilib/.github/ISSUE_TEMPLATE/bug_report.md
@@ -15,6 +15,8 @@
 1. ...
 2. ...
 
+ - Link to code:
+
 **Expected behavior**
 A clear and concise description of what you expected to happen.
 
@@ -22,10 +24,8 @@
 If applicable, add screenshots to help explain your problem.
 
 **Desktop (please complete the following information):**
- - WPILib Version: [e.g. 2021.3.1]
  - OS: [e.g. Windows 11]
- - Java version [e.g. 1.10.2]
- - C++ version [e.g. 17]
+ - Project Information: [In Visual Studio Code, press the WPILib button and choose WPILib: Open Project Information. Press the copy button and paste the data here. If not using VS Code, please include WPILib version, Gradle version, Java version, C++ version (if applicable), and any third party libraries and versions]
 
 **Additional context**
 Add any other context about the problem here.
diff --git a/third_party/allwpilib/.github/workflows/cmake.yml b/third_party/allwpilib/.github/workflows/cmake.yml
index f465a16..963dc75 100644
--- a/third_party/allwpilib/.github/workflows/cmake.yml
+++ b/third_party/allwpilib/.github/workflows/cmake.yml
@@ -14,25 +14,28 @@
         include:
           - os: ubuntu-22.04
             name: Linux
-            container: wpilib/roborio-cross-ubuntu:2023-22.04
-            flags: ""
-          - os: macOS-11
+            container: wpilib/roborio-cross-ubuntu:2024-22.04
+            flags: "-DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
+          - os: macOS-12
             name: macOS
             container: ""
-            flags: "-DWITH_JAVA=OFF"
+            env: "PATH=\"/usr/local/opt/protobuf@3/bin:$PATH\""
+            flags: "-DCMAKE_BUILD_TYPE=Release -DWITH_JAVA=OFF -DWITH_EXAMPLES=ON -DCMAKE_LIBRARY_PATH=/usr/local/opt/protobuf@3/lib -DProtobuf_INCLUDE_DIR=/usr/local/opt/protobuf@3/include -DProtobuf_PROTOC_EXECUTABLE=/usr/local/opt/protobuf@3/bin/protoc"
 
     name: "Build - ${{ matrix.name }}"
     runs-on: ${{ matrix.os }}
     container: ${{ matrix.container }}
     steps:
-      - uses: actions/checkout@v3
-
       - name: Install dependencies (Linux)
         if: runner.os == 'Linux'
-        run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3
+        run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3 libprotobuf-dev protobuf-compiler ninja-build
+
+      - name: Install QuickBuffers (Linux)
+        if: runner.os == 'Linux'
+        run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.2/protoc-gen-quickbuf_1.3.2_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.2_amd64.deb
 
       - name: Install opencv (macOS)
-        run: brew install opencv
+        run: brew install opencv protobuf@3 ninja
         if: runner.os == 'macOS'
 
       - name: Set up Python 3.8 (macOS)
@@ -41,16 +44,79 @@
         with:
           python-version: 3.8
 
+      - name: Run sccache-cache
+        uses: mozilla-actions/sccache-action@v0.0.3
+
       - name: Install jinja
         run: python -m pip install jinja2
 
+      - uses: actions/checkout@v3
+
       - name: configure
-        run: mkdir build && cd build && cmake ${{ matrix.flags }} ..
+        run: cmake -S . -B build -G "Ninja" -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache ${{ matrix.flags }}
+        env:
+          SCCACHE_GHA_ENABLED: "true"
 
       - name: build
         working-directory: build
         run: cmake --build . --parallel $(nproc)
+        env:
+          SCCACHE_GHA_ENABLED: "true"
 
       - name: test
         working-directory: build
         run: ctest --output-on-failure
+
+  build-windows:
+    name: "Build - Windows"
+    runs-on: windows-2022
+    steps:
+      - uses: ilammy/msvc-dev-cmd@v1
+
+      - name: Install CMake
+        uses: lukka/get-cmake@v3.27.6
+
+      - name: Run sccache-cache
+        uses: mozilla-actions/sccache-action@v0.0.3
+
+      - name: Install jinja
+        run: python -m pip install jinja2
+
+      - uses: actions/checkout@v3
+
+      - name: Run vcpkg
+        uses: lukka/run-vcpkg@v11.1
+        with:
+          vcpkgDirectory: ${{ runner.workspace }}/vcpkg
+          vcpkgGitCommitId: 78b61582c9e093fda56a01ebb654be15a0033897 # HEAD on 2023-08-6
+
+      - name: configure
+        run: cmake -S . -B build -G "Ninja" -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_BUILD_TYPE=Release -DWITH_JAVA=OFF -DWITH_EXAMPLES=ON -DUSE_SYSTEM_FMTLIB=ON -DUSE_SYSTEM_LIBUV=ON -DUSE_SYSTEM_EIGEN=ON -DCMAKE_TOOLCHAIN_FILE=${{ runner.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_INSTALL_OPTIONS=--clean-after-build -DVCPKG_TARGET_TRIPLET=x64-windows-release -DVCPKG_HOST_TRIPLET=x64-windows-release
+        env:
+          SCCACHE_GHA_ENABLED: "true"
+
+      # Build wpiutil at full speed, wpimath depends on wpiutil
+      - name: build wpiutil
+        working-directory: build
+        run: cmake --build . --parallel $(nproc) --target wpiutil/all
+        env:
+          SCCACHE_GHA_ENABLED: "true"
+
+      # Build wpimath slow to prevent OOM
+      - name: build wpimath
+        working-directory: build
+        run: cmake --build . --parallel 1 --target wpimath/all
+        env:
+          SCCACHE_GHA_ENABLED: "true"
+
+      # Build everything else fast
+      - name: build
+        working-directory: build
+        run: cmake --build . --parallel $(nproc)
+        env:
+          SCCACHE_GHA_ENABLED: "true"
+
+      - name: test
+        working-directory: build
+        # UnitTest_test segfaults on exit occasionally
+        run: ctest --output-on-failure -E 'UnitTest'
diff --git a/third_party/allwpilib/.github/workflows/comment-command.yml b/third_party/allwpilib/.github/workflows/comment-command.yml
index f58ed18..77ebae9 100644
--- a/third_party/allwpilib/.github/workflows/comment-command.yml
+++ b/third_party/allwpilib/.github/workflows/comment-command.yml
@@ -9,11 +9,11 @@
     runs-on: ubuntu-22.04
     steps:
       - name: React Rocket
-        uses: actions/github-script@v4
+        uses: actions/github-script@v6
         with:
           script: |
             const {owner, repo} = context.issue
-            github.reactions.createForIssueComment({
+            github.rest.reactions.createForIssueComment({
               owner,
               repo,
               comment_id: context.payload.comment.id,
@@ -34,7 +34,7 @@
           GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
           NUMBER: ${{ github.event.issue.number }}
       - name: Set up Python 3.8
-        uses: actions/setup-python@v2
+        uses: actions/setup-python@v4
         with:
           python-version: 3.8
       - name: Setup Java
@@ -42,16 +42,10 @@
         with:
           distribution: 'zulu'
           java-version: 11
-      - name: Install clang-format
-        run: |
-          wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
-          sudo sh -c "echo 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-14 main' >> /etc/apt/sources.list.d/proposed-repositories.list"
-          sudo apt-get update -q
-          sudo apt-get install -y clang-format-14
       - name: Install wpiformat
         run: pip3 install wpiformat
       - name: Run wpiformat
-        run: wpiformat -clang 14
+        run: wpiformat
       - name: Run spotlessApply
         run: ./gradlew spotlessApply
       - name: Commit
diff --git a/third_party/allwpilib/.github/workflows/documentation.yml b/third_party/allwpilib/.github/workflows/documentation.yml
index c963383..83d74da 100644
--- a/third_party/allwpilib/.github/workflows/documentation.yml
+++ b/third_party/allwpilib/.github/workflows/documentation.yml
@@ -26,39 +26,41 @@
           java-version: 13
       - name: Set environment variables (Development)
         run: |
-          echo "TARGET_FOLDER=$BASE_PATH/development" >> $GITHUB_ENV
+          echo "BRANCH=development" >> $GITHUB_ENV
         if: github.ref == 'refs/heads/main'
       - name: Set environment variables (Tag)
         run: |
           echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
-          echo "TARGET_FOLDER=$BASE_PATH/beta" >> $GITHUB_ENV
+          echo "BRANCH=beta" >> $GITHUB_ENV
         if: startsWith(github.ref, 'refs/tags/v')
       - name: Set environment variables (Release)
         run: |
           echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
-          echo "TARGET_FOLDER=$BASE_PATH/release" >> $GITHUB_ENV
+          echo "BRANCH=release" >> $GITHUB_ENV
         if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta')
       - name: Build with Gradle
         run: ./gradlew docs:generateJavaDocs docs:doxygen -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
       - name: Install SSH Client 🔑
-        uses: webfactory/ssh-agent@v0.7.0
+        uses: webfactory/ssh-agent@v0.8.0
         with:
           ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
-      - name: Deploy Java 🚀
-        uses: JamesIves/github-pages-deploy-action@3.7.1
+      - name: Deploy 🚀
+        uses: JamesIves/github-pages-deploy-action@v4.4.1
         with:
-          SSH: true
-          REPOSITORY_NAME: wpilibsuite/wpilibsuite.github.io
-          BRANCH: main
-          CLEAN: true
-          FOLDER: docs/build/docs/javadoc
-          TARGET_FOLDER: ${{ env.TARGET_FOLDER }}/java
-      - name: Deploy C++ 🚀
-        uses: JamesIves/github-pages-deploy-action@3.7.1
+          ssh-key: true
+          repository-name: wpilibsuite/wpilibsuite.github.io
+          branch: allwpilib-${{ env.BRANCH }}
+          clean: true
+          single-commit: true
+          folder: docs/build/docs
+      - name: Trigger Workflow
+        uses: actions/github-script@v6
         with:
-          SSH: true
-          REPOSITORY_NAME: wpilibsuite/wpilibsuite.github.io
-          BRANCH: main
-          CLEAN: true
-          FOLDER: docs/build/docs/doxygen/html
-          TARGET_FOLDER: ${{ env.TARGET_FOLDER }}/cpp
+          github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
+          script: |
+            github.rest.actions.createWorkflowDispatch({
+              owner: context.repo.owner,
+              repo: 'wpilibsuite.github.io',
+              workflow_id: 'static.yml',
+              ref: 'main',
+            })
diff --git a/third_party/allwpilib/.github/workflows/gradle.yml b/third_party/allwpilib/.github/workflows/gradle.yml
index f603a50..3af54fb 100644
--- a/third_party/allwpilib/.github/workflows/gradle.yml
+++ b/third_party/allwpilib/.github/workflows/gradle.yml
@@ -12,7 +12,7 @@
       fail-fast: false
       matrix:
         include:
-          - container: wpilib/roborio-cross-ubuntu:2023-22.04
+          - container: wpilib/roborio-cross-ubuntu:2024-22.04
             artifact-name: Athena
             build-options: "-Ponlylinuxathena"
           - container: wpilib/raspbian-cross-ubuntu:bullseye-22.04
@@ -26,8 +26,17 @@
             build-options: "-Ponlylinuxx86-64"
     name: "Build - ${{ matrix.artifact-name }}"
     runs-on: ubuntu-22.04
-    container: ${{ matrix.container }}
     steps:
+      - name: Free Disk Space
+        uses: jlumbroso/free-disk-space@main
+        with:
+          tool-cache: false
+          android: true
+          dotnet: true
+          haskell: true
+          large-packages: false
+          docker-images: false
+          swap-storage: false
       - uses: actions/checkout@v3
         with:
           fetch-depth: 0
@@ -35,10 +44,16 @@
         run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
         if: startsWith(github.ref, 'refs/tags/v')
       - name: Build with Gradle
-        run: ./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
+        uses: addnab/docker-run-action@v3
+        with:
+          image: ${{ matrix.container }}
+          options: -v ${{ github.workspace }}:/work -w /work -e ARTIFACTORY_PUBLISH_USERNAME -e ARTIFACTORY_PUBLISH_PASSWORD -e GITHUB_REF -e CI
+          run: df . && rm -f semicolon_delimited_script && echo $GITHUB_REF && ./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
         env:
           ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
           ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
+      - name: Check free disk space
+        run: df .
       - uses: actions/upload-artifact@v3
         with:
           name: ${{ matrix.artifact-name }}
@@ -46,7 +61,7 @@
 
   build-host:
     env:
-      MACOSX_DEPLOYMENT_TARGET: 10.15
+      MACOSX_DEPLOYMENT_TARGET: 11
     strategy:
       fail-fast: false
       matrix:
@@ -57,22 +72,40 @@
             task: "build"
             build-options: "-PciDebugOnly --max-workers 1"
             outputs: "build/allOutputs"
+            build-dir: "c:\\work"
           - os: windows-2022
             artifact-name: Win64Release
             architecture: x64
             build-options: "-PciReleaseOnly --max-workers 1"
             task: "copyAllOutputs"
             outputs: "build/allOutputs"
-          - os: macOS-11
+            build-dir: "c:\\work"
+          - os: windows-2022
+            artifact-name: WinArm64Debug
+            architecture: x64
+            task: "build"
+            build-options: "-PciDebugOnly -Pbuildwinarm64 -Ponlywindowsarm64 --max-workers 1"
+            outputs: "build/allOutputs"
+            build-dir: "c:\\work"
+          - os: windows-2022
+            artifact-name: WinArm64Release
+            architecture: x64
+            build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64 --max-workers 1"
+            task: "copyAllOutputs"
+            outputs: "build/allOutputs"
+            build-dir: "c:\\work"
+          - os: macOS-12
             artifact-name: macOS
             architecture: x64
             task: "build"
             outputs: "build/allOutputs"
+            build-dir: "."
           - os: windows-2022
             artifact-name: Win32
             architecture: x86
             task: ":ntcoreffi:build"
             outputs: "ntcoreffi/build/outputs"
+            build-dir: "c:\\work"
     name: "Build - ${{ matrix.artifact-name }}"
     runs-on: ${{ matrix.os }}
     steps:
@@ -105,20 +138,31 @@
       - name: Set Java Heap Size
         run: sed -i 's/-Xmx2g/-Xmx1g/g' gradle.properties
         if: matrix.artifact-name == 'Win32'
+      - name: Configure build directory (Windows)
+        run: xcopy . ${{ matrix.build-dir }} /i /s /e /h /q
+        if: matrix.os == 'windows-2022'
+      - name: Check disk free space (Windows)
+        run: wmic logicaldisk get caption, freespace
+        if: matrix.os == 'windows-2022'
       - name: Build with Gradle
         run: ./gradlew ${{ matrix.task }} --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
+        working-directory: ${{ matrix.build-dir }}
         env:
           ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
           ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
       - name: Sign Libraries with Developer ID
         run: ./gradlew copyAllOutputs --build-cache -PbuildServer -PskipJavaFormat -PdeveloperID=${{ secrets.APPLE_DEVELOPER_ID }} ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
+        working-directory: ${{ matrix.build-dir }}
         if: |
           matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' &&
           (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
+      - name: Check disk free space (Windows)
+        run: wmic logicaldisk get caption, freespace
+        if: matrix.os == 'windows-2022'
       - uses: actions/upload-artifact@v3
         with:
           name: ${{ matrix.artifact-name }}
-          path: ${{ matrix.outputs }}
+          path: ${{ matrix.build-dir }}/${{ matrix.outputs }}
 
   build-documentation:
     name: "Build - Documentation"
@@ -149,28 +193,51 @@
     needs: [build-docker, build-host, build-documentation]
     runs-on: ubuntu-22.04
     steps:
+      - name: Free Disk Space
+        if: |
+          github.repository_owner == 'wpilibsuite' &&
+          (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
+        uses: jlumbroso/free-disk-space@main
+        with:
+          tool-cache: false
+          android: true
+          dotnet: true
+          haskell: true
+          large-packages: false
+          docker-images: false
+          swap-storage: false
       - uses: actions/checkout@v3
+        if: |
+          github.repository_owner == 'wpilibsuite' &&
+          (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
         with:
           repository: wpilibsuite/build-tools
       - uses: actions/download-artifact@v3
+        if: |
+          github.repository_owner == 'wpilibsuite' &&
+          (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
         with:
           path: combiner/products/build/allOutputs
       - name: Flatten Artifacts
+        if: |
+          github.repository_owner == 'wpilibsuite' &&
+          (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
         run: rsync -a --delete combiner/products/build/allOutputs/*/* combiner/products/build/allOutputs/
       - name: Check version number exists
+        if: |
+          github.repository_owner == 'wpilibsuite' &&
+          (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
         run: |
           cat combiner/products/build/allOutputs/version.txt
           test -s combiner/products/build/allOutputs/version.txt
       - uses: actions/setup-java@v3
+        if: |
+          github.repository_owner == 'wpilibsuite' &&
+          (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
         with:
           distribution: 'zulu'
-          java-version: 11
-      - name: Combine
-        if: |
-          !startsWith(github.ref, 'refs/tags/v') &&
-          github.ref != 'refs/heads/main'
-        run: cd combiner && ./gradlew publish -Pallwpilib
-      - name: Combine (Master)
+          java-version: 17
+      - name: Combine (Main)
         if: |
           github.repository_owner == 'wpilibsuite' &&
           github.ref == 'refs/heads/main'
@@ -189,6 +256,9 @@
           ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
           ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
       - uses: actions/upload-artifact@v3
+        if: |
+          github.repository_owner == 'wpilibsuite' &&
+          (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
         with:
           name: Maven
           path: ~/releases
diff --git a/third_party/allwpilib/.github/workflows/lint-format.yml b/third_party/allwpilib/.github/workflows/lint-format.yml
index ba25a05..fe20537 100644
--- a/third_party/allwpilib/.github/workflows/lint-format.yml
+++ b/third_party/allwpilib/.github/workflows/lint-format.yml
@@ -26,16 +26,10 @@
         uses: actions/setup-python@v4
         with:
           python-version: 3.8
-      - name: Install clang-format
-        run: |
-          wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
-          sudo sh -c "echo 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-14 main' >> /etc/apt/sources.list.d/proposed-repositories.list"
-          sudo apt-get update -q
-          sudo apt-get install -y clang-format-14
       - name: Install wpiformat
         run: pip3 install wpiformat
       - name: Run
-        run: wpiformat -clang 14
+        run: wpiformat
       - name: Check output
         run: git --no-pager diff --exit-code HEAD
       - name: Generate diff
@@ -70,12 +64,6 @@
         uses: actions/setup-python@v4
         with:
           python-version: 3.8
-      - name: Install clang-tidy
-        run: |
-          wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
-          sudo sh -c "echo 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-14 main' >> /etc/apt/sources.list.d/proposed-repositories.list"
-          sudo apt-get update -q
-          sudo apt-get install -y clang-tidy-14 clang-format-14
       - name: Install wpiformat
         run: pip3 install wpiformat
       - name: Create compile_commands.json
@@ -83,7 +71,7 @@
       - name: List changed files
         run: wpiformat -list-changed-files
       - name: Run clang-tidy
-        run: wpiformat -clang 14 -no-format -tidy-changed -compile-commands=build/compile_commands/linuxx86-64 -vv
+        run: wpiformat -no-format -tidy-changed -compile-commands=build/compile_commands/linuxx86-64 -vv
   javaformat:
     name: "Java format"
     runs-on: ubuntu-22.04
diff --git a/third_party/allwpilib/.github/workflows/sanitizers.yml b/third_party/allwpilib/.github/workflows/sanitizers.yml
index 3f68a61..c0b6ac3 100644
--- a/third_party/allwpilib/.github/workflows/sanitizers.yml
+++ b/third_party/allwpilib/.github/workflows/sanitizers.yml
@@ -26,22 +26,33 @@
             ctest-flags: ""
     name: "${{ matrix.name }}"
     runs-on: ubuntu-22.04
-    container: wpilib/roborio-cross-ubuntu:2023-22.04
+    container: wpilib/roborio-cross-ubuntu:2024-22.04
     steps:
-      - uses: actions/checkout@v3
-
       - name: Install Dependencies
-        run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3 clang-14
+        run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3 clang-14 libprotobuf-dev protobuf-compiler ninja-build
+
+      - name: Install QuickBuffers
+        if: runner.os == 'Linux'
+        run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.2/protoc-gen-quickbuf_1.3.2_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.2_amd64.deb
+
+      - name: Run sccache-cache
+        uses: mozilla-actions/sccache-action@v0.0.3
 
       - name: Install jinja
         run: python -m pip install jinja2
 
+      - uses: actions/checkout@v3
+
       - name: configure
-        run: mkdir build && cd build && cmake -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-14 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-14 ${{ matrix.cmake-flags }} ..
+        run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-14 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-14 -DWITH_JAVA=OFF ${{ matrix.cmake-flags }} ..
+        env:
+          SCCACHE_GHA_ENABLED: "true"
 
       - name: build
         working-directory: build
         run: cmake --build . --parallel $(nproc)
+        env:
+          SCCACHE_GHA_ENABLED: "true"
 
       - name: test
         working-directory: build
diff --git a/third_party/allwpilib/.github/workflows/upstream-utils.yml b/third_party/allwpilib/.github/workflows/upstream-utils.yml
index ea6e9aa..0e4a46f 100644
--- a/third_party/allwpilib/.github/workflows/upstream-utils.yml
+++ b/third_party/allwpilib/.github/workflows/upstream-utils.yml
@@ -29,10 +29,6 @@
         run: |
           git config --global user.email "you@example.com"
           git config --global user.name "Your Name"
-      - name: Run update_drake.py
-        run: |
-          cd upstream_utils
-          ./update_drake.py
       - name: Run update_eigen.py
         run: |
           cd upstream_utils
@@ -41,6 +37,14 @@
         run: |
           cd upstream_utils
           ./update_fmt.py
+      - name: Run update_gcem.py
+        run: |
+          cd upstream_utils
+          ./update_gcem.py
+      - name: Run update_json.py
+        run: |
+          cd upstream_utils
+          ./update_json.py
       - name: Run update_libuv.py
         run: |
           cd upstream_utils
@@ -61,6 +65,10 @@
         run: |
           cd upstream_utils
           ./update_memory.py
+      - name: Run update_protobuf.py
+        run: |
+          cd upstream_utils
+          ./update_protobuf.py
       - name: Add untracked files to index so they count as changes
         run: git add -A
       - name: Check output
diff --git a/third_party/allwpilib/.gitignore b/third_party/allwpilib/.gitignore
index 8db5645..edea361 100644
--- a/third_party/allwpilib/.gitignore
+++ b/third_party/allwpilib/.gitignore
@@ -9,6 +9,8 @@
 simgui-window.json
 simgui.json
 
+networktables.json
+
 # Created by the jenkins test script
 test-reports
 
@@ -203,6 +205,7 @@
 *.user
 *.userosscache
 *.sln.docstates
+CMakeSettings.json
 
 # Visual C++ cache files
 ipch/
diff --git a/third_party/allwpilib/.styleguide b/third_party/allwpilib/.styleguide
index 9df73f7..1ba4e1d 100644
--- a/third_party/allwpilib/.styleguide
+++ b/third_party/allwpilib/.styleguide
@@ -29,8 +29,9 @@
   ^Eigen/
   ^cameraserver/
   ^cscore
-  ^drake/
   ^fmt/
+  ^gtest/
+  ^google/
   ^hal/
   ^imgui
   ^implot
diff --git a/third_party/allwpilib/CMakeLists.txt b/third_party/allwpilib/CMakeLists.txt
index 076eca2..f994bf1 100644
--- a/third_party/allwpilib/CMakeLists.txt
+++ b/third_party/allwpilib/CMakeLists.txt
@@ -11,10 +11,15 @@
     set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION 10.0.18362.0 CACHE STRING INTERNAL FORCE)
 endif()
 
+cmake_minimum_required(VERSION 3.11)
 project(allwpilib)
-cmake_minimum_required(VERSION 3.3.0)
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
 
+# Make timestamps of extracted files from FetchContent the time of extraction
+if (POLICY CMP0135)
+    cmake_policy(SET CMP0135 NEW)
+endif()
+
 message(STATUS "Platform version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
 
 set(WPILIB_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
@@ -50,7 +55,8 @@
 
 # Options for building certain parts of the repo. Everything is built by default.
 option(BUILD_SHARED_LIBS "Build with shared libs (needed for JNI)" ON)
-option(WITH_JAVA "Include java and JNI in the build" ON)
+option(WITH_JAVA "Include Java and JNI in the build" ON)
+option(WITH_JAVA_SOURCE "Build Java source jars" ON)
 option(WITH_CSCORE "Build cscore (needs OpenCV)" ON)
 option(WITH_NTCORE "Build ntcore" ON)
 option(WITH_WPIMATH "Build wpimath" ON)
@@ -81,11 +87,6 @@
     set (CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "" FORCE)
 endif()
 
-# We always want flat install with MSVC.
-if (MSVC)
-  set(WITH_FLAT_INSTALL ON)
-endif()
-
 if (WITH_JAVA AND NOT BUILD_SHARED_LIBS)
     message(FATAL_ERROR "
 FATAL: Cannot build static libs with Java enabled.
@@ -155,7 +156,6 @@
 
 set( wpilib_dest "")
 set( include_dest include )
-set( main_lib_dest lib )
 set( java_lib_dest java )
 set( jni_lib_dest jni )
 
@@ -167,8 +167,7 @@
 
 if (USE_SYSTEM_LIBUV)
 set (LIBUV_SYSTEM_REPLACE "
-find_package(PkgConfig REQUIRED)
-pkg_check_modules(libuv REQUIRED IMPORTED_TARGET libuv)
+find_dependency(libuv CONFIG)
 ")
 endif()
 
@@ -178,6 +177,12 @@
 
 find_package(LIBSSH 0.7.1)
 
+find_package(Protobuf REQUIRED)
+find_program(Quickbuf_EXECUTABLE
+    NAMES protoc-gen-quickbuf
+    DOC "The Quickbuf protoc plugin"
+)
+
 if (WITH_FLAT_INSTALL)
 set(WPIUTIL_DEP_REPLACE "include($\{SELF_DIR\}/wpiutil-config.cmake)")
 set(WPINET_DEP_REPLACE "include($\{SELF_DIR\}/wpinet-config.cmake)")
@@ -295,6 +300,7 @@
     add_subdirectory(wpigui)
     add_subdirectory(glass)
     add_subdirectory(outlineviewer)
+    add_subdirectory(sysid)
     if (LIBSSH_FOUND)
         add_subdirectory(roborioteamnumbersetter)
         add_subdirectory(datalogtool)
@@ -319,6 +325,8 @@
     add_subdirectory(wpilibj)
     add_subdirectory(wpilibc)
     add_subdirectory(wpilibNewCommands)
+    add_subdirectory(romiVendordep)
+    add_subdirectory(xrpVendordep)
     if (WITH_EXAMPLES)
         add_subdirectory(wpilibcExamples)
     endif()
diff --git a/third_party/allwpilib/CONTRIBUTING.md b/third_party/allwpilib/CONTRIBUTING.md
index d4ab59c..763521a 100644
--- a/third_party/allwpilib/CONTRIBUTING.md
+++ b/third_party/allwpilib/CONTRIBUTING.md
@@ -40,8 +40,39 @@
 WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system.
 While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome.
 
+### Math documentation
+
 When writing math expressions in documentation, use https://www.unicodeit.net/ to convert LaTeX to a Unicode equivalent that's easier to read. Not all expressions will translate (e.g., superscripts of superscripts) so focus on making it readable by someone who isn't familiar with LaTeX. If content on multiple lines needs to be aligned in Doxygen/Javadoc comments (e.g., integration/summation limits, matrices packed with square brackets and superscripts for them), put them in @verbatim/@endverbatim blocks in Doxygen or `<pre>` tags in Javadoc so they render with monospace font.
 
+The LaTeX to Unicode conversions can also be done locally via the unicodeit Python package. To install it, execute:
+```bash
+pip install --user unicodeit
+```
+
+Here's example usage:
+```bash
+$ python -m unicodeit.cli 'x_{k+1} = Ax_k + Bu_k'
+xₖ₊₁ = Axₖ + Buₖ
+```
+
+On Linux, this process can be streamlined further by adding the following Bash function to your .bashrc (requires `wl-clipboard` on Wayland or `xclip` on X11):
+```bash
+# Converts LaTeX to Unicode, prints the result, and copies it to the clipboard
+uc() {
+  if [ $WAYLAND_DISPLAY ]; then
+    python -m unicodeit.cli $@ | tee >(wl-copy -n)
+  else
+    python -m unicodeit.cli $@ | tee >(xclip -sel)
+  fi
+}
+```
+
+Here's example usage:
+```bash
+$ uc 'x_{k+1} = Ax_k + Bu_k'
+xₖ₊₁ = Axₖ + Buₖ
+```
+
 ## Submitting Changes
 
 ### Pull Request Format
diff --git a/third_party/allwpilib/DevelopmentBuilds.md b/third_party/allwpilib/DevelopmentBuilds.md
index e452f3a..86eb538 100644
--- a/third_party/allwpilib/DevelopmentBuilds.md
+++ b/third_party/allwpilib/DevelopmentBuilds.md
@@ -4,11 +4,16 @@
 
 **Note:** This only applies to Java/C++ teams.
 
+> [!WARNING]
+> **There are no stability or compatibility guarantees for builds outside of [tagged releases](https://github.com/wpilibsuite/allwpilib/releases). Changes may not be fully documented. Use them at your own risk!**
+>
+> Development builds may be non-functional between the end of the season and the start of beta testing. Development builds are also likely to be incompatible with vendor libraries during this time.
+
 ## Development Build
 
 Development builds are the per-commit build hosted every time a commit is pushed to the [allwpilib](https://github.com/wpilibsuite/allwpilib/) repository. These builds are then hosted on [artifactory](https://frcmaven.wpi.edu/artifactory/webapp/#/home).
 
-To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2023 GradleRIO version, ie `2023.0.0-alpha-1`
+To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2024 GradleRIO version, ie `2024.0.0-alpha-1`
 
 ```groovy
 wpi.maven.useLocal = false
@@ -23,13 +28,13 @@
 ```groovy
 plugins {
   id "java"
-  id "edu.wpi.first.GradleRIO" version "2023.0.0-alpha-1"
+  id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
 }
 
 wpi.maven.useLocal = false
 wpi.maven.useDevelopment = true
-wpi.versions.wpilibVersion = '2023.+'
-wpi.versions.wpimathVersion = '2023.+'
+wpi.versions.wpilibVersion = '2024.+'
+wpi.versions.wpimathVersion = '2024.+'
 ```
 
 C++
@@ -37,13 +42,13 @@
 plugins {
   id "cpp"
   id "google-test-test-suite"
-  id "edu.wpi.first.GradleRIO" version "2023.0.0-alpha-1"
+  id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
 }
 
 wpi.maven.useLocal = false
 wpi.maven.useDevelopment = true
-wpi.versions.wpilibVersion = '2023.+'
-wpi.versions.wpimathVersion = '2023.+'
+wpi.versions.wpilibVersion = '2024.+'
+wpi.versions.wpimathVersion = '2024.+'
 ```
 
 ### Development Build Documentation
@@ -59,7 +64,7 @@
 ```groovy
 plugins {
   id "java"
-  id "edu.wpi.first.GradleRIO" version "2023.0.0-alpha-1"
+  id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
 }
 
 wpi.maven.useLocal = false
@@ -73,7 +78,7 @@
 plugins {
   id "cpp"
   id "google-test-test-suite"
-  id "edu.wpi.first.GradleRIO" version "2023.0.0-alpha-1"
+  id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
 }
 
 wpi.maven.useLocal = false
diff --git a/third_party/allwpilib/LICENSE.md b/third_party/allwpilib/LICENSE.md
index 91fd148..43b62ec 100644
--- a/third_party/allwpilib/LICENSE.md
+++ b/third_party/allwpilib/LICENSE.md
@@ -1,4 +1,4 @@
-Copyright (c) 2009-2022 FIRST and other WPILib contributors
+Copyright (c) 2009-2023 FIRST and other WPILib contributors
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/third_party/allwpilib/MavenArtifacts.md b/third_party/allwpilib/MavenArtifacts.md
index 8ea5a49..655f9c5 100644
--- a/third_party/allwpilib/MavenArtifacts.md
+++ b/third_party/allwpilib/MavenArtifacts.md
@@ -16,7 +16,7 @@
 
 The first types are Java artifacts. These are usually published as `jar` files. Usually, the actual jar file is published with no classifier. The sources are published with the `-sources` classifier, and the javadocs are published with the `-javadoc` classifier.
 
-The second types are native artifacts. These are usually published as `zip` files (except for the `JNI` artifact types, which are `jar` files. See below for information on this). The `-sources` and `-headers` classifiers contain the sources and headers respecively for the library. Each artifact also contains a classifier for each platform we publish. This platform is in the format `{os}{arch}`. The platform artifact only contains the binaries for a specific platform. In addition, we provide a `-all` classifier. This classifer combines all of the platform artifacts into a single artifact. This is useful for tools that cannot determine what version to use during builds. However, we recommend using the platform specific classifier when possible. Note that the binary artifacts never contain the headers, you always need the `-headers` classifier to get those.
+The second types are native artifacts. These are usually published as `zip` files (except for the `JNI` artifact types, which are `jar` files. See below for information on this). The `-sources` and `-headers` classifiers contain the sources and headers respectively for the library. Each artifact also contains a classifier for each platform we publish. This platform is in the format `{os}{arch}`. The platform artifact only contains the binaries for a specific platform. In addition, we provide a `-all` classifier. This classifier combines all of the platform artifacts into a single artifact. This is useful for tools that cannot determine what version to use during builds. However, we recommend using the platform specific classifier when possible. Note that the binary artifacts never contain the headers, you always need the `-headers` classifier to get those.
 
 ## Artifact Names
 
diff --git a/third_party/allwpilib/README-CMAKE.md b/third_party/allwpilib/README-CMAKE.md
index 6a10d5b..776f808 100644
--- a/third_party/allwpilib/README-CMAKE.md
+++ b/third_party/allwpilib/README-CMAKE.md
@@ -12,14 +12,19 @@
 * halsim
 * wpigui
 * wpimath
+* wpilibNewCommands
 
 By default, all libraries except for the HAL and WPILib get built with a default CMake setup. The libraries are built as shared libraries, and include the JNI libraries as well as building the Java JARs.
 
 ## Prerequisites
 
-The most common prerequisite is going to be OpenCV. OpenCV needs to be findable by CMake. On systems like the Jetson, this is installed by default. Otherwise, you will need to build OpenCV from source and install it.
+The jinja2 pip package is needed to generate classes for NT4's pubsub.
 
-In addition, if you want JNI and Java, you will need a JDK of at least version 11 installed. In addition, you need a `JAVA_HOME` environment variable set properly and set to the JDK directory.
+The protobuf library and compiler are needed for protobuf generation. The QuickBuffers protoc-gen package is also required when Java is being built; this can be obtained from https://github.com/HebiRobotics/QuickBuffers/releases/.
+
+OpenCV needs to be findable by CMake. On systems like the Jetson, this is installed by default. Otherwise, you will need to build OpenCV from source and install it.
+
+If you want JNI and Java, you will need a JDK of at least version 11 installed. In addition, you need a `JAVA_HOME` environment variable set properly and set to the JDK directory.
 
 If you are building with unit tests or simulation modules, you will also need an Internet connection for the initial setup process, as CMake will clone google-test and imgui from GitHub.
 
@@ -29,6 +34,8 @@
 
 * `WITH_JAVA` (ON Default)
   * This option will enable Java and JNI builds. If this is on, `WITH_SHARED_LIBS` must be on. Otherwise CMake will error.
+* `WITH_JAVA_SOURCE` (ON Default)
+  * This option will build Java source JARs for each enabled Java library. This does not require `WITH_JAVA` to be on, allowing source JARs to be built without the compiled JARs if desired.
 * `WITH_SHARED_LIBS` (ON Default)
   * This option will cause cmake to build static libraries instead of shared libraries. If this is off, `WITH_JAVA` must be off. Otherwise CMake will error.
 * `WITH_CSCORE` (ON Default)
@@ -93,7 +100,7 @@
 To do so, create a new folder to contain your project. Add the following code below to a `CMakeLists.txt` file in that directory.
 
 ```
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.11)
 project(vision_app) # Project Name Here
 
 find_package(wpilib REQUIRED)
diff --git a/third_party/allwpilib/README.md b/third_party/allwpilib/README.md
index 998d91f..78f2486 100644
--- a/third_party/allwpilib/README.md
+++ b/third_party/allwpilib/README.md
@@ -17,6 +17,7 @@
     - [Custom toolchain location](#custom-toolchain-location)
     - [Formatting/Linting](#formattinglinting)
     - [CMake](#cmake)
+  - [Running examples in simulation](#running-examples-in-simulation)
   - [Publishing](#publishing)
   - [Structure and Organization](#structure-and-organization)
 - [Contributing to WPILib](#contributing-to-wpilib)
@@ -40,7 +41,7 @@
 
 ## Requirements
 
-- [JDK 11](https://adoptopenjdk.net/)
+- [JDK 11](https://adoptium.net/temurin/releases/?version=11)
     - Note that the JRE is insufficient; the full JDK is required
     - On Ubuntu, run `sudo apt install openjdk-11-jdk`
     - On Windows, install the JDK 11 .msi from the link above
@@ -48,7 +49,7 @@
 - C++ compiler
     - On Linux, install GCC 11 or greater
     - On Windows, install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio)
-    - On macOS, install the Xcode command-line build tools via `xcode-select --install`
+    - On macOS, install the Xcode command-line build tools via `xcode-select --install`. Xcode 13 or later is required.
 - ARM compiler toolchain
     - Run `./gradlew installRoboRioToolchain` after cloning this repository
     - If the WPILib installer was used, this toolchain is already installed
@@ -61,7 +62,7 @@
 
 Clone the WPILib repository and follow the instructions above for installing any required tooling.
 
-See the [styleguide README](https://github.com/wpilibsuite/styleguide/blob/main/README.md) for wpiformat setup instructions. We use clang-format 14.
+See the [styleguide README](https://github.com/wpilibsuite/styleguide/blob/main/README.md) for wpiformat setup instructions.
 
 ## Building
 
@@ -87,9 +88,24 @@
 
 `./gradlew build` builds _everything_, which includes debug and release builds for desktop and all installed cross compilers. Many developers don't need or want to build all of this. Therefore, common tasks have shortcuts to only build necessary components for common development and testing tasks.
 
-`./gradlew testDesktopCpp` and `./gradlew testDesktopJava` will build and run the tests for `wpilibc` and `wpilibj` respectively. They will only build the minimum components required to run the tests.
+`./gradlew testDesktopCpp` and `./gradlew testDesktopJava` will build and run the tests for `wpilibc` and `wpilibj` respectively. They will only build the minimum components required to run the tests. `./gradlew testDesktop` will run both `testDesktopJava` and `testDesktopCpp`.
 
-`testDesktopCpp` and `testDesktopJava` tasks also exist for the projects `wpiutil`, `ntcore`, `cscore`, `hal` `wpilibNewCommands` and `cameraserver`. These can be ran with `./gradlew :projectName:task`.
+`testDesktopCpp`, `testDesktopJava`, and `testDesktop` tasks also exist for the following projects:
+
+- `apriltag`
+- `cameraserver`
+- `cscore`
+- `hal`
+- `ntcore`
+- `wpilibNewCommands`
+- `wpimath`
+- `wpinet`
+- `wpiunits`
+- `wpiutil`
+- `romiVendordep`
+- `xrpVendordep`
+
+These can be ran with `./gradlew :projectName:task`.
 
 `./gradlew buildDesktopCpp` and `./gradlew buildDesktopJava` will compile `wpilibcExamples` and `wpilibjExamples` respectively. The results can't be ran, but they can compile.
 
@@ -119,7 +135,7 @@
 
 #### wpiformat
 
-wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat -clang 14` on Windows or `python3 -m wpiformat -clang 14` on other platforms.
+wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat` on Windows or `python3 -m wpiformat` on other platforms.
 
 #### Java Code Quality Tools
 
@@ -131,6 +147,16 @@
 
 CMake is also supported for building. See [README-CMAKE.md](README-CMAKE.md).
 
+## Running examples in simulation
+
+Examples can be run in simulation with the following command:
+
+```bash
+./gradlew wpilibcExamples:runExample
+./gradlew wpilibjExamples:runExample
+```
+where `Example` is the example's folder name.
+
 ## Publishing
 
 If you are building to test with other dependencies or just want to export the build as a Maven-style dependency, simply run the `publish` task. This task will publish all available packages to ~/releases/maven/development. If you need to publish the project to a different repo, you can specify it with `-Prepo=repo_name`. Valid options are:
diff --git a/third_party/allwpilib/ThirdPartyNotices.txt b/third_party/allwpilib/ThirdPartyNotices.txt
index 18163b2..3eaf7b9 100644
--- a/third_party/allwpilib/ThirdPartyNotices.txt
+++ b/third_party/allwpilib/ThirdPartyNotices.txt
@@ -41,9 +41,6 @@
                       wpilibc/src/main/native/include/trajectory/TrajectoryParameterizer.h
                       wpilibc/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp
 Portable File Dialogs wpigui/src/main/native/include/portable-file-dialogs.h
-Drake                 wpimath/src/main/native/thirdparty/drake/
-                      wpimath/src/test/native/cpp/drake/
-                      wpimath/src/test/native/include/drake/
 V8 export-template    wpiutil/src/main/native/include/wpi/SymbolExports.h
 GCEM                  wpimath/src/main/native/thirdparty/gcem/include/
 
@@ -1069,41 +1066,6 @@
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 
-=============
-Drake Library
-=============
-All components of Drake are licensed under the BSD 3-Clause License
-shown below. Where noted in the source code, some portions may
-be subject to other permissive, non-viral licenses.
-
-Copyright 2012-2016 Robot Locomotion Group @ CSAIL
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.  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.  Neither the name of
-the Massachusetts Institute of Technology nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS 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 COPYRIGHT
-HOLDER OR 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.
-
 =====================
 Portable File Dialogs
 =====================
diff --git a/third_party/allwpilib/apriltag/CMakeLists.txt b/third_party/allwpilib/apriltag/CMakeLists.txt
index 29dcd2b..9d97363 100644
--- a/third_party/allwpilib/apriltag/CMakeLists.txt
+++ b/third_party/allwpilib/apriltag/CMakeLists.txt
@@ -7,7 +7,7 @@
 FetchContent_Declare(
     apriltaglib
     GIT_REPOSITORY  https://github.com/wpilibsuite/apriltag.git
-    GIT_TAG         ad31e33d20f9782b7239cb15cde57c56c91383ad
+    GIT_TAG         64be6ab26abf5e995321997fd0752c609a7e30f4
 )
 
 # Don't use apriltag's CMakeLists.txt due to conflicting naming and JNI
@@ -40,7 +40,7 @@
   set(CMAKE_JAVA_INCLUDE_PATH apriltag.jar ${EJML_JARS} ${JACKSON_JARS})
 
   file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
-  file(GLOB_RECURSE JAVA_RESOURCES src/main/native/resources/*.json)
+  file(GLOB_RECURSE JAVA_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} src/main/native/resources/*.json)
   add_jar(apriltag_jar
     SOURCES ${JAVA_SOURCES}
     RESOURCES NAMESPACE "edu/wpi/first/apriltag" ${JAVA_RESOURCES}
@@ -62,15 +62,26 @@
   target_link_libraries(apriltagjni PRIVATE apriltag_jni_headers)
   add_dependencies(apriltagjni apriltag_jar)
 
-  if (MSVC)
-    install(TARGETS apriltagjni RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-  endif()
-
-  install(TARGETS apriltagjni EXPORT apriltagjni DESTINATION "${main_lib_dest}")
+  install(TARGETS apriltagjni EXPORT apriltagjni)
 
 endif()
 
-GENERATE_RESOURCES(src/main/native/resources/edu/wpi/first/apriltag generated/main/cpp APRILTAG frc apriltag_resources_src)
+if (WITH_JAVA_SOURCE)
+  find_package(Java REQUIRED)
+  include(UseJava)
+  file(GLOB APRILTAG_SOURCES src/main/java/edu/wpi/first/apriltag/*.java)
+  add_jar(apriltag_src_jar
+  RESOURCES NAMESPACE "edu/wpi/first/apriltag" ${APRILTAG_SOURCES}
+  NAMESPACE "edu/wpi/first/apriltag/jni" src/main/java/edu/wpi/first/apriltag/jni/AprilTagJNI.java
+  OUTPUT_NAME apriltag-sources)
+
+  get_property(APRILTAG_SRC_JAR_FILE TARGET apriltag_src_jar PROPERTY JAR_FILE)
+  install(FILES ${APRILTAG_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+  set_property(TARGET apriltag_src_jar PROPERTY FOLDER "java")
+endif()
+
+generate_resources(src/main/native/resources/edu/wpi/first/apriltag generated/main/cpp APRILTAG frc apriltag_resources_src)
 
 file(GLOB apriltag_native_src src/main/native/cpp/*.cpp)
 
@@ -82,9 +93,9 @@
 wpilib_target_warnings(apriltag)
 # disable warnings that apriltaglib can't handle
 if (MSVC)
-  target_compile_options(apriltag PRIVATE /wd4018)
+  target_compile_options(apriltag PRIVATE /wd4018 /wd4005 /wd4996)
 else()
-  target_compile_options(apriltag PRIVATE -Wno-sign-compare -Wno-gnu-zero-variadic-macro-arguments)
+  target_compile_options(apriltag PRIVATE -Wno-sign-compare -Wno-gnu-zero-variadic-macro-arguments -Wno-type-limits)
 endif()
 
 target_link_libraries(apriltag wpimath)
@@ -94,13 +105,9 @@
   $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
   $<INSTALL_INTERFACE:${include_dest}/apriltag>)
 
-install(TARGETS apriltag EXPORT apriltag DESTINATION "${main_lib_dest}")
+install(TARGETS apriltag EXPORT apriltag)
 install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/apriltag")
 
-if (WITH_JAVA AND MSVC)
-    install(TARGETS apriltag RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-endif()
-
 if (WITH_FLAT_INSTALL)
     set (apriltag_config_dir ${wpilib_dest})
 else()
diff --git a/third_party/allwpilib/apriltag/build.gradle b/third_party/allwpilib/apriltag/build.gradle
index 960af58..5fb7b34 100644
--- a/third_party/allwpilib/apriltag/build.gradle
+++ b/third_party/allwpilib/apriltag/build.gradle
@@ -35,7 +35,6 @@
 
 dependencies {
     implementation project(':wpimath')
-    devImplementation project(':wpimath')
 }
 
 sourceSets {
diff --git a/third_party/allwpilib/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFieldLayout.java b/third_party/allwpilib/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFieldLayout.java
index 605c97c..ce2336c 100644
--- a/third_party/allwpilib/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFieldLayout.java
+++ b/third_party/allwpilib/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFieldLayout.java
@@ -37,6 +37,9 @@
  * at the bottom-right corner of the blue alliance wall. {@link #setOrigin(OriginPosition)} can be
  * used to change the poses returned from {@link AprilTagFieldLayout#getTagPose(int)} to be from the
  * perspective of a specific alliance.
+ *
+ * <p>Tag poses represent the center of the tag, with a zero rotation representing a tag that is
+ * upright and facing away from the (blue) alliance wall (that is, towards the opposing alliance).
  */
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
@@ -111,6 +114,26 @@
   }
 
   /**
+   * Returns the length of the field the layout is representing in meters.
+   *
+   * @return length, in meters
+   */
+  @JsonIgnore
+  public double getFieldLength() {
+    return m_fieldDimensions.fieldLength;
+  }
+
+  /**
+   * Returns the length of the field the layout is representing in meters.
+   *
+   * @return width, in meters
+   */
+  @JsonIgnore
+  public double getFieldWidth() {
+    return m_fieldDimensions.fieldWidth;
+  }
+
+  /**
    * Sets the origin based on a predefined enumeration of coordinate frame origins. The origins are
    * calculated from the field dimensions.
    *
@@ -150,6 +173,16 @@
   }
 
   /**
+   * Returns the origin used for tag pose transformation.
+   *
+   * @return the origin
+   */
+  @JsonIgnore
+  public Pose3d getOrigin() {
+    return m_origin;
+  }
+
+  /**
    * Gets an AprilTag pose by its ID.
    *
    * @param ID The ID of the tag.
@@ -186,16 +219,26 @@
   }
 
   /**
-   * Deserializes a field layout from a resource within a jar file.
+   * Deserializes a field layout from a resource within a internal jar file.
+   *
+   * <p>Users should use {@link AprilTagFields#loadAprilTagLayoutField()} to load official layouts
+   * and {@link #AprilTagFieldLayout(String)} for custom layouts.
    *
    * @param resourcePath The absolute path of the resource
    * @return The deserialized layout
    * @throws IOException If the resource could not be loaded
    */
   public static AprilTagFieldLayout loadFromResource(String resourcePath) throws IOException {
-    try (InputStream stream = AprilTagFieldLayout.class.getResourceAsStream(resourcePath);
-        InputStreamReader reader = new InputStreamReader(stream)) {
+    InputStream stream = AprilTagFieldLayout.class.getResourceAsStream(resourcePath);
+    if (stream == null) {
+      // Class.getResourceAsStream() returns null if the resource does not exist.
+      throw new IOException("Could not locate resource: " + resourcePath);
+    }
+    InputStreamReader reader = new InputStreamReader(stream);
+    try {
       return new ObjectMapper().readerFor(AprilTagFieldLayout.class).readValue(reader);
+    } catch (IOException e) {
+      throw new IOException("Failed to load AprilTagFieldLayout: " + resourcePath);
     }
   }
 
diff --git a/third_party/allwpilib/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFields.java b/third_party/allwpilib/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFields.java
index 28eeb58..dda8d34 100644
--- a/third_party/allwpilib/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFields.java
+++ b/third_party/allwpilib/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFields.java
@@ -4,17 +4,36 @@
 
 package edu.wpi.first.apriltag;
 
+import java.io.IOException;
+import java.io.UncheckedIOException;
+
 public enum AprilTagFields {
-  k2022RapidReact("2022-rapidreact.json");
+  k2022RapidReact("2022-rapidreact.json"),
+  k2023ChargedUp("2023-chargedup.json");
 
   public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
 
   /** Alias to the current game. */
-  public static final AprilTagFields kDefaultField = k2022RapidReact;
+  public static final AprilTagFields kDefaultField = k2023ChargedUp;
 
   public final String m_resourceFile;
 
   AprilTagFields(String resourceFile) {
     m_resourceFile = kBaseResourceDir + resourceFile;
   }
+
+  /**
+   * Get a {@link AprilTagFieldLayout} from the resource JSON.
+   *
+   * @return AprilTagFieldLayout of the field
+   * @throws UncheckedIOException If the layout does not exist
+   */
+  public AprilTagFieldLayout loadAprilTagLayoutField() {
+    try {
+      return AprilTagFieldLayout.loadFromResource(m_resourceFile);
+    } catch (IOException e) {
+      throw new UncheckedIOException(
+          "Could not load AprilTagFieldLayout from " + m_resourceFile, e);
+    }
+  }
 }
diff --git a/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagFieldLayout.cpp b/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagFieldLayout.cpp
index 31e4cac..fd570ac 100644
--- a/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagFieldLayout.cpp
+++ b/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagFieldLayout.cpp
@@ -8,22 +8,21 @@
 
 #include <units/angle.h>
 #include <units/length.h>
+#include <wpi/MemoryBuffer.h>
 #include <wpi/json.h>
-#include <wpi/raw_istream.h>
 #include <wpi/raw_ostream.h>
 
 using namespace frc;
 
 AprilTagFieldLayout::AprilTagFieldLayout(std::string_view path) {
-  std::error_code error_code;
-
-  wpi::raw_fd_istream input{path, error_code};
-  if (error_code) {
+  std::error_code ec;
+  std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+      wpi::MemoryBuffer::GetFile(path, ec);
+  if (fileBuffer == nullptr || ec) {
     throw std::runtime_error(fmt::format("Cannot open file: {}", path));
   }
 
-  wpi::json json;
-  input >> json;
+  wpi::json json = wpi::json::parse(fileBuffer->begin(), fileBuffer->end());
 
   for (const auto& tag : json.at("tags").get<std::vector<AprilTag>>()) {
     m_apriltags[tag.ID] = tag;
@@ -42,6 +41,14 @@
   }
 }
 
+units::meter_t AprilTagFieldLayout::GetFieldLength() const {
+  return m_fieldLength;
+}
+
+units::meter_t AprilTagFieldLayout::GetFieldWidth() const {
+  return m_fieldWidth;
+}
+
 void AprilTagFieldLayout::SetOrigin(OriginPosition origin) {
   switch (origin) {
     case OriginPosition::kBlueAllianceWallRightSide:
@@ -60,6 +67,10 @@
   m_origin = origin;
 }
 
+Pose3d AprilTagFieldLayout::GetOrigin() const {
+  return m_origin;
+}
+
 std::optional<frc::Pose3d> AprilTagFieldLayout::GetTagPose(int ID) const {
   const auto& it = m_apriltags.find(ID);
   if (it == m_apriltags.end()) {
diff --git a/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagFields.cpp b/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagFields.cpp
index 3d7b1ed..2f92937 100644
--- a/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagFields.cpp
+++ b/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagFields.cpp
@@ -10,6 +10,7 @@
 
 // C++ generated from resource files
 std::string_view GetResource_2022_rapidreact_json();
+std::string_view GetResource_2023_chargedup_json();
 
 AprilTagFieldLayout LoadAprilTagLayoutField(AprilTagField field) {
   std::string_view fieldString;
@@ -17,6 +18,9 @@
     case AprilTagField::k2022RapidReact:
       fieldString = GetResource_2022_rapidreact_json();
       break;
+    case AprilTagField::k2023ChargedUp:
+      fieldString = GetResource_2023_chargedup_json();
+      break;
     case AprilTagField::kNumFields:
       throw std::invalid_argument("Invalid Field");
   }
diff --git a/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagPoseEstimator.cpp b/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagPoseEstimator.cpp
index dc4acde..29a0802 100644
--- a/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagPoseEstimator.cpp
+++ b/third_party/allwpilib/apriltag/src/main/native/cpp/AprilTagPoseEstimator.cpp
@@ -110,7 +110,7 @@
   apriltag_pose_t pose1, pose2;
   double err1, err2;
   estimate_tag_pose_orthogonal_iteration(&info, &err1, &pose1, &err2, &pose2,
-                                         nIters);
+                                         nIters, 1e-7);
   return {MakePose(pose1), MakePose(pose2), err1, err2};
 }
 
diff --git a/third_party/allwpilib/apriltag/src/main/native/cpp/jni/AprilTagJNI.cpp b/third_party/allwpilib/apriltag/src/main/native/cpp/jni/AprilTagJNI.cpp
index 31fd4f9..c957b2d 100644
--- a/third_party/allwpilib/apriltag/src/main/native/cpp/jni/AprilTagJNI.cpp
+++ b/third_party/allwpilib/apriltag/src/main/native/cpp/jni/AprilTagJNI.cpp
@@ -504,15 +504,14 @@
     nullPointerEx.Throw(env, "homography cannot be null");
     return nullptr;
   }
-  JDoubleArrayRef harr{env, homography};
+  JSpan<const jdouble, 9> harr{env, homography};
   if (harr.size() != 9) {
     illegalArgEx.Throw(env, "homography array must be size 9");
     return nullptr;
   }
 
   AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
-  return MakeJObject(env, estimator.EstimateHomography(
-                              std::span<const double, 9>{harr.array()}));
+  return MakeJObject(env, estimator.EstimateHomography(harr));
 }
 
 /*
@@ -530,7 +529,7 @@
     nullPointerEx.Throw(env, "homography cannot be null");
     return nullptr;
   }
-  JDoubleArrayRef harr{env, homography};
+  JSpan<const jdouble, 9> harr{env, homography};
   if (harr.size() != 9) {
     illegalArgEx.Throw(env, "homography array must be size 9");
     return nullptr;
@@ -541,7 +540,7 @@
     nullPointerEx.Throw(env, "corners cannot be null");
     return nullptr;
   }
-  JDoubleArrayRef carr{env, corners};
+  JSpan<const jdouble, 8> carr{env, corners};
   if (carr.size() != 8) {
     illegalArgEx.Throw(env, "corners array must be size 8");
     return nullptr;
@@ -549,9 +548,7 @@
 
   AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
   return MakeJObject(env,
-                     estimator.EstimateOrthogonalIteration(
-                         std::span<const double, 9>{harr.array()},
-                         std::span<const double, 8>{carr.array()}, nIters));
+                     estimator.EstimateOrthogonalIteration(harr, carr, nIters));
 }
 
 /*
@@ -569,7 +566,7 @@
     nullPointerEx.Throw(env, "homography cannot be null");
     return nullptr;
   }
-  JDoubleArrayRef harr{env, homography};
+  JSpan<const jdouble, 9> harr{env, homography};
   if (harr.size() != 9) {
     illegalArgEx.Throw(env, "homography array must be size 9");
     return nullptr;
@@ -580,16 +577,14 @@
     nullPointerEx.Throw(env, "corners cannot be null");
     return nullptr;
   }
-  JDoubleArrayRef carr{env, corners};
+  JSpan<const jdouble, 8> carr{env, corners};
   if (carr.size() != 8) {
     illegalArgEx.Throw(env, "corners array must be size 8");
     return nullptr;
   }
 
   AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
-  return MakeJObject(
-      env, estimator.Estimate(std::span<const double, 9>{harr.array()},
-                              std::span<const double, 8>{carr.array()}));
+  return MakeJObject(env, estimator.Estimate(harr, carr));
 }
 
 }  // extern "C"
diff --git a/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTag.h b/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTag.h
index fc57231..ba24530 100644
--- a/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTag.h
+++ b/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTag.h
@@ -5,13 +5,10 @@
 #pragma once
 
 #include <wpi/SymbolExports.h>
+#include <wpi/json_fwd.h>
 
 #include "frc/geometry/Pose3d.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace frc {
 
 struct WPILIB_DLLEXPORT AprilTag {
diff --git a/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagDetection.h b/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagDetection.h
index 5225f21..babcbd8 100644
--- a/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagDetection.h
+++ b/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagDetection.h
@@ -9,10 +9,9 @@
 #include <span>
 #include <string_view>
 
+#include <Eigen/Core>
 #include <wpi/SymbolExports.h>
 
-#include "frc/EigenCore.h"
-
 namespace frc {
 
 /**
diff --git a/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagFieldLayout.h b/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagFieldLayout.h
index c69e429..ddc1fed 100644
--- a/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagFieldLayout.h
+++ b/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagFieldLayout.h
@@ -11,14 +11,11 @@
 
 #include <units/length.h>
 #include <wpi/SymbolExports.h>
+#include <wpi/json_fwd.h>
 
 #include "frc/apriltag/AprilTag.h"
 #include "frc/geometry/Pose3d.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace frc {
 /**
  * Class for representing a layout of AprilTags on a field and reading them from
@@ -34,7 +31,11 @@
  * Pose3ds in the JSON are measured using the normal FRC coordinate system, NWU
  * with the origin at the bottom-right corner of the blue alliance wall.
  * SetOrigin(OriginPosition) can be used to change the poses returned from
- * GetTagPose(int) to be from the perspective of a specific alliance. */
+ * GetTagPose(int) to be from the perspective of a specific alliance.
+ *
+ * Tag poses represent the center of the tag, with a zero rotation representing
+ * a tag that is upright and facing away from the (blue) alliance wall (that is,
+ * towards the opposing alliance). */
 class WPILIB_DLLEXPORT AprilTagFieldLayout {
  public:
   enum class OriginPosition {
@@ -62,6 +63,18 @@
                       units::meter_t fieldLength, units::meter_t fieldWidth);
 
   /**
+   * Returns the length of the field the layout is representing.
+   * @return length
+   */
+  units::meter_t GetFieldLength() const;
+
+  /**
+   * Returns the length of the field the layout is representing.
+   * @return width
+   */
+  units::meter_t GetFieldWidth() const;
+
+  /**
    * Sets the origin based on a predefined enumeration of coordinate frame
    * origins. The origins are calculated from the field dimensions.
    *
@@ -75,7 +88,7 @@
   /**
    * Sets the origin for tag pose transformation.
    *
-   * This tranforms the Pose3ds returned by GetTagPose(int) to return the
+   * This transforms the Pose3ds returned by GetTagPose(int) to return the
    * correct pose relative to the provided origin.
    *
    * @param origin The new origin for tag transformations
@@ -83,6 +96,12 @@
   void SetOrigin(const Pose3d& origin);
 
   /**
+   * Returns the origin used for tag pose transformation.
+   * @return the origin
+   */
+  Pose3d GetOrigin() const;
+
+  /**
    * Gets an AprilTag pose by its ID.
    *
    * @param ID The ID of the tag.
diff --git a/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagFields.h b/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagFields.h
index 2810a1a..4bab299 100644
--- a/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagFields.h
+++ b/third_party/allwpilib/apriltag/src/main/native/include/frc/apriltag/AprilTagFields.h
@@ -14,6 +14,7 @@
 
 enum class AprilTagField {
   k2022RapidReact,
+  k2023ChargedUp,
 
   // This is a placeholder for denoting the last supported field. This should
   // always be the last entry in the enum and should not be used by users
diff --git a/third_party/allwpilib/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2022-rapidreact.json b/third_party/allwpilib/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2022-rapidreact.json
index 78cb523..1e6fa7f 100644
--- a/third_party/allwpilib/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2022-rapidreact.json
+++ b/third_party/allwpilib/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2022-rapidreact.json
@@ -1,415 +1,440 @@
 {
-  "tags" : [ {
-    "ID" : 0,
-    "pose" : {
-      "translation" : {
-        "x" : -0.0035306,
-        "y" : 7.578928199999999,
-        "z" : 0.8858503999999999
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 1.0,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 0.0
+  "tags": [
+    {
+      "ID": 0,
+      "pose": {
+        "translation": {
+          "x": -0.0035306,
+          "y": 7.578928199999999,
+          "z": 0.8858503999999999
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 1.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 1,
+      "pose": {
+        "translation": {
+          "x": 3.2327088,
+          "y": 5.486654,
+          "z": 1.7254728
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 1.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 2,
+      "pose": {
+        "translation": {
+          "x": 3.067812,
+          "y": 5.3305202,
+          "z": 1.3762228
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.7071067811865476,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": -0.7071067811865475
+          }
+        }
+      }
+    },
+    {
+      "ID": 3,
+      "pose": {
+        "translation": {
+          "x": 0.0039878,
+          "y": 5.058536999999999,
+          "z": 0.80645
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 1.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 4,
+      "pose": {
+        "translation": {
+          "x": 0.0039878,
+          "y": 3.5124898,
+          "z": 0.80645
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 1.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 5,
+      "pose": {
+        "translation": {
+          "x": 0.12110719999999998,
+          "y": 1.7178274,
+          "z": 0.8906002000000001
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.9196502204050923,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.39273842708457407
+          }
+        }
+      }
+    },
+    {
+      "ID": 6,
+      "pose": {
+        "translation": {
+          "x": 0.8733027999999999,
+          "y": 0.9412985999999999,
+          "z": 0.8906002000000001
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.9196502204050923,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.39273842708457407
+          }
+        }
+      }
+    },
+    {
+      "ID": 7,
+      "pose": {
+        "translation": {
+          "x": 1.6150844,
+          "y": 0.15725139999999999,
+          "z": 0.8906002000000001
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.9196502204050923,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.39273842708457407
+          }
+        }
+      }
+    },
+    {
+      "ID": 10,
+      "pose": {
+        "translation": {
+          "x": 16.4627306,
+          "y": 0.6506718,
+          "z": 0.8858503999999999
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 6.123233995736766E-17,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 1.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 11,
+      "pose": {
+        "translation": {
+          "x": 13.2350002,
+          "y": 2.743454,
+          "z": 1.7254728
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 6.123233995736766E-17,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 1.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 12,
+      "pose": {
+        "translation": {
+          "x": 13.391388000000001,
+          "y": 2.8998418,
+          "z": 1.3762228
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.7071067811865476,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.7071067811865475
+          }
+        }
+      }
+    },
+    {
+      "ID": 13,
+      "pose": {
+        "translation": {
+          "x": 16.4552122,
+          "y": 3.1755079999999998,
+          "z": 0.80645
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 6.123233995736766E-17,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 1.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 14,
+      "pose": {
+        "translation": {
+          "x": 16.4552122,
+          "y": 4.7171356,
+          "z": 0.80645
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 6.123233995736766E-17,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 1.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 15,
+      "pose": {
+        "translation": {
+          "x": 16.3350194,
+          "y": 6.5149729999999995,
+          "z": 0.8937752
+        },
+        "rotation": {
+          "quaternion": {
+            "W": -0.37298778257580906,
+            "X": -0.0,
+            "Y": 0.0,
+            "Z": 0.9278362538989199
+          }
+        }
+      }
+    },
+    {
+      "ID": 16,
+      "pose": {
+        "translation": {
+          "x": 15.5904946,
+          "y": 7.292695599999999,
+          "z": 0.8906002000000001
+        },
+        "rotation": {
+          "quaternion": {
+            "W": -0.37298778257580906,
+            "X": -0.0,
+            "Y": 0.0,
+            "Z": 0.9278362538989199
+          }
+        }
+      }
+    },
+    {
+      "ID": 17,
+      "pose": {
+        "translation": {
+          "x": 14.847188999999998,
+          "y": 8.0691228,
+          "z": 0.8906002000000001
+        },
+        "rotation": {
+          "quaternion": {
+            "W": -0.37298778257580906,
+            "X": -0.0,
+            "Y": 0.0,
+            "Z": 0.9278362538989199
+          }
+        }
+      }
+    },
+    {
+      "ID": 40,
+      "pose": {
+        "translation": {
+          "x": 7.874127,
+          "y": 4.9131728,
+          "z": 0.7032752
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.5446390350150271,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.838670567945424
+          }
+        }
+      }
+    },
+    {
+      "ID": 41,
+      "pose": {
+        "translation": {
+          "x": 7.4312271999999995,
+          "y": 3.759327,
+          "z": 0.7032752
+        },
+        "rotation": {
+          "quaternion": {
+            "W": -0.20791169081775934,
+            "X": -0.0,
+            "Y": 0.0,
+            "Z": 0.9781476007338057
+          }
+        }
+      }
+    },
+    {
+      "ID": 42,
+      "pose": {
+        "translation": {
+          "x": 8.585073,
+          "y": 3.3164272,
+          "z": 0.7032752
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.838670567945424,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": -0.5446390350150271
+          }
+        }
+      }
+    },
+    {
+      "ID": 43,
+      "pose": {
+        "translation": {
+          "x": 9.0279728,
+          "y": 4.470273,
+          "z": 0.7032752
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.9781476007338057,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.20791169081775934
+          }
+        }
+      }
+    },
+    {
+      "ID": 50,
+      "pose": {
+        "translation": {
+          "x": 7.6790296,
+          "y": 4.3261534,
+          "z": 2.4177244
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.17729273396782605,
+            "X": -0.22744989571511945,
+            "Y": 0.04215534644161733,
+            "Z": 0.9565859910053995
+          }
+        }
+      }
+    },
+    {
+      "ID": 51,
+      "pose": {
+        "translation": {
+          "x": 8.0182466,
+          "y": 3.5642296,
+          "z": 2.4177244
+        },
+        "rotation": {
+          "quaternion": {
+            "W": -0.5510435465842192,
+            "X": -0.19063969497246985,
+            "Y": -0.13102303230819815,
+            "Z": 0.8017733354717242
+          }
+        }
+      }
+    },
+    {
+      "ID": 52,
+      "pose": {
+        "translation": {
+          "x": 8.7801704,
+          "y": 3.9034466,
+          "z": 2.4177244
+        },
+        "rotation": {
+          "quaternion": {
+            "W": -0.9565859910053994,
+            "X": -0.04215534644161739,
+            "Y": -0.22744989571511942,
+            "Z": 0.17729273396782633
+          }
+        }
+      }
+    },
+    {
+      "ID": 53,
+      "pose": {
+        "translation": {
+          "x": 8.4409534,
+          "y": 4.6653704,
+          "z": 2.4177244
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.8017733354717241,
+            "X": -0.1310230323081982,
+            "Y": 0.19063969497246983,
+            "Z": 0.5510435465842194
+          }
         }
       }
     }
-  }, {
-    "ID" : 1,
-    "pose" : {
-      "translation" : {
-        "x" : 3.2327088,
-        "y" : 5.486654,
-        "z" : 1.7254728
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 1.0,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 0.0
-        }
-      }
-    }
-  }, {
-    "ID" : 2,
-    "pose" : {
-      "translation" : {
-        "x" : 3.067812,
-        "y" : 5.3305202,
-        "z" : 1.3762228
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 0.7071067811865476,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : -0.7071067811865475
-        }
-      }
-    }
-  }, {
-    "ID" : 3,
-    "pose" : {
-      "translation" : {
-        "x" : 0.0039878,
-        "y" : 5.058536999999999,
-        "z" : 0.80645
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 1.0,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 0.0
-        }
-      }
-    }
-  }, {
-    "ID" : 4,
-    "pose" : {
-      "translation" : {
-        "x" : 0.0039878,
-        "y" : 3.5124898,
-        "z" : 0.80645
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 1.0,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 0.0
-        }
-      }
-    }
-  }, {
-    "ID" : 5,
-    "pose" : {
-      "translation" : {
-        "x" : 0.12110719999999998,
-        "y" : 1.7178274,
-        "z" : 0.8906002000000001
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 0.9196502204050923,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 0.39273842708457407
-        }
-      }
-    }
-  }, {
-    "ID" : 6,
-    "pose" : {
-      "translation" : {
-        "x" : 0.8733027999999999,
-        "y" : 0.9412985999999999,
-        "z" : 0.8906002000000001
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 0.9196502204050923,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 0.39273842708457407
-        }
-      }
-    }
-  }, {
-    "ID" : 7,
-    "pose" : {
-      "translation" : {
-        "x" : 1.6150844,
-        "y" : 0.15725139999999999,
-        "z" : 0.8906002000000001
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 0.9196502204050923,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 0.39273842708457407
-        }
-      }
-    }
-  }, {
-    "ID" : 10,
-    "pose" : {
-      "translation" : {
-        "x" : 16.4627306,
-        "y" : 0.6506718,
-        "z" : 0.8858503999999999
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 6.123233995736766E-17,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 1.0
-        }
-      }
-    }
-  }, {
-    "ID" : 11,
-    "pose" : {
-      "translation" : {
-        "x" : 13.2350002,
-        "y" : 2.743454,
-        "z" : 1.7254728
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 6.123233995736766E-17,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 1.0
-        }
-      }
-    }
-  }, {
-    "ID" : 12,
-    "pose" : {
-      "translation" : {
-        "x" : 13.391388000000001,
-        "y" : 2.8998418,
-        "z" : 1.3762228
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 0.7071067811865476,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 0.7071067811865475
-        }
-      }
-    }
-  }, {
-    "ID" : 13,
-    "pose" : {
-      "translation" : {
-        "x" : 16.4552122,
-        "y" : 3.1755079999999998,
-        "z" : 0.80645
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 6.123233995736766E-17,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 1.0
-        }
-      }
-    }
-  }, {
-    "ID" : 14,
-    "pose" : {
-      "translation" : {
-        "x" : 16.4552122,
-        "y" : 4.7171356,
-        "z" : 0.80645
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 6.123233995736766E-17,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 1.0
-        }
-      }
-    }
-  }, {
-    "ID" : 15,
-    "pose" : {
-      "translation" : {
-        "x" : 16.3350194,
-        "y" : 6.5149729999999995,
-        "z" : 0.8937752
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : -0.37298778257580906,
-          "X" : -0.0,
-          "Y" : 0.0,
-          "Z" : 0.9278362538989199
-        }
-      }
-    }
-  }, {
-    "ID" : 16,
-    "pose" : {
-      "translation" : {
-        "x" : 15.5904946,
-        "y" : 7.292695599999999,
-        "z" : 0.8906002000000001
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : -0.37298778257580906,
-          "X" : -0.0,
-          "Y" : 0.0,
-          "Z" : 0.9278362538989199
-        }
-      }
-    }
-  }, {
-    "ID" : 17,
-    "pose" : {
-      "translation" : {
-        "x" : 14.847188999999998,
-        "y" : 8.0691228,
-        "z" : 0.8906002000000001
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : -0.37298778257580906,
-          "X" : -0.0,
-          "Y" : 0.0,
-          "Z" : 0.9278362538989199
-        }
-      }
-    }
-  }, {
-    "ID" : 40,
-    "pose" : {
-      "translation" : {
-        "x" : 7.874127,
-        "y" : 4.9131728,
-        "z" : 0.7032752
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 0.5446390350150271,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 0.838670567945424
-        }
-      }
-    }
-  }, {
-    "ID" : 41,
-    "pose" : {
-      "translation" : {
-        "x" : 7.4312271999999995,
-        "y" : 3.759327,
-        "z" : 0.7032752
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : -0.20791169081775934,
-          "X" : -0.0,
-          "Y" : 0.0,
-          "Z" : 0.9781476007338057
-        }
-      }
-    }
-  }, {
-    "ID" : 42,
-    "pose" : {
-      "translation" : {
-        "x" : 8.585073,
-        "y" : 3.3164272,
-        "z" : 0.7032752
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 0.838670567945424,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : -0.5446390350150271
-        }
-      }
-    }
-  }, {
-    "ID" : 43,
-    "pose" : {
-      "translation" : {
-        "x" : 9.0279728,
-        "y" : 4.470273,
-        "z" : 0.7032752
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 0.9781476007338057,
-          "X" : 0.0,
-          "Y" : 0.0,
-          "Z" : 0.20791169081775934
-        }
-      }
-    }
-  }, {
-    "ID" : 50,
-    "pose" : {
-      "translation" : {
-        "x" : 7.6790296,
-        "y" : 4.3261534,
-        "z" : 2.4177244
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 0.17729273396782605,
-          "X" : -0.22744989571511945,
-          "Y" : 0.04215534644161733,
-          "Z" : 0.9565859910053995
-        }
-      }
-    }
-  }, {
-    "ID" : 51,
-    "pose" : {
-      "translation" : {
-        "x" : 8.0182466,
-        "y" : 3.5642296,
-        "z" : 2.4177244
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : -0.5510435465842192,
-          "X" : -0.19063969497246985,
-          "Y" : -0.13102303230819815,
-          "Z" : 0.8017733354717242
-        }
-      }
-    }
-  }, {
-    "ID" : 52,
-    "pose" : {
-      "translation" : {
-        "x" : 8.7801704,
-        "y" : 3.9034466,
-        "z" : 2.4177244
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : -0.9565859910053994,
-          "X" : -0.04215534644161739,
-          "Y" : -0.22744989571511942,
-          "Z" : 0.17729273396782633
-        }
-      }
-    }
-  }, {
-    "ID" : 53,
-    "pose" : {
-      "translation" : {
-        "x" : 8.4409534,
-        "y" : 4.6653704,
-        "z" : 2.4177244
-      },
-      "rotation" : {
-        "quaternion" : {
-          "W" : 0.8017733354717241,
-          "X" : -0.1310230323081982,
-          "Y" : 0.19063969497246983,
-          "Z" : 0.5510435465842194
-        }
-      }
-    }
-  } ],
-  "field" : {
-    "length" : 16.4592,
-    "width" : 8.2296
+  ],
+  "field": {
+    "length": 16.4592,
+    "width": 8.2296
   }
 }
diff --git a/third_party/allwpilib/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2023-chargedup.json b/third_party/allwpilib/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2023-chargedup.json
new file mode 100644
index 0000000..5ce7288
--- /dev/null
+++ b/third_party/allwpilib/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2023-chargedup.json
@@ -0,0 +1,152 @@
+{
+  "tags": [
+    {
+      "ID": 1,
+      "pose": {
+        "translation": {
+          "x": 15.513558,
+          "y": 1.071626,
+          "z": 0.462788
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 1.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 2,
+      "pose": {
+        "translation": {
+          "x": 15.513558,
+          "y": 2.748026,
+          "z": 0.462788
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 1.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 3,
+      "pose": {
+        "translation": {
+          "x": 15.513558,
+          "y": 4.424426,
+          "z": 0.462788
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 1.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 4,
+      "pose": {
+        "translation": {
+          "x": 16.178784,
+          "y": 6.749796,
+          "z": 0.695452
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 0.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 1.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 5,
+      "pose": {
+        "translation": {
+          "x": 0.36195,
+          "y": 6.749796,
+          "z": 0.695452
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 1.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 6,
+      "pose": {
+        "translation": {
+          "x": 1.02743,
+          "y": 4.424426,
+          "z": 0.462788
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 1.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 7,
+      "pose": {
+        "translation": {
+          "x": 1.02743,
+          "y": 2.748026,
+          "z": 0.462788
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 1.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.0
+          }
+        }
+      }
+    },
+    {
+      "ID": 8,
+      "pose": {
+        "translation": {
+          "x": 1.02743,
+          "y": 1.071626,
+          "z": 0.462788
+        },
+        "rotation": {
+          "quaternion": {
+            "W": 1.0,
+            "X": 0.0,
+            "Y": 0.0,
+            "Z": 0.0
+          }
+        }
+      }
+    }
+  ],
+  "field": {
+    "length": 16.54175,
+    "width": 8.0137
+  }
+}
diff --git a/third_party/allwpilib/apriltag/src/test/java/edu/wpi/first/apriltag/AprilTagDetectorTest.java b/third_party/allwpilib/apriltag/src/test/java/edu/wpi/first/apriltag/AprilTagDetectorTest.java
index e91e0b4..dc97c1b 100644
--- a/third_party/allwpilib/apriltag/src/test/java/edu/wpi/first/apriltag/AprilTagDetectorTest.java
+++ b/third_party/allwpilib/apriltag/src/test/java/edu/wpi/first/apriltag/AprilTagDetectorTest.java
@@ -157,7 +157,7 @@
 
       var estimator =
           new AprilTagPoseEstimator(new AprilTagPoseEstimator.Config(0.2, 500, 500, 320, 240));
-      AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 50);
+      AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 200);
       assertEquals(new Transform3d(), est.pose2);
       Transform3d pose = estimator.estimate(results[0]);
       assertEquals(est.pose1, pose);
@@ -189,7 +189,7 @@
           new AprilTagPoseEstimator(
               new AprilTagPoseEstimator.Config(
                   0.2, 500, 500, image.cols() / 2.0, image.rows() / 2.0));
-      AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 50);
+      AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 200);
 
       assertEquals(Units.degreesToRadians(45), est.pose1.getRotation().getX(), 0.1);
       assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getY(), 0.1);
@@ -222,7 +222,7 @@
           new AprilTagPoseEstimator(
               new AprilTagPoseEstimator.Config(
                   0.2, 500, 500, image.cols() / 2.0, image.rows() / 2.0));
-      AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 50);
+      AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 200);
 
       assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getX(), 0.1);
       assertEquals(Units.degreesToRadians(45), est.pose1.getRotation().getY(), 0.1);
@@ -252,7 +252,7 @@
           new AprilTagPoseEstimator(
               new AprilTagPoseEstimator.Config(
                   0.2, 500, 500, image.cols() / 2.0, image.rows() / 2.0));
-      AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 50);
+      AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 200);
 
       assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getX(), 0.1);
       assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getY(), 0.1);
diff --git a/third_party/allwpilib/apriltag/src/test/java/edu/wpi/first/apriltag/LoadConfigTest.java b/third_party/allwpilib/apriltag/src/test/java/edu/wpi/first/apriltag/LoadConfigTest.java
index ab15994..901c72b 100644
--- a/third_party/allwpilib/apriltag/src/test/java/edu/wpi/first/apriltag/LoadConfigTest.java
+++ b/third_party/allwpilib/apriltag/src/test/java/edu/wpi/first/apriltag/LoadConfigTest.java
@@ -23,16 +23,13 @@
   @ParameterizedTest
   @EnumSource(AprilTagFields.class)
   void testLoad(AprilTagFields field) {
-    AprilTagFieldLayout layout =
-        Assertions.assertDoesNotThrow(
-            () -> AprilTagFieldLayout.loadFromResource(field.m_resourceFile));
+    AprilTagFieldLayout layout = Assertions.assertDoesNotThrow(field::loadAprilTagLayoutField);
     assertNotNull(layout);
   }
 
   @Test
   void test2022RapidReact() throws IOException {
-    AprilTagFieldLayout layout =
-        AprilTagFieldLayout.loadFromResource(AprilTagFields.k2022RapidReact.m_resourceFile);
+    AprilTagFieldLayout layout = AprilTagFields.k2022RapidReact.loadAprilTagLayoutField();
 
     // Blue Hangar Truss - Hub
     Pose3d expectedPose =
diff --git a/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagDetectorTest.cpp b/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagDetectorTest.cpp
index 8f93bb3..ab43563 100644
--- a/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagDetectorTest.cpp
+++ b/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagDetectorTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/apriltag/AprilTagDetector.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagJsonTest.cpp b/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagJsonTest.cpp
index 157c40f..4216479 100644
--- a/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagJsonTest.cpp
+++ b/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagJsonTest.cpp
@@ -4,12 +4,12 @@
 
 #include <vector>
 
+#include <gtest/gtest.h>
 #include <wpi/json.h>
 
 #include "frc/apriltag/AprilTag.h"
 #include "frc/apriltag/AprilTagFieldLayout.h"
 #include "frc/geometry/Pose3d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagPoseSetOriginTest.cpp b/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagPoseSetOriginTest.cpp
index 3849649..58fd873 100644
--- a/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagPoseSetOriginTest.cpp
+++ b/third_party/allwpilib/apriltag/src/test/native/cpp/AprilTagPoseSetOriginTest.cpp
@@ -4,12 +4,12 @@
 
 #include <vector>
 
+#include <gtest/gtest.h>
 #include <wpi/json.h>
 
 #include "frc/apriltag/AprilTag.h"
 #include "frc/apriltag/AprilTagFieldLayout.h"
 #include "frc/geometry/Pose3d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/apriltag/src/test/native/cpp/LoadConfigTest.cpp b/third_party/allwpilib/apriltag/src/test/native/cpp/LoadConfigTest.cpp
index 9db61e9..c3ff7c0 100644
--- a/third_party/allwpilib/apriltag/src/test/native/cpp/LoadConfigTest.cpp
+++ b/third_party/allwpilib/apriltag/src/test/native/cpp/LoadConfigTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/apriltag/AprilTagFields.h"
-#include "gtest/gtest.h"
 
 namespace frc {
 
diff --git a/third_party/allwpilib/apriltag/src/test/native/cpp/main.cpp b/third_party/allwpilib/apriltag/src/test/native/cpp/main.cpp
index 09072ee..e993c1f 100644
--- a/third_party/allwpilib/apriltag/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/apriltag/src/test/native/cpp/main.cpp
@@ -2,7 +2,7 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/third_party/allwpilib/build.gradle b/third_party/allwpilib/build.gradle
index ed2c520..b1c3ca2 100644
--- a/third_party/allwpilib/build.gradle
+++ b/third_party/allwpilib/build.gradle
@@ -7,7 +7,7 @@
         }
     }
     dependencies {
-        classpath 'com.hubspot.jinjava:jinjava:2.6.0'
+        classpath 'com.hubspot.jinjava:jinjava:2.7.1'
     }
 }
 
@@ -20,10 +20,11 @@
     id 'edu.wpi.first.GradleVsCode'
     id 'idea'
     id 'visual-studio'
-    id 'net.ltgt.errorprone' version '2.0.2' apply false
-    id 'com.github.johnrengelman.shadow' version '7.1.2' apply false
-    id 'com.diffplug.spotless' version '6.12.0' apply false
-    id 'com.github.spotbugs' version '5.0.8' apply false
+    id 'net.ltgt.errorprone' version '3.1.0' apply false
+    id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
+    id 'com.diffplug.spotless' version '6.20.0' apply false
+    id 'com.github.spotbugs' version '5.1.3' apply false
+    id 'com.google.protobuf' version '0.9.3' apply false
 }
 
 wpilibVersioning.buildServerMode = project.hasProperty('buildServer')
@@ -94,8 +95,8 @@
         return
     }
     copyAllOutputs.dependsOn task
-    copyAllOutputs.inputs.file task.archivePath
-    copyAllOutputs.from task.archivePath
+    copyAllOutputs.inputs.file task.archiveFile
+    copyAllOutputs.from task.archiveFile
 }
 
 subprojects {
@@ -109,8 +110,10 @@
     }
 
     plugins.withType(JavaPlugin) {
-        sourceCompatibility = 11
-        targetCompatibility = 11
+        java {
+            sourceCompatibility = 11
+            targetCompatibility = 11
+        }
     }
 
     apply from: "${rootDir}/shared/java/javastyle.gradle"
@@ -126,6 +129,7 @@
 
     tasks.withType(JavaCompile) {
         options.compilerArgs.add '-XDstringConcat=inline'
+        options.encoding = 'UTF-8'
     }
 
     // Enables UTF-8 support in Javadoc
@@ -167,5 +171,5 @@
 }
 
 wrapper {
-    gradleVersion = '7.5.1'
+    gradleVersion = '8.4'
 }
diff --git a/third_party/allwpilib/buildSrc/build.gradle b/third_party/allwpilib/buildSrc/build.gradle
index 5886aec..2c52479 100644
--- a/third_party/allwpilib/buildSrc/build.gradle
+++ b/third_party/allwpilib/buildSrc/build.gradle
@@ -9,5 +9,5 @@
     }
 }
 dependencies {
-    implementation "edu.wpi.first:native-utils:2023.11.1"
+    implementation "edu.wpi.first:native-utils:2024.3.1"
 }
diff --git a/third_party/allwpilib/buildSrc/src/main/groovy/MultiBuilds.groovy b/third_party/allwpilib/buildSrc/src/main/groovy/MultiBuilds.groovy
index 31530b8..2ce5ddb 100644
--- a/third_party/allwpilib/buildSrc/src/main/groovy/MultiBuilds.groovy
+++ b/third_party/allwpilib/buildSrc/src/main/groovy/MultiBuilds.groovy
@@ -9,6 +9,7 @@
 import org.gradle.language.base.plugins.ComponentModelBasePlugin;
 import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask;
 import org.gradle.model.ModelMap;
+import org.gradle.internal.os.OperatingSystem
 import edu.wpi.first.toolchain.ToolchainExtension
 import org.gradle.model.Mutate;
 import org.gradle.api.plugins.ExtensionContainer;
@@ -60,12 +61,16 @@
     @CompileStatic
     void disableReleaseGoogleTest(BinaryContainer binaries, ProjectLayout projectLayout) {
       def project = (Project) projectLayout.projectIdentifier
-      if (project.hasProperty('testRelease')) {
+      if (project.hasProperty('testOther')) {
         return
       }
+      def check_string = 'release'
+      if (project.hasProperty('buildServer') && !OperatingSystem.current().isWindows()) {
+        check_string = 'debug'
+      }
       binaries.withType(GoogleTestTestSuiteBinarySpec) { oSpec ->
         GoogleTestTestSuiteBinarySpec spec = (GoogleTestTestSuiteBinarySpec) oSpec
-        if (spec.buildType.name == 'release') {
+        if (spec.buildType.name == check_string) {
           Rules.setBuildableFalseDynamically(spec)
         }
       }
diff --git a/third_party/allwpilib/buildSrc/src/main/groovy/WPIJREArtifact.groovy b/third_party/allwpilib/buildSrc/src/main/groovy/WPIJREArtifact.groovy
index 1a0139b..eda268b 100644
--- a/third_party/allwpilib/buildSrc/src/main/groovy/WPIJREArtifact.groovy
+++ b/third_party/allwpilib/buildSrc/src/main/groovy/WPIJREArtifact.groovy
@@ -28,7 +28,7 @@
 

     private boolean checkJreVersion = true;

 

-    private final String artifactLocation = "edu.wpi.first.jdk:roborio-2023:17.0.5u7-1"

+    private final String artifactLocation = "edu.wpi.first.jdk:roborio-2024:17.0.9u7-1"

 

     @Inject

     public WPIJREArtifact(String name, RemoteTarget target) {

diff --git a/third_party/allwpilib/cameraserver/.styleguide b/third_party/allwpilib/cameraserver/.styleguide
index 63515d2..951f083 100644
--- a/third_party/allwpilib/cameraserver/.styleguide
+++ b/third_party/allwpilib/cameraserver/.styleguide
@@ -13,6 +13,7 @@
 
 includeOtherLibs {
   ^fmt/
+  ^gtest/
   ^hal/
   ^networktables/
   ^opencv2/
diff --git a/third_party/allwpilib/cameraserver/CMakeLists.txt b/third_party/allwpilib/cameraserver/CMakeLists.txt
index 4916be3..177e009 100644
--- a/third_party/allwpilib/cameraserver/CMakeLists.txt
+++ b/third_party/allwpilib/cameraserver/CMakeLists.txt
@@ -28,6 +28,22 @@
 
 endif()
 
+if (WITH_JAVA_SOURCE)
+    find_package(Java REQUIRED)
+    include(UseJava)
+    file(GLOB CAMERASERVER_SOURCES src/main/java/edu/wpi/first/cameraserver/*.java)
+    file(GLOB VISION_SOURCES src/main/java/edu/wpi/first/vision/*.java)
+    add_jar(cameraserver_src_jar
+    RESOURCES NAMESPACE "edu/wpi/first/cameraserver" ${CAMERASERVER_SOURCES}
+    NAMESPACE "edu/wpi/first/vision" ${VISION_SOURCES}
+    OUTPUT_NAME cameraserver-sources)
+
+    get_property(CAMERASERVER_SRC_JAR_FILE TARGET cameraserver_src_jar PROPERTY JAR_FILE)
+    install(FILES ${CAMERASERVER_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+    set_property(TARGET cameraserver_src_jar PROPERTY FOLDER "java")
+endif()
+
 file(GLOB_RECURSE
     cameraserver_native_src src/main/native/cpp/*.cpp)
 add_library(cameraserver ${cameraserver_native_src})
@@ -40,13 +56,9 @@
 
 set_property(TARGET cameraserver PROPERTY FOLDER "libraries")
 
-install(TARGETS cameraserver EXPORT cameraserver DESTINATION "${main_lib_dest}")
+install(TARGETS cameraserver EXPORT cameraserver)
 install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/cameraserver")
 
-if (WITH_JAVA AND MSVC)
-    install(TARGETS cameraserver RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-endif()
-
 if (WITH_FLAT_INSTALL)
     set (cameraserver_config_dir ${wpilib_dest})
 else()
diff --git a/third_party/allwpilib/cameraserver/build.gradle b/third_party/allwpilib/cameraserver/build.gradle
index 3f4b208..9075979 100644
--- a/third_party/allwpilib/cameraserver/build.gradle
+++ b/third_party/allwpilib/cameraserver/build.gradle
@@ -14,10 +14,6 @@
     implementation project(':wpinet')
     implementation project(':ntcore')
     implementation project(':cscore')
-    devImplementation project(':wpiutil')
-    devImplementation project(':wpinet')
-    devImplementation project(':ntcore')
-    devImplementation project(':cscore')
 }
 
 ext {
diff --git a/third_party/allwpilib/cameraserver/multiCameraServer/build.gradle b/third_party/allwpilib/cameraserver/multiCameraServer/build.gradle
index 4aafd26..d13637c 100644
--- a/third_party/allwpilib/cameraserver/multiCameraServer/build.gradle
+++ b/third_party/allwpilib/cameraserver/multiCameraServer/build.gradle
@@ -18,7 +18,9 @@
 
 apply from: "${rootDir}/shared/opencv.gradle"
 
-mainClassName = 'edu.wpi.Main'
+application {
+    mainClass = 'edu.wpi.Main'
+}
 
 apply plugin: 'com.github.johnrengelman.shadow'
 
@@ -29,7 +31,7 @@
 }
 
 dependencies {
-    implementation 'com.google.code.gson:gson:2.8.9'
+    implementation 'com.google.code.gson:gson:2.10.1'
 
     implementation project(':wpiutil')
     implementation project(':wpinet')
@@ -59,6 +61,9 @@
                 lib project: ':cscore', library: 'cscore', linkage: 'static'
                 lib project: ':wpinet', library: 'wpinet', linkage: 'static'
                 lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    nativeUtils.useRequiredLibrary(binary, 'ni_link_libraries', 'ni_runtime_libraries')
+                }
             }
         }
     }
diff --git a/third_party/allwpilib/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp b/third_party/allwpilib/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp
index 2cb1c89..279eeba 100644
--- a/third_party/allwpilib/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp
+++ b/third_party/allwpilib/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp
@@ -8,11 +8,12 @@
 #include <thread>
 #include <vector>
 
+#include <fmt/core.h>
 #include <networktables/NetworkTableInstance.h>
+#include <wpi/MemoryBuffer.h>
 #include <wpi/StringExtras.h>
 #include <wpi/fmt/raw_ostream.h>
 #include <wpi/json.h>
-#include <wpi/raw_istream.h>
 #include <wpi/raw_ostream.h>
 
 #include "cameraserver/CameraServer.h"
@@ -63,10 +64,6 @@
 
 std::vector<CameraConfig> cameras;
 
-wpi::raw_ostream& ParseError() {
-  return wpi::errs() << "config error in '" << configFile << "': ";
-}
-
 bool ReadCameraConfig(const wpi::json& config) {
   CameraConfig c;
 
@@ -74,7 +71,8 @@
   try {
     c.name = config.at("name").get<std::string>();
   } catch (const wpi::json::exception& e) {
-    ParseError() << "could not read camera name: " << e.what() << '\n';
+    fmt::print(stderr, "config error in '{}': could not read camera name: {}\n",
+               configFile, e.what());
     return false;
   }
 
@@ -82,8 +80,9 @@
   try {
     c.path = config.at("path").get<std::string>();
   } catch (const wpi::json::exception& e) {
-    ParseError() << "camera '" << c.name
-                 << "': could not read path: " << e.what() << '\n';
+    fmt::print(stderr,
+               "config error in '{}': camera '{}': could not read path: {}\n",
+               configFile, c.name, e.what());
     return false;
   }
 
@@ -96,25 +95,27 @@
 bool ReadConfig() {
   // open config file
   std::error_code ec;
-  wpi::raw_fd_istream is(configFile, ec);
-  if (ec) {
-    wpi::errs() << "could not open '" << configFile << "': " << ec.message()
-                << '\n';
+  std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+      wpi::MemoryBuffer::GetFile(configFile, ec);
+  if (fileBuffer == nullptr || ec) {
+    fmt::print(stderr, "could not open '{}': {}\n", configFile, ec.message());
     return false;
   }
 
   // parse file
   wpi::json j;
   try {
-    j = wpi::json::parse(is);
+    j = wpi::json::parse(fileBuffer->begin(), fileBuffer->end());
   } catch (const wpi::json::parse_error& e) {
-    fmt::print(ParseError(), "byte {}: {}\n", e.byte, e.what());
+    fmt::print(stderr, "config error in '{}': byte {}: {}\n", configFile,
+               e.byte, e.what());
     return false;
   }
 
   // top level must be an object
   if (!j.is_object()) {
-    ParseError() << "must be JSON object\n";
+    fmt::print(stderr, "config error in '{}': must be JSON object\n",
+               configFile);
     return false;
   }
 
@@ -122,7 +123,8 @@
   try {
     team = j.at("team").get<unsigned int>();
   } catch (const wpi::json::exception& e) {
-    ParseError() << "could not read team number: " << e.what() << '\n';
+    fmt::print(stderr, "config error in '{}': could not read team number: {}\n",
+               configFile, e.what());
     return false;
   }
 
@@ -135,10 +137,14 @@
       } else if (wpi::equals_lower(str, "server")) {
         server = true;
       } else {
-        ParseError() << "could not understand ntmode value '" << str << "'\n";
+        fmt::print(
+            stderr,
+            "config error in '{}': could not understand ntmode value '{}'\n",
+            configFile, str);
       }
     } catch (const wpi::json::exception& e) {
-      ParseError() << "could not read ntmode: " << e.what() << '\n';
+      fmt::print(stderr, "config error in '{}': could not read ntmode: {}\n",
+                 configFile, e.what());
     }
   }
 
@@ -150,7 +156,8 @@
       }
     }
   } catch (const wpi::json::exception& e) {
-    ParseError() << "could not read cameras: " << e.what() << '\n';
+    fmt::print(stderr, "config error in '{}': could not read cameras: {}\n",
+               configFile, e.what());
     return false;
   }
 
diff --git a/third_party/allwpilib/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp b/third_party/allwpilib/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp
index c6ecc3c..d6014fa 100644
--- a/third_party/allwpilib/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp
+++ b/third_party/allwpilib/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp
@@ -474,6 +474,7 @@
 }
 
 cs::UsbCamera CameraServer::StartAutomaticCapture(int dev) {
+  ::GetInstance();
   cs::UsbCamera camera{fmt::format("USB Camera {}", dev), dev};
   StartAutomaticCapture(camera);
   auto csShared = GetCameraServerShared();
@@ -483,6 +484,7 @@
 
 cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
                                                   int dev) {
+  ::GetInstance();
   cs::UsbCamera camera{name, dev};
   StartAutomaticCapture(camera);
   auto csShared = GetCameraServerShared();
@@ -492,6 +494,7 @@
 
 cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
                                                   std::string_view path) {
+  ::GetInstance();
   cs::UsbCamera camera{name, path};
   StartAutomaticCapture(camera);
   auto csShared = GetCameraServerShared();
@@ -517,6 +520,7 @@
 
 cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
                                            std::string_view host) {
+  ::GetInstance();
   cs::AxisCamera camera{name, host};
   StartAutomaticCapture(camera);
   auto csShared = GetCameraServerShared();
@@ -526,6 +530,7 @@
 
 cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
                                            const char* host) {
+  ::GetInstance();
   cs::AxisCamera camera{name, host};
   StartAutomaticCapture(camera);
   auto csShared = GetCameraServerShared();
@@ -535,6 +540,7 @@
 
 cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
                                            const std::string& host) {
+  ::GetInstance();
   cs::AxisCamera camera{name, host};
   StartAutomaticCapture(camera);
   auto csShared = GetCameraServerShared();
@@ -544,6 +550,7 @@
 
 cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
                                            std::span<const std::string> hosts) {
+  ::GetInstance();
   cs::AxisCamera camera{name, hosts};
   StartAutomaticCapture(camera);
   auto csShared = GetCameraServerShared();
@@ -552,10 +559,11 @@
 }
 
 cs::MjpegServer CameraServer::AddSwitchedCamera(std::string_view name) {
+  auto& inst = ::GetInstance();
   // create a dummy CvSource
   cs::CvSource source{name, cs::VideoMode::PixelFormat::kMJPEG, 160, 120, 30};
   cs::MjpegServer server = StartAutomaticCapture(source);
-  ::GetInstance().m_fixedSources[server.GetHandle()] = source.GetHandle();
+  inst.m_fixedSources[server.GetHandle()] = source.GetHandle();
 
   return server;
 }
@@ -632,6 +640,7 @@
 
 cs::CvSource CameraServer::PutVideo(std::string_view name, int width,
                                     int height) {
+  ::GetInstance();
   cs::CvSource source{name, cs::VideoMode::kMJPEG, width, height, 30};
   StartAutomaticCapture(source);
   return source;
@@ -648,6 +657,7 @@
 }
 
 cs::MjpegServer CameraServer::AddServer(std::string_view name, int port) {
+  ::GetInstance();
   cs::MjpegServer server{name, port};
   AddServer(server);
   return server;
@@ -707,22 +717,3 @@
   std::scoped_lock lock(inst.m_mutex);
   inst.m_sources.erase(name);
 }
-
-void CameraServer::SetSize(int size) {
-  auto& inst = ::GetInstance();
-  std::scoped_lock lock(inst.m_mutex);
-  if (inst.m_primarySourceName.empty()) {
-    return;
-  }
-  auto it = inst.m_sources.find(inst.m_primarySourceName);
-  if (it == inst.m_sources.end()) {
-    return;
-  }
-  if (size == kSize160x120) {
-    it->second.SetResolution(160, 120);
-  } else if (size == kSize320x240) {
-    it->second.SetResolution(320, 240);
-  } else if (size == kSize640x480) {
-    it->second.SetResolution(640, 480);
-  }
-}
diff --git a/third_party/allwpilib/cameraserver/src/main/native/include/cameraserver/CameraServer.h b/third_party/allwpilib/cameraserver/src/main/native/include/cameraserver/CameraServer.h
index f47d8aa..29e16d7 100644
--- a/third_party/allwpilib/cameraserver/src/main/native/include/cameraserver/CameraServer.h
+++ b/third_party/allwpilib/cameraserver/src/main/native/include/cameraserver/CameraServer.h
@@ -267,17 +267,6 @@
    */
   static void RemoveCamera(std::string_view name);
 
-  /**
-   * Sets the size of the image to use. Use the public kSize constants to set
-   * the correct mode, or set it directly on a camera and call the appropriate
-   * StartAutomaticCapture method.
-   *
-   * @deprecated Use SetResolution on the UsbCamera returned by
-   *             StartAutomaticCapture() instead.
-   * @param size The size to use
-   */
-  static void SetSize(int size);
-
  private:
   CameraServer() = default;
 };
diff --git a/third_party/allwpilib/cameraserver/src/main/native/include/vision/VisionRunner.inc b/third_party/allwpilib/cameraserver/src/main/native/include/vision/VisionRunner.inc
index 9a195ff..56cfe69 100644
--- a/third_party/allwpilib/cameraserver/src/main/native/include/vision/VisionRunner.inc
+++ b/third_party/allwpilib/cameraserver/src/main/native/include/vision/VisionRunner.inc
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <functional>
+
 #include "vision/VisionRunner.h"
 
 namespace frc {
diff --git a/third_party/allwpilib/cmake/modules/CompileWarnings.cmake b/third_party/allwpilib/cmake/modules/CompileWarnings.cmake
index 93b35b8..8f5d54e 100644
--- a/third_party/allwpilib/cmake/modules/CompileWarnings.cmake
+++ b/third_party/allwpilib/cmake/modules/CompileWarnings.cmake
@@ -12,4 +12,17 @@
     elseif(UNIX AND APPLE)
         target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wno-deprecated-anon-enum-enum-conversion>)
     endif()
+
+    # Suppress warning "enumeration types with a fixed underlying type are a
+    # Clang extension"
+    if(APPLE)
+      target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:C>:-Wno-fixed-enum-extension>)
+    endif()
+
+    # Compress debug info with GCC
+    if ((${CMAKE_BUILD_TYPE} STREQUAL "Debug" OR
+         ${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo") AND
+        ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
+      target_compile_options(${target} PRIVATE -gz=zlib)
+    endif()
 endmacro()
diff --git a/third_party/allwpilib/cmake/modules/DownloadAndCheck.cmake b/third_party/allwpilib/cmake/modules/DownloadAndCheck.cmake
new file mode 100644
index 0000000..59c8799
--- /dev/null
+++ b/third_party/allwpilib/cmake/modules/DownloadAndCheck.cmake
@@ -0,0 +1,11 @@
+macro(download_and_check source destination)
+  file(DOWNLOAD ${source} ${destination} STATUS download_status)
+  list(GET download_status 0 status_code)
+  list(GET download_status 1 status_message)
+
+  if(${status_code} EQUAL 0)
+    message(VERBOSE "Download of \"${source}\" successful.")
+  else()
+    message(FATAL_ERROR "Download of \"${source}\" failed: ${status_message}")
+  endif()
+endmacro()
diff --git a/third_party/allwpilib/cmake/modules/GenResources.cmake b/third_party/allwpilib/cmake/modules/GenResources.cmake
index 06d34a3..d80a364 100644
--- a/third_party/allwpilib/cmake/modules/GenResources.cmake
+++ b/third_party/allwpilib/cmake/modules/GenResources.cmake
@@ -1,26 +1,26 @@
-set(SCRIPTS_DIR "${CMAKE_CURRENT_LIST_DIR}/../scripts")
-MACRO(GENERATE_RESOURCES inputDir outputDir prefix namespace outputFiles)
-  FILE(GLOB inputFiles ${inputDir}/*)
-  SET(${outputFiles})
-  FOREACH(input ${inputFiles})
-    GET_FILENAME_COMPONENT(inputBase ${input} NAME)
-    IF("${inputBase}" MATCHES "^\\.")
-      CONTINUE()
-    ENDIF()
-    SET(output "${outputDir}/${inputBase}.cpp")
-    LIST(APPEND ${outputFiles} "${output}")
+set(scripts_dir "${CMAKE_CURRENT_LIST_DIR}/../scripts")
+macro(generate_resources inputDir outputDir prefix namespace outputFiles)
+  file(GLOB inputFiles ${inputDir}/*)
+  set(${outputFiles})
+  foreach(input ${inputFiles})
+    get_filename_component(inputBase ${input} NAME)
+    if("${inputBase}" MATCHES "^\\.")
+      continue()
+    endif()
+    set(output "${outputDir}/${inputBase}.cpp")
+    list(APPEND ${outputFiles} "${output}")
 
-    ADD_CUSTOM_COMMAND(
+    add_custom_command(
       OUTPUT ${output}
       COMMAND ${CMAKE_COMMAND}
         "-Dinput=${input}"
         "-Doutput=${output}"
         "-Dprefix=${prefix}"
         "-Dnamespace=${namespace}"
-        -P "${SCRIPTS_DIR}/GenResource.cmake"
+        -P "${scripts_dir}/GenResource.cmake"
       MAIN_DEPENDENCY ${input}
-      DEPENDS ${SCRIPTS_DIR}/GenResource.cmake
+      DEPENDS ${scripts_dir}/GenResource.cmake
       VERBATIM
     )
-  ENDFOREACH()
-ENDMACRO()
+  endforeach()
+endmacro()
diff --git a/third_party/allwpilib/cmake/modules/SubDirList.cmake b/third_party/allwpilib/cmake/modules/SubDirList.cmake
index b67160e..0776098 100644
--- a/third_party/allwpilib/cmake/modules/SubDirList.cmake
+++ b/third_party/allwpilib/cmake/modules/SubDirList.cmake
@@ -1,17 +1,17 @@
-MACRO(SUBDIR_LIST result curdir)
-  FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
-  SET(dirlist "")
-  FOREACH(child ${children})
-    IF(IS_DIRECTORY ${curdir}/${child})
-      LIST(APPEND dirlist ${child})
-    ENDIF()
-  ENDFOREACH()
-  SET(${result} ${dirlist})
-ENDMACRO()
+macro(subdir_list result curdir)
+  file(GLOB children RELATIVE ${curdir} ${curdir}/*)
+  set(dirlist "")
+  foreach(child ${children})
+    if(IS_DIRECTORY ${curdir}/${child})
+      list(APPEND dirlist ${child})
+    endif()
+  endforeach()
+  set(${result} ${dirlist})
+endmacro()
 
-MACRO(ADD_ALL_SUBDIRECTORIES curdir)
-  SUBDIR_LIST (_SUBPROJECTS ${curdir})
-  FOREACH (dir ${_SUBPROJECTS})
-    ADD_SUBDIRECTORY (${dir})
-  ENDFOREACH ()
-ENDMACRO()
+macro(add_all_subdirectories curdir)
+  subdir_list(_SUBPROJECTS ${curdir})
+  foreach(dir ${_SUBPROJECTS})
+    add_subdirectory(${dir})
+  endforeach()
+endmacro()
diff --git a/third_party/allwpilib/cmake/toolchains/gnu.toolchain.cmake b/third_party/allwpilib/cmake/toolchains/gnu.toolchain.cmake
index b21dad2..dbddbd0 100644
--- a/third_party/allwpilib/cmake/toolchains/gnu.toolchain.cmake
+++ b/third_party/allwpilib/cmake/toolchains/gnu.toolchain.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3.0)
+cmake_minimum_required(VERSION 3.11)
 
 # load settings in case of "try compile"
 set(TOOLCHAIN_CONFIG_FILE "${WPILIB_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain.config.cmake")
diff --git a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/AnalogTest.cpp b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/AnalogTest.cpp
index 3423ffe..3173423 100644
--- a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/AnalogTest.cpp
+++ b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/AnalogTest.cpp
@@ -2,13 +2,13 @@
 // 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 <gtest/gtest.h>
 #include <hal/AnalogInput.h>
 #include <hal/AnalogOutput.h>
 #include <wpi/SmallVector.h>
 
 #include "CrossConnects.h"
 #include "LifetimeWrappers.h"
-#include "gtest/gtest.h"
 
 using namespace hlt;
 
diff --git a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/DIOTest.cpp b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/DIOTest.cpp
index 84d2b9d..c03b7b4 100644
--- a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/DIOTest.cpp
+++ b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/DIOTest.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <hal/DIO.h>
 #include <wpi/SmallVector.h>
 
 #include "CrossConnects.h"
 #include "LifetimeWrappers.h"
-#include "gtest/gtest.h"
 
 using namespace hlt;
 
diff --git a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/DutyCycleTest.cpp b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/DutyCycleTest.cpp
index 830ddc4..3109926 100644
--- a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/DutyCycleTest.cpp
+++ b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/DutyCycleTest.cpp
@@ -2,11 +2,11 @@
 // 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 <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "CrossConnects.h"
 #include "LifetimeWrappers.h"
-#include "gtest/gtest.h"
 
 using namespace hlt;
 
@@ -21,10 +21,10 @@
   ASSERT_EQ(0, status);
 
   // Ensure our PWM is disabled, and set up properly
-  HAL_SetPWMRaw(pwmHandle, 0, &status);
+  HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
   ASSERT_EQ(0, status);
-  HAL_SetPWMConfig(pwmHandle, 2.0, 1.0, 1.0, 0, 0, &status);
-  HAL_SetPWMConfig(pwmHandle, 5.05, 2.525, 2.525, 2.525, 0, &status);
+  HAL_SetPWMConfigMicroseconds(pwmHandle, 2000, 1000, 1000, 0, 0, &status);
+  HAL_SetPWMConfigMicroseconds(pwmHandle, 5050, 2525, 2525, 2525, 0, &status);
   ASSERT_EQ(0, status);
   HAL_SetPWMPeriodScale(pwmHandle, 0, &status);
   ASSERT_EQ(0, status);
@@ -41,8 +41,9 @@
   // Sleep enough time for the frequency to converge
   usleep(3500000);
 
-  ASSERT_NEAR(1000 / 5.05,
-              (double)HAL_GetDutyCycleFrequency(dutyCycle, &status), 1);
+  ASSERT_NEAR(
+      1000 / 5.05,
+      static_cast<double>(HAL_GetDutyCycleFrequency(dutyCycle, &status)), 1);
 
   // TODO measure output
 }
diff --git a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/Main.cpp b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/Main.cpp
index 95bc5b5..70e9d83 100644
--- a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/Main.cpp
+++ b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/Main.cpp
@@ -2,7 +2,7 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
diff --git a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/PWMTest.cpp b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/PWMTest.cpp
index 316234a..d209658 100644
--- a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/PWMTest.cpp
+++ b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/PWMTest.cpp
@@ -5,6 +5,7 @@
 #include <atomic>
 #include <thread>
 
+#include <gtest/gtest.h>
 #include <hal/DMA.h>
 #include <hal/HAL.h>
 #include <wpi/SmallVector.h>
@@ -13,7 +14,6 @@
 
 #include "CrossConnects.h"
 #include "LifetimeWrappers.h"
-#include "gtest/gtest.h"
 
 using namespace hlt;
 
@@ -32,8 +32,8 @@
   ASSERT_EQ(0, status);
 
   // Ensure our PWM is disabled, and set up properly
-  HAL_SetPWMRaw(pwmHandle, 0, &status);
-  HAL_SetPWMConfig(pwmHandle, 2.0, 1.0, 1.0, 0, 0, &status);
+  HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
+  HAL_SetPWMConfigMicroseconds(pwmHandle, 2000, 1000, 1000, 0, 0, &status);
   HAL_SetPWMPeriodScale(pwmHandle, squelch, &status);
 
   unsigned int checkPeriod = 0;
@@ -163,8 +163,8 @@
   ASSERT_NE(pwmHandle, HAL_kInvalidHandle);
 
   // Ensure our PWM is disabled, and set up properly
-  HAL_SetPWMRaw(pwmHandle, 0, &status);
-  HAL_SetPWMConfig(pwmHandle, 2.0, 1.0, 1.0, 0, 0, &status);
+  HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
+  HAL_SetPWMConfigMicroseconds(pwmHandle, 2000, 1000, 1000, 0, 0, &status);
   HAL_SetPWMPeriodScale(pwmHandle, squelch, &status);
 
   unsigned int checkPeriod = 0;
@@ -251,7 +251,7 @@
       }
     }
 
-    HAL_SetPWMRaw(pwmHandle, 0, &status);
+    HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
 
     // Ensure our interrupts have the proper counts
     ASSERT_EQ(interruptData.risingStamps.size(),
diff --git a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/RelayAnalogTest.cpp b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/RelayAnalogTest.cpp
index fc7da0c..0ba1e2d 100644
--- a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/RelayAnalogTest.cpp
+++ b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/RelayAnalogTest.cpp
@@ -2,13 +2,13 @@
 // 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 <gtest/gtest.h>
 #include <hal/AnalogInput.h>
 #include <hal/Relay.h>
 #include <wpi/SmallVector.h>
 
 #include "CrossConnects.h"
 #include "LifetimeWrappers.h"
-#include "gtest/gtest.h"
 
 using namespace hlt;
 
diff --git a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/RelayDigitalTest.cpp b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/RelayDigitalTest.cpp
index c7923bc..54b3993 100644
--- a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/RelayDigitalTest.cpp
+++ b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/RelayDigitalTest.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <hal/Relay.h>
 #include <wpi/SmallVector.h>
 
 #include "CrossConnects.h"
 #include "LifetimeWrappers.h"
-#include "gtest/gtest.h"
 
 using namespace hlt;
 
diff --git a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/TestEnvironment.cpp b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/TestEnvironment.cpp
index 458046c..8ef1922 100644
--- a/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/TestEnvironment.cpp
+++ b/third_party/allwpilib/crossConnIntegrationTests/src/main/native/cpp/TestEnvironment.cpp
@@ -6,9 +6,9 @@
 #include <thread>
 
 #include <fmt/core.h>
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
-#include "gtest/gtest.h"
 #include "mockds/MockDS.h"
 
 using namespace std::chrono_literals;
diff --git a/third_party/allwpilib/cscore/.styleguide b/third_party/allwpilib/cscore/.styleguide
index dadecd2..e82e064 100644
--- a/third_party/allwpilib/cscore/.styleguide
+++ b/third_party/allwpilib/cscore/.styleguide
@@ -36,6 +36,7 @@
 
 includeOtherLibs {
   ^fmt/
+  ^gtest/
   ^opencv2/
   ^support/
   ^tcpsockets/
diff --git a/third_party/allwpilib/cscore/CMakeLists.txt b/third_party/allwpilib/cscore/CMakeLists.txt
index af0d803..a6a89b3 100644
--- a/third_party/allwpilib/cscore/CMakeLists.txt
+++ b/third_party/allwpilib/cscore/CMakeLists.txt
@@ -40,7 +40,7 @@
 
 set_property(TARGET cscore PROPERTY FOLDER "libraries")
 
-install(TARGETS cscore EXPORT cscore DESTINATION "${main_lib_dest}")
+install(TARGETS cscore EXPORT cscore)
 install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/cscore")
 
 if (WITH_FLAT_INSTALL)
@@ -53,7 +53,7 @@
 install(FILES ${WPILIB_BINARY_DIR}/cscore-config.cmake DESTINATION ${cscore_config_dir})
 install(EXPORT cscore DESTINATION ${cscore_config_dir})
 
-SUBDIR_LIST(cscore_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
+subdir_list(cscore_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
 foreach(example ${cscore_examples})
     file(GLOB cscore_example_src examples/${example}/*.cpp)
     unset(add_libs)
@@ -102,12 +102,7 @@
     file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
     set(CMAKE_JNI_TARGET true)
 
-    if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-        set(CMAKE_JAVA_COMPILE_FLAGS "-h" "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-        add_jar(cscore_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar ${OPENCV_JAR_FILE} OUTPUT_NAME cscore)
-    else()
-        add_jar(cscore_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar ${OPENCV_JAR_FILE} OUTPUT_NAME cscore GENERATE_NATIVE_HEADERS cscore_jni_headers)
-    endif()
+    add_jar(cscore_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar ${OPENCV_JAR_FILE} OUTPUT_NAME cscore GENERATE_NATIVE_HEADERS cscore_jni_headers)
 
     get_property(CSCORE_JAR_FILE TARGET cscore_jar PROPERTY JAR_FILE)
     install(FILES ${CSCORE_JAR_FILE} DESTINATION "${java_lib_dest}")
@@ -131,20 +126,27 @@
 
     set_property(TARGET cscorejni PROPERTY FOLDER "libraries")
 
-    if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-        target_include_directories(cscorejni PRIVATE ${JNI_INCLUDE_DIRS})
-        target_include_directories(cscorejni PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-    else()
-        target_link_libraries(cscorejni PRIVATE cscore_jni_headers)
-    endif()
+    target_link_libraries(cscorejni PRIVATE cscore_jni_headers)
     add_dependencies(cscorejni cscore_jar)
 
-    if (MSVC)
-        install(TARGETS cscorejni RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-    endif()
+    install(TARGETS cscorejni EXPORT cscorejni)
 
-    install(TARGETS cscorejni EXPORT cscorejni DESTINATION "${main_lib_dest}")
+endif()
 
+if (WITH_JAVA_SOURCE)
+    find_package(Java REQUIRED)
+    include(UseJava)
+    file(GLOB CSCORE_SOURCES src/main/java/edu/wpi/first/cscore/*.java)
+    file(GLOB CSCORE_RAW_SOURCES src/main/java/edu/wpi/first/cscore/raw/*.java)
+    add_jar(cscore_src_jar
+    RESOURCES NAMESPACE "edu/wpi/first/cscore" ${CSCORE_SOURCES}
+    NAMESPACE "edu/wpi/first/cscore/raw" ${CSCORE_RAW_SOURCES}
+    OUTPUT_NAME cscore-sources)
+
+    get_property(CSCORE_SRC_JAR_FILE TARGET cscore_src_jar PROPERTY JAR_FILE)
+    install(FILES ${CSCORE_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+    set_property(TARGET cscore_src_jar PROPERTY FOLDER "java")
 endif()
 
 if (WITH_TESTS)
diff --git a/third_party/allwpilib/cscore/build.gradle b/third_party/allwpilib/cscore/build.gradle
index a656731..5549eac 100644
--- a/third_party/allwpilib/cscore/build.gradle
+++ b/third_party/allwpilib/cscore/build.gradle
@@ -36,7 +36,6 @@
                         srcDir 'src/main/native/include'
                         include '**/*.h'
                     }
-
                 }
             }
             binaries.all {
@@ -216,15 +215,14 @@
                                     it.linker.args << '-lGL'
                                 }
                             }
-                        }
-                        sources {
-                            cpp {
-                                source {
-                                    srcDirs 'examples/' + "${key}"
-                                    include '**/*.cpp'
-                                }
+                            if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                                nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
                             }
                         }
+                        sources.cpp.source {
+                            srcDirs 'examples/' + "${key}"
+                            include '**/*.cpp'
+                        }
                     }
                 }
             } else {
@@ -234,15 +232,14 @@
                         lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
                         lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
                         lib library: 'cscore', linkage: 'shared'
-                    }
-                    sources {
-                        cpp {
-                            source {
-                                srcDirs 'examples/' + "${key}"
-                                include '**/*.cpp'
-                            }
+                        if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                            nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
                         }
                     }
+                    sources.cpp.source {
+                        srcDirs 'examples/' + "${key}"
+                        include '**/*.cpp'
+                    }
                 }
             }
         }
diff --git a/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java b/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java
index 3527dae..6619522 100644
--- a/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java
+++ b/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java
@@ -65,6 +65,7 @@
   public static native void putSourceFrame(int source, long imageNativeObj);
 
   public static native int createCvSink(String name);
+
   // public static native int createCvSinkCallback(String name,
   //                            void (*processFrame)(long time));
 
diff --git a/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java b/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java
index 37f35b0..148be68 100644
--- a/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java
+++ b/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java
@@ -143,7 +143,7 @@
   public static native void releaseSource(int source);
 
   //
-  // Camera Source Common Property Fuctions
+  // Camera Source Common Property Functions
   //
   public static native void setCameraBrightness(int source, int brightness);
 
diff --git a/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java b/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java
index 012874d..b818c7e 100644
--- a/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java
+++ b/third_party/allwpilib/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java
@@ -4,6 +4,8 @@
 
 package edu.wpi.first.cscore;
 
+import java.util.Objects;
+
 /** Video mode. */
 @SuppressWarnings("MemberName")
 public class VideoMode {
@@ -75,4 +77,28 @@
 
   /** Frames per second. */
   public int fps;
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    if (other == null) {
+      return false;
+    }
+    if (getClass() != other.getClass()) {
+      return false;
+    }
+    VideoMode mode = (VideoMode) other;
+
+    return pixelFormat == mode.pixelFormat
+        && width == mode.width
+        && height == mode.height
+        && fps == mode.fps;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(pixelFormat, width, height, fps);
+  }
 }
diff --git a/third_party/allwpilib/cscore/src/main/native/cpp/HttpCameraImpl.cpp b/third_party/allwpilib/cscore/src/main/native/cpp/HttpCameraImpl.cpp
index 7875060..da60d77 100644
--- a/third_party/allwpilib/cscore/src/main/native/cpp/HttpCameraImpl.cpp
+++ b/third_party/allwpilib/cscore/src/main/native/cpp/HttpCameraImpl.cpp
@@ -193,7 +193,8 @@
   auto [mediaType, contentType] = wpi::split(conn->contentType.str(), ';');
   mediaType = wpi::trim(mediaType);
   if (mediaType != "multipart/x-mixed-replace") {
-    SWARNING("\"{}\": unrecognized Content-Type \"{}\"", req.host, mediaType);
+    SWARNING("\"{}\": unrecognized Content-Type \"{}\"", req.host.str(),
+             mediaType);
     std::scoped_lock lock(m_mutex);
     m_streamConn = nullptr;
     return nullptr;
@@ -216,7 +217,8 @@
   }
 
   if (boundary.empty()) {
-    SWARNING("\"{}\": empty multi-part boundary or no Content-Type", req.host);
+    SWARNING("\"{}\": empty multi-part boundary or no Content-Type",
+             req.host.str());
     std::scoped_lock lock(m_mutex);
     m_streamConn = nullptr;
     return nullptr;
@@ -281,8 +283,8 @@
   // Check the content type (if present)
   if (!contentTypeBuf.str().empty() &&
       !wpi::starts_with(contentTypeBuf, "image/jpeg")) {
-    auto errMsg =
-        fmt::format("received unknown Content-Type \"{}\"", contentTypeBuf);
+    auto errMsg = fmt::format("received unknown Content-Type \"{}\"",
+                              contentTypeBuf.str());
     SWARNING("{}", errMsg);
     PutError(errMsg, wpi::Now());
     return false;
diff --git a/third_party/allwpilib/cscore/src/main/native/cpp/Image.h b/third_party/allwpilib/cscore/src/main/native/cpp/Image.h
index cb8df91..83cbe89 100644
--- a/third_party/allwpilib/cscore/src/main/native/cpp/Image.h
+++ b/third_party/allwpilib/cscore/src/main/native/cpp/Image.h
@@ -22,9 +22,7 @@
 
  public:
 #ifndef __linux__
-  explicit Image(size_t capacity) {
-    m_data.reserve(capacity);
-  }
+  explicit Image(size_t capacity) { m_data.reserve(capacity); }
 #else
   explicit Image(size_t capacity)
       : m_data{capacity, default_init_allocator<uchar>{}} {
@@ -39,35 +37,19 @@
   operator std::string_view() const {  // NOLINT
     return str();
   }
-  std::string_view str() const {
-    return {data(), size()};
-  }
-  size_t capacity() const {
-    return m_data.capacity();
-  }
+  std::string_view str() const { return {data(), size()}; }
+  size_t capacity() const { return m_data.capacity(); }
   const char* data() const {
     return reinterpret_cast<const char*>(m_data.data());
   }
-  char* data() {
-    return reinterpret_cast<char*>(m_data.data());
-  }
-  size_t size() const {
-    return m_data.size();
-  }
+  char* data() { return reinterpret_cast<char*>(m_data.data()); }
+  size_t size() const { return m_data.size(); }
 
-  const std::vector<uchar>& vec() const {
-    return m_data;
-  }
-  std::vector<uchar>& vec() {
-    return m_data;
-  }
+  const std::vector<uchar>& vec() const { return m_data; }
+  std::vector<uchar>& vec() { return m_data; }
 
-  void resize(size_t size) {
-    m_data.resize(size);
-  }
-  void SetSize(size_t size) {
-    m_data.resize(size);
-  }
+  void resize(size_t size) { m_data.resize(size); }
+  void SetSize(size_t size) { m_data.resize(size); }
 
   cv::Mat AsMat() {
     int type;
@@ -90,9 +72,7 @@
     return cv::Mat{height, width, type, m_data.data()};
   }
 
-  cv::_InputArray AsInputArray() {
-    return cv::_InputArray{m_data};
-  }
+  cv::_InputArray AsInputArray() { return cv::_InputArray{m_data}; }
 
   bool Is(int width_, int height_) {
     return width == width_ && height == height_;
@@ -114,12 +94,8 @@
   bool IsLarger(const Image& oth) {
     return width >= oth.width && height >= oth.height;
   }
-  bool IsSmaller(int width_, int height_) {
-    return !IsLarger(width_, height_);
-  }
-  bool IsSmaller(const Image& oth) {
-    return !IsLarger(oth);
-  }
+  bool IsSmaller(int width_, int height_) { return !IsLarger(width_, height_); }
+  bool IsSmaller(const Image& oth) { return !IsLarger(oth); }
 
  private:
   std::vector<uchar> m_data;
diff --git a/third_party/allwpilib/cscore/src/main/native/cpp/MjpegServerImpl.cpp b/third_party/allwpilib/cscore/src/main/native/cpp/MjpegServerImpl.cpp
index ea9ebe1..82273e5 100644
--- a/third_party/allwpilib/cscore/src/main/native/cpp/MjpegServerImpl.cpp
+++ b/third_party/allwpilib/cscore/src/main/native/cpp/MjpegServerImpl.cpp
@@ -400,12 +400,12 @@
           fmt::print(os,
                      "<input id=\"{0}{1}\" type=\"radio\" name=\"{0}\" "
                      "value=\"{2}\" onclick=\"update('{0}', {1})\"",
-                     name, j, ch_name);
+                     name, j, ch_name.str());
           if (j == valE) {
             os << " checked";
           }
           fmt::print(os, " /><label for=\"{}{}\">{}</label>\n", name, j,
-                     ch_name);
+                     ch_name.str());
         }
         break;
       }
@@ -543,7 +543,7 @@
         for (char ch : *choice) {
           ch_name.push_back(std::isprint(ch) ? ch : ' ');
         }
-        fmt::print(os, "\"{}\": \"{}\"", j, ch_name);
+        fmt::print(os, "\"{}\": \"{}\"", j, ch_name.str());
       }
       os << "}\n";
     }
diff --git a/third_party/allwpilib/cscore/src/main/native/cpp/PropertyContainer.h b/third_party/allwpilib/cscore/src/main/native/cpp/PropertyContainer.h
index 1f4ffc3..99d030b 100644
--- a/third_party/allwpilib/cscore/src/main/native/cpp/PropertyContainer.h
+++ b/third_party/allwpilib/cscore/src/main/native/cpp/PropertyContainer.h
@@ -14,6 +14,7 @@
 #include <vector>
 
 #include <wpi/StringMap.h>
+#include <wpi/json_fwd.h>
 #include <wpi/mutex.h>
 
 #include "PropertyImpl.h"
@@ -23,7 +24,6 @@
 class Logger;
 template <typename T>
 class SmallVectorImpl;
-class json;
 }  // namespace wpi
 
 namespace cs {
diff --git a/third_party/allwpilib/cscore/src/main/native/cpp/SinkImpl.h b/third_party/allwpilib/cscore/src/main/native/cpp/SinkImpl.h
index aa37d61..fae1967 100644
--- a/third_party/allwpilib/cscore/src/main/native/cpp/SinkImpl.h
+++ b/third_party/allwpilib/cscore/src/main/native/cpp/SinkImpl.h
@@ -10,14 +10,11 @@
 #include <string_view>
 
 #include <wpi/Logger.h>
+#include <wpi/json_fwd.h>
 #include <wpi/mutex.h>
 
 #include "SourceImpl.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace cs {
 
 class Frame;
diff --git a/third_party/allwpilib/cscore/src/main/native/cpp/SourceImpl.h b/third_party/allwpilib/cscore/src/main/native/cpp/SourceImpl.h
index 5df9ad0..840fb5d 100644
--- a/third_party/allwpilib/cscore/src/main/native/cpp/SourceImpl.h
+++ b/third_party/allwpilib/cscore/src/main/native/cpp/SourceImpl.h
@@ -14,6 +14,7 @@
 
 #include <wpi/Logger.h>
 #include <wpi/condition_variable.h>
+#include <wpi/json_fwd.h>
 #include <wpi/mutex.h>
 
 #include "Frame.h"
@@ -22,10 +23,6 @@
 #include "PropertyContainer.h"
 #include "cscore_cpp.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace cs {
 
 class Notifier;
diff --git a/third_party/allwpilib/cscore/src/main/native/cpp/UnlimitedHandleResource.h b/third_party/allwpilib/cscore/src/main/native/cpp/UnlimitedHandleResource.h
index 28ad301..fcde794 100644
--- a/third_party/allwpilib/cscore/src/main/native/cpp/UnlimitedHandleResource.h
+++ b/third_party/allwpilib/cscore/src/main/native/cpp/UnlimitedHandleResource.h
@@ -16,7 +16,7 @@
 namespace cs {
 
 // The UnlimitedHandleResource class is a way to track handles. This version
-// allows an unlimted number of handles that are allocated sequentially. When
+// allows an unlimited number of handles that are allocated sequentially. When
 // possible, indices are reused to save memory usage and keep the array length
 // down.
 // However, automatic array management has not been implemented, but might be in
diff --git a/third_party/allwpilib/cscore/src/main/native/cpp/cscore_c.cpp b/third_party/allwpilib/cscore/src/main/native/cpp/cscore_c.cpp
index ffbc164..3fbbfcd 100644
--- a/third_party/allwpilib/cscore/src/main/native/cpp/cscore_c.cpp
+++ b/third_party/allwpilib/cscore/src/main/native/cpp/cscore_c.cpp
@@ -516,7 +516,7 @@
   std::free(modes);
 }
 
-char* CS_GetHostname() {
+char* CS_GetHostname(void) {
   return cs::ConvertToC(cs::GetHostname());
 }
 
diff --git a/third_party/allwpilib/cscore/src/main/native/cpp/cscore_cpp.cpp b/third_party/allwpilib/cscore/src/main/native/cpp/cscore_cpp.cpp
index f81405b..a46b805 100644
--- a/third_party/allwpilib/cscore/src/main/native/cpp/cscore_cpp.cpp
+++ b/third_party/allwpilib/cscore/src/main/native/cpp/cscore_cpp.cpp
@@ -439,7 +439,7 @@
 }
 
 //
-// Camera Source Common Property Fuctions
+// Camera Source Common Property Functions
 //
 
 void SetCameraBrightness(CS_Source source, int brightness, CS_Status* status) {
diff --git a/third_party/allwpilib/cscore/src/main/native/include/cscore_c.h b/third_party/allwpilib/cscore/src/main/native/include/cscore_c.h
index ba8c811..3d30a0e 100644
--- a/third_party/allwpilib/cscore/src/main/native/include/cscore_c.h
+++ b/third_party/allwpilib/cscore/src/main/native/include/cscore_c.h
@@ -323,7 +323,7 @@
 /** @} */
 
 /**
- * @defgroup cscore_source_prop_cfunc Camera Source Common Property Fuctions
+ * @defgroup cscore_source_prop_cfunc Camera Source Common Property Functions
  * @{
  */
 void CS_SetCameraBrightness(CS_Source source, int brightness,
@@ -504,7 +504,7 @@
 void CS_FreeEnumeratedProperties(CS_Property* properties, int count);
 void CS_FreeEnumeratedVideoModes(CS_VideoMode* modes, int count);
 
-char* CS_GetHostname();
+char* CS_GetHostname(void);
 
 char** CS_GetNetworkInterfaces(int* count);
 void CS_FreeNetworkInterfaces(char** interfaces, int count);
diff --git a/third_party/allwpilib/cscore/src/main/native/include/cscore_cpp.h b/third_party/allwpilib/cscore/src/main/native/include/cscore_cpp.h
index 0031e5e..72c37f8 100644
--- a/third_party/allwpilib/cscore/src/main/native/include/cscore_cpp.h
+++ b/third_party/allwpilib/cscore/src/main/native/include/cscore_cpp.h
@@ -14,6 +14,7 @@
 #include <vector>
 
 #include <wpi/SmallVector.h>
+#include <wpi/json_fwd.h>
 
 #include "cscore_c.h"
 
@@ -23,10 +24,6 @@
 #pragma warning(disable : 26495)
 #endif
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 /** CameraServer (cscore) namespace */
 namespace cs {
 
@@ -262,7 +259,7 @@
 /** @} */
 
 /**
- * @defgroup cscore_camera_property_func Camera Source Common Property Fuctions
+ * @defgroup cscore_camera_property_func Camera Source Common Property Functions
  * @{
  */
 void SetCameraBrightness(CS_Source source, int brightness, CS_Status* status);
diff --git a/third_party/allwpilib/cscore/src/main/native/include/cscore_cv.h b/third_party/allwpilib/cscore/src/main/native/include/cscore_cv.h
index 30a356b..66cb23e 100644
--- a/third_party/allwpilib/cscore/src/main/native/include/cscore_cv.h
+++ b/third_party/allwpilib/cscore/src/main/native/include/cscore_cv.h
@@ -5,6 +5,8 @@
 #ifndef CSCORE_CSCORE_CV_H_
 #define CSCORE_CSCORE_CV_H_
 
+#include <functional>
+
 #include "cscore_c.h"
 
 #ifdef CSCORE_CSCORE_RAW_CV_H_
@@ -152,8 +154,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  [[nodiscard]] uint64_t GrabFrame(cv::Mat& image,
-                                   double timeout = 0.225) const;
+  [[nodiscard]]
+  uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225) const;
 
   /**
    * Wait for the next frame and get the image.  May block forever.
@@ -163,7 +165,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  [[nodiscard]] uint64_t GrabFrameNoTimeout(cv::Mat& image) const;
+  [[nodiscard]]
+  uint64_t GrabFrameNoTimeout(cv::Mat& image) const;
 };
 
 inline CvSource::CvSource(std::string_view name, const VideoMode& mode) {
diff --git a/third_party/allwpilib/cscore/src/main/native/include/cscore_oo.h b/third_party/allwpilib/cscore/src/main/native/include/cscore_oo.h
index ab09fe7..f13a6fd 100644
--- a/third_party/allwpilib/cscore/src/main/native/include/cscore_oo.h
+++ b/third_party/allwpilib/cscore/src/main/native/include/cscore_oo.h
@@ -5,6 +5,7 @@
 #ifndef CSCORE_CSCORE_OO_H_
 #define CSCORE_CSCORE_OO_H_
 
+#include <functional>
 #include <initializer_list>
 #include <span>
 #include <string>
diff --git a/third_party/allwpilib/cscore/src/main/native/include/cscore_oo.inc b/third_party/allwpilib/cscore/src/main/native/include/cscore_oo.inc
index ff3540d..18a1df2 100644
--- a/third_party/allwpilib/cscore/src/main/native/include/cscore_oo.inc
+++ b/third_party/allwpilib/cscore/src/main/native/include/cscore_oo.inc
@@ -5,6 +5,7 @@
 #ifndef CSCORE_CSCORE_OO_INC_
 #define CSCORE_CSCORE_OO_INC_
 
+#include <functional>
 #include <string>
 #include <string_view>
 #include <utility>
diff --git a/third_party/allwpilib/cscore/src/main/native/include/cscore_raw.h b/third_party/allwpilib/cscore/src/main/native/include/cscore_raw.h
index 0aaaeff..21f4009 100644
--- a/third_party/allwpilib/cscore/src/main/native/include/cscore_raw.h
+++ b/third_party/allwpilib/cscore/src/main/native/include/cscore_raw.h
@@ -5,6 +5,8 @@
 #ifndef CSCORE_CSCORE_RAW_H_
 #define CSCORE_CSCORE_RAW_H_
 
+#include <functional>
+
 #include "cscore_c.h"
 
 #ifdef __cplusplus
@@ -174,8 +176,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  [[nodiscard]] uint64_t GrabFrame(RawFrame& image,
-                                   double timeout = 0.225) const;
+  [[nodiscard]]
+  uint64_t GrabFrame(RawFrame& image, double timeout = 0.225) const;
 
   /**
    * Wait for the next frame and get the image.  May block forever.
@@ -185,7 +187,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  [[nodiscard]] uint64_t GrabFrameNoTimeout(RawFrame& image) const;
+  [[nodiscard]]
+  uint64_t GrabFrameNoTimeout(RawFrame& image) const;
 };
 
 inline RawSource::RawSource(std::string_view name, const VideoMode& mode) {
diff --git a/third_party/allwpilib/cscore/src/main/native/include/cscore_raw_cv.h b/third_party/allwpilib/cscore/src/main/native/include/cscore_raw_cv.h
index 5b9a374..11f42d6 100644
--- a/third_party/allwpilib/cscore/src/main/native/include/cscore_raw_cv.h
+++ b/third_party/allwpilib/cscore/src/main/native/include/cscore_raw_cv.h
@@ -9,6 +9,8 @@
 #error "Cannot include both cscore_cv.h and cscore_raw_cv.h in the same file"
 #endif
 
+#include <functional>
+
 #include <opencv2/core/mat.hpp>
 
 #include "cscore_raw.h"
@@ -111,7 +113,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  [[nodiscard]] uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225);
+  [[nodiscard]]
+  uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225);
 
   /**
    * Wait for the next frame and get the image.  May block forever.
@@ -121,7 +124,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  [[nodiscard]] uint64_t GrabFrameNoTimeout(cv::Mat& image);
+  [[nodiscard]]
+  uint64_t GrabFrameNoTimeout(cv::Mat& image);
 
   /**
    * Wait for the next frame and get the image.
@@ -132,8 +136,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  [[nodiscard]] uint64_t GrabFrameDirect(cv::Mat& image,
-                                         double timeout = 0.225);
+  [[nodiscard]]
+  uint64_t GrabFrameDirect(cv::Mat& image, double timeout = 0.225);
 
   /**
    * Wait for the next frame and get the image.  May block forever.
@@ -143,7 +147,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  [[nodiscard]] uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
+  [[nodiscard]]
+  uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
 
  private:
   RawFrame rawFrame;
diff --git a/third_party/allwpilib/cscore/src/main/native/linux/UsbCameraImpl.cpp b/third_party/allwpilib/cscore/src/main/native/linux/UsbCameraImpl.cpp
index 270905b..b50b6d9 100644
--- a/third_party/allwpilib/cscore/src/main/native/linux/UsbCameraImpl.cpp
+++ b/third_party/allwpilib/cscore/src/main/native/linux/UsbCameraImpl.cpp
@@ -486,7 +486,7 @@
         // If the name is what we expect...
         std::string_view name{raw_name.c_str()};
         SDEBUG4("got event on '{}' ({}) compare to '{}' ({}) mask {}", name,
-                name.size(), base, base.size(), event.mask);
+                name.size(), base.str(), base.size(), event.mask);
         if (name == base) {
           if ((event.mask & IN_DELETE) != 0) {
             wasStreaming = m_streaming;
@@ -595,7 +595,7 @@
   }
 
   if (m_connectVerbose) {
-    SINFO("Connecting to USB camera on {}", m_path);
+    SINFO("Attempting to connect to USB camera on {}", m_path);
   }
 
   // Try to open the device
@@ -606,6 +606,10 @@
   }
   m_fd = fd;
 
+  if (m_connectVerbose) {
+    SINFO("Connected to USB camera on {}", m_path);
+  }
+
   // Get capabilities
   SDEBUG3("getting capabilities");
   struct v4l2_capability vcap;
diff --git a/third_party/allwpilib/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm b/third_party/allwpilib/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm
index febe6a5..b01df63 100644
--- a/third_party/allwpilib/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm
+++ b/third_party/allwpilib/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm
@@ -79,7 +79,7 @@
     default:
       OBJCERROR(
           "Camera access explicitly blocked for application. No cameras are "
-          "accessable");
+          "accessible");
       self.isAuthorized = false;
       // TODO log
       break;
@@ -328,8 +328,6 @@
   @autoreleasepool {
     NSArray<AVCaptureDeviceFormat*>* formats = self.videoDevice.formats;
 
-    int count = 0;
-
     for (AVCaptureDeviceFormat* format in formats) {
       CMFormatDescriptionRef cmformat = format.formatDescription;
       CMVideoDimensions s1 = CMVideoFormatDescriptionGetDimensions(cmformat);
@@ -363,7 +361,6 @@
 
       modes.emplace_back(store.mode);
       platformModes.emplace_back(store);
-      count++;
     }
   }
 
@@ -524,7 +521,7 @@
   if (!self.isAuthorized) {
     OBJCERROR(
         "Camera access not authorized for application. No cameras are "
-        "accessable");
+        "accessible");
     return false;
   }
 
@@ -557,7 +554,7 @@
   }
 
   std::string pathStr = [self.path UTF8String];
-  OBJCINFO("Connecting to USB camera on {}", pathStr);
+  OBJCINFO("Attempting to connect to USB camera on {}", pathStr);
 
   self.videoDevice = [AVCaptureDevice deviceWithUniqueID:self.path];
   if (self.videoDevice == nil) {
@@ -597,6 +594,8 @@
     goto err;
   }
 
+  OBJCINFO("Connected to USB camera on {}", pathStr);
+
   [[NSNotificationCenter defaultCenter]
       addObserver:self
          selector:@selector(sessionRuntimeError:)
diff --git a/third_party/allwpilib/cscore/src/main/native/windows/COMCreators.cpp b/third_party/allwpilib/cscore/src/main/native/windows/COMCreators.cpp
index f9f5cae..5ccddff 100644
--- a/third_party/allwpilib/cscore/src/main/native/windows/COMCreators.cpp
+++ b/third_party/allwpilib/cscore/src/main/native/windows/COMCreators.cpp
@@ -78,7 +78,7 @@
     return S_OK;
   if (SUCCEEDED(hrStatus)) {
     if (pSample) {
-      // Prcoess sample
+      // Process sample
       source->ProcessFrame(pSample, m_mode);
       // DO NOT release the frame
     }
diff --git a/third_party/allwpilib/cscore/src/main/native/windows/UsbCameraImpl.cpp b/third_party/allwpilib/cscore/src/main/native/windows/UsbCameraImpl.cpp
index c6a8985..cde8c25 100644
--- a/third_party/allwpilib/cscore/src/main/native/windows/UsbCameraImpl.cpp
+++ b/third_party/allwpilib/cscore/src/main/native/windows/UsbCameraImpl.cpp
@@ -270,8 +270,9 @@
 }
 
 static bool IsPercentageProperty(std::string_view name) {
-  if (wpi::starts_with(name, "raw_"))
+  if (wpi::starts_with(name, "raw_")) {
     name = wpi::substr(name, 4);
+  }
   return name == "Brightness" || name == "Contrast" || name == "Saturation" ||
          name == "Hue" || name == "Sharpness" || name == "Gain" ||
          name == "Exposure";
@@ -498,7 +499,7 @@
   }
 
   if (m_connectVerbose) {
-    SINFO("Connecting to USB camera on {}", m_path);
+    SINFO("Attempting to connect to USB camera on {}", m_path);
   }
 
   SDEBUG3("opening device");
@@ -525,6 +526,10 @@
     return false;
   }
 
+  if (m_connectVerbose) {
+    SINFO("Connected to USB camera on {}", m_path);
+  }
+
   CS_Status st = 0;
   auto devices = EnumerateUsbCameras(&st);
 
diff --git a/third_party/allwpilib/cscore/src/test/java/edu/wpi/first/cscore/UsbCameraTest.java b/third_party/allwpilib/cscore/src/test/java/edu/wpi/first/cscore/UsbCameraTest.java
index f0a679f..395f7df 100644
--- a/third_party/allwpilib/cscore/src/test/java/edu/wpi/first/cscore/UsbCameraTest.java
+++ b/third_party/allwpilib/cscore/src/test/java/edu/wpi/first/cscore/UsbCameraTest.java
@@ -24,7 +24,7 @@
   static class ConnectVerbose {
     @Test
     void setConnectVerboseEnabledTest() {
-      try (UsbCamera camera = new UsbCamera("Nonexistant Camera", getNonexistentCameraDev())) {
+      try (UsbCamera camera = new UsbCamera("Nonexistent Camera", getNonexistentCameraDev())) {
         camera.setConnectVerbose(1);
 
         CompletableFuture<String> result = new CompletableFuture<>();
@@ -32,13 +32,13 @@
 
         assertTimeoutPreemptively(
             Duration.ofSeconds(5),
-            () -> assertTrue(result.get().contains("Connecting to USB camera on ")));
+            () -> assertTrue(result.get().contains("Attempting to connect to USB camera on ")));
       }
     }
 
     @Test
     void setConnectVerboseDisabledTest() {
-      try (UsbCamera camera = new UsbCamera("Nonexistant Camera", getNonexistentCameraDev())) {
+      try (UsbCamera camera = new UsbCamera("Nonexistent Camera", getNonexistentCameraDev())) {
         camera.setConnectVerbose(0);
 
         CompletableFuture<String> result = new CompletableFuture<>();
diff --git a/third_party/allwpilib/cscore/src/test/java/edu/wpi/first/cscore/VideoModeTest.java b/third_party/allwpilib/cscore/src/test/java/edu/wpi/first/cscore/VideoModeTest.java
new file mode 100644
index 0000000..738888d
--- /dev/null
+++ b/third_party/allwpilib/cscore/src/test/java/edu/wpi/first/cscore/VideoModeTest.java
@@ -0,0 +1,21 @@
+// 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.
+
+package edu.wpi.first.cscore;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+import org.junit.jupiter.api.Test;
+
+class VideoModeTest {
+  @Test
+  void equalityTest() {
+    VideoMode a = new VideoMode(VideoMode.PixelFormat.kMJPEG, 1920, 1080, 30);
+    VideoMode b = new VideoMode(VideoMode.PixelFormat.kMJPEG, 1920, 1080, 30);
+
+    assertEquals(a, b);
+    assertNotEquals(a, null);
+  }
+}
diff --git a/third_party/allwpilib/cscore/src/test/native/cpp/CameraSourceTest.cpp b/third_party/allwpilib/cscore/src/test/native/cpp/CameraSourceTest.cpp
index 4c860bf..2e9d1d8 100644
--- a/third_party/allwpilib/cscore/src/test/native/cpp/CameraSourceTest.cpp
+++ b/third_party/allwpilib/cscore/src/test/native/cpp/CameraSourceTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "cscore.h"
-#include "gtest/gtest.h"
 
 namespace cs {
 
diff --git a/third_party/allwpilib/cscore/src/test/native/cpp/main.cpp b/third_party/allwpilib/cscore/src/test/native/cpp/main.cpp
index 09072ee..e993c1f 100644
--- a/third_party/allwpilib/cscore/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/cscore/src/test/native/cpp/main.cpp
@@ -2,7 +2,7 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/third_party/allwpilib/datalogtool/.styleguide b/third_party/allwpilib/datalogtool/.styleguide
index 87142fb..886b930 100644
--- a/third_party/allwpilib/datalogtool/.styleguide
+++ b/third_party/allwpilib/datalogtool/.styleguide
@@ -23,6 +23,7 @@
   ^fmt/
   ^glass/
   ^imgui
+  ^libssh/
   ^portable-file-dialog
   ^wpi/
   ^wpigui
diff --git a/third_party/allwpilib/datalogtool/CMakeLists.txt b/third_party/allwpilib/datalogtool/CMakeLists.txt
index f3baa1d..72a05c6 100644
--- a/third_party/allwpilib/datalogtool/CMakeLists.txt
+++ b/third_party/allwpilib/datalogtool/CMakeLists.txt
@@ -5,7 +5,7 @@
 include(LinkMacOSGUI)
 
 configure_file(src/main/generate/WPILibVersion.cpp.in WPILibVersion.cpp)
-GENERATE_RESOURCES(src/main/native/resources generated/main/cpp DLT dlt datalogtool_resources_src)
+generate_resources(src/main/native/resources generated/main/cpp DLT dlt datalogtool_resources_src)
 
 file(GLOB datalogtool_src src/main/native/cpp/*.cpp ${CMAKE_CURRENT_BINARY_DIR}/WPILibVersion.cpp)
 
diff --git a/third_party/allwpilib/datalogtool/build.gradle b/third_party/allwpilib/datalogtool/build.gradle
index 46452a9..181d621 100644
--- a/third_party/allwpilib/datalogtool/build.gradle
+++ b/third_party/allwpilib/datalogtool/build.gradle
@@ -1,126 +1,123 @@
 import org.gradle.internal.os.OperatingSystem
 
-if (!project.hasProperty('onlylinuxathena')) {
-
-    description = "roboRIO Team Number Setter"
-
-    apply plugin: 'cpp'
-    apply plugin: 'c'
-    apply plugin: 'google-test-test-suite'
-    apply plugin: 'visual-studio'
-    apply plugin: 'edu.wpi.first.NativeUtils'
-
-    if (OperatingSystem.current().isWindows()) {
-        apply plugin: 'windows-resources'
-    }
-
-    ext {
-        nativeName = 'datalogtool'
-    }
-
-    apply from: "${rootDir}/shared/resources.gradle"
-    apply from: "${rootDir}/shared/config.gradle"
-
-    def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in")
-    def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp")
-
-    apply from: "${rootDir}/shared/libssh.gradle"
-    apply from: "${rootDir}/shared/imgui.gradle"
-
-    task generateCppVersion() {
-        description = 'Generates the wpilib version class'
-        group = 'WPILib'
-
-        outputs.file wpilibVersionFileOutput
-        inputs.file wpilibVersionFileInput
-
-        if (wpilibVersioning.releaseMode) {
-            outputs.upToDateWhen { false }
-        }
-
-        // We follow a simple set of checks to determine whether we should generate a new version file:
-        // 1. If the release type is not development, we generate a new version file
-        // 2. If there is no generated version number, we generate a new version file
-        // 3. If there is a generated build number, and the release type is development, then we will
-        //    only generate if the publish task is run.
-        doLast {
-            def version = wpilibVersioning.version.get()
-            println "Writing version ${version} to $wpilibVersionFileOutput"
-
-            if (wpilibVersionFileOutput.exists()) {
-                wpilibVersionFileOutput.delete()
-            }
-            def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version)
-            wpilibVersionFileOutput.write(read)
-        }
-    }
-
-    gradle.taskGraph.addTaskExecutionGraphListener { graph ->
-        def willPublish = graph.hasTask(publish)
-        if (willPublish) {
-            generateCppVersion.outputs.upToDateWhen { false }
-        }
-    }
-
-    def generateTask = createGenerateResourcesTask('main', 'DLT', 'dlt', project)
-
-    project(':').libraryBuild.dependsOn build
-    tasks.withType(CppCompile) {
-        dependsOn generateTask
-        dependsOn generateCppVersion
-    }
-
-    model {
-        components {
-            // By default, a development executable will be generated. This is to help the case of
-            // testing specific functionality of the library.
-            "${nativeName}"(NativeExecutableSpec) {
-                baseName = 'datalogtool'
-                sources {
-                    cpp {
-                        source {
-                            srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp"
-                            include '**/*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/main/native/include'
-                        }
-                    }
-                    if (OperatingSystem.current().isWindows()) {
-                        rc {
-                            source {
-                                srcDirs 'src/main/native/win'
-                                include '*.rc'
-                            }
-                        }
-                    }
-                }
-                binaries.all {
-                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
-                        it.buildable = false
-                        return
-                    }
-                    it.cppCompiler.define("LIBSSH_STATIC")
-                    lib project: ':glass', library: 'glass', linkage: 'static'
-                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
-                    lib project: ':wpigui', library: 'wpigui', linkage: 'static'
-                    nativeUtils.useRequiredLibrary(it, 'imgui', 'libssh')
-                    if (it.targetPlatform.operatingSystem.isWindows()) {
-                        it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
-                        it.linker.args << 'ws2_32.lib' << 'advapi32.lib' << 'crypt32.lib' << 'user32.lib'
-                    } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
-                        it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
-                        it.linker.args << '-framework' << 'Kerberos'
-                    } else {
-                        it.linker.args << '-lX11'
-                        if (it.targetPlatform.name.startsWith('linuxarm')) {
-                            it.linker.args << '-lGL'
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    apply from: 'publish.gradle'
+if (project.hasProperty('onlylinuxathena')) {
+    return;
 }
+
+description = "roboRIO Team Number Setter"
+
+apply plugin: 'cpp'
+apply plugin: 'visual-studio'
+apply plugin: 'edu.wpi.first.NativeUtils'
+
+if (OperatingSystem.current().isWindows()) {
+    apply plugin: 'windows-resources'
+}
+
+ext {
+    nativeName = 'datalogtool'
+}
+
+apply from: "${rootDir}/shared/resources.gradle"
+apply from: "${rootDir}/shared/config.gradle"
+
+def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in")
+def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp")
+
+apply from: "${rootDir}/shared/libssh.gradle"
+apply from: "${rootDir}/shared/imgui.gradle"
+
+task generateCppVersion() {
+    description = 'Generates the wpilib version class'
+    group = 'WPILib'
+
+    outputs.file wpilibVersionFileOutput
+    inputs.file wpilibVersionFileInput
+
+    if (wpilibVersioning.releaseMode) {
+        outputs.upToDateWhen { false }
+    }
+
+    // We follow a simple set of checks to determine whether we should generate a new version file:
+    // 1. If the release type is not development, we generate a new version file
+    // 2. If there is no generated version number, we generate a new version file
+    // 3. If there is a generated build number, and the release type is development, then we will
+    //    only generate if the publish task is run.
+    doLast {
+        def version = wpilibVersioning.version.get()
+        println "Writing version ${version} to $wpilibVersionFileOutput"
+
+        if (wpilibVersionFileOutput.exists()) {
+            wpilibVersionFileOutput.delete()
+        }
+        def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version)
+        wpilibVersionFileOutput.write(read)
+    }
+}
+
+gradle.taskGraph.addTaskExecutionGraphListener { graph ->
+    def willPublish = graph.hasTask(publish)
+    if (willPublish) {
+        generateCppVersion.outputs.upToDateWhen { false }
+    }
+}
+
+def generateTask = createGenerateResourcesTask('main', 'DLT', 'dlt', project)
+
+project(':').libraryBuild.dependsOn build
+tasks.withType(CppCompile) {
+    dependsOn generateTask
+    dependsOn generateCppVersion
+}
+
+model {
+    components {
+        // By default, a development executable will be generated. This is to help the case of
+        // testing specific functionality of the library.
+        "${nativeName}"(NativeExecutableSpec) {
+            baseName = 'datalogtool'
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp"
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/main/native/include'
+                    }
+                }
+                if (OperatingSystem.current().isWindows()) {
+                    rc.source {
+                        srcDirs 'src/main/native/win'
+                        include '*.rc'
+                    }
+                }
+            }
+            binaries.all {
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    it.buildable = false
+                    return
+                }
+                it.cppCompiler.define("LIBSSH_STATIC")
+                lib project: ':glass', library: 'glass', linkage: 'static'
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                lib project: ':wpigui', library: 'wpigui', linkage: 'static'
+                nativeUtils.useRequiredLibrary(it, 'imgui', 'libssh')
+                if (it.targetPlatform.operatingSystem.isWindows()) {
+                    it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
+                    it.linker.args << 'ws2_32.lib' << 'advapi32.lib' << 'crypt32.lib' << 'user32.lib'
+                } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
+                    it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
+                    it.linker.args << '-framework' << 'Kerberos'
+                } else {
+                    it.linker.args << '-lX11'
+                    if (it.targetPlatform.name.startsWith('linuxarm')) {
+                        it.linker.args << '-lGL'
+                    }
+                }
+            }
+        }
+    }
+}
+
+apply from: 'publish.gradle'
diff --git a/third_party/allwpilib/datalogtool/publish.gradle b/third_party/allwpilib/datalogtool/publish.gradle
index e822842..f24f51a 100644
--- a/third_party/allwpilib/datalogtool/publish.gradle
+++ b/third_party/allwpilib/datalogtool/publish.gradle
@@ -34,8 +34,16 @@
                             description("Creates a macOS application bundle for DataLogTool")
                             from(file("$project.projectDir/Info.plist"))
                             into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/DataLogTool.app/Contents"))
-                            into("MacOS") { with copySpec { from binary.executable.file } }
-                            into("Resources") { with copySpec { from icon } }
+                            into("MacOS") {
+                                with copySpec {
+                                    from binary.executable.file
+                                }
+                            }
+                            into("Resources") {
+                                with copySpec {
+                                    from icon
+                                }
+                            }
 
                             inputs.property "HasDeveloperId", project.hasProperty("developerID")
 
@@ -71,7 +79,7 @@
 
                             archiveBaseName = '_M_' + zipBaseName
                             duplicatesStrategy = 'exclude'
-                            classifier = nativeUtils.getPublishClassifier(binary)
+                            archiveClassifier = nativeUtils.getPublishClassifier(binary)
 
                             from(licenseFile) {
                                 into '/'
diff --git a/third_party/allwpilib/datalogtool/src/main/native/cpp/App.cpp b/third_party/allwpilib/datalogtool/src/main/native/cpp/App.cpp
index a4b466b..87f90f2 100644
--- a/third_party/allwpilib/datalogtool/src/main/native/cpp/App.cpp
+++ b/third_party/allwpilib/datalogtool/src/main/native/cpp/App.cpp
@@ -4,19 +4,19 @@
 
 #include "App.h"
 
-#include <libssh/libssh.h>
-
 #include <memory>
 #include <string_view>
 
-#define IMGUI_DEFINE_MATH_OPERATORS
-
 #include <glass/Context.h>
 #include <glass/MainMenuBar.h>
 #include <glass/Storage.h>
+#include <libssh/libssh.h>
+
+#define IMGUI_DEFINE_MATH_OPERATORS
 #include <imgui.h>
 #include <imgui_internal.h>
 #include <wpigui.h>
+#include <wpigui_openurl.h>
 
 #include "Downloader.h"
 #include "Exporter.h"
@@ -93,6 +93,15 @@
     ImGui::EndMenu();
   }
 
+  if (ImGui::BeginMenu("Docs")) {
+    if (ImGui::MenuItem("Online documentation")) {
+      wpi::gui::OpenURL(
+          "https://docs.wpilib.org/en/stable/docs/software/telemetry/"
+          "datalog.html");
+    }
+    ImGui::EndMenu();
+  }
+
   ImGui::EndMainMenuBar();
 
   if (about) {
@@ -104,6 +113,8 @@
     ImGui::Text("v%s", GetWPILibVersion());
     ImGui::Separator();
     ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
+    ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate,
+                ImGui::GetIO().Framerate);
     if (ImGui::Button("Close")) {
       ImGui::CloseCurrentPopup();
     }
diff --git a/third_party/allwpilib/datalogtool/src/main/native/cpp/App.h b/third_party/allwpilib/datalogtool/src/main/native/cpp/App.h
index 9d1520c..a9978da 100644
--- a/third_party/allwpilib/datalogtool/src/main/native/cpp/App.h
+++ b/third_party/allwpilib/datalogtool/src/main/native/cpp/App.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#define IMGUI_DEFINE_MATH_OPERATORS
 #include <imgui.h>
 
 void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0,
diff --git a/third_party/allwpilib/datalogtool/src/main/native/cpp/Downloader.cpp b/third_party/allwpilib/datalogtool/src/main/native/cpp/Downloader.cpp
index 3d19738..9629e3b 100644
--- a/third_party/allwpilib/datalogtool/src/main/native/cpp/Downloader.cpp
+++ b/third_party/allwpilib/datalogtool/src/main/native/cpp/Downloader.cpp
@@ -4,8 +4,6 @@
 
 #include "Downloader.h"
 
-#include <libssh/sftp.h>
-
 #ifdef _WIN32
 #include <fcntl.h>
 #include <io.h>
@@ -20,6 +18,7 @@
 #include <glass/Storage.h>
 #include <imgui.h>
 #include <imgui_stdlib.h>
+#include <libssh/sftp.h>
 #include <portable-file-dialogs.h>
 #include <wpi/StringExtras.h>
 #include <wpi/fs.h>
@@ -77,15 +76,15 @@
 
   ImGui::SameLine();
   if (ImGui::Button("Deselect All")) {
-    for (auto&& download : m_downloadList) {
-      download.enabled = false;
+    for (auto&& download : m_fileList) {
+      download.selected = false;
     }
   }
 
   ImGui::SameLine();
   if (ImGui::Button("Select All")) {
-    for (auto&& download : m_downloadList) {
-      download.enabled = true;
+    for (auto&& download : m_fileList) {
+      download.selected = true;
     }
   }
 
@@ -138,6 +137,27 @@
       m_cv.notify_all();
     }
   }
+
+  ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(180, 0, 0, 255));
+  ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(210, 0, 0, 255));
+  ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32(255, 0, 0, 255));
+  if (ImGui::Button("Delete WITHOUT Downloading")) {
+    ImGui::OpenPopup("DeleteConfirm");
+  }
+  ImGui::PopStyleColor(3);
+  if (ImGui::BeginPopup("DeleteConfirm")) {
+    ImGui::TextUnformatted("Are you sure? This will NOT download the files");
+    if (ImGui::Button("DELETE")) {
+      m_state = kDelete;
+      m_cv.notify_all();
+      ImGui::CloseCurrentPopup();
+    }
+    ImGui::SameLine();
+    if (ImGui::Button("Cancel")) {
+      ImGui::CloseCurrentPopup();
+    }
+    ImGui::EndPopup();
+  }
 }
 
 size_t Downloader::DisplayFiles() {
@@ -148,11 +168,15 @@
           ImGuiTableFlags_Borders | ImGuiTableFlags_SizingStretchProp)) {
     ImGui::TableSetupColumn("File");
     ImGui::TableSetupColumn("Size");
-    ImGui::TableSetupColumn("Download");
+    ImGui::TableSetupColumn((m_state == kDownload || m_state == kDownloadDone ||
+                             m_state == kDelete || m_state == kDeleteDone)
+                                ? "Status"
+                                : "Selected");
     ImGui::TableHeadersRow();
-    for (auto&& download : m_downloadList) {
-      if ((m_state == kDownload || m_state == kDownloadDone) &&
-          !download.enabled) {
+    for (auto&& file : m_fileList) {
+      if ((m_state == kDownload || m_state == kDownloadDone ||
+           m_state == kDelete || m_state == kDeleteDone) &&
+          !file.selected) {
         continue;
       }
 
@@ -160,20 +184,24 @@
 
       ImGui::TableNextRow();
       ImGui::TableNextColumn();
-      ImGui::TextUnformatted(download.name.c_str());
+      ImGui::TextUnformatted(file.name.c_str());
       ImGui::TableNextColumn();
-      auto sizeText = fmt::format("{}", download.size);
+      auto sizeText = fmt::format("{}", file.size);
       ImGui::TextUnformatted(sizeText.c_str());
       ImGui::TableNextColumn();
       if (m_state == kDownload || m_state == kDownloadDone) {
-        if (!download.status.empty()) {
-          ImGui::TextUnformatted(download.status.c_str());
+        if (!file.status.empty()) {
+          ImGui::TextUnformatted(file.status.c_str());
         } else {
-          ImGui::ProgressBar(download.complete);
+          ImGui::ProgressBar(file.complete);
+        }
+      } else if (m_state == kDelete || m_state == kDeleteDone) {
+        if (!file.status.empty()) {
+          ImGui::TextUnformatted(file.status.c_str());
         }
       } else {
-        auto checkboxLabel = fmt::format("##{}", download.name);
-        ImGui::Checkbox(checkboxLabel.c_str(), &download.enabled);
+        auto checkboxLabel = fmt::format("##{}", file.name);
+        ImGui::Checkbox(checkboxLabel.c_str(), &file.selected);
       }
     }
     ImGui::EndTable();
@@ -224,6 +252,17 @@
         }
       }
       break;
+    case kDelete:
+    case kDeleteDone:
+      DisplayDisconnectButton();
+      DisplayFiles();
+      if (m_state == kDeleteDone) {
+        if (ImGui::Button("Deletion complete!")) {
+          m_state = kGetFiles;
+          m_cv.notify_all();
+        }
+      }
+      break;
     default:
       break;
   }
@@ -274,7 +313,7 @@
             }
             m_error = ex.what();
             m_dirList.clear();
-            m_downloadList.clear();
+            m_fileList.clear();
             m_state = kConnected;
             break;
           }
@@ -284,7 +323,7 @@
           lock.lock();
 
           m_dirList.clear();
-          m_downloadList.clear();
+          m_fileList.clear();
           for (auto&& attr : fileList) {
             if (attr.type == SSH_FILEXFER_TYPE_DIRECTORY) {
               if (attr.name != ".") {
@@ -293,7 +332,7 @@
             } else if (attr.type == SSH_FILEXFER_TYPE_REGULAR &&
                        (attr.flags & SSH_FILEXFER_ATTR_SIZE) != 0 &&
                        wpi::ends_with(attr.name, ".wpilog")) {
-              m_downloadList.emplace_back(attr.name, attr.size);
+              m_fileList.emplace_back(attr.name, attr.size);
             }
           }
 
@@ -305,20 +344,20 @@
           m_state = kDisconnected;
           break;
         case kDownload: {
-          for (auto&& download : m_downloadList) {
+          for (auto&& file : m_fileList) {
             if (m_state != kDownload) {
               // user aborted
               break;
             }
-            if (!download.enabled) {
+            if (!file.selected) {
               continue;
             }
 
             auto remoteFilename = fmt::format(
                 "{}{}{}", m_remoteDir,
-                wpi::ends_with(m_remoteDir, '/') ? "" : "/", download.name);
-            auto localFilename = fs::path{m_localDir} / download.name;
-            uint64_t fileSize = download.size;
+                wpi::ends_with(m_remoteDir, '/') ? "" : "/", file.name);
+            auto localFilename = fs::path{m_localDir} / file.name;
+            uint64_t fileSize = file.size;
 
             lock.unlock();
 
@@ -329,14 +368,14 @@
             if (ec) {
               // failed to open
               lock.lock();
-              download.status = ec.message();
+              file.status = ec.message();
               continue;
             }
             int ofd = fs::FileToFd(of, ec, fs::OF_None);
             if (ofd == -1 || ec) {
               // failed to convert to fd
               lock.lock();
-              download.status = ec.message();
+              file.status = ec.message();
               continue;
             }
 
@@ -356,12 +395,12 @@
                   close(ofd);
                   fs::remove(localFilename, ec);
                   lock.lock();
-                  download.status = "error writing local file";
+                  file.status = "error writing local file";
                   goto err;
                 }
                 total += copied;
                 lock.lock();
-                download.complete = static_cast<float>(total) / fileSize;
+                file.complete = static_cast<float>(total) / fileSize;
                 lock.unlock();
               }
 
@@ -381,20 +420,53 @@
                 fs::remove(localFilename, ec);
               }
               lock.lock();
-              download.status = ex.what();
+              file.status = ex.what();
               if (ex.err == SSH_FX_OK || ex.err == SSH_FX_CONNECTION_LOST) {
                 throw;
               }
               continue;
             }
             lock.lock();
-          err : {}
+          err: {}
           }
           if (m_state == kDownload) {
             m_state = kDownloadDone;
           }
           break;
         }
+        case kDelete: {
+          for (auto&& file : m_fileList) {
+            if (m_state != kDelete) {
+              // user aborted
+              break;
+            }
+            if (!file.selected) {
+              continue;
+            }
+
+            auto remoteFilename = fmt::format(
+                "{}{}{}", m_remoteDir,
+                wpi::ends_with(m_remoteDir, '/') ? "" : "/", file.name);
+
+            lock.unlock();
+            try {
+              session->Unlink(remoteFilename);
+            } catch (sftp::Exception& ex) {
+              lock.lock();
+              file.status = ex.what();
+              if (ex.err == SSH_FX_OK || ex.err == SSH_FX_CONNECTION_LOST) {
+                throw;
+              }
+              continue;
+            }
+            lock.lock();
+            file.status = "Deleted";
+          }
+          if (m_state == kDelete) {
+            m_state = kDeleteDone;
+          }
+          break;
+        }
         default:
           break;
       }
diff --git a/third_party/allwpilib/datalogtool/src/main/native/cpp/Downloader.h b/third_party/allwpilib/datalogtool/src/main/native/cpp/Downloader.h
index f9bb32f..f427b99 100644
--- a/third_party/allwpilib/datalogtool/src/main/native/cpp/Downloader.h
+++ b/third_party/allwpilib/datalogtool/src/main/native/cpp/Downloader.h
@@ -45,6 +45,8 @@
     kGetFiles,
     kDownload,
     kDownloadDone,
+    kDelete,
+    kDeleteDone,
     kExit
   } m_state = kDisconnected;
   std::condition_variable m_cv;
@@ -60,17 +62,16 @@
   bool& m_deleteAfter;
 
   std::vector<std::string> m_dirList;
-  struct DownloadState {
-    DownloadState(std::string_view name, uint64_t size)
-        : name{name}, size{size} {}
+  struct FileState {
+    FileState(std::string_view name, uint64_t size) : name{name}, size{size} {}
 
     std::string name;
     uint64_t size;
-    bool enabled = true;
+    bool selected = true;
     float complete = 0.0;
     std::string status;
   };
-  std::vector<DownloadState> m_downloadList;
+  std::vector<FileState> m_fileList;
 
   std::string m_error;
 
diff --git a/third_party/allwpilib/datalogtool/src/main/native/cpp/Exporter.cpp b/third_party/allwpilib/datalogtool/src/main/native/cpp/Exporter.cpp
index f28545f..9201c8f 100644
--- a/third_party/allwpilib/datalogtool/src/main/native/cpp/Exporter.cpp
+++ b/third_party/allwpilib/datalogtool/src/main/native/cpp/Exporter.cpp
@@ -466,7 +466,8 @@
       fmt::print(os, "{}", val);
       return;
     }
-  } else if (entry.type == "int64") {
+  } else if (entry.type == "int64" || entry.type == "int") {
+    // support "int" for compatibility with old NT4 datalogs
     int64_t val;
     if (record.GetInteger(&val)) {
       fmt::print(os, "{}", val);
diff --git a/third_party/allwpilib/datalogtool/src/main/native/cpp/Sftp.h b/third_party/allwpilib/datalogtool/src/main/native/cpp/Sftp.h
index e6fec4a..e033c65 100644
--- a/third_party/allwpilib/datalogtool/src/main/native/cpp/Sftp.h
+++ b/third_party/allwpilib/datalogtool/src/main/native/cpp/Sftp.h
@@ -4,15 +4,15 @@
 
 #pragma once
 
-#include <libssh/libssh.h>
-#include <libssh/sftp.h>
-
 #include <span>
 #include <stdexcept>
 #include <string>
 #include <string_view>
 #include <vector>
 
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
+
 namespace sftp {
 
 struct Attributes {
diff --git a/third_party/allwpilib/docs/build.gradle b/third_party/allwpilib/docs/build.gradle
index e00e0f3..1a1e3b1 100644
--- a/third_party/allwpilib/docs/build.gradle
+++ b/third_party/allwpilib/docs/build.gradle
@@ -13,6 +13,7 @@
 evaluationDependsOn(':wpilibj')
 evaluationDependsOn(':wpimath')
 evaluationDependsOn(':wpinet')
+evaluationDependsOn(':wpiunits')
 evaluationDependsOn(':wpiutil')
 
 def baseArtifactIdCpp = 'documentation'
@@ -64,20 +65,16 @@
             cppIncludeRoots.add(it.absolutePath)
         }
     }
+    cppIncludeRoots << '../ntcore/build/generated/main/native/include/'
 
     if (project.hasProperty('docWarningsAsErrors')) {
-        // C++20 shims
-        exclude 'wpi/ghc/filesystem.hpp'
-
-        // Drake
-        exclude 'drake/common/**'
-
         // Eigen
         exclude 'Eigen/**'
         exclude 'unsupported/**'
 
         // LLVM
         exclude 'wpi/AlignOf.h'
+        exclude 'wpi/Casting.h'
         exclude 'wpi/Chrono.h'
         exclude 'wpi/Compiler.h'
         exclude 'wpi/ConvertUTF.h'
@@ -88,6 +85,7 @@
         exclude 'wpi/Errc.h'
         exclude 'wpi/Errno.h'
         exclude 'wpi/ErrorHandling.h'
+        exclude 'wpi/bit.h'
         exclude 'wpi/fs.h'
         exclude 'wpi/FunctionExtras.h'
         exclude 'wpi/function_ref.h'
@@ -123,7 +121,13 @@
         exclude 'wpinet/uv/**'
 
         // json
+        exclude 'wpi/adl_serializer.h'
+        exclude 'wpi/byte_container_with_subtype.h'
+        exclude 'wpi/detail/**'
         exclude 'wpi/json.h'
+        exclude 'wpi/json_fwd.h'
+        exclude 'wpi/ordered_map.h'
+        exclude 'wpi/thirdparty/**'
 
         // memory
         exclude 'wpi/memory/**'
@@ -138,6 +142,8 @@
     //TODO: building memory docs causes search to break
     exclude 'wpi/memory/**'
 
+    exclude '*.pb.h'
+
     aliases 'effects=\\par <i>Effects:</i>^^',
             'notes=\\par <i>Notes:</i>^^',
             'requires=\\par <i>Requires:</i>^^',
@@ -174,7 +180,9 @@
     enable_preprocessing true
     macro_expansion true
     expand_only_predef true
-    predefined "WPI_DEPRECATED(x)=[[deprecated(x)]]"
+    predefined "WPI_DEPRECATED(x)=[[deprecated(x)]]\"\\\n" +
+            "\"__cplusplus\"\\\n" +
+            "\"HAL_ENUM(name)=enum name : int32_t"
 
     if (project.hasProperty('docWarningsAsErrors')) {
         warn_as_error 'FAIL_ON_WARNINGS'
@@ -209,7 +217,7 @@
 
 task generateJavaDocs(type: Javadoc) {
     classpath += project(":wpimath").sourceSets.main.compileClasspath
-    options.links("https://docs.oracle.com/en/java/javase/11/docs/api/")
+    options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
     options.addStringOption("tag", "pre:a:Pre-Condition")
     options.addBooleanOption("Xdoclint:html,missing,reference,syntax", true)
     options.addBooleanOption('html5', true)
@@ -227,6 +235,7 @@
     source project(':wpilibj').sourceSets.main.java
     source project(':wpimath').sourceSets.main.java
     source project(':wpinet').sourceSets.main.java
+    source project(':wpiunits').sourceSets.main.java
     source project(':wpiutil').sourceSets.main.java
     source configurations.javaSource.collect { zipTree(it) }
     include '**/*.java'
@@ -238,7 +247,7 @@
     if (JavaVersion.current().isJava8Compatible() && project.hasProperty('docWarningsAsErrors')) {
         // Treat javadoc warnings as errors.
         //
-        // The second argument '-quiet' is a hack. The one paramater
+        // The second argument '-quiet' is a hack. The one parameter
         // addStringOption() doesn't work, so we add '-quiet', which is added
         // anyway by gradle. See https://github.com/gradle/gradle/issues/2354.
         //
diff --git a/third_party/allwpilib/docs/theme.css b/third_party/allwpilib/docs/theme.css
index a5bb22f..2bca53a 100644
--- a/third_party/allwpilib/docs/theme.css
+++ b/third_party/allwpilib/docs/theme.css
@@ -151,7 +151,7 @@
     --side-nav-arrow-opacity: 0;
     --side-nav-arrow-hover-opacity: 0.9;
 
-    /* height of an item in any tree / collapsable table */
+    /* height of an item in any tree / collapsible table */
     --tree-item-height: 30px;
 
     --darkmode-toggle-button-icon: "☀️";
@@ -1615,7 +1615,7 @@
 
 html {
     /* side nav width. MUST be = `TREEVIEW_WIDTH`.
-     * Make sure it is wide enought to contain the page title (logo + title + version)
+     * Make sure it is wide enough to contain the page title (logo + title + version)
      */
     --side-nav-fixed-width: 340px;
     --menu-display: none;
diff --git a/third_party/allwpilib/fieldImages/CMakeLists.txt b/third_party/allwpilib/fieldImages/CMakeLists.txt
index bf02505..3532790 100644
--- a/third_party/allwpilib/fieldImages/CMakeLists.txt
+++ b/third_party/allwpilib/fieldImages/CMakeLists.txt
@@ -11,7 +11,7 @@
     set(CMAKE_JAVA_INCLUDE_PATH fieldImages.jar ${JACKSON_JARS})
 
     file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
-    file(GLOB_RECURSE JAVA_RESOURCES src/main/native/resources/*.json src/main/native/resources/*.png src/main/native/resources/*.jpg)
+    file(GLOB_RECURSE JAVA_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} src/main/native/resources/*.json src/main/native/resources/*.png src/main/native/resources/*.jpg)
     add_jar(field_images_jar SOURCES ${JAVA_SOURCES} RESOURCES NAMESPACE "edu/wpi/first/fields" ${JAVA_RESOURCES} OUTPUT_NAME fieldImages)
 
     get_property(FIELD_IMAGES_JAR_FILE TARGET field_images_jar PROPERTY JAR_FILE)
@@ -22,10 +22,10 @@
 endif()
 
 
-GENERATE_RESOURCES(src/main/native/resources/edu/wpi/first/fields generated/main/cpp FIELDS fields field_images_resources_src)
+generate_resources(src/main/native/resources/edu/wpi/first/fields generated/main/cpp FIELDS fields field_images_resources_src)
 
 
-add_library(fieldImages ${field_images_resources_src})
+add_library(fieldImages src/main/native/cpp/fields.cpp ${field_images_resources_src})
 set_target_properties(fieldImages PROPERTIES DEBUG_POSTFIX "d")
 
 set_property(TARGET fieldImages PROPERTY FOLDER "libraries")
diff --git a/third_party/allwpilib/fieldImages/build.gradle b/third_party/allwpilib/fieldImages/build.gradle
index 123e89d..399ef35 100644
--- a/third_party/allwpilib/fieldImages/build.gradle
+++ b/third_party/allwpilib/fieldImages/build.gradle
@@ -1,76 +1,69 @@
 import org.gradle.internal.os.OperatingSystem
 
-if (!project.hasProperty('onlylinuxathena')) {
+if (project.hasProperty('onlylinuxathena')) {
+    return;
+}
 
-    apply plugin: 'cpp'
-    apply plugin: 'c'
-    apply plugin: 'java'
-    apply plugin: 'google-test-test-suite'
-    apply plugin: 'visual-studio'
-    apply plugin: 'edu.wpi.first.NativeUtils'
+apply plugin: 'cpp'
+apply plugin: 'java'
+apply plugin: 'visual-studio'
+apply plugin: 'edu.wpi.first.NativeUtils'
 
-    if (OperatingSystem.current().isWindows()) {
-        apply plugin: 'windows-resources'
-    }
+if (OperatingSystem.current().isWindows()) {
+    apply plugin: 'windows-resources'
+}
 
-    dependencies {
-        implementation "com.fasterxml.jackson.core:jackson-annotations:2.12.4"
-        implementation "com.fasterxml.jackson.core:jackson-core:2.12.4"
-        implementation "com.fasterxml.jackson.core:jackson-databind:2.12.4"
-    }
+dependencies {
+    implementation "com.fasterxml.jackson.core:jackson-annotations:2.15.2"
+    implementation "com.fasterxml.jackson.core:jackson-core:2.15.2"
+    implementation "com.fasterxml.jackson.core:jackson-databind:2.15.2"
+}
 
-    ext {
-        nativeName = 'fieldImages'
-        baseId = nativeName
-        groupId = 'edu.wpi.first.fieldImages'
-        devMain = "edu.wpi.first.fieldImages.DevMain"
-    }
+ext {
+    nativeName = 'fieldImages'
+    baseId = nativeName
+    groupId = 'edu.wpi.first.fieldImages'
+    devMain = "edu.wpi.first.fieldImages.DevMain"
+}
 
-    apply from: "${rootDir}/shared/resources.gradle"
-    apply from: "${rootDir}/shared/config.gradle"
-    apply from: "${rootDir}/shared/java/javacommon.gradle"
+apply from: "${rootDir}/shared/resources.gradle"
+apply from: "${rootDir}/shared/config.gradle"
+apply from: "${rootDir}/shared/java/javacommon.gradle"
 
-    def generateTask = createGenerateResourcesTask('main', 'FIELDS', 'fields', project)
+def generateTask = createGenerateResourcesTask('main', 'FIELDS', 'fields', project)
 
-    project(':').libraryBuild.dependsOn build
-    tasks.withType(CppCompile) {
-        dependsOn generateTask
-    }
+project(':').libraryBuild.dependsOn build
+tasks.withType(CppCompile) {
+    dependsOn generateTask
+}
 
-    sourceSets {
-        main {
-            resources {
-                srcDirs 'src/main/native/resources'
-            }
-        }
-    }
+sourceSets.main.resources {
+    srcDirs 'src/main/native/resources'
+}
 
-    model {
-        components {
-            "${nativeName}"(NativeLibrarySpec) {
-                baseName = 'fieldImages'
-                sources {
-                    cpp {
-                        source {
-                            srcDirs "$buildDir/generated/main/cpp"
-                            include '**/*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/main/native/include'
-                        }
+model {
+    components {
+        "${nativeName}"(NativeLibrarySpec) {
+            baseName = 'fieldImages'
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp"
+                        include '**/*.cpp'
                     }
-                    if (OperatingSystem.current().isWindows()) {
-                        rc {
-                            source {
-                                srcDirs 'src/main/native/win'
-                                include '*.rc'
-                            }
-                        }
+                    exportedHeaders {
+                        srcDirs 'src/main/native/include'
+                    }
+                }
+                if (OperatingSystem.current().isWindows()) {
+                    rc.source {
+                        srcDirs 'src/main/native/win'
+                        include '*.rc'
                     }
                 }
             }
         }
     }
-
-    apply from: 'publish.gradle'
 }
+
+apply from: 'publish.gradle'
diff --git a/third_party/allwpilib/fieldImages/publish.gradle b/third_party/allwpilib/fieldImages/publish.gradle
index 56b9411..33eb5ae 100644
--- a/third_party/allwpilib/fieldImages/publish.gradle
+++ b/third_party/allwpilib/fieldImages/publish.gradle
@@ -9,7 +9,7 @@
 task cppSourcesZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = cppZipBaseName
-    classifier = "sources"
+    archiveClassifier = "sources"
 
     from(licenseFile) {
         into '/'
@@ -26,7 +26,7 @@
 task cppHeadersZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = cppZipBaseName
-    classifier = "headers"
+    archiveClassifier = "headers"
 
     from(licenseFile) {
         into '/'
diff --git a/third_party/allwpilib/fieldImages/src/main/java/edu/wpi/fields/FieldConfig.java b/third_party/allwpilib/fieldImages/src/main/java/edu/wpi/fields/FieldConfig.java
index a8dd087..f33ddca 100644
--- a/third_party/allwpilib/fieldImages/src/main/java/edu/wpi/fields/FieldConfig.java
+++ b/third_party/allwpilib/fieldImages/src/main/java/edu/wpi/fields/FieldConfig.java
@@ -79,7 +79,7 @@
    *
    * @param resourcePath The path to the resource file
    * @return The field configuration
-   * @throws IOException Throws if the resoure could not be loaded
+   * @throws IOException Throws if the resource could not be loaded
    */
   public static FieldConfig loadFromResource(String resourcePath) throws IOException {
     try (InputStream stream = FieldConfig.class.getResourceAsStream(resourcePath);
diff --git a/third_party/allwpilib/fieldImages/src/main/java/edu/wpi/fields/Fields.java b/third_party/allwpilib/fieldImages/src/main/java/edu/wpi/fields/Fields.java
index 4fc3508..119415e 100644
--- a/third_party/allwpilib/fieldImages/src/main/java/edu/wpi/fields/Fields.java
+++ b/third_party/allwpilib/fieldImages/src/main/java/edu/wpi/fields/Fields.java
@@ -14,12 +14,13 @@
   k2021GalacticSearchA("2021-galacticsearcha.json"),
   k2021GalacticSearchB("2021-galacticsearchb.json"),
   k2021Slalom("2021-slalompath.json"),
-  k2022RapidReact("2022-rapidreact.json");
+  k2022RapidReact("2022-rapidreact.json"),
+  k2023ChargedUp("2023-chargedup.json");
 
   public static final String kBaseResourceDir = "/edu/wpi/first/fields/";
 
   /** Alias to the current game. */
-  public static final Fields kDefaultField = k2022RapidReact;
+  public static final Fields kDefaultField = k2023ChargedUp;
 
   public final String m_resourceFile;
 
diff --git a/third_party/allwpilib/fieldImages/src/main/native/cpp/fields.cpp b/third_party/allwpilib/fieldImages/src/main/native/cpp/fields.cpp
new file mode 100644
index 0000000..4f79e6b
--- /dev/null
+++ b/third_party/allwpilib/fieldImages/src/main/native/cpp/fields.cpp
@@ -0,0 +1,48 @@
+// 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 "fields/fields.h"
+
+#include "fields/2018-powerup.h"
+#include "fields/2019-deepspace.h"
+#include "fields/2020-infiniterecharge.h"
+#include "fields/2021-barrel.h"
+#include "fields/2021-bounce.h"
+#include "fields/2021-galacticsearcha.h"
+#include "fields/2021-galacticsearchb.h"
+#include "fields/2021-infiniterecharge.h"
+#include "fields/2021-slalom.h"
+#include "fields/2022-rapidreact.h"
+#include "fields/2023-chargedup.h"
+
+using namespace fields;
+
+static const Field kFields[] = {
+    {"2023 Charged Up", GetResource_2023_chargedup_json,
+     GetResource_2023_field_png},
+    {"2022 Rapid React", GetResource_2022_rapidreact_json,
+     GetResource_2022_field_png},
+    {"2021 Barrel Racing Path", GetResource_2021_barrelracingpath_json,
+     GetResource_2021_barrel_png},
+    {"2021 Bounce Path", GetResource_2021_bouncepath_json,
+     GetResource_2021_bounce_png},
+    {"2021 Galactic Search A", GetResource_2021_galacticsearcha_json,
+     GetResource_2021_galacticsearcha_png},
+    {"2021 Galactic Search B", GetResource_2021_galacticsearchb_json,
+     GetResource_2021_galacticsearchb_png},
+    {"2021 Infinite Recharge", GetResource_2021_infiniterecharge_json,
+     GetResource_2021_field_png},
+    {"2021 Slalom Path", GetResource_2021_slalompath_json,
+     GetResource_2021_slalom_png},
+    {"2020 Infinite Recharge", GetResource_2020_infiniterecharge_json,
+     GetResource_2020_field_png},
+    {"2019 Destination: Deep Space", GetResource_2019_deepspace_json,
+     GetResource_2019_field_jpg},
+    {"2018 Power Up", GetResource_2018_powerup_json,
+     GetResource_2018_field_jpg},
+};
+
+std::span<const Field> fields::GetFields() {
+  return kFields;
+}
diff --git a/third_party/allwpilib/fieldImages/src/main/native/include/fields/2023-chargedup.h b/third_party/allwpilib/fieldImages/src/main/native/include/fields/2023-chargedup.h
new file mode 100644
index 0000000..54663c5
--- /dev/null
+++ b/third_party/allwpilib/fieldImages/src/main/native/include/fields/2023-chargedup.h
@@ -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.
+
+#pragma once
+
+#include <string_view>
+
+namespace fields {
+std::string_view GetResource_2023_chargedup_json();
+std::string_view GetResource_2023_field_png();
+}  // namespace fields
diff --git a/third_party/allwpilib/fieldImages/src/main/native/include/fields/fields.h b/third_party/allwpilib/fieldImages/src/main/native/include/fields/fields.h
new file mode 100644
index 0000000..ea6b23e
--- /dev/null
+++ b/third_party/allwpilib/fieldImages/src/main/native/include/fields/fields.h
@@ -0,0 +1,20 @@
+// 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 <span>
+#include <string_view>
+
+namespace fields {
+
+struct Field {
+  const char* name;
+  std::string_view (*getJson)();
+  std::string_view (*getImage)();
+};
+
+std::span<const Field> GetFields();
+
+}  // namespace fields
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2018-powerup.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2018-powerup.json
index 1b63c29..74c5883 100644
--- a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2018-powerup.json
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2018-powerup.json
@@ -2,9 +2,18 @@
   "game": "FIRST Power Up",
   "field-image": "2018-field.jpg",
   "field-corners": {
-    "top-left": [125, 20],
-    "bottom-right": [827, 370]
+    "top-left": [
+      125,
+      20
+    ],
+    "bottom-right": [
+      827,
+      370
+    ]
   },
-  "field-size": [54, 27],
+  "field-size": [
+    54,
+    27
+  ],
   "field-unit": "feet"
 }
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2019-deepspace.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2019-deepspace.json
index 96087f7..a0b0fd1 100644
--- a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2019-deepspace.json
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2019-deepspace.json
@@ -1,10 +1,19 @@
 {
-"game" : "Destination: Deep Space",
-"field-image" : "2019-field.jpg",
-"field-corners": {
-	"top-left" : [217, 40],
-	"bottom-right" : [1372, 615]
-},
-"field-size" : [54, 27],
-"field-unit" : "foot"
+  "game": "Destination: Deep Space",
+  "field-image": "2019-field.jpg",
+  "field-corners": {
+    "top-left": [
+      217,
+      40
+    ],
+    "bottom-right": [
+      1372,
+      615
+    ]
+  },
+  "field-size": [
+    54,
+    27
+  ],
+  "field-unit": "foot"
 }
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2020-infiniterecharge.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2020-infiniterecharge.json
index 6187cae..7109f5c 100644
--- a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2020-infiniterecharge.json
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2020-infiniterecharge.json
@@ -1,10 +1,19 @@
 {
-    "game" : "Infinite Recharge",
-    "field-image" : "2020-field.png",
-    "field-corners": {
-        "top-left" : [96, 25],
-        "bottom-right" : [1040, 514]
-    },
-    "field-size" : [52.4375, 26.9375],
-    "field-unit" : "foot"
-    }
+  "game": "Infinite Recharge",
+  "field-image": "2020-field.png",
+  "field-corners": {
+    "top-left": [
+      96,
+      25
+    ],
+    "bottom-right": [
+      1040,
+      514
+    ]
+  },
+  "field-size": [
+    52.4375,
+    26.9375
+  ],
+  "field-unit": "foot"
+}
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-barrelracingpath.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-barrelracingpath.json
index c0e49dc..b7b063c 100644
--- a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-barrelracingpath.json
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-barrelracingpath.json
@@ -1,10 +1,19 @@
 {
-    "game": "Barrel Racing Path",
-    "field-image": "2021-barrel.png",
-    "field-corners": {
-      "top-left": [20, 20],
-      "bottom-right": [780, 400]
-    },
-    "field-size": [30, 15],
-    "field-unit": "feet"
-  }
+  "game": "Barrel Racing Path",
+  "field-image": "2021-barrel.png",
+  "field-corners": {
+    "top-left": [
+      20,
+      20
+    ],
+    "bottom-right": [
+      780,
+      400
+    ]
+  },
+  "field-size": [
+    30,
+    15
+  ],
+  "field-unit": "feet"
+}
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-bouncepath.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-bouncepath.json
index b02b855..a126e22 100644
--- a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-bouncepath.json
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-bouncepath.json
@@ -1,10 +1,19 @@
 {
-    "game": "Bounce Path",
-    "field-image": "2021-bounce.png",
-    "field-corners": {
-      "top-left": [20, 20],
-      "bottom-right": [780, 400]
-    },
-    "field-size": [30, 15],
-    "field-unit": "feet"
-  }
+  "game": "Bounce Path",
+  "field-image": "2021-bounce.png",
+  "field-corners": {
+    "top-left": [
+      20,
+      20
+    ],
+    "bottom-right": [
+      780,
+      400
+    ]
+  },
+  "field-size": [
+    30,
+    15
+  ],
+  "field-unit": "feet"
+}
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-galacticsearcha.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-galacticsearcha.json
index 063d351..5bed818 100644
--- a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-galacticsearcha.json
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-galacticsearcha.json
@@ -1,10 +1,19 @@
 {
-    "game": "Galactic Search A",
-    "field-image": "2021-galacticsearcha.png",
-    "field-corners": {
-      "top-left": [20, 20],
-      "bottom-right": [780, 400]
-    },
-    "field-size": [30, 15],
-    "field-unit": "feet"
-  }
+  "game": "Galactic Search A",
+  "field-image": "2021-galacticsearcha.png",
+  "field-corners": {
+    "top-left": [
+      20,
+      20
+    ],
+    "bottom-right": [
+      780,
+      400
+    ]
+  },
+  "field-size": [
+    30,
+    15
+  ],
+  "field-unit": "feet"
+}
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-galacticsearchb.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-galacticsearchb.json
index 615ec45..0d2d1b5 100644
--- a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-galacticsearchb.json
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-galacticsearchb.json
@@ -1,10 +1,19 @@
 {
-    "game": "Galactic Search B",
-    "field-image": "2021-galacticsearchb.png",
-    "field-corners": {
-      "top-left": [20, 20],
-      "bottom-right": [780, 400]
-    },
-    "field-size": [30, 15],
-    "field-unit": "feet"
-  }
+  "game": "Galactic Search B",
+  "field-image": "2021-galacticsearchb.png",
+  "field-corners": {
+    "top-left": [
+      20,
+      20
+    ],
+    "bottom-right": [
+      780,
+      400
+    ]
+  },
+  "field-size": [
+    30,
+    15
+  ],
+  "field-unit": "feet"
+}
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-infiniterecharge.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-infiniterecharge.json
index d34fc65..9af45c0 100644
--- a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-infiniterecharge.json
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-infiniterecharge.json
@@ -1,10 +1,19 @@
 {
-    "game": "Infinite Recharge 2021",
-    "field-image": "2021-field.png",
-    "field-corners": {
-      "top-left": [127, 34],
-      "bottom-right": [1323, 649]
-    },
-    "field-size": [52.4375, 26.9375],
-    "field-unit": "foot"
-  }
+  "game": "Infinite Recharge 2021",
+  "field-image": "2021-field.png",
+  "field-corners": {
+    "top-left": [
+      127,
+      34
+    ],
+    "bottom-right": [
+      1323,
+      649
+    ]
+  },
+  "field-size": [
+    52.4375,
+    26.9375
+  ],
+  "field-unit": "foot"
+}
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-slalompath.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-slalompath.json
index bb80f55..a728ef3 100644
--- a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-slalompath.json
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2021-slalompath.json
@@ -1,10 +1,19 @@
 {
-    "game": "Slalom Path",
-    "field-image": "2021-slalom.png",
-    "field-corners": {
-      "top-left": [20, 20],
-      "bottom-right": [780, 400]
-    },
-    "field-size": [30, 15],
-    "field-unit": "feet"
-  }
+  "game": "Slalom Path",
+  "field-image": "2021-slalom.png",
+  "field-corners": {
+    "top-left": [
+      20,
+      20
+    ],
+    "bottom-right": [
+      780,
+      400
+    ]
+  },
+  "field-size": [
+    30,
+    15
+  ],
+  "field-unit": "feet"
+}
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2022-rapidreact.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2022-rapidreact.json
index 7098c12..7d2428e 100644
--- a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2022-rapidreact.json
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2022-rapidreact.json
@@ -1,10 +1,19 @@
 {
-    "game": "Rapid React",
-    "field-image": "2022-field.png",
-    "field-corners": {
-      "top-left": [74, 50],
-      "bottom-right": [1774, 900]
-    },
-    "field-size": [54, 27],
-    "field-unit": "foot"
-  }
\ No newline at end of file
+  "game": "Rapid React",
+  "field-image": "2022-field.png",
+  "field-corners": {
+    "top-left": [
+      74,
+      50
+    ],
+    "bottom-right": [
+      1774,
+      900
+    ]
+  },
+  "field-size": [
+    54,
+    27
+  ],
+  "field-unit": "foot"
+}
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2023-chargedup.json b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2023-chargedup.json
new file mode 100644
index 0000000..bbd9f41
--- /dev/null
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2023-chargedup.json
@@ -0,0 +1,19 @@
+{
+  "game": "Charged Up",
+  "field-image": "2023-field.png",
+  "field-corners": {
+    "top-left": [
+      46,
+      36
+    ],
+    "bottom-right": [
+      1088,
+      544
+    ]
+  },
+  "field-size": [
+    54.27083,
+    26.2916
+  ],
+  "field-unit": "foot"
+}
diff --git a/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2023-field.png b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2023-field.png
new file mode 100644
index 0000000..ab3f0ff
--- /dev/null
+++ b/third_party/allwpilib/fieldImages/src/main/native/resources/edu/wpi/first/fields/2023-field.png
Binary files differ
diff --git a/third_party/allwpilib/glass/.styleguide b/third_party/allwpilib/glass/.styleguide
index c2a291a..148c634 100644
--- a/third_party/allwpilib/glass/.styleguide
+++ b/third_party/allwpilib/glass/.styleguide
@@ -22,7 +22,9 @@
   ^GLFW
   ^cscore
   ^fmt/
+  ^fields/
   ^frc/
+  ^google/
   ^imgui
   ^networktables/
   ^ntcore
diff --git a/third_party/allwpilib/glass/CMakeLists.txt b/third_party/allwpilib/glass/CMakeLists.txt
index 41a7819..b949fc0 100644
--- a/third_party/allwpilib/glass/CMakeLists.txt
+++ b/third_party/allwpilib/glass/CMakeLists.txt
@@ -9,20 +9,20 @@
 #
 file(GLOB_RECURSE libglass_src src/lib/native/cpp/*.cpp)
 
-add_library(libglass STATIC ${libglass_src})
+add_library(libglass ${libglass_src})
 set_target_properties(libglass PROPERTIES DEBUG_POSTFIX "d" OUTPUT_NAME "glass")
 set_property(TARGET libglass PROPERTY POSITION_INDEPENDENT_CODE ON)
 
 set_property(TARGET libglass PROPERTY FOLDER "libraries")
 
 wpilib_target_warnings(libglass)
-target_link_libraries(libglass PUBLIC wpigui wpimath wpiutil)
+target_link_libraries(libglass PUBLIC wpigui wpimath wpiutil fieldImages)
 
 target_include_directories(libglass PUBLIC
                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/lib/native/include>
                            $<INSTALL_INTERFACE:${include_dest}/glass>)
 
-install(TARGETS libglass EXPORT libglass DESTINATION "${main_lib_dest}")
+install(TARGETS libglass EXPORT libglass)
 install(DIRECTORY src/lib/native/include/ DESTINATION "${include_dest}/glass")
 
 #
@@ -30,7 +30,7 @@
 #
 file(GLOB_RECURSE libglassnt_src src/libnt/native/cpp/*.cpp)
 
-add_library(libglassnt STATIC ${libglassnt_src})
+add_library(libglassnt ${libglassnt_src})
 set_target_properties(libglassnt PROPERTIES DEBUG_POSTFIX "d" OUTPUT_NAME "glassnt")
 set_property(TARGET libglassnt PROPERTY POSITION_INDEPENDENT_CODE ON)
 
@@ -43,7 +43,7 @@
                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/libnt/native/include>
                            $<INSTALL_INTERFACE:${include_dest}/glass>)
 
-install(TARGETS libglassnt EXPORT libglassnt DESTINATION "${main_lib_dest}")
+install(TARGETS libglassnt EXPORT libglassnt)
 install(DIRECTORY src/libnt/native/include/ DESTINATION "${include_dest}/glass")
 
 #
@@ -51,7 +51,7 @@
 #
 
 configure_file(src/app/generate/WPILibVersion.cpp.in WPILibVersion.cpp)
-GENERATE_RESOURCES(src/app/native/resources generated/app/cpp GLASS glass glass_resources_src)
+generate_resources(src/app/native/resources generated/app/cpp GLASS glass glass_resources_src)
 
 file(GLOB glass_src src/app/native/cpp/*.cpp ${CMAKE_CURRENT_BINARY_DIR}/WPILibVersion.cpp)
 
@@ -65,6 +65,7 @@
 
 add_executable(glass ${glass_src} ${glass_resources_src} ${glass_rc} ${APP_ICON_MACOSX})
 wpilib_link_macos_gui(glass)
+wpilib_target_warnings(glass)
 target_link_libraries(glass libglassnt libglass)
 
 if (WIN32)
diff --git a/third_party/allwpilib/glass/build.gradle b/third_party/allwpilib/glass/build.gradle
index 47b943c..7323936 100644
--- a/third_party/allwpilib/glass/build.gradle
+++ b/third_party/allwpilib/glass/build.gradle
@@ -1,205 +1,201 @@
 import org.gradle.internal.os.OperatingSystem
 
-if (!project.hasProperty('onlylinuxathena')) {
-
-    description = "A different kind of dashboard"
-
-    apply plugin: 'cpp'
-    apply plugin: 'c'
-    apply plugin: 'google-test-test-suite'
-    apply plugin: 'visual-studio'
-    apply plugin: 'edu.wpi.first.NativeUtils'
-
-    if (OperatingSystem.current().isWindows()) {
-        apply plugin: 'windows-resources'
-    }
-
-    ext {
-        nativeName = 'glass'
-    }
-
-    apply from: "${rootDir}/shared/resources.gradle"
-    apply from: "${rootDir}/shared/config.gradle"
-
-    def wpilibVersionFileInput = file("src/app/generate/WPILibVersion.cpp.in")
-    def wpilibVersionFileOutput = file("$buildDir/generated/app/cpp/WPILibVersion.cpp")
-
-    apply from: "${rootDir}/shared/imgui.gradle"
-
-    task generateCppVersion() {
-        description = 'Generates the wpilib version class'
-        group = 'WPILib'
-
-        outputs.file wpilibVersionFileOutput
-        inputs.file wpilibVersionFileInput
-
-        if (wpilibVersioning.releaseMode) {
-            outputs.upToDateWhen { false }
-        }
-
-        // We follow a simple set of checks to determine whether we should generate a new version file:
-        // 1. If the release type is not development, we generate a new version file
-        // 2. If there is no generated version number, we generate a new version file
-        // 3. If there is a generated build number, and the release type is development, then we will
-        //    only generate if the publish task is run.
-        doLast {
-            def version = wpilibVersioning.version.get()
-            println "Writing version ${version} to $wpilibVersionFileOutput"
-
-            if (wpilibVersionFileOutput.exists()) {
-                wpilibVersionFileOutput.delete()
-            }
-            def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version)
-            wpilibVersionFileOutput.write(read)
-        }
-    }
-
-    gradle.taskGraph.addTaskExecutionGraphListener { graph ->
-        def willPublish = graph.hasTask(publish)
-        if (willPublish) {
-            generateCppVersion.outputs.upToDateWhen { false }
-        }
-    }
-
-    def generateTask = createGenerateResourcesTask('app', 'GLASS', 'glass', project)
-
-    project(':').libraryBuild.dependsOn build
-    tasks.withType(CppCompile) {
-        dependsOn generateTask
-        dependsOn generateCppVersion
-    }
-
-    nativeUtils.exportsConfigs {
-        glass {
-            x64ExcludeSymbols = [
-                '_CT??_R0?AV_System_error',
-                '_CT??_R0?AVexception',
-                '_CT??_R0?AVfailure',
-                '_CT??_R0?AVruntime_error',
-                '_CT??_R0?AVsystem_error',
-                '_CTA5?AVfailure',
-                '_TI5?AVfailure',
-                '_CT??_R0?AVout_of_range',
-                '_CTA3?AVout_of_range',
-                '_TI3?AVout_of_range',
-                '_CT??_R0?AVbad_cast'
-            ]
-        }
-    }
-
-    model {
-        components {
-            "${nativeName}"(NativeLibrarySpec) {
-                sources {
-                    cpp {
-                        source {
-                            srcDirs 'src/lib/native/cpp'
-                            include '**/*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/lib/native/include'
-                        }
-                    }
-                }
-                binaries.all {
-                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
-                        it.buildable = false
-                        return
-                    }
-                    if (it instanceof SharedLibraryBinarySpec) {
-                        it.buildable = false
-                        return
-                    }
-                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
-                    lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
-                    lib project: ':wpigui', library: 'wpigui', linkage: 'static'
-                    nativeUtils.useRequiredLibrary(it, 'imgui')
-                }
-                appendDebugPathToBinaries(binaries)
-            }
-            "${nativeName}nt"(NativeLibrarySpec) {
-                sources {
-                    cpp {
-                        source {
-                            srcDirs = ['src/libnt/native/cpp']
-                            include '**/*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/libnt/native/include'
-                        }
-                    }
-                }
-                binaries.all {
-                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
-                        it.buildable = false
-                        return
-                    }
-                    if (it instanceof SharedLibraryBinarySpec) {
-                        it.buildable = false
-                        return
-                    }
-                    lib library: nativeName, linkage: 'static'
-                    project(':ntcore').addNtcoreDependency(it, 'shared')
-                    lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
-                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
-                    lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
-                    lib project: ':wpigui', library: 'wpigui', linkage: 'static'
-                    nativeUtils.useRequiredLibrary(it, 'imgui')
-                }
-                appendDebugPathToBinaries(binaries)
-            }
-            // By default, a development executable will be generated. This is to help the case of
-            // testing specific functionality of the library.
-            "${nativeName}App"(NativeExecutableSpec) {
-                baseName = 'glass'
-                sources {
-                    cpp {
-                        source {
-                            srcDirs 'src/app/native/cpp', "$buildDir/generated/app/cpp"
-                            include '**/*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/app/native/include'
-                        }
-                    }
-                    if (OperatingSystem.current().isWindows()) {
-                        rc {
-                            source {
-                                srcDirs 'src/app/native/win'
-                            }
-                        }
-                    }
-                }
-                binaries.all {
-                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
-                        it.buildable = false
-                        return
-                    }
-                    lib project: ':cscore', library: 'cscore', linkage: 'static'
-                    lib library: 'glassnt', linkage: 'static'
-                    lib library: nativeName, linkage: 'static'
-                    project(':ntcore').addNtcoreDependency(it, 'static')
-                    lib project: ':wpinet', library: 'wpinet', linkage: 'static'
-                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
-                    lib project: ':wpimath', library: 'wpimath', linkage: 'static'
-                    lib project: ':wpigui', library: 'wpigui', linkage: 'static'
-                    nativeUtils.useRequiredLibrary(it, 'opencv_static')
-                    nativeUtils.useRequiredLibrary(it, 'imgui')
-                    if (it.targetPlatform.operatingSystem.isWindows()) {
-                        it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
-                        it.linker.args << '/DELAYLOAD:MF.dll' << '/DELAYLOAD:MFReadWrite.dll' << '/DELAYLOAD:MFPlat.dll' << '/delay:nobind'
-                    } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
-                        it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
-                    } else {
-                        it.linker.args << '-lX11'
-                        if (it.targetPlatform.name.startsWith('linuxarm')) {
-                            it.linker.args << '-lGL'
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    apply from: 'publish.gradle'
+if (project.hasProperty('onlylinuxathena')) {
+    return;
 }
+
+description = "A different kind of dashboard"
+
+apply plugin: 'cpp'
+apply plugin: 'visual-studio'
+apply plugin: 'edu.wpi.first.NativeUtils'
+
+if (OperatingSystem.current().isWindows()) {
+    apply plugin: 'windows-resources'
+}
+
+ext {
+    nativeName = 'glass'
+}
+
+apply from: "${rootDir}/shared/resources.gradle"
+apply from: "${rootDir}/shared/config.gradle"
+
+def wpilibVersionFileInput = file("src/app/generate/WPILibVersion.cpp.in")
+def wpilibVersionFileOutput = file("$buildDir/generated/app/cpp/WPILibVersion.cpp")
+
+apply from: "${rootDir}/shared/imgui.gradle"
+
+task generateCppVersion() {
+    description = 'Generates the wpilib version class'
+    group = 'WPILib'
+
+    outputs.file wpilibVersionFileOutput
+    inputs.file wpilibVersionFileInput
+
+    if (wpilibVersioning.releaseMode) {
+        outputs.upToDateWhen { false }
+    }
+
+    // We follow a simple set of checks to determine whether we should generate a new version file:
+    // 1. If the release type is not development, we generate a new version file
+    // 2. If there is no generated version number, we generate a new version file
+    // 3. If there is a generated build number, and the release type is development, then we will
+    //    only generate if the publish task is run.
+    doLast {
+        def version = wpilibVersioning.version.get()
+        println "Writing version ${version} to $wpilibVersionFileOutput"
+
+        if (wpilibVersionFileOutput.exists()) {
+            wpilibVersionFileOutput.delete()
+        }
+        def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version)
+        wpilibVersionFileOutput.write(read)
+    }
+}
+
+gradle.taskGraph.addTaskExecutionGraphListener { graph ->
+    def willPublish = graph.hasTask(publish)
+    if (willPublish) {
+        generateCppVersion.outputs.upToDateWhen { false }
+    }
+}
+
+def generateTask = createGenerateResourcesTask('app', 'GLASS', 'glass', project)
+
+project(':').libraryBuild.dependsOn build
+tasks.withType(CppCompile) {
+    dependsOn generateTask
+    dependsOn generateCppVersion
+}
+
+nativeUtils.exportsConfigs {
+    glass {
+        x64ExcludeSymbols = [
+            '_CT??_R0?AV_System_error',
+            '_CT??_R0?AVexception',
+            '_CT??_R0?AVfailure',
+            '_CT??_R0?AVruntime_error',
+            '_CT??_R0?AVsystem_error',
+            '_CTA5?AVfailure',
+            '_TI5?AVfailure',
+            '_CT??_R0?AVout_of_range',
+            '_CTA3?AVout_of_range',
+            '_TI3?AVout_of_range',
+            '_CT??_R0?AVbad_cast'
+        ]
+    }
+}
+
+model {
+    components {
+        "${nativeName}"(NativeLibrarySpec) {
+            sources.cpp {
+                source {
+                    srcDirs 'src/lib/native/cpp'
+                    include '**/*.cpp'
+                }
+                exportedHeaders {
+                    srcDirs 'src/lib/native/include'
+                }
+            }
+            binaries.all {
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    it.buildable = false
+                    return
+                }
+                if (it instanceof SharedLibraryBinarySpec) {
+                    it.buildable = false
+                    return
+                }
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
+                lib project: ':wpigui', library: 'wpigui', linkage: 'static'
+                lib project: ':fieldImages', library: 'fieldImages', linkage: 'shared'
+                nativeUtils.useRequiredLibrary(it, 'imgui')
+            }
+            appendDebugPathToBinaries(binaries)
+        }
+        "${nativeName}nt"(NativeLibrarySpec) {
+            sources.cpp {
+                source {
+                    srcDirs = ['src/libnt/native/cpp']
+                    include '**/*.cpp'
+                }
+                exportedHeaders {
+                    srcDirs 'src/libnt/native/include'
+                }
+            }
+            binaries.all {
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    it.buildable = false
+                    return
+                }
+                if (it instanceof SharedLibraryBinarySpec) {
+                    it.buildable = false
+                    return
+                }
+                lib library: nativeName, linkage: 'static'
+                project(':ntcore').addNtcoreDependency(it, 'shared')
+                lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
+                lib project: ':wpigui', library: 'wpigui', linkage: 'static'
+                lib project: ':fieldImages', library: 'fieldImages', linkage: 'shared'
+                nativeUtils.useRequiredLibrary(it, 'imgui')
+            }
+            appendDebugPathToBinaries(binaries)
+        }
+        // By default, a development executable will be generated. This is to help the case of
+        // testing specific functionality of the library.
+        "${nativeName}App"(NativeExecutableSpec) {
+            baseName = 'glass'
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/app/native/cpp', "$buildDir/generated/app/cpp"
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/app/native/include'
+                    }
+                }
+                if (OperatingSystem.current().isWindows()) {
+                    rc.source {
+                        srcDirs 'src/app/native/win'
+                    }
+                }
+            }
+            binaries.all {
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    it.buildable = false
+                    return
+                }
+                lib project: ':cscore', library: 'cscore', linkage: 'static'
+                lib library: 'glassnt', linkage: 'static'
+                lib library: nativeName, linkage: 'static'
+                project(':ntcore').addNtcoreDependency(it, 'static')
+                lib project: ':wpinet', library: 'wpinet', linkage: 'static'
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                lib project: ':wpimath', library: 'wpimath', linkage: 'static'
+                lib project: ':wpigui', library: 'wpigui', linkage: 'static'
+                lib project: ':fieldImages', library: 'fieldImages', linkage: 'static'
+                nativeUtils.useRequiredLibrary(it, 'opencv_static')
+                nativeUtils.useRequiredLibrary(it, 'imgui')
+                if (it.targetPlatform.operatingSystem.isWindows()) {
+                    it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
+                    it.linker.args << '/DELAYLOAD:MF.dll' << '/DELAYLOAD:MFReadWrite.dll' << '/DELAYLOAD:MFPlat.dll' << '/delay:nobind'
+                } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
+                    it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
+                } else {
+                    it.linker.args << '-lX11'
+                    if (it.targetPlatform.name.startsWith('linuxarm')) {
+                        it.linker.args << '-lGL'
+                    }
+                }
+            }
+        }
+    }
+}
+
+apply from: 'publish.gradle'
diff --git a/third_party/allwpilib/glass/publish.gradle b/third_party/allwpilib/glass/publish.gradle
index 5e7cbaf..0797fa1 100644
--- a/third_party/allwpilib/glass/publish.gradle
+++ b/third_party/allwpilib/glass/publish.gradle
@@ -17,7 +17,7 @@
 task libCppSourcesZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = libZipBaseName
-    classifier = "sources"
+    archiveClassifier = "sources"
 
     from(licenseFile) { into '/' }
     from('src/lib/native/cpp') { into '/' }
@@ -26,7 +26,7 @@
 task libCppHeadersZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = libZipBaseName
-    classifier = "headers"
+    archiveClassifier = "headers"
 
     from(licenseFile) { into '/' }
     from('src/lib/native/include') { into '/' }
@@ -35,7 +35,7 @@
 task libntCppSourcesZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = libntZipBaseName
-    classifier = "sources"
+    archiveClassifier = "sources"
 
     from(licenseFile) { into '/' }
     from('src/libnt/native/cpp') { into '/' }
@@ -44,7 +44,7 @@
 task libntCppHeadersZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = libntZipBaseName
-    classifier = "headers"
+    archiveClassifier = "headers"
 
     from(licenseFile) { into '/' }
     from('src/libnt/native/include') { into '/' }
@@ -84,49 +84,14 @@
                         def applicationPath = binary.executable.file
                         def icon = file("$project.projectDir/src/app/native/mac/glass.icns")
 
-                        // Create the macOS bundle.
-                        def bundleTask = project.tasks.create("bundleGlassOsxApp" + binary.targetPlatform.architecture.name, Copy) {
-                            description("Creates a macOS application bundle for Glass")
-                            from(file("$project.projectDir/Info.plist"))
-                            into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/Glass.app/Contents"))
-                            into("MacOS") { with copySpec { from binary.executable.file } }
-                            into("Resources") { with copySpec { from icon } }
-
-                            inputs.property "HasDeveloperId", project.hasProperty("developerID")
-
-                            doLast {
-                                if (project.hasProperty("developerID")) {
-                                    // Get path to binary.
-                                    exec {
-                                        workingDir rootDir
-                                        def args = [
-                                            "sh",
-                                            "-c",
-                                            "codesign --force --strict --deep " +
-                                            "--timestamp --options=runtime " +
-                                            "--verbose -s ${project.findProperty("developerID")} " +
-                                            "$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/Glass.app/"
-                                        ]
-                                        commandLine args
-                                    }
-                                }
-                            }
-                        }
-
-                        // Reset the application path if we are creating a bundle.
-                        if (binary.targetPlatform.operatingSystem.isMacOsX()) {
-                            applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name")
-                            project.build.dependsOn bundleTask
-                        }
-
                         // Create the ZIP.
-                        def task = project.tasks.create("copyGlassExecutable" + binary.targetPlatform.architecture.name, Zip) {
+                        def task = project.tasks.create("copyGlassExecutable" + binary.targetPlatform.operatingSystem.name + binary.targetPlatform.architecture.name, Zip) {
                             description("Copies the Glass executable to the outputs directory.")
                             destinationDirectory = outputsFolder
 
                             archiveBaseName = '_M_' + zipBaseName
                             duplicatesStrategy = 'exclude'
-                            classifier = nativeUtils.getPublishClassifier(binary)
+                            archiveClassifier = nativeUtils.getPublishClassifier(binary)
 
                             from(licenseFile) {
                                 into '/'
@@ -145,6 +110,47 @@
                         }
 
                         if (binary.targetPlatform.operatingSystem.isMacOsX()) {
+                            // Create the macOS bundle.
+                            def bundleTask = project.tasks.create("bundleGlassOsxApp" + binary.targetPlatform.architecture.name, Copy) {
+                                description("Creates a macOS application bundle for Glass")
+                                from(file("$project.projectDir/Info.plist"))
+                                into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/Glass.app/Contents"))
+                                into("MacOS") {
+                                    with copySpec {
+                                        from binary.executable.file
+                                    }
+                                }
+                                into("Resources") {
+                                    with copySpec {
+                                        from icon
+                                    }
+                                }
+
+                                inputs.property "HasDeveloperId", project.hasProperty("developerID")
+
+                                doLast {
+                                    if (project.hasProperty("developerID")) {
+                                        // Get path to binary.
+                                        exec {
+                                            workingDir rootDir
+                                            def args = [
+                                                "sh",
+                                                "-c",
+                                                "codesign --force --strict --deep " +
+                                                "--timestamp --options=runtime " +
+                                                "--verbose -s ${project.findProperty("developerID")} " +
+                                                "$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/Glass.app/"
+                                            ]
+                                            commandLine args
+                                        }
+                                    }
+                                }
+                            }
+
+                            // Reset the application path if we are creating a bundle.
+                            applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name")
+                            project.build.dependsOn bundleTask
+
                             bundleTask.dependsOn binary.tasks.link
                             task.dependsOn(bundleTask)
                         }
diff --git a/third_party/allwpilib/glass/src/app/native/cpp/main.cpp b/third_party/allwpilib/glass/src/app/native/cpp/main.cpp
index a20ff8b..0b3473c 100644
--- a/third_party/allwpilib/glass/src/app/native/cpp/main.cpp
+++ b/third_party/allwpilib/glass/src/app/native/cpp/main.cpp
@@ -8,7 +8,9 @@
 #include <fmt/format.h>
 #include <imgui.h>
 #include <ntcore_cpp.h>
+#include <wpi/StringExtras.h>
 #include <wpigui.h>
+#include <wpigui_openurl.h>
 
 #include "glass/Context.h"
 #include "glass/MainMenuBar.h"
@@ -52,6 +54,8 @@
 static bool gKeyEdit = false;
 static int* gEnterKey;
 static void (*gPrevKeyCallback)(GLFWwindow*, int, int, int, int);
+static bool gNetworkTablesDebugLog = false;
+static unsigned int gPrevMode = NT_NET_MODE_NONE;
 
 static void RemapEnterKeyCallback(GLFWwindow* window, int key, int scancode,
                                   int action, int mods) {
@@ -69,27 +73,46 @@
   }
 }
 
+/**
+ * Generates the proper title bar title based on current instance state and
+ * event.
+ */
+static std::string MakeTitle(NT_Inst inst, nt::Event event) {
+  auto mode = nt::GetNetworkMode(inst);
+  if (mode & NT_NET_MODE_SERVER) {
+    auto numClients = nt::GetConnections(inst).size();
+    return fmt::format("Glass - {} Client{} Connected", numClients,
+                       (numClients == 1 ? "" : "s"));
+  } else if (mode & NT_NET_MODE_CLIENT3 || mode & NT_NET_MODE_CLIENT4) {
+    if (event.Is(NT_EVENT_CONNECTED)) {
+      return fmt::format("Glass - Connected ({})",
+                         event.GetConnectionInfo()->remote_ip);
+    }
+  }
+  return "Glass - DISCONNECTED";
+}
+
 static void NtInitialize() {
   auto inst = nt::GetDefaultInstance();
   auto poller = nt::CreateListenerPoller(inst);
-  nt::AddPolledListener(
-      poller, inst,
-      NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE | NT_EVENT_LOGMESSAGE);
-  gui::AddEarlyExecute([poller] {
+  nt::AddPolledListener(poller, inst, NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE);
+  nt::AddPolledLogger(poller, 0, 100);
+  gui::AddEarlyExecute([inst, poller] {
     auto win = gui::GetSystemWindow();
     if (!win) {
       return;
     }
+    bool updateTitle = false;
+    nt::Event connectionEvent;
+    if (nt::GetNetworkMode(inst) != gPrevMode) {
+      gPrevMode = nt::GetNetworkMode(inst);
+      updateTitle = true;
+    }
+
     for (auto&& event : nt::ReadListenerQueue(poller)) {
-      if (auto connInfo = event.GetConnectionInfo()) {
-        // update window title when connection status changes
-        if ((event.flags & NT_EVENT_CONNECTED) != 0) {
-          glfwSetWindowTitle(
-              win, fmt::format("Glass - Connected ({})", connInfo->remote_ip)
-                       .c_str());
-        } else {
-          glfwSetWindowTitle(win, "Glass - DISCONNECTED");
-        }
+      if (event.Is(NT_EVENT_CONNECTION)) {
+        updateTitle = true;
+        connectionEvent = event;
       } else if (auto msg = event.GetLogMessage()) {
         const char* level = "";
         if (msg->level >= NT_LOG_CRITICAL) {
@@ -98,11 +121,17 @@
           level = "ERROR: ";
         } else if (msg->level >= NT_LOG_WARNING) {
           level = "WARNING: ";
+        } else if (msg->level < NT_LOG_INFO && !gNetworkTablesDebugLog) {
+          continue;
         }
         gNetworkTablesLog.Append(fmt::format(
             "{}{} ({}:{})\n", level, msg->message, msg->filename, msg->line));
       }
     }
+
+    if (updateTitle) {
+      glfwSetWindowTitle(win, MakeTitle(inst, connectionEvent).c_str());
+    }
   });
 
   gNetworkTablesLogWindow = std::make_unique<glass::Window>(
@@ -232,6 +261,8 @@
       if (gNetworkTablesLogWindow) {
         gNetworkTablesLogWindow->DisplayMenuItem("NetworkTables Log");
       }
+      ImGui::MenuItem("NetworkTables Debug Logging", nullptr,
+                      &gNetworkTablesDebugLog);
       ImGui::Separator();
       gNtProvider->DisplayMenu();
       ImGui::EndMenu();
@@ -252,6 +283,15 @@
       }
       ImGui::EndMenu();
     }
+
+    if (ImGui::BeginMenu("Docs")) {
+      if (ImGui::MenuItem("Online documentation")) {
+        wpi::gui::OpenURL(
+            "https://docs.wpilib.org/en/stable/docs/software/dashboards/"
+            "glass/");
+      }
+      ImGui::EndMenu();
+    }
   });
 
   gui::AddLateExecute([] {
@@ -265,6 +305,8 @@
       ImGui::Text("v%s", GetWPILibVersion());
       ImGui::Separator();
       ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
+      ImGui::Text("%.3f ms/frame (%.1f FPS)",
+                  1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
       if (ImGui::Button("Close")) {
         ImGui::CloseCurrentPopup();
       }
@@ -286,11 +328,13 @@
       char nameBuf[32];
       const char* name = glfwGetKeyName(*gEnterKey, 0);
       if (!name) {
-        std::snprintf(nameBuf, sizeof(nameBuf), "%d", *gEnterKey);
+        wpi::format_to_n_c_str(nameBuf, sizeof(nameBuf), "{}", *gEnterKey);
+
         name = nameBuf;
       }
-      std::snprintf(editLabel, sizeof(editLabel), "%s###edit",
-                    gKeyEdit ? "(press key)" : name);
+      wpi::format_to_n_c_str(editLabel, sizeof(editLabel), "{}###edit",
+                             gKeyEdit ? "(press key)" : name);
+
       if (ImGui::SmallButton(editLabel)) {
         gKeyEdit = true;
       }
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/Context.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/Context.cpp
index a55cf82..e09a86e 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/Context.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/Context.cpp
@@ -6,18 +6,16 @@
 
 #include <algorithm>
 #include <cinttypes>
-#include <cstdio>
 #include <filesystem>
 
 #include <fmt/format.h>
 #include <imgui.h>
 #include <imgui_internal.h>
 #include <imgui_stdlib.h>
+#include <wpi/MemoryBuffer.h>
 #include <wpi/StringExtras.h>
 #include <wpi/fs.h>
 #include <wpi/json.h>
-#include <wpi/json_serializer.h>
-#include <wpi/raw_istream.h>
 #include <wpi/raw_ostream.h>
 #include <wpi/timestamp.h>
 #include <wpigui.h>
@@ -131,14 +129,17 @@
 
 static bool LoadWindowStorageImpl(const std::string& filename) {
   std::error_code ec;
-  wpi::raw_fd_istream is{filename, ec};
-  if (ec) {
+  std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+      wpi::MemoryBuffer::GetFile(filename, ec);
+  if (fileBuffer == nullptr || ec) {
     ImGui::LogText("error opening %s: %s", filename.c_str(),
                    ec.message().c_str());
     return false;
   } else {
     try {
-      return JsonToWindow(wpi::json::parse(is), filename.c_str());
+      return JsonToWindow(
+          wpi::json::parse(fileBuffer->begin(), fileBuffer->end()),
+          filename.c_str());
     } catch (wpi::json::parse_error& e) {
       ImGui::LogText("Error loading %s: %s", filename.c_str(), e.what());
       return false;
@@ -149,8 +150,9 @@
 static bool LoadStorageRootImpl(Context* ctx, const std::string& filename,
                                 std::string_view rootName) {
   std::error_code ec;
-  wpi::raw_fd_istream is{filename, ec};
-  if (ec) {
+  std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+      wpi::MemoryBuffer::GetFile(filename, ec);
+  if (fileBuffer == nullptr || ec) {
     ImGui::LogText("error opening %s: %s", filename.c_str(),
                    ec.message().c_str());
     return false;
@@ -162,7 +164,9 @@
       createdStorage = true;
     }
     try {
-      storage->FromJson(wpi::json::parse(is), filename.c_str());
+      storage->FromJson(
+          wpi::json::parse(fileBuffer->begin(), fileBuffer->end()),
+          filename.c_str());
     } catch (wpi::json::parse_error& e) {
       ImGui::LogText("Error loading %s: %s", filename.c_str(), e.what());
       if (createdStorage) {
@@ -533,7 +537,8 @@
 
 void glass::PushID(int int_id) {
   char buf[16];
-  std::snprintf(buf, sizeof(buf), "%d", int_id);
+  wpi::format_to_n_c_str(buf, sizeof(buf), "{}", int_id);
+
   PushStorageStack(buf);
   ImGui::PushID(int_id);
 }
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/MainMenuBar.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/MainMenuBar.cpp
index 879f664..b426df4 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/MainMenuBar.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/MainMenuBar.cpp
@@ -4,9 +4,8 @@
 
 #include "glass/MainMenuBar.h"
 
-#include <cstdio>
-
 #include <imgui.h>
+#include <wpi/StringExtras.h>
 #include <wpigui.h>
 
 #include "glass/Context.h"
@@ -52,11 +51,11 @@
 
 #if 0
   char str[64];
-  std::snprintf(str, sizeof(str), "%.3f ms/frame (%.1f FPS)",
-                1000.0f / ImGui::GetIO().Framerate,
-                ImGui::GetIO().Framerate);
-  ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::CalcTextSize(str).x -
-                  10);
+  wpi::format_to_n_c_str(str, sizeof(str), "{:.3f} ms/frame ({:.1f} FPS)",
+                         1000.0f / ImGui::GetIO().Framerate,
+                         ImGui::GetIO().Framerate);
+
+  ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::CalcTextSize(str).x - 10);
   ImGui::Text("%s", str);
 #endif
   ImGui::EndMainMenuBar();
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/Storage.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/Storage.cpp
index add6203..6cab443 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/Storage.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/Storage.cpp
@@ -4,7 +4,7 @@
 
 #include "glass/Storage.h"
 
-#include <type_traits>
+#include <concepts>
 
 #include <imgui.h>
 #include <wpi/StringExtras.h>
@@ -14,7 +14,7 @@
 
 template <typename To>
 bool ConvertFromString(To* out, std::string_view str) {
-  if constexpr (std::is_same_v<To, bool>) {
+  if constexpr (std::same_as<To, bool>) {
     if (str == "true") {
       *out = true;
     } else if (str == "false") {
@@ -24,7 +24,7 @@
     } else {
       return false;
     }
-  } else if constexpr (std::is_floating_point_v<To>) {
+  } else if constexpr (std::floating_point<To>) {
     if (auto val = wpi::parse_float<To>(str)) {
       *out = val.value();
     } else {
@@ -95,10 +95,14 @@
 template <typename From, typename To>
 static void ConvertArray(std::vector<To>** outPtr, std::vector<From>** inPtr) {
   if (*inPtr) {
-    std::vector<To>* tmp;
-    tmp = new std::vector<To>{(*inPtr)->begin(), (*inPtr)->end()};
-    delete *inPtr;
-    *outPtr = tmp;
+    if (*outPtr) {
+      (*outPtr)->assign((*inPtr)->begin(), (*inPtr)->end());
+    } else {
+      std::vector<To>* tmp;
+      tmp = new std::vector<To>{(*inPtr)->begin(), (*inPtr)->end()};
+      delete *inPtr;
+      *outPtr = tmp;
+    }
   } else {
     *outPtr = nullptr;
   }
@@ -300,7 +304,7 @@
     childPtr = std::make_unique<Value>();
   }
   if (childPtr->type != Value::kChild) {
-    childPtr->type = Value::kChild;
+    childPtr->Reset(Value::kChild);
     childPtr->child = new Storage;
   }
   return *childPtr->child;
@@ -630,22 +634,46 @@
         value.stringVal = value.stringDefault;
         break;
       case Value::kIntArray:
-        *value.intArray = *value.intArrayDefault;
+        if (value.intArrayDefault) {
+          *value.intArray = *value.intArrayDefault;
+        } else {
+          value.intArray->clear();
+        }
         break;
       case Value::kInt64Array:
-        *value.int64Array = *value.int64ArrayDefault;
+        if (value.int64ArrayDefault) {
+          *value.int64Array = *value.int64ArrayDefault;
+        } else {
+          value.int64Array->clear();
+        }
         break;
       case Value::kBoolArray:
-        *value.boolArray = *value.boolArrayDefault;
+        if (value.boolArrayDefault) {
+          *value.boolArray = *value.boolArrayDefault;
+        } else {
+          value.boolArray->clear();
+        }
         break;
       case Value::kFloatArray:
-        *value.floatArray = *value.floatArrayDefault;
+        if (value.floatArrayDefault) {
+          *value.floatArray = *value.floatArrayDefault;
+        } else {
+          value.floatArray->clear();
+        }
         break;
       case Value::kDoubleArray:
-        *value.doubleArray = *value.doubleArrayDefault;
+        if (value.doubleArrayDefault) {
+          *value.doubleArray = *value.doubleArrayDefault;
+        } else {
+          value.doubleArray->clear();
+        }
         break;
       case Value::kStringArray:
-        *value.stringArray = *value.stringArrayDefault;
+        if (value.stringArrayDefault) {
+          *value.stringArray = *value.stringArrayDefault;
+        } else {
+          value.stringArray->clear();
+        }
         break;
       case Value::kChild:
         value.child->Clear();
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/Window.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/Window.cpp
index f43c0ee..32c7a21 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/Window.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/Window.cpp
@@ -56,9 +56,12 @@
   }
 
   char label[128];
-  std::snprintf(label, sizeof(label), "%s###%s",
-                m_name.empty() ? m_defaultName.c_str() : m_name.c_str(),
-                m_id.c_str());
+  if (m_name.empty()) {
+    wpi::format_to_n_c_str(label, sizeof(label), "{}###{}", m_defaultName,
+                           m_id);
+  } else {
+    wpi::format_to_n_c_str(label, sizeof(label), "{}###{}", m_name, m_id);
+  }
 
   if (Begin(label, &m_visible, m_flags)) {
     if (m_renamePopupEnabled || m_view->HasSettings()) {
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogGyro.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogGyro.cpp
index be06a71..259539f 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogGyro.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogGyro.cpp
@@ -4,6 +4,8 @@
 
 #include "glass/hardware/AnalogGyro.h"
 
+#include <wpi/StringExtras.h>
+
 #include "glass/DataSource.h"
 #include "glass/other/DeviceTree.h"
 
@@ -11,7 +13,8 @@
 
 void glass::DisplayAnalogGyroDevice(AnalogGyroModel* model, int index) {
   char name[32];
-  std::snprintf(name, sizeof(name), "AnalogGyro[%d]", index);
+  wpi::format_to_n_c_str(name, sizeof(name), "AnalogGyro[{}]", index);
+
   if (BeginDevice(name)) {
     // angle
     if (auto angleData = model->GetAngleData()) {
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogInput.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogInput.cpp
index af22511..a2051cf 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogInput.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogInput.cpp
@@ -5,6 +5,7 @@
 #include "glass/hardware/AnalogInput.h"
 
 #include <imgui.h>
+#include <wpi/StringExtras.h>
 
 #include "glass/Context.h"
 #include "glass/DataSource.h"
@@ -22,9 +23,9 @@
   std::string& name = GetStorage().GetString("name");
   char label[128];
   if (!name.empty()) {
-    std::snprintf(label, sizeof(label), "%s [%d]###name", name.c_str(), index);
+    wpi::format_to_n_c_str(label, sizeof(label), "{} [{}]###name", name, index);
   } else {
-    std::snprintf(label, sizeof(label), "In[%d]###name", index);
+    wpi::format_to_n_c_str(label, sizeof(label), "In[{}]###name", index);
   }
 
   if (model->IsGyro()) {
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogOutput.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogOutput.cpp
index 174e013..2436dd2 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogOutput.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/AnalogOutput.cpp
@@ -4,6 +4,8 @@
 
 #include "glass/hardware/AnalogOutput.h"
 
+#include <wpi/StringExtras.h>
+
 #include "glass/Context.h"
 #include "glass/DataSource.h"
 #include "glass/Storage.h"
@@ -30,9 +32,9 @@
       std::string& name = GetStorage().GetString("name");
       char label[128];
       if (!name.empty()) {
-        std::snprintf(label, sizeof(label), "%s [%d]###name", name.c_str(), i);
+        wpi::format_to_n_c_str(label, sizeof(label), "{} [{}]###name", name, i);
       } else {
-        std::snprintf(label, sizeof(label), "Out[%d]###name", i);
+        wpi::format_to_n_c_str(label, sizeof(label), "Out[{}]###name", i);
       }
 
       double value = analogOutData->GetValue();
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/Encoder.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/Encoder.cpp
index 7032636..b359274 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/Encoder.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/Encoder.cpp
@@ -6,6 +6,7 @@
 
 #include <fmt/format.h>
 #include <imgui.h>
+#include <wpi/StringExtras.h>
 
 #include "glass/Context.h"
 #include "glass/DataSource.h"
@@ -70,10 +71,11 @@
   std::string& name = GetStorage().GetString("name");
   char label[128];
   if (!name.empty()) {
-    std::snprintf(label, sizeof(label), "%s [%d,%d]###header", name.c_str(),
-                  chA, chB);
+    wpi::format_to_n_c_str(label, sizeof(label), "{} [{},{}]###header", name,
+                           chA, chB);
   } else {
-    std::snprintf(label, sizeof(label), "Encoder[%d,%d]###header", chA, chB);
+    wpi::format_to_n_c_str(label, sizeof(label), "Encoder[{},{}]###header", chA,
+                           chB);
   }
 
   // header
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/Gyro.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/Gyro.cpp
index 607b251..8ba5d5e 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/Gyro.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/Gyro.cpp
@@ -5,12 +5,13 @@
 #include "glass/hardware/Gyro.h"
 
 #include <cmath>
+#include <numbers>
 
 #define IMGUI_DEFINE_MATH_OPERATORS
 
 #include <imgui.h>
 #include <imgui_internal.h>
-#include <numbers>
+#include <wpi/StringExtras.h>
 
 #include "glass/Context.h"
 #include "glass/DataSource.h"
@@ -65,7 +66,8 @@
                   color, 1.2f);
     if (major) {
       char txt[16];
-      std::snprintf(txt, sizeof(txt), "%d°", i);
+      wpi::format_to_n_c_str(txt, sizeof(txt), "{}°", i);
+
       draw->AddText(
           center + (direction * radius * 1.25) - ImGui::CalcTextSize(txt) * 0.5,
           primaryColor, txt, nullptr);
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PCM.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PCM.cpp
index d260bda..6238fd9 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PCM.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PCM.cpp
@@ -9,6 +9,7 @@
 
 #include <imgui.h>
 #include <wpi/SmallVector.h>
+#include <wpi/StringExtras.h>
 
 #include "glass/Context.h"
 #include "glass/DataSource.h"
@@ -46,10 +47,10 @@
   std::string& name = GetStorage().GetString("name");
   char label[128];
   if (!name.empty()) {
-    std::snprintf(label, sizeof(label), "%s [%d]###header", name.c_str(),
-                  index);
+    wpi::format_to_n_c_str(label, sizeof(label), "{} [{}]###header", name,
+                           index);
   } else {
-    std::snprintf(label, sizeof(label), "PCM[%d]###header", index);
+    wpi::format_to_n_c_str(label, sizeof(label), "PCM[{}]###header", index);
   }
 
   // header
@@ -111,7 +112,8 @@
 void glass::DisplayCompressorDevice(CompressorModel* model, int index,
                                     bool outputsEnabled) {
   char name[32];
-  std::snprintf(name, sizeof(name), "Compressor[%d]", index);
+  wpi::format_to_n_c_str(name, sizeof(name), "Compressor[{}]", index);
+
   if (BeginDevice(name)) {
     // output enabled
     if (auto runningData = model->GetRunningData()) {
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PWM.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PWM.cpp
index 0200ac6..f719a2b 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PWM.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PWM.cpp
@@ -5,6 +5,7 @@
 #include "glass/hardware/PWM.h"
 
 #include <imgui.h>
+#include <wpi/StringExtras.h>
 
 #include "glass/Context.h"
 #include "glass/DataSource.h"
@@ -22,9 +23,9 @@
   std::string& name = GetStorage().GetString("name");
   char label[128];
   if (!name.empty()) {
-    std::snprintf(label, sizeof(label), "%s [%d]###name", name.c_str(), index);
+    wpi::format_to_n_c_str(label, sizeof(label), "{} [{}]###name", name, index);
   } else {
-    std::snprintf(label, sizeof(label), "PWM[%d]###name", index);
+    wpi::format_to_n_c_str(label, sizeof(label), "PWM[{}]###name", index);
   }
 
   int led = model->GetAddressableLED();
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PowerDistribution.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PowerDistribution.cpp
index f1de461..90aea0e 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PowerDistribution.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/hardware/PowerDistribution.cpp
@@ -5,9 +5,9 @@
 #include "glass/hardware/PowerDistribution.h"
 
 #include <algorithm>
-#include <cstdio>
 
 #include <imgui.h>
+#include <wpi/StringExtras.h>
 
 #include "glass/Context.h"
 #include "glass/DataSource.h"
@@ -36,7 +36,8 @@
 
 void glass::DisplayPowerDistribution(PowerDistributionModel* model, int index) {
   char name[128];
-  std::snprintf(name, sizeof(name), "PowerDistribution[%d]", index);
+  wpi::format_to_n_c_str(name, sizeof(name), "PowerDistribution[{}]", index);
+
   if (CollapsingHeader(name)) {
     // temperature
     if (auto tempData = model->GetTemperatureData()) {
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/other/DeviceTree.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/other/DeviceTree.cpp
index cfce8c4..b242c07 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/other/DeviceTree.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/other/DeviceTree.cpp
@@ -7,6 +7,7 @@
 #include <cinttypes>
 
 #include <imgui.h>
+#include <wpi/StringExtras.h>
 
 #include "glass/Context.h"
 #include "glass/ContextInternal.h"
@@ -53,8 +54,11 @@
   // build label
   std::string& name = GetStorage().GetString("name");
   char label[128];
-  std::snprintf(label, sizeof(label), "%s###header",
-                name.empty() ? id : name.c_str());
+  if (name.empty()) {
+    wpi::format_to_n_c_str(label, sizeof(label), "{}###header", id);
+  } else {
+    wpi::format_to_n_c_str(label, sizeof(label), "{}###header", name);
+  }
 
   bool open = CollapsingHeader(label, flags);
   PopupEditName("header", &name);
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/other/FMS.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/other/FMS.cpp
index fbd504e..67c3f8c 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/other/FMS.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/other/FMS.cpp
@@ -11,8 +11,8 @@
 
 using namespace glass;
 
-static const char* stations[] = {"Red 1",  "Red 2",  "Red 3",
-                                 "Blue 1", "Blue 2", "Blue 3"};
+static const char* stations[] = {"Invalid", "Red 1",  "Red 2", "Red 3",
+                                 "Blue 1",  "Blue 2", "Blue 3"};
 
 void glass::DisplayFMS(FMSModel* model) {
   if (!model->Exists() || model->IsReadOnly()) {
@@ -41,7 +41,7 @@
   if (auto data = model->GetAllianceStationIdData()) {
     int val = data->GetValue();
     ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
-    if (ImGui::Combo("Alliance Station", &val, stations, 6)) {
+    if (ImGui::Combo("Alliance Station", &val, stations, 7)) {
       model->SetAllianceStationId(val);
     }
     data->EmitDrag();
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/other/Field2D.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/other/Field2D.cpp
index 66e90b1..a99da09 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/other/Field2D.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/other/Field2D.cpp
@@ -11,6 +11,7 @@
 #include <string_view>
 #include <utility>
 
+#include <fields/fields.h>
 #include <fmt/format.h>
 #include <frc/geometry/Pose2d.h>
 #include <frc/geometry/Rotation2d.h>
@@ -23,12 +24,12 @@
 #include <portable-file-dialogs.h>
 #include <units/angle.h>
 #include <units/length.h>
+#include <wpi/MemoryBuffer.h>
 #include <wpi/SmallString.h>
 #include <wpi/StringExtras.h>
 #include <wpi/StringMap.h>
 #include <wpi/fs.h>
 #include <wpi/json.h>
-#include <wpi/raw_istream.h>
 #include <wpigui.h>
 
 #include "glass/Context.h"
@@ -237,10 +238,12 @@
  private:
   void Reset();
   bool LoadImageImpl(const std::string& fn);
-  void LoadJson(std::string_view jsonfile);
+  bool LoadJson(std::span<const char> is, std::string_view filename);
+  void LoadJsonFile(std::string_view jsonfile);
 
   std::unique_ptr<pfd::open_file> m_fileOpener;
 
+  std::string& m_builtin;
   std::string& m_filename;
   gui::Texture m_texture;
 
@@ -340,7 +343,8 @@
 }
 
 FieldInfo::FieldInfo(Storage& storage)
-    : m_filename{storage.GetString("image")},
+    : m_builtin{storage.GetString("builtin")},
+      m_filename{storage.GetString("image")},
       m_width{storage.GetFloat("width", kDefaultWidth.to<float>())},
       m_height{storage.GetFloat("height", kDefaultHeight.to<float>())},
       m_top{storage.GetInt("top", 0)},
@@ -349,7 +353,25 @@
       m_right{storage.GetInt("right", -1)} {}
 
 void FieldInfo::DisplaySettings() {
-  if (ImGui::Button("Choose image...")) {
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10);
+  if (ImGui::BeginCombo("Image",
+                        m_builtin.empty() ? "Custom" : m_builtin.c_str())) {
+    if (ImGui::Selectable("Custom", m_builtin.empty())) {
+      Reset();
+    }
+    for (auto&& field : fields::GetFields()) {
+      bool selected = field.name == m_builtin;
+      if (ImGui::Selectable(field.name, selected)) {
+        Reset();
+        m_builtin = field.name;
+      }
+      if (selected) {
+        ImGui::SetItemDefaultFocus();
+      }
+    }
+    ImGui::EndCombo();
+  }
+  if (m_builtin.empty() && ImGui::Button("Load image...")) {
     m_fileOpener = std::make_unique<pfd::open_file>(
         "Choose field image", "",
         std::vector<std::string>{"Image File",
@@ -370,6 +392,7 @@
 
 void FieldInfo::Reset() {
   m_texture = gui::Texture{};
+  m_builtin.clear();
   m_filename.clear();
   m_imageWidth = 0;
   m_imageHeight = 0;
@@ -384,7 +407,7 @@
     auto result = m_fileOpener->result();
     if (!result.empty()) {
       if (wpi::ends_with(result[0], ".json")) {
-        LoadJson(result[0]);
+        LoadJsonFile(result[0]);
       } else {
         LoadImageImpl(result[0].c_str());
         m_top = 0;
@@ -395,33 +418,46 @@
     }
     m_fileOpener.reset();
   }
-  if (!m_texture && !m_filename.empty()) {
-    if (!LoadImageImpl(m_filename)) {
-      m_filename.clear();
+  if (!m_texture) {
+    if (!m_builtin.empty()) {
+      for (auto&& field : fields::GetFields()) {
+        if (field.name == m_builtin) {
+          auto jsonstr = field.getJson();
+          auto imagedata = field.getImage();
+          auto texture = gui::Texture::CreateFromImage(
+              reinterpret_cast<const unsigned char*>(imagedata.data()),
+              imagedata.size());
+          if (texture && LoadJson({jsonstr.data(), jsonstr.size()}, {})) {
+            m_texture = std::move(texture);
+            m_imageWidth = m_texture.GetWidth();
+            m_imageHeight = m_texture.GetHeight();
+          } else {
+            m_builtin.clear();
+          }
+        }
+      }
+    } else if (!m_filename.empty()) {
+      if (!LoadImageImpl(m_filename)) {
+        m_filename.clear();
+      }
     }
   }
 }
 
-void FieldInfo::LoadJson(std::string_view jsonfile) {
-  std::error_code ec;
-  wpi::raw_fd_istream f(jsonfile, ec);
-  if (ec) {
-    std::fputs("GUI: could not open field JSON file\n", stderr);
-    return;
-  }
-
+bool FieldInfo::LoadJson(std::span<const char> is, std::string_view filename) {
   // parse file
   wpi::json j;
   try {
-    j = wpi::json::parse(f);
+    j = wpi::json::parse(is);
   } catch (const wpi::json::parse_error& e) {
     fmt::print(stderr, "GUI: JSON: could not parse: {}\n", e.what());
+    return false;
   }
 
   // top level must be an object
   if (!j.is_object()) {
     std::fputs("GUI: JSON: does not contain a top object\n", stderr);
-    return;
+    return false;
   }
 
   // image filename
@@ -430,7 +466,7 @@
     image = j.at("field-image").get<std::string>();
   } catch (const wpi::json::exception& e) {
     fmt::print(stderr, "GUI: JSON: could not read field-image: {}\n", e.what());
-    return;
+    return false;
   }
 
   // corners
@@ -443,7 +479,7 @@
   } catch (const wpi::json::exception& e) {
     fmt::print(stderr, "GUI: JSON: could not read field-corners: {}\n",
                e.what());
-    return;
+    return false;
   }
 
   // size
@@ -454,7 +490,7 @@
     height = j.at("field-size").at(1).get<float>();
   } catch (const wpi::json::exception& e) {
     fmt::print(stderr, "GUI: JSON: could not read field-size: {}\n", e.what());
-    return;
+    return false;
   }
 
   // units for size
@@ -463,7 +499,7 @@
     unit = j.at("field-unit").get<std::string>();
   } catch (const wpi::json::exception& e) {
     fmt::print(stderr, "GUI: JSON: could not read field-unit: {}\n", e.what());
-    return;
+    return false;
   }
 
   // convert size units to meters
@@ -472,22 +508,38 @@
     height = units::convert<units::feet, units::meters>(height);
   }
 
-  // the image filename is relative to the json file
-  auto pathname = fs::path{jsonfile}.replace_filename(image).string();
+  if (!filename.empty()) {
+    // the image filename is relative to the json file
+    auto pathname = fs::path{filename}.replace_filename(image).string();
 
-  // load field image
-  if (!LoadImageImpl(pathname.c_str())) {
-    return;
+    // load field image
+    if (!LoadImageImpl(pathname.c_str())) {
+      return false;
+    }
+    m_filename = pathname;
   }
 
   // save to field info
-  m_filename = pathname;
   m_top = top;
   m_left = left;
   m_bottom = bottom;
   m_right = right;
   m_width = width;
   m_height = height;
+  return true;
+}
+
+void FieldInfo::LoadJsonFile(std::string_view jsonfile) {
+  std::error_code ec;
+  std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+      wpi::MemoryBuffer::GetFile(jsonfile, ec);
+  if (fileBuffer == nullptr || ec) {
+    std::fputs("GUI: could not open field JSON file\n", stderr);
+    return;
+  }
+  LoadJson(
+      {reinterpret_cast<const char*>(fileBuffer->begin()), fileBuffer->size()},
+      jsonfile);
 }
 
 bool FieldInfo::LoadImageImpl(const std::string& fn) {
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/other/Plot.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/other/Plot.cpp
index 13d7c96..0b61709 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/other/Plot.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/other/Plot.cpp
@@ -8,7 +8,6 @@
 
 #include <algorithm>
 #include <atomic>
-#include <cstdio>
 #include <cstring>
 #include <memory>
 #include <string>
@@ -16,6 +15,7 @@
 #include <vector>
 
 #include <fmt/format.h>
+#include <wpi/StringExtras.h>
 
 #if defined(__GNUC__)
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
@@ -135,6 +135,8 @@
     }
   }
 
+  void SetColor(const ImVec4& color) { m_backgroundColor.SetColor(color); }
+
  private:
   void EmitSettingsLimits(int axis);
   void DragDropAccept(PlotView& view, size_t i, int yAxis);
@@ -143,6 +145,9 @@
 
   std::string& m_name;
   bool& m_visible;
+  static constexpr float kDefaultBackgroundColor[4] = {0.0, 0.0, 0.0,
+                                                       IMPLOT_AUTO};
+  ColorSetting m_backgroundColor;
   bool& m_showPause;
   bool& m_lockPrevX;
   bool& m_legend;
@@ -316,8 +321,8 @@
   CheckSource();
 
   char label[128];
-  std::snprintf(label, sizeof(label), "%s###name%d_%d", GetName(),
-                static_cast<int>(i), static_cast<int>(plotIndex));
+  wpi::format_to_n_c_str(label, sizeof(label), "{}###name{}_{}", GetName(),
+                         static_cast<int>(i), static_cast<int>(plotIndex));
 
   int size = m_size;
   int offset = m_offset;
@@ -484,6 +489,8 @@
     : m_seriesStorage{storage.GetChildArray("series")},
       m_name{storage.GetString("name")},
       m_visible{storage.GetBool("visible", true)},
+      m_backgroundColor{
+          storage.GetFloatArray("backgroundColor", kDefaultBackgroundColor)},
       m_showPause{storage.GetBool("showPause", true)},
       m_lockPrevX{storage.GetBool("lockPrevX", false)},
       m_legend{storage.GetBool("legend", true)},
@@ -573,13 +580,19 @@
   }
 
   char label[128];
-  std::snprintf(label, sizeof(label), "%s###plot%d", m_name.c_str(),
-                static_cast<int>(i));
+  wpi::format_to_n_c_str(label, sizeof(label), "{}###plot{}", m_name,
+                         static_cast<int>(i));
+
   ImPlotFlags plotFlags = (m_legend ? 0 : ImPlotFlags_NoLegend) |
                           (m_crosshairs ? ImPlotFlags_Crosshairs : 0) |
                           (m_mousePosition ? 0 : ImPlotFlags_NoMouseText);
 
   if (ImPlot::BeginPlot(label, ImVec2(-1, m_height), plotFlags)) {
+    if (m_backgroundColor.GetColorFloat()[3] == IMPLOT_AUTO) {
+      SetColor(ImGui::GetStyleColorVec4(ImGuiCol_WindowBg));
+    }
+    ImPlot::PushStyleColor(ImPlotCol_PlotBg, m_backgroundColor.GetColor());
+
     // setup legend
     if (m_legend) {
       ImPlotLegendFlags legendFlags =
@@ -656,6 +669,8 @@
     m_xaxisRange = ImPlot::GetPlotLimits().X;
 
     ImPlotPlot* plot = ImPlot::GetCurrentPlot();
+
+    ImPlot::PopStyleColor();
     ImPlot::EndPlot();
 
     // copy plot settings back to storage
@@ -715,6 +730,12 @@
   ImGui::Text("Edit plot name:");
   ImGui::InputText("##editname", &m_name);
   ImGui::Checkbox("Visible", &m_visible);
+  m_backgroundColor.ColorEdit3("Background color",
+                               ImGuiColorEditFlags_NoInputs);
+  ImGui::SameLine();
+  if (ImGui::Button("Default")) {
+    SetColor(ImGui::GetStyleColorVec4(ImGuiCol_WindowBg));
+  }
   ImGui::Checkbox("Show Pause Button", &m_showPause);
   if (i != 0) {
     ImGui::Checkbox("Lock X-axis to previous plot", &m_lockPrevX);
@@ -917,14 +938,15 @@
 
     char name[64];
     if (!plot->GetName().empty()) {
-      std::snprintf(name, sizeof(name), "%s", plot->GetName().c_str());
+      wpi::format_to_n_c_str(name, sizeof(name), "{}", plot->GetName().c_str());
     } else {
-      std::snprintf(name, sizeof(name), "Plot %d", static_cast<int>(i));
+      wpi::format_to_n_c_str(name, sizeof(name), "Plot {}",
+                             static_cast<int>(i));
     }
 
     char label[90];
-    std::snprintf(label, sizeof(label), "%s###header%d", name,
-                  static_cast<int>(i));
+    wpi::format_to_n_c_str(label, sizeof(label), "{}###header{}", name,
+                           static_cast<int>(i));
 
     bool open = ImGui::CollapsingHeader(label);
 
@@ -993,7 +1015,8 @@
     char id[32];
     size_t numWindows = m_windows.size();
     for (size_t i = 0; i <= numWindows; ++i) {
-      std::snprintf(id, sizeof(id), "Plot <%d>", static_cast<int>(i));
+      wpi::format_to_n_c_str(id, sizeof(id), "Plot <{}>", static_cast<int>(i));
+
       bool match = false;
       for (size_t j = 0; j < numWindows; ++j) {
         if (m_windows[j]->GetId() == id) {
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/support/ExtraGuiWidgets.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/support/ExtraGuiWidgets.cpp
index 191634e..dede4a0 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/support/ExtraGuiWidgets.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/support/ExtraGuiWidgets.cpp
@@ -4,9 +4,8 @@
 
 #include "glass/support/ExtraGuiWidgets.h"
 
-#include <imgui.h>
-
 #define IMGUI_DEFINE_MATH_OPERATORS
+#include <imgui.h>
 #include <imgui_internal.h>
 
 #include "glass/DataSource.h"
diff --git a/third_party/allwpilib/glass/src/lib/native/cpp/support/NameSetting.cpp b/third_party/allwpilib/glass/src/lib/native/cpp/support/NameSetting.cpp
index 1dc1d20..cfc7ab3 100644
--- a/third_party/allwpilib/glass/src/lib/native/cpp/support/NameSetting.cpp
+++ b/third_party/allwpilib/glass/src/lib/native/cpp/support/NameSetting.cpp
@@ -4,9 +4,6 @@
 
 #include "glass/support/NameSetting.h"
 
-#include <cstdio>
-#include <cstring>
-
 #include <imgui_internal.h>
 #include <imgui_stdlib.h>
 #include <wpi/StringExtras.h>
@@ -16,75 +13,80 @@
 void NameSetting::GetName(char* buf, size_t size,
                           const char* defaultName) const {
   if (!m_name.empty()) {
-    std::snprintf(buf, size, "%s", m_name.c_str());
+    wpi::format_to_n_c_str(buf, size, "{}", m_name);
   } else {
-    std::snprintf(buf, size, "%s", defaultName);
+    wpi::format_to_n_c_str(buf, size, "{}", defaultName);
   }
 }
 
 void NameSetting::GetName(char* buf, size_t size, const char* defaultName,
                           int index) const {
   if (!m_name.empty()) {
-    std::snprintf(buf, size, "%s [%d]", m_name.c_str(), index);
+    wpi::format_to_n_c_str(buf, size, "{} [{}]", m_name, index);
   } else {
-    std::snprintf(buf, size, "%s[%d]", defaultName, index);
+    wpi::format_to_n_c_str(buf, size, "{}[{}]", defaultName, index);
   }
 }
 
 void NameSetting::GetName(char* buf, size_t size, const char* defaultName,
                           int index, int index2) const {
   if (!m_name.empty()) {
-    std::snprintf(buf, size, "%s [%d,%d]", m_name.c_str(), index, index2);
+    wpi::format_to_n_c_str(buf, size, "{} [{},{}]", m_name, index, index2);
   } else {
-    std::snprintf(buf, size, "%s[%d,%d]", defaultName, index, index2);
+    wpi::format_to_n_c_str(buf, size, "{}[{},{}]", defaultName, index, index2);
   }
 }
 
 void NameSetting::GetLabel(char* buf, size_t size,
                            const char* defaultName) const {
   if (!m_name.empty()) {
-    std::snprintf(buf, size, "%s###Name%s", m_name.c_str(), defaultName);
+    wpi::format_to_n_c_str(buf, size, "{}###Name{}", m_name, defaultName);
   } else {
-    std::snprintf(buf, size, "%s###Name%s", defaultName, defaultName);
+    wpi::format_to_n_c_str(buf, size, "{}###Name{}", defaultName, defaultName);
   }
 }
 
 void NameSetting::GetLabel(char* buf, size_t size, const char* defaultName,
                            int index) const {
   if (!m_name.empty()) {
-    std::snprintf(buf, size, "%s [%d]###Name%d", m_name.c_str(), index, index);
+    wpi::format_to_n_c_str(buf, size, "{} [{}]###Name{}", m_name, index, index);
   } else {
-    std::snprintf(buf, size, "%s[%d]###Name%d", defaultName, index, index);
+    wpi::format_to_n_c_str(buf, size, "{}[{}]###Name{}", defaultName, index,
+                           index);
   }
 }
 
 void NameSetting::GetLabel(char* buf, size_t size, const char* defaultName,
                            int index, int index2) const {
   if (!m_name.empty()) {
-    std::snprintf(buf, size, "%s [%d,%d]###Name%d", m_name.c_str(), index,
-                  index2, index);
+    wpi::format_to_n_c_str(buf, size, "{} [{},{}]###Name{}", m_name, index,
+                           index2, index);
   } else {
-    std::snprintf(buf, size, "%s[%d,%d]###Name%d", defaultName, index, index2,
-                  index);
+    wpi::format_to_n_c_str(buf, size, "{}[{},{}]###Name{}", defaultName, index,
+                           index2, index);
   }
 }
 
 void NameSetting::PushEditNameId(int index) {
   char id[64];
-  std::snprintf(id, sizeof(id), "Name%d", index);
+  wpi::format_to_n_c_str(id, sizeof(id), "Name{}", index);
+
   ImGui::PushID(id);
 }
 
 void NameSetting::PushEditNameId(const char* name) {
   char id[128];
-  std::snprintf(id, sizeof(id), "Name%s", name);
+  wpi::format_to_n_c_str(id, sizeof(id), "Name{}", name);
+
   ImGui::PushID(id);
 }
 
 bool NameSetting::PopupEditName(int index) {
   bool rv = false;
+
   char id[64];
-  std::snprintf(id, sizeof(id), "Name%d", index);
+  wpi::format_to_n_c_str(id, sizeof(id), "Name{}", index);
+
   if (ImGui::BeginPopupContextItem(id)) {
     ImGui::Text("Edit name:");
     if (InputTextName("##edit")) {
@@ -101,8 +103,10 @@
 
 bool NameSetting::PopupEditName(const char* name) {
   bool rv = false;
+
   char id[128];
-  std::snprintf(id, sizeof(id), "Name%s", name);
+  wpi::format_to_n_c_str(id, sizeof(id), "Name{}", name);
+
   if (ImGui::BeginPopupContextItem(id)) {
     ImGui::Text("Edit name:");
     if (InputTextName("##edit")) {
diff --git a/third_party/allwpilib/glass/src/lib/native/include/glass/Context.h b/third_party/allwpilib/glass/src/lib/native/include/glass/Context.h
index e8dada3..f343d33 100644
--- a/third_party/allwpilib/glass/src/lib/native/include/glass/Context.h
+++ b/third_party/allwpilib/glass/src/lib/native/include/glass/Context.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <stdint.h>
+
 #include <functional>
 #include <string>
 #include <string_view>
diff --git a/third_party/allwpilib/glass/src/lib/native/include/glass/Storage.h b/third_party/allwpilib/glass/src/lib/native/include/glass/Storage.h
index 7ebfa6d..bdb2b3d 100644
--- a/third_party/allwpilib/glass/src/lib/native/include/glass/Storage.h
+++ b/third_party/allwpilib/glass/src/lib/native/include/glass/Storage.h
@@ -16,10 +16,7 @@
 
 #include <wpi/StringMap.h>
 #include <wpi/iterator_range.h>
-
-namespace wpi {
-class json;
-}  // namespace wpi
+#include <wpi/json_fwd.h>
 
 namespace glass {
 
diff --git a/third_party/allwpilib/glass/src/lib/native/include/glass/Window.h b/third_party/allwpilib/glass/src/lib/native/include/glass/Window.h
index 0a37f9a..62b369c 100644
--- a/third_party/allwpilib/glass/src/lib/native/include/glass/Window.h
+++ b/third_party/allwpilib/glass/src/lib/native/include/glass/Window.h
@@ -9,6 +9,7 @@
 #include <string_view>
 #include <utility>
 
+#define IMGUI_DEFINE_MATH_OPERATORS
 #include <imgui.h>
 
 #include "glass/View.h"
diff --git a/third_party/allwpilib/glass/src/lib/native/include/glass/other/Field2D.h b/third_party/allwpilib/glass/src/lib/native/include/glass/other/Field2D.h
index 9c9f72a..2b0f9a8 100644
--- a/third_party/allwpilib/glass/src/lib/native/include/glass/other/Field2D.h
+++ b/third_party/allwpilib/glass/src/lib/native/include/glass/other/Field2D.h
@@ -10,6 +10,8 @@
 #include <frc/geometry/Pose2d.h>
 #include <frc/geometry/Rotation2d.h>
 #include <frc/geometry/Translation2d.h>
+
+#define IMGUI_DEFINE_MATH_OPERATORS
 #include <imgui.h>
 #include <wpi/function_ref.h>
 
diff --git a/third_party/allwpilib/glass/src/lib/native/include/glass/other/Mechanism2D.h b/third_party/allwpilib/glass/src/lib/native/include/glass/other/Mechanism2D.h
index ab5ccdc..440fed3 100644
--- a/third_party/allwpilib/glass/src/lib/native/include/glass/other/Mechanism2D.h
+++ b/third_party/allwpilib/glass/src/lib/native/include/glass/other/Mechanism2D.h
@@ -6,6 +6,8 @@
 
 #include <frc/geometry/Rotation2d.h>
 #include <frc/geometry/Translation2d.h>
+
+#define IMGUI_DEFINE_MATH_OPERATORS
 #include <imgui.h>
 #include <wpi/function_ref.h>
 
diff --git a/third_party/allwpilib/glass/src/lib/native/include/glass/support/ExtraGuiWidgets.h b/third_party/allwpilib/glass/src/lib/native/include/glass/support/ExtraGuiWidgets.h
index 6788434..d56f342 100644
--- a/third_party/allwpilib/glass/src/lib/native/include/glass/support/ExtraGuiWidgets.h
+++ b/third_party/allwpilib/glass/src/lib/native/include/glass/support/ExtraGuiWidgets.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#define IMGUI_DEFINE_MATH_OPERATORS
 #include <imgui.h>
 
 namespace glass {
diff --git a/third_party/allwpilib/glass/src/libnt/native/cpp/NetworkTables.cpp b/third_party/allwpilib/glass/src/libnt/native/cpp/NetworkTables.cpp
index d368359..57469a2 100644
--- a/third_party/allwpilib/glass/src/libnt/native/cpp/NetworkTables.cpp
+++ b/third_party/allwpilib/glass/src/libnt/native/cpp/NetworkTables.cpp
@@ -5,7 +5,7 @@
 #include "glass/networktables/NetworkTables.h"
 
 #include <cinttypes>
-#include <cstdio>
+#include <concepts>
 #include <cstring>
 #include <initializer_list>
 #include <memory>
@@ -14,7 +14,10 @@
 #include <vector>
 
 #include <fmt/format.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
 #include <imgui.h>
+#include <imgui_stdlib.h>
 #include <networktables/NetworkTableInstance.h>
 #include <networktables/NetworkTableValue.h>
 #include <ntcore_c.h>
@@ -58,36 +61,6 @@
   }
 }
 
-static std::string BooleanArrayToString(std::span<const int> in) {
-  std::string rv;
-  wpi::raw_string_ostream os{rv};
-  os << '[';
-  bool first = true;
-  for (auto v : in) {
-    if (!first) {
-      os << ',';
-    }
-    first = false;
-    if (v) {
-      os << "true";
-    } else {
-      os << "false";
-    }
-  }
-  os << ']';
-  return rv;
-}
-
-static std::string IntegerArrayToString(std::span<const int64_t> in) {
-  return fmt::format("[{:d}]", fmt::join(in, ","));
-}
-
-template <typename T>
-static std::string FloatArrayToString(std::span<const T> in) {
-  static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>);
-  return fmt::format("[{:.6f}]", fmt::join(in, ","));
-}
-
 static std::string StringArrayToString(std::span<const std::string> in) {
   std::string rv;
   wpi::raw_string_ostream os{rv};
@@ -143,36 +116,37 @@
   }
 }
 
-static void UpdateMsgpackValueSource(NetworkTablesModel::ValueSource* out,
+static void UpdateMsgpackValueSource(NetworkTablesModel& model,
+                                     NetworkTablesModel::ValueSource* out,
                                      mpack_reader_t& r, std::string_view name,
                                      int64_t time) {
   mpack_tag_t tag = mpack_read_tag(&r);
   switch (mpack_tag_type(&tag)) {
     case mpack::mpack_type_bool:
-      out->UpdateFromValue(
-          nt::Value::MakeBoolean(mpack_tag_bool_value(&tag), time), name, "");
+      out->value = nt::Value::MakeBoolean(mpack_tag_bool_value(&tag), time);
+      out->UpdateFromValue(model, name, "");
       break;
     case mpack::mpack_type_int:
-      out->UpdateFromValue(
-          nt::Value::MakeInteger(mpack_tag_int_value(&tag), time), name, "");
+      out->value = nt::Value::MakeInteger(mpack_tag_int_value(&tag), time);
+      out->UpdateFromValue(model, name, "");
       break;
     case mpack::mpack_type_uint:
-      out->UpdateFromValue(
-          nt::Value::MakeInteger(mpack_tag_uint_value(&tag), time), name, "");
+      out->value = nt::Value::MakeInteger(mpack_tag_uint_value(&tag), time);
+      out->UpdateFromValue(model, name, "");
       break;
     case mpack::mpack_type_float:
-      out->UpdateFromValue(
-          nt::Value::MakeFloat(mpack_tag_float_value(&tag), time), name, "");
+      out->value = nt::Value::MakeFloat(mpack_tag_float_value(&tag), time);
+      out->UpdateFromValue(model, name, "");
       break;
     case mpack::mpack_type_double:
-      out->UpdateFromValue(
-          nt::Value::MakeDouble(mpack_tag_double_value(&tag), time), name, "");
+      out->value = nt::Value::MakeDouble(mpack_tag_double_value(&tag), time);
+      out->UpdateFromValue(model, name, "");
       break;
     case mpack::mpack_type_str: {
       std::string str;
       mpack_read_str(&r, &tag, &str);
-      out->UpdateFromValue(nt::Value::MakeString(std::move(str), time), name,
-                           "");
+      out->value = nt::Value::MakeString(std::move(str), time);
+      out->UpdateFromValue(model, name, "");
       break;
     }
     case mpack::mpack_type_bin:
@@ -193,7 +167,8 @@
           child.path = fmt::format("{}{}", name, child.name);
         }
         ++i;
-        UpdateMsgpackValueSource(&child, r, child.path, time);  // recurse
+        UpdateMsgpackValueSource(model, &child, r, child.path,
+                                 time);  // recurse
       }
       mpack_done_array(&r);
       break;
@@ -215,7 +190,7 @@
           auto it = elems.find(key);
           if (it != elems.end()) {
             auto& child = out->valueChildren[it->second];
-            UpdateMsgpackValueSource(&child, r, child.path, time);
+            UpdateMsgpackValueSource(model, &child, r, child.path, time);
             elems.erase(it);
           } else {
             added = true;
@@ -223,7 +198,7 @@
             auto& child = out->valueChildren.back();
             child.name = std::move(key);
             child.path = fmt::format("{}/{}", name, child.name);
-            UpdateMsgpackValueSource(&child, r, child.path, time);
+            UpdateMsgpackValueSource(model, &child, r, child.path, time);
           }
         }
       }
@@ -248,7 +223,318 @@
   }
 }
 
-static void UpdateJsonValueSource(NetworkTablesModel::ValueSource* out,
+static void UpdateStructValueSource(NetworkTablesModel& model,
+                                    NetworkTablesModel::ValueSource* out,
+                                    const wpi::DynamicStruct& s,
+                                    std::string_view name, int64_t time) {
+  auto desc = s.GetDescriptor();
+  out->typeStr = "struct:" + desc->GetName();
+  auto& fields = desc->GetFields();
+  if (!out->valueChildrenMap || fields.size() != out->valueChildren.size()) {
+    out->valueChildren.clear();
+    out->valueChildrenMap = true;
+    out->valueChildren.reserve(fields.size());
+    for (auto&& field : fields) {
+      out->valueChildren.emplace_back();
+      auto& child = out->valueChildren.back();
+      child.name = field.GetName();
+      child.path = fmt::format("{}/{}", name, child.name);
+    }
+  }
+  auto outIt = out->valueChildren.begin();
+  for (auto&& field : fields) {
+    auto& child = *outIt++;
+    switch (field.GetType()) {
+      case wpi::StructFieldType::kBool:
+        if (field.IsArray()) {
+          std::vector<int> v;
+          v.reserve(field.GetArraySize());
+          for (size_t i = 0; i < field.GetArraySize(); ++i) {
+            v.emplace_back(s.GetBoolField(&field, i));
+          }
+          child.value = nt::Value::MakeBooleanArray(std::move(v), time);
+        } else {
+          child.value = nt::Value::MakeBoolean(s.GetBoolField(&field), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case wpi::StructFieldType::kChar:
+        child.value = nt::Value::MakeString(s.GetStringField(&field), time);
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case wpi::StructFieldType::kInt8:
+      case wpi::StructFieldType::kInt16:
+      case wpi::StructFieldType::kInt32:
+      case wpi::StructFieldType::kInt64:
+      case wpi::StructFieldType::kUint8:
+      case wpi::StructFieldType::kUint16:
+      case wpi::StructFieldType::kUint32:
+      case wpi::StructFieldType::kUint64: {
+        bool isUint = field.IsUint();
+        if (field.IsArray()) {
+          std::vector<int64_t> v;
+          v.reserve(field.GetArraySize());
+          for (size_t i = 0; i < field.GetArraySize(); ++i) {
+            if (isUint) {
+              v.emplace_back(s.GetUintField(&field, i));
+            } else {
+              v.emplace_back(s.GetIntField(&field, i));
+            }
+          }
+          child.value = nt::Value::MakeIntegerArray(std::move(v), time);
+        } else {
+          if (isUint) {
+            child.value = nt::Value::MakeInteger(s.GetUintField(&field), time);
+          } else {
+            child.value = nt::Value::MakeInteger(s.GetIntField(&field), time);
+          }
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      }
+      case wpi::StructFieldType::kFloat:
+        if (field.IsArray()) {
+          std::vector<float> v;
+          v.reserve(field.GetArraySize());
+          for (size_t i = 0; i < field.GetArraySize(); ++i) {
+            v.emplace_back(s.GetFloatField(&field, i));
+          }
+          child.value = nt::Value::MakeFloatArray(std::move(v), time);
+        } else {
+          child.value = nt::Value::MakeFloat(s.GetFloatField(&field), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case wpi::StructFieldType::kDouble:
+        if (field.IsArray()) {
+          std::vector<double> v;
+          v.reserve(field.GetArraySize());
+          for (size_t i = 0; i < field.GetArraySize(); ++i) {
+            v.emplace_back(s.GetDoubleField(&field, i));
+          }
+          child.value = nt::Value::MakeDoubleArray(std::move(v), time);
+        } else {
+          child.value = nt::Value::MakeDouble(s.GetDoubleField(&field), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case wpi::StructFieldType::kStruct:
+        if (field.IsArray()) {
+          if (child.valueChildrenMap) {
+            child.valueChildren.clear();
+            child.valueChildrenMap = false;
+          }
+          child.valueChildren.resize(field.GetArraySize());
+          unsigned int i = 0;
+          for (auto&& child2 : child.valueChildren) {
+            if (child2.name.empty()) {
+              child2.name = fmt::format("[{}]", i);
+              child2.path = fmt::format("{}{}", name, child.name);
+            }
+            UpdateStructValueSource(model, &child2, s.GetStructField(&field, i),
+                                    child2.path, time);  // recurse
+            ++i;
+          }
+        } else {
+          UpdateStructValueSource(model, &child, s.GetStructField(&field),
+                                  child.path, time);  // recurse
+        }
+        break;
+    }
+  }
+}
+
+static void UpdateProtobufValueSource(NetworkTablesModel& model,
+                                      NetworkTablesModel::ValueSource* out,
+                                      const google::protobuf::Message& msg,
+                                      std::string_view name, int64_t time) {
+  auto desc = msg.GetDescriptor();
+  out->typeStr = "proto:" + desc->full_name();
+  if (!out->valueChildrenMap ||
+      desc->field_count() != static_cast<int>(out->valueChildren.size())) {
+    out->valueChildren.clear();
+    out->valueChildrenMap = true;
+    out->valueChildren.reserve(desc->field_count());
+    for (int i = 0, end = desc->field_count(); i < end; ++i) {
+      out->valueChildren.emplace_back();
+      auto& child = out->valueChildren.back();
+      child.name = desc->field(i)->name();
+      child.path = fmt::format("{}/{}", name, child.name);
+    }
+  }
+  auto refl = msg.GetReflection();
+  auto outIt = out->valueChildren.begin();
+  for (int fieldNum = 0, end = desc->field_count(); fieldNum < end;
+       ++fieldNum) {
+    auto field = desc->field(fieldNum);
+    auto& child = *outIt++;
+    switch (field->cpp_type()) {
+      case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
+        if (field->is_repeated()) {
+          size_t size = refl->FieldSize(msg, field);
+          std::vector<int> v;
+          v.reserve(size);
+          for (size_t i = 0; i < size; ++i) {
+            v.emplace_back(refl->GetRepeatedBool(msg, field, i));
+          }
+          child.value = nt::Value::MakeBooleanArray(std::move(v), time);
+        } else {
+          child.value = nt::Value::MakeBoolean(refl->GetBool(msg, field), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
+        if (field->is_repeated()) {
+          size_t size = refl->FieldSize(msg, field);
+          std::vector<std::string> v;
+          v.reserve(size);
+          for (size_t i = 0; i < size; ++i) {
+            v.emplace_back(refl->GetRepeatedString(msg, field, i));
+          }
+          child.value = nt::Value::MakeStringArray(std::move(v), time);
+        } else {
+          child.value =
+              nt::Value::MakeString(refl->GetString(msg, field), time);
+          child.UpdateFromValue(model, child.path, "");
+        }
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
+        if (field->is_repeated()) {
+          size_t size = refl->FieldSize(msg, field);
+          std::vector<int64_t> v;
+          v.reserve(size);
+          for (size_t i = 0; i < size; ++i) {
+            v.emplace_back(refl->GetRepeatedInt32(msg, field, i));
+          }
+          child.value = nt::Value::MakeIntegerArray(std::move(v), time);
+        } else {
+          child.value =
+              nt::Value::MakeInteger(refl->GetInt32(msg, field), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
+        if (field->is_repeated()) {
+          size_t size = refl->FieldSize(msg, field);
+          std::vector<int64_t> v;
+          v.reserve(size);
+          for (size_t i = 0; i < size; ++i) {
+            v.emplace_back(refl->GetRepeatedInt64(msg, field, i));
+          }
+          child.value = nt::Value::MakeIntegerArray(std::move(v), time);
+        } else {
+          child.value =
+              nt::Value::MakeInteger(refl->GetInt64(msg, field), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
+        if (field->is_repeated()) {
+          size_t size = refl->FieldSize(msg, field);
+          std::vector<int64_t> v;
+          v.reserve(size);
+          for (size_t i = 0; i < size; ++i) {
+            v.emplace_back(refl->GetRepeatedUInt32(msg, field, i));
+          }
+          child.value = nt::Value::MakeIntegerArray(std::move(v), time);
+        } else {
+          child.value =
+              nt::Value::MakeInteger(refl->GetUInt32(msg, field), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
+        if (field->is_repeated()) {
+          size_t size = refl->FieldSize(msg, field);
+          std::vector<int64_t> v;
+          v.reserve(size);
+          for (size_t i = 0; i < size; ++i) {
+            v.emplace_back(refl->GetRepeatedUInt64(msg, field, i));
+          }
+          child.value = nt::Value::MakeIntegerArray(std::move(v), time);
+        } else {
+          child.value =
+              nt::Value::MakeInteger(refl->GetUInt64(msg, field), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
+        if (field->is_repeated()) {
+          size_t size = refl->FieldSize(msg, field);
+          std::vector<float> v;
+          v.reserve(size);
+          for (size_t i = 0; i < size; ++i) {
+            v.emplace_back(refl->GetRepeatedFloat(msg, field, i));
+          }
+          child.value = nt::Value::MakeFloatArray(std::move(v), time);
+        } else {
+          child.value = nt::Value::MakeFloat(refl->GetFloat(msg, field), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
+        if (field->is_repeated()) {
+          size_t size = refl->FieldSize(msg, field);
+          std::vector<double> v;
+          v.reserve(size);
+          for (size_t i = 0; i < size; ++i) {
+            v.emplace_back(refl->GetRepeatedDouble(msg, field, i));
+          }
+          child.value = nt::Value::MakeDoubleArray(std::move(v), time);
+        } else {
+          child.value =
+              nt::Value::MakeDouble(refl->GetDouble(msg, field), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
+        if (field->is_repeated()) {
+          size_t size = refl->FieldSize(msg, field);
+          std::vector<std::string> v;
+          v.reserve(size);
+          for (size_t i = 0; i < size; ++i) {
+            v.emplace_back(refl->GetRepeatedEnum(msg, field, i)->name());
+          }
+          child.value = nt::Value::MakeStringArray(std::move(v), time);
+        } else {
+          child.value =
+              nt::Value::MakeString(refl->GetEnum(msg, field)->name(), time);
+        }
+        child.UpdateFromValue(model, child.path, "");
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
+        if (field->is_repeated()) {
+          if (child.valueChildrenMap) {
+            child.valueChildren.clear();
+            child.valueChildrenMap = false;
+          }
+          size_t size = refl->FieldSize(msg, field);
+          child.valueChildren.resize(size);
+          unsigned int i = 0;
+          for (auto&& child2 : child.valueChildren) {
+            if (child2.name.empty()) {
+              child2.name = fmt::format("[{}]", i);
+              child2.path = fmt::format("{}{}", name, child.name);
+            }
+            UpdateProtobufValueSource(model, &child2,
+                                      refl->GetRepeatedMessage(msg, field, i),
+                                      child2.path, time);  // recurse
+            ++i;
+          }
+        } else {
+          UpdateProtobufValueSource(
+              model, &child,
+              refl->GetMessage(msg, field,
+                               model.GetProtobufDatabase().GetMessageFactory()),
+              child.path, time);  // recurse
+        }
+        break;
+    }
+  }
+}
+
+static void UpdateJsonValueSource(NetworkTablesModel& model,
+                                  NetworkTablesModel::ValueSource* out,
                                   const wpi::json& j, std::string_view name,
                                   int64_t time) {
   switch (j.type()) {
@@ -266,7 +552,7 @@
         auto it = elems.find(kv.key());
         if (it != elems.end()) {
           auto& child = out->valueChildren[it->second];
-          UpdateJsonValueSource(&child, kv.value(), child.path, time);
+          UpdateJsonValueSource(model, &child, kv.value(), child.path, time);
           elems.erase(it);
         } else {
           added = true;
@@ -274,7 +560,7 @@
           auto& child = out->valueChildren.back();
           child.name = kv.key();
           child.path = fmt::format("{}/{}", name, child.name);
-          UpdateJsonValueSource(&child, kv.value(), child.path, time);
+          UpdateJsonValueSource(model, &child, kv.value(), child.path, time);
         }
       }
       // erase unmatched keys
@@ -302,31 +588,30 @@
           child.name = fmt::format("[{}]", i);
           child.path = fmt::format("{}{}", name, child.name);
         }
-        ++i;
-        UpdateJsonValueSource(&child, j[i], child.path, time);  // recurse
+        // recurse
+        UpdateJsonValueSource(model, &child, j[i++], child.path, time);
       }
       break;
     }
     case wpi::json::value_t::string:
-      out->UpdateFromValue(
-          nt::Value::MakeString(j.get_ref<const std::string&>(), time), name,
-          "");
+      out->value = nt::Value::MakeString(j.get_ref<const std::string&>(), time);
+      out->UpdateFromValue(model, name, "");
       break;
     case wpi::json::value_t::boolean:
-      out->UpdateFromValue(nt::Value::MakeBoolean(j.get<bool>(), time), name,
-                           "");
+      out->value = nt::Value::MakeBoolean(j.get<bool>(), time);
+      out->UpdateFromValue(model, name, "");
       break;
     case wpi::json::value_t::number_integer:
-      out->UpdateFromValue(nt::Value::MakeInteger(j.get<int64_t>(), time), name,
-                           "");
+      out->value = nt::Value::MakeInteger(j.get<int64_t>(), time);
+      out->UpdateFromValue(model, name, "");
       break;
     case wpi::json::value_t::number_unsigned:
-      out->UpdateFromValue(nt::Value::MakeInteger(j.get<uint64_t>(), time),
-                           name, "");
+      out->value = nt::Value::MakeInteger(j.get<uint64_t>(), time);
+      out->UpdateFromValue(model, name, "");
       break;
     case wpi::json::value_t::number_float:
-      out->UpdateFromValue(nt::Value::MakeDouble(j.get<double>(), time), name,
-                           "");
+      out->value = nt::Value::MakeDouble(j.get<double>(), time);
+      out->UpdateFromValue(model, name, "");
       break;
     default:
       out->value = {};
@@ -334,81 +619,161 @@
   }
 }
 
+void NetworkTablesModel::ValueSource::UpdateDiscreteSource(
+    std::string_view name, double value, int64_t time, bool digital) {
+  valueChildren.clear();
+  if (!source) {
+    source = std::make_unique<DataSource>(fmt::format("NT:{}", name));
+  }
+  source->SetValue(value, time);
+  source->SetDigital(digital);
+}
+
+template <typename T, typename MakeValue>
+void NetworkTablesModel::ValueSource::UpdateDiscreteArray(
+    std::string_view name, std::span<const T> arr, int64_t time,
+    MakeValue makeValue, bool digital) {
+  if (valueChildrenMap) {
+    valueChildren.clear();
+    valueChildrenMap = false;
+  }
+  valueChildren.resize(arr.size());
+  unsigned int i = 0;
+  for (auto&& child : valueChildren) {
+    if (child.name.empty()) {
+      child.name = fmt::format("[{}]", i);
+      child.path = fmt::format("{}{}", name, child.name);
+    }
+    child.value = makeValue(arr[i], time);
+    child.UpdateDiscreteSource(child.path, arr[i], time, digital);
+    ++i;
+  }
+}
+
 void NetworkTablesModel::ValueSource::UpdateFromValue(
-    nt::Value&& v, std::string_view name, std::string_view typeStr) {
-  value = v;
+    NetworkTablesModel& model, std::string_view name,
+    std::string_view typeStr) {
   switch (value.type()) {
     case NT_BOOLEAN:
-      valueChildren.clear();
-      if (!source) {
-        source = std::make_unique<DataSource>(fmt::format("NT:{}", name));
-      }
-      source->SetValue(value.GetBoolean() ? 1 : 0, value.last_change());
-      source->SetDigital(true);
+      UpdateDiscreteSource(name, value.GetBoolean() ? 1 : 0, value.time(),
+                           true);
       break;
     case NT_INTEGER:
-      valueChildren.clear();
-      if (!source) {
-        source = std::make_unique<DataSource>(fmt::format("NT:{}", name));
-      }
-      source->SetValue(value.GetInteger(), value.last_change());
-      source->SetDigital(false);
+      UpdateDiscreteSource(name, value.GetInteger(), value.time());
       break;
     case NT_FLOAT:
-      valueChildren.clear();
-      if (!source) {
-        source = std::make_unique<DataSource>(fmt::format("NT:{}", name));
-      }
-      source->SetValue(value.GetFloat(), value.last_change());
-      source->SetDigital(false);
+      UpdateDiscreteSource(name, value.GetFloat(), value.time());
       break;
     case NT_DOUBLE:
-      valueChildren.clear();
-      if (!source) {
-        source = std::make_unique<DataSource>(fmt::format("NT:{}", name));
-      }
-      source->SetValue(value.GetDouble(), value.last_change());
-      source->SetDigital(false);
+      UpdateDiscreteSource(name, value.GetDouble(), value.time());
       break;
     case NT_BOOLEAN_ARRAY:
-      valueChildren.clear();
-      valueStr = BooleanArrayToString(value.GetBooleanArray());
+      UpdateDiscreteArray(name, value.GetBooleanArray(), value.time(),
+                          nt::Value::MakeBoolean, true);
       break;
     case NT_INTEGER_ARRAY:
-      valueChildren.clear();
-      valueStr = IntegerArrayToString(value.GetIntegerArray());
+      UpdateDiscreteArray(name, value.GetIntegerArray(), value.time(),
+                          nt::Value::MakeInteger);
       break;
     case NT_FLOAT_ARRAY:
-      valueChildren.clear();
-      valueStr = FloatArrayToString(value.GetFloatArray());
+      UpdateDiscreteArray(name, value.GetFloatArray(), value.time(),
+                          nt::Value::MakeFloat);
       break;
     case NT_DOUBLE_ARRAY:
-      valueChildren.clear();
-      valueStr = FloatArrayToString(value.GetDoubleArray());
+      UpdateDiscreteArray(name, value.GetDoubleArray(), value.time(),
+                          nt::Value::MakeDouble);
       break;
-    case NT_STRING_ARRAY:
-      valueChildren.clear();
-      valueStr = StringArrayToString(value.GetStringArray());
+    case NT_STRING_ARRAY: {
+      auto arr = value.GetStringArray();
+      if (valueChildrenMap) {
+        valueChildren.clear();
+        valueChildrenMap = false;
+      }
+      valueChildren.resize(arr.size());
+      unsigned int i = 0;
+      for (auto&& child : valueChildren) {
+        if (child.name.empty()) {
+          child.name = fmt::format("[{}]", i);
+          child.path = fmt::format("{}{}", name, child.name);
+        }
+        child.value = nt::Value::MakeString(arr[i++], value.time());
+        child.UpdateFromValue(model, child.path, "");
+      }
       break;
+    }
     case NT_STRING:
       if (typeStr == "json") {
         try {
-          UpdateJsonValueSource(this, wpi::json::parse(value.GetString()), name,
+          UpdateJsonValueSource(model, this,
+                                wpi::json::parse(value.GetString()), name,
                                 value.last_change());
         } catch (wpi::json::exception&) {
           // ignore
         }
       } else {
         valueChildren.clear();
+        valueStr.clear();
+        wpi::raw_string_ostream os{valueStr};
+        os << '"';
+        os.write_escaped(value.GetString());
+        os << '"';
       }
       break;
     case NT_RAW:
       if (typeStr == "msgpack") {
         mpack_reader_t r;
         mpack_reader_init_data(&r, value.GetRaw());
-        UpdateMsgpackValueSource(this, r, name, value.last_change());
-
+        UpdateMsgpackValueSource(model, this, r, name, value.last_change());
         mpack_reader_destroy(&r);
+      } else if (wpi::starts_with(typeStr, "struct:")) {
+        auto structName = wpi::drop_front(typeStr, 7);
+        bool isArray = structName.ends_with("[]");
+        if (isArray) {
+          structName = wpi::drop_back(structName, 2);
+        }
+        auto desc = model.m_structDb.Find(structName);
+        if (desc && desc->IsValid()) {
+          if (isArray) {
+            // array of struct at top level
+            if (valueChildrenMap) {
+              valueChildren.clear();
+              valueChildrenMap = false;
+            }
+            auto raw = value.GetRaw();
+            valueChildren.resize(raw.size() / desc->GetSize());
+            unsigned int i = 0;
+            for (auto&& child : valueChildren) {
+              if (child.name.empty()) {
+                child.name = fmt::format("[{}]", i);
+                child.path = fmt::format("{}{}", name, child.name);
+              }
+              wpi::DynamicStruct s{desc, raw};
+              UpdateStructValueSource(model, &child, s, child.path,
+                                      value.last_change());
+              ++i;
+              raw = wpi::drop_front(raw, desc->GetSize());
+            }
+          } else {
+            wpi::DynamicStruct s{desc, value.GetRaw()};
+            UpdateStructValueSource(model, this, s, name, value.last_change());
+          }
+        } else {
+          valueChildren.clear();
+        }
+      } else if (wpi::starts_with(typeStr, "proto:")) {
+        auto msg = model.m_protoDb.Find(wpi::drop_front(typeStr, 6));
+        if (msg) {
+          msg->Clear();
+          auto raw = value.GetRaw();
+          if (msg->ParseFromArray(raw.data(), raw.size())) {
+            UpdateProtobufValueSource(model, this, *msg, name,
+                                      value.last_change());
+          } else {
+            valueChildren.clear();
+          }
+        } else {
+          valueChildren.clear();
+        }
       } else {
         valueChildren.clear();
       }
@@ -472,8 +837,8 @@
     } else if (auto valueData = event.GetValueEventData()) {
       auto& entry = m_entries[valueData->topic];
       if (entry) {
-        entry->UpdateFromValue(std::move(valueData->value), entry->info.name,
-                               entry->info.type_str);
+        entry->value = std::move(valueData->value);
+        entry->UpdateFromValue(*this);
         if (wpi::starts_with(entry->info.name, '$') && entry->value.IsRaw() &&
             entry->info.type_str == "msgpack") {
           // meta topic handling
@@ -498,6 +863,50 @@
               it->second.UpdateSubscribers(entry->value.GetRaw());
             }
           }
+        } else if (entry->value.IsRaw() &&
+                   wpi::starts_with(entry->info.name, "/.schema/struct:") &&
+                   entry->info.type_str == "structschema") {
+          // struct schema handling
+          auto typeStr = wpi::drop_front(entry->info.name, 16);
+          std::string_view schema{
+              reinterpret_cast<const char*>(entry->value.GetRaw().data()),
+              entry->value.GetRaw().size()};
+          std::string err;
+          auto desc = m_structDb.Add(typeStr, schema, &err);
+          if (!desc) {
+            fmt::print("could not decode struct '{}' schema '{}': {}\n",
+                       entry->info.name, schema, err);
+          } else if (desc->IsValid()) {
+            // loop over all entries with this type and update
+            for (auto&& entryPair : m_entries) {
+              auto ts = entryPair.second->info.type_str;
+              if (!wpi::starts_with(ts, "struct:")) {
+                continue;
+              }
+              ts = wpi::drop_front(ts, 7);
+              if (ts == typeStr || (wpi::ends_with(ts, "[]") &&
+                                    wpi::drop_back(ts, 2) == typeStr)) {
+                entryPair.second->UpdateFromValue(*this);
+              }
+            }
+          }
+        } else if (entry->value.IsRaw() &&
+                   wpi::starts_with(entry->info.name, "/.schema/proto:") &&
+                   entry->info.type_str == "proto:FileDescriptorProto") {
+          // protobuf descriptor handling
+          auto filename = wpi::drop_front(entry->info.name, 15);
+          if (!m_protoDb.Add(filename, entry->value.GetRaw())) {
+            fmt::print("could not decode protobuf '{}' filename '{}'\n",
+                       entry->info.name, filename);
+          } else {
+            // loop over all protobuf entries and update (conservatively)
+            for (auto&& entryPair : m_entries) {
+              auto& ts = entryPair.second->info.type_str;
+              if (wpi::starts_with(ts, "proto:")) {
+                entryPair.second->UpdateFromValue(*this);
+              }
+            }
+          }
         }
       }
     }
@@ -662,153 +1071,89 @@
   m_clients = std::move(newClients);
 }
 
-static bool StringToBooleanArray(std::string_view in, std::vector<int>* out) {
-  in = wpi::trim(in);
-  if (in.empty()) {
-    return false;
-  }
-  if (in.front() == '[') {
-    in.remove_prefix(1);
-  }
-  if (in.back() == ']') {
-    in.remove_suffix(1);
-  }
-  in = wpi::trim(in);
-
-  wpi::SmallVector<std::string_view, 16> inSplit;
-
-  wpi::split(in, inSplit, ',', -1, false);
-  for (auto val : inSplit) {
-    val = wpi::trim(val);
-    if (wpi::equals_lower(val, "true")) {
-      out->emplace_back(1);
-    } else if (wpi::equals_lower(val, "false")) {
-      out->emplace_back(0);
-    } else {
-      fmt::print(stderr,
-                 "GUI: NetworkTables: Could not understand value '{}'\n", val);
-      return false;
+static bool GetHeadingTypeString(std::string_view* ts) {
+  if (wpi::starts_with(*ts, "proto:")) {
+    *ts = wpi::drop_front(*ts, 6);
+    auto lastdot = ts->rfind('.');
+    if (lastdot != std::string_view::npos) {
+      *ts = wpi::substr(*ts, lastdot + 1);
     }
+    if (wpi::starts_with(*ts, "Protobuf")) {
+      *ts = wpi::drop_front(*ts, 8);
+    }
+    return true;
+  } else if (wpi::starts_with(*ts, "struct:")) {
+    *ts = wpi::drop_front(*ts, 7);
+    return true;
   }
-
-  return true;
+  return false;
 }
 
-static bool StringToIntegerArray(std::string_view in,
-                                 std::vector<int64_t>* out) {
-  in = wpi::trim(in);
-  if (in.empty()) {
-    return false;
+static const char* GetShortTypeString(std::string_view ts) {
+  if (wpi::starts_with(ts, "proto:")) {
+    return "protobuf";
+  } else if (wpi::starts_with(ts, "struct:")) {
+    return "struct";
+  } else {
+    return ts.data();
   }
-  if (in.front() == '[') {
-    in.remove_prefix(1);
-  }
-  if (in.back() == ']') {
-    in.remove_suffix(1);
-  }
-  in = wpi::trim(in);
-
-  wpi::SmallVector<std::string_view, 16> inSplit;
-
-  wpi::split(in, inSplit, ',', -1, false);
-  for (auto val : inSplit) {
-    if (auto num = wpi::parse_integer<int64_t>(wpi::trim(val), 0)) {
-      out->emplace_back(num.value());
-    } else {
-      fmt::print(stderr,
-                 "GUI: NetworkTables: Could not understand value '{}'\n", val);
-      return false;
-    }
-  }
-
-  return true;
 }
 
-template <typename T>
-static bool StringToFloatArray(std::string_view in, std::vector<T>* out) {
-  static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>);
-  in = wpi::trim(in);
-  if (in.empty()) {
-    return false;
+static const char* GetTypeString(NT_Type type, const char* overrideTypeStr) {
+  if (overrideTypeStr) {
+    return GetShortTypeString(overrideTypeStr);
   }
-  if (in.front() == '[') {
-    in.remove_prefix(1);
+  switch (type) {
+    case NT_BOOLEAN:
+      return "boolean";
+    case NT_INTEGER:
+      return "int";
+    case NT_FLOAT:
+      return "float";
+    case NT_DOUBLE:
+      return "double";
+    case NT_STRING:
+      return "string";
+    case NT_BOOLEAN_ARRAY:
+      return "boolean[]";
+    case NT_INTEGER_ARRAY:
+      return "int[]";
+    case NT_FLOAT_ARRAY:
+      return "float[]";
+    case NT_DOUBLE_ARRAY:
+      return "double[]";
+    case NT_STRING_ARRAY:
+      return "string[]";
+    case NT_RAW:
+      return "raw";
+    case NT_RPC:
+      return "rpc";
+    default:
+      return "other";
   }
-  if (in.back() == ']') {
-    in.remove_suffix(1);
-  }
-  in = wpi::trim(in);
-
-  wpi::SmallVector<std::string_view, 16> inSplit;
-
-  wpi::split(in, inSplit, ',', -1, false);
-  for (auto val : inSplit) {
-    if (auto num = wpi::parse_float<T>(wpi::trim(val))) {
-      out->emplace_back(num.value());
-    } else {
-      fmt::print(stderr,
-                 "GUI: NetworkTables: Could not understand value '{}'\n", val);
-      return false;
-    }
-  }
-
-  return true;
-}
-
-static bool StringToStringArray(std::string_view in,
-                                std::vector<std::string>* out) {
-  in = wpi::trim(in);
-  if (in.empty()) {
-    return false;
-  }
-  if (in.front() == '[') {
-    in.remove_prefix(1);
-  }
-  if (in.back() == ']') {
-    in.remove_suffix(1);
-  }
-  in = wpi::trim(in);
-
-  wpi::SmallVector<std::string_view, 16> inSplit;
-  wpi::SmallString<32> buf;
-
-  wpi::split(in, inSplit, ',', -1, false);
-  for (auto val : inSplit) {
-    val = wpi::trim(val);
-    if (val.empty()) {
-      continue;
-    }
-    if (val.front() != '"' || val.back() != '"') {
-      fmt::print(stderr,
-                 "GUI: NetworkTables: Could not understand value '{}'\n", val);
-      return false;
-    }
-    val.remove_prefix(1);
-    val.remove_suffix(1);
-    out->emplace_back(wpi::UnescapeCString(val, buf).first);
-  }
-
-  return true;
 }
 
 static void EmitEntryValueReadonly(const NetworkTablesModel::ValueSource& entry,
-                                   const char* typeStr,
+                                   const char* overrideTypeStr,
                                    NetworkTablesFlags flags) {
   auto& val = entry.value;
   if (!val) {
     return;
   }
 
+  const char* typeStr = GetTypeString(val.type(), overrideTypeStr);
+  ImGui::SetNextItemWidth(
+      -1 * (ImGui::CalcTextSize(typeStr).x + ImGui::GetStyle().FramePadding.x));
+
   switch (val.type()) {
     case NT_BOOLEAN:
-      ImGui::LabelText(typeStr ? typeStr : "boolean", "%s",
-                       val.GetBoolean() ? "true" : "false");
+      ImGui::LabelText(typeStr, "%s", val.GetBoolean() ? "true" : "false");
       break;
     case NT_INTEGER:
-      ImGui::LabelText(typeStr ? typeStr : "int", "%" PRId64, val.GetInteger());
+      ImGui::LabelText(typeStr, "%" PRId64, val.GetInteger());
       break;
     case NT_FLOAT:
-      ImGui::LabelText(typeStr ? typeStr : "double", "%.6f", val.GetFloat());
+      ImGui::LabelText(typeStr, "%.6f", val.GetFloat());
       break;
     case NT_DOUBLE: {
       unsigned char precision = (flags & NetworkTablesFlags_Precision) >>
@@ -817,8 +1162,7 @@
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
-      ImGui::LabelText(typeStr ? typeStr : "double",
-                       fmt::format("%.{}f", precision).c_str(),
+      ImGui::LabelText(typeStr, fmt::format("%.{}f", precision).c_str(),
                        val.GetDouble());
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
@@ -826,36 +1170,30 @@
       break;
     }
     case NT_STRING: {
-      // GetString() comes from a std::string, so it's null terminated
-      ImGui::LabelText(typeStr ? typeStr : "string", "%s",
-                       val.GetString().data());
+      ImGui::LabelText(typeStr, "%s", entry.valueStr.c_str());
       break;
     }
     case NT_BOOLEAN_ARRAY:
-      ImGui::LabelText(typeStr ? typeStr : "boolean[]", "%s",
-                       entry.valueStr.c_str());
-      break;
     case NT_INTEGER_ARRAY:
-      ImGui::LabelText(typeStr ? typeStr : "int[]", "%s",
-                       entry.valueStr.c_str());
-      break;
     case NT_FLOAT_ARRAY:
-      ImGui::LabelText(typeStr ? typeStr : "float[]", "%s",
-                       entry.valueStr.c_str());
-      break;
     case NT_DOUBLE_ARRAY:
-      ImGui::LabelText(typeStr ? typeStr : "double[]", "%s",
-                       entry.valueStr.c_str());
-      break;
     case NT_STRING_ARRAY:
-      ImGui::LabelText(typeStr ? typeStr : "string[]", "%s",
-                       entry.valueStr.c_str());
+      ImGui::LabelText(typeStr, "[]");
       break;
-    case NT_RAW:
-      ImGui::LabelText(typeStr ? typeStr : "raw", "[...]");
+    case NT_RAW: {
+      ImGui::LabelText(typeStr, val.GetRaw().empty() ? "[]" : "[...]");
+      if (ImGui::IsItemHovered()) {
+        ImGui::BeginTooltip();
+        if (overrideTypeStr) {
+          ImGui::TextUnformatted(overrideTypeStr);
+        }
+        ImGui::Text("%u bytes", static_cast<unsigned int>(val.GetRaw().size()));
+        ImGui::EndTooltip();
+      }
       break;
+    }
     default:
-      ImGui::LabelText(typeStr ? typeStr : "other", "?");
+      ImGui::LabelText(typeStr, "?");
       break;
   }
 }
@@ -870,21 +1208,165 @@
   return textBuffer;
 }
 
-static void EmitEntryValueEditable(NetworkTablesModel::Entry& entry,
+namespace {
+class ArrayEditor {
+ public:
+  virtual ~ArrayEditor() = default;
+  virtual bool Emit() = 0;
+};
+
+template <int NTType, typename T>
+class ArrayEditorImpl final : public ArrayEditor {
+ public:
+  ArrayEditorImpl(NetworkTablesModel& model, std::string name,
+                  NetworkTablesFlags flags, std::span<const T> value)
+      : m_model{model},
+        m_name{std::move(name)},
+        m_flags{flags},
+        m_arr{value.begin(), value.end()} {}
+
+  bool Emit() final;
+
+ private:
+  NetworkTablesModel& m_model;
+  std::string m_name;
+  NetworkTablesFlags m_flags;
+  std::vector<T> m_arr;
+};
+
+template <int NTType, typename T>
+bool ArrayEditorImpl<NTType, T>::Emit() {
+  if (ImGui::BeginTable(
+          "arrayvalues", 1,
+          ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit |
+              ImGuiTableFlags_RowBg,
+          ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 16))) {
+    ImGui::TableSetupScrollFreeze(0, 1);  // Make top row always visible
+    int toAdd = -1;
+    int toRemove = -1;
+    ImGuiListClipper clipper;
+    clipper.Begin(m_arr.size());
+    while (clipper.Step()) {
+      for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; ++row) {
+        ImGui::TableNextRow();
+        ImGui::TableNextColumn();
+        ImGui::PushID(row);
+        char label[16];
+        wpi::format_to_n_c_str(label, sizeof(label), "[{}]", row);
+        if constexpr (NTType == NT_BOOLEAN_ARRAY) {
+          static const char* boolOptions[] = {"false", "true"};
+          ImGui::Combo(label, &m_arr[row], boolOptions, 2);
+        } else if constexpr (NTType == NT_FLOAT_ARRAY) {
+          ImGui::InputFloat(label, &m_arr[row], 0, 0, "%.6f");
+        } else if constexpr (NTType == NT_DOUBLE_ARRAY) {
+          unsigned char precision = (m_flags & NetworkTablesFlags_Precision) >>
+                                    kNetworkTablesFlags_PrecisionBitShift;
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#endif
+          ImGui::InputDouble(label, &m_arr[row], 0, 0,
+                             fmt::format("%.{}f", precision).c_str());
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+        } else if constexpr (NTType == NT_INTEGER_ARRAY) {
+          ImGui::InputScalar(label, ImGuiDataType_S64, &m_arr[row]);
+        } else if constexpr (NTType == NT_STRING_ARRAY) {
+          ImGui::InputText(label, &m_arr[row]);
+        }
+        ImGui::SameLine();
+        if (ImGui::SmallButton("+")) {
+          toAdd = row;
+        }
+        ImGui::SameLine();
+        if (ImGui::SmallButton("-")) {
+          toRemove = row;
+        }
+        ImGui::PopID();
+      }
+    }
+    if (toAdd != -1) {
+      m_arr.emplace(m_arr.begin() + toAdd);
+    } else if (toRemove != -1) {
+      m_arr.erase(m_arr.begin() + toRemove);
+    }
+    ImGui::EndTable();
+  }
+  if (ImGui::Button("Add to end")) {
+    m_arr.emplace_back();
+  }
+  ImGui::SameLine();
+  if (ImGui::Button("Cancel")) {
+    return true;
+  }
+  ImGui::SameLine();
+  if (ImGui::Button("Apply")) {
+    auto* entry = m_model.GetEntry(m_name);
+    if (!entry) {
+      entry = m_model.AddEntry(
+          nt::GetTopic(m_model.GetInstance().GetHandle(), m_name));
+    }
+    if constexpr (NTType == NT_BOOLEAN_ARRAY) {
+      if (entry->publisher == 0) {
+        entry->publisher =
+            nt::Publish(entry->info.topic, NT_BOOLEAN_ARRAY, "boolean[]");
+      }
+      nt::SetBooleanArray(entry->publisher, m_arr);
+    } else if constexpr (NTType == NT_FLOAT_ARRAY) {
+      if (entry->publisher == 0) {
+        entry->publisher =
+            nt::Publish(entry->info.topic, NT_FLOAT_ARRAY, "float[]");
+      }
+      nt::SetFloatArray(entry->publisher, m_arr);
+    } else if constexpr (NTType == NT_DOUBLE_ARRAY) {
+      if (entry->publisher == 0) {
+        entry->publisher =
+            nt::Publish(entry->info.topic, NT_DOUBLE_ARRAY, "double[]");
+      }
+      nt::SetDoubleArray(entry->publisher, m_arr);
+    } else if constexpr (NTType == NT_INTEGER_ARRAY) {
+      if (entry->publisher == 0) {
+        entry->publisher =
+            nt::Publish(entry->info.topic, NT_INTEGER_ARRAY, "int[]");
+      }
+      nt::SetIntegerArray(entry->publisher, m_arr);
+    } else if constexpr (NTType == NT_STRING_ARRAY) {
+      if (entry->publisher == 0) {
+        entry->publisher =
+            nt::Publish(entry->info.topic, NT_STRING_ARRAY, "string[]");
+      }
+      nt::SetStringArray(entry->publisher, m_arr);
+    }
+    return true;
+  }
+  return false;
+}
+}  // namespace
+
+static ImGuiID gArrayEditorID;
+static std::unique_ptr<ArrayEditor> gArrayEditor;
+
+static void EmitEntryValueEditable(NetworkTablesModel* model,
+                                   NetworkTablesModel::Entry& entry,
                                    NetworkTablesFlags flags) {
   auto& val = entry.value;
   if (!val) {
     return;
   }
 
-  const char* typeStr =
-      entry.info.type_str.empty() ? nullptr : entry.info.type_str.c_str();
+  const char* typeStr = GetTypeString(
+      val.type(),
+      entry.info.type_str.empty() ? nullptr : entry.info.type_str.c_str());
+  ImGui::SetNextItemWidth(
+      -1 * (ImGui::CalcTextSize(typeStr).x + ImGui::GetStyle().FramePadding.x));
+
   ImGui::PushID(entry.info.name.c_str());
   switch (val.type()) {
     case NT_BOOLEAN: {
       static const char* boolOptions[] = {"false", "true"};
       int v = val.GetBoolean() ? 1 : 0;
-      if (ImGui::Combo(typeStr ? typeStr : "boolean", &v, boolOptions, 2)) {
+      if (ImGui::Combo(typeStr, &v, boolOptions, 2)) {
         if (entry.publisher == 0) {
           entry.publisher =
               nt::Publish(entry.info.topic, NT_BOOLEAN, "boolean");
@@ -895,9 +1377,8 @@
     }
     case NT_INTEGER: {
       int64_t v = val.GetInteger();
-      if (ImGui::InputScalar(typeStr ? typeStr : "int", ImGuiDataType_S64, &v,
-                             nullptr, nullptr, nullptr,
-                             ImGuiInputTextFlags_EnterReturnsTrue)) {
+      if (ImGui::InputScalar(typeStr, ImGuiDataType_S64, &v, nullptr, nullptr,
+                             nullptr, ImGuiInputTextFlags_EnterReturnsTrue)) {
         if (entry.publisher == 0) {
           entry.publisher = nt::Publish(entry.info.topic, NT_INTEGER, "int");
         }
@@ -907,7 +1388,7 @@
     }
     case NT_FLOAT: {
       float v = val.GetFloat();
-      if (ImGui::InputFloat(typeStr ? typeStr : "float", &v, 0, 0, "%.6f",
+      if (ImGui::InputFloat(typeStr, &v, 0, 0, "%.6f",
                             ImGuiInputTextFlags_EnterReturnsTrue)) {
         if (entry.publisher == 0) {
           entry.publisher = nt::Publish(entry.info.topic, NT_FLOAT, "float");
@@ -924,7 +1405,7 @@
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
-      if (ImGui::InputDouble(typeStr ? typeStr : "double", &v, 0, 0,
+      if (ImGui::InputDouble(typeStr, &v, 0, 0,
                              fmt::format("%.{}f", precision).c_str(),
                              ImGuiInputTextFlags_EnterReturnsTrue)) {
         if (entry.publisher == 0) {
@@ -938,100 +1419,101 @@
       break;
     }
     case NT_STRING: {
-      char* v = GetTextBuffer(val.GetString());
-      if (ImGui::InputText(typeStr ? typeStr : "string", v, kTextBufferSize,
-                           ImGuiInputTextFlags_EnterReturnsTrue)) {
-        if (entry.publisher == 0) {
-          entry.publisher = nt::Publish(entry.info.topic, NT_STRING, "string");
-        }
-        nt::SetString(entry.publisher, v);
-      }
-      break;
-    }
-    case NT_BOOLEAN_ARRAY: {
       char* v = GetTextBuffer(entry.valueStr);
-      if (ImGui::InputText(typeStr ? typeStr : "boolean[]", v, kTextBufferSize,
+      if (ImGui::InputText(typeStr, v, kTextBufferSize,
                            ImGuiInputTextFlags_EnterReturnsTrue)) {
-        std::vector<int> outv;
-        if (StringToBooleanArray(v, &outv)) {
+        if (v[0] == '"') {
           if (entry.publisher == 0) {
             entry.publisher =
-                nt::Publish(entry.info.topic, NT_BOOLEAN_ARRAY, "boolean[]");
+                nt::Publish(entry.info.topic, NT_STRING, "string");
           }
-          nt::SetBooleanArray(entry.publisher, outv);
+          wpi::SmallString<128> buf;
+          nt::SetString(entry.publisher,
+                        wpi::UnescapeCString(v + 1, buf).first);
         }
       }
       break;
     }
-    case NT_INTEGER_ARRAY: {
-      char* v = GetTextBuffer(entry.valueStr);
-      if (ImGui::InputText(typeStr ? typeStr : "int[]", v, kTextBufferSize,
-                           ImGuiInputTextFlags_EnterReturnsTrue)) {
-        std::vector<int64_t> outv;
-        if (StringToIntegerArray(v, &outv)) {
-          if (entry.publisher == 0) {
-            entry.publisher =
-                nt::Publish(entry.info.topic, NT_INTEGER_ARRAY, "int[]");
-          }
-          nt::SetIntegerArray(entry.publisher, outv);
+    case NT_BOOLEAN_ARRAY:
+      ImGui::LabelText(typeStr, "[]");
+      if (ImGui::BeginPopupContextItem("boolean[]")) {
+        if (ImGui::Selectable("Edit Array")) {
+          gArrayEditor =
+              std::make_unique<ArrayEditorImpl<NT_BOOLEAN_ARRAY, int>>(
+                  *model, entry.info.name, flags,
+                  entry.value.GetBooleanArray());
+          ImGui::OpenPopup(gArrayEditorID);
         }
+        ImGui::EndPopup();
+      }
+      break;
+    case NT_INTEGER_ARRAY:
+      ImGui::LabelText(typeStr, "[]");
+      if (ImGui::BeginPopupContextItem("int[]")) {
+        if (ImGui::Selectable("Edit Array")) {
+          gArrayEditor =
+              std::make_unique<ArrayEditorImpl<NT_INTEGER_ARRAY, int64_t>>(
+                  *model, entry.info.name, flags,
+                  entry.value.GetIntegerArray());
+          ImGui::OpenPopup(gArrayEditorID);
+        }
+        ImGui::EndPopup();
+      }
+      break;
+    case NT_FLOAT_ARRAY:
+      ImGui::LabelText(typeStr, "[]");
+      if (ImGui::BeginPopupContextItem("float[]")) {
+        if (ImGui::Selectable("Edit Array")) {
+          gArrayEditor =
+              std::make_unique<ArrayEditorImpl<NT_FLOAT_ARRAY, float>>(
+                  *model, entry.info.name, flags, entry.value.GetFloatArray());
+          ImGui::OpenPopup(gArrayEditorID);
+        }
+        ImGui::EndPopup();
+      }
+      break;
+    case NT_DOUBLE_ARRAY:
+      ImGui::LabelText(typeStr, "[]");
+      if (ImGui::BeginPopupContextItem("double[]")) {
+        if (ImGui::Selectable("Edit Array")) {
+          gArrayEditor =
+              std::make_unique<ArrayEditorImpl<NT_DOUBLE_ARRAY, double>>(
+                  *model, entry.info.name, flags, entry.value.GetDoubleArray());
+          ImGui::OpenPopup(gArrayEditorID);
+        }
+        ImGui::EndPopup();
+      }
+      break;
+    case NT_STRING_ARRAY:
+      ImGui::LabelText(typeStr, "[]");
+      if (ImGui::BeginPopupContextItem("string[]")) {
+        if (ImGui::Selectable("Edit Array")) {
+          gArrayEditor =
+              std::make_unique<ArrayEditorImpl<NT_STRING_ARRAY, std::string>>(
+                  *model, entry.info.name, flags, entry.value.GetStringArray());
+          ImGui::OpenPopup(gArrayEditorID);
+        }
+        ImGui::EndPopup();
+        break;
+      }
+      break;
+    case NT_RAW: {
+      ImGui::LabelText(typeStr, val.GetRaw().empty() ? "[]" : "[...]");
+      if (ImGui::IsItemHovered()) {
+        ImGui::BeginTooltip();
+        if (!entry.info.type_str.empty()) {
+          ImGui::TextUnformatted(entry.info.type_str.c_str());
+        }
+        ImGui::Text("%u bytes", static_cast<unsigned int>(val.GetRaw().size()));
+        ImGui::EndTooltip();
       }
       break;
     }
-    case NT_FLOAT_ARRAY: {
-      char* v = GetTextBuffer(entry.valueStr);
-      if (ImGui::InputText(typeStr ? typeStr : "float[]", v, kTextBufferSize,
-                           ImGuiInputTextFlags_EnterReturnsTrue)) {
-        std::vector<float> outv;
-        if (StringToFloatArray(v, &outv)) {
-          if (entry.publisher == 0) {
-            entry.publisher =
-                nt::Publish(entry.info.topic, NT_DOUBLE_ARRAY, "float[]");
-          }
-          nt::SetFloatArray(entry.publisher, outv);
-        }
-      }
-      break;
-    }
-    case NT_DOUBLE_ARRAY: {
-      char* v = GetTextBuffer(entry.valueStr);
-      if (ImGui::InputText(typeStr ? typeStr : "double[]", v, kTextBufferSize,
-                           ImGuiInputTextFlags_EnterReturnsTrue)) {
-        std::vector<double> outv;
-        if (StringToFloatArray(v, &outv)) {
-          if (entry.publisher == 0) {
-            entry.publisher =
-                nt::Publish(entry.info.topic, NT_DOUBLE_ARRAY, "double[]");
-          }
-          nt::SetDoubleArray(entry.publisher, outv);
-        }
-      }
-      break;
-    }
-    case NT_STRING_ARRAY: {
-      char* v = GetTextBuffer(entry.valueStr);
-      if (ImGui::InputText(typeStr ? typeStr : "string[]", v, kTextBufferSize,
-                           ImGuiInputTextFlags_EnterReturnsTrue)) {
-        std::vector<std::string> outv;
-        if (StringToStringArray(v, &outv)) {
-          if (entry.publisher == 0) {
-            entry.publisher =
-                nt::Publish(entry.info.topic, NT_STRING_ARRAY, "string[]");
-          }
-          nt::SetStringArray(entry.publisher, outv);
-        }
-      }
-      break;
-    }
-    case NT_RAW:
-      ImGui::LabelText(typeStr ? typeStr : "raw",
-                       val.GetRaw().empty() ? "[]" : "[...]");
-      break;
     case NT_RPC:
-      ImGui::LabelText(typeStr ? typeStr : "rpc", "[...]");
+      ImGui::LabelText(typeStr, "[...]");
       break;
     default:
-      ImGui::LabelText(typeStr ? typeStr : "other", "?");
+      ImGui::LabelText(typeStr, "?");
       break;
   }
   ImGui::PopID();
@@ -1045,58 +1527,97 @@
         model->AddEntry(nt::GetTopic(model->GetInstance().GetHandle(), path));
     if (entry->publisher == 0) {
       entry->publisher = nt::Publish(entry->info.topic, type, typeStr);
+      // publish a default value so it's editable
+      switch (type) {
+        case NT_BOOLEAN:
+          nt::SetDefaultBoolean(entry->publisher, false);
+          break;
+        case NT_INTEGER:
+          nt::SetDefaultInteger(entry->publisher, 0);
+          break;
+        case NT_FLOAT:
+          nt::SetDefaultFloat(entry->publisher, 0.0);
+          break;
+        case NT_DOUBLE:
+          nt::SetDefaultDouble(entry->publisher, 0.0);
+          break;
+        case NT_STRING:
+          nt::SetDefaultString(entry->publisher, "");
+          break;
+        case NT_BOOLEAN_ARRAY:
+          nt::SetDefaultBooleanArray(entry->publisher, {});
+          break;
+        case NT_INTEGER_ARRAY:
+          nt::SetDefaultIntegerArray(entry->publisher, {});
+          break;
+        case NT_FLOAT_ARRAY:
+          nt::SetDefaultFloatArray(entry->publisher, {});
+          break;
+        case NT_DOUBLE_ARRAY:
+          nt::SetDefaultDoubleArray(entry->publisher, {});
+          break;
+        case NT_STRING_ARRAY:
+          nt::SetDefaultStringArray(entry->publisher, {});
+          break;
+        default:
+          break;
+      }
     }
   }
 }
 
+void glass::DisplayNetworkTablesAddMenu(NetworkTablesModel* model,
+                                        std::string_view path,
+                                        NetworkTablesFlags flags) {
+  static char nameBuffer[kTextBufferSize];
+
+  if (ImGui::BeginMenu("Add new...")) {
+    if (ImGui::IsWindowAppearing()) {
+      nameBuffer[0] = '\0';
+    }
+
+    ImGui::InputTextWithHint("New item name", "example", nameBuffer,
+                             kTextBufferSize);
+    std::string fullNewPath;
+    if (path == "/") {
+      path = "";
+    }
+    fullNewPath = fmt::format("{}/{}", path, nameBuffer);
+
+    ImGui::Text("Adding: %s", fullNewPath.c_str());
+    ImGui::Separator();
+    auto entry = model->GetEntry(fullNewPath);
+    bool exists = entry && entry->info.type != NT_Type::NT_UNASSIGNED;
+    bool enabled = (flags & NetworkTablesFlags_CreateNoncanonicalKeys ||
+                    nameBuffer[0] != '\0') &&
+                   !exists;
+
+    CreateTopicMenuItem(model, fullNewPath, NT_STRING, "string", enabled);
+    CreateTopicMenuItem(model, fullNewPath, NT_INTEGER, "int", enabled);
+    CreateTopicMenuItem(model, fullNewPath, NT_FLOAT, "float", enabled);
+    CreateTopicMenuItem(model, fullNewPath, NT_DOUBLE, "double", enabled);
+    CreateTopicMenuItem(model, fullNewPath, NT_BOOLEAN, "boolean", enabled);
+    CreateTopicMenuItem(model, fullNewPath, NT_STRING_ARRAY, "string[]",
+                        enabled);
+    CreateTopicMenuItem(model, fullNewPath, NT_INTEGER_ARRAY, "int[]", enabled);
+    CreateTopicMenuItem(model, fullNewPath, NT_FLOAT_ARRAY, "float[]", enabled);
+    CreateTopicMenuItem(model, fullNewPath, NT_DOUBLE_ARRAY, "double[]",
+                        enabled);
+    CreateTopicMenuItem(model, fullNewPath, NT_BOOLEAN_ARRAY, "boolean[]",
+                        enabled);
+
+    ImGui::EndMenu();
+  }
+}
+
 static void EmitParentContextMenu(NetworkTablesModel* model,
                                   const std::string& path,
                                   NetworkTablesFlags flags) {
-  static char nameBuffer[kTextBufferSize];
   if (ImGui::BeginPopupContextItem(path.c_str())) {
     ImGui::Text("%s", path.c_str());
     ImGui::Separator();
 
-    if (ImGui::BeginMenu("Add new...")) {
-      if (ImGui::IsWindowAppearing()) {
-        nameBuffer[0] = '\0';
-      }
-
-      ImGui::InputTextWithHint("New item name", "example", nameBuffer,
-                               kTextBufferSize);
-      std::string fullNewPath;
-      if (path == "/") {
-        fullNewPath = path + nameBuffer;
-      } else {
-        fullNewPath = fmt::format("{}/{}", path, nameBuffer);
-      }
-
-      ImGui::Text("Adding: %s", fullNewPath.c_str());
-      ImGui::Separator();
-      auto entry = model->GetEntry(fullNewPath);
-      bool exists = entry && entry->info.type != NT_Type::NT_UNASSIGNED;
-      bool enabled = (flags & NetworkTablesFlags_CreateNoncanonicalKeys ||
-                      nameBuffer[0] != '\0') &&
-                     !exists;
-
-      CreateTopicMenuItem(model, fullNewPath, NT_STRING, "string", enabled);
-      CreateTopicMenuItem(model, fullNewPath, NT_INTEGER, "int", enabled);
-      CreateTopicMenuItem(model, fullNewPath, NT_FLOAT, "float", enabled);
-      CreateTopicMenuItem(model, fullNewPath, NT_DOUBLE, "double", enabled);
-      CreateTopicMenuItem(model, fullNewPath, NT_BOOLEAN, "boolean", enabled);
-      CreateTopicMenuItem(model, fullNewPath, NT_STRING_ARRAY, "string[]",
-                          enabled);
-      CreateTopicMenuItem(model, fullNewPath, NT_INTEGER_ARRAY, "int[]",
-                          enabled);
-      CreateTopicMenuItem(model, fullNewPath, NT_FLOAT_ARRAY, "float[]",
-                          enabled);
-      CreateTopicMenuItem(model, fullNewPath, NT_DOUBLE_ARRAY, "double[]",
-                          enabled);
-      CreateTopicMenuItem(model, fullNewPath, NT_BOOLEAN_ARRAY, "boolean[]",
-                          enabled);
-
-      ImGui::EndMenu();
-    }
+    DisplayNetworkTablesAddMenu(model, path, flags);
 
     ImGui::EndPopup();
   }
@@ -1123,13 +1644,28 @@
     ImGui::TableNextRow();
     ImGui::TableNextColumn();
     EmitValueName(child.source.get(), child.name.c_str(), child.path.c_str());
+
     ImGui::TableNextColumn();
     if (!child.valueChildren.empty()) {
-      char label[64];
-      std::snprintf(label, sizeof(label),
-                    child.valueChildrenMap ? "{...}##v_%s" : "[...]##v_%s",
-                    child.name.c_str());
-      if (TreeNodeEx(label, ImGuiTreeNodeFlags_SpanFullWidth)) {
+      auto pos = ImGui::GetCursorPos();
+      char label[128];
+      std::string_view ts = child.typeStr;
+      bool havePopup = GetHeadingTypeString(&ts);
+      wpi::format_to_n_c_str(label, sizeof(label), "{}##v_{}", ts.data(),
+                             child.name.c_str());
+      bool valueChildrenOpen =
+          TreeNodeEx(label, ImGuiTreeNodeFlags_SpanFullWidth);
+      if (havePopup) {
+        if (ImGui::IsItemHovered()) {
+          ImGui::BeginTooltip();
+          ImGui::TextUnformatted(child.typeStr.c_str());
+          ImGui::EndTooltip();
+        }
+      }
+      // make it look like a normal label w/type
+      ImGui::SetCursorPos(pos);
+      ImGui::LabelText(child.valueChildrenMap ? "{...}" : "[...]", "%s", "");
+      if (valueChildrenOpen) {
         EmitValueTree(child.valueChildren, flags);
         TreePop();
       }
@@ -1154,23 +1690,68 @@
   ImGui::TableNextColumn();
   if (!entry.valueChildren.empty()) {
     auto pos = ImGui::GetCursorPos();
-    char label[64];
-    std::snprintf(label, sizeof(label),
-                  entry.valueChildrenMap ? "{...}##v_%s" : "[...]##v_%s",
-                  entry.info.name.c_str());
+    char label[128];
+    std::string_view ts = entry.info.type_str;
+    bool havePopup = GetHeadingTypeString(&ts);
+    wpi::format_to_n_c_str(label, sizeof(label), "{}##v_{}", ts.data(),
+                           entry.info.name.c_str());
     valueChildrenOpen =
         TreeNodeEx(label, ImGuiTreeNodeFlags_SpanFullWidth |
                               ImGuiTreeNodeFlags_AllowItemOverlap);
+    if (havePopup) {
+      if (ImGui::IsItemHovered()) {
+        ImGui::BeginTooltip();
+        ImGui::TextUnformatted(entry.info.type_str.c_str());
+        ImGui::EndTooltip();
+      }
+    }
     // make it look like a normal label w/type
+    const char* typeStr = GetTypeString(
+        NT_RAW,
+        entry.info.type_str.empty() ? nullptr : entry.info.type_str.c_str());
     ImGui::SetCursorPos(pos);
-    ImGui::LabelText(entry.info.type_str.c_str(), "%s", "");
+    ImGui::SetNextItemWidth(-1 * (ImGui::CalcTextSize(typeStr).x +
+                                  ImGui::GetStyle().FramePadding.x));
+    ImGui::LabelText(typeStr, "%s", "");
+    if ((entry.value.IsBooleanArray() || entry.value.IsFloatArray() ||
+         entry.value.IsDoubleArray() || entry.value.IsIntegerArray() ||
+         entry.value.IsStringArray()) &&
+        ImGui::BeginPopupContextItem(label)) {
+      if (ImGui::Selectable("Edit Array")) {
+        if (entry.value.IsBooleanArray()) {
+          gArrayEditor =
+              std::make_unique<ArrayEditorImpl<NT_BOOLEAN_ARRAY, int>>(
+                  *model, entry.info.name, flags,
+                  entry.value.GetBooleanArray());
+        } else if (entry.value.IsFloatArray()) {
+          gArrayEditor =
+              std::make_unique<ArrayEditorImpl<NT_FLOAT_ARRAY, float>>(
+                  *model, entry.info.name, flags, entry.value.GetFloatArray());
+        } else if (entry.value.IsDoubleArray()) {
+          gArrayEditor =
+              std::make_unique<ArrayEditorImpl<NT_DOUBLE_ARRAY, double>>(
+                  *model, entry.info.name, flags, entry.value.GetDoubleArray());
+        } else if (entry.value.IsIntegerArray()) {
+          gArrayEditor =
+              std::make_unique<ArrayEditorImpl<NT_INTEGER_ARRAY, int64_t>>(
+                  *model, entry.info.name, flags,
+                  entry.value.GetIntegerArray());
+        } else if (entry.value.IsStringArray()) {
+          gArrayEditor =
+              std::make_unique<ArrayEditorImpl<NT_STRING_ARRAY, std::string>>(
+                  *model, entry.info.name, flags, entry.value.GetStringArray());
+        }
+        ImGui::OpenPopup(gArrayEditorID);
+      }
+      ImGui::EndPopup();
+    }
   } else if (flags & NetworkTablesFlags_ReadOnly) {
     EmitEntryValueReadonly(
         entry,
         entry.info.type_str.empty() ? nullptr : entry.info.type_str.c_str(),
         flags);
   } else {
-    EmitEntryValueEditable(entry, flags);
+    EmitEntryValueEditable(model, entry, flags);
   }
 
   if (flags & NetworkTablesFlags_ShowProperties) {
@@ -1280,7 +1861,6 @@
   }
   ImGui::TableHeadersRow();
 
-  // EmitParentContextMenu(model, "/", flags);
   if (flags & NetworkTablesFlags_TreeView) {
     switch (category) {
       case ShowPersistent:
@@ -1417,6 +1997,16 @@
 
 void glass::DisplayNetworkTables(NetworkTablesModel* model,
                                  NetworkTablesFlags flags) {
+  gArrayEditorID = ImGui::GetID("Array Editor");
+  if (ImGui::BeginPopupModal("Array Editor", nullptr,
+                             ImGuiWindowFlags_AlwaysAutoResize)) {
+    if (!gArrayEditor || gArrayEditor->Emit()) {
+      ImGui::CloseCurrentPopup();
+      gArrayEditor.release();
+    }
+    ImGui::EndPopup();
+  }
+
   if (flags & NetworkTablesFlags_CombinedView) {
     DisplayTable(model, model->GetTreeRoot(), flags, ShowAll);
   } else {
@@ -1511,6 +2101,7 @@
 
 void NetworkTablesView::Settings() {
   m_flags.DisplayMenu();
+  DisplayNetworkTablesAddMenu(m_model, {}, m_flags.GetFlags());
 }
 
 bool NetworkTablesView::HasSettings() {
diff --git a/third_party/allwpilib/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp b/third_party/allwpilib/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp
index fd6bd52..33c4f02 100644
--- a/third_party/allwpilib/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp
+++ b/third_party/allwpilib/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp
@@ -141,23 +141,45 @@
     case 1:
     case 2: {
       ImGui::InputText("Team/IP", &m_serverTeam);
+      if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) {
+        ImGui::SetTooltip("Team number or IP/MDNS address of server");
+      }
       int* port = m_mode.GetValue() == 1 ? &m_port4 : &m_port3;
       if (ImGui::InputInt("Port", port)) {
         LimitPortRange(port);
       }
+      if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) {
+        ImGui::SetTooltip("TCP Port - leave this at the default");
+      }
       ImGui::SameLine();
       if (ImGui::SmallButton("Default")) {
         *port = m_mode.GetValue() == 1 ? NT_DEFAULT_PORT4 : NT_DEFAULT_PORT3;
       }
       ImGui::InputText("Network Identity", &m_clientName);
+      if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) {
+        ImGui::SetTooltip(
+            "Arbitrary name used to identify clients on the network. Must not "
+            "be blank.");
+      }
       ImGui::Checkbox("Get Address from DS", &m_dsClient);
+      if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) {
+        ImGui::SetTooltip("Attempt to fetch server IP from Driver Station");
+      }
       break;
     }
     case 3:
       ImGui::InputText("Listen Address", &m_listenAddress);
+      if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) {
+        ImGui::SetTooltip(
+            "Address for server to listen on. Leave blank to listen on all "
+            "interfaces.");
+      }
       if (ImGui::InputInt("NT3 port", &m_port3)) {
         LimitPortRange(&m_port3);
       }
+      if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) {
+        ImGui::SetTooltip("TCP Port for NT3. Leave at default if unsure.");
+      }
       ImGui::SameLine();
       if (ImGui::SmallButton("Default##default3")) {
         m_port3 = NT_DEFAULT_PORT3;
@@ -165,11 +187,17 @@
       if (ImGui::InputInt("NT4 port", &m_port4)) {
         LimitPortRange(&m_port4);
       }
+      if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) {
+        ImGui::SetTooltip("TCP Port for NT4. Leave at default if unsure.");
+      }
       ImGui::SameLine();
       if (ImGui::SmallButton("Default##default4")) {
         m_port4 = NT_DEFAULT_PORT4;
       }
       ImGui::InputText("Persistent Filename", &m_persistentFilename);
+      if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) {
+        ImGui::SetTooltip("File for storage of persistent entries");
+      }
       break;
     default:
       break;
diff --git a/third_party/allwpilib/glass/src/libnt/native/include/glass/networktables/NetworkTables.h b/third_party/allwpilib/glass/src/libnt/native/include/glass/networktables/NetworkTables.h
index a7aa514..8416cf9 100644
--- a/third_party/allwpilib/glass/src/libnt/native/include/glass/networktables/NetworkTables.h
+++ b/third_party/allwpilib/glass/src/libnt/native/include/glass/networktables/NetworkTables.h
@@ -18,6 +18,8 @@
 #include <ntcore_cpp.h>
 #include <wpi/DenseMap.h>
 #include <wpi/json.h>
+#include <wpi/protobuf/ProtobufMessageDatabase.h>
+#include <wpi/struct/DynamicStruct.h>
 
 #include "glass/Model.h"
 #include "glass/View.h"
@@ -31,7 +33,7 @@
   struct EntryValueTreeNode;
 
   struct ValueSource {
-    void UpdateFromValue(nt::Value&& v, std::string_view name,
+    void UpdateFromValue(NetworkTablesModel& model, std::string_view name,
                          std::string_view typeStr);
 
     /** The latest value. */
@@ -40,6 +42,9 @@
     /** String representation of the value (for arrays / complex values). */
     std::string valueStr;
 
+    /** Data type */
+    std::string typeStr;
+
     /** Data source (for numeric values). */
     std::unique_ptr<DataSource> source;
 
@@ -48,6 +53,15 @@
 
     /** Whether or not the children represent a map */
     bool valueChildrenMap = false;
+
+   private:
+    void UpdateDiscreteSource(std::string_view name, double value, int64_t time,
+                              bool digital = false);
+
+    template <typename T, typename MakeValue>
+    void UpdateDiscreteArray(std::string_view name, std::span<const T> arr,
+                             int64_t time, MakeValue makeValue,
+                             bool digital = false);
   };
 
   struct EntryValueTreeNode : public ValueSource {
@@ -64,6 +78,10 @@
     Entry& operator=(const Entry&) = delete;
     ~Entry();
 
+    void UpdateFromValue(NetworkTablesModel& model) {
+      ValueSource::UpdateFromValue(model, info.name, info.type_str);
+    }
+
     void UpdateTopic(nt::Event&& event) {
       if (std::holds_alternative<nt::TopicInfo>(event.data)) {
         UpdateInfo(std::get<nt::TopicInfo>(std::move(event.data)));
@@ -149,6 +167,9 @@
   Entry* GetEntry(std::string_view name);
   Entry* AddEntry(NT_Topic topic);
 
+  wpi::StructDescriptorDatabase& GetStructDatabase() { return m_structDb; }
+  wpi::ProtobufMessageDatabase& GetProtobufDatabase() { return m_protoDb; }
+
  private:
   void RebuildTree();
   void RebuildTreeImpl(std::vector<TreeNode>* tree, int category);
@@ -168,6 +189,9 @@
 
   std::map<std::string, Client, std::less<>> m_clients;
   Client m_server;
+
+  wpi::StructDescriptorDatabase m_structDb;
+  wpi::ProtobufMessageDatabase m_protoDb;
 };
 
 using NetworkTablesFlags = int;
@@ -194,6 +218,10 @@
     NetworkTablesModel* model,
     NetworkTablesFlags flags = NetworkTablesFlags_Default);
 
+void DisplayNetworkTablesAddMenu(
+    NetworkTablesModel* model, std::string_view path = {},
+    NetworkTablesFlags flags = NetworkTablesFlags_Default);
+
 class NetworkTablesFlagsSettings {
  public:
   explicit NetworkTablesFlagsSettings(
diff --git a/third_party/allwpilib/googletest/CMakeLists.txt b/third_party/allwpilib/googletest/CMakeLists.txt
index bd6e588..e53fca1 100644
--- a/third_party/allwpilib/googletest/CMakeLists.txt
+++ b/third_party/allwpilib/googletest/CMakeLists.txt
@@ -19,3 +19,5 @@
 
 target_compile_features(gtest PUBLIC cxx_std_20)
 target_compile_features(gtest_main PUBLIC cxx_std_20)
+target_compile_features(gmock PUBLIC cxx_std_20)
+target_compile_features(gmock_main PUBLIC cxx_std_20)
diff --git a/third_party/allwpilib/gradle/wrapper/gradle-wrapper.jar b/third_party/allwpilib/gradle/wrapper/gradle-wrapper.jar
index 249e583..7f93135 100644
--- a/third_party/allwpilib/gradle/wrapper/gradle-wrapper.jar
+++ b/third_party/allwpilib/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/third_party/allwpilib/gradle/wrapper/gradle-wrapper.properties b/third_party/allwpilib/gradle/wrapper/gradle-wrapper.properties
index ae04661..3fa8f86 100644
--- a/third_party/allwpilib/gradle/wrapper/gradle-wrapper.properties
+++ b/third_party/allwpilib/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,7 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/third_party/allwpilib/gradlew b/third_party/allwpilib/gradlew
index a69d9cb..1aa94a4 100755
--- a/third_party/allwpilib/gradlew
+++ b/third_party/allwpilib/gradlew
@@ -55,7 +55,7 @@
 #       Darwin, MinGW, and NonStop.
 #
 #   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 #       within the Gradle project.
 #
 #       You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +80,11 @@
     esac
 done
 
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
+# This is normally unused
+# shellcheck disable=SC2034
 APP_BASE_NAME=${0##*/}
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
@@ -133,22 +131,29 @@
     fi
 else
     JAVACMD=java
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 
 Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
+    fi
 fi
 
 # Increase the maximum file descriptors if we can.
 if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
     case $MAX_FD in #(
       max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
         MAX_FD=$( ulimit -H -n ) ||
             warn "Could not query maximum file descriptor limit"
     esac
     case $MAX_FD in  #(
       '' | soft) :;; #(
       *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
         ulimit -n "$MAX_FD" ||
             warn "Could not set maximum file descriptor limit to $MAX_FD"
     esac
@@ -193,11 +198,15 @@
     done
 fi
 
-# Collect all arguments for the java command;
-#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-#     shell script including quotes and variable substitutions, so put them in
-#     double quotes to make sure that they get re-expanded; and
-#   * put everything else in single quotes, so that it's not re-expanded.
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
 
 set -- \
         "-Dorg.gradle.appname=$APP_BASE_NAME" \
diff --git a/third_party/allwpilib/gradlew.bat b/third_party/allwpilib/gradlew.bat
index 53a6b23..6689b85 100644
--- a/third_party/allwpilib/gradlew.bat
+++ b/third_party/allwpilib/gradlew.bat
@@ -26,6 +26,7 @@
 

 set DIRNAME=%~dp0

 if "%DIRNAME%"=="" set DIRNAME=.

+@rem This is normally unused

 set APP_BASE_NAME=%~n0

 set APP_HOME=%DIRNAME%

 

diff --git a/third_party/allwpilib/hal/.styleguide b/third_party/allwpilib/hal/.styleguide
index 77cd815..bdd9bc4 100644
--- a/third_party/allwpilib/hal/.styleguide
+++ b/third_party/allwpilib/hal/.styleguide
@@ -32,6 +32,7 @@
   ^FRC_FPGA_ChipObject/
   ^FRC_NetworkCommunication/
   ^fmt/
+  ^gtest/
   ^i2clib/
   ^llvm/
   ^opencv2/
diff --git a/third_party/allwpilib/hal/CMakeLists.txt b/third_party/allwpilib/hal/CMakeLists.txt
index fae74aa..603190b 100644
--- a/third_party/allwpilib/hal/CMakeLists.txt
+++ b/third_party/allwpilib/hal/CMakeLists.txt
@@ -55,7 +55,7 @@
 
 set_property(TARGET hal PROPERTY FOLDER "libraries")
 
-install(TARGETS hal EXPORT hal DESTINATION "${main_lib_dest}")
+install(TARGETS hal EXPORT hal)
 install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/hal")
 install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gen/ DESTINATION "${include_dest}/hal")
 
@@ -80,17 +80,10 @@
 
     file(GLOB_RECURSE hal_shared_jni_src src/main/native/cpp/jni/*.cpp)
 
-    file(GLOB_RECURSE JAVA_SOURCES
-        ${CMAKE_CURRENT_BINARY_DIR}/FRCNetComm.java
-        src/main/java/*.java)
+    file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
     set(CMAKE_JNI_TARGET true)
 
-    if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-        set(CMAKE_JAVA_COMPILE_FLAGS "-h" "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-        add_jar(hal_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar OUTPUT_NAME wpiHal)
-    else()
-        add_jar(hal_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar OUTPUT_NAME wpiHal GENERATE_NATIVE_HEADERS hal_jni_headers)
-    endif()
+    add_jar(hal_jar ${JAVA_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/FRCNetComm.java INCLUDE_JARS wpiutil_jar OUTPUT_NAME wpiHal GENERATE_NATIVE_HEADERS hal_jni_headers)
 
     get_property(HAL_JAR_FILE TARGET hal_jar PROPERTY JAR_FILE)
     install(FILES ${HAL_JAR_FILE} DESTINATION "${java_lib_dest}")
@@ -110,20 +103,32 @@
 
     set_property(TARGET haljni PROPERTY FOLDER "libraries")
 
-    if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-        target_include_directories(haljni PRIVATE ${JNI_INCLUDE_DIRS})
-        target_include_directories(haljni PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-    else()
-        target_link_libraries(haljni PRIVATE hal_jni_headers)
-    endif()
+    target_link_libraries(haljni PRIVATE hal_jni_headers)
     add_dependencies(haljni hal_jar)
 
-    if (MSVC)
-        install(TARGETS haljni RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-    endif()
+    install(TARGETS haljni EXPORT haljni)
 
-    install(TARGETS haljni EXPORT haljni DESTINATION "${main_lib_dest}")
+endif()
 
+if (WITH_JAVA_SOURCE)
+    find_package(Java REQUIRED)
+    include(UseJava)
+    file(GLOB HAL_SOURCES src/main/java/edu/wpi/first/hal/*.java ${CMAKE_CURRENT_BINARY_DIR}/FRCNetComm.java)
+    file(GLOB HAL_CAN_SOURCES src/main/java/edu/wpi/first/hal/can/*.java)
+    file(GLOB HAL_SIMULATION_SOURCES src/main/java/edu/wpi/first/hal/simulation/*.java)
+    file(GLOB HAL_UTIL_SOURCES src/main/java/edu/wpi/first/hal/util/*.java)
+    add_jar(hal_src_jar
+    RESOURCES NAMESPACE "edu/wpi/first/hal" ${HAL_SOURCES}
+    NAMESPACE "edu/wpi/first/hal/can" ${HAL_CAN_SOURCES}
+    NAMESPACE "edu/wpi/first/hal/communication" src/main/java/edu/wpi/first/hal/communication/NIRioStatus.java
+    NAMESPACE "edu/wpi/first/hal/simulation" ${HAL_SIMULATION_SOURCES}
+    NAMESPACE "edu/wpi/first/hal/util" ${HAL_UTIL_SOURCES}
+    OUTPUT_NAME wpiHal-sources)
+
+    get_property(HAL_SRC_JAR_FILE TARGET hal_src_jar PROPERTY JAR_FILE)
+    install(FILES ${HAL_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+    set_property(TARGET hal_src_jar PROPERTY FOLDER "java")
 endif()
 
 if (WITH_TESTS)
diff --git a/third_party/allwpilib/hal/src/generate/ResourceType.txt b/third_party/allwpilib/hal/src/generate/ResourceType.txt
index f48cb59..943d8b0 100644
--- a/third_party/allwpilib/hal/src/generate/ResourceType.txt
+++ b/third_party/allwpilib/hal/src/generate/ResourceType.txt
@@ -92,4 +92,11 @@
 kResourceType_DutyCycle = 91
 kResourceType_AddressableLEDs = 92
 kResourceType_FusionVenom = 93
-kResourceType_PS4Controller = 94
+kResourceType_CTRE_future7 = 94
+kResourceType_CTRE_future8 = 95
+kResourceType_CTRE_future9 = 96
+kResourceType_CTRE_future10 = 97
+kResourceType_CTRE_future11 = 98
+kResourceType_CTRE_future12 = 99
+kResourceType_CTRE_future13 = 100
+kResourceType_CTRE_future14 = 101
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AccelerometerJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AccelerometerJNI.java
index 3f22bcb..5f34924 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AccelerometerJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AccelerometerJNI.java
@@ -4,14 +4,59 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Accelerometer HAL JNI methods.
+ *
+ * @see "hal/Accelerometer.h"
+ */
 public class AccelerometerJNI extends JNIWrapper {
+  /**
+   * Sets the accelerometer to active or standby mode.
+   *
+   * <p>It must be in standby mode to change any configuration.
+   *
+   * @see "HAL_SetAccelerometerActive"
+   * @param active true to set to active, false for standby
+   */
   public static native void setAccelerometerActive(boolean active);
 
+  /**
+   * Sets the range of values that can be measured (either 2, 4, or 8 g-forces).
+   *
+   * <p>The accelerometer should be in standby mode when this is called.
+   *
+   * @see "HAL_SetAccelerometerRange(int range)"
+   * @param range the accelerometer range
+   */
   public static native void setAccelerometerRange(int range);
 
+  /**
+   * Gets the x-axis acceleration.
+   *
+   * <p>This is a floating point value in units of 1 g-force.
+   *
+   * @see "HAL_GetAccelerometerX()"
+   * @return the X acceleration
+   */
   public static native double getAccelerometerX();
 
+  /**
+   * Gets the y-axis acceleration.
+   *
+   * <p>This is a floating point value in units of 1 g-force.
+   *
+   * @see "HAL_GetAccelerometerY()"
+   * @return the Y acceleration
+   */
   public static native double getAccelerometerY();
 
+  /**
+   * Gets the z-axis acceleration.
+   *
+   * <p>This is a floating point value in units of 1 g-force.
+   *
+   * @see "HAL_GetAccelerometerZ()"
+   * @return the Z acceleration
+   */
   public static native double getAccelerometerZ();
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java
index 441ce9f..f416ff8 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java
@@ -9,6 +9,7 @@
 public class AccumulatorResult {
   /** The total value accumulated. */
   public long value;
+
   /** The number of sample value was accumulated over. */
   public long count;
 
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java
index 1dd80a7..b2695a3 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java
@@ -4,21 +4,97 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Addressable LED HAL JNI Methods.
+ *
+ * @see "hal/AdressableLED.h"
+ */
 public class AddressableLEDJNI extends JNIWrapper {
+  /**
+   * Initialize Addressable LED using a PWM Digital handle.
+   *
+   * @param pwmHandle handle of the digital port for PWM
+   * @return Addressable LED handle
+   * @see "HAL_InitializeAddressableLED"
+   */
   public static native int initialize(int pwmHandle);
 
+  /**
+   * Free the Addressable LED Handle.
+   *
+   * @param handle the Addressable LED handle to free
+   * @see "HAL_FreeAddressableLED"
+   */
   public static native void free(int handle);
 
+  /**
+   * Sets the length of the LED strip.
+   *
+   * <p>The max length is 5460 LEDs.
+   *
+   * @param handle the Addressable LED handle
+   * @param length the strip length
+   * @see "HAL_SetAddressableLEDLength"
+   */
   public static native void setLength(int handle, int length);
 
+  /**
+   * Sets the led output data.
+   *
+   * <p>If the output is enabled, this will start writing the next data cycle. It is safe to call,
+   * even while output is enabled.
+   *
+   * @param handle the Addressable LED handle
+   * @param data the buffer to write
+   * @see "HAL_WriteAddressableLEDData"
+   */
   public static native void setData(int handle, byte[] data);
 
+  /**
+   * Sets the bit timing.
+   *
+   * <p>By default, the driver is set up to drive WS2812Bs, so nothing needs to be set for those.
+   *
+   * @param handle the Addressable LED handle
+   * @param highTime0NanoSeconds high time for 0 bit (default 400ns)
+   * @param lowTime0NanoSeconds low time for 0 bit (default 900ns)
+   * @param highTime1NanoSeconds high time for 1 bit (default 900ns)
+   * @param lowTime1NanoSeconds low time for 1 bit (default 600ns)
+   * @see "HAL_SetAddressableLEDBitTiming"
+   */
   public static native void setBitTiming(
-      int handle, int lowTime0, int highTime0, int lowTime1, int highTime1);
+      int handle,
+      int highTime0NanoSeconds,
+      int lowTime0NanoSeconds,
+      int highTime1NanoSeconds,
+      int lowTime1NanoSeconds);
 
-  public static native void setSyncTime(int handle, int syncTime);
+  /**
+   * Sets the sync time.
+   *
+   * <p>The sync time is the time to hold output so LEDs enable. Default set for WS2812B.
+   *
+   * @param handle the Addressable LED handle
+   * @param syncTimeMicroSeconds the sync time (default 280us)
+   * @see "HAL_SetAddressableLEDSyncTime"
+   */
+  public static native void setSyncTime(int handle, int syncTimeMicroSeconds);
 
+  /**
+   * Starts the output.
+   *
+   * <p>The output writes continuously.
+   *
+   * @param handle the Addressable LED handle
+   * @see "HAL_StartAddressableLEDOutput"
+   */
   public static native void start(int handle);
 
+  /**
+   * Stops the output.
+   *
+   * @param handle the Addressable LED handle
+   * @see "HAL_StopAddressableLEDOutput"
+   */
   public static native void stop(int handle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AllianceStationID.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AllianceStationID.java
index 7b601b3..56c2143 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AllianceStationID.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AllianceStationID.java
@@ -5,6 +5,7 @@
 package edu.wpi.first.hal;
 
 public enum AllianceStationID {
+  Unknown,
   Red1,
   Red2,
   Red3,
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AnalogGyroJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AnalogGyroJNI.java
index 5763f6e..cbc3d4f 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AnalogGyroJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AnalogGyroJNI.java
@@ -4,30 +4,126 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Analog Gyro JNI Functions.
+ *
+ * @see "hal/AnalogGyro.h"
+ */
 public class AnalogGyroJNI extends JNIWrapper {
+  /**
+   * Initializes an analog gyro.
+   *
+   * @param halAnalogInputHandle handle to the analog input port
+   * @return the initialized gyro handle
+   * @see "HAL_InitializeAnalogGyro"
+   */
   public static native int initializeAnalogGyro(int halAnalogInputHandle);
 
+  /**
+   * Sets up an analog gyro with the proper offsets and settings for the KOP analog gyro.
+   *
+   * @param handle the gyro handle
+   * @see "HAL_SetupAnalogGyro"
+   */
   public static native void setupAnalogGyro(int handle);
 
+  /**
+   * Frees an analog gyro.
+   *
+   * @param handle the gyro handle
+   * @see "HAL_FreeAnalogGyro"
+   */
   public static native void freeAnalogGyro(int handle);
 
+  /**
+   * Sets the analog gyro parameters to the specified values.
+   *
+   * <p>This is meant to be used if you want to reuse the values from a previous calibration.
+   *
+   * @param handle the gyro handle
+   * @param voltsPerDegreePerSecond the gyro volts scaling
+   * @param offset the gyro offset
+   * @param center the gyro center
+   * @see "HAL_SetAnalogGyroParameters"
+   */
   public static native void setAnalogGyroParameters(
       int handle, double voltsPerDegreePerSecond, double offset, int center);
 
+  /**
+   * Sets the analog gyro volts per degrees per second scaling.
+   *
+   * @param handle the gyro handle
+   * @param voltsPerDegreePerSecond the gyro volts scaling
+   * @see "HAL_SetAnalogGyroVoltsPerDegreePerSecond"
+   */
   public static native void setAnalogGyroVoltsPerDegreePerSecond(
       int handle, double voltsPerDegreePerSecond);
 
+  /**
+   * Resets the analog gyro value to 0.
+   *
+   * @param handle the gyro handle
+   * @see "HAL_ResetAnalogGyro"
+   */
   public static native void resetAnalogGyro(int handle);
 
+  /**
+   * Calibrates the analog gyro.
+   *
+   * <p>This happens by calculating the average value of the gyro over 5 seconds, and setting that
+   * as the center. Note that this call blocks for 5 seconds to perform this.
+   *
+   * @param handle the gyro handle
+   * @see "HAL_CalibrateAnalogGyro"
+   */
   public static native void calibrateAnalogGyro(int handle);
 
+  /**
+   * Sets the deadband of the analog gyro.
+   *
+   * @param handle the gyro handle
+   * @param volts the voltage deadband
+   * @see "HAL_SetAnalogGyroDeadband"
+   */
   public static native void setAnalogGyroDeadband(int handle, double volts);
 
+  /**
+   * Gets the gyro angle in degrees.
+   *
+   * @param handle the gyro handle
+   * @return the gyro angle in degrees
+   * @see "HAL_GetAnalogGyroAngle"
+   */
   public static native double getAnalogGyroAngle(int handle);
 
+  /**
+   * Gets the gyro rate in degrees/second.
+   *
+   * @param handle the gyro handle
+   * @return the gyro rate in degrees/second
+   * @see "HAL_GetAnalogGyroRate"
+   */
   public static native double getAnalogGyroRate(int handle);
 
+  /**
+   * Gets the calibrated gyro offset.
+   *
+   * <p>Can be used to not repeat a calibration but reconstruct the gyro object.
+   *
+   * @param handle the gyro handle
+   * @return the gryo offset
+   * @see "HAL_GetAnalogGyroOffset"
+   */
   public static native double getAnalogGyroOffset(int handle);
 
+  /**
+   * Gets the calibrated gyro center.
+   *
+   * <p>Can be used to not repeat a calibration but reconstruct the gyro object.
+   *
+   * @param handle the gyro handle
+   * @return the gyro center
+   * @see "HAL_GetAnalogGyroCenter"
+   */
   public static native int getAnalogGyroCenter(int handle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java
index b80388d..bf8b27f 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java
@@ -4,6 +4,14 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Analog Input / Output / Accumulator / Trigger JNI Functions.
+ *
+ * @see "hal/AnalogInput.h"
+ * @see "hal/AnalogOutput.h"
+ * @see "hal/AnalogAccumulator.h"
+ * @see "hal/AnalogTrigger.h"
+ */
 public class AnalogJNI extends JNIWrapper {
   /**
    * <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:58</i><br>
@@ -12,104 +20,481 @@
   public interface AnalogTriggerType {
     /** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:54</i> */
     int kInWindow = 0;
+
     /** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:55</i> */
     int kState = 1;
+
     /** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:56</i> */
     int kRisingPulse = 2;
+
     /** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:57</i> */
     int kFallingPulse = 3;
   }
 
+  /**
+   * Initializes the analog input port using the given port object.
+   *
+   * @param halPortHandle Handle to the port to initialize.
+   * @return the created analog input handle
+   * @see "HAL_InitializeAnalogInputPort"
+   */
   public static native int initializeAnalogInputPort(int halPortHandle);
 
+  /**
+   * Frees an analog input port.
+   *
+   * @param portHandle Handle to the analog port.
+   * @see "HAL_FreeAnalogInputPort"
+   */
   public static native void freeAnalogInputPort(int portHandle);
 
+  /**
+   * Initializes the analog output port using the given port object.
+   *
+   * @param halPortHandle handle to the port
+   * @return the created analog output handle
+   * @see "HAL_InitializeAnalogOutputPort"
+   */
   public static native int initializeAnalogOutputPort(int halPortHandle);
 
+  /**
+   * Frees an analog output port.
+   *
+   * @param portHandle the analog output handle
+   * @see "HAL_FreeAnalogOutputPort"
+   */
   public static native void freeAnalogOutputPort(int portHandle);
 
+  /**
+   * Checks that the analog module number is valid.
+   *
+   * @param module The analog module number.
+   * @return Analog module is valid and present
+   * @see "HAL_CheckAnalogModule"
+   */
   public static native boolean checkAnalogModule(byte module);
 
+  /**
+   * Checks that the analog output channel number is valid. Verifies that the analog channel number
+   * is one of the legal channel numbers. Channel numbers are 0-based.
+   *
+   * @param channel The analog output channel number.
+   * @return Analog channel is valid
+   * @see "HAL_CheckAnalogInputChannel"
+   */
   public static native boolean checkAnalogInputChannel(int channel);
 
   public static native boolean checkAnalogOutputChannel(int channel);
 
+  /**
+   * Indicates the analog input is used by a simulated device.
+   *
+   * @param handle the analog input handle
+   * @param device simulated device handle
+   * @see "HAL_SetAnalogInputSimDevice"
+   */
   public static native void setAnalogInputSimDevice(int handle, int device);
 
   public static native void setAnalogOutput(int portHandle, double voltage);
 
   public static native double getAnalogOutput(int portHandle);
 
+  /**
+   * Sets the sample rate.
+   *
+   * <p>This is a global setting for the Athena and effects all channels.
+   *
+   * @param samplesPerSecond The number of samples per channel per second.
+   * @see "HAL_SetAnalogSampleRate"
+   */
   public static native void setAnalogSampleRate(double samplesPerSecond);
 
+  /**
+   * Gets the current sample rate.
+   *
+   * <p>This assumes one entry in the scan list. This is a global setting for the Athena and effects
+   * all channels.
+   *
+   * @return Sample rate.
+   * @see "HAL_GetAnalogSampleRate"
+   */
   public static native double getAnalogSampleRate();
 
+  /**
+   * Sets the number of averaging bits.
+   *
+   * <p>This sets the number of averaging bits. The actual number of averaged samples is 2**bits.
+   * Use averaging to improve the stability of your measurement at the expense of sampling rate. The
+   * averaging is done automatically in the FPGA.
+   *
+   * @param analogPortHandle Handle to the analog port to configure.
+   * @param bits Number of bits to average.
+   * @see "HAL_SetAnalogAverageBits"
+   */
   public static native void setAnalogAverageBits(int analogPortHandle, int bits);
 
+  /**
+   * Gets the number of averaging bits.
+   *
+   * <p>This gets the number of averaging bits from the FPGA. The actual number of averaged samples
+   * is 2**bits. The averaging is done automatically in the FPGA.
+   *
+   * @param analogPortHandle Handle to the analog port to use.
+   * @return Bits to average.
+   * @see "HAL_GetAnalogAverageBits"
+   */
   public static native int getAnalogAverageBits(int analogPortHandle);
 
+  /**
+   * Sets the number of oversample bits.
+   *
+   * <p>This sets the number of oversample bits. The actual number of oversampled values is 2**bits.
+   * Use oversampling to improve the resolution of your measurements at the expense of sampling
+   * rate. The oversampling is done automatically in the FPGA.
+   *
+   * @param analogPortHandle Handle to the analog port to use.
+   * @param bits Number of bits to oversample.
+   * @see "HAL_SetAnalogOversampleBits"
+   */
   public static native void setAnalogOversampleBits(int analogPortHandle, int bits);
 
+  /**
+   * Gets the number of oversample bits.
+   *
+   * <p>This gets the number of oversample bits from the FPGA. The actual number of oversampled
+   * values is 2**bits. The oversampling is done automatically in the FPGA.
+   *
+   * @param analogPortHandle Handle to the analog port to use.
+   * @return Bits to oversample.
+   * @see "HAL_GetAnalogOversampleBits"
+   */
   public static native int getAnalogOversampleBits(int analogPortHandle);
 
+  /**
+   * Gets a sample straight from the channel on this module.
+   *
+   * <p>The sample is a 12-bit value representing the 0V to 5V range of the A/D converter in the
+   * module. The units are in A/D converter codes. Use GetVoltage() to get the analog value in
+   * calibrated units.
+   *
+   * @param analogPortHandle Handle to the analog port to use.
+   * @return A sample straight from the channel on this module.
+   * @see "HAL_GetAnalogValue"
+   */
   public static native short getAnalogValue(int analogPortHandle);
 
+  /**
+   * Gets a sample from the output of the oversample and average engine for the channel.
+   *
+   * <p>The sample is 12-bit + the value configured in SetOversampleBits(). The value configured in
+   * SetAverageBits() will cause this value to be averaged 2**bits number of samples. This is not a
+   * sliding window. The sample will not change until 2**(OversampleBits + AverageBits) samples have
+   * been acquired from the module on this channel. Use GetAverageVoltage() to get the analog value
+   * in calibrated units.
+   *
+   * @param analogPortHandle Handle to the analog port to use.
+   * @return A sample from the oversample and average engine for the channel.
+   * @see "HAL_GetAnalogAverageValue"
+   */
   public static native int getAnalogAverageValue(int analogPortHandle);
 
+  /**
+   * Converts a voltage to a raw value for a specified channel.
+   *
+   * <p>This process depends on the calibration of each channel, so the channel must be specified.
+   *
+   * <p>todo This assumes raw values. Oversampling not supported as is.
+   *
+   * @param analogPortHandle Handle to the analog port to use.
+   * @param voltage The voltage to convert.
+   * @return The raw value for the channel.
+   * @see "HAL_GetAnalogVoltsToValue"
+   */
   public static native int getAnalogVoltsToValue(int analogPortHandle, double voltage);
 
+  /**
+   * Get the analog voltage from a raw value.
+   *
+   * @param analogPortHandle Handle to the analog port the values were read from.
+   * @param value The raw analog value
+   * @return The voltage relating to the value
+   * @see "HAL_GetAnalogValueToVolts"
+   */
   public static native double getAnalogValueToVolts(int analogPortHandle, int value);
 
+  /**
+   * Gets a scaled sample straight from the channel on this module.
+   *
+   * <p>The value is scaled to units of Volts using the calibrated scaling data from GetLSBWeight()
+   * and GetOffset().
+   *
+   * @param analogPortHandle Handle to the analog port to use.
+   * @return A scaled sample straight from the channel on this module.
+   * @see "HAL_GetAnalogVoltage"
+   */
   public static native double getAnalogVoltage(int analogPortHandle);
 
+  /**
+   * Gets a scaled sample from the output of the oversample and average engine for the channel.
+   *
+   * <p>The value is scaled to units of Volts using the calibrated scaling data from GetLSBWeight()
+   * and GetOffset(). Using oversampling will cause this value to be higher resolution, but it will
+   * update more slowly. Using averaging will cause this value to be more stable, but it will update
+   * more slowly.
+   *
+   * @param analogPortHandle Handle to the analog port to use.
+   * @return A scaled sample from the output of the oversample and average engine for the channel.
+   * @see "HAL_GetAnalogAverageVoltage"
+   */
   public static native double getAnalogAverageVoltage(int analogPortHandle);
 
+  /**
+   * Gets the factory scaling least significant bit weight constant. The least significant bit
+   * weight constant for the channel that was calibrated in manufacturing and stored in an eeprom in
+   * the module.
+   *
+   * <p>Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
+   *
+   * @param analogPortHandle Handle to the analog port to use.
+   * @return Least significant bit weight.
+   * @see "HAL_GetAnalogLSBWeight"
+   */
   public static native int getAnalogLSBWeight(int analogPortHandle);
 
+  /**
+   * Gets the factory scaling offset constant. The offset constant for the channel that was
+   * calibrated in manufacturing and stored in an eeprom in the module.
+   *
+   * <p>Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
+   *
+   * @param analogPortHandle Handle to the analog port to use.
+   * @return Offset constant.
+   * @see "HAL_GetAnalogOffset"
+   */
   public static native int getAnalogOffset(int analogPortHandle);
 
+  /**
+   * Is the channel attached to an accumulator.
+   *
+   * @param analogPortHandle Handle to the analog port.
+   * @return The analog channel is attached to an accumulator.
+   * @see "HAL_IsAccumulatorChannel"
+   */
   public static native boolean isAccumulatorChannel(int analogPortHandle);
 
+  /**
+   * Initialize the accumulator.
+   *
+   * @param analogPortHandle Handle to the analog port.
+   * @see "HAL_InitAccumulator"
+   */
   public static native void initAccumulator(int analogPortHandle);
 
+  /**
+   * Resets the accumulator to the initial value.
+   *
+   * @param analogPortHandle Handle to the analog port.
+   * @see "HAL_ResetAccumulator"
+   */
   public static native void resetAccumulator(int analogPortHandle);
 
+  /**
+   * Set the center value of the accumulator.
+   *
+   * <p>The center value is subtracted from each A/D value before it is added to the accumulator.
+   * This is used for the center value of devices like gyros and accelerometers to make integration
+   * work and to take the device offset into account when integrating.
+   *
+   * <p>This center value is based on the output of the oversampled and averaged source from channel
+   * 1. Because of this, any non-zero oversample bits will affect the size of the value for this
+   * field.
+   *
+   * @param analogPortHandle Handle to the analog port.
+   * @param center The center value of the accumulator.
+   * @see "HAL_SetAccumulatorCenter"
+   */
   public static native void setAccumulatorCenter(int analogPortHandle, int center);
 
+  /**
+   * Set the accumulator's deadband.
+   *
+   * @param analogPortHandle Handle to the analog port.
+   * @param deadband The deadband of the accumulator.
+   * @see "HAL_SetAccumulatorDeadband"
+   */
   public static native void setAccumulatorDeadband(int analogPortHandle, int deadband);
 
+  /**
+   * Read the accumulated value.
+   *
+   * <p>Read the value that has been accumulating on channel 1. The accumulator is attached after
+   * the oversample and average engine.
+   *
+   * @param analogPortHandle Handle to the analog port.
+   * @return The 64-bit value accumulated since the last Reset().
+   * @see "HAL_GetAccumulatorValue"
+   */
   public static native long getAccumulatorValue(int analogPortHandle);
 
+  /**
+   * Read the number of accumulated values.
+   *
+   * <p>Read the count of the accumulated values since the accumulator was last Reset().
+   *
+   * @param analogPortHandle Handle to the analog port.
+   * @return The number of times samples from the channel were accumulated.
+   * @see "HAL_GetAccumulatorCount"
+   */
   public static native int getAccumulatorCount(int analogPortHandle);
 
+  /**
+   * Read the accumulated value and the number of accumulated values atomically.
+   *
+   * <p>This function reads the value and count from the FPGA atomically. This can be used for
+   * averaging.
+   *
+   * @param analogPortHandle Handle to the analog port.
+   * @param result Accumulator result.
+   * @see "HAL_GetAccumulatorOutput"
+   */
   public static native void getAccumulatorOutput(int analogPortHandle, AccumulatorResult result);
 
+  /**
+   * Initializes an analog trigger.
+   *
+   * @param analogInputHandle the analog input to use for triggering
+   * @return the created analog trigger handle
+   * @see "HAL_InitializeAnalogTrigger"
+   */
   public static native int initializeAnalogTrigger(int analogInputHandle);
 
+  /**
+   * Initializes an analog trigger with a Duty Cycle input.
+   *
+   * @param dutyCycleHandle the analog input to use for duty cycle
+   * @return tbe created analog trigger handle
+   * @see "HAL_InitializeAnalogTriggerDutyCycle"
+   */
   public static native int initializeAnalogTriggerDutyCycle(int dutyCycleHandle);
 
+  /**
+   * Frees an analog trigger.
+   *
+   * @param analogTriggerHandle the trigger handle
+   * @see "HAL_CleanAnalogTrigger"
+   */
   public static native void cleanAnalogTrigger(int analogTriggerHandle);
 
+  /**
+   * Sets the raw ADC upper and lower limits of the analog trigger.
+   *
+   * <p>HAL_SetAnalogTriggerLimitsVoltage or HAL_SetAnalogTriggerLimitsDutyCycle is likely better in
+   * most cases.
+   *
+   * @param analogTriggerHandle the trigger handle
+   * @param lower the lower ADC value
+   * @param upper the upper ADC value
+   * @see "HAL_SetAnalogTriggerLimitsRaw"
+   */
   public static native void setAnalogTriggerLimitsRaw(
       int analogTriggerHandle, int lower, int upper);
 
+  /**
+   * Sets the upper and lower limits of the analog trigger.
+   *
+   * <p>The limits are given as floating point duty cycle values.
+   *
+   * @param analogTriggerHandle the trigger handle
+   * @param lower the lower duty cycle value
+   * @param higher the upper duty cycle value
+   * @see "HAL_SetAnalogTriggerLimitsDutyCycle"
+   */
   public static native void setAnalogTriggerLimitsDutyCycle(
       int analogTriggerHandle, double lower, double higher);
 
+  /**
+   * Sets the upper and lower limits of the analog trigger.
+   *
+   * <p>The limits are given as floating point voltage values.
+   *
+   * @param analogTriggerHandle the trigger handle
+   * @param lower the lower voltage value
+   * @param upper the upper voltage value
+   * @see "HAL_SetAnalogTriggerLimitsVoltage"
+   */
   public static native void setAnalogTriggerLimitsVoltage(
       int analogTriggerHandle, double lower, double upper);
 
+  /**
+   * Configures the analog trigger to use the averaged vs. raw values.
+   *
+   * <p>If the value is true, then the averaged value is selected for the analog trigger, otherwise
+   * the immediate value is used.
+   *
+   * <p>This is not allowed to be used if filtered mode is set. This is not allowed to be used with
+   * Duty Cycle based inputs.
+   *
+   * @param analogTriggerHandle the trigger handle
+   * @param useAveragedValue true to use averaged values, false for raw
+   * @see "HAL_SetAnalogTriggerAveraged"
+   */
   public static native void setAnalogTriggerAveraged(
       int analogTriggerHandle, boolean useAveragedValue);
 
+  /**
+   * Configures the analog trigger to use a filtered value.
+   *
+   * <p>The analog trigger will operate with a 3 point average rejection filter. This is designed to
+   * help with 360 degree pot applications for the period where the pot crosses through zero.
+   *
+   * <p>This is not allowed to be used if averaged mode is set.
+   *
+   * @param analogTriggerHandle the trigger handle
+   * @param useFilteredValue true to use filtered values, false for average or raw
+   * @see "HAL_SetAnalogTriggerFiltered"
+   */
   public static native void setAnalogTriggerFiltered(
       int analogTriggerHandle, boolean useFilteredValue);
 
+  /**
+   * Returns the InWindow output of the analog trigger.
+   *
+   * <p>True if the analog input is between the upper and lower limits.
+   *
+   * @param analogTriggerHandle the trigger handle
+   * @return the InWindow output of the analog trigger
+   * @see "HAL_GetAnalogTriggerInWindow"
+   */
   public static native boolean getAnalogTriggerInWindow(int analogTriggerHandle);
 
+  /**
+   * Returns the TriggerState output of the analog trigger.
+   *
+   * <p>True if above upper limit. False if below lower limit. If in Hysteresis, maintain previous
+   * state.
+   *
+   * @param analogTriggerHandle the trigger handle
+   * @return the TriggerState output of the analog trigger
+   * @see "HAL_GetAnalogTriggerTriggerState"
+   */
   public static native boolean getAnalogTriggerTriggerState(int analogTriggerHandle);
 
+  /**
+   * Gets the state of the analog trigger output.
+   *
+   * @param analogTriggerHandle the trigger handle
+   * @param type the type of trigger to trigger on
+   * @return the state of the analog trigger output
+   * @see "HAL_GetAnalogTriggerOutput"
+   */
   public static native boolean getAnalogTriggerOutput(int analogTriggerHandle, int type);
 
+  /**
+   * Get the FPGA index for the AnlogTrigger.
+   *
+   * @param analogTriggerHandle the trigger handle
+   * @return the FPGA index
+   * @see "HAL_GetAnalogTriggerFPGAIndex"
+   */
   public static native int getAnalogTriggerFPGAIndex(int analogTriggerHandle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java
index 227da4d..b42def4 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java
@@ -4,31 +4,177 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * CAN API HAL JNI Functions.
+ *
+ * @see "hal/CANAPI.h"
+ */
 public class CANAPIJNI extends JNIWrapper {
+  /**
+   * Reads the current value of the millisecond-resolution timer that the CAN API functions use as a
+   * time base.
+   *
+   * @return Current value of timer used as a base time by the CAN API in milliseconds.
+   * @see "HAL_GetCANPacketBaseTime"
+   */
+  public static native long getCANPacketBaseTime();
+
+  /**
+   * Initializes a CAN device.
+   *
+   * <p>These follow the FIRST standard CAN layout.
+   * https://docs.wpilib.org/en/stable/docs/software/can-devices/can-addressing.html
+   *
+   * @param manufacturer the can manufacturer
+   * @param deviceId the device ID (0-63)
+   * @param deviceType the device type
+   * @return the created CAN handle
+   * @see "HAL_InitializeCAN"
+   */
   public static native int initializeCAN(int manufacturer, int deviceId, int deviceType);
 
+  /**
+   * Frees a CAN device.
+   *
+   * @param handle the CAN handle
+   * @see "HAL_CleanCAN"
+   */
   public static native void cleanCAN(int handle);
 
+  /**
+   * Writes a packet to the CAN device with a specific ID.
+   *
+   * <p>This ID is 10 bits.
+   *
+   * @param handle the CAN handle
+   * @param data the data to write (0-8 bytes)
+   * @param apiId the ID to write (0-1023 bits)
+   * @see "HAL_WriteCANPacket"
+   */
   public static native void writeCANPacket(int handle, byte[] data, int apiId);
 
+  /**
+   * Writes a repeating packet to the CAN device with a specific ID.
+   *
+   * <p>This ID is 10 bits.
+   *
+   * <p>The RoboRIO will automatically repeat the packet at the specified interval
+   *
+   * @param handle the CAN handle
+   * @param data the data to write (0-8 bytes)
+   * @param apiId the ID to write (0-1023)
+   * @param repeatMs the period to repeat in ms
+   * @see "HAL_WriteCANPacketRepeating"
+   */
   public static native void writeCANPacketRepeating(
       int handle, byte[] data, int apiId, int repeatMs);
 
+  /**
+   * Writes an RTR frame of the specified length to the CAN device with the specific ID.
+   *
+   * <p>By spec, the length must be equal to the length sent by the other device, otherwise behavior
+   * is unspecified.
+   *
+   * @param handle the CAN handle
+   * @param length the length of data to request (0-8)
+   * @param apiId the ID to write (0-1023)
+   * @see "HAL_WriteCANRTRFrame"
+   */
   public static native void writeCANRTRFrame(int handle, int length, int apiId);
 
+  /**
+   * Writes a packet to the CAN device with a specific ID without throwing on error.
+   *
+   * <p>This ID is 10 bits.
+   *
+   * @param handle the CAN handle
+   * @param data the data to write (0-8 bytes)
+   * @param apiId the ID to write (0-1023 bits)
+   * @return Error status variable. 0 on success.
+   * @see "HAL_WriteCANPacket"
+   */
   public static native int writeCANPacketNoThrow(int handle, byte[] data, int apiId);
 
+  /**
+   * Writes a repeating packet to the CAN device with a specific ID without throwing on error.
+   *
+   * <p>This ID is 10 bits.
+   *
+   * <p>The RoboRIO will automatically repeat the packet at the specified interval
+   *
+   * @param handle the CAN handle
+   * @param data the data to write (0-8 bytes)
+   * @param apiId the ID to write (0-1023)
+   * @param repeatMs the period to repeat in ms
+   * @return Error status variable. 0 on success.
+   * @see "HAL_WriteCANPacketRepeating"
+   */
   public static native int writeCANPacketRepeatingNoThrow(
       int handle, byte[] data, int apiId, int repeatMs);
 
+  /**
+   * Writes an RTR frame of the specified length to the CAN device with the specific ID without
+   * throwing on error.
+   *
+   * <p>By spec, the length must be equal to the length sent by the other device, otherwise behavior
+   * is unspecified.
+   *
+   * @param handle the CAN handle
+   * @param length the length of data to request (0-8)
+   * @param apiId the ID to write (0-1023)
+   * @return Error status variable. 0 on success.
+   * @see "HAL_WriteCANRTRFrame"
+   */
   public static native int writeCANRTRFrameNoThrow(int handle, int length, int apiId);
 
+  /**
+   * Stops a repeating packet with a specific ID.
+   *
+   * <p>This ID is 10 bits.
+   *
+   * @param handle the CAN handle
+   * @param apiId the ID to stop repeating (0-1023)
+   * @see "HAL_StopCANPacketRepeating"
+   */
   public static native void stopCANPacketRepeating(int handle, int apiId);
 
+  /**
+   * Reads a new CAN packet.
+   *
+   * <p>This will only return properly once per packet received. Multiple calls without receiving
+   * another packet will return false.
+   *
+   * @param handle the CAN handle
+   * @param apiId the ID to read (0-1023)
+   * @param data the packet data (8 bytes)
+   * @return true on success, false on error
+   * @see "HAL_ReadCANPacketNew"
+   */
   public static native boolean readCANPacketNew(int handle, int apiId, CANData data);
 
+  /**
+   * Reads a CAN packet. The will continuously return the last packet received, without accounting
+   * for packet age.
+   *
+   * @param handle the CAN handle
+   * @param apiId the ID to read (0-1023)
+   * @param data the packet data (8 bytes)
+   * @return true on success, false on error
+   * @see "HAL_ReadCANPacketLatest"
+   */
   public static native boolean readCANPacketLatest(int handle, int apiId, CANData data);
 
+  /**
+   * Reads a CAN packet. The will return the last packet received until the packet is older then the
+   * requested timeout. Then it will return false.
+   *
+   * @param handle the CAN handle
+   * @param apiId the ID to read (0-1023)
+   * @param timeoutMs the timeout time for the packet
+   * @param data the packet data (8 bytes)
+   * @return true on success, false on error
+   * @see "HAL_ReadCANPacketTimeout"
+   */
   public static native boolean readCANPacketTimeout(
       int handle, int apiId, int timeoutMs, CANData data);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CANData.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CANData.java
index 0a644f6..0e29568 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CANData.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CANData.java
@@ -6,16 +6,21 @@
 
 @SuppressWarnings("MemberName")
 public class CANData {
+  /** Contents of the CAN frame. */
   public final byte[] data = new byte[8];
+
+  /** Length of the frame in bytes. */
   public int length;
+
+  /** CAN frame timestamp in milliseconds. */
   public long timestamp;
 
   /**
    * API used from JNI to set the data.
    *
    * @param length Length of packet in bytes.
-   * @param timestamp CAN frame timestamp in microseconds.
-   * @return Buffer containing CAN frame.
+   * @param timestamp CAN frame timestamp in milliseconds.
+   * @return Buffer to place CAN frame data in.
    */
   @SuppressWarnings("PMD.MethodReturnsInternalArray")
   public byte[] setData(int length, long timestamp) {
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java
index e94b183..b657da5 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java
@@ -4,48 +4,221 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * CTRE Pneumatic Control Module (PCM) Functions.
+ *
+ * @see "CTREPCM.h"
+ */
 public class CTREPCMJNI extends JNIWrapper {
+  /**
+   * Initializes a PCM.
+   *
+   * @param module the CAN ID to initialize
+   * @return the created PH handle
+   * @see "HAL_InitializeCTREPCM"
+   */
   public static native int initialize(int module);
 
+  /**
+   * Frees a PCM handle.
+   *
+   * @param handle the PCMhandle
+   * @see "HAL_FreeCTREPCM"
+   */
   public static native void free(int handle);
 
+  /**
+   * Checks if a solenoid channel number is valid.
+   *
+   * @param channel the channel to check
+   * @return true if the channel is valid, otherwise false
+   */
   public static native boolean checkSolenoidChannel(int channel);
 
+  /**
+   * Get whether compressor is turned on.
+   *
+   * @param handle the PCM handle
+   * @return true if the compressor is turned on
+   * @see "HAL_GetCTREPCMCompressor"
+   */
   public static native boolean getCompressor(int handle);
 
+  /**
+   * Enables the compressor closed loop control using the digital pressure switch. The compressor
+   * will turn on when the pressure switch indicates that the system is not full, and will turn off
+   * when the pressure switch indicates that the system is full.
+   *
+   * @param handle the PCM handle
+   * @param enabled true to enable closed loop control
+   * @see "HAL_SetCTREPCMClosedLoopControl"
+   */
   public static native void setClosedLoopControl(int handle, boolean enabled);
 
+  /**
+   * Get whether the PCM closed loop control is enabled.
+   *
+   * @param handle the PCM handle
+   * @return True if closed loop control is enabled, otherwise false.
+   */
   public static native boolean getClosedLoopControl(int handle);
 
+  /**
+   * Returns the state of the pressure switch.
+   *
+   * @param handle the PCM handle
+   * @return True if pressure switch indicates that the system is full, otherwise false.
+   * @see "HAL_GetCTREPCMPressureSwitch"
+   */
   public static native boolean getPressureSwitch(int handle);
 
+  /**
+   * Returns the current drawn by the compressor.
+   *
+   * @param handle the PCM handle
+   * @return The current drawn by the compressor in amps.
+   * @see "HAL_GetCTREPCMCompressorCurrent"
+   */
   public static native double getCompressorCurrent(int handle);
 
+  /**
+   * Return whether the compressor current is currently too high.
+   *
+   * @param handle the PCM handle
+   * @return True if the compressor current is too high, otherwise false.
+   * @see getCompressorCurrentTooHighStickyFault
+   * @see "HAL_GetCTREPCMCompressorCurrentTooHighFault"
+   */
   public static native boolean getCompressorCurrentTooHighFault(int handle);
 
+  /**
+   * Returns whether the compressor current has been too high since sticky faults were last cleared.
+   * This fault is persistent and can be cleared by clearAllStickyFaults()
+   *
+   * @param handle the PCM handle
+   * @return True if the compressor current has been too high since sticky faults were last cleared.
+   * @see getCompressorCurrentTooHighFault
+   * @see "HAL_GetCTREPCMCompressorCurrentTooHighStickyFault("
+   */
   public static native boolean getCompressorCurrentTooHighStickyFault(int handle);
 
+  /**
+   * Returns whether the compressor is currently shorted.
+   *
+   * @param handle the PCM handle
+   * @return True if the compressor is currently shorted, otherwise false.
+   * @see getCompressorCurrentTooHighStickyFault
+   * @see "HAL_GetCTREPCMCompressorShortedStickyFault"
+   */
   public static native boolean getCompressorShortedFault(int handle);
 
+  /**
+   * Returns whether the compressor has been shorted since sticky faults were last cleared. This
+   * fault is persistent and can be cleared by clearAllStickyFaults()
+   *
+   * @param handle the PCM handle
+   * @return True if the compressor has been shorted since sticky faults were last cleared,
+   *     otherwise false.
+   * @see getCompressorShortedFault
+   * @see "HAL_GetCTREPCMCompressorShortedFault"
+   */
   public static native boolean getCompressorShortedStickyFault(int handle);
 
+  /**
+   * Returns whether the compressor is currently disconnected.
+   *
+   * @param handle the PCM handle
+   * @return True if compressor is currently disconnected, otherwise false.
+   * @see getCompressorShortedStickyFault
+   * @see "HAL_GetCTREPCMCompressorNotConnectedFault"
+   */
   public static native boolean getCompressorNotConnectedFault(int handle);
 
+  /**
+   * Returns whether the compressor has been disconnected since sticky faults were last cleared.
+   * This fault is persistent and can be cleared by clearAllStickyFaults()
+   *
+   * @param handle the PCM handle
+   * @return True if the compressor has been disconnected since sticky faults were last cleared,
+   *     otherwise false.
+   * @see getCompressorNotConnectedFault
+   * @see "HAL_GetCTREPCMCompressorNotConnectedStickyFault"
+   */
   public static native boolean getCompressorNotConnectedStickyFault(int handle);
 
+  /**
+   * Gets a bitmask of solenoid values.
+   *
+   * @param handle the PCM handle
+   * @return solenoid values
+   * @see "HAL_GetCTREPCMSolenoids"
+   */
   public static native int getSolenoids(int handle);
 
+  /**
+   * Sets solenoids on a pneumatics module.
+   *
+   * @param handle the PCM handle
+   * @param mask bitmask to set
+   * @param values solenoid values
+   * @see "HAL_SetCTREPCMSolenoids"
+   */
   public static native void setSolenoids(int handle, int mask, int values);
 
+  /**
+   * Get a bitmask of disabled solenoids.
+   *
+   * @param handle the PCM handle
+   * @return bitmask of disabled solenoids
+   * @see "HAL_GetCTREPCMSolenoidDisabledList"
+   */
   public static native int getSolenoidDisabledList(int handle);
 
+  /**
+   * Returns whether the solenoid is currently reporting a voltage fault.
+   *
+   * @param handle the PCM handle
+   * @return True if solenoid is reporting a fault, otherwise false.
+   * @see getSolenoidVoltageStickyFault
+   * @see "HAL_GetCTREPCMSolenoidVoltageFault"
+   */
   public static native boolean getSolenoidVoltageFault(int handle);
 
+  /**
+   * Returns whether the solenoid has reported a voltage fault since sticky faults were last
+   * cleared. This fault is persistent and can be cleared by clearAllStickyFaults()
+   *
+   * @param handle the PCM handle
+   * @return True if solenoid is reporting a fault, otherwise false.
+   * @see getSolenoidVoltageFault
+   * @see "HAL_GetCTREPCMSolenoidVoltageStickyFault"
+   */
   public static native boolean getSolenoidVoltageStickyFault(int handle);
 
+  /**
+   * Clears all sticky faults on this device.
+   *
+   * @param handle the PCM handle
+   * @see "HAL_ClearAllCTREPCMStickyFaults"
+   */
   public static native void clearAllStickyFaults(int handle);
 
+  /**
+   * Fire a single solenoid shot.
+   *
+   * @param handle the PCM handle
+   * @param index solenoid index
+   * @see "HAL_FireCTREPCMOneShot"
+   */
   public static native void fireOneShot(int handle, int index);
 
+  /**
+   * Set the duration for a single solenoid shot.
+   *
+   * @param handle the PCM handle
+   * @param index solenoid index
+   * @param durMs shot duration in ms
+   * @see "HAL_SetCTREPCMOneShotDuration"
+   */
   public static native void setOneShotDuration(int handle, int index, int durMs);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/ConstantsJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/ConstantsJNI.java
index aba5b33..4843449 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/ConstantsJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/ConstantsJNI.java
@@ -4,6 +4,17 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Constants HAL JNI functions.
+ *
+ * @see "hal/Constants.h"
+ */
 public class ConstantsJNI extends JNIWrapper {
+  /**
+   * Gets the number of FPGA system clock ticks per microsecond.
+   *
+   * @return the number of clock ticks per microsecond
+   * @see "HAL_GetSystemClockTicksPerMicrosecond"
+   */
   public static native int getSystemClockTicksPerMicrosecond();
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java
index 8b95228..ebc4cff 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java
@@ -6,59 +6,277 @@
 
 import java.nio.IntBuffer;
 
+/**
+ * Counter HAL JNI functions.
+ *
+ * @see "hal/Counter.h"
+ */
 public class CounterJNI extends JNIWrapper {
   public static final int TWO_PULSE = 0;
   public static final int SEMI_PERIOD = 1;
   public static final int PULSE_LENGTH = 2;
   public static final int EXTERNAL_DIRECTION = 3;
 
+  /**
+   * Initializes a counter.
+   *
+   * @param mode the counter mode
+   * @param index the compressor index (output)
+   * @return the created handle
+   * @see "HAL_InitializeCounter"
+   */
   public static native int initializeCounter(int mode, IntBuffer index);
 
+  /**
+   * Frees a counter.
+   *
+   * @param counterHandle the counter handle
+   * @see "HAL_FreeCounter"
+   */
   public static native void freeCounter(int counterHandle);
 
+  /**
+   * Sets the average sample size of a counter.
+   *
+   * @param counterHandle the counter handle
+   * @param size the size of samples to average
+   * @see "HAL_SetCounterAverageSize"
+   */
   public static native void setCounterAverageSize(int counterHandle, int size);
 
+  /**
+   * Sets the source object that causes the counter to count up.
+   *
+   * @param counterHandle the counter handle
+   * @param digitalSourceHandle the digital source handle (either a HAL_AnalogTriggerHandle or a
+   *     HAL_DigitalHandle)
+   * @param analogTriggerType the analog trigger type if the source is an analog trigger
+   * @see "HAL_SetCounterUpSource"
+   */
   public static native void setCounterUpSource(
       int counterHandle, int digitalSourceHandle, int analogTriggerType);
 
+  /**
+   * Sets the up source to either detect rising edges or falling edges.
+   *
+   * <p>Note that both are allowed to be set true at the same time without issues.
+   *
+   * @param counterHandle the counter handle
+   * @param risingEdge true to trigger on rising
+   * @param fallingEdge true to trigger on falling
+   * @see "HAL_SetCounterUpSourceEdge"
+   */
   public static native void setCounterUpSourceEdge(
       int counterHandle, boolean risingEdge, boolean fallingEdge);
 
+  /**
+   * Disables the up counting source to the counter.
+   *
+   * @param counterHandle the counter handle
+   * @see "HAL_ClearCounterUpSource"
+   */
   public static native void clearCounterUpSource(int counterHandle);
 
+  /**
+   * Sets the source object that causes the counter to count down.
+   *
+   * @param counterHandle the counter handle
+   * @param digitalSourceHandle the digital source handle (either a HAL_AnalogTriggerHandle or a
+   *     HAL_DigitalHandle)
+   * @param analogTriggerType the analog trigger type if the source is an analog trigger
+   * @see "HAL_SetCounterDownSource"
+   */
   public static native void setCounterDownSource(
       int counterHandle, int digitalSourceHandle, int analogTriggerType);
 
+  /**
+   * Sets the down source to either detect rising edges or falling edges. Note that both are allowed
+   * to be set true at the same time without issues.
+   *
+   * @param counterHandle the counter handle
+   * @param risingEdge true to trigger on rising
+   * @param fallingEdge true to trigger on falling
+   * @see "HAL_SetCounterDownSourceEdge"
+   */
   public static native void setCounterDownSourceEdge(
       int counterHandle, boolean risingEdge, boolean fallingEdge);
 
+  /**
+   * Disables the down counting source to the counter.
+   *
+   * @param counterHandle the counter handle
+   * @see "HAL_ClearCounterDownSource"
+   */
   public static native void clearCounterDownSource(int counterHandle);
 
+  /**
+   * Sets standard up / down counting mode on this counter.
+   *
+   * <p>Up and down counts are sourced independently from two inputs.
+   *
+   * @param counterHandle the counter handle
+   * @see "HAL_SetCounterUpDownMode"
+   */
   public static native void setCounterUpDownMode(int counterHandle);
 
+  /**
+   * Sets directional counting mode on this counter.
+   *
+   * <p>The direction is determined by the B input, with counting happening with the A input.
+   *
+   * @param counterHandle the counter handle
+   * @see "HAL_SetCounterExternalDirectionMode"
+   */
   public static native void setCounterExternalDirectionMode(int counterHandle);
 
+  /**
+   * Sets Semi-period mode on this counter.
+   *
+   * <p>The counter counts up based on the time the input is triggered. High or Low depends on the
+   * highSemiPeriod parameter.
+   *
+   * @param counterHandle the counter handle
+   * @param highSemiPeriod true for counting when the input is high, false for low
+   * @see "HAL_SetCounterSemiPeriodMode"
+   */
   public static native void setCounterSemiPeriodMode(int counterHandle, boolean highSemiPeriod);
 
+  /**
+   * Configures the counter to count in up or down based on the length of the input pulse.
+   *
+   * <p>This mode is most useful for direction sensitive gear tooth sensors.
+   *
+   * @param counterHandle the counter handle
+   * @param threshold The pulse length beyond which the counter counts the opposite direction
+   *     (seconds)
+   * @see "HAL_SetCounterPulseLengthMode"
+   */
   public static native void setCounterPulseLengthMode(int counterHandle, double threshold);
 
+  /**
+   * Gets the Samples to Average which specifies the number of samples of the timer to average when
+   * calculating the period. Perform averaging to account for mechanical imperfections or as
+   * oversampling to increase resolution.
+   *
+   * @param counterHandle the counter handle
+   * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
+   * @see "HAL_GetCounterSamplesToAverage"
+   */
   public static native int getCounterSamplesToAverage(int counterHandle);
 
+  /**
+   * Sets the Samples to Average which specifies the number of samples of the timer to average when
+   * calculating the period. Perform averaging to account for mechanical imperfections or as
+   * oversampling to increase resolution.
+   *
+   * @param counterHandle the counter handle
+   * @param samplesToAverage The number of samples to average from 1 to 127
+   * @see "HAL_SetCounterSamplesToAverage"
+   */
   public static native void setCounterSamplesToAverage(int counterHandle, int samplesToAverage);
 
+  /**
+   * Resets the Counter to zero.
+   *
+   * <p>Sets the counter value to zero. This does not effect the running state of the counter, just
+   * sets the current value to zero.
+   *
+   * @param counterHandle the counter handle
+   * @see "HAL_ResetCounter"
+   */
   public static native void resetCounter(int counterHandle);
 
+  /**
+   * Reads the current counter value.
+   *
+   * <p>Reads the value at this instant. It may still be running, so it reflects the current value.
+   * Next time it is read, it might have a different value.
+   *
+   * @param counterHandle the counter handle
+   * @return the current counter value
+   * @see "HAL_GetCounter"
+   */
   public static native int getCounter(int counterHandle);
 
+  /**
+   * Gets the Period of the most recent count.
+   *
+   * <p>Returns the time interval of the most recent count. This can be used for velocity
+   * calculations to determine shaft speed.
+   *
+   * @param counterHandle the counter handle
+   * @return the period of the last two pulses in units of seconds
+   * @see "HAL_GetCounterPeriod"
+   */
   public static native double getCounterPeriod(int counterHandle);
 
+  /**
+   * Sets the maximum period where the device is still considered "moving".
+   *
+   * <p>Sets the maximum period where the device is considered moving. This value is used to
+   * determine the "stopped" state of the counter using the HAL_GetCounterStopped method.
+   *
+   * @param counterHandle the counter handle
+   * @param maxPeriod the maximum period where the counted device is considered moving in seconds
+   * @see "HAL_SetCounterMaxPeriod"
+   */
   public static native void setCounterMaxPeriod(int counterHandle, double maxPeriod);
 
+  /**
+   * Selects whether you want to continue updating the event timer output when there are no samples
+   * captured.
+   *
+   * <p>The output of the event timer has a buffer of periods that are averaged and posted to a
+   * register on the FPGA. When the timer detects that the event source has stopped (based on the
+   * MaxPeriod) the buffer of samples to be averaged is emptied.
+   *
+   * <p>If you enable the update when empty, you will be notified of the stopped source and the
+   * event time will report 0 samples.
+   *
+   * <p>If you disable update when empty, the most recent average will remain on the output until a
+   * new sample is acquired.
+   *
+   * <p>You will never see 0 samples output (except when there have been no events since an FPGA
+   * reset) and you will likely not see the stopped bit become true (since it is updated at the end
+   * of an average and there are no samples to average).
+   *
+   * @param counterHandle the counter handle
+   * @param enabled true to enable counter updating with no samples
+   * @see "HAL_SetCounterUpdateWhenEmpty"
+   */
   public static native void setCounterUpdateWhenEmpty(int counterHandle, boolean enabled);
 
+  /**
+   * Determines if the clock is stopped.
+   *
+   * <p>Determine if the clocked input is stopped based on the MaxPeriod value set using the
+   * SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the device (and counter) are
+   * assumed to be stopped and it returns true.
+   *
+   * @param counterHandle the counter handle
+   * @return true if the most recent counter period exceeds the MaxPeriod value set by SetMaxPeriod
+   * @see "HAL_GetCounterStopped"
+   */
   public static native boolean getCounterStopped(int counterHandle);
 
+  /**
+   * Gets the last direction the counter value changed.
+   *
+   * @param counterHandle the counter handle
+   * @return the last direction the counter value changed
+   * @see "HAL_GetCounterDirection"
+   */
   public static native boolean getCounterDirection(int counterHandle);
 
+  /**
+   * Sets the Counter to return reversed sensing on the direction.
+   *
+   * <p>This allows counters to change the direction they are counting in the case of 1X and 2X
+   * quadrature encoding only. Any other counter mode isn't supported.
+   *
+   * @param counterHandle the counter handle
+   * @param reverseDirection true if the value counted should be negated.
+   * @see "HAL_SetCounterReverseDirection"
+   */
   public static native void setCounterReverseDirection(int counterHandle, boolean reverseDirection);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java
index 689a95e..3330f4a 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java
@@ -4,42 +4,178 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Digital Input/Output (IO) JNI Functions.
+ *
+ * @see "hal/DIO.h"
+ */
 public class DIOJNI extends JNIWrapper {
+  /**
+   * Creates a new instance of a digital port.
+   *
+   * @param halPortHandle the port handle to create from
+   * @param input true for input, false for output
+   * @return the created digital handle
+   * @see "HAL_InitializeDIOPort"
+   */
   public static native int initializeDIOPort(int halPortHandle, boolean input);
 
+  /**
+   * Checks if a DIO channel is valid.
+   *
+   * @param channel the channel number to check
+   * @return true if the channel is valid, otherwise false
+   * @see "HAL_CheckDIOChannel"
+   */
   public static native boolean checkDIOChannel(int channel);
 
+  /**
+   * Frees a DIO port.
+   *
+   * @param dioPortHandle the DIO channel handle
+   * @see "HAL_FreeDIOPort"
+   */
   public static native void freeDIOPort(int dioPortHandle);
 
+  /**
+   * Indicates the DIO channel is used by a simulated device.
+   *
+   * @param handle the DIO channel handle
+   * @param device simulated device handle
+   * @see "HAL_SetDIOSimDevice"
+   */
   public static native void setDIOSimDevice(int handle, int device);
 
+  /**
+   * Writes a digital value to a DIO channel.
+   *
+   * @param dioPortHandle the digital port handle
+   * @param value the state to set the digital channel (if it is configured as an output)
+   * @see "HAL_SetDIO"
+   */
   public static native void setDIO(int dioPortHandle, boolean value);
 
+  /**
+   * Sets the direction of a DIO channel.
+   *
+   * @param dioPortHandle the digital port handle
+   * @param input true to set input, false for output
+   * @see "HAL_SetDIODirection"
+   */
   public static native void setDIODirection(int dioPortHandle, boolean input);
 
+  /**
+   * Reads a digital value from a DIO channel.
+   *
+   * @param dioPortHandle the digital port handle
+   * @return the state of the specified channel
+   * @see "HAL_GetDIO"
+   */
   public static native boolean getDIO(int dioPortHandle);
 
+  /**
+   * Reads the direction of a DIO channel.
+   *
+   * @param dioPortHandle the digital port handle
+   * @return true for input, false for output
+   * @see "HAL_GetDIODirection"
+   */
   public static native boolean getDIODirection(int dioPortHandle);
 
+  /**
+   * Generates a single digital pulse.
+   *
+   * <p>Write a pulse to the specified digital output channel. There can only be a single pulse
+   * going at any time.
+   *
+   * @param dioPortHandle the digital port handle
+   * @param pulseLengthSeconds the active length of the pulse (in seconds)
+   * @see "HAL_Pulse"
+   */
   public static native void pulse(int dioPortHandle, double pulseLengthSeconds);
 
+  /**
+   * Generates a single digital pulse on multiple channels.
+   *
+   * <p>Write a pulse to the channels enabled by the mask. There can only be a single pulse going at
+   * any time.
+   *
+   * @param channelMask the channel mask
+   * @param pulseLengthSeconds the active length of the pulse (in seconds)
+   * @see "HAL_PulseMultiple"
+   */
   public static native void pulseMultiple(long channelMask, double pulseLengthSeconds);
 
+  /**
+   * Checks a DIO line to see if it is currently generating a pulse.
+   *
+   * @param dioPortHandle the digital port handle
+   * @return true if a pulse is in progress, otherwise false
+   * @see "HAL_IsPulsing"
+   */
   public static native boolean isPulsing(int dioPortHandle);
 
+  /**
+   * Checks if any DIO line is currently generating a pulse.
+   *
+   * @return true if a pulse on some line is in progress
+   * @see "HAL_IsAnyPulsing"
+   */
   public static native boolean isAnyPulsing();
 
   public static native short getLoopTiming();
 
+  /**
+   * Allocates a DO PWM Generator.
+   *
+   * @return the allocated digital PWM handle
+   */
   public static native int allocateDigitalPWM();
 
+  /**
+   * Frees the resource associated with a DO PWM generator.
+   *
+   * @param pwmGenerator the digital PWM handle
+   * @see "HAL_FreeDigitalPWM"
+   */
   public static native void freeDigitalPWM(int pwmGenerator);
 
+  /**
+   * Changes the frequency of the DO PWM generator.
+   *
+   * <p>The valid range is from 0.6 Hz to 19 kHz.
+   *
+   * <p>The frequency resolution is logarithmic.
+   *
+   * @param rate the frequency to output all digital output PWM signals
+   * @see "HAL_SetDigitalPWMRate"
+   */
   public static native void setDigitalPWMRate(double rate);
 
+  /**
+   * Configures the duty-cycle of the PWM generator.
+   *
+   * @param pwmGenerator the digital PWM handle
+   * @param dutyCycle the percent duty cycle to output [0..1]
+   * @see "HAL_SetDigitalPWMDutyCycle"
+   */
   public static native void setDigitalPWMDutyCycle(int pwmGenerator, double dutyCycle);
 
+  /**
+   * Configures the digital PWM to be a PPS signal with specified duty cycle.
+   *
+   * @param pwmGenerator the digital PWM handle
+   * @param dutyCycle the percent duty cycle to output [0..1]
+   * @see "HAL_SetDigitalPWMPPS"
+   */
   public static native void setDigitalPWMPPS(int pwmGenerator, double dutyCycle);
 
+  /**
+   * Configures which DO channel the PWM signal is output on.
+   *
+   * @param pwmGenerator the digital PWM handle
+   * @param channel the channel to output on
+   * @see "HAL_SetDigitalPWMOutputChannel"
+   */
   public static native void setDigitalPWMOutputChannel(int pwmGenerator, int channel);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java
index 9a0cfeb..00000d2 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java
@@ -4,53 +4,245 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * DMA HAL JNI functions.
+ *
+ * @see "hal/DHA.h"
+ */
 public class DMAJNI extends JNIWrapper {
+  /**
+   * Initializes an object for performing DMA transfers.
+   *
+   * @return the created dma handle
+   * @see "HAL_InitializeDMA"
+   */
   public static native int initialize();
 
+  /**
+   * Frees a DMA object.
+   *
+   * @param handle the dma handle
+   * @see "HAL_FreeDMA"
+   */
   public static native void free(int handle);
 
+  /**
+   * Pauses or unpauses a DMA transfer.
+   *
+   * <p>This can only be called while DMA is running.
+   *
+   * @param handle the dma handle
+   * @param pause true to pause transfers, false to resume.
+   * @see "HAL_SetDMAPause"
+   */
   public static native void setPause(int handle, boolean pause);
 
+  /**
+   * Sets DMA transfers to occur at a specific timed interval.
+   *
+   * <p>This will remove any external triggers. Only timed or external is supported.
+   *
+   * <p>Only 1 timed period is supported.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param periodSeconds the period to trigger in seconds
+   * @see "HAL_SetDMATimedTrigger"
+   */
   public static native void setTimedTrigger(int handle, double periodSeconds);
 
+  /**
+   * Sets DMA transfers to occur at a specific timed interval in FPGA cycles.
+   *
+   * <p>This will remove any external triggers. Only timed or external is supported.
+   *
+   * <p>Only 1 timed period is supported
+   *
+   * <p>The FPGA currently runs at 40 MHz, but this can change.
+   * HAL_GetSystemClockTicksPerMicrosecond can be used to get a computable value for this.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param cycles the period to trigger in FPGA cycles
+   * @see "HAL_SetDMATimedTriggerCycles"
+   */
   public static native void setTimedTriggerCycles(int handle, int cycles);
 
+  /**
+   * Adds position data for an encoder to be collected by DMA.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param encoderHandle the encoder to add
+   * @see "HAL_AddDMAEncoder"
+   */
   public static native void addEncoder(int handle, int encoderHandle);
 
+  /**
+   * Adds timer data for an encoder to be collected by DMA.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param encoderHandle the encoder to add
+   * @see "HAL_AddDMAEncoderPeriod"
+   */
   public static native void addEncoderPeriod(int handle, int encoderHandle);
 
+  /**
+   * Adds position data for an counter to be collected by DMA.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param counterHandle the counter to add
+   * @see "HAL_AddDMACounter"
+   */
   public static native void addCounter(int handle, int counterHandle);
 
+  /**
+   * Adds timer data for an counter to be collected by DMA.
+   *
+   * @param handle the dma handle
+   * @param counterHandle the counter to add
+   * @see "HAL_AddDMACounterPeriod"
+   */
   public static native void addCounterPeriod(int handle, int counterHandle);
 
+  /**
+   * Adds a digital source to be collected by DMA.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param digitalSourceHandle the digital source to add
+   * @see "HAL_AddDMADigitalSource"
+   */
   public static native void addDigitalSource(int handle, int digitalSourceHandle);
 
+  /**
+   * Adds a duty cycle input to be collected by DMA.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param dutyCycleHandle the duty cycle input to add
+   * @see "HAL_AddDMADutyCycle"
+   */
   public static native void addDutyCycle(int handle, int dutyCycleHandle);
 
+  /**
+   * Adds an analog input to be collected by DMA.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param analogInputHandle the analog input to add
+   * @see "HAL_AddDMAAnalogInput"
+   */
   public static native void addAnalogInput(int handle, int analogInputHandle);
 
+  /**
+   * Adds averaged data of an analog input to be collected by DMA.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param analogInputHandle the analog input to add
+   * @see "HAL_AddDMAAveragedAnalogInput"
+   */
   public static native void addAveragedAnalogInput(int handle, int analogInputHandle);
 
+  /**
+   * Adds accumulator data of an analog input to be collected by DMA.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param analogInputHandle the analog input to add
+   * @see "HAL_AddDMAAnalogAccumulator"
+   */
   public static native void addAnalogAccumulator(int handle, int analogInputHandle);
 
+  /**
+   * Sets DMA transfers to occur on an external trigger.
+   *
+   * <p>This will remove any timed trigger set. Only timed or external is supported.
+   *
+   * <p>Up to 8 external triggers are currently supported.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @param digitalSourceHandle the digital source handle (either a HAL_AnalogTriggerHandle or a
+   *     HAL_DigitalHandle)
+   * @param analogTriggerType the analog trigger type if the source is an analog trigger
+   * @param rising true to trigger on rising edge
+   * @param falling true to trigger on falling edge
+   * @return the index of the trigger
+   * @see "HAL_SetDMAExternalTrigger"
+   */
   public static native int setExternalTrigger(
       int handle, int digitalSourceHandle, int analogTriggerType, boolean rising, boolean falling);
 
+  /**
+   * Clear all sensors from the DMA collection list.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @see "HAL_ClearDMASensors"
+   */
   public static native void clearSensors(int handle);
 
+  /**
+   * Clear all external triggers from the DMA trigger list.
+   *
+   * <p>This can only be called if DMA is not started.
+   *
+   * @param handle the dma handle
+   * @see "HAL_ClearDMAExternalTriggers"
+   */
   public static native void clearExternalTriggers(int handle);
 
+  /**
+   * Starts DMA Collection.
+   *
+   * @param handle the dma handle
+   * @param queueDepth the number of objects to be able to queue
+   * @see "HAL_StartDMA"
+   */
   public static native void startDMA(int handle, int queueDepth);
 
+  /**
+   * Stops DMA Collection.
+   *
+   * @param handle the dma handle
+   * @see "HAL_StopDMA"
+   */
   public static native void stopDMA(int handle);
 
-  // 0-21 channelOffsets
-  // 22: capture size
-  // 23: triggerChannels (bitflags)
-  // 24: remaining
-  // 25: read status
+  /**
+   * Reads a DMA sample from the queue.
+   *
+   * @param handle the dma handle
+   * @param timeoutSeconds the time to wait for data to be queued before timing out
+   * @param buffer the sample object to place data into
+   * @param sampleStore index 0-21 channelOffsets, index 22: capture size, index 23: triggerChannels
+   *     (bitflags), index 24: remaining, index 25: read status
+   * @return timestamp of the DMA Sample
+   */
   public static native long readDMA(
       int handle, double timeoutSeconds, int[] buffer, int[] sampleStore);
 
+  /**
+   * Get the sensor DMA sample.
+   *
+   * @param handle the dma handle
+   * @return The DMA sample
+   */
   public static native DMAJNISample.BaseStore getSensorReadData(int handle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DigitalGlitchFilterJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DigitalGlitchFilterJNI.java
index cf0a781..9a16984 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DigitalGlitchFilterJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DigitalGlitchFilterJNI.java
@@ -4,12 +4,59 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Digital Glitch Filter JNI functions.
+ *
+ * @see "hal/DIO.h"
+ */
 public class DigitalGlitchFilterJNI extends JNIWrapper {
+  /**
+   * Writes the filter index from the FPGA.
+   *
+   * <p>Set the filter index used to filter out short pulses.
+   *
+   * @param digitalPortHandle the digital port handle
+   * @param filterIndex the filter index (Must be in the range 0 - 3, where 0 means "none" and 1 - 3
+   *     means filter # filterIndex - 1)
+   * @see "HAL_SetFilterSelect"
+   */
   public static native void setFilterSelect(int digitalPortHandle, int filterIndex);
 
+  /**
+   * Reads the filter index from the FPGA.
+   *
+   * <p>Gets the filter index used to filter out short pulses.
+   *
+   * @param digitalPortHandle the digital port handle
+   * @return the filter index (Must be in the range 0 - 3, where 0 means "none" and 1 - 3 means
+   *     filter # filterIndex - 1)
+   * @see "HAL_GetFilterSelect"
+   */
   public static native int getFilterSelect(int digitalPortHandle);
 
+  /**
+   * Sets the filter period for the specified filter index.
+   *
+   * <p>Sets the filter period in FPGA cycles. Even though there are 2 different filter index
+   * domains (MXP vs HDR), ignore that distinction for now since it complicates the interface. That
+   * can be changed later.
+   *
+   * @param filterIndex the filter index, 0 - 2
+   * @param fpgaCycles the number of cycles that the signal must not transition to be counted as a
+   *     transition.
+   * @see "HAL_SetFilterPeriod"
+   */
   public static native void setFilterPeriod(int filterIndex, int fpgaCycles);
 
+  /**
+   * Gets the filter period for the specified filter index.
+   *
+   * <p>Gets the filter period in FPGA cycles. Even though there are 2 different filter index
+   * domains (MXP vs HDR), ignore that distinction for now since it complicates the interface.
+   *
+   * @param filterIndex the filter index, 0 - 2
+   * @return The number of FPGA cycles of the filter period.
+   * @see "HAL_GetFilterPeriod"
+   */
   public static native int getFilterPeriod(int filterIndex);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java
index f40a38c..4a46b52 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java
@@ -6,21 +6,88 @@
 
 import java.nio.ByteBuffer;
 
+/**
+ * Driver Station JNI Functions.
+ *
+ * @see "hal/DriverStation.h"
+ * @see "hal/FRCUsageReporting.h"
+ */
 public class DriverStationJNI extends JNIWrapper {
+  /**
+   * Sets the program starting flag in the DS.
+   *
+   * <p>This is what changes the DS to showing robot code ready.
+   *
+   * @see "HAL_ObserveUserProgramStarting"
+   */
   public static native void observeUserProgramStarting();
 
+  /**
+   * Sets the disabled flag in the DS.
+   *
+   * <p>This is used for the DS to ensure the robot is properly responding to its state request.
+   * Ensure this gets called about every 50ms, or the robot will be disabled by the DS.
+   *
+   * @see "HAL_ObserveUserProgramDisabled"
+   */
   public static native void observeUserProgramDisabled();
 
+  /**
+   * Sets the autonomous enabled flag in the DS.
+   *
+   * <p>This is used for the DS to ensure the robot is properly responding to its state request.
+   * Ensure this gets called about every 50ms, or the robot will be disabled by the DS.
+   *
+   * @see "HAL_ObserveUserProgramAutonomous"
+   */
   public static native void observeUserProgramAutonomous();
 
+  /**
+   * Sets the teleoperated enabled flag in the DS.
+   *
+   * <p>This is used for the DS to ensure the robot is properly responding to its state request.
+   * Ensure this gets called about every 50ms, or the robot will be disabled by the DS.
+   *
+   * @see "HAL_ObserveUserProgramTeleop"
+   */
   public static native void observeUserProgramTeleop();
 
+  /**
+   * Sets the test mode flag in the DS.
+   *
+   * <p>This is used for the DS to ensure the robot is properly responding to its state request.
+   * Ensure this gets called about every 50ms, or the robot will be disabled by the DS.
+   *
+   * @see "HAL_ObserveUserProgramTest"
+   */
   public static native void observeUserProgramTest();
 
+  /**
+   * Report the usage of a resource of interest.
+   *
+   * <p>Original signature: <code>uint32_t report(tResourceType, uint8_t, uint8_t, const
+   * char*)</code>
+   *
+   * @param resource one of the values in the tResourceType above.
+   * @param instanceNumber an index that identifies the resource instance.
+   * @see "HAL_Report"
+   */
   public static void report(int resource, int instanceNumber) {
     report(resource, instanceNumber, 0, "");
   }
 
+  /**
+   * Report the usage of a resource of interest.
+   *
+   * <p>Original signature: <code>uint32_t report(tResourceType, uint8_t, uint8_t, const
+   * char*)</code>
+   *
+   * @param resource one of the values in the tResourceType above.
+   * @param instanceNumber an index that identifies the resource instance.
+   * @param context an optional additional context number for some cases (such as module number).
+   *     Set to 0 to omit.
+   * @see "HAL_Report"
+   */
   public static void report(int resource, int instanceNumber, int context) {
     report(resource, instanceNumber, context, "");
   }
@@ -31,19 +98,36 @@
    * <p>Original signature: <code>uint32_t report(tResourceType, uint8_t, uint8_t, const
    * char*)</code>
    *
-   * @param resource one of the values in the tResourceType above (max value 51).
+   * @param resource one of the values in the tResourceType above.
    * @param instanceNumber an index that identifies the resource instance.
    * @param context an optional additional context number for some cases (such as module number).
    *     Set to 0 to omit.
    * @param feature a string to be included describing features in use on a specific resource.
    *     Setting the same resource more than once allows you to change the feature string.
-   * @return TODO
+   * @return the index of the added value in NetComm
+   * @see "HAL_Report"
    */
   public static native int report(int resource, int instanceNumber, int context, String feature);
 
+  /**
+   * Gets the current control word of the driver station.
+   *
+   * <p>The control word contains the robot state.
+   *
+   * @return the control word
+   * @see "HAL_GetControlWord"
+   * @see getControlWord for a version easier to parse
+   */
   public static native int nativeGetControlWord();
 
-  @SuppressWarnings("MissingJavadocMethod")
+  /**
+   * Gets the current control word of the driver station.
+   *
+   * <p>The control work contains the robot state.
+   *
+   * @param controlWord the ControlWord to update
+   * @see "HAL_GetControlWord"
+   */
   public static void getControlWord(ControlWord controlWord) {
     int word = nativeGetControlWord();
     controlWord.update(
@@ -55,18 +139,32 @@
         ((word >> 5) & 1) != 0);
   }
 
+  /**
+   * Gets the current alliance station ID.
+   *
+   * @return the alliance station ID int
+   * @see "HAL_GetAllianceStation"
+   */
   private static native int nativeGetAllianceStation();
 
-  public static final int kRed1AllianceStation = 0;
-  public static final int kRed2AllianceStation = 1;
-  public static final int kRed3AllianceStation = 2;
-  public static final int kBlue1AllianceStation = 3;
-  public static final int kBlue2AllianceStation = 4;
-  public static final int kBlue3AllianceStation = 5;
+  public static final int kUnknownAllianceStation = 0;
+  public static final int kRed1AllianceStation = 1;
+  public static final int kRed2AllianceStation = 2;
+  public static final int kRed3AllianceStation = 3;
+  public static final int kBlue1AllianceStation = 4;
+  public static final int kBlue2AllianceStation = 5;
+  public static final int kBlue3AllianceStation = 6;
 
-  @SuppressWarnings("MissingJavadocMethod")
+  /**
+   * Gets the current alliance station ID.
+   *
+   * @return the alliance station ID as AllianceStationID
+   * @see "HAL_GetAllianceStation"
+   */
   public static AllianceStationID getAllianceStation() {
     switch (nativeGetAllianceStation()) {
+      case kUnknownAllianceStation:
+        return AllianceStationID.Unknown;
       case kRed1AllianceStation:
         return AllianceStationID.Red1;
       case kRed2AllianceStation:
@@ -88,32 +186,156 @@
   public static final int kMaxJoystickPOVs = 12;
   public static final int kMaxJoysticks = 6;
 
+  /**
+   * Gets the axes of a specific joystick.
+   *
+   * @param joystickNum the joystick number
+   * @param axesArray the axes values
+   * @return number of joystick axes, or 0 for error
+   * @see "HAL_GetJoystickAxes"
+   */
   public static native int getJoystickAxes(byte joystickNum, float[] axesArray);
 
+  /**
+   * Gets the axes of a specific joystick.
+   *
+   * @param joystickNum the joystick number
+   * @param rawAxesArray the raw int axes values (0-255)
+   * @return number of joystick axes, or 0 for error
+   * @see "HAL_GetJoystickAxes"
+   */
   public static native int getJoystickAxesRaw(byte joystickNum, int[] rawAxesArray);
 
+  /**
+   * Gets the POVs of a specific joystick.
+   *
+   * @param joystickNum the joystick number
+   * @param povsArray the POV values
+   * @return number of POVs, or 0 for error
+   * @see "HAL_GetJoystickPOVs"
+   */
   public static native int getJoystickPOVs(byte joystickNum, short[] povsArray);
 
+  /**
+   * Gets the buttons of a specific joystick.
+   *
+   * @param joystickNum the joystick number
+   * @param count the count of buttons
+   * @return The joystick button values
+   * @see "HAL_GetJoystickButtons"
+   */
   public static native int getJoystickButtons(byte joystickNum, ByteBuffer count);
 
+  /**
+   * Get all joystick data.
+   *
+   * @param axesArray all joystick axes
+   * @param rawAxesArray all joystick axes as int
+   * @param povsArray all povs
+   * @param buttonsAndMetadata array of long joystick axes count, long joystick povs count, long
+   *     jostick buttons count, long joystick buttons values
+   * @see "HAL_GetAllJoystickData"
+   */
   public static native void getAllJoystickData(
       float[] axesArray, byte[] rawAxesArray, short[] povsArray, long[] buttonsAndMetadata);
 
+  /**
+   * Set joystick outputs.
+   *
+   * @param joystickNum the joystick number
+   * @param outputs bitmask of outputs, 1 for on 0 for off
+   * @param leftRumble the left rumble value (0-FFFF)
+   * @param rightRumble the right rumble value (0-FFFF)
+   * @return the error code, or 0 for success
+   * @see "HAL_SetJoystickOutputs"
+   */
   public static native int setJoystickOutputs(
       byte joystickNum, int outputs, short leftRumble, short rightRumble);
 
+  /**
+   * Gets whether a specific joystick is considered to be an XBox controller.
+   *
+   * @param joystickNum the joystick number
+   * @return 1 if xbox, 0 otherwise
+   * @see "HAL_GetJoystickIsXbox"
+   */
   public static native int getJoystickIsXbox(byte joystickNum);
 
+  /**
+   * Gets the type of joystick connected.
+   *
+   * <p>This is device specific, and different depending on what system input type the joystick
+   * uses.
+   *
+   * @param joystickNum the joystick number
+   * @return the enumerated joystick type
+   * @see "HAL_GetJoystickType"
+   */
   public static native int getJoystickType(byte joystickNum);
 
+  /**
+   * Gets the name of a joystick.
+   *
+   * <p>The returned array must be freed with HAL_FreeJoystickName.
+   *
+   * @param joystickNum the joystick number
+   * @return the joystick name
+   * @see "HAL_GetJoystickName"
+   */
   public static native String getJoystickName(byte joystickNum);
 
+  /**
+   * Gets the type of a specific joystick axis.
+   *
+   * <p>This is device specific, and different depending on what system input type the joystick
+   * uses.
+   *
+   * @param joystickNum the joystick number
+   * @param axis the axis number
+   * @return the enumerated axis type
+   * @see "HAL_GetJoystickAxisType"
+   */
   public static native int getJoystickAxisType(byte joystickNum, byte axis);
 
+  /**
+   * Returns the approximate match time.
+   *
+   * <p>The FMS does not send an official match time to the robots, but does send an approximate
+   * match time. The value will count down the time remaining in the current period (auto or
+   * teleop).
+   *
+   * <p>Warning: This is not an official time (so it cannot be used to dispute ref calls or
+   * guarantee that a function will trigger before the match ends).
+   *
+   * <p>The Practice Match function of the DS approximates the behavior seen on the field.
+   *
+   * @return time remaining in current match period (auto or teleop)
+   * @see "HAL_GetMatchTime"
+   */
   public static native double getMatchTime();
 
+  /**
+   * Gets info about a specific match.
+   *
+   * @param info the match info to populate
+   * @return the error code, or 0 for success
+   * @see "HAL_GetMatchInfo"
+   */
   public static native int getMatchInfo(MatchInfoData info);
 
+  /**
+   * Sends an error to the driver station.
+   *
+   * @param isError true for error, false for warning
+   * @param errorCode the error code
+   * @param isLVCode true for a LV error code, false for a standard error code
+   * @param details the details of the error
+   * @param location the file location of the error
+   * @param callStack the callstack of the error
+   * @param printMsg true to print the error message to stdout as well as to the DS
+   * @return the error code, or 0 for success
+   * @see "HAL_SendError"
+   */
   public static native int sendError(
       boolean isError,
       int errorCode,
@@ -123,14 +345,31 @@
       String callStack,
       boolean printMsg);
 
+  /**
+   * Sends a line to the driver station console.
+   *
+   * @param line the line to send
+   * @return the error code, or 0 for success
+   */
   public static native int sendConsoleLine(String line);
 
-  public static native void refreshDSData();
+  /**
+   * Refresh the DS control word.
+   *
+   * @return true if updated
+   * @see "HAL_RefreshDSData"
+   */
+  public static native boolean refreshDSData();
 
   public static native void provideNewDataEventHandle(int handle);
 
   public static native void removeNewDataEventHandle(int handle);
 
+  /**
+   * Gets if outputs are enabled by the control system.
+   *
+   * @return true if outputs are enabled
+   */
   public static native boolean getOutputsActive();
 
   private DriverStationJNI() {}
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java
index f2737d8..1e7dfa0 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java
@@ -4,18 +4,78 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * DutyCycle HAL JNI functions.
+ *
+ * @see "DutyCycle.h"
+ */
 public class DutyCycleJNI extends JNIWrapper {
+  /**
+   * Initialize a DutyCycle input.
+   *
+   * @param digitalSourceHandle the digital source to use (either a Digital Handle or a
+   *     AnalogTrigger Handle)
+   * @param analogTriggerType the analog trigger type of the source if it is an analog trigger
+   * @return the created duty cycle handle
+   * @see "HAL_InitializeDutyCycle"
+   */
   public static native int initialize(int digitalSourceHandle, int analogTriggerType);
 
+  /**
+   * Free a DutyCycle.
+   *
+   * @param handle the duty cycle handle
+   * @see "HAL_FreeDutyCycle"
+   */
   public static native void free(int handle);
 
+  /**
+   * Get the frequency of the duty cycle signal.
+   *
+   * @param handle the duty cycle handle
+   * @return frequency in Hertz
+   * @see "HAL_GetDutyCycleFrequency"
+   */
   public static native int getFrequency(int handle);
 
+  /**
+   * Get the output ratio of the duty cycle signal.
+   *
+   * <p>0 means always low, 1 means always high.
+   *
+   * @param handle the duty cycle handle
+   * @return output ratio between 0 and 1
+   * @see "HAL_GetDutyCycleOutput"
+   */
   public static native double getOutput(int handle);
 
+  /**
+   * Get the raw high time of the duty cycle signal.
+   *
+   * @param handle the duty cycle handle
+   * @return high time of last pulse in nanoseconds
+   * @see "HAL_GetDutyCycleHighTime"
+   */
   public static native int getHighTime(int handle);
 
+  /**
+   * Get the scale factor of the output.
+   *
+   * <p>An output equal to this value is always high, and then linearly scales down to 0. Divide a
+   * raw result by this in order to get the percentage between 0 and 1. Used by DMA.
+   *
+   * @param handle the duty cycle handle
+   * @return the output scale factor
+   * @see "HAL_GetDutyCycleOutputScaleFactor"
+   */
   public static native int getOutputScaleFactor(int handle);
 
+  /**
+   * Get the FPGA index for the DutyCycle.
+   *
+   * @param handle the duty cycle handle
+   * @return the FPGA index
+   * @see "HAL_GetDutyCycleFPGAIndex"
+   */
   public static native int getFPGAIndex(int handle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java
index 50ecc91..d219db0 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java
@@ -4,7 +4,24 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Encoder JNI Functions.
+ *
+ * @see "hal/Encoder.h"
+ */
 public class EncoderJNI extends JNIWrapper {
+  /**
+   * Initializes an encoder.
+   *
+   * @param digitalSourceHandleA the A source handle (either a digital or analog trigger)
+   * @param analogTriggerTypeA the analog trigger type of the A source if it is an analog trigger
+   * @param digitalSourceHandleB the B source handle (either a digital or analog trigger)
+   * @param analogTriggerTypeB the analog trigger type of the B source if it is an analog trigger
+   * @param reverseDirection true to reverse the counting direction from standard, otherwise false
+   * @param encodingType the encoding type
+   * @return the created encoder handle
+   * @see "HAL_InitializeEncoder"
+   */
   public static native int initializeEncoder(
       int digitalSourceHandleA,
       int analogTriggerTypeA,
@@ -13,50 +30,249 @@
       boolean reverseDirection,
       int encodingType);
 
+  /**
+   * Frees an encoder.
+   *
+   * @param encoderHandle the encoder handle
+   * @see "HAL_FreeEncoder"
+   */
   public static native void freeEncoder(int encoderHandle);
 
+  /**
+   * Indicates the encoder is used by a simulated device.
+   *
+   * @param handle the encoder handle
+   * @param device simulated device handle
+   * @see "HAL_SetEncoderSimDevice"
+   */
   public static native void setEncoderSimDevice(int handle, int device);
 
+  /**
+   * Gets the current counts of the encoder after encoding type scaling.
+   *
+   * <p>This is scaled by the value passed during initialization to encodingType.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the current scaled count
+   * @see "HAL_GetEncoder"
+   */
   public static native int getEncoder(int encoderHandle);
 
+  /**
+   * Gets the raw counts of the encoder.
+   *
+   * <p>This is not scaled by any values.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the raw encoder count
+   * @see "HAL_GetEncoderRaw"
+   */
   public static native int getEncoderRaw(int encoderHandle);
 
+  /**
+   * Gets the encoder scale value.
+   *
+   * <p>This is set by the value passed during initialization to encodingType.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the encoder scale value
+   * @see "HAL_GetEncoderEncodingScale"
+   */
   public static native int getEncodingScaleFactor(int encoderHandle);
 
+  /**
+   * Reads the current encoder value.
+   *
+   * <p>Read the value at this instant. It may still be running, so it reflects the current value.
+   * Next time it is read, it might have a different value.
+   *
+   * @param encoderHandle the encoder handle
+   * @see "HAL_ResetEncoder"
+   */
   public static native void resetEncoder(int encoderHandle);
 
+  /**
+   * Gets the Period of the most recent count.
+   *
+   * <p>Returns the time interval of the most recent count. This can be used for velocity
+   * calculations to determine shaft speed.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the period of the last two pulses in units of seconds
+   * @see "HAL_GetEncoderPeriod"
+   */
   public static native double getEncoderPeriod(int encoderHandle);
 
+  /**
+   * Sets the maximum period where the device is still considered "moving".
+   *
+   * <p>Sets the maximum period where the device is considered moving. This value is used to
+   * determine the "stopped" state of the encoder using the getEncoderStopped method.
+   *
+   * @param encoderHandle the encoder handle
+   * @param maxPeriod the maximum period where the counted device is considered moving in seconds
+   * @see "HAL_SetEncoderMaxPeriod"
+   */
   public static native void setEncoderMaxPeriod(int encoderHandle, double maxPeriod);
 
+  /**
+   * Determines if the clock is stopped.
+   *
+   * <p>Determines if the clocked input is stopped based on the MaxPeriod value set using the
+   * SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the device (and encoder) are
+   * assumed to be stopped and it returns true.
+   *
+   * @param encoderHandle the encoder handle
+   * @return true if the most recent encoder period exceeds the MaxPeriod value set by SetMaxPeriod
+   * @see "HAL_GetEncoderStopped"
+   */
   public static native boolean getEncoderStopped(int encoderHandle);
 
+  /**
+   * Gets the last direction the encoder value changed.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the last direction the encoder value changed
+   * @see "HAL_GetEncoderDirection"
+   */
   public static native boolean getEncoderDirection(int encoderHandle);
 
+  /**
+   * Gets the current distance traveled by the encoder.
+   *
+   * <p>This is the encoder count scaled by the distance per pulse set for the encoder.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the encoder distance (units are determined by the units passed to
+   *     setEncoderDistancePerPulse)
+   * @see "HAL_GetEncoderDistance"
+   */
   public static native double getEncoderDistance(int encoderHandle);
 
+  /**
+   * Gets the current rate of the encoder.
+   *
+   * <p>This is the encoder period scaled by the distance per pulse set for the encoder.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the encoder rate (units are determined by the units passed to
+   *     setEncoderDistancePerPulse, time value is seconds)
+   * @see "HAL_GetEncoderRate"
+   */
   public static native double getEncoderRate(int encoderHandle);
 
+  /**
+   * Sets the minimum rate to be considered moving by the encoder.
+   *
+   * <p>Units need to match what is set by setEncoderDistancePerPulse, with time as seconds.
+   *
+   * @param encoderHandle the encoder handle
+   * @param minRate the minimum rate to be considered moving (units are determined by the units
+   *     passed to setEncoderDistancePerPulse, time value is seconds)
+   * @see "HAL_SetEncoderMinRate"
+   */
   public static native void setEncoderMinRate(int encoderHandle, double minRate);
 
+  /**
+   * Sets the distance traveled per encoder pulse. This is used as a scaling factor for the rate and
+   * distance calls.
+   *
+   * @param encoderHandle the encoder handle
+   * @param distancePerPulse the distance traveled per encoder pulse (units user defined)
+   * @see "HAL_SetEncoderDistancePerPulse"
+   */
   public static native void setEncoderDistancePerPulse(int encoderHandle, double distancePerPulse);
 
+  /**
+   * Sets if to reverse the direction of the encoder.
+   *
+   * <p>Note that this is not a toggle. It is an absolute set.
+   *
+   * @param encoderHandle the encoder handle
+   * @param reverseDirection true to reverse the direction, false to not.
+   * @see "HAL_SetEncoderReverseDirection"
+   */
   public static native void setEncoderReverseDirection(int encoderHandle, boolean reverseDirection);
 
+  /**
+   * Sets the number of encoder samples to average when calculating encoder rate.
+   *
+   * @param encoderHandle the encoder handle
+   * @param samplesToAverage the number of samples to average
+   * @see "HAL_SetEncoderSamplesToAverage"
+   */
   public static native void setEncoderSamplesToAverage(int encoderHandle, int samplesToAverage);
 
+  /**
+   * Gets the current samples to average value.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the current samples to average value
+   * @see "HAL_GetEncoderSamplesToAverage"
+   */
   public static native int getEncoderSamplesToAverage(int encoderHandle);
 
+  /**
+   * Sets the source for an index pulse on the encoder.
+   *
+   * <p>The index pulse can be used to cause an encoder to reset based on an external input.
+   *
+   * @param encoderHandle the encoder handle
+   * @param digitalSourceHandle the index source handle (either a HAL_AnalogTriggerHandle or a
+   *     HAL_DigitalHandle)
+   * @param analogTriggerType the analog trigger type if the source is an analog trigger
+   * @param indexingType the index triggering type
+   * @see "HAL_SetEncoderIndexSource"
+   */
   public static native void setEncoderIndexSource(
       int encoderHandle, int digitalSourceHandle, int analogTriggerType, int indexingType);
 
+  /**
+   * Gets the FPGA index of the encoder.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the FPGA index of the encoder
+   * @see "HAL_GetEncoderFPGAIndex"
+   */
   public static native int getEncoderFPGAIndex(int encoderHandle);
 
+  /**
+   * Gets the encoder scale value.
+   *
+   * <p>This is set by the value passed during initialization to encodingType.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the encoder scale value
+   * @see "HAL_GetEncoderEncodingScale"
+   */
   public static native int getEncoderEncodingScale(int encoderHandle);
 
+  /**
+   * Gets the decoding scale factor of the encoder.
+   *
+   * <p>This is used to perform the scaling from raw to type scaled values.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the scale value for the encoder
+   * @see "HAL_GetEncoderDecodingScaleFactor"
+   */
   public static native double getEncoderDecodingScaleFactor(int encoderHandle);
 
+  /**
+   * Gets the user set distance per pulse of the encoder.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the set distance per pulse
+   * @see "HAL_GetEncoderDistancePerPulse"
+   */
   public static native double getEncoderDistancePerPulse(int encoderHandle);
 
+  /**
+   * Gets the encoding type of the encoder.
+   *
+   * @param encoderHandle the encoder handle
+   * @return the encoding type
+   * @see "HAL_GetEncoderEncodingType"
+   */
   public static native int getEncoderEncodingType(int encoderHandle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/HAL.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/HAL.java
index 68e6ec8..d9eb8d5 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/HAL.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/HAL.java
@@ -8,18 +8,71 @@
 import java.util.List;
 
 /**
- * JNI Wrapper for HAL<br>
- * .
+ * JNI Wrapper for Hardware Abstraction Layer (HAL).
+ *
+ * @see "hal/HALBase.h"
+ * @see "hal/Main.h"
+ * @see "hal/FRCUsageReporting.h"
  */
 public final class HAL extends JNIWrapper {
+  /**
+   * Call this to start up HAL. This is required for robot programs.
+   *
+   * <p>This must be called before any other HAL functions. Failure to do so will result in
+   * undefined behavior, and likely segmentation faults. This means that any statically initialized
+   * variables in a program MUST call this function in their constructors if they want to use other
+   * HAL calls.
+   *
+   * <p>The common parameters are 500 for timeout and 0 for mode.
+   *
+   * <p>This function is safe to call from any thread, and as many times as you wish. It internally
+   * guards from any reentrancy.
+   *
+   * <p>The applicable modes are: 0: Try to kill an existing HAL from another program, if not
+   * successful, error. 1: Force kill a HAL from another program. 2: Just warn if another hal exists
+   * and cannot be killed. Will likely result in undefined behavior.
+   *
+   * @param timeout the initialization timeout (ms)
+   * @param mode the initialization mode (see remarks)
+   * @return true if initialization was successful, otherwise false.
+   * @see "HAL_Initialize"
+   */
   public static native boolean initialize(int timeout, int mode);
 
+  /**
+   * Call this to shut down HAL.
+   *
+   * <p>This must be called at termination of the robot program to avoid potential segmentation
+   * faults with simulation extensions at exit.
+   *
+   * @see "HAL_Shutdown"
+   */
   public static native void shutdown();
 
+  /**
+   * Returns true if HAL_SetMain() has been called.
+   *
+   * @return True if HAL_SetMain() has been called, false otherwise.
+   * @see "HAL_HasMain"
+   */
   public static native boolean hasMain();
 
+  /**
+   * Runs the main function provided to HAL_SetMain().
+   *
+   * <p>If HAL_SetMain() has not been called, simply sleeps until exitMain() is called.
+   *
+   * @see "HAL_RunMain"
+   */
   public static native void runMain();
 
+  /**
+   * Causes HAL_RunMain() to exit.
+   *
+   * <p>If HAL_SetMain() has been called, this calls the exit function provided to that function.
+   *
+   * @see "HAL_ExitMain"
+   */
   public static native void exitMain();
 
   private static native void simPeriodicBeforeNative();
@@ -113,22 +166,108 @@
     }
   }
 
+  /**
+   * Gets if the system is in a browned out state.
+   *
+   * @return true if the system is in a low voltage brown out, false otherwise
+   * @see "HAL_GetBrownedOut"
+   */
   public static native boolean getBrownedOut();
 
+  /**
+   * Gets if the system outputs are currently active.
+   *
+   * @return true if the system outputs are active, false if disabled
+   * @see "HAL_GetSystemActive"
+   */
   public static native boolean getSystemActive();
 
+  /**
+   * Gets the current state of the Robot Signal Light (RSL).
+   *
+   * @return The current state of the RSL- true if on, false if off
+   * @see "HAL_GetRSLState"
+   */
+  public static native boolean getRSLState();
+
+  /**
+   * Gets if the system time is valid.
+   *
+   * @return True if the system time is valid, false otherwise
+   * @see "HAL_GetSystemTimeValid"
+   */
+  public static native boolean getSystemTimeValid();
+
+  /**
+   * Gets a port handle for a specific channel and module.
+   *
+   * <p>This is expected to be used for PCMs, as the roboRIO does not work with modules anymore.
+   *
+   * <p>The created handle does not need to be freed.
+   *
+   * @param module the module number
+   * @param channel the channel number
+   * @return the created port
+   * @see "HAL_GetPortWithModule"
+   */
   public static native int getPortWithModule(byte module, byte channel);
 
+  /**
+   * Gets a port handle for a specific channel.
+   *
+   * <p>The created handle does not need to be freed.
+   *
+   * @param channel the channel number
+   * @return the created port
+   * @see "HAL_GetPort"
+   */
   public static native int getPort(byte channel);
 
+  /**
+   * Report the usage of a resource of interest.
+   *
+   * <p>Original signature: <code>uint32_t report(tResourceType, uint8_t, uint8_t, const
+   * char*)</code>
+   *
+   * @param resource one of the values in the tResourceType above.
+   * @param instanceNumber an index that identifies the resource instance.
+   * @see "HAL_Report"
+   */
   public static void report(int resource, int instanceNumber) {
     report(resource, instanceNumber, 0, "");
   }
 
+  /**
+   * Report the usage of a resource of interest.
+   *
+   * <p>Original signature: <code>uint32_t report(tResourceType, uint8_t, uint8_t, const
+   * char*)</code>
+   *
+   * @param resource one of the values in the tResourceType above.
+   * @param instanceNumber an index that identifies the resource instance.
+   * @param context an optional additional context number for some cases (such as module number).
+   *     Set to 0 to omit.
+   * @see "HAL_Report"
+   */
   public static void report(int resource, int instanceNumber, int context) {
     report(resource, instanceNumber, context, "");
   }
 
+  /**
+   * Report the usage of a resource of interest.
+   *
+   * <p>Original signature: <code>uint32_t report(tResourceType, uint8_t, uint8_t, const
+   * char*)</code>
+   *
+   * @param resource one of the values in the tResourceType above.
+   * @param instanceNumber an index that identifies the resource instance.
+   * @param context an optional additional context number for some cases (such as module number).
+   *     Set to 0 to omit.
+   * @param feature a string to be included describing features in use on a specific resource.
+   *     Setting the same resource more than once allows you to change the feature string.
+   * @return the index of the added value in NetComm
+   * @see "HAL_Report"
+   */
   public static int report(int resource, int instanceNumber, int context, String feature) {
     return DriverStationJNI.report(resource, instanceNumber, context, feature);
   }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/HALUtil.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/HALUtil.java
index 7c0f41a..11d9b4d 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/HALUtil.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/HALUtil.java
@@ -4,6 +4,11 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Hardware Abstraction Layer (HAL) Utilities JNI Functions.
+ *
+ * @see "hal/HALBase.h"
+ */
 public final class HALUtil extends JNIWrapper {
   public static final int NULL_PARAMETER = -1005;
   public static final int SAMPLE_RATE_TOO_HIGH = 1001;
@@ -18,26 +23,108 @@
   public static final int RUNTIME_ROBORIO2 = 1;
   public static final int RUNTIME_SIMULATION = 2;
 
+  /**
+   * Returns the FPGA Version number.
+   *
+   * <p>For now, expect this to be competition year.
+   *
+   * @return FPGA Version number.
+   * @see "HAL_GetFPGAVersion"
+   */
   public static native short getFPGAVersion();
 
+  /**
+   * Returns the FPGA Revision number.
+   *
+   * <p>The format of the revision is 3 numbers. The 12 most significant bits are the Major
+   * Revision. the next 8 bits are the Minor Revision. The 12 least significant bits are the Build
+   * Number.
+   *
+   * @return FPGA Revision number.
+   * @see "HAL_GetFPGARevision"
+   */
   public static native int getFPGARevision();
 
+  /**
+   * Returns the roboRIO serial number.
+   *
+   * @return The roboRIO serial number.
+   * @see "HAL_GetSerialNumber"
+   */
   public static native String getSerialNumber();
 
+  /**
+   * Returns the comments from the roboRIO web interface.
+   *
+   * @return The comments string.
+   * @see "HAL_GetComments"
+   */
   public static native String getComments();
 
+  /**
+   * Returns the team number configured for the robot controller.
+   *
+   * @return team number, or 0 if not found.
+   * @see "HAL_GetTeamNumber"
+   */
+  public static native int getTeamNumber();
+
+  /**
+   * Reads the microsecond-resolution timer on the FPGA.
+   *
+   * @return The current time in microseconds according to the FPGA (since FPGA reset).
+   */
   public static native long getFPGATime();
 
+  /**
+   * Returns the runtime type of the HAL.
+   *
+   * @return HAL Runtime Type
+   * @see RUNTIME_ROBORIO
+   * @see RUNTIME_ROBORIO2
+   * @see RUNTIME_SIMULATION
+   * @see "HAL_GetRuntimeType"
+   */
   public static native int getHALRuntimeType();
 
+  /**
+   * Gets the state of the "USER" button on the roboRIO.
+   *
+   * @return true if the button is currently pressed down
+   * @see "HAL_GetFPGAButton"
+   */
   public static native boolean getFPGAButton();
 
+  /**
+   * Gets the error message for a specific status code.
+   *
+   * @param code the status code
+   * @return the error message for the code. This does not need to be freed.
+   * @see "HAL_GetErrorMessage"
+   */
   public static native String getHALErrorMessage(int code);
 
+  /**
+   * Get the last HAL error code.
+   *
+   * @return error code
+   */
   public static native int getHALErrno();
 
+  /**
+   * Returns the textual description of the system error code.
+   *
+   * @param errno errno to get description of
+   * @return description of errno
+   * @see "std:strerror"
+   */
   public static native String getHALstrerror(int errno);
 
+  /**
+   * Gets the error message for the last HAL error.
+   *
+   * @return the error message for the code.
+   */
   public static String getHALstrerror() {
     return getHALstrerror(getHALErrno());
   }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java
index 63f32a0..90e8ddb 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java
@@ -6,9 +6,38 @@
 
 import java.nio.ByteBuffer;
 
+/**
+ * I2C HAL JNI functions.
+ *
+ * @see "I2C.h"
+ */
 public class I2CJNI extends JNIWrapper {
+  /**
+   * Initializes the I2C port.
+   *
+   * <p>Opens the port if necessary and saves the handle. If opening the MXP port, also sets up the
+   * channel functions appropriately.
+   *
+   * @param port The port to open, 0 for the on-board, 1 for the MXP.
+   * @see "HAL_InitializeI2C"
+   */
   public static native void i2CInitialize(int port);
 
+  /**
+   * Generic I2C read/write transaction.
+   *
+   * <p>This is a lower-level interface to the I2C hardware giving you more control over each
+   * transaction.
+   *
+   * @param port The I2C port, 0 for the on-board, 1 for the MXP.
+   * @param address The address of the register on the device to be read/written.
+   * @param dataToSend Buffer of data to send as part of the transaction.
+   * @param sendSize Number of bytes to send as part of the transaction.
+   * @param dataReceived Buffer to read data into.
+   * @param receiveSize Number of bytes to read from the device.
+   * @return &gt;= 0 on success or -1 on transfer abort.
+   * @see "HAL_TransactionI2C"
+   */
   public static native int i2CTransaction(
       int port,
       byte address,
@@ -17,6 +46,21 @@
       ByteBuffer dataReceived,
       byte receiveSize);
 
+  /**
+   * Generic I2C read/write transaction.
+   *
+   * <p>This is a lower-level interface to the I2C hardware giving you more control over each
+   * transaction.
+   *
+   * @param port The I2C port, 0 for the on-board, 1 for the MXP.
+   * @param address The address of the register on the device to be read/written.
+   * @param dataToSend Buffer of data to send as part of the transaction.
+   * @param sendSize Number of bytes to send as part of the transaction.
+   * @param dataReceived Buffer to read data into.
+   * @param receiveSize Number of bytes to read from the device.
+   * @return &gt;= 0 on success or -1 on transfer abort.
+   * @see "HAL_TransactionI2C"
+   */
   public static native int i2CTransactionB(
       int port,
       byte address,
@@ -25,14 +69,70 @@
       byte[] dataReceived,
       byte receiveSize);
 
+  /**
+   * Executes a write transaction with the device.
+   *
+   * <p>Writes a single byte to a register on a device and wait until the transaction is complete.
+   *
+   * @param port The I2C port, 0 for the on-board, 1 for the MXP.
+   * @param address The address of the register on the device to be written.
+   * @param dataToSend The byte to write to the register on the device.
+   * @param sendSize Number of bytes to send.
+   * @return &gt;= 0 on success or -1 on transfer abort.
+   * @see "HAL_WriteI2C"
+   */
   public static native int i2CWrite(int port, byte address, ByteBuffer dataToSend, byte sendSize);
 
+  /**
+   * Executes a write transaction with the device.
+   *
+   * <p>Writes a single byte to a register on a device and wait until the transaction is complete.
+   *
+   * @param port The I2C port, 0 for the on-board, 1 for the MXP.
+   * @param address The address of the register on the device to be written.
+   * @param dataToSend The byte to write to the register on the device.
+   * @param sendSize Number of bytes to send.
+   * @return &gt;= 0 on success or -1 on transfer abort.
+   * @see "HAL_WriteI2C"
+   */
   public static native int i2CWriteB(int port, byte address, byte[] dataToSend, byte sendSize);
 
+  /**
+   * Executes a read transaction with the device.
+   *
+   * <p>Reads bytes from a device. Most I2C devices will auto-increment the register pointer
+   * internally allowing you to read consecutive registers on a device in a single transaction.
+   *
+   * @param port The I2C port, 0 for the on-board, 1 for the MXP.
+   * @param address The register to read first in the transaction.
+   * @param dataReceived A ByteBuffer to store the data read from the device.
+   * @param receiveSize The number of bytes to read in the transaction.
+   * @return &gt;= 0 on success or -1 on transfer abort.
+   * @see "HAL_ReadI2C"
+   */
   public static native int i2CRead(
       int port, byte address, ByteBuffer dataReceived, byte receiveSize);
 
+  /**
+   * Executes a read transaction with the device.
+   *
+   * <p>Reads bytes from a device. Most I2C devices will auto-increment the register pointer
+   * internally allowing you to read consecutive registers on a device in a single transaction.
+   *
+   * @param port The I2C port, 0 for the on-board, 1 for the MXP.
+   * @param address The register to read first in the transaction.
+   * @param dataReceived A byte array to store the data read from the device.
+   * @param receiveSize The number of bytes to read in the transaction.
+   * @return &gt;= 0 on success or -1 on transfer abort.
+   * @see "HAL_ReadI2C"
+   */
   public static native int i2CReadB(int port, byte address, byte[] dataReceived, byte receiveSize);
 
+  /**
+   * Closes an I2C port.
+   *
+   * @param port The I2C port, 0 for the on-board, 1 for the MXP.
+   * @see "HAL_CloseI2C"
+   */
   public static native void i2CClose(int port);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java
index 7dc2e6d..c605435 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java
@@ -4,28 +4,113 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Interrupt HAL JNI functions.
+ *
+ * @see "hal/Interrupts.h"
+ */
 public class InterruptJNI extends JNIWrapper {
   public static final int HalInvalidHandle = 0;
 
+  /**
+   * Initializes an interrupt.
+   *
+   * @return the created interrupt handle
+   * @see "HAL_InitializeInterrupts"
+   */
   public static native int initializeInterrupts();
 
+  /**
+   * Frees an interrupt.
+   *
+   * @param interruptHandle the interrupt handle
+   * @see "HAL_CleanInterrupts"
+   */
   public static native void cleanInterrupts(int interruptHandle);
 
+  /**
+   * Waits for the defined interrupt to occur.
+   *
+   * @param interruptHandle the interrupt handle
+   * @param timeout timeout in seconds
+   * @param ignorePrevious if true, ignore interrupts that happened before waitForInterrupt was
+   *     called
+   * @return the mask of interrupts that fired
+   * @see "HAL_WaitForInterrupt"
+   */
   public static native long waitForInterrupt(
       int interruptHandle, double timeout, boolean ignorePrevious);
 
+  /**
+   * Waits for any interrupt covered by the mask to occur.
+   *
+   * @param interruptHandle the interrupt handle to use for the context
+   * @param mask the mask of interrupts to wait for
+   * @param timeout timeout in seconds
+   * @param ignorePrevious if true, ignore interrupts that happened before waitForInterrupt was
+   *     called
+   * @return the mask of interrupts that fired
+   * @see "HAL_WaitForMultipleInterrupts"
+   */
   public static native long waitForMultipleInterrupts(
       int interruptHandle, long mask, double timeout, boolean ignorePrevious);
 
+  /**
+   * Returns the timestamp for the rising interrupt that occurred most recently.
+   *
+   * <p>This is in the same time domain as getFPGATime(). It only contains the bottom 32 bits of the
+   * timestamp. If your robot has been running for over 1 hour, you will need to fill in the upper
+   * 32 bits yourself.
+   *
+   * @param interruptHandle the interrupt handle
+   * @return timestamp in microseconds since FPGA Initialization
+   */
   public static native long readInterruptRisingTimestamp(int interruptHandle);
 
+  /**
+   * Returns the timestamp for the falling interrupt that occurred most recently.
+   *
+   * <p>This is in the same time domain as getFPGATime(). It only contains the bottom 32 bits of the
+   * timestamp. If your robot has been running for over 1 hour, you will need to fill in the upper
+   * 32 bits yourself.
+   *
+   * @param interruptHandle the interrupt handle
+   * @return timestamp in microseconds since FPGA Initialization
+   */
   public static native long readInterruptFallingTimestamp(int interruptHandle);
 
+  /**
+   * Requests interrupts on a specific digital source.
+   *
+   * @param interruptHandle the interrupt handle
+   * @param digitalSourceHandle the digital source handle (either a HAL_AnalogTriggerHandle or a
+   *     HAL_DigitalHandle)
+   * @param analogTriggerType the trigger type if the source is an AnalogTrigger
+   * @see "HAL_RequestInterrupts"
+   */
   public static native void requestInterrupts(
       int interruptHandle, int digitalSourceHandle, int analogTriggerType);
 
+  /**
+   * Sets the edges to trigger the interrupt on.
+   *
+   * <p>Note that both edges triggered is a valid configuration.
+   *
+   * @param interruptHandle the interrupt handle
+   * @param risingEdge true for triggering on rising edge
+   * @param fallingEdge true for triggering on falling edge
+   * @see "HAL_SetInterruptUpSourceEdge"
+   */
   public static native void setInterruptUpSourceEdge(
       int interruptHandle, boolean risingEdge, boolean fallingEdge);
 
+  /**
+   * Releases a waiting interrupt.
+   *
+   * <p>This will release both rising and falling waiters.
+   *
+   * @param interruptHandle the interrupt handle to release
+   * @see "HAL_ReleaseWaitingInterrupt"
+   */
   public static native void releaseWaitingInterrupt(int interruptHandle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java
index 648e8a2..5620f03 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java
@@ -9,22 +9,34 @@
  *
  * <p>This class is not meant for direct use by teams. Instead, the edu.wpi.first.wpilibj.Notifier
  * class, which corresponds to the C++ Notifier class, should be used.
+ *
+ * @see "hal/Notifier.h"
  */
 public class NotifierJNI extends JNIWrapper {
   /**
-   * Initializes the notifier.
+   * Initializes a notifier.
    *
-   * @return True on success.
+   * <p>A notifier is an FPGA controller timer that triggers at requested intervals based on the
+   * FPGA time. This can be used to make precise control loops.
+   *
+   * @return the created notifier
+   * @see "HAL_InitializeNotifier"
    */
   public static native int initializeNotifier();
 
   /**
    * Sets the HAL notifier thread priority.
    *
+   * <p>The HAL notifier thread is responsible for managing the FPGA's notifier interrupt and waking
+   * up user's Notifiers when it's their time to run. Giving the HAL notifier thread real-time
+   * priority helps ensure the user's real-time Notifiers, if any, are notified to run in a timely
+   * manner.
+   *
    * @param realTime Set to true to set a real-time priority, false for standard priority.
    * @param priority Priority to set the thread to. For real-time, this is 1-99 with 99 being
    *     highest. For non-real-time, this is forced to 0. See "man 7 sched" for more details.
    * @return True on success.
+   * @see "HAL_SetNotifierThreadPriority"
    */
   public static native boolean setHALThreadPriority(boolean realTime, int priority);
 
@@ -33,44 +45,60 @@
    *
    * @param notifierHandle Notifier handle.
    * @param name Notifier name.
+   * @see "HAL_SetNotifierName"
    */
   public static native void setNotifierName(int notifierHandle, String name);
 
   /**
-   * Wakes up the waiter with time=0. Note: after this function is called, all calls to
-   * waitForNotifierAlarm() will immediately start returning 0.
+   * Stops a notifier from running.
    *
-   * @param notifierHandle Notifier handle.
+   * <p>This will cause any call into waitForNotifierAlarm to return with time = 0.
+   *
+   * @param notifierHandle the notifier handle
+   * @see "HAL_StopNotifier"
    */
   public static native void stopNotifier(int notifierHandle);
 
   /**
-   * Deletes the notifier object when we are done with it.
+   * Cleans a notifier.
    *
-   * @param notifierHandle Notifier handle.
+   * <p>Note this also stops a notifier if it is already running.
+   *
+   * @param notifierHandle the notifier handle
+   * @see "HAL_CleanNotifier"
    */
   public static native void cleanNotifier(int notifierHandle);
 
   /**
-   * Sets the notifier to wake up the waiter at triggerTime microseconds.
+   * Updates the trigger time for a notifier.
    *
-   * @param notifierHandle Notifier handle.
-   * @param triggerTime Trigger time in microseconds.
+   * <p>Note that this time is an absolute time relative to getFPGATime()
+   *
+   * @param notifierHandle the notifier handle
+   * @param triggerTime the updated trigger time
+   * @see "HAL_UpdateNotifierAlarm"
    */
   public static native void updateNotifierAlarm(int notifierHandle, long triggerTime);
 
   /**
-   * Cancels any pending wakeups set by updateNotifierAlarm(). Does NOT wake up any waiters.
+   * Cancels the next notifier alarm.
    *
-   * @param notifierHandle Notifier handle.
+   * <p>This does not cause waitForNotifierAlarm to return.
+   *
+   * @param notifierHandle the notifier handle
+   * @see "HAL_CancelNotifierAlarm"
    */
   public static native void cancelNotifierAlarm(int notifierHandle);
 
   /**
-   * Block until woken up by an alarm (or stop).
+   * Waits for the next alarm for the specific notifier.
    *
-   * @param notifierHandle Notifier handle.
-   * @return Time when woken up.
+   * <p>This is a blocking call until either the time elapses or stopNotifier gets called. If the
+   * latter occurs, this function will return zero and any loops using this function should exit.
+   * Failing to do so can lead to use-after-frees.
+   *
+   * @param notifierHandle the notifier handle
+   * @return the FPGA time the notifier returned
    */
   public static native long waitForNotifierAlarm(int notifierHandle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PWMConfigDataResult.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PWMConfigDataResult.java
index ac3c8f94..0ec46dd 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PWMConfigDataResult.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PWMConfigDataResult.java
@@ -15,18 +15,18 @@
     this.min = min;
   }
 
-  /** The maximum PWM value. */
+  /** The maximum PWM value in microseconds. */
   public int max;
 
-  /** The deadband maximum PWM value. */
+  /** The deadband maximum PWM value in microseconds. */
   public int deadbandMax;
 
-  /** The center PWM value. */
+  /** The center PWM value in microseconds. */
   public int center;
 
-  /** The deadband minimum PWM value. */
+  /** The deadband minimum PWM value in microseconds. */
   public int deadbandMin;
 
-  /** The minimum PWM value. */
+  /** The minimum PWM value in microseconds. */
   public int min;
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java
index 1ed562c..dc2abc1 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java
@@ -11,7 +11,7 @@
 
   public static native void freePWMPort(int pwmPortHandle);
 
-  public static native void setPWMConfigRaw(
+  public static native void setPWMConfigMicroseconds(
       int pwmPortHandle,
       int maxPwm,
       int deadbandMaxPwm,
@@ -19,27 +19,19 @@
       int deadbandMinPwm,
       int minPwm);
 
-  public static native void setPWMConfig(
-      int pwmPortHandle,
-      double maxPwm,
-      double deadbandMaxPwm,
-      double centerPwm,
-      double deadbandMinPwm,
-      double minPwm);
-
-  public static native PWMConfigDataResult getPWMConfigRaw(int pwmPortHandle);
+  public static native PWMConfigDataResult getPWMConfigMicroseconds(int pwmPortHandle);
 
   public static native void setPWMEliminateDeadband(int pwmPortHandle, boolean eliminateDeadband);
 
   public static native boolean getPWMEliminateDeadband(int pwmPortHandle);
 
-  public static native void setPWMRaw(int pwmPortHandle, short value);
+  public static native void setPulseTimeMicroseconds(int pwmPortHandle, int microsecondPulseTime);
 
   public static native void setPWMSpeed(int pwmPortHandle, double speed);
 
   public static native void setPWMPosition(int pwmPortHandle, double position);
 
-  public static native short getPWMRaw(int pwmPortHandle);
+  public static native int getPulseTimeMicroseconds(int pwmPortHandle);
 
   public static native double getPWMSpeed(int pwmPortHandle);
 
@@ -49,5 +41,7 @@
 
   public static native void latchPWMZero(int pwmPortHandle);
 
+  public static native void setAlwaysHighMode(int pwmPortHandle);
+
   public static native void setPWMPeriodScale(int pwmPortHandle, int squelchMask);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java
index b4bd6cf..20f9b42 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java
@@ -4,48 +4,185 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Ports HAL JNI functions.
+ *
+ * @see "hal/Ports.h"
+ */
 public class PortsJNI extends JNIWrapper {
+  /**
+   * Gets the number of analog accumulators in the current system.
+   *
+   * @return the number of analog accumulators
+   * @see "HAL_GetNumAccumulators"
+   */
   public static native int getNumAccumulators();
 
+  /**
+   * Gets the number of analog triggers in the current system.
+   *
+   * @return the number of analog triggers
+   * @see "HAL_GetNumAnalogTriggers"
+   */
   public static native int getNumAnalogTriggers();
 
+  /**
+   * Gets the number of analog inputs in the current system.
+   *
+   * @return the number of analog inputs
+   * @see "HAL_GetNumAnalogInputs"
+   */
   public static native int getNumAnalogInputs();
 
+  /**
+   * Gets the number of analog outputs in the current system.
+   *
+   * @return the number of analog outputs
+   * @see "HAL_GetNumAnalogOutputs"
+   */
   public static native int getNumAnalogOutputs();
 
+  /**
+   * Gets the number of counters in the current system.
+   *
+   * @return the number of counters
+   * @see "HAL_GetNumCounters"
+   */
   public static native int getNumCounters();
 
+  /**
+   * Gets the number of digital headers in the current system.
+   *
+   * @return the number of digital headers
+   * @see "HAL_GetNumDigitalHeaders"
+   */
   public static native int getNumDigitalHeaders();
 
+  /**
+   * Gets the number of PWM headers in the current system.
+   *
+   * @return the number of PWM headers
+   * @see "HAL_GetNumPWMHeaders"
+   */
   public static native int getNumPWMHeaders();
 
+  /**
+   * Gets the number of digital channels in the current system.
+   *
+   * @return the number of digital channels
+   * @see "HAL_GetNumDigitalChannels"
+   */
   public static native int getNumDigitalChannels();
 
+  /**
+   * Gets the number of PWM channels in the current system.
+   *
+   * @return the number of PWM channels
+   * @see "HAL_GetNumPWMChannels"
+   */
   public static native int getNumPWMChannels();
 
+  /**
+   * Gets the number of digital IO PWM outputs in the current system.
+   *
+   * @return the number of digital IO PWM outputs
+   * @see "HAL_GetNumDigitalPWMOutputs"
+   */
   public static native int getNumDigitalPWMOutputs();
 
+  /**
+   * Gets the number of quadrature encoders in the current system.
+   *
+   * @return the number of quadrature encoders
+   * @see "HAL_GetNumEncoders"
+   */
   public static native int getNumEncoders();
 
+  /**
+   * Gets the number of interrupts in the current system.
+   *
+   * @return the number of interrupts
+   * @see "HAL_GetNumInterrupts"
+   */
   public static native int getNumInterrupts();
 
+  /**
+   * Gets the number of relay channels in the current system.
+   *
+   * @return the number of relay channels
+   * @see "HAL_GetNumRelayChannels"
+   */
   public static native int getNumRelayChannels();
 
+  /**
+   * Gets the number of relay headers in the current system.
+   *
+   * @return the number of relay headers
+   * @see "HAL_GetNumRelayHeaders"
+   */
   public static native int getNumRelayHeaders();
 
+  /**
+   * Gets the number of PCM modules in the current system.
+   *
+   * @return the number of PCM modules
+   * @see "HAL_GetNumCTREPCMModules"
+   */
   public static native int getNumCTREPCMModules();
 
+  /**
+   * Gets the number of solenoid channels in the current system.
+   *
+   * @return the number of solenoid channels
+   * @see "HAL_GetNumCTRESolenoidChannels"
+   */
   public static native int getNumCTRESolenoidChannels();
 
+  /**
+   * Gets the number of PDP modules in the current system.
+   *
+   * @return the number of PDP modules
+   * @see "HAL_GetNumCTREPDPModules"
+   */
   public static native int getNumCTREPDPModules();
 
+  /**
+   * Gets the number of PDP channels in the current system.
+   *
+   * @return the number of PDP channels
+   * @see "HAL_GetNumCTREPDPChannels"
+   */
   public static native int getNumCTREPDPChannels();
 
+  /**
+   * Gets the number of PDH modules in the current system.
+   *
+   * @return the number of PDH modules
+   * @see "HAL_GetNumREVPDHModules"
+   */
   public static native int getNumREVPDHModules();
 
+  /**
+   * Gets the number of PDH channels in the current system.
+   *
+   * @return the number of PDH channels
+   * @see "HAL_GetNumREVPDHChannels"
+   */
   public static native int getNumREVPDHChannels();
 
+  /**
+   * Gets the number of PH modules in the current system.
+   *
+   * @return the number of PH modules
+   * @see "HAL_GetNumREVPHModules"
+   */
   public static native int getNumREVPHModules();
 
+  /**
+   * Gets the number of PH channels in the current system.
+   *
+   * @return the number of PH channels
+   * @see "HAL_GetNumREVPHChannels"
+   */
   public static native int getNumREVPHChannels();
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java
index 8280f93..bc7060d 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java
@@ -4,69 +4,282 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Power Distribution JNI Functions.
+ *
+ * @see "hal/PowerDistribution.h"
+ */
 public class PowerDistributionJNI extends JNIWrapper {
   public static final int AUTOMATIC_TYPE = 0;
   public static final int CTRE_TYPE = 1;
   public static final int REV_TYPE = 2;
   public static final int DEFAULT_MODULE = -1;
 
+  /**
+   * Initializes a Power Distribution Panel.
+   *
+   * @param module the module number to initialize
+   * @param type the type of module to initialize
+   * @return the created PowerDistribution handle
+   * @see "HAL_InitializePowerDistribution"
+   */
   public static native int initialize(int module, int type);
 
+  /**
+   * Cleans a PowerDistribution module.
+   *
+   * @param handle the module handle
+   * @see "HAL_CleanPowerDistribution"
+   */
   public static native void free(int handle);
 
+  /**
+   * Gets the module number for a specific handle.
+   *
+   * @param handle the module handle
+   * @return the module number
+   * @see "HAL_GetPowerDistributionModuleNumber"
+   */
   public static native int getModuleNumber(int handle);
 
+  /**
+   * Checks if a PowerDistribution module is valid.
+   *
+   * @param module the module to check
+   * @param type the type of module
+   * @return true if the module is valid, otherwise false
+   * @see "HAL_CheckPowerDistributionModule"
+   */
   public static native boolean checkModule(int module, int type);
 
+  /**
+   * Checks if a PowerDistribution channel is valid.
+   *
+   * @param handle the module handle
+   * @param channel the channel to check
+   * @return true if the channel is valid, otherwise false
+   * @see "HAL_CheckPowerDistributionChannel"
+   */
   public static native boolean checkChannel(int handle, int channel);
 
+  /**
+   * Gets the type of PowerDistribution module.
+   *
+   * @param handle the module handle
+   * @return the type of module
+   * @see "HAL_GetPowerDistributionType"
+   */
   public static native int getType(int handle);
 
+  /**
+   * Gets the number of channels for this handle.
+   *
+   * @param handle the handle
+   * @return number of channels
+   * @see "HAL_GetPowerDistributionNumChannels"
+   */
   public static native int getNumChannels(int handle);
 
+  /**
+   * Gets the temperature of the PowerDistribution.
+   *
+   * @param handle the module handle
+   * @return the module temperature (celsius)
+   * @see "HAL_GetPowerDistributionTemperature"
+   */
   public static native double getTemperature(int handle);
 
+  /**
+   * Gets the PowerDistribution input voltage.
+   *
+   * @param handle the module handle
+   * @return the input voltage (volts)
+   * @see "HAL_GetPowerDistributionVoltage"
+   */
   public static native double getVoltage(int handle);
 
+  /**
+   * Gets the current of a specific PowerDistribution channel.
+   *
+   * @param handle the module handle
+   * @param channel the channel
+   * @return the channel current (amps)
+   * @see "HAL_GetPowerDistributionChannelCurrent"
+   */
   public static native double getChannelCurrent(int handle, int channel);
 
+  /**
+   * Gets the current of all channels on the PowerDistribution.
+   *
+   * <p>The array must be large enough to hold all channels.
+   *
+   * @param handle the module handle
+   * @param currents the currents
+   * @see "HAL_GetPowerDistributionAllChannelCurrents"
+   */
   public static native void getAllCurrents(int handle, double[] currents);
 
+  /**
+   * Gets the total current of the PowerDistribution.
+   *
+   * @param handle the module handle
+   * @return the total current (amps)
+   * @see "HAL_GetPowerDistributionTotalCurrent"
+   */
   public static native double getTotalCurrent(int handle);
 
+  /**
+   * Gets the total power of the PowerDistribution.
+   *
+   * @param handle the module handle
+   * @return the total power (watts)
+   * @see "HAL_GetPowerDistributionTotalPower"
+   */
   public static native double getTotalPower(int handle);
 
+  /**
+   * Gets the total energy of the PowerDistribution.
+   *
+   * @param handle the module handle
+   * @return the total energy (joules)
+   * @see "HAL_GetPowerDistributionTotalEnergy"
+   */
   public static native double getTotalEnergy(int handle);
 
+  /**
+   * Resets the PowerDistribution accumulated energy.
+   *
+   * @param handle the module handle
+   * @see "HAL_ClearPowerDistributionStickyFaults"
+   */
   public static native void resetTotalEnergy(int handle);
 
+  /**
+   * Clears any PowerDistribution sticky faults.
+   *
+   * @param handle the module handle
+   * @see "HAL_ClearPowerDistributionStickyFaults"
+   */
   public static native void clearStickyFaults(int handle);
 
+  /**
+   * Returns true if switchable channel is powered on.
+   *
+   * <p>This is a REV PDH-specific function. This function will no-op on CTRE PDP.
+   *
+   * @param handle the module handle
+   * @return the state of the switchable channel
+   * @see "HAL_GetPowerDistributionSwitchableChannel"
+   */
   public static native boolean getSwitchableChannel(int handle);
 
+  /**
+   * Power on/off switchable channel.
+   *
+   * <p>This is a REV PDH-specific function. This function will no-op on CTRE PDP.
+   *
+   * @param handle the module handle
+   * @param enabled true to turn on switchable channel
+   * @see "HAL_SetPowerDistributionSwitchableChannel"
+   */
   public static native void setSwitchableChannel(int handle, boolean enabled);
 
+  /**
+   * Gets the PowerDistribution input voltage without throwing any errors.
+   *
+   * @param handle the module handle
+   * @return the input voltage (volts)
+   * @see "HAL_GetPowerDistributionVoltage"
+   */
   public static native double getVoltageNoError(int handle);
 
+  /**
+   * Gets the current of a specific PowerDistribution channel without throwing any errors.
+   *
+   * @param handle the module handle
+   * @param channel the channel
+   * @return the channel current (amps)
+   * @see "HAL_GetPowerDistributionChannelCurrent"
+   */
   public static native double getChannelCurrentNoError(int handle, int channel);
 
+  /**
+   * Gets the total current of the PowerDistribution without throwing any errors.
+   *
+   * @param handle the module handle
+   * @return the total current (amps)
+   * @see "HAL_GetPowerDistributionTotalCurrent"
+   */
   public static native double getTotalCurrentNoError(int handle);
 
+  /**
+   * Returns true if switchable channel is powered on without throwing any errors.
+   *
+   * <p>This is a REV PDH-specific function. This function will no-op on CTRE PDP.
+   *
+   * @param handle the module handle
+   * @return the state of the switchable channel
+   * @see "HAL_GetPowerDistributionSwitchableChannel"
+   */
   public static native boolean getSwitchableChannelNoError(int handle);
 
+  /**
+   * Power on/off switchable channel without throwing any errors.
+   *
+   * <p>This is a REV PDH-specific function. This function will no-op on CTRE PDP.
+   *
+   * @param handle the module handle
+   * @param enabled true to turn on switchable channel
+   * @see "HAL_SetPowerDistributionSwitchableChannel"
+   */
   public static native void setSwitchableChannelNoError(int handle, boolean enabled);
 
+  /**
+   * Get the current faults of the PowerDistribution.
+   *
+   * @param handle the module handle
+   * @return the current faults
+   * @see "HAL_GetPowerDistributionFaults"
+   */
   public static native int getFaultsNative(int handle);
 
+  /**
+   * Get the current faults of the PowerDistribution.
+   *
+   * @param handle the module handle
+   * @return the current faults
+   * @see "HAL_GetPowerDistributionFaults"
+   */
   public static PowerDistributionFaults getFaults(int handle) {
     return new PowerDistributionFaults(getFaultsNative(handle));
   }
 
+  /**
+   * Gets the sticky faults of the PowerDistribution.
+   *
+   * @param handle the module handle
+   * @return the sticky faults
+   * @see "HAL_GetPowerDistributionStickyFaults"
+   */
   public static native int getStickyFaultsNative(int handle);
 
+  /**
+   * Gets the sticky faults of the PowerDistribution.
+   *
+   * @param handle the module handle
+   * @return the sticky faults
+   * @see "HAL_GetPowerDistributionStickyFaults"
+   */
   public static PowerDistributionStickyFaults getStickyFaults(int handle) {
     return new PowerDistributionStickyFaults(getStickyFaultsNative(handle));
   }
 
+  /**
+   * Get the version of the PowerDistribution.
+   *
+   * @param handle the module handle
+   * @return version
+   * @see "HAL_GetPowerDistributionVersion"
+   */
   public static native PowerDistributionVersion getVersion(int handle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PowerJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PowerJNI.java
index 0bea6ed..8dd9548 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PowerJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/PowerJNI.java
@@ -4,36 +4,167 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Power HAL JNI Functions.
+ *
+ * @see "Power.h"
+ */
 public class PowerJNI extends JNIWrapper {
+  /**
+   * Gets the roboRIO input voltage.
+   *
+   * @return the input voltage (volts)
+   * @see "HAL_GetVinVoltage"
+   */
   public static native double getVinVoltage();
 
+  /**
+   * Gets the roboRIO input current.
+   *
+   * @return the input current (amps)
+   * @see "HAL_GetVinCurrent"
+   */
   public static native double getVinCurrent();
 
+  /**
+   * Gets the 6V rail voltage.
+   *
+   * @return the 6V rail voltage (volts)
+   * @see "HAL_GetUserVoltage6V"
+   */
   public static native double getUserVoltage6V();
 
+  /**
+   * Gets the 6V rail current.
+   *
+   * @return the 6V rail current (amps)
+   * @see "HAL_GetUserCurrent6V"
+   */
   public static native double getUserCurrent6V();
 
+  /**
+   * Enables or disables the 6V rail.
+   *
+   * @param enabled whether the rail should be enabled
+   */
+  public static native void setUserEnabled6V(boolean enabled);
+
+  /**
+   * Gets the active state of the 6V rail.
+   *
+   * @return true if the rail is active, otherwise false
+   * @see "HAL_GetUserActive6V"
+   */
   public static native boolean getUserActive6V();
 
+  /**
+   * Gets the fault count for the 6V rail.
+   *
+   * @return the number of 6V fault counts
+   * @see "HAL_GetUserCurrentFaults6V"
+   */
   public static native int getUserCurrentFaults6V();
 
+  /**
+   * Gets the 5V rail voltage.
+   *
+   * @return the 5V rail voltage (volts)
+   * @see "HAL_GetUserVoltage5V"
+   */
   public static native double getUserVoltage5V();
 
+  /**
+   * Gets the 5V rail current.
+   *
+   * @return the 5V rail current (amps)
+   * @see "HAL_GetUserCurrent5V"
+   */
   public static native double getUserCurrent5V();
 
+  /**
+   * Enables or disables the 5V rail.
+   *
+   * @param enabled whether the rail should be enabled
+   */
+  public static native void setUserEnabled5V(boolean enabled);
+
+  /**
+   * Gets the active state of the 5V rail.
+   *
+   * @return true if the rail is active, otherwise false
+   * @see "HAL_GetUserActive5V"
+   */
   public static native boolean getUserActive5V();
 
+  /**
+   * Gets the fault count for the 5V rail.
+   *
+   * @return the number of 5V fault counts
+   * @see "HAL_GetUserCurrentFaults5V"
+   */
   public static native int getUserCurrentFaults5V();
 
+  /**
+   * Gets the 3V3 rail voltage.
+   *
+   * @return the 3V3 rail voltage (volts)
+   * @see "HAL_GetUserVoltage3V3"
+   */
   public static native double getUserVoltage3V3();
 
+  /**
+   * Gets the 3V3 rail current.
+   *
+   * @return the 3V3 rail current (amps)
+   * @see "HAL_GetUserCurrent3V3"
+   */
   public static native double getUserCurrent3V3();
 
+  /**
+   * Enables or disables the 3V3 rail.
+   *
+   * @param enabled whether the rail should be enabled
+   */
+  public static native void setUserEnabled3V3(boolean enabled);
+
+  /**
+   * Gets the active state of the 3V3 rail.
+   *
+   * @return true if the rail is active, otherwise false
+   * @see "HAL_GetUserActive3V3"
+   */
   public static native boolean getUserActive3V3();
 
+  /**
+   * Gets the fault count for the 3V3 rail.
+   *
+   * @return the number of 3V3 fault counts
+   * @see "HAL_GetUserCurrentFaults3V3"
+   */
   public static native int getUserCurrentFaults3V3();
 
+  /**
+   * Set the voltage the roboRIO will brownout and disable all outputs.
+   *
+   * <p>Note that this only does anything on the roboRIO 2. On the roboRIO it is a no-op.
+   *
+   * @param voltage The brownout voltage
+   * @see "HAL_SetBrownoutVoltage"
+   */
   public static native void setBrownoutVoltage(double voltage);
 
+  /**
+   * Get the current brownout voltage setting.
+   *
+   * @return The brownout voltage
+   * @see "HAL_GetBrownoutVoltage"
+   */
   public static native double getBrownoutVoltage();
+
+  /**
+   * Get the current CPU temperature in degrees Celsius.
+   *
+   * @return current CPU temperature in degrees Celsius
+   */
+  public static native double getCPUTemp();
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/REVPHFaults.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/REVPHFaults.java
index 3419810..23f0882 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/REVPHFaults.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/REVPHFaults.java
@@ -56,27 +56,27 @@
    * @param faults the fault bitfields
    */
   public REVPHFaults(int faults) {
-    Channel0Fault = (faults & 0x1) != 0;
-    Channel1Fault = (faults & 0x2) != 0;
-    Channel2Fault = (faults & 0x4) != 0;
-    Channel3Fault = (faults & 0x8) != 0;
-    Channel4Fault = (faults & 0x10) != 0;
-    Channel5Fault = (faults & 0x20) != 0;
-    Channel6Fault = (faults & 0x40) != 0;
-    Channel7Fault = (faults & 0x80) != 0;
-    Channel8Fault = (faults & 0x100) != 0;
-    Channel9Fault = (faults & 0x200) != 0;
-    Channel10Fault = (faults & 0x400) != 0;
-    Channel11Fault = (faults & 0x800) != 0;
-    Channel12Fault = (faults & 0x1000) != 0;
-    Channel13Fault = (faults & 0x2000) != 0;
-    Channel14Fault = (faults & 0x4000) != 0;
-    Channel15Fault = (faults & 0x8000) != 0;
-    CompressorOverCurrent = (faults & 0x8000) != 0;
-    CompressorOpen = (faults & 0x10000) != 0;
-    SolenoidOverCurrent = (faults & 0x20000) != 0;
-    Brownout = (faults & 0x40000) != 0;
-    CanWarning = (faults & 0x80000) != 0;
-    HardwareFault = (faults & 0x100000) != 0;
+    Channel0Fault = (faults & (1 << 0)) != 0;
+    Channel1Fault = (faults & (1 << 1)) != 0;
+    Channel2Fault = (faults & (1 << 2)) != 0;
+    Channel3Fault = (faults & (1 << 3)) != 0;
+    Channel4Fault = (faults & (1 << 4)) != 0;
+    Channel5Fault = (faults & (1 << 5)) != 0;
+    Channel6Fault = (faults & (1 << 6)) != 0;
+    Channel7Fault = (faults & (1 << 7)) != 0;
+    Channel8Fault = (faults & (1 << 8)) != 0;
+    Channel9Fault = (faults & (1 << 9)) != 0;
+    Channel10Fault = (faults & (1 << 10)) != 0;
+    Channel11Fault = (faults & (1 << 11)) != 0;
+    Channel12Fault = (faults & (1 << 12)) != 0;
+    Channel13Fault = (faults & (1 << 13)) != 0;
+    Channel14Fault = (faults & (1 << 14)) != 0;
+    Channel15Fault = (faults & (1 << 15)) != 0;
+    CompressorOverCurrent = (faults & (1 << 16)) != 0;
+    CompressorOpen = (faults & (1 << 17)) != 0;
+    SolenoidOverCurrent = (faults & (1 << 18)) != 0;
+    Brownout = (faults & (1 << 19)) != 0;
+    CanWarning = (faults & (1 << 20)) != 0;
+    HardwareFault = (faults & (1 << 21)) != 0;
   }
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java
index 44c67a8..b1935b2 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java
@@ -4,20 +4,64 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * REV Pneumatic Hub (PH) HAL JNI functions.
+ *
+ * @see "REVPH.h"
+ */
 public class REVPHJNI extends JNIWrapper {
   public static final int COMPRESSOR_CONFIG_TYPE_DISABLED = 0;
   public static final int COMPRESSOR_CONFIG_TYPE_DIGITAL = 1;
   public static final int COMPRESSOR_CONFIG_TYPE_ANALOG = 2;
   public static final int COMPRESSOR_CONFIG_TYPE_HYBRID = 3;
 
+  /**
+   * Initializes a PH.
+   *
+   * @param module the CAN ID to initialize
+   * @return the created PH handle
+   * @see "HAL_InitializeREVP"
+   */
   public static native int initialize(int module);
 
+  /**
+   * Frees a PH handle.
+   *
+   * @param handle the PH handle
+   * @see "HAL_FreeREVPH"
+   */
   public static native void free(int handle);
 
+  /**
+   * Checks if a solenoid channel number is valid.
+   *
+   * @param channel the channel to check
+   * @return true if the channel is valid, otherwise false
+   * @see "HAL_CheckREVPHSolenoidChannel"
+   */
   public static native boolean checkSolenoidChannel(int channel);
 
+  /**
+   * Get whether compressor is turned on.
+   *
+   * @param handle the PH handle
+   * @return true if the compressor is turned on
+   * @see "HAL_GetREVPHCompressor"
+   */
   public static native boolean getCompressor(int handle);
 
+  /**
+   * Send compressor configuration to the PH.
+   *
+   * @param handle the PH handle
+   * @param minAnalogVoltage The compressor will turn on when the analog pressure sensor voltage
+   *     drops below this value
+   * @param maxAnalogVoltage The compressor will turn off when the analog pressure sensor reaches
+   *     this value.
+   * @param forceDisable Disable Compressor
+   * @param useDigital use the digital pressure switch
+   * @see "HAL_SetREVPHCompressorConfig"
+   */
   public static native void setCompressorConfig(
       int handle,
       double minAnalogVoltage,
@@ -25,51 +69,221 @@
       boolean forceDisable,
       boolean useDigital);
 
+  /**
+   * Disable Compressor.
+   *
+   * @param handle the PH handle
+   * @see "HAL_SetREVPHClosedLoopControlDisabled"
+   */
   public static native void setClosedLoopControlDisabled(int handle);
 
+  /**
+   * Enables the compressor in digital mode using the digital pressure switch. The compressor will
+   * turn on when the pressure switch indicates that the system is not full, and will turn off when
+   * the pressure switch indicates that the system is full.
+   *
+   * @param handle the PH handle
+   * @see "HAL_SetREVPHClosedLoopControlDigital"
+   */
   public static native void setClosedLoopControlDigital(int handle);
 
+  /**
+   * Enables the compressor in analog mode. This mode uses an analog pressure sensor connected to
+   * analog channel 0 to cycle the compressor. The compressor will turn on when the pressure drops
+   * below minAnalogVoltage and will turn off when the pressure reaches maxAnalogVoltage. This mode
+   * is only supported by the REV PH with the REV Analog Pressure Sensor connected to analog channel
+   * 0.
+   *
+   * @param handle the PH handle
+   * @param minAnalogVoltage The compressor will turn on when the analog pressure sensor voltage
+   *     drops below this value
+   * @param maxAnalogVoltage The compressor will turn off when the analog pressure sensor reaches
+   *     this value.
+   * @see "HAL_SetREVPHClosedLoopControlAnalog"
+   */
   public static native void setClosedLoopControlAnalog(
       int handle, double minAnalogVoltage, double maxAnalogVoltage);
 
+  /**
+   * Enables the compressor in hybrid mode. This mode uses both a digital pressure switch and an
+   * analog pressure sensor connected to analog channel 0 to cycle the compressor.
+   *
+   * <p>The compressor will turn on when \a both:
+   *
+   * <p>- The digital pressure switch indicates the system is not full AND - The analog pressure
+   * sensor indicates that the pressure in the system is below the specified minimum pressure.
+   *
+   * <p>The compressor will turn off when \a either:
+   *
+   * <p>- The digital pressure switch is disconnected or indicates that the system is full OR - The
+   * pressure detected by the analog sensor is greater than the specified maximum pressure.
+   *
+   * @param handle the PH handle
+   * @param minAnalogVoltage The compressor will turn on when the analog pressure sensor voltage
+   *     drops below this value and the pressure switch indicates that the system is not full.
+   * @param maxAnalogVoltage The compressor will turn off when the analog pressure sensor reaches
+   *     this value or the pressure switch is disconnected or indicates that the system is full.
+   * @see "HAL_SetREVPHClosedLoopControlHybrid"
+   */
   public static native void setClosedLoopControlHybrid(
       int handle, double minAnalogVoltage, double maxAnalogVoltage);
 
+  /**
+   * Get compressor configuration from the PH.
+   *
+   * @param handle the PH handle
+   * @return compressor configuration
+   * @see "HAL_GetREVPHCompressorConfig"
+   */
   public static native int getCompressorConfig(int handle);
 
+  /**
+   * Returns the state of the digital pressure switch.
+   *
+   * @param handle the PH handle
+   * @return True if pressure switch indicates that the system is full, otherwise false.
+   * @see "HAL_GetREVPHPressureSwitch"
+   */
   public static native boolean getPressureSwitch(int handle);
 
+  /**
+   * Returns the raw voltage of the specified analog input channel.
+   *
+   * @param handle the PH handle
+   * @param channel The analog input channel to read voltage from.
+   * @return The voltage of the specified analog input channel in volts.
+   * @see "HAL_GetREVPHAnalogVoltage"
+   */
   public static native double getAnalogVoltage(int handle, int channel);
 
+  /**
+   * Returns the current drawn by the compressor.
+   *
+   * @param handle the PH handle
+   * @return The current drawn by the compressor in amps.
+   * @see "HAL_GetREVPHCompressorCurrent"
+   */
   public static native double getCompressorCurrent(int handle);
 
+  /**
+   * Gets a bitmask of solenoid values.
+   *
+   * @param handle the PH handle
+   * @return solenoid values
+   * @see "HAL_GetREVPHSolenoids"
+   */
   public static native int getSolenoids(int handle);
 
+  /**
+   * Sets solenoids on a PH.
+   *
+   * @param handle the PH handle
+   * @param mask bitmask to set
+   * @param values solenoid values
+   * @see "HAL_SetREVPHSolenoids"
+   */
   public static native void setSolenoids(int handle, int mask, int values);
 
+  /**
+   * Fire a single solenoid shot for the specified duration.
+   *
+   * @param handle the PH handle
+   * @param index solenoid index
+   * @param durMs shot duration in ms
+   * @see "HAL_FireREVPHOneShot"
+   */
   public static native void fireOneShot(int handle, int index, int durMs);
 
+  /**
+   * Clears the sticky faults.
+   *
+   * @param handle the PH handle
+   * @see "HAL_ClearREVPHStickyFaults"
+   */
   public static native void clearStickyFaults(int handle);
 
+  /**
+   * Returns the current input voltage for the PH.
+   *
+   * @param handle the PH handle
+   * @return The input voltage in volts.
+   * @see "HAL_GetREVPHVoltage"
+   */
   public static native double getInputVoltage(int handle);
 
+  /**
+   * Returns the current voltage of the regulated 5v supply.
+   *
+   * @param handle the PH handle
+   * @return The current voltage of the 5v supply in volts.
+   * @see "HAL_GetREVPH5VVoltage"
+   */
   public static native double get5VVoltage(int handle);
 
+  /**
+   * Returns the total current drawn by all solenoids.
+   *
+   * @param handle the PH handle
+   * @return Total current drawn by all solenoids in amps.
+   * @see "HAL_GetREVPHSolenoidCurrent"
+   */
   public static native double getSolenoidCurrent(int handle);
 
+  /**
+   * Returns the current voltage of the solenoid power supply.
+   *
+   * @param handle the PH handle
+   * @return The current voltage of the solenoid power supply in volts.
+   * @see "HAL_GetREVPHSolenoidVoltage"
+   */
   public static native double getSolenoidVoltage(int handle);
 
+  /**
+   * Returns the sticky faults currently active on this device.
+   *
+   * @param handle the PH handle
+   * @return The sticky faults.
+   * @see "HAL_GetREVPHStickyFaults"
+   */
   public static native int getStickyFaultsNative(int handle);
 
+  /**
+   * Returns the sticky faults currently active on this device.
+   *
+   * @param handle the PH handle
+   * @return The sticky faults.
+   * @see "HAL_GetREVPHStickyFaults"
+   */
   public static REVPHStickyFaults getStickyFaults(int handle) {
     return new REVPHStickyFaults(getStickyFaultsNative(handle));
   }
 
+  /**
+   * Returns the faults currently active on the PH.
+   *
+   * @param handle the PH handle
+   * @return The faults.
+   * @see "HAL_GetREVPHFaults"
+   */
   public static native int getFaultsNative(int handle);
 
+  /**
+   * Returns the faults currently active on the PH.
+   *
+   * @param handle the PH handle
+   * @return The faults.
+   * @see "HAL_GetREVPHFaults"
+   */
   public static REVPHFaults getFaults(int handle) {
     return new REVPHFaults(getFaultsNative(handle));
   }
 
+  /**
+   * Returns the hardware and firmware versions of the PH.
+   *
+   * @param handle the PH handle
+   * @return The hardware and firmware versions.
+   * @see "HAL_GetREVPHVersion"
+   */
   public static native REVPHVersion getVersion(int handle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/RelayJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/RelayJNI.java
index eee2854..10a39b4 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/RelayJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/RelayJNI.java
@@ -4,14 +4,57 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Relay Output HAL JNI Functions.
+ *
+ * @see "hal/Relay.h"
+ */
 public class RelayJNI extends DIOJNI {
+  /**
+   * Initializes a relay.
+   *
+   * <p>Note this call will only initialize either the forward or reverse port of the relay. If you
+   * need both, you will need to initialize 2 relays.
+   *
+   * @param halPortHandle the port handle to initialize
+   * @param forward true for the forward port, false for the reverse port
+   * @return the created relay handle
+   * @see "HAL_InitializeRelayPort"
+   */
   public static native int initializeRelayPort(int halPortHandle, boolean forward);
 
+  /**
+   * Frees a relay port.
+   *
+   * @param relayPortHandle the relay handle
+   * @see "HAL_FreeRelayPort"
+   */
   public static native void freeRelayPort(int relayPortHandle);
 
+  /**
+   * Checks if a relay channel is valid.
+   *
+   * @param channel the channel to check
+   * @return true if the channel is valid, otherwise false
+   * @see "HAL_CheckRelayChannel"
+   */
   public static native boolean checkRelayChannel(int channel);
 
+  /**
+   * Sets the state of a relay output.
+   *
+   * @param relayPortHandle the relay handle
+   * @param on true for on, false for off
+   * @see "HAL_SetRelay"
+   */
   public static native void setRelay(int relayPortHandle, boolean on);
 
+  /**
+   * Gets the current state of the relay channel.
+   *
+   * @param relayPortHandle the relay handle
+   * @return true for on, false for off
+   * @see "HAL_GetRelay"
+   */
   public static native boolean getRelay(int relayPortHandle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java
index 053f192..82b9fed 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java
@@ -6,6 +6,11 @@
 
 import java.nio.ByteBuffer;
 
+/**
+ * SPI HAL JNI functions.
+ *
+ * @see "SPI.h"
+ */
 public class SPIJNI extends JNIWrapper {
   public static final int INVALID_PORT = -1;
   public static final int ONBOARD_CS0_PORT = 0;
@@ -19,40 +24,198 @@
   public static final int SPI_MODE2 = 2;
   public static final int SPI_MODE3 = 3;
 
+  /**
+   * Initializes the SPI port. Opens the port if necessary and saves the handle.
+   *
+   * <p>If opening the MXP port, also sets up the channel functions appropriately.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS3, 4 for MXP
+   * @see "HAL_InitializeSPI"
+   */
   public static native void spiInitialize(int port);
 
+  /**
+   * Performs an SPI send/receive transaction.
+   *
+   * <p>This is a lower-level interface to the spi hardware giving you more control over each
+   * transaction.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @param dataToSend Buffer of data to send as part of the transaction.
+   * @param dataReceived Buffer to read data into.
+   * @param size Number of bytes to transfer. [0..7]
+   * @return Number of bytes transferred, -1 for error
+   * @see "HAL_TransactionSPI"
+   */
   public static native int spiTransaction(
       int port, ByteBuffer dataToSend, ByteBuffer dataReceived, byte size);
 
+  /**
+   * Performs an SPI send/receive transaction.
+   *
+   * <p>This is a lower-level interface to the spi hardware giving you more control over each
+   * transaction.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @param dataToSend Buffer of data to send as part of the transaction.
+   * @param dataReceived Buffer to read data into.
+   * @param size Number of bytes to transfer. [0..7]
+   * @return Number of bytes transferred, -1 for error
+   * @see "HAL_TransactionSPI"
+   */
   public static native int spiTransactionB(
       int port, byte[] dataToSend, byte[] dataReceived, byte size);
 
+  /**
+   * Executes a write transaction with the device.
+   *
+   * <p>Writes to a device and wait until the transaction is complete.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @param dataToSend The data to write to the register on the device.
+   * @param sendSize The number of bytes to be written
+   * @return The number of bytes written. -1 for an error
+   * @see "HAL_WriteSPI"
+   */
   public static native int spiWrite(int port, ByteBuffer dataToSend, byte sendSize);
 
+  /**
+   * Executes a write transaction with the device.
+   *
+   * <p>Writes to a device and wait until the transaction is complete.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @param dataToSend The data to write to the register on the device.
+   * @param sendSize The number of bytes to be written
+   * @return The number of bytes written. -1 for an error
+   * @see "HAL_WriteSPI"
+   */
   public static native int spiWriteB(int port, byte[] dataToSend, byte sendSize);
 
+  /**
+   * Executes a read from the device.
+   *
+   * <p>This method does not write any data out to the device.
+   *
+   * <p>Most spi devices will require a register address to be written before they begin returning
+   * data.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @param initiate initiates a transaction when true. Just reads when false.
+   * @param dataReceived A pointer to the array of bytes to store the data read from the device.
+   * @param size The number of bytes to read in the transaction. [1..7]
+   * @return Number of bytes read. -1 for error.
+   * @see "HAL_ReadSPI"
+   */
   public static native int spiRead(int port, boolean initiate, ByteBuffer dataReceived, byte size);
 
+  /**
+   * Executes a read from the device.
+   *
+   * <p>This method does not write any data out to the device.
+   *
+   * <p>Most spi devices will require a register address to be written before they begin returning
+   * data.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @param initiate initiates a transaction when true. Just reads when false.
+   * @param dataReceived A pointer to the array of bytes to store the data read from the device.
+   * @param size The number of bytes to read in the transaction. [1..7]
+   * @return Number of bytes read. -1 for error.
+   * @see "HAL_ReadSPI"
+   */
   public static native int spiReadB(int port, boolean initiate, byte[] dataReceived, byte size);
 
+  /**
+   * Closes the SPI port.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @see "HAL_CloseSPI"
+   */
   public static native void spiClose(int port);
 
+  /**
+   * Sets the clock speed for the SPI bus.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @param speed The speed in Hz (500KHz-10MHz)
+   * @see "HAL_SetSPISpeed"
+   */
   public static native void spiSetSpeed(int port, int speed);
 
+  /**
+   * Sets the SPI Mode.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @param mode The SPI mode to use
+   * @see "HAL_SetSPIMode"
+   */
   public static native void spiSetMode(int port, int mode);
 
+  /**
+   * Gets the SPI Mode.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @return The SPI mode currently set
+   * @see "HAL_GetSPIMode"
+   */
   public static native int spiGetMode(int port);
 
+  /**
+   * Sets the CS Active high for a SPI port.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @see "HAL_SetSPIChipSelectActiveHigh"
+   */
   public static native void spiSetChipSelectActiveHigh(int port);
 
+  /**
+   * Sets the CS Active low for a SPI port.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
+   * @see "HAL_SetSPIChipSelectActiveLow"
+   */
   public static native void spiSetChipSelectActiveLow(int port);
 
+  /**
+   * Initializes the SPI automatic accumulator.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @param bufferSize The accumulator buffer size.
+   * @see "HAL_InitSPIAuto"
+   */
   public static native void spiInitAuto(int port, int bufferSize);
 
+  /**
+   * Frees an SPI automatic accumulator.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @see "HAL_FreeSPIAuto"
+   */
   public static native void spiFreeAuto(int port);
 
+  /**
+   * Sets the period for automatic SPI accumulation.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @param period The accumulation period (seconds).
+   * @see "HAL_StartSPIAutoRate"
+   */
   public static native void spiStartAutoRate(int port, double period);
 
+  /**
+   * Starts the auto SPI accumulator on a specific trigger.
+   *
+   * <p>Note that triggering on both rising and falling edges is a valid configuration.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @param digitalSourceHandle The trigger source to use (Either HAL_AnalogTriggerHandle or
+   *     HAL_DigitalHandle).
+   * @param analogTriggerType The analog trigger type, if the source is an analog trigger.
+   * @param triggerRising Trigger on the rising edge if true.
+   * @param triggerFalling Trigger on the falling edge if true.
+   * @see "HAL_StartSPIAutoTrigger"
+   */
   public static native void spiStartAutoTrigger(
       int port,
       int digitalSourceHandle,
@@ -60,20 +223,83 @@
       boolean triggerRising,
       boolean triggerFalling);
 
+  /**
+   * Stops an automatic SPI accumulation.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @see "HAL_StopSPIAuto"
+   */
   public static native void spiStopAuto(int port);
 
+  /**
+   * Sets the data to be transmitted to the device to initiate a read.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @param dataToSend Pointer to the data to send (Gets copied for continue use, so no need to keep
+   *     alive).
+   * @param zeroSize The number of zeros to send after the data.
+   * @see "HAL_SetSPIAutoTransmitData"
+   */
   public static native void spiSetAutoTransmitData(int port, byte[] dataToSend, int zeroSize);
 
+  /**
+   * Immediately forces an SPI read to happen.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @see "HAL_ForceSPIAutoRead"
+   */
   public static native void spiForceAutoRead(int port);
 
+  /**
+   * Reads data received by the SPI accumulator. Each received data sequence consists of a timestamp
+   * followed by the received data bytes, one byte per word (in the least significant byte). The
+   * length of each received data sequence is the same as the combined dataSize + zeroSize set in
+   * spiSetAutoTransmitData.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @param buffer The buffer to store the data into.
+   * @param numToRead The number of words to read.
+   * @param timeout The read timeout (in seconds).
+   * @return The number of words actually read.
+   * @see "HAL_ReadSPIAutoReceivedData"
+   */
   public static native int spiReadAutoReceivedData(
       int port, ByteBuffer buffer, int numToRead, double timeout);
 
+  /**
+   * Reads data received by the SPI accumulator. Each received data sequence consists of a timestamp
+   * followed by the received data bytes, one byte per word (in the least significant byte). The
+   * length of each received data sequence is the same as the combined dataSize + zeroSize set in
+   * spiSetAutoTransmitData.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @param buffer The buffer to store the data into.
+   * @param numToRead The number of words to read.
+   * @param timeout The read timeout (in seconds).
+   * @return The number of words actually read.
+   * @see "HAL_ReadSPIAutoReceivedData"
+   */
   public static native int spiReadAutoReceivedData(
       int port, int[] buffer, int numToRead, double timeout);
 
+  /**
+   * Gets the count of how many SPI accumulations have been missed.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @return The number of missed accumulations.
+   * @see "HAL_GetSPIAutoDroppedCount"
+   */
   public static native int spiGetAutoDroppedCount(int port);
 
+  /**
+   * Configure the Auto SPI Stall time between reads.
+   *
+   * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
+   * @param csToSclkTicks the number of ticks to wait before asserting the cs pin
+   * @param stallTicks the number of ticks to stall for
+   * @param pow2BytesPerRead the number of bytes to read before stalling
+   * @see "HAL_ConfigureSPIAutoStall"
+   */
   public static native void spiConfigureAutoStall(
       int port, int csToSclkTicks, int stallTicks, int pow2BytesPerRead);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SerialPortJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SerialPortJNI.java
index ee9464e..e156f3a 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SerialPortJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SerialPortJNI.java
@@ -4,42 +4,205 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Serial Port JNI HAL functions.
+ *
+ * @see "SerialPort.h"
+ */
 public class SerialPortJNI extends JNIWrapper {
+  /**
+   * Initializes a serial port.
+   *
+   * <p>The channels are either the onboard RS232, the MXP UART, or 2 USB ports. The top port is
+   * USB1, the bottom port is USB2.
+   *
+   * @param port the serial port to initialize
+   * @return Serial Port Handle
+   * @see "HAL_InitializeSerialPort"
+   */
   public static native int serialInitializePort(byte port);
 
+  /**
+   * Initializes a serial port with a direct name.
+   *
+   * <p>This name is the /dev name for a specific port. Note these are not always consistent between
+   * roboRIO reboots.
+   *
+   * @param port the serial port to initialize
+   * @param portName the dev port name
+   * @return Serial Port Handle
+   * @see "HAL_InitializeSerialPortDirect"
+   */
   public static native int serialInitializePortDirect(byte port, String portName);
 
+  /**
+   * Sets the baud rate of a serial port.
+   *
+   * <p>Any value between 0 and 0xFFFFFFFF may be used. Default is 9600.
+   *
+   * @param handle the serial port handle
+   * @param baud the baud rate to set
+   * @see "HAL_SetSerialBaudRate"
+   */
   public static native void serialSetBaudRate(int handle, int baud);
 
+  /**
+   * Sets the number of data bits on a serial port.
+   *
+   * <p>Defaults to 8.
+   *
+   * @param handle the serial port handle
+   * @param bits the number of data bits (5-8)
+   * @see "HAL_SetSerialDataBits"
+   */
   public static native void serialSetDataBits(int handle, byte bits);
 
+  /**
+   * Sets the number of parity bits on a serial port.
+   *
+   * <p>Valid values are: 0: None (default) 1: Odd 2: Even 3: Mark - Means exists and always 1 4:
+   * Space - Means exists and always 0
+   *
+   * @param handle the serial port handle
+   * @param parity the parity bit mode (see remarks for valid values)
+   * @see "HAL_SetSerialParity"
+   */
   public static native void serialSetParity(int handle, byte parity);
 
+  /**
+   * Sets the number of stop bits on a serial port.
+   *
+   * <p>Valid values are: 10: One stop bit (default) 15: One and a half stop bits 20: Two stop bits
+   *
+   * @param handle the serial port handle
+   * @param stopBits the stop bit value (see remarks for valid values)
+   * @see "HAL_SetSerialStopBits"
+   */
   public static native void serialSetStopBits(int handle, byte stopBits);
 
+  /**
+   * Sets the write mode on a serial port.
+   *
+   * <p>Valid values are: 1: Flush on access 2: Flush when full (default)
+   *
+   * @param handle the serial port handle
+   * @param mode the mode to set (see remarks for valid values)
+   * @see "HAL_SetSerialWriteMode"
+   */
   public static native void serialSetWriteMode(int handle, byte mode);
 
+  /**
+   * Sets the flow control mode of a serial port.
+   *
+   * <p>Valid values are: 0: None (default) 1: XON-XOFF 2: RTS-CTS 3: DTR-DSR
+   *
+   * @param handle the serial port handle
+   * @param flow the mode to set (see remarks for valid values)
+   * @see "HAL_SetSerialFlowControl"
+   */
   public static native void serialSetFlowControl(int handle, byte flow);
 
+  /**
+   * Sets the minimum serial read timeout of a port.
+   *
+   * @param handle the serial port handle
+   * @param timeout the timeout in milliseconds
+   * @see "HAL_SetSerialTimeout"
+   */
   public static native void serialSetTimeout(int handle, double timeout);
 
+  /**
+   * Sets the termination character that terminates a read.
+   *
+   * <p>By default this is disabled.
+   *
+   * @param handle the serial port handle
+   * @param terminator the termination character to set
+   * @see "HAL_EnableSerialTermination"
+   */
   public static native void serialEnableTermination(int handle, char terminator);
 
+  /**
+   * Disables a termination character for reads.
+   *
+   * @param handle the serial port handle
+   * @see "HAL_DisableSerialTermination"
+   */
   public static native void serialDisableTermination(int handle);
 
+  /**
+   * Sets the size of the read buffer.
+   *
+   * @param handle the serial port handle
+   * @param size the read buffer size
+   * @see "HAL_SetSerialReadBufferSize"
+   */
   public static native void serialSetReadBufferSize(int handle, int size);
 
+  /**
+   * Sets the size of the write buffer.
+   *
+   * @param handle the serial port handle
+   * @param size the write buffer size
+   * @see "HAL_SetSerialWriteBufferSize"
+   */
   public static native void serialSetWriteBufferSize(int handle, int size);
 
+  /**
+   * Gets the number of bytes currently in the read buffer.
+   *
+   * @param handle the serial port handle
+   * @return the number of bytes in the read buffer
+   * @see "HAL_GetSerialBytesReceived"
+   */
   public static native int serialGetBytesReceived(int handle);
 
+  /**
+   * Reads data from the serial port.
+   *
+   * <p>Will wait for either timeout (if set), the termination char (if set), or the count to be
+   * full. Whichever one comes first.
+   *
+   * @param handle the serial port handle
+   * @param buffer the buffer in which to store bytes read
+   * @param count the number of bytes maximum to read
+   * @return the number of bytes actually read
+   * @see "HAL_ReadSerial"
+   */
   public static native int serialRead(int handle, byte[] buffer, int count);
 
+  /**
+   * Writes data to the serial port.
+   *
+   * @param handle the serial port handle
+   * @param buffer the buffer to write
+   * @param count the number of bytes to write from the buffer
+   * @return the number of bytes actually written
+   * @see "HAL_WriteSerial"
+   */
   public static native int serialWrite(int handle, byte[] buffer, int count);
 
+  /**
+   * Flushes the serial write buffer out to the port.
+   *
+   * @param handle the serial port handle
+   * @see "HAL_FlushSerial"
+   */
   public static native void serialFlush(int handle);
 
+  /**
+   * Clears the receive buffer of the serial port.
+   *
+   * @param handle the serial port handle
+   * @see "HAL_ClearSerial"
+   */
   public static native void serialClear(int handle);
 
+  /**
+   * Closes a serial port.
+   *
+   * @param handle the serial port handle to close
+   * @see "HAL_CloseSerial"
+   */
   public static native void serialClose(int handle);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SimDevice.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SimDevice.java
index db3a587..21ac69a 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SimDevice.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SimDevice.java
@@ -32,7 +32,7 @@
    * Creates a simulated device.
    *
    * <p>The device name must be unique. Returns null if the device name already exists. If multiple
-   * instances of the same device are desired, recommend appending the instance/unique identifer in
+   * instances of the same device are desired, recommend appending the instance/unique identifier in
    * brackets to the base name, e.g. "device[1]".
    *
    * <p>null is returned if not in simulation.
@@ -107,6 +107,15 @@
   }
 
   /**
+   * Get the name of the simulated device.
+   *
+   * @return the name
+   */
+  public String getName() {
+    return SimDeviceJNI.getSimDeviceName(m_handle);
+  }
+
+  /**
    * Creates a value on the simulated device.
    *
    * <p>Returns null if not in simulation.
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java
index 8723cd3..4352a2d 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java
@@ -13,7 +13,7 @@
    * Creates a simulated device.
    *
    * <p>The device name must be unique. 0 is returned if the device name already exists. If multiple
-   * instances of the same device are desired, recommend appending the instance/unique identifer in
+   * instances of the same device are desired, recommend appending the instance/unique identifier in
    * brackets to the base name, e.g. "device[1]".
    *
    * <p>0 is returned if not in simulation.
@@ -33,6 +33,14 @@
    */
   public static native void freeSimDevice(int handle);
 
+  /**
+   * Get the name of a simulated device.
+   *
+   * @param handle simulated device handle
+   * @return name of the simulated device
+   */
+  public static native String getSimDeviceName(int handle);
+
   private static native int createSimValueNative(
       int device, String name, int direction, int type, long value1, double value2);
 
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/ThreadsJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/ThreadsJNI.java
index c854e20..2ccc808 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/ThreadsJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/ThreadsJNI.java
@@ -4,10 +4,37 @@
 
 package edu.wpi.first.hal;
 
+/**
+ * Threads HAL JNI Functions.
+ *
+ * @see "Threads.h"
+ */
 public class ThreadsJNI extends JNIWrapper {
+  /**
+   * Gets the thread priority for the current thread.
+   *
+   * @return The current thread priority. For real-time, this is 1-99 with 99 being highest. For
+   *     non-real-time, this is 0. See "man 7 sched" for details.
+   * @see "HAL_GetCurrentThreadPriority"
+   */
   public static native int getCurrentThreadPriority();
 
+  /**
+   * Gets the real-time status for the current thread.
+   *
+   * @return Set to true if thread is real-time, otherwise false.
+   * @see "HAL_GetCurrentThreadPriority"
+   */
   public static native boolean getCurrentThreadIsRealTime();
 
+  /**
+   * Sets the thread priority for the current thread.
+   *
+   * @param realTime Set to true to set a real-time priority, false for standard priority.
+   * @param priority Priority to set the thread to. For real-time, this is 1-99 with 99 being
+   *     highest. For non-real-time, this is forced to 0. See "man 7 sched" for more details.
+   * @return True on success.
+   * @see "HAL_SetCurrentThreadPriority"
+   */
   public static native boolean setCurrentThreadPriority(boolean realTime, int priority);
 }
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/EncoderDataJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/EncoderDataJNI.java
index fc9d2e5..e48607a 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/EncoderDataJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/EncoderDataJNI.java
@@ -79,6 +79,15 @@
 
   public static native void setSamplesToAverage(int index, int samplesToAverage);
 
+  public static native int registerDistancePerPulseCallback(
+      int index, NotifyCallback callback, boolean initialNotify);
+
+  public static native void cancelDistancePerPulseCallback(int index, int uid);
+
+  public static native double getDistancePerPulse(int index);
+
+  public static native void setDistancePerPulse(int index, double distancePerPulse);
+
   public static native void setDistance(int index, double distance);
 
   public static native double getDistance(int index);
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/PWMDataJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/PWMDataJNI.java
index bf75398..d66256b 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/PWMDataJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/PWMDataJNI.java
@@ -16,14 +16,14 @@
 
   public static native void setInitialized(int index, boolean initialized);
 
-  public static native int registerRawValueCallback(
+  public static native int registerPulseMicrosecondCallback(
       int index, NotifyCallback callback, boolean initialNotify);
 
-  public static native void cancelRawValueCallback(int index, int uid);
+  public static native void cancelPulseMicrosecondCallback(int index, int uid);
 
-  public static native int getRawValue(int index);
+  public static native int getPulseMicrosecond(int index);
 
-  public static native void setRawValue(int index, int rawValue);
+  public static native void setPulseMicrosecond(int index, int microsecondPulseTime);
 
   public static native int registerSpeedCallback(
       int index, NotifyCallback callback, boolean initialNotify);
diff --git a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java
index ef06067..7f51331 100644
--- a/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java
+++ b/third_party/allwpilib/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java
@@ -151,6 +151,23 @@
 
   public static native void setBrownoutVoltage(double brownoutVoltage);
 
+  public static native int registerCPUTempCallback(NotifyCallback callback, boolean initialNotify);
+
+  public static native void cancelCPUTempCallback(int uid);
+
+  public static native double getCPUTemp();
+
+  public static native void setCPUTemp(double cpuTemp);
+
+  public static native int registerTeamNumberCallback(
+      NotifyCallback callback, boolean initialNotify);
+
+  public static native void cancelTeamNumberCallback(int uid);
+
+  public static native int getTeamNumber();
+
+  public static native void setTeamNumber(int teamNumber);
+
   public static native String getSerialNumber();
 
   public static native void setSerialNumber(String serialNumber);
diff --git a/third_party/allwpilib/hal/src/main/native/athena/AddressableLED.cpp b/third_party/allwpilib/hal/src/main/native/athena/AddressableLED.cpp
index 5e55c93..57f37cb 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/AddressableLED.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/AddressableLED.cpp
@@ -206,10 +206,10 @@
 }
 
 void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
-                                    int32_t lowTime0NanoSeconds,
                                     int32_t highTime0NanoSeconds,
-                                    int32_t lowTime1NanoSeconds,
+                                    int32_t lowTime0NanoSeconds,
                                     int32_t highTime1NanoSeconds,
+                                    int32_t lowTime1NanoSeconds,
                                     int32_t* status) {
   auto led = addressableLEDHandles->Get(handle);
   if (!led) {
@@ -217,10 +217,10 @@
     return;
   }
 
-  led->led->writeLowBitTickTiming(1, highTime0NanoSeconds / 25, status);
-  led->led->writeLowBitTickTiming(0, lowTime0NanoSeconds / 25, status);
-  led->led->writeHighBitTickTiming(1, highTime1NanoSeconds / 25, status);
-  led->led->writeHighBitTickTiming(0, lowTime1NanoSeconds / 25, status);
+  led->led->writeLowBitTickTiming(0, highTime0NanoSeconds / 25, status);
+  led->led->writeLowBitTickTiming(1, lowTime0NanoSeconds / 25, status);
+  led->led->writeHighBitTickTiming(0, highTime1NanoSeconds / 25, status);
+  led->led->writeHighBitTickTiming(1, lowTime1NanoSeconds / 25, status);
 }
 
 void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle,
diff --git a/third_party/allwpilib/hal/src/main/native/athena/AnalogGyro.cpp b/third_party/allwpilib/hal/src/main/native/athena/AnalogGyro.cpp
index 19f26dd..1751ac0 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/AnalogGyro.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/AnalogGyro.cpp
@@ -4,6 +4,7 @@
 
 #include "hal/AnalogGyro.h"
 
+#include <cmath>
 #include <string>
 #include <thread>
 
@@ -204,8 +205,8 @@
     return;
   }
 
-  gyro->center = static_cast<int32_t>(
-      static_cast<double>(value) / static_cast<double>(count) + 0.5);
+  gyro->center =
+      std::round(static_cast<double>(value) / static_cast<double>(count));
 
   gyro->offset = static_cast<double>(value) / static_cast<double>(count) -
                  static_cast<double>(gyro->center);
diff --git a/third_party/allwpilib/hal/src/main/native/athena/AnalogInternal.h b/third_party/allwpilib/hal/src/main/native/athena/AnalogInternal.h
index 431c624..eb8116b 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/AnalogInternal.h
+++ b/third_party/allwpilib/hal/src/main/native/athena/AnalogInternal.h
@@ -69,7 +69,7 @@
  * number of active channels and the sample rate.
  *
  * When the number of channels changes, use the new value.  Otherwise,
- * return the curent value.
+ * return the current value.
  *
  * @return Value to write to the active channels field.
  */
diff --git a/third_party/allwpilib/hal/src/main/native/athena/CANAPI.cpp b/third_party/allwpilib/hal/src/main/native/athena/CANAPI.cpp
index a3e4904..70f09e0 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/CANAPI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/CANAPI.cpp
@@ -35,15 +35,6 @@
 static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>*
     canHandles;
 
-static uint32_t GetPacketBaseTime() {
-  timespec t;
-  clock_gettime(CLOCK_MONOTONIC, &t);
-
-  // Convert t to milliseconds
-  uint64_t ms = t.tv_sec * 1000ull + t.tv_nsec / 1000000ull;
-  return ms & 0xFFFFFFFF;
-}
-
 namespace hal::init {
 void InitializeCANAPI() {
   static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>
@@ -63,6 +54,15 @@
 
 extern "C" {
 
+uint32_t HAL_GetCANPacketBaseTime(void) {
+  timespec t;
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  // Convert t to milliseconds
+  uint64_t ms = t.tv_sec * 1000ull + t.tv_nsec / 1000000ull;
+  return ms & 0xFFFFFFFF;
+}
+
 HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer,
                                 int32_t deviceId, HAL_CANDeviceType deviceType,
                                 int32_t* status) {
@@ -267,7 +267,7 @@
     auto i = can->receives.find(messageId);
     if (i != can->receives.end()) {
       // Found, check if new enough
-      uint32_t now = GetPacketBaseTime();
+      uint32_t now = HAL_GetCANPacketBaseTime();
       if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
         // Timeout, return bad status
         *status = HAL_CAN_TIMEOUT;
diff --git a/third_party/allwpilib/hal/src/main/native/athena/DIO.cpp b/third_party/allwpilib/hal/src/main/native/athena/DIO.cpp
index e6308fb..d75f32b 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/DIO.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/DIO.cpp
@@ -200,8 +200,7 @@
   if (*status != 0) {
     return;
   }
-  uint16_t pwmPeriodPower =
-      std::lround(std::log(1.0 / (16 * 1.0E-6 * rate)) / std::log(2.0));
+  uint16_t pwmPeriodPower = std::lround(std::log2(1.0 / (16 * 1.0E-6 * rate)));
   digitalSystem->writePWMPeriodPower(pwmPeriodPower, status);
 }
 
@@ -231,12 +230,13 @@
       // frequencies.
       rawDutyCycle = rawDutyCycle / std::pow(2.0, 4 - pwmPeriodPower);
     }
-    if (id < 4)
+    if (id < 4) {
       digitalSystem->writePWMDutyCycleA(id, static_cast<uint8_t>(rawDutyCycle),
                                         status);
-    else
+    } else {
       digitalSystem->writePWMDutyCycleB(
           id - 4, static_cast<uint8_t>(rawDutyCycle), status);
+    }
   }
 }
 
@@ -255,12 +255,13 @@
   }
   {
     std::scoped_lock lock(digitalPwmMutex);
-    if (id < 4)
+    if (id < 4) {
       digitalSystem->writePWMDutyCycleA(id, static_cast<uint8_t>(rawDutyCycle),
                                         status);
-    else
+    } else {
       digitalSystem->writePWMDutyCycleB(
           id - 4, static_cast<uint8_t>(rawDutyCycle), status);
+    }
   }
 }
 
diff --git a/third_party/allwpilib/hal/src/main/native/athena/DigitalInternal.cpp b/third_party/allwpilib/hal/src/main/native/athena/DigitalInternal.cpp
index b9339ee..fb0cf98 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/DigitalInternal.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/DigitalInternal.cpp
@@ -112,9 +112,7 @@
 
   pwmSystem->writeConfig_Period(std::lround(kDefaultPwmPeriod / loopTime),
                                 status);
-  uint16_t minHigh = std::lround(
-      (kDefaultPwmCenter - kDefaultPwmStepsDown * loopTime) / loopTime);
-  pwmSystem->writeConfig_MinHigh(minHigh, status);
+  pwmSystem->writeConfig_MinHigh(0, status);
   // Ensure that PWM output values are set to OFF
   for (uint8_t pwmIndex = 0; pwmIndex < kNumPWMChannels; pwmIndex++) {
     // Copy of SetPWM
diff --git a/third_party/allwpilib/hal/src/main/native/athena/DigitalInternal.h b/third_party/allwpilib/hal/src/main/native/athena/DigitalInternal.h
index 685987a..5b73a39 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/DigitalInternal.h
+++ b/third_party/allwpilib/hal/src/main/native/athena/DigitalInternal.h
@@ -47,15 +47,8 @@
  * devices.
  */
 constexpr double kDefaultPwmPeriod = 5.05;
-/**
- * kDefaultPwmCenter is the PWM range center in ms
- */
-constexpr double kDefaultPwmCenter = 1.5;
-/**
- * kDefaultPWMStepsDown is the number of PWM steps below the centerpoint
- */
-constexpr int32_t kDefaultPwmStepsDown = 1000;
 constexpr int32_t kPwmDisabled = 0;
+constexpr int32_t kPwmAlwaysHigh = 0xFFFF;
 
 extern std::unique_ptr<tDIO> digitalSystem;
 extern std::unique_ptr<tRelay> relaySystem;
diff --git a/third_party/allwpilib/hal/src/main/native/athena/FPGACalls.h b/third_party/allwpilib/hal/src/main/native/athena/FPGACalls.h
index 46e8ac4..3ef9004 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/FPGACalls.h
+++ b/third_party/allwpilib/hal/src/main/native/athena/FPGACalls.h
@@ -8,7 +8,8 @@
 
 namespace hal {
 namespace init {
-[[nodiscard]] int InitializeFPGA();
+[[nodiscard]]
+int InitializeFPGA();
 }  // namespace init
 
 using HAL_NiFpga_ReserveIrqContextFunc =
diff --git a/third_party/allwpilib/hal/src/main/native/athena/FPGAEncoder.h b/third_party/allwpilib/hal/src/main/native/athena/FPGAEncoder.h
index b401ccd..a819d96 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/FPGAEncoder.h
+++ b/third_party/allwpilib/hal/src/main/native/athena/FPGAEncoder.h
@@ -37,7 +37,7 @@
 /**
  * Returns the period of the most recent pulse.
  * Returns the period of the most recent Encoder pulse in seconds.
- * This method compenstates for the decoding type.
+ * This method compensates for the decoding type.
  *
  * @deprecated Use GetRate() in favor of this method.  This returns unscaled
  * periods and GetRate() scales using value from SetDistancePerPulse().
diff --git a/third_party/allwpilib/hal/src/main/native/athena/FRCDriverStation.cpp b/third_party/allwpilib/hal/src/main/native/athena/FRCDriverStation.cpp
index e8db9a5..12739c8 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/FRCDriverStation.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/FRCDriverStation.cpp
@@ -43,6 +43,7 @@
   HAL_JoystickButtons buttons[HAL_kMaxJoysticks];
   HAL_AllianceStationID allianceStation;
   float matchTime;
+  HAL_ControlWord controlWord;
 };
 static_assert(std::is_standard_layout_v<JoystickDataCache>);
 // static_assert(std::is_trivial_v<JoystickDataCache>);
@@ -101,9 +102,14 @@
     HAL_GetJoystickPOVsInternal(i, &povs[i]);
     HAL_GetJoystickButtonsInternal(i, &buttons[i]);
   }
-  FRC_NetworkCommunication_getAllianceStation(
-      reinterpret_cast<AllianceStationID_t*>(&allianceStation));
+  AllianceStationID_t alliance = kAllianceStationID_red1;
+  FRC_NetworkCommunication_getAllianceStation(&alliance);
+  int allianceInt = alliance;
+  allianceInt += 1;
+  allianceStation = static_cast<HAL_AllianceStationID>(allianceInt);
   FRC_NetworkCommunication_getMatchTime(&matchTime);
+  FRC_NetworkCommunication_getControlWord(
+      reinterpret_cast<ControlWord_t*>(&controlWord));
 }
 
 #define CHECK_JOYSTICK_NUMBER(stickNum)                  \
@@ -114,7 +120,7 @@
 static JoystickDataCache caches[3];
 static JoystickDataCache* currentRead = &caches[0];
 static JoystickDataCache* currentReadLocal = &caches[0];
-static std::atomic<JoystickDataCache*> currentCache{&caches[1]};
+static std::atomic<JoystickDataCache*> currentCache{nullptr};
 static JoystickDataCache* lastGiven = &caches[1];
 static JoystickDataCache* cacheToUpdate = &caches[2];
 
@@ -508,17 +514,27 @@
   }
 }
 
-void HAL_RefreshDSData(void) {
+HAL_Bool HAL_RefreshDSData(void) {
   HAL_ControlWord controlWord;
   std::memset(&controlWord, 0, sizeof(controlWord));
   FRC_NetworkCommunication_getControlWord(
       reinterpret_cast<ControlWord_t*>(&controlWord));
-  std::scoped_lock lock{cacheMutex};
-  JoystickDataCache* prev = currentCache.exchange(nullptr);
-  if (prev != nullptr) {
-    currentRead = prev;
+  JoystickDataCache* prev;
+  {
+    std::scoped_lock lock{cacheMutex};
+    prev = currentCache.exchange(nullptr);
+    if (prev != nullptr) {
+      currentRead = prev;
+    }
+    // If newest state shows we have a DS attached, just use the
+    // control word out of the cache, As it will be the one in sync
+    // with the data. Otherwise use the state that shows disconnected.
+    if (controlWord.dsAttached) {
+      newestControlWord = currentRead->controlWord;
+    } else {
+      newestControlWord = controlWord;
+    }
   }
-  newestControlWord = controlWord;
 
   uint32_t mask = tcpMask.exchange(0);
   if (mask != 0) {
@@ -526,6 +542,7 @@
     std::scoped_lock tcpLock(tcpCacheMutex);
     tcpCache.CloneTo(&tcpCurrent);
   }
+  return prev != nullptr;
 }
 
 void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle) {
@@ -551,4 +568,13 @@
   setNewDataOccurRef(refNumber);
   FRC_NetworkCommunication_setNewTcpDataOccurRef(tcpRefNumber);
 }
+
+void WaitForInitialPacket() {
+  wpi::Event waitForInitEvent;
+  driverStation->newDataEvents.Add(waitForInitEvent.GetHandle());
+  bool timed_out = false;
+  wpi::WaitForObject(waitForInitEvent.GetHandle(), 0.1, &timed_out);
+  // Don't care what the result is, just want to give it a chance.
+  driverStation->newDataEvents.Remove(waitForInitEvent.GetHandle());
+}
 }  // namespace hal
diff --git a/third_party/allwpilib/hal/src/main/native/athena/HAL.cpp b/third_party/allwpilib/hal/src/main/native/athena/HAL.cpp
index 5977724..2eeaeb8 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/HAL.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/HAL.cpp
@@ -33,6 +33,7 @@
 #include "hal/Errors.h"
 #include "hal/Notifier.h"
 #include "hal/handles/HandlesInternal.h"
+#include "hal/roborio/HMB.h"
 #include "hal/roborio/InterruptManager.h"
 #include "visa/visa.h"
 
@@ -46,10 +47,16 @@
 static size_t roboRioCommentsStringSize;
 static bool roboRioCommentsStringInitialized;
 
+static int32_t teamNumber = -1;
+
+static const volatile HAL_HMBData* hmbBuffer;
+#define HAL_HMB_TIMESTAMP_OFFSET 5
+
 using namespace hal;
 
 namespace hal {
 void InitializeDriverStation();
+void WaitForInitialPacket();
 namespace init {
 void InitializeHAL() {
   InitializeCTREPCM();
@@ -226,12 +233,10 @@
   }
 }
 
+static HAL_RuntimeType runtimeType = HAL_Runtime_RoboRIO;
+
 HAL_RuntimeType HAL_GetRuntimeType(void) {
-  nLoadOut::tTargetClass targetClass = nLoadOut::getTargetClass();
-  if (targetClass == nLoadOut::kTargetClass_RoboRIO2) {
-    return HAL_Runtime_RoboRIO2;
-  }
-  return HAL_Runtime_RoboRIO;
+  return runtimeType;
 }
 
 int32_t HAL_GetFPGAVersion(int32_t* status) {
@@ -291,11 +296,8 @@
       return;
     }
     start += searchString.size();
-    size_t end = fileContents.find("\"", start);
-    if (end == std::string_view::npos) {
-      end = fileContents.size();
-    }
-    std::string_view escapedComments = wpi::slice(fileContents, start, end);
+    std::string_view escapedComments =
+        wpi::slice(fileContents, start, fileContents.size());
     wpi::SmallString<64> buf;
     auto [unescapedComments, rem] = wpi::UnescapeCString(escapedComments, buf);
     unescapedComments.copy(roboRioCommentsString,
@@ -327,27 +329,61 @@
   return toCopy;
 }
 
+void InitializeTeamNumber(void) {
+  char hostnameBuf[25];
+  auto status = gethostname(hostnameBuf, sizeof(hostnameBuf));
+  if (status != 0) {
+    teamNumber = 0;
+    return;
+  }
+
+  std::string_view hostname{hostnameBuf, sizeof(hostnameBuf)};
+
+  // hostname is frc-{TEAM}-roborio
+  // Split string around '-' (max of 2 splits), take the second element of the
+  // resulting array.
+  wpi::SmallVector<std::string_view> elements;
+  wpi::split(hostname, elements, "-", 2);
+  if (elements.size() < 3) {
+    teamNumber = 0;
+    return;
+  }
+
+  teamNumber = wpi::parse_integer<int32_t>(elements[1], 10).value_or(0);
+}
+
+int32_t HAL_GetTeamNumber(void) {
+  if (teamNumber == -1) {
+    InitializeTeamNumber();
+  }
+  return teamNumber;
+}
+
 uint64_t HAL_GetFPGATime(int32_t* status) {
   hal::init::CheckInit();
-  if (!global) {
+  if (!hmbBuffer) {
     *status = NiFpga_Status_ResourceNotInitialized;
     return 0;
   }
-  *status = 0;
-  uint64_t upper1 = global->readLocalTimeUpper(status);
-  uint32_t lower = global->readLocalTime(status);
-  uint64_t upper2 = global->readLocalTimeUpper(status);
-  if (*status != 0) {
-    return 0;
-  }
+
+  asm("dmb");
+  uint64_t upper1 = hmbBuffer->Timestamp.Upper;
+  asm("dmb");
+  uint32_t lower = hmbBuffer->Timestamp.Lower;
+  asm("dmb");
+  uint64_t upper2 = hmbBuffer->Timestamp.Upper;
+
   if (upper1 != upper2) {
     // Rolled over between the lower call, reread lower
-    lower = global->readLocalTime(status);
-    if (*status != 0) {
-      return 0;
-    }
+    asm("dmb");
+    lower = hmbBuffer->Timestamp.Lower;
   }
-  return (upper2 << 32) + lower;
+  // 5 is added here because the time to write from the FPGA
+  // to the HMB buffer is longer then the time to read
+  // from the time register. This would cause register based
+  // timestamps to be ahead of HMB timestamps, which could
+  // be very bad.
+  return (upper2 << 32) + lower + HAL_HMB_TIMESTAMP_OFFSET;
 }
 
 uint64_t HAL_ExpandFPGATime(uint32_t unexpandedLower, int32_t* status) {
@@ -401,6 +437,21 @@
   return !(watchdog->readStatus_PowerAlive(status));
 }
 
+HAL_Bool HAL_GetRSLState(int32_t* status) {
+  hal::init::CheckInit();
+  if (!global) {
+    *status = NiFpga_Status_ResourceNotInitialized;
+    return false;
+  }
+  return global->readLEDs_RSL(status);
+}
+
+HAL_Bool HAL_GetSystemTimeValid(int32_t* status) {
+  uint8_t timeWasSet = 0;
+  *status = FRC_NetworkCommunication_getTimeWasSet(&timeWasSet);
+  return timeWasSet != 0;
+}
+
 static bool killExistingProgram(int timeout, int mode) {
   // Kill any previous robot programs
   std::fstream fs;
@@ -485,10 +536,18 @@
     setNewDataSem(nullptr);
   });
 
-  nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass =
-      nLoadOut::getTargetClass();
+  // Setup WPI_Now to use FPGA timestamp
+  // this also sets nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass
+  wpi::impl::SetupNowRio();
 
   int32_t status = 0;
+
+  HAL_InitializeHMB(&status);
+  if (status != 0) {
+    return false;
+  }
+  hmbBuffer = HAL_GetHMBBuffer();
+
   global.reset(tGlobal::create(&status));
   watchdog.reset(tSysWatchdog::create(&status));
 
@@ -496,6 +555,13 @@
     return false;
   }
 
+  nLoadOut::tTargetClass targetClass = nLoadOut::getTargetClass();
+  if (targetClass == nLoadOut::kTargetClass_RoboRIO2) {
+    runtimeType = HAL_Runtime_RoboRIO2;
+  } else {
+    runtimeType = HAL_Runtime_RoboRIO;
+  }
+
   InterruptManager::Initialize(global->getSystemInterface());
 
   hal::InitializeDriverStation();
@@ -505,20 +571,7 @@
     return false;
   }
 
-  // Set WPI_Now to use FPGA timestamp
-  wpi::SetNowImpl([]() -> uint64_t {
-    int32_t status = 0;
-    uint64_t rv = HAL_GetFPGATime(&status);
-    if (status != 0) {
-      fmt::print(stderr,
-                 "Call to HAL_GetFPGATime failed in wpi::Now() with status {}. "
-                 "Initialization might have failed. Time will not be correct\n",
-                 status);
-      std::fflush(stderr);
-      return 0u;
-    }
-    return rv;
-  });
+  hal::WaitForInitialPacket();
 
   initialized = true;
   return true;
diff --git a/third_party/allwpilib/hal/src/main/native/athena/HMB.cpp b/third_party/allwpilib/hal/src/main/native/athena/HMB.cpp
new file mode 100644
index 0000000..d22fcda
--- /dev/null
+++ b/third_party/allwpilib/hal/src/main/native/athena/HMB.cpp
@@ -0,0 +1,82 @@
+// 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 "hal/roborio/HMB.h"
+
+#include <memory>
+
+#include "FPGACalls.h"
+#include "hal/ChipObject.h"
+
+using namespace hal;
+
+// 16 classes of data, each takes up 16 uint16_t's
+static_assert(sizeof(HAL_HMBData) == 0x10 * 16 * sizeof(uint32_t));
+// Timestamp is the last class, and should be offset correctly
+static_assert(offsetof(HAL_HMBData, Timestamp) == 0x10 * 15 * sizeof(uint32_t));
+
+static volatile HAL_HMBData* hmbBuffer;
+static size_t hmbBufferSize;
+static constexpr const char hmbName[] = "HMB_0_RAM";
+static std::unique_ptr<tHMB> hmb;
+
+namespace {
+struct HMBHolder {
+  ~HMBHolder() {
+    if (hmbBuffer) {
+      hal::HAL_NiFpga_CloseHmb(hmb->getSystemInterface()->getHandle(), hmbName);
+    }
+  }
+};
+}  // namespace
+
+extern "C" {
+
+void HAL_InitializeHMB(int32_t* status) {
+  static HMBHolder holder;
+
+  hmb.reset(tHMB::create(status));
+  if (*status != 0) {
+    return;
+  }
+
+  *status = hal::HAL_NiFpga_OpenHmb(
+      hmb->getSystemInterface()->getHandle(), hmbName, &hmbBufferSize,
+      reinterpret_cast<void**>(const_cast<HAL_HMBData**>(&hmbBuffer)));
+
+  if (*status != 0) {
+    return;
+  }
+
+  auto cfg = hmb->readConfig(status);
+  cfg.Enables_AI0_Low = 1;
+  cfg.Enables_AI0_High = 1;
+  cfg.Enables_AIAveraged0_Low = 1;
+  cfg.Enables_AIAveraged0_High = 1;
+  cfg.Enables_Accumulator0 = 1;
+  cfg.Enables_Accumulator1 = 1;
+  cfg.Enables_DI = 1;
+  cfg.Enables_AnalogTriggers = 1;
+  cfg.Enables_Counters_Low = 1;
+  cfg.Enables_Counters_High = 1;
+  cfg.Enables_CounterTimers_Low = 1;
+  cfg.Enables_CounterTimers_High = 1;
+  cfg.Enables_Encoders_Low = 1;
+  cfg.Enables_Encoders_High = 1;
+  cfg.Enables_EncoderTimers_Low = 1;
+  cfg.Enables_EncoderTimers_High = 1;
+  cfg.Enables_DutyCycle_Low = 1;
+  cfg.Enables_DutyCycle_High = 1;
+  cfg.Enables_Interrupts = 1;
+  cfg.Enables_PWM = 1;
+  cfg.Enables_PWM_MXP = 1;
+  cfg.Enables_Relay_DO_AO = 1;
+  cfg.Enables_Timestamp = 1;
+  hmb->writeConfig(cfg, status);
+}
+
+const volatile HAL_HMBData* HAL_GetHMBBuffer(void) {
+  return hmbBuffer;
+}
+}  // extern "C"
diff --git a/third_party/allwpilib/hal/src/main/native/athena/Notifier.cpp b/third_party/allwpilib/hal/src/main/native/athena/Notifier.cpp
index 9c818d8..bed998d 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/Notifier.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/Notifier.cpp
@@ -128,8 +128,9 @@
 
 static void cleanupNotifierAtExit() {
   int32_t status = 0;
-  if (notifierAlarm)
+  if (notifierAlarm) {
     notifierAlarm->writeEnable(false, &status);
+  }
   notifierAlarm = nullptr;
   notifierRunning = false;
   hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
@@ -236,8 +237,9 @@
     // here (the atomic fetch_sub will prevent multiple parallel entries
     // into this function)
 
-    if (notifierAlarm)
+    if (notifierAlarm) {
       notifierAlarm->writeEnable(false, status);
+    }
     notifierRunning = false;
     hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
     if (notifierThread.joinable()) {
@@ -272,8 +274,9 @@
     notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
                                     status);
     // Enable the alarm.
-    if (!wasActive)
+    if (!wasActive) {
       notifierAlarm->writeEnable(true, status);
+    }
   }
 }
 
diff --git a/third_party/allwpilib/hal/src/main/native/athena/PWM.cpp b/third_party/allwpilib/hal/src/main/native/athena/PWM.cpp
index 83a0ca5..0233c07 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/PWM.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/PWM.cpp
@@ -9,6 +9,8 @@
 #include <cstdio>
 #include <thread>
 
+#include <fmt/format.h>
+
 #include "ConstantsInternal.h"
 #include "DigitalInternal.h"
 #include "HALInitializer.h"
@@ -118,7 +120,7 @@
   }
 
   // Defaults to allow an always valid config.
-  HAL_SetPWMConfig(handle, 2.0, 1.501, 1.5, 1.499, 1.0, status);
+  HAL_SetPWMConfigMicroseconds(handle, 2000, 1501, 1500, 1499, 1000, status);
 
   port->previousAllocation = allocationLocation ? allocationLocation : "";
 
@@ -158,62 +160,28 @@
   return channel < kNumPWMChannels && channel >= 0;
 }
 
-void HAL_SetPWMConfig(HAL_DigitalHandle pwmPortHandle, double max,
-                      double deadbandMax, double center, double deadbandMin,
-                      double min, int32_t* status) {
+void HAL_SetPWMConfigMicroseconds(HAL_DigitalHandle pwmPortHandle, int32_t max,
+                                  int32_t deadbandMax, int32_t center,
+                                  int32_t deadbandMin, int32_t min,
+                                  int32_t* status) {
   auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
   if (port == nullptr) {
     *status = HAL_HANDLE_ERROR;
     return;
   }
 
-  // calculate the loop time in milliseconds
-  double loopTime =
-      HAL_GetPWMLoopTiming(status) / (kSystemClockTicksPerMicrosecond * 1e3);
-  if (*status != 0) {
-    return;
-  }
-
-  int32_t maxPwm = static_cast<int32_t>((max - kDefaultPwmCenter) / loopTime +
-                                        kDefaultPwmStepsDown - 1);
-  int32_t deadbandMaxPwm = static_cast<int32_t>(
-      (deadbandMax - kDefaultPwmCenter) / loopTime + kDefaultPwmStepsDown - 1);
-  int32_t centerPwm = static_cast<int32_t>(
-      (center - kDefaultPwmCenter) / loopTime + kDefaultPwmStepsDown - 1);
-  int32_t deadbandMinPwm = static_cast<int32_t>(
-      (deadbandMin - kDefaultPwmCenter) / loopTime + kDefaultPwmStepsDown - 1);
-  int32_t minPwm = static_cast<int32_t>((min - kDefaultPwmCenter) / loopTime +
-                                        kDefaultPwmStepsDown - 1);
-
-  port->maxPwm = maxPwm;
-  port->deadbandMaxPwm = deadbandMaxPwm;
-  port->deadbandMinPwm = deadbandMinPwm;
-  port->centerPwm = centerPwm;
-  port->minPwm = minPwm;
+  port->maxPwm = max;
+  port->deadbandMaxPwm = deadbandMax;
+  port->deadbandMinPwm = deadbandMin;
+  port->centerPwm = center;
+  port->minPwm = min;
   port->configSet = true;
 }
 
-void HAL_SetPWMConfigRaw(HAL_DigitalHandle pwmPortHandle, int32_t maxPwm,
-                         int32_t deadbandMaxPwm, int32_t centerPwm,
-                         int32_t deadbandMinPwm, int32_t minPwm,
-                         int32_t* status) {
-  auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
-  if (port == nullptr) {
-    *status = HAL_HANDLE_ERROR;
-    return;
-  }
-
-  port->maxPwm = maxPwm;
-  port->deadbandMaxPwm = deadbandMaxPwm;
-  port->deadbandMinPwm = deadbandMinPwm;
-  port->centerPwm = centerPwm;
-  port->minPwm = minPwm;
-}
-
-void HAL_GetPWMConfigRaw(HAL_DigitalHandle pwmPortHandle, int32_t* maxPwm,
-                         int32_t* deadbandMaxPwm, int32_t* centerPwm,
-                         int32_t* deadbandMinPwm, int32_t* minPwm,
-                         int32_t* status) {
+void HAL_GetPWMConfigMicroseconds(HAL_DigitalHandle pwmPortHandle,
+                                  int32_t* maxPwm, int32_t* deadbandMaxPwm,
+                                  int32_t* centerPwm, int32_t* deadbandMinPwm,
+                                  int32_t* minPwm, int32_t* status) {
   auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
   if (port == nullptr) {
     *status = HAL_HANDLE_ERROR;
@@ -246,18 +214,30 @@
   return port->eliminateDeadband;
 }
 
-void HAL_SetPWMRaw(HAL_DigitalHandle pwmPortHandle, int32_t value,
-                   int32_t* status) {
+void HAL_SetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle,
+                                     int32_t microsecondPulseTime,
+                                     int32_t* status) {
   auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
   if (port == nullptr) {
     *status = HAL_HANDLE_ERROR;
     return;
   }
 
+  if (microsecondPulseTime < 0 ||
+      (microsecondPulseTime != 0xFFFF && microsecondPulseTime >= 4096)) {
+    *status = PARAMETER_OUT_OF_RANGE;
+    hal::SetLastError(
+        status,
+        fmt::format("Pulse time {} out of range. Expect [0-4096) or 0xFFFF",
+                    microsecondPulseTime));
+    return;
+  }
+
   if (port->channel < tPWM::kNumHdrRegisters) {
-    pwmSystem->writeHdr(port->channel, value, status);
+    pwmSystem->writeHdr(port->channel, microsecondPulseTime, status);
   } else {
-    pwmSystem->writeMXP(port->channel - tPWM::kNumHdrRegisters, value, status);
+    pwmSystem->writeMXP(port->channel - tPWM::kNumHdrRegisters,
+                        microsecondPulseTime, status);
   }
 }
 
@@ -302,7 +282,7 @@
     return;
   }
 
-  HAL_SetPWMRaw(pwmPortHandle, rawValue, status);
+  HAL_SetPWMPulseTimeMicroseconds(pwmPortHandle, rawValue, status);
 }
 
 void HAL_SetPWMPosition(HAL_DigitalHandle pwmPortHandle, double pos,
@@ -335,14 +315,15 @@
     return;
   }
 
-  HAL_SetPWMRaw(pwmPortHandle, rawValue, status);
+  HAL_SetPWMPulseTimeMicroseconds(pwmPortHandle, rawValue, status);
 }
 
 void HAL_SetPWMDisabled(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
-  HAL_SetPWMRaw(pwmPortHandle, kPwmDisabled, status);
+  HAL_SetPWMPulseTimeMicroseconds(pwmPortHandle, kPwmDisabled, status);
 }
 
-int32_t HAL_GetPWMRaw(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
+int32_t HAL_GetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle,
+                                        int32_t* status) {
   auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
   if (port == nullptr) {
     *status = HAL_HANDLE_ERROR;
@@ -367,12 +348,11 @@
     return 0;
   }
 
-  int32_t value = HAL_GetPWMRaw(pwmPortHandle, status);
+  int32_t value = HAL_GetPWMPulseTimeMicroseconds(pwmPortHandle, status);
   if (*status != 0) {
     return 0;
   }
   DigitalPort* dPort = port.get();
-
   if (value == kPwmDisabled) {
     return 0.0;
   } else if (value > GetMaxPositivePwm(dPort)) {
@@ -401,7 +381,7 @@
     return 0;
   }
 
-  int32_t value = HAL_GetPWMRaw(pwmPortHandle, status);
+  int32_t value = HAL_GetPWMPulseTimeMicroseconds(pwmPortHandle, status);
   if (*status != 0) {
     return 0;
   }
@@ -445,6 +425,11 @@
   }
 }
 
+void HAL_SetPWMAlwaysHighMode(HAL_DigitalHandle pwmPortHandle,
+                              int32_t* status) {
+  HAL_SetPWMPulseTimeMicroseconds(pwmPortHandle, kPwmAlwaysHigh, status);
+}
+
 int32_t HAL_GetPWMLoopTiming(int32_t* status) {
   initializeDigital(status);
   if (*status != 0) {
diff --git a/third_party/allwpilib/hal/src/main/native/athena/Power.cpp b/third_party/allwpilib/hal/src/main/native/athena/Power.cpp
index 895d6e3..aa3449b 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/Power.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/Power.cpp
@@ -66,6 +66,11 @@
       power->readFaultCounts_OverCurrentFaultCount6V(status));
 }
 
+void HAL_SetUserRailEnabled6V(HAL_Bool enabled, int32_t* status) {
+  initializePower(status);
+  power->writeDisable_User6V(!enabled, status);
+}
+
 double HAL_GetUserVoltage5V(int32_t* status) {
   initializePower(status);
   return power->readUserVoltage5V(status) / 4.096 * 0.005962 - 0.013;
@@ -87,6 +92,11 @@
       power->readFaultCounts_OverCurrentFaultCount5V(status));
 }
 
+void HAL_SetUserRailEnabled5V(HAL_Bool enabled, int32_t* status) {
+  initializePower(status);
+  power->writeDisable_User5V(!enabled, status);
+}
+
 double HAL_GetUserVoltage3V3(int32_t* status) {
   initializePower(status);
   return power->readUserVoltage3V3(status) / 4.096 * 0.004902 - 0.01;
@@ -108,6 +118,11 @@
       power->readFaultCounts_OverCurrentFaultCount3V3(status));
 }
 
+void HAL_SetUserRailEnabled3V3(HAL_Bool enabled, int32_t* status) {
+  initializePower(status);
+  power->writeDisable_User3V3(!enabled, status);
+}
+
 void HAL_SetBrownoutVoltage(double voltage, int32_t* status) {
   initializePower(status);
   if (voltage < 0) {
@@ -126,4 +141,9 @@
   return brownout / 4.0;
 }
 
+double HAL_GetCPUTemp(int32_t* status) {
+  initializePower(status);
+  return power->readOnChipTemperature(status) / 4096.0 * 503.975 - 273.15;
+}
+
 }  // extern "C"
diff --git a/third_party/allwpilib/hal/src/main/native/athena/SPI.cpp b/third_party/allwpilib/hal/src/main/native/athena/SPI.cpp
index 3629eb6..0dc30be 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/SPI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/SPI.cpp
@@ -649,8 +649,9 @@
   }
 
   // set tx data registers
-  for (int32_t i = 0; i < dataSize; ++i)
+  for (int32_t i = 0; i < dataSize; ++i) {
     spiSystem->writeAutoTx(i >> 2, i & 3, dataToSend[i], status);
+  }
 
   // set byte counts
   tSPI::tAutoByteCount config;
diff --git a/third_party/allwpilib/hal/src/main/native/athena/SimDevice.cpp b/third_party/allwpilib/hal/src/main/native/athena/SimDevice.cpp
index 3a15f46..03b35f7 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/SimDevice.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/SimDevice.cpp
@@ -12,6 +12,10 @@
 
 void HAL_FreeSimDevice(HAL_SimDeviceHandle handle) {}
 
+const char* HAL_GetSimDeviceName(HAL_SimDeviceHandle handle) {
+  return "";
+}
+
 HAL_SimValueHandle HAL_CreateSimValue(HAL_SimDeviceHandle device,
                                       const char* name, int32_t direction,
                                       const struct HAL_Value* initialValue) {
diff --git a/third_party/allwpilib/hal/src/main/native/athena/cpp/SerialHelper.cpp b/third_party/allwpilib/hal/src/main/native/athena/cpp/SerialHelper.cpp
index fef92d4..71e747c 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/cpp/SerialHelper.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/cpp/SerialHelper.cpp
@@ -192,45 +192,54 @@
     // Open the resource, grab its interface name, and close it.
     ViSession vSession;
     *status = viOpen(m_resourceHandle, desc, VI_NULL, VI_NULL, &vSession);
-    if (*status < 0)
+    if (*status < 0) {
       continue;
+    }
     *status = 0;
 
     *status = viGetAttribute(vSession, VI_ATTR_INTF_INST_NAME, &osName);
     // Ignore an error here, as we want to close the session on an error
     // Use a separate close variable so we can check
     ViStatus closeStatus = viClose(vSession);
-    if (*status < 0)
+    if (*status < 0) {
       continue;
-    if (closeStatus < 0)
+    }
+    if (closeStatus < 0) {
       continue;
+    }
     *status = 0;
 
     // split until (/dev/
     std::string_view devNameRef = wpi::split(osName, "(/dev/").second;
     // String not found, continue
-    if (wpi::equals(devNameRef, ""))
+    if (wpi::equals(devNameRef, "")) {
       continue;
+    }
 
     // Split at )
     std::string_view matchString = wpi::split(devNameRef, ')').first;
-    if (wpi::equals(matchString, devNameRef))
+    if (wpi::equals(matchString, devNameRef)) {
       continue;
+    }
 
     // Search directories to get a list of system accessors
     // The directories we need are not symbolic, so we can safely
     // disable symbolic links.
     std::error_code ec;
     for (auto& p : fs::recursive_directory_iterator("/sys/devices/soc0", ec)) {
-      if (ec)
+      if (ec) {
         break;
+      }
       std::string path = p.path();
-      if (path.find("amba") == std::string::npos)
+      if (path.find("amba") == std::string::npos) {
         continue;
-      if (path.find("usb") == std::string::npos)
+      }
+      if (path.find("usb") == std::string::npos) {
         continue;
-      if (path.find(matchString) == std::string::npos)
+      }
+      if (path.find(matchString) == std::string::npos) {
         continue;
+      }
 
       wpi::SmallVector<std::string_view, 16> pathSplitVec;
       // Split path into individual directories
@@ -254,13 +263,15 @@
 
       // Get the index for our device
       int hubIndex = findtty;
-      if (findtty == -1)
+      if (findtty == -1) {
         hubIndex = findregex;
+      }
 
       int devStart = findusb + 1;
 
-      if (hubIndex < devStart)
+      if (hubIndex < devStart) {
         continue;
+      }
 
       // Add our devices to our list
       m_unsortedHubPath.emplace_back(
diff --git a/third_party/allwpilib/hal/src/main/native/athena/frccansae/CANDeviceInterface.h b/third_party/allwpilib/hal/src/main/native/athena/frccansae/CANDeviceInterface.h
index 8fe4235..d9c9a0c 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/frccansae/CANDeviceInterface.h
+++ b/third_party/allwpilib/hal/src/main/native/athena/frccansae/CANDeviceInterface.h
@@ -51,7 +51,7 @@
    // Bootloader version.  Will not change for the life of the product, but additional
    // field upgrade features could be added in newer hardware.
    char bootloaderRev[MAX_STRING_LEN];
-   // Manufacture Date.  Could be a calender date or just the FRC season year.
+   // Manufacture Date.  Could be a calendar date or just the FRC season year.
    // Also helps troubleshooting "old ones" vs "new ones".
    char manufactureDate[MAX_STRING_LEN];
    // General status of the hardware.  For example if the device is in bootloader
diff --git a/third_party/allwpilib/hal/src/main/native/athena/mockdata/PWMData.cpp b/third_party/allwpilib/hal/src/main/native/athena/mockdata/PWMData.cpp
index 3e12398..4fb6bcf 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/mockdata/PWMData.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/mockdata/PWMData.cpp
@@ -13,7 +13,7 @@
   HAL_SIMDATAVALUE_STUB_CAPI(TYPE, HALSIM, PWM##CAPINAME, RETURN)
 
 DEFINE_CAPI(HAL_Bool, Initialized, false)
-DEFINE_CAPI(int32_t, RawValue, 0)
+DEFINE_CAPI(int32_t, PulseMicrosecond, 0)
 DEFINE_CAPI(double, Speed, 0)
 DEFINE_CAPI(double, Position, 0)
 DEFINE_CAPI(int32_t, PeriodScale, 0)
diff --git a/third_party/allwpilib/hal/src/main/native/athena/mockdata/RoboRioData.cpp b/third_party/allwpilib/hal/src/main/native/athena/mockdata/RoboRioData.cpp
index 9392fcb..8559394 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/mockdata/RoboRioData.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/mockdata/RoboRioData.cpp
@@ -28,6 +28,8 @@
 DEFINE_CAPI(int32_t, UserFaults5V, 0)
 DEFINE_CAPI(int32_t, UserFaults3V3, 0)
 DEFINE_CAPI(double, BrownoutVoltage, 6.75)
+DEFINE_CAPI(double, CPUTemp, 45.0)
+DEFINE_CAPI(int32_t, TeamNumber, 0)
 
 int32_t HALSIM_RegisterRoboRioSerialNumberCallback(
     HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) {
diff --git a/third_party/allwpilib/hal/src/main/native/athena/rev/PHFrames.h b/third_party/allwpilib/hal/src/main/native/athena/rev/PHFrames.h
index 20411a8..a1849e1 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/rev/PHFrames.h
+++ b/third_party/allwpilib/hal/src/main/native/athena/rev/PHFrames.h
@@ -79,7 +79,7 @@
 /**

  * Signals in message Compressor_Config.

  *

- * Configures compressor to use digitial/analog sensors

+ * Configures compressor to use digital/analog sensors

  *

  * All signal values are as on the CAN bus.

  */

diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp
index 75da176..66e3ed0 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp
@@ -69,12 +69,11 @@
   (JNIEnv* env, jclass, jint handle, jbyteArray arr)
 {
   int32_t status = 0;
-  JByteArrayRef jArrRef{env, arr};
-  auto arrRef = jArrRef.array();
+  JSpan<const jbyte> jArrRef{env, arr};
   HAL_WriteAddressableLEDData(
       static_cast<HAL_AddressableLEDHandle>(handle),
-      reinterpret_cast<const HAL_AddressableLEDData*>(arrRef.data()),
-      arrRef.size() / 4, &status);
+      reinterpret_cast<const HAL_AddressableLEDData*>(jArrRef.data()),
+      jArrRef.size() / 4, &status);
   CheckStatus(env, status);
 }
 
@@ -85,12 +84,12 @@
  */
 JNIEXPORT void JNICALL
 Java_edu_wpi_first_hal_AddressableLEDJNI_setBitTiming
-  (JNIEnv* env, jclass, jint handle, jint lowTime0, jint highTime0,
-   jint lowTime1, jint highTime1)
+  (JNIEnv* env, jclass, jint handle, jint highTime0, jint lowTime0,
+   jint highTime1, jint lowTime1)
 {
   int32_t status = 0;
   HAL_SetAddressableLEDBitTiming(static_cast<HAL_AddressableLEDHandle>(handle),
-                                 lowTime0, highTime0, lowTime1, highTime1,
+                                 highTime0, lowTime0, highTime1, lowTime1,
                                  &status);
   CheckStatus(env, status);
 }
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/CANAPIJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/CANAPIJNI.cpp
index 106167a..929f7da 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/CANAPIJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/CANAPIJNI.cpp
@@ -18,6 +18,18 @@
 using namespace wpi::java;
 
 extern "C" {
+
+/*
+ * Class:     edu_wpi_first_hal_CANAPIJNI
+ * Method:    getCANPacketBaseTime
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL
+Java_edu_wpi_first_hal_CANAPIJNI_getCANPacketBaseTime
+  (JNIEnv*, jclass)
+{
+  return HAL_GetCANPacketBaseTime();
+}
 /*
  * Class:     edu_wpi_first_hal_CANAPIJNI
  * Method:    initializeCAN
@@ -59,11 +71,10 @@
   (JNIEnv* env, jclass, jint handle, jbyteArray data, jint apiId)
 {
   auto halHandle = static_cast<HAL_CANHandle>(handle);
-  JByteArrayRef arr{env, data};
-  auto arrRef = arr.array();
+  JSpan<const jbyte> arr{env, data};
   int32_t status = 0;
-  HAL_WriteCANPacket(halHandle, reinterpret_cast<const uint8_t*>(arrRef.data()),
-                     arrRef.size(), apiId, &status);
+  HAL_WriteCANPacket(halHandle, reinterpret_cast<const uint8_t*>(arr.data()),
+                     arr.size(), apiId, &status);
   CheckStatus(env, status);
 }
 
@@ -78,12 +89,11 @@
    jint timeoutMs)
 {
   auto halHandle = static_cast<HAL_CANHandle>(handle);
-  JByteArrayRef arr{env, data};
-  auto arrRef = arr.array();
+  JSpan<const jbyte> arr{env, data};
   int32_t status = 0;
   HAL_WriteCANPacketRepeating(halHandle,
-                              reinterpret_cast<const uint8_t*>(arrRef.data()),
-                              arrRef.size(), apiId, timeoutMs, &status);
+                              reinterpret_cast<const uint8_t*>(arr.data()),
+                              arr.size(), apiId, timeoutMs, &status);
   CheckStatus(env, status);
 }
 
@@ -112,11 +122,10 @@
   (JNIEnv* env, jclass, jint handle, jbyteArray data, jint apiId)
 {
   auto halHandle = static_cast<HAL_CANHandle>(handle);
-  JByteArrayRef arr{env, data};
-  auto arrRef = arr.array();
+  JSpan<const jbyte> arr{env, data};
   int32_t status = 0;
-  HAL_WriteCANPacket(halHandle, reinterpret_cast<const uint8_t*>(arrRef.data()),
-                     arrRef.size(), apiId, &status);
+  HAL_WriteCANPacket(halHandle, reinterpret_cast<const uint8_t*>(arr.data()),
+                     arr.size(), apiId, &status);
   return status;
 }
 
@@ -131,12 +140,11 @@
    jint timeoutMs)
 {
   auto halHandle = static_cast<HAL_CANHandle>(handle);
-  JByteArrayRef arr{env, data};
-  auto arrRef = arr.array();
+  JSpan<const jbyte> arr{env, data};
   int32_t status = 0;
   HAL_WriteCANPacketRepeating(halHandle,
-                              reinterpret_cast<const uint8_t*>(arrRef.data()),
-                              arrRef.size(), apiId, timeoutMs, &status);
+                              reinterpret_cast<const uint8_t*>(arr.data()),
+                              arr.size(), apiId, timeoutMs, &status);
   return status;
 }
 
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/CANJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/CANJNI.cpp
index 4ad2a74..9375a06 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/CANJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/CANJNI.cpp
@@ -26,11 +26,11 @@
 Java_edu_wpi_first_hal_can_CANJNI_FRCNetCommCANSessionMuxSendMessage
   (JNIEnv* env, jclass, jint messageID, jbyteArray data, jint periodMs)
 {
-  JByteArrayRef dataArray{env, data};
+  JSpan<const jbyte> dataArray{env, data};
 
   const uint8_t* dataBuffer =
-      reinterpret_cast<const uint8_t*>(dataArray.array().data());
-  uint8_t dataSize = dataArray.array().size();
+      reinterpret_cast<const uint8_t*>(dataArray.data());
+  uint8_t dataSize = dataArray.size();
 
   int32_t status = 0;
   HAL_CAN_SendMessage(messageID, dataBuffer, dataSize, periodMs, &status);
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/DMAJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/DMAJNI.cpp
index 3e95c7b..79dc14b 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/DMAJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/DMAJNI.cpp
@@ -5,12 +5,15 @@
 #include <algorithm>
 #include <cstring>
 
+#include <wpi/jni_util.h>
+
 #include "HALUtil.h"
 #include "edu_wpi_first_hal_DMAJNI.h"
 #include "hal/DMA.h"
 #include "hal/handles/HandlesInternal.h"
 
 using namespace hal;
+using namespace wpi::java;
 
 namespace hal {
 bool GetEncoderBaseHandle(HAL_EncoderHandle handle,
@@ -312,20 +315,17 @@
   env->SetIntArrayRegion(buf, 0, dmaSample.captureSize,
                          reinterpret_cast<jint*>(dmaSample.readBuffer));
 
-  int32_t* nativeArr =
-      static_cast<int32_t*>(env->GetPrimitiveArrayCritical(store, nullptr));
+  CriticalJSpan<jint> nativeArr{env, store};
 
   std::copy_n(
       dmaSample.channelOffsets,
       sizeof(dmaSample.channelOffsets) / sizeof(dmaSample.channelOffsets[0]),
-      nativeArr);
+      nativeArr.data());
   nativeArr[22] = static_cast<int32_t>(dmaSample.captureSize);
   nativeArr[23] = static_cast<int32_t>(dmaSample.triggerChannels);
   nativeArr[24] = remaining;
   nativeArr[25] = readStatus;
 
-  env->ReleasePrimitiveArrayCritical(store, nativeArr, JNI_ABORT);
-
   return dmaSample.timeStamp;
 }
 
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/DriverStationJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/DriverStationJNI.cpp
index f050f02..5e3e97f 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/DriverStationJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/DriverStationJNI.cpp
@@ -15,7 +15,27 @@
 #include "hal/FRCUsageReporting.h"
 #include "hal/HALBase.h"
 
-// TODO Static asserts
+static_assert(edu_wpi_first_hal_DriverStationJNI_kUnknownAllianceStation ==
+              HAL_AllianceStationID_kUnknown);
+static_assert(edu_wpi_first_hal_DriverStationJNI_kRed1AllianceStation ==
+              HAL_AllianceStationID_kRed1);
+static_assert(edu_wpi_first_hal_DriverStationJNI_kRed2AllianceStation ==
+              HAL_AllianceStationID_kRed2);
+static_assert(edu_wpi_first_hal_DriverStationJNI_kRed3AllianceStation ==
+              HAL_AllianceStationID_kRed3);
+static_assert(edu_wpi_first_hal_DriverStationJNI_kBlue1AllianceStation ==
+              HAL_AllianceStationID_kBlue1);
+static_assert(edu_wpi_first_hal_DriverStationJNI_kBlue2AllianceStation ==
+              HAL_AllianceStationID_kBlue2);
+static_assert(edu_wpi_first_hal_DriverStationJNI_kBlue3AllianceStation ==
+              HAL_AllianceStationID_kBlue3);
+
+static_assert(edu_wpi_first_hal_DriverStationJNI_kMaxJoystickAxes ==
+              HAL_kMaxJoystickAxes);
+static_assert(edu_wpi_first_hal_DriverStationJNI_kMaxJoystickPOVs ==
+              HAL_kMaxJoystickPOVs);
+static_assert(edu_wpi_first_hal_DriverStationJNI_kMaxJoysticks ==
+              HAL_kMaxJoysticks);
 
 using namespace hal;
 using namespace wpi::java;
@@ -231,10 +251,10 @@
 
   HAL_GetAllJoystickData(axes, povs, buttons);
 
-  CriticalJFloatArrayRef jAxes(env, axesArray);
-  CriticalJByteArrayRef jRawAxes(env, rawAxesArray);
-  CriticalJShortArrayRef jPovs(env, povsArray);
-  CriticalJLongArrayRef jButtons(env, buttonsAndMetadataArray);
+  CriticalJSpan<jfloat> jAxes(env, axesArray);
+  CriticalJSpan<jbyte> jRawAxes(env, rawAxesArray);
+  CriticalJSpan<jshort> jPovs(env, povsArray);
+  CriticalJSpan<jlong> jButtons(env, buttonsAndMetadataArray);
 
   static_assert(sizeof(jAxes[0]) == sizeof(axes[0].axes[0]));
   static_assert(sizeof(jRawAxes[0]) == sizeof(axes[0].raw[0]));
@@ -403,13 +423,13 @@
 /*
  * Class:     edu_wpi_first_hal_DriverStationJNI
  * Method:    refreshDSData
- * Signature: ()V
+ * Signature: ()Z
  */
-JNIEXPORT void JNICALL
+JNIEXPORT jboolean JNICALL
 Java_edu_wpi_first_hal_DriverStationJNI_refreshDSData
   (JNIEnv*, jclass)
 {
-  HAL_RefreshDSData();
+  return HAL_RefreshDSData();
 }
 
 /*
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/HAL.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/HAL.cpp
index b603a76..6f486e5 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/HAL.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/HAL.cpp
@@ -138,6 +138,36 @@
 
 /*
  * Class:     edu_wpi_first_hal_HAL
+ * Method:    getRSLState
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_hal_HAL_getRSLState
+  (JNIEnv* env, jclass)
+{
+  int32_t status = 0;
+  bool val = HAL_GetRSLState(&status);
+  CheckStatus(env, status);
+  return val;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_HAL
+ * Method:    getSystemTimeValid
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_hal_HAL_getSystemTimeValid
+  (JNIEnv* env, jclass)
+{
+  int32_t status = 0;
+  bool val = HAL_GetSystemTimeValid(&status);
+  CheckStatus(env, status);
+  return val;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_HAL
  * Method:    getPortWithModule
  * Signature: (BB)I
  */
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/HALUtil.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/HALUtil.cpp
index 76c05f8..6316a8c 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/HALUtil.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/HALUtil.cpp
@@ -486,6 +486,18 @@
 
 /*
  * Class:     edu_wpi_first_hal_HALUtil
+ * Method:    getTeamNumber
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_HALUtil_getTeamNumber
+  (JNIEnv* env, jclass)
+{
+  return HAL_GetTeamNumber();
+}
+
+/*
+ * Class:     edu_wpi_first_hal_HALUtil
  * Method:    getFPGATime
  * Signature: ()J
  */
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/I2CJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/I2CJNI.cpp
index 68b8442..6e06bb8 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/I2CJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/I2CJNI.cpp
@@ -79,7 +79,7 @@
   jint returnValue =
       HAL_TransactionI2C(static_cast<HAL_I2CPort>(port), address,
                          reinterpret_cast<const uint8_t*>(
-                             JByteArrayRef(env, dataToSend).array().data()),
+                             JSpan<const jbyte>(env, dataToSend).data()),
                          sendSize, recvBuf.data(), receiveSize);
   env->SetByteArrayRegion(dataReceived, 0, receiveSize,
                           reinterpret_cast<const jbyte*>(recvBuf.data()));
@@ -120,7 +120,7 @@
   jint returnValue =
       HAL_WriteI2C(static_cast<HAL_I2CPort>(port), address,
                    reinterpret_cast<const uint8_t*>(
-                       JByteArrayRef(env, dataToSend).array().data()),
+                       JSpan<const jbyte>(env, dataToSend).data()),
                    sendSize);
   return returnValue;
 }
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/PWMJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/PWMJNI.cpp
index e83f11b..2f90ded 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/PWMJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/PWMJNI.cpp
@@ -63,43 +63,27 @@
 
 /*
  * Class:     edu_wpi_first_hal_PWMJNI
- * Method:    setPWMConfigRaw
+ * Method:    setPWMConfigMicroseconds
  * Signature: (IIIIII)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_PWMJNI_setPWMConfigRaw
+Java_edu_wpi_first_hal_PWMJNI_setPWMConfigMicroseconds
   (JNIEnv* env, jclass, jint id, jint maxPwm, jint deadbandMaxPwm,
    jint centerPwm, jint deadbandMinPwm, jint minPwm)
 {
   int32_t status = 0;
-  HAL_SetPWMConfigRaw((HAL_DigitalHandle)id, maxPwm, deadbandMaxPwm, centerPwm,
-                      deadbandMinPwm, minPwm, &status);
+  HAL_SetPWMConfigMicroseconds((HAL_DigitalHandle)id, maxPwm, deadbandMaxPwm,
+                               centerPwm, deadbandMinPwm, minPwm, &status);
   CheckStatus(env, status);
 }
 
 /*
  * Class:     edu_wpi_first_hal_PWMJNI
- * Method:    setPWMConfig
- * Signature: (IDDDDD)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_PWMJNI_setPWMConfig
-  (JNIEnv* env, jclass, jint id, jdouble maxPwm, jdouble deadbandMaxPwm,
-   jdouble centerPwm, jdouble deadbandMinPwm, jdouble minPwm)
-{
-  int32_t status = 0;
-  HAL_SetPWMConfig((HAL_DigitalHandle)id, maxPwm, deadbandMaxPwm, centerPwm,
-                   deadbandMinPwm, minPwm, &status);
-  CheckStatus(env, status);
-}
-
-/*
- * Class:     edu_wpi_first_hal_PWMJNI
- * Method:    getPWMConfigRaw
+ * Method:    getPWMConfigMicroseconds
  * Signature: (I)Ljava/lang/Object;
  */
 JNIEXPORT jobject JNICALL
-Java_edu_wpi_first_hal_PWMJNI_getPWMConfigRaw
+Java_edu_wpi_first_hal_PWMJNI_getPWMConfigMicroseconds
   (JNIEnv* env, jclass, jint id)
 {
   int32_t status = 0;
@@ -108,8 +92,8 @@
   int32_t centerPwm = 0;
   int32_t deadbandMinPwm = 0;
   int32_t minPwm = 0;
-  HAL_GetPWMConfigRaw((HAL_DigitalHandle)id, &maxPwm, &deadbandMaxPwm,
-                      &centerPwm, &deadbandMinPwm, &minPwm, &status);
+  HAL_GetPWMConfigMicroseconds((HAL_DigitalHandle)id, &maxPwm, &deadbandMaxPwm,
+                               &centerPwm, &deadbandMinPwm, &minPwm, &status);
   CheckStatus(env, status);
   return CreatePWMConfigDataResult(env, maxPwm, deadbandMaxPwm, centerPwm,
                                    deadbandMinPwm, minPwm);
@@ -146,15 +130,15 @@
 
 /*
  * Class:     edu_wpi_first_hal_PWMJNI
- * Method:    setPWMRaw
- * Signature: (IS)V
+ * Method:    setPulseTimeMicroseconds
+ * Signature: (II)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_PWMJNI_setPWMRaw
-  (JNIEnv* env, jclass, jint id, jshort value)
+Java_edu_wpi_first_hal_PWMJNI_setPulseTimeMicroseconds
+  (JNIEnv* env, jclass, jint id, jint value)
 {
   int32_t status = 0;
-  HAL_SetPWMRaw((HAL_DigitalHandle)id, value, &status);
+  HAL_SetPWMPulseTimeMicroseconds((HAL_DigitalHandle)id, value, &status);
   CheckStatus(env, status);
 }
 
@@ -188,15 +172,16 @@
 
 /*
  * Class:     edu_wpi_first_hal_PWMJNI
- * Method:    getPWMRaw
- * Signature: (I)S
+ * Method:    getPulseTimeMicroseconds
+ * Signature: (I)I
  */
-JNIEXPORT jshort JNICALL
-Java_edu_wpi_first_hal_PWMJNI_getPWMRaw
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_PWMJNI_getPulseTimeMicroseconds
   (JNIEnv* env, jclass, jint id)
 {
   int32_t status = 0;
-  jshort returnValue = HAL_GetPWMRaw((HAL_DigitalHandle)id, &status);
+  int32_t returnValue =
+      HAL_GetPWMPulseTimeMicroseconds((HAL_DigitalHandle)id, &status);
   CheckStatus(env, status);
   return returnValue;
 }
@@ -261,6 +246,20 @@
 
 /*
  * Class:     edu_wpi_first_hal_PWMJNI
+ * Method:    setAlwaysHighMode
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_PWMJNI_setAlwaysHighMode
+  (JNIEnv* env, jclass, jint id)
+{
+  int32_t status = 0;
+  HAL_SetPWMAlwaysHighMode((HAL_DigitalHandle)id, &status);
+  CheckStatus(env, status);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_PWMJNI
  * Method:    setPWMPeriodScale
  * Signature: (II)V
  */
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/PowerJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/PowerJNI.cpp
index 92b174d..2096205 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/PowerJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/PowerJNI.cpp
@@ -74,6 +74,20 @@
 
 /*
  * Class:     edu_wpi_first_hal_PowerJNI
+ * Method:    setUserEnabled6V
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_PowerJNI_setUserEnabled6V
+  (JNIEnv* env, jclass, jboolean enabled)
+{
+  int32_t status = 0;
+  HAL_SetUserRailEnabled6V(enabled, &status);
+  CheckStatus(env, status);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_PowerJNI
  * Method:    getUserActive6V
  * Signature: ()Z
  */
@@ -134,6 +148,20 @@
 
 /*
  * Class:     edu_wpi_first_hal_PowerJNI
+ * Method:    setUserEnabled5V
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_PowerJNI_setUserEnabled5V
+  (JNIEnv* env, jclass, jboolean enabled)
+{
+  int32_t status = 0;
+  HAL_SetUserRailEnabled5V(enabled, &status);
+  CheckStatus(env, status);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_PowerJNI
  * Method:    getUserActive5V
  * Signature: ()Z
  */
@@ -194,6 +222,20 @@
 
 /*
  * Class:     edu_wpi_first_hal_PowerJNI
+ * Method:    setUserEnabled3V3
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_PowerJNI_setUserEnabled3V3
+  (JNIEnv* env, jclass, jboolean enabled)
+{
+  int32_t status = 0;
+  HAL_SetUserRailEnabled3V3(enabled, &status);
+  CheckStatus(env, status);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_PowerJNI
  * Method:    getUserActive3V3
  * Signature: ()Z
  */
@@ -251,4 +293,19 @@
   return val;
 }
 
+/*
+ * Class:     edu_wpi_first_hal_PowerJNI
+ * Method:    getCPUTemp
+ * Signature: ()D
+ */
+JNIEXPORT jdouble JNICALL
+Java_edu_wpi_first_hal_PowerJNI_getCPUTemp
+  (JNIEnv* env, jclass)
+{
+  int32_t status = 0;
+  double val = HAL_GetCPUTemp(&status);
+  CheckStatus(env, status);
+  return val;
+}
+
 }  // extern "C"
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/SPIJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/SPIJNI.cpp
index 4f1e556..f8c5f7e 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/SPIJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/SPIJNI.cpp
@@ -94,7 +94,7 @@
   jint retVal =
       HAL_TransactionSPI(static_cast<HAL_SPIPort>(port),
                          reinterpret_cast<const uint8_t*>(
-                             JByteArrayRef(env, dataToSend).array().data()),
+                             JSpan<const jbyte>(env, dataToSend).data()),
                          recvBuf.data(), size);
   env->SetByteArrayRegion(dataReceived, 0, size,
                           reinterpret_cast<const jbyte*>(recvBuf.data()));
@@ -131,7 +131,7 @@
 {
   jint retVal = HAL_WriteSPI(static_cast<HAL_SPIPort>(port),
                              reinterpret_cast<const uint8_t*>(
-                                 JByteArrayRef(env, dataToSend).array().data()),
+                                 JSpan<const jbyte>(env, dataToSend).data()),
                              size);
   return retVal;
 }
@@ -356,12 +356,11 @@
 Java_edu_wpi_first_hal_SPIJNI_spiSetAutoTransmitData
   (JNIEnv* env, jclass, jint port, jbyteArray dataToSend, jint zeroSize)
 {
-  JByteArrayRef jarr(env, dataToSend);
+  JSpan<const jbyte> jarr(env, dataToSend);
   int32_t status = 0;
-  HAL_SetSPIAutoTransmitData(
-      static_cast<HAL_SPIPort>(port),
-      reinterpret_cast<const uint8_t*>(jarr.array().data()),
-      jarr.array().size(), zeroSize, &status);
+  HAL_SetSPIAutoTransmitData(static_cast<HAL_SPIPort>(port),
+                             reinterpret_cast<const uint8_t*>(jarr.data()),
+                             jarr.size(), zeroSize, &status);
   CheckStatus(env, status);
 }
 
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/SerialPortJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/SerialPortJNI.cpp
index e7f81aa..281e8ef 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/SerialPortJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/SerialPortJNI.cpp
@@ -261,11 +261,10 @@
   (JNIEnv* env, jclass, jint handle, jbyteArray dataToSend, jint size)
 {
   int32_t status = 0;
-  jint retVal =
-      HAL_WriteSerial(static_cast<HAL_SerialPortHandle>(handle),
-                      reinterpret_cast<const char*>(
-                          JByteArrayRef(env, dataToSend).array().data()),
-                      size, &status);
+  jint retVal = HAL_WriteSerial(
+      static_cast<HAL_SerialPortHandle>(handle),
+      reinterpret_cast<const char*>(JSpan<const jbyte>(env, dataToSend).data()),
+      size, &status);
   CheckStatus(env, status);
   return retVal;
 }
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp
index f681e71..2cb6c9c 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp
@@ -65,6 +65,18 @@
 
 /*
  * Class:     edu_wpi_first_hal_SimDeviceJNI
+ * Method:    getSimDeviceName
+ * Signature: (I)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_edu_wpi_first_hal_SimDeviceJNI_getSimDeviceName
+  (JNIEnv* env, jclass, jint handle)
+{
+  return MakeJString(env, HAL_GetSimDeviceName(handle));
+}
+
+/*
+ * Class:     edu_wpi_first_hal_SimDeviceJNI
  * Method:    createSimValueNative
  * Signature: (ILjava/lang/String;IIJD)I
  */
@@ -138,7 +150,7 @@
   }
   return HAL_CreateSimValueEnumDouble(
       device, JStringRef{env, name}.c_str(), direction, len, carr.data(),
-      JDoubleArrayRef{env, optionValues}.array().data(), initialValue);
+      JSpan<const jdouble>{env, optionValues}.data(), initialValue);
 }
 
 /*
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp
index 70b31ab..38d9ce7 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp
@@ -269,7 +269,7 @@
 Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_setData
   (JNIEnv* env, jclass, jint index, jbyteArray arr)
 {
-  JByteArrayRef jArrRef{env, arr};
+  JSpan<const jbyte> jArrRef{env, arr};
   auto arrRef = jArrRef.array();
   HALSIM_SetAddressableLEDData(
       index, reinterpret_cast<const HAL_AddressableLEDData*>(arrRef.data()),
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp
index c50b8e3..8b1ba4f 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp
@@ -4,8 +4,7 @@
 
 #include <jni.h>
 
-#include <cstring>
-
+#include <wpi/StringExtras.h>
 #include <wpi/jni_util.h>
 
 #include "CallbackStore.h"
@@ -436,7 +435,7 @@
 {
   HAL_JoystickAxes axes;
   {
-    wpi::java::JFloatArrayRef jArrayRef(env, axesArray);
+    JSpan<const jfloat> jArrayRef(env, axesArray);
     auto arrayRef = jArrayRef.array();
     auto arraySize = arrayRef.size();
     int maxCount =
@@ -461,7 +460,7 @@
 {
   HAL_JoystickPOVs povs;
   {
-    wpi::java::JShortArrayRef jArrayRef(env, povsArray);
+    JSpan<const jshort> jArrayRef(env, povsArray);
     auto arrayRef = jArrayRef.array();
     auto arraySize = arrayRef.size();
     int maxCount =
@@ -539,11 +538,12 @@
   JStringRef gameSpecificMessageRef{env, gameSpecificMessage};
 
   HAL_MatchInfo halMatchInfo;
-  std::snprintf(halMatchInfo.eventName, sizeof(halMatchInfo.eventName), "%s",
-                eventNameRef.c_str());
-  std::snprintf(reinterpret_cast<char*>(halMatchInfo.gameSpecificMessage),
-                sizeof(halMatchInfo.gameSpecificMessage), "%s",
-                gameSpecificMessageRef.c_str());
+  wpi::format_to_n_c_str(halMatchInfo.eventName, sizeof(halMatchInfo.eventName),
+                         "{}", eventNameRef.str());
+  wpi::format_to_n_c_str(
+      reinterpret_cast<char*>(halMatchInfo.gameSpecificMessage),
+      sizeof(halMatchInfo.gameSpecificMessage), "{}",
+      gameSpecificMessageRef.str());
   halMatchInfo.gameSpecificMessageSize = gameSpecificMessageRef.size();
   halMatchInfo.matchType = (HAL_MatchType)matchType;
   halMatchInfo.matchNumber = matchNumber;
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/EncoderDataJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/EncoderDataJNI.cpp
index 69549a1..a33458f 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/EncoderDataJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/EncoderDataJNI.cpp
@@ -414,6 +414,56 @@
 
 /*
  * Class:     edu_wpi_first_hal_simulation_EncoderDataJNI
+ * Method:    registerDistancePerPulseCallback
+ * Signature: (ILjava/lang/Object;Z)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_simulation_EncoderDataJNI_registerDistancePerPulseCallback
+  (JNIEnv* env, jclass, jint index, jobject callback, jboolean initialNotify)
+{
+  return sim::AllocateCallback(env, index, callback, initialNotify,
+                               &HALSIM_RegisterEncoderDistancePerPulseCallback);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_EncoderDataJNI
+ * Method:    cancelDistancePerPulseCallback
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_simulation_EncoderDataJNI_cancelDistancePerPulseCallback
+  (JNIEnv* env, jclass, jint index, jint handle)
+{
+  return sim::FreeCallback(env, handle, index,
+                           &HALSIM_CancelEncoderDistancePerPulseCallback);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_EncoderDataJNI
+ * Method:    getDistancePerPulse
+ * Signature: (I)D
+ */
+JNIEXPORT jdouble JNICALL
+Java_edu_wpi_first_hal_simulation_EncoderDataJNI_getDistancePerPulse
+  (JNIEnv*, jclass, jint index)
+{
+  return HALSIM_GetEncoderDistancePerPulse(index);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_EncoderDataJNI
+ * Method:    setDistancePerPulse
+ * Signature: (ID)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_simulation_EncoderDataJNI_setDistancePerPulse
+  (JNIEnv*, jclass, jint index, jdouble value)
+{
+  HALSIM_SetEncoderDistancePerPulse(index, value);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_EncoderDataJNI
  * Method:    setDistance
  * Signature: (ID)V
  */
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/PWMDataJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/PWMDataJNI.cpp
index 31c79a7..ea52728 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/PWMDataJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/PWMDataJNI.cpp
@@ -64,52 +64,52 @@
 
 /*
  * Class:     edu_wpi_first_hal_simulation_PWMDataJNI
- * Method:    registerRawValueCallback
+ * Method:    registerPulseMicrosecondCallback
  * Signature: (ILjava/lang/Object;Z)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_simulation_PWMDataJNI_registerRawValueCallback
+Java_edu_wpi_first_hal_simulation_PWMDataJNI_registerPulseMicrosecondCallback
   (JNIEnv* env, jclass, jint index, jobject callback, jboolean initialNotify)
 {
   return sim::AllocateCallback(env, index, callback, initialNotify,
-                               &HALSIM_RegisterPWMRawValueCallback);
+                               &HALSIM_RegisterPWMPulseMicrosecondCallback);
 }
 
 /*
  * Class:     edu_wpi_first_hal_simulation_PWMDataJNI
- * Method:    cancelRawValueCallback
+ * Method:    cancelPulseMicrosecondCallback
  * Signature: (II)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_simulation_PWMDataJNI_cancelRawValueCallback
+Java_edu_wpi_first_hal_simulation_PWMDataJNI_cancelPulseMicrosecondCallback
   (JNIEnv* env, jclass, jint index, jint handle)
 {
   return sim::FreeCallback(env, handle, index,
-                           &HALSIM_CancelPWMRawValueCallback);
+                           &HALSIM_CancelPWMPulseMicrosecondCallback);
 }
 
 /*
  * Class:     edu_wpi_first_hal_simulation_PWMDataJNI
- * Method:    getRawValue
+ * Method:    getPulseMicrosecond
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_simulation_PWMDataJNI_getRawValue
+Java_edu_wpi_first_hal_simulation_PWMDataJNI_getPulseMicrosecond
   (JNIEnv*, jclass, jint index)
 {
-  return HALSIM_GetPWMRawValue(index);
+  return HALSIM_GetPWMPulseMicrosecond(index);
 }
 
 /*
  * Class:     edu_wpi_first_hal_simulation_PWMDataJNI
- * Method:    setRawValue
+ * Method:    setPulseMicrosecond
  * Signature: (II)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_simulation_PWMDataJNI_setRawValue
+Java_edu_wpi_first_hal_simulation_PWMDataJNI_setPulseMicrosecond
   (JNIEnv*, jclass, jint index, jint value)
 {
-  HALSIM_SetPWMRawValue(index, value);
+  HALSIM_SetPWMPulseMicrosecond(index, value);
 }
 
 /*
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp
index 5bcd114..9555f59 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp
@@ -830,6 +830,106 @@
 
 /*
  * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    registerCPUTempCallback
+ * Signature: (Ljava/lang/Object;Z)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_registerCPUTempCallback
+  (JNIEnv* env, jclass, jobject callback, jboolean initialNotify)
+{
+  return sim::AllocateCallbackNoIndex(env, callback, initialNotify,
+                                      &HALSIM_RegisterRoboRioCPUTempCallback);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    cancelCPUTempCallback
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_cancelCPUTempCallback
+  (JNIEnv* env, jclass, jint handle)
+{
+  return sim::FreeCallbackNoIndex(env, handle,
+                                  &HALSIM_CancelRoboRioCPUTempCallback);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    getCPUTemp
+ * Signature: ()D
+ */
+JNIEXPORT jdouble JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_getCPUTemp
+  (JNIEnv*, jclass)
+{
+  return HALSIM_GetRoboRioCPUTemp();
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    setCPUTemp
+ * Signature: (D)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_setCPUTemp
+  (JNIEnv*, jclass, jdouble cpuTemp)
+{
+  HALSIM_SetRoboRioCPUTemp(cpuTemp);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    registerTeamNumberCallback
+ * Signature: (Ljava/lang/Object;Z)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_registerTeamNumberCallback
+  (JNIEnv* env, jclass, jobject callback, jboolean initialNotify)
+{
+  return sim::AllocateCallbackNoIndex(
+      env, callback, initialNotify, &HALSIM_RegisterRoboRioTeamNumberCallback);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    cancelTeamNumberCallback
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_cancelTeamNumberCallback
+  (JNIEnv* env, jclass, jint handle)
+{
+  return sim::FreeCallbackNoIndex(env, handle,
+                                  &HALSIM_CancelRoboRioTeamNumberCallback);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    getTeamNumber
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_getTeamNumber
+  (JNIEnv*, jclass)
+{
+  return HALSIM_GetRoboRioTeamNumber();
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    setTeamNumber
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_setTeamNumber
+  (JNIEnv*, jclass, jint value)
+{
+  HALSIM_SetRoboRioTeamNumber(value);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
  * Method:    getSerialNumber
  * Signature: ()Ljava/lang/String;
  */
diff --git a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp
index c7c2a19..98dab72 100644
--- a/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp
@@ -468,7 +468,11 @@
 Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_getSimDeviceName
   (JNIEnv* env, jclass, jint handle)
 {
-  return MakeJString(env, HALSIM_GetSimDeviceName(handle));
+  const char* name = HALSIM_GetSimDeviceName(handle);
+  if (!name) {
+    return nullptr;
+  }
+  return MakeJString(env, name);
 }
 
 /*
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/AddressableLED.h b/third_party/allwpilib/hal/src/main/native/include/hal/AddressableLED.h
index 7674979..1fba59c 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/AddressableLED.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/AddressableLED.h
@@ -19,36 +19,112 @@
 extern "C" {
 #endif
 
+/**
+ * Initialize Addressable LED using a PWM Digital handle.
+ *
+ * @param[in] outputPort handle of the digital port for PWM
+ * @param[out] status the error code, or 0 for success
+ * @return Addressable LED handle
+ */
 HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
     HAL_DigitalHandle outputPort, int32_t* status);
 
+/**
+ * Free the Addressable LED Handle.
+ *
+ * @param[in] handle the Addressable LED handle to free
+ */
 void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle);
 
+/**
+ * Set the Addressable LED PWM Digital port.
+ *
+ * @param[in] handle the Addressable LED handle
+ * @param[in] outputPort The digital handle of the PWM port
+ * @param[out] status the error code, or 0 for success
+ */
 void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle,
                                      HAL_DigitalHandle outputPort,
                                      int32_t* status);
 
+/**
+ * Sets the length of the LED strip.
+ *
+ * <p>The max length is 5460 LEDs.
+ *
+ * @param[in] handle the Addressable LED handle
+ * @param[in] length the strip length
+ * @param[out] status the error code, or 0 for success
+ */
 void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle,
                                  int32_t length, int32_t* status);
 
+/**
+ * Sets the led output data.
+ *
+ * <p>If the output is enabled, this will start writing the next data cycle.
+ * It is safe to call, even while output is enabled.
+ *
+ * @param[in] handle the Addressable LED handle
+ * @param[in] data the buffer to write
+ * @param[in] length the strip length
+ * @param[out] status the error code, or 0 for success
+ */
 void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
                                  const struct HAL_AddressableLEDData* data,
                                  int32_t length, int32_t* status);
 
+/**
+ * Sets the bit timing.
+ *
+ * <p>By default, the driver is set up to drive WS2812Bs, so nothing needs to
+ * be set for those.
+ *
+ * @param[in] handle the Addressable LED handle
+ * @param[in] highTime0NanoSeconds high time for 0 bit (default 400ns)
+ * @param[in] lowTime0NanoSeconds low time for 0 bit (default 900ns)
+ * @param[in] highTime1NanoSeconds high time for 1 bit (default 900ns)
+ * @param[in] lowTime1NanoSeconds low time for 1 bit (default 600ns)
+ * @param[out] status the error code, or 0 for success
+ */
 void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
-                                    int32_t lowTime0NanoSeconds,
                                     int32_t highTime0NanoSeconds,
-                                    int32_t lowTime1NanoSeconds,
+                                    int32_t lowTime0NanoSeconds,
                                     int32_t highTime1NanoSeconds,
+                                    int32_t lowTime1NanoSeconds,
                                     int32_t* status);
 
+/**
+ * Sets the sync time.
+ *
+ * <p>The sync time is the time to hold output so LEDs enable. Default set for
+ * WS2812B.
+ *
+ * @param[in] handle the Addressable LED handle
+ * @param[in] syncTimeMicroSeconds the sync time (default 280us)
+ * @param[out] status the error code, or 0 for success
+ */
 void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle,
                                    int32_t syncTimeMicroSeconds,
                                    int32_t* status);
 
+/**
+ * Starts the output.
+ *
+ * <p>The output writes continuously.
+ *
+ * @param[in] handle the Addressable LED handle
+ * @param[out] status the error code, or 0 for success
+ */
 void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle,
                                    int32_t* status);
 
+/**
+ * Stops the output.
+ *
+ * @param[in] handle the Addressable LED handle
+ * @param[out] status the error code, or 0 for success
+ */
 void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle,
                                   int32_t* status);
 
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/AddressableLEDTypes.h b/third_party/allwpilib/hal/src/main/native/include/hal/AddressableLEDTypes.h
index dbaa776..48918f4 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/AddressableLEDTypes.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/AddressableLEDTypes.h
@@ -6,11 +6,13 @@
 
 #include <stdint.h>
 
+/** max length of LED strip supported by FPGA. */
 #define HAL_kAddressableLEDMaxLength 5460
 
+/** structure for holding one LED's color data. */
 struct HAL_AddressableLEDData {
-  uint8_t b;
-  uint8_t g;
-  uint8_t r;
+  uint8_t b;  ///< blue value
+  uint8_t g;  ///< green value
+  uint8_t r;  ///< red value
   uint8_t padding;
 };
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/AnalogGyro.h b/third_party/allwpilib/hal/src/main/native/include/hal/AnalogGyro.h
index ed7c308..6dd948a 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/AnalogGyro.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/AnalogGyro.h
@@ -21,7 +21,7 @@
 /**
  * Initializes an analog gyro.
  *
- * @param[in] handle handle to the analog port
+ * @param[in] handle handle to the analog input port
  * @param[in] allocationLocation the location where the allocation is occurring
  *                                (can be null)
  * @param[out] status the error code, or 0 for success
@@ -43,7 +43,7 @@
 /**
  * Frees an analog gyro.
  *
- * @param handle the gyro handle
+ * @param[in,out] handle the gyro handle
  */
 void HAL_FreeAnalogGyro(HAL_GyroHandle handle);
 
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/AnalogInput.h b/third_party/allwpilib/hal/src/main/native/include/hal/AnalogInput.h
index 0a1a3c5..462103a 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/AnalogInput.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/AnalogInput.h
@@ -33,14 +33,14 @@
 /**
  * Frees an analog input port.
  *
- * @param analogPortHandle Handle to the analog port.
+ * @param[in,out] analogPortHandle Handle to the analog port.
  */
 void HAL_FreeAnalogInputPort(HAL_AnalogInputHandle analogPortHandle);
 
 /**
  * Checks that the analog module number is valid.
  *
- * @param module The analog module number.
+ * @param[in] module The analog module number.
  * @return Analog module is valid and present
  */
 HAL_Bool HAL_CheckAnalogModule(int32_t module);
@@ -50,7 +50,7 @@
  * Verifies that the analog channel number is one of the legal channel numbers.
  * Channel numbers are 0-based.
  *
- * @param channel The analog output channel number.
+ * @param[in] channel The analog output channel number.
  * @return Analog channel is valid
  */
 HAL_Bool HAL_CheckAnalogInputChannel(int32_t channel);
@@ -162,9 +162,9 @@
  * The sample is 12-bit + the value configured in SetOversampleBits().
  * The value configured in SetAverageBits() will cause this value to be averaged
  * 2**bits number of samples. This is not a sliding window.  The sample will not
- * change until 2**(OversamplBits + AverageBits) samples have been acquired from
- * the module on this channel. Use GetAverageVoltage() to get the analog value
- * in calibrated units.
+ * change until 2**(OversampleBits + AverageBits) samples have been acquired
+ * from the module on this channel. Use GetAverageVoltage() to get the analog
+ * value in calibrated units.
  *
  * @param[in] analogPortHandle Handle to the analog port to use.
  * @param[out] status the error code, or 0 for success
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/AnalogTrigger.h b/third_party/allwpilib/hal/src/main/native/include/hal/AnalogTrigger.h
index 72727a8..f4622e3 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/AnalogTrigger.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/AnalogTrigger.h
@@ -45,6 +45,7 @@
  *
  * @param[in] dutyCycleHandle the analog input to use for duty cycle
  * @param[out] status          Error status variable. 0 on success.
+ * @return tbe created analog trigger handle
  */
 HAL_AnalogTriggerHandle HAL_InitializeAnalogTriggerDutyCycle(
     HAL_DutyCycleHandle dutyCycleHandle, int32_t* status);
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/CANAPI.h b/third_party/allwpilib/hal/src/main/native/include/hal/CANAPI.h
index d5244b5..f515d60 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/CANAPI.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/CANAPI.h
@@ -20,6 +20,15 @@
 #endif
 
 /**
+ * Reads the current value of the millisecond-resolution timer that the CAN API
+ * functions use as a time base.
+ *
+ * @return Current value of timer used as a base time by the CAN API in
+ * milliseconds.
+ */
+uint32_t HAL_GetCANPacketBaseTime(void);
+
+/**
  * Initializes a CAN device.
  *
  * These follow the FIRST standard CAN layout.
@@ -38,7 +47,7 @@
 /**
  * Frees a CAN device
  *
- * @param handle the CAN handle
+ * @param[in,out] handle the CAN handle
  */
 void HAL_CleanCAN(HAL_CANHandle handle);
 
@@ -111,8 +120,8 @@
  * @param[in] apiId              the ID to read (0-1023)
  * @param[out] data              the packet data (8 bytes)
  * @param[out] length            the received length (0-8 bytes)
- * @param[out] receivedTimestamp the packet received timestamp (based off of
- *                               CLOCK_MONOTONIC)
+ * @param[out] receivedTimestamp the packet received timestamp in ms (based off
+ *                               of CLOCK_MONOTONIC)
  * @param[out] status            Error status variable. 0 on success.
  */
 void HAL_ReadCANPacketNew(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
@@ -127,8 +136,8 @@
  * @param[in] apiId              the ID to read (0-1023)
  * @param[out] data              the packet data (8 bytes)
  * @param[out] length            the received length (0-8 bytes)
- * @param[out] receivedTimestamp the packet received timestamp (based off of
- *                               CLOCK_MONOTONIC)
+ * @param[out] receivedTimestamp the packet received timestamp in ms (based off
+ *                               of CLOCK_MONOTONIC)
  * @param[out] status            Error status variable. 0 on success.
  */
 void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
@@ -144,8 +153,8 @@
  * @param[in] apiId              the ID to read (0-1023)
  * @param[out] data              the packet data (8 bytes)
  * @param[out] length            the received length (0-8 bytes)
- * @param[out] receivedTimestamp the packet received timestamp (based off of
- *                               CLOCK_MONOTONIC)
+ * @param[out] receivedTimestamp the packet received timestamp in ms (based off
+ *                               of CLOCK_MONOTONIC)
  * @param[out] timeoutMs         the timeout time for the packet
  * @param[out] status            Error status variable. 0 on success.
  */
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/CANAPITypes.h b/third_party/allwpilib/hal/src/main/native/include/hal/CANAPITypes.h
index 404eecf..decaf86 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/CANAPITypes.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/CANAPITypes.h
@@ -55,7 +55,10 @@
   HAL_CAN_Man_kCopperforge = 10,
   HAL_CAN_Man_kPWF = 11,
   HAL_CAN_Man_kStudica = 12,
-  HAL_CAN_Man_kTheThriftyBot = 13
+  HAL_CAN_Man_kTheThriftyBot = 13,
+  HAL_CAN_Man_kReduxRobotics = 14,
+  HAL_CAN_Man_kAndyMark = 15,
+  HAL_CAN_Man_kVividHosting = 16
 };
 // clang-format on
 /** @} */
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/CTREPCM.h b/third_party/allwpilib/hal/src/main/native/include/hal/CTREPCM.h
index 1bed928..932ed34 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/CTREPCM.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/CTREPCM.h
@@ -9,7 +9,7 @@
 #include "hal/Types.h"
 
 /**
- * @defgroup hal_ctre_pcm CTRE PCM Functions
+ * @defgroup hal_ctre_pcm CTRE Pneumatic Control Module (PCM) Functions
  * @ingroup hal_capi
  * @{
  */
@@ -18,51 +18,241 @@
 extern "C" {
 #endif
 
+/**
+ * Initializes a PCM.
+ *
+ * @param[in] module             the CAN ID to initialize
+ * @param[in] allocationLocation the location where the allocation is occurring
+ *                               (can be null)
+ * @param[out] status            Error status variable. 0 on success.
+ * @return the created PH handle
+ */
 HAL_CTREPCMHandle HAL_InitializeCTREPCM(int32_t module,
                                         const char* allocationLocation,
                                         int32_t* status);
+
+/**
+ * Frees a PCM handle.
+ *
+ * @param[in] handle the PCMhandle
+ */
 void HAL_FreeCTREPCM(HAL_CTREPCMHandle handle);
 
+/**
+ * Checks if a solenoid channel number is valid.
+ *
+ * @param[in] channel the channel to check
+ * @return true if the channel is valid, otherwise false
+ */
 HAL_Bool HAL_CheckCTREPCMSolenoidChannel(int32_t channel);
 
+/**
+ * Get whether compressor is turned on.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return true if the compressor is turned on
+ */
 HAL_Bool HAL_GetCTREPCMCompressor(HAL_CTREPCMHandle handle, int32_t* status);
+
+/**
+ * Enables the compressor closed loop control using the digital pressure switch.
+ * The compressor will turn on when the pressure switch indicates that the
+ * system is not full, and will turn off when the pressure switch indicates that
+ * the system is full.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[in] enabled true to enable closed loop control
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_SetCTREPCMClosedLoopControl(HAL_CTREPCMHandle handle, HAL_Bool enabled,
                                      int32_t* status);
+
+/**
+ * Get whether the PCM closed loop control is enabled.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if closed loop control is enabled, otherwise false.
+ */
 HAL_Bool HAL_GetCTREPCMClosedLoopControl(HAL_CTREPCMHandle handle,
                                          int32_t* status);
+
+/**
+ * Returns the state of the pressure switch.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if pressure switch indicates that the system is full,
+ * otherwise false.
+ */
 HAL_Bool HAL_GetCTREPCMPressureSwitch(HAL_CTREPCMHandle handle,
                                       int32_t* status);
+
+/**
+ * Returns the current drawn by the compressor.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return The current drawn by the compressor in amps.
+ */
 double HAL_GetCTREPCMCompressorCurrent(HAL_CTREPCMHandle handle,
                                        int32_t* status);
 
+/**
+ * Return whether the compressor current is currently too high.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if the compressor current is too high, otherwise false.
+ * @see HAL_GetCTREPCMCompressorShortedStickyFault
+ */
 HAL_Bool HAL_GetCTREPCMCompressorCurrentTooHighFault(HAL_CTREPCMHandle handle,
                                                      int32_t* status);
+
+/**
+ * Returns whether the compressor current has been too high since sticky
+ * faults were last cleared. This fault is persistent and can be cleared by
+ * HAL_ClearAllCTREPCMStickyFaults()
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if the compressor current has been too high since sticky
+ * faults were last cleared.
+ * @see HAL_GetCTREPCMCompressorCurrentTooHighFault()
+ */
 HAL_Bool HAL_GetCTREPCMCompressorCurrentTooHighStickyFault(
     HAL_CTREPCMHandle handle, int32_t* status);
+
+/**
+ * Returns whether the compressor has been shorted since sticky faults were
+ * last cleared. This fault is persistent and can be cleared by
+ * HAL_ClearAllCTREPCMStickyFaults()
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if the compressor has been shorted since sticky faults were
+ * last cleared, otherwise false.
+ * @see HAL_GetCTREPCMCompressorShortedFault()
+ */
 HAL_Bool HAL_GetCTREPCMCompressorShortedStickyFault(HAL_CTREPCMHandle handle,
                                                     int32_t* status);
+
+/**
+ * Returns whether the compressor is currently shorted.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if the compressor is currently shorted, otherwise false.
+ * @see HAL_GetCTREPCMCompressorShortedStickyFault
+ */
 HAL_Bool HAL_GetCTREPCMCompressorShortedFault(HAL_CTREPCMHandle handle,
                                               int32_t* status);
+
+/**
+ * Returns whether the compressor has been disconnected since sticky faults
+ * were last cleared. This fault is persistent and can be cleared by
+ * HAL_ClearAllCTREPCMStickyFaults()
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if the compressor has been disconnected since sticky faults
+ * were last cleared, otherwise false.
+ * @see HAL_GetCTREPCMCompressorShortedFault()
+ */
 HAL_Bool HAL_GetCTREPCMCompressorNotConnectedStickyFault(
     HAL_CTREPCMHandle handle, int32_t* status);
+
+/**
+ * Returns whether the compressor is currently disconnected.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if compressor is currently disconnected, otherwise false.
+ * @see HAL_GetCTREPCMCompressorNotConnectedStickyFault()
+ */
 HAL_Bool HAL_GetCTREPCMCompressorNotConnectedFault(HAL_CTREPCMHandle handle,
                                                    int32_t* status);
 
+/**
+ * Gets a bitmask of solenoid values.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return solenoid values
+ */
 int32_t HAL_GetCTREPCMSolenoids(HAL_CTREPCMHandle handle, int32_t* status);
+
+/**
+ * Sets solenoids on a pneumatics module.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[in] mask bitmask to set
+ * @param[in] values solenoid values
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_SetCTREPCMSolenoids(HAL_CTREPCMHandle handle, int32_t mask,
                              int32_t values, int32_t* status);
 
+/**
+ * Get a bitmask of disabled solenoids.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return bitmask of disabled solenoids
+ */
 int32_t HAL_GetCTREPCMSolenoidDisabledList(HAL_CTREPCMHandle handle,
                                            int32_t* status);
+
+/**
+ * Returns whether the solenoid has reported a voltage fault since sticky faults
+ * were last cleared. This fault is persistent and can be cleared by
+ * HAL_ClearAllCTREPCMStickyFaults()
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if solenoid is reporting a fault, otherwise false.
+ * @see HAL_GetCTREPCMSolenoidVoltageFault()
+ */
 HAL_Bool HAL_GetCTREPCMSolenoidVoltageStickyFault(HAL_CTREPCMHandle handle,
                                                   int32_t* status);
+/**
+ * Returns whether the solenoid is currently reporting a voltage fault.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if solenoid is reporting a fault, otherwise false.
+ * @see HAL_GetCTREPCMSolenoidVoltageStickyFault()
+ */
 HAL_Bool HAL_GetCTREPCMSolenoidVoltageFault(HAL_CTREPCMHandle handle,
                                             int32_t* status);
 
+/**
+ * Clears all sticky faults on this device.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_ClearAllCTREPCMStickyFaults(HAL_CTREPCMHandle handle, int32_t* status);
 
+/**
+ * Fire a single solenoid shot.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[in] index solenoid index
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_FireCTREPCMOneShot(HAL_CTREPCMHandle handle, int32_t index,
                             int32_t* status);
+
+/**
+ * Set the duration for a single solenoid shot.
+ *
+ * @param[in] handle  the PCM handle
+ * @param[in] index solenoid index
+ * @param[in] durMs shot duration in ms
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_SetCTREPCMOneShotDuration(HAL_CTREPCMHandle handle, int32_t index,
                                    int32_t durMs, int32_t* status);
 
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/Counter.h b/third_party/allwpilib/hal/src/main/native/include/hal/Counter.h
index cc2d776..0338cda 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/Counter.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/Counter.h
@@ -233,7 +233,7 @@
  */
 int32_t HAL_GetCounter(HAL_CounterHandle counterHandle, int32_t* status);
 
-/*
+/**
  * Gets the Period of the most recent count.
  *
  * Returns the time interval of the most recent count. This can be used for
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/DIO.h b/third_party/allwpilib/hal/src/main/native/include/hal/DIO.h
index 6b922b5..f531384 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/DIO.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/DIO.h
@@ -37,7 +37,7 @@
  * Checks if a DIO channel is valid.
  *
  * @param channel the channel number to check
- * @return true if the channel is correct, otherwise false
+ * @return true if the channel is valid, otherwise false
  */
 HAL_Bool HAL_CheckDIOChannel(int32_t channel);
 
@@ -77,7 +77,7 @@
  *
  * The valid range is from 0.6 Hz to 19 kHz.
  *
- *  The frequency resolution is logarithmic.
+ * The frequency resolution is logarithmic.
  *
  * @param[in] rate the frequency to output all digital output PWM signals
  * @param[out] status Error status variable. 0 on success.
@@ -227,7 +227,7 @@
  *
  * Sets the filter period in FPGA cycles.  Even though there are 2 different
  * filter index domains (MXP vs HDR), ignore that distinction for now since it
- * compilicates the interface.  That can be changed later.
+ * complicates the interface.  That can be changed later.
  *
  * @param[in] filterIndex the filter index, 0 - 2
  * @param[in] value       the number of cycles that the signal must not
@@ -241,11 +241,12 @@
  *
  * Gets the filter period in FPGA cycles.  Even though there are 2 different
  * filter index domains (MXP vs HDR), ignore that distinction for now since it
- * compilicates the interface.  Set status to NiFpga_Status_SoftwareFault if the
- * filter values miss-match.
+ * complicates the interface.  Set status to NiFpga_Status_SoftwareFault if the
+ * filter values mismatch.
  *
  * @param[in] filterIndex the filter index, 0 - 2
  * @param[out] status     Error status variable. 0 on success.
+ * @return                The number of FPGA cycles of the filter period.
  */
 int64_t HAL_GetFilterPeriod(int32_t filterIndex, int32_t* status);
 #ifdef __cplusplus
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/DMA.h b/third_party/allwpilib/hal/src/main/native/include/hal/DMA.h
index 7610d9b..b735b6d 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/DMA.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/DMA.h
@@ -40,7 +40,7 @@
 #endif
 
 /**
- * Initializes an object for peforming DMA transfers.
+ * Initializes an object for performing DMA transfers.
  *
  * @param[out] status Error status variable. 0 on success.
  * @return the created dma handle
@@ -185,7 +185,7 @@
                                    int32_t* status);
 
 /**
- * Adds acuumulator data of an analog input to be collected by DMA.
+ * Adds accumulator data of an analog input to be collected by DMA.
  *
  * This can only be called if DMA is not started.
  *
@@ -310,7 +310,7 @@
  *                           timing out
  * @param[in] remainingOut   the number of samples remaining in the queue
  * @param[out] status        Error status variable. 0 on success.
- * @return the succes result of the sample read
+ * @return the success result of the sample read
  */
 enum HAL_DMAReadStatus HAL_ReadDMA(HAL_DMAHandle handle,
                                    HAL_DMASample* dmaSample,
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/DriverStation.h b/third_party/allwpilib/hal/src/main/native/include/hal/DriverStation.h
index ae68b65..5edacf5 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/DriverStation.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/DriverStation.h
@@ -28,10 +28,11 @@
  * @param errorCode the error code
  * @param isLVCode  true for a LV error code, false for a standard error code
  * @param details   the details of the error
- * @param location  the file location of the errror
+ * @param location  the file location of the error
  * @param callStack the callstack of the error
  * @param printMsg  true to print the error message to stdout as well as to the
  * DS
+ * @return the error code, or 0 for success
  */
 int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode, HAL_Bool isLVCode,
                       const char* details, const char* location,
@@ -48,13 +49,14 @@
  * Sends a line to the driver station console.
  *
  * @param line the line to send (null terminated)
+ * @return the error code, or 0 for success
  */
 int32_t HAL_SendConsoleLine(const char* line);
 
 /**
  * Gets the current control word of the driver station.
  *
- * The control work contains the robot state.
+ * The control word contains the robot state.
  *
  * @param controlWord the control word (out)
  * @return the error code, or 0 for success
@@ -117,7 +119,7 @@
                                   HAL_JoystickDescriptor* desc);
 
 /**
- * Gets is a specific joystick is considered to be an XBox controller.
+ * Gets whether a specific joystick is considered to be an XBox controller.
  *
  * @param joystickNum the joystick number
  * @return true if xbox, false otherwise
@@ -179,25 +181,32 @@
                                int32_t leftRumble, int32_t rightRumble);
 
 /**
- * Returns the approximate match time.
- *
- * The FMS does not send an official match time to the robots, but does send
- * an approximate match time. The value will count down the time remaining in
- * the current period (auto or teleop).
- *
+ * Return the approximate match time. The FMS does not send an official match
+ * time to the robots, but does send an approximate match time. The value will
+ * count down the time remaining in the current period (auto or teleop).
  * Warning: This is not an official time (so it cannot be used to dispute ref
  * calls or guarantee that a function will trigger before the match ends).
  *
- * The Practice Match function of the DS approximates the behavior seen on
- * the field.
+ * <p>When connected to the real field, this number only changes in full integer
+ * increments, and always counts down.
+ *
+ * <p>When the DS is in practice mode, this number is a floating point number,
+ * and counts down.
+ *
+ * <p>When the DS is in teleop or autonomous mode, this number is a floating
+ * point number, and counts up.
+ *
+ * <p>Simulation matches DS behavior without an FMS connected.
  *
  * @param[out] status the error code, or 0 for success
- * @return time remaining in current match period (auto or teleop)
+ * @return Time remaining in current match period (auto or teleop) in seconds
  */
 double HAL_GetMatchTime(int32_t* status);
 
 /**
  * Gets if outputs are enabled by the control system.
+ *
+ * @return true if outputs are enabled
  */
 HAL_Bool HAL_GetOutputsEnabled(void);
 
@@ -209,7 +218,12 @@
  */
 int32_t HAL_GetMatchInfo(HAL_MatchInfo* info);
 
-void HAL_RefreshDSData(void);
+/**
+ * Refresh the DS control word.
+ *
+ * @return true if updated
+ */
+HAL_Bool HAL_RefreshDSData(void);
 
 void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle);
 void HAL_RemoveNewDataEventHandle(WPI_EventHandle handle);
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/DriverStationTypes.h b/third_party/allwpilib/hal/src/main/native/include/hal/DriverStationTypes.h
index 277c286..5917c51 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/DriverStationTypes.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/DriverStationTypes.h
@@ -42,6 +42,7 @@
 
 // clang-format off
 HAL_ENUM(HAL_AllianceStationID) {
+  HAL_AllianceStationID_kUnknown = 0,
   HAL_AllianceStationID_kRed1,
   HAL_AllianceStationID_kRed2,
   HAL_AllianceStationID_kRed3,
@@ -51,7 +52,7 @@
 };
 
 HAL_ENUM(HAL_MatchType) {
-  HAL_kMatchType_none,
+  HAL_kMatchType_none = 0,
   HAL_kMatchType_practice,
   HAL_kMatchType_qualification,
   HAL_kMatchType_elimination,
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/DutyCycle.h b/third_party/allwpilib/hal/src/main/native/include/hal/DutyCycle.h
index 90266fd..d5e655b 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/DutyCycle.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/DutyCycle.h
@@ -26,7 +26,7 @@
  * @param[in] triggerType the analog trigger type of the source if it is
  *                         an analog trigger
  * @param[out] status Error status variable. 0 on success.
- * @return thre created duty cycle handle
+ * @return the created duty cycle handle
  */
 HAL_DutyCycleHandle HAL_InitializeDutyCycle(HAL_Handle digitalSourceHandle,
                                             HAL_AnalogTriggerType triggerType,
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/Encoder.h b/third_party/allwpilib/hal/src/main/native/include/hal/Encoder.h
index a6bf4af..c6e16a6 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/Encoder.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/Encoder.h
@@ -83,7 +83,7 @@
 /**
  * Gets the current counts of the encoder after encoding type scaling.
  *
- * This is scaled by the value passed duing initialization to encodingType.
+ * This is scaled by the value passed during initialization to encodingType.
  *
  * @param[in] encoderHandle the encoder handle
  * @param[out] status Error status variable. 0 on success.
@@ -125,7 +125,7 @@
  */
 void HAL_ResetEncoder(HAL_EncoderHandle encoderHandle, int32_t* status);
 
-/*
+/**
  * Gets the Period of the most recent count.
  *
  * Returns the time interval of the most recent count. This can be used for
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/Extensions.h b/third_party/allwpilib/hal/src/main/native/include/hal/Extensions.h
index 6f64fdb..b39cc36 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/Extensions.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/Extensions.h
@@ -31,7 +31,7 @@
  * Expected to be called internally, not by users.
  *
  * @param library the library path
- * @return the succes state of the initialization
+ * @return the success state of the initialization
  */
 int HAL_LoadOneExtension(const char* library);
 
@@ -39,7 +39,7 @@
  * Loads any extra halsim libraries provided in the HALSIM_EXTENSIONS
  * environment variable.
  *
- * @return the succes state of the initialization
+ * @return the success state of the initialization
  */
 int HAL_LoadExtensions(void);
 
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/HALBase.h b/third_party/allwpilib/hal/src/main/native/include/hal/HALBase.h
index 1fe6b3a..fe8d1bf 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/HALBase.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/HALBase.h
@@ -18,7 +18,7 @@
 
 /**
  * @defgroup hal_capi WPILib HAL API
- * Hardware Abstraction Layer to hardware or simulator
+ * Hardware Abstraction Layer (HAL) to hardware or simulator
  * @{
  */
 
@@ -75,9 +75,9 @@
 int64_t HAL_GetFPGARevision(int32_t* status);
 
 /**
- * Returns the serial number.
+ * Returns the roboRIO serial number.
  *
- * @param[out] buffer The serial number.
+ * @param[out] buffer The roboRIO serial number.
  * @param size The maximum characters to copy into buffer.
  * @return Number of characters copied into buffer.
  */
@@ -93,6 +93,12 @@
 size_t HAL_GetComments(char* buffer, size_t size);
 
 /**
+ * Returns the team number configured for the robot controller.
+ * @return team number, or 0 if not found.
+ */
+int32_t HAL_GetTeamNumber(void);
+
+/**
  * Returns the runtime type of the HAL.
  *
  * @return HAL Runtime Type
@@ -108,7 +114,7 @@
 HAL_Bool HAL_GetFPGAButton(int32_t* status);
 
 /**
- * Gets if the system outputs are currently active
+ * Gets if the system outputs are currently active.
  *
  * @param[out] status the error code, or 0 for success
  * @return true if the system outputs are active, false if disabled
@@ -173,6 +179,22 @@
 uint64_t HAL_ExpandFPGATime(uint32_t unexpandedLower, int32_t* status);
 
 /**
+ * Gets the current state of the Robot Signal Light (RSL).
+ *
+ * @param[out] status the error code, or 0 for success
+ * @return The current state of the RSL- true if on, false if off
+ */
+HAL_Bool HAL_GetRSLState(int32_t* status);
+
+/**
+ * Gets if the system time is valid.
+ *
+ * @param[out] status the error code, or 0 for success
+ * @return True if the system time is valid, false otherwise
+ */
+HAL_Bool HAL_GetSystemTimeValid(int32_t* status);
+
+/**
  * Call this to start up HAL. This is required for robot programs.
  *
  * This must be called before any other HAL functions. Failure to do so will
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/Main.h b/third_party/allwpilib/hal/src/main/native/include/hal/Main.h
index 712ad01..9133135 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/Main.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/Main.h
@@ -7,7 +7,7 @@
 #include "hal/Types.h"
 
 /**
- * @defgroup hal_relay Main loop functions
+ * @defgroup hal_main Main loop functions
  * @ingroup hal_capi
  * @{
  */
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/Notifier.h b/third_party/allwpilib/hal/src/main/native/include/hal/Notifier.h
index 96452f4..9b451e4 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/Notifier.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/Notifier.h
@@ -63,7 +63,8 @@
 /**
  * Stops a notifier from running.
  *
- * This will cause any call into HAL_WaitForNotifierAlarm to return.
+ * This will cause any call into HAL_WaitForNotifierAlarm to return with time =
+ * 0.
  *
  * @param[in] notifierHandle the notifier handle
  * @param[out] status Error status variable. 0 on success.
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/PWM.h b/third_party/allwpilib/hal/src/main/native/include/hal/PWM.h
index df9020b..d308f8e 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/PWM.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/PWM.h
@@ -50,7 +50,7 @@
 /**
  * Sets the configuration settings for the PWM channel.
  *
- * All values are in milliseconds.
+ * All values are in microseconds.
  *
  * @param[in] pwmPortHandle  the PWM handle
  * @param[in] maxPwm         the maximum PWM value
@@ -60,17 +60,15 @@
  * @param[in] minPwm         the minimum PWM value
  * @param[out] status        Error status variable. 0 on success.
  */
-void HAL_SetPWMConfig(HAL_DigitalHandle pwmPortHandle, double maxPwm,
-                      double deadbandMaxPwm, double centerPwm,
-                      double deadbandMinPwm, double minPwm, int32_t* status);
+void HAL_SetPWMConfigMicroseconds(HAL_DigitalHandle pwmPortHandle,
+                                  int32_t maxPwm, int32_t deadbandMaxPwm,
+                                  int32_t centerPwm, int32_t deadbandMinPwm,
+                                  int32_t minPwm, int32_t* status);
 
 /**
- * Sets the raw configuration settings for the PWM channel.
+ * Gets the pwm configuration settings for the PWM channel.
  *
- * We recommend using HAL_SetPWMConfig() instead, as those values are properly
- * scaled. Usually used for values grabbed by HAL_GetPWMConfigRaw().
- *
- * Values are in raw FPGA units.
+ * Values are in microseconds.
  *
  * @param[in] pwmPortHandle  the PWM handle
  * @param[in] maxPwm         the maximum PWM value
@@ -80,29 +78,10 @@
  * @param[in] minPwm         the minimum PWM value
  * @param[out] status        Error status variable. 0 on success.
  */
-void HAL_SetPWMConfigRaw(HAL_DigitalHandle pwmPortHandle, int32_t maxPwm,
-                         int32_t deadbandMaxPwm, int32_t centerPwm,
-                         int32_t deadbandMinPwm, int32_t minPwm,
-                         int32_t* status);
-
-/**
- * Gets the raw pwm configuration settings for the PWM channel.
- *
- * Values are in raw FPGA units. These units have the potential to change for
- * any FPGA release.
- *
- * @param[in] pwmPortHandle  the PWM handle
- * @param[in] maxPwm         the maximum PWM value
- * @param[in] deadbandMaxPwm the high range of the center deadband
- * @param[in] centerPwm      the center PWM value
- * @param[in] deadbandMinPwm the low range of the center deadband
- * @param[in] minPwm         the minimum PWM value
- * @param[out] status        Error status variable. 0 on success.
- */
-void HAL_GetPWMConfigRaw(HAL_DigitalHandle pwmPortHandle, int32_t* maxPwm,
-                         int32_t* deadbandMaxPwm, int32_t* centerPwm,
-                         int32_t* deadbandMinPwm, int32_t* minPwm,
-                         int32_t* status);
+void HAL_GetPWMConfigMicroseconds(HAL_DigitalHandle pwmPortHandle,
+                                  int32_t* maxPwm, int32_t* deadbandMaxPwm,
+                                  int32_t* centerPwm, int32_t* deadbandMinPwm,
+                                  int32_t* minPwm, int32_t* status);
 
 /**
  * Sets if the FPGA should output the center value if the input value is within
@@ -126,17 +105,16 @@
                                      int32_t* status);
 
 /**
- * Sets a PWM channel to the desired value.
+ * Sets a PWM channel to the desired pulse width in microseconds.
  *
- * The values are in raw FPGA units, and have the potential to change with any
- * FPGA release.
  *
  * @param[in] pwmPortHandle the PWM handle
- * @param[in] value         the PWM value to set
+ * @param[in] microsecondPulseTime  the PWM value to set
  * @param[out] status       Error status variable. 0 on success.
  */
-void HAL_SetPWMRaw(HAL_DigitalHandle pwmPortHandle, int32_t value,
-                   int32_t* status);
+void HAL_SetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle,
+                                     int32_t microsecondPulseTime,
+                                     int32_t* status);
 
 /**
  * Sets a PWM channel to the desired scaled value.
@@ -177,16 +155,14 @@
 void HAL_SetPWMDisabled(HAL_DigitalHandle pwmPortHandle, int32_t* status);
 
 /**
- * Gets a value from a PWM channel.
- *
- * The values are in raw FPGA units, and have the potential to change with any
- * FPGA release.
+ * Gets the current microsecond pulse time from a PWM channel.
  *
  * @param[in] pwmPortHandle the PWM handle
  * @param[out] status       Error status variable. 0 on success.
- * @return the current raw PWM value
+ * @return the current PWM microsecond pulse time
  */
-int32_t HAL_GetPWMRaw(HAL_DigitalHandle pwmPortHandle, int32_t* status);
+int32_t HAL_GetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle,
+                                        int32_t* status);
 
 /**
  * Gets a scaled value from a PWM channel.
@@ -229,6 +205,14 @@
                            int32_t* status);
 
 /**
+ * Sets the PWM output to be a continuous high signal while enabled.
+ *
+ * @param[in] pwmPortHandle the PWM handle.
+ * @param[out] status       Error status variable. 0 on success.
+ */
+void HAL_SetPWMAlwaysHighMode(HAL_DigitalHandle pwmPortHandle, int32_t* status);
+
+/**
  * Gets the loop timing of the PWM system.
  *
  * @param[out] status Error status variable. 0 on success.
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/Ports.h b/third_party/allwpilib/hal/src/main/native/include/hal/Ports.h
index a212446..a6c610a 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/Ports.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/Ports.h
@@ -45,7 +45,7 @@
 int32_t HAL_GetNumAnalogOutputs(void);
 
 /**
- * Gets the number of analog counters in the current system.
+ * Gets the number of counters in the current system.
  *
  * @return the number of counters
  */
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/Power.h b/third_party/allwpilib/hal/src/main/native/include/hal/Power.h
index 2bd983a..b2a0169 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/Power.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/Power.h
@@ -67,6 +67,14 @@
 int32_t HAL_GetUserCurrentFaults6V(int32_t* status);
 
 /**
+ * Enables or disables the 6V rail.
+ *
+ * @param enabled whether the rail should be enabled
+ * @param[out] status the error code, or 0 for success
+ */
+void HAL_SetUserRailEnabled6V(HAL_Bool enabled, int32_t* status);
+
+/**
  * Gets the 5V rail voltage.
  *
  * @param[out] status the error code, or 0 for success
@@ -99,6 +107,14 @@
 int32_t HAL_GetUserCurrentFaults5V(int32_t* status);
 
 /**
+ * Enables or disables the 5V rail.
+ *
+ * @param enabled whether the rail should be enabled
+ * @param[out] status the error code, or 0 for success
+ */
+void HAL_SetUserRailEnabled5V(HAL_Bool enabled, int32_t* status);
+
+/**
  * Gets the 3V3 rail voltage.
  *
  * @param[out] status the error code, or 0 for success
@@ -131,6 +147,14 @@
 int32_t HAL_GetUserCurrentFaults3V3(int32_t* status);
 
 /**
+ * Enables or disables the 3V3 rail.
+ *
+ * @param enabled whether the rail should be enabled
+ * @param[out] status the error code, or 0 for success
+ */
+void HAL_SetUserRailEnabled3V3(HAL_Bool enabled, int32_t* status);
+
+/**
  * Get the current brownout voltage setting.
  *
  * @param[out] status the error code, or 0 for success
@@ -149,6 +173,14 @@
  */
 void HAL_SetBrownoutVoltage(double voltage, int32_t* status);
 
+/**
+ * Get the current CPU temperature in degrees Celsius
+ *
+ * @param[out] status the error code, or 0 for success
+ * @return current CPU temperature in degrees Celsius
+ */
+double HAL_GetCPUTemp(int32_t* status);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/PowerDistribution.h b/third_party/allwpilib/hal/src/main/native/include/hal/PowerDistribution.h
index 7c2b582..47543bb 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/PowerDistribution.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/PowerDistribution.h
@@ -17,7 +17,7 @@
 
 // clang-format off
 /**
- * The acceptable accelerometer ranges.
+ * The types of power distribution devices.
  */
 HAL_ENUM(HAL_PowerDistributionType) {
   HAL_PowerDistributionType_kAutomatic = 0,
@@ -36,10 +36,10 @@
  * Initializes a Power Distribution Panel.
  *
  * @param[in] moduleNumber       the module number to initialize
- * @param[in] type               the type of module to intialize
+ * @param[in] type               the type of module to initialize
  * @param[in] allocationLocation the location where the allocation is occurring
  * @param[out] status            Error status variable. 0 on success.
- * @return the created PowerDistribution
+ * @return the created PowerDistribution handle
  */
 HAL_PowerDistributionHandle HAL_InitializePowerDistribution(
     int32_t moduleNumber, HAL_PowerDistributionType type,
@@ -134,7 +134,7 @@
     HAL_PowerDistributionHandle handle, int32_t channel, int32_t* status);
 
 /**
- * Gets the current of all 24 channels on the PowerDistribution.
+ * Gets the current of all channels on the PowerDistribution.
  *
  * The array must be large enough to hold all channels.
  *
@@ -214,6 +214,7 @@
  *
  * @param[in] handle the module handle
  * @param[out] status Error status variable. 0 on success.
+ * @return the state of the switchable channel
  */
 HAL_Bool HAL_GetPowerDistributionSwitchableChannel(
     HAL_PowerDistributionHandle handle, int32_t* status);
@@ -291,12 +292,34 @@
   uint32_t hasReset : 1;
 };
 
+/**
+ * Get the version of the PowerDistribution.
+ *
+ * @param[in] handle the module handle
+ * @param[out] version the HAL_PowerDistributionVersion to populate
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_GetPowerDistributionVersion(HAL_PowerDistributionHandle handle,
                                      HAL_PowerDistributionVersion* version,
                                      int32_t* status);
+/**
+ * Get the current faults of the PowerDistribution.
+ *
+ * @param[in] handle the module handle
+ * @param[out] faults the HAL_PowerDistributionFaults to populate
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_GetPowerDistributionFaults(HAL_PowerDistributionHandle handle,
                                     HAL_PowerDistributionFaults* faults,
                                     int32_t* status);
+
+/**
+ * Gets the sticky faults of the PowerDistribution.
+ *
+ * @param[in] handle the module handle
+ * @param[out] stickyFaults the HAL_PowerDistributionStickyFaults to populate
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_GetPowerDistributionStickyFaults(
     HAL_PowerDistributionHandle handle,
     HAL_PowerDistributionStickyFaults* stickyFaults, int32_t* status);
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/REVPH.h b/third_party/allwpilib/hal/src/main/native/include/hal/REVPH.h
index 430c53d..4476675 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/REVPH.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/REVPH.h
@@ -9,7 +9,7 @@
 #include "hal/Types.h"
 
 /**
- * @defgroup hal_rev_ph REV PH Functions
+ * @defgroup hal_rev_ph REV Pneumatic Hub (PH) Functions
  * @ingroup hal_capi
  * @{
  */
@@ -91,58 +91,279 @@
 extern "C" {
 #endif
 
+/**
+ * Initializes a PH.
+ *
+ * @param[in] module             the CAN ID to initialize
+ * @param[in] allocationLocation the location where the allocation is occurring
+ *                               (can be null)
+ * @param[out] status            Error status variable. 0 on success.
+ * @return the created PH handle
+ */
 HAL_REVPHHandle HAL_InitializeREVPH(int32_t module,
                                     const char* allocationLocation,
                                     int32_t* status);
 
+/**
+ * Frees a PH handle.
+ *
+ * @param[in] handle the PH handle
+ */
 void HAL_FreeREVPH(HAL_REVPHHandle handle);
 
+/**
+ * Checks if a solenoid channel number is valid.
+ *
+ * @param[in] channel the channel to check
+ * @return true if the channel is valid, otherwise false
+ */
 HAL_Bool HAL_CheckREVPHSolenoidChannel(int32_t channel);
+
+/**
+ * Checks if a PH module (CAN ID) is valid.
+ *
+ * @param[in] module the module to check
+ * @return true if the module is valid, otherwise false
+ */
 HAL_Bool HAL_CheckREVPHModuleNumber(int32_t module);
 
+/**
+ * Get whether compressor is turned on.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return true if the compressor is turned on
+ */
 HAL_Bool HAL_GetREVPHCompressor(HAL_REVPHHandle handle, int32_t* status);
+
+/**
+ * Send compressor configuration to the PH.
+ *
+ * @param[in] handle  the PH handle
+ * @param[in] config  compressor configuration
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_SetREVPHCompressorConfig(HAL_REVPHHandle handle,
                                   const HAL_REVPHCompressorConfig* config,
                                   int32_t* status);
+
+/**
+ * Disable Compressor.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_SetREVPHClosedLoopControlDisabled(HAL_REVPHHandle handle,
                                            int32_t* status);
+
+/**
+ * Enables the compressor in digital mode using the digital pressure switch. The
+ * compressor will turn on when the pressure switch indicates that the system is
+ * not full, and will turn off when the pressure switch indicates that the
+ * system is full.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_SetREVPHClosedLoopControlDigital(HAL_REVPHHandle handle,
                                           int32_t* status);
+
+/**
+ * Enables the compressor in analog mode. This mode uses an analog
+ * pressure sensor connected to analog channel 0 to cycle the compressor. The
+ * compressor will turn on when the pressure drops below minAnalogVoltage and
+ * will turn off when the pressure reaches maxAnalogVoltage. This mode is only
+ * supported by the REV PH with the REV Analog Pressure Sensor connected to
+ * analog channel 0.
+ * @param[in] handle  the PH handle
+ * @param[in] minAnalogVoltage The compressor will turn on when the analog
+ * pressure sensor voltage drops below this value
+ * @param[in] maxAnalogVoltage The compressor will turn off when the analog
+ * pressure sensor reaches this value.
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_SetREVPHClosedLoopControlAnalog(HAL_REVPHHandle handle,
                                          double minAnalogVoltage,
                                          double maxAnalogVoltage,
                                          int32_t* status);
+
+/**
+ * Enables the compressor in hybrid mode. This mode uses both a digital
+ * pressure switch and an analog pressure sensor connected to analog channel 0
+ * to cycle the compressor.
+ *
+ * The compressor will turn on when \a both:
+ *
+ * - The digital pressure switch indicates the system is not full AND
+ * - The analog pressure sensor indicates that the pressure in the system is
+ * below the specified minimum pressure.
+ *
+ * The compressor will turn off when \a either:
+ *
+ * - The digital pressure switch is disconnected or indicates that the system
+ * is full OR
+ * - The pressure detected by the analog sensor is greater than the specified
+ * maximum pressure.
+ *
+ * @param[in] handle  the PH handle
+ * @param[in] minAnalogVoltage The compressor will turn on when the analog
+ * pressure sensor voltage drops below this value and the pressure switch
+ * indicates that the system is not full.
+ * @param[in] maxAnalogVoltage The compressor will turn off when the analog
+ * pressure sensor reaches this value or the pressure switch is disconnected or
+ * indicates that the system is full.
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_SetREVPHClosedLoopControlHybrid(HAL_REVPHHandle handle,
                                          double minAnalogVoltage,
                                          double maxAnalogVoltage,
                                          int32_t* status);
+
+/**
+ * Get compressor configuration from the PH.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return compressor configuration
+ */
 HAL_REVPHCompressorConfigType HAL_GetREVPHCompressorConfig(
     HAL_REVPHHandle handle, int32_t* status);
+
+/**
+ * Returns the state of the digital pressure switch.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return True if pressure switch indicates that the system is full,
+ * otherwise false.
+ */
 HAL_Bool HAL_GetREVPHPressureSwitch(HAL_REVPHHandle handle, int32_t* status);
+
+/**
+ * Returns the current drawn by the compressor.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return The current drawn by the compressor in amps.
+ */
 double HAL_GetREVPHCompressorCurrent(HAL_REVPHHandle handle, int32_t* status);
+
+/**
+ * Returns the raw voltage of the specified analog
+ * input channel.
+ *
+ * @param[in] handle  the PH handle
+ * @param[in] channel The analog input channel to read voltage from.
+ * @param[out] status Error status variable. 0 on success.
+ * @return The voltage of the specified analog input channel in volts.
+ */
 double HAL_GetREVPHAnalogVoltage(HAL_REVPHHandle handle, int32_t channel,
                                  int32_t* status);
+
+/**
+ * Returns the current input voltage for the PH.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return The input voltage in volts.
+ */
 double HAL_GetREVPHVoltage(HAL_REVPHHandle handle, int32_t* status);
+
+/**
+ * Returns the current voltage of the regulated 5v supply.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return The current voltage of the 5v supply in volts.
+ */
 double HAL_GetREVPH5VVoltage(HAL_REVPHHandle handle, int32_t* status);
+
+/**
+ * Returns the total current drawn by all solenoids.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return Total current drawn by all solenoids in amps.
+ */
 double HAL_GetREVPHSolenoidCurrent(HAL_REVPHHandle handle, int32_t* status);
+
+/**
+ * Returns the current voltage of the solenoid power supply.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return The current voltage of the solenoid power supply in volts.
+ */
 double HAL_GetREVPHSolenoidVoltage(HAL_REVPHHandle handle, int32_t* status);
+
+/**
+ * Returns the hardware and firmware versions of the PH.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] version The hardware and firmware versions.
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_GetREVPHVersion(HAL_REVPHHandle handle, HAL_REVPHVersion* version,
                          int32_t* status);
 
+/**
+ * Gets a bitmask of solenoid values.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ * @return solenoid values
+ */
 int32_t HAL_GetREVPHSolenoids(HAL_REVPHHandle handle, int32_t* status);
+
+/**
+ * Sets solenoids on a PH.
+ *
+ * @param[in] handle  the PH handle
+ * @param[in] mask bitmask to set
+ * @param[in] values solenoid values
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_SetREVPHSolenoids(HAL_REVPHHandle handle, int32_t mask, int32_t values,
                            int32_t* status);
 
+/**
+ * Fire a single solenoid shot for the specified duration.
+ *
+ * @param[in] handle  the PH handle
+ * @param[in] index solenoid index
+ * @param[in] durMs shot duration in ms
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_FireREVPHOneShot(HAL_REVPHHandle handle, int32_t index, int32_t durMs,
                           int32_t* status);
 
+/**
+ * Returns the faults currently active on the PH.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] faults The faults.
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_GetREVPHFaults(HAL_REVPHHandle handle, HAL_REVPHFaults* faults,
                         int32_t* status);
 
+/**
+ * Returns the sticky faults currently active on this device.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] stickyFaults The sticky faults.
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_GetREVPHStickyFaults(HAL_REVPHHandle handle,
                               HAL_REVPHStickyFaults* stickyFaults,
                               int32_t* status);
 
+/**
+ * Clears the sticky faults.
+ *
+ * @param[in] handle  the PH handle
+ * @param[out] status Error status variable. 0 on success.
+ */
 void HAL_ClearREVPHStickyFaults(HAL_REVPHHandle handle, int32_t* status);
 
 #ifdef __cplusplus
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/SPI.h b/third_party/allwpilib/hal/src/main/native/include/hal/SPI.h
index f3c7fdc..32644b6 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/SPI.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/SPI.h
@@ -108,7 +108,7 @@
  *
  * @param port  The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for
  * MXP
- * @returns     The SPI mode currently set
+ * @return      The SPI mode currently set
  */
 HAL_SPIMode HAL_GetSPIMode(HAL_SPIPort port);
 
@@ -172,7 +172,7 @@
  *
  * @param[in] port    The number of the port to use. 0-3 for Onboard CS0-CS2, 4
  *                    for MXP.
- * @param[in] period  The accumlation period (seconds).
+ * @param[in] period  The accumulation period (seconds).
  * @param[out] status the error code, or 0 for success
  */
 void HAL_StartSPIAutoRate(HAL_SPIPort port, double period, int32_t* status);
@@ -199,7 +199,7 @@
                              int32_t* status);
 
 /**
- * Stops an automatic SPI accumlation.
+ * Stops an automatic SPI accumulation.
  *
  * @param[in] port    The number of the port to use. 0-3 for Onboard CS0-CS2, 4
  *                    for MXP.
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/SerialPort.h b/third_party/allwpilib/hal/src/main/native/include/hal/SerialPort.h
index cd6140e..f4a357e 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/SerialPort.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/SerialPort.h
@@ -30,11 +30,12 @@
 /**
  * Initializes a serial port.
  *
- * The channels are either the onboard RS232, the mxp uart, or 2 USB ports. The
+ * The channels are either the onboard RS232, the MXP UART, or 2 USB ports. The
  * top port is USB1, the bottom port is USB2.
  *
  * @param[in] port the serial port to initialize
  * @param[out] status the error code, or 0 for success
+ * @return Serial Port Handle
  */
 HAL_SerialPortHandle HAL_InitializeSerialPort(HAL_SerialPort port,
                                               int32_t* status);
@@ -48,6 +49,7 @@
  * @param[in] port     the serial port to initialize
  * @param[in] portName the dev port name
  * @param[out] status  the error code, or 0 for success
+ * @return Serial Port Handle
  */
 HAL_SerialPortHandle HAL_InitializeSerialPortDirect(HAL_SerialPort port,
                                                     const char* portName,
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/SimDevice.h b/third_party/allwpilib/hal/src/main/native/include/hal/SimDevice.h
index f90cb9b..0e75cea 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/SimDevice.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/SimDevice.h
@@ -9,6 +9,7 @@
 #ifdef __cplusplus
 #include <initializer_list>
 #include <span>
+#include <string>
 #endif
 
 #include "hal/Types.h"
@@ -46,7 +47,7 @@
  *
  * The device name must be unique.  0 is returned if the device name already
  * exists.  If multiple instances of the same device are desired, recommend
- * appending the instance/unique identifer in brackets to the base name,
+ * appending the instance/unique identifier in brackets to the base name,
  * e.g. "device[1]".
  *
  * 0 is returned if not in simulation.
@@ -67,6 +68,14 @@
 void HAL_FreeSimDevice(HAL_SimDeviceHandle handle);
 
 /**
+ * Get the name of a simulated device
+ *
+ * @param handle simulated device handle
+ * @return name of the simulated device
+ */
+const char* HAL_GetSimDeviceName(HAL_SimDeviceHandle handle);
+
+/**
  * Creates a value on a simulated device.
  *
  * Returns 0 if not in simulation; this can be used to avoid calls
@@ -400,7 +409,7 @@
   /**
    * Wraps a simulated value handle as returned by HAL_CreateSimValue().
    *
-   * @param handle simulated value handle
+   * @param val simulated value handle
    */
   /*implicit*/ SimValue(HAL_SimValueHandle val)  // NOLINT
       : m_handle(val) {}
@@ -452,7 +461,7 @@
   /**
    * Wraps a simulated value handle as returned by HAL_CreateSimValueInt().
    *
-   * @param handle simulated value handle
+   * @param val simulated value handle
    */
   /*implicit*/ SimInt(HAL_SimValueHandle val)  // NOLINT
       : SimValue(val) {}
@@ -493,7 +502,7 @@
   /**
    * Wraps a simulated value handle as returned by HAL_CreateSimValueLong().
    *
-   * @param handle simulated value handle
+   * @param val simulated value handle
    */
   /*implicit*/ SimLong(HAL_SimValueHandle val)  // NOLINT
       : SimValue(val) {}
@@ -534,7 +543,7 @@
   /**
    * Wraps a simulated value handle as returned by HAL_CreateSimValueDouble().
    *
-   * @param handle simulated value handle
+   * @param val simulated value handle
    */
   /*implicit*/ SimDouble(HAL_SimValueHandle val)  // NOLINT
       : SimValue(val) {}
@@ -575,7 +584,7 @@
   /**
    * Wraps a simulated value handle as returned by HAL_CreateSimValueEnum().
    *
-   * @param handle simulated value handle
+   * @param val simulated value handle
    */
   /*implicit*/ SimEnum(HAL_SimValueHandle val)  // NOLINT
       : SimValue(val) {}
@@ -609,7 +618,7 @@
   /**
    * Wraps a simulated value handle as returned by HAL_CreateSimValueBoolean().
    *
-   * @param handle simulated value handle
+   * @param val simulated value handle
    */
   /*implicit*/ SimBoolean(HAL_SimValueHandle val)  // NOLINT
       : SimValue(val) {}
@@ -654,7 +663,7 @@
    *
    * The device name must be unique.  Returns null if the device name
    * already exists.  If multiple instances of the same device are desired,
-   * recommend appending the instance/unique identifer in brackets to the base
+   * recommend appending the instance/unique identifier in brackets to the base
    * name, e.g. "device[1]".
    *
    * If not in simulation, results in an "empty" object that evaluates to false
@@ -732,6 +741,15 @@
   operator HAL_SimDeviceHandle() const { return m_handle; }  // NOLINT
 
   /**
+   * Get the name of the simulated device.
+   *
+   * @return name
+   */
+  std::string GetName() const {
+    return std::string(HAL_GetSimDeviceName(m_handle));
+  }
+
+  /**
    * Creates a value on the simulated device.
    *
    * If not in simulation, results in an "empty" object that evaluates to false
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/cpp/UnsafeDIO.h b/third_party/allwpilib/hal/src/main/native/include/hal/cpp/UnsafeDIO.h
index c849fd0..e7f286b 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/cpp/UnsafeDIO.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/cpp/UnsafeDIO.h
@@ -63,8 +63,8 @@
 
 /**
  * Unsafe digital output set function
- * This function can be used to perform fast and determinstically set digital
- * outputs. This function holds the DIO lock, so calling anyting other then
+ * This function can be used to perform fast and deterministically set digital
+ * outputs. This function holds the DIO lock, so calling anything other then
  * functions on the Proxy object passed as a parameter can deadlock your
  * program.
  *
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/handles/UnlimitedHandleResource.h b/third_party/allwpilib/hal/src/main/native/include/hal/handles/UnlimitedHandleResource.h
index 09510ac..2048f62 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/handles/UnlimitedHandleResource.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/handles/UnlimitedHandleResource.h
@@ -19,7 +19,7 @@
 
 /**
  * The UnlimitedHandleResource class is a way to track handles. This version
- * allows an unlimted number of handles that are allocated sequentially. When
+ * allows an unlimited number of handles that are allocated sequentially. When
  * possible, indices are reused to save memory usage and keep the array length
  * down.
  * However, automatic array management has not been implemented, but might be in
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/roborio/HMB.h b/third_party/allwpilib/hal/src/main/native/include/hal/roborio/HMB.h
new file mode 100644
index 0000000..fc7381b
--- /dev/null
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/roborio/HMB.h
@@ -0,0 +1,112 @@
+// 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 <stdint.h>
+
+struct HAL_HMBData {
+  struct AnalogInputs {
+    uint32_t Values[8];
+    uint32_t Reserved[8];
+  } AnalogInputs;
+  struct AveragedAnalogInputs {
+    uint32_t Values[8];
+    uint32_t Reserved[8];
+  } AveragedAnalogInputs;
+  struct Accumulator0 {
+    uint32_t Count;
+    uint32_t Value0;
+    uint32_t Value1;
+    uint32_t Reserved[13];
+  } Accumulator0;
+  struct Accumulator1 {
+    uint32_t Count;
+    uint32_t Value0;
+    uint32_t Value1;
+    uint32_t Reserved[13];
+  } Accumulator1;
+  struct DI {
+    uint32_t Values;
+    uint32_t FilteredValues;
+    uint32_t Reserved[14];
+  } DI;
+  struct AnalogTriggers {
+    struct Trigger {
+      uint8_t InHysteresis0 : 1;
+      uint8_t OverLimit0 : 1;
+      uint8_t Rising0 : 1;
+      uint8_t Falling0 : 1;
+      uint8_t InHysteresis1 : 1;
+      uint8_t OverLimit1 : 1;
+      uint8_t Rising1 : 1;
+      uint8_t Falling1 : 1;
+    } Trigger[4];
+    uint32_t Reserved[15];
+  } AnalogTriggers;
+  struct Counters {
+    struct Counter {
+      uint32_t Direction : 1;
+      int32_t Value : 31;
+    } Counter[8];
+    uint32_t Reserved[8];
+  } Counters;
+  struct CounterTimers {
+    struct Timer {
+      uint32_t Period : 23;
+      int32_t Count : 8;
+      uint32_t Stalled : 1;
+    } Timer[8];
+    uint32_t Reserved[8];
+  } CounterTimers;
+  struct Encoders {
+    struct Encoder {
+      uint32_t Direction : 1;
+      int32_t Value : 31;
+    } Encoder[8];
+    uint32_t Reserved[8];
+  } Encoders;
+  struct EncoderTimers {
+    struct Timer {
+      uint32_t Period : 23;
+      int32_t Count : 8;
+      uint32_t Stalled : 1;
+    } Timer[8];
+    uint32_t Reserved[8];
+  } EncoderTimers;
+  struct DutyCycle {
+    uint32_t Output[8];
+    uint32_t Reserved[8];
+  } DutyCycle;
+  struct Interrupts {
+    struct Interrupt {
+      uint32_t FallingTimestamp;
+      uint32_t RisingTimestamp;
+    } Interrupt[8];
+  } Interrupts;
+  struct PWM {
+    uint32_t Headers[10];
+    uint32_t Reserved[6];
+    uint32_t MXP[10];
+    uint32_t Reserved2[6];
+  } PWM;
+  struct RelayDOAO {
+    uint32_t Relays;
+    uint32_t Reserved;
+    uint32_t AO[2];
+    uint32_t Reserved2[12];
+  } RelayDOAO;
+  struct Timestamp {
+    uint32_t Lower;
+    uint32_t Upper;
+    uint32_t Reserved[14];
+  } Timestamp;
+};
+
+extern "C" {
+
+void HAL_InitializeHMB(int32_t* status);
+
+const volatile HAL_HMBData* HAL_GetHMBBuffer(void);
+}  // extern "C"
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/simulation/PWMData.h b/third_party/allwpilib/hal/src/main/native/include/hal/simulation/PWMData.h
index 91d22df..8357fa2 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/simulation/PWMData.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/simulation/PWMData.h
@@ -20,12 +20,13 @@
 HAL_Bool HALSIM_GetPWMInitialized(int32_t index);
 void HALSIM_SetPWMInitialized(int32_t index, HAL_Bool initialized);
 
-int32_t HALSIM_RegisterPWMRawValueCallback(int32_t index,
-                                           HAL_NotifyCallback callback,
-                                           void* param, HAL_Bool initialNotify);
-void HALSIM_CancelPWMRawValueCallback(int32_t index, int32_t uid);
-int32_t HALSIM_GetPWMRawValue(int32_t index);
-void HALSIM_SetPWMRawValue(int32_t index, int32_t rawValue);
+int32_t HALSIM_RegisterPWMPulseMicrosecondCallback(int32_t index,
+                                                   HAL_NotifyCallback callback,
+                                                   void* param,
+                                                   HAL_Bool initialNotify);
+void HALSIM_CancelPWMPulseMicrosecondCallback(int32_t index, int32_t uid);
+int32_t HALSIM_GetPWMPulseMicrosecond(int32_t index);
+void HALSIM_SetPWMPulseMicrosecond(int32_t index, int32_t microsecondPulseTime);
 
 int32_t HALSIM_RegisterPWMSpeedCallback(int32_t index,
                                         HAL_NotifyCallback callback,
diff --git a/third_party/allwpilib/hal/src/main/native/include/hal/simulation/RoboRioData.h b/third_party/allwpilib/hal/src/main/native/include/hal/simulation/RoboRioData.h
index 864be5c..d191fcb 100644
--- a/third_party/allwpilib/hal/src/main/native/include/hal/simulation/RoboRioData.h
+++ b/third_party/allwpilib/hal/src/main/native/include/hal/simulation/RoboRioData.h
@@ -126,6 +126,13 @@
 double HALSIM_GetRoboRioBrownoutVoltage(void);
 void HALSIM_SetRoboRioBrownoutVoltage(double brownoutVoltage);
 
+int32_t HALSIM_RegisterRoboRioTeamNumberCallback(HAL_NotifyCallback callback,
+                                                 void* param,
+                                                 HAL_Bool initialNotify);
+void HALSIM_CancelRoboRioTeamNumberCallback(int32_t uid);
+int32_t HALSIM_GetRoboRioTeamNumber(void);
+void HALSIM_SetRoboRioTeamNumber(int32_t teamNumber);
+
 int32_t HALSIM_RegisterRoboRioSerialNumberCallback(
     HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify);
 void HALSIM_CancelRoboRioSerialNumberCallback(int32_t uid);
@@ -141,6 +148,13 @@
 void HALSIM_RegisterRoboRioAllCallbacks(HAL_NotifyCallback callback,
                                         void* param, HAL_Bool initialNotify);
 
+int32_t HALSIM_RegisterRoboRioCPUTempCallback(HAL_NotifyCallback callback,
+                                              void* param,
+                                              HAL_Bool initialNotify);
+void HALSIM_CancelRoboRioCPUTempCallback(int32_t uid);
+double HALSIM_GetRoboRioCPUTemp(void);
+void HALSIM_SetRoboRioCPUTemp(double cpuTemp);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/third_party/allwpilib/hal/src/main/native/sim/AddressableLED.cpp b/third_party/allwpilib/hal/src/main/native/sim/AddressableLED.cpp
index 75a09fb..bce3f1d 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/AddressableLED.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/AddressableLED.cpp
@@ -139,17 +139,17 @@
         status,
         fmt::format(
             "Data length must be less than or equal to {}. {} was requested",
-            SimAddressableLEDData[led->index].length, length));
+            SimAddressableLEDData[led->index].length.Get(), length));
     return;
   }
   SimAddressableLEDData[led->index].SetData(data, length);
 }
 
 void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
-                                    int32_t lowTime0NanoSeconds,
                                     int32_t highTime0NanoSeconds,
-                                    int32_t lowTime1NanoSeconds,
+                                    int32_t lowTime0NanoSeconds,
                                     int32_t highTime1NanoSeconds,
+                                    int32_t lowTime1NanoSeconds,
                                     int32_t* status) {}
 
 void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle,
diff --git a/third_party/allwpilib/hal/src/main/native/sim/CANAPI.cpp b/third_party/allwpilib/hal/src/main/native/sim/CANAPI.cpp
index 38014bd..0eae18a 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/CANAPI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/CANAPI.cpp
@@ -35,13 +35,6 @@
 static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>*
     canHandles;
 
-static uint32_t GetPacketBaseTime() {
-  int status = 0;
-  auto basetime = HAL_GetFPGATime(&status);
-  // us to ms
-  return (basetime / 1000ull) & 0xFFFFFFFF;
-}
-
 namespace hal {
 namespace init {
 void InitializeCANAPI() {
@@ -71,6 +64,13 @@
   return createdId;
 }
 
+uint32_t HAL_GetCANPacketBaseTime() {
+  int status = 0;
+  auto basetime = HAL_GetFPGATime(&status);
+  // us to ms
+  return (basetime / 1000ull) & 0xFFFFFFFF;
+}
+
 HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer,
                                 int32_t deviceId, HAL_CANDeviceType deviceType,
                                 int32_t* status) {
@@ -275,7 +275,7 @@
     auto i = can->receives.find(messageId);
     if (i != can->receives.end()) {
       // Found, check if new enough
-      uint32_t now = GetPacketBaseTime();
+      uint32_t now = HAL_GetCANPacketBaseTime();
       if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
         // Timeout, return bad status
         *status = HAL_CAN_TIMEOUT;
@@ -307,7 +307,7 @@
     auto i = can->receives.find(messageId);
     if (i != can->receives.end()) {
       // Found, check if new enough
-      uint32_t now = GetPacketBaseTime();
+      uint32_t now = HAL_GetCANPacketBaseTime();
       if (now - i->second.lastTimeStamp < static_cast<uint32_t>(periodMs)) {
         *status = 0;
         // Read the data from the stored message into the output
@@ -337,7 +337,7 @@
     auto i = can->receives.find(messageId);
     if (i != can->receives.end()) {
       // Found, check if new enough
-      uint32_t now = GetPacketBaseTime();
+      uint32_t now = HAL_GetCANPacketBaseTime();
       if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
         // Timeout, return bad status
         *status = HAL_CAN_TIMEOUT;
diff --git a/third_party/allwpilib/hal/src/main/native/sim/DIO.cpp b/third_party/allwpilib/hal/src/main/native/sim/DIO.cpp
index b611b35..f827bbf 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/DIO.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/DIO.cpp
@@ -127,9 +127,7 @@
   // higher freq.
   // TODO: Round in the linear rate domain.
   // uint8_t pwmPeriodPower = static_cast<uint8_t>(
-  //    std::log(1.0 / (kExpectedLoopTiming * 0.25E-6 * rate)) /
-  //        std::log(2.0) +
-  //    0.5);
+  //     std::log2(1.0 / (kExpectedLoopTiming * 0.25E-6 * rate)) + 0.5);
   // TODO(THAD) : Add a case to set this in the simulator
   // digitalSystem->writePWMPeriodPower(pwmPeriodPower, status);
 }
diff --git a/third_party/allwpilib/hal/src/main/native/sim/DigitalInternal.h b/third_party/allwpilib/hal/src/main/native/sim/DigitalInternal.h
index e7f531e..8fa679a 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/DigitalInternal.h
+++ b/third_party/allwpilib/hal/src/main/native/sim/DigitalInternal.h
@@ -39,14 +39,6 @@
  * devices.
  */
 constexpr float kDefaultPwmPeriod = 5.05f;
-/**
- * kDefaultPwmCenter is the PWM range center in ms
- */
-constexpr float kDefaultPwmCenter = 1.5f;
-/**
- * kDefaultPWMStepsDown is the number of PWM steps below the centerpoint
- */
-constexpr int32_t kDefaultPwmStepsDown = 1000;
 constexpr int32_t kPwmDisabled = 0;
 
 struct DigitalPort {
diff --git a/third_party/allwpilib/hal/src/main/native/sim/DriverStation.cpp b/third_party/allwpilib/hal/src/main/native/sim/DriverStation.cpp
index c99493d..67b2eb7 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/DriverStation.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/DriverStation.cpp
@@ -75,7 +75,7 @@
 static JoystickDataCache caches[3];
 static JoystickDataCache* currentRead = &caches[0];
 static JoystickDataCache* currentReadLocal = &caches[0];
-static std::atomic<JoystickDataCache*> currentCache{&caches[1]};
+static std::atomic<JoystickDataCache*> currentCache{nullptr};
 static JoystickDataCache* lastGiven = &caches[1];
 static JoystickDataCache* cacheToUpdate = &caches[2];
 
@@ -194,7 +194,7 @@
 
 HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
   if (gShutdown) {
-    return HAL_AllianceStationID_kRed1;
+    return HAL_AllianceStationID_kUnknown;
   }
   std::scoped_lock lock{driverStation->cacheMutex};
   return currentRead->allianceStation;
@@ -318,9 +318,9 @@
   // TODO
 }
 
-void HAL_RefreshDSData(void) {
+HAL_Bool HAL_RefreshDSData(void) {
   if (gShutdown) {
-    return;
+    return false;
   }
   HAL_ControlWord controlWord;
   std::memset(&controlWord, 0, sizeof(controlWord));
@@ -336,6 +336,7 @@
     currentRead = prev;
   }
   newestControlWord = controlWord;
+  return prev != nullptr;
 }
 
 void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle) {
diff --git a/third_party/allwpilib/hal/src/main/native/sim/Encoder.cpp b/third_party/allwpilib/hal/src/main/native/sim/Encoder.cpp
index 137ee78..964777d 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/Encoder.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/Encoder.cpp
@@ -363,7 +363,7 @@
     return 0.0;
   }
 
-  return encoder->distancePerPulse;
+  return SimEncoderData[encoder->index].distancePerPulse;
 }
 
 HAL_EncoderEncodingType HAL_GetEncoderEncodingType(
diff --git a/third_party/allwpilib/hal/src/main/native/sim/HAL.cpp b/third_party/allwpilib/hal/src/main/native/sim/HAL.cpp
index 82dd8c4..ce8b6dd 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/HAL.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/HAL.cpp
@@ -288,6 +288,10 @@
   return HALSIM_GetRoboRioComments(buffer, size);
 }
 
+int32_t HAL_GetTeamNumber(void) {
+  return HALSIM_GetRoboRioTeamNumber();
+}
+
 uint64_t HAL_GetFPGATime(int32_t* status) {
   return hal::GetFPGATime();
 }
@@ -328,6 +332,14 @@
   return false;  // Figure out if we need to detect a brownout condition
 }
 
+HAL_Bool HAL_GetRSLState(int32_t* status) {
+  return false;
+}
+
+HAL_Bool HAL_GetSystemTimeValid(int32_t* status) {
+  return true;
+}
+
 HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) {
   static std::atomic_bool initialized{false};
   static wpi::mutex initializeMutex;
diff --git a/third_party/allwpilib/hal/src/main/native/sim/Notifier.cpp b/third_party/allwpilib/hal/src/main/native/sim/Notifier.cpp
index c686eea..3d2c802 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/Notifier.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/Notifier.cpp
@@ -11,6 +11,7 @@
 #include <string>
 
 #include <wpi/SmallVector.h>
+#include <wpi/StringExtras.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
 
@@ -316,8 +317,9 @@
     if (num < size) {
       arr[num].handle = handle;
       if (notifier->name.empty()) {
-        std::snprintf(arr[num].name, sizeof(arr[num].name), "Notifier%d",
-                      static_cast<int>(getHandleIndex(handle)));
+        wpi::format_to_n_c_str(arr[num].name, sizeof(arr[num].name),
+                               "Notifier{}",
+                               static_cast<int>(getHandleIndex(handle)));
       } else {
         std::strncpy(arr[num].name, notifier->name.c_str(),
                      sizeof(arr[num].name) - 1);
diff --git a/third_party/allwpilib/hal/src/main/native/sim/PWM.cpp b/third_party/allwpilib/hal/src/main/native/sim/PWM.cpp
index 698769f..c668039 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/PWM.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/PWM.cpp
@@ -4,6 +4,9 @@
 
 #include "hal/PWM.h"
 
+#include <algorithm>
+#include <cmath>
+
 #include "ConstantsInternal.h"
 #include "DigitalInternal.h"
 #include "HALInitializer.h"
@@ -18,6 +21,46 @@
 void InitializePWM() {}
 }  // namespace hal::init
 
+static inline int32_t GetMaxPositivePwm(DigitalPort* port) {
+  return port->maxPwm;
+}
+
+static inline int32_t GetMinPositivePwm(DigitalPort* port) {
+  if (port->eliminateDeadband) {
+    return port->deadbandMaxPwm;
+  } else {
+    return port->centerPwm + 1;
+  }
+}
+
+static inline int32_t GetCenterPwm(DigitalPort* port) {
+  return port->centerPwm;
+}
+
+static inline int32_t GetMaxNegativePwm(DigitalPort* port) {
+  if (port->eliminateDeadband) {
+    return port->deadbandMinPwm;
+  } else {
+    return port->centerPwm - 1;
+  }
+}
+
+static inline int32_t GetMinNegativePwm(DigitalPort* port) {
+  return port->minPwm;
+}
+
+static inline int32_t GetPositiveScaleFactor(DigitalPort* port) {
+  return GetMaxPositivePwm(port) - GetMinPositivePwm(port);
+}  ///< The scale for positive speeds.
+
+static inline int32_t GetNegativeScaleFactor(DigitalPort* port) {
+  return GetMaxNegativePwm(port) - GetMinNegativePwm(port);
+}  ///< The scale for negative speeds.
+
+static inline int32_t GetFullRangeScaleFactor(DigitalPort* port) {
+  return GetMaxPositivePwm(port) - GetMinNegativePwm(port);
+}  ///< The scale for positions.
+
 extern "C" {
 
 HAL_DigitalHandle HAL_InitializePWMPort(HAL_PortHandle portHandle,
@@ -62,7 +105,7 @@
   SimPWMData[origChannel].initialized = true;
 
   // Defaults to allow an always valid config.
-  HAL_SetPWMConfig(handle, 2.0, 1.501, 1.5, 1.499, 1.0, status);
+  HAL_SetPWMConfigMicroseconds(handle, 2000, 1501, 1500, 1499, 1000, status);
 
   port->previousAllocation = allocationLocation ? allocationLocation : "";
 
@@ -84,62 +127,28 @@
   return channel < kNumPWMChannels && channel >= 0;
 }
 
-void HAL_SetPWMConfig(HAL_DigitalHandle pwmPortHandle, double max,
-                      double deadbandMax, double center, double deadbandMin,
-                      double min, int32_t* status) {
+void HAL_SetPWMConfigMicroseconds(HAL_DigitalHandle pwmPortHandle, int32_t max,
+                                  int32_t deadbandMax, int32_t center,
+                                  int32_t deadbandMin, int32_t min,
+                                  int32_t* status) {
   auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
   if (port == nullptr) {
     *status = HAL_HANDLE_ERROR;
     return;
   }
 
-  // calculate the loop time in milliseconds
-  double loopTime =
-      HAL_GetPWMLoopTiming(status) / (kSystemClockTicksPerMicrosecond * 1e3);
-  if (*status != 0) {
-    return;
-  }
-
-  int32_t maxPwm = static_cast<int32_t>((max - kDefaultPwmCenter) / loopTime +
-                                        kDefaultPwmStepsDown - 1);
-  int32_t deadbandMaxPwm = static_cast<int32_t>(
-      (deadbandMax - kDefaultPwmCenter) / loopTime + kDefaultPwmStepsDown - 1);
-  int32_t centerPwm = static_cast<int32_t>(
-      (center - kDefaultPwmCenter) / loopTime + kDefaultPwmStepsDown - 1);
-  int32_t deadbandMinPwm = static_cast<int32_t>(
-      (deadbandMin - kDefaultPwmCenter) / loopTime + kDefaultPwmStepsDown - 1);
-  int32_t minPwm = static_cast<int32_t>((min - kDefaultPwmCenter) / loopTime +
-                                        kDefaultPwmStepsDown - 1);
-
-  port->maxPwm = maxPwm;
-  port->deadbandMaxPwm = deadbandMaxPwm;
-  port->deadbandMinPwm = deadbandMinPwm;
-  port->centerPwm = centerPwm;
-  port->minPwm = minPwm;
+  port->maxPwm = max;
+  port->deadbandMaxPwm = deadbandMax;
+  port->deadbandMinPwm = deadbandMin;
+  port->centerPwm = center;
+  port->minPwm = min;
   port->configSet = true;
 }
 
-void HAL_SetPWMConfigRaw(HAL_DigitalHandle pwmPortHandle, int32_t maxPwm,
-                         int32_t deadbandMaxPwm, int32_t centerPwm,
-                         int32_t deadbandMinPwm, int32_t minPwm,
-                         int32_t* status) {
-  auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
-  if (port == nullptr) {
-    *status = HAL_HANDLE_ERROR;
-    return;
-  }
-
-  port->maxPwm = maxPwm;
-  port->deadbandMaxPwm = deadbandMaxPwm;
-  port->deadbandMinPwm = deadbandMinPwm;
-  port->centerPwm = centerPwm;
-  port->minPwm = minPwm;
-}
-
-void HAL_GetPWMConfigRaw(HAL_DigitalHandle pwmPortHandle, int32_t* maxPwm,
-                         int32_t* deadbandMaxPwm, int32_t* centerPwm,
-                         int32_t* deadbandMinPwm, int32_t* minPwm,
-                         int32_t* status) {
+void HAL_GetPWMConfigMicroseconds(HAL_DigitalHandle pwmPortHandle,
+                                  int32_t* maxPwm, int32_t* deadbandMaxPwm,
+                                  int32_t* centerPwm, int32_t* deadbandMinPwm,
+                                  int32_t* minPwm, int32_t* status) {
   auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
   if (port == nullptr) {
     *status = HAL_HANDLE_ERROR;
@@ -172,15 +181,49 @@
   return port->eliminateDeadband;
 }
 
-void HAL_SetPWMRaw(HAL_DigitalHandle pwmPortHandle, int32_t value,
-                   int32_t* status) {
+void HAL_SetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle,
+                                     int32_t value, int32_t* status) {
   auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
   if (port == nullptr) {
     *status = HAL_HANDLE_ERROR;
     return;
   }
 
-  SimPWMData[port->channel].rawValue = value;
+  SimPWMData[port->channel].pulseMicrosecond = value;
+
+  DigitalPort* dPort = port.get();
+  double speed = 0.0;
+
+  if (value == kPwmDisabled) {
+    speed = 0.0;
+  } else if (value > GetMaxPositivePwm(dPort)) {
+    speed = 1.0;
+  } else if (value < GetMinNegativePwm(dPort)) {
+    speed = -1.0;
+  } else if (value > GetMinPositivePwm(dPort)) {
+    speed = static_cast<double>(value - GetMinPositivePwm(dPort)) /
+            static_cast<double>(GetPositiveScaleFactor(dPort));
+  } else if (value < GetMaxNegativePwm(dPort)) {
+    speed = static_cast<double>(value - GetMaxNegativePwm(dPort)) /
+            static_cast<double>(GetNegativeScaleFactor(dPort));
+  } else {
+    speed = 0.0;
+  }
+
+  SimPWMData[port->channel].speed = speed;
+
+  double pos = 0.0;
+
+  if (value < GetMinNegativePwm(dPort)) {
+    pos = 0.0;
+  } else if (value > GetMaxPositivePwm(dPort)) {
+    pos = 1.0;
+  } else {
+    pos = static_cast<double>(value - GetMinNegativePwm(dPort)) /
+          static_cast<double>(GetFullRangeScaleFactor(dPort));
+  }
+
+  SimPWMData[port->channel].position = pos;
 }
 
 void HAL_SetPWMSpeed(HAL_DigitalHandle pwmPortHandle, double speed,
@@ -195,13 +238,36 @@
     return;
   }
 
-  if (speed < -1.0) {
-    speed = -1.0;
-  } else if (speed > 1.0) {
-    speed = 1.0;
+  if (std::isfinite(speed)) {
+    speed = std::clamp(speed, -1.0, 1.0);
+  } else {
+    speed = 0.0;
   }
 
-  SimPWMData[port->channel].speed = speed;
+  DigitalPort* dPort = port.get();
+
+  // calculate the desired output pwm value by scaling the speed appropriately
+  int32_t rawValue;
+  if (speed == 0.0) {
+    rawValue = GetCenterPwm(dPort);
+  } else if (speed > 0.0) {
+    rawValue =
+        std::lround(speed * static_cast<double>(GetPositiveScaleFactor(dPort)) +
+                    static_cast<double>(GetMinPositivePwm(dPort)));
+  } else {
+    rawValue =
+        std::lround(speed * static_cast<double>(GetNegativeScaleFactor(dPort)) +
+                    static_cast<double>(GetMaxNegativePwm(dPort)));
+  }
+
+  if (!((rawValue >= GetMinNegativePwm(dPort)) &&
+        (rawValue <= GetMaxPositivePwm(dPort))) ||
+      rawValue == kPwmDisabled) {
+    *status = HAL_PWM_SCALE_ERROR;
+    return;
+  }
+
+  HAL_SetPWMPulseTimeMicroseconds(pwmPortHandle, rawValue, status);
 }
 
 void HAL_SetPWMPosition(HAL_DigitalHandle pwmPortHandle, double pos,
@@ -222,7 +288,20 @@
     pos = 1.0;
   }
 
-  SimPWMData[port->channel].position = pos;
+  DigitalPort* dPort = port.get();
+
+  // note, need to perform the multiplication below as floating point before
+  // converting to int
+  int32_t rawValue = static_cast<int32_t>(
+      (pos * static_cast<double>(GetFullRangeScaleFactor(dPort))) +
+      GetMinNegativePwm(dPort));
+
+  if (rawValue == kPwmDisabled) {
+    *status = HAL_PWM_SCALE_ERROR;
+    return;
+  }
+
+  HAL_SetPWMPulseTimeMicroseconds(pwmPortHandle, rawValue, status);
 }
 
 void HAL_SetPWMDisabled(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
@@ -231,19 +310,20 @@
     *status = HAL_HANDLE_ERROR;
     return;
   }
-  SimPWMData[port->channel].rawValue = 0;
+  SimPWMData[port->channel].pulseMicrosecond = 0;
   SimPWMData[port->channel].position = 0;
   SimPWMData[port->channel].speed = 0;
 }
 
-int32_t HAL_GetPWMRaw(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
+int32_t HAL_GetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle,
+                                        int32_t* status) {
   auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
   if (port == nullptr) {
     *status = HAL_HANDLE_ERROR;
     return 0;
   }
 
-  return SimPWMData[port->channel].rawValue;
+  return SimPWMData[port->channel].pulseMicrosecond;
 }
 
 double HAL_GetPWMSpeed(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
@@ -299,6 +379,19 @@
   SimPWMData[port->channel].zeroLatch = false;
 }
 
+void HAL_SetPWMAlwaysHighMode(HAL_DigitalHandle pwmPortHandle,
+                              int32_t* status) {
+  auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
+  if (port == nullptr) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  SimPWMData[port->channel].pulseMicrosecond = 0xFFFF;
+  SimPWMData[port->channel].position = 0xFFFF;
+  SimPWMData[port->channel].speed = 0xFFFF;
+}
+
 void HAL_SetPWMPeriodScale(HAL_DigitalHandle pwmPortHandle, int32_t squelchMask,
                            int32_t* status) {
   auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
diff --git a/third_party/allwpilib/hal/src/main/native/sim/Power.cpp b/third_party/allwpilib/hal/src/main/native/sim/Power.cpp
index 08d5638..1eca18f 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/Power.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/Power.cpp
@@ -32,6 +32,7 @@
 int32_t HAL_GetUserCurrentFaults6V(int32_t* status) {
   return SimRoboRioData->userFaults6V;
 }
+void HAL_SetUserRailEnabled6V(HAL_Bool enabled, int32_t* status) {}
 double HAL_GetUserVoltage5V(int32_t* status) {
   return SimRoboRioData->userVoltage5V;
 }
@@ -44,6 +45,7 @@
 int32_t HAL_GetUserCurrentFaults5V(int32_t* status) {
   return SimRoboRioData->userFaults5V;
 }
+void HAL_SetUserRailEnabled5V(HAL_Bool enabled, int32_t* status) {}
 double HAL_GetUserVoltage3V3(int32_t* status) {
   return SimRoboRioData->userVoltage3V3;
 }
@@ -56,10 +58,14 @@
 int32_t HAL_GetUserCurrentFaults3V3(int32_t* status) {
   return SimRoboRioData->userFaults3V3;
 }
+void HAL_SetUserRailEnabled3V3(HAL_Bool enabled, int32_t* status) {}
 void HAL_SetBrownoutVoltage(double voltage, int32_t* status) {
   SimRoboRioData->brownoutVoltage = voltage;
 }
 double HAL_GetBrownoutVoltage(int32_t* status) {
   return SimRoboRioData->brownoutVoltage;
 }
+double HAL_GetCPUTemp(int32_t* status) {
+  return SimRoboRioData->cpuTemp;
+}
 }  // extern "C"
diff --git a/third_party/allwpilib/hal/src/main/native/sim/REVPH.cpp b/third_party/allwpilib/hal/src/main/native/sim/REVPH.cpp
index 163ca9c..a9e96f1 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/REVPH.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/REVPH.cpp
@@ -201,7 +201,7 @@
 
   std::scoped_lock lock{pcm->lock};
   auto& data = SimREVPHData[pcm->module].solenoidOutput;
-  uint8_t ret = 0;
+  int32_t ret = 0;
   for (int i = 0; i < kNumREVPHChannels; i++) {
     ret |= (data[i] << i);
   }
diff --git a/third_party/allwpilib/hal/src/main/native/sim/SimDevice.cpp b/third_party/allwpilib/hal/src/main/native/sim/SimDevice.cpp
index b6c637c..48b755e 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/SimDevice.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/SimDevice.cpp
@@ -26,6 +26,10 @@
   SimSimDeviceData->FreeDevice(handle);
 }
 
+const char* HAL_GetSimDeviceName(HAL_SimDeviceHandle handle) {
+  return SimSimDeviceData->GetDeviceName(handle);
+}
+
 HAL_SimValueHandle HAL_CreateSimValue(HAL_SimDeviceHandle device,
                                       const char* name, int32_t direction,
                                       const struct HAL_Value* initialValue) {
diff --git a/third_party/allwpilib/hal/src/main/native/sim/mockdata/PWMData.cpp b/third_party/allwpilib/hal/src/main/native/sim/mockdata/PWMData.cpp
index a221d13..005d95d 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/mockdata/PWMData.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/mockdata/PWMData.cpp
@@ -17,7 +17,7 @@
 PWMData* hal::SimPWMData;
 void PWMData::ResetData() {
   initialized.Reset(false);
-  rawValue.Reset(0);
+  pulseMicrosecond.Reset(0);
   speed.Reset(0);
   position.Reset(0);
   periodScale.Reset(0);
@@ -34,7 +34,7 @@
                                LOWERNAME)
 
 DEFINE_CAPI(HAL_Bool, Initialized, initialized)
-DEFINE_CAPI(int32_t, RawValue, rawValue)
+DEFINE_CAPI(int32_t, PulseMicrosecond, pulseMicrosecond)
 DEFINE_CAPI(double, Speed, speed)
 DEFINE_CAPI(double, Position, position)
 DEFINE_CAPI(int32_t, PeriodScale, periodScale)
@@ -46,7 +46,7 @@
 void HALSIM_RegisterPWMAllCallbacks(int32_t index, HAL_NotifyCallback callback,
                                     void* param, HAL_Bool initialNotify) {
   REGISTER(initialized);
-  REGISTER(rawValue);
+  REGISTER(pulseMicrosecond);
   REGISTER(speed);
   REGISTER(position);
   REGISTER(periodScale);
diff --git a/third_party/allwpilib/hal/src/main/native/sim/mockdata/PWMDataInternal.h b/third_party/allwpilib/hal/src/main/native/sim/mockdata/PWMDataInternal.h
index 737ced6..dd31a2a 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/mockdata/PWMDataInternal.h
+++ b/third_party/allwpilib/hal/src/main/native/sim/mockdata/PWMDataInternal.h
@@ -10,7 +10,7 @@
 namespace hal {
 class PWMData {
   HAL_SIMDATAVALUE_DEFINE_NAME(Initialized)
-  HAL_SIMDATAVALUE_DEFINE_NAME(RawValue)
+  HAL_SIMDATAVALUE_DEFINE_NAME(PulseMicrosecond)
   HAL_SIMDATAVALUE_DEFINE_NAME(Speed)
   HAL_SIMDATAVALUE_DEFINE_NAME(Position)
   HAL_SIMDATAVALUE_DEFINE_NAME(PeriodScale)
@@ -19,7 +19,8 @@
  public:
   SimDataValue<HAL_Bool, HAL_MakeBoolean, GetInitializedName> initialized{
       false};
-  SimDataValue<int32_t, HAL_MakeInt, GetRawValueName> rawValue{0};
+  SimDataValue<int32_t, HAL_MakeInt, GetPulseMicrosecondName> pulseMicrosecond{
+      0};
   SimDataValue<double, HAL_MakeDouble, GetSpeedName> speed{0};
   SimDataValue<double, HAL_MakeDouble, GetPositionName> position{0};
   SimDataValue<int32_t, HAL_MakeInt, GetPeriodScaleName> periodScale{0};
diff --git a/third_party/allwpilib/hal/src/main/native/sim/mockdata/RoboRioData.cpp b/third_party/allwpilib/hal/src/main/native/sim/mockdata/RoboRioData.cpp
index b73b0d9..9a9a1a1 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/mockdata/RoboRioData.cpp
+++ b/third_party/allwpilib/hal/src/main/native/sim/mockdata/RoboRioData.cpp
@@ -32,6 +32,8 @@
   userFaults5V.Reset(0);
   userFaults3V3.Reset(0);
   brownoutVoltage.Reset(6.75);
+  cpuTemp.Reset(45.0);
+  teamNumber.Reset(0);
   m_serialNumber = "";
   m_comments = "";
 }
@@ -132,6 +134,8 @@
 DEFINE_CAPI(int32_t, UserFaults5V, userFaults5V)
 DEFINE_CAPI(int32_t, UserFaults3V3, userFaults3V3)
 DEFINE_CAPI(double, BrownoutVoltage, brownoutVoltage)
+DEFINE_CAPI(double, CPUTemp, cpuTemp)
+DEFINE_CAPI(int32_t, TeamNumber, teamNumber)
 
 int32_t HALSIM_RegisterRoboRioSerialNumberCallback(
     HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) {
@@ -187,5 +191,6 @@
   REGISTER(userFaults5V);
   REGISTER(userFaults3V3);
   REGISTER(brownoutVoltage);
+  REGISTER(cpuTemp);
 }
 }  // extern "C"
diff --git a/third_party/allwpilib/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h b/third_party/allwpilib/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h
index c3ff17a..aef61be 100644
--- a/third_party/allwpilib/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h
+++ b/third_party/allwpilib/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h
@@ -30,6 +30,8 @@
   HAL_SIMDATAVALUE_DEFINE_NAME(UserFaults5V)
   HAL_SIMDATAVALUE_DEFINE_NAME(UserFaults3V3)
   HAL_SIMDATAVALUE_DEFINE_NAME(BrownoutVoltage)
+  HAL_SIMDATAVALUE_DEFINE_NAME(CPUTemp)
+  HAL_SIMDATAVALUE_DEFINE_NAME(TeamNumber)
 
   HAL_SIMCALLBACKREGISTRY_DEFINE_NAME(SerialNumber)
   HAL_SIMCALLBACKREGISTRY_DEFINE_NAME(Comments);
@@ -57,6 +59,8 @@
   SimDataValue<int32_t, HAL_MakeInt, GetUserFaults3V3Name> userFaults3V3{0};
   SimDataValue<double, HAL_MakeDouble, GetBrownoutVoltageName> brownoutVoltage{
       6.75};
+  SimDataValue<double, HAL_MakeDouble, GetCPUTempName> cpuTemp{45.0};
+  SimDataValue<int32_t, HAL_MakeInt, GetTeamNumberName> teamNumber{0};
 
   int32_t RegisterSerialNumberCallback(HAL_RoboRioStringCallback callback,
                                        void* param, HAL_Bool initialNotify);
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/HALTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/HALTest.cpp
index 62a4f85..0c4939e 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/HALTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/HALTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/HAL.h"
 
 namespace hal {
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/can/CANTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/can/CANTest.cpp
index db9dbf5..72a81cd 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/can/CANTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/can/CANTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/CANAPI.h"
 #include "hal/HAL.h"
 #include "hal/simulation/CanData.h"
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/handles/HandleTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/handles/HandleTest.cpp
index 5c98f7f..d230a06 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/handles/HandleTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/handles/HandleTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/HAL.h"
 #include "hal/handles/IndexedClassedHandleResource.h"
 
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/main.cpp b/third_party/allwpilib/hal/src/test/native/cpp/main.cpp
index 7981c04..20ccbad 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/main.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/HAL.h"
 
 int main(int argc, char** argv) {
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/AnalogInDataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/AnalogInDataTest.cpp
index 284ebd6..e823375 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/AnalogInDataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/AnalogInDataTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/AnalogInput.h"
 #include "hal/HAL.h"
 #include "hal/handles/HandlesInternal.h"
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/AnalogOutDataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/AnalogOutDataTest.cpp
index 15a5fa6..11dad05 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/AnalogOutDataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/AnalogOutDataTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/AnalogOutput.h"
 #include "hal/HAL.h"
 #include "hal/handles/HandlesInternal.h"
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/DIODataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/DIODataTest.cpp
index e8ab350..35bfac5 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/DIODataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/DIODataTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/DIO.h"
 #include "hal/HAL.h"
 #include "hal/handles/HandlesInternal.h"
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/DriverStationDataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/DriverStationDataTest.cpp
index 59658cb..ffab223 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/DriverStationDataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/DriverStationDataTest.cpp
@@ -4,7 +4,9 @@
 
 #include <cstring>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
+#include <wpi/StringExtras.h>
+
 #include "hal/HAL.h"
 #include "hal/simulation/DriverStationData.h"
 
@@ -116,13 +118,12 @@
 }
 
 TEST(DriverStationTest, EventInfo) {
-  std::string eventName = "UnitTest";
-  std::string gameData = "Insert game specific info here :D";
+  constexpr std::string_view eventName = "UnitTest";
+  constexpr std::string_view gameData = "Insert game specific info here :D";
   HAL_MatchInfo info;
-  std::snprintf(info.eventName, sizeof(info.eventName), "%s",
-                eventName.c_str());
-  std::snprintf(reinterpret_cast<char*>(info.gameSpecificMessage),
-                sizeof(info.gameSpecificMessage), "%s", gameData.c_str());
+  wpi::format_to_n_c_str(info.eventName, sizeof(info.eventName), eventName);
+  wpi::format_to_n_c_str(reinterpret_cast<char*>(info.gameSpecificMessage),
+                         sizeof(info.gameSpecificMessage), gameData);
   info.gameSpecificMessageSize = gameData.size();
   info.matchNumber = 5;
   info.matchType = HAL_MatchType::HAL_kMatchType_qualification;
@@ -135,7 +136,7 @@
   std::string gsm{reinterpret_cast<char*>(dataBack.gameSpecificMessage),
                   dataBack.gameSpecificMessageSize};
 
-  EXPECT_STREQ(eventName.c_str(), dataBack.eventName);
+  EXPECT_EQ(eventName, dataBack.eventName);
   EXPECT_EQ(gameData, gsm);
   EXPECT_EQ(5, dataBack.matchNumber);
   EXPECT_EQ(HAL_MatchType::HAL_kMatchType_qualification, dataBack.matchType);
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/I2CDataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/I2CDataTest.cpp
index 7678a4a..af4499b 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/I2CDataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/I2CDataTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/HAL.h"
 #include "hal/I2C.h"
 #include "hal/handles/HandlesInternal.h"
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PCMDataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PCMDataTest.cpp
index 58a94c0..70a36a9 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PCMDataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PCMDataTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/CTREPCM.h"
 #include "hal/HAL.h"
 #include "hal/handles/HandlesInternal.h"
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PDPDataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PDPDataTest.cpp
index 59b3ea4..2e9977f 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PDPDataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PDPDataTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/HAL.h"
 #include "hal/PowerDistribution.h"
 #include "hal/handles/HandlesInternal.h"
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PWMDataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PWMDataTest.cpp
index ab14704..b32e0a0 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PWMDataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/PWMDataTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/HAL.h"
 #include "hal/PWM.h"
 #include "hal/handles/HandlesInternal.h"
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/RelayDataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/RelayDataTest.cpp
index eef8631..e4017ca 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/RelayDataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/RelayDataTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/HAL.h"
 #include "hal/Relay.h"
 #include "hal/handles/HandlesInternal.h"
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/SPIDataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/SPIDataTest.cpp
index c427eaf..1ae6260 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/SPIDataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/SPIDataTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/HAL.h"
 #include "hal/SPI.h"
 #include "hal/handles/HandlesInternal.h"
diff --git a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/SimDeviceDataTest.cpp b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/SimDeviceDataTest.cpp
index 1203fd7..fef7ada 100644
--- a/third_party/allwpilib/hal/src/test/native/cpp/mockdata/SimDeviceDataTest.cpp
+++ b/third_party/allwpilib/hal/src/test/native/cpp/mockdata/SimDeviceDataTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "hal/SimDevice.h"
 #include "hal/simulation/SimDeviceData.h"
 
diff --git a/third_party/allwpilib/imgui/.styleguide b/third_party/allwpilib/imgui/.styleguide
index 5e97ca4..054020f 100644
--- a/third_party/allwpilib/imgui/.styleguide
+++ b/third_party/allwpilib/imgui/.styleguide
@@ -6,15 +6,3 @@
 cppSrcFileInclude {
   \.cpp$
 }
-
-modifiableFileExclude {
-}
-
-generatedFileExclude {
-}
-
-repoRootNameOverride {
-}
-
-includeOtherLibs {
-}
diff --git a/third_party/allwpilib/imgui/CMakeLists.txt b/third_party/allwpilib/imgui/CMakeLists.txt
index 17f6f64..6ba8455 100644
--- a/third_party/allwpilib/imgui/CMakeLists.txt
+++ b/third_party/allwpilib/imgui/CMakeLists.txt
@@ -13,12 +13,14 @@
 FetchContent_Declare(
     imgui
     GIT_REPOSITORY  https://github.com/ocornut/imgui.git
-    GIT_TAG         3ea0fad204e994d669f79ed29dcaf61cd5cb571d
+                    # docking branch
+    GIT_TAG         64b1e448d20c9be9275af731c34b4c7bf14a8e95
 )
 FetchContent_Declare(
     implot
     GIT_REPOSITORY  https://github.com/epezent/implot.git
-    GIT_TAG         e80e42e8b4136ddb84ccfe04fa28d0c745828952
+                    # v0.16
+    GIT_TAG         18c72431f8265e2b0b5378a3a73d8a883b2175ff
 )
 FetchContent_Declare(
     fonts
@@ -67,15 +69,19 @@
 file(GLOB imgui_sources ${imgui_SOURCE_DIR}/*.cpp ${imgui_SOURCE_DIR}/misc/cpp/*.cpp)
 file(GLOB implot_sources ${implot_SOURCE_DIR}/*.cpp)
 file(GLOB fonts_sources ${fonts_SOURCE_DIR}/src/*.cpp)
-add_library(imgui STATIC
+set(imgui_all_sources
     ${imgui_sources}
     ${implot_sources}
     ${imgui_SOURCE_DIR}/backends/imgui_impl_glfw.cpp
     ${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp
     ${gl3w_BINARY_DIR}/src/gl3w.c
     ${fonts_sources}
-    src/stb_image.cpp
-)
+    src/stb_image.cpp)
+if (MSVC)
+  add_library(imgui STATIC ${imgui_all_sources})
+else()
+  add_library(imgui ${imgui_all_sources})
+endif()
 target_compile_definitions(imgui PUBLIC IMGUI_IMPL_OPENGL_LOADER_GL3W)
 if (MSVC)
     target_sources(imgui PRIVATE ${imgui_SOURCE_DIR}/backends/imgui_impl_dx11.cpp)
diff --git a/third_party/allwpilib/msvcruntime/build.gradle b/third_party/allwpilib/msvcruntime/build.gradle
index 99e14ed..7e268e8 100644
--- a/third_party/allwpilib/msvcruntime/build.gradle
+++ b/third_party/allwpilib/msvcruntime/build.gradle
@@ -36,6 +36,7 @@
         return
     }
 
+    // TODO Remove VersionNumber
     def expectedVersion = VersionNumber.parse(defaultRedistFile.text.trim())
 
     def runtimeLocation = file("$vsDirectory\\VC\\Redist\\MSVC\\$expectedVersion")
@@ -53,7 +54,7 @@
         def x64ZipTask = tasks.create('x64RuntimeZip', Zip) {
             destinationDirectory = outputsFolder
             archiveBaseName = zipBaseName
-            classifier = 'x64'
+            archiveClassifier = 'x64'
 
             from x64Folder
         }
diff --git a/third_party/allwpilib/myRobot/build.gradle b/third_party/allwpilib/myRobot/build.gradle
index d2e98e6..17407cd 100644
--- a/third_party/allwpilib/myRobot/build.gradle
+++ b/third_party/allwpilib/myRobot/build.gradle
@@ -31,7 +31,9 @@
 
 apply from: "${rootDir}/shared/opencv.gradle"
 
-mainClassName = 'frc.robot.Main'
+application {
+    mainClass = 'frc.robot.Main'
+}
 
 apply plugin: 'com.github.johnrengelman.shadow'
 
@@ -51,6 +53,7 @@
     implementation project(':cscore')
     implementation project(':cameraserver')
     implementation project(':wpilibNewCommands')
+    implementation project(':apriltag')
 }
 
 tasks.withType(com.github.spotbugs.snom.SpotBugsTask).configureEach {
@@ -182,6 +185,7 @@
                         deploy.targets.roborio.artifacts.myRobotCppJava.binary = binary
                     }
                 }
+                lib project: ':apriltag', library: 'apriltag', linkage: 'shared'
                 lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'shared'
                 lib project: ':wpilibc', library: 'wpilibc', linkage: 'shared'
                 lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
@@ -230,6 +234,7 @@
                         deploy.targets.roborio.artifacts.myRobotCppStatic.binary = binary
                     }
                 }
+                lib project: ':apriltag', library: 'apriltag', linkage: 'static'
                 lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'static'
                 lib project: ':wpilibc', library: 'wpilibc', linkage: 'static'
                 lib project: ':wpimath', library: 'wpimath', linkage: 'static'
@@ -265,6 +270,7 @@
                                 run.dependsOn it.tasks.install
                                 run.systemProperty 'java.library.path', filePath
                                 run.environment 'LD_LIBRARY_PATH', filePath
+                                run.environment 'DYLD_LIBRARY_PATH', filePath
 
                                 def installTask = it.tasks.install
 
diff --git a/third_party/allwpilib/ntcore/.styleguide b/third_party/allwpilib/ntcore/.styleguide
index ef9cbb7..1808bb5 100644
--- a/third_party/allwpilib/ntcore/.styleguide
+++ b/third_party/allwpilib/ntcore/.styleguide
@@ -28,6 +28,7 @@
 
 includeOtherLibs {
   ^fmt/
+  ^gtest/
   ^support/
   ^wpi/
   ^wpinet/
diff --git a/third_party/allwpilib/ntcore/CMakeLists.txt b/third_party/allwpilib/ntcore/CMakeLists.txt
index cd4df28..5216eda 100644
--- a/third_party/allwpilib/ntcore/CMakeLists.txt
+++ b/third_party/allwpilib/ntcore/CMakeLists.txt
@@ -33,7 +33,7 @@
 
 set_property(TARGET ntcore PROPERTY FOLDER "libraries")
 
-install(TARGETS ntcore EXPORT ntcore DESTINATION "${main_lib_dest}")
+install(TARGETS ntcore EXPORT ntcore)
 install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/ntcore")
 install(DIRECTORY ${WPILIB_BINARY_DIR}/ntcore/generated/main/native/include/ DESTINATION "${include_dest}/ntcore")
 
@@ -54,6 +54,11 @@
     include(UseJava)
     set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
 
+    file(GLOB QUICKBUF_JAR
+        ${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf/*.jar)
+
+    set(CMAKE_JAVA_INCLUDE_PATH wpimath.jar ${QUICKBUF_JAR})
+
     file(GLOB ntcore_jni_src
         src/main/native/cpp/jni/*.cpp
         ${WPILIB_BINARY_DIR}/ntcore/generated/main/native/cpp/jni/*.cpp)
@@ -61,12 +66,7 @@
     file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java ${WPILIB_BINARY_DIR}/ntcore/generated/*.java)
     set(CMAKE_JNI_TARGET true)
 
-    if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-        set(CMAKE_JAVA_COMPILE_FLAGS "-h" "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-        add_jar(ntcore_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar OUTPUT_NAME ntcore)
-    else()
-        add_jar(ntcore_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar OUTPUT_NAME ntcore GENERATE_NATIVE_HEADERS ntcore_jni_headers)
-    endif()
+    add_jar(ntcore_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar OUTPUT_NAME ntcore GENERATE_NATIVE_HEADERS ntcore_jni_headers)
 
     get_property(NTCORE_JAR_FILE TARGET ntcore_jar PROPERTY JAR_FILE)
     install(FILES ${NTCORE_JAR_FILE} DESTINATION "${java_lib_dest}")
@@ -83,23 +83,33 @@
         install(TARGETS ntcorejni RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
     endif()
 
-    if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-        target_include_directories(ntcorejni PRIVATE ${JNI_INCLUDE_DIRS})
-        target_include_directories(ntcorejni PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-    else()
-        target_link_libraries(ntcorejni PRIVATE ntcore_jni_headers)
-    endif()
+    target_link_libraries(ntcorejni PRIVATE ntcore_jni_headers)
     add_dependencies(ntcorejni ntcore_jar)
 
-    install(TARGETS ntcorejni EXPORT ntcorejni DESTINATION "${main_lib_dest}")
+    install(TARGETS ntcorejni EXPORT ntcorejni)
 
 endif()
 
+if (WITH_JAVA_SOURCE)
+    find_package(Java REQUIRED)
+    include(UseJava)
+    file(GLOB NTCORE_SOURCES src/main/java/edu/wpi/first/networktables/*.java ${WPILIB_BINARY_DIR}/ntcore/generated/*.java)
+    add_jar(ntcore_src_jar
+    RESOURCES NAMESPACE "edu/wpi/first/networktables" ${NTCORE_SOURCES}
+    OUTPUT_NAME ntcore-sources)
+
+    get_property(NTCORE_SRC_JAR_FILE TARGET ntcore_src_jar PROPERTY JAR_FILE)
+    install(FILES ${NTCORE_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+    set_property(TARGET ntcore_src_jar PROPERTY FOLDER "java")
+endif()
+
 add_executable(ntcoredev src/dev/native/cpp/main.cpp)
+wpilib_target_warnings(ntcoredev)
 target_link_libraries(ntcoredev ntcore)
 
 if (WITH_TESTS)
     wpilib_add_test(ntcore src/test/native/cpp)
     target_include_directories(ntcore_test PRIVATE src/main/native/cpp)
-    target_link_libraries(ntcore_test ntcore gmock_main)
+    target_link_libraries(ntcore_test ntcore gmock_main wpiutil_testlib)
 endif()
diff --git a/third_party/allwpilib/ntcore/doc/networktables4.adoc b/third_party/allwpilib/ntcore/doc/networktables4.adoc
index 5384ebf..da157e5 100644
--- a/third_party/allwpilib/ntcore/doc/networktables4.adoc
+++ b/third_party/allwpilib/ntcore/doc/networktables4.adoc
@@ -1,16 +1,37 @@
-= Network Tables Protocol Specification, Version 4.0
+= Network Tables Protocol Specification, Version 4.1
 WPILib Developers <wpilib@wpi.edu>
-Protocol Revision 4.0, 2/14/2021
+Protocol Revision 4.1, 10/1/2023
 :toc:
 :toc-placement: preamble
 :sectanchors:
 
 A pub/sub WebSockets protocol based on NetworkTables concepts.
 
-[[motivation]]
-== Motivation
+[[motivation4.1]]
+== Motivation for Version 4.1
 
-Currently in NetworkTables there is no way to synchronize user value updates and NT update sweeps, and if user value updates occur more frequently than NT update sweeps, the intermediate values are lost.  This prevents NetworkTables from being a viable transport layer for seeing all value changes (e.g. for plotting) at rates higher than the NetworkTables update rate (e.g. for capturing high frequency PID changes).  While custom code can work around the second issue, it is more difficult to work around the first issue (unless full timestamps are also sent).
+While NetworkTables 4.0 made a large number of improvements to the 3.0 protocol, a few weaknesses have been discovered in "real world" use:
+
+* Keep alives are not required. This can result in very long timeframes before a disconnect is detected.
+* Periodic synchronization of timestamps is impacted by high variability of round trip time measurements on a stream connection shared with other data (due to network queueing in adverse network connections), resulting in values being "too old" even if actually more recent due to a change in base time
+* Disconnect loops can be caused by large amounts of data values being sent in response to a "subscribe all" type of message (e.g. subscribe with empty or `$` prefix), resulting in data transmission being blocked for an excessive amount of time
+* Publishing operations are not clearly subscriber-driven; the information is available via metatopics but not automatically sent to clients when clients publish
+
+Version 4.1 makes the following key changes to address these weaknesses:
+
+* Mandate the server and client send periodic WebSockets PING messages and track PONG responses
+* Recommend that timestamp synchronization occur immediately following connection establishment and prior to any other control messages
+* Recommend text and binary combining into a single WebSockets frame be limited to the network MTU (unless necessary to transport the message)
+* Recommend WebSockets fragmentation be used on large frames to enable rapid handling of PING messages
+* Add an option for topics to be marked transient (in which case no last value is retained by the server or sent to clients on initial subscription)
+* Recommend clients subscribe to the `$sub$<topic>` meta-topic for each topic published by the client, and use this information to control what value updates are sent over the network to the server
+
+Version 4.1 uses a different WebSockets subprotocol string than version 4.0, so it is easy for both clients and servers to simultaneously support both versions 4.0 and 4.1. Due to WebSockets implementation bugs in version 4.0, version 4.1 implementations must not send WebSockets PING messages on version 4.0 connections.
+
+[[motivation]]
+== Motivation for Version 4.0
+
+In <<networktables3,NetworkTables 3.0>>, there is no way to synchronize user value updates and NT update sweeps, and if user value updates occur more frequently than NT update sweeps, the intermediate values are lost.  This prevents NetworkTables from being a viable transport layer for seeing all value changes (e.g. for plotting) at rates higher than the NetworkTables update rate (e.g. for capturing high frequency PID changes).  While custom code can work around the second issue, it is more difficult to work around the first issue (unless full timestamps are also sent).
 
 Adding built-in support for capturing and communicating all timestamped data value changes with minimal additional user code changes will make it much easier for inexperienced teams to get high resolution, accurate data to dashboard displays with the minimal possible bandwidth and airtime usage.  Assuming the dashboard performs record and playback of NT updates, this also meets the desire to provide teams a robust data capture and playback mechanism.
 
@@ -67,6 +88,15 @@
 
 Due to the fact there can be multiple publishers for a single topic and unpredictable network delays / clock drift, there is no global total order for timestamps either globally or on a per-topic basis.  While single publishers for real-time data will be the norm, and in that case the timestamps will usually be in order, applications that use timestamps need to be able to handle out-of-order timestamps.
 
+[[aliveness]]
+=== Connection Aliveness Checking
+
+With a version 4.1 connection, both the client and the server should send periodic WebSockets PING messages and look for a PONG response within a reasonable period of time. On version 4.0 connections, or if this is not possible (e.g. the underlying WebSockets implementation does not have the ability to send PING messages), the client should use timestamp messages for aliveness testing. If no response is received after an appropriate amount of time, the client or server shall disconnect the WebSockets connection and try to re-establish a new connection.
+
+As the WebSockets protocol allows PONG responses to be sent in the middle of another message stream, WebSockets PING messages are preferred, as this allows for a shorter timeout period that is not dependent on the size of the transmitted messages. Sending a ping every 200 ms with a timeout of 1 second is recommended in this case.
+
+If using timestamp messages for aliveness checking on the primary connection, the client should use a timeout long enough to account for the largest expected message size (as the server can only respond after such a message has been completely transmitted). Sending a ping every 1 second with a timeout of 3 seconds is recommended in this case. If provided by the server, the <<rtt-subprotocol>> can be used in addition to the primary connection for aliveness testing with a shorter timeout.
+
 [[reconnection]]
 === Caching and Reconnection Handling
 
@@ -127,10 +157,12 @@
 
 Clients are responsible for keeping server connections established (e.g. via retries when a connection is lost).  Topic IDs must be treated as connection-specific; if the connection to the server is lost, the client is responsible for sending new <<msg-publish,`publish`>> and <<msg-subscribe,`subscribe`>> messages as required for the application when a new connection is established, and not using old topic IDs, but rather waiting for new <<msg-announce,`announce`>> messages to be received.
 
-Except for offline-published values with timestamps of 0, the client shall not send any other published values to the server until its clock is synchronized with the server per the <<timestamps>> section.
+Except for offline-published values with timestamps of 0, the client shall not send any other published values to the server until its clock is synchronized with the server per the <<timestamps>> section. Clients should measure RTT prior to sending any control messages (to avoid other traffic disrupting the measurement).
 
 Clients may publish a value at any time following clock synchronization.  Clients may subscribe to meta-topics to determine whether or not to publish a value change (e.g. based on whether there are any subscribers, or based on specific <<sub-options>>).
 
+Clients should subscribe to the `$sub$<topic>` meta topic for each topic published and use this metadata to determine how frequently to send updates to the network. However, this is not required--clients may choose to ignore this and send updates at any time.
+
 [[meta-topics]]
 === Server-Published Meta Topics
 
@@ -300,10 +332,22 @@
 
 Servers shall support a resource name of `/nt/<name>`, where `<name>` is an arbitrary string representing the client name.  The client name does not need to be unique; multiple connections to the same name are allowed; the server shall ensure the name is unique (for the purposes of meta-topics) by appending a '@' and a unique number (if necessary).  To support this, the name provided by the client should not contain an embedded '@'.  Clients should provide a way to specify the resource name (in particular, the client name portion).
 
-Both clients and servers shall support/use subprotocol `networktables.first.wpi.edu` for this protocol. Clients and servers shall terminate the connection in accordance with the WebSocket protocol unless both sides support this subprotocol.
+Both clients and servers should support/use subprotocol `v4.1.networktables.first.wpi.edu` (for version 4.1) and `networktables.first.wpi.edu` (for version 4.0). Version 4.1 should be preferred, with version 4.0 as a fallback, using standard WebSockets subprotocol negotiation. Clients and servers shall terminate the connection in accordance with the WebSocket protocol unless both sides support a common subprotocol.
 
 The unsecure standard server port number shall be 5810, the secure standard port number shall be 5811.
 
+[[fragmentation]]
+=== Fragmentation
+
+Combining multiple text or binary messages into a single WebSockets frame should be limited such that the WebSockets frame does not exceed the MTU unless otherwise required to fit the total binary data size.
+
+Client and server implementations should fragment WebSockets messages to roughly the network MTU in order to facilitate rapid handling of PING and PONG messages.
+
+[[rtt-subprotocol]]
+=== RTT Subprotocol
+
+Servers should provide subprotocol `rtt.networktables.first.wpi.edu` for RTT-only messages. This subprotocol provides a separate channel that can be used for RTT messages to avoid delays caused by other value transmissions. Clients that cannot send WebSocket PING messages are recommended to use this subprotocol (if available) for aliveness testing. Connections using this subprotocol do not appear in the client connections list. No text frames are used; only <<binary-frames>> with Topic ID of -1 (RTT measurement) should be sent by the client and responded to by the server.
+
 [[data-types]]
 == Supported Data Types
 
diff --git a/third_party/allwpilib/ntcore/manualTests/native/rpc_local.cpp b/third_party/allwpilib/ntcore/manualTests/native/rpc_local.cpp
deleted file mode 100644
index 2879c33..0000000
--- a/third_party/allwpilib/ntcore/manualTests/native/rpc_local.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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 <chrono>
-#include <climits>
-#include <cstdio>
-#include <thread>
-
-#include <support/json.h>
-
-#include "ntcore.h"
-
-void callback1(const nt::RpcAnswer& answer) {
-  wpi::json params;
-  try {
-    params = wpi::json::from_cbor(answer.params);
-  } catch (wpi::json::parse_error err) {
-    std::fputs("could not decode params?\n", stderr);
-    return;
-  }
-  if (!params.is_number()) {
-    std::fputs("did not get number\n", stderr);
-    return;
-  }
-  double val = params.get<double>();
-  std::fprintf(stderr, "called with %g\n", val);
-
-  answer.PostResponse(wpi::json::to_cbor(val + 1.2));
-}
-
-int main() {
-  auto inst = nt::GetDefaultInstance();
-  nt::AddLogger(
-      inst,
-      [](const nt::LogMessage& msg) {
-        std::fputs(msg.message.c_str(), stderr);
-        std::fputc('\n', stderr);
-      },
-      0, UINT_MAX);
-
-  nt::StartServer(inst, "rpc_local.ini", "", 10000);
-  auto entry = nt::GetEntry(inst, "func1");
-  nt::CreateRpc(entry, nt::StringRef("", 1), callback1);
-  std::fputs("calling rpc\n", stderr);
-  unsigned int call1_uid = nt::CallRpc(entry, wpi::json::to_cbor(2.0));
-  std::string call1_result_str;
-  std::fputs("waiting for rpc result\n", stderr);
-  nt::GetRpcResult(entry, call1_uid, &call1_result_str);
-  wpi::json call1_result;
-  try {
-    call1_result = wpi::json::from_cbor(call1_result_str);
-  } catch (wpi::json::parse_error err) {
-    std::fputs("could not decode result?\n", stderr);
-    return 1;
-  }
-  if (!call1_result.is_number()) {
-    std::fputs("result is not number?\n", stderr);
-    return 1;
-  }
-  std::fprintf(stderr, "got %g\n", call1_result.get<double>());
-
-  return 0;
-}
diff --git a/third_party/allwpilib/ntcore/manualTests/native/rpc_speed.cpp b/third_party/allwpilib/ntcore/manualTests/native/rpc_speed.cpp
deleted file mode 100644
index 6f9dbbf..0000000
--- a/third_party/allwpilib/ntcore/manualTests/native/rpc_speed.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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 <chrono>
-#include <climits>
-#include <thread>
-
-#include <fmt/core.h>
-#include <support/json.h>
-
-#include "ntcore.h"
-
-void callback1(const nt::RpcAnswer& answer) {
-  wpi::json params;
-  try {
-    params = wpi::json::from_cbor(answer.params);
-  } catch (wpi::json::parse_error err) {
-    fmt::print(stderr, "could not decode params?\n");
-    return;
-  }
-  if (!params.is_number()) {
-    fmt::print(stderr, "did not get number\n");
-    return;
-  }
-  double val = params.get<double>();
-  answer.PostResponse(wpi::json::to_cbor(val + 1.2));
-}
-
-int main() {
-  auto inst = nt::GetDefaultInstance();
-  nt::StartServer(inst, "rpc_speed.ini", "", 10000);
-  auto entry = nt::GetEntry(inst, "func1");
-  nt::CreateRpc(entry, nt::StringRef("", 1), callback1);
-  std::string call1_result_str;
-
-  auto start2 = std::chrono::high_resolution_clock::now();
-  auto start = nt::Now();
-  for (int i = 0; i < 10000; ++i) {
-    unsigned int call1_uid = nt::CallRpc(entry, wpi::json::to_cbor(i));
-    nt::GetRpcResult(entry, call1_uid, &call1_result_str);
-    wpi::json call1_result;
-    try {
-      call1_result = wpi::json::from_cbor(call1_result_str);
-    } catch (wpi::json::parse_error err) {
-      fmt::print(stderr, "could not decode result?\n");
-      return 1;
-    }
-    if (!call1_result.is_number()) {
-      fmt::print(stderr, "result is not number?\n");
-      return 1;
-    }
-  }
-  auto end2 = std::chrono::high_resolution_clock::now();
-  auto end = nt::Now();
-  fmt::print(stderr, "nt::Now start={} end={}\n", start, end);
-  fmt::print(stderr, "std::chrono start={} end={}\n",
-             std::chrono::duration_cast<std::chrono::nanoseconds>(
-                 start2.time_since_epoch())
-                 .count(),
-             std::chrono::duration_cast<std::chrono::nanoseconds>(
-                 end2.time_since_epoch())
-                 .count());
-  fmt::print(stderr, "time/call = %g us\n", (end - start) / 10.0 / 10000.0);
-  std::chrono::duration<double, std::micro> diff = end2 - start2;
-  fmt::print(stderr, "time/call = {} us\n", diff.count() / 10000.0);
-}
diff --git a/third_party/allwpilib/ntcore/ntcore-config.cmake.in b/third_party/allwpilib/ntcore/ntcore-config.cmake.in
index 17006a5..0a85f8b 100644
--- a/third_party/allwpilib/ntcore/ntcore-config.cmake.in
+++ b/third_party/allwpilib/ntcore/ntcore-config.cmake.in
@@ -1,6 +1,7 @@
 include(CMakeFindDependencyMacro)
 @FILENAME_DEP_REPLACE@
 @WPIUTIL_DEP_REPLACE@
+@WPINET_DEP_REPLACE@
 
 @FILENAME_DEP_REPLACE@
 include(${SELF_DIR}/ntcore.cmake)
diff --git a/third_party/allwpilib/ntcore/src/dev/native/cpp/main.cpp b/third_party/allwpilib/ntcore/src/dev/native/cpp/main.cpp
index fac64e7..6e43fdb 100644
--- a/third_party/allwpilib/ntcore/src/dev/native/cpp/main.cpp
+++ b/third_party/allwpilib/ntcore/src/dev/native/cpp/main.cpp
@@ -3,26 +3,42 @@
 // the WPILib BSD license file in the root directory of this project.
 
 #include <algorithm>
+#include <array>
 #include <chrono>
 #include <cmath>
 #include <cstdlib>
 #include <numeric>
+#include <random>
 #include <string_view>
 #include <thread>
 
 #include <fmt/format.h>
 #include <wpi/Synchronization.h>
+#include <wpi/timestamp.h>
 
 #include "ntcore.h"
 #include "ntcore_cpp.h"
 
 void bench();
+void bench2();
+void stress();
 
 int main(int argc, char* argv[]) {
+  wpi::impl::SetupNowRio();
+
   if (argc == 2 && std::string_view{argv[1]} == "bench") {
     bench();
     return EXIT_SUCCESS;
   }
+  if (argc == 2 && std::string_view{argv[1]} == "bench2") {
+    bench2();
+    return EXIT_SUCCESS;
+  }
+  if (argc == 2 && std::string_view{argv[1]} == "stress") {
+    stress();
+    return EXIT_SUCCESS;
+  }
+
   auto myValue = nt::GetEntry(nt::GetDefaultInstance(), "MyValue");
 
   nt::SetEntryValue(myValue, nt::Value::MakeString("Hello World"));
@@ -106,3 +122,145 @@
   fmt::print("-- Flush --\n");
   PrintTimes(flushTimes);
 }
+
+void bench2() {
+  // set up instances
+  auto client1 = nt::CreateInstance();
+  auto client2 = nt::CreateInstance();
+  auto server = nt::CreateInstance();
+
+  // connect client and server
+  nt::StartServer(server, "bench2.json", "127.0.0.1", 10001, 10000);
+  nt::StartClient4(client1, "client1");
+  nt::StartClient3(client2, "client2");
+  nt::SetServer(client1, "127.0.0.1", 10000);
+  nt::SetServer(client2, "127.0.0.1", 10001);
+
+  using namespace std::chrono_literals;
+  std::this_thread::sleep_for(1s);
+
+  // add "typical" set of subscribers on client and server
+  nt::SubscribeMultiple(client1, {{std::string_view{}}});
+  nt::SubscribeMultiple(client2, {{std::string_view{}}});
+  nt::SubscribeMultiple(server, {{std::string_view{}}});
+
+  // create 1000 entries
+  std::array<NT_Entry, 1000> pubs;
+  for (int i = 0; i < 1000; ++i) {
+    pubs[i] = nt::GetEntry(
+        nt::GetTopic(server,
+                     fmt::format("/some/long/name/with/lots/of/slashes/{}", i)),
+        NT_DOUBLE_ARRAY, "double[]");
+  }
+
+  // warm up
+  for (int i = 1; i <= 100; ++i) {
+    for (auto pub : pubs) {
+      double vals[3] = {i * 0.01, i * 0.02, i * 0.03};
+      nt::SetDoubleArray(pub, vals);
+    }
+    nt::FlushLocal(server);
+    std::this_thread::sleep_for(0.02s);
+  }
+
+  std::vector<int64_t> flushTimes;
+  flushTimes.reserve(1001);
+
+  std::vector<int64_t> times;
+  times.reserve(1001);
+
+  // benchmark
+  auto start = std::chrono::high_resolution_clock::now();
+  int64_t now = nt::Now();
+  for (int i = 1; i <= 1000; ++i) {
+    for (auto pub : pubs) {
+      double vals[3] = {i * 0.01, i * 0.02, i * 0.03};
+      nt::SetDoubleArray(pub, vals);
+    }
+    int64_t prev = now;
+    now = nt::Now();
+    times.emplace_back(now - prev);
+    nt::FlushLocal(server);
+    nt::Flush(server);
+    flushTimes.emplace_back(nt::Now() - now);
+    std::this_thread::sleep_for(0.02s);
+    now = nt::Now();
+  }
+  auto stop = std::chrono::high_resolution_clock::now();
+
+  fmt::print("total time: {}us\n",
+             std::chrono::duration_cast<std::chrono::microseconds>(stop - start)
+                 .count());
+  PrintTimes(times);
+  fmt::print("-- Flush --\n");
+  PrintTimes(flushTimes);
+}
+
+static std::random_device r;
+static std::mt19937 gen(r());
+static std::uniform_real_distribution<double> dist;
+
+void stress() {
+  auto server = nt::CreateInstance();
+  nt::StartServer(server, "stress.json", "127.0.0.1", 0, 10000);
+  nt::SubscribeMultiple(server, {{std::string_view{}}});
+
+  using namespace std::chrono_literals;
+
+  for (int count = 0; count < 10; ++count) {
+    std::thread{[] {
+      auto client = nt::CreateInstance();
+      nt::SubscribeMultiple(client, {{std::string_view{}}});
+      for (int i = 0; i < 300; ++i) {
+        // sleep a random amount of time
+        std::this_thread::sleep_for(0.1s * dist(gen));
+
+        // connect
+        nt::StartClient4(client, "client");
+        nt::SetServer(client, "127.0.0.1", 10000);
+
+        // sleep a random amount of time
+        std::this_thread::sleep_for(0.1s * dist(gen));
+
+        // disconnect
+        nt::StopClient(client);
+      }
+      nt::DestroyInstance(client);
+    }}.detach();
+
+    std::thread{[server, count] {
+      for (int n = 0; n < 300; ++n) {
+        // sleep a random amount of time
+        std::this_thread::sleep_for(0.01s * dist(gen));
+
+        // create publishers
+        NT_Publisher pub[30];
+        for (int i = 0; i < 30; ++i) {
+          pub[i] =
+              nt::Publish(nt::GetTopic(server, fmt::format("{}_{}", count, i)),
+                          NT_DOUBLE, "double", {});
+        }
+
+        // publish values
+        for (int i = 0; i < 200; ++i) {
+          // sleep a random amount of time between each value set
+          std::this_thread::sleep_for(0.001s * dist(gen));
+          for (int i = 0; i < 30; ++i) {
+            nt::SetDouble(pub[i], dist(gen));
+          }
+          nt::FlushLocal(server);
+        }
+
+        // sleep a random amount of time
+        std::this_thread::sleep_for(0.1s * dist(gen));
+
+        // remove publishers
+        for (int i = 0; i < 30; ++i) {
+          nt::Unpublish(pub[i]);
+        }
+      }
+    }}.detach();
+  }
+
+  std::this_thread::sleep_for(100s);
+}
diff --git a/third_party/allwpilib/ntcore/src/generate/cpp/jni/types_jni.cpp.jinja b/third_party/allwpilib/ntcore/src/generate/cpp/jni/types_jni.cpp.jinja
index a105278..29cf570 100644
--- a/third_party/allwpilib/ntcore/src/generate/cpp/jni/types_jni.cpp.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/cpp/jni/types_jni.cpp.jinja
@@ -22,6 +22,8 @@
 static JClass {{ t.jni.jtype }}Cls;
 {%- endif %}
 {%- endfor %}
+static JException illegalArgEx;
+static JException indexOobEx;
 static JException nullPointerEx;
 
 static const JClassInit classes[] = {
@@ -36,6 +38,8 @@
 };
 
 static const JExceptionInit exceptions[] = {
+    {"java/lang/IllegalArgumentException", &illegalArgEx},
+    {"java/lang/IndexOutOfBoundsException", &indexOobEx},
     {"java/lang/NullPointerException", &nullPointerEx},
 };
 
@@ -65,12 +69,15 @@
   for (auto& c : classes) {
     c.cls->free(env);
   }
+  for (auto& c : exceptions) {
+    c.cls->free(env);
+  }
 }
 
 }  // namespace nt
 
 static std::vector<int> FromJavaBooleanArray(JNIEnv* env, jbooleanArray jarr) {
-  CriticalJBooleanArrayRef ref{env, jarr};
+  CriticalJSpan<const jboolean> ref{env, jarr};
   if (!ref) {
     return {};
   }
@@ -185,9 +192,67 @@
 {
   return {{ t.jni.ToJavaArray }}(env, nt::ReadQueueValues{{ t.TypeName }}(subentry));
 }
+{% if t.TypeName == "Raw" %}
+/*
+ * Class:     edu_wpi_first_networktables_NetworkTablesJNI
+ * Method:    setRaw
+ * Signature: (IJ[BII)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_networktables_NetworkTablesJNI_setRaw
+  (JNIEnv* env, jclass, jint entry, jlong time, jbyteArray value, jint start, jint len)
+{
+  if (!value) {
+    nullPointerEx.Throw(env, "value is null");
+    return false;
+  }
+  if (start < 0) {
+    indexOobEx.Throw(env, "start must be >= 0");
+    return false;
+  }
+  if (len < 0) {
+    indexOobEx.Throw(env, "len must be >= 0");
+    return false;
+  }
+  CriticalJSpan<const jbyte> cvalue{env, value};
+  if (static_cast<unsigned int>(start + len) > cvalue.size()) {
+    indexOobEx.Throw(env, "start + len must be smaller than array length");
+    return false;
+  }
+  return nt::SetRaw(entry, cvalue.uarray().subspan(start, len), time);
+}
 
 /*
  * Class:     edu_wpi_first_networktables_NetworkTablesJNI
+ * Method:    setRawBuffer
+ * Signature: (IJLjava/nio/ByteBuffer;II)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_networktables_NetworkTablesJNI_setRawBuffer
+  (JNIEnv* env, jclass, jint entry, jlong time, jobject value, jint start, jint len)
+{
+  if (!value) {
+    nullPointerEx.Throw(env, "value is null");
+    return false;
+  }
+  if (start < 0) {
+    indexOobEx.Throw(env, "start must be >= 0");
+    return false;
+  }
+  if (len < 0) {
+    indexOobEx.Throw(env, "len must be >= 0");
+    return false;
+  }
+  JSpan<const jbyte> cvalue{env, value, static_cast<size_t>(start + len)};
+  if (!cvalue) {
+    illegalArgEx.Throw(env, "value must be a native ByteBuffer");
+    return false;
+  }
+  return nt::SetRaw(entry, cvalue.uarray().subspan(start, len), time);
+}
+{% else %}
+/*
+ * Class:     edu_wpi_first_networktables_NetworkTablesJNI
  * Method:    set{{ t.TypeName }}
  * Signature: (IJ{{ t.jni.jtypestr }})Z
  */
@@ -203,7 +268,7 @@
 {%- endif %}
   return nt::Set{{ t.TypeName }}(entry, {{ t.jni.FromJavaBegin }}value{{ t.jni.FromJavaEnd }}, time);
 }
-
+{% endif %}
 /*
  * Class:     edu_wpi_first_networktables_NetworkTablesJNI
  * Method:    get{{ t.TypeName }}
@@ -223,9 +288,67 @@
   return nt::Get{{ t.TypeName }}(entry, defaultValue);
 {%- endif %}
 }
+{% if t.TypeName == "Raw" %}
+/*
+ * Class:     edu_wpi_first_networktables_NetworkTablesJNI
+ * Method:    setDefaultRaw
+ * Signature: (IJ[BII)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_networktables_NetworkTablesJNI_setDefaultRaw
+  (JNIEnv* env, jclass, jint entry, jlong, jbyteArray defaultValue, jint start, jint len)
+{
+  if (!defaultValue) {
+    nullPointerEx.Throw(env, "value is null");
+    return false;
+  }
+  if (start < 0) {
+    indexOobEx.Throw(env, "start must be >= 0");
+    return false;
+  }
+  if (len < 0) {
+    indexOobEx.Throw(env, "len must be >= 0");
+    return false;
+  }
+  CriticalJSpan<const jbyte> cvalue{env, defaultValue};
+  if (static_cast<unsigned int>(start + len) > cvalue.size()) {
+    indexOobEx.Throw(env, "start + len must be smaller than array length");
+    return false;
+  }
+  return nt::SetDefaultRaw(entry, cvalue.uarray().subspan(start, len));
+}
 
 /*
  * Class:     edu_wpi_first_networktables_NetworkTablesJNI
+ * Method:    setDefaultRawBuffer
+ * Signature: (IJLjava/nio/ByteBuffer;II)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_networktables_NetworkTablesJNI_setDefaultRawBuffer
+  (JNIEnv* env, jclass, jint entry, jlong, jobject defaultValue, jint start, jint len)
+{
+  if (!defaultValue) {
+    nullPointerEx.Throw(env, "value is null");
+    return false;
+  }
+  if (start < 0) {
+    indexOobEx.Throw(env, "start must be >= 0");
+    return false;
+  }
+  if (len < 0) {
+    indexOobEx.Throw(env, "len must be >= 0");
+    return false;
+  }
+  JSpan<const jbyte> cvalue{env, defaultValue, static_cast<size_t>(start + len)};
+  if (!cvalue) {
+    illegalArgEx.Throw(env, "value must be a native ByteBuffer");
+    return false;
+  }
+  return nt::SetDefaultRaw(entry, cvalue.uarray().subspan(start, len));
+}
+{% else %}
+/*
+ * Class:     edu_wpi_first_networktables_NetworkTablesJNI
  * Method:    setDefault{{ t.TypeName }}
  * Signature: (IJ{{ t.jni.jtypestr }})Z
  */
@@ -241,5 +364,6 @@
 {%- endif %}
   return nt::SetDefault{{ t.TypeName }}(entry, {{ t.jni.FromJavaBegin }}defaultValue{{ t.jni.FromJavaEnd }});
 }
+{% endif %}
 {% endfor %}
 }  // extern "C"
diff --git a/third_party/allwpilib/ntcore/src/generate/cpp/ntcore_cpp_types.cpp.jinja b/third_party/allwpilib/ntcore/src/generate/cpp/ntcore_cpp_types.cpp.jinja
index ba8bca7..cdaf27f 100644
--- a/third_party/allwpilib/ntcore/src/generate/cpp/ntcore_cpp_types.cpp.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/cpp/ntcore_cpp_types.cpp.jinja
@@ -7,69 +7,124 @@
 #include "Handle.h"
 #include "InstanceImpl.h"
 
+namespace {
+template <nt::ValidType T>
+struct ValuesType {
+  using Vector =
+      std::vector<typename nt::TypeInfo<std::remove_cvref_t<T>>::Value>;
+};
+
+template <>
+struct ValuesType<bool> {
+  using Vector = std::vector<int>;
+};
+}  // namespace
+
 namespace nt {
-{% for t in types %}
-bool Set{{ t.TypeName }}(NT_Handle pubentry, {{ t.cpp.ParamType }} value, int64_t time) {
+
+template <ValidType T>
+static inline bool Set(NT_Handle pubentry, typename TypeInfo<T>::View value,
+                       int64_t time) {
   if (auto ii = InstanceImpl::Get(Handle{pubentry}.GetInst())) {
-    return ii->localStorage.SetEntryValue(pubentry,
-        Value::Make{{ t.TypeName }}(value, time == 0 ? Now() : time));
+    return ii->localStorage.SetEntryValue(
+        pubentry, MakeValue<T>(value, time == 0 ? Now() : time));
   } else {
     return {};
   }
 }
 
-bool SetDefault{{ t.TypeName }}(NT_Handle pubentry, {{ t.cpp.ParamType }} defaultValue) {
+template <ValidType T>
+static inline bool SetDefault(NT_Handle pubentry,
+                              typename TypeInfo<T>::View defaultValue) {
   if (auto ii = InstanceImpl::Get(Handle{pubentry}.GetInst())) {
     return ii->localStorage.SetDefaultEntryValue(pubentry,
-        Value::Make{{ t.TypeName }}(defaultValue, 1));
+                                                 MakeValue<T>(defaultValue, 1));
   } else {
     return {};
   }
 }
 
-{{ t.cpp.ValueType }} Get{{ t.TypeName }}(NT_Handle subentry, {{ t.cpp.ParamType }} defaultValue) {
-  return GetAtomic{{ t.TypeName }}(subentry, defaultValue).value;
-}
-
-Timestamped{{ t.TypeName }} GetAtomic{{ t.TypeName }}(NT_Handle subentry, {{ t.cpp.ParamType }} defaultValue) {
+template <ValidType T>
+static inline Timestamped<typename TypeInfo<T>::Value> GetAtomic(
+    NT_Handle subentry, typename TypeInfo<T>::View defaultValue) {
   if (auto ii = InstanceImpl::Get(Handle{subentry}.GetInst())) {
-    return ii->localStorage.GetAtomic{{ t.TypeName }}(subentry, defaultValue);
+    return ii->localStorage.GetAtomic<T>(subentry, defaultValue);
   } else {
     return {};
   }
 }
 
-std::vector<Timestamped{{ t.TypeName }}> ReadQueue{{ t.TypeName }}(NT_Handle subentry) {
+template <ValidType T>
+inline Timestamped<typename TypeInfo<T>::SmallRet> GetAtomic(
+    NT_Handle subentry,
+    wpi::SmallVectorImpl<typename TypeInfo<T>::SmallElem>& buf,
+    typename TypeInfo<T>::View defaultValue) {
   if (auto ii = InstanceImpl::Get(Handle{subentry}.GetInst())) {
-    return ii->localStorage.ReadQueue{{ t.TypeName }}(subentry);
+    return ii->localStorage.GetAtomic<T>(subentry, buf, defaultValue);
   } else {
     return {};
   }
 }
 
-std::vector<{% if t.cpp.ValueType == "bool" %}int{% else %}{{ t.cpp.ValueType }}{% endif %}> ReadQueueValues{{ t.TypeName }}(NT_Handle subentry) {
-  std::vector<{% if t.cpp.ValueType == "bool" %}int{% else %}{{ t.cpp.ValueType }}{% endif %}> rv;
-  auto arr = ReadQueue{{ t.TypeName }}(subentry);
+template <typename T>
+static inline std::vector<Timestamped<typename TypeInfo<T>::Value>> ReadQueue(
+    NT_Handle subentry) {
+  if (auto ii = InstanceImpl::Get(Handle{subentry}.GetInst())) {
+    return ii->localStorage.ReadQueue<T>(subentry);
+  } else {
+    return {};
+  }
+}
+
+template <typename T>
+static inline typename ValuesType<T>::Vector ReadQueueValues(
+    NT_Handle subentry) {
+  typename ValuesType<T>::Vector rv;
+  auto arr = ReadQueue<T>(subentry);
   rv.reserve(arr.size());
   for (auto&& elem : arr) {
     rv.emplace_back(std::move(elem.value));
   }
   return rv;
 }
+{% for t in types %}
+bool Set{{ t.TypeName }}(NT_Handle pubentry, {{ t.cpp.ParamType }} value, int64_t time) {
+  return Set<{{ t.cpp.TemplateType }}>(pubentry, value, time);
+}
+
+bool SetDefault{{ t.TypeName }}(NT_Handle pubentry, {{ t.cpp.ParamType }} defaultValue) {
+  return SetDefault<{{ t.cpp.TemplateType }}>(pubentry, defaultValue);
+}
+
+{{ t.cpp.ValueType }} Get{{ t.TypeName }}(NT_Handle subentry, {{ t.cpp.ParamType }} defaultValue) {
+  return GetAtomic<{{ t.cpp.TemplateType }}>(subentry, defaultValue).value;
+}
+
+Timestamped{{ t.TypeName }} GetAtomic{{ t.TypeName }}(
+    NT_Handle subentry, {{ t.cpp.ParamType }} defaultValue) {
+  return GetAtomic<{{ t.cpp.TemplateType }}>(subentry, defaultValue);
+}
+
+std::vector<Timestamped{{ t.TypeName }}> ReadQueue{{ t.TypeName }}(NT_Handle subentry) {
+  return ReadQueue<{{ t.cpp.TemplateType }}>(subentry);
+}
+
+std::vector<{% if t.cpp.ValueType == "bool" %}int{% else %}{{ t.cpp.ValueType }}{% endif %}> ReadQueueValues{{ t.TypeName }}(NT_Handle subentry) {
+  return ReadQueueValues<{{ t.cpp.TemplateType }}>(subentry);
+}
 {% if t.cpp.SmallRetType and t.cpp.SmallElemType %}
-{{ t.cpp.SmallRetType }} Get{{ t.TypeName }}(NT_Handle subentry, wpi::SmallVectorImpl<{{ t.cpp.SmallElemType }}>& buf, {{ t.cpp.ParamType }} defaultValue) {
-  return GetAtomic{{ t.TypeName }}(subentry, buf, defaultValue).value;
+{{ t.cpp.SmallRetType }} Get{{ t.TypeName }}(
+    NT_Handle subentry,
+    wpi::SmallVectorImpl<{{ t.cpp.SmallElemType }}>& buf,
+    {{ t.cpp.ParamType }} defaultValue) {
+  return GetAtomic<{{ t.cpp.TemplateType }}>(subentry, buf, defaultValue).value;
 }
 
 Timestamped{{ t.TypeName }}View GetAtomic{{ t.TypeName }}(
     NT_Handle subentry,
     wpi::SmallVectorImpl<{{ t.cpp.SmallElemType }}>& buf,
     {{ t.cpp.ParamType }} defaultValue) {
-  if (auto ii = InstanceImpl::Get(Handle{subentry}.GetInst())) {
-    return ii->localStorage.GetAtomic{{ t.TypeName }}(subentry, buf, defaultValue);
-  } else {
-    return {};
-  }
+  return GetAtomic<{{ t.cpp.TemplateType }}>(subentry, buf, defaultValue);
 }
 {% endif %}
 {% endfor %}
diff --git a/third_party/allwpilib/ntcore/src/generate/include/networktables/Topic.h.jinja b/third_party/allwpilib/ntcore/src/generate/include/networktables/Topic.h.jinja
index 84e80ec..ec2a915 100644
--- a/third_party/allwpilib/ntcore/src/generate/include/networktables/Topic.h.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/include/networktables/Topic.h.jinja
@@ -11,12 +11,13 @@
 #include <string_view>
 #include <vector>
 
+#include <wpi/json_fwd.h>
+
 #include "networktables/Topic.h"
 
 namespace wpi {
 template <typename T>
 class SmallVectorImpl;
-class json;
 }  // namespace wpi
 
 namespace nt {
diff --git a/third_party/allwpilib/ntcore/src/generate/include/ntcore_c_types.h.jinja b/third_party/allwpilib/ntcore/src/generate/include/ntcore_c_types.h.jinja
index d5b2448..83cc807 100644
--- a/third_party/allwpilib/ntcore/src/generate/include/ntcore_c_types.h.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/include/ntcore_c_types.h.jinja
@@ -15,7 +15,7 @@
 {% for t in types %}
 /**
  * Timestamped {{ t.TypeName }}.
- * @ingroup ntcore_c_handle_api
+ * @ingroup ntcore_c_api
  */
 struct NT_Timestamped{{ t.TypeName }} {
   /**
@@ -42,7 +42,7 @@
 
 /**
  * @defgroup ntcore_{{ t.TypeName }}_cfunc {{ t.TypeName }} Functions
- * @ingroup ntcore_c_handle_api
+ * @ingroup ntcore_c_api
  * @{
  */
 
diff --git a/third_party/allwpilib/ntcore/src/generate/include/ntcore_cpp_types.h.jinja b/third_party/allwpilib/ntcore/src/generate/include/ntcore_cpp_types.h.jinja
index e987186..df919de 100644
--- a/third_party/allwpilib/ntcore/src/generate/include/ntcore_cpp_types.h.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/include/ntcore_cpp_types.h.jinja
@@ -20,56 +20,43 @@
 }  // namespace wpi
 
 namespace nt {
+/**
+ * Timestamped value.
+ * @ingroup ntcore_cpp_handle_api
+ */
+template <typename T>
+struct Timestamped {
+  Timestamped() = default;
+  Timestamped(int64_t time, int64_t serverTime, T value)
+    : time{time}, serverTime{serverTime}, value{std::move(value)} {}
+
+  /**
+   * Time in local time base.
+   */
+  int64_t time = 0;
+
+  /**
+   * Time in server time base.  May be 0 or 1 for locally set values.
+   */
+  int64_t serverTime = 0;
+
+  /**
+   * Value.
+   */
+  T value = {};
+};
 {% for t in types %}
 /**
  * Timestamped {{ t.TypeName }}.
  * @ingroup ntcore_cpp_handle_api
  */
-struct Timestamped{{ t.TypeName }} {
-  Timestamped{{ t.TypeName }}() = default;
-  Timestamped{{ t.TypeName }}(int64_t time, int64_t serverTime, {{ t.cpp.ValueType }} value)
-    : time{time}, serverTime{serverTime}, value{std::move(value)} {}
-
-  /**
-   * Time in local time base.
-   */
-  int64_t time = 0;
-
-  /**
-   * Time in server time base.  May be 0 or 1 for locally set values.
-   */
-  int64_t serverTime = 0;
-
-  /**
-   * Value.
-   */
-  {{ t.cpp.ValueType }} value = {};
-};
+using Timestamped{{ t.TypeName }} = Timestamped<{{ t.cpp.ValueType }}>;
 {% if t.cpp.SmallRetType %}
 /**
  * Timestamped {{ t.TypeName }} view (for SmallVector-taking functions).
  * @ingroup ntcore_cpp_handle_api
  */
-struct Timestamped{{ t.TypeName }}View {
-  Timestamped{{ t.TypeName }}View() = default;
-  Timestamped{{ t.TypeName }}View(int64_t time, int64_t serverTime, {{ t.cpp.SmallRetType }} value)
-    : time{time}, serverTime{serverTime}, value{std::move(value)} {}
-
-  /**
-   * Time in local time base.
-   */
-  int64_t time = 0;
-
-  /**
-   * Time in server time base.  May be 0 or 1 for locally set values.
-   */
-  int64_t serverTime = 0;
-
-  /**
-   * Value.
-   */
-  {{ t.cpp.SmallRetType }} value = {};
-};
+using Timestamped{{ t.TypeName }}View = Timestamped<{{ t.cpp.SmallRetType }}>;
 {% endif %}
 /**
  * @defgroup ntcore_{{ t.TypeName }}_func {{ t.TypeName }} Functions
diff --git a/third_party/allwpilib/ntcore/src/generate/java/EntryImpl.java.jinja b/third_party/allwpilib/ntcore/src/generate/java/EntryImpl.java.jinja
index 43b31e4..b7432a7 100644
--- a/third_party/allwpilib/ntcore/src/generate/java/EntryImpl.java.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/java/EntryImpl.java.jinja
@@ -3,7 +3,9 @@
 // the WPILib BSD license file in the root directory of this project.
 
 package edu.wpi.first.networktables;
-
+{% if TypeName == "Raw" %}
+import java.nio.ByteBuffer;
+{% endif %}
 /** NetworkTables {{ TypeName }} implementation. */
 @SuppressWarnings("PMD.ArrayIsStoredDirectly")
 final class {{ TypeName }}EntryImpl extends EntryBase implements {{ TypeName }}Entry {
@@ -54,8 +56,28 @@
   public {{ java.ValueType }}[] readQueueValues() {
     return NetworkTablesJNI.readQueueValues{{ TypeName }}(m_handle);
   }
+{% if TypeName == "Raw" %}
+  @Override
+  public void set(byte[] value, int start, int len, long time) {
+    NetworkTablesJNI.setRaw(m_handle, time, value, start, len);
+  }
 
   @Override
+  public void set(ByteBuffer value, int start, int len, long time) {
+    NetworkTablesJNI.setRaw(m_handle, time, value, start, len);
+  }
+
+  @Override
+  public void setDefault(byte[] value, int start, int len) {
+    NetworkTablesJNI.setDefaultRaw(m_handle, 0, value, start, len);
+  }
+
+  @Override
+  public void setDefault(ByteBuffer value, int start, int len) {
+    NetworkTablesJNI.setDefaultRaw(m_handle, 0, value, start, len);
+  }
+{% else %}
+  @Override
   public void set({{ java.ValueType }} value, long time) {
     NetworkTablesJNI.set{{ TypeName }}(m_handle, time, value);
   }
@@ -64,7 +86,7 @@
   public void setDefault({{ java.ValueType }} value) {
     NetworkTablesJNI.setDefault{{ TypeName }}(m_handle, 0, value);
   }
-
+{% endif %}
   @Override
   public void unpublish() {
     NetworkTablesJNI.unpublish(m_handle);
diff --git a/third_party/allwpilib/ntcore/src/generate/java/GenericEntryImpl.java.jinja b/third_party/allwpilib/ntcore/src/generate/java/GenericEntryImpl.java.jinja
index e4296d7..29666bb 100644
--- a/third_party/allwpilib/ntcore/src/generate/java/GenericEntryImpl.java.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/java/GenericEntryImpl.java.jinja
@@ -4,6 +4,8 @@
 
 package edu.wpi.first.networktables;
 
+import java.nio.ByteBuffer;
+
 /** NetworkTables generic implementation. */
 final class GenericEntryImpl extends EntryBase implements GenericEntry {
   /**
@@ -140,6 +142,33 @@
     }
   }
 {% for t in types %}
+{% if t.TypeName == "Raw" %}
+  /**
+   * Sets the entry's value.
+   *
+   * @param value the value to set
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.length - start)
+   * @return False if the entry exists with a different type
+   */
+  @Override
+  public boolean setRaw(byte[] value, int start, int len, long time) {
+    return NetworkTablesJNI.setRaw(m_handle, time, value, start, len);
+  }
+
+  /**
+   * Sets the entry's value.
+   *
+   * @param value the value to set
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.capacity() - start)
+   * @return False if the entry exists with a different type
+   */
+  @Override
+  public boolean setRaw(ByteBuffer value, int start, int len, long time) {
+    return NetworkTablesJNI.setRaw(m_handle, time, value, start, len);
+  }
+{% else %}
   /**
    * Sets the entry's value.
    *
@@ -150,6 +179,7 @@
   public boolean set{{ t.TypeName }}({{ t.java.ValueType }} value, long time) {
     return NetworkTablesJNI.set{{ t.TypeName }}(m_handle, time, value);
   }
+{% endif -%}
 {% if t.java.WrapValueType %}
   /**
    * Sets the entry's value.
@@ -265,6 +295,33 @@
     }
   }
 {% for t in types %}
+{% if t.TypeName == "Raw" %}
+  /**
+   * Sets the entry's value if it does not exist.
+   *
+   * @param defaultValue the default value to set
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.length - start)
+   * @return False if the entry exists with a different type
+   */
+  @Override
+  public boolean setDefaultRaw(byte[] defaultValue, int start, int len) {
+    return NetworkTablesJNI.setDefaultRaw(m_handle, 0, defaultValue, start, len);
+  }
+
+  /**
+   * Sets the entry's value if it does not exist.
+   *
+   * @param defaultValue the default value to set
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.capacity() - start)
+   * @return False if the entry exists with a different type
+   */
+  @Override
+  public boolean setDefaultRaw(ByteBuffer defaultValue, int start, int len) {
+    return NetworkTablesJNI.setDefaultRaw(m_handle, 0, defaultValue, start, len);
+  }
+{% else %}
   /**
    * Sets the entry's value if it does not exist.
    *
@@ -275,6 +332,7 @@
   public boolean setDefault{{ t.TypeName }}({{ t.java.ValueType }} defaultValue) {
     return NetworkTablesJNI.setDefault{{ t.TypeName }}(m_handle, 0, defaultValue);
   }
+{% endif -%}
 {% if t.java.WrapValueType %}
   /**
    * Sets the entry's value if it does not exist.
diff --git a/third_party/allwpilib/ntcore/src/generate/java/GenericPublisher.java.jinja b/third_party/allwpilib/ntcore/src/generate/java/GenericPublisher.java.jinja
index d747f17..881aba6 100644
--- a/third_party/allwpilib/ntcore/src/generate/java/GenericPublisher.java.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/java/GenericPublisher.java.jinja
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.networktables;
 
+import java.nio.ByteBuffer;
 import java.util.function.Consumer;
 
 /** NetworkTables generic publisher. */
@@ -54,6 +55,16 @@
   default boolean set{{ t.TypeName }}({{ t.java.ValueType }} value) {
     return set{{ t.TypeName }}(value, 0);
   }
+{% if t.TypeName == "Raw" %}
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @return False if the topic already exists with a different type
+   */
+  default boolean setRaw(ByteBuffer value) {
+    return setRaw(value, 0);
+  }
 
   /**
    * Publish a new value.
@@ -62,7 +73,77 @@
    * @param time timestamp; 0 indicates current NT time should be used
    * @return False if the topic already exists with a different type
    */
+  default boolean setRaw(byte[] value, long time) {
+    return setRaw(value, 0, value.length, time);
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish; will send from value.position() to value.limit()
+   * @param time timestamp; 0 indicates current NT time should be used
+   * @return False if the topic already exists with a different type
+   */
+  default boolean setRaw(ByteBuffer value, long time) {
+    int pos = value.position();
+    return setRaw(value, pos, value.limit() - pos, time);
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.length - start)
+   * @return False if the topic already exists with a different type
+   */
+  default boolean setRaw(byte[] value, int start, int len) {
+    return setRaw(value, start, len, 0);
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.length - start)
+   * @param time timestamp; 0 indicates current NT time should be used
+   * @return False if the topic already exists with a different type
+   */
+  boolean setRaw(byte[] value, int start, int len, long time);
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.capacity() - start)
+   * @return False if the topic already exists with a different type
+   */
+  default boolean setRaw(ByteBuffer value, int start, int len) {
+    return setRaw(value, start, len, 0);
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.capacity() - start)
+   * @param time timestamp; 0 indicates current NT time should be used
+   * @return False if the topic already exists with a different type
+   */
+  boolean setRaw(ByteBuffer value, int start, int len, long time);
+{% else %}
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param time timestamp; 0 indicates current NT time should be used
+   * @return False if the topic already exists with a different type
+   */
   boolean set{{ t.TypeName }}({{ t.java.ValueType }} value, long time);
+{% endif -%}
 {% if t.java.WrapValueType %}
   /**
    * Publish a new value.
@@ -101,6 +182,49 @@
    */
   boolean setDefaultValue(Object defaultValue);
 {% for t in types %}
+{% if t.TypeName == "Raw" %}
+  /**
+   * Sets the entry's value if it does not exist.
+   *
+   * @param defaultValue the default value to set
+   * @return False if the entry exists with a different type
+   */
+  default boolean setDefaultRaw(byte[] defaultValue) {
+    return setDefaultRaw(defaultValue, 0, defaultValue.length);
+  }
+
+  /**
+   * Sets the entry's value if it does not exist.
+   *
+   * @param defaultValue the default value to set; will send from defaultValue.position() to
+   *                     defaultValue.limit()
+   * @return False if the entry exists with a different type
+   */
+  default boolean setDefaultRaw(ByteBuffer defaultValue) {
+    int pos = defaultValue.position();
+    return setDefaultRaw(defaultValue, pos, defaultValue.limit() - pos);
+  }
+
+  /**
+   * Sets the entry's value if it does not exist.
+   *
+   * @param defaultValue the default value to set
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.length - start)
+   * @return False if the entry exists with a different type
+   */
+  boolean setDefaultRaw(byte[] defaultValue, int start, int len);
+
+  /**
+   * Sets the entry's value if it does not exist.
+   *
+   * @param defaultValue the default value to set
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.capacity() - start)
+   * @return False if the entry exists with a different type
+   */
+  boolean setDefaultRaw(ByteBuffer defaultValue, int start, int len);
+{% else %}
   /**
    * Sets the entry's value if it does not exist.
    *
@@ -108,6 +232,7 @@
    * @return False if the entry exists with a different type
    */
   boolean setDefault{{ t.TypeName }}({{ t.java.ValueType }} defaultValue);
+{% endif -%}
 {% if t.java.WrapValueType %}
   boolean setDefault{{ t.TypeName }}({{ t.java.WrapValueType }} defaultValue);
 {% endif -%}
diff --git a/third_party/allwpilib/ntcore/src/generate/java/NetworkTableEntry.java.jinja b/third_party/allwpilib/ntcore/src/generate/java/NetworkTableEntry.java.jinja
index 783ec6f..7ee7d1f 100644
--- a/third_party/allwpilib/ntcore/src/generate/java/NetworkTableEntry.java.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/java/NetworkTableEntry.java.jinja
@@ -4,6 +4,8 @@
 
 package edu.wpi.first.networktables;
 
+import java.nio.ByteBuffer;
+
 /**
  * NetworkTables Entry.
  *
@@ -310,6 +312,42 @@
   public boolean setDefault{{ t.TypeName }}({{ t.java.ValueType }} defaultValue) {
     return NetworkTablesJNI.setDefault{{ t.TypeName }}(m_handle, 0, defaultValue);
   }
+{% if t.TypeName == "Raw" %}
+  /**
+   * Sets the entry's value if it does not exist.
+   *
+   * @param defaultValue the default value to set; will send from defaultValue.position() to
+   *                     defaultValue.capacity()
+   * @return False if the entry exists with a different type
+   */
+  public boolean setDefaultRaw(ByteBuffer defaultValue) {
+    return NetworkTablesJNI.setDefaultRaw(m_handle, 0, defaultValue);
+  }
+
+  /**
+   * Sets the entry's value if it does not exist.
+   *
+   * @param defaultValue the default value to set
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.length - start)
+   * @return False if the entry exists with a different type
+   */
+  public boolean setDefaultRaw(byte[] defaultValue, int start, int len) {
+    return NetworkTablesJNI.setDefaultRaw(m_handle, 0, defaultValue, start, len);
+  }
+
+  /**
+   * Sets the entry's value if it does not exist.
+   *
+   * @param defaultValue the default value to set
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.capacity() - start)
+   * @return False if the entry exists with a different type
+   */
+  public boolean setDefaultRaw(ByteBuffer defaultValue, int start, int len) {
+    return NetworkTablesJNI.setDefaultRaw(m_handle, 0, defaultValue, start, len);
+  }
+{% endif -%}
 {% if t.java.WrapValueType %}
   /**
    * Sets the entry's value if it does not exist.
@@ -427,6 +465,41 @@
   public boolean set{{ t.TypeName }}({{ t.java.ValueType }} value) {
     return NetworkTablesJNI.set{{ t.TypeName }}(m_handle, 0, value);
   }
+{% if t.TypeName == "Raw" %}
+  /**
+   * Sets the entry's value.
+   *
+   * @param value the value to set; will send from value.position() to value.capacity()
+   * @return False if the entry exists with a different type
+   */
+  public boolean setRaw(ByteBuffer value) {
+    return NetworkTablesJNI.setRaw(m_handle, 0, value);
+  }
+
+  /**
+   * Sets the entry's value.
+   *
+   * @param value the value to set
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.length - start)
+   * @return False if the entry exists with a different type
+   */
+  public boolean setRaw(byte[] value, int start, int len) {
+    return NetworkTablesJNI.setRaw(m_handle, 0, value, start, len);
+  }
+
+  /**
+   * Sets the entry's value.
+   *
+   * @param value the value to set
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.capacity() - start)
+   * @return False if the entry exists with a different type
+   */
+  public boolean setRaw(ByteBuffer value, int start, int len) {
+    return NetworkTablesJNI.setRaw(m_handle, 0, value, start, len);
+  }
+{% endif -%}
 {% if t.java.WrapValueType %}
   /**
    * Sets the entry's value.
diff --git a/third_party/allwpilib/ntcore/src/generate/java/NetworkTableInstance.java.jinja b/third_party/allwpilib/ntcore/src/generate/java/NetworkTableInstance.java.jinja
index ba5e33f..9df129d 100644
--- a/third_party/allwpilib/ntcore/src/generate/java/NetworkTableInstance.java.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/java/NetworkTableInstance.java.jinja
@@ -7,16 +7,22 @@
 import edu.wpi.first.util.WPIUtilJNI;
 import edu.wpi.first.util.concurrent.Event;
 import edu.wpi.first.util.datalog.DataLog;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.charset.StandardCharsets;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.OptionalLong;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Consumer;
+import us.hebi.quickbuf.ProtoMessage;
 
 /**
  * NetworkTables Instance.
@@ -86,6 +92,7 @@
   public synchronized void close() {
     if (m_owned && m_handle != 0) {
       m_listeners.close();
+      m_schemas.forEach((k, v) -> v.close());
       NetworkTablesJNI.destroyInstance(m_handle);
       m_handle = 0;
     }
@@ -176,15 +183,119 @@
       handle = topic.getHandle();
     }
 
-    topic = new {{ t.TypeName }}Topic(this, handle);
-    m_topics.put(name, topic);
+    {{ t.TypeName }}Topic wrapTopic = new {{ t.TypeName }}Topic(this, handle);
+    m_topics.put(name, wrapTopic);
 
     // also cache by handle
-    m_topicsByHandle.put(handle, topic);
+    m_topicsByHandle.put(handle, wrapTopic);
 
-    return ({{ t.TypeName }}Topic) topic;
+    return wrapTopic;
   }
 {% endfor %}
+
+  /**
+   * Get protobuf-encoded value topic.
+   *
+   * @param <T> value class (inferred from proto)
+   * @param <MessageType> protobuf message type (inferred from proto)
+   * @param name topic name
+   * @param proto protobuf serialization implementation
+   * @return ProtobufTopic
+   */
+  public <T, MessageType extends ProtoMessage<?>>
+      ProtobufTopic<T> getProtobufTopic(String name, Protobuf<T, MessageType> proto) {
+    Topic topic = m_topics.get(name);
+    if (topic instanceof ProtobufTopic<?>
+        && ((ProtobufTopic<?>) topic).getProto().equals(proto)) {
+      @SuppressWarnings("unchecked")
+      ProtobufTopic<T> wrapTopic = (ProtobufTopic<T>) topic;
+      return wrapTopic;
+    }
+
+    int handle;
+    if (topic == null) {
+      handle = NetworkTablesJNI.getTopic(m_handle, name);
+    } else {
+      handle = topic.getHandle();
+    }
+
+    ProtobufTopic<T> wrapTopic = ProtobufTopic.wrap(this, handle, proto);
+    m_topics.put(name, wrapTopic);
+
+    // also cache by handle
+    m_topicsByHandle.put(handle, wrapTopic);
+
+    return wrapTopic;
+  }
+
+  /**
+   * Get struct-encoded value topic.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param name topic name
+   * @param struct struct serialization implementation
+   * @return StructTopic
+   */
+  public <T>
+      StructTopic<T> getStructTopic(String name, Struct<T> struct) {
+    Topic topic = m_topics.get(name);
+    if (topic instanceof StructTopic<?>
+        && ((StructTopic<?>) topic).getStruct().equals(struct)) {
+      @SuppressWarnings("unchecked")
+      StructTopic<T> wrapTopic = (StructTopic<T>) topic;
+      return wrapTopic;
+    }
+
+    int handle;
+    if (topic == null) {
+      handle = NetworkTablesJNI.getTopic(m_handle, name);
+    } else {
+      handle = topic.getHandle();
+    }
+
+    StructTopic<T> wrapTopic = StructTopic.wrap(this, handle, struct);
+    m_topics.put(name, wrapTopic);
+
+    // also cache by handle
+    m_topicsByHandle.put(handle, wrapTopic);
+
+    return wrapTopic;
+  }
+
+  /**
+   * Get struct-encoded value array topic.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param name topic name
+   * @param struct struct serialization implementation
+   * @return StructArrayTopic
+   */
+  public <T>
+      StructArrayTopic<T> getStructArrayTopic(String name, Struct<T> struct) {
+    Topic topic = m_topics.get(name);
+    if (topic instanceof StructArrayTopic<?>
+        && ((StructArrayTopic<?>) topic).getStruct().equals(struct)) {
+      @SuppressWarnings("unchecked")
+      StructArrayTopic<T> wrapTopic = (StructArrayTopic<T>) topic;
+      return wrapTopic;
+    }
+
+    int handle;
+    if (topic == null) {
+      handle = NetworkTablesJNI.getTopic(m_handle, name);
+    } else {
+      handle = topic.getHandle();
+    }
+
+    StructArrayTopic<T> wrapTopic = StructArrayTopic.wrap(this, handle, struct);
+    m_topics.put(name, wrapTopic);
+
+    // also cache by handle
+    m_topicsByHandle.put(handle, wrapTopic);
+
+    return wrapTopic;
+  }
+
   private Topic[] topicHandlesToTopics(int[] handles) {
     Topic[] topics = new Topic[handles.length];
     for (int i = 0; i < handles.length; i++) {
@@ -553,7 +664,7 @@
     }
   }
 
-  private ListenerStorage m_listeners = new ListenerStorage(this);
+  private final ListenerStorage m_listeners = new ListenerStorage(this);
 
   /**
    * Remove a connection listener.
@@ -912,6 +1023,14 @@
   }
 
   /**
+   * Disconnects the client if it's running and connected. This will automatically start
+   * reconnection attempts to the current server list.
+   */
+  public void disconnect() {
+    NetworkTablesJNI.disconnect(m_handle);
+  }
+
+  /**
    * Starts requesting server address from Driver Station. This connects to the Driver Station
    * running on localhost to obtain the server IP address, and connects with the default port.
    */
@@ -1042,6 +1161,78 @@
     return m_listeners.addLogger(minLevel, maxLevel, func);
   }
 
+  /**
+   * Returns whether there is a data schema already registered with the given name that this
+   * instance has published. This does NOT perform a check as to whether the schema has already
+   * been published by another node on the network.
+   *
+   * @param name Name (the string passed as the data type for topics using this schema)
+   * @return True if schema already registered
+   */
+  public boolean hasSchema(String name) {
+    return m_schemas.containsKey("/.schema/" + name);
+  }
+
+  /**
+   * Registers a data schema. Data schemas provide information for how a certain data type string
+   * can be decoded. The type string of a data schema indicates the type of the schema itself (e.g.
+   * "protobuf" for protobuf schemas, "struct" for struct schemas, etc). In NetworkTables, schemas
+   * are published just like normal topics, with the name being generated from the provided name:
+   * "/.schema/name". Duplicate calls to this function with the same name are silently ignored.
+   *
+   * @param name Name (the string passed as the data type for topics using this schema)
+   * @param type Type of schema (e.g. "protobuf", "struct", etc)
+   * @param schema Schema data
+   */
+  public void addSchema(String name, String type, byte[] schema) {
+    m_schemas.computeIfAbsent("/.schema/" + name, k -> {
+      RawPublisher pub = getRawTopic(k).publishEx(type, "{\"retained\":true}");
+      pub.setDefault(schema);
+      return pub;
+    });
+  }
+
+  /**
+   * Registers a data schema. Data schemas provide information for how a certain data type string
+   * can be decoded. The type string of a data schema indicates the type of the schema itself (e.g.
+   * "protobuf" for protobuf schemas, "struct" for struct schemas, etc). In NetworkTables, schemas
+   * are published just like normal topics, with the name being generated from the provided name:
+   * "/.schema/name". Duplicate calls to this function with the same name are silently ignored.
+   *
+   * @param name Name (the string passed as the data type for topics using this schema)
+   * @param type Type of schema (e.g. "protobuf", "struct", etc)
+   * @param schema Schema data
+   */
+  public void addSchema(String name, String type, String schema) {
+    m_schemas.computeIfAbsent("/.schema/" + name, k -> {
+      RawPublisher pub = getRawTopic(k).publishEx(type, "{\"retained\":true}");
+      pub.setDefault(StandardCharsets.UTF_8.encode(schema));
+      return pub;
+    });
+  }
+
+  /**
+   * Registers a protobuf schema. Duplicate calls to this function with the same name are silently
+   * ignored.
+   *
+   * @param proto protobuf serialization object
+   */
+  public void addSchema(Protobuf<?, ?> proto) {
+    proto.forEachDescriptor(
+        this::hasSchema,
+        (typeString, schema) -> addSchema(typeString, "proto:FileDescriptorProto", schema));
+  }
+
+  /**
+   * Registers a struct schema. Duplicate calls to this function with the same name are silently
+   * ignored.
+   *
+   * @param struct struct serialization object
+   */
+  public void addSchema(Struct<?> struct) {
+    addSchemaImpl(struct, new HashSet<>());
+  }
+
   @Override
   public boolean equals(Object other) {
     if (other == this) {
@@ -1059,6 +1250,22 @@
     return m_handle;
   }
 
+  private void addSchemaImpl(Struct<?> struct, Set<String> seen) {
+    String typeString = struct.getTypeString();
+    if (hasSchema(typeString)) {
+      return;
+    }
+    if (!seen.add(typeString)) {
+      throw new UnsupportedOperationException(typeString + ": circular reference with " + seen);
+    }
+    addSchema(typeString, "structschema", struct.getSchema());
+    for (Struct<?> inner : struct.getNested()) {
+      addSchemaImpl(inner, seen);
+    }
+    seen.remove(typeString);
+  }
+
   private boolean m_owned;
   private int m_handle;
+  private final ConcurrentMap<String, RawPublisher> m_schemas = new ConcurrentHashMap<>();
 }
diff --git a/third_party/allwpilib/ntcore/src/generate/java/NetworkTablesJNI.java.jinja b/third_party/allwpilib/ntcore/src/generate/java/NetworkTablesJNI.java.jinja
index 2f119fe..6ff9c16 100644
--- a/third_party/allwpilib/ntcore/src/generate/java/NetworkTablesJNI.java.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/java/NetworkTablesJNI.java.jinja
@@ -7,6 +7,7 @@
 import edu.wpi.first.util.RuntimeLoader;
 import edu.wpi.first.util.datalog.DataLog;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.EnumSet;
 import java.util.OptionalLong;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -182,12 +183,77 @@
   public static native Timestamped{{ t.TypeName }}[] readQueue{{ t.TypeName }}(int subentry);
 
   public static native {{ t.java.ValueType }}[] readQueueValues{{ t.TypeName }}(int subentry);
+{% if t.TypeName == "Raw" %}
+  public static boolean setRaw(int entry, long time, byte[] value) {
+    return setRaw(entry, time, value, 0, value.length);
+  }
 
+  public static native boolean setRaw(int entry, long time, byte[] value, int start, int len);
+
+  public static boolean setRaw(int entry, long time, ByteBuffer value) {
+    int pos = value.position();
+    return setRaw(entry, time, value, pos, value.capacity() - pos);
+  }
+
+  public static boolean setRaw(int entry, long time, ByteBuffer value, int start, int len) {
+    if (value.isDirect()) {
+      if (start < 0) {
+        throw new IndexOutOfBoundsException("start must be >= 0");
+      }
+      if (len < 0) {
+        throw new IndexOutOfBoundsException("len must be >= 0");
+      }
+      if ((start + len) > value.capacity()) {
+        throw new IndexOutOfBoundsException("start + len must be smaller than buffer capacity");
+      }
+      return setRawBuffer(entry, time, value, start, len);
+    } else if (value.hasArray()) {
+      return setRaw(entry, time, value.array(), value.arrayOffset() + start, len);
+    } else {
+      throw new UnsupportedOperationException("ByteBuffer must be direct or have a backing array");
+    }
+  }
+
+  private static native boolean setRawBuffer(int entry, long time, ByteBuffer value, int start, int len);
+{% else %}
   public static native boolean set{{ t.TypeName }}(int entry, long time, {{ t.java.ValueType }} value);
-
+{% endif %}
   public static native {{ t.java.ValueType }} get{{ t.TypeName }}(int entry, {{ t.java.ValueType }} defaultValue);
+{% if t.TypeName == "Raw" %}
+  public static boolean setDefaultRaw(int entry, long time, byte[] defaultValue) {
+    return setDefaultRaw(entry, time, defaultValue, 0, defaultValue.length);
+  }
 
+  public static native boolean setDefaultRaw(int entry, long time, byte[] defaultValue, int start, int len);
+
+  public static boolean setDefaultRaw(int entry, long time, ByteBuffer defaultValue) {
+    int pos = defaultValue.position();
+    return setDefaultRaw(entry, time, defaultValue, pos, defaultValue.limit() - pos);
+  }
+
+  public static boolean setDefaultRaw(int entry, long time, ByteBuffer defaultValue, int start, int len) {
+    if (defaultValue.isDirect()) {
+      if (start < 0) {
+        throw new IndexOutOfBoundsException("start must be >= 0");
+      }
+      if (len < 0) {
+        throw new IndexOutOfBoundsException("len must be >= 0");
+      }
+      if ((start + len) > defaultValue.capacity()) {
+        throw new IndexOutOfBoundsException("start + len must be smaller than buffer capacity");
+      }
+      return setDefaultRawBuffer(entry, time, defaultValue, start, len);
+    } else if (defaultValue.hasArray()) {
+      return setDefaultRaw(entry, time, defaultValue.array(), defaultValue.arrayOffset() + start, len);
+    } else {
+      throw new UnsupportedOperationException("ByteBuffer must be direct or have a backing array");
+    }
+  }
+
+  private static native boolean setDefaultRawBuffer(int entry, long time, ByteBuffer defaultValue, int start, int len);
+{% else %}
   public static native boolean setDefault{{ t.TypeName }}(int entry, long time, {{ t.java.ValueType }} defaultValue);
+{% endif %}
 {% endfor %}
   public static native NetworkTableValue[] readQueueValue(int subentry);
 
@@ -251,6 +317,8 @@
 
   public static native void setServerTeam(int inst, int team, int port);
 
+  public static native void disconnect(int inst);
+
   public static native void startDSClient(int inst, int port);
 
   public static native void stopDSClient(int inst);
diff --git a/third_party/allwpilib/ntcore/src/generate/java/Publisher.java.jinja b/third_party/allwpilib/ntcore/src/generate/java/Publisher.java.jinja
index 19ead36..a403d91 100644
--- a/third_party/allwpilib/ntcore/src/generate/java/Publisher.java.jinja
+++ b/third_party/allwpilib/ntcore/src/generate/java/Publisher.java.jinja
@@ -4,6 +4,9 @@
 
 package edu.wpi.first.networktables;
 
+{% if TypeName == "Raw" %}
+import java.nio.ByteBuffer;
+{% endif -%}
 import {{ java.ConsumerFunctionPackage|default('java.util.function') }}.{{ java.FunctionTypePrefix }}Consumer;
 
 /** NetworkTables {{ TypeName }} publisher. */
@@ -25,6 +28,124 @@
     set(value, 0);
   }
 
+{% if TypeName == "Raw" %}
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  default void set(byte[] value, long time) {
+    set(value, 0, value.length, time);
+  }
+
+  /**
+   * Publish a new value using current NT time.
+   *
+   * @param value value to publish
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.length - start)
+   */
+  default void set(byte[] value, int start, int len) {
+    set(value, start, len, 0);
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.length - start)
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  void set(byte[] value, int start, int len, long time);
+
+  /**
+   * Publish a new value using current NT time.
+   *
+   * @param value value to publish; will send from value.position() to value.limit()
+   */
+  default void set(ByteBuffer value) {
+    set(value, 0);
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish; will send from value.position() to value.limit()
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  default void set(ByteBuffer value, long time) {
+    int pos = value.position();
+    set(value, pos, value.limit() - pos, time);
+  }
+
+  /**
+   * Publish a new value using current NT time.
+   *
+   * @param value value to publish
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.capacity() - start)
+   */
+  default void set(ByteBuffer value, int start, int len) {
+    set(value, start, len, 0);
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.capacity() - start)
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  void set(ByteBuffer value, int start, int len, long time);
+
+  /**
+   * Publish a default value.
+   * On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value
+   */
+  default void setDefault(byte[] value) {
+    setDefault(value, 0, value.length);
+  }
+
+  /**
+   * Publish a default value.
+   * On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.length - start)
+   */
+  void setDefault(byte[] value, int start, int len);
+
+  /**
+   * Publish a default value.
+   * On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value; will send from value.position() to value.limit()
+   */
+  default void setDefault(ByteBuffer value) {
+    int pos = value.position();
+    setDefault(value, pos, value.limit() - pos);
+  }
+
+  /**
+   * Publish a default value.
+   * On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to value.capacity() - start)
+   */
+  void setDefault(ByteBuffer value, int start, int len);
+{% else %}
   /**
    * Publish a new value.
    *
@@ -41,7 +162,7 @@
    * @param value value
    */
   void setDefault({{ java.ValueType }} value);
-
+{% endif %}
   @Override
   default void accept({{ java.ValueType }} value) {
     set(value);
diff --git a/third_party/allwpilib/ntcore/src/generate/types.json b/third_party/allwpilib/ntcore/src/generate/types.json
index 31a71b3..3f9f252 100644
--- a/third_party/allwpilib/ntcore/src/generate/types.json
+++ b/third_party/allwpilib/ntcore/src/generate/types.json
@@ -1,366 +1,377 @@
 [
-    {
-        "TypeName": "Boolean",
-        "TypeString": "\"boolean\"",
-        "c": {
-            "ValueType": "NT_Bool",
-            "ParamType": "NT_Bool"
-        },
-        "cpp": {
-            "ValueType": "bool",
-            "ParamType": "bool",
-            "TYPE_NAME": "BOOLEAN"
-        },
-        "java": {
-            "ValueType": "boolean",
-            "EmptyValue": "false",
-            "ConsumerFunctionPackage": "edu.wpi.first.util.function",
-            "FunctionTypePrefix": "Boolean",
-            "ToWrapObject": "Boolean.valueOf",
-            "FromStorageBegin": "(Boolean) "
-        },
-        "jni": {
-            "jtype": "jboolean",
-            "jtypestr": "Z",
-            "JavaObject": false,
-            "FromJavaBegin": "",
-            "FromJavaEnd": " != JNI_FALSE",
-            "ToJavaBegin": "static_cast<jboolean>(",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJBooleanArray"
-        }
+  {
+    "TypeName": "Boolean",
+    "TypeString": "\"boolean\"",
+    "c": {
+      "ValueType": "NT_Bool",
+      "ParamType": "NT_Bool"
     },
-    {
-        "TypeName": "Integer",
-        "TypeString": "\"int\"",
-        "c": {
-            "ValueType": "int64_t",
-            "ParamType": "int64_t"
-        },
-        "cpp": {
-            "ValueType": "int64_t",
-            "ParamType": "int64_t",
-            "TYPE_NAME": "INTEGER"
-        },
-        "java": {
-            "ValueType": "long",
-            "EmptyValue": "0",
-            "FunctionTypePrefix": "Long",
-            "ToWrapObject": "Long.valueOf",
-            "FromStorageBegin": "((Number) ",
-            "FromStorageEnd": ").longValue()"
-        },
-        "jni": {
-            "jtype": "jlong",
-            "jtypestr": "J",
-            "JavaObject": false,
-            "FromJavaBegin": "",
-            "FromJavaEnd": "",
-            "ToJavaBegin": "static_cast<jlong>(",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJLongArray"
-        }
+    "cpp": {
+      "ValueType": "bool",
+      "ParamType": "bool",
+      "TemplateType": "bool",
+      "TYPE_NAME": "BOOLEAN"
     },
-    {
-        "TypeName": "Float",
-        "TypeString": "\"float\"",
-        "c": {
-            "ValueType": "float",
-            "ParamType": "float"
-        },
-        "cpp": {
-            "ValueType": "float",
-            "ParamType": "float",
-            "TYPE_NAME": "FLOAT"
-        },
-        "java": {
-            "ValueType": "float",
-            "EmptyValue": "0",
-            "ConsumerFunctionPackage": "edu.wpi.first.util.function",
-            "SupplierFunctionPackage": "edu.wpi.first.util.function",
-            "FunctionTypePrefix": "Float",
-            "ToWrapObject": "Float.valueOf",
-            "FromStorageBegin": "((Number) ",
-            "FromStorageEnd": ").floatValue()"
-        },
-        "jni": {
-            "jtype": "jfloat",
-            "jtypestr": "F",
-            "JavaObject": false,
-            "FromJavaBegin": "",
-            "FromJavaEnd": "",
-            "ToJavaBegin": "static_cast<jfloat>(",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJFloatArray"
-        }
+    "java": {
+      "ValueType": "boolean",
+      "EmptyValue": "false",
+      "ConsumerFunctionPackage": "edu.wpi.first.util.function",
+      "FunctionTypePrefix": "Boolean",
+      "ToWrapObject": "Boolean.valueOf",
+      "FromStorageBegin": "(Boolean) "
     },
-    {
-        "TypeName": "Double",
-        "TypeString": "\"double\"",
-        "c": {
-            "ValueType": "double",
-            "ParamType": "double"
-        },
-        "cpp": {
-            "ValueType": "double",
-            "ParamType": "double",
-            "TYPE_NAME": "DOUBLE"
-        },
-        "java": {
-            "ValueType": "double",
-            "EmptyValue": "0",
-            "FunctionTypePrefix": "Double",
-            "ToWrapObject": "Double.valueOf",
-            "FromStorageBegin": "((Number) ",
-            "FromStorageEnd": ").doubleValue()"
-        },
-        "jni": {
-            "jtype": "jdouble",
-            "jtypestr": "D",
-            "JavaObject": false,
-            "FromJavaBegin": "",
-            "FromJavaEnd": "",
-            "ToJavaBegin": "static_cast<jdouble>(",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJDoubleArray"
-        }
-    },
-    {
-        "TypeName": "String",
-        "TypeString": "\"string\"",
-        "c": {
-            "ValueType": "char*",
-            "ParamType": "const char*",
-            "IsArray": true
-        },
-        "cpp": {
-            "ValueType": "std::string",
-            "ParamType": "std::string_view",
-            "TYPE_NAME": "STRING",
-            "INCLUDES": "#include <string>\n#include <string_view>\n#include <utility>",
-            "SmallRetType": "std::string_view",
-            "SmallElemType": "char"
-        },
-        "java": {
-            "ValueType": "String",
-            "EmptyValue": "\"\"",
-            "FunctionTypeSuffix": "<String>",
-            "FromStorageBegin": "(String) "
-        },
-        "jni": {
-            "jtype": "jstring",
-            "jtypestr": "Ljava/lang/String;",
-            "JavaObject": true,
-            "FromJavaBegin": "JStringRef{env, ",
-            "FromJavaEnd": "}",
-            "ToJavaBegin": "MakeJString(env, ",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJStringArray"
-        }
-    },
-    {
-        "TypeName": "Raw",
-        "c": {
-            "ValueType": "uint8_t*",
-            "ParamType": "const uint8_t*",
-            "IsArray": true
-        },
-        "cpp": {
-            "ValueType": "std::vector<uint8_t>",
-            "ParamType": "std::span<const uint8_t>",
-            "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
-            "TYPE_NAME": "RAW",
-            "INCLUDES": "#include <utility>",
-            "SmallRetType": "std::span<uint8_t>",
-            "SmallElemType": "uint8_t"
-        },
-        "java": {
-            "ValueType": "byte[]",
-            "EmptyValue": "new byte[] {}",
-            "FunctionTypeSuffix": "<byte[]>",
-            "FromStorageBegin": "(byte[]) "
-        },
-        "jni": {
-            "jtype": "jbyteArray",
-            "jtypestr": "[B",
-            "JavaObject": true,
-            "FromJavaBegin": "CriticalJByteArrayRef{env, ",
-            "FromJavaEnd": "}.uarray()",
-            "ToJavaBegin": "MakeJByteArray(env, ",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJObjectArray"
-        }
-    },
-    {
-        "TypeName": "BooleanArray",
-        "TypeString": "\"boolean[]\"",
-        "c": {
-            "ValueType": "NT_Bool*",
-            "ParamType": "const NT_Bool*",
-            "IsArray": true
-        },
-        "cpp": {
-            "ValueType": "std::vector<int>",
-            "ParamType": "std::span<const int>",
-            "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
-            "TYPE_NAME": "BOOLEAN_ARRAY",
-            "INCLUDES": "#include <utility>",
-            "SmallRetType": "std::span<int>",
-            "SmallElemType": "int"
-        },
-        "java": {
-            "ValueType": "boolean[]",
-            "WrapValueType": "Boolean[]",
-            "EmptyValue": "new boolean[] {}",
-            "FunctionTypeSuffix": "<boolean[]>",
-            "FromStorageBegin": "(boolean[]) "
-        },
-        "jni": {
-            "jtype": "jbooleanArray",
-            "jtypestr": "[Z",
-            "JavaObject": true,
-            "FromJavaBegin": "FromJavaBooleanArray(env, ",
-            "FromJavaEnd": ")",
-            "ToJavaBegin": "MakeJBooleanArray(env, ",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJObjectArray"
-        }
-    },
-    {
-        "TypeName": "IntegerArray",
-        "TypeString": "\"int[]\"",
-        "c": {
-            "ValueType": "int64_t*",
-            "ParamType": "const int64_t*",
-            "IsArray": true
-        },
-        "cpp": {
-            "ValueType": "std::vector<int64_t>",
-            "ParamType": "std::span<const int64_t>",
-            "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
-            "TYPE_NAME": "INTEGER_ARRAY",
-            "INCLUDES": "#include <utility>",
-            "SmallRetType": "std::span<int64_t>",
-            "SmallElemType": "int64_t"
-        },
-        "java": {
-            "ValueType": "long[]",
-            "WrapValueType": "Long[]",
-            "EmptyValue": "new long[] {}",
-            "FunctionTypeSuffix": "<long[]>",
-            "FromStorageBegin": "(long[]) "
-        },
-        "jni": {
-            "jtype": "jlongArray",
-            "jtypestr": "[J",
-            "JavaObject": true,
-            "FromJavaBegin": "CriticalJLongArrayRef{env, ",
-            "FromJavaEnd": "}",
-            "ToJavaBegin": "MakeJLongArray(env, ",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJObjectArray"
-        }
-    },
-    {
-        "TypeName": "FloatArray",
-        "TypeString": "\"float[]\"",
-        "c": {
-            "ValueType": "float*",
-            "ParamType": "const float*",
-            "IsArray": true
-        },
-        "cpp": {
-            "ValueType": "std::vector<float>",
-            "ParamType": "std::span<const float>",
-            "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
-            "TYPE_NAME": "FLOAT_ARRAY",
-            "INCLUDES": "#include <utility>",
-            "SmallRetType": "std::span<float>",
-            "SmallElemType": "float"
-        },
-        "java": {
-            "ValueType": "float[]",
-            "WrapValueType": "Float[]",
-            "EmptyValue": "new float[] {}",
-            "FunctionTypeSuffix": "<float[]>",
-            "FromStorageBegin": "(float[]) "
-        },
-        "jni": {
-            "jtype": "jfloatArray",
-            "jtypestr": "[F",
-            "JavaObject": true,
-            "FromJavaBegin": "CriticalJFloatArrayRef{env, ",
-            "FromJavaEnd": "}",
-            "ToJavaBegin": "MakeJFloatArray(env, ",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJObjectArray"
-        }
-    },
-    {
-        "TypeName": "DoubleArray",
-        "TypeString": "\"double[]\"",
-        "c": {
-            "ValueType": "double*",
-            "ParamType": "const double*",
-            "IsArray": true
-        },
-        "cpp": {
-            "ValueType": "std::vector<double>",
-            "ParamType": "std::span<const double>",
-            "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
-            "TYPE_NAME": "DOUBLE_ARRAY",
-            "INCLUDES": "#include <utility>",
-            "SmallRetType": "std::span<double>",
-            "SmallElemType": "double"
-        },
-        "java": {
-            "ValueType": "double[]",
-            "WrapValueType": "Double[]",
-            "EmptyValue": "new double[] {}",
-            "FunctionTypeSuffix": "<double[]>",
-            "FromStorageBegin": "(double[]) "
-        },
-        "jni": {
-            "jtype": "jdoubleArray",
-            "jtypestr": "[D",
-            "JavaObject": true,
-            "FromJavaBegin": "CriticalJDoubleArrayRef{env, ",
-            "FromJavaEnd": "}",
-            "ToJavaBegin": "MakeJDoubleArray(env, ",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJObjectArray"
-        }
-    },
-    {
-        "TypeName": "StringArray",
-        "TypeString": "\"string[]\"",
-        "c": {
-            "ValueType": "struct NT_String*",
-            "ParamType": "const struct NT_String*",
-            "IsArray": true
-        },
-        "cpp": {
-            "ValueType": "std::vector<std::string>",
-            "ParamType": "std::span<const std::string>",
-            "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
-            "TYPE_NAME": "STRING_ARRAY",
-            "INCLUDES": "#include <utility>"
-        },
-        "java": {
-            "ValueType": "String[]",
-            "EmptyValue": "new String[] {}",
-            "FunctionTypeSuffix": "<String[]>",
-            "FromStorageBegin": "(String[]) "
-        },
-        "jni": {
-            "jtype": "jobjectArray",
-            "jtypestr": "[Ljava/lang/Object;",
-            "JavaObject": true,
-            "FromJavaBegin": "FromJavaStringArray(env, ",
-            "FromJavaEnd": ")",
-            "ToJavaBegin": "MakeJStringArray(env, ",
-            "ToJavaEnd": ")",
-            "ToJavaArray": "MakeJObjectArray"
-        }
+    "jni": {
+      "jtype": "jboolean",
+      "jtypestr": "Z",
+      "JavaObject": false,
+      "FromJavaBegin": "",
+      "FromJavaEnd": " != JNI_FALSE",
+      "ToJavaBegin": "static_cast<jboolean>(",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJBooleanArray"
     }
+  },
+  {
+    "TypeName": "Integer",
+    "TypeString": "\"int\"",
+    "c": {
+      "ValueType": "int64_t",
+      "ParamType": "int64_t"
+    },
+    "cpp": {
+      "ValueType": "int64_t",
+      "ParamType": "int64_t",
+      "TemplateType": "int64_t",
+      "TYPE_NAME": "INTEGER"
+    },
+    "java": {
+      "ValueType": "long",
+      "EmptyValue": "0",
+      "FunctionTypePrefix": "Long",
+      "ToWrapObject": "Long.valueOf",
+      "FromStorageBegin": "((Number) ",
+      "FromStorageEnd": ").longValue()"
+    },
+    "jni": {
+      "jtype": "jlong",
+      "jtypestr": "J",
+      "JavaObject": false,
+      "FromJavaBegin": "",
+      "FromJavaEnd": "",
+      "ToJavaBegin": "static_cast<jlong>(",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJLongArray"
+    }
+  },
+  {
+    "TypeName": "Float",
+    "TypeString": "\"float\"",
+    "c": {
+      "ValueType": "float",
+      "ParamType": "float"
+    },
+    "cpp": {
+      "ValueType": "float",
+      "ParamType": "float",
+      "TemplateType": "float",
+      "TYPE_NAME": "FLOAT"
+    },
+    "java": {
+      "ValueType": "float",
+      "EmptyValue": "0",
+      "ConsumerFunctionPackage": "edu.wpi.first.util.function",
+      "SupplierFunctionPackage": "edu.wpi.first.util.function",
+      "FunctionTypePrefix": "Float",
+      "ToWrapObject": "Float.valueOf",
+      "FromStorageBegin": "((Number) ",
+      "FromStorageEnd": ").floatValue()"
+    },
+    "jni": {
+      "jtype": "jfloat",
+      "jtypestr": "F",
+      "JavaObject": false,
+      "FromJavaBegin": "",
+      "FromJavaEnd": "",
+      "ToJavaBegin": "static_cast<jfloat>(",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJFloatArray"
+    }
+  },
+  {
+    "TypeName": "Double",
+    "TypeString": "\"double\"",
+    "c": {
+      "ValueType": "double",
+      "ParamType": "double"
+    },
+    "cpp": {
+      "ValueType": "double",
+      "ParamType": "double",
+      "TemplateType": "double",
+      "TYPE_NAME": "DOUBLE"
+    },
+    "java": {
+      "ValueType": "double",
+      "EmptyValue": "0",
+      "FunctionTypePrefix": "Double",
+      "ToWrapObject": "Double.valueOf",
+      "FromStorageBegin": "((Number) ",
+      "FromStorageEnd": ").doubleValue()"
+    },
+    "jni": {
+      "jtype": "jdouble",
+      "jtypestr": "D",
+      "JavaObject": false,
+      "FromJavaBegin": "",
+      "FromJavaEnd": "",
+      "ToJavaBegin": "static_cast<jdouble>(",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJDoubleArray"
+    }
+  },
+  {
+    "TypeName": "String",
+    "TypeString": "\"string\"",
+    "c": {
+      "ValueType": "char*",
+      "ParamType": "const char*",
+      "IsArray": true
+    },
+    "cpp": {
+      "ValueType": "std::string",
+      "ParamType": "std::string_view",
+      "TemplateType": "std::string",
+      "TYPE_NAME": "STRING",
+      "INCLUDES": "#include <string>\n#include <string_view>\n#include <utility>",
+      "SmallRetType": "std::string_view",
+      "SmallElemType": "char"
+    },
+    "java": {
+      "ValueType": "String",
+      "EmptyValue": "\"\"",
+      "FunctionTypeSuffix": "<String>",
+      "FromStorageBegin": "(String) "
+    },
+    "jni": {
+      "jtype": "jstring",
+      "jtypestr": "Ljava/lang/String;",
+      "JavaObject": true,
+      "FromJavaBegin": "JStringRef{env, ",
+      "FromJavaEnd": "}",
+      "ToJavaBegin": "MakeJString(env, ",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJStringArray"
+    }
+  },
+  {
+    "TypeName": "Raw",
+    "c": {
+      "ValueType": "uint8_t*",
+      "ParamType": "const uint8_t*",
+      "IsArray": true
+    },
+    "cpp": {
+      "ValueType": "std::vector<uint8_t>",
+      "ParamType": "std::span<const uint8_t>",
+      "TemplateType": "uint8_t[]",
+      "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
+      "TYPE_NAME": "RAW",
+      "INCLUDES": "#include <utility>",
+      "SmallRetType": "std::span<uint8_t>",
+      "SmallElemType": "uint8_t"
+    },
+    "java": {
+      "ValueType": "byte[]",
+      "EmptyValue": "new byte[] {}",
+      "FunctionTypeSuffix": "<byte[]>",
+      "FromStorageBegin": "(byte[]) "
+    },
+    "jni": {
+      "jtype": "jbyteArray",
+      "jtypestr": "[B",
+      "JavaObject": true,
+      "FromJavaBegin": "CriticalJSpan<const jbyte>{env, ",
+      "FromJavaEnd": "}.uarray()",
+      "ToJavaBegin": "MakeJByteArray(env, ",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJObjectArray"
+    }
+  },
+  {
+    "TypeName": "BooleanArray",
+    "TypeString": "\"boolean[]\"",
+    "c": {
+      "ValueType": "NT_Bool*",
+      "ParamType": "const NT_Bool*",
+      "IsArray": true
+    },
+    "cpp": {
+      "ValueType": "std::vector<int>",
+      "ParamType": "std::span<const int>",
+      "TemplateType": "bool[]",
+      "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
+      "TYPE_NAME": "BOOLEAN_ARRAY",
+      "INCLUDES": "#include <utility>",
+      "SmallRetType": "std::span<int>",
+      "SmallElemType": "int"
+    },
+    "java": {
+      "ValueType": "boolean[]",
+      "WrapValueType": "Boolean[]",
+      "EmptyValue": "new boolean[] {}",
+      "FunctionTypeSuffix": "<boolean[]>",
+      "FromStorageBegin": "(boolean[]) "
+    },
+    "jni": {
+      "jtype": "jbooleanArray",
+      "jtypestr": "[Z",
+      "JavaObject": true,
+      "FromJavaBegin": "FromJavaBooleanArray(env, ",
+      "FromJavaEnd": ")",
+      "ToJavaBegin": "MakeJBooleanArray(env, ",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJObjectArray"
+    }
+  },
+  {
+    "TypeName": "IntegerArray",
+    "TypeString": "\"int[]\"",
+    "c": {
+      "ValueType": "int64_t*",
+      "ParamType": "const int64_t*",
+      "IsArray": true
+    },
+    "cpp": {
+      "ValueType": "std::vector<int64_t>",
+      "ParamType": "std::span<const int64_t>",
+      "TemplateType": "int64_t[]",
+      "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
+      "TYPE_NAME": "INTEGER_ARRAY",
+      "INCLUDES": "#include <utility>",
+      "SmallRetType": "std::span<int64_t>",
+      "SmallElemType": "int64_t"
+    },
+    "java": {
+      "ValueType": "long[]",
+      "WrapValueType": "Long[]",
+      "EmptyValue": "new long[] {}",
+      "FunctionTypeSuffix": "<long[]>",
+      "FromStorageBegin": "(long[]) "
+    },
+    "jni": {
+      "jtype": "jlongArray",
+      "jtypestr": "[J",
+      "JavaObject": true,
+      "FromJavaBegin": "CriticalJSpan<const jlong>{env, ",
+      "FromJavaEnd": "}",
+      "ToJavaBegin": "MakeJLongArray(env, ",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJObjectArray"
+    }
+  },
+  {
+    "TypeName": "FloatArray",
+    "TypeString": "\"float[]\"",
+    "c": {
+      "ValueType": "float*",
+      "ParamType": "const float*",
+      "IsArray": true
+    },
+    "cpp": {
+      "ValueType": "std::vector<float>",
+      "ParamType": "std::span<const float>",
+      "TemplateType": "float[]",
+      "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
+      "TYPE_NAME": "FLOAT_ARRAY",
+      "INCLUDES": "#include <utility>",
+      "SmallRetType": "std::span<float>",
+      "SmallElemType": "float"
+    },
+    "java": {
+      "ValueType": "float[]",
+      "WrapValueType": "Float[]",
+      "EmptyValue": "new float[] {}",
+      "FunctionTypeSuffix": "<float[]>",
+      "FromStorageBegin": "(float[]) "
+    },
+    "jni": {
+      "jtype": "jfloatArray",
+      "jtypestr": "[F",
+      "JavaObject": true,
+      "FromJavaBegin": "CriticalJSpan<const jfloat>{env, ",
+      "FromJavaEnd": "}",
+      "ToJavaBegin": "MakeJFloatArray(env, ",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJObjectArray"
+    }
+  },
+  {
+    "TypeName": "DoubleArray",
+    "TypeString": "\"double[]\"",
+    "c": {
+      "ValueType": "double*",
+      "ParamType": "const double*",
+      "IsArray": true
+    },
+    "cpp": {
+      "ValueType": "std::vector<double>",
+      "ParamType": "std::span<const double>",
+      "TemplateType": "double[]",
+      "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
+      "TYPE_NAME": "DOUBLE_ARRAY",
+      "INCLUDES": "#include <utility>",
+      "SmallRetType": "std::span<double>",
+      "SmallElemType": "double"
+    },
+    "java": {
+      "ValueType": "double[]",
+      "WrapValueType": "Double[]",
+      "EmptyValue": "new double[] {}",
+      "FunctionTypeSuffix": "<double[]>",
+      "FromStorageBegin": "(double[]) "
+    },
+    "jni": {
+      "jtype": "jdoubleArray",
+      "jtypestr": "[D",
+      "JavaObject": true,
+      "FromJavaBegin": "CriticalJSpan<const jdouble>{env, ",
+      "FromJavaEnd": "}",
+      "ToJavaBegin": "MakeJDoubleArray(env, ",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJObjectArray"
+    }
+  },
+  {
+    "TypeName": "StringArray",
+    "TypeString": "\"string[]\"",
+    "c": {
+      "ValueType": "struct NT_String*",
+      "ParamType": "const struct NT_String*",
+      "IsArray": true
+    },
+    "cpp": {
+      "ValueType": "std::vector<std::string>",
+      "ParamType": "std::span<const std::string>",
+      "TemplateType": "std::string[]",
+      "DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
+      "TYPE_NAME": "STRING_ARRAY",
+      "INCLUDES": "#include <utility>"
+    },
+    "java": {
+      "ValueType": "String[]",
+      "EmptyValue": "new String[] {}",
+      "FunctionTypeSuffix": "<String[]>",
+      "FromStorageBegin": "(String[]) "
+    },
+    "jni": {
+      "jtype": "jobjectArray",
+      "jtypestr": "[Ljava/lang/Object;",
+      "JavaObject": true,
+      "FromJavaBegin": "FromJavaStringArray(env, ",
+      "FromJavaEnd": ")",
+      "ToJavaBegin": "MakeJStringArray(env, ",
+      "ToJavaEnd": ")",
+      "ToJavaArray": "MakeJObjectArray"
+    }
+  }
 ]
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTable.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTable.java
index cc38239..3c65938 100644
--- a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTable.java
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTable.java
@@ -4,6 +4,8 @@
 
 package edu.wpi.first.networktables;
 
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.HashSet;
@@ -13,8 +15,10 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.Consumer;
+import us.hebi.quickbuf.ProtoMessage;
 
 /** A network table that knows its subtable path. */
+@SuppressWarnings("PMD.CouplingBetweenObjects")
 public final class NetworkTable {
   /** The path separator for sub-tables and keys. */
   public static final char PATH_SEPARATOR = '/';
@@ -250,6 +254,44 @@
     return m_inst.getStringArrayTopic(m_pathWithSep + name);
   }
 
+  /**
+   * Get protobuf-encoded value topic.
+   *
+   * @param <T> value class (inferred from proto)
+   * @param <MessageType> protobuf message type (inferred from proto)
+   * @param name topic name
+   * @param proto protobuf serialization implementation
+   * @return ProtobufTopic
+   */
+  public <T, MessageType extends ProtoMessage<?>> ProtobufTopic<T> getProtobufTopic(
+      String name, Protobuf<T, MessageType> proto) {
+    return m_inst.getProtobufTopic(m_pathWithSep + name, proto);
+  }
+
+  /**
+   * Get struct-encoded value topic.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param name topic name
+   * @param struct struct serialization implementation
+   * @return StructTopic
+   */
+  public <T> StructTopic<T> getStructTopic(String name, Struct<T> struct) {
+    return m_inst.getStructTopic(m_pathWithSep + name, struct);
+  }
+
+  /**
+   * Get struct-encoded value array topic.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param name topic name
+   * @param struct struct serialization implementation
+   * @return StructTopic
+   */
+  public <T> StructArrayTopic<T> getStructArrayTopic(String name, Struct<T> struct) {
+    return m_inst.getStructArrayTopic(m_pathWithSep + name, struct);
+  }
+
   private final ConcurrentMap<String, NetworkTableEntry> m_entries = new ConcurrentHashMap<>();
 
   /**
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableListener.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableListener.java
index 2568738..c34cd58 100644
--- a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableListener.java
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableListener.java
@@ -113,7 +113,7 @@
    * Create a time synchronization listener.
    *
    * @param inst instance
-   * @param immediateNotify notify listener of current time synchonization value
+   * @param immediateNotify notify listener of current time synchronization value
    * @param listener listener function
    * @return Listener
    */
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufEntry.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufEntry.java
new file mode 100644
index 0000000..3f82c7a
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufEntry.java
@@ -0,0 +1,17 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+/**
+ * NetworkTables protobuf-encoded value entry.
+ *
+ * <p>Unlike NetworkTableEntry, the entry goes away when close() is called.
+ *
+ * @param <T> value class
+ */
+public interface ProtobufEntry<T> extends ProtobufSubscriber<T>, ProtobufPublisher<T> {
+  /** Stops publishing the entry if it's published. */
+  void unpublish();
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufEntryImpl.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufEntryImpl.java
new file mode 100644
index 0000000..b4359ea
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufEntryImpl.java
@@ -0,0 +1,209 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import edu.wpi.first.util.protobuf.ProtobufBuffer;
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+
+/**
+ * NetworkTables protobuf-encoded value implementation.
+ *
+ * @param <T> value class
+ */
+final class ProtobufEntryImpl<T> extends EntryBase implements ProtobufEntry<T> {
+  /**
+   * Constructor.
+   *
+   * @param topic Topic
+   * @param handle Native handle
+   * @param defaultValue Default value for get()
+   */
+  ProtobufEntryImpl(
+      ProtobufTopic<T> topic,
+      ProtobufBuffer<T, ?> buf,
+      int handle,
+      T defaultValue,
+      boolean schemaPublished) {
+    super(handle);
+    m_topic = topic;
+    m_defaultValue = defaultValue;
+    m_buf = buf;
+    m_schemaPublished = schemaPublished;
+  }
+
+  @Override
+  public ProtobufTopic<T> getTopic() {
+    return m_topic;
+  }
+
+  @Override
+  public T get() {
+    return fromRaw(NetworkTablesJNI.getRaw(m_handle, m_emptyRaw), m_defaultValue);
+  }
+
+  @Override
+  public T get(T defaultValue) {
+    return fromRaw(NetworkTablesJNI.getRaw(m_handle, m_emptyRaw), defaultValue);
+  }
+
+  @Override
+  public boolean getInto(T out) {
+    byte[] raw = NetworkTablesJNI.getRaw(m_handle, m_emptyRaw);
+    if (raw.length == 0) {
+      return false;
+    }
+    try {
+      synchronized (m_buf) {
+        m_buf.readInto(out, raw);
+        return true;
+      }
+    } catch (IOException e) {
+      // ignored
+    }
+    return false;
+  }
+
+  @Override
+  public TimestampedObject<T> getAtomic() {
+    return fromRaw(NetworkTablesJNI.getAtomicRaw(m_handle, m_emptyRaw), m_defaultValue);
+  }
+
+  @Override
+  public TimestampedObject<T> getAtomic(T defaultValue) {
+    return fromRaw(NetworkTablesJNI.getAtomicRaw(m_handle, m_emptyRaw), defaultValue);
+  }
+
+  @Override
+  public TimestampedObject<T>[] readQueue() {
+    TimestampedRaw[] raw = NetworkTablesJNI.readQueueRaw(m_handle);
+    @SuppressWarnings("unchecked")
+    TimestampedObject<T>[] arr = (TimestampedObject<T>[]) new TimestampedObject<?>[raw.length];
+    int err = 0;
+    for (int i = 0; i < raw.length; i++) {
+      arr[i] = fromRaw(raw[i], null);
+      if (arr[i].value == null) {
+        err++;
+      }
+    }
+
+    // discard bad values
+    if (err > 0) {
+      @SuppressWarnings("unchecked")
+      TimestampedObject<T>[] newArr =
+          (TimestampedObject<T>[]) new TimestampedObject<?>[raw.length - err];
+      int i = 0;
+      for (TimestampedObject<T> e : arr) {
+        if (e.value != null) {
+          arr[i] = e;
+          i++;
+        }
+      }
+      arr = newArr;
+    }
+
+    return arr;
+  }
+
+  @Override
+  public T[] readQueueValues() {
+    byte[][] raw = NetworkTablesJNI.readQueueValuesRaw(m_handle);
+    @SuppressWarnings("unchecked")
+    T[] arr = (T[]) Array.newInstance(m_topic.getProto().getTypeClass(), raw.length);
+    int err = 0;
+    for (int i = 0; i < raw.length; i++) {
+      arr[i] = fromRaw(raw[i], null);
+      if (arr[i] == null) {
+        err++;
+      }
+    }
+
+    // discard bad values
+    if (err > 0) {
+      @SuppressWarnings("unchecked")
+      T[] newArr = (T[]) Array.newInstance(m_topic.getProto().getTypeClass(), raw.length - err);
+      int i = 0;
+      for (T e : arr) {
+        if (e != null) {
+          arr[i] = e;
+          i++;
+        }
+      }
+      arr = newArr;
+    }
+
+    return arr;
+  }
+
+  @Override
+  public void set(T value, long time) {
+    try {
+      synchronized (m_buf) {
+        if (!m_schemaPublished) {
+          m_schemaPublished = true;
+          m_topic.getInstance().addSchema(m_buf.getProto());
+        }
+        ByteBuffer bb = m_buf.write(value);
+        NetworkTablesJNI.setRaw(m_handle, time, bb, 0, bb.position());
+      }
+    } catch (IOException e) {
+      // ignore
+    }
+  }
+
+  @Override
+  public void setDefault(T value) {
+    try {
+      synchronized (m_buf) {
+        if (!m_schemaPublished) {
+          m_schemaPublished = true;
+          m_topic.getInstance().addSchema(m_buf.getProto());
+        }
+        ByteBuffer bb = m_buf.write(value);
+        NetworkTablesJNI.setDefaultRaw(m_handle, 0, bb, 0, bb.position());
+      }
+    } catch (IOException e) {
+      // ignore
+    }
+  }
+
+  @Override
+  public void unpublish() {
+    NetworkTablesJNI.unpublish(m_handle);
+  }
+
+  private T fromRaw(byte[] raw, T defaultValue) {
+    if (raw.length == 0) {
+      return defaultValue;
+    }
+    try {
+      synchronized (m_buf) {
+        return m_buf.read(raw);
+      }
+    } catch (IOException e) {
+      return defaultValue;
+    }
+  }
+
+  private TimestampedObject<T> fromRaw(TimestampedRaw raw, T defaultValue) {
+    if (raw.value.length == 0) {
+      return new TimestampedObject<T>(0, 0, defaultValue);
+    }
+    try {
+      synchronized (m_buf) {
+        return new TimestampedObject<T>(raw.timestamp, raw.serverTime, m_buf.read(raw.value));
+      }
+    } catch (IOException e) {
+      return new TimestampedObject<T>(0, 0, defaultValue);
+    }
+  }
+
+  private final ProtobufTopic<T> m_topic;
+  private final T m_defaultValue;
+  private final ProtobufBuffer<T, ?> m_buf;
+  private boolean m_schemaPublished;
+  private static final byte[] m_emptyRaw = new byte[] {};
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufPublisher.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufPublisher.java
new file mode 100644
index 0000000..ab22b86
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufPublisher.java
@@ -0,0 +1,52 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import java.util.function.Consumer;
+
+/**
+ * NetworkTables protobuf-encoded value publisher.
+ *
+ * @param <T> value class
+ */
+public interface ProtobufPublisher<T> extends Publisher, Consumer<T> {
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  @Override
+  ProtobufTopic<T> getTopic();
+
+  /**
+   * Publish a new value using current NT time.
+   *
+   * @param value value to publish
+   */
+  default void set(T value) {
+    set(value, 0);
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  void set(T value, long time);
+
+  /**
+   * Publish a default value. On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value
+   */
+  void setDefault(T value);
+
+  @Override
+  default void accept(T value) {
+    set(value);
+  }
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufSubscriber.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufSubscriber.java
new file mode 100644
index 0000000..2236ce9
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufSubscriber.java
@@ -0,0 +1,94 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import java.util.function.Supplier;
+
+/**
+ * NetworkTables protobuf-encoded value subscriber.
+ *
+ * @param <T> value class
+ */
+@SuppressWarnings("PMD.MissingOverride")
+public interface ProtobufSubscriber<T> extends Subscriber, Supplier<T> {
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  @Override
+  ProtobufTopic<T> getTopic();
+
+  /**
+   * Get the last published value. If no value has been published or the value cannot be unpacked,
+   * returns the stored default value.
+   *
+   * @return value
+   */
+  T get();
+
+  /**
+   * Get the last published value. If no value has been published or the value cannot be unpacked,
+   * returns the passed defaultValue.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return value
+   */
+  T get(T defaultValue);
+
+  /**
+   * Get the last published value, replacing the contents in place of an existing object. If no
+   * value has been published or the value cannot be unpacked, does not replace the contents and
+   * returns false. This function will not work (will throw UnsupportedOperationException) unless T
+   * is mutable (and the implementation of Struct implements unpackInto).
+   *
+   * <p>Note: due to Java language limitations, it's not possible to validate at compile time that
+   * the out parameter is mutable.
+   *
+   * @param out object to replace contents of; must be mutable
+   * @return true if successful
+   * @throws UnsupportedOperationException if T is immutable
+   */
+  boolean getInto(T out);
+
+  /**
+   * Get the last published value along with its timestamp. If no value has been published or the
+   * value cannot be unpacked, returns the stored default value and a timestamp of 0.
+   *
+   * @return timestamped value
+   */
+  TimestampedObject<T> getAtomic();
+
+  /**
+   * Get the last published value along with its timestamp. If no value has been published or the
+   * value cannot be unpacked, returns the passed defaultValue and a timestamp of 0.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return timestamped value
+   */
+  TimestampedObject<T> getAtomic(T defaultValue);
+
+  /**
+   * Get an array of all valid value changes since the last call to readQueue. Also provides a
+   * timestamp for each value. Values that cannot be unpacked are dropped.
+   *
+   * <p>The "poll storage" subscribe option can be used to set the queue depth.
+   *
+   * @return Array of timestamped values; empty array if no valid new changes have been published
+   *     since the previous call.
+   */
+  TimestampedObject<T>[] readQueue();
+
+  /**
+   * Get an array of all valid value changes since the last call to readQueue. Values that cannot be
+   * unpacked are dropped.
+   *
+   * <p>The "poll storage" subscribe option can be used to set the queue depth.
+   *
+   * @return Array of values; empty array if no valid new changes have been published since the
+   *     previous call.
+   */
+  T[] readQueueValues();
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufTopic.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufTopic.java
new file mode 100644
index 0000000..c3dad13
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufTopic.java
@@ -0,0 +1,178 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.protobuf.ProtobufBuffer;
+
+/**
+ * NetworkTables protobuf-encoded value topic.
+ *
+ * @param <T> value class
+ */
+public final class ProtobufTopic<T> extends Topic {
+  private ProtobufTopic(Topic topic, Protobuf<T, ?> proto) {
+    super(topic.m_inst, topic.m_handle);
+    m_proto = proto;
+  }
+
+  private ProtobufTopic(NetworkTableInstance inst, int handle, Protobuf<T, ?> proto) {
+    super(inst, handle);
+    m_proto = proto;
+  }
+
+  /**
+   * Create a ProtobufTopic from a generic topic.
+   *
+   * @param <T> value class (inferred from proto)
+   * @param topic generic topic
+   * @param proto protobuf serialization implementation
+   * @return ProtobufTopic for value class
+   */
+  public static <T> ProtobufTopic<T> wrap(Topic topic, Protobuf<T, ?> proto) {
+    return new ProtobufTopic<T>(topic, proto);
+  }
+
+  /**
+   * Create a ProtobufTopic from a native handle; generally NetworkTableInstance.getProtobufTopic()
+   * should be used instead.
+   *
+   * @param <T> value class (inferred from proto)
+   * @param inst Instance
+   * @param handle Native handle
+   * @param proto protobuf serialization implementation
+   * @return ProtobufTopic for value class
+   */
+  public static <T> ProtobufTopic<T> wrap(
+      NetworkTableInstance inst, int handle, Protobuf<T, ?> proto) {
+    return new ProtobufTopic<T>(inst, handle, proto);
+  }
+
+  /**
+   * Create a new subscriber to the topic.
+   *
+   * <p>The subscriber is only active as long as the returned object is not closed.
+   *
+   * <p>Subscribers that do not match the published data type do not return any values. To determine
+   * if the data type matches, use the appropriate Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a getter function
+   * @param options subscribe options
+   * @return subscriber
+   */
+  public ProtobufSubscriber<T> subscribe(T defaultValue, PubSubOption... options) {
+    return new ProtobufEntryImpl<T>(
+        this,
+        ProtobufBuffer.create(m_proto),
+        NetworkTablesJNI.subscribe(
+            m_handle, NetworkTableType.kRaw.getValue(), m_proto.getTypeString(), options),
+        defaultValue,
+        false);
+  }
+
+  /**
+   * Create a new publisher to the topic.
+   *
+   * <p>The publisher is only active as long as the returned object is not closed.
+   *
+   * <p>It is not possible to publish two different data types to the same topic. Conflicts between
+   * publishers are typically resolved by the server on a first-come, first-served basis. Any
+   * published values that do not match the topic's data type are dropped (ignored). To determine if
+   * the data type matches, use the appropriate Topic functions.
+   *
+   * @param options publish options
+   * @return publisher
+   */
+  public ProtobufPublisher<T> publish(PubSubOption... options) {
+    m_inst.addSchema(m_proto);
+    return new ProtobufEntryImpl<T>(
+        this,
+        ProtobufBuffer.create(m_proto),
+        NetworkTablesJNI.publish(
+            m_handle, NetworkTableType.kRaw.getValue(), m_proto.getTypeString(), options),
+        null,
+        true);
+  }
+
+  /**
+   * Create a new publisher to the topic, with type string and initial properties.
+   *
+   * <p>The publisher is only active as long as the returned object is not closed.
+   *
+   * <p>It is not possible to publish two different data types to the same topic. Conflicts between
+   * publishers are typically resolved by the server on a first-come, first-served basis. Any
+   * published values that do not match the topic's data type are dropped (ignored). To determine if
+   * the data type matches, use the appropriate Topic functions.
+   *
+   * @param properties JSON properties
+   * @param options publish options
+   * @return publisher
+   * @throws IllegalArgumentException if properties is not a JSON object
+   */
+  public ProtobufPublisher<T> publishEx(String properties, PubSubOption... options) {
+    m_inst.addSchema(m_proto);
+    return new ProtobufEntryImpl<T>(
+        this,
+        ProtobufBuffer.create(m_proto),
+        NetworkTablesJNI.publishEx(
+            m_handle,
+            NetworkTableType.kRaw.getValue(),
+            m_proto.getTypeString(),
+            properties,
+            options),
+        null,
+        true);
+  }
+
+  /**
+   * Create a new entry for the topic.
+   *
+   * <p>Entries act as a combination of a subscriber and a weak publisher. The subscriber is active
+   * as long as the entry is not closed. The publisher is created when the entry is first written
+   * to, and remains active until either unpublish() is called or the entry is closed.
+   *
+   * <p>It is not possible to use two different data types with the same topic. Conflicts between
+   * publishers are typically resolved by the server on a first-come, first-served basis. Any
+   * published values that do not match the topic's data type are dropped (ignored), and the entry
+   * will show no new values if the data type does not match. To determine if the data type matches,
+   * use the appropriate Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a getter function
+   * @param options publish and/or subscribe options
+   * @return entry
+   */
+  public ProtobufEntry<T> getEntry(T defaultValue, PubSubOption... options) {
+    return new ProtobufEntryImpl<T>(
+        this,
+        ProtobufBuffer.create(m_proto),
+        NetworkTablesJNI.getEntry(
+            m_handle, NetworkTableType.kRaw.getValue(), m_proto.getTypeString(), options),
+        defaultValue,
+        false);
+  }
+
+  public Protobuf<T, ?> getProto() {
+    return m_proto;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other == this) {
+      return true;
+    }
+    if (!(other instanceof ProtobufTopic)) {
+      return false;
+    }
+
+    return super.equals(other) && m_proto == ((ProtobufTopic<?>) other).m_proto;
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() ^ m_proto.hashCode();
+  }
+
+  private final Protobuf<T, ?> m_proto;
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayEntry.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayEntry.java
new file mode 100644
index 0000000..4cc9e85
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayEntry.java
@@ -0,0 +1,17 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+/**
+ * NetworkTables struct-encoded array value entry.
+ *
+ * <p>Unlike NetworkTableEntry, the entry goes away when close() is called.
+ *
+ * @param <T> value class
+ */
+public interface StructArrayEntry<T> extends StructArraySubscriber<T>, StructArrayPublisher<T> {
+  /** Stops publishing the entry if it's published. */
+  void unpublish();
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayEntryImpl.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayEntryImpl.java
new file mode 100644
index 0000000..4e8a4a0
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayEntryImpl.java
@@ -0,0 +1,197 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import edu.wpi.first.util.struct.StructBuffer;
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+
+/**
+ * NetworkTables struct-encoded value implementation.
+ *
+ * @param <T> value class
+ */
+@SuppressWarnings("PMD.ArrayIsStoredDirectly")
+final class StructArrayEntryImpl<T> extends EntryBase implements StructArrayEntry<T> {
+  /**
+   * Constructor.
+   *
+   * @param topic Topic
+   * @param handle Native handle
+   * @param defaultValue Default value for get()
+   */
+  StructArrayEntryImpl(
+      StructArrayTopic<T> topic,
+      StructBuffer<T> buf,
+      int handle,
+      T[] defaultValue,
+      boolean schemaPublished) {
+    super(handle);
+    m_topic = topic;
+    m_defaultValue = defaultValue;
+    m_buf = buf;
+    m_schemaPublished = schemaPublished;
+  }
+
+  @Override
+  public StructArrayTopic<T> getTopic() {
+    return m_topic;
+  }
+
+  @Override
+  public T[] get() {
+    return fromRaw(NetworkTablesJNI.getRaw(m_handle, m_emptyRaw), m_defaultValue);
+  }
+
+  @Override
+  public T[] get(T[] defaultValue) {
+    return fromRaw(NetworkTablesJNI.getRaw(m_handle, m_emptyRaw), defaultValue);
+  }
+
+  @Override
+  public TimestampedObject<T[]> getAtomic() {
+    return fromRaw(NetworkTablesJNI.getAtomicRaw(m_handle, m_emptyRaw), m_defaultValue);
+  }
+
+  @Override
+  public TimestampedObject<T[]> getAtomic(T[] defaultValue) {
+    return fromRaw(NetworkTablesJNI.getAtomicRaw(m_handle, m_emptyRaw), defaultValue);
+  }
+
+  @Override
+  public TimestampedObject<T[]>[] readQueue() {
+    TimestampedRaw[] raw = NetworkTablesJNI.readQueueRaw(m_handle);
+    @SuppressWarnings("unchecked")
+    TimestampedObject<T[]>[] arr = (TimestampedObject<T[]>[]) new TimestampedObject<?>[raw.length];
+    int err = 0;
+    for (int i = 0; i < raw.length; i++) {
+      arr[i] = fromRaw(raw[i], null);
+      if (arr[i].value == null) {
+        err++;
+      }
+    }
+
+    // discard bad values
+    if (err > 0) {
+      @SuppressWarnings("unchecked")
+      TimestampedObject<T[]>[] newArr =
+          (TimestampedObject<T[]>[]) new TimestampedObject<?>[raw.length - err];
+      int i = 0;
+      for (TimestampedObject<T[]> e : arr) {
+        if (e.value != null) {
+          arr[i] = e;
+          i++;
+        }
+      }
+      arr = newArr;
+    }
+
+    return arr;
+  }
+
+  @Override
+  public T[][] readQueueValues() {
+    byte[][] raw = NetworkTablesJNI.readQueueValuesRaw(m_handle);
+    @SuppressWarnings("unchecked")
+    T[][] arr = (T[][]) Array.newInstance(Array.class, raw.length);
+    int err = 0;
+    for (int i = 0; i < raw.length; i++) {
+      arr[i] = fromRaw(raw[i], null);
+      if (arr[i] == null) {
+        err++;
+      }
+    }
+
+    // discard bad values
+    if (err > 0) {
+      @SuppressWarnings("unchecked")
+      T[][] newArr = (T[][]) Array.newInstance(Array.class, raw.length - err);
+      int i = 0;
+      for (T[] e : arr) {
+        if (e != null) {
+          arr[i] = e;
+          i++;
+        }
+      }
+      arr = newArr;
+    }
+
+    return arr;
+  }
+
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  @Override
+  public void set(T[] value, long time) {
+    try {
+      synchronized (m_buf) {
+        if (!m_schemaPublished) {
+          m_schemaPublished = true;
+          m_topic.getInstance().addSchema(m_buf.getStruct());
+        }
+        ByteBuffer bb = m_buf.writeArray(value);
+        NetworkTablesJNI.setRaw(m_handle, time, bb, 0, bb.position());
+      }
+    } catch (RuntimeException e) {
+      // ignore
+    }
+  }
+
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  @Override
+  public void setDefault(T[] value) {
+    try {
+      synchronized (m_buf) {
+        if (!m_schemaPublished) {
+          m_schemaPublished = true;
+          m_topic.getInstance().addSchema(m_buf.getStruct());
+        }
+        ByteBuffer bb = m_buf.writeArray(value);
+        NetworkTablesJNI.setDefaultRaw(m_handle, 0, bb, 0, bb.position());
+      }
+    } catch (RuntimeException e) {
+      // ignore
+    }
+  }
+
+  @Override
+  public void unpublish() {
+    NetworkTablesJNI.unpublish(m_handle);
+  }
+
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  private T[] fromRaw(byte[] raw, T[] defaultValue) {
+    if (raw.length == 0) {
+      return defaultValue;
+    }
+    try {
+      synchronized (m_buf) {
+        return m_buf.readArray(raw);
+      }
+    } catch (RuntimeException e) {
+      return defaultValue;
+    }
+  }
+
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  private TimestampedObject<T[]> fromRaw(TimestampedRaw raw, T[] defaultValue) {
+    if (raw.value.length == 0) {
+      return new TimestampedObject<T[]>(0, 0, defaultValue);
+    }
+    try {
+      synchronized (m_buf) {
+        return new TimestampedObject<T[]>(
+            raw.timestamp, raw.serverTime, m_buf.readArray(raw.value));
+      }
+    } catch (RuntimeException e) {
+      return new TimestampedObject<T[]>(0, 0, defaultValue);
+    }
+  }
+
+  private final StructArrayTopic<T> m_topic;
+  private final T[] m_defaultValue;
+  private final StructBuffer<T> m_buf;
+  private boolean m_schemaPublished;
+  private static final byte[] m_emptyRaw = new byte[] {};
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayPublisher.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayPublisher.java
new file mode 100644
index 0000000..aa291e9
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayPublisher.java
@@ -0,0 +1,52 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import java.util.function.Consumer;
+
+/**
+ * NetworkTables struct-encoded array value publisher.
+ *
+ * @param <T> value class
+ */
+public interface StructArrayPublisher<T> extends Publisher, Consumer<T[]> {
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  @Override
+  StructArrayTopic<T> getTopic();
+
+  /**
+   * Publish a new value using current NT time.
+   *
+   * @param value value to publish
+   */
+  default void set(T[] value) {
+    set(value, 0);
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  void set(T[] value, long time);
+
+  /**
+   * Publish a default value. On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value
+   */
+  void setDefault(T[] value);
+
+  @Override
+  default void accept(T[] value) {
+    set(value);
+  }
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArraySubscriber.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArraySubscriber.java
new file mode 100644
index 0000000..b4755e6
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArraySubscriber.java
@@ -0,0 +1,79 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import java.util.function.Supplier;
+
+/**
+ * NetworkTables struct-encoded array value subscriber.
+ *
+ * @param <T> value class
+ */
+@SuppressWarnings("PMD.MissingOverride")
+public interface StructArraySubscriber<T> extends Subscriber, Supplier<T[]> {
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  @Override
+  StructArrayTopic<T> getTopic();
+
+  /**
+   * Get the last published value. If no value has been published or the value cannot be unpacked,
+   * returns the stored default value.
+   *
+   * @return value
+   */
+  T[] get();
+
+  /**
+   * Get the last published value. If no value has been published or the value cannot be unpacked,
+   * returns the passed defaultValue.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return value
+   */
+  T[] get(T[] defaultValue);
+
+  /**
+   * Get the last published value along with its timestamp. If no value has been published or the
+   * value cannot be unpacked, returns the stored default value and a timestamp of 0.
+   *
+   * @return timestamped value
+   */
+  TimestampedObject<T[]> getAtomic();
+
+  /**
+   * Get the last published value along with its timestamp. If no value has been published or the
+   * value cannot be unpacked, returns the passed defaultValue and a timestamp of 0.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return timestamped value
+   */
+  TimestampedObject<T[]> getAtomic(T[] defaultValue);
+
+  /**
+   * Get an array of all valid value changes since the last call to readQueue. Also provides a
+   * timestamp for each value. Values that cannot be unpacked are dropped.
+   *
+   * <p>The "poll storage" subscribe option can be used to set the queue depth.
+   *
+   * @return Array of timestamped values; empty array if no valid new changes have been published
+   *     since the previous call.
+   */
+  TimestampedObject<T[]>[] readQueue();
+
+  /**
+   * Get an array of all valid value changes since the last call to readQueue. Values that cannot be
+   * unpacked are dropped.
+   *
+   * <p>The "poll storage" subscribe option can be used to set the queue depth.
+   *
+   * @return Array of values; empty array if no valid new changes have been published since the
+   *     previous call.
+   */
+  T[][] readQueueValues();
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayTopic.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayTopic.java
new file mode 100644
index 0000000..247501b
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayTopic.java
@@ -0,0 +1,178 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import edu.wpi.first.util.struct.Struct;
+import edu.wpi.first.util.struct.StructBuffer;
+
+/**
+ * NetworkTables struct-encoded array value topic.
+ *
+ * @param <T> value class
+ */
+public final class StructArrayTopic<T> extends Topic {
+  private StructArrayTopic(Topic topic, Struct<T> struct) {
+    super(topic.m_inst, topic.m_handle);
+    m_struct = struct;
+  }
+
+  private StructArrayTopic(NetworkTableInstance inst, int handle, Struct<T> struct) {
+    super(inst, handle);
+    m_struct = struct;
+  }
+
+  /**
+   * Create a StructArrayTopic from a generic topic.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param topic generic topic
+   * @param struct struct serialization implementation
+   * @return StructArrayTopic for value class
+   */
+  public static <T> StructArrayTopic<T> wrap(Topic topic, Struct<T> struct) {
+    return new StructArrayTopic<T>(topic, struct);
+  }
+
+  /**
+   * Create a StructArrayTopic from a native handle; generally
+   * NetworkTableInstance.getStructArrayTopic() should be used instead.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param inst Instance
+   * @param handle Native handle
+   * @param struct struct serialization implementation
+   * @return StructArrayTopic for value class
+   */
+  public static <T> StructArrayTopic<T> wrap(
+      NetworkTableInstance inst, int handle, Struct<T> struct) {
+    return new StructArrayTopic<T>(inst, handle, struct);
+  }
+
+  /**
+   * Create a new subscriber to the topic.
+   *
+   * <p>The subscriber is only active as long as the returned object is not closed.
+   *
+   * <p>Subscribers that do not match the published data type do not return any values. To determine
+   * if the data type matches, use the appropriate Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a getter function
+   * @param options subscribe options
+   * @return subscriber
+   */
+  public StructArraySubscriber<T> subscribe(T[] defaultValue, PubSubOption... options) {
+    return new StructArrayEntryImpl<T>(
+        this,
+        StructBuffer.create(m_struct),
+        NetworkTablesJNI.subscribe(
+            m_handle, NetworkTableType.kRaw.getValue(), m_struct.getTypeString() + "[]", options),
+        defaultValue,
+        false);
+  }
+
+  /**
+   * Create a new publisher to the topic.
+   *
+   * <p>The publisher is only active as long as the returned object is not closed.
+   *
+   * <p>It is not possible to publish two different data types to the same topic. Conflicts between
+   * publishers are typically resolved by the server on a first-come, first-served basis. Any
+   * published values that do not match the topic's data type are dropped (ignored). To determine if
+   * the data type matches, use the appropriate Topic functions.
+   *
+   * @param options publish options
+   * @return publisher
+   */
+  public StructArrayPublisher<T> publish(PubSubOption... options) {
+    m_inst.addSchema(m_struct);
+    return new StructArrayEntryImpl<T>(
+        this,
+        StructBuffer.create(m_struct),
+        NetworkTablesJNI.publish(
+            m_handle, NetworkTableType.kRaw.getValue(), m_struct.getTypeString() + "[]", options),
+        null,
+        true);
+  }
+
+  /**
+   * Create a new publisher to the topic, with type string and initial properties.
+   *
+   * <p>The publisher is only active as long as the returned object is not closed.
+   *
+   * <p>It is not possible to publish two different data types to the same topic. Conflicts between
+   * publishers are typically resolved by the server on a first-come, first-served basis. Any
+   * published values that do not match the topic's data type are dropped (ignored). To determine if
+   * the data type matches, use the appropriate Topic functions.
+   *
+   * @param properties JSON properties
+   * @param options publish options
+   * @return publisher
+   * @throws IllegalArgumentException if properties is not a JSON object
+   */
+  public StructArrayPublisher<T> publishEx(String properties, PubSubOption... options) {
+    m_inst.addSchema(m_struct);
+    return new StructArrayEntryImpl<T>(
+        this,
+        StructBuffer.create(m_struct),
+        NetworkTablesJNI.publishEx(
+            m_handle,
+            NetworkTableType.kRaw.getValue(),
+            m_struct.getTypeString() + "[]",
+            properties,
+            options),
+        null,
+        true);
+  }
+
+  /**
+   * Create a new entry for the topic.
+   *
+   * <p>Entries act as a combination of a subscriber and a weak publisher. The subscriber is active
+   * as long as the entry is not closed. The publisher is created when the entry is first written
+   * to, and remains active until either unpublish() is called or the entry is closed.
+   *
+   * <p>It is not possible to use two different data types with the same topic. Conflicts between
+   * publishers are typically resolved by the server on a first-come, first-served basis. Any
+   * published values that do not match the topic's data type are dropped (ignored), and the entry
+   * will show no new values if the data type does not match. To determine if the data type matches,
+   * use the appropriate Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a getter function
+   * @param options publish and/or subscribe options
+   * @return entry
+   */
+  public StructArrayEntry<T> getEntry(T[] defaultValue, PubSubOption... options) {
+    return new StructArrayEntryImpl<T>(
+        this,
+        StructBuffer.create(m_struct),
+        NetworkTablesJNI.getEntry(
+            m_handle, NetworkTableType.kRaw.getValue(), m_struct.getTypeString() + "[]", options),
+        defaultValue,
+        false);
+  }
+
+  public Struct<T> getStruct() {
+    return m_struct;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other == this) {
+      return true;
+    }
+    if (!(other instanceof StructArrayTopic)) {
+      return false;
+    }
+
+    return super.equals(other) && m_struct == ((StructArrayTopic<?>) other).m_struct;
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() ^ m_struct.hashCode();
+  }
+
+  private final Struct<T> m_struct;
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructEntry.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructEntry.java
new file mode 100644
index 0000000..e687fdb
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructEntry.java
@@ -0,0 +1,17 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+/**
+ * NetworkTables struct-encoded value entry.
+ *
+ * <p>Unlike NetworkTableEntry, the entry goes away when close() is called.
+ *
+ * @param <T> value class
+ */
+public interface StructEntry<T> extends StructSubscriber<T>, StructPublisher<T> {
+  /** Stops publishing the entry if it's published. */
+  void unpublish();
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructEntryImpl.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructEntryImpl.java
new file mode 100644
index 0000000..bd02d27
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructEntryImpl.java
@@ -0,0 +1,207 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import edu.wpi.first.util.struct.StructBuffer;
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+
+/**
+ * NetworkTables struct-encoded value implementation.
+ *
+ * @param <T> value class
+ */
+final class StructEntryImpl<T> extends EntryBase implements StructEntry<T> {
+  /**
+   * Constructor.
+   *
+   * @param topic Topic
+   * @param handle Native handle
+   * @param defaultValue Default value for get()
+   */
+  StructEntryImpl(
+      StructTopic<T> topic,
+      StructBuffer<T> buf,
+      int handle,
+      T defaultValue,
+      boolean schemaPublished) {
+    super(handle);
+    m_topic = topic;
+    m_defaultValue = defaultValue;
+    m_buf = buf;
+    m_schemaPublished = schemaPublished;
+  }
+
+  @Override
+  public StructTopic<T> getTopic() {
+    return m_topic;
+  }
+
+  @Override
+  public T get() {
+    return fromRaw(NetworkTablesJNI.getRaw(m_handle, m_emptyRaw), m_defaultValue);
+  }
+
+  @Override
+  public T get(T defaultValue) {
+    return fromRaw(NetworkTablesJNI.getRaw(m_handle, m_emptyRaw), defaultValue);
+  }
+
+  @Override
+  public boolean getInto(T out) {
+    byte[] raw = NetworkTablesJNI.getRaw(m_handle, m_emptyRaw);
+    if (raw.length == 0) {
+      return false;
+    }
+    synchronized (m_buf) {
+      m_buf.readInto(out, raw);
+      return true;
+    }
+  }
+
+  @Override
+  public TimestampedObject<T> getAtomic() {
+    return fromRaw(NetworkTablesJNI.getAtomicRaw(m_handle, m_emptyRaw), m_defaultValue);
+  }
+
+  @Override
+  public TimestampedObject<T> getAtomic(T defaultValue) {
+    return fromRaw(NetworkTablesJNI.getAtomicRaw(m_handle, m_emptyRaw), defaultValue);
+  }
+
+  @Override
+  public TimestampedObject<T>[] readQueue() {
+    TimestampedRaw[] raw = NetworkTablesJNI.readQueueRaw(m_handle);
+    @SuppressWarnings("unchecked")
+    TimestampedObject<T>[] arr = (TimestampedObject<T>[]) new TimestampedObject<?>[raw.length];
+    int err = 0;
+    for (int i = 0; i < raw.length; i++) {
+      arr[i] = fromRaw(raw[i], null);
+      if (arr[i].value == null) {
+        err++;
+      }
+    }
+
+    // discard bad values
+    if (err > 0) {
+      @SuppressWarnings("unchecked")
+      TimestampedObject<T>[] newArr =
+          (TimestampedObject<T>[]) new TimestampedObject<?>[raw.length - err];
+      int i = 0;
+      for (TimestampedObject<T> e : arr) {
+        if (e.value != null) {
+          arr[i] = e;
+          i++;
+        }
+      }
+      arr = newArr;
+    }
+
+    return arr;
+  }
+
+  @Override
+  public T[] readQueueValues() {
+    byte[][] raw = NetworkTablesJNI.readQueueValuesRaw(m_handle);
+    @SuppressWarnings("unchecked")
+    T[] arr = (T[]) Array.newInstance(m_topic.getStruct().getTypeClass(), raw.length);
+    int err = 0;
+    for (int i = 0; i < raw.length; i++) {
+      arr[i] = fromRaw(raw[i], null);
+      if (arr[i] == null) {
+        err++;
+      }
+    }
+
+    // discard bad values
+    if (err > 0) {
+      @SuppressWarnings("unchecked")
+      T[] newArr = (T[]) Array.newInstance(m_topic.getStruct().getTypeClass(), raw.length - err);
+      int i = 0;
+      for (T e : arr) {
+        if (e != null) {
+          arr[i] = e;
+          i++;
+        }
+      }
+      arr = newArr;
+    }
+
+    return arr;
+  }
+
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  @Override
+  public void set(T value, long time) {
+    try {
+      synchronized (m_buf) {
+        if (!m_schemaPublished) {
+          m_schemaPublished = true;
+          m_topic.getInstance().addSchema(m_buf.getStruct());
+        }
+        ByteBuffer bb = m_buf.write(value);
+        NetworkTablesJNI.setRaw(m_handle, time, bb, 0, bb.position());
+      }
+    } catch (RuntimeException e) {
+      // ignore
+    }
+  }
+
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  @Override
+  public void setDefault(T value) {
+    try {
+      synchronized (m_buf) {
+        if (!m_schemaPublished) {
+          m_schemaPublished = true;
+          m_topic.getInstance().addSchema(m_buf.getStruct());
+        }
+        ByteBuffer bb = m_buf.write(value);
+        NetworkTablesJNI.setDefaultRaw(m_handle, 0, bb, 0, bb.position());
+      }
+    } catch (RuntimeException e) {
+      // ignore
+    }
+  }
+
+  @Override
+  public void unpublish() {
+    NetworkTablesJNI.unpublish(m_handle);
+  }
+
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  private T fromRaw(byte[] raw, T defaultValue) {
+    if (raw.length == 0) {
+      return defaultValue;
+    }
+    try {
+      synchronized (m_buf) {
+        return m_buf.read(raw);
+      }
+    } catch (RuntimeException e) {
+      return defaultValue;
+    }
+  }
+
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  private TimestampedObject<T> fromRaw(TimestampedRaw raw, T defaultValue) {
+    if (raw.value.length == 0) {
+      return new TimestampedObject<T>(0, 0, defaultValue);
+    }
+    try {
+      synchronized (m_buf) {
+        return new TimestampedObject<T>(raw.timestamp, raw.serverTime, m_buf.read(raw.value));
+      }
+    } catch (RuntimeException e) {
+      return new TimestampedObject<T>(0, 0, defaultValue);
+    }
+  }
+
+  private final StructTopic<T> m_topic;
+  private final T m_defaultValue;
+  private final StructBuffer<T> m_buf;
+  private boolean m_schemaPublished;
+  private static final byte[] m_emptyRaw = new byte[] {};
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructPublisher.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructPublisher.java
new file mode 100644
index 0000000..ec766e4
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructPublisher.java
@@ -0,0 +1,52 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import java.util.function.Consumer;
+
+/**
+ * NetworkTables struct-encoded value publisher.
+ *
+ * @param <T> value class
+ */
+public interface StructPublisher<T> extends Publisher, Consumer<T> {
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  @Override
+  StructTopic<T> getTopic();
+
+  /**
+   * Publish a new value using current NT time.
+   *
+   * @param value value to publish
+   */
+  default void set(T value) {
+    set(value, 0);
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  void set(T value, long time);
+
+  /**
+   * Publish a default value. On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value
+   */
+  void setDefault(T value);
+
+  @Override
+  default void accept(T value) {
+    set(value);
+  }
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructSubscriber.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructSubscriber.java
new file mode 100644
index 0000000..b537b5c
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructSubscriber.java
@@ -0,0 +1,94 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import java.util.function.Supplier;
+
+/**
+ * NetworkTables struct-encoded value subscriber.
+ *
+ * @param <T> value class
+ */
+@SuppressWarnings("PMD.MissingOverride")
+public interface StructSubscriber<T> extends Subscriber, Supplier<T> {
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  @Override
+  StructTopic<T> getTopic();
+
+  /**
+   * Get the last published value. If no value has been published or the value cannot be unpacked,
+   * returns the stored default value.
+   *
+   * @return value
+   */
+  T get();
+
+  /**
+   * Get the last published value. If no value has been published or the value cannot be unpacked,
+   * returns the passed defaultValue.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return value
+   */
+  T get(T defaultValue);
+
+  /**
+   * Get the last published value, replacing the contents in place of an existing object. If no
+   * value has been published or the value cannot be unpacked, does not replace the contents and
+   * returns false. This function will not work (will throw UnsupportedOperationException) unless T
+   * is mutable (and the implementation of Struct implements unpackInto).
+   *
+   * <p>Note: due to Java language limitations, it's not possible to validate at compile time that
+   * the out parameter is mutable.
+   *
+   * @param out object to replace contents of; must be mutable
+   * @return true if successful, false if no value has been published
+   * @throws UnsupportedOperationException if T is immutable
+   */
+  boolean getInto(T out);
+
+  /**
+   * Get the last published value along with its timestamp. If no value has been published or the
+   * value cannot be unpacked, returns the stored default value and a timestamp of 0.
+   *
+   * @return timestamped value
+   */
+  TimestampedObject<T> getAtomic();
+
+  /**
+   * Get the last published value along with its timestamp If no value has been published or the
+   * value cannot be unpacked, returns the passed defaultValue and a timestamp of 0.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return timestamped value
+   */
+  TimestampedObject<T> getAtomic(T defaultValue);
+
+  /**
+   * Get an array of all valid value changes since the last call to readQueue. Also provides a
+   * timestamp for each value. Values that cannot be unpacked are dropped.
+   *
+   * <p>The "poll storage" subscribe option can be used to set the queue depth.
+   *
+   * @return Array of timestamped values; empty array if no valid new changes have been published
+   *     since the previous call.
+   */
+  TimestampedObject<T>[] readQueue();
+
+  /**
+   * Get an array of all value changes since the last call to readQueue. Values that cannot be
+   * unpacked are dropped.
+   *
+   * <p>The "poll storage" subscribe option can be used to set the queue depth.
+   *
+   * @return Array of values; empty array if no valid new changes have been published since the
+   *     previous call.
+   */
+  T[] readQueueValues();
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructTopic.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructTopic.java
new file mode 100644
index 0000000..b1ff026
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/StructTopic.java
@@ -0,0 +1,177 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import edu.wpi.first.util.struct.Struct;
+import edu.wpi.first.util.struct.StructBuffer;
+
+/**
+ * NetworkTables struct-encoded value topic.
+ *
+ * @param <T> value class
+ */
+public final class StructTopic<T> extends Topic {
+  private StructTopic(Topic topic, Struct<T> struct) {
+    super(topic.m_inst, topic.m_handle);
+    m_struct = struct;
+  }
+
+  private StructTopic(NetworkTableInstance inst, int handle, Struct<T> struct) {
+    super(inst, handle);
+    m_struct = struct;
+  }
+
+  /**
+   * Create a StructTopic from a generic topic.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param topic generic topic
+   * @param struct struct serialization implementation
+   * @return StructTopic for value class
+   */
+  public static <T> StructTopic<T> wrap(Topic topic, Struct<T> struct) {
+    return new StructTopic<T>(topic, struct);
+  }
+
+  /**
+   * Create a StructTopic from a native handle; generally NetworkTableInstance.getStructTopic()
+   * should be used instead.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param inst Instance
+   * @param handle Native handle
+   * @param struct struct serialization implementation
+   * @return StructTopic for value class
+   */
+  public static <T> StructTopic<T> wrap(NetworkTableInstance inst, int handle, Struct<T> struct) {
+    return new StructTopic<T>(inst, handle, struct);
+  }
+
+  /**
+   * Create a new subscriber to the topic.
+   *
+   * <p>The subscriber is only active as long as the returned object is not closed.
+   *
+   * <p>Subscribers that do not match the published data type do not return any values. To determine
+   * if the data type matches, use the appropriate Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a getter function
+   * @param options subscribe options
+   * @return subscriber
+   */
+  public StructSubscriber<T> subscribe(T defaultValue, PubSubOption... options) {
+    return new StructEntryImpl<T>(
+        this,
+        StructBuffer.create(m_struct),
+        NetworkTablesJNI.subscribe(
+            m_handle, NetworkTableType.kRaw.getValue(), m_struct.getTypeString(), options),
+        defaultValue,
+        false);
+  }
+
+  /**
+   * Create a new publisher to the topic.
+   *
+   * <p>The publisher is only active as long as the returned object is not closed.
+   *
+   * <p>It is not possible to publish two different data types to the same topic. Conflicts between
+   * publishers are typically resolved by the server on a first-come, first-served basis. Any
+   * published values that do not match the topic's data type are dropped (ignored). To determine if
+   * the data type matches, use the appropriate Topic functions.
+   *
+   * @param options publish options
+   * @return publisher
+   */
+  public StructPublisher<T> publish(PubSubOption... options) {
+    m_inst.addSchema(m_struct);
+    return new StructEntryImpl<T>(
+        this,
+        StructBuffer.create(m_struct),
+        NetworkTablesJNI.publish(
+            m_handle, NetworkTableType.kRaw.getValue(), m_struct.getTypeString(), options),
+        null,
+        true);
+  }
+
+  /**
+   * Create a new publisher to the topic, with type string and initial properties.
+   *
+   * <p>The publisher is only active as long as the returned object is not closed.
+   *
+   * <p>It is not possible to publish two different data types to the same topic. Conflicts between
+   * publishers are typically resolved by the server on a first-come, first-served basis. Any
+   * published values that do not match the topic's data type are dropped (ignored). To determine if
+   * the data type matches, use the appropriate Topic functions.
+   *
+   * @param properties JSON properties
+   * @param options publish options
+   * @return publisher
+   * @throws IllegalArgumentException if properties is not a JSON object
+   */
+  public StructPublisher<T> publishEx(String properties, PubSubOption... options) {
+    m_inst.addSchema(m_struct);
+    return new StructEntryImpl<T>(
+        this,
+        StructBuffer.create(m_struct),
+        NetworkTablesJNI.publishEx(
+            m_handle,
+            NetworkTableType.kRaw.getValue(),
+            m_struct.getTypeString(),
+            properties,
+            options),
+        null,
+        true);
+  }
+
+  /**
+   * Create a new entry for the topic.
+   *
+   * <p>Entries act as a combination of a subscriber and a weak publisher. The subscriber is active
+   * as long as the entry is not closed. The publisher is created when the entry is first written
+   * to, and remains active until either unpublish() is called or the entry is closed.
+   *
+   * <p>It is not possible to use two different data types with the same topic. Conflicts between
+   * publishers are typically resolved by the server on a first-come, first-served basis. Any
+   * published values that do not match the topic's data type are dropped (ignored), and the entry
+   * will show no new values if the data type does not match. To determine if the data type matches,
+   * use the appropriate Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a getter function
+   * @param options publish and/or subscribe options
+   * @return entry
+   */
+  public StructEntry<T> getEntry(T defaultValue, PubSubOption... options) {
+    return new StructEntryImpl<T>(
+        this,
+        StructBuffer.create(m_struct),
+        NetworkTablesJNI.getEntry(
+            m_handle, NetworkTableType.kRaw.getValue(), m_struct.getTypeString(), options),
+        defaultValue,
+        false);
+  }
+
+  public Struct<T> getStruct() {
+    return m_struct;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other == this) {
+      return true;
+    }
+    if (!(other instanceof StructTopic)) {
+      return false;
+    }
+
+    return super.equals(other) && m_struct == ((StructTopic<?>) other).m_struct;
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() ^ m_struct.hashCode();
+  }
+
+  private final Struct<T> m_struct;
+}
diff --git a/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/TimestampedObject.java b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/TimestampedObject.java
new file mode 100644
index 0000000..37c1544
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/java/edu/wpi/first/networktables/TimestampedObject.java
@@ -0,0 +1,33 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+/** NetworkTables timestamped object. */
+public final class TimestampedObject<T> {
+  /**
+   * Create a timestamped value.
+   *
+   * @param timestamp timestamp in local time base
+   * @param serverTime timestamp in server time base
+   * @param value value
+   */
+  public TimestampedObject(long timestamp, long serverTime, T value) {
+    this.timestamp = timestamp;
+    this.serverTime = serverTime;
+    this.value = value;
+  }
+
+  /** Timestamp in local time base. */
+  @SuppressWarnings("MemberName")
+  public final long timestamp;
+
+  /** Timestamp in server time base. May be 0 or 1 for locally set values. */
+  @SuppressWarnings("MemberName")
+  public final long serverTime;
+
+  /** Value. */
+  @SuppressWarnings("MemberName")
+  public final T value;
+}
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/ConnectionList.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/ConnectionList.cpp
index 4781f52..8dfca21 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/ConnectionList.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/ConnectionList.cpp
@@ -5,7 +5,7 @@
 #include "ConnectionList.h"
 
 #include <wpi/SmallVector.h>
-#include <wpi/json_serializer.h>
+#include <wpi/json.h>
 #include <wpi/raw_ostream.h>
 
 #include "IListenerStorage.h"
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/INetworkClient.h b/third_party/allwpilib/ntcore/src/main/native/cpp/INetworkClient.h
index 986f43a..e9523c8 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/INetworkClient.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/INetworkClient.h
@@ -18,6 +18,7 @@
 
   virtual void SetServers(
       std::span<const std::pair<std::string, unsigned int>> servers) = 0;
+  virtual void Disconnect() = 0;
 
   virtual void StartDSClient(unsigned int port) = 0;
   virtual void StopDSClient() = 0;
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/ListenerStorage.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/ListenerStorage.cpp
index fdbf03b..4270d0b 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/ListenerStorage.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/ListenerStorage.cpp
@@ -6,25 +6,12 @@
 
 #include <algorithm>
 
-#include <wpi/DenseMap.h>
 #include <wpi/SmallVector.h>
 
 #include "ntcore_c.h"
 
 using namespace nt;
 
-class ListenerStorage::Thread final : public wpi::SafeThreadEvent {
- public:
-  explicit Thread(NT_ListenerPoller poller) : m_poller{poller} {}
-
-  void Main() final;
-
-  NT_ListenerPoller m_poller;
-  wpi::DenseMap<NT_Listener, ListenerCallback> m_callbacks;
-  wpi::Event m_waitQueueWakeup;
-  wpi::Event m_waitQueueWaiter;
-};
-
 void ListenerStorage::Thread::Main() {
   while (m_active) {
     WPI_Handle signaledBuf[3];
@@ -55,10 +42,6 @@
   }
 }
 
-ListenerStorage::ListenerStorage(int inst) : m_inst{inst} {}
-
-ListenerStorage::~ListenerStorage() = default;
-
 void ListenerStorage::Activate(NT_Listener listenerHandle, unsigned int mask,
                                FinishEventFunc finishEvent) {
   std::scoped_lock lock{m_mutex};
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/ListenerStorage.h b/third_party/allwpilib/ntcore/src/main/native/cpp/ListenerStorage.h
index b291a12..49dc8ce 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/ListenerStorage.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/ListenerStorage.h
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include <wpi/DenseMap.h>
 #include <wpi/SafeThread.h>
 #include <wpi/SmallVector.h>
 #include <wpi/Synchronization.h>
@@ -19,16 +20,16 @@
 #include "Handle.h"
 #include "HandleMap.h"
 #include "IListenerStorage.h"
+#include "VectorSet.h"
 #include "ntcore_cpp.h"
 
 namespace nt {
 
 class ListenerStorage final : public IListenerStorage {
  public:
-  explicit ListenerStorage(int inst);
+  explicit ListenerStorage(int inst) : m_inst{inst} {}
   ListenerStorage(const ListenerStorage&) = delete;
   ListenerStorage& operator=(const ListenerStorage&) = delete;
-  ~ListenerStorage() final;
 
   // IListenerStorage interface
   void Activate(NT_Listener listenerHandle, unsigned int mask,
@@ -50,14 +51,16 @@
   NT_ListenerPoller CreateListenerPoller();
 
   // returns listener handle and mask for each listener that was destroyed
-  [[nodiscard]] std::vector<std::pair<NT_Listener, unsigned int>>
-  DestroyListenerPoller(NT_ListenerPoller pollerHandle);
+  [[nodiscard]]
+  std::vector<std::pair<NT_Listener, unsigned int>> DestroyListenerPoller(
+      NT_ListenerPoller pollerHandle);
 
   std::vector<Event> ReadListenerQueue(NT_ListenerPoller pollerHandle);
 
   // returns listener handle and mask for each listener that was destroyed
-  [[nodiscard]] std::vector<std::pair<NT_Listener, unsigned int>>
-  RemoveListener(NT_Listener listenerHandle);
+  [[nodiscard]]
+  std::vector<std::pair<NT_Listener, unsigned int>> RemoveListener(
+      NT_Listener listenerHandle);
 
   bool WaitForListenerQueue(double timeout);
 
@@ -95,21 +98,23 @@
   };
   HandleMap<ListenerData, 8> m_listeners;
 
-  // Utility wrapper for making a set-like vector
-  template <typename T>
-  class VectorSet : public std::vector<T> {
-   public:
-    void Add(T value) { this->push_back(value); }
-    void Remove(T value) { std::erase(*this, value); }
-  };
-
   VectorSet<ListenerData*> m_connListeners;
   VectorSet<ListenerData*> m_topicListeners;
   VectorSet<ListenerData*> m_valueListeners;
   VectorSet<ListenerData*> m_logListeners;
   VectorSet<ListenerData*> m_timeSyncListeners;
 
-  class Thread;
+  class Thread final : public wpi::SafeThreadEvent {
+   public:
+    explicit Thread(NT_ListenerPoller poller) : m_poller{poller} {}
+
+    void Main() final;
+
+    NT_ListenerPoller m_poller;
+    wpi::DenseMap<NT_Listener, ListenerCallback> m_callbacks;
+    wpi::Event m_waitQueueWakeup;
+    wpi::Event m_waitQueueWaiter;
+  };
   wpi::SafeThreadOwner<Thread> m_thread;
 };
 
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/LocalStorage.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/LocalStorage.cpp
index db8e065..0377f4f 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/LocalStorage.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/LocalStorage.cpp
@@ -7,22 +7,15 @@
 #include <algorithm>
 
 #include <wpi/DataLog.h>
+#include <wpi/SmallString.h>
 #include <wpi/StringExtras.h>
-#include <wpi/StringMap.h>
-#include <wpi/Synchronization.h>
-#include <wpi/UidVector.h>
-#include <wpi/circular_buffer.h>
 #include <wpi/json.h>
 
-#include "Handle.h"
-#include "HandleMap.h"
 #include "IListenerStorage.h"
 #include "Log.h"
-#include "PubSubOptions.h"
 #include "Types_internal.h"
 #include "Value_internal.h"
 #include "networktables/NetworkTableValue.h"
-#include "ntcore_c.h"
 
 using namespace nt;
 
@@ -32,177 +25,18 @@
 static constexpr size_t kMaxMultiSubscribers = 512;
 static constexpr size_t kMaxListeners = 512;
 
-namespace {
-
-static constexpr bool IsSpecial(std::string_view name) {
-  return name.empty() ? false : name.front() == '$';
-}
-
 static constexpr bool PrefixMatch(std::string_view name,
                                   std::string_view prefix, bool special) {
   return (!special || !prefix.empty()) && wpi::starts_with(name, prefix);
 }
 
-// Utility wrapper for making a set-like vector
-template <typename T>
-class VectorSet : public std::vector<T> {
- public:
-  void Add(T value) { this->push_back(value); }
-  void Remove(T value) { std::erase(*this, value); }
-};
+std::string LocalStorage::DataLoggerEntry::MakeMetadata(
+    std::string_view properties) {
+  return fmt::format("{{\"properties\":{},\"source\":\"NT\"}}", properties);
+}
 
-struct EntryData;
-struct PublisherData;
-struct SubscriberData;
-struct MultiSubscriberData;
-
-struct DataLoggerEntry {
-  DataLoggerEntry(wpi::log::DataLog& log, int entry, NT_DataLogger logger)
-      : log{&log}, entry{entry}, logger{logger} {}
-
-  static std::string MakeMetadata(std::string_view properties) {
-    return fmt::format("{{\"properties\":{},\"source\":\"NT\"}}", properties);
-  }
-
-  void Append(const Value& v);
-
-  wpi::log::DataLog* log;
-  int entry;
-  NT_DataLogger logger;
-};
-
-struct TopicData {
-  static constexpr auto kType = Handle::kTopic;
-
-  TopicData(NT_Topic handle, std::string_view name)
-      : handle{handle}, name{name}, special{IsSpecial(name)} {}
-
-  bool Exists() const { return onNetwork || !localPublishers.empty(); }
-
-  TopicInfo GetTopicInfo() const;
-
-  // invariants
-  wpi::SignalObject<NT_Topic> handle;
-  std::string name;
-  bool special;
-
-  Value lastValue;  // also stores timestamp
-  Value lastValueNetwork;
-  NT_Type type{NT_UNASSIGNED};
-  std::string typeStr;
-  unsigned int flags{0};            // for NT3 APIs
-  std::string propertiesStr{"{}"};  // cached string for GetTopicInfo() et al
-  wpi::json properties = wpi::json::object();
-  NT_Entry entry{0};  // cached entry for GetEntry()
-
-  bool onNetwork{false};  // true if there are any remote publishers
-
-  wpi::SmallVector<DataLoggerEntry, 1> datalogs;
-  NT_Type datalogType{NT_UNASSIGNED};
-
-  VectorSet<PublisherData*> localPublishers;
-  VectorSet<SubscriberData*> localSubscribers;
-  VectorSet<MultiSubscriberData*> multiSubscribers;
-  VectorSet<EntryData*> entries;
-  VectorSet<NT_Listener> listeners;
-};
-
-struct PubSubConfig : public PubSubOptionsImpl {
-  PubSubConfig() = default;
-  PubSubConfig(NT_Type type, std::string_view typeStr,
-               const PubSubOptions& options)
-      : PubSubOptionsImpl{options}, type{type}, typeStr{typeStr} {
-    prefixMatch = false;
-  }
-
-  NT_Type type{NT_UNASSIGNED};
-  std::string typeStr;
-};
-
-struct PublisherData {
-  static constexpr auto kType = Handle::kPublisher;
-
-  PublisherData(NT_Publisher handle, TopicData* topic, PubSubConfig config)
-      : handle{handle}, topic{topic}, config{std::move(config)} {}
-
-  void UpdateActive();
-
-  // invariants
-  wpi::SignalObject<NT_Publisher> handle;
-  TopicData* topic;
-  PubSubConfig config;
-
-  // whether or not the publisher should actually publish values
-  bool active{false};
-};
-
-struct SubscriberData {
-  static constexpr auto kType = Handle::kSubscriber;
-
-  SubscriberData(NT_Subscriber handle, TopicData* topic, PubSubConfig config)
-      : handle{handle},
-        topic{topic},
-        config{std::move(config)},
-        pollStorage{config.pollStorage} {}
-
-  void UpdateActive();
-
-  // invariants
-  wpi::SignalObject<NT_Subscriber> handle;
-  TopicData* topic;
-  PubSubConfig config;
-
-  // whether or not the subscriber should actually receive values
-  bool active{false};
-
-  // polling storage
-  wpi::circular_buffer<Value> pollStorage;
-
-  // value listeners
-  VectorSet<NT_Listener> valueListeners;
-};
-
-struct EntryData {
-  static constexpr auto kType = Handle::kEntry;
-
-  EntryData(NT_Entry handle, SubscriberData* subscriber)
-      : handle{handle}, topic{subscriber->topic}, subscriber{subscriber} {}
-
-  // invariants
-  wpi::SignalObject<NT_Entry> handle;
-  TopicData* topic;
-  SubscriberData* subscriber;
-
-  // the publisher (created on demand)
-  PublisherData* publisher{nullptr};
-};
-
-struct MultiSubscriberData {
-  static constexpr auto kType = Handle::kMultiSubscriber;
-
-  MultiSubscriberData(NT_MultiSubscriber handle,
-                      std::span<const std::string_view> prefixes,
-                      const PubSubOptionsImpl& options)
-      : handle{handle}, options{options} {
-    this->options.prefixMatch = true;
-    this->prefixes.reserve(prefixes.size());
-    for (auto&& prefix : prefixes) {
-      this->prefixes.emplace_back(prefix);
-    }
-  }
-
-  bool Matches(std::string_view name, bool special);
-
-  // invariants
-  wpi::SignalObject<NT_MultiSubscriber> handle;
-  std::vector<std::string> prefixes;
-  PubSubOptionsImpl options;
-
-  // value listeners
-  VectorSet<NT_Listener> valueListeners;
-};
-
-bool MultiSubscriberData::Matches(std::string_view name, bool special) {
+bool LocalStorage::MultiSubscriberData::Matches(std::string_view name,
+                                                bool special) {
   for (auto&& prefix : prefixes) {
     if (PrefixMatch(name, prefix, special)) {
       return true;
@@ -211,155 +45,14 @@
   return false;
 }
 
-struct ListenerData {
-  ListenerData(NT_Listener handle, SubscriberData* subscriber,
-               unsigned int eventMask, bool subscriberOwned)
-      : handle{handle},
-        eventMask{eventMask},
-        subscriber{subscriber},
-        subscriberOwned{subscriberOwned} {}
-  ListenerData(NT_Listener handle, MultiSubscriberData* subscriber,
-               unsigned int eventMask, bool subscriberOwned)
-      : handle{handle},
-        eventMask{eventMask},
-        multiSubscriber{subscriber},
-        subscriberOwned{subscriberOwned} {}
+int LocalStorage::DataLoggerData::Start(TopicData* topic, int64_t time) {
+  return log.Start(fmt::format("{}{}", logPrefix,
+                               wpi::drop_front(topic->name, prefix.size())),
+                   topic->typeStr == "int" ? "int64" : topic->typeStr,
+                   DataLoggerEntry::MakeMetadata(topic->propertiesStr), time);
+}
 
-  NT_Listener handle;
-  unsigned int eventMask;
-  SubscriberData* subscriber{nullptr};
-  MultiSubscriberData* multiSubscriber{nullptr};
-  bool subscriberOwned;
-};
-
-struct DataLoggerData {
-  static constexpr auto kType = Handle::kDataLogger;
-
-  DataLoggerData(NT_DataLogger handle, wpi::log::DataLog& log,
-                 std::string_view prefix, std::string_view logPrefix)
-      : handle{handle}, log{log}, prefix{prefix}, logPrefix{logPrefix} {}
-
-  int Start(TopicData* topic, int64_t time) {
-    return log.Start(fmt::format("{}{}", logPrefix,
-                                 wpi::drop_front(topic->name, prefix.size())),
-                     topic->typeStr,
-                     DataLoggerEntry::MakeMetadata(topic->propertiesStr), time);
-  }
-
-  NT_DataLogger handle;
-  wpi::log::DataLog& log;
-  std::string prefix;
-  std::string logPrefix;
-};
-
-struct LSImpl {
-  LSImpl(int inst, IListenerStorage& listenerStorage, wpi::Logger& logger)
-      : m_inst{inst}, m_listenerStorage{listenerStorage}, m_logger{logger} {}
-
-  int m_inst;
-  IListenerStorage& m_listenerStorage;
-  wpi::Logger& m_logger;
-  net::NetworkInterface* m_network{nullptr};
-
-  // handle mappings
-  HandleMap<TopicData, 16> m_topics;
-  HandleMap<PublisherData, 16> m_publishers;
-  HandleMap<SubscriberData, 16> m_subscribers;
-  HandleMap<EntryData, 16> m_entries;
-  HandleMap<MultiSubscriberData, 16> m_multiSubscribers;
-  HandleMap<DataLoggerData, 16> m_dataloggers;
-
-  // name mappings
-  wpi::StringMap<TopicData*> m_nameTopics;
-
-  // listeners
-  wpi::DenseMap<NT_Listener, std::unique_ptr<ListenerData>> m_listeners;
-
-  // string-based listeners
-  VectorSet<ListenerData*> m_topicPrefixListeners;
-
-  // topic functions
-  void NotifyTopic(TopicData* topic, unsigned int eventFlags);
-
-  void CheckReset(TopicData* topic);
-
-  bool SetValue(TopicData* topic, const Value& value, unsigned int eventFlags,
-                bool isDuplicate, const PublisherData* publisher);
-  void NotifyValue(TopicData* topic, unsigned int eventFlags, bool isDuplicate,
-                   const PublisherData* publisher);
-
-  void SetFlags(TopicData* topic, unsigned int flags);
-  void SetPersistent(TopicData* topic, bool value);
-  void SetRetained(TopicData* topic, bool value);
-  void SetProperties(TopicData* topic, const wpi::json& update,
-                     bool sendNetwork);
-  void PropertiesUpdated(TopicData* topic, const wpi::json& update,
-                         unsigned int eventFlags, bool sendNetwork,
-                         bool updateFlags = true);
-
-  void RefreshPubSubActive(TopicData* topic, bool warnOnSubMismatch);
-
-  void NetworkAnnounce(TopicData* topic, std::string_view typeStr,
-                       const wpi::json& properties, NT_Publisher pubHandle);
-  void RemoveNetworkPublisher(TopicData* topic);
-  void NetworkPropertiesUpdate(TopicData* topic, const wpi::json& update,
-                               bool ack);
-
-  PublisherData* AddLocalPublisher(TopicData* topic,
-                                   const wpi::json& properties,
-                                   const PubSubConfig& options);
-  std::unique_ptr<PublisherData> RemoveLocalPublisher(NT_Publisher pubHandle);
-
-  SubscriberData* AddLocalSubscriber(TopicData* topic,
-                                     const PubSubConfig& options);
-  std::unique_ptr<SubscriberData> RemoveLocalSubscriber(
-      NT_Subscriber subHandle);
-
-  EntryData* AddEntry(SubscriberData* subscriber);
-  std::unique_ptr<EntryData> RemoveEntry(NT_Entry entryHandle);
-
-  MultiSubscriberData* AddMultiSubscriber(
-      std::span<const std::string_view> prefixes, const PubSubOptions& options);
-  std::unique_ptr<MultiSubscriberData> RemoveMultiSubscriber(
-      NT_MultiSubscriber subHandle);
-
-  void AddListenerImpl(NT_Listener listenerHandle, TopicData* topic,
-                       unsigned int eventMask);
-  void AddListenerImpl(NT_Listener listenerHandle, SubscriberData* subscriber,
-                       unsigned int eventMask, NT_Handle subentryHandle,
-                       bool subscriberOwned);
-  void AddListenerImpl(NT_Listener listenerHandle,
-                       MultiSubscriberData* subscriber, unsigned int eventMask,
-                       bool subscriberOwned);
-  void AddListenerImpl(NT_Listener listenerHandle,
-                       std::span<const std::string_view> prefixes,
-                       unsigned int eventMask);
-
-  void AddListener(NT_Listener listenerHandle,
-                   std::span<const std::string_view> prefixes,
-                   unsigned int mask);
-  void AddListener(NT_Listener listenerHandle, NT_Handle handle,
-                   unsigned int mask);
-  void RemoveListener(NT_Listener listenerHandle, unsigned int mask);
-
-  TopicData* GetOrCreateTopic(std::string_view name);
-  TopicData* GetTopic(NT_Handle handle);
-  SubscriberData* GetSubEntry(NT_Handle subentryHandle);
-  PublisherData* PublishEntry(EntryData* entry, NT_Type type);
-  Value* GetSubEntryValue(NT_Handle subentryHandle);
-
-  bool PublishLocalValue(PublisherData* publisher, const Value& value,
-                         bool force = false);
-
-  bool SetEntryValue(NT_Handle pubentryHandle, const Value& value);
-  bool SetDefaultEntryValue(NT_Handle pubsubentryHandle, const Value& value);
-
-  void RemoveSubEntry(NT_Handle subentryHandle);
-};
-
-}  // namespace
-
-void DataLoggerEntry::Append(const Value& v) {
+void LocalStorage::DataLoggerEntry::Append(const Value& v) {
   auto time = v.time();
   switch (v.type()) {
     case NT_BOOLEAN:
@@ -404,7 +97,7 @@
   }
 }
 
-TopicInfo TopicData::GetTopicInfo() const {
+TopicInfo LocalStorage::TopicData::GetTopicInfo() const {
   TopicInfo info;
   info.topic = handle;
   info.name = name;
@@ -414,19 +107,8 @@
   return info;
 }
 
-void PublisherData::UpdateActive() {
-  active = config.type == topic->type && config.typeStr == topic->typeStr;
-}
-
-void SubscriberData::UpdateActive() {
-  // for subscribers, unassigned is a wildcard
-  // also allow numerically compatible subscribers
-  active = config.type == NT_UNASSIGNED ||
-           (config.type == topic->type && config.typeStr == topic->typeStr) ||
-           IsNumericCompatible(config.type, topic->type);
-}
-
-void LSImpl::NotifyTopic(TopicData* topic, unsigned int eventFlags) {
+void LocalStorage::Impl::NotifyTopic(TopicData* topic,
+                                     unsigned int eventFlags) {
   DEBUG4("NotifyTopic({}, {})", topic->name, eventFlags);
   auto topicInfo = topic->GetTopicInfo();
   if (!topic->listeners.empty()) {
@@ -478,12 +160,13 @@
   }
 }
 
-void LSImpl::CheckReset(TopicData* topic) {
+void LocalStorage::Impl::CheckReset(TopicData* topic) {
   if (topic->Exists()) {
     return;
   }
   topic->lastValue = {};
   topic->lastValueNetwork = {};
+  topic->lastValueFromNetwork = false;
   topic->type = NT_UNASSIGNED;
   topic->typeStr.clear();
   topic->flags = 0;
@@ -491,30 +174,37 @@
   topic->propertiesStr = "{}";
 }
 
-bool LSImpl::SetValue(TopicData* topic, const Value& value,
-                      unsigned int eventFlags, bool isDuplicate,
-                      const PublisherData* publisher) {
+bool LocalStorage::Impl::SetValue(TopicData* topic, const Value& value,
+                                  unsigned int eventFlags, bool isDuplicate,
+                                  bool suppressIfDuplicate,
+                                  const PublisherData* publisher) {
   DEBUG4("SetValue({}, {}, {}, {})", topic->name, value.time(), eventFlags,
          isDuplicate);
   if (topic->type != NT_UNASSIGNED && topic->type != value.type()) {
     return false;
   }
-  if (!topic->lastValue || value.time() >= topic->lastValue.time()) {
+  if (!topic->lastValue || topic->lastValue.time() == 0 ||
+      value.time() >= topic->lastValue.time()) {
     // TODO: notify option even if older value
-    topic->type = value.type();
-    topic->lastValue = value;
-    NotifyValue(topic, eventFlags, isDuplicate, publisher);
-  }
-  if (!isDuplicate && topic->datalogType == value.type()) {
-    for (auto&& datalog : topic->datalogs) {
-      datalog.Append(value);
+    if (!(suppressIfDuplicate && isDuplicate)) {
+      topic->type = value.type();
+      topic->lastValue = value;
+      topic->lastValueFromNetwork = false;
+      NotifyValue(topic, eventFlags, isDuplicate, publisher);
+      if (topic->datalogType == value.type()) {
+        for (auto&& datalog : topic->datalogs) {
+          datalog.Append(value);
+        }
+      }
     }
   }
+
   return true;
 }
 
-void LSImpl::NotifyValue(TopicData* topic, unsigned int eventFlags,
-                         bool isDuplicate, const PublisherData* publisher) {
+void LocalStorage::Impl::NotifyValue(TopicData* topic, unsigned int eventFlags,
+                                     bool isDuplicate,
+                                     const PublisherData* publisher) {
   bool isNetwork = (eventFlags & NT_EVENT_VALUE_REMOTE) != 0;
   for (auto&& subscriber : topic->localSubscribers) {
     if (subscriber->active &&
@@ -543,7 +233,7 @@
   }
 }
 
-void LSImpl::SetFlags(TopicData* topic, unsigned int flags) {
+void LocalStorage::Impl::SetFlags(TopicData* topic, unsigned int flags) {
   wpi::json update = wpi::json::object();
   if ((flags & NT_PERSISTENT) != 0) {
     topic->properties["persistent"] = true;
@@ -565,7 +255,7 @@
   }
 }
 
-void LSImpl::SetPersistent(TopicData* topic, bool value) {
+void LocalStorage::Impl::SetPersistent(TopicData* topic, bool value) {
   wpi::json update = wpi::json::object();
   if (value) {
     topic->flags |= NT_PERSISTENT;
@@ -579,7 +269,7 @@
   PropertiesUpdated(topic, update, NT_EVENT_NONE, true, false);
 }
 
-void LSImpl::SetRetained(TopicData* topic, bool value) {
+void LocalStorage::Impl::SetRetained(TopicData* topic, bool value) {
   wpi::json update = wpi::json::object();
   if (value) {
     topic->flags |= NT_RETAINED;
@@ -593,8 +283,9 @@
   PropertiesUpdated(topic, update, NT_EVENT_NONE, true, false);
 }
 
-void LSImpl::SetProperties(TopicData* topic, const wpi::json& update,
-                           bool sendNetwork) {
+void LocalStorage::Impl::SetProperties(TopicData* topic,
+                                       const wpi::json& update,
+                                       bool sendNetwork) {
   if (!update.is_object()) {
     return;
   }
@@ -609,9 +300,10 @@
   PropertiesUpdated(topic, update, NT_EVENT_NONE, sendNetwork);
 }
 
-void LSImpl::PropertiesUpdated(TopicData* topic, const wpi::json& update,
-                               unsigned int eventFlags, bool sendNetwork,
-                               bool updateFlags) {
+void LocalStorage::Impl::PropertiesUpdated(TopicData* topic,
+                                           const wpi::json& update,
+                                           unsigned int eventFlags,
+                                           bool sendNetwork, bool updateFlags) {
   DEBUG4("PropertiesUpdated({}, {}, {}, {}, {})", topic->name, update.dump(),
          eventFlags, sendNetwork, updateFlags);
   if (updateFlags) {
@@ -646,7 +338,8 @@
   }
 }
 
-void LSImpl::RefreshPubSubActive(TopicData* topic, bool warnOnSubMismatch) {
+void LocalStorage::Impl::RefreshPubSubActive(TopicData* topic,
+                                             bool warnOnSubMismatch) {
   for (auto&& publisher : topic->localPublishers) {
     publisher->UpdateActive();
   }
@@ -662,9 +355,10 @@
   }
 }
 
-void LSImpl::NetworkAnnounce(TopicData* topic, std::string_view typeStr,
-                             const wpi::json& properties,
-                             NT_Publisher pubHandle) {
+void LocalStorage::Impl::NetworkAnnounce(TopicData* topic,
+                                         std::string_view typeStr,
+                                         const wpi::json& properties,
+                                         NT_Publisher pubHandle) {
   DEBUG4("LS NetworkAnnounce({}, {}, {}, {})", topic->name, typeStr,
          properties.dump(), pubHandle);
   if (pubHandle != 0) {
@@ -716,8 +410,9 @@
   }
 }
 
-void LSImpl::RemoveNetworkPublisher(TopicData* topic) {
-  DEBUG4("LS RemoveNetworkPublisher({}, {})", topic->handle, topic->name);
+void LocalStorage::Impl::RemoveNetworkPublisher(TopicData* topic) {
+  DEBUG4("LS RemoveNetworkPublisher({}, {})", topic->handle.GetHandle(),
+         topic->name);
   // this acts as an unpublish
   bool didExist = topic->Exists();
   topic->onNetwork = false;
@@ -746,8 +441,9 @@
   }
 }
 
-void LSImpl::NetworkPropertiesUpdate(TopicData* topic, const wpi::json& update,
-                                     bool ack) {
+void LocalStorage::Impl::NetworkPropertiesUpdate(TopicData* topic,
+                                                 const wpi::json& update,
+                                                 bool ack) {
   DEBUG4("NetworkPropertiesUpdate({},{})", topic->name, ack);
   if (ack) {
     return;  // ignore acks
@@ -755,9 +451,8 @@
   SetProperties(topic, update, false);
 }
 
-PublisherData* LSImpl::AddLocalPublisher(TopicData* topic,
-                                         const wpi::json& properties,
-                                         const PubSubConfig& config) {
+LocalStorage::PublisherData* LocalStorage::Impl::AddLocalPublisher(
+    TopicData* topic, const wpi::json& properties, const PubSubConfig& config) {
   bool didExist = topic->Exists();
   auto publisher = m_publishers.Add(m_inst, topic, config);
   topic->localPublishers.Add(publisher);
@@ -775,8 +470,7 @@
     } else if (properties.is_object()) {
       topic->properties = properties;
     } else {
-      WARNING("ignoring non-object properties when publishing '{}'",
-              topic->name);
+      WARN("ignoring non-object properties when publishing '{}'", topic->name);
       topic->properties = wpi::json::object();
     }
 
@@ -804,8 +498,8 @@
   return publisher;
 }
 
-std::unique_ptr<PublisherData> LSImpl::RemoveLocalPublisher(
-    NT_Publisher pubHandle) {
+std::unique_ptr<LocalStorage::PublisherData>
+LocalStorage::Impl::RemoveLocalPublisher(NT_Publisher pubHandle) {
   auto publisher = m_publishers.Remove(pubHandle);
   if (publisher) {
     auto topic = publisher->topic;
@@ -840,8 +534,8 @@
   return publisher;
 }
 
-SubscriberData* LSImpl::AddLocalSubscriber(TopicData* topic,
-                                           const PubSubConfig& config) {
+LocalStorage::SubscriberData* LocalStorage::Impl::AddLocalSubscriber(
+    TopicData* topic, const PubSubConfig& config) {
   DEBUG4("AddLocalSubscriber({})", topic->name);
   auto subscriber = m_subscribers.Add(m_inst, topic, config);
   topic->localSubscribers.Add(subscriber);
@@ -858,11 +552,22 @@
     DEBUG4("-> NetworkSubscribe({})", topic->name);
     m_network->Subscribe(subscriber->handle, {{topic->name}}, config);
   }
+
+  // queue current value
+  if (subscriber->active) {
+    if (!topic->lastValueFromNetwork && !config.disableLocal) {
+      subscriber->pollStorage.emplace_back(topic->lastValue);
+      subscriber->handle.Set();
+    } else if (topic->lastValueFromNetwork && !config.disableRemote) {
+      subscriber->pollStorage.emplace_back(topic->lastValueNetwork);
+      subscriber->handle.Set();
+    }
+  }
   return subscriber;
 }
 
-std::unique_ptr<SubscriberData> LSImpl::RemoveLocalSubscriber(
-    NT_Subscriber subHandle) {
+std::unique_ptr<LocalStorage::SubscriberData>
+LocalStorage::Impl::RemoveLocalSubscriber(NT_Subscriber subHandle) {
   auto subscriber = m_subscribers.Remove(subHandle);
   if (subscriber) {
     auto topic = subscriber->topic;
@@ -879,13 +584,15 @@
   return subscriber;
 }
 
-EntryData* LSImpl::AddEntry(SubscriberData* subscriber) {
+LocalStorage::EntryData* LocalStorage::Impl::AddEntry(
+    SubscriberData* subscriber) {
   auto entry = m_entries.Add(m_inst, subscriber);
   subscriber->topic->entries.Add(entry);
   return entry;
 }
 
-std::unique_ptr<EntryData> LSImpl::RemoveEntry(NT_Entry entryHandle) {
+std::unique_ptr<LocalStorage::EntryData> LocalStorage::Impl::RemoveEntry(
+    NT_Entry entryHandle) {
   auto entry = m_entries.Remove(entryHandle);
   if (entry) {
     entry->topic->entries.Remove(entry.get());
@@ -893,8 +600,9 @@
   return entry;
 }
 
-MultiSubscriberData* LSImpl::AddMultiSubscriber(
+LocalStorage::MultiSubscriberData* LocalStorage::Impl::AddMultiSubscriber(
     std::span<const std::string_view> prefixes, const PubSubOptions& options) {
+  DEBUG4("AddMultiSubscriber({})", fmt::join(prefixes, ","));
   auto subscriber = m_multiSubscribers.Add(m_inst, prefixes, options);
   // subscribe to any already existing topics
   for (auto&& topic : m_topics) {
@@ -906,14 +614,15 @@
     }
   }
   if (m_network) {
+    DEBUG4("-> NetworkSubscribe");
     m_network->Subscribe(subscriber->handle, subscriber->prefixes,
                          subscriber->options);
   }
   return subscriber;
 }
 
-std::unique_ptr<MultiSubscriberData> LSImpl::RemoveMultiSubscriber(
-    NT_MultiSubscriber subHandle) {
+std::unique_ptr<LocalStorage::MultiSubscriberData>
+LocalStorage::Impl::RemoveMultiSubscriber(NT_MultiSubscriber subHandle) {
   auto subscriber = m_multiSubscribers.Remove(subHandle);
   if (subscriber) {
     for (auto&& topic : m_topics) {
@@ -931,11 +640,11 @@
   return subscriber;
 }
 
-void LSImpl::AddListenerImpl(NT_Listener listenerHandle, TopicData* topic,
-                             unsigned int eventMask) {
+void LocalStorage::Impl::AddListenerImpl(NT_Listener listenerHandle,
+                                         TopicData* topic,
+                                         unsigned int eventMask) {
   if (topic->localSubscribers.size() >= kMaxSubscribers) {
-    ERROR(
-        "reached maximum number of subscribers to '{}', ignoring listener add",
+    ERR("reached maximum number of subscribers to '{}', ignoring listener add",
         topic->name);
     return;
   }
@@ -946,9 +655,11 @@
   AddListenerImpl(listenerHandle, sub, eventMask, sub->handle, true);
 }
 
-void LSImpl::AddListenerImpl(NT_Listener listenerHandle,
-                             SubscriberData* subscriber, unsigned int eventMask,
-                             NT_Handle subentryHandle, bool subscriberOwned) {
+void LocalStorage::Impl::AddListenerImpl(NT_Listener listenerHandle,
+                                         SubscriberData* subscriber,
+                                         unsigned int eventMask,
+                                         NT_Handle subentryHandle,
+                                         bool subscriberOwned) {
   m_listeners.try_emplace(listenerHandle, std::make_unique<ListenerData>(
                                               listenerHandle, subscriber,
                                               eventMask, subscriberOwned));
@@ -957,8 +668,8 @@
 
   if ((eventMask & NT_EVENT_TOPIC) != 0) {
     if (topic->listeners.size() >= kMaxListeners) {
-      ERROR("reached maximum number of listeners to '{}', not adding listener",
-            topic->name);
+      ERR("reached maximum number of listeners to '{}', not adding listener",
+          topic->name);
       return;
     }
 
@@ -979,8 +690,8 @@
 
   if ((eventMask & NT_EVENT_VALUE_ALL) != 0) {
     if (subscriber->valueListeners.size() >= kMaxListeners) {
-      ERROR("reached maximum number of listeners to '{}', not adding listener",
-            topic->name);
+      ERR("reached maximum number of listeners to '{}', not adding listener",
+          topic->name);
       return;
     }
     m_listenerStorage.Activate(
@@ -1004,9 +715,10 @@
   }
 }
 
-void LSImpl::AddListenerImpl(NT_Listener listenerHandle,
-                             MultiSubscriberData* subscriber,
-                             unsigned int eventMask, bool subscriberOwned) {
+void LocalStorage::Impl::AddListenerImpl(NT_Listener listenerHandle,
+                                         MultiSubscriberData* subscriber,
+                                         unsigned int eventMask,
+                                         bool subscriberOwned) {
   auto listener =
       m_listeners
           .try_emplace(listenerHandle, std::make_unique<ListenerData>(
@@ -1028,7 +740,7 @@
 
   if ((eventMask & NT_EVENT_TOPIC) != 0) {
     if (m_topicPrefixListeners.size() >= kMaxListeners) {
-      ERROR("reached maximum number of listeners, not adding listener");
+      ERR("reached maximum number of listeners, not adding listener");
       return;
     }
 
@@ -1054,7 +766,7 @@
 
   if ((eventMask & NT_EVENT_VALUE_ALL) != 0) {
     if (subscriber->valueListeners.size() >= kMaxListeners) {
-      ERROR("reached maximum number of listeners, not adding listener");
+      ERR("reached maximum number of listeners, not adding listener");
       return;
     }
 
@@ -1084,61 +796,8 @@
   }
 }
 
-void LSImpl::AddListener(NT_Listener listenerHandle,
-                         std::span<const std::string_view> prefixes,
-                         unsigned int eventMask) {
-  if (m_multiSubscribers.size() >= kMaxMultiSubscribers) {
-    ERROR("reached maximum number of multi-subscribers, not adding listener");
-    return;
-  }
-  // subscribe to make sure topic updates are received
-  auto sub = AddMultiSubscriber(
-      prefixes, {.topicsOnly = (eventMask & NT_EVENT_VALUE_ALL) == 0});
-  AddListenerImpl(listenerHandle, sub, eventMask, true);
-}
-
-void LSImpl::AddListener(NT_Listener listenerHandle, NT_Handle handle,
-                         unsigned int mask) {
-  if (auto topic = m_topics.Get(handle)) {
-    AddListenerImpl(listenerHandle, topic, mask);
-  } else if (auto sub = m_multiSubscribers.Get(handle)) {
-    AddListenerImpl(listenerHandle, sub, mask, false);
-  } else if (auto sub = m_subscribers.Get(handle)) {
-    AddListenerImpl(listenerHandle, sub, mask, sub->handle, false);
-  } else if (auto entry = m_entries.Get(handle)) {
-    AddListenerImpl(listenerHandle, entry->subscriber, mask, entry->handle,
-                    false);
-  }
-}
-
-void LSImpl::RemoveListener(NT_Listener listenerHandle, unsigned int mask) {
-  auto listenerIt = m_listeners.find(listenerHandle);
-  if (listenerIt == m_listeners.end()) {
-    return;
-  }
-  auto listener = std::move(listenerIt->getSecond());
-  m_listeners.erase(listenerIt);
-  if (!listener) {
-    return;
-  }
-
-  m_topicPrefixListeners.Remove(listener.get());
-  if (listener->subscriber) {
-    listener->subscriber->valueListeners.Remove(listenerHandle);
-    listener->subscriber->topic->listeners.Remove(listenerHandle);
-    if (listener->subscriberOwned) {
-      RemoveLocalSubscriber(listener->subscriber->handle);
-    }
-  }
-  if (listener->multiSubscriber) {
-    listener->multiSubscriber->valueListeners.Remove(listenerHandle);
-    if (listener->subscriberOwned) {
-      RemoveMultiSubscriber(listener->multiSubscriber->handle);
-    }
-  }
-}
-
-TopicData* LSImpl::GetOrCreateTopic(std::string_view name) {
+LocalStorage::TopicData* LocalStorage::Impl::GetOrCreateTopic(
+    std::string_view name) {
   auto& topic = m_nameTopics[name];
   // create if it does not already exist
   if (!topic) {
@@ -1153,7 +812,7 @@
   return topic;
 }
 
-TopicData* LSImpl::GetTopic(NT_Handle handle) {
+LocalStorage::TopicData* LocalStorage::Impl::GetTopic(NT_Handle handle) {
   switch (Handle{handle}.GetType()) {
     case Handle::kEntry: {
       if (auto entry = m_entries.Get(handle)) {
@@ -1181,7 +840,8 @@
   return {};
 }
 
-SubscriberData* LSImpl::GetSubEntry(NT_Handle subentryHandle) {
+LocalStorage::SubscriberData* LocalStorage::Impl::GetSubEntry(
+    NT_Handle subentryHandle) {
   Handle h{subentryHandle};
   if (h.IsType(Handle::kSubscriber)) {
     return m_subscribers.Get(subentryHandle);
@@ -1193,39 +853,36 @@
   }
 }
 
-PublisherData* LSImpl::PublishEntry(EntryData* entry, NT_Type type) {
+LocalStorage::PublisherData* LocalStorage::Impl::PublishEntry(EntryData* entry,
+                                                              NT_Type type) {
   if (entry->publisher) {
     return entry->publisher;
   }
-  auto typeStr = TypeToString(type);
   if (entry->subscriber->config.type == NT_UNASSIGNED) {
+    auto typeStr = TypeToString(type);
     entry->subscriber->config.type = type;
     entry->subscriber->config.typeStr = typeStr;
-  } else if (entry->subscriber->config.type != type ||
-             entry->subscriber->config.typeStr != typeStr) {
+  } else if (entry->subscriber->config.type != type) {
     if (!IsNumericCompatible(type, entry->subscriber->config.type)) {
       // don't allow dynamically changing the type of an entry
-      ERROR("cannot publish entry {} as type {}, previously subscribed as {}",
-            entry->topic->name, typeStr, entry->subscriber->config.typeStr);
+      auto typeStr = TypeToString(type);
+      ERR("cannot publish entry {} as type {}, previously subscribed as {}",
+          entry->topic->name, typeStr, entry->subscriber->config.typeStr);
       return nullptr;
     }
   }
   // create publisher
   entry->publisher = AddLocalPublisher(entry->topic, wpi::json::object(),
                                        entry->subscriber->config);
+  // exclude publisher if requested
+  if (entry->subscriber->config.excludeSelf) {
+    entry->subscriber->config.excludePublisher = entry->publisher->handle;
+  }
   return entry->publisher;
 }
 
-Value* LSImpl::GetSubEntryValue(NT_Handle subentryHandle) {
-  if (auto subscriber = GetSubEntry(subentryHandle)) {
-    return &subscriber->topic->lastValue;
-  } else {
-    return nullptr;
-  }
-}
-
-bool LSImpl::PublishLocalValue(PublisherData* publisher, const Value& value,
-                               bool force) {
+bool LocalStorage::Impl::PublishLocalValue(PublisherData* publisher,
+                                           const Value& value, bool force) {
   if (!value) {
     return false;
   }
@@ -1238,26 +895,28 @@
     return false;
   }
   if (publisher->active) {
-    bool isDuplicate, isNetworkDuplicate;
+    bool isDuplicate, isNetworkDuplicate, suppressDuplicates;
     if (force || publisher->config.keepDuplicates) {
-      isDuplicate = false;
+      suppressDuplicates = false;
       isNetworkDuplicate = false;
     } else {
-      isDuplicate = (publisher->topic->lastValue == value);
+      suppressDuplicates = true;
       isNetworkDuplicate = (publisher->topic->lastValueNetwork == value);
     }
+    isDuplicate = (publisher->topic->lastValue == value);
     if (!isNetworkDuplicate && m_network) {
       publisher->topic->lastValueNetwork = value;
       m_network->SetValue(publisher->handle, value);
     }
     return SetValue(publisher->topic, value, NT_EVENT_VALUE_LOCAL, isDuplicate,
-                    publisher);
+                    suppressDuplicates, publisher);
   } else {
     return false;
   }
 }
 
-bool LSImpl::SetEntryValue(NT_Handle pubentryHandle, const Value& value) {
+bool LocalStorage::Impl::SetEntryValue(NT_Handle pubentryHandle,
+                                       const Value& value) {
   if (!value) {
     return false;
   }
@@ -1265,9 +924,6 @@
   if (!publisher) {
     if (auto entry = m_entries.Get(pubentryHandle)) {
       publisher = PublishEntry(entry, value.type());
-      if (entry->subscriber->config.excludeSelf) {
-        entry->subscriber->config.excludePublisher = publisher->handle;
-      }
     }
     if (!publisher) {
       return false;
@@ -1276,8 +932,8 @@
   return PublishLocalValue(publisher, value);
 }
 
-bool LSImpl::SetDefaultEntryValue(NT_Handle pubsubentryHandle,
-                                  const Value& value) {
+bool LocalStorage::Impl::SetDefaultEntryValue(NT_Handle pubsubentryHandle,
+                                              const Value& value) {
   DEBUG4("SetDefaultEntryValue({}, {})", pubsubentryHandle,
          static_cast<int>(value.type()));
   if (!value) {
@@ -1299,17 +955,20 @@
       if (topic->type == NT_UNASSIGNED) {
         topic->type = value.type();
       }
+      Value newValue;
       if (topic->type == value.type()) {
-        topic->lastValue = value;
+        newValue = value;
       } else if (IsNumericCompatible(topic->type, value.type())) {
-        topic->lastValue = ConvertNumericValue(value, topic->type);
+        newValue = ConvertNumericValue(value, topic->type);
       } else {
         return true;
       }
-      topic->lastValue.SetTime(0);
-      topic->lastValue.SetServerTime(0);
+      newValue.SetTime(0);
+      newValue.SetServerTime(0);
       if (publisher) {
-        PublishLocalValue(publisher, topic->lastValue, true);
+        PublishLocalValue(publisher, newValue, true);
+      } else {
+        topic->lastValue = newValue;
       }
       return true;
     }
@@ -1317,7 +976,7 @@
   return false;
 }
 
-void LSImpl::RemoveSubEntry(NT_Handle subentryHandle) {
+void LocalStorage::Impl::RemoveSubEntry(NT_Handle subentryHandle) {
   Handle h{subentryHandle};
   if (h.IsType(Handle::kSubscriber)) {
     RemoveLocalSubscriber(subentryHandle);
@@ -1333,15 +992,9 @@
   }
 }
 
-class LocalStorage::Impl : public LSImpl {
- public:
-  Impl(int inst, IListenerStorage& listenerStorage, wpi::Logger& logger)
-      : LSImpl{inst, listenerStorage, logger} {}
-};
-
-LocalStorage::LocalStorage(int inst, IListenerStorage& listenerStorage,
-                           wpi::Logger& logger)
-    : m_impl{std::make_unique<Impl>(inst, listenerStorage, logger)} {}
+LocalStorage::Impl::Impl(int inst, IListenerStorage& listenerStorage,
+                         wpi::Logger& logger)
+    : m_inst{inst}, m_listenerStorage{listenerStorage}, m_logger{logger} {}
 
 LocalStorage::~LocalStorage() = default;
 
@@ -1350,43 +1003,48 @@
                                        const wpi::json& properties,
                                        NT_Publisher pubHandle) {
   std::scoped_lock lock{m_mutex};
-  auto topic = m_impl->GetOrCreateTopic(name);
-  m_impl->NetworkAnnounce(topic, typeStr, properties, pubHandle);
+  auto topic = m_impl.GetOrCreateTopic(name);
+  m_impl.NetworkAnnounce(topic, typeStr, properties, pubHandle);
   return topic->handle;
 }
 
 void LocalStorage::NetworkUnannounce(std::string_view name) {
   std::scoped_lock lock{m_mutex};
-  auto topic = m_impl->GetOrCreateTopic(name);
-  m_impl->RemoveNetworkPublisher(topic);
+  auto topic = m_impl.GetOrCreateTopic(name);
+  m_impl.RemoveNetworkPublisher(topic);
 }
 
 void LocalStorage::NetworkPropertiesUpdate(std::string_view name,
                                            const wpi::json& update, bool ack) {
   std::scoped_lock lock{m_mutex};
-  auto it = m_impl->m_nameTopics.find(name);
-  if (it != m_impl->m_nameTopics.end()) {
-    m_impl->NetworkPropertiesUpdate(it->second, update, ack);
+  auto it = m_impl.m_nameTopics.find(name);
+  if (it != m_impl.m_nameTopics.end()) {
+    m_impl.NetworkPropertiesUpdate(it->second, update, ack);
   }
 }
 
 void LocalStorage::NetworkSetValue(NT_Topic topicHandle, const Value& value) {
   std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    if (m_impl->SetValue(topic, value, NT_EVENT_VALUE_REMOTE,
-                         value == topic->lastValue, nullptr)) {
+  if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+    if (m_impl.SetValue(topic, value, NT_EVENT_VALUE_REMOTE,
+                        value == topic->lastValue, false, nullptr)) {
       topic->lastValueNetwork = value;
+      topic->lastValueFromNetwork = true;
     }
   }
 }
 
 void LocalStorage::StartNetwork(net::NetworkInterface* network) {
-  WPI_DEBUG4(m_impl->m_logger, "StartNetwork()");
   std::scoped_lock lock{m_mutex};
-  m_impl->m_network = network;
+  m_impl.StartNetwork(network);
+}
+
+void LocalStorage::Impl::StartNetwork(net::NetworkInterface* network) {
+  DEBUG4("StartNetwork()");
+  m_network = network;
   // publish all active publishers to the network and send last values
   // only send value once per topic
-  for (auto&& topic : m_impl->m_topics) {
+  for (auto&& topic : m_topics) {
     PublisherData* anyPublisher = nullptr;
     for (auto&& publisher : topic->localPublishers) {
       if (publisher->active) {
@@ -1399,23 +1057,23 @@
       network->SetValue(anyPublisher->handle, topic->lastValue);
     }
   }
-  for (auto&& subscriber : m_impl->m_subscribers) {
+  for (auto&& subscriber : m_subscribers) {
     network->Subscribe(subscriber->handle, {{subscriber->topic->name}},
                        subscriber->config);
   }
-  for (auto&& subscriber : m_impl->m_multiSubscribers) {
+  for (auto&& subscriber : m_multiSubscribers) {
     network->Subscribe(subscriber->handle, subscriber->prefixes,
                        subscriber->options);
   }
 }
 
 void LocalStorage::ClearNetwork() {
-  WPI_DEBUG4(m_impl->m_logger, "ClearNetwork()");
+  WPI_DEBUG4(m_impl.m_logger, "ClearNetwork()");
   std::scoped_lock lock{m_mutex};
-  m_impl->m_network = nullptr;
+  m_impl.m_network = nullptr;
   // treat as an unannounce all from the network side
-  for (auto&& topic : m_impl->m_topics) {
-    m_impl->RemoveNetworkPublisher(topic.get());
+  for (auto&& topic : m_impl.m_topics) {
+    m_impl.RemoveNetworkPublisher(topic.get());
   }
 }
 
@@ -1466,7 +1124,7 @@
                                               unsigned int types) {
   std::scoped_lock lock(m_mutex);
   std::vector<NT_Topic> rv;
-  ForEachTopic(m_impl->m_topics, prefix, types,
+  ForEachTopic(m_impl.m_topics, prefix, types,
                [&](TopicData& topic) { rv.push_back(topic.handle); });
   return rv;
 }
@@ -1475,7 +1133,7 @@
     std::string_view prefix, std::span<const std::string_view> types) {
   std::scoped_lock lock(m_mutex);
   std::vector<NT_Topic> rv;
-  ForEachTopic(m_impl->m_topics, prefix, types,
+  ForEachTopic(m_impl.m_topics, prefix, types,
                [&](TopicData& topic) { rv.push_back(topic.handle); });
   return rv;
 }
@@ -1484,7 +1142,7 @@
                                                   unsigned int types) {
   std::scoped_lock lock(m_mutex);
   std::vector<TopicInfo> rv;
-  ForEachTopic(m_impl->m_topics, prefix, types, [&](TopicData& topic) {
+  ForEachTopic(m_impl.m_topics, prefix, types, [&](TopicData& topic) {
     rv.emplace_back(topic.GetTopicInfo());
   });
   return rv;
@@ -1494,99 +1152,16 @@
     std::string_view prefix, std::span<const std::string_view> types) {
   std::scoped_lock lock(m_mutex);
   std::vector<TopicInfo> rv;
-  ForEachTopic(m_impl->m_topics, prefix, types, [&](TopicData& topic) {
+  ForEachTopic(m_impl.m_topics, prefix, types, [&](TopicData& topic) {
     rv.emplace_back(topic.GetTopicInfo());
   });
   return rv;
 }
 
-NT_Topic LocalStorage::GetTopic(std::string_view name) {
-  if (name.empty()) {
-    return {};
-  }
-  std::scoped_lock lock{m_mutex};
-  return m_impl->GetOrCreateTopic(name)->handle;
-}
-
-std::string LocalStorage::GetTopicName(NT_Topic topicHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    return topic->name;
-  } else {
-    return {};
-  }
-}
-
-NT_Type LocalStorage::GetTopicType(NT_Topic topicHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    return topic->type;
-  } else {
-    return {};
-  }
-}
-
-std::string LocalStorage::GetTopicTypeString(NT_Topic topicHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    return topic->typeStr;
-  } else {
-    return {};
-  }
-}
-
-void LocalStorage::SetTopicPersistent(NT_Topic topicHandle, bool value) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    m_impl->SetPersistent(topic, value);
-  }
-}
-
-bool LocalStorage::GetTopicPersistent(NT_Topic topicHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    return (topic->flags & NT_PERSISTENT) != 0;
-  } else {
-    return false;
-  }
-}
-
-void LocalStorage::SetTopicRetained(NT_Topic topicHandle, bool value) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    m_impl->SetRetained(topic, value);
-  }
-}
-
-bool LocalStorage::GetTopicRetained(NT_Topic topicHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    return (topic->flags & NT_RETAINED) != 0;
-  } else {
-    return false;
-  }
-}
-
-bool LocalStorage::GetTopicExists(NT_Handle handle) {
-  std::scoped_lock lock{m_mutex};
-  TopicData* topic = m_impl->GetTopic(handle);
-  return topic && topic->Exists();
-}
-
-wpi::json LocalStorage::GetTopicProperty(NT_Topic topicHandle,
-                                         std::string_view name) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    return topic->properties.value(name, wpi::json{});
-  } else {
-    return {};
-  }
-}
-
 void LocalStorage::SetTopicProperty(NT_Topic topicHandle, std::string_view name,
                                     const wpi::json& value) {
   std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
+  if (auto topic = m_impl.m_topics.Get(topicHandle)) {
     if (value.is_null()) {
       topic->properties.erase(name);
     } else {
@@ -1594,27 +1169,18 @@
     }
     wpi::json update = wpi::json::object();
     update[name] = value;
-    m_impl->PropertiesUpdated(topic, update, NT_EVENT_NONE, true);
+    m_impl.PropertiesUpdated(topic, update, NT_EVENT_NONE, true);
   }
 }
 
 void LocalStorage::DeleteTopicProperty(NT_Topic topicHandle,
                                        std::string_view name) {
   std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
+  if (auto topic = m_impl.m_topics.Get(topicHandle)) {
     topic->properties.erase(name);
     wpi::json update = wpi::json::object();
     update[name] = wpi::json();
-    m_impl->PropertiesUpdated(topic, update, NT_EVENT_NONE, true);
-  }
-}
-
-wpi::json LocalStorage::GetTopicProperties(NT_Topic topicHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    return topic->properties;
-  } else {
-    return wpi::json::object();
+    m_impl.PropertiesUpdated(topic, update, NT_EVENT_NONE, true);
   }
 }
 
@@ -1624,67 +1190,48 @@
     return false;
   }
   std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    m_impl->SetProperties(topic, update, true);
+  if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+    m_impl.SetProperties(topic, update, true);
     return true;
   } else {
     return {};
   }
 }
 
-TopicInfo LocalStorage::GetTopicInfo(NT_Topic topicHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->m_topics.Get(topicHandle)) {
-    return topic->GetTopicInfo();
-  } else {
-    return {};
-  }
-}
-
 NT_Subscriber LocalStorage::Subscribe(NT_Topic topicHandle, NT_Type type,
                                       std::string_view typeStr,
                                       const PubSubOptions& options) {
   std::scoped_lock lock{m_mutex};
 
   // Get the topic
-  auto* topic = m_impl->m_topics.Get(topicHandle);
+  auto* topic = m_impl.m_topics.Get(topicHandle);
   if (!topic) {
     return 0;
   }
 
   if (topic->localSubscribers.size() >= kMaxSubscribers) {
-    WPI_ERROR(m_impl->m_logger,
+    WPI_ERROR(m_impl.m_logger,
               "reached maximum number of subscribers to '{}', not subscribing",
               topic->name);
     return 0;
   }
 
   // Create subscriber
-  return m_impl->AddLocalSubscriber(topic, PubSubConfig{type, typeStr, options})
+  return m_impl.AddLocalSubscriber(topic, PubSubConfig{type, typeStr, options})
       ->handle;
 }
 
-void LocalStorage::Unsubscribe(NT_Subscriber subHandle) {
-  std::scoped_lock lock{m_mutex};
-  m_impl->RemoveSubEntry(subHandle);
-}
-
 NT_MultiSubscriber LocalStorage::SubscribeMultiple(
     std::span<const std::string_view> prefixes, const PubSubOptions& options) {
   std::scoped_lock lock{m_mutex};
 
-  if (m_impl->m_multiSubscribers.size() >= kMaxMultiSubscribers) {
-    WPI_ERROR(m_impl->m_logger,
+  if (m_impl.m_multiSubscribers.size() >= kMaxMultiSubscribers) {
+    WPI_ERROR(m_impl.m_logger,
               "reached maximum number of multi-subscribers, not subscribing");
     return 0;
   }
 
-  return m_impl->AddMultiSubscriber(prefixes, options)->handle;
-}
-
-void LocalStorage::UnsubscribeMultiple(NT_MultiSubscriber subHandle) {
-  std::scoped_lock lock{m_mutex};
-  m_impl->RemoveMultiSubscriber(subHandle);
+  return m_impl.AddMultiSubscriber(prefixes, options)->handle;
 }
 
 NT_Publisher LocalStorage::Publish(NT_Topic topicHandle, NT_Type type,
@@ -1694,31 +1241,31 @@
   std::scoped_lock lock{m_mutex};
 
   // Get the topic
-  auto* topic = m_impl->m_topics.Get(topicHandle);
+  auto* topic = m_impl.m_topics.Get(topicHandle);
   if (!topic) {
-    WPI_ERROR(m_impl->m_logger, "trying to publish invalid topic handle ({})",
+    WPI_ERROR(m_impl.m_logger, "trying to publish invalid topic handle ({})",
               topicHandle);
     return 0;
   }
 
   if (type == NT_UNASSIGNED || typeStr.empty()) {
     WPI_ERROR(
-        m_impl->m_logger,
+        m_impl.m_logger,
         "cannot publish '{}' with an unassigned type or empty type string",
         topic->name);
     return 0;
   }
 
   if (topic->localPublishers.size() >= kMaxPublishers) {
-    WPI_ERROR(m_impl->m_logger,
+    WPI_ERROR(m_impl.m_logger,
               "reached maximum number of publishers to '{}', not publishing",
               topic->name);
     return 0;
   }
 
   return m_impl
-      ->AddLocalPublisher(topic, properties,
-                          PubSubConfig{type, typeStr, options})
+      .AddLocalPublisher(topic, properties,
+                         PubSubConfig{type, typeStr, options})
       ->handle;
 }
 
@@ -1726,10 +1273,10 @@
   std::scoped_lock lock{m_mutex};
 
   if (Handle{pubentryHandle}.IsType(Handle::kPublisher)) {
-    m_impl->RemoveLocalPublisher(pubentryHandle);
-  } else if (auto entry = m_impl->m_entries.Get(pubentryHandle)) {
+    m_impl.RemoveLocalPublisher(pubentryHandle);
+  } else if (auto entry = m_impl.m_entries.Get(pubentryHandle)) {
     if (entry->publisher) {
-      m_impl->RemoveLocalPublisher(entry->publisher->handle);
+      m_impl.RemoveLocalPublisher(entry->publisher->handle);
       entry->publisher = nullptr;
     }
   } else {
@@ -1744,14 +1291,14 @@
   std::scoped_lock lock{m_mutex};
 
   // Get the topic
-  auto* topic = m_impl->m_topics.Get(topicHandle);
+  auto* topic = m_impl.m_topics.Get(topicHandle);
   if (!topic) {
     return 0;
   }
 
   if (topic->localSubscribers.size() >= kMaxSubscribers) {
     WPI_ERROR(
-        m_impl->m_logger,
+        m_impl.m_logger,
         "reached maximum number of subscribers to '{}', not creating entry",
         topic->name);
     return 0;
@@ -1759,15 +1306,10 @@
 
   // Create subscriber
   auto subscriber =
-      m_impl->AddLocalSubscriber(topic, PubSubConfig{type, typeStr, options});
+      m_impl.AddLocalSubscriber(topic, PubSubConfig{type, typeStr, options});
 
   // Create entry
-  return m_impl->AddEntry(subscriber)->handle;
-}
-
-void LocalStorage::ReleaseEntry(NT_Entry entryHandle) {
-  std::scoped_lock lock{m_mutex};
-  m_impl->RemoveSubEntry(entryHandle);
+  return m_impl.AddEntry(subscriber)->handle;
 }
 
 void LocalStorage::Release(NT_Handle pubsubentryHandle) {
@@ -1789,324 +1331,9 @@
   }
 }
 
-NT_Topic LocalStorage::GetTopicFromHandle(NT_Handle pubsubentryHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto topic = m_impl->GetTopic(pubsubentryHandle)) {
-    return topic->handle;
-  } else {
-    return {};
-  }
-}
-
-bool LocalStorage::SetEntryValue(NT_Handle pubentryHandle, const Value& value) {
-  std::scoped_lock lock{m_mutex};
-  return m_impl->SetEntryValue(pubentryHandle, value);
-}
-
-bool LocalStorage::SetDefaultEntryValue(NT_Handle pubsubentryHandle,
-                                        const Value& value) {
-  std::scoped_lock lock{m_mutex};
-  return m_impl->SetDefaultEntryValue(pubsubentryHandle, value);
-}
-
-TimestampedBoolean LocalStorage::GetAtomicBoolean(NT_Handle subentryHandle,
-                                                  bool defaultValue) {
-  std::scoped_lock lock{m_mutex};
-  Value* value = m_impl->GetSubEntryValue(subentryHandle);
-  if (value && value->type() == NT_BOOLEAN) {
-    return {value->time(), value->server_time(), value->GetBoolean()};
-  } else {
-    return {0, 0, defaultValue};
-  }
-}
-
-TimestampedString LocalStorage::GetAtomicString(NT_Handle subentryHandle,
-                                                std::string_view defaultValue) {
-  std::scoped_lock lock{m_mutex};
-  Value* value = m_impl->GetSubEntryValue(subentryHandle);
-  if (value && value->type() == NT_STRING) {
-    return {value->time(), value->server_time(),
-            std::string{value->GetString()}};
-  } else {
-    return {0, 0, std::string{defaultValue}};
-  }
-}
-
-TimestampedStringView LocalStorage::GetAtomicString(
-    NT_Handle subentryHandle, wpi::SmallVectorImpl<char>& buf,
-    std::string_view defaultValue) {
-  std::scoped_lock lock{m_mutex};
-  Value* value = m_impl->GetSubEntryValue(subentryHandle);
-  if (value && value->type() == NT_STRING) {
-    auto str = value->GetString();
-    buf.assign(str.begin(), str.end());
-    return {value->time(), value->server_time(), {buf.data(), buf.size()}};
-  } else {
-    return {0, 0, defaultValue};
-  }
-}
-
-template <typename T, typename U>
-static T GetAtomicNumber(Value* value, U defaultValue) {
-  if (value && value->type() == NT_INTEGER) {
-    return {value->time(), value->server_time(),
-            static_cast<U>(value->GetInteger())};
-  } else if (value && value->type() == NT_FLOAT) {
-    return {value->time(), value->server_time(),
-            static_cast<U>(value->GetFloat())};
-  } else if (value && value->type() == NT_DOUBLE) {
-    return {value->time(), value->server_time(),
-            static_cast<U>(value->GetDouble())};
-  } else {
-    return {0, 0, defaultValue};
-  }
-}
-
-template <typename T, typename U>
-static T GetAtomicNumberArray(Value* value, std::span<const U> defaultValue) {
-  if (value && value->type() == NT_INTEGER_ARRAY) {
-    auto arr = value->GetIntegerArray();
-    return {value->time(), value->server_time(), {arr.begin(), arr.end()}};
-  } else if (value && value->type() == NT_FLOAT_ARRAY) {
-    auto arr = value->GetFloatArray();
-    return {value->time(), value->server_time(), {arr.begin(), arr.end()}};
-  } else if (value && value->type() == NT_DOUBLE_ARRAY) {
-    auto arr = value->GetDoubleArray();
-    return {value->time(), value->server_time(), {arr.begin(), arr.end()}};
-  } else {
-    return {0, 0, {defaultValue.begin(), defaultValue.end()}};
-  }
-}
-
-template <typename T, typename U>
-static T GetAtomicNumberArray(Value* value, wpi::SmallVectorImpl<U>& buf,
-                              std::span<const U> defaultValue) {
-  if (value && value->type() == NT_INTEGER_ARRAY) {
-    auto str = value->GetIntegerArray();
-    buf.assign(str.begin(), str.end());
-    return {value->time(), value->server_time(), {buf.data(), buf.size()}};
-  } else if (value && value->type() == NT_FLOAT_ARRAY) {
-    auto str = value->GetFloatArray();
-    buf.assign(str.begin(), str.end());
-    return {value->time(), value->server_time(), {buf.data(), buf.size()}};
-  } else if (value && value->type() == NT_DOUBLE_ARRAY) {
-    auto str = value->GetDoubleArray();
-    buf.assign(str.begin(), str.end());
-    return {value->time(), value->server_time(), {buf.data(), buf.size()}};
-  } else {
-    buf.assign(defaultValue.begin(), defaultValue.end());
-    return {0, 0, {buf.data(), buf.size()}};
-  }
-}
-
-#define GET_ATOMIC_NUMBER(Name, dtype)                                  \
-  Timestamped##Name LocalStorage::GetAtomic##Name(NT_Handle subentry,   \
-                                                  dtype defaultValue) { \
-    std::scoped_lock lock{m_mutex};                                     \
-    return GetAtomicNumber<Timestamped##Name>(                          \
-        m_impl->GetSubEntryValue(subentry), defaultValue);              \
-  }                                                                     \
-                                                                        \
-  Timestamped##Name##Array LocalStorage::GetAtomic##Name##Array(        \
-      NT_Handle subentry, std::span<const dtype> defaultValue) {        \
-    std::scoped_lock lock{m_mutex};                                     \
-    return GetAtomicNumberArray<Timestamped##Name##Array>(              \
-        m_impl->GetSubEntryValue(subentry), defaultValue);              \
-  }                                                                     \
-                                                                        \
-  Timestamped##Name##ArrayView LocalStorage::GetAtomic##Name##Array(    \
-      NT_Handle subentry, wpi::SmallVectorImpl<dtype>& buf,             \
-      std::span<const dtype> defaultValue) {                            \
-    std::scoped_lock lock{m_mutex};                                     \
-    return GetAtomicNumberArray<Timestamped##Name##ArrayView>(          \
-        m_impl->GetSubEntryValue(subentry), buf, defaultValue);         \
-  }
-
-GET_ATOMIC_NUMBER(Integer, int64_t)
-GET_ATOMIC_NUMBER(Float, float)
-GET_ATOMIC_NUMBER(Double, double)
-
-#define GET_ATOMIC_ARRAY(Name, dtype)                                         \
-  Timestamped##Name LocalStorage::GetAtomic##Name(                            \
-      NT_Handle subentry, std::span<const dtype> defaultValue) {              \
-    std::scoped_lock lock{m_mutex};                                           \
-    Value* value = m_impl->GetSubEntryValue(subentry);                        \
-    if (value && value->Is##Name()) {                                         \
-      auto arr = value->Get##Name();                                          \
-      return {value->time(), value->server_time(), {arr.begin(), arr.end()}}; \
-    } else {                                                                  \
-      return {0, 0, {defaultValue.begin(), defaultValue.end()}};              \
-    }                                                                         \
-  }
-
-GET_ATOMIC_ARRAY(Raw, uint8_t)
-GET_ATOMIC_ARRAY(BooleanArray, int)
-GET_ATOMIC_ARRAY(StringArray, std::string)
-
-#define GET_ATOMIC_SMALL_ARRAY(Name, dtype)                                   \
-  Timestamped##Name##View LocalStorage::GetAtomic##Name(                      \
-      NT_Handle subentry, wpi::SmallVectorImpl<dtype>& buf,                   \
-      std::span<const dtype> defaultValue) {                                  \
-    std::scoped_lock lock{m_mutex};                                           \
-    Value* value = m_impl->GetSubEntryValue(subentry);                        \
-    if (value && value->Is##Name()) {                                         \
-      auto str = value->Get##Name();                                          \
-      buf.assign(str.begin(), str.end());                                     \
-      return {value->time(), value->server_time(), {buf.data(), buf.size()}}; \
-    } else {                                                                  \
-      buf.assign(defaultValue.begin(), defaultValue.end());                   \
-      return {0, 0, {buf.data(), buf.size()}};                                \
-    }                                                                         \
-  }
-
-GET_ATOMIC_SMALL_ARRAY(Raw, uint8_t)
-GET_ATOMIC_SMALL_ARRAY(BooleanArray, int)
-
-std::vector<Value> LocalStorage::ReadQueueValue(NT_Handle subentry) {
-  std::scoped_lock lock{m_mutex};
-  auto subscriber = m_impl->GetSubEntry(subentry);
-  if (!subscriber) {
-    return {};
-  }
-  std::vector<Value> rv;
-  rv.reserve(subscriber->pollStorage.size());
-  for (auto&& val : subscriber->pollStorage) {
-    rv.emplace_back(std::move(val));
-  }
-  subscriber->pollStorage.reset();
-  return rv;
-}
-
-std::vector<TimestampedBoolean> LocalStorage::ReadQueueBoolean(
-    NT_Handle subentry) {
-  std::scoped_lock lock{m_mutex};
-  auto subscriber = m_impl->GetSubEntry(subentry);
-  if (!subscriber) {
-    return {};
-  }
-  std::vector<TimestampedBoolean> rv;
-  rv.reserve(subscriber->pollStorage.size());
-  for (auto&& val : subscriber->pollStorage) {
-    if (val.IsBoolean()) {
-      rv.emplace_back(val.time(), val.server_time(), val.GetBoolean());
-    }
-  }
-  subscriber->pollStorage.reset();
-  return rv;
-}
-
-std::vector<TimestampedString> LocalStorage::ReadQueueString(
-    NT_Handle subentry) {
-  std::scoped_lock lock{m_mutex};
-  auto subscriber = m_impl->GetSubEntry(subentry);
-  if (!subscriber) {
-    return {};
-  }
-  std::vector<TimestampedString> rv;
-  rv.reserve(subscriber->pollStorage.size());
-  for (auto&& val : subscriber->pollStorage) {
-    if (val.IsString()) {
-      rv.emplace_back(val.time(), val.server_time(),
-                      std::string{val.GetString()});
-    }
-  }
-  subscriber->pollStorage.reset();
-  return rv;
-}
-
-#define READ_QUEUE_ARRAY(Name)                                         \
-  std::vector<Timestamped##Name> LocalStorage::ReadQueue##Name(        \
-      NT_Handle subentry) {                                            \
-    std::scoped_lock lock{m_mutex};                                    \
-    auto subscriber = m_impl->GetSubEntry(subentry);                   \
-    if (!subscriber) {                                                 \
-      return {};                                                       \
-    }                                                                  \
-    std::vector<Timestamped##Name> rv;                                 \
-    rv.reserve(subscriber->pollStorage.size());                        \
-    for (auto&& val : subscriber->pollStorage) {                       \
-      if (val.Is##Name()) {                                            \
-        auto arr = val.Get##Name();                                    \
-        rv.emplace_back(Timestamped##Name{                             \
-            val.time(), val.server_time(), {arr.begin(), arr.end()}}); \
-      }                                                                \
-    }                                                                  \
-    subscriber->pollStorage.reset();                                   \
-    return rv;                                                         \
-  }
-
-READ_QUEUE_ARRAY(Raw)
-READ_QUEUE_ARRAY(BooleanArray)
-READ_QUEUE_ARRAY(StringArray)
-
-template <typename T>
-static std::vector<T> ReadQueueNumber(SubscriberData* subscriber) {
-  if (!subscriber) {
-    return {};
-  }
-  std::vector<T> rv;
-  rv.reserve(subscriber->pollStorage.size());
-  for (auto&& val : subscriber->pollStorage) {
-    auto ts = val.time();
-    auto sts = val.server_time();
-    if (val.IsInteger()) {
-      rv.emplace_back(T(ts, sts, val.GetInteger()));
-    } else if (val.IsFloat()) {
-      rv.emplace_back(T(ts, sts, val.GetFloat()));
-    } else if (val.IsDouble()) {
-      rv.emplace_back(T(ts, sts, val.GetDouble()));
-    }
-  }
-  subscriber->pollStorage.reset();
-  return rv;
-}
-
-template <typename T>
-static std::vector<T> ReadQueueNumberArray(SubscriberData* subscriber) {
-  if (!subscriber) {
-    return {};
-  }
-  std::vector<T> rv;
-  rv.reserve(subscriber->pollStorage.size());
-  for (auto&& val : subscriber->pollStorage) {
-    auto ts = val.time();
-    auto sts = val.server_time();
-    if (val.IsIntegerArray()) {
-      auto arr = val.GetIntegerArray();
-      rv.emplace_back(T{ts, sts, {arr.begin(), arr.end()}});
-    } else if (val.IsFloatArray()) {
-      auto arr = val.GetFloatArray();
-      rv.emplace_back(T{ts, sts, {arr.begin(), arr.end()}});
-    } else if (val.IsDoubleArray()) {
-      auto arr = val.GetDoubleArray();
-      rv.emplace_back(T{ts, sts, {arr.begin(), arr.end()}});
-    }
-  }
-  subscriber->pollStorage.reset();
-  return rv;
-}
-
-#define READ_QUEUE_NUMBER(Name)                                               \
-  std::vector<Timestamped##Name> LocalStorage::ReadQueue##Name(               \
-      NT_Handle subentry) {                                                   \
-    std::scoped_lock lock{m_mutex};                                           \
-    return ReadQueueNumber<Timestamped##Name>(m_impl->GetSubEntry(subentry)); \
-  }                                                                           \
-                                                                              \
-  std::vector<Timestamped##Name##Array> LocalStorage::ReadQueue##Name##Array( \
-      NT_Handle subentry) {                                                   \
-    std::scoped_lock lock{m_mutex};                                           \
-    return ReadQueueNumberArray<Timestamped##Name##Array>(                    \
-        m_impl->GetSubEntry(subentry));                                       \
-  }
-
-READ_QUEUE_NUMBER(Integer)
-READ_QUEUE_NUMBER(Float)
-READ_QUEUE_NUMBER(Double)
-
 Value LocalStorage::GetEntryValue(NT_Handle subentryHandle) {
   std::scoped_lock lock{m_mutex};
-  if (auto subscriber = m_impl->GetSubEntry(subentryHandle)) {
+  if (auto subscriber = m_impl.GetSubEntry(subentryHandle)) {
     if (subscriber->config.type == NT_UNASSIGNED ||
         !subscriber->topic->lastValue ||
         subscriber->config.type == subscriber->topic->lastValue.type()) {
@@ -2120,22 +1347,6 @@
   return {};
 }
 
-void LocalStorage::SetEntryFlags(NT_Entry entryHandle, unsigned int flags) {
-  std::scoped_lock lock{m_mutex};
-  if (auto entry = m_impl->m_entries.Get(entryHandle)) {
-    m_impl->SetFlags(entry->subscriber->topic, flags);
-  }
-}
-
-unsigned int LocalStorage::GetEntryFlags(NT_Entry entryHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto entry = m_impl->m_entries.Get(entryHandle)) {
-    return entry->subscriber->topic->flags;
-  } else {
-    return 0;
-  }
-}
-
 NT_Entry LocalStorage::GetEntry(std::string_view name) {
   if (name.empty()) {
     return {};
@@ -2144,72 +1355,87 @@
   std::scoped_lock lock{m_mutex};
 
   // Get the topic data
-  auto* topic = m_impl->GetOrCreateTopic(name);
+  auto* topic = m_impl.GetOrCreateTopic(name);
 
   if (topic->entry == 0) {
     if (topic->localSubscribers.size() >= kMaxSubscribers) {
       WPI_ERROR(
-          m_impl->m_logger,
+          m_impl.m_logger,
           "reached maximum number of subscribers to '{}', not creating entry",
           topic->name);
       return 0;
     }
 
     // Create subscriber
-    auto* subscriber = m_impl->AddLocalSubscriber(topic, {});
+    auto* subscriber = m_impl.AddLocalSubscriber(topic, {});
 
     // Create entry
-    topic->entry = m_impl->AddEntry(subscriber)->handle;
+    topic->entry = m_impl.AddEntry(subscriber)->handle;
   }
 
   return topic->entry;
 }
 
-std::string LocalStorage::GetEntryName(NT_Handle subentryHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto subscriber = m_impl->GetSubEntry(subentryHandle)) {
-    return subscriber->topic->name;
-  } else {
-    return {};
-  }
-}
-
-NT_Type LocalStorage::GetEntryType(NT_Handle subentryHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto subscriber = m_impl->GetSubEntry(subentryHandle)) {
-    return subscriber->topic->type;
-  } else {
-    return {};
-  }
-}
-
-int64_t LocalStorage::GetEntryLastChange(NT_Handle subentryHandle) {
-  std::scoped_lock lock{m_mutex};
-  if (auto subscriber = m_impl->GetSubEntry(subentryHandle)) {
-    return subscriber->topic->lastValue.time();
-  } else {
-    return 0;
-  }
-}
-
-void LocalStorage::AddListener(NT_Listener listener,
+void LocalStorage::AddListener(NT_Listener listenerHandle,
                                std::span<const std::string_view> prefixes,
                                unsigned int mask) {
   mask &= (NT_EVENT_TOPIC | NT_EVENT_VALUE_ALL | NT_EVENT_IMMEDIATE);
   std::scoped_lock lock{m_mutex};
-  m_impl->AddListener(listener, prefixes, mask);
+  if (m_impl.m_multiSubscribers.size() >= kMaxMultiSubscribers) {
+    WPI_ERROR(
+        m_impl.m_logger,
+        "reached maximum number of multi-subscribers, not adding listener");
+    return;
+  }
+  // subscribe to make sure topic updates are received
+  auto sub = m_impl.AddMultiSubscriber(
+      prefixes, {.topicsOnly = (mask & NT_EVENT_VALUE_ALL) == 0});
+  m_impl.AddListenerImpl(listenerHandle, sub, mask, true);
 }
 
-void LocalStorage::AddListener(NT_Listener listener, NT_Handle handle,
+void LocalStorage::AddListener(NT_Listener listenerHandle, NT_Handle handle,
                                unsigned int mask) {
   mask &= (NT_EVENT_TOPIC | NT_EVENT_VALUE_ALL | NT_EVENT_IMMEDIATE);
   std::scoped_lock lock{m_mutex};
-  m_impl->AddListener(listener, handle, mask);
+  if (auto topic = m_impl.m_topics.Get(handle)) {
+    m_impl.AddListenerImpl(listenerHandle, topic, mask);
+  } else if (auto sub = m_impl.m_multiSubscribers.Get(handle)) {
+    m_impl.AddListenerImpl(listenerHandle, sub, mask, false);
+  } else if (auto sub = m_impl.m_subscribers.Get(handle)) {
+    m_impl.AddListenerImpl(listenerHandle, sub, mask, sub->handle, false);
+  } else if (auto entry = m_impl.m_entries.Get(handle)) {
+    m_impl.AddListenerImpl(listenerHandle, entry->subscriber, mask,
+                           entry->handle, false);
+  }
 }
 
-void LocalStorage::RemoveListener(NT_Listener listener, unsigned int mask) {
+void LocalStorage::RemoveListener(NT_Listener listenerHandle,
+                                  unsigned int mask) {
   std::scoped_lock lock{m_mutex};
-  m_impl->RemoveListener(listener, mask);
+  auto listenerIt = m_impl.m_listeners.find(listenerHandle);
+  if (listenerIt == m_impl.m_listeners.end()) {
+    return;
+  }
+  auto listener = std::move(listenerIt->getSecond());
+  m_impl.m_listeners.erase(listenerIt);
+  if (!listener) {
+    return;
+  }
+
+  m_impl.m_topicPrefixListeners.Remove(listener.get());
+  if (listener->subscriber) {
+    listener->subscriber->valueListeners.Remove(listenerHandle);
+    listener->subscriber->topic->listeners.Remove(listenerHandle);
+    if (listener->subscriberOwned) {
+      m_impl.RemoveLocalSubscriber(listener->subscriber->handle);
+    }
+  }
+  if (listener->multiSubscriber) {
+    listener->multiSubscriber->valueListeners.Remove(listenerHandle);
+    if (listener->subscriberOwned) {
+      m_impl.RemoveMultiSubscriber(listener->multiSubscriber->handle);
+    }
+  }
 }
 
 NT_DataLogger LocalStorage::StartDataLog(wpi::log::DataLog& log,
@@ -2217,24 +1443,23 @@
                                          std::string_view logPrefix) {
   std::scoped_lock lock{m_mutex};
   auto datalogger =
-      m_impl->m_dataloggers.Add(m_impl->m_inst, log, prefix, logPrefix);
+      m_impl.m_dataloggers.Add(m_impl.m_inst, log, prefix, logPrefix);
 
   // start logging any matching topics
   auto now = nt::Now();
-  for (auto&& topic : m_impl->m_topics) {
+  for (auto&& topic : m_impl.m_topics) {
     if (!wpi::starts_with(topic->name, prefix) ||
         topic->type == NT_UNASSIGNED || topic->typeStr.empty()) {
       continue;
     }
     topic->datalogs.emplace_back(log, datalogger->Start(topic.get(), now),
                                  datalogger->handle);
+    topic->datalogType = topic->type;
 
     // log current value, if any
-    if (!topic->lastValue) {
-      continue;
+    if (topic->lastValue) {
+      topic->datalogs.back().Append(topic->lastValue);
     }
-    topic->datalogType = topic->type;
-    topic->datalogs.back().Append(topic->lastValue);
   }
 
   return datalogger->handle;
@@ -2242,10 +1467,10 @@
 
 void LocalStorage::StopDataLog(NT_DataLogger logger) {
   std::scoped_lock lock{m_mutex};
-  if (auto datalogger = m_impl->m_dataloggers.Remove(logger)) {
+  if (auto datalogger = m_impl.m_dataloggers.Remove(logger)) {
     // finish any active entries
     auto now = Now();
-    for (auto&& topic : m_impl->m_topics) {
+    for (auto&& topic : m_impl.m_topics) {
       auto it =
           std::find_if(topic->datalogs.begin(), topic->datalogs.end(),
                        [&](const auto& elem) { return elem.logger == logger; });
@@ -2257,8 +1482,51 @@
   }
 }
 
+bool LocalStorage::HasSchema(std::string_view name) {
+  std::scoped_lock lock{m_mutex};
+  wpi::SmallString<128> fullName{"/.schema/"};
+  fullName += name;
+  auto it = m_impl.m_schemas.find(fullName);
+  return it != m_impl.m_schemas.end();
+}
+
+void LocalStorage::AddSchema(std::string_view name, std::string_view type,
+                             std::span<const uint8_t> schema) {
+  std::scoped_lock lock{m_mutex};
+  wpi::SmallString<128> fullName{"/.schema/"};
+  fullName += name;
+  auto& pubHandle = m_impl.m_schemas[fullName];
+  if (pubHandle != 0) {
+    return;
+  }
+
+  auto topic = m_impl.GetOrCreateTopic(fullName);
+
+  if (topic->localPublishers.size() >= kMaxPublishers) {
+    WPI_ERROR(m_impl.m_logger,
+              "reached maximum number of publishers to '{}', not publishing",
+              topic->name);
+    return;
+  }
+
+  pubHandle = m_impl
+                  .AddLocalPublisher(topic, {{"retained", true}},
+                                     PubSubConfig{NT_RAW, type, {}})
+                  ->handle;
+
+  m_impl.SetDefaultEntryValue(pubHandle, Value::MakeRaw(schema));
+}
+
 void LocalStorage::Reset() {
   std::scoped_lock lock{m_mutex};
-  m_impl = std::make_unique<Impl>(m_impl->m_inst, m_impl->m_listenerStorage,
-                                  m_impl->m_logger);
+  m_impl.m_network = nullptr;
+  m_impl.m_topics.clear();
+  m_impl.m_publishers.clear();
+  m_impl.m_subscribers.clear();
+  m_impl.m_entries.clear();
+  m_impl.m_multiSubscribers.clear();
+  m_impl.m_dataloggers.clear();
+  m_impl.m_nameTopics.clear();
+  m_impl.m_listeners.clear();
+  m_impl.m_topicPrefixListeners.clear();
 }
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/LocalStorage.h b/third_party/allwpilib/ntcore/src/main/native/cpp/LocalStorage.h
index a93adb0..af2b4de 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/LocalStorage.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/LocalStorage.h
@@ -14,8 +14,18 @@
 #include <utility>
 #include <vector>
 
+#include <wpi/DenseMap.h>
+#include <wpi/StringMap.h>
+#include <wpi/Synchronization.h>
+#include <wpi/json.h>
 #include <wpi/mutex.h>
 
+#include "Handle.h"
+#include "HandleMap.h"
+#include "PubSubOptions.h"
+#include "Types_internal.h"
+#include "ValueCircularBuffer.h"
+#include "VectorSet.h"
 #include "net/NetworkInterface.h"
 #include "ntcore_cpp.h"
 
@@ -29,8 +39,8 @@
 
 class LocalStorage final : public net::ILocalStorage {
  public:
-  LocalStorage(int inst, IListenerStorage& listenerStorage,
-               wpi::Logger& logger);
+  LocalStorage(int inst, IListenerStorage& listenerStorage, wpi::Logger& logger)
+      : m_impl{inst, listenerStorage, logger} {}
   LocalStorage(const LocalStorage&) = delete;
   LocalStorage& operator=(const LocalStorage&) = delete;
   ~LocalStorage() final;
@@ -59,47 +69,129 @@
   std::vector<TopicInfo> GetTopicInfo(std::string_view prefix,
                                       std::span<const std::string_view> types);
 
-  NT_Topic GetTopic(std::string_view name);
+  NT_Topic GetTopic(std::string_view name) {
+    if (name.empty()) {
+      return {};
+    }
+    std::scoped_lock lock{m_mutex};
+    return m_impl.GetOrCreateTopic(name)->handle;
+  }
 
-  std::string GetTopicName(NT_Topic topic);
+  std::string GetTopicName(NT_Topic topicHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+      return topic->name;
+    } else {
+      return {};
+    }
+  }
 
-  NT_Type GetTopicType(NT_Topic topic);
+  NT_Type GetTopicType(NT_Topic topicHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+      return topic->type;
+    } else {
+      return {};
+    }
+  }
 
-  std::string GetTopicTypeString(NT_Topic topic);
+  std::string GetTopicTypeString(NT_Topic topicHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+      return topic->typeStr;
+    } else {
+      return {};
+    }
+  }
 
-  void SetTopicPersistent(NT_Topic topic, bool value);
+  void SetTopicPersistent(NT_Topic topicHandle, bool value) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+      m_impl.SetPersistent(topic, value);
+    }
+  }
 
-  bool GetTopicPersistent(NT_Topic topic);
+  bool GetTopicPersistent(NT_Topic topicHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+      return (topic->flags & NT_PERSISTENT) != 0;
+    } else {
+      return false;
+    }
+  }
 
-  void SetTopicRetained(NT_Topic topic, bool value);
+  void SetTopicRetained(NT_Topic topicHandle, bool value) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+      m_impl.SetRetained(topic, value);
+    }
+  }
 
-  bool GetTopicRetained(NT_Topic topic);
+  bool GetTopicRetained(NT_Topic topicHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+      return (topic->flags & NT_RETAINED) != 0;
+    } else {
+      return false;
+    }
+  }
 
-  bool GetTopicExists(NT_Handle handle);
+  bool GetTopicExists(NT_Handle handle) {
+    std::scoped_lock lock{m_mutex};
+    TopicData* topic = m_impl.GetTopic(handle);
+    return topic && topic->Exists();
+  }
 
-  wpi::json GetTopicProperty(NT_Topic topic, std::string_view name);
+  wpi::json GetTopicProperty(NT_Topic topicHandle, std::string_view name) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+      return topic->properties.value(name, wpi::json{});
+    } else {
+      return {};
+    }
+  }
 
   void SetTopicProperty(NT_Topic topic, std::string_view name,
                         const wpi::json& value);
 
   void DeleteTopicProperty(NT_Topic topic, std::string_view name);
 
-  wpi::json GetTopicProperties(NT_Topic topic);
+  wpi::json GetTopicProperties(NT_Topic topicHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+      return topic->properties;
+    } else {
+      return wpi::json::object();
+    }
+  }
 
   bool SetTopicProperties(NT_Topic topic, const wpi::json& update);
 
-  TopicInfo GetTopicInfo(NT_Topic topic);
+  TopicInfo GetTopicInfo(NT_Topic topicHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.m_topics.Get(topicHandle)) {
+      return topic->GetTopicInfo();
+    } else {
+      return {};
+    }
+  }
 
   NT_Subscriber Subscribe(NT_Topic topic, NT_Type type,
                           std::string_view typeStr,
                           const PubSubOptions& options);
 
-  void Unsubscribe(NT_Subscriber sub);
+  void Unsubscribe(NT_Subscriber subHandle) {
+    std::scoped_lock lock{m_mutex};
+    m_impl.RemoveSubEntry(subHandle);
+  }
 
   NT_MultiSubscriber SubscribeMultiple(
       std::span<const std::string_view> prefixes, const PubSubOptions& options);
 
-  void UnsubscribeMultiple(NT_MultiSubscriber subHandle);
+  void UnsubscribeMultiple(NT_MultiSubscriber subHandle) {
+    std::scoped_lock lock{m_mutex};
+    m_impl.RemoveMultiSubscriber(subHandle);
+  }
 
   NT_Publisher Publish(NT_Topic topic, NT_Type type, std::string_view typeStr,
                        const wpi::json& properties,
@@ -110,84 +202,106 @@
   NT_Entry GetEntry(NT_Topic topic, NT_Type type, std::string_view typeStr,
                     const PubSubOptions& options);
 
-  void ReleaseEntry(NT_Entry entry);
+  void ReleaseEntry(NT_Entry entryHandle) {
+    std::scoped_lock lock{m_mutex};
+    m_impl.RemoveSubEntry(entryHandle);
+  }
 
   void Release(NT_Handle pubsubentry);
 
-  NT_Topic GetTopicFromHandle(NT_Handle pubsubentry);
+  NT_Topic GetTopicFromHandle(NT_Handle pubsubentryHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto topic = m_impl.GetTopic(pubsubentryHandle)) {
+      return topic->handle;
+    } else {
+      return {};
+    }
+  }
 
-  bool SetEntryValue(NT_Handle pubentry, const Value& value);
+  bool SetEntryValue(NT_Handle pubentryHandle, const Value& value) {
+    std::scoped_lock lock{m_mutex};
+    return m_impl.SetEntryValue(pubentryHandle, value);
+  }
 
-  bool SetDefaultEntryValue(NT_Handle pubsubentry, const Value& value);
+  bool SetDefaultEntryValue(NT_Handle pubsubentryHandle, const Value& value) {
+    std::scoped_lock lock{m_mutex};
+    return m_impl.SetDefaultEntryValue(pubsubentryHandle, value);
+  }
 
-  TimestampedBoolean GetAtomicBoolean(NT_Handle subentry, bool defaultValue);
-  TimestampedInteger GetAtomicInteger(NT_Handle subentry, int64_t defaultValue);
-  TimestampedFloat GetAtomicFloat(NT_Handle subentry, float defaultValue);
-  TimestampedDouble GetAtomicDouble(NT_Handle subentry, double defaultValue);
-  TimestampedString GetAtomicString(NT_Handle subentry,
-                                    std::string_view defaultValue);
-  TimestampedRaw GetAtomicRaw(NT_Handle subentry,
-                              std::span<const uint8_t> defaultValue);
-  TimestampedBooleanArray GetAtomicBooleanArray(
-      NT_Handle subentry, std::span<const int> defaultValue);
-  TimestampedIntegerArray GetAtomicIntegerArray(
-      NT_Handle subentry, std::span<const int64_t> defaultValue);
-  TimestampedFloatArray GetAtomicFloatArray(
-      NT_Handle subentry, std::span<const float> defaultValue);
-  TimestampedDoubleArray GetAtomicDoubleArray(
-      NT_Handle subentry, std::span<const double> defaultValue);
-  TimestampedStringArray GetAtomicStringArray(
-      NT_Handle subentry, std::span<const std::string> defaultValue);
+  template <ValidType T>
+  Timestamped<typename TypeInfo<T>::Value> GetAtomic(
+      NT_Handle subentry, typename TypeInfo<T>::View defaultValue);
 
-  TimestampedStringView GetAtomicString(NT_Handle subentry,
-                                        wpi::SmallVectorImpl<char>& buf,
-                                        std::string_view defaultValue);
-  TimestampedRawView GetAtomicRaw(NT_Handle subentry,
-                                  wpi::SmallVectorImpl<uint8_t>& buf,
-                                  std::span<const uint8_t> defaultValue);
-  TimestampedBooleanArrayView GetAtomicBooleanArray(
-      NT_Handle subentry, wpi::SmallVectorImpl<int>& buf,
-      std::span<const int> defaultValue);
-  TimestampedIntegerArrayView GetAtomicIntegerArray(
-      NT_Handle subentry, wpi::SmallVectorImpl<int64_t>& buf,
-      std::span<const int64_t> defaultValue);
-  TimestampedFloatArrayView GetAtomicFloatArray(
-      NT_Handle subentry, wpi::SmallVectorImpl<float>& buf,
-      std::span<const float> defaultValue);
-  TimestampedDoubleArrayView GetAtomicDoubleArray(
-      NT_Handle subentry, wpi::SmallVectorImpl<double>& buf,
-      std::span<const double> defaultValue);
+  template <SmallArrayType T>
+  Timestamped<typename TypeInfo<T>::SmallRet> GetAtomic(
+      NT_Handle subentry,
+      wpi::SmallVectorImpl<typename TypeInfo<T>::SmallElem>& buf,
+      typename TypeInfo<T>::View defaultValue);
 
-  std::vector<Value> ReadQueueValue(NT_Handle subentry);
+  std::vector<Value> ReadQueueValue(NT_Handle subentry) {
+    std::scoped_lock lock{m_mutex};
+    auto subscriber = m_impl.GetSubEntry(subentry);
+    if (!subscriber) {
+      return {};
+    }
+    return subscriber->pollStorage.ReadValue();
+  }
 
-  std::vector<TimestampedBoolean> ReadQueueBoolean(NT_Handle subentry);
-  std::vector<TimestampedInteger> ReadQueueInteger(NT_Handle subentry);
-  std::vector<TimestampedFloat> ReadQueueFloat(NT_Handle subentry);
-  std::vector<TimestampedDouble> ReadQueueDouble(NT_Handle subentry);
-  std::vector<TimestampedString> ReadQueueString(NT_Handle subentry);
-  std::vector<TimestampedRaw> ReadQueueRaw(NT_Handle subentry);
-  std::vector<TimestampedBooleanArray> ReadQueueBooleanArray(
+  template <ValidType T>
+  std::vector<Timestamped<typename TypeInfo<T>::Value>> ReadQueue(
       NT_Handle subentry);
-  std::vector<TimestampedIntegerArray> ReadQueueIntegerArray(
-      NT_Handle subentry);
-  std::vector<TimestampedFloatArray> ReadQueueFloatArray(NT_Handle subentry);
-  std::vector<TimestampedDoubleArray> ReadQueueDoubleArray(NT_Handle subentry);
-  std::vector<TimestampedStringArray> ReadQueueStringArray(NT_Handle subentry);
 
   //
   // Backwards compatible user functions
   //
 
   Value GetEntryValue(NT_Handle subentry);
-  void SetEntryFlags(NT_Entry entry, unsigned int flags);
-  unsigned int GetEntryFlags(NT_Entry entry);
+
+  void SetEntryFlags(NT_Entry entryHandle, unsigned int flags) {
+    std::scoped_lock lock{m_mutex};
+    if (auto entry = m_impl.m_entries.Get(entryHandle)) {
+      m_impl.SetFlags(entry->subscriber->topic, flags);
+    }
+  }
+
+  unsigned int GetEntryFlags(NT_Entry entryHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto entry = m_impl.m_entries.Get(entryHandle)) {
+      return entry->subscriber->topic->flags;
+    } else {
+      return 0;
+    }
+  }
 
   // Index-only
   NT_Entry GetEntry(std::string_view name);
 
-  std::string GetEntryName(NT_Entry entry);
-  NT_Type GetEntryType(NT_Entry entry);
-  int64_t GetEntryLastChange(NT_Entry entry);
+  std::string GetEntryName(NT_Entry subentryHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto subscriber = m_impl.GetSubEntry(subentryHandle)) {
+      return subscriber->topic->name;
+    } else {
+      return {};
+    }
+  }
+
+  NT_Type GetEntryType(NT_Entry subentryHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto subscriber = m_impl.GetSubEntry(subentryHandle)) {
+      return subscriber->topic->type;
+    } else {
+      return {};
+    }
+  }
+
+  int64_t GetEntryLastChange(NT_Entry subentryHandle) {
+    std::scoped_lock lock{m_mutex};
+    if (auto subscriber = m_impl.GetSubEntry(subentryHandle)) {
+      return subscriber->topic->lastValue.time();
+    } else {
+      return 0;
+    }
+  }
 
   //
   // Listener functions
@@ -207,13 +321,365 @@
                              std::string_view logPrefix);
   void StopDataLog(NT_DataLogger logger);
 
+  //
+  // Schema functions
+  //
+  bool HasSchema(std::string_view name);
+  void AddSchema(std::string_view name, std::string_view type,
+                 std::span<const uint8_t> schema);
+
   void Reset();
 
  private:
-  class Impl;
-  std::unique_ptr<Impl> m_impl;
+  static constexpr bool IsSpecial(std::string_view name) {
+    return name.empty() ? false : name.front() == '$';
+  }
+
+  struct EntryData;
+  struct PublisherData;
+  struct SubscriberData;
+  struct MultiSubscriberData;
+
+  struct DataLoggerEntry {
+    DataLoggerEntry(wpi::log::DataLog& log, int entry, NT_DataLogger logger)
+        : log{&log}, entry{entry}, logger{logger} {}
+
+    static std::string MakeMetadata(std::string_view properties);
+
+    void Append(const Value& v);
+
+    wpi::log::DataLog* log;
+    int entry;
+    NT_DataLogger logger;
+  };
+
+  struct TopicData {
+    static constexpr auto kType = Handle::kTopic;
+
+    TopicData(NT_Topic handle, std::string_view name)
+        : handle{handle}, name{name}, special{IsSpecial(name)} {}
+
+    bool Exists() const { return onNetwork || !localPublishers.empty(); }
+
+    TopicInfo GetTopicInfo() const;
+
+    // invariants
+    wpi::SignalObject<NT_Topic> handle;
+    std::string name;
+    bool special;
+
+    Value lastValue;  // also stores timestamp
+    Value lastValueNetwork;
+    NT_Type type{NT_UNASSIGNED};
+    std::string typeStr;
+    unsigned int flags{0};            // for NT3 APIs
+    std::string propertiesStr{"{}"};  // cached string for GetTopicInfo() et al
+    wpi::json properties = wpi::json::object();
+    NT_Entry entry{0};  // cached entry for GetEntry()
+
+    bool onNetwork{false};  // true if there are any remote publishers
+    bool lastValueFromNetwork{false};
+
+    wpi::SmallVector<DataLoggerEntry, 1> datalogs;
+    NT_Type datalogType{NT_UNASSIGNED};
+
+    VectorSet<PublisherData*> localPublishers;
+    VectorSet<SubscriberData*> localSubscribers;
+    VectorSet<MultiSubscriberData*> multiSubscribers;
+    VectorSet<EntryData*> entries;
+    VectorSet<NT_Listener> listeners;
+  };
+
+  struct PubSubConfig : public PubSubOptionsImpl {
+    PubSubConfig() = default;
+    PubSubConfig(NT_Type type, std::string_view typeStr,
+                 const PubSubOptions& options)
+        : PubSubOptionsImpl{options}, type{type}, typeStr{typeStr} {
+      prefixMatch = false;
+    }
+
+    NT_Type type{NT_UNASSIGNED};
+    std::string typeStr;
+  };
+
+  struct PublisherData {
+    static constexpr auto kType = Handle::kPublisher;
+
+    PublisherData(NT_Publisher handle, TopicData* topic, PubSubConfig config)
+        : handle{handle}, topic{topic}, config{std::move(config)} {}
+
+    void UpdateActive() {
+      active = config.type == topic->type && config.typeStr == topic->typeStr;
+    }
+
+    // invariants
+    wpi::SignalObject<NT_Publisher> handle;
+    TopicData* topic;
+    PubSubConfig config;
+
+    // whether or not the publisher should actually publish values
+    bool active{false};
+  };
+
+  struct SubscriberData {
+    static constexpr auto kType = Handle::kSubscriber;
+
+    SubscriberData(NT_Subscriber handle, TopicData* topic, PubSubConfig config)
+        : handle{handle},
+          topic{topic},
+          config{std::move(config)},
+          pollStorage{config.pollStorage} {}
+
+    void UpdateActive() {
+      // for subscribers, unassigned is a wildcard
+      // also allow numerically compatible subscribers
+      active =
+          config.type == NT_UNASSIGNED ||
+          (config.type == topic->type && config.typeStr == topic->typeStr) ||
+          IsNumericCompatible(config.type, topic->type);
+    }
+
+    // invariants
+    wpi::SignalObject<NT_Subscriber> handle;
+    TopicData* topic;
+    PubSubConfig config;
+
+    // whether or not the subscriber should actually receive values
+    bool active{false};
+
+    // polling storage
+    ValueCircularBuffer pollStorage;
+
+    // value listeners
+    VectorSet<NT_Listener> valueListeners;
+  };
+
+  struct EntryData {
+    static constexpr auto kType = Handle::kEntry;
+
+    EntryData(NT_Entry handle, SubscriberData* subscriber)
+        : handle{handle}, topic{subscriber->topic}, subscriber{subscriber} {}
+
+    // invariants
+    wpi::SignalObject<NT_Entry> handle;
+    TopicData* topic;
+    SubscriberData* subscriber;
+
+    // the publisher (created on demand)
+    PublisherData* publisher{nullptr};
+  };
+
+  struct MultiSubscriberData {
+    static constexpr auto kType = Handle::kMultiSubscriber;
+
+    MultiSubscriberData(NT_MultiSubscriber handle,
+                        std::span<const std::string_view> prefixes,
+                        const PubSubOptionsImpl& options)
+        : handle{handle}, options{options} {
+      this->options.prefixMatch = true;
+      this->prefixes.reserve(prefixes.size());
+      for (auto&& prefix : prefixes) {
+        this->prefixes.emplace_back(prefix);
+      }
+    }
+
+    bool Matches(std::string_view name, bool special);
+
+    // invariants
+    wpi::SignalObject<NT_MultiSubscriber> handle;
+    std::vector<std::string> prefixes;
+    PubSubOptionsImpl options;
+
+    // value listeners
+    VectorSet<NT_Listener> valueListeners;
+  };
+
+  struct ListenerData {
+    ListenerData(NT_Listener handle, SubscriberData* subscriber,
+                 unsigned int eventMask, bool subscriberOwned)
+        : handle{handle},
+          eventMask{eventMask},
+          subscriber{subscriber},
+          subscriberOwned{subscriberOwned} {}
+    ListenerData(NT_Listener handle, MultiSubscriberData* subscriber,
+                 unsigned int eventMask, bool subscriberOwned)
+        : handle{handle},
+          eventMask{eventMask},
+          multiSubscriber{subscriber},
+          subscriberOwned{subscriberOwned} {}
+
+    NT_Listener handle;
+    unsigned int eventMask;
+    SubscriberData* subscriber{nullptr};
+    MultiSubscriberData* multiSubscriber{nullptr};
+    bool subscriberOwned;
+  };
+
+  struct DataLoggerData {
+    static constexpr auto kType = Handle::kDataLogger;
+
+    DataLoggerData(NT_DataLogger handle, wpi::log::DataLog& log,
+                   std::string_view prefix, std::string_view logPrefix)
+        : handle{handle}, log{log}, prefix{prefix}, logPrefix{logPrefix} {}
+
+    int Start(TopicData* topic, int64_t time);
+
+    NT_DataLogger handle;
+    wpi::log::DataLog& log;
+    std::string prefix;
+    std::string logPrefix;
+  };
+
+  // inner struct to protect against accidentally deadlocking on the mutex
+  struct Impl {
+    Impl(int inst, IListenerStorage& listenerStorage, wpi::Logger& logger);
+
+    int m_inst;
+    IListenerStorage& m_listenerStorage;
+    wpi::Logger& m_logger;
+    net::NetworkInterface* m_network{nullptr};
+
+    // handle mappings
+    HandleMap<TopicData, 16> m_topics;
+    HandleMap<PublisherData, 16> m_publishers;
+    HandleMap<SubscriberData, 16> m_subscribers;
+    HandleMap<EntryData, 16> m_entries;
+    HandleMap<MultiSubscriberData, 16> m_multiSubscribers;
+    HandleMap<DataLoggerData, 16> m_dataloggers;
+
+    // name mappings
+    wpi::StringMap<TopicData*> m_nameTopics;
+
+    // listeners
+    wpi::DenseMap<NT_Listener, std::unique_ptr<ListenerData>> m_listeners;
+
+    // string-based listeners
+    VectorSet<ListenerData*> m_topicPrefixListeners;
+
+    // schema publishers
+    wpi::StringMap<NT_Publisher> m_schemas;
+
+    // topic functions
+    void NotifyTopic(TopicData* topic, unsigned int eventFlags);
+
+    void CheckReset(TopicData* topic);
+
+    bool SetValue(TopicData* topic, const Value& value, unsigned int eventFlags,
+                  bool isDuplicate, bool suppressIfDuplicate,
+                  const PublisherData* publisher);
+    void NotifyValue(TopicData* topic, unsigned int eventFlags,
+                     bool isDuplicate, const PublisherData* publisher);
+
+    void SetFlags(TopicData* topic, unsigned int flags);
+    void SetPersistent(TopicData* topic, bool value);
+    void SetRetained(TopicData* topic, bool value);
+    void SetProperties(TopicData* topic, const wpi::json& update,
+                       bool sendNetwork);
+    void PropertiesUpdated(TopicData* topic, const wpi::json& update,
+                           unsigned int eventFlags, bool sendNetwork,
+                           bool updateFlags = true);
+
+    void RefreshPubSubActive(TopicData* topic, bool warnOnSubMismatch);
+
+    void NetworkAnnounce(TopicData* topic, std::string_view typeStr,
+                         const wpi::json& properties, NT_Publisher pubHandle);
+    void RemoveNetworkPublisher(TopicData* topic);
+    void NetworkPropertiesUpdate(TopicData* topic, const wpi::json& update,
+                                 bool ack);
+    void StartNetwork(net::NetworkInterface* network);
+
+    PublisherData* AddLocalPublisher(TopicData* topic,
+                                     const wpi::json& properties,
+                                     const PubSubConfig& options);
+    std::unique_ptr<PublisherData> RemoveLocalPublisher(NT_Publisher pubHandle);
+
+    SubscriberData* AddLocalSubscriber(TopicData* topic,
+                                       const PubSubConfig& options);
+    std::unique_ptr<SubscriberData> RemoveLocalSubscriber(
+        NT_Subscriber subHandle);
+
+    EntryData* AddEntry(SubscriberData* subscriber);
+    std::unique_ptr<EntryData> RemoveEntry(NT_Entry entryHandle);
+
+    MultiSubscriberData* AddMultiSubscriber(
+        std::span<const std::string_view> prefixes,
+        const PubSubOptions& options);
+    std::unique_ptr<MultiSubscriberData> RemoveMultiSubscriber(
+        NT_MultiSubscriber subHandle);
+
+    void AddListenerImpl(NT_Listener listenerHandle, TopicData* topic,
+                         unsigned int eventMask);
+    void AddListenerImpl(NT_Listener listenerHandle, SubscriberData* subscriber,
+                         unsigned int eventMask, NT_Handle subentryHandle,
+                         bool subscriberOwned);
+    void AddListenerImpl(NT_Listener listenerHandle,
+                         MultiSubscriberData* subscriber,
+                         unsigned int eventMask, bool subscriberOwned);
+    void AddListenerImpl(NT_Listener listenerHandle,
+                         std::span<const std::string_view> prefixes,
+                         unsigned int eventMask);
+
+    TopicData* GetOrCreateTopic(std::string_view name);
+    TopicData* GetTopic(NT_Handle handle);
+    SubscriberData* GetSubEntry(NT_Handle subentryHandle);
+    PublisherData* PublishEntry(EntryData* entry, NT_Type type);
+
+    Value* GetSubEntryValue(NT_Handle subentryHandle) {
+      if (auto subscriber = GetSubEntry(subentryHandle)) {
+        return &subscriber->topic->lastValue;
+      } else {
+        return nullptr;
+      }
+    }
+
+    bool PublishLocalValue(PublisherData* publisher, const Value& value,
+                           bool force = false);
+
+    bool SetEntryValue(NT_Handle pubentryHandle, const Value& value);
+    bool SetDefaultEntryValue(NT_Handle pubsubentryHandle, const Value& value);
+
+    void RemoveSubEntry(NT_Handle subentryHandle);
+  };
 
   wpi::mutex m_mutex;
+  Impl m_impl;
 };
 
+template <ValidType T>
+Timestamped<typename TypeInfo<T>::Value> LocalStorage::GetAtomic(
+    NT_Handle subentry, typename TypeInfo<T>::View defaultValue) {
+  std::scoped_lock lock{m_mutex};
+  Value* value = m_impl.GetSubEntryValue(subentry);
+  if (value && (IsNumericConvertibleTo<T>(*value) || IsType<T>(*value))) {
+    return GetTimestamped<T, true>(*value);
+  } else {
+    return {0, 0, CopyValue<T>(defaultValue)};
+  }
+}
+
+template <SmallArrayType T>
+Timestamped<typename TypeInfo<T>::SmallRet> LocalStorage::GetAtomic(
+    NT_Handle subentry,
+    wpi::SmallVectorImpl<typename TypeInfo<T>::SmallElem>& buf,
+    typename TypeInfo<T>::View defaultValue) {
+  std::scoped_lock lock{m_mutex};
+  Value* value = m_impl.GetSubEntryValue(subentry);
+  if (value && (IsNumericConvertibleTo<T>(*value) || IsType<T>(*value))) {
+    return GetTimestamped<T, true>(*value, buf);
+  } else {
+    return {0, 0, CopyValue<T>(defaultValue, buf)};
+  }
+}
+
+template <ValidType T>
+std::vector<Timestamped<typename TypeInfo<T>::Value>> LocalStorage::ReadQueue(
+    NT_Handle subentry) {
+  std::scoped_lock lock{m_mutex};
+  auto subscriber = m_impl.GetSubEntry(subentry);
+  if (!subscriber) {
+    return {};
+  }
+  return subscriber->pollStorage.Read<T>();
+}
+
 }  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/Log.h b/third_party/allwpilib/ntcore/src/main/native/cpp/Log.h
index 7e052f9..ef9743b 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/Log.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/Log.h
@@ -9,10 +9,8 @@
 #define LOG(level, format, ...) \
   WPI_LOG(m_logger, level, format __VA_OPT__(, ) __VA_ARGS__)
 
-#undef ERROR
-#define ERROR(format, ...) \
-  WPI_ERROR(m_logger, format __VA_OPT__(, ) __VA_ARGS__)
-#define WARNING(format, ...) \
+#define ERR(format, ...) WPI_ERROR(m_logger, format __VA_OPT__(, ) __VA_ARGS__)
+#define WARN(format, ...) \
   WPI_WARNING(m_logger, format __VA_OPT__(, ) __VA_ARGS__)
 #define INFO(format, ...) WPI_INFO(m_logger, format __VA_OPT__(, ) __VA_ARGS__)
 
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkClient.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkClient.cpp
index a329fb9..7634be9 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkClient.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkClient.cpp
@@ -13,25 +13,13 @@
 #include <fmt/format.h>
 #include <wpi/SmallString.h>
 #include <wpi/StringExtras.h>
-#include <wpinet/DsClient.h>
-#include <wpinet/EventLoopRunner.h>
 #include <wpinet/HttpUtil.h>
-#include <wpinet/ParallelTcpConnector.h>
-#include <wpinet/WebSocket.h>
-#include <wpinet/uv/Async.h>
 #include <wpinet/uv/Loop.h>
 #include <wpinet/uv/Tcp.h>
-#include <wpinet/uv/Timer.h>
 #include <wpinet/uv/util.h>
 
 #include "IConnectionList.h"
 #include "Log.h"
-#include "net/ClientImpl.h"
-#include "net/Message.h"
-#include "net/NetworkLoopQueue.h"
-#include "net/WebSocketConnection.h"
-#include "net3/ClientImpl3.h"
-#include "net3/UvStreamConnection3.h"
 
 using namespace nt;
 namespace uv = wpi::uv;
@@ -41,94 +29,10 @@
 // use a larger max message size for websockets
 static constexpr size_t kMaxMessageSize = 2 * 1024 * 1024;
 
-namespace {
-
-class NCImpl {
- public:
-  NCImpl(int inst, std::string_view id, net::ILocalStorage& localStorage,
-         IConnectionList& connList, wpi::Logger& logger);
-  virtual ~NCImpl() = default;
-
-  // user-facing functions
-  void SetServers(std::span<const std::pair<std::string, unsigned int>> servers,
-                  unsigned int defaultPort);
-  void StartDSClient(unsigned int port);
-  void StopDSClient();
-
-  virtual void TcpConnected(uv::Tcp& tcp) = 0;
-  virtual void Disconnect(std::string_view reason);
-
-  // invariants
-  int m_inst;
-  net::ILocalStorage& m_localStorage;
-  IConnectionList& m_connList;
-  wpi::Logger& m_logger;
-  std::string m_id;
-
-  // used only from loop
-  std::shared_ptr<wpi::ParallelTcpConnector> m_parallelConnect;
-  std::shared_ptr<uv::Timer> m_readLocalTimer;
-  std::shared_ptr<uv::Timer> m_sendValuesTimer;
-  std::shared_ptr<uv::Async<>> m_flushLocal;
-  std::shared_ptr<uv::Async<>> m_flush;
-
-  std::vector<net::ClientMessage> m_localMsgs;
-
-  std::vector<std::pair<std::string, unsigned int>> m_servers;
-
-  std::pair<std::string, unsigned int> m_dsClientServer{"", 0};
-  std::shared_ptr<wpi::DsClient> m_dsClient;
-
-  // shared with user
-  std::atomic<uv::Async<>*> m_flushLocalAtomic{nullptr};
-  std::atomic<uv::Async<>*> m_flushAtomic{nullptr};
-
-  net::NetworkLoopQueue m_localQueue;
-
-  int m_connHandle = 0;
-
-  wpi::EventLoopRunner m_loopRunner;
-  uv::Loop& m_loop;
-};
-
-class NCImpl3 : public NCImpl {
- public:
-  NCImpl3(int inst, std::string_view id, net::ILocalStorage& localStorage,
-          IConnectionList& connList, wpi::Logger& logger);
-  ~NCImpl3() override;
-
-  void HandleLocal();
-  void TcpConnected(uv::Tcp& tcp) final;
-  void Disconnect(std::string_view reason) override;
-
-  std::shared_ptr<net3::UvStreamConnection3> m_wire;
-  std::shared_ptr<net3::ClientImpl3> m_clientImpl;
-};
-
-class NCImpl4 : public NCImpl {
- public:
-  NCImpl4(
-      int inst, std::string_view id, net::ILocalStorage& localStorage,
-      IConnectionList& connList, wpi::Logger& logger,
-      std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
-          timeSyncUpdated);
-  ~NCImpl4() override;
-
-  void HandleLocal();
-  void TcpConnected(uv::Tcp& tcp) final;
-  void WsConnected(wpi::WebSocket& ws, uv::Tcp& tcp);
-  void Disconnect(std::string_view reason) override;
-
-  std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
-      m_timeSyncUpdated;
-  std::shared_ptr<net::WebSocketConnection> m_wire;
-  std::unique_ptr<net::ClientImpl> m_clientImpl;
-};
-
-}  // namespace
-
-NCImpl::NCImpl(int inst, std::string_view id, net::ILocalStorage& localStorage,
-               IConnectionList& connList, wpi::Logger& logger)
+NetworkClientBase::NetworkClientBase(int inst, std::string_view id,
+                                     net::ILocalStorage& localStorage,
+                                     IConnectionList& connList,
+                                     wpi::Logger& logger)
     : m_inst{inst},
       m_localStorage{localStorage},
       m_connList{connList},
@@ -141,7 +45,62 @@
   INFO("starting network client");
 }
 
-void NCImpl::SetServers(
+NetworkClientBase::~NetworkClientBase() {
+  m_localStorage.ClearNetwork();
+  m_connList.ClearConnections();
+}
+
+void NetworkClientBase::Disconnect() {
+  m_loopRunner.ExecAsync(
+      [this](auto&) { ForceDisconnect("requested by application"); });
+}
+
+void NetworkClientBase::StartDSClient(unsigned int port) {
+  m_loopRunner.ExecAsync([=, this](uv::Loop& loop) {
+    if (m_dsClient) {
+      return;
+    }
+    m_dsClientServer.second = port == 0 ? NT_DEFAULT_PORT4 : port;
+    m_dsClient = wpi::DsClient::Create(m_loop, m_logger);
+    if (m_dsClient) {
+      m_dsClient->setIp.connect([this](std::string_view ip) {
+        m_dsClientServer.first = ip;
+        if (m_parallelConnect) {
+          m_parallelConnect->SetServers({{m_dsClientServer}});
+        }
+      });
+      m_dsClient->clearIp.connect([this] {
+        m_dsClientServer.first.clear();
+        if (m_parallelConnect) {
+          m_parallelConnect->SetServers(m_servers);
+        }
+      });
+    }
+  });
+}
+
+void NetworkClientBase::StopDSClient() {
+  m_loopRunner.ExecAsync([this](uv::Loop& loop) {
+    if (m_dsClient) {
+      m_dsClient->Close();
+      m_dsClient.reset();
+    }
+  });
+}
+
+void NetworkClientBase::FlushLocal() {
+  if (auto async = m_flushLocalAtomic.load(std::memory_order_relaxed)) {
+    async->UnsafeSend();
+  }
+}
+
+void NetworkClientBase::Flush() {
+  if (auto async = m_flushAtomic.load(std::memory_order_relaxed)) {
+    async->UnsafeSend();
+  }
+}
+
+void NetworkClientBase::DoSetServers(
     std::span<const std::pair<std::string, unsigned int>> servers,
     unsigned int defaultPort) {
   std::vector<std::pair<std::string, unsigned int>> serversCopy;
@@ -155,87 +114,73 @@
       [this, servers = std::move(serversCopy)](uv::Loop&) mutable {
         m_servers = std::move(servers);
         if (m_dsClientServer.first.empty()) {
-          m_parallelConnect->SetServers(m_servers);
+          if (m_parallelConnect) {
+            m_parallelConnect->SetServers(m_servers);
+          }
         }
       });
 }
 
-void NCImpl::StartDSClient(unsigned int port) {
-  m_loopRunner.ExecAsync([=, this](uv::Loop& loop) {
-    if (m_dsClient) {
-      return;
-    }
-    m_dsClientServer.second = port == 0 ? NT_DEFAULT_PORT4 : port;
-    m_dsClient = wpi::DsClient::Create(m_loop, m_logger);
-    m_dsClient->setIp.connect([this](std::string_view ip) {
-      m_dsClientServer.first = ip;
-      m_parallelConnect->SetServers({{m_dsClientServer}});
-    });
-    m_dsClient->clearIp.connect([this] {
-      m_dsClientServer.first.clear();
-      m_parallelConnect->SetServers(m_servers);
-    });
-  });
-}
-
-void NCImpl::StopDSClient() {
-  m_loopRunner.ExecAsync([this](uv::Loop& loop) {
-    if (m_dsClient) {
-      m_dsClient->Close();
-      m_dsClient.reset();
-    }
-  });
-}
-
-void NCImpl::Disconnect(std::string_view reason) {
+void NetworkClientBase::DoDisconnect(std::string_view reason) {
   if (m_readLocalTimer) {
     m_readLocalTimer->Stop();
   }
-  m_sendValuesTimer->Stop();
+  if (m_sendOutgoingTimer) {
+    m_sendOutgoingTimer->Stop();
+  }
   m_localStorage.ClearNetwork();
   m_localQueue.ClearQueue();
   m_connList.RemoveConnection(m_connHandle);
   m_connHandle = 0;
 
   // start trying to connect again
-  uv::Timer::SingleShot(m_loop, kReconnectRate,
-                        [this] { m_parallelConnect->Disconnected(); });
+  uv::Timer::SingleShot(m_loop, kReconnectRate, [this] {
+    if (m_parallelConnect) {
+      m_parallelConnect->Disconnected();
+    }
+  });
 }
 
-NCImpl3::NCImpl3(int inst, std::string_view id,
-                 net::ILocalStorage& localStorage, IConnectionList& connList,
-                 wpi::Logger& logger)
-    : NCImpl{inst, id, localStorage, connList, logger} {
+NetworkClient3::NetworkClient3(int inst, std::string_view id,
+                               net::ILocalStorage& localStorage,
+                               IConnectionList& connList, wpi::Logger& logger)
+    : NetworkClientBase{inst, id, localStorage, connList, logger} {
   m_loopRunner.ExecAsync([this](uv::Loop& loop) {
     m_parallelConnect = wpi::ParallelTcpConnector::Create(
         loop, kReconnectRate, m_logger,
         [this](uv::Tcp& tcp) { TcpConnected(tcp); });
 
-    m_sendValuesTimer = uv::Timer::Create(loop);
-    m_sendValuesTimer->timeout.connect([this] {
-      if (m_clientImpl) {
-        HandleLocal();
-        m_clientImpl->SendPeriodic(m_loop.Now().count());
-      }
-    });
+    m_sendOutgoingTimer = uv::Timer::Create(loop);
+    if (m_sendOutgoingTimer) {
+      m_sendOutgoingTimer->timeout.connect([this] {
+        if (m_clientImpl) {
+          HandleLocal();
+          m_clientImpl->SendPeriodic(m_loop.Now().count(), false);
+        }
+      });
+    }
 
     // set up flush async
     m_flush = uv::Async<>::Create(m_loop);
-    m_flush->wakeup.connect([this] {
-      if (m_clientImpl) {
-        HandleLocal();
-        m_clientImpl->SendPeriodic(m_loop.Now().count());
-      }
-    });
+    if (m_flush) {
+      m_flush->wakeup.connect([this] {
+        if (m_clientImpl) {
+          HandleLocal();
+          m_clientImpl->SendPeriodic(m_loop.Now().count(), true);
+        }
+      });
+    }
     m_flushAtomic = m_flush.get();
 
     m_flushLocal = uv::Async<>::Create(m_loop);
-    m_flushLocal->wakeup.connect([this] { HandleLocal(); });
+    if (m_flushLocal) {
+      m_flushLocal->wakeup.connect([this] { HandleLocal(); });
+    }
     m_flushLocalAtomic = m_flushLocal.get();
   });
 }
 
-NCImpl3::~NCImpl3() {
+NetworkClient3::~NetworkClient3() {
   // must explicitly destroy these on loop
   m_loopRunner.ExecSync([&](auto&) {
     m_clientImpl.reset();
@@ -245,14 +190,14 @@
   m_loopRunner.Stop();
 }
 
-void NCImpl3::HandleLocal() {
+void NetworkClient3::HandleLocal() {
   m_localQueue.ReadQueue(&m_localMsgs);
   if (m_clientImpl) {
     m_clientImpl->HandleLocal(m_localMsgs);
   }
 }
 
-void NCImpl3::TcpConnected(uv::Tcp& tcp) {
+void NetworkClient3::TcpConnected(uv::Tcp& tcp) {
   tcp.SetNoDelay(true);
 
   // create as shared_ptr and capture in lambda because there may be multiple
@@ -261,8 +206,10 @@
   auto clientImpl = std::make_shared<net3::ClientImpl3>(
       m_loop.Now().count(), m_inst, *wire, m_logger, [this](uint32_t repeatMs) {
         DEBUG4("Setting periodic timer to {}", repeatMs);
-        m_sendValuesTimer->Start(uv::Timer::Time{repeatMs},
-                                 uv::Timer::Time{repeatMs});
+        if (m_sendOutgoingTimer) {
+          m_sendOutgoingTimer->Start(uv::Timer::Time{repeatMs},
+                                     uv::Timer::Time{repeatMs});
+        }
       });
   clientImpl->Start(
       m_id, [this, wire,
@@ -276,7 +223,9 @@
           return;
         }
 
-        m_parallelConnect->Succeeded(tcp);
+        if (m_parallelConnect) {
+          m_parallelConnect->Succeeded(tcp);
+        }
 
         m_wire = std::move(wire);
         m_clientImpl = std::move(clientImpl);
@@ -293,19 +242,23 @@
         tcp.error.connect([this, &tcp](uv::Error err) {
           DEBUG3("NT3 TCP error {}", err.str());
           if (!tcp.IsLoopClosing()) {
-            Disconnect(err.str());
+            // we could be in the middle of sending data, so defer disconnect
+            uv::Timer::SingleShot(m_loop, uv::Timer::Time{0},
+                                  [this, reason = std::string{err.str()}] {
+                                    DoDisconnect(reason);
+                                  });
           }
         });
         tcp.end.connect([this, &tcp] {
           DEBUG3("NT3 TCP read ended");
           if (!tcp.IsLoopClosing()) {
-            Disconnect("remote end closed connection");
+            DoDisconnect("remote end closed connection");
           }
         });
         tcp.closed.connect([this, &tcp] {
           DEBUG3("NT3 TCP connection closed");
           if (!tcp.IsLoopClosing()) {
-            Disconnect(m_wire->GetDisconnectReason());
+            DoDisconnect(m_wire ? m_wire->GetDisconnectReason() : "unknown");
           }
         });
 
@@ -323,19 +276,25 @@
   tcp.StartRead();
 }
 
-void NCImpl3::Disconnect(std::string_view reason) {
+void NetworkClient3::ForceDisconnect(std::string_view reason) {
+  if (m_wire) {
+    m_wire->Disconnect(reason);
+  }
+}
+
+void NetworkClient3::DoDisconnect(std::string_view reason) {
   INFO("DISCONNECTED NT3 connection: {}", reason);
   m_clientImpl.reset();
   m_wire.reset();
-  NCImpl::Disconnect(reason);
+  NetworkClientBase::DoDisconnect(reason);
 }
 
-NCImpl4::NCImpl4(
+NetworkClient::NetworkClient(
     int inst, std::string_view id, net::ILocalStorage& localStorage,
     IConnectionList& connList, wpi::Logger& logger,
     std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
         timeSyncUpdated)
-    : NCImpl{inst, id, localStorage, connList, logger},
+    : NetworkClientBase{inst, id, localStorage, connList, logger},
       m_timeSyncUpdated{std::move(timeSyncUpdated)} {
   m_loopRunner.ExecAsync([this](uv::Loop& loop) {
     m_parallelConnect = wpi::ParallelTcpConnector::Create(
@@ -343,39 +302,47 @@
         [this](uv::Tcp& tcp) { TcpConnected(tcp); });
 
     m_readLocalTimer = uv::Timer::Create(loop);
-    m_readLocalTimer->timeout.connect([this] {
-      if (m_clientImpl) {
-        HandleLocal();
-        m_clientImpl->SendControl(m_loop.Now().count());
-      }
-    });
-    m_readLocalTimer->Start(uv::Timer::Time{100}, uv::Timer::Time{100});
+    if (m_readLocalTimer) {
+      m_readLocalTimer->timeout.connect([this] {
+        if (m_clientImpl) {
+          HandleLocal();
+          m_clientImpl->SendOutgoing(m_loop.Now().count(), false);
+        }
+      });
+      m_readLocalTimer->Start(uv::Timer::Time{100}, uv::Timer::Time{100});
+    }
 
-    m_sendValuesTimer = uv::Timer::Create(loop);
-    m_sendValuesTimer->timeout.connect([this] {
-      if (m_clientImpl) {
-        HandleLocal();
-        m_clientImpl->SendValues(m_loop.Now().count());
-      }
-    });
+    m_sendOutgoingTimer = uv::Timer::Create(loop);
+    if (m_sendOutgoingTimer) {
+      m_sendOutgoingTimer->timeout.connect([this] {
+        if (m_clientImpl) {
+          HandleLocal();
+          m_clientImpl->SendOutgoing(m_loop.Now().count(), false);
+        }
+      });
+    }
 
     // set up flush async
     m_flush = uv::Async<>::Create(m_loop);
-    m_flush->wakeup.connect([this] {
-      if (m_clientImpl) {
-        HandleLocal();
-        m_clientImpl->SendValues(m_loop.Now().count());
-      }
-    });
+    if (m_flush) {
+      m_flush->wakeup.connect([this] {
+        if (m_clientImpl) {
+          HandleLocal();
+          m_clientImpl->SendOutgoing(m_loop.Now().count(), true);
+        }
+      });
+    }
     m_flushAtomic = m_flush.get();
 
     m_flushLocal = uv::Async<>::Create(m_loop);
-    m_flushLocal->wakeup.connect([this] { HandleLocal(); });
+    if (m_flushLocal) {
+      m_flushLocal->wakeup.connect([this] { HandleLocal(); });
+    }
     m_flushLocalAtomic = m_flushLocal.get();
   });
 }
 
-NCImpl4::~NCImpl4() {
+NetworkClient::~NetworkClient() {
   // must explicitly destroy these on loop
   m_loopRunner.ExecSync([&](auto&) {
     m_clientImpl.reset();
@@ -385,14 +352,14 @@
   m_loopRunner.Stop();
 }
 
-void NCImpl4::HandleLocal() {
+void NetworkClient::HandleLocal() {
   m_localQueue.ReadQueue(&m_localMsgs);
   if (m_clientImpl) {
     m_clientImpl->HandleLocal(std::move(m_localMsgs));
   }
 }
 
-void NCImpl4::TcpConnected(uv::Tcp& tcp) {
+void NetworkClient::TcpConnected(uv::Tcp& tcp) {
   tcp.SetNoDelay(true);
   // Start the WS client
   if (m_logger.min_level() >= wpi::WPI_LOG_DEBUG4) {
@@ -406,34 +373,43 @@
   wpi::SmallString<128> idBuf;
   auto ws = wpi::WebSocket::CreateClient(
       tcp, fmt::format("/nt/{}", wpi::EscapeURI(m_id, idBuf)), "",
-      {{"networktables.first.wpi.edu"}}, options);
+      {"v4.1.networktables.first.wpi.edu", "networktables.first.wpi.edu"},
+      options);
   ws->SetMaxMessageSize(kMaxMessageSize);
-  ws->open.connect([this, &tcp, ws = ws.get()](std::string_view) {
+  ws->open.connect([this, &tcp, ws = ws.get()](std::string_view protocol) {
     if (m_connList.IsConnected()) {
       ws->Terminate(1006, "no longer needed");
       return;
     }
-    WsConnected(*ws, tcp);
+    WsConnected(*ws, tcp, protocol);
   });
 }
 
-void NCImpl4::WsConnected(wpi::WebSocket& ws, uv::Tcp& tcp) {
-  m_parallelConnect->Succeeded(tcp);
+void NetworkClient::WsConnected(wpi::WebSocket& ws, uv::Tcp& tcp,
+                                std::string_view protocol) {
+  if (m_parallelConnect) {
+    m_parallelConnect->Succeeded(tcp);
+  }
 
   ConnectionInfo connInfo;
   uv::AddrToName(tcp.GetPeer(), &connInfo.remote_ip, &connInfo.remote_port);
-  connInfo.protocol_version = 0x0400;
+  connInfo.protocol_version =
+      protocol == "v4.1.networktables.first.wpi.edu" ? 0x0401 : 0x0400;
 
   INFO("CONNECTED NT4 to {} port {}", connInfo.remote_ip, connInfo.remote_port);
   m_connHandle = m_connList.AddConnection(connInfo);
 
-  m_wire = std::make_shared<net::WebSocketConnection>(ws);
+  m_wire =
+      std::make_shared<net::WebSocketConnection>(ws, connInfo.protocol_version);
+  m_wire->Start();
   m_clientImpl = std::make_unique<net::ClientImpl>(
       m_loop.Now().count(), m_inst, *m_wire, m_logger, m_timeSyncUpdated,
       [this](uint32_t repeatMs) {
         DEBUG4("Setting periodic timer to {}", repeatMs);
-        m_sendValuesTimer->Start(uv::Timer::Time{repeatMs},
-                                 uv::Timer::Time{repeatMs});
+        if (m_sendOutgoingTimer) {
+          m_sendOutgoingTimer->Start(uv::Timer::Time{repeatMs},
+                                     uv::Timer::Time{repeatMs});
+        }
       });
   m_clientImpl->SetLocal(&m_localStorage);
   m_localStorage.StartNetwork(&m_localQueue);
@@ -441,7 +417,14 @@
   m_clientImpl->SendInitial();
   ws.closed.connect([this, &ws](uint16_t, std::string_view reason) {
     if (!ws.GetStream().IsLoopClosing()) {
-      Disconnect(reason);
+      // we could be in the middle of sending data, so defer disconnect
+      // capture a shared_ptr copy of ws to make sure it doesn't get destroyed
+      // until after DoDisconnect returns
+      uv::Timer::SingleShot(
+          m_loop, uv::Timer::Time{0},
+          [this, reason = std::string{reason}, keepws = ws.shared_from_this()] {
+            DoDisconnect(reason);
+          });
     }
   });
   ws.text.connect([this](std::string_view data, bool) {
@@ -451,107 +434,26 @@
   });
   ws.binary.connect([this](std::span<const uint8_t> data, bool) {
     if (m_clientImpl) {
-      m_clientImpl->ProcessIncomingBinary(data);
+      m_clientImpl->ProcessIncomingBinary(m_loop.Now().count(), data);
     }
   });
 }
 
-void NCImpl4::Disconnect(std::string_view reason) {
-  INFO("DISCONNECTED NT4 connection: {}", reason);
+void NetworkClient::ForceDisconnect(std::string_view reason) {
+  if (m_wire) {
+    m_wire->Disconnect(reason);
+  }
+}
+
+void NetworkClient::DoDisconnect(std::string_view reason) {
+  std::string realReason;
+  if (m_wire) {
+    realReason = m_wire->GetDisconnectReason();
+  }
+  INFO("DISCONNECTED NT4 connection: {}",
+       realReason.empty() ? reason : realReason);
   m_clientImpl.reset();
   m_wire.reset();
-  NCImpl::Disconnect(reason);
+  NetworkClientBase::DoDisconnect(reason);
   m_timeSyncUpdated(0, 0, false);
 }
-
-class NetworkClient::Impl final : public NCImpl4 {
- public:
-  Impl(int inst, std::string_view id, net::ILocalStorage& localStorage,
-       IConnectionList& connList, wpi::Logger& logger,
-       std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
-           timeSyncUpdated)
-      : NCImpl4{inst,     id,     localStorage,
-                connList, logger, std::move(timeSyncUpdated)} {}
-};
-
-NetworkClient::NetworkClient(
-    int inst, std::string_view id, net::ILocalStorage& localStorage,
-    IConnectionList& connList, wpi::Logger& logger,
-    std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
-        timeSyncUpdated)
-    : m_impl{std::make_unique<Impl>(inst, id, localStorage, connList, logger,
-                                    std::move(timeSyncUpdated))} {}
-
-NetworkClient::~NetworkClient() {
-  m_impl->m_localStorage.ClearNetwork();
-  m_impl->m_connList.ClearConnections();
-}
-
-void NetworkClient::SetServers(
-    std::span<const std::pair<std::string, unsigned int>> servers) {
-  m_impl->SetServers(servers, NT_DEFAULT_PORT4);
-}
-
-void NetworkClient::StartDSClient(unsigned int port) {
-  m_impl->StartDSClient(port);
-}
-
-void NetworkClient::StopDSClient() {
-  m_impl->StopDSClient();
-}
-
-void NetworkClient::FlushLocal() {
-  m_impl->m_loopRunner.ExecAsync([this](uv::Loop&) { m_impl->HandleLocal(); });
-}
-
-void NetworkClient::Flush() {
-  m_impl->m_loopRunner.ExecAsync([this](uv::Loop&) {
-    m_impl->HandleLocal();
-    if (m_impl->m_clientImpl) {
-      m_impl->m_clientImpl->SendValues(m_impl->m_loop.Now().count());
-    }
-  });
-}
-
-class NetworkClient3::Impl final : public NCImpl3 {
- public:
-  Impl(int inst, std::string_view id, net::ILocalStorage& localStorage,
-       IConnectionList& connList, wpi::Logger& logger)
-      : NCImpl3{inst, id, localStorage, connList, logger} {}
-};
-
-NetworkClient3::NetworkClient3(int inst, std::string_view id,
-                               net::ILocalStorage& localStorage,
-                               IConnectionList& connList, wpi::Logger& logger)
-    : m_impl{std::make_unique<Impl>(inst, id, localStorage, connList, logger)} {
-}
-
-NetworkClient3::~NetworkClient3() {
-  m_impl->m_localStorage.ClearNetwork();
-  m_impl->m_connList.ClearConnections();
-}
-
-void NetworkClient3::SetServers(
-    std::span<const std::pair<std::string, unsigned int>> servers) {
-  m_impl->SetServers(servers, NT_DEFAULT_PORT3);
-}
-
-void NetworkClient3::StartDSClient(unsigned int port) {
-  m_impl->StartDSClient(port);
-}
-
-void NetworkClient3::StopDSClient() {
-  m_impl->StopDSClient();
-}
-
-void NetworkClient3::FlushLocal() {
-  if (auto async = m_impl->m_flushLocalAtomic.load(std::memory_order_relaxed)) {
-    async->UnsafeSend();
-  }
-}
-
-void NetworkClient3::Flush() {
-  if (auto async = m_impl->m_flushAtomic.load(std::memory_order_relaxed)) {
-    async->UnsafeSend();
-  }
-}
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkClient.h b/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkClient.h
index 34bf379..73e1134 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkClient.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkClient.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <atomic>
 #include <functional>
 #include <memory>
 #include <optional>
@@ -11,8 +12,22 @@
 #include <string>
 #include <string_view>
 #include <utility>
+#include <vector>
+
+#include <wpinet/DsClient.h>
+#include <wpinet/EventLoopRunner.h>
+#include <wpinet/ParallelTcpConnector.h>
+#include <wpinet/WebSocket.h>
+#include <wpinet/uv/Async.h>
+#include <wpinet/uv/Timer.h>
 
 #include "INetworkClient.h"
+#include "net/ClientImpl.h"
+#include "net/Message.h"
+#include "net/NetworkLoopQueue.h"
+#include "net/WebSocketConnection.h"
+#include "net3/ClientImpl3.h"
+#include "net3/UvStreamConnection3.h"
 #include "ntcore_cpp.h"
 
 namespace wpi {
@@ -27,7 +42,86 @@
 
 class IConnectionList;
 
-class NetworkClient final : public INetworkClient {
+class NetworkClientBase : public INetworkClient {
+ public:
+  NetworkClientBase(int inst, std::string_view id,
+                    net::ILocalStorage& localStorage, IConnectionList& connList,
+                    wpi::Logger& logger);
+  ~NetworkClientBase() override;
+
+  void Disconnect() override;
+
+  void StartDSClient(unsigned int port) override;
+  void StopDSClient() override;
+
+  void FlushLocal() override;
+  void Flush() override;
+
+ protected:
+  void DoSetServers(
+      std::span<const std::pair<std::string, unsigned int>> servers,
+      unsigned int defaultPort);
+
+  virtual void TcpConnected(wpi::uv::Tcp& tcp) = 0;
+  virtual void ForceDisconnect(std::string_view reason) = 0;
+  virtual void DoDisconnect(std::string_view reason);
+
+  // invariants
+  int m_inst;
+  net::ILocalStorage& m_localStorage;
+  IConnectionList& m_connList;
+  wpi::Logger& m_logger;
+  std::string m_id;
+
+  // used only from loop
+  std::shared_ptr<wpi::ParallelTcpConnector> m_parallelConnect;
+  std::shared_ptr<wpi::uv::Timer> m_readLocalTimer;
+  std::shared_ptr<wpi::uv::Timer> m_sendOutgoingTimer;
+  std::shared_ptr<wpi::uv::Async<>> m_flushLocal;
+  std::shared_ptr<wpi::uv::Async<>> m_flush;
+
+  std::vector<net::ClientMessage> m_localMsgs;
+
+  std::vector<std::pair<std::string, unsigned int>> m_servers;
+
+  std::pair<std::string, unsigned int> m_dsClientServer{"", 0};
+  std::shared_ptr<wpi::DsClient> m_dsClient;
+
+  // shared with user
+  std::atomic<wpi::uv::Async<>*> m_flushLocalAtomic{nullptr};
+  std::atomic<wpi::uv::Async<>*> m_flushAtomic{nullptr};
+
+  net::NetworkLoopQueue m_localQueue;
+
+  int m_connHandle = 0;
+
+  wpi::EventLoopRunner m_loopRunner;
+  wpi::uv::Loop& m_loop;
+};
+
+class NetworkClient3 final : public NetworkClientBase {
+ public:
+  NetworkClient3(int inst, std::string_view id,
+                 net::ILocalStorage& localStorage, IConnectionList& connList,
+                 wpi::Logger& logger);
+  ~NetworkClient3() final;
+
+  void SetServers(
+      std::span<const std::pair<std::string, unsigned int>> servers) final {
+    DoSetServers(servers, NT_DEFAULT_PORT3);
+  }
+
+ private:
+  void HandleLocal();
+  void TcpConnected(wpi::uv::Tcp& tcp) final;
+  void ForceDisconnect(std::string_view reason) override;
+  void DoDisconnect(std::string_view reason) override;
+
+  std::shared_ptr<net3::UvStreamConnection3> m_wire;
+  std::shared_ptr<net3::ClientImpl3> m_clientImpl;
+};
+
+class NetworkClient final : public NetworkClientBase {
  public:
   NetworkClient(
       int inst, std::string_view id, net::ILocalStorage& localStorage,
@@ -37,38 +131,22 @@
   ~NetworkClient() final;
 
   void SetServers(
-      std::span<const std::pair<std::string, unsigned int>> servers) final;
-
-  void StartDSClient(unsigned int port) final;
-  void StopDSClient() final;
-
-  void FlushLocal() final;
-  void Flush() final;
+      std::span<const std::pair<std::string, unsigned int>> servers) final {
+    DoSetServers(servers, NT_DEFAULT_PORT4);
+  }
 
  private:
-  class Impl;
-  std::unique_ptr<Impl> m_impl;
-};
+  void HandleLocal();
+  void TcpConnected(wpi::uv::Tcp& tcp) final;
+  void WsConnected(wpi::WebSocket& ws, wpi::uv::Tcp& tcp,
+                   std::string_view protocol);
+  void ForceDisconnect(std::string_view reason) override;
+  void DoDisconnect(std::string_view reason) override;
 
-class NetworkClient3 final : public INetworkClient {
- public:
-  NetworkClient3(int inst, std::string_view id,
-                 net::ILocalStorage& localStorage, IConnectionList& connList,
-                 wpi::Logger& logger);
-  ~NetworkClient3() final;
-
-  void SetServers(
-      std::span<const std::pair<std::string, unsigned int>> servers) final;
-
-  void StartDSClient(unsigned int port) final;
-  void StopDSClient() final;
-
-  void FlushLocal() final;
-  void Flush() final;
-
- private:
-  class Impl;
-  std::unique_ptr<Impl> m_impl;
+  std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
+      m_timeSyncUpdated;
+  std::shared_ptr<net::WebSocketConnection> m_wire;
+  std::unique_ptr<net::ClientImpl> m_clientImpl;
 };
 
 }  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkServer.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkServer.cpp
index 0086d24..9062c7f 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkServer.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkServer.cpp
@@ -11,17 +11,16 @@
 #include <system_error>
 #include <vector>
 
+#include <wpi/MemoryBuffer.h>
 #include <wpi/SmallString.h>
 #include <wpi/StringExtras.h>
 #include <wpi/fs.h>
 #include <wpi/mutex.h>
-#include <wpi/raw_istream.h>
 #include <wpi/raw_ostream.h>
-#include <wpinet/EventLoopRunner.h>
+#include <wpi/timestamp.h>
 #include <wpinet/HttpUtil.h>
 #include <wpinet/HttpWebSocketServerConnection.h>
 #include <wpinet/UrlParser.h>
-#include <wpinet/uv/Async.h>
 #include <wpinet/uv/Tcp.h>
 #include <wpinet/uv/Work.h>
 #include <wpinet/uv/util.h>
@@ -29,10 +28,9 @@
 #include "IConnectionList.h"
 #include "InstanceImpl.h"
 #include "Log.h"
-#include "net/Message.h"
-#include "net/NetworkLoopQueue.h"
-#include "net/ServerImpl.h"
 #include "net/WebSocketConnection.h"
+#include "net/WireDecoder.h"
+#include "net/WireEncoder.h"
 #include "net3/UvStreamConnection3.h"
 
 using namespace nt;
@@ -41,14 +39,10 @@
 // use a larger max message size for websockets
 static constexpr size_t kMaxMessageSize = 2 * 1024 * 1024;
 
-namespace {
-
-class NSImpl;
-
-class ServerConnection {
+class NetworkServer::ServerConnection {
  public:
-  ServerConnection(NSImpl& server, std::string_view addr, unsigned int port,
-                   wpi::Logger& logger)
+  ServerConnection(NetworkServer& server, std::string_view addr,
+                   unsigned int port, wpi::Logger& logger)
       : m_server{server},
         m_connInfo{fmt::format("{}:{}", addr, port)},
         m_logger{logger} {
@@ -59,29 +53,42 @@
   int GetClientId() const { return m_clientId; }
 
  protected:
-  void SetupPeriodicTimer();
-  void UpdatePeriodicTimer(uint32_t repeatMs);
+  void SetupOutgoingTimer();
+  void UpdateOutgoingTimer(uint32_t repeatMs);
   void ConnectionClosed();
 
-  NSImpl& m_server;
+  NetworkServer& m_server;
   ConnectionInfo m_info;
   std::string m_connInfo;
   wpi::Logger& m_logger;
   int m_clientId;
 
  private:
-  std::shared_ptr<uv::Timer> m_sendValuesTimer;
+  std::shared_ptr<uv::Timer> m_outgoingTimer;
 };
 
-class ServerConnection4 final
+class NetworkServer::ServerConnection3 : public ServerConnection {
+ public:
+  ServerConnection3(std::shared_ptr<uv::Stream> stream, NetworkServer& server,
+                    std::string_view addr, unsigned int port,
+                    wpi::Logger& logger);
+
+ private:
+  std::shared_ptr<net3::UvStreamConnection3> m_wire;
+};
+
+class NetworkServer::ServerConnection4 final
     : public ServerConnection,
       public wpi::HttpWebSocketServerConnection<ServerConnection4> {
  public:
-  ServerConnection4(std::shared_ptr<uv::Stream> stream, NSImpl& server,
+  ServerConnection4(std::shared_ptr<uv::Stream> stream, NetworkServer& server,
                     std::string_view addr, unsigned int port,
                     wpi::Logger& logger)
       : ServerConnection{server, addr, port, logger},
-        HttpWebSocketServerConnection(stream, {"networktables.first.wpi.edu"}) {
+        HttpWebSocketServerConnection(
+            stream,
+            {"v4.1.networktables.first.wpi.edu", "networktables.first.wpi.edu",
+             "rtt.networktables.first.wpi.edu"}) {
     m_info.protocol_version = 0x0400;
   }
 
@@ -92,97 +99,82 @@
   std::shared_ptr<net::WebSocketConnection> m_wire;
 };
 
-class ServerConnection3 : public ServerConnection {
- public:
-  ServerConnection3(std::shared_ptr<uv::Stream> stream, NSImpl& server,
-                    std::string_view addr, unsigned int port,
-                    wpi::Logger& logger);
-
- private:
-  std::shared_ptr<net3::UvStreamConnection3> m_wire;
-};
-
-class NSImpl {
- public:
-  NSImpl(std::string_view persistFilename, std::string_view listenAddress,
-         unsigned int port3, unsigned int port4,
-         net::ILocalStorage& localStorage, IConnectionList& connList,
-         wpi::Logger& logger, std::function<void()> initDone);
-  ~NSImpl();
-
-  void HandleLocal();
-  void LoadPersistent();
-  void SavePersistent(std::string_view filename, std::string_view data);
-  void Init();
-  void AddConnection(ServerConnection* conn, const ConnectionInfo& info);
-  void RemoveConnection(ServerConnection* conn);
-
-  net::ILocalStorage& m_localStorage;
-  IConnectionList& m_connList;
-  wpi::Logger& m_logger;
-  std::function<void()> m_initDone;
-  std::string m_persistentData;
-  std::string m_persistentFilename;
-  std::string m_listenAddress;
-  unsigned int m_port3;
-  unsigned int m_port4;
-
-  // used only from loop
-  std::shared_ptr<uv::Timer> m_readLocalTimer;
-  std::shared_ptr<uv::Timer> m_savePersistentTimer;
-  std::shared_ptr<uv::Async<>> m_flushLocal;
-  std::shared_ptr<uv::Async<>> m_flush;
-  bool m_shutdown = false;
-
-  std::vector<net::ClientMessage> m_localMsgs;
-
-  net::ServerImpl m_serverImpl;
-
-  // shared with user (must be atomic or mutex-protected)
-  std::atomic<uv::Async<>*> m_flushLocalAtomic{nullptr};
-  std::atomic<uv::Async<>*> m_flushAtomic{nullptr};
-  mutable wpi::mutex m_mutex;
-  struct Connection {
-    ServerConnection* conn;
-    int connHandle;
-  };
-  std::vector<Connection> m_connections;
-
-  net::NetworkLoopQueue m_localQueue;
-
-  wpi::EventLoopRunner m_loopRunner;
-  wpi::uv::Loop& m_loop;
-};
-
-}  // namespace
-
-void ServerConnection::SetupPeriodicTimer() {
-  m_sendValuesTimer = uv::Timer::Create(m_server.m_loop);
-  m_sendValuesTimer->timeout.connect([this] {
+void NetworkServer::ServerConnection::SetupOutgoingTimer() {
+  m_outgoingTimer = uv::Timer::Create(m_server.m_loop);
+  m_outgoingTimer->timeout.connect([this] {
     m_server.HandleLocal();
-    m_server.m_serverImpl.SendValues(m_clientId, m_server.m_loop.Now().count());
+    m_server.m_serverImpl.SendOutgoing(m_clientId,
+                                       m_server.m_loop.Now().count());
   });
 }
 
-void ServerConnection::UpdatePeriodicTimer(uint32_t repeatMs) {
+void NetworkServer::ServerConnection::UpdateOutgoingTimer(uint32_t repeatMs) {
+  DEBUG4("Setting periodic timer to {}", repeatMs);
   if (repeatMs == UINT32_MAX) {
-    m_sendValuesTimer->Stop();
+    m_outgoingTimer->Stop();
   } else {
-    m_sendValuesTimer->Start(uv::Timer::Time{repeatMs},
-                             uv::Timer::Time{repeatMs});
+    m_outgoingTimer->Start(uv::Timer::Time{repeatMs},
+                           uv::Timer::Time{repeatMs});
   }
 }
 
-void ServerConnection::ConnectionClosed() {
+void NetworkServer::ServerConnection::ConnectionClosed() {
   // don't call back into m_server if it's being destroyed
-  if (!m_sendValuesTimer->IsLoopClosing()) {
+  if (!m_outgoingTimer->IsLoopClosing()) {
     m_server.m_serverImpl.RemoveClient(m_clientId);
     m_server.RemoveConnection(this);
   }
-  m_sendValuesTimer->Close();
+  m_outgoingTimer->Close();
 }
 
-void ServerConnection4::ProcessRequest() {
+NetworkServer::ServerConnection3::ServerConnection3(
+    std::shared_ptr<uv::Stream> stream, NetworkServer& server,
+    std::string_view addr, unsigned int port, wpi::Logger& logger)
+    : ServerConnection{server, addr, port, logger},
+      m_wire{std::make_shared<net3::UvStreamConnection3>(*stream)} {
+  m_info.remote_ip = addr;
+  m_info.remote_port = port;
+
+  // TODO: set local flag appropriately
+  m_clientId = m_server.m_serverImpl.AddClient3(
+      m_connInfo, false, *m_wire,
+      [this](std::string_view name, uint16_t proto) {
+        m_info.remote_id = name;
+        m_info.protocol_version = proto;
+        m_server.AddConnection(this, m_info);
+        INFO("CONNECTED NT3 client '{}' (from {})", name, m_connInfo);
+      },
+      [this](uint32_t repeatMs) { UpdateOutgoingTimer(repeatMs); });
+
+  stream->error.connect([this](uv::Error err) {
+    if (!m_wire->GetDisconnectReason().empty()) {
+      return;
+    }
+    m_wire->Disconnect(fmt::format("stream error: {}", err.name()));
+    m_wire->GetStream().Shutdown([this] { m_wire->GetStream().Close(); });
+  });
+  stream->end.connect([this] {
+    if (!m_wire->GetDisconnectReason().empty()) {
+      return;
+    }
+    m_wire->Disconnect("remote end closed connection");
+    m_wire->GetStream().Shutdown([this] { m_wire->GetStream().Close(); });
+  });
+  stream->closed.connect([this] {
+    INFO("DISCONNECTED NT3 client '{}' (from {}): {}", m_info.remote_id,
+         m_connInfo, m_wire->GetDisconnectReason());
+    ConnectionClosed();
+  });
+  stream->data.connect([this](uv::Buffer& buf, size_t size) {
+    m_server.m_serverImpl.ProcessIncomingBinary(
+        m_clientId, {reinterpret_cast<const uint8_t*>(buf.base), size});
+  });
+  stream->StartRead();
+
+  SetupOutgoingTimer();
+}
+
+void NetworkServer::ServerConnection4::ProcessRequest() {
   DEBUG1("HTTP request: '{}'", m_request.GetUrl());
   wpi::UrlParser url{m_request.GetUrl(),
                      m_request.GetMethod() == wpi::HTTP_CONNECT};
@@ -219,7 +211,7 @@
   }
 }
 
-void ServerConnection4::ProcessWsUpgrade() {
+void NetworkServer::ServerConnection4::ProcessWsUpgrade() {
   // get name from URL
   wpi::UrlParser url{m_request.GetUrl(), false};
   std::string_view path;
@@ -244,19 +236,55 @@
 
   m_websocket->SetMaxMessageSize(kMaxMessageSize);
 
-  m_websocket->open.connect([this, name = std::string{name}](std::string_view) {
-    m_wire = std::make_shared<net::WebSocketConnection>(*m_websocket);
+  m_websocket->open.connect([this, name = std::string{name}](
+                                std::string_view protocol) {
+    m_info.protocol_version =
+        protocol == "v4.1.networktables.first.wpi.edu" ? 0x0401 : 0x0400;
+    m_wire = std::make_shared<net::WebSocketConnection>(
+        *m_websocket, m_info.protocol_version);
+
+    if (protocol == "rtt.networktables.first.wpi.edu") {
+      INFO("CONNECTED RTT client (from {})", m_connInfo);
+      m_websocket->binary.connect([this](std::span<const uint8_t> data, bool) {
+        while (!data.empty()) {
+          // decode message
+          int64_t pubuid;
+          Value value;
+          std::string error;
+          if (!net::WireDecodeBinary(&data, &pubuid, &value, &error, 0)) {
+            m_wire->Disconnect(fmt::format("binary decode error: {}", error));
+            break;
+          }
+
+          // respond to RTT ping
+          if (pubuid == -1) {
+            m_wire->SendBinary([&](auto& os) {
+              net::WireEncodeBinary(os, -1, wpi::Now(), value);
+            });
+          }
+        }
+      });
+      m_websocket->closed.connect([this](uint16_t, std::string_view reason) {
+        auto realReason = m_wire->GetDisconnectReason();
+        INFO("DISCONNECTED RTT client (from {}): {}", m_connInfo,
+             realReason.empty() ? reason : realReason);
+      });
+      return;
+    }
+
     // TODO: set local flag appropriately
     std::string dedupName;
     std::tie(dedupName, m_clientId) = m_server.m_serverImpl.AddClient(
         name, m_connInfo, false, *m_wire,
-        [this](uint32_t repeatMs) { UpdatePeriodicTimer(repeatMs); });
+        [this](uint32_t repeatMs) { UpdateOutgoingTimer(repeatMs); });
     INFO("CONNECTED NT4 client '{}' (from {})", dedupName, m_connInfo);
     m_info.remote_id = dedupName;
     m_server.AddConnection(this, m_info);
+    m_wire->Start();
     m_websocket->closed.connect([this](uint16_t, std::string_view reason) {
+      auto realReason = m_wire->GetDisconnectReason();
       INFO("DISCONNECTED NT4 client '{}' (from {}): {}", m_info.remote_id,
-           m_connInfo, reason);
+           m_connInfo, realReason.empty() ? reason : realReason);
       ConnectionClosed();
     });
     m_websocket->text.connect([this](std::string_view data, bool) {
@@ -266,62 +294,16 @@
       m_server.m_serverImpl.ProcessIncomingBinary(m_clientId, data);
     });
 
-    SetupPeriodicTimer();
+    SetupOutgoingTimer();
   });
 }
 
-ServerConnection3::ServerConnection3(std::shared_ptr<uv::Stream> stream,
-                                     NSImpl& server, std::string_view addr,
-                                     unsigned int port, wpi::Logger& logger)
-    : ServerConnection{server, addr, port, logger},
-      m_wire{std::make_shared<net3::UvStreamConnection3>(*stream)} {
-  m_info.remote_ip = addr;
-  m_info.remote_port = port;
-
-  // TODO: set local flag appropriately
-  m_clientId = m_server.m_serverImpl.AddClient3(
-      m_connInfo, false, *m_wire,
-      [this](std::string_view name, uint16_t proto) {
-        m_info.remote_id = name;
-        m_info.protocol_version = proto;
-        m_server.AddConnection(this, m_info);
-        INFO("CONNECTED NT3 client '{}' (from {})", name, m_connInfo);
-      },
-      [this](uint32_t repeatMs) { UpdatePeriodicTimer(repeatMs); });
-
-  stream->error.connect([this](uv::Error err) {
-    if (!m_wire->GetDisconnectReason().empty()) {
-      return;
-    }
-    m_wire->Disconnect(fmt::format("stream error: {}", err.name()));
-    m_wire->GetStream().Shutdown([this] { m_wire->GetStream().Close(); });
-  });
-  stream->end.connect([this] {
-    if (!m_wire->GetDisconnectReason().empty()) {
-      return;
-    }
-    m_wire->Disconnect("remote end closed connection");
-    m_wire->GetStream().Shutdown([this] { m_wire->GetStream().Close(); });
-  });
-  stream->closed.connect([this] {
-    INFO("DISCONNECTED NT3 client '{}' (from {}): {}", m_info.remote_id,
-         m_connInfo, m_wire->GetDisconnectReason());
-    ConnectionClosed();
-  });
-  stream->data.connect([this](uv::Buffer& buf, size_t size) {
-    m_server.m_serverImpl.ProcessIncomingBinary(
-        m_clientId, {reinterpret_cast<const uint8_t*>(buf.base), size});
-  });
-  stream->StartRead();
-
-  SetupPeriodicTimer();
-}
-
-NSImpl::NSImpl(std::string_view persistentFilename,
-               std::string_view listenAddress, unsigned int port3,
-               unsigned int port4, net::ILocalStorage& localStorage,
-               IConnectionList& connList, wpi::Logger& logger,
-               std::function<void()> initDone)
+NetworkServer::NetworkServer(std::string_view persistentFilename,
+                             std::string_view listenAddress, unsigned int port3,
+                             unsigned int port4,
+                             net::ILocalStorage& localStorage,
+                             IConnectionList& connList, wpi::Logger& logger,
+                             std::function<void()> initDone)
     : m_localStorage{localStorage},
       m_connList{connList},
       m_logger{logger},
@@ -346,33 +328,52 @@
   });
 }
 
-NSImpl::~NSImpl() {
+NetworkServer::~NetworkServer() {
   m_loopRunner.ExecAsync([this](uv::Loop&) { m_shutdown = true; });
+  m_localStorage.ClearNetwork();
+  m_connList.ClearConnections();
 }
 
-void NSImpl::HandleLocal() {
+void NetworkServer::FlushLocal() {
+  if (auto async = m_flushLocalAtomic.load(std::memory_order_relaxed)) {
+    async->UnsafeSend();
+  }
+}
+
+void NetworkServer::Flush() {
+  if (auto async = m_flushAtomic.load(std::memory_order_relaxed)) {
+    async->UnsafeSend();
+  }
+}
+
+void NetworkServer::HandleLocal() {
   m_localQueue.ReadQueue(&m_localMsgs);
   m_serverImpl.HandleLocal(m_localMsgs);
 }
 
-void NSImpl::LoadPersistent() {
+void NetworkServer::LoadPersistent() {
   std::error_code ec;
-  auto size = fs::file_size(m_persistentFilename, ec);
-  wpi::raw_fd_istream is{m_persistentFilename, ec};
-  if (ec.value() != 0) {
-    INFO("could not open persistent file '{}': {}", m_persistentFilename,
-         ec.message());
+  std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+      wpi::MemoryBuffer::GetFile(m_persistentFilename, ec);
+  if (fileBuffer == nullptr || ec.value() != 0) {
+    INFO(
+        "could not open persistent file '{}': {} "
+        "(this can be ignored if you aren't expecting persistent values)",
+        m_persistentFilename, ec.message());
+    // try to write an empty file so it doesn't happen again
+    wpi::raw_fd_ostream os{m_persistentFilename, ec, fs::F_Text};
+    if (ec.value() == 0) {
+      os << "[]\n";
+      os.close();
+    }
     return;
   }
-  is.readinto(m_persistentData, size);
+  m_persistentData = std::string{fileBuffer->begin(), fileBuffer->end()};
   DEBUG4("read data: {}", m_persistentData);
-  if (is.has_error()) {
-    WARNING("error reading persistent file");
-    return;
-  }
 }
 
-void NSImpl::SavePersistent(std::string_view filename, std::string_view data) {
+void NetworkServer::SavePersistent(std::string_view filename,
+                                   std::string_view data) {
   // write to temporary file
   auto tmp = fmt::format("{}.tmp", filename);
   std::error_code ec;
@@ -400,47 +401,55 @@
   }
 }
 
-void NSImpl::Init() {
+void NetworkServer::Init() {
   if (m_shutdown) {
     return;
   }
   auto errs = m_serverImpl.LoadPersistent(m_persistentData);
   if (!errs.empty()) {
-    WARNING("error reading persistent file: {}", errs);
+    WARN("error reading persistent file: {}", errs);
   }
 
   // set up timers
   m_readLocalTimer = uv::Timer::Create(m_loop);
-  m_readLocalTimer->timeout.connect([this] {
-    HandleLocal();
-    m_serverImpl.SendControl(m_loop.Now().count());
-  });
-  m_readLocalTimer->Start(uv::Timer::Time{100}, uv::Timer::Time{100});
+  if (m_readLocalTimer) {
+    m_readLocalTimer->timeout.connect([this] {
+      HandleLocal();
+      m_serverImpl.SendAllOutgoing(m_loop.Now().count(), false);
+    });
+    m_readLocalTimer->Start(uv::Timer::Time{100}, uv::Timer::Time{100});
+  }
 
   m_savePersistentTimer = uv::Timer::Create(m_loop);
-  m_savePersistentTimer->timeout.connect([this] {
-    if (m_serverImpl.PersistentChanged()) {
-      uv::QueueWork(
-          m_loop,
-          [this, fn = m_persistentFilename,
-           data = m_serverImpl.DumpPersistent()] { SavePersistent(fn, data); },
-          nullptr);
-    }
-  });
-  m_savePersistentTimer->Start(uv::Timer::Time{1000}, uv::Timer::Time{1000});
+  if (m_savePersistentTimer) {
+    m_savePersistentTimer->timeout.connect([this] {
+      if (m_serverImpl.PersistentChanged()) {
+        uv::QueueWork(
+            m_loop,
+            [this, fn = m_persistentFilename,
+             data = m_serverImpl.DumpPersistent()] {
+              SavePersistent(fn, data);
+            },
+            nullptr);
+      }
+    });
+    m_savePersistentTimer->Start(uv::Timer::Time{1000}, uv::Timer::Time{1000});
+  }
 
   // set up flush async
   m_flush = uv::Async<>::Create(m_loop);
-  m_flush->wakeup.connect([this] {
-    HandleLocal();
-    for (auto&& conn : m_connections) {
-      m_serverImpl.SendValues(conn.conn->GetClientId(), m_loop.Now().count());
-    }
-  });
+  if (m_flush) {
+    m_flush->wakeup.connect([this] {
+      HandleLocal();
+      m_serverImpl.SendAllOutgoing(m_loop.Now().count(), true);
+    });
+  }
   m_flushAtomic = m_flush.get();
 
   m_flushLocal = uv::Async<>::Create(m_loop);
-  m_flushLocal->wakeup.connect([this] { HandleLocal(); });
+  if (m_flushLocal) {
+    m_flushLocal->wakeup.connect([this] { HandleLocal(); });
+  }
   m_flushLocalAtomic = m_flushLocal.get();
 
   INFO("Listening on NT3 port {}, NT4 port {}", m_port3, m_port4);
@@ -516,13 +525,14 @@
   }
 }
 
-void NSImpl::AddConnection(ServerConnection* conn, const ConnectionInfo& info) {
+void NetworkServer::AddConnection(ServerConnection* conn,
+                                  const ConnectionInfo& info) {
   std::scoped_lock lock{m_mutex};
   m_connections.emplace_back(Connection{conn, m_connList.AddConnection(info)});
   m_serverImpl.ConnectionsChanged(m_connList.GetConnections());
 }
 
-void NSImpl::RemoveConnection(ServerConnection* conn) {
+void NetworkServer::RemoveConnection(ServerConnection* conn) {
   std::scoped_lock lock{m_mutex};
   auto it = std::find_if(m_connections.begin(), m_connections.end(),
                          [=](auto&& c) { return c.conn == conn; });
@@ -532,40 +542,3 @@
     m_serverImpl.ConnectionsChanged(m_connList.GetConnections());
   }
 }
-
-class NetworkServer::Impl final : public NSImpl {
- public:
-  Impl(std::string_view persistFilename, std::string_view listenAddress,
-       unsigned int port3, unsigned int port4, net::ILocalStorage& localStorage,
-       IConnectionList& connList, wpi::Logger& logger,
-       std::function<void()> initDone)
-      : NSImpl{persistFilename, listenAddress, port3,  port4,
-               localStorage,    connList,      logger, std::move(initDone)} {}
-};
-
-NetworkServer::NetworkServer(std::string_view persistFilename,
-                             std::string_view listenAddress, unsigned int port3,
-                             unsigned int port4,
-                             net::ILocalStorage& localStorage,
-                             IConnectionList& connList, wpi::Logger& logger,
-                             std::function<void()> initDone)
-    : m_impl{std::make_unique<Impl>(persistFilename, listenAddress, port3,
-                                    port4, localStorage, connList, logger,
-                                    std::move(initDone))} {}
-
-NetworkServer::~NetworkServer() {
-  m_impl->m_localStorage.ClearNetwork();
-  m_impl->m_connList.ClearConnections();
-}
-
-void NetworkServer::FlushLocal() {
-  if (auto async = m_impl->m_flushLocalAtomic.load(std::memory_order_relaxed)) {
-    async->UnsafeSend();
-  }
-}
-
-void NetworkServer::Flush() {
-  if (auto async = m_impl->m_flushAtomic.load(std::memory_order_relaxed)) {
-    async->UnsafeSend();
-  }
-}
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkServer.h b/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkServer.h
index b70c968..3f5a094 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkServer.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/NetworkServer.h
@@ -4,10 +4,20 @@
 
 #pragma once
 
+#include <atomic>
 #include <functional>
 #include <memory>
+#include <string>
 #include <string_view>
+#include <vector>
 
+#include <wpinet/EventLoopRunner.h>
+#include <wpinet/uv/Async.h>
+#include <wpinet/uv/Timer.h>
+
+#include "net/Message.h"
+#include "net/NetworkLoopQueue.h"
+#include "net/ServerImpl.h"
 #include "ntcore_cpp.h"
 
 namespace wpi {
@@ -35,8 +45,52 @@
   void Flush();
 
  private:
-  class Impl;
-  std::unique_ptr<Impl> m_impl;
+  class ServerConnection;
+  class ServerConnection3;
+  class ServerConnection4;
+
+  void HandleLocal();
+  void LoadPersistent();
+  void SavePersistent(std::string_view filename, std::string_view data);
+  void Init();
+  void AddConnection(ServerConnection* conn, const ConnectionInfo& info);
+  void RemoveConnection(ServerConnection* conn);
+
+  net::ILocalStorage& m_localStorage;
+  IConnectionList& m_connList;
+  wpi::Logger& m_logger;
+  std::function<void()> m_initDone;
+  std::string m_persistentData;
+  std::string m_persistentFilename;
+  std::string m_listenAddress;
+  unsigned int m_port3;
+  unsigned int m_port4;
+
+  // used only from loop
+  std::shared_ptr<wpi::uv::Timer> m_readLocalTimer;
+  std::shared_ptr<wpi::uv::Timer> m_savePersistentTimer;
+  std::shared_ptr<wpi::uv::Async<>> m_flushLocal;
+  std::shared_ptr<wpi::uv::Async<>> m_flush;
+  bool m_shutdown = false;
+
+  std::vector<net::ClientMessage> m_localMsgs;
+
+  net::ServerImpl m_serverImpl;
+
+  // shared with user (must be atomic or mutex-protected)
+  std::atomic<wpi::uv::Async<>*> m_flushLocalAtomic{nullptr};
+  std::atomic<wpi::uv::Async<>*> m_flushAtomic{nullptr};
+  mutable wpi::mutex m_mutex;
+  struct Connection {
+    ServerConnection* conn;
+    int connHandle;
+  };
+  std::vector<Connection> m_connections;
+
+  net::NetworkLoopQueue m_localQueue;
+
+  wpi::EventLoopRunner m_loopRunner;
+  wpi::uv::Loop& m_loop;
 };
 
 }  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/Value.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/Value.cpp
index 240447f..09ba775 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/Value.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/Value.cpp
@@ -4,7 +4,9 @@
 
 #include <stdint.h>
 
+#include <algorithm>
 #include <cstring>
+#include <numeric>
 #include <span>
 
 #include <wpi/MemAlloc.h>
@@ -27,10 +29,32 @@
     InitNtStrings();
   }
   void InitNtStrings();
+  size_t EstimateSize() const {
+    return sizeof(StringArrayStorage) +
+           strings.capacity() * sizeof(std::string) +
+           ntStrings.capacity() * sizeof(NT_String) +
+           std::accumulate(strings.begin(), strings.end(), 0,
+                           [](const auto& sum, const auto& val) {
+                             return sum + val.capacity();
+                           });
+  }
 
   std::vector<std::string> strings;
   std::vector<NT_String> ntStrings;
 };
+
+template <typename T>
+inline std::shared_ptr<T[]> AllocateArray(size_t nelem) {
+#if __cpp_lib_shared_ptr_arrays >= 201707L
+#if __cpp_lib_smart_ptr_for_overwrite >= 202002L
+  return std::make_shared_for_overwrite<T[]>(nelem);
+#else
+  return std::make_shared<T[]>(nelem);
+#endif
+#else
+  return std::shared_ptr<T[]>{new T[nelem]};
+#endif
+}
 }  // namespace
 
 void StringArrayStorage::InitNtStrings() {
@@ -46,12 +70,13 @@
   m_val.type = NT_UNASSIGNED;
   m_val.last_change = 0;
   m_val.server_time = 0;
+  m_size = 0;
 }
 
-Value::Value(NT_Type type, int64_t time, const private_init&)
-    : Value{type, time == 0 ? nt::Now() : time, 1, private_init{}} {}
+Value::Value(NT_Type type, size_t size, int64_t time, const private_init&)
+    : Value{type, size, time == 0 ? nt::Now() : time, 1, private_init{}} {}
 
-Value::Value(NT_Type type, int64_t time, int64_t serverTime,
+Value::Value(NT_Type type, size_t size, int64_t time, int64_t serverTime,
              const private_init&) {
   m_val.type = type;
   m_val.last_change = time;
@@ -67,30 +92,31 @@
   } else if (m_val.type == NT_STRING_ARRAY) {
     m_val.data.arr_string.arr = nullptr;
   }
+  m_size = size;
 }
 
 Value Value::MakeBooleanArray(std::span<const bool> value, int64_t time) {
-  Value val{NT_BOOLEAN_ARRAY, time, private_init{}};
-  auto data = std::make_shared<std::vector<int>>(value.begin(), value.end());
-  // data->reserve(value.size());
-  // std::copy(value.begin(), value.end(), *data);
-  val.m_val.data.arr_boolean.arr = data->data();
-  val.m_val.data.arr_boolean.size = data->size();
+  Value val{NT_BOOLEAN_ARRAY, value.size() * sizeof(int), time, private_init{}};
+  auto data = AllocateArray<int>(value.size());
+  std::copy(value.begin(), value.end(), data.get());
+  val.m_val.data.arr_boolean.arr = data.get();
+  val.m_val.data.arr_boolean.size = value.size();
   val.m_storage = std::move(data);
   return val;
 }
 
 Value Value::MakeBooleanArray(std::span<const int> value, int64_t time) {
-  Value val{NT_BOOLEAN_ARRAY, time, private_init{}};
-  auto data = std::make_shared<std::vector<int>>(value.begin(), value.end());
-  val.m_val.data.arr_boolean.arr = data->data();
-  val.m_val.data.arr_boolean.size = data->size();
+  Value val{NT_BOOLEAN_ARRAY, value.size() * sizeof(int), time, private_init{}};
+  auto data = AllocateArray<int>(value.size());
+  std::copy(value.begin(), value.end(), data.get());
+  val.m_val.data.arr_boolean.arr = data.get();
+  val.m_val.data.arr_boolean.size = value.size();
   val.m_storage = std::move(data);
   return val;
 }
 
 Value Value::MakeBooleanArray(std::vector<int>&& value, int64_t time) {
-  Value val{NT_BOOLEAN_ARRAY, time, private_init{}};
+  Value val{NT_BOOLEAN_ARRAY, value.size() * sizeof(int), time, private_init{}};
   auto data = std::make_shared<std::vector<int>>(std::move(value));
   val.m_val.data.arr_boolean.arr = data->data();
   val.m_val.data.arr_boolean.size = data->size();
@@ -99,17 +125,19 @@
 }
 
 Value Value::MakeIntegerArray(std::span<const int64_t> value, int64_t time) {
-  Value val{NT_INTEGER_ARRAY, time, private_init{}};
-  auto data =
-      std::make_shared<std::vector<int64_t>>(value.begin(), value.end());
-  val.m_val.data.arr_int.arr = data->data();
-  val.m_val.data.arr_int.size = data->size();
+  Value val{NT_INTEGER_ARRAY, value.size() * sizeof(int64_t), time,
+            private_init{}};
+  auto data = AllocateArray<int64_t>(value.size());
+  std::copy(value.begin(), value.end(), data.get());
+  val.m_val.data.arr_int.arr = data.get();
+  val.m_val.data.arr_int.size = value.size();
   val.m_storage = std::move(data);
   return val;
 }
 
 Value Value::MakeIntegerArray(std::vector<int64_t>&& value, int64_t time) {
-  Value val{NT_INTEGER_ARRAY, time, private_init{}};
+  Value val{NT_INTEGER_ARRAY, value.size() * sizeof(int64_t), time,
+            private_init{}};
   auto data = std::make_shared<std::vector<int64_t>>(std::move(value));
   val.m_val.data.arr_int.arr = data->data();
   val.m_val.data.arr_int.size = data->size();
@@ -118,16 +146,17 @@
 }
 
 Value Value::MakeFloatArray(std::span<const float> value, int64_t time) {
-  Value val{NT_FLOAT_ARRAY, time, private_init{}};
-  auto data = std::make_shared<std::vector<float>>(value.begin(), value.end());
-  val.m_val.data.arr_float.arr = data->data();
-  val.m_val.data.arr_float.size = data->size();
+  Value val{NT_FLOAT_ARRAY, value.size() * sizeof(float), time, private_init{}};
+  auto data = AllocateArray<float>(value.size());
+  std::copy(value.begin(), value.end(), data.get());
+  val.m_val.data.arr_float.arr = data.get();
+  val.m_val.data.arr_float.size = value.size();
   val.m_storage = std::move(data);
   return val;
 }
 
 Value Value::MakeFloatArray(std::vector<float>&& value, int64_t time) {
-  Value val{NT_FLOAT_ARRAY, time, private_init{}};
+  Value val{NT_FLOAT_ARRAY, value.size() * sizeof(float), time, private_init{}};
   auto data = std::make_shared<std::vector<float>>(std::move(value));
   val.m_val.data.arr_float.arr = data->data();
   val.m_val.data.arr_float.size = data->size();
@@ -136,16 +165,19 @@
 }
 
 Value Value::MakeDoubleArray(std::span<const double> value, int64_t time) {
-  Value val{NT_DOUBLE_ARRAY, time, private_init{}};
-  auto data = std::make_shared<std::vector<double>>(value.begin(), value.end());
-  val.m_val.data.arr_double.arr = data->data();
-  val.m_val.data.arr_double.size = data->size();
+  Value val{NT_DOUBLE_ARRAY, value.size() * sizeof(double), time,
+            private_init{}};
+  auto data = AllocateArray<double>(value.size());
+  std::copy(value.begin(), value.end(), data.get());
+  val.m_val.data.arr_double.arr = data.get();
+  val.m_val.data.arr_double.size = value.size();
   val.m_storage = std::move(data);
   return val;
 }
 
 Value Value::MakeDoubleArray(std::vector<double>&& value, int64_t time) {
-  Value val{NT_DOUBLE_ARRAY, time, private_init{}};
+  Value val{NT_DOUBLE_ARRAY, value.size() * sizeof(double), time,
+            private_init{}};
   auto data = std::make_shared<std::vector<double>>(std::move(value));
   val.m_val.data.arr_double.arr = data->data();
   val.m_val.data.arr_double.size = data->size();
@@ -154,8 +186,8 @@
 }
 
 Value Value::MakeStringArray(std::span<const std::string> value, int64_t time) {
-  Value val{NT_STRING_ARRAY, time, private_init{}};
   auto data = std::make_shared<StringArrayStorage>(value);
+  Value val{NT_STRING_ARRAY, data->EstimateSize(), time, private_init{}};
   val.m_val.data.arr_string.arr = data->ntStrings.data();
   val.m_val.data.arr_string.size = data->ntStrings.size();
   val.m_storage = std::move(data);
@@ -163,8 +195,8 @@
 }
 
 Value Value::MakeStringArray(std::vector<std::string>&& value, int64_t time) {
-  Value val{NT_STRING_ARRAY, time, private_init{}};
   auto data = std::make_shared<StringArrayStorage>(std::move(value));
+  Value val{NT_STRING_ARRAY, data->EstimateSize(), time, private_init{}};
   val.m_val.data.arr_string.arr = data->ntStrings.data();
   val.m_val.data.arr_string.size = data->ntStrings.size();
   val.m_storage = std::move(data);
@@ -317,6 +349,9 @@
       if (lhs.m_val.data.arr_boolean.size != rhs.m_val.data.arr_boolean.size) {
         return false;
       }
+      if (lhs.m_val.data.arr_boolean.size == 0) {
+        return true;
+      }
       return std::memcmp(lhs.m_val.data.arr_boolean.arr,
                          rhs.m_val.data.arr_boolean.arr,
                          lhs.m_val.data.arr_boolean.size *
@@ -325,6 +360,9 @@
       if (lhs.m_val.data.arr_int.size != rhs.m_val.data.arr_int.size) {
         return false;
       }
+      if (lhs.m_val.data.arr_int.size == 0) {
+        return true;
+      }
       return std::memcmp(lhs.m_val.data.arr_int.arr, rhs.m_val.data.arr_int.arr,
                          lhs.m_val.data.arr_int.size *
                              sizeof(lhs.m_val.data.arr_int.arr[0])) == 0;
@@ -332,6 +370,9 @@
       if (lhs.m_val.data.arr_float.size != rhs.m_val.data.arr_float.size) {
         return false;
       }
+      if (lhs.m_val.data.arr_float.size == 0) {
+        return true;
+      }
       return std::memcmp(lhs.m_val.data.arr_float.arr,
                          rhs.m_val.data.arr_float.arr,
                          lhs.m_val.data.arr_float.size *
@@ -340,11 +381,20 @@
       if (lhs.m_val.data.arr_double.size != rhs.m_val.data.arr_double.size) {
         return false;
       }
+      if (lhs.m_val.data.arr_double.size == 0) {
+        return true;
+      }
       return std::memcmp(lhs.m_val.data.arr_double.arr,
                          rhs.m_val.data.arr_double.arr,
                          lhs.m_val.data.arr_double.size *
                              sizeof(lhs.m_val.data.arr_double.arr[0])) == 0;
     case NT_STRING_ARRAY:
+      if (lhs.m_val.data.arr_string.size != rhs.m_val.data.arr_string.size) {
+        return false;
+      }
+      if (lhs.m_val.data.arr_string.size == 0) {
+        return true;
+      }
       return static_cast<StringArrayStorage*>(lhs.m_storage.get())->strings ==
              static_cast<StringArrayStorage*>(rhs.m_storage.get())->strings;
     default:
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/ValueCircularBuffer.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/ValueCircularBuffer.cpp
new file mode 100644
index 0000000..f611a33
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/ValueCircularBuffer.cpp
@@ -0,0 +1,17 @@
+// 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 "ValueCircularBuffer.h"
+
+using namespace nt;
+
+std::vector<Value> ValueCircularBuffer::ReadValue() {
+  std::vector<Value> rv;
+  rv.reserve(m_storage.size());
+  for (auto&& val : m_storage) {
+    rv.emplace_back(std::move(val));
+  }
+  m_storage.reset();
+  return rv;
+}
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/ValueCircularBuffer.h b/third_party/allwpilib/ntcore/src/main/native/cpp/ValueCircularBuffer.h
new file mode 100644
index 0000000..b80b5db
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/ValueCircularBuffer.h
@@ -0,0 +1,49 @@
+// 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 <utility>
+#include <vector>
+
+#include <wpi/circular_buffer.h>
+
+#include "Value_internal.h"
+#include "networktables/NetworkTableValue.h"
+#include "ntcore_cpp_types.h"
+
+namespace nt {
+
+class ValueCircularBuffer {
+ public:
+  explicit ValueCircularBuffer(size_t size) : m_storage{size} {}
+
+  template <class... Args>
+  void emplace_back(Args&&... args) {
+    m_storage.emplace_back(std::forward<Args...>(args...));
+  }
+
+  std::vector<Value> ReadValue();
+  template <ValidType T>
+  std::vector<Timestamped<typename TypeInfo<T>::Value>> Read();
+
+ private:
+  wpi::circular_buffer<Value> m_storage;
+};
+
+template <ValidType T>
+std::vector<Timestamped<typename TypeInfo<T>::Value>>
+ValueCircularBuffer::Read() {
+  std::vector<Timestamped<typename TypeInfo<T>::Value>> rv;
+  rv.reserve(m_storage.size());
+  for (auto&& val : m_storage) {
+    if (IsNumericConvertibleTo<T>(val) || IsType<T>(val)) {
+      rv.emplace_back(GetTimestamped<T, true>(val));
+    }
+  }
+  m_storage.reset();
+  return rv;
+}
+
+}  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/Value_internal.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/Value_internal.cpp
index 2003d31..c947bdc 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/Value_internal.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/Value_internal.cpp
@@ -26,19 +26,19 @@
       return newval;
     }
     case NT_INTEGER_ARRAY: {
-      Value newval = Value::MakeIntegerArray(GetNumericArrayAs<int64_t>(value),
-                                             value.time());
+      Value newval = Value::MakeIntegerArray(
+          GetNumericArrayAs<int64_t[]>(value), value.time());
       newval.SetServerTime(value.server_time());
       return newval;
     }
     case NT_FLOAT_ARRAY: {
-      Value newval =
-          Value::MakeFloatArray(GetNumericArrayAs<float>(value), value.time());
+      Value newval = Value::MakeFloatArray(GetNumericArrayAs<float[]>(value),
+                                           value.time());
       newval.SetServerTime(value.server_time());
       return newval;
     }
     case NT_DOUBLE_ARRAY: {
-      Value newval = Value::MakeDoubleArray(GetNumericArrayAs<double>(value),
+      Value newval = Value::MakeDoubleArray(GetNumericArrayAs<double[]>(value),
                                             value.time());
       newval.SetServerTime(value.server_time());
       return newval;
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/Value_internal.h b/third_party/allwpilib/ntcore/src/main/native/cpp/Value_internal.h
index 03532ac..8f2c8fb 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/Value_internal.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/Value_internal.h
@@ -4,20 +4,413 @@
 
 #pragma once
 
+#include <concepts>
 #include <cstring>
 #include <memory>
+#include <span>
 #include <string>
 #include <string_view>
+#include <type_traits>
 #include <vector>
 
 #include <wpi/MemAlloc.h>
 
 #include "networktables/NetworkTableValue.h"
 #include "ntcore_c.h"
+#include "ntcore_cpp_types.h"
 
 namespace nt {
 
-class Value;
+template <typename T>
+struct TypeInfo {};
+
+template <>
+struct TypeInfo<bool> {
+  static constexpr NT_Type kType = NT_BOOLEAN;
+
+  using Value = bool;
+  using View = bool;
+};
+
+template <>
+struct TypeInfo<int64_t> {
+  static constexpr NT_Type kType = NT_INTEGER;
+
+  using Value = int64_t;
+  using View = int64_t;
+};
+
+template <>
+struct TypeInfo<float> {
+  static constexpr NT_Type kType = NT_FLOAT;
+
+  using Value = float;
+  using View = float;
+};
+
+template <>
+struct TypeInfo<double> {
+  static constexpr NT_Type kType = NT_DOUBLE;
+
+  using Value = double;
+  using View = double;
+};
+
+template <>
+struct TypeInfo<std::string> {
+  static constexpr NT_Type kType = NT_STRING;
+
+  using Value = std::string;
+  using View = std::string_view;
+
+  using SmallRet = std::string_view;
+  using SmallElem = char;
+};
+
+template <>
+struct TypeInfo<std::string_view> : public TypeInfo<std::string> {};
+
+template <>
+struct TypeInfo<uint8_t[]> {
+  static constexpr NT_Type kType = NT_RAW;
+
+  using Value = std::vector<uint8_t>;
+  using View = std::span<const uint8_t>;
+
+  using SmallRet = std::span<uint8_t>;
+  using SmallElem = uint8_t;
+};
+
+template <>
+struct TypeInfo<std::vector<uint8_t>> : public TypeInfo<uint8_t[]> {};
+template <>
+struct TypeInfo<std::span<const uint8_t>> : public TypeInfo<uint8_t[]> {};
+
+template <>
+struct TypeInfo<bool[]> {
+  static constexpr NT_Type kType = NT_BOOLEAN_ARRAY;
+  using ElementType = bool;
+
+  using Value = std::vector<int>;
+  using View = std::span<const int>;
+
+  using SmallRet = std::span<int>;
+  using SmallElem = int;
+};
+
+template <>
+struct TypeInfo<int64_t[]> {
+  static constexpr NT_Type kType = NT_INTEGER_ARRAY;
+  using ElementType = int64_t;
+
+  using Value = std::vector<int64_t>;
+  using View = std::span<const int64_t>;
+
+  using SmallRet = std::span<int64_t>;
+  using SmallElem = int64_t;
+};
+
+template <>
+struct TypeInfo<std::vector<int64_t>> : public TypeInfo<int64_t[]> {};
+template <>
+struct TypeInfo<std::span<const int64_t>> : public TypeInfo<int64_t[]> {};
+
+template <>
+struct TypeInfo<float[]> {
+  static constexpr NT_Type kType = NT_FLOAT_ARRAY;
+  using ElementType = float;
+
+  using Value = std::vector<float>;
+  using View = std::span<const float>;
+
+  using SmallRet = std::span<float>;
+  using SmallElem = float;
+};
+
+template <>
+struct TypeInfo<std::vector<float>> : public TypeInfo<float[]> {};
+template <>
+struct TypeInfo<std::span<const float>> : public TypeInfo<float[]> {};
+
+template <>
+struct TypeInfo<double[]> {
+  static constexpr NT_Type kType = NT_DOUBLE_ARRAY;
+  using ElementType = double;
+
+  using Value = std::vector<double>;
+  using View = std::span<const double>;
+
+  using SmallRet = std::span<double>;
+  using SmallElem = double;
+};
+
+template <>
+struct TypeInfo<std::vector<double>> : public TypeInfo<double[]> {};
+template <>
+struct TypeInfo<std::span<const double>> : public TypeInfo<double[]> {};
+
+template <>
+struct TypeInfo<std::string[]> {
+  static constexpr NT_Type kType = NT_STRING_ARRAY;
+  using ElementType = std::string;
+
+  using Value = std::vector<std::string>;
+  using View = std::span<const std::string>;
+};
+
+template <>
+struct TypeInfo<std::vector<std::string>> : public TypeInfo<std::string[]> {};
+template <>
+struct TypeInfo<std::span<const std::string>> : public TypeInfo<std::string[]> {
+};
+
+template <typename T>
+concept ValidType = requires {
+  { TypeInfo<std::remove_cvref_t<T>>::kType } -> std::convertible_to<NT_Type>;
+  typename TypeInfo<std::remove_cvref_t<T>>::Value;
+  typename TypeInfo<std::remove_cvref_t<T>>::View;
+};
+
+static_assert(ValidType<bool>);
+static_assert(!ValidType<uint8_t>);
+static_assert(ValidType<uint8_t[]>);
+static_assert(ValidType<std::vector<uint8_t>>);
+
+template <ValidType T, NT_Type type>
+constexpr bool IsNTType = TypeInfo<std::remove_cvref_t<T>>::kType == type;
+
+static_assert(IsNTType<bool, NT_BOOLEAN>);
+static_assert(!IsNTType<bool, NT_DOUBLE>);
+
+template <typename T>
+concept ArrayType =
+    requires { typename TypeInfo<std::remove_cvref_t<T>>::ElementType; };
+
+static_assert(ArrayType<std::string[]>);
+static_assert(!ArrayType<uint8_t[]>);
+
+template <typename T>
+concept SmallArrayType = requires {
+  typename TypeInfo<std::remove_cvref_t<T>>::SmallRet;
+  typename TypeInfo<std::remove_cvref_t<T>>::SmallElem;
+};
+
+static_assert(SmallArrayType<float[]>);
+static_assert(!SmallArrayType<std::string[]>);
+
+template <typename T>
+concept NumericType =
+    IsNTType<T, NT_INTEGER> || IsNTType<T, NT_FLOAT> || IsNTType<T, NT_DOUBLE>;
+
+static_assert(NumericType<int64_t>);
+static_assert(NumericType<float>);
+static_assert(NumericType<double>);
+static_assert(!NumericType<bool>);
+static_assert(!NumericType<std::string>);
+static_assert(!NumericType<int64_t[]>);
+static_assert(!NumericType<double[]>);
+static_assert(!NumericType<bool[]>);
+static_assert(!NumericType<uint8_t[]>);
+
+template <typename T>
+concept NumericArrayType =
+    ArrayType<T> &&
+    NumericType<typename TypeInfo<std::remove_cvref_t<T>>::ElementType>;
+
+static_assert(NumericArrayType<int64_t[]>);
+static_assert(NumericArrayType<float[]>);
+static_assert(NumericArrayType<double[]>);
+static_assert(!NumericArrayType<bool[]>);
+
+template <NumericType T>
+inline typename TypeInfo<T>::Value GetNumericAs(const Value& value) {
+  if (value.IsInteger()) {
+    return static_cast<typename TypeInfo<T>::Value>(value.GetInteger());
+  } else if (value.IsFloat()) {
+    return static_cast<typename TypeInfo<T>::Value>(value.GetFloat());
+  } else if (value.IsDouble()) {
+    return static_cast<typename TypeInfo<T>::Value>(value.GetDouble());
+  } else {
+    return {};
+  }
+}
+
+template <NumericArrayType T>
+typename TypeInfo<T>::Value GetNumericArrayAs(const Value& value) {
+  if (value.IsIntegerArray()) {
+    auto arr = value.GetIntegerArray();
+    return {arr.begin(), arr.end()};
+  } else if (value.IsFloatArray()) {
+    auto arr = value.GetFloatArray();
+    return {arr.begin(), arr.end()};
+  } else if (value.IsDoubleArray()) {
+    auto arr = value.GetDoubleArray();
+    return {arr.begin(), arr.end()};
+  } else {
+    return {};
+  }
+}
+
+template <ValidType T>
+inline bool IsType(const Value& value) {
+  return value.type() == TypeInfo<T>::kType;
+}
+
+template <ValidType T>
+inline bool IsNumericConvertibleTo(const Value& value) {
+  if constexpr (NumericType<T>) {
+    return value.IsInteger() || value.IsFloat() || value.IsDouble();
+  } else if constexpr (NumericArrayType<T>) {
+    return value.IsIntegerArray() || value.IsFloatArray() ||
+           value.IsDoubleArray();
+  } else {
+    return false;
+  }
+}
+
+template <ValidType T>
+inline typename TypeInfo<T>::View GetValueView(const Value& value) {
+  if constexpr (IsNTType<T, NT_BOOLEAN>) {
+    return value.GetBoolean();
+  } else if constexpr (IsNTType<T, NT_INTEGER>) {
+    return value.GetInteger();
+  } else if constexpr (IsNTType<T, NT_FLOAT>) {
+    return value.GetFloat();
+  } else if constexpr (IsNTType<T, NT_DOUBLE>) {
+    return value.GetDouble();
+  } else if constexpr (IsNTType<T, NT_STRING>) {
+    return value.GetString();
+  } else if constexpr (IsNTType<T, NT_RAW>) {
+    return value.GetRaw();
+  } else if constexpr (IsNTType<T, NT_BOOLEAN_ARRAY>) {
+    return value.GetBooleanArray();
+  } else if constexpr (IsNTType<T, NT_INTEGER_ARRAY>) {
+    return value.GetIntegerArray();
+  } else if constexpr (IsNTType<T, NT_FLOAT_ARRAY>) {
+    return value.GetFloatArray();
+  } else if constexpr (IsNTType<T, NT_DOUBLE_ARRAY>) {
+    return value.GetDoubleArray();
+  } else if constexpr (IsNTType<T, NT_STRING_ARRAY>) {
+    return value.GetStringArray();
+  }
+}
+
+template <ValidType T>
+inline Value MakeValue(typename TypeInfo<T>::View value, int64_t time) {
+  if constexpr (IsNTType<T, NT_BOOLEAN>) {
+    return Value::MakeBoolean(value, time);
+  } else if constexpr (IsNTType<T, NT_INTEGER>) {
+    return Value::MakeInteger(value, time);
+  } else if constexpr (IsNTType<T, NT_FLOAT>) {
+    return Value::MakeFloat(value, time);
+  } else if constexpr (IsNTType<T, NT_DOUBLE>) {
+    return Value::MakeDouble(value, time);
+  } else if constexpr (IsNTType<T, NT_STRING>) {
+    return Value::MakeString(value, time);
+  } else if constexpr (IsNTType<T, NT_RAW>) {
+    return Value::MakeRaw(value, time);
+  } else if constexpr (IsNTType<T, NT_BOOLEAN_ARRAY>) {
+    return Value::MakeBooleanArray(value, time);
+  } else if constexpr (IsNTType<T, NT_INTEGER_ARRAY>) {
+    return Value::MakeIntegerArray(value, time);
+  } else if constexpr (IsNTType<T, NT_FLOAT_ARRAY>) {
+    return Value::MakeFloatArray(value, time);
+  } else if constexpr (IsNTType<T, NT_DOUBLE_ARRAY>) {
+    return Value::MakeDoubleArray(value, time);
+  } else if constexpr (IsNTType<T, NT_STRING_ARRAY>) {
+    return Value::MakeStringArray(value, time);
+  }
+}
+
+template <ValidType T>
+  requires ArrayType<T> || IsNTType<T, NT_STRING> || IsNTType<T, NT_RAW>
+inline Value MakeValue(typename TypeInfo<T>::Value&& value, int64_t time) {
+  if constexpr (IsNTType<T, NT_STRING>) {
+    return Value::MakeString(value, time);
+  } else if constexpr (IsNTType<T, NT_RAW>) {
+    return Value::MakeRaw(value, time);
+  } else if constexpr (IsNTType<T, NT_BOOLEAN_ARRAY>) {
+    return Value::MakeBooleanArray(value, time);
+  } else if constexpr (IsNTType<T, NT_INTEGER_ARRAY>) {
+    return Value::MakeIntegerArray(value, time);
+  } else if constexpr (IsNTType<T, NT_FLOAT_ARRAY>) {
+    return Value::MakeFloatArray(value, time);
+  } else if constexpr (IsNTType<T, NT_DOUBLE_ARRAY>) {
+    return Value::MakeDoubleArray(value, time);
+  } else if constexpr (IsNTType<T, NT_STRING_ARRAY>) {
+    return Value::MakeStringArray(value, time);
+  }
+}
+
+template <ValidType T>
+inline typename TypeInfo<T>::Value CopyValue(typename TypeInfo<T>::View value) {
+  if constexpr (ArrayType<T> || IsNTType<T, NT_RAW>) {
+    return {value.begin(), value.end()};
+  } else if constexpr (IsNTType<T, NT_STRING>) {
+    return std::string{value};
+  } else {
+    return value;
+  }
+}
+
+template <SmallArrayType T>
+inline typename TypeInfo<T>::SmallRet CopyValue(
+    typename TypeInfo<T>::View arr,
+    wpi::SmallVectorImpl<typename TypeInfo<T>::SmallElem>& buf) {
+  buf.assign(arr.begin(), arr.end());
+  return {buf.data(), buf.size()};
+}
+
+template <ValidType T, bool ConvertNumeric>
+inline typename TypeInfo<T>::Value GetValueCopy(const Value& value) {
+  if constexpr (ConvertNumeric && NumericType<T>) {
+    return GetNumericAs<T>(value);
+  } else if constexpr (ConvertNumeric && NumericArrayType<T>) {
+    return GetNumericArrayAs<T>(value);
+  } else {
+    return CopyValue<T>(GetValueView<T>(value));
+  }
+}
+
+template <SmallArrayType T, bool ConvertNumeric>
+inline typename TypeInfo<T>::SmallRet GetValueCopy(
+    const Value& value,
+    wpi::SmallVectorImpl<typename TypeInfo<T>::SmallElem>& buf) {
+  if constexpr (ConvertNumeric && NumericArrayType<T>) {
+    if (value.IsIntegerArray()) {
+      auto arr = value.GetIntegerArray();
+      buf.assign(arr.begin(), arr.end());
+      return {buf.data(), buf.size()};
+    } else if (value.IsFloatArray()) {
+      auto arr = value.GetFloatArray();
+      buf.assign(arr.begin(), arr.end());
+      return {buf.data(), buf.size()};
+    } else if (value.IsDoubleArray()) {
+      auto arr = value.GetDoubleArray();
+      buf.assign(arr.begin(), arr.end());
+      return {buf.data(), buf.size()};
+    } else {
+      return {};
+    }
+  } else {
+    return CopyValue<T>(GetValueView<T>(value), buf);
+  }
+}
+
+template <ValidType T, bool ConvertNumeric>
+inline Timestamped<typename TypeInfo<T>::Value> GetTimestamped(
+    const Value& value) {
+  return {value.time(), value.server_time(),
+          GetValueCopy<T, ConvertNumeric>(value)};
+}
+
+template <SmallArrayType T, bool ConvertNumeric>
+inline Timestamped<typename TypeInfo<T>::SmallRet> GetTimestamped(
+    const Value& value,
+    wpi::SmallVectorImpl<typename TypeInfo<T>::SmallElem>& buf) {
+  return {value.time(), value.server_time(),
+          GetValueCopy<T, ConvertNumeric>(value, buf)};
+}
 
 template <typename T>
 inline void ConvertToC(const T& in, T* out) {
@@ -57,35 +450,6 @@
   return out;
 }
 
-template <typename T>
-T GetNumericAs(const Value& value) {
-  if (value.IsInteger()) {
-    return static_cast<T>(value.GetInteger());
-  } else if (value.IsFloat()) {
-    return static_cast<T>(value.GetFloat());
-  } else if (value.IsDouble()) {
-    return static_cast<T>(value.GetDouble());
-  } else {
-    return {};
-  }
-}
-
-template <typename T>
-std::vector<T> GetNumericArrayAs(const Value& value) {
-  if (value.IsIntegerArray()) {
-    auto arr = value.GetIntegerArray();
-    return {arr.begin(), arr.end()};
-  } else if (value.IsFloatArray()) {
-    auto arr = value.GetFloatArray();
-    return {arr.begin(), arr.end()};
-  } else if (value.IsDoubleArray()) {
-    auto arr = value.GetDoubleArray();
-    return {arr.begin(), arr.end()};
-  } else {
-    return {};
-  }
-}
-
 Value ConvertNumericValue(const Value& value, NT_Type type);
 
 }  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/VectorSet.h b/third_party/allwpilib/ntcore/src/main/native/cpp/VectorSet.h
new file mode 100644
index 0000000..9e13490
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/VectorSet.h
@@ -0,0 +1,21 @@
+// 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 <vector>
+
+namespace nt {
+
+// Utility wrapper for making a set-like vector
+template <typename T>
+class VectorSet : public std::vector<T> {
+ public:
+  using iterator = typename std::vector<T>::iterator;
+  void Add(T value) { this->push_back(value); }
+  // returns true if element was present
+  bool Remove(T value) { return std::erase(*this, value) != 0; }
+};
+
+}  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp
index b868604..14b5df1 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp
@@ -31,7 +31,6 @@
 //
 
 // Used for callback.
-static JavaVM* jvm = nullptr;
 static JClass booleanCls;
 static JClass connectionInfoCls;
 static JClass doubleCls;
@@ -72,8 +71,6 @@
 extern "C" {
 
 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
-  jvm = vm;
-
   JNIEnv* env;
   if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
     return JNI_ERR;
@@ -114,7 +111,6 @@
     c.cls->free(env);
   }
   nt::JNI_UnloadTypes(env);
-  jvm = nullptr;
 }
 
 }  // extern "C"
@@ -723,7 +719,7 @@
 {
   wpi::json j;
   try {
-    j = wpi::json::parse(JStringRef{env, value});
+    j = wpi::json::parse(std::string_view{JStringRef{env, value}});
   } catch (wpi::json::parse_error& err) {
     illegalArgEx.Throw(
         env, fmt::format("could not parse value JSON: {}", err.what()));
@@ -767,7 +763,7 @@
 {
   wpi::json j;
   try {
-    j = wpi::json::parse(JStringRef{env, properties});
+    j = wpi::json::parse(std::string_view{JStringRef{env, properties}});
   } catch (wpi::json::parse_error& err) {
     illegalArgEx.Throw(
         env, fmt::format("could not parse properties JSON: {}", err.what()));
@@ -832,7 +828,7 @@
 {
   wpi::json j;
   try {
-    j = wpi::json::parse(JStringRef{env, properties});
+    j = wpi::json::parse(std::string_view{JStringRef{env, properties}});
   } catch (wpi::json::parse_error& err) {
     illegalArgEx.Throw(
         env, fmt::format("could not parse properties JSON: {}", err.what()));
@@ -1262,7 +1258,7 @@
                        "serverNames and ports arrays must be the same size");
     return;
   }
-  jint* portInts = env->GetIntArrayElements(ports, nullptr);
+  JSpan<const jint> portInts{env, ports};
   if (!portInts) {
     return;
   }
@@ -1282,7 +1278,6 @@
     servers.emplace_back(
         std::make_pair(std::string_view{names.back()}, portInts[i]));
   }
-  env->ReleaseIntArrayElements(ports, portInts, JNI_ABORT);
   nt::SetServer(inst, servers);
 }
 
@@ -1300,6 +1295,18 @@
 
 /*
  * Class:     edu_wpi_first_networktables_NetworkTablesJNI
+ * Method:    disconnect
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_networktables_NetworkTablesJNI_disconnect
+  (JNIEnv* env, jclass, jint inst)
+{
+  nt::Disconnect(inst);
+}
+
+/*
+ * Class:     edu_wpi_first_networktables_NetworkTablesJNI
  * Method:    startDSClient
  * Signature: (II)V
  */
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/ClientImpl.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net/ClientImpl.cpp
index 2efbb29..309b01a 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/ClientImpl.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/ClientImpl.cpp
@@ -10,7 +10,6 @@
 #include <variant>
 
 #include <fmt/format.h>
-#include <wpi/DenseMap.h>
 #include <wpi/Logger.h>
 #include <wpi/raw_ostream.h>
 #include <wpi/timestamp.h>
@@ -19,94 +18,15 @@
 #include "Log.h"
 #include "Message.h"
 #include "NetworkInterface.h"
-#include "PubSubOptions.h"
 #include "WireConnection.h"
-#include "WireDecoder.h"
 #include "WireEncoder.h"
+#include "net/NetworkOutgoingQueue.h"
 #include "networktables/NetworkTableValue.h"
 
 using namespace nt;
 using namespace nt::net;
 
-static constexpr uint32_t kMinPeriodMs = 5;
-
-// maximum number of times the wire can be not ready to send another
-// transmission before we close the connection
-static constexpr int kWireMaxNotReady = 10;
-
-namespace {
-
-struct PublisherData {
-  NT_Publisher handle;
-  PubSubOptionsImpl options;
-  // in options as double, but copy here as integer; rounded to the nearest
-  // 10 ms
-  uint32_t periodMs;
-  uint64_t nextSendMs{0};
-  std::vector<Value> outValues;  // outgoing values
-};
-
-class CImpl : public ServerMessageHandler {
- public:
-  CImpl(uint64_t curTimeMs, int inst, WireConnection& wire, wpi::Logger& logger,
-        std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
-            timeSyncUpdated,
-        std::function<void(uint32_t repeatMs)> setPeriodic);
-
-  void ProcessIncomingBinary(std::span<const uint8_t> data);
-  void HandleLocal(std::vector<ClientMessage>&& msgs);
-  bool SendControl(uint64_t curTimeMs);
-  void SendValues(uint64_t curTimeMs);
-  void SendInitialValues();
-  bool CheckNetworkReady();
-
-  // ServerMessageHandler interface
-  void ServerAnnounce(std::string_view name, int64_t id,
-                      std::string_view typeStr, const wpi::json& properties,
-                      std::optional<int64_t> pubuid) final;
-  void ServerUnannounce(std::string_view name, int64_t id) final;
-  void ServerPropertiesUpdate(std::string_view name, const wpi::json& update,
-                              bool ack) final;
-
-  void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
-               std::string_view name, std::string_view typeStr,
-               const wpi::json& properties, const PubSubOptionsImpl& options);
-  bool Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle);
-  void SetValue(NT_Publisher pubHandle, const Value& value);
-
-  int m_inst;
-  WireConnection& m_wire;
-  wpi::Logger& m_logger;
-  LocalInterface* m_local{nullptr};
-  std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
-      m_timeSyncUpdated;
-  std::function<void(uint32_t repeatMs)> m_setPeriodic;
-
-  // indexed by publisher index
-  std::vector<std::unique_ptr<PublisherData>> m_publishers;
-
-  // indexed by server-provided topic id
-  wpi::DenseMap<int64_t, NT_Topic> m_topicMap;
-
-  // timestamp handling
-  static constexpr uint32_t kPingIntervalMs = 3000;
-  uint64_t m_nextPingTimeMs{0};
-  uint32_t m_rtt2Us{UINT32_MAX};
-  bool m_haveTimeOffset{false};
-  int64_t m_serverTimeOffsetUs{0};
-
-  // periodic sweep handling
-  uint32_t m_periodMs{kPingIntervalMs + 10};
-  uint64_t m_lastSendMs{0};
-  int m_notReadyCount{0};
-
-  // outgoing queue
-  std::vector<ClientMessage> m_outgoing;
-};
-
-}  // namespace
-
-CImpl::CImpl(
+ClientImpl::ClientImpl(
     uint64_t curTimeMs, int inst, WireConnection& wire, wpi::Logger& logger,
     std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
         timeSyncUpdated,
@@ -116,17 +36,21 @@
       m_logger{logger},
       m_timeSyncUpdated{std::move(timeSyncUpdated)},
       m_setPeriodic{std::move(setPeriodic)},
-      m_nextPingTimeMs{curTimeMs + kPingIntervalMs} {
+      m_ping{wire},
+      m_nextPingTimeMs{curTimeMs + (wire.GetVersion() >= 0x0401
+                                        ? NetworkPing::kPingIntervalMs
+                                        : kRttIntervalMs)},
+      m_outgoing{wire, false} {
   // immediately send RTT ping
-  auto out = m_wire.SendBinary();
   auto now = wpi::Now();
   DEBUG4("Sending initial RTT ping {}", now);
-  WireEncodeBinary(out.Add(), -1, 0, Value::MakeInteger(now));
-  m_wire.Flush();
+  m_wire.SendBinary(
+      [&](auto& os) { WireEncodeBinary(os, -1, 0, Value::MakeInteger(now)); });
   m_setPeriodic(m_periodMs);
 }
 
-void CImpl::ProcessIncomingBinary(std::span<const uint8_t> data) {
+void ClientImpl::ProcessIncomingBinary(uint64_t curTimeMs,
+                                       std::span<const uint8_t> data) {
   for (;;) {
     if (data.empty()) {
       break;
@@ -136,29 +60,34 @@
     int64_t id;
     Value value;
     std::string error;
-    if (!WireDecodeBinary(&data, &id, &value, &error, -m_serverTimeOffsetUs)) {
-      ERROR("binary decode error: {}", error);
+    if (!WireDecodeBinary(&data, &id, &value, &error,
+                          -m_outgoing.GetTimeOffset())) {
+      ERR("binary decode error: {}", error);
       break;  // FIXME
     }
     DEBUG4("BinaryMessage({})", id);
 
-    // handle RTT ping response
-    if (id == -1) {
+    // handle RTT ping response (only use first one)
+    if (!m_haveTimeOffset && id == -1) {
       if (!value.IsInteger()) {
-        WARNING("RTT ping response with non-integer type {}",
-                static_cast<int>(value.type()));
+        WARN("RTT ping response with non-integer type {}",
+             static_cast<int>(value.type()));
         continue;
       }
       DEBUG4("RTT ping response time {} value {}", value.time(),
              value.GetInteger());
+      if (m_wire.GetVersion() < 0x0401) {
+        m_pongTimeMs = curTimeMs;
+      }
       int64_t now = wpi::Now();
       int64_t rtt2 = (now - value.GetInteger()) / 2;
       if (rtt2 < m_rtt2Us) {
         m_rtt2Us = rtt2;
-        m_serverTimeOffsetUs = value.server_time() + rtt2 - now;
-        DEBUG3("Time offset: {}", m_serverTimeOffsetUs);
+        int64_t serverTimeOffsetUs = value.server_time() + rtt2 - now;
+        DEBUG3("Time offset: {}", serverTimeOffsetUs);
+        m_outgoing.SetTimeOffset(serverTimeOffsetUs);
         m_haveTimeOffset = true;
-        m_timeSyncUpdated(m_serverTimeOffsetUs, m_rtt2Us, true);
+        m_timeSyncUpdated(serverTimeOffsetUs, m_rtt2Us, true);
       }
       continue;
     }
@@ -166,7 +95,7 @@
     // otherwise it's a value message, get the local topic handle for it
     auto topicIt = m_topicMap.find(id);
     if (topicIt == m_topicMap.end()) {
-      WARNING("received unknown id {}", id);
+      WARN("received unknown id {}", id);
       continue;
     }
 
@@ -177,152 +106,77 @@
   }
 }
 
-void CImpl::HandleLocal(std::vector<ClientMessage>&& msgs) {
+void ClientImpl::HandleLocal(std::vector<ClientMessage>&& msgs) {
   DEBUG4("HandleLocal()");
   for (auto&& elem : msgs) {
     // common case is value
     if (auto msg = std::get_if<ClientValueMsg>(&elem.contents)) {
       SetValue(msg->pubHandle, msg->value);
-      // setvalue puts on individual publish outgoing queues
     } else if (auto msg = std::get_if<PublishMsg>(&elem.contents)) {
       Publish(msg->pubHandle, msg->topicHandle, msg->name, msg->typeStr,
               msg->properties, msg->options);
-      m_outgoing.emplace_back(std::move(elem));
+      m_outgoing.SendMessage(msg->pubHandle, std::move(elem));
     } else if (auto msg = std::get_if<UnpublishMsg>(&elem.contents)) {
       if (Unpublish(msg->pubHandle, msg->topicHandle)) {
-        m_outgoing.emplace_back(std::move(elem));
+        m_outgoing.SendMessage(msg->pubHandle, std::move(elem));
       }
     } else {
-      m_outgoing.emplace_back(std::move(elem));
+      m_outgoing.SendMessage(0, std::move(elem));
     }
   }
 }
 
-bool CImpl::SendControl(uint64_t curTimeMs) {
-  DEBUG4("SendControl({})", curTimeMs);
+void ClientImpl::SendOutgoing(uint64_t curTimeMs, bool flush) {
+  DEBUG4("SendOutgoing({}, {})", curTimeMs, flush);
 
-  // rate limit sends
-  if (curTimeMs < (m_lastSendMs + kMinPeriodMs)) {
-    return false;
-  }
-
-  // start a timestamp RTT ping if it's time to do one
-  if (curTimeMs >= m_nextPingTimeMs) {
-    if (!CheckNetworkReady()) {
-      return false;
+  if (m_wire.GetVersion() >= 0x0401) {
+    // Use WS pings
+    if (!m_ping.Send(curTimeMs)) {
+      return;
     }
-    auto now = wpi::Now();
-    DEBUG4("Sending RTT ping {}", now);
-    WireEncodeBinary(m_wire.SendBinary().Add(), -1, 0, Value::MakeInteger(now));
-    // drift isn't critical here, so just go from current time
-    m_nextPingTimeMs = curTimeMs + kPingIntervalMs;
-  }
-
-  if (!m_outgoing.empty()) {
-    if (!CheckNetworkReady()) {
-      return false;
-    }
-    auto writer = m_wire.SendText();
-    for (auto&& msg : m_outgoing) {
-      auto& stream = writer.Add();
-      if (!WireEncodeText(stream, msg)) {
-        // shouldn't happen, but just in case...
-        stream << "{}";
+  } else {
+    // Use RTT pings; it's unsafe to use WS pings due to bugs in WS message
+    // fragmentation in earlier NT4 implementations
+    if (curTimeMs >= m_nextPingTimeMs) {
+      // if we didn't receive a response to our last ping, disconnect
+      if (m_nextPingTimeMs != 0 && m_pongTimeMs == 0) {
+        m_wire.Disconnect("connection timed out");
+        return;
       }
+
+      auto now = wpi::Now();
+      DEBUG4("Sending RTT ping {}", now);
+      m_wire.SendBinary([&](auto& os) {
+        WireEncodeBinary(os, -1, 0, Value::MakeInteger(now));
+      });
+      // drift isn't critical here, so just go from current time
+      m_nextPingTimeMs = curTimeMs + kRttIntervalMs;
+      m_pongTimeMs = 0;
     }
-    m_outgoing.resize(0);
   }
 
-  m_lastSendMs = curTimeMs;
-  return true;
-}
-
-void CImpl::SendValues(uint64_t curTimeMs) {
-  DEBUG4("SendValues({})", curTimeMs);
-
-  // can't send value updates until we have a RTT
+  // wait until we have a RTT measurement before sending messages
   if (!m_haveTimeOffset) {
     return;
   }
 
-  // ensure all control messages are sent ahead of value updates
-  if (!SendControl(curTimeMs)) {
-    return;
-  }
-
-  // send any pending updates due to be sent
-  bool checkedNetwork = false;
-  auto writer = m_wire.SendBinary();
-  for (auto&& pub : m_publishers) {
-    if (pub && !pub->outValues.empty() && curTimeMs >= pub->nextSendMs) {
-      for (auto&& val : pub->outValues) {
-        if (!checkedNetwork) {
-          if (!CheckNetworkReady()) {
-            return;
-          }
-          checkedNetwork = true;
-        }
-        DEBUG4("Sending {} value time={} server_time={} st_off={}", pub->handle,
-               val.time(), val.server_time(), m_serverTimeOffsetUs);
-        int64_t time = val.time();
-        if (time != 0) {
-          time += m_serverTimeOffsetUs;
-        }
-        WireEncodeBinary(writer.Add(), Handle{pub->handle}.GetIndex(), time,
-                         val);
-      }
-      pub->outValues.resize(0);
-      pub->nextSendMs = curTimeMs + pub->periodMs;
-    }
-  }
+  m_outgoing.SendOutgoing(curTimeMs, flush);
 }
 
-void CImpl::SendInitialValues() {
-  DEBUG4("SendInitialValues()");
-
-  // ensure all control messages are sent ahead of value updates
-  if (!SendControl(0)) {
-    return;
+void ClientImpl::UpdatePeriodic() {
+  if (m_periodMs < kMinPeriodMs) {
+    m_periodMs = kMinPeriodMs;
   }
-
-  // only send time=0 values (as we don't have a RTT yet)
-  auto writer = m_wire.SendBinary();
-  for (auto&& pub : m_publishers) {
-    if (pub && !pub->outValues.empty()) {
-      bool sent = false;
-      for (auto&& val : pub->outValues) {
-        if (val.server_time() == 0) {
-          DEBUG4("Sending {} value time={} server_time={}", pub->handle,
-                 val.time(), val.server_time());
-          WireEncodeBinary(writer.Add(), Handle{pub->handle}.GetIndex(), 0,
-                           val);
-          sent = true;
-        }
-      }
-      if (sent) {
-        std::erase_if(pub->outValues,
-                      [](const auto& v) { return v.server_time() == 0; });
-      }
-    }
+  if (m_periodMs > kMaxPeriodMs) {
+    m_periodMs = kMaxPeriodMs;
   }
+  m_setPeriodic(m_periodMs);
 }
 
-bool CImpl::CheckNetworkReady() {
-  if (!m_wire.Ready()) {
-    ++m_notReadyCount;
-    if (m_notReadyCount > kWireMaxNotReady) {
-      m_wire.Disconnect("transmit stalled");
-    }
-    return false;
-  }
-  m_notReadyCount = 0;
-  return true;
-}
-
-void CImpl::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
-                    std::string_view name, std::string_view typeStr,
-                    const wpi::json& properties,
-                    const PubSubOptionsImpl& options) {
+void ClientImpl::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
+                         std::string_view name, std::string_view typeStr,
+                         const wpi::json& properties,
+                         const PubSubOptionsImpl& options) {
   unsigned int index = Handle{pubHandle}.GetIndex();
   if (index >= m_publishers.size()) {
     m_publishers.resize(index + 1);
@@ -337,74 +191,53 @@
   if (publisher->periodMs < kMinPeriodMs) {
     publisher->periodMs = kMinPeriodMs;
   }
+  m_outgoing.SetPeriod(pubHandle, publisher->periodMs);
 
   // update period
-  m_periodMs = std::gcd(m_periodMs, publisher->periodMs);
-  if (m_periodMs < kMinPeriodMs) {
-    m_periodMs = kMinPeriodMs;
-  }
-  m_setPeriodic(m_periodMs);
+  m_periodMs = UpdatePeriodCalc(m_periodMs, publisher->periodMs);
+  UpdatePeriodic();
 }
 
-bool CImpl::Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle) {
+bool ClientImpl::Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle) {
   unsigned int index = Handle{pubHandle}.GetIndex();
   if (index >= m_publishers.size()) {
     return false;
   }
   bool doSend = true;
-  if (m_publishers[index]) {
-    // Look through outgoing queue to see if the publish hasn't been sent yet;
-    // if it hasn't, delete it and don't send the server a message.
-    // The outgoing queue doesn't contain values; those are deleted with the
-    // publisher object.
-    auto it = std::find_if(
-        m_outgoing.begin(), m_outgoing.end(), [&](const auto& elem) {
-          if (auto msg = std::get_if<PublishMsg>(&elem.contents)) {
-            return msg->pubHandle == pubHandle;
-          }
-          return false;
-        });
-    if (it != m_outgoing.end()) {
-      m_outgoing.erase(it);
-      doSend = false;
-    }
-  }
   m_publishers[index].reset();
 
   // loop over all publishers to update period
-  m_periodMs = kPingIntervalMs + 10;
+  m_periodMs = kMaxPeriodMs;
   for (auto&& pub : m_publishers) {
     if (pub) {
       m_periodMs = std::gcd(m_periodMs, pub->periodMs);
     }
   }
-  if (m_periodMs < kMinPeriodMs) {
-    m_periodMs = kMinPeriodMs;
-  }
-  m_setPeriodic(m_periodMs);
+  UpdatePeriodic();
+
+  // remove from outgoing handle map
+  m_outgoing.EraseHandle(pubHandle);
 
   return doSend;
 }
 
-void CImpl::SetValue(NT_Publisher pubHandle, const Value& value) {
-  DEBUG4("SetValue({}, time={}, server_time={}, st_off={})", pubHandle,
-         value.time(), value.server_time(), m_serverTimeOffsetUs);
+void ClientImpl::SetValue(NT_Publisher pubHandle, const Value& value) {
+  DEBUG4("SetValue({}, time={}, server_time={})", pubHandle, value.time(),
+         value.server_time());
   unsigned int index = Handle{pubHandle}.GetIndex();
   if (index >= m_publishers.size() || !m_publishers[index]) {
     return;
   }
   auto& publisher = *m_publishers[index];
-  if (publisher.outValues.empty() || publisher.options.sendAll) {
-    publisher.outValues.emplace_back(value);
-  } else {
-    publisher.outValues.back() = value;
-  }
+  m_outgoing.SendValue(
+      pubHandle, value,
+      publisher.options.sendAll ? ValueSendMode::kAll : ValueSendMode::kNormal);
 }
 
-void CImpl::ServerAnnounce(std::string_view name, int64_t id,
-                           std::string_view typeStr,
-                           const wpi::json& properties,
-                           std::optional<int64_t> pubuid) {
+void ClientImpl::ServerAnnounce(std::string_view name, int64_t id,
+                                std::string_view typeStr,
+                                const wpi::json& properties,
+                                std::optional<int64_t> pubuid) {
   DEBUG4("ServerAnnounce({}, {}, {})", name, id, typeStr);
   assert(m_local);
   NT_Publisher pubHandle{0};
@@ -415,75 +248,25 @@
       m_local->NetworkAnnounce(name, typeStr, properties, pubHandle);
 }
 
-void CImpl::ServerUnannounce(std::string_view name, int64_t id) {
+void ClientImpl::ServerUnannounce(std::string_view name, int64_t id) {
   DEBUG4("ServerUnannounce({}, {})", name, id);
   assert(m_local);
   m_local->NetworkUnannounce(name);
   m_topicMap.erase(id);
 }
 
-void CImpl::ServerPropertiesUpdate(std::string_view name,
-                                   const wpi::json& update, bool ack) {
+void ClientImpl::ServerPropertiesUpdate(std::string_view name,
+                                        const wpi::json& update, bool ack) {
   DEBUG4("ServerProperties({}, {}, {})", name, update.dump(), ack);
   assert(m_local);
   m_local->NetworkPropertiesUpdate(name, update, ack);
 }
 
-class ClientImpl::Impl final : public CImpl {
- public:
-  Impl(uint64_t curTimeMs, int inst, WireConnection& wire, wpi::Logger& logger,
-       std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
-           timeSyncUpdated,
-       std::function<void(uint32_t repeatMs)> setPeriodic)
-      : CImpl{curTimeMs,
-              inst,
-              wire,
-              logger,
-              std::move(timeSyncUpdated),
-              std::move(setPeriodic)} {}
-};
-
-ClientImpl::ClientImpl(
-    uint64_t curTimeMs, int inst, WireConnection& wire, wpi::Logger& logger,
-    std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
-        timeSyncUpdated,
-    std::function<void(uint32_t repeatMs)> setPeriodic)
-    : m_impl{std::make_unique<Impl>(curTimeMs, inst, wire, logger,
-                                    std::move(timeSyncUpdated),
-                                    std::move(setPeriodic))} {}
-
-ClientImpl::~ClientImpl() = default;
-
 void ClientImpl::ProcessIncomingText(std::string_view data) {
-  if (!m_impl->m_local) {
+  if (!m_local) {
     return;
   }
-  WireDecodeText(data, *m_impl, m_impl->m_logger);
+  WireDecodeText(data, *this, m_logger);
 }
 
-void ClientImpl::ProcessIncomingBinary(std::span<const uint8_t> data) {
-  m_impl->ProcessIncomingBinary(data);
-}
-
-void ClientImpl::HandleLocal(std::vector<ClientMessage>&& msgs) {
-  m_impl->HandleLocal(std::move(msgs));
-}
-
-void ClientImpl::SendControl(uint64_t curTimeMs) {
-  m_impl->SendControl(curTimeMs);
-  m_impl->m_wire.Flush();
-}
-
-void ClientImpl::SendValues(uint64_t curTimeMs) {
-  m_impl->SendValues(curTimeMs);
-  m_impl->m_wire.Flush();
-}
-
-void ClientImpl::SetLocal(LocalInterface* local) {
-  m_impl->m_local = local;
-}
-
-void ClientImpl::SendInitial() {
-  m_impl->SendInitialValues();
-  m_impl->m_wire.Flush();
-}
+void ClientImpl::SendInitial() {}
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/ClientImpl.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net/ClientImpl.h
index 0e7fd4a..b72ce38 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/ClientImpl.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/ClientImpl.h
@@ -13,8 +13,14 @@
 #include <string_view>
 #include <vector>
 
+#include <wpi/DenseMap.h>
+
 #include "NetworkInterface.h"
+#include "NetworkOutgoingQueue.h"
+#include "NetworkPing.h"
+#include "PubSubOptions.h"
 #include "WireConnection.h"
+#include "WireDecoder.h"
 
 namespace wpi {
 class Logger;
@@ -30,28 +36,79 @@
 struct ClientMessage;
 class WireConnection;
 
-class ClientImpl {
+class ClientImpl final : private ServerMessageHandler {
  public:
   ClientImpl(
       uint64_t curTimeMs, int inst, WireConnection& wire, wpi::Logger& logger,
       std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
           timeSyncUpdated,
       std::function<void(uint32_t repeatMs)> setPeriodic);
-  ~ClientImpl();
 
   void ProcessIncomingText(std::string_view data);
-  void ProcessIncomingBinary(std::span<const uint8_t> data);
+  void ProcessIncomingBinary(uint64_t curTimeMs, std::span<const uint8_t> data);
   void HandleLocal(std::vector<ClientMessage>&& msgs);
 
-  void SendControl(uint64_t curTimeMs);
-  void SendValues(uint64_t curTimeMs);
+  void SendOutgoing(uint64_t curTimeMs, bool flush);
 
-  void SetLocal(LocalInterface* local);
+  void SetLocal(LocalInterface* local) { m_local = local; }
   void SendInitial();
 
  private:
-  class Impl;
-  std::unique_ptr<Impl> m_impl;
+  struct PublisherData {
+    NT_Publisher handle;
+    PubSubOptionsImpl options;
+    // in options as double, but copy here as integer; rounded to the nearest
+    // 10 ms
+    uint32_t periodMs;
+  };
+
+  void UpdatePeriodic();
+
+  // ServerMessageHandler interface
+  void ServerAnnounce(std::string_view name, int64_t id,
+                      std::string_view typeStr, const wpi::json& properties,
+                      std::optional<int64_t> pubuid) final;
+  void ServerUnannounce(std::string_view name, int64_t id) final;
+  void ServerPropertiesUpdate(std::string_view name, const wpi::json& update,
+                              bool ack) final;
+
+  void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
+               std::string_view name, std::string_view typeStr,
+               const wpi::json& properties, const PubSubOptionsImpl& options);
+  bool Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle);
+  void SetValue(NT_Publisher pubHandle, const Value& value);
+
+  int m_inst;
+  WireConnection& m_wire;
+  wpi::Logger& m_logger;
+  LocalInterface* m_local{nullptr};
+  std::function<void(int64_t serverTimeOffset, int64_t rtt2, bool valid)>
+      m_timeSyncUpdated;
+  std::function<void(uint32_t repeatMs)> m_setPeriodic;
+
+  // indexed by publisher index
+  std::vector<std::unique_ptr<PublisherData>> m_publishers;
+
+  // indexed by server-provided topic id
+  wpi::DenseMap<int64_t, NT_Topic> m_topicMap;
+
+  // ping
+  NetworkPing m_ping;
+
+  // timestamp handling
+  static constexpr uint32_t kRttIntervalMs = 3000;
+  uint64_t m_nextPingTimeMs{0};
+  uint64_t m_pongTimeMs{0};
+  uint32_t m_rtt2Us{UINT32_MAX};
+  bool m_haveTimeOffset{false};
+
+  // periodic sweep handling
+  static constexpr uint32_t kMinPeriodMs = 5;
+  static constexpr uint32_t kMaxPeriodMs = NetworkPing::kPingIntervalMs;
+  uint32_t m_periodMs{kMaxPeriodMs};
+
+  // outgoing queue
+  NetworkOutgoingQueue<ClientMessage> m_outgoing;
 };
 
 }  // namespace nt::net
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/Message.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net/Message.h
index a95c5e8..d2a02b0 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/Message.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/Message.h
@@ -17,6 +17,11 @@
 
 namespace nt::net {
 
+#if __GNUC__ >= 13
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+
 struct PublishMsg {
   static constexpr std::string_view kMethodStr = "publish";
   NT_Publisher pubHandle{0};
@@ -57,10 +62,15 @@
   Value value;
 };
 
+#if __GNUC__ >= 13
+#pragma GCC diagnostic pop
+#endif
+
 struct ClientMessage {
   using Contents =
       std::variant<std::monostate, PublishMsg, UnpublishMsg, SetPropertiesMsg,
                    SubscribeMsg, UnsubscribeMsg, ClientValueMsg>;
+  using ValueMsg = ClientValueMsg;
   Contents contents;
 };
 
@@ -94,6 +104,7 @@
 struct ServerMessage {
   using Contents = std::variant<std::monostate, AnnounceMsg, UnannounceMsg,
                                 PropertiesUpdateMsg, ServerValueMsg>;
+  using ValueMsg = ServerValueMsg;
   Contents contents;
 };
 
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkInterface.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkInterface.h
index 3b2e7dd..4c9be54 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkInterface.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkInterface.h
@@ -8,11 +8,9 @@
 #include <string>
 #include <string_view>
 
-#include "ntcore_cpp.h"
+#include <wpi/json_fwd.h>
 
-namespace wpi {
-class json;
-}  // namespace wpi
+#include "ntcore_cpp.h"
 
 namespace nt {
 class PubSubOptionsImpl;
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkLoopQueue.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkLoopQueue.cpp
index 7c58c65..944524a 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkLoopQueue.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkLoopQueue.cpp
@@ -12,37 +12,7 @@
 
 void NetworkLoopQueue::SetValue(NT_Publisher pubHandle, const Value& value) {
   std::scoped_lock lock{m_mutex};
-  switch (value.type()) {
-    case NT_STRING:
-      m_size += value.GetString().size();  // imperfect but good enough
-      break;
-    case NT_RAW:
-      m_size += value.GetRaw().size_bytes();
-      break;
-    case NT_BOOLEAN_ARRAY:
-      m_size += value.GetBooleanArray().size_bytes();
-      break;
-    case NT_INTEGER_ARRAY:
-      m_size += value.GetIntegerArray().size_bytes();
-      break;
-    case NT_FLOAT_ARRAY:
-      m_size += value.GetFloatArray().size_bytes();
-      break;
-    case NT_DOUBLE_ARRAY:
-      m_size += value.GetDoubleArray().size_bytes();
-      break;
-    case NT_STRING_ARRAY: {
-      auto arr = value.GetStringArray();
-      m_size += arr.size_bytes();
-      for (auto&& s : arr) {
-        m_size += s.capacity();
-      }
-      break;
-    }
-    default:
-      break;
-  }
-  m_size += sizeof(ClientMessage);
+  m_size += sizeof(ClientMessage) + value.size();
   if (m_size > kMaxSize) {
     if (!m_sizeErrored) {
       WPI_ERROR(m_logger, "NT: dropping value set due to memory limits");
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkOutgoingQueue.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkOutgoingQueue.h
new file mode 100644
index 0000000..81c2b2e
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkOutgoingQueue.h
@@ -0,0 +1,342 @@
+// 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 <stdint.h>
+
+#include <algorithm>
+#include <concepts>
+#include <numeric>
+#include <optional>
+#include <span>
+#include <utility>
+#include <vector>
+
+#include <wpi/DenseMap.h>
+
+#include "Handle.h"
+#include "Message.h"
+#include "WireConnection.h"
+#include "WireEncoder.h"
+#include "networktables/NetworkTableValue.h"
+#include "ntcore_c.h"
+
+namespace nt::net {
+
+static constexpr uint32_t kMinPeriodMs = 5;
+
+inline uint32_t UpdatePeriodCalc(uint32_t period, uint32_t aPeriod) {
+  uint32_t newPeriod;
+  if (period == UINT32_MAX) {
+    newPeriod = aPeriod;
+  } else {
+    newPeriod = std::gcd(period, aPeriod);
+  }
+  if (newPeriod < kMinPeriodMs) {
+    return kMinPeriodMs;
+  }
+  return newPeriod;
+}
+
+template <typename T, typename F>
+uint32_t CalculatePeriod(const T& container, F&& getPeriod) {
+  uint32_t period = UINT32_MAX;
+  for (auto&& item : container) {
+    if (period == UINT32_MAX) {
+      period = getPeriod(item);
+    } else {
+      period = std::gcd(period, getPeriod(item));
+    }
+  }
+  if (period < kMinPeriodMs) {
+    return kMinPeriodMs;
+  }
+  return period;
+}
+
+template <typename MessageType>
+concept NetworkMessage =
+    std::same_as<typename MessageType::ValueMsg, ServerValueMsg> ||
+    std::same_as<typename MessageType::ValueMsg, ClientValueMsg>;
+
+enum class ValueSendMode { kDisabled = 0, kAll, kNormal, kImm };
+
+template <NetworkMessage MessageType>
+class NetworkOutgoingQueue {
+ public:
+  NetworkOutgoingQueue(WireConnection& wire, bool local)
+      : m_wire{wire}, m_local{local} {
+    m_queues.emplace_back(100);  // default queue is 100 ms period
+  }
+
+  void SetPeriod(NT_Handle handle, uint32_t periodMs);
+
+  void EraseHandle(NT_Handle handle) { m_handleMap.erase(handle); }
+
+  template <typename T>
+  void SendMessage(NT_Handle handle, T&& msg) {
+    m_queues[m_handleMap[handle].queueIndex].Append(handle,
+                                                    std::forward<T>(msg));
+    m_totalSize += sizeof(Message);
+  }
+
+  void SendValue(NT_Handle handle, const Value& value, ValueSendMode mode);
+
+  void SendOutgoing(uint64_t curTimeMs, bool flush);
+
+  void SetTimeOffset(int64_t offsetUs) { m_timeOffsetUs = offsetUs; }
+  int64_t GetTimeOffset() const { return m_timeOffsetUs; }
+
+ public:
+  WireConnection& m_wire;
+
+ private:
+  using ValueMsg = typename MessageType::ValueMsg;
+
+  void EncodeValue(wpi::raw_ostream& os, NT_Handle handle, const Value& value);
+
+  struct Message {
+    Message() = default;
+    template <typename T>
+    Message(T&& msg, NT_Handle handle)
+        : msg{std::forward<T>(msg)}, handle{handle} {}
+
+    MessageType msg;
+    NT_Handle handle;
+  };
+
+  struct Queue {
+    explicit Queue(uint32_t periodMs) : periodMs{periodMs} {}
+    template <typename T>
+    void Append(NT_Handle handle, T&& msg) {
+      msgs.emplace_back(std::forward<T>(msg), handle);
+    }
+    std::vector<Message> msgs;
+    uint64_t nextSendMs = 0;
+    uint32_t periodMs;
+  };
+
+  std::vector<Queue> m_queues;
+
+  struct HandleInfo {
+    unsigned int queueIndex = 0;
+    int valuePos = -1;  // -1 if not in queue
+  };
+  wpi::DenseMap<NT_Handle, HandleInfo> m_handleMap;
+  size_t m_totalSize{0};
+  uint64_t m_lastSendMs{0};
+  int64_t m_timeOffsetUs{0};
+  unsigned int m_lastSetPeriodQueueIndex = 0;
+  unsigned int m_lastSetPeriod = 100;
+  bool m_local;
+
+  // maximum total size of outgoing queues in bytes (approximate)
+  static constexpr size_t kOutgoingLimit = 1024 * 1024;
+};
+
+template <NetworkMessage MessageType>
+void NetworkOutgoingQueue<MessageType>::SetPeriod(NT_Handle handle,
+                                                  uint32_t periodMs) {
+  // it's quite common to set a lot of things in a row with the same period
+  unsigned int queueIndex;
+  if (m_lastSetPeriod == periodMs) {
+    queueIndex = m_lastSetPeriodQueueIndex;
+  } else {
+    // find and possibly create queue for this period
+    auto it =
+        std::find_if(m_queues.begin(), m_queues.end(),
+                     [&](const auto& q) { return q.periodMs == periodMs; });
+    if (it == m_queues.end()) {
+      queueIndex = m_queues.size();
+      m_queues.emplace_back(periodMs);
+    } else {
+      queueIndex = it - m_queues.begin();
+    }
+    m_lastSetPeriodQueueIndex = queueIndex;
+    m_lastSetPeriod = periodMs;
+  }
+
+  // map the handle to the queue
+  auto [infoIt, created] = m_handleMap.try_emplace(handle);
+  if (!created && infoIt->getSecond().queueIndex != queueIndex) {
+    // need to move any items from old queue to new queue
+    auto& oldMsgs = m_queues[infoIt->getSecond().queueIndex].msgs;
+    auto it = std::stable_partition(
+        oldMsgs.begin(), oldMsgs.end(),
+        [&](const auto& e) { return e.handle != handle; });
+    auto& newMsgs = m_queues[queueIndex].msgs;
+    for (auto i = it, end = oldMsgs.end(); i != end; ++i) {
+      newMsgs.emplace_back(std::move(*i));
+    }
+    oldMsgs.erase(it, oldMsgs.end());
+  }
+
+  infoIt->getSecond().queueIndex = queueIndex;
+}
+
+template <NetworkMessage MessageType>
+void NetworkOutgoingQueue<MessageType>::SendValue(NT_Handle handle,
+                                                  const Value& value,
+                                                  ValueSendMode mode) {
+  if (m_local) {
+    mode = ValueSendMode::kImm;  // always send local immediately
+  }
+  // backpressure by stopping sending all if the buffer is too full
+  if (mode == ValueSendMode::kAll && m_totalSize >= kOutgoingLimit) {
+    mode = ValueSendMode::kNormal;
+  }
+  switch (mode) {
+    case ValueSendMode::kDisabled:  // do nothing
+      break;
+    case ValueSendMode::kImm:  // send immediately
+      m_wire.SendBinary([&](auto& os) { EncodeValue(os, handle, value); });
+      break;
+    case ValueSendMode::kAll: {  // append to outgoing
+      auto& info = m_handleMap[handle];
+      auto& queue = m_queues[info.queueIndex];
+      info.valuePos = queue.msgs.size();
+      queue.Append(handle, ValueMsg{handle, value});
+      m_totalSize += sizeof(Message) + value.size();
+      break;
+    }
+    case ValueSendMode::kNormal: {
+      // replace, or append if not present
+      auto& info = m_handleMap[handle];
+      auto& queue = m_queues[info.queueIndex];
+      if (info.valuePos != -1 &&
+          static_cast<unsigned int>(info.valuePos) < queue.msgs.size()) {
+        auto& elem = queue.msgs[info.valuePos];
+        if (auto m = std::get_if<ValueMsg>(&elem.msg.contents)) {
+          // double-check handle, and only replace if timestamp newer
+          if (elem.handle == handle &&
+              (m->value.time() == 0 || value.time() >= m->value.time())) {
+            int delta = value.size() - m->value.size();
+            m->value = value;
+            m_totalSize += delta;
+            return;
+          }
+        }
+      }
+      info.valuePos = queue.msgs.size();
+      queue.Append(handle, ValueMsg{handle, value});
+      m_totalSize += sizeof(Message) + value.size();
+      break;
+    }
+  }
+}
+
+template <NetworkMessage MessageType>
+void NetworkOutgoingQueue<MessageType>::SendOutgoing(uint64_t curTimeMs,
+                                                     bool flush) {
+  if (m_totalSize == 0) {
+    return;  // nothing to do
+  }
+
+  // rate limit frequency of transmissions
+  if (curTimeMs < (m_lastSendMs + kMinPeriodMs)) {
+    return;
+  }
+
+  if (!m_wire.Ready()) {
+    return;  // don't bother, still sending the last batch
+  }
+
+  // what queues are ready to send?
+  wpi::SmallVector<unsigned int, 16> queues;
+  for (unsigned int i = 0; i < m_queues.size(); ++i) {
+    if (!m_queues[i].msgs.empty() &&
+        (flush || curTimeMs >= m_queues[i].nextSendMs)) {
+      queues.emplace_back(i);
+    }
+  }
+  if (queues.empty()) {
+    return;  // nothing needs to be sent yet
+  }
+
+  // Sort transmission order by what queue has been waiting the longest time.
+  // XXX: byte-weighted fair queueing might be better, but is much more complex
+  // to implement.
+  std::sort(queues.begin(), queues.end(), [&](const auto& a, const auto& b) {
+    return m_queues[a].nextSendMs < m_queues[b].nextSendMs;
+  });
+
+  for (unsigned int queueIndex : queues) {
+    auto& queue = m_queues[queueIndex];
+    auto& msgs = queue.msgs;
+    auto it = msgs.begin();
+    auto end = msgs.end();
+    int unsent = 0;
+    for (; it != end && unsent == 0; ++it) {
+      if (auto m = std::get_if<ValueMsg>(&it->msg.contents)) {
+        unsent = m_wire.WriteBinary(
+            [&](auto& os) { EncodeValue(os, it->handle, m->value); });
+      } else {
+        unsent = m_wire.WriteText([&](auto& os) {
+          if (!WireEncodeText(os, it->msg)) {
+            os << "{}";
+          }
+        });
+      }
+    }
+    if (unsent < 0) {
+      return;  // error
+    }
+    if (unsent == 0) {
+      // finish writing any partial buffers
+      unsent = m_wire.Flush();
+      if (unsent < 0) {
+        return;  // error
+      }
+    }
+    int delta = it - msgs.begin() - unsent;
+    for (auto&& msg : std::span{msgs}.subspan(0, delta)) {
+      if (auto m = std::get_if<ValueMsg>(&msg.msg.contents)) {
+        m_totalSize -= sizeof(Message) + m->value.size();
+      } else {
+        m_totalSize -= sizeof(Message);
+      }
+    }
+    msgs.erase(msgs.begin(), it - unsent);
+    for (auto&& kv : m_handleMap) {
+      auto& info = kv.getSecond();
+      if (info.queueIndex == queueIndex) {
+        if (info.valuePos < delta) {
+          info.valuePos = -1;
+        } else {
+          info.valuePos -= delta;
+        }
+      }
+    }
+
+    // try to stay on periodic timing, unless it's falling behind current time
+    if (unsent == 0) {
+      queue.nextSendMs += queue.periodMs;
+      if (queue.nextSendMs < curTimeMs) {
+        queue.nextSendMs = curTimeMs + queue.periodMs;
+      }
+    }
+  }
+
+  m_lastSendMs = curTimeMs;
+}
+
+template <NetworkMessage MessageType>
+void NetworkOutgoingQueue<MessageType>::EncodeValue(wpi::raw_ostream& os,
+                                                    NT_Handle handle,
+                                                    const Value& value) {
+  int64_t time = value.time();
+  if constexpr (std::same_as<ValueMsg, ClientValueMsg>) {
+    if (time != 0) {
+      time += m_timeOffsetUs;
+      // make sure resultant time isn't exactly 0
+      if (time == 0) {
+        time = 1;
+      }
+    }
+  }
+  WireEncodeBinary(os, Handle{handle}.GetIndex(), time, value);
+}
+
+}  // namespace nt::net
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkPing.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkPing.cpp
new file mode 100644
index 0000000..fdbd26c
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkPing.cpp
@@ -0,0 +1,30 @@
+// 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 "NetworkPing.h"
+
+#include "WireConnection.h"
+
+using namespace nt::net;
+
+bool NetworkPing::Send(uint64_t curTimeMs) {
+  if (curTimeMs < m_nextPingTimeMs) {
+    return true;
+  }
+  // if we didn't receive a timely response to our last ping, disconnect
+  uint64_t lastPing = m_wire.GetLastPingResponse();
+  // DEBUG4("WS ping: lastPing={} curTime={} pongTimeMs={}\n", lastPing,
+  //        curTimeMs, m_pongTimeMs);
+  if (lastPing == 0) {
+    lastPing = m_pongTimeMs;
+  }
+  if (m_pongTimeMs != 0 && curTimeMs > (lastPing + kPingTimeoutMs)) {
+    m_wire.Disconnect("connection timed out");
+    return false;
+  }
+  m_wire.SendPing(curTimeMs);
+  m_nextPingTimeMs = curTimeMs + kPingIntervalMs;
+  m_pongTimeMs = curTimeMs;
+  return true;
+}
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkPing.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkPing.h
new file mode 100644
index 0000000..304e01f
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/NetworkPing.h
@@ -0,0 +1,28 @@
+// 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 <stdint.h>
+
+namespace nt::net {
+
+class WireConnection;
+
+class NetworkPing {
+ public:
+  static constexpr uint32_t kPingIntervalMs = 200;
+  static constexpr uint32_t kPingTimeoutMs = 1000;
+
+  explicit NetworkPing(WireConnection& wire) : m_wire{wire} {}
+
+  bool Send(uint64_t curTimeMs);
+
+ private:
+  WireConnection& m_wire;
+  uint64_t m_nextPingTimeMs{0};
+  uint64_t m_pongTimeMs{0};
+};
+
+}  // namespace nt::net
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/ServerImpl.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net/ServerImpl.cpp
index d9b1c0c..1119809 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/ServerImpl.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/ServerImpl.cpp
@@ -14,30 +14,19 @@
 #include <vector>
 
 #include <wpi/Base64.h>
-#include <wpi/DenseMap.h>
 #include <wpi/MessagePack.h>
 #include <wpi/SmallVector.h>
 #include <wpi/StringExtras.h>
-#include <wpi/StringMap.h>
-#include <wpi/UidVector.h>
 #include <wpi/json.h>
-#include <wpi/json_serializer.h>
 #include <wpi/raw_ostream.h>
 #include <wpi/timestamp.h>
 
 #include "IConnectionList.h"
 #include "Log.h"
-#include "Message.h"
 #include "NetworkInterface.h"
-#include "PubSubOptions.h"
 #include "Types_internal.h"
-#include "WireConnection.h"
-#include "WireDecoder.h"
-#include "WireEncoder.h"
-#include "net3/Message3.h"
-#include "net3/SequenceNumber.h"
+#include "net/WireEncoder.h"
 #include "net3/WireConnection3.h"
-#include "net3/WireDecoder3.h"
 #include "net3/WireEncoder3.h"
 #include "networktables/NetworkTableValue.h"
 #include "ntcore_c.h"
@@ -46,405 +35,11 @@
 using namespace nt::net;
 using namespace mpack;
 
-static constexpr uint32_t kMinPeriodMs = 5;
-
-// maximum number of times the wire can be not ready to send another
+// maximum amount of time the wire can be not ready to send another
 // transmission before we close the connection
-static constexpr int kWireMaxNotReady = 10;
+static constexpr uint32_t kWireMaxNotReadyUs = 1000000;
 
 namespace {
-
-// Utility wrapper for making a set-like vector
-template <typename T>
-class VectorSet : public std::vector<T> {
- public:
-  using iterator = typename std::vector<T>::iterator;
-  void Add(T value) { this->push_back(value); }
-  // returns true if element was present
-  bool Remove(T value) {
-    auto removeIt = std::remove(this->begin(), this->end(), value);
-    if (removeIt == this->end()) {
-      return false;
-    }
-    this->erase(removeIt, this->end());
-    return true;
-  }
-};
-
-struct PublisherData;
-struct SubscriberData;
-struct TopicData;
-class SImpl;
-
-class ClientData {
- public:
-  ClientData(std::string_view originalName, std::string_view name,
-             std::string_view connInfo, bool local,
-             ServerImpl::SetPeriodicFunc setPeriodic, SImpl& server, int id,
-             wpi::Logger& logger)
-      : m_originalName{originalName},
-        m_name{name},
-        m_connInfo{connInfo},
-        m_local{local},
-        m_setPeriodic{std::move(setPeriodic)},
-        m_server{server},
-        m_id{id},
-        m_logger{logger} {}
-  virtual ~ClientData() = default;
-
-  virtual void ProcessIncomingText(std::string_view data) = 0;
-  virtual void ProcessIncomingBinary(std::span<const uint8_t> data) = 0;
-
-  enum SendMode { kSendDisabled = 0, kSendAll, kSendNormal, kSendImmNoFlush };
-
-  virtual void SendValue(TopicData* topic, const Value& value,
-                         SendMode mode) = 0;
-  virtual void SendAnnounce(TopicData* topic,
-                            std::optional<int64_t> pubuid) = 0;
-  virtual void SendUnannounce(TopicData* topic) = 0;
-  virtual void SendPropertiesUpdate(TopicData* topic, const wpi::json& update,
-                                    bool ack) = 0;
-  virtual void SendOutgoing(uint64_t curTimeMs) = 0;
-  virtual void Flush() = 0;
-
-  void UpdateMetaClientPub();
-  void UpdateMetaClientSub();
-
-  std::span<SubscriberData*> GetSubscribers(
-      std::string_view name, bool special,
-      wpi::SmallVectorImpl<SubscriberData*>& buf);
-
-  std::string_view GetOriginalName() const { return m_originalName; }
-  std::string_view GetName() const { return m_name; }
-  int GetId() const { return m_id; }
-
- protected:
-  std::string m_originalName;
-  std::string m_name;
-  std::string m_connInfo;
-  bool m_local;  // local to machine
-  ServerImpl::SetPeriodicFunc m_setPeriodic;
-  // TODO: make this per-topic?
-  uint32_t m_periodMs{UINT32_MAX};
-  uint64_t m_lastSendMs{0};
-  SImpl& m_server;
-  int m_id;
-
-  wpi::Logger& m_logger;
-
-  wpi::DenseMap<int64_t, std::unique_ptr<PublisherData>> m_publishers;
-  wpi::DenseMap<int64_t, std::unique_ptr<SubscriberData>> m_subscribers;
-
- public:
-  // meta topics
-  TopicData* m_metaPub = nullptr;
-  TopicData* m_metaSub = nullptr;
-};
-
-class ClientData4Base : public ClientData, protected ClientMessageHandler {
- public:
-  ClientData4Base(std::string_view originalName, std::string_view name,
-                  std::string_view connInfo, bool local,
-                  ServerImpl::SetPeriodicFunc setPeriodic, SImpl& server,
-                  int id, wpi::Logger& logger)
-      : ClientData{originalName, name,   connInfo, local,
-                   setPeriodic,  server, id,       logger} {}
-
- protected:
-  // ClientMessageHandler interface
-  void ClientPublish(int64_t pubuid, std::string_view name,
-                     std::string_view typeStr,
-                     const wpi::json& properties) final;
-  void ClientUnpublish(int64_t pubuid) final;
-  void ClientSetProperties(std::string_view name,
-                           const wpi::json& update) final;
-  void ClientSubscribe(int64_t subuid, std::span<const std::string> topicNames,
-                       const PubSubOptionsImpl& options) final;
-  void ClientUnsubscribe(int64_t subuid) final;
-
-  void ClientSetValue(int64_t pubuid, const Value& value);
-
-  wpi::DenseMap<TopicData*, bool> m_announceSent;
-};
-
-class ClientDataLocal final : public ClientData4Base {
- public:
-  ClientDataLocal(SImpl& server, int id, wpi::Logger& logger)
-      : ClientData4Base{"", "", "", true, [](uint32_t) {}, server, id, logger} {
-  }
-
-  void ProcessIncomingText(std::string_view data) final {}
-  void ProcessIncomingBinary(std::span<const uint8_t> data) final {}
-
-  void SendValue(TopicData* topic, const Value& value, SendMode mode) final;
-  void SendAnnounce(TopicData* topic, std::optional<int64_t> pubuid) final;
-  void SendUnannounce(TopicData* topic) final;
-  void SendPropertiesUpdate(TopicData* topic, const wpi::json& update,
-                            bool ack) final;
-  void SendOutgoing(uint64_t curTimeMs) final {}
-  void Flush() final {}
-
-  void HandleLocal(std::span<const ClientMessage> msgs);
-};
-
-class ClientData4 final : public ClientData4Base {
- public:
-  ClientData4(std::string_view originalName, std::string_view name,
-              std::string_view connInfo, bool local, WireConnection& wire,
-              ServerImpl::SetPeriodicFunc setPeriodic, SImpl& server, int id,
-              wpi::Logger& logger)
-      : ClientData4Base{originalName, name,   connInfo, local,
-                        setPeriodic,  server, id,       logger},
-        m_wire{wire} {}
-
-  void ProcessIncomingText(std::string_view data) final;
-  void ProcessIncomingBinary(std::span<const uint8_t> data) final;
-
-  void SendValue(TopicData* topic, const Value& value, SendMode mode) final;
-  void SendAnnounce(TopicData* topic, std::optional<int64_t> pubuid) final;
-  void SendUnannounce(TopicData* topic) final;
-  void SendPropertiesUpdate(TopicData* topic, const wpi::json& update,
-                            bool ack) final;
-  void SendOutgoing(uint64_t curTimeMs) final;
-
-  void Flush() final;
-
- public:
-  WireConnection& m_wire;
-
- private:
-  std::vector<ServerMessage> m_outgoing;
-  int m_notReadyCount{0};
-
-  bool WriteBinary(int64_t id, int64_t time, const Value& value) {
-    return WireEncodeBinary(SendBinary().Add(), id, time, value);
-  }
-
-  TextWriter& SendText() {
-    m_outBinary.reset();  // ensure proper interleaving of text and binary
-    if (!m_outText) {
-      m_outText = m_wire.SendText();
-    }
-    return *m_outText;
-  }
-
-  BinaryWriter& SendBinary() {
-    m_outText.reset();  // ensure proper interleaving of text and binary
-    if (!m_outBinary) {
-      m_outBinary = m_wire.SendBinary();
-    }
-    return *m_outBinary;
-  }
-
-  // valid when we are actively writing to this client
-  std::optional<TextWriter> m_outText;
-  std::optional<BinaryWriter> m_outBinary;
-};
-
-class ClientData3 final : public ClientData, private net3::MessageHandler3 {
- public:
-  ClientData3(std::string_view connInfo, bool local,
-              net3::WireConnection3& wire, ServerImpl::Connected3Func connected,
-              ServerImpl::SetPeriodicFunc setPeriodic, SImpl& server, int id,
-              wpi::Logger& logger)
-      : ClientData{"", "", connInfo, local, setPeriodic, server, id, logger},
-        m_connected{std::move(connected)},
-        m_wire{wire},
-        m_decoder{*this} {}
-
-  void ProcessIncomingText(std::string_view data) final {}
-  void ProcessIncomingBinary(std::span<const uint8_t> data) final;
-
-  void SendValue(TopicData* topic, const Value& value, SendMode mode) final;
-  void SendAnnounce(TopicData* topic, std::optional<int64_t> pubuid) final;
-  void SendUnannounce(TopicData* topic) final;
-  void SendPropertiesUpdate(TopicData* topic, const wpi::json& update,
-                            bool ack) final;
-  void SendOutgoing(uint64_t curTimeMs) final;
-
-  void Flush() final { m_wire.Flush(); }
-
- private:
-  // MessageHandler3 interface
-  void KeepAlive() final;
-  void ServerHelloDone() final;
-  void ClientHelloDone() final;
-  void ClearEntries() final;
-  void ProtoUnsup(unsigned int proto_rev) final;
-  void ClientHello(std::string_view self_id, unsigned int proto_rev) final;
-  void ServerHello(unsigned int flags, std::string_view self_id) final;
-  void EntryAssign(std::string_view name, unsigned int id, unsigned int seq_num,
-                   const Value& value, unsigned int flags) final;
-  void EntryUpdate(unsigned int id, unsigned int seq_num,
-                   const Value& value) final;
-  void FlagsUpdate(unsigned int id, unsigned int flags) final;
-  void EntryDelete(unsigned int id) final;
-  void ExecuteRpc(unsigned int id, unsigned int uid,
-                  std::span<const uint8_t> params) final {}
-  void RpcResponse(unsigned int id, unsigned int uid,
-                   std::span<const uint8_t> result) final {}
-
-  ServerImpl::Connected3Func m_connected;
-  net3::WireConnection3& m_wire;
-
-  enum State { kStateInitial, kStateServerHelloComplete, kStateRunning };
-  State m_state{kStateInitial};
-  net3::WireDecoder3 m_decoder;
-
-  std::vector<net3::Message3> m_outgoing;
-  int64_t m_nextPubUid{1};
-  int m_notReadyCount{0};
-
-  struct TopicData3 {
-    explicit TopicData3(TopicData* topic) { UpdateFlags(topic); }
-
-    unsigned int flags{0};
-    net3::SequenceNumber seqNum;
-    bool sentAssign{false};
-    bool published{false};
-    int64_t pubuid{0};
-
-    bool UpdateFlags(TopicData* topic);
-  };
-  wpi::DenseMap<TopicData*, TopicData3> m_topics3;
-  TopicData3* GetTopic3(TopicData* topic) {
-    return &m_topics3.try_emplace(topic, topic).first->second;
-  }
-};
-
-struct TopicData {
-  TopicData(std::string_view name, std::string_view typeStr)
-      : name{name}, typeStr{typeStr} {}
-  TopicData(std::string_view name, std::string_view typeStr,
-            wpi::json properties)
-      : name{name}, typeStr{typeStr}, properties(std::move(properties)) {
-    RefreshProperties();
-  }
-
-  bool IsPublished() const {
-    return persistent || retained || !publishers.empty();
-  }
-
-  // returns true if properties changed
-  bool SetProperties(const wpi::json& update);
-  void RefreshProperties();
-  bool SetFlags(unsigned int flags_);
-
-  std::string name;
-  unsigned int id;
-  Value lastValue;
-  ClientData* lastValueClient = nullptr;
-  std::string typeStr;
-  wpi::json properties = wpi::json::object();
-  bool persistent{false};
-  bool retained{false};
-  bool special{false};
-  NT_Topic localHandle{0};
-
-  VectorSet<PublisherData*> publishers;
-  VectorSet<SubscriberData*> subscribers;
-
-  // meta topics
-  TopicData* metaPub = nullptr;
-  TopicData* metaSub = nullptr;
-};
-
-struct PublisherData {
-  PublisherData(ClientData* client, TopicData* topic, int64_t pubuid)
-      : client{client}, topic{topic}, pubuid{pubuid} {}
-
-  ClientData* client;
-  TopicData* topic;
-  int64_t pubuid;
-};
-
-struct SubscriberData {
-  SubscriberData(ClientData* client, std::span<const std::string> topicNames,
-                 int64_t subuid, const PubSubOptionsImpl& options)
-      : client{client},
-        topicNames{topicNames.begin(), topicNames.end()},
-        subuid{subuid},
-        options{options},
-        periodMs(std::lround(options.periodicMs / 10.0) * 10) {
-    if (periodMs < kMinPeriodMs) {
-      periodMs = kMinPeriodMs;
-    }
-  }
-
-  void Update(std::span<const std::string> topicNames_,
-              const PubSubOptionsImpl& options_) {
-    topicNames = {topicNames_.begin(), topicNames_.end()};
-    options = options_;
-    periodMs = std::lround(options_.periodicMs / 10.0) * 10;
-    if (periodMs < kMinPeriodMs) {
-      periodMs = kMinPeriodMs;
-    }
-  }
-
-  bool Matches(std::string_view name, bool special);
-
-  ClientData* client;
-  std::vector<std::string> topicNames;
-  int64_t subuid;
-  PubSubOptionsImpl options;
-  // in options as double, but copy here as integer; rounded to the nearest
-  // 10 ms
-  uint32_t periodMs;
-};
-
-class SImpl {
- public:
-  explicit SImpl(wpi::Logger& logger);
-
-  wpi::Logger& m_logger;
-  LocalInterface* m_local{nullptr};
-  bool m_controlReady{false};
-
-  ClientDataLocal* m_localClient;
-  std::vector<std::unique_ptr<ClientData>> m_clients;
-  wpi::UidVector<std::unique_ptr<TopicData>, 16> m_topics;
-  wpi::StringMap<TopicData*> m_nameTopics;
-  bool m_persistentChanged{false};
-
-  // global meta topics (other meta topics are linked to from the specific
-  // client or topic)
-  TopicData* m_metaClients;
-
-  // ServerImpl interface
-  std::pair<std::string, int> AddClient(
-      std::string_view name, std::string_view connInfo, bool local,
-      WireConnection& wire, ServerImpl::SetPeriodicFunc setPeriodic);
-  int AddClient3(std::string_view connInfo, bool local,
-                 net3::WireConnection3& wire,
-                 ServerImpl::Connected3Func connected,
-                 ServerImpl::SetPeriodicFunc setPeriodic);
-  void RemoveClient(int clientId);
-
-  bool PersistentChanged();
-  void DumpPersistent(wpi::raw_ostream& os);
-  std::string LoadPersistent(std::string_view in);
-
-  // helper functions
-  TopicData* CreateTopic(ClientData* client, std::string_view name,
-                         std::string_view typeStr, const wpi::json& properties,
-                         bool special = false);
-  TopicData* CreateMetaTopic(std::string_view name);
-  void DeleteTopic(TopicData* topic);
-  void SetProperties(ClientData* client, TopicData* topic,
-                     const wpi::json& update);
-  void SetFlags(ClientData* client, TopicData* topic, unsigned int flags);
-  void SetValue(ClientData* client, TopicData* topic, const Value& value);
-
-  // update meta topic values from data structures
-  void UpdateMetaClients(const std::vector<ConnectionInfo>& conns);
-  void UpdateMetaTopicPub(TopicData* topic);
-  void UpdateMetaTopicSub(TopicData* topic);
-
- private:
-  void PropertiesChanged(ClientData* client, TopicData* topic,
-                         const wpi::json& update);
-};
-
 struct Writer : public mpack_writer_t {
   Writer() {
     mpack_writer_init(this, buf, sizeof(buf));
@@ -486,19 +81,86 @@
   mpack_finish_map(&w);
 }
 
-void ClientData::UpdateMetaClientPub() {
+void ServerImpl::PublisherData::UpdateMeta() {
+  {
+    Writer w;
+    mpack_start_map(&w, 2);
+    mpack_write_str(&w, "uid");
+    mpack_write_int(&w, pubuid);
+    mpack_write_str(&w, "topic");
+    mpack_write_str(&w, topic->name);
+    mpack_finish_map(&w);
+    if (mpack_writer_destroy(&w) == mpack_ok) {
+      metaClient = std::move(w.bytes);
+    }
+  }
+  {
+    Writer w;
+    mpack_start_map(&w, 2);
+    mpack_write_str(&w, "client");
+    if (client) {
+      mpack_write_str(&w, client->GetName());
+    } else {
+      mpack_write_str(&w, "");
+    }
+    mpack_write_str(&w, "pubuid");
+    mpack_write_int(&w, pubuid);
+    mpack_finish_map(&w);
+    if (mpack_writer_destroy(&w) == mpack_ok) {
+      metaTopic = std::move(w.bytes);
+    }
+  }
+}
+
+void ServerImpl::SubscriberData::UpdateMeta() {
+  {
+    Writer w;
+    mpack_start_map(&w, 3);
+    mpack_write_str(&w, "uid");
+    mpack_write_int(&w, subuid);
+    mpack_write_str(&w, "topics");
+    mpack_start_array(&w, topicNames.size());
+    for (auto&& name : topicNames) {
+      mpack_write_str(&w, name);
+    }
+    mpack_finish_array(&w);
+    mpack_write_str(&w, "options");
+    WriteOptions(w, options);
+    mpack_finish_map(&w);
+    if (mpack_writer_destroy(&w) == mpack_ok) {
+      metaClient = std::move(w.bytes);
+    }
+  }
+  {
+    Writer w;
+    mpack_start_map(&w, 3);
+    mpack_write_str(&w, "client");
+    if (client) {
+      mpack_write_str(&w, client->GetName());
+    } else {
+      mpack_write_str(&w, "");
+    }
+    mpack_write_str(&w, "subuid");
+    mpack_write_int(&w, subuid);
+    mpack_write_str(&w, "options");
+    WriteOptions(w, options);
+    mpack_finish_map(&w);
+    if (mpack_writer_destroy(&w) == mpack_ok) {
+      metaTopic = std::move(w.bytes);
+    }
+  }
+}
+
+void ServerImpl::ClientData::UpdateMetaClientPub() {
   if (!m_metaPub) {
     return;
   }
   Writer w;
   mpack_start_array(&w, m_publishers.size());
   for (auto&& pub : m_publishers) {
-    mpack_start_map(&w, 2);
-    mpack_write_str(&w, "uid");
-    mpack_write_int(&w, pub.first);
-    mpack_write_str(&w, "topic");
-    mpack_write_str(&w, pub.second->topic->name);
-    mpack_finish_map(&w);
+    mpack_write_object_bytes(
+        &w, reinterpret_cast<const char*>(pub.second->metaClient.data()),
+        pub.second->metaClient.size());
   }
   mpack_finish_array(&w);
   if (mpack_writer_destroy(&w) == mpack_ok) {
@@ -506,25 +168,16 @@
   }
 }
 
-void ClientData::UpdateMetaClientSub() {
+void ServerImpl::ClientData::UpdateMetaClientSub() {
   if (!m_metaSub) {
     return;
   }
   Writer w;
   mpack_start_array(&w, m_subscribers.size());
   for (auto&& sub : m_subscribers) {
-    mpack_start_map(&w, 3);
-    mpack_write_str(&w, "uid");
-    mpack_write_int(&w, sub.first);
-    mpack_write_str(&w, "topics");
-    mpack_start_array(&w, sub.second->topicNames.size());
-    for (auto&& name : sub.second->topicNames) {
-      mpack_write_str(&w, name);
-    }
-    mpack_finish_array(&w);
-    mpack_write_str(&w, "options");
-    WriteOptions(w, sub.second->options);
-    mpack_finish_map(&w);
+    mpack_write_object_bytes(
+        &w, reinterpret_cast<const char*>(sub.second->metaClient.data()),
+        sub.second->metaClient.size());
   }
   mpack_finish_array(&w);
   if (mpack_writer_destroy(&w) == mpack_ok) {
@@ -532,7 +185,7 @@
   }
 }
 
-std::span<SubscriberData*> ClientData::GetSubscribers(
+std::span<ServerImpl::SubscriberData*> ServerImpl::ClientData::GetSubscribers(
     std::string_view name, bool special,
     wpi::SmallVectorImpl<SubscriberData*>& buf) {
   buf.resize(0);
@@ -545,9 +198,10 @@
   return {buf.data(), buf.size()};
 }
 
-void ClientData4Base::ClientPublish(int64_t pubuid, std::string_view name,
-                                    std::string_view typeStr,
-                                    const wpi::json& properties) {
+void ServerImpl::ClientData4Base::ClientPublish(int64_t pubuid,
+                                                std::string_view name,
+                                                std::string_view typeStr,
+                                                const wpi::json& properties) {
   DEBUG3("ClientPublish({}, {}, {}, {})", m_id, name, pubuid, typeStr);
   auto topic = m_server.CreateTopic(this, name, typeStr, properties);
 
@@ -555,22 +209,21 @@
   auto [publisherIt, isNew] = m_publishers.try_emplace(
       pubuid, std::make_unique<PublisherData>(this, topic, pubuid));
   if (!isNew) {
-    WARNING("client {} duplicate publish of pubuid {}", m_id, pubuid);
+    WARN("client {} duplicate publish of pubuid {}", m_id, pubuid);
   }
 
   // add publisher to topic
-  topic->publishers.Add(publisherIt->getSecond().get());
+  topic->AddPublisher(this, publisherIt->getSecond().get());
 
   // update meta data
   m_server.UpdateMetaTopicPub(topic);
-  UpdateMetaClientPub();
 
   // respond with announce with pubuid to client
   DEBUG4("client {}: announce {} pubuid {}", m_id, topic->name, pubuid);
   SendAnnounce(topic, pubuid);
 }
 
-void ClientData4Base::ClientUnpublish(int64_t pubuid) {
+void ServerImpl::ClientData4Base::ClientUnpublish(int64_t pubuid) {
   DEBUG3("ClientUnpublish({}, {})", m_id, pubuid);
   auto publisherIt = m_publishers.find(pubuid);
   if (publisherIt == m_publishers.end()) {
@@ -580,14 +233,13 @@
   auto topic = publisher->topic;
 
   // remove publisher from topic
-  topic->publishers.Remove(publisher);
+  topic->RemovePublisher(this, publisher);
 
   // remove publisher from client
   m_publishers.erase(publisherIt);
 
   // update meta data
   m_server.UpdateMetaTopicPub(topic);
-  UpdateMetaClientPub();
 
   // delete topic if no longer published
   if (!topic->IsPublished()) {
@@ -595,27 +247,30 @@
   }
 }
 
-void ClientData4Base::ClientSetProperties(std::string_view name,
-                                          const wpi::json& update) {
+void ServerImpl::ClientData4Base::ClientSetProperties(std::string_view name,
+                                                      const wpi::json& update) {
   DEBUG4("ClientSetProperties({}, {}, {})", m_id, name, update.dump());
   auto topicIt = m_server.m_nameTopics.find(name);
   if (topicIt == m_server.m_nameTopics.end() ||
       !topicIt->second->IsPublished()) {
-    DEBUG3("ignored SetProperties from {} on non-existent topic '{}'", m_id,
-           name);
+    WARN(
+        "server ignoring SetProperties({}) from client {} on unpublished topic "
+        "'{}'; publish or set a value first",
+        update.dump(), m_id, name);
     return;  // nothing to do
   }
   auto topic = topicIt->second;
   if (topic->special) {
-    DEBUG3("ignored SetProperties from {} on meta topic '{}'", m_id, name);
+    WARN("server ignoring SetProperties({}) from client {} on meta topic '{}'",
+         update.dump(), m_id, name);
     return;  // nothing to do
   }
   m_server.SetProperties(nullptr, topic, update);
 }
 
-void ClientData4Base::ClientSubscribe(int64_t subuid,
-                                      std::span<const std::string> topicNames,
-                                      const PubSubOptionsImpl& options) {
+void ServerImpl::ClientData4Base::ClientSubscribe(
+    int64_t subuid, std::span<const std::string> topicNames,
+    const PubSubOptionsImpl& options) {
   DEBUG4("ClientSubscribe({}, ({}), {})", m_id, fmt::join(topicNames, ","),
          subuid);
   auto& sub = m_subscribers[subuid];
@@ -636,63 +291,62 @@
 
   // update periodic sender (if not local)
   if (!m_local) {
-    if (m_periodMs == UINT32_MAX) {
-      m_periodMs = sub->periodMs;
-    } else {
-      m_periodMs = std::gcd(m_periodMs, sub->periodMs);
-    }
-    if (m_periodMs < kMinPeriodMs) {
-      m_periodMs = kMinPeriodMs;
-    }
+    m_periodMs = UpdatePeriodCalc(m_periodMs, sub->periodMs);
     m_setPeriodic(m_periodMs);
   }
 
   // see if this immediately subscribes to any topics
+  // for transmit efficiency, we want to batch announcements and values, so
+  // send announcements in first loop and remember what we want to send in
+  // second loop.
+  std::vector<TopicData*> dataToSend;
+  dataToSend.reserve(m_server.m_topics.size());
   for (auto&& topic : m_server.m_topics) {
-    bool removed = false;
-    if (replace) {
-      removed = topic->subscribers.Remove(sub.get());
-    }
+    auto tcdIt = topic->clients.find(this);
+    bool removed = tcdIt != topic->clients.end() && replace &&
+                   tcdIt->second.subscribers.erase(sub.get());
 
     // is client already subscribed?
-    bool wasSubscribed = false;
-    for (auto subscriber : topic->subscribers) {
-      if (subscriber->client == this) {
-        wasSubscribed = true;
-        break;
-      }
-    }
+    bool wasSubscribed =
+        tcdIt != topic->clients.end() && !tcdIt->second.subscribers.empty();
+    bool wasSubscribedValue =
+        wasSubscribed ? tcdIt->second.sendMode != ValueSendMode::kDisabled
+                      : false;
 
     bool added = false;
     if (sub->Matches(topic->name, topic->special)) {
-      topic->subscribers.Add(sub.get());
+      if (tcdIt == topic->clients.end()) {
+        tcdIt = topic->clients.try_emplace(this).first;
+      }
+      tcdIt->second.AddSubscriber(sub.get());
       added = true;
     }
 
     if (added ^ removed) {
+      UpdatePeriod(tcdIt->second, topic.get());
       m_server.UpdateMetaTopicSub(topic.get());
     }
 
-    if (!wasSubscribed && added && !removed) {
-      // announce topic to client
+    // announce topic to client if not previously announced
+    if (added && !removed && !wasSubscribed) {
       DEBUG4("client {}: announce {}", m_id, topic->name);
       SendAnnounce(topic.get(), std::nullopt);
+    }
 
-      // send last value
-      if (!sub->options.topicsOnly && topic->lastValue) {
-        DEBUG4("send last value for {} to client {}", topic->name, m_id);
-        SendValue(topic.get(), topic->lastValue, kSendAll);
-      }
+    // send last value
+    if (added && !sub->options.topicsOnly && !wasSubscribedValue &&
+        topic->lastValue) {
+      dataToSend.emplace_back(topic.get());
     }
   }
 
-  // update meta data
-  UpdateMetaClientSub();
-
-  Flush();
+  for (auto topic : dataToSend) {
+    DEBUG4("send last value for {} to client {}", topic->name, m_id);
+    SendValue(topic, topic->lastValue, ValueSendMode::kAll);
+  }
 }
 
-void ClientData4Base::ClientUnsubscribe(int64_t subuid) {
+void ServerImpl::ClientData4Base::ClientUnsubscribe(int64_t subuid) {
   DEBUG3("ClientUnsubscribe({}, {})", m_id, subuid);
   auto subIt = m_subscribers.find(subuid);
   if (subIt == m_subscribers.end() || !subIt->getSecond()) {
@@ -702,50 +356,48 @@
 
   // remove from topics
   for (auto&& topic : m_server.m_topics) {
-    if (topic->subscribers.Remove(sub)) {
-      m_server.UpdateMetaTopicSub(topic.get());
+    auto tcdIt = topic->clients.find(this);
+    if (tcdIt != topic->clients.end()) {
+      if (tcdIt->second.subscribers.erase(sub)) {
+        UpdatePeriod(tcdIt->second, topic.get());
+        m_server.UpdateMetaTopicSub(topic.get());
+      }
     }
   }
 
   // delete it from client (future value sets will be ignored)
   m_subscribers.erase(subIt);
-  UpdateMetaClientSub();
 
-  // loop over all publishers to update period
-  m_periodMs = UINT32_MAX;
-  for (auto&& sub : m_subscribers) {
-    if (m_periodMs == UINT32_MAX) {
-      m_periodMs = sub.getSecond()->periodMs;
-    } else {
-      m_periodMs = std::gcd(m_periodMs, sub.getSecond()->periodMs);
-    }
+  // loop over all subscribers to update period
+  if (!m_local) {
+    m_periodMs = CalculatePeriod(
+        m_subscribers, [](auto& x) { return x.getSecond()->periodMs; });
+    m_setPeriodic(m_periodMs);
   }
-  if (m_periodMs < kMinPeriodMs) {
-    m_periodMs = kMinPeriodMs;
-  }
-  m_setPeriodic(m_periodMs);
 }
 
-void ClientData4Base::ClientSetValue(int64_t pubuid, const Value& value) {
+void ServerImpl::ClientData4Base::ClientSetValue(int64_t pubuid,
+                                                 const Value& value) {
   DEBUG4("ClientSetValue({}, {})", m_id, pubuid);
   auto publisherIt = m_publishers.find(pubuid);
   if (publisherIt == m_publishers.end()) {
-    WARNING("unrecognized client {} pubuid {}, ignoring set", m_id, pubuid);
+    WARN("unrecognized client {} pubuid {}, ignoring set", m_id, pubuid);
     return;  // ignore unrecognized pubuids
   }
   auto topic = publisherIt->getSecond().get()->topic;
   m_server.SetValue(this, topic, value);
 }
 
-void ClientDataLocal::SendValue(TopicData* topic, const Value& value,
-                                SendMode mode) {
+void ServerImpl::ClientDataLocal::SendValue(TopicData* topic,
+                                            const Value& value,
+                                            ValueSendMode mode) {
   if (m_server.m_local) {
     m_server.m_local->NetworkSetValue(topic->localHandle, value);
   }
 }
 
-void ClientDataLocal::SendAnnounce(TopicData* topic,
-                                   std::optional<int64_t> pubuid) {
+void ServerImpl::ClientDataLocal::SendAnnounce(TopicData* topic,
+                                               std::optional<int64_t> pubuid) {
   if (m_server.m_local) {
     auto& sent = m_announceSent[topic];
     if (sent) {
@@ -758,7 +410,7 @@
   }
 }
 
-void ClientDataLocal::SendUnannounce(TopicData* topic) {
+void ServerImpl::ClientDataLocal::SendUnannounce(TopicData* topic) {
   if (m_server.m_local) {
     auto& sent = m_announceSent[topic];
     if (!sent) {
@@ -769,8 +421,9 @@
   }
 }
 
-void ClientDataLocal::SendPropertiesUpdate(TopicData* topic,
-                                           const wpi::json& update, bool ack) {
+void ServerImpl::ClientDataLocal::SendPropertiesUpdate(TopicData* topic,
+                                                       const wpi::json& update,
+                                                       bool ack) {
   if (m_server.m_local) {
     if (!m_announceSent.lookup(topic)) {
       return;
@@ -779,32 +432,52 @@
   }
 }
 
-void ClientDataLocal::HandleLocal(std::span<const ClientMessage> msgs) {
+void ServerImpl::ClientDataLocal::HandleLocal(
+    std::span<const ClientMessage> msgs) {
   DEBUG4("HandleLocal()");
+  if (msgs.empty()) {
+    return;
+  }
   // just map as a normal client into client=0 calls
+  bool updatepub = false;
+  bool updatesub = false;
   for (const auto& elem : msgs) {  // NOLINT
     // common case is value, so check that first
     if (auto msg = std::get_if<ClientValueMsg>(&elem.contents)) {
       ClientSetValue(msg->pubHandle, msg->value);
     } else if (auto msg = std::get_if<PublishMsg>(&elem.contents)) {
       ClientPublish(msg->pubHandle, msg->name, msg->typeStr, msg->properties);
+      updatepub = true;
     } else if (auto msg = std::get_if<UnpublishMsg>(&elem.contents)) {
       ClientUnpublish(msg->pubHandle);
+      updatepub = true;
     } else if (auto msg = std::get_if<SetPropertiesMsg>(&elem.contents)) {
       ClientSetProperties(msg->name, msg->update);
     } else if (auto msg = std::get_if<SubscribeMsg>(&elem.contents)) {
       ClientSubscribe(msg->subHandle, msg->topicNames, msg->options);
+      updatesub = true;
     } else if (auto msg = std::get_if<UnsubscribeMsg>(&elem.contents)) {
       ClientUnsubscribe(msg->subHandle);
+      updatesub = true;
     }
   }
+  if (updatepub) {
+    UpdateMetaClientPub();
+  }
+  if (updatesub) {
+    UpdateMetaClientSub();
+  }
 }
 
-void ClientData4::ProcessIncomingText(std::string_view data) {
-  WireDecodeText(data, *this, m_logger);
+void ServerImpl::ClientData4::ProcessIncomingText(std::string_view data) {
+  if (WireDecodeText(data, *this, m_logger)) {
+    UpdateMetaClientPub();
+    UpdateMetaClientSub();
+  }
 }
 
-void ClientData4::ProcessIncomingBinary(std::span<const uint8_t> data) {
+void ServerImpl::ClientData4::ProcessIncomingBinary(
+    std::span<const uint8_t> data) {
   for (;;) {
     if (data.empty()) {
       break;
@@ -823,11 +496,8 @@
     if (pubuid == -1) {
       auto now = wpi::Now();
       DEBUG4("RTT ping from {}, responding with time={}", m_id, now);
-      {
-        auto out = m_wire.SendBinary();
-        WireEncodeBinary(out.Add(), -1, now, value);
-      }
-      m_wire.Flush();
+      m_wire.SendBinary(
+          [&](auto& os) { WireEncodeBinary(os, -1, now, value); });
       continue;
     }
 
@@ -836,46 +506,13 @@
   }
 }
 
-void ClientData4::SendValue(TopicData* topic, const Value& value,
-                            SendMode mode) {
-  if (m_local) {
-    mode = ClientData::kSendImmNoFlush;  // always send local immediately
-  }
-  switch (mode) {
-    case ClientData::kSendDisabled:  // do nothing
-      break;
-    case ClientData::kSendImmNoFlush:  // send immediately
-      WriteBinary(topic->id, value.time(), value);
-      if (m_local) {
-        Flush();
-      }
-      break;
-    case ClientData::kSendAll:  // append to outgoing
-      m_outgoing.emplace_back(ServerMessage{ServerValueMsg{topic->id, value}});
-      break;
-    case ClientData::kSendNormal: {
-      // scan outgoing and replace, or append if not present
-      bool found = false;
-      for (auto&& msg : m_outgoing) {
-        if (auto m = std::get_if<ServerValueMsg>(&msg.contents)) {
-          if (m->topic == topic->id) {
-            m->value = value;
-            found = true;
-            break;
-          }
-        }
-      }
-      if (!found) {
-        m_outgoing.emplace_back(
-            ServerMessage{ServerValueMsg{topic->id, value}});
-      }
-      break;
-    }
-  }
+void ServerImpl::ClientData4::SendValue(TopicData* topic, const Value& value,
+                                        ValueSendMode mode) {
+  m_outgoing.SendValue(topic->GetIdHandle(), value, mode);
 }
 
-void ClientData4::SendAnnounce(TopicData* topic,
-                               std::optional<int64_t> pubuid) {
+void ServerImpl::ClientData4::SendAnnounce(TopicData* topic,
+                                           std::optional<int64_t> pubuid) {
   auto& sent = m_announceSent[topic];
   if (sent) {
     return;
@@ -883,17 +520,24 @@
   sent = true;
 
   if (m_local) {
-    WireEncodeAnnounce(SendText().Add(), topic->name, topic->id, topic->typeStr,
-                       topic->properties, pubuid);
-    Flush();
-  } else {
-    m_outgoing.emplace_back(ServerMessage{AnnounceMsg{
-        topic->name, topic->id, topic->typeStr, pubuid, topic->properties}});
-    m_server.m_controlReady = true;
+    int unsent = m_wire.WriteText([&](auto& os) {
+      WireEncodeAnnounce(os, topic->name, topic->id, topic->typeStr,
+                         topic->properties, pubuid);
+    });
+    if (unsent < 0) {
+      return;  // error
+    }
+    if (unsent == 0 && m_wire.Flush() == 0) {
+      return;
+    }
   }
+  m_outgoing.SendMessage(topic->GetIdHandle(),
+                         AnnounceMsg{topic->name, topic->id, topic->typeStr,
+                                     pubuid, topic->properties});
+  m_server.m_controlReady = true;
 }
 
-void ClientData4::SendUnannounce(TopicData* topic) {
+void ServerImpl::ClientData4::SendUnannounce(TopicData* topic) {
   auto& sent = m_announceSent[topic];
   if (!sent) {
     return;
@@ -901,95 +545,91 @@
   sent = false;
 
   if (m_local) {
-    WireEncodeUnannounce(SendText().Add(), topic->name, topic->id);
-    Flush();
-  } else {
-    m_outgoing.emplace_back(
-        ServerMessage{UnannounceMsg{topic->name, topic->id}});
-    m_server.m_controlReady = true;
+    int unsent = m_wire.WriteText(
+        [&](auto& os) { WireEncodeUnannounce(os, topic->name, topic->id); });
+    if (unsent < 0) {
+      return;  // error
+    }
+    if (unsent == 0 && m_wire.Flush() == 0) {
+      return;
+    }
   }
+  m_outgoing.SendMessage(topic->GetIdHandle(),
+                         UnannounceMsg{topic->name, topic->id});
+  m_outgoing.EraseHandle(topic->GetIdHandle());
+  m_server.m_controlReady = true;
 }
 
-void ClientData4::SendPropertiesUpdate(TopicData* topic,
-                                       const wpi::json& update, bool ack) {
+void ServerImpl::ClientData4::SendPropertiesUpdate(TopicData* topic,
+                                                   const wpi::json& update,
+                                                   bool ack) {
   if (!m_announceSent.lookup(topic)) {
     return;
   }
 
   if (m_local) {
-    WireEncodePropertiesUpdate(SendText().Add(), topic->name, update, ack);
-    Flush();
-  } else {
-    m_outgoing.emplace_back(
-        ServerMessage{PropertiesUpdateMsg{topic->name, update, ack}});
-    m_server.m_controlReady = true;
-  }
-}
-
-void ClientData4::SendOutgoing(uint64_t curTimeMs) {
-  if (m_outgoing.empty()) {
-    return;  // nothing to do
-  }
-
-  // rate limit frequency of transmissions
-  if (curTimeMs < (m_lastSendMs + kMinPeriodMs)) {
-    return;
-  }
-
-  if (!m_wire.Ready()) {
-    ++m_notReadyCount;
-    if (m_notReadyCount > kWireMaxNotReady) {
-      m_wire.Disconnect("transmit stalled");
+    int unsent = m_wire.WriteText([&](auto& os) {
+      WireEncodePropertiesUpdate(os, topic->name, update, ack);
+    });
+    if (unsent < 0) {
+      return;  // error
     }
-    return;
-  }
-  m_notReadyCount = 0;
-
-  for (auto&& msg : m_outgoing) {
-    if (auto m = std::get_if<ServerValueMsg>(&msg.contents)) {
-      WriteBinary(m->topic, m->value.time(), m->value);
-    } else {
-      WireEncodeText(SendText().Add(), msg);
+    if (unsent == 0 && m_wire.Flush() == 0) {
+      return;
     }
   }
-  m_outgoing.resize(0);
-  m_lastSendMs = curTimeMs;
+  m_outgoing.SendMessage(topic->GetIdHandle(),
+                         PropertiesUpdateMsg{topic->name, update, ack});
+  m_server.m_controlReady = true;
 }
 
-void ClientData4::Flush() {
-  m_outText.reset();
-  m_outBinary.reset();
-  m_wire.Flush();
+void ServerImpl::ClientData4::SendOutgoing(uint64_t curTimeMs, bool flush) {
+  if (m_wire.GetVersion() >= 0x0401) {
+    if (!m_ping.Send(curTimeMs)) {
+      return;
+    }
+  }
+  m_outgoing.SendOutgoing(curTimeMs, flush);
 }
 
-bool ClientData3::TopicData3::UpdateFlags(TopicData* topic) {
+void ServerImpl::ClientData4::UpdatePeriod(TopicData::TopicClientData& tcd,
+                                           TopicData* topic) {
+  uint32_t period =
+      CalculatePeriod(tcd.subscribers, [](auto& x) { return x->periodMs; });
+  DEBUG4("updating {} period to {} ms", topic->name, period);
+  m_outgoing.SetPeriod(topic->GetIdHandle(), period);
+}
+
+bool ServerImpl::ClientData3::TopicData3::UpdateFlags(TopicData* topic) {
   unsigned int newFlags = topic->persistent ? NT_PERSISTENT : 0;
   bool updated = flags != newFlags;
   flags = newFlags;
   return updated;
 }
 
-void ClientData3::ProcessIncomingBinary(std::span<const uint8_t> data) {
+void ServerImpl::ClientData3::ProcessIncomingBinary(
+    std::span<const uint8_t> data) {
   if (!m_decoder.Execute(&data)) {
     m_wire.Disconnect(m_decoder.GetError());
   }
 }
 
-void ClientData3::SendValue(TopicData* topic, const Value& value,
-                            SendMode mode) {
+void ServerImpl::ClientData3::SendValue(TopicData* topic, const Value& value,
+                                        ValueSendMode mode) {
   if (m_state != kStateRunning) {
-    if (mode == kSendImmNoFlush) {
-      mode = kSendAll;
+    if (mode == ValueSendMode::kImm) {
+      mode = ValueSendMode::kAll;
     }
   } else if (m_local) {
-    mode = ClientData::kSendImmNoFlush;  // always send local immediately
+    mode = ValueSendMode::kImm;  // always send local immediately
   }
   TopicData3* topic3 = GetTopic3(topic);
+  bool added = false;
 
   switch (mode) {
-    case ClientData::kSendDisabled:  // do nothing
+    case ValueSendMode::kDisabled:  // do nothing
       break;
-    case ClientData::kSendImmNoFlush:  // send immediately and flush
+    case ValueSendMode::kImm:  // send immediately
       ++topic3->seqNum;
       if (topic3->sentAssign) {
         net3::WireEncodeEntryUpdate(m_wire.Send().stream(), topic->id,
@@ -1004,25 +644,27 @@
         Flush();
       }
       break;
-    case ClientData::kSendNormal: {
-      // scan outgoing and replace, or append if not present
-      bool found = false;
-      for (auto&& msg : m_outgoing) {
+    case ValueSendMode::kNormal: {
+      // replace, or append if not present
+      wpi::DenseMap<NT_Topic, size_t>::iterator it;
+      std::tie(it, added) =
+          m_outgoingValueMap.try_emplace(topic->id, m_outgoing.size());
+      if (!added && it->second < m_outgoing.size()) {
+        auto& msg = m_outgoing[it->second];
         if (msg.Is(net3::Message3::kEntryUpdate) ||
             msg.Is(net3::Message3::kEntryAssign)) {
-          if (msg.id() == topic->id) {
+          if (msg.id() == topic->id) {  // should always be true
             msg.SetValue(value);
-            found = true;
             break;
           }
         }
       }
-      if (found) {
-        break;
-      }
     }
       // fallthrough
-    case ClientData::kSendAll:  // append to outgoing
+    case ValueSendMode::kAll:  // append to outgoing
+      if (!added) {
+        m_outgoingValueMap[topic->id] = m_outgoing.size();
+      }
       ++topic3->seqNum;
       if (topic3->sentAssign) {
         m_outgoing.emplace_back(net3::Message3::EntryUpdate(
@@ -1037,8 +679,8 @@
   }
 }
 
-void ClientData3::SendAnnounce(TopicData* topic,
-                               std::optional<int64_t> pubuid) {
+void ServerImpl::ClientData3::SendAnnounce(TopicData* topic,
+                                           std::optional<int64_t> pubuid) {
   // ignore if we've not yet built the subscriber
   if (m_subscribers.empty()) {
     return;
@@ -1046,7 +688,7 @@
 
   // subscribe to all non-special topics
   if (!topic->special) {
-    topic->subscribers.Add(m_subscribers[0].get());
+    topic->clients[this].AddSubscriber(m_subscribers[0].get());
     m_server.UpdateMetaTopicSub(topic);
   }
 
@@ -1054,7 +696,7 @@
   // will get sent when the first value is sent (by SendValue).
 }
 
-void ClientData3::SendUnannounce(TopicData* topic) {
+void ServerImpl::ClientData3::SendUnannounce(TopicData* topic) {
   auto it = m_topics3.find(topic);
   if (it == m_topics3.end()) {
     return;  // never sent to client
@@ -1074,8 +716,9 @@
   }
 }
 
-void ClientData3::SendPropertiesUpdate(TopicData* topic,
-                                       const wpi::json& update, bool ack) {
+void ServerImpl::ClientData3::SendPropertiesUpdate(TopicData* topic,
+                                                   const wpi::json& update,
+                                                   bool ack) {
   if (ack) {
     return;  // we don't ack in NT3
   }
@@ -1099,7 +742,7 @@
   }
 }
 
-void ClientData3::SendOutgoing(uint64_t curTimeMs) {
+void ServerImpl::ClientData3::SendOutgoing(uint64_t curTimeMs, bool flush) {
   if (m_outgoing.empty() || m_state != kStateRunning) {
     return;  // nothing to do
   }
@@ -1110,23 +753,25 @@
   }
 
   if (!m_wire.Ready()) {
-    ++m_notReadyCount;
-    if (m_notReadyCount > kWireMaxNotReady) {
+    uint64_t lastFlushTime = m_wire.GetLastFlushTime();
+    uint64_t now = wpi::Now();
+    if (lastFlushTime != 0 && now > (lastFlushTime + kWireMaxNotReadyUs)) {
       m_wire.Disconnect("transmit stalled");
     }
     return;
   }
-  m_notReadyCount = 0;
 
   auto out = m_wire.Send();
   for (auto&& msg : m_outgoing) {
     net3::WireEncode(out.stream(), msg);
   }
+  m_wire.Flush();
   m_outgoing.resize(0);
+  m_outgoingValueMap.clear();
   m_lastSendMs = curTimeMs;
 }
 
-void ClientData3::KeepAlive() {
+void ServerImpl::ClientData3::KeepAlive() {
   DEBUG4("KeepAlive({})", m_id);
   if (m_state != kStateRunning) {
     m_decoder.SetError("received unexpected KeepAlive message");
@@ -1135,12 +780,12 @@
   // ignore
 }
 
-void ClientData3::ServerHelloDone() {
+void ServerImpl::ClientData3::ServerHelloDone() {
   DEBUG4("ServerHelloDone({})", m_id);
   m_decoder.SetError("received unexpected ServerHelloDone message");
 }
 
-void ClientData3::ClientHelloDone() {
+void ServerImpl::ClientData3::ClientHelloDone() {
   DEBUG4("ClientHelloDone({})", m_id);
   if (m_state != kStateServerHelloComplete) {
     m_decoder.SetError("received unexpected ClientHelloDone message");
@@ -1149,7 +794,7 @@
   m_state = kStateRunning;
 }
 
-void ClientData3::ClearEntries() {
+void ServerImpl::ClientData3::ClearEntries() {
   DEBUG4("ClearEntries({})", m_id);
   if (m_state != kStateRunning) {
     m_decoder.SetError("received unexpected ClearEntries message");
@@ -1168,7 +813,7 @@
       auto publisherIt = m_publishers.find(topic3it.second.pubuid);
       if (publisherIt != m_publishers.end()) {
         // remove publisher from topic
-        topic->publishers.Remove(publisherIt->second.get());
+        topic->RemovePublisher(this, publisherIt->second.get());
 
         // remove publisher from client
         m_publishers.erase(publisherIt);
@@ -1184,13 +829,13 @@
   }
 }
 
-void ClientData3::ProtoUnsup(unsigned int proto_rev) {
+void ServerImpl::ClientData3::ProtoUnsup(unsigned int proto_rev) {
   DEBUG4("ProtoUnsup({})", m_id);
   m_decoder.SetError("received unexpected ProtoUnsup message");
 }
 
-void ClientData3::ClientHello(std::string_view self_id,
-                              unsigned int proto_rev) {
+void ServerImpl::ClientData3::ClientHello(std::string_view self_id,
+                                          unsigned int proto_rev) {
   DEBUG4("ClientHello({}, '{}', {:04x})", m_id, self_id, proto_rev);
   if (m_state != kStateInitial) {
     m_decoder.SetError("received unexpected ClientHello message");
@@ -1219,10 +864,7 @@
   options.prefixMatch = true;
   sub = std::make_unique<SubscriberData>(
       this, std::span<const std::string>{{prefix}}, 0, options);
-  m_periodMs = std::gcd(m_periodMs, sub->periodMs);
-  if (m_periodMs < kMinPeriodMs) {
-    m_periodMs = kMinPeriodMs;
-  }
+  m_periodMs = UpdatePeriodCalc(m_periodMs, sub->periodMs);
   m_setPeriodic(m_periodMs);
 
   {
@@ -1233,7 +875,7 @@
           topic->lastValue) {
         DEBUG4("client {}: initial announce of '{}' (id {})", m_id, topic->name,
                topic->id);
-        topic->subscribers.Add(sub.get());
+        topic->clients[this].AddSubscriber(sub.get());
         m_server.UpdateMetaTopicSub(topic.get());
 
         TopicData3* topic3 = GetTopic3(topic.get());
@@ -1254,14 +896,16 @@
   UpdateMetaClientSub();
 }
 
-void ClientData3::ServerHello(unsigned int flags, std::string_view self_id) {
+void ServerImpl::ClientData3::ServerHello(unsigned int flags,
+                                          std::string_view self_id) {
   DEBUG4("ServerHello({}, {}, {})", m_id, flags, self_id);
   m_decoder.SetError("received unexpected ServerHello message");
 }
 
-void ClientData3::EntryAssign(std::string_view name, unsigned int id,
-                              unsigned int seq_num, const Value& value,
-                              unsigned int flags) {
+void ServerImpl::ClientData3::EntryAssign(std::string_view name,
+                                          unsigned int id, unsigned int seq_num,
+                                          const Value& value,
+                                          unsigned int flags) {
   DEBUG4("EntryAssign({}, {}, {}, {}, {})", m_id, id, seq_num,
          static_cast<int>(value.type()), flags);
   if (id != 0xffff) {
@@ -1281,7 +925,7 @@
   auto topic = m_server.CreateTopic(this, name, typeStr, properties);
   TopicData3* topic3 = GetTopic3(topic);
   if (topic3->published || topic3->sentAssign) {
-    WARNING("ignorning client {} duplicate publish of '{}'", m_id, name);
+    WARN("ignoring client {} duplicate publish of '{}'", m_id, name);
     return;
   }
   ++topic3->seqNum;
@@ -1298,7 +942,7 @@
   }
 
   // add publisher to topic
-  topic->publishers.Add(publisherIt->getSecond().get());
+  topic->AddPublisher(this, publisherIt->getSecond().get());
 
   // update meta data
   m_server.UpdateMetaTopicPub(topic);
@@ -1318,8 +962,8 @@
   }
 }
 
-void ClientData3::EntryUpdate(unsigned int id, unsigned int seq_num,
-                              const Value& value) {
+void ServerImpl::ClientData3::EntryUpdate(unsigned int id, unsigned int seq_num,
+                                          const Value& value) {
   DEBUG4("EntryUpdate({}, {}, {}, {})", m_id, id, seq_num,
          static_cast<int>(value.type()));
   if (m_state != kStateRunning) {
@@ -1348,7 +992,7 @@
         std::make_unique<PublisherData>(this, topic, topic3->pubuid));
     if (isNew) {
       // add publisher to topic
-      topic->publishers.Add(publisherIt->getSecond().get());
+      topic->AddPublisher(this, publisherIt->getSecond().get());
 
       // update meta data
       m_server.UpdateMetaTopicPub(topic);
@@ -1360,7 +1004,7 @@
   m_server.SetValue(this, topic, value);
 }
 
-void ClientData3::FlagsUpdate(unsigned int id, unsigned int flags) {
+void ServerImpl::ClientData3::FlagsUpdate(unsigned int id, unsigned int flags) {
   DEBUG4("FlagsUpdate({}, {}, {})", m_id, id, flags);
   if (m_state != kStateRunning) {
     m_decoder.SetError("received unexpected FlagsUpdate message");
@@ -1382,7 +1026,7 @@
   m_server.SetFlags(this, topic, flags);
 }
 
-void ClientData3::EntryDelete(unsigned int id) {
+void ServerImpl::ClientData3::EntryDelete(unsigned int id) {
   DEBUG4("EntryDelete({}, {})", m_id, id);
   if (m_state != kStateRunning) {
     m_decoder.SetError("received unexpected EntryDelete message");
@@ -1413,7 +1057,7 @@
       auto publisherIt = m_publishers.find(topic3it->second.pubuid);
       if (publisherIt != m_publishers.end()) {
         // remove publisher from topic
-        topic->publishers.Remove(publisherIt->second.get());
+        topic->RemovePublisher(this, publisherIt->second.get());
 
         // remove publisher from client
         m_publishers.erase(publisherIt);
@@ -1429,7 +1073,7 @@
   m_server.SetProperties(this, topic, {{"retained", false}});
 }
 
-bool TopicData::SetProperties(const wpi::json& update) {
+bool ServerImpl::TopicData::SetProperties(const wpi::json& update) {
   if (!update.is_object()) {
     return false;
   }
@@ -1448,7 +1092,7 @@
   return updated;
 }
 
-void TopicData::RefreshProperties() {
+void ServerImpl::TopicData::RefreshProperties() {
   persistent = false;
   retained = false;
 
@@ -1467,7 +1111,7 @@
   }
 }
 
-bool TopicData::SetFlags(unsigned int flags_) {
+bool ServerImpl::TopicData::SetFlags(unsigned int flags_) {
   bool updated;
   if ((flags_ & NT_PERSISTENT) != 0) {
     updated = !persistent;
@@ -1481,7 +1125,7 @@
   return updated;
 }
 
-bool SubscriberData::Matches(std::string_view name, bool special) {
+bool ServerImpl::SubscriberData::Matches(std::string_view name, bool special) {
   for (auto&& topicName : topicNames) {
     if ((!options.prefixMatch && name == topicName) ||
         (options.prefixMatch && (!special || !topicName.empty()) &&
@@ -1492,48 +1136,38 @@
   return false;
 }
 
-SImpl::SImpl(wpi::Logger& logger) : m_logger{logger} {
+ServerImpl::ServerImpl(wpi::Logger& logger) : m_logger{logger} {
   // local is client 0
   m_clients.emplace_back(std::make_unique<ClientDataLocal>(*this, 0, logger));
   m_localClient = static_cast<ClientDataLocal*>(m_clients.back().get());
 }
 
-std::pair<std::string, int> SImpl::AddClient(
+std::pair<std::string, int> ServerImpl::AddClient(
     std::string_view name, std::string_view connInfo, bool local,
     WireConnection& wire, ServerImpl::SetPeriodicFunc setPeriodic) {
-  // strip anything after @ in the name
-  name = wpi::split(name, '@').first;
   if (name.empty()) {
     name = "NT4";
   }
   size_t index = m_clients.size();
-  // find an empty slot and check for duplicates
+  // find an empty slot
   // just do a linear search as number of clients is typically small (<10)
-  int duplicateName = 0;
   for (size_t i = 0, end = index; i < end; ++i) {
-    auto& clientData = m_clients[i];
-    if (clientData && clientData->GetOriginalName() == name) {
-      ++duplicateName;
-    } else if (!clientData && index == end) {
+    if (!m_clients[i]) {
       index = i;
+      break;
     }
   }
   if (index == m_clients.size()) {
     m_clients.emplace_back();
   }
 
-  // if duplicate name, de-duplicate
-  std::string dedupName;
-  if (duplicateName > 0) {
-    dedupName = fmt::format("{}@{}", name, duplicateName);
-  } else {
-    dedupName = name;
-  }
+  // ensure name is unique by suffixing index
+  std::string dedupName = fmt::format("{}@{}", name, index);
 
   auto& clientData = m_clients[index];
-  clientData = std::make_unique<ClientData4>(name, dedupName, connInfo, local,
-                                             wire, std::move(setPeriodic),
-                                             *this, index, m_logger);
+  clientData = std::make_unique<ClientData4>(dedupName, connInfo, local, wire,
+                                             std::move(setPeriodic), *this,
+                                             index, m_logger);
 
   // create client meta topics
   clientData->m_metaPub =
@@ -1545,16 +1179,14 @@
   clientData->UpdateMetaClientPub();
   clientData->UpdateMetaClientSub();
 
-  wire.Flush();
-
   DEBUG3("AddClient('{}', '{}') -> {}", name, connInfo, index);
   return {std::move(dedupName), index};
 }
 
-int SImpl::AddClient3(std::string_view connInfo, bool local,
-                      net3::WireConnection3& wire,
-                      ServerImpl::Connected3Func connected,
-                      ServerImpl::SetPeriodicFunc setPeriodic) {
+int ServerImpl::AddClient3(std::string_view connInfo, bool local,
+                           net3::WireConnection3& wire,
+                           ServerImpl::Connected3Func connected,
+                           ServerImpl::SetPeriodicFunc setPeriodic) {
   size_t index = m_clients.size();
   // find an empty slot; we can't check for duplicates until we get a hello.
   // just do a linear search as number of clients is typically small (<10)
@@ -1576,24 +1208,21 @@
   return index;
 }
 
-void SImpl::RemoveClient(int clientId) {
+void ServerImpl::RemoveClient(int clientId) {
   DEBUG3("RemoveClient({})", clientId);
   auto& client = m_clients[clientId];
 
   // remove all publishers and subscribers for this client
   wpi::SmallVector<TopicData*, 16> toDelete;
   for (auto&& topic : m_topics) {
-    auto pubRemove =
-        std::remove_if(topic->publishers.begin(), topic->publishers.end(),
-                       [&](auto&& e) { return e->client == client.get(); });
-    bool pubChanged = pubRemove != topic->publishers.end();
-    topic->publishers.erase(pubRemove, topic->publishers.end());
-
-    auto subRemove =
-        std::remove_if(topic->subscribers.begin(), topic->subscribers.end(),
-                       [&](auto&& e) { return e->client == client.get(); });
-    bool subChanged = subRemove != topic->subscribers.end();
-    topic->subscribers.erase(subRemove, topic->subscribers.end());
+    bool pubChanged = false;
+    bool subChanged = false;
+    auto tcdIt = topic->clients.find(client.get());
+    if (tcdIt != topic->clients.end()) {
+      pubChanged = !tcdIt->second.publishers.empty();
+      subChanged = !tcdIt->second.subscribers.empty();
+      topic->clients.erase(tcdIt);
+    }
 
     if (!topic->IsPublished()) {
       toDelete.push_back(topic.get());
@@ -1618,7 +1247,7 @@
   client.reset();
 }
 
-bool SImpl::PersistentChanged() {
+bool ServerImpl::PersistentChanged() {
   bool rv = m_persistentChanged;
   m_persistentChanged = false;
   return rv;
@@ -1736,7 +1365,7 @@
   }
 }
 
-void SImpl::DumpPersistent(wpi::raw_ostream& os) {
+void ServerImpl::DumpPersistent(wpi::raw_ostream& os) {
   wpi::json::serializer s{os, ' ', 16};
   os << "[\n";
   bool first = true;
@@ -1776,7 +1405,7 @@
   return val;
 }
 
-std::string SImpl::LoadPersistent(std::string_view in) {
+std::string ServerImpl::LoadPersistent(std::string_view in) {
   if (in.empty()) {
     return {};
   }
@@ -1997,15 +1626,17 @@
   return allerrors;
 }
 
-TopicData* SImpl::CreateTopic(ClientData* client, std::string_view name,
-                              std::string_view typeStr,
-                              const wpi::json& properties, bool special) {
+ServerImpl::TopicData* ServerImpl::CreateTopic(ClientData* client,
+                                               std::string_view name,
+                                               std::string_view typeStr,
+                                               const wpi::json& properties,
+                                               bool special) {
   auto& topic = m_nameTopics[name];
   if (topic) {
     if (typeStr != topic->typeStr) {
       if (client) {
-        WARNING("client {} publish '{}' conflicting type '{}' (currently '{}')",
-                client->GetName(), name, typeStr, topic->typeStr);
+        WARN("client {} publish '{}' conflicting type '{}' (currently '{}')",
+             client->GetName(), name, typeStr, topic->typeStr);
       }
     }
   } else {
@@ -2025,15 +1656,17 @@
       wpi::SmallVector<SubscriberData*, 16> subscribersBuf;
       auto subscribers =
           aClient->GetSubscribers(name, topic->special, subscribersBuf);
-      for (auto subscriber : subscribers) {
-        topic->subscribers.Add(subscriber);
-      }
 
       // don't announce to this client if no subscribers
       if (subscribers.empty()) {
         continue;
       }
 
+      auto& tcd = topic->clients[aClient.get()];
+      for (auto subscriber : subscribers) {
+        tcd.AddSubscriber(subscriber);
+      }
+
       if (aClient.get() == client) {
         continue;  // don't announce to requesting client again
       }
@@ -2054,11 +1687,11 @@
   return topic;
 }
 
-TopicData* SImpl::CreateMetaTopic(std::string_view name) {
+ServerImpl::TopicData* ServerImpl::CreateMetaTopic(std::string_view name) {
   return CreateTopic(nullptr, name, "msgpack", {{"retained", true}}, true);
 }
 
-void SImpl::DeleteTopic(TopicData* topic) {
+void ServerImpl::DeleteTopic(TopicData* topic) {
   if (!topic) {
     return;
   }
@@ -2072,17 +1705,9 @@
   }
 
   // unannounce to all subscribers
-  wpi::SmallVector<bool, 16> clients;
-  clients.resize(m_clients.size());
-  for (auto&& sub : topic->subscribers) {
-    clients[sub->client->GetId()] = true;
-  }
-  for (size_t i = 0, iend = clients.size(); i < iend; ++i) {
-    if (!clients[i]) {
-      continue;
-    }
-    if (auto aClient = m_clients[i].get()) {
-      aClient->SendUnannounce(topic);
+  for (auto&& tcd : topic->clients) {
+    if (!tcd.second.subscribers.empty()) {
+      tcd.first->SendUnannounce(topic);
     }
   }
 
@@ -2091,8 +1716,8 @@
   m_topics.erase(topic->id);
 }
 
-void SImpl::SetProperties(ClientData* client, TopicData* topic,
-                          const wpi::json& update) {
+void ServerImpl::SetProperties(ClientData* client, TopicData* topic,
+                               const wpi::json& update) {
   DEBUG4("SetProperties({}, {}, {})", client ? client->GetId() : -1,
          topic->name, update.dump());
   bool wasPersistent = topic->persistent;
@@ -2105,7 +1730,8 @@
   }
 }
 
-void SImpl::SetFlags(ClientData* client, TopicData* topic, unsigned int flags) {
+void ServerImpl::SetFlags(ClientData* client, TopicData* topic,
+                          unsigned int flags) {
   bool wasPersistent = topic->persistent;
   if (topic->SetFlags(flags)) {
     // update persistentChanged flag
@@ -2122,10 +1748,11 @@
   }
 }
 
-void SImpl::SetValue(ClientData* client, TopicData* topic, const Value& value) {
+void ServerImpl::SetValue(ClientData* client, TopicData* topic,
+                          const Value& value) {
   // update retained value if from same client or timestamp newer
   if (!topic->lastValue || topic->lastValueClient == client ||
-      value.time() >= topic->lastValue.time()) {
+      topic->lastValue.time() == 0 || value.time() >= topic->lastValue.time()) {
     DEBUG4("updating '{}' last value (time was {} is {})", topic->name,
            topic->lastValue.time(), value.time());
     topic->lastValue = value;
@@ -2137,37 +1764,14 @@
     }
   }
 
-  // propagate to subscribers; as each client may have multiple subscribers,
-  // but we only want to send the value once, first map to clients and then
-  // take action based on union of subscriptions
-
-  // indexed by clientId
-  wpi::SmallVector<ClientData::SendMode, 16> toSend;
-  toSend.resize(m_clients.size());
-
-  for (auto&& subscriber : topic->subscribers) {
-    int id = subscriber->client->GetId();
-    if (subscriber->options.topicsOnly) {
-      continue;
-    } else if (subscriber->options.sendAll) {
-      toSend[id] = ClientData::kSendAll;
-    } else if (toSend[id] != ClientData::kSendAll) {
-      toSend[id] = ClientData::kSendNormal;
-    }
-  }
-
-  for (size_t i = 0, iend = toSend.size(); i < iend; ++i) {
-    auto aClient = m_clients[i].get();
-    if (!aClient || client == aClient) {
-      continue;  // don't echo back
-    }
-    if (toSend[i] != ClientData::kSendDisabled) {
-      aClient->SendValue(topic, value, toSend[i]);
+  for (auto&& tcd : topic->clients) {
+    if (tcd.second.sendMode != ValueSendMode::kDisabled) {
+      tcd.first->SendValue(topic, value, tcd.second.sendMode);
     }
   }
 }
 
-void SImpl::UpdateMetaClients(const std::vector<ConnectionInfo>& conns) {
+void ServerImpl::UpdateMetaClients(const std::vector<ConnectionInfo>& conns) {
   Writer w;
   mpack_start_array(&w, conns.size());
   for (auto&& conn : conns) {
@@ -2188,23 +1792,22 @@
   }
 }
 
-void SImpl::UpdateMetaTopicPub(TopicData* topic) {
+void ServerImpl::UpdateMetaTopicPub(TopicData* topic) {
   if (!topic->metaPub) {
     return;
   }
   Writer w;
-  mpack_start_array(&w, topic->publishers.size());
-  for (auto&& pub : topic->publishers) {
-    mpack_start_map(&w, 2);
-    mpack_write_str(&w, "client");
-    if (pub->client) {
-      mpack_write_str(&w, pub->client->GetName());
-    } else {
-      mpack_write_str(&w, "");
+  uint32_t count = 0;
+  for (auto&& tcd : topic->clients) {
+    count += tcd.second.publishers.size();
+  }
+  mpack_start_array(&w, count);
+  for (auto&& tcd : topic->clients) {
+    for (auto&& pub : tcd.second.publishers) {
+      mpack_write_object_bytes(
+          &w, reinterpret_cast<const char*>(pub->metaTopic.data()),
+          pub->metaTopic.size());
     }
-    mpack_write_str(&w, "pubuid");
-    mpack_write_int(&w, pub->pubuid);
-    mpack_finish_map(&w);
   }
   mpack_finish_array(&w);
   if (mpack_writer_destroy(&w) == mpack_ok) {
@@ -2212,25 +1815,22 @@
   }
 }
 
-void SImpl::UpdateMetaTopicSub(TopicData* topic) {
+void ServerImpl::UpdateMetaTopicSub(TopicData* topic) {
   if (!topic->metaSub) {
     return;
   }
   Writer w;
-  mpack_start_array(&w, topic->subscribers.size());
-  for (auto&& sub : topic->subscribers) {
-    mpack_start_map(&w, 3);
-    mpack_write_str(&w, "client");
-    if (sub->client) {
-      mpack_write_str(&w, sub->client->GetName());
-    } else {
-      mpack_write_str(&w, "");
+  uint32_t count = 0;
+  for (auto&& tcd : topic->clients) {
+    count += tcd.second.subscribers.size();
+  }
+  mpack_start_array(&w, count);
+  for (auto&& tcd : topic->clients) {
+    for (auto&& sub : tcd.second.subscribers) {
+      mpack_write_object_bytes(
+          &w, reinterpret_cast<const char*>(sub->metaTopic.data()),
+          sub->metaTopic.size());
     }
-    mpack_write_str(&w, "subuid");
-    mpack_write_int(&w, sub->subuid);
-    mpack_write_str(&w, "options");
-    WriteOptions(w, sub->options);
-    mpack_finish_map(&w);
   }
   mpack_finish_array(&w);
   if (mpack_writer_destroy(&w) == mpack_ok) {
@@ -2238,126 +1838,75 @@
   }
 }
 
-void SImpl::PropertiesChanged(ClientData* client, TopicData* topic,
-                              const wpi::json& update) {
+void ServerImpl::PropertiesChanged(ClientData* client, TopicData* topic,
+                                   const wpi::json& update) {
   // removing some properties can result in the topic being unpublished
   if (!topic->IsPublished()) {
     DeleteTopic(topic);
   } else {
     // send updated announcement to all subscribers
-    wpi::SmallVector<bool, 16> clients;
-    clients.resize(m_clients.size());
-    for (auto&& sub : topic->subscribers) {
-      clients[sub->client->GetId()] = true;
-    }
-    for (size_t i = 0, iend = clients.size(); i < iend; ++i) {
-      if (!clients[i]) {
-        continue;
-      }
-      if (auto aClient = m_clients[i].get()) {
-        aClient->SendPropertiesUpdate(topic, update, aClient == client);
-      }
+    for (auto&& tcd : topic->clients) {
+      tcd.first->SendPropertiesUpdate(topic, update, tcd.first == client);
     }
   }
 }
 
-class ServerImpl::Impl final : public SImpl {
- public:
-  explicit Impl(wpi::Logger& logger) : SImpl{logger} {}
-};
-
-ServerImpl::ServerImpl(wpi::Logger& logger)
-    : m_impl{std::make_unique<Impl>(logger)} {}
-
-ServerImpl::~ServerImpl() = default;
-
-void ServerImpl::SendControl(uint64_t curTimeMs) {
-  if (!m_impl->m_controlReady) {
-    return;
-  }
-  m_impl->m_controlReady = false;
-
-  for (auto&& client : m_impl->m_clients) {
+void ServerImpl::SendAllOutgoing(uint64_t curTimeMs, bool flush) {
+  for (auto&& client : m_clients) {
     if (client) {
-      // to ensure ordering, just send everything
-      client->SendOutgoing(curTimeMs);
-      client->Flush();
+      client->SendOutgoing(curTimeMs, flush);
     }
   }
 }
 
-void ServerImpl::SendValues(int clientId, uint64_t curTimeMs) {
-  auto client = m_impl->m_clients[clientId].get();
-  client->SendOutgoing(curTimeMs);
-  client->Flush();
+void ServerImpl::SendOutgoing(int clientId, uint64_t curTimeMs) {
+  if (auto client = m_clients[clientId].get()) {
+    client->SendOutgoing(curTimeMs, false);
+  }
 }
 
 void ServerImpl::HandleLocal(std::span<const ClientMessage> msgs) {
   // just map as a normal client into client=0 calls
-  m_impl->m_localClient->HandleLocal(msgs);
+  m_localClient->HandleLocal(msgs);
 }
 
 void ServerImpl::SetLocal(LocalInterface* local) {
-  WPI_DEBUG4(m_impl->m_logger, "SetLocal()");
-  m_impl->m_local = local;
+  DEBUG4("SetLocal()");
+  m_local = local;
 
   // create server meta topics
-  m_impl->m_metaClients = m_impl->CreateMetaTopic("$clients");
+  m_metaClients = CreateMetaTopic("$clients");
 
   // create local client meta topics
-  m_impl->m_localClient->m_metaPub = m_impl->CreateMetaTopic("$serverpub");
-  m_impl->m_localClient->m_metaSub = m_impl->CreateMetaTopic("$serversub");
+  m_localClient->m_metaPub = CreateMetaTopic("$serverpub");
+  m_localClient->m_metaSub = CreateMetaTopic("$serversub");
 
   // update meta topics
-  m_impl->m_localClient->UpdateMetaClientPub();
-  m_impl->m_localClient->UpdateMetaClientSub();
+  m_localClient->UpdateMetaClientPub();
+  m_localClient->UpdateMetaClientSub();
 }
 
 void ServerImpl::ProcessIncomingText(int clientId, std::string_view data) {
-  m_impl->m_clients[clientId]->ProcessIncomingText(data);
+  if (auto client = m_clients[clientId].get()) {
+    client->ProcessIncomingText(data);
+  }
 }
 
 void ServerImpl::ProcessIncomingBinary(int clientId,
                                        std::span<const uint8_t> data) {
-  m_impl->m_clients[clientId]->ProcessIncomingBinary(data);
-}
-
-std::pair<std::string, int> ServerImpl::AddClient(std::string_view name,
-                                                  std::string_view connInfo,
-                                                  bool local,
-                                                  WireConnection& wire,
-                                                  SetPeriodicFunc setPeriodic) {
-  return m_impl->AddClient(name, connInfo, local, wire, std::move(setPeriodic));
-}
-
-int ServerImpl::AddClient3(std::string_view connInfo, bool local,
-                           net3::WireConnection3& wire,
-                           Connected3Func connected,
-                           SetPeriodicFunc setPeriodic) {
-  return m_impl->AddClient3(connInfo, local, wire, std::move(connected),
-                            std::move(setPeriodic));
-}
-
-void ServerImpl::RemoveClient(int clientId) {
-  m_impl->RemoveClient(clientId);
+  if (auto client = m_clients[clientId].get()) {
+    client->ProcessIncomingBinary(data);
+  }
 }
 
 void ServerImpl::ConnectionsChanged(const std::vector<ConnectionInfo>& conns) {
-  m_impl->UpdateMetaClients(conns);
-}
-
-bool ServerImpl::PersistentChanged() {
-  return m_impl->PersistentChanged();
+  UpdateMetaClients(conns);
 }
 
 std::string ServerImpl::DumpPersistent() {
   std::string rv;
   wpi::raw_string_ostream os{rv};
-  m_impl->DumpPersistent(os);
+  DumpPersistent(os);
   os.flush();
   return rv;
 }
-
-std::string ServerImpl::LoadPersistent(std::string_view in) {
-  return m_impl->LoadPersistent(in);
-}
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/ServerImpl.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net/ServerImpl.h
index 86607e9..3ea5a82 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/ServerImpl.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/ServerImpl.h
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include <cmath>
 #include <functional>
 #include <memory>
 #include <span>
@@ -14,11 +15,33 @@
 #include <utility>
 #include <vector>
 
+#include <wpi/DenseMap.h>
+#include <wpi/SmallPtrSet.h>
+#include <wpi/StringMap.h>
+#include <wpi/UidVector.h>
+#include <wpi/json.h>
+
+#include "Handle.h"
+#include "Log.h"
+#include "Message.h"
 #include "NetworkInterface.h"
+#include "NetworkOutgoingQueue.h"
+#include "NetworkPing.h"
+#include "PubSubOptions.h"
+#include "VectorSet.h"
+#include "WireConnection.h"
+#include "WireDecoder.h"
+#include "WireEncoder.h"
+#include "net3/Message3.h"
+#include "net3/SequenceNumber.h"
 #include "net3/WireConnection3.h"
+#include "net3/WireDecoder3.h"
 
 namespace wpi {
 class Logger;
+template <typename T>
+class SmallVectorImpl;
+class raw_ostream;
 }  // namespace wpi
 
 namespace nt::net3 {
@@ -38,10 +61,9 @@
       std::function<void(std::string_view name, uint16_t proto)>;
 
   explicit ServerImpl(wpi::Logger& logger);
-  ~ServerImpl();
 
-  void SendControl(uint64_t curTimeMs);
-  void SendValues(int clientId, uint64_t curTimeMs);
+  void SendAllOutgoing(uint64_t curTimeMs, bool flush);
+  void SendOutgoing(int clientId, uint64_t curTimeMs);
 
   void HandleLocal(std::span<const ClientMessage> msgs);
   void SetLocal(LocalInterface* local);
@@ -69,8 +91,385 @@
   std::string LoadPersistent(std::string_view in);
 
  private:
-  class Impl;
-  std::unique_ptr<Impl> m_impl;
+  static constexpr uint32_t kMinPeriodMs = 5;
+
+  class ClientData;
+  struct PublisherData;
+  struct SubscriberData;
+
+  struct TopicData {
+    TopicData(std::string_view name, std::string_view typeStr)
+        : name{name}, typeStr{typeStr} {}
+    TopicData(std::string_view name, std::string_view typeStr,
+              wpi::json properties)
+        : name{name}, typeStr{typeStr}, properties(std::move(properties)) {
+      RefreshProperties();
+    }
+
+    bool IsPublished() const {
+      return persistent || retained || publisherCount != 0;
+    }
+
+    // returns true if properties changed
+    bool SetProperties(const wpi::json& update);
+    void RefreshProperties();
+    bool SetFlags(unsigned int flags_);
+
+    NT_Handle GetIdHandle() const { return Handle(0, id, Handle::kTopic); }
+
+    std::string name;
+    unsigned int id;
+    Value lastValue;
+    ClientData* lastValueClient = nullptr;
+    std::string typeStr;
+    wpi::json properties = wpi::json::object();
+    unsigned int publisherCount{0};
+    bool persistent{false};
+    bool retained{false};
+    bool special{false};
+    NT_Topic localHandle{0};
+
+    void AddPublisher(ClientData* client, PublisherData* pub) {
+      if (clients[client].publishers.insert(pub).second) {
+        ++publisherCount;
+      }
+    }
+
+    void RemovePublisher(ClientData* client, PublisherData* pub) {
+      if (clients[client].publishers.erase(pub)) {
+        --publisherCount;
+      }
+    }
+
+    struct TopicClientData {
+      wpi::SmallPtrSet<PublisherData*, 2> publishers;
+      wpi::SmallPtrSet<SubscriberData*, 2> subscribers;
+      ValueSendMode sendMode = ValueSendMode::kDisabled;
+
+      bool AddSubscriber(SubscriberData* sub) {
+        bool added = subscribers.insert(sub).second;
+        if (!sub->options.topicsOnly && sendMode == ValueSendMode::kDisabled) {
+          sendMode = ValueSendMode::kNormal;
+        } else if (sub->options.sendAll) {
+          sendMode = ValueSendMode::kAll;
+        }
+        return added;
+      }
+    };
+    wpi::SmallDenseMap<ClientData*, TopicClientData, 4> clients;
+
+    // meta topics
+    TopicData* metaPub = nullptr;
+    TopicData* metaSub = nullptr;
+  };
+
+  class ClientData {
+   public:
+    ClientData(std::string_view name, std::string_view connInfo, bool local,
+               ServerImpl::SetPeriodicFunc setPeriodic, ServerImpl& server,
+               int id, wpi::Logger& logger)
+        : m_name{name},
+          m_connInfo{connInfo},
+          m_local{local},
+          m_setPeriodic{std::move(setPeriodic)},
+          m_server{server},
+          m_id{id},
+          m_logger{logger} {}
+    virtual ~ClientData() = default;
+
+    virtual void ProcessIncomingText(std::string_view data) = 0;
+    virtual void ProcessIncomingBinary(std::span<const uint8_t> data) = 0;
+
+    virtual void SendValue(TopicData* topic, const Value& value,
+                           ValueSendMode mode) = 0;
+    virtual void SendAnnounce(TopicData* topic,
+                              std::optional<int64_t> pubuid) = 0;
+    virtual void SendUnannounce(TopicData* topic) = 0;
+    virtual void SendPropertiesUpdate(TopicData* topic, const wpi::json& update,
+                                      bool ack) = 0;
+    virtual void SendOutgoing(uint64_t curTimeMs, bool flush) = 0;
+    virtual void Flush() = 0;
+
+    void UpdateMetaClientPub();
+    void UpdateMetaClientSub();
+
+    std::span<SubscriberData*> GetSubscribers(
+        std::string_view name, bool special,
+        wpi::SmallVectorImpl<SubscriberData*>& buf);
+
+    std::string_view GetName() const { return m_name; }
+    int GetId() const { return m_id; }
+
+   protected:
+    virtual void UpdatePeriodic(TopicData* topic) {}
+
+    std::string m_name;
+    std::string m_connInfo;
+    bool m_local;  // local to machine
+    ServerImpl::SetPeriodicFunc m_setPeriodic;
+    // TODO: make this per-topic?
+    uint32_t m_periodMs{UINT32_MAX};
+    ServerImpl& m_server;
+    int m_id;
+
+    wpi::Logger& m_logger;
+
+    wpi::DenseMap<int64_t, std::unique_ptr<PublisherData>> m_publishers;
+    wpi::DenseMap<int64_t, std::unique_ptr<SubscriberData>> m_subscribers;
+
+   public:
+    // meta topics
+    TopicData* m_metaPub = nullptr;
+    TopicData* m_metaSub = nullptr;
+  };
+
+  class ClientData4Base : public ClientData, protected ClientMessageHandler {
+   public:
+    ClientData4Base(std::string_view name, std::string_view connInfo,
+                    bool local, ServerImpl::SetPeriodicFunc setPeriodic,
+                    ServerImpl& server, int id, wpi::Logger& logger)
+        : ClientData{name, connInfo, local, setPeriodic, server, id, logger} {}
+
+   protected:
+    // ClientMessageHandler interface
+    void ClientPublish(int64_t pubuid, std::string_view name,
+                       std::string_view typeStr,
+                       const wpi::json& properties) final;
+    void ClientUnpublish(int64_t pubuid) final;
+    void ClientSetProperties(std::string_view name,
+                             const wpi::json& update) final;
+    void ClientSubscribe(int64_t subuid,
+                         std::span<const std::string> topicNames,
+                         const PubSubOptionsImpl& options) final;
+    void ClientUnsubscribe(int64_t subuid) final;
+
+    void ClientSetValue(int64_t pubuid, const Value& value);
+
+    virtual void UpdatePeriod(TopicData::TopicClientData& tcd,
+                              TopicData* topic) {}
+
+    wpi::DenseMap<TopicData*, bool> m_announceSent;
+  };
+
+  class ClientDataLocal final : public ClientData4Base {
+   public:
+    ClientDataLocal(ServerImpl& server, int id, wpi::Logger& logger)
+        : ClientData4Base{"", "", true, [](uint32_t) {}, server, id, logger} {}
+
+    void ProcessIncomingText(std::string_view data) final {}
+    void ProcessIncomingBinary(std::span<const uint8_t> data) final {}
+
+    void SendValue(TopicData* topic, const Value& value,
+                   ValueSendMode mode) final;
+    void SendAnnounce(TopicData* topic, std::optional<int64_t> pubuid) final;
+    void SendUnannounce(TopicData* topic) final;
+    void SendPropertiesUpdate(TopicData* topic, const wpi::json& update,
+                              bool ack) final;
+    void SendOutgoing(uint64_t curTimeMs, bool flush) final {}
+    void Flush() final {}
+
+    void HandleLocal(std::span<const ClientMessage> msgs);
+  };
+
+  class ClientData4 final : public ClientData4Base {
+   public:
+    ClientData4(std::string_view name, std::string_view connInfo, bool local,
+                WireConnection& wire, ServerImpl::SetPeriodicFunc setPeriodic,
+                ServerImpl& server, int id, wpi::Logger& logger)
+        : ClientData4Base{name,   connInfo, local, setPeriodic,
+                          server, id,       logger},
+          m_wire{wire},
+          m_ping{wire},
+          m_outgoing{wire, local} {}
+
+    void ProcessIncomingText(std::string_view data) final;
+    void ProcessIncomingBinary(std::span<const uint8_t> data) final;
+
+    void SendValue(TopicData* topic, const Value& value,
+                   ValueSendMode mode) final;
+    void SendAnnounce(TopicData* topic, std::optional<int64_t> pubuid) final;
+    void SendUnannounce(TopicData* topic) final;
+    void SendPropertiesUpdate(TopicData* topic, const wpi::json& update,
+                              bool ack) final;
+    void SendOutgoing(uint64_t curTimeMs, bool flush) final;
+
+    void Flush() final {}
+
+    void UpdatePeriod(TopicData::TopicClientData& tcd, TopicData* topic) final;
+
+   public:
+    WireConnection& m_wire;
+
+   private:
+    NetworkPing m_ping;
+    NetworkOutgoingQueue<ServerMessage> m_outgoing;
+  };
+
+  class ClientData3 final : public ClientData, private net3::MessageHandler3 {
+   public:
+    ClientData3(std::string_view connInfo, bool local,
+                net3::WireConnection3& wire,
+                ServerImpl::Connected3Func connected,
+                ServerImpl::SetPeriodicFunc setPeriodic, ServerImpl& server,
+                int id, wpi::Logger& logger)
+        : ClientData{"", connInfo, local, setPeriodic, server, id, logger},
+          m_connected{std::move(connected)},
+          m_wire{wire},
+          m_decoder{*this} {}
+
+    void ProcessIncomingText(std::string_view data) final {}
+    void ProcessIncomingBinary(std::span<const uint8_t> data) final;
+
+    void SendValue(TopicData* topic, const Value& value,
+                   ValueSendMode mode) final;
+    void SendAnnounce(TopicData* topic, std::optional<int64_t> pubuid) final;
+    void SendUnannounce(TopicData* topic) final;
+    void SendPropertiesUpdate(TopicData* topic, const wpi::json& update,
+                              bool ack) final;
+    void SendOutgoing(uint64_t curTimeMs, bool flush) final;
+
+    void Flush() final { m_wire.Flush(); }
+
+   private:
+    // MessageHandler3 interface
+    void KeepAlive() final;
+    void ServerHelloDone() final;
+    void ClientHelloDone() final;
+    void ClearEntries() final;
+    void ProtoUnsup(unsigned int proto_rev) final;
+    void ClientHello(std::string_view self_id, unsigned int proto_rev) final;
+    void ServerHello(unsigned int flags, std::string_view self_id) final;
+    void EntryAssign(std::string_view name, unsigned int id,
+                     unsigned int seq_num, const Value& value,
+                     unsigned int flags) final;
+    void EntryUpdate(unsigned int id, unsigned int seq_num,
+                     const Value& value) final;
+    void FlagsUpdate(unsigned int id, unsigned int flags) final;
+    void EntryDelete(unsigned int id) final;
+    void ExecuteRpc(unsigned int id, unsigned int uid,
+                    std::span<const uint8_t> params) final {}
+    void RpcResponse(unsigned int id, unsigned int uid,
+                     std::span<const uint8_t> result) final {}
+
+    ServerImpl::Connected3Func m_connected;
+    net3::WireConnection3& m_wire;
+
+    enum State { kStateInitial, kStateServerHelloComplete, kStateRunning };
+    State m_state{kStateInitial};
+    net3::WireDecoder3 m_decoder;
+
+    std::vector<net3::Message3> m_outgoing;
+    wpi::DenseMap<NT_Topic, size_t> m_outgoingValueMap;
+    int64_t m_nextPubUid{1};
+    uint64_t m_lastSendMs{0};
+
+    struct TopicData3 {
+      explicit TopicData3(TopicData* topic) { UpdateFlags(topic); }
+
+      unsigned int flags{0};
+      net3::SequenceNumber seqNum;
+      bool sentAssign{false};
+      bool published{false};
+      int64_t pubuid{0};
+
+      bool UpdateFlags(TopicData* topic);
+    };
+    wpi::DenseMap<TopicData*, TopicData3> m_topics3;
+    TopicData3* GetTopic3(TopicData* topic) {
+      return &m_topics3.try_emplace(topic, topic).first->second;
+    }
+  };
+
+  struct PublisherData {
+    PublisherData(ClientData* client, TopicData* topic, int64_t pubuid)
+        : client{client}, topic{topic}, pubuid{pubuid} {
+      UpdateMeta();
+    }
+
+    void UpdateMeta();
+
+    ClientData* client;
+    TopicData* topic;
+    int64_t pubuid;
+    std::vector<uint8_t> metaClient;
+    std::vector<uint8_t> metaTopic;
+  };
+
+  struct SubscriberData {
+    SubscriberData(ClientData* client, std::span<const std::string> topicNames,
+                   int64_t subuid, const PubSubOptionsImpl& options)
+        : client{client},
+          topicNames{topicNames.begin(), topicNames.end()},
+          subuid{subuid},
+          options{options},
+          periodMs(std::lround(options.periodicMs / 10.0) * 10) {
+      UpdateMeta();
+      if (periodMs < kMinPeriodMs) {
+        periodMs = kMinPeriodMs;
+      }
+    }
+
+    void Update(std::span<const std::string> topicNames_,
+                const PubSubOptionsImpl& options_) {
+      topicNames = {topicNames_.begin(), topicNames_.end()};
+      options = options_;
+      UpdateMeta();
+      periodMs = std::lround(options_.periodicMs / 10.0) * 10;
+      if (periodMs < kMinPeriodMs) {
+        periodMs = kMinPeriodMs;
+      }
+    }
+
+    bool Matches(std::string_view name, bool special);
+
+    void UpdateMeta();
+
+    ClientData* client;
+    std::vector<std::string> topicNames;
+    int64_t subuid;
+    PubSubOptionsImpl options;
+    std::vector<uint8_t> metaClient;
+    std::vector<uint8_t> metaTopic;
+    wpi::DenseMap<TopicData*, bool> topics;
+    // in options as double, but copy here as integer; rounded to the nearest
+    // 10 ms
+    uint32_t periodMs;
+  };
+
+  wpi::Logger& m_logger;
+  LocalInterface* m_local{nullptr};
+  bool m_controlReady{false};
+
+  ClientDataLocal* m_localClient;
+  std::vector<std::unique_ptr<ClientData>> m_clients;
+  wpi::UidVector<std::unique_ptr<TopicData>, 16> m_topics;
+  wpi::StringMap<TopicData*> m_nameTopics;
+  bool m_persistentChanged{false};
+
+  // global meta topics (other meta topics are linked to from the specific
+  // client or topic)
+  TopicData* m_metaClients;
+
+  void DumpPersistent(wpi::raw_ostream& os);
+
+  // helper functions
+  TopicData* CreateTopic(ClientData* client, std::string_view name,
+                         std::string_view typeStr, const wpi::json& properties,
+                         bool special = false);
+  TopicData* CreateMetaTopic(std::string_view name);
+  void DeleteTopic(TopicData* topic);
+  void SetProperties(ClientData* client, TopicData* topic,
+                     const wpi::json& update);
+  void SetFlags(ClientData* client, TopicData* topic, unsigned int flags);
+  void SetValue(ClientData* client, TopicData* topic, const Value& value);
+
+  // update meta topic values from data structures
+  void UpdateMetaClients(const std::vector<ConnectionInfo>& conns);
+  void UpdateMetaTopicPub(TopicData* topic);
+  void UpdateMetaTopicSub(TopicData* topic);
+
+  void PropertiesChanged(ClientData* client, TopicData* topic,
+                         const wpi::json& update);
 };
 
 }  // namespace nt::net
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WebSocketConnection.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WebSocketConnection.cpp
index d3d192f..dcfbabe 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WebSocketConnection.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WebSocketConnection.cpp
@@ -4,116 +4,256 @@
 
 #include "WebSocketConnection.h"
 
+#include <algorithm>
 #include <span>
 
+#include <wpi/Endian.h>
 #include <wpi/SpanExtras.h>
+#include <wpi/raw_ostream.h>
+#include <wpi/timestamp.h>
 #include <wpinet/WebSocket.h>
+#include <wpinet/raw_uv_ostream.h>
 
 using namespace nt;
 using namespace nt::net;
 
-static constexpr size_t kAllocSize = 4096;
-static constexpr size_t kTextFrameRolloverSize = 4096;
-static constexpr size_t kBinaryFrameRolloverSize = 8192;
+// MTU - assume Ethernet, IPv6, TCP; does not include WS frame header (max 10)
+static constexpr size_t kMTU = 1500 - 40 - 20;
+static constexpr size_t kAllocSize = kMTU - 10;
+// leave enough room for a "typical" message size so we don't create lots of
+// fragmented frames
+static constexpr size_t kNewFrameThresholdBytes = kAllocSize - 50;
+static constexpr size_t kFlushThresholdFrames = 32;
+static constexpr size_t kFlushThresholdBytes = 16384;
+static constexpr size_t kMaxPoolSize = 32;
 
-WebSocketConnection::WebSocketConnection(wpi::WebSocket& ws)
-    : m_ws{ws},
-      m_text_os{m_text_buffers, [this] { return AllocBuf(); }},
-      m_binary_os{m_binary_buffers, [this] { return AllocBuf(); }} {}
+class WebSocketConnection::Stream final : public wpi::raw_ostream {
+ public:
+  explicit Stream(WebSocketConnection& conn) : m_conn{conn} {
+    auto& buf = conn.m_bufs.back();
+    SetBuffer(buf.base, kAllocSize);
+    SetNumBytesInBuffer(buf.len);
+  }
+
+  ~Stream() final {
+    m_disableAlloc = true;
+    flush();
+  }
+
+ private:
+  size_t preferred_buffer_size() const final { return 0; }
+  void write_impl(const char* data, size_t len) final;
+  uint64_t current_pos() const final { return m_conn.m_framePos; }
+
+  WebSocketConnection& m_conn;
+  bool m_disableAlloc = false;
+};
+
+void WebSocketConnection::Stream::write_impl(const char* data, size_t len) {
+  if (data == m_conn.m_bufs.back().base) {
+    // flush_nonempty() case
+    m_conn.m_bufs.back().len = len;
+    if (!m_disableAlloc) {
+      m_conn.m_frames.back().opcode &= ~wpi::WebSocket::kFlagFin;
+      m_conn.StartFrame(wpi::WebSocket::Frame::kFragment);
+      SetBuffer(m_conn.m_bufs.back().base, kAllocSize);
+    }
+    return;
+  }
+
+  bool updateBuffer = false;
+  while (len > 0) {
+    auto& buf = m_conn.m_bufs.back();
+    assert(buf.len <= kAllocSize);
+    size_t amt = (std::min)(static_cast<int>(kAllocSize - buf.len),
+                            static_cast<int>(len));
+    if (amt > 0) {
+      std::memcpy(buf.base + buf.len, data, amt);
+      buf.len += amt;
+      m_conn.m_framePos += amt;
+      m_conn.m_written += amt;
+      data += amt;
+      len -= amt;
+    }
+    if (buf.len >= kAllocSize && (len > 0 || !m_disableAlloc)) {
+      // fragment the current frame and start a new one
+      m_conn.m_frames.back().opcode &= ~wpi::WebSocket::kFlagFin;
+      m_conn.StartFrame(wpi::WebSocket::Frame::kFragment);
+      updateBuffer = true;
+    }
+  }
+
+  if (updateBuffer) {
+    SetBuffer(m_conn.m_bufs.back().base, kAllocSize);
+  }
+}
+
+WebSocketConnection::WebSocketConnection(wpi::WebSocket& ws,
+                                         unsigned int version)
+    : m_ws{ws}, m_version{version} {}
 
 WebSocketConnection::~WebSocketConnection() {
+  for (auto&& buf : m_bufs) {
+    buf.Deallocate();
+  }
   for (auto&& buf : m_buf_pool) {
     buf.Deallocate();
   }
 }
 
-void WebSocketConnection::Flush() {
-  FinishSendText();
-  FinishSendBinary();
-  if (m_frames.empty()) {
-    return;
+void WebSocketConnection::Start() {
+  m_ws.pong.connect([selfweak = weak_from_this()](auto data) {
+    if (data.size() != 8) {
+      return;
+    }
+    if (auto self = selfweak.lock()) {
+      self->m_lastPingResponse =
+          wpi::support::endian::read64<wpi::support::native>(data.data());
+    }
+  });
+}
+
+void WebSocketConnection::SendPing(uint64_t time) {
+  auto buf = AllocBuf();
+  buf.len = 8;
+  wpi::support::endian::write64<wpi::support::native>(buf.base, time);
+  m_ws.SendPing({buf}, [selfweak = weak_from_this()](auto bufs, auto err) {
+    if (auto self = selfweak.lock()) {
+      self->m_err = err;
+      self->ReleaseBufs(bufs);
+    } else {
+      for (auto&& buf : bufs) {
+        buf.Deallocate();
+      }
+    }
+  });
+}
+
+void WebSocketConnection::StartFrame(uint8_t opcode) {
+  m_frames.emplace_back(opcode, m_bufs.size(), m_bufs.size() + 1);
+  m_bufs.emplace_back(AllocBuf());
+  m_bufs.back().len = 0;
+}
+
+void WebSocketConnection::FinishText() {
+  assert(!m_bufs.empty());
+  auto& buf = m_bufs.back();
+  assert(buf.len < (kAllocSize + 1));  // safe because we alloc one more byte
+  buf.base[buf.len++] = ']';
+}
+
+int WebSocketConnection::Write(
+    State kind, wpi::function_ref<void(wpi::raw_ostream& os)> writer) {
+  bool first = false;
+  if (m_state != kind ||
+      (m_state == kind && m_framePos >= kNewFrameThresholdBytes)) {
+    // start a new frame
+    if (m_state == kText) {
+      FinishText();
+    }
+    m_state = kind;
+    if (!m_frames.empty()) {
+      m_frames.back().opcode |= wpi::WebSocket::kFlagFin;
+    }
+    StartFrame(m_state == kText ? wpi::WebSocket::Frame::kText
+                                : wpi::WebSocket::Frame::kBinary);
+    m_framePos = 0;
+    first = true;
   }
+  {
+    Stream os{*this};
+    if (kind == kText) {
+      os << (first ? '[' : ',');
+    }
+    writer(os);
+  }
+  ++m_frames.back().count;
+  if (m_frames.size() > kFlushThresholdFrames ||
+      m_written >= kFlushThresholdBytes) {
+    return Flush();
+  }
+  return 0;
+}
+
+int WebSocketConnection::Flush() {
+  m_lastFlushTime = wpi::Now();
+  if (m_state == kEmpty) {
+    return 0;
+  }
+  if (m_state == kText) {
+    FinishText();
+  }
+  m_state = kEmpty;
+  m_written = 0;
+
+  if (m_frames.empty()) {
+    return 0;
+  }
+  m_frames.back().opcode |= wpi::WebSocket::kFlagFin;
 
   // convert internal frames into WS frames
   m_ws_frames.clear();
   m_ws_frames.reserve(m_frames.size());
   for (auto&& frame : m_frames) {
-    m_ws_frames.emplace_back(frame.opcode,
-                             std::span{frame.bufs->begin() + frame.start,
-                                       frame.bufs->begin() + frame.end});
+    m_ws_frames.emplace_back(
+        frame.opcode,
+        std::span{m_bufs}.subspan(frame.start, frame.end - frame.start));
   }
 
-  ++m_sendsActive;
-  m_ws.SendFrames(m_ws_frames, [selfweak = weak_from_this()](auto bufs, auto) {
+  auto unsentFrames = m_ws.TrySendFrames(
+      m_ws_frames, [selfweak = weak_from_this()](auto bufs, auto err) {
+        if (auto self = selfweak.lock()) {
+          self->m_err = err;
+          self->ReleaseBufs(bufs);
+        } else {
+          for (auto&& buf : bufs) {
+            buf.Deallocate();
+          }
+        }
+      });
+  m_ws_frames.clear();
+  if (m_err) {
+    m_frames.clear();
+    m_bufs.clear();
+    return m_err.code();
+  }
+
+  int count = 0;
+  for (auto&& frame :
+       wpi::take_back(std::span{m_frames}, unsentFrames.size())) {
+    count += frame.count;
+  }
+  m_frames.clear();
+  m_bufs.clear();
+  return count;
+}
+
+void WebSocketConnection::Send(
+    uint8_t opcode, wpi::function_ref<void(wpi::raw_ostream& os)> writer) {
+  wpi::SmallVector<wpi::uv::Buffer, 4> bufs;
+  wpi::raw_uv_ostream os{bufs, [this] { return AllocBuf(); }};
+  if (opcode == wpi::WebSocket::Frame::kText) {
+    os << '[';
+  }
+  writer(os);
+  if (opcode == wpi::WebSocket::Frame::kText) {
+    os << ']';
+  }
+  wpi::WebSocket::Frame frame{opcode, os.bufs()};
+  m_ws.SendFrames({{frame}}, [selfweak = weak_from_this()](auto bufs, auto) {
     if (auto self = selfweak.lock()) {
-      self->m_buf_pool.insert(self->m_buf_pool.end(), bufs.begin(), bufs.end());
-      if (self->m_sendsActive > 0) {
-        --self->m_sendsActive;
+      self->ReleaseBufs(bufs);
+    } else {
+      for (auto&& buf : bufs) {
+        buf.Deallocate();
       }
     }
   });
-  m_frames.clear();
-  m_text_buffers.clear();
-  m_binary_buffers.clear();
-  m_text_pos = 0;
-  m_binary_pos = 0;
 }
 
 void WebSocketConnection::Disconnect(std::string_view reason) {
-  m_ws.Close(1005, reason);
-}
-
-void WebSocketConnection::StartSendText() {
-  // limit amount per single frame
-  size_t total = 0;
-  for (size_t i = m_text_pos; i < m_text_buffers.size(); ++i) {
-    total += m_text_buffers[i].len;
-  }
-  if (total >= kTextFrameRolloverSize) {
-    FinishSendText();
-  }
-
-  if (m_in_text) {
-    m_text_os << ',';
-  } else {
-    m_text_os << '[';
-    m_in_text = true;
-  }
-}
-
-void WebSocketConnection::FinishSendText() {
-  if (m_in_text) {
-    m_text_os << ']';
-    m_in_text = false;
-  }
-  if (m_text_pos >= m_text_buffers.size()) {
-    return;
-  }
-  m_frames.emplace_back(wpi::WebSocket::Frame::kText, &m_text_buffers,
-                        m_text_pos, m_text_buffers.size());
-  m_text_pos = m_text_buffers.size();
-  m_text_os.reset();
-}
-
-void WebSocketConnection::StartSendBinary() {
-  // limit amount per single frame
-  size_t total = 0;
-  for (size_t i = m_binary_pos; i < m_binary_buffers.size(); ++i) {
-    total += m_binary_buffers[i].len;
-  }
-  if (total >= kBinaryFrameRolloverSize) {
-    FinishSendBinary();
-  }
-}
-
-void WebSocketConnection::FinishSendBinary() {
-  if (m_binary_pos >= m_binary_buffers.size()) {
-    return;
-  }
-  m_frames.emplace_back(wpi::WebSocket::Frame::kBinary, &m_binary_buffers,
-                        m_binary_pos, m_binary_buffers.size());
-  m_binary_pos = m_binary_buffers.size();
-  m_binary_os.reset();
+  m_reason = reason;
+  m_ws.Fail(1005, reason);
 }
 
 wpi::uv::Buffer WebSocketConnection::AllocBuf() {
@@ -122,5 +262,17 @@
     m_buf_pool.pop_back();
     return buf;
   }
-  return wpi::uv::Buffer::Allocate(kAllocSize);
+  return wpi::uv::Buffer::Allocate(kAllocSize + 1);  // leave space for ']'
+}
+
+void WebSocketConnection::ReleaseBufs(std::span<wpi::uv::Buffer> bufs) {
+#ifdef __SANITIZE_ADDRESS__
+  size_t numToPool = 0;
+#else
+  size_t numToPool = (std::min)(bufs.size(), kMaxPoolSize - m_buf_pool.size());
+  m_buf_pool.insert(m_buf_pool.end(), bufs.begin(), bufs.begin() + numToPool);
+#endif
+  for (auto&& buf : bufs.subspan(numToPool)) {
+    buf.Deallocate();
+  }
 }
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WebSocketConnection.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WebSocketConnection.h
index ba15215..4398451 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WebSocketConnection.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WebSocketConnection.h
@@ -5,11 +5,12 @@
 #pragma once
 
 #include <memory>
+#include <string>
+#include <string_view>
 #include <vector>
 
-#include <wpi/SmallVector.h>
+#include <wpi/function_ref.h>
 #include <wpinet/WebSocket.h>
-#include <wpinet/raw_uv_ostream.h>
 #include <wpinet/uv/Buffer.h>
 
 #include "WireConnection.h"
@@ -20,50 +21,79 @@
     : public WireConnection,
       public std::enable_shared_from_this<WebSocketConnection> {
  public:
-  explicit WebSocketConnection(wpi::WebSocket& ws);
+  WebSocketConnection(wpi::WebSocket& ws, unsigned int version);
   ~WebSocketConnection() override;
   WebSocketConnection(const WebSocketConnection&) = delete;
   WebSocketConnection& operator=(const WebSocketConnection&) = delete;
 
-  bool Ready() const final { return m_sendsActive == 0; }
+  void Start();
 
-  TextWriter SendText() final { return {m_text_os, *this}; }
-  BinaryWriter SendBinary() final { return {m_binary_os, *this}; }
+  unsigned int GetVersion() const final { return m_version; }
 
-  void Flush() final;
+  void SendPing(uint64_t time) final;
+
+  bool Ready() const final { return !m_ws.IsWriteInProgress(); }
+
+  int WriteText(wpi::function_ref<void(wpi::raw_ostream& os)> writer) final {
+    return Write(kText, writer);
+  }
+  int WriteBinary(wpi::function_ref<void(wpi::raw_ostream& os)> writer) final {
+    return Write(kBinary, writer);
+  }
+  int Flush() final;
+
+  void SendText(wpi::function_ref<void(wpi::raw_ostream& os)> writer) final {
+    Send(wpi::WebSocket::Frame::kText, writer);
+  }
+  void SendBinary(wpi::function_ref<void(wpi::raw_ostream& os)> writer) final {
+    Send(wpi::WebSocket::Frame::kBinary, writer);
+  }
+
+  uint64_t GetLastFlushTime() const final { return m_lastFlushTime; }
+
+  uint64_t GetLastPingResponse() const final { return m_lastPingResponse; }
 
   void Disconnect(std::string_view reason) final;
 
- private:
-  void StartSendText() final;
-  void FinishSendText() final;
-  void StartSendBinary() final;
-  void FinishSendBinary() final;
+  std::string_view GetDisconnectReason() const { return m_reason; }
 
+ private:
+  enum State { kEmpty, kText, kBinary };
+
+  int Write(State kind, wpi::function_ref<void(wpi::raw_ostream& os)> writer);
+  void Send(uint8_t opcode,
+            wpi::function_ref<void(wpi::raw_ostream& os)> writer);
+
+  void StartFrame(uint8_t opcode);
+  void FinishText();
   wpi::uv::Buffer AllocBuf();
+  void ReleaseBufs(std::span<wpi::uv::Buffer> bufs);
 
   wpi::WebSocket& m_ws;
+
+  class Stream;
+
   // Can't use WS frames directly as span could have dangling pointers
   struct Frame {
-    Frame(uint8_t opcode, wpi::SmallVectorImpl<wpi::uv::Buffer>* bufs,
-          size_t start, size_t end)
-        : opcode{opcode}, bufs{bufs}, start{start}, end{end} {}
-    uint8_t opcode;
-    wpi::SmallVectorImpl<wpi::uv::Buffer>* bufs;
+    Frame(uint8_t opcode, size_t start, size_t end)
+        : start{start}, end{end}, opcode{opcode} {}
     size_t start;
     size_t end;
+    unsigned int count = 0;
+    uint8_t opcode;
   };
-  std::vector<Frame> m_frames;
   std::vector<wpi::WebSocket::Frame> m_ws_frames;  // to reduce allocs
-  wpi::SmallVector<wpi::uv::Buffer, 4> m_text_buffers;
-  wpi::SmallVector<wpi::uv::Buffer, 4> m_binary_buffers;
+  std::vector<Frame> m_frames;
+  std::vector<wpi::uv::Buffer> m_bufs;
   std::vector<wpi::uv::Buffer> m_buf_pool;
-  wpi::raw_uv_ostream m_text_os;
-  wpi::raw_uv_ostream m_binary_os;
-  size_t m_text_pos = 0;
-  size_t m_binary_pos = 0;
-  bool m_in_text = false;
-  int m_sendsActive = 0;
+  size_t m_framePos = 0;
+  size_t m_written = 0;
+  wpi::uv::Error m_err;
+  State m_state = kEmpty;
+  std::string m_reason;
+  uint64_t m_lastFlushTime = 0;
+  uint64_t m_lastPingResponse = 0;
+  unsigned int m_version;
 };
 
 }  // namespace nt::net
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireConnection.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireConnection.h
index 2a79a12..2c876cc 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireConnection.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireConnection.h
@@ -8,103 +8,53 @@
 
 #include <string_view>
 
-#include <wpi/raw_ostream.h>
+#include <wpi/function_ref.h>
+
+namespace wpi {
+class raw_ostream;
+}  // namespace wpi
 
 namespace nt::net {
 
-class BinaryWriter;
-class TextWriter;
-
 class WireConnection {
-  friend class TextWriter;
-  friend class BinaryWriter;
-
  public:
   virtual ~WireConnection() = default;
 
+  virtual unsigned int GetVersion() const = 0;
+
+  virtual void SendPing(uint64_t time) = 0;
+
   virtual bool Ready() const = 0;
 
-  virtual TextWriter SendText() = 0;
+  // These return <0 on error, 0 on success. On buffer full, a positive number
+  // is is returned indicating the number of previous messages (including this
+  // call) that were NOT sent, e.g. 1 if just this call to WriteText or
+  // WriteBinary was not sent, 2 if the this call and the *previous* call were
+  // not sent.
+  [[nodiscard]]
+  virtual int WriteText(
+      wpi::function_ref<void(wpi::raw_ostream& os)> writer) = 0;
+  [[nodiscard]]
+  virtual int WriteBinary(
+      wpi::function_ref<void(wpi::raw_ostream& os)> writer) = 0;
 
-  virtual BinaryWriter SendBinary() = 0;
+  // Flushes any pending buffers. Return value equivalent to
+  // WriteText/WriteBinary (e.g. 1 means the last WriteX call was not sent).
+  [[nodiscard]]
+  virtual int Flush() = 0;
 
-  virtual void Flush() = 0;
+  // These immediately send the data even if the buffer is full.
+  virtual void SendText(
+      wpi::function_ref<void(wpi::raw_ostream& os)> writer) = 0;
+  virtual void SendBinary(
+      wpi::function_ref<void(wpi::raw_ostream& os)> writer) = 0;
+
+  virtual uint64_t GetLastFlushTime() const = 0;  // in microseconds
+
+  // Gets the timestamp of the last ping we got a reply to
+  virtual uint64_t GetLastPingResponse() const = 0;  // in microseconds
 
   virtual void Disconnect(std::string_view reason) = 0;
-
- protected:
-  virtual void StartSendText() = 0;
-  virtual void FinishSendText() = 0;
-  virtual void StartSendBinary() = 0;
-  virtual void FinishSendBinary() = 0;
-};
-
-class TextWriter {
- public:
-  TextWriter(wpi::raw_ostream& os, WireConnection& wire)
-      : m_os{&os}, m_wire{&wire} {}
-  TextWriter(const TextWriter&) = delete;
-  TextWriter(TextWriter&& rhs) : m_os{rhs.m_os}, m_wire{rhs.m_wire} {
-    rhs.m_os = nullptr;
-    rhs.m_wire = nullptr;
-  }
-  TextWriter& operator=(const TextWriter&) = delete;
-  TextWriter& operator=(TextWriter&& rhs) {
-    m_os = rhs.m_os;
-    m_wire = rhs.m_wire;
-    rhs.m_os = nullptr;
-    rhs.m_wire = nullptr;
-    return *this;
-  }
-  ~TextWriter() {
-    if (m_os) {
-      m_wire->FinishSendText();
-    }
-  }
-
-  wpi::raw_ostream& Add() {
-    m_wire->StartSendText();
-    return *m_os;
-  }
-  WireConnection& wire() { return *m_wire; }
-
- private:
-  wpi::raw_ostream* m_os;
-  WireConnection* m_wire;
-};
-
-class BinaryWriter {
- public:
-  BinaryWriter(wpi::raw_ostream& os, WireConnection& wire)
-      : m_os{&os}, m_wire{&wire} {}
-  BinaryWriter(const BinaryWriter&) = delete;
-  BinaryWriter(BinaryWriter&& rhs) : m_os{rhs.m_os}, m_wire{rhs.m_wire} {
-    rhs.m_os = nullptr;
-    rhs.m_wire = nullptr;
-  }
-  BinaryWriter& operator=(const BinaryWriter&) = delete;
-  BinaryWriter& operator=(BinaryWriter&& rhs) {
-    m_os = rhs.m_os;
-    m_wire = rhs.m_wire;
-    rhs.m_os = nullptr;
-    rhs.m_wire = nullptr;
-    return *this;
-  }
-  ~BinaryWriter() {
-    if (m_wire) {
-      m_wire->FinishSendBinary();
-    }
-  }
-
-  wpi::raw_ostream& Add() {
-    m_wire->StartSendBinary();
-    return *m_os;
-  }
-  WireConnection& wire() { return *m_wire; }
-
- private:
-  wpi::raw_ostream* m_os;
-  WireConnection* m_wire;
 };
 
 }  // namespace nt::net
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireDecoder.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireDecoder.cpp
index e6474b2..536a62f 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireDecoder.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireDecoder.cpp
@@ -5,6 +5,7 @@
 #include "WireDecoder.h"
 
 #include <algorithm>
+#include <concepts>
 
 #include <fmt/format.h>
 #include <wpi/Logger.h>
@@ -104,25 +105,24 @@
 #endif
 
 template <typename T>
-static void WireDecodeTextImpl(std::string_view in, T& out,
+  requires(std::same_as<T, ClientMessageHandler> ||
+           std::same_as<T, ServerMessageHandler>)
+static bool WireDecodeTextImpl(std::string_view in, T& out,
                                wpi::Logger& logger) {
-  static_assert(std::is_same_v<T, ClientMessageHandler> ||
-                    std::is_same_v<T, ServerMessageHandler>,
-                "T must be ClientMessageHandler or ServerMessageHandler");
-
   wpi::json j;
   try {
     j = wpi::json::parse(in);
   } catch (wpi::json::parse_error& err) {
     WPI_WARNING(logger, "could not decode JSON message: {}", err.what());
-    return;
+    return false;
   }
 
   if (!j.is_array()) {
     WPI_WARNING(logger, "expected JSON array at top level");
-    return;
+    return false;
   }
 
+  bool rv = false;
   int i = -1;
   for (auto&& jmsg : j) {
     ++i;
@@ -150,7 +150,7 @@
         goto err;
       }
 
-      if constexpr (std::is_same_v<T, ClientMessageHandler>) {
+      if constexpr (std::same_as<T, ClientMessageHandler>) {
         if (*method == PublishMsg::kMethodStr) {
           // name
           auto name = ObjGetString(*params, "name", &error);
@@ -188,6 +188,7 @@
 
           // complete
           out.ClientPublish(pubuid, *name, *typeStr, *properties);
+          rv = true;
         } else if (*method == UnpublishMsg::kMethodStr) {
           // pubuid
           int64_t pubuid;
@@ -197,6 +198,7 @@
 
           // complete
           out.ClientUnpublish(pubuid);
+          rv = true;
         } else if (*method == SetPropertiesMsg::kMethodStr) {
           // name
           auto name = ObjGetString(*params, "name", &error);
@@ -289,6 +291,7 @@
 
           // complete
           out.ClientSubscribe(subuid, topicNames, options);
+          rv = true;
         } else if (*method == UnsubscribeMsg::kMethodStr) {
           // subuid
           int64_t subuid;
@@ -298,11 +301,12 @@
 
           // complete
           out.ClientUnsubscribe(subuid);
+          rv = true;
         } else {
           error = fmt::format("unrecognized method '{}'", *method);
           goto err;
         }
-      } else if constexpr (std::is_same_v<T, ServerMessageHandler>) {
+      } else if constexpr (std::same_as<T, ServerMessageHandler>) {
         if (*method == AnnounceMsg::kMethodStr) {
           // name
           auto name = ObjGetString(*params, "name", &error);
@@ -405,15 +409,17 @@
   err:
     WPI_WARNING(logger, "{}: {}", i, error);
   }
+
+  return rv;
 }
 
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
 
-void nt::net::WireDecodeText(std::string_view in, ClientMessageHandler& out,
+bool nt::net::WireDecodeText(std::string_view in, ClientMessageHandler& out,
                              wpi::Logger& logger) {
-  ::WireDecodeTextImpl(in, out, logger);
+  return ::WireDecodeTextImpl(in, out, logger);
 }
 
 void nt::net::WireDecodeText(std::string_view in, ServerMessageHandler& out,
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireDecoder.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireDecoder.h
index 128dff2..b4f7b4e 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireDecoder.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireDecoder.h
@@ -11,9 +11,10 @@
 #include <string>
 #include <string_view>
 
+#include <wpi/json_fwd.h>
+
 namespace wpi {
 class Logger;
-class json;
 }  // namespace wpi
 
 namespace nt {
@@ -51,7 +52,8 @@
                                       const wpi::json& update, bool ack) = 0;
 };
 
-void WireDecodeText(std::string_view in, ClientMessageHandler& out,
+// return true if client pub/sub metadata needs updating
+bool WireDecodeText(std::string_view in, ClientMessageHandler& out,
                     wpi::Logger& logger);
 void WireDecodeText(std::string_view in, ServerMessageHandler& out,
                     wpi::Logger& logger);
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireEncoder.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireEncoder.cpp
index 143fe0d..42de91c 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireEncoder.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireEncoder.cpp
@@ -6,7 +6,7 @@
 
 #include <optional>
 
-#include <wpi/json_serializer.h>
+#include <wpi/json.h>
 #include <wpi/mpack.h>
 #include <wpi/raw_ostream.h>
 
@@ -142,10 +142,9 @@
   } else if (auto m = std::get_if<SetPropertiesMsg>(&msg.contents)) {
     WireEncodeSetProperties(os, m->name, m->update);
   } else if (auto m = std::get_if<SubscribeMsg>(&msg.contents)) {
-    WireEncodeSubscribe(os, Handle{m->subHandle}.GetIndex(), m->topicNames,
-                        m->options);
+    WireEncodeSubscribe(os, m->subHandle, m->topicNames, m->options);
   } else if (auto m = std::get_if<UnsubscribeMsg>(&msg.contents)) {
-    WireEncodeUnsubscribe(os, Handle{m->subHandle}.GetIndex());
+    WireEncodeUnsubscribe(os, m->subHandle);
   } else {
     return false;
   }
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireEncoder.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireEncoder.h
index d0a04cb..00d8e12 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireEncoder.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net/WireEncoder.h
@@ -9,8 +9,9 @@
 #include <string>
 #include <string_view>
 
+#include <wpi/json_fwd.h>
+
 namespace wpi {
-class json;
 class raw_ostream;
 }  // namespace wpi
 
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/ClientImpl3.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/ClientImpl3.cpp
index 0783865..a65dd89 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/ClientImpl3.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/ClientImpl3.cpp
@@ -12,17 +12,13 @@
 #include <fmt/format.h>
 #include <wpi/DenseMap.h>
 #include <wpi/StringMap.h>
-#include <wpi/json.h>
+#include <wpi/timestamp.h>
 
 #include "Handle.h"
 #include "Log.h"
 #include "Types_internal.h"
 #include "net/Message.h"
 #include "net/NetworkInterface.h"
-#include "net3/Message3.h"
-#include "net3/SequenceNumber.h"
-#include "net3/WireConnection3.h"
-#include "net3/WireDecoder3.h"
 #include "net3/WireEncoder3.h"
 #include "networktables/NetworkTableValue.h"
 
@@ -31,150 +27,11 @@
 
 static constexpr uint32_t kMinPeriodMs = 5;
 
-// maximum number of times the wire can be not ready to send another
+// maximum amount of time the wire can be not ready to send another
 // transmission before we close the connection
-static constexpr int kWireMaxNotReady = 10;
+static constexpr uint32_t kWireMaxNotReadyUs = 1000000;
 
-namespace {
-
-struct Entry;
-
-struct PublisherData {
-  explicit PublisherData(Entry* entry) : entry{entry} {}
-
-  Entry* entry;
-  NT_Publisher handle;
-  PubSubOptionsImpl options;
-  // in options as double, but copy here as integer; rounded to the nearest
-  // 10 ms
-  uint32_t periodMs;
-  uint64_t nextSendMs{0};
-  std::vector<Value> outValues;  // outgoing values
-};
-
-// data for each entry
-struct Entry {
-  explicit Entry(std::string_view name_) : name(name_) {}
-  bool IsPersistent() const { return (flags & NT_PERSISTENT) != 0; }
-  wpi::json SetFlags(unsigned int flags_);
-
-  std::string name;
-
-  std::string typeStr;
-  NT_Type type{NT_UNASSIGNED};
-
-  wpi::json properties = wpi::json::object();
-
-  // The current value and flags
-  Value value;
-  unsigned int flags{0};
-
-  // Unique ID used in network messages; this is 0xffff until assigned
-  // by the server.
-  unsigned int id{0xffff};
-
-  // Sequence number for update resolution
-  SequenceNumber seqNum;
-
-  // Local topic handle
-  NT_Topic topic{0};
-
-  // Local publishers
-  std::vector<PublisherData*> publishers;
-};
-
-class CImpl : public MessageHandler3 {
- public:
-  CImpl(uint64_t curTimeMs, int inst, WireConnection3& wire,
-        wpi::Logger& logger,
-        std::function<void(uint32_t repeatMs)> setPeriodic);
-
-  void ProcessIncoming(std::span<const uint8_t> data);
-  void HandleLocal(std::span<const net::ClientMessage> msgs);
-  void SendPeriodic(uint64_t curTimeMs, bool initial);
-  void SendValue(Writer& out, Entry* entry, const Value& value);
-  bool CheckNetworkReady();
-
-  // Outgoing handlers
-  void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
-               std::string_view name, std::string_view typeStr,
-               const wpi::json& properties, const PubSubOptionsImpl& options);
-  void Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle);
-  void SetProperties(NT_Topic topicHandle, std::string_view name,
-                     const wpi::json& update);
-  void SetValue(NT_Publisher pubHandle, const Value& value);
-
-  // MessageHandler interface
-  void KeepAlive() final;
-  void ServerHelloDone() final;
-  void ClientHelloDone() final;
-  void ClearEntries() final;
-  void ProtoUnsup(unsigned int proto_rev) final;
-  void ClientHello(std::string_view self_id, unsigned int proto_rev) final;
-  void ServerHello(unsigned int flags, std::string_view self_id) final;
-  void EntryAssign(std::string_view name, unsigned int id, unsigned int seq_num,
-                   const Value& value, unsigned int flags) final;
-  void EntryUpdate(unsigned int id, unsigned int seq_num,
-                   const Value& value) final;
-  void FlagsUpdate(unsigned int id, unsigned int flags) final;
-  void EntryDelete(unsigned int id) final;
-  void ExecuteRpc(unsigned int id, unsigned int uid,
-                  std::span<const uint8_t> params) final {}
-  void RpcResponse(unsigned int id, unsigned int uid,
-                   std::span<const uint8_t> result) final {}
-
-  enum State {
-    kStateInitial,
-    kStateHelloSent,
-    kStateInitialAssignments,
-    kStateRunning
-  };
-
-  int m_inst;
-  WireConnection3& m_wire;
-  wpi::Logger& m_logger;
-  net::LocalInterface* m_local{nullptr};
-  std::function<void(uint32_t repeatMs)> m_setPeriodic;
-  uint64_t m_initTimeMs;
-
-  // periodic sweep handling
-  static constexpr uint32_t kKeepAliveIntervalMs = 1000;
-  uint32_t m_periodMs{kKeepAliveIntervalMs + 10};
-  uint64_t m_lastSendMs{0};
-  uint64_t m_nextKeepAliveTimeMs;
-  int m_notReadyCount{0};
-
-  // indexed by publisher index
-  std::vector<std::unique_ptr<PublisherData>> m_publishers;
-
-  State m_state{kStateInitial};
-  WireDecoder3 m_decoder;
-  std::string m_remoteId;
-  std::function<void()> m_handshakeSucceeded;
-
-  std::vector<std::pair<unsigned int, unsigned int>> m_outgoingFlags;
-
-  using NameMap = wpi::StringMap<std::unique_ptr<Entry>>;
-  using IdMap = std::vector<Entry*>;
-
-  NameMap m_nameMap;
-  IdMap m_idMap;
-
-  Entry* GetOrNewEntry(std::string_view name) {
-    auto& entry = m_nameMap[name];
-    if (!entry) {
-      entry = std::make_unique<Entry>(name);
-    }
-    return entry.get();
-  }
-  Entry* LookupId(unsigned int id) {
-    return id < m_idMap.size() ? m_idMap[id] : nullptr;
-  }
-};
-
-}  // namespace
-
-wpi::json Entry::SetFlags(unsigned int flags_) {
+wpi::json ClientImpl3::Entry::SetFlags(unsigned int flags_) {
   bool wasPersistent = IsPersistent();
   flags = flags_;
   bool isPersistent = IsPersistent();
@@ -189,25 +46,28 @@
   }
 }
 
-CImpl::CImpl(uint64_t curTimeMs, int inst, WireConnection3& wire,
-             wpi::Logger& logger,
-             std::function<void(uint32_t repeatMs)> setPeriodic)
-    : m_inst{inst},
-      m_wire{wire},
+ClientImpl3::ClientImpl3(uint64_t curTimeMs, int inst, WireConnection3& wire,
+                         wpi::Logger& logger,
+                         std::function<void(uint32_t repeatMs)> setPeriodic)
+    : m_wire{wire},
       m_logger{logger},
       m_setPeriodic{std::move(setPeriodic)},
       m_initTimeMs{curTimeMs},
       m_nextKeepAliveTimeMs{curTimeMs + kKeepAliveIntervalMs},
       m_decoder{*this} {}
 
-void CImpl::ProcessIncoming(std::span<const uint8_t> data) {
+ClientImpl3::~ClientImpl3() {
+  DEBUG4("NT3 ClientImpl destroyed");
+}
+
+void ClientImpl3::ProcessIncoming(std::span<const uint8_t> data) {
   DEBUG4("received {} bytes", data.size());
   if (!m_decoder.Execute(&data)) {
     m_wire.Disconnect(m_decoder.GetError());
   }
 }
 
-void CImpl::HandleLocal(std::span<const net::ClientMessage> msgs) {
+void ClientImpl3::HandleLocal(std::span<const net::ClientMessage> msgs) {
   for (const auto& elem : msgs) {  // NOLINT
     // common case is value
     if (auto msg = std::get_if<net::ClientValueMsg>(&elem.contents)) {
@@ -223,7 +83,7 @@
   }
 }
 
-void CImpl::SendPeriodic(uint64_t curTimeMs, bool initial) {
+void ClientImpl3::DoSendPeriodic(uint64_t curTimeMs, bool initial, bool flush) {
   DEBUG4("SendPeriodic({})", curTimeMs);
 
   // rate limit sends
@@ -233,9 +93,9 @@
 
   auto out = m_wire.Send();
 
-  // send keep-alives
+  // send keep-alive
   if (curTimeMs >= m_nextKeepAliveTimeMs) {
-    if (!CheckNetworkReady()) {
+    if (!CheckNetworkReady(curTimeMs)) {
       return;
     }
     DEBUG4("Sending keep alive");
@@ -246,7 +106,7 @@
 
   // send any stored-up flags updates
   if (!m_outgoingFlags.empty()) {
-    if (!CheckNetworkReady()) {
+    if (!CheckNetworkReady(curTimeMs)) {
       return;
     }
     for (auto&& p : m_outgoingFlags) {
@@ -258,9 +118,10 @@
   // send any pending updates due to be sent
   bool checkedNetwork = false;
   for (auto&& pub : m_publishers) {
-    if (pub && !pub->outValues.empty() && curTimeMs >= pub->nextSendMs) {
+    if (pub && !pub->outValues.empty() &&
+        (flush || curTimeMs >= pub->nextSendMs)) {
       if (!checkedNetwork) {
-        if (!CheckNetworkReady()) {
+        if (!CheckNetworkReady(curTimeMs)) {
           return;
         }
         checkedNetwork = true;
@@ -282,7 +143,7 @@
   m_lastSendMs = curTimeMs;
 }
 
-void CImpl::SendValue(Writer& out, Entry* entry, const Value& value) {
+void ClientImpl3::SendValue(Writer& out, Entry* entry, const Value& value) {
   DEBUG4("sending value for '{}', seqnum {}", entry->name,
          entry->seqNum.value());
 
@@ -301,22 +162,22 @@
   }
 }
 
-bool CImpl::CheckNetworkReady() {
+bool ClientImpl3::CheckNetworkReady(uint64_t curTimeMs) {
   if (!m_wire.Ready()) {
-    ++m_notReadyCount;
-    if (m_notReadyCount > kWireMaxNotReady) {
+    uint64_t lastFlushTime = m_wire.GetLastFlushTime();
+    uint64_t now = wpi::Now();
+    if (lastFlushTime != 0 && now > (lastFlushTime + kWireMaxNotReadyUs)) {
       m_wire.Disconnect("transmit stalled");
     }
     return false;
   }
-  m_notReadyCount = 0;
   return true;
 }
 
-void CImpl::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
-                    std::string_view name, std::string_view typeStr,
-                    const wpi::json& properties,
-                    const PubSubOptionsImpl& options) {
+void ClientImpl3::Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
+                          std::string_view name, std::string_view typeStr,
+                          const wpi::json& properties,
+                          const PubSubOptionsImpl& options) {
   DEBUG4("Publish('{}', '{}')", name, typeStr);
   unsigned int index = Handle{pubHandle}.GetIndex();
   if (index >= m_publishers.size()) {
@@ -341,7 +202,7 @@
   m_setPeriodic(m_periodMs);
 }
 
-void CImpl::Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle) {
+void ClientImpl3::Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle) {
   DEBUG4("Unpublish({}, {})", pubHandle, topicHandle);
   unsigned int index = Handle{pubHandle}.GetIndex();
   if (index >= m_publishers.size()) {
@@ -364,8 +225,8 @@
   m_setPeriodic(m_periodMs);
 }
 
-void CImpl::SetProperties(NT_Topic topicHandle, std::string_view name,
-                          const wpi::json& update) {
+void ClientImpl3::SetProperties(NT_Topic topicHandle, std::string_view name,
+                                const wpi::json& update) {
   DEBUG4("SetProperties({}, {}, {})", topicHandle, name, update.dump());
   auto entry = GetOrNewEntry(name);
   bool updated = false;
@@ -387,7 +248,7 @@
   }
 }
 
-void CImpl::SetValue(NT_Publisher pubHandle, const Value& value) {
+void ClientImpl3::SetValue(NT_Publisher pubHandle, const Value& value) {
   DEBUG4("SetValue({})", pubHandle);
   unsigned int index = Handle{pubHandle}.GetIndex();
   assert(index < m_publishers.size() && m_publishers[index]);
@@ -403,7 +264,7 @@
   }
 }
 
-void CImpl::KeepAlive() {
+void ClientImpl3::KeepAlive() {
   DEBUG4("KeepAlive()");
   if (m_state != kStateRunning && m_state != kStateInitialAssignments) {
     m_decoder.SetError("received unexpected KeepAlive message");
@@ -412,7 +273,7 @@
   // ignore
 }
 
-void CImpl::ServerHelloDone() {
+void ClientImpl3::ServerHelloDone() {
   DEBUG4("ServerHelloDone()");
   if (m_state != kStateInitialAssignments) {
     m_decoder.SetError("received unexpected ServerHelloDone message");
@@ -420,28 +281,29 @@
   }
 
   // send initial assignments
-  SendPeriodic(m_initTimeMs, true);
+  DoSendPeriodic(m_initTimeMs, true, true);
 
   m_state = kStateRunning;
   m_setPeriodic(m_periodMs);
 }
 
-void CImpl::ClientHelloDone() {
+void ClientImpl3::ClientHelloDone() {
   DEBUG4("ClientHelloDone()");
   m_decoder.SetError("received unexpected ClientHelloDone message");
 }
 
-void CImpl::ProtoUnsup(unsigned int proto_rev) {
+void ClientImpl3::ProtoUnsup(unsigned int proto_rev) {
   DEBUG4("ProtoUnsup({})", proto_rev);
   m_decoder.SetError(fmt::format("received ProtoUnsup(version={})", proto_rev));
 }
 
-void CImpl::ClientHello(std::string_view self_id, unsigned int proto_rev) {
+void ClientImpl3::ClientHello(std::string_view self_id,
+                              unsigned int proto_rev) {
   DEBUG4("ClientHello({}, {})", self_id, proto_rev);
   m_decoder.SetError("received unexpected ClientHello message");
 }
 
-void CImpl::ServerHello(unsigned int flags, std::string_view self_id) {
+void ClientImpl3::ServerHello(unsigned int flags, std::string_view self_id) {
   DEBUG4("ServerHello({}, {})", flags, self_id);
   if (m_state != kStateHelloSent) {
     m_decoder.SetError("received unexpected ServerHello message");
@@ -453,9 +315,9 @@
   m_handshakeSucceeded = nullptr;  // no longer required
 }
 
-void CImpl::EntryAssign(std::string_view name, unsigned int id,
-                        unsigned int seq_num, const Value& value,
-                        unsigned int flags) {
+void ClientImpl3::EntryAssign(std::string_view name, unsigned int id,
+                              unsigned int seq_num, const Value& value,
+                              unsigned int flags) {
   DEBUG4("EntryAssign({}, {}, {}, value, {})", name, id, seq_num, flags);
   if (m_state != kStateInitialAssignments && m_state != kStateRunning) {
     m_decoder.SetError("received unexpected EntryAssign message");
@@ -512,8 +374,8 @@
   }
 }
 
-void CImpl::EntryUpdate(unsigned int id, unsigned int seq_num,
-                        const Value& value) {
+void ClientImpl3::EntryUpdate(unsigned int id, unsigned int seq_num,
+                              const Value& value) {
   DEBUG4("EntryUpdate({}, {}, value)", id, seq_num);
   if (m_state != kStateRunning) {
     m_decoder.SetError("received EntryUpdate message before ServerHelloDone");
@@ -527,7 +389,7 @@
   }
 }
 
-void CImpl::FlagsUpdate(unsigned int id, unsigned int flags) {
+void ClientImpl3::FlagsUpdate(unsigned int id, unsigned int flags) {
   DEBUG4("FlagsUpdate({}, {})", id, flags);
   if (m_state != kStateRunning) {
     m_decoder.SetError("received FlagsUpdate message before ServerHelloDone");
@@ -547,7 +409,7 @@
       m_outgoingFlags.end());
 }
 
-void CImpl::EntryDelete(unsigned int id) {
+void ClientImpl3::EntryDelete(unsigned int id) {
   DEBUG4("EntryDelete({})", id);
   if (m_state != kStateRunning) {
     m_decoder.SetError("received EntryDelete message before ServerHelloDone");
@@ -572,7 +434,7 @@
       m_outgoingFlags.end());
 }
 
-void CImpl::ClearEntries() {
+void ClientImpl3::ClearEntries() {
   DEBUG4("ClearEntries()");
   if (m_state != kStateRunning) {
     m_decoder.SetError("received ClearEntries message before ServerHelloDone");
@@ -596,47 +458,14 @@
   m_outgoingFlags.resize(0);
 }
 
-class ClientImpl3::Impl final : public CImpl {
- public:
-  Impl(uint64_t curTimeMs, int inst, WireConnection3& wire, wpi::Logger& logger,
-       std::function<void(uint32_t repeatMs)> setPeriodic)
-      : CImpl{curTimeMs, inst, wire, logger, std::move(setPeriodic)} {}
-};
-
-ClientImpl3::ClientImpl3(uint64_t curTimeMs, int inst, WireConnection3& wire,
-                         wpi::Logger& logger,
-                         std::function<void(uint32_t repeatMs)> setPeriodic)
-    : m_impl{std::make_unique<Impl>(curTimeMs, inst, wire, logger,
-                                    std::move(setPeriodic))} {}
-
-ClientImpl3::~ClientImpl3() {
-  WPI_DEBUG4(m_impl->m_logger, "NT3 ClientImpl destroyed");
-}
-
 void ClientImpl3::Start(std::string_view selfId,
                         std::function<void()> succeeded) {
-  if (m_impl->m_state != CImpl::kStateInitial) {
+  if (m_state != kStateInitial) {
     return;
   }
-  m_impl->m_handshakeSucceeded = std::move(succeeded);
-  auto writer = m_impl->m_wire.Send();
+  m_handshakeSucceeded = std::move(succeeded);
+  auto writer = m_wire.Send();
   WireEncodeClientHello(writer.stream(), selfId, 0x0300);
-  m_impl->m_wire.Flush();
-  m_impl->m_state = CImpl::kStateHelloSent;
-}
-
-void ClientImpl3::ProcessIncoming(std::span<const uint8_t> data) {
-  m_impl->ProcessIncoming(data);
-}
-
-void ClientImpl3::HandleLocal(std::span<const net::ClientMessage> msgs) {
-  m_impl->HandleLocal(msgs);
-}
-
-void ClientImpl3::SendPeriodic(uint64_t curTimeMs) {
-  m_impl->SendPeriodic(curTimeMs, false);
-}
-
-void ClientImpl3::SetLocal(net::LocalInterface* local) {
-  m_impl->m_local = local;
+  m_wire.Flush();
+  m_state = kStateHelloSent;
 }
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/ClientImpl3.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/ClientImpl3.h
index 484ea3d..49489c5 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/ClientImpl3.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/ClientImpl3.h
@@ -11,8 +11,18 @@
 #include <span>
 #include <string>
 #include <string_view>
+#include <utility>
+#include <vector>
 
+#include <wpi/StringMap.h>
+#include <wpi/json.h>
+
+#include "PubSubOptions.h"
 #include "net/NetworkInterface.h"
+#include "net3/Message3.h"
+#include "net3/SequenceNumber.h"
+#include "net3/WireConnection3.h"
+#include "net3/WireDecoder3.h"
 
 namespace wpi {
 class Logger;
@@ -27,24 +37,147 @@
 
 class WireConnection3;
 
-class ClientImpl3 {
+class ClientImpl3 final : private MessageHandler3 {
  public:
   explicit ClientImpl3(uint64_t curTimeMs, int inst, WireConnection3& wire,
                        wpi::Logger& logger,
                        std::function<void(uint32_t repeatMs)> setPeriodic);
-  ~ClientImpl3();
+  ~ClientImpl3() final;
 
   void Start(std::string_view selfId, std::function<void()> succeeded);
   void ProcessIncoming(std::span<const uint8_t> data);
   void HandleLocal(std::span<const net::ClientMessage> msgs);
 
-  void SendPeriodic(uint64_t curTimeMs);
+  void SendPeriodic(uint64_t curTimeMs, bool flush) {
+    DoSendPeriodic(curTimeMs, false, flush);
+  }
 
-  void SetLocal(net::LocalInterface* local);
+  void SetLocal(net::LocalInterface* local) { m_local = local; }
 
  private:
-  class Impl;
-  std::unique_ptr<Impl> m_impl;
+  struct Entry;
+
+  struct PublisherData {
+    explicit PublisherData(Entry* entry) : entry{entry} {}
+
+    Entry* entry;
+    NT_Publisher handle;
+    PubSubOptionsImpl options;
+    // in options as double, but copy here as integer; rounded to the nearest
+    // 10 ms
+    uint32_t periodMs;
+    uint64_t nextSendMs{0};
+    std::vector<Value> outValues;  // outgoing values
+  };
+
+  // data for each entry
+  struct Entry {
+    explicit Entry(std::string_view name_) : name(name_) {}
+    bool IsPersistent() const { return (flags & NT_PERSISTENT) != 0; }
+    wpi::json SetFlags(unsigned int flags_);
+
+    std::string name;
+
+    std::string typeStr;
+    NT_Type type{NT_UNASSIGNED};
+
+    wpi::json properties = wpi::json::object();
+
+    // The current value and flags
+    Value value;
+    unsigned int flags{0};
+
+    // Unique ID used in network messages; this is 0xffff until assigned
+    // by the server.
+    unsigned int id{0xffff};
+
+    // Sequence number for update resolution
+    SequenceNumber seqNum;
+
+    // Local topic handle
+    NT_Topic topic{0};
+
+    // Local publishers
+    std::vector<PublisherData*> publishers;
+  };
+
+  void DoSendPeriodic(uint64_t curTimeMs, bool initial, bool flush);
+  void SendValue(Writer& out, Entry* entry, const Value& value);
+  bool CheckNetworkReady(uint64_t curTimeMs);
+
+  // Outgoing handlers
+  void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
+               std::string_view name, std::string_view typeStr,
+               const wpi::json& properties, const PubSubOptionsImpl& options);
+  void Unpublish(NT_Publisher pubHandle, NT_Topic topicHandle);
+  void SetProperties(NT_Topic topicHandle, std::string_view name,
+                     const wpi::json& update);
+  void SetValue(NT_Publisher pubHandle, const Value& value);
+
+  // MessageHandler interface
+  void KeepAlive() final;
+  void ServerHelloDone() final;
+  void ClientHelloDone() final;
+  void ClearEntries() final;
+  void ProtoUnsup(unsigned int proto_rev) final;
+  void ClientHello(std::string_view self_id, unsigned int proto_rev) final;
+  void ServerHello(unsigned int flags, std::string_view self_id) final;
+  void EntryAssign(std::string_view name, unsigned int id, unsigned int seq_num,
+                   const Value& value, unsigned int flags) final;
+  void EntryUpdate(unsigned int id, unsigned int seq_num,
+                   const Value& value) final;
+  void FlagsUpdate(unsigned int id, unsigned int flags) final;
+  void EntryDelete(unsigned int id) final;
+  void ExecuteRpc(unsigned int id, unsigned int uid,
+                  std::span<const uint8_t> params) final {}
+  void RpcResponse(unsigned int id, unsigned int uid,
+                   std::span<const uint8_t> result) final {}
+
+  enum State {
+    kStateInitial,
+    kStateHelloSent,
+    kStateInitialAssignments,
+    kStateRunning
+  };
+
+  WireConnection3& m_wire;
+  wpi::Logger& m_logger;
+  net::LocalInterface* m_local{nullptr};
+  std::function<void(uint32_t repeatMs)> m_setPeriodic;
+  uint64_t m_initTimeMs;
+
+  // periodic sweep handling
+  static constexpr uint32_t kKeepAliveIntervalMs = 1000;
+  uint32_t m_periodMs{kKeepAliveIntervalMs + 10};
+  uint64_t m_lastSendMs{0};
+  uint64_t m_nextKeepAliveTimeMs;
+
+  // indexed by publisher index
+  std::vector<std::unique_ptr<PublisherData>> m_publishers;
+
+  State m_state{kStateInitial};
+  WireDecoder3 m_decoder;
+  std::string m_remoteId;
+  std::function<void()> m_handshakeSucceeded;
+
+  std::vector<std::pair<unsigned int, unsigned int>> m_outgoingFlags;
+
+  using NameMap = wpi::StringMap<std::unique_ptr<Entry>>;
+  using IdMap = std::vector<Entry*>;
+
+  NameMap m_nameMap;
+  IdMap m_idMap;
+
+  Entry* GetOrNewEntry(std::string_view name) {
+    auto& entry = m_nameMap[name];
+    if (!entry) {
+      entry = std::make_unique<Entry>(name);
+    }
+    return entry.get();
+  }
+  Entry* LookupId(unsigned int id) {
+    return id < m_idMap.size() ? m_idMap[id] : nullptr;
+  }
 };
 
 }  // namespace nt::net3
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/UvStreamConnection3.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/UvStreamConnection3.cpp
index 93af700..efc4534 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/UvStreamConnection3.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/UvStreamConnection3.cpp
@@ -4,11 +4,14 @@
 
 #include "UvStreamConnection3.h"
 
+#include <wpi/timestamp.h>
 #include <wpinet/uv/Stream.h>
 
 using namespace nt;
 using namespace nt::net3;
 
+static constexpr size_t kMaxPoolSize = 16;
+
 UvStreamConnection3::UvStreamConnection3(wpi::uv::Stream& stream)
     : m_stream{stream}, m_os{m_buffers, [this] { return AllocBuf(); }} {}
 
@@ -25,7 +28,17 @@
   ++m_sendsActive;
   m_stream.Write(m_buffers, [selfweak = weak_from_this()](auto bufs, auto) {
     if (auto self = selfweak.lock()) {
-      self->m_buf_pool.insert(self->m_buf_pool.end(), bufs.begin(), bufs.end());
+#ifdef __SANITIZE_ADDRESS__
+      size_t numToPool = 0;
+#else
+      size_t numToPool =
+          (std::min)(bufs.size(), kMaxPoolSize - self->m_buf_pool.size());
+      self->m_buf_pool.insert(self->m_buf_pool.end(), bufs.begin(),
+                              bufs.begin() + numToPool);
+#endif
+      for (auto&& buf : bufs.subspan(numToPool)) {
+        buf.Deallocate();
+      }
       if (self->m_sendsActive > 0) {
         --self->m_sendsActive;
       }
@@ -33,6 +46,7 @@
   });
   m_buffers.clear();
   m_os.reset();
+  m_lastFlushTime = wpi::Now();
 }
 
 void UvStreamConnection3::Disconnect(std::string_view reason) {
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/UvStreamConnection3.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/UvStreamConnection3.h
index f94c4a3..35eef02 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/UvStreamConnection3.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/UvStreamConnection3.h
@@ -38,6 +38,8 @@
 
   void Flush() final;
 
+  uint64_t GetLastFlushTime() const final { return m_lastFlushTime; }
+
   void Disconnect(std::string_view reason) final;
 
   std::string_view GetDisconnectReason() const { return m_reason; }
@@ -54,6 +56,7 @@
   std::vector<wpi::uv::Buffer> m_buf_pool;
   wpi::raw_uv_ostream m_os;
   std::string m_reason;
+  uint64_t m_lastFlushTime = 0;
   int m_sendsActive = 0;
 };
 
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireConnection3.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireConnection3.h
index 85453d7..0bb26f6 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireConnection3.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireConnection3.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <stdint.h>
+
 #include <string_view>
 
 namespace wpi {
@@ -26,6 +28,8 @@
 
   virtual void Flush() = 0;
 
+  virtual uint64_t GetLastFlushTime() const = 0;  // in microseconds
+
   virtual void Disconnect(std::string_view reason) = 0;
 
  protected:
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireDecoder3.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireDecoder3.cpp
index ea08358..5118027 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireDecoder3.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireDecoder3.cpp
@@ -5,147 +5,24 @@
 #include "WireDecoder3.h"
 
 #include <algorithm>
-#include <optional>
 #include <string>
-#include <vector>
 
 #include <fmt/format.h>
 #include <wpi/MathExtras.h>
 #include <wpi/SpanExtras.h>
-#include <wpi/leb128.h>
 
 #include "Message3.h"
 
 using namespace nt;
 using namespace nt::net3;
 
-namespace {
-
-class SimpleValueReader {
- public:
-  std::optional<uint16_t> Read16(std::span<const uint8_t>* in);
-  std::optional<uint32_t> Read32(std::span<const uint8_t>* in);
-  std::optional<uint64_t> Read64(std::span<const uint8_t>* in);
-  std::optional<double> ReadDouble(std::span<const uint8_t>* in);
-
- private:
-  uint64_t m_value = 0;
-  int m_count = 0;
-};
-
-struct StringReader {
-  void SetLen(uint64_t len_) {
-    len = len_;
-    buf.clear();
-  }
-
-  std::optional<uint64_t> len;
-  std::string buf;
-};
-
-struct RawReader {
-  void SetLen(uint64_t len_) {
-    len = len_;
-    buf.clear();
-  }
-
-  std::optional<uint64_t> len;
-  std::vector<uint8_t> buf;
-};
-
-struct ValueReader {
-  ValueReader() = default;
-  explicit ValueReader(NT_Type type_) : type{type_} {}
-
-  void SetSize(uint32_t size_) {
-    haveSize = true;
-    size = size_;
-    ints.clear();
-    doubles.clear();
-    strings.clear();
-  }
-
-  NT_Type type = NT_UNASSIGNED;
-  bool haveSize = false;
-  uint32_t size = 0;
-  std::vector<int> ints;
-  std::vector<double> doubles;
-  std::vector<std::string> strings;
-};
-
-struct WDImpl {
-  explicit WDImpl(MessageHandler3& out) : m_out{out} {}
-
-  MessageHandler3& m_out;
-
-  // primary (message) decode state
-  enum {
-    kStart,
-    kClientHello_1ProtoRev,
-    kClientHello_2Id,
-    kProtoUnsup_1ProtoRev,
-    kServerHello_1Flags,
-    kServerHello_2Id,
-    kEntryAssign_1Name,
-    kEntryAssign_2Type,
-    kEntryAssign_3Id,
-    kEntryAssign_4SeqNum,
-    kEntryAssign_5Flags,
-    kEntryAssign_6Value,
-    kEntryUpdate_1Id,
-    kEntryUpdate_2SeqNum,
-    kEntryUpdate_3Type,
-    kEntryUpdate_4Value,
-    kFlagsUpdate_1Id,
-    kFlagsUpdate_2Flags,
-    kEntryDelete_1Id,
-    kClearEntries_1Magic,
-    kExecuteRpc_1Id,
-    kExecuteRpc_2Uid,
-    kExecuteRpc_3Params,
-    kRpcResponse_1Id,
-    kRpcResponse_2Uid,
-    kRpcResponse_3Result,
-    kError
-  } m_state = kStart;
-
-  // detail decoders
-  wpi::Uleb128Reader m_ulebReader;
-  SimpleValueReader m_simpleReader;
-  StringReader m_stringReader;
-  RawReader m_rawReader;
-  ValueReader m_valueReader;
-
-  std::string m_error;
-
-  std::string m_str;
-  unsigned int m_id{0};  // also used for proto_rev
-  unsigned int m_flags{0};
-  unsigned int m_seq_num_uid{0};
-
-  void Execute(std::span<const uint8_t>* in);
-
-  std::nullopt_t EmitError(std::string_view msg) {
-    m_state = kError;
-    m_error = msg;
-    return std::nullopt;
-  }
-
-  std::optional<std::string> ReadString(std::span<const uint8_t>* in);
-  std::optional<std::vector<uint8_t>> ReadRaw(std::span<const uint8_t>* in);
-  std::optional<NT_Type> ReadType(std::span<const uint8_t>* in);
-  std::optional<Value> ReadValue(std::span<const uint8_t>* in);
-};
-
-}  // namespace
-
 static uint8_t Read8(std::span<const uint8_t>* in) {
   uint8_t val = in->front();
   *in = wpi::drop_front(*in);
   return val;
 }
 
-std::optional<uint16_t> SimpleValueReader::Read16(
+std::optional<uint16_t> WireDecoder3::SimpleValueReader::Read16(
     std::span<const uint8_t>* in) {
   while (!in->empty()) {
     m_value <<= 8;
@@ -161,7 +38,7 @@
   return std::nullopt;
 }
 
-std::optional<uint32_t> SimpleValueReader::Read32(
+std::optional<uint32_t> WireDecoder3::SimpleValueReader::Read32(
     std::span<const uint8_t>* in) {
   while (!in->empty()) {
     m_value <<= 8;
@@ -177,7 +54,7 @@
   return std::nullopt;
 }
 
-std::optional<uint64_t> SimpleValueReader::Read64(
+std::optional<uint64_t> WireDecoder3::SimpleValueReader::Read64(
     std::span<const uint8_t>* in) {
   while (!in->empty()) {
     m_value <<= 8;
@@ -193,16 +70,16 @@
   return std::nullopt;
 }
 
-std::optional<double> SimpleValueReader::ReadDouble(
+std::optional<double> WireDecoder3::SimpleValueReader::ReadDouble(
     std::span<const uint8_t>* in) {
   if (auto val = Read64(in)) {
-    return wpi::BitsToDouble(val.value());
+    return wpi::bit_cast<double>(val.value());
   } else {
     return std::nullopt;
   }
 }
 
-void WDImpl::Execute(std::span<const uint8_t>* in) {
+void WireDecoder3::DoExecute(std::span<const uint8_t>* in) {
   while (!in->empty()) {
     switch (m_state) {
       case kStart: {
@@ -417,7 +294,8 @@
   }
 }
 
-std::optional<std::string> WDImpl::ReadString(std::span<const uint8_t>* in) {
+std::optional<std::string> WireDecoder3::ReadString(
+    std::span<const uint8_t>* in) {
   // string length
   if (!m_stringReader.len) {
     if (auto val = m_ulebReader.ReadOne(in)) {
@@ -442,7 +320,7 @@
   return std::nullopt;
 }
 
-std::optional<std::vector<uint8_t>> WDImpl::ReadRaw(
+std::optional<std::vector<uint8_t>> WireDecoder3::ReadRaw(
     std::span<const uint8_t>* in) {
   // string length
   if (!m_rawReader.len) {
@@ -468,7 +346,7 @@
   return std::nullopt;
 }
 
-std::optional<NT_Type> WDImpl::ReadType(std::span<const uint8_t>* in) {
+std::optional<NT_Type> WireDecoder3::ReadType(std::span<const uint8_t>* in) {
   // Convert from byte value to enum
   switch (Read8(in)) {
     case Message3::kBoolean:
@@ -492,7 +370,7 @@
   }
 }
 
-std::optional<Value> WDImpl::ReadValue(std::span<const uint8_t>* in) {
+std::optional<Value> WireDecoder3::ReadValue(std::span<const uint8_t>* in) {
   while (!in->empty()) {
     switch (m_valueReader.type) {
       case NT_BOOLEAN:
@@ -577,24 +455,3 @@
   }
   return std::nullopt;
 }
-
-struct WireDecoder3::Impl : public WDImpl {
-  explicit Impl(MessageHandler3& out) : WDImpl{out} {}
-};
-
-WireDecoder3::WireDecoder3(MessageHandler3& out) : m_impl{new Impl{out}} {}
-
-WireDecoder3::~WireDecoder3() = default;
-
-bool WireDecoder3::Execute(std::span<const uint8_t>* in) {
-  m_impl->Execute(in);
-  return m_impl->m_state != Impl::kError;
-}
-
-void WireDecoder3::SetError(std::string_view message) {
-  m_impl->EmitError(message);
-}
-
-std::string WireDecoder3::GetError() const {
-  return m_impl->m_error;
-}
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireDecoder3.h b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireDecoder3.h
index e877833..48064f7 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireDecoder3.h
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireDecoder3.h
@@ -7,8 +7,14 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <span>
 #include <string>
+#include <vector>
+
+#include <wpi/leb128.h>
+
+#include "ntcore_c.h"
 
 namespace nt {
 class Value;
@@ -18,6 +24,8 @@
 
 class MessageHandler3 {
  public:
+  virtual ~MessageHandler3() = default;
+
   virtual void KeepAlive() = 0;
   virtual void ServerHelloDone() = 0;
   virtual void ClientHelloDone() = 0;
@@ -42,8 +50,7 @@
 /* Decodes NT3 protocol into native representation. */
 class WireDecoder3 {
  public:
-  explicit WireDecoder3(MessageHandler3& out);
-  ~WireDecoder3();
+  explicit WireDecoder3(MessageHandler3& out) : m_out{out} {}
 
   /**
    * Executes the decoder.  All input data will be consumed unless an error
@@ -51,14 +58,126 @@
    * @param in input data (updated during parse)
    * @return false if error occurred
    */
-  bool Execute(std::span<const uint8_t>* in);
+  bool Execute(std::span<const uint8_t>* in) {
+    DoExecute(in);
+    return m_state != kError;
+  }
 
-  void SetError(std::string_view message);
-  std::string GetError() const;
+  void SetError(std::string_view message) { EmitError(message); }
+  std::string GetError() const { return m_error; }
 
  private:
-  struct Impl;
-  std::unique_ptr<Impl> m_impl;
+  class SimpleValueReader {
+   public:
+    std::optional<uint16_t> Read16(std::span<const uint8_t>* in);
+    std::optional<uint32_t> Read32(std::span<const uint8_t>* in);
+    std::optional<uint64_t> Read64(std::span<const uint8_t>* in);
+    std::optional<double> ReadDouble(std::span<const uint8_t>* in);
+
+   private:
+    uint64_t m_value = 0;
+    int m_count = 0;
+  };
+
+  struct StringReader {
+    void SetLen(uint64_t len_) {
+      len = len_;
+      buf.clear();
+    }
+
+    std::optional<uint64_t> len;
+    std::string buf;
+  };
+
+  struct RawReader {
+    void SetLen(uint64_t len_) {
+      len = len_;
+      buf.clear();
+    }
+
+    std::optional<uint64_t> len;
+    std::vector<uint8_t> buf;
+  };
+
+  struct ValueReader {
+    ValueReader() = default;
+    explicit ValueReader(NT_Type type_) : type{type_} {}
+
+    void SetSize(uint32_t size_) {
+      haveSize = true;
+      size = size_;
+      ints.clear();
+      doubles.clear();
+      strings.clear();
+    }
+
+    NT_Type type = NT_UNASSIGNED;
+    bool haveSize = false;
+    uint32_t size = 0;
+    std::vector<int> ints;
+    std::vector<double> doubles;
+    std::vector<std::string> strings;
+  };
+
+  MessageHandler3& m_out;
+
+  // primary (message) decode state
+  enum {
+    kStart,
+    kClientHello_1ProtoRev,
+    kClientHello_2Id,
+    kProtoUnsup_1ProtoRev,
+    kServerHello_1Flags,
+    kServerHello_2Id,
+    kEntryAssign_1Name,
+    kEntryAssign_2Type,
+    kEntryAssign_3Id,
+    kEntryAssign_4SeqNum,
+    kEntryAssign_5Flags,
+    kEntryAssign_6Value,
+    kEntryUpdate_1Id,
+    kEntryUpdate_2SeqNum,
+    kEntryUpdate_3Type,
+    kEntryUpdate_4Value,
+    kFlagsUpdate_1Id,
+    kFlagsUpdate_2Flags,
+    kEntryDelete_1Id,
+    kClearEntries_1Magic,
+    kExecuteRpc_1Id,
+    kExecuteRpc_2Uid,
+    kExecuteRpc_3Params,
+    kRpcResponse_1Id,
+    kRpcResponse_2Uid,
+    kRpcResponse_3Result,
+    kError
+  } m_state = kStart;
+
+  // detail decoders
+  wpi::Uleb128Reader m_ulebReader;
+  SimpleValueReader m_simpleReader;
+  StringReader m_stringReader;
+  RawReader m_rawReader;
+  ValueReader m_valueReader;
+
+  std::string m_error;
+
+  std::string m_str;
+  unsigned int m_id{0};  // also used for proto_rev
+  unsigned int m_flags{0};
+  unsigned int m_seq_num_uid{0};
+
+  void DoExecute(std::span<const uint8_t>* in);
+
+  std::nullopt_t EmitError(std::string_view msg) {
+    m_state = kError;
+    m_error = msg;
+    return std::nullopt;
+  }
+
+  std::optional<std::string> ReadString(std::span<const uint8_t>* in);
+  std::optional<std::vector<uint8_t>> ReadRaw(std::span<const uint8_t>* in);
+  std::optional<NT_Type> ReadType(std::span<const uint8_t>* in);
+  std::optional<Value> ReadValue(std::span<const uint8_t>* in);
 };
 
 }  // namespace nt::net3
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireEncoder3.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireEncoder3.cpp
index 6bf3435..d1e4c78 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireEncoder3.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/net3/WireEncoder3.cpp
@@ -32,7 +32,7 @@
 
 static void WriteDouble(wpi::raw_ostream& os, double val) {
   // The highest performance way to do this, albeit non-portable.
-  uint64_t v = wpi::DoubleToBits(val);
+  uint64_t v = wpi::bit_cast<uint64_t>(val);
   os << std::span<const uint8_t>{{static_cast<uint8_t>((v >> 56) & 0xff),
                                   static_cast<uint8_t>((v >> 48) & 0xff),
                                   static_cast<uint8_t>((v >> 40) & 0xff),
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp
index 1f6a760..77b726a 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp
@@ -231,8 +231,14 @@
     if (end_subtable == std::string_view::npos) {
       continue;
     }
-    keys.emplace_back(wpi::substr(relative_key, 0, end_subtable));
+    auto subTable = wpi::substr(relative_key, 0, end_subtable);
+    if (keys.empty() || keys.back() != subTable) {
+      keys.emplace_back(subTable);
+    }
   }
+  // remove duplicates
+  std::sort(keys.begin(), keys.end());
+  keys.erase(std::unique(keys.begin(), keys.end()), keys.end());
   return keys;
 }
 
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/networktables/Topic.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/networktables/Topic.cpp
index e3f6ac0..1188667 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/networktables/Topic.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/networktables/Topic.cpp
@@ -7,9 +7,14 @@
 #include <wpi/json.h>
 
 #include "networktables/GenericEntry.h"
+#include "networktables/NetworkTableInstance.h"
 
 using namespace nt;
 
+NetworkTableInstance Topic::GetInstance() const {
+  return NetworkTableInstance{GetInstanceFromHandle(m_handle)};
+}
+
 wpi::json Topic::GetProperty(std::string_view name) const {
   return ::nt::GetTopicProperty(m_handle, name);
 }
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/ntcore_c.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/ntcore_c.cpp
index 151e0f1..b432664 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/ntcore_c.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/ntcore_c.cpp
@@ -537,6 +537,10 @@
   nt::SetServerTeam(inst, team, port);
 }
 
+void NT_Disconnect(NT_Inst inst) {
+  nt::Disconnect(inst);
+}
+
 void NT_StartDSClient(NT_Inst inst, unsigned int port) {
   nt::StartDSClient(inst, port);
 }
@@ -584,6 +588,27 @@
   nt::SetNow(timestamp);
 }
 
+NT_DataLogger NT_StartEntryDataLog(NT_Inst inst, struct WPI_DataLog* log,
+                                   const char* prefix, const char* logPrefix) {
+  return nt::StartEntryDataLog(inst, *reinterpret_cast<wpi::log::DataLog*>(log),
+                               prefix, logPrefix);
+}
+
+void NT_StopEntryDataLog(NT_DataLogger logger) {
+  nt::StopEntryDataLog(logger);
+}
+
+NT_ConnectionDataLogger NT_StartConnectionDataLog(NT_Inst inst,
+                                                  struct WPI_DataLog* log,
+                                                  const char* name) {
+  return nt::StartConnectionDataLog(
+      inst, *reinterpret_cast<wpi::log::DataLog*>(log), name);
+}
+
+void NT_StopConnectionDataLog(NT_ConnectionDataLogger logger) {
+  nt::StopConnectionDataLog(logger);
+}
+
 NT_Listener NT_AddLogger(NT_Inst inst, unsigned int min_level,
                          unsigned int max_level, void* data,
                          NT_ListenerCallback func) {
@@ -600,6 +625,15 @@
   return nt::AddPolledLogger(poller, min_level, max_level);
 }
 
+NT_Bool NT_HasSchema(NT_Inst inst, const char* name) {
+  return nt::HasSchema(inst, name);
+}
+
+void NT_AddSchema(NT_Inst inst, const char* name, const char* type,
+                  const uint8_t* schema, size_t schemaSize) {
+  nt::AddSchema(inst, name, type, {schema, schemaSize});
+}
+
 void NT_DisposeValue(NT_Value* value) {
   switch (value->type) {
     case NT_UNASSIGNED:
diff --git a/third_party/allwpilib/ntcore/src/main/native/cpp/ntcore_cpp.cpp b/third_party/allwpilib/ntcore/src/main/native/cpp/ntcore_cpp.cpp
index cfb5af2..4b48f05 100644
--- a/third_party/allwpilib/ntcore/src/main/native/cpp/ntcore_cpp.cpp
+++ b/third_party/allwpilib/ntcore/src/main/native/cpp/ntcore_cpp.cpp
@@ -685,6 +685,14 @@
   }
 }
 
+void Disconnect(NT_Inst inst) {
+  if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
+    if (auto client = ii->GetClient()) {
+      client->Disconnect();
+    }
+  }
+}
+
 void StartDSClient(NT_Inst inst, unsigned int port) {
   if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
     if (auto client = ii->GetClient()) {
@@ -774,4 +782,19 @@
   }
 }
 
+bool HasSchema(NT_Inst inst, std::string_view name) {
+  if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
+    return ii->localStorage.HasSchema(name);
+  } else {
+    return false;
+  }
+}
+
+void AddSchema(NT_Inst inst, std::string_view name, std::string_view type,
+               std::span<const uint8_t> schema) {
+  if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
+    ii->localStorage.AddSchema(name, type, schema);
+  }
+}
+
 }  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTable.h b/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTable.h
index d34f54b..e03ea9e 100644
--- a/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTable.h
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTable.h
@@ -14,8 +14,11 @@
 
 #include <wpi/StringMap.h>
 #include <wpi/mutex.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
 #include "networktables/NetworkTableEntry.h"
+#include "networktables/Topic.h"
 #include "ntcore_c.h"
 
 namespace nt {
@@ -29,9 +32,15 @@
 class IntegerArrayTopic;
 class IntegerTopic;
 class NetworkTableInstance;
+template <wpi::ProtobufSerializable T>
+class ProtobufTopic;
 class RawTopic;
 class StringArrayTopic;
 class StringTopic;
+template <wpi::StructSerializable T>
+class StructArrayTopic;
+template <wpi::StructSerializable T>
+class StructTopic;
 class Topic;
 
 /**
@@ -221,6 +230,39 @@
   StringArrayTopic GetStringArrayTopic(std::string_view name) const;
 
   /**
+   * Gets a protobuf serialized value topic.
+   *
+   * @param name topic name
+   * @return Topic
+   */
+  template <wpi::ProtobufSerializable T>
+  ProtobufTopic<T> GetProtobufTopic(std::string_view name) const {
+    return ProtobufTopic<T>{GetTopic(name)};
+  }
+
+  /**
+   * Gets a raw struct serialized value topic.
+   *
+   * @param name topic name
+   * @return Topic
+   */
+  template <wpi::StructSerializable T>
+  StructTopic<T> GetStructTopic(std::string_view name) const {
+    return StructTopic<T>{GetTopic(name)};
+  }
+
+  /**
+   * Gets a raw struct serialized array topic.
+   *
+   * @param name topic name
+   * @return Topic
+   */
+  template <wpi::StructSerializable T>
+  StructArrayTopic<T> GetStructArrayTopic(std::string_view name) const {
+    return StructArrayTopic<T>{GetTopic(name)};
+  }
+
+  /**
    * Returns the table at the specified key. If there is no table at the
    * specified key, it will create a new table
    *
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableEntry.h b/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableEntry.h
index b9d509a..31fe9a6 100644
--- a/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableEntry.h
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableEntry.h
@@ -13,8 +13,6 @@
 #include <string_view>
 #include <vector>
 
-#include <wpi/deprecated.h>
-
 #include "networktables/NetworkTableType.h"
 #include "networktables/NetworkTableValue.h"
 #include "ntcore_c.h"
@@ -101,7 +99,7 @@
    * @return the flags (bitmask)
    * @deprecated Use IsPersistent() or topic properties instead
    */
-  WPI_DEPRECATED("Use IsPersistent() or topic properties instead")
+  [[deprecated("Use IsPersistent() or topic properties instead")]]
   unsigned int GetFlags() const;
 
   /**
@@ -468,7 +466,7 @@
    * @param flags the flags to set (bitmask)
    * @deprecated Use SetPersistent() or topic properties instead
    */
-  WPI_DEPRECATED("Use SetPersistent() or topic properties instead")
+  [[deprecated("Use SetPersistent() or topic properties instead")]]
   void SetFlags(unsigned int flags);
 
   /**
@@ -477,7 +475,7 @@
    * @param flags the flags to clear (bitmask)
    * @deprecated Use SetPersistent() or topic properties instead
    */
-  WPI_DEPRECATED("Use SetPersistent() or topic properties instead")
+  [[deprecated("Use SetPersistent() or topic properties instead")]]
   void ClearFlags(unsigned int flags);
 
   /**
@@ -506,7 +504,7 @@
    * Deletes the entry.
    * @deprecated Use Unpublish() instead.
    */
-  WPI_DEPRECATED("Use Unpublish() instead")
+  [[deprecated("Use Unpublish() instead")]]
   void Delete();
 
   /**
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableInstance.h b/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableInstance.h
index fabc634..06e2cd6 100644
--- a/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableInstance.h
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableInstance.h
@@ -13,6 +13,9 @@
 #include <utility>
 #include <vector>
 
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
+
 #include "networktables/NetworkTable.h"
 #include "networktables/NetworkTableEntry.h"
 #include "ntcore_c.h"
@@ -29,9 +32,15 @@
 class IntegerArrayTopic;
 class IntegerTopic;
 class MultiSubscriber;
+template <wpi::ProtobufSerializable T>
+class ProtobufTopic;
 class RawTopic;
 class StringArrayTopic;
 class StringTopic;
+template <wpi::StructSerializable T>
+class StructArrayTopic;
+template <wpi::StructSerializable T>
+class StructTopic;
 class Subscriber;
 class Topic;
 
@@ -239,6 +248,33 @@
   StringArrayTopic GetStringArrayTopic(std::string_view name) const;
 
   /**
+   * Gets a protobuf serialized value topic.
+   *
+   * @param name topic name
+   * @return Topic
+   */
+  template <wpi::ProtobufSerializable T>
+  ProtobufTopic<T> GetProtobufTopic(std::string_view name) const;
+
+  /**
+   * Gets a raw struct serialized value topic.
+   *
+   * @param name topic name
+   * @return Topic
+   */
+  template <wpi::StructSerializable T>
+  StructTopic<T> GetStructTopic(std::string_view name) const;
+
+  /**
+   * Gets a raw struct serialized array topic.
+   *
+   * @param name topic name
+   * @return Topic
+   */
+  template <wpi::StructSerializable T>
+  StructArrayTopic<T> GetStructArrayTopic(std::string_view name) const;
+
+  /**
    * Get Published Topics.
    *
    * Returns an array of topics.
@@ -587,6 +623,12 @@
   void SetServerTeam(unsigned int team, unsigned int port = 0);
 
   /**
+   * Disconnects the client if it's running and connected. This will
+   * automatically start reconnection attempts to the current server list.
+   */
+  void Disconnect();
+
+  /**
    * Starts requesting server address from Driver Station.
    * This connects to the Driver Station running on localhost to obtain the
    * server IP address.
@@ -713,6 +755,75 @@
   /** @} */
 
   /**
+   * @{
+   * @name Schema Functions
+   */
+
+  /**
+   * Returns whether there is a data schema already registered with the given
+   * name. This does NOT perform a check as to whether the schema has already
+   * been published by another node on the network.
+   *
+   * @param name Name (the string passed as the data type for topics using this
+   *             schema)
+   * @return True if schema already registered
+   */
+  bool HasSchema(std::string_view name) const;
+
+  /**
+   * Registers a data schema.  Data schemas provide information for how a
+   * certain data type string can be decoded.  The type string of a data schema
+   * indicates the type of the schema itself (e.g. "protobuf" for protobuf
+   * schemas, "struct" for struct schemas, etc). In NetworkTables, schemas are
+   * published just like normal topics, with the name being generated from the
+   * provided name: "/.schema/<name>".  Duplicate calls to this function with
+   * the same name are silently ignored.
+   *
+   * @param name Name (the string passed as the data type for topics using this
+   *             schema)
+   * @param type Type of schema (e.g. "protobuf", "struct", etc)
+   * @param schema Schema data
+   */
+  void AddSchema(std::string_view name, std::string_view type,
+                 std::span<const uint8_t> schema);
+
+  /**
+   * Registers a data schema.  Data schemas provide information for how a
+   * certain data type string can be decoded.  The type string of a data schema
+   * indicates the type of the schema itself (e.g. "protobuf" for protobuf
+   * schemas, "struct" for struct schemas, etc). In NetworkTables, schemas are
+   * published just like normal topics, with the name being generated from the
+   * provided name: "/.schema/<name>".  Duplicate calls to this function with
+   * the same name are silently ignored.
+   *
+   * @param name Name (the string passed as the data type for topics using this
+   *             schema)
+   * @param type Type of schema (e.g. "protobuf", "struct", etc)
+   * @param schema Schema data
+   */
+  void AddSchema(std::string_view name, std::string_view type,
+                 std::string_view schema);
+
+  /**
+   * Registers a protobuf schema. Duplicate calls to this function with the same
+   * name are silently ignored.
+   *
+   * @tparam T protobuf serializable type
+   * @param msg protobuf message
+   */
+  template <wpi::ProtobufSerializable T>
+  void AddProtobufSchema(wpi::ProtobufMessage<T>& msg);
+
+  /**
+   * Registers a struct schema. Duplicate calls to this function with the same
+   * name are silently ignored.
+   *
+   * @param T struct serializable type
+   */
+  template <wpi::StructSerializable T>
+  void AddStructSchema();
+
+  /**
    * Equality operator.  Returns true if both instances refer to the same
    * native handle.
    */
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableInstance.inc b/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableInstance.inc
index 9b712eb..fdd517e 100644
--- a/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableInstance.inc
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableInstance.inc
@@ -38,6 +38,24 @@
   return m_handle;
 }
 
+template <wpi::ProtobufSerializable T>
+inline ProtobufTopic<T> NetworkTableInstance::GetProtobufTopic(
+    std::string_view name) const {
+  return ProtobufTopic<T>{GetTopic(name)};
+}
+
+template <wpi::StructSerializable T>
+inline StructTopic<T> NetworkTableInstance::GetStructTopic(
+    std::string_view name) const {
+  return StructTopic<T>{GetTopic(name)};
+}
+
+template <wpi::StructSerializable T>
+inline StructArrayTopic<T> NetworkTableInstance::GetStructArrayTopic(
+    std::string_view name) const {
+  return StructArrayTopic<T>{GetTopic(name)};
+}
+
 inline std::vector<Topic> NetworkTableInstance::GetTopics() {
   auto handles = ::nt::GetTopics(m_handle, "", 0);
   return {handles.begin(), handles.end()};
@@ -163,6 +181,10 @@
   ::nt::SetServerTeam(m_handle, team, port);
 }
 
+inline void NetworkTableInstance::Disconnect() {
+  ::nt::Disconnect(m_handle);
+}
+
 inline void NetworkTableInstance::StartDSClient(unsigned int port) {
   ::nt::StartDSClient(m_handle, port);
 }
@@ -219,4 +241,36 @@
   return ::nt::AddLogger(m_handle, min_level, max_level, std::move(func));
 }
 
+inline bool NetworkTableInstance::HasSchema(std::string_view name) const {
+  return ::nt::HasSchema(m_handle, name);
+}
+
+inline void NetworkTableInstance::AddSchema(std::string_view name,
+                                            std::string_view type,
+                                            std::span<const uint8_t> schema) {
+  ::nt::AddSchema(m_handle, name, type, schema);
+}
+
+inline void NetworkTableInstance::AddSchema(std::string_view name,
+                                            std::string_view type,
+                                            std::string_view schema) {
+  ::nt::AddSchema(m_handle, name, type, schema);
+}
+
+template <wpi::ProtobufSerializable T>
+void NetworkTableInstance::AddProtobufSchema(wpi::ProtobufMessage<T>& msg) {
+  msg.ForEachProtobufDescriptor(
+      [this](auto typeString) { return HasSchema(typeString); },
+      [this](auto typeString, auto schema) {
+        AddSchema(typeString, "proto:FileDescriptorProto", schema);
+      });
+}
+
+template <wpi::StructSerializable T>
+void NetworkTableInstance::AddStructSchema() {
+  wpi::ForEachStructSchema<T>([this](auto typeString, auto schema) {
+    AddSchema(typeString, "structschema", schema);
+  });
+}
+
 }  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableValue.h b/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableValue.h
index 673a833..80cee51 100644
--- a/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableValue.h
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/NetworkTableValue.h
@@ -7,12 +7,12 @@
 #include <stdint.h>
 
 #include <cassert>
+#include <concepts>
 #include <initializer_list>
 #include <memory>
 #include <span>
 #include <string>
 #include <string_view>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -20,6 +20,11 @@
 
 namespace nt {
 
+#if __GNUC__ >= 13
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+
 /**
  * A network table entry value.
  * @ingroup ntcore_cpp_api
@@ -29,8 +34,9 @@
 
  public:
   Value();
-  Value(NT_Type type, int64_t time, const private_init&);
-  Value(NT_Type type, int64_t time, int64_t serverTime, const private_init&);
+  Value(NT_Type type, size_t size, int64_t time, const private_init&);
+  Value(NT_Type type, size_t size, int64_t time, int64_t serverTime,
+        const private_init&);
 
   explicit operator bool() const { return m_val.type != NT_UNASSIGNED; }
 
@@ -63,6 +69,15 @@
   int64_t time() const { return m_val.last_change; }
 
   /**
+   * Get the approximate in-memory size of the value in bytes. This is zero for
+   * values that do not require additional memory beyond the memory of the Value
+   * itself.
+   *
+   * @return The size in bytes.
+   */
+  size_t size() const { return m_size; }
+
+  /**
    * Set the local creation time of the value.
    *
    * @param time The time.
@@ -305,7 +320,7 @@
    * @return The entry value
    */
   static Value MakeBoolean(bool value, int64_t time = 0) {
-    Value val{NT_BOOLEAN, time, private_init{}};
+    Value val{NT_BOOLEAN, 0, time, private_init{}};
     val.m_val.data.v_boolean = value;
     return val;
   }
@@ -319,7 +334,7 @@
    * @return The entry value
    */
   static Value MakeInteger(int64_t value, int64_t time = 0) {
-    Value val{NT_INTEGER, time, private_init{}};
+    Value val{NT_INTEGER, 0, time, private_init{}};
     val.m_val.data.v_int = value;
     return val;
   }
@@ -333,7 +348,7 @@
    * @return The entry value
    */
   static Value MakeFloat(float value, int64_t time = 0) {
-    Value val{NT_FLOAT, time, private_init{}};
+    Value val{NT_FLOAT, 0, time, private_init{}};
     val.m_val.data.v_float = value;
     return val;
   }
@@ -347,7 +362,7 @@
    * @return The entry value
    */
   static Value MakeDouble(double value, int64_t time = 0) {
-    Value val{NT_DOUBLE, time, private_init{}};
+    Value val{NT_DOUBLE, 0, time, private_init{}};
     val.m_val.data.v_double = value;
     return val;
   }
@@ -361,8 +376,8 @@
    * @return The entry value
    */
   static Value MakeString(std::string_view value, int64_t time = 0) {
-    Value val{NT_STRING, time, private_init{}};
     auto data = std::make_shared<std::string>(value);
+    Value val{NT_STRING, data->capacity(), time, private_init{}};
     val.m_val.data.v_string.str = const_cast<char*>(data->c_str());
     val.m_val.data.v_string.len = data->size();
     val.m_storage = std::move(data);
@@ -377,11 +392,10 @@
    *             time)
    * @return The entry value
    */
-  template <typename T,
-            typename std::enable_if<std::is_same<T, std::string>::value>::type>
+  template <std::same_as<std::string> T>
   static Value MakeString(T&& value, int64_t time = 0) {
-    Value val{NT_STRING, time, private_init{}};
-    auto data = std::make_shared<std::string>(std::forward(value));
+    auto data = std::make_shared<std::string>(std::forward<T>(value));
+    Value val{NT_STRING, data->capacity(), time, private_init{}};
     val.m_val.data.v_string.str = const_cast<char*>(data->c_str());
     val.m_val.data.v_string.len = data->size();
     val.m_storage = std::move(data);
@@ -397,9 +411,9 @@
    * @return The entry value
    */
   static Value MakeRaw(std::span<const uint8_t> value, int64_t time = 0) {
-    Value val{NT_RAW, time, private_init{}};
     auto data =
         std::make_shared<std::vector<uint8_t>>(value.begin(), value.end());
+    Value val{NT_RAW, data->capacity(), time, private_init{}};
     val.m_val.data.v_raw.data = const_cast<uint8_t*>(data->data());
     val.m_val.data.v_raw.size = data->size();
     val.m_storage = std::move(data);
@@ -414,11 +428,10 @@
    *             time)
    * @return The entry value
    */
-  template <typename T, typename std::enable_if<
-                            std::is_same<T, std::vector<uint8_t>>::value>::type>
+  template <std::same_as<std::vector<uint8_t>> T>
   static Value MakeRaw(T&& value, int64_t time = 0) {
-    Value val{NT_RAW, time, private_init{}};
-    auto data = std::make_shared<std::vector<uint8_t>>(std::forward(value));
+    auto data = std::make_shared<std::vector<uint8_t>>(std::forward<T>(value));
+    Value val{NT_RAW, data->capacity(), time, private_init{}};
     val.m_val.data.v_raw.data = const_cast<uint8_t*>(data->data());
     val.m_val.data.v_raw.size = data->size();
     val.m_storage = std::move(data);
@@ -631,10 +644,15 @@
   friend bool operator==(const Value& lhs, const Value& rhs);
 
  private:
-  NT_Value m_val;
+  NT_Value m_val = {};
   std::shared_ptr<void> m_storage;
+  size_t m_size = 0;
 };
 
+#if __GNUC__ >= 13
+#pragma GCC diagnostic pop
+#endif
+
 bool operator==(const Value& lhs, const Value& rhs);
 
 /**
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/ProtobufTopic.h b/third_party/allwpilib/ntcore/src/main/native/include/networktables/ProtobufTopic.h
new file mode 100644
index 0000000..4c30bf7
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/ProtobufTopic.h
@@ -0,0 +1,474 @@
+// 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 <stdint.h>
+
+#include <atomic>
+#include <concepts>
+#include <span>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <wpi/SmallVector.h>
+#include <wpi/mutex.h>
+#include <wpi/protobuf/Protobuf.h>
+
+#include "networktables/NetworkTableInstance.h"
+#include "networktables/Topic.h"
+#include "ntcore_cpp.h"
+
+namespace wpi {
+class json;
+}  // namespace wpi
+
+namespace nt {
+
+template <wpi::ProtobufSerializable T>
+class ProtobufTopic;
+
+/**
+ * NetworkTables protobuf-encoded value subscriber.
+ */
+template <wpi::ProtobufSerializable T>
+class ProtobufSubscriber : public Subscriber {
+ public:
+  using TopicType = ProtobufTopic<T>;
+  using ValueType = T;
+  using ParamType = const T&;
+  using TimestampedValueType = Timestamped<T>;
+
+  ProtobufSubscriber() = default;
+
+  /**
+   * Construct from a subscriber handle; recommended to use
+   * ProtobufTopic::Subscribe() instead.
+   *
+   * @param handle Native handle
+   * @param msg Protobuf message
+   * @param defaultValue Default value
+   */
+  ProtobufSubscriber(NT_Subscriber handle, wpi::ProtobufMessage<T> msg,
+                     T defaultValue)
+      : Subscriber{handle},
+        m_msg{std::move(msg)},
+        m_defaultValue{std::move(defaultValue)} {}
+
+  ProtobufSubscriber(const ProtobufSubscriber&) = delete;
+  ProtobufSubscriber& operator=(const ProtobufSubscriber&) = delete;
+
+  ProtobufSubscriber(ProtobufSubscriber&& rhs)
+      : Subscriber{std::move(rhs)},
+        m_msg{std::move(rhs.m_msg)},
+        m_defaultValue{std::move(rhs.defaultValue)} {}
+
+  ProtobufSubscriber& operator=(ProtobufSubscriber&& rhs) {
+    Subscriber::operator=(std::move(rhs));
+    m_msg = std::move(rhs.m_msg);
+    m_defaultValue = std::move(rhs.defaultValue);
+    return *this;
+  }
+
+  /**
+   * Get the last published value.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * stored default value.
+   *
+   * @return value
+   */
+  ValueType Get() const { return Get(m_defaultValue); }
+
+  /**
+   * Get the last published value.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * passed defaultValue.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return value
+   */
+  ValueType Get(const T& defaultValue) const {
+    return GetAtomic(defaultValue).value;
+  }
+
+  /**
+   * Get the last published value, replacing the contents in place of an
+   * existing object. If no value has been published or the value cannot be
+   * unpacked, does not replace the contents and returns false.
+   *
+   * @param[out] out object to replace contents of
+   * @return true if successful
+   */
+  bool GetInto(T* out) {
+    wpi::SmallVector<uint8_t, 128> buf;
+    TimestampedRawView view = ::nt::GetAtomicRaw(m_subHandle, buf, {});
+    if (view.value.empty()) {
+      return false;
+    } else {
+      std::scoped_lock lock{m_mutex};
+      return m_msg.UnpackInto(out, view.value);
+    }
+  }
+
+  /**
+   * Get the last published value along with its timestamp
+   * If no value has been published or the value cannot be unpacked, returns the
+   * stored default value and a timestamp of 0.
+   *
+   * @return timestamped value
+   */
+  TimestampedValueType GetAtomic() const { return GetAtomic(m_defaultValue); }
+
+  /**
+   * Get the last published value along with its timestamp.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * passed defaultValue and a timestamp of 0.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return timestamped value
+   */
+  TimestampedValueType GetAtomic(const T& defaultValue) const {
+    wpi::SmallVector<uint8_t, 128> buf;
+    TimestampedRawView view = ::nt::GetAtomicRaw(m_subHandle, buf, {});
+    if (!view.value.empty()) {
+      std::scoped_lock lock{m_mutex};
+      if (auto optval = m_msg.Unpack(view.value)) {
+        return {view.time, view.serverTime, *optval};
+      }
+    }
+    return {0, 0, defaultValue};
+  }
+
+  /**
+   * Get an array of all valid value changes since the last call to ReadQueue.
+   * Also provides a timestamp for each value. Values that cannot be unpacked
+   * are dropped.
+   *
+   * @note The "poll storage" subscribe option can be used to set the queue
+   *     depth.
+   *
+   * @return Array of timestamped values; empty array if no valid new changes
+   *     have been published since the previous call.
+   */
+  std::vector<TimestampedValueType> ReadQueue() {
+    auto raw = ::nt::ReadQueueRaw(m_subHandle);
+    std::vector<TimestampedValueType> rv;
+    rv.reserve(raw.size());
+    std::scoped_lock lock{m_mutex};
+    for (auto&& r : raw) {
+      if (auto optval = m_msg.Unpack(r.value)) {
+        rv.emplace_back(r.time, r.serverTime, *optval);
+      }
+    }
+    return rv;
+  }
+
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  TopicType GetTopic() const {
+    return ProtobufTopic<T>{::nt::GetTopicFromHandle(m_subHandle)};
+  }
+
+ private:
+  wpi::mutex m_mutex;
+  wpi::ProtobufMessage<T> m_msg;
+  ValueType m_defaultValue;
+};
+
+/**
+ * NetworkTables protobuf-encoded value publisher.
+ */
+template <wpi::ProtobufSerializable T>
+class ProtobufPublisher : public Publisher {
+ public:
+  using TopicType = ProtobufTopic<T>;
+  using ValueType = T;
+  using ParamType = const T&;
+
+  using TimestampedValueType = Timestamped<T>;
+
+  ProtobufPublisher() = default;
+
+  /**
+   * Construct from a publisher handle; recommended to use
+   * ProtobufTopic::Publish() instead.
+   *
+   * @param handle Native handle
+   * @param msg Protobuf message
+   */
+  explicit ProtobufPublisher(NT_Publisher handle, wpi::ProtobufMessage<T> msg)
+      : Publisher{handle}, m_msg{std::move(msg)} {}
+
+  ProtobufPublisher(const ProtobufPublisher&) = delete;
+  ProtobufPublisher& operator=(const ProtobufPublisher&) = delete;
+
+  ProtobufPublisher(ProtobufPublisher&& rhs)
+      : Publisher{std::move(rhs)},
+        m_msg{std::move(rhs.m_msg)},
+        m_schemaPublished{rhs.m_schemaPublished} {}
+
+  ProtobufPublisher& operator=(ProtobufPublisher&& rhs) {
+    Publisher::operator=(std::move(rhs));
+    m_msg = std::move(rhs.m_msg);
+    m_schemaPublished.clear();
+    if (rhs.m_schemaPublished.test()) {
+      m_schemaPublished.test_and_set();
+    }
+    return *this;
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  void Set(const T& value, int64_t time = 0) {
+    wpi::SmallVector<uint8_t, 128> buf;
+    {
+      std::scoped_lock lock{m_mutex};
+      if (!m_schemaPublished.test_and_set()) {
+        GetTopic().GetInstance().template AddProtobufSchema<T>(m_msg);
+      }
+      m_msg.Pack(buf, value);
+    }
+    ::nt::SetRaw(m_pubHandle, buf, time);
+  }
+
+  /**
+   * Publish a default value.
+   * On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value
+   */
+  void SetDefault(const T& value) {
+    wpi::SmallVector<uint8_t, 128> buf;
+    {
+      std::scoped_lock lock{m_mutex};
+      if (!m_schemaPublished.test_and_set()) {
+        GetTopic().GetInstance().template AddProtobufSchema<T>(m_msg);
+      }
+      m_msg.Pack(buf, value);
+    }
+    ::nt::SetDefaultRaw(m_pubHandle, buf);
+  }
+
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  TopicType GetTopic() const {
+    return ProtobufTopic<T>{::nt::GetTopicFromHandle(m_pubHandle)};
+  }
+
+ private:
+  wpi::mutex m_mutex;
+  wpi::ProtobufMessage<T> m_msg;
+  std::atomic_flag m_schemaPublished = ATOMIC_FLAG_INIT;
+};
+
+/**
+ * NetworkTables protobuf-encoded value entry.
+ *
+ * @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
+ */
+template <wpi::ProtobufSerializable T>
+class ProtobufEntry final : public ProtobufSubscriber<T>,
+                            public ProtobufPublisher<T> {
+ public:
+  using SubscriberType = ProtobufSubscriber<T>;
+  using PublisherType = ProtobufPublisher<T>;
+  using TopicType = ProtobufTopic<T>;
+  using ValueType = T;
+  using ParamType = const T&;
+
+  using TimestampedValueType = Timestamped<T>;
+
+  ProtobufEntry() = default;
+
+  /**
+   * Construct from an entry handle; recommended to use
+   * ProtobufTopic::GetEntry() instead.
+   *
+   * @param handle Native handle
+   * @param msg Protobuf message
+   * @param defaultValue Default value
+   */
+  ProtobufEntry(NT_Entry handle, wpi::ProtobufMessage<T> msg, T defaultValue)
+      : ProtobufSubscriber<T>{handle, std::move(msg), std::move(defaultValue)},
+        ProtobufPublisher<T>{handle, {}} {}
+
+  /**
+   * Determines if the native handle is valid.
+   *
+   * @return True if the native handle is valid, false otherwise.
+   */
+  explicit operator bool() const { return this->m_subHandle != 0; }
+
+  /**
+   * Gets the native handle for the entry.
+   *
+   * @return Native handle
+   */
+  NT_Entry GetHandle() const { return this->m_subHandle; }
+
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  TopicType GetTopic() const {
+    return ProtobufTopic<T>{::nt::GetTopicFromHandle(this->m_subHandle)};
+  }
+
+  /**
+   * Stops publishing the entry if it's published.
+   */
+  void Unpublish() { ::nt::Unpublish(this->m_pubHandle); }
+};
+
+/**
+ * NetworkTables protobuf-encoded value topic.
+ */
+template <wpi::ProtobufSerializable T>
+class ProtobufTopic final : public Topic {
+ public:
+  using SubscriberType = ProtobufSubscriber<T>;
+  using PublisherType = ProtobufPublisher<T>;
+  using EntryType = ProtobufEntry<T>;
+  using ValueType = T;
+  using ParamType = const T&;
+  using TimestampedValueType = Timestamped<T>;
+
+  ProtobufTopic() = default;
+
+  /**
+   * Construct from a topic handle; recommended to use
+   * NetworkTableInstance::GetProtobufTopic() instead.
+   *
+   * @param handle Native handle
+   */
+  explicit ProtobufTopic(NT_Topic handle) : Topic{handle} {}
+
+  /**
+   * Construct from a generic topic.
+   *
+   * @param topic Topic
+   */
+  explicit ProtobufTopic(Topic topic) : Topic{topic} {}
+
+  /**
+   * Create a new subscriber to the topic.
+   *
+   * <p>The subscriber is only active as long as the returned object
+   * is not destroyed.
+   *
+   * @note Subscribers that do not match the published data type do not return
+   *     any values. To determine if the data type matches, use the appropriate
+   *     Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a
+   *        getter function
+   * @param options subscribe options
+   * @return subscriber
+   */
+  [[nodiscard]]
+  SubscriberType Subscribe(
+      T defaultValue, const PubSubOptions& options = kDefaultPubSubOptions) {
+    wpi::ProtobufMessage<T> msg;
+    auto typeString = msg.GetTypeString();
+    return ProtobufSubscriber<T>{
+        ::nt::Subscribe(m_handle, NT_RAW, typeString, options), std::move(msg),
+        std::move(defaultValue)};
+  }
+
+  /**
+   * Create a new publisher to the topic.
+   *
+   * The publisher is only active as long as the returned object
+   * is not destroyed.
+   *
+   * @note It is not possible to publish two different data types to the same
+   *     topic. Conflicts between publishers are typically resolved by the
+   *     server on a first-come, first-served basis. Any published values that
+   *     do not match the topic's data type are dropped (ignored). To determine
+   *     if the data type matches, use the appropriate Topic functions.
+   *
+   * @param options publish options
+   * @return publisher
+   */
+  [[nodiscard]]
+  PublisherType Publish(const PubSubOptions& options = kDefaultPubSubOptions) {
+    wpi::ProtobufMessage<T> msg;
+    auto typeString = msg.GetTypeString();
+    return ProtobufPublisher<T>{
+        ::nt::Publish(m_handle, NT_RAW, typeString, options), std::move(msg)};
+  }
+
+  /**
+   * Create a new publisher to the topic, with type string and initial
+   * properties.
+   *
+   * The publisher is only active as long as the returned object
+   * is not destroyed.
+   *
+   * @note It is not possible to publish two different data types to the same
+   *     topic. Conflicts between publishers are typically resolved by the
+   *     server on a first-come, first-served basis. Any published values that
+   *     do not match the topic's data type are dropped (ignored). To determine
+   *     if the data type matches, use the appropriate Topic functions.
+   *
+   * @param properties JSON properties
+   * @param options publish options
+   * @return publisher
+   */
+  [[nodiscard]]
+  PublisherType PublishEx(
+      const wpi::json& properties,
+      const PubSubOptions& options = kDefaultPubSubOptions) {
+    wpi::ProtobufMessage<T> msg;
+    auto typeString = msg.GetTypeString();
+    return ProtobufPublisher<T>{
+        ::nt::PublishEx(m_handle, NT_RAW, typeString, properties, options),
+        std::move(msg)};
+  }
+
+  /**
+   * Create a new entry for the topic.
+   *
+   * Entries act as a combination of a subscriber and a weak publisher. The
+   * subscriber is active as long as the entry is not destroyed. The publisher
+   * is created when the entry is first written to, and remains active until
+   * either Unpublish() is called or the entry is destroyed.
+   *
+   * @note It is not possible to use two different data types with the same
+   *     topic. Conflicts between publishers are typically resolved by the
+   *     server on a first-come, first-served basis. Any published values that
+   *     do not match the topic's data type are dropped (ignored), and the entry
+   *     will show no new values if the data type does not match. To determine
+   *     if the data type matches, use the appropriate Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a
+   *        getter function
+   * @param options publish and/or subscribe options
+   * @return entry
+   */
+  [[nodiscard]]
+  EntryType GetEntry(T defaultValue,
+                     const PubSubOptions& options = kDefaultPubSubOptions) {
+    wpi::ProtobufMessage<T> msg;
+    auto typeString = msg.GetTypeString();
+    return ProtobufEntry<T>{
+        ::nt::GetEntry(m_handle, NT_RAW, typeString, options), std::move(msg),
+        std::move(defaultValue)};
+  }
+};
+
+}  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/StructArrayTopic.h b/third_party/allwpilib/ntcore/src/main/native/include/networktables/StructArrayTopic.h
new file mode 100644
index 0000000..91f4721
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/StructArrayTopic.h
@@ -0,0 +1,593 @@
+// 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 <stdint.h>
+
+#include <atomic>
+#include <ranges>
+#include <span>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <wpi/SmallVector.h>
+#include <wpi/mutex.h>
+#include <wpi/struct/Struct.h>
+
+#include "networktables/NetworkTableInstance.h"
+#include "networktables/Topic.h"
+#include "ntcore_cpp.h"
+
+namespace wpi {
+class json;
+}  // namespace wpi
+
+namespace nt {
+
+template <wpi::StructSerializable T>
+class StructArrayTopic;
+
+/**
+ * NetworkTables struct-encoded value array subscriber.
+ */
+template <wpi::StructSerializable T>
+class StructArraySubscriber : public Subscriber {
+  using S = wpi::Struct<T>;
+
+ public:
+  using TopicType = StructArrayTopic<T>;
+  using ValueType = std::vector<T>;
+  using ParamType = std::span<const T>;
+  using TimestampedValueType = Timestamped<ValueType>;
+
+  StructArraySubscriber() = default;
+
+  /**
+   * Construct from a subscriber handle; recommended to use
+   * StructTopic::Subscribe() instead.
+   *
+   * @param handle Native handle
+   * @param defaultValue Default value
+   */
+  template <typename U>
+#if __cpp_lib_ranges >= 201911L
+    requires std::ranges::range<U> &&
+                 std::convertible_to<std::ranges::range_value_t<U>, T>
+#endif
+  StructArraySubscriber(NT_Subscriber handle, U&& defaultValue)
+      : Subscriber{handle},
+        m_defaultValue{defaultValue.begin(), defaultValue.end()} {
+  }
+
+  /**
+   * Get the last published value.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * stored default value.
+   *
+   * @return value
+   */
+  ValueType Get() const { return Get(m_defaultValue); }
+
+  /**
+   * Get the last published value.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * passed defaultValue.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return value
+   */
+  template <typename U>
+#if __cpp_lib_ranges >= 201911L
+    requires std::ranges::range<U> &&
+             std::convertible_to<std::ranges::range_value_t<U>, T>
+#endif
+  ValueType Get(U&& defaultValue) const {
+    return GetAtomic(std::forward<U>(defaultValue)).value;
+  }
+
+  /**
+   * Get the last published value.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * passed defaultValue.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return value
+   */
+  ValueType Get(std::span<const T> defaultValue) const {
+    return GetAtomic(defaultValue).value;
+  }
+
+  /**
+   * Get the last published value along with its timestamp
+   * If no value has been published or the value cannot be unpacked, returns the
+   * stored default value and a timestamp of 0.
+   *
+   * @return timestamped value
+   */
+  TimestampedValueType GetAtomic() const { return GetAtomic(m_defaultValue); }
+
+  /**
+   * Get the last published value along with its timestamp.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * passed defaultValue and a timestamp of 0.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return timestamped value
+   */
+  template <typename U>
+#if __cpp_lib_ranges >= 201911L
+    requires std::ranges::range<U> &&
+             std::convertible_to<std::ranges::range_value_t<U>, T>
+#endif
+  TimestampedValueType GetAtomic(U&& defaultValue) const {
+    wpi::SmallVector<uint8_t, 128> buf;
+    TimestampedRawView view = ::nt::GetAtomicRaw(m_subHandle, buf, {});
+    if (view.value.size() == 0 || (view.value.size() % S::kSize) != 0) {
+      return {0, 0, std::forward<U>(defaultValue)};
+    }
+    TimestampedValueType rv{view.time, view.serverTime, {}};
+    rv.value.reserve(view.value.size() / S::kSize);
+    for (auto in = view.value.begin(), end = view.value.end(); in != end;
+         in += S::kSize) {
+      rv.value.emplace_back(
+          S::Unpack(std::span<const uint8_t, S::kSize>{in, in + S::kSize}));
+    }
+    return rv;
+  }
+
+  /**
+   * Get the last published value along with its timestamp.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * passed defaultValue and a timestamp of 0.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return timestamped value
+   */
+  TimestampedValueType GetAtomic(std::span<const T> defaultValue) const {
+    wpi::SmallVector<uint8_t, 128> buf;
+    TimestampedRawView view = ::nt::GetAtomicRaw(m_subHandle, buf, {});
+    if (view.value.size() == 0 || (view.value.size() % S::kSize) != 0) {
+      return {0, 0, {defaultValue.begin(), defaultValue.end()}};
+    }
+    TimestampedValueType rv{view.time, view.serverTime, {}};
+    rv.value.reserve(view.value.size() / S::kSize);
+    for (auto in = view.value.begin(), end = view.value.end(); in != end;
+         in += S::kSize) {
+      rv.value.emplace_back(
+          S::Unpack(std::span<const uint8_t, S::kSize>{in, in + S::kSize}));
+    }
+    return rv;
+  }
+
+  /**
+   * Get an array of all valid value changes since the last call to ReadQueue.
+   * Also provides a timestamp for each value. Values that cannot be unpacked
+   * are dropped.
+   *
+   * @note The "poll storage" subscribe option can be used to set the queue
+   *     depth.
+   *
+   * @return Array of timestamped values; empty array if no valid new changes
+   *     have been published since the previous call.
+   */
+  std::vector<TimestampedValueType> ReadQueue() {
+    auto raw = ::nt::ReadQueueRaw(m_subHandle);
+    std::vector<TimestampedValueType> rv;
+    rv.reserve(raw.size());
+    for (auto&& r : raw) {
+      if (r.value.size() == 0 || (r.value.size() % S::kSize) != 0) {
+        continue;
+      }
+      std::vector<T> values;
+      values.reserve(r.value.size() / S::kSize);
+      for (auto in = r.value.begin(), end = r.value.end(); in != end;
+           in += S::kSize) {
+        values.emplace_back(
+            S::Unpack(std::span<const uint8_t, S::kSize>{in, in + S::kSize}));
+      }
+      rv.emplace_back(r.time, r.serverTime, std::move(values));
+    }
+    return rv;
+  }
+
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  TopicType GetTopic() const {
+    return StructArrayTopic<T>{::nt::GetTopicFromHandle(m_subHandle)};
+  }
+
+ private:
+  ValueType m_defaultValue;
+};
+
+/**
+ * NetworkTables struct-encoded value array publisher.
+ */
+template <wpi::StructSerializable T>
+class StructArrayPublisher : public Publisher {
+  using S = wpi::Struct<T>;
+
+ public:
+  using TopicType = StructArrayTopic<T>;
+  using ValueType = std::vector<T>;
+  using ParamType = std::span<const T>;
+
+  using TimestampedValueType = Timestamped<ValueType>;
+
+  StructArrayPublisher() = default;
+
+  /**
+   * Construct from a publisher handle; recommended to use
+   * StructTopic::Publish() instead.
+   *
+   * @param handle Native handle
+   */
+  explicit StructArrayPublisher(NT_Publisher handle) : Publisher{handle} {}
+
+  StructArrayPublisher(const StructArrayPublisher&) = delete;
+  StructArrayPublisher& operator=(const StructArrayPublisher&) = delete;
+
+  StructArrayPublisher(StructArrayPublisher&& rhs)
+      : Publisher{std::move(rhs)},
+        m_buf{std::move(rhs.m_buf)},
+        m_schemaPublished{rhs.m_schemaPublished} {}
+
+  StructArrayPublisher& operator=(StructArrayPublisher&& rhs) {
+    Publisher::operator=(std::move(rhs));
+    m_buf = std::move(rhs.m_buf);
+    m_schemaPublished.clear();
+    if (rhs.m_schemaPublished.test()) {
+      m_schemaPublished.test_and_set();
+    }
+    return *this;
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  template <typename U>
+#if __cpp_lib_ranges >= 201911L
+    requires std::ranges::range<U> &&
+             std::convertible_to<std::ranges::range_value_t<U>, T>
+#endif
+  void Set(U&& value, int64_t time = 0) {
+    if (!m_schemaPublished.test_and_set()) {
+      GetTopic().GetInstance().template AddStructSchema<T>();
+    }
+    m_buf.Write(std::forward<U>(value),
+                [&](auto bytes) { ::nt::SetRaw(m_pubHandle, bytes, time); });
+  }
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  void Set(std::span<const T> value, int64_t time = 0) {
+    m_buf.Write(value,
+                [&](auto bytes) { ::nt::SetRaw(m_pubHandle, bytes, time); });
+  }
+
+  /**
+   * Publish a default value.
+   * On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value
+   */
+  template <typename U>
+#if __cpp_lib_ranges >= 201911L
+    requires std::ranges::range<U> &&
+             std::convertible_to<std::ranges::range_value_t<U>, T>
+#endif
+  void SetDefault(U&& value) {
+    if (!m_schemaPublished.test_and_set()) {
+      GetTopic().GetInstance().template AddStructSchema<T>();
+    }
+    m_buf.Write(std::forward<U>(value),
+                [&](auto bytes) { ::nt::SetDefaultRaw(m_pubHandle, bytes); });
+  }
+
+  /**
+   * Publish a default value.
+   * On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value
+   */
+  void SetDefault(std::span<const T> value) {
+    m_buf.Write(value,
+                [&](auto bytes) { ::nt::SetDefaultRaw(m_pubHandle, bytes); });
+  }
+
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  TopicType GetTopic() const {
+    return StructArrayTopic<T>{::nt::GetTopicFromHandle(m_pubHandle)};
+  }
+
+ private:
+  wpi::StructArrayBuffer<T> m_buf;
+  std::atomic_flag m_schemaPublished = ATOMIC_FLAG_INIT;
+};
+
+/**
+ * NetworkTables struct-encoded value array entry.
+ *
+ * @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
+ */
+template <wpi::StructSerializable T>
+class StructArrayEntry final : public StructArraySubscriber<T>,
+                               public StructArrayPublisher<T> {
+ public:
+  using SubscriberType = StructArraySubscriber<T>;
+  using PublisherType = StructArrayPublisher<T>;
+  using TopicType = StructArrayTopic<T>;
+  using ValueType = std::vector<T>;
+  using ParamType = std::span<const T>;
+
+  using TimestampedValueType = Timestamped<ValueType>;
+
+  StructArrayEntry() = default;
+
+  /**
+   * Construct from an entry handle; recommended to use
+   * StructTopic::GetEntry() instead.
+   *
+   * @param handle Native handle
+   * @param defaultValue Default value
+   */
+  template <typename U>
+#if __cpp_lib_ranges >= 201911L
+    requires std::ranges::range<U> &&
+                 std::convertible_to<std::ranges::range_value_t<U>, T>
+#endif
+  StructArrayEntry(NT_Entry handle, U&& defaultValue)
+      : StructArraySubscriber<T>{handle, defaultValue},
+        StructArrayPublisher<T>{handle} {
+  }
+
+  /**
+   * Determines if the native handle is valid.
+   *
+   * @return True if the native handle is valid, false otherwise.
+   */
+  explicit operator bool() const { return this->m_subHandle != 0; }
+
+  /**
+   * Gets the native handle for the entry.
+   *
+   * @return Native handle
+   */
+  NT_Entry GetHandle() const { return this->m_subHandle; }
+
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  TopicType GetTopic() const {
+    return StructArrayTopic<T>{::nt::GetTopicFromHandle(this->m_subHandle)};
+  }
+
+  /**
+   * Stops publishing the entry if it's published.
+   */
+  void Unpublish() { ::nt::Unpublish(this->m_pubHandle); }
+};
+
+/**
+ * NetworkTables struct-encoded value array topic.
+ */
+template <wpi::StructSerializable T>
+class StructArrayTopic final : public Topic {
+ public:
+  using SubscriberType = StructArraySubscriber<T>;
+  using PublisherType = StructArrayPublisher<T>;
+  using EntryType = StructArrayEntry<T>;
+  using ValueType = std::vector<T>;
+  using ParamType = std::span<const T>;
+  using TimestampedValueType = Timestamped<ValueType>;
+
+  StructArrayTopic() = default;
+
+  /**
+   * Construct from a topic handle; recommended to use
+   * NetworkTableInstance::GetStructTopic() instead.
+   *
+   * @param handle Native handle
+   */
+  explicit StructArrayTopic(NT_Topic handle) : Topic{handle} {}
+
+  /**
+   * Construct from a generic topic.
+   *
+   * @param topic Topic
+   */
+  explicit StructArrayTopic(Topic topic) : Topic{topic} {}
+
+  /**
+   * Create a new subscriber to the topic.
+   *
+   * <p>The subscriber is only active as long as the returned object
+   * is not destroyed.
+   *
+   * @note Subscribers that do not match the published data type do not return
+   *     any values. To determine if the data type matches, use the appropriate
+   *     Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a
+   *        getter function
+   * @param options subscribe options
+   * @return subscriber
+   */
+  template <typename U>
+#if __cpp_lib_ranges >= 201911L
+    requires std::ranges::range<U> &&
+             std::convertible_to<std::ranges::range_value_t<U>, T>
+#endif
+  [[nodiscard]]
+  SubscriberType Subscribe(
+      U&& defaultValue, const PubSubOptions& options = kDefaultPubSubOptions) {
+    return StructArraySubscriber<T>{
+        ::nt::Subscribe(
+            m_handle, NT_RAW,
+            wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(), options),
+        defaultValue};
+  }
+
+  /**
+   * Create a new subscriber to the topic.
+   *
+   * <p>The subscriber is only active as long as the returned object
+   * is not destroyed.
+   *
+   * @note Subscribers that do not match the published data type do not return
+   *     any values. To determine if the data type matches, use the appropriate
+   *     Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a
+   *        getter function
+   * @param options subscribe options
+   * @return subscriber
+   */
+  [[nodiscard]]
+  SubscriberType Subscribe(
+      std::span<const T> defaultValue,
+      const PubSubOptions& options = kDefaultPubSubOptions) {
+    return StructArraySubscriber<T>{
+        ::nt::Subscribe(
+            m_handle, NT_RAW,
+            wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(), options),
+        defaultValue};
+  }
+
+  /**
+   * Create a new publisher to the topic.
+   *
+   * The publisher is only active as long as the returned object
+   * is not destroyed.
+   *
+   * @note It is not possible to publish two different data types to the same
+   *     topic. Conflicts between publishers are typically resolved by the
+   *     server on a first-come, first-served basis. Any published values that
+   *     do not match the topic's data type are dropped (ignored). To determine
+   *     if the data type matches, use the appropriate Topic functions.
+   *
+   * @param options publish options
+   * @return publisher
+   */
+  [[nodiscard]]
+  PublisherType Publish(const PubSubOptions& options = kDefaultPubSubOptions) {
+    return StructArrayPublisher<T>{::nt::Publish(
+        m_handle, NT_RAW,
+        wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(), options)};
+  }
+
+  /**
+   * Create a new publisher to the topic, with type string and initial
+   * properties.
+   *
+   * The publisher is only active as long as the returned object
+   * is not destroyed.
+   *
+   * @note It is not possible to publish two different data types to the same
+   *     topic. Conflicts between publishers are typically resolved by the
+   *     server on a first-come, first-served basis. Any published values that
+   *     do not match the topic's data type are dropped (ignored). To determine
+   *     if the data type matches, use the appropriate Topic functions.
+   *
+   * @param properties JSON properties
+   * @param options publish options
+   * @return publisher
+   */
+  [[nodiscard]]
+  PublisherType PublishEx(
+      const wpi::json& properties,
+      const PubSubOptions& options = kDefaultPubSubOptions) {
+    return StructArrayPublisher<T>{::nt::PublishEx(
+        m_handle, NT_RAW,
+        wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(), properties,
+        options)};
+  }
+
+  /**
+   * Create a new entry for the topic.
+   *
+   * Entries act as a combination of a subscriber and a weak publisher. The
+   * subscriber is active as long as the entry is not destroyed. The publisher
+   * is created when the entry is first written to, and remains active until
+   * either Unpublish() is called or the entry is destroyed.
+   *
+   * @note It is not possible to use two different data types with the same
+   *     topic. Conflicts between publishers are typically resolved by the
+   *     server on a first-come, first-served basis. Any published values that
+   *     do not match the topic's data type are dropped (ignored), and the entry
+   *     will show no new values if the data type does not match. To determine
+   *     if the data type matches, use the appropriate Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a
+   *        getter function
+   * @param options publish and/or subscribe options
+   * @return entry
+   */
+  template <typename U>
+#if __cpp_lib_ranges >= 201911L
+    requires std::ranges::range<U> &&
+             std::convertible_to<std::ranges::range_value_t<U>, T>
+#endif
+  [[nodiscard]]
+  EntryType GetEntry(U&& defaultValue,
+                     const PubSubOptions& options = kDefaultPubSubOptions) {
+    return StructArrayEntry<T>{
+        ::nt::GetEntry(m_handle, NT_RAW,
+                       wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(),
+                       options),
+        defaultValue};
+  }
+
+  /**
+   * Create a new entry for the topic.
+   *
+   * Entries act as a combination of a subscriber and a weak publisher. The
+   * subscriber is active as long as the entry is not destroyed. The publisher
+   * is created when the entry is first written to, and remains active until
+   * either Unpublish() is called or the entry is destroyed.
+   *
+   * @note It is not possible to use two different data types with the same
+   *     topic. Conflicts between publishers are typically resolved by the
+   *     server on a first-come, first-served basis. Any published values that
+   *     do not match the topic's data type are dropped (ignored), and the entry
+   *     will show no new values if the data type does not match. To determine
+   *     if the data type matches, use the appropriate Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a
+   *        getter function
+   * @param options publish and/or subscribe options
+   * @return entry
+   */
+  [[nodiscard]]
+  EntryType GetEntry(std::span<const T> defaultValue,
+                     const PubSubOptions& options = kDefaultPubSubOptions) {
+    return StructArrayEntry<T>{
+        ::nt::GetEntry(m_handle, NT_RAW,
+                       wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(),
+                       options),
+        defaultValue};
+  }
+};
+
+}  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/StructTopic.h b/third_party/allwpilib/ntcore/src/main/native/include/networktables/StructTopic.h
new file mode 100644
index 0000000..88da9f3
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/StructTopic.h
@@ -0,0 +1,438 @@
+// 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 <stdint.h>
+
+#include <atomic>
+#include <concepts>
+#include <span>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <wpi/SmallVector.h>
+#include <wpi/struct/Struct.h>
+
+#include "networktables/NetworkTableInstance.h"
+#include "networktables/Topic.h"
+#include "ntcore_cpp.h"
+
+namespace wpi {
+class json;
+}  // namespace wpi
+
+namespace nt {
+
+template <wpi::StructSerializable T>
+class StructTopic;
+
+/**
+ * NetworkTables struct-encoded value subscriber.
+ */
+template <wpi::StructSerializable T>
+class StructSubscriber : public Subscriber {
+  using S = wpi::Struct<T>;
+
+ public:
+  using TopicType = StructTopic<T>;
+  using ValueType = T;
+  using ParamType = const T&;
+  using TimestampedValueType = Timestamped<T>;
+
+  StructSubscriber() = default;
+
+  /**
+   * Construct from a subscriber handle; recommended to use
+   * StructTopic::Subscribe() instead.
+   *
+   * @param handle Native handle
+   * @param defaultValue Default value
+   */
+  StructSubscriber(NT_Subscriber handle, T defaultValue)
+      : Subscriber{handle}, m_defaultValue{std::move(defaultValue)} {}
+
+  /**
+   * Get the last published value.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * stored default value.
+   *
+   * @return value
+   */
+  ValueType Get() const { return Get(m_defaultValue); }
+
+  /**
+   * Get the last published value.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * passed defaultValue.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return value
+   */
+  ValueType Get(const T& defaultValue) const {
+    return GetAtomic(defaultValue).value;
+  }
+
+  /**
+   * Get the last published value, replacing the contents in place of an
+   * existing object. If no value has been published or the value cannot be
+   * unpacked, does not replace the contents and returns false.
+   *
+   * @param[out] out object to replace contents of
+   * @return true if successful
+   */
+  bool GetInto(T* out) {
+    wpi::SmallVector<uint8_t, S::kSize> buf;
+    TimestampedRawView view = ::nt::GetAtomicRaw(m_subHandle, buf, {});
+    if (view.value.size() < S::kSize) {
+      return false;
+    } else {
+      wpi::UnpackStructInto(out, view.value.subspan<0, S::kSize>());
+      return true;
+    }
+  }
+
+  /**
+   * Get the last published value along with its timestamp
+   * If no value has been published or the value cannot be unpacked, returns the
+   * stored default value and a timestamp of 0.
+   *
+   * @return timestamped value
+   */
+  TimestampedValueType GetAtomic() const { return GetAtomic(m_defaultValue); }
+
+  /**
+   * Get the last published value along with its timestamp.
+   * If no value has been published or the value cannot be unpacked, returns the
+   * passed defaultValue and a timestamp of 0.
+   *
+   * @param defaultValue default value to return if no value has been published
+   * @return timestamped value
+   */
+  TimestampedValueType GetAtomic(const T& defaultValue) const {
+    wpi::SmallVector<uint8_t, S::kSize> buf;
+    TimestampedRawView view = ::nt::GetAtomicRaw(m_subHandle, buf, {});
+    if (view.value.size() < S::kSize) {
+      return {0, 0, defaultValue};
+    } else {
+      return {view.time, view.serverTime,
+              S::Unpack(view.value.subspan<0, S::kSize>())};
+    }
+  }
+
+  /**
+   * Get an array of all valid value changes since the last call to ReadQueue.
+   * Also provides a timestamp for each value. Values that cannot be unpacked
+   * are dropped.
+   *
+   * @note The "poll storage" subscribe option can be used to set the queue
+   *     depth.
+   *
+   * @return Array of timestamped values; empty array if no valid new changes
+   *     have been published since the previous call.
+   */
+  std::vector<TimestampedValueType> ReadQueue() {
+    auto raw = ::nt::ReadQueueRaw(m_subHandle);
+    std::vector<TimestampedValueType> rv;
+    rv.reserve(raw.size());
+    for (auto&& r : raw) {
+      if (r.value.size() < S::kSize) {
+        continue;
+      } else {
+        rv.emplace_back(
+            r.time, r.serverTime,
+            S::Unpack(
+                std::span<const uint8_t>(r.value).subspan<0, S::kSize>()));
+      }
+    }
+    return rv;
+  }
+
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  TopicType GetTopic() const {
+    return StructTopic<T>{::nt::GetTopicFromHandle(m_subHandle)};
+  }
+
+ private:
+  ValueType m_defaultValue;
+};
+
+/**
+ * NetworkTables struct-encoded value publisher.
+ */
+template <wpi::StructSerializable T>
+class StructPublisher : public Publisher {
+  using S = wpi::Struct<T>;
+
+ public:
+  using TopicType = StructTopic<T>;
+  using ValueType = T;
+  using ParamType = const T&;
+
+  using TimestampedValueType = Timestamped<T>;
+
+  StructPublisher() = default;
+
+  StructPublisher(const StructPublisher&) = delete;
+  StructPublisher& operator=(const StructPublisher&) = delete;
+
+  StructPublisher(StructPublisher&& rhs)
+      : Publisher{std::move(rhs)}, m_schemaPublished{rhs.m_schemaPublished} {}
+
+  StructPublisher& operator=(StructPublisher&& rhs) {
+    Publisher::operator=(std::move(rhs));
+    m_schemaPublished.clear();
+    if (rhs.m_schemaPublished.test()) {
+      m_schemaPublished.test_and_set();
+    }
+    return *this;
+  }
+
+  /**
+   * Construct from a publisher handle; recommended to use
+   * StructTopic::Publish() instead.
+   *
+   * @param handle Native handle
+   */
+  explicit StructPublisher(NT_Publisher handle) : Publisher{handle} {}
+
+  /**
+   * Publish a new value.
+   *
+   * @param value value to publish
+   * @param time timestamp; 0 indicates current NT time should be used
+   */
+  void Set(const T& value, int64_t time = 0) {
+    if (!m_schemaPublished.test_and_set()) {
+      GetTopic().GetInstance().template AddStructSchema<T>();
+    }
+    uint8_t buf[S::kSize];
+    S::Pack(buf, value);
+    ::nt::SetRaw(m_pubHandle, buf, time);
+  }
+
+  /**
+   * Publish a default value.
+   * On reconnect, a default value will never be used in preference to a
+   * published value.
+   *
+   * @param value value
+   */
+  void SetDefault(const T& value) {
+    if (!m_schemaPublished.test_and_set()) {
+      GetTopic().GetInstance().template AddStructSchema<T>();
+    }
+    uint8_t buf[S::kSize];
+    S::Pack(buf, value);
+    ::nt::SetDefaultRaw(m_pubHandle, buf);
+  }
+
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  TopicType GetTopic() const {
+    return StructTopic<T>{::nt::GetTopicFromHandle(m_pubHandle)};
+  }
+
+ private:
+  std::atomic_flag m_schemaPublished = ATOMIC_FLAG_INIT;
+};
+
+/**
+ * NetworkTables struct-encoded value entry.
+ *
+ * @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
+ */
+template <wpi::StructSerializable T>
+class StructEntry final : public StructSubscriber<T>,
+                          public StructPublisher<T> {
+ public:
+  using SubscriberType = StructSubscriber<T>;
+  using PublisherType = StructPublisher<T>;
+  using TopicType = StructTopic<T>;
+  using ValueType = T;
+  using ParamType = const T&;
+
+  using TimestampedValueType = Timestamped<T>;
+
+  StructEntry() = default;
+
+  /**
+   * Construct from an entry handle; recommended to use
+   * StructTopic::GetEntry() instead.
+   *
+   * @param handle Native handle
+   * @param defaultValue Default value
+   */
+  StructEntry(NT_Entry handle, T defaultValue)
+      : StructSubscriber<T>{handle, std::move(defaultValue)},
+        StructPublisher<T>{handle} {}
+
+  /**
+   * Determines if the native handle is valid.
+   *
+   * @return True if the native handle is valid, false otherwise.
+   */
+  explicit operator bool() const { return this->m_subHandle != 0; }
+
+  /**
+   * Gets the native handle for the entry.
+   *
+   * @return Native handle
+   */
+  NT_Entry GetHandle() const { return this->m_subHandle; }
+
+  /**
+   * Get the corresponding topic.
+   *
+   * @return Topic
+   */
+  TopicType GetTopic() const {
+    return StructTopic<T>{::nt::GetTopicFromHandle(this->m_subHandle)};
+  }
+
+  /**
+   * Stops publishing the entry if it's published.
+   */
+  void Unpublish() { ::nt::Unpublish(this->m_pubHandle); }
+};
+
+/**
+ * NetworkTables struct-encoded value topic.
+ */
+template <wpi::StructSerializable T>
+class StructTopic final : public Topic {
+ public:
+  using SubscriberType = StructSubscriber<T>;
+  using PublisherType = StructPublisher<T>;
+  using EntryType = StructEntry<T>;
+  using ValueType = T;
+  using ParamType = const T&;
+  using TimestampedValueType = Timestamped<T>;
+
+  StructTopic() = default;
+
+  /**
+   * Construct from a topic handle; recommended to use
+   * NetworkTableInstance::GetStructTopic() instead.
+   *
+   * @param handle Native handle
+   */
+  explicit StructTopic(NT_Topic handle) : Topic{handle} {}
+
+  /**
+   * Construct from a generic topic.
+   *
+   * @param topic Topic
+   */
+  explicit StructTopic(Topic topic) : Topic{topic} {}
+
+  /**
+   * Create a new subscriber to the topic.
+   *
+   * <p>The subscriber is only active as long as the returned object
+   * is not destroyed.
+   *
+   * @note Subscribers that do not match the published data type do not return
+   *     any values. To determine if the data type matches, use the appropriate
+   *     Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a
+   *        getter function
+   * @param options subscribe options
+   * @return subscriber
+   */
+  [[nodiscard]]
+  SubscriberType Subscribe(
+      T defaultValue, const PubSubOptions& options = kDefaultPubSubOptions) {
+    return StructSubscriber<T>{
+        ::nt::Subscribe(m_handle, NT_RAW, wpi::GetStructTypeString<T>(),
+                        options),
+        std::move(defaultValue)};
+  }
+
+  /**
+   * Create a new publisher to the topic.
+   *
+   * The publisher is only active as long as the returned object
+   * is not destroyed.
+   *
+   * @note It is not possible to publish two different data types to the same
+   *     topic. Conflicts between publishers are typically resolved by the
+   *     server on a first-come, first-served basis. Any published values that
+   *     do not match the topic's data type are dropped (ignored). To determine
+   *     if the data type matches, use the appropriate Topic functions.
+   *
+   * @param options publish options
+   * @return publisher
+   */
+  [[nodiscard]]
+  PublisherType Publish(const PubSubOptions& options = kDefaultPubSubOptions) {
+    return StructPublisher<T>{::nt::Publish(
+        m_handle, NT_RAW, wpi::GetStructTypeString<T>(), options)};
+  }
+
+  /**
+   * Create a new publisher to the topic, with type string and initial
+   * properties.
+   *
+   * The publisher is only active as long as the returned object
+   * is not destroyed.
+   *
+   * @note It is not possible to publish two different data types to the same
+   *     topic. Conflicts between publishers are typically resolved by the
+   *     server on a first-come, first-served basis. Any published values that
+   *     do not match the topic's data type are dropped (ignored). To determine
+   *     if the data type matches, use the appropriate Topic functions.
+   *
+   * @param properties JSON properties
+   * @param options publish options
+   * @return publisher
+   */
+  [[nodiscard]]
+  PublisherType PublishEx(
+      const wpi::json& properties,
+      const PubSubOptions& options = kDefaultPubSubOptions) {
+    return StructPublisher<T>{::nt::PublishEx(
+        m_handle, NT_RAW, wpi::GetStructTypeString<T>(), properties, options)};
+  }
+
+  /**
+   * Create a new entry for the topic.
+   *
+   * Entries act as a combination of a subscriber and a weak publisher. The
+   * subscriber is active as long as the entry is not destroyed. The publisher
+   * is created when the entry is first written to, and remains active until
+   * either Unpublish() is called or the entry is destroyed.
+   *
+   * @note It is not possible to use two different data types with the same
+   *     topic. Conflicts between publishers are typically resolved by the
+   *     server on a first-come, first-served basis. Any published values that
+   *     do not match the topic's data type are dropped (ignored), and the entry
+   *     will show no new values if the data type does not match. To determine
+   *     if the data type matches, use the appropriate Topic functions.
+   *
+   * @param defaultValue default value used when a default is not provided to a
+   *        getter function
+   * @param options publish and/or subscribe options
+   * @return entry
+   */
+  [[nodiscard]]
+  EntryType GetEntry(T defaultValue,
+                     const PubSubOptions& options = kDefaultPubSubOptions) {
+    return StructEntry<T>{
+        ::nt::GetEntry(m_handle, NT_RAW, wpi::GetStructTypeString<T>(),
+                       options),
+        std::move(defaultValue)};
+  }
+};
+
+}  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/Topic.h b/third_party/allwpilib/ntcore/src/main/native/include/networktables/Topic.h
index e2a8a5a..d623fbd 100644
--- a/third_party/allwpilib/ntcore/src/main/native/include/networktables/Topic.h
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/Topic.h
@@ -11,14 +11,12 @@
 #include <utility>
 #include <vector>
 
+#include <wpi/json_fwd.h>
+
 #include "networktables/NetworkTableType.h"
 #include "ntcore_c.h"
 #include "ntcore_cpp.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace nt {
 
 class GenericEntry;
@@ -169,7 +167,8 @@
    * @param options subscribe options
    * @return subscriber
    */
-  [[nodiscard]] GenericSubscriber GenericSubscribe(
+  [[nodiscard]]
+  GenericSubscriber GenericSubscribe(
       const PubSubOptions& options = kDefaultPubSubOptions);
 
   /**
@@ -186,7 +185,8 @@
    * @param options subscribe options
    * @return subscriber
    */
-  [[nodiscard]] GenericSubscriber GenericSubscribe(
+  [[nodiscard]]
+  GenericSubscriber GenericSubscribe(
       std::string_view typeString,
       const PubSubOptions& options = kDefaultPubSubOptions);
 
@@ -206,7 +206,8 @@
    * @param options publish options
    * @return publisher
    */
-  [[nodiscard]] GenericPublisher GenericPublish(
+  [[nodiscard]]
+  GenericPublisher GenericPublish(
       std::string_view typeString,
       const PubSubOptions& options = kDefaultPubSubOptions);
 
@@ -228,7 +229,8 @@
    * @param options publish options
    * @return publisher
    */
-  [[nodiscard]] GenericPublisher GenericPublishEx(
+  [[nodiscard]]
+  GenericPublisher GenericPublishEx(
       std::string_view typeString, const wpi::json& properties,
       const PubSubOptions& options = kDefaultPubSubOptions);
 
@@ -250,7 +252,8 @@
    * @param options publish and/or subscribe options
    * @return entry
    */
-  [[nodiscard]] GenericEntry GetGenericEntry(
+  [[nodiscard]]
+  GenericEntry GetGenericEntry(
       const PubSubOptions& options = kDefaultPubSubOptions);
 
   /**
@@ -272,7 +275,8 @@
    * @param options publish and/or subscribe options
    * @return entry
    */
-  [[nodiscard]] GenericEntry GetGenericEntry(
+  [[nodiscard]]
+  GenericEntry GetGenericEntry(
       std::string_view typeString,
       const PubSubOptions& options = kDefaultPubSubOptions);
 
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/Topic.inc b/third_party/allwpilib/ntcore/src/main/native/include/networktables/Topic.inc
index 642e49e..166dc6d 100644
--- a/third_party/allwpilib/ntcore/src/main/native/include/networktables/Topic.inc
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/Topic.inc
@@ -6,7 +6,6 @@
 
 #include <string>
 
-#include "networktables/NetworkTableInstance.h"
 #include "networktables/NetworkTableType.h"
 #include "networktables/Topic.h"
 #include "ntcore_c.h"
@@ -14,10 +13,6 @@
 
 namespace nt {
 
-inline NetworkTableInstance Topic::GetInstance() const {
-  return NetworkTableInstance{GetInstanceFromHandle(m_handle)};
-}
-
 inline std::string Topic::GetName() const {
   return ::nt::GetTopicName(m_handle);
 }
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/networktables/UnitTopic.h b/third_party/allwpilib/ntcore/src/main/native/include/networktables/UnitTopic.h
index cac9501..eb6ad6d 100644
--- a/third_party/allwpilib/ntcore/src/main/native/include/networktables/UnitTopic.h
+++ b/third_party/allwpilib/ntcore/src/main/native/include/networktables/UnitTopic.h
@@ -10,13 +10,11 @@
 #include <string_view>
 #include <vector>
 
+#include <wpi/json_fwd.h>
+
 #include "networktables/Topic.h"
 #include "ntcore_cpp.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace nt {
 
 template <typename T>
@@ -295,7 +293,8 @@
    * @param options subscribe options
    * @return subscriber
    */
-  [[nodiscard]] SubscriberType Subscribe(
+  [[nodiscard]]
+  SubscriberType Subscribe(
       ParamType defaultValue,
       const PubSubOptions& options = kDefaultPubSubOptions);
 
@@ -315,7 +314,8 @@
    * @param options subscribe options
    * @return subscriber
    */
-  [[nodiscard]] SubscriberType SubscribeEx(
+  [[nodiscard]]
+  SubscriberType SubscribeEx(
       std::string_view typeString, ParamType defaultValue,
       const PubSubOptions& options = kDefaultPubSubOptions);
 
@@ -334,8 +334,8 @@
    * @param options publish options
    * @return publisher
    */
-  [[nodiscard]] PublisherType Publish(
-      const PubSubOptions& options = kDefaultPubSubOptions);
+  [[nodiscard]]
+  PublisherType Publish(const PubSubOptions& options = kDefaultPubSubOptions);
 
   /**
    * Create a new publisher to the topic, with type string and initial
@@ -355,9 +355,10 @@
    * @param options publish options
    * @return publisher
    */
-  [[nodiscard]] PublisherType PublishEx(
-      std::string_view typeString, const wpi::json& properties,
-      const PubSubOptions& options = kDefaultPubSubOptions);
+  [[nodiscard]]
+  PublisherType PublishEx(std::string_view typeString,
+                          const wpi::json& properties,
+                          const PubSubOptions& options = kDefaultPubSubOptions);
 
   /**
    * Create a new entry for the topic.
@@ -379,9 +380,9 @@
    * @param options publish and/or subscribe options
    * @return entry
    */
-  [[nodiscard]] EntryType GetEntry(
-      ParamType defaultValue,
-      const PubSubOptions& options = kDefaultPubSubOptions);
+  [[nodiscard]]
+  EntryType GetEntry(ParamType defaultValue,
+                     const PubSubOptions& options = kDefaultPubSubOptions);
 
   /**
    * Create a new entry for the topic, with specific type string.
@@ -404,9 +405,9 @@
    * @param options publish and/or subscribe options
    * @return entry
    */
-  [[nodiscard]] EntryType GetEntryEx(
-      std::string_view typeString, ParamType defaultValue,
-      const PubSubOptions& options = kDefaultPubSubOptions);
+  [[nodiscard]]
+  EntryType GetEntryEx(std::string_view typeString, ParamType defaultValue,
+                       const PubSubOptions& options = kDefaultPubSubOptions);
 };
 
 }  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/ntcore_c.h b/third_party/allwpilib/ntcore/src/main/native/include/ntcore_c.h
index e9e9f81..1af0e66 100644
--- a/third_party/allwpilib/ntcore/src/main/native/include/ntcore_c.h
+++ b/third_party/allwpilib/ntcore/src/main/native/include/ntcore_c.h
@@ -16,6 +16,8 @@
 extern "C" {
 #endif
 
+struct WPI_DataLog;
+
 /**
  * @defgroup ntcore_c_api ntcore C API
  *
@@ -1148,6 +1150,14 @@
 void NT_SetServerTeam(NT_Inst inst, unsigned int team, unsigned int port);
 
 /**
+ * Disconnects the client if it's running and connected. This will automatically
+ * start reconnection attempts to the current server list.
+ *
+ * @param inst instance handle
+ */
+void NT_Disconnect(NT_Inst inst);
+
+/**
  * Starts requesting server address from Driver Station.
  * This connects to the Driver Station running on localhost to obtain the
  * server IP address.
@@ -1340,6 +1350,54 @@
 /** @} */
 
 /**
+ * @defgroup ntcore_data_logger_cfunc Data Logger Functions
+ * @{
+ */
+
+/**
+ * Starts logging entry changes to a DataLog.
+ *
+ * @param inst instance handle
+ * @param log data log object; lifetime must extend until StopEntryDataLog is
+ *            called or the instance is destroyed
+ * @param prefix only store entries with names that start with this prefix;
+ *               the prefix is not included in the data log entry name
+ * @param logPrefix prefix to add to data log entry names
+ * @return Data logger handle
+ */
+NT_DataLogger NT_StartEntryDataLog(NT_Inst inst, struct WPI_DataLog* log,
+                                   const char* prefix, const char* logPrefix);
+
+/**
+ * Stops logging entry changes to a DataLog.
+ *
+ * @param logger data logger handle
+ */
+void NT_StopEntryDataLog(NT_DataLogger logger);
+
+/**
+ * Starts logging connection changes to a DataLog.
+ *
+ * @param inst instance handle
+ * @param log data log object; lifetime must extend until StopConnectionDataLog
+ *            is called or the instance is destroyed
+ * @param name data log entry name
+ * @return Data logger handle
+ */
+NT_ConnectionDataLogger NT_StartConnectionDataLog(NT_Inst inst,
+                                                  struct WPI_DataLog* log,
+                                                  const char* name);
+
+/**
+ * Stops logging connection changes to a DataLog.
+ *
+ * @param logger data logger handle
+ */
+void NT_StopConnectionDataLog(NT_ConnectionDataLogger logger);
+
+/** @} */
+
+/**
  * @defgroup ntcore_logger_cfunc Logger Functions
  * @{
  */
@@ -1378,6 +1436,44 @@
 /** @} */
 
 /**
+ * @defgroup ntcore_schema_cfunc Schema Functions
+ * @{
+ */
+
+/**
+ * Returns whether there is a data schema already registered with the given
+ * name. This does NOT perform a check as to whether the schema has already
+ * been published by another node on the network.
+ *
+ * @param inst instance
+ * @param name Name (the string passed as the data type for topics using this
+ *             schema)
+ * @return True if schema already registered
+ */
+NT_Bool NT_HasSchema(NT_Inst inst, const char* name);
+
+/**
+ * Registers a data schema.  Data schemas provide information for how a
+ * certain data type string can be decoded.  The type string of a data schema
+ * indicates the type of the schema itself (e.g. "protobuf" for protobuf
+ * schemas, "struct" for struct schemas, etc). In NetworkTables, schemas are
+ * published just like normal topics, with the name being generated from the
+ * provided name: "/.schema/<name>".  Duplicate calls to this function with
+ * the same name are silently ignored.
+ *
+ * @param inst instance
+ * @param name Name (the string passed as the data type for topics using this
+ *             schema)
+ * @param type Type of schema (e.g. "protobuf", "struct", etc)
+ * @param schema Schema data
+ * @param schemaSize Size of schema data
+ */
+void NT_AddSchema(NT_Inst inst, const char* name, const char* type,
+                  const uint8_t* schema, size_t schemaSize);
+
+/** @} */
+
+/**
  * @defgroup ntcore_interop_cfunc Interop Utility Functions
  * @{
  */
diff --git a/third_party/allwpilib/ntcore/src/main/native/include/ntcore_cpp.h b/third_party/allwpilib/ntcore/src/main/native/include/ntcore_cpp.h
index cbb541b..482d1e3 100644
--- a/third_party/allwpilib/ntcore/src/main/native/include/ntcore_cpp.h
+++ b/third_party/allwpilib/ntcore/src/main/native/include/ntcore_cpp.h
@@ -17,6 +17,8 @@
 #include <variant>
 #include <vector>
 
+#include <wpi/json_fwd.h>
+
 #include "networktables/NetworkTableValue.h"
 #include "ntcore_c.h"
 #include "ntcore_cpp_types.h"
@@ -24,7 +26,6 @@
 namespace wpi {
 template <typename T>
 class SmallVectorImpl;
-class json;
 }  // namespace wpi
 
 namespace wpi::log {
@@ -1088,6 +1089,14 @@
 void SetServerTeam(NT_Inst inst, unsigned int team, unsigned int port);
 
 /**
+ * Disconnects the client if it's running and connected. This will automatically
+ * start reconnection attempts to the current server list.
+ *
+ * @param inst instance handle
+ */
+void Disconnect(NT_Inst inst);
+
+/**
  * Starts requesting server address from Driver Station.
  * This connects to the Driver Station running on localhost to obtain the
  * server IP address.
@@ -1292,6 +1301,66 @@
                             unsigned int max_level);
 
 /** @} */
+
+/**
+ * @defgroup ntcore_schema_func Schema Functions
+ * @{
+ */
+
+/**
+ * Returns whether there is a data schema already registered with the given
+ * name. This does NOT perform a check as to whether the schema has already
+ * been published by another node on the network.
+ *
+ * @param inst instance
+ * @param name Name (the string passed as the data type for topics using this
+ *             schema)
+ * @return True if schema already registered
+ */
+bool HasSchema(NT_Inst inst, std::string_view name);
+
+/**
+ * Registers a data schema.  Data schemas provide information for how a
+ * certain data type string can be decoded.  The type string of a data schema
+ * indicates the type of the schema itself (e.g. "protobuf" for protobuf
+ * schemas, "struct" for struct schemas, etc). In NetworkTables, schemas are
+ * published just like normal topics, with the name being generated from the
+ * provided name: "/.schema/<name>".  Duplicate calls to this function with
+ * the same name are silently ignored.
+ *
+ * @param inst instance
+ * @param name Name (the string passed as the data type for topics using this
+ *             schema)
+ * @param type Type of schema (e.g. "protobuf", "struct", etc)
+ * @param schema Schema data
+ */
+void AddSchema(NT_Inst inst, std::string_view name, std::string_view type,
+               std::span<const uint8_t> schema);
+
+/**
+ * Registers a data schema.  Data schemas provide information for how a
+ * certain data type string can be decoded.  The type string of a data schema
+ * indicates the type of the schema itself (e.g. "protobuf" for protobuf
+ * schemas, "struct" for struct schemas, etc). In NetworkTables, schemas are
+ * published just like normal topics, with the name being generated from the
+ * provided name: "/.schema/<name>".  Duplicate calls to this function with
+ * the same name are silently ignored.
+ *
+ * @param inst instance
+ * @param name Name (the string passed as the data type for topics using this
+ *             schema)
+ * @param type Type of schema (e.g. "protobuf", "struct", etc)
+ * @param schema Schema data
+ */
+inline void AddSchema(NT_Inst inst, std::string_view name,
+                      std::string_view type, std::string_view schema) {
+  AddSchema(
+      inst, name, type,
+      std::span<const uint8_t>{reinterpret_cast<const uint8_t*>(schema.data()),
+                               schema.size()});
+}
+
+/** @} */
 /** @} */
 
 /**
diff --git a/third_party/allwpilib/ntcore/src/test/java/edu/wpi/first/networktables/RawTest.java b/third_party/allwpilib/ntcore/src/test/java/edu/wpi/first/networktables/RawTest.java
new file mode 100644
index 0000000..73d5efb
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/test/java/edu/wpi/first/networktables/RawTest.java
@@ -0,0 +1,137 @@
+// 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.
+
+package edu.wpi.first.networktables;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@SuppressWarnings("PMD.SimplifiableTestAssertion")
+class RawTest {
+  private NetworkTableInstance m_inst;
+
+  @BeforeEach
+  void setUp() {
+    m_inst = NetworkTableInstance.create();
+  }
+
+  @AfterEach
+  void tearDown() {
+    m_inst.close();
+  }
+
+  @Test
+  void testGenericByteArray() {
+    GenericEntry entry = m_inst.getTopic("test").getGenericEntry("raw");
+    entry.setRaw(new byte[] {5}, 10);
+    assertTrue(Arrays.equals(entry.getRaw(new byte[] {}), new byte[] {5}));
+    entry.setRaw(new byte[] {5, 6, 7}, 1, 2, 15);
+    assertTrue(Arrays.equals(entry.getRaw(new byte[] {}), new byte[] {6, 7}));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.setRaw(new byte[] {5}, -1, 2, 20));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.setRaw(new byte[] {5}, 1, -2, 20));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.setRaw(new byte[] {5}, 1, 1, 20));
+  }
+
+  @Test
+  void testRawByteArray() {
+    RawEntry entry = m_inst.getRawTopic("test").getEntry("raw", new byte[] {});
+    entry.set(new byte[] {5}, 10);
+    assertTrue(Arrays.equals(entry.get(new byte[] {}), new byte[] {5}));
+    entry.set(new byte[] {5, 6, 7}, 1, 2, 15);
+    assertTrue(Arrays.equals(entry.get(new byte[] {}), new byte[] {6, 7}));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.set(new byte[] {5}, -1, 1, 20));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.set(new byte[] {5}, 1, -1, 20));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.set(new byte[] {5}, 1, 1, 20));
+  }
+
+  @Test
+  void testGenericByteBuffer() {
+    GenericEntry entry = m_inst.getTopic("test").getGenericEntry("raw");
+    entry.setRaw(ByteBuffer.wrap(new byte[] {5}), 10);
+    assertTrue(Arrays.equals(entry.getRaw(new byte[] {}), new byte[] {5}));
+    entry.setRaw(ByteBuffer.wrap(new byte[] {5, 6, 7}).position(1), 15);
+    assertTrue(Arrays.equals(entry.getRaw(new byte[] {}), new byte[] {6, 7}));
+    entry.setRaw(ByteBuffer.wrap(new byte[] {5, 6, 7}).position(1).limit(2), 16);
+    assertTrue(Arrays.equals(entry.getRaw(new byte[] {}), new byte[] {6}));
+    entry.setRaw(ByteBuffer.wrap(new byte[] {8, 9, 0}), 1, 2, 20);
+    assertTrue(Arrays.equals(entry.getRaw(new byte[] {}), new byte[] {9, 0}));
+    entry.setRaw(ByteBuffer.wrap(new byte[] {1, 2, 3}).position(2), 0, 2, 25);
+    assertTrue(Arrays.equals(entry.getRaw(new byte[] {}), new byte[] {1, 2}));
+    assertThrows(
+        IndexOutOfBoundsException.class,
+        () -> entry.setRaw(ByteBuffer.wrap(new byte[] {5}), -1, 1, 30));
+    assertThrows(
+        IndexOutOfBoundsException.class,
+        () -> entry.setRaw(ByteBuffer.wrap(new byte[] {5}), 1, -1, 30));
+    assertThrows(
+        IndexOutOfBoundsException.class,
+        () -> entry.setRaw(ByteBuffer.wrap(new byte[] {5}), 1, 1, 30));
+  }
+
+  @Test
+  void testRawByteBuffer() {
+    RawEntry entry = m_inst.getRawTopic("test").getEntry("raw", new byte[] {});
+    entry.set(ByteBuffer.wrap(new byte[] {5}), 10);
+    assertTrue(Arrays.equals(entry.get(new byte[] {}), new byte[] {5}));
+    entry.set(ByteBuffer.wrap(new byte[] {5, 6, 7}).position(1), 15);
+    assertTrue(Arrays.equals(entry.get(new byte[] {}), new byte[] {6, 7}));
+    entry.set(ByteBuffer.wrap(new byte[] {5, 6, 7}).position(1).limit(2), 16);
+    assertTrue(Arrays.equals(entry.get(new byte[] {}), new byte[] {6}));
+    entry.set(ByteBuffer.wrap(new byte[] {8, 9, 0}), 1, 2, 20);
+    assertTrue(Arrays.equals(entry.get(new byte[] {}), new byte[] {9, 0}));
+    entry.set(ByteBuffer.wrap(new byte[] {1, 2, 3}).position(2), 0, 2, 25);
+    assertTrue(Arrays.equals(entry.get(new byte[] {}), new byte[] {1, 2}));
+    assertThrows(
+        IndexOutOfBoundsException.class,
+        () -> entry.set(ByteBuffer.wrap(new byte[] {5}), -1, 1, 30));
+    assertThrows(
+        IndexOutOfBoundsException.class,
+        () -> entry.set(ByteBuffer.wrap(new byte[] {5}), 1, -1, 30));
+    assertThrows(
+        IndexOutOfBoundsException.class,
+        () -> entry.set(ByteBuffer.wrap(new byte[] {5}), 1, 1, 30));
+  }
+
+  @Test
+  void testGenericNativeByteBuffer() {
+    GenericEntry entry = m_inst.getTopic("test").getGenericEntry("raw");
+    ByteBuffer bb = ByteBuffer.allocateDirect(3);
+    bb.put(new byte[] {5, 6, 7});
+    entry.setRaw(bb.position(1), 15);
+    assertTrue(Arrays.equals(entry.getRaw(new byte[] {}), new byte[] {6, 7}));
+    entry.setRaw(bb.limit(2), 16);
+    assertTrue(Arrays.equals(entry.getRaw(new byte[] {}), new byte[] {6}));
+    bb.clear();
+    bb.put(new byte[] {8, 9, 0});
+    entry.setRaw(bb, 1, 2, 20);
+    assertTrue(Arrays.equals(entry.getRaw(new byte[] {}), new byte[] {9, 0}));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.setRaw(bb, -1, 1, 25));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.setRaw(bb, 1, -1, 25));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.setRaw(bb, 2, 2, 25));
+  }
+
+  @Test
+  void testRawNativeByteBuffer() {
+    RawEntry entry = m_inst.getRawTopic("test").getEntry("raw", new byte[] {});
+    ByteBuffer bb = ByteBuffer.allocateDirect(3);
+    bb.put(new byte[] {5, 6, 7});
+    entry.set(bb.position(1), 15);
+    assertTrue(Arrays.equals(entry.get(new byte[] {}), new byte[] {6, 7}));
+    entry.set(bb.limit(2), 16);
+    assertTrue(Arrays.equals(entry.get(new byte[] {}), new byte[] {6}));
+    bb.clear();
+    bb.put(new byte[] {8, 9, 0});
+    entry.set(bb, 1, 2, 20);
+    assertTrue(Arrays.equals(entry.get(new byte[] {}), new byte[] {9, 0}));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.set(bb, -1, 1, 25));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.set(bb, 1, -1, 25));
+    assertThrows(IndexOutOfBoundsException.class, () -> entry.set(bb, 2, 2, 25));
+  }
+}
diff --git a/third_party/allwpilib/ntcore/src/test/java/edu/wpi/first/networktables/TimeSyncTest.java b/third_party/allwpilib/ntcore/src/test/java/edu/wpi/first/networktables/TimeSyncTest.java
index c539d20..9048ef4 100644
--- a/third_party/allwpilib/ntcore/src/test/java/edu/wpi/first/networktables/TimeSyncTest.java
+++ b/third_party/allwpilib/ntcore/src/test/java/edu/wpi/first/networktables/TimeSyncTest.java
@@ -34,29 +34,30 @@
 
   @Test
   void testServer() {
-    var poller = new NetworkTableListenerPoller(m_inst);
-    poller.addTimeSyncListener(false);
+    try (var poller = new NetworkTableListenerPoller(m_inst)) {
+      poller.addTimeSyncListener(false);
 
-    m_inst.startServer("timesynctest.json", "127.0.0.1", 0, 10030);
-    var offset = m_inst.getServerTimeOffset();
-    assertTrue(offset.isPresent());
-    assertEquals(0L, offset.getAsLong());
+      m_inst.startServer("timesynctest.json", "127.0.0.1", 0, 10030);
+      var offset = m_inst.getServerTimeOffset();
+      assertTrue(offset.isPresent());
+      assertEquals(0L, offset.getAsLong());
 
-    NetworkTableEvent[] events = poller.readQueue();
-    assertEquals(1, events.length);
-    assertNotNull(events[0].timeSyncData);
-    assertTrue(events[0].timeSyncData.valid);
-    assertEquals(0L, events[0].timeSyncData.serverTimeOffset);
-    assertEquals(0L, events[0].timeSyncData.rtt2);
+      NetworkTableEvent[] events = poller.readQueue();
+      assertEquals(1, events.length);
+      assertNotNull(events[0].timeSyncData);
+      assertTrue(events[0].timeSyncData.valid);
+      assertEquals(0L, events[0].timeSyncData.serverTimeOffset);
+      assertEquals(0L, events[0].timeSyncData.rtt2);
 
-    m_inst.stopServer();
-    offset = m_inst.getServerTimeOffset();
-    assertFalse(offset.isPresent());
+      m_inst.stopServer();
+      offset = m_inst.getServerTimeOffset();
+      assertFalse(offset.isPresent());
 
-    events = poller.readQueue();
-    assertEquals(1, events.length);
-    assertNotNull(events[0].timeSyncData);
-    assertFalse(events[0].timeSyncData.valid);
+      events = poller.readQueue();
+      assertEquals(1, events.length);
+      assertNotNull(events[0].timeSyncData);
+      assertFalse(events[0].timeSyncData.valid);
+    }
   }
 
   @Test
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/ConnectionListenerTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/ConnectionListenerTest.cpp
index 1561277..3e4dd24 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/ConnectionListenerTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/ConnectionListenerTest.cpp
@@ -5,11 +5,11 @@
 #include <chrono>
 #include <thread>
 
+#include <gtest/gtest.h>
 #include <wpi/Synchronization.h>
 #include <wpi/mutex.h>
 
 #include "TestPrinters.h"
-#include "gtest/gtest.h"
 #include "ntcore_cpp.h"
 
 class ConnectionListenerTest : public ::testing::Test {
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/LocalStorageTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/LocalStorageTest.cpp
index 5734284..eb57913 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/LocalStorageTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/LocalStorageTest.cpp
@@ -2,15 +2,16 @@
 // 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 <gtest/gtest.h>
+#include <wpi/SpanMatcher.h>
+
 #include "LocalStorage.h"
 #include "MockListenerStorage.h"
 #include "MockLogger.h"
 #include "PubSubOptionsMatcher.h"
-#include "SpanMatcher.h"
 #include "TestPrinters.h"
 #include "ValueMatcher.h"
 #include "gmock/gmock.h"
-#include "gtest/gtest.h"
 #include "net/MockNetworkInterface.h"
 #include "ntcore_c.h"
 #include "ntcore_cpp.h"
@@ -162,7 +163,7 @@
   EXPECT_EQ(value.GetBoolean(), true);
   EXPECT_EQ(value.time(), 5);
 
-  auto vals = storage.ReadQueueBoolean(sub);
+  auto vals = storage.ReadQueue<bool>(sub);
   ASSERT_EQ(vals.size(), 1u);
   EXPECT_EQ(vals[0].value, true);
   EXPECT_EQ(vals[0].time, 5);
@@ -171,7 +172,7 @@
   EXPECT_CALL(network, SetValue(pub, val));
   storage.SetEntryValue(pub, val);
 
-  auto vals2 = storage.ReadQueueInteger(sub);  // mismatched type
+  auto vals2 = storage.ReadQueue<int64_t>(sub);  // mismatched type
   ASSERT_TRUE(vals2.empty());
 }
 
@@ -197,9 +198,6 @@
   ASSERT_TRUE(value.IsBoolean());
   EXPECT_EQ(value.GetBoolean(), true);
   EXPECT_EQ(value.time(), 5);
-
-  auto vals = storage.ReadQueueValue(sub);  // read queue won't get anything
-  ASSERT_TRUE(vals.empty());
 }
 
 TEST_F(LocalStorageTest, EntryNoTypeLocalSet) {
@@ -224,7 +222,7 @@
   EXPECT_EQ(value.GetBoolean(), true);
   EXPECT_EQ(value.time(), 5);
 
-  auto vals = storage.ReadQueueBoolean(entry);
+  auto vals = storage.ReadQueue<bool>(entry);
   ASSERT_EQ(vals.size(), 1u);
   EXPECT_EQ(vals[0].value, true);
   EXPECT_EQ(vals[0].time, 5);
@@ -234,7 +232,7 @@
   EXPECT_CALL(network, SetValue(_, val));
   EXPECT_TRUE(storage.SetEntryValue(entry, val));
 
-  auto vals2 = storage.ReadQueueInteger(entry);  // mismatched type
+  auto vals2 = storage.ReadQueue<int64_t>(entry);  // mismatched type
   ASSERT_TRUE(vals2.empty());
 
   // cannot change type; won't generate network message
@@ -244,7 +242,7 @@
   EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN);
   EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean");
 
-  auto vals3 = storage.ReadQueueInteger(entry);  // mismatched type
+  auto vals3 = storage.ReadQueue<int64_t>(entry);  // mismatched type
   ASSERT_TRUE(vals3.empty());
 }
 
@@ -256,9 +254,11 @@
   EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
                                std::string_view{"boolean"}, wpi::json::object(),
                                IsDefaultPubSubOptions()));
-  EXPECT_CALL(logger, Call(NT_LOG_INFO, _, _,
-                           "local subscribe to 'foo' disabled due to type "
-                           "mismatch (wanted 'int', published as 'boolean')"));
+  EXPECT_CALL(logger,
+              Call(NT_LOG_INFO, _, _,
+                   std::string_view{
+                       "local subscribe to 'foo' disabled due to type "
+                       "mismatch (wanted 'int', published as 'boolean')"}));
   auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
 
   auto val = Value::MakeBoolean(true, 5);
@@ -269,7 +269,7 @@
   EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean");
   EXPECT_TRUE(storage.GetTopicExists(fooTopic));
 
-  EXPECT_TRUE(storage.ReadQueueInteger(sub).empty());
+  EXPECT_TRUE(storage.ReadQueue<int64_t>(sub).empty());
 
   EXPECT_CALL(network, Unpublish(pub, fooTopic));
   storage.Unpublish(pub);
@@ -291,7 +291,7 @@
   EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "int");
   EXPECT_TRUE(storage.GetTopicExists(fooTopic));
 
-  EXPECT_EQ(storage.ReadQueueInteger(sub).size(), 1u);
+  EXPECT_EQ(storage.ReadQueue<int64_t>(sub).size(), 1u);
 }
 
 TEST_F(LocalStorageTest, LocalPubConflict) {
@@ -300,9 +300,11 @@
                                IsDefaultPubSubOptions()));
   auto pub1 = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
 
-  EXPECT_CALL(logger, Call(NT_LOG_INFO, _, _,
-                           "local publish to 'foo' disabled due to type "
-                           "mismatch (wanted 'int', currently 'boolean')"));
+  EXPECT_CALL(
+      logger,
+      Call(NT_LOG_INFO, _, _,
+           std::string_view{"local publish to 'foo' disabled due to type "
+                            "mismatch (wanted 'int', currently 'boolean')"}));
   auto pub2 = storage.Publish(fooTopic, NT_INTEGER, "int", {}, {});
 
   EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN);
@@ -339,9 +341,11 @@
 
   EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
                                  IsDefaultPubSubOptions()));
-  EXPECT_CALL(logger, Call(NT_LOG_INFO, _, _,
-                           "local subscribe to 'foo' disabled due to type "
-                           "mismatch (wanted 'int', published as 'boolean')"));
+  EXPECT_CALL(logger,
+              Call(NT_LOG_INFO, _, _,
+                   std::string_view{
+                       "local subscribe to 'foo' disabled due to type "
+                       "mismatch (wanted 'int', published as 'boolean')"}));
   storage.Subscribe(fooTopic, NT_INTEGER, "int", {});
 }
 
@@ -352,9 +356,11 @@
 
   storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
 
-  EXPECT_CALL(logger, Call(NT_LOG_INFO, _, _,
-                           "network announce of 'foo' overriding local publish "
-                           "(was 'boolean', now 'int')"));
+  EXPECT_CALL(logger,
+              Call(NT_LOG_INFO, _, _,
+                   std::string_view{
+                       "network announce of 'foo' overriding local publish "
+                       "(was 'boolean', now 'int')"}));
 
   storage.NetworkAnnounce("foo", "int", wpi::json::object(), {});
 
@@ -478,11 +484,10 @@
 }
 
 TEST_F(LocalStorageTest, PublishUntyped) {
-  EXPECT_CALL(
-      logger,
-      Call(
-          NT_LOG_ERROR, _, _,
-          "cannot publish 'foo' with an unassigned type or empty type string"));
+  EXPECT_CALL(logger,
+              Call(NT_LOG_ERROR, _, _,
+                   std::string_view{"cannot publish 'foo' with an unassigned "
+                                    "type or empty type string"}));
 
   EXPECT_EQ(storage.Publish(fooTopic, NT_UNASSIGNED, "", {}, {}), 0u);
 }
@@ -494,7 +499,7 @@
 class LocalStorageDuplicatesTest : public LocalStorageTest {
  public:
   void SetupPubSub(bool keepPub, bool keepSub);
-  void SetValues();
+  void SetValues(bool expectDuplicates);
 
   NT_Publisher pub;
   NT_Subscriber sub;
@@ -521,11 +526,12 @@
                           {.pollStorage = 10, .keepDuplicates = keepSub});
 }
 
-void LocalStorageDuplicatesTest::SetValues() {
+void LocalStorageDuplicatesTest::SetValues(bool expectDuplicates) {
   storage.SetEntryValue(pub, val1);
   storage.SetEntryValue(pub, val2);
-  // verify the timestamp was updated
-  EXPECT_EQ(storage.GetEntryLastChange(sub), val2.time());
+  // verify the timestamp was updated (or not)
+  EXPECT_EQ(storage.GetEntryLastChange(sub),
+            expectDuplicates ? val2.time() : val1.time());
   storage.SetEntryValue(pub, val3);
 }
 
@@ -534,10 +540,10 @@
 
   EXPECT_CALL(network, SetValue(pub, val1));
   EXPECT_CALL(network, SetValue(pub, val3));
-  SetValues();
+  SetValues(false);
 
   // verify 2nd update was dropped locally
-  auto values = storage.ReadQueueDouble(sub);
+  auto values = storage.ReadQueue<double>(sub);
   ASSERT_EQ(values.size(), 2u);
   ASSERT_EQ(values[0].value, val1.GetDouble());
   ASSERT_EQ(values[0].time, val1.time());
@@ -551,11 +557,11 @@
   EXPECT_CALL(network, SetValue(pub, val1)).Times(2);
   // EXPECT_CALL(network, SetValue(pub, val2));
   EXPECT_CALL(network, SetValue(pub, val3));
-  SetValues();
+  SetValues(true);
 
-  // verify all 3 updates were received locally
-  auto values = storage.ReadQueueDouble(sub);
-  ASSERT_EQ(values.size(), 3u);
+  // verify only 2 updates were received locally
+  auto values = storage.ReadQueue<double>(sub);
+  ASSERT_EQ(values.size(), 2u);
 }
 
 TEST_F(LocalStorageDuplicatesTest, KeepSub) {
@@ -564,14 +570,28 @@
   // second update should NOT go to the network
   EXPECT_CALL(network, SetValue(pub, val1));
   EXPECT_CALL(network, SetValue(pub, val3));
-  SetValues();
+  SetValues(false);
+
+  // verify 2 updates were received locally
+  auto values = storage.ReadQueue<double>(sub);
+  ASSERT_EQ(values.size(), 2u);
+}
+
+TEST_F(LocalStorageDuplicatesTest, KeepPubSub) {
+  SetupPubSub(true, true);
+
+  // second update SHOULD go to the network
+  EXPECT_CALL(network, SetValue(pub, val1)).Times(2);
+  // EXPECT_CALL(network, SetValue(pub, val2));
+  EXPECT_CALL(network, SetValue(pub, val3));
+  SetValues(true);
 
   // verify all 3 updates were received locally
-  auto values = storage.ReadQueueDouble(sub);
+  auto values = storage.ReadQueue<double>(sub);
   ASSERT_EQ(values.size(), 3u);
 }
 
-TEST_F(LocalStorageDuplicatesTest, FromNetwork) {
+TEST_F(LocalStorageDuplicatesTest, FromNetworkDefault) {
   SetupPubSub(false, false);
 
   // incoming from the network are treated like a normal local publish
@@ -582,8 +602,8 @@
   EXPECT_EQ(storage.GetEntryLastChange(sub), val2.time());
   storage.NetworkSetValue(topic, val3);
 
-  // verify 2nd update was dropped locally
-  auto values = storage.ReadQueueDouble(sub);
+  // verify 2nd update was dropped for local subscriber
+  auto values = storage.ReadQueue<double>(sub);
   ASSERT_EQ(values.size(), 2u);
   ASSERT_EQ(values[0].value, val1.GetDouble());
   ASSERT_EQ(values[0].time, val1.time());
@@ -591,6 +611,69 @@
   ASSERT_EQ(values[1].time, val3.time());
 }
 
+TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPub) {
+  SetupPubSub(true, false);
+
+  // incoming from the network are treated like a normal local publish
+  auto topic = storage.NetworkAnnounce("foo", "double", {{}}, 0);
+  storage.NetworkSetValue(topic, val1);
+  storage.NetworkSetValue(topic, val2);
+  // verify the timestamp was updated
+  EXPECT_EQ(storage.GetEntryLastChange(sub), val2.time());
+  storage.NetworkSetValue(topic, val3);
+
+  // verify 2nd update was dropped for local subscriber
+  auto values = storage.ReadQueue<double>(sub);
+  ASSERT_EQ(values.size(), 2u);
+  ASSERT_EQ(values[0].value, val1.GetDouble());
+  ASSERT_EQ(values[0].time, val1.time());
+  ASSERT_EQ(values[1].value, val3.GetDouble());
+  ASSERT_EQ(values[1].time, val3.time());
+}
+TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepSub) {
+  SetupPubSub(false, true);
+
+  // incoming from the network are treated like a normal local publish
+  auto topic = storage.NetworkAnnounce("foo", "double", {{}}, 0);
+  storage.NetworkSetValue(topic, val1);
+  storage.NetworkSetValue(topic, val2);
+  // verify the timestamp was updated
+  EXPECT_EQ(storage.GetEntryLastChange(sub), val2.time());
+  storage.NetworkSetValue(topic, val3);
+
+  // verify 2nd update was received by local subscriber
+  auto values = storage.ReadQueue<double>(sub);
+  ASSERT_EQ(values.size(), 3u);
+  ASSERT_EQ(values[0].value, val1.GetDouble());
+  ASSERT_EQ(values[0].time, val1.time());
+  ASSERT_EQ(values[1].value, val2.GetDouble());
+  ASSERT_EQ(values[1].time, val2.time());
+  ASSERT_EQ(values[2].value, val3.GetDouble());
+  ASSERT_EQ(values[2].time, val3.time());
+}
+
+TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPubSub) {
+  SetupPubSub(true, true);
+
+  // incoming from the network are treated like a normal local publish
+  auto topic = storage.NetworkAnnounce("foo", "double", {{}}, 0);
+  storage.NetworkSetValue(topic, val1);
+  storage.NetworkSetValue(topic, val2);
+  // verify the timestamp was updated
+  EXPECT_EQ(storage.GetEntryLastChange(sub), val2.time());
+  storage.NetworkSetValue(topic, val3);
+
+  // verify 2nd update was received by local subscriber
+  auto values = storage.ReadQueue<double>(sub);
+  ASSERT_EQ(values.size(), 3u);
+  ASSERT_EQ(values[0].value, val1.GetDouble());
+  ASSERT_EQ(values[0].time, val1.time());
+  ASSERT_EQ(values[1].value, val2.GetDouble());
+  ASSERT_EQ(values[1].time, val2.time());
+  ASSERT_EQ(values[2].value, val3.GetDouble());
+  ASSERT_EQ(values[2].time, val3.time());
+}
+
 class LocalStorageNumberVariantsTest : public LocalStorageTest {
  public:
   void CreateSubscriber(NT_Handle* handle, std::string_view name, NT_Type type,
@@ -621,8 +704,9 @@
 void LocalStorageNumberVariantsTest::CreateSubscribers() {
   EXPECT_CALL(logger,
               Call(NT_LOG_INFO, _, _,
-                   "local subscribe to 'foo' disabled due to type "
-                   "mismatch (wanted 'boolean', published as 'double')"));
+                   std::string_view{
+                       "local subscribe to 'foo' disabled due to type "
+                       "mismatch (wanted 'boolean', published as 'double')"}));
   CreateSubscriber(&sub1, "subDouble", NT_DOUBLE, "double");
   CreateSubscriber(&sub2, "subInteger", NT_INTEGER, "int");
   CreateSubscriber(&sub3, "subFloat", NT_FLOAT, "float");
@@ -632,10 +716,12 @@
 }
 
 void LocalStorageNumberVariantsTest::CreateSubscribersArray() {
-  EXPECT_CALL(logger,
-              Call(NT_LOG_INFO, _, _,
-                   "local subscribe to 'foo' disabled due to type "
-                   "mismatch (wanted 'boolean[]', published as 'double[]')"));
+  EXPECT_CALL(
+      logger,
+      Call(NT_LOG_INFO, _, _,
+           std::string_view{
+               "local subscribe to 'foo' disabled due to type "
+               "mismatch (wanted 'boolean[]', published as 'double[]')"}));
   CreateSubscriber(&sub1, "subDouble", NT_DOUBLE_ARRAY, "double[]");
   CreateSubscriber(&sub2, "subInteger", NT_INTEGER_ARRAY, "int[]");
   CreateSubscriber(&sub3, "subFloat", NT_FLOAT_ARRAY, "float[]");
@@ -703,13 +789,13 @@
 
   for (auto&& subentry : subentries) {
     SCOPED_TRACE(subentry.name);
-    EXPECT_THAT(storage.GetAtomicDouble(subentry.subentry, 0),
+    EXPECT_THAT(storage.GetAtomic<double>(subentry.subentry, 0),
                 TSEq<TimestampedDouble>(1.0, 50));
-    EXPECT_THAT(storage.GetAtomicInteger(subentry.subentry, 0),
+    EXPECT_THAT(storage.GetAtomic<int64_t>(subentry.subentry, 0),
                 TSEq<TimestampedInteger>(1, 50));
-    EXPECT_THAT(storage.GetAtomicFloat(subentry.subentry, 0),
+    EXPECT_THAT(storage.GetAtomic<float>(subentry.subentry, 0),
                 TSEq<TimestampedFloat>(1.0, 50));
-    EXPECT_THAT(storage.GetAtomicBoolean(subentry.subentry, false),
+    EXPECT_THAT(storage.GetAtomic<bool>(subentry.subentry, false),
                 TSEq<TimestampedBoolean>(false, 0));
   }
 }
@@ -732,15 +818,15 @@
   for (auto&& subentry : subentries) {
     SCOPED_TRACE(subentry.name);
     double doubleVal = 1.0;
-    EXPECT_THAT(storage.GetAtomicDoubleArray(subentry.subentry, {}),
+    EXPECT_THAT(storage.GetAtomic<double[]>(subentry.subentry, {}),
                 TSSpanEq<TimestampedDoubleArray>(std::span{&doubleVal, 1}, 50));
     int64_t intVal = 1;
-    EXPECT_THAT(storage.GetAtomicIntegerArray(subentry.subentry, {}),
+    EXPECT_THAT(storage.GetAtomic<int64_t[]>(subentry.subentry, {}),
                 TSSpanEq<TimestampedIntegerArray>(std::span{&intVal, 1}, 50));
     float floatVal = 1.0;
-    EXPECT_THAT(storage.GetAtomicFloatArray(subentry.subentry, {}),
+    EXPECT_THAT(storage.GetAtomic<float[]>(subentry.subentry, {}),
                 TSSpanEq<TimestampedFloatArray>(std::span{&floatVal, 1}, 50));
-    EXPECT_THAT(storage.GetAtomicBooleanArray(subentry.subentry, {}),
+    EXPECT_THAT(storage.GetAtomic<bool[]>(subentry.subentry, {}),
                 TSSpanEq<TimestampedBooleanArray>(std::span<int>{}, 0));
   }
 }
@@ -756,9 +842,9 @@
   for (auto&& subentry : subentries) {
     SCOPED_TRACE(subentry.name);
     if (subentry.type == NT_BOOLEAN) {
-      EXPECT_THAT(storage.ReadQueueDouble(subentry.subentry), IsEmpty());
+      EXPECT_THAT(storage.ReadQueue<double>(subentry.subentry), IsEmpty());
     } else {
-      EXPECT_THAT(storage.ReadQueueDouble(subentry.subentry),
+      EXPECT_THAT(storage.ReadQueue<double>(subentry.subentry),
                   ElementsAre(TSEq<TimestampedDouble>(1.0, 50)));
     }
   }
@@ -767,9 +853,9 @@
   for (auto&& subentry : subentries) {
     SCOPED_TRACE(subentry.name);
     if (subentry.type == NT_BOOLEAN) {
-      EXPECT_THAT(storage.ReadQueueInteger(subentry.subentry), IsEmpty());
+      EXPECT_THAT(storage.ReadQueue<int64_t>(subentry.subentry), IsEmpty());
     } else {
-      EXPECT_THAT(storage.ReadQueueInteger(subentry.subentry),
+      EXPECT_THAT(storage.ReadQueue<int64_t>(subentry.subentry),
                   ElementsAre(TSEq<TimestampedInteger>(2, 50)));
     }
   }
@@ -778,9 +864,9 @@
   for (auto&& subentry : subentries) {
     SCOPED_TRACE(subentry.name);
     if (subentry.type == NT_BOOLEAN) {
-      EXPECT_THAT(storage.ReadQueueFloat(subentry.subentry), IsEmpty());
+      EXPECT_THAT(storage.ReadQueue<float>(subentry.subentry), IsEmpty());
     } else {
-      EXPECT_THAT(storage.ReadQueueFloat(subentry.subentry),
+      EXPECT_THAT(storage.ReadQueue<float>(subentry.subentry),
                   ElementsAre(TSEq<TimestampedFloat>(3.0, 50)));
     }
   }
@@ -788,7 +874,7 @@
   storage.SetEntryValue(pub, Value::MakeDouble(4.0, 50));
   for (auto&& subentry : subentries) {
     SCOPED_TRACE(subentry.name);
-    EXPECT_THAT(storage.ReadQueueBoolean(subentry.subentry), IsEmpty());
+    EXPECT_THAT(storage.ReadQueue<bool>(subentry.subentry), IsEmpty());
   }
 }
 
@@ -855,19 +941,19 @@
   // local set
   EXPECT_CALL(network, SetValue(_, _));
   storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50));
-  EXPECT_THAT(storage.ReadQueueDouble(subBoth),
+  EXPECT_THAT(storage.ReadQueue<double>(subBoth),
               ElementsAre(TSEq<TimestampedDouble>(1.0, 50)));
-  EXPECT_THAT(storage.ReadQueueDouble(subLocal),
+  EXPECT_THAT(storage.ReadQueue<double>(subLocal),
               ElementsAre(TSEq<TimestampedDouble>(1.0, 50)));
-  EXPECT_THAT(storage.ReadQueueDouble(subRemote), IsEmpty());
+  EXPECT_THAT(storage.ReadQueue<double>(subRemote), IsEmpty());
 
   // network set
   storage.NetworkSetValue(remoteTopic, Value::MakeDouble(2.0, 60));
-  EXPECT_THAT(storage.ReadQueueDouble(subBoth),
+  EXPECT_THAT(storage.ReadQueue<double>(subBoth),
               ElementsAre(TSEq<TimestampedDouble>(2.0, 60)));
-  EXPECT_THAT(storage.ReadQueueDouble(subRemote),
+  EXPECT_THAT(storage.ReadQueue<double>(subRemote),
               ElementsAre(TSEq<TimestampedDouble>(2.0, 60)));
-  EXPECT_THAT(storage.ReadQueueDouble(subLocal), IsEmpty());
+  EXPECT_THAT(storage.ReadQueue<double>(subLocal), IsEmpty());
 }
 
 TEST_F(LocalStorageTest, SubExcludePub) {
@@ -884,15 +970,15 @@
   // local set
   EXPECT_CALL(network, SetValue(_, _));
   storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50));
-  EXPECT_THAT(storage.ReadQueueDouble(subActive),
+  EXPECT_THAT(storage.ReadQueue<double>(subActive),
               ElementsAre(TSEq<TimestampedDouble>(1.0, 50)));
-  EXPECT_THAT(storage.ReadQueueDouble(subExclude), IsEmpty());
+  EXPECT_THAT(storage.ReadQueue<double>(subExclude), IsEmpty());
 
   // network set
   storage.NetworkSetValue(remoteTopic, Value::MakeDouble(2.0, 60));
-  EXPECT_THAT(storage.ReadQueueDouble(subActive),
+  EXPECT_THAT(storage.ReadQueue<double>(subActive),
               ElementsAre(TSEq<TimestampedDouble>(2.0, 60)));
-  EXPECT_THAT(storage.ReadQueueDouble(subExclude),
+  EXPECT_THAT(storage.ReadQueue<double>(subExclude),
               ElementsAre(TSEq<TimestampedDouble>(2.0, 60)));
 }
 
@@ -908,12 +994,56 @@
   EXPECT_CALL(network, Publish(_, _, _, _, _, _));
   EXPECT_CALL(network, SetValue(_, _));
   storage.SetEntryValue(entry, Value::MakeDouble(1.0, 50));
-  EXPECT_THAT(storage.ReadQueueDouble(entry), IsEmpty());
+  EXPECT_THAT(storage.ReadQueue<double>(entry), IsEmpty());
 
   // network set
   storage.NetworkSetValue(remoteTopic, Value::MakeDouble(2.0, 60));
-  EXPECT_THAT(storage.ReadQueueDouble(entry),
+  EXPECT_THAT(storage.ReadQueue<double>(entry),
               ElementsAre(TSEq<TimestampedDouble>(2.0, 60)));
 }
 
+TEST_F(LocalStorageTest, ReadQueueInitialLocal) {
+  EXPECT_CALL(network, Publish(_, _, _, _, _, _));
+  EXPECT_CALL(network, SetValue(_, _));
+  EXPECT_CALL(network, Subscribe(_, _, _)).Times(3);
+
+  auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {});
+  storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50));
+
+  auto subBoth =
+      storage.Subscribe(fooTopic, NT_DOUBLE, "double", kDefaultPubSubOptions);
+  auto subLocal =
+      storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.disableRemote = true});
+  auto subRemote =
+      storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.disableLocal = true});
+
+  EXPECT_THAT(storage.ReadQueue<double>(subBoth),
+              ElementsAre(TSEq<TimestampedDouble>(1.0, 50)));
+  EXPECT_THAT(storage.ReadQueue<double>(subLocal),
+              ElementsAre(TSEq<TimestampedDouble>(1.0, 50)));
+  EXPECT_THAT(storage.ReadQueue<double>(subRemote), IsEmpty());
+}
+
+TEST_F(LocalStorageTest, ReadQueueInitialRemote) {
+  EXPECT_CALL(network, Subscribe(_, _, _)).Times(3);
+
+  auto remoteTopic =
+      storage.NetworkAnnounce("foo", "double", wpi::json::object(), 0);
+  storage.NetworkSetValue(remoteTopic, Value::MakeDouble(2.0, 60));
+
+  auto subBoth =
+      storage.Subscribe(fooTopic, NT_DOUBLE, "double", kDefaultPubSubOptions);
+  auto subLocal =
+      storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.disableRemote = true});
+  auto subRemote =
+      storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.disableLocal = true});
+
+  // network set
+  EXPECT_THAT(storage.ReadQueue<double>(subBoth),
+              ElementsAre(TSEq<TimestampedDouble>(2.0, 60)));
+  EXPECT_THAT(storage.ReadQueue<double>(subRemote),
+              ElementsAre(TSEq<TimestampedDouble>(2.0, 60)));
+  EXPECT_THAT(storage.ReadQueue<double>(subLocal), IsEmpty());
+}
+
 }  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/LoggerTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/LoggerTest.cpp
index a9f499c..9e974c3 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/LoggerTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/LoggerTest.cpp
@@ -2,11 +2,11 @@
 // 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 <gtest/gtest.h>
 #include <wpi/Synchronization.h>
 
 #include "Handle.h"
 #include "TestPrinters.h"
-#include "gtest/gtest.h"
 #include "ntcore_cpp.h"
 
 class LoggerTest : public ::testing::Test {
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/NetworkTableTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/NetworkTableTest.cpp
index aa28afd..4429c89 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/NetworkTableTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/NetworkTableTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "TestPrinters.h"
-#include "gtest/gtest.h"
 #include "networktables/NetworkTable.h"
 #include "networktables/NetworkTableInstance.h"
 
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/SpanMatcher.h b/third_party/allwpilib/ntcore/src/test/native/cpp/SpanMatcher.h
deleted file mode 100644
index 9973c03..0000000
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/SpanMatcher.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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 <algorithm>
-#include <initializer_list>
-#include <memory>
-#include <ostream>
-#include <span>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "TestPrinters.h"
-#include "gmock/gmock.h"
-
-namespace wpi {
-
-template <typename T>
-class SpanMatcher : public ::testing::MatcherInterface<std::span<T>> {
- public:
-  explicit SpanMatcher(std::span<T> good_) : good{good_.begin(), good_.end()} {}
-
-  bool MatchAndExplain(std::span<T> val,
-                       ::testing::MatchResultListener* listener) const override;
-  void DescribeTo(::std::ostream* os) const override;
-  void DescribeNegationTo(::std::ostream* os) const override;
-
- private:
-  std::vector<std::remove_cv_t<T>> good;
-};
-
-template <typename T>
-inline ::testing::Matcher<std::span<const T>> SpanEq(std::span<const T> good) {
-  return ::testing::MakeMatcher(new SpanMatcher(good));
-}
-
-template <typename T>
-inline ::testing::Matcher<std::span<const T>> SpanEq(
-    std::initializer_list<const T> good) {
-  return ::testing::MakeMatcher(
-      new SpanMatcher<const T>({good.begin(), good.end()}));
-}
-
-template <typename T>
-bool SpanMatcher<T>::MatchAndExplain(
-    std::span<T> val, ::testing::MatchResultListener* listener) const {
-  if (val.size() != good.size() ||
-      !std::equal(val.begin(), val.end(), good.begin())) {
-    return false;
-  }
-  return true;
-}
-
-template <typename T>
-void SpanMatcher<T>::DescribeTo(::std::ostream* os) const {
-  PrintTo(std::span<T>{good}, os);
-}
-
-template <typename T>
-void SpanMatcher<T>::DescribeNegationTo(::std::ostream* os) const {
-  *os << "is not equal to ";
-  PrintTo(std::span<T>{good}, os);
-}
-
-}  // namespace wpi
-
-inline std::span<const uint8_t> operator"" _us(const char* str, size_t len) {
-  return {reinterpret_cast<const uint8_t*>(str), len};
-}
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/TableListenerTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/TableListenerTest.cpp
index 7fb1947..797c505 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/TableListenerTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/TableListenerTest.cpp
@@ -4,9 +4,10 @@
 
 #include <memory>
 
+#include <gtest/gtest.h>
+
 #include "TestPrinters.h"
 #include "gmock/gmock.h"
-#include "gtest/gtest.h"
 #include "networktables/DoubleTopic.h"
 #include "networktables/NetworkTableInstance.h"
 #include "ntcore_cpp.h"
@@ -45,7 +46,7 @@
   MockTableEventListener listener;
   table->AddListener(NT_EVENT_TOPIC | NT_EVENT_IMMEDIATE,
                      listener.AsStdFunction());
-  EXPECT_CALL(listener, Call(table.get(), "foovalue", _));
+  EXPECT_CALL(listener, Call(table.get(), std::string_view{"foovalue"}, _));
   PublishTopics();
   EXPECT_TRUE(m_inst.WaitForListenerQueue(1.0));
 }
@@ -54,7 +55,7 @@
   auto table = m_inst.GetTable("/foo");
   MockSubTableListener listener;
   table->AddSubTableListener(listener.AsStdFunction());
-  EXPECT_CALL(listener, Call(table.get(), "bar", _));
+  EXPECT_CALL(listener, Call(table.get(), std::string_view{"bar"}, _));
   PublishTopics();
   EXPECT_TRUE(m_inst.WaitForListenerQueue(1.0));
 }
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/TestPrinters.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/TestPrinters.cpp
index 66afe4e..708573f 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/TestPrinters.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/TestPrinters.cpp
@@ -4,8 +4,6 @@
 
 #include "TestPrinters.h"
 
-#include <wpi/json.h>
-
 #include "Handle.h"
 #include "PubSubOptions.h"
 #include "net/Message.h"
@@ -13,12 +11,6 @@
 #include "networktables/NetworkTableValue.h"
 #include "ntcore_cpp.h"
 
-namespace wpi {
-void PrintTo(const json& val, ::std::ostream* os) {
-  *os << val.dump();
-}
-}  // namespace wpi
-
 namespace nt {
 
 void PrintTo(const Event& event, std::ostream* os) {
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/TestPrinters.h b/third_party/allwpilib/ntcore/src/test/native/cpp/TestPrinters.h
index 2b8f1da..3481e76 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/TestPrinters.h
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/TestPrinters.h
@@ -9,34 +9,8 @@
 #include <string>
 #include <string_view>
 
-#include "gtest/gtest.h"
-
-namespace wpi {
-
-class json;
-
-inline void PrintTo(std::string_view str, ::std::ostream* os) {
-  ::testing::internal::PrintStringTo(std::string{str}, os);
-}
-
-template <typename T>
-void PrintTo(std::span<T> val, ::std::ostream* os) {
-  *os << '{';
-  bool first = true;
-  for (auto v : val) {
-    if (first) {
-      first = false;
-    } else {
-      *os << ", ";
-    }
-    *os << ::testing::PrintToString(v);
-  }
-  *os << '}';
-}
-
-void PrintTo(const json& val, ::std::ostream* os);
-
-}  // namespace wpi
+#include <gtest/gtest.h>
+#include <wpi/TestPrinters.h>
 
 namespace nt {
 
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/TimeSyncTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/TimeSyncTest.cpp
index 54e1f7c..1820b37 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/TimeSyncTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/TimeSyncTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "networktables/NetworkTableInstance.h"
 #include "networktables/NetworkTableListener.h"
 
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/TopicListenerTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/TopicListenerTest.cpp
index f0a002a..8f46742 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/TopicListenerTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/TopicListenerTest.cpp
@@ -5,12 +5,12 @@
 #include <chrono>
 #include <thread>
 
+#include <gtest/gtest.h>
 #include <wpi/Synchronization.h>
 #include <wpi/json.h>
 
 #include "TestPrinters.h"
 #include "ValueMatcher.h"
-#include "gtest/gtest.h"
 #include "ntcore_c.h"
 #include "ntcore_cpp.h"
 
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/ValueListenerTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/ValueListenerTest.cpp
index dbe3201..83ef6d8 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/ValueListenerTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/ValueListenerTest.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <wpi/StringExtras.h>
 #include <wpi/Synchronization.h>
 
 #include "TestPrinters.h"
 #include "ValueMatcher.h"
-#include "gtest/gtest.h"
 #include "ntcore_c.h"
 #include "ntcore_cpp.h"
 
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/ValueTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/ValueTest.cpp
index d49be44..ba99782 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/ValueTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/ValueTest.cpp
@@ -5,14 +5,15 @@
 #include <algorithm>
 #include <string_view>
 
+#include <gtest/gtest.h>
+
 #include "TestPrinters.h"
 #include "Value_internal.h"
-#include "gtest/gtest.h"
 #include "networktables/NetworkTableValue.h"
 
 using namespace std::string_view_literals;
 
-namespace std {  // NOLINT (clang-tidy.cert-dcl58-cpp)
+namespace std {  // NOLINT(clang-tidy.cert-dcl58-cpp)
 template <typename T, typename U>
 inline bool operator==(std::span<T> lhs, std::span<U> rhs) {
   if (lhs.size() != rhs.size()) {
@@ -336,6 +337,58 @@
   vec = {1, 0};
   v2 = Value::MakeBooleanArray(vec);
   ASSERT_NE(v1, v2);
+
+  // empty
+  vec = {};
+  v1 = Value::MakeBooleanArray(vec);
+  v2 = Value::MakeBooleanArray(vec);
+  ASSERT_EQ(v1, v2);
+}
+
+TEST_F(ValueTest, IntegerArrayComparison) {
+  std::vector<int64_t> vec{-42, 0, 1};
+  auto v1 = Value::MakeIntegerArray(vec);
+  auto v2 = Value::MakeIntegerArray(vec);
+  ASSERT_EQ(v1, v2);
+
+  // different contents
+  vec = {-42, 1, 1};
+  v2 = Value::MakeIntegerArray(vec);
+  ASSERT_NE(v1, v2);
+
+  // different size
+  vec = {-42, 0};
+  v2 = Value::MakeIntegerArray(vec);
+  ASSERT_NE(v1, v2);
+
+  // empty
+  vec = {};
+  v1 = Value::MakeIntegerArray(vec);
+  v2 = Value::MakeIntegerArray(vec);
+  ASSERT_EQ(v1, v2);
+}
+
+TEST_F(ValueTest, FloatArrayComparison) {
+  std::vector<float> vec{0.5, 0.25, 0.5};
+  auto v1 = Value::MakeFloatArray(vec);
+  auto v2 = Value::MakeFloatArray(vec);
+  ASSERT_EQ(v1, v2);
+
+  // different contents
+  vec = {0.5, 0.5, 0.5};
+  v2 = Value::MakeFloatArray(vec);
+  ASSERT_NE(v1, v2);
+
+  // different size
+  vec = {0.5, 0.25};
+  v2 = Value::MakeFloatArray(vec);
+  ASSERT_NE(v1, v2);
+
+  // empty
+  vec = {};
+  v1 = Value::MakeFloatArray(vec);
+  v2 = Value::MakeFloatArray(vec);
+  ASSERT_EQ(v1, v2);
 }
 
 TEST_F(ValueTest, DoubleArrayComparison) {
@@ -353,6 +406,12 @@
   vec = {0.5, 0.25};
   v2 = Value::MakeDoubleArray(vec);
   ASSERT_NE(v1, v2);
+
+  // empty
+  vec = {};
+  v1 = Value::MakeDoubleArray(vec);
+  v2 = Value::MakeDoubleArray(vec);
+  ASSERT_EQ(v1, v2);
 }
 
 TEST_F(ValueTest, StringArrayComparison) {
@@ -390,6 +449,12 @@
   vec.push_back("goodbye");
   v2 = Value::MakeStringArray(std::move(vec));
   ASSERT_NE(v1, v2);
+
+  // empty
+  vec.clear();
+  v1 = Value::MakeStringArray(vec);
+  v2 = Value::MakeStringArray(std::move(vec));
+  ASSERT_EQ(v1, v2);
 }
 
 }  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/main.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/main.cpp
index 6d37027..0f060b0 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/main.cpp
@@ -4,10 +4,13 @@
 
 #include <climits>
 
+#include <wpi/timestamp.h>
+
 #include "gmock/gmock.h"
 #include "ntcore.h"
 
 int main(int argc, char** argv) {
+  wpi::impl::SetupNowRio();
   nt::AddLogger(nt::GetDefaultInstance(), 0, UINT_MAX, [](auto& event) {
     if (auto msg = event.GetLogMessage()) {
       std::fputs(msg->message.c_str(), stderr);
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/net/MockWireConnection.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/net/MockWireConnection.cpp
deleted file mode 100644
index a5ddb1f..0000000
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/net/MockWireConnection.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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 "MockWireConnection.h"
-
-using namespace nt::net;
-
-void MockWireConnection::StartSendText() {
-  if (m_in_text) {
-    m_text_os << ',';
-  } else {
-    m_text_os << '[';
-    m_in_text = true;
-  }
-}
-
-void MockWireConnection::FinishSendText() {
-  if (m_in_text) {
-    m_text_os << ']';
-    m_in_text = false;
-  }
-  m_text_os.flush();
-  Text(m_text);
-  m_text.clear();
-}
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/net/MockWireConnection.h b/third_party/allwpilib/ntcore/src/test/native/cpp/net/MockWireConnection.h
index 3909cab..797ca25 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/net/MockWireConnection.h
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/net/MockWireConnection.h
@@ -20,35 +20,52 @@
 
 class MockWireConnection : public WireConnection {
  public:
-  MockWireConnection() : m_text_os{m_text}, m_binary_os{m_binary} {}
+  MOCK_METHOD(unsigned int, GetVersion, (), (const, override));
+
+  MOCK_METHOD(void, SendPing, (uint64_t time), (override));
 
   MOCK_METHOD(bool, Ready, (), (const, override));
 
-  TextWriter SendText() override { return {m_text_os, *this}; }
-  BinaryWriter SendBinary() override { return {m_binary_os, *this}; }
-
-  MOCK_METHOD(void, Text, (std::string_view contents));
-  MOCK_METHOD(void, Binary, (std::span<const uint8_t> contents));
-
-  MOCK_METHOD(void, Flush, (), (override));
-
-  MOCK_METHOD(void, Disconnect, (std::string_view reason), (override));
-
- protected:
-  void StartSendText() override;
-  void FinishSendText() override;
-  void StartSendBinary() override {}
-  void FinishSendBinary() override {
-    Binary(m_binary);
-    m_binary.resize(0);
+  int WriteText(wpi::function_ref<void(wpi::raw_ostream& os)> writer) override {
+    std::string text;
+    wpi::raw_string_ostream os{text};
+    writer(os);
+    return DoWriteText(text);
+  }
+  int WriteBinary(
+      wpi::function_ref<void(wpi::raw_ostream& os)> writer) override {
+    std::vector<uint8_t> binary;
+    wpi::raw_uvector_ostream os{binary};
+    writer(os);
+    return DoWriteBinary(binary);
   }
 
- private:
-  std::string m_text;
-  wpi::raw_string_ostream m_text_os;
-  std::vector<uint8_t> m_binary;
-  wpi::raw_uvector_ostream m_binary_os;
-  bool m_in_text{false};
+  void SendText(wpi::function_ref<void(wpi::raw_ostream& os)> writer) override {
+    std::string text;
+    wpi::raw_string_ostream os{text};
+    writer(os);
+    DoSendText(text);
+  }
+  void SendBinary(
+      wpi::function_ref<void(wpi::raw_ostream& os)> writer) override {
+    std::vector<uint8_t> binary;
+    wpi::raw_uvector_ostream os{binary};
+    writer(os);
+    DoSendBinary(binary);
+  }
+
+  MOCK_METHOD(int, DoWriteText, (std::string_view contents));
+  MOCK_METHOD(int, DoWriteBinary, (std::span<const uint8_t> contents));
+
+  MOCK_METHOD(void, DoSendText, (std::string_view contents));
+  MOCK_METHOD(void, DoSendBinary, (std::span<const uint8_t> contents));
+
+  MOCK_METHOD(int, Flush, (), (override));
+
+  MOCK_METHOD(uint64_t, GetLastFlushTime, (), (const, override));
+  MOCK_METHOD(uint64_t, GetLastPingResponse, (), (const, override));
+
+  MOCK_METHOD(void, Disconnect, (std::string_view reason), (override));
 };
 
 }  // namespace nt::net
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/net/ServerImplTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/net/ServerImplTest.cpp
new file mode 100644
index 0000000..6423811
--- /dev/null
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/net/ServerImplTest.cpp
@@ -0,0 +1,364 @@
+// 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 <stdint.h>
+
+#include <concepts>
+#include <span>
+#include <string_view>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <wpi/SpanMatcher.h>
+
+#include "../MockLogger.h"
+#include "../PubSubOptionsMatcher.h"
+#include "../TestPrinters.h"
+#include "../ValueMatcher.h"
+#include "Handle.h"
+#include "MockNetworkInterface.h"
+#include "MockWireConnection.h"
+#include "gmock/gmock.h"
+#include "net/Message.h"
+#include "net/ServerImpl.h"
+#include "net/WireEncoder.h"
+#include "ntcore_c.h"
+#include "ntcore_cpp.h"
+
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::Property;
+using ::testing::Return;
+using ::testing::StrEq;
+
+using MockSetPeriodicFunc = ::testing::MockFunction<void(uint32_t repeatMs)>;
+using MockConnected3Func =
+    ::testing::MockFunction<void(std::string_view name, uint16_t proto)>;
+
+namespace nt {
+
+class ServerImplTest : public ::testing::Test {
+ public:
+  ::testing::StrictMock<net::MockLocalInterface> local;
+  wpi::MockLogger logger;
+  net::ServerImpl server{logger};
+};
+
+TEST_F(ServerImplTest, AddClient) {
+  ::testing::StrictMock<net::MockWireConnection> wire;
+  // EXPECT_CALL(wire, Flush());
+  MockSetPeriodicFunc setPeriodic;
+  auto [name, id] = server.AddClient("test", "connInfo", false, wire,
+                                     setPeriodic.AsStdFunction());
+  EXPECT_EQ(name, "test@1");
+  EXPECT_NE(id, -1);
+}
+
+TEST_F(ServerImplTest, AddDuplicateClient) {
+  ::testing::StrictMock<net::MockWireConnection> wire1;
+  ::testing::StrictMock<net::MockWireConnection> wire2;
+  MockSetPeriodicFunc setPeriodic1;
+  MockSetPeriodicFunc setPeriodic2;
+  // EXPECT_CALL(wire1, Flush());
+  // EXPECT_CALL(wire2, Flush());
+
+  auto [name1, id1] = server.AddClient("test", "connInfo", false, wire1,
+                                       setPeriodic1.AsStdFunction());
+  auto [name2, id2] = server.AddClient("test", "connInfo2", false, wire2,
+                                       setPeriodic2.AsStdFunction());
+  EXPECT_EQ(name1, "test@1");
+  EXPECT_NE(id1, -1);
+  EXPECT_EQ(name2, "test@2");
+  EXPECT_NE(id1, id2);
+  EXPECT_NE(id2, -1);
+}
+
+TEST_F(ServerImplTest, AddClient3) {}
+
+template <typename T>
+static std::string EncodeText1(const T& msg) {
+  std::string data;
+  wpi::raw_string_ostream os{data};
+  net::WireEncodeText(os, msg);
+  return data;
+}
+
+template <typename T>
+static std::string EncodeText(const T& msgs) {
+  std::string data;
+  wpi::raw_string_ostream os{data};
+  bool first = true;
+  for (auto&& msg : msgs) {
+    if (first) {
+      os << '[';
+      first = false;
+    } else {
+      os << ',';
+    }
+    net::WireEncodeText(os, msg);
+  }
+  os << ']';
+  return data;
+}
+
+template <typename T>
+static std::vector<uint8_t> EncodeServerBinary1(const T& msg) {
+  std::vector<uint8_t> data;
+  wpi::raw_uvector_ostream os{data};
+  if constexpr (std::same_as<T, net::ServerMessage>) {
+    if (auto m = std::get_if<net::ServerValueMsg>(&msg.contents)) {
+      net::WireEncodeBinary(os, m->topic, m->value.time(), m->value);
+    }
+  } else if constexpr (std::same_as<T, net::ClientMessage>) {
+    if (auto m = std::get_if<net::ClientValueMsg>(&msg.contents)) {
+      net::WireEncodeBinary(os, Handle{m->pubHandle}.GetIndex(),
+                            m->value.time(), m->value);
+    }
+  }
+  return data;
+}
+
+template <typename T>
+static std::vector<uint8_t> EncodeServerBinary(const T& msgs) {
+  std::vector<uint8_t> data;
+  wpi::raw_uvector_ostream os{data};
+  for (auto&& msg : msgs) {
+    if constexpr (std::same_as<typename T::value_type, net::ServerMessage>) {
+      if (auto m = std::get_if<net::ServerValueMsg>(&msg.contents)) {
+        net::WireEncodeBinary(os, m->topic, m->value.time(), m->value);
+      }
+    } else if constexpr (std::same_as<typename T::value_type,
+                                      net::ClientMessage>) {
+      if (auto m = std::get_if<net::ClientValueMsg>(&msg.contents)) {
+        net::WireEncodeBinary(os, Handle{m->pubHandle}.GetIndex(),
+                              m->value.time(), m->value);
+      }
+    }
+  }
+  return data;
+}
+
+TEST_F(ServerImplTest, PublishLocal) {
+  // publish before client connect
+  server.SetLocal(&local);
+  NT_Publisher pubHandle = nt::Handle{0, 1, nt::Handle::kPublisher};
+  NT_Topic topicHandle = nt::Handle{0, 1, nt::Handle::kTopic};
+  NT_Publisher pubHandle2 = nt::Handle{0, 2, nt::Handle::kPublisher};
+  NT_Topic topicHandle2 = nt::Handle{0, 2, nt::Handle::kTopic};
+  NT_Publisher pubHandle3 = nt::Handle{0, 3, nt::Handle::kPublisher};
+  NT_Topic topicHandle3 = nt::Handle{0, 3, nt::Handle::kTopic};
+  {
+    ::testing::InSequence seq;
+    EXPECT_CALL(local, NetworkAnnounce(std::string_view{"test"},
+                                       std::string_view{"double"},
+                                       wpi::json::object(), pubHandle));
+    EXPECT_CALL(local, NetworkAnnounce(std::string_view{"test2"},
+                                       std::string_view{"double"},
+                                       wpi::json::object(), pubHandle2));
+    EXPECT_CALL(local, NetworkAnnounce(std::string_view{"test3"},
+                                       std::string_view{"double"},
+                                       wpi::json::object(), pubHandle3));
+  }
+
+  {
+    std::vector<net::ClientMessage> msgs;
+    msgs.emplace_back(net::ClientMessage{net::PublishMsg{
+        pubHandle, topicHandle, "test", "double", wpi::json::object(), {}}});
+    server.HandleLocal(msgs);
+  }
+
+  // client connect; it should get already-published topic as soon as it
+  // subscribes
+  ::testing::StrictMock<net::MockWireConnection> wire;
+  MockSetPeriodicFunc setPeriodic;
+  EXPECT_CALL(wire, GetVersion()).WillRepeatedly(Return(0x0401));
+  {
+    ::testing::InSequence seq;
+    // EXPECT_CALL(wire, Flush()).WillOnce(Return(0));     // AddClient()
+    EXPECT_CALL(setPeriodic, Call(100));  // ClientSubscribe()
+    // EXPECT_CALL(wire, Flush()).WillOnce(Return(0));     // ClientSubscribe()
+    EXPECT_CALL(wire, GetLastPingResponse()).WillOnce(Return(0));
+    EXPECT_CALL(wire, SendPing(100));
+    EXPECT_CALL(wire, Ready()).WillOnce(Return(true));  // SendControl()
+    EXPECT_CALL(
+        wire, DoWriteText(StrEq(EncodeText1(net::ServerMessage{net::AnnounceMsg{
+                  "test", 3, "double", std::nullopt, wpi::json::object()}}))))
+        .WillOnce(Return(0));
+    EXPECT_CALL(
+        wire, DoWriteText(StrEq(EncodeText1(net::ServerMessage{net::AnnounceMsg{
+                  "test2", 8, "double", std::nullopt, wpi::json::object()}}))))
+        .WillOnce(Return(0));
+    EXPECT_CALL(wire, Flush()).WillOnce(Return(0));     // SendControl()
+    EXPECT_CALL(wire, Ready()).WillOnce(Return(true));  // SendControl()
+    EXPECT_CALL(
+        wire, DoWriteText(StrEq(EncodeText1(net::ServerMessage{net::AnnounceMsg{
+                  "test3", 11, "double", std::nullopt, wpi::json::object()}}))))
+        .WillOnce(Return(0));
+    EXPECT_CALL(wire, Flush()).WillOnce(Return(0));  // SendControl()
+  }
+  auto [name, id] = server.AddClient("test", "connInfo", false, wire,
+                                     setPeriodic.AsStdFunction());
+
+  {
+    NT_Subscriber subHandle = nt::Handle{0, 1, nt::Handle::kSubscriber};
+    std::vector<net::ClientMessage> msgs;
+    msgs.emplace_back(net::ClientMessage{net::SubscribeMsg{
+        subHandle, {{""}}, PubSubOptions{.prefixMatch = true}}});
+    server.ProcessIncomingText(id, EncodeText(msgs));
+  }
+
+  // publish before send control
+  {
+    std::vector<net::ClientMessage> msgs;
+    msgs.emplace_back(net::ClientMessage{net::PublishMsg{
+        pubHandle2, topicHandle2, "test2", "double", wpi::json::object(), {}}});
+    server.HandleLocal(msgs);
+  }
+
+  server.SendAllOutgoing(100, false);
+
+  // publish after send control
+  {
+    std::vector<net::ClientMessage> msgs;
+    msgs.emplace_back(net::ClientMessage{net::PublishMsg{
+        pubHandle3, topicHandle3, "test3", "double", wpi::json::object(), {}}});
+    server.HandleLocal(msgs);
+  }
+
+  server.SendAllOutgoing(200, false);
+}
+
+TEST_F(ServerImplTest, ClientSubTopicOnlyThenValue) {
+  // publish before client connect
+  server.SetLocal(&local);
+  NT_Publisher pubHandle = nt::Handle{0, 1, nt::Handle::kPublisher};
+  NT_Topic topicHandle = nt::Handle{0, 1, nt::Handle::kTopic};
+  EXPECT_CALL(local, NetworkAnnounce(std::string_view{"test"},
+                                     std::string_view{"double"},
+                                     wpi::json::object(), pubHandle));
+
+  {
+    std::vector<net::ClientMessage> msgs;
+    msgs.emplace_back(net::ClientMessage{net::PublishMsg{
+        pubHandle, topicHandle, "test", "double", wpi::json::object(), {}}});
+    msgs.emplace_back(net::ClientMessage{
+        net::ClientValueMsg{pubHandle, Value::MakeDouble(1.0, 10)}});
+    server.HandleLocal(msgs);
+  }
+
+  ::testing::StrictMock<net::MockWireConnection> wire;
+  EXPECT_CALL(wire, GetVersion()).WillRepeatedly(Return(0x0401));
+  MockSetPeriodicFunc setPeriodic;
+  {
+    ::testing::InSequence seq;
+    // EXPECT_CALL(wire, Flush()).WillOnce(Return(0));     // AddClient()
+    EXPECT_CALL(setPeriodic, Call(100));  // ClientSubscribe()
+    // EXPECT_CALL(wire, Flush()).WillOnce(Return(0));     // ClientSubscribe()
+    EXPECT_CALL(wire, GetLastPingResponse()).WillOnce(Return(0));
+    EXPECT_CALL(wire, SendPing(100));
+    EXPECT_CALL(wire, Ready()).WillOnce(Return(true));  // SendValues()
+    EXPECT_CALL(
+        wire, DoWriteText(StrEq(EncodeText1(net::ServerMessage{net::AnnounceMsg{
+                  "test", 3, "double", std::nullopt, wpi::json::object()}}))))
+        .WillOnce(Return(0));
+    EXPECT_CALL(wire, Flush()).WillOnce(Return(0));  // SendValues()
+    EXPECT_CALL(setPeriodic, Call(100));             // ClientSubscribe()
+    // EXPECT_CALL(wire, Flush()).WillOnce(Return(0));     // ClientSubscribe()
+    EXPECT_CALL(wire, Ready()).WillOnce(Return(true));  // SendValues()
+    EXPECT_CALL(
+        wire, DoWriteBinary(wpi::SpanEq(EncodeServerBinary1(net::ServerMessage{
+                  net::ServerValueMsg{3, Value::MakeDouble(1.0, 10)}}))))
+        .WillOnce(Return(0));
+    EXPECT_CALL(wire, Flush());  // SendValues()
+  }
+
+  // connect client
+  auto [name, id] = server.AddClient("test", "connInfo", false, wire,
+                                     setPeriodic.AsStdFunction());
+
+  // subscribe topics only; will not send value
+  {
+    NT_Subscriber subHandle = nt::Handle{0, 1, nt::Handle::kSubscriber};
+    std::vector<net::ClientMessage> msgs;
+    msgs.emplace_back(net::ClientMessage{net::SubscribeMsg{
+        subHandle,
+        {{""}},
+        PubSubOptions{.topicsOnly = true, .prefixMatch = true}}});
+    server.ProcessIncomingText(id, EncodeText(msgs));
+  }
+
+  server.SendOutgoing(id, 100);
+
+  // subscribe normal; will not resend announcement, but will send value
+  {
+    NT_Subscriber subHandle = nt::Handle{0, 2, nt::Handle::kSubscriber};
+    std::vector<net::ClientMessage> msgs;
+    msgs.emplace_back(net::ClientMessage{
+        net::SubscribeMsg{subHandle, {{"test"}}, PubSubOptions{}}});
+    server.ProcessIncomingText(id, EncodeText(msgs));
+  }
+
+  server.SendOutgoing(id, 200);
+}
+
+TEST_F(ServerImplTest, ZeroTimestampNegativeTime) {
+  // publish before client connect
+  server.SetLocal(&local);
+  NT_Publisher pubHandle = nt::Handle{0, 1, nt::Handle::kPublisher};
+  NT_Topic topicHandle = nt::Handle{0, 1, nt::Handle::kTopic};
+  NT_Subscriber subHandle = nt::Handle{0, 1, nt::Handle::kSubscriber};
+  Value defaultValue = Value::MakeDouble(1.0, 10);
+  defaultValue.SetTime(0);
+  defaultValue.SetServerTime(0);
+  Value value = Value::MakeDouble(5, -10);
+  {
+    ::testing::InSequence seq;
+    EXPECT_CALL(local, NetworkAnnounce(std::string_view{"test"},
+                                       std::string_view{"double"},
+                                       wpi::json::object(), pubHandle))
+        .WillOnce(Return(topicHandle));
+    EXPECT_CALL(local, NetworkSetValue(topicHandle, defaultValue));
+    EXPECT_CALL(local, NetworkSetValue(topicHandle, value));
+  }
+
+  {
+    std::vector<net::ClientMessage> msgs;
+    msgs.emplace_back(net::ClientMessage{net::PublishMsg{
+        pubHandle, topicHandle, "test", "double", wpi::json::object(), {}}});
+    msgs.emplace_back(
+        net::ClientMessage{net::ClientValueMsg{pubHandle, defaultValue}});
+    msgs.emplace_back(
+        net::ClientMessage{net::SubscribeMsg{subHandle, {"test"}, {}}});
+    server.HandleLocal(msgs);
+  }
+
+  // client connect; it should get already-published topic as soon as it
+  // subscribes
+  ::testing::StrictMock<net::MockWireConnection> wire;
+  MockSetPeriodicFunc setPeriodic;
+  {
+    ::testing::InSequence seq;
+    // EXPECT_CALL(wire, Flush());  // AddClient()
+  }
+  auto [name, id] = server.AddClient("test", "connInfo", false, wire,
+                                     setPeriodic.AsStdFunction());
+
+  // publish and send non-default value with negative time offset
+  {
+    NT_Subscriber pubHandle2 = nt::Handle{0, 2, nt::Handle::kPublisher};
+    std::vector<net::ClientMessage> msgs;
+    msgs.emplace_back(net::ClientMessage{net::PublishMsg{
+        pubHandle2, topicHandle, "test", "double", wpi::json::object(), {}}});
+    server.ProcessIncomingText(id, EncodeText(msgs));
+    msgs.clear();
+    msgs.emplace_back(
+        net::ClientMessage{net::ClientValueMsg{pubHandle2, value}});
+    server.ProcessIncomingBinary(id, EncodeServerBinary(msgs));
+  }
+}
+
+}  // namespace nt
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp
index f893302..891d693 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp
@@ -2,6 +2,7 @@
 // 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 <gtest/gtest.h>
 #include <wpi/SmallString.h>
 #include <wpi/raw_ostream.h>
 
@@ -9,7 +10,6 @@
 #include "../TestPrinters.h"
 #include "Handle.h"
 #include "gmock/gmock.h"
-#include "gtest/gtest.h"
 #include "net/Message.h"
 #include "net/WireDecoder.h"
 #include "networktables/NetworkTableValue.h"
@@ -67,8 +67,8 @@
       logger,
       Call(_, _, _,
            "could not decode JSON message: [json.exception.parse_error.101] "
-           "parse error at 1: syntax error - "
-           "unexpected end of input; expected '[', '{', or a literal"sv));
+           "parse error at line 1, column 1: syntax error while parsing value "
+           "- unexpected end of input; expected '[', '{', or a literal"sv));
   net::WireDecodeText("", handler, logger);
 }
 
@@ -77,8 +77,8 @@
       logger,
       Call(_, _, _,
            "could not decode JSON message: [json.exception.parse_error.101] "
-           "parse error at 2: syntax error - "
-           "unexpected end of input; expected '[', '{', or a literal"sv));
+           "parse error at line 1, column 2: syntax error while parsing value "
+           "- unexpected end of input; expected '[', '{', or a literal"sv));
   net::WireDecodeText("[", handler, logger);
 }
 
@@ -87,8 +87,8 @@
       logger,
       Call(_, _, _,
            "could not decode JSON message: [json.exception.parse_error.101] "
-           "parse error at 3: syntax error - "
-           "unexpected end of input; expected string literal"sv));
+           "parse error at line 1, column 3: syntax error while parsing object "
+           "key - unexpected end of input; expected string literal"sv));
   net::WireDecodeText("[{", handler, logger);
 }
 
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp
index 9cc3187..222a5b7 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp
@@ -7,15 +7,15 @@
 #include <string_view>
 #include <vector>
 
+#include <gtest/gtest.h>
+#include <wpi/SpanMatcher.h>
 #include <wpi/json.h>
 #include <wpi/raw_ostream.h>
 
-#include "../SpanMatcher.h"
 #include "../TestPrinters.h"
 #include "Handle.h"
 #include "PubSubOptions.h"
 #include "gmock/gmock-matchers.h"
-#include "gtest/gtest.h"
 #include "net/Message.h"
 #include "net/WireEncoder.h"
 #include "networktables/NetworkTableValue.h"
@@ -172,14 +172,15 @@
   ASSERT_TRUE(net::WireEncodeText(os, msg));
   ASSERT_EQ(os.str(),
             "{\"method\":\"subscribe\",\"params\":{"
-            "\"options\":{},\"topics\":[\"a\",\"b\"],\"subuid\":5}}");
+            "\"options\":{},\"topics\":[\"a\",\"b\"],\"subuid\":402653189}}");
 }
 
 TEST_F(WireEncoderTextTest, MessageUnsubscribe) {
   net::ClientMessage msg{
       net::UnsubscribeMsg{Handle{0, 5, Handle::kSubscriber}}};
   ASSERT_TRUE(net::WireEncodeText(os, msg));
-  ASSERT_EQ(os.str(), "{\"method\":\"unsubscribe\",\"params\":{\"subuid\":5}}");
+  ASSERT_EQ(os.str(),
+            "{\"method\":\"unsubscribe\",\"params\":{\"subuid\":402653189}}");
 }
 
 TEST_F(WireEncoderTextTest, MessageAnnounce) {
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/net3/MockWireConnection3.h b/third_party/allwpilib/ntcore/src/test/native/cpp/net3/MockWireConnection3.h
index b7c785f..50fc80c 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/net3/MockWireConnection3.h
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/net3/MockWireConnection3.h
@@ -28,6 +28,8 @@
 
   MOCK_METHOD(void, Flush, (), (override));
 
+  MOCK_METHOD(uint64_t, GetLastFlushTime, (), (const, override));
+
   MOCK_METHOD(void, Disconnect, (std::string_view reason), (override));
 
  protected:
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/net3/WireDecoder3Test.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/net3/WireDecoder3Test.cpp
index 1cd6ecb..74f0d54 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/net3/WireDecoder3Test.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/net3/WireDecoder3Test.cpp
@@ -9,11 +9,12 @@
 #include <string>
 #include <string_view>
 
-#include "../SpanMatcher.h"
+#include <gtest/gtest.h>
+#include <wpi/SpanMatcher.h>
+
 #include "../TestPrinters.h"
 #include "../ValueMatcher.h"
 #include "gmock/gmock.h"
-#include "gtest/gtest.h"
 #include "net3/WireDecoder3.h"
 #include "networktables/NetworkTableValue.h"
 
diff --git a/third_party/allwpilib/ntcore/src/test/native/cpp/net3/WireEncoder3Test.cpp b/third_party/allwpilib/ntcore/src/test/native/cpp/net3/WireEncoder3Test.cpp
index bb4bfd2..de05934 100644
--- a/third_party/allwpilib/ntcore/src/test/native/cpp/net3/WireEncoder3Test.cpp
+++ b/third_party/allwpilib/ntcore/src/test/native/cpp/net3/WireEncoder3Test.cpp
@@ -8,11 +8,11 @@
 #include <string_view>
 #include <vector>
 
+#include <gtest/gtest.h>
+#include <wpi/SpanMatcher.h>
 #include <wpi/raw_ostream.h>
 
-#include "../SpanMatcher.h"
 #include "../TestPrinters.h"
-#include "gtest/gtest.h"
 #include "net3/Message3.h"
 #include "net3/WireEncoder3.h"
 #include "networktables/NetworkTableValue.h"
diff --git a/third_party/allwpilib/ntcoreffi/.styleguide b/third_party/allwpilib/ntcoreffi/.styleguide
new file mode 100644
index 0000000..ba13804
--- /dev/null
+++ b/third_party/allwpilib/ntcoreffi/.styleguide
@@ -0,0 +1,27 @@
+cppHeaderFileInclude {
+  \.h$
+}
+
+cppSrcFileInclude {
+  \.cpp$
+}
+
+modifiableFileExclude {
+  src/main/native/include/DataLogManager\.h$
+}
+
+repoRootNameOverride {
+  ntcoreffi
+}
+
+includeOtherLibs {
+  ^fmt/
+  ^gmock/
+  ^gtest/
+  ^networktables/
+  ^wpi/
+}
+
+includeGuardRoots {
+  ntcoreffi/src/main/native/include/
+}
diff --git a/third_party/allwpilib/ntcoreffi/build.gradle b/third_party/allwpilib/ntcoreffi/build.gradle
index 7a377a7..98ffeaa 100644
--- a/third_party/allwpilib/ntcoreffi/build.gradle
+++ b/third_party/allwpilib/ntcoreffi/build.gradle
@@ -1,5 +1,6 @@
 plugins {
     id 'c'
+    id 'cpp'
     id 'maven-publish'
 }
 
@@ -49,6 +50,16 @@
                         srcDir generatedHeaders
                     }
                 }
+                cpp {
+                    source {
+                        srcDirs = ['src/main/native/cpp']
+                        includes = ['**/*.cpp']
+                    }
+                    exportedHeaders {
+                        srcDir 'src/main/native/include'
+                        srcDir generatedHeaders
+                    }
+                }
             }
             binaries.all { binary ->
                 if (binary instanceof StaticLibraryBinarySpec) {
@@ -61,6 +72,9 @@
                 project(':ntcore').addNtcoreDependency(binary, 'static')
                 lib project: ':wpinet', library: 'wpinet', linkage: 'static'
                 lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    nativeUtils.useRequiredLibrary(binary, 'ni_link_libraries')
+                }
             }
         }
     }
diff --git a/third_party/allwpilib/ntcoreffi/src/main/native/cpp/DataLogManager.cpp b/third_party/allwpilib/ntcoreffi/src/main/native/cpp/DataLogManager.cpp
new file mode 100644
index 0000000..e1e49b7
--- /dev/null
+++ b/third_party/allwpilib/ntcoreffi/src/main/native/cpp/DataLogManager.cpp
@@ -0,0 +1,545 @@
+// 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 "DataLogManager.h"
+
+#include <algorithm>
+#include <ctime>
+#include <random>
+
+#include <fmt/chrono.h>
+#include <fmt/format.h>
+#include <networktables/NetworkTableInstance.h>
+#include <wpi/DataLog.h>
+#include <wpi/SafeThread.h>
+#include <wpi/StringExtras.h>
+#include <wpi/fs.h>
+#include <wpi/timestamp.h>
+
+#ifdef __FRC_ROBORIO__
+#include <FRC_NetworkCommunication/FRCComm.h>
+#include <FRC_NetworkCommunication/LoadOut.h>
+#endif
+
+using namespace wpi;
+
+/** Shims to keep the code as similar as possible to wpilibc */
+namespace {
+
+namespace warn {
+static constexpr int Warning = 16;
+}  // namespace warn
+
+namespace frc {
+void ReportErrorV(int32_t status, const char* fileName, int lineNumber,
+                  const char* funcName, fmt::string_view format,
+                  fmt::format_args args) {
+#ifdef __FRC_ROBORIO__
+  if (status == 0) {
+    return;
+  }
+  fmt::memory_buffer out;
+  fmt::format_to(fmt::appender{out}, "Warning: ");
+  fmt::vformat_to(fmt::appender{out}, format, args);
+  out.push_back('\0');
+  FRC_NetworkCommunication_sendError(status < 0, status, 0, out.data(),
+                                     "DataLogManager", "");
+#endif
+}
+
+template <typename... Args>
+inline void ReportError(int32_t status, const char* fileName, int lineNumber,
+                        const char* funcName, fmt::string_view format,
+                        Args&&... args) {
+  ReportErrorV(status, fileName, lineNumber, funcName, format,
+               fmt::make_format_args(args...));
+}
+}  // namespace frc
+
+#define FRC_ReportError(status, format, ...)                             \
+  do {                                                                   \
+    if ((status) != 0) {                                                 \
+      ::frc::ReportError(status, __FILE__, __LINE__, __FUNCTION__,       \
+                         FMT_STRING(format) __VA_OPT__(, ) __VA_ARGS__); \
+    }                                                                    \
+  } while (0)
+
+namespace RobotController {
+inline bool IsSystemTimeValid() {
+#ifdef __FRC_ROBORIO__
+  uint8_t timeWasSet = 0;
+  FRC_NetworkCommunication_getTimeWasSet(&timeWasSet);
+  return timeWasSet != 0;
+#else
+  return true;
+#endif
+}
+}  // namespace RobotController
+
+namespace filesystem {
+inline std::string GetOperatingDirectory() {
+#ifdef __FRC_ROBORIO__
+  return "/home/lvuser";
+#else
+  return fs::current_path().string();
+#endif
+}
+}  // namespace filesystem
+
+namespace DriverStation {
+#ifdef __FRC_ROBORIO__
+using MatchType = MatchType_t;
+constexpr int kNone = kMatchType_none;
+constexpr int kPractice = kMatchType_practice;
+constexpr int kQualification = kMatchType_qualification;
+constexpr int kElimination = kMatchType_elimination;
+char gEventName[128];
+MatchType_t gMatchType;
+uint16_t gMatchNumber;
+uint8_t gReplayNumber;
+uint8_t gGameSpecificMessage[16];
+uint16_t gGameSpecificMessageSize;
+#else
+enum MatchType { kNone, kPractice, kQualification, kElimination };
+#endif
+
+inline void UpdateMatchInfo() {
+#ifdef __FRC_ROBORIO__
+  gGameSpecificMessageSize = sizeof(gGameSpecificMessage);
+  FRC_NetworkCommunication_getMatchInfo(gEventName, &gMatchType, &gMatchNumber,
+                                        &gReplayNumber, gGameSpecificMessage,
+                                        &gGameSpecificMessageSize);
+#endif
+}
+
+inline MatchType GetMatchType() {
+#ifdef __FRC_ROBORIO__
+  return gMatchType;
+#else
+  return kNone;
+#endif
+}
+
+inline std::string_view GetEventName() {
+#ifdef __FRC_ROBORIO__
+  return gEventName;
+#else
+  return "";
+#endif
+}
+
+inline uint16_t GetMatchNumber() {
+#ifdef __FRC_ROBORIO__
+  return gMatchNumber;
+#else
+  return 0;
+#endif
+}
+
+inline bool IsDSAttached() {
+#ifdef __FRC_ROBORIO__
+  struct ControlWord_t cw;
+  FRC_NetworkCommunication_getControlWord(&cw);
+  return cw.dsAttached;
+#else
+  return true;
+#endif
+}
+
+inline bool IsFMSAttached() {
+#ifdef __FRC_ROBORIO__
+  struct ControlWord_t cw;
+  FRC_NetworkCommunication_getControlWord(&cw);
+  return cw.fmsAttached;
+#else
+  return false;
+#endif
+}
+
+WPI_EventHandle gNewDataEvent;
+
+inline void ProvideRefreshedDataEventHandle(WPI_EventHandle event) {
+  gNewDataEvent = event;
+}
+
+inline void RemoveRefreshedDataEventHandle(WPI_EventHandle event) {}
+
+}  // namespace DriverStation
+
+#ifdef __FRC_ROBORIO__
+static constexpr int kRoboRIO = 0;
+namespace RobotBase {
+inline int GetRuntimeType() {
+  nLoadOut::tTargetClass targetClass = nLoadOut::getTargetClass();
+  if (targetClass == nLoadOut::kTargetClass_RoboRIO2) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+}  // namespace RobotBase
+#endif
+
+struct Thread final : public wpi::SafeThread {
+  Thread(std::string_view dir, std::string_view filename, double period);
+  ~Thread() override;
+
+  void Main() final;
+
+  void StartNTLog();
+  void StopNTLog();
+
+  std::string m_logDir;
+  bool m_filenameOverride;
+  wpi::log::DataLog m_log;
+  bool m_ntLoggerEnabled = false;
+  NT_DataLogger m_ntEntryLogger = 0;
+  NT_ConnectionDataLogger m_ntConnLogger = 0;
+  wpi::log::StringLogEntry m_messageLog;
+};
+
+struct Instance {
+  Instance(std::string_view dir, std::string_view filename, double period);
+  wpi::SafeThreadOwner<Thread> owner;
+};
+
+}  // namespace
+
+// if less than this much free space, delete log files until there is this much
+// free space OR there are this many files remaining.
+static constexpr uintmax_t kFreeSpaceThreshold = 50000000;
+static constexpr int kFileCountThreshold = 10;
+
+static std::string MakeLogDir(std::string_view dir) {
+  if (!dir.empty()) {
+    return std::string{dir};
+  }
+#ifdef __FRC_ROBORIO__
+  // prefer a mounted USB drive if one is accessible
+  constexpr std::string_view usbDir{"/u"};
+  std::error_code ec;
+  auto s = fs::status(usbDir, ec);
+  if (!ec && fs::is_directory(s) &&
+      (s.permissions() & fs::perms::others_write) != fs::perms::none) {
+    return std::string{usbDir};
+  }
+  if (RobotBase::GetRuntimeType() == kRoboRIO) {
+    FRC_ReportError(warn::Warning,
+                    "DataLogManager: Logging to RoboRIO 1 internal storage is "
+                    "not recommended! Plug in a FAT32 formatted flash drive!");
+  }
+#endif
+  return filesystem::GetOperatingDirectory();
+}
+
+static std::string MakeLogFilename(std::string_view filenameOverride) {
+  if (!filenameOverride.empty()) {
+    return std::string{filenameOverride};
+  }
+  static std::random_device dev;
+  static std::mt19937 rng(dev());
+  std::uniform_int_distribution<int> dist(0, 15);
+  const char* v = "0123456789abcdef";
+  std::string filename = "FRC_TBD_";
+  for (int i = 0; i < 16; i++) {
+    filename += v[dist(rng)];
+  }
+  filename += ".wpilog";
+  return filename;
+}
+
+Thread::Thread(std::string_view dir, std::string_view filename, double period)
+    : m_logDir{dir},
+      m_filenameOverride{!filename.empty()},
+      m_log{dir, MakeLogFilename(filename), period},
+      m_messageLog{m_log, "messages"} {
+  StartNTLog();
+}
+
+Thread::~Thread() {
+  StopNTLog();
+}
+
+void Thread::Main() {
+  // based on free disk space, scan for "old" FRC_*.wpilog files and remove
+  {
+    std::error_code ec;
+    uintmax_t freeSpace;
+    auto freeSpaceInfo = fs::space(m_logDir, ec);
+    if (!ec) {
+      freeSpace = freeSpaceInfo.available;
+    } else {
+      freeSpace = UINTMAX_MAX;
+    }
+    if (freeSpace < kFreeSpaceThreshold) {
+      // Delete oldest FRC_*.wpilog files (ignore FRC_TBD_*.wpilog as we just
+      // created one)
+      std::vector<fs::directory_entry> entries;
+      for (auto&& entry : fs::directory_iterator{m_logDir, ec}) {
+        auto stem = entry.path().stem().string();
+        if (wpi::starts_with(stem, "FRC_") &&
+            entry.path().extension() == ".wpilog" &&
+            !wpi::starts_with(stem, "FRC_TBD_")) {
+          entries.emplace_back(entry);
+        }
+      }
+      std::sort(entries.begin(), entries.end(),
+                [](const auto& a, const auto& b) {
+                  return a.last_write_time() < b.last_write_time();
+                });
+
+      int count = entries.size();
+      for (auto&& entry : entries) {
+        --count;
+        if (count < kFileCountThreshold) {
+          break;
+        }
+        auto size = entry.file_size();
+        if (fs::remove(entry.path(), ec)) {
+          FRC_ReportError(warn::Warning, "DataLogManager: Deleted {}",
+                          entry.path().string());
+          freeSpace += size;
+          if (freeSpace >= kFreeSpaceThreshold) {
+            break;
+          }
+        } else {
+          fmt::print(stderr, "DataLogManager: could not delete {}\n",
+                     entry.path().string());
+        }
+      }
+    } else if (freeSpace < 2 * kFreeSpaceThreshold) {
+      FRC_ReportError(
+          warn::Warning,
+          "DataLogManager: Log storage device has {} MB of free space "
+          "remaining! Logs will get deleted below {} MB of free space. "
+          "Consider deleting logs off the storage device.",
+          freeSpace / 1000000, kFreeSpaceThreshold / 1000000);
+    }
+  }
+
+  int timeoutCount = 0;
+  bool paused = false;
+  int dsAttachCount = 0;
+  int fmsAttachCount = 0;
+  bool dsRenamed = m_filenameOverride;
+  bool fmsRenamed = m_filenameOverride;
+  int sysTimeCount = 0;
+  wpi::log::IntegerLogEntry sysTimeEntry{
+      m_log, "systemTime",
+      "{\"source\":\"DataLogManager\",\"format\":\"time_t_us\"}"};
+
+  wpi::Event newDataEvent;
+  DriverStation::ProvideRefreshedDataEventHandle(newDataEvent.GetHandle());
+
+  for (;;) {
+    bool timedOut = false;
+    bool newData =
+        wpi::WaitForObject(newDataEvent.GetHandle(), 0.25, &timedOut);
+    if (!m_active) {
+      break;
+    }
+    if (!newData) {
+      ++timeoutCount;
+      // pause logging after being disconnected for 10 seconds
+      if (timeoutCount > 40 && !paused) {
+        timeoutCount = 0;
+        paused = true;
+        m_log.Pause();
+      }
+      continue;
+    }
+    // when we connect to the DS, resume logging
+    timeoutCount = 0;
+    if (paused) {
+      paused = false;
+      m_log.Resume();
+    }
+
+    if (!dsRenamed) {
+      // track DS attach
+      if (DriverStation::IsDSAttached()) {
+        ++dsAttachCount;
+      } else {
+        dsAttachCount = 0;
+      }
+      if (dsAttachCount > 50) {  // 1 second
+        if (RobotController::IsSystemTimeValid()) {
+          std::time_t now = std::time(nullptr);
+          auto tm = std::gmtime(&now);
+          m_log.SetFilename(fmt::format("FRC_{:%Y%m%d_%H%M%S}.wpilog", *tm));
+          dsRenamed = true;
+        } else {
+          dsAttachCount = 0;  // wait a bit and try again
+        }
+      }
+    }
+
+    if (!fmsRenamed) {
+      // track FMS attach
+      if (DriverStation::IsFMSAttached()) {
+        ++fmsAttachCount;
+      } else {
+        fmsAttachCount = 0;
+      }
+      if (fmsAttachCount > 250) {  // 5 seconds
+        // match info comes through TCP, so we need to double-check we've
+        // actually received it
+        DriverStation::UpdateMatchInfo();
+        auto matchType = DriverStation::GetMatchType();
+        if (matchType != DriverStation::kNone) {
+          // rename per match info
+          char matchTypeChar;
+          switch (matchType) {
+            case DriverStation::kPractice:
+              matchTypeChar = 'P';
+              break;
+            case DriverStation::kQualification:
+              matchTypeChar = 'Q';
+              break;
+            case DriverStation::kElimination:
+              matchTypeChar = 'E';
+              break;
+            default:
+              matchTypeChar = '_';
+              break;
+          }
+          std::time_t now = std::time(nullptr);
+          m_log.SetFilename(
+              fmt::format("FRC_{:%Y%m%d_%H%M%S}_{}_{}{}.wpilog",
+                          *std::gmtime(&now), DriverStation::GetEventName(),
+                          matchTypeChar, DriverStation::GetMatchNumber()));
+          fmsRenamed = true;
+          dsRenamed = true;  // don't override FMS rename
+        }
+      }
+    }
+
+    // Write system time every ~5 seconds
+    ++sysTimeCount;
+    if (sysTimeCount >= 250) {
+      sysTimeCount = 0;
+      if (RobotController::IsSystemTimeValid()) {
+        sysTimeEntry.Append(wpi::GetSystemTime(), wpi::Now());
+      }
+    }
+  }
+  DriverStation::RemoveRefreshedDataEventHandle(newDataEvent.GetHandle());
+}
+
+void Thread::StartNTLog() {
+  if (!m_ntLoggerEnabled) {
+    m_ntLoggerEnabled = true;
+    auto inst = nt::NetworkTableInstance::GetDefault();
+    m_ntEntryLogger = inst.StartEntryDataLog(m_log, "", "NT:");
+    m_ntConnLogger = inst.StartConnectionDataLog(m_log, "NTConnection");
+  }
+}
+
+void Thread::StopNTLog() {
+  if (m_ntLoggerEnabled) {
+    m_ntLoggerEnabled = false;
+    nt::NetworkTableInstance::StopEntryDataLog(m_ntEntryLogger);
+    nt::NetworkTableInstance::StopConnectionDataLog(m_ntConnLogger);
+  }
+}
+
+Instance::Instance(std::string_view dir, std::string_view filename,
+                   double period) {
+  // Delete all previously existing FRC_TBD_*.wpilog files. These only exist
+  // when the robot never connects to the DS, so they are very unlikely to
+  // have useful data and just clutter the filesystem.
+  auto logDir = MakeLogDir(dir);
+  std::error_code ec;
+  for (auto&& entry : fs::directory_iterator{logDir, ec}) {
+    if (wpi::starts_with(entry.path().stem().string(), "FRC_TBD_") &&
+        entry.path().extension() == ".wpilog") {
+      if (!fs::remove(entry, ec)) {
+        fmt::print(stderr, "DataLogManager: could not delete {}\n",
+                   entry.path().string());
+      }
+    }
+  }
+
+  owner.Start(logDir, filename, period);
+}
+
+static Instance& GetInstance(std::string_view dir = "",
+                             std::string_view filename = "",
+                             double period = 0.25) {
+  static Instance instance(dir, filename, period);
+  if (!instance.owner) {
+    instance.owner.Start(MakeLogDir(dir), filename, period);
+  }
+  return instance;
+}
+
+void DataLogManager::Start(std::string_view dir, std::string_view filename,
+                           double period) {
+  GetInstance(dir, filename, period);
+}
+
+void DataLogManager::Stop() {
+  auto& inst = GetInstance();
+  inst.owner.GetThread()->m_log.Stop();
+  inst.owner.Stop();
+}
+
+void DataLogManager::Log(std::string_view message) {
+  GetInstance().owner.GetThread()->m_messageLog.Append(message);
+  fmt::print("{}\n", message);
+}
+
+wpi::log::DataLog& DataLogManager::GetLog() {
+  return GetInstance().owner.GetThread()->m_log;
+}
+
+std::string_view DataLogManager::GetLogDir() {
+  return GetInstance().owner.GetThread()->m_logDir;
+}
+
+void DataLogManager::LogNetworkTables(bool enabled) {
+  if (auto thr = GetInstance().owner.GetThread()) {
+    if (enabled) {
+      thr->StartNTLog();
+    } else if (!enabled) {
+      thr->StopNTLog();
+    }
+  }
+}
+
+void DataLogManager::SignalNewDSDataOccur() {
+  wpi::SetSignalObject(DriverStation::gNewDataEvent);
+}
+
+extern "C" {
+
+void DLM_Start(const char* dir, const char* filename, double period) {
+  DataLogManager::Start(dir, filename, period);
+}
+
+void DLM_Stop(void) {
+  DataLogManager::Stop();
+}
+
+void DLM_Log(const char* message) {
+  DataLogManager::Log(message);
+}
+
+WPI_DataLog* DLM_GetLog(void) {
+  return reinterpret_cast<WPI_DataLog*>(&DataLogManager::GetLog());
+}
+
+const char* DLM_GetLogDir(void) {
+  return DataLogManager::GetLogDir().data();
+}
+
+void DLM_LogNetworkTables(int enabled) {
+  DataLogManager::LogNetworkTables(enabled);
+}
+
+void DLM_SignalNewDSDataOccur(void) {
+  DataLogManager::SignalNewDSDataOccur();
+}
+
+}  // extern "C"
diff --git a/third_party/allwpilib/ntcoreffi/src/main/native/include/DataLogManager.h b/third_party/allwpilib/ntcoreffi/src/main/native/include/DataLogManager.h
new file mode 100644
index 0000000..d2635c1
--- /dev/null
+++ b/third_party/allwpilib/ntcoreffi/src/main/native/include/DataLogManager.h
@@ -0,0 +1,162 @@
+// 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
+
+#ifdef __cplusplus
+#include <string>
+#include <string_view>
+#endif  // __cplusplus
+
+#ifdef __cplusplus
+namespace wpi::log {
+class DataLog;
+}  // namespace wpi::log
+
+namespace wpi {
+
+/**
+ * Centralized data log that provides automatic data log file management. It
+ * automatically cleans up old files when disk space is low and renames the file
+ * based either on current date/time or (if available) competition match number.
+ * The deta file will be saved to a USB flash drive if one is attached, or to
+ * /home/lvuser otherwise.
+ *
+ * Log files are initially named "FRC_TBD_{random}.wpilog" until the DS
+ * connects. After the DS connects, the log file is renamed to
+ * "FRC_yyyyMMdd_HHmmss.wpilog" (where the date/time is UTC). If the FMS is
+ * connected and provides a match number, the log file is renamed to
+ * "FRC_yyyyMMdd_HHmmss_{event}_{match}.wpilog".
+ *
+ * On startup, all existing FRC_TBD log files are deleted. If there is less than
+ * 50 MB of free space on the target storage, FRC_ log files are deleted (oldest
+ * to newest) until there is 50 MB free OR there are 10 files remaining.
+ *
+ * By default, all NetworkTables value changes are stored to the data log.
+ */
+class DataLogManager final {
+ public:
+  DataLogManager() = delete;
+
+  /**
+   * Start data log manager. The parameters have no effect if the data log
+   * manager was already started (e.g. by calling another static function).
+   *
+   * @param dir if not empty, directory to use for data log storage
+   * @param filename filename to use; if none provided, the filename is
+   *     automatically generated
+   * @param period time between automatic flushes to disk, in seconds;
+   *               this is a time/storage tradeoff
+   */
+  static void Start(std::string_view dir = "", std::string_view filename = "",
+                    double period = 0.25);
+
+  /**
+   * Stop data log manager.
+   */
+  static void Stop();
+
+  /**
+   * Log a message to the "messages" entry. The message is also printed to
+   * standard output (followed by a newline).
+   *
+   * @param message message
+   */
+  static void Log(std::string_view message);
+
+  /**
+   * Get the managed data log (for custom logging). Starts the data log manager
+   * if not already started.
+   *
+   * @return data log
+   */
+  static wpi::log::DataLog& GetLog();
+
+  /**
+   * Get the log directory.
+   *
+   * @return log directory
+   */
+  static std::string_view GetLogDir();
+
+  /**
+   * Enable or disable logging of NetworkTables data. Note that unlike the
+   * network interface for NetworkTables, this will capture every value change.
+   * Defaults to enabled.
+   *
+   * @param enabled true to enable, false to disable
+   */
+  static void LogNetworkTables(bool enabled);
+
+  /**
+   * Signal new DS data is available.
+   */
+  static void SignalNewDSDataOccur();
+};
+
+}  // namespace wpi
+
+extern "C" {
+#endif  // __cplusplus
+
+/** C-compatible data log (opaque struct). */
+struct WPI_DataLog;
+
+/**
+ * Start data log manager. The parameters have no effect if the data log
+ * manager was already started (e.g. by calling another static function).
+ *
+ * @param dir if not empty, directory to use for data log storage
+ * @param filename filename to use; if none provided, the filename is
+ *     automatically generated
+ * @param period time between automatic flushes to disk, in seconds;
+ *               this is a time/storage tradeoff
+ */
+void DLM_Start(const char* dir, const char* filename, double period);
+
+/**
+ * Stop data log manager.
+ */
+void DLM_Stop(void);
+
+/**
+ * Log a message to the "messages" entry. The message is also printed to
+ * standard output (followed by a newline).
+ *
+ * @param message message
+ */
+void DLM_Log(const char* message);
+
+/**
+ * Get the managed data log (for custom logging). Starts the data log manager
+ * if not already started.
+ *
+ * @return data log
+ */
+WPI_DataLog* DLM_GetLog(void);
+
+/**
+ * Get the log directory.
+ *
+ * @return log directory
+ */
+const char* DLM_GetLogDir(void);
+
+/**
+ * Enable or disable logging of NetworkTables data. Note that unlike the
+ * network interface for NetworkTables, this will capture every value change.
+ * Defaults to enabled.
+ *
+ * @param enabled true to enable, false to disable
+ */
+void DLM_LogNetworkTables(int enabled);
+
+/**
+ * Signal new DS data is available.
+ */
+void DLM_SignalNewDSDataOccur(void);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
diff --git a/third_party/allwpilib/ntcoreffi/src/main/native/symbols.txt b/third_party/allwpilib/ntcoreffi/src/main/native/symbols.txt
index 4f6ebeb..c18014b 100644
--- a/third_party/allwpilib/ntcoreffi/src/main/native/symbols.txt
+++ b/third_party/allwpilib/ntcoreffi/src/main/native/symbols.txt
@@ -1,3 +1,10 @@
+DLM_GetLog
+DLM_GetLogDir
+DLM_Log
+DLM_LogNetworkTables
+DLM_SignalNewDSDataOccur
+DLM_Start
+DLM_Stop
 NT_AddListener
 NT_AddListenerMultiple
 NT_AddListenerSingle
@@ -199,11 +206,15 @@
 NT_SetTopicRetained
 NT_StartClient3
 NT_StartClient4
+NT_StartConnectionDataLog
 NT_StartDSClient
+NT_StartEntryDataLog
 NT_StartLocal
 NT_StartServer
 NT_StopClient
+NT_StopConnectionDataLog
 NT_StopDSClient
+NT_StopEntryDataLog
 NT_StopLocal
 NT_StopServer
 NT_Subscribe
@@ -213,10 +224,35 @@
 WPI_CreateEvent
 WPI_CreateSemaphore
 WPI_CreateSignalObject
+WPI_DataLog_AppendBoolean
+WPI_DataLog_AppendBooleanArray
+WPI_DataLog_AppendBooleanArrayByte
+WPI_DataLog_AppendDouble
+WPI_DataLog_AppendDoubleArray
+WPI_DataLog_AppendFloat
+WPI_DataLog_AppendFloatArray
+WPI_DataLog_AppendInteger
+WPI_DataLog_AppendIntegerArray
+WPI_DataLog_AppendRaw
+WPI_DataLog_AppendString
+WPI_DataLog_AppendStringArray
+WPI_DataLog_Create
+WPI_DataLog_Create_Func
+WPI_DataLog_Finish
+WPI_DataLog_Flush
+WPI_DataLog_Pause
+WPI_DataLog_Release
+WPI_DataLog_Resume
+WPI_DataLog_SetFilename
+WPI_DataLog_SetMetadata
+WPI_DataLog_Start
+WPI_DataLog_Stop
 WPI_DestroyEvent
 WPI_DestroySemaphore
 WPI_DestroySignalObject
 WPI_GetSystemTime
+WPI_Impl_SetupNowRio
+WPI_Impl_ShutdownNowRio
 WPI_Now
 WPI_NowDefault
 WPI_ReleaseSemaphore
diff --git a/third_party/allwpilib/outlineviewer/.styleguide b/third_party/allwpilib/outlineviewer/.styleguide
index d4917bf..cdbb90b 100644
--- a/third_party/allwpilib/outlineviewer/.styleguide
+++ b/third_party/allwpilib/outlineviewer/.styleguide
@@ -21,8 +21,15 @@
 includeOtherLibs {
   ^GLFW
   ^fmt/
+  ^frc/
+  ^glass/
+  ^gtest/
   ^imgui
+  ^implot\.h$
+  ^networktables/
+  ^portable-file-dialogs\.h$
   ^ntcore
+  ^units/
   ^wpi/
   ^wpigui
 }
diff --git a/third_party/allwpilib/outlineviewer/CMakeLists.txt b/third_party/allwpilib/outlineviewer/CMakeLists.txt
index 537134d..a92498e 100644
--- a/third_party/allwpilib/outlineviewer/CMakeLists.txt
+++ b/third_party/allwpilib/outlineviewer/CMakeLists.txt
@@ -5,7 +5,7 @@
 include(LinkMacOSGUI)
 
 configure_file(src/main/generate/WPILibVersion.cpp.in WPILibVersion.cpp)
-GENERATE_RESOURCES(src/main/native/resources generated/main/cpp OV ov outlineviewer_resources_src)
+generate_resources(src/main/native/resources generated/main/cpp OV ov outlineviewer_resources_src)
 
 file(GLOB outlineviewer_src src/main/native/cpp/*.cpp ${CMAKE_CURRENT_BINARY_DIR}/WPILibVersion.cpp)
 
@@ -19,6 +19,7 @@
 
 add_executable(outlineviewer ${outlineviewer_src} ${outlineviewer_resources_src} ${outlineviewer_rc} ${APP_ICON_MACOSX})
 wpilib_link_macos_gui(outlineviewer)
+wpilib_target_warnings(outlineviewer)
 target_link_libraries(outlineviewer libglassnt libglass)
 
 if (WIN32)
diff --git a/third_party/allwpilib/outlineviewer/build.gradle b/third_party/allwpilib/outlineviewer/build.gradle
index bb4a4ca..94324a6 100644
--- a/third_party/allwpilib/outlineviewer/build.gradle
+++ b/third_party/allwpilib/outlineviewer/build.gradle
@@ -1,125 +1,123 @@
 import org.gradle.internal.os.OperatingSystem
 
-if (!project.hasProperty('onlylinuxathena')) {
-
-    description = "NetworkTables Viewer"
-
-    apply plugin: 'cpp'
-    apply plugin: 'c'
-    apply plugin: 'google-test-test-suite'
-    apply plugin: 'visual-studio'
-    apply plugin: 'edu.wpi.first.NativeUtils'
-
-    if (OperatingSystem.current().isWindows()) {
-        apply plugin: 'windows-resources'
-    }
-
-    ext {
-        nativeName = 'outlineviewer'
-    }
-
-    apply from: "${rootDir}/shared/resources.gradle"
-    apply from: "${rootDir}/shared/config.gradle"
-
-    def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in")
-    def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp")
-
-    apply from: "${rootDir}/shared/imgui.gradle"
-
-    task generateCppVersion() {
-        description = 'Generates the wpilib version class'
-        group = 'WPILib'
-
-        outputs.file wpilibVersionFileOutput
-        inputs.file wpilibVersionFileInput
-
-        if (wpilibVersioning.releaseMode) {
-            outputs.upToDateWhen { false }
-        }
-
-        // We follow a simple set of checks to determine whether we should generate a new version file:
-        // 1. If the release type is not development, we generate a new version file
-        // 2. If there is no generated version number, we generate a new version file
-        // 3. If there is a generated build number, and the release type is development, then we will
-        //    only generate if the publish task is run.
-        doLast {
-            def version = wpilibVersioning.version.get()
-            println "Writing version ${version} to $wpilibVersionFileOutput"
-
-            if (wpilibVersionFileOutput.exists()) {
-                wpilibVersionFileOutput.delete()
-            }
-            def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version)
-            wpilibVersionFileOutput.write(read)
-        }
-    }
-
-    gradle.taskGraph.addTaskExecutionGraphListener { graph ->
-        def willPublish = graph.hasTask(publish)
-        if (willPublish) {
-            generateCppVersion.outputs.upToDateWhen { false }
-        }
-    }
-
-    def generateTask = createGenerateResourcesTask('main', 'OV', 'ov', project)
-
-    project(':').libraryBuild.dependsOn build
-    tasks.withType(CppCompile) {
-        dependsOn generateTask
-        dependsOn generateCppVersion
-    }
-
-    model {
-        components {
-            // By default, a development executable will be generated. This is to help the case of
-            // testing specific functionality of the library.
-            "${nativeName}"(NativeExecutableSpec) {
-                baseName = 'outlineviewer'
-                sources {
-                    cpp {
-                        source {
-                            srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp"
-                            include '**/*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/main/native/include'
-                        }
-                    }
-                    if (OperatingSystem.current().isWindows()) {
-                        rc {
-                            source {
-                                srcDirs 'src/main/native/win'
-                                include '*.rc'
-                            }
-                        }
-                    }
-                }
-                binaries.all {
-                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
-                        it.buildable = false
-                        return
-                    }
-                    lib project: ':glass', library: 'glassnt', linkage: 'static'
-                    lib project: ':glass', library: 'glass', linkage: 'static'
-                    project(':ntcore').addNtcoreDependency(it, 'static')
-                    lib project: ':wpinet', library: 'wpinet', linkage: 'static'
-                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
-                    lib project: ':wpigui', library: 'wpigui', linkage: 'static'
-                    nativeUtils.useRequiredLibrary(it, 'imgui')
-                    if (it.targetPlatform.operatingSystem.isWindows()) {
-                        it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
-                    } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
-                        it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
-                    } else {
-                        it.linker.args << '-lX11'
-                        if (it.targetPlatform.name.startsWith('linuxarm')) {
-                            it.linker.args << '-lGL'
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    apply from: 'publish.gradle'
+if (project.hasProperty('onlylinuxathena')) {
+    return;
 }
+
+description = "NetworkTables Viewer"
+
+apply plugin: 'cpp'
+apply plugin: 'visual-studio'
+apply plugin: 'edu.wpi.first.NativeUtils'
+
+if (OperatingSystem.current().isWindows()) {
+    apply plugin: 'windows-resources'
+}
+
+ext {
+    nativeName = 'outlineviewer'
+}
+
+apply from: "${rootDir}/shared/resources.gradle"
+apply from: "${rootDir}/shared/config.gradle"
+
+def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in")
+def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp")
+
+apply from: "${rootDir}/shared/imgui.gradle"
+
+task generateCppVersion() {
+    description = 'Generates the wpilib version class'
+    group = 'WPILib'
+
+    outputs.file wpilibVersionFileOutput
+    inputs.file wpilibVersionFileInput
+
+    if (wpilibVersioning.releaseMode) {
+        outputs.upToDateWhen { false }
+    }
+
+    // We follow a simple set of checks to determine whether we should generate a new version file:
+    // 1. If the release type is not development, we generate a new version file
+    // 2. If there is no generated version number, we generate a new version file
+    // 3. If there is a generated build number, and the release type is development, then we will
+    //    only generate if the publish task is run.
+    doLast {
+        def version = wpilibVersioning.version.get()
+        println "Writing version ${version} to $wpilibVersionFileOutput"
+
+        if (wpilibVersionFileOutput.exists()) {
+            wpilibVersionFileOutput.delete()
+        }
+        def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version)
+        wpilibVersionFileOutput.write(read)
+    }
+}
+
+gradle.taskGraph.addTaskExecutionGraphListener { graph ->
+    def willPublish = graph.hasTask(publish)
+    if (willPublish) {
+        generateCppVersion.outputs.upToDateWhen { false }
+    }
+}
+
+def generateTask = createGenerateResourcesTask('main', 'OV', 'ov', project)
+
+project(':').libraryBuild.dependsOn build
+tasks.withType(CppCompile) {
+    dependsOn generateTask
+    dependsOn generateCppVersion
+}
+
+model {
+    components {
+        // By default, a development executable will be generated. This is to help the case of
+        // testing specific functionality of the library.
+        "${nativeName}"(NativeExecutableSpec) {
+            baseName = 'outlineviewer'
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp"
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/main/native/include'
+                    }
+                }
+                if (OperatingSystem.current().isWindows()) {
+                    rc.source {
+                        srcDirs 'src/main/native/win'
+                        include '*.rc'
+                    }
+                }
+            }
+            binaries.all {
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    it.buildable = false
+                    return
+                }
+                lib project: ':glass', library: 'glassnt', linkage: 'static'
+                lib project: ':glass', library: 'glass', linkage: 'static'
+                project(':ntcore').addNtcoreDependency(it, 'static')
+                lib project: ':wpinet', library: 'wpinet', linkage: 'static'
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                lib project: ':wpigui', library: 'wpigui', linkage: 'static'
+                lib project: ':fieldImages', library: 'fieldImages', linkage: 'static'
+                nativeUtils.useRequiredLibrary(it, 'imgui')
+                if (it.targetPlatform.operatingSystem.isWindows()) {
+                    it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
+                } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
+                    it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
+                } else {
+                    it.linker.args << '-lX11'
+                    if (it.targetPlatform.name.startsWith('linuxarm')) {
+                        it.linker.args << '-lGL'
+                    }
+                }
+            }
+        }
+    }
+}
+
+apply from: 'publish.gradle'
diff --git a/third_party/allwpilib/outlineviewer/publish.gradle b/third_party/allwpilib/outlineviewer/publish.gradle
index 9c53ae5..58222be 100644
--- a/third_party/allwpilib/outlineviewer/publish.gradle
+++ b/third_party/allwpilib/outlineviewer/publish.gradle
@@ -34,8 +34,16 @@
                             description("Creates a macOS application bundle for OutlineViewer")
                             from(file("$project.projectDir/Info.plist"))
                             into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/OutlineViewer.app/Contents"))
-                            into("MacOS") { with copySpec { from binary.executable.file } }
-                            into("Resources") { with copySpec { from icon } }
+                            into("MacOS") {
+                                with copySpec {
+                                    from binary.executable.file
+                                }
+                            }
+                            into("Resources") {
+                                with copySpec {
+                                    from icon
+                                }
+                            }
 
                             inputs.property "HasDeveloperId", project.hasProperty("developerID")
 
@@ -71,7 +79,7 @@
 
                             archiveBaseName = '_M_' + zipBaseName
                             duplicatesStrategy = 'exclude'
-                            classifier = nativeUtils.getPublishClassifier(binary)
+                            archiveClassifier = nativeUtils.getPublishClassifier(binary)
 
                             from(licenseFile) {
                                 into '/'
diff --git a/third_party/allwpilib/outlineviewer/src/main/native/cpp/main.cpp b/third_party/allwpilib/outlineviewer/src/main/native/cpp/main.cpp
index 20f4dcc..0310756 100644
--- a/third_party/allwpilib/outlineviewer/src/main/native/cpp/main.cpp
+++ b/third_party/allwpilib/outlineviewer/src/main/native/cpp/main.cpp
@@ -6,17 +6,17 @@
 
 #include <GLFW/glfw3.h>
 #include <fmt/format.h>
+#include <glass/Context.h>
+#include <glass/MainMenuBar.h>
+#include <glass/Model.h>
+#include <glass/Storage.h>
+#include <glass/networktables/NetworkTables.h>
+#include <glass/networktables/NetworkTablesSettings.h>
+#include <glass/other/Log.h>
 #include <imgui.h>
 #include <ntcore_cpp.h>
 #include <wpigui.h>
-
-#include "glass/Context.h"
-#include "glass/MainMenuBar.h"
-#include "glass/Model.h"
-#include "glass/Storage.h"
-#include "glass/networktables/NetworkTables.h"
-#include "glass/networktables/NetworkTablesSettings.h"
-#include "glass/other/Log.h"
+#include <wpigui_openurl.h>
 
 namespace gui = wpi::gui;
 
@@ -37,34 +37,47 @@
 static glass::LogData gLog;
 static glass::NetworkTablesFlagsSettings gFlagsSettings;
 static glass::MainMenuBar gMainMenu;
+static unsigned int gPrevMode = NT_NET_MODE_NONE;
+
+/**
+ * Generates the proper title bar title based on current instance state and
+ * event.
+ */
+static std::string MakeTitle(NT_Inst inst, nt::Event event) {
+  auto mode = nt::GetNetworkMode(inst);
+  if (mode & NT_NET_MODE_SERVER) {
+    auto numClients = nt::GetConnections(inst).size();
+    return fmt::format("OutlineViewer - {} Client{} Connected", numClients,
+                       (numClients == 1 ? "" : "s"));
+  } else if (mode & NT_NET_MODE_CLIENT3 || mode & NT_NET_MODE_CLIENT4) {
+    if (event.Is(NT_EVENT_CONNECTED)) {
+      return fmt::format("OutlineViewer - Connected ({})",
+                         event.GetConnectionInfo()->remote_ip);
+    }
+  }
+  return "OutlineViewer - DISCONNECTED";
+}
 
 static void NtInitialize() {
   auto inst = nt::GetDefaultInstance();
   auto poller = nt::CreateListenerPoller(inst);
-  nt::AddPolledListener(
-      poller, inst,
-      NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE | NT_EVENT_LOGMESSAGE);
+  nt::AddPolledListener(poller, inst, NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE);
+  nt::AddPolledLogger(poller, 0, 100);
   gui::AddEarlyExecute([inst, poller] {
     auto win = gui::GetSystemWindow();
     if (!win) {
       return;
     }
+    bool updateTitle = false;
+    nt::Event connectionEvent;
+    if (nt::GetNetworkMode(inst) != gPrevMode) {
+      gPrevMode = nt::GetNetworkMode(inst);
+      updateTitle = true;
+    }
     for (auto&& event : nt::ReadListenerQueue(poller)) {
-      if (auto connInfo = event.GetConnectionInfo()) {
-        // update window title when connection status changes
-        if ((nt::GetNetworkMode(inst) & NT_NET_MODE_SERVER) != 0) {
-          // for server mode, just print number of clients connected
-          glfwSetWindowTitle(win,
-                             fmt::format("OutlineViewer - {} Clients Connected",
-                                         nt::GetConnections(inst).size())
-                                 .c_str());
-        } else if ((event.flags & NT_EVENT_CONNECTED) != 0) {
-          glfwSetWindowTitle(win, fmt::format("OutlineViewer - Connected ({})",
-                                              connInfo->remote_ip)
-                                      .c_str());
-        } else {
-          glfwSetWindowTitle(win, "OutlineViewer - DISCONNECTED");
-        }
+      if (event.Is(NT_EVENT_CONNECTION)) {
+        updateTitle = true;
+        connectionEvent = event;
       } else if (auto msg = event.GetLogMessage()) {
         // handle NetworkTables log messages
         const char* level = "";
@@ -79,6 +92,10 @@
                                 msg->filename, msg->line));
       }
     }
+
+    if (updateTitle) {
+      glfwSetWindowTitle(win, MakeTitle(inst, connectionEvent).c_str());
+    }
   });
 
   // NetworkTables table window
@@ -99,7 +116,8 @@
   ImGui::SetNextWindowPos(ImVec2(0, 0));
   int width, height;
   glfwGetWindowSize(gui::GetSystemWindow(), &width, &height);
-  ImGui::SetNextWindowSize(ImVec2(width, height));
+  ImGui::SetNextWindowSize(
+      ImVec2(static_cast<float>(width), static_cast<float>(height)));
 
   ImGui::Begin("Entries", nullptr,
                ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_MenuBar |
@@ -119,6 +137,7 @@
   gui::EmitViewMenu();
   if (ImGui::BeginMenu("View")) {
     gFlagsSettings.DisplayMenu();
+    glass::DisplayNetworkTablesAddMenu(gModel.get());
     ImGui::EndMenu();
   }
 
@@ -139,6 +158,15 @@
     }
     ImGui::EndMenu();
   }
+
+  if (ImGui::BeginMenu("Docs")) {
+    if (ImGui::MenuItem("Online documentation")) {
+      wpi::gui::OpenURL(
+          "https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/"
+          "outlineviewer/");
+    }
+    ImGui::EndMenu();
+  }
   ImGui::EndMenuBar();
 
   // settings popup
@@ -183,6 +211,8 @@
     ImGui::Text("v%s", GetWPILibVersion());
     ImGui::Separator();
     ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
+    ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate,
+                ImGui::GetIO().Framerate);
     if (ImGui::Button("Close")) {
       ImGui::CloseCurrentPopup();
     }
diff --git a/third_party/allwpilib/processstarter/build.gradle b/third_party/allwpilib/processstarter/build.gradle
new file mode 100644
index 0000000..962a55c
--- /dev/null
+++ b/third_party/allwpilib/processstarter/build.gradle
@@ -0,0 +1,85 @@
+import org.gradle.internal.os.OperatingSystem
+
+if (project.hasProperty('onlylinuxathena')) {
+    return;
+}
+
+description = "Process Starter"
+
+apply plugin: 'cpp'
+apply plugin: 'objective-cpp'
+apply plugin: 'visual-studio'
+apply plugin: 'edu.wpi.first.NativeUtils'
+
+ext {
+    nativeName = 'processstarter'
+}
+
+apply from: "${rootDir}/shared/config.gradle"
+
+// Replace shared crt with static crt.
+// Note this means no wpilib binaries can be dependencies
+nativeUtils.platformConfigs.named(nativeUtils.wpi.platforms.windowsx64).configure {
+    cppCompiler.debugArgs.remove('/MDd')
+    cppCompiler.debugArgs.add('/MTd')
+    cppCompiler.releaseArgs.remove('/MD')
+    cppCompiler.releaseArgs.add('/MT')
+}
+
+project(':').libraryBuild.dependsOn build
+
+model {
+    components {
+        "${nativeName}"(NativeExecutableSpec) {
+            baseName = 'processstarter'
+            binaries.all {
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    it.buildable = false
+                    return
+                }
+                if (it.targetPlatform.operatingSystem.isMacOsX()) {
+                    it.sources {
+                        macObjCpp(ObjectiveCppSourceSet) {
+                            source {
+                                srcDirs 'src/main/native/osx'
+                                include '**/*.mm'
+                            }
+                            exportedHeaders {
+                                srcDirs 'src/main/native/include'
+                                include '**/*.h'
+                            }
+                        }
+                    }
+                } else if (it.targetPlatform.operatingSystem.isLinux()) {
+                    it.sources {
+                        linuxCpp(CppSourceSet) {
+                            source {
+                                srcDirs 'src/main/native/linux'
+                                include '**/*.cpp'
+                            }
+                            exportedHeaders {
+                                srcDirs 'src/main/native/include'
+                                include '**/*.h'
+                            }
+                        }
+                    }
+                } else if (it.targetPlatform.operatingSystem.isWindows()) {
+                    it.sources {
+                        windowsCpp(CppSourceSet) {
+                            source {
+                                srcDirs 'src/main/native/windows'
+                                include '**/*.cpp'
+                            }
+                            exportedHeaders {
+                                srcDirs 'src/main/native/include'
+                                include '**/*.h'
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+apply from: 'publish.gradle'
diff --git a/third_party/allwpilib/processstarter/publish.gradle b/third_party/allwpilib/processstarter/publish.gradle
new file mode 100644
index 0000000..925fa96
--- /dev/null
+++ b/third_party/allwpilib/processstarter/publish.gradle
@@ -0,0 +1,67 @@
+apply plugin: 'maven-publish'
+
+def baseArtifactId = 'processstarter'
+def artifactGroupId = 'edu.wpi.first.tools'
+def zipBaseName = '_GROUP_edu_wpi_first_tools_ID_processstarter_CLS'
+
+def outputsFolder = file("$project.buildDir/outputs")
+
+model {
+    tasks {
+        // Create the run task.
+        $.components.processstarter.binaries.each { bin ->
+            if (bin.buildable && bin.name.toLowerCase().contains("debug") && nativeUtils.isNativeDesktopPlatform(bin.targetPlatform)) {
+                Task run = project.tasks.create("run", Exec) {
+                    commandLine bin.tasks.install.runScriptFile.get().asFile.toString()
+                }
+                run.dependsOn bin.tasks.install
+            }
+        }
+    }
+    publishing {
+        def processstarterTaskList = []
+        $.components.each { component ->
+            component.binaries.each { binary ->
+                if (binary in NativeExecutableBinarySpec && binary.component.name.contains("processstarter")) {
+                    if (binary.buildable && (binary.name.contains('Release') || binary.name.contains('release'))) {
+                        // We are now in the binary that we want.
+                        // This is the default application path for the ZIP task.
+                        def applicationPath = binary.executable.file
+
+                        // Create the ZIP.
+                        def task = project.tasks.create("copyprocessstarterExecutable" + binary.targetPlatform.architecture.name, Zip) {
+                            description("Copies the processstarter executable to the outputs directory.")
+                            destinationDirectory = outputsFolder
+
+                            archiveBaseName = '_M_' + zipBaseName
+                            duplicatesStrategy = 'exclude'
+                            archiveClassifier = nativeUtils.getPublishClassifier(binary)
+
+                            from(licenseFile) {
+                                into '/'
+                            }
+
+                            from(applicationPath)
+                        }
+
+                        task.dependsOn binary.tasks.link
+                        processstarterTaskList.add(task)
+                        project.build.dependsOn task
+                        project.artifacts { task }
+                        addTaskToCopyAllOutputs(task)
+                    }
+                }
+            }
+        }
+
+        publications {
+            processstarter(MavenPublication) {
+                processstarterTaskList.each { artifact it }
+
+                artifactId = baseArtifactId
+                groupId = artifactGroupId
+                version wpilibVersioning.version.get()
+            }
+        }
+    }
+}
diff --git a/third_party/allwpilib/processstarter/src/main/native/linux/main.cpp b/third_party/allwpilib/processstarter/src/main/native/linux/main.cpp
new file mode 100644
index 0000000..1ddf1cd
--- /dev/null
+++ b/third_party/allwpilib/processstarter/src/main/native/linux/main.cpp
@@ -0,0 +1,86 @@
+// 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 <poll.h>
+#include <spawn.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <filesystem>
+
+int main(int argc, char* argv[]) {
+  char path[PATH_MAX];
+  char dest[PATH_MAX];
+  std::memset(dest, 0, sizeof(dest));  // readlink does not null terminate!
+  pid_t pid = getpid();
+  std::snprintf(path, PATH_MAX, "/proc/%d/exe", pid);
+  int readlink_len = readlink(path, dest, PATH_MAX);
+  if (readlink_len < 0) {
+    std::perror("readlink");
+    return 1;
+  } else if (readlink_len == PATH_MAX) {
+    std::printf("Truncation occurred\n");
+    return 1;
+  }
+
+  std::filesystem::path exePath{dest};
+  if (exePath.empty()) {
+    return 1;
+  }
+
+  if (!exePath.has_stem()) {
+    return 1;
+  }
+
+  if (!exePath.has_parent_path()) {
+    return 1;
+  }
+
+  std::filesystem::path jarPath{exePath};
+  jarPath.replace_extension("jar");
+  std::filesystem::path parentPath{exePath.parent_path()};
+
+  if (!parentPath.has_parent_path()) {
+    return 1;
+  }
+  std::filesystem::path toolsFolder{parentPath.parent_path()};
+
+  std::filesystem::path Java = toolsFolder / "jdk" / "bin" / "java";
+
+  pid = 0;
+  std::string data = jarPath;
+  std::string jarArg = "-jar";
+  char* const arguments[] = {jarArg.data(), data.data(), nullptr};
+
+  int status =
+      posix_spawn(&pid, Java.c_str(), nullptr, nullptr, arguments, environ);
+  if (status != 0) {
+    char* home = std::getenv("JAVA_HOME");
+    std::string javaLocal = "java";
+    if (home != nullptr) {
+      std::filesystem::path javaHomePath{home};
+      javaHomePath /= "bin";
+      javaHomePath /= "java";
+      javaLocal = javaHomePath;
+    }
+
+    status = posix_spawn(&pid, javaLocal.c_str(), nullptr, nullptr, arguments,
+                         environ);
+    if (status != 0) {
+      return 1;
+    }
+  }
+
+  int childPid = syscall(SYS_pidfd_open, pid, 0);
+  if (childPid <= 0) {
+    return 1;
+  }
+
+  struct pollfd pfd = {childPid, POLLIN, 0};
+  return poll(&pfd, 1, 3000);
+}
diff --git a/third_party/allwpilib/processstarter/src/main/native/osx/main.mm b/third_party/allwpilib/processstarter/src/main/native/osx/main.mm
new file mode 100644
index 0000000..57b6678
--- /dev/null
+++ b/third_party/allwpilib/processstarter/src/main/native/osx/main.mm
@@ -0,0 +1,80 @@
+// 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.
+
+#import <Foundation/Foundation.h>
+#include <string>
+#include <filesystem>
+
+int main(int argc, char* argv[]) {
+  (void)argc;
+  (void)argv;
+  NSString* exePathPlat = [[NSBundle mainBundle] bundlePath];
+  NSString* identifier = [[NSBundle mainBundle] bundleIdentifier];
+  if (identifier == nil) {
+    exePathPlat = [[NSBundle mainBundle] executablePath];
+  }
+
+  std::filesystem::path exePath{[exePathPlat UTF8String]};
+  if (exePath.empty()) {
+    return 1;
+  }
+
+  if (!exePath.has_stem()) {
+    return 1;
+  }
+
+  if (!exePath.has_parent_path()) {
+    return 1;
+  }
+
+  std::filesystem::path jarPath{exePath};
+  jarPath.replace_extension("jar");
+  std::filesystem::path parentPath{exePath.parent_path()};
+
+  if (!parentPath.has_parent_path()) {
+    return 1;
+  }
+  std::filesystem::path toolsFolder{parentPath.parent_path()};
+
+  std::filesystem::path java = toolsFolder / "jdk" / "bin" / "java";
+
+  NSArray<NSString*>* Arguments =
+      @[ @"-jar", [NSString stringWithFormat:@"%s", jarPath.c_str()] ];
+
+  NSTask* task = [[NSTask alloc] init];
+  task.launchPath = [NSString stringWithFormat:@"%s", java.c_str()];
+  task.arguments = Arguments;
+  task.terminationHandler = ^(NSTask* t) {
+    (void)t;
+    CFRunLoopStop(CFRunLoopGetMain());
+  };
+
+  if (![task launchAndReturnError:nil]) {
+    task.terminationHandler = nil;
+
+    NSString* javaHome =
+        [[[NSProcessInfo processInfo] environment] objectForKey:@"JAVA_HOME"];
+    task = [[NSTask alloc] init];
+    task.launchPath = @"java";
+    if (javaHome != nil) {
+      std::filesystem::path javaHomePath{[javaHome UTF8String]};
+      javaHomePath /= "bin";
+      javaHomePath /= "java";
+      task.launchPath = [NSString stringWithFormat:@"%s", javaHomePath.c_str()];
+    }
+    task.arguments = Arguments;
+    task.terminationHandler = ^(NSTask* t) {
+      (void)t;
+      CFRunLoopStop(CFRunLoopGetMain());
+    };
+
+    if (![task launchAndReturnError:nil]) {
+      return 1;
+    }
+  }
+
+  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 3, false);
+
+  return task.running ? 0 : 1;
+}
diff --git a/third_party/allwpilib/processstarter/src/main/native/windows/main.cpp b/third_party/allwpilib/processstarter/src/main/native/windows/main.cpp
new file mode 100644
index 0000000..8b22aef
--- /dev/null
+++ b/third_party/allwpilib/processstarter/src/main/native/windows/main.cpp
@@ -0,0 +1,85 @@
+// 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 <filesystem>
+
+#include "Windows.h"
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+                   LPTSTR pCmdLine, int nCmdShow) {
+  DWORD Status;
+  WCHAR ExePathRaw[1024];
+  Status = GetModuleFileNameW(NULL, ExePathRaw, 1024);
+  if (Status == 0) {
+    DWORD LastError = GetLastError();
+    return LastError;
+  }
+
+  std::filesystem::path ExePath{ExePathRaw};
+  if (ExePath.empty()) {
+    return 1;
+  }
+
+  if (!ExePath.has_stem()) {
+    return 1;
+  }
+
+  if (!ExePath.has_parent_path()) {
+    return 1;
+  }
+
+  std::filesystem::path JarPath{ExePath};
+  JarPath.replace_extension(L"jar");
+  std::filesystem::path ParentPath{ExePath.parent_path()};
+
+  if (!ParentPath.has_parent_path()) {
+    return 1;
+  }
+  std::filesystem::path ToolsFolder{ParentPath.parent_path()};
+
+  std::filesystem::path Javaw = ToolsFolder / L"jdk" / L"bin" / L"javaw.exe";
+
+  std::wstring ToRun = L" -jar \"";
+  ToRun += JarPath;
+  ToRun += L"\"";
+
+  STARTUPINFOW StartupInfo;
+  PROCESS_INFORMATION ProcessInfo;
+
+  ZeroMemory(&StartupInfo, sizeof(StartupInfo));
+  StartupInfo.cb = sizeof(StartupInfo);
+  ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
+
+  if (!CreateProcessW(Javaw.c_str(), ToRun.data(), NULL, NULL, FALSE, 0, NULL,
+                      NULL, &StartupInfo, &ProcessInfo)) {
+    ZeroMemory(&StartupInfo, sizeof(StartupInfo));
+    StartupInfo.cb = sizeof(StartupInfo);
+    ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
+
+    ToRun = L" -jar \"";
+    ToRun += JarPath;
+    ToRun += L"\"";
+
+    Status = GetEnvironmentVariableW(L"JAVA_HOME", ExePathRaw, 1024);
+    std::wstring JavawLocal = L"javaw";
+    if (Status != 0 && Status < 1024) {
+      std::filesystem::path JavaHomePath{ExePathRaw};
+      JavaHomePath /= "bin";
+      JavaHomePath /= "javaw.exe";
+      JavawLocal = JavaHomePath;
+    }
+
+    if (!CreateProcessW(JavawLocal.c_str(), ToRun.data(), NULL, NULL, FALSE, 0,
+                        NULL, NULL, &StartupInfo, &ProcessInfo)) {
+      return 1;
+    }
+  }
+
+  Status =
+      WaitForSingleObject(ProcessInfo.hProcess, 3000);  // Wait for 3 seconds
+  CloseHandle(ProcessInfo.hProcess);
+  CloseHandle(ProcessInfo.hThread);
+
+  return Status == WAIT_TIMEOUT ? 0 : 1;
+}
diff --git a/third_party/allwpilib/roborioteamnumbersetter/.styleguide b/third_party/allwpilib/roborioteamnumbersetter/.styleguide
index 65af50d..6e411bc 100644
--- a/third_party/allwpilib/roborioteamnumbersetter/.styleguide
+++ b/third_party/allwpilib/roborioteamnumbersetter/.styleguide
@@ -22,6 +22,7 @@
   ^GLFW
   ^fmt/
   ^imgui
+  ^libssh/
   ^ntcore
   ^wpi/
   ^wpigui
diff --git a/third_party/allwpilib/roborioteamnumbersetter/CMakeLists.txt b/third_party/allwpilib/roborioteamnumbersetter/CMakeLists.txt
index 13a079a..4e2c5e0 100644
--- a/third_party/allwpilib/roborioteamnumbersetter/CMakeLists.txt
+++ b/third_party/allwpilib/roborioteamnumbersetter/CMakeLists.txt
@@ -5,7 +5,7 @@
 include(LinkMacOSGUI)
 
 configure_file(src/main/generate/WPILibVersion.cpp.in WPILibVersion.cpp)
-GENERATE_RESOURCES(src/main/native/resources generated/main/cpp RTNS rtns rtns_resources_src)
+generate_resources(src/main/native/resources generated/main/cpp RTNS rtns rtns_resources_src)
 
 file(GLOB rtns_src src/main/native/cpp/*.cpp ${CMAKE_CURRENT_BINARY_DIR}/WPILibVersion.cpp)
 
diff --git a/third_party/allwpilib/roborioteamnumbersetter/build.gradle b/third_party/allwpilib/roborioteamnumbersetter/build.gradle
index 8a7070e..ae9f9cc 100644
--- a/third_party/allwpilib/roborioteamnumbersetter/build.gradle
+++ b/third_party/allwpilib/roborioteamnumbersetter/build.gradle
@@ -1,127 +1,124 @@
 import org.gradle.internal.os.OperatingSystem
 
-if (!project.hasProperty('onlylinuxathena')) {
-
-    description = "roboRIO Team Number Setter"
-
-    apply plugin: 'cpp'
-    apply plugin: 'c'
-    apply plugin: 'google-test-test-suite'
-    apply plugin: 'visual-studio'
-    apply plugin: 'edu.wpi.first.NativeUtils'
-
-    if (OperatingSystem.current().isWindows()) {
-        apply plugin: 'windows-resources'
-    }
-
-    ext {
-        nativeName = 'roborioteamnumbersetter'
-    }
-
-    apply from: "${rootDir}/shared/resources.gradle"
-    apply from: "${rootDir}/shared/config.gradle"
-
-    def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in")
-    def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp")
-
-    apply from: "${rootDir}/shared/libssh.gradle"
-    apply from: "${rootDir}/shared/imgui.gradle"
-
-    task generateCppVersion() {
-        description = 'Generates the wpilib version class'
-        group = 'WPILib'
-
-        outputs.file wpilibVersionFileOutput
-        inputs.file wpilibVersionFileInput
-
-        if (wpilibVersioning.releaseMode) {
-            outputs.upToDateWhen { false }
-        }
-
-        // We follow a simple set of checks to determine whether we should generate a new version file:
-        // 1. If the release type is not development, we generate a new version file
-        // 2. If there is no generated version number, we generate a new version file
-        // 3. If there is a generated build number, and the release type is development, then we will
-        //    only generate if the publish task is run.
-        doLast {
-            def version = wpilibVersioning.version.get()
-            println "Writing version ${version} to $wpilibVersionFileOutput"
-
-            if (wpilibVersionFileOutput.exists()) {
-                wpilibVersionFileOutput.delete()
-            }
-            def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version)
-            wpilibVersionFileOutput.write(read)
-        }
-    }
-
-    gradle.taskGraph.addTaskExecutionGraphListener { graph ->
-        def willPublish = graph.hasTask(publish)
-        if (willPublish) {
-            generateCppVersion.outputs.upToDateWhen { false }
-        }
-    }
-
-    def generateTask = createGenerateResourcesTask('main', 'RTNS', 'rtns', project)
-
-    project(':').libraryBuild.dependsOn build
-    tasks.withType(CppCompile) {
-        dependsOn generateTask
-        dependsOn generateCppVersion
-    }
-
-    model {
-        components {
-            // By default, a development executable will be generated. This is to help the case of
-            // testing specific functionality of the library.
-            "${nativeName}"(NativeExecutableSpec) {
-                baseName = 'roborioteamnumbersetter'
-                sources {
-                    cpp {
-                        source {
-                            srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp"
-                            include '**/*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/main/native/include'
-                        }
-                    }
-                    if (OperatingSystem.current().isWindows()) {
-                        rc {
-                            source {
-                                srcDirs 'src/main/native/win'
-                                include '*.rc'
-                            }
-                        }
-                    }
-                }
-                binaries.all {
-                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
-                        it.buildable = false
-                        return
-                    }
-                    it.cppCompiler.define("LIBSSH_STATIC")
-                    lib project: ':glass', library: 'glass', linkage: 'static'
-                    lib project: ':wpinet', library: 'wpinet', linkage: 'static'
-                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
-                    lib project: ':wpigui', library: 'wpigui', linkage: 'static'
-                    nativeUtils.useRequiredLibrary(it, 'imgui', 'libssh')
-                    if (it.targetPlatform.operatingSystem.isWindows()) {
-                        it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
-                        it.linker.args << 'ws2_32.lib' << 'advapi32.lib' << 'crypt32.lib' << 'user32.lib'
-                    } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
-                        it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
-                        it.linker.args << '-framework' << 'Kerberos'
-                    } else {
-                        it.linker.args << '-lX11'
-                        if (it.targetPlatform.name.startsWith('linuxarm')) {
-                            it.linker.args << '-lGL'
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    apply from: 'publish.gradle'
+if (project.hasProperty('onlylinuxathena')) {
+    return;
 }
+
+description = "roboRIO Team Number Setter"
+
+apply plugin: 'cpp'
+apply plugin: 'visual-studio'
+apply plugin: 'edu.wpi.first.NativeUtils'
+
+if (OperatingSystem.current().isWindows()) {
+    apply plugin: 'windows-resources'
+}
+
+ext {
+    nativeName = 'roborioteamnumbersetter'
+}
+
+apply from: "${rootDir}/shared/resources.gradle"
+apply from: "${rootDir}/shared/config.gradle"
+
+def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in")
+def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp")
+
+apply from: "${rootDir}/shared/libssh.gradle"
+apply from: "${rootDir}/shared/imgui.gradle"
+
+task generateCppVersion() {
+    description = 'Generates the wpilib version class'
+    group = 'WPILib'
+
+    outputs.file wpilibVersionFileOutput
+    inputs.file wpilibVersionFileInput
+
+    if (wpilibVersioning.releaseMode) {
+        outputs.upToDateWhen { false }
+    }
+
+    // We follow a simple set of checks to determine whether we should generate a new version file:
+    // 1. If the release type is not development, we generate a new version file
+    // 2. If there is no generated version number, we generate a new version file
+    // 3. If there is a generated build number, and the release type is development, then we will
+    //    only generate if the publish task is run.
+    doLast {
+        def version = wpilibVersioning.version.get()
+        println "Writing version ${version} to $wpilibVersionFileOutput"
+
+        if (wpilibVersionFileOutput.exists()) {
+            wpilibVersionFileOutput.delete()
+        }
+        def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version)
+        wpilibVersionFileOutput.write(read)
+    }
+}
+
+gradle.taskGraph.addTaskExecutionGraphListener { graph ->
+    def willPublish = graph.hasTask(publish)
+    if (willPublish) {
+        generateCppVersion.outputs.upToDateWhen { false }
+    }
+}
+
+def generateTask = createGenerateResourcesTask('main', 'RTNS', 'rtns', project)
+
+project(':').libraryBuild.dependsOn build
+tasks.withType(CppCompile) {
+    dependsOn generateTask
+    dependsOn generateCppVersion
+}
+
+model {
+    components {
+        // By default, a development executable will be generated. This is to help the case of
+        // testing specific functionality of the library.
+        "${nativeName}"(NativeExecutableSpec) {
+            baseName = 'roborioteamnumbersetter'
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp"
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/main/native/include'
+                    }
+                }
+                if (OperatingSystem.current().isWindows()) {
+                    rc.source {
+                        srcDirs 'src/main/native/win'
+                        include '*.rc'
+                    }
+                }
+            }
+            binaries.all {
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    it.buildable = false
+                    return
+                }
+                it.cppCompiler.define("LIBSSH_STATIC")
+                lib project: ':glass', library: 'glass', linkage: 'static'
+                lib project: ':wpinet', library: 'wpinet', linkage: 'static'
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                lib project: ':wpigui', library: 'wpigui', linkage: 'static'
+                nativeUtils.useRequiredLibrary(it, 'imgui', 'libssh')
+                if (it.targetPlatform.operatingSystem.isWindows()) {
+                    it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
+                    it.linker.args << 'ws2_32.lib' << 'advapi32.lib' << 'crypt32.lib' << 'user32.lib'
+                } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
+                    it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
+                    it.linker.args << '-framework' << 'Kerberos'
+                } else {
+                    it.linker.args << '-lX11'
+                    if (it.targetPlatform.name.startsWith('linuxarm')) {
+                        it.linker.args << '-lGL'
+                    }
+                }
+            }
+        }
+    }
+}
+
+apply from: 'publish.gradle'
diff --git a/third_party/allwpilib/roborioteamnumbersetter/publish.gradle b/third_party/allwpilib/roborioteamnumbersetter/publish.gradle
index da6a0f6..824c2b9 100644
--- a/third_party/allwpilib/roborioteamnumbersetter/publish.gradle
+++ b/third_party/allwpilib/roborioteamnumbersetter/publish.gradle
@@ -29,49 +29,14 @@
                         def applicationPath = binary.executable.file
                         def icon = file("$project.projectDir/src/main/native/mac/rtns.icns")
 
-                        // Create the macOS bundle.
-                        def bundleTask = project.tasks.create("bundleroboRIOTeamNumberSetterOsxApp" + binary.targetPlatform.architecture.name, Copy) {
-                            description("Creates a macOS application bundle for roboRIO Team Number Setter")
-                            from(file("$project.projectDir/Info.plist"))
-                            into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/roboRIOTeamNumberSetter.app/Contents"))
-                            into("MacOS") { with copySpec { from binary.executable.file } }
-                            into("Resources") { with copySpec { from icon } }
-
-                            inputs.property "HasDeveloperId", project.hasProperty("developerID")
-
-                            doLast {
-                                if (project.hasProperty("developerID")) {
-                                    // Get path to binary.
-                                    exec {
-                                        workingDir rootDir
-                                        def args = [
-                                            "sh",
-                                            "-c",
-                                            "codesign --force --strict --deep " +
-                                            "--timestamp --options=runtime " +
-                                            "--verbose -s ${project.findProperty("developerID")} " +
-                                            "$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/roboRIOTeamNumberSetter.app/"
-                                        ]
-                                        commandLine args
-                                    }
-                                }
-                            }
-                        }
-
-                        // Reset the application path if we are creating a bundle.
-                        if (binary.targetPlatform.operatingSystem.isMacOsX()) {
-                            applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name")
-                            project.build.dependsOn bundleTask
-                        }
-
                         // Create the ZIP.
-                        def task = project.tasks.create("copyroboRIOTeamNumberSetterExecutable" + binary.targetPlatform.architecture.name, Zip) {
+                        def task = project.tasks.create("copyroboRIOTeamNumberSetterExecutable" + binary.targetPlatform.operatingSystem.name + binary.targetPlatform.architecture.name, Zip) {
                             description("Copies the roboRIOTeamNumberSetter executable to the outputs directory.")
                             destinationDirectory = outputsFolder
 
                             archiveBaseName = '_M_' + zipBaseName
                             duplicatesStrategy = 'exclude'
-                            classifier = nativeUtils.getPublishClassifier(binary)
+                            archiveClassifier = nativeUtils.getPublishClassifier(binary)
 
                             from(licenseFile) {
                                 into '/'
@@ -90,6 +55,47 @@
                         }
 
                         if (binary.targetPlatform.operatingSystem.isMacOsX()) {
+                            // Create the macOS bundle.
+                            def bundleTask = project.tasks.create("bundleroboRIOTeamNumberSetterOsxApp" + binary.targetPlatform.architecture.name, Copy) {
+                                description("Creates a macOS application bundle for roboRIO Team Number Setter")
+                                from(file("$project.projectDir/Info.plist"))
+                                into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/roboRIOTeamNumberSetter.app/Contents"))
+                                into("MacOS") {
+                                    with copySpec {
+                                        from binary.executable.file
+                                    }
+                                }
+                                into("Resources") {
+                                    with copySpec {
+                                        from icon
+                                    }
+                                }
+
+                                inputs.property "HasDeveloperId", project.hasProperty("developerID")
+
+                                doLast {
+                                    if (project.hasProperty("developerID")) {
+                                        // Get path to binary.
+                                        exec {
+                                            workingDir rootDir
+                                            def args = [
+                                                "sh",
+                                                "-c",
+                                                "codesign --force --strict --deep " +
+                                                "--timestamp --options=runtime " +
+                                                "--verbose -s ${project.findProperty("developerID")} " +
+                                                "$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/roboRIOTeamNumberSetter.app/"
+                                            ]
+                                            commandLine args
+                                        }
+                                    }
+                                }
+                            }
+
+                            // Reset the application path if we are creating a bundle.
+                            applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name")
+                            project.build.dependsOn bundleTask
+
                             bundleTask.dependsOn binary.tasks.link
                             task.dependsOn(bundleTask)
                         }
diff --git a/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/App.cpp b/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/App.cpp
index e6421d5..eefb52b 100644
--- a/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/App.cpp
+++ b/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/App.cpp
@@ -127,6 +127,8 @@
                 static_cast<int>(multicastResolver->HasImplementation()));
     ImGui::Separator();
     ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
+    ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate,
+                ImGui::GetIO().Framerate);
     if (ImGui::Button("Close")) {
       ImGui::CloseCurrentPopup();
     }
diff --git a/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp b/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp
index 380cfe0..3fd63cd 100644
--- a/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp
+++ b/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp
@@ -73,11 +73,11 @@
         in_addr addr;
         addr.s_addr = ipAddress;
         wpi::uv::AddrToName(addr, &ip);
-        DEBUG("Trying to establish SSH connection to {}.", ip);
+        DEBUG("Trying to establish SSH connection to {}.", ip.str());
         try {
           SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger};
           session.Open();
-          DEBUG("SSH connection to {} was successful.", ip);
+          DEBUG("SSH connection to {} was successful.", ip.str());
 
           SUCCESS("roboRIO Connected!");
 
@@ -85,7 +85,7 @@
             session.Execute(fmt::format(
                 "/usr/local/natinst/bin/nirtcfg "
                 "--file=/etc/natinst/share/ni-rt.ini --set "
-                "section=systemsettings,token=host_name,value=roborio-{"
+                "section=systemsettings,token=host_name,value=roboRIO-{"
                 "}-FRC ; sync",
                 teamNumber));
           } catch (const SshSession::SshException& e) {
@@ -93,7 +93,7 @@
             throw e;
           }
         } catch (const SshSession::SshException& e) {
-          DEBUG("SSH connection to {} failed with {}.", ip, e.what());
+          DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what());
           throw e;
         }
         return 0;
@@ -117,11 +117,11 @@
         in_addr addr;
         addr.s_addr = ipAddress;
         wpi::uv::AddrToName(addr, &ip);
-        DEBUG("Trying to establish SSH connection to {}.", ip);
+        DEBUG("Trying to establish SSH connection to {}.", ip.str());
         try {
           SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger};
           session.Open();
-          DEBUG("SSH connection to {} was successful.", ip);
+          DEBUG("SSH connection to {} was successful.", ip.str());
 
           SUCCESS("roboRIO Connected!");
 
@@ -132,7 +132,7 @@
             throw e;
           }
         } catch (const SshSession::SshException& e) {
-          DEBUG("SSH connection to {} failed with {}.", ip, e.what());
+          DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what());
           throw e;
         }
         return 0;
@@ -156,11 +156,11 @@
         in_addr addr;
         addr.s_addr = ipAddress;
         wpi::uv::AddrToName(addr, &ip);
-        DEBUG("Trying to establish SSH connection to {}.", ip);
+        DEBUG("Trying to establish SSH connection to {}.", ip.str());
         try {
           SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger};
           session.Open();
-          DEBUG("SSH connection to {} was successful.", ip);
+          DEBUG("SSH connection to {} was successful.", ip.str());
 
           SUCCESS("roboRIO Connected!");
 
@@ -175,7 +175,7 @@
             throw e;
           }
         } catch (const SshSession::SshException& e) {
-          DEBUG("SSH connection to {} failed with {}.", ip, e.what());
+          DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what());
           throw e;
         }
         return 0;
diff --git a/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp b/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp
index 6424ce0..74fc638 100644
--- a/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp
+++ b/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp
@@ -5,8 +5,6 @@
 #include "SshSession.h"
 
 #include <fcntl.h>
-#include <libssh/libssh.h>
-#include <libssh/sftp.h>
 #include <stdint.h>
 #include <sys/stat.h>
 
@@ -14,6 +12,8 @@
 #include <stdexcept>
 
 #include <fmt/format.h>
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
 #include <wpi/Logger.h>
 
 using namespace sysid;
diff --git a/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.h b/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.h
index 2e97120..47db332 100644
--- a/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.h
+++ b/third_party/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.h
@@ -4,12 +4,11 @@
 
 #pragma once
 
-#include <libssh/libssh.h>
-
 #include <stdexcept>
 #include <string>
 #include <string_view>
 
+#include <libssh/libssh.h>
 #include <wpi/Logger.h>
 
 namespace sysid {
diff --git a/third_party/allwpilib/romiVendordep/.styleguide b/third_party/allwpilib/romiVendordep/.styleguide
new file mode 100644
index 0000000..7c8d7a7
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/.styleguide
@@ -0,0 +1,31 @@
+cppHeaderFileInclude {
+  \.h$
+  \.hpp$
+  \.inc$
+}
+
+cppSrcFileInclude {
+  \.cpp$
+}
+
+repoRootNameOverride {
+  wpilib
+}
+
+includeOtherLibs {
+  ^cameraserver/
+  ^cscore
+  ^fmt/
+  ^frc/
+  ^gtest/
+  ^hal/
+  ^imgui
+  ^mockdata/
+  ^networktables/
+  ^ntcore
+  ^opencv2/
+  ^support/
+  ^units/
+  ^vision/
+  ^wpi/
+}
diff --git a/third_party/allwpilib/romiVendordep/CMakeLists.txt b/third_party/allwpilib/romiVendordep/CMakeLists.txt
new file mode 100644
index 0000000..bd5f413
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/CMakeLists.txt
@@ -0,0 +1,71 @@
+project(romiVendordep)
+
+include(SubDirList)
+include(CompileWarnings)
+include(AddTest)
+
+if (WITH_JAVA)
+  find_package(Java REQUIRED)
+  include(UseJava)
+  set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
+
+  file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
+  add_jar(romiVendordep_jar ${JAVA_SOURCES} INCLUDE_JARS hal_jar ntcore_jar cscore_jar cameraserver_jar wpimath_jar wpiutil_jar wpilibj_jar OUTPUT_NAME romiVendordep)
+
+  get_property(ROMIVENDORDEP_JAR_FILE TARGET romiVendordep_jar PROPERTY JAR_FILE)
+  install(FILES ${ROMIVENDORDEP_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+  set_property(TARGET romiVendordep_jar PROPERTY FOLDER "java")
+
+  if (WITH_FLAT_INSTALL)
+      set (romiVendordep_config_dir ${wpilib_dest})
+  else()
+      set (romiVendordep_config_dir share/romiVendordep)
+  endif()
+endif()
+
+if (WITH_JAVA_SOURCE)
+  find_package(Java REQUIRED)
+  include(UseJava)
+  file(GLOB_RECURSE ROMIVENDORDEP_SOURCES src/main/java/*.java)
+  add_jar(romiVendordep_src_jar
+  RESOURCES NAMESPACE "edu/wpi/first/wpilibj/romi" ${ROMIVENDORDEP_SOURCES}
+  OUTPUT_NAME romiVendordep-sources)
+
+  get_property(ROMIVENDORDEP_SRC_JAR_FILE TARGET romiVendordep_src_jar PROPERTY JAR_FILE)
+  install(FILES ${ROMIVENDORDEP_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+  set_property(TARGET romiVendordep_src_jar PROPERTY FOLDER "java")
+endif()
+
+file(GLOB_RECURSE romiVendordep_native_src src/main/native/cpp/*.cpp)
+add_library(romiVendordep ${romiVendordep_native_src})
+set_target_properties(romiVendordep PROPERTIES DEBUG_POSTFIX "d")
+set_property(TARGET romiVendordep PROPERTY FOLDER "libraries")
+
+target_compile_features(romiVendordep PUBLIC cxx_std_20)
+wpilib_target_warnings(romiVendordep)
+target_link_libraries(romiVendordep wpilibc)
+
+target_include_directories(romiVendordep PUBLIC
+                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
+                            $<INSTALL_INTERFACE:${include_dest}/romiVendordep>)
+
+install(TARGETS romiVendordep EXPORT romiVendordep DESTINATION "${main_lib_dest}")
+install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/romiVendordep")
+
+if (FLAT_INSTALL_WPILIB)
+     set(romiVendordep_config_dir ${wpilib_dest})
+ else()
+     set(romiVendordep_config_dir share/romiVendordep)
+ endif()
+
+ configure_file(romiVendordep-config.cmake.in ${WPILIB_BINARY_DIR}/romiVendordep-config.cmake)
+ install(FILES ${WPILIB_BINARY_DIR}/romiVendordep-config.cmake DESTINATION ${romiVendordep_config_dir})
+ install(EXPORT romiVendordep DESTINATION ${romiVendordep_config_dir})
+
+ if (WITH_TESTS)
+     wpilib_add_test(romiVendordep src/test/native/cpp)
+     target_include_directories(romiVendordep_test PRIVATE src/test/native/include)
+     target_link_libraries(romiVendordep_test romiVendordep gmock_main)
+ endif()
diff --git a/third_party/allwpilib/romiVendordep/README.md b/third_party/allwpilib/romiVendordep/README.md
new file mode 100644
index 0000000..e970a6c
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/README.md
@@ -0,0 +1,2 @@
+# romi-vendordep
+WPILib Romi Vendordep
diff --git a/third_party/allwpilib/romiVendordep/RomiVendordep.json b/third_party/allwpilib/romiVendordep/RomiVendordep.json
new file mode 100644
index 0000000..4ebc02f
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/RomiVendordep.json
@@ -0,0 +1,37 @@
+{
+  "fileName": "RomiVendordep.json",
+  "name": "Romi-Vendordep",
+  "version": "1.0.0",
+  "uuid": "1010372a-b446-46f4-b229-61e53a26a7dc",
+  "frcYear": "2024",
+  "mavenUrls": [],
+  "jsonUrl": "",
+  "javaDependencies": [
+    {
+      "groupId": "edu.wpi.first.romiVendordep",
+      "artifactId": "romiVendordep-java",
+      "version": "wpilib"
+    }
+  ],
+  "jniDependencies": [],
+  "cppDependencies": [
+    {
+      "groupId": "edu.wpi.first.romiVendordep",
+      "artifactId": "romiVendordep-cpp",
+      "version": "wpilib",
+      "libName": "romiVendordep",
+      "headerClassifier": "headers",
+      "sourcesClassifier": "sources",
+      "sharedLibrary": true,
+      "skipInvalidPlatforms": true,
+      "binaryPlatforms": [
+        "linuxarm32",
+        "linuxarm64",
+        "windowsx86-64",
+        "windowsx86",
+        "linuxx86-64",
+        "osxuniversal"
+      ]
+    }
+  ]
+}
diff --git a/third_party/allwpilib/romiVendordep/build.gradle b/third_party/allwpilib/romiVendordep/build.gradle
new file mode 100644
index 0000000..d2f0c0d
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/build.gradle
@@ -0,0 +1,98 @@
+ext {
+    nativeName = 'romiVendordep'
+    devMain = 'edu.wpi.first.wpilibj.romi.DevMain'
+}
+
+evaluationDependsOn(":ntcore")
+evaluationDependsOn(":cscore")
+evaluationDependsOn(":hal")
+evaluationDependsOn(":wpimath")
+evaluationDependsOn(":wpilibc")
+evaluationDependsOn(":cameraserver")
+evaluationDependsOn(":wpilibj")
+
+apply from: "${rootDir}/shared/javacpp/setupBuild.gradle"
+
+dependencies {
+    implementation project(":wpiutil")
+    implementation project(":wpinet")
+    implementation project(":ntcore")
+    implementation project(":cscore")
+    implementation project(":hal")
+    implementation project(":wpimath")
+    implementation project(":wpilibj")
+}
+
+nativeUtils.exportsConfigs {
+    // Main library is just default empty. This will export everything
+    romiVendordep {
+        x64ExcludeSymbols = [
+            '_CT??_R0?AV_System_error',
+            '_CT??_R0?AVexception',
+            '_CT??_R0?AVfailure',
+            '_CT??_R0?AVruntime_error',
+            '_CT??_R0?AVsystem_error',
+            '_CTA5?AVfailure',
+            '_TI5?AVfailure',
+            '_CT??_R0?AVout_of_range',
+            '_CTA3?AVout_of_range',
+            '_TI3?AVout_of_range',
+            '_CT??_R0?AVbad_cast'
+        ]
+    }
+}
+
+model {
+    components {}
+    binaries {
+        all {
+            if (!it.buildable || !(it instanceof NativeBinarySpec)) {
+                return
+            }
+            lib project: ":wpilibc", library: "wpilibc", linkage: "shared"
+            project(":ntcore").addNtcoreDependency(it, "shared")
+            project(":hal").addHalDependency(it, "shared")
+            lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+            lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
+            lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
+
+            if (it.component.name == "${nativeName}Dev") {
+                project(":ntcore").addNtcoreJniDependency(it)
+                lib project: ":wpinet", library: "wpinetJNIShared", linkage: "shared"
+                lib project: ":wpiutil", library: "wpiutilJNIShared", linkage: "shared"
+                lib project: ":wpimath", library: "wpimathJNIShared", linkage: "shared"
+                project(":hal").addHalJniDependency(it)
+            }
+
+            if (it instanceof GoogleTestTestSuiteBinarySpec) {
+                nativeUtils.useRequiredLibrary(it, "opencv_shared")
+                lib project: ":cscore", library: "cscore", linkage: "shared"
+            }
+        }
+    }
+    tasks {
+        def c = $.components
+        def found = false
+        def systemArch = getCurrentArch()
+        c.each {
+            if (it in NativeExecutableSpec && it.name == "${nativeName}Dev") {
+                it.binaries.each {
+                    if (!found) {
+                        def arch = it.targetPlatform.name
+                        if (arch == systemArch) {
+                            def filePath = it.tasks.install.installDirectory.get().toString() + File.separatorChar + "lib"
+                            found = true
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+test {
+    testLogging {
+        outputs.upToDateWhen {false}
+        showStandardStreams = true
+    }
+}
diff --git a/third_party/allwpilib/romiVendordep/romiVendordep-config.cmake.in b/third_party/allwpilib/romiVendordep/romiVendordep-config.cmake.in
new file mode 100644
index 0000000..3711d9c
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/romiVendordep-config.cmake.in
@@ -0,0 +1,11 @@
+include(CMakeFindDependencyMacro)
+ @WPIUTIL_DEP_REPLACE@
+ @NTCORE_DEP_REPLACE@
+ @CSCORE_DEP_REPLACE@
+ @CAMERASERVER_DEP_REPLACE@
+ @HAL_DEP_REPLACE@
+ @WPILIBC_DEP_REPLACE@
+ @WPIMATH_DEP_REPLACE@
+
+ @FILENAME_DEP_REPLACE@
+ include(${SELF_DIR}/romiVendordep.cmake)
diff --git a/third_party/allwpilib/romiVendordep/src/dev/java/edu/wpi/first/wpilibj/romi/DevMain.java b/third_party/allwpilib/romiVendordep/src/dev/java/edu/wpi/first/wpilibj/romi/DevMain.java
new file mode 100644
index 0000000..846263e
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/dev/java/edu/wpi/first/wpilibj/romi/DevMain.java
@@ -0,0 +1,21 @@
+// 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.
+
+package edu.wpi.first.wpilibj.romi;
+
+import edu.wpi.first.hal.HALUtil;
+import edu.wpi.first.networktables.NetworkTablesJNI;
+import edu.wpi.first.util.RuntimeDetector;
+
+public final class DevMain {
+  /** Main entry point. */
+  public static void main(String[] args) {
+    System.out.println("Hello World!");
+    System.out.println(RuntimeDetector.getPlatformPath());
+    System.out.println(NetworkTablesJNI.now());
+    System.out.println(HALUtil.getHALRuntimeType());
+  }
+
+  private DevMain() {}
+}
diff --git a/third_party/allwpilib/romiVendordep/src/dev/native/cpp/main.cpp b/third_party/allwpilib/romiVendordep/src/dev/native/cpp/main.cpp
new file mode 100644
index 0000000..a3e363e
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/dev/native/cpp/main.cpp
@@ -0,0 +1,5 @@
+// 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.
+
+int main() {}
diff --git a/third_party/allwpilib/romiVendordep/src/main/java/edu/wpi/first/wpilibj/romi/OnBoardIO.java b/third_party/allwpilib/romiVendordep/src/main/java/edu/wpi/first/wpilibj/romi/OnBoardIO.java
new file mode 100644
index 0000000..02dcc2a
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/main/java/edu/wpi/first/wpilibj/romi/OnBoardIO.java
@@ -0,0 +1,150 @@
+// 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.
+
+package edu.wpi.first.wpilibj.romi;
+
+import edu.wpi.first.wpilibj.DigitalInput;
+import edu.wpi.first.wpilibj.DigitalOutput;
+import edu.wpi.first.wpilibj.DriverStation;
+import edu.wpi.first.wpilibj.Timer;
+
+/**
+ * This class represents the onboard IO of the Romi reference robot. This includes the pushbuttons
+ * and LEDs.
+ *
+ * <p>DIO 0 - Button A (input only) DIO 1 - Button B (input) or Green LED (output) DIO 2 - Button C
+ * (input) or Red LED (output) DIO 3 - Yellow LED (output only)
+ */
+public class OnBoardIO {
+  private final DigitalInput m_buttonA = new DigitalInput(0);
+  private final DigitalOutput m_yellowLed = new DigitalOutput(3);
+
+  // DIO 1
+  private final DigitalInput m_buttonB;
+  private final DigitalOutput m_greenLed;
+
+  // DIO 2
+  private final DigitalInput m_buttonC;
+  private final DigitalOutput m_redLed;
+
+  private static final double MESSAGE_INTERVAL = 1.0;
+  private double m_nextMessageTime;
+
+  public enum ChannelMode {
+    INPUT,
+    OUTPUT
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param dio1 Mode for DIO 1 (input = Button B, output = green LED)
+   * @param dio2 Mode for DIO 2 (input = Button C, output = red LED)
+   */
+  public OnBoardIO(ChannelMode dio1, ChannelMode dio2) {
+    if (dio1 == ChannelMode.INPUT) {
+      m_buttonB = new DigitalInput(1);
+      m_greenLed = null;
+    } else {
+      m_greenLed = new DigitalOutput(1);
+      m_buttonB = null;
+    }
+
+    if (dio2 == ChannelMode.INPUT) {
+      m_buttonC = new DigitalInput(2);
+      m_redLed = null;
+    } else {
+      m_redLed = new DigitalOutput(2);
+      m_buttonC = null;
+    }
+  }
+
+  /**
+   * Gets if the A button is pressed.
+   *
+   * @return Whether or not Button A is pressed
+   */
+  public boolean getButtonAPressed() {
+    return m_buttonA.get();
+  }
+
+  /**
+   * Gets if the B button is pressed.
+   *
+   * @return Whether or not Button B is pressed
+   */
+  public boolean getButtonBPressed() {
+    if (m_buttonB != null) {
+      return m_buttonB.get();
+    }
+
+    double currentTime = Timer.getFPGATimestamp();
+    if (currentTime > m_nextMessageTime) {
+      DriverStation.reportError("Button B was not configured", true);
+      m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
+    }
+    return false;
+  }
+
+  /**
+   * Gets if the C button is pressed.
+   *
+   * @return Whether or not Button C is pressed
+   */
+  public boolean getButtonCPressed() {
+    if (m_buttonC != null) {
+      return m_buttonC.get();
+    }
+
+    double currentTime = Timer.getFPGATimestamp();
+    if (currentTime > m_nextMessageTime) {
+      DriverStation.reportError("Button C was not configured", true);
+      m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
+    }
+    return false;
+  }
+
+  /**
+   * Sets the green LED.
+   *
+   * @param value Set whether or not to turn the Green LED on
+   */
+  public void setGreenLed(boolean value) {
+    if (m_greenLed != null) {
+      m_greenLed.set(value);
+    } else {
+      double currentTime = Timer.getFPGATimestamp();
+      if (currentTime > m_nextMessageTime) {
+        DriverStation.reportError("Green LED was not configured", true);
+        m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
+      }
+    }
+  }
+
+  /**
+   * Sets the red LED.
+   *
+   * @param value Set whether or not to turn the Red LED on
+   */
+  public void setRedLed(boolean value) {
+    if (m_redLed != null) {
+      m_redLed.set(value);
+    } else {
+      double currentTime = Timer.getFPGATimestamp();
+      if (currentTime > m_nextMessageTime) {
+        DriverStation.reportError("Red LED was not configured", true);
+        m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
+      }
+    }
+  }
+
+  /**
+   * Sets the yellow LED.
+   *
+   * @param value Set whether or not to turn the Yellow LED on
+   */
+  public void setYellowLed(boolean value) {
+    m_yellowLed.set(value);
+  }
+}
diff --git a/third_party/allwpilib/romiVendordep/src/main/java/edu/wpi/first/wpilibj/romi/RomiGyro.java b/third_party/allwpilib/romiVendordep/src/main/java/edu/wpi/first/wpilibj/romi/RomiGyro.java
new file mode 100644
index 0000000..40eaefc
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/main/java/edu/wpi/first/wpilibj/romi/RomiGyro.java
@@ -0,0 +1,147 @@
+// 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.
+
+package edu.wpi.first.wpilibj.romi;
+
+import edu.wpi.first.hal.SimDevice;
+import edu.wpi.first.hal.SimDevice.Direction;
+import edu.wpi.first.hal.SimDouble;
+
+public class RomiGyro {
+  private final SimDevice m_simDevice;
+  private final SimDouble m_simRateX;
+  private final SimDouble m_simRateY;
+  private final SimDouble m_simRateZ;
+  private final SimDouble m_simAngleX;
+  private final SimDouble m_simAngleY;
+  private final SimDouble m_simAngleZ;
+
+  private double m_angleXOffset;
+  private double m_angleYOffset;
+  private double m_angleZOffset;
+
+  /** Create a new RomiGyro. */
+  public RomiGyro() {
+    m_simDevice = SimDevice.create("Gyro:RomiGyro");
+    if (m_simDevice != null) {
+      m_simDevice.createBoolean("init", Direction.kOutput, true);
+      m_simRateX = m_simDevice.createDouble("rate_x", Direction.kInput, 0.0);
+      m_simRateY = m_simDevice.createDouble("rate_y", Direction.kInput, 0.0);
+      m_simRateZ = m_simDevice.createDouble("rate_z", Direction.kInput, 0.0);
+
+      m_simAngleX = m_simDevice.createDouble("angle_x", Direction.kInput, 0.0);
+      m_simAngleY = m_simDevice.createDouble("angle_y", Direction.kInput, 0.0);
+      m_simAngleZ = m_simDevice.createDouble("angle_z", Direction.kInput, 0.0);
+    } else {
+      m_simRateX = null;
+      m_simRateY = null;
+      m_simRateZ = null;
+      m_simAngleX = null;
+      m_simAngleY = null;
+      m_simAngleZ = null;
+    }
+  }
+
+  /**
+   * Get the rate of turn in degrees-per-second around the X-axis.
+   *
+   * @return rate of turn in degrees-per-second
+   */
+  public double getRateX() {
+    if (m_simRateX != null) {
+      return m_simRateX.get();
+    }
+
+    return 0.0;
+  }
+
+  /**
+   * Get the rate of turn in degrees-per-second around the Y-axis.
+   *
+   * @return rate of turn in degrees-per-second
+   */
+  public double getRateY() {
+    if (m_simRateY != null) {
+      return m_simRateY.get();
+    }
+
+    return 0.0;
+  }
+
+  /**
+   * Get the rate of turn in degrees-per-second around the Z-axis.
+   *
+   * @return rate of turn in degrees-per-second
+   */
+  public double getRateZ() {
+    if (m_simRateZ != null) {
+      return m_simRateZ.get();
+    }
+
+    return 0.0;
+  }
+
+  /**
+   * Get the currently reported angle around the X-axis.
+   *
+   * @return current angle around X-axis in degrees
+   */
+  public double getAngleX() {
+    if (m_simAngleX != null) {
+      return m_simAngleX.get() - m_angleXOffset;
+    }
+
+    return 0.0;
+  }
+
+  /**
+   * Get the currently reported angle around the X-axis.
+   *
+   * @return current angle around Y-axis in degrees
+   */
+  public double getAngleY() {
+    if (m_simAngleY != null) {
+      return m_simAngleY.get() - m_angleYOffset;
+    }
+
+    return 0.0;
+  }
+
+  /**
+   * Get the currently reported angle around the Z-axis.
+   *
+   * @return current angle around Z-axis in degrees
+   */
+  public double getAngleZ() {
+    if (m_simAngleZ != null) {
+      return m_simAngleZ.get() - m_angleZOffset;
+    }
+
+    return 0.0;
+  }
+
+  /** Reset the gyro angles to 0. */
+  public void reset() {
+    if (m_simAngleX != null) {
+      m_angleXOffset = m_simAngleX.get();
+      m_angleYOffset = m_simAngleY.get();
+      m_angleZOffset = m_simAngleZ.get();
+    }
+  }
+
+  public double getAngle() {
+    return getAngleZ();
+  }
+
+  public double getRate() {
+    return getRateZ();
+  }
+
+  /** Close out the SimDevice. */
+  public void close() {
+    if (m_simDevice != null) {
+      m_simDevice.close();
+    }
+  }
+}
diff --git a/third_party/allwpilib/romiVendordep/src/main/java/edu/wpi/first/wpilibj/romi/RomiMotor.java b/third_party/allwpilib/romiVendordep/src/main/java/edu/wpi/first/wpilibj/romi/RomiMotor.java
new file mode 100644
index 0000000..93024f6
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/main/java/edu/wpi/first/wpilibj/romi/RomiMotor.java
@@ -0,0 +1,33 @@
+// 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.
+
+package edu.wpi.first.wpilibj.romi;
+
+import edu.wpi.first.wpilibj.PWM;
+import edu.wpi.first.wpilibj.motorcontrol.PWMMotorController;
+
+/**
+ * RomiMotor.
+ *
+ * <p>A general use PWM motor controller representing the motors on a Romi robot
+ */
+public class RomiMotor extends PWMMotorController {
+  /** Common initialization code called by all constructors. */
+  protected void initRomiMotor() {
+    m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
+    m_pwm.setSpeed(0.0);
+    m_pwm.setZeroLatch();
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param channel The PWM channel that the RomiMotor is attached to. 0 is the left motor, 1 is the
+   *     right.
+   */
+  public RomiMotor(final int channel) {
+    super("Romi Motor", channel);
+    initRomiMotor();
+  }
+}
diff --git a/third_party/allwpilib/romiVendordep/src/main/native/cpp/romi/OnBoardIO.cpp b/third_party/allwpilib/romiVendordep/src/main/native/cpp/romi/OnBoardIO.cpp
new file mode 100644
index 0000000..cefe9ff
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/main/native/cpp/romi/OnBoardIO.cpp
@@ -0,0 +1,82 @@
+// 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 <frc/DigitalInput.h>
+#include <frc/DigitalOutput.h>
+#include <frc/Errors.h>
+#include <frc/Timer.h>
+#include <frc/romi/OnBoardIO.h>
+
+using namespace frc;
+
+OnBoardIO::OnBoardIO(OnBoardIO::ChannelMode dio1, OnBoardIO::ChannelMode dio2) {
+  if (dio1 == ChannelMode::INPUT) {
+    m_buttonB = std::make_unique<frc::DigitalInput>(1);
+  } else {
+    m_greenLed = std::make_unique<frc::DigitalOutput>(1);
+  }
+  if (dio2 == ChannelMode::INPUT) {
+    m_buttonC = std::make_unique<frc::DigitalInput>(2);
+  } else {
+    m_redLed = std::make_unique<frc::DigitalOutput>(2);
+  }
+}
+
+bool OnBoardIO::GetButtonAPressed() {
+  return m_buttonA.Get();
+}
+
+bool OnBoardIO::GetButtonBPressed() {
+  if (m_buttonB) {
+    return m_buttonB->Get();
+  }
+
+  auto currentTime = frc::Timer::GetFPGATimestamp();
+  if (currentTime > m_nextMessageTime) {
+    FRC_ReportError(frc::err::Error, "{}", "Button B was not configured");
+    m_nextMessageTime = currentTime + kMessageInterval;
+  }
+  return false;
+}
+
+bool OnBoardIO::GetButtonCPressed() {
+  if (m_buttonC) {
+    return m_buttonC->Get();
+  }
+
+  auto currentTime = frc::Timer::GetFPGATimestamp();
+  if (currentTime > m_nextMessageTime) {
+    FRC_ReportError(frc::err::Error, "{}", "Button C was not configured");
+    m_nextMessageTime = currentTime + kMessageInterval;
+  }
+  return false;
+}
+
+void OnBoardIO::SetGreenLed(bool value) {
+  if (m_greenLed) {
+    m_greenLed->Set(value);
+  } else {
+    auto currentTime = frc::Timer::GetFPGATimestamp();
+    if (currentTime > m_nextMessageTime) {
+      FRC_ReportError(frc::err::Error, "{}", "Green LED was not configured");
+      m_nextMessageTime = currentTime + kMessageInterval;
+    }
+  }
+}
+
+void OnBoardIO::SetRedLed(bool value) {
+  if (m_redLed) {
+    m_redLed->Set(value);
+  } else {
+    auto currentTime = frc::Timer::GetFPGATimestamp();
+    if (currentTime > m_nextMessageTime) {
+      FRC_ReportError(frc::err::Error, "{}", "Red LED was not configured");
+      m_nextMessageTime = currentTime + kMessageInterval;
+    }
+  }
+}
+
+void OnBoardIO::SetYellowLed(bool value) {
+  m_yellowLed.Set(value);
+}
diff --git a/third_party/allwpilib/romiVendordep/src/main/native/cpp/romi/RomiGyro.cpp b/third_party/allwpilib/romiVendordep/src/main/native/cpp/romi/RomiGyro.cpp
new file mode 100644
index 0000000..c44cff9
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/main/native/cpp/romi/RomiGyro.cpp
@@ -0,0 +1,89 @@
+// 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 <frc/romi/RomiGyro.h>
+
+using namespace frc;
+
+RomiGyro::RomiGyro() : m_simDevice("Gyro:RomiGyro") {
+  if (m_simDevice) {
+    m_simDevice.CreateBoolean("init", hal::SimDevice::kOutput, true);
+    m_simRateX =
+        m_simDevice.CreateDouble("rate_x", hal::SimDevice::kInput, 0.0);
+    m_simRateY =
+        m_simDevice.CreateDouble("rate_y", hal::SimDevice::kInput, 0.0);
+    m_simRateZ =
+        m_simDevice.CreateDouble("rate_z", hal::SimDevice::kInput, 0.0);
+    m_simAngleX =
+        m_simDevice.CreateDouble("angle_x", hal::SimDevice::kInput, 0.0);
+    m_simAngleY =
+        m_simDevice.CreateDouble("angle_y", hal::SimDevice::kInput, 0.0);
+    m_simAngleZ =
+        m_simDevice.CreateDouble("angle_z", hal::SimDevice::kInput, 0.0);
+  }
+}
+
+double RomiGyro::GetAngle() const {
+  return GetAngleZ();
+}
+
+double RomiGyro::GetRate() const {
+  return GetRateZ();
+}
+
+double RomiGyro::GetRateX() const {
+  if (m_simRateX) {
+    return m_simRateX.Get();
+  }
+
+  return 0.0;
+}
+
+double RomiGyro::GetRateY() const {
+  if (m_simRateY) {
+    return m_simRateY.Get();
+  }
+
+  return 0.0;
+}
+
+double RomiGyro::GetRateZ() const {
+  if (m_simRateZ) {
+    return m_simRateZ.Get();
+  }
+
+  return 0.0;
+}
+
+double RomiGyro::GetAngleX() const {
+  if (m_simAngleX) {
+    return m_simAngleX.Get() - m_angleXOffset;
+  }
+
+  return 0.0;
+}
+
+double RomiGyro::GetAngleY() const {
+  if (m_simAngleY) {
+    return m_simAngleY.Get() - m_angleYOffset;
+  }
+
+  return 0.0;
+}
+
+double RomiGyro::GetAngleZ() const {
+  if (m_simAngleZ) {
+    return m_simAngleZ.Get() - m_angleZOffset;
+  }
+
+  return 0.0;
+}
+
+void RomiGyro::Reset() {
+  if (m_simAngleX) {
+    m_angleXOffset = m_simAngleX.Get();
+    m_angleYOffset = m_simAngleY.Get();
+    m_angleZOffset = m_simAngleZ.Get();
+  }
+}
diff --git a/third_party/allwpilib/romiVendordep/src/main/native/cpp/romi/RomiMotor.cpp b/third_party/allwpilib/romiVendordep/src/main/native/cpp/romi/RomiMotor.cpp
new file mode 100644
index 0000000..729dc03
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/main/native/cpp/romi/RomiMotor.cpp
@@ -0,0 +1,13 @@
+// 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 <frc/romi/RomiMotor.h>
+
+using namespace frc;
+
+RomiMotor::RomiMotor(int channel) : PWMMotorController("Romi Motor", channel) {
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+}
diff --git a/third_party/allwpilib/romiVendordep/src/main/native/include/frc/romi/OnBoardIO.h b/third_party/allwpilib/romiVendordep/src/main/native/include/frc/romi/OnBoardIO.h
new file mode 100644
index 0000000..8e08a35
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/main/native/include/frc/romi/OnBoardIO.h
@@ -0,0 +1,76 @@
+// 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 <frc/DigitalInput.h>
+#include <frc/DigitalOutput.h>
+#include <units/time.h>
+
+namespace frc {
+
+/**
+ * This class represents the onboard IO of the Romi
+ * reference robot. This includes the pushbuttons and
+ * LEDs.
+ *
+ * <p>DIO 0 - Button A (input only)
+ * DIO 1 - Button B (input) or Green LED (output)
+ * DIO 2 - Button C (input) or Red LED (output)
+ * DIO 3 - Yellow LED (output only)
+ */
+class OnBoardIO {
+ public:
+  enum ChannelMode { INPUT, OUTPUT };
+  OnBoardIO(OnBoardIO::ChannelMode dio1, OnBoardIO::ChannelMode dio2);
+
+  static constexpr auto kMessageInterval = 1_s;
+  units::second_t m_nextMessageTime = 0_s;
+
+  /**
+   * Gets if the A button is pressed.
+   */
+  bool GetButtonAPressed();
+
+  /**
+   * Gets if the B button is pressed.
+   */
+  bool GetButtonBPressed();
+
+  /**
+   * Gets if the C button is pressed.
+   */
+  bool GetButtonCPressed();
+
+  /**
+   * Sets the green LED.
+   */
+  void SetGreenLed(bool value);
+
+  /**
+   * Sets the red LED.
+   */
+  void SetRedLed(bool value);
+
+  /**
+   * Sets the yellow LED.
+   */
+  void SetYellowLed(bool value);
+
+ private:
+  frc::DigitalInput m_buttonA{0};
+  frc::DigitalOutput m_yellowLed{3};
+
+  // DIO 1
+  std::unique_ptr<frc::DigitalInput> m_buttonB;
+  std::unique_ptr<frc::DigitalOutput> m_greenLed;
+
+  // DIO 2
+  std::unique_ptr<frc::DigitalInput> m_buttonC;
+  std::unique_ptr<frc::DigitalOutput> m_redLed;
+};
+
+}  // namespace frc
diff --git a/third_party/allwpilib/romiVendordep/src/main/native/include/frc/romi/RomiGyro.h b/third_party/allwpilib/romiVendordep/src/main/native/include/frc/romi/RomiGyro.h
new file mode 100644
index 0000000..363daf3
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/main/native/include/frc/romi/RomiGyro.h
@@ -0,0 +1,91 @@
+// 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 <hal/SimDevice.h>
+
+namespace frc {
+
+/**
+ * Use a rate gyro to return the robots heading relative to a starting position.
+ *
+ * This class is for the Romi onboard gyro, and will only work in
+ * simulation/Romi mode. Only one instance of a RomiGyro is supported.
+ */
+class RomiGyro {
+ public:
+  RomiGyro();
+
+  /**
+   * Return the actual angle in degrees that the robot is currently facing.
+   *
+   * The angle is based on integration of the returned rate form the gyro.
+   * The angle is continuous, that is, it will continue from 360->361 degrees.
+   * This allows algorithms that wouldn't want to see a discontinuity in the
+   * gyro output as it sweeps from 360 to 0 on the second time around.
+   *
+   * @return the current heading of the robot in degrees.
+   */
+  double GetAngle() const;
+
+  /**
+   * Return the rate of rotation of the gyro
+   *
+   * The rate is based on the most recent reading of the gyro.
+   *
+   * @return the current rate in degrees per second
+   */
+  double GetRate() const;
+
+  /**
+   * Gets the rate of turn in degrees-per-second around the X-axis
+   */
+  double GetRateX() const;
+
+  /**
+   * Gets the rate of turn in degrees-per-second around the Y-axis
+   */
+  double GetRateY() const;
+
+  /**
+   * Gets the rate of turn in degrees-per-second around the Z-axis
+   */
+  double GetRateZ() const;
+
+  /**
+   * Gets the currently reported angle around the X-axis
+   */
+  double GetAngleX() const;
+
+  /**
+   * Gets the currently reported angle around the X-axis
+   */
+  double GetAngleY() const;
+
+  /**
+   * Gets the currently reported angle around the X-axis
+   */
+  double GetAngleZ() const;
+
+  /**
+   * Resets the gyro
+   */
+  void Reset();
+
+ private:
+  hal::SimDevice m_simDevice;
+  hal::SimDouble m_simRateX;
+  hal::SimDouble m_simRateY;
+  hal::SimDouble m_simRateZ;
+  hal::SimDouble m_simAngleX;
+  hal::SimDouble m_simAngleY;
+  hal::SimDouble m_simAngleZ;
+
+  double m_angleXOffset = 0;
+  double m_angleYOffset = 0;
+  double m_angleZOffset = 0;
+};
+
+}  // namespace frc
diff --git a/third_party/allwpilib/romiVendordep/src/main/native/include/frc/romi/RomiMotor.h b/third_party/allwpilib/romiVendordep/src/main/native/include/frc/romi/RomiMotor.h
new file mode 100644
index 0000000..4bb9250
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/main/native/include/frc/romi/RomiMotor.h
@@ -0,0 +1,30 @@
+// 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 <frc/motorcontrol/PWMMotorController.h>
+
+namespace frc {
+
+/**
+ * RomiMotor
+ *
+ * A general use PWM motor controller representing the motors on a Romi robot
+ */
+class RomiMotor : public PWMMotorController {
+ public:
+  /**
+   * Constructor for a RomiMotor.
+   *
+   * @param channel The PWM channel that the RomiMotor is attached to.
+   *                0 is left, 1 is right
+   */
+  explicit RomiMotor(int channel);
+
+  RomiMotor(RomiMotor&&) = default;
+  RomiMotor& operator=(RomiMotor&&) = default;
+};
+
+}  // namespace frc
diff --git a/third_party/allwpilib/romiVendordep/src/test/native/cpp/main.cpp b/third_party/allwpilib/romiVendordep/src/test/native/cpp/main.cpp
new file mode 100644
index 0000000..2d710be
--- /dev/null
+++ b/third_party/allwpilib/romiVendordep/src/test/native/cpp/main.cpp
@@ -0,0 +1,10 @@
+// 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 <gtest/gtest.h>

+

+int main(int argc, char** argv) {

+  ::testing::InitGoogleTest(&argc, argv);

+  return RUN_ALL_TESTS();

+}

diff --git a/third_party/allwpilib/settings.gradle b/third_party/allwpilib/settings.gradle
index 818046b..38ebe2a 100644
--- a/third_party/allwpilib/settings.gradle
+++ b/third_party/allwpilib/settings.gradle
@@ -10,7 +10,7 @@
 }
 
 plugins {
-    id "com.gradle.enterprise" version "3.0"
+    id "com.gradle.enterprise" version "3.15.1"
 }
 
 // Set the flag to tell gradle to ignore unresolved headers
@@ -33,25 +33,31 @@
 include 'wpilibjExamples'
 include 'wpilibjIntegrationTests'
 include 'wpilibj'
+include 'wpiunits'
 include 'crossConnIntegrationTests'
 include 'fieldImages'
 include 'glass'
 include 'outlineviewer'
 include 'roborioteamnumbersetter'
 include 'datalogtool'
+include 'sysid'
 include 'simulation:halsim_ds_socket'
 include 'simulation:halsim_gui'
 include 'simulation:halsim_ws_core'
 include 'simulation:halsim_ws_client'
 include 'simulation:halsim_ws_server'
+include 'simulation:halsim_xrp'
 include 'cameraserver'
 include 'cameraserver:multiCameraServer'
 include 'wpilibNewCommands'
+include 'romiVendordep'
+include 'xrpVendordep'
 include 'myRobot'
 include 'docs'
 include 'msvcruntime'
 include 'ntcoreffi'
 include 'apriltag'
+include 'processstarter'
 
 buildCache {
     def cred = {
@@ -65,7 +71,7 @@
         enabled = !System.getenv().containsKey("CI")
     }
     remote(HttpBuildCache) {
-        url = "https://frcmaven.wpi.edu/artifactory/wpilib-generic-gradle-cache/"
+        url = "https://frcmaven.wpi.edu/artifactory/wpilib-generic-gradlecache/"
         String user = cred('ARTIFACTORY_PUBLISH_USERNAME')
         String pass = cred('ARTIFACTORY_PUBLISH_PASSWORD')
         if (user && pass) {
diff --git a/third_party/allwpilib/shared/apriltaglib.gradle b/third_party/allwpilib/shared/apriltaglib.gradle
index 1e508de..b91a70f 100644
--- a/third_party/allwpilib/shared/apriltaglib.gradle
+++ b/third_party/allwpilib/shared/apriltaglib.gradle
@@ -1,12 +1,12 @@
 nativeUtils {
     nativeDependencyContainer {
         apriltaglib(getNativeDependencyTypeClass('WPIStaticMavenDependency')) {
-            groupId = "edu.wpi.first.thirdparty.frc2023"
+            groupId = "edu.wpi.first.thirdparty.frc2024"
             artifactId = "apriltaglib"
             headerClassifier = "headers"
             sourceClassifier = "sources"
             ext = "zip"
-            version = '3.2.0-3'
+            version = '3.3.0-1'
             targetPlatforms.addAll(nativeUtils.wpi.platforms.allPlatforms)
         }
     }
diff --git a/third_party/allwpilib/shared/config.gradle b/third_party/allwpilib/shared/config.gradle
index 8036cca..36aee7f 100644
--- a/third_party/allwpilib/shared/config.gradle
+++ b/third_party/allwpilib/shared/config.gradle
@@ -13,9 +13,11 @@
 nativeUtils {
     wpi {
         configureDependencies {
-            niLibVersion = "2023.3.0"
-            opencvVersion = "4.6.0-3"
-            googleTestVersion = "1.12.1-1"
+            opencvYear = "frc2024"
+            googleTestYear = "frc2024"
+            niLibVersion = "2024.1.1"
+            opencvVersion = "4.8.0-2"
+            googleTestVersion = "1.14.0-1"
         }
     }
 }
@@ -41,18 +43,18 @@
     }
 }
 
+// Compress debug info on Linux
+nativeUtils.platformConfigs.each {
+  if (it.name.contains('linux')) {
+    it.cppCompiler.debugArgs.add("-gz=zlib")
+  }
+}
+
 // NativeUtils adds the following OpenCV warning suppression for Linux, but not
 // for macOS
 // https://github.com/opencv/opencv/issues/20269
 nativeUtils.platformConfigs.osxuniversal.cppCompiler.args.add("-Wno-deprecated-anon-enum-enum-conversion")
 
-// NativeUtils uses the wrong compiler arguments for roboRIO targets, but it's
-// too late to fix NativeUtils for the 2023 season. This temporarily overwrites
-// the flags.
-nativeUtils.platformConfigs.named(nativeUtils.wpi.platforms.roborio).configure {
-    cppCompiler.args.remove('-Wno-error=deprecated-declarations')
-}
-
 nativeUtils.platformConfigs.linuxathena.linker.args.add("-Wl,--fatal-warnings")
 
 model {
@@ -138,7 +140,7 @@
         def task = project.tasks.create(base + "-${key}", type) {
             description = 'Creates component archive for platform ' + key
             destinationDirectory = outputsFolder
-            classifier = key
+            archiveClassifier = key
             archiveBaseName = '_M_' + base
             duplicatesStrategy = 'exclude'
 
diff --git a/third_party/allwpilib/shared/cppJavaDesktopTestTask.gradle b/third_party/allwpilib/shared/cppJavaDesktopTestTask.gradle
new file mode 100644
index 0000000..475c38c
--- /dev/null
+++ b/third_party/allwpilib/shared/cppJavaDesktopTestTask.gradle
@@ -0,0 +1,7 @@
+apply from: "${rootDir}/shared/cppDesktopTestTask.gradle"
+apply from: "${rootDir}/shared/javaDesktopTestTask.gradle"
+
+tasks.register('testDesktop') {
+    dependsOn testDesktopJava
+    dependsOn testDesktopCpp
+}
diff --git a/third_party/allwpilib/shared/examplecheck.gradle b/third_party/allwpilib/shared/examplecheck.gradle
index 3d05c67..9463ea1 100644
--- a/third_party/allwpilib/shared/examplecheck.gradle
+++ b/third_party/allwpilib/shared/examplecheck.gradle
@@ -42,6 +42,48 @@
     }
 }
 
+def tagList = [
+        /* --- Categories --- */
+        // On-RIO image processing
+        "Vision",
+        // Command-based
+        "Command-based",
+        // Romi
+        "Romi",
+        // XRP
+        "XRP",
+        // Extremely simple programs showcasing a single hardware API
+        "Hardware",
+        // Full robot, with multiple mechanisms
+        "Complete Robot",
+        // A single mechanism in the Robot class
+        "Basic Robot",
+
+        /* --- Mechanisms --- */
+        "Intake", "Flywheel", "Elevator", "Arm", "Differential Drive", "Mecanum Drive",
+        "Swerve Drive",
+
+        /* --- Telemetry --- */
+        "SmartDashboard", "Shuffleboard", "Sendable", "DataLog",
+
+        /* --- Controls --- */
+        "Exponential Profile", "PID", "State-Space", "Ramsete", "Path Following", "Trajectory",
+        "SysId", "Simulation", "Trapezoid Profile", "Profiled PID", "Odometry", "LQR",
+        "Pose Estimator",
+
+        /* --- Hardware --- */
+        "Analog", "Ultrasonic", "Gyro", "Pneumatics", "I2C", "Duty Cycle", "PDP", "DMA", "Relay",
+        "AddressableLEDs", "HAL", "Encoder", "Smart Motor Controller", "Digital Input",
+        "Digital Output",
+
+        /* --- HID --- */
+        "XboxController", "PS4Controller", "PS5Controller", "Joystick",
+
+        /* --- Misc --- */
+        /* (try to keep this section minimal) */
+        "EventLoop", "AprilTags", "Mechanism2d", "Preferences",
+]
+
 task checkExamples(type: Task) {
     doLast {
         def parsedJson = new groovy.json.JsonSlurper().parseText(exampleFile.text)
@@ -50,6 +92,7 @@
             assert it.name != null
             assert it.description != null
             assert it.tags != null
+            assert it.tags.findAll { !tagList.contains(it) }.empty
             assert it.foldername != null
             assert it.gradlebase != null
             assert it.commandversion != null
diff --git a/third_party/allwpilib/shared/imgui.gradle b/third_party/allwpilib/shared/imgui.gradle
index 04d4265..5ffdc52 100644
--- a/third_party/allwpilib/shared/imgui.gradle
+++ b/third_party/allwpilib/shared/imgui.gradle
@@ -1,12 +1,12 @@
 nativeUtils {
     nativeDependencyContainer {
         imgui(getNativeDependencyTypeClass('WPIStaticMavenDependency')) {
-            groupId = "edu.wpi.first.thirdparty.frc2023"
+            groupId = "edu.wpi.first.thirdparty.frc2024"
             artifactId = "imgui"
             headerClassifier = "headers"
             sourceClassifier = "sources"
             ext = "zip"
-            version = '1.89.1-1'
+            version = '1.89.9-1'
             targetPlatforms.addAll(nativeUtils.wpi.platforms.allPlatforms)
         }
     }
diff --git a/third_party/allwpilib/shared/java/javacommon.gradle b/third_party/allwpilib/shared/java/javacommon.gradle
index 4c57f1f..32078f8 100644
--- a/third_party/allwpilib/shared/java/javacommon.gradle
+++ b/third_party/allwpilib/shared/java/javacommon.gradle
@@ -1,6 +1,7 @@
 apply plugin: 'maven-publish'
 apply plugin: 'java-library'
 apply plugin: 'jacoco'
+apply plugin: 'com.google.protobuf'
 
 def baseArtifactId = project.baseId
 def artifactGroupId = project.groupId
@@ -9,12 +10,12 @@
 def outputsFolder = file("$project.buildDir/outputs")
 
 task sourcesJar(type: Jar, dependsOn: classes) {
-    classifier = 'sources'
+    archiveClassifier = 'sources'
     from sourceSets.main.allSource
 }
 
 task javadocJar(type: Jar, dependsOn: javadoc) {
-    classifier = 'javadoc'
+    archiveClassifier = 'javadoc'
     from javadoc.destinationDir
 }
 
@@ -27,14 +28,14 @@
 task outputSourcesJar(type: Jar, dependsOn: classes) {
     archiveBaseName = javaBaseName
     destinationDirectory = outputsFolder
-    classifier = 'sources'
+    archiveClassifier = 'sources'
     from sourceSets.main.allSource
 }
 
 task outputJavadocJar(type: Jar, dependsOn: javadoc) {
     archiveBaseName = javaBaseName
     destinationDirectory = outputsFolder
-    classifier = 'javadoc'
+    archiveClassifier = 'javadoc'
     from javadoc.destinationDir
 }
 
@@ -81,7 +82,7 @@
     finalizedBy jacocoTestReport
 }
 
-if (project.hasProperty('onlylinuxathena') || project.hasProperty('onlylinuxarm32') || project.hasProperty('onlylinuxarm64')) {
+if (project.hasProperty('onlylinuxathena') || project.hasProperty('onlylinuxarm32') || project.hasProperty('onlylinuxarm64') || project.hasProperty('onlywindowsarm64')) {
     test.enabled = false
 }
 
@@ -96,6 +97,10 @@
     dev
 }
 
+configurations {
+    devImplementation.extendsFrom(implementation)
+}
+
 tasks.withType(JavaCompile).configureEach {
     options.compilerArgs = [
         '--release',
@@ -112,9 +117,9 @@
 }
 
 dependencies {
-    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
-    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'
-    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
+    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
+    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0'
+    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
 
     devImplementation sourceSets.main.output
 }
@@ -128,7 +133,7 @@
 build.dependsOn devClasses
 
 jacoco {
-    toolVersion = "0.8.8"
+    toolVersion = "0.8.10"
 }
 
 jacocoTestReport {
@@ -137,3 +142,27 @@
         html.required = true
     }
 }
+
+protobuf {
+    protoc {
+        artifact = 'com.google.protobuf:protoc:3.21.12'
+    }
+    plugins {
+        quickbuf {
+            artifact = 'us.hebi.quickbuf:protoc-gen-quickbuf:1.3.2'
+        }
+    }
+    generateProtoTasks {
+        all().configureEach { task ->
+            task.builtins {
+                cpp {}
+                remove java
+            }
+            task.plugins {
+                quickbuf {
+                    option "gen_descriptors=true"
+                }
+            }
+        }
+    }
+}
diff --git a/third_party/allwpilib/shared/java/javastyle.gradle b/third_party/allwpilib/shared/java/javastyle.gradle
index 6259c2a..49332bf 100644
--- a/third_party/allwpilib/shared/java/javastyle.gradle
+++ b/third_party/allwpilib/shared/java/javastyle.gradle
@@ -2,7 +2,7 @@
     apply plugin: 'checkstyle'
 
     checkstyle {
-        toolVersion = "10.1"
+        toolVersion = "10.12.2"
         configDirectory = file("${project.rootDir}/styleguide")
         config = resources.text.fromFile(new File(configDirectory.get().getAsFile(), "checkstyle.xml"))
     }
@@ -10,7 +10,7 @@
     apply plugin: 'pmd'
 
     pmd {
-        toolVersion = '6.44.0'
+        toolVersion = '6.55.0'
         consoleOutput = true
         reportsDir = file("$project.buildDir/reports/pmd")
         ruleSetFiles = files(new File(rootDir, "styleguide/pmd-ruleset.xml"))
@@ -23,7 +23,7 @@
         java {
             target fileTree('.') {
                 include '**/*.java'
-                exclude '**/build/**', '**/build-*/**'
+                exclude '**/build/**', '**/build-*/**', '**/bin/**'
             }
             toggleOffOn()
             googleJavaFormat()
@@ -34,17 +34,26 @@
         groovyGradle {
             target fileTree('.') {
                 include '**/*.gradle'
-                exclude '**/build/**', '**/build-*/**'
+                exclude '**/build/**', '**/build-*/**', '**/bin/**'
             }
             greclipse()
             indentWithSpaces(4)
             trimTrailingWhitespace()
             endWithNewline()
         }
+        json {
+            target fileTree('.') {
+                include '**/*.json'
+                exclude '**/build/**', '**/build-*/**', '**/bin/**'
+                exclude '**/simgui-ds.json', '**/simgui-window.json', '**/simgui.json', '**/networktables.json'
+            }
+            gson()
+              .indentWithSpaces(2)
+        }
         format 'xml', {
             target fileTree('.') {
                 include '**/*.xml'
-                exclude '**/build/**', '**/build-*/**'
+                exclude '**/build/**', '**/build-*/**', '**/bin/**', '**/.idea/**', '**/.run/**'
             }
             eclipseWtp('xml')
             trimTrailingWhitespace()
@@ -54,7 +63,7 @@
         format 'misc', {
             target fileTree('.') {
                 include '**/*.md', '**/.gitignore'
-                exclude '**/build/**', '**/build-*/**'
+                exclude '**/build/**', '**/build-*/**', '**/bin/**'
             }
             trimTrailingWhitespace()
             indentWithSpaces(2)
diff --git a/third_party/allwpilib/shared/javacpp/publish.gradle b/third_party/allwpilib/shared/javacpp/publish.gradle
index 201c023..7599a74 100644
--- a/third_party/allwpilib/shared/javacpp/publish.gradle
+++ b/third_party/allwpilib/shared/javacpp/publish.gradle
@@ -11,7 +11,7 @@
 task cppSourcesZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = zipBaseName
-    classifier = "sources"
+    archiveClassifier = "sources"
 
     from(licenseFile) {
         into '/'
@@ -25,7 +25,7 @@
 task cppHeadersZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = zipBaseName
-    classifier = "headers"
+    archiveClassifier = "headers"
 
     from(licenseFile) {
         into '/'
diff --git a/third_party/allwpilib/shared/javacpp/setupBuild.gradle b/third_party/allwpilib/shared/javacpp/setupBuild.gradle
index 04086ad..1f3ff10 100644
--- a/third_party/allwpilib/shared/javacpp/setupBuild.gradle
+++ b/third_party/allwpilib/shared/javacpp/setupBuild.gradle
@@ -30,11 +30,11 @@
             sources {
                 cpp {
                     source {
-                        srcDirs 'src/main/native/cpp'
-                        include '**/*.cpp'
+                        srcDirs 'src/main/native/cpp', "$buildDir/generated/source/proto/main/cpp"
+                        include '**/*.cpp', '**/*.cc'
                     }
                     exportedHeaders {
-                        srcDirs 'src/main/native/include'
+                        srcDirs 'src/main/native/include', "$buildDir/generated/source/proto/main/cpp"
                     }
                 }
             }
@@ -43,6 +43,9 @@
                     it.buildable = false
                     return
                 }
+                it.tasks.withType(CppCompile) {
+                    it.dependsOn generateProto
+                }
                 if (project.hasProperty('extraSetup')) {
                     extraSetup(it)
                 }
@@ -78,6 +81,31 @@
                     }
                 }
             }
+            binaries.all {
+                lib library: nativeName, linkage: 'shared'
+                if (!project.hasProperty('noWpiutil')) {
+                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                        nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
+                    }
+                }
+                if (project.hasProperty('exeSplitSetup')) {
+                    exeSplitSetup(it)
+                }
+            }
+        }
+        "${nativeName}TestLib"(NativeLibrarySpec) {
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/testlib/native/cpp'
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/test/native/include'
+                    }
+                }
+            }
         }
     }
     testSuites {
@@ -104,6 +132,17 @@
     binaries {
         withType(GoogleTestTestSuiteBinarySpec) {
             lib library: nativeName, linkage: 'shared'
+            lib library: "${nativeName}TestLib", linkage: 'shared'
+            if (!project.hasProperty('noWpiutil')) {
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                lib project: ':wpiutil', library: 'wpiutilTestLib', linkage: 'shared'
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
+                }
+            }
+            if (project.hasProperty('exeSplitSetup')) {
+                exeSplitSetup(it)
+            }
         }
     }
     tasks {
@@ -125,10 +164,12 @@
                                 test.dependsOn it.tasks.install
                                 test.systemProperty 'java.library.path', filePath
                                 test.environment 'LD_LIBRARY_PATH', filePath
+                                test.environment 'DYLD_LIBRARY_PATH', filePath
                                 test.workingDir filePath
                                 run.dependsOn it.tasks.install
                                 run.systemProperty 'java.library.path', filePath
                                 run.environment 'LD_LIBRARY_PATH', filePath
+                                run.environment 'DYLD_LIBRARY_PATH', filePath
                                 run.workingDir filePath
 
                                 found = true
@@ -141,8 +182,7 @@
     }
 }
 
-apply from: "${rootDir}/shared/cppDesktopTestTask.gradle"
-apply from: "${rootDir}/shared/javaDesktopTestTask.gradle"
+apply from: "${rootDir}/shared/cppJavaDesktopTestTask.gradle"
 
 tasks.withType(RunTestExecutable) {
     args "--gtest_output=xml:test_detail.xml"
diff --git a/third_party/allwpilib/shared/jni/publish.gradle b/third_party/allwpilib/shared/jni/publish.gradle
index 8745caf..a33ca82 100644
--- a/third_party/allwpilib/shared/jni/publish.gradle
+++ b/third_party/allwpilib/shared/jni/publish.gradle
@@ -6,6 +6,7 @@
 def baseArtifactId = nativeName
 def artifactGroupId = "edu.wpi.first.${nativeName}"
 def zipBaseName = "_GROUP_edu_wpi_first_${nativeName}_ID_${nativeName}-cpp_CLS"
+ext.zipBaseName = zipBaseName
 def jniBaseName = "_GROUP_edu_wpi_first_${nativeName}_ID_${nativeName}-jni_CLS"
 def jniCvStaticBaseName = "_GROUP_edu_wpi_first_${nativeName}_ID_${nativeName}-jnicvstatic_CLS"
 
@@ -14,7 +15,7 @@
 task cppSourcesZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = zipBaseName
-    classifier = "sources"
+    archiveClassifier = "sources"
     duplicatesStrategy = 'exclude'
 
     from(licenseFile) {
@@ -44,7 +45,7 @@
 task cppHeadersZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = zipBaseName
-    classifier = "headers"
+    archiveClassifier = "headers"
 
     from(licenseFile) {
         into '/'
diff --git a/third_party/allwpilib/shared/jni/setupBuild.gradle b/third_party/allwpilib/shared/jni/setupBuild.gradle
index 4d8894f..9f9334f 100644
--- a/third_party/allwpilib/shared/jni/setupBuild.gradle
+++ b/third_party/allwpilib/shared/jni/setupBuild.gradle
@@ -41,15 +41,15 @@
             sources {
                 cpp {
                     source {
-                        srcDirs 'src/main/native/cpp'
+                        srcDirs 'src/main/native/cpp', "$buildDir/generated/source/proto/main/cpp"
                         if (project.hasProperty('generatedSources')) {
                             srcDir generatedSources
                         }
-                        include '**/*.cpp'
+                        include '**/*.cpp', '**/*.cc'
                         exclude '**/jni/**/*.cpp'
                     }
                     exportedHeaders {
-                        srcDir 'src/main/native/include'
+                        srcDirs 'src/main/native/include', "$buildDir/generated/source/proto/main/cpp"
                         if (project.hasProperty('generatedHeaders')) {
                             srcDir generatedHeaders
                         }
@@ -67,6 +67,9 @@
                 if (!project.hasProperty('noWpiutil')) {
                     lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
                 }
+                it.tasks.withType(CppCompile) {
+                    it.dependsOn generateProto
+                }
                 if (project.hasProperty('splitSetup')) {
                     splitSetup(it)
                 }
@@ -198,7 +201,6 @@
             targetBuildTypes 'debug'
             sources {
                 cpp {
-
                     source {
                         srcDirs 'src/dev/native/cpp'
                         include '**/*.cpp'
@@ -217,12 +219,28 @@
                 if (!project.hasProperty('noWpiutil')) {
                     lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
                     lib project: ':wpiutil', library: 'wpiutilJNIShared', linkage: 'shared'
+                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                        nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
+                    }
                 }
                 if (project.hasProperty('exeSplitSetup')) {
                     exeSplitSetup(it)
                 }
             }
         }
+        "${nativeName}TestLib"(NativeLibrarySpec) {
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/testlib/native/cpp'
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/test/native/include'
+                    }
+                }
+            }
+        }
     }
     testSuites {
         "${nativeName}Test"(GoogleTestTestSuiteSpec) {
@@ -253,6 +271,10 @@
             lib library: nativeName, linkage: 'shared'
             if (!project.hasProperty('noWpiutil')) {
                 lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                lib project: ':wpiutil', library: 'wpiutilTestLib', linkage: 'shared'
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
+                }
             }
             if (project.hasProperty('exeSplitSetup')) {
                 exeSplitSetup(it)
@@ -287,10 +309,12 @@
 
                                 test.systemProperty 'java.library.path', filePath
                                 test.environment 'LD_LIBRARY_PATH', filePath
+                                test.environment 'DYLD_LIBRARY_PATH', filePath
                                 test.workingDir filePath
                                 run.dependsOn it.tasks.install
                                 run.systemProperty 'java.library.path', filePath
                                 run.environment 'LD_LIBRARY_PATH', filePath
+                                run.environment 'DYLD_LIBRARY_PATH', filePath
                                 run.workingDir filePath
 
                                 found = true
@@ -303,8 +327,7 @@
     }
 }
 
-apply from: "${rootDir}/shared/cppDesktopTestTask.gradle"
-apply from: "${rootDir}/shared/javaDesktopTestTask.gradle"
+apply from: "${rootDir}/shared/cppJavaDesktopTestTask.gradle"
 
 ext.getJniSpecClass = {
     return JniNativeLibrarySpec
diff --git a/third_party/allwpilib/shared/libssh.gradle b/third_party/allwpilib/shared/libssh.gradle
index 743dd57..c62d47d 100644
--- a/third_party/allwpilib/shared/libssh.gradle
+++ b/third_party/allwpilib/shared/libssh.gradle
@@ -1,12 +1,12 @@
 nativeUtils {
     nativeDependencyContainer {
         libssh(getNativeDependencyTypeClass('WPIStaticMavenDependency')) {
-            groupId = "edu.wpi.first.thirdparty.frc2023"
+            groupId = "edu.wpi.first.thirdparty.frc2024"
             artifactId = "libssh"
             headerClassifier = "headers"
             sourceClassifier = "sources"
             ext = "zip"
-            version = '0.95-6'
+            version = '0.105-1'
             targetPlatforms.addAll(nativeUtils.wpi.platforms.allPlatforms)
         }
     }
diff --git a/third_party/allwpilib/shared/opencv.gradle b/third_party/allwpilib/shared/opencv.gradle
index 40d3c34..7f5e881 100644
--- a/third_party/allwpilib/shared/opencv.gradle
+++ b/third_party/allwpilib/shared/opencv.gradle
@@ -1,4 +1,4 @@
-def opencvVersion = '4.6.0-2'
+def opencvVersion = '4.8.0-2'
 
 if (project.hasProperty('useCpp') && project.useCpp) {
     model {
@@ -22,12 +22,12 @@
 
 if (project.hasProperty('useJava') && project.useJava) {
     dependencies {
-        implementation "edu.wpi.first.thirdparty.frc2023.opencv:opencv-java:${opencvVersion}"
+        implementation "edu.wpi.first.thirdparty.frc2024.opencv:opencv-java:${opencvVersion}"
         if (!project.hasProperty('skipDev') || !project.skipDev) {
-            devImplementation "edu.wpi.first.thirdparty.frc2023.opencv:opencv-java:${opencvVersion}"
+            devImplementation "edu.wpi.first.thirdparty.frc2024.opencv:opencv-java:${opencvVersion}"
         }
         if (project.hasProperty('useDocumentation') && project.useDocumentation) {
-            javaSource "edu.wpi.first.thirdparty.frc2023.opencv:opencv-java:${opencvVersion}:sources"
+            javaSource "edu.wpi.first.thirdparty.frc2024.opencv:opencv-java:${opencvVersion}:sources"
         }
     }
 }
diff --git a/third_party/allwpilib/shared/plugins/publish.gradle b/third_party/allwpilib/shared/plugins/publish.gradle
index 26caad9..8d7b9d4 100644
--- a/third_party/allwpilib/shared/plugins/publish.gradle
+++ b/third_party/allwpilib/shared/plugins/publish.gradle
@@ -9,7 +9,7 @@
 task cppSourcesZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = zipBaseName
-    classifier = "sources"
+    archiveClassifier = "sources"
 
     from(licenseFile) {
         into '/'
@@ -23,7 +23,7 @@
 task cppHeadersZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = zipBaseName
-    classifier = "headers"
+    archiveClassifier = "headers"
 
     from(licenseFile) {
         into '/'
diff --git a/third_party/allwpilib/simulation/CMakeLists.txt b/third_party/allwpilib/simulation/CMakeLists.txt
index 7a54c48..0a1a953 100644
--- a/third_party/allwpilib/simulation/CMakeLists.txt
+++ b/third_party/allwpilib/simulation/CMakeLists.txt
@@ -5,3 +5,4 @@
 add_subdirectory(halsim_ws_core)
 add_subdirectory(halsim_ws_client)
 add_subdirectory(halsim_ws_server)
+add_subdirectory(halsim_xrp)
diff --git a/third_party/allwpilib/simulation/halsim_ds_socket/CMakeLists.txt b/third_party/allwpilib/simulation/halsim_ds_socket/CMakeLists.txt
index 6d770d9..4eb23bb 100644
--- a/third_party/allwpilib/simulation/halsim_ds_socket/CMakeLists.txt
+++ b/third_party/allwpilib/simulation/halsim_ds_socket/CMakeLists.txt
@@ -13,4 +13,4 @@
 
 set_property(TARGET halsim_ds_socket PROPERTY FOLDER "libraries")
 
-install(TARGETS halsim_ds_socket EXPORT halsim_ds_socket DESTINATION "${main_lib_dest}")
+install(TARGETS halsim_ds_socket EXPORT halsim_ds_socket)
diff --git a/third_party/allwpilib/simulation/halsim_ds_socket/build.gradle b/third_party/allwpilib/simulation/halsim_ds_socket/build.gradle
index 0e90938..b9a39a8 100644
--- a/third_party/allwpilib/simulation/halsim_ds_socket/build.gradle
+++ b/third_party/allwpilib/simulation/halsim_ds_socket/build.gradle
@@ -1,3 +1,7 @@
+if (project.hasProperty('onlylinuxathena')) {
+    return;
+}
+
 description = "A plugin that listens on a socket so that you can use the real Driver Station software to connect to the simulation"
 
 ext {
@@ -7,7 +11,6 @@
 
 apply plugin: 'google-test-test-suite'
 
-
 ext {
     staticGtestConfigs = [:]
 }
@@ -17,28 +20,22 @@
 
 apply from: "${rootDir}/shared/plugins/setupBuild.gradle"
 
-
 model {
     testSuites {
-        def comps = $.components
-        if (!project.hasProperty('onlylinuxathena')) {
-            "${pluginName}Test"(GoogleTestTestSuiteSpec) {
-                for(NativeComponentSpec c : comps) {
-                    if (c.name == pluginName) {
-                        testing c
-                        break
-                    }
+        "${pluginName}Test"(GoogleTestTestSuiteSpec) {
+            for(NativeComponentSpec c : $.components) {
+                if (c.name == pluginName) {
+                    testing c
+                    break
                 }
-                sources {
-                    cpp {
-                        source {
-                            srcDirs 'src/test/native/cpp'
-                            include '**/*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/test/native/include', 'src/main/native/cpp'
-                        }
-                    }
+            }
+            sources.cpp {
+                source {
+                    srcDirs 'src/test/native/cpp'
+                    include '**/*.cpp'
+                }
+                exportedHeaders {
+                    srcDirs 'src/test/native/include', 'src/main/native/cpp'
                 }
             }
         }
diff --git a/third_party/allwpilib/simulation/halsim_ds_socket/src/main/native/cpp/DSCommPacket.cpp b/third_party/allwpilib/simulation/halsim_ds_socket/src/main/native/cpp/DSCommPacket.cpp
index 004752c..5773722 100644
--- a/third_party/allwpilib/simulation/halsim_ds_socket/src/main/native/cpp/DSCommPacket.cpp
+++ b/third_party/allwpilib/simulation/halsim_ds_socket/src/main/native/cpp/DSCommPacket.cpp
@@ -146,7 +146,9 @@
   m_lo = packet[1];
   // Comm Version is packet 2, ignore
   SetControl(packet[3], packet[4]);
-  SetAlliance(packet[5]);
+  // DS sends values 0, 1, and 2 for Red, but kUnknown is 0, so the value needs
+  // to be offset by one
+  SetAlliance(packet[5] + 1);
 
   // Return if packet finished
   if (packet.size() == 6) {
diff --git a/third_party/allwpilib/simulation/halsim_ds_socket/src/main/native/cpp/main.cpp b/third_party/allwpilib/simulation/halsim_ds_socket/src/main/native/cpp/main.cpp
index af84632..e602ecc 100644
--- a/third_party/allwpilib/simulation/halsim_ds_socket/src/main/native/cpp/main.cpp
+++ b/third_party/allwpilib/simulation/halsim_ds_socket/src/main/native/cpp/main.cpp
@@ -16,6 +16,7 @@
 #include <atomic>
 #include <cstdio>
 #include <cstring>
+#include <exception>
 #include <string_view>
 
 #include <DSCommPacket.h>
@@ -124,33 +125,45 @@
     });
   });
   simLoopTimer->Start(Timer::Time{100}, Timer::Time{100});
+  // DS Timeout
+  int timeoutMs = 100;
+  if (auto envTimeout = std::getenv("DS_TIMEOUT_MS")) {
+    try {
+      timeoutMs = std::stoi(envTimeout);
+    } catch (const std::exception& e) {
+      fmt::print(stderr, "Error parsing DS_TIMEOUT_MS: {}\n", e.what());
+    }
+  }
+  auto autoDisableTimer = Timer::Create(loop);
+  autoDisableTimer->timeout.connect([] { HALSIM_SetDriverStationEnabled(0); });
 
   // UDP Receive then send
-  udp->received.connect([udpLocal = udp.get()](Buffer& buf, size_t len,
-                                               const sockaddr& recSock,
-                                               unsigned int port) {
-    auto ds = udpLocal->GetLoop()->GetData<halsim::DSCommPacket>();
-    ds->DecodeUDP({reinterpret_cast<uint8_t*>(buf.base), len});
+  udp->received.connect(
+      [udpLocal = udp.get(), autoDisableTimer, timeoutMs](
+          Buffer& buf, size_t len, const sockaddr& recSock, unsigned int port) {
+        autoDisableTimer->Start(Timer::Time(timeoutMs));
+        auto ds = udpLocal->GetLoop()->GetData<halsim::DSCommPacket>();
+        ds->DecodeUDP({reinterpret_cast<uint8_t*>(buf.base), len});
 
-    struct sockaddr_in outAddr;
-    std::memcpy(&outAddr, &recSock, sizeof(sockaddr_in));
-    outAddr.sin_family = PF_INET;
-    outAddr.sin_port = htons(1150);
+        struct sockaddr_in outAddr;
+        std::memcpy(&outAddr, &recSock, sizeof(sockaddr_in));
+        outAddr.sin_family = PF_INET;
+        outAddr.sin_port = htons(1150);
 
-    wpi::SmallVector<wpi::uv::Buffer, 4> sendBufs;
-    wpi::raw_uv_ostream stream{sendBufs,
-                               [] { return GetBufferPool().Allocate(); }};
-    ds->SetupSendBuffer(stream);
+        wpi::SmallVector<wpi::uv::Buffer, 4> sendBufs;
+        wpi::raw_uv_ostream stream{sendBufs,
+                                   [] { return GetBufferPool().Allocate(); }};
+        ds->SetupSendBuffer(stream);
 
-    udpLocal->Send(outAddr, sendBufs, [](auto bufs, Error err) {
-      GetBufferPool().Release(bufs);
-      if (err) {
-        fmt::print(stderr, "{}\n", err.str());
-        std::fflush(stderr);
-      }
-    });
-    ds->SendUDPToHALSim();
-  });
+        udpLocal->Send(outAddr, sendBufs, [](auto bufs, Error err) {
+          GetBufferPool().Release(bufs);
+          if (err) {
+            fmt::print(stderr, "{}\n", err.str());
+            std::fflush(stderr);
+          }
+        });
+        ds->SendUDPToHALSim();
+      });
 
   udp->StartRecv();
 }
diff --git a/third_party/allwpilib/simulation/halsim_ds_socket/src/test/native/cpp/DSCommPacketTest.cpp b/third_party/allwpilib/simulation/halsim_ds_socket/src/test/native/cpp/DSCommPacketTest.cpp
index d1a892a..1c7bfc5 100644
--- a/third_party/allwpilib/simulation/halsim_ds_socket/src/test/native/cpp/DSCommPacketTest.cpp
+++ b/third_party/allwpilib/simulation/halsim_ds_socket/src/test/native/cpp/DSCommPacketTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "DSCommPacket.h"
-#include "gtest/gtest.h"
 
 class DSCommPacketTest : public ::testing::Test {
  public:
diff --git a/third_party/allwpilib/simulation/halsim_ds_socket/src/test/native/cpp/main.cpp b/third_party/allwpilib/simulation/halsim_ds_socket/src/test/native/cpp/main.cpp
index 6aea19a..d181e39 100644
--- a/third_party/allwpilib/simulation/halsim_ds_socket/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/simulation/halsim_ds_socket/src/test/native/cpp/main.cpp
@@ -2,10 +2,9 @@
 // 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 <gtest/gtest.h>
 #include <hal/HALBase.h>
 
-#include "gtest/gtest.h"
-
 int main(int argc, char** argv) {
   HAL_Initialize(500, 0);
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/third_party/allwpilib/simulation/halsim_gui/CMakeLists.txt b/third_party/allwpilib/simulation/halsim_gui/CMakeLists.txt
index 949f9f1..957a9c2 100644
--- a/third_party/allwpilib/simulation/halsim_gui/CMakeLists.txt
+++ b/third_party/allwpilib/simulation/halsim_gui/CMakeLists.txt
@@ -16,4 +16,4 @@
 
 set_property(TARGET halsim_gui PROPERTY FOLDER "libraries")
 
-install(TARGETS halsim_gui EXPORT halsim_gui DESTINATION "${main_lib_dest}")
+install(TARGETS halsim_gui EXPORT halsim_gui)
diff --git a/third_party/allwpilib/simulation/halsim_gui/build.gradle b/third_party/allwpilib/simulation/halsim_gui/build.gradle
index a8608e3..72bcd22 100644
--- a/third_party/allwpilib/simulation/halsim_gui/build.gradle
+++ b/third_party/allwpilib/simulation/halsim_gui/build.gradle
@@ -1,50 +1,51 @@
-if (!project.hasProperty('onlylinuxathena')) {
+if (project.hasProperty('onlylinuxathena')) {
+    return;
+}
 
-    description = "A plugin that creates a simulation gui"
+description = "A plugin that creates a simulation gui"
 
-    ext {
-        pluginName = 'halsim_gui'
-    }
+ext {
+    pluginName = 'halsim_gui'
+}
 
-    apply plugin: 'google-test-test-suite'
+apply plugin: 'google-test-test-suite'
+
+ext {
+    staticGtestConfigs = [:]
+}
+
+staticGtestConfigs["${pluginName}Test"] = []
+apply from: "${rootDir}/shared/googletest.gradle"
+
+apply from: "${rootDir}/shared/plugins/setupBuild.gradle"
 
 
-    ext {
-        staticGtestConfigs = [:]
-    }
+apply from: "${rootDir}/shared/imgui.gradle"
 
-    staticGtestConfigs["${pluginName}Test"] = []
-    apply from: "${rootDir}/shared/googletest.gradle"
-
-    apply from: "${rootDir}/shared/plugins/setupBuild.gradle"
-
-
-    apply from: "${rootDir}/shared/imgui.gradle"
-
-    model {
-        binaries {
-            all {
-                lib project: ':glass', library: 'glassnt', linkage: 'static'
-                lib project: ':glass', library: 'glass', linkage: 'static'
-                lib project: ':wpigui', library: 'wpigui', linkage: 'static'
-                lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
-                project(':ntcore').addNtcoreDependency(it, 'shared')
-                lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
-                lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
-                nativeUtils.useRequiredLibrary(it, 'imgui')
-                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
-                    it.buildable = false
-                    return
-                }
-                if (it.targetPlatform.operatingSystem.isWindows()) {
-                    it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
-                } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
-                    it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
-                } else {
-                    it.linker.args << '-lX11'
-                    if (it.targetPlatform.name.startsWith('linuxarm')) {
-                        it.linker.args << '-lGL'
-                    }
+model {
+    binaries {
+        all {
+            lib project: ':glass', library: 'glassnt', linkage: 'static'
+            lib project: ':glass', library: 'glass', linkage: 'static'
+            lib project: ':wpigui', library: 'wpigui', linkage: 'static'
+            lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
+            project(':ntcore').addNtcoreDependency(it, 'shared')
+            lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
+            lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+            lib project: ':fieldImages', library: 'fieldImages', linkage: 'static'
+            nativeUtils.useRequiredLibrary(it, 'imgui')
+            if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                it.buildable = false
+                return
+            }
+            if (it.targetPlatform.operatingSystem.isWindows()) {
+                it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
+            } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
+                it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
+            } else {
+                it.linker.args << '-lX11'
+                if (it.targetPlatform.name.startsWith('linuxarm')) {
+                    it.linker.args << '-lGL'
                 }
             }
         }
diff --git a/third_party/allwpilib/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp b/third_party/allwpilib/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp
index e7271fd..4edaa5c 100644
--- a/third_party/allwpilib/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp
+++ b/third_party/allwpilib/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp
@@ -487,7 +487,7 @@
     }
   } else {
     std::memcpy(data->axes.axes, sysAxes,
-                data->axes.count * sizeof(&data->axes.axes[0]));
+                data->axes.count * sizeof(data->axes.axes[0]));
   }
 
   data->povs.count = data->desc.povCount;
@@ -555,8 +555,8 @@
       m_axisStorage{storage.GetChildArray("axisConfig")},
       m_buttonKey{storage.GetIntArray("buttonKeys")},
       m_povStorage{storage.GetChildArray("povConfig")} {
-  std::snprintf(m_name, sizeof(m_name), "Keyboard %d", index);
-  std::snprintf(m_guid, sizeof(m_guid), "Keyboard%d", index);
+  wpi::format_to_n_c_str(m_name, sizeof(m_name), "Keyboard {}", index);
+  wpi::format_to_n_c_str(m_guid, sizeof(m_guid), "Keyboard{}", index);
 
   // init axes
   for (auto&& axisConfig : m_axisStorage) {
@@ -587,9 +587,15 @@
   ImGui::PushID(label);
   ImGui::Text("%s", label);
   ImGui::SameLine();
+
   char editLabel[32];
-  std::snprintf(editLabel, sizeof(editLabel), "%s###edit",
-                s_keyEdit == key ? "(press key)" : GetKeyName(*key));
+  if (s_keyEdit == key) {
+    wpi::format_to_n_c_str(editLabel, sizeof(editLabel), "(press key)###edit");
+  } else {
+    wpi::format_to_n_c_str(editLabel, sizeof(editLabel), "{}###edit",
+                           GetKeyName(*key));
+  }
+
   if (ImGui::SmallButton(editLabel)) {
     s_keyEdit = key;
   }
@@ -633,7 +639,8 @@
       m_axisConfig.emplace_back(*m_axisStorage.back());
     }
     for (int i = 0; i < m_axisCount; ++i) {
-      std::snprintf(label, sizeof(label), "Axis %d", i);
+      wpi::format_to_n_c_str(label, sizeof(label), "Axis {}", i);
+
       if (ImGui::TreeNodeEx(label, ImGuiTreeNodeFlags_DefaultOpen)) {
         EditKey("Increase", &m_axisConfig[i].incKey);
         EditKey("Decrease", &m_axisConfig[i].decKey);
@@ -666,7 +673,8 @@
       m_buttonKey.emplace_back(-1);
     }
     for (int i = 0; i < m_buttonCount; ++i) {
-      std::snprintf(label, sizeof(label), "Button %d", i + 1);
+      wpi::format_to_n_c_str(label, sizeof(label), "Button {}", i + 1);
+
       EditKey(label, &m_buttonKey[i]);
     }
     ImGui::PopID();
@@ -688,7 +696,8 @@
       m_povConfig.emplace_back(*m_povStorage.back());
     }
     for (int i = 0; i < m_povCount; ++i) {
-      std::snprintf(label, sizeof(label), "POV %d", i);
+      wpi::format_to_n_c_str(label, sizeof(label), "POV {}", i);
+
       if (ImGui::TreeNodeEx(label, ImGuiTreeNodeFlags_DefaultOpen)) {
         EditKey("  0 deg", &m_povConfig[i].key0);
         EditKey(" 45 deg", &m_povConfig[i].key45);
@@ -1174,7 +1183,7 @@
 
 static void DisplaySystemJoystick(SystemJoystick& joy, int i) {
   char label[64];
-  std::snprintf(label, sizeof(label), "%d: %s", i, joy.GetName());
+  wpi::format_to_n_c_str(label, sizeof(label), "{}: {}", i, joy.GetName());
 
   // highlight if any buttons pressed
   bool anyButtonPressed = joy.IsAnyButtonPressed();
@@ -1208,7 +1217,8 @@
     DisplaySystemJoystick(*joy, i + GLFW_JOYSTICK_LAST + 1);
     if (ImGui::BeginPopupContextItem()) {
       char buf[64];
-      std::snprintf(buf, sizeof(buf), "%s Settings", joy->GetName());
+      wpi::format_to_n_c_str(buf, sizeof(buf), "{} Settings", joy->GetName());
+
       if (ImGui::MenuItem(buf)) {
         if (auto win = DriverStationGui::dsManager->GetWindow(buf)) {
           win->SetVisible(true);
@@ -1293,7 +1303,8 @@
       for (int j = 0; j < joy.data.axes.count; ++j) {
         if (source && source->axes[j]) {
           char label[64];
-          std::snprintf(label, sizeof(label), "Axis[%d]", j);
+          wpi::format_to_n_c_str(label, sizeof(label), "Axis[{}]", j);
+
           ImGui::Selectable(label);
           source->axes[j]->EmitDrag();
           ImGui::SameLine();
@@ -1306,7 +1317,8 @@
       for (int j = 0; j < joy.data.povs.count; ++j) {
         if (source && source->povs[j]) {
           char label[64];
-          std::snprintf(label, sizeof(label), "POVs[%d]", j);
+          wpi::format_to_n_c_str(label, sizeof(label), "POVs[{}]", j);
+
           ImGui::Selectable(label);
           source->povs[j]->EmitDrag();
           ImGui::SameLine();
@@ -1406,7 +1418,9 @@
     int i = 0;
     for (auto&& joy : gKeyboardJoysticks) {
       char label[64];
-      std::snprintf(label, sizeof(label), "%s Settings", joy->GetName());
+      wpi::format_to_n_c_str(label, sizeof(label), "{} Settings",
+                             joy->GetName());
+
       if (auto win = dsManager->AddWindow(
               label, [j = joy.get()] { j->SettingsDisplay(); },
               glass::Window::kHide)) {
diff --git a/third_party/allwpilib/simulation/halsim_gui/src/main/native/cpp/main.cpp b/third_party/allwpilib/simulation/halsim_gui/src/main/native/cpp/main.cpp
index d0db8d4..0cedd36 100644
--- a/third_party/allwpilib/simulation/halsim_gui/src/main/native/cpp/main.cpp
+++ b/third_party/allwpilib/simulation/halsim_gui/src/main/native/cpp/main.cpp
@@ -116,7 +116,8 @@
     }
   });
 
-  if (!gui::Initialize("Robot Simulation", 1280, 720)) {
+  if (!gui::Initialize("Robot Simulation", 1280, 720,
+                       ImGuiConfigFlags_DockingEnable)) {
     return 0;
   }
   HAL_RegisterExtensionListener(
diff --git a/third_party/allwpilib/simulation/halsim_gui/src/main/native/include/HALDataSource.h b/third_party/allwpilib/simulation/halsim_gui/src/main/native/include/HALDataSource.h
index c04f358..295b3f8 100644
--- a/third_party/allwpilib/simulation/halsim_gui/src/main/native/include/HALDataSource.h
+++ b/third_party/allwpilib/simulation/halsim_gui/src/main/native/include/HALDataSource.h
@@ -57,9 +57,13 @@
         HALSIM_Cancel##cbname##Callback(m_index, m_callback);                 \
     }                                                                         \
                                                                               \
-    int32_t GetIndex() const { return m_index; }                              \
+    int32_t GetIndex() const {                                                \
+      return m_index;                                                         \
+    }                                                                         \
                                                                               \
-    int GetChannel() const { return m_channel; }                              \
+    int GetChannel() const {                                                  \
+      return m_channel;                                                       \
+    }                                                                         \
                                                                               \
    private:                                                                   \
     static void CallbackFunc(const char*, void* param,                        \
@@ -96,9 +100,13 @@
         HALSIM_Cancel##cbname##Callback(m_index, m_channel, m_callback);      \
     }                                                                         \
                                                                               \
-    int32_t GetIndex() const { return m_index; }                              \
+    int32_t GetIndex() const {                                                \
+      return m_index;                                                         \
+    }                                                                         \
                                                                               \
-    int32_t GetChannel() const { return m_channel; }                          \
+    int32_t GetChannel() const {                                              \
+      return m_channel;                                                       \
+    }                                                                         \
                                                                               \
    private:                                                                   \
     static void CallbackFunc(const char*, void* param,                        \
diff --git a/third_party/allwpilib/simulation/halsim_gui/src/test/native/cpp/main.cpp b/third_party/allwpilib/simulation/halsim_gui/src/test/native/cpp/main.cpp
index 6aea19a..d181e39 100644
--- a/third_party/allwpilib/simulation/halsim_gui/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/simulation/halsim_gui/src/test/native/cpp/main.cpp
@@ -2,10 +2,9 @@
 // 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 <gtest/gtest.h>
 #include <hal/HALBase.h>
 
-#include "gtest/gtest.h"
-
 int main(int argc, char** argv) {
   HAL_Initialize(500, 0);
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/third_party/allwpilib/simulation/halsim_ws_client/CMakeLists.txt b/third_party/allwpilib/simulation/halsim_ws_client/CMakeLists.txt
index 5bc99db..18a2a12 100644
--- a/third_party/allwpilib/simulation/halsim_ws_client/CMakeLists.txt
+++ b/third_party/allwpilib/simulation/halsim_ws_client/CMakeLists.txt
@@ -13,4 +13,4 @@
 
 set_property(TARGET halsim_ws_client PROPERTY FOLDER "libraries")
 
-install(TARGETS halsim_ws_client EXPORT halsim_ws_client DESTINATION "${main_lib_dest}")
+install(TARGETS halsim_ws_client EXPORT halsim_ws_client)
diff --git a/third_party/allwpilib/simulation/halsim_ws_client/build.gradle b/third_party/allwpilib/simulation/halsim_ws_client/build.gradle
index 1fe4d29..fb563ab 100644
--- a/third_party/allwpilib/simulation/halsim_ws_client/build.gradle
+++ b/third_party/allwpilib/simulation/halsim_ws_client/build.gradle
@@ -1,35 +1,35 @@
-if (!project.hasProperty('onlylinuxathena')) {
+if (project.hasProperty('onlylinuxathena')) {
+    return;
+}
 
-    description = "WebSocket Client Extension"
+description = "WebSocket Client Extension"
 
-    ext {
-        includeWpiutil = true
-        pluginName = 'halsim_ws_client'
-    }
+ext {
+    includeWpiutil = true
+    pluginName = 'halsim_ws_client'
+}
 
-    apply plugin: 'google-test-test-suite'
+apply plugin: 'google-test-test-suite'
 
+ext {
+    staticGtestConfigs = [:]
+}
 
-    ext {
-        staticGtestConfigs = [:]
-    }
+staticGtestConfigs["${pluginName}Test"] = []
+apply from: "${rootDir}/shared/googletest.gradle"
 
-    staticGtestConfigs["${pluginName}Test"] = []
-    apply from: "${rootDir}/shared/googletest.gradle"
+apply from: "${rootDir}/shared/plugins/setupBuild.gradle"
 
-    apply from: "${rootDir}/shared/plugins/setupBuild.gradle"
-
-    model {
-        binaries {
-            all {
-                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
-                    it.buildable = false
-                    return
-                }
-
-                lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
-                lib project: ":simulation:halsim_ws_core", library: "halsim_ws_core", linkage: "static"
+model {
+    binaries {
+        all {
+            if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                it.buildable = false
+                return
             }
+
+            lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
+            lib project: ":simulation:halsim_ws_core", library: "halsim_ws_core", linkage: "static"
         }
     }
 }
diff --git a/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp b/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp
index f4c5926..bc38c3c 100644
--- a/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp
+++ b/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp
@@ -8,6 +8,8 @@
 
 #include <fmt/format.h>
 #include <wpi/SmallString.h>
+#include <wpi/SmallVector.h>
+#include <wpi/StringExtras.h>
 #include <wpinet/uv/util.h>
 
 #include "HALSimWSClientConnection.h"
@@ -66,6 +68,22 @@
     m_uri = "/wpilibws";
   }
 
+  const char* msgFilters = std::getenv("HALSIMWS_FILTERS");
+  if (msgFilters != nullptr) {
+    m_useMsgFiltering = true;
+
+    std::string_view filters(msgFilters);
+    filters = wpi::trim(filters);
+    wpi::SmallVector<std::string_view, 16> filtersSplit;
+
+    wpi::split(filters, filtersSplit, ',', -1, false);
+    for (auto val : filtersSplit) {
+      m_msgFilters[wpi::trim(val)] = true;
+    }
+  } else {
+    m_useMsgFiltering = false;
+  }
+
   return true;
 }
 
@@ -87,8 +105,19 @@
 
   m_tcp_client->closed.connect([]() { std::puts("TCP connection closed"); });
 
-  // Set up the connection timer
   std::puts("HALSimWS Initialized");
+
+  // Print any filters we are using
+  if (m_useMsgFiltering) {
+    fmt::print("WS Message Filters:");
+    for (auto filter : m_msgFilters.keys()) {
+      fmt::print("* \"{}\"\n", filter);
+    }
+  } else {
+    fmt::print("No WS Message Filters specified");
+  }
+
+  // Set up the connection timer
   fmt::print("Will attempt to connect to ws://{}:{}{}\n", m_host, m_port,
              m_uri);
 
@@ -171,3 +200,10 @@
     fmt::print(stderr, "Error with incoming message: {}\n", e.what());
   }
 }
+
+bool HALSimWS::CanSendMessage(std::string_view type) {
+  if (!m_useMsgFiltering) {
+    return true;
+  }
+  return m_msgFilters.count(type) > 0;
+}
diff --git a/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/cpp/HALSimWSClientConnection.cpp b/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/cpp/HALSimWSClientConnection.cpp
index 10ea8af..cba94c8 100644
--- a/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/cpp/HALSimWSClientConnection.cpp
+++ b/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/cpp/HALSimWSClientConnection.cpp
@@ -74,6 +74,17 @@
   if (msg.empty()) {
     return;
   }
+
+  // Skip sending if this message is not in the allowed filter list
+  try {
+    auto& type = msg.at("type").get_ref<const std::string&>();
+    if (!m_client->CanSendMessage(type)) {
+      return;
+    }
+  } catch (wpi::json::exception& e) {
+    fmt::print(stderr, "Error with message: {}\n", e.what());
+  }
+
   wpi::SmallVector<uv::Buffer, 4> sendBufs;
   wpi::raw_uv_ostream os{sendBufs, [this]() -> uv::Buffer {
                            std::lock_guard lock(m_buffers_mutex);
diff --git a/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/include/HALSimWS.h b/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/include/HALSimWS.h
index 5bec17c..ea01d28 100644
--- a/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/include/HALSimWS.h
+++ b/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/include/HALSimWS.h
@@ -7,25 +7,24 @@
 #include <functional>
 #include <memory>
 #include <string>
+#include <string_view>
 
 #include <WSProviderContainer.h>
 #include <WSProvider_SimDevice.h>
+#include <wpi/StringMap.h>
+#include <wpi/json_fwd.h>
 #include <wpinet/uv/Async.h>
 #include <wpinet/uv/Loop.h>
 #include <wpinet/uv/Tcp.h>
 #include <wpinet/uv/Timer.h>
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace wpilibws {
 
 class HALSimWSClientConnection;
 
 class HALSimWS : public std::enable_shared_from_this<HALSimWS> {
  public:
-  using LoopFunc = std::function<void(void)>;
+  using LoopFunc = std::function<void()>;
   using UvExecFunc = wpi::uv::Async<LoopFunc>;
 
   HALSimWS(wpi::uv::Loop& loop, ProviderContainer& providers,
@@ -41,6 +40,8 @@
 
   void OnNetValueChanged(const wpi::json& msg);
 
+  bool CanSendMessage(std::string_view type);
+
   const std::string& GetTargetHost() const { return m_host; }
   const std::string& GetTargetUri() const { return m_uri; }
   int GetTargetPort() const { return m_port; }
@@ -67,6 +68,9 @@
   std::string m_host;
   std::string m_uri;
   int m_port;
+
+  bool m_useMsgFiltering;
+  wpi::StringMap<bool> m_msgFilters;
 };
 
 }  // namespace wpilibws
diff --git a/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/include/HALSimWSClientConnection.h b/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/include/HALSimWSClientConnection.h
index b712e70..005813f 100644
--- a/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/include/HALSimWSClientConnection.h
+++ b/third_party/allwpilib/simulation/halsim_ws_client/src/main/native/include/HALSimWSClientConnection.h
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include <HALSimBaseWebSocketConnection.h>
+#include <wpi/json_fwd.h>
 #include <wpi/mutex.h>
 #include <wpinet/WebSocket.h>
 #include <wpinet/uv/Buffer.h>
@@ -15,10 +16,6 @@
 
 #include "HALSimWS.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace wpilibws {
 
 class HALSimWS;
diff --git a/third_party/allwpilib/simulation/halsim_ws_core/CMakeLists.txt b/third_party/allwpilib/simulation/halsim_ws_core/CMakeLists.txt
index 91bcbb2..f7e240f 100644
--- a/third_party/allwpilib/simulation/halsim_ws_core/CMakeLists.txt
+++ b/third_party/allwpilib/simulation/halsim_ws_core/CMakeLists.txt
@@ -13,4 +13,4 @@
 
 set_property(TARGET halsim_ws_core PROPERTY FOLDER "libraries")
 
-install(TARGETS halsim_ws_core EXPORT halsim_ws_core DESTINATION "${main_lib_dest}")
+install(TARGETS halsim_ws_core EXPORT halsim_ws_core)
diff --git a/third_party/allwpilib/simulation/halsim_ws_core/build.gradle b/third_party/allwpilib/simulation/halsim_ws_core/build.gradle
index abdfddc..5dd8d2a 100644
--- a/third_party/allwpilib/simulation/halsim_ws_core/build.gradle
+++ b/third_party/allwpilib/simulation/halsim_ws_core/build.gradle
@@ -1,58 +1,57 @@
+if (project.hasProperty('onlylinuxathena')) {
+    return;
+}
+
 apply plugin: 'cpp'
 apply plugin: 'edu.wpi.first.NativeUtils'
 apply plugin: ExtraTasks
 
-if (!project.hasProperty('onlylinuxathena')) {
+description = "Core library for WebSocket extensions"
 
-    description = "Core library for WebSocket extensions"
+ext {
+    includeWpiutil = true
+    includeWpinet = true
+    pluginName = 'halsim_ws_core'
+}
 
-    ext {
-        includeWpiutil = true
-        includeWpinet = true
-        pluginName = 'halsim_ws_core'
-    }
-
-    apply plugin: 'google-test-test-suite'
+apply plugin: 'google-test-test-suite'
 
 
-    ext {
-        staticGtestConfigs = [:]
-    }
+ext {
+    staticGtestConfigs = [:]
+}
 
-    staticGtestConfigs["${pluginName}Test"] = []
-    apply from: "${rootDir}/shared/googletest.gradle"
+staticGtestConfigs["${pluginName}Test"] = []
+apply from: "${rootDir}/shared/googletest.gradle"
 
-    apply from: "${rootDir}/shared/config.gradle"
-    apply from: "${rootDir}/shared/plugins/publish.gradle"
+apply from: "${rootDir}/shared/config.gradle"
+apply from: "${rootDir}/shared/plugins/publish.gradle"
 
-    model {
-        components {
-            halsim_ws_core(NativeLibrarySpec) {
-                sources {
-                    cpp {
-                        source {
-                            srcDirs = ['src/main/native/cpp']
-                            includes = ["**/*.cpp"]
-                        }
-                        exportedHeaders {
-                            srcDirs = ["src/main/native/include"]
-                        }
-                    }
+model {
+    components {
+        halsim_ws_core(NativeLibrarySpec) {
+            sources.cpp {
+                source {
+                    srcDirs = ['src/main/native/cpp']
+                    includes = ["**/*.cpp"]
                 }
-                binaries.all {
-                    project(':hal').addHalDependency(it, 'shared')
-                    lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
-                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                exportedHeaders {
+                    srcDirs = ["src/main/native/include"]
                 }
-                appendDebugPathToBinaries(binaries)
             }
+            binaries.all {
+                project(':hal').addHalDependency(it, 'shared')
+                lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+            }
+            appendDebugPathToBinaries(binaries)
         }
-        binaries {
-            all {
-                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
-                    it.buildable = false
-                    return
-                }
+    }
+    binaries {
+        all {
+            if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                it.buildable = false
+                return
             }
         }
     }
diff --git a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DriverStation.cpp b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DriverStation.cpp
index 7307b48..ee959b6b 100644
--- a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DriverStation.cpp
+++ b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DriverStation.cpp
@@ -84,6 +84,9 @@
           case HAL_AllianceStationID_kBlue3:
             station = "blue3";
             break;
+          case HAL_AllianceStationID_kUnknown:
+            station = "unknown";
+            break;
         }
         static_cast<HALSimWSProviderDriverStation*>(param)->ProcessHalCallback(
             {{">station", station}});
diff --git a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_HAL.cpp b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_HAL.cpp
new file mode 100644
index 0000000..4010a85
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_HAL.cpp
@@ -0,0 +1,59 @@
+// 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 "WSProvider_HAL.h"
+
+#include <algorithm>
+#include <atomic>
+#include <string_view>
+
+#include <hal/Extensions.h>
+#include <hal/HAL.h>
+#include <hal/Ports.h>
+#include <hal/simulation/MockHooks.h>
+#include <wpi/raw_ostream.h>
+
+namespace wpilibws {
+
+void HALSimWSProviderHAL::Initialize(WSRegisterFunc webRegisterFunc) {
+  CreateSingleProvider<HALSimWSProviderHAL>("HAL", webRegisterFunc);
+}
+
+HALSimWSProviderHAL::~HALSimWSProviderHAL() {
+  DoCancelCallbacks();
+}
+
+void HALSimWSProviderHAL::RegisterCallbacks() {
+  m_simPeriodicBeforeCbKey = HALSIM_RegisterSimPeriodicBeforeCallback(
+      [](void* param) {
+        static_cast<HALSimWSProviderHAL*>(param)->ProcessHalCallback(
+            {{">sim_periodic_before", true}});
+      },
+      this);
+
+  m_simPeriodicAfterCbKey = HALSIM_RegisterSimPeriodicAfterCallback(
+      [](void* param) {
+        static_cast<HALSimWSProviderHAL*>(param)->ProcessHalCallback(
+            {{">sim_periodic_after", true}});
+      },
+      this);
+}
+
+void HALSimWSProviderHAL::CancelCallbacks() {
+  DoCancelCallbacks();
+}
+
+void HALSimWSProviderHAL::DoCancelCallbacks() {
+  HALSIM_CancelSimPeriodicBeforeCallback(m_simPeriodicBeforeCbKey);
+  HALSIM_CancelSimPeriodicAfterCallback(m_simPeriodicAfterCbKey);
+
+  m_simPeriodicBeforeCbKey = 0;
+  m_simPeriodicAfterCbKey = 0;
+}
+
+void HALSimWSProviderHAL::OnNetValueChanged(const wpi::json& json) {
+  // no-op. This is all one way
+}
+
+}  // namespace wpilibws
diff --git a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PWM.cpp b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PWM.cpp
index 5792ef5..046e12c 100644
--- a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PWM.cpp
+++ b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PWM.cpp
@@ -30,7 +30,7 @@
   m_initCbKey = REGISTER(Initialized, "<init", bool, boolean);
   m_speedCbKey = REGISTER(Speed, "<speed", double, double);
   m_positionCbKey = REGISTER(Position, "<position", double, double);
-  m_rawCbKey = REGISTER(RawValue, "<raw", int32_t, int);
+  m_rawCbKey = REGISTER(PulseMicrosecond, "<raw", int32_t, int);
   m_periodScaleCbKey = REGISTER(PeriodScale, "<period_scale", int32_t, int);
   m_zeroLatchCbKey = REGISTER(ZeroLatch, "<zero_latch", bool, boolean);
 }
@@ -43,7 +43,7 @@
   HALSIM_CancelPWMInitializedCallback(m_channel, m_initCbKey);
   HALSIM_CancelPWMSpeedCallback(m_channel, m_speedCbKey);
   HALSIM_CancelPWMPositionCallback(m_channel, m_positionCbKey);
-  HALSIM_CancelPWMRawValueCallback(m_channel, m_rawCbKey);
+  HALSIM_CancelPWMPulseMicrosecondCallback(m_channel, m_rawCbKey);
   HALSIM_CancelPWMPeriodScaleCallback(m_channel, m_periodScaleCbKey);
   HALSIM_CancelPWMZeroLatchCallback(m_channel, m_zeroLatchCbKey);
 
diff --git a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/HALSimBaseWebSocketConnection.h b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/HALSimBaseWebSocketConnection.h
index bcf29c2..5a699c8 100644
--- a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/HALSimBaseWebSocketConnection.h
+++ b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/HALSimBaseWebSocketConnection.h
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include <wpi/json.h>
+#include <wpi/json_fwd.h>
 
 namespace wpilibws {
 
diff --git a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSHalProviders.h b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSHalProviders.h
index e1bc1f8..849edeb 100644
--- a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSHalProviders.h
+++ b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSHalProviders.h
@@ -10,7 +10,7 @@
 #include <string_view>
 
 #include <hal/simulation/NotifyListener.h>
-#include <wpi/json.h>
+#include <wpi/json_fwd.h>
 #include <wpi/mutex.h>
 
 #include "WSBaseProvider.h"
diff --git a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSProvider_HAL.h b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSProvider_HAL.h
new file mode 100644
index 0000000..05ea722
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSProvider_HAL.h
@@ -0,0 +1,32 @@
+// 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 "WSHalProviders.h"
+
+namespace wpilibws {
+
+class HALSimWSProviderHAL : public HALSimWSHalProvider {
+ public:
+  static void Initialize(WSRegisterFunc webRegisterFunc);
+
+  using HALSimWSHalProvider::HALSimWSHalProvider;
+  ~HALSimWSProviderHAL() override;
+
+  void OnNetValueChanged(const wpi::json& json) override;
+
+ protected:
+  void RegisterCallbacks() override;
+  void CancelCallbacks() override;
+  void DoCancelCallbacks();
+
+ private:
+  int32_t m_simPeriodicBeforeCbKey = 0;
+  int32_t m_simPeriodicAfterCbKey = 0;
+};
+
+}  // namespace wpilibws
diff --git a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSProvider_SimDevice.h b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSProvider_SimDevice.h
index 3a1ff5f..3bc2df1 100644
--- a/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSProvider_SimDevice.h
+++ b/third_party/allwpilib/simulation/halsim_ws_core/src/main/native/include/WSProvider_SimDevice.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <string>
 #include <string_view>
@@ -86,7 +87,7 @@
 
 class HALSimWSProviderSimDevices {
  public:
-  using LoopFn = std::function<void(void)>;
+  using LoopFn = std::function<void()>;
   using UvExecFn = wpi::uv::AsyncFunction<void(LoopFn)>;
 
   explicit HALSimWSProviderSimDevices(ProviderContainer& providers)
diff --git a/third_party/allwpilib/simulation/halsim_ws_server/CMakeLists.txt b/third_party/allwpilib/simulation/halsim_ws_server/CMakeLists.txt
index e5b55c8..370d2f8 100644
--- a/third_party/allwpilib/simulation/halsim_ws_server/CMakeLists.txt
+++ b/third_party/allwpilib/simulation/halsim_ws_server/CMakeLists.txt
@@ -13,4 +13,4 @@
 
 set_property(TARGET halsim_ws_server PROPERTY FOLDER "libraries")
 
-install(TARGETS halsim_ws_server EXPORT halsim_ws_server DESTINATION "${main_lib_dest}")
+install(TARGETS halsim_ws_server EXPORT halsim_ws_server)
diff --git a/third_party/allwpilib/simulation/halsim_ws_server/build.gradle b/third_party/allwpilib/simulation/halsim_ws_server/build.gradle
index fbdee04..8db1951 100644
--- a/third_party/allwpilib/simulation/halsim_ws_server/build.gradle
+++ b/third_party/allwpilib/simulation/halsim_ws_server/build.gradle
@@ -1,3 +1,6 @@
+if (project.hasProperty('onlylinuxathena')) {
+    return;
+}
 
 description = "WebSocket Server Extension"
 
@@ -20,25 +23,20 @@
 
 model {
     testSuites {
-        def comps = $.components
-        if (!project.hasProperty('onlylinuxathena')) {
-            "${pluginName}Test"(GoogleTestTestSuiteSpec) {
-                for(NativeComponentSpec c : comps) {
-                    if (c.name == pluginName) {
-                        testing c
-                        break
-                    }
+        "${pluginName}Test"(GoogleTestTestSuiteSpec) {
+            for(NativeComponentSpec c : $.components) {
+                if (c.name == pluginName) {
+                    testing c
+                    break
                 }
-                sources {
-                    cpp {
-                        source {
-                            srcDirs 'src/test/native/cpp'
-                            include '**/*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/test/native/include', 'src/main/native/cpp'
-                        }
-                    }
+            }
+            sources.cpp {
+                source {
+                    srcDirs 'src/test/native/cpp'
+                    include '**/*.cpp'
+                }
+                exportedHeaders {
+                    srcDirs 'src/test/native/include', 'src/main/native/cpp'
                 }
             }
         }
diff --git a/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp b/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp
index 77dec08..ee188b3 100644
--- a/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp
+++ b/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp
@@ -9,10 +9,10 @@
 #include <string_view>
 
 #include <fmt/format.h>
+#include <wpi/MemoryBuffer.h>
 #include <wpi/SmallVector.h>
 #include <wpi/StringExtras.h>
 #include <wpi/fs.h>
-#include <wpi/raw_istream.h>
 #include <wpinet/MimeTypes.h>
 #include <wpinet/UrlParser.h>
 #include <wpinet/raw_uv_ostream.h>
@@ -76,6 +76,16 @@
 }
 
 void HALSimHttpConnection::OnSimValueChanged(const wpi::json& msg) {
+  // Skip sending if this message is not in the allowed filter list
+  try {
+    auto& type = msg.at("type").get_ref<const std::string&>();
+    if (!m_server->CanSendMessage(type)) {
+      return;
+    }
+  } catch (wpi::json::exception& e) {
+    fmt::print(stderr, "Error with message: {}\n", e.what());
+  }
+
   // render json to buffers
   wpi::SmallVector<uv::Buffer, 4> sendBufs;
   wpi::raw_uv_ostream os{sendBufs, [this]() -> uv::Buffer {
@@ -115,8 +125,9 @@
   }
 
   // open file
-  wpi::raw_fd_istream is{filename, ec, true};
-  if (ec) {
+  std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+      wpi::MemoryBuffer::GetFile(filename, ec);
+  if (fileBuffer == nullptr || ec) {
     MySendError(404, "error opening file");
     return;
   }
@@ -132,16 +143,7 @@
   wpi::SmallVector<uv::Buffer, 4> bodyData;
   wpi::raw_uv_ostream bodyOs{bodyData, 4096};
 
-  std::string fileBuf;
-  size_t oldSize = 0;
-
-  while (fileBuf.size() < size) {
-    oldSize = fileBuf.size();
-    fileBuf.resize(oldSize + 1);
-    is.read(&(*fileBuf.begin()) + oldSize, 1);
-  }
-
-  bodyOs << fileBuf;
+  bodyOs << fileBuffer->GetBuffer();
 
   SendData(bodyOs.bufs(), false);
   if (!m_keepAlive) {
diff --git a/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp b/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp
index 8a6df97..790d0b5 100644
--- a/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp
+++ b/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp
@@ -77,6 +77,22 @@
     m_port = 3300;
   }
 
+  const char* msgFilters = std::getenv("HALSIMWS_FILTERS");
+  if (msgFilters != nullptr) {
+    m_useMsgFiltering = true;
+
+    std::string_view filters(msgFilters);
+    filters = wpi::trim(filters);
+    wpi::SmallVector<std::string_view, 16> filtersSplit;
+
+    wpi::split(filters, filtersSplit, ',', -1, false);
+    for (auto val : filtersSplit) {
+      m_msgFilters[wpi::trim(val)] = true;
+    }
+  } else {
+    m_useMsgFiltering = false;
+  }
+
   return true;
 }
 
@@ -100,6 +116,16 @@
   m_server->Listen();
   fmt::print("Listening at http://localhost:{}\n", m_port);
   fmt::print("WebSocket URI: {}\n", m_uri);
+
+  // Print any filters we are using
+  if (m_useMsgFiltering) {
+    fmt::print("WS Message Filters:");
+    for (auto filter : m_msgFilters.keys()) {
+      fmt::print("* \"{}\"\n", filter);
+    }
+  } else {
+    fmt::print("No WS Message Filters specified");
+  }
 }
 
 bool HALSimWeb::RegisterWebsocket(
@@ -157,3 +183,10 @@
     fmt::print(stderr, "Error with incoming message: {}\n", e.what());
   }
 }
+
+bool HALSimWeb::CanSendMessage(std::string_view type) {
+  if (!m_useMsgFiltering) {
+    return true;
+  }
+  return m_msgFilters.count(type) > 0;
+}
diff --git a/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/include/HALSimHttpConnection.h b/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/include/HALSimHttpConnection.h
index 91cfb61..7073256 100644
--- a/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/include/HALSimHttpConnection.h
+++ b/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/include/HALSimHttpConnection.h
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include <HALSimBaseWebSocketConnection.h>
+#include <wpi/json_fwd.h>
 #include <wpi/mutex.h>
 #include <wpinet/HttpWebSocketServerConnection.h>
 #include <wpinet/uv/AsyncFunction.h>
@@ -17,10 +18,6 @@
 
 #include "HALSimWeb.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace wpilibws {
 
 class HALSimHttpConnection
diff --git a/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/include/HALSimWeb.h b/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/include/HALSimWeb.h
index de5d1f2..03f2cb6 100644
--- a/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/include/HALSimWeb.h
+++ b/third_party/allwpilib/simulation/halsim_ws_server/src/main/native/include/HALSimWeb.h
@@ -7,23 +7,22 @@
 #include <functional>
 #include <memory>
 #include <string>
+#include <string_view>
 
 #include <WSBaseProvider.h>
 #include <WSProviderContainer.h>
 #include <WSProvider_SimDevice.h>
+#include <wpi/StringMap.h>
+#include <wpi/json_fwd.h>
 #include <wpinet/uv/Async.h>
 #include <wpinet/uv/Loop.h>
 #include <wpinet/uv/Tcp.h>
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace wpilibws {
 
 class HALSimWeb : public std::enable_shared_from_this<HALSimWeb> {
  public:
-  using LoopFunc = std::function<void(void)>;
+  using LoopFunc = std::function<void()>;
   using UvExecFunc = wpi::uv::Async<LoopFunc>;
 
   HALSimWeb(wpi::uv::Loop& loop, ProviderContainer& providers,
@@ -41,6 +40,8 @@
   // network -> sim
   void OnNetValueChanged(const wpi::json& msg);
 
+  bool CanSendMessage(std::string_view type);
+
   const std::string& GetWebrootSys() const { return m_webroot_sys; }
   const std::string& GetWebrootUser() const { return m_webroot_user; }
   const std::string& GetServerUri() const { return m_uri; }
@@ -69,6 +70,9 @@
 
   std::string m_uri;
   int m_port;
+
+  bool m_useMsgFiltering;
+  wpi::StringMap<bool> m_msgFilters;
 };
 
 }  // namespace wpilibws
diff --git a/third_party/allwpilib/simulation/halsim_ws_server/src/test/native/cpp/main.cpp b/third_party/allwpilib/simulation/halsim_ws_server/src/test/native/cpp/main.cpp
index a9f739a..61e3a88 100644
--- a/third_party/allwpilib/simulation/halsim_ws_server/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/simulation/halsim_ws_server/src/test/native/cpp/main.cpp
@@ -5,6 +5,7 @@
 #include <thread>
 
 #include <fmt/format.h>
+#include <gtest/gtest.h>
 #include <hal/DriverStation.h>
 #include <hal/HALBase.h>
 #include <hal/Main.h>
@@ -13,7 +14,6 @@
 
 #include "HALSimWSServer.h"
 #include "WebServerClientTest.h"
-#include "gtest/gtest.h"
 
 namespace uv = wpi::uv;
 
diff --git a/third_party/allwpilib/simulation/halsim_ws_server/src/test/native/include/WebServerClientTest.h b/third_party/allwpilib/simulation/halsim_ws_server/src/test/native/include/WebServerClientTest.h
index cda61dd..6f2995f 100644
--- a/third_party/allwpilib/simulation/halsim_ws_server/src/test/native/include/WebServerClientTest.h
+++ b/third_party/allwpilib/simulation/halsim_ws_server/src/test/native/include/WebServerClientTest.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <string>
 
@@ -21,7 +22,7 @@
 class WebServerClientTest {
  public:
   using BufferPool = wpi::uv::SimpleBufferPool<4>;
-  using LoopFunc = std::function<void(void)>;
+  using LoopFunc = std::function<void()>;
   using UvExecFunc = wpi::uv::AsyncFunction<void(LoopFunc)>;
 
   explicit WebServerClientTest(wpi::uv::Loop& loop) : m_loop(loop) {}
diff --git a/third_party/allwpilib/simulation/halsim_xrp/CMakeLists.txt b/third_party/allwpilib/simulation/halsim_xrp/CMakeLists.txt
new file mode 100644
index 0000000..cc83874
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/CMakeLists.txt
@@ -0,0 +1,16 @@
+project(halsim_xrp)
+
+include(CompileWarnings)
+
+file(GLOB halsim_xrp_src src/main/native/cpp/*.cpp)
+
+add_library(halsim_xrp SHARED ${halsim_xrp_src})
+wpilib_target_warnings(halsim_xrp)
+set_target_properties(halsim_xrp PROPERTIES DEBUG_POSTFIX "d")
+target_link_libraries(halsim_xrp PUBLIC hal halsim_ws_core)
+
+target_include_directories(halsim_xrp PRIVATE src/main/native/include)
+
+set_property(TARGET halsim_xrp PROPERTY FOLDER "libraries")
+
+install(TARGETS halsim_xrp EXPORT halsim_xrp DESTINATION "${main_lib_dest}")
diff --git a/third_party/allwpilib/simulation/halsim_xrp/README.md b/third_party/allwpilib/simulation/halsim_xrp/README.md
new file mode 100644
index 0000000..79d6d52
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/README.md
@@ -0,0 +1,125 @@
+# HAL XRP Client
+
+This is an extension that provides a client version of the XRP protocol for transmitting robot hardware interface state to an XRP robot over UDP.
+
+## Configuration
+
+The XRP client has a number of configuration options available through environment variables.
+
+``HALSIMXRP_HOST``: The host to connect to.  Defaults to localhost.
+
+``HALSIMXRP_PORT``: The port number to connect to.  Defaults to 3540.
+
+## XRP Protocol
+
+The WPILib -> XRP protocol is binary-based to save on bandwidth due to hardware limitations of the XRP robot. The messages to/from the XRP follow a the format below:
+
+| 2 bytes             | 1 byte            | n bytes                             |
+|---------------------|-------------------|-------------------------------------|
+| _uint16_t_ sequence | _uint8_t_ control | [&lt;Tagged Data&gt;](#tagged-data) |
+
+### Control Byte
+
+The control byte is used to indicate the current `enabled` state of the WPILib robot code. When this is set to `1`, the robot is enabled, and when it is set to `0` it is disabled.
+
+Messages originating from the XRP have an unspecified value for the control byte.
+
+### Tagged Data
+
+The `Tagged Data` section can contain an arbitrary number of data blocks. Each block has the format below:
+
+| 1 byte         | 1 byte          | n bytes         |
+|----------------|-----------------|-----------------|
+| _uint8_t_ size | _uint8_t_ tagID | &lt;payload&gt; |
+
+The `size` byte encodes the size of the data block, _excluding_ itself. Thus the smallest block size is 2 bytes, with a size value of 1 (1 size byte, 1 tag byte, 0 payload bytes). Maximum size of the payload is 254 bytes.
+
+
+Utilizing tagged data blocks allows us to send multiple pieces of data in a single UDP packet. The tags currently implemented for the XRP are as follows:
+
+| Tag  | Description                   |
+|------|-------------------------------|
+| 0x12 | [XRPMotor](#xrpmotor)         |
+| 0x13 | [XRPServo](#xrpservo)         |
+| 0x14 | [DIO](#dio)                   |
+| 0x15 | [AnalogIn](#analogin)         |
+| 0x16 | [XRPGyro](#xrpgyro)           |
+| 0x17 | [BuiltInAccel](#builtinaccel) |
+| 0x18 | [Encoder](#encoder)           |
+
+
+#### XRPMotor
+
+| Order | Data Type | Description       |
+|-------|-----------|-------------------|
+| 0     | _uint8_t_ | ID                |
+| 1     | _float_   | Value [-1.0, 1.0] |
+
+IDs:
+| ID | Description |
+|----|-------------|
+| 0  | Left Motor  |
+| 1  | Right Motor |
+| 2  | Motor 3     |
+| 3  | Motor 4     |
+
+#### XRPServo
+
+| Order | Data Type | Description      |
+|-------|-----------|------------------|
+| 0     | _uint8_t_ | ID               |
+| 1     | _float_   | Value [0.0, 1.0] |
+
+IDs:
+| ID | Description |
+|----|-------------|
+| 4  | Servo 1     |
+| 5  | Servo 2     |
+
+#### DIO
+
+| Order | Data Type | Description        |
+|-------|-----------|--------------------|
+| 0     | _uint8_t_ | ID                 |
+| 1     | _uint8_t_ | Value (True/False) |
+
+#### AnalogIn
+
+| Order | Data Type | Description |
+|-------|-----------|-------------|
+| 0     | _uint8_t_ | ID          |
+| 1     | _float_   | Value       |
+
+#### XRPGyro
+
+| Order | Data Type | Description   |
+|-------|-----------|---------------|
+| 0     | _float_   | rate_x (dps)  |
+| 1     | _float_   | rate_y (dps)  |
+| 2     | _float_   | rate_z (dps)  |
+| 3     | _float_   | angle_x (deg) |
+| 4     | _float_   | angle_y (deg) |
+| 5     | _float_   | angle_z (deg) |
+
+#### BuiltInAccel
+
+| Order | Data Type | Description |
+|-------|-----------|-------------|
+| 0     | _float_   | accel_x (g) |
+| 1     | _float_   | accel_y (g) |
+| 2     | _float_   | accel_z (g) |
+
+#### Encoder
+
+| Order | Data Type | Description |
+|-------|-----------|-------------|
+| 0     | _uint8_t_ | ID          |
+| 1     | _int32_t_ | Value       |
+
+IDs:
+| ID | Description         |
+|----|---------------------|
+| 0  | Left Motor Encoder  |
+| 1  | Right Motor Encoder |
+| 2  | Motor 3 Encoder     |
+| 3  | Motor 4 Encoder     |
diff --git a/third_party/allwpilib/simulation/halsim_xrp/build.gradle b/third_party/allwpilib/simulation/halsim_xrp/build.gradle
new file mode 100644
index 0000000..e745820
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/build.gradle
@@ -0,0 +1,35 @@
+if (project.hasProperty('onlylinuxathena')) {
+    return;
+}
+
+description = "XRP Extension"
+
+ext {
+    includeWpiutil = true
+    pluginName = 'halsim_xrp'
+}
+
+apply plugin: 'google-test-test-suite'
+
+ext {
+    staticGtestConfigs = [:]
+}
+
+staticGtestConfigs["${pluginName}Test"] = []
+apply from: "${rootDir}/shared/googletest.gradle"
+
+apply from: "${rootDir}/shared/plugins/setupBuild.gradle"
+
+model {
+    binaries {
+        all {
+            if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                it.buildable = false
+                return
+            }
+
+            lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
+            lib project: ":simulation:halsim_ws_core", library: "halsim_ws_core", linkage: "static"
+        }
+    }
+}
diff --git a/third_party/allwpilib/simulation/halsim_xrp/src/dev/native/cpp/main.cpp b/third_party/allwpilib/simulation/halsim_xrp/src/dev/native/cpp/main.cpp
new file mode 100644
index 0000000..a3e363e
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/src/dev/native/cpp/main.cpp
@@ -0,0 +1,5 @@
+// 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.
+
+int main() {}
diff --git a/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/HALSimXRP.cpp b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/HALSimXRP.cpp
new file mode 100644
index 0000000..0f2b93b
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/HALSimXRP.cpp
@@ -0,0 +1,164 @@
+// 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 "HALSimXRP.h"
+
+#include <cstdio>
+
+#include <fmt/format.h>
+#include <wpi/Endian.h>
+#include <wpi/MathExtras.h>
+#include <wpi/SmallString.h>
+#include <wpinet/raw_uv_ostream.h>
+#include <wpinet/uv/util.h>
+
+namespace uv = wpi::uv;
+
+using namespace wpilibxrp;
+
+HALSimXRP::HALSimXRP(wpi::uv::Loop& loop,
+                     wpilibws::ProviderContainer& providers,
+                     wpilibws::HALSimWSProviderSimDevices& simDevicesProvider)
+    : m_loop(loop),
+      m_providers(providers),
+      m_simDevicesProvider(simDevicesProvider) {
+  m_loop.error.connect([](uv::Error err) {
+    fmt::print(stderr, "HALSim XRP Client libuv Error: {}\n", err.str());
+  });
+
+  m_udp_client = uv::Udp::Create(m_loop);
+  m_exec = UvExecFunc::Create(m_loop);
+  if (m_exec) {
+    m_exec->wakeup.connect([](auto func) { func(); });
+  }
+}
+
+bool HALSimXRP::Initialize() {
+  if (!m_udp_client || !m_exec) {
+    return false;
+  }
+
+  const char* host = std::getenv("HALSIMXRP_HOST");
+  if (host != nullptr) {
+    m_host = host;
+  } else {
+    m_host = "localhost";
+  }
+
+  const char* port = std::getenv("HALSIMXRP_PORT");
+  if (port != nullptr) {
+    try {
+      m_port = std::stoi(port);
+    } catch (const std::invalid_argument& err) {
+      fmt::print(stderr, "Error decoding HALSIMXRP_PORT ({})\n", err.what());
+      return false;
+    }
+  } else {
+    m_port = 3540;
+  }
+
+  wpilibxrp::WPILibUpdateFunc func = [&](const wpi::json& data) {
+    OnNetValueChanged(data);
+  };
+
+  m_xrp.SetWPILibUpdateFunc(func);
+
+  return true;
+}
+
+void HALSimXRP::Start() {
+  // struct sockaddr_in dest;
+  uv::NameToAddr(m_host, m_port, &m_dest);
+
+  m_udp_client->Connect(m_dest);
+
+  m_udp_client->received.connect(
+      [this, socket = m_udp_client.get()](auto data, size_t len, auto rinfo,
+                                          unsigned int flags) {
+        ParsePacket({reinterpret_cast<uint8_t*>(data.base), len});
+      });
+
+  m_udp_client->closed.connect([]() { fmt::print("Socket Closed\n"); });
+
+  // Fake the OnNetworkConnected call
+  auto hws = shared_from_this();
+  m_simDevicesProvider.OnNetworkConnected(hws);
+  m_providers.ForEach(
+      [hws](std::shared_ptr<wpilibws::HALSimWSBaseProvider> provider) {
+        provider->OnNetworkConnected(hws);
+      });
+
+  m_udp_client->StartRecv();
+
+  std::puts("HALSimXRP Initialized");
+}
+
+void HALSimXRP::ParsePacket(std::span<const uint8_t> packet) {
+  if (packet.size() < 3) {
+    return;
+  }
+
+  // Hand this off to the XRP object to deal with the messages
+  m_xrp.HandleXRPUpdate(packet);
+}
+
+void HALSimXRP::OnNetValueChanged(const wpi::json& msg) {
+  try {
+    auto& type = msg.at("type").get_ref<const std::string&>();
+    auto& device = msg.at("device").get_ref<const std::string&>();
+
+    wpi::SmallString<64> key;
+    key.append(type);
+    if (!device.empty()) {
+      key.append("/");
+      key.append(device);
+    }
+
+    auto provider = m_providers.Get(key.str());
+    if (provider) {
+      provider->OnNetValueChanged(msg.at("data"));
+    }
+  } catch (wpi::json::exception& e) {
+    fmt::print(stderr, "Error with incoming message: {}\n", e.what());
+  }
+}
+
+void HALSimXRP::OnSimValueChanged(const wpi::json& simData) {
+  // We'll use a signal from robot code to send all the data
+  if (simData["type"] == "HAL") {
+    auto halData = simData["data"];
+    if (halData.find(">sim_periodic_after") != halData.end()) {
+      SendStateToXRP();
+    }
+  } else {
+    m_xrp.HandleWPILibUpdate(simData);
+  }
+}
+
+uv::SimpleBufferPool<4>& HALSimXRP::GetBufferPool() {
+  static uv::SimpleBufferPool<4> bufferPool(128);
+  return bufferPool;
+}
+
+void HALSimXRP::SendStateToXRP() {
+  wpi::SmallVector<uv::Buffer, 4> sendBufs;
+  wpi::raw_uv_ostream stream{sendBufs, [&] {
+                               std::lock_guard lock(m_buffer_mutex);
+                               return GetBufferPool().Allocate();
+                             }};
+  m_xrp.SetupXRPSendBuffer(stream);
+
+  m_exec->Send([this, sendBufs]() mutable {
+    m_udp_client->Send(sendBufs, [&](auto bufs, uv::Error err) {
+      {
+        std::lock_guard lock(m_buffer_mutex);
+        GetBufferPool().Release(bufs);
+      }
+
+      if (err) {
+        // no-op
+      }
+    });
+  });
+}
diff --git a/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/HALSimXRPClient.cpp b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/HALSimXRPClient.cpp
new file mode 100644
index 0000000..2cd36c3
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/HALSimXRPClient.cpp
@@ -0,0 +1,48 @@
+// 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 "HALSimXRPClient.h"
+
+#include <WSProviderContainer.h>
+#include <WSProvider_Analog.h>
+#include <WSProvider_BuiltInAccelerometer.h>
+#include <WSProvider_DIO.h>
+#include <WSProvider_DriverStation.h>
+#include <WSProvider_Encoder.h>
+#include <WSProvider_HAL.h>
+#include <WSProvider_SimDevice.h>
+#include <wpinet/EventLoopRunner.h>
+
+using namespace wpilibxrp;
+using namespace wpilibws;
+
+bool HALSimXRPClient::Initialize() {
+  bool result = true;
+  runner.ExecSync([&](wpi::uv::Loop& loop) {
+    simxrp = std::make_shared<HALSimXRP>(loop, providers, simDevices);
+
+    if (!simxrp->Initialize()) {
+      result = false;
+      return;
+    }
+
+    WSRegisterFunc registerFunc = [&](auto key, auto provider) {
+      providers.Add(key, provider);
+    };
+
+    // Minimized set of HAL providers
+    HALSimWSProviderAnalogIn::Initialize(registerFunc);
+    HALSimWSProviderBuiltInAccelerometer::Initialize(registerFunc);
+    HALSimWSProviderDIO::Initialize(registerFunc);
+    HALSimWSProviderDriverStation::Initialize(registerFunc);
+    HALSimWSProviderEncoder::Initialize(registerFunc);
+    HALSimWSProviderHAL::Initialize(registerFunc);
+
+    simDevices.Initialize(loop);
+
+    simxrp->Start();
+  });
+
+  return result;
+}
diff --git a/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/XRP.cpp b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/XRP.cpp
new file mode 100644
index 0000000..ae78469
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/XRP.cpp
@@ -0,0 +1,386 @@
+// 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 "XRP.h"
+
+#include <fmt/format.h>
+#include <wpi/Endian.h>
+#include <wpi/MathExtras.h>
+#include <wpi/json.h>
+
+using namespace wpilibxrp;
+
+XRP::XRP()
+    : m_gyro_name{"XRPGyro"}, m_wpilib_update_func([](const wpi::json&) {}) {
+  // Set up the inputs and outputs
+  m_motor_outputs.emplace(0, 0.0f);
+  m_motor_outputs.emplace(1, 0.0f);
+  m_motor_outputs.emplace(2, 0.0f);
+  m_motor_outputs.emplace(3, 0.0f);
+
+  m_servo_outputs.emplace(4, 0.5f);
+  m_servo_outputs.emplace(5, 0.5f);
+
+  m_encoder_inputs.emplace(1, 0);
+  m_encoder_inputs.emplace(2, 0);
+  m_encoder_inputs.emplace(0, 0);
+  m_encoder_inputs.emplace(3, 0);
+}
+
+void XRP::HandleWPILibUpdate(const wpi::json& data) {
+  if (data.count("type") == 0) {
+    return;
+  }
+
+  if (data["type"] == "DriverStation") {
+    HandleDriverStationSimValueChanged(data);
+  } else if (data["type"] == "XRPMotor") {
+    HandleMotorSimValueChanged(data);
+  } else if (data["type"] == "XRPServo") {
+    HandleServoSimValueChanged(data);
+  } else if (data["type"] == "DIO") {
+    HandleDIOSimValueChanged(data);
+  } else if (data["type"] == "Gyro") {
+    HandleGyroSimValueChanged(data);
+  } else if (data["type"] == "Encoder") {
+    HandleEncoderSimValueChanged(data);
+  }
+}
+
+void XRP::HandleXRPUpdate(std::span<const uint8_t> packet) {
+  uint16_t seq = (packet[0] << 8) + packet[1];
+
+  if (seq <= m_wpilib_bound_seq) {
+    // If the old sequence was within 3 or uint16_t max and the new
+    // sequence is < 3 - we've prob rolled over
+    if (!((0xFFFF - m_wpilib_bound_seq < 3) && seq < 3)) {
+      return;
+    }
+  }
+
+  m_wpilib_bound_seq = seq;
+
+  // Tagged data starts at byte 3
+  packet = packet.subspan(3);
+
+  // Loop to handle multiple tags
+  while (!packet.empty()) {
+    auto tagLength = packet[0];
+    auto tagPacket = packet.subspan(0, tagLength + 1);
+
+    // NOTE: tagPacket contains the size and tag bytes as well
+    // Verify that the packet is indeed the right size
+    if (tagPacket.size() != static_cast<size_t>(tagLength + 1)) {
+      break;
+    }
+
+    switch (packet[1]) {
+      case XRP_TAG_GYRO:
+        ReadGyroTag(tagPacket);
+        break;
+      case XRP_TAG_ACCEL:
+        ReadAccelTag(tagPacket);
+        break;
+      case XRP_TAG_DIO:
+        ReadDIOTag(tagPacket);
+        break;
+      case XRP_TAG_ENCODER:
+        ReadEncoderTag(tagPacket);
+        break;
+      case XRP_TAG_ANALOG:
+        ReadAnalogTag(tagPacket);
+        break;
+    }
+
+    packet = packet.subspan(tagLength + 1);
+  }
+}
+
+void XRP::SetupXRPSendBuffer(wpi::raw_uv_ostream& buf) {
+  SetupSendHeader(buf);
+  SetupMotorTag(buf);
+  SetupServoTag(buf);
+  SetupDigitalOutTag(buf);
+  m_xrp_bound_seq++;
+}
+
+// WPILib Sim Handlers
+void XRP::HandleDriverStationSimValueChanged(const wpi::json& data) {
+  auto dsData = data["data"];
+  if (dsData.find(">enabled") != dsData.end()) {
+    m_robot_enabled = dsData[">enabled"];
+  }
+}
+
+void XRP::HandleMotorSimValueChanged(const wpi::json& data) {
+  int deviceId = -1;
+  auto motorData = data["data"];
+
+  if (data["device"] == "motorL") {
+    deviceId = 0;
+  } else if (data["device"] == "motorR") {
+    deviceId = 1;
+  } else if (data["device"] == "motor3") {
+    deviceId = 2;
+  } else if (data["device"] == "motor4") {
+    deviceId = 3;
+  }
+
+  if (deviceId != -1 && motorData.find("<speed") != motorData.end()) {
+    m_motor_outputs[deviceId] = motorData["<speed"];
+  }
+}
+
+void XRP::HandleServoSimValueChanged(const wpi::json& data) {
+  int deviceId = -1;
+  auto servoData = data["data"];
+
+  if (data["device"] == "servo1") {
+    deviceId = 4;
+  } else if (data["device"] == "servo2") {
+    deviceId = 5;
+  }
+
+  if (deviceId != -1 && servoData.find("<position") != servoData.end()) {
+    m_servo_outputs[deviceId] = servoData["<position"];
+  }
+}
+
+void XRP::HandleDIOSimValueChanged(const wpi::json& data) {
+  int deviceId = -1;
+  auto dioData = data["data"];
+
+  try {
+    deviceId = std::stoi(data["device"].get<std::string>());
+  } catch (const std::invalid_argument&) {
+    deviceId = -1;
+  }
+
+  // Bail out early if device ID is invalid or if it's "spoken for"
+  if (deviceId == -1) {
+    return;
+  }
+
+  if (dioData.find("<init") != dioData.end() && dioData["<init"]) {
+    // All DIOs are initialized as inputs by default
+    m_digital_inputs.emplace(deviceId, false);
+  }
+
+  if (dioData.find("<input") != dioData.end() && dioData["<input"] == false) {
+    // We're registering an output device
+    // Remove from the digital inputs list (if present)
+    m_digital_inputs.erase(deviceId);
+    m_digital_outputs.emplace(deviceId, false);
+  }
+
+  if (dioData.find("<>value") != dioData.end() &&
+      m_digital_outputs.count(deviceId) > 0) {
+    m_digital_outputs[deviceId] = dioData["<>value"];
+  }
+}
+
+void XRP::HandleGyroSimValueChanged(const wpi::json& data) {
+  m_gyro_name = data["device"].get<std::string>();
+}
+
+void XRP::HandleEncoderSimValueChanged(const wpi::json& data) {
+  // We need to handle the various encoder cases
+  // 4/5 -> Encoder 0
+  // 6/7 -> Encoder 1
+  // 8/9 -> Encoder 2
+  // 10/11 -> Encoder 3
+  int deviceId = -1;
+  auto encData = data["data"];
+
+  try {
+    deviceId = std::stoi(data["device"].get<std::string>());
+  } catch (const std::invalid_argument&) {
+    deviceId = -1;
+  }
+
+  if (deviceId == -1) {
+    return;
+  }
+
+  if (encData.find("<init") != encData.end() && encData["<init"]) {
+    // The <channel_a and <channel_b values come with the init message
+    int chA = encData["<channel_a"];
+    int chB = encData["<channel_b"];
+
+    if ((chA == 4 && chB == 5) || (chA == 5 && chB == 4)) {
+      m_encoder_channel_map.emplace(0, deviceId);
+    } else if ((chA == 6 && chB == 7) || (chA == 7 && chB == 6)) {
+      m_encoder_channel_map.emplace(1, deviceId);
+    } else if ((chA == 8 && chB == 9) || (chA == 9 && chB == 8)) {
+      m_encoder_channel_map.emplace(2, deviceId);
+    } else if ((chA == 10 && chB == 11) || (chA == 11 && chB == 10)) {
+      m_encoder_channel_map.emplace(3, deviceId);
+    }
+  }
+}
+
+// ==================================
+// XRP Buffer Generation/Read Methods
+// ==================================
+
+void XRP::SetupSendHeader(wpi::raw_uv_ostream& buf) {
+  uint8_t pktSeq[2];
+  wpi::support::endian::write16be(pktSeq, m_xrp_bound_seq);
+
+  buf << pktSeq[0] << pktSeq[1]
+      << static_cast<uint8_t>(m_robot_enabled ? 1 : 0);
+}
+
+void XRP::SetupMotorTag(wpi::raw_uv_ostream& buf) {
+  uint8_t value[4];
+
+  for (auto motor : m_motor_outputs) {
+    // Motor payload is 6 bytes
+    buf << static_cast<uint8_t>(6)              // Size
+        << static_cast<uint8_t>(XRP_TAG_MOTOR)  // Tag
+        << static_cast<uint8_t>(motor.first);   // Channel
+
+    // Convert the value
+    wpi::support::endian::write32be(value,
+                                    wpi::bit_cast<uint32_t>(motor.second));
+    buf << value[0] << value[1] << value[2] << value[3];
+  }
+}
+
+void XRP::SetupServoTag(wpi::raw_uv_ostream& buf) {
+  uint8_t value[4];
+
+  for (auto servo : m_servo_outputs) {
+    // Servo payload is 6 bytes
+    buf << static_cast<uint8_t>(6)              // Size
+        << static_cast<uint8_t>(XRP_TAG_SERVO)  // Tag
+        << static_cast<uint8_t>(servo.first);   // Channel
+
+    // Convert the value
+    wpi::support::endian::write32be(value,
+                                    wpi::bit_cast<uint32_t>(servo.second));
+    buf << value[0] << value[1] << value[2] << value[3];
+  }
+}
+
+void XRP::SetupDigitalOutTag(wpi::raw_uv_ostream& buf) {
+  for (auto digitalOut : m_digital_outputs) {
+    // DIO payload is 3 bytes
+    buf << static_cast<uint8_t>(3)                           // Size
+        << static_cast<uint8_t>(XRP_TAG_DIO)                 // Tag
+        << static_cast<uint8_t>(digitalOut.first)            // Channel
+        << static_cast<uint8_t>(digitalOut.second ? 1 : 0);  // Value
+  }
+}
+
+void XRP::ReadGyroTag(std::span<const uint8_t> packet) {
+  if (packet.size() < 26) {
+    return;  // size(1) + tag(1) + 6x 4byte
+  }
+
+  packet = packet.subspan(2);  // Skip past the size and tag
+  float rate_x =
+      wpi::bit_cast<float>(wpi::support::endian::read32be(&packet[0]));
+  float rate_y =
+      wpi::bit_cast<float>(wpi::support::endian::read32be(&packet[4]));
+  float rate_z =
+      wpi::bit_cast<float>(wpi::support::endian::read32be(&packet[8]));
+  float angle_x =
+      wpi::bit_cast<float>(wpi::support::endian::read32be(&packet[12]));
+  float angle_y =
+      wpi::bit_cast<float>(wpi::support::endian::read32be(&packet[16]));
+  float angle_z =
+      wpi::bit_cast<float>(wpi::support::endian::read32be(&packet[20]));
+
+  // Make the json object
+  wpi::json gyroJson;
+  gyroJson["type"] = "Gyro";
+  gyroJson["device"] = m_gyro_name;
+  gyroJson["data"] = {{">rate_x", rate_x},   {">rate_y", rate_y},
+                      {">rate_z", rate_z},   {">angle_x", angle_x},
+                      {">angle_y", angle_y}, {">angle_z", angle_z}};
+
+  // Update WPILib
+  m_wpilib_update_func(gyroJson);
+}
+
+void XRP::ReadAccelTag(std::span<const uint8_t> packet) {
+  if (packet.size() < 14) {
+    return;  // size(1) + tag(1) + 3x 4 byte
+  }
+
+  packet = packet.subspan(2);  // Skip past the size and tag
+  float accel_x =
+      wpi::bit_cast<float>(wpi::support::endian::read32be(&packet[0]));
+  float accel_y =
+      wpi::bit_cast<float>(wpi::support::endian::read32be(&packet[4]));
+  float accel_z =
+      wpi::bit_cast<float>(wpi::support::endian::read32be(&packet[8]));
+
+  wpi::json accelJson;
+  accelJson["type"] = "Accel";
+  accelJson["device"] = "BuiltInAccel";
+  accelJson["data"] = {{">x", accel_x}, {">y", accel_y}, {">z", accel_z}};
+
+  // Update WPILib
+  m_wpilib_update_func(accelJson);
+}
+
+void XRP::ReadDIOTag(std::span<const uint8_t> packet) {
+  if (packet.size() < 4) {
+    return;  // size(1) + tag(1) + id(1) + value(1)
+  }
+
+  wpi::json dioJson;
+  dioJson["type"] = "DIO";
+  dioJson["device"] = std::to_string(packet[2]);
+  dioJson["data"] = {{"<>value", packet[3] == 1}};
+
+  m_wpilib_update_func(dioJson);
+}
+
+void XRP::ReadEncoderTag(std::span<const uint8_t> packet) {
+  if (packet.size() < 7) {
+    return;  // size(1) + tag(1) + id(1) + value(4)
+  }
+
+  uint8_t encoderId = packet[2];
+
+  packet = packet.subspan(3);  // Skip past the size and tag and ID
+  int32_t value =
+      static_cast<int32_t>(wpi::support::endian::read32be(&packet[0]));
+
+  // Look up the registered encoders
+  if (m_encoder_channel_map.count(encoderId) == 0) {
+    return;
+  }
+
+  uint8_t wpilibEncoderChannel = m_encoder_channel_map[encoderId];
+
+  wpi::json encJson;
+  encJson["type"] = "Encoder";
+  encJson["device"] = std::to_string(wpilibEncoderChannel);
+  encJson["data"] = {{">count", value}};
+
+  m_wpilib_update_func(encJson);
+}
+
+void XRP::ReadAnalogTag(std::span<const uint8_t> packet) {
+  if (packet.size() < 7) {
+    return;  // size(1) + tag(1) + id(1) + float
+  }
+
+  uint8_t analogId = packet[2];
+
+  packet = packet.subspan(3);
+  float voltage =
+      wpi::bit_cast<float>(wpi::support::endian::read32be(&packet[0]));
+
+  wpi::json analogJson;
+  analogJson["type"] = "AI";
+  analogJson["device"] = std::to_string(analogId);
+  analogJson["data"] = {{">voltage", voltage}};
+
+  m_wpilib_update_func(analogJson);
+}
diff --git a/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/main.cpp b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/main.cpp
new file mode 100644
index 0000000..a399c59
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/cpp/main.cpp
@@ -0,0 +1,42 @@
+// 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 <cstdio>
+#include <memory>
+
+#include <hal/Extensions.h>
+
+#include "HALSimXRPClient.h"
+
+#if defined(Win32) || defined(_WIN32)
+#pragma comment(lib, "Ws2_32.lib")
+#endif
+
+using namespace wpilibxrp;
+
+static std::unique_ptr<HALSimXRPClient> gClient;
+
+/*--------------------------------------------------------------------------
+** Main Entry Point. Start up the listening threads
+**------------------------------------------------------------------------*/
+extern "C" {
+#if defined(WIN32) || defined(_WIN32)
+__declspec(dllexport)
+#endif
+
+    int HALSIM_InitExtension(void) {
+  std::puts("HALSim XRP Extension Initializing");
+
+  HAL_OnShutdown(nullptr, [](void*) { gClient.reset(); });
+
+  gClient = std::make_unique<HALSimXRPClient>();
+  if (!gClient->Initialize()) {
+    return -1;
+  }
+
+  std::puts("HALSim XRP Extension Initialized");
+  return 0;
+}
+
+}  // extern "C"
diff --git a/third_party/allwpilib/simulation/halsim_xrp/src/main/native/include/HALSimXRP.h b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/include/HALSimXRP.h
new file mode 100644
index 0000000..ee6d730
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/include/HALSimXRP.h
@@ -0,0 +1,72 @@
+// 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 <functional>
+#include <memory>
+#include <span>
+#include <string>
+
+#include <HALSimBaseWebSocketConnection.h>
+#include <WSProviderContainer.h>
+#include <WSProvider_SimDevice.h>
+#include <wpi/json_fwd.h>
+#include <wpinet/uv/Async.h>
+#include <wpinet/uv/Buffer.h>
+#include <wpinet/uv/Loop.h>
+#include <wpinet/uv/Timer.h>
+#include <wpinet/uv/Udp.h>
+
+#include "XRP.h"
+
+namespace wpilibxrp {
+
+// This masquerades as a "WebSocket" so that we can reuse the
+// stuff in halsim_ws_core
+class HALSimXRP : public wpilibws::HALSimBaseWebSocketConnection,
+                  public std::enable_shared_from_this<HALSimXRP> {
+ public:
+  using LoopFunc = std::function<void()>;
+  using UvExecFunc = wpi::uv::Async<LoopFunc>;
+
+  HALSimXRP(wpi::uv::Loop& loop, wpilibws::ProviderContainer& providers,
+            wpilibws::HALSimWSProviderSimDevices& simDevicesProvider);
+  HALSimXRP(const HALSimXRP&) = delete;
+  HALSimXRP& operator=(const HALSimXRP&) = delete;
+
+  bool Initialize();
+  void Start();
+
+  void ParsePacket(std::span<const uint8_t> packet);
+  void OnNetValueChanged(const wpi::json& msg);
+  void OnSimValueChanged(const wpi::json& simData) override;
+
+  const std::string& GetTargetHost() const { return m_host; }
+  int GetTargetPort() const { return m_port; }
+  wpi::uv::Loop& GetLoop() { return m_loop; }
+
+  UvExecFunc& GetExec() { return *m_exec; }
+
+ private:
+  XRP m_xrp;
+
+  wpi::uv::Loop& m_loop;
+  std::shared_ptr<wpi::uv::Udp> m_udp_client;
+  std::shared_ptr<UvExecFunc> m_exec;
+
+  wpilibws::ProviderContainer& m_providers;
+  wpilibws::HALSimWSProviderSimDevices& m_simDevicesProvider;
+
+  std::string m_host;
+  int m_port;
+
+  void SendStateToXRP();
+  wpi::uv::SimpleBufferPool<4>& GetBufferPool();
+  std::mutex m_buffer_mutex;
+
+  struct sockaddr_in m_dest;
+};
+
+}  // namespace wpilibxrp
diff --git a/third_party/allwpilib/simulation/halsim_xrp/src/main/native/include/HALSimXRPClient.h b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/include/HALSimXRPClient.h
new file mode 100644
index 0000000..d6b26ff
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/include/HALSimXRPClient.h
@@ -0,0 +1,31 @@
+// 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 <WSProviderContainer.h>
+#include <WSProvider_SimDevice.h>
+#include <wpinet/EventLoopRunner.h>
+
+#include "HALSimXRP.h"
+
+namespace wpilibxrp {
+
+class HALSimXRPClient {
+ public:
+  HALSimXRPClient() = default;
+  HALSimXRPClient(const HALSimXRPClient&) = delete;
+  HALSimXRPClient& operator=(const HALSimXRPClient&) = delete;
+
+  bool Initialize();
+
+  wpilibws::ProviderContainer providers;
+  wpilibws::HALSimWSProviderSimDevices simDevices{providers};
+  wpi::EventLoopRunner runner;
+  std::shared_ptr<HALSimXRP> simxrp;
+};
+
+}  // namespace wpilibxrp
diff --git a/third_party/allwpilib/simulation/halsim_xrp/src/main/native/include/XRP.h b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/include/XRP.h
new file mode 100644
index 0000000..2eaf2b0
--- /dev/null
+++ b/third_party/allwpilib/simulation/halsim_xrp/src/main/native/include/XRP.h
@@ -0,0 +1,88 @@
+// 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 <functional>
+#include <map>
+#include <span>
+#include <string>
+
+#include <wpi/json_fwd.h>
+#include <wpinet/raw_uv_ostream.h>
+
+#define XRP_TAG_MOTOR 0x12
+#define XRP_TAG_SERVO 0x13
+#define XRP_TAG_DIO 0x14
+#define XRP_TAG_ANALOG 0x15
+#define XRP_TAG_GYRO 0x16
+#define XRP_TAG_ACCEL 0x17
+#define XRP_TAG_ENCODER 0x18
+
+namespace wpilibxrp {
+
+using WPILibUpdateFunc = std::function<void(const wpi::json&)>;
+
+class XRP {
+ public:
+  XRP();
+
+  void SetWPILibUpdateFunc(WPILibUpdateFunc func) {
+    m_wpilib_update_func = func;
+  }
+
+  void HandleWPILibUpdate(const wpi::json& data);
+  void HandleXRPUpdate(std::span<const uint8_t> packet);
+
+  void SetupXRPSendBuffer(wpi::raw_uv_ostream& buf);
+
+ private:
+  // To XRP Methods
+  void SetupSendHeader(wpi::raw_uv_ostream& buf);
+  void SetupMotorTag(wpi::raw_uv_ostream& buf);
+  void SetupServoTag(wpi::raw_uv_ostream& buf);
+  void SetupDigitalOutTag(wpi::raw_uv_ostream& buf);
+
+  // WPILib Sim Update Handlers
+  void HandleDriverStationSimValueChanged(const wpi::json& data);
+  void HandleMotorSimValueChanged(const wpi::json& data);
+  void HandleServoSimValueChanged(const wpi::json& data);
+  void HandleDIOSimValueChanged(const wpi::json& data);
+  void HandleGyroSimValueChanged(const wpi::json& data);
+  void HandleEncoderSimValueChanged(const wpi::json& data);
+
+  // XRP Packet Update Handlers
+  void ReadGyroTag(std::span<const uint8_t> packet);
+  void ReadAccelTag(std::span<const uint8_t> packet);
+  void ReadDIOTag(std::span<const uint8_t> packet);
+  void ReadEncoderTag(std::span<const uint8_t> packet);
+  void ReadAnalogTag(std::span<const uint8_t> packet);
+
+  // Robot State
+  std::map<uint8_t, bool> m_digital_outputs;
+  std::map<uint8_t, float> m_motor_outputs;
+  std::map<uint8_t, float> m_servo_outputs;
+
+  // Might not need these
+  std::map<uint8_t, bool> m_digital_inputs;
+  std::map<uint8_t, float> m_analog_inputs;
+  std::map<uint8_t, int32_t> m_encoder_inputs;
+
+  // We need a map from XRP encoder channels (0=left, 1=right etc)
+  // to WPILib device ID
+  // Key: XRP encoder number, Value: WPILib channel
+  // If no encoders are init-ed, this map is empty
+  std::map<uint8_t, uint8_t> m_encoder_channel_map;
+
+  uint16_t m_wpilib_bound_seq = 0;
+  uint16_t m_xrp_bound_seq = 0;
+
+  bool m_robot_enabled = false;
+
+  std::string m_gyro_name;
+
+  WPILibUpdateFunc m_wpilib_update_func;
+};
+
+}  // namespace wpilibxrp
diff --git a/third_party/allwpilib/styleguide/checkstyle-suppressions.xml b/third_party/allwpilib/styleguide/checkstyle-suppressions.xml
index 9a66026..2197fe2 100644
--- a/third_party/allwpilib/styleguide/checkstyle-suppressions.xml
+++ b/third_party/allwpilib/styleguide/checkstyle-suppressions.xml
@@ -6,8 +6,10 @@
   <suppress files=".*test.*" checks="MissingJavadocMethod" />
   <suppress files=".*wpilibjIntegrationTests.*"
     checks="MissingJavadocMethod" />
-  <suppress files="wpimath/*"
+  <suppress files="wpimath.*"
     checks="(LocalVariableName|MemberName|MethodName|MethodTypeParameterName|ParameterName)" />
   <suppress files=".*JNI.*"
     checks="(EmptyLineSeparator|LineLength|MissingJavadocMethod|ParameterName)" />
+  <suppress files=".*quickbuf.*"
+    checks="(CustomImportOrder|EmptyLineSeparator|LineLength|JavadocParagraph|MissingJavadocMethod|OverloadMethodsDeclarationOrder|SummaryJavadoc|UnnecessaryParentheses|OperatorWrap|JavadocMethod|JavadocTagContinuationIndentation)" />
 </suppressions>
diff --git a/third_party/allwpilib/styleguide/pmd-ruleset.xml b/third_party/allwpilib/styleguide/pmd-ruleset.xml
index 54a4881..f2ef2bc 100644
--- a/third_party/allwpilib/styleguide/pmd-ruleset.xml
+++ b/third_party/allwpilib/styleguide/pmd-ruleset.xml
@@ -8,6 +8,7 @@
 
   <exclude-pattern>.*/*JNI.*</exclude-pattern>
   <exclude-pattern>.*/*IntegrationTests.*</exclude-pattern>
+  <exclude-pattern>.*/quickbuf/.*</exclude-pattern>
 
   <rule ref="category/java/bestpractices.xml">
     <exclude name="AccessorClassGeneration" />
@@ -64,7 +65,6 @@
     <exclude name="AvoidCatchingThrowable" />
     <exclude name="AvoidDuplicateLiterals" />
     <exclude name="AvoidLiteralsInIfCondition" />
-    <exclude name="BeanMembersShouldSerialize" />
     <exclude name="CloseResource" />
     <exclude name="ConstructorCallsOverridableMethod" />
     <exclude name="DataflowAnomalyAnalysis" />
@@ -74,6 +74,7 @@
     <exclude name="FinalizeDoesNotCallSuperFinalize" />
     <exclude name="JUnitSpelling" />
     <exclude name="MissingSerialVersionUID" />
+    <exclude name="NonSerializableClass" />
     <exclude name="NullAssignment" />
   </rule>
 
diff --git a/third_party/allwpilib/styleguide/spotbugs-exclude.xml b/third_party/allwpilib/styleguide/spotbugs-exclude.xml
index c5e2d20..f224099 100644
--- a/third_party/allwpilib/styleguide/spotbugs-exclude.xml
+++ b/third_party/allwpilib/styleguide/spotbugs-exclude.xml
@@ -110,9 +110,21 @@
   </Match>
   <Match>
     <Bug pattern="UC_USELESS_VOID_METHOD" />
+    <Class name="edu.wpi.first.wpilibj.templates.xrptimed.Robot" />
+  </Match>
+  <Match>
+    <Bug pattern="UC_USELESS_VOID_METHOD" />
     <Class name="edu.wpi.first.wpilibj.templates.timed.Robot" />
   </Match>
   <Match>
+    <Bug pattern="UC_USELESS_VOID_METHOD" />
+    <Class name="edu.wpi.first.wpilibj.templates.timeslice.Robot" />
+  </Match>
+  <Match>
+    <Bug pattern="UC_USELESS_VOID_METHOD" />
+    <Class name="edu.wpi.first.wpilibj.templates.timesliceskeleton.Robot" />
+  </Match>
+  <Match>
     <Bug pattern="URF_UNREAD_FIELD" />
     <Class name="edu.wpi.first.wpilibj.ADIS16448_IMU" />
   </Match>
diff --git a/third_party/allwpilib/sysid/.styleguide b/third_party/allwpilib/sysid/.styleguide
new file mode 100644
index 0000000..8506bf1
--- /dev/null
+++ b/third_party/allwpilib/sysid/.styleguide
@@ -0,0 +1,35 @@
+cppHeaderFileInclude {
+  \.h$
+  \.inc$
+  \.inl$
+}
+
+cppSrcFileInclude {
+  \.cpp$
+}
+
+generatedFileExclude {
+  src/main/native/resources/
+  src/main/native/win/sysid.ico
+  src/main/native/mac/sysid.icns
+}
+
+repoRootNameOverride {
+  sysid
+}
+
+includeOtherLibs {
+  ^GLFW
+  ^fmt/
+  ^frc/
+  ^glass/
+  ^gtest/
+  ^imgui
+  ^implot\.h$
+  ^networktables/
+  ^portable-file-dialogs\.h$
+  ^ntcore
+  ^units/
+  ^wpi/
+  ^wpigui
+}
diff --git a/third_party/allwpilib/sysid/CMakeLists.txt b/third_party/allwpilib/sysid/CMakeLists.txt
new file mode 100644
index 0000000..31fbe08
--- /dev/null
+++ b/third_party/allwpilib/sysid/CMakeLists.txt
@@ -0,0 +1,41 @@
+project(sysid)
+
+include(CompileWarnings)
+include(GenResources)
+include(LinkMacOSGUI)
+include(AddTest)
+
+configure_file(src/main/generate/WPILibVersion.cpp.in WPILibVersion.cpp)
+generate_resources(src/main/native/resources generated/main/cpp SYSID sysid sysid_resources_src)
+
+file(GLOB_RECURSE sysid_src src/main/native/cpp/*.cpp ${CMAKE_CURRENT_BINARY_DIR}/WPILibVersion.cpp)
+
+if (WIN32)
+    set(sysid_rc src/main/native/win/sysid.rc)
+elseif(APPLE)
+    set(MACOSX_BUNDLE_ICON_FILE sysid.icns)
+    set(APP_ICON_MACOSX src/main/native/mac/sysid.icns)
+    set_source_files_properties(${APP_ICON_MACOSX} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
+endif()
+
+add_executable(sysid ${sysid_src} ${sysid_resources_src} ${sysid_rc} ${APP_ICON_MACOSX})
+wpilib_link_macos_gui(sysid)
+wpilib_target_warnings(sysid)
+target_include_directories(sysid PRIVATE src/main/native/include)
+target_link_libraries(sysid wpimath libglassnt libglass)
+
+if (WIN32)
+    set_target_properties(sysid PROPERTIES WIN32_EXECUTABLE YES)
+elseif(APPLE)
+    set_target_properties(sysid PROPERTIES MACOSX_BUNDLE YES OUTPUT_NAME "SysId")
+endif()
+
+if (WITH_TESTS)
+    wpilib_add_test(sysid src/test/native/cpp)
+    wpilib_link_macos_gui(sysid_test)
+    target_sources(sysid_test PRIVATE ${sysid_src})
+    target_compile_definitions(sysid_test PRIVATE RUNNING_SYSID_TESTS)
+    target_include_directories(sysid_test PRIVATE src/main/native/cpp
+                                                  src/main/native/include)
+    target_link_libraries(sysid_test wpimath libglassnt libglass gtest)
+endif()
diff --git a/third_party/allwpilib/sysid/Info.plist b/third_party/allwpilib/sysid/Info.plist
new file mode 100644
index 0000000..37bb798
--- /dev/null
+++ b/third_party/allwpilib/sysid/Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleName</key>
+	<string>SysId</string>
+	<key>CFBundleExecutable</key>
+	<string>sysid</string>
+	<key>CFBundleDisplayName</key>
+	<string>SysId</string>
+	<key>CFBundleIdentifier</key>
+	<string>edu.wpi.first.tools.SysId</string>
+	<key>CFBundleIconFile</key>
+	<string>sysid.icns</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSupportedPlatforms</key>
+	<array>
+		<string>MacOSX</string>
+	</array>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleShortVersionString</key>
+	<string>2021</string>
+	<key>CFBundleVersion</key>
+	<string>2021</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>10.14</string>
+	<key>NSHighResolutionCapable</key>
+	<true/>
+</dict>
+</plist>
diff --git a/third_party/allwpilib/sysid/build.gradle b/third_party/allwpilib/sysid/build.gradle
new file mode 100644
index 0000000..e6709fc
--- /dev/null
+++ b/third_party/allwpilib/sysid/build.gradle
@@ -0,0 +1,172 @@
+import org.gradle.internal.os.OperatingSystem
+
+if (project.hasProperty('onlylinuxathena')) {
+    return;
+}
+
+description = 'System identification for robot mechanisms'
+
+apply plugin: 'cpp'
+apply plugin: 'google-test-test-suite'
+apply plugin: 'visual-studio'
+apply plugin: 'edu.wpi.first.NativeUtils'
+
+if (OperatingSystem.current().isWindows()) {
+    apply plugin: 'windows-resources'
+}
+
+ext {
+    nativeName = 'sysid'
+}
+
+apply from: "${rootDir}/shared/resources.gradle"
+apply from: "${rootDir}/shared/config.gradle"
+
+def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in")
+def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp")
+
+apply from: "${rootDir}/shared/imgui.gradle"
+
+task generateCppVersion() {
+    description = 'Generates the wpilib version class'
+    group = 'WPILib'
+
+    outputs.file wpilibVersionFileOutput
+    inputs.file wpilibVersionFileInput
+
+    if (wpilibVersioning.releaseMode) {
+        outputs.upToDateWhen { false }
+    }
+
+    // We follow a simple set of checks to determine whether we should generate a new version file:
+    // 1. If the release type is not development, we generate a new version file
+    // 2. If there is no generated version number, we generate a new version file
+    // 3. If there is a generated build number, and the release type is development, then we will
+    //    only generate if the publish task is run.
+    doLast {
+        def version = wpilibVersioning.version.get()
+        println "Writing version ${version} to $wpilibVersionFileOutput"
+
+        if (wpilibVersionFileOutput.exists()) {
+            wpilibVersionFileOutput.delete()
+        }
+        def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version)
+        wpilibVersionFileOutput.write(read)
+    }
+}
+
+gradle.taskGraph.addTaskExecutionGraphListener { graph ->
+    def willPublish = graph.hasTask(publish)
+    if (willPublish) {
+        generateCppVersion.outputs.upToDateWhen { false }
+    }
+}
+
+def generateTask = createGenerateResourcesTask('main', 'SYSID', 'sysid', project)
+
+project(':').libraryBuild.dependsOn build
+tasks.withType(CppCompile) {
+    dependsOn generateTask
+    dependsOn generateCppVersion
+}
+
+model {
+    components {
+        // By default, a development executable will be generated. This is to help the case of
+        // testing specific functionality of the library.
+        "${nativeName}"(NativeExecutableSpec) {
+            baseName = 'sysid'
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp"
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/main/native/include'
+                    }
+                }
+                if (OperatingSystem.current().isWindows()) {
+                    rc.source {
+                        srcDirs 'src/main/native/win'
+                        include '*.rc'
+                    }
+                }
+            }
+            binaries.all {
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    it.buildable = false
+                    return
+                }
+                lib project: ':glass', library: 'glassnt', linkage: 'static'
+                lib project: ':glass', library: 'glass', linkage: 'static'
+                project(':ntcore').addNtcoreDependency(it, 'static')
+                lib project: ':wpinet', library: 'wpinet', linkage: 'static'
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                lib project: ':wpimath', library: 'wpimath', linkage: 'static'
+                lib project: ':wpigui', library: 'wpigui', linkage: 'static'
+                nativeUtils.useRequiredLibrary(it, 'imgui')
+                if (it.targetPlatform.operatingSystem.isWindows()) {
+                    it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
+                    it.linker.args << '/DELAYLOAD:MF.dll' << '/DELAYLOAD:MFReadWrite.dll' << '/DELAYLOAD:MFPlat.dll' << '/delay:nobind'
+                } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
+                    it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
+                    if (it.buildType.getName() == "release") {
+                        it.linker.args << '-s'
+                    }
+                } else {
+                    it.linker.args << '-lX11'
+                    if (it.targetPlatform.name.startsWith('linuxarm')) {
+                        it.linker.args << '-lGL'
+                    }
+                }
+            }
+        }
+    }
+    testSuites {
+        "${nativeName}Test"(GoogleTestTestSuiteSpec) {
+            for (NativeComponentSpec c : $.components) {
+                if (c.name == nativeName) {
+                    testing c
+                    break
+                }
+            }
+            sources.cpp.source {
+                srcDirs "src/test/native/cpp"
+                include "**/*.cpp"
+            }
+            binaries.all {
+                if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    it.buildable = false
+                    return
+                }
+                lib project: ':glass', library: 'glassnt', linkage: 'static'
+                lib project: ':glass', library: 'glass', linkage: 'static'
+                project(':ntcore').addNtcoreDependency(it, 'static')
+                lib project: ':wpinet', library: 'wpinet', linkage: 'static'
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                lib project: ':wpimath', library: 'wpimath', linkage: 'static'
+                lib project: ':wpigui', library: 'wpigui', linkage: 'static'
+                nativeUtils.useRequiredLibrary(it, 'imgui')
+                if (it.targetPlatform.operatingSystem.isWindows()) {
+                    it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
+                    it.linker.args << '/DELAYLOAD:MF.dll' << '/DELAYLOAD:MFReadWrite.dll' << '/DELAYLOAD:MFPlat.dll' << '/delay:nobind'
+                } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
+                    it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
+                    if (it.buildType.getName() == "release") {
+                        it.linker.args << '-s'
+                    }
+                } else {
+                    it.linker.args << '-lX11'
+                    if (it.targetPlatform.name.startsWith('linuxarm')) {
+                        it.linker.args << '-lGL'
+                    }
+                }
+                nativeUtils.useRequiredLibrary(it, "googletest_static")
+                it.cppCompiler.define("RUNNING_SYSID_TESTS")
+            }
+        }
+    }
+}
+
+apply from: 'publish.gradle'
diff --git a/third_party/allwpilib/sysid/docs/arm-ols-with-angle-offset.md b/third_party/allwpilib/sysid/docs/arm-ols-with-angle-offset.md
new file mode 100644
index 0000000..ecb5c43
--- /dev/null
+++ b/third_party/allwpilib/sysid/docs/arm-ols-with-angle-offset.md
@@ -0,0 +1,66 @@
+# Arm OLS with angle offset
+
+If the arm encoder doesn't read zero degrees when the arm is horizontal, the fit
+for `Kg` will be wrong. An angle offset should be added to the model like so.
+```
+dx/dt = -Kv/Ka x + 1/Ka u - Ks/Ka sgn(x) - Kg/Ka cos(angle + offset)
+```
+Use a trig identity to split the cosine into two terms.
+```
+dx/dt = -Kv/Ka x + 1/Ka u - Ks/Ka sgn(x) - Kg/Ka (cos(angle) cos(offset) - sin(angle) sin(offset))
+dx/dt = -Kv/Ka x + 1/Ka u - Ks/Ka sgn(x) - Kg/Ka cos(angle) cos(offset) + Kg/Ka sin(angle) sin(offset)
+```
+Reorder multiplicands so the offset trig is absorbed by the OLS terms.
+```
+dx/dt = -Kv/Ka x + 1/Ka u - Ks/Ka sgn(x) - Kg/Ka cos(offset) cos(angle) + Kg/Ka sin(offset) sin(angle)
+```
+
+## OLS
+
+Let `α = -Kv/Ka`, `β = 1/Ka`, `γ = -Ks/Ka`, `δ = -Kg/Ka cos(offset)`, and `ε = Kg/Ka sin(offset)`.
+```
+dx/dt = αx + βu + γ sgn(x) + δ cos(angle) + ε sin(angle)
+```
+
+### Ks, Kv, Ka
+
+Divide the OLS terms by each other to obtain `Ks`, `Kv`, and `Ka`.
+```
+Ks = -γ/β
+Kv = -α/β
+Ka = 1/β
+```
+
+### Kg
+
+Take the sum of squares of the OLS terms containing the angle offset. The angle
+offset trig functions will form a trig identity that cancels out. Then, just
+solve for `Kg`.
+```
+δ²+ε² = (-Kg/Ka cos(offset))² + (Kg/Ka sin(offset))²
+δ²+ε² = (-Kg/Ka)² cos²(offset) + (Kg/Ka)² sin²(offset)
+δ²+ε² = (Kg/Ka)² cos²(offset) + (Kg/Ka)² sin²(offset)
+δ²+ε² = (Kg/Ka)² (cos²(offset) + sin²(offset))
+δ²+ε² = (Kg/Ka)² (1)
+δ²+ε² = (Kg/Ka)²
+√(δ²+ε²) = Kg/Ka
+√(δ²+ε²) = Kg β
+Kg = √(δ²+ε²)/β
+```
+
+As a sanity check, when the offset is zero, ε is zero and the equation for
+`Kg` simplifies to -δ/β, the equation previously used by SysId.
+
+### Angle offset
+
+Divide ε by δ, combine the trig functions into `tan(offset)`, then use `atan2()`
+to preserve the angle quadrant. Maintaining the proper negative signs in the
+numerator and denominator are important for obtaining the correct result.
+```
+δ = -Kg/Ka cos(offset)
+ε = Kg/Ka sin(offset)
+sin(offset)/-cos(offset) = ε/δ
+sin(offset)/cos(offset) = ε/-δ
+tan(offset) = ε/-δ
+offset = atan2(ε, -δ)
+```
diff --git a/third_party/allwpilib/sysid/docs/data-collection.md b/third_party/allwpilib/sysid/docs/data-collection.md
new file mode 100644
index 0000000..9d28a15
--- /dev/null
+++ b/third_party/allwpilib/sysid/docs/data-collection.md
@@ -0,0 +1,210 @@
+# Data Collection
+
+This document details how data must be sent over NetworkTables for accurate data collection. Note that the data format has changed from what the old [frc-characterization](https://github.com/wpilibsuite/frc-characterization) tool used to generate.
+
+## NetworkTables Data Entries
+
+Here is a list of the NT entries that are used to send and collect data between sysid and the robot program:
+
+|               NT Entry                |   Type   |           Description                                                                                                                                        |
+| --------------------------------------| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `/SmartDashboard/SysIdTelemetry`      | `string` | Used to send telemetry from the robot program. This data is sent after the test completes once the robot enters the disabled state.  |
+| `/SmartDashboard/SysIdVoltageCommand` | `double` | Used to either send the ramp rate (V/s) for the quasistatic test or the voltage (V) for the dynamic test.  |
+| `/SmartDashboard/SysIdTestType`       | `string` | Used to send the test type ("Quasistatic" or "Dynamic") which helps determine how the `VoltageCommand` entry will be used.  |
+| `/SmartDashboard/SysIdRotate`         | `bool`   | Used to receive the rotation bool from the Logger. If this is set to true, the drivetrain will rotate. It is only applicable for drivetrain tests.  |
+
+## Telemetry Format
+
+There are two formats used to send telemetry from the robot program. One format is for non-drivetrain mechanisms, whereas the other is for all drivetrain tests (linear and angular).
+
+### Non-Drivetrain Mechanisms
+
+`timestamp, voltage, position, velocity`
+
+Example JSON:
+
+```json
+{
+"fast-backward": [
+[
+timestamp 1,
+voltage 1,
+position 1,
+velocity 1
+],
+[
+timestamp 2,
+voltage 2,
+position 2,
+velocity 2
+]
+],
+"fast-forward": [
+[
+timestamp 1,
+voltage 1,
+position 1,
+velocity 1
+],
+[
+timestamp 2,
+voltage 2,
+position 2,
+velocity 2
+]
+],
+"slow-backward": [
+[
+timestamp 1,
+voltage 1,
+position 1,
+velocity 1
+],
+[
+timestamp 2,
+voltage 2,
+position 2,
+velocity 2
+]
+],
+"slow-forward": [
+[
+timestamp 1,
+voltage 1,
+position 1,
+velocity 1
+],
+[
+timestamp 2,
+voltage 2,
+position 2,
+velocity 2
+]
+],
+"sysid": true,
+"test": "Simple",
+"units": "Rotations",
+"unitsPerRotation": 1.0
+}
+```
+
+Supported test types for the "test" field in this data format include "Arm",
+"Elevator", and "Simple". Supported unit types include "Meters", "Feet",
+"Inches", "Radians", "Rotations", and "Degrees".
+
+### Drivetrain
+
+`timestamp, l voltage, r voltage, l position, r position, l velocity, r velocity, angle, angular rate`
+
+Note that all positions and velocities should be in rotations of the output and rotations/sec of the output respectively. If there is a gearing between the encoder and the output, that should be taken into account.
+
+Example JSON:
+
+```json
+{
+"fast-backward": [
+[
+timestamp 1,
+l voltage 1,
+r voltage 1,
+l position 1,
+r position 1,
+l velocity 1,
+r velocity 1,
+angle 1,
+angular rate 1
+],
+[
+timestamp 2,
+l voltage 2,
+r voltage 2,
+l position 2,
+r position 2,
+l velocity 2,
+r velocity 2,
+angle 2,
+angular rate 2
+]
+],
+"fast-forward": [
+[
+timestamp 1,
+l voltage 1,
+r voltage 1,
+l position 1,
+r position 1,
+l velocity 1,
+r velocity 1,
+angle 1,
+angular rate 1
+],
+[
+timestamp 2,
+l voltage 2,
+r voltage 2,
+l position 2,
+r position 2,
+l velocity 2,
+r velocity 2,
+angle 2,
+angular rate 2
+]
+],
+"slow-backward": [
+[
+timestamp 1,
+l voltage 1,
+r voltage 1,
+l position 1,
+r position 1,
+l velocity 1,
+r velocity 1,
+angle 1,
+angular rate 1
+],
+[
+timestamp 2,
+l voltage 2,
+r voltage 2,
+l position 2,
+r position 2,
+l velocity 2,
+r velocity 2,
+angle 2,
+angular rate 2
+]
+],
+"slow-forward": [
+[
+timestamp 1,
+l voltage 1,
+r voltage 1,
+l position 1,
+r position 1,
+l velocity 1,
+r velocity 1,
+angle 1,
+angular rate 1
+],
+[
+timestamp 2,
+l voltage 2,
+r voltage 2,
+l position 2,
+r position 2,
+l velocity 2,
+r velocity 2,
+angle 2,
+angular rate 2
+]
+],
+"sysid": true,
+"test": "Drivetrain",
+"units": "Rotations",
+"unitsPerRotation": 1.0
+}
+```
+
+Supported test types for the "test" field in this data format include
+"Drivetrain" and "Drivetrain (Angular)". Supported unit types include "Meters",
+"Feet", "Inches", "Radians", "Rotations", and "Degrees".
diff --git a/third_party/allwpilib/sysid/publish.gradle b/third_party/allwpilib/sysid/publish.gradle
new file mode 100644
index 0000000..1356946
--- /dev/null
+++ b/third_party/allwpilib/sysid/publish.gradle
@@ -0,0 +1,125 @@
+apply plugin: 'maven-publish'
+
+def baseArtifactId = 'SysId'
+def artifactGroupId = 'edu.wpi.first.tools'
+def zipBaseName = '_GROUP_edu_wpi_first_tools_ID_SysId_CLS'
+
+def outputsFolder = file("$project.buildDir/outputs")
+
+model {
+    tasks {
+        // Create the run task.
+        $.components.sysid.binaries.each { bin ->
+            if (bin.buildable && bin.name.toLowerCase().contains("debug") && nativeUtils.isNativeDesktopPlatform(bin.targetPlatform)) {
+                Task run = project.tasks.create("run", Exec) {
+                    commandLine bin.tasks.install.runScriptFile.get().asFile.toString()
+                }
+                run.dependsOn bin.tasks.install
+            }
+        }
+    }
+    publishing {
+        def sysIdTaskList = []
+        $.components.each { component ->
+            component.binaries.each { binary ->
+                if (binary in NativeExecutableBinarySpec && binary.component.name.contains("sysid")) {
+                    if (binary.buildable && (binary.name.contains('Release') || binary.name.contains('release'))) {
+                        // We are now in the binary that we want.
+                        // This is the default application path for the ZIP task.
+                        def applicationPath = binary.executable.file
+                        def icon = file("$project.projectDir/src/main/native/mac/ov.icns")
+
+                        // Create the macOS bundle.
+                        def bundleTask = project.tasks.create("bundleSysIdOsxApp" + binary.targetPlatform.architecture.name, Copy) {
+                            description("Creates a macOS application bundle for SysId")
+                            from(file("$project.projectDir/Info.plist"))
+                            into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/SysId.app/Contents"))
+                            into("MacOS") {
+                                with copySpec {
+                                    from binary.executable.file
+                                }
+                            }
+                            into("Resources") {
+                                with copySpec {
+                                    from icon
+                                }
+                            }
+
+                            inputs.property "HasDeveloperId", project.hasProperty("developerID")
+
+                            doLast {
+                                if (project.hasProperty("developerID")) {
+                                    // Get path to binary.
+                                    exec {
+                                        workingDir rootDir
+                                        def args = [
+                                            "sh",
+                                            "-c",
+                                            "codesign --force --strict --deep " +
+                                            "--timestamp --options=runtime " +
+                                            "--verbose -s ${project.findProperty("developerID")} " +
+                                            "$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/SysId.app/"
+                                        ]
+                                        commandLine args
+                                    }
+                                }
+                            }
+                        }
+
+                        // Reset the application path if we are creating a bundle.
+                        if (binary.targetPlatform.operatingSystem.isMacOsX()) {
+                            applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name")
+                            project.build.dependsOn bundleTask
+                        }
+
+                        // Create the ZIP.
+                        def task = project.tasks.create("copySysIdExecutable" + binary.targetPlatform.architecture.name, Zip) {
+                            description("Copies the SysId executable to the outputs directory.")
+                            destinationDirectory = outputsFolder
+
+                            archiveBaseName = '_M_' + zipBaseName
+                            duplicatesStrategy = 'exclude'
+                            archiveClassifier = nativeUtils.getPublishClassifier(binary)
+
+                            from(licenseFile) {
+                                into '/'
+                            }
+
+                            from(applicationPath)
+
+                            if (binary.targetPlatform.operatingSystem.isWindows()) {
+                                def exePath = binary.executable.file.absolutePath
+                                exePath = exePath.substring(0, exePath.length() - 4)
+                                def pdbPath = new File(exePath + '.pdb')
+                                from(pdbPath)
+                            }
+
+                            into(nativeUtils.getPlatformPath(binary))
+                        }
+
+                        if (binary.targetPlatform.operatingSystem.isMacOsX()) {
+                            bundleTask.dependsOn binary.tasks.link
+                            task.dependsOn(bundleTask)
+                        }
+
+                        task.dependsOn binary.tasks.link
+                        sysIdTaskList.add(task)
+                        project.build.dependsOn task
+                        project.artifacts { task }
+                        addTaskToCopyAllOutputs(task)
+                    }
+                }
+            }
+        }
+
+        publications {
+            sysId(MavenPublication) {
+                sysIdTaskList.each { artifact it }
+
+                artifactId = baseArtifactId
+                groupId = artifactGroupId
+                version wpilibVersioning.version.get()
+            }
+        }
+    }
+}
diff --git a/third_party/allwpilib/sysid/scripts/time_plots.py b/third_party/allwpilib/sysid/scripts/time_plots.py
new file mode 100755
index 0000000..6878126
--- /dev/null
+++ b/third_party/allwpilib/sysid/scripts/time_plots.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+
+import json
+import pathlib
+
+import matplotlib.pyplot as plt
+import pandas as pd
+import sys
+
+# Load data
+filename = pathlib.Path(sys.argv[1])
+
+UNIT_TO_ABBREVIATION = {
+    "Meters": "m",
+    "Feet": "ft",
+    "Inches": "in",
+    "Degrees": "deg",
+    "Rotations": "rot",
+    "Radians": "rad",
+}
+
+# Make DataFrame to facilitate plotting
+if filename.suffix == ".json":
+    raw_data = json.loads(filename.read_text())
+    unit = raw_data["units"]
+
+    # Get Unit
+    try:
+        abbreviation = UNIT_TO_ABBREVIATION[unit]
+    except KeyError:
+        raise ValueError("Invalid Unit")
+
+    # Make Columns
+    columns = ["Timestamp (s)", "Test"]
+    if "Drive" in raw_data["test"]:
+        columns.extend(
+            [
+                "Left Volts (V)",
+                "Right Volts (V)",
+                f"Left Position ({abbreviation})",
+                f"Right Position ({abbreviation})",
+                f"Left Velocity ({abbreviation}/s)",
+                f"Right Velocity ({abbreviation}/s)",
+                "Gyro Position (deg)",
+                "Gyro Rate (deg/s)",
+            ]
+        )
+        unit_columns = columns[4:8]
+    else:
+        columns.extend(
+            ["Volts (V)", f"Position ({abbreviation})", f"Velocity ({abbreviation}/s)"]
+        )
+        unit_columns = columns[3:]
+
+    prepared_data = pd.DataFrame(columns=columns)
+    for test in raw_data.keys():
+        if "-" not in test:
+            continue
+        formatted_entry = [[pt[0], test, *pt[1:]] for pt in raw_data[test]]
+        prepared_data = pd.concat(
+            [prepared_data, pd.DataFrame(formatted_entry, columns=columns)]
+        )
+
+    units_per_rot = raw_data["unitsPerRotation"]
+
+    for column in unit_columns:
+        prepared_data[column] *= units_per_rot
+else:
+    prepared_data = pd.read_csv(filename)
+
+# First 2 columns are Timestamp and Test
+for column in prepared_data.columns[2:]:
+    # Configure Plot Labels
+    plt.figure()
+    plt.xlabel("Timestamp (s)")
+    plt.ylabel(column)
+
+    # Configure title without units
+    print(column)
+    end = column.find("(")
+    plt.title(f"{column[:end].strip()} vs Time")
+
+    # Plot data for each test
+    for test in pd.unique(prepared_data["Test"]):
+        test_data = prepared_data[prepared_data["Test"] == test]
+        plt.plot(test_data["Timestamp (s)"], test_data[column], label=test)
+    plt.legend()
+
+plt.show()
diff --git a/third_party/allwpilib/sysid/src/main/generate/WPILibVersion.cpp.in b/third_party/allwpilib/sysid/src/main/generate/WPILibVersion.cpp.in
new file mode 100644
index 0000000..b0a4490
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/generate/WPILibVersion.cpp.in
@@ -0,0 +1,7 @@
+/*
+ * Autogenerated file! Do not manually edit this file. This version is regenerated
+ * any time the publish task is run, or when this file is deleted.
+ */
+const char* GetWPILibVersion() {
+  return "${wpilib_version}";
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/App.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/App.cpp
new file mode 100644
index 0000000..947ea43
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/App.cpp
@@ -0,0 +1,219 @@
+// 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 <cstdio>
+
+#ifndef RUNNING_SYSID_TESTS
+
+#include <filesystem>
+#include <memory>
+#include <string_view>
+
+#include <fmt/format.h>
+#include <glass/Context.h>
+#include <glass/MainMenuBar.h>
+#include <glass/Storage.h>
+#include <glass/Window.h>
+#include <glass/WindowManager.h>
+#include <glass/other/Log.h>
+#include <imgui.h>
+#include <uv.h>
+#include <wpi/Logger.h>
+#include <wpigui.h>
+#include <wpigui_openurl.h>
+
+#include "sysid/view/Analyzer.h"
+#include "sysid/view/JSONConverter.h"
+#include "sysid/view/Logger.h"
+#include "sysid/view/UILayout.h"
+
+namespace gui = wpi::gui;
+
+static std::unique_ptr<glass::WindowManager> gWindowManager;
+
+glass::Window* gLoggerWindow;
+glass::Window* gAnalyzerWindow;
+glass::Window* gProgramLogWindow;
+static glass::MainMenuBar gMainMenu;
+
+std::unique_ptr<sysid::JSONConverter> gJSONConverter;
+
+glass::LogData gLog;
+wpi::Logger gLogger;
+
+const char* GetWPILibVersion();
+
+namespace sysid {
+std::string_view GetResource_sysid_16_png();
+std::string_view GetResource_sysid_32_png();
+std::string_view GetResource_sysid_48_png();
+std::string_view GetResource_sysid_64_png();
+std::string_view GetResource_sysid_128_png();
+std::string_view GetResource_sysid_256_png();
+std::string_view GetResource_sysid_512_png();
+}  // namespace sysid
+
+void Application(std::string_view saveDir) {
+  // Create the wpigui (along with Dear ImGui) and Glass contexts.
+  gui::CreateContext();
+  glass::CreateContext();
+
+  // Add icons
+  gui::AddIcon(sysid::GetResource_sysid_16_png());
+  gui::AddIcon(sysid::GetResource_sysid_32_png());
+  gui::AddIcon(sysid::GetResource_sysid_48_png());
+  gui::AddIcon(sysid::GetResource_sysid_64_png());
+  gui::AddIcon(sysid::GetResource_sysid_128_png());
+  gui::AddIcon(sysid::GetResource_sysid_256_png());
+  gui::AddIcon(sysid::GetResource_sysid_512_png());
+
+  glass::SetStorageName("sysid");
+  glass::SetStorageDir(saveDir.empty() ? gui::GetPlatformSaveFileDir()
+                                       : saveDir);
+
+  // Add messages from the global sysid logger into the Log window.
+  gLogger.SetLogger([](unsigned int level, const char* file, unsigned int line,
+                       const char* msg) {
+    const char* lvl = "";
+    if (level >= wpi::WPI_LOG_CRITICAL) {
+      lvl = "CRITICAL: ";
+    } else if (level >= wpi::WPI_LOG_ERROR) {
+      lvl = "ERROR: ";
+    } else if (level >= wpi::WPI_LOG_WARNING) {
+      lvl = "WARNING: ";
+    } else if (level >= wpi::WPI_LOG_INFO) {
+      lvl = "INFO: ";
+    } else if (level >= wpi::WPI_LOG_DEBUG) {
+      lvl = "DEBUG: ";
+    }
+    std::string filename = std::filesystem::path{file}.filename().string();
+    gLog.Append(fmt::format("{}{} ({}:{})\n", lvl, msg, filename, line));
+#ifndef NDEBUG
+    fmt::print(stderr, "{}{} ({}:{})\n", lvl, msg, filename, line);
+#endif
+  });
+
+  gLogger.set_min_level(wpi::WPI_LOG_DEBUG);
+  // Set the number of workers for the libuv threadpool.
+  uv_os_setenv("UV_THREADPOOL_SIZE", "6");
+
+  // Initialize window manager and add views.
+  auto& storage = glass::GetStorageRoot().GetChild("SysId");
+  gWindowManager = std::make_unique<glass::WindowManager>(storage);
+  gWindowManager->GlobalInit();
+
+  gLoggerWindow = gWindowManager->AddWindow(
+      "Logger", std::make_unique<sysid::Logger>(storage, gLogger));
+
+  gAnalyzerWindow = gWindowManager->AddWindow(
+      "Analyzer", std::make_unique<sysid::Analyzer>(storage, gLogger));
+
+  gProgramLogWindow = gWindowManager->AddWindow(
+      "Program Log", std::make_unique<glass::LogView>(&gLog));
+
+  // Set default positions and sizes for windows.
+
+  // Logger window position/size
+  gLoggerWindow->SetDefaultPos(sysid::kLoggerWindowPos.x,
+                               sysid::kLoggerWindowPos.y);
+  gLoggerWindow->SetDefaultSize(sysid::kLoggerWindowSize.x,
+                                sysid::kLoggerWindowSize.y);
+
+  // Analyzer window position/size
+  gAnalyzerWindow->SetDefaultPos(sysid::kAnalyzerWindowPos.x,
+                                 sysid::kAnalyzerWindowPos.y);
+  gAnalyzerWindow->SetDefaultSize(sysid::kAnalyzerWindowSize.x,
+                                  sysid::kAnalyzerWindowSize.y);
+
+  // Program log window position/size
+  gProgramLogWindow->SetDefaultPos(sysid::kProgramLogWindowPos.x,
+                                   sysid::kProgramLogWindowPos.y);
+  gProgramLogWindow->SetDefaultSize(sysid::kProgramLogWindowSize.x,
+                                    sysid::kProgramLogWindowSize.y);
+  gProgramLogWindow->DisableRenamePopup();
+
+  gJSONConverter = std::make_unique<sysid::JSONConverter>(gLogger);
+
+  // Configure save file.
+  gui::ConfigurePlatformSaveFile("sysid.ini");
+
+  // Add menu bar.
+  gui::AddLateExecute([] {
+    ImGui::BeginMainMenuBar();
+    gMainMenu.WorkspaceMenu();
+    gui::EmitViewMenu();
+
+    if (ImGui::BeginMenu("Widgets")) {
+      gWindowManager->DisplayMenu();
+      ImGui::EndMenu();
+    }
+
+    bool about = false;
+    if (ImGui::BeginMenu("Info")) {
+      if (ImGui::MenuItem("About")) {
+        about = true;
+      }
+      ImGui::EndMenu();
+    }
+
+    bool toCSV = false;
+    if (ImGui::BeginMenu("JSON Converters")) {
+      if (ImGui::MenuItem("JSON to CSV Converter")) {
+        toCSV = true;
+      }
+
+      ImGui::EndMenu();
+    }
+
+    if (ImGui::BeginMenu("Docs")) {
+      if (ImGui::MenuItem("Online documentation")) {
+        wpi::gui::OpenURL(
+            "https://docs.wpilib.org/en/stable/docs/software/pathplanning/"
+            "system-identification/");
+      }
+
+      ImGui::EndMenu();
+    }
+
+    ImGui::EndMainMenuBar();
+
+    if (toCSV) {
+      ImGui::OpenPopup("SysId JSON to CSV Converter");
+      toCSV = false;
+    }
+
+    if (ImGui::BeginPopupModal("SysId JSON to CSV Converter")) {
+      gJSONConverter->DisplayCSVConvert();
+      if (ImGui::Button("Close")) {
+        ImGui::CloseCurrentPopup();
+      }
+      ImGui::EndPopup();
+    }
+
+    if (about) {
+      ImGui::OpenPopup("About");
+      about = false;
+    }
+    if (ImGui::BeginPopupModal("About")) {
+      ImGui::Text("SysId: System Identification for Robot Mechanisms");
+      ImGui::Separator();
+      ImGui::Text("v%s", GetWPILibVersion());
+      ImGui::Separator();
+      ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
+      if (ImGui::Button("Close")) {
+        ImGui::CloseCurrentPopup();
+      }
+      ImGui::EndPopup();
+    }
+  });
+
+  gui::Initialize("System Identification", sysid::kAppWindowSize.x,
+                  sysid::kAppWindowSize.y);
+  gui::Main();
+
+  glass::DestroyContext();
+  gui::DestroyContext();
+}
+
+#endif
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/Main.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/Main.cpp
new file mode 100644
index 0000000..6fba848
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/Main.cpp
@@ -0,0 +1,29 @@
+// 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 <string_view>
+
+#ifndef RUNNING_SYSID_TESTS
+
+void Application(std::string_view saveDir);
+
+#ifdef _WIN32
+int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine,
+                      int nCmdShow) {
+  int argc = __argc;
+  char** argv = __argv;
+#else
+int main(int argc, char** argv) {
+#endif
+  std::string_view saveDir;
+  if (argc == 2) {
+    saveDir = argv[1];
+  }
+
+  Application(saveDir);
+
+  return 0;
+}
+
+#endif
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/Util.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/Util.cpp
new file mode 100644
index 0000000..a9acc53
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/Util.cpp
@@ -0,0 +1,80 @@
+// 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 "sysid/Util.h"
+
+#include <filesystem>
+#include <stdexcept>
+
+#include <imgui.h>
+#include <wpi/raw_ostream.h>
+
+void sysid::CreateTooltip(const char* text) {
+  ImGui::SameLine();
+  ImGui::TextDisabled(" (?)");
+
+  if (ImGui::IsItemHovered()) {
+    ImGui::BeginTooltip();
+    ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
+    ImGui::TextUnformatted(text);
+    ImGui::PopTextWrapPos();
+    ImGui::EndTooltip();
+  }
+}
+
+void sysid::CreateErrorPopup(bool& isError, std::string_view errorMessage) {
+  if (isError) {
+    ImGui::OpenPopup("Exception Caught!");
+  }
+
+  // Handle exceptions.
+  ImGui::SetNextWindowSize(ImVec2(480.f, 0.0f));
+  if (ImGui::BeginPopupModal("Exception Caught!")) {
+    ImGui::PushTextWrapPos(0.0f);
+    ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%s",
+                       errorMessage.data());
+    ImGui::PopTextWrapPos();
+    if (ImGui::Button("Close")) {
+      ImGui::CloseCurrentPopup();
+      isError = false;
+    }
+    ImGui::EndPopup();
+  }
+}
+
+std::string_view sysid::GetAbbreviation(std::string_view unit) {
+  if (unit == "Meters") {
+    return "m";
+  } else if (unit == "Feet") {
+    return "ft";
+  } else if (unit == "Inches") {
+    return "in";
+  } else if (unit == "Radians") {
+    return "rad";
+  } else if (unit == "Degrees") {
+    return "deg";
+  } else if (unit == "Rotations") {
+    return "rot";
+  } else {
+    throw std::runtime_error("Invalid Unit");
+  }
+}
+
+void sysid::SaveFile(std::string_view contents,
+                     const std::filesystem::path& path) {
+  // Create the path if it doesn't already exist.
+  std::filesystem::create_directories(path.root_directory());
+
+  // Open a fd_ostream to write to file.
+  std::error_code ec;
+  wpi::raw_fd_ostream ostream{path.string(), ec};
+
+  // Check error code.
+  if (ec) {
+    throw std::runtime_error("Cannot write to file: " + ec.message());
+  }
+
+  // Write contents.
+  ostream << contents;
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/AnalysisManager.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/AnalysisManager.cpp
new file mode 100644
index 0000000..b15cae8
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/AnalysisManager.cpp
@@ -0,0 +1,568 @@
+// 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 "sysid/analysis/AnalysisManager.h"
+
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <stdexcept>
+
+#include <fmt/format.h>
+#include <units/angle.h>
+#include <units/math.h>
+#include <wpi/MemoryBuffer.h>
+#include <wpi/StringExtras.h>
+#include <wpi/StringMap.h>
+
+#include "sysid/Util.h"
+#include "sysid/analysis/FilteringUtils.h"
+#include "sysid/analysis/JSONConverter.h"
+#include "sysid/analysis/TrackWidthAnalysis.h"
+
+using namespace sysid;
+
+/**
+ * Converts a raw data vector into a PreparedData vector with only the
+ * timestamp, voltage, position, and velocity fields filled out.
+ *
+ * @tparam S The size of the arrays in the raw data vector
+ * @tparam Timestamp The index of the Timestamp data in the raw data vector
+ * arrays
+ * @tparam Voltage The index of the Voltage data in the raw data vector arrays
+ * @tparam Position The index of the Position data in the raw data vector arrays
+ * @tparam Velocity The index of the Velocity data in the raw data vector arrays
+ *
+ * @param data A raw data vector
+ *
+ * @return A PreparedData vector
+ */
+template <size_t S, size_t Timestamp, size_t Voltage, size_t Position,
+          size_t Velocity>
+static std::vector<PreparedData> ConvertToPrepared(
+    const std::vector<std::array<double, S>>& data) {
+  std::vector<PreparedData> prepared;
+  for (int i = 0; i < static_cast<int>(data.size()) - 1; ++i) {
+    const auto& pt1 = data[i];
+    const auto& pt2 = data[i + 1];
+    prepared.emplace_back(PreparedData{
+        units::second_t{pt1[Timestamp]}, pt1[Voltage], pt1[Position],
+        pt1[Velocity], units::second_t{pt2[Timestamp] - pt1[Timestamp]}});
+  }
+  return prepared;
+}
+
+/**
+ * To preserve a raw copy of the data, this method saves a raw version
+ * in the dataset StringMap where the key of the raw data starts with "raw-"
+ * before the name of the original data.
+ *
+ * @tparam S The size of the array data that's being used
+ *
+ * @param dataset A reference to the dataset being used
+ */
+template <size_t S>
+static void CopyRawData(
+    wpi::StringMap<std::vector<std::array<double, S>>>* dataset) {
+  auto& data = *dataset;
+  // Loads the Raw Data
+  for (auto& it : data) {
+    auto key = it.first();
+    auto& dataset = it.getValue();
+
+    if (!wpi::contains(key, "raw")) {
+      data[fmt::format("raw-{}", key)] = dataset;
+      data[fmt::format("original-raw-{}", key)] = dataset;
+    }
+  }
+}
+
+/**
+ * Assigns the combines the various datasets into a single one for analysis.
+ *
+ * @param slowForward The slow forward dataset
+ * @param slowBackward The slow backward dataset
+ * @param fastForward The fast forward dataset
+ * @param fastBackward The fast backward dataset
+ */
+static Storage CombineDatasets(const std::vector<PreparedData>& slowForward,
+                               const std::vector<PreparedData>& slowBackward,
+                               const std::vector<PreparedData>& fastForward,
+                               const std::vector<PreparedData>& fastBackward) {
+  return Storage{slowForward, slowBackward, fastForward, fastBackward};
+}
+
+void AnalysisManager::PrepareGeneralData() {
+  using Data = std::array<double, 4>;
+  wpi::StringMap<std::vector<Data>> data;
+  wpi::StringMap<std::vector<PreparedData>> preparedData;
+
+  // Store the raw data columns.
+  static constexpr size_t kTimeCol = 0;
+  static constexpr size_t kVoltageCol = 1;
+  static constexpr size_t kPosCol = 2;
+  static constexpr size_t kVelCol = 3;
+
+  WPI_INFO(m_logger, "{}", "Reading JSON data.");
+  // Get the major components from the JSON and store them inside a StringMap.
+  for (auto&& key : AnalysisManager::kJsonDataKeys) {
+    data[key] = m_json.at(key).get<std::vector<Data>>();
+  }
+
+  WPI_INFO(m_logger, "{}", "Preprocessing raw data.");
+  // Ensure that voltage and velocity have the same sign. Also multiply
+  // positions and velocities by the factor.
+  for (auto it = data.begin(); it != data.end(); ++it) {
+    for (auto&& pt : it->second) {
+      pt[kVoltageCol] = std::copysign(pt[kVoltageCol], pt[kVelCol]);
+      pt[kPosCol] *= m_factor;
+      pt[kVelCol] *= m_factor;
+    }
+  }
+
+  WPI_INFO(m_logger, "{}", "Copying raw data.");
+  CopyRawData(&data);
+
+  WPI_INFO(m_logger, "{}", "Converting raw data to PreparedData struct.");
+  // Convert data to PreparedData structs
+  for (auto& it : data) {
+    auto key = it.first();
+    preparedData[key] =
+        ConvertToPrepared<4, kTimeCol, kVoltageCol, kPosCol, kVelCol>(
+            data[key]);
+  }
+
+  // Store the original datasets
+  m_originalDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kCombined)] =
+      CombineDatasets(preparedData["original-raw-slow-forward"],
+                      preparedData["original-raw-slow-backward"],
+                      preparedData["original-raw-fast-forward"],
+                      preparedData["original-raw-fast-backward"]);
+
+  WPI_INFO(m_logger, "{}", "Initial trimming and filtering.");
+  sysid::InitialTrimAndFilter(&preparedData, &m_settings, m_positionDelays,
+                              m_velocityDelays, m_minStepTime, m_maxStepTime,
+                              m_unit);
+
+  WPI_INFO(m_logger, "{}", "Acceleration filtering.");
+  sysid::AccelFilter(&preparedData);
+
+  WPI_INFO(m_logger, "{}", "Storing datasets.");
+  // Store the raw datasets
+  m_rawDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kCombined)] =
+      CombineDatasets(
+          preparedData["raw-slow-forward"], preparedData["raw-slow-backward"],
+          preparedData["raw-fast-forward"], preparedData["raw-fast-backward"]);
+
+  // Store the filtered datasets
+  m_filteredDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kCombined)] =
+      CombineDatasets(
+          preparedData["slow-forward"], preparedData["slow-backward"],
+          preparedData["fast-forward"], preparedData["fast-backward"]);
+
+  m_startTimes = {preparedData["raw-slow-forward"][0].timestamp,
+                  preparedData["raw-slow-backward"][0].timestamp,
+                  preparedData["raw-fast-forward"][0].timestamp,
+                  preparedData["raw-fast-backward"][0].timestamp};
+}
+
+void AnalysisManager::PrepareAngularDrivetrainData() {
+  using Data = std::array<double, 9>;
+  wpi::StringMap<std::vector<Data>> data;
+  wpi::StringMap<std::vector<PreparedData>> preparedData;
+
+  // Store the relevant raw data columns.
+  static constexpr size_t kTimeCol = 0;
+  static constexpr size_t kLVoltageCol = 1;
+  static constexpr size_t kRVoltageCol = 2;
+  static constexpr size_t kLPosCol = 3;
+  static constexpr size_t kRPosCol = 4;
+  static constexpr size_t kLVelCol = 5;
+  static constexpr size_t kRVelCol = 6;
+  static constexpr size_t kAngleCol = 7;
+  static constexpr size_t kAngularRateCol = 8;
+
+  WPI_INFO(m_logger, "{}", "Reading JSON data.");
+  // Get the major components from the JSON and store them inside a StringMap.
+  for (auto&& key : AnalysisManager::kJsonDataKeys) {
+    data[key] = m_json.at(key).get<std::vector<Data>>();
+  }
+
+  WPI_INFO(m_logger, "{}", "Preprocessing raw data.");
+  // Ensure that voltage and velocity have the same sign. Also multiply
+  // positions and velocities by the factor.
+  for (auto it = data.begin(); it != data.end(); ++it) {
+    for (auto&& pt : it->second) {
+      pt[kLPosCol] *= m_factor;
+      pt[kRPosCol] *= m_factor;
+      pt[kLVelCol] *= m_factor;
+      pt[kRVelCol] *= m_factor;
+
+      // Stores the average voltages in the left voltage column.
+      // This aggregates the left and right voltages into a single voltage
+      // column for the ConvertToPrepared() method. std::copysign() ensures the
+      // polarity of the voltage matches the direction the robot turns.
+      pt[kLVoltageCol] = std::copysign(
+          (std::abs(pt[kLVoltageCol]) + std::abs(pt[kRVoltageCol])) / 2,
+          pt[kAngularRateCol]);
+
+      // ω = (v_r - v_l) / trackwidth
+      // v = ωr => v = ω * trackwidth / 2
+      // (v_r - v_l) / trackwidth * (trackwidth / 2) = (v_r - v_l) / 2
+      // However, since we know this is an angular test, the left and right
+      // wheel velocities will have opposite signs, allowing us to add their
+      // absolute values and get the same result (in terms of magnitude).
+      // std::copysign() is used to make sure the direction of the wheel
+      // velocities matches the direction the robot turns.
+      pt[kAngularRateCol] =
+          std::copysign((std::abs(pt[kRVelCol]) + std::abs(pt[kLVelCol])) / 2,
+                        pt[kAngularRateCol]);
+    }
+  }
+
+  WPI_INFO(m_logger, "{}", "Calculating trackwidth");
+  // Aggregating all the deltas from all the tests
+  double leftDelta = 0.0;
+  double rightDelta = 0.0;
+  double angleDelta = 0.0;
+  for (const auto& it : data) {
+    auto key = it.first();
+    auto& trackWidthData = data[key];
+    leftDelta += std::abs(trackWidthData.back()[kLPosCol] -
+                          trackWidthData.front()[kLPosCol]);
+    rightDelta += std::abs(trackWidthData.back()[kRPosCol] -
+                           trackWidthData.front()[kRPosCol]);
+    angleDelta += std::abs(trackWidthData.back()[kAngleCol] -
+                           trackWidthData.front()[kAngleCol]);
+  }
+  m_trackWidth = sysid::CalculateTrackWidth(leftDelta, rightDelta,
+                                            units::radian_t{angleDelta});
+
+  WPI_INFO(m_logger, "{}", "Copying raw data.");
+  CopyRawData(&data);
+
+  WPI_INFO(m_logger, "{}", "Converting to PreparedData struct.");
+  // Convert raw data to prepared data
+  for (const auto& it : data) {
+    auto key = it.first();
+    preparedData[key] = ConvertToPrepared<9, kTimeCol, kLVoltageCol, kAngleCol,
+                                          kAngularRateCol>(data[key]);
+  }
+
+  // Create the distinct datasets and store them
+  m_originalDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kCombined)] =
+      CombineDatasets(preparedData["original-raw-slow-forward"],
+                      preparedData["original-raw-slow-backward"],
+                      preparedData["original-raw-fast-forward"],
+                      preparedData["original-raw-fast-backward"]);
+
+  WPI_INFO(m_logger, "{}", "Applying trimming and filtering.");
+  sysid::InitialTrimAndFilter(&preparedData, &m_settings, m_positionDelays,
+                              m_velocityDelays, m_minStepTime, m_maxStepTime);
+
+  WPI_INFO(m_logger, "{}", "Acceleration filtering.");
+  sysid::AccelFilter(&preparedData);
+
+  WPI_INFO(m_logger, "{}", "Storing datasets.");
+  // Create the distinct datasets and store them
+  m_rawDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kCombined)] =
+      CombineDatasets(
+          preparedData["raw-slow-forward"], preparedData["raw-slow-backward"],
+          preparedData["raw-fast-forward"], preparedData["raw-fast-backward"]);
+  m_filteredDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kCombined)] =
+      CombineDatasets(
+          preparedData["slow-forward"], preparedData["slow-backward"],
+          preparedData["fast-forward"], preparedData["fast-backward"]);
+
+  m_startTimes = {preparedData["slow-forward"][0].timestamp,
+                  preparedData["slow-backward"][0].timestamp,
+                  preparedData["fast-forward"][0].timestamp,
+                  preparedData["fast-backward"][0].timestamp};
+}
+
+void AnalysisManager::PrepareLinearDrivetrainData() {
+  using Data = std::array<double, 9>;
+  wpi::StringMap<std::vector<Data>> data;
+  wpi::StringMap<std::vector<PreparedData>> preparedData;
+
+  // Store the relevant raw data columns.
+  static constexpr size_t kTimeCol = 0;
+  static constexpr size_t kLVoltageCol = 1;
+  static constexpr size_t kRVoltageCol = 2;
+  static constexpr size_t kLPosCol = 3;
+  static constexpr size_t kRPosCol = 4;
+  static constexpr size_t kLVelCol = 5;
+  static constexpr size_t kRVelCol = 6;
+
+  // Get the major components from the JSON and store them inside a StringMap.
+  WPI_INFO(m_logger, "{}", "Reading JSON data.");
+  for (auto&& key : AnalysisManager::kJsonDataKeys) {
+    data[key] = m_json.at(key).get<std::vector<Data>>();
+  }
+
+  // Ensure that voltage and velocity have the same sign. Also multiply
+  // positions and velocities by the factor.
+  WPI_INFO(m_logger, "{}", "Preprocessing raw data.");
+  for (auto it = data.begin(); it != data.end(); ++it) {
+    for (auto&& pt : it->second) {
+      pt[kLVoltageCol] = std::copysign(pt[kLVoltageCol], pt[kLVelCol]);
+      pt[kRVoltageCol] = std::copysign(pt[kRVoltageCol], pt[kRVelCol]);
+      pt[kLPosCol] *= m_factor;
+      pt[kRPosCol] *= m_factor;
+      pt[kLVelCol] *= m_factor;
+      pt[kRVelCol] *= m_factor;
+    }
+  }
+
+  WPI_INFO(m_logger, "{}", "Copying raw data.");
+  CopyRawData(&data);
+
+  // Convert data to PreparedData
+  WPI_INFO(m_logger, "{}", "Converting to PreparedData struct.");
+  for (auto& it : data) {
+    auto key = it.first();
+
+    preparedData[fmt::format("left-{}", key)] =
+        ConvertToPrepared<9, kTimeCol, kLVoltageCol, kLPosCol, kLVelCol>(
+            data[key]);
+    preparedData[fmt::format("right-{}", key)] =
+        ConvertToPrepared<9, kTimeCol, kRVoltageCol, kRPosCol, kRVelCol>(
+            data[key]);
+  }
+
+  // Create the distinct raw datasets and store them
+  auto originalSlowForward = AnalysisManager::DataConcat(
+      preparedData["left-original-raw-slow-forward"],
+      preparedData["right-original-raw-slow-forward"]);
+  auto originalSlowBackward = AnalysisManager::DataConcat(
+      preparedData["left-original-raw-slow-backward"],
+      preparedData["right-original-raw-slow-backward"]);
+  auto originalFastForward = AnalysisManager::DataConcat(
+      preparedData["left-original-raw-fast-forward"],
+      preparedData["right-original-raw-fast-forward"]);
+  auto originalFastBackward = AnalysisManager::DataConcat(
+      preparedData["left-original-raw-fast-backward"],
+      preparedData["right-original-raw-fast-backward"]);
+  m_originalDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kCombined)] =
+      CombineDatasets(originalSlowForward, originalSlowBackward,
+                      originalFastForward, originalFastBackward);
+  m_originalDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kLeft)] =
+      CombineDatasets(preparedData["left-original-raw-slow-forward"],
+                      preparedData["left-original-raw-slow-backward"],
+                      preparedData["left-original-raw-fast-forward"],
+                      preparedData["left-original-raw-fast-backward"]);
+  m_originalDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kRight)] =
+      CombineDatasets(preparedData["right-original-raw-slow-forward"],
+                      preparedData["right-original-raw-slow-backward"],
+                      preparedData["right-original-raw-fast-forward"],
+                      preparedData["right-original-raw-fast-backward"]);
+
+  WPI_INFO(m_logger, "{}", "Applying trimming and filtering.");
+  sysid::InitialTrimAndFilter(&preparedData, &m_settings, m_positionDelays,
+                              m_velocityDelays, m_minStepTime, m_maxStepTime);
+
+  auto slowForward = AnalysisManager::DataConcat(
+      preparedData["left-slow-forward"], preparedData["right-slow-forward"]);
+  auto slowBackward = AnalysisManager::DataConcat(
+      preparedData["left-slow-backward"], preparedData["right-slow-backward"]);
+  auto fastForward = AnalysisManager::DataConcat(
+      preparedData["left-fast-forward"], preparedData["right-fast-forward"]);
+  auto fastBackward = AnalysisManager::DataConcat(
+      preparedData["left-fast-backward"], preparedData["right-fast-backward"]);
+
+  WPI_INFO(m_logger, "{}", "Acceleration filtering.");
+  sysid::AccelFilter(&preparedData);
+
+  WPI_INFO(m_logger, "{}", "Storing datasets.");
+
+  // Create the distinct raw datasets and store them
+  auto rawSlowForward =
+      AnalysisManager::DataConcat(preparedData["left-raw-slow-forward"],
+                                  preparedData["right-raw-slow-forward"]);
+  auto rawSlowBackward =
+      AnalysisManager::DataConcat(preparedData["left-raw-slow-backward"],
+                                  preparedData["right-raw-slow-backward"]);
+  auto rawFastForward =
+      AnalysisManager::DataConcat(preparedData["left-raw-fast-forward"],
+                                  preparedData["right-raw-fast-forward"]);
+  auto rawFastBackward =
+      AnalysisManager::DataConcat(preparedData["left-raw-fast-backward"],
+                                  preparedData["right-raw-fast-backward"]);
+
+  m_rawDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kCombined)] =
+      CombineDatasets(rawSlowForward, rawSlowBackward, rawFastForward,
+                      rawFastBackward);
+  m_rawDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kLeft)] =
+      CombineDatasets(preparedData["left-raw-slow-forward"],
+                      preparedData["left-raw-slow-backward"],
+                      preparedData["left-raw-fast-forward"],
+                      preparedData["left-raw-fast-backward"]);
+  m_rawDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kRight)] =
+      CombineDatasets(preparedData["right-raw-slow-forward"],
+                      preparedData["right-raw-slow-backward"],
+                      preparedData["right-raw-fast-forward"],
+                      preparedData["right-raw-fast-backward"]);
+
+  // Create the distinct filtered datasets and store them
+  m_filteredDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kCombined)] =
+      CombineDatasets(slowForward, slowBackward, fastForward, fastBackward);
+  m_filteredDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kLeft)] =
+      CombineDatasets(preparedData["left-slow-forward"],
+                      preparedData["left-slow-backward"],
+                      preparedData["left-fast-forward"],
+                      preparedData["left-fast-backward"]);
+  m_filteredDataset[static_cast<int>(
+      AnalysisManager::Settings::DrivetrainDataset::kRight)] =
+      CombineDatasets(preparedData["right-slow-forward"],
+                      preparedData["right-slow-backward"],
+                      preparedData["right-fast-forward"],
+                      preparedData["right-fast-backward"]);
+
+  m_startTimes = {
+      rawSlowForward.front().timestamp, rawSlowBackward.front().timestamp,
+      rawFastForward.front().timestamp, rawFastBackward.front().timestamp};
+}
+
+AnalysisManager::AnalysisManager(Settings& settings, wpi::Logger& logger)
+    : m_logger{logger},
+      m_settings{settings},
+      m_type{analysis::kSimple},
+      m_unit{"Meters"},
+      m_factor{1} {}
+
+AnalysisManager::AnalysisManager(std::string_view path, Settings& settings,
+                                 wpi::Logger& logger)
+    : m_logger{logger}, m_settings{settings} {
+  {
+    // Read JSON from the specified path
+    std::error_code ec;
+    std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+        wpi::MemoryBuffer::GetFile(path, ec);
+    if (fileBuffer == nullptr || ec) {
+      throw FileReadingError(path);
+    }
+
+    m_json = wpi::json::parse(fileBuffer->begin(), fileBuffer->end());
+
+    WPI_INFO(m_logger, "Read {}", path);
+  }
+
+  // Check that we have a sysid JSON
+  if (m_json.find("sysid") == m_json.end()) {
+    // If it's not a sysid JSON, try converting it from frc-char format
+    std::string newPath = sysid::ConvertJSON(path, logger);
+
+    // Read JSON from the specified path
+    std::error_code ec;
+    std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+        wpi::MemoryBuffer::GetFile(path, ec);
+    if (fileBuffer == nullptr || ec) {
+      throw FileReadingError(newPath);
+    }
+
+    m_json = wpi::json::parse(fileBuffer->begin(), fileBuffer->end());
+
+    WPI_INFO(m_logger, "Read {}", newPath);
+  }
+
+  WPI_INFO(m_logger, "Parsing initial data of {}", path);
+  // Get the analysis type from the JSON.
+  m_type = sysid::analysis::FromName(m_json.at("test").get<std::string>());
+
+  // Get the rotation -> output units factor from the JSON.
+  m_unit = m_json.at("units").get<std::string>();
+  m_factor = m_json.at("unitsPerRotation").get<double>();
+  WPI_DEBUG(m_logger, "Parsing units per rotation as {} {} per rotation",
+            m_factor, m_unit);
+
+  // Reset settings for Dynamic Test Limits
+  m_settings.stepTestDuration = units::second_t{0.0};
+  m_settings.motionThreshold = std::numeric_limits<double>::infinity();
+}
+
+void AnalysisManager::PrepareData() {
+  WPI_INFO(m_logger, "Preparing {} data", m_type.name);
+  if (m_type == analysis::kDrivetrain) {
+    PrepareLinearDrivetrainData();
+  } else if (m_type == analysis::kDrivetrainAngular) {
+    PrepareAngularDrivetrainData();
+  } else {
+    PrepareGeneralData();
+  }
+  WPI_INFO(m_logger, "{}", "Finished Preparing Data");
+}
+
+AnalysisManager::FeedforwardGains AnalysisManager::CalculateFeedforward() {
+  if (m_filteredDataset.empty()) {
+    throw sysid::InvalidDataError(
+        "There is no data to perform gain calculation on.");
+  }
+
+  WPI_INFO(m_logger, "{}", "Calculating Gains");
+  // Calculate feedforward gains from the data.
+  const auto& ff = sysid::CalculateFeedforwardGains(GetFilteredData(), m_type);
+  FeedforwardGains ffGains = {ff, m_trackWidth};
+
+  const auto& Ks = std::get<0>(ff)[0];
+  const auto& Kv = std::get<0>(ff)[1];
+  const auto& Ka = std::get<0>(ff)[2];
+
+  if (Ka <= 0 || Kv < 0) {
+    throw InvalidDataError(
+        fmt::format("The calculated feedforward gains of kS: {}, Kv: {}, Ka: "
+                    "{} are erroneous. Your Ka should be > 0 while your Kv and "
+                    "Ks constants should both >= 0. Try adjusting the "
+                    "filtering and trimming settings or collect better data.",
+                    Ks, Kv, Ka));
+  }
+
+  return ffGains;
+}
+
+sysid::FeedbackGains AnalysisManager::CalculateFeedback(
+    std::vector<double> ff) {
+  const auto& Kv = ff[1];
+  const auto& Ka = ff[2];
+  FeedbackGains fb;
+  if (m_settings.type == FeedbackControllerLoopType::kPosition) {
+    fb = sysid::CalculatePositionFeedbackGains(
+        m_settings.preset, m_settings.lqr, Kv, Ka,
+        m_settings.convertGainsToEncTicks
+            ? m_settings.gearing * m_settings.cpr * m_factor
+            : 1);
+  } else {
+    fb = sysid::CalculateVelocityFeedbackGains(
+        m_settings.preset, m_settings.lqr, Kv, Ka,
+        m_settings.convertGainsToEncTicks
+            ? m_settings.gearing * m_settings.cpr * m_factor
+            : 1);
+  }
+
+  return fb;
+}
+
+void AnalysisManager::OverrideUnits(std::string_view unit,
+                                    double unitsPerRotation) {
+  m_unit = unit;
+  m_factor = unitsPerRotation;
+}
+
+void AnalysisManager::ResetUnitsFromJSON() {
+  m_unit = m_json.at("units").get<std::string>();
+  m_factor = m_json.at("unitsPerRotation").get<double>();
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/AnalysisType.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/AnalysisType.cpp
new file mode 100644
index 0000000..18b461f
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/AnalysisType.cpp
@@ -0,0 +1,23 @@
+// 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 "sysid/analysis/AnalysisType.h"
+
+using namespace sysid;
+
+AnalysisType sysid::analysis::FromName(std::string_view name) {
+  if (name == "Drivetrain") {
+    return sysid::analysis::kDrivetrain;
+  }
+  if (name == "Drivetrain (Angular)") {
+    return sysid::analysis::kDrivetrainAngular;
+  }
+  if (name == "Elevator") {
+    return sysid::analysis::kElevator;
+  }
+  if (name == "Arm") {
+    return sysid::analysis::kArm;
+  }
+  return sysid::analysis::kSimple;
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/ArmSim.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/ArmSim.cpp
new file mode 100644
index 0000000..a72f505
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/ArmSim.cpp
@@ -0,0 +1,64 @@
+// 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 "sysid/analysis/ArmSim.h"
+
+#include <cmath>
+
+#include <frc/StateSpaceUtil.h>
+#include <frc/system/NumericalIntegration.h>
+#include <wpi/MathExtras.h>
+
+using namespace sysid;
+
+ArmSim::ArmSim(double Ks, double Kv, double Ka, double Kg, double offset,
+               double initialPosition, double initialVelocity)
+    // u = Ks sgn(x) + Kv x + Ka a + Kg cos(theta)
+    // Ka a = u - Ks sgn(x) - Kv x - Kg cos(theta)
+    // a = 1/Ka u - Ks/Ka sgn(x) - Kv/Ka x - Kg/Ka cos(theta)
+    // a = -Kv/Ka x + 1/Ka u - Ks/Ka sgn(x) - Kg/Ka cos(theta)
+    // a = Ax + Bu + c sgn(x) + d cos(theta)
+    : m_A{-Kv / Ka},
+      m_B{1.0 / Ka},
+      m_c{-Ks / Ka},
+      m_d{-Kg / Ka},
+      m_offset{offset} {
+  Reset(initialPosition, initialVelocity);
+}
+
+void ArmSim::Update(units::volt_t voltage, units::second_t dt) {
+  // Returns arm acceleration under gravity
+  auto f = [=, this](
+               const Eigen::Vector<double, 2>& x,
+               const Eigen::Vector<double, 1>& u) -> Eigen::Vector<double, 2> {
+    return Eigen::Vector<double, 2>{
+        x(1), (m_A * x.block<1, 1>(1, 0) + m_B * u + m_c * wpi::sgn(x(1)) +
+               m_d * std::cos(x(0) + m_offset))(0)};
+  };
+
+  // Max error is large because an accurate sim isn't as important as the sim
+  // finishing in a timely manner. Otherwise, the timestep can become absurdly
+  // small for ill-conditioned data (e.g., high velocities with sharp spikes in
+  // acceleration).
+  Eigen::Vector<double, 1> u{voltage.value()};
+  m_x = frc::RKDP(f, m_x, u, dt, 0.25);
+}
+
+double ArmSim::GetPosition() const {
+  return m_x(0);
+}
+
+double ArmSim::GetVelocity() const {
+  return m_x(1);
+}
+
+double ArmSim::GetAcceleration(units::volt_t voltage) const {
+  Eigen::Vector<double, 1> u{voltage.value()};
+  return (m_A * m_x.block<1, 1>(1, 0) + m_B * u +
+          m_c * wpi::sgn(GetVelocity()) + m_d * std::cos(m_x(0) + m_offset))(0);
+}
+
+void ArmSim::Reset(double position, double velocity) {
+  m_x = Eigen::Vector<double, 2>{position, velocity};
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/ElevatorSim.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/ElevatorSim.cpp
new file mode 100644
index 0000000..5cfcabe
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/ElevatorSim.cpp
@@ -0,0 +1,50 @@
+// 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 "sysid/analysis/ElevatorSim.h"
+
+#include <frc/StateSpaceUtil.h>
+#include <frc/system/Discretization.h>
+#include <wpi/MathExtras.h>
+
+using namespace sysid;
+
+ElevatorSim::ElevatorSim(double Ks, double Kv, double Ka, double Kg,
+                         double initialPosition, double initialVelocity)
+    // dx/dt = Ax + Bu + c sgn(x) + d
+    : m_A{{0.0, 1.0}, {0.0, -Kv / Ka}},
+      m_B{0.0, 1.0 / Ka},
+      m_c{0.0, -Ks / Ka},
+      m_d{0.0, -Kg / Ka} {
+  Reset(initialPosition, initialVelocity);
+}
+
+void ElevatorSim::Update(units::volt_t voltage, units::second_t dt) {
+  Eigen::Vector<double, 1> u{voltage.value()};
+
+  // Given dx/dt = Ax + Bu + c sgn(x) + d,
+  // x_k+1 = e^(AT) x_k + A^-1 (e^(AT) - 1) (Bu + c sgn(x) + d)
+  Eigen::Matrix<double, 2, 2> Ad;
+  Eigen::Matrix<double, 2, 1> Bd;
+  frc::DiscretizeAB<2, 1>(m_A, m_B, dt, &Ad, &Bd);
+  m_x = Ad * m_x + Bd * u +
+        Bd * m_B.householderQr().solve(m_c * wpi::sgn(GetVelocity()) + m_d);
+}
+
+double ElevatorSim::GetPosition() const {
+  return m_x(0);
+}
+
+double ElevatorSim::GetVelocity() const {
+  return m_x(1);
+}
+
+double ElevatorSim::GetAcceleration(units::volt_t voltage) const {
+  Eigen::Vector<double, 1> u{voltage.value()};
+  return (m_A * m_x + m_B * u + m_c * wpi::sgn(GetVelocity()) + m_d)(1);
+}
+
+void ElevatorSim::Reset(double position, double velocity) {
+  m_x = Eigen::Vector<double, 2>{position, velocity};
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/FeedbackAnalysis.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/FeedbackAnalysis.cpp
new file mode 100644
index 0000000..0691aaf
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/FeedbackAnalysis.cpp
@@ -0,0 +1,78 @@
+// 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 "sysid/analysis/FeedbackAnalysis.h"
+
+#include <frc/controller/LinearQuadraticRegulator.h>
+#include <frc/system/LinearSystem.h>
+#include <frc/system/plant/LinearSystemId.h>
+#include <units/acceleration.h>
+#include <units/velocity.h>
+#include <units/voltage.h>
+
+#include "sysid/analysis/FeedbackControllerPreset.h"
+
+using namespace sysid;
+
+using Kv_t = decltype(1_V / 1_mps);
+using Ka_t = decltype(1_V / 1_mps_sq);
+
+FeedbackGains sysid::CalculatePositionFeedbackGains(
+    const FeedbackControllerPreset& preset, const LQRParameters& params,
+    double Kv, double Ka, double encFactor) {
+  // If acceleration requires no effort, velocity becomes an input for position
+  // control. We choose an appropriate model in this case to avoid numerical
+  // instabilities in the LQR.
+  if (Ka > 1E-7) {
+    // Create a position system from our feedforward gains.
+    auto system = frc::LinearSystemId::IdentifyPositionSystem<units::meter>(
+        Kv_t(Kv), Ka_t(Ka));
+    // Create an LQR with 2 states to control -- position and velocity.
+    frc::LinearQuadraticRegulator<2, 1> controller{
+        system, {params.qp, params.qv}, {params.r}, preset.period};
+    // Compensate for any latency from sensor measurements, filtering, etc.
+    controller.LatencyCompensate(system, preset.period, 0.0_s);
+
+    return {controller.K(0, 0) * preset.outputConversionFactor / encFactor,
+            controller.K(0, 1) * preset.outputConversionFactor /
+                (encFactor * (preset.normalized ? 1 : preset.period.value()))};
+  }
+
+  // This is our special model to avoid instabilities in the LQR.
+  auto system = frc::LinearSystem<1, 1, 1>(
+      Eigen::Matrix<double, 1, 1>{0.0}, Eigen::Matrix<double, 1, 1>{1.0},
+      Eigen::Matrix<double, 1, 1>{1.0}, Eigen::Matrix<double, 1, 1>{0.0});
+  // Create an LQR with one state -- position.
+  frc::LinearQuadraticRegulator<1, 1> controller{
+      system, {params.qp}, {params.r}, preset.period};
+  // Compensate for any latency from sensor measurements, filtering, etc.
+  controller.LatencyCompensate(system, preset.period, 0.0_s);
+
+  return {Kv * controller.K(0, 0) * preset.outputConversionFactor / encFactor,
+          0.0};
+}
+
+FeedbackGains sysid::CalculateVelocityFeedbackGains(
+    const FeedbackControllerPreset& preset, const LQRParameters& params,
+    double Kv, double Ka, double encFactor) {
+  // If acceleration for velocity control requires no effort, the feedback
+  // control gains approach zero. We special-case it here because numerical
+  // instabilities arise in LQR otherwise.
+  if (Ka < 1E-7) {
+    return {0.0, 0.0};
+  }
+
+  // Create a velocity system from our feedforward gains.
+  auto system = frc::LinearSystemId::IdentifyVelocitySystem<units::meter>(
+      Kv_t(Kv), Ka_t(Ka));
+  // Create an LQR controller with 1 state -- velocity.
+  frc::LinearQuadraticRegulator<1, 1> controller{
+      system, {params.qv}, {params.r}, preset.period};
+  // Compensate for any latency from sensor measurements, filtering, etc.
+  controller.LatencyCompensate(system, preset.period, preset.measurementDelay);
+
+  return {controller.K(0, 0) * preset.outputConversionFactor /
+              (preset.outputVelocityTimeFactor * encFactor),
+          0.0};
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/FeedforwardAnalysis.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/FeedforwardAnalysis.cpp
new file mode 100644
index 0000000..b7a9fce
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/FeedforwardAnalysis.cpp
@@ -0,0 +1,120 @@
+// 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 "sysid/analysis/FeedforwardAnalysis.h"
+
+#include <cmath>
+
+#include <units/math.h>
+#include <units/time.h>
+
+#include "sysid/analysis/AnalysisManager.h"
+#include "sysid/analysis/FilteringUtils.h"
+#include "sysid/analysis/OLS.h"
+
+using namespace sysid;
+
+/**
+ * Populates OLS data for (xₖ₊₁ − xₖ)/τ = αxₖ + βuₖ + γ sgn(xₖ).
+ *
+ * @param d List of characterization data.
+ * @param type Type of system being identified.
+ * @param X Vector representation of X in y = Xβ.
+ * @param y Vector representation of y in y = Xβ.
+ */
+static void PopulateOLSData(const std::vector<PreparedData>& d,
+                            const AnalysisType& type,
+                            Eigen::Block<Eigen::MatrixXd> X,
+                            Eigen::VectorBlock<Eigen::VectorXd> y) {
+  for (size_t sample = 0; sample < d.size(); ++sample) {
+    const auto& pt = d[sample];
+
+    // Add the velocity term (for α)
+    X(sample, 0) = pt.velocity;
+
+    // Add the voltage term (for β)
+    X(sample, 1) = pt.voltage;
+
+    // Add the intercept term (for γ)
+    X(sample, 2) = std::copysign(1, pt.velocity);
+
+    // Add test-specific variables
+    if (type == analysis::kElevator) {
+      // Add the gravity term (for Kg)
+      X(sample, 3) = 1.0;
+    } else if (type == analysis::kArm) {
+      // Add the cosine and sine terms (for Kg)
+      X(sample, 3) = pt.cos;
+      X(sample, 4) = pt.sin;
+    }
+
+    // Add the dependent variable (acceleration)
+    y(sample) = pt.acceleration;
+  }
+}
+
+std::tuple<std::vector<double>, double, double>
+sysid::CalculateFeedforwardGains(const Storage& data,
+                                 const AnalysisType& type) {
+  // Iterate through the data and add it to our raw vector.
+  const auto& [slowForward, slowBackward, fastForward, fastBackward] = data;
+
+  const auto size = slowForward.size() + slowBackward.size() +
+                    fastForward.size() + fastBackward.size();
+
+  // Create a raw vector of doubles with our data in it.
+  Eigen::MatrixXd X{size, type.independentVariables};
+  Eigen::VectorXd y{size};
+
+  int rowOffset = 0;
+  PopulateOLSData(slowForward, type,
+                  X.block(rowOffset, 0, slowForward.size(), X.cols()),
+                  y.segment(rowOffset, slowForward.size()));
+
+  rowOffset += slowForward.size();
+  PopulateOLSData(slowBackward, type,
+                  X.block(rowOffset, 0, slowBackward.size(), X.cols()),
+                  y.segment(rowOffset, slowBackward.size()));
+
+  rowOffset += slowBackward.size();
+  PopulateOLSData(fastForward, type,
+                  X.block(rowOffset, 0, fastForward.size(), X.cols()),
+                  y.segment(rowOffset, fastForward.size()));
+
+  rowOffset += fastForward.size();
+  PopulateOLSData(fastBackward, type,
+                  X.block(rowOffset, 0, fastBackward.size(), X.cols()),
+                  y.segment(rowOffset, fastBackward.size()));
+
+  // Perform OLS with accel = alpha*vel + beta*voltage + gamma*signum(vel)
+  // OLS performs best with the noisiest variable as the dependent var,
+  // so we regress accel in terms of the other variables.
+  auto ols = sysid::OLS(X, y);
+  double alpha = std::get<0>(ols)[0];  // -Kv/Ka
+  double beta = std::get<0>(ols)[1];   // 1/Ka
+  double gamma = std::get<0>(ols)[2];  // -Ks/Ka
+
+  // Initialize gains list with Ks, Kv, and Ka
+  std::vector<double> gains{-gamma / beta, -alpha / beta, 1 / beta};
+
+  if (type == analysis::kElevator) {
+    // Add Kg to gains list
+    double delta = std::get<0>(ols)[3];  // -Kg/Ka
+    gains.emplace_back(-delta / beta);
+  }
+
+  if (type == analysis::kArm) {
+    double delta = std::get<0>(ols)[3];    // -Kg/Ka cos(offset)
+    double epsilon = std::get<0>(ols)[4];  // Kg/Ka sin(offset)
+
+    // Add Kg to gains list
+    gains.emplace_back(std::hypot(delta, epsilon) / beta);
+
+    // Add offset to gains list
+    gains.emplace_back(std::atan2(epsilon, -delta));
+  }
+
+  // Gains are Ks, Kv, Ka, Kg (elevator/arm only), offset (arm only)
+  return std::tuple{gains, std::get<1>(ols), std::get<2>(ols)};
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/FilteringUtils.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/FilteringUtils.cpp
new file mode 100644
index 0000000..6c66ef8
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/FilteringUtils.cpp
@@ -0,0 +1,417 @@
+// 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 "sysid/analysis/FilteringUtils.h"
+
+#include <limits>
+#include <numbers>
+#include <numeric>
+#include <stdexcept>
+#include <vector>
+
+#include <fmt/format.h>
+#include <frc/filter/LinearFilter.h>
+#include <frc/filter/MedianFilter.h>
+#include <units/math.h>
+#include <wpi/StringExtras.h>
+
+using namespace sysid;
+
+/**
+ * Helper function that throws if it detects that the data vector is too small
+ * for an operation of a certain window size.
+ *
+ * @param data The data that is being used.
+ * @param window The window size for the operation.
+ * @param operation The operation we're checking the size for (for error
+ *                  throwing purposes).
+ */
+static void CheckSize(const std::vector<PreparedData>& data, size_t window,
+                      std::string_view operation) {
+  if (data.size() < window) {
+    throw sysid::InvalidDataError(
+        fmt::format("Not enough data to run {} which has a window size of {}.",
+                    operation, window));
+  }
+}
+
+/**
+ * Helper function that determines if a certain key is storing raw data.
+ *
+ * @param key The key of the dataset
+ *
+ * @return True, if the key corresponds to a raw dataset.
+ */
+static bool IsRaw(std::string_view key) {
+  return wpi::contains(key, "raw") && !wpi::contains(key, "original");
+}
+
+/**
+ * Helper function that determines if a certain key is storing filtered data.
+ *
+ * @param key The key of the dataset
+ *
+ * @return True, if the key corresponds to a filtered dataset.
+ */
+static bool IsFiltered(std::string_view key) {
+  return !wpi::contains(key, "raw") && !wpi::contains(key, "original");
+}
+
+/**
+ * Fills in the rest of the PreparedData Structs for a PreparedData Vector.
+ *
+ * @param data A reference to a vector of the raw data.
+ * @param unit The units that the data is in (rotations, radians, or degrees)
+ *             for arm mechanisms.
+ */
+static void PrepareMechData(std::vector<PreparedData>* data,
+                            std::string_view unit = "") {
+  constexpr size_t kWindow = 3;
+
+  CheckSize(*data, kWindow, "Acceleration Calculation");
+
+  // Calculates the cosine of the position data for single jointed arm analysis
+  for (size_t i = 0; i < data->size(); ++i) {
+    auto& pt = data->at(i);
+
+    double cos = 0.0;
+    double sin = 0.0;
+    if (unit == "Radians") {
+      cos = std::cos(pt.position);
+      sin = std::sin(pt.position);
+    } else if (unit == "Degrees") {
+      cos = std::cos(pt.position * std::numbers::pi / 180.0);
+      sin = std::sin(pt.position * std::numbers::pi / 180.0);
+    } else if (unit == "Rotations") {
+      cos = std::cos(pt.position * 2 * std::numbers::pi);
+      sin = std::sin(pt.position * 2 * std::numbers::pi);
+    }
+    pt.cos = cos;
+    pt.sin = sin;
+  }
+
+  auto derivative =
+      CentralFiniteDifference<1, kWindow>(GetMeanTimeDelta(*data));
+
+  // Load the derivative filter with the first value for accurate initial
+  // behavior
+  for (size_t i = 0; i < kWindow; ++i) {
+    derivative.Calculate(data->at(0).velocity);
+  }
+
+  for (size_t i = (kWindow - 1) / 2; i < data->size(); ++i) {
+    data->at(i - (kWindow - 1) / 2).acceleration =
+        derivative.Calculate(data->at(i).velocity);
+  }
+
+  // Fill in accelerations past end of derivative filter
+  for (size_t i = data->size() - (kWindow - 1) / 2; i < data->size(); ++i) {
+    data->at(i).acceleration = 0.0;
+  }
+}
+
+std::tuple<units::second_t, units::second_t, units::second_t>
+sysid::TrimStepVoltageData(std::vector<PreparedData>* data,
+                           AnalysisManager::Settings* settings,
+                           units::second_t minStepTime,
+                           units::second_t maxStepTime) {
+  auto voltageBegins =
+      std::find_if(data->begin(), data->end(),
+                   [](auto& datum) { return std::abs(datum.voltage) > 0; });
+
+  units::second_t firstTimestamp = voltageBegins->timestamp;
+  double firstPosition = voltageBegins->position;
+
+  auto motionBegins = std::find_if(
+      data->begin(), data->end(), [settings, firstPosition](auto& datum) {
+        return std::abs(datum.position - firstPosition) >
+               (settings->motionThreshold * datum.dt.value());
+      });
+
+  units::second_t positionDelay;
+  if (motionBegins != data->end()) {
+    positionDelay = motionBegins->timestamp - firstTimestamp;
+  } else {
+    positionDelay = 0_s;
+  }
+
+  auto maxAccel = std::max_element(
+      data->begin(), data->end(), [](const auto& a, const auto& b) {
+        return std::abs(a.acceleration) < std::abs(b.acceleration);
+      });
+
+  units::second_t velocityDelay;
+  if (maxAccel != data->end()) {
+    velocityDelay = maxAccel->timestamp - firstTimestamp;
+
+    // Trim data before max acceleration
+    data->erase(data->begin(), maxAccel);
+  } else {
+    velocityDelay = 0_s;
+  }
+
+  minStepTime = std::min(data->at(0).timestamp - firstTimestamp, minStepTime);
+
+  // If step duration hasn't been set yet, calculate a default (find the entry
+  // before the acceleration first hits zero)
+  if (settings->stepTestDuration <= minStepTime) {
+    // Get noise floor
+    const double accelNoiseFloor = GetNoiseFloor(
+        *data, kNoiseMeanWindow, [](auto&& pt) { return pt.acceleration; });
+    // Find latest element with nonzero acceleration
+    auto endIt = std::find_if(
+        data->rbegin(), data->rend(), [&](const PreparedData& entry) {
+          return std::abs(entry.acceleration) > accelNoiseFloor;
+        });
+
+    if (endIt != data->rend()) {
+      // Calculate default duration
+      settings->stepTestDuration = std::min(
+          endIt->timestamp - data->front().timestamp + minStepTime + 1_s,
+          maxStepTime);
+    } else {
+      settings->stepTestDuration = maxStepTime;
+    }
+  }
+
+  // Find first entry greater than the step test duration
+  auto maxIt =
+      std::find_if(data->begin(), data->end(), [&](PreparedData entry) {
+        return entry.timestamp - data->front().timestamp + minStepTime >
+               settings->stepTestDuration;
+      });
+
+  // Trim data beyond desired step test duration
+  if (maxIt != data->end()) {
+    data->erase(maxIt, data->end());
+  }
+  return std::make_tuple(minStepTime, positionDelay, velocityDelay);
+}
+
+double sysid::GetNoiseFloor(
+    const std::vector<PreparedData>& data, int window,
+    std::function<double(const PreparedData&)> accessorFunction) {
+  double sum = 0.0;
+  size_t step = window / 2;
+  auto averageFilter = frc::LinearFilter<double>::MovingAverage(window);
+  for (size_t i = 0; i < data.size(); i++) {
+    double mean = averageFilter.Calculate(accessorFunction(data[i]));
+    if (i >= step) {
+      sum += std::pow(accessorFunction(data[i - step]) - mean, 2);
+    }
+  }
+  return std::sqrt(sum / (data.size() - step));
+}
+
+units::second_t sysid::GetMeanTimeDelta(const std::vector<PreparedData>& data) {
+  std::vector<units::second_t> dts;
+
+  for (const auto& pt : data) {
+    if (pt.dt > 0_s && pt.dt < 500_ms) {
+      dts.emplace_back(pt.dt);
+    }
+  }
+
+  return std::accumulate(dts.begin(), dts.end(), 0_s) / dts.size();
+}
+
+units::second_t sysid::GetMeanTimeDelta(const Storage& data) {
+  std::vector<units::second_t> dts;
+
+  for (const auto& pt : data.slowForward) {
+    if (pt.dt > 0_s && pt.dt < 500_ms) {
+      dts.emplace_back(pt.dt);
+    }
+  }
+
+  for (const auto& pt : data.slowBackward) {
+    if (pt.dt > 0_s && pt.dt < 500_ms) {
+      dts.emplace_back(pt.dt);
+    }
+  }
+
+  for (const auto& pt : data.fastForward) {
+    if (pt.dt > 0_s && pt.dt < 500_ms) {
+      dts.emplace_back(pt.dt);
+    }
+  }
+
+  for (const auto& pt : data.fastBackward) {
+    if (pt.dt > 0_s && pt.dt < 500_ms) {
+      dts.emplace_back(pt.dt);
+    }
+  }
+
+  return std::accumulate(dts.begin(), dts.end(), 0_s) / dts.size();
+}
+
+void sysid::ApplyMedianFilter(std::vector<PreparedData>* data, int window) {
+  CheckSize(*data, window, "Median Filter");
+
+  frc::MedianFilter<double> medianFilter(window);
+
+  // Load the median filter with the first value for accurate initial behavior
+  for (int i = 0; i < window; i++) {
+    medianFilter.Calculate(data->at(0).velocity);
+  }
+
+  for (size_t i = (window - 1) / 2; i < data->size(); i++) {
+    data->at(i - (window - 1) / 2).velocity =
+        medianFilter.Calculate(data->at(i).velocity);
+  }
+
+  // Run the median filter for the last half window of datapoints by loading the
+  // median filter with the last recorded velocity value
+  for (size_t i = data->size() - (window - 1) / 2; i < data->size(); i++) {
+    data->at(i).velocity =
+        medianFilter.Calculate(data->at(data->size() - 1).velocity);
+  }
+}
+
+/**
+ * Removes a substring from a string reference
+ *
+ * @param str The std::string_view that needs modification
+ * @param removeStr The substring that needs to be removed
+ *
+ * @return an std::string without the specified substring
+ */
+static std::string RemoveStr(std::string_view str, std::string_view removeStr) {
+  size_t idx = str.find(removeStr);
+  if (idx == std::string_view::npos) {
+    return std::string{str};
+  } else {
+    return fmt::format("{}{}", str.substr(0, idx),
+                       str.substr(idx + removeStr.size()));
+  }
+}
+
+/**
+ * Figures out the max duration of the Dynamic tests
+ *
+ * @param data The raw data String Map
+ *
+ * @return The maximum duration of the Dynamic Tests
+ */
+static units::second_t GetMaxStepTime(
+    wpi::StringMap<std::vector<PreparedData>>& data) {
+  auto maxStepTime = 0_s;
+  for (auto& it : data) {
+    auto key = it.first();
+    auto& dataset = it.getValue();
+
+    if (IsRaw(key) && wpi::contains(key, "fast")) {
+      auto duration = dataset.back().timestamp - dataset.front().timestamp;
+      if (duration > maxStepTime) {
+        maxStepTime = duration;
+      }
+    }
+  }
+  return maxStepTime;
+}
+
+void sysid::InitialTrimAndFilter(
+    wpi::StringMap<std::vector<PreparedData>>* data,
+    AnalysisManager::Settings* settings,
+    std::vector<units::second_t>& positionDelays,
+    std::vector<units::second_t>& velocityDelays, units::second_t& minStepTime,
+    units::second_t& maxStepTime, std::string_view unit) {
+  auto& preparedData = *data;
+
+  // Find the maximum Step Test Duration of the dynamic tests
+  maxStepTime = GetMaxStepTime(preparedData);
+
+  // Calculate Velocity Threshold if it hasn't been set yet
+  if (settings->motionThreshold == std::numeric_limits<double>::infinity()) {
+    for (auto& it : preparedData) {
+      auto key = it.first();
+      auto& dataset = it.getValue();
+      if (wpi::contains(key, "slow")) {
+        settings->motionThreshold =
+            std::min(settings->motionThreshold,
+                     GetNoiseFloor(dataset, kNoiseMeanWindow,
+                                   [](auto&& pt) { return pt.velocity; }));
+      }
+    }
+  }
+
+  for (auto& it : preparedData) {
+    auto key = it.first();
+    auto& dataset = it.getValue();
+
+    // Trim quasistatic test data to remove all points where voltage is zero or
+    // velocity < motion threshold.
+    if (wpi::contains(key, "slow")) {
+      dataset.erase(std::remove_if(dataset.begin(), dataset.end(),
+                                   [&](const auto& pt) {
+                                     return std::abs(pt.voltage) <= 0 ||
+                                            std::abs(pt.velocity) <
+                                                settings->motionThreshold;
+                                   }),
+                    dataset.end());
+
+      // Confirm there's still data
+      if (dataset.empty()) {
+        throw sysid::NoQuasistaticDataError();
+      }
+    }
+
+    // Apply Median filter
+    if (IsFiltered(key) && settings->medianWindow > 1) {
+      ApplyMedianFilter(&dataset, settings->medianWindow);
+    }
+
+    // Recalculate Accel and Cosine
+    PrepareMechData(&dataset, unit);
+
+    // Trims filtered Dynamic Test Data
+    if (IsFiltered(key) && wpi::contains(key, "fast")) {
+      // Get the filtered dataset name
+      auto filteredKey = RemoveStr(key, "raw-");
+
+      // Trim Filtered Data
+      auto [tempMinStepTime, positionDelay, velocityDelay] =
+          TrimStepVoltageData(&preparedData[filteredKey], settings, minStepTime,
+                              maxStepTime);
+
+      positionDelays.emplace_back(positionDelay);
+      velocityDelays.emplace_back(velocityDelay);
+
+      // Set the Raw Data to start at the same time as the Filtered Data
+      auto startTime = preparedData[filteredKey].front().timestamp;
+      auto rawStart =
+          std::find_if(preparedData[key].begin(), preparedData[key].end(),
+                       [&](auto&& pt) { return pt.timestamp == startTime; });
+      preparedData[key].erase(preparedData[key].begin(), rawStart);
+
+      // Confirm there's still data
+      if (preparedData[key].empty()) {
+        throw sysid::NoDynamicDataError();
+      }
+    }
+  }
+}
+
+void sysid::AccelFilter(wpi::StringMap<std::vector<PreparedData>>* data) {
+  auto& preparedData = *data;
+
+  // Remove points with acceleration = 0
+  for (auto& it : preparedData) {
+    auto& dataset = it.getValue();
+
+    for (size_t i = 0; i < dataset.size(); i++) {
+      if (dataset.at(i).acceleration == 0.0) {
+        dataset.erase(dataset.begin() + i);
+        i--;
+      }
+    }
+  }
+
+  // Confirm there's still data
+  if (std::any_of(preparedData.begin(), preparedData.end(),
+                  [](const auto& it) { return it.getValue().empty(); })) {
+    throw sysid::InvalidDataError(
+        "Acceleration filtering has removed all data.");
+  }
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/JSONConverter.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/JSONConverter.cpp
new file mode 100644
index 0000000..9bab8c6
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/JSONConverter.cpp
@@ -0,0 +1,164 @@
+// 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 "sysid/analysis/JSONConverter.h"
+
+#include <stdexcept>
+#include <string>
+
+#include <fmt/core.h>
+#include <fmt/format.h>
+#include <wpi/Logger.h>
+#include <wpi/MemoryBuffer.h>
+#include <wpi/fmt/raw_ostream.h>
+#include <wpi/json.h>
+#include <wpi/raw_ostream.h>
+
+#include "sysid/Util.h"
+#include "sysid/analysis/AnalysisManager.h"
+#include "sysid/analysis/AnalysisType.h"
+
+// Sizes of the arrays for new sysid data.
+static constexpr size_t kDrivetrainSize = 9;
+static constexpr size_t kGeneralSize = 4;
+
+// Indices for the old data.
+static constexpr size_t kTimestampCol = 0;
+static constexpr size_t kLVoltsCol = 3;
+static constexpr size_t kRVoltsCol = 4;
+static constexpr size_t kLPosCol = 5;
+static constexpr size_t kRPosCol = 6;
+static constexpr size_t kLVelCol = 7;
+static constexpr size_t kRVelCol = 8;
+
+static wpi::json GetJSON(std::string_view path, wpi::Logger& logger) {
+  std::error_code ec;
+  std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+      wpi::MemoryBuffer::GetFile(path, ec);
+  if (fileBuffer == nullptr || ec) {
+    throw std::runtime_error(fmt::format("Unable to read: {}", path));
+  }
+
+  wpi::json json = wpi::json::parse(fileBuffer->begin(), fileBuffer->end());
+  WPI_INFO(logger, "Read frc-characterization JSON from {}", path);
+  return json;
+}
+
+std::string sysid::ConvertJSON(std::string_view path, wpi::Logger& logger) {
+  wpi::json ojson = GetJSON(path, logger);
+
+  auto type = sysid::analysis::FromName(ojson.at("test").get<std::string>());
+  auto factor = ojson.at("unitsPerRotation").get<double>();
+  auto unit = ojson.at("units").get<std::string>();
+
+  wpi::json json;
+  for (auto&& key : AnalysisManager::kJsonDataKeys) {
+    if (type == analysis::kDrivetrain) {
+      // Get the old data; create a vector for the new data; reserve the
+      // appropriate size for the new data.
+      auto odata = ojson.at(key).get<std::vector<std::array<double, 10>>>();
+      std::vector<std::array<double, kDrivetrainSize>> data;
+      data.reserve(odata.size());
+
+      // Transfer the data.
+      for (auto&& pt : odata) {
+        data.push_back(std::array<double, kDrivetrainSize>{
+            pt[kTimestampCol], pt[kLVoltsCol], pt[kRVoltsCol], pt[kLPosCol],
+            pt[kRPosCol], pt[kLVelCol], pt[kRVelCol], 0.0, 0.0});
+      }
+      json[key] = data;
+    } else {
+      // Get the old data; create a vector for the new data; reserve the
+      // appropriate size for the new data.
+      auto odata = ojson.at(key).get<std::vector<std::array<double, 10>>>();
+      std::vector<std::array<double, kGeneralSize>> data;
+      data.reserve(odata.size());
+
+      // Transfer the data.
+      for (auto&& pt : odata) {
+        data.push_back(std::array<double, kGeneralSize>{
+            pt[kTimestampCol], pt[kLVoltsCol], pt[kLPosCol], pt[kLVelCol]});
+      }
+      json[key] = data;
+    }
+  }
+  json["units"] = unit;
+  json["unitsPerRotation"] = factor;
+  json["test"] = type.name;
+  json["sysid"] = true;
+
+  // Write the new file with "_new" appended to it.
+  path.remove_suffix(std::string_view{".json"}.size());
+  std::string loc = fmt::format("{}_new.json", path);
+
+  sysid::SaveFile(json.dump(2), std::filesystem::path{loc});
+
+  WPI_INFO(logger, "Wrote new JSON to: {}", loc);
+  return loc;
+}
+
+std::string sysid::ToCSV(std::string_view path, wpi::Logger& logger) {
+  wpi::json json = GetJSON(path, logger);
+
+  auto type = sysid::analysis::FromName(json.at("test").get<std::string>());
+  auto factor = json.at("unitsPerRotation").get<double>();
+  auto unit = json.at("units").get<std::string>();
+  std::string_view abbreviation = GetAbbreviation(unit);
+
+  std::error_code ec;
+  // Naming: {sysid-json-name}(Test, Units).csv
+  path.remove_suffix(std::string_view{".json"}.size());
+  std::string loc = fmt::format("{} ({}, {}).csv", path, type.name, unit);
+  wpi::raw_fd_ostream outputFile{loc, ec};
+
+  if (ec) {
+    throw std::runtime_error("Unable to write to: " + loc);
+  }
+
+  fmt::print(outputFile, "Timestamp (s),Test,");
+  if (type == analysis::kDrivetrain || type == analysis::kDrivetrainAngular) {
+    fmt::print(
+        outputFile,
+        "Left Volts (V),Right Volts (V),Left Position ({0}),Right "
+        "Position ({0}),Left Velocity ({0}/s),Right Velocity ({0}/s),Gyro "
+        "Position (deg),Gyro Rate (deg/s)\n",
+        abbreviation);
+  } else {
+    fmt::print(outputFile, "Volts (V),Position({0}),Velocity ({0}/s)\n",
+               abbreviation);
+  }
+  outputFile << "\n";
+
+  for (auto&& key : AnalysisManager::kJsonDataKeys) {
+    if (type == analysis::kDrivetrain || type == analysis::kDrivetrainAngular) {
+      auto tempData =
+          json.at(key).get<std::vector<std::array<double, kDrivetrainSize>>>();
+      for (auto&& pt : tempData) {
+        fmt::print(outputFile, "{},{},{},{},{},{},{},{},{},{}\n",
+                   pt[0],                           // Timestamp
+                   key,                             // Test
+                   pt[1], pt[2],                    // Left and Right Voltages
+                   pt[3] * factor, pt[4] * factor,  // Left and Right Positions
+                   pt[5] * factor, pt[6] * factor,  // Left and Right Velocity
+                   pt[7], pt[8]  // Gyro Position and Velocity
+        );
+      }
+    } else {
+      auto tempData =
+          json.at(key).get<std::vector<std::array<double, kGeneralSize>>>();
+      for (auto&& pt : tempData) {
+        fmt::print(outputFile, "{},{},{},{},{}\n",
+                   pt[0],           // Timestamp,
+                   key,             // Test
+                   pt[1],           // Voltage
+                   pt[2] * factor,  // Position
+                   pt[3] * factor   // Velocity
+        );
+      }
+    }
+  }
+  outputFile.flush();
+  WPI_INFO(logger, "Wrote CSV to: {}", loc);
+  return loc;
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/OLS.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/OLS.cpp
new file mode 100644
index 0000000..d095a48
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/OLS.cpp
@@ -0,0 +1,48 @@
+// 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 "sysid/analysis/OLS.h"
+
+#include <tuple>
+#include <vector>
+
+#include <Eigen/Cholesky>
+#include <Eigen/Core>
+
+using namespace sysid;
+
+std::tuple<std::vector<double>, double, double> sysid::OLS(
+    const Eigen::MatrixXd& X, const Eigen::VectorXd& y) {
+  assert(X.rows() == y.rows());
+
+  // The linear model can be written as follows:
+  // y = Xβ + u, where y is the dependent observed variable, X is the matrix
+  // of independent variables, β is a vector of coefficients, and u is a
+  // vector of residuals.
+
+  // We want to minimize u² = uᵀu = (y - Xβ)ᵀ(y - Xβ).
+  // β = (XᵀX)⁻¹Xᵀy
+
+  // Calculate β that minimizes uᵀu.
+  Eigen::MatrixXd beta = (X.transpose() * X).llt().solve(X.transpose() * y);
+
+  // We will now calculate R² or the coefficient of determination, which
+  // tells us how much of the total variation (variation in y) can be
+  // explained by the regression model.
+
+  // We will first calculate the sum of the squares of the error, or the
+  // variation in error (SSE).
+  double SSE = (y - X * beta).squaredNorm();
+
+  int n = X.cols();
+
+  // Now we will calculate the total variation in y, known as SSTO.
+  double SSTO = ((y.transpose() * y) - (1.0 / n) * (y.transpose() * y)).value();
+
+  double rSquared = (SSTO - SSE) / SSTO;
+  double adjRSquared = 1.0 - (1.0 - rSquared) * ((n - 1.0) / (n - 3.0));
+  double RMSE = std::sqrt(SSE / n);
+
+  return {{beta.data(), beta.data() + beta.rows()}, adjRSquared, RMSE};
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/SimpleMotorSim.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/SimpleMotorSim.cpp
new file mode 100644
index 0000000..58a8b30
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/SimpleMotorSim.cpp
@@ -0,0 +1,47 @@
+// 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 "sysid/analysis/SimpleMotorSim.h"
+
+#include <frc/StateSpaceUtil.h>
+#include <frc/system/Discretization.h>
+#include <wpi/MathExtras.h>
+
+using namespace sysid;
+
+SimpleMotorSim::SimpleMotorSim(double Ks, double Kv, double Ka,
+                               double initialPosition, double initialVelocity)
+    // dx/dt = Ax + Bu + c sgn(x)
+    : m_A{{0.0, 1.0}, {0.0, -Kv / Ka}}, m_B{0.0, 1.0 / Ka}, m_c{0.0, -Ks / Ka} {
+  Reset(initialPosition, initialVelocity);
+}
+
+void SimpleMotorSim::Update(units::volt_t voltage, units::second_t dt) {
+  Eigen::Vector<double, 1> u{voltage.value()};
+
+  // Given dx/dt = Ax + Bu + c sgn(x),
+  // x_k+1 = e^(AT) x_k + A^-1 (e^(AT) - 1) (Bu + c sgn(x))
+  Eigen::Matrix<double, 2, 2> Ad;
+  Eigen::Matrix<double, 2, 1> Bd;
+  frc::DiscretizeAB<2, 1>(m_A, m_B, dt, &Ad, &Bd);
+  m_x = Ad * m_x + Bd * u +
+        Bd * m_B.householderQr().solve(m_c * wpi::sgn(GetVelocity()));
+}
+
+double SimpleMotorSim::GetPosition() const {
+  return m_x(0);
+}
+
+double SimpleMotorSim::GetVelocity() const {
+  return m_x(1);
+}
+
+double SimpleMotorSim::GetAcceleration(units::volt_t voltage) const {
+  Eigen::Vector<double, 1> u{voltage.value()};
+  return (m_A * m_x + m_B * u + m_c * wpi::sgn(GetVelocity()))(1);
+}
+
+void SimpleMotorSim::Reset(double position, double velocity) {
+  m_x = Eigen::Vector<double, 2>{position, velocity};
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/analysis/TrackWidthAnalysis.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/TrackWidthAnalysis.cpp
new file mode 100644
index 0000000..eebfe8c
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/analysis/TrackWidthAnalysis.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 "sysid/analysis/TrackWidthAnalysis.h"
+
+#include <cmath>
+
+double sysid::CalculateTrackWidth(double l, double r, units::radian_t accum) {
+  // The below comes from solving ω = (vr − vl) / 2r for 2r.
+  return (std::abs(r) + std::abs(l)) / std::abs(accum.value());
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/telemetry/TelemetryManager.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/telemetry/TelemetryManager.cpp
new file mode 100644
index 0000000..ac97cdb
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/telemetry/TelemetryManager.cpp
@@ -0,0 +1,275 @@
+// 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 "sysid/telemetry/TelemetryManager.h"
+
+#include <algorithm>
+#include <cctype>  // for ::tolower
+#include <numbers>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+#include <fmt/chrono.h>
+#include <networktables/NetworkTableInstance.h>
+#include <wpi/Logger.h>
+#include <wpi/SmallVector.h>
+#include <wpi/StringExtras.h>
+#include <wpi/raw_ostream.h>
+#include <wpi/timestamp.h>
+
+#include "sysid/Util.h"
+#include "sysid/analysis/AnalysisType.h"
+
+using namespace sysid;
+
+TelemetryManager::TelemetryManager(const Settings& settings,
+                                   wpi::Logger& logger,
+                                   nt::NetworkTableInstance instance)
+    : m_settings(settings), m_logger(logger), m_inst(instance) {}
+
+void TelemetryManager::BeginTest(std::string_view name) {
+  // Create a new test params instance for this test.
+  m_params =
+      TestParameters{name.starts_with("fast"), name.ends_with("forward"),
+                     m_settings.mechanism == analysis::kDrivetrainAngular,
+                     State::WaitingForEnable};
+
+  // Add this test to the list of running tests and set the running flag.
+  m_tests.push_back(std::string{name});
+  m_isRunningTest = true;
+
+  // Set the Voltage Command Entry
+  m_voltageCommand.Set((m_params.fast ? m_settings.stepVoltage
+                                      : m_settings.quasistaticRampRate) *
+                       (m_params.forward ? 1 : -1));
+
+  // Set the test type
+  m_testType.Set(m_params.fast ? "Dynamic" : "Quasistatic");
+
+  // Set the rotate entry
+  m_rotate.Set(m_params.rotate);
+
+  // Set the current mechanism in NT.
+  m_mechanism.Set(m_settings.mechanism.name);
+  // Set Overflow to False
+  m_overflowPub.Set(false);
+  // Set Mechanism Error to False
+  m_mechErrorPub.Set(false);
+  m_inst.Flush();
+
+  // Display the warning message.
+  for (auto&& func : m_callbacks) {
+    func(
+        "Please enable the robot in autonomous mode, and then "
+        "disable it "
+        "before it runs out of space. \n Note: The robot will "
+        "continue "
+        "to move until you disable it - It is your "
+        "responsibility to "
+        "ensure it does not hit anything!");
+  }
+
+  WPI_INFO(m_logger, "Started {} test.", m_tests.back());
+}
+
+void TelemetryManager::EndTest() {
+  // If there is no test running, this is a no-op
+  if (!m_isRunningTest) {
+    return;
+  }
+
+  // Disable the running flag and store the data in the JSON.
+  m_isRunningTest = false;
+  m_data[m_tests.back()] = m_params.data;
+
+  // Call the cancellation callbacks.
+  for (auto&& func : m_callbacks) {
+    std::string msg;
+    if (m_params.mechError) {
+      msg +=
+          "\nERROR: The robot indicated that you are using the wrong project "
+          "for characterizing your mechanism. \nThis most likely means you "
+          "are trying to characterize a mechanism like a Drivetrain with a "
+          "deployed config for a General Mechanism (e.g. Arm, Flywheel, and "
+          "Elevator) or vice versa. Please double check your settings and "
+          "try again.";
+    } else if (!m_params.data.empty()) {
+      std::string units = m_settings.units;
+      std::transform(m_settings.units.begin(), m_settings.units.end(),
+                     units.begin(), ::tolower);
+
+      if (std::string_view{m_settings.mechanism.name}.starts_with(
+              "Drivetrain")) {
+        double p = (m_params.data.back()[3] - m_params.data.front()[3]) *
+                   m_settings.unitsPerRotation;
+        double s = (m_params.data.back()[4] - m_params.data.front()[4]) *
+                   m_settings.unitsPerRotation;
+        double g = m_params.data.back()[7] - m_params.data.front()[7];
+
+        msg = fmt::format(
+            "The left and right encoders traveled {} {} and {} {} "
+            "respectively.\nThe gyro angle delta was {} degrees.",
+            p, units, s, units, g * 180.0 / std::numbers::pi);
+      } else {
+        double p = (m_params.data.back()[2] - m_params.data.front()[2]) *
+                   m_settings.unitsPerRotation;
+        msg = fmt::format("The encoder reported traveling {} {}.", p, units);
+      }
+
+      if (m_params.overflow) {
+        msg +=
+            "\nNOTE: the robot stopped recording data early because the entry "
+            "storage was exceeded.";
+      }
+    } else {
+      msg = "No data was detected.";
+    }
+    func(msg);
+  }
+
+  // Remove previously run test from list of tests if no data was detected.
+  if (m_params.data.empty()) {
+    m_tests.pop_back();
+  }
+
+  // Send a zero command over NT.
+  m_voltageCommand.Set(0.0);
+  m_inst.Flush();
+}
+
+void TelemetryManager::Update() {
+  // If there is no test running, these is nothing to update.
+  if (!m_isRunningTest) {
+    return;
+  }
+
+  // Update the NT entries that we're reading.
+
+  int currAckNumber = m_ackNumberSub.Get();
+  std::string telemetryValue;
+
+  // Get the FMS Control Word.
+  for (auto tsValue : m_fmsControlData.ReadQueue()) {
+    uint32_t ctrl = tsValue.value;
+    m_params.enabled = ctrl & 0x01;
+  }
+
+  // Get the string in the data field.
+  for (auto tsValue : m_telemetry.ReadQueue()) {
+    telemetryValue = tsValue.value;
+  }
+
+  // Get the overflow flag
+  for (auto tsValue : m_overflowSub.ReadQueue()) {
+    m_params.overflow = tsValue.value;
+  }
+
+  // Get the mechanism error flag
+  for (auto tsValue : m_mechErrorSub.ReadQueue()) {
+    m_params.mechError = tsValue.value;
+  }
+
+  // Go through our state machine.
+  if (m_params.state == State::WaitingForEnable) {
+    if (m_params.enabled) {
+      m_params.enableStart = wpi::Now() * 1E-6;
+      m_params.state = State::RunningTest;
+      m_ackNumber = currAckNumber;
+      WPI_INFO(m_logger, "{}", "Transitioned to running test state.");
+    }
+  }
+
+  if (m_params.state == State::RunningTest) {
+    // If for some reason we've disconnected, end the test.
+    if (!m_inst.IsConnected()) {
+      WPI_WARNING(m_logger, "{}",
+                  "NT connection was dropped when executing the test. The test "
+                  "has been canceled.");
+      EndTest();
+    }
+
+    // If the robot has disabled, then we can move on to the next step.
+    if (!m_params.enabled) {
+      m_params.disableStart = wpi::Now() * 1E-6;
+      m_params.state = State::WaitingForData;
+      WPI_INFO(m_logger, "{}", "Transitioned to waiting for data.");
+    }
+  }
+
+  if (m_params.state == State::WaitingForData) {
+    double now = wpi::Now() * 1E-6;
+    m_voltageCommand.Set(0.0);
+    m_inst.Flush();
+
+    // Process valid data
+    if (!telemetryValue.empty() && m_ackNumber < currAckNumber) {
+      m_params.raw = std::move(telemetryValue);
+      m_ackNumber = currAckNumber;
+    }
+
+    // We have the data that we need, so we can parse it and end the test.
+    if (!m_params.raw.empty() &&
+        wpi::starts_with(m_params.raw, m_tests.back())) {
+      // Remove test type from start of string
+      m_params.raw.erase(0, m_params.raw.find(';') + 1);
+
+      // Clean up the string -- remove spaces if there are any.
+      m_params.raw.erase(
+          std::remove_if(m_params.raw.begin(), m_params.raw.end(), ::isspace),
+          m_params.raw.end());
+
+      // Split the string into individual components.
+      wpi::SmallVector<std::string_view, 16> res;
+      wpi::split(m_params.raw, res, ',');
+
+      // Convert each string to double.
+      std::vector<double> values;
+      values.reserve(res.size());
+      for (auto&& str : res) {
+        values.push_back(wpi::parse_float<double>(str).value());
+      }
+
+      // Add the values to our result vector.
+      for (size_t i = 0; i < values.size() - m_settings.mechanism.rawDataSize;
+           i += m_settings.mechanism.rawDataSize) {
+        std::vector<double> d(m_settings.mechanism.rawDataSize);
+
+        std::copy_n(std::make_move_iterator(values.begin() + i),
+                    m_settings.mechanism.rawDataSize, d.begin());
+        m_params.data.push_back(std::move(d));
+      }
+
+      WPI_INFO(m_logger,
+               "Received data with size: {} for the {} test in {} seconds.",
+               m_params.data.size(), m_tests.back(),
+               m_params.data.back()[0] - m_params.data.front()[0]);
+      m_ackNumberPub.Set(++m_ackNumber);
+      EndTest();
+    }
+
+    // If we timed out, end the test and let the user know.
+    if (now - m_params.disableStart > 5.0) {
+      WPI_WARNING(m_logger, "{}",
+                  "TelemetryManager did not receieve data 5 seconds after "
+                  "completing the test...");
+      EndTest();
+    }
+  }
+}
+
+std::string TelemetryManager::SaveJSON(std::string_view location) {
+  m_data["test"] = m_settings.mechanism.name;
+  m_data["units"] = m_settings.units;
+  m_data["unitsPerRotation"] = m_settings.unitsPerRotation;
+  m_data["sysid"] = true;
+
+  std::string loc = fmt::format("{}/sysid_data{:%Y%m%d-%H%M%S}.json", location,
+                                std::chrono::system_clock::now());
+
+  sysid::SaveFile(m_data.dump(2), std::filesystem::path{loc});
+  WPI_INFO(m_logger, "Wrote JSON to: {}", loc);
+
+  return loc;
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/view/Analyzer.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/view/Analyzer.cpp
new file mode 100644
index 0000000..3270918
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/view/Analyzer.cpp
@@ -0,0 +1,851 @@
+// 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 "sysid/view/Analyzer.h"
+
+#include <algorithm>
+#include <exception>
+#include <filesystem>
+#include <numbers>
+#include <thread>
+
+#include <fmt/core.h>
+#include <glass/Context.h>
+#include <glass/Storage.h>
+#include <imgui.h>
+#include <imgui_internal.h>
+#include <imgui_stdlib.h>
+#include <wpi/json.h>
+
+#include "sysid/Util.h"
+#include "sysid/analysis/AnalysisManager.h"
+#include "sysid/analysis/AnalysisType.h"
+#include "sysid/analysis/FeedbackControllerPreset.h"
+#include "sysid/analysis/FilteringUtils.h"
+#include "sysid/view/UILayout.h"
+
+using namespace sysid;
+
+Analyzer::Analyzer(glass::Storage& storage, wpi::Logger& logger)
+    : m_location(""), m_logger(logger) {
+  // Fill the StringMap with preset values.
+  m_presets["Default"] = presets::kDefault;
+  m_presets["WPILib (2020-)"] = presets::kWPILibNew;
+  m_presets["WPILib (Pre-2020)"] = presets::kWPILibOld;
+  m_presets["CANCoder"] = presets::kCTRECANCoder;
+  m_presets["CTRE"] = presets::kCTREDefault;
+  m_presets["CTRE (Pro)"] = presets::kCTREProDefault;
+  m_presets["REV Brushless Encoder Port"] = presets::kREVNEOBuiltIn;
+  m_presets["REV Brushed Encoder Port"] = presets::kREVNonNEO;
+  m_presets["REV Data Port"] = presets::kREVNonNEO;
+  m_presets["Venom"] = presets::kVenom;
+
+  ResetData();
+  UpdateFeedbackGains();
+}
+
+void Analyzer::UpdateFeedforwardGains() {
+  WPI_INFO(m_logger, "{}", "Gain calc");
+  try {
+    const auto& [ff, trackWidth] = m_manager->CalculateFeedforward();
+    m_ff = std::get<0>(ff);
+    m_accelRSquared = std::get<1>(ff);
+    m_accelRMSE = std::get<2>(ff);
+    m_trackWidth = trackWidth;
+    m_settings.preset.measurementDelay =
+        m_settings.type == FeedbackControllerLoopType::kPosition
+            ? m_manager->GetPositionDelay()
+            : m_manager->GetVelocityDelay();
+    m_conversionFactor = m_manager->GetFactor();
+    PrepareGraphs();
+  } catch (const sysid::InvalidDataError& e) {
+    m_state = AnalyzerState::kGeneralDataError;
+    HandleError(e.what());
+  } catch (const sysid::NoQuasistaticDataError& e) {
+    m_state = AnalyzerState::kMotionThresholdError;
+    HandleError(e.what());
+  } catch (const sysid::NoDynamicDataError& e) {
+    m_state = AnalyzerState::kTestDurationError;
+    HandleError(e.what());
+  } catch (const AnalysisManager::FileReadingError& e) {
+    m_state = AnalyzerState::kFileError;
+    HandleError(e.what());
+  } catch (const wpi::json::exception& e) {
+    m_state = AnalyzerState::kFileError;
+    HandleError(e.what());
+  } catch (const std::exception& e) {
+    m_state = AnalyzerState::kFileError;
+    HandleError(e.what());
+  }
+}
+
+void Analyzer::UpdateFeedbackGains() {
+  if (m_ff[1] > 0 && m_ff[2] > 0) {
+    const auto& fb = m_manager->CalculateFeedback(m_ff);
+    m_timescale = units::second_t{m_ff[2] / m_ff[1]};
+    m_Kp = fb.Kp;
+    m_Kd = fb.Kd;
+  }
+}
+
+bool Analyzer::DisplayGain(const char* text, double* data,
+                           bool readOnly = true) {
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5);
+  if (readOnly) {
+    return ImGui::InputDouble(text, data, 0.0, 0.0, "%.5G",
+                              ImGuiInputTextFlags_ReadOnly);
+  } else {
+    return ImGui::InputDouble(text, data, 0.0, 0.0, "%.5G");
+  }
+}
+
+static void SetPosition(double beginX, double beginY, double xShift,
+                        double yShift) {
+  ImGui::SetCursorPos(ImVec2(beginX + xShift * 10 * ImGui::GetFontSize(),
+                             beginY + yShift * 1.75 * ImGui::GetFontSize()));
+}
+
+bool Analyzer::IsErrorState() {
+  return m_state == AnalyzerState::kMotionThresholdError ||
+         m_state == AnalyzerState::kTestDurationError ||
+         m_state == AnalyzerState::kGeneralDataError ||
+         m_state == AnalyzerState::kFileError;
+}
+
+bool Analyzer::IsDataErrorState() {
+  return m_state == AnalyzerState::kMotionThresholdError ||
+         m_state == AnalyzerState::kTestDurationError ||
+         m_state == AnalyzerState::kGeneralDataError;
+}
+
+void Analyzer::DisplayFileSelector() {
+  // Get the current width of the window. This will be used to scale
+  // our UI elements.
+  float width = ImGui::GetContentRegionAvail().x;
+
+  // Show the file location along with an option to choose.
+  if (ImGui::Button("Select")) {
+    m_selector = std::make_unique<pfd::open_file>(
+        "Select Data", "",
+        std::vector<std::string>{"JSON File", SYSID_PFD_JSON_EXT});
+  }
+  ImGui::SameLine();
+  ImGui::SetNextItemWidth(width - ImGui::CalcTextSize("Select").x -
+                          ImGui::GetFontSize() * 5);
+  ImGui::InputText("##location", &m_location, ImGuiInputTextFlags_ReadOnly);
+}
+
+void Analyzer::ResetData() {
+  m_plot.ResetData();
+  m_manager = std::make_unique<AnalysisManager>(m_settings, m_logger);
+  m_location = "";
+  m_ff = std::vector<double>{1, 1, 1};
+  UpdateFeedbackGains();
+}
+
+bool Analyzer::DisplayResetAndUnitOverride() {
+  auto type = m_manager->GetAnalysisType();
+  auto unit = m_manager->GetUnit();
+
+  float width = ImGui::GetContentRegionAvail().x;
+  ImGui::SameLine(width - ImGui::CalcTextSize("Reset").x);
+  if (ImGui::Button("Reset")) {
+    ResetData();
+    m_state = AnalyzerState::kWaitingForJSON;
+    return true;
+  }
+
+  if (type == analysis::kDrivetrain) {
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * kTextBoxWidthMultiple);
+    if (ImGui::Combo("Dataset", &m_dataset, kDatasets, 3)) {
+      m_settings.dataset =
+          static_cast<AnalysisManager::Settings::DrivetrainDataset>(m_dataset);
+      PrepareData();
+    }
+    ImGui::SameLine();
+  } else {
+    m_settings.dataset =
+        AnalysisManager::Settings::DrivetrainDataset::kCombined;
+  }
+
+  ImGui::Spacing();
+  ImGui::Text(
+      "Units:              %s\n"
+      "Units Per Rotation: %.4f\n"
+      "Type:               %s",
+      std::string(unit).c_str(), m_conversionFactor, type.name);
+
+  if (type == analysis::kDrivetrainAngular) {
+    ImGui::SameLine();
+    sysid::CreateTooltip(
+        "Here, the units and units per rotation represent what the wheel "
+        "positions and velocities were captured in. The track width value "
+        "will reflect the unit selected here. However, the Kv and Ka will "
+        "always be in Vs/rad and Vs^2 / rad respectively.");
+  }
+
+  if (ImGui::Button("Override Units")) {
+    ImGui::OpenPopup("Override Units");
+  }
+
+  auto size = ImGui::GetIO().DisplaySize;
+  ImGui::SetNextWindowSize(ImVec2(size.x / 4, size.y * 0.2));
+  if (ImGui::BeginPopupModal("Override Units")) {
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * 7);
+    ImGui::Combo("Units", &m_selectedOverrideUnit, kUnits,
+                 IM_ARRAYSIZE(kUnits));
+    unit = kUnits[m_selectedOverrideUnit];
+
+    if (unit == "Degrees") {
+      m_conversionFactor = 360.0;
+    } else if (unit == "Radians") {
+      m_conversionFactor = 2 * std::numbers::pi;
+    } else if (unit == "Rotations") {
+      m_conversionFactor = 1.0;
+    }
+
+    bool isRotational = m_selectedOverrideUnit > 2;
+
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * 7);
+    ImGui::InputDouble(
+        "Units Per Rotation", &m_conversionFactor, 0.0, 0.0, "%.4f",
+        isRotational ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_None);
+
+    if (ImGui::Button("Close")) {
+      ImGui::CloseCurrentPopup();
+      m_manager->OverrideUnits(unit, m_conversionFactor);
+      PrepareData();
+    }
+
+    ImGui::EndPopup();
+  }
+
+  ImGui::SameLine();
+  if (ImGui::Button("Reset Units from JSON")) {
+    m_manager->ResetUnitsFromJSON();
+    PrepareData();
+  }
+
+  return false;
+}
+
+void Analyzer::ConfigParamsOnFileSelect() {
+  WPI_INFO(m_logger, "{}", "Configuring Params");
+  m_stepTestDuration = m_settings.stepTestDuration.to<float>();
+
+  // Estimate qp as 1/8 * units-per-rot
+  m_settings.lqr.qp = 0.125 * m_manager->GetFactor();
+  // Estimate qv as 1/4 * max velocity = 1/4 * (12V - kS) / kV
+  m_settings.lqr.qv = 0.25 * (12.0 - m_ff[0]) / m_ff[1];
+}
+
+void Analyzer::Display() {
+  DisplayFileSelector();
+  DisplayGraphs();
+
+  switch (m_state) {
+    case AnalyzerState::kWaitingForJSON: {
+      ImGui::Text(
+          "SysId is currently in theoretical analysis mode.\n"
+          "To analyze recorded test data, select a "
+          "data JSON.");
+      sysid::CreateTooltip(
+          "Theoretical feedback gains can be calculated from a "
+          "physical model of the mechanism being controlled. "
+          "Theoretical gains for several common mechanisms can "
+          "be obtained from ReCalc (https://reca.lc).");
+      ImGui::Spacing();
+      ImGui::Spacing();
+
+      ImGui::SetNextItemOpen(true, ImGuiCond_Once);
+      if (ImGui::CollapsingHeader("Feedforward Gains (Theoretical)")) {
+        float beginX = ImGui::GetCursorPosX();
+        float beginY = ImGui::GetCursorPosY();
+        CollectFeedforwardGains(beginX, beginY);
+      }
+      ImGui::SetNextItemOpen(true, ImGuiCond_Once);
+      if (ImGui::CollapsingHeader("Feedback Analysis")) {
+        DisplayFeedbackGains();
+      }
+      break;
+    }
+    case AnalyzerState::kNominalDisplay: {  // Allow the user to select which
+                                            // data set they want analyzed and
+                                            // add a
+      // reset button. Also show the units and the units per rotation.
+      if (DisplayResetAndUnitOverride()) {
+        return;
+      }
+      ImGui::Spacing();
+      ImGui::Spacing();
+
+      ImGui::SetNextItemOpen(true, ImGuiCond_Once);
+      if (ImGui::CollapsingHeader("Feedforward Analysis")) {
+        float beginX = ImGui::GetCursorPosX();
+        float beginY = ImGui::GetCursorPosY();
+        DisplayFeedforwardGains(beginX, beginY);
+      }
+      ImGui::SetNextItemOpen(true, ImGuiCond_Once);
+      if (ImGui::CollapsingHeader("Feedback Analysis")) {
+        DisplayFeedbackGains();
+      }
+      break;
+    }
+    case AnalyzerState::kFileError: {
+      CreateErrorPopup(m_errorPopup, m_exception);
+      if (!m_errorPopup) {
+        m_state = AnalyzerState::kWaitingForJSON;
+        return;
+      }
+      break;
+    }
+    case AnalyzerState::kGeneralDataError:
+    case AnalyzerState::kTestDurationError:
+    case AnalyzerState::kMotionThresholdError: {
+      CreateErrorPopup(m_errorPopup, m_exception);
+      if (DisplayResetAndUnitOverride()) {
+        return;
+      }
+      float beginX = ImGui::GetCursorPosX();
+      float beginY = ImGui::GetCursorPosY();
+      DisplayFeedforwardParameters(beginX, beginY);
+      break;
+    }
+  }
+
+  // Periodic functions
+  try {
+    SelectFile();
+  } catch (const AnalysisManager::FileReadingError& e) {
+    m_state = AnalyzerState::kFileError;
+    HandleError(e.what());
+  } catch (const wpi::json::exception& e) {
+    m_state = AnalyzerState::kFileError;
+    HandleError(e.what());
+  }
+}
+
+void Analyzer::PrepareData() {
+  try {
+    m_manager->PrepareData();
+    UpdateFeedforwardGains();
+    UpdateFeedbackGains();
+  } catch (const sysid::InvalidDataError& e) {
+    m_state = AnalyzerState::kGeneralDataError;
+    HandleError(e.what());
+  } catch (const sysid::NoQuasistaticDataError& e) {
+    m_state = AnalyzerState::kMotionThresholdError;
+    HandleError(e.what());
+  } catch (const sysid::NoDynamicDataError& e) {
+    m_state = AnalyzerState::kTestDurationError;
+    HandleError(e.what());
+  } catch (const AnalysisManager::FileReadingError& e) {
+    m_state = AnalyzerState::kFileError;
+    HandleError(e.what());
+  } catch (const wpi::json::exception& e) {
+    m_state = AnalyzerState::kFileError;
+    HandleError(e.what());
+  } catch (const std::exception& e) {
+    m_state = AnalyzerState::kFileError;
+    HandleError(e.what());
+  }
+}
+
+void Analyzer::PrepareRawGraphs() {
+  if (m_manager->HasData()) {
+    AbortDataPrep();
+    m_dataThread = std::thread([&] {
+      m_plot.SetRawData(m_manager->GetOriginalData(), m_manager->GetUnit(),
+                        m_abortDataPrep);
+    });
+  }
+}
+
+void Analyzer::PrepareGraphs() {
+  if (m_manager->HasData()) {
+    WPI_INFO(m_logger, "{}", "Graph state");
+    AbortDataPrep();
+    m_dataThread = std::thread([&] {
+      m_plot.SetData(m_manager->GetRawData(), m_manager->GetFilteredData(),
+                     m_manager->GetUnit(), m_ff, m_manager->GetStartTimes(),
+                     m_manager->GetAnalysisType(), m_abortDataPrep);
+    });
+    UpdateFeedbackGains();
+    m_state = AnalyzerState::kNominalDisplay;
+  }
+}
+
+void Analyzer::HandleError(std::string_view msg) {
+  m_exception = msg;
+  m_errorPopup = true;
+  if (m_state == AnalyzerState::kFileError) {
+    m_location = "";
+  }
+  PrepareRawGraphs();
+}
+
+void Analyzer::DisplayGraphs() {
+  ImGui::SetNextWindowPos(ImVec2{kDiagnosticPlotWindowPos},
+                          ImGuiCond_FirstUseEver);
+  ImGui::SetNextWindowSize(ImVec2{kDiagnosticPlotWindowSize},
+                           ImGuiCond_FirstUseEver);
+  ImGui::Begin("Diagnostic Plots");
+
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6);
+  if (ImGui::SliderFloat("Point Size", &m_plot.m_pointSize, 1, 2, "%.2f")) {
+    if (!IsErrorState()) {
+      PrepareGraphs();
+    } else {
+      PrepareRawGraphs();
+    }
+  }
+
+  ImGui::SameLine();
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6);
+  const char* items[] = {"Forward", "Backward"};
+  if (ImGui::Combo("Direction", &m_plot.m_direction, items, 2)) {
+    if (!IsErrorState()) {
+      PrepareGraphs();
+    } else {
+      PrepareRawGraphs();
+    }
+  }
+
+  // If the plots were already loaded, store the scroll position. Else go to
+  // the last recorded scroll position if they have just been initialized
+  bool plotsLoaded = m_plot.DisplayPlots();
+  if (plotsLoaded) {
+    if (m_prevPlotsLoaded) {
+      m_graphScroll = ImGui::GetScrollY();
+    } else {
+      ImGui::SetScrollY(m_graphScroll);
+    }
+
+    // If a JSON is selected
+    if (m_state == AnalyzerState::kNominalDisplay) {
+      DisplayGain("Acceleration R²", &m_accelRSquared);
+      CreateTooltip(
+          "The coefficient of determination of the OLS fit of acceleration "
+          "versus velocity and voltage.  Acceleration is extremely noisy, "
+          "so this is generally quite small.");
+
+      ImGui::SameLine();
+      DisplayGain("Acceleration RMSE", &m_accelRMSE);
+      CreateTooltip(
+          "The standard deviation of the residuals from the predicted "
+          "acceleration."
+          "This can be interpreted loosely as the mean measured disturbance "
+          "from the \"ideal\" system equation.");
+
+      DisplayGain("Sim velocity R²", m_plot.GetSimRSquared());
+      CreateTooltip(
+          "The coefficient of determination the simulated velocity. "
+          "Velocity is much less-noisy than acceleration, so this "
+          "is pretty close to 1 for a decent fit.");
+
+      ImGui::SameLine();
+      DisplayGain("Sim velocity RMSE", m_plot.GetSimRMSE());
+      CreateTooltip(
+          "The standard deviation of the residuals from the simulated velocity "
+          "predictions - essentially the size of the mean error of the "
+          "simulated model "
+          "in the recorded velocity units.");
+    }
+  }
+  m_prevPlotsLoaded = plotsLoaded;
+
+  ImGui::End();
+}
+
+void Analyzer::SelectFile() {
+  // If the selector exists and is ready with a result, we can store it.
+  if (m_selector && m_selector->ready() && !m_selector->result().empty()) {
+    // Store the location of the file and reset the selector.
+    WPI_INFO(m_logger, "Opening File: {}", m_selector->result()[0]);
+    m_location = m_selector->result()[0];
+    m_selector.reset();
+    WPI_INFO(m_logger, "{}", "Opened File");
+    m_manager =
+        std::make_unique<AnalysisManager>(m_location, m_settings, m_logger);
+    PrepareData();
+    m_dataset = 0;
+    m_settings.dataset =
+        AnalysisManager::Settings::DrivetrainDataset::kCombined;
+    ConfigParamsOnFileSelect();
+    UpdateFeedbackGains();
+  }
+}
+
+void Analyzer::AbortDataPrep() {
+  if (m_dataThread.joinable()) {
+    m_abortDataPrep = true;
+    m_dataThread.join();
+    m_abortDataPrep = false;
+  }
+}
+
+void Analyzer::DisplayFeedforwardParameters(float beginX, float beginY) {
+  // Increase spacing to not run into trackwidth in the normal analyzer view
+  constexpr double kHorizontalOffset = 0.9;
+  SetPosition(beginX, beginY, kHorizontalOffset, 0);
+
+  bool displayAll =
+      !IsErrorState() || m_state == AnalyzerState::kGeneralDataError;
+
+  if (displayAll) {
+    // Wait for enter before refresh so double digit entries like "15" don't
+    // prematurely refresh with "1". That can cause display stuttering.
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
+    int window = m_settings.medianWindow;
+    if (ImGui::InputInt("Window Size", &window, 0, 0,
+                        ImGuiInputTextFlags_EnterReturnsTrue)) {
+      m_settings.medianWindow = std::clamp(window, 1, 15);
+      PrepareData();
+    }
+
+    CreateTooltip(
+        "The number of samples in the velocity median "
+        "filter's sliding window.");
+  }
+
+  if (displayAll || m_state == AnalyzerState::kMotionThresholdError) {
+    // Wait for enter before refresh so decimal inputs like "0.2" don't
+    // prematurely refresh with a velocity threshold of "0".
+    SetPosition(beginX, beginY, kHorizontalOffset, 1);
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
+    double threshold = m_settings.motionThreshold;
+    if (ImGui::InputDouble("Velocity Threshold", &threshold, 0.0, 0.0, "%.3f",
+                           ImGuiInputTextFlags_EnterReturnsTrue)) {
+      m_settings.motionThreshold = std::max(0.0, threshold);
+      PrepareData();
+    }
+    CreateTooltip("Velocity data below this threshold will be ignored.");
+  }
+
+  if (displayAll || m_state == AnalyzerState::kTestDurationError) {
+    SetPosition(beginX, beginY, kHorizontalOffset, 2);
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
+    if (ImGui::SliderFloat("Test Duration", &m_stepTestDuration,
+                           m_manager->GetMinStepTime().value(),
+                           m_manager->GetMaxStepTime().value(), "%.2f")) {
+      m_settings.stepTestDuration = units::second_t{m_stepTestDuration};
+      PrepareData();
+    }
+  }
+}
+
+void Analyzer::CollectFeedforwardGains(float beginX, float beginY) {
+  SetPosition(beginX, beginY, 0, 0);
+  if (DisplayGain("Kv", &m_ff[1], false)) {
+    UpdateFeedbackGains();
+  }
+
+  SetPosition(beginX, beginY, 0, 1);
+  if (DisplayGain("Ka", &m_ff[2], false)) {
+    UpdateFeedbackGains();
+  }
+
+  SetPosition(beginX, beginY, 0, 2);
+  // Show Timescale
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
+  DisplayGain("Response Timescale (ms)",
+              reinterpret_cast<double*>(&m_timescale));
+  CreateTooltip(
+      "The characteristic timescale of the system response in milliseconds. "
+      "Both the control loop period and total signal delay should be "
+      "at least 3-5 times shorter than this to optimally control the "
+      "system.");
+}
+
+void Analyzer::DisplayFeedforwardGains(float beginX, float beginY) {
+  SetPosition(beginX, beginY, 0, 0);
+  DisplayGain("Ks", &m_ff[0]);
+
+  SetPosition(beginX, beginY, 0, 1);
+  DisplayGain("Kv", &m_ff[1]);
+
+  SetPosition(beginX, beginY, 0, 2);
+  DisplayGain("Ka", &m_ff[2]);
+
+  SetPosition(beginX, beginY, 0, 3);
+  // Show Timescale
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
+  DisplayGain("Response Timescale (ms)",
+              reinterpret_cast<double*>(&m_timescale));
+  CreateTooltip(
+      "The characteristic timescale of the system response in milliseconds. "
+      "Both the control loop period and total signal delay should be "
+      "at least 3-5 times shorter than this to optimally control the "
+      "system.");
+
+  SetPosition(beginX, beginY, 0, 4);
+  auto positionDelay = m_manager->GetPositionDelay();
+  DisplayGain("Position Measurement Delay (ms)",
+              reinterpret_cast<double*>(&positionDelay));
+  CreateTooltip(
+      "The average elapsed time between the first application of "
+      "voltage and the first detected change in mechanism position "
+      "in the step-voltage tests.  This includes CAN delays, and "
+      "may overestimate the true delay for on-motor-controller "
+      "feedback loops by up to 20ms.");
+
+  SetPosition(beginX, beginY, 0, 5);
+  auto velocityDelay = m_manager->GetVelocityDelay();
+  DisplayGain("Velocity Measurement Delay (ms)",
+              reinterpret_cast<double*>(&velocityDelay));
+  CreateTooltip(
+      "The average elapsed time between the first application of "
+      "voltage and the maximum calculated mechanism acceleration "
+      "in the step-voltage tests.  This includes CAN delays, and "
+      "may overestimate the true delay for on-motor-controller "
+      "feedback loops by up to 20ms.");
+
+  SetPosition(beginX, beginY, 0, 6);
+
+  if (m_manager->GetAnalysisType() == analysis::kElevator) {
+    DisplayGain("Kg", &m_ff[3]);
+  } else if (m_manager->GetAnalysisType() == analysis::kArm) {
+    DisplayGain("Kg", &m_ff[3]);
+
+    double offset;
+    auto unit = m_manager->GetUnit();
+    if (unit == "Radians") {
+      offset = m_ff[4];
+    } else if (unit == "Degrees") {
+      offset = m_ff[4] / std::numbers::pi * 180.0;
+    } else if (unit == "Rotations") {
+      offset = m_ff[4] / (2 * std::numbers::pi);
+    }
+    DisplayGain(
+        fmt::format("Angle offset to horizontal ({})", GetAbbreviation(unit))
+            .c_str(),
+        &offset);
+    CreateTooltip(
+        "This is the angle offset which, when added to the angle measurement, "
+        "zeroes it out when the arm is horizontal. This is needed for the arm "
+        "feedforward to work.");
+  } else if (m_trackWidth) {
+    DisplayGain("Track Width", &*m_trackWidth);
+  }
+  double endY = ImGui::GetCursorPosY();
+
+  DisplayFeedforwardParameters(beginX, beginY);
+  ImGui::SetCursorPosY(endY);
+}
+
+void Analyzer::DisplayFeedbackGains() {
+  // Allow the user to select a feedback controller preset.
+  ImGui::Spacing();
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * kTextBoxWidthMultiple);
+  if (ImGui::Combo("Gain Preset", &m_selectedPreset, kPresetNames,
+                   IM_ARRAYSIZE(kPresetNames))) {
+    m_settings.preset = m_presets[kPresetNames[m_selectedPreset]];
+    m_settings.type = FeedbackControllerLoopType::kVelocity;
+    m_selectedLoopType =
+        static_cast<int>(FeedbackControllerLoopType::kVelocity);
+    m_settings.convertGainsToEncTicks = m_selectedPreset > 2;
+    UpdateFeedbackGains();
+  }
+  ImGui::SameLine();
+  sysid::CreateTooltip(
+      "Gain presets represent how feedback gains are calculated for your "
+      "specific feedback controller:\n\n"
+      "Default, WPILib (2020-): For use with the new WPILib PIDController "
+      "class.\n"
+      "WPILib (Pre-2020): For use with the old WPILib PIDController class.\n"
+      "CTRE: For use with CTRE units. These are the default units that ship "
+      "with CTRE motor controllers.\n"
+      "REV (Brushless): For use with NEO and NEO 550 motors on a SPARK MAX.\n"
+      "REV (Brushed): For use with brushed motors connected to a SPARK MAX.");
+
+  if (m_settings.preset != m_presets[kPresetNames[m_selectedPreset]]) {
+    ImGui::SameLine();
+    ImGui::TextDisabled("(modified)");
+  }
+
+  // Show our feedback controller preset values.
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
+  double value = m_settings.preset.outputConversionFactor * 12;
+  if (ImGui::InputDouble("Max Controller Output", &value, 0.0, 0.0, "%.1f") &&
+      value > 0) {
+    m_settings.preset.outputConversionFactor = value / 12.0;
+    UpdateFeedbackGains();
+  }
+
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
+  value = m_settings.preset.outputVelocityTimeFactor;
+  if (ImGui::InputDouble("Velocity Denominator Units (s)", &value, 0.0, 0.0,
+                         "%.1f") &&
+      value > 0) {
+    m_settings.preset.outputVelocityTimeFactor = value;
+    UpdateFeedbackGains();
+  }
+
+  sysid::CreateTooltip(
+      "This represents the denominator of the velocity unit used by the "
+      "feedback controller. For example, CTRE uses 100 ms = 0.1 s.");
+
+  auto ShowPresetValue = [](const char* text, double* data,
+                            float cursorX = 0.0f) {
+    if (cursorX > 0) {
+      ImGui::SetCursorPosX(cursorX);
+    }
+
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
+    return ImGui::InputDouble(text, data, 0.0, 0.0, "%.5G");
+  };
+
+  // Show controller period.
+  if (ShowPresetValue("Controller Period (ms)",
+                      reinterpret_cast<double*>(&m_settings.preset.period))) {
+    if (m_settings.preset.period > 0_s &&
+        m_settings.preset.measurementDelay >= 0_s) {
+      UpdateFeedbackGains();
+    }
+  }
+
+  // Show whether the controller gains are time-normalized.
+  if (ImGui::Checkbox("Time-Normalized?", &m_settings.preset.normalized)) {
+    UpdateFeedbackGains();
+  }
+
+  // Show position/velocity measurement delay.
+  if (ShowPresetValue(
+          "Measurement Delay (ms)",
+          reinterpret_cast<double*>(&m_settings.preset.measurementDelay))) {
+    if (m_settings.preset.period > 0_s &&
+        m_settings.preset.measurementDelay >= 0_s) {
+      UpdateFeedbackGains();
+    }
+  }
+
+  sysid::CreateTooltip(
+      "The average measurement delay of the process variable in milliseconds. "
+      "This may depend on your encoder settings and choice of motor "
+      "controller. Default velocity filtering windows are quite long "
+      "on many motor controllers, so be careful that this value is "
+      "accurate if the characteristic timescale of the mechanism "
+      "is small.");
+
+  // Add CPR and Gearing for converting Feedback Gains
+  ImGui::Separator();
+  ImGui::Spacing();
+
+  if (ImGui::Checkbox("Convert Gains to Encoder Counts",
+                      &m_settings.convertGainsToEncTicks)) {
+    UpdateFeedbackGains();
+  }
+  sysid::CreateTooltip(
+      "Whether the feedback gains should be in terms of encoder counts or "
+      "output units. Because smart motor controllers usually don't have "
+      "direct access to the output units (i.e. m/s for a drivetrain), they "
+      "perform feedback on the encoder counts directly. If you are using a "
+      "PID Controller on the RoboRIO, you are probably performing feedback "
+      "on the output units directly.\n\nNote that if you have properly set "
+      "up position and velocity conversion factors with the SPARK MAX, you "
+      "can leave this box unchecked. The motor controller will perform "
+      "feedback on the output directly.");
+
+  if (m_settings.convertGainsToEncTicks) {
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5);
+    if (ImGui::InputDouble("##Numerator", &m_gearingNumerator, 0.0, 0.0, "%.4f",
+                           ImGuiInputTextFlags_EnterReturnsTrue) &&
+        m_gearingNumerator > 0) {
+      m_settings.gearing = m_gearingNumerator / m_gearingDenominator;
+      UpdateFeedbackGains();
+    }
+    ImGui::SameLine();
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5);
+    if (ImGui::InputDouble("##Denominator", &m_gearingDenominator, 0.0, 0.0,
+                           "%.4f", ImGuiInputTextFlags_EnterReturnsTrue) &&
+        m_gearingDenominator > 0) {
+      m_settings.gearing = m_gearingNumerator / m_gearingDenominator;
+      UpdateFeedbackGains();
+    }
+    sysid::CreateTooltip(
+        "The gearing between the encoder and the motor shaft (# of encoder "
+        "turns / # of motor shaft turns).");
+
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5);
+    if (ImGui::InputInt("CPR", &m_settings.cpr, 0, 0,
+                        ImGuiInputTextFlags_EnterReturnsTrue) &&
+        m_settings.cpr > 0) {
+      UpdateFeedbackGains();
+    }
+    sysid::CreateTooltip(
+        "The counts per rotation of your encoder. This is the number of counts "
+        "reported in user code when the encoder is rotated exactly once. Some "
+        "common values for various motors/encoders are:\n\n"
+        "Falcon 500: 2048\nNEO: 1\nCTRE Mag Encoder / CANCoder: 4096\nREV "
+        "Through Bore Encoder: 8192\n");
+  }
+
+  ImGui::Separator();
+  ImGui::Spacing();
+
+  // Allow the user to select a loop type.
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * kTextBoxWidthMultiple);
+  if (ImGui::Combo("Loop Type", &m_selectedLoopType, kLoopTypes,
+                   IM_ARRAYSIZE(kLoopTypes))) {
+    m_settings.type =
+        static_cast<FeedbackControllerLoopType>(m_selectedLoopType);
+    if (m_state == AnalyzerState::kWaitingForJSON) {
+      m_settings.preset.measurementDelay = 0_ms;
+    } else {
+      if (m_settings.type == FeedbackControllerLoopType::kPosition) {
+        m_settings.preset.measurementDelay = m_manager->GetPositionDelay();
+      } else {
+        m_settings.preset.measurementDelay = m_manager->GetVelocityDelay();
+      }
+    }
+    UpdateFeedbackGains();
+  }
+
+  ImGui::Spacing();
+
+  // Show Kp and Kd.
+  float beginY = ImGui::GetCursorPosY();
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
+  DisplayGain("Kp", &m_Kp);
+
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
+  DisplayGain("Kd", &m_Kd);
+
+  // Come back to the starting y pos.
+  ImGui::SetCursorPosY(beginY);
+
+  if (m_selectedLoopType == 0) {
+    std::string unit;
+    if (m_state != AnalyzerState::kWaitingForJSON) {
+      unit = fmt::format(" ({})", GetAbbreviation(m_manager->GetUnit()));
+    }
+
+    ImGui::SetCursorPosX(ImGui::GetFontSize() * 9);
+    if (DisplayGain(fmt::format("Max Position Error{}", unit).c_str(),
+                    &m_settings.lqr.qp, false)) {
+      if (m_settings.lqr.qp > 0) {
+        UpdateFeedbackGains();
+      }
+    }
+  }
+
+  std::string unit;
+  if (m_state != AnalyzerState::kWaitingForJSON) {
+    unit = fmt::format(" ({}/s)", GetAbbreviation(m_manager->GetUnit()));
+  }
+
+  ImGui::SetCursorPosX(ImGui::GetFontSize() * 9);
+  if (DisplayGain(fmt::format("Max Velocity Error{}", unit).c_str(),
+                  &m_settings.lqr.qv, false)) {
+    if (m_settings.lqr.qv > 0) {
+      UpdateFeedbackGains();
+    }
+  }
+  ImGui::SetCursorPosX(ImGui::GetFontSize() * 9);
+  if (DisplayGain("Max Control Effort (V)", &m_settings.lqr.r, false)) {
+    if (m_settings.lqr.r > 0) {
+      UpdateFeedbackGains();
+    }
+  }
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/view/AnalyzerPlot.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/view/AnalyzerPlot.cpp
new file mode 100644
index 0000000..d8afbaf
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/view/AnalyzerPlot.cpp
@@ -0,0 +1,531 @@
+// 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 "sysid/view/AnalyzerPlot.h"
+
+#include <algorithm>
+#include <cmath>
+#include <mutex>
+
+#include <fmt/format.h>
+#include <units/math.h>
+
+#include "sysid/Util.h"
+#include "sysid/analysis/AnalysisManager.h"
+#include "sysid/analysis/ArmSim.h"
+#include "sysid/analysis/ElevatorSim.h"
+#include "sysid/analysis/FilteringUtils.h"
+#include "sysid/analysis/SimpleMotorSim.h"
+
+using namespace sysid;
+
+static ImPlotPoint Getter(int idx, void* data) {
+  return static_cast<ImPlotPoint*>(data)[idx];
+}
+
+template <typename Model>
+static std::vector<std::vector<ImPlotPoint>> PopulateTimeDomainSim(
+    const std::vector<PreparedData>& data,
+    const std::array<units::second_t, 4>& startTimes, size_t step, Model model,
+    double* simSquaredErrorSum, double* squaredVariationSum,
+    int* timeSeriesPoints) {
+  // Create the vector of ImPlotPoints that will contain our simulated data.
+  std::vector<std::vector<ImPlotPoint>> pts;
+  std::vector<ImPlotPoint> tmp;
+
+  auto startTime = data[0].timestamp;
+
+  tmp.emplace_back(startTime.value(), data[0].velocity);
+
+  model.Reset(data[0].position, data[0].velocity);
+  units::second_t t = 0_s;
+
+  for (size_t i = 1; i < data.size(); ++i) {
+    const auto& now = data[i];
+    const auto& pre = data[i - 1];
+
+    t += now.timestamp - pre.timestamp;
+
+    // If the current time stamp and previous time stamp are across a test's
+    // start timestamp, it is the start of a new test and the model needs to be
+    // reset.
+    if (std::find(startTimes.begin(), startTimes.end(), now.timestamp) !=
+        startTimes.end()) {
+      pts.emplace_back(std::move(tmp));
+      model.Reset(now.position, now.velocity);
+      continue;
+    }
+
+    model.Update(units::volt_t{pre.voltage}, now.timestamp - pre.timestamp);
+    tmp.emplace_back((startTime + t).value(), model.GetVelocity());
+    *simSquaredErrorSum += std::pow(now.velocity - model.GetVelocity(), 2);
+    *squaredVariationSum += std::pow(now.velocity, 2);
+    ++(*timeSeriesPoints);
+  }
+
+  pts.emplace_back(std::move(tmp));
+  return pts;
+}
+
+AnalyzerPlot::AnalyzerPlot(wpi::Logger& logger) : m_logger(logger) {}
+
+void AnalyzerPlot::SetRawTimeData(const std::vector<PreparedData>& rawSlow,
+                                  const std::vector<PreparedData>& rawFast,
+                                  std::atomic<bool>& abort) {
+  auto rawSlowStep = std::ceil(rawSlow.size() * 1.0 / kMaxSize * 4);
+  auto rawFastStep = std::ceil(rawFast.size() * 1.0 / kMaxSize * 4);
+  // Populate Raw Slow Time Series Data
+  for (size_t i = 0; i < rawSlow.size(); i += rawSlowStep) {
+    if (abort) {
+      return;
+    }
+    m_quasistaticData.rawData.emplace_back((rawSlow[i].timestamp).value(),
+                                           rawSlow[i].velocity);
+  }
+
+  // Populate Raw fast Time Series Data
+  for (size_t i = 0; i < rawFast.size(); i += rawFastStep) {
+    if (abort) {
+      return;
+    }
+    m_dynamicData.rawData.emplace_back((rawFast[i].timestamp).value(),
+                                       rawFast[i].velocity);
+  }
+}
+
+void AnalyzerPlot::ResetData() {
+  m_quasistaticData.Clear();
+  m_dynamicData.Clear();
+  m_regressionData.Clear();
+  m_timestepData.Clear();
+
+  FitPlots();
+}
+
+void AnalyzerPlot::SetGraphLabels(std::string_view unit) {
+  std::string_view abbreviation = GetAbbreviation(unit);
+  m_velocityLabel = fmt::format("Velocity ({}/s)", abbreviation);
+  m_accelerationLabel = fmt::format("Acceleration ({}/s²)", abbreviation);
+  m_velPortionAccelLabel =
+      fmt::format("Velocity-Portion Accel ({}/s²)", abbreviation);
+}
+
+void AnalyzerPlot::SetRawData(const Storage& data, std::string_view unit,
+                              std::atomic<bool>& abort) {
+  const auto& [slowForward, slowBackward, fastForward, fastBackward] = data;
+  const auto& slow = m_direction == 0 ? slowForward : slowBackward;
+  const auto& fast = m_direction == 0 ? fastForward : fastBackward;
+
+  SetGraphLabels(unit);
+
+  std::scoped_lock lock(m_mutex);
+
+  ResetData();
+  SetRawTimeData(slow, fast, abort);
+}
+
+void AnalyzerPlot::SetData(const Storage& rawData, const Storage& filteredData,
+                           std::string_view unit,
+                           const std::vector<double>& ffGains,
+                           const std::array<units::second_t, 4>& startTimes,
+                           AnalysisType type, std::atomic<bool>& abort) {
+  double simSquaredErrorSum = 0;
+  double squaredVariationSum = 0;
+  int timeSeriesPoints = 0;
+
+  const auto& Ks = ffGains[0];
+  const auto& Kv = ffGains[1];
+  const auto& Ka = ffGains[2];
+
+  auto& [slowForward, slowBackward, fastForward, fastBackward] = filteredData;
+  auto& [rawSlowForward, rawSlowBackward, rawFastForward, rawFastBackward] =
+      rawData;
+
+  const auto slow = AnalysisManager::DataConcat(slowForward, slowBackward);
+  const auto fast = AnalysisManager::DataConcat(fastForward, fastBackward);
+  const auto rawSlow =
+      AnalysisManager::DataConcat(rawSlowForward, rawSlowBackward);
+  const auto rawFast =
+      AnalysisManager::DataConcat(rawFastForward, rawFastBackward);
+
+  SetGraphLabels(unit);
+
+  std::scoped_lock lock(m_mutex);
+
+  ResetData();
+
+  // Calculate step sizes to ensure that we only use the memory that we
+  // allocated.
+  auto slowStep = std::ceil(slow.size() * 1.0 / kMaxSize * 4);
+  auto fastStep = std::ceil(fast.size() * 1.0 / kMaxSize * 4);
+
+  units::second_t dtMean = GetMeanTimeDelta(filteredData);
+
+  // Velocity-vs-time plots
+  {
+    const auto& slow = m_direction == 0 ? slowForward : slowBackward;
+    const auto& fast = m_direction == 0 ? fastForward : fastBackward;
+    const auto& rawSlow = m_direction == 0 ? rawSlowForward : rawSlowBackward;
+    const auto& rawFast = m_direction == 0 ? rawFastForward : rawFastBackward;
+
+    // Populate quasistatic time-domain graphs
+    for (size_t i = 0; i < slow.size(); i += slowStep) {
+      if (abort) {
+        return;
+      }
+
+      m_quasistaticData.filteredData.emplace_back((slow[i].timestamp).value(),
+                                                  slow[i].velocity);
+
+      if (i > 0) {
+        // If the current timestamp is not in the startTimes array, it is the
+        // during a test and should be included. If it is in the startTimes
+        // array, it is the beginning of a new test and the dt will be inflated.
+        // Therefore we skip those to exclude that dt and effectively reset dt
+        // calculations.
+        if (slow[i].dt > 0_s &&
+            std::find(startTimes.begin(), startTimes.end(),
+                      slow[i].timestamp) == startTimes.end()) {
+          m_timestepData.data.emplace_back(
+              (slow[i].timestamp).value(),
+              units::millisecond_t{slow[i].dt}.value());
+        }
+      }
+    }
+
+    // Populate dynamic time-domain graphs
+    for (size_t i = 0; i < fast.size(); i += fastStep) {
+      if (abort) {
+        return;
+      }
+
+      m_dynamicData.filteredData.emplace_back((fast[i].timestamp).value(),
+                                              fast[i].velocity);
+
+      if (i > 0) {
+        // If the current timestamp is not in the startTimes array, it is the
+        // during a test and should be included. If it is in the startTimes
+        // array, it is the beginning of a new test and the dt will be inflated.
+        // Therefore we skip those to exclude that dt and effectively reset dt
+        // calculations.
+        if (fast[i].dt > 0_s &&
+            std::find(startTimes.begin(), startTimes.end(),
+                      fast[i].timestamp) == startTimes.end()) {
+          m_timestepData.data.emplace_back(
+              (fast[i].timestamp).value(),
+              units::millisecond_t{fast[i].dt}.value());
+        }
+      }
+    }
+
+    SetRawTimeData(rawSlow, rawFast, abort);
+
+    // Populate simulated time domain data
+    if (type == analysis::kElevator) {
+      const auto& Kg = ffGains[3];
+      m_quasistaticData.simData = PopulateTimeDomainSim(
+          rawSlow, startTimes, fastStep, sysid::ElevatorSim{Ks, Kv, Ka, Kg},
+          &simSquaredErrorSum, &squaredVariationSum, &timeSeriesPoints);
+      m_dynamicData.simData = PopulateTimeDomainSim(
+          rawFast, startTimes, fastStep, sysid::ElevatorSim{Ks, Kv, Ka, Kg},
+          &simSquaredErrorSum, &squaredVariationSum, &timeSeriesPoints);
+    } else if (type == analysis::kArm) {
+      const auto& Kg = ffGains[3];
+      const auto& offset = ffGains[4];
+      m_quasistaticData.simData = PopulateTimeDomainSim(
+          rawSlow, startTimes, fastStep, sysid::ArmSim{Ks, Kv, Ka, Kg, offset},
+          &simSquaredErrorSum, &squaredVariationSum, &timeSeriesPoints);
+      m_dynamicData.simData = PopulateTimeDomainSim(
+          rawFast, startTimes, fastStep, sysid::ArmSim{Ks, Kv, Ka, Kg, offset},
+          &simSquaredErrorSum, &squaredVariationSum, &timeSeriesPoints);
+    } else {
+      m_quasistaticData.simData = PopulateTimeDomainSim(
+          rawSlow, startTimes, fastStep, sysid::SimpleMotorSim{Ks, Kv, Ka},
+          &simSquaredErrorSum, &squaredVariationSum, &timeSeriesPoints);
+      m_dynamicData.simData = PopulateTimeDomainSim(
+          rawFast, startTimes, fastStep, sysid::SimpleMotorSim{Ks, Kv, Ka},
+          &simSquaredErrorSum, &squaredVariationSum, &timeSeriesPoints);
+    }
+  }
+
+  // Acceleration-vs-velocity plot
+
+  // Find minimum velocity of slow and fast datasets, then find point for line
+  // of best fit
+  auto slowMinVel =
+      std::min_element(slow.cbegin(), slow.cend(), [](auto& a, auto& b) {
+        return a.velocity < b.velocity;
+      })->velocity;
+  auto fastMinVel =
+      std::min_element(fast.cbegin(), fast.cend(), [](auto& a, auto& b) {
+        return a.velocity < b.velocity;
+      })->velocity;
+  auto minVel = std::min(slowMinVel, fastMinVel);
+  m_regressionData.fitLine[0] = ImPlotPoint{minVel, -Kv / Ka * minVel};
+
+  // Find maximum velocity of slow and fast datasets, then find point for line
+  // of best fit
+  auto slowMaxVel =
+      std::max_element(slow.cbegin(), slow.cend(), [](auto& a, auto& b) {
+        return a.velocity < b.velocity;
+      })->velocity;
+  auto fastMaxVel =
+      std::max_element(fast.cbegin(), fast.cend(), [](auto& a, auto& b) {
+        return a.velocity < b.velocity;
+      })->velocity;
+  auto maxVel = std::max(slowMaxVel, fastMaxVel);
+  m_regressionData.fitLine[1] = ImPlotPoint{maxVel, -Kv / Ka * maxVel};
+
+  // Populate acceleration vs velocity graph
+  for (size_t i = 0; i < slow.size(); i += slowStep) {
+    if (abort) {
+      return;
+    }
+
+    // Calculate portion of acceleration caused by back-EMF
+    double accelPortion = slow[i].acceleration - 1.0 / Ka * slow[i].voltage +
+                          std::copysign(Ks / Ka, slow[i].velocity);
+
+    if (type == analysis::kElevator) {
+      const auto& Kg = ffGains[3];
+      accelPortion -= Kg / Ka;
+    } else if (type == analysis::kArm) {
+      const auto& Kg = ffGains[3];
+      accelPortion -= Kg / Ka * slow[i].cos;
+    }
+
+    m_regressionData.data.emplace_back(slow[i].velocity, accelPortion);
+  }
+  for (size_t i = 0; i < fast.size(); i += fastStep) {
+    if (abort) {
+      return;
+    }
+
+    // Calculate portion of voltage that corresponds to change in acceleration.
+    double accelPortion = fast[i].acceleration - 1.0 / Ka * fast[i].voltage +
+                          std::copysign(Ks / Ka, fast[i].velocity);
+
+    if (type == analysis::kElevator) {
+      const auto& Kg = ffGains[3];
+      accelPortion -= Kg / Ka;
+    } else if (type == analysis::kArm) {
+      const auto& Kg = ffGains[3];
+      accelPortion -= Kg / Ka * fast[i].cos;
+    }
+
+    m_regressionData.data.emplace_back(fast[i].velocity, accelPortion);
+  }
+
+  // Timestep-vs-time plot
+
+  for (size_t i = 0; i < slow.size(); i += slowStep) {
+    if (i > 0) {
+      // If the current timestamp is not in the startTimes array, it is the
+      // during a test and should be included. If it is in the startTimes
+      // array, it is the beginning of a new test and the dt will be inflated.
+      // Therefore we skip those to exclude that dt and effectively reset dt
+      // calculations.
+      if (slow[i].dt > 0_s &&
+          std::find(startTimes.begin(), startTimes.end(), slow[i].timestamp) ==
+              startTimes.end()) {
+        m_timestepData.data.emplace_back(
+            (slow[i].timestamp).value(),
+            units::millisecond_t{slow[i].dt}.value());
+      }
+    }
+  }
+
+  for (size_t i = 0; i < fast.size(); i += fastStep) {
+    if (i > 0) {
+      // If the current timestamp is not in the startTimes array, it is the
+      // during a test and should be included. If it is in the startTimes
+      // array, it is the beginning of a new test and the dt will be inflated.
+      // Therefore we skip those to exclude that dt and effectively reset dt
+      // calculations.
+      if (fast[i].dt > 0_s &&
+          std::find(startTimes.begin(), startTimes.end(), fast[i].timestamp) ==
+              startTimes.end()) {
+        m_timestepData.data.emplace_back(
+            (fast[i].timestamp).value(),
+            units::millisecond_t{fast[i].dt}.value());
+      }
+    }
+  }
+
+  auto minTime =
+      units::math::min(slow.front().timestamp, fast.front().timestamp);
+  m_timestepData.fitLine[0] =
+      ImPlotPoint{minTime.value(), units::millisecond_t{dtMean}.value()};
+
+  auto maxTime = units::math::max(slow.back().timestamp, fast.back().timestamp);
+  m_timestepData.fitLine[1] =
+      ImPlotPoint{maxTime.value(), units::millisecond_t{dtMean}.value()};
+
+  // RMSE = std::sqrt(sum((x_i - x^_i)^2) / N) where sum represents the sum of
+  // all time series points, x_i represents the velocity at a timestep, x^_i
+  // represents the prediction at the timestep, and N represents the number of
+  // points
+  m_RMSE = std::sqrt(simSquaredErrorSum / timeSeriesPoints);
+  m_accelRSquared =
+      1 - m_RMSE / std::sqrt(squaredVariationSum / timeSeriesPoints);
+  FitPlots();
+}
+
+void AnalyzerPlot::FitPlots() {
+  // Set the "fit" flag to true.
+  m_quasistaticData.fitNextPlot = true;
+  m_dynamicData.fitNextPlot = true;
+  m_regressionData.fitNextPlot = true;
+  m_timestepData.fitNextPlot = true;
+}
+
+double* AnalyzerPlot::GetSimRMSE() {
+  return &m_RMSE;
+}
+
+double* AnalyzerPlot::GetSimRSquared() {
+  return &m_accelRSquared;
+}
+
+static void PlotSimData(std::vector<std::vector<ImPlotPoint>>& data) {
+  for (auto&& pts : data) {
+    ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL, 1.5);
+    ImPlot::PlotLineG("Simulation", Getter, pts.data(), pts.size());
+  }
+}
+
+bool AnalyzerPlot::DisplayPlots() {
+  std::unique_lock lock(m_mutex, std::defer_lock);
+
+  if (!lock.try_lock()) {
+    ImGui::Text("Loading %c",
+                "|/-\\"[static_cast<int>(ImGui::GetTime() / 0.05f) & 3]);
+    return false;
+  }
+
+  ImVec2 plotSize = ImGui::GetContentRegionAvail();
+
+  // Fit two plots horizontally
+  plotSize.x = (plotSize.x - ImGui::GetStyle().ItemSpacing.x) / 2.f;
+
+  // Fit two plots vertically while leaving room for three text boxes
+  const float textBoxHeight = ImGui::GetFontSize() * 1.75;
+  plotSize.y =
+      (plotSize.y - textBoxHeight * 3 - ImGui::GetStyle().ItemSpacing.y) / 2.f;
+
+  m_quasistaticData.Plot("Quasistatic Velocity vs. Time", plotSize,
+                         m_velocityLabel.c_str(), m_pointSize);
+  ImGui::SameLine();
+  m_dynamicData.Plot("Dynamic Velocity vs. Time", plotSize,
+                     m_velocityLabel.c_str(), m_pointSize);
+
+  m_regressionData.Plot("Acceleration vs. Velocity", plotSize,
+                        m_velocityLabel.c_str(), m_velPortionAccelLabel.c_str(),
+                        true, true, m_pointSize);
+  ImGui::SameLine();
+  m_timestepData.Plot("Timesteps vs. Time", plotSize, "Time (s)",
+                      "Timestep duration (ms)", true, false, m_pointSize,
+                      [] { ImPlot::SetupAxisLimits(ImAxis_Y1, 0, 50); });
+
+  return true;
+}
+
+AnalyzerPlot::FilteredDataVsTimePlot::FilteredDataVsTimePlot() {
+  rawData.reserve(kMaxSize);
+  filteredData.reserve(kMaxSize);
+  simData.reserve(kMaxSize);
+}
+
+void AnalyzerPlot::FilteredDataVsTimePlot::Plot(const char* title,
+                                                const ImVec2& size,
+                                                const char* yLabel,
+                                                float pointSize) {
+  // Generate Sim vs Filtered Plot
+  if (fitNextPlot) {
+    ImPlot::SetNextAxesToFit();
+  }
+
+  if (ImPlot::BeginPlot(title, size)) {
+    ImPlot::SetupAxis(ImAxis_X1, "Time (s)", ImPlotAxisFlags_NoGridLines);
+    ImPlot::SetupAxis(ImAxis_Y1, yLabel, ImPlotAxisFlags_NoGridLines);
+    ImPlot::SetupLegend(ImPlotLocation_NorthEast);
+
+    // Plot Raw Data
+    ImPlot::SetNextMarkerStyle(IMPLOT_AUTO, 1, IMPLOT_AUTO_COL, 0);
+    ImPlot::SetNextMarkerStyle(ImPlotStyleVar_MarkerSize, pointSize);
+    ImPlot::PlotScatterG("Raw Data", Getter, rawData.data(), rawData.size());
+
+    // Plot Filtered Data after Raw data
+    ImPlot::SetNextMarkerStyle(IMPLOT_AUTO, 1, IMPLOT_AUTO_COL, 0);
+    ImPlot::SetNextMarkerStyle(ImPlotStyleVar_MarkerSize, pointSize);
+    ImPlot::PlotScatterG("Filtered Data", Getter, filteredData.data(),
+                         filteredData.size());
+
+    // Plot Simulation Data for Velocity Data
+    PlotSimData(simData);
+
+    // Disable constant resizing
+    if (fitNextPlot) {
+      fitNextPlot = false;
+    }
+
+    ImPlot::EndPlot();
+  }
+}
+
+void AnalyzerPlot::FilteredDataVsTimePlot::Clear() {
+  rawData.clear();
+  filteredData.clear();
+  simData.clear();
+}
+
+AnalyzerPlot::DataWithFitLinePlot::DataWithFitLinePlot() {
+  data.reserve(kMaxSize);
+}
+
+void AnalyzerPlot::DataWithFitLinePlot::Plot(const char* title,
+                                             const ImVec2& size,
+                                             const char* xLabel,
+                                             const char* yLabel, bool fitX,
+                                             bool fitY, float pointSize,
+                                             std::function<void()> setup) {
+  if (fitNextPlot) {
+    if (fitX && fitY) {
+      ImPlot::SetNextAxesToFit();
+    } else if (fitX && !fitY) {
+      ImPlot::SetNextAxisToFit(ImAxis_X1);
+    } else if (!fitX && fitY) {
+      ImPlot::SetNextAxisToFit(ImAxis_Y1);
+    }
+  }
+
+  if (ImPlot::BeginPlot(title, size)) {
+    setup();
+    ImPlot::SetupAxis(ImAxis_X1, xLabel, ImPlotAxisFlags_NoGridLines);
+    ImPlot::SetupAxis(ImAxis_Y1, yLabel, ImPlotAxisFlags_NoGridLines);
+    ImPlot::SetupLegend(ImPlotLocation_NorthEast);
+
+    // Get a reference to the data that we are plotting.
+    ImPlot::SetNextMarkerStyle(IMPLOT_AUTO, 1, IMPLOT_AUTO_COL, 0);
+    ImPlot::SetNextMarkerStyle(ImPlotStyleVar_MarkerSize, pointSize);
+    ImPlot::PlotScatterG("Filtered Data", Getter, data.data(), data.size());
+
+    ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL, 1.5);
+    ImPlot::PlotLineG("Fit", Getter, fitLine.data(), fitLine.size());
+
+    ImPlot::EndPlot();
+
+    if (fitNextPlot) {
+      fitNextPlot = false;
+    }
+  }
+}
+
+void AnalyzerPlot::DataWithFitLinePlot::Clear() {
+  data.clear();
+
+  // Reset line of best fit
+  fitLine[0] = ImPlotPoint{0, 0};
+  fitLine[1] = ImPlotPoint{0, 0};
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/view/JSONConverter.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/view/JSONConverter.cpp
new file mode 100644
index 0000000..88eaa6a
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/view/JSONConverter.cpp
@@ -0,0 +1,64 @@
+// 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 "sysid/analysis/JSONConverter.h"
+#include "sysid/view/JSONConverter.h"
+
+#include <exception>
+
+#include <imgui.h>
+#include <portable-file-dialogs.h>
+#include <wpi/timestamp.h>
+
+#include "sysid/Util.h"
+
+using namespace sysid;
+
+void JSONConverter::DisplayConverter(
+    const char* tooltip,
+    std::function<std::string(std::string_view, wpi::Logger&)> converter) {
+  if (ImGui::Button(tooltip)) {
+    m_opener = std::make_unique<pfd::open_file>(
+        tooltip, "", std::vector<std::string>{"JSON File", SYSID_PFD_JSON_EXT});
+  }
+
+  if (m_opener && m_opener->ready()) {
+    if (!m_opener->result().empty()) {
+      m_location = m_opener->result()[0];
+      try {
+        converter(m_location, m_logger);
+        m_timestamp = wpi::Now() * 1E-6;
+      } catch (const std::exception& e) {
+        ImGui::OpenPopup("Exception Caught!");
+        m_exception = e.what();
+      }
+    }
+    m_opener.reset();
+  }
+
+  if (wpi::Now() * 1E-6 - m_timestamp < 5) {
+    ImGui::SameLine();
+    ImGui::Text("Saved!");
+  }
+
+  // Handle exceptions.
+  ImGui::SetNextWindowSize(ImVec2(480.f, 0.0f));
+  if (ImGui::BeginPopupModal("Exception Caught!")) {
+    ImGui::PushTextWrapPos(0.0f);
+    ImGui::Text(
+        "An error occurred when parsing the JSON. This most likely means that "
+        "the JSON data is incorrectly formatted.");
+    ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%s",
+                       m_exception.c_str());
+    ImGui::PopTextWrapPos();
+    if (ImGui::Button("Close")) {
+      ImGui::CloseCurrentPopup();
+    }
+    ImGui::EndPopup();
+  }
+}
+
+void JSONConverter::DisplayCSVConvert() {
+  DisplayConverter("Select SysId JSON", sysid::ToCSV);
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/cpp/view/Logger.cpp b/third_party/allwpilib/sysid/src/main/native/cpp/view/Logger.cpp
new file mode 100644
index 0000000..5e7773d
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/cpp/view/Logger.cpp
@@ -0,0 +1,222 @@
+// 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 "sysid/view/Logger.h"
+
+#include <exception>
+#include <numbers>
+
+#include <glass/Context.h>
+#include <glass/Storage.h>
+#include <imgui.h>
+#include <imgui_internal.h>
+#include <imgui_stdlib.h>
+#include <networktables/NetworkTable.h>
+#include <units/angle.h>
+#include <wpigui.h>
+
+#include "sysid/Util.h"
+#include "sysid/analysis/AnalysisType.h"
+#include "sysid/view/UILayout.h"
+
+using namespace sysid;
+
+Logger::Logger(glass::Storage& storage, wpi::Logger& logger)
+    : m_logger{logger}, m_ntSettings{"sysid", storage} {
+  wpi::gui::AddEarlyExecute([&] { m_ntSettings.Update(); });
+
+  m_ntSettings.EnableServerOption(false);
+}
+
+void Logger::Display() {
+  // Get the current width of the window. This will be used to scale
+  // our UI elements.
+  float width = ImGui::GetContentRegionAvail().x;
+
+  // Add team number input and apply button for NT connection.
+  m_ntSettings.Display();
+
+  // Reset and clear the internal manager state.
+  ImGui::SameLine();
+  if (ImGui::Button("Reset Telemetry")) {
+    m_settings = TelemetryManager::Settings{};
+    m_manager = std::make_unique<TelemetryManager>(m_settings, m_logger);
+    m_settings.mechanism = analysis::FromName(kTypes[m_selectedType]);
+  }
+
+  // Add NT connection indicator.
+  static ImVec4 kColorDisconnected{1.0f, 0.4f, 0.4f, 1.0f};
+  static ImVec4 kColorConnected{0.2f, 1.0f, 0.2f, 1.0f};
+  ImGui::SameLine();
+  bool ntConnected = nt::NetworkTableInstance::GetDefault().IsConnected();
+  ImGui::TextColored(ntConnected ? kColorConnected : kColorDisconnected,
+                     ntConnected ? "NT Connected" : "NT Disconnected");
+
+  // Create a Section for project configuration
+  ImGui::Separator();
+  ImGui::Spacing();
+  ImGui::Text("Project Parameters");
+
+  // Add a dropdown for mechanism type.
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * kTextBoxWidthMultiple);
+
+  if (ImGui::Combo("Mechanism", &m_selectedType, kTypes,
+                   IM_ARRAYSIZE(kTypes))) {
+    m_settings.mechanism = analysis::FromName(kTypes[m_selectedType]);
+  }
+
+  // Add Dropdown for Units
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * kTextBoxWidthMultiple);
+  if (ImGui::Combo("Unit Type", &m_selectedUnit, kUnits,
+                   IM_ARRAYSIZE(kUnits))) {
+    m_settings.units = kUnits[m_selectedUnit];
+  }
+
+  sysid::CreateTooltip(
+      "This is the type of units that your gains will be in. For example, if "
+      "you want your flywheel gains in terms of radians, then use the radians "
+      "unit. On the other hand, if your drivetrain will use gains in meters, "
+      "choose meters.");
+
+  // Rotational units have fixed Units per rotations
+  m_isRotationalUnits =
+      (m_settings.units == "Rotations" || m_settings.units == "Degrees" ||
+       m_settings.units == "Radians");
+  if (m_settings.units == "Degrees") {
+    m_settings.unitsPerRotation = 360.0;
+  } else if (m_settings.units == "Radians") {
+    m_settings.unitsPerRotation = 2 * std::numbers::pi;
+  } else if (m_settings.units == "Rotations") {
+    m_settings.unitsPerRotation = 1.0;
+  }
+
+  // Units Per Rotations entry
+  ImGui::SetNextItemWidth(ImGui::GetFontSize() * kTextBoxWidthMultiple);
+  ImGui::InputDouble("Units Per Rotation", &m_settings.unitsPerRotation, 0.0f,
+                     0.0f, "%.4f",
+                     m_isRotationalUnits ? ImGuiInputTextFlags_ReadOnly
+                                         : ImGuiInputTextFlags_None);
+  sysid::CreateTooltip(
+      "The logger assumes that the code will be sending recorded motor shaft "
+      "rotations over NetworkTables. This value will then be multiplied by the "
+      "units per rotation to get the measurement in the units you "
+      "specified.\n\nFor non-rotational units (e.g. meters), this value is "
+      "usually the wheel diameter times pi (should not include gearing).");
+  // Create a section for voltage parameters.
+  ImGui::Separator();
+  ImGui::Spacing();
+  ImGui::Text("Voltage Parameters");
+
+  auto CreateVoltageParameters = [this](const char* text, double* data,
+                                        float min, float max) {
+    ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6);
+    ImGui::PushItemFlag(ImGuiItemFlags_Disabled,
+                        m_manager && m_manager->IsActive());
+    float value = static_cast<float>(*data);
+    if (ImGui::SliderFloat(text, &value, min, max, "%.2f")) {
+      *data = value;
+    }
+    ImGui::PopItemFlag();
+  };
+
+  CreateVoltageParameters("Quasistatic Ramp Rate (V/s)",
+                          &m_settings.quasistaticRampRate, 0.10f, 0.60f);
+  sysid::CreateTooltip(
+      "This is the rate at which the voltage will increase during the "
+      "quasistatic test.");
+
+  CreateVoltageParameters("Dynamic Step Voltage (V)", &m_settings.stepVoltage,
+                          0.0f, 10.0f);
+  sysid::CreateTooltip(
+      "This is the voltage that will be applied for the "
+      "dynamic voltage (acceleration) tests.");
+
+  // Create a section for tests.
+  ImGui::Separator();
+  ImGui::Spacing();
+  ImGui::Text("Tests");
+
+  auto CreateTest = [this, width](const char* text, const char* itext) {
+    // Display buttons if we have an NT connection.
+    if (nt::NetworkTableInstance::GetDefault().IsConnected()) {
+      // Create button to run tests.
+      if (ImGui::Button(text)) {
+        // Open the warning message.
+        ImGui::OpenPopup("Warning");
+        m_manager->BeginTest(itext);
+        m_opened = text;
+      }
+      if (m_opened == text && ImGui::BeginPopupModal("Warning")) {
+        ImGui::TextWrapped("%s", m_popupText.c_str());
+        if (ImGui::Button(m_manager->IsActive() ? "End Test" : "Close")) {
+          m_manager->EndTest();
+          ImGui::CloseCurrentPopup();
+          m_opened = "";
+        }
+        ImGui::EndPopup();
+      }
+    } else {
+      // Show disabled text when there is no connection.
+      ImGui::TextDisabled("%s", text);
+    }
+
+    // Show whether the tests were run or not.
+    bool run = m_manager->HasRunTest(itext);
+    ImGui::SameLine(width * 0.7);
+    ImGui::Text(run ? "Run" : "Not Run");
+  };
+
+  CreateTest("Quasistatic Forward", "slow-forward");
+  CreateTest("Quasistatic Backward", "slow-backward");
+  CreateTest("Dynamic Forward", "fast-forward");
+  CreateTest("Dynamic Backward", "fast-backward");
+
+  m_manager->RegisterDisplayCallback(
+      [this](const auto& str) { m_popupText = str; });
+
+  // Display the path to where the JSON will be saved and a button to select the
+  // location.
+  ImGui::Separator();
+  ImGui::Spacing();
+  ImGui::Text("Save Location");
+  if (ImGui::Button("Choose")) {
+    m_selector = std::make_unique<pfd::select_folder>("Select Folder");
+  }
+  ImGui::SameLine();
+  ImGui::InputText("##savelocation", &m_jsonLocation,
+                   ImGuiInputTextFlags_ReadOnly);
+
+  // Add button to save.
+  ImGui::SameLine(width * 0.9);
+  if (ImGui::Button("Save")) {
+    try {
+      m_manager->SaveJSON(m_jsonLocation);
+    } catch (const std::exception& e) {
+      ImGui::OpenPopup("Exception Caught!");
+      m_exception = e.what();
+    }
+  }
+
+  // Handle exceptions.
+  if (ImGui::BeginPopupModal("Exception Caught!")) {
+    ImGui::Text("%s", m_exception.c_str());
+    if (ImGui::Button("Close")) {
+      ImGui::CloseCurrentPopup();
+    }
+    ImGui::EndPopup();
+  }
+
+  // Run periodic methods.
+  SelectDataFolder();
+  m_ntSettings.Update();
+  m_manager->Update();
+}
+
+void Logger::SelectDataFolder() {
+  // If the selector exists and is ready with a result, we can store it.
+  if (m_selector && m_selector->ready()) {
+    m_jsonLocation = m_selector->result();
+    m_selector.reset();
+  }
+}
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/Util.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/Util.h
new file mode 100644
index 0000000..38cf8b2
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/Util.h
@@ -0,0 +1,89 @@
+// 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 <algorithm>
+#include <array>
+#include <filesystem>
+#include <string>
+#include <string_view>
+
+// The generated AppleScript by portable-file-dialogs for just *.json does not
+// work correctly because it is a uniform type identifier. This means that
+// "public." needs to be prepended to it.
+#ifdef __APPLE__
+#define SYSID_PFD_JSON_EXT "*.public.json"
+#else
+#define SYSID_PFD_JSON_EXT "*.json"
+#endif
+
+#ifdef _WIN32
+#define LAUNCH "gradlew"
+#define LAUNCH_DETACHED "start /b gradlew"
+#define DETACHED_SUFFIX ""
+#else
+#define LAUNCH "./gradlew"
+#define LAUNCH_DETACHED "./gradlew"
+#define DETACHED_SUFFIX "&"
+#endif
+
+// Based on https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html
+#define EXPAND_STRINGIZE(s) STRINGIZE(s)
+#define STRINGIZE(s) #s
+
+namespace sysid {
+static constexpr const char* kUnits[] = {"Meters",  "Feet",      "Inches",
+                                         "Radians", "Rotations", "Degrees"};
+
+/**
+ * Displays a tooltip beside the widget that this method is called after with
+ * the provided text.
+ *
+ * @param text The text to show in the tooltip.
+ */
+void CreateTooltip(const char* text);
+
+/**
+ * Utility function to launch an error popup if an exception is detected.
+ *
+ * @param isError True if an exception is detected
+ * @param errorMessage The error message associated with the exception
+ */
+void CreateErrorPopup(bool& isError, std::string_view errorMessage);
+
+/**
+ * Returns the abbreviation for the unit.
+ *
+ * @param unit The unit to return the abbreviation for.
+ * @return The abbreviation for the unit.
+ */
+std::string_view GetAbbreviation(std::string_view unit);
+
+/**
+ * Saves a file with the provided contents to a specified location.
+ *
+ * @param contents The file contents.
+ * @param path The file location.
+ */
+void SaveFile(std::string_view contents, const std::filesystem::path& path);
+
+/**
+ * Concatenates all the arrays passed as arguments and returns the result.
+ *
+ * @tparam Type The array element type.
+ * @tparam Sizes The array sizes.
+ * @param arrays Parameter pack of arrays to concatenate.
+ */
+template <typename Type, size_t... Sizes>
+constexpr auto ArrayConcat(const std::array<Type, Sizes>&... arrays) {
+  std::array<Type, (Sizes + ...)> result;
+  size_t index = 0;
+
+  ((std::copy_n(arrays.begin(), Sizes, result.begin() + index), index += Sizes),
+   ...);
+
+  return result;
+}
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/AnalysisManager.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/AnalysisManager.h
new file mode 100644
index 0000000..d572578
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/AnalysisManager.h
@@ -0,0 +1,358 @@
+// 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 <algorithm>
+#include <array>
+#include <exception>
+#include <limits>
+#include <numeric>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <vector>
+
+#include <units/time.h>
+#include <wpi/Logger.h>
+#include <wpi/json.h>
+
+#include "sysid/analysis/AnalysisType.h"
+#include "sysid/analysis/FeedbackAnalysis.h"
+#include "sysid/analysis/FeedbackControllerPreset.h"
+#include "sysid/analysis/FeedforwardAnalysis.h"
+#include "sysid/analysis/Storage.h"
+
+namespace sysid {
+
+/**
+ * Manages analysis of data. Each instance of this class represents a JSON file
+ * that is read from storage.
+ */
+class AnalysisManager {
+ public:
+  /**
+   * Represents settings for an instance of the analysis manager. This contains
+   * information about the feedback controller preset, loop type, motion
+   * threshold, acceleration window size, LQR parameters, and the selected
+   * dataset.
+   */
+  struct Settings {
+    enum class DrivetrainDataset { kCombined = 0, kLeft = 1, kRight = 2 };
+    /**
+     * The feedback controller preset used to calculate gains.
+     */
+    FeedbackControllerPreset preset = presets::kDefault;
+
+    /**
+     * The feedback controller loop type (position or velocity).
+     */
+    FeedbackControllerLoopType type = FeedbackControllerLoopType::kVelocity;
+
+    /**
+     * LQR parameters used for feedback gain calculation.
+     */
+    LQRParameters lqr{1, 1.5, 7};
+
+    /**
+     * The motion threshold (units/s) for trimming quasistatic test data.
+     */
+    double motionThreshold = 0.2;
+
+    /**
+     * The window size for the median filter.
+     */
+    int medianWindow = 1;
+
+    /**
+     * The duration of the dynamic test that should be considered. A value of
+     * zero indicates it needs to be set to the default.
+     */
+    units::second_t stepTestDuration = 0_s;
+
+    /**
+     * The conversion factor of counts per revolution.
+     */
+    int cpr = 1440;
+
+    /**
+     * The conversion factor of gearing.
+     */
+    double gearing = 1;
+
+    /**
+     * Whether or not the gains should be in the encoder's units (mainly for use
+     * in a smart motor controller).
+     */
+    bool convertGainsToEncTicks = false;
+
+    DrivetrainDataset dataset = DrivetrainDataset::kCombined;
+  };
+
+  /**
+   * Stores feedforward.
+   */
+  struct FeedforwardGains {
+    /**
+     * Stores the Feedforward gains.
+     */
+    std::tuple<std::vector<double>, double, double> ffGains;
+
+    /**
+     * Stores the trackwidth for angular drivetrain tests.
+     */
+    std::optional<double> trackWidth;
+  };
+
+  /**
+   * Exception for File Reading Errors.
+   */
+  struct FileReadingError : public std::exception {
+    /**
+     * Creates a FileReadingError object
+     *
+     * @param path The path of the file attempted to open
+     */
+    explicit FileReadingError(std::string_view path) {
+      msg = fmt::format("Unable to read: {}", path);
+    }
+
+    /**
+     * The path of the file that was opened.
+     */
+    std::string msg;
+    const char* what() const noexcept override { return msg.c_str(); }
+  };
+
+  /**
+   * The keys (which contain sysid data) that are in the JSON to analyze.
+   */
+  static constexpr const char* kJsonDataKeys[] = {
+      "slow-forward", "slow-backward", "fast-forward", "fast-backward"};
+
+  /**
+   * Concatenates a list of vectors. The contents of the source vectors are
+   * copied (not moved) into the new vector. Also sorts the datapoints by
+   * timestamp to assist with future simulation.
+   *
+   * @param sources The source vectors.
+   * @return The concatenated vector
+   */
+  template <typename... Sources>
+  static std::vector<PreparedData> DataConcat(const Sources&... sources) {
+    std::vector<PreparedData> result;
+    (result.insert(result.end(), sources.begin(), sources.end()), ...);
+
+    // Sort data by timestamp to remove the possibility of negative dts in
+    // future simulations.
+    std::sort(result.begin(), result.end(), [](const auto& a, const auto& b) {
+      return a.timestamp < b.timestamp;
+    });
+
+    return result;
+  }
+
+  /**
+   * Constructs an instance of the analysis manager for theoretical analysis,
+   * containing settings and gains but no data.
+   *
+   * @param settings The settings for this instance of the analysis manager.
+   * @param logger The logger instance to use for log data.
+   */
+  AnalysisManager(Settings& settings, wpi::Logger& logger);
+
+  /**
+   * Constructs an instance of the analysis manager with the given path (to the
+   * JSON) and analysis manager settings.
+   *
+   * @param path     The path to the JSON containing the sysid data.
+   * @param settings The settings for this instance of the analysis manager.
+   * @param logger   The logger instance to use for log data.
+   */
+  AnalysisManager(std::string_view path, Settings& settings,
+                  wpi::Logger& logger);
+
+  /**
+   * Prepares data from the JSON and stores the output in Storage member
+   * variables.
+   */
+  void PrepareData();
+
+  /**
+   * Calculates the gains with the latest data (from the pointers in the
+   * settings struct that this instance was constructed with).
+   *
+   * @return The latest feedforward gains and trackwidth (if needed).
+   */
+  FeedforwardGains CalculateFeedforward();
+
+  /**
+   * Calculates feedback gains from the given feedforward gains.
+   *
+   * @param ff The feedforward gains.
+   * @return The calculated feedback gains.
+   */
+  FeedbackGains CalculateFeedback(std::vector<double> ff);
+
+  /**
+   * Overrides the units in the JSON with the user-provided ones.
+   *
+   * @param unit             The unit to output gains in.
+   * @param unitsPerRotation The conversion factor between rotations and the
+   *                         selected unit.
+   */
+  void OverrideUnits(std::string_view unit, double unitsPerRotation);
+
+  /**
+   * Resets the units back to those defined in the JSON.
+   */
+  void ResetUnitsFromJSON();
+
+  /**
+   * Returns the analysis type of the current instance (read from the JSON).
+   *
+   * @return The analysis type.
+   */
+  const AnalysisType& GetAnalysisType() const { return m_type; }
+
+  /**
+   * Returns the units of analysis.
+   *
+   * @return The units of analysis.
+   */
+  std::string_view GetUnit() const { return m_unit; }
+
+  /**
+   * Returns the factor (a.k.a. units per rotation) for analysis.
+   *
+   * @return The factor (a.k.a. units per rotation) for analysis.
+   */
+  double GetFactor() const { return m_factor; }
+
+  /**
+   * Returns a reference to the iterator of the currently selected raw datset.
+   * Unfortunately, due to ImPlot internals, the reference cannot be const so
+   * the user should be careful not to change any data.
+   *
+   * @return A reference to the raw internal data.
+   */
+  Storage& GetRawData() {
+    return m_rawDataset[static_cast<int>(m_settings.dataset)];
+  }
+
+  /**
+   * Returns a reference to the iterator of the currently selected filtered
+   * datset. Unfortunately, due to ImPlot internals, the reference cannot be
+   * const so the user should be careful not to change any data.
+   *
+   * @return A reference to the filtered internal data.
+   */
+  Storage& GetFilteredData() {
+    return m_filteredDataset[static_cast<int>(m_settings.dataset)];
+  }
+
+  /**
+   * Returns the original dataset.
+   *
+   * @return The original (untouched) dataset
+   */
+  Storage& GetOriginalData() {
+    return m_originalDataset[static_cast<int>(m_settings.dataset)];
+  }
+
+  /**
+   * Returns the minimum duration of the Step Voltage Test of the currently
+   * stored data.
+   *
+   * @return The minimum step test duration.
+   */
+  units::second_t GetMinStepTime() const { return m_minStepTime; }
+
+  /**
+   * Returns the maximum duration of the Step Voltage Test of the currently
+   * stored data.
+   *
+   * @return  Maximum step test duration
+   */
+  units::second_t GetMaxStepTime() const { return m_maxStepTime; }
+
+  /**
+   * Returns the estimated time delay of the measured position, including
+   * CAN delays.
+   *
+   * @return Position delay in milliseconds
+   */
+  units::millisecond_t GetPositionDelay() const {
+    return std::accumulate(m_positionDelays.begin(), m_positionDelays.end(),
+                           0_s) /
+           m_positionDelays.size();
+  }
+
+  /**
+   * Returns the estimated time delay of the measured velocity, including
+   * CAN delays.
+   *
+   * @return Velocity delay in milliseconds
+   */
+  units::millisecond_t GetVelocityDelay() const {
+    return std::accumulate(m_velocityDelays.begin(), m_velocityDelays.end(),
+                           0_s) /
+           m_positionDelays.size();
+  }
+
+  /**
+   * Returns the different start times of the recorded tests.
+   *
+   * @return The start times for each test
+   */
+  const std::array<units::second_t, 4>& GetStartTimes() const {
+    return m_startTimes;
+  }
+
+  bool HasData() const {
+    return !m_originalDataset[static_cast<int>(
+                                  Settings::DrivetrainDataset::kCombined)]
+                .empty();
+  }
+
+ private:
+  wpi::Logger& m_logger;
+
+  // This is used to store the various datasets (i.e. Combined, Forward,
+  // Backward, etc.)
+  wpi::json m_json;
+
+  std::array<Storage, 3> m_originalDataset;
+  std::array<Storage, 3> m_rawDataset;
+  std::array<Storage, 3> m_filteredDataset;
+
+  // Stores the various start times of the different tests.
+  std::array<units::second_t, 4> m_startTimes;
+
+  // The settings for this instance. This contains pointers to the feedback
+  // controller preset, LQR parameters, acceleration window size, etc.
+  Settings& m_settings;
+
+  // Miscellaneous data from the JSON -- the analysis type, the units, and the
+  // units per rotation.
+  AnalysisType m_type;
+  std::string m_unit;
+  double m_factor;
+
+  units::second_t m_minStepTime{0};
+  units::second_t m_maxStepTime{std::numeric_limits<double>::infinity()};
+  std::vector<units::second_t> m_positionDelays;
+  std::vector<units::second_t> m_velocityDelays;
+
+  // Stores an optional track width if we are doing the drivetrain angular test.
+  std::optional<double> m_trackWidth;
+
+  void PrepareGeneralData();
+
+  void PrepareAngularDrivetrainData();
+
+  void PrepareLinearDrivetrainData();
+};
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/AnalysisType.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/AnalysisType.h
new file mode 100644
index 0000000..7feedb3
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/AnalysisType.h
@@ -0,0 +1,63 @@
+// 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 <cstddef>
+#include <string_view>
+
+namespace sysid {
+
+/**
+ * Stores the type of Analysis and relevant information about the analysis.
+ */
+struct AnalysisType {
+  /**
+   * The number of independent variables for feedforward analysis.
+   */
+  size_t independentVariables;
+
+  /**
+   * The number of fields in the raw data within the mechanism's JSON.
+   */
+  size_t rawDataSize;
+
+  /**
+   * Display name for the analysis type.
+   */
+  const char* name;
+
+  /**
+   * Compares equality of two AnalysisType structs.
+   *
+   * @param rhs Another AnalysisType
+   * @return True if the two analysis types are equal
+   */
+  constexpr bool operator==(const AnalysisType& rhs) const {
+    return std::string_view{name} == rhs.name &&
+           independentVariables == rhs.independentVariables &&
+           rawDataSize == rhs.rawDataSize;
+  }
+
+  /**
+   * Compares inequality of two AnalysisType structs.
+   *
+   * @param rhs Another AnalysisType
+   * @return True if the two analysis types are not equal
+   */
+  constexpr bool operator!=(const AnalysisType& rhs) const {
+    return !operator==(rhs);
+  }
+};
+
+namespace analysis {
+constexpr AnalysisType kDrivetrain{3, 9, "Drivetrain"};
+constexpr AnalysisType kDrivetrainAngular{3, 9, "Drivetrain (Angular)"};
+constexpr AnalysisType kElevator{4, 4, "Elevator"};
+constexpr AnalysisType kArm{5, 4, "Arm"};
+constexpr AnalysisType kSimple{3, 4, "Simple"};
+
+AnalysisType FromName(std::string_view name);
+}  // namespace analysis
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/ArmSim.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/ArmSim.h
new file mode 100644
index 0000000..af6c10e
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/ArmSim.h
@@ -0,0 +1,79 @@
+// 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 <Eigen/Core>
+#include <units/time.h>
+#include <units/voltage.h>
+
+namespace sysid {
+/**
+ * Simulation of an Arm mechanism based off of a model from SysId Feedforward
+ * gains.
+ */
+class ArmSim {
+ public:
+  /**
+   * @param Ks              Static friction gain.
+   * @param Kv              Velocity gain.
+   * @param Ka              Acceleration gain.
+   * @param Kg              Gravity cosine gain.
+   * @param offset          Arm position offset.
+   * @param initialPosition Initial arm position.
+   * @param initialVelocity Initial arm velocity.
+   */
+  ArmSim(double Ks, double Kv, double Ka, double Kg, double offset = 0.0,
+         double initialPosition = 0.0, double initialVelocity = 0.0);
+
+  /**
+   * Simulates affine state-space system:
+   * dx/dt = Ax + Bu + c sgn(x) + d cos(theta)
+   * forward dt seconds.
+   *
+   * @param voltage Voltage to apply over the timestep.
+   * @param dt      Sammple period.
+   */
+  void Update(units::volt_t voltage, units::second_t dt);
+
+  /**
+   * Returns the position.
+   *
+   * @return The current position
+   */
+  double GetPosition() const;
+
+  /**
+   * Returns the velocity.
+   *
+   * @return The current velocity
+   */
+  double GetVelocity() const;
+
+  /**
+   * Returns the acceleration for the current state and given input.
+   *
+   * @param voltage The voltage that is being applied to the mechanism / input
+   * @return The acceleration given the state and input
+   */
+  double GetAcceleration(units::volt_t voltage) const;
+
+  /**
+   * Resets model position and velocity.
+   *
+   * @param position The position the mechanism should be reset to
+   * @param velocity The velocity the mechanism should be reset to
+   */
+  void Reset(double position = 0.0, double velocity = 0.0);
+
+ private:
+  Eigen::Matrix<double, 1, 1> m_A;
+  Eigen::Matrix<double, 1, 1> m_B;
+  Eigen::Vector<double, 1> m_c;
+  Eigen::Vector<double, 1> m_d;
+  Eigen::Vector<double, 2> m_x;
+  double m_offset;
+};
+
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/ElevatorSim.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/ElevatorSim.h
new file mode 100644
index 0000000..2d0c5f6
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/ElevatorSim.h
@@ -0,0 +1,76 @@
+// 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 <Eigen/Core>
+#include <units/time.h>
+#include <units/voltage.h>
+
+namespace sysid {
+/**
+ * Simulation of an Elevator mechanism based off of a model from SysId
+ * Feedforward gains.
+ */
+class ElevatorSim {
+ public:
+  /**
+   * @param Ks              Static friction gain.
+   * @param Kv              Velocity gain.
+   * @param Ka              Acceleration gain.
+   * @param Kg              Gravity gain.
+   * @param initialPosition Initial elevator position.
+   * @param initialVelocity Initial elevator velocity.
+   */
+  ElevatorSim(double Ks, double Kv, double Ka, double Kg,
+              double initialPosition = 0.0, double initialVelocity = 0.0);
+
+  /**
+   * Simulates affine state-space system dx/dt = Ax + Bu + c sgn(x) + d forward
+   * dt seconds.
+   *
+   * @param voltage Voltage to apply over the timestep.
+   * @param dt      Sammple period.
+   */
+  void Update(units::volt_t voltage, units::second_t dt);
+
+  /**
+   * Returns the position.
+   *
+   * @return The current position
+   */
+  double GetPosition() const;
+
+  /**
+   * Returns the velocity.
+   *
+   * @return The current velocity
+   */
+  double GetVelocity() const;
+
+  /**
+   * Returns the acceleration for the current state and given input.
+   *
+   * @param voltage The voltage that is being applied to the mechanism / input
+   * @return The acceleration given the state and input
+   */
+  double GetAcceleration(units::volt_t voltage) const;
+
+  /**
+   * Resets model position and velocity.
+   *
+   * @param position The position the mechanism should be reset to
+   * @param velocity The velocity the mechanism should be reset to
+   */
+  void Reset(double position = 0.0, double velocity = 0.0);
+
+ private:
+  Eigen::Matrix<double, 2, 2> m_A;
+  Eigen::Matrix<double, 2, 1> m_B;
+  Eigen::Vector<double, 2> m_c;
+  Eigen::Vector<double, 2> m_d;
+  Eigen::Vector<double, 2> m_x;
+};
+
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FeedbackAnalysis.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FeedbackAnalysis.h
new file mode 100644
index 0000000..51754a2
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FeedbackAnalysis.h
@@ -0,0 +1,80 @@
+// 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
+
+namespace sysid {
+
+struct FeedbackControllerPreset;
+
+/**
+ * Represents parameters used to calculate optimal feedback gains using a
+ * linear-quadratic regulator (LQR).
+ */
+struct LQRParameters {
+  /**
+   * The maximum allowable deviation in position.
+   */
+  double qp;
+
+  /**
+   * The maximum allowable deviation in velocity.
+   */
+  double qv;
+
+  /**
+   * The maximum allowable control effort.
+   */
+  double r;
+};
+
+/**
+ * Stores feedback controller gains.
+ */
+struct FeedbackGains {
+  /**
+   * The calculated Proportional gain
+   */
+  double Kp;
+
+  /**
+   * The calculated Derivative gain
+   */
+  double Kd;
+};
+
+/**
+ * Calculates position feedback gains for the given controller preset, LQR
+ * controller gain parameters and feedforward gains.
+ *
+ * @param preset           The feedback controller preset.
+ * @param params           The parameters for calculating optimal feedback
+ *                         gains.
+ * @param Kv               Velocity feedforward gain.
+ * @param Ka               Acceleration feedforward gain.
+ * @param encFactor        The factor to convert the gains from output units to
+ *                         encoder units. This is usually encoder EPR * gearing
+ *                         * units per rotation.
+ */
+FeedbackGains CalculatePositionFeedbackGains(
+    const FeedbackControllerPreset& preset, const LQRParameters& params,
+    double Kv, double Ka, double encFactor = 1.0);
+
+/**
+ * Calculates velocity feedback gains for the given controller preset, LQR
+ * controller gain parameters and feedforward gains.
+ *
+ * @param preset           The feedback controller preset.
+ * @param params           The parameters for calculating optimal feedback
+ *                         gains.
+ * @param Kv               Velocity feedforward gain.
+ * @param Ka               Acceleration feedforward gain.
+ * @param encFactor        The factor to convert the gains from output units to
+ *                         encoder units. This is usually encoder EPR * gearing
+ *                         * units per rotation.
+ */
+FeedbackGains CalculateVelocityFeedbackGains(
+    const FeedbackControllerPreset& preset, const LQRParameters& params,
+    double Kv, double Ka, double encFactor = 1.0);
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FeedbackControllerPreset.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FeedbackControllerPreset.h
new file mode 100644
index 0000000..4b13c6c
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FeedbackControllerPreset.h
@@ -0,0 +1,164 @@
+// 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 <units/time.h>
+
+namespace sysid {
+/**
+ * Represents a preset for a specific feedback controller. This includes info
+ * about the max controller output, the controller period, whether the gains
+ * are time-normalized, and whether there are measurement delays from sensors or
+ * onboard filtering.
+ */
+struct FeedbackControllerPreset {
+  /**
+   * The conversion factor between volts and the final controller output.
+   */
+  double outputConversionFactor;
+
+  /**
+   * The conversion factor for using controller output for velocity gains. This
+   * is necessary as some controllers do velocity controls with different time
+   * units.
+   */
+  double outputVelocityTimeFactor;
+
+  /**
+   * The period at which the controller runs.
+   */
+  units::millisecond_t period;
+
+  /**
+   * Whether the controller gains are time-normalized.
+   */
+  bool normalized;
+
+  /**
+   * The measurement delay in the encoder measurements.
+   */
+  units::millisecond_t measurementDelay;
+
+  /**
+   * Checks equality between two feedback controller presets.
+   *
+   * @param rhs Another FeedbackControllerPreset
+   * @return If the two presets are equal
+   */
+  constexpr bool operator==(const FeedbackControllerPreset& rhs) const {
+    return outputConversionFactor == rhs.outputConversionFactor &&
+           outputVelocityTimeFactor == rhs.outputVelocityTimeFactor &&
+           period == rhs.period && normalized == rhs.normalized &&
+           measurementDelay == rhs.measurementDelay;
+  }
+
+  /**
+   * Checks inequality between two feedback controller presets.
+   *
+   * @param rhs Another FeedbackControllerPreset
+   * @return If the two presets are not equal
+   */
+  constexpr bool operator!=(const FeedbackControllerPreset& rhs) const {
+    return !operator==(rhs);
+  }
+};
+
+/**
+ * The loop type for the feedback controller.
+ */
+enum class FeedbackControllerLoopType { kPosition, kVelocity };
+
+namespace presets {
+constexpr FeedbackControllerPreset kDefault{1.0, 1.0, 20_ms, true, 0_s};
+
+constexpr FeedbackControllerPreset kWPILibNew{kDefault};
+constexpr FeedbackControllerPreset kWPILibOld{1.0 / 12.0, 1.0, 50_ms, false,
+                                              0_s};
+
+// Measurement delay from a moving average filter:
+//
+// A moving average filter with a window size of N is an FIR filter with N taps.
+// The average delay of a moving average filter with N taps and a period between
+// samples of T is (N - 1)/2 T.
+//
+// Proof:
+// N taps with delays of 0 .. (N - 1) T
+//
+// average delay = (sum 0 .. N - 1) / N T
+// = (sum 1 .. N - 1) / N T
+//
+// note: sum 1 .. n = n(n + 1) / 2
+//
+// = (N - 1)((N - 1) + 1) / (2N) T
+// = (N - 1)N / (2N) T
+// = (N - 1)/2 T
+
+// Measurement delay from a backward finite difference:
+//
+// Velocities calculated via a backward finite difference with a period of T
+// look like:
+//
+// velocity = (position at timestep k - position at timestep k-1) / T
+//
+// The average delay is T / 2.
+//
+// Proof:
+// average delay = (0 ms + T) / 2
+//               = T / 2
+
+/**
+ * https://phoenix-documentation.readthedocs.io/en/latest/ch14_MCSensor.html#changing-velocity-measurement-parameters
+ *
+ * Backward finite difference delay = 100 ms / 2 = 50 ms.
+ *
+ * 64-tap moving average delay = (64 - 1) / 2 * 1 ms = 31.5 ms.
+ *
+ * Total delay = 50 ms + 31.5 ms = 81.5 ms.
+ */
+constexpr FeedbackControllerPreset kCTRECANCoder{1.0 / 12.0, 60.0, 1_ms, true,
+                                                 81.5_ms};
+constexpr FeedbackControllerPreset kCTREDefault{1023.0 / 12.0, 0.1, 1_ms, false,
+                                                81.5_ms};
+/**
+ * https://api.ctr-electronics.com/phoenixpro/release/cpp/classctre_1_1phoenixpro_1_1hardware_1_1core_1_1_core_c_a_ncoder.html#a718a1a214b58d3c4543e88e3cb51ade5
+ *
+ * Phoenix Pro uses standard units and Voltage output. This means the output
+ * is 1.0, time factor is 1.0, and closed loop operates at 1 millisecond. All
+ * Pro devices make use of Kalman filters default-tuned to lowest latency, which
+ * in testing is roughly 1 millisecond
+ */
+constexpr FeedbackControllerPreset kCTREProDefault{1.0, 1.0, 1_ms, true, 1_ms};
+
+/**
+ * https://github.com/wpilibsuite/sysid/issues/258#issuecomment-1010658237
+ *
+ * 8-sample moving average with 32 ms between samples.
+ *
+ * Total delay = 8-tap moving average delay = (8 - 1) / 2 * 32 ms = 112 ms.
+ */
+constexpr FeedbackControllerPreset kREVNEOBuiltIn{1.0 / 12.0, 60.0, 1_ms, false,
+                                                  112_ms};
+
+/**
+ * https://www.revrobotics.com/content/sw/max/sw-docs/cpp/classrev_1_1_c_a_n_encoder.html#a7e6ce792bc0c0558fb944771df572e6a
+ *
+ * Backward finite difference delay = 100 ms / 2 = 50 ms.
+ *
+ * 64-tap moving average delay = (64 - 1) / 2 * 1 ms = 31.5 ms.
+ *
+ * Total delay = 50 ms + 31.5 ms = 81.5 ms.
+ */
+constexpr FeedbackControllerPreset kREVNonNEO{1.0 / 12.0, 60.0, 1_ms, false,
+                                              81.5_ms};
+
+/**
+ * https://github.com/wpilibsuite/sysid/pull/138#issuecomment-841734229
+ *
+ * Backward finite difference delay = 10 ms / 2 = 5 ms.
+ */
+constexpr FeedbackControllerPreset kVenom{4096.0 / 12.0, 60.0, 1_ms, false,
+                                          5_ms};
+}  // namespace presets
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FeedforwardAnalysis.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FeedforwardAnalysis.h
new file mode 100644
index 0000000..fc9e47c
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FeedforwardAnalysis.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 <tuple>
+#include <vector>
+
+#include "sysid/analysis/AnalysisType.h"
+#include "sysid/analysis/Storage.h"
+
+namespace sysid {
+
+/**
+ * Calculates feedforward gains given the data and the type of analysis to
+ * perform.
+ *
+ * @return Tuple containing the coefficients of the analysis along with the
+ *         r-squared (coefficient of determination) and RMSE (standard deviation
+ * of the residuals) of the fit.
+ */
+std::tuple<std::vector<double>, double, double> CalculateFeedforwardGains(
+    const Storage& data, const AnalysisType& type);
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FilteringUtils.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FilteringUtils.h
new file mode 100644
index 0000000..9030c00
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/FilteringUtils.h
@@ -0,0 +1,210 @@
+// 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 <algorithm>
+#include <cmath>
+#include <exception>
+#include <functional>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <frc/filter/LinearFilter.h>
+#include <units/time.h>
+#include <wpi/StringMap.h>
+#include <wpi/array.h>
+
+#include "sysid/analysis/AnalysisManager.h"
+#include "sysid/analysis/Storage.h"
+
+namespace sysid {
+
+constexpr int kNoiseMeanWindow = 9;
+
+/**
+ * Exception for Invalid Data Errors in which we can't pin the cause of error to
+ * any one specific setting of the GUI.
+ */
+struct InvalidDataError : public std::exception {
+  /**
+   * Creates an InvalidDataError Exception. It adds additional steps after the
+   * initial error message to inform users in the ways that they could fix their
+   * data.
+   *
+   * @param message The error message
+   */
+  explicit InvalidDataError(std::string_view message) {
+    m_message = fmt::format(
+        "{}. Please verify that your units and data is reasonable and then "
+        "adjust your motion threshold, test duration, and/or window size to "
+        "try to fix this issue.",
+        message);
+  }
+
+  /**
+   * Stores the error message
+   */
+  std::string m_message;
+  const char* what() const noexcept override { return m_message.c_str(); }
+};
+
+/**
+ * Exception for Quasistatic Data being completely removed.
+ */
+struct NoQuasistaticDataError : public std::exception {
+  const char* what() const noexcept override {
+    return "Quasistatic test trimming removed all data. Please adjust your "
+           "motion threshold and double check "
+           "your units and test data to make sure that the robot is reporting "
+           "reasonable values.";
+  }
+};
+
+/**
+ * Exception for Dynamic Data being completely removed.
+ */
+struct NoDynamicDataError : public std::exception {
+  const char* what() const noexcept override {
+    return "Dynamic test trimming removed all data. Please adjust your test "
+           "duration and double check "
+           "your units and test data to make sure that the robot is reporting "
+           "reasonable values.";
+  }
+};
+
+/**
+ * Calculates the expected acceleration noise to be used as the floor of the
+ * Voltage Trim. This is done by taking the standard deviation from the moving
+ * average values of each point.
+ *
+ * @param data the prepared data vector containing acceleration data
+ * @param window the size of the window for the moving average
+ * @param accessorFunction a function that accesses the desired data from the
+ *                         PreparedData struct.
+ * @return The expected acceleration noise
+ */
+double GetNoiseFloor(
+    const std::vector<PreparedData>& data, int window,
+    std::function<double(const PreparedData&)> accessorFunction);
+
+/**
+ * Reduces noise in velocity data by applying a median filter.
+ *
+ * @tparam S The size of the raw data array
+ * @tparam Velocity The index of the velocity entry in the raw data.
+ * @param data the vector of arrays representing sysid data (must contain
+ *             velocity data)
+ * @param window the size of the window of the median filter (must be odd)
+ */
+void ApplyMedianFilter(std::vector<PreparedData>* data, int window);
+
+/**
+ * Trims the step voltage data to discard all points before the maximum
+ * acceleration and after reaching stead-state velocity. Also trims the end of
+ * the test based off of user specified test durations, but it will determine a
+ * default duration if the requested duration is less than the minimum step test
+ * duration.
+ *
+ * @param data A pointer to the step voltage data.
+ * @param settings A pointer to the settings of an analysis manager object.
+ * @param minStepTime The current minimum step test duration as one of the
+ *                    trimming procedures will remove this amount from the start
+ *                    of the test.
+ * @param maxStepTime The maximum step test duration.
+ * @return The updated minimum step test duration.
+ */
+std::tuple<units::second_t, units::second_t, units::second_t>
+TrimStepVoltageData(std::vector<PreparedData>* data,
+                    AnalysisManager::Settings* settings,
+                    units::second_t minStepTime, units::second_t maxStepTime);
+
+/**
+ * Compute the mean time delta of the given data.
+ *
+ * @param data A reference to all of the collected PreparedData
+ * @return The mean time delta for all the data points
+ */
+units::second_t GetMeanTimeDelta(const std::vector<PreparedData>& data);
+
+/**
+ * Compute the mean time delta of the given data.
+ *
+ * @param data A reference to all of the collected PreparedData
+ * @return The mean time delta for all the data points
+ */
+units::second_t GetMeanTimeDelta(const Storage& data);
+
+/**
+ * Creates a central finite difference filter that computes the nth
+ * derivative of the input given the specified number of samples.
+ *
+ * Since this requires data from the future, it should only be used in offline
+ * filtering scenarios.
+ *
+ * For example, a first derivative filter that uses two samples and a sample
+ * period of 20 ms would be
+ *
+ * <pre><code>
+ * CentralFiniteDifference<1, 2>(20_ms);
+ * </code></pre>
+ *
+ * @tparam Derivative The order of the derivative to compute.
+ * @tparam Samples    The number of samples to use to compute the given
+ *                    derivative. This must be odd and one more than the order
+ *                    of derivative or higher.
+ * @param period      The period in seconds between samples taken by the user.
+ */
+template <int Derivative, int Samples>
+frc::LinearFilter<double> CentralFiniteDifference(units::second_t period) {
+  static_assert(Samples % 2 != 0, "Number of samples must be odd.");
+
+  // Generate stencil points from -(samples - 1)/2 to (samples - 1)/2
+  wpi::array<int, Samples> stencil{wpi::empty_array};
+  for (int i = 0; i < Samples; ++i) {
+    stencil[i] = -(Samples - 1) / 2 + i;
+  }
+
+  return frc::LinearFilter<double>::FiniteDifference<Derivative, Samples>(
+      stencil, period);
+}
+
+/**
+ * Trims the quasistatic tests, applies a median filter to the velocity data,
+ * calculates acceleration and cosine (arm only) data, and trims the dynamic
+ * tests.
+ *
+ * @param data A pointer to a data vector recently created by the
+ *             ConvertToPrepared method
+ * @param settings A reference to the analysis settings
+ * @param positionDelays A reference to the vector of computed position signal
+ * delays.
+ * @param velocityDelays A reference to the vector of computed velocity signal
+ * delays.
+ * @param minStepTime A reference to the minimum dynamic test duration as one of
+ *                    the trimming procedures will remove this amount from the
+ *                    start of the test.
+ * @param maxStepTime A reference to the maximum dynamic test duration
+ * @param unit The angular unit that the arm test is in (only for calculating
+ *             cosine data)
+ */
+void InitialTrimAndFilter(wpi::StringMap<std::vector<PreparedData>>* data,
+                          AnalysisManager::Settings* settings,
+                          std::vector<units::second_t>& positionDelays,
+                          std::vector<units::second_t>& velocityDelays,
+                          units::second_t& minStepTime,
+                          units::second_t& maxStepTime,
+                          std::string_view unit = "");
+
+/**
+ * Removes all points with acceleration = 0.
+ *
+ * @param data A pointer to a PreparedData vector
+ */
+void AccelFilter(wpi::StringMap<std::vector<PreparedData>>* data);
+
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/JSONConverter.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/JSONConverter.h
new file mode 100644
index 0000000..7581d25
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/JSONConverter.h
@@ -0,0 +1,24 @@
+// 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 <string>
+#include <string_view>
+
+#include <wpi/Logger.h>
+
+namespace sysid {
+/**
+ * Converts a JSON from the old frc-characterization format to the new sysid
+ * format.
+ *
+ * @param path   The path to the old JSON.
+ * @param logger The logger instance for log messages.
+ * @return The full file path of the newly saved JSON.
+ */
+std::string ConvertJSON(std::string_view path, wpi::Logger& logger);
+
+std::string ToCSV(std::string_view path, wpi::Logger& logger);
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/OLS.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/OLS.h
new file mode 100644
index 0000000..cf97904
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/OLS.h
@@ -0,0 +1,26 @@
+// 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 <cstddef>
+#include <tuple>
+#include <vector>
+
+#include <Eigen/Core>
+
+namespace sysid {
+
+/**
+ * Performs ordinary least squares multiple regression on the provided data and
+ * returns a vector of coefficients along with the r-squared (coefficient of
+ * determination) and RMSE (stardard deviation of the residuals) of the fit.
+ *
+ * @param X The independent data in y = Xβ.
+ * @param y The dependent data in y = Xβ.
+ */
+std::tuple<std::vector<double>, double, double> OLS(const Eigen::MatrixXd& X,
+                                                    const Eigen::VectorXd& y);
+
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/SimpleMotorSim.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/SimpleMotorSim.h
new file mode 100644
index 0000000..a452c64
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/SimpleMotorSim.h
@@ -0,0 +1,74 @@
+// 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 <Eigen/Core>
+#include <units/time.h>
+#include <units/voltage.h>
+
+namespace sysid {
+/**
+ * Simulation of a Simple Motor mechanism based off of a model from SysId
+ * Feedforward gains.
+ */
+class SimpleMotorSim {
+ public:
+  /**
+   * @param Ks              Static friction gain.
+   * @param Kv              Velocity gain.
+   * @param Ka              Acceleration gain.
+   * @param initialPosition Initial flywheel position.
+   * @param initialVelocity Initial flywheel velocity.
+   */
+  SimpleMotorSim(double Ks, double Kv, double Ka, double initialPosition = 0.0,
+                 double initialVelocity = 0.0);
+
+  /**
+   * Simulates affine state-space system dx/dt = Ax + Bu + c sgn(x) forward dt
+   * seconds.
+   *
+   * @param voltage Voltage to apply over the timestep.
+   * @param dt      Sammple period.
+   */
+  void Update(units::volt_t voltage, units::second_t dt);
+
+  /**
+   * Returns the position.
+   *
+   * @return The current position
+   */
+  double GetPosition() const;
+
+  /**
+   * Returns the velocity.
+   *
+   * @return The current velocity
+   */
+  double GetVelocity() const;
+
+  /**
+   * Returns the acceleration for the current state and given input.
+   *
+   * @param voltage The voltage that is being applied to the mechanism / input
+   * @return The acceleration given the state and input
+   */
+  double GetAcceleration(units::volt_t voltage) const;
+
+  /**
+   * Resets model position and velocity.
+   *
+   * @param position The position the mechanism should be reset to
+   * @param velocity The velocity the mechanism should be reset to
+   */
+  void Reset(double position = 0.0, double velocity = 0.0);
+
+ private:
+  Eigen::Matrix<double, 2, 2> m_A;
+  Eigen::Matrix<double, 2, 1> m_B;
+  Eigen::Vector<double, 2> m_c;
+  Eigen::Vector<double, 2> m_x;
+};
+
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/Storage.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/Storage.h
new file mode 100644
index 0000000..52899a0
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/Storage.h
@@ -0,0 +1,95 @@
+// 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 <vector>
+
+#include <units/time.h>
+
+namespace sysid {
+
+/**
+ * Represents each data point after it is cleaned and various parameters are
+ * calculated.
+ */
+struct PreparedData {
+  /**
+   * The timestamp of the data point.
+   */
+  units::second_t timestamp;
+
+  /**
+   * The voltage of the data point.
+   */
+  double voltage;
+
+  /**
+   * The position of the data point.
+   */
+  double position;
+
+  /**
+   * The velocity of the data point.
+   */
+  double velocity;
+
+  /**
+   * The difference in timestamps between this point and the next point.
+   */
+  units::second_t dt = 0_s;
+
+  /**
+   * The acceleration of the data point
+   */
+  double acceleration = 0.0;
+
+  /**
+   * The cosine value of the data point. This is only used for arm data where we
+   * take the cosine of the position.
+   */
+  double cos = 0.0;
+
+  /**
+   * The sine value of the data point. This is only used for arm data where we
+   * take the sine of the position.
+   */
+  double sin = 0.0;
+
+  /**
+   * Equality operator between PreparedData structs
+   *
+   * @param rhs Another PreparedData struct
+   * @return If the other struct is equal to this one
+   */
+  constexpr bool operator==(const PreparedData& rhs) const {
+    return timestamp == rhs.timestamp && voltage == rhs.voltage &&
+           position == rhs.position && velocity == rhs.velocity &&
+           dt == rhs.dt && acceleration == rhs.acceleration && cos == rhs.cos;
+  }
+};
+
+/**
+ * Storage used by the analysis manger.
+ */
+struct Storage {
+  /**
+   * Dataset for slow (aka quasistatic) test
+   */
+  std::vector<PreparedData> slowForward;
+  std::vector<PreparedData> slowBackward;
+
+  /**
+   * Dataset for fast (aka dynamic) test
+   */
+  std::vector<PreparedData> fastForward;
+  std::vector<PreparedData> fastBackward;
+
+  bool empty() const {
+    return slowForward.empty() || slowBackward.empty() || fastForward.empty() ||
+           fastBackward.empty();
+  }
+};
+
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/TrackWidthAnalysis.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/TrackWidthAnalysis.h
new file mode 100644
index 0000000..b022072
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/analysis/TrackWidthAnalysis.h
@@ -0,0 +1,19 @@
+// 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 <units/angle.h>
+
+namespace sysid {
+/**
+ * Calculates the track width given the left distance, right distance, and
+ * accumulated gyro angle.
+ *
+ * @param l     The distance traveled by the left side of the drivetrain.
+ * @param r     The distance traveled by the right side of the drivetrain.
+ * @param accum The accumulated gyro angle.
+ */
+double CalculateTrackWidth(double l, double r, units::radian_t accum);
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/telemetry/TelemetryManager.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/telemetry/TelemetryManager.h
new file mode 100644
index 0000000..85ee09e
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/telemetry/TelemetryManager.h
@@ -0,0 +1,237 @@
+// 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 <array>
+#include <cstddef>
+#include <functional>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <networktables/BooleanTopic.h>
+#include <networktables/DoubleTopic.h>
+#include <networktables/IntegerTopic.h>
+#include <networktables/NetworkTableInstance.h>
+#include <networktables/StringTopic.h>
+#include <units/time.h>
+#include <wpi/Logger.h>
+#include <wpi/SmallVector.h>
+#include <wpi/json.h>
+
+#include "sysid/analysis/AnalysisType.h"
+
+namespace sysid {
+/**
+ * This class is responsible for collecting data from the robot and storing it
+ * inside a JSON.
+ */
+class TelemetryManager {
+ public:
+  /**
+   * Represents settings for an instance of the TelemetryManager class. This
+   * contains information about the quasistatic ramp rate for slow tests, the
+   * step voltage for fast tests, and the mechanism type for characterization.
+   */
+  struct Settings {
+    /**
+     * The rate at which the voltage should increase during the quasistatic test
+     * (V/s).
+     */
+    double quasistaticRampRate = 0.25;
+
+    /**
+     * The voltage that the dynamic test should run at (V).
+     */
+    double stepVoltage = 7.0;
+
+    /**
+     * The units the mechanism moves per recorded rotation. The sysid project
+     * will be recording things in rotations of the shaft so the
+     * unitsPerRotation is to convert those measurements to the units the user
+     * wants to use.
+     */
+    double unitsPerRotation = 1.0;
+
+    /**
+     * The name of the units used.
+     * Valid units:  "Meters", "Feet", "Inches", "Radians", "Degrees",
+     * "Rotations"
+     */
+    std::string units = "Meters";
+
+    /**
+     * The type of mechanism that will be analyzed.
+     * Supported mechanisms: Drivetrain, Angular Drivetrain, Elevator, Arm,
+     * Simple motor.
+     */
+    AnalysisType mechanism = analysis::kDrivetrain;
+  };
+
+  /**
+   * Constructs an instance of the telemetry manager with the provided settings
+   * and NT instance to collect data over.
+   *
+   * @param settings The settings for this instance of the telemetry manager.
+   * @param logger   The logger instance to use for log data.
+   * @param instance The NT instance to collect data over. The default value of
+   *                 this parameter should suffice in production; it should only
+   *                 be changed during unit testing.
+   */
+  explicit TelemetryManager(const Settings& settings, wpi::Logger& logger,
+                            nt::NetworkTableInstance instance =
+                                nt::NetworkTableInstance::GetDefault());
+
+  /**
+   * Begins a test with the given parameters.
+   *
+   * @param name The name of the test.
+   */
+  void BeginTest(std::string_view name);
+
+  /**
+   * Ends the currently running test. If there is no test running, this is a
+   * no-op.
+   */
+  void EndTest();
+
+  /**
+   * Updates the telemetry manager -- this adds a new autospeed entry and
+   * collects newest data from the robot. This must be called periodically by
+   * the user.
+   */
+  void Update();
+
+  /**
+   * Registers a callback that's called by the TelemetryManager when there is a
+   * message to display to the user.
+   *
+   * @param callback Callback function that runs based off of the message
+   */
+  void RegisterDisplayCallback(std::function<void(std::string_view)> callback) {
+    m_callbacks.emplace_back(std::move(callback));
+  }
+
+  /**
+   * Saves a JSON with the stored data at the given location.
+   *
+   * @param location The location to save the JSON at (this is the folder that
+   *                 should contain the saved JSON).
+   * @return The full file path of the saved JSON.
+   */
+  std::string SaveJSON(std::string_view location);
+
+  /**
+   * Returns whether a test is currently running.
+   *
+   * @return Whether a test is currently running.
+   */
+  bool IsActive() const { return m_isRunningTest; }
+
+  /**
+   * Returns whether the specified test is running or has run.
+   *
+   * @param name The test to check.
+   *
+   * @return Whether the specified test is running or has run.
+   */
+  bool HasRunTest(std::string_view name) const {
+    return std::find(m_tests.cbegin(), m_tests.cend(), name) != m_tests.end();
+  }
+
+  /**
+   * Gets the size of the stored data.
+   *
+   * @return The size of the stored data
+   */
+  size_t GetCurrentDataSize() const { return m_params.data.size(); }
+
+ private:
+  enum class State { WaitingForEnable, RunningTest, WaitingForData };
+
+  /**
+   * Stores information about a currently running test. This information
+   * includes whether the robot will be traveling quickly (dynamic) or slowly
+   * (quasistatic), the direction of movement, the start time of the test,
+   * whether the robot is enabled, the current speed of the robot, and the
+   * collected data.
+   */
+  struct TestParameters {
+    bool fast = false;
+    bool forward = false;
+    bool rotate = false;
+
+    State state = State::WaitingForEnable;
+
+    double enableStart = 0.0;
+    double disableStart = 0.0;
+
+    bool enabled = false;
+    double speed = 0.0;
+
+    std::string raw;
+    std::vector<std::vector<double>> data{};
+    bool overflow = false;
+    bool mechError = false;
+
+    TestParameters() = default;
+    TestParameters(bool fast, bool forward, bool rotate, State state)
+        : fast{fast}, forward{forward}, rotate{rotate}, state{state} {}
+  };
+
+  // Settings for this instance.
+  const Settings& m_settings;
+
+  // Logger.
+  wpi::Logger& m_logger;
+
+  // Test parameters for the currently running test.
+  TestParameters m_params;
+  bool m_isRunningTest = false;
+
+  // A list of running or already run tests.
+  std::vector<std::string> m_tests;
+
+  // Stores the test data.
+  wpi::json m_data;
+
+  // Display callbacks.
+  wpi::SmallVector<std::function<void(std::string_view)>, 1> m_callbacks;
+
+  // NetworkTables instance and entries.
+  nt::NetworkTableInstance m_inst;
+  std::shared_ptr<nt::NetworkTable> table = m_inst.GetTable("SmartDashboard");
+  nt::DoublePublisher m_voltageCommand =
+      table->GetDoubleTopic("SysIdVoltageCommand").Publish();
+  nt::StringPublisher m_testType =
+      table->GetStringTopic("SysIdTestType").Publish();
+  nt::BooleanPublisher m_rotate =
+      table->GetBooleanTopic("SysIdRotate").Publish();
+  nt::StringPublisher m_mechanism =
+      table->GetStringTopic("SysIdTest").Publish();
+  nt::BooleanPublisher m_overflowPub =
+      table->GetBooleanTopic("SysIdOverflow").Publish();
+  nt::BooleanSubscriber m_overflowSub =
+      table->GetBooleanTopic("SysIdOverflow").Subscribe(false);
+  nt::BooleanPublisher m_mechErrorPub =
+      table->GetBooleanTopic("SysIdWrongMech").Publish();
+  nt::BooleanSubscriber m_mechErrorSub =
+      table->GetBooleanTopic("SysIdWrongMech").Subscribe(false);
+  nt::StringSubscriber m_telemetry =
+      table->GetStringTopic("SysIdTelemetry").Subscribe("");
+  nt::IntegerSubscriber m_fmsControlData =
+      m_inst.GetTable("FMSInfo")
+          ->GetIntegerTopic("FMSControlData")
+          .Subscribe(0);
+  nt::DoublePublisher m_ackNumberPub =
+      table->GetDoubleTopic("SysIdAckNumber").Publish();
+  nt::DoubleSubscriber m_ackNumberSub =
+      table->GetDoubleTopic("SysIdAckNumber").Subscribe(0);
+
+  int m_ackNumber;
+};
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/view/Analyzer.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/view/Analyzer.h
new file mode 100644
index 0000000..2f30f61
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/view/Analyzer.h
@@ -0,0 +1,261 @@
+// 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 <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <vector>
+
+#include <glass/View.h>
+#include <implot.h>
+#include <portable-file-dialogs.h>
+#include <units/time.h>
+#include <units/voltage.h>
+#include <wpi/Logger.h>
+#include <wpi/StringMap.h>
+
+#include "sysid/analysis/AnalysisManager.h"
+#include "sysid/analysis/AnalysisType.h"
+#include "sysid/analysis/FeedbackAnalysis.h"
+#include "sysid/analysis/FeedbackControllerPreset.h"
+#include "sysid/view/AnalyzerPlot.h"
+
+struct ImPlotPoint;
+
+namespace glass {
+class Storage;
+}  // namespace glass
+
+namespace sysid {
+/**
+ * The Analyzer GUI takes care of providing the user with a user interface to
+ * load their data, visualize the data, adjust certain variables, and then view
+ * the calculated gains.
+ */
+class Analyzer : public glass::View {
+ public:
+  /**
+   * The different display and processing states for the GUI
+   */
+  enum class AnalyzerState {
+    kWaitingForJSON,
+    kNominalDisplay,
+    kMotionThresholdError,
+    kTestDurationError,
+    kGeneralDataError,
+    kFileError
+  };
+  /**
+   * The different motor controller timing presets that can be used.
+   */
+  static constexpr const char* kPresetNames[] = {"Default",
+                                                 "WPILib (2020-)",
+                                                 "WPILib (Pre-2020)",
+                                                 "CANCoder",
+                                                 "CTRE (Pro)",
+                                                 "CTRE",
+                                                 "REV Brushless Encoder Port",
+                                                 "REV Brushed Encoder Port",
+                                                 "REV Data Port",
+                                                 "Venom"};
+
+  /**
+   * The different control loops that can be used.
+   */
+  static constexpr const char* kLoopTypes[] = {"Position", "Velocity"};
+
+  /**
+   * Linear drivetrain analysis subsets
+   */
+  static constexpr const char* kDatasets[] = {"Combined", "Left", "Right"};
+
+  /**
+   * Creates the Analyzer widget
+   *
+   * @param storage Glass Storage
+   * @param logger The program logger
+   */
+  Analyzer(glass::Storage& storage, wpi::Logger& logger);
+
+  /**
+   * Displays the analyzer widget
+   */
+  void Display() override;
+
+  ~Analyzer() override { AbortDataPrep(); };
+
+ private:
+  /**
+   * Handles the logic for selecting a json to analyze
+   */
+  void SelectFile();
+
+  /**
+   * Kills the data preparation thread
+   */
+  void AbortDataPrep();
+
+  /**
+   * Displays the settings to adjust trimming and filtering for feedforward
+   * gains.
+   */
+  void DisplayFeedforwardParameters(float beginX, float beginY);
+
+  /**
+   * Displays the graphs of the data.
+   */
+  void DisplayGraphs();
+
+  /**
+   * Displays the file selection widget.
+   */
+  void DisplayFileSelector();
+
+  /**
+   * Resets the current analysis data.
+   */
+  void ResetData();
+
+  /**
+   * Sets up the reset button and Unit override buttons.
+   *
+   * @return True if the tool had been reset.
+   */
+  bool DisplayResetAndUnitOverride();
+
+  /**
+   * Prepares the data for analysis.
+   */
+  void PrepareData();
+
+  /**
+   * Sets up the graphs to display Raw Data.
+   */
+  void PrepareRawGraphs();
+
+  /**
+   * Sets up the graphs to display filtered/processed data.
+   */
+  void PrepareGraphs();
+
+  /**
+   * True if the stored state is associated with an error.
+   */
+  bool IsErrorState();
+
+  /**
+   * True if the stored state is associated with a data processing error.
+   */
+  bool IsDataErrorState();
+
+  /**
+   * Displays inputs to allow the collecting of theoretical feedforward gains.
+   */
+  void CollectFeedforwardGains(float beginX, float beginY);
+
+  /**
+   * Displays calculated feedforward gains.
+   */
+  void DisplayFeedforwardGains(float beginX, float beginY);
+
+  /**
+   * Displays calculated feedback gains.
+   */
+  void DisplayFeedbackGains();
+
+  /**
+   * Estimates ideal step test duration, qp, and qv for the LQR based off of the
+   * data given
+   */
+  void ConfigParamsOnFileSelect();
+
+  /**
+   * Updates feedforward gains from the analysis manager.
+   */
+  void UpdateFeedforwardGains();
+
+  /**
+   * Updates feedback gains from the analysis manager.
+   */
+  void UpdateFeedbackGains();
+
+  /**
+   * Handles logic of displaying a gain on ImGui
+   */
+  bool DisplayGain(const char* text, double* data, bool readOnly);
+
+  /**
+   * Handles errors when they pop up.
+   */
+  void HandleError(std::string_view msg);
+
+  // State of the Display GUI
+  AnalyzerState m_state = AnalyzerState::kWaitingForJSON;
+
+  // Stores the exception message.
+  std::string m_exception;
+
+  bool m_calcDefaults = false;
+
+  // This is true if the error popup needs to be displayed
+  bool m_errorPopup = false;
+
+  // Everything related to feedback controller calculations.
+  AnalysisManager::Settings m_settings;
+  wpi::StringMap<FeedbackControllerPreset> m_presets;
+
+  int m_selectedLoopType = 1;
+  int m_selectedPreset = 0;
+
+  // Feedforward and feedback gains.
+  std::vector<double> m_ff;
+  double m_accelRSquared;
+  double m_accelRMSE;
+  double m_Kp;
+  double m_Kd;
+  units::millisecond_t m_timescale;
+
+  // Track width
+  std::optional<double> m_trackWidth;
+
+  // Units
+  int m_selectedOverrideUnit = 0;
+  double m_conversionFactor = 0.0;
+
+  // Data analysis
+  std::unique_ptr<AnalysisManager> m_manager;
+  int m_dataset = 0;
+  int m_window = 8;
+  double m_threshold = 0.2;
+  float m_stepTestDuration = 0.0;
+
+  double m_gearingNumerator = 1.0;
+  double m_gearingDenominator = 1.0;
+
+  bool combinedGraphFit = false;
+
+  // File manipulation
+  std::unique_ptr<pfd::open_file> m_selector;
+  std::string m_location;
+
+  // Logger
+  wpi::Logger& m_logger;
+
+  // Plot
+  AnalyzerPlot m_plot{m_logger};
+  bool m_prevPlotsLoaded = false;
+
+  // Stores graph scroll bar position and states for keeping track of scroll
+  // positions after loading graphs
+  float m_graphScroll = 0;
+
+  std::atomic<bool> m_abortDataPrep{false};
+  std::thread m_dataThread;
+};
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/view/AnalyzerPlot.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/view/AnalyzerPlot.h
new file mode 100644
index 0000000..e5fbe8c
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/view/AnalyzerPlot.h
@@ -0,0 +1,203 @@
+// 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 <array>
+#include <atomic>
+#include <functional>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include <imgui.h>
+#include <implot.h>
+#include <units/time.h>
+#include <wpi/Logger.h>
+#include <wpi/spinlock.h>
+
+#include "sysid/analysis/AnalysisType.h"
+#include "sysid/analysis/Storage.h"
+
+namespace sysid {
+/**
+ * Class that helps with plotting data in the analyzer view.
+ */
+class AnalyzerPlot {
+ public:
+  float m_pointSize = 1.25;
+  // 0 for forward, 1 for reverse
+  int m_direction = 0;
+
+  /**
+   * Constructs an instance of the analyzer plot helper and allocates memory for
+   * all data vectors.
+   *
+   * @param logger The program logger
+   */
+  explicit AnalyzerPlot(wpi::Logger& logger);
+
+  /**
+   * Sets the data to be displayed on the plots.
+   *
+   * @param rawData      Raw data storage.
+   * @param filteredData Filtered data storage.
+   * @param unit         Unit of the dataset
+   * @param ff           List of feedforward gains (Ks, Kv, Ka, and optionally
+   *                     Kg).
+   * @param startTimes   Array of dataset start times.
+   * @param type         Type of analysis.
+   * @param abort        Aborts analysis early if set to true from another
+   *                     thread.
+   */
+  void SetData(const Storage& rawData, const Storage& filteredData,
+               std::string_view unit, const std::vector<double>& ff,
+               const std::array<units::second_t, 4>& startTimes,
+               AnalysisType type, std::atomic<bool>& abort);
+
+  /**
+   * Utility method to plot the raw time series data
+   *
+   * @param rawSlow The raw slow (quasistatic) test data
+   * @param rawFast The raw fast (dynamic) test data
+   * @param abort   Aborts analysis early if set to true from another thread
+   */
+  void SetRawTimeData(const std::vector<PreparedData>& rawSlow,
+                      const std::vector<PreparedData>& rawFast,
+                      std::atomic<bool>& abort);
+
+  /**
+   * Utility method to reset everything before generating the points to plot.
+   */
+  void ResetData();
+
+  /**
+   * Utility method to get set the graph labels based off of the units
+   *
+   * @param unit Unit of the dataset
+   */
+  void SetGraphLabels(std::string_view unit);
+
+  /**
+   * Sets up only the raw time series data to be plotted. This is mainly
+   * intended to be used if the filtered data has issues with it.
+   *
+   * @param data    The raw data.
+   * @param unit    Unit of the dataset.
+   * @param abort   Aborts analysis early if set to true from another thread.
+   */
+  void SetRawData(const Storage& data, std::string_view unit,
+                  std::atomic<bool>& abort);
+
+  /**
+   * Displays time domain plots.
+   *
+   * @return Returns true if plots aren't in the loading state
+   */
+  bool DisplayPlots();
+
+  /**
+   * Sets certain flags to true so that the GUI automatically fits the plots
+   */
+  void FitPlots();
+
+  /**
+   * Gets the pointer to the stored Root Mean Squared Error for display
+   *
+   * @return A pointer to the RMSE
+   */
+  double* GetSimRMSE();
+
+  /**
+   * Gets the pointer to the stored simulated velocity R-squared for display
+   *
+   * @return A pointer to the R-squared
+   */
+  double* GetSimRSquared();
+
+ private:
+  // The maximum size of each vector (dataset to plot)
+  static constexpr size_t kMaxSize = 2048;
+
+  struct FilteredDataVsTimePlot {
+    std::vector<ImPlotPoint> rawData;
+    std::vector<ImPlotPoint> filteredData;
+
+    // Simulated time domain data
+    std::vector<std::vector<ImPlotPoint>> simData;
+
+    // Stores whether this was the first call to Plot() since setting data
+    bool fitNextPlot = false;
+
+    FilteredDataVsTimePlot();
+
+    /**
+     * Plots data and fit line.
+     *
+     * @param title Plot title.
+     * @param size Plot size.
+     * @param yLabel Y axis label.
+     * @param pointSize The size of the data point markers (in pixels).
+     */
+    void Plot(const char* title, const ImVec2& size, const char* yLabel,
+              float pointSize);
+
+    /**
+     * Clears plot.
+     */
+    void Clear();
+  };
+
+  struct DataWithFitLinePlot {
+    std::vector<ImPlotPoint> data;
+    std::array<ImPlotPoint, 2> fitLine;
+
+    // Stores whether this was the first call to Plot() since setting data
+    bool fitNextPlot = false;
+
+    DataWithFitLinePlot();
+
+    /**
+     * Plots data and fit line.
+     *
+     * @param title Plot title.
+     * @param size Plot size.
+     * @param xLabel X axis label.
+     * @param yLabel Y axis label.
+     * @param fitX True if X axis should be autofitted.
+     * @param fitY True if Y axis should be autofitted.
+     * @param pointSize The size of the data point markers (in pixels).
+     * @param setup Callback within BeginPlot() block that performs custom plot
+     *              setup.
+     */
+    void Plot(
+        const char* title, const ImVec2& size, const char* xLabel,
+        const char* yLabel, bool fitX, bool fitY, float pointSize,
+        std::function<void()> setup = [] {});
+
+    /**
+     * Clears plot.
+     */
+    void Clear();
+  };
+
+  std::string m_velocityLabel;
+  std::string m_accelerationLabel;
+  std::string m_velPortionAccelLabel;
+
+  // Thread safety
+  wpi::spinlock m_mutex;
+
+  // Logger
+  wpi::Logger& m_logger;
+
+  FilteredDataVsTimePlot m_quasistaticData;
+  FilteredDataVsTimePlot m_dynamicData;
+  DataWithFitLinePlot m_regressionData;
+  DataWithFitLinePlot m_timestepData;
+
+  double m_RMSE;
+  double m_accelRSquared;
+};
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/view/JSONConverter.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/view/JSONConverter.h
new file mode 100644
index 0000000..89bfa32
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/view/JSONConverter.h
@@ -0,0 +1,57 @@
+// 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 <functional>
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <glass/View.h>
+#include <portable-file-dialogs.h>
+#include <wpi/Logger.h>
+
+namespace sysid {
+/**
+ * Helps with converting different JSONs into different formats. Primarily
+ * enables users to convert an old 2020 FRC-Characterization JSON into a SysId
+ * JSON or a SysId JSON into a CSV file.
+ */
+class JSONConverter {
+ public:
+  /**
+   * Creates a JSONConverter widget
+   *
+   * @param logger The program logger
+   */
+  explicit JSONConverter(wpi::Logger& logger) : m_logger(logger) {}
+
+  /**
+   * Function to display the SysId JSON to CSV converter.
+   */
+  void DisplayCSVConvert();
+
+ private:
+  /**
+   * Helper method to display a specific JSON converter
+   *
+   * @param tooltip The tooltip describing the JSON converter
+   * @param converter The function that takes a filename path and performs the
+   *                  previously specifid JSON conversion.
+   */
+  void DisplayConverter(
+      const char* tooltip,
+      std::function<std::string(std::string_view, wpi::Logger&)> converter);
+
+  wpi::Logger& m_logger;
+
+  std::string m_location;
+  std::unique_ptr<pfd::open_file> m_opener;
+
+  std::string m_exception;
+
+  double m_timestamp = 0;
+};
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/view/Logger.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/view/Logger.h
new file mode 100644
index 0000000..d06d650
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/view/Logger.h
@@ -0,0 +1,82 @@
+// 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 <string>
+
+#include <glass/DataSource.h>
+#include <glass/View.h>
+#include <glass/networktables/NetworkTablesSettings.h>
+#include <portable-file-dialogs.h>
+#include <wpi/Logger.h>
+
+#include "sysid/telemetry/TelemetryManager.h"
+
+namespace glass {
+class Storage;
+}  // namespace glass
+
+namespace sysid {
+/**
+ * The logger GUI takes care of running the system idenfitication tests over
+ * NetworkTables and logging the data. This data is then stored in a JSON file
+ * which can be used for analysis.
+ */
+class Logger : public glass::View {
+ public:
+  /**
+   * Makes a logger widget.
+   *
+   * @param storage The glass storage object
+   * @param logger A logger object that keeps track of the program's logs
+   */
+  Logger(glass::Storage& storage, wpi::Logger& logger);
+
+  /**
+   * Displays the logger widget.
+   */
+  void Display() override;
+
+  /**
+   * The different mechanism / analysis types that are supported.
+   */
+  static constexpr const char* kTypes[] = {"Drivetrain", "Drivetrain (Angular)",
+                                           "Arm", "Elevator", "Simple"};
+
+  /**
+   * The different units that are supported.
+   */
+  static constexpr const char* kUnits[] = {"Meters",  "Feet",      "Inches",
+                                           "Radians", "Rotations", "Degrees"};
+
+ private:
+  /**
+   * Handles the logic of selecting a folder to save the SysId JSON to
+   */
+  void SelectDataFolder();
+
+  wpi::Logger& m_logger;
+
+  TelemetryManager::Settings m_settings;
+  int m_selectedType = 0;
+  int m_selectedUnit = 0;
+
+  std::unique_ptr<TelemetryManager> m_manager =
+      std::make_unique<TelemetryManager>(m_settings, m_logger);
+
+  std::unique_ptr<pfd::select_folder> m_selector;
+  std::string m_jsonLocation;
+
+  glass::NetworkTablesSettings m_ntSettings;
+
+  bool m_isRotationalUnits = false;
+
+  std::string m_popupText;
+
+  std::string m_opened;
+  std::string m_exception;
+};
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/include/sysid/view/UILayout.h b/third_party/allwpilib/sysid/src/main/native/include/sysid/view/UILayout.h
new file mode 100644
index 0000000..732a1aa
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/include/sysid/view/UILayout.h
@@ -0,0 +1,95 @@
+// 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 <imgui.h>
+
+namespace sysid {
+/**
+ * constexpr shim for ImVec2.
+ */
+struct Vector2d {
+  /**
+   * X coordinate.
+   */
+  float x = 0;
+
+  /**
+   * Y coordinate.
+   */
+  float y = 0;
+
+  /**
+   * Vector2d addition operator.
+   *
+   * @param rhs Vector to add.
+   * @return Sum of two vectors.
+   */
+  constexpr Vector2d operator+(const Vector2d& rhs) const {
+    return Vector2d{x + rhs.x, y + rhs.y};
+  }
+
+  /**
+   * Vector2d subtraction operator.
+   *
+   * @param rhs Vector to subtract.
+   * @return Difference of two vectors.
+   */
+  constexpr Vector2d operator-(const Vector2d& rhs) const {
+    return Vector2d{x - rhs.x, y - rhs.y};
+  }
+
+  /**
+   * Conversion operator to ImVec2.
+   */
+  explicit operator ImVec2() const { return ImVec2{x, y}; }
+};
+
+// App window size
+inline constexpr Vector2d kAppWindowSize{1280, 720};
+
+// Menubar height
+inline constexpr int kMenubarHeight = 20;
+
+// Gap between window edges
+inline constexpr int kWindowGap = 5;
+
+// Left column position and size
+inline constexpr Vector2d kLeftColPos{kWindowGap, kMenubarHeight + kWindowGap};
+inline constexpr Vector2d kLeftColSize{
+    310, kAppWindowSize.y - kLeftColPos.y - kWindowGap};
+
+// Left column contents
+inline constexpr Vector2d kLoggerWindowPos = kLeftColPos;
+inline constexpr Vector2d kLoggerWindowSize{
+    kLeftColSize.x, kAppWindowSize.y - kWindowGap - kLoggerWindowPos.y};
+
+// Center column position and size
+inline constexpr Vector2d kCenterColPos =
+    kLeftColPos + Vector2d{kLeftColSize.x + kWindowGap, 0};
+inline constexpr Vector2d kCenterColSize{
+    360, kAppWindowSize.y - kLeftColPos.y - kWindowGap};
+
+// Center column contents
+inline constexpr Vector2d kAnalyzerWindowPos = kCenterColPos;
+inline constexpr Vector2d kAnalyzerWindowSize{kCenterColSize.x, 550};
+inline constexpr Vector2d kProgramLogWindowPos =
+    kAnalyzerWindowPos + Vector2d{0, kAnalyzerWindowSize.y + kWindowGap};
+inline constexpr Vector2d kProgramLogWindowSize{
+    kCenterColSize.x, kAppWindowSize.y - kWindowGap - kProgramLogWindowPos.y};
+
+// Right column position and size
+inline constexpr Vector2d kRightColPos =
+    kCenterColPos + Vector2d{kCenterColSize.x + kWindowGap, 0};
+inline constexpr Vector2d kRightColSize =
+    kAppWindowSize - kRightColPos - Vector2d{kWindowGap, kWindowGap};
+
+// Right column contents
+inline constexpr Vector2d kDiagnosticPlotWindowPos = kRightColPos;
+inline constexpr Vector2d kDiagnosticPlotWindowSize = kRightColSize;
+
+// Text box width as a multiple of the font size
+inline constexpr int kTextBoxWidthMultiple = 10;
+}  // namespace sysid
diff --git a/third_party/allwpilib/sysid/src/main/native/mac/sysid.icns b/third_party/allwpilib/sysid/src/main/native/mac/sysid.icns
new file mode 100644
index 0000000..53f0d69
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/mac/sysid.icns
Binary files differ
diff --git a/third_party/allwpilib/sysid/src/main/native/resources/sysid-128.png b/third_party/allwpilib/sysid/src/main/native/resources/sysid-128.png
new file mode 100644
index 0000000..1da8dfe
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/resources/sysid-128.png
Binary files differ
diff --git a/third_party/allwpilib/sysid/src/main/native/resources/sysid-16.png b/third_party/allwpilib/sysid/src/main/native/resources/sysid-16.png
new file mode 100644
index 0000000..851739f
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/resources/sysid-16.png
Binary files differ
diff --git a/third_party/allwpilib/sysid/src/main/native/resources/sysid-256.png b/third_party/allwpilib/sysid/src/main/native/resources/sysid-256.png
new file mode 100644
index 0000000..a66aa94
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/resources/sysid-256.png
Binary files differ
diff --git a/third_party/allwpilib/sysid/src/main/native/resources/sysid-32.png b/third_party/allwpilib/sysid/src/main/native/resources/sysid-32.png
new file mode 100644
index 0000000..3332bd1
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/resources/sysid-32.png
Binary files differ
diff --git a/third_party/allwpilib/sysid/src/main/native/resources/sysid-48.png b/third_party/allwpilib/sysid/src/main/native/resources/sysid-48.png
new file mode 100644
index 0000000..bee41b7
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/resources/sysid-48.png
Binary files differ
diff --git a/third_party/allwpilib/sysid/src/main/native/resources/sysid-512.png b/third_party/allwpilib/sysid/src/main/native/resources/sysid-512.png
new file mode 100644
index 0000000..5fc1d8e
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/resources/sysid-512.png
Binary files differ
diff --git a/third_party/allwpilib/sysid/src/main/native/resources/sysid-64.png b/third_party/allwpilib/sysid/src/main/native/resources/sysid-64.png
new file mode 100644
index 0000000..efb193d
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/resources/sysid-64.png
Binary files differ
diff --git a/third_party/allwpilib/sysid/src/main/native/win/sysid.ico b/third_party/allwpilib/sysid/src/main/native/win/sysid.ico
new file mode 100644
index 0000000..89f6d3f
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/win/sysid.ico
Binary files differ
diff --git a/third_party/allwpilib/sysid/src/main/native/win/sysid.rc b/third_party/allwpilib/sysid/src/main/native/win/sysid.rc
new file mode 100644
index 0000000..21d11e8
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/main/native/win/sysid.rc
@@ -0,0 +1 @@
+IDI_ICON1 ICON "sysid.ico"
diff --git a/third_party/allwpilib/sysid/src/test/native/cpp/Main.cpp b/third_party/allwpilib/sysid/src/test/native/cpp/Main.cpp
new file mode 100644
index 0000000..e993c1f
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/test/native/cpp/Main.cpp
@@ -0,0 +1,11 @@
+// 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 <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  return ret;
+}
diff --git a/third_party/allwpilib/sysid/src/test/native/cpp/analysis/AnalysisTypeTest.cpp b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/AnalysisTypeTest.cpp
new file mode 100644
index 0000000..0abb2a1
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/AnalysisTypeTest.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 <gtest/gtest.h>
+
+#include "sysid/analysis/AnalysisType.h"
+
+TEST(AnalysisTypeTest, FromName) {
+  EXPECT_EQ(sysid::analysis::kDrivetrain,
+            sysid::analysis::FromName("Drivetrain"));
+  EXPECT_EQ(sysid::analysis::kDrivetrainAngular,
+            sysid::analysis::FromName("Drivetrain (Angular)"));
+  EXPECT_EQ(sysid::analysis::kElevator, sysid::analysis::FromName("Elevator"));
+  EXPECT_EQ(sysid::analysis::kArm, sysid::analysis::FromName("Arm"));
+  EXPECT_EQ(sysid::analysis::kSimple, sysid::analysis::FromName("Simple"));
+  EXPECT_EQ(sysid::analysis::kSimple, sysid::analysis::FromName("Random"));
+}
diff --git a/third_party/allwpilib/sysid/src/test/native/cpp/analysis/FeedbackAnalysisTest.cpp b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/FeedbackAnalysisTest.cpp
new file mode 100644
index 0000000..44f664c
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/FeedbackAnalysisTest.cpp
@@ -0,0 +1,105 @@
+// 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 <gtest/gtest.h>
+
+#include "sysid/analysis/FeedbackAnalysis.h"
+#include "sysid/analysis/FeedbackControllerPreset.h"
+
+TEST(FeedbackAnalysisTest, Velocity1) {
+  auto Kv = 3.060;
+  auto Ka = 0.327;
+
+  sysid::LQRParameters params{1, 1.5, 7};
+
+  auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
+      sysid::presets::kDefault, params, Kv, Ka);
+
+  EXPECT_NEAR(Kp, 2.11, 0.05);
+  EXPECT_NEAR(Kd, 0.00, 0.05);
+}
+
+TEST(FeedbackAnalysisTest, Velocity2) {
+  auto Kv = 0.0693;
+  auto Ka = 0.1170;
+
+  sysid::LQRParameters params{1, 1.5, 7};
+
+  auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
+      sysid::presets::kDefault, params, Kv, Ka);
+
+  EXPECT_NEAR(Kp, 3.11, 0.05);
+  EXPECT_NEAR(Kd, 0.00, 0.05);
+}
+
+TEST(FeedbackAnalysisTest, VelocityConversion) {
+  auto Kv = 0.0693;
+  auto Ka = 0.1170;
+
+  sysid::LQRParameters params{1, 1.5, 7};
+
+  auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
+      sysid::presets::kDefault, params, Kv, Ka, 3.0 * 1023);
+
+  // This should have the same Kp as the test above, but scaled by a factor of 3
+  // * 1023.
+  EXPECT_NEAR(Kp, 3.11 / (3 * 1023), 0.005);
+  EXPECT_NEAR(Kd, 0.00, 0.05);
+}
+
+TEST(FeedbackAnalysisTest, VelocityCTRE) {
+  auto Kv = 1.97;
+  auto Ka = 0.179;
+
+  sysid::LQRParameters params{1, 1.5, 7};
+
+  auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
+      sysid::presets::kCTRECANCoder, params, Kv, Ka);
+
+  EXPECT_NEAR(Kp, 0.000417, 0.00005);
+  EXPECT_NEAR(Kd, 0.00, 0.05);
+}
+
+TEST(FeedbackAnalysisTest, VelocityCTREConversion) {
+  auto Kv = 1.97;
+  auto Ka = 0.179;
+
+  sysid::LQRParameters params{1, 1.5, 7};
+
+  auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
+      sysid::presets::kCTRECANCoder, params, Kv, Ka, 3.0);
+
+  // This should have the same Kp as the test above, but scaled by a factor
+  // of 3.
+  EXPECT_NEAR(Kp, 0.000417 / 3, 0.00005);
+  EXPECT_NEAR(Kd, 0.00, 0.05);
+}
+
+TEST(FeedbackAnalysisTest, VelocityREV) {
+  auto Kv = 1.97;
+  auto Ka = 0.179;
+
+  sysid::LQRParameters params{1, 1.5, 7};
+
+  auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
+      sysid::presets::kREVNEOBuiltIn, params, Kv, Ka);
+
+  EXPECT_NEAR(Kp, 0.00241, 0.005);
+  EXPECT_NEAR(Kd, 0.00, 0.05);
+}
+
+TEST(FeedbackAnalysisTest, VelocityREVConversion) {
+  auto Kv = 1.97;
+  auto Ka = 0.179;
+
+  sysid::LQRParameters params{1, 1.5, 7};
+
+  auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
+      sysid::presets::kREVNEOBuiltIn, params, Kv, Ka, 3.0);
+
+  // This should have the same Kp as the test above, but scaled by a factor
+  // of 3.
+  EXPECT_NEAR(Kp, 0.00241 / 3, 0.005);
+  EXPECT_NEAR(Kd, 0.00, 0.05);
+}
diff --git a/third_party/allwpilib/sysid/src/test/native/cpp/analysis/FeedforwardAnalysisTest.cpp b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/FeedforwardAnalysisTest.cpp
new file mode 100644
index 0000000..a52840d
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/FeedforwardAnalysisTest.cpp
@@ -0,0 +1,251 @@
+// 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 <cmath>
+
+#include <gtest/gtest.h>
+#include <units/time.h>
+#include <units/voltage.h>
+
+#include "sysid/analysis/AnalysisManager.h"
+#include "sysid/analysis/ArmSim.h"
+#include "sysid/analysis/ElevatorSim.h"
+#include "sysid/analysis/FeedforwardAnalysis.h"
+#include "sysid/analysis/SimpleMotorSim.h"
+
+/**
+ * Return simulated test data for a given simulation model.
+ *
+ * @param Ks Static friction gain.
+ * @param Kv Velocity gain.
+ * @param Ka Acceleration gain.
+ * @param Kg Gravity cosine gain.
+ */
+template <typename Model>
+sysid::Storage CollectData(Model& model) {
+  constexpr auto kUstep = 0.25_V / 1_s;
+  constexpr units::volt_t kUmax = 7_V;
+  constexpr units::second_t T = 5_ms;
+  constexpr units::second_t kTestDuration = 5_s;
+
+  sysid::Storage storage;
+  auto& [slowForward, slowBackward, fastForward, fastBackward] = storage;
+
+  // Slow forward test
+  auto voltage = 0_V;
+  for (int i = 0; i < (kTestDuration / T).value(); ++i) {
+    slowForward.emplace_back(sysid::PreparedData{
+        i * T, voltage.value(), model.GetPosition(), model.GetVelocity(), T,
+        model.GetAcceleration(voltage), std::cos(model.GetPosition()),
+        std::sin(model.GetPosition())});
+
+    model.Update(voltage, T);
+    voltage += kUstep * T;
+  }
+
+  // Slow backward test
+  model.Reset();
+  voltage = 0_V;
+  for (int i = 0; i < (kTestDuration / T).value(); ++i) {
+    slowBackward.emplace_back(sysid::PreparedData{
+        i * T, voltage.value(), model.GetPosition(), model.GetVelocity(), T,
+        model.GetAcceleration(voltage), std::cos(model.GetPosition()),
+        std::sin(model.GetPosition())});
+
+    model.Update(voltage, T);
+    voltage -= kUstep * T;
+  }
+
+  // Fast forward test
+  model.Reset();
+  voltage = 0_V;
+  for (int i = 0; i < (kTestDuration / T).value(); ++i) {
+    fastForward.emplace_back(sysid::PreparedData{
+        i * T, voltage.value(), model.GetPosition(), model.GetVelocity(), T,
+        model.GetAcceleration(voltage), std::cos(model.GetPosition()),
+        std::sin(model.GetPosition())});
+
+    model.Update(voltage, T);
+    voltage = kUmax;
+  }
+
+  // Fast backward test
+  model.Reset();
+  voltage = 0_V;
+  for (int i = 0; i < (kTestDuration / T).value(); ++i) {
+    fastBackward.emplace_back(sysid::PreparedData{
+        i * T, voltage.value(), model.GetPosition(), model.GetVelocity(), T,
+        model.GetAcceleration(voltage), std::cos(model.GetPosition()),
+        std::sin(model.GetPosition())});
+
+    model.Update(voltage, T);
+    voltage = -kUmax;
+  }
+
+  return storage;
+}
+
+TEST(FeedforwardAnalysisTest, Arm1) {
+  constexpr double Ks = 1.01;
+  constexpr double Kv = 3.060;
+  constexpr double Ka = 0.327;
+  constexpr double Kg = 0.211;
+
+  for (const auto& offset : {-2.0, -1.0, 0.0, 1.0, 2.0}) {
+    sysid::ArmSim model{Ks, Kv, Ka, Kg, offset};
+    auto ff = sysid::CalculateFeedforwardGains(CollectData(model),
+                                               sysid::analysis::kArm);
+    auto& gains = std::get<0>(ff);
+
+    EXPECT_NEAR(gains[0], Ks, 0.003);
+    EXPECT_NEAR(gains[1], Kv, 0.003);
+    EXPECT_NEAR(gains[2], Ka, 0.003);
+    EXPECT_NEAR(gains[3], Kg, 0.003);
+    EXPECT_NEAR(gains[4], offset, 0.007);
+  }
+}
+
+TEST(FeedforwardAnalysisTest, Arm2) {
+  constexpr double Ks = 0.547;
+  constexpr double Kv = 0.0693;
+  constexpr double Ka = 0.1170;
+  constexpr double Kg = 0.122;
+
+  for (const auto& offset : {-2.0, -1.0, 0.0, 1.0, 2.0}) {
+    sysid::ArmSim model{Ks, Kv, Ka, Kg, offset};
+    auto ff = sysid::CalculateFeedforwardGains(CollectData(model),
+                                               sysid::analysis::kArm);
+    auto& gains = std::get<0>(ff);
+
+    EXPECT_NEAR(gains[0], Ks, 0.003);
+    EXPECT_NEAR(gains[1], Kv, 0.003);
+    EXPECT_NEAR(gains[2], Ka, 0.003);
+    EXPECT_NEAR(gains[3], Kg, 0.003);
+    EXPECT_NEAR(gains[4], offset, 0.007);
+  }
+}
+
+TEST(FeedforwardAnalysisTest, Drivetrain1) {
+  constexpr double Ks = 1.01;
+  constexpr double Kv = 3.060;
+  constexpr double Ka = 0.327;
+
+  sysid::SimpleMotorSim model{Ks, Kv, Ka};
+  auto ff = sysid::CalculateFeedforwardGains(CollectData(model),
+                                             sysid::analysis::kDrivetrain);
+  auto& gains = std::get<0>(ff);
+
+  EXPECT_NEAR(gains[0], Ks, 0.003);
+  EXPECT_NEAR(gains[1], Kv, 0.003);
+  EXPECT_NEAR(gains[2], Ka, 0.003);
+}
+
+TEST(FeedforwardAnalysisTest, Drivetrain2) {
+  constexpr double Ks = 0.547;
+  constexpr double Kv = 0.0693;
+  constexpr double Ka = 0.1170;
+
+  sysid::SimpleMotorSim model{Ks, Kv, Ka};
+  auto ff = sysid::CalculateFeedforwardGains(CollectData(model),
+                                             sysid::analysis::kDrivetrain);
+  auto& gains = std::get<0>(ff);
+
+  EXPECT_NEAR(gains[0], Ks, 0.003);
+  EXPECT_NEAR(gains[1], Kv, 0.003);
+  EXPECT_NEAR(gains[2], Ka, 0.003);
+}
+
+TEST(FeedforwardAnalysisTest, DrivetrainAngular1) {
+  constexpr double Ks = 1.01;
+  constexpr double Kv = 3.060;
+  constexpr double Ka = 0.327;
+
+  sysid::SimpleMotorSim model{Ks, Kv, Ka};
+  auto ff = sysid::CalculateFeedforwardGains(
+      CollectData(model), sysid::analysis::kDrivetrainAngular);
+  auto& gains = std::get<0>(ff);
+
+  EXPECT_NEAR(gains[0], Ks, 0.003);
+  EXPECT_NEAR(gains[1], Kv, 0.003);
+  EXPECT_NEAR(gains[2], Ka, 0.003);
+}
+
+TEST(FeedforwardAnalysisTest, DrivetrainAngular2) {
+  constexpr double Ks = 0.547;
+  constexpr double Kv = 0.0693;
+  constexpr double Ka = 0.1170;
+
+  sysid::SimpleMotorSim model{Ks, Kv, Ka};
+  auto ff = sysid::CalculateFeedforwardGains(
+      CollectData(model), sysid::analysis::kDrivetrainAngular);
+  auto& gains = std::get<0>(ff);
+
+  EXPECT_NEAR(gains[0], Ks, 0.003);
+  EXPECT_NEAR(gains[1], Kv, 0.003);
+  EXPECT_NEAR(gains[2], Ka, 0.003);
+}
+
+TEST(FeedforwardAnalysisTest, Elevator1) {
+  constexpr double Ks = 1.01;
+  constexpr double Kv = 3.060;
+  constexpr double Ka = 0.327;
+  constexpr double Kg = -0.211;
+
+  sysid::ElevatorSim model{Ks, Kv, Ka, Kg};
+  auto ff = sysid::CalculateFeedforwardGains(CollectData(model),
+                                             sysid::analysis::kElevator);
+  auto& gains = std::get<0>(ff);
+
+  EXPECT_NEAR(gains[0], Ks, 0.003);
+  EXPECT_NEAR(gains[1], Kv, 0.003);
+  EXPECT_NEAR(gains[2], Ka, 0.003);
+  EXPECT_NEAR(gains[3], Kg, 0.003);
+}
+
+TEST(FeedforwardAnalysisTest, Elevator2) {
+  constexpr double Ks = 0.547;
+  constexpr double Kv = 0.0693;
+  constexpr double Ka = 0.1170;
+  constexpr double Kg = -0.122;
+
+  sysid::ElevatorSim model{Ks, Kv, Ka, Kg};
+  auto ff = sysid::CalculateFeedforwardGains(CollectData(model),
+                                             sysid::analysis::kElevator);
+  auto& gains = std::get<0>(ff);
+
+  EXPECT_NEAR(gains[0], Ks, 0.003);
+  EXPECT_NEAR(gains[1], Kv, 0.003);
+  EXPECT_NEAR(gains[2], Ka, 0.003);
+  EXPECT_NEAR(gains[3], Kg, 0.003);
+}
+
+TEST(FeedforwardAnalysisTest, Simple1) {
+  constexpr double Ks = 1.01;
+  constexpr double Kv = 3.060;
+  constexpr double Ka = 0.327;
+
+  sysid::SimpleMotorSim model{Ks, Kv, Ka};
+  auto ff = sysid::CalculateFeedforwardGains(CollectData(model),
+                                             sysid::analysis::kSimple);
+  auto& gains = std::get<0>(ff);
+
+  EXPECT_NEAR(gains[0], Ks, 0.003);
+  EXPECT_NEAR(gains[1], Kv, 0.003);
+  EXPECT_NEAR(gains[2], Ka, 0.003);
+}
+
+TEST(FeedforwardAnalysisTest, Simple2) {
+  constexpr double Ks = 0.547;
+  constexpr double Kv = 0.0693;
+  constexpr double Ka = 0.1170;
+
+  sysid::SimpleMotorSim model{Ks, Kv, Ka};
+  auto ff = sysid::CalculateFeedforwardGains(CollectData(model),
+                                             sysid::analysis::kSimple);
+  auto& gains = std::get<0>(ff);
+
+  EXPECT_NEAR(gains[0], Ks, 0.003);
+  EXPECT_NEAR(gains[1], Kv, 0.003);
+  EXPECT_NEAR(gains[2], Ka, 0.003);
+}
diff --git a/third_party/allwpilib/sysid/src/test/native/cpp/analysis/FilterTest.cpp b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/FilterTest.cpp
new file mode 100644
index 0000000..a7b0349
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/FilterTest.cpp
@@ -0,0 +1,170 @@
+// 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 <array>
+#include <cmath>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "sysid/analysis/AnalysisManager.h"
+#include "sysid/analysis/FeedforwardAnalysis.h"
+#include "sysid/analysis/FilteringUtils.h"
+#include "sysid/analysis/Storage.h"
+
+TEST(FilterTest, MedianFilter) {
+  std::vector<sysid::PreparedData> testData{
+      sysid::PreparedData{0_s, 0, 0, 0},    sysid::PreparedData{0_s, 0, 0, 1},
+      sysid::PreparedData{0_s, 0, 0, 10},   sysid::PreparedData{0_s, 0, 0, 5},
+      sysid::PreparedData{0_s, 0, 0, 3},    sysid::PreparedData{0_s, 0, 0, 0},
+      sysid::PreparedData{0_s, 0, 0, 1000}, sysid::PreparedData{0_s, 0, 0, 7},
+      sysid::PreparedData{0_s, 0, 0, 6},    sysid::PreparedData{0_s, 0, 0, 5}};
+  std::vector<sysid::PreparedData> expectedData{
+      sysid::PreparedData{0_s, 0, 0, 0}, sysid::PreparedData{0_s, 0, 0, 1},
+      sysid::PreparedData{0_s, 0, 0, 5}, sysid::PreparedData{0_s, 0, 0, 5},
+      sysid::PreparedData{0_s, 0, 0, 3}, sysid::PreparedData{0_s, 0, 0, 3},
+      sysid::PreparedData{0_s, 0, 0, 7}, sysid::PreparedData{0_s, 0, 0, 7},
+      sysid::PreparedData{0_s, 0, 0, 6}, sysid::PreparedData{0_s, 0, 0, 5}};
+
+  sysid::ApplyMedianFilter(&testData, 3);
+  EXPECT_EQ(expectedData, testData);
+}
+
+TEST(FilterTest, NoiseFloor) {
+  std::vector<sysid::PreparedData> testData = {
+      {0_s, 1, 2, 3, 5_ms, 0, 0},    {1_s, 1, 2, 3, 5_ms, 1, 0},
+      {2_s, 1, 2, 3, 5_ms, 2, 0},    {3_s, 1, 2, 3, 5_ms, 5, 0},
+      {4_s, 1, 2, 3, 5_ms, 0.35, 0}, {5_s, 1, 2, 3, 5_ms, 0.15, 0},
+      {6_s, 1, 2, 3, 5_ms, 0, 0},    {7_s, 1, 2, 3, 5_ms, 0.02, 0},
+      {8_s, 1, 2, 3, 5_ms, 0.01, 0}, {9_s, 1, 2, 3, 5_ms, 0, 0}};
+  double noiseFloor =
+      GetNoiseFloor(testData, 2, [](auto&& pt) { return pt.acceleration; });
+  EXPECT_NEAR(0.953, noiseFloor, 0.001);
+}
+
+TEST(FilterTest, StepTrim) {
+  std::vector<sysid::PreparedData> testData = {
+      {0_s, 1, 2, 3, 5_ms, 0, 0},    {1_s, 1, 2, 3, 5_ms, 0.25, 0},
+      {2_s, 1, 2, 3, 5_ms, 0.5, 0},  {3_s, 1, 2, 3, 5_ms, 0.45, 0},
+      {4_s, 1, 2, 3, 5_ms, 0.35, 0}, {5_s, 1, 2, 3, 5_ms, 0.15, 0},
+      {6_s, 1, 2, 3, 5_ms, 0, 0},    {7_s, 1, 2, 3, 5_ms, 0.02, 0},
+      {8_s, 1, 2, 3, 5_ms, 0.01, 0}, {9_s, 1, 2, 3, 5_ms, 0, 0},
+  };
+
+  std::vector<sysid::PreparedData> expectedData = {
+      {2_s, 1, 2, 3, 5_ms, 0.5, 0},
+      {3_s, 1, 2, 3, 5_ms, 0.45, 0},
+      {4_s, 1, 2, 3, 5_ms, 0.35, 0},
+      {5_s, 1, 2, 3, 5_ms, 0.15, 0}};
+
+  auto maxTime = 9_s;
+  auto minTime = maxTime;
+
+  sysid::AnalysisManager::Settings settings;
+  auto [tempMinTime, positionDelay, velocityDelay] =
+      sysid::TrimStepVoltageData(&testData, &settings, minTime, maxTime);
+  minTime = tempMinTime;
+
+  EXPECT_EQ(expectedData[0].acceleration, testData[0].acceleration);
+  EXPECT_EQ(expectedData.back().acceleration, testData.back().acceleration);
+  EXPECT_EQ(5, settings.stepTestDuration.value());
+  EXPECT_EQ(2, minTime.value());
+}
+
+template <int Derivative, int Samples, typename F, typename DfDx>
+void AssertCentralResults(F&& f, DfDx&& dfdx, units::second_t h, double min,
+                          double max) {
+  static_assert(Samples % 2 != 0, "Number of samples must be odd.");
+
+  auto filter = sysid::CentralFiniteDifference<Derivative, Samples>(h);
+
+  for (int i = min / h.value(); i < max / h.value(); ++i) {
+    // Let filter initialize
+    if (i < static_cast<int>(min / h.value()) + Samples) {
+      filter.Calculate(f(i * h.value()));
+      continue;
+    }
+
+    // For central finite difference, the derivative computed at this point is
+    // half the window size in the past.
+    // The order of accuracy is O(h^(N - d)) where N is number of stencil
+    // points and d is order of derivative
+    EXPECT_NEAR(dfdx((i - static_cast<int>((Samples - 1) / 2)) * h.value()),
+                filter.Calculate(f(i * h.value())),
+                std::pow(h.value(), Samples - Derivative));
+  }
+}
+
+/**
+ * Test central finite difference.
+ */
+TEST(LinearFilterOutputTest, CentralFiniteDifference) {
+  constexpr auto h = 5_ms;
+
+  AssertCentralResults<1, 3>(
+      [](double x) {
+        // f(x) = x²
+        return x * x;
+      },
+      [](double x) {
+        // df/dx = 2x
+        return 2.0 * x;
+      },
+      h, -20.0, 20.0);
+
+  AssertCentralResults<1, 3>(
+      [](double x) {
+        // f(x) = std::sin(x)
+        return std::sin(x);
+      },
+      [](double x) {
+        // df/dx = std::cos(x)
+        return std::cos(x);
+      },
+      h, -20.0, 20.0);
+
+  AssertCentralResults<1, 3>(
+      [](double x) {
+        // f(x) = ln(x)
+        return std::log(x);
+      },
+      [](double x) {
+        // df/dx = 1 / x
+        return 1.0 / x;
+      },
+      h, 1.0, 20.0);
+
+  AssertCentralResults<2, 5>(
+      [](double x) {
+        // f(x) = x^2
+        return x * x;
+      },
+      [](double x) {
+        // d²f/dx² = 2
+        return 2.0;
+      },
+      h, -20.0, 20.0);
+
+  AssertCentralResults<2, 5>(
+      [](double x) {
+        // f(x) = std::sin(x)
+        return std::sin(x);
+      },
+      [](double x) {
+        // d²f/dx² = -std::sin(x)
+        return -std::sin(x);
+      },
+      h, -20.0, 20.0);
+
+  AssertCentralResults<2, 5>(
+      [](double x) {
+        // f(x) = ln(x)
+        return std::log(x);
+      },
+      [](double x) {
+        // d²f/dx² = -1 / x²
+        return -1.0 / (x * x);
+      },
+      h, 1.0, 20.0);
+}
diff --git a/third_party/allwpilib/sysid/src/test/native/cpp/analysis/OLSTest.cpp b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/OLSTest.cpp
new file mode 100644
index 0000000..bf20516
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/OLSTest.cpp
@@ -0,0 +1,42 @@
+// 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 <gtest/gtest.h>
+
+#include "sysid/analysis/OLS.h"
+
+TEST(OLSTest, TwoVariablesTwoPoints) {
+  // (1, 3) and (2, 5). Should produce y = 2x + 1.
+  Eigen::MatrixXd X{{1.0, 1.0}, {1.0, 2.0}};
+  Eigen::VectorXd y{{3.0}, {5.0}};
+
+  auto [coefficients, cod, rmse] = sysid::OLS(X, y);
+  EXPECT_EQ(coefficients.size(), 2u);
+
+  EXPECT_NEAR(coefficients[0], 1.0, 0.05);
+  EXPECT_NEAR(coefficients[1], 2.0, 0.05);
+  EXPECT_NEAR(cod, 1.0, 1E-4);
+}
+
+TEST(OLSTest, TwoVariablesFivePoints) {
+  // (2, 4), (3, 5), (5, 7), (7, 10), (9, 15)
+  // Should produce 1.518x + 0.305.
+  Eigen::MatrixXd X{{1, 2}, {1, 3}, {1, 5}, {1, 7}, {1, 9}};
+  Eigen::VectorXd y{{4}, {5}, {7}, {10}, {15}};
+
+  auto [coefficients, cod, rmse] = sysid::OLS(X, y);
+  EXPECT_EQ(coefficients.size(), 2u);
+
+  EXPECT_NEAR(coefficients[0], 0.305, 0.05);
+  EXPECT_NEAR(coefficients[1], 1.518, 0.05);
+  EXPECT_NEAR(cod, 0.985, 0.05);
+}
+
+#ifndef NDEBUG
+TEST(OLSTest, MalformedData) {
+  Eigen::MatrixXd X{{1, 2}, {1, 3}, {1, 4}};
+  Eigen::VectorXd y{{4}, {5}};
+  EXPECT_DEATH(sysid::OLS(X, y), "");
+}
+#endif
diff --git a/third_party/allwpilib/sysid/src/test/native/cpp/analysis/TrackWidthAnalysisTest.cpp b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/TrackWidthAnalysisTest.cpp
new file mode 100644
index 0000000..e7b662f
--- /dev/null
+++ b/third_party/allwpilib/sysid/src/test/native/cpp/analysis/TrackWidthAnalysisTest.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 <gtest/gtest.h>
+
+#include "sysid/analysis/TrackWidthAnalysis.h"
+
+TEST(TrackWidthAnalysisTest, Calculate) {
+  double result = sysid::CalculateTrackWidth(-0.5386, 0.5386, 90_deg);
+  EXPECT_NEAR(result, 0.6858, 1E-4);
+}
diff --git a/third_party/allwpilib/test-scripts/deploy-and-run-test-on-robot.sh b/third_party/allwpilib/test-scripts/deploy-and-run-test-on-robot.sh
index ba67d8b..1caf045 100755
--- a/third_party/allwpilib/test-scripts/deploy-and-run-test-on-robot.sh
+++ b/third_party/allwpilib/test-scripts/deploy-and-run-test-on-robot.sh
@@ -35,7 +35,7 @@
 LOCAL_TEST_RESULTS=none
 
 
-# Begin searching for options from the second paramater on
+# Begin searching for options from the second parameter on
 PARAM_ARGS=${@:2}
 
 if [[ "$1" = java ]]; then
diff --git a/third_party/allwpilib/test-scripts/run-tests-on-robot.sh b/third_party/allwpilib/test-scripts/run-tests-on-robot.sh
index 457818c..32b66b5 100755
--- a/third_party/allwpilib/test-scripts/run-tests-on-robot.sh
+++ b/third_party/allwpilib/test-scripts/run-tests-on-robot.sh
@@ -9,7 +9,7 @@
 # This file is intended to be run in the DEFAULT_TEST_DIR on the roboRIO.
 # Do not attempt to run this file on your local system.
 # There is one file (delploy-and-run-test-on-robot.sh) that is designed to
-# deploy this file allong with the compiled tests for you.
+# deploy this file along with the compiled tests for you.
 
 # Configurable variables
 source config.sh
@@ -24,9 +24,9 @@
     -h    Show this help text
     -d    The directory where the tests have been placed.
           This is done to prevent overwriting an already running program.
-          This scrip will automatically move the test into the ${DEFAULT_TEST_DIR}
+          This script will automatically move the test into the ${DEFAULT_TEST_DIR}
           directory.
-          Default: Assumes the test is in the same directory as this scrip.
+          Default: Assumes the test is in the same directory as this script.
     -A    Do not use the default arguments for the given language.
     arg   The arguments to be passed to test."
 
@@ -42,7 +42,7 @@
 LANGUAGE=none
 TEST_FILE=none
 TEST_DIR="$DEFAULT_TEST_DIR"
-# Begin searching for options from the second paramater on
+# Begin searching for options from the second parameter on
 PARAM_ARGS=${@:2}
 # Where the test arguments start
 TEST_RUN_ARGS=${@:2}
@@ -125,7 +125,7 @@
     mv "${TEST_DIR}/${TEST_FILE}" "${DEFAULT_TEST_DIR}"
 fi
 
-# Make sure the excecutable file has permission to run
+# Make sure the executable file has permission to run
 
 # Get the serial number and FPGADeviceCode for this rio
 export serialnum=`/sbin/fw_printenv -n serial#`
diff --git a/third_party/allwpilib/upstream_utils/README.md b/third_party/allwpilib/upstream_utils/README.md
index 587fc9e..979dd0d 100644
--- a/third_party/allwpilib/upstream_utils/README.md
+++ b/third_party/allwpilib/upstream_utils/README.md
@@ -11,10 +11,9 @@
 
 `upstream_utils.py` contains utilities common to these update scripts.
 
-Patches are generated in the thirdparty repo with
-`git format-patch --no-signature` so they can be applied as individual commits
-and easily rebased onto newer versions. Each library has its own patch directory
-(e.g., `lib_patches`).
+Patches are generated in the thirdparty repo with git's format-patch command so
+they can be applied as individual commits and easily rebased onto newer
+versions. Each library has its own patch directory (e.g., `lib_patches`).
 
 ## Updating thirdparty library version
 
@@ -23,7 +22,7 @@
 
 Start in the `upstream_utils` folder. Restore the original repo.
 ```bash
-./update_lib.py
+./update_<lib>.py
 ```
 
 Navigate to the repo.
@@ -40,14 +39,23 @@
 git fetch --depth 1 origin tag 2.0
 ```
 
-Rebase any patches onto the new version.
+Rebase any patches onto the new version. If the old version and new version are
+on the same branch, run the following.
 ```bash
 git rebase 2.0
 ```
 
+If the old version and new version are on different branches (e.g.,
+llvm-project), use interactive rebase instead and remove commits that are common
+between the two branches from the list of commits to rebase. In other words,
+only commits representing downstream patches should be listed.
+```bash
+git rebase -i 2.0
+```
+
 Generate patch files for the new version.
 ```bash
-git format-patch 2.0..HEAD
+git format-patch 2.0..HEAD --zero-commit --abbrev=40 --no-signature
 ```
 
 Move the patch files to `upstream_utils`.
@@ -55,13 +63,57 @@
 mv *.patch allwpilib/upstream_utils/lib_patches
 ```
 
-Navigate back to `upstream_utils`
+Navigate back to `upstream_utils`.
 ```bash
 cd allwpilib/upstream_utils
 ```
 
 Modify the version number in the call to `setup_upstream_repo()` in
-`update_lib.py`, then  rerun `update_lib.py` to reimport the thirdparty files.
+`update_<lib>.py`, then rerun `update_<lib>.py` to reimport the thirdparty
+files.
 ```bash
-./update_lib.py
+./update_<lib>.py
+```
+
+## Adding patch to thirdparty library
+
+The example below will add a new patch file to a hypothetical library called
+`lib` (Replace `<lib>` with `llvm`, `fmt`, `eigen`, ... in the following steps).
+
+Start in the `upstream_utils` folder. Restore the original repo.
+```bash
+./update_<lib>.py
+```
+
+Navigate to the repo.
+```bash
+cd /tmp/<lib>
+```
+
+Make a commit with the desired changes.
+```bash
+git add ...
+git commit -m "..."
+```
+
+Generate patch files.
+```bash
+git format-patch 2.0..HEAD --zero-commit --abbrev=40 --no-signature
+```
+where `2.0` is replaced with the version specified in `update_<lib>.py`.
+
+Move the patch files to `upstream_utils`.
+```
+mv *.patch allwpilib/upstream_utils/<lib>_patches
+```
+
+Navigate back to `upstream_utils`.
+```bash
+cd allwpilib/upstream_utils
+```
+
+Update the list of patch files in `update_<lib>.py`, then rerun
+`update_<lib>.py` to reimport the thirdparty files.
+```bash
+./update_<lib>.py
 ```
diff --git a/third_party/allwpilib/upstream_utils/drake_patches/0001-Replace-Eigen-Dense-with-Eigen-Core.patch b/third_party/allwpilib/upstream_utils/drake_patches/0001-Replace-Eigen-Dense-with-Eigen-Core.patch
deleted file mode 100644
index dcd2e8f..0000000
--- a/third_party/allwpilib/upstream_utils/drake_patches/0001-Replace-Eigen-Dense-with-Eigen-Core.patch
+++ /dev/null
@@ -1,76 +0,0 @@
-From 02d023c7cdfdfb72ccdbccbac0883b4a1f6ec6d5 Mon Sep 17 00:00:00 2001
-From: Tyler Veness <calcmogul@gmail.com>
-Date: Wed, 18 May 2022 11:13:21 -0700
-Subject: [PATCH 1/2] Replace <Eigen/Dense> with <Eigen/Core>
-
----
- common/is_approx_equal_abstol.h                       | 2 +-
- common/test_utilities/eigen_matrix_compare.h          | 2 +-
- math/discrete_algebraic_riccati_equation.cc           | 3 +++
- math/discrete_algebraic_riccati_equation.h            | 2 +-
- math/test/discrete_algebraic_riccati_equation_test.cc | 1 +
- 5 files changed, 7 insertions(+), 3 deletions(-)
-
-diff --git a/common/is_approx_equal_abstol.h b/common/is_approx_equal_abstol.h
-index 9af0c45..b3f369c 100644
---- a/common/is_approx_equal_abstol.h
-+++ b/common/is_approx_equal_abstol.h
-@@ -2,7 +2,7 @@
- 
- #include <vector>
- 
--#include <Eigen/Dense>
-+#include <Eigen/Core>
- 
- namespace drake {
- 
-diff --git a/common/test_utilities/eigen_matrix_compare.h b/common/test_utilities/eigen_matrix_compare.h
-index a595da9..c22567d 100644
---- a/common/test_utilities/eigen_matrix_compare.h
-+++ b/common/test_utilities/eigen_matrix_compare.h
-@@ -4,7 +4,7 @@
- #include <cmath>
- #include <limits>
- 
--#include <Eigen/Dense>
-+#include <Eigen/Core>
- #include <gtest/gtest.h>
- 
- #include "drake/common/text_logging.h"
-diff --git a/math/discrete_algebraic_riccati_equation.cc b/math/discrete_algebraic_riccati_equation.cc
-index 901f2ef..20ea2b7 100644
---- a/math/discrete_algebraic_riccati_equation.cc
-+++ b/math/discrete_algebraic_riccati_equation.cc
-@@ -1,5 +1,8 @@
- #include "drake/math/discrete_algebraic_riccati_equation.h"
- 
-+#include <Eigen/Eigenvalues>
-+#include <Eigen/QR>
-+
- #include "drake/common/drake_assert.h"
- #include "drake/common/drake_throw.h"
- #include "drake/common/is_approx_equal_abstol.h"
-diff --git a/math/discrete_algebraic_riccati_equation.h b/math/discrete_algebraic_riccati_equation.h
-index 891373f..df7a58b 100644
---- a/math/discrete_algebraic_riccati_equation.h
-+++ b/math/discrete_algebraic_riccati_equation.h
-@@ -3,7 +3,7 @@
- #include <cmath>
- #include <cstdlib>
- 
--#include <Eigen/Dense>
-+#include <Eigen/Core>
- 
- namespace drake {
- namespace math {
-diff --git a/math/test/discrete_algebraic_riccati_equation_test.cc b/math/test/discrete_algebraic_riccati_equation_test.cc
-index 533ced1..e4ecfd2 100644
---- a/math/test/discrete_algebraic_riccati_equation_test.cc
-+++ b/math/test/discrete_algebraic_riccati_equation_test.cc
-@@ -1,5 +1,6 @@
- #include "drake/math/discrete_algebraic_riccati_equation.h"
- 
-+#include <Eigen/Eigenvalues>
- #include <gtest/gtest.h>
- 
- #include "drake/common/test_utilities/eigen_matrix_compare.h"
diff --git a/third_party/allwpilib/upstream_utils/drake_patches/0002-Add-WPILIB_DLLEXPORT-to-DARE-function-declarations.patch b/third_party/allwpilib/upstream_utils/drake_patches/0002-Add-WPILIB_DLLEXPORT-to-DARE-function-declarations.patch
deleted file mode 100644
index 1c7b469..0000000
--- a/third_party/allwpilib/upstream_utils/drake_patches/0002-Add-WPILIB_DLLEXPORT-to-DARE-function-declarations.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From b208372a18b37f6cbc49dd45d15adf63c9b60755 Mon Sep 17 00:00:00 2001
-From: Tyler Veness <calcmogul@gmail.com>
-Date: Wed, 18 May 2022 11:15:27 -0700
-Subject: [PATCH 2/2] Add WPILIB_DLLEXPORT to DARE function declarations
-
----
- math/discrete_algebraic_riccati_equation.h | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/math/discrete_algebraic_riccati_equation.h b/math/discrete_algebraic_riccati_equation.h
-index df7a58b..55b8442 100644
---- a/math/discrete_algebraic_riccati_equation.h
-+++ b/math/discrete_algebraic_riccati_equation.h
-@@ -4,6 +4,7 @@
- #include <cstdlib>
- 
- #include <Eigen/Core>
-+#include <wpi/SymbolExports.h>
- 
- namespace drake {
- namespace math {
-@@ -21,6 +22,7 @@ Based on the Schur Vector approach outlined in this paper:
- "On the Numerical Solution of the Discrete-Time Algebraic Riccati Equation"
- by Thrasyvoulos Pappas, Alan J. Laub, and Nils R. Sandell
- */
-+WPILIB_DLLEXPORT
- Eigen::MatrixXd DiscreteAlgebraicRiccatiEquation(
-     const Eigen::Ref<const Eigen::MatrixXd>& A,
-     const Eigen::Ref<const Eigen::MatrixXd>& B,
-@@ -71,6 +73,7 @@ J = Σ [uₖ] [0 R][uₖ] ΔT
- @throws std::runtime_error if Q − NR⁻¹Nᵀ is not positive semi-definite.
- @throws std::runtime_error if R is not positive definite.
- */
-+WPILIB_DLLEXPORT
- Eigen::MatrixXd DiscreteAlgebraicRiccatiEquation(
-     const Eigen::Ref<const Eigen::MatrixXd>& A,
-     const Eigen::Ref<const Eigen::MatrixXd>& B,
diff --git a/third_party/allwpilib/upstream_utils/eigen_patches/0001-Disable-warnings.patch b/third_party/allwpilib/upstream_utils/eigen_patches/0001-Disable-warnings.patch
index 78a5922..e89fec5 100644
--- a/third_party/allwpilib/upstream_utils/eigen_patches/0001-Disable-warnings.patch
+++ b/third_party/allwpilib/upstream_utils/eigen_patches/0001-Disable-warnings.patch
@@ -1,14 +1,14 @@
-From 3bfc3d1e3cbc9d7032446cc4aa6246d1c7750901 Mon Sep 17 00:00:00 2001
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Tyler Veness <calcmogul@gmail.com>
 Date: Wed, 18 May 2022 09:14:24 -0700
-Subject: [PATCH] Disable warnings
+Subject: [PATCH 1/3] Disable warnings
 
 ---
  Eigen/src/Core/util/DisableStupidWarnings.h | 11 +++++++++++
  1 file changed, 11 insertions(+)
 
 diff --git a/Eigen/src/Core/util/DisableStupidWarnings.h b/Eigen/src/Core/util/DisableStupidWarnings.h
-index fe0cfec..d973255 100755
+index fe0cfec0bc2461ac44abca8f3d05b468d3c60fd9..9a630e4ae692aee0277d60b3083c968d087920dd 100755
 --- a/Eigen/src/Core/util/DisableStupidWarnings.h
 +++ b/Eigen/src/Core/util/DisableStupidWarnings.h
 @@ -71,6 +71,17 @@
@@ -22,7 +22,7 @@
 +    // This warning is a false positive
 +    #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
 +  #endif
-+  #if __GNUC__==12
++  #if __GNUC__>=12
 +    // This warning is a false positive
 +    #pragma GCC diagnostic ignored "-Warray-bounds"
 +  #endif
diff --git a/third_party/allwpilib/upstream_utils/eigen_patches/0002-Intellisense-fix.patch b/third_party/allwpilib/upstream_utils/eigen_patches/0002-Intellisense-fix.patch
new file mode 100644
index 0000000..ce4cdb6
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/eigen_patches/0002-Intellisense-fix.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Fri, 20 Jan 2023 23:41:56 -0800
+Subject: [PATCH 2/3] Intellisense fix
+
+---
+ Eigen/src/Core/util/Macros.h | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/Eigen/src/Core/util/Macros.h b/Eigen/src/Core/util/Macros.h
+index 986c3d44db94c8ba339792b6738c47cdd2c5acbc..81986b9447824c440e004f38a220393ef5a089c6 100644
+--- a/Eigen/src/Core/util/Macros.h
++++ b/Eigen/src/Core/util/Macros.h
+@@ -58,6 +58,16 @@
+ // Compiler identification, EIGEN_COMP_*
+ //------------------------------------------------------------------------------------------
+ 
++/// \internal Disable NEON features in Intellisense
++#if __INTELLISENSE__
++#ifdef __ARM_NEON
++#undef __ARM_NEON
++#endif
++#ifdef __ARM_NEON__
++#undef __ARM_NEON__
++#endif
++#endif
++
+ /// \internal EIGEN_COMP_GNUC set to 1 for all compilers compatible with GCC
+ #ifdef __GNUC__
+   #define EIGEN_COMP_GNUC (__GNUC__*10+__GNUC_MINOR__)
diff --git a/third_party/allwpilib/upstream_utils/eigen_patches/0003-Eigen-Sparse-fix-warnings-Wunused-but-set-variable.patch b/third_party/allwpilib/upstream_utils/eigen_patches/0003-Eigen-Sparse-fix-warnings-Wunused-but-set-variable.patch
new file mode 100644
index 0000000..d68f094
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/eigen_patches/0003-Eigen-Sparse-fix-warnings-Wunused-but-set-variable.patch
@@ -0,0 +1,65 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Laurent Rineau <laurent.rineau@cgal.org>
+Date: Tue, 11 Oct 2022 17:37:04 +0000
+Subject: [PATCH 3/3] Eigen/Sparse: fix warnings -Wunused-but-set-variable
+
+---
+ Eigen/src/SparseCore/TriangularSolver.h        | 4 ++--
+ Eigen/src/SparseLU/SparseLU_heap_relax_snode.h | 5 -----
+ 2 files changed, 2 insertions(+), 7 deletions(-)
+
+diff --git a/Eigen/src/SparseCore/TriangularSolver.h b/Eigen/src/SparseCore/TriangularSolver.h
+index f9c56ba79800e209dcf3f18ba37dbb8023488bca..7cb2c2665f0e24924da88f11c0fe3ca0c0af52e3 100644
+--- a/Eigen/src/SparseCore/TriangularSolver.h
++++ b/Eigen/src/SparseCore/TriangularSolver.h
+@@ -270,11 +270,11 @@ struct sparse_solve_triangular_sparse_selector<Lhs,Rhs,Mode,UpLo,ColMajor>
+       }
+ 
+ 
+-      Index count = 0;
++//       Index count = 0;
+       // FIXME compute a reference value to filter zeros
+       for (typename AmbiVector<Scalar,StorageIndex>::Iterator it(tempVector/*,1e-12*/); it; ++it)
+       {
+-        ++ count;
++//         ++ count;
+ //         std::cerr << "fill " << it.index() << ", " << col << "\n";
+ //         std::cout << it.value() << "  ";
+         // FIXME use insertBack
+diff --git a/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h b/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h
+index 6f75d500e5f831f414175ce46dbceffa0acd5539..7aecbcad8ed2703000d62cfd5d88d983c69a7423 100644
+--- a/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h
++++ b/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h
+@@ -75,8 +75,6 @@ void SparseLUImpl<Scalar,StorageIndex>::heap_relax_snode (const Index n, IndexVe
+   // Identify the relaxed supernodes by postorder traversal of the etree
+   Index snode_start; // beginning of a snode 
+   StorageIndex k;
+-  Index nsuper_et_post = 0; // Number of relaxed snodes in postordered etree 
+-  Index nsuper_et = 0; // Number of relaxed snodes in the original etree 
+   StorageIndex l; 
+   for (j = 0; j < n; )
+   {
+@@ -88,7 +86,6 @@ void SparseLUImpl<Scalar,StorageIndex>::heap_relax_snode (const Index n, IndexVe
+       parent = et(j);
+     }
+     // Found a supernode in postordered etree, j is the last column 
+-    ++nsuper_et_post;
+     k = StorageIndex(n);
+     for (Index i = snode_start; i <= j; ++i)
+       k = (std::min)(k, inv_post(i));
+@@ -97,7 +94,6 @@ void SparseLUImpl<Scalar,StorageIndex>::heap_relax_snode (const Index n, IndexVe
+     {
+       // This is also a supernode in the original etree
+       relax_end(k) = l; // Record last column 
+-      ++nsuper_et; 
+     }
+     else 
+     {
+@@ -107,7 +103,6 @@ void SparseLUImpl<Scalar,StorageIndex>::heap_relax_snode (const Index n, IndexVe
+         if (descendants(i) == 0) 
+         {
+           relax_end(l) = l;
+-          ++nsuper_et;
+         }
+       }
+     }
diff --git a/third_party/allwpilib/upstream_utils/fmt_patches/0001-Don-t-throw-on-write-failure.patch b/third_party/allwpilib/upstream_utils/fmt_patches/0001-Don-t-throw-on-write-failure.patch
index edaf575..5b05dbf 100644
--- a/third_party/allwpilib/upstream_utils/fmt_patches/0001-Don-t-throw-on-write-failure.patch
+++ b/third_party/allwpilib/upstream_utils/fmt_patches/0001-Don-t-throw-on-write-failure.patch
@@ -1,4 +1,4 @@
-From e685209746aabbbed0a9db54694b8ea1ca504163 Mon Sep 17 00:00:00 2001
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Tyler Veness <calcmogul@gmail.com>
 Date: Wed, 18 May 2022 10:21:49 -0700
 Subject: [PATCH 1/2] Don't throw on write failure
@@ -6,14 +6,14 @@
 ---
  include/fmt/format-inl.h | 4 +---
  include/fmt/xchar.h      | 3 +--
- src/os.cc                | 3 +--
- 3 files changed, 3 insertions(+), 7 deletions(-)
+ src/os.cc                | 4 +---
+ 3 files changed, 3 insertions(+), 8 deletions(-)
 
 diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h
-index 22b1ec8..abe4ff1 100644
+index dac2d437a41ab7b0b4e72895212b5a972ada73a9..af6ba74d618f29c77339e8a82906cccd26a2efa4 100644
 --- a/include/fmt/format-inl.h
 +++ b/include/fmt/format-inl.h
-@@ -79,9 +79,7 @@ FMT_FUNC void report_error(format_func func, int error_code,
+@@ -75,9 +75,7 @@ FMT_FUNC void report_error(format_func func, int error_code,
  // A wrapper around fwrite that throws on error.
  inline void fwrite_fully(const void* ptr, size_t size, size_t count,
                           FILE* stream) {
@@ -25,30 +25,31 @@
  
  #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
 diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h
-index 3b5bc15..fc3c67f 100644
+index 625ec36922e9bcc44a76b3c40792cb08ede63813..0f79c1720a4c855bb7088381e93af08eae56d66c 100644
 --- a/include/fmt/xchar.h
 +++ b/include/fmt/xchar.h
-@@ -200,8 +200,7 @@ inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
-   wmemory_buffer buffer;
-   detail::vformat_to(buffer, fmt, args);
-   buffer.push_back(L'\0');
--  if (std::fputws(buffer.data(), f) == -1)
+@@ -220,8 +220,7 @@ inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
+   auto buf = wmemory_buffer();
+   detail::vformat_to(buf, fmt, args);
+   buf.push_back(L'\0');
+-  if (std::fputws(buf.data(), f) == -1)
 -    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
-+  std::fputws(buffer.data(), f);
++  std::fputws(buf.data(), f);
  }
  
  inline void vprint(wstring_view fmt, wformat_args args) {
 diff --git a/src/os.cc b/src/os.cc
-index f388ead..2c49951 100644
+index bca410e945e0347d349e06179906a43b38b56a5c..d7ded50f9870a885d1ce1835fecc4f740858127a 100644
 --- a/src/os.cc
 +++ b/src/os.cc
-@@ -277,8 +277,7 @@ std::size_t file::read(void* buffer, std::size_t count) {
- std::size_t file::write(const void* buffer, std::size_t count) {
+@@ -258,9 +258,7 @@ long long file::size() const {
+ std::size_t file::read(void* buffer, std::size_t count) {
    rwresult result = 0;
-   FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
--  if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
+   FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
+-  if (result < 0)
+-    FMT_THROW(system_error(errno, FMT_STRING("cannot read from file")));
 -  return detail::to_unsigned(result);
 +  return count;
  }
  
- file file::dup(int fd) {
+ std::size_t file::write(const void* buffer, std::size_t count) {
diff --git a/third_party/allwpilib/upstream_utils/fmt_patches/0002-Suppress-C-20-clang-tidy-warning-false-positive.patch b/third_party/allwpilib/upstream_utils/fmt_patches/0002-Suppress-C-20-clang-tidy-warning-false-positive.patch
deleted file mode 100644
index 7e25fc0..0000000
--- a/third_party/allwpilib/upstream_utils/fmt_patches/0002-Suppress-C-20-clang-tidy-warning-false-positive.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From 1d8e07241d380d13383a6ff479f3895ef49ce514 Mon Sep 17 00:00:00 2001
-From: Tyler Veness <calcmogul@gmail.com>
-Date: Fri, 2 Sep 2022 15:12:54 -0700
-Subject: [PATCH 2/2] Suppress C++20 clang-tidy warning false positive
-
----
- include/fmt/core.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/fmt/core.h b/include/fmt/core.h
-index f6a37af..5c210bc 100644
---- a/include/fmt/core.h
-+++ b/include/fmt/core.h
-@@ -2952,7 +2952,7 @@ class format_string_checker {
-       basic_string_view<Char> format_str, ErrorHandler eh)
-       : context_(format_str, num_args, types_, eh),
-         parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
--        types_{
-+        types_{  // NOLINT(clang-analyzer-optin.cplusplus.UninitializedObject)
-             mapped_type_constant<Args,
-                                  basic_format_context<Char*, Char>>::value...} {
-   }
diff --git a/third_party/allwpilib/upstream_utils/fmt_patches/0002-Suppress-warnings-we-can-t-fix.patch b/third_party/allwpilib/upstream_utils/fmt_patches/0002-Suppress-warnings-we-can-t-fix.patch
new file mode 100644
index 0000000..a3866e2
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/fmt_patches/0002-Suppress-warnings-we-can-t-fix.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Tue, 16 May 2023 13:49:18 -0700
+Subject: [PATCH 2/2] Suppress warnings we can't fix
+
+---
+ include/fmt/format.h | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/include/fmt/format.h b/include/fmt/format.h
+index e5bd8b110efe49e12a12b004ea246a4dba671a6f..f11be0d6d58f3d992d7d06adb3d9576f81ecfe11 100644
+--- a/include/fmt/format.h
++++ b/include/fmt/format.h
+@@ -1324,7 +1324,14 @@ inline auto equal2(const char* lhs, const char* rhs) -> bool {
+ template <typename Char>
+ FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) {
+   if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) {
++#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1000
++#  pragma GCC diagnostic push
++#  pragma GCC diagnostic ignored "-Wstringop-overflow"
++#endif
+     memcpy(dst, src, 2);
++#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1000
++#  pragma GCC diagnostic pop
++#endif
+     return;
+   }
+   *dst++ = static_cast<Char>(*src++);
diff --git a/third_party/allwpilib/upstream_utils/json_patches/0001-Remove-version-from-namespace.patch b/third_party/allwpilib/upstream_utils/json_patches/0001-Remove-version-from-namespace.patch
new file mode 100644
index 0000000..64e3efc
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/json_patches/0001-Remove-version-from-namespace.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Fri, 8 Sep 2023 19:21:41 -0700
+Subject: [PATCH 1/4] Remove version from namespace
+
+---
+ include/nlohmann/detail/abi_macros.hpp | 45 ++------------------------
+ 1 file changed, 3 insertions(+), 42 deletions(-)
+
+diff --git a/include/nlohmann/detail/abi_macros.hpp b/include/nlohmann/detail/abi_macros.hpp
+index 0d3108d166602886d41b5f0fec1e56dd3dbe7e3c..ce9291306cdd9a9baeb8fbb77ca1dc33959e0d36 100644
+--- a/include/nlohmann/detail/abi_macros.hpp
++++ b/include/nlohmann/detail/abi_macros.hpp
+@@ -42,40 +42,6 @@
+     #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
+ #endif
+ 
+-#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
+-    #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
+-#endif
+-
+-// Construct the namespace ABI tags component
+-#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
+-#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
+-    NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
+-
+-#define NLOHMANN_JSON_ABI_TAGS                                       \
+-    NLOHMANN_JSON_ABI_TAGS_CONCAT(                                   \
+-            NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS,                       \
+-            NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
+-
+-// Construct the namespace version component
+-#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
+-    _v ## major ## _ ## minor ## _ ## patch
+-#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
+-    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
+-
+-#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
+-#define NLOHMANN_JSON_NAMESPACE_VERSION
+-#else
+-#define NLOHMANN_JSON_NAMESPACE_VERSION                                 \
+-    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
+-                                           NLOHMANN_JSON_VERSION_MINOR, \
+-                                           NLOHMANN_JSON_VERSION_PATCH)
+-#endif
+-
+-// Combine namespace components
+-#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
+-#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
+-    NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
+-
+ #ifndef NLOHMANN_JSON_NAMESPACE
+ #define NLOHMANN_JSON_NAMESPACE               \
+     nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
+@@ -84,17 +50,12 @@
+ #endif
+ 
+ #ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
+-#define NLOHMANN_JSON_NAMESPACE_BEGIN                \
+-    namespace nlohmann                               \
+-    {                                                \
+-    inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
+-                NLOHMANN_JSON_ABI_TAGS,              \
+-                NLOHMANN_JSON_NAMESPACE_VERSION)     \
++#define NLOHMANN_JSON_NAMESPACE_BEGIN \
++    namespace nlohmann                \
+     {
+ #endif
+ 
+ #ifndef NLOHMANN_JSON_NAMESPACE_END
+-#define NLOHMANN_JSON_NAMESPACE_END                                     \
+-    }  /* namespace (inline namespace) NOLINT(readability/namespace) */ \
++#define NLOHMANN_JSON_NAMESPACE_END \
+     }  // namespace nlohmann
+ #endif
diff --git a/third_party/allwpilib/upstream_utils/json_patches/0002-Make-serializer-public.patch b/third_party/allwpilib/upstream_utils/json_patches/0002-Make-serializer-public.patch
new file mode 100644
index 0000000..cb6ad1c
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/json_patches/0002-Make-serializer-public.patch
@@ -0,0 +1,55 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Thu, 7 Sep 2023 22:02:27 -0700
+Subject: [PATCH 2/4] Make serializer public
+
+---
+ include/nlohmann/detail/output/serializer.hpp | 4 +++-
+ include/nlohmann/json.hpp                     | 3 +--
+ 2 files changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp
+index 500fc55ec5e5895ead2b372a6fe79ae941d88d83..7674d134a4d9f230aa4e432294c19dac8dd366b1 100644
+--- a/include/nlohmann/detail/output/serializer.hpp
++++ b/include/nlohmann/detail/output/serializer.hpp
+@@ -373,7 +373,7 @@ class serializer
+         }
+     }
+ 
+-  JSON_PRIVATE_UNLESS_TESTED:
++  public:
+     /*!
+     @brief dump escaped string
+ 
+@@ -696,6 +696,7 @@ class serializer
+         return false;
+     }
+ 
++  public:
+     /*!
+     @brief dump an integer
+ 
+@@ -876,6 +877,7 @@ class serializer
+         }
+     }
+ 
++  private:
+     /*!
+     @brief check whether a string is UTF-8 encoded
+ 
+diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
+index 18a7c875774527a2e08c5ab72e5564aa50381608..c462cade8a7167a00697f6f940be35c5609a283c 100644
+--- a/include/nlohmann/json.hpp
++++ b/include/nlohmann/json.hpp
+@@ -153,10 +153,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
+     using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;
+     template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
+ 
+-  JSON_PRIVATE_UNLESS_TESTED:
++  public:
+     using serializer = ::nlohmann::detail::serializer<basic_json>;
+ 
+-  public:
+     using value_t = detail::value_t;
+     /// JSON Pointer, see @ref nlohmann::json_pointer
+     using json_pointer = ::nlohmann::json_pointer<StringType>;
diff --git a/third_party/allwpilib/upstream_utils/json_patches/0003-Make-dump_escaped-take-std-string_view.patch b/third_party/allwpilib/upstream_utils/json_patches/0003-Make-dump_escaped-take-std-string_view.patch
new file mode 100644
index 0000000..84ba3ea
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/json_patches/0003-Make-dump_escaped-take-std-string_view.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Fri, 8 Sep 2023 21:42:01 -0700
+Subject: [PATCH 3/4] Make dump_escaped() take std::string_view
+
+---
+ include/nlohmann/detail/output/serializer.hpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp
+index 7674d134a4d9f230aa4e432294c19dac8dd366b1..ecc4f7d500b9e0bc15917503061a4db100391366 100644
+--- a/include/nlohmann/detail/output/serializer.hpp
++++ b/include/nlohmann/detail/output/serializer.hpp
+@@ -388,7 +388,7 @@ class serializer
+ 
+     @complexity Linear in the length of string @a s.
+     */
+-    void dump_escaped(const string_t& s, const bool ensure_ascii)
++    void dump_escaped(std::string_view s, const bool ensure_ascii)
+     {
+         std::uint32_t codepoint{};
+         std::uint8_t state = UTF8_ACCEPT;
diff --git a/third_party/allwpilib/upstream_utils/json_patches/0004-Add-llvm-stream-support.patch b/third_party/allwpilib/upstream_utils/json_patches/0004-Add-llvm-stream-support.patch
new file mode 100644
index 0000000..c47586f
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/json_patches/0004-Add-llvm-stream-support.patch
@@ -0,0 +1,133 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Wed, 20 Sep 2023 02:23:10 -0400
+Subject: [PATCH 4/4] Add llvm stream support
+
+---
+ .../detail/output/output_adapters.hpp         | 26 +++++++++++++++++++
+ include/nlohmann/detail/output/serializer.hpp | 11 ++++++--
+ include/nlohmann/json.hpp                     | 24 +++++++++++++++++
+ 3 files changed, 59 insertions(+), 2 deletions(-)
+
+diff --git a/include/nlohmann/detail/output/output_adapters.hpp b/include/nlohmann/detail/output/output_adapters.hpp
+index 630bd8f73f38b7bf18be571217873f6215e6e31a..78addc557eec3b2a31cde78fb4c6f7f6efc7e777 100644
+--- a/include/nlohmann/detail/output/output_adapters.hpp
++++ b/include/nlohmann/detail/output/output_adapters.hpp
+@@ -22,6 +22,8 @@
+ 
+ #include <nlohmann/detail/macro_scope.hpp>
+ 
++#include <wpi/raw_ostream.h>
++
+ NLOHMANN_JSON_NAMESPACE_BEGIN
+ namespace detail
+ {
+@@ -118,6 +120,27 @@ class output_string_adapter : public output_adapter_protocol<CharType>
+     StringType& str;
+ };
+ 
++template<typename CharType>
++class raw_ostream_adapter : public output_adapter_protocol<CharType>
++{
++  public:
++    explicit raw_ostream_adapter(raw_ostream& s) noexcept
++        : os(s) {}
++
++
++    void write_character(CharType c) override {
++        os << c;
++    }
++
++    JSON_HEDLEY_NON_NULL(2)
++    void write_characters(const CharType* s, std::size_t length) override {
++        os.write(s, length);
++    }
++
++  private:
++    raw_ostream& os;
++};
++
+ template<typename CharType, typename StringType = std::basic_string<CharType>>
+ class output_adapter
+ {
+@@ -134,6 +157,9 @@ class output_adapter
+     output_adapter(StringType& s)
+         : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
+ 
++    output_adapter(raw_ostream& os)
++        : oa(std::make_shared<raw_ostream_adapter<CharType>>(os)) {}
++
+     operator output_adapter_t<CharType>()
+     {
+         return oa;
+diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp
+index ecc4f7d500b9e0bc15917503061a4db100391366..bb392a985b57b79020c949593c155052a4271d6b 100644
+--- a/include/nlohmann/detail/output/serializer.hpp
++++ b/include/nlohmann/detail/output/serializer.hpp
+@@ -65,15 +65,22 @@ class serializer
+     @param[in] error_handler_  how to react on decoding errors
+     */
+     serializer(output_adapter_t<char> s, const char ichar,
+-               error_handler_t error_handler_ = error_handler_t::strict)
++               error_handler_t error_handler_ = error_handler_t::strict,
++               size_t indent_init_len = 512)
+         : o(std::move(s))
+         , loc(std::localeconv())
+         , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
+         , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
+         , indent_char(ichar)
+-        , indent_string(512, indent_char)
++        , indent_string(indent_init_len, indent_char)
+         , error_handler(error_handler_)
+     {}
++    
++    serializer(raw_ostream& os, const char ichar,
++               size_t indent_init_len = 512,
++               error_handler_t error_handler_ = error_handler_t::strict)
++        : serializer(output_adapter<char>(os), ichar, error_handler_, indent_init_len)
++    {}
+ 
+     // delete because of pointer members
+     serializer(const serializer&) = delete;
+diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
+index c462cade8a7167a00697f6f940be35c5609a283c..ad98956ba880f844ed1a17765266880f6ea08b2f 100644
+--- a/include/nlohmann/json.hpp
++++ b/include/nlohmann/json.hpp
+@@ -1275,6 +1275,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
+         return result;
+     }
+ 
++    void dump(raw_ostream& os, const int indent = -1,
++                  const char indent_char = ' ',
++                  const bool ensure_ascii = false,
++                  const error_handler_t error_handler = error_handler_t::strict) 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();
++    }
++
+     /// @brief return the type of the JSON value (explicit)
+     /// @sa https://json.nlohmann.me/api/basic_json/type/
+     constexpr value_t type() const noexcept
+@@ -3990,6 +4008,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
+         return o << j;
+     }
+ #endif  // JSON_NO_IO
++    
++    friend raw_ostream& operator<<(raw_ostream& o, const basic_json& j)
++    {
++        j.dump(o, 0);
++        return o;
++    }
+     /// @}
+ 
+ 
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0001-Fix-missing-casts.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0001-Fix-missing-casts.patch
deleted file mode 100644
index 6965d0d..0000000
--- a/third_party/allwpilib/upstream_utils/libuv_patches/0001-Fix-missing-casts.patch
+++ /dev/null
@@ -1,1533 +0,0 @@
-From d5613423f057b088f6b3753f49162947d5559ad9 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Tue, 26 Apr 2022 15:01:25 -0400
-Subject: [PATCH 1/9] Fix missing casts
-
----
- include/uv/unix.h              |  2 +-
- src/fs-poll.c                  | 10 ++++-----
- src/inet.c                     | 11 +++++-----
- src/strscpy.c                  |  2 +-
- src/threadpool.c               |  2 +-
- src/unix/bsd-ifaddrs.c         |  2 +-
- src/unix/core.c                | 12 +++++-----
- src/unix/darwin-proctitle.c    |  5 +++--
- src/unix/darwin.c              |  2 +-
- src/unix/epoll.c               |  2 +-
- src/unix/freebsd.c             |  4 ++--
- src/unix/fs.c                  | 20 ++++++++---------
- src/unix/fsevents.c            | 40 +++++++++++++++++-----------------
- src/unix/getaddrinfo.c         |  8 +++----
- src/unix/ibmi.c                |  2 +-
- src/unix/kqueue.c              |  6 ++---
- src/unix/linux-core.c          |  5 ++---
- src/unix/linux-inotify.c       |  4 ++--
- src/unix/loop.c                |  2 +-
- src/unix/netbsd.c              |  4 ++--
- src/unix/openbsd.c             |  4 ++--
- src/unix/pipe.c                |  4 ++--
- src/unix/poll.c                |  2 +-
- src/unix/posix-poll.c          |  2 +-
- src/unix/process.c             |  4 ++--
- src/unix/proctitle.c           |  2 +-
- src/unix/random-sysctl-linux.c |  2 +-
- src/unix/stream.c              | 35 ++++++++++++++---------------
- src/unix/thread.c              |  7 +++---
- src/unix/udp.c                 |  8 +++----
- src/uv-common.c                | 16 +++++++-------
- src/win/core.c                 |  8 ++++---
- src/win/fs-event.c             |  4 ++--
- src/win/fs-fd-hash-inl.h       |  2 +-
- src/win/fs.c                   | 26 +++++++++++-----------
- src/win/pipe.c                 | 10 ++++-----
- src/win/process.c              | 12 +++++-----
- src/win/tcp.c                  |  2 +-
- src/win/thread.c               |  4 ++--
- src/win/util.c                 | 29 ++++++++++++------------
- 40 files changed, 166 insertions(+), 162 deletions(-)
-
-diff --git a/include/uv/unix.h b/include/uv/unix.h
-index ea37d787..420be86c 100644
---- a/include/uv/unix.h
-+++ b/include/uv/unix.h
-@@ -223,7 +223,7 @@ typedef struct {
-   int backend_fd;                                                             \
-   void* pending_queue[2];                                                     \
-   void* watcher_queue[2];                                                     \
--  uv__io_t** watchers;                                                        \
-+  void** watchers;                                                            \
-   unsigned int nwatchers;                                                     \
-   unsigned int nfds;                                                          \
-   void* wq[2];                                                                \
-diff --git a/src/fs-poll.c b/src/fs-poll.c
-index 1bac1c56..5a39daed 100644
---- a/src/fs-poll.c
-+++ b/src/fs-poll.c
-@@ -77,7 +77,7 @@ int uv_fs_poll_start(uv_fs_poll_t* handle,
- 
-   loop = handle->loop;
-   len = strlen(path);
--  ctx = uv__calloc(1, sizeof(*ctx) + len);
-+  ctx = (struct poll_ctx*)uv__calloc(1, sizeof(*ctx) + len);
- 
-   if (ctx == NULL)
-     return UV_ENOMEM;
-@@ -101,7 +101,7 @@ int uv_fs_poll_start(uv_fs_poll_t* handle,
-     goto error;
- 
-   if (handle->poll_ctx != NULL)
--    ctx->previous = handle->poll_ctx;
-+    ctx->previous = (struct poll_ctx*)handle->poll_ctx;
-   handle->poll_ctx = ctx;
-   uv__handle_start(handle);
- 
-@@ -119,7 +119,7 @@ int uv_fs_poll_stop(uv_fs_poll_t* handle) {
-   if (!uv_is_active((uv_handle_t*)handle))
-     return 0;
- 
--  ctx = handle->poll_ctx;
-+  ctx = (struct poll_ctx*)handle->poll_ctx;
-   assert(ctx != NULL);
-   assert(ctx->parent_handle == handle);
- 
-@@ -144,7 +144,7 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
-     return UV_EINVAL;
-   }
- 
--  ctx = handle->poll_ctx;
-+  ctx = (struct poll_ctx*)handle->poll_ctx;
-   assert(ctx != NULL);
- 
-   required_len = strlen(ctx->path);
-@@ -244,7 +244,7 @@ static void timer_close_cb(uv_handle_t* timer) {
-     if (handle->poll_ctx == NULL && uv__is_closing(handle))
-       uv__make_close_pending((uv_handle_t*)handle);
-   } else {
--    for (last = handle->poll_ctx, it = last->previous;
-+    for (last = (struct poll_ctx*)handle->poll_ctx, it = last->previous;
-          it != ctx;
-          last = it, it = it->previous) {
-       assert(last->previous != NULL);
-diff --git a/src/inet.c b/src/inet.c
-index ddabf22f..ca8b6ac8 100644
---- a/src/inet.c
-+++ b/src/inet.c
-@@ -40,9 +40,9 @@ static int inet_pton6(const char *src, unsigned char *dst);
- int uv_inet_ntop(int af, const void* src, char* dst, size_t size) {
-   switch (af) {
-   case AF_INET:
--    return (inet_ntop4(src, dst, size));
-+    return (inet_ntop4((const unsigned char*)src, dst, size));
-   case AF_INET6:
--    return (inet_ntop6(src, dst, size));
-+    return (inet_ntop6((const unsigned char*)src, dst, size));
-   default:
-     return UV_EAFNOSUPPORT;
-   }
-@@ -154,10 +154,11 @@ int uv_inet_pton(int af, const char* src, void* dst) {
- 
-   switch (af) {
-   case AF_INET:
--    return (inet_pton4(src, dst));
-+    return (inet_pton4(src, (unsigned char*)dst));
-   case AF_INET6: {
-     int len;
--    char tmp[UV__INET6_ADDRSTRLEN], *s, *p;
-+    char tmp[UV__INET6_ADDRSTRLEN], *s;
-+    const char *p;
-     s = (char*) src;
-     p = strchr(src, '%');
-     if (p != NULL) {
-@@ -168,7 +169,7 @@ int uv_inet_pton(int af, const char* src, void* dst) {
-       memcpy(s, src, len);
-       s[len] = '\0';
-     }
--    return inet_pton6(s, dst);
-+    return inet_pton6(s, (unsigned char*)dst);
-   }
-   default:
-     return UV_EAFNOSUPPORT;
-diff --git a/src/strscpy.c b/src/strscpy.c
-index 20df6fcb..6b4cc3bc 100644
---- a/src/strscpy.c
-+++ b/src/strscpy.c
-@@ -27,7 +27,7 @@ ssize_t uv__strscpy(char* d, const char* s, size_t n) {
- 
-   for (i = 0; i < n; i++)
-     if ('\0' == (d[i] = s[i]))
--      return i > SSIZE_MAX ? UV_E2BIG : (ssize_t) i;
-+      return i > SSIZE_MAX ? (ssize_t) UV_E2BIG : (ssize_t) i;
- 
-   if (i == 0)
-     return 0;
-diff --git a/src/threadpool.c b/src/threadpool.c
-index e804c7c4..1241ace1 100644
---- a/src/threadpool.c
-+++ b/src/threadpool.c
-@@ -206,7 +206,7 @@ static void init_threads(void) {
- 
-   threads = default_threads;
-   if (nthreads > ARRAY_SIZE(default_threads)) {
--    threads = uv__malloc(nthreads * sizeof(threads[0]));
-+    threads = (uv_thread_t*)uv__malloc(nthreads * sizeof(threads[0]));
-     if (threads == NULL) {
-       nthreads = ARRAY_SIZE(default_threads);
-       threads = default_threads;
-diff --git a/src/unix/bsd-ifaddrs.c b/src/unix/bsd-ifaddrs.c
-index 11ca9559..c3dd71a1 100644
---- a/src/unix/bsd-ifaddrs.c
-+++ b/src/unix/bsd-ifaddrs.c
-@@ -92,7 +92,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
-   }
- 
-   /* Make sure the memory is initiallized to zero using calloc() */
--  *addresses = uv__calloc(*count, sizeof(**addresses));
-+  *addresses = (uv_interface_address_t*)uv__calloc(*count, sizeof(**addresses));
- 
-   if (*addresses == NULL) {
-     freeifaddrs(addrs);
-diff --git a/src/unix/core.c b/src/unix/core.c
-index 54c769f3..6353b0e3 100644
---- a/src/unix/core.c
-+++ b/src/unix/core.c
-@@ -824,7 +824,7 @@ static unsigned int next_power_of_two(unsigned int val) {
- }
- 
- static void maybe_resize(uv_loop_t* loop, unsigned int len) {
--  uv__io_t** watchers;
-+  void** watchers;
-   void* fake_watcher_list;
-   void* fake_watcher_count;
-   unsigned int nwatchers;
-@@ -843,8 +843,8 @@ static void maybe_resize(uv_loop_t* loop, unsigned int len) {
-   }
- 
-   nwatchers = next_power_of_two(len + 2) - 2;
--  watchers = uv__reallocf(loop->watchers,
--                          (nwatchers + 2) * sizeof(loop->watchers[0]));
-+  watchers = (void**)
-+      uv__reallocf(loop->watchers, (nwatchers + 2) * sizeof(loop->watchers[0]));
- 
-   if (watchers == NULL)
-     abort();
-@@ -1184,7 +1184,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
-    * is frequently 1024 or 4096, so we can just use that directly. The pwent
-    * will not usually be large. */
-   for (bufsize = 2000;; bufsize *= 2) {
--    buf = uv__malloc(bufsize);
-+    buf = (char*)uv__malloc(bufsize);
- 
-     if (buf == NULL)
-       return UV_ENOMEM;
-@@ -1210,7 +1210,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
-   name_size = strlen(pw.pw_name) + 1;
-   homedir_size = strlen(pw.pw_dir) + 1;
-   shell_size = strlen(pw.pw_shell) + 1;
--  pwd->username = uv__malloc(name_size + homedir_size + shell_size);
-+  pwd->username = (char*)uv__malloc(name_size + homedir_size + shell_size);
- 
-   if (pwd->username == NULL) {
-     uv__free(buf);
-@@ -1274,7 +1274,7 @@ int uv_os_environ(uv_env_item_t** envitems, int* count) {
- 
-   for (i = 0; environ[i] != NULL; i++);
- 
--  *envitems = uv__calloc(i, sizeof(**envitems));
-+  *envitems = (uv_env_item_s*)uv__calloc(i, sizeof(**envitems));
- 
-   if (*envitems == NULL)
-     return UV_ENOMEM;
-diff --git a/src/unix/darwin-proctitle.c b/src/unix/darwin-proctitle.c
-index 5288083e..9bd55dd7 100644
---- a/src/unix/darwin-proctitle.c
-+++ b/src/unix/darwin-proctitle.c
-@@ -128,8 +128,9 @@ int uv__set_process_title(const char* title) {
-   if (pLSSetApplicationInformationItem == NULL)
-     goto out;
- 
--  display_name_key = pCFBundleGetDataPointerForName(launch_services_bundle,
--                                                    S("_kLSDisplayNameKey"));
-+  display_name_key = (CFStringRef*)
-+      pCFBundleGetDataPointerForName(launch_services_bundle,
-+                                     S("_kLSDisplayNameKey"));
- 
-   if (display_name_key == NULL || *display_name_key == NULL)
-     goto out;
-diff --git a/src/unix/darwin.c b/src/unix/darwin.c
-index 62f04d31..5fbf7342 100644
---- a/src/unix/darwin.c
-+++ b/src/unix/darwin.c
-@@ -353,7 +353,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
-     return UV_EINVAL;  /* FIXME(bnoordhuis) Translate error. */
-   }
- 
--  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
-+  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
-   if (!(*cpu_infos)) {
-     vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
-     return UV_ENOMEM;
-diff --git a/src/unix/epoll.c b/src/unix/epoll.c
-index 97348e25..4c057fb3 100644
---- a/src/unix/epoll.c
-+++ b/src/unix/epoll.c
-@@ -325,7 +325,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
-       assert(fd >= 0);
-       assert((unsigned) fd < loop->nwatchers);
- 
--      w = loop->watchers[fd];
-+      w = (uv__io_t*)loop->watchers[fd];
- 
-       if (w == NULL) {
-         /* File descriptor that we've stopped watching, disarm it.
-diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c
-index 658ff262..6700ff61 100644
---- a/src/unix/freebsd.c
-+++ b/src/unix/freebsd.c
-@@ -215,7 +215,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
-   if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
-     return UV__ERR(errno);
- 
--  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
-+  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
-   if (!(*cpu_infos))
-     return UV_ENOMEM;
- 
-@@ -232,7 +232,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
- 
-   size = maxcpus * CPUSTATES * sizeof(long);
- 
--  cp_times = uv__malloc(size);
-+  cp_times = (long*)uv__malloc(size);
-   if (cp_times == NULL) {
-     uv__free(*cpu_infos);
-     return UV_ENOMEM;
-diff --git a/src/unix/fs.c b/src/unix/fs.c
-index 933c9c0d..1a615244 100644
---- a/src/unix/fs.c
-+++ b/src/unix/fs.c
-@@ -137,7 +137,7 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
-       size_t new_path_len;                                                    \
-       path_len = strlen(path) + 1;                                            \
-       new_path_len = strlen(new_path) + 1;                                    \
--      req->path = uv__malloc(path_len + new_path_len);                        \
-+      req->path = (char*)uv__malloc(path_len + new_path_len);                 \
-       if (req->path == NULL)                                                  \
-         return UV_ENOMEM;                                                     \
-       req->new_path = req->path + path_len;                                   \
-@@ -572,7 +572,7 @@ static ssize_t uv__fs_scandir(uv_fs_t* req) {
- static int uv__fs_opendir(uv_fs_t* req) {
-   uv_dir_t* dir;
- 
--  dir = uv__malloc(sizeof(*dir));
-+  dir = (uv_dir_t*)uv__malloc(sizeof(*dir));
-   if (dir == NULL)
-     goto error;
- 
-@@ -596,7 +596,7 @@ static int uv__fs_readdir(uv_fs_t* req) {
-   unsigned int dirent_idx;
-   unsigned int i;
- 
--  dir = req->ptr;
-+  dir = (uv_dir_t*)req->ptr;
-   dirent_idx = 0;
- 
-   while (dirent_idx < dir->nentries) {
-@@ -638,7 +638,7 @@ error:
- static int uv__fs_closedir(uv_fs_t* req) {
-   uv_dir_t* dir;
- 
--  dir = req->ptr;
-+  dir = (uv_dir_t*)req->ptr;
- 
-   if (dir->dir != NULL) {
-     closedir(dir->dir);
-@@ -667,7 +667,7 @@ static int uv__fs_statfs(uv_fs_t* req) {
- #endif /* defined(__sun) */
-     return -1;
- 
--  stat_fs = uv__malloc(sizeof(*stat_fs));
-+  stat_fs = (uv_statfs_t*)uv__malloc(sizeof(*stat_fs));
-   if (stat_fs == NULL) {
-     errno = ENOMEM;
-     return -1;
-@@ -731,7 +731,7 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
-     maxlen = uv__fs_pathmax_size(req->path);
- #endif
- 
--  buf = uv__malloc(maxlen);
-+  buf = (char*)uv__malloc(maxlen);
- 
-   if (buf == NULL) {
-     errno = ENOMEM;
-@@ -751,7 +751,7 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
- 
-   /* Uncommon case: resize to make room for the trailing nul byte. */
-   if (len == maxlen) {
--    buf = uv__reallocf(buf, len + 1);
-+    buf = (char*)uv__reallocf(buf, len + 1);
- 
-     if (buf == NULL)
-       return -1;
-@@ -774,7 +774,7 @@ static ssize_t uv__fs_realpath(uv_fs_t* req) {
-   ssize_t len;
- 
-   len = uv__fs_pathmax_size(req->path);
--  buf = uv__malloc(len + 1);
-+  buf = (char*)uv__malloc(len + 1);
- 
-   if (buf == NULL) {
-     errno = ENOMEM;
-@@ -2010,7 +2010,7 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req,
-   req->nbufs = nbufs;
-   req->bufs = req->bufsml;
-   if (nbufs > ARRAY_SIZE(req->bufsml))
--    req->bufs = uv__malloc(nbufs * sizeof(*bufs));
-+    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
- 
-   if (req->bufs == NULL)
-     return UV_ENOMEM;
-@@ -2180,7 +2180,7 @@ int uv_fs_write(uv_loop_t* loop,
-   req->nbufs = nbufs;
-   req->bufs = req->bufsml;
-   if (nbufs > ARRAY_SIZE(req->bufsml))
--    req->bufs = uv__malloc(nbufs * sizeof(*bufs));
-+    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
- 
-   if (req->bufs == NULL)
-     return UV_ENOMEM;
-diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c
-index bf4f1f6a..648c8a98 100644
---- a/src/unix/fsevents.c
-+++ b/src/unix/fsevents.c
-@@ -185,7 +185,7 @@ static void (*pFSEventStreamStop)(FSEventStreamRef);
- static void uv__fsevents_cb(uv_async_t* cb) {
-   uv_fs_event_t* handle;
- 
--  handle = cb->data;
-+  handle = (uv_fs_event_t*)cb->data;
- 
-   UV__FSEVENTS_PROCESS(handle, {
-     handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
-@@ -233,10 +233,10 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
-   FSEventStreamEventFlags flags;
-   QUEUE head;
- 
--  loop = info;
--  state = loop->cf_state;
-+  loop = (uv_loop_t*)info;
-+  state = (uv__cf_loop_state_t*)loop->cf_state;
-   assert(state != NULL);
--  paths = eventPaths;
-+  paths = (char**)eventPaths;
- 
-   /* For each handle */
-   uv_mutex_lock(&state->fsevent_mutex);
-@@ -306,7 +306,7 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
-           continue;
-       }
- 
--      event = uv__malloc(sizeof(*event) + len);
-+      event = (uv__fsevents_event_t*)uv__malloc(sizeof(*event) + len);
-       if (event == NULL)
-         break;
- 
-@@ -373,7 +373,7 @@ static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
-                              flags);
-   assert(ref != NULL);
- 
--  state = loop->cf_state;
-+  state = (uv__cf_loop_state_t*)loop->cf_state;
-   pFSEventStreamScheduleWithRunLoop(ref,
-                                     state->loop,
-                                     *pkCFRunLoopDefaultMode);
-@@ -392,7 +392,7 @@ static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
- static void uv__fsevents_destroy_stream(uv_loop_t* loop) {
-   uv__cf_loop_state_t* state;
- 
--  state = loop->cf_state;
-+  state = (uv__cf_loop_state_t*)loop->cf_state;
- 
-   if (state->fsevent_stream == NULL)
-     return;
-@@ -419,7 +419,7 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle,
-   int err;
-   unsigned int path_count;
- 
--  state = handle->loop->cf_state;
-+  state = (uv__cf_loop_state_t*)handle->loop->cf_state;
-   paths = NULL;
-   cf_paths = NULL;
-   err = 0;
-@@ -447,7 +447,7 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle,
-   uv_mutex_lock(&state->fsevent_mutex);
-   path_count = state->fsevent_handle_count;
-   if (path_count != 0) {
--    paths = uv__malloc(sizeof(*paths) * path_count);
-+    paths = (CFStringRef*)uv__malloc(sizeof(*paths) * path_count);
-     if (paths == NULL) {
-       uv_mutex_unlock(&state->fsevent_mutex);
-       goto final;
-@@ -605,7 +605,7 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) {
-   if (err)
-     return err;
- 
--  state = uv__calloc(1, sizeof(*state));
-+  state = (uv__cf_loop_state_t*)uv__calloc(1, sizeof(*state));
-   if (state == NULL)
-     return UV_ENOMEM;
- 
-@@ -707,7 +707,7 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) {
-   }
- 
-   /* Destroy state */
--  state = loop->cf_state;
-+  state = (uv__cf_loop_state_t*)loop->cf_state;
-   uv_sem_destroy(&state->fsevent_sem);
-   uv_mutex_destroy(&state->fsevent_mutex);
-   pCFRelease(state->signal_source);
-@@ -721,8 +721,8 @@ static void* uv__cf_loop_runner(void* arg) {
-   uv_loop_t* loop;
-   uv__cf_loop_state_t* state;
- 
--  loop = arg;
--  state = loop->cf_state;
-+  loop = (uv_loop_t*)arg;
-+  state = (uv__cf_loop_state_t*)loop->cf_state;
-   state->loop = pCFRunLoopGetCurrent();
- 
-   pCFRunLoopAddSource(state->loop,
-@@ -750,8 +750,8 @@ static void uv__cf_loop_cb(void* arg) {
-   QUEUE split_head;
-   uv__cf_loop_signal_t* s;
- 
--  loop = arg;
--  state = loop->cf_state;
-+  loop = (uv_loop_t*)arg;
-+  state = (uv__cf_loop_state_t*)loop->cf_state;
- 
-   uv_mutex_lock(&loop->cf_mutex);
-   QUEUE_MOVE(&loop->cf_signals, &split_head);
-@@ -781,7 +781,7 @@ int uv__cf_loop_signal(uv_loop_t* loop,
-   uv__cf_loop_signal_t* item;
-   uv__cf_loop_state_t* state;
- 
--  item = uv__malloc(sizeof(*item));
-+  item = (uv__cf_loop_signal_t*)uv__malloc(sizeof(*item));
-   if (item == NULL)
-     return UV_ENOMEM;
- 
-@@ -791,7 +791,7 @@ int uv__cf_loop_signal(uv_loop_t* loop,
-   uv_mutex_lock(&loop->cf_mutex);
-   QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);
- 
--  state = loop->cf_state;
-+  state = (uv__cf_loop_state_t*)loop->cf_state;
-   assert(state != NULL);
-   pCFRunLoopSourceSignal(state->signal_source);
-   pCFRunLoopWakeUp(state->loop);
-@@ -825,7 +825,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) {
-    * Events will occur in other thread.
-    * Initialize callback for getting them back into event loop's thread
-    */
--  handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb));
-+  handle->cf_cb = (uv_async_t*)uv__malloc(sizeof(*handle->cf_cb));
-   if (handle->cf_cb == NULL) {
-     err = UV_ENOMEM;
-     goto fail_cf_cb_malloc;
-@@ -841,7 +841,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) {
-     goto fail_cf_mutex_init;
- 
-   /* Insert handle into the list */
--  state = handle->loop->cf_state;
-+  state = (uv__cf_loop_state_t*)handle->loop->cf_state;
-   uv_mutex_lock(&state->fsevent_mutex);
-   QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member);
-   state->fsevent_handle_count++;
-@@ -881,7 +881,7 @@ int uv__fsevents_close(uv_fs_event_t* handle) {
-     return UV_EINVAL;
- 
-   /* Remove handle from  the list */
--  state = handle->loop->cf_state;
-+  state = (uv__cf_loop_state_t*)handle->loop->cf_state;
-   uv_mutex_lock(&state->fsevent_mutex);
-   QUEUE_REMOVE(&handle->cf_member);
-   state->fsevent_handle_count--;
-diff --git a/src/unix/getaddrinfo.c b/src/unix/getaddrinfo.c
-index 77337ace..41dc3909 100644
---- a/src/unix/getaddrinfo.c
-+++ b/src/unix/getaddrinfo.c
-@@ -172,7 +172,7 @@ int uv_getaddrinfo(uv_loop_t* loop,
-   hostname_len = hostname ? strlen(hostname) + 1 : 0;
-   service_len = service ? strlen(service) + 1 : 0;
-   hints_len = hints ? sizeof(*hints) : 0;
--  buf = uv__malloc(hostname_len + service_len + hints_len);
-+  buf = (char*)uv__malloc(hostname_len + service_len + hints_len);
- 
-   if (buf == NULL)
-     return UV_ENOMEM;
-@@ -190,17 +190,17 @@ int uv_getaddrinfo(uv_loop_t* loop,
-   len = 0;
- 
-   if (hints) {
--    req->hints = memcpy(buf + len, hints, sizeof(*hints));
-+    req->hints = (struct addrinfo*)memcpy(buf + len, hints, sizeof(*hints));
-     len += sizeof(*hints);
-   }
- 
-   if (service) {
--    req->service = memcpy(buf + len, service, service_len);
-+    req->service = (char*)memcpy(buf + len, service, service_len);
-     len += service_len;
-   }
- 
-   if (hostname)
--    req->hostname = memcpy(buf + len, hostname, hostname_len);
-+    req->hostname = (char*)memcpy(buf + len, hostname, hostname_len);
- 
-   if (cb) {
-     uv__work_submit(loop,
-diff --git a/src/unix/ibmi.c b/src/unix/ibmi.c
-index 8c6ae636..56af31e9 100644
---- a/src/unix/ibmi.c
-+++ b/src/unix/ibmi.c
-@@ -288,7 +288,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
- 
-   numcpus = sysconf(_SC_NPROCESSORS_ONLN);
- 
--  *cpu_infos = uv__malloc(numcpus * sizeof(uv_cpu_info_t));
-+  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(uv_cpu_info_t));
-   if (!*cpu_infos) {
-     return UV_ENOMEM;
-   }
-diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c
-index 5dac76ae..86eb529b 100644
---- a/src/unix/kqueue.c
-+++ b/src/unix/kqueue.c
-@@ -281,8 +281,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
-     nevents = 0;
- 
-     assert(loop->watchers != NULL);
--    loop->watchers[loop->nwatchers] = (void*) events;
--    loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
-+    loop->watchers[loop->nwatchers] = (uv__io_t*) events;
-+    loop->watchers[loop->nwatchers + 1] = (uv__io_t*) (uintptr_t) nfds;
-     for (i = 0; i < nfds; i++) {
-       ev = events + i;
-       fd = ev->ident;
-@@ -304,7 +304,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
-       /* Skip invalidated events, see uv__platform_invalidate_fd */
-       if (fd == -1)
-         continue;
--      w = loop->watchers[fd];
-+      w = (uv__io_t*)loop->watchers[fd];
- 
-       if (w == NULL) {
-         /* File descriptor that we've stopped watching, disarm it.
-diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c
-index 23a7dafe..85f3fc01 100644
---- a/src/unix/linux-core.c
-+++ b/src/unix/linux-core.c
-@@ -117,7 +117,6 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
- }
- 
- 
--
- uint64_t uv__hrtime(uv_clocktype_t type) {
-   static clock_t fast_clock_id = -1;
-   struct timespec t;
-@@ -283,7 +282,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
-     goto out;
- 
-   err = UV_ENOMEM;
--  ci = uv__calloc(numcpus, sizeof(*ci));
-+  ci = (uv_cpu_info_t*)uv__calloc(numcpus, sizeof(*ci));
-   if (ci == NULL)
-     goto out;
- 
-@@ -663,7 +662,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
-   }
- 
-   /* Make sure the memory is initiallized to zero using calloc() */
--  *addresses = uv__calloc(*count, sizeof(**addresses));
-+  *addresses = (uv_interface_address_t*)uv__calloc(*count, sizeof(**addresses));
-   if (!(*addresses)) {
-     freeifaddrs(addrs);
-     return UV_ENOMEM;
-diff --git a/src/unix/linux-inotify.c b/src/unix/linux-inotify.c
-index c1bd260e..f5366e96 100644
---- a/src/unix/linux-inotify.c
-+++ b/src/unix/linux-inotify.c
-@@ -281,12 +281,12 @@ int uv_fs_event_start(uv_fs_event_t* handle,
-     goto no_insert;
- 
-   len = strlen(path) + 1;
--  w = uv__malloc(sizeof(*w) + len);
-+  w = (watcher_list*)uv__malloc(sizeof(*w) + len);
-   if (w == NULL)
-     return UV_ENOMEM;
- 
-   w->wd = wd;
--  w->path = memcpy(w + 1, path, len);
-+  w->path = (char*)memcpy(w + 1, path, len);
-   QUEUE_INIT(&w->watchers);
-   w->iterating = 0;
-   RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w);
-diff --git a/src/unix/loop.c b/src/unix/loop.c
-index a88e71c3..2e819cdd 100644
---- a/src/unix/loop.c
-+++ b/src/unix/loop.c
-@@ -148,7 +148,7 @@ int uv_loop_fork(uv_loop_t* loop) {
- 
-   /* Rearm all the watchers that aren't re-queued by the above. */
-   for (i = 0; i < loop->nwatchers; i++) {
--    w = loop->watchers[i];
-+    w = (uv__io_t*)loop->watchers[i];
-     if (w == NULL)
-       continue;
- 
-diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c
-index c66333f5..b6886a1c 100644
---- a/src/unix/netbsd.c
-+++ b/src/unix/netbsd.c
-@@ -206,14 +206,14 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
-     cpuspeed = 0;
- 
-   size = numcpus * CPUSTATES * sizeof(*cp_times);
--  cp_times = uv__malloc(size);
-+  cp_times = (u_int64_t*)uv__malloc(size);
-   if (cp_times == NULL)
-     return UV_ENOMEM;
- 
-   if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0))
-     return UV__ERR(errno);
- 
--  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
-+  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
-   if (!(*cpu_infos)) {
-     uv__free(cp_times);
-     uv__free(*cpu_infos);
-diff --git a/src/unix/openbsd.c b/src/unix/openbsd.c
-index f32a94df..62740f73 100644
---- a/src/unix/openbsd.c
-+++ b/src/unix/openbsd.c
-@@ -72,7 +72,7 @@ int uv_exepath(char* buffer, size_t* size) {
-   mypid = getpid();
-   for (;;) {
-     err = UV_ENOMEM;
--    argsbuf = uv__reallocf(argsbuf, argsbuf_size);
-+    argsbuf = (char**)uv__reallocf(argsbuf, argsbuf_size);
-     if (argsbuf == NULL)
-       goto out;
-     mib[0] = CTL_KERN;
-@@ -197,7 +197,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
-   if (sysctl(which, ARRAY_SIZE(which), &numcpus, &size, NULL, 0))
-     return UV__ERR(errno);
- 
--  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
-+  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
-   if (!(*cpu_infos))
-     return UV_ENOMEM;
- 
-diff --git a/src/unix/pipe.c b/src/unix/pipe.c
-index e8cfa148..c8ba31da 100644
---- a/src/unix/pipe.c
-+++ b/src/unix/pipe.c
-@@ -309,7 +309,7 @@ int uv_pipe_pending_count(uv_pipe_t* handle) {
-   if (handle->queued_fds == NULL)
-     return 1;
- 
--  queued_fds = handle->queued_fds;
-+  queued_fds = (uv__stream_queued_fds_t*)(handle->queued_fds);
-   return queued_fds->offset + 1;
- }
- 
-@@ -346,7 +346,7 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
-   if (r != UV_ENOBUFS)
-     return r;
- 
--  name_buffer = uv__malloc(name_len);
-+  name_buffer = (char*)uv__malloc(name_len);
-   if (name_buffer == NULL)
-     return UV_ENOMEM;
- 
-diff --git a/src/unix/poll.c b/src/unix/poll.c
-index 7a12e2d1..73647317 100644
---- a/src/unix/poll.c
-+++ b/src/unix/poll.c
-@@ -117,7 +117,7 @@ int uv_poll_stop(uv_poll_t* handle) {
- 
- 
- int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) {
--  uv__io_t** watchers;
-+  void** watchers;
-   uv__io_t* w;
-   int events;
- 
-diff --git a/src/unix/posix-poll.c b/src/unix/posix-poll.c
-index 0f4bf938..8da038d1 100644
---- a/src/unix/posix-poll.c
-+++ b/src/unix/posix-poll.c
-@@ -61,7 +61,7 @@ static void uv__pollfds_maybe_resize(uv_loop_t* loop) {
-     return;
- 
-   n = loop->poll_fds_size ? loop->poll_fds_size * 2 : 64;
--  p = uv__reallocf(loop->poll_fds, n * sizeof(*loop->poll_fds));
-+  p = (struct pollfd*)uv__reallocf(loop->poll_fds, n * sizeof(*loop->poll_fds));
-   if (p == NULL)
-     abort();
- 
-diff --git a/src/unix/process.c b/src/unix/process.c
-index f8415368..0916aa45 100644
---- a/src/unix/process.c
-+++ b/src/unix/process.c
-@@ -403,7 +403,7 @@ static int posix_spawn_can_use_setsid;
- static void uv__spawn_init_posix_spawn_fncs(void) {
-   /* Try to locate all non-portable functions at runtime */
-   posix_spawn_fncs.file_actions.addchdir_np =
--    dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addchdir_np");
-+    (int (*)(void* const*, const char*)) dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addchdir_np");
- }
- 
- 
-@@ -967,7 +967,7 @@ int uv_spawn(uv_loop_t* loop,
-   err = UV_ENOMEM;
-   pipes = pipes_storage;
-   if (stdio_count > (int) ARRAY_SIZE(pipes_storage))
--    pipes = uv__malloc(stdio_count * sizeof(*pipes));
-+    pipes = (int (*)[2])uv__malloc(stdio_count * sizeof(*pipes));
- 
-   if (pipes == NULL)
-     goto error;
-diff --git a/src/unix/proctitle.c b/src/unix/proctitle.c
-index 9d1f00dd..8cdec753 100644
---- a/src/unix/proctitle.c
-+++ b/src/unix/proctitle.c
-@@ -65,7 +65,7 @@ char** uv_setup_args(int argc, char** argv) {
-   /* Add space for the argv pointers. */
-   size += (argc + 1) * sizeof(char*);
- 
--  new_argv = uv__malloc(size);
-+  new_argv = (char**)uv__malloc(size);
-   if (new_argv == NULL)
-     return argv;
- 
-diff --git a/src/unix/random-sysctl-linux.c b/src/unix/random-sysctl-linux.c
-index 66ba8d74..9ef18df0 100644
---- a/src/unix/random-sysctl-linux.c
-+++ b/src/unix/random-sysctl-linux.c
-@@ -48,7 +48,7 @@ int uv__random_sysctl(void* buf, size_t buflen) {
-   char* pe;
-   size_t n;
- 
--  p = buf;
-+  p = (char*)buf;
-   pe = p + buflen;
- 
-   while (p < pe) {
-diff --git a/src/unix/stream.c b/src/unix/stream.c
-index b1f6359e..c6cc50e7 100644
---- a/src/unix/stream.c
-+++ b/src/unix/stream.c
-@@ -113,7 +113,7 @@ static void uv__stream_osx_interrupt_select(uv_stream_t* stream) {
-   uv__stream_select_t* s;
-   int r;
- 
--  s = stream->select;
-+  s = (uv__stream_select_t*)stream->select;
-   if (s == NULL)
-     return;
- 
-@@ -142,8 +142,8 @@ static void uv__stream_osx_select(void* arg) {
-   int r;
-   int max_fd;
- 
--  stream = arg;
--  s = stream->select;
-+  stream = (uv_stream_t*)arg;
-+  s = (uv__stream_select_t*)stream->select;
-   fd = s->fd;
- 
-   if (fd > s->int_fd)
-@@ -320,7 +320,7 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) {
-   sread_sz = ROUND_UP(max_fd + 1, sizeof(uint32_t) * NBBY) / NBBY;
-   swrite_sz = sread_sz;
- 
--  s = uv__malloc(sizeof(*s) + sread_sz + swrite_sz);
-+  s = (uv__stream_select_t*)uv__malloc(sizeof(*s) + sread_sz + swrite_sz);
-   if (s == NULL) {
-     err = UV_ENOMEM;
-     goto failed_malloc;
-@@ -605,7 +605,7 @@ done:
-   if (server->queued_fds != NULL) {
-     uv__stream_queued_fds_t* queued_fds;
- 
--    queued_fds = server->queued_fds;
-+    queued_fds = (uv__stream_queued_fds_t*)(server->queued_fds);
- 
-     /* Read first */
-     server->accepted_fd = queued_fds->fds[0];
-@@ -844,7 +844,7 @@ static int uv__try_write(uv_stream_t* stream,
-     /* silence aliasing warning */
-     {
-       void* pv = CMSG_DATA(cmsg);
--      int* pi = pv;
-+      int* pi = (int*)pv;
-       *pi = fd_to_send;
-     }
- 
-@@ -975,11 +975,12 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) {
-   uv__stream_queued_fds_t* queued_fds;
-   unsigned int queue_size;
- 
--  queued_fds = stream->queued_fds;
-+  queued_fds = (uv__stream_queued_fds_t*)stream->queued_fds;
-   if (queued_fds == NULL) {
-     queue_size = 8;
--    queued_fds = uv__malloc((queue_size - 1) * sizeof(*queued_fds->fds) +
--                            sizeof(*queued_fds));
-+    queued_fds = (uv__stream_queued_fds_t*)
-+        uv__malloc((queue_size - 1) * sizeof(*queued_fds->fds) +
-+                   sizeof(*queued_fds));
-     if (queued_fds == NULL)
-       return UV_ENOMEM;
-     queued_fds->size = queue_size;
-@@ -989,9 +990,9 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) {
-     /* Grow */
-   } else if (queued_fds->size == queued_fds->offset) {
-     queue_size = queued_fds->size + 8;
--    queued_fds = uv__realloc(queued_fds,
--                             (queue_size - 1) * sizeof(*queued_fds->fds) +
--                              sizeof(*queued_fds));
-+    queued_fds = (uv__stream_queued_fds_t*)
-+        uv__realloc(queued_fds, (queue_size - 1) * sizeof(*queued_fds->fds) +
-+                    sizeof(*queued_fds));
- 
-     /*
-      * Allocation failure, report back.
-@@ -1039,7 +1040,7 @@ static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
- 
-     /* silence aliasing warning */
-     pv = CMSG_DATA(cmsg);
--    pi = pv;
-+    pi = (int*)pv;
- 
-     /* Count available fds */
-     start = (char*) cmsg;
-@@ -1423,7 +1424,7 @@ int uv_write2(uv_write_t* req,
- 
-   req->bufs = req->bufsml;
-   if (nbufs > ARRAY_SIZE(req->bufsml))
--    req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
-+    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(bufs[0]));
- 
-   if (req->bufs == NULL)
-     return UV_ENOMEM;
-@@ -1557,7 +1558,7 @@ int uv___stream_fd(const uv_stream_t* handle) {
-          handle->type == UV_TTY ||
-          handle->type == UV_NAMED_PIPE);
- 
--  s = handle->select;
-+  s = (const uv__stream_select_t*)handle->select;
-   if (s != NULL)
-     return s->fd;
- 
-@@ -1575,7 +1576,7 @@ void uv__stream_close(uv_stream_t* handle) {
-   if (handle->select != NULL) {
-     uv__stream_select_t* s;
- 
--    s = handle->select;
-+    s = (uv__stream_select_t*)handle->select;
- 
-     uv_sem_post(&s->close_sem);
-     uv_sem_post(&s->async_sem);
-@@ -1610,7 +1611,7 @@ void uv__stream_close(uv_stream_t* handle) {
- 
-   /* Close all queued fds */
-   if (handle->queued_fds != NULL) {
--    queued_fds = handle->queued_fds;
-+    queued_fds = (uv__stream_queued_fds_t*)(handle->queued_fds);
-     for (i = 0; i < queued_fds->offset; i++)
-       uv__close(queued_fds->fds[i]);
-     uv__free(handle->queued_fds);
-diff --git a/src/unix/thread.c b/src/unix/thread.c
-index d89e5cd1..759cd0c2 100644
---- a/src/unix/thread.c
-+++ b/src/unix/thread.c
-@@ -59,7 +59,7 @@ int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
-   if (barrier == NULL || count == 0)
-     return UV_EINVAL;
- 
--  b = uv__malloc(sizeof(*b));
-+  b = (_uv_barrier*)uv__malloc(sizeof(*b));
-   if (b == NULL)
-     return UV_ENOMEM;
- 
-@@ -275,8 +275,7 @@ int uv_thread_create_ex(uv_thread_t* tid,
-       abort();
-   }
- 
--  f.in = entry;
--  err = pthread_create(tid, attr, f.out, arg);
-+  err = pthread_create(tid, attr, (void*(*)(void*)) (void(*)(void)) entry, arg);
- 
-   if (attr != NULL)
-     pthread_attr_destroy(attr);
-@@ -547,7 +546,7 @@ static int uv__custom_sem_init(uv_sem_t* sem_, unsigned int value) {
-   int err;
-   uv_semaphore_t* sem;
- 
--  sem = uv__malloc(sizeof(*sem));
-+  sem = (uv_semaphore_t*)uv__malloc(sizeof(*sem));
-   if (sem == NULL)
-     return UV_ENOMEM;
- 
-diff --git a/src/unix/udp.c b/src/unix/udp.c
-index 4d985b88..a130aeaa 100644
---- a/src/unix/udp.c
-+++ b/src/unix/udp.c
-@@ -227,11 +227,11 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
-       if (msgs[k].msg_hdr.msg_flags & MSG_TRUNC)
-         flags |= UV_UDP_PARTIAL;
- 
--      chunk_buf = uv_buf_init(iov[k].iov_base, iov[k].iov_len);
-+      chunk_buf = uv_buf_init((char*) iov[k].iov_base, iov[k].iov_len);
-       handle->recv_cb(handle,
-                       msgs[k].msg_len,
-                       &chunk_buf,
--                      msgs[k].msg_hdr.msg_name,
-+                      (const sockaddr*) msgs[k].msg_hdr.msg_name,
-                       flags);
-     }
- 
-@@ -281,7 +281,7 @@ static void uv__udp_recvmsg(uv_udp_t* handle) {
-     memset(&peer, 0, sizeof(peer));
-     h.msg_name = &peer;
-     h.msg_namelen = sizeof(peer);
--    h.msg_iov = (void*) &buf;
-+    h.msg_iov = (iovec*) &buf;
-     h.msg_iovlen = 1;
- 
-     do {
-@@ -765,7 +765,7 @@ int uv__udp_send(uv_udp_send_t* req,
- 
-   req->bufs = req->bufsml;
-   if (nbufs > ARRAY_SIZE(req->bufsml))
--    req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
-+    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(bufs[0]));
- 
-   if (req->bufs == NULL) {
-     uv__req_unregister(handle->loop, req);
-diff --git a/src/uv-common.c b/src/uv-common.c
-index efc9eb50..dfb606e3 100644
---- a/src/uv-common.c
-+++ b/src/uv-common.c
-@@ -54,10 +54,10 @@ static uv__allocator_t uv__allocator = {
- 
- char* uv__strdup(const char* s) {
-   size_t len = strlen(s) + 1;
--  char* m = uv__malloc(len);
-+  char* m = (char*)uv__malloc(len);
-   if (m == NULL)
-     return NULL;
--  return memcpy(m, s, len);
-+  return (char*)memcpy(m, s, len);
- }
- 
- char* uv__strndup(const char* s, size_t n) {
-@@ -65,11 +65,11 @@ char* uv__strndup(const char* s, size_t n) {
-   size_t len = strlen(s);
-   if (n < len)
-     len = n;
--  m = uv__malloc(len + 1);
-+  m = (char*)uv__malloc(len + 1);
-   if (m == NULL)
-     return NULL;
-   m[len] = '\0';
--  return memcpy(m, s, len);
-+  return (char*)memcpy(m, s, len);
- }
- 
- void* uv__malloc(size_t size) {
-@@ -653,7 +653,7 @@ void uv__fs_scandir_cleanup(uv_fs_t* req) {
- 
-   unsigned int* nbufs = uv__get_nbufs(req);
- 
--  dents = req->ptr;
-+  dents = (uv__dirent_t**)(req->ptr);
-   if (*nbufs > 0 && *nbufs != (unsigned int) req->result)
-     (*nbufs)--;
-   for (; *nbufs < (unsigned int) req->result; (*nbufs)++)
-@@ -680,7 +680,7 @@ int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) {
-   nbufs = uv__get_nbufs(req);
-   assert(nbufs);
- 
--  dents = req->ptr;
-+  dents = (uv__dirent_t**)(req->ptr);
- 
-   /* Free previous entity */
-   if (*nbufs > 0)
-@@ -745,7 +745,7 @@ void uv__fs_readdir_cleanup(uv_fs_t* req) {
-   if (req->ptr == NULL)
-     return;
- 
--  dir = req->ptr;
-+  dir = (uv_dir_t*)req->ptr;
-   dirents = dir->dirents;
-   req->ptr = NULL;
- 
-@@ -791,7 +791,7 @@ uv_loop_t* uv_default_loop(void) {
- uv_loop_t* uv_loop_new(void) {
-   uv_loop_t* loop;
- 
--  loop = uv__malloc(sizeof(*loop));
-+  loop = (uv_loop_t*)uv__malloc(sizeof(*loop));
-   if (loop == NULL)
-     return NULL;
- 
-diff --git a/src/win/core.c b/src/win/core.c
-index 67af93e6..0752edff 100644
---- a/src/win/core.c
-+++ b/src/win/core.c
-@@ -98,7 +98,8 @@ static int uv__loops_add(uv_loop_t* loop) {
- 
-   if (uv__loops_size == uv__loops_capacity) {
-     new_capacity = uv__loops_capacity + UV__LOOPS_CHUNK_SIZE;
--    new_loops = uv__realloc(uv__loops, sizeof(uv_loop_t*) * new_capacity);
-+    new_loops = (uv_loop_t**)
-+        uv__realloc(uv__loops, sizeof(uv_loop_t*) * new_capacity);
-     if (!new_loops)
-       goto failed_loops_realloc;
-     uv__loops = new_loops;
-@@ -152,7 +153,8 @@ static void uv__loops_remove(uv_loop_t* loop) {
-   smaller_capacity = uv__loops_capacity / 2;
-   if (uv__loops_size >= smaller_capacity)
-     goto loop_removed;
--  new_loops = uv__realloc(uv__loops, sizeof(uv_loop_t*) * smaller_capacity);
-+  new_loops = (uv_loop_t**)
-+      uv__realloc(uv__loops, sizeof(uv_loop_t*) * smaller_capacity);
-   if (!new_loops)
-     goto loop_removed;
-   uv__loops = new_loops;
-@@ -261,7 +263,7 @@ int uv_loop_init(uv_loop_t* loop) {
- 
-   loop->endgame_handles = NULL;
- 
--  loop->timer_heap = timer_heap = uv__malloc(sizeof(*timer_heap));
-+  loop->timer_heap = timer_heap = (heap*)uv__malloc(sizeof(*timer_heap));
-   if (timer_heap == NULL) {
-     err = UV_ENOMEM;
-     goto fail_timers_alloc;
-diff --git a/src/win/fs-event.c b/src/win/fs-event.c
-index 6758c7c7..15046731 100644
---- a/src/win/fs-event.c
-+++ b/src/win/fs-event.c
-@@ -73,7 +73,7 @@ static void uv__relative_path(const WCHAR* filename,
-   if (dirlen > 0 && dir[dirlen - 1] == '\\')
-     dirlen--;
-   relpathlen = filenamelen - dirlen - 1;
--  *relpath = uv__malloc((relpathlen + 1) * sizeof(WCHAR));
-+  *relpath = (WCHAR*)uv__malloc((relpathlen + 1) * sizeof(WCHAR));
-   if (!*relpath)
-     uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
-   wcsncpy(*relpath, filename + dirlen + 1, relpathlen);
-@@ -242,7 +242,7 @@ int uv_fs_event_start(uv_fs_event_t* handle,
-     if (short_path_buffer_len == 0) {
-       goto short_path_done;
-     }
--    short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR));
-+    short_path_buffer = (WCHAR*)uv__malloc(short_path_buffer_len * sizeof(WCHAR));
-     if (short_path_buffer == NULL) {
-       goto short_path_done;
-     }
-diff --git a/src/win/fs-fd-hash-inl.h b/src/win/fs-fd-hash-inl.h
-index 0b532af1..703a8d8f 100644
---- a/src/win/fs-fd-hash-inl.h
-+++ b/src/win/fs-fd-hash-inl.h
-@@ -146,7 +146,7 @@ INLINE static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) {
- 
-     if (bucket_ptr->size != 0 && i == 0) {
-       struct uv__fd_hash_entry_group_s* new_group_ptr =
--        uv__malloc(sizeof(*new_group_ptr));
-+        (struct uv__fd_hash_entry_group_s*)uv__malloc(sizeof(*new_group_ptr));
-       if (new_group_ptr == NULL) {
-         uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
-       }
-diff --git a/src/win/fs.c b/src/win/fs.c
-index 79230799..8374012f 100644
---- a/src/win/fs.c
-+++ b/src/win/fs.c
-@@ -285,7 +285,7 @@ static int fs__wide_to_utf8(WCHAR* w_source_ptr,
-     return 0;
-   }
- 
--  target = uv__malloc(target_len + 1);
-+  target = (char*)uv__malloc(target_len + 1);
-   if (target == NULL) {
-     SetLastError(ERROR_OUTOFMEMORY);
-     return -1;
-@@ -1464,7 +1464,7 @@ void fs__scandir(uv_fs_t* req) {
-       if (dirents_used >= dirents_size) {
-         size_t new_dirents_size =
-             dirents_size == 0 ? dirents_initial_size : dirents_size << 1;
--        uv__dirent_t** new_dirents =
-+        uv__dirent_t** new_dirents = (uv__dirent_t**)
-             uv__realloc(dirents, new_dirents_size * sizeof *dirents);
- 
-         if (new_dirents == NULL)
-@@ -1478,7 +1478,7 @@ void fs__scandir(uv_fs_t* req) {
-        * includes room for the first character of the filename, but `utf8_len`
-        * doesn't count the NULL terminator at this point.
-        */
--      dirent = uv__malloc(sizeof *dirent + utf8_len);
-+      dirent = (uv__dirent_t*)uv__malloc(sizeof *dirent + utf8_len);
-       if (dirent == NULL)
-         goto out_of_memory_error;
- 
-@@ -1589,7 +1589,7 @@ void fs__opendir(uv_fs_t* req) {
-     goto error;
-   }
- 
--  dir = uv__malloc(sizeof(*dir));
-+  dir = (uv_dir_t*)uv__malloc(sizeof(*dir));
-   if (dir == NULL) {
-     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
-     goto error;
-@@ -1604,7 +1604,7 @@ void fs__opendir(uv_fs_t* req) {
-   else
-     fmt = L"%s\\*";
- 
--  find_path = uv__malloc(sizeof(WCHAR) * (len + 4));
-+  find_path = (WCHAR*)uv__malloc(sizeof(WCHAR) * (len + 4));
-   if (find_path == NULL) {
-     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
-     goto error;
-@@ -1641,7 +1641,7 @@ void fs__readdir(uv_fs_t* req) {
-   int r;
- 
-   req->flags |= UV_FS_FREE_PTR;
--  dir = req->ptr;
-+  dir = (uv_dir_t*)req->ptr;
-   dirents = dir->dirents;
-   memset(dirents, 0, dir->nentries * sizeof(*dir->dirents));
-   find_data = &dir->find_data;
-@@ -1698,7 +1698,7 @@ error:
- void fs__closedir(uv_fs_t* req) {
-   uv_dir_t* dir;
- 
--  dir = req->ptr;
-+  dir = (uv_dir_t*)req->ptr;
-   FindClose(dir->dir_handle);
-   uv__free(req->ptr);
-   SET_REQ_RESULT(req, 0);
-@@ -2627,7 +2627,7 @@ static ssize_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) {
-     return -1;
-   }
- 
--  w_realpath_buf = uv__malloc((w_realpath_len + 1) * sizeof(WCHAR));
-+  w_realpath_buf = (WCHAR*)uv__malloc((w_realpath_len + 1) * sizeof(WCHAR));
-   if (w_realpath_buf == NULL) {
-     SetLastError(ERROR_OUTOFMEMORY);
-     return -1;
-@@ -2738,7 +2738,7 @@ retry_get_disk_free_space:
-     }
- 
-     len = MAX_PATH + 1;
--    pathw = uv__malloc(len * sizeof(*pathw));
-+    pathw = (WCHAR*)uv__malloc(len * sizeof(*pathw));
-     if (pathw == NULL) {
-       SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
-       return;
-@@ -2754,7 +2754,7 @@ retry_get_full_path_name:
-       return;
-     } else if (ret > len) {
-       len = ret;
--      pathw = uv__reallocf(pathw, len * sizeof(*pathw));
-+      pathw = (WCHAR*)uv__reallocf(pathw, len * sizeof(*pathw));
-       if (pathw == NULL) {
-         SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
-         return;
-@@ -2770,7 +2770,7 @@ retry_get_full_path_name:
-     uv__free(pathw);
-   }
- 
--  stat_fs = uv__malloc(sizeof(*stat_fs));
-+  stat_fs = (uv_statfs_t*)uv__malloc(sizeof(*stat_fs));
-   if (stat_fs == NULL) {
-     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
-     return;
-@@ -2929,7 +2929,7 @@ int uv_fs_read(uv_loop_t* loop,
-   req->fs.info.nbufs = nbufs;
-   req->fs.info.bufs = req->fs.info.bufsml;
-   if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
--    req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
-+    req->fs.info.bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
- 
-   if (req->fs.info.bufs == NULL) {
-     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
-@@ -2962,7 +2962,7 @@ int uv_fs_write(uv_loop_t* loop,
-   req->fs.info.nbufs = nbufs;
-   req->fs.info.bufs = req->fs.info.bufsml;
-   if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
--    req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
-+    req->fs.info.bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
- 
-   if (req->fs.info.bufs == NULL) {
-     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
-diff --git a/src/win/pipe.c b/src/win/pipe.c
-index 99846181..cd77061a 100644
---- a/src/win/pipe.c
-+++ b/src/win/pipe.c
-@@ -728,7 +728,7 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
- 
-   /* Convert name to UTF16. */
-   nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
--  handle->name = uv__malloc(nameSize);
-+  handle->name = (WCHAR*)uv__malloc(nameSize);
-   if (!handle->name) {
-     uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
-   }
-@@ -841,7 +841,7 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle,
- 
-   /* Convert name to UTF16. */
-   nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
--  handle->name = uv__malloc(nameSize);
-+  handle->name = (WCHAR*)uv__malloc(nameSize);
-   if (!handle->name) {
-     uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
-   }
-@@ -1453,7 +1453,7 @@ static int uv__build_coalesced_write_req(uv_write_t* user_req,
-                        data_length;                  /* (c) */
- 
-   /* Allocate buffer. */
--  heap_buffer = uv__malloc(heap_buffer_length);
-+  heap_buffer = (char*)uv__malloc(heap_buffer_length);
-   if (heap_buffer == NULL)
-     return ERROR_NOT_ENOUGH_MEMORY; /* Maps to UV_ENOMEM. */
- 
-@@ -1698,7 +1698,7 @@ int uv__pipe_write_ipc(uv_loop_t* loop,
-     bufs = stack_bufs;
-   } else {
-     /* Use heap-allocated buffer array. */
--    bufs = uv__calloc(buf_count, sizeof(uv_buf_t));
-+    bufs = (uv_buf_t*)uv__calloc(buf_count, sizeof(uv_buf_t));
-     if (bufs == NULL)
-       return ERROR_NOT_ENOUGH_MEMORY; /* Maps to UV_ENOMEM. */
-   }
-@@ -2430,7 +2430,7 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size)
-                                       FileNameInformation);
-   if (nt_status == STATUS_BUFFER_OVERFLOW) {
-     name_size = sizeof(*name_info) + tmp_name_info.FileNameLength;
--    name_info = uv__malloc(name_size);
-+    name_info = (FILE_NAME_INFORMATION*)uv__malloc(name_size);
-     if (!name_info) {
-       *size = 0;
-       err = UV_ENOMEM;
-diff --git a/src/win/process.c b/src/win/process.c
-index 24c63339..e857db3e 100644
---- a/src/win/process.c
-+++ b/src/win/process.c
-@@ -616,8 +616,8 @@ error:
- 
- 
- int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
--  wchar_t* a_eq;
--  wchar_t* b_eq;
-+  const wchar_t* a_eq;
-+  const wchar_t* b_eq;
-   wchar_t* A;
-   wchar_t* B;
-   int nb;
-@@ -634,8 +634,8 @@ int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
-   assert(b_eq);
-   nb = b_eq - b;
- 
--  A = alloca((na+1) * sizeof(wchar_t));
--  B = alloca((nb+1) * sizeof(wchar_t));
-+  A = (wchar_t*)alloca((na+1) * sizeof(wchar_t));
-+  B = (wchar_t*)alloca((nb+1) * sizeof(wchar_t));
- 
-   r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na);
-   assert(r==na);
-@@ -718,7 +718,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
-   if (dst_copy == NULL && env_len > 0) {
-     return ERROR_OUTOFMEMORY;
-   }
--  env_copy = alloca(env_block_count * sizeof(WCHAR*));
-+  env_copy = (WCHAR**)alloca(env_block_count * sizeof(WCHAR*));
- 
-   ptr = dst_copy;
-   ptr_copy = env_copy;
-@@ -772,7 +772,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
-   }
- 
-   /* final pass: copy, in sort order, and inserting required variables */
--  dst = uv__malloc((1+env_len) * sizeof(WCHAR));
-+  dst = (WCHAR*)uv__malloc((1+env_len) * sizeof(WCHAR));
-   if (!dst) {
-     uv__free(dst_copy);
-     return ERROR_OUTOFMEMORY;
-diff --git a/src/win/tcp.c b/src/win/tcp.c
-index b6aa4c51..4cccee42 100644
---- a/src/win/tcp.c
-+++ b/src/win/tcp.c
-@@ -612,7 +612,7 @@ int uv__tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
- 
-   if (handle->tcp.serv.accept_reqs == NULL) {
-     handle->tcp.serv.accept_reqs =
--      uv__malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t));
-+      (uv_tcp_accept_t*)uv__malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t));
-     if (!handle->tcp.serv.accept_reqs) {
-       uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
-     }
-diff --git a/src/win/thread.c b/src/win/thread.c
-index d3b1c96b..9ad60c91 100644
---- a/src/win/thread.c
-+++ b/src/win/thread.c
-@@ -98,7 +98,7 @@ static UINT __stdcall uv__thread_start(void* arg) {
-   struct thread_ctx *ctx_p;
-   struct thread_ctx ctx;
- 
--  ctx_p = arg;
-+  ctx_p = (struct thread_ctx*)arg;
-   ctx = *ctx_p;
-   uv__free(ctx_p);
- 
-@@ -141,7 +141,7 @@ int uv_thread_create_ex(uv_thread_t* tid,
-       return UV_EINVAL;
-   }
- 
--  ctx = uv__malloc(sizeof(*ctx));
-+  ctx = (struct thread_ctx*)uv__malloc(sizeof(*ctx));
-   if (ctx == NULL)
-     return UV_ENOMEM;
- 
-diff --git a/src/win/util.c b/src/win/util.c
-index 99432053..c655f532 100644
---- a/src/win/util.c
-+++ b/src/win/util.c
-@@ -164,7 +164,7 @@ int uv_cwd(char* buffer, size_t* size) {
-   if (utf16_len == 0) {
-     return uv_translate_sys_error(GetLastError());
-   }
--  utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR));
-+  utf16_buffer = (WCHAR*)uv__malloc(utf16_len * sizeof(WCHAR));
-   if (utf16_buffer == NULL) {
-     return UV_ENOMEM;
-   }
-@@ -242,7 +242,7 @@ int uv_chdir(const char* dir) {
-   if (utf16_len == 0) {
-     return uv_translate_sys_error(GetLastError());
-   }
--  utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR));
-+  utf16_buffer = (WCHAR*)uv__malloc(utf16_len * sizeof(WCHAR));
-   if (utf16_buffer == NULL) {
-     return UV_ENOMEM;
-   }
-@@ -268,7 +268,7 @@ int uv_chdir(const char* dir) {
-   new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
-   if (new_utf16_len > utf16_len ) {
-     uv__free(utf16_buffer);
--    utf16_buffer = uv__malloc(new_utf16_len * sizeof(WCHAR));
-+    utf16_buffer = (WCHAR*)uv__malloc(new_utf16_len * sizeof(WCHAR));
-     if (utf16_buffer == NULL) {
-       /* When updating the environment variable fails, return UV_OK anyway.
-        * We did successfully change current working directory, only updating
-@@ -573,14 +573,14 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
-   GetSystemInfo(&system_info);
-   cpu_count = system_info.dwNumberOfProcessors;
- 
--  cpu_infos = uv__calloc(cpu_count, sizeof *cpu_infos);
-+  cpu_infos = (uv_cpu_info_t*)uv__calloc(cpu_count, sizeof *cpu_infos);
-   if (cpu_infos == NULL) {
-     err = ERROR_OUTOFMEMORY;
-     goto error;
-   }
- 
-   sppi_size = cpu_count * sizeof(*sppi);
--  sppi = uv__malloc(sppi_size);
-+  sppi = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*)uv__malloc(sppi_size);
-   if (sppi == NULL) {
-     err = ERROR_OUTOFMEMORY;
-     goto error;
-@@ -802,7 +802,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
-       case ERROR_BUFFER_OVERFLOW:
-         /* This happens when win_address_buf is NULL or too small to hold all
-          * adapters. */
--        win_address_buf = uv__malloc(win_address_buf_size);
-+        win_address_buf =
-+            (IP_ADAPTER_ADDRESSES*)uv__malloc(win_address_buf_size);
-         if (win_address_buf == NULL)
-           return UV_ENOMEM;
- 
-@@ -810,7 +811,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
- 
-       case ERROR_NO_DATA: {
-         /* No adapters were found. */
--        uv_address_buf = uv__malloc(1);
-+        uv_address_buf = (uv_interface_address_t*)uv__malloc(1);
-         if (uv_address_buf == NULL)
-           return UV_ENOMEM;
- 
-@@ -887,7 +888,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
-   }
- 
-   /* Allocate space to store interface data plus adapter names. */
--  uv_address_buf = uv__malloc(uv_address_buf_size);
-+  uv_address_buf = (uv_interface_address_t*)uv__malloc(uv_address_buf_size);
-   if (uv_address_buf == NULL) {
-     uv__free(win_address_buf);
-     return UV_ENOMEM;
-@@ -1131,7 +1132,7 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
-   }
-   /* Include space for terminating null char. */
-   len += 1;
--  path = uv__malloc(len * sizeof(wchar_t));
-+  path = (wchar_t*)uv__malloc(len * sizeof(wchar_t));
-   if (path == NULL) {
-     return UV_ENOMEM;
-   }
-@@ -1221,7 +1222,7 @@ int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8) {
-   /* Allocate the destination buffer adding an extra byte for the terminating
-    * NULL. If utf16len is not -1 WideCharToMultiByte will not add it, so
-    * we do it ourselves always, just in case. */
--  *utf8 = uv__malloc(bufsize + 1);
-+  *utf8 = (char*)uv__malloc(bufsize + 1);
- 
-   if (*utf8 == NULL)
-     return UV_ENOMEM;
-@@ -1269,7 +1270,7 @@ int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) {
-   /* Allocate the destination buffer adding an extra byte for the terminating
-    * NULL. If utf8len is not -1 MultiByteToWideChar will not add it, so
-    * we do it ourselves always, just in case. */
--  *utf16 = uv__malloc(sizeof(WCHAR) * (bufsize + 1));
-+  *utf16 = (WCHAR*)uv__malloc(sizeof(WCHAR) * (bufsize + 1));
- 
-   if (*utf16 == NULL)
-     return UV_ENOMEM;
-@@ -1310,7 +1311,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
-     return uv_translate_sys_error(r);
-   }
- 
--  path = uv__malloc(bufsize * sizeof(wchar_t));
-+  path = (wchar_t*)uv__malloc(bufsize * sizeof(wchar_t));
-   if (path == NULL) {
-     CloseHandle(token);
-     return UV_ENOMEM;
-@@ -1381,7 +1382,7 @@ int uv_os_environ(uv_env_item_t** envitems, int* count) {
- 
-   for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++);
- 
--  *envitems = uv__calloc(i, sizeof(**envitems));
-+  *envitems = (uv_env_item_t*)uv__calloc(i, sizeof(**envitems));
-   if (*envitems == NULL) {
-     FreeEnvironmentStringsW(env);
-     return UV_ENOMEM;
-@@ -1471,7 +1472,7 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
-       uv__free(var);
- 
-     varlen = 1 + len;
--    var = uv__malloc(varlen * sizeof(*var));
-+    var = (wchar_t*)uv__malloc(varlen * sizeof(*var));
- 
-     if (var == NULL) {
-       r = UV_ENOMEM;
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0001-Revert-win-process-write-minidumps-when-sending-SIGQ.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0001-Revert-win-process-write-minidumps-when-sending-SIGQ.patch
new file mode 100644
index 0000000..890b8c3
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/libuv_patches/0001-Revert-win-process-write-minidumps-when-sending-SIGQ.patch
@@ -0,0 +1,190 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Fri, 14 Jul 2023 17:33:08 -0700
+Subject: [PATCH 01/10] Revert "win,process: write minidumps when sending
+ SIGQUIT (#3840)"
+
+This reverts commit 748d894e82abcdfff7429cf745003e182c47f163.
+---
+ CMakeLists.txt    |   5 +-
+ configure.ac      |   2 +-
+ include/uv/win.h  |   1 -
+ src/win/process.c | 116 ----------------------------------------------
+ 4 files changed, 2 insertions(+), 122 deletions(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 93733dd04783436cc1f1a801133e67e315f4af8d..0958dfb1bd93311cd0e20506311e1e41774c5fa4 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -183,10 +183,7 @@ if(WIN32)
+        advapi32
+        iphlpapi
+        userenv
+-       ws2_32
+-       dbghelp
+-       ole32
+-       uuid)
++       ws2_32)
+   list(APPEND uv_sources
+        src/win/async.c
+        src/win/core.c
+diff --git a/configure.ac b/configure.ac
+index deb083605de639e896df83882715ddca25340fa3..76177a4bc8e5f17bc1e062af3a9028d2dfc76dc9 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -74,7 +74,7 @@ AM_CONDITIONAL([OS400],    [AS_CASE([$host_os],[os400],         [true], [false])
+ AM_CONDITIONAL([SUNOS],    [AS_CASE([$host_os],[solaris*],      [true], [false])])
+ AM_CONDITIONAL([WINNT],    [AS_CASE([$host_os],[mingw*],        [true], [false])])
+ AS_CASE([$host_os],[mingw*], [
+-    LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -luser32 -ldbghelp -lole32 -luuid"
++    LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -luser32"
+ ])
+ AS_CASE([$host_os], [solaris2.10], [
+     CFLAGS="$CFLAGS -DSUNOS_NO_IFADDRS"
+diff --git a/include/uv/win.h b/include/uv/win.h
+index 6f8c47298e407bcb0151cf383a8370b71074f03e..eb74776978340a4910194bae35a9da6493e8c0a6 100644
+--- a/include/uv/win.h
++++ b/include/uv/win.h
+@@ -91,7 +91,6 @@ typedef struct pollfd {
+  * variants (Linux and Darwin)
+  */
+ #define SIGHUP                1
+-#define SIGQUIT               3
+ #define SIGKILL               9
+ #define SIGWINCH             28
+ 
+diff --git a/src/win/process.c b/src/win/process.c
+index 3e451e2291d6ed200ec258e874becbbea22bbc27..ed44adc67c6d52785a199206d9ba0357e2d0b045 100644
+--- a/src/win/process.c
++++ b/src/win/process.c
+@@ -32,9 +32,6 @@
+ #include "internal.h"
+ #include "handle-inl.h"
+ #include "req-inl.h"
+-#include <dbghelp.h>
+-#include <shlobj.h>
+-#include <psapi.h>     /* GetModuleBaseNameW */
+ 
+ 
+ #define SIGKILL         9
+@@ -1197,120 +1194,7 @@ static int uv__kill(HANDLE process_handle, int signum) {
+     return UV_EINVAL;
+   }
+ 
+-  /* Create a dump file for the targeted process, if the registry key
+-   * `HKLM:Software\Microsoft\Windows\Windows Error Reporting\LocalDumps`
+-   * exists.  The location of the dumps can be influenced by the `DumpFolder`
+-   * sub-key, which has a default value of `%LOCALAPPDATA%\CrashDumps`, see [0]
+-   * for more detail.  Note that if the dump folder does not exist, we attempt
+-   * to create it, to match behavior with WER itself.
+-   * [0]: https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps */
+-  if (signum == SIGQUIT) {
+-    HKEY registry_key;
+-    DWORD pid, ret;
+-    WCHAR basename[MAX_PATH];
+-
+-    /* Get target process name. */
+-    GetModuleBaseNameW(process_handle, NULL, &basename[0], sizeof(basename));
+-
+-    /* Get PID of target process. */
+-    pid = GetProcessId(process_handle);
+-
+-    /* Get LocalDumps directory path. */
+-    ret = RegOpenKeyExW(
+-        HKEY_LOCAL_MACHINE,
+-        L"SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps",
+-        0,
+-        KEY_QUERY_VALUE,
+-        &registry_key);
+-    if (ret == ERROR_SUCCESS) {
+-      HANDLE hDumpFile = NULL;
+-      WCHAR dump_folder[MAX_PATH], dump_name[MAX_PATH];
+-      DWORD dump_folder_len = sizeof(dump_folder), key_type = 0;
+-      ret = RegGetValueW(registry_key,
+-                         NULL,
+-                         L"DumpFolder",
+-                         RRF_RT_ANY,
+-                         &key_type,
+-                         (PVOID) dump_folder,
+-                         &dump_folder_len);
+-      if (ret != ERROR_SUCCESS) {
+-        /* Default value for `dump_folder` is `%LOCALAPPDATA%\CrashDumps`. */
+-        WCHAR* localappdata;
+-        SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, NULL, &localappdata);
+-        _snwprintf_s(dump_folder,
+-                     sizeof(dump_folder),
+-                     _TRUNCATE,
+-                     L"%ls\\CrashDumps",
+-                     localappdata);
+-        CoTaskMemFree(localappdata);
+-      }
+-      RegCloseKey(registry_key);
+-
+-      /* Create dump folder if it doesn't already exist. */
+-      CreateDirectoryW(dump_folder, NULL);
+-
+-      /* Construct dump filename from process name and PID. */
+-      _snwprintf_s(dump_name,
+-                   sizeof(dump_name),
+-                   _TRUNCATE,
+-                   L"%ls\\%ls.%d.dmp",
+-                   dump_folder,
+-                   basename,
+-                   pid);
+-
+-      hDumpFile = CreateFileW(dump_name,
+-                              GENERIC_WRITE,
+-                              0,
+-                              NULL,
+-                              CREATE_NEW,
+-                              FILE_ATTRIBUTE_NORMAL,
+-                              NULL);
+-      if (hDumpFile != INVALID_HANDLE_VALUE) {
+-        DWORD dump_options, sym_options;
+-        FILE_DISPOSITION_INFO DeleteOnClose = { TRUE };
+-
+-        /* If something goes wrong while writing it out, delete the file. */
+-        SetFileInformationByHandle(hDumpFile,
+-                                   FileDispositionInfo,
+-                                   &DeleteOnClose,
+-                                   sizeof(DeleteOnClose));
+-
+-        /* Tell wine to dump ELF modules as well. */
+-        sym_options = SymGetOptions();
+-        SymSetOptions(sym_options | 0x40000000);
+-
+-/* MiniDumpWithAvxXStateContext might be undef in server2012r2 or mingw < 12 */
+-#ifndef MiniDumpWithAvxXStateContext
+-#define MiniDumpWithAvxXStateContext 0x00200000
+-#endif
+-        /* We default to a fairly complete dump.  In the future, we may want to
+-         * allow clients to customize what kind of dump to create. */
+-        dump_options = MiniDumpWithFullMemory |
+-                       MiniDumpIgnoreInaccessibleMemory |
+-                       MiniDumpWithAvxXStateContext;
+-
+-        if (MiniDumpWriteDump(process_handle,
+-                              pid,
+-                              hDumpFile,
+-                              dump_options,
+-                              NULL,
+-                              NULL,
+-                              NULL)) {
+-          /* Don't delete the file on close if we successfully wrote it out. */
+-          FILE_DISPOSITION_INFO DontDeleteOnClose = { FALSE };
+-          SetFileInformationByHandle(hDumpFile,
+-                                     FileDispositionInfo,
+-                                     &DontDeleteOnClose,
+-                                     sizeof(DontDeleteOnClose));
+-        }
+-        SymSetOptions(sym_options);
+-        CloseHandle(hDumpFile);
+-      }
+-    }
+-  }
+-
+   switch (signum) {
+-    case SIGQUIT:
+     case SIGTERM:
+     case SIGKILL:
+     case SIGINT: {
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0002-Fix-missing-casts.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0002-Fix-missing-casts.patch
new file mode 100644
index 0000000..4d9603f
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/libuv_patches/0002-Fix-missing-casts.patch
@@ -0,0 +1,1642 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Tue, 26 Apr 2022 15:01:25 -0400
+Subject: [PATCH 02/10] Fix missing casts
+
+---
+ src/fs-poll.c                  | 10 ++++----
+ src/inet.c                     | 11 ++++----
+ src/strscpy.c                  |  2 +-
+ src/thread-common.c            |  2 +-
+ src/threadpool.c               |  2 +-
+ src/unix/bsd-ifaddrs.c         |  2 +-
+ src/unix/core.c                | 18 ++++++-------
+ src/unix/darwin-proctitle.c    |  5 ++--
+ src/unix/darwin.c              |  2 +-
+ src/unix/freebsd.c             |  4 +--
+ src/unix/fs.c                  | 20 +++++++--------
+ src/unix/fsevents.c            | 34 ++++++++++++-------------
+ src/unix/getaddrinfo.c         |  8 +++---
+ src/unix/ibmi.c                |  2 +-
+ src/unix/kqueue.c              |  6 ++---
+ src/unix/linux.c               | 46 +++++++++++++++++-----------------
+ src/unix/loop.c                |  2 +-
+ src/unix/netbsd.c              |  4 +--
+ src/unix/openbsd.c             |  4 +--
+ src/unix/pipe.c                |  4 +--
+ src/unix/poll.c                |  4 +--
+ src/unix/posix-poll.c          |  2 +-
+ src/unix/process.c             |  4 +--
+ src/unix/proctitle.c           |  2 +-
+ src/unix/random-sysctl-linux.c |  2 +-
+ src/unix/stream.c              | 31 ++++++++++++-----------
+ src/unix/thread.c              |  5 ++--
+ src/unix/udp.c                 |  8 +++---
+ src/uv-common.c                | 16 ++++++------
+ src/win/core.c                 |  8 +++---
+ src/win/fs-event.c             |  4 +--
+ src/win/fs-fd-hash-inl.h       |  2 +-
+ src/win/fs.c                   | 28 ++++++++++-----------
+ src/win/pipe.c                 | 12 ++++-----
+ src/win/process.c              | 12 ++++-----
+ src/win/tcp.c                  |  2 +-
+ src/win/thread.c               |  4 +--
+ src/win/util.c                 | 27 ++++++++++----------
+ 38 files changed, 183 insertions(+), 178 deletions(-)
+
+diff --git a/src/fs-poll.c b/src/fs-poll.c
+index 1bac1c568e36cadd0b68451926c6f045f88342d2..5a39daed095502b2db34f23fcaf0ab04f31f96ff 100644
+--- a/src/fs-poll.c
++++ b/src/fs-poll.c
+@@ -77,7 +77,7 @@ int uv_fs_poll_start(uv_fs_poll_t* handle,
+ 
+   loop = handle->loop;
+   len = strlen(path);
+-  ctx = uv__calloc(1, sizeof(*ctx) + len);
++  ctx = (struct poll_ctx*)uv__calloc(1, sizeof(*ctx) + len);
+ 
+   if (ctx == NULL)
+     return UV_ENOMEM;
+@@ -101,7 +101,7 @@ int uv_fs_poll_start(uv_fs_poll_t* handle,
+     goto error;
+ 
+   if (handle->poll_ctx != NULL)
+-    ctx->previous = handle->poll_ctx;
++    ctx->previous = (struct poll_ctx*)handle->poll_ctx;
+   handle->poll_ctx = ctx;
+   uv__handle_start(handle);
+ 
+@@ -119,7 +119,7 @@ int uv_fs_poll_stop(uv_fs_poll_t* handle) {
+   if (!uv_is_active((uv_handle_t*)handle))
+     return 0;
+ 
+-  ctx = handle->poll_ctx;
++  ctx = (struct poll_ctx*)handle->poll_ctx;
+   assert(ctx != NULL);
+   assert(ctx->parent_handle == handle);
+ 
+@@ -144,7 +144,7 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
+     return UV_EINVAL;
+   }
+ 
+-  ctx = handle->poll_ctx;
++  ctx = (struct poll_ctx*)handle->poll_ctx;
+   assert(ctx != NULL);
+ 
+   required_len = strlen(ctx->path);
+@@ -244,7 +244,7 @@ static void timer_close_cb(uv_handle_t* timer) {
+     if (handle->poll_ctx == NULL && uv__is_closing(handle))
+       uv__make_close_pending((uv_handle_t*)handle);
+   } else {
+-    for (last = handle->poll_ctx, it = last->previous;
++    for (last = (struct poll_ctx*)handle->poll_ctx, it = last->previous;
+          it != ctx;
+          last = it, it = it->previous) {
+       assert(last->previous != NULL);
+diff --git a/src/inet.c b/src/inet.c
+index cd77496846e90e8b8e61c63c10f498f153344fe5..dd94bea3886ca37945fcad7909d765e3700e3c21 100644
+--- a/src/inet.c
++++ b/src/inet.c
+@@ -35,9 +35,9 @@ static int inet_pton6(const char *src, unsigned char *dst);
+ int uv_inet_ntop(int af, const void* src, char* dst, size_t size) {
+   switch (af) {
+   case AF_INET:
+-    return (inet_ntop4(src, dst, size));
++    return (inet_ntop4((const unsigned char*)src, dst, size));
+   case AF_INET6:
+-    return (inet_ntop6(src, dst, size));
++    return (inet_ntop6((const unsigned char*)src, dst, size));
+   default:
+     return UV_EAFNOSUPPORT;
+   }
+@@ -149,10 +149,11 @@ int uv_inet_pton(int af, const char* src, void* dst) {
+ 
+   switch (af) {
+   case AF_INET:
+-    return (inet_pton4(src, dst));
++    return (inet_pton4(src, (unsigned char*)dst));
+   case AF_INET6: {
+     int len;
+-    char tmp[UV__INET6_ADDRSTRLEN], *s, *p;
++    char tmp[UV__INET6_ADDRSTRLEN], *s;
++    const char *p;
+     s = (char*) src;
+     p = strchr(src, '%');
+     if (p != NULL) {
+@@ -163,7 +164,7 @@ int uv_inet_pton(int af, const char* src, void* dst) {
+       memcpy(s, src, len);
+       s[len] = '\0';
+     }
+-    return inet_pton6(s, dst);
++    return inet_pton6(s, (unsigned char*)dst);
+   }
+   default:
+     return UV_EAFNOSUPPORT;
+diff --git a/src/strscpy.c b/src/strscpy.c
+index 20df6fcbed29e9d944c866ddbcd5c09345a426b3..6b4cc3bc741b40b9c2b13d4c06e7090f8083a7ba 100644
+--- a/src/strscpy.c
++++ b/src/strscpy.c
+@@ -27,7 +27,7 @@ ssize_t uv__strscpy(char* d, const char* s, size_t n) {
+ 
+   for (i = 0; i < n; i++)
+     if ('\0' == (d[i] = s[i]))
+-      return i > SSIZE_MAX ? UV_E2BIG : (ssize_t) i;
++      return i > SSIZE_MAX ? (ssize_t) UV_E2BIG : (ssize_t) i;
+ 
+   if (i == 0)
+     return 0;
+diff --git a/src/thread-common.c b/src/thread-common.c
+index c67c0a7dd7279af6c67b7d5d4a623c47bdf3fff2..c0e39b543df229dd8cb8492bb695e61e40911453 100644
+--- a/src/thread-common.c
++++ b/src/thread-common.c
+@@ -49,7 +49,7 @@ int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
+   if (barrier == NULL || count == 0)
+     return UV_EINVAL;
+ 
+-  b = uv__malloc(sizeof(*b));
++  b = (struct _uv_barrier *)uv__malloc(sizeof(*b));
+   if (b == NULL)
+     return UV_ENOMEM;
+ #endif
+diff --git a/src/threadpool.c b/src/threadpool.c
+index dbef67f2f10f1df498f228c21eba2a71ceceee29..f572de5aaf1a1b150e58c7b989949441cac279c4 100644
+--- a/src/threadpool.c
++++ b/src/threadpool.c
+@@ -207,7 +207,7 @@ static void init_threads(void) {
+ 
+   threads = default_threads;
+   if (nthreads > ARRAY_SIZE(default_threads)) {
+-    threads = uv__malloc(nthreads * sizeof(threads[0]));
++    threads = (uv_thread_t*)uv__malloc(nthreads * sizeof(threads[0]));
+     if (threads == NULL) {
+       nthreads = ARRAY_SIZE(default_threads);
+       threads = default_threads;
+diff --git a/src/unix/bsd-ifaddrs.c b/src/unix/bsd-ifaddrs.c
+index 11ca95591fc38244e931fecd9dd4038d3968af7a..c3dd71a1889bfae08cfdf95acda61e6c3472bd2c 100644
+--- a/src/unix/bsd-ifaddrs.c
++++ b/src/unix/bsd-ifaddrs.c
+@@ -92,7 +92,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+   }
+ 
+   /* Make sure the memory is initiallized to zero using calloc() */
+-  *addresses = uv__calloc(*count, sizeof(**addresses));
++  *addresses = (uv_interface_address_t*)uv__calloc(*count, sizeof(**addresses));
+ 
+   if (*addresses == NULL) {
+     freeifaddrs(addrs);
+diff --git a/src/unix/core.c b/src/unix/core.c
+index 25c5181f370e94983e8a5f797f02f7a8dc207e00..28c036f94f3e76717afa651451969f128c5a573c 100644
+--- a/src/unix/core.c
++++ b/src/unix/core.c
+@@ -855,7 +855,7 @@ static unsigned int next_power_of_two(unsigned int val) {
+ }
+ 
+ static void maybe_resize(uv_loop_t* loop, unsigned int len) {
+-  uv__io_t** watchers;
++  void** watchers;
+   void* fake_watcher_list;
+   void* fake_watcher_count;
+   unsigned int nwatchers;
+@@ -874,8 +874,8 @@ static void maybe_resize(uv_loop_t* loop, unsigned int len) {
+   }
+ 
+   nwatchers = next_power_of_two(len + 2) - 2;
+-  watchers = uv__reallocf(loop->watchers,
+-                          (nwatchers + 2) * sizeof(loop->watchers[0]));
++  watchers = (void**)
++      uv__reallocf(loop->watchers, (nwatchers + 2) * sizeof(loop->watchers[0]));
+ 
+   if (watchers == NULL)
+     abort();
+@@ -884,7 +884,7 @@ static void maybe_resize(uv_loop_t* loop, unsigned int len) {
+   watchers[nwatchers] = fake_watcher_list;
+   watchers[nwatchers + 1] = fake_watcher_count;
+ 
+-  loop->watchers = watchers;
++  loop->watchers = (uv__io_t**)watchers;
+   loop->nwatchers = nwatchers;
+ }
+ 
+@@ -1216,7 +1216,7 @@ static int uv__getpwuid_r(uv_passwd_t *pwd, uid_t uid) {
+    * is frequently 1024 or 4096, so we can just use that directly. The pwent
+    * will not usually be large. */
+   for (bufsize = 2000;; bufsize *= 2) {
+-    buf = uv__malloc(bufsize);
++    buf = (char*)uv__malloc(bufsize);
+ 
+     if (buf == NULL)
+       return UV_ENOMEM;
+@@ -1242,7 +1242,7 @@ static int uv__getpwuid_r(uv_passwd_t *pwd, uid_t uid) {
+   name_size = strlen(pw.pw_name) + 1;
+   homedir_size = strlen(pw.pw_dir) + 1;
+   shell_size = strlen(pw.pw_shell) + 1;
+-  pwd->username = uv__malloc(name_size + homedir_size + shell_size);
++  pwd->username = (char*)uv__malloc(name_size + homedir_size + shell_size);
+ 
+   if (pwd->username == NULL) {
+     uv__free(buf);
+@@ -1292,7 +1292,7 @@ int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) {
+    * is frequently 1024 or 4096, so we can just use that directly. The pwent
+    * will not usually be large. */
+   for (bufsize = 2000;; bufsize *= 2) {
+-    buf = uv__malloc(bufsize);
++    buf = (char*)uv__malloc(bufsize);
+ 
+     if (buf == NULL)
+       return UV_ENOMEM;
+@@ -1323,7 +1323,7 @@ int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) {
+     members++;
+   }
+ 
+-  gr_mem = uv__malloc(name_size + mem_size);
++  gr_mem = (char*)uv__malloc(name_size + mem_size);
+   if (gr_mem == NULL) {
+     uv__free(buf);
+     return UV_ENOMEM;
+@@ -1380,7 +1380,7 @@ int uv_os_environ(uv_env_item_t** envitems, int* count) {
+ 
+   for (i = 0; environ[i] != NULL; i++);
+ 
+-  *envitems = uv__calloc(i, sizeof(**envitems));
++  *envitems = (uv_env_item_s*)uv__calloc(i, sizeof(**envitems));
+ 
+   if (*envitems == NULL)
+     return UV_ENOMEM;
+diff --git a/src/unix/darwin-proctitle.c b/src/unix/darwin-proctitle.c
+index 5288083ef04fd78d90c34071cc76281adbc310d8..9bd55dd764b845cf8ea441d525b4e136699eb52e 100644
+--- a/src/unix/darwin-proctitle.c
++++ b/src/unix/darwin-proctitle.c
+@@ -128,8 +128,9 @@ int uv__set_process_title(const char* title) {
+   if (pLSSetApplicationInformationItem == NULL)
+     goto out;
+ 
+-  display_name_key = pCFBundleGetDataPointerForName(launch_services_bundle,
+-                                                    S("_kLSDisplayNameKey"));
++  display_name_key = (CFStringRef*)
++      pCFBundleGetDataPointerForName(launch_services_bundle,
++                                     S("_kLSDisplayNameKey"));
+ 
+   if (display_name_key == NULL || *display_name_key == NULL)
+     goto out;
+diff --git a/src/unix/darwin.c b/src/unix/darwin.c
+index 90790d701c4327518d17230c5aa69b9a74112e73..9ee5cd8eb9d02fb8b71986c47fe8a686f0983847 100644
+--- a/src/unix/darwin.c
++++ b/src/unix/darwin.c
+@@ -217,7 +217,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+     return UV_EINVAL;  /* FIXME(bnoordhuis) Translate error. */
+   }
+ 
+-  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
++  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
+   if (!(*cpu_infos)) {
+     vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
+     return UV_ENOMEM;
+diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c
+index 191bc8bc213ffddb15c5e04baa66e2a0a8d69a3d..1bd63886b823be6451ac013d94e29885795375b7 100644
+--- a/src/unix/freebsd.c
++++ b/src/unix/freebsd.c
+@@ -220,7 +220,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+   if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
+     return UV__ERR(errno);
+ 
+-  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
++  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
+   if (!(*cpu_infos))
+     return UV_ENOMEM;
+ 
+@@ -237,7 +237,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+ 
+   size = maxcpus * CPUSTATES * sizeof(long);
+ 
+-  cp_times = uv__malloc(size);
++  cp_times = (long*)uv__malloc(size);
+   if (cp_times == NULL) {
+     uv__free(*cpu_infos);
+     return UV_ENOMEM;
+diff --git a/src/unix/fs.c b/src/unix/fs.c
+index 6b051c124f2fd9b0f72b41d7d7ba9c715e9686e1..e25d02e54dbe93e4b9c22b0140108c99ae2cb4f7 100644
+--- a/src/unix/fs.c
++++ b/src/unix/fs.c
+@@ -140,7 +140,7 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
+       size_t new_path_len;                                                    \
+       path_len = strlen(path) + 1;                                            \
+       new_path_len = strlen(new_path) + 1;                                    \
+-      req->path = uv__malloc(path_len + new_path_len);                        \
++      req->path = (char*)uv__malloc(path_len + new_path_len);                 \
+       if (req->path == NULL)                                                  \
+         return UV_ENOMEM;                                                     \
+       req->new_path = req->path + path_len;                                   \
+@@ -568,7 +568,7 @@ static ssize_t uv__fs_scandir(uv_fs_t* req) {
+ static int uv__fs_opendir(uv_fs_t* req) {
+   uv_dir_t* dir;
+ 
+-  dir = uv__malloc(sizeof(*dir));
++  dir = (uv_dir_t*)uv__malloc(sizeof(*dir));
+   if (dir == NULL)
+     goto error;
+ 
+@@ -592,7 +592,7 @@ static int uv__fs_readdir(uv_fs_t* req) {
+   unsigned int dirent_idx;
+   unsigned int i;
+ 
+-  dir = req->ptr;
++  dir = (uv_dir_t*)req->ptr;
+   dirent_idx = 0;
+ 
+   while (dirent_idx < dir->nentries) {
+@@ -634,7 +634,7 @@ error:
+ static int uv__fs_closedir(uv_fs_t* req) {
+   uv_dir_t* dir;
+ 
+-  dir = req->ptr;
++  dir = (uv_dir_t*)req->ptr;
+ 
+   if (dir->dir != NULL) {
+     closedir(dir->dir);
+@@ -663,7 +663,7 @@ static int uv__fs_statfs(uv_fs_t* req) {
+ #endif /* defined(__sun) */
+     return -1;
+ 
+-  stat_fs = uv__malloc(sizeof(*stat_fs));
++  stat_fs = (uv_statfs_t*)uv__malloc(sizeof(*stat_fs));
+   if (stat_fs == NULL) {
+     errno = ENOMEM;
+     return -1;
+@@ -727,7 +727,7 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
+     maxlen = uv__fs_pathmax_size(req->path);
+ #endif
+ 
+-  buf = uv__malloc(maxlen);
++  buf = (char*)uv__malloc(maxlen);
+ 
+   if (buf == NULL) {
+     errno = ENOMEM;
+@@ -747,7 +747,7 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
+ 
+   /* Uncommon case: resize to make room for the trailing nul byte. */
+   if (len == maxlen) {
+-    buf = uv__reallocf(buf, len + 1);
++    buf = (char*)uv__reallocf(buf, len + 1);
+ 
+     if (buf == NULL)
+       return -1;
+@@ -770,7 +770,7 @@ static ssize_t uv__fs_realpath(uv_fs_t* req) {
+   ssize_t len;
+ 
+   len = uv__fs_pathmax_size(req->path);
+-  buf = uv__malloc(len + 1);
++  buf = (char*)uv__malloc(len + 1);
+ 
+   if (buf == NULL) {
+     errno = ENOMEM;
+@@ -1984,7 +1984,7 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req,
+   req->nbufs = nbufs;
+   req->bufs = req->bufsml;
+   if (nbufs > ARRAY_SIZE(req->bufsml))
+-    req->bufs = uv__malloc(nbufs * sizeof(*bufs));
++    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
+ 
+   if (req->bufs == NULL)
+     return UV_ENOMEM;
+@@ -2171,7 +2171,7 @@ int uv_fs_write(uv_loop_t* loop,
+   req->nbufs = nbufs;
+   req->bufs = req->bufsml;
+   if (nbufs > ARRAY_SIZE(req->bufsml))
+-    req->bufs = uv__malloc(nbufs * sizeof(*bufs));
++    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
+ 
+   if (req->bufs == NULL)
+     return UV_ENOMEM;
+diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c
+index df703f3635fc95bab21debc9697dba06a2a44827..c31d08ba37cfd10672ab6a7a8fd38a1c79b952fe 100644
+--- a/src/unix/fsevents.c
++++ b/src/unix/fsevents.c
+@@ -183,7 +183,7 @@ static void (*pFSEventStreamStop)(FSEventStreamRef);
+ static void uv__fsevents_cb(uv_async_t* cb) {
+   uv_fs_event_t* handle;
+ 
+-  handle = cb->data;
++  handle = (uv_fs_event_t*)cb->data;
+ 
+   UV__FSEVENTS_PROCESS(handle, {
+     handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
+@@ -231,10 +231,10 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
+   FSEventStreamEventFlags flags;
+   struct uv__queue head;
+ 
+-  loop = info;
+-  state = loop->cf_state;
++  loop = (uv_loop_t*)info;
++  state = (uv__cf_loop_state_t*)loop->cf_state;
+   assert(state != NULL);
+-  paths = eventPaths;
++  paths = (char**)eventPaths;
+ 
+   /* For each handle */
+   uv_mutex_lock(&state->fsevent_mutex);
+@@ -304,7 +304,7 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
+           continue;
+       }
+ 
+-      event = uv__malloc(sizeof(*event) + len);
++      event = (uv__fsevents_event_t*)uv__malloc(sizeof(*event) + len);
+       if (event == NULL)
+         break;
+ 
+@@ -438,7 +438,7 @@ static void uv__fsevents_reschedule(uv__cf_loop_state_t* state,
+   uv_mutex_lock(&state->fsevent_mutex);
+   path_count = state->fsevent_handle_count;
+   if (path_count != 0) {
+-    paths = uv__malloc(sizeof(*paths) * path_count);
++    paths = (CFStringRef*)uv__malloc(sizeof(*paths) * path_count);
+     if (paths == NULL) {
+       uv_mutex_unlock(&state->fsevent_mutex);
+       goto final;
+@@ -594,7 +594,7 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) {
+   if (err)
+     return err;
+ 
+-  state = uv__calloc(1, sizeof(*state));
++  state = (uv__cf_loop_state_t*)uv__calloc(1, sizeof(*state));
+   if (state == NULL)
+     return UV_ENOMEM;
+ 
+@@ -696,7 +696,7 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) {
+   }
+ 
+   /* Destroy state */
+-  state = loop->cf_state;
++  state = (uv__cf_loop_state_t*)loop->cf_state;
+   uv_sem_destroy(&state->fsevent_sem);
+   uv_mutex_destroy(&state->fsevent_mutex);
+   pCFRelease(state->signal_source);
+@@ -710,8 +710,8 @@ static void* uv__cf_loop_runner(void* arg) {
+   uv_loop_t* loop;
+   uv__cf_loop_state_t* state;
+ 
+-  loop = arg;
+-  state = loop->cf_state;
++  loop = (uv_loop_t*)arg;
++  state = (uv__cf_loop_state_t*)loop->cf_state;
+   state->loop = pCFRunLoopGetCurrent();
+ 
+   pCFRunLoopAddSource(state->loop,
+@@ -739,8 +739,8 @@ static void uv__cf_loop_cb(void* arg) {
+   struct uv__queue split_head;
+   uv__cf_loop_signal_t* s;
+ 
+-  loop = arg;
+-  state = loop->cf_state;
++  loop = (uv_loop_t*)arg;
++  state = (uv__cf_loop_state_t*)loop->cf_state;
+ 
+   uv_mutex_lock(&loop->cf_mutex);
+   uv__queue_move(&loop->cf_signals, &split_head);
+@@ -770,7 +770,7 @@ int uv__cf_loop_signal(uv_loop_t* loop,
+   uv__cf_loop_signal_t* item;
+   uv__cf_loop_state_t* state;
+ 
+-  item = uv__malloc(sizeof(*item));
++  item = (uv__cf_loop_signal_t*)uv__malloc(sizeof(*item));
+   if (item == NULL)
+     return UV_ENOMEM;
+ 
+@@ -780,7 +780,7 @@ int uv__cf_loop_signal(uv_loop_t* loop,
+   uv_mutex_lock(&loop->cf_mutex);
+   uv__queue_insert_tail(&loop->cf_signals, &item->member);
+ 
+-  state = loop->cf_state;
++  state = (uv__cf_loop_state_t*)loop->cf_state;
+   assert(state != NULL);
+   pCFRunLoopSourceSignal(state->signal_source);
+   pCFRunLoopWakeUp(state->loop);
+@@ -814,7 +814,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) {
+    * Events will occur in other thread.
+    * Initialize callback for getting them back into event loop's thread
+    */
+-  handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb));
++  handle->cf_cb = (uv_async_t*)uv__malloc(sizeof(*handle->cf_cb));
+   if (handle->cf_cb == NULL) {
+     err = UV_ENOMEM;
+     goto fail_cf_cb_malloc;
+@@ -830,7 +830,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) {
+     goto fail_cf_mutex_init;
+ 
+   /* Insert handle into the list */
+-  state = handle->loop->cf_state;
++  state = (uv__cf_loop_state_t*)handle->loop->cf_state;
+   uv_mutex_lock(&state->fsevent_mutex);
+   uv__queue_insert_tail(&state->fsevent_handles, &handle->cf_member);
+   state->fsevent_handle_count++;
+@@ -870,7 +870,7 @@ int uv__fsevents_close(uv_fs_event_t* handle) {
+     return UV_EINVAL;
+ 
+   /* Remove handle from  the list */
+-  state = handle->loop->cf_state;
++  state = (uv__cf_loop_state_t*)handle->loop->cf_state;
+   uv_mutex_lock(&state->fsevent_mutex);
+   uv__queue_remove(&handle->cf_member);
+   state->fsevent_handle_count--;
+diff --git a/src/unix/getaddrinfo.c b/src/unix/getaddrinfo.c
+index 77337ace9454e032a392c97cb9aa311f15518956..41dc3909969a643e129847ae3a3252d51feadb27 100644
+--- a/src/unix/getaddrinfo.c
++++ b/src/unix/getaddrinfo.c
+@@ -172,7 +172,7 @@ int uv_getaddrinfo(uv_loop_t* loop,
+   hostname_len = hostname ? strlen(hostname) + 1 : 0;
+   service_len = service ? strlen(service) + 1 : 0;
+   hints_len = hints ? sizeof(*hints) : 0;
+-  buf = uv__malloc(hostname_len + service_len + hints_len);
++  buf = (char*)uv__malloc(hostname_len + service_len + hints_len);
+ 
+   if (buf == NULL)
+     return UV_ENOMEM;
+@@ -190,17 +190,17 @@ int uv_getaddrinfo(uv_loop_t* loop,
+   len = 0;
+ 
+   if (hints) {
+-    req->hints = memcpy(buf + len, hints, sizeof(*hints));
++    req->hints = (struct addrinfo*)memcpy(buf + len, hints, sizeof(*hints));
+     len += sizeof(*hints);
+   }
+ 
+   if (service) {
+-    req->service = memcpy(buf + len, service, service_len);
++    req->service = (char*)memcpy(buf + len, service, service_len);
+     len += service_len;
+   }
+ 
+   if (hostname)
+-    req->hostname = memcpy(buf + len, hostname, hostname_len);
++    req->hostname = (char*)memcpy(buf + len, hostname, hostname_len);
+ 
+   if (cb) {
+     uv__work_submit(loop,
+diff --git a/src/unix/ibmi.c b/src/unix/ibmi.c
+index 837bba6e2fef7b834a8d104d263bef47eaed0950..5e0fa98d104428534e5264a1c6358e3f68c58b82 100644
+--- a/src/unix/ibmi.c
++++ b/src/unix/ibmi.c
+@@ -293,7 +293,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+ 
+   numcpus = sysconf(_SC_NPROCESSORS_ONLN);
+ 
+-  *cpu_infos = uv__malloc(numcpus * sizeof(uv_cpu_info_t));
++  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(uv_cpu_info_t));
+   if (!*cpu_infos) {
+     return UV_ENOMEM;
+   }
+diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c
+index b78242d3be4e3cf6b7b998f56dc65213982d4bc7..28e55aae6c613576ede7024a5c73d746e134d865 100644
+--- a/src/unix/kqueue.c
++++ b/src/unix/kqueue.c
+@@ -299,8 +299,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
+     nevents = 0;
+ 
+     assert(loop->watchers != NULL);
+-    loop->watchers[loop->nwatchers] = (void*) events;
+-    loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
++    loop->watchers[loop->nwatchers] = (uv__io_t*) events;
++    loop->watchers[loop->nwatchers + 1] = (uv__io_t*) (uintptr_t) nfds;
+     for (i = 0; i < nfds; i++) {
+       ev = events + i;
+       fd = ev->ident;
+@@ -322,7 +322,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
+       /* Skip invalidated events, see uv__platform_invalidate_fd */
+       if (fd == -1)
+         continue;
+-      w = loop->watchers[fd];
++      w = (uv__io_t*)loop->watchers[fd];
+ 
+       if (w == NULL) {
+         /* File descriptor that we've stopped watching, disarm it. */
+diff --git a/src/unix/linux.c b/src/unix/linux.c
+index 48b9c2c43e104079d3ccb5d830d1d79f891fb1a3..9173850bd158eaf9c41deca38f9ba84762a027a1 100644
+--- a/src/unix/linux.c
++++ b/src/unix/linux.c
+@@ -456,8 +456,8 @@ static void uv__iou_init(int epollfd,
+   char* sqe;
+   int ringfd;
+ 
+-  sq = MAP_FAILED;
+-  sqe = MAP_FAILED;
++  sq = (char*)MAP_FAILED;
++  sqe = (char*)MAP_FAILED;
+ 
+   if (!uv__use_io_uring())
+     return;
+@@ -496,14 +496,14 @@ static void uv__iou_init(int epollfd,
+   maxlen = sqlen < cqlen ? cqlen : sqlen;
+   sqelen = params.sq_entries * sizeof(struct uv__io_uring_sqe);
+ 
+-  sq = mmap(0,
++  sq = (char*)mmap(0,
+             maxlen,
+             PROT_READ | PROT_WRITE,
+             MAP_SHARED | MAP_POPULATE,
+             ringfd,
+             0);  /* IORING_OFF_SQ_RING */
+ 
+-  sqe = mmap(0,
++  sqe = (char*)mmap(0,
+              sqelen,
+              PROT_READ | PROT_WRITE,
+              MAP_SHARED | MAP_POPULATE,
+@@ -643,7 +643,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+   int i;
+ 
+   lfields = uv__get_internal_fields(loop);
+-  inv = lfields->inv;
++  inv = (uv__invalidate*)lfields->inv;
+ 
+   /* Invalidate events with same file descriptor */
+   if (inv != NULL)
+@@ -718,7 +718,7 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
+     return NULL;  /* No room in ring buffer. TODO(bnoordhuis) maybe flush it? */
+ 
+   slot = tail & mask;
+-  sqe = iou->sqe;
++  sqe = (uv__io_uring_sqe*)iou->sqe;
+   sqe = &sqe[slot];
+   memset(sqe, 0, sizeof(*sqe));
+   sqe->user_data = (uintptr_t) req;
+@@ -986,7 +986,7 @@ int uv__iou_fs_statx(uv_loop_t* loop,
+   struct uv__statx* statxbuf;
+   struct uv__iou* iou;
+ 
+-  statxbuf = uv__malloc(sizeof(*statxbuf));
++  statxbuf = (struct uv__statx*)uv__malloc(sizeof(*statxbuf));
+   if (statxbuf == NULL)
+     return 0;
+ 
+@@ -1050,7 +1050,7 @@ static void uv__iou_fs_statx_post(uv_fs_t* req) {
+   uv_stat_t* buf;
+ 
+   buf = &req->statbuf;
+-  statxbuf = req->ptr;
++  statxbuf = (struct uv__statx*)req->ptr;
+   req->ptr = NULL;
+ 
+   if (req->result == 0) {
+@@ -1079,7 +1079,7 @@ static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) {
+   tail = atomic_load_explicit((_Atomic uint32_t*) iou->cqtail,
+                               memory_order_acquire);
+   mask = iou->cqmask;
+-  cqe = iou->cqe;
++  cqe = (uv__io_uring_cqe*)iou->cqe;
+   nevents = 0;
+ 
+   for (i = head; i != tail; i++) {
+@@ -1170,7 +1170,7 @@ static void uv__epoll_ctl_prep(int epollfd,
+     pe = &(*events)[slot];
+     *pe = *e;
+ 
+-    sqe = ctl->sqe;
++    sqe = (uv__io_uring_sqe*)ctl->sqe;
+     sqe = &sqe[slot];
+ 
+     memset(sqe, 0, sizeof(*sqe));
+@@ -1226,7 +1226,7 @@ static void uv__epoll_ctl_flush(int epollfd,
+   while (*ctl->cqhead != *ctl->cqtail) {
+     slot = (*ctl->cqhead)++ & ctl->cqmask;
+ 
+-    cqe = ctl->cqe;
++    cqe = (uv__io_uring_cqe*)ctl->cqe;
+     cqe = &cqe[slot];
+ 
+     if (cqe->res == 0)
+@@ -1708,7 +1708,7 @@ int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
+   snprintf(*models, sizeof(*models), "unknown");
+   maxcpu = 0;
+ 
+-  cpus = uv__calloc(ARRAY_SIZE(*cpus), sizeof(**cpus));
++  cpus = (decltype(cpus))uv__calloc(ARRAY_SIZE(*cpus), sizeof(**cpus));
+   if (cpus == NULL)
+     return UV_ENOMEM;
+ 
+@@ -1764,9 +1764,9 @@ int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
+ 
+     /* arm64: translate CPU part code to model name. */
+     if (*parts) {
+-      p = memmem(parts, sizeof(parts) - 1, p, n + 1);
++      p = (char*)memmem(parts, sizeof(parts) - 1, p, n + 1);
+       if (p == NULL)
+-        p = "unknown";
++        p = const_cast<char*>("unknown");
+       else
+         p += n + 1;
+       n = (int) strcspn(p, "\n");
+@@ -1815,7 +1815,7 @@ nocpuinfo:
+   }
+ 
+   size = n * sizeof(**ci) + sizeof(models);
+-  *ci = uv__malloc(size);
++  *ci = (uv_cpu_info_t*)uv__malloc(size);
+   *count = 0;
+ 
+   if (*ci == NULL) {
+@@ -1824,7 +1824,7 @@ nocpuinfo:
+   }
+ 
+   *count = n;
+-  p = memcpy(*ci + n, models, sizeof(models));
++  p = (char*)memcpy(*ci + n, models, sizeof(models));
+ 
+   i = 0;
+   for (cpu = 0; cpu < maxcpu; cpu++) {
+@@ -1833,19 +1833,19 @@ nocpuinfo:
+ 
+     c = *cpus + cpu;
+ 
+-    (*ci)[i++] = (uv_cpu_info_t) {
++    (*ci)[i++] = uv_cpu_info_t{
+       .model     = p + c->model * sizeof(*model),
+-      .speed     = c->freq / 1000,
++      .speed     = (int)(c->freq / 1000),
+       /* Note: sysconf(_SC_CLK_TCK) is fixed at 100 Hz,
+        * therefore the multiplier is always 1000/100 = 10.
+        */
+-      .cpu_times = (struct uv_cpu_times_s) {
++      .cpu_times = {
+         .user = 10 * c->user,
+         .nice = 10 * c->nice,
+         .sys  = 10 * c->sys,
+         .idle = 10 * c->idle,
+         .irq  = 10 * c->irq,
+-      },
++      }
+     };
+   }
+ 
+@@ -1902,7 +1902,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+   }
+ 
+   /* Make sure the memory is initiallized to zero using calloc() */
+-  *addresses = uv__calloc(*count, sizeof(**addresses));
++  *addresses = (uv_interface_address_t*)uv__calloc(*count, sizeof(**addresses));
+   if (!(*addresses)) {
+     freeifaddrs(addrs);
+     return UV_ENOMEM;
+@@ -2470,12 +2470,12 @@ int uv_fs_event_start(uv_fs_event_t* handle,
+     goto no_insert;
+ 
+   len = strlen(path) + 1;
+-  w = uv__malloc(sizeof(*w) + len);
++  w = (watcher_list*)uv__malloc(sizeof(*w) + len);
+   if (w == NULL)
+     return UV_ENOMEM;
+ 
+   w->wd = wd;
+-  w->path = memcpy(w + 1, path, len);
++  w->path = (char*)memcpy(w + 1, path, len);
+   uv__queue_init(&w->watchers);
+   w->iterating = 0;
+   RB_INSERT(watcher_root, uv__inotify_watchers(loop), w);
+diff --git a/src/unix/loop.c b/src/unix/loop.c
+index a9468e8e19cbede795032980c47eb83aee1e0c68..3babe4d701949ebc69d74f7dedee33c777d89892 100644
+--- a/src/unix/loop.c
++++ b/src/unix/loop.c
+@@ -148,7 +148,7 @@ int uv_loop_fork(uv_loop_t* loop) {
+ 
+   /* Rearm all the watchers that aren't re-queued by the above. */
+   for (i = 0; i < loop->nwatchers; i++) {
+-    w = loop->watchers[i];
++    w = (uv__io_t*)loop->watchers[i];
+     if (w == NULL)
+       continue;
+ 
+diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c
+index fa21e98e41aec8b7d8bc46c299c4c20a7a0c3f0c..4c6d5a24fe896d89e3b2f1db1dc2b1dd7d010aec 100644
+--- a/src/unix/netbsd.c
++++ b/src/unix/netbsd.c
+@@ -211,14 +211,14 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+     cpuspeed = 0;
+ 
+   size = numcpus * CPUSTATES * sizeof(*cp_times);
+-  cp_times = uv__malloc(size);
++  cp_times = (u_int64_t*)uv__malloc(size);
+   if (cp_times == NULL)
+     return UV_ENOMEM;
+ 
+   if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0))
+     return UV__ERR(errno);
+ 
+-  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
++  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
+   if (!(*cpu_infos)) {
+     uv__free(cp_times);
+     uv__free(*cpu_infos);
+diff --git a/src/unix/openbsd.c b/src/unix/openbsd.c
+index 9c863b6c90dad9864cb8341c2b6203c1390a9487..2aa61e2ee3321d91ba84887c7ed6dcfa23d00ccf 100644
+--- a/src/unix/openbsd.c
++++ b/src/unix/openbsd.c
+@@ -72,7 +72,7 @@ int uv_exepath(char* buffer, size_t* size) {
+   mypid = getpid();
+   for (;;) {
+     err = UV_ENOMEM;
+-    argsbuf = uv__reallocf(argsbuf, argsbuf_size);
++    argsbuf = (char**)uv__reallocf(argsbuf, argsbuf_size);
+     if (argsbuf == NULL)
+       goto out;
+     mib[0] = CTL_KERN;
+@@ -202,7 +202,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+   if (sysctl(which, ARRAY_SIZE(which), &numcpus, &size, NULL, 0))
+     return UV__ERR(errno);
+ 
+-  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
++  *cpu_infos = (uv_cpu_info_t*)uv__malloc(numcpus * sizeof(**cpu_infos));
+   if (!(*cpu_infos))
+     return UV_ENOMEM;
+ 
+diff --git a/src/unix/pipe.c b/src/unix/pipe.c
+index d332f3518303d6ef1dee1d835c392bb60b935bad..a60b1a0e442bb3c482575da117f30e9f119f55d3 100644
+--- a/src/unix/pipe.c
++++ b/src/unix/pipe.c
+@@ -377,7 +377,7 @@ int uv_pipe_pending_count(uv_pipe_t* handle) {
+   if (handle->queued_fds == NULL)
+     return 1;
+ 
+-  queued_fds = handle->queued_fds;
++  queued_fds = (uv__stream_queued_fds_t*)(handle->queued_fds);
+   return queued_fds->offset + 1;
+ }
+ 
+@@ -414,7 +414,7 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
+   if (r != UV_ENOBUFS)
+     return r;
+ 
+-  name_buffer = uv__malloc(name_len);
++  name_buffer = (char*)uv__malloc(name_len);
+   if (name_buffer == NULL)
+     return UV_ENOMEM;
+ 
+diff --git a/src/unix/poll.c b/src/unix/poll.c
+index 7a12e2d1488a9d48712439e62c6637b9e1161f69..c21722b2e8eef4d16c523f7319fb57c3167d8dd9 100644
+--- a/src/unix/poll.c
++++ b/src/unix/poll.c
+@@ -117,7 +117,7 @@ int uv_poll_stop(uv_poll_t* handle) {
+ 
+ 
+ int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) {
+-  uv__io_t** watchers;
++  void** watchers;
+   uv__io_t* w;
+   int events;
+ 
+@@ -125,7 +125,7 @@ int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) {
+                       UV_PRIORITIZED)) == 0);
+   assert(!uv__is_closing(handle));
+ 
+-  watchers = handle->loop->watchers;
++  watchers = (void**)handle->loop->watchers;
+   w = &handle->io_watcher;
+ 
+   if (uv__fd_exists(handle->loop, w->fd))
+diff --git a/src/unix/posix-poll.c b/src/unix/posix-poll.c
+index 2e016c2fbaed2eeccd78080969a86aff821a4251..b71eee3f01a3f30b3b5efef539194139f258009a 100644
+--- a/src/unix/posix-poll.c
++++ b/src/unix/posix-poll.c
+@@ -61,7 +61,7 @@ static void uv__pollfds_maybe_resize(uv_loop_t* loop) {
+     return;
+ 
+   n = loop->poll_fds_size ? loop->poll_fds_size * 2 : 64;
+-  p = uv__reallocf(loop->poll_fds, n * sizeof(*loop->poll_fds));
++  p = (struct pollfd*)uv__reallocf(loop->poll_fds, n * sizeof(*loop->poll_fds));
+   if (p == NULL)
+     abort();
+ 
+diff --git a/src/unix/process.c b/src/unix/process.c
+index dd58c18d9b9359fca1a924698c39bd6390dafbe0..2d622c956de6d7b6f8a04be3ecd311f39602b241 100644
+--- a/src/unix/process.c
++++ b/src/unix/process.c
+@@ -423,7 +423,7 @@ static int posix_spawn_can_use_setsid;
+ static void uv__spawn_init_posix_spawn_fncs(void) {
+   /* Try to locate all non-portable functions at runtime */
+   posix_spawn_fncs.file_actions.addchdir_np =
+-    dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addchdir_np");
++    (int (*)(void* const*, const char*)) dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addchdir_np");
+ }
+ 
+ 
+@@ -988,7 +988,7 @@ int uv_spawn(uv_loop_t* loop,
+   err = UV_ENOMEM;
+   pipes = pipes_storage;
+   if (stdio_count > (int) ARRAY_SIZE(pipes_storage))
+-    pipes = uv__malloc(stdio_count * sizeof(*pipes));
++    pipes = (int (*)[2])uv__malloc(stdio_count * sizeof(*pipes));
+ 
+   if (pipes == NULL)
+     goto error;
+diff --git a/src/unix/proctitle.c b/src/unix/proctitle.c
+index 9d1f00ddf66e291abd40d0c0052a7f9bd5c03017..8cdec753d003ebe16485db2b47ffb3863a9473ff 100644
+--- a/src/unix/proctitle.c
++++ b/src/unix/proctitle.c
+@@ -65,7 +65,7 @@ char** uv_setup_args(int argc, char** argv) {
+   /* Add space for the argv pointers. */
+   size += (argc + 1) * sizeof(char*);
+ 
+-  new_argv = uv__malloc(size);
++  new_argv = (char**)uv__malloc(size);
+   if (new_argv == NULL)
+     return argv;
+ 
+diff --git a/src/unix/random-sysctl-linux.c b/src/unix/random-sysctl-linux.c
+index 66ba8d74ec22b72d318b91d82365f5b9693feb3c..9ef18df01a51aa6a26ae6d1d9660a819d18604d0 100644
+--- a/src/unix/random-sysctl-linux.c
++++ b/src/unix/random-sysctl-linux.c
+@@ -48,7 +48,7 @@ int uv__random_sysctl(void* buf, size_t buflen) {
+   char* pe;
+   size_t n;
+ 
+-  p = buf;
++  p = (char*)buf;
+   pe = p + buflen;
+ 
+   while (p < pe) {
+diff --git a/src/unix/stream.c b/src/unix/stream.c
+index 28c4d5463c4622725a433b8807e5e7bde580dadd..265ddade7aec129eb9dbf07cde2a16a0e341d1a7 100644
+--- a/src/unix/stream.c
++++ b/src/unix/stream.c
+@@ -123,7 +123,7 @@ static void uv__stream_osx_interrupt_select(uv_stream_t* stream) {
+   uv__stream_select_t* s;
+   int r;
+ 
+-  s = stream->select;
++  s = (uv__stream_select_t*)stream->select;
+   if (s == NULL)
+     return;
+ 
+@@ -152,8 +152,8 @@ static void uv__stream_osx_select(void* arg) {
+   int r;
+   int max_fd;
+ 
+-  stream = arg;
+-  s = stream->select;
++  stream = (uv_stream_t*)arg;
++  s = (uv__stream_select_t*)stream->select;
+   fd = s->fd;
+ 
+   if (fd > s->int_fd)
+@@ -330,7 +330,7 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) {
+   sread_sz = ROUND_UP(max_fd + 1, sizeof(uint32_t) * NBBY) / NBBY;
+   swrite_sz = sread_sz;
+ 
+-  s = uv__malloc(sizeof(*s) + sread_sz + swrite_sz);
++  s = (uv__stream_select_t*)uv__malloc(sizeof(*s) + sread_sz + swrite_sz);
+   if (s == NULL) {
+     err = UV_ENOMEM;
+     goto failed_malloc;
+@@ -573,7 +573,7 @@ done:
+   if (server->queued_fds != NULL) {
+     uv__stream_queued_fds_t* queued_fds;
+ 
+-    queued_fds = server->queued_fds;
++    queued_fds = (uv__stream_queued_fds_t*)(server->queued_fds);
+ 
+     /* Read first */
+     server->accepted_fd = queued_fds->fds[0];
+@@ -942,11 +942,12 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) {
+   uv__stream_queued_fds_t* queued_fds;
+   unsigned int queue_size;
+ 
+-  queued_fds = stream->queued_fds;
++  queued_fds = (uv__stream_queued_fds_t*)stream->queued_fds;
+   if (queued_fds == NULL) {
+     queue_size = 8;
+-    queued_fds = uv__malloc((queue_size - 1) * sizeof(*queued_fds->fds) +
+-                            sizeof(*queued_fds));
++    queued_fds = (uv__stream_queued_fds_t*)
++        uv__malloc((queue_size - 1) * sizeof(*queued_fds->fds) +
++                   sizeof(*queued_fds));
+     if (queued_fds == NULL)
+       return UV_ENOMEM;
+     queued_fds->size = queue_size;
+@@ -956,9 +957,9 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) {
+     /* Grow */
+   } else if (queued_fds->size == queued_fds->offset) {
+     queue_size = queued_fds->size + 8;
+-    queued_fds = uv__realloc(queued_fds,
+-                             (queue_size - 1) * sizeof(*queued_fds->fds) +
+-                              sizeof(*queued_fds));
++    queued_fds = (uv__stream_queued_fds_t*)
++        uv__realloc(queued_fds, (queue_size - 1) * sizeof(*queued_fds->fds) +
++                    sizeof(*queued_fds));
+ 
+     /*
+      * Allocation failure, report back.
+@@ -1356,7 +1357,7 @@ int uv_write2(uv_write_t* req,
+ 
+   req->bufs = req->bufsml;
+   if (nbufs > ARRAY_SIZE(req->bufsml))
+-    req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
++    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(bufs[0]));
+ 
+   if (req->bufs == NULL)
+     return UV_ENOMEM;
+@@ -1490,7 +1491,7 @@ int uv___stream_fd(const uv_stream_t* handle) {
+          handle->type == UV_TTY ||
+          handle->type == UV_NAMED_PIPE);
+ 
+-  s = handle->select;
++  s = (const uv__stream_select_t*)handle->select;
+   if (s != NULL)
+     return s->fd;
+ 
+@@ -1508,7 +1509,7 @@ void uv__stream_close(uv_stream_t* handle) {
+   if (handle->select != NULL) {
+     uv__stream_select_t* s;
+ 
+-    s = handle->select;
++    s = (uv__stream_select_t*)handle->select;
+ 
+     uv_sem_post(&s->close_sem);
+     uv_sem_post(&s->async_sem);
+@@ -1543,7 +1544,7 @@ void uv__stream_close(uv_stream_t* handle) {
+ 
+   /* Close all queued fds */
+   if (handle->queued_fds != NULL) {
+-    queued_fds = handle->queued_fds;
++    queued_fds = (uv__stream_queued_fds_t*)(handle->queued_fds);
+     for (i = 0; i < queued_fds->offset; i++)
+       uv__close(queued_fds->fds[i]);
+     uv__free(handle->queued_fds);
+diff --git a/src/unix/thread.c b/src/unix/thread.c
+index 4d6f4b8232ec6dc0bd27258a1315340e3e272ae9..531c6211bb4321e5f11031a0644b4e3ab9174396 100644
+--- a/src/unix/thread.c
++++ b/src/unix/thread.c
+@@ -168,8 +168,7 @@ int uv_thread_create_ex(uv_thread_t* tid,
+       abort();
+   }
+ 
+-  f.in = entry;
+-  err = pthread_create(tid, attr, f.out, arg);
++  err = pthread_create(tid, attr, (void*(*)(void*)) (void(*)(void)) entry, arg);
+ 
+   if (attr != NULL)
+     pthread_attr_destroy(attr);
+@@ -540,7 +539,7 @@ static int uv__custom_sem_init(uv_sem_t* sem_, unsigned int value) {
+   int err;
+   uv_semaphore_t* sem;
+ 
+-  sem = uv__malloc(sizeof(*sem));
++  sem = (uv_semaphore_t*)uv__malloc(sizeof(*sem));
+   if (sem == NULL)
+     return UV_ENOMEM;
+ 
+diff --git a/src/unix/udp.c b/src/unix/udp.c
+index c2814512a5f507ceb9e764cdb7c6ff3d36e77974..cbee16b22a36b1c82e74f6a81c3811052e9b8482 100644
+--- a/src/unix/udp.c
++++ b/src/unix/udp.c
+@@ -191,11 +191,11 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
+       if (msgs[k].msg_hdr.msg_flags & MSG_TRUNC)
+         flags |= UV_UDP_PARTIAL;
+ 
+-      chunk_buf = uv_buf_init(iov[k].iov_base, iov[k].iov_len);
++      chunk_buf = uv_buf_init((char*) iov[k].iov_base, iov[k].iov_len);
+       handle->recv_cb(handle,
+                       msgs[k].msg_len,
+                       &chunk_buf,
+-                      msgs[k].msg_hdr.msg_name,
++                      (const sockaddr*) msgs[k].msg_hdr.msg_name,
+                       flags);
+     }
+ 
+@@ -245,7 +245,7 @@ static void uv__udp_recvmsg(uv_udp_t* handle) {
+     memset(&peer, 0, sizeof(peer));
+     h.msg_name = &peer;
+     h.msg_namelen = sizeof(peer);
+-    h.msg_iov = (void*) &buf;
++    h.msg_iov = (iovec*) &buf;
+     h.msg_iovlen = 1;
+ 
+     do {
+@@ -719,7 +719,7 @@ int uv__udp_send(uv_udp_send_t* req,
+ 
+   req->bufs = req->bufsml;
+   if (nbufs > ARRAY_SIZE(req->bufsml))
+-    req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
++    req->bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(bufs[0]));
+ 
+   if (req->bufs == NULL) {
+     uv__req_unregister(handle->loop, req);
+diff --git a/src/uv-common.c b/src/uv-common.c
+index 916f3f4e00664ab47f94d2a6ee7c6a9ef0461389..c04f93596ab1f730576256d86e216ccb7f258b72 100644
+--- a/src/uv-common.c
++++ b/src/uv-common.c
+@@ -54,10 +54,10 @@ static uv__allocator_t uv__allocator = {
+ 
+ char* uv__strdup(const char* s) {
+   size_t len = strlen(s) + 1;
+-  char* m = uv__malloc(len);
++  char* m = (char*)uv__malloc(len);
+   if (m == NULL)
+     return NULL;
+-  return memcpy(m, s, len);
++  return (char*)memcpy(m, s, len);
+ }
+ 
+ char* uv__strndup(const char* s, size_t n) {
+@@ -65,11 +65,11 @@ char* uv__strndup(const char* s, size_t n) {
+   size_t len = strlen(s);
+   if (n < len)
+     len = n;
+-  m = uv__malloc(len + 1);
++  m = (char*)uv__malloc(len + 1);
+   if (m == NULL)
+     return NULL;
+   m[len] = '\0';
+-  return memcpy(m, s, len);
++  return (char*)memcpy(m, s, len);
+ }
+ 
+ void* uv__malloc(size_t size) {
+@@ -688,7 +688,7 @@ void uv__fs_scandir_cleanup(uv_fs_t* req) {
+   unsigned int n;
+ 
+   if (req->result >= 0) {
+-    dents = req->ptr;
++    dents = (uv__dirent_t**)(req->ptr);
+     nbufs = uv__get_nbufs(req);
+ 
+     i = 0;
+@@ -721,7 +721,7 @@ int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) {
+   nbufs = uv__get_nbufs(req);
+   assert(nbufs);
+ 
+-  dents = req->ptr;
++  dents = (uv__dirent_t**)(req->ptr);
+ 
+   /* Free previous entity */
+   if (*nbufs > 0)
+@@ -786,7 +786,7 @@ void uv__fs_readdir_cleanup(uv_fs_t* req) {
+   if (req->ptr == NULL)
+     return;
+ 
+-  dir = req->ptr;
++  dir = (uv_dir_t*)req->ptr;
+   dirents = dir->dirents;
+   req->ptr = NULL;
+ 
+@@ -832,7 +832,7 @@ uv_loop_t* uv_default_loop(void) {
+ uv_loop_t* uv_loop_new(void) {
+   uv_loop_t* loop;
+ 
+-  loop = uv__malloc(sizeof(*loop));
++  loop = (uv_loop_t*)uv__malloc(sizeof(*loop));
+   if (loop == NULL)
+     return NULL;
+ 
+diff --git a/src/win/core.c b/src/win/core.c
+index e9885a0f1ff3890a8d957c8793e22b01cedc0e97..87ade7ad65243ee3ff940320f84e1960390300e1 100644
+--- a/src/win/core.c
++++ b/src/win/core.c
+@@ -98,7 +98,8 @@ static int uv__loops_add(uv_loop_t* loop) {
+ 
+   if (uv__loops_size == uv__loops_capacity) {
+     new_capacity = uv__loops_capacity + UV__LOOPS_CHUNK_SIZE;
+-    new_loops = uv__realloc(uv__loops, sizeof(uv_loop_t*) * new_capacity);
++    new_loops = (uv_loop_t**)
++        uv__realloc(uv__loops, sizeof(uv_loop_t*) * new_capacity);
+     if (!new_loops)
+       goto failed_loops_realloc;
+     uv__loops = new_loops;
+@@ -152,7 +153,8 @@ static void uv__loops_remove(uv_loop_t* loop) {
+   smaller_capacity = uv__loops_capacity / 2;
+   if (uv__loops_size >= smaller_capacity)
+     goto loop_removed;
+-  new_loops = uv__realloc(uv__loops, sizeof(uv_loop_t*) * smaller_capacity);
++  new_loops = (uv_loop_t**)
++      uv__realloc(uv__loops, sizeof(uv_loop_t*) * smaller_capacity);
+   if (!new_loops)
+     goto loop_removed;
+   uv__loops = new_loops;
+@@ -264,7 +266,7 @@ int uv_loop_init(uv_loop_t* loop) {
+ 
+   loop->endgame_handles = NULL;
+ 
+-  loop->timer_heap = timer_heap = uv__malloc(sizeof(*timer_heap));
++  loop->timer_heap = timer_heap = (heap*)uv__malloc(sizeof(*timer_heap));
+   if (timer_heap == NULL) {
+     err = UV_ENOMEM;
+     goto fail_timers_alloc;
+diff --git a/src/win/fs-event.c b/src/win/fs-event.c
+index 6758c7c78bc1d6a5316a8ae7dc2d1e23cd0f32bc..150467313d576bfe2966b55f3d8cffa23cbb8ea3 100644
+--- a/src/win/fs-event.c
++++ b/src/win/fs-event.c
+@@ -73,7 +73,7 @@ static void uv__relative_path(const WCHAR* filename,
+   if (dirlen > 0 && dir[dirlen - 1] == '\\')
+     dirlen--;
+   relpathlen = filenamelen - dirlen - 1;
+-  *relpath = uv__malloc((relpathlen + 1) * sizeof(WCHAR));
++  *relpath = (WCHAR*)uv__malloc((relpathlen + 1) * sizeof(WCHAR));
+   if (!*relpath)
+     uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+   wcsncpy(*relpath, filename + dirlen + 1, relpathlen);
+@@ -242,7 +242,7 @@ int uv_fs_event_start(uv_fs_event_t* handle,
+     if (short_path_buffer_len == 0) {
+       goto short_path_done;
+     }
+-    short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR));
++    short_path_buffer = (WCHAR*)uv__malloc(short_path_buffer_len * sizeof(WCHAR));
+     if (short_path_buffer == NULL) {
+       goto short_path_done;
+     }
+diff --git a/src/win/fs-fd-hash-inl.h b/src/win/fs-fd-hash-inl.h
+index 0b532af12d4371c2311bd50a66913287a0716f43..703a8d8f87de1089ac8b18bd817d416d48dc442e 100644
+--- a/src/win/fs-fd-hash-inl.h
++++ b/src/win/fs-fd-hash-inl.h
+@@ -146,7 +146,7 @@ INLINE static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) {
+ 
+     if (bucket_ptr->size != 0 && i == 0) {
+       struct uv__fd_hash_entry_group_s* new_group_ptr =
+-        uv__malloc(sizeof(*new_group_ptr));
++        (struct uv__fd_hash_entry_group_s*)uv__malloc(sizeof(*new_group_ptr));
+       if (new_group_ptr == NULL) {
+         uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+       }
+diff --git a/src/win/fs.c b/src/win/fs.c
+index fc209c54f470edaa031009979061cff071cbf66d..328c8f2e0513562b53c948ffea59d0841e14b264 100644
+--- a/src/win/fs.c
++++ b/src/win/fs.c
+@@ -258,7 +258,7 @@ INLINE static int fs__capture_path(uv_fs_t* req, const char* path,
+     return 0;
+   }
+ 
+-  buf = uv__malloc(buf_sz);
++  buf = (WCHAR *)uv__malloc(buf_sz);
+   if (buf == NULL) {
+     return ERROR_OUTOFMEMORY;
+   }
+@@ -376,7 +376,7 @@ static int fs__wide_to_wtf8(WCHAR* w_source_ptr,
+     return 0;
+ 
+   if (*target_ptr == NULL) {
+-    target = uv__malloc(target_len + 1);
++    target = (char *)uv__malloc(target_len + 1);
+     if (target == NULL) {
+       SetLastError(ERROR_OUTOFMEMORY);
+       return -1;
+@@ -1575,7 +1575,7 @@ void fs__scandir(uv_fs_t* req) {
+       if (dirents_used >= dirents_size) {
+         size_t new_dirents_size =
+             dirents_size == 0 ? dirents_initial_size : dirents_size << 1;
+-        uv__dirent_t** new_dirents =
++        uv__dirent_t** new_dirents = (uv__dirent_t**)
+             uv__realloc(dirents, new_dirents_size * sizeof *dirents);
+ 
+         if (new_dirents == NULL)
+@@ -1589,7 +1589,7 @@ void fs__scandir(uv_fs_t* req) {
+        * includes room for the first character of the filename, but `utf8_len`
+        * doesn't count the NULL terminator at this point.
+        */
+-      dirent = uv__malloc(sizeof *dirent + wtf8_len);
++      dirent = (uv__dirent_t*)uv__malloc(sizeof *dirent + wtf8_len);
+       if (dirent == NULL)
+         goto out_of_memory_error;
+ 
+@@ -1691,7 +1691,7 @@ void fs__opendir(uv_fs_t* req) {
+     goto error;
+   }
+ 
+-  dir = uv__malloc(sizeof(*dir));
++  dir = (uv_dir_t*)uv__malloc(sizeof(*dir));
+   if (dir == NULL) {
+     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+     goto error;
+@@ -1706,7 +1706,7 @@ void fs__opendir(uv_fs_t* req) {
+   else
+     fmt = L"%s\\*";
+ 
+-  find_path = uv__malloc(sizeof(WCHAR) * (len + 4));
++  find_path = (WCHAR*)uv__malloc(sizeof(WCHAR) * (len + 4));
+   if (find_path == NULL) {
+     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+     goto error;
+@@ -1743,7 +1743,7 @@ void fs__readdir(uv_fs_t* req) {
+   int r;
+ 
+   req->flags |= UV_FS_FREE_PTR;
+-  dir = req->ptr;
++  dir = (uv_dir_t*)req->ptr;
+   dirents = dir->dirents;
+   memset(dirents, 0, dir->nentries * sizeof(*dir->dirents));
+   find_data = &dir->find_data;
+@@ -1800,7 +1800,7 @@ error:
+ void fs__closedir(uv_fs_t* req) {
+   uv_dir_t* dir;
+ 
+-  dir = req->ptr;
++  dir = (uv_dir_t*)req->ptr;
+   FindClose(dir->dir_handle);
+   uv__free(req->ptr);
+   SET_REQ_RESULT(req, 0);
+@@ -2791,7 +2791,7 @@ static ssize_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) {
+     return -1;
+   }
+ 
+-  w_realpath_buf = uv__malloc((w_realpath_len + 1) * sizeof(WCHAR));
++  w_realpath_buf = (WCHAR*)uv__malloc((w_realpath_len + 1) * sizeof(WCHAR));
+   if (w_realpath_buf == NULL) {
+     SetLastError(ERROR_OUTOFMEMORY);
+     return -1;
+@@ -2904,7 +2904,7 @@ retry_get_disk_free_space:
+     }
+ 
+     len = MAX_PATH + 1;
+-    pathw = uv__malloc(len * sizeof(*pathw));
++    pathw = (WCHAR*)uv__malloc(len * sizeof(*pathw));
+     if (pathw == NULL) {
+       SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+       return;
+@@ -2920,7 +2920,7 @@ retry_get_full_path_name:
+       return;
+     } else if (ret > len) {
+       len = ret;
+-      pathw = uv__reallocf(pathw, len * sizeof(*pathw));
++      pathw = (WCHAR*)uv__reallocf(pathw, len * sizeof(*pathw));
+       if (pathw == NULL) {
+         SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+         return;
+@@ -2936,7 +2936,7 @@ retry_get_full_path_name:
+     uv__free(pathw);
+   }
+ 
+-  stat_fs = uv__malloc(sizeof(*stat_fs));
++  stat_fs = (uv_statfs_t*)uv__malloc(sizeof(*stat_fs));
+   if (stat_fs == NULL) {
+     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+     return;
+@@ -3095,7 +3095,7 @@ int uv_fs_read(uv_loop_t* loop,
+   req->fs.info.nbufs = nbufs;
+   req->fs.info.bufs = req->fs.info.bufsml;
+   if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
+-    req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
++    req->fs.info.bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
+ 
+   if (req->fs.info.bufs == NULL) {
+     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+@@ -3128,7 +3128,7 @@ int uv_fs_write(uv_loop_t* loop,
+   req->fs.info.nbufs = nbufs;
+   req->fs.info.bufs = req->fs.info.bufsml;
+   if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
+-    req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
++    req->fs.info.bufs = (uv_buf_t*)uv__malloc(nbufs * sizeof(*bufs));
+ 
+   if (req->fs.info.bufs == NULL) {
+     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+diff --git a/src/win/pipe.c b/src/win/pipe.c
+index f0cac3822564e14052feb5e1934f54c57c78160d..c1739efe82b8755999145860b4da6b50c73518a2 100644
+--- a/src/win/pipe.c
++++ b/src/win/pipe.c
+@@ -756,7 +756,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
+ 
+   /* Convert name to UTF16. */
+   nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
+-  handle->name = uv__malloc(nameSize);
++  handle->name = (WCHAR*)uv__malloc(nameSize);
+   if (!handle->name) {
+     uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+   }
+@@ -906,7 +906,7 @@ int uv_pipe_connect2(uv_connect_t* req,
+ 
+   /* Convert name to UTF16. */
+   nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
+-  handle->name = uv__malloc(nameSize);
++  handle->name = (WCHAR*)uv__malloc(nameSize);
+   if (!handle->name) {
+     uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+   }
+@@ -924,7 +924,7 @@ int uv_pipe_connect2(uv_connect_t* req,
+   pipeHandle = open_named_pipe(handle->name, &duplex_flags);
+   if (pipeHandle == INVALID_HANDLE_VALUE) {
+     if (GetLastError() == ERROR_PIPE_BUSY) {
+-      req->u.connect.name = uv__malloc(nameSize);
++      req->u.connect.name = (WCHAR *)uv__malloc(nameSize);
+       if (!req->u.connect.name) {
+         uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+       }
+@@ -1528,7 +1528,7 @@ static int uv__build_coalesced_write_req(uv_write_t* user_req,
+                        data_length;                  /* (c) */
+ 
+   /* Allocate buffer. */
+-  heap_buffer = uv__malloc(heap_buffer_length);
++  heap_buffer = (char*)uv__malloc(heap_buffer_length);
+   if (heap_buffer == NULL)
+     return ERROR_NOT_ENOUGH_MEMORY; /* Maps to UV_ENOMEM. */
+ 
+@@ -1777,7 +1777,7 @@ int uv__pipe_write_ipc(uv_loop_t* loop,
+     bufs = stack_bufs;
+   } else {
+     /* Use heap-allocated buffer array. */
+-    bufs = uv__calloc(buf_count, sizeof(uv_buf_t));
++    bufs = (uv_buf_t*)uv__calloc(buf_count, sizeof(uv_buf_t));
+     if (bufs == NULL)
+       return ERROR_NOT_ENOUGH_MEMORY; /* Maps to UV_ENOMEM. */
+   }
+@@ -2514,7 +2514,7 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size)
+                                       FileNameInformation);
+   if (nt_status == STATUS_BUFFER_OVERFLOW) {
+     name_size = sizeof(*name_info) + tmp_name_info.FileNameLength;
+-    name_info = uv__malloc(name_size);
++    name_info = (FILE_NAME_INFORMATION*)uv__malloc(name_size);
+     if (!name_info) {
+       *size = 0;
+       err = UV_ENOMEM;
+diff --git a/src/win/process.c b/src/win/process.c
+index ed44adc67c6d52785a199206d9ba0357e2d0b045..b383e8b405db56d413985b38df216d09c58ec4a0 100644
+--- a/src/win/process.c
++++ b/src/win/process.c
+@@ -615,8 +615,8 @@ error:
+ 
+ 
+ int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
+-  wchar_t* a_eq;
+-  wchar_t* b_eq;
++  const wchar_t* a_eq;
++  const wchar_t* b_eq;
+   wchar_t* A;
+   wchar_t* B;
+   int nb;
+@@ -633,8 +633,8 @@ int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
+   assert(b_eq);
+   nb = b_eq - b;
+ 
+-  A = alloca((na+1) * sizeof(wchar_t));
+-  B = alloca((nb+1) * sizeof(wchar_t));
++  A = (wchar_t*)alloca((na+1) * sizeof(wchar_t));
++  B = (wchar_t*)alloca((nb+1) * sizeof(wchar_t));
+ 
+   r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na);
+   assert(r==na);
+@@ -717,7 +717,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
+   if (dst_copy == NULL && env_len > 0) {
+     return ERROR_OUTOFMEMORY;
+   }
+-  env_copy = alloca(env_block_count * sizeof(WCHAR*));
++  env_copy = (WCHAR**)alloca(env_block_count * sizeof(WCHAR*));
+ 
+   ptr = dst_copy;
+   ptr_copy = env_copy;
+@@ -771,7 +771,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
+   }
+ 
+   /* final pass: copy, in sort order, and inserting required variables */
+-  dst = uv__malloc((1+env_len) * sizeof(WCHAR));
++  dst = (WCHAR*)uv__malloc((1+env_len) * sizeof(WCHAR));
+   if (!dst) {
+     uv__free(dst_copy);
+     return ERROR_OUTOFMEMORY;
+diff --git a/src/win/tcp.c b/src/win/tcp.c
+index 187f36e2a61c870b0d16e17e9d4a9e1161ba8851..d8da4d941a51b0625fc0c072342ec4edf74c0ea3 100644
+--- a/src/win/tcp.c
++++ b/src/win/tcp.c
+@@ -585,7 +585,7 @@ int uv__tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
+ 
+   if (handle->tcp.serv.accept_reqs == NULL) {
+     handle->tcp.serv.accept_reqs =
+-      uv__malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t));
++      (uv_tcp_accept_t*)uv__malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t));
+     if (!handle->tcp.serv.accept_reqs) {
+       uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+     }
+diff --git a/src/win/thread.c b/src/win/thread.c
+index 57c25e8f5a861c9d8a4c402c260d3ac235200423..57f1698f595e2410a51044f7f228b5a235206819 100644
+--- a/src/win/thread.c
++++ b/src/win/thread.c
+@@ -98,7 +98,7 @@ static UINT __stdcall uv__thread_start(void* arg) {
+   struct thread_ctx *ctx_p;
+   struct thread_ctx ctx;
+ 
+-  ctx_p = arg;
++  ctx_p = (struct thread_ctx*)arg;
+   ctx = *ctx_p;
+   uv__free(ctx_p);
+ 
+@@ -141,7 +141,7 @@ int uv_thread_create_ex(uv_thread_t* tid,
+       return UV_EINVAL;
+   }
+ 
+-  ctx = uv__malloc(sizeof(*ctx));
++  ctx = (struct thread_ctx*)uv__malloc(sizeof(*ctx));
+   if (ctx == NULL)
+     return UV_ENOMEM;
+ 
+diff --git a/src/win/util.c b/src/win/util.c
+index f6ec79cd57b5010ed5fd6829d952bcdacc8b7671..1cfd7b2caf0d4ad1a6a66df9406c21f4e2b69b04 100644
+--- a/src/win/util.c
++++ b/src/win/util.c
+@@ -160,7 +160,7 @@ static int uv__cwd(WCHAR** buf, DWORD *len) {
+       return uv_translate_sys_error(GetLastError());
+ 
+     /* |t| is the size of the buffer _including_ nul. */
+-    p = uv__malloc(t * sizeof(*p));
++    p = (WCHAR *)uv__malloc(t * sizeof(*p));
+     if (p == NULL)
+       return UV_ENOMEM;
+ 
+@@ -265,7 +265,7 @@ int uv_chdir(const char* dir) {
+   if (utf16_len == 0) {
+     return uv_translate_sys_error(GetLastError());
+   }
+-  utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR));
++  utf16_buffer = (WCHAR*)uv__malloc(utf16_len * sizeof(WCHAR));
+   if (utf16_buffer == NULL) {
+     return UV_ENOMEM;
+   }
+@@ -623,14 +623,14 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
+   GetSystemInfo(&system_info);
+   cpu_count = system_info.dwNumberOfProcessors;
+ 
+-  cpu_infos = uv__calloc(cpu_count, sizeof *cpu_infos);
++  cpu_infos = (uv_cpu_info_t*)uv__calloc(cpu_count, sizeof *cpu_infos);
+   if (cpu_infos == NULL) {
+     err = ERROR_OUTOFMEMORY;
+     goto error;
+   }
+ 
+   sppi_size = cpu_count * sizeof(*sppi);
+-  sppi = uv__malloc(sppi_size);
++  sppi = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*)uv__malloc(sppi_size);
+   if (sppi == NULL) {
+     err = ERROR_OUTOFMEMORY;
+     goto error;
+@@ -774,7 +774,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
+       case ERROR_BUFFER_OVERFLOW:
+         /* This happens when win_address_buf is NULL or too small to hold all
+          * adapters. */
+-        win_address_buf = uv__malloc(win_address_buf_size);
++        win_address_buf =
++            (IP_ADAPTER_ADDRESSES*)uv__malloc(win_address_buf_size);
+         if (win_address_buf == NULL)
+           return UV_ENOMEM;
+ 
+@@ -782,7 +783,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
+ 
+       case ERROR_NO_DATA: {
+         /* No adapters were found. */
+-        uv_address_buf = uv__malloc(1);
++        uv_address_buf = (uv_interface_address_t*)uv__malloc(1);
+         if (uv_address_buf == NULL)
+           return UV_ENOMEM;
+ 
+@@ -859,7 +860,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
+   }
+ 
+   /* Allocate space to store interface data plus adapter names. */
+-  uv_address_buf = uv__malloc(uv_address_buf_size);
++  uv_address_buf = (uv_interface_address_t*)uv__malloc(uv_address_buf_size);
+   if (uv_address_buf == NULL) {
+     uv__free(win_address_buf);
+     return UV_ENOMEM;
+@@ -1074,7 +1075,7 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
+   }
+   /* Include space for terminating null char. */
+   len += 1;
+-  path = uv__malloc(len * sizeof(wchar_t));
++  path = (wchar_t*)uv__malloc(len * sizeof(wchar_t));
+   if (path == NULL) {
+     return UV_ENOMEM;
+   }
+@@ -1153,7 +1154,7 @@ int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8) {
+   /* Allocate the destination buffer adding an extra byte for the terminating
+    * NULL. If utf16len is not -1 WideCharToMultiByte will not add it, so
+    * we do it ourselves always, just in case. */
+-  *utf8 = uv__malloc(bufsize + 1);
++  *utf8 = (char*)uv__malloc(bufsize + 1);
+ 
+   if (*utf8 == NULL)
+     return UV_ENOMEM;
+@@ -1201,7 +1202,7 @@ int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) {
+   /* Allocate the destination buffer adding an extra byte for the terminating
+    * NULL. If utf8len is not -1 MultiByteToWideChar will not add it, so
+    * we do it ourselves always, just in case. */
+-  *utf16 = uv__malloc(sizeof(WCHAR) * (bufsize + 1));
++  *utf16 = (WCHAR*)uv__malloc(sizeof(WCHAR) * (bufsize + 1));
+ 
+   if (*utf16 == NULL)
+     return UV_ENOMEM;
+@@ -1242,7 +1243,7 @@ static int uv__getpwuid_r(uv_passwd_t* pwd) {
+     return uv_translate_sys_error(r);
+   }
+ 
+-  path = uv__malloc(bufsize * sizeof(wchar_t));
++  path = (wchar_t*)uv__malloc(bufsize * sizeof(wchar_t));
+   if (path == NULL) {
+     CloseHandle(token);
+     return UV_ENOMEM;
+@@ -1323,7 +1324,7 @@ int uv_os_environ(uv_env_item_t** envitems, int* count) {
+ 
+   for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++);
+ 
+-  *envitems = uv__calloc(i, sizeof(**envitems));
++  *envitems = (uv_env_item_t*)uv__calloc(i, sizeof(**envitems));
+   if (*envitems == NULL) {
+     FreeEnvironmentStringsW(env);
+     return UV_ENOMEM;
+@@ -1413,7 +1414,7 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
+       uv__free(var);
+ 
+     varlen = 1 + len;
+-    var = uv__malloc(varlen * sizeof(*var));
++    var = (wchar_t*)uv__malloc(varlen * sizeof(*var));
+ 
+     if (var == NULL) {
+       r = UV_ENOMEM;
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0002-Fix-warnings.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0002-Fix-warnings.patch
deleted file mode 100644
index fedaea0..0000000
--- a/third_party/allwpilib/upstream_utils/libuv_patches/0002-Fix-warnings.patch
+++ /dev/null
@@ -1,365 +0,0 @@
-From f7b4492d37b35a64a3a6c5bbbdb375494095b6ff Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Tue, 26 Apr 2022 15:09:43 -0400
-Subject: [PATCH 2/9] Fix warnings
-
----
- include/uv/win.h    |   5 +++
- src/idna.c          |   2 +-
- src/inet.c          |   4 ++
- src/threadpool.c    |   4 ++
- src/unix/core.c     |  12 ++++-
- src/unix/darwin.c   | 106 +++++++++++++++++++++++---------------------
- src/unix/internal.h |   4 +-
- src/unix/thread.c   |   6 ---
- src/uv-common.c     |   8 ++++
- src/win/fs-event.c  |   2 +
- src/win/fs.c        |   2 +
- src/win/pipe.c      |   2 +
- src/win/process.c   |   2 +
- src/win/tty.c       |   2 +
- 14 files changed, 99 insertions(+), 62 deletions(-)
-
-diff --git a/include/uv/win.h b/include/uv/win.h
-index 56a4cf11..10d5e8f1 100644
---- a/include/uv/win.h
-+++ b/include/uv/win.h
-@@ -201,11 +201,16 @@ typedef int (WSAAPI* LPFN_WSARECVFROM)
-              LPWSAOVERLAPPED overlapped,
-              LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
- 
-+#pragma warning(push)
-+#pragma warning(disable : 28251)
-+
- #ifndef _NTDEF_
-   typedef LONG NTSTATUS;
-   typedef NTSTATUS *PNTSTATUS;
- #endif
- 
-+#pragma warning(pop)
-+
- #ifndef RTL_CONDITION_VARIABLE_INIT
-   typedef PVOID CONDITION_VARIABLE, *PCONDITION_VARIABLE;
- #endif
-diff --git a/src/idna.c b/src/idna.c
-index 93d982ca..36a39a08 100644
---- a/src/idna.c
-+++ b/src/idna.c
-@@ -106,7 +106,7 @@ static int uv__idna_toascii_label(const char* s, const char* se,
-                                   char** d, char* de) {
-   static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789";
-   const char* ss;
--  unsigned c;
-+  unsigned c = 0;
-   unsigned h;
-   unsigned k;
-   unsigned n;
-diff --git a/src/inet.c b/src/inet.c
-index ca8b6ac8..1b190255 100644
---- a/src/inet.c
-+++ b/src/inet.c
-@@ -27,6 +27,10 @@
- #include "uv.h"
- #include "uv-common.h"
- 
-+#ifdef _WIN32
-+#pragma warning(disable : 6001)
-+#endif
-+
- #define UV__INET_ADDRSTRLEN         16
- #define UV__INET6_ADDRSTRLEN        46
- 
-diff --git a/src/threadpool.c b/src/threadpool.c
-index 1241ace1..718972c3 100644
---- a/src/threadpool.c
-+++ b/src/threadpool.c
-@@ -27,6 +27,10 @@
- 
- #include <stdlib.h>
- 
-+#ifdef _WIN32
-+#pragma warning(disable: 6001 6011)
-+#endif
-+
- #define MAX_THREADPOOL_SIZE 1024
- 
- static uv_once_t once = UV_ONCE_INIT;
-diff --git a/src/unix/core.c b/src/unix/core.c
-index 6353b0e3..223c5513 100644
---- a/src/unix/core.c
-+++ b/src/unix/core.c
-@@ -544,6 +544,16 @@ int uv__accept(int sockfd) {
-   return peerfd;
- }
- 
-+#if defined(__APPLE__)
-+#pragma GCC diagnostic push
-+#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
-+#if defined(__LP64__)
-+  extern "C" int close$NOCANCEL(int);
-+#else
-+  extern "C" int close$NOCANCEL$UNIX2003(int);
-+#endif
-+#pragma GCC diagnostic pop
-+#endif
- 
- /* close() on macos has the "interesting" quirk that it fails with EINTR
-  * without closing the file descriptor when a thread is in the cancel state.
-@@ -558,10 +568,8 @@ int uv__close_nocancel(int fd) {
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
- #if defined(__LP64__) || TARGET_OS_IPHONE
--  extern int close$NOCANCEL(int);
-   return close$NOCANCEL(fd);
- #else
--  extern int close$NOCANCEL$UNIX2003(int);
-   return close$NOCANCEL$UNIX2003(fd);
- #endif
- #pragma GCC diagnostic pop
-diff --git a/src/unix/darwin.c b/src/unix/darwin.c
-index 5fbf7342..eeb35720 100644
---- a/src/unix/darwin.c
-+++ b/src/unix/darwin.c
-@@ -253,64 +253,68 @@ static int uv__get_cpu_speed(uint64_t* speed) {
- 
- #define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)
- 
--  kr = pIOMasterPort(MACH_PORT_NULL, &mach_port);
--  assert(kr == KERN_SUCCESS);
--  CFMutableDictionaryRef classes_to_match
--      = pIOServiceMatching("IOPlatformDevice");
--  kr = pIOServiceGetMatchingServices(mach_port, classes_to_match, &it);
--  assert(kr == KERN_SUCCESS);
--  service = pIOIteratorNext(it);
--
--  CFStringRef device_type_str = S("device_type");
--  CFStringRef clock_frequency_str = S("clock-frequency");
--
--  while (service != 0) {
--    CFDataRef data;
--    data = pIORegistryEntryCreateCFProperty(service,
--                                            device_type_str,
--                                            NULL,
--                                            0);
--    if (data) {
--      const UInt8* raw = pCFDataGetBytePtr(data);
--      if (strncmp((char*)raw, "cpu", 3) == 0 ||
--          strncmp((char*)raw, "processor", 9) == 0) {
--        CFDataRef freq_ref;
--        freq_ref = pIORegistryEntryCreateCFProperty(service,
--                                                    clock_frequency_str,
--                                                    NULL,
--                                                    0);
--        if (freq_ref) {
--          const UInt8* freq_ref_ptr = pCFDataGetBytePtr(freq_ref);
--          CFIndex len = pCFDataGetLength(freq_ref);
--          if (len == 8)
--            memcpy(speed, freq_ref_ptr, 8);
--          else if (len == 4) {
--            uint32_t v;
--            memcpy(&v, freq_ref_ptr, 4);
--            *speed = v;
--          } else {
--            *speed = 0;
--          }
-+  // Braces ensure goto doesn't jump into device_type_str's and
-+  // clock_frequency_str's lifetimes after their initialization
-+  {
-+    kr = pIOMasterPort(MACH_PORT_NULL, &mach_port);
-+    assert(kr == KERN_SUCCESS);
-+    CFMutableDictionaryRef classes_to_match
-+        = pIOServiceMatching("IOPlatformDevice");
-+    kr = pIOServiceGetMatchingServices(mach_port, classes_to_match, &it);
-+    assert(kr == KERN_SUCCESS);
-+    service = pIOIteratorNext(it);
- 
--          pCFRelease(freq_ref);
--          pCFRelease(data);
--          break;
-+    CFStringRef device_type_str = S("device_type");
-+    CFStringRef clock_frequency_str = S("clock-frequency");
-+
-+    while (service != 0) {
-+      CFDataRef data;
-+      data = pIORegistryEntryCreateCFProperty(service,
-+                                              device_type_str,
-+                                              NULL,
-+                                              0);
-+      if (data) {
-+        const UInt8* raw = pCFDataGetBytePtr(data);
-+        if (strncmp((char*)raw, "cpu", 3) == 0 ||
-+            strncmp((char*)raw, "processor", 9) == 0) {
-+          CFDataRef freq_ref;
-+          freq_ref = pIORegistryEntryCreateCFProperty(service,
-+                                                      clock_frequency_str,
-+                                                      NULL,
-+                                                      0);
-+          if (freq_ref) {
-+            const UInt8* freq_ref_ptr = pCFDataGetBytePtr(freq_ref);
-+            CFIndex len = pCFDataGetLength(freq_ref);
-+            if (len == 8)
-+              memcpy(speed, freq_ref_ptr, 8);
-+            else if (len == 4) {
-+              uint32_t v;
-+              memcpy(&v, freq_ref_ptr, 4);
-+              *speed = v;
-+            } else {
-+              *speed = 0;
-+            }
-+
-+            pCFRelease(freq_ref);
-+            pCFRelease(data);
-+            break;
-+          }
-         }
-+        pCFRelease(data);
-       }
--      pCFRelease(data);
--    }
- 
--    service = pIOIteratorNext(it);
--  }
-+      service = pIOIteratorNext(it);
-+    }
- 
--  pIOObjectRelease(it);
-+    pIOObjectRelease(it);
- 
--  err = 0;
-+    err = 0;
- 
--  if (device_type_str != NULL)
--    pCFRelease(device_type_str);
--  if (clock_frequency_str != NULL)
--    pCFRelease(clock_frequency_str);
-+    if (device_type_str != NULL)
-+      pCFRelease(device_type_str);
-+    if (clock_frequency_str != NULL)
-+      pCFRelease(clock_frequency_str);
-+  }
- 
- out:
-   if (core_foundation_handle != NULL)
-diff --git a/src/unix/internal.h b/src/unix/internal.h
-index cee35c21..f9d1666d 100644
---- a/src/unix/internal.h
-+++ b/src/unix/internal.h
-@@ -312,8 +312,8 @@ UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) {
-   loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000;
- }
- 
--UV_UNUSED(static char* uv__basename_r(const char* path)) {
--  char* s;
-+UV_UNUSED(static const char* uv__basename_r(const char* path)) {
-+  const char* s;
- 
-   s = strrchr(path, '/');
-   if (s == NULL)
-diff --git a/src/unix/thread.c b/src/unix/thread.c
-index 759cd0c2..64726bd6 100644
---- a/src/unix/thread.c
-+++ b/src/unix/thread.c
-@@ -244,12 +244,6 @@ int uv_thread_create_ex(uv_thread_t* tid,
-   size_t stack_size;
-   size_t min_stack_size;
- 
--  /* Used to squelch a -Wcast-function-type warning. */
--  union {
--    void (*in)(void*);
--    void* (*out)(void*);
--  } f;
--
-   stack_size =
-       params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
- 
-diff --git a/src/uv-common.c b/src/uv-common.c
-index dfb606e3..49026c03 100644
---- a/src/uv-common.c
-+++ b/src/uv-common.c
-@@ -758,6 +758,10 @@ void uv__fs_readdir_cleanup(uv_fs_t* req) {
-   }
- }
- 
-+#ifdef __clang__
-+# pragma clang diagnostic push
-+# pragma clang diagnostic ignored "-Wvarargs"
-+#endif
- 
- int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) {
-   va_list ap;
-@@ -771,6 +775,10 @@ int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) {
-   return err;
- }
- 
-+#ifdef __clang__
-+# pragma clang diagnostic pop
-+#endif
-+
- 
- static uv_loop_t default_loop_struct;
- static uv_loop_t* default_loop_ptr;
-diff --git a/src/win/fs-event.c b/src/win/fs-event.c
-index 15046731..3244a4e4 100644
---- a/src/win/fs-event.c
-+++ b/src/win/fs-event.c
-@@ -19,6 +19,8 @@
-  * IN THE SOFTWARE.
-  */
- 
-+#define _CRT_NONSTDC_NO_WARNINGS
-+
- #include <assert.h>
- #include <errno.h>
- #include <stdio.h>
-diff --git a/src/win/fs.c b/src/win/fs.c
-index 8374012f..f71b3c04 100644
---- a/src/win/fs.c
-+++ b/src/win/fs.c
-@@ -19,6 +19,8 @@
-  * IN THE SOFTWARE.
-  */
- 
-+#define _CRT_NONSTDC_NO_WARNINGS
-+
- #include <assert.h>
- #include <stdlib.h>
- #include <direct.h>
-diff --git a/src/win/pipe.c b/src/win/pipe.c
-index cd77061a..f413a72d 100644
---- a/src/win/pipe.c
-+++ b/src/win/pipe.c
-@@ -19,6 +19,8 @@
-  * IN THE SOFTWARE.
-  */
- 
-+#define _CRT_NONSTDC_NO_WARNINGS
-+
- #include <assert.h>
- #include <io.h>
- #include <stdio.h>
-diff --git a/src/win/process.c b/src/win/process.c
-index e857db3e..a49016f6 100644
---- a/src/win/process.c
-+++ b/src/win/process.c
-@@ -19,6 +19,8 @@
-  * IN THE SOFTWARE.
-  */
- 
-+#define _CRT_NONSTDC_NO_WARNINGS
-+
- #include <assert.h>
- #include <io.h>
- #include <stdio.h>
-diff --git a/src/win/tty.c b/src/win/tty.c
-index 267ca645..d7522668 100644
---- a/src/win/tty.c
-+++ b/src/win/tty.c
-@@ -19,6 +19,8 @@
-  * IN THE SOFTWARE.
-  */
- 
-+#define _CRT_NONSTDC_NO_WARNINGS
-+
- #include <assert.h>
- #include <io.h>
- #include <string.h>
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0003-Fix-warnings.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0003-Fix-warnings.patch
new file mode 100644
index 0000000..cbf5c70
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/libuv_patches/0003-Fix-warnings.patch
@@ -0,0 +1,297 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Tue, 26 Apr 2022 15:09:43 -0400
+Subject: [PATCH 03/10] Fix warnings
+
+---
+ include/uv/win.h    |  5 +++++
+ src/idna.c          |  2 +-
+ src/inet.c          |  4 ++++
+ src/threadpool.c    |  4 ++++
+ src/unix/core.c     | 12 ++++++++++--
+ src/unix/internal.h |  4 ++--
+ src/unix/linux.c    | 15 ++++++++++++---
+ src/unix/thread.c   |  6 ------
+ src/uv-common.c     |  8 ++++++++
+ src/win/fs-event.c  |  2 ++
+ src/win/fs.c        |  2 ++
+ src/win/pipe.c      |  2 ++
+ src/win/process.c   |  2 ++
+ src/win/thread.c    |  4 ++--
+ src/win/tty.c       |  2 ++
+ 15 files changed, 58 insertions(+), 16 deletions(-)
+
+diff --git a/include/uv/win.h b/include/uv/win.h
+index eb74776978340a4910194bae35a9da6493e8c0a6..6d0afe69e7dd4caf4c9459e548fe75cf0c51b501 100644
+--- a/include/uv/win.h
++++ b/include/uv/win.h
+@@ -201,11 +201,16 @@ typedef int (WSAAPI* LPFN_WSARECVFROM)
+              LPWSAOVERLAPPED overlapped,
+              LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
+ 
++#pragma warning(push)
++#pragma warning(disable : 28251)
++
+ #ifndef _NTDEF_
+   typedef LONG NTSTATUS;
+   typedef NTSTATUS *PNTSTATUS;
+ #endif
+ 
++#pragma warning(pop)
++
+ #ifndef RTL_CONDITION_VARIABLE_INIT
+   typedef PVOID CONDITION_VARIABLE, *PCONDITION_VARIABLE;
+ #endif
+diff --git a/src/idna.c b/src/idna.c
+index 93d982ca018f2d39d9c0ffab07998c2c637029eb..36a39a089019fb4c2a35ec84516658c392eec0a3 100644
+--- a/src/idna.c
++++ b/src/idna.c
+@@ -106,7 +106,7 @@ static int uv__idna_toascii_label(const char* s, const char* se,
+                                   char** d, char* de) {
+   static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+   const char* ss;
+-  unsigned c;
++  unsigned c = 0;
+   unsigned h;
+   unsigned k;
+   unsigned n;
+diff --git a/src/inet.c b/src/inet.c
+index dd94bea3886ca37945fcad7909d765e3700e3c21..71c9e5b774d64d505e6c6d6ed2637178b8532d4d 100644
+--- a/src/inet.c
++++ b/src/inet.c
+@@ -22,6 +22,10 @@
+ #include "uv.h"
+ #include "uv-common.h"
+ 
++#ifdef _WIN32
++#pragma warning(disable : 6001)
++#endif
++
+ #define UV__INET_ADDRSTRLEN         16
+ #define UV__INET6_ADDRSTRLEN        46
+ 
+diff --git a/src/threadpool.c b/src/threadpool.c
+index f572de5aaf1a1b150e58c7b989949441cac279c4..aa282af468935b680140295a175e503ca82d8fa4 100644
+--- a/src/threadpool.c
++++ b/src/threadpool.c
+@@ -27,6 +27,10 @@
+ 
+ #include <stdlib.h>
+ 
++#ifdef _WIN32
++#pragma warning(disable: 6001 6011)
++#endif
++
+ #define MAX_THREADPOOL_SIZE 1024
+ 
+ static uv_once_t once = UV_ONCE_INIT;
+diff --git a/src/unix/core.c b/src/unix/core.c
+index 28c036f94f3e76717afa651451969f128c5a573c..268fc9652f437eb0d0cda2a9e0b06b9e91eb9742 100644
+--- a/src/unix/core.c
++++ b/src/unix/core.c
+@@ -575,6 +575,16 @@ int uv__accept(int sockfd) {
+   return peerfd;
+ }
+ 
++#if defined(__APPLE__)
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
++#if defined(__LP64__)
++  extern "C" int close$NOCANCEL(int);
++#else
++  extern "C" int close$NOCANCEL$UNIX2003(int);
++#endif
++#pragma GCC diagnostic pop
++#endif
+ 
+ /* close() on macos has the "interesting" quirk that it fails with EINTR
+  * without closing the file descriptor when a thread is in the cancel state.
+@@ -589,10 +599,8 @@ int uv__close_nocancel(int fd) {
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
+ #if defined(__LP64__) || TARGET_OS_IPHONE
+-  extern int close$NOCANCEL(int);
+   return close$NOCANCEL(fd);
+ #else
+-  extern int close$NOCANCEL$UNIX2003(int);
+   return close$NOCANCEL$UNIX2003(fd);
+ #endif
+ #pragma GCC diagnostic pop
+diff --git a/src/unix/internal.h b/src/unix/internal.h
+index fe5885136039d5332623467b86bf52cd4b32ca0f..98c437dcadec5b5106d697e82d5394d459f55e47 100644
+--- a/src/unix/internal.h
++++ b/src/unix/internal.h
+@@ -384,8 +384,8 @@ UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) {
+   loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000;
+ }
+ 
+-UV_UNUSED(static char* uv__basename_r(const char* path)) {
+-  char* s;
++UV_UNUSED(static const char* uv__basename_r(const char* path)) {
++  const char* s;
+ 
+   s = strrchr(path, '/');
+   if (s == NULL)
+diff --git a/src/unix/linux.c b/src/unix/linux.c
+index 9173850bd158eaf9c41deca38f9ba84762a027a1..157443792f1216c83b4221c3810d17c81c5913c4 100644
+--- a/src/unix/linux.c
++++ b/src/unix/linux.c
+@@ -1718,7 +1718,11 @@ int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
+     return UV__ERR(errno);
+   }
+ 
+-  fgets(buf, sizeof(buf), fp);  /* Skip first line. */
++  /* Skip first line. */
++  if (!fgets(buf, sizeof(buf), fp)) {
++    uv__free(cpus);
++    return UV__ERR(errno);
++  }
+ 
+   for (;;) {
+     memset(&t, 0, sizeof(t));
+@@ -1729,7 +1733,10 @@ int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
+     if (n != 7)
+       break;
+ 
+-    fgets(buf, sizeof(buf), fp);  /* Skip rest of line. */
++    /* Skip rest of line. */
++    if (!fgets(buf, sizeof(buf), fp)) {
++      break;
++    }
+ 
+     if (cpu >= ARRAY_SIZE(*cpus))
+       continue;
+@@ -1809,7 +1816,9 @@ nocpuinfo:
+     if (fp == NULL)
+       continue;
+ 
+-    fscanf(fp, "%llu", &(*cpus)[cpu].freq);
++    if (0 > fscanf(fp, "%llu", &(*cpus)[cpu].freq)) {
++      (*cpus)[cpu].freq = 0llu;
++    }
+     fclose(fp);
+     fp = NULL;
+   }
+diff --git a/src/unix/thread.c b/src/unix/thread.c
+index 531c6211bb4321e5f11031a0644b4e3ab9174396..f8600947e3e7df015c4302af4feee740707b2c46 100644
+--- a/src/unix/thread.c
++++ b/src/unix/thread.c
+@@ -137,12 +137,6 @@ int uv_thread_create_ex(uv_thread_t* tid,
+   size_t stack_size;
+   size_t min_stack_size;
+ 
+-  /* Used to squelch a -Wcast-function-type warning. */
+-  union {
+-    void (*in)(void*);
+-    void* (*out)(void*);
+-  } f;
+-
+   stack_size =
+       params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
+ 
+diff --git a/src/uv-common.c b/src/uv-common.c
+index c04f93596ab1f730576256d86e216ccb7f258b72..cd10b36b4a393e325ea03b93eb9897193ca9800b 100644
+--- a/src/uv-common.c
++++ b/src/uv-common.c
+@@ -799,6 +799,10 @@ void uv__fs_readdir_cleanup(uv_fs_t* req) {
+   }
+ }
+ 
++#ifdef __clang__
++# pragma clang diagnostic push
++# pragma clang diagnostic ignored "-Wvarargs"
++#endif
+ 
+ int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) {
+   va_list ap;
+@@ -812,6 +816,10 @@ int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) {
+   return err;
+ }
+ 
++#ifdef __clang__
++# pragma clang diagnostic pop
++#endif
++
+ 
+ static uv_loop_t default_loop_struct;
+ static uv_loop_t* default_loop_ptr;
+diff --git a/src/win/fs-event.c b/src/win/fs-event.c
+index 150467313d576bfe2966b55f3d8cffa23cbb8ea3..3244a4e4320d7ce98f226b49b2634c65de89c213 100644
+--- a/src/win/fs-event.c
++++ b/src/win/fs-event.c
+@@ -19,6 +19,8 @@
+  * IN THE SOFTWARE.
+  */
+ 
++#define _CRT_NONSTDC_NO_WARNINGS
++
+ #include <assert.h>
+ #include <errno.h>
+ #include <stdio.h>
+diff --git a/src/win/fs.c b/src/win/fs.c
+index 328c8f2e0513562b53c948ffea59d0841e14b264..565c05fff13c2e6e74091c1da7b31636d7fd370d 100644
+--- a/src/win/fs.c
++++ b/src/win/fs.c
+@@ -19,6 +19,8 @@
+  * IN THE SOFTWARE.
+  */
+ 
++#define _CRT_NONSTDC_NO_WARNINGS
++
+ #include <assert.h>
+ #include <stdlib.h>
+ #include <direct.h>
+diff --git a/src/win/pipe.c b/src/win/pipe.c
+index c1739efe82b8755999145860b4da6b50c73518a2..258d6a684c67f154096a25e7226f1a7d08b93d5b 100644
+--- a/src/win/pipe.c
++++ b/src/win/pipe.c
+@@ -19,6 +19,8 @@
+  * IN THE SOFTWARE.
+  */
+ 
++#define _CRT_NONSTDC_NO_WARNINGS
++
+ #include <assert.h>
+ #include <io.h>
+ #include <stdio.h>
+diff --git a/src/win/process.c b/src/win/process.c
+index b383e8b405db56d413985b38df216d09c58ec4a0..2b1b46259959867482079962d0ea44246a42e7cb 100644
+--- a/src/win/process.c
++++ b/src/win/process.c
+@@ -19,6 +19,8 @@
+  * IN THE SOFTWARE.
+  */
+ 
++#define _CRT_NONSTDC_NO_WARNINGS
++
+ #include <assert.h>
+ #include <io.h>
+ #include <stdio.h>
+diff --git a/src/win/thread.c b/src/win/thread.c
+index 57f1698f595e2410a51044f7f228b5a235206819..03b33e9b4de6fe2532095d717a8639e8df454cce 100644
+--- a/src/win/thread.c
++++ b/src/win/thread.c
+@@ -204,8 +204,8 @@ int uv_thread_setaffinity(uv_thread_t* tid,
+   threadmask = 0;
+   for (i = 0; i < cpumasksize; i++) {
+     if (cpumask[i]) {
+-      if (procmask & (1 << i))
+-        threadmask |= 1 << i;
++      if (procmask & (1LL << i))
++        threadmask |= 1LL << i;
+       else
+         return UV_EINVAL;
+     }
+diff --git a/src/win/tty.c b/src/win/tty.c
+index 7e1f15544b177382a774300f832bc982d85bd62b..abbe1315883257d6825b794344dcd4cba9514097 100644
+--- a/src/win/tty.c
++++ b/src/win/tty.c
+@@ -19,6 +19,8 @@
+  * IN THE SOFTWARE.
+  */
+ 
++#define _CRT_NONSTDC_NO_WARNINGS
++
+ #include <assert.h>
+ #include <io.h>
+ #include <string.h>
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0003-Preprocessor-cleanup.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0003-Preprocessor-cleanup.patch
deleted file mode 100644
index f55f5cc..0000000
--- a/third_party/allwpilib/upstream_utils/libuv_patches/0003-Preprocessor-cleanup.patch
+++ /dev/null
@@ -1,179 +0,0 @@
-From dec4f95751a103f132e9674adf184188ec69176f Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Tue, 26 Apr 2022 15:19:14 -0400
-Subject: [PATCH 3/9] Preprocessor cleanup
-
----
- include/uv.h        | 18 +-----------------
- include/uv/unix.h   |  8 --------
- include/uv/win.h    |  6 +-----
- src/unix/internal.h |  2 ++
- src/win/fs.c        |  1 +
- src/win/tty.c       |  2 ++
- src/win/util.c      |  8 ++++++++
- src/win/winsock.c   |  1 +
- 8 files changed, 16 insertions(+), 30 deletions(-)
-
-diff --git a/include/uv.h b/include/uv.h
-index ee1c94cc..dbaeb1e9 100644
---- a/include/uv.h
-+++ b/include/uv.h
-@@ -23,9 +23,6 @@
- 
- #ifndef UV_H
- #define UV_H
--#ifdef __cplusplus
--extern "C" {
--#endif
- 
- #if defined(BUILDING_UV_SHARED) && defined(USING_UV_SHARED)
- #error "Define either BUILDING_UV_SHARED or USING_UV_SHARED, not both."
-@@ -56,11 +53,7 @@ extern "C" {
- #include <stddef.h>
- #include <stdio.h>
- 
--#if defined(_MSC_VER) && _MSC_VER < 1600
--# include "uv/stdint-msvc2008.h"
--#else
--# include <stdint.h>
--#endif
-+#include <stdint.h>
- 
- #if defined(_WIN32)
- # include "uv/win.h"
-@@ -767,16 +760,10 @@ UV_EXTERN int uv_tty_get_winsize(uv_tty_t*, int* width, int* height);
- UV_EXTERN void uv_tty_set_vterm_state(uv_tty_vtermstate_t state);
- UV_EXTERN int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state);
- 
--#ifdef __cplusplus
--extern "C++" {
--
- inline int uv_tty_set_mode(uv_tty_t* handle, int mode) {
-   return uv_tty_set_mode(handle, static_cast<uv_tty_mode_t>(mode));
- }
- 
--}
--#endif
--
- UV_EXTERN uv_handle_type uv_guess_handle(uv_file file);
- 
- /*
-@@ -1844,7 +1831,4 @@ UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data);
- #undef UV_LOOP_PRIVATE_PLATFORM_FIELDS
- #undef UV__ERR
- 
--#ifdef __cplusplus
--}
--#endif
- #endif /* UV_H */
-diff --git a/include/uv/unix.h b/include/uv/unix.h
-index 420be86c..256fef37 100644
---- a/include/uv/unix.h
-+++ b/include/uv/unix.h
-@@ -47,14 +47,6 @@
- 
- #if defined(__linux__)
- # include "uv/linux.h"
--#elif defined (__MVS__)
--# include "uv/os390.h"
--#elif defined(__PASE__)  /* __PASE__ and _AIX are both defined on IBM i */
--# include "uv/posix.h"  /* IBM i needs uv/posix.h, not uv/aix.h */
--#elif defined(_AIX)
--# include "uv/aix.h"
--#elif defined(__sun)
--# include "uv/sunos.h"
- #elif defined(__APPLE__)
- # include "uv/darwin.h"
- #elif defined(__DragonFly__)       || \
-diff --git a/include/uv/win.h b/include/uv/win.h
-index 10d5e8f1..0a33366f 100644
---- a/include/uv/win.h
-+++ b/include/uv/win.h
-@@ -60,11 +60,7 @@ typedef struct pollfd {
- #include <fcntl.h>
- #include <sys/stat.h>
- 
--#if defined(_MSC_VER) && _MSC_VER < 1600
--# include "uv/stdint-msvc2008.h"
--#else
--# include <stdint.h>
--#endif
-+#include <stdint.h>
- 
- #include "uv/tree.h"
- #include "uv/threadpool.h"
-diff --git a/src/unix/internal.h b/src/unix/internal.h
-index f9d1666d..2b654157 100644
---- a/src/unix/internal.h
-+++ b/src/unix/internal.h
-@@ -192,6 +192,8 @@ struct uv__stream_queued_fds_s {
- #if defined(__linux__) && O_NDELAY != O_NONBLOCK
- #undef uv__nonblock
- #define uv__nonblock uv__nonblock_fcntl
-+#undef UV__NONBLOCK_IS_IOCTL
-+#define UV__NONBLOCK_IS_FCNTL
- #endif
- 
- /* core */
-diff --git a/src/win/fs.c b/src/win/fs.c
-index f71b3c04..71c9b169 100644
---- a/src/win/fs.c
-+++ b/src/win/fs.c
-@@ -38,6 +38,7 @@
- #include "handle-inl.h"
- #include "fs-fd-hash-inl.h"
- 
-+#pragma comment(lib, "Advapi32.lib")
- 
- #define UV_FS_FREE_PATHS         0x0002
- #define UV_FS_FREE_PTR           0x0008
-diff --git a/src/win/tty.c b/src/win/tty.c
-index d7522668..9753784d 100644
---- a/src/win/tty.c
-+++ b/src/win/tty.c
-@@ -42,6 +42,8 @@
- #include "stream-inl.h"
- #include "req-inl.h"
- 
-+#pragma comment(lib, "User32.lib")
-+
- #ifndef InterlockedOr
- # define InterlockedOr _InterlockedOr
- #endif
-diff --git a/src/win/util.c b/src/win/util.c
-index c655f532..7a5dd2ef 100644
---- a/src/win/util.c
-+++ b/src/win/util.c
-@@ -63,12 +63,20 @@
- 
- 
- /* A RtlGenRandom() by any other name... */
-+extern "C" {
- extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength);
-+}
- 
- /* Cached copy of the process title, plus a mutex guarding it. */
- static char *process_title;
- static CRITICAL_SECTION process_title_lock;
- 
-+#pragma comment(lib, "Advapi32.lib")
-+#pragma comment(lib, "IPHLPAPI.lib")
-+#pragma comment(lib, "Psapi.lib")
-+#pragma comment(lib, "Userenv.lib")
-+#pragma comment(lib, "kernel32.lib")
-+
- /* Frequency of the high-resolution clock. */
- static uint64_t hrtime_frequency_ = 0;
- 
-diff --git a/src/win/winsock.c b/src/win/winsock.c
-index a68b0953..7843e9f1 100644
---- a/src/win/winsock.c
-+++ b/src/win/winsock.c
-@@ -25,6 +25,7 @@
- #include "uv.h"
- #include "internal.h"
- 
-+#pragma comment(lib, "Ws2_32.lib")
- 
- /* Whether there are any non-IFS LSPs stacked on TCP */
- int uv_tcp_non_ifs_lsp_ipv4;
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0004-Cleanup-problematic-language.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0004-Cleanup-problematic-language.patch
deleted file mode 100644
index a29043b..0000000
--- a/third_party/allwpilib/upstream_utils/libuv_patches/0004-Cleanup-problematic-language.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-From 2d06f216dec3abbeaaabb465b945e09856d1b687 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Tue, 26 Apr 2022 15:24:47 -0400
-Subject: [PATCH 4/9] Cleanup problematic language
-
----
- src/unix/tty.c | 21 +++++++++++----------
- 1 file changed, 11 insertions(+), 10 deletions(-)
-
-diff --git a/src/unix/tty.c b/src/unix/tty.c
-index b4150525..ed81e26a 100644
---- a/src/unix/tty.c
-+++ b/src/unix/tty.c
-@@ -79,7 +79,7 @@ int uv__tcsetattr(int fd, int how, const struct termios *term) {
-   return 0;
- }
- 
--static int uv__tty_is_slave(const int fd) {
-+static int uv__tty_is_peripheral(const int fd) {
-   int result;
- #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-   int dummy;
-@@ -91,15 +91,16 @@ static int uv__tty_is_slave(const int fd) {
-   result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0;
- #elif defined(__NetBSD__)
-   /*
--   * NetBSD as an extension returns with ptsname(3) and ptsname_r(3) the slave
--   * device name for both descriptors, the master one and slave one.
-+   * NetBSD as an extension returns with ptsname(3) and ptsname_r(3) the
-+   * peripheral device name for both descriptors, the controller one and
-+   * peripheral one.
-    *
-    * Implement function to compare major device number with pts devices.
-    *
-    * The major numbers are machine-dependent, on NetBSD/amd64 they are
-    * respectively:
--   *  - master tty: ptc - major 6
--   *  - slave tty:  pts - major 5
-+   *  - controller tty: ptc - major 6
-+   *  - peripheral tty:  pts - major 5
-    */
- 
-   struct stat sb;
-@@ -174,12 +175,12 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int unused) {
-    * other processes.
-    */
-   if (type == UV_TTY) {
--    /* Reopening a pty in master mode won't work either because the reopened
--     * pty will be in slave mode (*BSD) or reopening will allocate a new
--     * master/slave pair (Linux). Therefore check if the fd points to a
--     * slave device.
-+    /* Reopening a pty in controller mode won't work either because the reopened
-+     * pty will be in peripheral mode (*BSD) or reopening will allocate a new
-+     * controller/peripheral pair (Linux). Therefore check if the fd points to a
-+     * peripheral device.
-      */
--    if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0)
-+    if (uv__tty_is_peripheral(fd) && ttyname_r(fd, path, sizeof(path)) == 0)
-       r = uv__open_cloexec(path, mode | O_NOCTTY);
-     else
-       r = -1;
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0004-Preprocessor-cleanup.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0004-Preprocessor-cleanup.patch
new file mode 100644
index 0000000..c95b28a
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/libuv_patches/0004-Preprocessor-cleanup.patch
@@ -0,0 +1,148 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Tue, 26 Apr 2022 15:19:14 -0400
+Subject: [PATCH 04/10] Preprocessor cleanup
+
+---
+ include/uv.h        | 12 ------------
+ include/uv/unix.h   |  8 --------
+ src/unix/internal.h |  2 ++
+ src/win/fs.c        |  1 +
+ src/win/tty.c       |  2 ++
+ src/win/util.c      |  8 ++++++++
+ src/win/winsock.c   |  1 +
+ 7 files changed, 14 insertions(+), 20 deletions(-)
+
+diff --git a/include/uv.h b/include/uv.h
+index 02397dd0fdd43d51f86c0dde9a62046702f12bdb..d5342b0d52232bbf83825948cc6bc09e5d74a4c7 100644
+--- a/include/uv.h
++++ b/include/uv.h
+@@ -23,9 +23,6 @@
+ 
+ #ifndef UV_H
+ #define UV_H
+-#ifdef __cplusplus
+-extern "C" {
+-#endif
+ 
+ #if defined(BUILDING_UV_SHARED) && defined(USING_UV_SHARED)
+ #error "Define either BUILDING_UV_SHARED or USING_UV_SHARED, not both."
+@@ -796,16 +793,10 @@ UV_EXTERN int uv_tty_get_winsize(uv_tty_t*, int* width, int* height);
+ UV_EXTERN void uv_tty_set_vterm_state(uv_tty_vtermstate_t state);
+ UV_EXTERN int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state);
+ 
+-#ifdef __cplusplus
+-extern "C++" {
+-
+ inline int uv_tty_set_mode(uv_tty_t* handle, int mode) {
+   return uv_tty_set_mode(handle, static_cast<uv_tty_mode_t>(mode));
+ }
+ 
+-}
+-#endif
+-
+ UV_EXTERN uv_handle_type uv_guess_handle(uv_file file);
+ 
+ enum {
+@@ -1906,7 +1897,4 @@ UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data);
+ #undef UV_LOOP_PRIVATE_PLATFORM_FIELDS
+ #undef UV__ERR
+ 
+-#ifdef __cplusplus
+-}
+-#endif
+ #endif /* UV_H */
+diff --git a/include/uv/unix.h b/include/uv/unix.h
+index 09f88a5674280d762c094d956e5dec6971c6a33e..e334cabe0280ef94bacbb1171049c71f17bf56ff 100644
+--- a/include/uv/unix.h
++++ b/include/uv/unix.h
+@@ -47,14 +47,6 @@
+ 
+ #if defined(__linux__)
+ # include "uv/linux.h"
+-#elif defined (__MVS__)
+-# include "uv/os390.h"
+-#elif defined(__PASE__)  /* __PASE__ and _AIX are both defined on IBM i */
+-# include "uv/posix.h"  /* IBM i needs uv/posix.h, not uv/aix.h */
+-#elif defined(_AIX)
+-# include "uv/aix.h"
+-#elif defined(__sun)
+-# include "uv/sunos.h"
+ #elif defined(__APPLE__)
+ # include "uv/darwin.h"
+ #elif defined(__DragonFly__)       || \
+diff --git a/src/unix/internal.h b/src/unix/internal.h
+index 98c437dcadec5b5106d697e82d5394d459f55e47..854d98a16a74c45e0b6cb92b17782de6803f6e28 100644
+--- a/src/unix/internal.h
++++ b/src/unix/internal.h
+@@ -233,6 +233,8 @@ struct uv__statx {
+ #if defined(__linux__) && O_NDELAY != O_NONBLOCK
+ #undef uv__nonblock
+ #define uv__nonblock uv__nonblock_fcntl
++#undef UV__NONBLOCK_IS_IOCTL
++#define UV__NONBLOCK_IS_FCNTL
+ #endif
+ 
+ /* core */
+diff --git a/src/win/fs.c b/src/win/fs.c
+index 565c05fff13c2e6e74091c1da7b31636d7fd370d..f415ddc2c39d09eea317fc857777acce1ce7d13e 100644
+--- a/src/win/fs.c
++++ b/src/win/fs.c
+@@ -40,6 +40,7 @@
+ 
+ #include <winioctl.h>
+ 
++#pragma comment(lib, "Advapi32.lib")
+ 
+ #define UV_FS_FREE_PATHS         0x0002
+ #define UV_FS_FREE_PTR           0x0008
+diff --git a/src/win/tty.c b/src/win/tty.c
+index abbe1315883257d6825b794344dcd4cba9514097..9bb3d9e830c901122da5e521e0c6b032dfd5044c 100644
+--- a/src/win/tty.c
++++ b/src/win/tty.c
+@@ -37,6 +37,8 @@
+ #include "stream-inl.h"
+ #include "req-inl.h"
+ 
++#pragma comment(lib, "User32.lib")
++
+ #ifndef InterlockedOr
+ # define InterlockedOr _InterlockedOr
+ #endif
+diff --git a/src/win/util.c b/src/win/util.c
+index 1cfd7b2caf0d4ad1a6a66df9406c21f4e2b69b04..af18cfa6c106c5de0996e4fff9b4127aaa3b576b 100644
+--- a/src/win/util.c
++++ b/src/win/util.c
+@@ -64,12 +64,20 @@
+ 
+ 
+ /* A RtlGenRandom() by any other name... */
++extern "C" {
+ extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength);
++}
+ 
+ /* Cached copy of the process title, plus a mutex guarding it. */
+ static char *process_title;
+ static CRITICAL_SECTION process_title_lock;
+ 
++#pragma comment(lib, "Advapi32.lib")
++#pragma comment(lib, "IPHLPAPI.lib")
++#pragma comment(lib, "Psapi.lib")
++#pragma comment(lib, "Userenv.lib")
++#pragma comment(lib, "kernel32.lib")
++
+ /* Frequency of the high-resolution clock. */
+ static uint64_t hrtime_frequency_ = 0;
+ 
+diff --git a/src/win/winsock.c b/src/win/winsock.c
+index a68b095366a78e78675b49a078749f45dbad6015..7843e9f13321c32419cebaacde82d30f471e11d7 100644
+--- a/src/win/winsock.c
++++ b/src/win/winsock.c
+@@ -25,6 +25,7 @@
+ #include "uv.h"
+ #include "internal.h"
+ 
++#pragma comment(lib, "Ws2_32.lib")
+ 
+ /* Whether there are any non-IFS LSPs stacked on TCP */
+ int uv_tcp_non_ifs_lsp_ipv4;
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0005-Cleanup-problematic-language.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0005-Cleanup-problematic-language.patch
new file mode 100644
index 0000000..e016c2e
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/libuv_patches/0005-Cleanup-problematic-language.patch
@@ -0,0 +1,61 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Tue, 26 Apr 2022 15:24:47 -0400
+Subject: [PATCH 05/10] Cleanup problematic language
+
+---
+ src/unix/tty.c | 21 +++++++++++----------
+ 1 file changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/src/unix/tty.c b/src/unix/tty.c
+index d099bdb3b677212d21e06ac7bb1031c8e5386499..1bd217b5a15eed13a8349c479b53471dd36ca216 100644
+--- a/src/unix/tty.c
++++ b/src/unix/tty.c
+@@ -79,7 +79,7 @@ int uv__tcsetattr(int fd, int how, const struct termios *term) {
+   return 0;
+ }
+ 
+-static int uv__tty_is_slave(const int fd) {
++static int uv__tty_is_peripheral(const int fd) {
+   int result;
+ #if defined(__linux__) || defined(__FreeBSD__)
+   int dummy;
+@@ -91,15 +91,16 @@ static int uv__tty_is_slave(const int fd) {
+   result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0;
+ #elif defined(__NetBSD__)
+   /*
+-   * NetBSD as an extension returns with ptsname(3) and ptsname_r(3) the slave
+-   * device name for both descriptors, the master one and slave one.
++   * NetBSD as an extension returns with ptsname(3) and ptsname_r(3) the
++   * peripheral device name for both descriptors, the controller one and
++   * peripheral one.
+    *
+    * Implement function to compare major device number with pts devices.
+    *
+    * The major numbers are machine-dependent, on NetBSD/amd64 they are
+    * respectively:
+-   *  - master tty: ptc - major 6
+-   *  - slave tty:  pts - major 5
++   *  - controller tty: ptc - major 6
++   *  - peripheral tty:  pts - major 5
+    */
+ 
+   struct stat sb;
+@@ -174,12 +175,12 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int unused) {
+    * other processes.
+    */
+   if (type == UV_TTY) {
+-    /* Reopening a pty in master mode won't work either because the reopened
+-     * pty will be in slave mode (*BSD) or reopening will allocate a new
+-     * master/slave pair (Linux). Therefore check if the fd points to a
+-     * slave device.
++    /* Reopening a pty in controller mode won't work either because the reopened
++     * pty will be in peripheral mode (*BSD) or reopening will allocate a new
++     * controller/peripheral pair (Linux). Therefore check if the fd points to a
++     * peripheral device.
+      */
+-    if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0)
++    if (uv__tty_is_peripheral(fd) && ttyname_r(fd, path, sizeof(path)) == 0)
+       r = uv__open_cloexec(path, mode | O_NOCTTY);
+     else
+       r = -1;
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0005-Use-roborio-time.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0005-Use-roborio-time.patch
deleted file mode 100644
index 46cc46f..0000000
--- a/third_party/allwpilib/upstream_utils/libuv_patches/0005-Use-roborio-time.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 690d487df2e60a01ab811aba34587466a38caead Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Tue, 26 Apr 2022 15:26:03 -0400
-Subject: [PATCH 5/9] Use roborio time
-
----
- src/unix/linux-core.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
-diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c
-index 85f3fc01..12ed7ff1 100644
---- a/src/unix/linux-core.c
-+++ b/src/unix/linux-core.c
-@@ -67,6 +67,10 @@
- # define CLOCK_MONOTONIC_COARSE 6
- #endif
- 
-+#ifdef __FRC_ROBORIO__
-+#include "wpi/timestamp.h"
-+#endif
-+
- /* This is rather annoying: CLOCK_BOOTTIME lives in <linux/time.h> but we can't
-  * include that file because it conflicts with <time.h>. We'll just have to
-  * define it ourselves.
-@@ -118,6 +122,9 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
- 
- 
- uint64_t uv__hrtime(uv_clocktype_t type) {
-+#ifdef __FRC_ROBORIO__
-+  return wpi::Now() * 1000u;
-+#else
-   static clock_t fast_clock_id = -1;
-   struct timespec t;
-   clock_t clock_id;
-@@ -151,6 +158,7 @@ done:
-     return 0;  /* Not really possible. */
- 
-   return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
-+#endif
- }
- 
- 
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0006-Style-comments-cleanup.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0006-Style-comments-cleanup.patch
index 964d944..d4aa9a2 100644
--- a/third_party/allwpilib/upstream_utils/libuv_patches/0006-Style-comments-cleanup.patch
+++ b/third_party/allwpilib/upstream_utils/libuv_patches/0006-Style-comments-cleanup.patch
@@ -1,19 +1,18 @@
-From ad60aa8e1c03cc5f0c88159d37f63b0c063927c7 Mon Sep 17 00:00:00 2001
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: PJ Reiniger <pj.reiniger@gmail.com>
 Date: Tue, 26 Apr 2022 15:28:52 -0400
-Subject: [PATCH 6/9] Style / comments cleanup
+Subject: [PATCH 06/10] Style / comments cleanup
 
 ---
  src/fs-poll.c     | 1 +
  src/unix/core.c   | 1 +
- src/unix/thread.c | 3 +--
  src/uv-common.c   | 1 +
  src/win/process.c | 1 -
  src/win/winsock.c | 1 +
- 6 files changed, 5 insertions(+), 3 deletions(-)
+ 5 files changed, 4 insertions(+), 1 deletion(-)
 
 diff --git a/src/fs-poll.c b/src/fs-poll.c
-index 5a39daed..1a7ca70d 100644
+index 5a39daed095502b2db34f23fcaf0ab04f31f96ff..1a7ca70d62c71f6eaef2b9985796cc46a6438869 100644
 --- a/src/fs-poll.c
 +++ b/src/fs-poll.c
 @@ -34,6 +34,7 @@
@@ -25,10 +24,10 @@
    uv_fs_poll_t* parent_handle;
    int busy_polling;
 diff --git a/src/unix/core.c b/src/unix/core.c
-index 223c5513..4c23f608 100644
+index 268fc9652f437eb0d0cda2a9e0b06b9e91eb9742..f53adc156a7c454c492abaeac29d90be436785fc 100644
 --- a/src/unix/core.c
 +++ b/src/unix/core.c
-@@ -544,6 +544,7 @@ int uv__accept(int sockfd) {
+@@ -575,6 +575,7 @@ int uv__accept(int sockfd) {
    return peerfd;
  }
  
@@ -36,39 +35,11 @@
  #if defined(__APPLE__)
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
-diff --git a/src/unix/thread.c b/src/unix/thread.c
-index 64726bd6..392a0715 100644
---- a/src/unix/thread.c
-+++ b/src/unix/thread.c
-@@ -85,7 +85,6 @@ error2:
-   return rc;
- }
- 
--
- int uv_barrier_wait(uv_barrier_t* barrier) {
-   struct _uv_barrier* b;
-   int last;
-@@ -94,6 +93,7 @@ int uv_barrier_wait(uv_barrier_t* barrier) {
-     return UV_EINVAL;
- 
-   b = barrier->b;
-+  /* Lock the mutex*/
-   uv_mutex_lock(&b->mutex);
- 
-   if (++b->in == b->threshold) {
-@@ -113,7 +113,6 @@ int uv_barrier_wait(uv_barrier_t* barrier) {
-   return last;
- }
- 
--
- void uv_barrier_destroy(uv_barrier_t* barrier) {
-   struct _uv_barrier* b;
- 
 diff --git a/src/uv-common.c b/src/uv-common.c
-index 49026c03..c9a32c03 100644
+index cd10b36b4a393e325ea03b93eb9897193ca9800b..bfcc3ef10f4fd7763221638947da6e02e7a17c33 100644
 --- a/src/uv-common.c
 +++ b/src/uv-common.c
-@@ -758,6 +758,7 @@ void uv__fs_readdir_cleanup(uv_fs_t* req) {
+@@ -799,6 +799,7 @@ void uv__fs_readdir_cleanup(uv_fs_t* req) {
    }
  }
  
@@ -77,7 +48,7 @@
  # pragma clang diagnostic push
  # pragma clang diagnostic ignored "-Wvarargs"
 diff --git a/src/win/process.c b/src/win/process.c
-index a49016f6..8e7835a5 100644
+index 2b1b46259959867482079962d0ea44246a42e7cb..18816d3b1e8c863f8ca74fe0104de1aecd0ae3fa 100644
 --- a/src/win/process.c
 +++ b/src/win/process.c
 @@ -35,7 +35,6 @@
@@ -89,7 +60,7 @@
  
  
 diff --git a/src/win/winsock.c b/src/win/winsock.c
-index 7843e9f1..cda82bc3 100644
+index 7843e9f13321c32419cebaacde82d30f471e11d7..cda82bc33c2c6e3dbfa9d978b5b40476228452bd 100644
 --- a/src/win/winsock.c
 +++ b/src/win/winsock.c
 @@ -25,6 +25,7 @@
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0007-Fix-Win32-warning-suppression-pragma.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0007-Fix-Win32-warning-suppression-pragma.patch
new file mode 100644
index 0000000..5ef597c
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/libuv_patches/0007-Fix-Win32-warning-suppression-pragma.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Sat, 21 May 2022 22:58:06 -0700
+Subject: [PATCH 07/10] Fix Win32 warning suppression pragma
+
+---
+ src/win/util.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/win/util.c b/src/win/util.c
+index af18cfa6c106c5de0996e4fff9b4127aaa3b576b..9324992ec521cc3496e3e9304e600963a3f20897 100644
+--- a/src/win/util.c
++++ b/src/win/util.c
+@@ -1692,7 +1692,7 @@ int uv_os_uname(uv_utsname_t* buffer) {
+   } else {
+     /* Silence GetVersionEx() deprecation warning. */
+     #ifdef _MSC_VER
+-    #pragma warning(suppress : 4996)
++    #pragma warning(disable : 4996)
+     #endif
+     if (GetVersionExW(&os_info) == 0) {
+       r = uv_translate_sys_error(GetLastError());
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0007-Squelch-GCC-12.1-warnings.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0007-Squelch-GCC-12.1-warnings.patch
deleted file mode 100644
index 92032da..0000000
--- a/third_party/allwpilib/upstream_utils/libuv_patches/0007-Squelch-GCC-12.1-warnings.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From ee8a72764f602928cc08d16d661602c0aefde050 Mon Sep 17 00:00:00 2001
-From: Tyler Veness <calcmogul@gmail.com>
-Date: Tue, 17 May 2022 21:36:57 -0700
-Subject: [PATCH 7/9] Squelch GCC 12.1 warnings
-
----
- src/unix/stream.c | 9 +++++++++
- src/uv-common.c   | 9 +++++++++
- 2 files changed, 18 insertions(+)
-
-diff --git a/src/unix/stream.c b/src/unix/stream.c
-index c6cc50e7..fa25812a 100644
---- a/src/unix/stream.c
-+++ b/src/unix/stream.c
-@@ -938,7 +938,16 @@ static void uv__write_callbacks(uv_stream_t* stream) {
-   if (QUEUE_EMPTY(&stream->write_completed_queue))
-     return;
- 
-+// FIXME: GCC 12.1 gives a possibly real warning, but we don't know how to fix
-+// it
-+#if __GNUC__ >= 12
-+#pragma GCC diagnostic push
-+#pragma GCC diagnostic ignored "-Wdangling-pointer="
-+#endif  // __GNUC__ >= 12
-   QUEUE_MOVE(&stream->write_completed_queue, &pq);
-+#if __GNUC__ >= 12
-+#pragma GCC diagnostic pop
-+#endif  // __GNUC__ >= 12
- 
-   while (!QUEUE_EMPTY(&pq)) {
-     /* Pop a req off write_completed_queue. */
-diff --git a/src/uv-common.c b/src/uv-common.c
-index c9a32c03..8ab600df 100644
---- a/src/uv-common.c
-+++ b/src/uv-common.c
-@@ -504,7 +504,16 @@ void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) {
-   QUEUE* q;
-   uv_handle_t* h;
- 
-+// FIXME: GCC 12.1 gives a possibly real warning, but we don't know how to fix
-+// it
-+#if __GNUC__ >= 12
-+#pragma GCC diagnostic push
-+#pragma GCC diagnostic ignored "-Wdangling-pointer="
-+#endif  // __GNUC__ >= 12
-   QUEUE_MOVE(&loop->handle_queue, &queue);
-+#if __GNUC__ >= 12
-+#pragma GCC diagnostic pop
-+#endif  // __GNUC__ >= 12
-   while (!QUEUE_EMPTY(&queue)) {
-     q = QUEUE_HEAD(&queue);
-     h = QUEUE_DATA(q, uv_handle_t, handle_queue);
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0008-Fix-Win32-warning-suppression-pragma.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0008-Fix-Win32-warning-suppression-pragma.patch
deleted file mode 100644
index bdc5f61..0000000
--- a/third_party/allwpilib/upstream_utils/libuv_patches/0008-Fix-Win32-warning-suppression-pragma.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From 49d5945dde1d182fd2d75cdf550120951796cb1f Mon Sep 17 00:00:00 2001
-From: Tyler Veness <calcmogul@gmail.com>
-Date: Sat, 21 May 2022 22:58:06 -0700
-Subject: [PATCH 8/9] Fix Win32 warning suppression pragma
-
----
- src/win/util.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/win/util.c b/src/win/util.c
-index 7a5dd2ef..d9888aec 100644
---- a/src/win/util.c
-+++ b/src/win/util.c
-@@ -1750,7 +1750,7 @@ int uv_os_uname(uv_utsname_t* buffer) {
-   } else {
-     /* Silence GetVersionEx() deprecation warning. */
-     #ifdef _MSC_VER
--    #pragma warning(suppress : 4996)
-+    #pragma warning(disable : 4996)
-     #endif
-     if (GetVersionExW(&os_info) == 0) {
-       r = uv_translate_sys_error(GetLastError());
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0008-Use-C-atomics.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0008-Use-C-atomics.patch
new file mode 100644
index 0000000..ffdc588
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/libuv_patches/0008-Use-C-atomics.patch
@@ -0,0 +1,531 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Thu, 13 Jul 2023 22:13:47 -0700
+Subject: [PATCH 08/10] Use C++ atomics
+
+---
+ src/unix/async.c  | 25 +++++++++++++------------
+ src/unix/core.c   |  3 ++-
+ src/unix/fs.c     | 32 +++++++++++++++++---------------
+ src/unix/kqueue.c | 10 ++++++----
+ src/unix/linux.c  | 45 +++++++++++++++++++++++----------------------
+ src/unix/tty.c    |  5 +++--
+ src/uv-common.c   |  2 +-
+ src/uv-common.h   |  8 +++-----
+ 8 files changed, 68 insertions(+), 62 deletions(-)
+
+diff --git a/src/unix/async.c b/src/unix/async.c
+index 0ff2669e30a628dbb2df9e28ba14b38cf14114e5..fef4ae93343edc0341179a1c4739dcd831ef6e26 100644
+--- a/src/unix/async.c
++++ b/src/unix/async.c
+@@ -26,7 +26,6 @@
+ #include "internal.h"
+ 
+ #include <errno.h>
+-#include <stdatomic.h>
+ #include <stdio.h>  /* snprintf() */
+ #include <assert.h>
+ #include <stdlib.h>
+@@ -38,6 +37,8 @@
+ #include <sys/eventfd.h>
+ #endif
+ 
++#include <atomic>
++
+ static void uv__async_send(uv_loop_t* loop);
+ static int uv__async_start(uv_loop_t* loop);
+ static void uv__cpu_relax(void);
+@@ -63,14 +64,14 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
+ 
+ 
+ int uv_async_send(uv_async_t* handle) {
+-  _Atomic int* pending;
+-  _Atomic int* busy;
++  std::atomic<int>* pending;
++  std::atomic<int>* busy;
+ 
+-  pending = (_Atomic int*) &handle->pending;
+-  busy = (_Atomic int*) &handle->u.fd;
++  pending = (std::atomic<int>*) &handle->pending;
++  busy = (std::atomic<int>*) &handle->u.fd;
+ 
+   /* Do a cheap read first. */
+-  if (atomic_load_explicit(pending, memory_order_relaxed) != 0)
++  if (atomic_load_explicit(pending, std::memory_order_relaxed) != 0)
+     return 0;
+ 
+   /* Set the loop to busy. */
+@@ -90,12 +91,12 @@ int uv_async_send(uv_async_t* handle) {
+ /* Wait for the busy flag to clear before closing.
+  * Only call this from the event loop thread. */
+ static void uv__async_spin(uv_async_t* handle) {
+-  _Atomic int* pending;
+-  _Atomic int* busy;
++  std::atomic<int>* pending;
++  std::atomic<int>* busy;
+   int i;
+ 
+-  pending = (_Atomic int*) &handle->pending;
+-  busy = (_Atomic int*) &handle->u.fd;
++  pending = (std::atomic<int>*) &handle->pending;
++  busy = (std::atomic<int>*) &handle->u.fd;
+ 
+   /* Set the pending flag first, so no new events will be added by other
+    * threads after this function returns. */
+@@ -135,7 +136,7 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+   struct uv__queue queue;
+   struct uv__queue* q;
+   uv_async_t* h;
+-  _Atomic int *pending;
++  std::atomic<int> *pending;
+ 
+   assert(w == &loop->async_io_watcher);
+ 
+@@ -166,7 +167,7 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+     uv__queue_insert_tail(&loop->async_handles, q);
+ 
+     /* Atomically fetch and clear pending flag */
+-    pending = (_Atomic int*) &h->pending;
++    pending = (std::atomic<int>*) &h->pending;
+     if (atomic_exchange(pending, 0) == 0)
+       continue;
+ 
+diff --git a/src/unix/core.c b/src/unix/core.c
+index f53adc156a7c454c492abaeac29d90be436785fc..ce7fd2cdfdd53410dc694450bd56dffc26ff4792 100644
+--- a/src/unix/core.c
++++ b/src/unix/core.c
+@@ -45,6 +45,7 @@
+ #include <sys/utsname.h>
+ #include <sys/time.h>
+ #include <time.h> /* clock_gettime */
++#include <atomic>
+ 
+ #ifdef __sun
+ # include <sys/filio.h>
+@@ -263,7 +264,7 @@ int uv__getiovmax(void) {
+ #if defined(IOV_MAX)
+   return IOV_MAX;
+ #elif defined(_SC_IOV_MAX)
+-  static _Atomic int iovmax_cached = -1;
++  static std::atomic<int> iovmax_cached = -1;
+   int iovmax;
+ 
+   iovmax = atomic_load_explicit(&iovmax_cached, memory_order_relaxed);
+diff --git a/src/unix/fs.c b/src/unix/fs.c
+index e25d02e54dbe93e4b9c22b0140108c99ae2cb4f7..aba190a9c0240fba0128fb7fbc5d92d7fa86214b 100644
+--- a/src/unix/fs.c
++++ b/src/unix/fs.c
+@@ -46,6 +46,8 @@
+ #include <fcntl.h>
+ #include <poll.h>
+ 
++#include <atomic>
++
+ #if defined(__DragonFly__)        ||                                      \
+     defined(__FreeBSD__)          ||                                      \
+     defined(__OpenBSD__)          ||                                      \
+@@ -313,7 +315,7 @@ static int uv__fs_mkstemp(uv_fs_t* req) {
+   static uv_once_t once = UV_ONCE_INIT;
+   int r;
+ #ifdef O_CLOEXEC
+-  static _Atomic int no_cloexec_support;
++  static std::atomic<int> no_cloexec_support;
+ #endif
+   static const char pattern[] = "XXXXXX";
+   static const size_t pattern_size = sizeof(pattern) - 1;
+@@ -338,7 +340,7 @@ static int uv__fs_mkstemp(uv_fs_t* req) {
+   uv_once(&once, uv__mkostemp_initonce);
+ 
+ #ifdef O_CLOEXEC
+-  if (atomic_load_explicit(&no_cloexec_support, memory_order_relaxed) == 0 &&
++  if (atomic_load_explicit(&no_cloexec_support, std::memory_order_relaxed) == 0 &&
+       uv__mkostemp != NULL) {
+     r = uv__mkostemp(path, O_CLOEXEC);
+ 
+@@ -352,7 +354,7 @@ static int uv__fs_mkstemp(uv_fs_t* req) {
+ 
+     /* We set the static variable so that next calls don't even
+        try to use mkostemp. */
+-    atomic_store_explicit(&no_cloexec_support, 1, memory_order_relaxed);
++    atomic_store_explicit(&no_cloexec_support, 1, std::memory_order_relaxed);
+   }
+ #endif  /* O_CLOEXEC */
+ 
+@@ -462,7 +464,7 @@ static ssize_t uv__fs_preadv(uv_file fd,
+ 
+ static ssize_t uv__fs_read(uv_fs_t* req) {
+ #if TRY_PREADV
+-  static _Atomic int no_preadv;
++  static std::atomic<int> no_preadv;
+ #endif
+   unsigned int iovmax;
+   ssize_t result;
+@@ -486,7 +488,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
+     result = preadv(req->file, (struct iovec*) req->bufs, req->nbufs, req->off);
+ #else
+ # if TRY_PREADV
+-    if (atomic_load_explicit(&no_preadv, memory_order_relaxed)) retry:
++    if (atomic_load_explicit(&no_preadv, std::memory_order_relaxed)) retry:
+ # endif
+     {
+       result = uv__fs_preadv(req->file, req->bufs, req->nbufs, req->off);
+@@ -498,7 +500,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
+                       req->nbufs,
+                       req->off);
+       if (result == -1 && errno == ENOSYS) {
+-        atomic_store_explicit(&no_preadv, 1, memory_order_relaxed);
++        atomic_store_explicit(&no_preadv, 1, std::memory_order_relaxed);
+         goto retry;
+       }
+     }
+@@ -939,10 +941,10 @@ static int uv__is_cifs_or_smb(int fd) {
+ 
+ static ssize_t uv__fs_try_copy_file_range(int in_fd, off_t* off,
+                                           int out_fd, size_t len) {
+-  static _Atomic int no_copy_file_range_support;
++  static std::atomic<int> no_copy_file_range_support;
+   ssize_t r;
+ 
+-  if (atomic_load_explicit(&no_copy_file_range_support, memory_order_relaxed)) {
++  if (atomic_load_explicit(&no_copy_file_range_support, std::memory_order_relaxed)) {
+     errno = ENOSYS;
+     return -1;
+   }
+@@ -961,7 +963,7 @@ static ssize_t uv__fs_try_copy_file_range(int in_fd, off_t* off,
+       errno = ENOSYS;  /* Use fallback. */
+     break;
+   case ENOSYS:
+-    atomic_store_explicit(&no_copy_file_range_support, 1, memory_order_relaxed);
++    atomic_store_explicit(&no_copy_file_range_support, 1, std::memory_order_relaxed);
+     break;
+   case EPERM:
+     /* It's been reported that CIFS spuriously fails.
+@@ -1162,7 +1164,7 @@ static ssize_t uv__fs_lutime(uv_fs_t* req) {
+ 
+ static ssize_t uv__fs_write(uv_fs_t* req) {
+ #if TRY_PREADV
+-  static _Atomic int no_pwritev;
++  static std::atomic<int> no_pwritev;
+ #endif
+   ssize_t r;
+ 
+@@ -1191,7 +1193,7 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
+     r = pwritev(req->file, (struct iovec*) req->bufs, req->nbufs, req->off);
+ #else
+ # if TRY_PREADV
+-    if (atomic_load_explicit(&no_pwritev, memory_order_relaxed)) retry:
++    if (atomic_load_explicit(&no_pwritev, std::memory_order_relaxed)) retry:
+ # endif
+     {
+       r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
+@@ -1203,7 +1205,7 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
+                   req->nbufs,
+                   req->off);
+       if (r == -1 && errno == ENOSYS) {
+-        atomic_store_explicit(&no_pwritev, 1, memory_order_relaxed);
++        atomic_store_explicit(&no_pwritev, 1, std::memory_order_relaxed);
+         goto retry;
+       }
+     }
+@@ -1483,14 +1485,14 @@ static int uv__fs_statx(int fd,
+                         uv_stat_t* buf) {
+   STATIC_ASSERT(UV_ENOSYS != -1);
+ #ifdef __linux__
+-  static _Atomic int no_statx;
++  static std::atomic<int> no_statx;
+   struct uv__statx statxbuf;
+   int dirfd;
+   int flags;
+   int mode;
+   int rc;
+ 
+-  if (atomic_load_explicit(&no_statx, memory_order_relaxed))
++  if (atomic_load_explicit(&no_statx, std::memory_order_relaxed))
+     return UV_ENOSYS;
+ 
+   dirfd = AT_FDCWD;
+@@ -1524,7 +1526,7 @@ static int uv__fs_statx(int fd,
+      * implemented, rc might return 1 with 0 set as the error code in which
+      * case we return ENOSYS.
+      */
+-    atomic_store_explicit(&no_statx, 1, memory_order_relaxed);
++    atomic_store_explicit(&no_statx, 1, std::memory_order_relaxed);
+     return UV_ENOSYS;
+   }
+ 
+diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c
+index 28e55aae6c613576ede7024a5c73d746e134d865..ffe0f9191cc7b0c233447db358077d8814e0217e 100644
+--- a/src/unix/kqueue.c
++++ b/src/unix/kqueue.c
+@@ -34,6 +34,8 @@
+ #include <fcntl.h>
+ #include <time.h>
+ 
++#include <atomic>
++
+ /*
+  * Required on
+  * - Until at least FreeBSD 11.0
+@@ -60,7 +62,7 @@ int uv__kqueue_init(uv_loop_t* loop) {
+ 
+ 
+ #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+-static _Atomic int uv__has_forked_with_cfrunloop;
++static std::atomic<int> uv__has_forked_with_cfrunloop;
+ #endif
+ 
+ int uv__io_fork(uv_loop_t* loop) {
+@@ -84,7 +86,7 @@ int uv__io_fork(uv_loop_t* loop) {
+     */
+     atomic_store_explicit(&uv__has_forked_with_cfrunloop,
+                           1,
+-                          memory_order_relaxed);
++                          std::memory_order_relaxed);
+     uv__free(loop->cf_state);
+     loop->cf_state = NULL;
+   }
+@@ -541,7 +543,7 @@ int uv_fs_event_start(uv_fs_event_t* handle,
+     goto fallback;
+ 
+   if (0 == atomic_load_explicit(&uv__has_forked_with_cfrunloop,
+-                                memory_order_relaxed)) {
++                                std::memory_order_relaxed)) {
+     int r;
+     /* The fallback fd is no longer needed */
+     uv__close_nocheckstdio(fd);
+@@ -577,7 +579,7 @@ int uv_fs_event_stop(uv_fs_event_t* handle) {
+ 
+ #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+   if (0 == atomic_load_explicit(&uv__has_forked_with_cfrunloop,
+-                                memory_order_relaxed))
++                                std::memory_order_relaxed))
+     if (handle->cf_cb != NULL)
+       r = uv__fsevents_close(handle);
+ #endif
+diff --git a/src/unix/linux.c b/src/unix/linux.c
+index 157443792f1216c83b4221c3810d17c81c5913c4..e3dfb186dc531e5c8197a81681c00d693e0913c6 100644
+--- a/src/unix/linux.c
++++ b/src/unix/linux.c
+@@ -27,7 +27,6 @@
+ #include "internal.h"
+ 
+ #include <inttypes.h>
+-#include <stdatomic.h>
+ #include <stddef.h>  /* offsetof */
+ #include <stdint.h>
+ #include <stdio.h>
+@@ -133,6 +132,8 @@
+ # include <netpacket/packet.h>
+ #endif /* HAVE_IFADDRS_H */
+ 
++#include <atomic>
++
+ enum {
+   UV__IORING_SETUP_SQPOLL = 2u,
+ };
+@@ -311,14 +312,14 @@ static struct watcher_root* uv__inotify_watchers(uv_loop_t* loop) {
+ 
+ 
+ unsigned uv__kernel_version(void) {
+-  static _Atomic unsigned cached_version;
++  static std::atomic<unsigned int> cached_version;
+   struct utsname u;
+   unsigned version;
+   unsigned major;
+   unsigned minor;
+   unsigned patch;
+ 
+-  version = atomic_load_explicit(&cached_version, memory_order_relaxed);
++  version = std::atomic_load_explicit(&cached_version, std::memory_order_relaxed);
+   if (version != 0)
+     return version;
+ 
+@@ -329,7 +330,7 @@ unsigned uv__kernel_version(void) {
+     return 0;
+ 
+   version = major * 65536 + minor * 256 + patch;
+-  atomic_store_explicit(&cached_version, version, memory_order_relaxed);
++  std::atomic_store_explicit(&cached_version, version, std::memory_order_relaxed);
+ 
+   return version;
+ }
+@@ -424,16 +425,16 @@ static int uv__use_io_uring(void) {
+   return 0;  /* Possibly available but blocked by seccomp. */
+ #else
+   /* Ternary: unknown=0, yes=1, no=-1 */
+-  static _Atomic int use_io_uring;
++  static std::atomic<int> use_io_uring;
+   char* val;
+   int use;
+ 
+-  use = atomic_load_explicit(&use_io_uring, memory_order_relaxed);
++  use = std::atomic_load_explicit(&use_io_uring, std::memory_order_relaxed);
+ 
+   if (use == 0) {
+     val = getenv("UV_USE_IO_URING");
+     use = val == NULL || atoi(val) ? 1 : -1;
+-    atomic_store_explicit(&use_io_uring, use, memory_order_relaxed);
++    std::atomic_store_explicit(&use_io_uring, use, std::memory_order_relaxed);
+   }
+ 
+   return use > 0;
+@@ -709,8 +710,8 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
+   if (iou->ringfd == -1)
+     return NULL;
+ 
+-  head = atomic_load_explicit((_Atomic uint32_t*) iou->sqhead,
+-                              memory_order_acquire);
++  head = std::atomic_load_explicit((std::atomic<uint32_t>*) iou->sqhead,
++                              std::memory_order_acquire);
+   tail = *iou->sqtail;
+   mask = iou->sqmask;
+ 
+@@ -739,12 +740,12 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
+ static void uv__iou_submit(struct uv__iou* iou) {
+   uint32_t flags;
+ 
+-  atomic_store_explicit((_Atomic uint32_t*) iou->sqtail,
++  std::atomic_store_explicit((std::atomic<uint32_t>*) iou->sqtail,
+                         *iou->sqtail + 1,
+-                        memory_order_release);
++                        std::memory_order_release);
+ 
+-  flags = atomic_load_explicit((_Atomic uint32_t*) iou->sqflags,
+-                               memory_order_acquire);
++  flags = std::atomic_load_explicit((std::atomic<uint32_t>*) iou->sqflags,
++                               std::memory_order_acquire);
+ 
+   if (flags & UV__IORING_SQ_NEED_WAKEUP)
+     if (uv__io_uring_enter(iou->ringfd, 0, 0, UV__IORING_ENTER_SQ_WAKEUP))
+@@ -1076,8 +1077,8 @@ static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) {
+   int rc;
+ 
+   head = *iou->cqhead;
+-  tail = atomic_load_explicit((_Atomic uint32_t*) iou->cqtail,
+-                              memory_order_acquire);
++  tail = std::atomic_load_explicit((std::atomic<uint32_t>*) iou->cqtail,
++                              std::memory_order_acquire);
+   mask = iou->cqmask;
+   cqe = (uv__io_uring_cqe*)iou->cqe;
+   nevents = 0;
+@@ -1109,15 +1110,15 @@ static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) {
+     nevents++;
+   }
+ 
+-  atomic_store_explicit((_Atomic uint32_t*) iou->cqhead,
++  std::atomic_store_explicit((std::atomic<uint32_t>*) iou->cqhead,
+                         tail,
+-                        memory_order_release);
++                        std::memory_order_release);
+ 
+   /* Check whether CQE's overflowed, if so enter the kernel to make them
+    * available. Don't grab them immediately but in the next loop iteration to
+    * avoid loop starvation. */
+-  flags = atomic_load_explicit((_Atomic uint32_t*) iou->sqflags,
+-                               memory_order_acquire);
++  flags = std::atomic_load_explicit((std::atomic<uint32_t>*) iou->sqflags,
++                               std::memory_order_acquire);
+ 
+   if (flags & UV__IORING_SQ_CQ_OVERFLOW) {
+     do
+@@ -1531,7 +1532,7 @@ update_timeout:
+ }
+ 
+ uint64_t uv__hrtime(uv_clocktype_t type) {
+-  static _Atomic clock_t fast_clock_id = -1;
++  static std::atomic<clock_t> fast_clock_id = -1;
+   struct timespec t;
+   clock_t clock_id;
+ 
+@@ -1547,7 +1548,7 @@ uint64_t uv__hrtime(uv_clocktype_t type) {
+   if (type != UV_CLOCK_FAST)
+     goto done;
+ 
+-  clock_id = atomic_load_explicit(&fast_clock_id, memory_order_relaxed);
++  clock_id = std::atomic_load_explicit(&fast_clock_id, std::memory_order_relaxed);
+   if (clock_id != -1)
+     goto done;
+ 
+@@ -1556,7 +1557,7 @@ uint64_t uv__hrtime(uv_clocktype_t type) {
+     if (t.tv_nsec <= 1 * 1000 * 1000)
+       clock_id = CLOCK_MONOTONIC_COARSE;
+ 
+-  atomic_store_explicit(&fast_clock_id, clock_id, memory_order_relaxed);
++  std::atomic_store_explicit(&fast_clock_id, clock_id, std::memory_order_relaxed);
+ 
+ done:
+ 
+diff --git a/src/unix/tty.c b/src/unix/tty.c
+index 1bd217b5a15eed13a8349c479b53471dd36ca216..1304c6d8685cfd122cffea066dc668d1dfc9ae02 100644
+--- a/src/unix/tty.c
++++ b/src/unix/tty.c
+@@ -22,7 +22,6 @@
+ #include "uv.h"
+ #include "internal.h"
+ 
+-#include <stdatomic.h>
+ #include <stdlib.h>
+ #include <assert.h>
+ #include <unistd.h>
+@@ -30,6 +29,8 @@
+ #include <errno.h>
+ #include <sys/ioctl.h>
+ 
++#include <atomic>
++
+ #if defined(__MVS__) && !defined(IMAXBEL)
+ #define IMAXBEL 0
+ #endif
+@@ -64,7 +65,7 @@ static int isreallyatty(int file) {
+ 
+ static int orig_termios_fd = -1;
+ static struct termios orig_termios;
+-static _Atomic int termios_spinlock;
++static std::atomic<int> termios_spinlock;
+ 
+ int uv__tcsetattr(int fd, int how, const struct termios *term) {
+   int rc;
+diff --git a/src/uv-common.c b/src/uv-common.c
+index bfcc3ef10f4fd7763221638947da6e02e7a17c33..5c6d84155408ae4f7c3c6ff9b48bd09ccd16a92e 100644
+--- a/src/uv-common.c
++++ b/src/uv-common.c
+@@ -951,7 +951,7 @@ void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
+ __attribute__((destructor))
+ #endif
+ void uv_library_shutdown(void) {
+-  static int was_shutdown;
++  static std::atomic<int> was_shutdown;
+ 
+   if (uv__exchange_int_relaxed(&was_shutdown, 1))
+     return;
+diff --git a/src/uv-common.h b/src/uv-common.h
+index cd57e5a35153d0557351b60cce0c5be7a4468b60..5dce8eaf2705b47935b218181f6dd69af0d5b61b 100644
+--- a/src/uv-common.h
++++ b/src/uv-common.h
+@@ -32,15 +32,13 @@
+ #include <stddef.h>
+ #include <stdint.h>
+ 
++#include <atomic>
++
+ #include "uv.h"
+ #include "uv/tree.h"
+ #include "queue.h"
+ #include "strscpy.h"
+ 
+-#ifndef _MSC_VER
+-# include <stdatomic.h>
+-#endif
+-
+ #if EDOM > 0
+ # define UV__ERR(x) (-(x))
+ #else
+@@ -70,7 +68,7 @@ extern int snprintf(char*, size_t, const char*, ...);
+   InterlockedExchangeNoFence((LONG volatile*)(p), v)
+ #else
+ #define uv__exchange_int_relaxed(p, v)                                        \
+-  atomic_exchange_explicit((_Atomic int*)(p), v, memory_order_relaxed)
++  std::atomic_exchange_explicit((std::atomic<int>*)(p), v, std::memory_order_relaxed)
+ #endif
+ 
+ #define UV__UDP_DGRAM_MAXSIZE (64 * 1024)
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0009-Avoid-unused-variable-warning-on-Mac.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0009-Avoid-unused-variable-warning-on-Mac.patch
deleted file mode 100644
index 8db2deb..0000000
--- a/third_party/allwpilib/upstream_utils/libuv_patches/0009-Avoid-unused-variable-warning-on-Mac.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-From d83eaeff1f53bc3dede8a46a05cdb3ca94d1aac4 Mon Sep 17 00:00:00 2001
-From: Peter Johnson <johnson.peter@gmail.com>
-Date: Sun, 5 Jun 2022 15:40:35 -0700
-Subject: [PATCH 9/9] Avoid unused variable warning on Mac
-
----
- src/unix/darwin.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/unix/darwin.c b/src/unix/darwin.c
-index eeb35720..ed51a6ad 100644
---- a/src/unix/darwin.c
-+++ b/src/unix/darwin.c
-@@ -257,6 +257,7 @@ static int uv__get_cpu_speed(uint64_t* speed) {
-   // clock_frequency_str's lifetimes after their initialization
-   {
-     kr = pIOMasterPort(MACH_PORT_NULL, &mach_port);
-+    (void) kr;
-     assert(kr == KERN_SUCCESS);
-     CFMutableDictionaryRef classes_to_match
-         = pIOServiceMatching("IOPlatformDevice");
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0009-Remove-static-from-array-indices.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0009-Remove-static-from-array-indices.patch
new file mode 100644
index 0000000..f95fc5f
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/libuv_patches/0009-Remove-static-from-array-indices.patch
@@ -0,0 +1,67 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Thu, 13 Jul 2023 23:30:58 -0700
+Subject: [PATCH 09/10] Remove static from array indices
+
+---
+ src/unix/linux.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/src/unix/linux.c b/src/unix/linux.c
+index e3dfb186dc531e5c8197a81681c00d693e0913c6..d365b623a0a25228f0c6acf1fa14a5c7a9f1efbf 100644
+--- a/src/unix/linux.c
++++ b/src/unix/linux.c
+@@ -2060,7 +2060,7 @@ static uint64_t uv__read_uint64(const char* filename) {
+  * finds the location and length of the memory controller mount path.
+  * This disregards the leading / for easy concatenation of paths.
+  * Returns NULL if the memory controller wasn't found. */
+-static char* uv__cgroup1_find_memory_controller(char buf[static 1024],
++static char* uv__cgroup1_find_memory_controller(char buf[1024],
+                                                 int* n) {
+   char* p;
+ 
+@@ -2081,7 +2081,7 @@ static char* uv__cgroup1_find_memory_controller(char buf[static 1024],
+   return p;
+ }
+ 
+-static void uv__get_cgroup1_memory_limits(char buf[static 1024], uint64_t* high,
++static void uv__get_cgroup1_memory_limits(char buf[1024], uint64_t* high,
+                                           uint64_t* max) {
+   char filename[4097];
+   char* p;
+@@ -2121,7 +2121,7 @@ update_limits:
+     *max = UINT64_MAX;
+ }
+ 
+-static void uv__get_cgroup2_memory_limits(char buf[static 1024], uint64_t* high,
++static void uv__get_cgroup2_memory_limits(char buf[1024], uint64_t* high,
+                                           uint64_t* max) {
+   char filename[4097];
+   char* p;
+@@ -2138,7 +2138,7 @@ static void uv__get_cgroup2_memory_limits(char buf[static 1024], uint64_t* high,
+   *high = uv__read_uint64(filename);
+ }
+ 
+-static uint64_t uv__get_cgroup_constrained_memory(char buf[static 1024]) {
++static uint64_t uv__get_cgroup_constrained_memory(char buf[1024]) {
+   uint64_t high;
+   uint64_t max;
+ 
+@@ -2164,7 +2164,7 @@ uint64_t uv_get_constrained_memory(void) {
+ }
+ 
+ 
+-static uint64_t uv__get_cgroup1_current_memory(char buf[static 1024]) {
++static uint64_t uv__get_cgroup1_current_memory(char buf[1024]) {
+   char filename[4097];
+   uint64_t current;
+   char* p;
+@@ -2188,7 +2188,7 @@ static uint64_t uv__get_cgroup1_current_memory(char buf[static 1024]) {
+   return uv__read_uint64("/sys/fs/cgroup/memory/memory.usage_in_bytes");
+ }
+ 
+-static uint64_t uv__get_cgroup2_current_memory(char buf[static 1024]) {
++static uint64_t uv__get_cgroup2_current_memory(char buf[1024]) {
+   char filename[4097];
+   char* p;
+   int n;
diff --git a/third_party/allwpilib/upstream_utils/libuv_patches/0010-Add-pragmas-for-missing-libraries-and-set-_WIN32_WIN.patch b/third_party/allwpilib/upstream_utils/libuv_patches/0010-Add-pragmas-for-missing-libraries-and-set-_WIN32_WIN.patch
new file mode 100644
index 0000000..07d02f5
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/libuv_patches/0010-Add-pragmas-for-missing-libraries-and-set-_WIN32_WIN.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Fri, 14 Jul 2023 16:40:18 -0700
+Subject: [PATCH 10/10] Add pragmas for missing libraries and set _WIN32_WINNT
+ to Windows 10
+
+This makes GetSystemTimePreciseAsFileTime() available.
+
+The #define value is from
+https://learn.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt.
+---
+ include/uv/win.h | 2 +-
+ src/win/util.c   | 2 ++
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/include/uv/win.h b/include/uv/win.h
+index 6d0afe69e7dd4caf4c9459e548fe75cf0c51b501..613065df435d813cd517efbc138b13ee46f01f2d 100644
+--- a/include/uv/win.h
++++ b/include/uv/win.h
+@@ -20,7 +20,7 @@
+  */
+ 
+ #ifndef _WIN32_WINNT
+-# define _WIN32_WINNT   0x0600
++# define _WIN32_WINNT   0x0A00
+ #endif
+ 
+ #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
+diff --git a/src/win/util.c b/src/win/util.c
+index 9324992ec521cc3496e3e9304e600963a3f20897..4b76417fcbac2480725471740c037deb859e17ca 100644
+--- a/src/win/util.c
++++ b/src/win/util.c
+@@ -73,7 +73,9 @@ static char *process_title;
+ static CRITICAL_SECTION process_title_lock;
+ 
+ #pragma comment(lib, "Advapi32.lib")
++#pragma comment(lib, "Dbghelp.lib")
+ #pragma comment(lib, "IPHLPAPI.lib")
++#pragma comment(lib, "Ole32.lib")
+ #pragma comment(lib, "Psapi.lib")
+ #pragma comment(lib, "Userenv.lib")
+ #pragma comment(lib, "kernel32.lib")
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0001-Fix-spelling-language-errors.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0001-Fix-spelling-language-errors.patch
deleted file mode 100644
index 5d6916d..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0001-Fix-spelling-language-errors.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From 3d09b3d7b78ffc037a32725cc4002976b908d965 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sat, 7 May 2022 20:50:26 -0400
-Subject: [PATCH 01/28] Fix spelling/language errors
-
----
- llvm/include/llvm/Support/ErrorHandling.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/llvm/include/llvm/Support/ErrorHandling.h b/llvm/include/llvm/Support/ErrorHandling.h
-index f980510d3..6791df6be 100644
---- a/llvm/include/llvm/Support/ErrorHandling.h
-+++ b/llvm/include/llvm/Support/ErrorHandling.h
-@@ -44,7 +44,7 @@ namespace llvm {
-   void install_fatal_error_handler(fatal_error_handler_t handler,
-                                    void *user_data = nullptr);
- 
--  /// Restores default error handling behaviour.
-+  /// Restores default error handling behavior.
-   void remove_fatal_error_handler();
- 
-   /// ScopedFatalErrorHandler - This is a simple helper class which just
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0001-Remove-StringRef-ArrayRef-and-Optional.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0001-Remove-StringRef-ArrayRef-and-Optional.patch
new file mode 100644
index 0000000..48327f8
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0001-Remove-StringRef-ArrayRef-and-Optional.patch
@@ -0,0 +1,2034 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sat, 7 May 2022 22:09:18 -0400
+Subject: [PATCH 01/31] Remove StringRef, ArrayRef, and Optional
+
+---
+ llvm/include/llvm/ADT/PointerUnion.h          |  1 -
+ llvm/include/llvm/ADT/SmallSet.h              |  2 +-
+ llvm/include/llvm/ADT/SmallString.h           | 77 ++++++++++---------
+ llvm/include/llvm/ADT/SmallVector.h           |  7 +-
+ llvm/include/llvm/ADT/StringMap.h             | 38 ++++-----
+ llvm/include/llvm/ADT/StringMapEntry.h        | 20 ++---
+ llvm/include/llvm/Support/Chrono.h            | 10 +--
+ llvm/include/llvm/Support/Compiler.h          |  2 +-
+ llvm/include/llvm/Support/ConvertUTF.h        | 31 ++++----
+ llvm/include/llvm/Support/ErrorHandling.h     |  9 +--
+ .../llvm/Support/SmallVectorMemoryBuffer.h    |  6 +-
+ llvm/include/llvm/Support/VersionTuple.h      |  6 --
+ .../llvm/Support/Windows/WindowsSupport.h     |  4 +-
+ llvm/include/llvm/Support/raw_ostream.h       | 46 ++++++-----
+ llvm/include/llvm/Support/xxhash.h            | 16 ++--
+ llvm/lib/Support/ConvertUTFWrapper.cpp        | 38 ++++-----
+ llvm/lib/Support/ErrorHandling.cpp            | 13 ++--
+ llvm/lib/Support/SmallVector.cpp              |  5 +-
+ llvm/lib/Support/StringMap.cpp                | 12 +--
+ llvm/lib/Support/raw_ostream.cpp              | 25 +++---
+ llvm/lib/Support/xxhash.cpp                   | 10 +--
+ llvm/unittests/ADT/DenseMapTest.cpp           | 29 +------
+ llvm/unittests/ADT/FunctionExtrasTest.cpp     | 12 +--
+ llvm/unittests/ADT/HashingTest.cpp            |  2 +-
+ llvm/unittests/ADT/SmallPtrSetTest.cpp        |  1 -
+ llvm/unittests/ADT/SmallStringTest.cpp        | 50 ++++++------
+ llvm/unittests/ADT/SmallVectorTest.cpp        | 30 ++------
+ llvm/unittests/ADT/StringMapTest.cpp          | 32 ++++----
+ llvm/unittests/Support/ConvertUTFTest.cpp     | 41 +++++-----
+ llvm/unittests/Support/xxhashTest.cpp         |  4 +-
+ 30 files changed, 264 insertions(+), 315 deletions(-)
+
+diff --git a/llvm/include/llvm/ADT/PointerUnion.h b/llvm/include/llvm/ADT/PointerUnion.h
+index 7d4ed02b622626bb8043acb57b8ce7ed97a5f949..8ac68dbc0a791b8ac0e0ca865e69024cb642aa70 100644
+--- a/llvm/include/llvm/ADT/PointerUnion.h
++++ b/llvm/include/llvm/ADT/PointerUnion.h
+@@ -17,7 +17,6 @@
+ 
+ #include "llvm/ADT/DenseMapInfo.h"
+ #include "llvm/ADT/PointerIntPair.h"
+-#include "llvm/ADT/STLExtras.h"
+ #include "llvm/Support/Casting.h"
+ #include "llvm/Support/PointerLikeTypeTraits.h"
+ #include <algorithm>
+diff --git a/llvm/include/llvm/ADT/SmallSet.h b/llvm/include/llvm/ADT/SmallSet.h
+index a16e8ac6f07552d98250e808190b00ee270f12b3..aeee5f97799aea7e7588d7afba1e47b4fa3d8c7b 100644
+--- a/llvm/include/llvm/ADT/SmallSet.h
++++ b/llvm/include/llvm/ADT/SmallSet.h
+@@ -16,12 +16,12 @@
+ 
+ #include "llvm/ADT/SmallPtrSet.h"
+ #include "llvm/ADT/SmallVector.h"
+-#include "llvm/ADT/STLExtras.h"
+ #include "llvm/ADT/iterator.h"
+ #include "llvm/Support/Compiler.h"
+ #include "llvm/Support/type_traits.h"
+ #include <cstddef>
+ #include <functional>
++#include <optional>
+ #include <set>
+ #include <type_traits>
+ #include <utility>
+diff --git a/llvm/include/llvm/ADT/SmallString.h b/llvm/include/llvm/ADT/SmallString.h
+index 0052c86fb37b82dcdf577a7acf06e3a47f54da61..4d673cc8b1c49cf8a3f19653de53881cd12662ee 100644
+--- a/llvm/include/llvm/ADT/SmallString.h
++++ b/llvm/include/llvm/ADT/SmallString.h
+@@ -15,8 +15,9 @@
+ #define LLVM_ADT_SMALLSTRING_H
+ 
+ #include "llvm/ADT/SmallVector.h"
+-#include "llvm/ADT/StringRef.h"
+ #include <cstddef>
++#include <string>
++#include <string_view>
+ 
+ namespace llvm {
+ 
+@@ -28,11 +29,11 @@ public:
+   /// Default ctor - Initialize to empty.
+   SmallString() = default;
+ 
+-  /// Initialize from a StringRef.
+-  SmallString(StringRef S) : SmallVector<char, InternalLen>(S.begin(), S.end()) {}
++  /// Initialize from a std::string_view.
++  SmallString(std::string_view S) : SmallVector<char, InternalLen>(S.begin(), S.end()) {}
+ 
+-  /// Initialize by concatenating a list of StringRefs.
+-  SmallString(std::initializer_list<StringRef> Refs)
++  /// Initialize by concatenating a list of std::string_views.
++  SmallString(std::initializer_list<std::string_view> Refs)
+       : SmallVector<char, InternalLen>() {
+     this->append(Refs);
+   }
+@@ -47,13 +48,13 @@ public:
+ 
+   using SmallVector<char, InternalLen>::assign;
+ 
+-  /// Assign from a StringRef.
+-  void assign(StringRef RHS) {
++  /// Assign from a std::string_view.
++  void assign(std::string_view RHS) {
+     SmallVectorImpl<char>::assign(RHS.begin(), RHS.end());
+   }
+ 
+-  /// Assign from a list of StringRefs.
+-  void assign(std::initializer_list<StringRef> Refs) {
++  /// Assign from a list of std::string_views.
++  void assign(std::initializer_list<std::string_view> Refs) {
+     this->clear();
+     append(Refs);
+   }
+@@ -64,19 +65,19 @@ public:
+ 
+   using SmallVector<char, InternalLen>::append;
+ 
+-  /// Append from a StringRef.
+-  void append(StringRef RHS) {
++  /// Append from a std::string_view.
++  void append(std::string_view RHS) {
+     SmallVectorImpl<char>::append(RHS.begin(), RHS.end());
+   }
+ 
+-  /// Append from a list of StringRefs.
+-  void append(std::initializer_list<StringRef> Refs) {
++  /// Append from a list of std::string_views.
++  void append(std::initializer_list<std::string_view> Refs) {
+     size_t CurrentSize = this->size();
+     size_t SizeNeeded = CurrentSize;
+-    for (const StringRef &Ref : Refs)
++    for (const std::string_view &Ref : Refs)
+       SizeNeeded += Ref.size();
+     this->resize_for_overwrite(SizeNeeded);
+-    for (const StringRef &Ref : Refs) {
++    for (const std::string_view &Ref : Refs) {
+       std::copy(Ref.begin(), Ref.end(), this->begin() + CurrentSize);
+       CurrentSize += Ref.size();
+     }
+@@ -89,30 +90,30 @@ public:
+ 
+   /// Check for string equality.  This is more efficient than compare() when
+   /// the relative ordering of inequal strings isn't needed.
+-  bool equals(StringRef RHS) const {
++  bool equals(std::string_view RHS) const {
+     return str().equals(RHS);
+   }
+ 
+   /// Check for string equality, ignoring case.
+-  bool equals_insensitive(StringRef RHS) const {
++  bool equals_insensitive(std::string_view RHS) const {
+     return str().equals_insensitive(RHS);
+   }
+ 
+   /// compare - Compare two strings; the result is negative, zero, or positive
+   /// if this string is lexicographically less than, equal to, or greater than
+   /// the \p RHS.
+-  int compare(StringRef RHS) const {
++  int compare(std::string_view RHS) const {
+     return str().compare(RHS);
+   }
+ 
+   /// compare_insensitive - Compare two strings, ignoring case.
+-  int compare_insensitive(StringRef RHS) const {
++  int compare_insensitive(std::string_view RHS) const {
+     return str().compare_insensitive(RHS);
+   }
+ 
+   /// compare_numeric - Compare two strings, treating sequences of digits as
+   /// numbers.
+-  int compare_numeric(StringRef RHS) const {
++  int compare_numeric(std::string_view RHS) const {
+     return str().compare_numeric(RHS);
+   }
+ 
+@@ -121,12 +122,12 @@ public:
+   /// @{
+ 
+   /// startswith - Check if this string starts with the given \p Prefix.
+-  bool startswith(StringRef Prefix) const {
++  bool startswith(std::string_view Prefix) const {
+     return str().startswith(Prefix);
+   }
+ 
+   /// endswith - Check if this string ends with the given \p Suffix.
+-  bool endswith(StringRef Suffix) const {
++  bool endswith(std::string_view Suffix) const {
+     return str().endswith(Suffix);
+   }
+ 
+@@ -146,7 +147,7 @@ public:
+   ///
+   /// \returns The index of the first occurrence of \p Str, or npos if not
+   /// found.
+-  size_t find(StringRef Str, size_t From = 0) const {
++  size_t find(std::string_view Str, size_t From = 0) const {
+     return str().find(Str, From);
+   }
+ 
+@@ -154,7 +155,7 @@ public:
+   ///
+   /// \returns The index of the last occurrence of \p C, or npos if not
+   /// found.
+-  size_t rfind(char C, size_t From = StringRef::npos) const {
++  size_t rfind(char C, size_t From = std::string_view::npos) const {
+     return str().rfind(C, From);
+   }
+ 
+@@ -162,7 +163,7 @@ public:
+   ///
+   /// \returns The index of the last occurrence of \p Str, or npos if not
+   /// found.
+-  size_t rfind(StringRef Str) const {
++  size_t rfind(std::string_view Str) const {
+     return str().rfind(Str);
+   }
+ 
+@@ -176,7 +177,7 @@ public:
+   /// not found.
+   ///
+   /// Complexity: O(size() + Chars.size())
+-  size_t find_first_of(StringRef Chars, size_t From = 0) const {
++  size_t find_first_of(std::string_view Chars, size_t From = 0) const {
+     return str().find_first_of(Chars, From);
+   }
+ 
+@@ -190,13 +191,13 @@ public:
+   /// \p Chars, or npos if not found.
+   ///
+   /// Complexity: O(size() + Chars.size())
+-  size_t find_first_not_of(StringRef Chars, size_t From = 0) const {
++  size_t find_first_not_of(std::string_view Chars, size_t From = 0) const {
+     return str().find_first_not_of(Chars, From);
+   }
+ 
+   /// Find the last character in the string that is \p C, or npos if not
+   /// found.
+-  size_t find_last_of(char C, size_t From = StringRef::npos) const {
++  size_t find_last_of(char C, size_t From = std::string_view::npos) const {
+     return str().find_last_of(C, From);
+   }
+ 
+@@ -205,7 +206,7 @@ public:
+   ///
+   /// Complexity: O(size() + Chars.size())
+   size_t find_last_of(
+-      StringRef Chars, size_t From = StringRef::npos) const {
++      std::string_view Chars, size_t From = std::string_view::npos) const {
+     return str().find_last_of(Chars, From);
+   }
+ 
+@@ -220,7 +221,7 @@ public:
+ 
+   /// Return the number of non-overlapped occurrences of \p Str in the
+   /// string.
+-  size_t count(StringRef Str) const {
++  size_t count(std::string_view Str) const {
+     return str().count(Str);
+   }
+ 
+@@ -237,7 +238,7 @@ public:
+   /// \param N The number of characters to included in the substring. If \p N
+   /// exceeds the number of characters remaining in the string, the string
+   /// suffix (starting with \p Start) will be returned.
+-  StringRef substr(size_t Start, size_t N = StringRef::npos) const {
++  std::string_view substr(size_t Start, size_t N = std::string_view::npos) const {
+     return str().substr(Start, N);
+   }
+ 
+@@ -251,14 +252,14 @@ public:
+   /// substring. If this is npos, or less than \p Start, or exceeds the
+   /// number of characters remaining in the string, the string suffix
+   /// (starting with \p Start) will be returned.
+-  StringRef slice(size_t Start, size_t End) const {
++  std::string_view slice(size_t Start, size_t End) const {
+     return str().slice(Start, End);
+   }
+ 
+   // Extra methods.
+ 
+-  /// Explicit conversion to StringRef.
+-  StringRef str() const { return StringRef(this->data(), this->size()); }
++  /// Explicit conversion to std::string_view.
++  std::string_view str() const { return std::string_view(this->begin(), this->size()); }
+ 
+   // TODO: Make this const, if it's safe...
+   const char* c_str() {
+@@ -267,20 +268,20 @@ public:
+     return this->data();
+   }
+ 
+-  /// Implicit conversion to StringRef.
+-  operator StringRef() const { return str(); }
++  /// Implicit conversion to std::string_view.
++  operator std::string_view() const { return str(); }
+ 
+   explicit operator std::string() const {
+     return std::string(this->data(), this->size());
+   }
+ 
+   // Extra operators.
+-  SmallString &operator=(StringRef RHS) {
++  SmallString &operator=(std::string_view RHS) {
+     this->assign(RHS);
+     return *this;
+   }
+ 
+-  SmallString &operator+=(StringRef RHS) {
++  SmallString &operator+=(std::string_view RHS) {
+     this->append(RHS.begin(), RHS.end());
+     return *this;
+   }
+diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h
+index 53a107b1574c6a35c66c7fe3c61deb2ffc84b991..4559864ed231206b098936dae4fc378bfa986371 100644
+--- a/llvm/include/llvm/ADT/SmallVector.h
++++ b/llvm/include/llvm/ADT/SmallVector.h
+@@ -27,13 +27,12 @@
+ #include <limits>
+ #include <memory>
+ #include <new>
++#include <span>
+ #include <type_traits>
+ #include <utility>
+ 
+ namespace llvm {
+ 
+-template <typename T> class ArrayRef;
+-
+ template <typename IteratorT> class iterator_range;
+ 
+ template <class Iterator>
+@@ -117,7 +116,7 @@ template <class T, typename = void> struct SmallVectorAlignmentAndSize {
+ };
+ 
+ /// This is the part of SmallVectorTemplateBase which does not depend on whether
+-/// the type T is a POD. The extra dummy template argument is used by ArrayRef
++/// the type T is a POD. The extra dummy template argument is used by span
+ /// to avoid unnecessarily requiring T to be complete.
+ template <typename T, typename = void>
+ class SmallVectorTemplateCommon
+@@ -1233,7 +1232,7 @@ public:
+ 
+   template <typename U,
+             typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+-  explicit SmallVector(ArrayRef<U> A) : SmallVectorImpl<T>(N) {
++  explicit SmallVector(span<const U> A) : SmallVectorImpl<T>(N) {
+     this->append(A.begin(), A.end());
+   }
+ 
+diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h
+index 466f95254d102e98343290b211f317f749d7692b..34dfbf83c681f4e81a9dadd9382ddca6ef8d6c1d 100644
+--- a/llvm/include/llvm/ADT/StringMap.h
++++ b/llvm/include/llvm/ADT/StringMap.h
+@@ -60,12 +60,12 @@ protected:
+   /// 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 LookupBucketFor(StringRef Key);
++  unsigned LookupBucketFor(std::string_view Key);
+ 
+   /// 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 FindKey(StringRef Key) const;
++  int FindKey(std::string_view Key) const;
+ 
+   /// RemoveKey - Remove the specified StringMapEntry from the table, but do not
+   /// delete it.  This aborts if the value isn't in the table.
+@@ -73,7 +73,7 @@ protected:
+ 
+   /// 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 *RemoveKey(StringRef Key);
++  StringMapEntryBase *RemoveKey(std::string_view Key);
+ 
+   /// Allocate the table with the specified number of buckets and otherwise
+   /// setup the map as empty.
+@@ -127,7 +127,7 @@ public:
+       : StringMapImpl(InitialSize, static_cast<unsigned>(sizeof(MapEntryTy))),
+         AllocTy(A) {}
+ 
+-  StringMap(std::initializer_list<std::pair<StringRef, ValueTy>> List)
++  StringMap(std::initializer_list<std::pair<std::string_view, ValueTy>> List)
+       : StringMapImpl(List.size(), static_cast<unsigned>(sizeof(MapEntryTy))) {
+     insert(List);
+   }
+@@ -215,14 +215,14 @@ public:
+                       StringMapKeyIterator<ValueTy>(end()));
+   }
+ 
+-  iterator find(StringRef Key) {
++  iterator find(std::string_view Key) {
+     int Bucket = FindKey(Key);
+     if (Bucket == -1)
+       return end();
+     return iterator(TheTable + Bucket, true);
+   }
+ 
+-  const_iterator find(StringRef Key) const {
++  const_iterator find(std::string_view Key) const {
+     int Bucket = FindKey(Key);
+     if (Bucket == -1)
+       return end();
+@@ -231,7 +231,7 @@ public:
+ 
+   /// lookup - Return the entry for the specified key, or a default
+   /// constructed value if no such entry exists.
+-  ValueTy lookup(StringRef Key) const {
++  ValueTy lookup(std::string_view Key) const {
+     const_iterator Iter = find(Key);
+     if (Iter != end())
+       return Iter->second;
+@@ -240,7 +240,7 @@ public:
+ 
+   /// at - Return the entry for the specified key, or abort if no such
+   /// entry exists.
+-  const ValueTy &at(StringRef Val) const {
++  const ValueTy &at(std::string_view Val) const {
+     auto Iter = this->find(std::move(Val));
+     assert(Iter != this->end() && "StringMap::at failed due to a missing key");
+     return Iter->second;
+@@ -248,13 +248,13 @@ public:
+ 
+   /// Lookup the ValueTy for the \p Key, or create a default constructed value
+   /// if the key is not in the map.
+-  ValueTy &operator[](StringRef Key) { return try_emplace(Key).first->second; }
++  ValueTy &operator[](std::string_view Key) { return try_emplace(Key).first->second; }
+ 
+   /// contains - Return true if the element is in the map, false otherwise.
+-  bool contains(StringRef Key) const { return find(Key) != end(); }
++  bool contains(std::string_view Key) const { return find(Key) != end(); }
+ 
+   /// count - Return 1 if the element is in the map, 0 otherwise.
+-  size_type count(StringRef Key) const { return contains(Key) ? 1 : 0; }
++  size_type count(std::string_view Key) const { return contains(Key) ? 1 : 0; }
+ 
+   template <typename InputTy>
+   size_type count(const StringMapEntry<InputTy> &MapEntry) const {
+@@ -304,7 +304,7 @@ public:
+   /// isn't already in the map. The bool component of the returned pair is true
+   /// if and only if the insertion takes place, and the iterator component of
+   /// the pair points to the element with key equivalent to the key of the pair.
+-  std::pair<iterator, bool> insert(std::pair<StringRef, ValueTy> KV) {
++  std::pair<iterator, bool> insert(std::pair<std::string_view, ValueTy> KV) {
+     return try_emplace(KV.first, std::move(KV.second));
+   }
+ 
+@@ -319,14 +319,14 @@ public:
+   ///  Inserts elements from initializer list ilist. If multiple elements in
+   /// the range have keys that compare equivalent, it is unspecified which
+   /// element is inserted
+-  void insert(std::initializer_list<std::pair<StringRef, ValueTy>> List) {
++  void insert(std::initializer_list<std::pair<std::string_view, ValueTy>> List) {
+     insert(List.begin(), List.end());
+   }
+ 
+   /// Inserts an element or assigns to the current element if the key already
+   /// exists. The return type is the same as try_emplace.
+   template <typename V>
+-  std::pair<iterator, bool> insert_or_assign(StringRef Key, V &&Val) {
++  std::pair<iterator, bool> insert_or_assign(std::string_view Key, V &&Val) {
+     auto Ret = try_emplace(Key, std::forward<V>(Val));
+     if (!Ret.second)
+       Ret.first->second = std::forward<V>(Val);
+@@ -338,7 +338,7 @@ public:
+   /// if and only if the insertion takes place, and the iterator component of
+   /// the pair points to the element with key equivalent to the key of the pair.
+   template <typename... ArgsTy>
+-  std::pair<iterator, bool> try_emplace(StringRef Key, ArgsTy &&...Args) {
++  std::pair<iterator, bool> try_emplace(std::string_view Key, ArgsTy &&...Args) {
+     unsigned BucketNo = LookupBucketFor(Key);
+     StringMapEntryBase *&Bucket = TheTable[BucketNo];
+     if (Bucket && Bucket != getTombstoneVal())
+@@ -385,7 +385,7 @@ public:
+     V.Destroy(getAllocator());
+   }
+ 
+-  bool erase(StringRef Key) {
++  bool erase(std::string_view Key) {
+     iterator I = find(Key);
+     if (I == end())
+       return false;
+@@ -482,17 +482,17 @@ template <typename ValueTy>
+ class StringMapKeyIterator
+     : public iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
+                                    StringMapConstIterator<ValueTy>,
+-                                   std::forward_iterator_tag, StringRef> {
++                                   std::forward_iterator_tag, std::string_view> {
+   using base = iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
+                                      StringMapConstIterator<ValueTy>,
+-                                     std::forward_iterator_tag, StringRef>;
++                                     std::forward_iterator_tag, std::string_view>;
+ 
+ public:
+   StringMapKeyIterator() = default;
+   explicit StringMapKeyIterator(StringMapConstIterator<ValueTy> Iter)
+       : base(std::move(Iter)) {}
+ 
+-  StringRef operator*() const { return this->wrapped()->getKey(); }
++  std::string_view operator*() const { return this->wrapped()->getKey(); }
+ };
+ 
+ } // end namespace llvm
+diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h
+index 98b51cc1aebd59eba20076e6d8a4eebc0eebb982..388e81c361642113937f7d5680de73a50635b07d 100644
+--- a/llvm/include/llvm/ADT/StringMapEntry.h
++++ b/llvm/include/llvm/ADT/StringMapEntry.h
+@@ -16,8 +16,8 @@
+ #ifndef LLVM_ADT_STRINGMAPENTRY_H
+ #define LLVM_ADT_STRINGMAPENTRY_H
+ 
+-#include "llvm/ADT/StringRef.h"
+ #include <optional>
++#include <string_view>
+ 
+ namespace llvm {
+ 
+@@ -36,13 +36,13 @@ protected:
+   /// type-erase the allocator and put it in a source file.
+   template <typename AllocatorTy>
+   static void *allocateWithKey(size_t EntrySize, size_t EntryAlign,
+-                               StringRef Key, AllocatorTy &Allocator);
++                               std::string_view Key, AllocatorTy &Allocator);
+ };
+ 
+ // Define out-of-line to dissuade inlining.
+ template <typename AllocatorTy>
+ void *StringMapEntryBase::allocateWithKey(size_t EntrySize, size_t EntryAlign,
+-                                          StringRef Key,
++                                          std::string_view Key,
+                                           AllocatorTy &Allocator) {
+   size_t KeyLength = Key.size();
+ 
+@@ -105,8 +105,8 @@ public:
+ 
+   using ValueType = ValueTy;
+ 
+-  StringRef getKey() const {
+-    return StringRef(getKeyData(), this->getKeyLength());
++  std::string_view getKey() const {
++    return std::string_view(getKeyData(), this->getKeyLength());
+   }
+ 
+   /// getKeyData - Return the start of the string data that is the key for this
+@@ -116,15 +116,15 @@ public:
+     return reinterpret_cast<const char *>(this + 1);
+   }
+ 
+-  StringRef first() const {
+-    return StringRef(getKeyData(), this->getKeyLength());
++  std::string_view first() const {
++    return std::string_view(getKeyData(), this->getKeyLength());
+   }
+ 
+   /// Create a StringMapEntry for the specified key construct the value using
+   /// \p InitiVals.
+   template <typename AllocatorTy, typename... InitTy>
+-  static StringMapEntry *create(StringRef key, AllocatorTy &allocator,
+-                                InitTy &&...initVals) {
++  static StringMapEntry *create(std::string_view key, AllocatorTy &allocator,
++                                InitTy &&... initVals) {
+     return new (StringMapEntryBase::allocateWithKey(
+         sizeof(StringMapEntry), alignof(StringMapEntry), key, allocator))
+         StringMapEntry(key.size(), std::forward<InitTy>(initVals)...);
+@@ -167,7 +167,7 @@ struct tuple_size<llvm::StringMapEntry<ValueTy>>
+ 
+ template <std::size_t I, typename ValueTy>
+ struct tuple_element<I, llvm::StringMapEntry<ValueTy>>
+-    : std::conditional<I == 0, llvm::StringRef, ValueTy> {};
++    : std::conditional<I == 0, std::string_view, ValueTy> {};
+ } // namespace std
+ 
+ #endif // LLVM_ADT_STRINGMAPENTRY_H
+diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h
+index 9c2bd45d2803e56ed316d8552d899d87f2fbbb07..a7dea19d9193bcff4bc6b553b80a10b2bc7b64af 100644
+--- a/llvm/include/llvm/Support/Chrono.h
++++ b/llvm/include/llvm/Support/Chrono.h
+@@ -70,7 +70,7 @@ raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
+ template <>
+ struct format_provider<sys::TimePoint<>> {
+   static void format(const sys::TimePoint<> &TP, llvm::raw_ostream &OS,
+-                     StringRef Style);
++                     std::string_view Style);
+ };
+ 
+ namespace detail {
+@@ -122,7 +122,7 @@ private:
+     return duration_cast<duration<InternalRep, AsPeriod>>(D).count();
+   }
+ 
+-  static std::pair<InternalRep, StringRef> consumeUnit(StringRef &Style,
++  static std::pair<InternalRep, std::string_view> consumeUnit(std::string_view &Style,
+                                                         const Dur &D) {
+     using namespace std::chrono;
+     if (Style.consume_front("ns"))
+@@ -140,7 +140,7 @@ private:
+     return {D.count(), detail::unit<Period>::value};
+   }
+ 
+-  static bool consumeShowUnit(StringRef &Style) {
++  static bool consumeShowUnit(std::string_view &Style) {
+     if (Style.empty())
+       return true;
+     if (Style.consume_front("-"))
+@@ -152,9 +152,9 @@ private:
+   }
+ 
+ public:
+-  static void format(const Dur &D, llvm::raw_ostream &Stream, StringRef Style) {
++  static void format(const Dur &D, llvm::raw_ostream &Stream, std::string_view Style) {
+     InternalRep count;
+-    StringRef unit;
++    std::string_view unit;
+     std::tie(count, unit) = consumeUnit(Style, D);
+     bool show_unit = consumeShowUnit(Style);
+ 
+diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h
+index 10d5cec231a523c943c37a5464cb3943627239a9..92376629c607461061bc60597a47aed1e535af52 100644
+--- a/llvm/include/llvm/Support/Compiler.h
++++ b/llvm/include/llvm/Support/Compiler.h
+@@ -300,7 +300,7 @@
+ #endif
+ 
+ /// LLVM_GSL_POINTER - Apply this to non-owning classes like
+-/// StringRef to enable lifetime warnings.
++/// std::string_view to enable lifetime warnings.
+ #if LLVM_HAS_CPP_ATTRIBUTE(gsl::Pointer)
+ #define LLVM_GSL_POINTER [[gsl::Pointer]]
+ #else
+diff --git a/llvm/include/llvm/Support/ConvertUTF.h b/llvm/include/llvm/Support/ConvertUTF.h
+index c892bb3c03cb569994429649bdbb96e4118dcef1..5c0e3009c25446a34882fb98329b1d955231bb39 100644
+--- a/llvm/include/llvm/Support/ConvertUTF.h
++++ b/llvm/include/llvm/Support/ConvertUTF.h
+@@ -107,10 +107,9 @@
+ 
+ #include <cstddef>
+ #include <string>
+-
+-#if defined(_WIN32)
++#include <span>
++#include <string_view>
+ #include <system_error>
+-#endif
+ 
+ // Wrap everything in namespace llvm so that programs can link with llvm and
+ // their own version of the unicode libraries.
+@@ -204,12 +203,10 @@ unsigned getNumBytesForUTF8(UTF8 firstByte);
+ /*************************************************************************/
+ /* Below are LLVM-specific wrappers of the functions above. */
+ 
+-template <typename T> class ArrayRef;
+ template <typename T> class SmallVectorImpl;
+-class StringRef;
+ 
+ /**
+- * Convert an UTF8 StringRef to UTF8, UTF16, or UTF32 depending on
++ * Convert an UTF8 string_view to UTF8, UTF16, or UTF32 depending on
+  * WideCharWidth. The converted data is written to ResultPtr, which needs to
+  * point to at least WideCharWidth * (Source.Size() + 1) bytes. On success,
+  * ResultPtr will point one after the end of the copied string. On failure,
+@@ -217,14 +214,14 @@ class StringRef;
+  * the first character which could not be converted.
+  * \return true on success.
+  */
+-bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source,
++bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source,
+                        char *&ResultPtr, const UTF8 *&ErrorPtr);
+ 
+ /**
+-* Converts a UTF-8 StringRef to a std::wstring.
++* Converts a UTF-8 string_view to a std::wstring.
+ * \return true on success.
+ */
+-bool ConvertUTF8toWide(llvm::StringRef Source, std::wstring &Result);
++bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result);
+ 
+ /**
+ * Converts a UTF-8 C-string to a std::wstring.
+@@ -282,7 +279,7 @@ inline ConversionResult convertUTF8Sequence(const UTF8 **source,
+  * Returns true if a blob of text starts with a UTF-16 big or little endian byte
+  * order mark.
+  */
+-bool hasUTF16ByteOrderMark(ArrayRef<char> SrcBytes);
++bool hasUTF16ByteOrderMark(span<const char> SrcBytes);
+ 
+ /**
+  * Converts a stream of raw bytes assumed to be UTF16 into a UTF8 std::string.
+@@ -291,7 +288,7 @@ bool hasUTF16ByteOrderMark(ArrayRef<char> SrcBytes);
+  * \param [out] Out Converted UTF-8 is stored here on success.
+  * \returns true on success
+  */
+-bool convertUTF16ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out);
++bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out);
+ 
+ /**
+ * Converts a UTF16 string into a UTF8 std::string.
+@@ -300,7 +297,7 @@ bool convertUTF16ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out);
+ * \param [out] Out Converted UTF-8 is stored here on success.
+ * \returns true on success
+ */
+-bool convertUTF16ToUTF8String(ArrayRef<UTF16> Src, std::string &Out);
++bool convertUTF16ToUTF8String(span<const UTF16> Src, std::string &Out);
+ 
+ /**
+  * Converts a stream of raw bytes assumed to be UTF32 into a UTF8 std::string.
+@@ -309,7 +306,7 @@ bool convertUTF16ToUTF8String(ArrayRef<UTF16> Src, std::string &Out);
+  * \param [out] Out Converted UTF-8 is stored here on success.
+  * \returns true on success
+  */
+-bool convertUTF32ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out);
++bool convertUTF32ToUTF8String(span<const char> SrcBytes, std::string &Out);
+ 
+ /**
+  * Converts a UTF32 string into a UTF8 std::string.
+@@ -318,22 +315,22 @@ bool convertUTF32ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out);
+  * \param [out] Out Converted UTF-8 is stored here on success.
+  * \returns true on success
+  */
+-bool convertUTF32ToUTF8String(ArrayRef<UTF32> Src, std::string &Out);
++bool convertUTF32ToUTF8String(span<const UTF32> Src, std::string &Out);
+ 
+ /**
+  * Converts a UTF-8 string into a UTF-16 string with native endianness.
+  *
+  * \returns true on success
+  */
+-bool convertUTF8ToUTF16String(StringRef SrcUTF8,
++bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
+                               SmallVectorImpl<UTF16> &DstUTF16);
+ 
+ #if defined(_WIN32)
+ namespace sys {
+ namespace windows {
+-std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
++std::error_code UTF8ToUTF16(std::string_view utf8, SmallVectorImpl<wchar_t> &utf16);
+ /// Convert to UTF16 from the current code page used in the system
+-std::error_code CurCPToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
++std::error_code CurCPToUTF16(std::string_view utf8, SmallVectorImpl<wchar_t> &utf16);
+ std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
+                             SmallVectorImpl<char> &utf8);
+ /// Convert from UTF16 to the current code page used in the system
+diff --git a/llvm/include/llvm/Support/ErrorHandling.h b/llvm/include/llvm/Support/ErrorHandling.h
+index 9c8e3448f3a03e3540adb8b9dd730c77dd9b20ba..68c27a8c67c4f378b92cfa726659ef7824b56dea 100644
+--- a/llvm/include/llvm/Support/ErrorHandling.h
++++ b/llvm/include/llvm/Support/ErrorHandling.h
+@@ -15,10 +15,10 @@
+ #define LLVM_SUPPORT_ERRORHANDLING_H
+ 
+ #include "llvm/Support/Compiler.h"
++#include <string>
++#include <string_view>
+ 
+ namespace llvm {
+-  class StringRef;
+-  class Twine;
+ 
+   /// An error handler callback.
+   typedef void (*fatal_error_handler_t)(void *user_data,
+@@ -67,12 +67,11 @@ namespace llvm {
+ /// standard error, followed by a newline.
+ /// After the error handler is called this function will call abort(), it
+ /// does not return.
+-/// NOTE: The std::string variant was removed to avoid a <string> dependency.
+ [[noreturn]] void report_fatal_error(const char *reason,
+                                      bool gen_crash_diag = true);
+-[[noreturn]] void report_fatal_error(StringRef reason,
++[[noreturn]] void report_fatal_error(const std::string &reason,
+                                      bool gen_crash_diag = true);
+-[[noreturn]] void report_fatal_error(const Twine &reason,
++[[noreturn]] void report_fatal_error(std::string_view reason,
+                                      bool gen_crash_diag = true);
+ 
+ /// Installs a new bad alloc error handler that should be used whenever a
+diff --git a/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h b/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h
+index f7f2d4e54e705d6f29812dc93d1fb0a3ca2dee12..b5e321b5f74ce35940649b9d1342b3cdf0c4931f 100644
+--- a/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h
++++ b/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h
+@@ -35,8 +35,8 @@ public:
+                                 RequiresNullTerminator) {}
+ 
+   /// Construct a named SmallVectorMemoryBuffer from the given SmallVector
+-  /// r-value and StringRef.
+-  SmallVectorMemoryBuffer(SmallVectorImpl<char> &&SV, StringRef Name,
++  /// r-value and std::string_view.
++  SmallVectorMemoryBuffer(SmallVectorImpl<char> &&SV, std::string_view Name,
+                           bool RequiresNullTerminator = true)
+       : SV(std::move(SV)), BufferName(std::string(Name)) {
+     if (RequiresNullTerminator) {
+@@ -49,7 +49,7 @@ public:
+   // Key function.
+   ~SmallVectorMemoryBuffer() override;
+ 
+-  StringRef getBufferIdentifier() const override { return BufferName; }
++  std::string_view getBufferIdentifier() const override { return BufferName; }
+ 
+   BufferKind getBufferKind() const override { return MemoryBuffer_Malloc; }
+ 
+diff --git a/llvm/include/llvm/Support/VersionTuple.h b/llvm/include/llvm/Support/VersionTuple.h
+index 828a6db54708dfa9a1a4b4456a92945a92ad80cb..953b40701dc934c1a356b5413c9c6c692d5f5679 100644
+--- a/llvm/include/llvm/Support/VersionTuple.h
++++ b/llvm/include/llvm/Support/VersionTuple.h
+@@ -25,7 +25,6 @@ namespace llvm {
+ template <typename HasherT, support::endianness Endianness>
+ class HashBuilderImpl;
+ class raw_ostream;
+-class StringRef;
+ 
+ /// Represents a version number in the form major[.minor[.subminor[.build]]].
+ class VersionTuple {
+@@ -182,11 +181,6 @@ public:
+ 
+   /// Retrieve a string representation of the version number.
+   std::string getAsString() const;
+-
+-  /// Try to parse the given string as a version number.
+-  /// \returns \c true if the string does not match the regular expression
+-  ///   [0-9]+(\.[0-9]+){0,3}
+-  bool tryParse(StringRef string);
+ };
+ 
+ /// Print a version number.
+diff --git a/llvm/include/llvm/Support/Windows/WindowsSupport.h b/llvm/include/llvm/Support/Windows/WindowsSupport.h
+index d3aacd14b2097b1e7e13c1003987c1fd52e0cf76..aabdb2f14668a990329b57f5454a0d7db73e12ce 100644
+--- a/llvm/include/llvm/Support/Windows/WindowsSupport.h
++++ b/llvm/include/llvm/Support/Windows/WindowsSupport.h
+@@ -35,8 +35,6 @@
+ 
+ #include "llvm/ADT/SmallVector.h"
+ #include "llvm/ADT/StringExtras.h"
+-#include "llvm/ADT/StringRef.h"
+-#include "llvm/ADT/Twine.h"
+ #include "llvm/Config/llvm-config.h" // Get build system configuration settings
+ #include "llvm/Support/Allocator.h"
+ #include "llvm/Support/Chrono.h"
+@@ -74,7 +72,7 @@ bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix);
+ [[noreturn]] inline void ReportLastErrorFatal(const char *Msg) {
+   std::string ErrMsg;
+   MakeErrMsg(&ErrMsg, Msg);
+-  llvm::report_fatal_error(Twine(ErrMsg));
++  llvm::report_fatal_error(ErrMsg);
+ }
+ 
+ template <typename HandleTraits>
+diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
+index 1e01eb9ea19c4187302a91457b6d34fbe5b67584..2463f1af612a78cafafe3c0e16d496e607cdc322 100644
+--- a/llvm/include/llvm/Support/raw_ostream.h
++++ b/llvm/include/llvm/Support/raw_ostream.h
+@@ -14,13 +14,12 @@
+ #define LLVM_SUPPORT_RAW_OSTREAM_H
+ 
+ #include "llvm/ADT/SmallVector.h"
+-#include "llvm/ADT/StringRef.h"
+-#include "llvm/Support/DataTypes.h"
+ #include <cassert>
+ #include <cstddef>
+ #include <cstdint>
+ #include <cstring>
+ #include <optional>
++#include <span>
+ #include <string>
+ #include <string_view>
+ #include <system_error>
+@@ -208,7 +207,22 @@ public:
+     return *this;
+   }
+ 
+-  raw_ostream &operator<<(StringRef Str) {
++  raw_ostream &operator<<(span<const uint8_t> Arr) {
++    // Inline fast path, particularly for arrays with a known length.
++    size_t Size = Arr.size();
++
++    // Make sure we can use the fast path.
++    if (Size > (size_t)(OutBufEnd - OutBufCur))
++      return write(Arr.data(), Size);
++
++    if (Size) {
++      memcpy(OutBufCur, Arr.data(), Size);
++      OutBufCur += Size;
++    }
++    return *this;
++  }
++
++  raw_ostream &operator<<(std::string_view Str) {
+     // Inline fast path, particularly for strings with a known length.
+     size_t Size = Str.size();
+ 
+@@ -241,7 +255,7 @@ public:
+     // Inline fast path, particularly for constant strings where a sufficiently
+     // smart compiler will simplify strlen.
+ 
+-    return this->operator<<(StringRef(Str));
++    return this->operator<<(std::string_view(Str));
+   }
+ 
+   raw_ostream &operator<<(const std::string &Str) {
+@@ -249,10 +263,6 @@ public:
+     return write(Str.data(), Str.length());
+   }
+ 
+-  raw_ostream &operator<<(const std::string_view &Str) {
+-    return write(Str.data(), Str.length());
+-  }
+-
+   raw_ostream &operator<<(const SmallVectorImpl<char> &Str) {
+     return write(Str.data(), Str.size());
+   }
+@@ -285,7 +295,7 @@ public:
+ 
+   /// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't
+   /// satisfy llvm::isPrint into an escape sequence.
+-  raw_ostream &write_escaped(StringRef Str, bool UseHexEscapes = false);
++  raw_ostream &write_escaped(std::string_view Str, bool UseHexEscapes = false);
+ 
+   raw_ostream &write(unsigned char C);
+   raw_ostream &write(const char *Ptr, size_t Size);
+@@ -501,14 +511,14 @@ public:
+   /// As a special case, if Filename is "-", then the stream will use
+   /// STDOUT_FILENO instead of opening a file. This will not close the stdout
+   /// descriptor.
+-  raw_fd_ostream(StringRef Filename, std::error_code &EC);
+-  raw_fd_ostream(StringRef Filename, std::error_code &EC,
++  raw_fd_ostream(std::string_view Filename, std::error_code &EC);
++  raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+                  sys::fs::CreationDisposition Disp);
+-  raw_fd_ostream(StringRef Filename, std::error_code &EC,
++  raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+                  sys::fs::FileAccess Access);
+-  raw_fd_ostream(StringRef Filename, std::error_code &EC,
++  raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+                  sys::fs::OpenFlags Flags);
+-  raw_fd_ostream(StringRef Filename, std::error_code &EC,
++  raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+                  sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
+                  sys::fs::OpenFlags Flags);
+ 
+@@ -613,7 +623,7 @@ public:
+   /// Open the specified file for reading/writing/seeking. If an error occurs,
+   /// information about the error is put into EC, and the stream should be
+   /// immediately destroyed.
+-  raw_fd_stream(StringRef Filename, std::error_code &EC);
++  raw_fd_stream(std::string_view Filename, std::error_code &EC);
+ 
+   /// This reads the \p Size bytes into a buffer pointed by \p Ptr.
+   ///
+@@ -693,8 +703,8 @@ public:
+ 
+   void flush() = delete;
+ 
+-  /// Return a StringRef for the vector contents.
+-  StringRef str() const { return StringRef(OS.data(), OS.size()); }
++  /// Return a std::string_view for the vector contents.
++  std::string_view str() const { return std::string_view(OS.data(), OS.size()); }
+ 
+   void reserveExtraSpace(uint64_t ExtraSize) override {
+     OS.reserve(tell() + ExtraSize);
+@@ -751,7 +761,7 @@ class Error;
+ /// for other names. For raw_fd_ostream instances, the stream writes to
+ /// a temporary file. The final output file is atomically replaced with the
+ /// temporary file after the \p Write function is finished.
+-Error writeToOutput(StringRef OutputFileName,
++Error writeToOutput(std::string_view OutputFileName,
+                     std::function<Error(raw_ostream &)> Write);
+ 
+ raw_ostream &operator<<(raw_ostream &OS, std::nullopt_t);
+diff --git a/llvm/include/llvm/Support/xxhash.h b/llvm/include/llvm/Support/xxhash.h
+index 0cef3a54e50d70177a7401324f7a4daca83c6599..3e19ebabb7ad0ff437220d9fdfe59a313386762a 100644
+--- a/llvm/include/llvm/Support/xxhash.h
++++ b/llvm/include/llvm/Support/xxhash.h
+@@ -38,16 +38,18 @@
+ #ifndef LLVM_SUPPORT_XXHASH_H
+ #define LLVM_SUPPORT_XXHASH_H
+ 
+-#include "llvm/ADT/ArrayRef.h"
+-#include "llvm/ADT/StringRef.h"
++#include <stdint.h>
++
++#include <span>
++#include <string_view>
+ 
+ namespace llvm {
+-uint64_t xxHash64(llvm::StringRef Data);
+-uint64_t xxHash64(llvm::ArrayRef<uint8_t> Data);
++uint64_t xxHash64(std::string_view Data);
++uint64_t xxHash64(span<const uint8_t> Data);
+ 
+-uint64_t xxh3_64bits(ArrayRef<uint8_t> data);
+-inline uint64_t xxh3_64bits(StringRef data) {
+-  return xxh3_64bits(ArrayRef(data.bytes_begin(), data.size()));
++uint64_t xxh3_64bits(span<const uint8_t> data);
++inline uint64_t xxh3_64bits(std::string_view data) {
++  return xxh3_64bits(span(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
+ }
+ }
+ 
+diff --git a/llvm/lib/Support/ConvertUTFWrapper.cpp b/llvm/lib/Support/ConvertUTFWrapper.cpp
+index 3fa7365e72d34a5db941d1cbe2b1beebad5c10e6..d53462e742e61d3476915d5b2c5aa63772e78a8a 100644
+--- a/llvm/lib/Support/ConvertUTFWrapper.cpp
++++ b/llvm/lib/Support/ConvertUTFWrapper.cpp
+@@ -6,24 +6,24 @@
+ //
+ //===----------------------------------------------------------------------===//
+ 
+-#include "llvm/ADT/ArrayRef.h"
+-#include "llvm/ADT/StringRef.h"
+ #include "llvm/Support/ConvertUTF.h"
+ #include "llvm/Support/ErrorHandling.h"
+ #include "llvm/Support/SwapByteOrder.h"
++#include <span>
+ #include <string>
++#include <string_view>
+ #include <vector>
+ 
+ namespace llvm {
+ 
+-bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source,
++bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source,
+                        char *&ResultPtr, const UTF8 *&ErrorPtr) {
+   assert(WideCharWidth == 1 || WideCharWidth == 2 || WideCharWidth == 4);
+   ConversionResult result = conversionOK;
+   // Copy the character span over.
+   if (WideCharWidth == 1) {
+-    const UTF8 *Pos = reinterpret_cast<const UTF8*>(Source.begin());
+-    if (!isLegalUTF8String(&Pos, reinterpret_cast<const UTF8*>(Source.end()))) {
++    const UTF8 *Pos = reinterpret_cast<const UTF8*>(Source.data());
++    if (!isLegalUTF8String(&Pos, reinterpret_cast<const UTF8*>(Source.data() + Source.size()))) {
+       result = sourceIllegal;
+       ErrorPtr = Pos;
+     } else {
+@@ -76,12 +76,12 @@ bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr) {
+   return true;
+ }
+ 
+-bool hasUTF16ByteOrderMark(ArrayRef<char> S) {
++bool hasUTF16ByteOrderMark(span<const char> S) {
+   return (S.size() >= 2 && ((S[0] == '\xff' && S[1] == '\xfe') ||
+                             (S[0] == '\xfe' && S[1] == '\xff')));
+ }
+ 
+-bool convertUTF16ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out) {
++bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out) {
+   assert(Out.empty());
+ 
+   // Error out on an uneven byte count.
+@@ -132,14 +132,14 @@ bool convertUTF16ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out) {
+   return true;
+ }
+ 
+-bool convertUTF16ToUTF8String(ArrayRef<UTF16> Src, std::string &Out) {
++bool convertUTF16ToUTF8String(span<const UTF16> Src, std::string &Out) {
+   return convertUTF16ToUTF8String(
+-      llvm::ArrayRef<char>(reinterpret_cast<const char *>(Src.data()),
++      span<const char>(reinterpret_cast<const char *>(Src.data()),
+                            Src.size() * sizeof(UTF16)),
+       Out);
+ }
+ 
+-bool convertUTF32ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out) {
++bool convertUTF32ToUTF8String(span<const char> SrcBytes, std::string &Out) {
+   assert(Out.empty());
+ 
+   // Error out on an uneven byte count.
+@@ -190,14 +190,14 @@ bool convertUTF32ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out) {
+   return true;
+ }
+ 
+-bool convertUTF32ToUTF8String(ArrayRef<UTF32> Src, std::string &Out) {
++bool convertUTF32ToUTF8String(span<const UTF32> Src, std::string &Out) {
+   return convertUTF32ToUTF8String(
+-      llvm::ArrayRef<char>(reinterpret_cast<const char *>(Src.data()),
++      span<const char>(reinterpret_cast<const char *>(Src.data()),
+                            Src.size() * sizeof(UTF32)),
+       Out);
+ }
+ 
+-bool convertUTF8ToUTF16String(StringRef SrcUTF8,
++bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
+                               SmallVectorImpl<UTF16> &DstUTF16) {
+   assert(DstUTF16.empty());
+ 
+@@ -208,8 +208,8 @@ bool convertUTF8ToUTF16String(StringRef SrcUTF8,
+     return true;
+   }
+ 
+-  const UTF8 *Src = reinterpret_cast<const UTF8 *>(SrcUTF8.begin());
+-  const UTF8 *SrcEnd = reinterpret_cast<const UTF8 *>(SrcUTF8.end());
++  const UTF8 *Src = reinterpret_cast<const UTF8 *>(SrcUTF8.data());
++  const UTF8 *SrcEnd = reinterpret_cast<const UTF8 *>(SrcUTF8.data() + SrcUTF8.size());
+ 
+   // 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
+@@ -240,7 +240,7 @@ static_assert(sizeof(wchar_t) == 1 || sizeof(wchar_t) == 2 ||
+               "Expected wchar_t to be 1, 2, or 4 bytes");
+ 
+ template <typename TResult>
+-static inline bool ConvertUTF8toWideInternal(llvm::StringRef Source,
++static inline bool ConvertUTF8toWideInternal(std::string_view Source,
+                                              TResult &Result) {
+   // Even in the case of UTF-16, the number of bytes in a UTF-8 string is
+   // at least as large as the number of elements in the resulting wide
+@@ -256,7 +256,7 @@ static inline bool ConvertUTF8toWideInternal(llvm::StringRef Source,
+   return true;
+ }
+ 
+-bool ConvertUTF8toWide(llvm::StringRef Source, std::wstring &Result) {
++bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result) {
+   return ConvertUTF8toWideInternal(Source, Result);
+ }
+ 
+@@ -265,7 +265,7 @@ bool ConvertUTF8toWide(const char *Source, std::wstring &Result) {
+     Result.clear();
+     return true;
+   }
+-  return ConvertUTF8toWide(llvm::StringRef(Source), Result);
++  return ConvertUTF8toWide(std::string_view(Source), Result);
+ }
+ 
+ bool convertWideToUTF8(const std::wstring &Source, std::string &Result) {
+@@ -280,7 +280,7 @@ bool convertWideToUTF8(const std::wstring &Source, std::string &Result) {
+     return true;
+   } else if (sizeof(wchar_t) == 2) {
+     return convertUTF16ToUTF8String(
+-        llvm::ArrayRef<UTF16>(reinterpret_cast<const UTF16 *>(Source.data()),
++        span<const UTF16>(reinterpret_cast<const UTF16 *>(Source.data()),
+                               Source.size()),
+         Result);
+   } else if (sizeof(wchar_t) == 4) {
+diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
+index b8b3b7424ac6b1de782e739782f9671194ce77a1..0aa13a0f78eb370b2a673ca4a773f26820575052 100644
+--- a/llvm/lib/Support/ErrorHandling.cpp
++++ b/llvm/lib/Support/ErrorHandling.cpp
+@@ -14,7 +14,6 @@
+ #include "llvm/Support/ErrorHandling.h"
+ #include "llvm-c/ErrorHandling.h"
+ #include "llvm/ADT/SmallVector.h"
+-#include "llvm/ADT/Twine.h"
+ #include "llvm/Config/config.h"
+ #include "llvm/Support/Debug.h"
+ #include "llvm/Support/Errc.h"
+@@ -80,14 +79,14 @@ void llvm::remove_fatal_error_handler() {
+ }
+ 
+ void llvm::report_fatal_error(const char *Reason, bool GenCrashDiag) {
+-  report_fatal_error(Twine(Reason), GenCrashDiag);
++  report_fatal_error(std::string_view(Reason), GenCrashDiag);
+ }
+ 
+-void llvm::report_fatal_error(StringRef Reason, bool GenCrashDiag) {
+-  report_fatal_error(Twine(Reason), GenCrashDiag);
++void llvm::report_fatal_error(const std::string &Reason, bool GenCrashDiag) {
++  report_fatal_error(std::string_view(Reason), GenCrashDiag);
+ }
+ 
+-void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
++void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
+   llvm::fatal_error_handler_t handler = nullptr;
+   void* handlerData = nullptr;
+   {
+@@ -101,7 +100,7 @@ void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
+   }
+ 
+   if (handler) {
+-    handler(handlerData, Reason.str().c_str(), GenCrashDiag);
++    handler(handlerData, std::string{Reason}.c_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
+@@ -109,7 +108,7 @@ void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
+     SmallVector<char, 64> Buffer;
+     raw_svector_ostream OS(Buffer);
+     OS << "LLVM ERROR: " << Reason << "\n";
+-    StringRef MessageStr = OS.str();
++    std::string_view MessageStr = OS.str();
+     ssize_t written = ::write(2, MessageStr.data(), MessageStr.size());
+     (void)written; // If something went wrong, we deliberately just give up.
+   }
+diff --git a/llvm/lib/Support/SmallVector.cpp b/llvm/lib/Support/SmallVector.cpp
+index f7e7e80332cc337f6dfa388d1e218e6f3ec95cf2..6cefdff7c28060ca18b522acf5279af3a206e23a 100644
+--- a/llvm/lib/Support/SmallVector.cpp
++++ b/llvm/lib/Support/SmallVector.cpp
+@@ -11,7 +11,6 @@
+ //===----------------------------------------------------------------------===//
+ 
+ #include "llvm/ADT/SmallVector.h"
+-#include "llvm/ADT/Twine.h"
+ #include "llvm/Support/MemAlloc.h"
+ #include <cstdint>
+ #ifdef LLVM_ENABLE_EXCEPTIONS
+@@ -67,7 +66,7 @@ static void report_size_overflow(size_t MinSize, size_t MaxSize) {
+ #ifdef LLVM_ENABLE_EXCEPTIONS
+   throw std::length_error(Reason);
+ #else
+-  report_fatal_error(Twine(Reason));
++  report_fatal_error(Reason);
+ #endif
+ }
+ 
+@@ -81,7 +80,7 @@ static void report_at_maximum_capacity(size_t MaxSize) {
+ #ifdef LLVM_ENABLE_EXCEPTIONS
+   throw std::length_error(Reason);
+ #else
+-  report_fatal_error(Twine(Reason));
++  report_fatal_error(Reason);
+ #endif
+ }
+ 
+diff --git a/llvm/lib/Support/StringMap.cpp b/llvm/lib/Support/StringMap.cpp
+index 67c05a87959cf0c243d17646ae2f28f6c9f0d708..7be219323f6d76f32a9a841115f2f146141cdbab 100644
+--- a/llvm/lib/Support/StringMap.cpp
++++ b/llvm/lib/Support/StringMap.cpp
+@@ -81,7 +81,7 @@ void StringMapImpl::init(unsigned InitSize) {
+ /// 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 StringMapImpl::LookupBucketFor(std::string_view Name) {
+   // Hash table unallocated so far?
+   if (NumBuckets == 0)
+     init(16);
+@@ -121,7 +121,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
+       // Do the comparison like this because Name isn't necessarily
+       // null-terminated!
+       char *ItemStr = (char *)BucketItem + ItemSize;
+-      if (Name == StringRef(ItemStr, BucketItem->getKeyLength())) {
++      if (Name == std::string_view(ItemStr, BucketItem->getKeyLength())) {
+         // We found a match!
+         return BucketNo;
+       }
+@@ -139,7 +139,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
+ /// 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 {
++int StringMapImpl::FindKey(std::string_view Key) const {
+   if (NumBuckets == 0)
+     return -1; // Really empty table?
+   unsigned FullHashValue = xxh3_64bits(Key);
+@@ -166,7 +166,7 @@ int StringMapImpl::FindKey(StringRef Key) const {
+       // Do the comparison like this because NameStart isn't necessarily
+       // null-terminated!
+       char *ItemStr = (char *)BucketItem + ItemSize;
+-      if (Key == StringRef(ItemStr, BucketItem->getKeyLength())) {
++      if (Key == std::string_view(ItemStr, BucketItem->getKeyLength())) {
+         // We found a match!
+         return BucketNo;
+       }
+@@ -185,14 +185,14 @@ int StringMapImpl::FindKey(StringRef Key) const {
+ /// 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()));
++  StringMapEntryBase *V2 = RemoveKey(std::string_view(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) {
++StringMapEntryBase *StringMapImpl::RemoveKey(std::string_view Key) {
+   int Bucket = FindKey(Key);
+   if (Bucket == -1)
+     return nullptr;
+diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
+index a4fc605019c211f93dde009e89e7a79b07400aa3..9966a0056ae4f24a7a38346ee1c2f5d83ac20248 100644
+--- a/llvm/lib/Support/raw_ostream.cpp
++++ b/llvm/lib/Support/raw_ostream.cpp
+@@ -166,7 +166,7 @@ raw_ostream &raw_ostream::write_uuid(const uuid_t UUID) {
+ }
+ 
+ 
+-raw_ostream &raw_ostream::write_escaped(StringRef Str,
++raw_ostream &raw_ostream::write_escaped(std::string_view Str,
+                                         bool UseHexEscapes) {
+   for (unsigned char c : Str) {
+     switch (c) {
+@@ -569,7 +569,7 @@ void format_object_base::home() {
+ //  raw_fd_ostream
+ //===----------------------------------------------------------------------===//
+ 
+-static int getFD(StringRef Filename, std::error_code &EC,
++static int getFD(std::string_view Filename, std::error_code &EC,
+                  sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
+                  sys::fs::OpenFlags Flags) {
+   assert((Access & sys::fs::FA_Write) &&
+@@ -595,25 +595,25 @@ static int getFD(StringRef Filename, std::error_code &EC,
+   return FD;
+ }
+ 
+-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC)
++raw_fd_ostream::raw_fd_ostream(std::string_view 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,
++raw_fd_ostream::raw_fd_ostream(std::string_view 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,
++raw_fd_ostream::raw_fd_ostream(std::string_view 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,
++raw_fd_ostream::raw_fd_ostream(std::string_view 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,
++raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+                                sys::fs::CreationDisposition Disp,
+                                sys::fs::FileAccess Access,
+                                sys::fs::OpenFlags Flags)
+@@ -685,8 +685,7 @@ raw_fd_ostream::~raw_fd_ostream() {
+   // 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(Twine("IO failure on output stream: ") +
+-                           error().message(),
++    report_fatal_error("IO failure on output stream: " + error().message(),
+                        /*gen_crash_diag=*/false);
+ }
+ 
+@@ -705,7 +704,7 @@ raw_fd_ostream::~raw_fd_ostream() {
+ // 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) {
++static bool write_console_impl(int FD, std::string_view Data) {
+   SmallVector<wchar_t, 256> WideText;
+ 
+   // Fall back to ::write if it wasn't valid UTF-8.
+@@ -748,7 +747,7 @@ void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
+   // 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)))
++    if (write_console_impl(FD, std::string_view(Ptr, Size)))
+       return;
+ #endif
+ 
+@@ -919,7 +918,7 @@ raw_ostream &llvm::nulls() {
+ // File Streams
+ //===----------------------------------------------------------------------===//
+ 
+-raw_fd_stream::raw_fd_stream(StringRef Filename, std::error_code &EC)
++raw_fd_stream::raw_fd_stream(std::string_view Filename, std::error_code &EC)
+     : raw_fd_ostream(getFD(Filename, EC, sys::fs::CD_CreateAlways,
+                            sys::fs::FA_Write | sys::fs::FA_Read,
+                            sys::fs::OF_None),
+@@ -997,7 +996,7 @@ void buffer_ostream::anchor() {}
+ 
+ void buffer_unique_ostream::anchor() {}
+ 
+-Error llvm::writeToOutput(StringRef OutputFileName,
++Error llvm::writeToOutput(std::string_view OutputFileName,
+                           std::function<Error(raw_ostream &)> Write) {
+   if (OutputFileName == "-")
+     return Write(outs());
+diff --git a/llvm/lib/Support/xxhash.cpp b/llvm/lib/Support/xxhash.cpp
+index 577f14189caff7d74377f7b28d8332deef4c62c4..b9c15e885a1751eaca43317323bd7a85fa201073 100644
+--- a/llvm/lib/Support/xxhash.cpp
++++ b/llvm/lib/Support/xxhash.cpp
+@@ -84,11 +84,11 @@ static uint64_t XXH64_avalanche(uint64_t hash) {
+   return hash;
+ }
+ 
+-uint64_t llvm::xxHash64(StringRef Data) {
++uint64_t llvm::xxHash64(std::string_view Data) {
+   size_t Len = Data.size();
+   uint64_t Seed = 0;
+-  const unsigned char *P = Data.bytes_begin();
+-  const unsigned char *const BEnd = Data.bytes_end();
++  const unsigned char *P = reinterpret_cast<const unsigned char*>(Data.data());
++  const unsigned char *const BEnd = P + Data.size();
+   uint64_t H64;
+ 
+   if (Len >= 32) {
+@@ -144,7 +144,7 @@ uint64_t llvm::xxHash64(StringRef Data) {
+   return XXH64_avalanche(H64);
+ }
+ 
+-uint64_t llvm::xxHash64(ArrayRef<uint8_t> Data) {
++uint64_t llvm::xxHash64(span<const uint8_t> Data) {
+   return xxHash64({(const char *)Data.data(), Data.size()});
+ }
+ 
+@@ -394,7 +394,7 @@ static uint64_t XXH3_hashLong_64b(const uint8_t *input, size_t len,
+                         (uint64_t)len * PRIME64_1);
+ }
+ 
+-uint64_t llvm::xxh3_64bits(ArrayRef<uint8_t> data) {
++uint64_t llvm::xxh3_64bits(span<const uint8_t> data) {
+   auto *in = data.data();
+   size_t len = data.size();
+   if (len <= 16)
+diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
+index cc3244528f27e2bd7eaa385d8b7f49b2fbb7a3e6..b710ac07461ba58faa99cedeae7f209dc0f5902b 100644
+--- a/llvm/unittests/ADT/DenseMapTest.cpp
++++ b/llvm/unittests/ADT/DenseMapTest.cpp
+@@ -9,11 +9,11 @@
+ #include "llvm/ADT/DenseMap.h"
+ #include "llvm/ADT/DenseMapInfo.h"
+ #include "llvm/ADT/DenseMapInfoVariant.h"
+-#include "llvm/ADT/StringRef.h"
+ #include "gmock/gmock.h"
+ #include "gtest/gtest.h"
+ #include <map>
+ #include <set>
++#include <string_view>
+ #include <utility>
+ #include <variant>
+ 
+@@ -499,31 +499,6 @@ TEST(DenseMapCustomTest, ReserveTest) {
+   }
+ }
+ 
+-// Make sure DenseMap works with StringRef keys.
+-TEST(DenseMapCustomTest, StringRefTest) {
+-  DenseMap<StringRef, int> M;
+-
+-  M["a"] = 1;
+-  M["b"] = 2;
+-  M["c"] = 3;
+-
+-  EXPECT_EQ(3u, M.size());
+-  EXPECT_EQ(1, M.lookup("a"));
+-  EXPECT_EQ(2, M.lookup("b"));
+-  EXPECT_EQ(3, M.lookup("c"));
+-
+-  EXPECT_EQ(0, M.lookup("q"));
+-
+-  // Test the empty string, spelled various ways.
+-  EXPECT_EQ(0, M.lookup(""));
+-  EXPECT_EQ(0, M.lookup(StringRef()));
+-  EXPECT_EQ(0, M.lookup(StringRef("a", 0)));
+-  M[""] = 42;
+-  EXPECT_EQ(42, M.lookup(""));
+-  EXPECT_EQ(42, M.lookup(StringRef()));
+-  EXPECT_EQ(42, M.lookup(StringRef("a", 0)));
+-}
+-
+ // Key traits that allows lookup with either an unsigned or char* key;
+ // In the latter case, "a" == 0, "b" == 1 and so on.
+ struct TestDenseMapInfo {
+@@ -761,7 +736,7 @@ TEST(DenseMapCustomTest, VariantSupport) {
+ // Test that gTest prints map entries as pairs instead of opaque objects.
+ // See third-party/unittest/googletest/internal/custom/gtest-printers.h
+ TEST(DenseMapCustomTest, PairPrinting) {
+-  DenseMap<int, StringRef> Map = {{1, "one"}, {2, "two"}};
++  DenseMap<int, std::string_view> Map = {{1, "one"}, {2, "two"}};
+   EXPECT_EQ(R"({ (1, "one"), (2, "two") })", ::testing::PrintToString(Map));
+ }
+ 
+diff --git a/llvm/unittests/ADT/FunctionExtrasTest.cpp b/llvm/unittests/ADT/FunctionExtrasTest.cpp
+index fc856a976946bf6decda9b6724cac66afc7bdcd6..aff9d61c7f0d48834123b04b74a2e4f7c86a56d8 100644
+--- a/llvm/unittests/ADT/FunctionExtrasTest.cpp
++++ b/llvm/unittests/ADT/FunctionExtrasTest.cpp
+@@ -249,23 +249,23 @@ TEST(UniqueFunctionTest, Const) {
+ 
+   // Overloaded call operator correctly resolved.
+   struct ChooseCorrectOverload {
+-    StringRef operator()() { return "non-const"; }
+-    StringRef operator()() const { return "const"; }
++    std::string_view operator()() { return "non-const"; }
++    std::string_view operator()() const { return "const"; }
+   };
+-  unique_function<StringRef()> ChooseMutable = ChooseCorrectOverload();
++  unique_function<std::string_view()> ChooseMutable = ChooseCorrectOverload();
+   ChooseCorrectOverload A;
+   EXPECT_EQ("non-const", ChooseMutable());
+   EXPECT_EQ("non-const", A());
+-  unique_function<StringRef() const> ChooseConst = ChooseCorrectOverload();
++  unique_function<std::string_view() const> ChooseConst = ChooseCorrectOverload();
+   const ChooseCorrectOverload &X = A;
+   EXPECT_EQ("const", ChooseConst());
+   EXPECT_EQ("const", X());
+ }
+ 
+ // Test that overloads on unique_functions are resolved as expected.
+-std::string returns(StringRef) { return "not a function"; }
++std::string returns(std::string_view) { return "not a function"; }
+ std::string returns(unique_function<double()> F) { return "number"; }
+-std::string returns(unique_function<StringRef()> F) { return "string"; }
++std::string returns(unique_function<std::string_view()> F) { return "string"; }
+ 
+ TEST(UniqueFunctionTest, SFINAE) {
+   EXPECT_EQ("not a function", returns("boo!"));
+diff --git a/llvm/unittests/ADT/HashingTest.cpp b/llvm/unittests/ADT/HashingTest.cpp
+index 01a8a962b8e2e33ca8f189c049e9548ced42ec3a..62aff9c3b21eea785ca71c6e290c9c4f3a20ae00 100644
+--- a/llvm/unittests/ADT/HashingTest.cpp
++++ b/llvm/unittests/ADT/HashingTest.cpp
+@@ -295,7 +295,7 @@ TEST(HashingTest, HashCombineRangeGoldenTest) {
+ #endif
+   };
+   for (unsigned i = 0; i < sizeof(golden_data)/sizeof(*golden_data); ++i) {
+-    StringRef str = golden_data[i].s;
++    std::string_view str = golden_data[i].s;
+     hash_code hash = hash_combine_range(str.begin(), str.end());
+ #if 0 // Enable this to generate paste-able text for the above structure.
+     std::string member_str = "\"" + str.str() + "\",";
+diff --git a/llvm/unittests/ADT/SmallPtrSetTest.cpp b/llvm/unittests/ADT/SmallPtrSetTest.cpp
+index a97f2617cbf70783f3569709f7ee1bff03baebd2..7ed8670fd31ea2a14e6ba7f59a8ac8e35046890c 100644
+--- a/llvm/unittests/ADT/SmallPtrSetTest.cpp
++++ b/llvm/unittests/ADT/SmallPtrSetTest.cpp
+@@ -12,7 +12,6 @@
+ 
+ #include "llvm/ADT/SmallPtrSet.h"
+ #include "llvm/ADT/PointerIntPair.h"
+-#include "llvm/ADT/STLExtras.h"
+ #include "llvm/Support/PointerLikeTypeTraits.h"
+ #include "gtest/gtest.h"
+ 
+diff --git a/llvm/unittests/ADT/SmallStringTest.cpp b/llvm/unittests/ADT/SmallStringTest.cpp
+index 2f4df8afeafa592cb9616bb78feb4964187786f2..6cf14700b34739420cd3dc4ff8a4c16ce162f715 100644
+--- a/llvm/unittests/ADT/SmallStringTest.cpp
++++ b/llvm/unittests/ADT/SmallStringTest.cpp
+@@ -50,43 +50,43 @@ TEST_F(SmallStringTest, AssignRepeated) {
+ }
+ 
+ TEST_F(SmallStringTest, AssignIterPair) {
+-  StringRef abc = "abc";
++  std::string_view abc = "abc";
+   theString.assign(abc.begin(), abc.end());
+   EXPECT_EQ(3u, theString.size());
+   EXPECT_STREQ("abc", theString.c_str());
+ }
+ 
+-TEST_F(SmallStringTest, AssignStringRef) {
+-  StringRef abc = "abc";
++TEST_F(SmallStringTest, AssignStringView) {
++  std::string_view abc = "abc";
+   theString.assign(abc);
+   EXPECT_EQ(3u, theString.size());
+   EXPECT_STREQ("abc", theString.c_str());
+ }
+ 
+ TEST_F(SmallStringTest, AssignSmallVector) {
+-  StringRef abc = "abc";
++  std::string_view abc = "abc";
+   SmallVector<char, 10> abcVec(abc.begin(), abc.end());
+   theString.assign(abcVec);
+   EXPECT_EQ(3u, theString.size());
+   EXPECT_STREQ("abc", theString.c_str());
+ }
+ 
+-TEST_F(SmallStringTest, AssignStringRefs) {
++TEST_F(SmallStringTest, AssignStringViews) {
+   theString.assign({"abc", "def", "ghi"});
+   EXPECT_EQ(9u, theString.size());
+   EXPECT_STREQ("abcdefghi", theString.c_str());
+ }
+ 
+ TEST_F(SmallStringTest, AppendIterPair) {
+-  StringRef abc = "abc";
++  std::string_view abc = "abc";
+   theString.append(abc.begin(), abc.end());
+   theString.append(abc.begin(), abc.end());
+   EXPECT_EQ(6u, theString.size());
+   EXPECT_STREQ("abcabc", theString.c_str());
+ }
+ 
+-TEST_F(SmallStringTest, AppendStringRef) {
+-  StringRef abc = "abc";
++TEST_F(SmallStringTest, AppendStringView) {
++  std::string_view abc = "abc";
+   theString.append(abc);
+   theString.append(abc);
+   EXPECT_EQ(6u, theString.size());
+@@ -94,7 +94,7 @@ TEST_F(SmallStringTest, AppendStringRef) {
+ }
+ 
+ TEST_F(SmallStringTest, AppendSmallVector) {
+-  StringRef abc = "abc";
++  std::string_view abc = "abc";
+   SmallVector<char, 10> abcVec(abc.begin(), abc.end());
+   theString.append(abcVec);
+   theString.append(abcVec);
+@@ -102,11 +102,11 @@ TEST_F(SmallStringTest, AppendSmallVector) {
+   EXPECT_STREQ("abcabc", theString.c_str());
+ }
+ 
+-TEST_F(SmallStringTest, AppendStringRefs) {
++TEST_F(SmallStringTest, AppendStringViews) {
+   theString.append({"abc", "def", "ghi"});
+   EXPECT_EQ(9u, theString.size());
+   EXPECT_STREQ("abcdefghi", theString.c_str());
+-  StringRef Jkl = "jkl";
++  std::string_view Jkl = "jkl";
+   std::string Mno = "mno";
+   SmallString<4> Pqr("pqr");
+   const char *Stu = "stu";
+@@ -115,15 +115,15 @@ TEST_F(SmallStringTest, AppendStringRefs) {
+   EXPECT_STREQ("abcdefghijklmnopqrstu", theString.c_str());
+ }
+ 
+-TEST_F(SmallStringTest, StringRefConversion) {
+-  StringRef abc = "abc";
++TEST_F(SmallStringTest, StringViewConversion) {
++  std::string_view abc = "abc";
+   theString.assign(abc.begin(), abc.end());
+-  StringRef theStringRef = theString;
+-  EXPECT_EQ("abc", theStringRef);
++  std::string_view theStringView = theString;
++  EXPECT_EQ("abc", theStringView);
+ }
+ 
+ TEST_F(SmallStringTest, StdStringConversion) {
+-  StringRef abc = "abc";
++  std::string_view abc = "abc";
+   theString.assign(abc.begin(), abc.end());
+   std::string theStdString = std::string(theString);
+   EXPECT_EQ("abc", theStdString);
+@@ -149,29 +149,29 @@ TEST_F(SmallStringTest, Slice) {
+ TEST_F(SmallStringTest, Find) {
+   theString = "hello";
+   EXPECT_EQ(2U, theString.find('l'));
+-  EXPECT_EQ(StringRef::npos, theString.find('z'));
+-  EXPECT_EQ(StringRef::npos, theString.find("helloworld"));
++  EXPECT_EQ(std::string_view::npos, theString.find('z'));
++  EXPECT_EQ(std::string_view::npos, theString.find("helloworld"));
+   EXPECT_EQ(0U, theString.find("hello"));
+   EXPECT_EQ(1U, theString.find("ello"));
+-  EXPECT_EQ(StringRef::npos, theString.find("zz"));
++  EXPECT_EQ(std::string_view::npos, theString.find("zz"));
+   EXPECT_EQ(2U, theString.find("ll", 2));
+-  EXPECT_EQ(StringRef::npos, theString.find("ll", 3));
++  EXPECT_EQ(std::string_view::npos, theString.find("ll", 3));
+   EXPECT_EQ(0U, theString.find(""));
+ 
+   EXPECT_EQ(3U, theString.rfind('l'));
+-  EXPECT_EQ(StringRef::npos, theString.rfind('z'));
+-  EXPECT_EQ(StringRef::npos, theString.rfind("helloworld"));
++  EXPECT_EQ(std::string_view::npos, theString.rfind('z'));
++  EXPECT_EQ(std::string_view::npos, theString.rfind("helloworld"));
+   EXPECT_EQ(0U, theString.rfind("hello"));
+   EXPECT_EQ(1U, theString.rfind("ello"));
+-  EXPECT_EQ(StringRef::npos, theString.rfind("zz"));
++  EXPECT_EQ(std::string_view::npos, theString.rfind("zz"));
+ 
+   EXPECT_EQ(2U, theString.find_first_of('l'));
+   EXPECT_EQ(1U, theString.find_first_of("el"));
+-  EXPECT_EQ(StringRef::npos, theString.find_first_of("xyz"));
++  EXPECT_EQ(std::string_view::npos, theString.find_first_of("xyz"));
+ 
+   EXPECT_EQ(1U, theString.find_first_not_of('h'));
+   EXPECT_EQ(4U, theString.find_first_not_of("hel"));
+-  EXPECT_EQ(StringRef::npos, theString.find_first_not_of("hello"));
++  EXPECT_EQ(std::string_view::npos, theString.find_first_not_of("hello"));
+ 
+   theString = "hellx xello hell ello world foo bar hello";
+   EXPECT_EQ(36U, theString.find("hello"));
+diff --git a/llvm/unittests/ADT/SmallVectorTest.cpp b/llvm/unittests/ADT/SmallVectorTest.cpp
+index 137dd43b473068eae34b39edc4b9b8b9633bab95..7029038d18d433cef987bedbfa4fda269b24fb8f 100644
+--- a/llvm/unittests/ADT/SmallVectorTest.cpp
++++ b/llvm/unittests/ADT/SmallVectorTest.cpp
+@@ -11,10 +11,10 @@
+ //===----------------------------------------------------------------------===//
+ 
+ #include "llvm/ADT/SmallVector.h"
+-#include "llvm/ADT/ArrayRef.h"
+ #include "llvm/Support/Compiler.h"
+ #include "gtest/gtest.h"
+ #include <list>
++#include <span>
+ #include <stdarg.h>
+ 
+ using namespace llvm;
+@@ -252,11 +252,11 @@ TYPED_TEST(SmallVectorTest, ConstructorIterTest) {
+ }
+ 
+ // Constructor test.
+-TYPED_TEST(SmallVectorTest, ConstructorFromArrayRefSimpleTest) {
+-  SCOPED_TRACE("ConstructorFromArrayRefSimpleTest");
++TYPED_TEST(SmallVectorTest, ConstructorFromSpanSimpleTest) {
++  SCOPED_TRACE("ConstructorFromSpanSimpleTest");
+   std::array<Constructable, 3> StdArray = {Constructable(1), Constructable(2),
+                                            Constructable(3)};
+-  ArrayRef<Constructable> Array = StdArray;
++  span<const Constructable> Array = StdArray;
+   auto &V = this->theVector;
+   V = SmallVector<Constructable, 4>(Array);
+   assertValuesInOrder(V, 3u, 1, 2, 3);
+@@ -1129,24 +1129,6 @@ TEST(SmallVectorTest, DefaultInlinedElements) {
+   EXPECT_EQ(NestedV[0][0][0], 42);
+ }
+ 
+-TEST(SmallVectorTest, InitializerList) {
+-  SmallVector<int, 2> V1 = {};
+-  EXPECT_TRUE(V1.empty());
+-  V1 = {0, 0};
+-  EXPECT_TRUE(ArrayRef(V1).equals({0, 0}));
+-  V1 = {-1, -1};
+-  EXPECT_TRUE(ArrayRef(V1).equals({-1, -1}));
+-
+-  SmallVector<int, 2> V2 = {1, 2, 3, 4};
+-  EXPECT_TRUE(ArrayRef(V2).equals({1, 2, 3, 4}));
+-  V2.assign({4});
+-  EXPECT_TRUE(ArrayRef(V2).equals({4}));
+-  V2.append({3, 2});
+-  EXPECT_TRUE(ArrayRef(V2).equals({4, 3, 2}));
+-  V2.insert(V2.begin() + 1, 5);
+-  EXPECT_TRUE(ArrayRef(V2).equals({4, 5, 3, 2}));
+-}
+-
+ TEST(SmallVectorTest, ToVector) {
+   {
+     std::vector<char> v = {'a', 'b', 'c'};
+@@ -1183,10 +1165,10 @@ private:
+   To T;
+ };
+ 
+-TEST(SmallVectorTest, ConstructFromArrayRefOfConvertibleType) {
++TEST(SmallVectorTest, ConstructFromSpanOfConvertibleType) {
+   To to1{1}, to2{2}, to3{3};
+   std::vector<From> StdVector = {From(to1), From(to2), From(to3)};
+-  ArrayRef<From> Array = StdVector;
++  span<const From> Array = StdVector;
+   {
+     llvm::SmallVector<To> Vector(Array);
+ 
+diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
+index f9b138e9a472137139397d9cae76823711594211..7f10b3d7d3a8894b1ab0ac660268d94a8b89e082 100644
+--- a/llvm/unittests/ADT/StringMapTest.cpp
++++ b/llvm/unittests/ADT/StringMapTest.cpp
+@@ -7,8 +7,6 @@
+ //===----------------------------------------------------------------------===//
+ 
+ #include "llvm/ADT/StringMap.h"
+-#include "llvm/ADT/STLExtras.h"
+-#include "llvm/ADT/Twine.h"
+ #include "llvm/Support/DataTypes.h"
+ #include "gtest/gtest.h"
+ #include <limits>
+@@ -43,10 +41,10 @@ protected:
+     // Lookup tests
+     EXPECT_FALSE(testMap.contains(testKey));
+     EXPECT_EQ(0u, testMap.count(testKey));
+-    EXPECT_EQ(0u, testMap.count(StringRef(testKeyFirst, testKeyLength)));
++    EXPECT_EQ(0u, testMap.count(std::string_view(testKeyFirst, testKeyLength)));
+     EXPECT_EQ(0u, testMap.count(testKeyStr));
+     EXPECT_TRUE(testMap.find(testKey) == testMap.end());
+-    EXPECT_TRUE(testMap.find(StringRef(testKeyFirst, testKeyLength)) ==
++    EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
+                 testMap.end());
+     EXPECT_TRUE(testMap.find(testKeyStr) == testMap.end());
+   }
+@@ -67,10 +65,10 @@ protected:
+     // Lookup tests
+     EXPECT_TRUE(testMap.contains(testKey));
+     EXPECT_EQ(1u, testMap.count(testKey));
+-    EXPECT_EQ(1u, testMap.count(StringRef(testKeyFirst, testKeyLength)));
++    EXPECT_EQ(1u, testMap.count(std::string_view(testKeyFirst, testKeyLength)));
+     EXPECT_EQ(1u, testMap.count(testKeyStr));
+     EXPECT_TRUE(testMap.find(testKey) == testMap.begin());
+-    EXPECT_TRUE(testMap.find(StringRef(testKeyFirst, testKeyLength)) ==
++    EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
+                 testMap.begin());
+     EXPECT_TRUE(testMap.find(testKeyStr) == testMap.begin());
+   }
+@@ -108,10 +106,10 @@ TEST_F(StringMapTest, ConstEmptyMapTest) {
+ 
+   // Lookup tests
+   EXPECT_EQ(0u, constTestMap.count(testKey));
+-  EXPECT_EQ(0u, constTestMap.count(StringRef(testKeyFirst, testKeyLength)));
++  EXPECT_EQ(0u, constTestMap.count(std::string_view(testKeyFirst, testKeyLength)));
+   EXPECT_EQ(0u, constTestMap.count(testKeyStr));
+   EXPECT_TRUE(constTestMap.find(testKey) == constTestMap.end());
+-  EXPECT_TRUE(constTestMap.find(StringRef(testKeyFirst, testKeyLength)) ==
++  EXPECT_TRUE(constTestMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
+               constTestMap.end());
+   EXPECT_TRUE(constTestMap.find(testKeyStr) == constTestMap.end());
+ }
+@@ -251,7 +249,7 @@ TEST_F(StringMapTest, StringMapEntryTest) {
+   MallocAllocator Allocator;
+   StringMap<uint32_t>::value_type *entry =
+       StringMap<uint32_t>::value_type::create(
+-          StringRef(testKeyFirst, testKeyLength), Allocator, 1u);
++          std::string_view(testKeyFirst, testKeyLength), Allocator, 1u);
+   EXPECT_STREQ(testKey, entry->first().data());
+   EXPECT_EQ(1u, entry->second);
+   entry->Destroy(Allocator);
+@@ -261,7 +259,7 @@ TEST_F(StringMapTest, StringMapEntryTest) {
+ TEST_F(StringMapTest, InsertTest) {
+   SCOPED_TRACE("InsertTest");
+   testMap.insert(StringMap<uint32_t>::value_type::create(
+-      StringRef(testKeyFirst, testKeyLength), testMap.getAllocator(), 1u));
++      std::string_view(testKeyFirst, testKeyLength), testMap.getAllocator(), 1u));
+   assertSingleItemMap();
+ }
+ 
+@@ -330,10 +328,10 @@ TEST_F(StringMapTest, IterMapKeysVector) {
+   Map["C"] = 3;
+   Map["D"] = 3;
+ 
+-  std::vector<StringRef> Keys{Map.keys().begin(), Map.keys().end()};
++  std::vector<std::string_view> Keys{Map.keys().begin(), Map.keys().end()};
+   llvm::sort(Keys);
+ 
+-  std::vector<StringRef> Expected{{"A", "B", "C", "D"}};
++  std::vector<std::string_view> Expected{{"A", "B", "C", "D"}};
+   EXPECT_EQ(Expected, Keys);
+ }
+ 
+@@ -347,7 +345,7 @@ TEST_F(StringMapTest, IterMapKeysSmallVector) {
+   auto Keys = to_vector<4>(Map.keys());
+   llvm::sort(Keys);
+ 
+-  SmallVector<StringRef, 4> Expected = {"A", "B", "C", "D"};
++  SmallVector<std::string_view, 4> Expected = {"A", "B", "C", "D"};
+   EXPECT_EQ(Expected, Keys);
+ }
+ 
+@@ -389,13 +387,13 @@ private:
+ TEST_F(StringMapTest, MoveOnly) {
+   StringMap<MoveOnly> t;
+   t.insert(std::make_pair("Test", MoveOnly(42)));
+-  StringRef Key = "Test";
++  std::string_view Key = "Test";
+   StringMapEntry<MoveOnly>::create(Key, t.getAllocator(), MoveOnly(42))
+       ->Destroy(t.getAllocator());
+ }
+ 
+ TEST_F(StringMapTest, CtorArg) {
+-  StringRef Key = "Test";
++  std::string_view Key = "Test";
+   MallocAllocator Allocator;
+   StringMapEntry<MoveOnly>::create(Key, Allocator, Immovable())
+       ->Destroy(Allocator);
+@@ -580,7 +578,7 @@ TEST(StringMapCustomTest, InitialSizeTest) {
+     CountCtorCopyAndMove::Copy = 0;
+     for (int i = 0; i < Size; ++i)
+       Map.insert(std::pair<std::string, CountCtorCopyAndMove>(
+-          std::piecewise_construct, std::forward_as_tuple(Twine(i).str()),
++          std::piecewise_construct, std::forward_as_tuple(std::to_string(i)),
+           std::forward_as_tuple(i)));
+     // After the initial move, the map will move the Elts in the Entry.
+     EXPECT_EQ((unsigned)Size * 2, CountCtorCopyAndMove::Move);
+@@ -649,7 +647,7 @@ TEST(StringMapCustomTest, StringMapEntrySize) {
+   else
+     LargeValue = std::numeric_limits<unsigned>::max() + 1ULL;
+   StringMapEntry<int> LargeEntry(LargeValue);
+-  StringRef Key = LargeEntry.getKey();
++  std::string_view Key = LargeEntry.getKey();
+   EXPECT_EQ(LargeValue, Key.size());
+ 
+   // Test that the entry can hold at least max size_t.
+diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp
+index 6e75fbae0969ba1bf0a76c4d79a123e405a8dae7..3b07d344f15a555f11ad5f8177a0a65b8a4fa472 100644
+--- a/llvm/unittests/Support/ConvertUTFTest.cpp
++++ b/llvm/unittests/Support/ConvertUTFTest.cpp
+@@ -7,7 +7,6 @@
+ //===----------------------------------------------------------------------===//
+ 
+ #include "llvm/Support/ConvertUTF.h"
+-#include "llvm/ADT/ArrayRef.h"
+ #include "gtest/gtest.h"
+ #include <string>
+ #include <vector>
+@@ -17,7 +16,7 @@ using namespace llvm;
+ TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) {
+   // Src is the look of disapproval.
+   alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";
+-  ArrayRef<char> Ref(Src, sizeof(Src) - 1);
++  span<const char> Ref(Src, sizeof(Src) - 1);
+   std::string Result;
+   bool Success = convertUTF16ToUTF8String(Ref, Result);
+   EXPECT_TRUE(Success);
+@@ -29,7 +28,7 @@ TEST(ConvertUTFTest, ConvertUTF32LittleEndianToUTF8String) {
+   // Src is the look of disapproval.
+   alignas(UTF32) static const char Src[] =
+       "\xFF\xFE\x00\x00\xA0\x0C\x00\x00\x5F\x00\x00\x00\xA0\x0C\x00\x00";
+-  ArrayRef<char> Ref(Src, sizeof(Src) - 1);
++  span<const char> Ref(Src, sizeof(Src) - 1);
+   std::string Result;
+   bool Success = convertUTF32ToUTF8String(Ref, Result);
+   EXPECT_TRUE(Success);
+@@ -40,7 +39,7 @@ TEST(ConvertUTFTest, ConvertUTF32LittleEndianToUTF8String) {
+ TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) {
+   // Src is the look of disapproval.
+   alignas(UTF16) static const char Src[] = "\xfe\xff\x0c\xa0\x00_\x0c\xa0";
+-  ArrayRef<char> Ref(Src, sizeof(Src) - 1);
++  span<const char> Ref(Src, sizeof(Src) - 1);
+   std::string Result;
+   bool Success = convertUTF16ToUTF8String(Ref, Result);
+   EXPECT_TRUE(Success);
+@@ -52,7 +51,7 @@ TEST(ConvertUTFTest, ConvertUTF32BigEndianToUTF8String) {
+   // Src is the look of disapproval.
+   alignas(UTF32) static const char Src[] =
+       "\x00\x00\xFE\xFF\x00\x00\x0C\xA0\x00\x00\x00\x5F\x00\x00\x0C\xA0";
+-  ArrayRef<char> Ref(Src, sizeof(Src) - 1);
++  span<const char> Ref(Src, sizeof(Src) - 1);
+   std::string Result;
+   bool Success = convertUTF32ToUTF8String(Ref, Result);
+   EXPECT_TRUE(Success);
+@@ -63,7 +62,7 @@ TEST(ConvertUTFTest, ConvertUTF32BigEndianToUTF8String) {
+ TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) {
+   // Src is the look of disapproval.
+   static const char Src[] = "\xe0\xb2\xa0_\xe0\xb2\xa0";
+-  StringRef Ref(Src, sizeof(Src) - 1);
++  std::string_view Ref(Src, sizeof(Src) - 1);
+   SmallVector<UTF16, 5> Result;
+   bool Success = convertUTF8ToUTF16String(Ref, Result);
+   EXPECT_TRUE(Success);
+@@ -75,38 +74,38 @@ TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) {
+ 
+ TEST(ConvertUTFTest, OddLengthInput) {
+   std::string Result;
+-  bool Success = convertUTF16ToUTF8String(ArrayRef("xxxxx", 5), Result);
++  bool Success = convertUTF16ToUTF8String(span<const char>("xxxxx", 5), Result);
+   EXPECT_FALSE(Success);
+ }
+ 
+ TEST(ConvertUTFTest, Empty) {
+   std::string Result;
+   bool Success =
+-      convertUTF16ToUTF8String(llvm::ArrayRef<char>(std::nullopt), Result);
++      convertUTF16ToUTF8String(span<const char>(), Result);
+   EXPECT_TRUE(Success);
+   EXPECT_TRUE(Result.empty());
+ }
+ 
+ TEST(ConvertUTFTest, HasUTF16BOM) {
+-  bool HasBOM = hasUTF16ByteOrderMark(ArrayRef("\xff\xfe", 2));
++  bool HasBOM = hasUTF16ByteOrderMark("\xff\xfe");
+   EXPECT_TRUE(HasBOM);
+-  HasBOM = hasUTF16ByteOrderMark(ArrayRef("\xfe\xff", 2));
++  HasBOM = hasUTF16ByteOrderMark("\xfe\xff");
+   EXPECT_TRUE(HasBOM);
+-  HasBOM = hasUTF16ByteOrderMark(ArrayRef("\xfe\xff ", 3));
++  HasBOM = hasUTF16ByteOrderMark("\xfe\xff ");
+   EXPECT_TRUE(HasBOM); // Don't care about odd lengths.
+-  HasBOM = hasUTF16ByteOrderMark(ArrayRef("\xfe\xff\x00asdf", 6));
++  HasBOM = hasUTF16ByteOrderMark("\xfe\xff\x00asdf");
+   EXPECT_TRUE(HasBOM);
+ 
+-  HasBOM = hasUTF16ByteOrderMark(std::nullopt);
++  HasBOM = hasUTF16ByteOrderMark("");
+   EXPECT_FALSE(HasBOM);
+-  HasBOM = hasUTF16ByteOrderMark(ArrayRef("\xfe", 1));
++  HasBOM = hasUTF16ByteOrderMark("\xfe");
+   EXPECT_FALSE(HasBOM);
+ }
+ 
+ TEST(ConvertUTFTest, UTF16WrappersForConvertUTF16ToUTF8String) {
+   // Src is the look of disapproval.
+   alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";
+-  ArrayRef<UTF16> SrcRef = ArrayRef((const UTF16 *)Src, 4);
++  span<const UTF16> SrcRef((const UTF16 *)Src, 4);
+   std::string Result;
+   bool Success = convertUTF16ToUTF8String(SrcRef, Result);
+   EXPECT_TRUE(Success);
+@@ -123,7 +122,7 @@ TEST(ConvertUTFTest, ConvertUTF8toWide) {
+   std::wstring Expected(L"\x0ca0_\x0ca0");
+   EXPECT_EQ(Expected, Result);
+   Result.clear();
+-  Success = ConvertUTF8toWide(StringRef(Src, 7), Result);
++  Success = ConvertUTF8toWide(Src, Result);
+   EXPECT_TRUE(Success);
+   EXPECT_EQ(Expected, Result);
+ }
+@@ -172,7 +171,7 @@ struct ConvertUTFResultContainer {
+ };
+ 
+ std::pair<ConversionResult, std::vector<unsigned>>
+-ConvertUTF8ToUnicodeScalarsLenient(StringRef S) {
++ConvertUTF8ToUnicodeScalarsLenient(std::string_view S) {
+   const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(S.data());
+ 
+   const UTF8 *SourceNext = SourceStart;
+@@ -189,7 +188,7 @@ ConvertUTF8ToUnicodeScalarsLenient(StringRef S) {
+ }
+ 
+ std::pair<ConversionResult, std::vector<unsigned>>
+-ConvertUTF8ToUnicodeScalarsPartialLenient(StringRef S) {
++ConvertUTF8ToUnicodeScalarsPartialLenient(std::string_view S) {
+   const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(S.data());
+ 
+   const UTF8 *SourceNext = SourceStart;
+@@ -207,7 +206,7 @@ ConvertUTF8ToUnicodeScalarsPartialLenient(StringRef S) {
+ 
+ ::testing::AssertionResult
+ CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer Expected,
+-                                 StringRef S, bool Partial = false) {
++                                 std::string_view S, bool Partial = false) {
+   ConversionResult ErrorCode;
+   std::vector<unsigned> Decoded;
+   if (!Partial)
+@@ -302,7 +301,7 @@ TEST(ConvertUTFTest, UTF8ToUTF32Lenient) {
+   // U+0000 NULL
+   EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(
+       ConvertUTFResultContainer(conversionOK).withScalars(0x0000),
+-      StringRef("\x00", 1)));
++      std::string_view("\x00", 1)));
+ 
+   // U+0080 PADDING CHARACTER
+   EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(
+@@ -1076,7 +1075,7 @@ TEST(ConvertUTFTest, UTF8ToUTF32Lenient) {
+   // U+0000 NULL
+   EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(
+       ConvertUTFResultContainer(conversionOK).withScalars(0x0000),
+-      StringRef("\x00", 1)));
++      std::string_view("\x00", 1)));
+ 
+   // Overlong sequences of the above.
+   EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(
+diff --git a/llvm/unittests/Support/xxhashTest.cpp b/llvm/unittests/Support/xxhashTest.cpp
+index 7d78de6772b5159459572fe11633c76d04b86907..d61a5acd21f4d685ca631d3adb20c2649e050bc3 100644
+--- a/llvm/unittests/Support/xxhashTest.cpp
++++ b/llvm/unittests/Support/xxhashTest.cpp
+@@ -12,7 +12,7 @@
+ using namespace llvm;
+ 
+ TEST(xxhashTest, Basic) {
+-  EXPECT_EQ(0xef46db3751d8e999U, xxHash64(StringRef()));
++  EXPECT_EQ(0xef46db3751d8e999U, xxHash64(std::string_view()));
+   EXPECT_EQ(0x33bf00a859c4ba3fU, xxHash64("foo"));
+   EXPECT_EQ(0x48a37c90ad27a659U, xxHash64("bar"));
+   EXPECT_EQ(0x69196c1b3af0bff9U,
+@@ -31,7 +31,7 @@ TEST(xxhashTest, xxh3) {
+   }
+ 
+ #define F(len, expected)                                                       \
+-  EXPECT_EQ(uint64_t(expected), xxh3_64bits(ArrayRef(a, size_t(len))))
++  EXPECT_EQ(uint64_t(expected), xxh3_64bits(span(a, size_t(len))))
+   F(0, 0x2d06800538d394c2);
+   F(1, 0xd0d496e05c553485);
+   F(2, 0x84d625edb7055eac);
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0002-Remove-StringRef-ArrayRef-and-Optional.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0002-Remove-StringRef-ArrayRef-and-Optional.patch
deleted file mode 100644
index eb5b759..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0002-Remove-StringRef-ArrayRef-and-Optional.patch
+++ /dev/null
@@ -1,1894 +0,0 @@
-From afca62cd2f1616bcf2e648dc121a057d59168424 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sat, 7 May 2022 22:09:18 -0400
-Subject: [PATCH 02/28] Remove StringRef, ArrayRef, and Optional
-
----
- llvm/include/llvm/ADT/PointerUnion.h          |  1 -
- llvm/include/llvm/ADT/SmallSet.h              | 13 ++--
- llvm/include/llvm/ADT/SmallString.h           | 77 ++++++++++---------
- llvm/include/llvm/ADT/StringMap.h             | 34 ++++----
- llvm/include/llvm/ADT/StringMapEntry.h        | 25 +++---
- llvm/include/llvm/Support/Chrono.h            | 10 +--
- llvm/include/llvm/Support/Compiler.h          |  2 +-
- llvm/include/llvm/Support/ConvertUTF.h        | 28 ++++---
- llvm/include/llvm/Support/DJB.h               |  6 +-
- llvm/include/llvm/Support/ErrorHandling.h     |  9 +--
- .../llvm/Support/SmallVectorMemoryBuffer.h    |  6 +-
- llvm/include/llvm/Support/VersionTuple.h      | 20 ++---
- .../llvm/Support/Windows/WindowsSupport.h     |  4 +-
- llvm/include/llvm/Support/raw_ostream.h       | 51 ++++++------
- llvm/lib/Support/ConvertUTFWrapper.cpp        | 32 ++++----
- llvm/lib/Support/ErrorHandling.cpp            | 13 ++--
- llvm/lib/Support/SmallVector.cpp              |  5 +-
- llvm/lib/Support/StringMap.cpp                | 12 +--
- llvm/lib/Support/raw_ostream.cpp              | 25 +++---
- llvm/unittests/ADT/DenseMapTest.cpp           | 25 ------
- llvm/unittests/ADT/FunctionExtrasTest.cpp     | 12 +--
- llvm/unittests/ADT/HashingTest.cpp            |  2 +-
- llvm/unittests/ADT/SmallPtrSetTest.cpp        |  1 -
- llvm/unittests/ADT/SmallStringTest.cpp        | 50 ++++++------
- llvm/unittests/ADT/SmallVectorTest.cpp        | 20 +----
- llvm/unittests/ADT/StringMapTest.cpp          | 32 ++++----
- llvm/unittests/Support/ConvertUTFTest.cpp     | 37 +++++----
- 27 files changed, 248 insertions(+), 304 deletions(-)
-
-diff --git a/llvm/include/llvm/ADT/PointerUnion.h b/llvm/include/llvm/ADT/PointerUnion.h
-index 04d566bbc..1d4cc747c 100644
---- a/llvm/include/llvm/ADT/PointerUnion.h
-+++ b/llvm/include/llvm/ADT/PointerUnion.h
-@@ -17,7 +17,6 @@
- 
- #include "llvm/ADT/DenseMapInfo.h"
- #include "llvm/ADT/PointerIntPair.h"
--#include "llvm/ADT/STLExtras.h"
- #include "llvm/Support/PointerLikeTypeTraits.h"
- #include <algorithm>
- #include <cassert>
-diff --git a/llvm/include/llvm/ADT/SmallSet.h b/llvm/include/llvm/ADT/SmallSet.h
-index 0eed85449..bfe93e997 100644
---- a/llvm/include/llvm/ADT/SmallSet.h
-+++ b/llvm/include/llvm/ADT/SmallSet.h
-@@ -14,15 +14,14 @@
- #ifndef LLVM_ADT_SMALLSET_H
- #define LLVM_ADT_SMALLSET_H
- 
--#include "llvm/ADT/None.h"
- #include "llvm/ADT/SmallPtrSet.h"
- #include "llvm/ADT/SmallVector.h"
--#include "llvm/ADT/STLExtras.h"
- #include "llvm/ADT/iterator.h"
- #include "llvm/Support/Compiler.h"
- #include "llvm/Support/type_traits.h"
- #include <cstddef>
- #include <functional>
-+#include <optional>
- #include <set>
- #include <type_traits>
- #include <utility>
-@@ -179,16 +178,16 @@ public:
-   /// concept.
-   // FIXME: Add iterators that abstract over the small and large form, and then
-   // return those here.
--  std::pair<NoneType, bool> insert(const T &V) {
-+  std::pair<std::nullopt_t, bool> insert(const T &V) {
-     if (!isSmall())
--      return std::make_pair(None, Set.insert(V).second);
-+      return std::make_pair(std::nullopt, Set.insert(V).second);
- 
-     VIterator I = vfind(V);
-     if (I != Vector.end())    // Don't reinsert if it already exists.
--      return std::make_pair(None, false);
-+      return std::make_pair(std::nullopt, false);
-     if (Vector.size() < N) {
-       Vector.push_back(V);
--      return std::make_pair(None, true);
-+      return std::make_pair(std::nullopt, true);
-     }
- 
-     // Otherwise, grow from vector to set.
-@@ -197,7 +196,7 @@ public:
-       Vector.pop_back();
-     }
-     Set.insert(V);
--    return std::make_pair(None, true);
-+    return std::make_pair(std::nullopt, true);
-   }
- 
-   template <typename IterT>
-diff --git a/llvm/include/llvm/ADT/SmallString.h b/llvm/include/llvm/ADT/SmallString.h
-index 874968f0a..50cbdade4 100644
---- a/llvm/include/llvm/ADT/SmallString.h
-+++ b/llvm/include/llvm/ADT/SmallString.h
-@@ -15,8 +15,9 @@
- #define LLVM_ADT_SMALLSTRING_H
- 
- #include "llvm/ADT/SmallVector.h"
--#include "llvm/ADT/StringRef.h"
- #include <cstddef>
-+#include <string>
-+#include <string_view>
- 
- namespace llvm {
- 
-@@ -28,11 +29,11 @@ public:
-   /// Default ctor - Initialize to empty.
-   SmallString() = default;
- 
--  /// Initialize from a StringRef.
--  SmallString(StringRef S) : SmallVector<char, InternalLen>(S.begin(), S.end()) {}
-+  /// Initialize from a std::string_view.
-+  SmallString(std::string_view S) : SmallVector<char, InternalLen>(S.begin(), S.end()) {}
- 
--  /// Initialize by concatenating a list of StringRefs.
--  SmallString(std::initializer_list<StringRef> Refs)
-+  /// Initialize by concatenating a list of std::string_views.
-+  SmallString(std::initializer_list<std::string_view> Refs)
-       : SmallVector<char, InternalLen>() {
-     this->append(Refs);
-   }
-@@ -47,13 +48,13 @@ public:
- 
-   using SmallVector<char, InternalLen>::assign;
- 
--  /// Assign from a StringRef.
--  void assign(StringRef RHS) {
-+  /// Assign from a std::string_view.
-+  void assign(std::string_view RHS) {
-     SmallVectorImpl<char>::assign(RHS.begin(), RHS.end());
-   }
- 
--  /// Assign from a list of StringRefs.
--  void assign(std::initializer_list<StringRef> Refs) {
-+  /// Assign from a list of std::string_views.
-+  void assign(std::initializer_list<std::string_view> Refs) {
-     this->clear();
-     append(Refs);
-   }
-@@ -64,19 +65,19 @@ public:
- 
-   using SmallVector<char, InternalLen>::append;
- 
--  /// Append from a StringRef.
--  void append(StringRef RHS) {
-+  /// Append from a std::string_view.
-+  void append(std::string_view RHS) {
-     SmallVectorImpl<char>::append(RHS.begin(), RHS.end());
-   }
- 
--  /// Append from a list of StringRefs.
--  void append(std::initializer_list<StringRef> Refs) {
-+  /// Append from a list of std::string_views.
-+  void append(std::initializer_list<std::string_view> Refs) {
-     size_t CurrentSize = this->size();
-     size_t SizeNeeded = CurrentSize;
--    for (const StringRef &Ref : Refs)
-+    for (const std::string_view &Ref : Refs)
-       SizeNeeded += Ref.size();
-     this->resize_for_overwrite(SizeNeeded);
--    for (const StringRef &Ref : Refs) {
-+    for (const std::string_view &Ref : Refs) {
-       std::copy(Ref.begin(), Ref.end(), this->begin() + CurrentSize);
-       CurrentSize += Ref.size();
-     }
-@@ -89,29 +90,29 @@ public:
- 
-   /// Check for string equality.  This is more efficient than compare() when
-   /// the relative ordering of inequal strings isn't needed.
--  bool equals(StringRef RHS) const {
-+  bool equals(std::string_view RHS) const {
-     return str().equals(RHS);
-   }
- 
-   /// Check for string equality, ignoring case.
--  bool equals_insensitive(StringRef RHS) const {
-+  bool equals_insensitive(std::string_view RHS) const {
-     return str().equals_insensitive(RHS);
-   }
- 
-   /// Compare two strings; the result is -1, 0, or 1 if this string is
-   /// lexicographically less than, equal to, or greater than the \p RHS.
--  int compare(StringRef RHS) const {
-+  int compare(std::string_view RHS) const {
-     return str().compare(RHS);
-   }
- 
-   /// compare_insensitive - Compare two strings, ignoring case.
--  int compare_insensitive(StringRef RHS) const {
-+  int compare_insensitive(std::string_view RHS) const {
-     return str().compare_insensitive(RHS);
-   }
- 
-   /// compare_numeric - Compare two strings, treating sequences of digits as
-   /// numbers.
--  int compare_numeric(StringRef RHS) const {
-+  int compare_numeric(std::string_view RHS) const {
-     return str().compare_numeric(RHS);
-   }
- 
-@@ -120,12 +121,12 @@ public:
-   /// @{
- 
-   /// startswith - Check if this string starts with the given \p Prefix.
--  bool startswith(StringRef Prefix) const {
-+  bool startswith(std::string_view Prefix) const {
-     return str().startswith(Prefix);
-   }
- 
-   /// endswith - Check if this string ends with the given \p Suffix.
--  bool endswith(StringRef Suffix) const {
-+  bool endswith(std::string_view Suffix) const {
-     return str().endswith(Suffix);
-   }
- 
-@@ -145,7 +146,7 @@ public:
-   ///
-   /// \returns The index of the first occurrence of \p Str, or npos if not
-   /// found.
--  size_t find(StringRef Str, size_t From = 0) const {
-+  size_t find(std::string_view Str, size_t From = 0) const {
-     return str().find(Str, From);
-   }
- 
-@@ -153,7 +154,7 @@ public:
-   ///
-   /// \returns The index of the last occurrence of \p C, or npos if not
-   /// found.
--  size_t rfind(char C, size_t From = StringRef::npos) const {
-+  size_t rfind(char C, size_t From = std::string_view::npos) const {
-     return str().rfind(C, From);
-   }
- 
-@@ -161,7 +162,7 @@ public:
-   ///
-   /// \returns The index of the last occurrence of \p Str, or npos if not
-   /// found.
--  size_t rfind(StringRef Str) const {
-+  size_t rfind(std::string_view Str) const {
-     return str().rfind(Str);
-   }
- 
-@@ -175,7 +176,7 @@ public:
-   /// not found.
-   ///
-   /// Complexity: O(size() + Chars.size())
--  size_t find_first_of(StringRef Chars, size_t From = 0) const {
-+  size_t find_first_of(std::string_view Chars, size_t From = 0) const {
-     return str().find_first_of(Chars, From);
-   }
- 
-@@ -189,13 +190,13 @@ public:
-   /// \p Chars, or npos if not found.
-   ///
-   /// Complexity: O(size() + Chars.size())
--  size_t find_first_not_of(StringRef Chars, size_t From = 0) const {
-+  size_t find_first_not_of(std::string_view Chars, size_t From = 0) const {
-     return str().find_first_not_of(Chars, From);
-   }
- 
-   /// Find the last character in the string that is \p C, or npos if not
-   /// found.
--  size_t find_last_of(char C, size_t From = StringRef::npos) const {
-+  size_t find_last_of(char C, size_t From = std::string_view::npos) const {
-     return str().find_last_of(C, From);
-   }
- 
-@@ -204,7 +205,7 @@ public:
-   ///
-   /// Complexity: O(size() + Chars.size())
-   size_t find_last_of(
--      StringRef Chars, size_t From = StringRef::npos) const {
-+      std::string_view Chars, size_t From = std::string_view::npos) const {
-     return str().find_last_of(Chars, From);
-   }
- 
-@@ -219,7 +220,7 @@ public:
- 
-   /// Return the number of non-overlapped occurrences of \p Str in the
-   /// string.
--  size_t count(StringRef Str) const {
-+  size_t count(std::string_view Str) const {
-     return str().count(Str);
-   }
- 
-@@ -236,7 +237,7 @@ public:
-   /// \param N The number of characters to included in the substring. If \p N
-   /// exceeds the number of characters remaining in the string, the string
-   /// suffix (starting with \p Start) will be returned.
--  StringRef substr(size_t Start, size_t N = StringRef::npos) const {
-+  std::string_view substr(size_t Start, size_t N = std::string_view::npos) const {
-     return str().substr(Start, N);
-   }
- 
-@@ -250,14 +251,14 @@ public:
-   /// substring. If this is npos, or less than \p Start, or exceeds the
-   /// number of characters remaining in the string, the string suffix
-   /// (starting with \p Start) will be returned.
--  StringRef slice(size_t Start, size_t End) const {
-+  std::string_view slice(size_t Start, size_t End) const {
-     return str().slice(Start, End);
-   }
- 
-   // Extra methods.
- 
--  /// Explicit conversion to StringRef.
--  StringRef str() const { return StringRef(this->data(), this->size()); }
-+  /// Explicit conversion to std::string_view.
-+  std::string_view str() const { return std::string_view(this->begin(), this->size()); }
- 
-   // TODO: Make this const, if it's safe...
-   const char* c_str() {
-@@ -266,20 +267,20 @@ public:
-     return this->data();
-   }
- 
--  /// Implicit conversion to StringRef.
--  operator StringRef() const { return str(); }
-+  /// Implicit conversion to std::string_view.
-+  operator std::string_view() const { return str(); }
- 
-   explicit operator std::string() const {
-     return std::string(this->data(), this->size());
-   }
- 
-   // Extra operators.
--  SmallString &operator=(StringRef RHS) {
-+  SmallString &operator=(std::string_view RHS) {
-     this->assign(RHS);
-     return *this;
-   }
- 
--  SmallString &operator+=(StringRef RHS) {
-+  SmallString &operator+=(std::string_view RHS) {
-     this->append(RHS.begin(), RHS.end());
-     return *this;
-   }
-diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h
-index 23248093c..8747cdb35 100644
---- a/llvm/include/llvm/ADT/StringMap.h
-+++ b/llvm/include/llvm/ADT/StringMap.h
-@@ -60,12 +60,12 @@ protected:
-   /// 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 LookupBucketFor(StringRef Key);
-+  unsigned LookupBucketFor(std::string_view Key);
- 
-   /// 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 FindKey(StringRef Key) const;
-+  int FindKey(std::string_view Key) const;
- 
-   /// RemoveKey - Remove the specified StringMapEntry from the table, but do not
-   /// delete it.  This aborts if the value isn't in the table.
-@@ -73,7 +73,7 @@ protected:
- 
-   /// 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 *RemoveKey(StringRef Key);
-+  StringMapEntryBase *RemoveKey(std::string_view Key);
- 
-   /// Allocate the table with the specified number of buckets and otherwise
-   /// setup the map as empty.
-@@ -126,7 +126,7 @@ public:
-       : StringMapImpl(InitialSize, static_cast<unsigned>(sizeof(MapEntryTy))),
-         Allocator(A) {}
- 
--  StringMap(std::initializer_list<std::pair<StringRef, ValueTy>> List)
-+  StringMap(std::initializer_list<std::pair<std::string_view, ValueTy>> List)
-       : StringMapImpl(List.size(), static_cast<unsigned>(sizeof(MapEntryTy))) {
-     insert(List);
-   }
-@@ -215,14 +215,14 @@ public:
-                       StringMapKeyIterator<ValueTy>(end()));
-   }
- 
--  iterator find(StringRef Key) {
-+  iterator find(std::string_view Key) {
-     int Bucket = FindKey(Key);
-     if (Bucket == -1)
-       return end();
-     return iterator(TheTable + Bucket, true);
-   }
- 
--  const_iterator find(StringRef Key) const {
-+  const_iterator find(std::string_view Key) const {
-     int Bucket = FindKey(Key);
-     if (Bucket == -1)
-       return end();
-@@ -231,7 +231,7 @@ public:
- 
-   /// lookup - Return the entry for the specified key, or a default
-   /// constructed value if no such entry exists.
--  ValueTy lookup(StringRef Key) const {
-+  ValueTy lookup(std::string_view Key) const {
-     const_iterator it = find(Key);
-     if (it != end())
-       return it->second;
-@@ -240,10 +240,10 @@ public:
- 
-   /// Lookup the ValueTy for the \p Key, or create a default constructed value
-   /// if the key is not in the map.
--  ValueTy &operator[](StringRef Key) { return try_emplace(Key).first->second; }
-+  ValueTy &operator[](std::string_view Key) { return try_emplace(Key).first->second; }
- 
-   /// count - Return 1 if the element is in the map, 0 otherwise.
--  size_type count(StringRef Key) const { return find(Key) == end() ? 0 : 1; }
-+  size_type count(std::string_view Key) const { return find(Key) == end() ? 0 : 1; }
- 
-   template <typename InputTy>
-   size_type count(const StringMapEntry<InputTy> &MapEntry) const {
-@@ -293,7 +293,7 @@ public:
-   /// isn't already in the map. The bool component of the returned pair is true
-   /// if and only if the insertion takes place, and the iterator component of
-   /// the pair points to the element with key equivalent to the key of the pair.
--  std::pair<iterator, bool> insert(std::pair<StringRef, ValueTy> KV) {
-+  std::pair<iterator, bool> insert(std::pair<std::string_view, ValueTy> KV) {
-     return try_emplace(KV.first, std::move(KV.second));
-   }
- 
-@@ -308,14 +308,14 @@ public:
-   ///  Inserts elements from initializer list ilist. If multiple elements in
-   /// the range have keys that compare equivalent, it is unspecified which
-   /// element is inserted
--  void insert(std::initializer_list<std::pair<StringRef, ValueTy>> List) {
-+  void insert(std::initializer_list<std::pair<std::string_view, ValueTy>> List) {
-     insert(List.begin(), List.end());
-   }
- 
-   /// Inserts an element or assigns to the current element if the key already
-   /// exists. The return type is the same as try_emplace.
-   template <typename V>
--  std::pair<iterator, bool> insert_or_assign(StringRef Key, V &&Val) {
-+  std::pair<iterator, bool> insert_or_assign(std::string_view Key, V &&Val) {
-     auto Ret = try_emplace(Key, std::forward<V>(Val));
-     if (!Ret.second)
-       Ret.first->second = std::forward<V>(Val);
-@@ -327,7 +327,7 @@ public:
-   /// if and only if the insertion takes place, and the iterator component of
-   /// the pair points to the element with key equivalent to the key of the pair.
-   template <typename... ArgsTy>
--  std::pair<iterator, bool> try_emplace(StringRef Key, ArgsTy &&... Args) {
-+  std::pair<iterator, bool> try_emplace(std::string_view Key, ArgsTy &&... Args) {
-     unsigned BucketNo = LookupBucketFor(Key);
-     StringMapEntryBase *&Bucket = TheTable[BucketNo];
-     if (Bucket && Bucket != getTombstoneVal())
-@@ -373,7 +373,7 @@ public:
-     V.Destroy(Allocator);
-   }
- 
--  bool erase(StringRef Key) {
-+  bool erase(std::string_view Key) {
-     iterator I = find(Key);
-     if (I == end())
-       return false;
-@@ -470,17 +470,17 @@ template <typename ValueTy>
- class StringMapKeyIterator
-     : public iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
-                                    StringMapConstIterator<ValueTy>,
--                                   std::forward_iterator_tag, StringRef> {
-+                                   std::forward_iterator_tag, std::string_view> {
-   using base = iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
-                                      StringMapConstIterator<ValueTy>,
--                                     std::forward_iterator_tag, StringRef>;
-+                                     std::forward_iterator_tag, std::string_view>;
- 
- public:
-   StringMapKeyIterator() = default;
-   explicit StringMapKeyIterator(StringMapConstIterator<ValueTy> Iter)
-       : base(std::move(Iter)) {}
- 
--  StringRef operator*() const { return this->wrapped()->getKey(); }
-+  std::string_view operator*() const { return this->wrapped()->getKey(); }
- };
- 
- } // end namespace llvm
-diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h
-index 6e13c8618..39976a02b 100644
---- a/llvm/include/llvm/ADT/StringMapEntry.h
-+++ b/llvm/include/llvm/ADT/StringMapEntry.h
-@@ -16,9 +16,8 @@
- #ifndef LLVM_ADT_STRINGMAPENTRY_H
- #define LLVM_ADT_STRINGMAPENTRY_H
- 
--#include "llvm/ADT/None.h"
--#include "llvm/ADT/StringRef.h"
--#include "llvm/ADT/STLFunctionalExtras.h"
-+#include <optional>
-+#include <string_view>
- 
- namespace llvm {
- 
-@@ -37,13 +36,13 @@ protected:
-   /// type-erase the allocator and put it in a source file.
-   template <typename AllocatorTy>
-   static void *allocateWithKey(size_t EntrySize, size_t EntryAlign,
--                               StringRef Key, AllocatorTy &Allocator);
-+                               std::string_view Key, AllocatorTy &Allocator);
- };
- 
- // Define out-of-line to dissuade inlining.
- template <typename AllocatorTy>
- void *StringMapEntryBase::allocateWithKey(size_t EntrySize, size_t EntryAlign,
--                                          StringRef Key,
-+                                          std::string_view Key,
-                                           AllocatorTy &Allocator) {
-   size_t KeyLength = Key.size();
- 
-@@ -85,13 +84,13 @@ public:
-   void setValue(const ValueTy &V) { second = V; }
- };
- 
--template <> class StringMapEntryStorage<NoneType> : public StringMapEntryBase {
-+template <> class StringMapEntryStorage<std::nullopt_t> : public StringMapEntryBase {
- public:
--  explicit StringMapEntryStorage(size_t keyLength, NoneType = None)
-+  explicit StringMapEntryStorage(size_t keyLength, std::nullopt_t = std::nullopt)
-       : StringMapEntryBase(keyLength) {}
-   StringMapEntryStorage(StringMapEntryStorage &entry) = delete;
- 
--  NoneType getValue() const { return None; }
-+  std::nullopt_t getValue() const { return std::nullopt; }
- };
- 
- /// StringMapEntry - This is used to represent one value that is inserted into
-@@ -102,8 +101,8 @@ class StringMapEntry final : public StringMapEntryStorage<ValueTy> {
- public:
-   using StringMapEntryStorage<ValueTy>::StringMapEntryStorage;
- 
--  StringRef getKey() const {
--    return StringRef(getKeyData(), this->getKeyLength());
-+  std::string_view getKey() const {
-+    return std::string_view(getKeyData(), this->getKeyLength());
-   }
- 
-   /// getKeyData - Return the start of the string data that is the key for this
-@@ -113,14 +112,14 @@ public:
-     return reinterpret_cast<const char *>(this + 1);
-   }
- 
--  StringRef first() const {
--    return StringRef(getKeyData(), this->getKeyLength());
-+  std::string_view first() const {
-+    return std::string_view(getKeyData(), this->getKeyLength());
-   }
- 
-   /// Create a StringMapEntry for the specified key construct the value using
-   /// \p InitiVals.
-   template <typename AllocatorTy, typename... InitTy>
--  static StringMapEntry *Create(StringRef key, AllocatorTy &allocator,
-+  static StringMapEntry *Create(std::string_view key, AllocatorTy &allocator,
-                                 InitTy &&... initVals) {
-     return new (StringMapEntryBase::allocateWithKey(
-         sizeof(StringMapEntry), alignof(StringMapEntry), key, allocator))
-diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h
-index 9c2bd45d2..a7dea19d9 100644
---- a/llvm/include/llvm/Support/Chrono.h
-+++ b/llvm/include/llvm/Support/Chrono.h
-@@ -70,7 +70,7 @@ raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
- template <>
- struct format_provider<sys::TimePoint<>> {
-   static void format(const sys::TimePoint<> &TP, llvm::raw_ostream &OS,
--                     StringRef Style);
-+                     std::string_view Style);
- };
- 
- namespace detail {
-@@ -122,7 +122,7 @@ private:
-     return duration_cast<duration<InternalRep, AsPeriod>>(D).count();
-   }
- 
--  static std::pair<InternalRep, StringRef> consumeUnit(StringRef &Style,
-+  static std::pair<InternalRep, std::string_view> consumeUnit(std::string_view &Style,
-                                                         const Dur &D) {
-     using namespace std::chrono;
-     if (Style.consume_front("ns"))
-@@ -140,7 +140,7 @@ private:
-     return {D.count(), detail::unit<Period>::value};
-   }
- 
--  static bool consumeShowUnit(StringRef &Style) {
-+  static bool consumeShowUnit(std::string_view &Style) {
-     if (Style.empty())
-       return true;
-     if (Style.consume_front("-"))
-@@ -152,9 +152,9 @@ private:
-   }
- 
- public:
--  static void format(const Dur &D, llvm::raw_ostream &Stream, StringRef Style) {
-+  static void format(const Dur &D, llvm::raw_ostream &Stream, std::string_view Style) {
-     InternalRep count;
--    StringRef unit;
-+    std::string_view unit;
-     std::tie(count, unit) = consumeUnit(Style, D);
-     bool show_unit = consumeShowUnit(Style);
- 
-diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h
-index 80b2dfaec..f5d726ec8 100644
---- a/llvm/include/llvm/Support/Compiler.h
-+++ b/llvm/include/llvm/Support/Compiler.h
-@@ -312,7 +312,7 @@
- #endif
- 
- /// LLVM_GSL_POINTER - Apply this to non-owning classes like
--/// StringRef to enable lifetime warnings.
-+/// std::string_view to enable lifetime warnings.
- #if LLVM_HAS_CPP_ATTRIBUTE(gsl::Pointer)
- #define LLVM_GSL_POINTER [[gsl::Pointer]]
- #else
-diff --git a/llvm/include/llvm/Support/ConvertUTF.h b/llvm/include/llvm/Support/ConvertUTF.h
-index 374cdb907..7f1527f51 100644
---- a/llvm/include/llvm/Support/ConvertUTF.h
-+++ b/llvm/include/llvm/Support/ConvertUTF.h
-@@ -89,12 +89,12 @@
- #ifndef LLVM_SUPPORT_CONVERTUTF_H
- #define LLVM_SUPPORT_CONVERTUTF_H
- 
-+#include "wpi/span.h"
-+
- #include <cstddef>
- #include <string>
--
--#if defined(_WIN32)
-+#include <string_view>
- #include <system_error>
--#endif
- 
- // Wrap everything in namespace llvm so that programs can link with llvm and
- // their own version of the unicode libraries.
-@@ -183,12 +183,10 @@ unsigned getNumBytesForUTF8(UTF8 firstByte);
- /*************************************************************************/
- /* Below are LLVM-specific wrappers of the functions above. */
- 
--template <typename T> class ArrayRef;
- template <typename T> class SmallVectorImpl;
--class StringRef;
- 
- /**
-- * Convert an UTF8 StringRef to UTF8, UTF16, or UTF32 depending on
-+ * Convert an UTF8 string_view to UTF8, UTF16, or UTF32 depending on
-  * WideCharWidth. The converted data is written to ResultPtr, which needs to
-  * point to at least WideCharWidth * (Source.Size() + 1) bytes. On success,
-  * ResultPtr will point one after the end of the copied string. On failure,
-@@ -196,14 +194,14 @@ class StringRef;
-  * the first character which could not be converted.
-  * \return true on success.
-  */
--bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source,
-+bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source,
-                        char *&ResultPtr, const UTF8 *&ErrorPtr);
- 
- /**
--* Converts a UTF-8 StringRef to a std::wstring.
-+* Converts a UTF-8 string_view to a std::wstring.
- * \return true on success.
- */
--bool ConvertUTF8toWide(llvm::StringRef Source, std::wstring &Result);
-+bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result);
- 
- /**
- * Converts a UTF-8 C-string to a std::wstring.
-@@ -261,7 +259,7 @@ inline ConversionResult convertUTF8Sequence(const UTF8 **source,
-  * Returns true if a blob of text starts with a UTF-16 big or little endian byte
-  * order mark.
-  */
--bool hasUTF16ByteOrderMark(ArrayRef<char> SrcBytes);
-+bool hasUTF16ByteOrderMark(span<const char> SrcBytes);
- 
- /**
-  * Converts a stream of raw bytes assumed to be UTF16 into a UTF8 std::string.
-@@ -270,7 +268,7 @@ bool hasUTF16ByteOrderMark(ArrayRef<char> SrcBytes);
-  * \param [out] Out Converted UTF-8 is stored here on success.
-  * \returns true on success
-  */
--bool convertUTF16ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out);
-+bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out);
- 
- /**
- * Converts a UTF16 string into a UTF8 std::string.
-@@ -279,22 +277,22 @@ bool convertUTF16ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out);
- * \param [out] Out Converted UTF-8 is stored here on success.
- * \returns true on success
- */
--bool convertUTF16ToUTF8String(ArrayRef<UTF16> Src, std::string &Out);
-+bool convertUTF16ToUTF8String(span<const UTF16> Src, std::string &Out);
- 
- /**
-  * Converts a UTF-8 string into a UTF-16 string with native endianness.
-  *
-  * \returns true on success
-  */
--bool convertUTF8ToUTF16String(StringRef SrcUTF8,
-+bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
-                               SmallVectorImpl<UTF16> &DstUTF16);
- 
- #if defined(_WIN32)
- namespace sys {
- namespace windows {
--std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
-+std::error_code UTF8ToUTF16(std::string_view utf8, SmallVectorImpl<wchar_t> &utf16);
- /// Convert to UTF16 from the current code page used in the system
--std::error_code CurCPToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
-+std::error_code CurCPToUTF16(std::string_view utf8, SmallVectorImpl<wchar_t> &utf16);
- std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
-                             SmallVectorImpl<char> &utf8);
- /// Convert from UTF16 to the current code page used in the system
-diff --git a/llvm/include/llvm/Support/DJB.h b/llvm/include/llvm/Support/DJB.h
-index 8a04a324a..8737cd144 100644
---- a/llvm/include/llvm/Support/DJB.h
-+++ b/llvm/include/llvm/Support/DJB.h
-@@ -13,13 +13,13 @@
- #ifndef LLVM_SUPPORT_DJB_H
- #define LLVM_SUPPORT_DJB_H
- 
--#include "llvm/ADT/StringRef.h"
-+#include <string_view>
- 
- namespace llvm {
- 
- /// The Bernstein hash function used by the DWARF accelerator tables.
--inline uint32_t djbHash(StringRef Buffer, uint32_t H = 5381) {
--  for (unsigned char C : Buffer.bytes())
-+inline uint32_t djbHash(std::string_view Buffer, uint32_t H = 5381) {
-+  for (unsigned char C : Buffer)
-     H = (H << 5) + H + C;
-   return H;
- }
-diff --git a/llvm/include/llvm/Support/ErrorHandling.h b/llvm/include/llvm/Support/ErrorHandling.h
-index 6791df6be..3f726d40b 100644
---- a/llvm/include/llvm/Support/ErrorHandling.h
-+++ b/llvm/include/llvm/Support/ErrorHandling.h
-@@ -15,10 +15,10 @@
- #define LLVM_SUPPORT_ERRORHANDLING_H
- 
- #include "llvm/Support/Compiler.h"
-+#include <string>
-+#include <string_view>
- 
- namespace llvm {
--  class StringRef;
--  class Twine;
- 
-   /// An error handler callback.
-   typedef void (*fatal_error_handler_t)(void *user_data,
-@@ -67,12 +67,11 @@ namespace llvm {
- /// standard error, followed by a newline.
- /// After the error handler is called this function will call abort(), it
- /// does not return.
--/// NOTE: The std::string variant was removed to avoid a <string> dependency.
- [[noreturn]] void report_fatal_error(const char *reason,
-                                      bool gen_crash_diag = true);
--[[noreturn]] void report_fatal_error(StringRef reason,
-+[[noreturn]] void report_fatal_error(const std::string &reason,
-                                      bool gen_crash_diag = true);
--[[noreturn]] void report_fatal_error(const Twine &reason,
-+[[noreturn]] void report_fatal_error(std::string_view reason,
-                                      bool gen_crash_diag = true);
- 
- /// Installs a new bad alloc error handler that should be used whenever a
-diff --git a/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h b/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h
-index f7f2d4e54..b5e321b5f 100644
---- a/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h
-+++ b/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h
-@@ -35,8 +35,8 @@ public:
-                                 RequiresNullTerminator) {}
- 
-   /// Construct a named SmallVectorMemoryBuffer from the given SmallVector
--  /// r-value and StringRef.
--  SmallVectorMemoryBuffer(SmallVectorImpl<char> &&SV, StringRef Name,
-+  /// r-value and std::string_view.
-+  SmallVectorMemoryBuffer(SmallVectorImpl<char> &&SV, std::string_view Name,
-                           bool RequiresNullTerminator = true)
-       : SV(std::move(SV)), BufferName(std::string(Name)) {
-     if (RequiresNullTerminator) {
-@@ -49,7 +49,7 @@ public:
-   // Key function.
-   ~SmallVectorMemoryBuffer() override;
- 
--  StringRef getBufferIdentifier() const override { return BufferName; }
-+  std::string_view getBufferIdentifier() const override { return BufferName; }
- 
-   BufferKind getBufferKind() const override { return MemoryBuffer_Malloc; }
- 
-diff --git a/llvm/include/llvm/Support/VersionTuple.h b/llvm/include/llvm/Support/VersionTuple.h
-index 1a1072d22..3d6573bf5 100644
---- a/llvm/include/llvm/Support/VersionTuple.h
-+++ b/llvm/include/llvm/Support/VersionTuple.h
-@@ -16,14 +16,13 @@
- 
- #include "llvm/ADT/DenseMapInfo.h"
- #include "llvm/ADT/Hashing.h"
--#include "llvm/ADT/Optional.h"
- #include "llvm/Support/HashBuilder.h"
-+#include <optional>
- #include <string>
- #include <tuple>
- 
- namespace llvm {
- class raw_ostream;
--class StringRef;
- 
- /// Represents a version number in the form major[.minor[.subminor[.build]]].
- class VersionTuple {
-@@ -70,23 +69,23 @@ public:
-   unsigned getMajor() const { return Major; }
- 
-   /// Retrieve the minor version number, if provided.
--  Optional<unsigned> getMinor() const {
-+  std::optional<unsigned> getMinor() const {
-     if (!HasMinor)
--      return None;
-+      return std::nullopt;
-     return Minor;
-   }
- 
-   /// Retrieve the subminor version number, if provided.
--  Optional<unsigned> getSubminor() const {
-+  std::optional<unsigned> getSubminor() const {
-     if (!HasSubminor)
--      return None;
-+      return std::nullopt;
-     return Subminor;
-   }
- 
-   /// Retrieve the build version number, if provided.
--  Optional<unsigned> getBuild() const {
-+  std::optional<unsigned> getBuild() const {
-     if (!HasBuild)
--      return None;
-+      return std::nullopt;
-     return Build;
-   }
- 
-@@ -173,11 +172,6 @@ public:
- 
-   /// Retrieve a string representation of the version number.
-   std::string getAsString() const;
--
--  /// Try to parse the given string as a version number.
--  /// \returns \c true if the string does not match the regular expression
--  ///   [0-9]+(\.[0-9]+){0,3}
--  bool tryParse(StringRef string);
- };
- 
- /// Print a version number.
-diff --git a/llvm/include/llvm/Support/Windows/WindowsSupport.h b/llvm/include/llvm/Support/Windows/WindowsSupport.h
-index 917822678..180803fbd 100644
---- a/llvm/include/llvm/Support/Windows/WindowsSupport.h
-+++ b/llvm/include/llvm/Support/Windows/WindowsSupport.h
-@@ -35,8 +35,6 @@
- 
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/ADT/StringExtras.h"
--#include "llvm/ADT/StringRef.h"
--#include "llvm/ADT/Twine.h"
- #include "llvm/Config/llvm-config.h" // Get build system configuration settings
- #include "llvm/Support/Allocator.h"
- #include "llvm/Support/Chrono.h"
-@@ -71,7 +69,7 @@ bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix);
- [[noreturn]] inline void ReportLastErrorFatal(const char *Msg) {
-   std::string ErrMsg;
-   MakeErrMsg(&ErrMsg, Msg);
--  llvm::report_fatal_error(Twine(ErrMsg));
-+  llvm::report_fatal_error(ErrMsg);
- }
- 
- template <typename HandleTraits>
-diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
-index 58adb41cb..9a1dd7a60 100644
---- a/llvm/include/llvm/Support/raw_ostream.h
-+++ b/llvm/include/llvm/Support/raw_ostream.h
-@@ -14,9 +14,7 @@
- #define LLVM_SUPPORT_RAW_OSTREAM_H
- 
- #include "llvm/ADT/SmallVector.h"
--#include "llvm/ADT/StringRef.h"
--#include "llvm/ADT/Optional.h"
--#include "llvm/Support/DataTypes.h"
-+#include "llvm/ADT/span.h"
- #include <cassert>
- #include <cstddef>
- #include <cstdint>
-@@ -210,7 +208,22 @@ public:
-     return *this;
-   }
- 
--  raw_ostream &operator<<(StringRef Str) {
-+  raw_ostream &operator<<(span<const uint8_t> Arr) {
-+    // Inline fast path, particularly for arrays with a known length.
-+    size_t Size = Arr.size();
-+
-+    // Make sure we can use the fast path.
-+    if (Size > (size_t)(OutBufEnd - OutBufCur))
-+      return write(Arr.data(), Size);
-+
-+    if (Size) {
-+      memcpy(OutBufCur, Arr.data(), Size);
-+      OutBufCur += Size;
-+    }
-+    return *this;
-+  }
-+
-+  raw_ostream &operator<<(std::string_view Str) {
-     // Inline fast path, particularly for strings with a known length.
-     size_t Size = Str.size();
- 
-@@ -229,7 +242,7 @@ public:
-     // Inline fast path, particularly for constant strings where a sufficiently
-     // smart compiler will simplify strlen.
- 
--    return this->operator<<(StringRef(Str));
-+    return this->operator<<(std::string_view(Str));
-   }
- 
-   raw_ostream &operator<<(const std::string &Str) {
-@@ -237,12 +250,6 @@ public:
-     return write(Str.data(), Str.length());
-   }
- 
--#if __cplusplus > 201402L
--  raw_ostream &operator<<(const std::string_view &Str) {
--    return write(Str.data(), Str.length());
--  }
--#endif
--
-   raw_ostream &operator<<(const SmallVectorImpl<char> &Str) {
-     return write(Str.data(), Str.size());
-   }
-@@ -275,7 +282,7 @@ public:
- 
-   /// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't
-   /// satisfy llvm::isPrint into an escape sequence.
--  raw_ostream &write_escaped(StringRef Str, bool UseHexEscapes = false);
-+  raw_ostream &write_escaped(std::string_view Str, bool UseHexEscapes = false);
- 
-   raw_ostream &write(unsigned char C);
-   raw_ostream &write(const char *Ptr, size_t Size);
-@@ -446,7 +453,7 @@ class raw_fd_ostream : public raw_pwrite_stream {
-   bool ShouldClose;
-   bool SupportsSeeking = false;
-   bool IsRegularFile = false;
--  mutable Optional<bool> HasColors;
-+  mutable std::optional<bool> HasColors;
- 
- #ifdef _WIN32
-   /// True if this fd refers to a Windows console device. Mintty and other
-@@ -491,14 +498,14 @@ public:
-   /// As a special case, if Filename is "-", then the stream will use
-   /// STDOUT_FILENO instead of opening a file. This will not close the stdout
-   /// descriptor.
--  raw_fd_ostream(StringRef Filename, std::error_code &EC);
--  raw_fd_ostream(StringRef Filename, std::error_code &EC,
-+  raw_fd_ostream(std::string_view Filename, std::error_code &EC);
-+  raw_fd_ostream(std::string_view Filename, std::error_code &EC,
-                  sys::fs::CreationDisposition Disp);
--  raw_fd_ostream(StringRef Filename, std::error_code &EC,
-+  raw_fd_ostream(std::string_view Filename, std::error_code &EC,
-                  sys::fs::FileAccess Access);
--  raw_fd_ostream(StringRef Filename, std::error_code &EC,
-+  raw_fd_ostream(std::string_view Filename, std::error_code &EC,
-                  sys::fs::OpenFlags Flags);
--  raw_fd_ostream(StringRef Filename, std::error_code &EC,
-+  raw_fd_ostream(std::string_view Filename, std::error_code &EC,
-                  sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
-                  sys::fs::OpenFlags Flags);
- 
-@@ -603,7 +610,7 @@ public:
-   /// Open the specified file for reading/writing/seeking. If an error occurs,
-   /// information about the error is put into EC, and the stream should be
-   /// immediately destroyed.
--  raw_fd_stream(StringRef Filename, std::error_code &EC);
-+  raw_fd_stream(std::string_view Filename, std::error_code &EC);
- 
-   /// This reads the \p Size bytes into a buffer pointed by \p Ptr.
-   ///
-@@ -683,8 +690,8 @@ public:
- 
-   void flush() = delete;
- 
--  /// Return a StringRef for the vector contents.
--  StringRef str() const { return StringRef(OS.data(), OS.size()); }
-+  /// Return a std::string_view for the vector contents.
-+  std::string_view str() const { return std::string_view(OS.data(), OS.size()); }
- 
-   void reserveExtraSpace(uint64_t ExtraSize) override {
-     OS.reserve(tell() + ExtraSize);
-@@ -741,7 +748,7 @@ class Error;
- /// for other names. For raw_fd_ostream instances, the stream writes to
- /// a temporary file. The final output file is atomically replaced with the
- /// temporary file after the \p Write function is finished.
--Error writeToOutput(StringRef OutputFileName,
-+Error writeToOutput(std::string_view OutputFileName,
-                     std::function<Error(raw_ostream &)> Write);
- 
- } // end namespace llvm
-diff --git a/llvm/lib/Support/ConvertUTFWrapper.cpp b/llvm/lib/Support/ConvertUTFWrapper.cpp
-index 392c4c489..396ab0c65 100644
---- a/llvm/lib/Support/ConvertUTFWrapper.cpp
-+++ b/llvm/lib/Support/ConvertUTFWrapper.cpp
-@@ -6,24 +6,24 @@
- //
- //===----------------------------------------------------------------------===//
- 
--#include "llvm/ADT/ArrayRef.h"
--#include "llvm/ADT/StringRef.h"
-+#include "llvm/ADT/span.h"
- #include "llvm/Support/ConvertUTF.h"
- #include "llvm/Support/ErrorHandling.h"
- #include "llvm/Support/SwapByteOrder.h"
- #include <string>
-+#include <string_view>
- #include <vector>
- 
- namespace llvm {
- 
--bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source,
-+bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source,
-                        char *&ResultPtr, const UTF8 *&ErrorPtr) {
-   assert(WideCharWidth == 1 || WideCharWidth == 2 || WideCharWidth == 4);
-   ConversionResult result = conversionOK;
-   // Copy the character span over.
-   if (WideCharWidth == 1) {
--    const UTF8 *Pos = reinterpret_cast<const UTF8*>(Source.begin());
--    if (!isLegalUTF8String(&Pos, reinterpret_cast<const UTF8*>(Source.end()))) {
-+    const UTF8 *Pos = reinterpret_cast<const UTF8*>(Source.data());
-+    if (!isLegalUTF8String(&Pos, reinterpret_cast<const UTF8*>(Source.data() + Source.size()))) {
-       result = sourceIllegal;
-       ErrorPtr = Pos;
-     } else {
-@@ -77,13 +77,13 @@ bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr) {
-   return true;
- }
- 
--bool hasUTF16ByteOrderMark(ArrayRef<char> S) {
-+bool hasUTF16ByteOrderMark(span<const char> S) {
-   return (S.size() >= 2 &&
-           ((S[0] == '\xff' && S[1] == '\xfe') ||
-            (S[0] == '\xfe' && S[1] == '\xff')));
- }
- 
--bool convertUTF16ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out) {
-+bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out) {
-   assert(Out.empty());
- 
-   // Error out on an uneven byte count.
-@@ -134,14 +134,14 @@ bool convertUTF16ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out) {
-   return true;
- }
- 
--bool convertUTF16ToUTF8String(ArrayRef<UTF16> Src, std::string &Out)
-+bool convertUTF16ToUTF8String(span<const UTF16> Src, std::string &Out)
- {
-   return convertUTF16ToUTF8String(
--      llvm::ArrayRef<char>(reinterpret_cast<const char *>(Src.data()),
-+      span<const char>(reinterpret_cast<const char *>(Src.data()),
-       Src.size() * sizeof(UTF16)), Out);
- }
- 
--bool convertUTF8ToUTF16String(StringRef SrcUTF8,
-+bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
-                               SmallVectorImpl<UTF16> &DstUTF16) {
-   assert(DstUTF16.empty());
- 
-@@ -152,8 +152,8 @@ bool convertUTF8ToUTF16String(StringRef SrcUTF8,
-     return true;
-   }
- 
--  const UTF8 *Src = reinterpret_cast<const UTF8 *>(SrcUTF8.begin());
--  const UTF8 *SrcEnd = reinterpret_cast<const UTF8 *>(SrcUTF8.end());
-+  const UTF8 *Src = reinterpret_cast<const UTF8 *>(SrcUTF8.data());
-+  const UTF8 *SrcEnd = reinterpret_cast<const UTF8 *>(SrcUTF8.data() + SrcUTF8.size());
- 
-   // 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
-@@ -184,7 +184,7 @@ static_assert(sizeof(wchar_t) == 1 || sizeof(wchar_t) == 2 ||
-               "Expected wchar_t to be 1, 2, or 4 bytes");
- 
- template <typename TResult>
--static inline bool ConvertUTF8toWideInternal(llvm::StringRef Source,
-+static inline bool ConvertUTF8toWideInternal(std::string_view Source,
-                                              TResult &Result) {
-   // Even in the case of UTF-16, the number of bytes in a UTF-8 string is
-   // at least as large as the number of elements in the resulting wide
-@@ -200,7 +200,7 @@ static inline bool ConvertUTF8toWideInternal(llvm::StringRef Source,
-   return true;
- }
- 
--bool ConvertUTF8toWide(llvm::StringRef Source, std::wstring &Result) {
-+bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result) {
-   return ConvertUTF8toWideInternal(Source, Result);
- }
- 
-@@ -209,7 +209,7 @@ bool ConvertUTF8toWide(const char *Source, std::wstring &Result) {
-     Result.clear();
-     return true;
-   }
--  return ConvertUTF8toWide(llvm::StringRef(Source), Result);
-+  return ConvertUTF8toWide(std::string_view(Source), Result);
- }
- 
- bool convertWideToUTF8(const std::wstring &Source, std::string &Result) {
-@@ -224,7 +224,7 @@ bool convertWideToUTF8(const std::wstring &Source, std::string &Result) {
-     return true;
-   } else if (sizeof(wchar_t) == 2) {
-     return convertUTF16ToUTF8String(
--        llvm::ArrayRef<UTF16>(reinterpret_cast<const UTF16 *>(Source.data()),
-+        span<const UTF16>(reinterpret_cast<const UTF16 *>(Source.data()),
-                               Source.size()),
-         Result);
-   } else if (sizeof(wchar_t) == 4) {
-diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
-index 80c0e0043..8ae8fb8b4 100644
---- a/llvm/lib/Support/ErrorHandling.cpp
-+++ b/llvm/lib/Support/ErrorHandling.cpp
-@@ -14,7 +14,6 @@
- #include "llvm/Support/ErrorHandling.h"
- #include "llvm-c/ErrorHandling.h"
- #include "llvm/ADT/SmallVector.h"
--#include "llvm/ADT/Twine.h"
- #include "llvm/Config/config.h"
- #include "llvm/Support/Debug.h"
- #include "llvm/Support/Errc.h"
-@@ -80,14 +79,14 @@ void llvm::remove_fatal_error_handler() {
- }
- 
- void llvm::report_fatal_error(const char *Reason, bool GenCrashDiag) {
--  report_fatal_error(Twine(Reason), GenCrashDiag);
-+  report_fatal_error(std::string_view(Reason), GenCrashDiag);
- }
- 
--void llvm::report_fatal_error(StringRef Reason, bool GenCrashDiag) {
--  report_fatal_error(Twine(Reason), GenCrashDiag);
-+void llvm::report_fatal_error(const std::string &Reason, bool GenCrashDiag) {
-+  report_fatal_error(std::string_view(Reason), GenCrashDiag);
- }
- 
--void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
-+void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
-   llvm::fatal_error_handler_t handler = nullptr;
-   void* handlerData = nullptr;
-   {
-@@ -101,7 +100,7 @@ void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
-   }
- 
-   if (handler) {
--    handler(handlerData, Reason.str().c_str(), GenCrashDiag);
-+    handler(handlerData, std::string{Reason}.c_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
-@@ -109,7 +108,7 @@ void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
-     SmallVector<char, 64> Buffer;
-     raw_svector_ostream OS(Buffer);
-     OS << "LLVM ERROR: " << Reason << "\n";
--    StringRef MessageStr = OS.str();
-+    std::string_view MessageStr = OS.str();
-     ssize_t written = ::write(2, MessageStr.data(), MessageStr.size());
-     (void)written; // If something went wrong, we deliberately just give up.
-   }
-diff --git a/llvm/lib/Support/SmallVector.cpp b/llvm/lib/Support/SmallVector.cpp
-index 8cafbc7fa..8bad715e4 100644
---- a/llvm/lib/Support/SmallVector.cpp
-+++ b/llvm/lib/Support/SmallVector.cpp
-@@ -11,7 +11,6 @@
- //===----------------------------------------------------------------------===//
- 
- #include "llvm/ADT/SmallVector.h"
--#include "llvm/ADT/Twine.h"
- #include "llvm/Support/MemAlloc.h"
- #include <cstdint>
- #ifdef LLVM_ENABLE_EXCEPTIONS
-@@ -67,7 +66,7 @@ static void report_size_overflow(size_t MinSize, size_t MaxSize) {
- #ifdef LLVM_ENABLE_EXCEPTIONS
-   throw std::length_error(Reason);
- #else
--  report_fatal_error(Twine(Reason));
-+  report_fatal_error(Reason);
- #endif
- }
- 
-@@ -81,7 +80,7 @@ static void report_at_maximum_capacity(size_t MaxSize) {
- #ifdef LLVM_ENABLE_EXCEPTIONS
-   throw std::length_error(Reason);
- #else
--  report_fatal_error(Twine(Reason));
-+  report_fatal_error(Reason);
- #endif
- }
- 
-diff --git a/llvm/lib/Support/StringMap.cpp b/llvm/lib/Support/StringMap.cpp
-index 012c785b4..317f4ee43 100644
---- a/llvm/lib/Support/StringMap.cpp
-+++ b/llvm/lib/Support/StringMap.cpp
-@@ -70,7 +70,7 @@ void StringMapImpl::init(unsigned InitSize) {
- /// 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 StringMapImpl::LookupBucketFor(std::string_view Name) {
-   unsigned HTSize = NumBuckets;
-   if (HTSize == 0) { // Hash table unallocated so far?
-     init(16);
-@@ -110,7 +110,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
-       // Do the comparison like this because Name isn't necessarily
-       // null-terminated!
-       char *ItemStr = (char *)BucketItem + ItemSize;
--      if (Name == StringRef(ItemStr, BucketItem->getKeyLength())) {
-+      if (Name == std::string_view(ItemStr, BucketItem->getKeyLength())) {
-         // We found a match!
-         return BucketNo;
-       }
-@@ -128,7 +128,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
- /// 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 {
-+int StringMapImpl::FindKey(std::string_view Key) const {
-   unsigned HTSize = NumBuckets;
-   if (HTSize == 0)
-     return -1; // Really empty table?
-@@ -154,7 +154,7 @@ int StringMapImpl::FindKey(StringRef Key) const {
-       // Do the comparison like this because NameStart isn't necessarily
-       // null-terminated!
-       char *ItemStr = (char *)BucketItem + ItemSize;
--      if (Key == StringRef(ItemStr, BucketItem->getKeyLength())) {
-+      if (Key == std::string_view(ItemStr, BucketItem->getKeyLength())) {
-         // We found a match!
-         return BucketNo;
-       }
-@@ -173,14 +173,14 @@ int StringMapImpl::FindKey(StringRef Key) const {
- /// 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()));
-+  StringMapEntryBase *V2 = RemoveKey(std::string_view(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) {
-+StringMapEntryBase *StringMapImpl::RemoveKey(std::string_view Key) {
-   int Bucket = FindKey(Key);
-   if (Bucket == -1)
-     return nullptr;
-diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
-index 69d4fe96b..e4c318eb8 100644
---- a/llvm/lib/Support/raw_ostream.cpp
-+++ b/llvm/lib/Support/raw_ostream.cpp
-@@ -159,7 +159,7 @@ raw_ostream &raw_ostream::write_uuid(const uuid_t UUID) {
- }
- 
- 
--raw_ostream &raw_ostream::write_escaped(StringRef Str,
-+raw_ostream &raw_ostream::write_escaped(std::string_view Str,
-                                         bool UseHexEscapes) {
-   for (unsigned char c : Str) {
-     switch (c) {
-@@ -563,7 +563,7 @@ void format_object_base::home() {
- //  raw_fd_ostream
- //===----------------------------------------------------------------------===//
- 
--static int getFD(StringRef Filename, std::error_code &EC,
-+static int getFD(std::string_view Filename, std::error_code &EC,
-                  sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
-                  sys::fs::OpenFlags Flags) {
-   assert((Access & sys::fs::FA_Write) &&
-@@ -589,25 +589,25 @@ static int getFD(StringRef Filename, std::error_code &EC,
-   return FD;
- }
- 
--raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC)
-+raw_fd_ostream::raw_fd_ostream(std::string_view 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,
-+raw_fd_ostream::raw_fd_ostream(std::string_view 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,
-+raw_fd_ostream::raw_fd_ostream(std::string_view 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,
-+raw_fd_ostream::raw_fd_ostream(std::string_view 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,
-+raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
-                                sys::fs::CreationDisposition Disp,
-                                sys::fs::FileAccess Access,
-                                sys::fs::OpenFlags Flags)
-@@ -679,8 +679,7 @@ raw_fd_ostream::~raw_fd_ostream() {
-   // 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(Twine("IO failure on output stream: ") +
--                           error().message(),
-+    report_fatal_error("IO failure on output stream: " + error().message(),
-                        /*gen_crash_diag=*/false);
- }
- 
-@@ -699,7 +698,7 @@ raw_fd_ostream::~raw_fd_ostream() {
- // 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) {
-+static bool write_console_impl(int FD, std::string_view Data) {
-   SmallVector<wchar_t, 256> WideText;
- 
-   // Fall back to ::write if it wasn't valid UTF-8.
-@@ -742,7 +741,7 @@ void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
-   // 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)))
-+    if (write_console_impl(FD, std::string_view(Ptr, Size)))
-       return;
- #endif
- 
-@@ -906,7 +905,7 @@ raw_ostream &llvm::nulls() {
- // File Streams
- //===----------------------------------------------------------------------===//
- 
--raw_fd_stream::raw_fd_stream(StringRef Filename, std::error_code &EC)
-+raw_fd_stream::raw_fd_stream(std::string_view Filename, std::error_code &EC)
-     : raw_fd_ostream(getFD(Filename, EC, sys::fs::CD_CreateAlways,
-                            sys::fs::FA_Write | sys::fs::FA_Read,
-                            sys::fs::OF_None),
-@@ -984,7 +983,7 @@ void buffer_ostream::anchor() {}
- 
- void buffer_unique_ostream::anchor() {}
- 
--Error llvm::writeToOutput(StringRef OutputFileName,
-+Error llvm::writeToOutput(std::string_view OutputFileName,
-                           std::function<Error(raw_ostream &)> Write) {
-   if (OutputFileName == "-")
-     return Write(outs());
-diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
-index 4dd314c5c..e505b1907 100644
---- a/llvm/unittests/ADT/DenseMapTest.cpp
-+++ b/llvm/unittests/ADT/DenseMapTest.cpp
-@@ -481,31 +481,6 @@ TEST(DenseMapCustomTest, ReserveTest) {
-   }
- }
- 
--// Make sure DenseMap works with StringRef keys.
--TEST(DenseMapCustomTest, StringRefTest) {
--  DenseMap<StringRef, int> M;
--
--  M["a"] = 1;
--  M["b"] = 2;
--  M["c"] = 3;
--
--  EXPECT_EQ(3u, M.size());
--  EXPECT_EQ(1, M.lookup("a"));
--  EXPECT_EQ(2, M.lookup("b"));
--  EXPECT_EQ(3, M.lookup("c"));
--
--  EXPECT_EQ(0, M.lookup("q"));
--
--  // Test the empty string, spelled various ways.
--  EXPECT_EQ(0, M.lookup(""));
--  EXPECT_EQ(0, M.lookup(StringRef()));
--  EXPECT_EQ(0, M.lookup(StringRef("a", 0)));
--  M[""] = 42;
--  EXPECT_EQ(42, M.lookup(""));
--  EXPECT_EQ(42, M.lookup(StringRef()));
--  EXPECT_EQ(42, M.lookup(StringRef("a", 0)));
--}
--
- // Key traits that allows lookup with either an unsigned or char* key;
- // In the latter case, "a" == 0, "b" == 1 and so on.
- struct TestDenseMapInfo {
-diff --git a/llvm/unittests/ADT/FunctionExtrasTest.cpp b/llvm/unittests/ADT/FunctionExtrasTest.cpp
-index fc856a976..aff9d61c7 100644
---- a/llvm/unittests/ADT/FunctionExtrasTest.cpp
-+++ b/llvm/unittests/ADT/FunctionExtrasTest.cpp
-@@ -249,23 +249,23 @@ TEST(UniqueFunctionTest, Const) {
- 
-   // Overloaded call operator correctly resolved.
-   struct ChooseCorrectOverload {
--    StringRef operator()() { return "non-const"; }
--    StringRef operator()() const { return "const"; }
-+    std::string_view operator()() { return "non-const"; }
-+    std::string_view operator()() const { return "const"; }
-   };
--  unique_function<StringRef()> ChooseMutable = ChooseCorrectOverload();
-+  unique_function<std::string_view()> ChooseMutable = ChooseCorrectOverload();
-   ChooseCorrectOverload A;
-   EXPECT_EQ("non-const", ChooseMutable());
-   EXPECT_EQ("non-const", A());
--  unique_function<StringRef() const> ChooseConst = ChooseCorrectOverload();
-+  unique_function<std::string_view() const> ChooseConst = ChooseCorrectOverload();
-   const ChooseCorrectOverload &X = A;
-   EXPECT_EQ("const", ChooseConst());
-   EXPECT_EQ("const", X());
- }
- 
- // Test that overloads on unique_functions are resolved as expected.
--std::string returns(StringRef) { return "not a function"; }
-+std::string returns(std::string_view) { return "not a function"; }
- std::string returns(unique_function<double()> F) { return "number"; }
--std::string returns(unique_function<StringRef()> F) { return "string"; }
-+std::string returns(unique_function<std::string_view()> F) { return "string"; }
- 
- TEST(UniqueFunctionTest, SFINAE) {
-   EXPECT_EQ("not a function", returns("boo!"));
-diff --git a/llvm/unittests/ADT/HashingTest.cpp b/llvm/unittests/ADT/HashingTest.cpp
-index bb19a5699..0634767a4 100644
---- a/llvm/unittests/ADT/HashingTest.cpp
-+++ b/llvm/unittests/ADT/HashingTest.cpp
-@@ -277,7 +277,7 @@ TEST(HashingTest, HashCombineRangeGoldenTest) {
- #endif
-   };
-   for (unsigned i = 0; i < sizeof(golden_data)/sizeof(*golden_data); ++i) {
--    StringRef str = golden_data[i].s;
-+    std::string_view str = golden_data[i].s;
-     hash_code hash = hash_combine_range(str.begin(), str.end());
- #if 0 // Enable this to generate paste-able text for the above structure.
-     std::string member_str = "\"" + str.str() + "\",";
-diff --git a/llvm/unittests/ADT/SmallPtrSetTest.cpp b/llvm/unittests/ADT/SmallPtrSetTest.cpp
-index 414298c4e..6f3c94eed 100644
---- a/llvm/unittests/ADT/SmallPtrSetTest.cpp
-+++ b/llvm/unittests/ADT/SmallPtrSetTest.cpp
-@@ -12,7 +12,6 @@
- 
- #include "llvm/ADT/SmallPtrSet.h"
- #include "llvm/ADT/PointerIntPair.h"
--#include "llvm/ADT/STLExtras.h"
- #include "llvm/Support/PointerLikeTypeTraits.h"
- #include "gtest/gtest.h"
- 
-diff --git a/llvm/unittests/ADT/SmallStringTest.cpp b/llvm/unittests/ADT/SmallStringTest.cpp
-index b207f582e..bee3875d1 100644
---- a/llvm/unittests/ADT/SmallStringTest.cpp
-+++ b/llvm/unittests/ADT/SmallStringTest.cpp
-@@ -50,43 +50,43 @@ TEST_F(SmallStringTest, AssignRepeated) {
- }
- 
- TEST_F(SmallStringTest, AssignIterPair) {
--  StringRef abc = "abc";
-+  std::string_view abc = "abc";
-   theString.assign(abc.begin(), abc.end());
-   EXPECT_EQ(3u, theString.size());
-   EXPECT_STREQ("abc", theString.c_str());
- }
- 
--TEST_F(SmallStringTest, AssignStringRef) {
--  StringRef abc = "abc";
-+TEST_F(SmallStringTest, AssignStringView) {
-+  std::string_view abc = "abc";
-   theString.assign(abc);
-   EXPECT_EQ(3u, theString.size());
-   EXPECT_STREQ("abc", theString.c_str());
- }
- 
- TEST_F(SmallStringTest, AssignSmallVector) {
--  StringRef abc = "abc";
-+  std::string_view abc = "abc";
-   SmallVector<char, 10> abcVec(abc.begin(), abc.end());
-   theString.assign(abcVec);
-   EXPECT_EQ(3u, theString.size());
-   EXPECT_STREQ("abc", theString.c_str());
- }
- 
--TEST_F(SmallStringTest, AssignStringRefs) {
-+TEST_F(SmallStringTest, AssignStringViews) {
-   theString.assign({"abc", "def", "ghi"});
-   EXPECT_EQ(9u, theString.size());
-   EXPECT_STREQ("abcdefghi", theString.c_str());
- }
- 
- TEST_F(SmallStringTest, AppendIterPair) {
--  StringRef abc = "abc";
-+  std::string_view abc = "abc";
-   theString.append(abc.begin(), abc.end());
-   theString.append(abc.begin(), abc.end());
-   EXPECT_EQ(6u, theString.size());
-   EXPECT_STREQ("abcabc", theString.c_str());
- }
- 
--TEST_F(SmallStringTest, AppendStringRef) {
--  StringRef abc = "abc";
-+TEST_F(SmallStringTest, AppendStringView) {
-+  std::string_view abc = "abc";
-   theString.append(abc);
-   theString.append(abc);
-   EXPECT_EQ(6u, theString.size());
-@@ -94,7 +94,7 @@ TEST_F(SmallStringTest, AppendStringRef) {
- }
- 
- TEST_F(SmallStringTest, AppendSmallVector) {
--  StringRef abc = "abc";
-+  std::string_view abc = "abc";
-   SmallVector<char, 10> abcVec(abc.begin(), abc.end());
-   theString.append(abcVec);
-   theString.append(abcVec);
-@@ -102,11 +102,11 @@ TEST_F(SmallStringTest, AppendSmallVector) {
-   EXPECT_STREQ("abcabc", theString.c_str());
- }
- 
--TEST_F(SmallStringTest, AppendStringRefs) {
-+TEST_F(SmallStringTest, AppendStringViews) {
-   theString.append({"abc", "def", "ghi"});
-   EXPECT_EQ(9u, theString.size());
-   EXPECT_STREQ("abcdefghi", theString.c_str());
--  StringRef Jkl = "jkl";
-+  std::string_view Jkl = "jkl";
-   std::string Mno = "mno";
-   SmallString<4> Pqr("pqr");
-   const char *Stu = "stu";
-@@ -115,15 +115,15 @@ TEST_F(SmallStringTest, AppendStringRefs) {
-   EXPECT_STREQ("abcdefghijklmnopqrstu", theString.c_str());
- }
- 
--TEST_F(SmallStringTest, StringRefConversion) {
--  StringRef abc = "abc";
-+TEST_F(SmallStringTest, StringViewConversion) {
-+  std::string_view abc = "abc";
-   theString.assign(abc.begin(), abc.end());
--  StringRef theStringRef = theString;
--  EXPECT_EQ("abc", theStringRef);
-+  std::string_view theStringView = theString;
-+  EXPECT_EQ("abc", theStringView);
- }
- 
- TEST_F(SmallStringTest, StdStringConversion) {
--  StringRef abc = "abc";
-+  std::string_view abc = "abc";
-   theString.assign(abc.begin(), abc.end());
-   std::string theStdString = std::string(theString);
-   EXPECT_EQ("abc", theStdString);
-@@ -149,29 +149,29 @@ TEST_F(SmallStringTest, Slice) {
- TEST_F(SmallStringTest, Find) {
-   theString = "hello";
-   EXPECT_EQ(2U, theString.find('l'));
--  EXPECT_EQ(StringRef::npos, theString.find('z'));
--  EXPECT_EQ(StringRef::npos, theString.find("helloworld"));
-+  EXPECT_EQ(std::string_view::npos, theString.find('z'));
-+  EXPECT_EQ(std::string_view::npos, theString.find("helloworld"));
-   EXPECT_EQ(0U, theString.find("hello"));
-   EXPECT_EQ(1U, theString.find("ello"));
--  EXPECT_EQ(StringRef::npos, theString.find("zz"));
-+  EXPECT_EQ(std::string_view::npos, theString.find("zz"));
-   EXPECT_EQ(2U, theString.find("ll", 2));
--  EXPECT_EQ(StringRef::npos, theString.find("ll", 3));
-+  EXPECT_EQ(std::string_view::npos, theString.find("ll", 3));
-   EXPECT_EQ(0U, theString.find(""));
- 
-   EXPECT_EQ(3U, theString.rfind('l'));
--  EXPECT_EQ(StringRef::npos, theString.rfind('z'));
--  EXPECT_EQ(StringRef::npos, theString.rfind("helloworld"));
-+  EXPECT_EQ(std::string_view::npos, theString.rfind('z'));
-+  EXPECT_EQ(std::string_view::npos, theString.rfind("helloworld"));
-   EXPECT_EQ(0U, theString.rfind("hello"));
-   EXPECT_EQ(1U, theString.rfind("ello"));
--  EXPECT_EQ(StringRef::npos, theString.rfind("zz"));
-+  EXPECT_EQ(std::string_view::npos, theString.rfind("zz"));
- 
-   EXPECT_EQ(2U, theString.find_first_of('l'));
-   EXPECT_EQ(1U, theString.find_first_of("el"));
--  EXPECT_EQ(StringRef::npos, theString.find_first_of("xyz"));
-+  EXPECT_EQ(std::string_view::npos, theString.find_first_of("xyz"));
- 
-   EXPECT_EQ(1U, theString.find_first_not_of('h'));
-   EXPECT_EQ(4U, theString.find_first_not_of("hel"));
--  EXPECT_EQ(StringRef::npos, theString.find_first_not_of("hello"));
-+  EXPECT_EQ(std::string_view::npos, theString.find_first_not_of("hello"));
- 
-   theString = "hellx xello hell ello world foo bar hello";
-   EXPECT_EQ(36U, theString.find("hello"));
-diff --git a/llvm/unittests/ADT/SmallVectorTest.cpp b/llvm/unittests/ADT/SmallVectorTest.cpp
-index 3fbea5299..fe827546a 100644
---- a/llvm/unittests/ADT/SmallVectorTest.cpp
-+++ b/llvm/unittests/ADT/SmallVectorTest.cpp
-@@ -11,7 +11,7 @@
- //===----------------------------------------------------------------------===//
- 
- #include "llvm/ADT/SmallVector.h"
--#include "llvm/ADT/ArrayRef.h"
-+#include "llvm/ADT/span.h"
- #include "llvm/Support/Compiler.h"
- #include "gtest/gtest.h"
- #include <list>
-@@ -1070,24 +1070,6 @@ TEST(SmallVectorTest, DefaultInlinedElements) {
-   EXPECT_EQ(NestedV[0][0][0], 42);
- }
- 
--TEST(SmallVectorTest, InitializerList) {
--  SmallVector<int, 2> V1 = {};
--  EXPECT_TRUE(V1.empty());
--  V1 = {0, 0};
--  EXPECT_TRUE(makeArrayRef(V1).equals({0, 0}));
--  V1 = {-1, -1};
--  EXPECT_TRUE(makeArrayRef(V1).equals({-1, -1}));
--
--  SmallVector<int, 2> V2 = {1, 2, 3, 4};
--  EXPECT_TRUE(makeArrayRef(V2).equals({1, 2, 3, 4}));
--  V2.assign({4});
--  EXPECT_TRUE(makeArrayRef(V2).equals({4}));
--  V2.append({3, 2});
--  EXPECT_TRUE(makeArrayRef(V2).equals({4, 3, 2}));
--  V2.insert(V2.begin() + 1, 5);
--  EXPECT_TRUE(makeArrayRef(V2).equals({4, 5, 3, 2}));
--}
--
- template <class VectorT>
- class SmallVectorReferenceInvalidationTest : public SmallVectorTestBase {
- protected:
-diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
-index 817fec6c3..86907ab61 100644
---- a/llvm/unittests/ADT/StringMapTest.cpp
-+++ b/llvm/unittests/ADT/StringMapTest.cpp
-@@ -7,8 +7,6 @@
- //===----------------------------------------------------------------------===//
- 
- #include "llvm/ADT/StringMap.h"
--#include "llvm/ADT/STLExtras.h"
--#include "llvm/ADT/Twine.h"
- #include "llvm/Support/DataTypes.h"
- #include "gtest/gtest.h"
- #include <limits>
-@@ -38,10 +36,10 @@ protected:
- 
-     // Lookup tests
-     EXPECT_EQ(0u, testMap.count(testKey));
--    EXPECT_EQ(0u, testMap.count(StringRef(testKeyFirst, testKeyLength)));
-+    EXPECT_EQ(0u, testMap.count(std::string_view(testKeyFirst, testKeyLength)));
-     EXPECT_EQ(0u, testMap.count(testKeyStr));
-     EXPECT_TRUE(testMap.find(testKey) == testMap.end());
--    EXPECT_TRUE(testMap.find(StringRef(testKeyFirst, testKeyLength)) == 
-+    EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) == 
-                 testMap.end());
-     EXPECT_TRUE(testMap.find(testKeyStr) == testMap.end());
-   }
-@@ -61,10 +59,10 @@ protected:
- 
-     // Lookup tests
-     EXPECT_EQ(1u, testMap.count(testKey));
--    EXPECT_EQ(1u, testMap.count(StringRef(testKeyFirst, testKeyLength)));
-+    EXPECT_EQ(1u, testMap.count(std::string_view(testKeyFirst, testKeyLength)));
-     EXPECT_EQ(1u, testMap.count(testKeyStr));
-     EXPECT_TRUE(testMap.find(testKey) == testMap.begin());
--    EXPECT_TRUE(testMap.find(StringRef(testKeyFirst, testKeyLength)) == 
-+    EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) == 
-                 testMap.begin());
-     EXPECT_TRUE(testMap.find(testKeyStr) == testMap.begin());
-   }
-@@ -104,10 +102,10 @@ TEST_F(StringMapTest, ConstEmptyMapTest) {
- 
-   // Lookup tests
-   EXPECT_EQ(0u, constTestMap.count(testKey));
--  EXPECT_EQ(0u, constTestMap.count(StringRef(testKeyFirst, testKeyLength)));
-+  EXPECT_EQ(0u, constTestMap.count(std::string_view(testKeyFirst, testKeyLength)));
-   EXPECT_EQ(0u, constTestMap.count(testKeyStr));
-   EXPECT_TRUE(constTestMap.find(testKey) == constTestMap.end());
--  EXPECT_TRUE(constTestMap.find(StringRef(testKeyFirst, testKeyLength)) ==
-+  EXPECT_TRUE(constTestMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
-               constTestMap.end());
-   EXPECT_TRUE(constTestMap.find(testKeyStr) == constTestMap.end());
- }
-@@ -235,7 +233,7 @@ TEST_F(StringMapTest, StringMapEntryTest) {
-   MallocAllocator Allocator;
-   StringMap<uint32_t>::value_type *entry =
-       StringMap<uint32_t>::value_type::Create(
--          StringRef(testKeyFirst, testKeyLength), Allocator, 1u);
-+          std::string_view(testKeyFirst, testKeyLength), Allocator, 1u);
-   EXPECT_STREQ(testKey, entry->first().data());
-   EXPECT_EQ(1u, entry->second);
-   entry->Destroy(Allocator);
-@@ -246,7 +244,7 @@ TEST_F(StringMapTest, InsertTest) {
-   SCOPED_TRACE("InsertTest");
-   testMap.insert(
-       StringMap<uint32_t>::value_type::Create(
--          StringRef(testKeyFirst, testKeyLength),
-+          std::string_view(testKeyFirst, testKeyLength),
-           testMap.getAllocator(), 1u));
-   assertSingleItemMap();
- }
-@@ -316,10 +314,10 @@ TEST_F(StringMapTest, IterMapKeysVector) {
-   Map["C"] = 3;
-   Map["D"] = 3;
- 
--  std::vector<StringRef> Keys{Map.keys().begin(), Map.keys().end()};
-+  std::vector<std::string_view> Keys{Map.keys().begin(), Map.keys().end()};
-   llvm::sort(Keys);
- 
--  std::vector<StringRef> Expected{{"A", "B", "C", "D"}};
-+  std::vector<std::string_view> Expected{{"A", "B", "C", "D"}};
-   EXPECT_EQ(Expected, Keys);
- }
- 
-@@ -333,7 +331,7 @@ TEST_F(StringMapTest, IterMapKeysSmallVector) {
-   auto Keys = to_vector<4>(Map.keys());
-   llvm::sort(Keys);
- 
--  SmallVector<StringRef, 4> Expected = {"A", "B", "C", "D"};
-+  SmallVector<std::string_view, 4> Expected = {"A", "B", "C", "D"};
-   EXPECT_EQ(Expected, Keys);
- }
- 
-@@ -375,13 +373,13 @@ private:
- TEST_F(StringMapTest, MoveOnly) {
-   StringMap<MoveOnly> t;
-   t.insert(std::make_pair("Test", MoveOnly(42)));
--  StringRef Key = "Test";
-+  std::string_view Key = "Test";
-   StringMapEntry<MoveOnly>::Create(Key, t.getAllocator(), MoveOnly(42))
-       ->Destroy(t.getAllocator());
- }
- 
- TEST_F(StringMapTest, CtorArg) {
--  StringRef Key = "Test";
-+  std::string_view Key = "Test";
-   MallocAllocator Allocator;
-   StringMapEntry<MoveOnly>::Create(Key, Allocator, Immovable())
-       ->Destroy(Allocator);
-@@ -556,7 +554,7 @@ TEST(StringMapCustomTest, InitialSizeTest) {
-     CountCtorCopyAndMove::Copy = 0;
-     for (int i = 0; i < Size; ++i)
-       Map.insert(std::pair<std::string, CountCtorCopyAndMove>(
--          std::piecewise_construct, std::forward_as_tuple(Twine(i).str()),
-+          std::piecewise_construct, std::forward_as_tuple(std::to_string(i)),
-           std::forward_as_tuple(i)));
-     // After the initial move, the map will move the Elts in the Entry.
-     EXPECT_EQ((unsigned)Size * 2, CountCtorCopyAndMove::Move);
-@@ -625,7 +623,7 @@ TEST(StringMapCustomTest, StringMapEntrySize) {
-   else
-     LargeValue = std::numeric_limits<unsigned>::max() + 1ULL;
-   StringMapEntry<int> LargeEntry(LargeValue);
--  StringRef Key = LargeEntry.getKey();
-+  std::string_view Key = LargeEntry.getKey();
-   EXPECT_EQ(LargeValue, Key.size());
- 
-   // Test that the entry can hold at least max size_t.
-diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp
-index 7bda6ea28..9c798437a 100644
---- a/llvm/unittests/Support/ConvertUTFTest.cpp
-+++ b/llvm/unittests/Support/ConvertUTFTest.cpp
-@@ -7,7 +7,6 @@
- //===----------------------------------------------------------------------===//
- 
- #include "llvm/Support/ConvertUTF.h"
--#include "llvm/ADT/ArrayRef.h"
- #include "gtest/gtest.h"
- #include <string>
- #include <vector>
-@@ -17,7 +16,7 @@ using namespace llvm;
- TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) {
-   // Src is the look of disapproval.
-   alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";
--  ArrayRef<char> Ref(Src, sizeof(Src) - 1);
-+  span<const char> Ref(Src, sizeof(Src) - 1);
-   std::string Result;
-   bool Success = convertUTF16ToUTF8String(Ref, Result);
-   EXPECT_TRUE(Success);
-@@ -28,7 +27,7 @@ TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) {
- TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) {
-   // Src is the look of disapproval.
-   alignas(UTF16) static const char Src[] = "\xfe\xff\x0c\xa0\x00_\x0c\xa0";
--  ArrayRef<char> Ref(Src, sizeof(Src) - 1);
-+  span<const char> Ref(Src, sizeof(Src) - 1);
-   std::string Result;
-   bool Success = convertUTF16ToUTF8String(Ref, Result);
-   EXPECT_TRUE(Success);
-@@ -39,7 +38,7 @@ TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) {
- TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) {
-   // Src is the look of disapproval.
-   static const char Src[] = "\xe0\xb2\xa0_\xe0\xb2\xa0";
--  StringRef Ref(Src, sizeof(Src) - 1);
-+  std::string_view Ref(Src, sizeof(Src) - 1);
-   SmallVector<UTF16, 5> Result;
-   bool Success = convertUTF8ToUTF16String(Ref, Result);
-   EXPECT_TRUE(Success);
-@@ -51,37 +50,37 @@ TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) {
- 
- TEST(ConvertUTFTest, OddLengthInput) {
-   std::string Result;
--  bool Success = convertUTF16ToUTF8String(makeArrayRef("xxxxx", 5), Result);
-+  bool Success = convertUTF16ToUTF8String(span<const char>("xxxxx", 5), Result);
-   EXPECT_FALSE(Success);
- }
- 
- TEST(ConvertUTFTest, Empty) {
-   std::string Result;
--  bool Success = convertUTF16ToUTF8String(llvm::ArrayRef<char>(None), Result);
-+  bool Success = convertUTF16ToUTF8String(span<const char>(), Result);
-   EXPECT_TRUE(Success);
-   EXPECT_TRUE(Result.empty());
- }
- 
- TEST(ConvertUTFTest, HasUTF16BOM) {
--  bool HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xff\xfe", 2));
-+  bool HasBOM = hasUTF16ByteOrderMark("\xff\xfe");
-   EXPECT_TRUE(HasBOM);
--  HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe\xff", 2));
-+  HasBOM = hasUTF16ByteOrderMark("\xfe\xff");
-   EXPECT_TRUE(HasBOM);
--  HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe\xff ", 3));
-+  HasBOM = hasUTF16ByteOrderMark("\xfe\xff ");
-   EXPECT_TRUE(HasBOM); // Don't care about odd lengths.
--  HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe\xff\x00asdf", 6));
-+  HasBOM = hasUTF16ByteOrderMark("\xfe\xff\x00asdf");
-   EXPECT_TRUE(HasBOM);
- 
--  HasBOM = hasUTF16ByteOrderMark(None);
-+  HasBOM = hasUTF16ByteOrderMark("");
-   EXPECT_FALSE(HasBOM);
--  HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe", 1));
-+  HasBOM = hasUTF16ByteOrderMark("\xfe");
-   EXPECT_FALSE(HasBOM);
- }
- 
- TEST(ConvertUTFTest, UTF16WrappersForConvertUTF16ToUTF8String) {
-   // Src is the look of disapproval.
-   alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";
--  ArrayRef<UTF16> SrcRef = makeArrayRef((const UTF16 *)Src, 4);
-+  span<const UTF16> SrcRef((const UTF16 *)Src, 4);
-   std::string Result;
-   bool Success = convertUTF16ToUTF8String(SrcRef, Result);
-   EXPECT_TRUE(Success);
-@@ -98,7 +97,7 @@ TEST(ConvertUTFTest, ConvertUTF8toWide) {
-   std::wstring Expected(L"\x0ca0_\x0ca0");
-   EXPECT_EQ(Expected, Result);
-   Result.clear();
--  Success = ConvertUTF8toWide(StringRef(Src, 7), Result);
-+  Success = ConvertUTF8toWide(Src, Result);
-   EXPECT_TRUE(Success);
-   EXPECT_EQ(Expected, Result);
- }
-@@ -147,7 +146,7 @@ struct ConvertUTFResultContainer {
- };
- 
- std::pair<ConversionResult, std::vector<unsigned>>
--ConvertUTF8ToUnicodeScalarsLenient(StringRef S) {
-+ConvertUTF8ToUnicodeScalarsLenient(std::string_view S) {
-   const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(S.data());
- 
-   const UTF8 *SourceNext = SourceStart;
-@@ -164,7 +163,7 @@ ConvertUTF8ToUnicodeScalarsLenient(StringRef S) {
- }
- 
- std::pair<ConversionResult, std::vector<unsigned>>
--ConvertUTF8ToUnicodeScalarsPartialLenient(StringRef S) {
-+ConvertUTF8ToUnicodeScalarsPartialLenient(std::string_view S) {
-   const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(S.data());
- 
-   const UTF8 *SourceNext = SourceStart;
-@@ -182,7 +181,7 @@ ConvertUTF8ToUnicodeScalarsPartialLenient(StringRef S) {
- 
- ::testing::AssertionResult
- CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer Expected,
--                                 StringRef S, bool Partial = false) {
-+                                 std::string_view S, bool Partial = false) {
-   ConversionResult ErrorCode;
-   std::vector<unsigned> Decoded;
-   if (!Partial)
-@@ -277,7 +276,7 @@ TEST(ConvertUTFTest, UTF8ToUTF32Lenient) {
-   // U+0000 NULL
-   EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(
-       ConvertUTFResultContainer(conversionOK).withScalars(0x0000),
--      StringRef("\x00", 1)));
-+      std::string_view("\x00", 1)));
- 
-   // U+0080 PADDING CHARACTER
-   EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(
-@@ -1051,7 +1050,7 @@ TEST(ConvertUTFTest, UTF8ToUTF32Lenient) {
-   // U+0000 NULL
-   EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(
-       ConvertUTFResultContainer(conversionOK).withScalars(0x0000),
--      StringRef("\x00", 1)));
-+      std::string_view("\x00", 1)));
- 
-   // Overlong sequences of the above.
-   EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0002-Wrap-std-min-max-calls-in-parens-for-Windows-warning.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0002-Wrap-std-min-max-calls-in-parens-for-Windows-warning.patch
new file mode 100644
index 0000000..fe5c48e
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0002-Wrap-std-min-max-calls-in-parens-for-Windows-warning.patch
@@ -0,0 +1,176 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sat, 7 May 2022 22:12:41 -0400
+Subject: [PATCH 02/31] Wrap std::min/max calls in parens, for Windows warnings
+
+---
+ llvm/include/llvm/ADT/DenseMap.h       |  4 ++--
+ llvm/include/llvm/ADT/SmallVector.h    | 12 ++++++------
+ llvm/include/llvm/Support/ConvertUTF.h |  2 +-
+ llvm/include/llvm/Support/MathExtras.h | 18 +++++++++---------
+ 4 files changed, 18 insertions(+), 18 deletions(-)
+
+diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
+index 3ef6a7cd1b4b587e61fcb9475d9f3516018bf2ee..108193f04486425f3b7f039cd9d2004be6facafb 100644
+--- a/llvm/include/llvm/ADT/DenseMap.h
++++ b/llvm/include/llvm/ADT/DenseMap.h
+@@ -416,7 +416,7 @@ protected:
+       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);
++    return static_cast<unsigned>(NextPowerOf2(NumEntries * 4 / 3 + 1));
+   }
+ 
+   void moveFromOldBuckets(BucketT *OldBucketsBegin, BucketT *OldBucketsEnd) {
+@@ -852,7 +852,7 @@ public:
+     // Reduce the number of buckets.
+     unsigned NewNumBuckets = 0;
+     if (OldNumEntries)
+-      NewNumBuckets = std::max(64, 1 << (Log2_32_Ceil(OldNumEntries) + 1));
++      NewNumBuckets = (std::max)(64, 1 << (Log2_32_Ceil(OldNumEntries) + 1));
+     if (NewNumBuckets == NumBuckets) {
+       this->BaseT::initEmpty();
+       return;
+diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h
+index 4559864ed231206b098936dae4fc378bfa986371..84f4d0931a30f4be29549354c85cb4c0489e14c9 100644
+--- a/llvm/include/llvm/ADT/SmallVector.h
++++ b/llvm/include/llvm/ADT/SmallVector.h
+@@ -55,12 +55,12 @@ protected:
+ 
+   /// The maximum value of the Size_T used.
+   static constexpr size_t SizeTypeMax() {
+-    return std::numeric_limits<Size_T>::max();
++    return (std::numeric_limits<Size_T>::max)();
+   }
+ 
+   SmallVectorBase() = delete;
+   SmallVectorBase(void *FirstEl, size_t TotalCapacity)
+-      : BeginX(FirstEl), Capacity(TotalCapacity) {}
++      : BeginX(FirstEl), Capacity(static_cast<unsigned>(TotalCapacity)) {}
+ 
+   /// This is a helper for \a grow() that's out of line to reduce code
+   /// duplication.  This function will report a fatal error if it can't grow at
+@@ -99,7 +99,7 @@ protected:
+   /// This does not construct or destroy any elements in the vector.
+   void set_size(size_t N) {
+     assert(N <= capacity());
+-    Size = N;
++    Size = static_cast<unsigned>(N);
+   }
+ };
+ 
+@@ -279,7 +279,7 @@ public:
+ 
+   size_type size_in_bytes() const { return size() * sizeof(T); }
+   size_type max_size() const {
+-    return std::min(this->SizeTypeMax(), size_type(-1) / sizeof(T));
++    return (std::min)(this->SizeTypeMax(), size_type(-1) / sizeof(T));
+   }
+ 
+   size_t capacity_in_bytes() const { return capacity() * sizeof(T); }
+@@ -467,7 +467,7 @@ void SmallVectorTemplateBase<T, TriviallyCopyable>::takeAllocationForGrow(
+     free(this->begin());
+ 
+   this->BeginX = NewElts;
+-  this->Capacity = NewCapacity;
++  this->Capacity = static_cast<unsigned>(NewCapacity);
+ }
+ 
+ /// SmallVectorTemplateBase<TriviallyCopyable = true> - This is where we put
+@@ -712,7 +712,7 @@ public:
+     }
+ 
+     // Assign over existing elements.
+-    std::fill_n(this->begin(), std::min(NumElts, this->size()), Elt);
++    std::fill_n(this->begin(), (std::min)(NumElts, this->size()), Elt);
+     if (NumElts > this->size())
+       std::uninitialized_fill_n(this->end(), NumElts - this->size(), Elt);
+     else if (NumElts < this->size())
+diff --git a/llvm/include/llvm/Support/ConvertUTF.h b/llvm/include/llvm/Support/ConvertUTF.h
+index 5c0e3009c25446a34882fb98329b1d955231bb39..72321022beb373945f7935ed72944fd68eb7d02f 100644
+--- a/llvm/include/llvm/Support/ConvertUTF.h
++++ b/llvm/include/llvm/Support/ConvertUTF.h
+@@ -127,7 +127,7 @@ namespace llvm {
+ typedef unsigned int    UTF32;  /* at least 32 bits */
+ typedef unsigned short  UTF16;  /* at least 16 bits */
+ typedef unsigned char   UTF8;   /* typically 8 bits */
+-typedef unsigned char   Boolean; /* 0 or 1 */
++typedef bool   Boolean; /* 0 or 1 */
+ 
+ /* Some fundamental constants */
+ #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
+index dc095941fdc8a9f2b3b822e6e014f0640676c0d3..0bd572d07fcbf2ff56998dbf366215068b62f527 100644
+--- a/llvm/include/llvm/Support/MathExtras.h
++++ b/llvm/include/llvm/Support/MathExtras.h
+@@ -311,26 +311,26 @@ template <> constexpr inline size_t CTLog2<1>() { return 0; }
+ /// (32 bit edition.)
+ /// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
+ inline unsigned Log2_32(uint32_t Value) {
+-  return 31 - llvm::countl_zero(Value);
++  return static_cast<unsigned>(31 - llvm::countl_zero(Value));
+ }
+ 
+ /// Return the floor log base 2 of the specified value, -1 if the value is zero.
+ /// (64 bit edition.)
+ inline unsigned Log2_64(uint64_t Value) {
+-  return 63 - llvm::countl_zero(Value);
++  return static_cast<unsigned>(63 - llvm::countl_zero(Value));
+ }
+ 
+ /// Return the ceil log base 2 of the specified value, 32 if the value is zero.
+ /// (32 bit edition).
+ /// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
+ inline unsigned Log2_32_Ceil(uint32_t Value) {
+-  return 32 - llvm::countl_zero(Value - 1);
++  return static_cast<unsigned>(32 - llvm::countl_zero(Value - 1));
+ }
+ 
+ /// Return the ceil log base 2 of the specified value, 64 if the value is zero.
+ /// (64 bit edition.)
+ inline unsigned Log2_64_Ceil(uint64_t Value) {
+-  return 64 - llvm::countl_zero(Value - 1);
++  return static_cast<unsigned>(64 - llvm::countl_zero(Value - 1));
+ }
+ 
+ /// A and B are either alignments or offsets. Return the minimum alignment that
+@@ -479,7 +479,7 @@ SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
+   T Z = X + Y;
+   Overflowed = (Z < X || Z < Y);
+   if (Overflowed)
+-    return std::numeric_limits<T>::max();
++    return (std::numeric_limits<T>::max)();
+   else
+     return Z;
+ }
+@@ -492,7 +492,7 @@ std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y, T Z,
+   bool Overflowed = false;
+   T XY = SaturatingAdd(X, Y, &Overflowed);
+   if (Overflowed)
+-    return SaturatingAdd(std::numeric_limits<T>::max(), T(1), Args...);
++    return SaturatingAdd((std::numeric_limits<T>::max)(), T(1), Args...);
+   return SaturatingAdd(XY, Z, Args...);
+ }
+ 
+@@ -516,7 +516,7 @@ SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
+   // Special case: if X or Y is 0, Log2_64 gives -1, and Log2Z
+   // will necessarily be less than Log2Max as desired.
+   int Log2Z = Log2_64(X) + Log2_64(Y);
+-  const T Max = std::numeric_limits<T>::max();
++  const T Max = (std::numeric_limits<T>::max)();
+   int Log2Max = Log2_64(Max);
+   if (Log2Z < Log2Max) {
+     return X * Y;
+@@ -636,9 +636,9 @@ std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
+   // Check how the max allowed absolute value (2^n for negative, 2^(n-1) for
+   // positive) divided by an argument compares to the other.
+   if (IsNegative)
+-    return UX > (static_cast<U>(std::numeric_limits<T>::max()) + U(1)) / UY;
++    return UX > (static_cast<U>((std::numeric_limits<T>::max)()) + U(1)) / UY;
+   else
+-    return UX > (static_cast<U>(std::numeric_limits<T>::max())) / UY;
++    return UX > (static_cast<U>((std::numeric_limits<T>::max)())) / UY;
+ }
+ 
+ } // End llvm namespace
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0003-Change-unique_function-storage-size.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0003-Change-unique_function-storage-size.patch
new file mode 100644
index 0000000..c4e5176
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0003-Change-unique_function-storage-size.patch
@@ -0,0 +1,31 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sat, 7 May 2022 22:13:55 -0400
+Subject: [PATCH 03/31] Change unique_function storage size
+
+---
+ llvm/include/llvm/ADT/FunctionExtras.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/llvm/include/llvm/ADT/FunctionExtras.h b/llvm/include/llvm/ADT/FunctionExtras.h
+index 4cf1de488c7bde2692d4878ccb4c4d60241e3a66..9d10b16e3cbe9c0df818a3254fcd3a6032d54b39 100644
+--- a/llvm/include/llvm/ADT/FunctionExtras.h
++++ b/llvm/include/llvm/ADT/FunctionExtras.h
+@@ -78,7 +78,7 @@ using EnableIfCallable = std::enable_if_t<std::disjunction<
+ 
+ template <typename ReturnT, typename... ParamTs> class UniqueFunctionBase {
+ protected:
+-  static constexpr size_t InlineStorageSize = sizeof(void *) * 3;
++  static constexpr size_t InlineStorageSize = sizeof(void *) * 4;
+ 
+   template <typename T, class = void>
+   struct IsSizeLessThanThresholdT : std::false_type {};
+@@ -157,7 +157,7 @@ protected:
+         "Should always use all of the out-of-line storage for inline storage!");
+ 
+     // For in-line storage, we just provide an aligned character buffer. We
+-    // provide three pointers worth of storage here.
++    // provide four pointers worth of storage here.
+     // This is mutable as an inlined `const unique_function<void() const>` may
+     // still modify its own mutable members.
+     mutable std::aligned_storage_t<InlineStorageSize, alignof(void *)>
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0003-Wrap-std-min-max-calls-in-parens-for-Windows-warning.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0003-Wrap-std-min-max-calls-in-parens-for-Windows-warning.patch
deleted file mode 100644
index a67e326..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0003-Wrap-std-min-max-calls-in-parens-for-Windows-warning.patch
+++ /dev/null
@@ -1,208 +0,0 @@
-From 5fccde024bea117d90d215390f09c7d779195ea5 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sat, 7 May 2022 22:12:41 -0400
-Subject: [PATCH 03/28] Wrap std::min/max calls in parens, for Windows warnings
-
----
- llvm/include/llvm/ADT/DenseMap.h       |  4 ++--
- llvm/include/llvm/ADT/SmallVector.h    | 12 ++++++------
- llvm/include/llvm/Support/ConvertUTF.h |  2 +-
- llvm/include/llvm/Support/MathExtras.h | 22 +++++++++++-----------
- llvm/lib/Support/SmallVector.cpp       |  2 +-
- 5 files changed, 21 insertions(+), 21 deletions(-)
-
-diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
-index 7673b66ca..975c3b97e 100644
---- a/llvm/include/llvm/ADT/DenseMap.h
-+++ b/llvm/include/llvm/ADT/DenseMap.h
-@@ -390,7 +390,7 @@ protected:
-       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);
-+    return static_cast<unsigned>(NextPowerOf2(NumEntries * 4 / 3 + 1));
-   }
- 
-   void moveFromOldBuckets(BucketT *OldBucketsBegin, BucketT *OldBucketsEnd) {
-@@ -826,7 +826,7 @@ public:
-     // Reduce the number of buckets.
-     unsigned NewNumBuckets = 0;
-     if (OldNumEntries)
--      NewNumBuckets = std::max(64, 1 << (Log2_32_Ceil(OldNumEntries) + 1));
-+      NewNumBuckets = (std::max)(64, 1 << (Log2_32_Ceil(OldNumEntries) + 1));
-     if (NewNumBuckets == NumBuckets) {
-       this->BaseT::initEmpty();
-       return;
-diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h
-index a4a790323..8686f7bb5 100644
---- a/llvm/include/llvm/ADT/SmallVector.h
-+++ b/llvm/include/llvm/ADT/SmallVector.h
-@@ -49,12 +49,12 @@ protected:
- 
-   /// The maximum value of the Size_T used.
-   static constexpr size_t SizeTypeMax() {
--    return std::numeric_limits<Size_T>::max();
-+    return (std::numeric_limits<Size_T>::max)();
-   }
- 
-   SmallVectorBase() = delete;
-   SmallVectorBase(void *FirstEl, size_t TotalCapacity)
--      : BeginX(FirstEl), Capacity(TotalCapacity) {}
-+      : BeginX(FirstEl), Capacity(static_cast<unsigned>(TotalCapacity)) {}
- 
-   /// This is a helper for \a grow() that's out of line to reduce code
-   /// duplication.  This function will report a fatal error if it can't grow at
-@@ -79,7 +79,7 @@ protected:
-   /// This does not construct or destroy any elements in the vector.
-   void set_size(size_t N) {
-     assert(N <= capacity());
--    Size = N;
-+    Size = static_cast<unsigned>(N);
-   }
- };
- 
-@@ -259,7 +259,7 @@ public:
- 
-   size_type size_in_bytes() const { return size() * sizeof(T); }
-   size_type max_size() const {
--    return std::min(this->SizeTypeMax(), size_type(-1) / sizeof(T));
-+    return (std::min)(this->SizeTypeMax(), size_type(-1) / sizeof(T));
-   }
- 
-   size_t capacity_in_bytes() const { return capacity() * sizeof(T); }
-@@ -444,7 +444,7 @@ void SmallVectorTemplateBase<T, TriviallyCopyable>::takeAllocationForGrow(
-     free(this->begin());
- 
-   this->BeginX = NewElts;
--  this->Capacity = NewCapacity;
-+  this->Capacity = static_cast<unsigned>(NewCapacity);
- }
- 
- /// SmallVectorTemplateBase<TriviallyCopyable = true> - This is where we put
-@@ -693,7 +693,7 @@ public:
-     }
- 
-     // Assign over existing elements.
--    std::fill_n(this->begin(), std::min(NumElts, this->size()), Elt);
-+    std::fill_n(this->begin(), (std::min)(NumElts, this->size()), Elt);
-     if (NumElts > this->size())
-       std::uninitialized_fill_n(this->end(), NumElts - this->size(), Elt);
-     else if (NumElts < this->size())
-diff --git a/llvm/include/llvm/Support/ConvertUTF.h b/llvm/include/llvm/Support/ConvertUTF.h
-index 7f1527f51..b085c8a17 100644
---- a/llvm/include/llvm/Support/ConvertUTF.h
-+++ b/llvm/include/llvm/Support/ConvertUTF.h
-@@ -112,7 +112,7 @@ namespace llvm {
- typedef unsigned int    UTF32;  /* at least 32 bits */
- typedef unsigned short  UTF16;  /* at least 16 bits */
- typedef unsigned char   UTF8;   /* typically 8 bits */
--typedef unsigned char   Boolean; /* 0 or 1 */
-+typedef bool   Boolean; /* 0 or 1 */
- 
- /* Some fundamental constants */
- #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
-diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
-index 753b1998c..db9fbc148 100644
---- a/llvm/include/llvm/Support/MathExtras.h
-+++ b/llvm/include/llvm/Support/MathExtras.h
-@@ -97,7 +97,7 @@ template <typename T, std::size_t SizeOfT> struct TrailingZerosCounter {
-     // Bisection method.
-     unsigned ZeroBits = 0;
-     T Shift = std::numeric_limits<T>::digits >> 1;
--    T Mask = std::numeric_limits<T>::max() >> Shift;
-+    T Mask = (std::numeric_limits<T>::max)() >> Shift;
-     while (Shift) {
-       if ((Val & Mask) == 0) {
-         Val >>= Shift;
-@@ -238,7 +238,7 @@ unsigned countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
- ///   valid arguments.
- template <typename T> T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) {
-   if (ZB == ZB_Max && Val == 0)
--    return std::numeric_limits<T>::max();
-+    return (std::numeric_limits<T>::max)();
- 
-   return countTrailingZeros(Val, ZB_Undefined);
- }
-@@ -279,7 +279,7 @@ template <typename T> T maskLeadingZeros(unsigned N) {
- ///   valid arguments.
- template <typename T> T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) {
-   if (ZB == ZB_Max && Val == 0)
--    return std::numeric_limits<T>::max();
-+    return (std::numeric_limits<T>::max)();
- 
-   // Use ^ instead of - because both gcc and llvm can remove the associated ^
-   // in the __builtin_clz intrinsic on x86.
-@@ -594,26 +594,26 @@ inline double Log2(double Value) {
- /// (32 bit edition.)
- /// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
- inline unsigned Log2_32(uint32_t Value) {
--  return 31 - countLeadingZeros(Value);
-+  return static_cast<unsigned>(31 - countLeadingZeros(Value));
- }
- 
- /// Return the floor log base 2 of the specified value, -1 if the value is zero.
- /// (64 bit edition.)
- inline unsigned Log2_64(uint64_t Value) {
--  return 63 - countLeadingZeros(Value);
-+  return static_cast<unsigned>(63 - countLeadingZeros(Value));
- }
- 
- /// Return the ceil log base 2 of the specified value, 32 if the value is zero.
- /// (32 bit edition).
- /// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
- inline unsigned Log2_32_Ceil(uint32_t Value) {
--  return 32 - countLeadingZeros(Value - 1);
-+  return static_cast<unsigned>(32 - countLeadingZeros(Value - 1));
- }
- 
- /// Return the ceil log base 2 of the specified value, 64 if the value is zero.
- /// (64 bit edition.)
- inline unsigned Log2_64_Ceil(uint64_t Value) {
--  return 64 - countLeadingZeros(Value - 1);
-+  return static_cast<unsigned>(64 - countLeadingZeros(Value - 1));
- }
- 
- /// Return the greatest common divisor of the values using Euclid's algorithm.
-@@ -807,7 +807,7 @@ SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
-   T Z = X + Y;
-   Overflowed = (Z < X || Z < Y);
-   if (Overflowed)
--    return std::numeric_limits<T>::max();
-+    return (std::numeric_limits<T>::max)();
-   else
-     return Z;
- }
-@@ -832,7 +832,7 @@ SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
-   // Special case: if X or Y is 0, Log2_64 gives -1, and Log2Z
-   // will necessarily be less than Log2Max as desired.
-   int Log2Z = Log2_64(X) + Log2_64(Y);
--  const T Max = std::numeric_limits<T>::max();
-+  const T Max = (std::numeric_limits<T>::max)();
-   int Log2Max = Log2_64(Max);
-   if (Log2Z < Log2Max) {
-     return X * Y;
-@@ -952,9 +952,9 @@ std::enable_if_t<std::is_signed<T>::value, T> MulOverflow(T X, T Y, T &Result) {
-   // Check how the max allowed absolute value (2^n for negative, 2^(n-1) for
-   // positive) divided by an argument compares to the other.
-   if (IsNegative)
--    return UX > (static_cast<U>(std::numeric_limits<T>::max()) + U(1)) / UY;
-+    return UX > (static_cast<U>((std::numeric_limits<T>::max)()) + U(1)) / UY;
-   else
--    return UX > (static_cast<U>(std::numeric_limits<T>::max())) / UY;
-+    return UX > (static_cast<U>((std::numeric_limits<T>::max)())) / UY;
- }
- 
- } // End llvm namespace
-diff --git a/llvm/lib/Support/SmallVector.cpp b/llvm/lib/Support/SmallVector.cpp
-index 8bad715e4..a2b4899e1 100644
---- a/llvm/lib/Support/SmallVector.cpp
-+++ b/llvm/lib/Support/SmallVector.cpp
-@@ -104,7 +104,7 @@ static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) {
-   // In theory 2*capacity can overflow if the capacity is 64 bit, but the
-   // original capacity would never be large enough for this to be a problem.
-   size_t NewCapacity = 2 * OldCapacity + 1; // Always grow.
--  return std::min(std::max(NewCapacity, MinSize), MaxSize);
-+  return (std::min)((std::max)(NewCapacity, MinSize), MaxSize);
- }
- 
- // Note: Moving this function into the header may cause performance regression.
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0004-Change-unique_function-storage-size.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0004-Change-unique_function-storage-size.patch
deleted file mode 100644
index 6e57262..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0004-Change-unique_function-storage-size.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 376285281b6173ee3d6650d71148bc173e4a9f7a Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sat, 7 May 2022 22:13:55 -0400
-Subject: [PATCH 04/28] Change unique_function storage size
-
----
- llvm/include/llvm/ADT/FunctionExtras.h | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/llvm/include/llvm/ADT/FunctionExtras.h b/llvm/include/llvm/ADT/FunctionExtras.h
-index 5a37417dd..8a9d78f41 100644
---- a/llvm/include/llvm/ADT/FunctionExtras.h
-+++ b/llvm/include/llvm/ADT/FunctionExtras.h
-@@ -78,7 +78,7 @@ using EnableIfCallable = std::enable_if_t<llvm::disjunction<
- 
- template <typename ReturnT, typename... ParamTs> class UniqueFunctionBase {
- protected:
--  static constexpr size_t InlineStorageSize = sizeof(void *) * 3;
-+  static constexpr size_t InlineStorageSize = sizeof(void *) * 4;
- 
-   template <typename T, class = void>
-   struct IsSizeLessThanThresholdT : std::false_type {};
-@@ -157,7 +157,7 @@ protected:
-         "Should always use all of the out-of-line storage for inline storage!");
- 
-     // For in-line storage, we just provide an aligned character buffer. We
--    // provide three pointers worth of storage here.
-+    // provide four pointers worth of storage here.
-     // This is mutable as an inlined `const unique_function<void() const>` may
-     // still modify its own mutable members.
-     mutable
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0004-Threading-updates.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0004-Threading-updates.patch
new file mode 100644
index 0000000..98c1d8a
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0004-Threading-updates.patch
@@ -0,0 +1,185 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sat, 7 May 2022 22:17:19 -0400
+Subject: [PATCH 04/31] Threading updates
+
+- Remove guards for threads and exception
+- Prefer scope gaurd over lock gaurd
+---
+ llvm/include/llvm/Support/Compiler.h |  6 -----
+ llvm/lib/Support/ErrorHandling.cpp   | 38 +++++-----------------------
+ llvm/lib/Support/ManagedStatic.cpp   | 10 ++++----
+ 3 files changed, 11 insertions(+), 43 deletions(-)
+
+diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h
+index 92376629c607461061bc60597a47aed1e535af52..2662839b27bf368cd5da0668099c4b44cbc6435d 100644
+--- a/llvm/include/llvm/Support/Compiler.h
++++ b/llvm/include/llvm/Support/Compiler.h
+@@ -530,7 +530,6 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
+ /// initialize to some constant value. In almost all circumstances this is most
+ /// appropriate for use with a pointer, integer, or small aggregation of
+ /// pointers and integers.
+-#if LLVM_ENABLE_THREADS
+ #if __has_feature(cxx_thread_local) || defined(_MSC_VER)
+ #define LLVM_THREAD_LOCAL thread_local
+ #else
+@@ -538,11 +537,6 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
+ // we only need the restricted functionality that provides.
+ #define LLVM_THREAD_LOCAL __thread
+ #endif
+-#else // !LLVM_ENABLE_THREADS
+-// If threading is disabled entirely, this compiles to nothing and you get
+-// a normal global variable.
+-#define LLVM_THREAD_LOCAL
+-#endif
+ 
+ /// \macro LLVM_ENABLE_EXCEPTIONS
+ /// Whether LLVM is built with exception support.
+diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
+index 0aa13a0f78eb370b2a673ca4a773f26820575052..637b669a7d0dae69ef4b34955f21a9fb8ba1276e 100644
+--- a/llvm/lib/Support/ErrorHandling.cpp
++++ b/llvm/lib/Support/ErrorHandling.cpp
+@@ -44,7 +44,6 @@ static void *ErrorHandlerUserData = nullptr;
+ static fatal_error_handler_t BadAllocErrorHandler = nullptr;
+ static void *BadAllocErrorHandlerUserData = nullptr;
+ 
+-#if LLVM_ENABLE_THREADS == 1
+ // 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.
+@@ -58,22 +57,17 @@ static void *BadAllocErrorHandlerUserData = nullptr;
+ // builds. We can remove these ifdefs if that script goes away.
+ static std::mutex ErrorHandlerMutex;
+ static std::mutex BadAllocErrorHandlerMutex;
+-#endif
+ 
+ void llvm::install_fatal_error_handler(fatal_error_handler_t handler,
+                                        void *user_data) {
+-#if LLVM_ENABLE_THREADS == 1
+-  std::lock_guard<std::mutex> Lock(ErrorHandlerMutex);
+-#endif
++  std::scoped_lock Lock(ErrorHandlerMutex);
+   assert(!ErrorHandler && "Error handler already registered!\n");
+   ErrorHandler = handler;
+   ErrorHandlerUserData = user_data;
+ }
+ 
+ void llvm::remove_fatal_error_handler() {
+-#if LLVM_ENABLE_THREADS == 1
+-  std::lock_guard<std::mutex> Lock(ErrorHandlerMutex);
+-#endif
++  std::scoped_lock Lock(ErrorHandlerMutex);
+   ErrorHandler = nullptr;
+   ErrorHandlerUserData = nullptr;
+ }
+@@ -92,9 +86,7 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
+   {
+     // Only acquire the mutex while reading the handler, so as not to invoke a
+     // user-supplied callback under a lock.
+-#if LLVM_ENABLE_THREADS == 1
+-    std::lock_guard<std::mutex> Lock(ErrorHandlerMutex);
+-#endif
++    std::scoped_lock Lock(ErrorHandlerMutex);
+     handler = ErrorHandler;
+     handlerData = ErrorHandlerUserData;
+   }
+@@ -126,18 +118,14 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
+ 
+ void llvm::install_bad_alloc_error_handler(fatal_error_handler_t handler,
+                                            void *user_data) {
+-#if LLVM_ENABLE_THREADS == 1
+-  std::lock_guard<std::mutex> Lock(BadAllocErrorHandlerMutex);
+-#endif
++  std::scoped_lock Lock(BadAllocErrorHandlerMutex);
+   assert(!ErrorHandler && "Bad alloc error handler already registered!\n");
+   BadAllocErrorHandler = handler;
+   BadAllocErrorHandlerUserData = user_data;
+ }
+ 
+ void llvm::remove_bad_alloc_error_handler() {
+-#if LLVM_ENABLE_THREADS == 1
+-  std::lock_guard<std::mutex> Lock(BadAllocErrorHandlerMutex);
+-#endif
++  std::scoped_lock Lock(BadAllocErrorHandlerMutex);
+   BadAllocErrorHandler = nullptr;
+   BadAllocErrorHandlerUserData = nullptr;
+ }
+@@ -148,9 +136,7 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
+   {
+     // Only acquire the mutex while reading the handler, so as not to invoke a
+     // user-supplied callback under a lock.
+-#if LLVM_ENABLE_THREADS == 1
+-    std::lock_guard<std::mutex> Lock(BadAllocErrorHandlerMutex);
+-#endif
++    std::scoped_lock Lock(BadAllocErrorHandlerMutex);
+     Handler = BadAllocErrorHandler;
+     HandlerData = BadAllocErrorHandlerUserData;
+   }
+@@ -160,10 +146,6 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
+     llvm_unreachable("bad alloc handler should not return");
+   }
+ 
+-#ifdef LLVM_ENABLE_EXCEPTIONS
+-  // If exceptions are enabled, make OOM in malloc look like OOM in new.
+-  throw std::bad_alloc();
+-#else
+   // Don't call the normal error handler. It may allocate memory. Directly write
+   // an OOM to stderr and abort.
+   const char *OOMMessage = "LLVM ERROR: out of memory\n";
+@@ -172,15 +154,8 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
+   (void)!::write(2, Reason, strlen(Reason));
+   (void)!::write(2, Newline, strlen(Newline));
+   abort();
+-#endif
+ }
+ 
+-#ifdef LLVM_ENABLE_EXCEPTIONS
+-// Do not set custom new handler if exceptions are enabled. In this case OOM
+-// errors are handled by throwing 'std::bad_alloc'.
+-void llvm::install_out_of_memory_new_handler() {
+-}
+-#else
+ // 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() {
+@@ -195,7 +170,6 @@ void llvm::install_out_of_memory_new_handler() {
+   assert((old == nullptr || old == out_of_memory_new_handler) &&
+          "new-handler already installed");
+ }
+-#endif
+ 
+ void llvm::llvm_unreachable_internal(const char *msg, const char *file,
+                                      unsigned line) {
+diff --git a/llvm/lib/Support/ManagedStatic.cpp b/llvm/lib/Support/ManagedStatic.cpp
+index a6ae67066ea0423334e8ee52106f220cd456e25e..fc798b7ec1b788e232c7374b9968dc71d4f506f0 100644
+--- a/llvm/lib/Support/ManagedStatic.cpp
++++ b/llvm/lib/Support/ManagedStatic.cpp
+@@ -12,23 +12,23 @@
+ 
+ #include "llvm/Support/ManagedStatic.h"
+ #include "llvm/Config/config.h"
+-#include "llvm/Support/Threading.h"
++#include "llvm/Support/mutex.h"
+ #include <cassert>
+ #include <mutex>
+ using namespace llvm;
+ 
+ static const ManagedStaticBase *StaticList = nullptr;
+ 
+-static std::recursive_mutex *getManagedStaticMutex() {
+-  static std::recursive_mutex m;
++static llvm::mutex *getManagedStaticMutex() {
++  static llvm::mutex m;
+   return &m;
+ }
+ 
+ void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(),
+                                               void (*Deleter)(void*)) const {
+   assert(Creator);
+-  if (llvm_is_multithreaded()) {
+-    std::lock_guard<std::recursive_mutex> Lock(*getManagedStaticMutex());
++  if (1) {
++    std::scoped_lock Lock(*getManagedStaticMutex());
+ 
+     if (!Ptr.load(std::memory_order_relaxed)) {
+       void *Tmp = Creator();
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0005-Threading-updates.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0005-Threading-updates.patch
deleted file mode 100644
index 1ccd217..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0005-Threading-updates.patch
+++ /dev/null
@@ -1,185 +0,0 @@
-From bc86b62f72cbb76a0911996f4b1c6ce476cd1fac Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sat, 7 May 2022 22:17:19 -0400
-Subject: [PATCH 05/28] Threading updates
-
-- Remove guards for threads and exception
-- Prefer scope gaurd over lock gaurd
----
- llvm/include/llvm/Support/Compiler.h |  6 -----
- llvm/lib/Support/ErrorHandling.cpp   | 38 +++++-----------------------
- llvm/lib/Support/ManagedStatic.cpp   | 10 ++++----
- 3 files changed, 11 insertions(+), 43 deletions(-)
-
-diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h
-index f5d726ec8..ede1cb172 100644
---- a/llvm/include/llvm/Support/Compiler.h
-+++ b/llvm/include/llvm/Support/Compiler.h
-@@ -540,7 +540,6 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
- /// initialize to some constant value. In almost all circumstances this is most
- /// appropriate for use with a pointer, integer, or small aggregation of
- /// pointers and integers.
--#if LLVM_ENABLE_THREADS
- #if __has_feature(cxx_thread_local) || defined(_MSC_VER)
- #define LLVM_THREAD_LOCAL thread_local
- #else
-@@ -548,11 +547,6 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
- // we only need the restricted functionality that provides.
- #define LLVM_THREAD_LOCAL __thread
- #endif
--#else // !LLVM_ENABLE_THREADS
--// If threading is disabled entirely, this compiles to nothing and you get
--// a normal global variable.
--#define LLVM_THREAD_LOCAL
--#endif
- 
- /// \macro LLVM_ENABLE_EXCEPTIONS
- /// Whether LLVM is built with exception support.
-diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
-index 8ae8fb8b4..89440b5ab 100644
---- a/llvm/lib/Support/ErrorHandling.cpp
-+++ b/llvm/lib/Support/ErrorHandling.cpp
-@@ -44,7 +44,6 @@ static void *ErrorHandlerUserData = nullptr;
- static fatal_error_handler_t BadAllocErrorHandler = nullptr;
- static void *BadAllocErrorHandlerUserData = nullptr;
- 
--#if LLVM_ENABLE_THREADS == 1
- // 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.
-@@ -58,22 +57,17 @@ static void *BadAllocErrorHandlerUserData = nullptr;
- // builds. We can remove these ifdefs if that script goes away.
- static std::mutex ErrorHandlerMutex;
- static std::mutex BadAllocErrorHandlerMutex;
--#endif
- 
- void llvm::install_fatal_error_handler(fatal_error_handler_t handler,
-                                        void *user_data) {
--#if LLVM_ENABLE_THREADS == 1
--  std::lock_guard<std::mutex> Lock(ErrorHandlerMutex);
--#endif
-+  std::scoped_lock Lock(ErrorHandlerMutex);
-   assert(!ErrorHandler && "Error handler already registered!\n");
-   ErrorHandler = handler;
-   ErrorHandlerUserData = user_data;
- }
- 
- void llvm::remove_fatal_error_handler() {
--#if LLVM_ENABLE_THREADS == 1
--  std::lock_guard<std::mutex> Lock(ErrorHandlerMutex);
--#endif
-+  std::scoped_lock Lock(ErrorHandlerMutex);
-   ErrorHandler = nullptr;
-   ErrorHandlerUserData = nullptr;
- }
-@@ -92,9 +86,7 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
-   {
-     // Only acquire the mutex while reading the handler, so as not to invoke a
-     // user-supplied callback under a lock.
--#if LLVM_ENABLE_THREADS == 1
--    std::lock_guard<std::mutex> Lock(ErrorHandlerMutex);
--#endif
-+    std::scoped_lock Lock(ErrorHandlerMutex);
-     handler = ErrorHandler;
-     handlerData = ErrorHandlerUserData;
-   }
-@@ -123,18 +115,14 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
- 
- void llvm::install_bad_alloc_error_handler(fatal_error_handler_t handler,
-                                            void *user_data) {
--#if LLVM_ENABLE_THREADS == 1
--  std::lock_guard<std::mutex> Lock(BadAllocErrorHandlerMutex);
--#endif
-+  std::scoped_lock Lock(BadAllocErrorHandlerMutex);
-   assert(!ErrorHandler && "Bad alloc error handler already registered!\n");
-   BadAllocErrorHandler = handler;
-   BadAllocErrorHandlerUserData = user_data;
- }
- 
- void llvm::remove_bad_alloc_error_handler() {
--#if LLVM_ENABLE_THREADS == 1
--  std::lock_guard<std::mutex> Lock(BadAllocErrorHandlerMutex);
--#endif
-+  std::scoped_lock Lock(BadAllocErrorHandlerMutex);
-   BadAllocErrorHandler = nullptr;
-   BadAllocErrorHandlerUserData = nullptr;
- }
-@@ -145,9 +133,7 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
-   {
-     // Only acquire the mutex while reading the handler, so as not to invoke a
-     // user-supplied callback under a lock.
--#if LLVM_ENABLE_THREADS == 1
--    std::lock_guard<std::mutex> Lock(BadAllocErrorHandlerMutex);
--#endif
-+    std::scoped_lock Lock(BadAllocErrorHandlerMutex);
-     Handler = BadAllocErrorHandler;
-     HandlerData = BadAllocErrorHandlerUserData;
-   }
-@@ -157,10 +143,6 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
-     llvm_unreachable("bad alloc handler should not return");
-   }
- 
--#ifdef LLVM_ENABLE_EXCEPTIONS
--  // If exceptions are enabled, make OOM in malloc look like OOM in new.
--  throw std::bad_alloc();
--#else
-   // Don't call the normal error handler. It may allocate memory. Directly write
-   // an OOM to stderr and abort.
-   const char *OOMMessage = "LLVM ERROR: out of memory\n";
-@@ -169,15 +151,8 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
-   (void)!::write(2, Reason, strlen(Reason));
-   (void)!::write(2, Newline, strlen(Newline));
-   abort();
--#endif
- }
- 
--#ifdef LLVM_ENABLE_EXCEPTIONS
--// Do not set custom new handler if exceptions are enabled. In this case OOM
--// errors are handled by throwing 'std::bad_alloc'.
--void llvm::install_out_of_memory_new_handler() {
--}
--#else
- // 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() {
-@@ -192,7 +167,6 @@ void llvm::install_out_of_memory_new_handler() {
-   assert((old == nullptr || old == out_of_memory_new_handler) &&
-          "new-handler already installed");
- }
--#endif
- 
- void llvm::llvm_unreachable_internal(const char *msg, const char *file,
-                                      unsigned line) {
-diff --git a/llvm/lib/Support/ManagedStatic.cpp b/llvm/lib/Support/ManagedStatic.cpp
-index a6ae67066..fc798b7ec 100644
---- a/llvm/lib/Support/ManagedStatic.cpp
-+++ b/llvm/lib/Support/ManagedStatic.cpp
-@@ -12,23 +12,23 @@
- 
- #include "llvm/Support/ManagedStatic.h"
- #include "llvm/Config/config.h"
--#include "llvm/Support/Threading.h"
-+#include "llvm/Support/mutex.h"
- #include <cassert>
- #include <mutex>
- using namespace llvm;
- 
- static const ManagedStaticBase *StaticList = nullptr;
- 
--static std::recursive_mutex *getManagedStaticMutex() {
--  static std::recursive_mutex m;
-+static llvm::mutex *getManagedStaticMutex() {
-+  static llvm::mutex m;
-   return &m;
- }
- 
- void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(),
-                                               void (*Deleter)(void*)) const {
-   assert(Creator);
--  if (llvm_is_multithreaded()) {
--    std::lock_guard<std::recursive_mutex> Lock(*getManagedStaticMutex());
-+  if (1) {
-+    std::scoped_lock Lock(*getManagedStaticMutex());
- 
-     if (!Ptr.load(std::memory_order_relaxed)) {
-       void *Tmp = Creator();
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0005-ifdef-guard-safety.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0005-ifdef-guard-safety.patch
new file mode 100644
index 0000000..e74cf17
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0005-ifdef-guard-safety.patch
@@ -0,0 +1,294 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sat, 7 May 2022 22:28:13 -0400
+Subject: [PATCH 05/31] \#ifdef guard safety
+
+Prevents redefinition if someone is pulling in real LLVM, since the macros are in global namespace
+---
+ llvm/include/llvm/Support/Compiler.h | 42 ++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h
+index 2662839b27bf368cd5da0668099c4b44cbc6435d..ce75702c8c6f99780ecdb6dc77e848519998685b 100644
+--- a/llvm/include/llvm/Support/Compiler.h
++++ b/llvm/include/llvm/Support/Compiler.h
+@@ -90,6 +90,7 @@
+ /// * 1928: VS2019, version 16.8 + 16.9
+ /// * 1929: VS2019, version 16.10 + 16.11
+ /// * 1930: VS2022, version 17.0
++#ifndef LLVM_MSC_PREREQ
+ #ifdef _MSC_VER
+ #define LLVM_MSC_PREREQ(version) (_MSC_VER >= (version))
+ 
+@@ -103,6 +104,7 @@
+ #else
+ #define LLVM_MSC_PREREQ(version) 0
+ #endif
++#endif
+ 
+ /// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked
+ /// into a shared library, then the class should be private to the library and
+@@ -139,17 +141,21 @@
+ #define LLVM_EXTERNAL_VISIBILITY
+ #endif
+ 
++#ifndef LLVM_PREFETCH
+ #if defined(__GNUC__)
+ #define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality)
+ #else
+ #define LLVM_PREFETCH(addr, rw, locality)
+ #endif
++#endif
+ 
++#ifndef LLVM_ATTRIBUTE_USED
+ #if __has_attribute(used)
+ #define LLVM_ATTRIBUTE_USED __attribute__((__used__))
+ #else
+ #define LLVM_ATTRIBUTE_USED
+ #endif
++#endif
+ 
+ #if defined(__clang__)
+ #define LLVM_DEPRECATED(MSG, FIX) __attribute__((deprecated(MSG, FIX)))
+@@ -178,11 +184,13 @@
+ // more portable solution:
+ //   (void)unused_var_name;
+ // Prefer cast-to-void wherever it is sufficient.
++#ifndef LLVM_ATTRIBUTE_UNUSED
+ #if __has_attribute(unused)
+ #define LLVM_ATTRIBUTE_UNUSED __attribute__((__unused__))
+ #else
+ #define LLVM_ATTRIBUTE_UNUSED
+ #endif
++#endif
+ 
+ // FIXME: Provide this for PE/COFF targets.
+ #if __has_attribute(weak) && !defined(__MINGW32__) && !defined(__CYGWIN__) &&  \
+@@ -192,6 +200,7 @@
+ #define LLVM_ATTRIBUTE_WEAK
+ #endif
+ 
++#ifndef LLVM_READNONE
+ // Prior to clang 3.2, clang did not accept any spelling of
+ // __has_attribute(const), so assume it is supported.
+ #if defined(__clang__) || defined(__GNUC__)
+@@ -200,13 +209,16 @@
+ #else
+ #define LLVM_READNONE
+ #endif
++#endif
+ 
++#ifndef LLVM_READONLY
+ #if __has_attribute(pure) || defined(__GNUC__)
+ // aka 'PURE' but following LLVM Conventions.
+ #define LLVM_READONLY __attribute__((__pure__))
+ #else
+ #define LLVM_READONLY
+ #endif
++#endif
+ 
+ #if __has_attribute(minsize)
+ #define LLVM_ATTRIBUTE_MINSIZE __attribute__((minsize))
+@@ -214,6 +226,7 @@
+ #define LLVM_ATTRIBUTE_MINSIZE
+ #endif
+ 
++#ifndef LLVM_LIKELY
+ #if __has_builtin(__builtin_expect) || defined(__GNUC__)
+ #define LLVM_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true)
+ #define LLVM_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false)
+@@ -221,9 +234,11 @@
+ #define LLVM_LIKELY(EXPR) (EXPR)
+ #define LLVM_UNLIKELY(EXPR) (EXPR)
+ #endif
++#endif
+ 
+ /// LLVM_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so,
+ /// mark a method "not for inlining".
++#ifndef LLVM_ATTRIBUTE_NOINLINE
+ #if __has_attribute(noinline)
+ #define LLVM_ATTRIBUTE_NOINLINE __attribute__((noinline))
+ #elif defined(_MSC_VER)
+@@ -231,9 +246,11 @@
+ #else
+ #define LLVM_ATTRIBUTE_NOINLINE
+ #endif
++#endif
+ 
+ /// LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do
+ /// so, mark a method "always inline" because it is performance sensitive.
++#ifndef LLVM_ATTRIBUTE_ALWAYS_INLINE
+ #if __has_attribute(always_inline)
+ #define LLVM_ATTRIBUTE_ALWAYS_INLINE inline __attribute__((always_inline))
+ #elif defined(_MSC_VER)
+@@ -241,6 +258,7 @@
+ #else
+ #define LLVM_ATTRIBUTE_ALWAYS_INLINE inline
+ #endif
++#endif
+ 
+ /// LLVM_ATTRIBUTE_NO_DEBUG - On compilers where we have a directive to do
+ /// so, mark a method "no debug" because debug info makes the debugger
+@@ -251,6 +269,7 @@
+ #define LLVM_ATTRIBUTE_NODEBUG
+ #endif
+ 
++#ifndef LLVM_ATTRIBUTE_RETURNS_NONNULL
+ #if __has_attribute(returns_nonnull)
+ #define LLVM_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
+ #elif defined(_MSC_VER)
+@@ -258,9 +277,11 @@
+ #else
+ #define LLVM_ATTRIBUTE_RETURNS_NONNULL
+ #endif
++#endif
+ 
+ /// \macro LLVM_ATTRIBUTE_RETURNS_NOALIAS Used to mark a function as returning a
+ /// pointer that does not alias any other valid pointer.
++#ifndef LLVM_ATTRIBUTE_RETURNS_NOALIAS
+ #ifdef __GNUC__
+ #define LLVM_ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__))
+ #elif defined(_MSC_VER)
+@@ -268,8 +289,10 @@
+ #else
+ #define LLVM_ATTRIBUTE_RETURNS_NOALIAS
+ #endif
++#endif
+ 
+ /// LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements.
++#ifndef LLVM_FALLTHROUGH
+ #if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(fallthrough)
+ #define LLVM_FALLTHROUGH [[fallthrough]]
+ #elif LLVM_HAS_CPP_ATTRIBUTE(gnu::fallthrough)
+@@ -281,6 +304,7 @@
+ #else
+ #define LLVM_FALLTHROUGH
+ #endif
++#endif
+ 
+ /// LLVM_REQUIRE_CONSTANT_INITIALIZATION - Apply this to globals to ensure that
+ /// they are constant initialized.
+@@ -309,11 +333,13 @@
+ 
+ /// LLVM_EXTENSION - Support compilers where we have a keyword to suppress
+ /// pedantic diagnostics.
++#ifndef LLVM_EXTENSION
+ #ifdef __GNUC__
+ #define LLVM_EXTENSION __extension__
+ #else
+ #define LLVM_EXTENSION
+ #endif
++#endif
+ 
+ /// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands
+ /// to an expression which states that it is undefined behavior for the
+@@ -322,14 +348,17 @@
+ /// '#else' is intentionally left out so that other macro logic (e.g.,
+ /// LLVM_ASSUME_ALIGNED and llvm_unreachable()) can detect whether
+ /// LLVM_BUILTIN_UNREACHABLE has a definition.
++#ifndef LLVM_BUILTIN_UNREACHABLE
+ #if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
+ # define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable()
+ #elif defined(_MSC_VER)
+ # define LLVM_BUILTIN_UNREACHABLE __assume(false)
+ #endif
++#endif
+ 
+ /// LLVM_BUILTIN_TRAP - On compilers which support it, expands to an expression
+ /// which causes the program to exit abnormally.
++#ifndef LLVM_BUILTIN_TRAP
+ #if __has_builtin(__builtin_trap) || defined(__GNUC__)
+ # define LLVM_BUILTIN_TRAP __builtin_trap()
+ #elif defined(_MSC_VER)
+@@ -341,10 +370,12 @@
+ #else
+ # define LLVM_BUILTIN_TRAP *(volatile int*)0x11 = 0
+ #endif
++#endif
+ 
+ /// LLVM_BUILTIN_DEBUGTRAP - On compilers which support it, expands to
+ /// an expression which causes the program to break while running
+ /// under a debugger.
++#ifndef LLVM_BUILTIN_DEBUGTRAP
+ #if __has_builtin(__builtin_debugtrap)
+ # define LLVM_BUILTIN_DEBUGTRAP __builtin_debugtrap()
+ #elif defined(_MSC_VER)
+@@ -358,9 +389,11 @@
+ // program to abort if encountered.
+ # define LLVM_BUILTIN_DEBUGTRAP
+ #endif
++#endif
+ 
+ /// \macro LLVM_ASSUME_ALIGNED
+ /// Returns a pointer with an assumed alignment.
++#ifndef LLVM_ASSUME_ALIGNED
+ #if __has_builtin(__builtin_assume_aligned) || defined(__GNUC__)
+ # define LLVM_ASSUME_ALIGNED(p, a) __builtin_assume_aligned(p, a)
+ #elif defined(LLVM_BUILTIN_UNREACHABLE)
+@@ -369,6 +402,7 @@
+ #else
+ # define LLVM_ASSUME_ALIGNED(p, a) (p)
+ #endif
++#endif
+ 
+ /// \macro LLVM_PACKED
+ /// Used to specify a packed structure.
+@@ -388,6 +422,7 @@
+ ///   long long l;
+ /// };
+ /// LLVM_PACKED_END
++#ifndef LLVM_PACKED
+ #ifdef _MSC_VER
+ # define LLVM_PACKED(d) __pragma(pack(push, 1)) d __pragma(pack(pop))
+ # define LLVM_PACKED_START __pragma(pack(push, 1))
+@@ -397,6 +432,7 @@
+ # define LLVM_PACKED_START _Pragma("pack(push, 1)")
+ # define LLVM_PACKED_END   _Pragma("pack(pop)")
+ #endif
++#endif
+ 
+ /// \macro LLVM_MEMORY_SANITIZER_BUILD
+ /// Whether LLVM itself is built with MemorySanitizer instrumentation.
+@@ -488,11 +524,13 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
+ 
+ /// \macro LLVM_NO_SANITIZE
+ /// Disable a particular sanitizer for a function.
++#ifndef LLVM_NO_SANITIZE
+ #if __has_attribute(no_sanitize)
+ #define LLVM_NO_SANITIZE(KIND) __attribute__((no_sanitize(KIND)))
+ #else
+ #define LLVM_NO_SANITIZE(KIND)
+ #endif
++#endif
+ 
+ /// Mark debug helper function definitions like dump() that should not be
+ /// stripped from debug builds.
+@@ -500,17 +538,20 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
+ /// `#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)` so they do always
+ /// get stripped in release builds.
+ // FIXME: Move this to a private config.h as it's not usable in public headers.
++#ifndef LLVM_DUMP_METHOD
+ #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ #define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED
+ #else
+ #define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE
+ #endif
++#endif
+ 
+ /// \macro LLVM_PRETTY_FUNCTION
+ /// Gets a user-friendly looking function signature for the current scope
+ /// using the best available method on each platform.  The exact format of the
+ /// resulting string is implementation specific and non-portable, so this should
+ /// only be used, for example, for logging or diagnostics.
++#ifndef LLVM_PRETTY_FUNCTION
+ #if defined(_MSC_VER)
+ #define LLVM_PRETTY_FUNCTION __FUNCSIG__
+ #elif defined(__GNUC__) || defined(__clang__)
+@@ -518,6 +559,7 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
+ #else
+ #define LLVM_PRETTY_FUNCTION __func__
+ #endif
++#endif
+ 
+ /// \macro LLVM_THREAD_LOCAL
+ /// A thread-local storage specifier which can be used with globals,
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0006-Explicitly-use-std.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0006-Explicitly-use-std.patch
new file mode 100644
index 0000000..0d841d9
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0006-Explicitly-use-std.patch
@@ -0,0 +1,115 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sat, 7 May 2022 22:37:34 -0400
+Subject: [PATCH 06/31] Explicitly use std::
+
+---
+ llvm/include/llvm/ADT/SmallSet.h       |  2 +-
+ llvm/lib/Support/ErrorHandling.cpp     |  2 +-
+ llvm/unittests/ADT/SmallPtrSetTest.cpp |  2 +-
+ llvm/unittests/ADT/SmallSetTest.cpp    | 10 +++++-----
+ llvm/unittests/ADT/StringMapTest.cpp   |  2 +-
+ 5 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/llvm/include/llvm/ADT/SmallSet.h b/llvm/include/llvm/ADT/SmallSet.h
+index aeee5f97799aea7e7588d7afba1e47b4fa3d8c7b..4969dfb0d61c2fad805c9cb7bc0184ea6d47bf23 100644
+--- a/llvm/include/llvm/ADT/SmallSet.h
++++ b/llvm/include/llvm/ADT/SmallSet.h
+@@ -269,7 +269,7 @@ bool operator==(const SmallSet<T, LN, C> &LHS, const SmallSet<T, RN, C> &RHS) {
+     return false;
+ 
+   // All elements in LHS must also be in RHS
+-  return all_of(LHS, [&RHS](const T &E) { return RHS.count(E); });
++  return std::all_of(LHS.begin(), LHS.end(), [&RHS](const T &E) { return RHS.count(E); });
+ }
+ 
+ /// Inequality comparison for SmallSet.
+diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
+index 637b669a7d0dae69ef4b34955f21a9fb8ba1276e..0b87b375de67dc18647e3ebe646bf323dd05e8c5 100644
+--- a/llvm/lib/Support/ErrorHandling.cpp
++++ b/llvm/lib/Support/ErrorHandling.cpp
+@@ -213,7 +213,7 @@ void LLVMResetFatalErrorHandler() {
+ // I'd rather not double the line count of the following.
+ #define MAP_ERR_TO_COND(x, y)                                                  \
+   case x:                                                                      \
+-    return make_error_code(errc::y)
++    return std::make_error_code(std::errc::y)
+ 
+ std::error_code llvm::mapWindowsError(unsigned EV) {
+   switch (EV) {
+diff --git a/llvm/unittests/ADT/SmallPtrSetTest.cpp b/llvm/unittests/ADT/SmallPtrSetTest.cpp
+index 7ed8670fd31ea2a14e6ba7f59a8ac8e35046890c..531f81ab5b3fc1dcff731230f3cb7649cb90aedf 100644
+--- a/llvm/unittests/ADT/SmallPtrSetTest.cpp
++++ b/llvm/unittests/ADT/SmallPtrSetTest.cpp
+@@ -298,7 +298,7 @@ TEST(SmallPtrSetTest, dereferenceAndIterate) {
+ 
+   // Sort.  We should hit the first element just once and the final element N
+   // times.
+-  llvm::sort(Found);
++  std::sort(std::begin(Found), std::end(Found));
+   for (auto F = std::begin(Found), E = std::end(Found); F != E; ++F)
+     EXPECT_EQ(F - Found + 1, *F);
+ }
+diff --git a/llvm/unittests/ADT/SmallSetTest.cpp b/llvm/unittests/ADT/SmallSetTest.cpp
+index b50b368ae663614f050c220432c05b32c201db00..f9d84fa8a42a7feaaffa3aa080e84574dc3671b3 100644
+--- a/llvm/unittests/ADT/SmallSetTest.cpp
++++ b/llvm/unittests/ADT/SmallSetTest.cpp
+@@ -11,8 +11,8 @@
+ //===----------------------------------------------------------------------===//
+ 
+ #include "llvm/ADT/SmallSet.h"
+-#include "llvm/ADT/STLExtras.h"
+ #include "gtest/gtest.h"
++#include <algorithm>
+ #include <string>
+ 
+ using namespace llvm;
+@@ -94,7 +94,7 @@ TEST(SmallSetTest, IteratorInt) {
+ 
+   std::vector<int> V(s1.begin(), s1.end());
+   // Make sure the elements are in the expected order.
+-  llvm::sort(V);
++  std::sort(V.begin(), V.end());
+   for (int i = 0; i < 3; i++)
+     EXPECT_EQ(i, V[i]);
+ 
+@@ -105,7 +105,7 @@ TEST(SmallSetTest, IteratorInt) {
+ 
+   V.assign(s1.begin(), s1.end());
+   // Make sure the elements are in the expected order.
+-  llvm::sort(V);
++  std::sort(V.begin(), V.end());
+   for (int i = 0; i < 6; i++)
+     EXPECT_EQ(i, V[i]);
+ }
+@@ -120,7 +120,7 @@ TEST(SmallSetTest, IteratorString) {
+   s1.insert("str 1");
+ 
+   std::vector<std::string> V(s1.begin(), s1.end());
+-  llvm::sort(V);
++  std::sort(V.begin(), V.end());
+   EXPECT_EQ(2u, s1.size());
+   EXPECT_EQ("str 1", V[0]);
+   EXPECT_EQ("str 2", V[1]);
+@@ -131,7 +131,7 @@ TEST(SmallSetTest, IteratorString) {
+ 
+   V.assign(s1.begin(), s1.end());
+   // Make sure the elements are in the expected order.
+-  llvm::sort(V);
++  std::sort(V.begin(), V.end());
+   EXPECT_EQ(4u, s1.size());
+   EXPECT_EQ("str 0", V[0]);
+   EXPECT_EQ("str 1", V[1]);
+diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
+index 7f10b3d7d3a8894b1ab0ac660268d94a8b89e082..acd8b566f9c7a6efc2c9204624c01104dd34daf6 100644
+--- a/llvm/unittests/ADT/StringMapTest.cpp
++++ b/llvm/unittests/ADT/StringMapTest.cpp
+@@ -343,7 +343,7 @@ TEST_F(StringMapTest, IterMapKeysSmallVector) {
+   Map["D"] = 3;
+ 
+   auto Keys = to_vector<4>(Map.keys());
+-  llvm::sort(Keys);
++  std::sort(Keys.begin(), Keys.end());
+ 
+   SmallVector<std::string_view, 4> Expected = {"A", "B", "C", "D"};
+   EXPECT_EQ(Expected, Keys);
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0006-ifdef-guard-safety.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0006-ifdef-guard-safety.patch
deleted file mode 100644
index bcb4950..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0006-ifdef-guard-safety.patch
+++ /dev/null
@@ -1,341 +0,0 @@
-From 008e921f77933f475174d74a6b70309c6fbe0771 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sat, 7 May 2022 22:28:13 -0400
-Subject: [PATCH 06/28] \#ifdef guard safety
-
-Prevents redefinition if someone is pulling in real LLVM, since the macros are in global namespace
----
- llvm/include/llvm/Support/Compiler.h | 50 ++++++++++++++++++++++++++++
- 1 file changed, 50 insertions(+)
-
-diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h
-index ede1cb172..8b8260b50 100644
---- a/llvm/include/llvm/Support/Compiler.h
-+++ b/llvm/include/llvm/Support/Compiler.h
-@@ -86,6 +86,7 @@
- /// * 1928: VS2019, version 16.8 + 16.9
- /// * 1929: VS2019, version 16.10 + 16.11
- /// * 1930: VS2022, version 17.0
-+#ifndef LLVM_MSC_PREREQ
- #ifdef _MSC_VER
- #define LLVM_MSC_PREREQ(version) (_MSC_VER >= (version))
- 
-@@ -99,6 +100,7 @@
- #else
- #define LLVM_MSC_PREREQ(version) 0
- #endif
-+#endif
- 
- /// Does the compiler support ref-qualifiers for *this?
- ///
-@@ -112,11 +114,13 @@
- ///
- /// This can be used to provide lvalue/rvalue overrides of member functions.
- /// The rvalue override should be guarded by LLVM_HAS_RVALUE_REFERENCE_THIS
-+#ifndef LLVM_LVALUE_FUNCTION
- #if LLVM_HAS_RVALUE_REFERENCE_THIS
- #define LLVM_LVALUE_FUNCTION &
- #else
- #define LLVM_LVALUE_FUNCTION
- #endif
-+#endif
- 
- /// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked
- /// into a shared library, then the class should be private to the library and
-@@ -140,21 +144,26 @@
- #define LLVM_EXTERNAL_VISIBILITY
- #endif
- 
-+#ifndef LLVM_PREFETCH
- #if defined(__GNUC__)
- #define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality)
- #else
- #define LLVM_PREFETCH(addr, rw, locality)
- #endif
-+#endif
- 
-+#ifndef LLVM_ATTRIBUTE_USED
- #if __has_attribute(used)
- #define LLVM_ATTRIBUTE_USED __attribute__((__used__))
- #else
- #define LLVM_ATTRIBUTE_USED
- #endif
-+#endif
- 
- /// LLVM_NODISCARD - Warn if a type or return value is discarded.
- 
- // Use the 'nodiscard' attribute in C++17 or newer mode.
-+#ifndef LLVM_NODISCARD
- #if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(nodiscard)
- #define LLVM_NODISCARD [[nodiscard]]
- #elif LLVM_HAS_CPP_ATTRIBUTE(clang::warn_unused_result)
-@@ -168,6 +177,7 @@
- #else
- #define LLVM_NODISCARD
- #endif
-+#endif
- 
- // Indicate that a non-static, non-const C++ member function reinitializes
- // the entire object to a known state, independent of the previous state of
-@@ -190,11 +200,13 @@
- // more portable solution:
- //   (void)unused_var_name;
- // Prefer cast-to-void wherever it is sufficient.
-+#ifndef LLVM_ATTRIBUTE_UNUSED
- #if __has_attribute(unused)
- #define LLVM_ATTRIBUTE_UNUSED __attribute__((__unused__))
- #else
- #define LLVM_ATTRIBUTE_UNUSED
- #endif
-+#endif
- 
- // FIXME: Provide this for PE/COFF targets.
- #if __has_attribute(weak) && !defined(__MINGW32__) && !defined(__CYGWIN__) &&  \
-@@ -204,6 +216,7 @@
- #define LLVM_ATTRIBUTE_WEAK
- #endif
- 
-+#ifndef LLVM_READNONE
- // Prior to clang 3.2, clang did not accept any spelling of
- // __has_attribute(const), so assume it is supported.
- #if defined(__clang__) || defined(__GNUC__)
-@@ -212,13 +225,16 @@
- #else
- #define LLVM_READNONE
- #endif
-+#endif
- 
-+#ifndef LLVM_READONLY
- #if __has_attribute(pure) || defined(__GNUC__)
- // aka 'PURE' but following LLVM Conventions.
- #define LLVM_READONLY __attribute__((__pure__))
- #else
- #define LLVM_READONLY
- #endif
-+#endif
- 
- #if __has_attribute(minsize)
- #define LLVM_ATTRIBUTE_MINSIZE __attribute__((minsize))
-@@ -226,6 +242,7 @@
- #define LLVM_ATTRIBUTE_MINSIZE
- #endif
- 
-+#ifndef LLVM_LIKELY
- #if __has_builtin(__builtin_expect) || defined(__GNUC__)
- #define LLVM_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true)
- #define LLVM_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false)
-@@ -233,9 +250,11 @@
- #define LLVM_LIKELY(EXPR) (EXPR)
- #define LLVM_UNLIKELY(EXPR) (EXPR)
- #endif
-+#endif
- 
- /// LLVM_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so,
- /// mark a method "not for inlining".
-+#ifndef LLVM_ATTRIBUTE_NOINLINE
- #if __has_attribute(noinline)
- #define LLVM_ATTRIBUTE_NOINLINE __attribute__((noinline))
- #elif defined(_MSC_VER)
-@@ -243,9 +262,11 @@
- #else
- #define LLVM_ATTRIBUTE_NOINLINE
- #endif
-+#endif
- 
- /// LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do
- /// so, mark a method "always inline" because it is performance sensitive.
-+#ifndef LLVM_ATTRIBUTE_ALWAYS_INLINE
- #if __has_attribute(always_inline)
- #define LLVM_ATTRIBUTE_ALWAYS_INLINE inline __attribute__((always_inline))
- #elif defined(_MSC_VER)
-@@ -253,6 +274,7 @@
- #else
- #define LLVM_ATTRIBUTE_ALWAYS_INLINE inline
- #endif
-+#endif
- 
- /// LLVM_ATTRIBUTE_NO_DEBUG - On compilers where we have a directive to do
- /// so, mark a method "no debug" because debug info makes the debugger
-@@ -263,6 +285,7 @@
- #define LLVM_ATTRIBUTE_NODEBUG
- #endif
- 
-+#ifndef LLVM_ATTRIBUTE_RETURNS_NONNULL
- #if __has_attribute(returns_nonnull)
- #define LLVM_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
- #elif defined(_MSC_VER)
-@@ -270,9 +293,11 @@
- #else
- #define LLVM_ATTRIBUTE_RETURNS_NONNULL
- #endif
-+#endif
- 
- /// \macro LLVM_ATTRIBUTE_RETURNS_NOALIAS Used to mark a function as returning a
- /// pointer that does not alias any other valid pointer.
-+#ifndef LLVM_ATTRIBUTE_RETURNS_NOALIAS
- #ifdef __GNUC__
- #define LLVM_ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__))
- #elif defined(_MSC_VER)
-@@ -280,8 +305,10 @@
- #else
- #define LLVM_ATTRIBUTE_RETURNS_NOALIAS
- #endif
-+#endif
- 
- /// LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements.
-+#ifndef LLVM_FALLTHROUGH
- #if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(fallthrough)
- #define LLVM_FALLTHROUGH [[fallthrough]]
- #elif LLVM_HAS_CPP_ATTRIBUTE(gnu::fallthrough)
-@@ -293,6 +320,7 @@
- #else
- #define LLVM_FALLTHROUGH
- #endif
-+#endif
- 
- /// LLVM_REQUIRE_CONSTANT_INITIALIZATION - Apply this to globals to ensure that
- /// they are constant initialized.
-@@ -321,20 +349,25 @@
- 
- /// LLVM_EXTENSION - Support compilers where we have a keyword to suppress
- /// pedantic diagnostics.
-+#ifndef LLVM_EXTENSION
- #ifdef __GNUC__
- #define LLVM_EXTENSION __extension__
- #else
- #define LLVM_EXTENSION
- #endif
-+#endif
- 
- // LLVM_ATTRIBUTE_DEPRECATED(decl, "message")
- // This macro will be removed.
- // Use C++14's attribute instead: [[deprecated("message")]]
-+#ifndef LLVM_ATTRIBUTE_DEPRECATED
- #define LLVM_ATTRIBUTE_DEPRECATED(decl, message) [[deprecated(message)]] decl
-+#endif
- 
- /// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands
- /// to an expression which states that it is undefined behavior for the
- /// compiler to reach this point.  Otherwise is not defined.
-+#ifndef LLVM_BUILTIN_UNREACHABLE
- #if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
- # define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable()
- #elif defined(_MSC_VER)
-@@ -342,9 +375,11 @@
- #else
- # define LLVM_BUILTIN_UNREACHABLE
- #endif
-+#endif
- 
- /// LLVM_BUILTIN_TRAP - On compilers which support it, expands to an expression
- /// which causes the program to exit abnormally.
-+#ifndef LLVM_BUILTIN_TRAP
- #if __has_builtin(__builtin_trap) || defined(__GNUC__)
- # define LLVM_BUILTIN_TRAP __builtin_trap()
- #elif defined(_MSC_VER)
-@@ -356,10 +391,12 @@
- #else
- # define LLVM_BUILTIN_TRAP *(volatile int*)0x11 = 0
- #endif
-+#endif
- 
- /// LLVM_BUILTIN_DEBUGTRAP - On compilers which support it, expands to
- /// an expression which causes the program to break while running
- /// under a debugger.
-+#ifndef LLVM_BUILTIN_DEBUGTRAP
- #if __has_builtin(__builtin_debugtrap)
- # define LLVM_BUILTIN_DEBUGTRAP __builtin_debugtrap()
- #elif defined(_MSC_VER)
-@@ -373,9 +410,11 @@
- // program to abort if encountered.
- # define LLVM_BUILTIN_DEBUGTRAP
- #endif
-+#endif
- 
- /// \macro LLVM_ASSUME_ALIGNED
- /// Returns a pointer with an assumed alignment.
-+#ifndef LLVM_ASSUME_ALIGNED
- #if __has_builtin(__builtin_assume_aligned) || defined(__GNUC__)
- # define LLVM_ASSUME_ALIGNED(p, a) __builtin_assume_aligned(p, a)
- #elif defined(LLVM_BUILTIN_UNREACHABLE)
-@@ -384,6 +423,7 @@
- #else
- # define LLVM_ASSUME_ALIGNED(p, a) (p)
- #endif
-+#endif
- 
- /// \macro LLVM_PACKED
- /// Used to specify a packed structure.
-@@ -403,6 +443,7 @@
- ///   long long l;
- /// };
- /// LLVM_PACKED_END
-+#ifndef LLVM_PACKED
- #ifdef _MSC_VER
- # define LLVM_PACKED(d) __pragma(pack(push, 1)) d __pragma(pack(pop))
- # define LLVM_PACKED_START __pragma(pack(push, 1))
-@@ -412,11 +453,13 @@
- # define LLVM_PACKED_START _Pragma("pack(push, 1)")
- # define LLVM_PACKED_END   _Pragma("pack(pop)")
- #endif
-+#endif
- 
- /// \macro LLVM_PTR_SIZE
- /// A constant integer equivalent to the value of sizeof(void*).
- /// Generally used in combination with alignas or when doing computation in the
- /// preprocessor.
-+#ifndef LLVM_PTR_SIZE
- #ifdef __SIZEOF_POINTER__
- # define LLVM_PTR_SIZE __SIZEOF_POINTER__
- #elif defined(_WIN64)
-@@ -428,6 +471,7 @@
- #else
- # define LLVM_PTR_SIZE sizeof(void *)
- #endif
-+#endif
- 
- /// \macro LLVM_MEMORY_SANITIZER_BUILD
- /// Whether LLVM itself is built with MemorySanitizer instrumentation.
-@@ -498,11 +542,13 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
- 
- /// \macro LLVM_NO_SANITIZE
- /// Disable a particular sanitizer for a function.
-+#ifndef LLVM_NO_SANITIZE
- #if __has_attribute(no_sanitize)
- #define LLVM_NO_SANITIZE(KIND) __attribute__((no_sanitize(KIND)))
- #else
- #define LLVM_NO_SANITIZE(KIND)
- #endif
-+#endif
- 
- /// Mark debug helper function definitions like dump() that should not be
- /// stripped from debug builds.
-@@ -510,17 +556,20 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
- /// `#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)` so they do always
- /// get stripped in release builds.
- // FIXME: Move this to a private config.h as it's not usable in public headers.
-+#ifndef LLVM_DUMP_METHOD
- #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- #define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED
- #else
- #define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE
- #endif
-+#endif
- 
- /// \macro LLVM_PRETTY_FUNCTION
- /// Gets a user-friendly looking function signature for the current scope
- /// using the best available method on each platform.  The exact format of the
- /// resulting string is implementation specific and non-portable, so this should
- /// only be used, for example, for logging or diagnostics.
-+#ifndef LLVM_PRETTY_FUNCTION
- #if defined(_MSC_VER)
- #define LLVM_PRETTY_FUNCTION __FUNCSIG__
- #elif defined(__GNUC__) || defined(__clang__)
-@@ -528,6 +577,7 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
- #else
- #define LLVM_PRETTY_FUNCTION __func__
- #endif
-+#endif
- 
- /// \macro LLVM_THREAD_LOCAL
- /// A thread-local storage specifier which can be used with globals,
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0007-Explicitly-use-std.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0007-Explicitly-use-std.patch
deleted file mode 100644
index cfbe9a1..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0007-Explicitly-use-std.patch
+++ /dev/null
@@ -1,78 +0,0 @@
-From c26562caae6a685716a8785ad8689833c9996549 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sat, 7 May 2022 22:37:34 -0400
-Subject: [PATCH 07/28] Explicitly use std::
-
----
- llvm/include/llvm/ADT/SmallSet.h       | 2 +-
- llvm/include/llvm/Support/MathExtras.h | 2 +-
- llvm/lib/Support/ErrorHandling.cpp     | 2 +-
- llvm/unittests/ADT/SmallPtrSetTest.cpp | 2 +-
- llvm/unittests/ADT/StringMapTest.cpp   | 2 +-
- 5 files changed, 5 insertions(+), 5 deletions(-)
-
-diff --git a/llvm/include/llvm/ADT/SmallSet.h b/llvm/include/llvm/ADT/SmallSet.h
-index bfe93e997..403e108fd 100644
---- a/llvm/include/llvm/ADT/SmallSet.h
-+++ b/llvm/include/llvm/ADT/SmallSet.h
-@@ -270,7 +270,7 @@ bool operator==(const SmallSet<T, LN, C> &LHS, const SmallSet<T, RN, C> &RHS) {
-     return false;
- 
-   // All elements in LHS must also be in RHS
--  return all_of(LHS, [&RHS](const T &E) { return RHS.count(E); });
-+  return std::all_of(LHS.begin(), LHS.end(), [&RHS](const T &E) { return RHS.count(E); });
- }
- 
- /// Inequality comparison for SmallSet.
-diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
-index db9fbc148..da843ef79 100644
---- a/llvm/include/llvm/Support/MathExtras.h
-+++ b/llvm/include/llvm/Support/MathExtras.h
-@@ -586,7 +586,7 @@ inline double Log2(double Value) {
- #if defined(__ANDROID_API__) && __ANDROID_API__ < 18
-   return __builtin_log(Value) / __builtin_log(2.0);
- #else
--  return log2(Value);
-+  return std::log2(Value);
- #endif
- }
- 
-diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
-index 89440b5ab..f80e28e87 100644
---- a/llvm/lib/Support/ErrorHandling.cpp
-+++ b/llvm/lib/Support/ErrorHandling.cpp
-@@ -210,7 +210,7 @@ void LLVMResetFatalErrorHandler() {
- // I'd rather not double the line count of the following.
- #define MAP_ERR_TO_COND(x, y)                                                  \
-   case x:                                                                      \
--    return make_error_code(errc::y)
-+    return std::make_error_code(std::errc::y)
- 
- std::error_code llvm::mapWindowsError(unsigned EV) {
-   switch (EV) {
-diff --git a/llvm/unittests/ADT/SmallPtrSetTest.cpp b/llvm/unittests/ADT/SmallPtrSetTest.cpp
-index 6f3c94eed..531f81ab5 100644
---- a/llvm/unittests/ADT/SmallPtrSetTest.cpp
-+++ b/llvm/unittests/ADT/SmallPtrSetTest.cpp
-@@ -298,7 +298,7 @@ TEST(SmallPtrSetTest, dereferenceAndIterate) {
- 
-   // Sort.  We should hit the first element just once and the final element N
-   // times.
--  llvm::sort(std::begin(Found), std::end(Found));
-+  std::sort(std::begin(Found), std::end(Found));
-   for (auto F = std::begin(Found), E = std::end(Found); F != E; ++F)
-     EXPECT_EQ(F - Found + 1, *F);
- }
-diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
-index 86907ab61..6d0c0942c 100644
---- a/llvm/unittests/ADT/StringMapTest.cpp
-+++ b/llvm/unittests/ADT/StringMapTest.cpp
-@@ -329,7 +329,7 @@ TEST_F(StringMapTest, IterMapKeysSmallVector) {
-   Map["D"] = 3;
- 
-   auto Keys = to_vector<4>(Map.keys());
--  llvm::sort(Keys);
-+  std::sort(Keys.begin(), Keys.end());
- 
-   SmallVector<std::string_view, 4> Expected = {"A", "B", "C", "D"};
-   EXPECT_EQ(Expected, Keys);
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0007-Remove-format_provider.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0007-Remove-format_provider.patch
new file mode 100644
index 0000000..6495bb9
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0007-Remove-format_provider.patch
@@ -0,0 +1,238 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sat, 7 May 2022 22:53:50 -0400
+Subject: [PATCH 07/31] Remove format_provider
+
+---
+ llvm/include/llvm/Support/Chrono.h      | 109 ------------------------
+ llvm/include/llvm/Support/raw_ostream.h |   6 --
+ llvm/unittests/Support/Chrono.cpp       |  67 ---------------
+ 3 files changed, 182 deletions(-)
+
+diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h
+index a7dea19d9193bcff4bc6b553b80a10b2bc7b64af..9f9a2b5cab270327898cee3f97d9ae7cf77eb564 100644
+--- a/llvm/include/llvm/Support/Chrono.h
++++ b/llvm/include/llvm/Support/Chrono.h
+@@ -10,7 +10,6 @@
+ #define LLVM_SUPPORT_CHRONO_H
+ 
+ #include "llvm/Support/Compiler.h"
+-#include "llvm/Support/FormatProviders.h"
+ 
+ #include <chrono>
+ #include <ctime>
+@@ -59,114 +58,6 @@ toTimePoint(std::time_t T, uint32_t nsec) {
+ 
+ raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
+ 
+-/// Format provider for TimePoint<>
+-///
+-/// The options string is a strftime format string, with extensions:
+-///   - %L is millis: 000-999
+-///   - %f is micros: 000000-999999
+-///   - %N is nanos: 000000000 - 999999999
+-///
+-/// If no options are given, the default format is "%Y-%m-%d %H:%M:%S.%N".
+-template <>
+-struct format_provider<sys::TimePoint<>> {
+-  static void format(const sys::TimePoint<> &TP, llvm::raw_ostream &OS,
+-                     std::string_view Style);
+-};
+-
+-namespace detail {
+-template <typename Period> struct unit { static const char value[]; };
+-template <typename Period> const char unit<Period>::value[] = "";
+-
+-template <> struct unit<std::ratio<3600>> { static const char value[]; };
+-template <> struct unit<std::ratio<60>> { static const char value[]; };
+-template <> struct unit<std::ratio<1>> { static const char value[]; };
+-template <> struct unit<std::milli> { static const char value[]; };
+-template <> struct unit<std::micro> { static const char value[]; };
+-template <> struct unit<std::nano> { static const char value[]; };
+-} // namespace detail
+-
+-/// Implementation of format_provider<T> for duration types.
+-///
+-/// The options string of a duration type has the grammar:
+-///
+-///   duration_options  ::= [unit][show_unit [number_options]]
+-///   unit              ::= `h`|`m`|`s`|`ms|`us`|`ns`
+-///   show_unit         ::= `+` | `-`
+-///   number_options    ::= options string for a integral or floating point type
+-///
+-///   Examples
+-///   =================================
+-///   |  options  | Input | Output    |
+-///   =================================
+-///   | ""        | 1s    | 1 s       |
+-///   | "ms"      | 1s    | 1000 ms   |
+-///   | "ms-"     | 1s    | 1000      |
+-///   | "ms-n"    | 1s    | 1,000     |
+-///   | ""        | 1.0s  | 1.00 s    |
+-///   =================================
+-///
+-///  If the unit of the duration type is not one of the units specified above,
+-///  it is still possible to format it, provided you explicitly request a
+-///  display unit or you request that the unit is not displayed.
+-
+-template <typename Rep, typename Period>
+-struct format_provider<std::chrono::duration<Rep, Period>> {
+-private:
+-  typedef std::chrono::duration<Rep, Period> Dur;
+-  typedef std::conditional_t<std::chrono::treat_as_floating_point<Rep>::value,
+-                             double, intmax_t>
+-      InternalRep;
+-
+-  template <typename AsPeriod> static InternalRep getAs(const Dur &D) {
+-    using namespace std::chrono;
+-    return duration_cast<duration<InternalRep, AsPeriod>>(D).count();
+-  }
+-
+-  static std::pair<InternalRep, std::string_view> consumeUnit(std::string_view &Style,
+-                                                        const Dur &D) {
+-    using namespace std::chrono;
+-    if (Style.consume_front("ns"))
+-      return {getAs<std::nano>(D), "ns"};
+-    if (Style.consume_front("us"))
+-      return {getAs<std::micro>(D), "us"};
+-    if (Style.consume_front("ms"))
+-      return {getAs<std::milli>(D), "ms"};
+-    if (Style.consume_front("s"))
+-      return {getAs<std::ratio<1>>(D), "s"};
+-    if (Style.consume_front("m"))
+-      return {getAs<std::ratio<60>>(D), "m"};
+-    if (Style.consume_front("h"))
+-      return {getAs<std::ratio<3600>>(D), "h"};
+-    return {D.count(), detail::unit<Period>::value};
+-  }
+-
+-  static bool consumeShowUnit(std::string_view &Style) {
+-    if (Style.empty())
+-      return true;
+-    if (Style.consume_front("-"))
+-      return false;
+-    if (Style.consume_front("+"))
+-      return true;
+-    assert(0 && "Unrecognised duration format");
+-    return true;
+-  }
+-
+-public:
+-  static void format(const Dur &D, llvm::raw_ostream &Stream, std::string_view Style) {
+-    InternalRep count;
+-    std::string_view unit;
+-    std::tie(count, unit) = consumeUnit(Style, D);
+-    bool show_unit = consumeShowUnit(Style);
+-
+-    format_provider<InternalRep>::format(count, Stream, Style);
+-
+-    if (show_unit) {
+-      assert(!unit.empty());
+-      Stream << " " << unit;
+-    }
+-  }
+-};
+-
+ } // namespace llvm
+ 
+ #endif // LLVM_SUPPORT_CHRONO_H
+diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
+index 2463f1af612a78cafafe3c0e16d496e607cdc322..5d08596b4cf6bf9e9b8e2c2c1aef731bb8832da5 100644
+--- a/llvm/include/llvm/Support/raw_ostream.h
++++ b/llvm/include/llvm/Support/raw_ostream.h
+@@ -27,12 +27,6 @@
+ 
+ namespace llvm {
+ 
+-class Duration;
+-class formatv_object_base;
+-class format_object_base;
+-class FormattedString;
+-class FormattedNumber;
+-class FormattedBytes;
+ template <class T> class [[nodiscard]] Expected;
+ 
+ namespace sys {
+diff --git a/llvm/unittests/Support/Chrono.cpp b/llvm/unittests/Support/Chrono.cpp
+index daf8a8a350f08c748ba05af44f43f3faca8e2c61..3c049de18c0a80465f4b0a8c054df2602d5e9b1c 100644
+--- a/llvm/unittests/Support/Chrono.cpp
++++ b/llvm/unittests/Support/Chrono.cpp
+@@ -30,43 +30,6 @@ TEST(Chrono, TimeTConversion) {
+   EXPECT_EQ(TP, toTimePoint(toTimeT(TP)));
+ }
+ 
+-TEST(Chrono, TimePointFormat) {
+-  using namespace std::chrono;
+-  struct tm TM {};
+-  TM.tm_year = 106;
+-  TM.tm_mon = 0;
+-  TM.tm_mday = 2;
+-  TM.tm_hour = 15;
+-  TM.tm_min = 4;
+-  TM.tm_sec = 5;
+-  TM.tm_isdst = -1;
+-  TimePoint<> T =
+-      system_clock::from_time_t(mktime(&TM)) + nanoseconds(123456789);
+-  TimePoint<> T2 =
+-      system_clock::from_time_t(mktime(&TM)) + nanoseconds(23456789);
+-
+-  // operator<< uses the format YYYY-MM-DD HH:MM:SS.NNNNNNNNN
+-  std::string S;
+-  raw_string_ostream OS(S);
+-  OS << T;
+-  EXPECT_EQ("2006-01-02 15:04:05.123456789", OS.str());
+-  S.clear();
+-  OS << T2;
+-  EXPECT_EQ("2006-01-02 15:04:05.023456789", OS.str());
+-
+-  // formatv default style matches operator<<.
+-  EXPECT_EQ("2006-01-02 15:04:05.123456789", formatv("{0}", T).str());
+-  EXPECT_EQ("2006-01-02 15:04:05.023456789", formatv("{0}", T2).str());
+-  // formatv supports strftime-style format strings.
+-  EXPECT_EQ("15:04:05", formatv("{0:%H:%M:%S}", T).str());
+-  // formatv supports our strftime extensions for sub-second precision.
+-  EXPECT_EQ("123", formatv("{0:%L}", T).str());
+-  EXPECT_EQ("123456", formatv("{0:%f}", T).str());
+-  EXPECT_EQ("123456789", formatv("{0:%N}", T).str());
+-  // our extensions don't interfere with %% escaping.
+-  EXPECT_EQ("%foo", formatv("{0:%%foo}", T).str());
+-}
+-
+ // Test that toTimePoint and toTimeT can be called with a arguments with varying
+ // precisions.
+ TEST(Chrono, ImplicitConversions) {
+@@ -84,34 +47,4 @@ TEST(Chrono, ImplicitConversions) {
+   EXPECT_EQ(TimeT, toTimeT(Nano));
+ }
+ 
+-TEST(Chrono, DurationFormat) {
+-  EXPECT_EQ("1 h", formatv("{0}", hours(1)).str());
+-  EXPECT_EQ("1 m", formatv("{0}", minutes(1)).str());
+-  EXPECT_EQ("1 s", formatv("{0}", seconds(1)).str());
+-  EXPECT_EQ("1 ms", formatv("{0}", milliseconds(1)).str());
+-  EXPECT_EQ("1 us", formatv("{0}", microseconds(1)).str());
+-  EXPECT_EQ("1 ns", formatv("{0}", nanoseconds(1)).str());
+-
+-  EXPECT_EQ("1 s", formatv("{0:+}", seconds(1)).str());
+-  EXPECT_EQ("1", formatv("{0:-}", seconds(1)).str());
+-
+-  EXPECT_EQ("1000 ms", formatv("{0:ms}", seconds(1)).str());
+-  EXPECT_EQ("1000000 us", formatv("{0:us}", seconds(1)).str());
+-  EXPECT_EQ("1000", formatv("{0:ms-}", seconds(1)).str());
+-
+-  EXPECT_EQ("1,000 ms", formatv("{0:+n}", milliseconds(1000)).str());
+-  EXPECT_EQ("0x3e8", formatv("{0:-x}", milliseconds(1000)).str());
+-  EXPECT_EQ("010", formatv("{0:-3}", milliseconds(10)).str());
+-  EXPECT_EQ("10,000", formatv("{0:ms-n}", seconds(10)).str());
+-
+-  EXPECT_EQ("1.00 s", formatv("{0}", duration<float>(1)).str());
+-  EXPECT_EQ("0.123 s", formatv("{0:+3}", duration<float>(0.123f)).str());
+-  EXPECT_EQ("1.230e-01 s", formatv("{0:+e3}", duration<float>(0.123f)).str());
+-
+-  typedef duration<float, std::ratio<60 * 60 * 24 * 14, 1000000>>
+-      microfortnights;
+-  EXPECT_EQ("1.00", formatv("{0:-}", microfortnights(1)).str());
+-  EXPECT_EQ("1209.60 ms", formatv("{0:ms}", microfortnights(1)).str());
+-}
+-
+ } // anonymous namespace
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0008-Add-compiler-warning-pragmas.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0008-Add-compiler-warning-pragmas.patch
new file mode 100644
index 0000000..15442f1
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0008-Add-compiler-warning-pragmas.patch
@@ -0,0 +1,228 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sun, 8 May 2022 13:34:07 -0400
+Subject: [PATCH 08/31] Add compiler warning pragmas
+
+---
+ llvm/include/llvm/ADT/FunctionExtras.h | 11 +++++++++++
+ llvm/include/llvm/ADT/Hashing.h        |  9 +++++++++
+ llvm/include/llvm/ADT/SmallVector.h    |  8 ++++++++
+ llvm/include/llvm/Support/MathExtras.h |  9 +++++++++
+ llvm/include/llvm/Support/MemAlloc.h   | 13 +++++++++++++
+ llvm/lib/Support/raw_ostream.cpp       |  4 ++++
+ llvm/unittests/ADT/DenseMapTest.cpp    |  4 ++++
+ llvm/unittests/ADT/MapVectorTest.cpp   |  7 +++++++
+ llvm/unittests/ADT/SmallVectorTest.cpp |  4 ++++
+ llvm/unittests/Support/AlignOfTest.cpp |  7 +++----
+ 10 files changed, 72 insertions(+), 4 deletions(-)
+
+diff --git a/llvm/include/llvm/ADT/FunctionExtras.h b/llvm/include/llvm/ADT/FunctionExtras.h
+index 9d10b16e3cbe9c0df818a3254fcd3a6032d54b39..1daeae915eb506b32a2d1296d2f0fe4e6dab606e 100644
+--- a/llvm/include/llvm/ADT/FunctionExtras.h
++++ b/llvm/include/llvm/ADT/FunctionExtras.h
+@@ -55,6 +55,13 @@ namespace llvm {
+ ///   It can hold functions with a non-const operator(), like mutable lambdas.
+ template <typename FunctionT> class unique_function;
+ 
++// GCC warns on OutOfLineStorage
++#if defined(__GNUC__) && !defined(__clang__)
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Warray-bounds"
++#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
++#endif
++
+ namespace detail {
+ 
+ template <typename T>
+@@ -409,6 +416,10 @@ public:
+   }
+ };
+ 
++#if defined(__GNUC__) && !defined(__clang__)
++#pragma GCC diagnostic pop
++#endif
++
+ } // end namespace llvm
+ 
+ #endif // LLVM_ADT_FUNCTIONEXTRAS_H
+diff --git a/llvm/include/llvm/ADT/Hashing.h b/llvm/include/llvm/ADT/Hashing.h
+index ef983105c7bae67bb2ef832e4473939a0406e0df..781bdb7416392e3f60a1ac3a38fbcf5324b5395f 100644
+--- a/llvm/include/llvm/ADT/Hashing.h
++++ b/llvm/include/llvm/ADT/Hashing.h
+@@ -56,6 +56,11 @@
+ #include <tuple>
+ #include <utility>
+ 
++#ifdef _WIN32
++#pragma warning(push)
++#pragma warning(disable : 26495)
++#endif
++
+ namespace llvm {
+ template <typename T, typename Enable> struct DenseMapInfo;
+ 
+@@ -683,4 +688,8 @@ template <> struct DenseMapInfo<hash_code, void> {
+ 
+ } // namespace llvm
+ 
++#ifdef _WIN32
++#pragma warning(pop)
++#endif
++
+ #endif
+diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h
+index 84f4d0931a30f4be29549354c85cb4c0489e14c9..b42438a9b16c273f9ef5b5cce6192873c78cb964 100644
+--- a/llvm/include/llvm/ADT/SmallVector.h
++++ b/llvm/include/llvm/ADT/SmallVector.h
+@@ -14,6 +14,14 @@
+ #ifndef LLVM_ADT_SMALLVECTOR_H
+ #define LLVM_ADT_SMALLVECTOR_H
+ 
++// This file uses std::memcpy() to copy std::pair<unsigned int, unsigned int>.
++// That type is POD, but the standard doesn't guarantee that. GCC doesn't treat
++// the type as POD so it throws a warning. We want to consider this a warning
++// instead of an error.
++#if __GNUC__ >= 8
++#pragma GCC diagnostic warning "-Wclass-memaccess"
++#endif
++
+ #include "llvm/Support/Compiler.h"
+ #include "llvm/Support/type_traits.h"
+ #include <algorithm>
+diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
+index 0bd572d07fcbf2ff56998dbf366215068b62f527..cd5a64a746b2eb7491e9b6cf8570bdf436d94a6d 100644
+--- a/llvm/include/llvm/Support/MathExtras.h
++++ b/llvm/include/llvm/Support/MathExtras.h
+@@ -208,6 +208,11 @@ inline uint64_t maxUIntN(uint64_t N) {
+   return UINT64_MAX >> (64 - N);
+ }
+ 
++#ifdef _WIN32
++#pragma warning(push)
++#pragma warning(disable : 4146)
++#endif
++
+ /// Gets the minimum value for a N-bit signed integer.
+ inline int64_t minIntN(int64_t N) {
+   assert(N > 0 && N <= 64 && "integer width out of range");
+@@ -215,6 +220,10 @@ inline int64_t minIntN(int64_t N) {
+   return UINT64_C(1) + ~(UINT64_C(1) << (N - 1));
+ }
+ 
++#ifdef _WIN32
++#pragma warning(pop)
++#endif
++
+ /// Gets the maximum value for a N-bit signed integer.
+ inline int64_t maxIntN(int64_t N) {
+   assert(N > 0 && N <= 64 && "integer width out of range");
+diff --git a/llvm/include/llvm/Support/MemAlloc.h b/llvm/include/llvm/Support/MemAlloc.h
+index d6012bd5a6985d8405136039aa85931605cd8a40..01007deb89bba625b1b3ad3e703d0c16ed6f757b 100644
+--- a/llvm/include/llvm/Support/MemAlloc.h
++++ b/llvm/include/llvm/Support/MemAlloc.h
+@@ -22,6 +22,14 @@
+ 
+ namespace llvm {
+ 
++#ifdef _WIN32
++#pragma warning(push)
++// Warning on NONNULL, report is not known to abort
++#pragma warning(disable : 6387)
++#pragma warning(disable : 28196)
++#pragma warning(disable : 28183)
++#endif
++
+ LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_malloc(size_t Sz) {
+   void *Result = std::malloc(Sz);
+   if (Result == nullptr) {
+@@ -84,4 +92,9 @@ allocate_buffer(size_t Size, size_t Alignment);
+ void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment);
+ 
+ } // namespace llvm
++
++#ifdef _WIN32
++#pragma warning(pop)
++#endif
++
+ #endif
+diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
+index 9966a0056ae4f24a7a38346ee1c2f5d83ac20248..a23f567a37abdc199363607446f33f29e021d7ad 100644
+--- a/llvm/lib/Support/raw_ostream.cpp
++++ b/llvm/lib/Support/raw_ostream.cpp
+@@ -10,6 +10,10 @@
+ //
+ //===----------------------------------------------------------------------===//
+ 
++#ifdef _WIN32
++#define _CRT_NONSTDC_NO_WARNINGS
++#endif
++
+ #include "llvm/Support/raw_ostream.h"
+ #include "llvm/ADT/StringExtras.h"
+ #include "llvm/Config/config.h"
+diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
+index b710ac07461ba58faa99cedeae7f209dc0f5902b..1f232d3046292c0da940ba4bef7d50604556e4c2 100644
+--- a/llvm/unittests/ADT/DenseMapTest.cpp
++++ b/llvm/unittests/ADT/DenseMapTest.cpp
+@@ -6,6 +6,10 @@
+ //
+ //===----------------------------------------------------------------------===//
+ 
++#if defined(__GNUC__) && !defined(__clang__)
++#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
++#endif
++
+ #include "llvm/ADT/DenseMap.h"
+ #include "llvm/ADT/DenseMapInfo.h"
+ #include "llvm/ADT/DenseMapInfoVariant.h"
+diff --git a/llvm/unittests/ADT/MapVectorTest.cpp b/llvm/unittests/ADT/MapVectorTest.cpp
+index 1a371cbfba81e8ea4b57c4077ca94c86c3db8991..62fafcaf04a67d4c67b98b8f42d837ccca245fe9 100644
+--- a/llvm/unittests/ADT/MapVectorTest.cpp
++++ b/llvm/unittests/ADT/MapVectorTest.cpp
+@@ -6,6 +6,13 @@
+ //
+ //===----------------------------------------------------------------------===//
+ 
++#if defined(__GNUC__)
++#pragma GCC diagnostic ignored "-Wpedantic"
++#if !defined(__clang__)
++#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
++#endif
++#endif
++
+ #include "llvm/ADT/MapVector.h"
+ #include "llvm/ADT/iterator_range.h"
+ #include "gtest/gtest.h"
+diff --git a/llvm/unittests/ADT/SmallVectorTest.cpp b/llvm/unittests/ADT/SmallVectorTest.cpp
+index 7029038d18d433cef987bedbfa4fda269b24fb8f..f8c37820ef9fdfe0af067f5aa8d2297ed15e73bc 100644
+--- a/llvm/unittests/ADT/SmallVectorTest.cpp
++++ b/llvm/unittests/ADT/SmallVectorTest.cpp
+@@ -17,6 +17,10 @@
+ #include <span>
+ #include <stdarg.h>
+ 
++#if defined(__GNUC__)
++#pragma GCC diagnostic ignored "-Wpedantic"
++#endif
++
+ using namespace llvm;
+ 
+ namespace {
+diff --git a/llvm/unittests/Support/AlignOfTest.cpp b/llvm/unittests/Support/AlignOfTest.cpp
+index f84895c18602d3936d623ed79c5d9689cd57cc91..6a50205b143b7ff553066f048a45bf4e1ecc475b 100644
+--- a/llvm/unittests/Support/AlignOfTest.cpp
++++ b/llvm/unittests/Support/AlignOfTest.cpp
+@@ -31,10 +31,9 @@ namespace {
+ #pragma clang diagnostic ignored "-Wunknown-pragmas"
+ #pragma clang diagnostic ignored "-Winaccessible-base"
+ #elif ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402
+-// Pragma based warning suppression was introduced in GGC 4.2.  Additionally
+-// this warning is "enabled by default".  The warning still appears if -Wall is
+-// suppressed.  Apparently GCC suppresses it when -w is specifed, which is odd.
+-#pragma GCC diagnostic warning "-w"
++#pragma GCC diagnostic warning "-Wunknown-pragmas"
++#pragma GCC diagnostic warning "-Winaccessible-base"
++#pragma GCC diagnostic warning "-Wunused-function"
+ #endif
+ 
+ // Define some fixed alignment types to use in these tests.
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0008-Remove-format_provider.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0008-Remove-format_provider.patch
deleted file mode 100644
index 1e43a8b..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0008-Remove-format_provider.patch
+++ /dev/null
@@ -1,232 +0,0 @@
-From f35fcb2c40caceed14437e65131e9fe1cf94deac Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sat, 7 May 2022 22:53:50 -0400
-Subject: [PATCH 08/28] Remove format_provider
-
----
- llvm/include/llvm/Support/Chrono.h      | 109 ------------------------
- llvm/include/llvm/Support/raw_ostream.h |   6 --
- llvm/unittests/Support/Chrono.cpp       |  61 -------------
- 3 files changed, 176 deletions(-)
-
-diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h
-index a7dea19d9..9f9a2b5ca 100644
---- a/llvm/include/llvm/Support/Chrono.h
-+++ b/llvm/include/llvm/Support/Chrono.h
-@@ -10,7 +10,6 @@
- #define LLVM_SUPPORT_CHRONO_H
- 
- #include "llvm/Support/Compiler.h"
--#include "llvm/Support/FormatProviders.h"
- 
- #include <chrono>
- #include <ctime>
-@@ -59,114 +58,6 @@ toTimePoint(std::time_t T, uint32_t nsec) {
- 
- raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
- 
--/// Format provider for TimePoint<>
--///
--/// The options string is a strftime format string, with extensions:
--///   - %L is millis: 000-999
--///   - %f is micros: 000000-999999
--///   - %N is nanos: 000000000 - 999999999
--///
--/// If no options are given, the default format is "%Y-%m-%d %H:%M:%S.%N".
--template <>
--struct format_provider<sys::TimePoint<>> {
--  static void format(const sys::TimePoint<> &TP, llvm::raw_ostream &OS,
--                     std::string_view Style);
--};
--
--namespace detail {
--template <typename Period> struct unit { static const char value[]; };
--template <typename Period> const char unit<Period>::value[] = "";
--
--template <> struct unit<std::ratio<3600>> { static const char value[]; };
--template <> struct unit<std::ratio<60>> { static const char value[]; };
--template <> struct unit<std::ratio<1>> { static const char value[]; };
--template <> struct unit<std::milli> { static const char value[]; };
--template <> struct unit<std::micro> { static const char value[]; };
--template <> struct unit<std::nano> { static const char value[]; };
--} // namespace detail
--
--/// Implementation of format_provider<T> for duration types.
--///
--/// The options string of a duration type has the grammar:
--///
--///   duration_options  ::= [unit][show_unit [number_options]]
--///   unit              ::= `h`|`m`|`s`|`ms|`us`|`ns`
--///   show_unit         ::= `+` | `-`
--///   number_options    ::= options string for a integral or floating point type
--///
--///   Examples
--///   =================================
--///   |  options  | Input | Output    |
--///   =================================
--///   | ""        | 1s    | 1 s       |
--///   | "ms"      | 1s    | 1000 ms   |
--///   | "ms-"     | 1s    | 1000      |
--///   | "ms-n"    | 1s    | 1,000     |
--///   | ""        | 1.0s  | 1.00 s    |
--///   =================================
--///
--///  If the unit of the duration type is not one of the units specified above,
--///  it is still possible to format it, provided you explicitly request a
--///  display unit or you request that the unit is not displayed.
--
--template <typename Rep, typename Period>
--struct format_provider<std::chrono::duration<Rep, Period>> {
--private:
--  typedef std::chrono::duration<Rep, Period> Dur;
--  typedef std::conditional_t<std::chrono::treat_as_floating_point<Rep>::value,
--                             double, intmax_t>
--      InternalRep;
--
--  template <typename AsPeriod> static InternalRep getAs(const Dur &D) {
--    using namespace std::chrono;
--    return duration_cast<duration<InternalRep, AsPeriod>>(D).count();
--  }
--
--  static std::pair<InternalRep, std::string_view> consumeUnit(std::string_view &Style,
--                                                        const Dur &D) {
--    using namespace std::chrono;
--    if (Style.consume_front("ns"))
--      return {getAs<std::nano>(D), "ns"};
--    if (Style.consume_front("us"))
--      return {getAs<std::micro>(D), "us"};
--    if (Style.consume_front("ms"))
--      return {getAs<std::milli>(D), "ms"};
--    if (Style.consume_front("s"))
--      return {getAs<std::ratio<1>>(D), "s"};
--    if (Style.consume_front("m"))
--      return {getAs<std::ratio<60>>(D), "m"};
--    if (Style.consume_front("h"))
--      return {getAs<std::ratio<3600>>(D), "h"};
--    return {D.count(), detail::unit<Period>::value};
--  }
--
--  static bool consumeShowUnit(std::string_view &Style) {
--    if (Style.empty())
--      return true;
--    if (Style.consume_front("-"))
--      return false;
--    if (Style.consume_front("+"))
--      return true;
--    assert(0 && "Unrecognised duration format");
--    return true;
--  }
--
--public:
--  static void format(const Dur &D, llvm::raw_ostream &Stream, std::string_view Style) {
--    InternalRep count;
--    std::string_view unit;
--    std::tie(count, unit) = consumeUnit(Style, D);
--    bool show_unit = consumeShowUnit(Style);
--
--    format_provider<InternalRep>::format(count, Stream, Style);
--
--    if (show_unit) {
--      assert(!unit.empty());
--      Stream << " " << unit;
--    }
--  }
--};
--
- } // namespace llvm
- 
- #endif // LLVM_SUPPORT_CHRONO_H
-diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
-index 9a1dd7a60..a25ca5b7b 100644
---- a/llvm/include/llvm/Support/raw_ostream.h
-+++ b/llvm/include/llvm/Support/raw_ostream.h
-@@ -28,12 +28,6 @@
- 
- namespace llvm {
- 
--class Duration;
--class formatv_object_base;
--class format_object_base;
--class FormattedString;
--class FormattedNumber;
--class FormattedBytes;
- template <class T> class LLVM_NODISCARD Expected;
- 
- namespace sys {
-diff --git a/llvm/unittests/Support/Chrono.cpp b/llvm/unittests/Support/Chrono.cpp
-index 9a08a5c1b..3c049de18 100644
---- a/llvm/unittests/Support/Chrono.cpp
-+++ b/llvm/unittests/Support/Chrono.cpp
-@@ -30,37 +30,6 @@ TEST(Chrono, TimeTConversion) {
-   EXPECT_EQ(TP, toTimePoint(toTimeT(TP)));
- }
- 
--TEST(Chrono, TimePointFormat) {
--  using namespace std::chrono;
--  struct tm TM {};
--  TM.tm_year = 106;
--  TM.tm_mon = 0;
--  TM.tm_mday = 2;
--  TM.tm_hour = 15;
--  TM.tm_min = 4;
--  TM.tm_sec = 5;
--  TM.tm_isdst = -1;
--  TimePoint<> T =
--      system_clock::from_time_t(mktime(&TM)) + nanoseconds(123456789);
--
--  // operator<< uses the format YYYY-MM-DD HH:MM:SS.NNNNNNNNN
--  std::string S;
--  raw_string_ostream OS(S);
--  OS << T;
--  EXPECT_EQ("2006-01-02 15:04:05.123456789", OS.str());
--
--  // formatv default style matches operator<<.
--  EXPECT_EQ("2006-01-02 15:04:05.123456789", formatv("{0}", T).str());
--  // formatv supports strftime-style format strings.
--  EXPECT_EQ("15:04:05", formatv("{0:%H:%M:%S}", T).str());
--  // formatv supports our strftime extensions for sub-second precision.
--  EXPECT_EQ("123", formatv("{0:%L}", T).str());
--  EXPECT_EQ("123456", formatv("{0:%f}", T).str());
--  EXPECT_EQ("123456789", formatv("{0:%N}", T).str());
--  // our extensions don't interfere with %% escaping.
--  EXPECT_EQ("%foo", formatv("{0:%%foo}", T).str());
--}
--
- // Test that toTimePoint and toTimeT can be called with a arguments with varying
- // precisions.
- TEST(Chrono, ImplicitConversions) {
-@@ -78,34 +47,4 @@ TEST(Chrono, ImplicitConversions) {
-   EXPECT_EQ(TimeT, toTimeT(Nano));
- }
- 
--TEST(Chrono, DurationFormat) {
--  EXPECT_EQ("1 h", formatv("{0}", hours(1)).str());
--  EXPECT_EQ("1 m", formatv("{0}", minutes(1)).str());
--  EXPECT_EQ("1 s", formatv("{0}", seconds(1)).str());
--  EXPECT_EQ("1 ms", formatv("{0}", milliseconds(1)).str());
--  EXPECT_EQ("1 us", formatv("{0}", microseconds(1)).str());
--  EXPECT_EQ("1 ns", formatv("{0}", nanoseconds(1)).str());
--
--  EXPECT_EQ("1 s", formatv("{0:+}", seconds(1)).str());
--  EXPECT_EQ("1", formatv("{0:-}", seconds(1)).str());
--
--  EXPECT_EQ("1000 ms", formatv("{0:ms}", seconds(1)).str());
--  EXPECT_EQ("1000000 us", formatv("{0:us}", seconds(1)).str());
--  EXPECT_EQ("1000", formatv("{0:ms-}", seconds(1)).str());
--
--  EXPECT_EQ("1,000 ms", formatv("{0:+n}", milliseconds(1000)).str());
--  EXPECT_EQ("0x3e8", formatv("{0:-x}", milliseconds(1000)).str());
--  EXPECT_EQ("010", formatv("{0:-3}", milliseconds(10)).str());
--  EXPECT_EQ("10,000", formatv("{0:ms-n}", seconds(10)).str());
--
--  EXPECT_EQ("1.00 s", formatv("{0}", duration<float>(1)).str());
--  EXPECT_EQ("0.123 s", formatv("{0:+3}", duration<float>(0.123f)).str());
--  EXPECT_EQ("1.230e-01 s", formatv("{0:+e3}", duration<float>(0.123f)).str());
--
--  typedef duration<float, std::ratio<60 * 60 * 24 * 14, 1000000>>
--      microfortnights;
--  EXPECT_EQ("1.00", formatv("{0:-}", microfortnights(1)).str());
--  EXPECT_EQ("1209.60 ms", formatv("{0:ms}", microfortnights(1)).str());
--}
--
- } // anonymous namespace
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0009-Add-compiler-warning-pragmas.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0009-Add-compiler-warning-pragmas.patch
deleted file mode 100644
index 2255dd1..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0009-Add-compiler-warning-pragmas.patch
+++ /dev/null
@@ -1,228 +0,0 @@
-From 2c53d8ac36f378fda347f36ef2bc7fbc2038cb93 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sun, 8 May 2022 13:34:07 -0400
-Subject: [PATCH 09/28] Add compiler warning pragmas
-
----
- llvm/include/llvm/ADT/FunctionExtras.h | 11 +++++++++++
- llvm/include/llvm/ADT/Hashing.h        |  9 +++++++++
- llvm/include/llvm/ADT/SmallVector.h    |  8 ++++++++
- llvm/include/llvm/Support/MathExtras.h |  9 +++++++++
- llvm/include/llvm/Support/MemAlloc.h   | 13 +++++++++++++
- llvm/lib/Support/raw_ostream.cpp       |  4 ++++
- llvm/unittests/ADT/DenseMapTest.cpp    |  4 ++++
- llvm/unittests/ADT/MapVectorTest.cpp   |  7 +++++++
- llvm/unittests/ADT/SmallVectorTest.cpp |  4 ++++
- llvm/unittests/Support/AlignOfTest.cpp |  7 +++----
- 10 files changed, 72 insertions(+), 4 deletions(-)
-
-diff --git a/llvm/include/llvm/ADT/FunctionExtras.h b/llvm/include/llvm/ADT/FunctionExtras.h
-index 8a9d78f41..3efa73587 100644
---- a/llvm/include/llvm/ADT/FunctionExtras.h
-+++ b/llvm/include/llvm/ADT/FunctionExtras.h
-@@ -55,6 +55,13 @@ namespace llvm {
- ///   It can hold functions with a non-const operator(), like mutable lambdas.
- template <typename FunctionT> class unique_function;
- 
-+// GCC warns on OutOfLineStorage
-+#if defined(__GNUC__) && !defined(__clang__)
-+#pragma GCC diagnostic push
-+#pragma GCC diagnostic ignored "-Warray-bounds"
-+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-+#endif
-+
- namespace detail {
- 
- template <typename T>
-@@ -411,6 +418,10 @@ public:
-   }
- };
- 
-+#if defined(__GNUC__) && !defined(__clang__)
-+#pragma GCC diagnostic pop
-+#endif
-+
- } // end namespace llvm
- 
- #endif // LLVM_ADT_FUNCTIONEXTRAS_H
-diff --git a/llvm/include/llvm/ADT/Hashing.h b/llvm/include/llvm/ADT/Hashing.h
-index 74a87a3d8..47ff1b2bc 100644
---- a/llvm/include/llvm/ADT/Hashing.h
-+++ b/llvm/include/llvm/ADT/Hashing.h
-@@ -55,6 +55,11 @@
- #include <tuple>
- #include <utility>
- 
-+#ifdef _WIN32
-+#pragma warning(push)
-+#pragma warning(disable : 26495)
-+#endif
-+
- namespace llvm {
- template <typename T, typename Enable> struct DenseMapInfo;
- 
-@@ -687,4 +692,8 @@ template <> struct DenseMapInfo<hash_code, void> {
- 
- } // namespace llvm
- 
-+#ifdef _WIN32
-+#pragma warning(pop)
-+#endif
-+
- #endif
-diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h
-index 8686f7bb5..1e311ea56 100644
---- a/llvm/include/llvm/ADT/SmallVector.h
-+++ b/llvm/include/llvm/ADT/SmallVector.h
-@@ -14,6 +14,14 @@
- #ifndef LLVM_ADT_SMALLVECTOR_H
- #define LLVM_ADT_SMALLVECTOR_H
- 
-+// This file uses std::memcpy() to copy std::pair<unsigned int, unsigned int>.
-+// That type is POD, but the standard doesn't guarantee that. GCC doesn't treat
-+// the type as POD so it throws a warning. We want to consider this a warning
-+// instead of an error.
-+#if __GNUC__ >= 8
-+#pragma GCC diagnostic warning "-Wclass-memaccess"
-+#endif
-+
- #include "llvm/Support/Compiler.h"
- #include "llvm/Support/type_traits.h"
- #include <algorithm>
-diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
-index da843ef79..fac12dd0e 100644
---- a/llvm/include/llvm/Support/MathExtras.h
-+++ b/llvm/include/llvm/Support/MathExtras.h
-@@ -435,6 +435,11 @@ inline uint64_t maxUIntN(uint64_t N) {
-   return UINT64_MAX >> (64 - N);
- }
- 
-+#ifdef _WIN32
-+#pragma warning(push)
-+#pragma warning(disable : 4146)
-+#endif
-+
- /// Gets the minimum value for a N-bit signed integer.
- inline int64_t minIntN(int64_t N) {
-   assert(N > 0 && N <= 64 && "integer width out of range");
-@@ -442,6 +447,10 @@ inline int64_t minIntN(int64_t N) {
-   return UINT64_C(1) + ~(UINT64_C(1) << (N - 1));
- }
- 
-+#ifdef _WIN32
-+#pragma warning(pop)
-+#endif
-+
- /// Gets the maximum value for a N-bit signed integer.
- inline int64_t maxIntN(int64_t N) {
-   assert(N > 0 && N <= 64 && "integer width out of range");
-diff --git a/llvm/include/llvm/Support/MemAlloc.h b/llvm/include/llvm/Support/MemAlloc.h
-index d6012bd5a..01007deb8 100644
---- a/llvm/include/llvm/Support/MemAlloc.h
-+++ b/llvm/include/llvm/Support/MemAlloc.h
-@@ -22,6 +22,14 @@
- 
- namespace llvm {
- 
-+#ifdef _WIN32
-+#pragma warning(push)
-+// Warning on NONNULL, report is not known to abort
-+#pragma warning(disable : 6387)
-+#pragma warning(disable : 28196)
-+#pragma warning(disable : 28183)
-+#endif
-+
- LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_malloc(size_t Sz) {
-   void *Result = std::malloc(Sz);
-   if (Result == nullptr) {
-@@ -84,4 +92,9 @@ allocate_buffer(size_t Size, size_t Alignment);
- void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment);
- 
- } // namespace llvm
-+
-+#ifdef _WIN32
-+#pragma warning(pop)
-+#endif
-+
- #endif
-diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
-index e4c318eb8..ee01a9522 100644
---- a/llvm/lib/Support/raw_ostream.cpp
-+++ b/llvm/lib/Support/raw_ostream.cpp
-@@ -10,6 +10,10 @@
- //
- //===----------------------------------------------------------------------===//
- 
-+#ifdef _WIN32
-+#define _CRT_NONSTDC_NO_WARNINGS
-+#endif
-+
- #include "llvm/Support/raw_ostream.h"
- #include "llvm/ADT/STLArrayExtras.h"
- #include "llvm/ADT/StringExtras.h"
-diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
-index e505b1907..9fe83a45d 100644
---- a/llvm/unittests/ADT/DenseMapTest.cpp
-+++ b/llvm/unittests/ADT/DenseMapTest.cpp
-@@ -6,6 +6,10 @@
- //
- //===----------------------------------------------------------------------===//
- 
-+#if defined(__GNUC__) && !defined(__clang__)
-+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-+#endif
-+
- #include "llvm/ADT/DenseMap.h"
- #include "gtest/gtest.h"
- #include <map>
-diff --git a/llvm/unittests/ADT/MapVectorTest.cpp b/llvm/unittests/ADT/MapVectorTest.cpp
-index 552f9956b..20ebcd753 100644
---- a/llvm/unittests/ADT/MapVectorTest.cpp
-+++ b/llvm/unittests/ADT/MapVectorTest.cpp
-@@ -6,6 +6,13 @@
- //
- //===----------------------------------------------------------------------===//
- 
-+#if defined(__GNUC__)
-+#pragma GCC diagnostic ignored "-Wpedantic"
-+#if !defined(__clang__)
-+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-+#endif
-+#endif
-+
- #include "llvm/ADT/MapVector.h"
- #include "llvm/ADT/iterator_range.h"
- #include "gtest/gtest.h"
-diff --git a/llvm/unittests/ADT/SmallVectorTest.cpp b/llvm/unittests/ADT/SmallVectorTest.cpp
-index fe827546a..0e68bad6c 100644
---- a/llvm/unittests/ADT/SmallVectorTest.cpp
-+++ b/llvm/unittests/ADT/SmallVectorTest.cpp
-@@ -17,6 +17,10 @@
- #include <list>
- #include <stdarg.h>
- 
-+#if defined(__GNUC__)
-+#pragma GCC diagnostic ignored "-Wpedantic"
-+#endif
-+
- using namespace llvm;
- 
- namespace {
-diff --git a/llvm/unittests/Support/AlignOfTest.cpp b/llvm/unittests/Support/AlignOfTest.cpp
-index f84895c18..6a50205b1 100644
---- a/llvm/unittests/Support/AlignOfTest.cpp
-+++ b/llvm/unittests/Support/AlignOfTest.cpp
-@@ -31,10 +31,9 @@ namespace {
- #pragma clang diagnostic ignored "-Wunknown-pragmas"
- #pragma clang diagnostic ignored "-Winaccessible-base"
- #elif ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402
--// Pragma based warning suppression was introduced in GGC 4.2.  Additionally
--// this warning is "enabled by default".  The warning still appears if -Wall is
--// suppressed.  Apparently GCC suppresses it when -w is specifed, which is odd.
--#pragma GCC diagnostic warning "-w"
-+#pragma GCC diagnostic warning "-Wunknown-pragmas"
-+#pragma GCC diagnostic warning "-Winaccessible-base"
-+#pragma GCC diagnostic warning "-Wunused-function"
- #endif
- 
- // Define some fixed alignment types to use in these tests.
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0009-Remove-unused-functions.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0009-Remove-unused-functions.patch
new file mode 100644
index 0000000..13bec69
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0009-Remove-unused-functions.patch
@@ -0,0 +1,770 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sun, 8 May 2022 13:43:50 -0400
+Subject: [PATCH 09/31] Remove unused functions
+
+---
+ llvm/include/llvm/ADT/SmallString.h      |  85 +-----
+ llvm/include/llvm/Support/Errno.h        |   9 -
+ llvm/include/llvm/Support/VersionTuple.h |  39 ---
+ llvm/include/llvm/Support/raw_ostream.h  | 127 +--------
+ llvm/lib/Support/raw_ostream.cpp         | 329 -----------------------
+ 5 files changed, 10 insertions(+), 579 deletions(-)
+
+diff --git a/llvm/include/llvm/ADT/SmallString.h b/llvm/include/llvm/ADT/SmallString.h
+index 4d673cc8b1c49cf8a3f19653de53881cd12662ee..bfa965fd68a09d7ca3b332e38585ae37a5982ed0 100644
+--- a/llvm/include/llvm/ADT/SmallString.h
++++ b/llvm/include/llvm/ADT/SmallString.h
+@@ -88,49 +88,12 @@ public:
+   /// @name String Comparison
+   /// @{
+ 
+-  /// Check for string equality.  This is more efficient than compare() when
+-  /// the relative ordering of inequal strings isn't needed.
+-  bool equals(std::string_view RHS) const {
+-    return str().equals(RHS);
+-  }
+-
+-  /// Check for string equality, ignoring case.
+-  bool equals_insensitive(std::string_view RHS) const {
+-    return str().equals_insensitive(RHS);
+-  }
+-
+-  /// compare - Compare two strings; the result is negative, zero, or positive
+-  /// if this string is lexicographically less than, equal to, or greater than
+-  /// the \p RHS.
++  /// Compare two strings; the result is -1, 0, or 1 if this string is
++  /// lexicographically less than, equal to, or greater than the \p RHS.
+   int compare(std::string_view RHS) const {
+     return str().compare(RHS);
+   }
+ 
+-  /// compare_insensitive - Compare two strings, ignoring case.
+-  int compare_insensitive(std::string_view RHS) const {
+-    return str().compare_insensitive(RHS);
+-  }
+-
+-  /// compare_numeric - Compare two strings, treating sequences of digits as
+-  /// numbers.
+-  int compare_numeric(std::string_view RHS) const {
+-    return str().compare_numeric(RHS);
+-  }
+-
+-  /// @}
+-  /// @name String Predicates
+-  /// @{
+-
+-  /// startswith - Check if this string starts with the given \p Prefix.
+-  bool startswith(std::string_view Prefix) const {
+-    return str().startswith(Prefix);
+-  }
+-
+-  /// endswith - Check if this string ends with the given \p Suffix.
+-  bool endswith(std::string_view Suffix) const {
+-    return str().endswith(Suffix);
+-  }
+-
+   /// @}
+   /// @name String Searching
+   /// @{
+@@ -211,50 +174,6 @@ public:
+   }
+ 
+   /// @}
+-  /// @name Helpful Algorithms
+-  /// @{
+-
+-  /// Return the number of occurrences of \p C in the string.
+-  size_t count(char C) const {
+-    return str().count(C);
+-  }
+-
+-  /// Return the number of non-overlapped occurrences of \p Str in the
+-  /// string.
+-  size_t count(std::string_view Str) const {
+-    return str().count(Str);
+-  }
+-
+-  /// @}
+-  /// @name Substring Operations
+-  /// @{
+-
+-  /// Return a reference to the substring from [Start, Start + N).
+-  ///
+-  /// \param Start The index of the starting character in the substring; if
+-  /// the index is npos or greater than the length of the string then the
+-  /// empty substring will be returned.
+-  ///
+-  /// \param N The number of characters to included in the substring. If \p N
+-  /// exceeds the number of characters remaining in the string, the string
+-  /// suffix (starting with \p Start) will be returned.
+-  std::string_view substr(size_t Start, size_t N = std::string_view::npos) const {
+-    return str().substr(Start, N);
+-  }
+-
+-  /// Return a reference to the substring from [Start, End).
+-  ///
+-  /// \param Start The index of the starting character in the substring; if
+-  /// the index is npos or greater than the length of the string then the
+-  /// empty substring will be returned.
+-  ///
+-  /// \param End The index following the last character to include in the
+-  /// substring. If this is npos, or less than \p Start, or exceeds the
+-  /// number of characters remaining in the string, the string suffix
+-  /// (starting with \p Start) will be returned.
+-  std::string_view slice(size_t Start, size_t End) const {
+-    return str().slice(Start, End);
+-  }
+ 
+   // Extra methods.
+ 
+diff --git a/llvm/include/llvm/Support/Errno.h b/llvm/include/llvm/Support/Errno.h
+index e095c66b90860001d90b5c2eb74f6032de6de454..787805dac6c5e3c8cb85dabeb80254443d60806c 100644
+--- a/llvm/include/llvm/Support/Errno.h
++++ b/llvm/include/llvm/Support/Errno.h
+@@ -19,15 +19,6 @@
+ namespace llvm {
+ namespace sys {
+ 
+-/// Returns a string representation of the errno value, using whatever
+-/// thread-safe variant of strerror() is available.  Be sure to call this
+-/// immediately after the function that set errno, or errno may have been
+-/// overwritten by an intervening call.
+-std::string StrError();
+-
+-/// Like the no-argument version above, but uses \p errnum instead of errno.
+-std::string StrError(int errnum);
+-
+ template <typename FailT, typename Fun, typename... Args>
+ inline decltype(auto) RetryAfterSignal(const FailT &Fail, const Fun &F,
+                                        const Args &... As) {
+diff --git a/llvm/include/llvm/Support/VersionTuple.h b/llvm/include/llvm/Support/VersionTuple.h
+index 953b40701dc934c1a356b5413c9c6c692d5f5679..30cf717973fb15ff65a47a2d80795c351b0d2768 100644
+--- a/llvm/include/llvm/Support/VersionTuple.h
++++ b/llvm/include/llvm/Support/VersionTuple.h
+@@ -168,45 +168,6 @@ public:
+   friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) {
+     return !(X < Y);
+   }
+-
+-  friend hash_code hash_value(const VersionTuple &VT) {
+-    return hash_combine(VT.Major, VT.Minor, VT.Subminor, VT.Build);
+-  }
+-
+-  template <typename HasherT, llvm::support::endianness Endianness>
+-  friend void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder,
+-                      const VersionTuple &VT) {
+-    HBuilder.add(VT.Major, VT.Minor, VT.Subminor, VT.Build);
+-  }
+-
+-  /// Retrieve a string representation of the version number.
+-  std::string getAsString() const;
+-};
+-
+-/// Print a version number.
+-raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V);
+-
+-// Provide DenseMapInfo for version tuples.
+-template <> struct DenseMapInfo<VersionTuple> {
+-  static inline VersionTuple getEmptyKey() { return VersionTuple(0x7FFFFFFF); }
+-  static inline VersionTuple getTombstoneKey() {
+-    return VersionTuple(0x7FFFFFFE);
+-  }
+-  static unsigned getHashValue(const VersionTuple &Value) {
+-    unsigned Result = Value.getMajor();
+-    if (auto Minor = Value.getMinor())
+-      Result = detail::combineHashValue(Result, *Minor);
+-    if (auto Subminor = Value.getSubminor())
+-      Result = detail::combineHashValue(Result, *Subminor);
+-    if (auto Build = Value.getBuild())
+-      Result = detail::combineHashValue(Result, *Build);
+-
+-    return Result;
+-  }
+-
+-  static bool isEqual(const VersionTuple &LHS, const VersionTuple &RHS) {
+-    return LHS == RHS;
+-  }
+ };
+ 
+ } // end namespace llvm
+diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
+index 5d08596b4cf6bf9e9b8e2c2c1aef731bb8832da5..95019180a9deb406ed4f2991c664a4cc4e956dac 100644
+--- a/llvm/include/llvm/Support/raw_ostream.h
++++ b/llvm/include/llvm/Support/raw_ostream.h
+@@ -261,32 +261,6 @@ public:
+     return write(Str.data(), Str.size());
+   }
+ 
+-  raw_ostream &operator<<(unsigned long N);
+-  raw_ostream &operator<<(long N);
+-  raw_ostream &operator<<(unsigned long long N);
+-  raw_ostream &operator<<(long long N);
+-  raw_ostream &operator<<(const void *P);
+-
+-  raw_ostream &operator<<(unsigned int N) {
+-    return this->operator<<(static_cast<unsigned long>(N));
+-  }
+-
+-  raw_ostream &operator<<(int N) {
+-    return this->operator<<(static_cast<long>(N));
+-  }
+-
+-  raw_ostream &operator<<(double N);
+-
+-  /// Output \p N in hexadecimal, without any prefix or padding.
+-  raw_ostream &write_hex(unsigned long long N);
+-
+-  // Change the foreground color of text.
+-  raw_ostream &operator<<(Colors C);
+-
+-  /// Output a formatted UUID with dash separators.
+-  using uuid_t = uint8_t[16];
+-  raw_ostream &write_uuid(const uuid_t UUID);
+-
+   /// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't
+   /// satisfy llvm::isPrint into an escape sequence.
+   raw_ostream &write_escaped(std::string_view Str, bool UseHexEscapes = false);
+@@ -294,21 +268,6 @@ public:
+   raw_ostream &write(unsigned char C);
+   raw_ostream &write(const char *Ptr, size_t Size);
+ 
+-  // Formatted output, see the format() function in Support/Format.h.
+-  raw_ostream &operator<<(const format_object_base &Fmt);
+-
+-  // Formatted output, see the leftJustify() function in Support/Format.h.
+-  raw_ostream &operator<<(const FormattedString &);
+-
+-  // Formatted output, see the formatHex() function in Support/Format.h.
+-  raw_ostream &operator<<(const FormattedNumber &);
+-
+-  // Formatted output, see the formatv() function in Support/FormatVariadic.h.
+-  raw_ostream &operator<<(const formatv_object_base &);
+-
+-  // Formatted output, see the format_bytes() function in Support/Format.h.
+-  raw_ostream &operator<<(const FormattedBytes &);
+-
+   /// indent - Insert 'NumSpaces' spaces.
+   raw_ostream &indent(unsigned NumSpaces);
+ 
+@@ -323,14 +282,19 @@ public:
+   /// @param BG if true change the background, default: change foreground
+   /// @returns itself so it can be used within << invocations
+   virtual raw_ostream &changeColor(enum Colors Color, bool Bold = false,
+-                                   bool BG = false);
++                                   bool BG = false)  {
++    (void)Color;
++    (void)Bold;
++    (void)BG;
++    return *this;
++  }
+ 
+   /// Resets the colors to terminal defaults. Call this when you are done
+   /// outputting colored text, or before program exit.
+-  virtual raw_ostream &resetColor();
++  virtual raw_ostream &resetColor() { return *this; }
+ 
+   /// Reverses the foreground and background colors.
+-  virtual raw_ostream &reverseColor();
++  virtual raw_ostream &reverseColor() { return *this; }
+ 
+   /// This function determines if this stream is connected to a "tty" or
+   /// "console" window. That is, the output would be displayed to the user
+@@ -405,10 +369,6 @@ private:
+   /// unused bytes in the buffer.
+   void copy_to_buffer(const char *Ptr, size_t Size);
+ 
+-  /// Compute whether colors should be used and do the necessary work such as
+-  /// flushing. The result is affected by calls to enable_color().
+-  bool prepare_colors();
+-
+   /// Flush the tied-to stream (if present) and then write the required data.
+   void flush_tied_then_write(const char *Ptr, size_t Size);
+ 
+@@ -460,7 +420,6 @@ class raw_fd_ostream : public raw_pwrite_stream {
+   bool ShouldClose;
+   bool SupportsSeeking = false;
+   bool IsRegularFile = false;
+-  mutable std::optional<bool> HasColors;
+ 
+ #ifdef _WIN32
+   /// True if this fd refers to a Windows console device. Mintty and other
+@@ -536,10 +495,6 @@ public:
+   /// to the offset specified from the beginning of the file.
+   uint64_t seek(uint64_t off);
+ 
+-  bool is_displayed() const override;
+-
+-  bool has_colors() const override;
+-
+   std::error_code error() const { return EC; }
+ 
+   /// Return the value of the flag in this raw_fd_ostream indicating whether an
+@@ -558,38 +513,6 @@ public:
+   ///      - from The Zen of Python, by Tim Peters
+   ///
+   void clear_error() { EC = std::error_code(); }
+-
+-  /// Locks the underlying file.
+-  ///
+-  /// @returns RAII object that releases the lock upon leaving the scope, if the
+-  ///          locking was successful. Otherwise returns corresponding
+-  ///          error code.
+-  ///
+-  /// The function blocks the current thread until the lock become available or
+-  /// error occurs.
+-  ///
+-  /// Possible use of this function may be as follows:
+-  ///
+-  ///   @code{.cpp}
+-  ///   if (auto L = stream.lock()) {
+-  ///     // ... do action that require file to be locked.
+-  ///   } else {
+-  ///     handleAllErrors(std::move(L.takeError()), [&](ErrorInfoBase &EIB) {
+-  ///       // ... handle lock error.
+-  ///     });
+-  ///   }
+-  ///   @endcode
+-  [[nodiscard]] Expected<sys::fs::FileLocker> lock();
+-
+-  /// Tries to lock the underlying file within the specified period.
+-  ///
+-  /// @returns RAII object that releases the lock upon leaving the scope, if the
+-  ///          locking was successful. Otherwise returns corresponding
+-  ///          error code.
+-  ///
+-  /// It is used as @ref lock.
+-  [[nodiscard]] Expected<sys::fs::FileLocker>
+-  tryLockFor(Duration const &Timeout);
+ };
+ 
+ /// This returns a reference to a raw_fd_ostream for standard output. Use it
+@@ -619,17 +542,6 @@ public:
+   /// immediately destroyed.
+   raw_fd_stream(std::string_view Filename, std::error_code &EC);
+ 
+-  /// This reads the \p Size bytes into a buffer pointed by \p Ptr.
+-  ///
+-  /// \param Ptr The start of the buffer to hold data to be read.
+-  ///
+-  /// \param Size The number of bytes to be read.
+-  ///
+-  /// On success, the number of bytes read is returned, and the file position is
+-  /// advanced by this number. On error, -1 is returned, use error() to get the
+-  /// error code.
+-  ssize_t read(char *Ptr, size_t Size);
+-
+   /// Check if \p OS is a pointer of type raw_fd_stream*.
+   static bool classof(const raw_ostream *OS);
+ };
+@@ -747,29 +659,6 @@ public:
+   ~buffer_unique_ostream() override { *OS << str(); }
+ };
+ 
+-class Error;
+-
+-/// This helper creates an output stream and then passes it to \p Write.
+-/// The stream created is based on the specified \p OutputFileName:
+-/// llvm::outs for "-", raw_null_ostream for "/dev/null", and raw_fd_ostream
+-/// for other names. For raw_fd_ostream instances, the stream writes to
+-/// a temporary file. The final output file is atomically replaced with the
+-/// temporary file after the \p Write function is finished.
+-Error writeToOutput(std::string_view OutputFileName,
+-                    std::function<Error(raw_ostream &)> Write);
+-
+-raw_ostream &operator<<(raw_ostream &OS, std::nullopt_t);
+-
+-template <typename T, typename = decltype(std::declval<raw_ostream &>()
+-                                          << std::declval<const T &>())>
+-raw_ostream &operator<<(raw_ostream &OS, const std::optional<T> &O) {
+-  if (O)
+-    OS << *O;
+-  else
+-    OS << std::nullopt;
+-  return OS;
+-}
+-
+ } // end namespace llvm
+ 
+ #endif // LLVM_SUPPORT_RAW_OSTREAM_H
+diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
+index a23f567a37abdc199363607446f33f29e021d7ad..76c32155b4296fbbf3f4b164cd58d63f472ccd5e 100644
+--- a/llvm/lib/Support/raw_ostream.cpp
++++ b/llvm/lib/Support/raw_ostream.cpp
+@@ -18,7 +18,6 @@
+ #include "llvm/ADT/StringExtras.h"
+ #include "llvm/Config/config.h"
+ #include "llvm/Support/Compiler.h"
+-#include "llvm/Support/Duration.h"
+ #include "llvm/Support/ErrorHandling.h"
+ #include "llvm/Support/FileSystem.h"
+ #include "llvm/Support/Format.h"
+@@ -127,49 +126,6 @@ void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size,
+   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) {
+-  llvm::write_hex(*this, N, HexPrintStyle::Lower);
+-  return *this;
+-}
+-
+-raw_ostream &raw_ostream::operator<<(Colors C) {
+-  if (C == Colors::RESET)
+-    resetColor();
+-  else
+-    changeColor(C);
+-  return *this;
+-}
+-
+-raw_ostream &raw_ostream::write_uuid(const uuid_t UUID) {
+-  for (int Idx = 0; Idx < 16; ++Idx) {
+-    *this << format("%02" PRIX32, UUID[Idx]);
+-    if (Idx == 3 || Idx == 5 || Idx == 7 || Idx == 9)
+-      *this << "-";
+-  }
+-  return *this;
+-}
+-
+-
+ raw_ostream &raw_ostream::write_escaped(std::string_view Str,
+                                         bool UseHexEscapes) {
+   for (unsigned char c : Str) {
+@@ -315,173 +271,6 @@ void raw_ostream::flush_tied_then_write(const char *Ptr, size_t Size) {
+   write_impl(Ptr, 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 formatv_object_base &Obj) {
+-  Obj.format(*this);
+-  return *this;
+-}
+-
+-raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
+-  unsigned LeftIndent = 0;
+-  unsigned RightIndent = 0;
+-  const ssize_t Difference = FS.Width - FS.Str.size();
+-  if (Difference > 0) {
+-    switch (FS.Justify) {
+-    case FormattedString::JustifyNone:
+-      break;
+-    case FormattedString::JustifyLeft:
+-      RightIndent = Difference;
+-      break;
+-    case FormattedString::JustifyRight:
+-      LeftIndent = Difference;
+-      break;
+-    case FormattedString::JustifyCenter:
+-      LeftIndent = Difference / 2;
+-      RightIndent = Difference - LeftIndent;
+-      break;
+-    }
+-  }
+-  indent(LeftIndent);
+-  (*this) << FS.Str;
+-  indent(RightIndent);
+-  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;
+-    llvm::write_hex(*this, FN.HexValue, Style, FN.Width);
+-  } else {
+-    llvm::SmallString<16> Buffer;
+-    llvm::raw_svector_ostream Stream(Buffer);
+-    llvm::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) {
+-    // 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 = llvm::Log2_64_Ceil(MaxOffset);
+-    OffsetWidth = std::max<uint64_t>(4, llvm::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) {
+-      uint64_t Offset = *FB.FirstByteOffset;
+-      llvm::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 << " ";
+-      }
+-      llvm::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,
+@@ -512,63 +301,8 @@ raw_ostream &raw_ostream::write_zeros(unsigned NumZeros) {
+   return write_padding<'\0'>(*this, NumZeros);
+ }
+ 
+-bool raw_ostream::prepare_colors() {
+-  // Colors were explicitly disabled.
+-  if (!ColorEnabled)
+-    return false;
+-
+-  // Colors require changing the terminal but this stream is not going to a
+-  // terminal.
+-  if (sys::Process::ColorNeedsFlush() && !is_displayed())
+-    return false;
+-
+-  if (sys::Process::ColorNeedsFlush())
+-    flush();
+-
+-  return true;
+-}
+-
+-raw_ostream &raw_ostream::changeColor(enum Colors colors, bool bold, bool bg) {
+-  if (!prepare_colors())
+-    return *this;
+-
+-  const char *colorcode =
+-      (colors == SAVEDCOLOR)
+-          ? sys::Process::OutputBold(bg)
+-          : sys::Process::OutputColor(static_cast<char>(colors), bold, bg);
+-  if (colorcode)
+-    write(colorcode, strlen(colorcode));
+-  return *this;
+-}
+-
+-raw_ostream &raw_ostream::resetColor() {
+-  if (!prepare_colors())
+-    return *this;
+-
+-  if (const char *colorcode = sys::Process::ResetColor())
+-    write(colorcode, strlen(colorcode));
+-  return *this;
+-}
+-
+-raw_ostream &raw_ostream::reverseColor() {
+-  if (!prepare_colors())
+-    return *this;
+-
+-  if (const char *colorcode = sys::Process::OutputReverse())
+-    write(colorcode, strlen(colorcode));
+-  return *this;
+-}
+-
+ void raw_ostream::anchor() {}
+ 
+-//===----------------------------------------------------------------------===//
+-//  Formatted Output
+-//===----------------------------------------------------------------------===//
+-
+-// Out of line virtual method.
+-void format_object_base::home() {
+-}
+-
+ //===----------------------------------------------------------------------===//
+ //  raw_fd_ostream
+ //===----------------------------------------------------------------------===//
+@@ -867,31 +601,6 @@ size_t raw_fd_ostream::preferred_buffer_size() const {
+ #endif
+ }
+ 
+-bool raw_fd_ostream::is_displayed() const {
+-  return sys::Process::FileDescriptorIsDisplayed(FD);
+-}
+-
+-bool raw_fd_ostream::has_colors() const {
+-  if (!HasColors)
+-    HasColors = sys::Process::FileDescriptorHasColors(FD);
+-  return *HasColors;
+-}
+-
+-Expected<sys::fs::FileLocker> raw_fd_ostream::lock() {
+-  std::error_code EC = sys::fs::lockFile(FD);
+-  if (!EC)
+-    return sys::fs::FileLocker(FD);
+-  return errorCodeToError(EC);
+-}
+-
+-Expected<sys::fs::FileLocker>
+-raw_fd_ostream::tryLockFor(Duration const& Timeout) {
+-  std::error_code EC = sys::fs::tryLockFile(FD, Timeout.getDuration());
+-  if (!EC)
+-    return sys::fs::FileLocker(FD);
+-  return errorCodeToError(EC);
+-}
+-
+ void raw_fd_ostream::anchor() {}
+ 
+ //===----------------------------------------------------------------------===//
+@@ -934,16 +643,6 @@ raw_fd_stream::raw_fd_stream(std::string_view Filename, std::error_code &EC)
+     EC = std::make_error_code(std::errc::invalid_argument);
+ }
+ 
+-ssize_t raw_fd_stream::read(char *Ptr, size_t Size) {
+-  assert(get_fd() >= 0 && "File already closed.");
+-  ssize_t Ret = ::read(get_fd(), (void *)Ptr, Size);
+-  if (Ret >= 0)
+-    inc_pos(Ret);
+-  else
+-    error_detected(std::error_code(errno, std::generic_category()));
+-  return Ret;
+-}
+-
+ bool raw_fd_stream::classof(const raw_ostream *OS) {
+   return OS->get_kind() == OStreamKind::OK_FDStream;
+ }
+@@ -999,31 +698,3 @@ void raw_pwrite_stream::anchor() {}
+ void buffer_ostream::anchor() {}
+ 
+ void buffer_unique_ostream::anchor() {}
+-
+-Error llvm::writeToOutput(std::string_view OutputFileName,
+-                          std::function<Error(raw_ostream &)> Write) {
+-  if (OutputFileName == "-")
+-    return Write(outs());
+-
+-  if (OutputFileName == "/dev/null") {
+-    raw_null_ostream Out;
+-    return Write(Out);
+-  }
+-
+-  unsigned Mode = sys::fs::all_read | sys::fs::all_write;
+-  Expected<sys::fs::TempFile> Temp =
+-      sys::fs::TempFile::create(OutputFileName + ".temp-stream-%%%%%%", Mode);
+-  if (!Temp)
+-    return createFileError(OutputFileName, Temp.takeError());
+-
+-  raw_fd_ostream Out(Temp->FD, false);
+-
+-  if (Error E = Write(Out)) {
+-    if (Error DiscardError = Temp->discard())
+-      return joinErrors(std::move(E), std::move(DiscardError));
+-    return E;
+-  }
+-  Out.flush();
+-
+-  return Temp->keep(OutputFileName);
+-}
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0010-Detemplatize-SmallVectorBase.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0010-Detemplatize-SmallVectorBase.patch
new file mode 100644
index 0000000..8d28e0f
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0010-Detemplatize-SmallVectorBase.patch
@@ -0,0 +1,167 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Thu, 5 May 2022 23:18:34 -0400
+Subject: [PATCH 10/31] Detemplatize SmallVectorBase
+
+---
+ llvm/include/llvm/ADT/SmallVector.h | 27 +++++++--------------
+ llvm/lib/Support/SmallVector.cpp    | 37 +++++------------------------
+ 2 files changed, 14 insertions(+), 50 deletions(-)
+
+diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h
+index b42438a9b16c273f9ef5b5cce6192873c78cb964..7775ed7e8e083908f033529c30b1e4beae91b10a 100644
+--- a/llvm/include/llvm/ADT/SmallVector.h
++++ b/llvm/include/llvm/ADT/SmallVector.h
+@@ -56,14 +56,14 @@ using EnableIfConvertibleToInputIterator = std::enable_if_t<std::is_convertible<
+ /// Using 64 bit size is desirable for cases like SmallVector<char>, where a
+ /// 32 bit size would limit the vector to ~4GB. SmallVectors are used for
+ /// buffering bitcode output - which can exceed 4GB.
+-template <class Size_T> class SmallVectorBase {
++class SmallVectorBase {
+ protected:
+   void *BeginX;
+-  Size_T Size = 0, Capacity;
++  unsigned Size = 0, Capacity;
+ 
+   /// The maximum value of the Size_T used.
+   static constexpr size_t SizeTypeMax() {
+-    return (std::numeric_limits<Size_T>::max)();
++    return (std::numeric_limits<unsigned>::max)();
+   }
+ 
+   SmallVectorBase() = delete;
+@@ -111,15 +111,10 @@ protected:
+   }
+ };
+ 
+-template <class T>
+-using SmallVectorSizeType =
+-    std::conditional_t<sizeof(T) < 4 && sizeof(void *) >= 8, uint64_t,
+-                       uint32_t>;
+-
+ /// Figure out the offset of the first element.
+ template <class T, typename = void> struct SmallVectorAlignmentAndSize {
+-  alignas(SmallVectorBase<SmallVectorSizeType<T>>) char Base[sizeof(
+-      SmallVectorBase<SmallVectorSizeType<T>>)];
++  alignas(SmallVectorBase) char Base[sizeof(
++      SmallVectorBase)];
+   alignas(T) char FirstEl[sizeof(T)];
+ };
+ 
+@@ -128,8 +123,8 @@ template <class T, typename = void> struct SmallVectorAlignmentAndSize {
+ /// to avoid unnecessarily requiring T to be complete.
+ template <typename T, typename = void>
+ class SmallVectorTemplateCommon
+-    : public SmallVectorBase<SmallVectorSizeType<T>> {
+-  using Base = SmallVectorBase<SmallVectorSizeType<T>>;
++    : public SmallVectorBase {
++  using Base = SmallVectorBase;
+ 
+ protected:
+   /// Find the address of the first element.  For this pointer math to be valid
+@@ -451,7 +446,7 @@ template <typename T, bool TriviallyCopyable>
+ T *SmallVectorTemplateBase<T, TriviallyCopyable>::mallocForGrow(
+     size_t MinSize, size_t &NewCapacity) {
+   return static_cast<T *>(
+-      SmallVectorBase<SmallVectorSizeType<T>>::mallocForGrow(
++      SmallVectorBase::mallocForGrow(
+           this->getFirstEl(), MinSize, sizeof(T), NewCapacity));
+ }
+ 
+@@ -1324,12 +1319,6 @@ template <typename Out, typename R> SmallVector<Out> to_vector_of(R &&Range) {
+   return {std::begin(Range), std::end(Range)};
+ }
+ 
+-// Explicit instantiations
+-extern template class llvm::SmallVectorBase<uint32_t>;
+-#if SIZE_MAX > UINT32_MAX
+-extern template class llvm::SmallVectorBase<uint64_t>;
+-#endif
+-
+ } // end namespace llvm
+ 
+ namespace std {
+diff --git a/llvm/lib/Support/SmallVector.cpp b/llvm/lib/Support/SmallVector.cpp
+index 6cefdff7c28060ca18b522acf5279af3a206e23a..ae64a36dcf4b9ceaf8767adbf8100f164f3738ac 100644
+--- a/llvm/lib/Support/SmallVector.cpp
++++ b/llvm/lib/Support/SmallVector.cpp
+@@ -51,10 +51,6 @@ static_assert(sizeof(SmallVector<void *, 1>) ==
+                   sizeof(unsigned) * 2 + sizeof(void *) * 2,
+               "wasted space in SmallVector size 1");
+ 
+-static_assert(sizeof(SmallVector<char, 0>) ==
+-                  sizeof(void *) * 2 + sizeof(void *),
+-              "1 byte elements have word-sized type for size and capacity");
+-
+ /// Report that MinSize doesn't fit into this vector's size type. Throws
+ /// std::length_error or calls report_fatal_error.
+ [[noreturn]] static void report_size_overflow(size_t MinSize, size_t MaxSize);
+@@ -85,9 +81,8 @@ static void report_at_maximum_capacity(size_t MaxSize) {
+ }
+ 
+ // Note: Moving this function into the header may cause performance regression.
+-template <class Size_T>
+ static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) {
+-  constexpr size_t MaxSize = std::numeric_limits<Size_T>::max();
++  constexpr size_t MaxSize = std::numeric_limits<unsigned>::max();
+ 
+   // Ensure we can fit the new capacity.
+   // This is only going to be applicable when the capacity is 32 bit.
+@@ -107,8 +102,7 @@ static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) {
+   return std::clamp(NewCapacity, MinSize, MaxSize);
+ }
+ 
+-template <class Size_T>
+-void *SmallVectorBase<Size_T>::replaceAllocation(void *NewElts, size_t TSize,
++void *SmallVectorBase::replaceAllocation(void *NewElts, size_t TSize,
+                                                  size_t NewCapacity,
+                                                  size_t VSize) {
+   void *NewEltsReplace = llvm::safe_malloc(NewCapacity * TSize);
+@@ -119,11 +113,10 @@ void *SmallVectorBase<Size_T>::replaceAllocation(void *NewElts, size_t TSize,
+ }
+ 
+ // Note: Moving this function into the header may cause performance regression.
+-template <class Size_T>
+-void *SmallVectorBase<Size_T>::mallocForGrow(void *FirstEl, size_t MinSize,
++void *SmallVectorBase::mallocForGrow(void *FirstEl, size_t MinSize,
+                                              size_t TSize,
+                                              size_t &NewCapacity) {
+-  NewCapacity = getNewCapacity<Size_T>(MinSize, TSize, this->capacity());
++  NewCapacity = getNewCapacity(MinSize, TSize, this->capacity());
+   // Even if capacity is not 0 now, if the vector was originally created with
+   // capacity 0, it's possible for the malloc to return FirstEl.
+   void *NewElts = llvm::safe_malloc(NewCapacity * TSize);
+@@ -133,10 +126,9 @@ void *SmallVectorBase<Size_T>::mallocForGrow(void *FirstEl, size_t MinSize,
+ }
+ 
+ // Note: Moving this function into the header may cause performance regression.
+-template <class Size_T>
+-void SmallVectorBase<Size_T>::grow_pod(void *FirstEl, size_t MinSize,
++void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSize,
+                                        size_t TSize) {
+-  size_t NewCapacity = getNewCapacity<Size_T>(MinSize, TSize, this->capacity());
++  size_t NewCapacity = getNewCapacity(MinSize, TSize, this->capacity());
+   void *NewElts;
+   if (BeginX == FirstEl) {
+     NewElts = llvm::safe_malloc(NewCapacity * TSize);
+@@ -155,20 +147,3 @@ void SmallVectorBase<Size_T>::grow_pod(void *FirstEl, size_t MinSize,
+   this->BeginX = NewElts;
+   this->Capacity = NewCapacity;
+ }
+-
+-template class llvm::SmallVectorBase<uint32_t>;
+-
+-// Disable the uint64_t instantiation for 32-bit builds.
+-// Both uint32_t and uint64_t instantiations are needed for 64-bit builds.
+-// This instantiation will never be used in 32-bit builds, and will cause
+-// warnings when sizeof(Size_T) > sizeof(size_t).
+-#if SIZE_MAX > UINT32_MAX
+-template class llvm::SmallVectorBase<uint64_t>;
+-
+-// Assertions to ensure this #if stays in sync with SmallVectorSizeType.
+-static_assert(sizeof(SmallVectorSizeType<char>) == sizeof(uint64_t),
+-              "Expected SmallVectorBase<uint64_t> variant to be in use.");
+-#else
+-static_assert(sizeof(SmallVectorSizeType<char>) == sizeof(uint32_t),
+-              "Expected SmallVectorBase<uint32_t> variant to be in use.");
+-#endif
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0010-Remove-unused-functions.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0010-Remove-unused-functions.patch
deleted file mode 100644
index 50f5300..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0010-Remove-unused-functions.patch
+++ /dev/null
@@ -1,762 +0,0 @@
-From df2dc9fdb3d57e01423104a71a6a1d1d6382644a Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sun, 8 May 2022 13:43:50 -0400
-Subject: [PATCH 10/28] Remove unused functions
-
----
- llvm/include/llvm/ADT/SmallString.h      |  80 ------
- llvm/include/llvm/Support/Errno.h        |   9 -
- llvm/include/llvm/Support/VersionTuple.h |  40 ---
- llvm/include/llvm/Support/raw_ostream.h  | 115 +-------
- llvm/lib/Support/raw_ostream.cpp         | 328 -----------------------
- 5 files changed, 8 insertions(+), 564 deletions(-)
-
-diff --git a/llvm/include/llvm/ADT/SmallString.h b/llvm/include/llvm/ADT/SmallString.h
-index 50cbdade4..bfa965fd6 100644
---- a/llvm/include/llvm/ADT/SmallString.h
-+++ b/llvm/include/llvm/ADT/SmallString.h
-@@ -88,48 +88,12 @@ public:
-   /// @name String Comparison
-   /// @{
- 
--  /// Check for string equality.  This is more efficient than compare() when
--  /// the relative ordering of inequal strings isn't needed.
--  bool equals(std::string_view RHS) const {
--    return str().equals(RHS);
--  }
--
--  /// Check for string equality, ignoring case.
--  bool equals_insensitive(std::string_view RHS) const {
--    return str().equals_insensitive(RHS);
--  }
--
-   /// Compare two strings; the result is -1, 0, or 1 if this string is
-   /// lexicographically less than, equal to, or greater than the \p RHS.
-   int compare(std::string_view RHS) const {
-     return str().compare(RHS);
-   }
- 
--  /// compare_insensitive - Compare two strings, ignoring case.
--  int compare_insensitive(std::string_view RHS) const {
--    return str().compare_insensitive(RHS);
--  }
--
--  /// compare_numeric - Compare two strings, treating sequences of digits as
--  /// numbers.
--  int compare_numeric(std::string_view RHS) const {
--    return str().compare_numeric(RHS);
--  }
--
--  /// @}
--  /// @name String Predicates
--  /// @{
--
--  /// startswith - Check if this string starts with the given \p Prefix.
--  bool startswith(std::string_view Prefix) const {
--    return str().startswith(Prefix);
--  }
--
--  /// endswith - Check if this string ends with the given \p Suffix.
--  bool endswith(std::string_view Suffix) const {
--    return str().endswith(Suffix);
--  }
--
-   /// @}
-   /// @name String Searching
-   /// @{
-@@ -210,50 +174,6 @@ public:
-   }
- 
-   /// @}
--  /// @name Helpful Algorithms
--  /// @{
--
--  /// Return the number of occurrences of \p C in the string.
--  size_t count(char C) const {
--    return str().count(C);
--  }
--
--  /// Return the number of non-overlapped occurrences of \p Str in the
--  /// string.
--  size_t count(std::string_view Str) const {
--    return str().count(Str);
--  }
--
--  /// @}
--  /// @name Substring Operations
--  /// @{
--
--  /// Return a reference to the substring from [Start, Start + N).
--  ///
--  /// \param Start The index of the starting character in the substring; if
--  /// the index is npos or greater than the length of the string then the
--  /// empty substring will be returned.
--  ///
--  /// \param N The number of characters to included in the substring. If \p N
--  /// exceeds the number of characters remaining in the string, the string
--  /// suffix (starting with \p Start) will be returned.
--  std::string_view substr(size_t Start, size_t N = std::string_view::npos) const {
--    return str().substr(Start, N);
--  }
--
--  /// Return a reference to the substring from [Start, End).
--  ///
--  /// \param Start The index of the starting character in the substring; if
--  /// the index is npos or greater than the length of the string then the
--  /// empty substring will be returned.
--  ///
--  /// \param End The index following the last character to include in the
--  /// substring. If this is npos, or less than \p Start, or exceeds the
--  /// number of characters remaining in the string, the string suffix
--  /// (starting with \p Start) will be returned.
--  std::string_view slice(size_t Start, size_t End) const {
--    return str().slice(Start, End);
--  }
- 
-   // Extra methods.
- 
-diff --git a/llvm/include/llvm/Support/Errno.h b/llvm/include/llvm/Support/Errno.h
-index 07df6765d..d9bf68bb3 100644
---- a/llvm/include/llvm/Support/Errno.h
-+++ b/llvm/include/llvm/Support/Errno.h
-@@ -20,15 +20,6 @@
- namespace llvm {
- namespace sys {
- 
--/// Returns a string representation of the errno value, using whatever
--/// thread-safe variant of strerror() is available.  Be sure to call this
--/// immediately after the function that set errno, or errno may have been
--/// overwritten by an intervening call.
--std::string StrError();
--
--/// Like the no-argument version above, but uses \p errnum instead of errno.
--std::string StrError(int errnum);
--
- template <typename FailT, typename Fun, typename... Args>
- inline decltype(auto) RetryAfterSignal(const FailT &Fail, const Fun &F,
-                                        const Args &... As) {
-diff --git a/llvm/include/llvm/Support/VersionTuple.h b/llvm/include/llvm/Support/VersionTuple.h
-index 3d6573bf5..a28e12adc 100644
---- a/llvm/include/llvm/Support/VersionTuple.h
-+++ b/llvm/include/llvm/Support/VersionTuple.h
-@@ -16,7 +16,6 @@
- 
- #include "llvm/ADT/DenseMapInfo.h"
- #include "llvm/ADT/Hashing.h"
--#include "llvm/Support/HashBuilder.h"
- #include <optional>
- #include <string>
- #include <tuple>
-@@ -159,45 +158,6 @@ public:
-   friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) {
-     return !(X < Y);
-   }
--
--  friend llvm::hash_code hash_value(const VersionTuple &VT) {
--    return llvm::hash_combine(VT.Major, VT.Minor, VT.Subminor, VT.Build);
--  }
--
--  template <typename HasherT, llvm::support::endianness Endianness>
--  friend void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder,
--                      const VersionTuple &VT) {
--    HBuilder.add(VT.Major, VT.Minor, VT.Subminor, VT.Build);
--  }
--
--  /// Retrieve a string representation of the version number.
--  std::string getAsString() const;
--};
--
--/// Print a version number.
--raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V);
--
--// Provide DenseMapInfo for version tuples.
--template <> struct DenseMapInfo<VersionTuple> {
--  static inline VersionTuple getEmptyKey() { return VersionTuple(0x7FFFFFFF); }
--  static inline VersionTuple getTombstoneKey() {
--    return VersionTuple(0x7FFFFFFE);
--  }
--  static unsigned getHashValue(const VersionTuple &Value) {
--    unsigned Result = Value.getMajor();
--    if (auto Minor = Value.getMinor())
--      Result = detail::combineHashValue(Result, *Minor);
--    if (auto Subminor = Value.getSubminor())
--      Result = detail::combineHashValue(Result, *Subminor);
--    if (auto Build = Value.getBuild())
--      Result = detail::combineHashValue(Result, *Build);
--
--    return Result;
--  }
--
--  static bool isEqual(const VersionTuple &LHS, const VersionTuple &RHS) {
--    return LHS == RHS;
--  }
- };
- 
- } // end namespace llvm
-diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
-index a25ca5b7b..d4521c8e2 100644
---- a/llvm/include/llvm/Support/raw_ostream.h
-+++ b/llvm/include/llvm/Support/raw_ostream.h
-@@ -248,32 +248,6 @@ public:
-     return write(Str.data(), Str.size());
-   }
- 
--  raw_ostream &operator<<(unsigned long N);
--  raw_ostream &operator<<(long N);
--  raw_ostream &operator<<(unsigned long long N);
--  raw_ostream &operator<<(long long N);
--  raw_ostream &operator<<(const void *P);
--
--  raw_ostream &operator<<(unsigned int N) {
--    return this->operator<<(static_cast<unsigned long>(N));
--  }
--
--  raw_ostream &operator<<(int N) {
--    return this->operator<<(static_cast<long>(N));
--  }
--
--  raw_ostream &operator<<(double N);
--
--  /// Output \p N in hexadecimal, without any prefix or padding.
--  raw_ostream &write_hex(unsigned long long N);
--
--  // Change the foreground color of text.
--  raw_ostream &operator<<(Colors C);
--
--  /// Output a formatted UUID with dash separators.
--  using uuid_t = uint8_t[16];
--  raw_ostream &write_uuid(const uuid_t UUID);
--
-   /// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't
-   /// satisfy llvm::isPrint into an escape sequence.
-   raw_ostream &write_escaped(std::string_view Str, bool UseHexEscapes = false);
-@@ -281,21 +255,6 @@ public:
-   raw_ostream &write(unsigned char C);
-   raw_ostream &write(const char *Ptr, size_t Size);
- 
--  // Formatted output, see the format() function in Support/Format.h.
--  raw_ostream &operator<<(const format_object_base &Fmt);
--
--  // Formatted output, see the leftJustify() function in Support/Format.h.
--  raw_ostream &operator<<(const FormattedString &);
--
--  // Formatted output, see the formatHex() function in Support/Format.h.
--  raw_ostream &operator<<(const FormattedNumber &);
--
--  // Formatted output, see the formatv() function in Support/FormatVariadic.h.
--  raw_ostream &operator<<(const formatv_object_base &);
--
--  // Formatted output, see the format_bytes() function in Support/Format.h.
--  raw_ostream &operator<<(const FormattedBytes &);
--
-   /// indent - Insert 'NumSpaces' spaces.
-   raw_ostream &indent(unsigned NumSpaces);
- 
-@@ -310,14 +269,19 @@ public:
-   /// @param BG if true change the background, default: change foreground
-   /// @returns itself so it can be used within << invocations
-   virtual raw_ostream &changeColor(enum Colors Color, bool Bold = false,
--                                   bool BG = false);
-+                                   bool BG = false)  {
-+    (void)Color;
-+    (void)Bold;
-+    (void)BG;
-+    return *this;
-+  }
- 
-   /// Resets the colors to terminal defaults. Call this when you are done
-   /// outputting colored text, or before program exit.
--  virtual raw_ostream &resetColor();
-+  virtual raw_ostream &resetColor() { return *this; }
- 
-   /// Reverses the foreground and background colors.
--  virtual raw_ostream &reverseColor();
-+  virtual raw_ostream &reverseColor() { return *this; }
- 
-   /// This function determines if this stream is connected to a "tty" or
-   /// "console" window. That is, the output would be displayed to the user
-@@ -392,10 +356,6 @@ private:
-   /// unused bytes in the buffer.
-   void copy_to_buffer(const char *Ptr, size_t Size);
- 
--  /// Compute whether colors should be used and do the necessary work such as
--  /// flushing. The result is affected by calls to enable_color().
--  bool prepare_colors();
--
-   /// Flush the tied-to stream (if present) and then write the required data.
-   void flush_tied_then_write(const char *Ptr, size_t Size);
- 
-@@ -447,7 +407,6 @@ class raw_fd_ostream : public raw_pwrite_stream {
-   bool ShouldClose;
-   bool SupportsSeeking = false;
-   bool IsRegularFile = false;
--  mutable std::optional<bool> HasColors;
- 
- #ifdef _WIN32
-   /// True if this fd refers to a Windows console device. Mintty and other
-@@ -523,10 +482,6 @@ public:
-   /// to the offset specified from the beginning of the file.
-   uint64_t seek(uint64_t off);
- 
--  bool is_displayed() const override;
--
--  bool has_colors() const override;
--
-   std::error_code error() const { return EC; }
- 
-   /// Return the value of the flag in this raw_fd_ostream indicating whether an
-@@ -545,38 +500,6 @@ public:
-   ///      - from The Zen of Python, by Tim Peters
-   ///
-   void clear_error() { EC = std::error_code(); }
--
--  /// Locks the underlying file.
--  ///
--  /// @returns RAII object that releases the lock upon leaving the scope, if the
--  ///          locking was successful. Otherwise returns corresponding
--  ///          error code.
--  ///
--  /// The function blocks the current thread until the lock become available or
--  /// error occurs.
--  ///
--  /// Possible use of this function may be as follows:
--  ///
--  ///   @code{.cpp}
--  ///   if (auto L = stream.lock()) {
--  ///     // ... do action that require file to be locked.
--  ///   } else {
--  ///     handleAllErrors(std::move(L.takeError()), [&](ErrorInfoBase &EIB) {
--  ///       // ... handle lock error.
--  ///     });
--  ///   }
--  ///   @endcode
--  LLVM_NODISCARD Expected<sys::fs::FileLocker> lock();
--
--  /// Tries to lock the underlying file within the specified period.
--  ///
--  /// @returns RAII object that releases the lock upon leaving the scope, if the
--  ///          locking was successful. Otherwise returns corresponding
--  ///          error code.
--  ///
--  /// It is used as @ref lock.
--  LLVM_NODISCARD
--  Expected<sys::fs::FileLocker> tryLockFor(Duration const& Timeout);
- };
- 
- /// This returns a reference to a raw_fd_ostream for standard output. Use it
-@@ -606,17 +529,6 @@ public:
-   /// immediately destroyed.
-   raw_fd_stream(std::string_view Filename, std::error_code &EC);
- 
--  /// This reads the \p Size bytes into a buffer pointed by \p Ptr.
--  ///
--  /// \param Ptr The start of the buffer to hold data to be read.
--  ///
--  /// \param Size The number of bytes to be read.
--  ///
--  /// On success, the number of bytes read is returned, and the file position is
--  /// advanced by this number. On error, -1 is returned, use error() to get the
--  /// error code.
--  ssize_t read(char *Ptr, size_t Size);
--
-   /// Check if \p OS is a pointer of type raw_fd_stream*.
-   static bool classof(const raw_ostream *OS);
- };
-@@ -734,17 +646,6 @@ public:
-   ~buffer_unique_ostream() override { *OS << str(); }
- };
- 
--class Error;
--
--/// This helper creates an output stream and then passes it to \p Write.
--/// The stream created is based on the specified \p OutputFileName:
--/// llvm::outs for "-", raw_null_ostream for "/dev/null", and raw_fd_ostream
--/// for other names. For raw_fd_ostream instances, the stream writes to
--/// a temporary file. The final output file is atomically replaced with the
--/// temporary file after the \p Write function is finished.
--Error writeToOutput(std::string_view OutputFileName,
--                    std::function<Error(raw_ostream &)> Write);
--
- } // end namespace llvm
- 
- #endif // LLVM_SUPPORT_RAW_OSTREAM_H
-diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
-index ee01a9522..875eda7ba 100644
---- a/llvm/lib/Support/raw_ostream.cpp
-+++ b/llvm/lib/Support/raw_ostream.cpp
-@@ -19,7 +19,6 @@
- #include "llvm/ADT/StringExtras.h"
- #include "llvm/Config/config.h"
- #include "llvm/Support/Compiler.h"
--#include "llvm/Support/Duration.h"
- #include "llvm/Support/ErrorHandling.h"
- #include "llvm/Support/FileSystem.h"
- #include "llvm/Support/Format.h"
-@@ -120,49 +119,6 @@ void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size,
-   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) {
--  llvm::write_hex(*this, N, HexPrintStyle::Lower);
--  return *this;
--}
--
--raw_ostream &raw_ostream::operator<<(Colors C) {
--  if (C == Colors::RESET)
--    resetColor();
--  else
--    changeColor(C);
--  return *this;
--}
--
--raw_ostream &raw_ostream::write_uuid(const uuid_t UUID) {
--  for (int Idx = 0; Idx < 16; ++Idx) {
--    *this << format("%02" PRIX32, UUID[Idx]);
--    if (Idx == 3 || Idx == 5 || Idx == 7 || Idx == 9)
--      *this << "-";
--  }
--  return *this;
--}
--
--
- raw_ostream &raw_ostream::write_escaped(std::string_view Str,
-                                         bool UseHexEscapes) {
-   for (unsigned char c : Str) {
-@@ -308,172 +264,6 @@ void raw_ostream::flush_tied_then_write(const char *Ptr, size_t Size) {
-   write_impl(Ptr, 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 formatv_object_base &Obj) {
--  Obj.format(*this);
--  return *this;
--}
--
--raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
--  unsigned LeftIndent = 0;
--  unsigned RightIndent = 0;
--  const ssize_t Difference = FS.Width - FS.Str.size();
--  if (Difference > 0) {
--    switch (FS.Justify) {
--    case FormattedString::JustifyNone:
--      break;
--    case FormattedString::JustifyLeft:
--      RightIndent = Difference;
--      break;
--    case FormattedString::JustifyRight:
--      LeftIndent = Difference;
--      break;
--    case FormattedString::JustifyCenter:
--      LeftIndent = Difference / 2;
--      RightIndent = Difference - LeftIndent;
--      break;
--    }
--  }
--  indent(LeftIndent);
--  (*this) << FS.Str;
--  indent(RightIndent);
--  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;
--    llvm::write_hex(*this, FN.HexValue, Style, FN.Width);
--  } else {
--    llvm::SmallString<16> Buffer;
--    llvm::raw_svector_ostream Stream(Buffer);
--    llvm::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.hasValue()) {
--    // 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 = llvm::Log2_64_Ceil(MaxOffset);
--    OffsetWidth = std::max<uint64_t>(4, llvm::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.hasValue()) {
--      uint64_t Offset = FB.FirstByteOffset.getValue();
--      llvm::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 << " ";
--      }
--      llvm::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) {
-@@ -506,63 +296,8 @@ raw_ostream &raw_ostream::write_zeros(unsigned NumZeros) {
-   return write_padding<'\0'>(*this, NumZeros);
- }
- 
--bool raw_ostream::prepare_colors() {
--  // Colors were explicitly disabled.
--  if (!ColorEnabled)
--    return false;
--
--  // Colors require changing the terminal but this stream is not going to a
--  // terminal.
--  if (sys::Process::ColorNeedsFlush() && !is_displayed())
--    return false;
--
--  if (sys::Process::ColorNeedsFlush())
--    flush();
--
--  return true;
--}
--
--raw_ostream &raw_ostream::changeColor(enum Colors colors, bool bold, bool bg) {
--  if (!prepare_colors())
--    return *this;
--
--  const char *colorcode =
--      (colors == SAVEDCOLOR)
--          ? sys::Process::OutputBold(bg)
--          : sys::Process::OutputColor(static_cast<char>(colors), bold, bg);
--  if (colorcode)
--    write(colorcode, strlen(colorcode));
--  return *this;
--}
--
--raw_ostream &raw_ostream::resetColor() {
--  if (!prepare_colors())
--    return *this;
--
--  if (const char *colorcode = sys::Process::ResetColor())
--    write(colorcode, strlen(colorcode));
--  return *this;
--}
--
--raw_ostream &raw_ostream::reverseColor() {
--  if (!prepare_colors())
--    return *this;
--
--  if (const char *colorcode = sys::Process::OutputReverse())
--    write(colorcode, strlen(colorcode));
--  return *this;
--}
--
- void raw_ostream::anchor() {}
- 
--//===----------------------------------------------------------------------===//
--//  Formatted Output
--//===----------------------------------------------------------------------===//
--
--// Out of line virtual method.
--void format_object_base::home() {
--}
--
- //===----------------------------------------------------------------------===//
- //  raw_fd_ostream
- //===----------------------------------------------------------------------===//
-@@ -854,31 +589,6 @@ size_t raw_fd_ostream::preferred_buffer_size() const {
- #endif
- }
- 
--bool raw_fd_ostream::is_displayed() const {
--  return sys::Process::FileDescriptorIsDisplayed(FD);
--}
--
--bool raw_fd_ostream::has_colors() const {
--  if (!HasColors)
--    HasColors = sys::Process::FileDescriptorHasColors(FD);
--  return *HasColors;
--}
--
--Expected<sys::fs::FileLocker> raw_fd_ostream::lock() {
--  std::error_code EC = sys::fs::lockFile(FD);
--  if (!EC)
--    return sys::fs::FileLocker(FD);
--  return errorCodeToError(EC);
--}
--
--Expected<sys::fs::FileLocker>
--raw_fd_ostream::tryLockFor(Duration const& Timeout) {
--  std::error_code EC = sys::fs::tryLockFile(FD, Timeout.getDuration());
--  if (!EC)
--    return sys::fs::FileLocker(FD);
--  return errorCodeToError(EC);
--}
--
- void raw_fd_ostream::anchor() {}
- 
- //===----------------------------------------------------------------------===//
-@@ -921,16 +631,6 @@ raw_fd_stream::raw_fd_stream(std::string_view Filename, std::error_code &EC)
-     EC = std::make_error_code(std::errc::invalid_argument);
- }
- 
--ssize_t raw_fd_stream::read(char *Ptr, size_t Size) {
--  assert(get_fd() >= 0 && "File already closed.");
--  ssize_t Ret = ::read(get_fd(), (void *)Ptr, Size);
--  if (Ret >= 0)
--    inc_pos(Ret);
--  else
--    error_detected(std::error_code(errno, std::generic_category()));
--  return Ret;
--}
--
- bool raw_fd_stream::classof(const raw_ostream *OS) {
-   return OS->get_kind() == OStreamKind::OK_FDStream;
- }
-@@ -986,31 +686,3 @@ void raw_pwrite_stream::anchor() {}
- void buffer_ostream::anchor() {}
- 
- void buffer_unique_ostream::anchor() {}
--
--Error llvm::writeToOutput(std::string_view OutputFileName,
--                          std::function<Error(raw_ostream &)> Write) {
--  if (OutputFileName == "-")
--    return Write(outs());
--
--  if (OutputFileName == "/dev/null") {
--    raw_null_ostream Out;
--    return Write(Out);
--  }
--
--  unsigned Mode = sys::fs::all_read | sys::fs::all_write | sys::fs::all_exe;
--  Expected<sys::fs::TempFile> Temp =
--      sys::fs::TempFile::create(OutputFileName + ".temp-stream-%%%%%%", Mode);
--  if (!Temp)
--    return createFileError(OutputFileName, Temp.takeError());
--
--  raw_fd_ostream Out(Temp->FD, false);
--
--  if (Error E = Write(Out)) {
--    if (Error DiscardError = Temp->discard())
--      return joinErrors(std::move(E), std::move(DiscardError));
--    return E;
--  }
--  Out.flush();
--
--  return Temp->keep(OutputFileName);
--}
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0011-Add-vectors-to-raw_ostream.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0011-Add-vectors-to-raw_ostream.patch
new file mode 100644
index 0000000..cba909f
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0011-Add-vectors-to-raw_ostream.patch
@@ -0,0 +1,214 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sun, 8 May 2022 13:48:59 -0400
+Subject: [PATCH 11/31] Add vectors to raw_ostream
+
+---
+ llvm/include/llvm/Support/raw_ostream.h | 115 ++++++++++++++++++++++++
+ llvm/lib/Support/raw_ostream.cpp        |  47 ++++++++++
+ 2 files changed, 162 insertions(+)
+
+diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
+index 95019180a9deb406ed4f2991c664a4cc4e956dac..e7526e016a858ad728feb7cf1c5014b9691759d4 100644
+--- a/llvm/include/llvm/Support/raw_ostream.h
++++ b/llvm/include/llvm/Support/raw_ostream.h
+@@ -24,6 +24,7 @@
+ #include <string_view>
+ #include <system_error>
+ #include <type_traits>
++#include <vector>
+ 
+ namespace llvm {
+ 
+@@ -261,12 +262,24 @@ public:
+     return write(Str.data(), Str.size());
+   }
+ 
++  raw_ostream &operator<<(const std::vector<uint8_t> &Arr) {
++    // Avoid the fast path, it would only increase code size for a marginal win.
++    return write(Arr.data(), Arr.size());
++  }
++
++  raw_ostream &operator<<(const SmallVectorImpl<uint8_t> &Arr) {
++    return write(Arr.data(), Arr.size());
++  }
++
+   /// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't
+   /// satisfy llvm::isPrint into an escape sequence.
+   raw_ostream &write_escaped(std::string_view Str, bool UseHexEscapes = false);
+ 
+   raw_ostream &write(unsigned char C);
+   raw_ostream &write(const char *Ptr, size_t Size);
++  raw_ostream &write(const uint8_t *Ptr, size_t Size) {
++    return write(reinterpret_cast<const char *>(Ptr), Size);
++  }
+ 
+   /// indent - Insert 'NumSpaces' spaces.
+   raw_ostream &indent(unsigned NumSpaces);
+@@ -617,6 +630,108 @@ public:
+   }
+ };
+ 
++/// A raw_ostream that writes to a vector.  This is a
++/// simple adaptor class. This class does not encounter output errors.
++/// raw_vector_ostream operates without a buffer, delegating all memory
++/// management to the vector. Thus the vector is always up-to-date,
++/// may be used directly and there is no need to call flush().
++class raw_vector_ostream : public raw_pwrite_stream {
++  std::vector<char> &OS;
++
++  /// See raw_ostream::write_impl.
++  void write_impl(const char *Ptr, size_t Size) override;
++
++  void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override;
++
++  /// Return the current position within the stream.
++  uint64_t current_pos() const override;
++
++public:
++  /// Construct a new raw_svector_ostream.
++  ///
++  /// \param O The vector to write to; this should generally have at least 128
++  /// bytes free to avoid any extraneous memory overhead.
++  explicit raw_vector_ostream(std::vector<char> &O) : OS(O) {
++    SetUnbuffered();
++  }
++
++  ~raw_vector_ostream() override = default;
++
++  void flush() = delete;
++
++  /// Return a std::string_view for the vector contents.
++  std::string_view str() { return std::string_view(OS.data(), OS.size()); }
++};
++
++/// A raw_ostream that writes to an SmallVector or SmallString.  This is a
++/// simple adaptor class. This class does not encounter output errors.
++/// raw_svector_ostream operates without a buffer, delegating all memory
++/// management to the SmallString. Thus the SmallString is always up-to-date,
++/// may be used directly and there is no need to call flush().
++class raw_usvector_ostream : public raw_pwrite_stream {
++  SmallVectorImpl<uint8_t> &OS;
++
++  /// See raw_ostream::write_impl.
++  void write_impl(const char *Ptr, size_t Size) override;
++
++  void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override;
++
++  /// Return the current position within the stream.
++  uint64_t current_pos() const override;
++
++public:
++  /// Construct a new raw_svector_ostream.
++  ///
++  /// \param O The vector to write to; this should generally have at least 128
++  /// bytes free to avoid any extraneous memory overhead.
++  explicit raw_usvector_ostream(SmallVectorImpl<uint8_t> &O) : OS(O) {
++    SetUnbuffered();
++  }
++
++  ~raw_usvector_ostream() override = default;
++
++  void flush() = delete;
++
++  /// Return an span for the vector contents.
++  span<uint8_t> array() { return {OS.data(), OS.size()}; }
++  span<const uint8_t> array() const { return {OS.data(), OS.size()}; }
++};
++
++/// A raw_ostream that writes to a vector.  This is a
++/// simple adaptor class. This class does not encounter output errors.
++/// raw_vector_ostream operates without a buffer, delegating all memory
++/// management to the vector. Thus the vector is always up-to-date,
++/// may be used directly and there is no need to call flush().
++class raw_uvector_ostream : public raw_pwrite_stream {
++  std::vector<uint8_t> &OS;
++
++  /// See raw_ostream::write_impl.
++  void write_impl(const char *Ptr, size_t Size) override;
++
++  void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override;
++
++  /// Return the current position within the stream.
++  uint64_t current_pos() const override;
++
++public:
++  /// Construct a new raw_svector_ostream.
++  ///
++  /// \param O The vector to write to; this should generally have at least 128
++  /// bytes free to avoid any extraneous memory overhead.
++  explicit raw_uvector_ostream(std::vector<uint8_t> &O) : OS(O) {
++    SetUnbuffered();
++  }
++
++  ~raw_uvector_ostream() override = default;
++
++  void flush() = delete;
++
++  /// Return a span for the vector contents.
++  span<uint8_t> array() { return {OS.data(), OS.size()}; }
++  span<const uint8_t> array() const { return {OS.data(), OS.size()}; }
++};
++
++
+ /// A raw_ostream that discards all output.
+ class raw_null_ostream : public raw_pwrite_stream {
+   /// See raw_ostream::write_impl.
+diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
+index 76c32155b4296fbbf3f4b164cd58d63f472ccd5e..dcdfdfd7a8e3fcc4019538a4fc9e158aeda0a8b8 100644
+--- a/llvm/lib/Support/raw_ostream.cpp
++++ b/llvm/lib/Support/raw_ostream.cpp
+@@ -670,6 +670,53 @@ void raw_svector_ostream::pwrite_impl(const char *Ptr, size_t Size,
+   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
+ //===----------------------------------------------------------------------===//
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0011-Detemplatize-SmallVectorBase.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0011-Detemplatize-SmallVectorBase.patch
deleted file mode 100644
index 320e500..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0011-Detemplatize-SmallVectorBase.patch
+++ /dev/null
@@ -1,140 +0,0 @@
-From ef26f059859d3a0d08b06a70519928bcd5f9bb79 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Thu, 5 May 2022 23:18:34 -0400
-Subject: [PATCH 11/28] Detemplatize SmallVectorBase
-
----
- llvm/include/llvm/ADT/SmallVector.h | 21 +++++++-----------
- llvm/lib/Support/SmallVector.cpp    | 34 +++++------------------------
- 2 files changed, 13 insertions(+), 42 deletions(-)
-
-diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h
-index 1e311ea56..4b6bbdeb2 100644
---- a/llvm/include/llvm/ADT/SmallVector.h
-+++ b/llvm/include/llvm/ADT/SmallVector.h
-@@ -50,14 +50,14 @@ template <typename IteratorT> class iterator_range;
- /// Using 64 bit size is desirable for cases like SmallVector<char>, where a
- /// 32 bit size would limit the vector to ~4GB. SmallVectors are used for
- /// buffering bitcode output - which can exceed 4GB.
--template <class Size_T> class SmallVectorBase {
-+class SmallVectorBase {
- protected:
-   void *BeginX;
--  Size_T Size = 0, Capacity;
-+  unsigned Size = 0, Capacity;
- 
-   /// The maximum value of the Size_T used.
-   static constexpr size_t SizeTypeMax() {
--    return (std::numeric_limits<Size_T>::max)();
-+    return (std::numeric_limits<unsigned>::max)();
-   }
- 
-   SmallVectorBase() = delete;
-@@ -91,15 +91,10 @@ protected:
-   }
- };
- 
--template <class T>
--using SmallVectorSizeType =
--    typename std::conditional<sizeof(T) < 4 && sizeof(void *) >= 8, uint64_t,
--                              uint32_t>::type;
--
- /// Figure out the offset of the first element.
- template <class T, typename = void> struct SmallVectorAlignmentAndSize {
--  alignas(SmallVectorBase<SmallVectorSizeType<T>>) char Base[sizeof(
--      SmallVectorBase<SmallVectorSizeType<T>>)];
-+  alignas(SmallVectorBase) char Base[sizeof(
-+      SmallVectorBase)];
-   alignas(T) char FirstEl[sizeof(T)];
- };
- 
-@@ -108,8 +103,8 @@ template <class T, typename = void> struct SmallVectorAlignmentAndSize {
- /// to avoid unnecessarily requiring T to be complete.
- template <typename T, typename = void>
- class SmallVectorTemplateCommon
--    : public SmallVectorBase<SmallVectorSizeType<T>> {
--  using Base = SmallVectorBase<SmallVectorSizeType<T>>;
-+    : public SmallVectorBase {
-+  using Base = SmallVectorBase;
- 
-   /// Find the address of the first element.  For this pointer math to be valid
-   /// with small-size of 0 for T with lots of alignment, it's important that
-@@ -356,7 +351,7 @@ protected:
-   /// in \p NewCapacity. This is the first section of \a grow().
-   T *mallocForGrow(size_t MinSize, size_t &NewCapacity) {
-     return static_cast<T *>(
--        SmallVectorBase<SmallVectorSizeType<T>>::mallocForGrow(
-+        SmallVectorBase::mallocForGrow(
-             MinSize, sizeof(T), NewCapacity));
-   }
- 
-diff --git a/llvm/lib/Support/SmallVector.cpp b/llvm/lib/Support/SmallVector.cpp
-index a2b4899e1..bdfc963d7 100644
---- a/llvm/lib/Support/SmallVector.cpp
-+++ b/llvm/lib/Support/SmallVector.cpp
-@@ -51,10 +51,6 @@ static_assert(sizeof(SmallVector<void *, 1>) ==
-                   sizeof(unsigned) * 2 + sizeof(void *) * 2,
-               "wasted space in SmallVector size 1");
- 
--static_assert(sizeof(SmallVector<char, 0>) ==
--                  sizeof(void *) * 2 + sizeof(void *),
--              "1 byte elements have word-sized type for size and capacity");
--
- /// Report that MinSize doesn't fit into this vector's size type. Throws
- /// std::length_error or calls report_fatal_error.
- [[noreturn]] static void report_size_overflow(size_t MinSize, size_t MaxSize);
-@@ -85,9 +81,8 @@ static void report_at_maximum_capacity(size_t MaxSize) {
- }
- 
- // Note: Moving this function into the header may cause performance regression.
--template <class Size_T>
- static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) {
--  constexpr size_t MaxSize = std::numeric_limits<Size_T>::max();
-+  constexpr size_t MaxSize = std::numeric_limits<unsigned>::max();
- 
-   // Ensure we can fit the new capacity.
-   // This is only going to be applicable when the capacity is 32 bit.
-@@ -108,18 +103,16 @@ static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) {
- }
- 
- // Note: Moving this function into the header may cause performance regression.
--template <class Size_T>
--void *SmallVectorBase<Size_T>::mallocForGrow(size_t MinSize, size_t TSize,
-+void *SmallVectorBase::mallocForGrow(size_t MinSize, size_t TSize,
-                                              size_t &NewCapacity) {
--  NewCapacity = getNewCapacity<Size_T>(MinSize, TSize, this->capacity());
-+  NewCapacity = getNewCapacity(MinSize, TSize, this->capacity());
-   return llvm::safe_malloc(NewCapacity * TSize);
- }
- 
- // Note: Moving this function into the header may cause performance regression.
--template <class Size_T>
--void SmallVectorBase<Size_T>::grow_pod(void *FirstEl, size_t MinSize,
-+void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSize,
-                                        size_t TSize) {
--  size_t NewCapacity = getNewCapacity<Size_T>(MinSize, TSize, this->capacity());
-+  size_t NewCapacity = getNewCapacity(MinSize, TSize, this->capacity());
-   void *NewElts;
-   if (BeginX == FirstEl) {
-     NewElts = safe_malloc(NewCapacity * TSize);
-@@ -134,20 +127,3 @@ void SmallVectorBase<Size_T>::grow_pod(void *FirstEl, size_t MinSize,
-   this->BeginX = NewElts;
-   this->Capacity = NewCapacity;
- }
--
--template class llvm::SmallVectorBase<uint32_t>;
--
--// Disable the uint64_t instantiation for 32-bit builds.
--// Both uint32_t and uint64_t instantiations are needed for 64-bit builds.
--// This instantiation will never be used in 32-bit builds, and will cause
--// warnings when sizeof(Size_T) > sizeof(size_t).
--#if SIZE_MAX > UINT32_MAX
--template class llvm::SmallVectorBase<uint64_t>;
--
--// Assertions to ensure this #if stays in sync with SmallVectorSizeType.
--static_assert(sizeof(SmallVectorSizeType<char>) == sizeof(uint64_t),
--              "Expected SmallVectorBase<uint64_t> variant to be in use.");
--#else
--static_assert(sizeof(SmallVectorSizeType<char>) == sizeof(uint32_t),
--              "Expected SmallVectorBase<uint32_t> variant to be in use.");
--#endif
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0012-Add-vectors-to-raw_ostream.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0012-Add-vectors-to-raw_ostream.patch
deleted file mode 100644
index cc0bd5d..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0012-Add-vectors-to-raw_ostream.patch
+++ /dev/null
@@ -1,214 +0,0 @@
-From 342ebd445bb3dae8b0c119a6126a318ad378a377 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sun, 8 May 2022 13:48:59 -0400
-Subject: [PATCH 12/28] Add vectors to raw_ostream
-
----
- llvm/include/llvm/Support/raw_ostream.h | 115 ++++++++++++++++++++++++
- llvm/lib/Support/raw_ostream.cpp        |  47 ++++++++++
- 2 files changed, 162 insertions(+)
-
-diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
-index d4521c8e2..bf5630ab5 100644
---- a/llvm/include/llvm/Support/raw_ostream.h
-+++ b/llvm/include/llvm/Support/raw_ostream.h
-@@ -25,6 +25,7 @@
- #endif
- #include <system_error>
- #include <type_traits>
-+#include <vector>
- 
- namespace llvm {
- 
-@@ -248,12 +249,24 @@ public:
-     return write(Str.data(), Str.size());
-   }
- 
-+  raw_ostream &operator<<(const std::vector<uint8_t> &Arr) {
-+    // Avoid the fast path, it would only increase code size for a marginal win.
-+    return write(Arr.data(), Arr.size());
-+  }
-+
-+  raw_ostream &operator<<(const SmallVectorImpl<uint8_t> &Arr) {
-+    return write(Arr.data(), Arr.size());
-+  }
-+
-   /// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't
-   /// satisfy llvm::isPrint into an escape sequence.
-   raw_ostream &write_escaped(std::string_view Str, bool UseHexEscapes = false);
- 
-   raw_ostream &write(unsigned char C);
-   raw_ostream &write(const char *Ptr, size_t Size);
-+  raw_ostream &write(const uint8_t *Ptr, size_t Size) {
-+    return write(reinterpret_cast<const char *>(Ptr), Size);
-+  }
- 
-   /// indent - Insert 'NumSpaces' spaces.
-   raw_ostream &indent(unsigned NumSpaces);
-@@ -604,6 +617,108 @@ public:
-   }
- };
- 
-+/// A raw_ostream that writes to a vector.  This is a
-+/// simple adaptor class. This class does not encounter output errors.
-+/// raw_vector_ostream operates without a buffer, delegating all memory
-+/// management to the vector. Thus the vector is always up-to-date,
-+/// may be used directly and there is no need to call flush().
-+class raw_vector_ostream : public raw_pwrite_stream {
-+  std::vector<char> &OS;
-+
-+  /// See raw_ostream::write_impl.
-+  void write_impl(const char *Ptr, size_t Size) override;
-+
-+  void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override;
-+
-+  /// Return the current position within the stream.
-+  uint64_t current_pos() const override;
-+
-+public:
-+  /// Construct a new raw_svector_ostream.
-+  ///
-+  /// \param O The vector to write to; this should generally have at least 128
-+  /// bytes free to avoid any extraneous memory overhead.
-+  explicit raw_vector_ostream(std::vector<char> &O) : OS(O) {
-+    SetUnbuffered();
-+  }
-+
-+  ~raw_vector_ostream() override = default;
-+
-+  void flush() = delete;
-+
-+  /// Return a std::string_view for the vector contents.
-+  std::string_view str() { return std::string_view(OS.data(), OS.size()); }
-+};
-+
-+/// A raw_ostream that writes to an SmallVector or SmallString.  This is a
-+/// simple adaptor class. This class does not encounter output errors.
-+/// raw_svector_ostream operates without a buffer, delegating all memory
-+/// management to the SmallString. Thus the SmallString is always up-to-date,
-+/// may be used directly and there is no need to call flush().
-+class raw_usvector_ostream : public raw_pwrite_stream {
-+  SmallVectorImpl<uint8_t> &OS;
-+
-+  /// See raw_ostream::write_impl.
-+  void write_impl(const char *Ptr, size_t Size) override;
-+
-+  void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override;
-+
-+  /// Return the current position within the stream.
-+  uint64_t current_pos() const override;
-+
-+public:
-+  /// Construct a new raw_svector_ostream.
-+  ///
-+  /// \param O The vector to write to; this should generally have at least 128
-+  /// bytes free to avoid any extraneous memory overhead.
-+  explicit raw_usvector_ostream(SmallVectorImpl<uint8_t> &O) : OS(O) {
-+    SetUnbuffered();
-+  }
-+
-+  ~raw_usvector_ostream() override = default;
-+
-+  void flush() = delete;
-+
-+  /// Return an span for the vector contents.
-+  span<uint8_t> array() { return {OS.data(), OS.size()}; }
-+  span<const uint8_t> array() const { return {OS.data(), OS.size()}; }
-+};
-+
-+/// A raw_ostream that writes to a vector.  This is a
-+/// simple adaptor class. This class does not encounter output errors.
-+/// raw_vector_ostream operates without a buffer, delegating all memory
-+/// management to the vector. Thus the vector is always up-to-date,
-+/// may be used directly and there is no need to call flush().
-+class raw_uvector_ostream : public raw_pwrite_stream {
-+  std::vector<uint8_t> &OS;
-+
-+  /// See raw_ostream::write_impl.
-+  void write_impl(const char *Ptr, size_t Size) override;
-+
-+  void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override;
-+
-+  /// Return the current position within the stream.
-+  uint64_t current_pos() const override;
-+
-+public:
-+  /// Construct a new raw_svector_ostream.
-+  ///
-+  /// \param O The vector to write to; this should generally have at least 128
-+  /// bytes free to avoid any extraneous memory overhead.
-+  explicit raw_uvector_ostream(std::vector<uint8_t> &O) : OS(O) {
-+    SetUnbuffered();
-+  }
-+
-+  ~raw_uvector_ostream() override = default;
-+
-+  void flush() = delete;
-+
-+  /// Return a span for the vector contents.
-+  span<uint8_t> array() { return {OS.data(), OS.size()}; }
-+  span<const uint8_t> array() const { return {OS.data(), OS.size()}; }
-+};
-+
-+
- /// A raw_ostream that discards all output.
- class raw_null_ostream : public raw_pwrite_stream {
-   /// See raw_ostream::write_impl.
-diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
-index 875eda7ba..36faf744c 100644
---- a/llvm/lib/Support/raw_ostream.cpp
-+++ b/llvm/lib/Support/raw_ostream.cpp
-@@ -658,6 +658,53 @@ void raw_svector_ostream::pwrite_impl(const char *Ptr, size_t Size,
-   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
- //===----------------------------------------------------------------------===//
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0012-Extra-collections-features.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0012-Extra-collections-features.patch
new file mode 100644
index 0000000..7da2c10
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0012-Extra-collections-features.patch
@@ -0,0 +1,162 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Tue, 3 May 2022 22:16:10 -0400
+Subject: [PATCH 12/31] Extra collections features
+
+---
+ llvm/include/llvm/ADT/StringMap.h | 103 +++++++++++++++++++++++++++++-
+ llvm/lib/Support/raw_ostream.cpp  |   8 +++
+ 2 files changed, 110 insertions(+), 1 deletion(-)
+
+diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h
+index 34dfbf83c681f4e81a9dadd9382ddca6ef8d6c1d..c133e84f9b2e3a225cdac782c011fadbf07adab2 100644
+--- a/llvm/include/llvm/ADT/StringMap.h
++++ b/llvm/include/llvm/ADT/StringMap.h
+@@ -42,7 +42,7 @@ protected:
+ 
+ protected:
+   explicit StringMapImpl(unsigned itemSize) : ItemSize(itemSize) {}
+-  StringMapImpl(StringMapImpl &&RHS)
++  StringMapImpl(StringMapImpl &&RHS) noexcept
+       : TheTable(RHS.TheTable), NumBuckets(RHS.NumBuckets),
+         NumItems(RHS.NumItems), NumTombstones(RHS.NumTombstones),
+         ItemSize(RHS.ItemSize) {
+@@ -432,11 +432,27 @@ public:
+     return Tmp;
+   }
+ 
++  DerivedTy &operator--() { // Predecrement
++    --Ptr;
++    ReversePastEmptyBuckets();
++    return static_cast<DerivedTy &>(*this);
++  }
++
++  DerivedTy operator--(int) { // Post-decrement
++    DerivedTy Tmp(Ptr);
++    --*this;
++    return Tmp;
++  }
++
+ private:
+   void AdvancePastEmptyBuckets() {
+     while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
+       ++Ptr;
+   }
++  void ReversePastEmptyBuckets() {
++    while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
++      --Ptr;
++  }
+ };
+ 
+ template <typename ValueTy>
+@@ -495,6 +511,91 @@ public:
+   std::string_view operator*() const { return this->wrapped()->getKey(); }
+ };
+ 
++template <typename ValueTy>
++bool operator==(const StringMap<ValueTy>& lhs, const StringMap<ValueTy>& rhs) {
++  // same instance?
++  if (&lhs == &rhs) return true;
++
++  // first check that sizes are identical
++  if (lhs.size() != rhs.size()) return false;
++
++  // copy into vectors and sort by key
++  SmallVector<StringMapConstIterator<ValueTy>, 16> lhs_items;
++  lhs_items.reserve(lhs.size());
++  for (auto i = lhs.begin(), end = lhs.end(); i != end; ++i)
++    lhs_items.push_back(i);
++  std::sort(lhs_items.begin(), lhs_items.end(),
++            [](const StringMapConstIterator<ValueTy>& a,
++               const StringMapConstIterator<ValueTy>& b) {
++              return a->getKey() < b->getKey();
++            });
++
++  SmallVector<StringMapConstIterator<ValueTy>, 16> rhs_items;
++  rhs_items.reserve(rhs.size());
++  for (auto i = rhs.begin(), end = rhs.end(); i != end; ++i)
++    rhs_items.push_back(i);
++  std::sort(rhs_items.begin(), rhs_items.end(),
++            [](const StringMapConstIterator<ValueTy>& a,
++               const StringMapConstIterator<ValueTy>& b) {
++              return a->getKey() < b->getKey();
++            });
++
++  // compare vector keys and values
++  for (auto a = lhs_items.begin(), b = rhs_items.begin(),
++            aend = lhs_items.end(), bend = rhs_items.end();
++       a != aend && b != bend; ++a, ++b) {
++    if ((*a)->first() != (*b)->first() || (*a)->second != (*b)->second)
++      return false;
++  }
++  return true;
++}
++
++template <typename ValueTy>
++inline bool operator!=(const StringMap<ValueTy>& lhs,
++                       const StringMap<ValueTy>& rhs) {
++  return !(lhs == rhs);
++}
++
++template <typename ValueTy>
++bool operator<(const StringMap<ValueTy>& lhs, const StringMap<ValueTy>& rhs) {
++  // same instance?
++  if (&lhs == &rhs) return false;
++
++  // copy into vectors and sort by key
++  SmallVector<std::string_view, 16> lhs_keys;
++  lhs_keys.reserve(lhs.size());
++  for (auto i = lhs.begin(), end = lhs.end(); i != end; ++i)
++    lhs_keys.push_back(i->getKey());
++  std::sort(lhs_keys.begin(), lhs_keys.end());
++
++  SmallVector<std::string_view, 16> rhs_keys;
++  rhs_keys.reserve(rhs.size());
++  for (auto i = rhs.begin(), end = rhs.end(); i != end; ++i)
++    rhs_keys.push_back(i->getKey());
++  std::sort(rhs_keys.begin(), rhs_keys.end());
++
++  // use std::vector comparison
++  return lhs_keys < rhs_keys;
++}
++
++template <typename ValueTy>
++inline bool operator<=(const StringMap<ValueTy>& lhs,
++                       const StringMap<ValueTy>& rhs) {
++  return !(rhs < lhs);
++}
++
++template <typename ValueTy>
++inline bool operator>(const StringMap<ValueTy>& lhs,
++                      const StringMap<ValueTy>& rhs) {
++  return !(lhs <= rhs);
++}
++
++template <typename ValueTy>
++inline bool operator>=(const StringMap<ValueTy>& lhs,
++                       const StringMap<ValueTy>& rhs) {
++  return !(lhs < rhs);
++}
++
+ } // end namespace llvm
+ 
+ #endif // LLVM_ADT_STRINGMAP_H
+diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
+index dcdfdfd7a8e3fcc4019538a4fc9e158aeda0a8b8..b2a726633b48b179abfd24a5de110a2301e0f877 100644
+--- a/llvm/lib/Support/raw_ostream.cpp
++++ b/llvm/lib/Support/raw_ostream.cpp
+@@ -76,6 +76,14 @@ constexpr raw_ostream::Colors raw_ostream::WHITE;
+ constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR;
+ constexpr raw_ostream::Colors raw_ostream::RESET;
+ 
++namespace {
++// Find the length of an array.
++template <class T, std::size_t N>
++constexpr inline size_t array_lengthof(T (&)[N]) {
++  return N;
++}
++}  // namespace
++
+ raw_ostream::~raw_ostream() {
+   // raw_ostream's subclasses should take care to flush the buffer
+   // in their destructors.
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0013-EpochTracker-ABI-macro.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0013-EpochTracker-ABI-macro.patch
new file mode 100644
index 0000000..a280def
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0013-EpochTracker-ABI-macro.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Wed, 4 May 2022 00:01:00 -0400
+Subject: [PATCH 13/31] EpochTracker ABI macro
+
+---
+ llvm/include/llvm/ADT/EpochTracker.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/llvm/include/llvm/ADT/EpochTracker.h b/llvm/include/llvm/ADT/EpochTracker.h
+index fc41d6f2c92d2f9876c741067b5645a74663db04..56d0acda2c1a0e390cfed086fa298b650c4a561f 100644
+--- a/llvm/include/llvm/ADT/EpochTracker.h
++++ b/llvm/include/llvm/ADT/EpochTracker.h
+@@ -22,7 +22,7 @@
+ 
+ namespace llvm {
+ 
+-#if LLVM_ENABLE_ABI_BREAKING_CHECKS
++#ifndef NDEBUG //ifndef LLVM_ENABLE_ABI_BREAKING_CHECKS
+ #define LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE
+ 
+ /// A base class for data structure classes wishing to make iterators
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0013-Extra-collections-features.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0013-Extra-collections-features.patch
deleted file mode 100644
index 19aef1a..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0013-Extra-collections-features.patch
+++ /dev/null
@@ -1,162 +0,0 @@
-From 5db7e9f9370a5e04949846cc68eeb13e2c1db187 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Tue, 3 May 2022 22:16:10 -0400
-Subject: [PATCH 13/28] Extra collections features
-
----
- llvm/include/llvm/ADT/StringMap.h | 103 +++++++++++++++++++++++++++++-
- llvm/lib/Support/raw_ostream.cpp  |   8 +++
- 2 files changed, 110 insertions(+), 1 deletion(-)
-
-diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h
-index 8747cdb35..16f13f048 100644
---- a/llvm/include/llvm/ADT/StringMap.h
-+++ b/llvm/include/llvm/ADT/StringMap.h
-@@ -42,7 +42,7 @@ protected:
- 
- protected:
-   explicit StringMapImpl(unsigned itemSize) : ItemSize(itemSize) {}
--  StringMapImpl(StringMapImpl &&RHS)
-+  StringMapImpl(StringMapImpl &&RHS) noexcept
-       : TheTable(RHS.TheTable), NumBuckets(RHS.NumBuckets),
-         NumItems(RHS.NumItems), NumTombstones(RHS.NumTombstones),
-         ItemSize(RHS.ItemSize) {
-@@ -420,11 +420,27 @@ public:
-     return Tmp;
-   }
- 
-+  DerivedTy &operator--() { // Predecrement
-+    --Ptr;
-+    ReversePastEmptyBuckets();
-+    return static_cast<DerivedTy &>(*this);
-+  }
-+
-+  DerivedTy operator--(int) { // Post-decrement
-+    DerivedTy Tmp(Ptr);
-+    --*this;
-+    return Tmp;
-+  }
-+
- private:
-   void AdvancePastEmptyBuckets() {
-     while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
-       ++Ptr;
-   }
-+  void ReversePastEmptyBuckets() {
-+    while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
-+      --Ptr;
-+  }
- };
- 
- template <typename ValueTy>
-@@ -483,6 +499,91 @@ public:
-   std::string_view operator*() const { return this->wrapped()->getKey(); }
- };
- 
-+template <typename ValueTy>
-+bool operator==(const StringMap<ValueTy>& lhs, const StringMap<ValueTy>& rhs) {
-+  // same instance?
-+  if (&lhs == &rhs) return true;
-+
-+  // first check that sizes are identical
-+  if (lhs.size() != rhs.size()) return false;
-+
-+  // copy into vectors and sort by key
-+  SmallVector<StringMapConstIterator<ValueTy>, 16> lhs_items;
-+  lhs_items.reserve(lhs.size());
-+  for (auto i = lhs.begin(), end = lhs.end(); i != end; ++i)
-+    lhs_items.push_back(i);
-+  std::sort(lhs_items.begin(), lhs_items.end(),
-+            [](const StringMapConstIterator<ValueTy>& a,
-+               const StringMapConstIterator<ValueTy>& b) {
-+              return a->getKey() < b->getKey();
-+            });
-+
-+  SmallVector<StringMapConstIterator<ValueTy>, 16> rhs_items;
-+  rhs_items.reserve(rhs.size());
-+  for (auto i = rhs.begin(), end = rhs.end(); i != end; ++i)
-+    rhs_items.push_back(i);
-+  std::sort(rhs_items.begin(), rhs_items.end(),
-+            [](const StringMapConstIterator<ValueTy>& a,
-+               const StringMapConstIterator<ValueTy>& b) {
-+              return a->getKey() < b->getKey();
-+            });
-+
-+  // compare vector keys and values
-+  for (auto a = lhs_items.begin(), b = rhs_items.begin(),
-+            aend = lhs_items.end(), bend = rhs_items.end();
-+       a != aend && b != bend; ++a, ++b) {
-+    if ((*a)->first() != (*b)->first() || (*a)->second != (*b)->second)
-+      return false;
-+  }
-+  return true;
-+}
-+
-+template <typename ValueTy>
-+inline bool operator!=(const StringMap<ValueTy>& lhs,
-+                       const StringMap<ValueTy>& rhs) {
-+  return !(lhs == rhs);
-+}
-+
-+template <typename ValueTy>
-+bool operator<(const StringMap<ValueTy>& lhs, const StringMap<ValueTy>& rhs) {
-+  // same instance?
-+  if (&lhs == &rhs) return false;
-+
-+  // copy into vectors and sort by key
-+  SmallVector<std::string_view, 16> lhs_keys;
-+  lhs_keys.reserve(lhs.size());
-+  for (auto i = lhs.begin(), end = lhs.end(); i != end; ++i)
-+    lhs_keys.push_back(i->getKey());
-+  std::sort(lhs_keys.begin(), lhs_keys.end());
-+
-+  SmallVector<std::string_view, 16> rhs_keys;
-+  rhs_keys.reserve(rhs.size());
-+  for (auto i = rhs.begin(), end = rhs.end(); i != end; ++i)
-+    rhs_keys.push_back(i->getKey());
-+  std::sort(rhs_keys.begin(), rhs_keys.end());
-+
-+  // use std::vector comparison
-+  return lhs_keys < rhs_keys;
-+}
-+
-+template <typename ValueTy>
-+inline bool operator<=(const StringMap<ValueTy>& lhs,
-+                       const StringMap<ValueTy>& rhs) {
-+  return !(rhs < lhs);
-+}
-+
-+template <typename ValueTy>
-+inline bool operator>(const StringMap<ValueTy>& lhs,
-+                      const StringMap<ValueTy>& rhs) {
-+  return !(lhs <= rhs);
-+}
-+
-+template <typename ValueTy>
-+inline bool operator>=(const StringMap<ValueTy>& lhs,
-+                       const StringMap<ValueTy>& rhs) {
-+  return !(lhs < rhs);
-+}
-+
- } // end namespace llvm
- 
- #endif // LLVM_ADT_STRINGMAP_H
-diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
-index 36faf744c..95152849c 100644
---- a/llvm/lib/Support/raw_ostream.cpp
-+++ b/llvm/lib/Support/raw_ostream.cpp
-@@ -76,6 +76,14 @@ constexpr raw_ostream::Colors raw_ostream::WHITE;
- constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR;
- constexpr raw_ostream::Colors raw_ostream::RESET;
- 
-+namespace {
-+// Find the length of an array.
-+template <class T, std::size_t N>
-+constexpr inline size_t array_lengthof(T (&)[N]) {
-+  return N;
-+}
-+}  // namespace
-+
- raw_ostream::~raw_ostream() {
-   // raw_ostream's subclasses should take care to flush the buffer
-   // in their destructors.
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0014-Delete-numbers-from-MathExtras.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0014-Delete-numbers-from-MathExtras.patch
new file mode 100644
index 0000000..ea27672
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0014-Delete-numbers-from-MathExtras.patch
@@ -0,0 +1,56 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Thu, 5 May 2022 18:09:45 -0400
+Subject: [PATCH 14/31] Delete numbers from MathExtras
+
+---
+ llvm/include/llvm/Support/MathExtras.h | 36 --------------------------
+ 1 file changed, 36 deletions(-)
+
+diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
+index cd5a64a746b2eb7491e9b6cf8570bdf436d94a6d..cdf859ccfaca22a04b08a351d7c2c9789a70627e 100644
+--- a/llvm/include/llvm/Support/MathExtras.h
++++ b/llvm/include/llvm/Support/MathExtras.h
+@@ -24,42 +24,6 @@
+ 
+ namespace llvm {
+ 
+-/// Mathematical constants.
+-namespace numbers {
+-// TODO: Track C++20 std::numbers.
+-// TODO: Favor using the hexadecimal FP constants (requires C++17).
+-constexpr double e          = 2.7182818284590452354, // (0x1.5bf0a8b145749P+1) https://oeis.org/A001113
+-                 egamma     = .57721566490153286061, // (0x1.2788cfc6fb619P-1) https://oeis.org/A001620
+-                 ln2        = .69314718055994530942, // (0x1.62e42fefa39efP-1) https://oeis.org/A002162
+-                 ln10       = 2.3025850929940456840, // (0x1.24bb1bbb55516P+1) https://oeis.org/A002392
+-                 log2e      = 1.4426950408889634074, // (0x1.71547652b82feP+0)
+-                 log10e     = .43429448190325182765, // (0x1.bcb7b1526e50eP-2)
+-                 pi         = 3.1415926535897932385, // (0x1.921fb54442d18P+1) https://oeis.org/A000796
+-                 inv_pi     = .31830988618379067154, // (0x1.45f306bc9c883P-2) https://oeis.org/A049541
+-                 sqrtpi     = 1.7724538509055160273, // (0x1.c5bf891b4ef6bP+0) https://oeis.org/A002161
+-                 inv_sqrtpi = .56418958354775628695, // (0x1.20dd750429b6dP-1) https://oeis.org/A087197
+-                 sqrt2      = 1.4142135623730950488, // (0x1.6a09e667f3bcdP+0) https://oeis.org/A00219
+-                 inv_sqrt2  = .70710678118654752440, // (0x1.6a09e667f3bcdP-1)
+-                 sqrt3      = 1.7320508075688772935, // (0x1.bb67ae8584caaP+0) https://oeis.org/A002194
+-                 inv_sqrt3  = .57735026918962576451, // (0x1.279a74590331cP-1)
+-                 phi        = 1.6180339887498948482; // (0x1.9e3779b97f4a8P+0) https://oeis.org/A001622
+-constexpr float ef          = 2.71828183F, // (0x1.5bf0a8P+1) https://oeis.org/A001113
+-                egammaf     = .577215665F, // (0x1.2788d0P-1) https://oeis.org/A001620
+-                ln2f        = .693147181F, // (0x1.62e430P-1) https://oeis.org/A002162
+-                ln10f       = 2.30258509F, // (0x1.26bb1cP+1) https://oeis.org/A002392
+-                log2ef      = 1.44269504F, // (0x1.715476P+0)
+-                log10ef     = .434294482F, // (0x1.bcb7b2P-2)
+-                pif         = 3.14159265F, // (0x1.921fb6P+1) https://oeis.org/A000796
+-                inv_pif     = .318309886F, // (0x1.45f306P-2) https://oeis.org/A049541
+-                sqrtpif     = 1.77245385F, // (0x1.c5bf8aP+0) https://oeis.org/A002161
+-                inv_sqrtpif = .564189584F, // (0x1.20dd76P-1) https://oeis.org/A087197
+-                sqrt2f      = 1.41421356F, // (0x1.6a09e6P+0) https://oeis.org/A002193
+-                inv_sqrt2f  = .707106781F, // (0x1.6a09e6P-1)
+-                sqrt3f      = 1.73205081F, // (0x1.bb67aeP+0) https://oeis.org/A002194
+-                inv_sqrt3f  = .577350269F, // (0x1.279a74P-1)
+-                phif        = 1.61803399F; // (0x1.9e377aP+0) https://oeis.org/A001622
+-} // namespace numbers
+-
+ /// Create a bitmask with the N right-most bits set to 1, and all other
+ /// bits set to 0.  Only unsigned types are allowed.
+ template <typename T> T maskTrailingOnes(unsigned N) {
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0014-EpochTracker-ABI-macro.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0014-EpochTracker-ABI-macro.patch
deleted file mode 100644
index 1049911..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0014-EpochTracker-ABI-macro.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From 9951c4b3fea6b2dbe7141070444de8df6ae4ce9b Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Wed, 4 May 2022 00:01:00 -0400
-Subject: [PATCH 14/28] EpochTracker ABI macro
-
----
- llvm/include/llvm/ADT/EpochTracker.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/llvm/include/llvm/ADT/EpochTracker.h b/llvm/include/llvm/ADT/EpochTracker.h
-index b06888494..f35461d1c 100644
---- a/llvm/include/llvm/ADT/EpochTracker.h
-+++ b/llvm/include/llvm/ADT/EpochTracker.h
-@@ -22,7 +22,7 @@
- 
- namespace llvm {
- 
--#if LLVM_ENABLE_ABI_BREAKING_CHECKS
-+#ifndef NDEBUG //ifndef LLVM_ENABLE_ABI_BREAKING_CHECKS
- 
- /// A base class for data structure classes wishing to make iterators
- /// ("handles") pointing into themselves fail-fast.  When building without
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0015-Add-lerp-and-sgn.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0015-Add-lerp-and-sgn.patch
new file mode 100644
index 0000000..c2fe9ed
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0015-Add-lerp-and-sgn.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Tue, 3 May 2022 22:50:24 -0400
+Subject: [PATCH 15/31] Add lerp and sgn
+
+---
+ llvm/include/llvm/Support/MathExtras.h | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
+index cdf859ccfaca22a04b08a351d7c2c9789a70627e..b82d9883c41008dcbbd933709c6e854ad74c5b58 100644
+--- a/llvm/include/llvm/Support/MathExtras.h
++++ b/llvm/include/llvm/Support/MathExtras.h
+@@ -614,6 +614,26 @@ std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
+     return UX > (static_cast<U>((std::numeric_limits<T>::max)())) / UY;
+ }
+ 
++// Typesafe implementation of the signum function.
++// Returns -1 if negative, 1 if positive, 0 if 0.
++template <typename T>
++constexpr int sgn(T val) {
++  return (T(0) < val) - (val < T(0));
++}
++
++/**
++ * Linearly interpolates between two values.
++ *
++ * @param startValue The start value.
++ * @param endValue The end value.
++ * @param t The fraction for interpolation.
++ *
++ * @return The interpolated value.
++ */
++template <typename T>
++constexpr T Lerp(const T& startValue, const T& endValue, double t) {
++  return startValue + (endValue - startValue) * t;
++}
+ } // End llvm namespace
+ 
+ #endif
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0015-Delete-numbers-from-MathExtras.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0015-Delete-numbers-from-MathExtras.patch
deleted file mode 100644
index 5578739..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0015-Delete-numbers-from-MathExtras.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-From 907608f09061ab272b0a127330b1b24e28d50a9f Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Thu, 5 May 2022 18:09:45 -0400
-Subject: [PATCH 15/28] Delete numbers from MathExtras
-
----
- llvm/include/llvm/Support/MathExtras.h | 36 --------------------------
- 1 file changed, 36 deletions(-)
-
-diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
-index fac12dd0e..e8f1f2aca 100644
---- a/llvm/include/llvm/Support/MathExtras.h
-+++ b/llvm/include/llvm/Support/MathExtras.h
-@@ -50,42 +50,6 @@ enum ZeroBehavior {
-   ZB_Width
- };
- 
--/// Mathematical constants.
--namespace numbers {
--// TODO: Track C++20 std::numbers.
--// TODO: Favor using the hexadecimal FP constants (requires C++17).
--constexpr double e          = 2.7182818284590452354, // (0x1.5bf0a8b145749P+1) https://oeis.org/A001113
--                 egamma     = .57721566490153286061, // (0x1.2788cfc6fb619P-1) https://oeis.org/A001620
--                 ln2        = .69314718055994530942, // (0x1.62e42fefa39efP-1) https://oeis.org/A002162
--                 ln10       = 2.3025850929940456840, // (0x1.24bb1bbb55516P+1) https://oeis.org/A002392
--                 log2e      = 1.4426950408889634074, // (0x1.71547652b82feP+0)
--                 log10e     = .43429448190325182765, // (0x1.bcb7b1526e50eP-2)
--                 pi         = 3.1415926535897932385, // (0x1.921fb54442d18P+1) https://oeis.org/A000796
--                 inv_pi     = .31830988618379067154, // (0x1.45f306bc9c883P-2) https://oeis.org/A049541
--                 sqrtpi     = 1.7724538509055160273, // (0x1.c5bf891b4ef6bP+0) https://oeis.org/A002161
--                 inv_sqrtpi = .56418958354775628695, // (0x1.20dd750429b6dP-1) https://oeis.org/A087197
--                 sqrt2      = 1.4142135623730950488, // (0x1.6a09e667f3bcdP+0) https://oeis.org/A00219
--                 inv_sqrt2  = .70710678118654752440, // (0x1.6a09e667f3bcdP-1)
--                 sqrt3      = 1.7320508075688772935, // (0x1.bb67ae8584caaP+0) https://oeis.org/A002194
--                 inv_sqrt3  = .57735026918962576451, // (0x1.279a74590331cP-1)
--                 phi        = 1.6180339887498948482; // (0x1.9e3779b97f4a8P+0) https://oeis.org/A001622
--constexpr float ef          = 2.71828183F, // (0x1.5bf0a8P+1) https://oeis.org/A001113
--                egammaf     = .577215665F, // (0x1.2788d0P-1) https://oeis.org/A001620
--                ln2f        = .693147181F, // (0x1.62e430P-1) https://oeis.org/A002162
--                ln10f       = 2.30258509F, // (0x1.26bb1cP+1) https://oeis.org/A002392
--                log2ef      = 1.44269504F, // (0x1.715476P+0)
--                log10ef     = .434294482F, // (0x1.bcb7b2P-2)
--                pif         = 3.14159265F, // (0x1.921fb6P+1) https://oeis.org/A000796
--                inv_pif     = .318309886F, // (0x1.45f306P-2) https://oeis.org/A049541
--                sqrtpif     = 1.77245385F, // (0x1.c5bf8aP+0) https://oeis.org/A002161
--                inv_sqrtpif = .564189584F, // (0x1.20dd76P-1) https://oeis.org/A087197
--                sqrt2f      = 1.41421356F, // (0x1.6a09e6P+0) https://oeis.org/A002193
--                inv_sqrt2f  = .707106781F, // (0x1.6a09e6P-1)
--                sqrt3f      = 1.73205081F, // (0x1.bb67aeP+0) https://oeis.org/A002194
--                inv_sqrt3f  = .577350269F, // (0x1.279a74P-1)
--                phif        = 1.61803399F; // (0x1.9e377aP+0) https://oeis.org/A001622
--} // namespace numbers
--
- namespace detail {
- template <typename T, std::size_t SizeOfT> struct TrailingZerosCounter {
-   static unsigned count(T Val, ZeroBehavior) {
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0016-Add-lerp-and-sgn.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0016-Add-lerp-and-sgn.patch
deleted file mode 100644
index 8dcc9c7..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0016-Add-lerp-and-sgn.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From 9ef10e5b331d00d7d5822afdb70c1f2d9981d772 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Tue, 3 May 2022 22:50:24 -0400
-Subject: [PATCH 16/28] Add lerp and sgn
-
----
- llvm/include/llvm/Support/MathExtras.h | 20 ++++++++++++++++++++
- 1 file changed, 20 insertions(+)
-
-diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
-index e8f1f2aca..8116c58bd 100644
---- a/llvm/include/llvm/Support/MathExtras.h
-+++ b/llvm/include/llvm/Support/MathExtras.h
-@@ -930,6 +930,26 @@ std::enable_if_t<std::is_signed<T>::value, T> MulOverflow(T X, T Y, T &Result) {
-     return UX > (static_cast<U>((std::numeric_limits<T>::max)())) / UY;
- }
- 
-+// Typesafe implementation of the signum function.
-+// Returns -1 if negative, 1 if positive, 0 if 0.
-+template <typename T>
-+constexpr int sgn(T val) {
-+  return (T(0) < val) - (val < T(0));
-+}
-+
-+/**
-+ * Linearly interpolates between two values.
-+ *
-+ * @param startValue The start value.
-+ * @param endValue The end value.
-+ * @param t The fraction for interpolation.
-+ *
-+ * @return The interpolated value.
-+ */
-+template <typename T>
-+constexpr T Lerp(const T& startValue, const T& endValue, double t) {
-+  return startValue + (endValue - startValue) * t;
-+}
- } // End llvm namespace
- 
- #endif
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0016-Fixup-includes.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0016-Fixup-includes.patch
new file mode 100644
index 0000000..6d32c22
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0016-Fixup-includes.patch
@@ -0,0 +1,180 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sun, 8 May 2022 16:38:11 -0400
+Subject: [PATCH 16/31] Fixup includes
+
+---
+ llvm/include/llvm/ADT/StringMap.h                 |  4 ++++
+ llvm/include/llvm/ADT/StringMapEntry.h            |  4 ++++
+ llvm/include/llvm/Support/PointerLikeTypeTraits.h |  1 +
+ llvm/lib/Support/ConvertUTFWrapper.cpp            |  1 +
+ llvm/lib/Support/ErrorHandling.cpp                |  7 +++----
+ llvm/lib/Support/raw_ostream.cpp                  | 11 ++++++-----
+ llvm/unittests/ADT/SmallPtrSetTest.cpp            |  2 ++
+ llvm/unittests/ADT/SmallVectorTest.cpp            |  1 +
+ llvm/unittests/ADT/StringMapTest.cpp              |  1 +
+ llvm/unittests/Support/ConvertUTFTest.cpp         |  2 ++
+ 10 files changed, 25 insertions(+), 9 deletions(-)
+
+diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h
+index c133e84f9b2e3a225cdac782c011fadbf07adab2..2173a4159111e7fea70325de82dbfce628ae3ea8 100644
+--- a/llvm/include/llvm/ADT/StringMap.h
++++ b/llvm/include/llvm/ADT/StringMap.h
+@@ -17,6 +17,10 @@
+ #include "llvm/ADT/StringMapEntry.h"
+ #include "llvm/ADT/iterator.h"
+ #include "llvm/Support/AllocatorBase.h"
++#include "llvm/Support/MemAlloc.h"
++#include "llvm/Support/SmallVector.h"
++#include "llvm/Support/iterator.h"
++#include "llvm/Support/iterator_range.h"
+ #include "llvm/Support/PointerLikeTypeTraits.h"
+ #include <initializer_list>
+ #include <iterator>
+diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h
+index 388e81c361642113937f7d5680de73a50635b07d..011806f5fd89ff738ed805a82b3ddbc6fc9b08ce 100644
+--- a/llvm/include/llvm/ADT/StringMapEntry.h
++++ b/llvm/include/llvm/ADT/StringMapEntry.h
+@@ -16,6 +16,10 @@
+ #ifndef LLVM_ADT_STRINGMAPENTRY_H
+ #define LLVM_ADT_STRINGMAPENTRY_H
+ 
++#include "wpi/MemAlloc.h"
++
++#include <cassert>
++#include <cstring>
+ #include <optional>
+ #include <string_view>
+ 
+diff --git a/llvm/include/llvm/Support/PointerLikeTypeTraits.h b/llvm/include/llvm/Support/PointerLikeTypeTraits.h
+index 1b15f930bd87d97d51824af5e62ea5f222a6b4c9..acadd5e89a1651cfbad67a5b1b0933d1f288d094 100644
+--- a/llvm/include/llvm/Support/PointerLikeTypeTraits.h
++++ b/llvm/include/llvm/Support/PointerLikeTypeTraits.h
+@@ -16,6 +16,7 @@
+ 
+ #include "llvm/Support/DataTypes.h"
+ #include <cassert>
++#include <cstdint>
+ #include <type_traits>
+ 
+ namespace llvm {
+diff --git a/llvm/lib/Support/ConvertUTFWrapper.cpp b/llvm/lib/Support/ConvertUTFWrapper.cpp
+index d53462e742e61d3476915d5b2c5aa63772e78a8a..34054140489e4d536ace4650207c783d669d850e 100644
+--- a/llvm/lib/Support/ConvertUTFWrapper.cpp
++++ b/llvm/lib/Support/ConvertUTFWrapper.cpp
+@@ -7,6 +7,7 @@
+ //===----------------------------------------------------------------------===//
+ 
+ #include "llvm/Support/ConvertUTF.h"
++#include "llvm/Support/SmallVector.h"
+ #include "llvm/Support/ErrorHandling.h"
+ #include "llvm/Support/SwapByteOrder.h"
+ #include <span>
+diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
+index 0b87b375de67dc18647e3ebe646bf323dd05e8c5..3a88178cfbbcf7062a958c7de820247bc913ab33 100644
+--- a/llvm/lib/Support/ErrorHandling.cpp
++++ b/llvm/lib/Support/ErrorHandling.cpp
+@@ -28,12 +28,11 @@
+ #include <mutex>
+ #include <new>
+ 
+-#if defined(HAVE_UNISTD_H)
+-# include <unistd.h>
++#ifndef _WIN32
++#include <unistd.h>
+ #endif
+ #if defined(_MSC_VER)
+-# include <io.h>
+-# include <fcntl.h>
++#include <io.h>
+ #endif
+ 
+ using namespace llvm;
+diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
+index b2a726633b48b179abfd24a5de110a2301e0f877..3b7d8d6db5f0df31e18b91be716a4fd21e7e3549 100644
+--- a/llvm/lib/Support/raw_ostream.cpp
++++ b/llvm/lib/Support/raw_ostream.cpp
+@@ -15,6 +15,8 @@
+ #endif
+ 
+ #include "llvm/Support/raw_ostream.h"
++#include "wpi/SmallString.h"
++#include "wpi/SmallVector.h"
+ #include "llvm/ADT/StringExtras.h"
+ #include "llvm/Config/config.h"
+ #include "llvm/Support/Compiler.h"
+@@ -32,12 +34,11 @@
+ #include <sys/stat.h>
+ 
+ // <fcntl.h> may provide O_BINARY.
+-#if defined(HAVE_FCNTL_H)
+ # include <fcntl.h>
+-#endif
+ 
+-#if defined(HAVE_UNISTD_H)
+-# include <unistd.h>
++#ifndef _WIN32
++#include <unistd.h>
++#include <sys/uio.h>
+ #endif
+ 
+ #if defined(__CYGWIN__)
+@@ -60,7 +61,7 @@
+ #ifdef _WIN32
+ #include "llvm/Support/ConvertUTF.h"
+ #include "llvm/Support/Signals.h"
+-#include "llvm/Support/Windows/WindowsSupport.h"
++#include "Windows/WindowsSupport.h"
+ #endif
+ 
+ using namespace llvm;
+diff --git a/llvm/unittests/ADT/SmallPtrSetTest.cpp b/llvm/unittests/ADT/SmallPtrSetTest.cpp
+index 531f81ab5b3fc1dcff731230f3cb7649cb90aedf..3db8b6e37d31a0a3cc304da8fc4cbbe1d89252b5 100644
+--- a/llvm/unittests/ADT/SmallPtrSetTest.cpp
++++ b/llvm/unittests/ADT/SmallPtrSetTest.cpp
+@@ -15,6 +15,8 @@
+ #include "llvm/Support/PointerLikeTypeTraits.h"
+ #include "gtest/gtest.h"
+ 
++#include <algorithm>
++
+ using namespace llvm;
+ 
+ TEST(SmallPtrSetTest, Assignment) {
+diff --git a/llvm/unittests/ADT/SmallVectorTest.cpp b/llvm/unittests/ADT/SmallVectorTest.cpp
+index f8c37820ef9fdfe0af067f5aa8d2297ed15e73bc..5e91f71bc9ac0e499a64dd1591e581d0707417f6 100644
+--- a/llvm/unittests/ADT/SmallVectorTest.cpp
++++ b/llvm/unittests/ADT/SmallVectorTest.cpp
+@@ -13,6 +13,7 @@
+ #include "llvm/ADT/SmallVector.h"
+ #include "llvm/Support/Compiler.h"
+ #include "gtest/gtest.h"
++#include <array>
+ #include <list>
+ #include <span>
+ #include <stdarg.h>
+diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
+index acd8b566f9c7a6efc2c9204624c01104dd34daf6..6b6cf564909f329c220eb225f3b7af6c35301029 100644
+--- a/llvm/unittests/ADT/StringMapTest.cpp
++++ b/llvm/unittests/ADT/StringMapTest.cpp
+@@ -9,6 +9,7 @@
+ #include "llvm/ADT/StringMap.h"
+ #include "llvm/Support/DataTypes.h"
+ #include "gtest/gtest.h"
++#include <algorithm>
+ #include <limits>
+ #include <tuple>
+ using namespace llvm;
+diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp
+index 3b07d344f15a555f11ad5f8177a0a65b8a4fa472..77e70a46d3621ecfaed923d87256184addfda721 100644
+--- a/llvm/unittests/Support/ConvertUTFTest.cpp
++++ b/llvm/unittests/Support/ConvertUTFTest.cpp
+@@ -7,6 +7,8 @@
+ //===----------------------------------------------------------------------===//
+ 
+ #include "llvm/Support/ConvertUTF.h"
++#include "llvm/Support/SmallString.h"
++#include "llvm/Support/SmallVector.h"
+ #include "gtest/gtest.h"
+ #include <string>
+ #include <vector>
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0017-Fixup-includes.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0017-Fixup-includes.patch
deleted file mode 100644
index f247db0..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0017-Fixup-includes.patch
+++ /dev/null
@@ -1,168 +0,0 @@
-From aa30b80d86cb0774efc094646339b54694ab8398 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sun, 8 May 2022 16:38:11 -0400
-Subject: [PATCH 17/28] Fixup includes
-
----
- llvm/include/llvm/ADT/StringMap.h                 |  4 ++++
- llvm/include/llvm/ADT/StringMapEntry.h            |  4 ++++
- llvm/include/llvm/Support/PointerLikeTypeTraits.h |  1 +
- llvm/lib/Support/ConvertUTFWrapper.cpp            |  1 +
- llvm/lib/Support/ErrorHandling.cpp                |  7 +++----
- llvm/lib/Support/raw_ostream.cpp                  | 12 ++++++------
- llvm/unittests/ADT/SmallPtrSetTest.cpp            |  2 ++
- llvm/unittests/ADT/StringMapTest.cpp              |  1 +
- llvm/unittests/Support/ConvertUTFTest.cpp         |  2 ++
- 9 files changed, 24 insertions(+), 10 deletions(-)
-
-diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h
-index 16f13f048..6ae0e39a1 100644
---- a/llvm/include/llvm/ADT/StringMap.h
-+++ b/llvm/include/llvm/ADT/StringMap.h
-@@ -17,6 +17,10 @@
- #include "llvm/ADT/StringMapEntry.h"
- #include "llvm/ADT/iterator.h"
- #include "llvm/Support/AllocatorBase.h"
-+#include "llvm/Support/MemAlloc.h"
-+#include "llvm/Support/SmallVector.h"
-+#include "llvm/Support/iterator.h"
-+#include "llvm/Support/iterator_range.h"
- #include "llvm/Support/PointerLikeTypeTraits.h"
- #include <initializer_list>
- #include <iterator>
-diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h
-index 39976a02b..cdefc5449 100644
---- a/llvm/include/llvm/ADT/StringMapEntry.h
-+++ b/llvm/include/llvm/ADT/StringMapEntry.h
-@@ -16,6 +16,10 @@
- #ifndef LLVM_ADT_STRINGMAPENTRY_H
- #define LLVM_ADT_STRINGMAPENTRY_H
- 
-+#include "wpi/MemAlloc.h"
-+
-+#include <cassert>
-+#include <cstring>
- #include <optional>
- #include <string_view>
- 
-diff --git a/llvm/include/llvm/Support/PointerLikeTypeTraits.h b/llvm/include/llvm/Support/PointerLikeTypeTraits.h
-index 1b15f930b..acadd5e89 100644
---- a/llvm/include/llvm/Support/PointerLikeTypeTraits.h
-+++ b/llvm/include/llvm/Support/PointerLikeTypeTraits.h
-@@ -16,6 +16,7 @@
- 
- #include "llvm/Support/DataTypes.h"
- #include <cassert>
-+#include <cstdint>
- #include <type_traits>
- 
- namespace llvm {
-diff --git a/llvm/lib/Support/ConvertUTFWrapper.cpp b/llvm/lib/Support/ConvertUTFWrapper.cpp
-index 396ab0c65..cff30f16c 100644
---- a/llvm/lib/Support/ConvertUTFWrapper.cpp
-+++ b/llvm/lib/Support/ConvertUTFWrapper.cpp
-@@ -8,6 +8,7 @@
- 
- #include "llvm/ADT/span.h"
- #include "llvm/Support/ConvertUTF.h"
-+#include "llvm/Support/SmallVector.h"
- #include "llvm/Support/ErrorHandling.h"
- #include "llvm/Support/SwapByteOrder.h"
- #include <string>
-diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
-index f80e28e87..ec1a1633a 100644
---- a/llvm/lib/Support/ErrorHandling.cpp
-+++ b/llvm/lib/Support/ErrorHandling.cpp
-@@ -28,12 +28,11 @@
- #include <mutex>
- #include <new>
- 
--#if defined(HAVE_UNISTD_H)
--# include <unistd.h>
-+#ifndef _WIN32
-+#include <unistd.h>
- #endif
- #if defined(_MSC_VER)
--# include <io.h>
--# include <fcntl.h>
-+#include <io.h>
- #endif
- 
- using namespace llvm;
-diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
-index 95152849c..878a3a5e9 100644
---- a/llvm/lib/Support/raw_ostream.cpp
-+++ b/llvm/lib/Support/raw_ostream.cpp
-@@ -15,7 +15,8 @@
- #endif
- 
- #include "llvm/Support/raw_ostream.h"
--#include "llvm/ADT/STLArrayExtras.h"
-+#include "wpi/SmallString.h"
-+#include "wpi/SmallVector.h"
- #include "llvm/ADT/StringExtras.h"
- #include "llvm/Config/config.h"
- #include "llvm/Support/Compiler.h"
-@@ -33,12 +34,11 @@
- #include <sys/stat.h>
- 
- // <fcntl.h> may provide O_BINARY.
--#if defined(HAVE_FCNTL_H)
- # include <fcntl.h>
--#endif
- 
--#if defined(HAVE_UNISTD_H)
--# include <unistd.h>
-+#ifndef _WIN32
-+#include <unistd.h>
-+#include <sys/uio.h>
- #endif
- 
- #if defined(__CYGWIN__)
-@@ -60,7 +60,7 @@
- 
- #ifdef _WIN32
- #include "llvm/Support/ConvertUTF.h"
--#include "llvm/Support/Windows/WindowsSupport.h"
-+#include "Windows/WindowsSupport.h"
- #endif
- 
- using namespace llvm;
-diff --git a/llvm/unittests/ADT/SmallPtrSetTest.cpp b/llvm/unittests/ADT/SmallPtrSetTest.cpp
-index 531f81ab5..3db8b6e37 100644
---- a/llvm/unittests/ADT/SmallPtrSetTest.cpp
-+++ b/llvm/unittests/ADT/SmallPtrSetTest.cpp
-@@ -15,6 +15,8 @@
- #include "llvm/Support/PointerLikeTypeTraits.h"
- #include "gtest/gtest.h"
- 
-+#include <algorithm>
-+
- using namespace llvm;
- 
- TEST(SmallPtrSetTest, Assignment) {
-diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
-index 6d0c0942c..de6daf3da 100644
---- a/llvm/unittests/ADT/StringMapTest.cpp
-+++ b/llvm/unittests/ADT/StringMapTest.cpp
-@@ -9,6 +9,7 @@
- #include "llvm/ADT/StringMap.h"
- #include "llvm/Support/DataTypes.h"
- #include "gtest/gtest.h"
-+#include <algorithm>
- #include <limits>
- #include <tuple>
- using namespace llvm;
-diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp
-index 9c798437a..2fee8ad5c 100644
---- a/llvm/unittests/Support/ConvertUTFTest.cpp
-+++ b/llvm/unittests/Support/ConvertUTFTest.cpp
-@@ -7,6 +7,8 @@
- //===----------------------------------------------------------------------===//
- 
- #include "llvm/Support/ConvertUTF.h"
-+#include "llvm/Support/SmallString.h"
-+#include "llvm/Support/SmallVector.h"
- #include "gtest/gtest.h"
- #include <string>
- #include <vector>
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0017-Use-std-is_trivially_copy_constructible.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0017-Use-std-is_trivially_copy_constructible.patch
new file mode 100644
index 0000000..c78fd42
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0017-Use-std-is_trivially_copy_constructible.patch
@@ -0,0 +1,36 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sun, 8 May 2022 16:42:09 -0400
+Subject: [PATCH 17/31] Use std::is_trivially_copy_constructible
+
+---
+ llvm/include/llvm/Support/type_traits.h | 16 ----------------
+ 1 file changed, 16 deletions(-)
+
+diff --git a/llvm/include/llvm/Support/type_traits.h b/llvm/include/llvm/Support/type_traits.h
+index 3fd158def34d7256a736f8fb0b30dadea2177864..3171af93fa7ffe4707c03289270cf5951e3db7c5 100644
+--- a/llvm/include/llvm/Support/type_traits.h
++++ b/llvm/include/llvm/Support/type_traits.h
+@@ -76,22 +76,6 @@ union trivial_helper {
+ 
+ } // end namespace detail
+ 
+-template <typename T>
+-struct is_copy_assignable {
+-  template<class F>
+-    static auto get(F*) -> decltype(std::declval<F &>() = std::declval<const F &>(), std::true_type{});
+-    static std::false_type get(...);
+-    static constexpr bool value = decltype(get((T*)nullptr))::value;
+-};
+-
+-template <typename T>
+-struct is_move_assignable {
+-  template<class F>
+-    static auto get(F*) -> decltype(std::declval<F &>() = std::declval<F &&>(), std::true_type{});
+-    static std::false_type get(...);
+-    static constexpr bool value = decltype(get((T*)nullptr))::value;
+-};
+-
+ } // end namespace llvm
+ 
+ #endif // LLVM_SUPPORT_TYPE_TRAITS_H
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0018-Use-std-is_trivially_copy_constructible.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0018-Use-std-is_trivially_copy_constructible.patch
index 4e2650e..47fdf0a 100644
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0018-Use-std-is_trivially_copy_constructible.patch
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0018-Use-std-is_trivially_copy_constructible.patch
@@ -1,41 +1,17 @@
-From 1c3e8a6ff8d8b6c054141503c7318d69319d8d41 Mon Sep 17 00:00:00 2001
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: PJ Reiniger <pj.reiniger@gmail.com>
 Date: Sun, 8 May 2022 16:42:09 -0400
-Subject: [PATCH 18/28] Use std::is_trivially_copy_constructible
+Subject: [PATCH 18/31] Use std::is_trivially_copy_constructible
 
 ---
- llvm/include/llvm/ADT/PointerIntPair.h  | 12 ----
- llvm/include/llvm/Support/type_traits.h | 91 +------------------------
- 2 files changed, 2 insertions(+), 101 deletions(-)
+ llvm/include/llvm/Support/type_traits.h | 37 ++-----------------------
+ 1 file changed, 2 insertions(+), 35 deletions(-)
 
-diff --git a/llvm/include/llvm/ADT/PointerIntPair.h b/llvm/include/llvm/ADT/PointerIntPair.h
-index b7ddf8855..a48fb904b 100644
---- a/llvm/include/llvm/ADT/PointerIntPair.h
-+++ b/llvm/include/llvm/ADT/PointerIntPair.h
-@@ -128,18 +128,6 @@ public:
-   }
- };
- 
--// Specialize is_trivially_copyable to avoid limitation of llvm::is_trivially_copyable
--// when compiled with gcc 4.9.
--template <typename PointerTy, unsigned IntBits, typename IntType,
--          typename PtrTraits,
--          typename Info>
--struct is_trivially_copyable<PointerIntPair<PointerTy, IntBits, IntType, PtrTraits, Info>> : std::true_type {
--#ifdef HAVE_STD_IS_TRIVIALLY_COPYABLE
--  static_assert(std::is_trivially_copyable<PointerIntPair<PointerTy, IntBits, IntType, PtrTraits, Info>>::value,
--                "inconsistent behavior between llvm:: and std:: implementation of is_trivially_copyable");
--#endif
--};
--
- 
- template <typename PointerT, unsigned IntBits, typename PtrTraits>
- struct PointerIntPairInfo {
 diff --git a/llvm/include/llvm/Support/type_traits.h b/llvm/include/llvm/Support/type_traits.h
-index 7b7d5d991..72a2e84ad 100644
+index a6046de87d1e3e1264a0040826ea347b870a8f9e..292a2cb78b93717647cf88d00f88c2eec14f52f6 100644
 --- a/llvm/include/llvm/Support/type_traits.h
 +++ b/llvm/include/llvm/Support/type_traits.h
-@@ -92,98 +92,11 @@ union trivial_helper {
+@@ -92,44 +92,11 @@ union trivial_helper {
  
  } // end namespace detail
  
@@ -78,61 +54,7 @@
 -    static std::false_type get(...);
 -    static constexpr bool value = decltype(get((T*)nullptr))::value;
 -};
--
--
--// An implementation of `std::is_trivially_copyable` since STL version
--// is not equally supported by all compilers, especially GCC 4.9.
--// Uniform implementation of this trait is important for ABI compatibility
--// as it has an impact on SmallVector's ABI (among others).
--template <typename T>
--class is_trivially_copyable {
--
--  // copy constructors
--  static constexpr bool has_trivial_copy_constructor =
--      std::is_copy_constructible<detail::trivial_helper<T>>::value;
--  static constexpr bool has_deleted_copy_constructor =
--      !std::is_copy_constructible<T>::value;
--
--  // move constructors
--  static constexpr bool has_trivial_move_constructor =
--      std::is_move_constructible<detail::trivial_helper<T>>::value;
--  static constexpr bool has_deleted_move_constructor =
--      !std::is_move_constructible<T>::value;
--
--  // copy assign
--  static constexpr bool has_trivial_copy_assign =
--      is_copy_assignable<detail::trivial_helper<T>>::value;
--  static constexpr bool has_deleted_copy_assign =
--      !is_copy_assignable<T>::value;
--
--  // move assign
--  static constexpr bool has_trivial_move_assign =
--      is_move_assignable<detail::trivial_helper<T>>::value;
--  static constexpr bool has_deleted_move_assign =
--      !is_move_assignable<T>::value;
--
--  // destructor
--  static constexpr bool has_trivial_destructor =
--      std::is_destructible<detail::trivial_helper<T>>::value;
--
--  public:
--
--  static constexpr bool value =
--      has_trivial_destructor &&
--      (has_deleted_move_assign || has_trivial_move_assign) &&
--      (has_deleted_move_constructor || has_trivial_move_constructor) &&
--      (has_deleted_copy_assign || has_trivial_copy_assign) &&
--      (has_deleted_copy_constructor || has_trivial_copy_constructor);
--
--#ifdef HAVE_STD_IS_TRIVIALLY_COPYABLE
--  static_assert(value == std::is_trivially_copyable<T>::value,
--                "inconsistent behavior between llvm:: and std:: implementation of is_trivially_copyable");
--#endif
--};
--template <typename T>
--class is_trivially_copyable<T*> : public std::true_type {
--};
 +using is_trivially_copy_constructible = std::is_trivially_copy_constructible<T>;
  
- 
  } // end namespace llvm
+ 
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0018-Windows-support.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0018-Windows-support.patch
new file mode 100644
index 0000000..43e0a07
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0018-Windows-support.patch
@@ -0,0 +1,217 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Tue, 3 May 2022 20:22:38 -0400
+Subject: [PATCH 18/31] Windows support
+
+---
+ .../llvm/Support/Windows/WindowsSupport.h     | 45 +++++----
+ llvm/lib/Support/ConvertUTF.cpp               | 95 +++++++++++++++++++
+ llvm/lib/Support/raw_ostream.cpp              |  1 -
+ 3 files changed, 124 insertions(+), 17 deletions(-)
+
+diff --git a/llvm/include/llvm/Support/Windows/WindowsSupport.h b/llvm/include/llvm/Support/Windows/WindowsSupport.h
+index aabdb2f14668a990329b57f5454a0d7db73e12ce..2ac474092a62d488da1ec7f07a1cd10b0781d938 100644
+--- a/llvm/include/llvm/Support/Windows/WindowsSupport.h
++++ b/llvm/include/llvm/Support/Windows/WindowsSupport.h
+@@ -35,8 +35,6 @@
+ 
+ #include "llvm/ADT/SmallVector.h"
+ #include "llvm/ADT/StringExtras.h"
+-#include "llvm/Config/llvm-config.h" // Get build system configuration settings
+-#include "llvm/Support/Allocator.h"
+ #include "llvm/Support/Chrono.h"
+ #include "llvm/Support/Compiler.h"
+ #include "llvm/Support/ErrorHandling.h"
+@@ -44,18 +42,46 @@
+ #include <cassert>
+ #include <string>
+ #include <system_error>
++#define WIN32_NO_STATUS
+ #include <windows.h>
++#undef WIN32_NO_STATUS
++#include <winternl.h>
++#include <ntstatus.h>
+ 
+ // Must be included after windows.h
+ #include <wincrypt.h>
+ 
+ namespace llvm {
+ 
++/// 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 llvm::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 llvm::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0,
++                                  info.dwBuildNumber);
++      }
++    }
++  }
++  return llvm::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.
+-bool RunningWindows8OrGreater();
++inline bool RunningWindows8OrGreater() {
++  // Windows 8 is version 6.2, service pack 0.
++  return GetWindowsOSVersion() >= llvm::VersionTuple(6, 2, 0, 0);
++}
+ 
+ /// Determines if the program is running on Windows 11 or Windows Server 2022.
+ bool RunningWindows11OrGreater();
+@@ -231,19 +257,6 @@ inline FILETIME toFILETIME(TimePoint<> TP) {
+   return Time;
+ }
+ 
+-namespace windows {
+-// Returns command line arguments. Unlike arguments given to main(),
+-// this function guarantees that the returned arguments are encoded in
+-// UTF-8 regardless of the current code page setting.
+-std::error_code GetCommandLineArguments(SmallVectorImpl<const char *> &Args,
+-                                        BumpPtrAllocator &Alloc);
+-
+-/// Convert UTF-8 path to a suitable UTF-16 path for use with the Win32 Unicode
+-/// File API.
+-std::error_code widenPath(const Twine &Path8, SmallVectorImpl<wchar_t> &Path16,
+-                          size_t MaxPathLen = MAX_PATH);
+-
+-} // end namespace windows
+ } // end namespace sys
+ } // end namespace llvm.
+ 
+diff --git a/llvm/lib/Support/ConvertUTF.cpp b/llvm/lib/Support/ConvertUTF.cpp
+index bc04c5ab5113563fb82d7b3b168985369b611f4b..57eb64a6017a6964ab14b40b8c6b3563cd41ffac 100644
+--- a/llvm/lib/Support/ConvertUTF.cpp
++++ b/llvm/lib/Support/ConvertUTF.cpp
+@@ -67,6 +67,11 @@
+ #endif
+ #include <assert.h>
+ 
++#ifdef _WIN32
++#include "wpi/WindowsError.h"
++#include "Windows/WindowsSupport.h"
++#endif
++
+ /*
+  * This code extensively uses fall-through switches.
+  * Keep the compiler from warning about that.
+@@ -759,6 +764,96 @@ ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart,
+ 
+    --------------------------------------------------------------------- */
+ 
++#ifdef _WIN32
++
++namespace sys {
++namespace windows {
++std::error_code CodePageToUTF16(unsigned codepage,
++                                std::string_view original,
++                                wpi::SmallVectorImpl<wchar_t> &utf16) {
++  if (!original.empty()) {
++    int len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.data(),
++                                    original.size(), utf16.begin(), 0);
++
++    if (len == 0) {
++      return mapWindowsError(::GetLastError());
++    }
++
++    utf16.reserve(len + 1);
++    utf16.resize_for_overwrite(len);
++
++    len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.data(),
++                                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(std::string_view utf8,
++                            wpi::SmallVectorImpl<wchar_t> &utf16) {
++  return CodePageToUTF16(CP_UTF8, utf8, utf16);
++}
++
++std::error_code CurCPToUTF16(std::string_view 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.resize_for_overwrite(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
++
++#endif  // _WIN32
++
+ } // namespace llvm
+ 
+ ConvertUTF_RESTORE_WARNINGS
+diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
+index 3b7d8d6db5f0df31e18b91be716a4fd21e7e3549..4769d34a14f3f2cbaaa4df50ea7111fe9fa2792f 100644
+--- a/llvm/lib/Support/raw_ostream.cpp
++++ b/llvm/lib/Support/raw_ostream.cpp
+@@ -534,7 +534,6 @@ void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
+       DWORD WinLastError = GetLastError();
+       if (WinLastError == ERROR_BROKEN_PIPE ||
+           (WinLastError == ERROR_NO_DATA && errno == EINVAL)) {
+-        llvm::sys::CallOneShotPipeSignalHandler();
+         errno = EPIPE;
+       }
+ #endif
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0019-Prefer-fmtlib.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0019-Prefer-fmtlib.patch
new file mode 100644
index 0000000..4d5b81c
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0019-Prefer-fmtlib.patch
@@ -0,0 +1,55 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sun, 8 May 2022 16:46:20 -0400
+Subject: [PATCH 19/31] Prefer fmtlib
+
+---
+ llvm/lib/Support/ErrorHandling.cpp | 20 ++++++--------------
+ 1 file changed, 6 insertions(+), 14 deletions(-)
+
+diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
+index 3a88178cfbbcf7062a958c7de820247bc913ab33..54137a331ca9e752b02c0f16ae996094a6f2fafa 100644
+--- a/llvm/lib/Support/ErrorHandling.cpp
++++ b/llvm/lib/Support/ErrorHandling.cpp
+@@ -22,7 +22,7 @@
+ #include "llvm/Support/Signals.h"
+ #include "llvm/Support/Threading.h"
+ #include "llvm/Support/WindowsError.h"
+-#include "llvm/Support/raw_ostream.h"
++#include "fmt/format.h"
+ #include <cassert>
+ #include <cstdlib>
+ #include <mutex>
+@@ -93,15 +93,7 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
+   if (handler) {
+     handler(handlerData, std::string{Reason}.c_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";
+-    std::string_view MessageStr = OS.str();
+-    ssize_t written = ::write(2, MessageStr.data(), MessageStr.size());
+-    (void)written; // If something went wrong, we deliberately just give up.
++    fmt::print(stderr, "LLVM ERROR: {}\n", Reason);
+   }
+ 
+   // If we reached here, we are failing ungracefully. Run the interrupt handlers
+@@ -176,11 +168,11 @@ void llvm::llvm_unreachable_internal(const char *msg, const char *file,
+   // llvm_unreachable is intended to be used to indicate "impossible"
+   // situations, and not legitimate runtime errors.
+   if (msg)
+-    dbgs() << msg << "\n";
+-  dbgs() << "UNREACHABLE executed";
++    fmt::print(stderr, "{}\n", msg);
++  std::fputs("UNREACHABLE executed", stderr);
+   if (file)
+-    dbgs() << " at " << file << ":" << line;
+-  dbgs() << "!\n";
++    fmt::print(stderr, " at {}:{}", file, line);
++  fmt::print(stderr, "!\n");
+   abort();
+ #ifdef LLVM_BUILTIN_UNREACHABLE
+   // Windows systems and possibly others don't declare abort() to be noreturn,
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0019-Windows-support.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0019-Windows-support.patch
deleted file mode 100644
index 07d973b..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0019-Windows-support.patch
+++ /dev/null
@@ -1,204 +0,0 @@
-From 36f7f08e257f2b58b2894f165a38ff2a831aed8f Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Tue, 3 May 2022 20:22:38 -0400
-Subject: [PATCH 19/28] Windows support
-
----
- .../llvm/Support/Windows/WindowsSupport.h     | 45 +++++----
- llvm/lib/Support/ConvertUTF.cpp               | 95 +++++++++++++++++++
- 2 files changed, 124 insertions(+), 16 deletions(-)
-
-diff --git a/llvm/include/llvm/Support/Windows/WindowsSupport.h b/llvm/include/llvm/Support/Windows/WindowsSupport.h
-index 180803fbd..31120cfa0 100644
---- a/llvm/include/llvm/Support/Windows/WindowsSupport.h
-+++ b/llvm/include/llvm/Support/Windows/WindowsSupport.h
-@@ -35,8 +35,6 @@
- 
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/ADT/StringExtras.h"
--#include "llvm/Config/llvm-config.h" // Get build system configuration settings
--#include "llvm/Support/Allocator.h"
- #include "llvm/Support/Chrono.h"
- #include "llvm/Support/Compiler.h"
- #include "llvm/Support/ErrorHandling.h"
-@@ -44,18 +42,46 @@
- #include <cassert>
- #include <string>
- #include <system_error>
-+#define WIN32_NO_STATUS
- #include <windows.h>
-+#undef WIN32_NO_STATUS
-+#include <winternl.h>
-+#include <ntstatus.h>
- 
- // Must be included after windows.h
- #include <wincrypt.h>
- 
- namespace llvm {
- 
-+/// 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 llvm::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 llvm::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0,
-+                                  info.dwBuildNumber);
-+      }
-+    }
-+  }
-+  return llvm::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.
--bool RunningWindows8OrGreater();
-+inline bool RunningWindows8OrGreater() {
-+  // Windows 8 is version 6.2, service pack 0.
-+  return GetWindowsOSVersion() >= llvm::VersionTuple(6, 2, 0, 0);
-+}
- 
- /// Returns the Windows version as Major.Minor.0.BuildNumber. Uses
- /// RtlGetVersion or GetVersionEx under the hood depending on what is available.
-@@ -228,19 +254,6 @@ inline FILETIME toFILETIME(TimePoint<> TP) {
-   return Time;
- }
- 
--namespace windows {
--// Returns command line arguments. Unlike arguments given to main(),
--// this function guarantees that the returned arguments are encoded in
--// UTF-8 regardless of the current code page setting.
--std::error_code GetCommandLineArguments(SmallVectorImpl<const char *> &Args,
--                                        BumpPtrAllocator &Alloc);
--
--/// Convert UTF-8 path to a suitable UTF-16 path for use with the Win32 Unicode
--/// File API.
--std::error_code widenPath(const Twine &Path8, SmallVectorImpl<wchar_t> &Path16,
--                          size_t MaxPathLen = MAX_PATH);
--
--} // end namespace windows
- } // end namespace sys
- } // end namespace llvm.
- 
-diff --git a/llvm/lib/Support/ConvertUTF.cpp b/llvm/lib/Support/ConvertUTF.cpp
-index e24a918c5..c906ded91 100644
---- a/llvm/lib/Support/ConvertUTF.cpp
-+++ b/llvm/lib/Support/ConvertUTF.cpp
-@@ -51,6 +51,11 @@
- #endif
- #include <assert.h>
- 
-+#ifdef _WIN32
-+#include "wpi/WindowsError.h"
-+#include "Windows/WindowsSupport.h"
-+#endif
-+
- /*
-  * This code extensively uses fall-through switches.
-  * Keep the compiler from warning about that.
-@@ -733,6 +738,96 @@ ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart,
- 
-    --------------------------------------------------------------------- */
- 
-+#ifdef _WIN32
-+
-+namespace sys {
-+namespace windows {
-+std::error_code CodePageToUTF16(unsigned codepage,
-+                                std::string_view original,
-+                                wpi::SmallVectorImpl<wchar_t> &utf16) {
-+  if (!original.empty()) {
-+    int len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.data(),
-+                                    original.size(), utf16.begin(), 0);
-+
-+    if (len == 0) {
-+      return mapWindowsError(::GetLastError());
-+    }
-+
-+    utf16.reserve(len + 1);
-+    utf16.resize_for_overwrite(len);
-+
-+    len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.data(),
-+                                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(std::string_view utf8,
-+                            wpi::SmallVectorImpl<wchar_t> &utf16) {
-+  return CodePageToUTF16(CP_UTF8, utf8, utf16);
-+}
-+
-+std::error_code CurCPToUTF16(std::string_view 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.resize_for_overwrite(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
-+
-+#endif  // _WIN32
-+
- } // namespace llvm
- 
- ConvertUTF_RESTORE_WARNINGS
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0020-Prefer-fmtlib.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0020-Prefer-fmtlib.patch
deleted file mode 100644
index 4315754..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0020-Prefer-fmtlib.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From 8834260a9ee172311cc08d0b4e4e958bf36a260f Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sun, 8 May 2022 16:46:20 -0400
-Subject: [PATCH 20/28] Prefer fmtlib
-
----
- llvm/lib/Support/ErrorHandling.cpp | 20 ++++++--------------
- 1 file changed, 6 insertions(+), 14 deletions(-)
-
-diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
-index ec1a1633a..8de7b726d 100644
---- a/llvm/lib/Support/ErrorHandling.cpp
-+++ b/llvm/lib/Support/ErrorHandling.cpp
-@@ -22,7 +22,7 @@
- #include "llvm/Support/Signals.h"
- #include "llvm/Support/Threading.h"
- #include "llvm/Support/WindowsError.h"
--#include "llvm/Support/raw_ostream.h"
-+#include "fmt/format.h"
- #include <cassert>
- #include <cstdlib>
- #include <mutex>
-@@ -93,15 +93,7 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
-   if (handler) {
-     handler(handlerData, std::string{Reason}.c_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";
--    std::string_view MessageStr = OS.str();
--    ssize_t written = ::write(2, MessageStr.data(), MessageStr.size());
--    (void)written; // If something went wrong, we deliberately just give up.
-+    fmt::print(stderr, "LLVM ERROR: {}\n", Reason);
-   }
- 
-   // If we reached here, we are failing ungracefully. Run the interrupt handlers
-@@ -173,11 +165,11 @@ void llvm::llvm_unreachable_internal(const char *msg, const char *file,
-   // llvm_unreachable is intended to be used to indicate "impossible"
-   // situations, and not legitimate runtime errors.
-   if (msg)
--    dbgs() << msg << "\n";
--  dbgs() << "UNREACHABLE executed";
-+    fmt::print(stderr, "{}\n", msg);
-+  std::fputs("UNREACHABLE executed", stderr);
-   if (file)
--    dbgs() << " at " << file << ":" << line;
--  dbgs() << "!\n";
-+    fmt::print(stderr, " at {}:{}", file, line);
-+  fmt::print(stderr, "!\n");
-   abort();
- #ifdef LLVM_BUILTIN_UNREACHABLE
-   // Windows systems and possibly others don't declare abort() to be noreturn,
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0020-Prefer-wpi-s-fs.h.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0020-Prefer-wpi-s-fs.h.patch
new file mode 100644
index 0000000..18ae0e7
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0020-Prefer-wpi-s-fs.h.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sun, 8 May 2022 16:49:36 -0400
+Subject: [PATCH 20/31] Prefer wpi's fs.h
+
+---
+ llvm/include/llvm/Support/raw_ostream.h | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
+index e7526e016a858ad728feb7cf1c5014b9691759d4..d56999186f719f8d91f3a047a19960caf62a066c 100644
+--- a/llvm/include/llvm/Support/raw_ostream.h
++++ b/llvm/include/llvm/Support/raw_ostream.h
+@@ -26,18 +26,15 @@
+ #include <type_traits>
+ #include <vector>
+ 
+-namespace llvm {
+-
+-template <class T> class [[nodiscard]] Expected;
+ 
+-namespace sys {
+ namespace fs {
+ enum FileAccess : unsigned;
+ enum OpenFlags : unsigned;
+ enum CreationDisposition : unsigned;
+ class FileLocker;
+ } // end namespace fs
+-} // end namespace sys
++
++namespace llvm {
+ 
+ /// This class implements an extremely fast bulk output stream that can *only*
+ /// output to a stream.  It does not support seeking, reopening, rewinding, line
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0021-Prefer-wpi-s-fs.h.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0021-Prefer-wpi-s-fs.h.patch
deleted file mode 100644
index 2f246a0..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0021-Prefer-wpi-s-fs.h.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 7943842aea1a05a1dd2c2c753378af569c24293b Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sun, 8 May 2022 16:49:36 -0400
-Subject: [PATCH 21/28] Prefer wpi's fs.h
-
----
- llvm/include/llvm/Support/raw_ostream.h | 7 ++-----
- 1 file changed, 2 insertions(+), 5 deletions(-)
-
-diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
-index bf5630ab5..256bcec25 100644
---- a/llvm/include/llvm/Support/raw_ostream.h
-+++ b/llvm/include/llvm/Support/raw_ostream.h
-@@ -27,18 +27,15 @@
- #include <type_traits>
- #include <vector>
- 
--namespace llvm {
--
--template <class T> class LLVM_NODISCARD Expected;
- 
--namespace sys {
- namespace fs {
- enum FileAccess : unsigned;
- enum OpenFlags : unsigned;
- enum CreationDisposition : unsigned;
- class FileLocker;
- } // end namespace fs
--} // end namespace sys
-+
-+namespace llvm {
- 
- /// This class implements an extremely fast bulk output stream that can *only*
- /// output to a stream.  It does not support seeking, reopening, rewinding, line
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0021-Remove-unused-functions.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0021-Remove-unused-functions.patch
new file mode 100644
index 0000000..4f6ba93
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0021-Remove-unused-functions.patch
@@ -0,0 +1,259 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sun, 8 May 2022 19:16:51 -0400
+Subject: [PATCH 21/31] Remove unused functions
+
+---
+ llvm/include/llvm/Support/raw_ostream.h |  5 +-
+ llvm/lib/Support/ErrorHandling.cpp      | 16 -----
+ llvm/lib/Support/raw_ostream.cpp        | 47 +++++++-------
+ llvm/unittests/ADT/SmallStringTest.cpp  | 81 -------------------------
+ 4 files changed, 23 insertions(+), 126 deletions(-)
+
+diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
+index d56999186f719f8d91f3a047a19960caf62a066c..9a9a1f688313a5784a58a70f2cb4cc0d6ec70e79 100644
+--- a/llvm/include/llvm/Support/raw_ostream.h
++++ b/llvm/include/llvm/Support/raw_ostream.h
+@@ -70,7 +70,6 @@ private:
+   /// for a \see write_impl() call to handle the data which has been put into
+   /// this buffer.
+   char *OutBufStart, *OutBufEnd, *OutBufCur;
+-  bool ColorEnabled = false;
+ 
+   /// Optional stream this stream is tied to. If this stream is written to, the
+   /// tied-to stream will be flushed first.
+@@ -317,9 +316,9 @@ public:
+ 
+   // Enable or disable colors. Once enable_colors(false) is called,
+   // changeColor() has no effect until enable_colors(true) is called.
+-  virtual void enable_colors(bool enable) { ColorEnabled = enable; }
++  virtual void enable_colors(bool /*enable*/) {}
+ 
+-  bool colors_enabled() const { return ColorEnabled; }
++  bool colors_enabled() const { return false; }
+ 
+   /// Tie this stream to the specified stream. Replaces any existing tied-to
+   /// stream. Specifying a nullptr unties the stream.
+diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
+index 54137a331ca9e752b02c0f16ae996094a6f2fafa..e253d6f7a5ca3aee75823efdb9717dcd93fff5dc 100644
+--- a/llvm/lib/Support/ErrorHandling.cpp
++++ b/llvm/lib/Support/ErrorHandling.cpp
+@@ -181,22 +181,6 @@ void llvm::llvm_unreachable_internal(const char *msg, const char *file,
+ #endif
+ }
+ 
+-static void bindingsErrorHandler(void *user_data, const char *reason,
+-                                 bool gen_crash_diag) {
+-  LLVMFatalErrorHandler handler =
+-      LLVM_EXTENSION reinterpret_cast<LLVMFatalErrorHandler>(user_data);
+-  handler(reason);
+-}
+-
+-void LLVMInstallFatalErrorHandler(LLVMFatalErrorHandler Handler) {
+-  install_fatal_error_handler(bindingsErrorHandler,
+-                              LLVM_EXTENSION reinterpret_cast<void *>(Handler));
+-}
+-
+-void LLVMResetFatalErrorHandler() {
+-  remove_fatal_error_handler();
+-}
+-
+ #ifdef _WIN32
+ 
+ #include <winerror.h>
+diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
+index 4769d34a14f3f2cbaaa4df50ea7111fe9fa2792f..f9928ac969932b6baea60a80750477d78b6a5b02 100644
+--- a/llvm/lib/Support/raw_ostream.cpp
++++ b/llvm/lib/Support/raw_ostream.cpp
+@@ -175,16 +175,6 @@ raw_ostream &raw_ostream::write_escaped(std::string_view Str,
+   return *this;
+ }
+ 
+-raw_ostream &raw_ostream::operator<<(const void *P) {
+-  llvm::write_hex(*this, (uintptr_t)P, HexPrintStyle::PrefixLower);
+-  return *this;
+-}
+-
+-raw_ostream &raw_ostream::operator<<(double N) {
+-  llvm::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;
+@@ -327,15 +317,22 @@ static int getFD(std::string_view Filename, std::error_code &EC,
+   if (Filename == "-") {
+     EC = std::error_code();
+     // Change stdout's text/binary mode based on the Flags.
+-    sys::ChangeStdoutMode(Flags);
++    if (!(Flags & 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);
++  fs::file_t F;
++  if (Access & fs::FA_Read)
++    F = fs::OpenFileForReadWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags);
+   else
+-    EC = sys::fs::openFileForWrite(Filename, FD, Disp, Flags);
++    F = fs::OpenFileForWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags);
++  if (EC)
++    return -1;
++  int FD = fs::FileToFd(F, EC, Flags);
+   if (EC)
+     return -1;
+ 
+@@ -395,12 +392,8 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered,
+ 
+   // Get the starting position.
+   off_t loc = ::lseek(FD, 0, SEEK_CUR);
+-  sys::fs::file_status Status;
+-  std::error_code EC = status(FD, Status);
+-  IsRegularFile = Status.type() == sys::fs::file_type::regular_file;
+ #ifdef _WIN32
+-  // MSVCRT's _lseek(SEEK_CUR) doesn't return -1 for pipes.
+-  SupportsSeeking = !EC && IsRegularFile;
++  SupportsSeeking = loc != (off_t)-1 && ::GetFileType(reinterpret_cast<HANDLE>(::_get_osfhandle(FD))) != FILE_TYPE_PIPE;
+ #else
+   SupportsSeeking = !EC && loc != (off_t)-1;
+ #endif
+@@ -413,10 +406,8 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered,
+ raw_fd_ostream::~raw_fd_ostream() {
+   if (FD >= 0) {
+     flush();
+-    if (ShouldClose) {
+-      if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
+-        error_detected(EC);
+-    }
++    if (ShouldClose && ::close(FD) < 0)
++      error_detected(std::error_code(errno, std::generic_category()));
+   }
+ 
+ #ifdef __MINGW32__
+@@ -511,7 +502,11 @@ void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
+ 
+   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.
+@@ -554,8 +549,8 @@ void raw_fd_ostream::close() {
+   assert(ShouldClose);
+   ShouldClose = false;
+   flush();
+-  if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
+-    error_detected(EC);
++  if (::close(FD) < 0)
++    error_detected(std::error_code(errno, std::generic_category()));
+   FD = -1;
+ }
+ 
+diff --git a/llvm/unittests/ADT/SmallStringTest.cpp b/llvm/unittests/ADT/SmallStringTest.cpp
+index 6cf14700b34739420cd3dc4ff8a4c16ce162f715..87600ea4704bc98acab9085d16cfafd3d586714e 100644
+--- a/llvm/unittests/ADT/SmallStringTest.cpp
++++ b/llvm/unittests/ADT/SmallStringTest.cpp
+@@ -129,23 +129,6 @@ TEST_F(SmallStringTest, StdStringConversion) {
+   EXPECT_EQ("abc", theStdString);
+ }
+ 
+-TEST_F(SmallStringTest, Substr) {
+-  theString = "hello";
+-  EXPECT_EQ("lo", theString.substr(3));
+-  EXPECT_EQ("", theString.substr(100));
+-  EXPECT_EQ("hello", theString.substr(0, 100));
+-  EXPECT_EQ("o", theString.substr(4, 10));
+-}
+-
+-TEST_F(SmallStringTest, Slice) {
+-  theString = "hello";
+-  EXPECT_EQ("l", theString.slice(2, 3));
+-  EXPECT_EQ("ell", theString.slice(1, 4));
+-  EXPECT_EQ("llo", theString.slice(2, 100));
+-  EXPECT_EQ("", theString.slice(2, 1));
+-  EXPECT_EQ("", theString.slice(10, 20));
+-}
+-
+ TEST_F(SmallStringTest, Find) {
+   theString = "hello";
+   EXPECT_EQ(2U, theString.find('l'));
+@@ -180,68 +163,4 @@ TEST_F(SmallStringTest, Find) {
+   EXPECT_EQ(0U, theString.find(""));
+ }
+ 
+-TEST_F(SmallStringTest, Count) {
+-  theString = "hello";
+-  EXPECT_EQ(2U, theString.count('l'));
+-  EXPECT_EQ(1U, theString.count('o'));
+-  EXPECT_EQ(0U, theString.count('z'));
+-  EXPECT_EQ(0U, theString.count("helloworld"));
+-  EXPECT_EQ(1U, theString.count("hello"));
+-  EXPECT_EQ(1U, theString.count("ello"));
+-  EXPECT_EQ(0U, theString.count("zz"));
+-}
+-
+-TEST_F(SmallStringTest, Realloc) {
+-  theString = "abcd";
+-  theString.reserve(100);
+-  EXPECT_EQ("abcd", theString);
+-  unsigned const N = 100000;
+-  theString.reserve(N);
+-  for (unsigned i = 0; i < N - 4; ++i)
+-    theString.push_back('y');
+-  EXPECT_EQ("abcdyyy", theString.slice(0, 7));
+-}
+-
+-TEST_F(SmallStringTest, Comparisons) {
+-  EXPECT_GT( 0, SmallString<10>("aab").compare("aad"));
+-  EXPECT_EQ( 0, SmallString<10>("aab").compare("aab"));
+-  EXPECT_LT( 0, SmallString<10>("aab").compare("aaa"));
+-  EXPECT_GT( 0, SmallString<10>("aab").compare("aabb"));
+-  EXPECT_LT( 0, SmallString<10>("aab").compare("aa"));
+-  EXPECT_LT( 0, SmallString<10>("\xFF").compare("\1"));
+-
+-  EXPECT_EQ(-1, SmallString<10>("AaB").compare_insensitive("aAd"));
+-  EXPECT_EQ( 0, SmallString<10>("AaB").compare_insensitive("aab"));
+-  EXPECT_EQ( 1, SmallString<10>("AaB").compare_insensitive("AAA"));
+-  EXPECT_EQ(-1, SmallString<10>("AaB").compare_insensitive("aaBb"));
+-  EXPECT_EQ( 1, SmallString<10>("AaB").compare_insensitive("aA"));
+-  EXPECT_EQ( 1, SmallString<10>("\xFF").compare_insensitive("\1"));
+-
+-  EXPECT_EQ(-1, SmallString<10>("aab").compare_numeric("aad"));
+-  EXPECT_EQ( 0, SmallString<10>("aab").compare_numeric("aab"));
+-  EXPECT_EQ( 1, SmallString<10>("aab").compare_numeric("aaa"));
+-  EXPECT_EQ(-1, SmallString<10>("aab").compare_numeric("aabb"));
+-  EXPECT_EQ( 1, SmallString<10>("aab").compare_numeric("aa"));
+-  EXPECT_EQ(-1, SmallString<10>("1").compare_numeric("10"));
+-  EXPECT_EQ( 0, SmallString<10>("10").compare_numeric("10"));
+-  EXPECT_EQ( 0, SmallString<10>("10a").compare_numeric("10a"));
+-  EXPECT_EQ( 1, SmallString<10>("2").compare_numeric("1"));
+-  EXPECT_EQ( 0, SmallString<10>("llvm_v1i64_ty").compare_numeric("llvm_v1i64_ty"));
+-  EXPECT_EQ( 1, SmallString<10>("\xFF").compare_numeric("\1"));
+-  EXPECT_EQ( 1, SmallString<10>("V16").compare_numeric("V1_q0"));
+-  EXPECT_EQ(-1, SmallString<10>("V1_q0").compare_numeric("V16"));
+-  EXPECT_EQ(-1, SmallString<10>("V8_q0").compare_numeric("V16"));
+-  EXPECT_EQ( 1, SmallString<10>("V16").compare_numeric("V8_q0"));
+-  EXPECT_EQ(-1, SmallString<10>("V1_q0").compare_numeric("V8_q0"));
+-  EXPECT_EQ( 1, SmallString<10>("V8_q0").compare_numeric("V1_q0"));
+-}
+-
+-// Check gtest prints SmallString as a string instead of a container of chars.
+-// The code is in utils/unittest/googletest/internal/custom/gtest-printers.h
+-TEST_F(SmallStringTest, GTestPrinter) {
+-  EXPECT_EQ(R"("foo")", ::testing::PrintToString(SmallString<1>("foo")));
+-  const SmallVectorImpl<char> &ErasedSmallString = SmallString<1>("foo");
+-  EXPECT_EQ(R"("foo")", ::testing::PrintToString(ErasedSmallString));
+-}
+-
+ } // namespace
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0022-OS-specific-changes.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0022-OS-specific-changes.patch
new file mode 100644
index 0000000..4b3b3b7
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0022-OS-specific-changes.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Sun, 8 May 2022 19:30:43 -0400
+Subject: [PATCH 22/31] OS-specific changes
+
+---
+ llvm/lib/Support/ErrorHandling.cpp | 16 +++++++---------
+ 1 file changed, 7 insertions(+), 9 deletions(-)
+
+diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
+index e253d6f7a5ca3aee75823efdb9717dcd93fff5dc..5c08e469e2e44b27e69f4aa974bc59deb8217c9b 100644
+--- a/llvm/lib/Support/ErrorHandling.cpp
++++ b/llvm/lib/Support/ErrorHandling.cpp
+@@ -96,15 +96,7 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
+     fmt::print(stderr, "LLVM ERROR: {}\n", Reason);
+   }
+ 
+-  // If we reached here, we are failing ungracefully. Run the interrupt handlers
+-  // to make sure any special cleanups get done, in particular that we remove
+-  // files registered with RemoveFileOnSignal.
+-  sys::RunInterruptHandlers();
+-
+-  if (GenCrashDiag)
+-    abort();
+-  else
+-    exit(1);
++  exit(1);
+ }
+ 
+ void llvm::install_bad_alloc_error_handler(fatal_error_handler_t handler,
+@@ -141,9 +133,15 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
+   // an OOM to stderr and abort.
+   const char *OOMMessage = "LLVM ERROR: out of memory\n";
+   const char *Newline = "\n";
++#ifdef _WIN32
++  (void)!::_write(2, OOMMessage, strlen(OOMMessage));
++  (void)!::_write(2, Reason, strlen(Reason));
++  (void)!::_write(2, Newline, strlen(Newline));
++#else
+   (void)!::write(2, OOMMessage, strlen(OOMMessage));
+   (void)!::write(2, Reason, strlen(Reason));
+   (void)!::write(2, Newline, strlen(Newline));
++#endif
+   abort();
+ }
+ 
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0022-Remove-unused-functions.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0022-Remove-unused-functions.patch
deleted file mode 100644
index 244148e..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0022-Remove-unused-functions.patch
+++ /dev/null
@@ -1,283 +0,0 @@
-From a172c93df929ac6c9f783fd5af1e4df3604c4c66 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sun, 8 May 2022 19:16:51 -0400
-Subject: [PATCH 22/28] Remove unused functions
-
----
- llvm/include/llvm/Support/DJB.h         |  3 -
- llvm/include/llvm/Support/raw_ostream.h |  5 +-
- llvm/lib/Support/ErrorHandling.cpp      | 16 -----
- llvm/lib/Support/raw_ostream.cpp        | 49 +++++++--------
- llvm/unittests/ADT/SmallStringTest.cpp  | 81 -------------------------
- 5 files changed, 23 insertions(+), 131 deletions(-)
-
-diff --git a/llvm/include/llvm/Support/DJB.h b/llvm/include/llvm/Support/DJB.h
-index 8737cd144..67b0ae91b 100644
---- a/llvm/include/llvm/Support/DJB.h
-+++ b/llvm/include/llvm/Support/DJB.h
-@@ -24,9 +24,6 @@ inline uint32_t djbHash(std::string_view Buffer, uint32_t H = 5381) {
-   return H;
- }
- 
--/// Computes the Bernstein hash after folding the input according to the Dwarf 5
--/// standard case folding rules.
--uint32_t caseFoldingDjbHash(StringRef Buffer, uint32_t H = 5381);
- } // namespace llvm
- 
- #endif // LLVM_SUPPORT_DJB_H
-diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
-index 256bcec25..9b3a87e1b 100644
---- a/llvm/include/llvm/Support/raw_ostream.h
-+++ b/llvm/include/llvm/Support/raw_ostream.h
-@@ -71,7 +71,6 @@ private:
-   /// for a \see write_impl() call to handle the data which has been put into
-   /// this buffer.
-   char *OutBufStart, *OutBufEnd, *OutBufCur;
--  bool ColorEnabled = false;
- 
-   /// Optional stream this stream is tied to. If this stream is written to, the
-   /// tied-to stream will be flushed first.
-@@ -304,9 +303,9 @@ public:
- 
-   // Enable or disable colors. Once enable_colors(false) is called,
-   // changeColor() has no effect until enable_colors(true) is called.
--  virtual void enable_colors(bool enable) { ColorEnabled = enable; }
-+  virtual void enable_colors(bool /*enable*/) {}
- 
--  bool colors_enabled() const { return ColorEnabled; }
-+  bool colors_enabled() const { return false; }
- 
-   /// Tie this stream to the specified stream. Replaces any existing tied-to
-   /// stream. Specifying a nullptr unties the stream.
-diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
-index 8de7b726d..bc08199a1 100644
---- a/llvm/lib/Support/ErrorHandling.cpp
-+++ b/llvm/lib/Support/ErrorHandling.cpp
-@@ -178,22 +178,6 @@ void llvm::llvm_unreachable_internal(const char *msg, const char *file,
- #endif
- }
- 
--static void bindingsErrorHandler(void *user_data, const char *reason,
--                                 bool gen_crash_diag) {
--  LLVMFatalErrorHandler handler =
--      LLVM_EXTENSION reinterpret_cast<LLVMFatalErrorHandler>(user_data);
--  handler(reason);
--}
--
--void LLVMInstallFatalErrorHandler(LLVMFatalErrorHandler Handler) {
--  install_fatal_error_handler(bindingsErrorHandler,
--                              LLVM_EXTENSION reinterpret_cast<void *>(Handler));
--}
--
--void LLVMResetFatalErrorHandler() {
--  remove_fatal_error_handler();
--}
--
- #ifdef _WIN32
- 
- #include <winerror.h>
-diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
-index 878a3a5e9..632b52235 100644
---- a/llvm/lib/Support/raw_ostream.cpp
-+++ b/llvm/lib/Support/raw_ostream.cpp
-@@ -167,16 +167,6 @@ raw_ostream &raw_ostream::write_escaped(std::string_view Str,
-   return *this;
- }
- 
--raw_ostream &raw_ostream::operator<<(const void *P) {
--  llvm::write_hex(*this, (uintptr_t)P, HexPrintStyle::PrefixLower);
--  return *this;
--}
--
--raw_ostream &raw_ostream::operator<<(double N) {
--  llvm::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;
-@@ -321,15 +311,22 @@ static int getFD(std::string_view Filename, std::error_code &EC,
-   if (Filename == "-") {
-     EC = std::error_code();
-     // Change stdout's text/binary mode based on the Flags.
--    sys::ChangeStdoutMode(Flags);
-+    if (!(Flags & 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);
-+  fs::file_t F;
-+  if (Access & fs::FA_Read)
-+    F = fs::OpenFileForReadWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags);
-   else
--    EC = sys::fs::openFileForWrite(Filename, FD, Disp, Flags);
-+    F = fs::OpenFileForWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags);
-+  if (EC)
-+    return -1;
-+  int FD = fs::FileToFd(F, EC, Flags);
-   if (EC)
-     return -1;
- 
-@@ -389,12 +386,8 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered,
- 
-   // Get the starting position.
-   off_t loc = ::lseek(FD, 0, SEEK_CUR);
--  sys::fs::file_status Status;
--  std::error_code EC = status(FD, Status);
--  IsRegularFile = Status.type() == sys::fs::file_type::regular_file;
- #ifdef _WIN32
--  // MSVCRT's _lseek(SEEK_CUR) doesn't return -1 for pipes.
--  SupportsSeeking = !EC && IsRegularFile;
-+  SupportsSeeking = loc != (off_t)-1 && ::GetFileType(reinterpret_cast<HANDLE>(::_get_osfhandle(FD))) != FILE_TYPE_PIPE;
- #else
-   SupportsSeeking = !EC && loc != (off_t)-1;
- #endif
-@@ -407,10 +400,8 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered,
- raw_fd_ostream::~raw_fd_ostream() {
-   if (FD >= 0) {
-     flush();
--    if (ShouldClose) {
--      if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
--        error_detected(EC);
--    }
-+    if (ShouldClose && ::close(FD) < 0)
-+      error_detected(std::error_code(errno, std::generic_category()));
-   }
- 
- #ifdef __MINGW32__
-@@ -505,7 +496,11 @@ void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
- 
-   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.
-@@ -540,8 +535,8 @@ void raw_fd_ostream::close() {
-   assert(ShouldClose);
-   ShouldClose = false;
-   flush();
--  if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
--    error_detected(EC);
-+  if (::close(FD) < 0)
-+    error_detected(std::error_code(errno, std::generic_category()));
-   FD = -1;
- }
- 
-@@ -550,8 +545,6 @@ uint64_t raw_fd_ostream::seek(uint64_t off) {
-   flush();
- #ifdef _WIN32
-   pos = ::_lseeki64(FD, off, SEEK_SET);
--#elif defined(HAVE_LSEEK64)
--  pos = ::lseek64(FD, off, SEEK_SET);
- #else
-   pos = ::lseek(FD, off, SEEK_SET);
- #endif
-diff --git a/llvm/unittests/ADT/SmallStringTest.cpp b/llvm/unittests/ADT/SmallStringTest.cpp
-index bee3875d1..87600ea47 100644
---- a/llvm/unittests/ADT/SmallStringTest.cpp
-+++ b/llvm/unittests/ADT/SmallStringTest.cpp
-@@ -129,23 +129,6 @@ TEST_F(SmallStringTest, StdStringConversion) {
-   EXPECT_EQ("abc", theStdString);
- }
- 
--TEST_F(SmallStringTest, Substr) {
--  theString = "hello";
--  EXPECT_EQ("lo", theString.substr(3));
--  EXPECT_EQ("", theString.substr(100));
--  EXPECT_EQ("hello", theString.substr(0, 100));
--  EXPECT_EQ("o", theString.substr(4, 10));
--}
--
--TEST_F(SmallStringTest, Slice) {
--  theString = "hello";
--  EXPECT_EQ("l", theString.slice(2, 3));
--  EXPECT_EQ("ell", theString.slice(1, 4));
--  EXPECT_EQ("llo", theString.slice(2, 100));
--  EXPECT_EQ("", theString.slice(2, 1));
--  EXPECT_EQ("", theString.slice(10, 20));
--}
--
- TEST_F(SmallStringTest, Find) {
-   theString = "hello";
-   EXPECT_EQ(2U, theString.find('l'));
-@@ -180,68 +163,4 @@ TEST_F(SmallStringTest, Find) {
-   EXPECT_EQ(0U, theString.find(""));
- }
- 
--TEST_F(SmallStringTest, Count) {
--  theString = "hello";
--  EXPECT_EQ(2U, theString.count('l'));
--  EXPECT_EQ(1U, theString.count('o'));
--  EXPECT_EQ(0U, theString.count('z'));
--  EXPECT_EQ(0U, theString.count("helloworld"));
--  EXPECT_EQ(1U, theString.count("hello"));
--  EXPECT_EQ(1U, theString.count("ello"));
--  EXPECT_EQ(0U, theString.count("zz"));
--}
--
--TEST_F(SmallStringTest, Realloc) {
--  theString = "abcd";
--  theString.reserve(100);
--  EXPECT_EQ("abcd", theString);
--  unsigned const N = 100000;
--  theString.reserve(N);
--  for (unsigned i = 0; i < N - 4; ++i)
--    theString.push_back('y');
--  EXPECT_EQ("abcdyyy", theString.slice(0, 7));
--}
--
--TEST_F(SmallStringTest, Comparisons) {
--  EXPECT_EQ(-1, SmallString<10>("aab").compare("aad"));
--  EXPECT_EQ( 0, SmallString<10>("aab").compare("aab"));
--  EXPECT_EQ( 1, SmallString<10>("aab").compare("aaa"));
--  EXPECT_EQ(-1, SmallString<10>("aab").compare("aabb"));
--  EXPECT_EQ( 1, SmallString<10>("aab").compare("aa"));
--  EXPECT_EQ( 1, SmallString<10>("\xFF").compare("\1"));
--
--  EXPECT_EQ(-1, SmallString<10>("AaB").compare_insensitive("aAd"));
--  EXPECT_EQ( 0, SmallString<10>("AaB").compare_insensitive("aab"));
--  EXPECT_EQ( 1, SmallString<10>("AaB").compare_insensitive("AAA"));
--  EXPECT_EQ(-1, SmallString<10>("AaB").compare_insensitive("aaBb"));
--  EXPECT_EQ( 1, SmallString<10>("AaB").compare_insensitive("aA"));
--  EXPECT_EQ( 1, SmallString<10>("\xFF").compare_insensitive("\1"));
--
--  EXPECT_EQ(-1, SmallString<10>("aab").compare_numeric("aad"));
--  EXPECT_EQ( 0, SmallString<10>("aab").compare_numeric("aab"));
--  EXPECT_EQ( 1, SmallString<10>("aab").compare_numeric("aaa"));
--  EXPECT_EQ(-1, SmallString<10>("aab").compare_numeric("aabb"));
--  EXPECT_EQ( 1, SmallString<10>("aab").compare_numeric("aa"));
--  EXPECT_EQ(-1, SmallString<10>("1").compare_numeric("10"));
--  EXPECT_EQ( 0, SmallString<10>("10").compare_numeric("10"));
--  EXPECT_EQ( 0, SmallString<10>("10a").compare_numeric("10a"));
--  EXPECT_EQ( 1, SmallString<10>("2").compare_numeric("1"));
--  EXPECT_EQ( 0, SmallString<10>("llvm_v1i64_ty").compare_numeric("llvm_v1i64_ty"));
--  EXPECT_EQ( 1, SmallString<10>("\xFF").compare_numeric("\1"));
--  EXPECT_EQ( 1, SmallString<10>("V16").compare_numeric("V1_q0"));
--  EXPECT_EQ(-1, SmallString<10>("V1_q0").compare_numeric("V16"));
--  EXPECT_EQ(-1, SmallString<10>("V8_q0").compare_numeric("V16"));
--  EXPECT_EQ( 1, SmallString<10>("V16").compare_numeric("V8_q0"));
--  EXPECT_EQ(-1, SmallString<10>("V1_q0").compare_numeric("V8_q0"));
--  EXPECT_EQ( 1, SmallString<10>("V8_q0").compare_numeric("V1_q0"));
--}
--
--// Check gtest prints SmallString as a string instead of a container of chars.
--// The code is in utils/unittest/googletest/internal/custom/gtest-printers.h
--TEST_F(SmallStringTest, GTestPrinter) {
--  EXPECT_EQ(R"("foo")", ::testing::PrintToString(SmallString<1>("foo")));
--  const SmallVectorImpl<char> &ErasedSmallString = SmallString<1>("foo");
--  EXPECT_EQ(R"("foo")", ::testing::PrintToString(ErasedSmallString));
--}
--
- } // namespace
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0023-OS-specific-changes.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0023-OS-specific-changes.patch
deleted file mode 100644
index ed58dac..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0023-OS-specific-changes.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From ae08bb29b4d2a8ea15a4b82b909c0f4aee5e9060 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sun, 8 May 2022 19:30:43 -0400
-Subject: [PATCH 23/28] OS-specific changes
-
----
- llvm/lib/Support/ErrorHandling.cpp | 13 +++++++------
- 1 file changed, 7 insertions(+), 6 deletions(-)
-
-diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp
-index bc08199a1..839819094 100644
---- a/llvm/lib/Support/ErrorHandling.cpp
-+++ b/llvm/lib/Support/ErrorHandling.cpp
-@@ -96,12 +96,7 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
-     fmt::print(stderr, "LLVM ERROR: {}\n", Reason);
-   }
- 
--  // If we reached here, we are failing ungracefully. Run the interrupt handlers
--  // to make sure any special cleanups get done, in particular that we remove
--  // files registered with RemoveFileOnSignal.
--  sys::RunInterruptHandlers();
--
--  abort();
-+  exit(1);
- }
- 
- void llvm::install_bad_alloc_error_handler(fatal_error_handler_t handler,
-@@ -138,9 +133,15 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
-   // an OOM to stderr and abort.
-   const char *OOMMessage = "LLVM ERROR: out of memory\n";
-   const char *Newline = "\n";
-+#ifdef _WIN32
-+  (void)!::_write(2, OOMMessage, strlen(OOMMessage));
-+  (void)!::_write(2, Reason, strlen(Reason));
-+  (void)!::_write(2, Newline, strlen(Newline));
-+#else
-   (void)!::write(2, OOMMessage, strlen(OOMMessage));
-   (void)!::write(2, Reason, strlen(Reason));
-   (void)!::write(2, Newline, strlen(Newline));
-+#endif
-   abort();
- }
- 
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0023-Use-SmallVector-for-UTF-conversion.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0023-Use-SmallVector-for-UTF-conversion.patch
new file mode 100644
index 0000000..d04e93b
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0023-Use-SmallVector-for-UTF-conversion.patch
@@ -0,0 +1,154 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Mon, 9 May 2022 00:04:30 -0400
+Subject: [PATCH 23/31] Use SmallVector for UTF conversion
+
+---
+ llvm/include/llvm/Support/ConvertUTF.h    |  6 +++---
+ llvm/lib/Support/ConvertUTFWrapper.cpp    |  6 +++---
+ llvm/unittests/Support/ConvertUTFTest.cpp | 22 +++++++++++-----------
+ 3 files changed, 17 insertions(+), 17 deletions(-)
+
+diff --git a/llvm/include/llvm/Support/ConvertUTF.h b/llvm/include/llvm/Support/ConvertUTF.h
+index 72321022beb373945f7935ed72944fd68eb7d02f..5c8b966ce296699a0315d72cdfdcdb5af3d1d0b0 100644
+--- a/llvm/include/llvm/Support/ConvertUTF.h
++++ b/llvm/include/llvm/Support/ConvertUTF.h
+@@ -233,7 +233,7 @@ bool ConvertUTF8toWide(const char *Source, std::wstring &Result);
+ * Converts a std::wstring to a UTF-8 encoded std::string.
+ * \return true on success.
+ */
+-bool convertWideToUTF8(const std::wstring &Source, std::string &Result);
++bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result);
+ 
+ 
+ /**
+@@ -288,7 +288,7 @@ bool hasUTF16ByteOrderMark(span<const char> SrcBytes);
+  * \param [out] Out Converted UTF-8 is stored here on success.
+  * \returns true on success
+  */
+-bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out);
++bool convertUTF16ToUTF8String(span<const char> SrcBytes, SmallVectorImpl<char> &Out);
+ 
+ /**
+ * Converts a UTF16 string into a UTF8 std::string.
+@@ -297,7 +297,7 @@ bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out);
+ * \param [out] Out Converted UTF-8 is stored here on success.
+ * \returns true on success
+ */
+-bool convertUTF16ToUTF8String(span<const UTF16> Src, std::string &Out);
++bool convertUTF16ToUTF8String(span<const UTF16> Src, SmallVectorImpl<char> &Out);
+ 
+ /**
+  * Converts a stream of raw bytes assumed to be UTF32 into a UTF8 std::string.
+diff --git a/llvm/lib/Support/ConvertUTFWrapper.cpp b/llvm/lib/Support/ConvertUTFWrapper.cpp
+index 34054140489e4d536ace4650207c783d669d850e..0b62315e3461ff60a8313e73b4142b1f83e36ca7 100644
+--- a/llvm/lib/Support/ConvertUTFWrapper.cpp
++++ b/llvm/lib/Support/ConvertUTFWrapper.cpp
+@@ -82,7 +82,7 @@ bool hasUTF16ByteOrderMark(span<const char> S) {
+                             (S[0] == '\xfe' && S[1] == '\xff')));
+ }
+ 
+-bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out) {
++bool convertUTF16ToUTF8String(span<const char> SrcBytes, SmallVectorImpl<char> &Out) {
+   assert(Out.empty());
+ 
+   // Error out on an uneven byte count.
+@@ -133,7 +133,7 @@ bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out) {
+   return true;
+ }
+ 
+-bool convertUTF16ToUTF8String(span<const UTF16> Src, std::string &Out) {
++bool convertUTF16ToUTF8String(span<const UTF16> Src, SmallVectorImpl<char> &Out) {
+   return convertUTF16ToUTF8String(
+       span<const char>(reinterpret_cast<const char *>(Src.data()),
+                            Src.size() * sizeof(UTF16)),
+@@ -269,7 +269,7 @@ bool ConvertUTF8toWide(const char *Source, std::wstring &Result) {
+   return ConvertUTF8toWide(std::string_view(Source), Result);
+ }
+ 
+-bool convertWideToUTF8(const std::wstring &Source, std::string &Result) {
++bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result) {
+   if (sizeof(wchar_t) == 1) {
+     const UTF8 *Start = reinterpret_cast<const UTF8 *>(Source.data());
+     const UTF8 *End =
+diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp
+index 77e70a46d3621ecfaed923d87256184addfda721..eb17a06c4369c9486c57b61f519a7429d9ef3d80 100644
+--- a/llvm/unittests/Support/ConvertUTFTest.cpp
++++ b/llvm/unittests/Support/ConvertUTFTest.cpp
+@@ -19,11 +19,11 @@ TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) {
+   // Src is the look of disapproval.
+   alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";
+   span<const char> Ref(Src, sizeof(Src) - 1);
+-  std::string Result;
++  SmallString<20> Result;
+   bool Success = convertUTF16ToUTF8String(Ref, Result);
+   EXPECT_TRUE(Success);
+   std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");
+-  EXPECT_EQ(Expected, Result);
++  EXPECT_EQ(Expected, std::string{Result});
+ }
+ 
+ TEST(ConvertUTFTest, ConvertUTF32LittleEndianToUTF8String) {
+@@ -42,11 +42,11 @@ TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) {
+   // Src is the look of disapproval.
+   alignas(UTF16) static const char Src[] = "\xfe\xff\x0c\xa0\x00_\x0c\xa0";
+   span<const char> Ref(Src, sizeof(Src) - 1);
+-  std::string Result;
++  SmallString<20> Result;
+   bool Success = convertUTF16ToUTF8String(Ref, Result);
+   EXPECT_TRUE(Success);
+   std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");
+-  EXPECT_EQ(Expected, Result);
++  EXPECT_EQ(Expected, std::string{Result});
+ }
+ 
+ TEST(ConvertUTFTest, ConvertUTF32BigEndianToUTF8String) {
+@@ -75,17 +75,17 @@ TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) {
+ }
+ 
+ TEST(ConvertUTFTest, OddLengthInput) {
+-  std::string Result;
++  SmallString<20> Result;
+   bool Success = convertUTF16ToUTF8String(span<const char>("xxxxx", 5), Result);
+   EXPECT_FALSE(Success);
+ }
+ 
+ TEST(ConvertUTFTest, Empty) {
+-  std::string Result;
++  SmallString<20> Result;
+   bool Success =
+       convertUTF16ToUTF8String(span<const char>(), Result);
+   EXPECT_TRUE(Success);
+-  EXPECT_TRUE(Result.empty());
++  EXPECT_TRUE(std::string{Result}.empty());
+ }
+ 
+ TEST(ConvertUTFTest, HasUTF16BOM) {
+@@ -108,11 +108,11 @@ TEST(ConvertUTFTest, UTF16WrappersForConvertUTF16ToUTF8String) {
+   // Src is the look of disapproval.
+   alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";
+   span<const UTF16> SrcRef((const UTF16 *)Src, 4);
+-  std::string Result;
++  SmallString<20> Result;
+   bool Success = convertUTF16ToUTF8String(SrcRef, Result);
+   EXPECT_TRUE(Success);
+   std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");
+-  EXPECT_EQ(Expected, Result);
++  EXPECT_EQ(Expected, std::string{Result});
+ }
+ 
+ TEST(ConvertUTFTest, ConvertUTF8toWide) {
+@@ -132,11 +132,11 @@ TEST(ConvertUTFTest, ConvertUTF8toWide) {
+ TEST(ConvertUTFTest, convertWideToUTF8) {
+   // Src is the look of disapproval.
+   static const wchar_t Src[] = L"\x0ca0_\x0ca0";
+-  std::string Result;
++  SmallString<20> Result;
+   bool Success = convertWideToUTF8(Src, Result);
+   EXPECT_TRUE(Success);
+   std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");
+-  EXPECT_EQ(Expected, Result);
++  EXPECT_EQ(Expected, std::string{Result});
+ }
+ 
+ struct ConvertUTFResultContainer {
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0024-Prefer-to-use-static-pointers-in-raw_ostream.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0024-Prefer-to-use-static-pointers-in-raw_ostream.patch
new file mode 100644
index 0000000..f5dce6b
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0024-Prefer-to-use-static-pointers-in-raw_ostream.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Thu, 19 May 2022 00:58:36 -0400
+Subject: [PATCH 24/31] Prefer to use static pointers in raw_ostream
+
+See #1401
+---
+ llvm/lib/Support/raw_ostream.cpp | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
+index f9928ac969932b6baea60a80750477d78b6a5b02..1de34976844d500970b833fca35324e2948733b7 100644
+--- a/llvm/lib/Support/raw_ostream.cpp
++++ b/llvm/lib/Support/raw_ostream.cpp
+@@ -613,15 +613,15 @@ void raw_fd_ostream::anchor() {}
+ raw_fd_ostream &llvm::outs() {
+   // Set buffer settings to model stdout behavior.
+   std::error_code EC;
+-  static raw_fd_ostream S("-", EC, sys::fs::OF_None);
++  static raw_fd_ostream* S = new raw_fd_ostream("-", EC, sys::fs::OF_None);
+   assert(!EC);
+-  return S;
++  return *S;
+ }
+ 
+ raw_fd_ostream &llvm::errs() {
+   // Set standard error to be unbuffered and tied to outs() by default.
+-  static raw_fd_ostream S(STDERR_FILENO, false, true);
+-  return S;
++  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.
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0024-Use-SmallVector-for-UTF-conversion.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0024-Use-SmallVector-for-UTF-conversion.patch
deleted file mode 100644
index 96088d2..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0024-Use-SmallVector-for-UTF-conversion.patch
+++ /dev/null
@@ -1,152 +0,0 @@
-From f58b9bad86f61c35cc530ff113ccbb13415261c8 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Mon, 9 May 2022 00:04:30 -0400
-Subject: [PATCH 24/28] Use SmallVector for UTF conversion
-
----
- llvm/include/llvm/Support/ConvertUTF.h    |  6 +++---
- llvm/lib/Support/ConvertUTFWrapper.cpp    |  6 +++---
- llvm/unittests/Support/ConvertUTFTest.cpp | 22 +++++++++++-----------
- 3 files changed, 17 insertions(+), 17 deletions(-)
-
-diff --git a/llvm/include/llvm/Support/ConvertUTF.h b/llvm/include/llvm/Support/ConvertUTF.h
-index b085c8a17..c82947006 100644
---- a/llvm/include/llvm/Support/ConvertUTF.h
-+++ b/llvm/include/llvm/Support/ConvertUTF.h
-@@ -213,7 +213,7 @@ bool ConvertUTF8toWide(const char *Source, std::wstring &Result);
- * Converts a std::wstring to a UTF-8 encoded std::string.
- * \return true on success.
- */
--bool convertWideToUTF8(const std::wstring &Source, std::string &Result);
-+bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result);
- 
- 
- /**
-@@ -268,7 +268,7 @@ bool hasUTF16ByteOrderMark(span<const char> SrcBytes);
-  * \param [out] Out Converted UTF-8 is stored here on success.
-  * \returns true on success
-  */
--bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out);
-+bool convertUTF16ToUTF8String(span<const char> SrcBytes, SmallVectorImpl<char> &Out);
- 
- /**
- * Converts a UTF16 string into a UTF8 std::string.
-@@ -277,7 +277,7 @@ bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out);
- * \param [out] Out Converted UTF-8 is stored here on success.
- * \returns true on success
- */
--bool convertUTF16ToUTF8String(span<const UTF16> Src, std::string &Out);
-+bool convertUTF16ToUTF8String(span<const UTF16> Src, SmallVectorImpl<char> &Out);
- 
- /**
-  * Converts a UTF-8 string into a UTF-16 string with native endianness.
-diff --git a/llvm/lib/Support/ConvertUTFWrapper.cpp b/llvm/lib/Support/ConvertUTFWrapper.cpp
-index cff30f16c..d3689d92a 100644
---- a/llvm/lib/Support/ConvertUTFWrapper.cpp
-+++ b/llvm/lib/Support/ConvertUTFWrapper.cpp
-@@ -84,7 +84,7 @@ bool hasUTF16ByteOrderMark(span<const char> S) {
-            (S[0] == '\xfe' && S[1] == '\xff')));
- }
- 
--bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out) {
-+bool convertUTF16ToUTF8String(span<const char> SrcBytes, SmallVectorImpl<char> &Out) {
-   assert(Out.empty());
- 
-   // Error out on an uneven byte count.
-@@ -135,7 +135,7 @@ bool convertUTF16ToUTF8String(span<const char> SrcBytes, std::string &Out) {
-   return true;
- }
- 
--bool convertUTF16ToUTF8String(span<const UTF16> Src, std::string &Out)
-+bool convertUTF16ToUTF8String(span<const UTF16> Src, SmallVectorImpl<char> &Out)
- {
-   return convertUTF16ToUTF8String(
-       span<const char>(reinterpret_cast<const char *>(Src.data()),
-@@ -213,7 +213,7 @@ bool ConvertUTF8toWide(const char *Source, std::wstring &Result) {
-   return ConvertUTF8toWide(std::string_view(Source), Result);
- }
- 
--bool convertWideToUTF8(const std::wstring &Source, std::string &Result) {
-+bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result) {
-   if (sizeof(wchar_t) == 1) {
-     const UTF8 *Start = reinterpret_cast<const UTF8 *>(Source.data());
-     const UTF8 *End =
-diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp
-index 2fee8ad5c..7d7650b1c 100644
---- a/llvm/unittests/Support/ConvertUTFTest.cpp
-+++ b/llvm/unittests/Support/ConvertUTFTest.cpp
-@@ -19,22 +19,22 @@ TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) {
-   // Src is the look of disapproval.
-   alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";
-   span<const char> Ref(Src, sizeof(Src) - 1);
--  std::string Result;
-+  SmallString<20> Result;
-   bool Success = convertUTF16ToUTF8String(Ref, Result);
-   EXPECT_TRUE(Success);
-   std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");
--  EXPECT_EQ(Expected, Result);
-+  EXPECT_EQ(Expected, std::string{Result});
- }
- 
- TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) {
-   // Src is the look of disapproval.
-   alignas(UTF16) static const char Src[] = "\xfe\xff\x0c\xa0\x00_\x0c\xa0";
-   span<const char> Ref(Src, sizeof(Src) - 1);
--  std::string Result;
-+  SmallString<20> Result;
-   bool Success = convertUTF16ToUTF8String(Ref, Result);
-   EXPECT_TRUE(Success);
-   std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");
--  EXPECT_EQ(Expected, Result);
-+  EXPECT_EQ(Expected, std::string{Result});
- }
- 
- TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) {
-@@ -51,16 +51,16 @@ TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) {
- }
- 
- TEST(ConvertUTFTest, OddLengthInput) {
--  std::string Result;
-+  SmallString<20> Result;
-   bool Success = convertUTF16ToUTF8String(span<const char>("xxxxx", 5), Result);
-   EXPECT_FALSE(Success);
- }
- 
- TEST(ConvertUTFTest, Empty) {
--  std::string Result;
-+  SmallString<20> Result;
-   bool Success = convertUTF16ToUTF8String(span<const char>(), Result);
-   EXPECT_TRUE(Success);
--  EXPECT_TRUE(Result.empty());
-+  EXPECT_TRUE(std::string{Result}.empty());
- }
- 
- TEST(ConvertUTFTest, HasUTF16BOM) {
-@@ -83,11 +83,11 @@ TEST(ConvertUTFTest, UTF16WrappersForConvertUTF16ToUTF8String) {
-   // Src is the look of disapproval.
-   alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";
-   span<const UTF16> SrcRef((const UTF16 *)Src, 4);
--  std::string Result;
-+  SmallString<20> Result;
-   bool Success = convertUTF16ToUTF8String(SrcRef, Result);
-   EXPECT_TRUE(Success);
-   std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");
--  EXPECT_EQ(Expected, Result);
-+  EXPECT_EQ(Expected, std::string{Result});
- }
- 
- TEST(ConvertUTFTest, ConvertUTF8toWide) {
-@@ -107,11 +107,11 @@ TEST(ConvertUTFTest, ConvertUTF8toWide) {
- TEST(ConvertUTFTest, convertWideToUTF8) {
-   // Src is the look of disapproval.
-   static const wchar_t Src[] = L"\x0ca0_\x0ca0";
--  std::string Result;
-+  SmallString<20> Result;
-   bool Success = convertWideToUTF8(Src, Result);
-   EXPECT_TRUE(Success);
-   std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");
--  EXPECT_EQ(Expected, Result);
-+  EXPECT_EQ(Expected, std::string{Result});
- }
- 
- struct ConvertUTFResultContainer {
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0025-Prefer-to-use-static-pointers-in-raw_ostream.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0025-Prefer-to-use-static-pointers-in-raw_ostream.patch
deleted file mode 100644
index 36a884c..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0025-Prefer-to-use-static-pointers-in-raw_ostream.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 35b1a8382240732065790c88a0c515701c1a2beb Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Thu, 19 May 2022 00:58:36 -0400
-Subject: [PATCH 25/28] Prefer to use static pointers in raw_ostream
-
-See #1401
----
- llvm/lib/Support/raw_ostream.cpp | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
-index 632b52235..a703a75ed 100644
---- a/llvm/lib/Support/raw_ostream.cpp
-+++ b/llvm/lib/Support/raw_ostream.cpp
-@@ -599,15 +599,15 @@ void raw_fd_ostream::anchor() {}
- raw_fd_ostream &llvm::outs() {
-   // Set buffer settings to model stdout behavior.
-   std::error_code EC;
--  static raw_fd_ostream S("-", EC, sys::fs::OF_None);
-+  static raw_fd_ostream* S = new raw_fd_ostream("-", EC, sys::fs::OF_None);
-   assert(!EC);
--  return S;
-+  return *S;
- }
- 
- raw_fd_ostream &llvm::errs() {
-   // Set standard error to be unbuffered and tied to outs() by default.
--  static raw_fd_ostream S(STDERR_FILENO, false, true);
--  return S;
-+  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.
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0025-constexpr-endian-byte-swap.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0025-constexpr-endian-byte-swap.patch
new file mode 100644
index 0000000..acbded1
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0025-constexpr-endian-byte-swap.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: PJ Reiniger <pj.reiniger@gmail.com>
+Date: Thu, 19 May 2022 01:12:41 -0400
+Subject: [PATCH 25/31] constexpr endian byte swap
+
+---
+ llvm/include/llvm/Support/Endian.h | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/llvm/include/llvm/Support/Endian.h b/llvm/include/llvm/Support/Endian.h
+index 5e7c1e961b9d1e0fd6bcd0c923f678a5e1596860..2e883ff05b7e287151b6031f66d3b4aefd1bf297 100644
+--- a/llvm/include/llvm/Support/Endian.h
++++ b/llvm/include/llvm/Support/Endian.h
+@@ -55,7 +55,9 @@ inline value_type byte_swap(value_type value, endianness endian) {
+ /// Swap the bytes of value to match the given endianness.
+ template<typename value_type, endianness endian>
+ inline value_type byte_swap(value_type value) {
+-  return byte_swap(value, endian);
++  if constexpr ((endian != native) && (endian != system_endianness()))
++    sys::swapByteOrder(value);
++  return value;
+ }
+ 
+ /// Read a value of a particular endianness from memory.
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0026-Copy-type-traits-from-STLExtras.h-into-PointerUnion..patch b/third_party/allwpilib/upstream_utils/llvm_patches/0026-Copy-type-traits-from-STLExtras.h-into-PointerUnion..patch
new file mode 100644
index 0000000..efa9e6c
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0026-Copy-type-traits-from-STLExtras.h-into-PointerUnion..patch
@@ -0,0 +1,69 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Wed, 10 Aug 2022 17:07:52 -0700
+Subject: [PATCH 26/31] Copy type traits from STLExtras.h into PointerUnion.h
+
+---
+ llvm/include/llvm/ADT/PointerUnion.h | 46 ++++++++++++++++++++++++++++
+ 1 file changed, 46 insertions(+)
+
+diff --git a/llvm/include/llvm/ADT/PointerUnion.h b/llvm/include/llvm/ADT/PointerUnion.h
+index 8ac68dbc0a791b8ac0e0ca865e69024cb642aa70..273ba02934bd405ea4f1b911ebb58f7080837ff0 100644
+--- a/llvm/include/llvm/ADT/PointerUnion.h
++++ b/llvm/include/llvm/ADT/PointerUnion.h
+@@ -23,9 +23,55 @@
+ #include <cassert>
+ #include <cstddef>
+ #include <cstdint>
++#include <type_traits>
+ 
+ namespace llvm {
+ 
++namespace detail {
++template <typename T, typename... Us> struct TypesAreDistinct;
++template <typename T, typename... Us>
++struct TypesAreDistinct
++    : std::integral_constant<bool, !std::disjunction_v<std::is_same<T, Us>...> &&
++                                       TypesAreDistinct<Us...>::value> {};
++template <typename T> struct TypesAreDistinct<T> : std::true_type {};
++} // namespace detail
++
++/// Determine if all types in Ts are distinct.
++///
++/// Useful to statically assert when Ts is intended to describe a non-multi set
++/// of types.
++///
++/// Expensive (currently quadratic in sizeof(Ts...)), and so should only be
++/// asserted once per instantiation of a type which requires it.
++template <typename... Ts> struct TypesAreDistinct;
++template <> struct TypesAreDistinct<> : std::true_type {};
++template <typename... Ts>
++struct TypesAreDistinct
++    : std::integral_constant<bool, detail::TypesAreDistinct<Ts...>::value> {};
++
++/// Find the first index where a type appears in a list of types.
++///
++/// FirstIndexOfType<T, Us...>::value is the first index of T in Us.
++///
++/// Typically only meaningful when it is otherwise statically known that the
++/// type pack has no duplicate types. This should be guaranteed explicitly with
++/// static_assert(TypesAreDistinct<Us...>::value).
++///
++/// It is a compile-time error to instantiate when T is not present in Us, i.e.
++/// if is_one_of<T, Us...>::value is false.
++template <typename T, typename... Us> struct FirstIndexOfType;
++template <typename T, typename U, typename... Us>
++struct FirstIndexOfType<T, U, Us...>
++    : std::integral_constant<size_t, 1 + FirstIndexOfType<T, Us...>::value> {};
++template <typename T, typename... Us>
++struct FirstIndexOfType<T, T, Us...> : std::integral_constant<size_t, 0> {};
++
++/// Find the type at a given index in a list of types.
++///
++/// TypeAtIndex<I, Ts...> is the type at index I in Ts.
++template <size_t I, typename... Ts>
++using TypeAtIndex = std::tuple_element_t<I, std::tuple<Ts...>>;
++
+ namespace pointer_union_detail {
+   /// Determine the number of bits required to store integers with values < n.
+   /// This is ceil(log2(n)).
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0026-constexpr-endian-byte-swap.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0026-constexpr-endian-byte-swap.patch
deleted file mode 100644
index 79e0e7d..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0026-constexpr-endian-byte-swap.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From 34f44d312c918b3b5dd69fc689ec0b628dc712f9 Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Thu, 19 May 2022 01:12:41 -0400
-Subject: [PATCH 26/28] constexpr endian byte swap
-
----
- llvm/include/llvm/Support/Endian.h | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/llvm/include/llvm/Support/Endian.h b/llvm/include/llvm/Support/Endian.h
-index 5e7c1e961..2e883ff05 100644
---- a/llvm/include/llvm/Support/Endian.h
-+++ b/llvm/include/llvm/Support/Endian.h
-@@ -55,7 +55,9 @@ inline value_type byte_swap(value_type value, endianness endian) {
- /// Swap the bytes of value to match the given endianness.
- template<typename value_type, endianness endian>
- inline value_type byte_swap(value_type value) {
--  return byte_swap(value, endian);
-+  if constexpr ((endian != native) && (endian != system_endianness()))
-+    sys::swapByteOrder(value);
-+  return value;
- }
- 
- /// Read a value of a particular endianness from memory.
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0027-Copy-type-traits-from-STLExtras.h-into-PointerUnion..patch b/third_party/allwpilib/upstream_utils/llvm_patches/0027-Copy-type-traits-from-STLExtras.h-into-PointerUnion..patch
deleted file mode 100644
index 9f192a1..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0027-Copy-type-traits-from-STLExtras.h-into-PointerUnion..patch
+++ /dev/null
@@ -1,69 +0,0 @@
-From 8f51777a3117a7b010a4cfb2ded1f5d226466f34 Mon Sep 17 00:00:00 2001
-From: Tyler Veness <calcmogul@gmail.com>
-Date: Wed, 10 Aug 2022 17:07:52 -0700
-Subject: [PATCH 27/28] Copy type traits from STLExtras.h into PointerUnion.h
-
----
- llvm/include/llvm/ADT/PointerUnion.h | 46 ++++++++++++++++++++++++++++
- 1 file changed, 46 insertions(+)
-
-diff --git a/llvm/include/llvm/ADT/PointerUnion.h b/llvm/include/llvm/ADT/PointerUnion.h
-index 1d4cc747c..7d090da59 100644
---- a/llvm/include/llvm/ADT/PointerUnion.h
-+++ b/llvm/include/llvm/ADT/PointerUnion.h
-@@ -22,9 +22,55 @@
- #include <cassert>
- #include <cstddef>
- #include <cstdint>
-+#include <type_traits>
- 
- namespace llvm {
- 
-+namespace detail {
-+template <typename T, typename... Us> struct TypesAreDistinct;
-+template <typename T, typename... Us>
-+struct TypesAreDistinct
-+    : std::integral_constant<bool, !std::disjunction_v<std::is_same<T, Us>...> &&
-+                                       TypesAreDistinct<Us...>::value> {};
-+template <typename T> struct TypesAreDistinct<T> : std::true_type {};
-+} // namespace detail
-+
-+/// Determine if all types in Ts are distinct.
-+///
-+/// Useful to statically assert when Ts is intended to describe a non-multi set
-+/// of types.
-+///
-+/// Expensive (currently quadratic in sizeof(Ts...)), and so should only be
-+/// asserted once per instantiation of a type which requires it.
-+template <typename... Ts> struct TypesAreDistinct;
-+template <> struct TypesAreDistinct<> : std::true_type {};
-+template <typename... Ts>
-+struct TypesAreDistinct
-+    : std::integral_constant<bool, detail::TypesAreDistinct<Ts...>::value> {};
-+
-+/// Find the first index where a type appears in a list of types.
-+///
-+/// FirstIndexOfType<T, Us...>::value is the first index of T in Us.
-+///
-+/// Typically only meaningful when it is otherwise statically known that the
-+/// type pack has no duplicate types. This should be guaranteed explicitly with
-+/// static_assert(TypesAreDistinct<Us...>::value).
-+///
-+/// It is a compile-time error to instantiate when T is not present in Us, i.e.
-+/// if is_one_of<T, Us...>::value is false.
-+template <typename T, typename... Us> struct FirstIndexOfType;
-+template <typename T, typename U, typename... Us>
-+struct FirstIndexOfType<T, U, Us...>
-+    : std::integral_constant<size_t, 1 + FirstIndexOfType<T, Us...>::value> {};
-+template <typename T, typename... Us>
-+struct FirstIndexOfType<T, T, Us...> : std::integral_constant<size_t, 0> {};
-+
-+/// Find the type at a given index in a list of types.
-+///
-+/// TypeAtIndex<I, Ts...> is the type at index I in Ts.
-+template <size_t I, typename... Ts>
-+using TypeAtIndex = std::tuple_element_t<I, std::tuple<Ts...>>;
-+
- namespace pointer_union_detail {
-   /// Determine the number of bits required to store integers with values < n.
-   /// This is ceil(log2(n)).
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0027-Remove-StringMap-test-for-llvm-sort.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0027-Remove-StringMap-test-for-llvm-sort.patch
new file mode 100644
index 0000000..977dff3
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0027-Remove-StringMap-test-for-llvm-sort.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Wed, 10 Aug 2022 22:35:00 -0700
+Subject: [PATCH 27/31] Remove StringMap test for llvm::sort()
+
+---
+ llvm/unittests/ADT/StringMapTest.cpp | 14 --------------
+ 1 file changed, 14 deletions(-)
+
+diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
+index 6b6cf564909f329c220eb225f3b7af6c35301029..0d83669a580408e925ec6308410ebe7c01b48b12 100644
+--- a/llvm/unittests/ADT/StringMapTest.cpp
++++ b/llvm/unittests/ADT/StringMapTest.cpp
+@@ -322,20 +322,6 @@ TEST_F(StringMapTest, InsertOrAssignTest) {
+   EXPECT_EQ(0, try1.first->second.copy);
+ }
+ 
+-TEST_F(StringMapTest, IterMapKeysVector) {
+-  StringMap<int> Map;
+-  Map["A"] = 1;
+-  Map["B"] = 2;
+-  Map["C"] = 3;
+-  Map["D"] = 3;
+-
+-  std::vector<std::string_view> Keys{Map.keys().begin(), Map.keys().end()};
+-  llvm::sort(Keys);
+-
+-  std::vector<std::string_view> Expected{{"A", "B", "C", "D"}};
+-  EXPECT_EQ(Expected, Keys);
+-}
+-
+ TEST_F(StringMapTest, IterMapKeysSmallVector) {
+   StringMap<int> Map;
+   Map["A"] = 1;
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0028-Remove-StringMap-test-for-llvm-sort.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0028-Remove-StringMap-test-for-llvm-sort.patch
deleted file mode 100644
index 5c847e1..0000000
--- a/third_party/allwpilib/upstream_utils/llvm_patches/0028-Remove-StringMap-test-for-llvm-sort.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 498e56e09e8f264978831519ff9c8afa701bf208 Mon Sep 17 00:00:00 2001
-From: Tyler Veness <calcmogul@gmail.com>
-Date: Wed, 10 Aug 2022 22:35:00 -0700
-Subject: [PATCH 28/28] Remove StringMap test for llvm::sort()
-
----
- llvm/unittests/ADT/StringMapTest.cpp | 14 --------------
- 1 file changed, 14 deletions(-)
-
-diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
-index de6daf3da..ca41631cc 100644
---- a/llvm/unittests/ADT/StringMapTest.cpp
-+++ b/llvm/unittests/ADT/StringMapTest.cpp
-@@ -308,20 +308,6 @@ TEST_F(StringMapTest, InsertOrAssignTest) {
-   EXPECT_EQ(0, try1.first->second.copy);
- }
- 
--TEST_F(StringMapTest, IterMapKeysVector) {
--  StringMap<int> Map;
--  Map["A"] = 1;
--  Map["B"] = 2;
--  Map["C"] = 3;
--  Map["D"] = 3;
--
--  std::vector<std::string_view> Keys{Map.keys().begin(), Map.keys().end()};
--  llvm::sort(Keys);
--
--  std::vector<std::string_view> Expected{{"A", "B", "C", "D"}};
--  EXPECT_EQ(Expected, Keys);
--}
--
- TEST_F(StringMapTest, IterMapKeysSmallVector) {
-   StringMap<int> Map;
-   Map["A"] = 1;
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0028-Unused-variable-in-release-mode.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0028-Unused-variable-in-release-mode.patch
new file mode 100644
index 0000000..2492883
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0028-Unused-variable-in-release-mode.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Leander Schulten <Leander.Schulten@rwth-aachen.de>
+Date: Mon, 10 Jul 2023 00:53:43 +0200
+Subject: [PATCH 28/31] Unused variable in release mode
+
+---
+ llvm/include/llvm/ADT/DenseMap.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
+index 108193f04486425f3b7f039cd9d2004be6facafb..e9bd3bfa4a6fe0fa26ff20069bbadc816c8baa65 100644
+--- a/llvm/include/llvm/ADT/DenseMap.h
++++ b/llvm/include/llvm/ADT/DenseMap.h
+@@ -124,7 +124,7 @@ public:
+       for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P)
+         P->getFirst() = EmptyKey;
+     } else {
+-      unsigned NumEntries = getNumEntries();
++      [[maybe_unused]] unsigned NumEntries = getNumEntries();
+       for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) {
+         if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey)) {
+           if (!KeyInfoT::isEqual(P->getFirst(), TombstoneKey)) {
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0029-Use-C-20-bit-header.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0029-Use-C-20-bit-header.patch
new file mode 100644
index 0000000..fad61d3
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0029-Use-C-20-bit-header.patch
@@ -0,0 +1,509 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Tue, 11 Jul 2023 22:56:09 -0700
+Subject: [PATCH 29/31] Use C++20 <bit> header
+
+---
+ llvm/include/llvm/ADT/DenseMap.h       |   3 +-
+ llvm/include/llvm/ADT/Hashing.h        |  35 +--
+ llvm/include/llvm/ADT/bit.h            | 287 -------------------------
+ llvm/include/llvm/Support/MathExtras.h |  21 +-
+ 4 files changed, 31 insertions(+), 315 deletions(-)
+
+diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
+index e9bd3bfa4a6fe0fa26ff20069bbadc816c8baa65..93b50c9e53af4ea3af5fd0329a8a03bdce659e9d 100644
+--- a/llvm/include/llvm/ADT/DenseMap.h
++++ b/llvm/include/llvm/ADT/DenseMap.h
+@@ -23,6 +23,7 @@
+ #include "llvm/Support/ReverseIteration.h"
+ #include "llvm/Support/type_traits.h"
+ #include <algorithm>
++#include <bit>
+ #include <cassert>
+ #include <cstddef>
+ #include <cstring>
+@@ -933,7 +934,7 @@ class SmallDenseMap
+ public:
+   explicit SmallDenseMap(unsigned NumInitBuckets = 0) {
+     if (NumInitBuckets > InlineBuckets)
+-      NumInitBuckets = llvm::bit_ceil(NumInitBuckets);
++      NumInitBuckets = std::bit_ceil(NumInitBuckets);
+     init(NumInitBuckets);
+   }
+ 
+diff --git a/llvm/include/llvm/ADT/Hashing.h b/llvm/include/llvm/ADT/Hashing.h
+index 781bdb7416392e3f60a1ac3a38fbcf5324b5395f..28934add722f518ae1e9cb9c4a23d2212a47cbdf 100644
+--- a/llvm/include/llvm/ADT/Hashing.h
++++ b/llvm/include/llvm/ADT/Hashing.h
+@@ -49,6 +49,7 @@
+ #include "llvm/Support/SwapByteOrder.h"
+ #include "llvm/Support/type_traits.h"
+ #include <algorithm>
++#include <bit>
+ #include <cassert>
+ #include <cstring>
+ #include <optional>
+@@ -224,30 +225,30 @@ inline uint64_t hash_17to32_bytes(const char *s, size_t len, uint64_t seed) {
+   uint64_t b = fetch64(s + 8);
+   uint64_t c = fetch64(s + len - 8) * k2;
+   uint64_t d = fetch64(s + len - 16) * k0;
+-  return hash_16_bytes(llvm::rotr<uint64_t>(a - b, 43) +
+-                           llvm::rotr<uint64_t>(c ^ seed, 30) + d,
+-                       a + llvm::rotr<uint64_t>(b ^ k3, 20) - c + len + seed);
++  return hash_16_bytes(std::rotr<uint64_t>(a - b, 43) +
++                           std::rotr<uint64_t>(c ^ seed, 30) + d,
++                       a + std::rotr<uint64_t>(b ^ k3, 20) - c + len + seed);
+ }
+ 
+ inline uint64_t hash_33to64_bytes(const char *s, size_t len, uint64_t seed) {
+   uint64_t z = fetch64(s + 24);
+   uint64_t a = fetch64(s) + (len + fetch64(s + len - 16)) * k0;
+-  uint64_t b = llvm::rotr<uint64_t>(a + z, 52);
+-  uint64_t c = llvm::rotr<uint64_t>(a, 37);
++  uint64_t b = std::rotr<uint64_t>(a + z, 52);
++  uint64_t c = std::rotr<uint64_t>(a, 37);
+   a += fetch64(s + 8);
+-  c += llvm::rotr<uint64_t>(a, 7);
++  c += std::rotr<uint64_t>(a, 7);
+   a += fetch64(s + 16);
+   uint64_t vf = a + z;
+-  uint64_t vs = b + llvm::rotr<uint64_t>(a, 31) + c;
++  uint64_t vs = b + std::rotr<uint64_t>(a, 31) + c;
+   a = fetch64(s + 16) + fetch64(s + len - 32);
+   z = fetch64(s + len - 8);
+-  b = llvm::rotr<uint64_t>(a + z, 52);
+-  c = llvm::rotr<uint64_t>(a, 37);
++  b = std::rotr<uint64_t>(a + z, 52);
++  c = std::rotr<uint64_t>(a, 37);
+   a += fetch64(s + len - 24);
+-  c += llvm::rotr<uint64_t>(a, 7);
++  c += std::rotr<uint64_t>(a, 7);
+   a += fetch64(s + len - 16);
+   uint64_t wf = a + z;
+-  uint64_t ws = b + llvm::rotr<uint64_t>(a, 31) + c;
++  uint64_t ws = b + std::rotr<uint64_t>(a, 31) + c;
+   uint64_t r = shift_mix((vf + ws) * k2 + (wf + vs) * k0);
+   return shift_mix((seed ^ (r * k0)) + vs) * k2;
+ }
+@@ -280,7 +281,7 @@ struct hash_state {
+     hash_state state = {0,
+                         seed,
+                         hash_16_bytes(seed, k1),
+-                        llvm::rotr<uint64_t>(seed ^ k1, 49),
++                        std::rotr<uint64_t>(seed ^ k1, 49),
+                         seed * k1,
+                         shift_mix(seed),
+                         0};
+@@ -294,10 +295,10 @@ struct hash_state {
+   static void mix_32_bytes(const char *s, uint64_t &a, uint64_t &b) {
+     a += fetch64(s);
+     uint64_t c = fetch64(s + 24);
+-    b = llvm::rotr<uint64_t>(b + a + c, 21);
++    b = std::rotr<uint64_t>(b + a + c, 21);
+     uint64_t d = a;
+     a += fetch64(s + 8) + fetch64(s + 16);
+-    b += llvm::rotr<uint64_t>(a, 44) + d;
++    b += std::rotr<uint64_t>(a, 44) + d;
+     a += c;
+   }
+ 
+@@ -305,11 +306,11 @@ struct hash_state {
+   /// We mix all 64 bytes even when the chunk length is smaller, but we
+   /// record the actual length.
+   void mix(const char *s) {
+-    h0 = llvm::rotr<uint64_t>(h0 + h1 + h3 + fetch64(s + 8), 37) * k1;
+-    h1 = llvm::rotr<uint64_t>(h1 + h4 + fetch64(s + 48), 42) * k1;
++    h0 = std::rotr<uint64_t>(h0 + h1 + h3 + fetch64(s + 8), 37) * k1;
++    h1 = std::rotr<uint64_t>(h1 + h4 + fetch64(s + 48), 42) * k1;
+     h0 ^= h6;
+     h1 += h3 + fetch64(s + 40);
+-    h2 = llvm::rotr<uint64_t>(h2 + h5, 33) * k1;
++    h2 = std::rotr<uint64_t>(h2 + h5, 33) * k1;
+     h3 = h4 * k1;
+     h4 = h0 + h5;
+     mix_32_bytes(s, h3, h4);
+diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
+index 2840c5f608d3ea896e1867dd4710685da9572f2d..0a4a3634820efbc0a8ca675e3ad7c98469260d0b 100644
+--- a/llvm/include/llvm/ADT/bit.h
++++ b/llvm/include/llvm/ADT/bit.h
+@@ -27,18 +27,6 @@
+ #include <cstdlib>  // for _byteswap_{ushort,ulong,uint64}
+ #endif
+ 
+-#ifdef _MSC_VER
+-// Declare these intrinsics manually rather including intrin.h. It's very
+-// expensive, and bit.h is popular via MathExtras.h.
+-// #include <intrin.h>
+-extern "C" {
+-unsigned char _BitScanForward(unsigned long *_Index, unsigned long _Mask);
+-unsigned char _BitScanForward64(unsigned long *_Index, unsigned __int64 _Mask);
+-unsigned char _BitScanReverse(unsigned long *_Index, unsigned long _Mask);
+-unsigned char _BitScanReverse64(unsigned long *_Index, unsigned __int64 _Mask);
+-}
+-#endif
+-
+ namespace llvm {
+ 
+ // This implementation of bit_cast is different from the C++20 one in two ways:
+@@ -106,281 +94,6 @@ template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
+   }
+ }
+ 
+-template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
+-[[nodiscard]] constexpr inline bool has_single_bit(T Value) noexcept {
+-  return (Value != 0) && ((Value & (Value - 1)) == 0);
+-}
+-
+-namespace detail {
+-template <typename T, std::size_t SizeOfT> struct TrailingZerosCounter {
+-  static unsigned count(T Val) {
+-    if (!Val)
+-      return std::numeric_limits<T>::digits;
+-    if (Val & 0x1)
+-      return 0;
+-
+-    // Bisection method.
+-    unsigned ZeroBits = 0;
+-    T Shift = std::numeric_limits<T>::digits >> 1;
+-    T Mask = std::numeric_limits<T>::max() >> Shift;
+-    while (Shift) {
+-      if ((Val & Mask) == 0) {
+-        Val >>= Shift;
+-        ZeroBits |= Shift;
+-      }
+-      Shift >>= 1;
+-      Mask >>= Shift;
+-    }
+-    return ZeroBits;
+-  }
+-};
+-
+-#if defined(__GNUC__) || defined(_MSC_VER)
+-template <typename T> struct TrailingZerosCounter<T, 4> {
+-  static unsigned count(T Val) {
+-    if (Val == 0)
+-      return 32;
+-
+-#if __has_builtin(__builtin_ctz) || defined(__GNUC__)
+-    return __builtin_ctz(Val);
+-#elif defined(_MSC_VER)
+-    unsigned long Index;
+-    _BitScanForward(&Index, Val);
+-    return Index;
+-#endif
+-  }
+-};
+-
+-#if !defined(_MSC_VER) || defined(_M_X64)
+-template <typename T> struct TrailingZerosCounter<T, 8> {
+-  static unsigned count(T Val) {
+-    if (Val == 0)
+-      return 64;
+-
+-#if __has_builtin(__builtin_ctzll) || defined(__GNUC__)
+-    return __builtin_ctzll(Val);
+-#elif defined(_MSC_VER)
+-    unsigned long Index;
+-    _BitScanForward64(&Index, Val);
+-    return Index;
+-#endif
+-  }
+-};
+-#endif
+-#endif
+-} // namespace detail
+-
+-/// Count number of 0's from the least significant bit to the most
+-///   stopping at the first 1.
+-///
+-/// Only unsigned integral types are allowed.
+-///
+-/// Returns std::numeric_limits<T>::digits on an input of 0.
+-template <typename T> [[nodiscard]] int countr_zero(T Val) {
+-  static_assert(std::is_unsigned_v<T>,
+-                "Only unsigned integral types are allowed.");
+-  return llvm::detail::TrailingZerosCounter<T, sizeof(T)>::count(Val);
+-}
+-
+-namespace detail {
+-template <typename T, std::size_t SizeOfT> struct LeadingZerosCounter {
+-  static unsigned count(T Val) {
+-    if (!Val)
+-      return std::numeric_limits<T>::digits;
+-
+-    // Bisection method.
+-    unsigned ZeroBits = 0;
+-    for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) {
+-      T Tmp = Val >> Shift;
+-      if (Tmp)
+-        Val = Tmp;
+-      else
+-        ZeroBits |= Shift;
+-    }
+-    return ZeroBits;
+-  }
+-};
+-
+-#if defined(__GNUC__) || defined(_MSC_VER)
+-template <typename T> struct LeadingZerosCounter<T, 4> {
+-  static unsigned count(T Val) {
+-    if (Val == 0)
+-      return 32;
+-
+-#if __has_builtin(__builtin_clz) || defined(__GNUC__)
+-    return __builtin_clz(Val);
+-#elif defined(_MSC_VER)
+-    unsigned long Index;
+-    _BitScanReverse(&Index, Val);
+-    return Index ^ 31;
+-#endif
+-  }
+-};
+-
+-#if !defined(_MSC_VER) || defined(_M_X64)
+-template <typename T> struct LeadingZerosCounter<T, 8> {
+-  static unsigned count(T Val) {
+-    if (Val == 0)
+-      return 64;
+-
+-#if __has_builtin(__builtin_clzll) || defined(__GNUC__)
+-    return __builtin_clzll(Val);
+-#elif defined(_MSC_VER)
+-    unsigned long Index;
+-    _BitScanReverse64(&Index, Val);
+-    return Index ^ 63;
+-#endif
+-  }
+-};
+-#endif
+-#endif
+-} // namespace detail
+-
+-/// Count number of 0's from the most significant bit to the least
+-///   stopping at the first 1.
+-///
+-/// Only unsigned integral types are allowed.
+-///
+-/// Returns std::numeric_limits<T>::digits on an input of 0.
+-template <typename T> [[nodiscard]] int countl_zero(T Val) {
+-  static_assert(std::is_unsigned_v<T>,
+-                "Only unsigned integral types are allowed.");
+-  return llvm::detail::LeadingZerosCounter<T, sizeof(T)>::count(Val);
+-}
+-
+-/// Count the number of ones from the most significant bit to the first
+-/// zero bit.
+-///
+-/// Ex. countl_one(0xFF0FFF00) == 8.
+-/// Only unsigned integral types are allowed.
+-///
+-/// Returns std::numeric_limits<T>::digits on an input of all ones.
+-template <typename T> [[nodiscard]] int countl_one(T Value) {
+-  static_assert(std::is_unsigned_v<T>,
+-                "Only unsigned integral types are allowed.");
+-  return llvm::countl_zero<T>(~Value);
+-}
+-
+-/// Count the number of ones from the least significant bit to the first
+-/// zero bit.
+-///
+-/// Ex. countr_one(0x00FF00FF) == 8.
+-/// Only unsigned integral types are allowed.
+-///
+-/// Returns std::numeric_limits<T>::digits on an input of all ones.
+-template <typename T> [[nodiscard]] int countr_one(T Value) {
+-  static_assert(std::is_unsigned_v<T>,
+-                "Only unsigned integral types are allowed.");
+-  return llvm::countr_zero<T>(~Value);
+-}
+-
+-/// Returns the number of bits needed to represent Value if Value is nonzero.
+-/// Returns 0 otherwise.
+-///
+-/// Ex. bit_width(5) == 3.
+-template <typename T> [[nodiscard]] int bit_width(T Value) {
+-  static_assert(std::is_unsigned_v<T>,
+-                "Only unsigned integral types are allowed.");
+-  return std::numeric_limits<T>::digits - llvm::countl_zero(Value);
+-}
+-
+-/// Returns the largest integral power of two no greater than Value if Value is
+-/// nonzero.  Returns 0 otherwise.
+-///
+-/// Ex. bit_floor(5) == 4.
+-template <typename T> [[nodiscard]] T bit_floor(T Value) {
+-  static_assert(std::is_unsigned_v<T>,
+-                "Only unsigned integral types are allowed.");
+-  if (!Value)
+-    return 0;
+-  return T(1) << (llvm::bit_width(Value) - 1);
+-}
+-
+-/// Returns the smallest integral power of two no smaller than Value if Value is
+-/// nonzero.  Returns 1 otherwise.
+-///
+-/// Ex. bit_ceil(5) == 8.
+-///
+-/// The return value is undefined if the input is larger than the largest power
+-/// of two representable in T.
+-template <typename T> [[nodiscard]] T bit_ceil(T Value) {
+-  static_assert(std::is_unsigned_v<T>,
+-                "Only unsigned integral types are allowed.");
+-  if (Value < 2)
+-    return 1;
+-  return T(1) << llvm::bit_width<T>(Value - 1u);
+-}
+-
+-namespace detail {
+-template <typename T, std::size_t SizeOfT> struct PopulationCounter {
+-  static int count(T Value) {
+-    // Generic version, forward to 32 bits.
+-    static_assert(SizeOfT <= 4, "Not implemented!");
+-#if defined(__GNUC__)
+-    return (int)__builtin_popcount(Value);
+-#else
+-    uint32_t v = Value;
+-    v = v - ((v >> 1) & 0x55555555);
+-    v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
+-    return int(((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24);
+-#endif
+-  }
+-};
+-
+-template <typename T> struct PopulationCounter<T, 8> {
+-  static int count(T Value) {
+-#if defined(__GNUC__)
+-    return (int)__builtin_popcountll(Value);
+-#else
+-    uint64_t v = Value;
+-    v = v - ((v >> 1) & 0x5555555555555555ULL);
+-    v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
+-    v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
+-    return int((uint64_t)(v * 0x0101010101010101ULL) >> 56);
+-#endif
+-  }
+-};
+-} // namespace detail
+-
+-/// Count the number of set bits in a value.
+-/// Ex. popcount(0xF000F000) = 8
+-/// Returns 0 if the word is zero.
+-template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
+-[[nodiscard]] inline int popcount(T Value) noexcept {
+-  return detail::PopulationCounter<T, sizeof(T)>::count(Value);
+-}
+-
+-// Forward-declare rotr so that rotl can use it.
+-template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
+-[[nodiscard]] constexpr T rotr(T V, int R);
+-
+-template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
+-[[nodiscard]] constexpr T rotl(T V, int R) {
+-  unsigned N = std::numeric_limits<T>::digits;
+-
+-  R = R % N;
+-  if (!R)
+-    return V;
+-
+-  if (R < 0)
+-    return llvm::rotr(V, -R);
+-
+-  return (V << R) | (V >> (N - R));
+-}
+-
+-template <typename T, typename> [[nodiscard]] constexpr T rotr(T V, int R) {
+-  unsigned N = std::numeric_limits<T>::digits;
+-
+-  R = R % N;
+-  if (!R)
+-    return V;
+-
+-  if (R < 0)
+-    return llvm::rotl(V, -R);
+-
+-  return (V >> R) | (V << (N - R));
+-}
+-
+ } // namespace llvm
+ 
+ #endif
+diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
+index b82d9883c41008dcbbd933709c6e854ad74c5b58..5f034b694989d8ef24e0b249abd12a5c20146b97 100644
+--- a/llvm/include/llvm/Support/MathExtras.h
++++ b/llvm/include/llvm/Support/MathExtras.h
+@@ -15,6 +15,7 @@
+ 
+ #include "llvm/ADT/bit.h"
+ #include "llvm/Support/Compiler.h"
++#include <bit>
+ #include <cassert>
+ #include <climits>
+ #include <cstdint>
+@@ -235,12 +236,12 @@ constexpr inline bool isShiftedMask_64(uint64_t Value) {
+ /// Return true if the argument is a power of two > 0.
+ /// Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.)
+ constexpr inline bool isPowerOf2_32(uint32_t Value) {
+-  return llvm::has_single_bit(Value);
++  return std::has_single_bit(Value);
+ }
+ 
+ /// Return true if the argument is a power of two > 0 (64 bit edition.)
+ constexpr inline bool isPowerOf2_64(uint64_t Value) {
+-  return llvm::has_single_bit(Value);
++  return std::has_single_bit(Value);
+ }
+ 
+ /// Return true if the argument contains a non-empty sequence of ones with the
+@@ -252,8 +253,8 @@ inline bool isShiftedMask_32(uint32_t Value, unsigned &MaskIdx,
+                              unsigned &MaskLen) {
+   if (!isShiftedMask_32(Value))
+     return false;
+-  MaskIdx = llvm::countr_zero(Value);
+-  MaskLen = llvm::popcount(Value);
++  MaskIdx = std::countr_zero(Value);
++  MaskLen = std::popcount(Value);
+   return true;
+ }
+ 
+@@ -265,8 +266,8 @@ inline bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx,
+                              unsigned &MaskLen) {
+   if (!isShiftedMask_64(Value))
+     return false;
+-  MaskIdx = llvm::countr_zero(Value);
+-  MaskLen = llvm::popcount(Value);
++  MaskIdx = std::countr_zero(Value);
++  MaskLen = std::popcount(Value);
+   return true;
+ }
+ 
+@@ -284,26 +285,26 @@ template <> constexpr inline size_t CTLog2<1>() { return 0; }
+ /// (32 bit edition.)
+ /// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
+ inline unsigned Log2_32(uint32_t Value) {
+-  return static_cast<unsigned>(31 - llvm::countl_zero(Value));
++  return static_cast<unsigned>(31 - std::countl_zero(Value));
+ }
+ 
+ /// Return the floor log base 2 of the specified value, -1 if the value is zero.
+ /// (64 bit edition.)
+ inline unsigned Log2_64(uint64_t Value) {
+-  return static_cast<unsigned>(63 - llvm::countl_zero(Value));
++  return static_cast<unsigned>(63 - std::countl_zero(Value));
+ }
+ 
+ /// Return the ceil log base 2 of the specified value, 32 if the value is zero.
+ /// (32 bit edition).
+ /// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
+ inline unsigned Log2_32_Ceil(uint32_t Value) {
+-  return static_cast<unsigned>(32 - llvm::countl_zero(Value - 1));
++  return static_cast<unsigned>(32 - std::countl_zero(Value - 1));
+ }
+ 
+ /// Return the ceil log base 2 of the specified value, 64 if the value is zero.
+ /// (64 bit edition.)
+ inline unsigned Log2_64_Ceil(uint64_t Value) {
+-  return static_cast<unsigned>(64 - llvm::countl_zero(Value - 1));
++  return static_cast<unsigned>(64 - std::countl_zero(Value - 1));
+ }
+ 
+ /// A and B are either alignments or offsets. Return the minimum alignment that
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0030-Remove-DenseMap-GTest-printer-test.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0030-Remove-DenseMap-GTest-printer-test.patch
new file mode 100644
index 0000000..8ee47b8
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0030-Remove-DenseMap-GTest-printer-test.patch
@@ -0,0 +1,26 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Sun, 30 Jul 2023 14:17:37 -0700
+Subject: [PATCH 30/31] Remove DenseMap GTest printer test
+
+LLVM modifies internal GTest headers to support it, which we can't do.
+---
+ llvm/unittests/ADT/DenseMapTest.cpp | 7 -------
+ 1 file changed, 7 deletions(-)
+
+diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
+index 1f232d3046292c0da940ba4bef7d50604556e4c2..8d90d5afea79c619590cc32539e5124d02b1349c 100644
+--- a/llvm/unittests/ADT/DenseMapTest.cpp
++++ b/llvm/unittests/ADT/DenseMapTest.cpp
+@@ -737,11 +737,4 @@ TEST(DenseMapCustomTest, VariantSupport) {
+   EXPECT_FALSE(DenseMapInfo<variant>::isEqual(Keys[2], Keys[2]));
+ }
+ 
+-// Test that gTest prints map entries as pairs instead of opaque objects.
+-// See third-party/unittest/googletest/internal/custom/gtest-printers.h
+-TEST(DenseMapCustomTest, PairPrinting) {
+-  DenseMap<int, std::string_view> Map = {{1, "one"}, {2, "two"}};
+-  EXPECT_EQ(R"({ (1, "one"), (2, "two") })", ::testing::PrintToString(Map));
+-}
+-
+ } // namespace
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0031-Replace-deprecated-std-aligned_storage_t.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0031-Replace-deprecated-std-aligned_storage_t.patch
new file mode 100644
index 0000000..e074f27
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0031-Replace-deprecated-std-aligned_storage_t.patch
@@ -0,0 +1,31 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness <calcmogul@gmail.com>
+Date: Fri, 15 Sep 2023 18:26:50 -0700
+Subject: [PATCH 31/31] Replace deprecated std::aligned_storage_t
+
+---
+ llvm/include/llvm/ADT/FunctionExtras.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/llvm/include/llvm/ADT/FunctionExtras.h b/llvm/include/llvm/ADT/FunctionExtras.h
+index 1daeae915eb506b32a2d1296d2f0fe4e6dab606e..2e60e34d69914bd9b2197fc0a0e75a8e025674b2 100644
+--- a/llvm/include/llvm/ADT/FunctionExtras.h
++++ b/llvm/include/llvm/ADT/FunctionExtras.h
+@@ -37,6 +37,7 @@
+ #include "llvm/ADT/STLForwardCompat.h"
+ #include "llvm/Support/MemAlloc.h"
+ #include "llvm/Support/type_traits.h"
++#include <cstddef>
+ #include <cstring>
+ #include <memory>
+ #include <type_traits>
+@@ -167,8 +168,7 @@ protected:
+     // provide four pointers worth of storage here.
+     // This is mutable as an inlined `const unique_function<void() const>` may
+     // still modify its own mutable members.
+-    mutable std::aligned_storage_t<InlineStorageSize, alignof(void *)>
+-        InlineStorage;
++    alignas(void*) mutable std::byte InlineStorage[InlineStorageSize];
+   } StorageUnion;
+ 
+   // A compressed pointer to either our dispatching callback or our table of
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0032-Fix-compilation-of-MathExtras.h-on-Windows-with-sdl.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0032-Fix-compilation-of-MathExtras.h-on-Windows-with-sdl.patch
new file mode 100644
index 0000000..52956f9
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0032-Fix-compilation-of-MathExtras.h-on-Windows-with-sdl.patch
@@ -0,0 +1,26 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Benjamin Hall <bhallctre@gmail.com>
+Date: Mon, 23 Oct 2023 21:36:40 -0400
+Subject: [PATCH 32/32] Fix compilation of MathExtras.h on Windows with /sdl
+
+See https://github.com/llvm/llvm-project/pull/68978
+---
+ llvm/include/llvm/Support/MathExtras.h | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
+index 5f034b694989d8ef24e0b249abd12a5c20146b97..03db6e4d92cb3b62ac3d8b3cbd97783817c6326b 100644
+--- a/llvm/include/llvm/Support/MathExtras.h
++++ b/llvm/include/llvm/Support/MathExtras.h
+@@ -356,7 +356,10 @@ inline uint64_t alignTo(uint64_t Value, uint64_t Align) {
+ inline uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) {
+   assert(Align != 0 && (Align & (Align - 1)) == 0 &&
+          "Align must be a power of 2");
+-  return (Value + Align - 1) & -Align;
++  // Replace unary minus to avoid compilation error on Windows:
++  // "unary minus operator applied to unsigned type, result still unsigned"
++  uint64_t negAlign = (~Align) + 1;
++  return (Value + Align - 1) & negAlign;
+ }
+ 
+ /// If non-zero \p Skew is specified, the return value will be a minimal integer
diff --git a/third_party/allwpilib/upstream_utils/llvm_patches/0033-raw_ostream-Add-SetNumBytesInBuffer.patch b/third_party/allwpilib/upstream_utils/llvm_patches/0033-raw_ostream-Add-SetNumBytesInBuffer.patch
new file mode 100644
index 0000000..a36cfc6
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/llvm_patches/0033-raw_ostream-Add-SetNumBytesInBuffer.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Sun, 29 Oct 2023 23:00:08 -0700
+Subject: [PATCH 33/33] raw_ostream: Add SetNumBytesInBuffer
+
+---
+ llvm/include/llvm/Support/raw_ostream.h | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
+index 9a9a1f688313a5784a58a70f2cb4cc0d6ec70e79..39f98e4e7696e28587779e90a03995463767b02d 100644
+--- a/llvm/include/llvm/Support/raw_ostream.h
++++ b/llvm/include/llvm/Support/raw_ostream.h
+@@ -356,6 +356,11 @@ protected:
+     SetBufferAndMode(BufferStart, Size, BufferKind::ExternalBuffer);
+   }
+ 
++  /// Force-set the number of bytes in the raw_ostream buffer.
++  void SetNumBytesInBuffer(size_t Size) {
++    OutBufCur = OutBufStart + Size;
++  }
++
+   /// Return an efficient buffer size for the underlying output mechanism.
+   virtual size_t preferred_buffer_size() const;
+ 
diff --git a/third_party/allwpilib/upstream_utils/mpack_patches/0001-Don-t-emit-inline-defs.patch b/third_party/allwpilib/upstream_utils/mpack_patches/0001-Don-t-emit-inline-defs.patch
index a899a5e..0937d13 100644
--- a/third_party/allwpilib/upstream_utils/mpack_patches/0001-Don-t-emit-inline-defs.patch
+++ b/third_party/allwpilib/upstream_utils/mpack_patches/0001-Don-t-emit-inline-defs.patch
@@ -1,14 +1,14 @@
-From 05864e768ca1458c1e24f433d091306a7d47562b Mon Sep 17 00:00:00 2001
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: PJ Reiniger <pj.reiniger@gmail.com>
 Date: Sat, 29 Oct 2022 12:09:03 -0400
-Subject: [PATCH 1/3] Don't emit inline defs
+Subject: [PATCH 1/4] Don't emit inline defs
 
 ---
  src/mpack/mpack-platform.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 diff --git a/src/mpack/mpack-platform.c b/src/mpack/mpack-platform.c
-index 6599e1f..d4a2fa3 100644
+index 6599e1fd444c381b9b72b20601c22488d7266b1f..d4a2fa3198f39fc964baeed4d15f6fd89affc24c 100644
 --- a/src/mpack/mpack-platform.c
 +++ b/src/mpack/mpack-platform.c
 @@ -24,7 +24,7 @@
diff --git a/third_party/allwpilib/upstream_utils/mpack_patches/0002-Update-amalgamation-script.patch b/third_party/allwpilib/upstream_utils/mpack_patches/0002-Update-amalgamation-script.patch
index eabf154..68f6258 100644
--- a/third_party/allwpilib/upstream_utils/mpack_patches/0002-Update-amalgamation-script.patch
+++ b/third_party/allwpilib/upstream_utils/mpack_patches/0002-Update-amalgamation-script.patch
@@ -1,14 +1,14 @@
-From d4d045c843d4b4de747d800e570c32cff3759a80 Mon Sep 17 00:00:00 2001
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: PJ Reiniger <pj.reiniger@gmail.com>
 Date: Sat, 29 Oct 2022 12:16:36 -0400
-Subject: [PATCH 2/3] Update amalgamation script
+Subject: [PATCH 2/4] Update amalgamation script
 
 ---
  tools/amalgamate.sh | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)
 
 diff --git a/tools/amalgamate.sh b/tools/amalgamate.sh
-index 2e24e27..4dfe999 100755
+index 2e24e278e54bbc52967bfe1f8970f4f8d4b238d5..4dfe999ee49ee67fd394e948b0bb3133d6acdbdc 100755
 --- a/tools/amalgamate.sh
 +++ b/tools/amalgamate.sh
 @@ -74,8 +74,8 @@ echo -e "#endif\n" >> $HEADER
diff --git a/third_party/allwpilib/upstream_utils/mpack_patches/0003-Use-namespace-for-C.patch b/third_party/allwpilib/upstream_utils/mpack_patches/0003-Use-namespace-for-C.patch
index 4adb7f8..74f2050 100644
--- a/third_party/allwpilib/upstream_utils/mpack_patches/0003-Use-namespace-for-C.patch
+++ b/third_party/allwpilib/upstream_utils/mpack_patches/0003-Use-namespace-for-C.patch
@@ -1,7 +1,7 @@
-From 37854ea8a4a4b387940719c40bd32792f1e6e027 Mon Sep 17 00:00:00 2001
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: PJ Reiniger <pj.reiniger@gmail.com>
 Date: Sat, 29 Oct 2022 12:22:50 -0400
-Subject: [PATCH 3/3] Use namespace for C++
+Subject: [PATCH 3/4] Use namespace for C++
 
 ---
  src/mpack/mpack-common.c   | 2 ++
@@ -15,7 +15,7 @@
  8 files changed, 15 insertions(+), 2 deletions(-)
 
 diff --git a/src/mpack/mpack-common.c b/src/mpack/mpack-common.c
-index 2c133a3..dc7207f 100644
+index 2c133a3303f7a2a10ac7d11a9eb3ee5a04a8335c..e5b4ab0007df2c4108791534b1aa4f9da4f1742a 100644
 --- a/src/mpack/mpack-common.c
 +++ b/src/mpack/mpack-common.c
 @@ -24,6 +24,7 @@
@@ -33,7 +33,7 @@
 +}  // namespace mpack
  MPACK_SILENCE_WARNINGS_END
 diff --git a/src/mpack/mpack-expect.c b/src/mpack/mpack-expect.c
-index 81576d1..6232a67 100644
+index 81576d1e0e7b942e93a27a6dd5a75a751dba2384..44f00ffc5387b5646d700b174283dec12f2c4962 100644
 --- a/src/mpack/mpack-expect.c
 +++ b/src/mpack/mpack-expect.c
 @@ -24,6 +24,7 @@
@@ -51,7 +51,7 @@
 +}  // namespace mpack
  MPACK_SILENCE_WARNINGS_END
 diff --git a/src/mpack/mpack-node.c b/src/mpack/mpack-node.c
-index 3d4b0f4..aba9897 100644
+index 3d4b0f4e69f9e026ad48dec30ac091a024ef30cf..016531360ef27a2003489c1cb4c9aa2f1a7d0865 100644
 --- a/src/mpack/mpack-node.c
 +++ b/src/mpack/mpack-node.c
 @@ -24,6 +24,7 @@
@@ -69,7 +69,7 @@
 +}  // namespace mpack
  MPACK_SILENCE_WARNINGS_END
 diff --git a/src/mpack/mpack-platform.c b/src/mpack/mpack-platform.c
-index d4a2fa3..75d2de3 100644
+index d4a2fa3198f39fc964baeed4d15f6fd89affc24c..aadeee0f9de0833a5eabe3a7efc41a9ddf5cee70 100644
 --- a/src/mpack/mpack-platform.c
 +++ b/src/mpack/mpack-platform.c
 @@ -30,6 +30,7 @@
@@ -87,7 +87,7 @@
 +}  // namespace mpack
  MPACK_SILENCE_WARNINGS_END
 diff --git a/src/mpack/mpack-platform.h b/src/mpack/mpack-platform.h
-index 79604c9..27a2f9e 100644
+index 79604c90828ebbeae52eb3d6c13afeec6b861fa9..27a2f9eeb10cc8c6aa274a5e1b0c96bead6a37fb 100644
 --- a/src/mpack/mpack-platform.h
 +++ b/src/mpack/mpack-platform.h
 @@ -1043,7 +1043,7 @@ void mpack_assert_fail(const char* message);
@@ -100,7 +100,7 @@
  #else
      #define MPACK_EXTERN_C_BEGIN /*nothing*/
 diff --git a/src/mpack/mpack-reader.c b/src/mpack/mpack-reader.c
-index c6d2223..a135879 100644
+index c6d2223ef8208f4b918b8890ca81915cb2494bb0..e3cbe7a7086f0fcd0a1ce5e5004ce410fb0e457a 100644
 --- a/src/mpack/mpack-reader.c
 +++ b/src/mpack/mpack-reader.c
 @@ -24,6 +24,7 @@
@@ -118,7 +118,7 @@
 +}  // namespace mpack
  MPACK_SILENCE_WARNINGS_END
 diff --git a/src/mpack/mpack-writer.c b/src/mpack/mpack-writer.c
-index 4d052b1..9630d9e 100644
+index 4d052b14d83851a45a4899b96904ed661664008c..272f861979103495627887b0d3edfad117bff748 100644
 --- a/src/mpack/mpack-writer.c
 +++ b/src/mpack/mpack-writer.c
 @@ -24,6 +24,7 @@
@@ -136,7 +136,7 @@
 +}  // namespace mpack
  MPACK_SILENCE_WARNINGS_END
 diff --git a/src/mpack/mpack-writer.h b/src/mpack/mpack-writer.h
-index c239ee6..abeee1a 100644
+index c239ee6441e753cba46f2ab5a6136eead9c30fb4..abeee1a6eb960b0ce61203ae75eb7c7488186860 100644
 --- a/src/mpack/mpack-writer.h
 +++ b/src/mpack/mpack-writer.h
 @@ -1168,6 +1168,7 @@ MPACK_EXTERN_C_END
diff --git a/third_party/allwpilib/upstream_utils/mpack_patches/0004-Group-doxygen-into-MPack-module.patch b/third_party/allwpilib/upstream_utils/mpack_patches/0004-Group-doxygen-into-MPack-module.patch
new file mode 100644
index 0000000..a296623
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/mpack_patches/0004-Group-doxygen-into-MPack-module.patch
@@ -0,0 +1,133 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Ryan Blue <ryanzblue@gmail.com>
+Date: Thu, 8 Jun 2023 19:28:50 -0400
+Subject: [PATCH 4/4] Group doxygen into MPack module
+
+---
+ src/mpack/mpack-common.h   | 3 ++-
+ src/mpack/mpack-expect.h   | 3 ++-
+ src/mpack/mpack-node.h     | 3 ++-
+ src/mpack/mpack-platform.h | 3 ++-
+ src/mpack/mpack-reader.h   | 3 ++-
+ src/mpack/mpack-writer.h   | 3 ++-
+ src/mpack/mpack.h          | 6 ++++++
+ tools/amalgamate.sh        | 5 +++++
+ 8 files changed, 23 insertions(+), 6 deletions(-)
+
+diff --git a/src/mpack/mpack-common.h b/src/mpack/mpack-common.h
+index 1c6b8d472f9591575973db8f7573ce98c5a6169d..14f7dd05c0f9cdc3689bb357f8aac9c8d0671f45 100644
+--- a/src/mpack/mpack-common.h
++++ b/src/mpack/mpack-common.h
+@@ -40,7 +40,8 @@ MPACK_EXTERN_C_BEGIN
+ 
+ 
+ /**
+- * @defgroup common Tags and Common Elements
++ * @defgroup mpack_common Tags and Common Elements
++ * @ingroup mpack
+  *
+  * Contains types, constants and functions shared by both the encoding
+  * and decoding portions of MPack.
+diff --git a/src/mpack/mpack-expect.h b/src/mpack/mpack-expect.h
+index 02a1abc0cbe0250636d4a82c21517fba36484608..e62536e46bcab4fdf6bca1d3ea74ee5d9e8e7bd8 100644
+--- a/src/mpack/mpack-expect.h
++++ b/src/mpack/mpack-expect.h
+@@ -40,7 +40,8 @@ MPACK_EXTERN_C_BEGIN
+ #endif
+ 
+ /**
+- * @defgroup expect Expect API
++ * @defgroup mpack_expect Expect API
++ * @ingroup mpack
+  *
+  * The MPack Expect API allows you to easily read MessagePack data when you
+  * expect it to follow a predefined schema.
+diff --git a/src/mpack/mpack-node.h b/src/mpack/mpack-node.h
+index 52142c4f9965c690e4227a6f89354e1e65b9b6a4..4c1289c834398a20f0c25f86e17e57a31832d623 100644
+--- a/src/mpack/mpack-node.h
++++ b/src/mpack/mpack-node.h
+@@ -36,7 +36,8 @@ MPACK_EXTERN_C_BEGIN
+ #if MPACK_NODE
+ 
+ /**
+- * @defgroup node Node API
++ * @defgroup mpack_node Node API
++ * @ingroup mpack
+  *
+  * The MPack Node API allows you to parse a chunk of MessagePack into a
+  * dynamically typed data structure, providing random access to the parsed
+diff --git a/src/mpack/mpack-platform.h b/src/mpack/mpack-platform.h
+index 27a2f9eeb10cc8c6aa274a5e1b0c96bead6a37fb..3a100564c11d12494cd0f607645c8cfa5dd86dec 100644
+--- a/src/mpack/mpack-platform.h
++++ b/src/mpack/mpack-platform.h
+@@ -40,7 +40,8 @@
+ 
+ 
+ /**
+- * @defgroup config Configuration Options
++ * @defgroup mpack_config Configuration Options
++ * @ingroup mpack
+  *
+  * Defines the MPack configuration options.
+  *
+diff --git a/src/mpack/mpack-reader.h b/src/mpack/mpack-reader.h
+index 092e2ba2c36f8709b912d24b4d12974e81c82678..f981683512a60c0c2b5363b7f27cbd874c1b44f8 100644
+--- a/src/mpack/mpack-reader.h
++++ b/src/mpack/mpack-reader.h
+@@ -44,7 +44,8 @@ struct mpack_track_t;
+ #define MPACK_READER_SMALL_FRACTION_DENOMINATOR 32
+ 
+ /**
+- * @defgroup reader Reader API
++ * @defgroup mpack_reader Reader API
++ * @ingroup mpack
+  *
+  * The MPack Reader API contains functions for imperatively reading dynamically
+  * typed data from a MessagePack stream.
+diff --git a/src/mpack/mpack-writer.h b/src/mpack/mpack-writer.h
+index abeee1a6eb960b0ce61203ae75eb7c7488186860..03fdb7592b0bcaeeeaaf2f8c64c0fb30ba5e1292 100644
+--- a/src/mpack/mpack-writer.h
++++ b/src/mpack/mpack-writer.h
+@@ -40,7 +40,8 @@ struct mpack_track_t;
+ #endif
+ 
+ /**
+- * @defgroup writer Write API
++ * @defgroup mpack_writer Write API
++ * @ingroup mpack
+  *
+  * The MPack Write API encodes structured data of a fixed (hardcoded) schema to MessagePack.
+  *
+diff --git a/src/mpack/mpack.h b/src/mpack/mpack.h
+index 129a276115d85e09d717504df3736a7614fbd2ea..9739205efd0406a1bd05c728556c8f36368b1528 100644
+--- a/src/mpack/mpack.h
++++ b/src/mpack/mpack.h
+@@ -29,6 +29,12 @@
+ #ifndef MPACK_H
+ #define MPACK_H 1
+ 
++/**
++ * @defgroup mpack MPack
++ * MPack is a C implementation of an encoder and decoder for the MessagePack
++ * serialization format.
++ */
++
+ #include "mpack-common.h"
+ #include "mpack-writer.h"
+ #include "mpack-reader.h"
+diff --git a/tools/amalgamate.sh b/tools/amalgamate.sh
+index 4dfe999ee49ee67fd394e948b0bb3133d6acdbdc..ebff08200198406940720efe093df909a31caf65 100755
+--- a/tools/amalgamate.sh
++++ b/tools/amalgamate.sh
+@@ -66,6 +66,11 @@ echo -e "#define MPACK_AMALGAMATED 1\n" >> $HEADER
+ echo -e "#if defined(MPACK_HAS_CONFIG) && MPACK_HAS_CONFIG" >> $HEADER
+ echo -e "#include \"mpack-config.h\"" >> $HEADER
+ echo -e "#endif\n" >> $HEADER
++echo -e "/**" >> $HEADER
++echo -e " * @defgroup mpack MPack" >> $HEADER
++echo -e " * MPack is a C implementation of an encoder and decoder for the MessagePack" >> $HEADER
++echo -e " * serialization format." >> $HEADER
++echo -e " */" >> $HEADER
+ for f in $HEADERS; do
+     echo -e "\n/* $f.h */" >> $HEADER
+     sed -e 's@^#include ".*@/* & */@' -e '0,/^ \*\/$/d' src/$f >> $HEADER
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0001-Fix-sign-compare-warnings.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0001-Fix-sign-compare-warnings.patch
new file mode 100644
index 0000000..e916c0f
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0001-Fix-sign-compare-warnings.patch
@@ -0,0 +1,325 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Sat, 10 Jun 2023 14:13:07 -0700
+Subject: [PATCH 01/11] Fix sign-compare warnings
+
+---
+ src/google/protobuf/compiler/importer.cc                  | 2 +-
+ src/google/protobuf/compiler/parser.cc                    | 4 ++--
+ src/google/protobuf/io/io_win32.cc                        | 2 +-
+ src/google/protobuf/stubs/stringprintf.cc                 | 4 ++--
+ src/google/protobuf/stubs/strutil.cc                      | 2 +-
+ src/google/protobuf/stubs/substitute.cc                   | 2 +-
+ src/google/protobuf/util/field_mask_util.cc               | 2 +-
+ src/google/protobuf/util/internal/datapiece.cc            | 7 +++++++
+ .../protobuf/util/internal/default_value_objectwriter.cc  | 4 ++--
+ .../protobuf/util/internal/default_value_objectwriter.h   | 2 +-
+ src/google/protobuf/util/internal/json_escaping.cc        | 6 +++---
+ src/google/protobuf/util/internal/json_objectwriter.h     | 2 +-
+ src/google/protobuf/util/internal/json_stream_parser.cc   | 8 ++++----
+ src/google/protobuf/util/internal/proto_writer.cc         | 2 +-
+ .../protobuf/util/internal/protostream_objectwriter.cc    | 2 +-
+ src/google/protobuf/util/internal/utility.cc              | 2 +-
+ src/google/protobuf/util/json_util.cc                     | 2 +-
+ 17 files changed, 31 insertions(+), 24 deletions(-)
+
+diff --git a/src/google/protobuf/compiler/importer.cc b/src/google/protobuf/compiler/importer.cc
+index f1e26f8bdd1d3619acd8827f9a2a0e6b2acdd124..678e87eb03cc3959a1890327cd1e918cb1896fa3 100644
+--- a/src/google/protobuf/compiler/importer.cc
++++ b/src/google/protobuf/compiler/importer.cc
+@@ -398,7 +398,7 @@ DiskSourceTree::DiskFileToVirtualFile(const std::string& disk_file,
+   int mapping_index = -1;
+   std::string canonical_disk_file = CanonicalizePath(disk_file);
+ 
+-  for (int i = 0; i < mappings_.size(); i++) {
++  for (size_t i = 0; i < mappings_.size(); i++) {
+     // Apply the mapping in reverse.
+     if (ApplyMapping(canonical_disk_file, mappings_[i].disk_path,
+                      mappings_[i].virtual_path, virtual_file)) {
+diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc
+index 5bd37d147bc449444f875f89367a208a32a9146e..e36a4a74359fcace20c017f241d58930660b9381 100644
+--- a/src/google/protobuf/compiler/parser.cc
++++ b/src/google/protobuf/compiler/parser.cc
+@@ -159,7 +159,7 @@ bool IsLowerUnderscore(const std::string& name) {
+ }
+ 
+ bool IsNumberFollowUnderscore(const std::string& name) {
+-  for (int i = 1; i < name.length(); i++) {
++  for (size_t i = 1; i < name.length(); i++) {
+     const char c = name[i];
+     if (IsNumber(c) && name[i - 1] == '_') {
+       return true;
+@@ -500,7 +500,7 @@ void Parser::LocationRecorder::AttachComments(
+   if (!trailing->empty()) {
+     location_->mutable_trailing_comments()->swap(*trailing);
+   }
+-  for (int i = 0; i < detached_comments->size(); ++i) {
++  for (size_t i = 0; i < detached_comments->size(); ++i) {
+     location_->add_leading_detached_comments()->swap((*detached_comments)[i]);
+   }
+   detached_comments->clear();
+diff --git a/src/google/protobuf/io/io_win32.cc b/src/google/protobuf/io/io_win32.cc
+index 4e8190880918f1ba155d75db76d6c1ee0b003247..78c07d0d771b9c227c6cd930fc91d272fd67500f 100644
+--- a/src/google/protobuf/io/io_win32.cc
++++ b/src/google/protobuf/io/io_win32.cc
+@@ -198,7 +198,7 @@ wstring normalize(wstring path) {
+   // Join all segments.
+   bool first = true;
+   std::wstringstream result;
+-  for (int i = 0; i < segments.size(); ++i) {
++  for (size_t i = 0; i < segments.size(); ++i) {
+     if (!first) {
+       result << L'\\';
+     }
+diff --git a/src/google/protobuf/stubs/stringprintf.cc b/src/google/protobuf/stubs/stringprintf.cc
+index a6ad4c0da4080f5241c26176046a3add5247e25c..8b890f47c386f0d6b0ab9fd9928fae03edd076eb 100644
+--- a/src/google/protobuf/stubs/stringprintf.cc
++++ b/src/google/protobuf/stubs/stringprintf.cc
+@@ -149,10 +149,10 @@ std::string StringPrintfVector(const char* format,
+   // or displaying random chunks of memory to users.
+ 
+   const char* cstr[kStringPrintfVectorMaxArgs];
+-  for (int i = 0; i < v.size(); ++i) {
++  for (size_t i = 0; i < v.size(); ++i) {
+     cstr[i] = v[i].c_str();
+   }
+-  for (int i = v.size(); i < GOOGLE_ARRAYSIZE(cstr); ++i) {
++  for (size_t i = v.size(); i < GOOGLE_ARRAYSIZE(cstr); ++i) {
+     cstr[i] = &string_printf_empty_block[0];
+   }
+ 
+diff --git a/src/google/protobuf/stubs/strutil.cc b/src/google/protobuf/stubs/strutil.cc
+index 594c8eac6a6ebff6d8bc8cc8518e3fd521f24da1..3462e91ff273dc071628f06b91698a0f166514fc 100644
+--- a/src/google/protobuf/stubs/strutil.cc
++++ b/src/google/protobuf/stubs/strutil.cc
+@@ -697,7 +697,7 @@ bool safe_parse_positive_int(std::string text, IntType *value_p) {
+   IntType value = 0;
+   const IntType vmax = std::numeric_limits<IntType>::max();
+   assert(vmax > 0);
+-  assert(vmax >= base);
++  assert(static_cast<int>(vmax) >= base);
+   const IntType vmax_over_base = vmax / base;
+   const char* start = text.data();
+   const char* end = start + text.size();
+diff --git a/src/google/protobuf/stubs/substitute.cc b/src/google/protobuf/stubs/substitute.cc
+index d301682ee3377760430839bc5d6530621333e48d..8c75b2562e43d9d4ade3ef187d38e2e81b43e2c7 100644
+--- a/src/google/protobuf/stubs/substitute.cc
++++ b/src/google/protobuf/stubs/substitute.cc
+@@ -128,7 +128,7 @@ void SubstituteAndAppend(std::string* output, const char* format,
+     }
+   }
+ 
+-  GOOGLE_DCHECK_EQ(target - output->data(), output->size());
++  GOOGLE_DCHECK_EQ(target - output->data(), static_cast<int>(output->size()));
+ }
+ 
+ }  // namespace strings
+diff --git a/src/google/protobuf/util/field_mask_util.cc b/src/google/protobuf/util/field_mask_util.cc
+index 700e59004a083c731477bcc0bb4d5c36d06f306c..9a40b851a9e51d30b286ff5d89707bf9f279d0c0 100644
+--- a/src/google/protobuf/util/field_mask_util.cc
++++ b/src/google/protobuf/util/field_mask_util.cc
+@@ -366,7 +366,7 @@ void FieldMaskTree::RemovePath(const std::string& path,
+   Node* node = &root_;
+   const Descriptor* current_descriptor = descriptor;
+   Node* new_branch_node = nullptr;
+-  for (int i = 0; i < parts.size(); ++i) {
++  for (size_t i = 0; i < parts.size(); ++i) {
+     nodes[i] = node;
+     const FieldDescriptor* field_descriptor =
+         current_descriptor->FindFieldByName(parts[i]);
+diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc
+index 3e7aa84da7181e2ab270e181b9f63deb1905542f..56f4a18fa4afc64708938fa5352937cdd17b5747 100644
+--- a/src/google/protobuf/util/internal/datapiece.cc
++++ b/src/google/protobuf/util/internal/datapiece.cc
+@@ -53,6 +53,10 @@ namespace {
+ 
+ template <typename To, typename From>
+ util::StatusOr<To> ValidateNumberConversion(To after, From before) {
++#ifdef __GNUC__
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wsign-compare"
++#endif
+   if (after == before &&
+       MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) {
+     return after;
+@@ -62,6 +66,9 @@ util::StatusOr<To> ValidateNumberConversion(To after, From before) {
+         : std::is_same<From, double>::value ? DoubleAsString(before)
+                                             : FloatAsString(before));
+   }
++#ifdef __GNUC__
++#pragma GCC diagnostic pop
++#endif
+ }
+ 
+ // For general conversion between
+diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc
+index 7f61cdafa7c771a69364c5e9c49667535b16d957..a7d4ce78bd47e0250def474df8937927be9ef116 100644
+--- a/src/google/protobuf/util/internal/default_value_objectwriter.cc
++++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc
+@@ -312,7 +312,7 @@ void DefaultValueObjectWriter::Node::PopulateChildren(
+   std::unordered_map<std::string, int> orig_children_map;
+ 
+   // Creates a map of child nodes to speed up lookup.
+-  for (int i = 0; i < children_.size(); ++i) {
++  for (size_t i = 0; i < children_.size(); ++i) {
+     InsertIfNotPresent(&orig_children_map, children_[i]->name_, i);
+   }
+ 
+@@ -389,7 +389,7 @@ void DefaultValueObjectWriter::Node::PopulateChildren(
+     new_children.push_back(child.release());
+   }
+   // Adds all leftover nodes in children_ to the beginning of new_child.
+-  for (int i = 0; i < children_.size(); ++i) {
++  for (size_t i = 0; i < children_.size(); ++i) {
+     if (children_[i] == nullptr) {
+       continue;
+     }
+diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.h b/src/google/protobuf/util/internal/default_value_objectwriter.h
+index a9e1673fa1e4ed35ab6890a44eed1d362265d914..1a151ab25951f8b0e1c9c724253b16524b88530a 100644
+--- a/src/google/protobuf/util/internal/default_value_objectwriter.h
++++ b/src/google/protobuf/util/internal/default_value_objectwriter.h
+@@ -154,7 +154,7 @@ class PROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
+          bool preserve_proto_field_names, bool use_ints_for_enums,
+          FieldScrubCallBack field_scrub_callback);
+     virtual ~Node() {
+-      for (int i = 0; i < children_.size(); ++i) {
++      for (size_t i = 0; i < children_.size(); ++i) {
+         delete children_[i];
+       }
+     }
+diff --git a/src/google/protobuf/util/internal/json_escaping.cc b/src/google/protobuf/util/internal/json_escaping.cc
+index e4fa8cf788642e4a9d9c0460c287b1c24891b9fa..c192ddca7aff3984ffcbf82e13585bdf34ad8aae 100644
+--- a/src/google/protobuf/util/internal/json_escaping.cc
++++ b/src/google/protobuf/util/internal/json_escaping.cc
+@@ -179,7 +179,7 @@ bool ReadCodePoint(StringPiece str, int index, uint32_t* cp,
+     // the last unicode code point.
+     *num_read = 0;
+   }
+-  while (*num_left > 0 && index < str.size()) {
++  while (*num_left > 0 && index < static_cast<int>(str.size())) {
+     uint32_t ch = static_cast<uint8_t>(str[index++]);
+     --(*num_left);
+     ++(*num_read);
+@@ -309,7 +309,7 @@ void JsonEscaping::Escape(strings::ByteSource* input,
+   while (input->Available() > 0) {
+     StringPiece str = input->Peek();
+     StringPiece escaped;
+-    int i = 0;
++    size_t i = 0;
+     int num_read;
+     bool ok;
+     bool cp_was_split = num_left > 0;
+@@ -349,7 +349,7 @@ void JsonEscaping::Escape(StringPiece input, strings::ByteSink* output) {
+   const char* p = input.data();
+ 
+   bool can_skip_escaping = true;
+-  for (int i = 0; i < len; i++) {
++  for (size_t i = 0; i < len; i++) {
+     char c = p[i];
+     if (c < 0x20 || c >= 0x7F || c == '"' || c == '<' || c == '>' ||
+         c == '\\') {
+diff --git a/src/google/protobuf/util/internal/json_objectwriter.h b/src/google/protobuf/util/internal/json_objectwriter.h
+index cb7dff6e9fe79858a73b2c7501106fe8d05ccac5..92348da3b5c3f07e6146136352f976c94fe54340 100644
+--- a/src/google/protobuf/util/internal/json_objectwriter.h
++++ b/src/google/protobuf/util/internal/json_objectwriter.h
+@@ -102,7 +102,7 @@ class PROTOBUF_EXPORT JsonObjectWriter : public StructuredObjectWriter {
+     if (!indent_string.empty()) {
+       indent_char_ = indent_string[0];
+       indent_count_ = indent_string.length();
+-      for (int i = 1; i < indent_string.length(); i++) {
++      for (size_t i = 1; i < indent_string.length(); i++) {
+         if (indent_char_ != indent_string_[i]) {
+           indent_char_ = '\0';
+           indent_count_ = 0;
+diff --git a/src/google/protobuf/util/internal/json_stream_parser.cc b/src/google/protobuf/util/internal/json_stream_parser.cc
+index 5c34dbccafb9f40249ba3c0b7318b2e897f203dc..e786781f6de23c8a7ea282d061df6032111f6d21 100644
+--- a/src/google/protobuf/util/internal/json_stream_parser.cc
++++ b/src/google/protobuf/util/internal/json_stream_parser.cc
+@@ -80,7 +80,7 @@ inline void ReplaceInvalidCodePoints(StringPiece str,
+                                      const std::string& replacement,
+                                      std::string* dst) {
+   while (!str.empty()) {
+-    int n_valid_bytes = internal::UTF8SpnStructurallyValid(str);
++    size_t n_valid_bytes = internal::UTF8SpnStructurallyValid(str);
+     StringPiece valid_part = str.substr(0, n_valid_bytes);
+     StrAppend(dst, valid_part);
+ 
+@@ -98,7 +98,7 @@ inline void ReplaceInvalidCodePoints(StringPiece str,
+ 
+ static bool ConsumeKey(StringPiece* input, StringPiece* key) {
+   if (input->empty() || !IsLetter((*input)[0])) return false;
+-  int len = 1;
++  size_t len = 1;
+   for (; len < input->size(); ++len) {
+     if (!IsAlphanumeric((*input)[len])) {
+       break;
+@@ -113,7 +113,7 @@ static bool ConsumeKey(StringPiece* input, StringPiece* key) {
+ static bool ConsumeKeyPermissive(StringPiece* input,
+                                  StringPiece* key) {
+   if (input->empty() || !IsLetter((*input)[0])) return false;
+-  int len = 1;
++  size_t len = 1;
+   for (; len < input->size(); ++len) {
+     if (IsKeySeparator((*input)[len])) {
+       break;
+@@ -946,7 +946,7 @@ util::Status JsonStreamParser::ParseKey() {
+ JsonStreamParser::TokenType JsonStreamParser::GetNextTokenType() {
+   SkipWhitespace();
+ 
+-  int size = p_.size();
++  size_t size = p_.size();
+   if (size == 0) {
+     // If we ran out of data, report unknown and we'll place the previous parse
+     // type onto the stack and try again when we have more data.
+diff --git a/src/google/protobuf/util/internal/proto_writer.cc b/src/google/protobuf/util/internal/proto_writer.cc
+index afa5e2e474b6960b8826a40b73615d5dffd971de..11b6df13d8f4f9506e828c39d6e74bc8acceb23d 100644
+--- a/src/google/protobuf/util/internal/proto_writer.cc
++++ b/src/google/protobuf/util/internal/proto_writer.cc
+@@ -408,7 +408,7 @@ std::string ProtoWriter::ProtoElement::ToString() const {
+     if (!ow_->IsRepeated(*(now->parent_field_)) ||
+         now->parent()->parent_field_ != now->parent_field_) {
+       std::string name = now->parent_field_->name();
+-      int i = 0;
++      size_t i = 0;
+       while (i < name.size() &&
+              (ascii_isalnum(name[i]) || name[i] == '_'))
+         ++i;
+diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc
+index ecb219e06e514b1a6ba0e3e343126a75852d0a1d..ce94cfcefb417203f80142c54003efea283f6a1c 100644
+--- a/src/google/protobuf/util/internal/protostream_objectwriter.cc
++++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc
+@@ -378,7 +378,7 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
+ 
+   // Now we know the proto type and can interpret all data fields we gathered
+   // before the "@type" field.
+-  for (int i = 0; i < uninterpreted_events_.size(); ++i) {
++  for (size_t i = 0; i < uninterpreted_events_.size(); ++i) {
+     uninterpreted_events_[i].Replay(this);
+   }
+ }
+diff --git a/src/google/protobuf/util/internal/utility.cc b/src/google/protobuf/util/internal/utility.cc
+index 918ee17d9b040ae1bf9d98e3f46f75770c471393..3c4ac086d594d67b334cbc1dc046c281cd59a374 100644
+--- a/src/google/protobuf/util/internal/utility.cc
++++ b/src/google/protobuf/util/internal/utility.cc
+@@ -345,7 +345,7 @@ void DeleteWellKnownTypes() { delete well_known_types_; }
+ 
+ void InitWellKnownTypes() {
+   well_known_types_ = new std::set<std::string>;
+-  for (int i = 0; i < GOOGLE_ARRAYSIZE(well_known_types_name_array_); ++i) {
++  for (size_t i = 0; i < GOOGLE_ARRAYSIZE(well_known_types_name_array_); ++i) {
+     well_known_types_->insert(well_known_types_name_array_[i]);
+   }
+   google::protobuf::internal::OnShutdown(&DeleteWellKnownTypes);
+diff --git a/src/google/protobuf/util/json_util.cc b/src/google/protobuf/util/json_util.cc
+index c39c10d87b7d8bf6fc18cae1ce459257c45945d6..a9b1c52a73c86d3e3655ba0748f2a82c68bd40ce 100644
+--- a/src/google/protobuf/util/json_util.cc
++++ b/src/google/protobuf/util/json_util.cc
+@@ -64,7 +64,7 @@ ZeroCopyStreamByteSink::~ZeroCopyStreamByteSink() {
+ 
+ void ZeroCopyStreamByteSink::Append(const char* bytes, size_t len) {
+   while (true) {
+-    if (len <= buffer_size_) {  // NOLINT
++    if (static_cast<int>(len) <= buffer_size_) {  // NOLINT
+       memcpy(buffer_, bytes, len);
+       buffer_ = static_cast<char*>(buffer_) + len;
+       buffer_size_ -= len;
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0002-Remove-redundant-move.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0002-Remove-redundant-move.patch
new file mode 100644
index 0000000..09ab3c5
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0002-Remove-redundant-move.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Sat, 10 Jun 2023 14:41:39 -0700
+Subject: [PATCH 02/11] Remove redundant move
+
+---
+ src/google/protobuf/extension_set.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/google/protobuf/extension_set.h b/src/google/protobuf/extension_set.h
+index 0e6d0521104d7f721bdbad1dd593233b035c5b85..b5343689ef7c16442995746439bbe8928022c593 100644
+--- a/src/google/protobuf/extension_set.h
++++ b/src/google/protobuf/extension_set.h
+@@ -714,7 +714,7 @@ class PROTOBUF_EXPORT ExtensionSet {
+   static KeyValueFunctor ForEach(Iterator begin, Iterator end,
+                                  KeyValueFunctor func) {
+     for (Iterator it = begin; it != end; ++it) func(it->first, it->second);
+-    return std::move(func);
++    return func;
+   }
+ 
+   // Applies a functor to the <int, Extension&> pairs in sorted order.
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0003-Fix-maybe-uninitialized-warnings.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0003-Fix-maybe-uninitialized-warnings.patch
new file mode 100644
index 0000000..40ee920
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0003-Fix-maybe-uninitialized-warnings.patch
@@ -0,0 +1,168 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Sat, 10 Jun 2023 15:00:20 -0700
+Subject: [PATCH 03/11] Fix maybe-uninitialized warnings
+
+---
+ src/google/protobuf/arena.cc                     |  6 +++---
+ src/google/protobuf/arena_impl.h                 |  4 ++--
+ src/google/protobuf/extension_set_inl.h          |  2 +-
+ .../protobuf/generated_message_tctable_lite.cc   | 16 ++++++++--------
+ src/google/protobuf/io/coded_stream.cc           |  2 +-
+ 5 files changed, 15 insertions(+), 15 deletions(-)
+
+diff --git a/src/google/protobuf/arena.cc b/src/google/protobuf/arena.cc
+index 6ba508a5f0f3436d52ccc326cc932ceed3cd79fa..194404213ee5f6ad05b1e01f2bff23859d60dc56 100644
+--- a/src/google/protobuf/arena.cc
++++ b/src/google/protobuf/arena.cc
+@@ -411,7 +411,7 @@ uint64_t ThreadSafeArena::Reset() {
+ std::pair<void*, SerialArena::CleanupNode*>
+ ThreadSafeArena::AllocateAlignedWithCleanup(size_t n,
+                                             const std::type_info* type) {
+-  SerialArena* arena;
++  SerialArena* arena = nullptr;
+   if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
+                             GetSerialArenaFast(&arena))) {
+     return arena->AllocateAlignedWithCleanup(n, alloc_policy_.get());
+@@ -421,7 +421,7 @@ ThreadSafeArena::AllocateAlignedWithCleanup(size_t n,
+ }
+ 
+ void ThreadSafeArena::AddCleanup(void* elem, void (*cleanup)(void*)) {
+-  SerialArena* arena;
++  SerialArena* arena = nullptr;
+   if (PROTOBUF_PREDICT_FALSE(!GetSerialArenaFast(&arena))) {
+     arena = GetSerialArenaFallback(&thread_cache());
+   }
+@@ -433,7 +433,7 @@ void* ThreadSafeArena::AllocateAlignedFallback(size_t n,
+                                                const std::type_info* type) {
+   if (alloc_policy_.should_record_allocs()) {
+     alloc_policy_.RecordAlloc(type, n);
+-    SerialArena* arena;
++    SerialArena* arena = nullptr;
+     if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
+       return arena->AllocateAligned(n, alloc_policy_.get());
+     }
+diff --git a/src/google/protobuf/arena_impl.h b/src/google/protobuf/arena_impl.h
+index 76727688b558354be18f0c6576db1d4619348ef4..8af70c48f13feece7cf8580e71845ec267ddaddb 100644
+--- a/src/google/protobuf/arena_impl.h
++++ b/src/google/protobuf/arena_impl.h
+@@ -482,7 +482,7 @@ class PROTOBUF_EXPORT ThreadSafeArena {
+ 
+   template <AllocationClient alloc_client = AllocationClient::kDefault>
+   void* AllocateAligned(size_t n, const std::type_info* type) {
+-    SerialArena* arena;
++    SerialArena* arena = nullptr;
+     if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
+                               GetSerialArenaFast(&arena))) {
+       return arena->AllocateAligned<alloc_client>(n, AllocPolicy());
+@@ -492,7 +492,7 @@ class PROTOBUF_EXPORT ThreadSafeArena {
+   }
+ 
+   void ReturnArrayMemory(void* p, size_t size) {
+-    SerialArena* arena;
++    SerialArena* arena = nullptr;
+     if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
+       arena->ReturnArrayMemory(p, size);
+     }
+diff --git a/src/google/protobuf/extension_set_inl.h b/src/google/protobuf/extension_set_inl.h
+index e4e711721d4d8690e1e87fc8e31df1b5836d4fd7..50c04cd41feccf6cb5fda2740d6d4adb874645d7 100644
+--- a/src/google/protobuf/extension_set_inl.h
++++ b/src/google/protobuf/extension_set_inl.h
+@@ -206,7 +206,7 @@ const char* ExtensionSet::ParseMessageSetItemTmpl(
+     const char* ptr, const Msg* extendee, internal::InternalMetadata* metadata,
+     internal::ParseContext* ctx) {
+   std::string payload;
+-  uint32_t type_id;
++  uint32_t type_id = 0;
+   enum class State { kNoTag, kHasType, kHasPayload, kDone };
+   State state = State::kNoTag;
+ 
+diff --git a/src/google/protobuf/generated_message_tctable_lite.cc b/src/google/protobuf/generated_message_tctable_lite.cc
+index 9993811dca3229edc766061c4a8d54933bcb0eba..2268b2be4d4c60c545765469549d73c6a468dac8 100644
+--- a/src/google/protobuf/generated_message_tctable_lite.cc
++++ b/src/google/protobuf/generated_message_tctable_lite.cc
+@@ -770,7 +770,7 @@ PROTOBUF_NOINLINE const char* TcParser::SingularVarBigint(
+   };
+   volatile Spill spill = {data.data, msg, table, hasbits};
+ 
+-  uint64_t tmp;
++  uint64_t tmp = 0;
+   PROTOBUF_ASSUME(static_cast<int8_t>(*ptr) < 0);
+   ptr = ParseVarint(ptr, &tmp);
+ 
+@@ -845,7 +845,7 @@ PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedVarint(
+   auto expected_tag = UnalignedLoad<TagType>(ptr);
+   do {
+     ptr += sizeof(TagType);
+-    uint64_t tmp;
++    uint64_t tmp = 0;
+     ptr = ParseVarint(ptr, &tmp);
+     if (ptr == nullptr) {
+       return Error(PROTOBUF_TC_PARAM_PASS);
+@@ -1001,7 +1001,7 @@ PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularEnum(
+   }
+   const char* ptr2 = ptr;  // Save for unknown enum case
+   ptr += sizeof(TagType);  // Consume tag
+-  uint64_t tmp;
++  uint64_t tmp = 0;
+   ptr = ParseVarint(ptr, &tmp);
+   if (ptr == nullptr) {
+     return Error(PROTOBUF_TC_PARAM_PASS);
+@@ -1052,7 +1052,7 @@ PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedEnum(
+   do {
+     const char* ptr2 = ptr;  // save for unknown enum case
+     ptr += sizeof(TagType);
+-    uint64_t tmp;
++    uint64_t tmp = 0;
+     ptr = ParseVarint(ptr, &tmp);
+     if (ptr == nullptr) {
+       return Error(PROTOBUF_TC_PARAM_PASS);
+@@ -1477,7 +1477,7 @@ const char* TcParser::MpVarint(PROTOBUF_TC_PARAM_DECL) {
+ 
+   // Parse the value:
+   const char* ptr2 = ptr;  // save for unknown enum case
+-  uint64_t tmp;
++  uint64_t tmp = 0;
+   ptr = ParseVarint(ptr, &tmp);
+   if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+ 
+@@ -1542,7 +1542,7 @@ const char* TcParser::MpRepeatedVarint(PROTOBUF_TC_PARAM_DECL) {
+     const char* ptr2 = ptr;
+     uint32_t next_tag;
+     do {
+-      uint64_t tmp;
++      uint64_t tmp = 0;
+       ptr = ParseVarint(ptr2, &tmp);
+       if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+       field.Add(is_zigzag ? WireFormatLite::ZigZagDecode64(tmp) : tmp);
+@@ -1554,7 +1554,7 @@ const char* TcParser::MpRepeatedVarint(PROTOBUF_TC_PARAM_DECL) {
+     const char* ptr2 = ptr;
+     uint32_t next_tag;
+     do {
+-      uint64_t tmp;
++      uint64_t tmp = 0;
+       ptr = ParseVarint(ptr2, &tmp);
+       if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+       if (is_validated_enum) {
+@@ -1575,7 +1575,7 @@ const char* TcParser::MpRepeatedVarint(PROTOBUF_TC_PARAM_DECL) {
+     const char* ptr2 = ptr;
+     uint32_t next_tag;
+     do {
+-      uint64_t tmp;
++      uint64_t tmp = 0;
+       ptr = ParseVarint(ptr2, &tmp);
+       if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+       field.Add(static_cast<bool>(tmp));
+diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc
+index 487e1b8a379b86bfa3097d68a64ee0a727d36cab..53997901f333292f71ac52e7f9c876bd918f7bf6 100644
+--- a/src/google/protobuf/io/coded_stream.cc
++++ b/src/google/protobuf/io/coded_stream.cc
+@@ -462,7 +462,7 @@ int64_t CodedInputStream::ReadVarint32Fallback(uint32_t first_byte_or_zero) {
+       (buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) {
+     GOOGLE_DCHECK_NE(first_byte_or_zero, 0)
+         << "Caller should provide us with *buffer_ when buffer is non-empty";
+-    uint32_t temp;
++    uint32_t temp = 0;
+     ::std::pair<bool, const uint8_t*> p =
+         ReadVarint32FromArray(first_byte_or_zero, buffer_, &temp);
+     if (!p.first) return -1;
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0004-Fix-coded_stream-WriteRaw.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0004-Fix-coded_stream-WriteRaw.patch
new file mode 100644
index 0000000..90fe162
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0004-Fix-coded_stream-WriteRaw.patch
@@ -0,0 +1,38 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Sat, 10 Jun 2023 15:03:38 -0700
+Subject: [PATCH 04/11] Fix coded_stream WriteRaw
+
+---
+ src/google/protobuf/implicit_weak_message.h | 2 +-
+ src/google/protobuf/io/coded_stream.h       | 4 ++--
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/google/protobuf/implicit_weak_message.h b/src/google/protobuf/implicit_weak_message.h
+index b894ab4809090011f381828857d1674a6b7bff9b..c358c14c06f773d87f5089c75566a5a6fcba13e6 100644
+--- a/src/google/protobuf/implicit_weak_message.h
++++ b/src/google/protobuf/implicit_weak_message.h
+@@ -100,7 +100,7 @@ class PROTOBUF_EXPORT ImplicitWeakMessage : public MessageLite {
+     if (data_ == nullptr) {
+       return target;
+     }
+-    return stream->WriteRaw(data_->data(), static_cast<int>(data_->size()),
++    return stream->WriteRaw(data_->data(), data_->size(),
+                             target);
+   }
+ 
+diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h
+index c8fc994f916d047c0a7b176e53c9e946ebd752de..6c0dd4ab4099d1d748957af8bfc5f8c59c2aa3d6 100644
+--- a/src/google/protobuf/io/coded_stream.h
++++ b/src/google/protobuf/io/coded_stream.h
+@@ -677,8 +677,8 @@ class PROTOBUF_EXPORT EpsCopyOutputStream {
+     return ptr;
+   }
+ 
+-  uint8_t* WriteRaw(const void* data, int size, uint8_t* ptr) {
+-    if (PROTOBUF_PREDICT_FALSE(end_ - ptr < size)) {
++  uint8_t* WriteRaw(const void* data, size_t size, uint8_t* ptr) {
++    if (PROTOBUF_PREDICT_FALSE(end_ - ptr < static_cast<int>(size))) {
+       return WriteRawFallback(data, size, ptr);
+     }
+     std::memcpy(ptr, data, size);
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0005-Suppress-enum-enum-conversion-warning.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0005-Suppress-enum-enum-conversion-warning.patch
new file mode 100644
index 0000000..85d12a0
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0005-Suppress-enum-enum-conversion-warning.patch
@@ -0,0 +1,36 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Sat, 10 Jun 2023 15:13:45 -0700
+Subject: [PATCH 05/11] Suppress enum-enum conversion warning
+
+---
+ src/google/protobuf/generated_message_tctable_impl.h | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/src/google/protobuf/generated_message_tctable_impl.h b/src/google/protobuf/generated_message_tctable_impl.h
+index 21fa5332d39b24e0bdb6432f27183df743d512c6..ce3d9110e28706ca18dcf0f2c94feba75f447dc7 100644
+--- a/src/google/protobuf/generated_message_tctable_impl.h
++++ b/src/google/protobuf/generated_message_tctable_impl.h
+@@ -180,6 +180,12 @@ static_assert(kFmtShift + kFmtBits == 12, "number of bits changed");
+ // This assertion should not change unless the storage width changes:
+ static_assert(kFmtShift + kFmtBits <= 16, "too many bits");
+ 
++#ifdef __GNUC__
++#pragma GCC diagnostic push
++#if __GNUC__ >= 12 || (__GNUC__ == 11 && __GNUC_MINOR >= 1)
++#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion"
++#endif
++#endif
+ // Convenience aliases (16 bits, with format):
+ enum FieldType : uint16_t {
+   // Numeric types:
+@@ -232,6 +238,9 @@ enum FieldType : uint16_t {
+   // Map types:
+   kMap             = kFkMap,
+ };
++#ifdef __GNUC__
++#pragma GCC diagnostic pop
++#endif
+ 
+ // clang-format on
+ }  // namespace field_layout
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0006-Fix-noreturn-function-returning.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0006-Fix-noreturn-function-returning.patch
new file mode 100644
index 0000000..d255d80
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0006-Fix-noreturn-function-returning.patch
@@ -0,0 +1,23 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Sat, 10 Jun 2023 15:16:46 -0700
+Subject: [PATCH 06/11] Fix noreturn function returning
+
+---
+ src/google/protobuf/generated_message_tctable_impl.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/google/protobuf/generated_message_tctable_impl.h b/src/google/protobuf/generated_message_tctable_impl.h
+index ce3d9110e28706ca18dcf0f2c94feba75f447dc7..a8f8bdc192081763b0ad8c685384ae68d901736c 100644
+--- a/src/google/protobuf/generated_message_tctable_impl.h
++++ b/src/google/protobuf/generated_message_tctable_impl.h
+@@ -262,6 +262,9 @@ template <size_t align>
+ #endif
+ void AlignFail(uintptr_t address) {
+   GOOGLE_LOG(FATAL) << "Unaligned (" << align << ") access at " << address;
++#ifdef __GNUC__
++  __builtin_unreachable();
++#endif
+ }
+ 
+ extern template void AlignFail<4>(uintptr_t);
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0007-Work-around-GCC-12-restrict-warning-compiler-bug.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0007-Work-around-GCC-12-restrict-warning-compiler-bug.patch
new file mode 100644
index 0000000..aa67e21
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0007-Work-around-GCC-12-restrict-warning-compiler-bug.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Sat, 10 Jun 2023 15:59:45 -0700
+Subject: [PATCH 07/11] Work around GCC 12 restrict warning compiler bug
+
+https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105329
+---
+ src/google/protobuf/io/tokenizer.cc | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/google/protobuf/io/tokenizer.cc b/src/google/protobuf/io/tokenizer.cc
+index f9e07763e7362bd37267619336db841d0ae9df25..30d62ac9647b897c2e7c8ad43cd27ff0e08922a2 100644
+--- a/src/google/protobuf/io/tokenizer.cc
++++ b/src/google/protobuf/io/tokenizer.cc
+@@ -585,7 +585,14 @@ Tokenizer::NextCommentStatus Tokenizer::TryConsumeCommentStart() {
+     } else {
+       // Oops, it was just a slash.  Return it.
+       current_.type = TYPE_SYMBOL;
++#if defined(__GNUC__) && !defined(__clang__)
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wrestrict"
++#endif
+       current_.text = "/";
++#if defined(__GNUC__) && !defined(__clang__)
++#pragma GCC diagnostic pop
++#endif
+       current_.line = line_;
+       current_.column = column_ - 1;
+       current_.end_column = column_;
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0008-Disable-MSVC-switch-warning.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0008-Disable-MSVC-switch-warning.patch
new file mode 100644
index 0000000..b0c485b
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0008-Disable-MSVC-switch-warning.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Tue, 13 Jun 2023 23:56:15 -0700
+Subject: [PATCH 08/11] Disable MSVC switch warning
+
+---
+ src/google/protobuf/generated_message_reflection.cc | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc
+index 2a807e066bb748f214e008971309ef15473344b5..599dde80b671085d87ff1812929cafe8d2aecf75 100644
+--- a/src/google/protobuf/generated_message_reflection.cc
++++ b/src/google/protobuf/generated_message_reflection.cc
+@@ -75,6 +75,10 @@ using google::protobuf::internal::RepeatedPtrFieldBase;
+ using google::protobuf::internal::StringSpaceUsedExcludingSelfLong;
+ using google::protobuf::internal::WrappedMutex;
+ 
++#ifdef _MSC_VER
++#pragma warning(disable : 4065)
++#endif
++
+ namespace google {
+ namespace protobuf {
+ 
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0009-Disable-unused-function-warning.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0009-Disable-unused-function-warning.patch
new file mode 100644
index 0000000..b3aa64e
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0009-Disable-unused-function-warning.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Tue, 13 Jun 2023 23:58:50 -0700
+Subject: [PATCH 09/11] Disable unused function warning
+
+---
+ src/google/protobuf/generated_message_tctable_lite.cc | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/google/protobuf/generated_message_tctable_lite.cc b/src/google/protobuf/generated_message_tctable_lite.cc
+index 2268b2be4d4c60c545765469549d73c6a468dac8..23557a614752a9f0c93d8bd56724f3bc0f962185 100644
+--- a/src/google/protobuf/generated_message_tctable_lite.cc
++++ b/src/google/protobuf/generated_message_tctable_lite.cc
+@@ -43,6 +43,10 @@
+ #include <google/protobuf/port_def.inc>
+ // clang-format on
+ 
++#ifdef __GNUC__
++#pragma GCC diagnostic ignored "-Wunused-function"
++#endif
++
+ namespace google {
+ namespace protobuf {
+ namespace internal {
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0010-Disable-pedantic-warning.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0010-Disable-pedantic-warning.patch
new file mode 100644
index 0000000..1555d19
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0010-Disable-pedantic-warning.patch
@@ -0,0 +1,95 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Wed, 14 Jun 2023 00:02:26 -0700
+Subject: [PATCH 10/11] Disable pedantic warning
+
+---
+ src/google/protobuf/descriptor.h                    | 8 ++++++++
+ src/google/protobuf/generated_message_reflection.cc | 2 ++
+ src/google/protobuf/parse_context.h                 | 8 ++++++++
+ src/google/protobuf/stubs/common.cc                 | 4 ++--
+ 4 files changed, 20 insertions(+), 2 deletions(-)
+
+diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h
+index 6e536e5705f8df4f7c13638d50c114cbfb92fb4a..bee3e32b9f1d5ba47b83d1e388716a3c3b6e82c6 100644
+--- a/src/google/protobuf/descriptor.h
++++ b/src/google/protobuf/descriptor.h
+@@ -80,6 +80,10 @@
+ #define PROTOBUF_EXPORT
+ #endif
+ 
++#ifdef __GNUC__
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wpedantic"
++#endif
+ 
+ namespace google {
+ namespace protobuf {
+@@ -2434,6 +2438,10 @@ inline FileDescriptor::Syntax FileDescriptor::syntax() const {
+ }  // namespace protobuf
+ }  // namespace google
+ 
++#ifdef __GNUC__
++#pragma GCC diagnostic pop
++#endif
++
+ #undef PROTOBUF_INTERNAL_CHECK_CLASS_SIZE
+ #include <google/protobuf/port_undef.inc>
+ 
+diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc
+index 599dde80b671085d87ff1812929cafe8d2aecf75..aaed21920908b329e22c2e0d92f69397996a9f93 100644
+--- a/src/google/protobuf/generated_message_reflection.cc
++++ b/src/google/protobuf/generated_message_reflection.cc
+@@ -77,6 +77,8 @@ using google::protobuf::internal::WrappedMutex;
+ 
+ #ifdef _MSC_VER
+ #pragma warning(disable : 4065)
++#elif defined(__GNUC__)
++#pragma GCC diagnostic ignored "-Wpedantic"
+ #endif
+ 
+ namespace google {
+diff --git a/src/google/protobuf/parse_context.h b/src/google/protobuf/parse_context.h
+index 7aea50cdc385f0ed01b3989e12276494bf574939..97daae09cbff11fd3b4b99cee935aeb542c42eb4 100644
+--- a/src/google/protobuf/parse_context.h
++++ b/src/google/protobuf/parse_context.h
+@@ -52,6 +52,10 @@
+ // Must be included last.
+ #include <google/protobuf/port_def.inc>
+ 
++#ifdef __GNUC__
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wpedantic"
++#endif
+ 
+ namespace google {
+ namespace protobuf {
+@@ -1020,6 +1024,10 @@ PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* UnknownFieldParse(
+ }  // namespace protobuf
+ }  // namespace google
+ 
++#ifdef __GNUC__
++#pragma GCC diagnostic pop
++#endif
++
+ #include <google/protobuf/port_undef.inc>
+ 
+ #endif  // GOOGLE_PROTOBUF_PARSE_CONTEXT_H__
+diff --git a/src/google/protobuf/stubs/common.cc b/src/google/protobuf/stubs/common.cc
+index e0a807ffbbc94d07176e20db230204384170607b..1423021b846966eb02d36c10df488f8aa0082a64 100644
+--- a/src/google/protobuf/stubs/common.cc
++++ b/src/google/protobuf/stubs/common.cc
+@@ -277,11 +277,11 @@ LogHandler* SetLogHandler(LogHandler* new_func) {
+ 
+ LogSilencer::LogSilencer() {
+   ++internal::log_silencer_count_;
+-};
++}
+ 
+ LogSilencer::~LogSilencer() {
+   --internal::log_silencer_count_;
+-};
++}
+ 
+ // ===================================================================
+ // emulates google3/base/callback.cc
diff --git a/third_party/allwpilib/upstream_utils/protobuf_patches/0011-Avoid-use-of-sprintf.patch b/third_party/allwpilib/upstream_utils/protobuf_patches/0011-Avoid-use-of-sprintf.patch
new file mode 100644
index 0000000..27e81af
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/protobuf_patches/0011-Avoid-use-of-sprintf.patch
@@ -0,0 +1,35 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Johnson <johnson.peter@gmail.com>
+Date: Mon, 9 Oct 2023 19:28:08 -0700
+Subject: [PATCH 11/11] Avoid use of sprintf
+
+---
+ src/google/protobuf/stubs/strutil.cc | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/src/google/protobuf/stubs/strutil.cc b/src/google/protobuf/stubs/strutil.cc
+index 3462e91ff273dc071628f06b91698a0f166514fc..e063d0d3039c87d55a6df0dbfe50f365184fe292 100644
+--- a/src/google/protobuf/stubs/strutil.cc
++++ b/src/google/protobuf/stubs/strutil.cc
+@@ -503,10 +503,18 @@ int CEscapeInternal(const char* src, int src_len, char* dest,
+              (last_hex_escape && isxdigit(*src)))) {
+           if (dest_len - used < 4) // need space for 4 letter escape
+             return -1;
+-          sprintf(dest + used, (use_hex ? "\\x%02x" : "\\%03o"),
+-                  static_cast<uint8_t>(*src));
++          dest[used++] = '\\';
++          if (use_hex) {
++            constexpr char hexdigits[] = "0123456789abcdef";
++            dest[used++] = 'x';
++            dest[used++] = hexdigits[(static_cast<uint8_t>(*src) >> 4) & 0xf];
++            dest[used++] = hexdigits[static_cast<uint8_t>(*src) & 0xf];
++          } else {
++            dest[used++] = '0' + ((static_cast<uint8_t>(*src) >> 6) & 0x3);
++            dest[used++] = '0' + ((static_cast<uint8_t>(*src) >> 3) & 0x7);
++            dest[used++] = '0' + (static_cast<uint8_t>(*src) & 0x7);
++          }
+           is_hex_escape = use_hex;
+-          used += 4;
+         } else {
+           dest[used++] = *src; break;
+         }
diff --git a/third_party/allwpilib/upstream_utils/stack_walker_patches/0001-Add-advapi-pragma.patch b/third_party/allwpilib/upstream_utils/stack_walker_patches/0001-Add-advapi-pragma.patch
new file mode 100644
index 0000000..d85bd5e
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/stack_walker_patches/0001-Add-advapi-pragma.patch
@@ -0,0 +1,21 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Thad House <thadhouse1@gmail.com>
+Date: Sat, 22 Jul 2023 13:03:13 -0700
+Subject: [PATCH] Add advapi pragma
+
+---
+ Main/StackWalker/StackWalker.cpp | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/Main/StackWalker/StackWalker.cpp b/Main/StackWalker/StackWalker.cpp
+index 89545f8612d0d099d48fcf4184a2f2a30cf8577f..b2bcbaa447c5db1a3bcc155fb317ebc8a8050e79 100644
+--- a/Main/StackWalker/StackWalker.cpp
++++ b/Main/StackWalker/StackWalker.cpp
+@@ -91,6 +91,7 @@
+ #include <new>
+
+ #pragma comment(lib, "version.lib") // for "VerQueryValue"
++#pragma comment(lib, "Advapi32.lib") // for "GetUserName"
+
+ #pragma warning(disable : 4826)
+ #if _MSC_VER >= 1900
diff --git a/third_party/allwpilib/upstream_utils/stack_walker_patches/0001-Apply-PR-35.patch b/third_party/allwpilib/upstream_utils/stack_walker_patches/0001-Apply-PR-35.patch
deleted file mode 100644
index 8fe466e..0000000
--- a/third_party/allwpilib/upstream_utils/stack_walker_patches/0001-Apply-PR-35.patch
+++ /dev/null
@@ -1,1353 +0,0 @@
-From 6e2f70b7bb7c59fe99b7469bf3e3a257876403dc Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Sun, 22 May 2022 23:58:57 -0400
-Subject: [PATCH 1/3] Apply PR #35
-
----
- .gitignore                              |   9 +
- Main/StackWalker/StackWalker.cpp        | 642 ++++++++++--------------
- Main/StackWalker/StackWalker.h          |  40 +-
- Main/StackWalker/StackWalker_VC2017.sln |  16 +-
- Main/StackWalker/main.cpp               |   2 +-
- 5 files changed, 306 insertions(+), 403 deletions(-)
- create mode 100644 .gitignore
-
-diff --git a/.gitignore b/.gitignore
-new file mode 100644
-index 0000000..5d102c5
---- /dev/null
-+++ b/.gitignore
-@@ -0,0 +1,9 @@
-+################################################################################
-+# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
-+################################################################################
-+
-+*.suo
-+*.db
-+*.sqlite
-+/Main/StackWalker/_ReSharper.Caches/*
-+/.vs/*
-diff --git a/Main/StackWalker/StackWalker.cpp b/Main/StackWalker/StackWalker.cpp
-index 7008ac6..48c7c57 100644
---- a/Main/StackWalker/StackWalker.cpp
-+++ b/Main/StackWalker/StackWalker.cpp
-@@ -1,4 +1,4 @@
--/**********************************************************************
-+/**********************************************************************
-  *
-  * StackWalker.cpp
-  * https://github.com/JochenKalmbach/StackWalker
-@@ -87,162 +87,36 @@
- #include <stdio.h>
- #include <stdlib.h>
- #include <tchar.h>
--#include <windows.h>
- #pragma comment(lib, "version.lib") // for "VerQueryValue"
- #pragma warning(disable : 4826)
- 
-+#ifdef UNICODE
-+  #define DBGHELP_TRANSLATE_TCHAR
- 
--// If VC7 and later, then use the shipped 'dbghelp.h'-file
-+#endif
- #pragma pack(push, 8)
--#if _MSC_VER >= 1300
- #include <dbghelp.h>
--#else
--// inline the important dbghelp.h-declarations...
--typedef enum
--{
--  SymNone = 0,
--  SymCoff,
--  SymCv,
--  SymPdb,
--  SymExport,
--  SymDeferred,
--  SymSym,
--  SymDia,
--  SymVirtual,
--  NumSymTypes
--} SYM_TYPE;
--typedef struct _IMAGEHLP_LINE64
--{
--  DWORD   SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
--  PVOID   Key;          // internal
--  DWORD   LineNumber;   // line number in file
--  PCHAR   FileName;     // full filename
--  DWORD64 Address;      // first instruction of line
--} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
--typedef struct _IMAGEHLP_MODULE64
--{
--  DWORD    SizeOfStruct;         // set to sizeof(IMAGEHLP_MODULE64)
--  DWORD64  BaseOfImage;          // base load address of module
--  DWORD    ImageSize;            // virtual size of the loaded module
--  DWORD    TimeDateStamp;        // date/time stamp from pe header
--  DWORD    CheckSum;             // checksum from the pe header
--  DWORD    NumSyms;              // number of symbols in the symbol table
--  SYM_TYPE SymType;              // type of symbols loaded
--  CHAR     ModuleName[32];       // module name
--  CHAR     ImageName[256];       // image name
--  CHAR     LoadedImageName[256]; // symbol file name
--} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
--typedef struct _IMAGEHLP_SYMBOL64
--{
--  DWORD   SizeOfStruct;  // set to sizeof(IMAGEHLP_SYMBOL64)
--  DWORD64 Address;       // virtual address including dll base address
--  DWORD   Size;          // estimated size of symbol, can be zero
--  DWORD   Flags;         // info about the symbols, see the SYMF defines
--  DWORD   MaxNameLength; // maximum size of symbol name in 'Name'
--  CHAR    Name[1];       // symbol name (null terminated string)
--} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
--typedef enum
--{
--  AddrMode1616,
--  AddrMode1632,
--  AddrModeReal,
--  AddrModeFlat
--} ADDRESS_MODE;
--typedef struct _tagADDRESS64
--{
--  DWORD64      Offset;
--  WORD         Segment;
--  ADDRESS_MODE Mode;
--} ADDRESS64, *LPADDRESS64;
--typedef struct _KDHELP64
--{
--  DWORD64 Thread;
--  DWORD   ThCallbackStack;
--  DWORD   ThCallbackBStore;
--  DWORD   NextCallback;
--  DWORD   FramePointer;
--  DWORD64 KiCallUserMode;
--  DWORD64 KeUserCallbackDispatcher;
--  DWORD64 SystemRangeStart;
--  DWORD64 Reserved[8];
--} KDHELP64, *PKDHELP64;
--typedef struct _tagSTACKFRAME64
--{
--  ADDRESS64 AddrPC;         // program counter
--  ADDRESS64 AddrReturn;     // return address
--  ADDRESS64 AddrFrame;      // frame pointer
--  ADDRESS64 AddrStack;      // stack pointer
--  ADDRESS64 AddrBStore;     // backing store pointer
--  PVOID     FuncTableEntry; // pointer to pdata/fpo or NULL
--  DWORD64   Params[4];      // possible arguments to the function
--  BOOL      Far;            // WOW far call
--  BOOL      Virtual;        // is this a virtual frame?
--  DWORD64   Reserved[3];
--  KDHELP64  KdHelp;
--} STACKFRAME64, *LPSTACKFRAME64;
--typedef BOOL(__stdcall* PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE  hProcess,
--                                                        DWORD64 qwBaseAddress,
--                                                        PVOID   lpBuffer,
--                                                        DWORD   nSize,
--                                                        LPDWORD lpNumberOfBytesRead);
--typedef PVOID(__stdcall* PFUNCTION_TABLE_ACCESS_ROUTINE64)(HANDLE hProcess, DWORD64 AddrBase);
--typedef DWORD64(__stdcall* PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, DWORD64 Address);
--typedef DWORD64(__stdcall* PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE      hProcess,
--                                                         HANDLE      hThread,
--                                                         LPADDRESS64 lpaddr);
--
--// clang-format off
--#define SYMOPT_CASE_INSENSITIVE         0x00000001
--#define SYMOPT_UNDNAME                  0x00000002
--#define SYMOPT_DEFERRED_LOADS           0x00000004
--#define SYMOPT_NO_CPP                   0x00000008
--#define SYMOPT_LOAD_LINES               0x00000010
--#define SYMOPT_OMAP_FIND_NEAREST        0x00000020
--#define SYMOPT_LOAD_ANYTHING            0x00000040
--#define SYMOPT_IGNORE_CVREC             0x00000080
--#define SYMOPT_NO_UNQUALIFIED_LOADS     0x00000100
--#define SYMOPT_FAIL_CRITICAL_ERRORS     0x00000200
--#define SYMOPT_EXACT_SYMBOLS            0x00000400
--#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS   0x00000800
--#define SYMOPT_IGNORE_NT_SYMPATH        0x00001000
--#define SYMOPT_INCLUDE_32BIT_MODULES    0x00002000
--#define SYMOPT_PUBLICS_ONLY             0x00004000
--#define SYMOPT_NO_PUBLICS               0x00008000
--#define SYMOPT_AUTO_PUBLICS             0x00010000
--#define SYMOPT_NO_IMAGE_SEARCH          0x00020000
--#define SYMOPT_SECURE                   0x00040000
--#define SYMOPT_DEBUG                    0x80000000
--#define UNDNAME_COMPLETE                 (0x0000) // Enable full undecoration
--#define UNDNAME_NAME_ONLY                (0x1000) // Crack only the name for primary declaration;
--// clang-format on
--
--#endif // _MSC_VER < 1300
- #pragma pack(pop)
- 
--// Some missing defines (for VC5/6):
--#ifndef INVALID_FILE_ATTRIBUTES
--#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
--#endif
- 
--// secure-CRT_functions are only available starting with VC8
--#if _MSC_VER < 1400
--#define strcpy_s(dst, len, src) strcpy(dst, src)
--#define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src)
--#define strcat_s(dst, len, src) strcat(dst, src)
--#define _snprintf_s _snprintf
--#define _tcscat_s _tcscat
--#endif
--
--static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc)
-+static void MyStrCpy(TCHAR* szDest, size_t nMaxDestSize, const TCHAR* szSrc)
- {
-   if (nMaxDestSize <= 0)
-     return;
--  strncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE);
-+  _tcsncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE);
-   // INFO: _TRUNCATE will ensure that it is null-terminated;
-   // but with older compilers (<1400) it uses "strncpy" and this does not!)
-   szDest[nMaxDestSize - 1] = 0;
- } // MyStrCpy
- 
-+#ifdef _UNICODE
-+  typedef SYMBOL_INFOW   tSymbolInfo;
-+  typedef IMAGEHLP_LINEW64  tImageHelperLine;
-+#else
-+  typedef SYMBOL_INFO   tSymbolInfo;
-+  typedef IMAGEHLP_LINE64  tImageHelperLine;
-+#endif
-+
- // Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
- #define USED_CONTEXT_FLAGS CONTEXT_FULL
- 
-@@ -253,26 +127,26 @@ public:
-   {
-     m_parent = parent;
-     m_hDbhHelp = NULL;
--    pSC = NULL;
-+    symCleanup = NULL;
-     m_hProcess = hProcess;
-     m_szSymPath = NULL;
--    pSFTA = NULL;
--    pSGLFA = NULL;
--    pSGMB = NULL;
--    pSGMI = NULL;
--    pSGO = NULL;
--    pSGSFA = NULL;
--    pSI = NULL;
--    pSLM = NULL;
--    pSSO = NULL;
--    pSW = NULL;
--    pUDSN = NULL;
--    pSGSP = NULL;
-+    symFunctionTableAccess64 = NULL;
-+    symGetLineFromAddr64 = NULL;
-+    symGetModuleBase64 = NULL;
-+    symGetModuleInfo64 = NULL;
-+    symGetOptions = NULL;
-+    symFromAddr = NULL;
-+    symInitialize = NULL;
-+    symLoadModuleEx = NULL;
-+    symSetOptions = NULL;
-+    stackWalk64 = NULL;
-+    unDecorateSymbolName = NULL;
-+    symGetSearchPath = NULL;
-   }
-   ~StackWalkerInternal()
-   {
--    if (pSC != NULL)
--      pSC(m_hProcess); // SymCleanup
-+    if (symCleanup != NULL)
-+      symCleanup(m_hProcess); // SymCleanup
-     if (m_hDbhHelp != NULL)
-       FreeLibrary(m_hDbhHelp);
-     m_hDbhHelp = NULL;
-@@ -281,7 +155,7 @@ public:
-       free(m_szSymPath);
-     m_szSymPath = NULL;
-   }
--  BOOL Init(LPCSTR szSymPath)
-+  BOOL Init(LPCTSTR szSymPath)
-   {
-     if (m_parent == NULL)
-       return FALSE;
-@@ -354,54 +228,72 @@ public:
-       m_hDbhHelp = LoadLibrary(_T("dbghelp.dll"));
-     if (m_hDbhHelp == NULL)
-       return FALSE;
--    pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize");
--    pSC = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup");
--
--    pSW = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64");
--    pSGO = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions");
--    pSSO = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions");
--
--    pSFTA = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64");
--    pSGLFA = (tSGLFA)GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64");
--    pSGMB = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64");
--    pSGMI = (tSGMI)GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64");
--    pSGSFA = (tSGSFA)GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64");
--    pUDSN = (tUDSN)GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName");
--    pSLM = (tSLM)GetProcAddress(m_hDbhHelp, "SymLoadModule64");
--    pSGSP = (tSGSP)GetProcAddress(m_hDbhHelp, "SymGetSearchPath");
--
--    if (pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || pSGO == NULL ||
--        pSGSFA == NULL || pSI == NULL || pSSO == NULL || pSW == NULL || pUDSN == NULL ||
--        pSLM == NULL)
-+
-+#ifdef _UNICODE
-+    static const char strSymInitialize[] = "SymInitializeW";
-+    static const char strUnDecorateSymbolName[] = "UnDecorateSymbolNameW";
-+    static const char strSymGetSearchPath[] = "SymGetSearchPathW";
-+    static const char strSymLoadModuleEx[] = "SymLoadModuleExW";
-+    static const char strSymGetLineFromAddr64[] = "SymGetLineFromAddrW64";
-+    static const char strSymGetModuleInfo64[] = "SymGetModuleInfoW64";
-+    static const char strSymFromAddr[] = "SymFromAddrW";
-+#else
-+    static const char strSymInitialize[] = "SymInitialize";
-+    static const char strUnDecorateSymbolName[] = "UnDecorateSymbolName";
-+    static const char strSymGetSearchPath[] = "SymGetSearchPath";
-+    static const char strSymLoadModuleEx[] = "SymLoadModuleEx";
-+    static const char strSymGetLineFromAddr64[] = "SymGetLineFromAddr64";
-+    static const char strSymGetModuleInfo64[] = "SymGetModuleInfo64";
-+    static const char strSymFromAddr[] = "SymFromAddr";
-+#endif
-+    symInitialize = (tSI)GetProcAddress(m_hDbhHelp, strSymInitialize);
-+    symCleanup = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup");
-+
-+    stackWalk64 = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64");
-+    symGetOptions = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions");
-+    symSetOptions = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions");
-+
-+    symFunctionTableAccess64 = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64");
-+    symGetLineFromAddr64 = (tSGLFA)GetProcAddress(m_hDbhHelp, strSymGetLineFromAddr64);
-+    symGetModuleBase64 = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64");
-+    symGetModuleInfo64 = (tSGMI)GetProcAddress(m_hDbhHelp, strSymGetModuleInfo64);
-+    symFromAddr = (tSFA)GetProcAddress(m_hDbhHelp, strSymFromAddr);
-+    unDecorateSymbolName = (tUDSN)GetProcAddress(m_hDbhHelp, strUnDecorateSymbolName);
-+    symLoadModuleEx = (tSLM)GetProcAddress(m_hDbhHelp, strSymLoadModuleEx);
-+    symGetSearchPath = (tSGSP)GetProcAddress(m_hDbhHelp, strSymGetSearchPath);
-+
-+    if (symCleanup == NULL || symFunctionTableAccess64 == NULL || symGetModuleBase64 == NULL || symGetModuleInfo64 == NULL || symGetOptions == NULL ||
-+        symFromAddr == NULL || symInitialize == NULL || symSetOptions == NULL || stackWalk64 == NULL || unDecorateSymbolName == NULL ||
-+        symLoadModuleEx == NULL)
-     {
-       FreeLibrary(m_hDbhHelp);
-       m_hDbhHelp = NULL;
--      pSC = NULL;
-+      symCleanup = NULL;
-       return FALSE;
-     }
- 
-     // SymInitialize
-     if (szSymPath != NULL)
--      m_szSymPath = _strdup(szSymPath);
--    if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE)
--      this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
-+      m_szSymPath = _tcsdup(szSymPath);
-+    if (this->symInitialize(m_hProcess, m_szSymPath, FALSE) == FALSE)
-+      this->m_parent->OnDbgHelpErr(_T("SymInitialize"), GetLastError(), 0);
- 
--    DWORD symOptions = this->pSGO(); // SymGetOptions
-+    DWORD symOptions = this->symGetOptions(); // SymGetOptions
-     symOptions |= SYMOPT_LOAD_LINES;
-     symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
-     //symOptions |= SYMOPT_NO_PROMPTS;
-     // SymSetOptions
--    symOptions = this->pSSO(symOptions);
-+    symOptions = this->symSetOptions(symOptions);
- 
--    char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
--    if (this->pSGSP != NULL)
-+    TCHAR buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
-+    if (this->symGetSearchPath != NULL)
-     {
--      if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE)
--        this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
-+      if (this->symGetSearchPath(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE)
-+        this->m_parent->OnDbgHelpErr(_T("SymGetSearchPath"), GetLastError(), 0);
-     }
--    char  szUserName[1024] = {0};
-+    TCHAR  szUserName[1024] = {0};
-     DWORD dwSize = 1024;
--    GetUserNameA(szUserName, &dwSize);
-+    GetUserName(szUserName, &dwSize);
-     this->m_parent->OnSymInit(buf, symOptions, szUserName);
- 
-     return TRUE;
-@@ -411,7 +303,7 @@ public:
- 
-   HMODULE m_hDbhHelp;
-   HANDLE  m_hProcess;
--  LPSTR   m_szSymPath;
-+  LPTSTR   m_szSymPath;
- 
- #pragma pack(push, 8)
-   typedef struct IMAGEHLP_MODULE64_V3
-@@ -423,13 +315,13 @@ public:
-     DWORD    CheckSum;             // checksum from the pe header
-     DWORD    NumSyms;              // number of symbols in the symbol table
-     SYM_TYPE SymType;              // type of symbols loaded
--    CHAR     ModuleName[32];       // module name
--    CHAR     ImageName[256];       // image name
--    CHAR     LoadedImageName[256]; // symbol file name
-+    TCHAR     ModuleName[32];       // module name
-+    TCHAR     ImageName[256];       // image name
-+    TCHAR     LoadedImageName[256]; // symbol file name
-     // new elements: 07-Jun-2002
--    CHAR  LoadedPdbName[256];   // pdb file name
-+    TCHAR  LoadedPdbName[256];   // pdb file name
-     DWORD CVSig;                // Signature of the CV record in the debug directories
--    CHAR  CVData[MAX_PATH * 3]; // Contents of the CV record
-+    TCHAR  CVData[MAX_PATH * 3]; // Contents of the CV record
-     DWORD PdbSig;               // Signature of PDB
-     GUID  PdbSig70;             // Signature of PDB (VC 7 and up)
-     DWORD PdbAge;               // DBI age of pdb
-@@ -460,56 +352,59 @@ public:
- 
-   // SymCleanup()
-   typedef BOOL(__stdcall* tSC)(IN HANDLE hProcess);
--  tSC pSC;
-+  tSC symCleanup;
- 
-   // SymFunctionTableAccess64()
-   typedef PVOID(__stdcall* tSFTA)(HANDLE hProcess, DWORD64 AddrBase);
--  tSFTA pSFTA;
-+  tSFTA symFunctionTableAccess64;
- 
-   // SymGetLineFromAddr64()
-   typedef BOOL(__stdcall* tSGLFA)(IN HANDLE hProcess,
-                                   IN DWORD64 dwAddr,
-                                   OUT PDWORD pdwDisplacement,
--                                  OUT PIMAGEHLP_LINE64 Line);
--  tSGLFA pSGLFA;
-+                                  OUT tImageHelperLine* Line);
-+  tSGLFA symGetLineFromAddr64;
- 
-   // SymGetModuleBase64()
-   typedef DWORD64(__stdcall* tSGMB)(IN HANDLE hProcess, IN DWORD64 dwAddr);
--  tSGMB pSGMB;
-+  tSGMB symGetModuleBase64;
- 
-   // SymGetModuleInfo64()
-   typedef BOOL(__stdcall* tSGMI)(IN HANDLE hProcess,
-                                  IN DWORD64 dwAddr,
-                                  OUT IMAGEHLP_MODULE64_V3* ModuleInfo);
--  tSGMI pSGMI;
-+  tSGMI symGetModuleInfo64;
- 
-   // SymGetOptions()
-   typedef DWORD(__stdcall* tSGO)(VOID);
--  tSGO pSGO;
-+  tSGO symGetOptions;
-+
- 
-   // SymGetSymFromAddr64()
--  typedef BOOL(__stdcall* tSGSFA)(IN HANDLE hProcess,
--                                  IN DWORD64 dwAddr,
-+  typedef BOOL(__stdcall* tSFA)(IN HANDLE hProcess,
-+                                  IN DWORD64 Address,
-                                   OUT PDWORD64 pdwDisplacement,
--                                  OUT PIMAGEHLP_SYMBOL64 Symbol);
--  tSGSFA pSGSFA;
-+                                  OUT tSymbolInfo* Symbol);
-+  tSFA symFromAddr;
- 
-   // SymInitialize()
--  typedef BOOL(__stdcall* tSI)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess);
--  tSI pSI;
-+  typedef BOOL(__stdcall* tSI)(IN HANDLE hProcess, IN PTSTR UserSearchPath, IN BOOL fInvadeProcess);
-+  tSI symInitialize;
- 
-   // SymLoadModule64()
-   typedef DWORD64(__stdcall* tSLM)(IN HANDLE hProcess,
-                                    IN HANDLE hFile,
--                                   IN PSTR ImageName,
--                                   IN PSTR ModuleName,
-+                                   IN PTSTR ImageName,
-+                                   IN PTSTR ModuleName,
-                                    IN DWORD64 BaseOfDll,
--                                   IN DWORD SizeOfDll);
--  tSLM pSLM;
-+                                   IN DWORD SizeOfDll,
-+                                   IN PMODLOAD_DATA Data,
-+                                   IN DWORD         Flags);
-+  tSLM symLoadModuleEx;
- 
-   // SymSetOptions()
-   typedef DWORD(__stdcall* tSSO)(IN DWORD SymOptions);
--  tSSO pSSO;
-+  tSSO symSetOptions;
- 
-   // StackWalk64()
-   typedef BOOL(__stdcall* tSW)(DWORD                            MachineType,
-@@ -521,17 +416,17 @@ public:
-                                PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
-                                PGET_MODULE_BASE_ROUTINE64       GetModuleBaseRoutine,
-                                PTRANSLATE_ADDRESS_ROUTINE64     TranslateAddress);
--  tSW pSW;
-+  tSW stackWalk64;
- 
-   // UnDecorateSymbolName()
--  typedef DWORD(__stdcall WINAPI* tUDSN)(PCSTR DecoratedName,
--                                         PSTR  UnDecoratedName,
-+  typedef DWORD(__stdcall WINAPI* tUDSN)(PCTSTR DecoratedName,
-+                                         PTSTR  UnDecoratedName,
-                                          DWORD UndecoratedLength,
-                                          DWORD Flags);
--  tUDSN pUDSN;
-+  tUDSN unDecorateSymbolName;
- 
--  typedef BOOL(__stdcall WINAPI* tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);
--  tSGSP pSGSP;
-+  typedef BOOL(__stdcall WINAPI* tSGSP)(HANDLE hProcess, PTSTR SearchPath, DWORD SearchPathLength);
-+  tSGSP symGetSearchPath;
- 
- private:
- // **************************************** ToolHelp32 ************************
-@@ -548,8 +443,8 @@ private:
-     BYTE*   modBaseAddr;   // Base address of module in th32ProcessID's context
-     DWORD   modBaseSize;   // Size in bytes of module starting at modBaseAddr
-     HMODULE hModule;       // The hModule of this module in th32ProcessID's context
--    char    szModule[MAX_MODULE_NAME32 + 1];
--    char    szExePath[MAX_PATH];
-+    TCHAR   szModule[MAX_MODULE_NAME32 + 1];
-+    TCHAR   szExePath[MAX_PATH];
-   } MODULEENTRY32;
-   typedef MODULEENTRY32* PMODULEENTRY32;
-   typedef MODULEENTRY32* LPMODULEENTRY32;
-@@ -567,25 +462,31 @@ private:
-     // try both dlls...
-     const TCHAR* dllname[] = {_T("kernel32.dll"), _T("tlhelp32.dll")};
-     HINSTANCE    hToolhelp = NULL;
--    tCT32S       pCT32S = NULL;
--    tM32F        pM32F = NULL;
--    tM32N        pM32N = NULL;
-+    tCT32S       createToolhelp32Snapshot = NULL;
-+    tM32F        module32First = NULL;
-+    tM32N        module32Next = NULL;
- 
-     HANDLE        hSnap;
--    MODULEENTRY32 me;
--    me.dwSize = sizeof(me);
-+    MODULEENTRY32 moduleEntry32;
-+    moduleEntry32.dwSize = sizeof(moduleEntry32);
-     BOOL   keepGoing;
--    size_t i;
- 
--    for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++)
-+#ifdef _UNICODE
-+    static const char strModule32First[] = "Module32FirstW";
-+    static const char strModule32Next[] = "Module32NextW";
-+ #else
-+    static const char strModule32First[] = "Module32First";
-+    static const char strModule32Next[] = "Module32Next";
-+#endif
-+    for (size_t i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++)
-     {
-       hToolhelp = LoadLibrary(dllname[i]);
-       if (hToolhelp == NULL)
-         continue;
--      pCT32S = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
--      pM32F = (tM32F)GetProcAddress(hToolhelp, "Module32First");
--      pM32N = (tM32N)GetProcAddress(hToolhelp, "Module32Next");
--      if ((pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL))
-+      createToolhelp32Snapshot = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
-+      module32First = (tM32F)GetProcAddress(hToolhelp, strModule32First);  
-+      module32Next = (tM32N)GetProcAddress(hToolhelp, strModule32Next); 
-+      if ((createToolhelp32Snapshot != NULL) && (module32First != NULL) && (module32Next != NULL))
-         break; // found the functions!
-       FreeLibrary(hToolhelp);
-       hToolhelp = NULL;
-@@ -594,21 +495,21 @@ private:
-     if (hToolhelp == NULL)
-       return FALSE;
- 
--    hSnap = pCT32S(TH32CS_SNAPMODULE, pid);
-+    hSnap = createToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
-     if (hSnap == (HANDLE)-1)
-     {
-       FreeLibrary(hToolhelp);
-       return FALSE;
-     }
- 
--    keepGoing = !!pM32F(hSnap, &me);
-+    keepGoing = !!module32First(hSnap, &moduleEntry32);
-     int cnt = 0;
-     while (keepGoing)
-     {
--      this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr,
--                       me.modBaseSize);
-+      this->LoadModule(hProcess, moduleEntry32.szExePath, moduleEntry32.szModule, (DWORD64)moduleEntry32.modBaseAddr,
-+                       moduleEntry32.modBaseSize);
-       cnt++;
--      keepGoing = !!pM32N(hSnap, &me);
-+      keepGoing = !!module32Next(hSnap, &moduleEntry32);
-     }
-     CloseHandle(hSnap);
-     FreeLibrary(hToolhelp);
-@@ -631,39 +532,41 @@ private:
-     typedef BOOL(__stdcall * tEPM)(HANDLE hProcess, HMODULE * lphModule, DWORD cb,
-                                    LPDWORD lpcbNeeded);
-     // GetModuleFileNameEx()
--    typedef DWORD(__stdcall * tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename,
-+    typedef DWORD(__stdcall * tGMFNE)(HANDLE hProcess, HMODULE hModule, LPTSTR lpFilename,
-                                       DWORD nSize);
-     // GetModuleBaseName()
--    typedef DWORD(__stdcall * tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename,
-+    typedef DWORD(__stdcall * tGMBN)(HANDLE hProcess, HMODULE hModule, LPTSTR lpFilename,
-                                      DWORD nSize);
-     // GetModuleInformation()
-     typedef BOOL(__stdcall * tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize);
- 
--    HINSTANCE hPsapi;
--    tEPM      pEPM;
--    tGMFNE    pGMFNE;
--    tGMBN     pGMBN;
--    tGMI      pGMI;
--
--    DWORD i;
--    //ModuleEntry e;
-+      //ModuleEntry e;
-     DWORD        cbNeeded;
-     MODULEINFO   mi;
-     HMODULE*     hMods = 0;
--    char*        tt = NULL;
--    char*        tt2 = NULL;
-+    TCHAR*        tt = NULL;
-+    TCHAR*        tt2 = NULL;
-     const SIZE_T TTBUFLEN = 8096;
-     int          cnt = 0;
- 
--    hPsapi = LoadLibrary(_T("psapi.dll"));
-+    HINSTANCE hPsapi = LoadLibrary(_T("psapi.dll"));
-     if (hPsapi == NULL)
-       return FALSE;
- 
--    pEPM = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules");
--    pGMFNE = (tGMFNE)GetProcAddress(hPsapi, "GetModuleFileNameExA");
--    pGMBN = (tGMFNE)GetProcAddress(hPsapi, "GetModuleBaseNameA");
--    pGMI = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation");
--    if ((pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL))
-+#ifdef _UNICODE
-+    static const char strGetModuleFileName[] = "GetModuleFileNameExW";
-+    static const char strGetModuleBaseName[] = "GetModuleBaseNameW";
-+#else
-+    static const char strGetModuleFileName[] = "GetModulefileNameExA";
-+    static const char strGetModuleBaseName[] = "GetModuleBaseNameA";
-+#endif
-+
-+    tEPM   enumProcessModules = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules");
-+    tGMFNE getModuleFileNameEx = (tGMFNE)GetProcAddress(hPsapi, strGetModuleFileName);
-+    tGMBN  getModuleBaseName = (tGMFNE)GetProcAddress(hPsapi, strGetModuleBaseName);
-+    tGMI   getModuleInformation = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation");
-+    if ((enumProcessModules == NULL) || (getModuleFileNameEx == NULL) ||
-+        (getModuleBaseName == NULL) || (getModuleInformation == NULL))
-     {
-       // we couldn't find all functions
-       FreeLibrary(hPsapi);
-@@ -671,12 +574,12 @@ private:
-     }
- 
-     hMods = (HMODULE*)malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof(HMODULE)));
--    tt = (char*)malloc(sizeof(char) * TTBUFLEN);
--    tt2 = (char*)malloc(sizeof(char) * TTBUFLEN);
-+    tt = (TCHAR*)malloc(sizeof(TCHAR) * TTBUFLEN);
-+    tt2 = (TCHAR*)malloc(sizeof(TCHAR) * TTBUFLEN);
-     if ((hMods == NULL) || (tt == NULL) || (tt2 == NULL))
-       goto cleanup;
- 
--    if (!pEPM(hProcess, hMods, TTBUFLEN, &cbNeeded))
-+    if (!enumProcessModules(hProcess, hMods, TTBUFLEN, &cbNeeded))
-     {
-       //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
-       goto cleanup;
-@@ -688,20 +591,20 @@ private:
-       goto cleanup;
-     }
- 
--    for (i = 0; i < cbNeeded / sizeof(hMods[0]); i++)
-+    for (DWORD i = 0; i < cbNeeded / sizeof(hMods[0]); i++)
-     {
-       // base address, size
--      pGMI(hProcess, hMods[i], &mi, sizeof(mi));
-+      getModuleInformation(hProcess, hMods[i], &mi, sizeof(mi));
-       // image file name
-       tt[0] = 0;
--      pGMFNE(hProcess, hMods[i], tt, TTBUFLEN);
-+      getModuleFileNameEx(hProcess, hMods[i], tt, TTBUFLEN);
-       // module name
-       tt2[0] = 0;
--      pGMBN(hProcess, hMods[i], tt2, TTBUFLEN);
-+      getModuleBaseName(hProcess, hMods[i], tt2, TTBUFLEN);
- 
-       DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64)mi.lpBaseOfDll, mi.SizeOfImage);
-       if (dwRes != ERROR_SUCCESS)
--        this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);
-+        this->m_parent->OnDbgHelpErr(_T("LoadModule"), dwRes, 0);
-       cnt++;
-     }
- 
-@@ -718,16 +621,16 @@ private:
-     return cnt != 0;
-   } // GetModuleListPSAPI
- 
--  DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)
-+  DWORD LoadModule(HANDLE hProcess, LPCTSTR img, LPCTSTR mod, DWORD64 baseAddr, DWORD size)
-   {
--    CHAR* szImg = _strdup(img);
--    CHAR* szMod = _strdup(mod);
-+    TCHAR* szImg = _tcsdup(img);
-+    TCHAR* szMod = _tcsdup(mod);
-     DWORD result = ERROR_SUCCESS;
-     if ((szImg == NULL) || (szMod == NULL))
-       result = ERROR_NOT_ENOUGH_MEMORY;
-     else
-     {
--      if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
-+      if (symLoadModuleEx(hProcess, 0, szImg, szMod, baseAddr, size, 0, 0) == 0)
-         result = GetLastError();
-     }
-     ULONGLONG fileVersion = 0;
-@@ -738,13 +641,13 @@ private:
-       {
-         VS_FIXEDFILEINFO* fInfo = NULL;
-         DWORD             dwHandle;
--        DWORD             dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
-+        DWORD             dwSize = GetFileVersionInfoSize(szImg, &dwHandle);
-         if (dwSize > 0)
-         {
-           LPVOID vData = malloc(dwSize);
-           if (vData != NULL)
-           {
--            if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)
-+            if (GetFileVersionInfo(szImg, dwHandle, dwSize, vData) != 0)
-             {
-               UINT  len;
-               TCHAR szSubBlock[] = _T("\\");
-@@ -763,41 +666,41 @@ private:
- 
-       // Retrieve some additional-infos about the module
-       IMAGEHLP_MODULE64_V3 Module;
--      const char*          szSymType = "-unknown-";
-+      const TCHAR*          szSymType = _T("-unknown-");
-       if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)
-       {
-         switch (Module.SymType)
-         {
-           case SymNone:
--            szSymType = "-nosymbols-";
-+            szSymType = _T("-nosymbols-");
-             break;
-           case SymCoff: // 1
--            szSymType = "COFF";
-+            szSymType = _T("COFF");
-             break;
-           case SymCv: // 2
--            szSymType = "CV";
-+            szSymType = _T("CV");
-             break;
-           case SymPdb: // 3
--            szSymType = "PDB";
-+            szSymType = _T("PDB");
-             break;
-           case SymExport: // 4
--            szSymType = "-exported-";
-+            szSymType = _T("-exported-");
-             break;
-           case SymDeferred: // 5
--            szSymType = "-deferred-";
-+            szSymType = _T("-deferred-");
-             break;
-           case SymSym: // 6
--            szSymType = "SYM";
-+            szSymType = _T("SYM");
-             break;
-           case 7: // SymDia:
--            szSymType = "DIA";
-+            szSymType = _T("DIA");
-             break;
-           case 8: //SymVirtual:
--            szSymType = "Virtual";
-+            szSymType = _T("Virtual");
-             break;
-         }
-       }
--      LPCSTR pdbName = Module.LoadedImageName;
-+      LPCTSTR pdbName = Module.LoadedImageName;
-       if (Module.LoadedPdbName[0] != 0)
-         pdbName = Module.LoadedPdbName;
-       this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, pdbName,
-@@ -823,7 +726,7 @@ public:
-   BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3* pModuleInfo)
-   {
-     memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3));
--    if (this->pSGMI == NULL)
-+    if (this->symGetModuleInfo64 == NULL)
-     {
-       SetLastError(ERROR_DLL_INIT_FAILED);
-       return FALSE;
-@@ -841,7 +744,7 @@ public:
-     static bool s_useV3Version = true;
-     if (s_useV3Version)
-     {
--      if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE)
-+      if (this->symGetModuleInfo64(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE)
-       {
-         // only copy as much memory as is reserved...
-         memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V3));
-@@ -855,7 +758,7 @@ public:
-     // could not retrieve the bigger structure, try with the smaller one (as defined in VC7.1)...
-     pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
-     memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));
--    if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE)
-+    if (this->symGetModuleInfo64(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE)
-     {
-       // only copy as much memory as is reserved...
-       memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));
-@@ -880,7 +783,7 @@ StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess)
-   this->m_szSymPath = NULL;
-   this->m_MaxRecursionCount = 1000;
- }
--StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)
-+StackWalker::StackWalker(int options, LPCTSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)
- {
-   this->m_options = options;
-   this->m_modulesLoaded = FALSE;
-@@ -889,7 +792,7 @@ StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDL
-   this->m_dwProcessId = dwProcessId;
-   if (szSymPath != NULL)
-   {
--    this->m_szSymPath = _strdup(szSymPath);
-+    this->m_szSymPath = _tcsdup(szSymPath);
-     this->m_options |= SymBuildPath;
-   }
-   else
-@@ -918,11 +821,11 @@ BOOL StackWalker::LoadModules()
-     return TRUE;
- 
-   // Build the sym-path:
--  char* szSymPath = NULL;
-+  TCHAR* szSymPath = NULL;
-   if ((this->m_options & SymBuildPath) != 0)
-   {
-     const size_t nSymPathLen = 4096;
--    szSymPath = (char*)malloc(nSymPathLen);
-+    szSymPath = (TCHAR*)malloc(nSymPathLen * sizeof(TCHAR));
-     if (szSymPath == NULL)
-     {
-       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-@@ -932,27 +835,27 @@ BOOL StackWalker::LoadModules()
-     // Now first add the (optional) provided sympath:
-     if (this->m_szSymPath != NULL)
-     {
--      strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);
--      strcat_s(szSymPath, nSymPathLen, ";");
-+      _tcscat_s(szSymPath, nSymPathLen, this->m_szSymPath);
-+      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
-     }
- 
--    strcat_s(szSymPath, nSymPathLen, ".;");
-+    _tcscat_s(szSymPath, nSymPathLen, _T(".;"));
- 
-     const size_t nTempLen = 1024;
--    char         szTemp[nTempLen];
-+    TCHAR         szTemp[nTempLen];
-     // Now add the current directory:
--    if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
-+    if (GetCurrentDirectory(nTempLen, szTemp) > 0)
-     {
-       szTemp[nTempLen - 1] = 0;
--      strcat_s(szSymPath, nSymPathLen, szTemp);
--      strcat_s(szSymPath, nSymPathLen, ";");
-+      _tcscat_s(szSymPath, nSymPathLen, szTemp);
-+      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
-     }
- 
-     // Now add the path for the main-module:
--    if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)
-+    if (GetModuleFileName(NULL, szTemp, nTempLen) > 0)
-     {
-       szTemp[nTempLen - 1] = 0;
--      for (char* p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p)
-+      for (TCHAR* p = (szTemp + _tcslen(szTemp) - 1); p >= szTemp; --p)
-       {
-         // locate the rightmost path separator
-         if ((*p == '\\') || (*p == '/') || (*p == ':'))
-@@ -961,48 +864,48 @@ BOOL StackWalker::LoadModules()
-           break;
-         }
-       } // for (search for path separator...)
--      if (strlen(szTemp) > 0)
-+      if (_tcslen(szTemp) > 0)
-       {
--        strcat_s(szSymPath, nSymPathLen, szTemp);
--        strcat_s(szSymPath, nSymPathLen, ";");
-+        _tcscat_s(szSymPath, nSymPathLen, szTemp);
-+        _tcscat_s(szSymPath, nSymPathLen, _T(";"));
-       }
-     }
--    if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
-+    if (GetEnvironmentVariable(_T("_NT_SYMBOL_PATH"), szTemp, nTempLen) > 0)
-     {
-       szTemp[nTempLen - 1] = 0;
--      strcat_s(szSymPath, nSymPathLen, szTemp);
--      strcat_s(szSymPath, nSymPathLen, ";");
-+      _tcscat_s(szSymPath, nSymPathLen, szTemp);
-+      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
-     }
--    if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
-+    if (GetEnvironmentVariable(_T("_NT_ALTERNATE_SYMBOL_PATH"), szTemp, nTempLen) > 0)
-     {
-       szTemp[nTempLen - 1] = 0;
--      strcat_s(szSymPath, nSymPathLen, szTemp);
--      strcat_s(szSymPath, nSymPathLen, ";");
-+      _tcscat_s(szSymPath, nSymPathLen, szTemp);
-+      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
-     }
--    if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
-+    if (GetEnvironmentVariable(_T("SYSTEMROOT"), szTemp, nTempLen) > 0)
-     {
-       szTemp[nTempLen - 1] = 0;
--      strcat_s(szSymPath, nSymPathLen, szTemp);
--      strcat_s(szSymPath, nSymPathLen, ";");
-+      _tcscat_s(szSymPath, nSymPathLen, szTemp);
-+      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
-       // also add the "system32"-directory:
--      strcat_s(szTemp, nTempLen, "\\system32");
--      strcat_s(szSymPath, nSymPathLen, szTemp);
--      strcat_s(szSymPath, nSymPathLen, ";");
-+      _tcscat_s(szTemp, nTempLen, _T("\\system32"));
-+      _tcscat_s(szSymPath, nSymPathLen, szTemp);
-+      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
-     }
- 
-     if ((this->m_options & SymUseSymSrv) != 0)
-     {
--      if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)
-+      if (GetEnvironmentVariable(_T("SYSTEMDRIVE"), szTemp, nTempLen) > 0)
-       {
-         szTemp[nTempLen - 1] = 0;
--        strcat_s(szSymPath, nSymPathLen, "SRV*");
--        strcat_s(szSymPath, nSymPathLen, szTemp);
--        strcat_s(szSymPath, nSymPathLen, "\\websymbols");
--        strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");
-+        _tcscat_s(szSymPath, nSymPathLen, _T("SRV*"));
-+        _tcscat_s(szSymPath, nSymPathLen, szTemp);
-+        _tcscat_s(szSymPath, nSymPathLen, _T("\\websymbols"));
-+        _tcscat_s(szSymPath, nSymPathLen, _T("*http://msdl.microsoft.com/download/symbols;"));
-       }
-       else
--        strcat_s(szSymPath, nSymPathLen,
--                 "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
-+        _tcscat_s(szSymPath, nSymPathLen,
-+                 _T("SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"));
-     }
-   } // if SymBuildPath
- 
-@@ -1013,7 +916,7 @@ BOOL StackWalker::LoadModules()
-   szSymPath = NULL;
-   if (bRet == FALSE)
-   {
--    this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
-+    this->OnDbgHelpErr(_T("Error while initializing dbghelp.dll"), 0, 0);
-     SetLastError(ERROR_DLL_INIT_FAILED);
-     return FALSE;
-   }
-@@ -1038,9 +941,10 @@ BOOL StackWalker::ShowCallstack(HANDLE                    hThread,
- {
-   CONTEXT                                   c;
-   CallstackEntry                            csEntry;
--  IMAGEHLP_SYMBOL64*                        pSym = NULL;
-+
-+  tSymbolInfo* pSym = NULL;
-   StackWalkerInternal::IMAGEHLP_MODULE64_V3 Module;
--  IMAGEHLP_LINE64                           Line;
-+  tImageHelperLine                           Line;
-   int                                       frameNum;
-   bool                                      bLastEntryCalled = true;
-   int                                       curRecursionCount = 0;
-@@ -1125,12 +1029,12 @@ BOOL StackWalker::ShowCallstack(HANDLE                    hThread,
- #error "Platform not supported!"
- #endif
- 
--  pSym = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
-+  pSym = (tSymbolInfo*)malloc(sizeof(tSymbolInfo) + STACKWALK_MAX_NAMELEN*sizeof(TCHAR));
-   if (!pSym)
-     goto cleanup; // not enough memory...
--  memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
--  pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
--  pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
-+  memset(pSym, 0, sizeof(tSymbolInfo) + STACKWALK_MAX_NAMELEN*sizeof(TCHAR));
-+  pSym->SizeOfStruct = sizeof(tSymbolInfo);
-+  pSym->MaxNameLen = STACKWALK_MAX_NAMELEN;
- 
-   memset(&Line, 0, sizeof(Line));
-   Line.SizeOfStruct = sizeof(Line);
-@@ -1145,11 +1049,11 @@ BOOL StackWalker::ShowCallstack(HANDLE                    hThread,
-     // assume that either you are done, or that the stack is so hosed that the next
-     // deeper frame could not be found.
-     // CONTEXT need not to be supplied if imageTyp is IMAGE_FILE_MACHINE_I386!
--    if (!this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem,
--                         this->m_sw->pSFTA, this->m_sw->pSGMB, NULL))
-+    if (!this->m_sw->stackWalk64(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem,
-+                         this->m_sw->symFunctionTableAccess64, this->m_sw->symGetModuleBase64, NULL))
-     {
-       // INFO: "StackWalk64" does not set "GetLastError"...
--      this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset);
-+      this->OnDbgHelpErr(_T("StackWalk64"), 0, s.AddrPC.Offset);
-       break;
-     }
- 
-@@ -1167,7 +1071,7 @@ BOOL StackWalker::ShowCallstack(HANDLE                    hThread,
-     {
-       if ((this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount))
-       {
--        this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
-+        this->OnDbgHelpErr(_T("StackWalk64-Endless-Callstack!"), 0, s.AddrPC.Offset);
-         break;
-       }
-       curRecursionCount++;
-@@ -1178,23 +1082,23 @@ BOOL StackWalker::ShowCallstack(HANDLE                    hThread,
-     {
-       // we seem to have a valid PC
-       // show procedure info (SymGetSymFromAddr64())
--      if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol),
-+      if (this->m_sw->symFromAddr(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol),
-                              pSym) != FALSE)
-       {
-         MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name);
-         // UnDecorateSymbolName()
--        this->m_sw->pUDSN(pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY);
--        this->m_sw->pUDSN(pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE);
-+        DWORD res = this->m_sw->unDecorateSymbolName(pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY);
-+        res = this->m_sw->unDecorateSymbolName(pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE);
-       }
-       else
-       {
--        this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
-+        this->OnDbgHelpErr(_T("SymGetSymFromAddr64"), GetLastError(), s.AddrPC.Offset);
-       }
- 
-       // show line number info, NT5.0-method (SymGetLineFromAddr64())
--      if (this->m_sw->pSGLFA != NULL)
-+      if (this->m_sw->symGetLineFromAddr64 != NULL)
-       { // yes, we have SymGetLineFromAddr64()
--        if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine),
-+        if (this->m_sw->symGetLineFromAddr64(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine),
-                                &Line) != FALSE)
-         {
-           csEntry.lineNumber = Line.LineNumber;
-@@ -1202,7 +1106,7 @@ BOOL StackWalker::ShowCallstack(HANDLE                    hThread,
-         }
-         else
-         {
--          this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
-+          this->OnDbgHelpErr(_T("SymGetLineFromAddr64"), GetLastError(), s.AddrPC.Offset);
-         }
-       } // yes, we have SymGetLineFromAddr64()
- 
-@@ -1252,7 +1156,7 @@ BOOL StackWalker::ShowCallstack(HANDLE                    hThread,
-       } // got module info OK
-       else
-       {
--        this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
-+        this->OnDbgHelpErr(_T("SymGetModuleInfo64"), GetLastError(), s.AddrPC.Offset);
-       }
-     } // we seem to have a valid PC
- 
-@@ -1298,20 +1202,20 @@ BOOL StackWalker::ShowObject(LPVOID pObject)
-   }
- 
-   // SymGetSymFromAddr64() is required
--  if (this->m_sw->pSGSFA == NULL)
-+  if (this->m_sw->symFromAddr == NULL)
-     return FALSE;
- 
-   // Show object info (SymGetSymFromAddr64())
-   DWORD64            dwAddress = DWORD64(pObject);
-   DWORD64            dwDisplacement = 0;
--  IMAGEHLP_SYMBOL64* pSym =
--      (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
--  memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
--  pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
--  pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
--  if (this->m_sw->pSGSFA(this->m_hProcess, dwAddress, &dwDisplacement, pSym) == FALSE)
-+  tSymbolInfo* pSym =
-+      (tSymbolInfo*)malloc(sizeof(tSymbolInfo) + STACKWALK_MAX_NAMELEN*sizeof(TCHAR));
-+  memset(pSym, 0, sizeof(tSymbolInfo) + STACKWALK_MAX_NAMELEN*sizeof(TCHAR));
-+  pSym->SizeOfStruct = sizeof(tSymbolInfo);
-+  pSym->MaxNameLen = STACKWALK_MAX_NAMELEN;
-+  if (this->m_sw->symFromAddr(this->m_hProcess, dwAddress, &dwDisplacement, pSym) == FALSE)
-   {
--    this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), dwAddress);
-+    this->OnDbgHelpErr(_T("SymGetSymFromAddr64"), GetLastError(), dwAddress);
-     return FALSE;
-   }
-   // Object name output
-@@ -1342,22 +1246,22 @@ BOOL __stdcall StackWalker::myReadProcMem(HANDLE  hProcess,
-   }
- }
- 
--void StackWalker::OnLoadModule(LPCSTR    img,
--                               LPCSTR    mod,
-+void StackWalker::OnLoadModule(LPCTSTR    img,
-+                               LPCTSTR    mod,
-                                DWORD64   baseAddr,
-                                DWORD     size,
-                                DWORD     result,
--                               LPCSTR    symType,
--                               LPCSTR    pdbName,
-+                               LPCTSTR    symType,
-+                               LPCTSTR    pdbName,
-                                ULONGLONG fileVersion)
- {
--  CHAR   buffer[STACKWALK_MAX_NAMELEN];
-+  TCHAR   buffer[STACKWALK_MAX_NAMELEN];
-   size_t maxLen = STACKWALK_MAX_NAMELEN;
- #if _MSC_VER >= 1400
-   maxLen = _TRUNCATE;
- #endif
-   if (fileVersion == 0)
--    _snprintf_s(buffer, maxLen, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n",
-+    _sntprintf_s(buffer, maxLen, _T("%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n"),
-                 img, mod, (LPVOID)baseAddr, size, result, symType, pdbName);
-   else
-   {
-@@ -1365,9 +1269,9 @@ void StackWalker::OnLoadModule(LPCSTR    img,
-     DWORD v3 = (DWORD)((fileVersion >> 16) & 0xFFFF);
-     DWORD v2 = (DWORD)((fileVersion >> 32) & 0xFFFF);
-     DWORD v1 = (DWORD)((fileVersion >> 48) & 0xFFFF);
--    _snprintf_s(
-+    _sntprintf_s(
-         buffer, maxLen,
--        "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n",
-+        _T("%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n"),
-         img, mod, (LPVOID)baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);
-   }
-   buffer[STACKWALK_MAX_NAMELEN - 1] = 0; // be sure it is NULL terminated
-@@ -1376,7 +1280,7 @@ void StackWalker::OnLoadModule(LPCSTR    img,
- 
- void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry)
- {
--  CHAR   buffer[STACKWALK_MAX_NAMELEN];
-+  TCHAR   buffer[STACKWALK_MAX_NAMELEN];
-   size_t maxLen = STACKWALK_MAX_NAMELEN;
- #if _MSC_VER >= 1400
-   maxLen = _TRUNCATE;
-@@ -1384,48 +1288,48 @@ void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry& ent
-   if ((eType != lastEntry) && (entry.offset != 0))
-   {
-     if (entry.name[0] == 0)
--      MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)");
-+      MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, _T("(function-name not available)"));
-     if (entry.undName[0] != 0)
-       MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName);
-     if (entry.undFullName[0] != 0)
-       MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName);
-     if (entry.lineFileName[0] == 0)
-     {
--      MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)");
-+      MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, _T("(filename not available)"));
-       if (entry.moduleName[0] == 0)
--        MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)");
--      _snprintf_s(buffer, maxLen, "%p (%s): %s: %s\n", (LPVOID)entry.offset, entry.moduleName,
-+        MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, _T("(module-name not available)"));
-+      _sntprintf_s(buffer, maxLen, _T("%p (%s): %s: %s\n"), (LPVOID)entry.offset, entry.moduleName,
-                   entry.lineFileName, entry.name);
-     }
-     else
--      _snprintf_s(buffer, maxLen, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber,
-+      _sntprintf_s(buffer, maxLen, _T("%s (%d): %s\n"), entry.lineFileName, entry.lineNumber,
-                   entry.name);
-     buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
-     OnOutput(buffer);
-   }
- }
- 
--void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
-+void StackWalker::OnDbgHelpErr(LPCTSTR szFuncName, DWORD gle, DWORD64 addr)
- {
--  CHAR   buffer[STACKWALK_MAX_NAMELEN];
-+  TCHAR   buffer[STACKWALK_MAX_NAMELEN];
-   size_t maxLen = STACKWALK_MAX_NAMELEN;
- #if _MSC_VER >= 1400
-   maxLen = _TRUNCATE;
- #endif
--  _snprintf_s(buffer, maxLen, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle,
-+  _sntprintf_s(buffer, maxLen, _T("ERROR: %s, GetLastError: %d (Address: %p)\n"), szFuncName, gle,
-               (LPVOID)addr);
-   buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
-   OnOutput(buffer);
- }
- 
--void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
-+void StackWalker::OnSymInit(LPCTSTR szSearchPath, DWORD symOptions, LPCTSTR szUserName)
- {
--  CHAR   buffer[STACKWALK_MAX_NAMELEN];
-+  TCHAR   buffer[STACKWALK_MAX_NAMELEN];
-   size_t maxLen = STACKWALK_MAX_NAMELEN;
- #if _MSC_VER >= 1400
-   maxLen = _TRUNCATE;
- #endif
--  _snprintf_s(buffer, maxLen, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n",
-+  _sntprintf_s(buffer, maxLen, _T("SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n"),
-               szSearchPath, symOptions, szUserName);
-   buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
-   OnOutput(buffer);
-@@ -1442,16 +1346,16 @@ void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUser
-     OnOutput(buffer);
-   }
- #else
--  OSVERSIONINFOEXA ver;
--  ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));
-+  OSVERSIONINFOEX ver;
-+  ZeroMemory(&ver, sizeof(OSVERSIONINFOEX));
-   ver.dwOSVersionInfoSize = sizeof(ver);
- #if _MSC_VER >= 1900
- #pragma warning(push)
- #pragma warning(disable : 4996)
- #endif
--  if (GetVersionExA((OSVERSIONINFOA*)&ver) != FALSE)
-+  if (GetVersionEx((OSVERSIONINFO*)&ver) != FALSE)
-   {
--    _snprintf_s(buffer, maxLen, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", ver.dwMajorVersion,
-+    _sntprintf_s(buffer, maxLen, _T("OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n"), ver.dwMajorVersion,
-                 ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion, ver.wSuiteMask,
-                 ver.wProductType);
-     buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
-@@ -1463,7 +1367,7 @@ void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUser
- #endif
- }
- 
--void StackWalker::OnOutput(LPCSTR buffer)
-+void StackWalker::OnOutput(LPCTSTR buffer)
- {
--  OutputDebugStringA(buffer);
-+  OutputDebugString(buffer);
- }
-diff --git a/Main/StackWalker/StackWalker.h b/Main/StackWalker/StackWalker.h
-index 0a004d9..03efcec 100644
---- a/Main/StackWalker/StackWalker.h
-+++ b/Main/StackWalker/StackWalker.h
-@@ -47,16 +47,6 @@
- #pragma warning(disable : 4091)
- #endif
- 
--// special defines for VC5/6 (if no actual PSDK is installed):
--#if _MSC_VER < 1300
--typedef unsigned __int64 DWORD64, *PDWORD64;
--#if defined(_WIN64)
--typedef unsigned __int64 SIZE_T, *PSIZE_T;
--#else
--typedef unsigned long SIZE_T, *PSIZE_T;
--#endif
--#endif // _MSC_VER < 1300
--
- class StackWalkerInternal; // forward
- class StackWalker
- {
-@@ -96,7 +86,7 @@ public:
-   } StackWalkOptions;
- 
-   StackWalker(int    options = OptionsAll, // 'int' is by design, to combine the enum-flags
--              LPCSTR szSymPath = NULL,
-+              LPCTSTR szSymPath = NULL,
-               DWORD  dwProcessId = GetCurrentProcessId(),
-               HANDLE hProcess = GetCurrentProcess());
-   StackWalker(DWORD dwProcessId, HANDLE hProcess);
-@@ -137,18 +127,18 @@ protected:
-   typedef struct CallstackEntry
-   {
-     DWORD64 offset; // if 0, we have no valid entry
--    CHAR    name[STACKWALK_MAX_NAMELEN];
--    CHAR    undName[STACKWALK_MAX_NAMELEN];
--    CHAR    undFullName[STACKWALK_MAX_NAMELEN];
-+    TCHAR    name[STACKWALK_MAX_NAMELEN];
-+    TCHAR    undName[STACKWALK_MAX_NAMELEN];
-+    TCHAR    undFullName[STACKWALK_MAX_NAMELEN];
-     DWORD64 offsetFromSmybol;
-     DWORD   offsetFromLine;
-     DWORD   lineNumber;
--    CHAR    lineFileName[STACKWALK_MAX_NAMELEN];
-+    TCHAR    lineFileName[STACKWALK_MAX_NAMELEN];
-     DWORD   symType;
-     LPCSTR  symTypeString;
--    CHAR    moduleName[STACKWALK_MAX_NAMELEN];
-+    TCHAR    moduleName[STACKWALK_MAX_NAMELEN];
-     DWORD64 baseOfImage;
--    CHAR    loadedImageName[STACKWALK_MAX_NAMELEN];
-+    TCHAR    loadedImageName[STACKWALK_MAX_NAMELEN];
-   } CallstackEntry;
- 
-   typedef enum CallstackEntryType
-@@ -158,24 +148,24 @@ protected:
-     lastEntry
-   } CallstackEntryType;
- 
--  virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
--  virtual void OnLoadModule(LPCSTR    img,
--                            LPCSTR    mod,
-+  virtual void OnSymInit(LPCTSTR szSearchPath, DWORD symOptions, LPCTSTR szUserName);
-+  virtual void OnLoadModule(LPCTSTR    img,
-+                            LPCTSTR    mod,
-                             DWORD64   baseAddr,
-                             DWORD     size,
-                             DWORD     result,
--                            LPCSTR    symType,
--                            LPCSTR    pdbName,
-+                            LPCTSTR    symType,
-+                            LPCTSTR    pdbName,
-                             ULONGLONG fileVersion);
-   virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry);
--  virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
--  virtual void OnOutput(LPCSTR szText);
-+  virtual void OnDbgHelpErr(LPCTSTR szFuncName, DWORD gle, DWORD64 addr);
-+  virtual void OnOutput(LPCTSTR szText);
- 
-   StackWalkerInternal* m_sw;
-   HANDLE               m_hProcess;
-   DWORD                m_dwProcessId;
-   BOOL                 m_modulesLoaded;
--  LPSTR                m_szSymPath;
-+  LPTSTR               m_szSymPath;
- 
-   int m_options;
-   int m_MaxRecursionCount;
-diff --git a/Main/StackWalker/StackWalker_VC2017.sln b/Main/StackWalker/StackWalker_VC2017.sln
-index 790d550..2209e23 100644
---- a/Main/StackWalker/StackWalker_VC2017.sln
-+++ b/Main/StackWalker/StackWalker_VC2017.sln
-@@ -16,18 +16,18 @@ Global
- 		Release_VC2017-UNICODE|x64 = Release_VC2017-UNICODE|x64
- 	EndGlobalSection
- 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
--		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017|Win32.ActiveCfg = Debug_VC2017-UNICODE|Win32
--		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017|Win32.Build.0 = Debug_VC2017-UNICODE|Win32
--		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017|x64.ActiveCfg = Debug_VC2017-UNICODE|x64
--		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017|x64.Build.0 = Debug_VC2017-UNICODE|x64
-+		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017|Win32.ActiveCfg = Debug_VC2017|Win32
-+		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017|Win32.Build.0 = Debug_VC2017|Win32
-+		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017|x64.ActiveCfg = Debug_VC2017|x64
-+		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017|x64.Build.0 = Debug_VC2017|x64
- 		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017-UNICODE|Win32.ActiveCfg = Debug_VC2017-UNICODE|Win32
- 		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017-UNICODE|Win32.Build.0 = Debug_VC2017-UNICODE|Win32
- 		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017-UNICODE|x64.ActiveCfg = Debug_VC2017-UNICODE|x64
- 		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Debug_VC2017-UNICODE|x64.Build.0 = Debug_VC2017-UNICODE|x64
--		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017|Win32.ActiveCfg = Release_VC2017-UNICODE|Win32
--		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017|Win32.Build.0 = Release_VC2017-UNICODE|Win32
--		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017|x64.ActiveCfg = Release_VC2017-UNICODE|x64
--		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017|x64.Build.0 = Release_VC2017-UNICODE|x64
-+		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017|Win32.ActiveCfg = Release_VC2017|Win32
-+		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017|Win32.Build.0 = Release_VC2017|Win32
-+		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017|x64.ActiveCfg = Release_VC2017|x64
-+		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017|x64.Build.0 = Release_VC2017|x64
- 		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017-UNICODE|Win32.ActiveCfg = Release_VC2017-UNICODE|Win32
- 		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017-UNICODE|Win32.Build.0 = Release_VC2017-UNICODE|Win32
- 		{89B2BD42-B130-4811-9043-71A8EBC40DE5}.Release_VC2017-UNICODE|x64.ActiveCfg = Release_VC2017-UNICODE|x64
-diff --git a/Main/StackWalker/main.cpp b/Main/StackWalker/main.cpp
-index 220c97b..496021e 100644
---- a/Main/StackWalker/main.cpp
-+++ b/Main/StackWalker/main.cpp
-@@ -33,7 +33,7 @@ void (*pGlobalFuncPtr)() = 0;
- class StackWalkerToConsole : public StackWalker
- {
- protected:
--  virtual void OnOutput(LPCSTR szText) { printf("%s", szText); }
-+  virtual void OnOutput(LPCTSTR szText) { _tprintf(_T("%s"), szText); }
- };
- 
- void Func5()
diff --git a/third_party/allwpilib/upstream_utils/stack_walker_patches/0002-Remove-_M_IX86-checks.patch b/third_party/allwpilib/upstream_utils/stack_walker_patches/0002-Remove-_M_IX86-checks.patch
deleted file mode 100644
index 14d7bae..0000000
--- a/third_party/allwpilib/upstream_utils/stack_walker_patches/0002-Remove-_M_IX86-checks.patch
+++ /dev/null
@@ -1,79 +0,0 @@
-From 238eda525de70b57bade634447c967f4f92bc96d Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Mon, 23 May 2022 00:06:45 -0400
-Subject: [PATCH 2/3] Remove _M_IX86 checks
-
----
- Main/StackWalker/StackWalker.h | 52 ----------------------------------
- 1 file changed, 52 deletions(-)
-
-diff --git a/Main/StackWalker/StackWalker.h b/Main/StackWalker/StackWalker.h
-index 03efcec..89be951 100644
---- a/Main/StackWalker/StackWalker.h
-+++ b/Main/StackWalker/StackWalker.h
-@@ -179,57 +179,6 @@ protected:
-   friend StackWalkerInternal;
- }; // class StackWalker
- 
--// The "ugly" assembler-implementation is needed for systems before XP
--// If you have a new PSDK and you only compile for XP and later, then you can use
--// the "RtlCaptureContext"
--// Currently there is no define which determines the PSDK-Version...
--// So we just use the compiler-version (and assumes that the PSDK is
--// the one which was installed by the VS-IDE)
--
--// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
--//       But I currently use it in x64/IA64 environments...
--//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
--
--#if defined(_M_IX86)
--#ifdef CURRENT_THREAD_VIA_EXCEPTION
--// TODO: The following is not a "good" implementation,
--// because the callstack is only valid in the "__except" block...
--#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags)               \
--  do                                                                            \
--  {                                                                             \
--    memset(&c, 0, sizeof(CONTEXT));                                             \
--    EXCEPTION_POINTERS* pExp = NULL;                                            \
--    __try                                                                       \
--    {                                                                           \
--      throw 0;                                                                  \
--    }                                                                           \
--    __except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER   \
--                                                  : EXCEPTION_EXECUTE_HANDLER)) \
--    {                                                                           \
--    }                                                                           \
--    if (pExp != NULL)                                                           \
--      memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT));                         \
--    c.ContextFlags = contextFlags;                                              \
--  } while (0);
--#else
--// clang-format off
--// The following should be enough for walking the callstack...
--#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
--  do                                                              \
--  {                                                               \
--    memset(&c, 0, sizeof(CONTEXT));                               \
--    c.ContextFlags = contextFlags;                                \
--    __asm    call x                                               \
--    __asm x: pop eax                                              \
--    __asm    mov c.Eip, eax                                       \
--    __asm    mov c.Ebp, ebp                                       \
--    __asm    mov c.Esp, esp                                       \
--  } while (0)
--// clang-format on
--#endif
--
--#else
--
- // The following is defined for x86 (XP and higher), x64 and IA64:
- #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
-   do                                                              \
-@@ -238,7 +187,6 @@ protected:
-     c.ContextFlags = contextFlags;                                \
-     RtlCaptureContext(&c);                                        \
-   } while (0);
--#endif
- 
- #endif //defined(_MSC_VER)
- 
diff --git a/third_party/allwpilib/upstream_utils/stack_walker_patches/0003-Add-advapi-pragma.patch b/third_party/allwpilib/upstream_utils/stack_walker_patches/0003-Add-advapi-pragma.patch
deleted file mode 100644
index f48ee4e..0000000
--- a/third_party/allwpilib/upstream_utils/stack_walker_patches/0003-Add-advapi-pragma.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-From 61646f76602a77059c18298caa575b95b702c94c Mon Sep 17 00:00:00 2001
-From: PJ Reiniger <pj.reiniger@gmail.com>
-Date: Tue, 24 May 2022 01:24:31 -0400
-Subject: [PATCH 3/3] Add advapi pragma
-
----
- Main/StackWalker/StackWalker.cpp | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/Main/StackWalker/StackWalker.cpp b/Main/StackWalker/StackWalker.cpp
-index 48c7c57..6f0fbf2 100644
---- a/Main/StackWalker/StackWalker.cpp
-+++ b/Main/StackWalker/StackWalker.cpp
-@@ -88,6 +88,7 @@
- #include <stdlib.h>
- #include <tchar.h>
- #pragma comment(lib, "version.lib") // for "VerQueryValue"
-+#pragma comment(lib, "Advapi32.lib") // for "GetUserName"
- #pragma warning(disable : 4826)
- 
- #ifdef UNICODE
diff --git a/third_party/allwpilib/upstream_utils/update_drake.py b/third_party/allwpilib/upstream_utils/update_drake.py
deleted file mode 100755
index 1849494..0000000
--- a/third_party/allwpilib/upstream_utils/update_drake.py
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-import shutil
-
-from upstream_utils import (
-    get_repo_root,
-    clone_repo,
-    comment_out_invalid_includes,
-    walk_cwd_and_copy_if,
-    git_am,
-)
-
-
-def main():
-    upstream_root = clone_repo("https://github.com/RobotLocomotion/drake", "v1.6.0")
-    wpilib_root = get_repo_root()
-    wpimath = os.path.join(wpilib_root, "wpimath")
-
-    # Apply patches to upstream Git repo
-    os.chdir(upstream_root)
-    for f in [
-        "0001-Replace-Eigen-Dense-with-Eigen-Core.patch",
-        "0002-Add-WPILIB_DLLEXPORT-to-DARE-function-declarations.patch",
-    ]:
-        git_am(os.path.join(wpilib_root, "upstream_utils/drake_patches", f))
-
-    # Delete old install
-    for d in [
-        "src/main/native/thirdparty/drake/src",
-        "src/main/native/thirdparty/drake/include",
-        "src/test/native/cpp/drake",
-        "src/test/native/include/drake",
-    ]:
-        shutil.rmtree(os.path.join(wpimath, d), ignore_errors=True)
-
-    # Copy drake source files into allwpilib
-    src_files = walk_cwd_and_copy_if(
-        lambda dp, f: f
-        in ["drake_assert_and_throw.cc", "discrete_algebraic_riccati_equation.cc"],
-        os.path.join(wpimath, "src/main/native/thirdparty/drake/src"),
-    )
-
-    # Copy drake header files into allwpilib
-    include_files = walk_cwd_and_copy_if(
-        lambda dp, f: f
-        in [
-            "drake_assert.h",
-            "drake_assertion_error.h",
-            "is_approx_equal_abstol.h",
-            "never_destroyed.h",
-            "drake_copyable.h",
-            "drake_throw.h",
-            "discrete_algebraic_riccati_equation.h",
-        ],
-        os.path.join(wpimath, "src/main/native/thirdparty/drake/include/drake"),
-    )
-
-    # Copy drake test source files into allwpilib
-    os.chdir(os.path.join(upstream_root, "math/test"))
-    test_src_files = walk_cwd_and_copy_if(
-        lambda dp, f: f == "discrete_algebraic_riccati_equation_test.cc",
-        os.path.join(wpimath, "src/test/native/cpp/drake"),
-    )
-    os.chdir(upstream_root)
-
-    # Copy drake test header files into allwpilib
-    test_include_files = walk_cwd_and_copy_if(
-        lambda dp, f: f == "eigen_matrix_compare.h",
-        os.path.join(wpimath, "src/test/native/include/drake"),
-    )
-
-    for f in src_files:
-        comment_out_invalid_includes(
-            f, [os.path.join(wpimath, "src/main/native/thirdparty/drake/include")]
-        )
-    for f in include_files:
-        comment_out_invalid_includes(
-            f, [os.path.join(wpimath, "src/main/native/thirdparty/drake/include")]
-        )
-    for f in test_src_files:
-        comment_out_invalid_includes(
-            f,
-            [
-                os.path.join(wpimath, "src/main/native/thirdparty/drake/include"),
-                os.path.join(wpimath, "src/test/native/include"),
-            ],
-        )
-    for f in test_include_files:
-        comment_out_invalid_includes(
-            f,
-            [
-                os.path.join(wpimath, "src/main/native/thirdparty/drake/include"),
-                os.path.join(wpimath, "src/test/native/include"),
-            ],
-        )
-
-
-if __name__ == "__main__":
-    main()
diff --git a/third_party/allwpilib/upstream_utils/update_eigen.py b/third_party/allwpilib/upstream_utils/update_eigen.py
index e04689a..ce4c620 100755
--- a/third_party/allwpilib/upstream_utils/update_eigen.py
+++ b/third_party/allwpilib/upstream_utils/update_eigen.py
@@ -67,14 +67,10 @@
         "SparseCore",
         "SparseLU",
         "SparseQR",
-        "StlSupport",
         "misc",
         "plugins",
     ]
-    modules_rgx = r"|".join("/" + m for m in modules)
-
-    # "Std" matches StdDeque, StdList, and StdVector headers. Other modules are excluded.
-    return bool(re.search(modules_rgx, abspath) or "Std" in f)
+    return bool(re.search(r"|".join("/" + m for m in modules), abspath))
 
 
 def unsupported_inclusions(dp, f):
@@ -105,7 +101,11 @@
 
     # Apply patches to upstream Git repo
     os.chdir(upstream_root)
-    for f in ["0001-Disable-warnings.patch"]:
+    for f in [
+        "0001-Disable-warnings.patch",
+        "0002-Intellisense-fix.patch",
+        "0003-Eigen-Sparse-fix-warnings-Wunused-but-set-variable.patch",
+    ]:
         git_am(os.path.join(wpilib_root, "upstream_utils/eigen_patches", f))
 
     # Delete old install
diff --git a/third_party/allwpilib/upstream_utils/update_fmt.py b/third_party/allwpilib/upstream_utils/update_fmt.py
index 1088c39..746e879 100755
--- a/third_party/allwpilib/upstream_utils/update_fmt.py
+++ b/third_party/allwpilib/upstream_utils/update_fmt.py
@@ -13,7 +13,7 @@
 
 
 def main():
-    upstream_root = clone_repo("https://github.com/fmtlib/fmt", "9.1.0")
+    upstream_root = clone_repo("https://github.com/fmtlib/fmt", "10.1.1")
     wpilib_root = get_repo_root()
     wpiutil = os.path.join(wpilib_root, "wpiutil")
 
@@ -21,7 +21,7 @@
     os.chdir(upstream_root)
     for f in [
         "0001-Don-t-throw-on-write-failure.patch",
-        "0002-Suppress-C-20-clang-tidy-warning-false-positive.patch",
+        "0002-Suppress-warnings-we-can-t-fix.patch",
     ]:
         git_am(os.path.join(wpilib_root, "upstream_utils/fmt_patches", f))
 
@@ -34,13 +34,13 @@
 
     # Copy fmt source files into allwpilib
     src_files = walk_cwd_and_copy_if(
-        lambda dp, f: dp.endswith("src") and f.endswith(".cc") and f != "fmt.cc",
+        lambda dp, f: dp.startswith("./src") and f.endswith(".cc") and f != "fmt.cc",
         os.path.join(wpiutil, "src/main/native/thirdparty/fmtlib"),
     )
 
     # Copy fmt header files into allwpilib
     include_files = walk_cwd_and_copy_if(
-        lambda dp, f: dp.endswith("include/fmt"),
+        lambda dp, f: dp.startswith("./include/fmt"),
         os.path.join(wpiutil, "src/main/native/thirdparty/fmtlib"),
     )
 
diff --git a/third_party/allwpilib/upstream_utils/update_gcem.py b/third_party/allwpilib/upstream_utils/update_gcem.py
old mode 100644
new mode 100755
index ad1cb5a..0f3fe1f
--- a/third_party/allwpilib/upstream_utils/update_gcem.py
+++ b/third_party/allwpilib/upstream_utils/update_gcem.py
@@ -13,7 +13,7 @@
 
 
 def main():
-    upstream_root = clone_repo("https://github.com/kthohr/gcem.git", "v1.16.0")
+    upstream_root = clone_repo("https://github.com/kthohr/gcem.git", "v1.17.0")
     wpilib_root = get_repo_root()
     wpimath = os.path.join(wpilib_root, "wpimath")
 
@@ -30,9 +30,7 @@
 
     # Copy gcem include files into allwpilib
     include_files = walk_cwd_and_copy_if(
-        lambda dp, f: dp.endswith("include")
-        or dp.endswith("gcem_incl")
-        or dp.endswith("quadrature"),
+        lambda dp, f: dp.startswith("./include"),
         os.path.join(wpimath, "src/main/native/thirdparty/gcem"),
     )
 
diff --git a/third_party/allwpilib/upstream_utils/update_json.py b/third_party/allwpilib/upstream_utils/update_json.py
new file mode 100755
index 0000000..1f5839c
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/update_json.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+
+import os
+import shutil
+
+from upstream_utils import (
+    get_repo_root,
+    clone_repo,
+    walk_if,
+    git_am,
+)
+
+
+def main():
+    upstream_root = clone_repo("https://github.com/nlohmann/json", "v3.11.2")
+    wpilib_root = get_repo_root()
+    wpiutil = os.path.join(wpilib_root, "wpiutil")
+
+    # Apply patches to upstream Git repo
+    os.chdir(upstream_root)
+    for f in [
+        "0001-Remove-version-from-namespace.patch",
+        "0002-Make-serializer-public.patch",
+        "0003-Make-dump_escaped-take-std-string_view.patch",
+        "0004-Add-llvm-stream-support.patch",
+    ]:
+        git_am(
+            os.path.join(wpilib_root, "upstream_utils/json_patches", f),
+            use_threeway=True,
+        )
+
+    # Delete old install
+    for d in [
+        "src/main/native/thirdparty/json/include",
+    ]:
+        shutil.rmtree(os.path.join(wpiutil, d), ignore_errors=True)
+
+    # Create lists of source and destination files
+    os.chdir(os.path.join(upstream_root, "include/nlohmann"))
+    files = walk_if(".", lambda dp, f: True)
+    src_include_files = [
+        os.path.join(os.path.join(upstream_root, "include/nlohmann"), f) for f in files
+    ]
+    wpiutil_json_root = os.path.join(
+        wpiutil, "src/main/native/thirdparty/json/include/wpi"
+    )
+    dest_include_files = [
+        os.path.join(wpiutil_json_root, f.replace(".hpp", ".h")) for f in files
+    ]
+
+    # Copy json header files into allwpilib
+    for i in range(len(src_include_files)):
+        dest_dir = os.path.dirname(dest_include_files[i])
+        if not os.path.exists(dest_dir):
+            os.makedirs(dest_dir)
+        shutil.copyfile(src_include_files[i], dest_include_files[i])
+
+    for include_file in dest_include_files:
+        with open(include_file) as f:
+            content = f.read()
+
+        # Rename namespace from nlohmann to wpi
+        content = content.replace("namespace nlohmann", "namespace wpi")
+        content = content.replace("nlohmann::", "wpi::")
+
+        # Fix internal includes
+        content = content.replace(".hpp>", ".h>")
+        content = content.replace("include <nlohmann/", "include <wpi/")
+
+        # Fix include guards and other #defines
+        content = content.replace("NLOHMANN_", "WPI_")
+
+        with open(include_file, "w") as f:
+            f.write(content)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/third_party/allwpilib/upstream_utils/update_libuv.py b/third_party/allwpilib/upstream_utils/update_libuv.py
index a90a6d7..179632f 100755
--- a/third_party/allwpilib/upstream_utils/update_libuv.py
+++ b/third_party/allwpilib/upstream_utils/update_libuv.py
@@ -13,22 +13,23 @@
 
 
 def main():
-    upstream_root = clone_repo("https://github.com/libuv/libuv", "v1.44.2")
+    upstream_root = clone_repo("https://github.com/libuv/libuv", "v1.46.0")
     wpilib_root = get_repo_root()
     wpinet = os.path.join(wpilib_root, "wpinet")
 
     # Apply patches to upstream Git repo
     os.chdir(upstream_root)
     for f in [
-        "0001-Fix-missing-casts.patch",
-        "0002-Fix-warnings.patch",
-        "0003-Preprocessor-cleanup.patch",
-        "0004-Cleanup-problematic-language.patch",
-        "0005-Use-roborio-time.patch",
+        "0001-Revert-win-process-write-minidumps-when-sending-SIGQ.patch",
+        "0002-Fix-missing-casts.patch",
+        "0003-Fix-warnings.patch",
+        "0004-Preprocessor-cleanup.patch",
+        "0005-Cleanup-problematic-language.patch",
         "0006-Style-comments-cleanup.patch",
-        "0007-Squelch-GCC-12.1-warnings.patch",
-        "0008-Fix-Win32-warning-suppression-pragma.patch",
-        "0009-Avoid-unused-variable-warning-on-Mac.patch",
+        "0007-Fix-Win32-warning-suppression-pragma.patch",
+        "0008-Use-C-atomics.patch",
+        "0009-Remove-static-from-array-indices.patch",
+        "0010-Add-pragmas-for-missing-libraries-and-set-_WIN32_WIN.patch",
     ]:
         git_am(os.path.join(wpilib_root, "upstream_utils/libuv_patches", f))
 
@@ -44,7 +45,7 @@
     ]
 
     include_files = walk_cwd_and_copy_if(
-        lambda dp, f: "include" in dp and f not in include_ignorelist,
+        lambda dp, f: dp.startswith("./include") and f not in include_ignorelist,
         os.path.join(wpinet, "src/main/native/thirdparty/libuv"),
     )
 
@@ -65,7 +66,7 @@
         "sysinfo-memory.c",
     ]
     src_files = walk_cwd_and_copy_if(
-        lambda dp, f: "src" in dp and "docs" not in dp and f not in src_ignorelist,
+        lambda dp, f: dp.startswith("./src") and f not in src_ignorelist,
         os.path.join(wpinet, "src/main/native/thirdparty/libuv"),
     )
 
diff --git a/third_party/allwpilib/upstream_utils/update_llvm.py b/third_party/allwpilib/upstream_utils/update_llvm.py
index c2ebd12..5910396 100755
--- a/third_party/allwpilib/upstream_utils/update_llvm.py
+++ b/third_party/allwpilib/upstream_utils/update_llvm.py
@@ -13,7 +13,6 @@
 
 
 def run_global_replacements(wpiutil_llvm_files):
-
     for wpi_file in wpiutil_llvm_files:
         with open(wpi_file) as f:
             content = f.read()
@@ -29,7 +28,7 @@
 
         # Fix uses of span
         content = content.replace("span", "std::span")
-        content = content.replace('include "wpi/std::span.h"', "include <span>")
+        content = content.replace("include <std::span>", "include <span>")
         if wpi_file.endswith("ConvertUTFWrapper.cpp"):
             content = content.replace(
                 "const UTF16 *Src = reinterpret_cast<const UTF16 *>(SrcBytes.begin());",
@@ -39,6 +38,14 @@
                 "const UTF16 *SrcEnd = reinterpret_cast<const UTF16 *>(SrcBytes.end());",
                 "const UTF16 *SrcEnd = reinterpret_cast<const UTF16 *>(&*SrcBytes.begin() + SrcBytes.size());",
             )
+            content = content.replace(
+                "const UTF32 *Src = reinterpret_cast<const UTF32 *>(SrcBytes.begin());",
+                "const UTF32 *Src = reinterpret_cast<const UTF32 *>(&*SrcBytes.begin());",
+            )
+            content = content.replace(
+                "const UTF32 *SrcEnd = reinterpret_cast<const UTF32 *>(SrcBytes.end());",
+                "const UTF32 *SrcEnd = reinterpret_cast<const UTF32 *>(&*SrcBytes.begin() + SrcBytes.size());",
+            )
 
         # Remove unused headers
         content = content.replace('#include "llvm-c/ErrorHandling.h"\n', "")
@@ -100,7 +107,6 @@
 
 
 def find_wpiutil_llvm_files(wpiutil_root, subfolder):
-
     # These files have substantial changes, not worth managing with the patching process
     ignore_list = [
         "StringExtras.h",
@@ -165,41 +171,46 @@
 
 
 def main():
-    upstream_root = clone_repo("https://github.com/llvm/llvm-project", "llvmorg-14.0.6")
+    upstream_root = clone_repo("https://github.com/llvm/llvm-project", "llvmorg-17.0.4")
     wpilib_root = get_repo_root()
     wpiutil = os.path.join(wpilib_root, "wpiutil")
 
     # Apply patches to upstream Git repo
     os.chdir(upstream_root)
     for f in [
-        "0001-Fix-spelling-language-errors.patch",
-        "0002-Remove-StringRef-ArrayRef-and-Optional.patch",
-        "0003-Wrap-std-min-max-calls-in-parens-for-Windows-warning.patch",
-        "0004-Change-unique_function-storage-size.patch",
-        "0005-Threading-updates.patch",
-        "0006-ifdef-guard-safety.patch",
-        "0007-Explicitly-use-std.patch",
-        "0008-Remove-format_provider.patch",
-        "0009-Add-compiler-warning-pragmas.patch",
-        "0010-Remove-unused-functions.patch",
-        "0011-Detemplatize-SmallVectorBase.patch",
-        "0012-Add-vectors-to-raw_ostream.patch",
-        "0013-Extra-collections-features.patch",
-        "0014-EpochTracker-ABI-macro.patch",
-        "0015-Delete-numbers-from-MathExtras.patch",
-        "0016-Add-lerp-and-sgn.patch",
-        "0017-Fixup-includes.patch",
-        "0018-Use-std-is_trivially_copy_constructible.patch",
-        "0019-Windows-support.patch",
-        "0020-Prefer-fmtlib.patch",
-        "0021-Prefer-wpi-s-fs.h.patch",
-        "0022-Remove-unused-functions.patch",
-        "0023-OS-specific-changes.patch",
-        "0024-Use-SmallVector-for-UTF-conversion.patch",
-        "0025-Prefer-to-use-static-pointers-in-raw_ostream.patch",
-        "0026-constexpr-endian-byte-swap.patch",
-        "0027-Copy-type-traits-from-STLExtras.h-into-PointerUnion..patch",
-        "0028-Remove-StringMap-test-for-llvm-sort.patch",
+        "0001-Remove-StringRef-ArrayRef-and-Optional.patch",
+        "0002-Wrap-std-min-max-calls-in-parens-for-Windows-warning.patch",
+        "0003-Change-unique_function-storage-size.patch",
+        "0004-Threading-updates.patch",
+        "0005-ifdef-guard-safety.patch",
+        "0006-Explicitly-use-std.patch",
+        "0007-Remove-format_provider.patch",
+        "0008-Add-compiler-warning-pragmas.patch",
+        "0009-Remove-unused-functions.patch",
+        "0010-Detemplatize-SmallVectorBase.patch",
+        "0011-Add-vectors-to-raw_ostream.patch",
+        "0012-Extra-collections-features.patch",
+        "0013-EpochTracker-ABI-macro.patch",
+        "0014-Delete-numbers-from-MathExtras.patch",
+        "0015-Add-lerp-and-sgn.patch",
+        "0016-Fixup-includes.patch",
+        "0017-Use-std-is_trivially_copy_constructible.patch",
+        "0018-Windows-support.patch",
+        "0019-Prefer-fmtlib.patch",
+        "0020-Prefer-wpi-s-fs.h.patch",
+        "0021-Remove-unused-functions.patch",
+        "0022-OS-specific-changes.patch",
+        "0023-Use-SmallVector-for-UTF-conversion.patch",
+        "0024-Prefer-to-use-static-pointers-in-raw_ostream.patch",
+        "0025-constexpr-endian-byte-swap.patch",
+        "0026-Copy-type-traits-from-STLExtras.h-into-PointerUnion..patch",
+        "0027-Remove-StringMap-test-for-llvm-sort.patch",
+        "0028-Unused-variable-in-release-mode.patch",
+        "0029-Use-C-20-bit-header.patch",
+        "0030-Remove-DenseMap-GTest-printer-test.patch",
+        "0031-Replace-deprecated-std-aligned_storage_t.patch",
+        "0032-Fix-compilation-of-MathExtras.h-on-Windows-with-sdl.patch",
+        "0033-raw_ostream-Add-SetNumBytesInBuffer.patch",
     ]:
         git_am(
             os.path.join(wpilib_root, "upstream_utils/llvm_patches", f),
diff --git a/third_party/allwpilib/upstream_utils/update_memory.py b/third_party/allwpilib/upstream_utils/update_memory.py
index e72717a..6645aaf 100755
--- a/third_party/allwpilib/upstream_utils/update_memory.py
+++ b/third_party/allwpilib/upstream_utils/update_memory.py
@@ -59,7 +59,7 @@
 
 
 def main():
-    upstream_root = clone_repo("https://github.com/foonathan/memory", "v0.7-2")
+    upstream_root = clone_repo("https://github.com/foonathan/memory", "v0.7-3")
     wpilib_root = get_repo_root()
     wpiutil = os.path.join(wpilib_root, "wpiutil")
 
diff --git a/third_party/allwpilib/upstream_utils/update_mpack.py b/third_party/allwpilib/upstream_utils/update_mpack.py
index 25eabfc..68315e9 100755
--- a/third_party/allwpilib/upstream_utils/update_mpack.py
+++ b/third_party/allwpilib/upstream_utils/update_mpack.py
@@ -13,7 +13,7 @@
 
 
 def main():
-    upstream_root = clone_repo("https://github.com/ludocode/mpack", "v1.1")
+    upstream_root = clone_repo("https://github.com/ludocode/mpack", "v1.1.1")
     wpilib_root = get_repo_root()
     wpiutil = os.path.join(wpilib_root, "wpiutil")
 
@@ -31,6 +31,7 @@
         "0001-Don-t-emit-inline-defs.patch",
         "0002-Update-amalgamation-script.patch",
         "0003-Use-namespace-for-C.patch",
+        "0004-Group-doxygen-into-MPack-module.patch",
     ]:
         git_am(
             os.path.join(wpilib_root, "upstream_utils/mpack_patches", f),
diff --git a/third_party/allwpilib/upstream_utils/update_protobuf.py b/third_party/allwpilib/upstream_utils/update_protobuf.py
new file mode 100755
index 0000000..fbecb9c
--- /dev/null
+++ b/third_party/allwpilib/upstream_utils/update_protobuf.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python3
+
+import os
+import shutil
+
+from upstream_utils import (
+    get_repo_root,
+    clone_repo,
+    comment_out_invalid_includes,
+    copy_to,
+    walk_cwd_and_copy_if,
+    walk_if,
+    git_am,
+)
+
+protobuf_lite_sources = set(
+    [
+        "google/protobuf/any_lite.cc",
+        "google/protobuf/arena.cc",
+        "google/protobuf/arenastring.cc",
+        "google/protobuf/arenaz_sampler.cc",
+        "google/protobuf/extension_set.cc",
+        "google/protobuf/generated_enum_util.cc",
+        "google/protobuf/generated_message_tctable_lite.cc",
+        "google/protobuf/generated_message_util.cc",
+        "google/protobuf/implicit_weak_message.cc",
+        "google/protobuf/inlined_string_field.cc",
+        "google/protobuf/io/coded_stream.cc",
+        "google/protobuf/io/io_win32.cc",
+        "google/protobuf/io/strtod.cc",
+        "google/protobuf/io/zero_copy_stream.cc",
+        "google/protobuf/io/zero_copy_stream_impl.cc",
+        "google/protobuf/io/zero_copy_stream_impl_lite.cc",
+        "google/protobuf/map.cc",
+        "google/protobuf/message_lite.cc",
+        "google/protobuf/parse_context.cc",
+        "google/protobuf/repeated_field.cc",
+        "google/protobuf/repeated_ptr_field.cc",
+        "google/protobuf/stubs/bytestream.cc",
+        "google/protobuf/stubs/common.cc",
+        "google/protobuf/stubs/int128.cc",
+        "google/protobuf/stubs/status.cc",
+        "google/protobuf/stubs/statusor.cc",
+        "google/protobuf/stubs/stringpiece.cc",
+        "google/protobuf/stubs/stringprintf.cc",
+        "google/protobuf/stubs/structurally_valid.cc",
+        "google/protobuf/stubs/strutil.cc",
+        "google/protobuf/stubs/time.cc",
+        "google/protobuf/wire_format_lite.cc",
+    ]
+)
+
+protobuf_lite_includes = set(
+    [
+        "google/protobuf/any.h",
+        "google/protobuf/arena.h",
+        "google/protobuf/arena_impl.h",
+        "google/protobuf/arenastring.h",
+        "google/protobuf/arenaz_sampler.h",
+        "google/protobuf/endian.h",
+        "google/protobuf/explicitly_constructed.h",
+        "google/protobuf/extension_set.h",
+        "google/protobuf/extension_set_inl.h",
+        "google/protobuf/generated_enum_util.h",
+        "google/protobuf/generated_message_tctable_decl.h",
+        "google/protobuf/generated_message_tctable_impl.h",
+        "google/protobuf/generated_message_util.h",
+        "google/protobuf/has_bits.h",
+        "google/protobuf/implicit_weak_message.h",
+        "google/protobuf/inlined_string_field.h",
+        "google/protobuf/io/coded_stream.h",
+        "google/protobuf/io/io_win32.h",
+        "google/protobuf/io/strtod.h",
+        "google/protobuf/io/zero_copy_stream.h",
+        "google/protobuf/io/zero_copy_stream_impl.h",
+        "google/protobuf/io/zero_copy_stream_impl_lite.h",
+        "google/protobuf/map.h",
+        "google/protobuf/map_entry_lite.h",
+        "google/protobuf/map_field_lite.h",
+        "google/protobuf/map_type_handler.h",
+        "google/protobuf/message_lite.h",
+        "google/protobuf/metadata_lite.h",
+        "google/protobuf/parse_context.h",
+        "google/protobuf/port.h",
+        "google/protobuf/repeated_field.h",
+        "google/protobuf/repeated_ptr_field.h",
+        "google/protobuf/stubs/bytestream.h",
+        "google/protobuf/stubs/callback.h",
+        "google/protobuf/stubs/casts.h",
+        "google/protobuf/stubs/common.h",
+        "google/protobuf/stubs/hash.h",
+        "google/protobuf/stubs/logging.h",
+        "google/protobuf/stubs/macros.h",
+        "google/protobuf/stubs/map_util.h",
+        "google/protobuf/stubs/mutex.h",
+        "google/protobuf/stubs/once.h",
+        "google/protobuf/stubs/platform_macros.h",
+        "google/protobuf/stubs/port.h",
+        "google/protobuf/stubs/status.h",
+        "google/protobuf/stubs/stl_util.h",
+        "google/protobuf/stubs/stringpiece.h",
+        "google/protobuf/stubs/strutil.h",
+        "google/protobuf/stubs/template_util.h",
+        "google/protobuf/wire_format_lite.h",
+    ]
+)
+
+protobuf_sources = set(
+    [
+        "google/protobuf/any.cc",
+        "google/protobuf/any.pb.cc",
+        "google/protobuf/api.pb.cc",
+        "google/protobuf/compiler/importer.cc",
+        "google/protobuf/compiler/parser.cc",
+        "google/protobuf/descriptor.cc",
+        "google/protobuf/descriptor.pb.cc",
+        "google/protobuf/descriptor_database.cc",
+        "google/protobuf/duration.pb.cc",
+        "google/protobuf/dynamic_message.cc",
+        "google/protobuf/empty.pb.cc",
+        "google/protobuf/extension_set_heavy.cc",
+        "google/protobuf/field_mask.pb.cc",
+        "google/protobuf/generated_message_bases.cc",
+        "google/protobuf/generated_message_reflection.cc",
+        "google/protobuf/generated_message_tctable_full.cc",
+        "google/protobuf/io/gzip_stream.cc",
+        "google/protobuf/io/printer.cc",
+        "google/protobuf/io/tokenizer.cc",
+        "google/protobuf/map_field.cc",
+        "google/protobuf/message.cc",
+        "google/protobuf/reflection_ops.cc",
+        "google/protobuf/service.cc",
+        "google/protobuf/source_context.pb.cc",
+        "google/protobuf/struct.pb.cc",
+        "google/protobuf/stubs/substitute.cc",
+        "google/protobuf/text_format.cc",
+        "google/protobuf/timestamp.pb.cc",
+        "google/protobuf/type.pb.cc",
+        "google/protobuf/unknown_field_set.cc",
+        "google/protobuf/util/delimited_message_util.cc",
+        "google/protobuf/util/field_comparator.cc",
+        "google/protobuf/util/field_mask_util.cc",
+        "google/protobuf/util/internal/datapiece.cc",
+        "google/protobuf/util/internal/default_value_objectwriter.cc",
+        "google/protobuf/util/internal/error_listener.cc",
+        "google/protobuf/util/internal/field_mask_utility.cc",
+        "google/protobuf/util/internal/json_escaping.cc",
+        "google/protobuf/util/internal/json_objectwriter.cc",
+        "google/protobuf/util/internal/json_stream_parser.cc",
+        "google/protobuf/util/internal/object_writer.cc",
+        "google/protobuf/util/internal/proto_writer.cc",
+        "google/protobuf/util/internal/protostream_objectsource.cc",
+        "google/protobuf/util/internal/protostream_objectwriter.cc",
+        "google/protobuf/util/internal/type_info.cc",
+        "google/protobuf/util/internal/utility.cc",
+        "google/protobuf/util/json_util.cc",
+        "google/protobuf/util/message_differencer.cc",
+        "google/protobuf/util/time_util.cc",
+        "google/protobuf/util/type_resolver_util.cc",
+        "google/protobuf/wire_format.cc",
+        "google/protobuf/wrappers.pb.cc",
+    ]
+)
+
+protobuf_includes = set(
+    [
+        "google/protobuf/any.pb.h",
+        "google/protobuf/api.pb.h",
+        "google/protobuf/compiler/importer.h",
+        "google/protobuf/compiler/parser.h",
+        "google/protobuf/descriptor.h",
+        "google/protobuf/descriptor.pb.h",
+        "google/protobuf/descriptor_database.h",
+        "google/protobuf/duration.pb.h",
+        "google/protobuf/dynamic_message.h",
+        "google/protobuf/empty.pb.h",
+        "google/protobuf/field_access_listener.h",
+        "google/protobuf/field_mask.pb.h",
+        "google/protobuf/generated_enum_reflection.h",
+        "google/protobuf/generated_message_bases.h",
+        "google/protobuf/generated_message_reflection.h",
+        "google/protobuf/io/gzip_stream.h",
+        "google/protobuf/io/printer.h",
+        "google/protobuf/io/tokenizer.h",
+        "google/protobuf/map_entry.h",
+        "google/protobuf/map_field.h",
+        "google/protobuf/map_field_inl.h",
+        "google/protobuf/message.h",
+        "google/protobuf/metadata.h",
+        "google/protobuf/reflection.h",
+        "google/protobuf/reflection_internal.h",
+        "google/protobuf/reflection_ops.h",
+        "google/protobuf/service.h",
+        "google/protobuf/source_context.pb.h",
+        "google/protobuf/struct.pb.h",
+        "google/protobuf/text_format.h",
+        "google/protobuf/timestamp.pb.h",
+        "google/protobuf/type.pb.h",
+        "google/protobuf/unknown_field_set.h",
+        "google/protobuf/util/delimited_message_util.h",
+        "google/protobuf/util/field_comparator.h",
+        "google/protobuf/util/field_mask_util.h",
+        "google/protobuf/util/json_util.h",
+        "google/protobuf/util/message_differencer.h",
+        "google/protobuf/util/time_util.h",
+        "google/protobuf/util/type_resolver.h",
+        "google/protobuf/util/type_resolver_util.h",
+        "google/protobuf/wire_format.h",
+        "google/protobuf/wrappers.pb.h",
+    ]
+)
+
+protobuf_internal_includes = set(
+    [
+        "google/protobuf/port_def.inc",
+        "google/protobuf/port_undef.inc",
+        "google/protobuf/stubs/int128.h",
+        "google/protobuf/stubs/mathutil.h",
+        "google/protobuf/stubs/statusor.h",
+        "google/protobuf/stubs/status_macros.h",
+        "google/protobuf/stubs/stringprintf.h",
+        "google/protobuf/stubs/substitute.h",
+        "google/protobuf/stubs/time.h",
+        "google/protobuf/util/internal/constants.h",
+        "google/protobuf/util/internal/datapiece.h",
+        "google/protobuf/util/internal/default_value_objectwriter.h",
+        "google/protobuf/util/internal/error_listener.h",
+        "google/protobuf/util/internal/field_mask_utility.h",
+        "google/protobuf/util/internal/json_escaping.h",
+        "google/protobuf/util/internal/json_objectwriter.h",
+        "google/protobuf/util/internal/json_stream_parser.h",
+        "google/protobuf/util/internal/location_tracker.h",
+        "google/protobuf/util/internal/object_location_tracker.h",
+        "google/protobuf/util/internal/object_source.h",
+        "google/protobuf/util/internal/object_writer.h",
+        "google/protobuf/util/internal/proto_writer.h",
+        "google/protobuf/util/internal/protostream_objectsource.h",
+        "google/protobuf/util/internal/protostream_objectwriter.h",
+        "google/protobuf/util/internal/structured_objectwriter.h",
+        "google/protobuf/util/internal/type_info.h",
+        "google/protobuf/util/internal/utility.h",
+    ]
+)
+
+use_src_files = protobuf_lite_sources | protobuf_sources
+use_include_files = (
+    protobuf_lite_includes | protobuf_includes | protobuf_internal_includes
+)
+
+
+def matches(dp, f, files):
+    if not dp.startswith("./src/"):
+        return False
+    p = dp[6:] + "/" + f
+    return p in files
+
+
+def main():
+    upstream_root = clone_repo(
+        "https://github.com/protocolbuffers/protobuf", "v3.21.12"
+    )
+    wpilib_root = get_repo_root()
+    wpiutil = os.path.join(wpilib_root, "wpiutil")
+
+    # Apply patches to upstream Git repo
+    os.chdir(upstream_root)
+    for f in [
+        "0001-Fix-sign-compare-warnings.patch",
+        "0002-Remove-redundant-move.patch",
+        "0003-Fix-maybe-uninitialized-warnings.patch",
+        "0004-Fix-coded_stream-WriteRaw.patch",
+        "0005-Suppress-enum-enum-conversion-warning.patch",
+        "0006-Fix-noreturn-function-returning.patch",
+        "0007-Work-around-GCC-12-restrict-warning-compiler-bug.patch",
+        "0008-Disable-MSVC-switch-warning.patch",
+        "0009-Disable-unused-function-warning.patch",
+        "0010-Disable-pedantic-warning.patch",
+        "0011-Avoid-use-of-sprintf.patch",
+    ]:
+        git_am(os.path.join(wpilib_root, "upstream_utils/protobuf_patches", f))
+
+    # Delete old install
+    for d in [
+        "src/main/native/thirdparty/protobuf/src",
+        "src/main/native/thirdparty/protobuf/include",
+    ]:
+        shutil.rmtree(os.path.join(wpiutil, d), ignore_errors=True)
+
+    # Copy protobuf source files into allwpilib
+    src_files = walk_if(".", lambda dp, f: matches(dp, f, use_src_files))
+    src_files = [f[22:] for f in src_files]
+    os.chdir(os.path.join(upstream_root, "src/google/protobuf"))
+    copy_to(src_files, os.path.join(wpiutil, "src/main/native/thirdparty/protobuf/src"))
+
+    # Copy protobuf header files into allwpilib
+    os.chdir(upstream_root)
+    include_files = walk_if(".", lambda dp, f: matches(dp, f, use_include_files))
+    include_files = [f[6:] for f in include_files]
+    os.chdir(os.path.join(upstream_root, "src"))
+    copy_to(
+        include_files,
+        os.path.join(wpiutil, "src/main/native/thirdparty/protobuf/include"),
+    )
+
+
+if __name__ == "__main__":
+    main()
diff --git a/third_party/allwpilib/upstream_utils/update_stack_walker.py b/third_party/allwpilib/upstream_utils/update_stack_walker.py
index 95173d2..a58886e 100755
--- a/third_party/allwpilib/upstream_utils/update_stack_walker.py
+++ b/third_party/allwpilib/upstream_utils/update_stack_walker.py
@@ -38,7 +38,7 @@
 def main():
     upstream_root = clone_repo(
         "https://github.com/JochenKalmbach/StackWalker",
-        "42e7a6e056a9e7aca911a7e9e54e2e4f90bc2652",
+        "5b0df7a4db8896f6b6dc45d36e383c52577e3c6b",
         shallow=False,
     )
     wpilib_root = get_repo_root()
@@ -50,9 +50,7 @@
     # Apply patches to upstream Git repo
     os.chdir(upstream_root)
     for f in [
-        "0001-Apply-PR-35.patch",
-        "0002-Remove-_M_IX86-checks.patch",
-        "0003-Add-advapi-pragma.patch",
+        "0001-Add-advapi-pragma.patch",
     ]:
         git_am(
             os.path.join(wpilib_root, "upstream_utils/stack_walker_patches", f),
diff --git a/third_party/allwpilib/upstream_utils/upstream_utils.py b/third_party/allwpilib/upstream_utils/upstream_utils.py
index 6454d54..ccf668b 100644
--- a/third_party/allwpilib/upstream_utils/upstream_utils.py
+++ b/third_party/allwpilib/upstream_utils/upstream_utils.py
@@ -18,7 +18,10 @@
     root -- root directory of the cloned Git repository
     """
     cwd = os.getcwd()
-    os.chdir(tempfile.gettempdir())
+    if url.startswith("file://"):
+        os.chdir(os.path.dirname(url[7:]))
+    else:
+        os.chdir(tempfile.gettempdir())
 
     repo = os.path.basename(url)
     dest = os.path.join(os.getcwd(), repo)
diff --git a/third_party/allwpilib/vcpkg.json b/third_party/allwpilib/vcpkg.json
index e326359..ea60ac2 100644
--- a/third_party/allwpilib/vcpkg.json
+++ b/third_party/allwpilib/vcpkg.json
@@ -2,6 +2,11 @@
   "name": "main",
   "version-string": "latest",
   "dependencies": [
-    "opencv"
-  ]
+    "opencv",
+    "eigen3",
+    "fmt",
+    "libuv",
+    "protobuf"
+  ],
+  "builtin-baseline": "78b61582c9e093fda56a01ebb654be15a0033897"
 }
diff --git a/third_party/allwpilib/wpigui/CMakeLists.txt b/third_party/allwpilib/wpigui/CMakeLists.txt
index 59c4d6f..68acbea 100644
--- a/third_party/allwpilib/wpigui/CMakeLists.txt
+++ b/third_party/allwpilib/wpigui/CMakeLists.txt
@@ -8,7 +8,11 @@
 file(GLOB wpigui_mac_src src/main/native/metal/*.mm)
 file(GLOB wpigui_unix_src src/main/native/opengl3/*.cpp)
 
-add_library(wpigui STATIC ${wpigui_src})
+if (MSVC)
+  add_library(wpigui STATIC ${wpigui_src})
+else()
+  add_library(wpigui ${wpigui_src})
+endif()
 set_target_properties(wpigui PROPERTIES DEBUG_POSTFIX "d")
 set_property(TARGET wpigui PROPERTY POSITION_INDEPENDENT_CODE ON)
 
@@ -35,9 +39,10 @@
 
 add_executable(wpiguidev src/dev/native/cpp/main.cpp)
 wpilib_link_macos_gui(wpiguidev)
+wpilib_target_warnings(wpiguidev)
 target_link_libraries(wpiguidev wpigui)
 
-install(TARGETS wpigui EXPORT wpigui DESTINATION "${main_lib_dest}")
+install(TARGETS wpigui EXPORT wpigui)
 install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/wpigui")
 
 #if (WITH_FLAT_INSTALL)
diff --git a/third_party/allwpilib/wpigui/build.gradle b/third_party/allwpilib/wpigui/build.gradle
index fb08198..3e70c4b 100644
--- a/third_party/allwpilib/wpigui/build.gradle
+++ b/third_party/allwpilib/wpigui/build.gradle
@@ -1,146 +1,143 @@
 import org.gradle.internal.os.OperatingSystem
 
-if (!project.hasProperty('onlylinuxathena')) {
+if (project.hasProperty('onlylinuxathena')) {
+    return;
+}
 
-    apply plugin: 'cpp'
-    if (OperatingSystem.current().isMacOsX()) {
-        apply plugin: 'objective-cpp'
+apply plugin: 'cpp'
+if (OperatingSystem.current().isMacOsX()) {
+    apply plugin: 'objective-cpp'
+}
+apply plugin: 'visual-studio'
+apply plugin: 'edu.wpi.first.NativeUtils'
+
+ext {
+    nativeName = 'wpigui'
+}
+
+apply from: "${rootDir}/shared/config.gradle"
+apply from: "${rootDir}/shared/imgui.gradle"
+
+nativeUtils.exportsConfigs {
+    wpigui {
+        x64ExcludeSymbols = [
+            '_CT??_R0?AV_System_error',
+            '_CT??_R0?AVexception',
+            '_CT??_R0?AVfailure',
+            '_CT??_R0?AVruntime_error',
+            '_CT??_R0?AVsystem_error',
+            '_CTA5?AVfailure',
+            '_TI5?AVfailure',
+            '_CT??_R0?AVout_of_range',
+            '_CTA3?AVout_of_range',
+            '_TI3?AVout_of_range',
+            '_CT??_R0?AVbad_cast'
+        ]
     }
-    apply plugin: 'visual-studio'
-    apply plugin: 'edu.wpi.first.NativeUtils'
+}
 
-    ext {
-        nativeName = 'wpigui'
-    }
-
-    apply from: "${rootDir}/shared/config.gradle"
-    apply from: "${rootDir}/shared/imgui.gradle"
-
-    nativeUtils.exportsConfigs {
-        wpigui {
-            x64ExcludeSymbols = [
-                '_CT??_R0?AV_System_error',
-                '_CT??_R0?AVexception',
-                '_CT??_R0?AVfailure',
-                '_CT??_R0?AVruntime_error',
-                '_CT??_R0?AVsystem_error',
-                '_CTA5?AVfailure',
-                '_TI5?AVfailure',
-                '_CT??_R0?AVout_of_range',
-                '_CTA3?AVout_of_range',
-                '_TI3?AVout_of_range',
-                '_CT??_R0?AVbad_cast'
-            ]
-        }
-    }
-
-    model {
-        components {
-            "${nativeName}"(NativeLibrarySpec) {
-                sources {
-                    cpp {
-                        source {
-                            srcDirs "src/main/native/cpp"
-                            include '*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/main/native/include'
-                        }
-                    }
+model {
+    components {
+        "${nativeName}"(NativeLibrarySpec) {
+            sources.cpp {
+                source {
+                    srcDirs "src/main/native/cpp"
+                    include '*.cpp'
                 }
-                binaries.all {
-                    nativeUtils.useRequiredLibrary(it, 'imgui')
-                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
-                        it.buildable = false
-                        return
-                    }
-                    if (it.targetPlatform.operatingSystem.isWindows()) {
-                        it.sources {
-                            wpiguiWindowsCpp(CppSourceSet) {
-                                source {
-                                    srcDirs 'src/main/native/directx11'
-                                    include '*.cpp'
-                                }
-                            }
-                        }
-                    } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
-                        it.sources {
-                            wpiguiMacObjectiveCpp(ObjectiveCppSourceSet) {
-                                source {
-                                    srcDirs 'src/main/native/metal'
-                                    include '*.mm'
-                                }
-                            }
-                        }
-                    } else if (it.targetPlatform.name.startsWith('linuxarm')) {
-                        it.sources {
-                            wpiguiUnixGl2Cpp(CppSourceSet) {
-                                source {
-                                    srcDirs 'src/main/native/opengl2'
-                                    include '*.cpp'
-                                }
-                            }
-                        }
-                    } else {
-                        it.sources {
-                            wpiguiUnixGl3Cpp(CppSourceSet) {
-                                source {
-                                    srcDirs 'src/main/native/opengl3'
-                                    include '*.cpp'
-                                }
-                            }
-                        }
-                    }
-                    it.sources.each {
-                        it.exportedHeaders {
-                            srcDirs 'src/main/native/include'
-                        }
-                    }
+                exportedHeaders {
+                    srcDirs 'src/main/native/include'
                 }
             }
-            // By default, a development executable will be generated. This is to help the case of
-            // testing specific functionality of the library.
-            "${nativeName}Dev"(NativeExecutableSpec) {
-                targetBuildTypes 'debug'
-                sources {
-                    cpp {
-                        source {
-                            srcDirs 'src/dev/native/cpp'
-                            include '**/*.cpp'
-                        }
-                        exportedHeaders {
-                            srcDirs 'src/dev/native/include'
-                        }
-                    }
-                }
-                binaries.all {
-                    lib library: 'wpigui'
-                    nativeUtils.useRequiredLibrary(it, 'imgui')
-                }
-            }
-        }
-        binaries {
-            all {
+            binaries.all {
+                nativeUtils.useRequiredLibrary(it, 'imgui')
                 if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
                     it.buildable = false
                     return
                 }
                 if (it.targetPlatform.operatingSystem.isWindows()) {
-                    it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
+                    it.sources {
+                        wpiguiWindowsCpp(CppSourceSet) {
+                            source {
+                                srcDirs 'src/main/native/directx11'
+                                include '*.cpp'
+                            }
+                        }
+                    }
                 } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
-                    it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
+                    it.sources {
+                        wpiguiMacObjectiveCpp(ObjectiveCppSourceSet) {
+                            source {
+                                srcDirs 'src/main/native/metal'
+                                include '*.mm'
+                            }
+                        }
+                    }
+                } else if (it.targetPlatform.name.startsWith('linuxarm')) {
+                    it.sources {
+                        wpiguiUnixGl2Cpp(CppSourceSet) {
+                            source {
+                                srcDirs 'src/main/native/opengl2'
+                                include '*.cpp'
+                            }
+                        }
+                    }
                 } else {
-                    it.linker.args << '-lX11'
-                    if (it.targetPlatform.name.startsWith('linuxarm')) {
-                        it.linker.args << '-lGL'
+                    it.sources {
+                        wpiguiUnixGl3Cpp(CppSourceSet) {
+                            source {
+                                srcDirs 'src/main/native/opengl3'
+                                include '*.cpp'
+                            }
+                        }
+                    }
+                }
+                it.sources.each {
+                    it.exportedHeaders {
+                        srcDirs 'src/main/native/include'
                     }
                 }
             }
-            withType(SharedLibraryBinarySpec) {
-                buildable = false
+        }
+        // By default, a development executable will be generated. This is to help the case of
+        // testing specific functionality of the library.
+        "${nativeName}Dev"(NativeExecutableSpec) {
+            targetBuildTypes 'debug'
+            sources.cpp {
+                source {
+                    srcDirs 'src/dev/native/cpp'
+                    include '**/*.cpp'
+                }
+                exportedHeaders {
+                    srcDirs 'src/dev/native/include'
+                }
+            }
+            binaries.all {
+                lib library: 'wpigui'
+                nativeUtils.useRequiredLibrary(it, 'imgui')
             }
         }
     }
-
-    apply from: 'publish.gradle'
+    binaries {
+        all {
+            if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                it.buildable = false
+                return
+            }
+            if (it.targetPlatform.operatingSystem.isWindows()) {
+                it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
+            } else if (it.targetPlatform.operatingSystem.isMacOsX()) {
+                it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
+            } else {
+                it.linker.args << '-lX11'
+                if (it.targetPlatform.name.startsWith('linuxarm')) {
+                    it.linker.args << '-lGL'
+                }
+            }
+        }
+        withType(SharedLibraryBinarySpec) {
+            buildable = false
+        }
+    }
 }
+
+apply from: 'publish.gradle'
diff --git a/third_party/allwpilib/wpigui/publish.gradle b/third_party/allwpilib/wpigui/publish.gradle
index 3114118..c137d7c 100644
--- a/third_party/allwpilib/wpigui/publish.gradle
+++ b/third_party/allwpilib/wpigui/publish.gradle
@@ -9,7 +9,7 @@
 task cppSourcesZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = zipBaseName
-    classifier = "sources"
+    archiveClassifier = "sources"
 
     from(licenseFile) {
         into '/'
@@ -32,7 +32,7 @@
 task cppHeadersZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = zipBaseName
-    classifier = "headers"
+    archiveClassifier = "headers"
 
     from(licenseFile) {
         into '/'
diff --git a/third_party/allwpilib/wpigui/src/main/native/cpp/portable-file-dialogs.cpp b/third_party/allwpilib/wpigui/src/main/native/cpp/portable-file-dialogs.cpp
index 2e2ac7b..c4afb0b 100644
--- a/third_party/allwpilib/wpigui/src/main/native/cpp/portable-file-dialogs.cpp
+++ b/third_party/allwpilib/wpigui/src/main/native/cpp/portable-file-dialogs.cpp
@@ -702,6 +702,12 @@
 
 // dialog implementation
 
+internal::dialog::~dialog() {
+    if (m_async->m_running) {
+        kill();
+    }
+}
+
 bool internal::dialog::ready(int timeout /* = default_wait_timeout */) const
 {
     return m_async->ready(timeout);
@@ -804,7 +810,7 @@
 #if _WIN32
     static int CALLBACK bffcallback(HWND hwnd, UINT uMsg, LPARAM, LPARAM pData);
 #if PFD_HAS_IFILEDIALOG
-    std::string select_folder_vista(IFileDialog *ifd, bool force_path);
+    std::string select_folder_vista(IFileDialog *ifd, bool force_path, HWND active_window);
 #endif
 
     std::wstring m_wtitle;
@@ -831,8 +837,9 @@
     }
     filter_list += '\0';
 
+    HWND active_window = GetActiveWindow();
     m_async->start_func([this, in_type, title, default_path, filter_list,
-                         options](int *exit_code) -> std::string
+                         options, active_window](int *exit_code) -> std::string
     {
         (void)exit_code;
         m_impl->m_wtitle = internal::str2wstr(title);
@@ -858,7 +865,7 @@
 
                 // In case CoCreateInstance fails (which it should not), try legacy approach
                 if (SUCCEEDED(hr))
-                    return m_impl->select_folder_vista(ifd, options & opt::force_path);
+                    return m_impl->select_folder_vista(ifd, options & opt::force_path, active_window);
             }
 #endif
 
@@ -892,7 +899,7 @@
         OPENFILENAMEW ofn;
         memset(&ofn, 0, sizeof(ofn));
         ofn.lStructSize = sizeof(OPENFILENAMEW);
-        ofn.hwndOwner = GetActiveWindow();
+        ofn.hwndOwner = active_window;
 
         ofn.lpstrFilter = wfilter_list.c_str();
 
@@ -1169,7 +1176,7 @@
 }
 
 #if PFD_HAS_IFILEDIALOG
-std::string internal::file_dialog::Impl::select_folder_vista(IFileDialog *ifd, bool force_path)
+std::string internal::file_dialog::Impl::select_folder_vista(IFileDialog *ifd, bool force_path, HWND active_window)
 {
     std::string result;
 
@@ -1206,7 +1213,7 @@
     ifd->SetOptions(FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM);
     ifd->SetTitle(m_wtitle.c_str());
 
-    hr = ifd->Show(GetActiveWindow());
+    hr = ifd->Show(active_window);
     if (SUCCEEDED(hr))
     {
         IShellItem* item;
@@ -1393,13 +1400,15 @@
     m_mappings[IDRETRY] = button::retry;
     m_mappings[IDIGNORE] = button::ignore;
 
-    m_async->start_func([text, title, style](int* exit_code) -> std::string
+    HWND active_window = GetActiveWindow();
+
+    m_async->start_func([text, title, style, active_window](int* exit_code) -> std::string
     {
         auto wtext = internal::str2wstr(text);
         auto wtitle = internal::str2wstr(title);
         // Apply new visual style (required for all Windows versions)
         internal::platform::new_style_context ctx;
-        *exit_code = MessageBoxW(GetActiveWindow(), wtext.c_str(), wtitle.c_str(), style);
+        *exit_code = MessageBoxW(active_window, wtext.c_str(), wtitle.c_str(), style);
         return "";
     });
 
diff --git a/third_party/allwpilib/wpigui/src/main/native/cpp/wpigui.cpp b/third_party/allwpilib/wpigui/src/main/native/cpp/wpigui.cpp
index a93ca2b..e2e05ce 100644
--- a/third_party/allwpilib/wpigui/src/main/native/cpp/wpigui.cpp
+++ b/third_party/allwpilib/wpigui/src/main/native/cpp/wpigui.cpp
@@ -4,9 +4,13 @@
 
 #include "wpigui.h"
 
+#include <stdint.h>
+
 #include <algorithm>
+#include <chrono>
 #include <cstdio>
 #include <cstring>
+#include <thread>
 
 #include <GLFW/glfw3.h>
 #include <IconsFontAwesome6.h>
@@ -88,6 +92,8 @@
     impl->userScale = num;
   } else if (std::strncmp(lineStr, "style=", 6) == 0) {
     impl->style = num;
+  } else if (std::strncmp(lineStr, "fps=", 4) == 0) {
+    impl->fps = num;
   }
 }
 
@@ -98,9 +104,10 @@
   }
   out_buf->appendf(
       "[MainWindow][GLOBAL]\nwidth=%d\nheight=%d\nmaximized=%d\n"
-      "xpos=%d\nypos=%d\nuserScale=%d\nstyle=%d\n\n",
+      "xpos=%d\nypos=%d\nuserScale=%d\nstyle=%d\nfps=%d\n\n",
       gContext->width, gContext->height, gContext->maximized ? 1 : 0,
-      gContext->xPos, gContext->yPos, gContext->userScale, gContext->style);
+      gContext->xPos, gContext->yPos, gContext->userScale, gContext->style,
+      gContext->fps);
 }
 
 void gui::CreateContext() {
@@ -220,8 +227,6 @@
   }
 
   // Set initial window settings
-  glfwWindowHint(GLFW_MAXIMIZED, gContext->maximized ? GLFW_TRUE : GLFW_FALSE);
-
   if (gContext->width == 0 || gContext->height == 0) {
     gContext->width = gContext->defaultWidth;
     gContext->height = gContext->defaultHeight;
@@ -296,6 +301,10 @@
     glfwShowWindow(gContext->window);
   }
 
+  if (gContext->maximized) {
+    glfwMaximizeWindow(gContext->window);
+  }
+
   // Set window callbacks
   glfwGetWindowSize(gContext->window, &gContext->width, &gContext->height);
   glfwSetWindowSizeCallback(gContext->window, WindowSizeCallback);
@@ -331,6 +340,8 @@
 void gui::Main() {
   // Main loop
   while (!glfwWindowShouldClose(gContext->window) && !gContext->exit) {
+    double startTime = glfwGetTime();
+
     // Poll and handle events (inputs, window resize, etc.)
     glfwPollEvents();
     gContext->isPlatformRendering = true;
@@ -351,6 +362,15 @@
         io.WantSaveIniSettings = false;  // reset flag
       }
     }
+
+    // if FPS limiting, sleep until next frame time
+    if (gContext->fps != 0) {
+      double sleepTime = (1.0 / gContext->fps) - (glfwGetTime() - startTime);
+      if (sleepTime > 1e-6) {
+        std::this_thread::sleep_for(
+            std::chrono::microseconds(static_cast<int64_t>(sleepTime * 1e6)));
+      }
+    }
   }
 
   // Save (if custom save)
@@ -477,6 +497,10 @@
   }
 }
 
+void gui::SetFPS(int fps) {
+  gContext->fps = fps;
+}
+
 void gui::SetClearColor(ImVec4 color) {
   gContext->clearColor = color;
 }
@@ -539,6 +563,27 @@
       ImGui::EndMenu();
     }
 
+    if (ImGui::BeginMenu("Frame Rate")) {
+      bool selected;
+      selected = gContext->fps == 0;
+      if (ImGui::MenuItem("vsync", nullptr, &selected)) {
+        gContext->fps = 0;
+      }
+      selected = gContext->fps == 30;
+      if (ImGui::MenuItem("30 fps", nullptr, &selected)) {
+        gContext->fps = 30;
+      }
+      selected = gContext->fps == 60;
+      if (ImGui::MenuItem("60 fps", nullptr, &selected)) {
+        gContext->fps = 60;
+      }
+      selected = gContext->fps == 120;
+      if (ImGui::MenuItem("120 fps", nullptr, &selected)) {
+        gContext->fps = 120;
+      }
+      ImGui::EndMenu();
+    }
+
     if (!gContext->saveSettings) {
       ImGui::MenuItem("Reset UI on Exit?", nullptr, &gContext->resetOnExit);
     }
diff --git a/third_party/allwpilib/wpigui/src/main/native/cpp/wpigui_openurl.cpp b/third_party/allwpilib/wpigui/src/main/native/cpp/wpigui_openurl.cpp
index b913ceb..f609567 100644
--- a/third_party/allwpilib/wpigui/src/main/native/cpp/wpigui_openurl.cpp
+++ b/third_party/allwpilib/wpigui/src/main/native/cpp/wpigui_openurl.cpp
@@ -23,6 +23,10 @@
 #else
   static constexpr const char* opencmd = "xdg-open";
 #endif
-  execlp(opencmd, opencmd, url.c_str(), static_cast<const char*>(nullptr));
+  // If we forked into the child process, run execlp(), which replaces the
+  // current process image
+  if (fork() == 0) {
+    execlp(opencmd, opencmd, url.c_str(), static_cast<const char*>(nullptr));
+  }
 #endif
 }
diff --git a/third_party/allwpilib/wpigui/src/main/native/include/portable-file-dialogs.h b/third_party/allwpilib/wpigui/src/main/native/include/portable-file-dialogs.h
index 95d5f43..e4a4f96 100644
--- a/third_party/allwpilib/wpigui/src/main/native/include/portable-file-dialogs.h
+++ b/third_party/allwpilib/wpigui/src/main/native/include/portable-file-dialogs.h
@@ -128,6 +128,7 @@
 class dialog : protected settings
 {
 public:
+    virtual ~dialog();
     bool ready(int timeout = default_wait_timeout) const;
     bool kill() const;
 
@@ -146,7 +147,7 @@
     std::shared_ptr<executor> m_async;
 };
 
-class file_dialog : public dialog
+class file_dialog : public internal::dialog
 {
 protected:
     enum type
diff --git a/third_party/allwpilib/wpigui/src/main/native/include/wpigui.h b/third_party/allwpilib/wpigui/src/main/native/include/wpigui.h
index db74291..a745431 100644
--- a/third_party/allwpilib/wpigui/src/main/native/include/wpigui.h
+++ b/third_party/allwpilib/wpigui/src/main/native/include/wpigui.h
@@ -128,7 +128,7 @@
 
 inline bool AddIcon(std::string_view data) {
   return AddIcon(reinterpret_cast<const unsigned char*>(data.data()),
-                 data.size());
+                 static_cast<int>(data.size()));
 }
 
 /**
@@ -163,6 +163,13 @@
 void SetStyle(Style style);
 
 /**
+ * Sets the FPS limit.  Using this function makes this setting persistent.
+ *
+ * @param fps FPS (0=vsync)
+ */
+void SetFPS(int fps);
+
+/**
  * Sets the clear (background) color.
  *
  * @param color Color
diff --git a/third_party/allwpilib/wpigui/src/main/native/include/wpigui_internal.h b/third_party/allwpilib/wpigui/src/main/native/include/wpigui_internal.h
index f4d352b..e9735f6 100644
--- a/third_party/allwpilib/wpigui/src/main/native/include/wpigui_internal.h
+++ b/third_party/allwpilib/wpigui/src/main/native/include/wpigui_internal.h
@@ -24,6 +24,7 @@
   int yPos = -1;
   int userScale = 2;
   int style = 0;
+  int fps = 120;
 };
 
 constexpr int kFontScaledLevels = 9;
diff --git a/third_party/allwpilib/wpilib-config.cmake.in b/third_party/allwpilib/wpilib-config.cmake.in
index 36a9d25..aaf4bb8 100644
--- a/third_party/allwpilib/wpilib-config.cmake.in
+++ b/third_party/allwpilib/wpilib-config.cmake.in
@@ -11,3 +11,4 @@
 @CAMERASERVER_DEP_REPLACE@
 @HAL_DEP_REPLACE@
 @WPILIBC_DEP_REPLACE@
+@WPILIBNEWCOMMANDS_DEP_REPLACE@
diff --git a/third_party/allwpilib/wpilibNewCommands/.styleguide b/third_party/allwpilib/wpilibNewCommands/.styleguide
index 3f20ec8..7c8d7a7 100644
--- a/third_party/allwpilib/wpilibNewCommands/.styleguide
+++ b/third_party/allwpilib/wpilibNewCommands/.styleguide
@@ -17,6 +17,7 @@
   ^cscore
   ^fmt/
   ^frc/
+  ^gtest/
   ^hal/
   ^imgui
   ^mockdata/
diff --git a/third_party/allwpilib/wpilibNewCommands/CMakeLists.txt b/third_party/allwpilib/wpilibNewCommands/CMakeLists.txt
index 716c508..34a6bb1 100644
--- a/third_party/allwpilib/wpilibNewCommands/CMakeLists.txt
+++ b/third_party/allwpilib/wpilibNewCommands/CMakeLists.txt
@@ -22,8 +22,22 @@
   else()
       set (wpilibNewCommands_config_dir share/wpilibNewCommands)
   endif()
+endif()
 
-  install(FILES wpilibNewCommands-config.cmake DESTINATION ${wpilibNewCommands_config_dir})
+if (WITH_JAVA_SOURCE)
+  find_package(Java REQUIRED)
+  include(UseJava)
+  file(GLOB WPILIBNEWCOMMANDS_SOURCES src/main/java/edu/wpi/first/wpilibj2/command/*.java)
+  file(GLOB WPILIBNEWCOMMANDS_BUTTON_SOURCES src/main/java/edu/wpi/first/wpilibj2/command/button*.java)
+  add_jar(wpilibNewCommands_src_jar
+  RESOURCES NAMESPACE "edu/wpi/first/wpilibj2/command" ${WPILIBNEWCOMMANDS_SOURCES}
+  NAMESPACE "edu/wpi/first/wpilibj2/command/button" ${WPILIBNEWCOMMANDS_BUTTON_SOURCES}
+  OUTPUT_NAME wpilibNewCommands-sources)
+
+  get_property(WPILIBNEWCOMMANDS_SRC_JAR_FILE TARGET wpilibNewCommands_src_jar PROPERTY JAR_FILE)
+  install(FILES ${WPILIBNEWCOMMANDS_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+  set_property(TARGET wpilibNewCommands_src_jar PROPERTY FOLDER "java")
 endif()
 
 file(GLOB_RECURSE wpilibNewCommands_native_src src/main/native/cpp/*.cpp)
@@ -39,10 +53,10 @@
                             $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
                             $<INSTALL_INTERFACE:${include_dest}/wpilibNewCommands>)
 
-install(TARGETS wpilibNewCommands EXPORT wpilibNewCommands DESTINATION "${main_lib_dest}")
+install(TARGETS wpilibNewCommands EXPORT wpilibNewCommands)
 install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/wpilibNewCommands")
 
-if (MSVC OR FLAT_INSTALL_WPILIB)
+if (FLAT_INSTALL_WPILIB)
      set(wpilibNewCommands_config_dir ${wpilib_dest})
  else()
      set(wpilibNewCommands_config_dir share/wpilibNewCommands)
diff --git a/third_party/allwpilib/wpilibNewCommands/WPILibNewCommands.json b/third_party/allwpilib/wpilibNewCommands/WPILibNewCommands.json
index da4bc52..67bf389 100644
--- a/third_party/allwpilib/wpilibNewCommands/WPILibNewCommands.json
+++ b/third_party/allwpilib/wpilibNewCommands/WPILibNewCommands.json
@@ -1,37 +1,38 @@
-{

-  "fileName": "WPILibNewCommands.json",

-  "name": "WPILib-New-Commands",

-  "version": "1.0.0",

-  "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266",

-  "mavenUrls": [],

-  "jsonUrl": "",

-  "javaDependencies": [

-      {

-          "groupId": "edu.wpi.first.wpilibNewCommands",

-          "artifactId": "wpilibNewCommands-java",

-          "version": "wpilib"

-      }

-  ],

-  "jniDependencies": [],

-  "cppDependencies": [

-      {

-          "groupId": "edu.wpi.first.wpilibNewCommands",

-          "artifactId": "wpilibNewCommands-cpp",

-          "version": "wpilib",

-          "libName": "wpilibNewCommands",

-          "headerClassifier": "headers",

-          "sourcesClassifier": "sources",

-          "sharedLibrary": true,

-          "skipInvalidPlatforms": true,

-          "binaryPlatforms": [

-              "linuxathena",

-              "linuxarm32",

-              "linuxarm64",

-              "windowsx86-64",

-              "windowsx86",

-              "linuxx86-64",

-              "osxuniversal"

-          ]

-      }

-  ]

-}

+{
+  "fileName": "WPILibNewCommands.json",
+  "name": "WPILib-New-Commands",
+  "version": "1.0.0",
+  "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266",
+  "frcYear": "2024",
+  "mavenUrls": [],
+  "jsonUrl": "",
+  "javaDependencies": [
+    {
+      "groupId": "edu.wpi.first.wpilibNewCommands",
+      "artifactId": "wpilibNewCommands-java",
+      "version": "wpilib"
+    }
+  ],
+  "jniDependencies": [],
+  "cppDependencies": [
+    {
+      "groupId": "edu.wpi.first.wpilibNewCommands",
+      "artifactId": "wpilibNewCommands-cpp",
+      "version": "wpilib",
+      "libName": "wpilibNewCommands",
+      "headerClassifier": "headers",
+      "sourcesClassifier": "sources",
+      "sharedLibrary": true,
+      "skipInvalidPlatforms": true,
+      "binaryPlatforms": [
+        "linuxathena",
+        "linuxarm32",
+        "linuxarm64",
+        "windowsx86-64",
+        "windowsx86",
+        "linuxx86-64",
+        "osxuniversal"
+      ]
+    }
+  ]
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/build.gradle b/third_party/allwpilib/wpilibNewCommands/build.gradle
index 82fc470..d60fcde 100644
--- a/third_party/allwpilib/wpilibNewCommands/build.gradle
+++ b/third_party/allwpilib/wpilibNewCommands/build.gradle
@@ -1,6 +1,6 @@
 ext {
     nativeName = 'wpilibNewCommands'
-    devMain = 'edu.wpi.first.wpilibj.commands.DevMain'
+    devMain = 'edu.wpi.first.wpilibj2.commands.DevMain'
 }
 
 evaluationDependsOn(':ntcore')
@@ -21,13 +21,6 @@
     implementation project(':hal')
     implementation project(':wpimath')
     implementation project(':wpilibj')
-    devImplementation project(':wpiutil')
-    devImplementation project(':wpinet')
-    devImplementation project(':ntcore')
-    devImplementation project(':cscore')
-    devImplementation project(':hal')
-    devImplementation project(':wpimath')
-    devImplementation project(':wpilibj')
     testImplementation 'org.mockito:mockito-core:4.1.0'
 }
 
@@ -67,6 +60,7 @@
                 project(':ntcore').addNtcoreJniDependency(it)
                 lib project: ':wpinet', library: 'wpinetJNIShared', linkage: 'shared'
                 lib project: ':wpiutil', library: 'wpiutilJNIShared', linkage: 'shared'
+                lib project: ':wpimath', library: 'wpimathJNIShared', linkage: 'shared'
                 project(':hal').addHalJniDependency(it)
             }
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java
index 0383099..a913b55 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java
@@ -7,6 +7,10 @@
 import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
 
 import edu.wpi.first.util.function.BooleanConsumer;
+import edu.wpi.first.util.sendable.Sendable;
+import edu.wpi.first.util.sendable.SendableBuilder;
+import edu.wpi.first.util.sendable.SendableRegistry;
+import java.util.HashSet;
 import java.util.Set;
 import java.util.function.BooleanSupplier;
 
@@ -20,12 +24,19 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public interface Command {
+public abstract class Command implements Sendable {
+  protected Set<Subsystem> m_requirements = new HashSet<>();
+
+  protected Command() {
+    String name = getClass().getName();
+    SendableRegistry.add(this, name.substring(name.lastIndexOf('.') + 1));
+  }
+
   /** The initial subroutine of a command. Called once when the command is initially scheduled. */
-  default void initialize() {}
+  public void initialize() {}
 
   /** The main body of a command. Called repeatedly while the command is scheduled. */
-  default void execute() {}
+  public void execute() {}
 
   /**
    * The action to take when the command ends. Called when either the command finishes normally, or
@@ -36,7 +47,7 @@
    *
    * @param interrupted whether the command was interrupted/canceled
    */
-  default void end(boolean interrupted) {}
+  public void end(boolean interrupted) {}
 
   /**
    * Whether the command has finished. Once a command finishes, the scheduler will call its end()
@@ -44,7 +55,7 @@
    *
    * @return whether the command has finished.
    */
-  default boolean isFinished() {
+  public boolean isFinished() {
     return false;
   }
 
@@ -60,12 +71,67 @@
    * @return the set of subsystems that are required
    * @see InterruptionBehavior
    */
-  Set<Subsystem> getRequirements();
+  public Set<Subsystem> getRequirements() {
+    return m_requirements;
+  }
+
+  /**
+   * Adds the specified subsystems to the requirements of the command. The scheduler will prevent
+   * two commands that require the same subsystem from being scheduled simultaneously.
+   *
+   * <p>Note that the scheduler determines the requirements of a command when it is scheduled, so
+   * this method should normally be called from the command's constructor.
+   *
+   * @param requirements the requirements to add
+   */
+  public final void addRequirements(Subsystem... requirements) {
+    for (Subsystem requirement : requirements) {
+      m_requirements.add(requireNonNullParam(requirement, "requirement", "addRequirements"));
+    }
+  }
+
+  /**
+   * Gets the name of this Command.
+   *
+   * <p>By default, the simple class name is used. This can be changed with {@link
+   * #setName(String)}.
+   *
+   * @return The display name of the Command
+   */
+  public String getName() {
+    return SendableRegistry.getName(this);
+  }
+
+  /**
+   * Sets the name of this Command.
+   *
+   * @param name The display name of the Command.
+   */
+  public void setName(String name) {
+    SendableRegistry.setName(this, name);
+  }
+
+  /**
+   * Gets the subsystem name of this Command.
+   *
+   * @return Subsystem name
+   */
+  public String getSubsystem() {
+    return SendableRegistry.getSubsystem(this);
+  }
+
+  /**
+   * Sets the subsystem name of this Command.
+   *
+   * @param subsystem subsystem name
+   */
+  public void setSubsystem(String subsystem) {
+    SendableRegistry.setSubsystem(this, subsystem);
+  }
 
   /**
    * Decorates this command with a timeout. If the specified timeout is exceeded before the command
-   * finishes normally, the command will be interrupted and un-scheduled. Note that the timeout only
-   * applies to the command returned by this method; the calling command is not itself changed.
+   * finishes normally, the command will be interrupted and un-scheduled.
    *
    * <p>Note: This decorator works by adding this command to a composition. The command the
    * decorator was called on cannot be scheduled independently or be added to a different
@@ -76,15 +142,13 @@
    * @param seconds the timeout duration
    * @return the command with the timeout added
    */
-  default ParallelRaceGroup withTimeout(double seconds) {
+  public ParallelRaceGroup withTimeout(double seconds) {
     return raceWith(new WaitCommand(seconds));
   }
 
   /**
    * Decorates this command with an interrupt condition. If the specified condition becomes true
-   * before the command finishes normally, the command will be interrupted and un-scheduled. Note
-   * that this only applies to the command returned by this method; the calling command is not
-   * itself changed.
+   * before the command finishes normally, the command will be interrupted and un-scheduled.
    *
    * <p>Note: This decorator works by adding this command to a composition. The command the
    * decorator was called on cannot be scheduled independently or be added to a different
@@ -94,16 +158,15 @@
    *
    * @param condition the interrupt condition
    * @return the command with the interrupt condition added
+   * @see #onlyWhile(BooleanSupplier)
    */
-  default ParallelRaceGroup until(BooleanSupplier condition) {
+  public ParallelRaceGroup until(BooleanSupplier condition) {
     return raceWith(new WaitUntilCommand(condition));
   }
 
   /**
-   * Decorates this command with an interrupt condition. If the specified condition becomes true
-   * before the command finishes normally, the command will be interrupted and un-scheduled. Note
-   * that this only applies to the command returned by this method; the calling command is not
-   * itself changed.
+   * Decorates this command with a run condition. If the specified condition becomes false before
+   * the command finishes normally, the command will be interrupted and un-scheduled.
    *
    * <p>Note: This decorator works by adding this command to a composition. The command the
    * decorator was called on cannot be scheduled independently or be added to a different
@@ -111,13 +174,12 @@
    * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
    * returned from this method can be further decorated without issue.
    *
-   * @param condition the interrupt condition
-   * @return the command with the interrupt condition added
-   * @deprecated Replace with {@link #until(BooleanSupplier)}
+   * @param condition the run condition
+   * @return the command with the run condition added
+   * @see #until(BooleanSupplier)
    */
-  @Deprecated(since = "2023")
-  default ParallelRaceGroup withInterrupt(BooleanSupplier condition) {
-    return until(condition);
+  public ParallelRaceGroup onlyWhile(BooleanSupplier condition) {
+    return until(() -> !condition.getAsBoolean());
   }
 
   /**
@@ -133,7 +195,7 @@
    * @param requirements the required subsystems
    * @return the decorated command
    */
-  default SequentialCommandGroup beforeStarting(Runnable toRun, Subsystem... requirements) {
+  public SequentialCommandGroup beforeStarting(Runnable toRun, Subsystem... requirements) {
     return beforeStarting(new InstantCommand(toRun, requirements));
   }
 
@@ -149,7 +211,7 @@
    * @param before the command to run before this one
    * @return the decorated command
    */
-  default SequentialCommandGroup beforeStarting(Command before) {
+  public SequentialCommandGroup beforeStarting(Command before) {
     return new SequentialCommandGroup(before, this);
   }
 
@@ -166,7 +228,7 @@
    * @param requirements the required subsystems
    * @return the decorated command
    */
-  default SequentialCommandGroup andThen(Runnable toRun, Subsystem... requirements) {
+  public SequentialCommandGroup andThen(Runnable toRun, Subsystem... requirements) {
     return andThen(new InstantCommand(toRun, requirements));
   }
 
@@ -183,7 +245,7 @@
    * @param next the commands to run next
    * @return the decorated command
    */
-  default SequentialCommandGroup andThen(Command... next) {
+  public SequentialCommandGroup andThen(Command... next) {
     SequentialCommandGroup group = new SequentialCommandGroup(this);
     group.addCommands(next);
     return group;
@@ -203,7 +265,7 @@
    * @param parallel the commands to run in parallel
    * @return the decorated command
    */
-  default ParallelDeadlineGroup deadlineWith(Command... parallel) {
+  public ParallelDeadlineGroup deadlineWith(Command... parallel) {
     return new ParallelDeadlineGroup(this, parallel);
   }
 
@@ -221,7 +283,7 @@
    * @param parallel the commands to run in parallel
    * @return the decorated command
    */
-  default ParallelCommandGroup alongWith(Command... parallel) {
+  public ParallelCommandGroup alongWith(Command... parallel) {
     ParallelCommandGroup group = new ParallelCommandGroup(this);
     group.addCommands(parallel);
     return group;
@@ -241,35 +303,13 @@
    * @param parallel the commands to run in parallel
    * @return the decorated command
    */
-  default ParallelRaceGroup raceWith(Command... parallel) {
+  public ParallelRaceGroup raceWith(Command... parallel) {
     ParallelRaceGroup group = new ParallelRaceGroup(this);
     group.addCommands(parallel);
     return group;
   }
 
   /**
-   * Decorates this command to run perpetually, ignoring its ordinary end conditions. The decorated
-   * command can still be interrupted or canceled.
-   *
-   * <p>Note: This decorator works by adding this command to a composition. The command the
-   * decorator was called on cannot be scheduled independently or be added to a different
-   * composition (namely, decorators), unless it is manually cleared from the list of composed
-   * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
-   * returned from this method can be further decorated without issue.
-   *
-   * @return the decorated command
-   * @deprecated PerpetualCommand violates the assumption that execute() doesn't get called after
-   *     isFinished() returns true -- an assumption that should be valid. This was unsafe/undefined
-   *     behavior from the start, and RepeatCommand provides an easy way to achieve similar end
-   *     results with slightly different (and safe) semantics.
-   */
-  @SuppressWarnings("removal") // PerpetualCommand
-  @Deprecated(forRemoval = true, since = "2023")
-  default PerpetualCommand perpetually() {
-    return new PerpetualCommand(this);
-  }
-
-  /**
    * Decorates this command to run repeatedly, restarting it when it ends, until this command is
    * interrupted. The decorated command can still be canceled.
    *
@@ -281,7 +321,7 @@
    *
    * @return the decorated command
    */
-  default RepeatCommand repeatedly() {
+  public RepeatCommand repeatedly() {
     return new RepeatCommand(this);
   }
 
@@ -292,7 +332,7 @@
    *
    * @return the decorated command
    */
-  default ProxyCommand asProxy() {
+  public ProxyCommand asProxy() {
     return new ProxyCommand(this);
   }
 
@@ -301,20 +341,46 @@
    * running and the condition changes to true, the command will not stop running. The requirements
    * of this command will be kept for the new conditional command.
    *
+   * <p>Note: This decorator works by adding this command to a composition. The command the
+   * decorator was called on cannot be scheduled independently or be added to a different
+   * composition (namely, decorators), unless it is manually cleared from the list of composed
+   * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
+   * returned from this method can be further decorated without issue.
+   *
    * @param condition the condition that will prevent the command from running
    * @return the decorated command
+   * @see #onlyIf(BooleanSupplier)
    */
-  default ConditionalCommand unless(BooleanSupplier condition) {
+  public ConditionalCommand unless(BooleanSupplier condition) {
     return new ConditionalCommand(new InstantCommand(), this, condition);
   }
 
   /**
+   * Decorates this command to only run if this condition is met. If the command is already running
+   * and the condition changes to false, the command will not stop running. The requirements of this
+   * command will be kept for the new conditional command.
+   *
+   * <p>Note: This decorator works by adding this command to a composition. The command the
+   * decorator was called on cannot be scheduled independently or be added to a different
+   * composition (namely, decorators), unless it is manually cleared from the list of composed
+   * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
+   * returned from this method can be further decorated without issue.
+   *
+   * @param condition the condition that will allow the command to run
+   * @return the decorated command
+   * @see #unless(BooleanSupplier)
+   */
+  public ConditionalCommand onlyIf(BooleanSupplier condition) {
+    return unless(() -> !condition.getAsBoolean());
+  }
+
+  /**
    * Decorates this command to run or stop when disabled.
    *
    * @param doesRunWhenDisabled true to run when disabled.
    * @return the decorated command
    */
-  default WrapperCommand ignoringDisable(boolean doesRunWhenDisabled) {
+  public WrapperCommand ignoringDisable(boolean doesRunWhenDisabled) {
     return new WrapperCommand(this) {
       @Override
       public boolean runsWhenDisabled() {
@@ -329,7 +395,7 @@
    * @param interruptBehavior the desired interrupt behavior
    * @return the decorated command
    */
-  default WrapperCommand withInterruptBehavior(InterruptionBehavior interruptBehavior) {
+  public WrapperCommand withInterruptBehavior(InterruptionBehavior interruptBehavior) {
     return new WrapperCommand(this) {
       @Override
       public InterruptionBehavior getInterruptionBehavior() {
@@ -346,7 +412,7 @@
    *     interrupted.
    * @return the decorated command
    */
-  default WrapperCommand finallyDo(BooleanConsumer end) {
+  public WrapperCommand finallyDo(BooleanConsumer end) {
     requireNonNullParam(end, "end", "Command.finallyDo()");
     return new WrapperCommand(this) {
       @Override
@@ -358,13 +424,25 @@
   }
 
   /**
+   * Decorates this command with a lambda to call on interrupt or end, following the command's
+   * inherent {@link #end(boolean)} method. The provided lambda will run identically in both
+   * interrupt and end cases.
+   *
+   * @param end a lambda to run when the command ends, whether or not it was interrupted.
+   * @return the decorated command
+   */
+  public WrapperCommand finallyDo(Runnable end) {
+    return finallyDo(interrupted -> end.run());
+  }
+
+  /**
    * Decorates this command with a lambda to call on interrupt, following the command's inherent
    * {@link #end(boolean)} method.
    *
    * @param handler a lambda to run when the command is interrupted
    * @return the decorated command
    */
-  default WrapperCommand handleInterrupt(Runnable handler) {
+  public WrapperCommand handleInterrupt(Runnable handler) {
     requireNonNullParam(handler, "handler", "Command.handleInterrupt()");
     return finallyDo(
         interrupted -> {
@@ -375,7 +453,7 @@
   }
 
   /** Schedules this command. */
-  default void schedule() {
+  public void schedule() {
     CommandScheduler.getInstance().schedule(this);
   }
 
@@ -385,7 +463,7 @@
    *
    * @see CommandScheduler#cancel(Command...)
    */
-  default void cancel() {
+  public void cancel() {
     CommandScheduler.getInstance().cancel(this);
   }
 
@@ -395,7 +473,7 @@
    *
    * @return Whether the command is scheduled.
    */
-  default boolean isScheduled() {
+  public boolean isScheduled() {
     return CommandScheduler.getInstance().isScheduled(this);
   }
 
@@ -405,7 +483,7 @@
    * @param requirement the subsystem to inquire about
    * @return whether the subsystem is required
    */
-  default boolean hasRequirement(Subsystem requirement) {
+  public boolean hasRequirement(Subsystem requirement) {
     return getRequirements().contains(requirement);
   }
 
@@ -415,7 +493,7 @@
    * @return a variant of {@link InterruptionBehavior}, defaulting to {@link
    *     InterruptionBehavior#kCancelSelf kCancelSelf}.
    */
-  default InterruptionBehavior getInterruptionBehavior() {
+  public InterruptionBehavior getInterruptionBehavior() {
     return InterruptionBehavior.kCancelSelf;
   }
 
@@ -425,43 +503,52 @@
    *
    * @return whether the command should run when the robot is disabled
    */
-  default boolean runsWhenDisabled() {
+  public boolean runsWhenDisabled() {
     return false;
   }
 
   /**
-   * Gets the name of this Command. Defaults to the simple class name if not overridden.
-   *
-   * @return The display name of the Command
-   */
-  default String getName() {
-    return this.getClass().getSimpleName();
-  }
-
-  /**
-   * Sets the name of this Command. Nullop if not overridden.
-   *
-   * @param name The display name of the Command.
-   */
-  default void setName(String name) {}
-
-  /**
    * Decorates this Command with a name.
    *
    * @param name name
    * @return the decorated Command
    */
-  default WrapperCommand withName(String name) {
+  public WrapperCommand withName(String name) {
     WrapperCommand wrapper = new WrapperCommand(Command.this) {};
     wrapper.setName(name);
     return wrapper;
   }
 
+  @Override
+  public void initSendable(SendableBuilder builder) {
+    builder.setSmartDashboardType("Command");
+    builder.addStringProperty(".name", this::getName, null);
+    builder.addBooleanProperty(
+        "running",
+        this::isScheduled,
+        value -> {
+          if (value) {
+            if (!isScheduled()) {
+              schedule();
+            }
+          } else {
+            if (isScheduled()) {
+              cancel();
+            }
+          }
+        });
+    builder.addBooleanProperty(
+        ".isParented", () -> CommandScheduler.getInstance().isComposed(this), null);
+    builder.addStringProperty(
+        "interruptBehavior", () -> getInterruptionBehavior().toString(), null);
+    builder.addBooleanProperty("runsWhenDisabled", this::runsWhenDisabled, null);
+  }
+
   /**
    * An enum describing the command's behavior when another command with a shared requirement is
    * scheduled.
    */
-  enum InterruptionBehavior {
+  public enum InterruptionBehavior {
     /**
      * This command ends, {@link #end(boolean) end(true)} is called, and the incoming command is
      * scheduled normally.
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java
index 395322a..12243cb 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java
@@ -4,103 +4,16 @@
 
 package edu.wpi.first.wpilibj2.command;
 
-import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
-
 import edu.wpi.first.util.sendable.Sendable;
-import edu.wpi.first.util.sendable.SendableBuilder;
-import edu.wpi.first.util.sendable.SendableRegistry;
-import java.util.HashSet;
-import java.util.Set;
 
 /**
  * A {@link Sendable} base class for {@link Command}s.
  *
  * <p>This class is provided by the NewCommands VendorDep
+ *
+ * @deprecated All functionality provided by {@link CommandBase} has been merged into {@link
+ *     Command}. Use {@link Command} instead.
  */
-public abstract class CommandBase implements Sendable, Command {
-  protected Set<Subsystem> m_requirements = new HashSet<>();
-
-  protected CommandBase() {
-    String name = getClass().getName();
-    SendableRegistry.add(this, name.substring(name.lastIndexOf('.') + 1));
-  }
-
-  /**
-   * Adds the specified requirements to the command.
-   *
-   * @param requirements the requirements to add
-   */
-  public final void addRequirements(Subsystem... requirements) {
-    for (Subsystem requirement : requirements) {
-      m_requirements.add(requireNonNullParam(requirement, "requirement", "addRequirements"));
-    }
-  }
-
-  @Override
-  public Set<Subsystem> getRequirements() {
-    return m_requirements;
-  }
-
-  @Override
-  public String getName() {
-    return SendableRegistry.getName(this);
-  }
-
-  /**
-   * Sets the name of this Command.
-   *
-   * @param name name
-   */
-  @Override
-  public void setName(String name) {
-    SendableRegistry.setName(this, name);
-  }
-
-  /**
-   * Gets the subsystem name of this Command.
-   *
-   * @return Subsystem name
-   */
-  public String getSubsystem() {
-    return SendableRegistry.getSubsystem(this);
-  }
-
-  /**
-   * Sets the subsystem name of this Command.
-   *
-   * @param subsystem subsystem name
-   */
-  public void setSubsystem(String subsystem) {
-    SendableRegistry.setSubsystem(this, subsystem);
-  }
-
-  /**
-   * Initializes this sendable. Useful for allowing implementations to easily extend SendableBase.
-   *
-   * @param builder the builder used to construct this sendable
-   */
-  @Override
-  public void initSendable(SendableBuilder builder) {
-    builder.setSmartDashboardType("Command");
-    builder.addStringProperty(".name", this::getName, null);
-    builder.addBooleanProperty(
-        "running",
-        this::isScheduled,
-        value -> {
-          if (value) {
-            if (!isScheduled()) {
-              schedule();
-            }
-          } else {
-            if (isScheduled()) {
-              cancel();
-            }
-          }
-        });
-    builder.addBooleanProperty(
-        ".isParented", () -> CommandScheduler.getInstance().isComposed(this), null);
-    builder.addStringProperty(
-        "interruptBehavior", () -> getInterruptionBehavior().toString(), null);
-    builder.addBooleanProperty("runsWhenDisabled", this::runsWhenDisabled, null);
-  }
-}
+@Deprecated(since = "2024", forRemoval = true)
+@SuppressWarnings("PMD.AbstractClassWithoutAnyMethod")
+public abstract class CommandBase extends Command {}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandGroupBase.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandGroupBase.java
deleted file mode 100644
index 73a1342..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandGroupBase.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj2.command;
-
-/**
- * A base for CommandGroups.
- *
- * <p>This class is provided by the NewCommands VendorDep
- *
- * @deprecated This class is an empty abstraction. Inherit directly from CommandBase/Command.
- */
-@Deprecated(forRemoval = true)
-public abstract class CommandGroupBase extends CommandBase {
-  /**
-   * Adds the given commands to the command group.
-   *
-   * @param commands The commands to add.
-   */
-  public abstract void addCommands(Command... commands);
-
-  /**
-   * Factory method for {@link SequentialCommandGroup}, included for brevity/convenience.
-   *
-   * @param commands the commands to include
-   * @return the command group
-   * @deprecated Replace with {@link Commands#sequence(Command...)}
-   */
-  @Deprecated
-  public static SequentialCommandGroup sequence(Command... commands) {
-    return new SequentialCommandGroup(commands);
-  }
-
-  /**
-   * Factory method for {@link ParallelCommandGroup}, included for brevity/convenience.
-   *
-   * @param commands the commands to include
-   * @return the command group
-   * @deprecated Replace with {@link Commands#parallel(Command...)}
-   */
-  @Deprecated
-  public static ParallelCommandGroup parallel(Command... commands) {
-    return new ParallelCommandGroup(commands);
-  }
-
-  /**
-   * Factory method for {@link ParallelRaceGroup}, included for brevity/convenience.
-   *
-   * @param commands the commands to include
-   * @return the command group
-   * @deprecated Replace with {@link Commands#race(Command...)}
-   */
-  @Deprecated
-  public static ParallelRaceGroup race(Command... commands) {
-    return new ParallelRaceGroup(commands);
-  }
-
-  /**
-   * Factory method for {@link ParallelDeadlineGroup}, included for brevity/convenience.
-   *
-   * @param deadline the deadline command
-   * @param commands the commands to include
-   * @return the command group
-   * @deprecated Replace with {@link Commands#deadline(Command, Command...)}
-   */
-  @Deprecated
-  public static ParallelDeadlineGroup deadline(Command deadline, Command... commands) {
-    return new ParallelDeadlineGroup(deadline, commands);
-  }
-}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java
index 15c8606..c868539 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java
@@ -9,13 +9,8 @@
 import edu.wpi.first.hal.FRCNetComm.tInstances;
 import edu.wpi.first.hal.FRCNetComm.tResourceType;
 import edu.wpi.first.hal.HAL;
-import edu.wpi.first.networktables.IntegerArrayEntry;
-import edu.wpi.first.networktables.IntegerArrayPublisher;
-import edu.wpi.first.networktables.IntegerArrayTopic;
-import edu.wpi.first.networktables.NTSendable;
-import edu.wpi.first.networktables.NTSendableBuilder;
-import edu.wpi.first.networktables.StringArrayPublisher;
-import edu.wpi.first.networktables.StringArrayTopic;
+import edu.wpi.first.util.sendable.Sendable;
+import edu.wpi.first.util.sendable.SendableBuilder;
 import edu.wpi.first.util.sendable.SendableRegistry;
 import edu.wpi.first.wpilibj.DriverStation;
 import edu.wpi.first.wpilibj.RobotBase;
@@ -33,8 +28,10 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.WeakHashMap;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 /**
@@ -46,7 +43,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public final class CommandScheduler implements NTSendable, AutoCloseable {
+public final class CommandScheduler implements Sendable, AutoCloseable {
   /** The Singleton Instance. */
   private static CommandScheduler instance;
 
@@ -62,6 +59,8 @@
     return instance;
   }
 
+  private static final Optional<Command> kNoInterruptor = Optional.empty();
+
   private final Set<Command> m_composedCommands = Collections.newSetFromMap(new WeakHashMap<>());
 
   // A set of the currently-running commands.
@@ -84,14 +83,16 @@
   // Lists of user-supplied actions to be executed on scheduling events for every command.
   private final List<Consumer<Command>> m_initActions = new ArrayList<>();
   private final List<Consumer<Command>> m_executeActions = new ArrayList<>();
-  private final List<Consumer<Command>> m_interruptActions = new ArrayList<>();
+  private final List<BiConsumer<Command, Optional<Command>>> m_interruptActions = new ArrayList<>();
   private final List<Consumer<Command>> m_finishActions = new ArrayList<>();
 
   // Flag and queues for avoiding ConcurrentModificationException if commands are
   // scheduled/canceled during run
   private boolean m_inRunLoop;
   private final Set<Command> m_toSchedule = new LinkedHashSet<>();
-  private final List<Command> m_toCancel = new ArrayList<>();
+  private final List<Command> m_toCancelCommands = new ArrayList<>();
+  private final List<Optional<Command>> m_toCancelInterruptors = new ArrayList<>();
+  private final Set<Command> m_endingCommands = new LinkedHashSet<>();
 
   private final Watchdog m_watchdog = new Watchdog(TimedRobot.kDefaultPeriod, () -> {});
 
@@ -152,27 +153,6 @@
   }
 
   /**
-   * Adds a button binding to the scheduler, which will be polled to schedule commands.
-   *
-   * @param button The button to add
-   * @deprecated Use {@link edu.wpi.first.wpilibj2.command.button.Trigger}
-   */
-  @Deprecated(since = "2023")
-  public void addButton(Runnable button) {
-    m_activeButtonLoop.bind(requireNonNullParam(button, "button", "addButton"));
-  }
-
-  /**
-   * Removes all button bindings from the scheduler.
-   *
-   * @deprecated call {@link EventLoop#clear()} on {@link #getActiveButtonLoop()} directly instead.
-   */
-  @Deprecated(since = "2023")
-  public void clearButtons() {
-    m_activeButtonLoop.clear();
-  }
-
-  /**
    * Initializes a given command, adds its requirements to the list, and performs the init actions.
    *
    * @param command The command to initialize
@@ -237,7 +217,7 @@
       for (Subsystem requirement : requirements) {
         Command requiring = requiring(requirement);
         if (requiring != null) {
-          cancel(requiring);
+          cancel(requiring, Optional.of(command));
         }
       }
       initCommand(command, requirements);
@@ -292,18 +272,13 @@
     m_watchdog.addEpoch("buttons.run()");
 
     m_inRunLoop = true;
+    boolean isDisabled = RobotState.isDisabled();
     // Run scheduled commands, remove finished commands.
     for (Iterator<Command> iterator = m_scheduledCommands.iterator(); iterator.hasNext(); ) {
       Command command = iterator.next();
 
-      if (!command.runsWhenDisabled() && RobotState.isDisabled()) {
-        command.end(true);
-        for (Consumer<Command> action : m_interruptActions) {
-          action.accept(command);
-        }
-        m_requirements.keySet().removeAll(command.getRequirements());
-        iterator.remove();
-        m_watchdog.addEpoch(command.getName() + ".end(true)");
+      if (isDisabled && !command.runsWhenDisabled()) {
+        cancel(command, kNoInterruptor);
         continue;
       }
 
@@ -313,10 +288,12 @@
       }
       m_watchdog.addEpoch(command.getName() + ".execute()");
       if (command.isFinished()) {
+        m_endingCommands.add(command);
         command.end(false);
         for (Consumer<Command> action : m_finishActions) {
           action.accept(command);
         }
+        m_endingCommands.remove(command);
         iterator.remove();
 
         m_requirements.keySet().removeAll(command.getRequirements());
@@ -330,12 +307,13 @@
       schedule(command);
     }
 
-    for (Command command : m_toCancel) {
-      cancel(command);
+    for (int i = 0; i < m_toCancelCommands.size(); i++) {
+      cancel(m_toCancelCommands.get(i), m_toCancelInterruptors.get(i));
     }
 
     m_toSchedule.clear();
-    m_toCancel.clear();
+    m_toCancelCommands.clear();
+    m_toCancelInterruptors.clear();
 
     // Add default commands for un-required registered subsystems.
     for (Map.Entry<Subsystem, Command> subsystemCommand : m_subsystems.entrySet()) {
@@ -384,6 +362,15 @@
   }
 
   /**
+   * Un-registers all registered Subsystems with the scheduler. All currently registered subsystems
+   * will no longer have their periodic block called, and will not have their default command
+   * scheduled.
+   */
+  public void unregisterAllSubsystems() {
+    m_subsystems.clear();
+  }
+
+  /**
    * Sets the default command for a subsystem. Registers that subsystem if it is not already
    * registered. Default commands will run whenever there is no other command currently scheduled
    * that requires the subsystem. Default commands should be written to never end (i.e. their {@link
@@ -457,28 +444,47 @@
    * @param commands the commands to cancel
    */
   public void cancel(Command... commands) {
+    for (Command command : commands) {
+      cancel(command, kNoInterruptor);
+    }
+  }
+
+  /**
+   * Cancels a command. The scheduler will only call {@link Command#end(boolean)} method of the
+   * canceled command with {@code true}, indicating they were canceled (as opposed to finishing
+   * normally).
+   *
+   * <p>Commands will be canceled regardless of {@link InterruptionBehavior interruption behavior}.
+   *
+   * @param command the command to cancel
+   * @param interruptor the interrupting command, if any
+   */
+  private void cancel(Command command, Optional<Command> interruptor) {
+    if (command == null) {
+      DriverStation.reportWarning("Tried to cancel a null command", true);
+      return;
+    }
+    if (m_endingCommands.contains(command)) {
+      return;
+    }
     if (m_inRunLoop) {
-      m_toCancel.addAll(List.of(commands));
+      m_toCancelCommands.add(command);
+      m_toCancelInterruptors.add(interruptor);
+      return;
+    }
+    if (!isScheduled(command)) {
       return;
     }
 
-    for (Command command : commands) {
-      if (command == null) {
-        DriverStation.reportWarning("Tried to cancel a null command", true);
-        continue;
-      }
-      if (!isScheduled(command)) {
-        continue;
-      }
-
-      m_scheduledCommands.remove(command);
-      m_requirements.keySet().removeAll(command.getRequirements());
-      command.end(true);
-      for (Consumer<Command> action : m_interruptActions) {
-        action.accept(command);
-      }
-      m_watchdog.addEpoch(command.getName() + ".end(true)");
+    m_endingCommands.add(command);
+    command.end(true);
+    for (BiConsumer<Command, Optional<Command>> action : m_interruptActions) {
+      action.accept(command, interruptor);
     }
+    m_endingCommands.remove(command);
+    m_scheduledCommands.remove(command);
+    m_requirements.keySet().removeAll(command.getRequirements());
+    m_watchdog.addEpoch(command.getName() + ".end(true)");
   }
 
   /** Cancels all commands that are currently scheduled. */
@@ -545,6 +551,19 @@
    * @param action the action to perform
    */
   public void onCommandInterrupt(Consumer<Command> action) {
+    requireNonNullParam(action, "action", "onCommandInterrupt");
+    m_interruptActions.add((command, interruptor) -> action.accept(command));
+  }
+
+  /**
+   * Adds an action to perform on the interruption of any command by the scheduler. The action
+   * receives the interrupted command and an Optional containing the interrupting command, or
+   * Optional.empty() if it was not canceled by a command (e.g., by {@link
+   * CommandScheduler#cancel}).
+   *
+   * @param action the action to perform
+   */
+  public void onCommandInterrupt(BiConsumer<Command, Optional<Command>> action) {
     m_interruptActions.add(requireNonNullParam(action, "action", "onCommandInterrupt"));
   }
 
@@ -602,7 +621,7 @@
   public void requireNotComposed(Command command) {
     if (m_composedCommands.contains(command)) {
       throw new IllegalArgumentException(
-          "Commands that have been composed may not be added to another composition or scheduled"
+          "Commands that have been composed may not be added to another composition or scheduled "
               + "individually!");
     }
   }
@@ -616,7 +635,7 @@
   public void requireNotComposed(Collection<Command> commands) {
     if (!Collections.disjoint(commands, getComposedCommands())) {
       throw new IllegalArgumentException(
-          "Commands that have been composed may not be added to another composition or scheduled"
+          "Commands that have been composed may not be added to another composition or scheduled "
               + "individually!");
     }
   }
@@ -626,7 +645,6 @@
    *
    * @param command The command to check
    * @return true if composed
-   * @throws IllegalArgumentException if the given commands have already been composed.
    */
   public boolean isComposed(Command command) {
     return getComposedCommands().contains(command);
@@ -637,45 +655,46 @@
   }
 
   @Override
-  public void initSendable(NTSendableBuilder builder) {
+  public void initSendable(SendableBuilder builder) {
     builder.setSmartDashboardType("Scheduler");
-    final StringArrayPublisher namesPub = new StringArrayTopic(builder.getTopic("Names")).publish();
-    final IntegerArrayPublisher idsPub = new IntegerArrayTopic(builder.getTopic("Ids")).publish();
-    final IntegerArrayEntry cancelEntry =
-        new IntegerArrayTopic(builder.getTopic("Cancel")).getEntry(new long[] {});
-    builder.addCloseable(namesPub);
-    builder.addCloseable(idsPub);
-    builder.addCloseable(cancelEntry);
-    builder.setUpdateTable(
+    builder.addStringArrayProperty(
+        "Names",
         () -> {
-          if (namesPub == null || idsPub == null || cancelEntry == null) {
-            return;
-          }
-
-          Map<Long, Command> ids = new LinkedHashMap<>();
-          List<String> names = new ArrayList<>();
-          long[] ids2 = new long[m_scheduledCommands.size()];
-
+          String[] names = new String[m_scheduledCommands.size()];
           int i = 0;
           for (Command command : m_scheduledCommands) {
+            names[i] = command.getName();
+            i++;
+          }
+          return names;
+        },
+        null);
+    builder.addIntegerArrayProperty(
+        "Ids",
+        () -> {
+          long[] ids = new long[m_scheduledCommands.size()];
+          int i = 0;
+          for (Command command : m_scheduledCommands) {
+            ids[i] = command.hashCode();
+            i++;
+          }
+          return ids;
+        },
+        null);
+    builder.addIntegerArrayProperty(
+        "Cancel",
+        () -> {
+          return new long[] {};
+        },
+        toCancel -> {
+          Map<Long, Command> ids = new LinkedHashMap<>();
+          for (Command command : m_scheduledCommands) {
             long id = command.hashCode();
             ids.put(id, command);
-            names.add(command.getName());
-            ids2[i] = id;
-            i++;
           }
-
-          long[] toCancel = cancelEntry.get();
-          if (toCancel.length > 0) {
-            for (long hash : toCancel) {
-              cancel(ids.get(hash));
-              ids.remove(hash);
-            }
-            cancelEntry.set(new long[] {});
+          for (long hash : toCancel) {
+            cancel(ids.get(hash));
           }
-
-          namesPub.set(names.toArray(new String[] {}));
-          idsPub.set(ids2);
         });
   }
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Commands.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Commands.java
index 737b9cc..7295e3c 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Commands.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Commands.java
@@ -7,6 +7,7 @@
 import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
 
 import java.util.Map;
+import java.util.Set;
 import java.util.function.BooleanSupplier;
 import java.util.function.Supplier;
 
@@ -21,10 +22,20 @@
    *
    * @return the command
    */
-  public static CommandBase none() {
+  public static Command none() {
     return new InstantCommand();
   }
 
+  /**
+   * Constructs a command that does nothing until interrupted.
+   *
+   * @param requirements Subsystems to require
+   * @return the command
+   */
+  public static Command idle(Subsystem... requirements) {
+    return run(() -> {}, requirements);
+  }
+
   // Action Commands
 
   /**
@@ -35,7 +46,7 @@
    * @return the command
    * @see InstantCommand
    */
-  public static CommandBase runOnce(Runnable action, Subsystem... requirements) {
+  public static Command runOnce(Runnable action, Subsystem... requirements) {
     return new InstantCommand(action, requirements);
   }
 
@@ -47,7 +58,7 @@
    * @return the command
    * @see RunCommand
    */
-  public static CommandBase run(Runnable action, Subsystem... requirements) {
+  public static Command run(Runnable action, Subsystem... requirements) {
     return new RunCommand(action, requirements);
   }
 
@@ -61,7 +72,7 @@
    * @return the command
    * @see StartEndCommand
    */
-  public static CommandBase startEnd(Runnable start, Runnable end, Subsystem... requirements) {
+  public static Command startEnd(Runnable start, Runnable end, Subsystem... requirements) {
     return new StartEndCommand(start, end, requirements);
   }
 
@@ -74,7 +85,7 @@
    * @param requirements subsystems the action requires
    * @return the command
    */
-  public static CommandBase runEnd(Runnable run, Runnable end, Subsystem... requirements) {
+  public static Command runEnd(Runnable run, Runnable end, Subsystem... requirements) {
     requireNonNullParam(end, "end", "Command.runEnd");
     return new FunctionalCommand(
         () -> {}, run, interrupted -> end.run(), () -> false, requirements);
@@ -87,7 +98,7 @@
    * @return the command
    * @see PrintCommand
    */
-  public static CommandBase print(String message) {
+  public static Command print(String message) {
     return new PrintCommand(message);
   }
 
@@ -100,7 +111,7 @@
    * @return the command
    * @see WaitCommand
    */
-  public static CommandBase waitSeconds(double seconds) {
+  public static Command waitSeconds(double seconds) {
     return new WaitCommand(seconds);
   }
 
@@ -111,7 +122,7 @@
    * @return the command
    * @see WaitUntilCommand
    */
-  public static CommandBase waitUntil(BooleanSupplier condition) {
+  public static Command waitUntil(BooleanSupplier condition) {
     return new WaitUntilCommand(condition);
   }
 
@@ -126,35 +137,61 @@
    * @return the command
    * @see ConditionalCommand
    */
-  public static CommandBase either(Command onTrue, Command onFalse, BooleanSupplier selector) {
+  public static Command either(Command onTrue, Command onFalse, BooleanSupplier selector) {
     return new ConditionalCommand(onTrue, onFalse, selector);
   }
 
   /**
    * Runs one of several commands, based on the selector function.
    *
+   * @param <K> The type of key used to select the command
    * @param selector the selector function
    * @param commands map of commands to select from
    * @return the command
    * @see SelectCommand
    */
-  public static CommandBase select(Map<Object, Command> commands, Supplier<Object> selector) {
-    return new SelectCommand(commands, selector);
+  public static <K> Command select(Map<K, Command> commands, Supplier<? extends K> selector) {
+    return new SelectCommand<>(commands, selector);
   }
 
   /**
+   * Runs the command supplied by the supplier.
+   *
+   * @param supplier the command supplier
+   * @param requirements the set of requirements for this command
+   * @return the command
+   * @see DeferredCommand
+   */
+  public static Command defer(Supplier<Command> supplier, Set<Subsystem> requirements) {
+    return new DeferredCommand(supplier, requirements);
+  }
+
+  /**
+   * Constructs a command that schedules the command returned from the supplier when initialized,
+   * and ends when it is no longer scheduled. The supplier is called when the command is
+   * initialized.
+   *
+   * @param supplier the command supplier
+   * @return the command
+   * @see ProxyCommand
+   */
+  public static Command deferredProxy(Supplier<Command> supplier) {
+    return new ProxyCommand(supplier);
+  }
+
+  // Command Groups
+
+  /**
    * Runs a group of commands in series, one after the other.
    *
    * @param commands the commands to include
    * @return the command group
    * @see SequentialCommandGroup
    */
-  public static CommandBase sequence(Command... commands) {
+  public static Command sequence(Command... commands) {
     return new SequentialCommandGroup(commands);
   }
 
-  // Command Groups
-
   /**
    * Runs a group of commands in series, one after the other. Once the last command ends, the group
    * is restarted.
@@ -164,7 +201,7 @@
    * @see SequentialCommandGroup
    * @see Command#repeatedly()
    */
-  public static CommandBase repeatingSequence(Command... commands) {
+  public static Command repeatingSequence(Command... commands) {
     return sequence(commands).repeatedly();
   }
 
@@ -175,7 +212,7 @@
    * @return the command
    * @see ParallelCommandGroup
    */
-  public static CommandBase parallel(Command... commands) {
+  public static Command parallel(Command... commands) {
     return new ParallelCommandGroup(commands);
   }
 
@@ -187,7 +224,7 @@
    * @return the command group
    * @see ParallelRaceGroup
    */
-  public static CommandBase race(Command... commands) {
+  public static Command race(Command... commands) {
     return new ParallelRaceGroup(commands);
   }
 
@@ -200,7 +237,7 @@
    * @return the command group
    * @see ParallelDeadlineGroup
    */
-  public static CommandBase deadline(Command deadline, Command... commands) {
+  public static Command deadline(Command deadline, Command... commands) {
     return new ParallelDeadlineGroup(deadline, commands);
   }
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ConditionalCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ConditionalCommand.java
index 782f217..42db3f0 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ConditionalCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ConditionalCommand.java
@@ -19,7 +19,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class ConditionalCommand extends CommandBase {
+public class ConditionalCommand extends Command {
   private final Command m_onTrue;
   private final Command m_onFalse;
   private final BooleanSupplier m_condition;
@@ -74,6 +74,16 @@
   }
 
   @Override
+  public InterruptionBehavior getInterruptionBehavior() {
+    if (m_onTrue.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf
+        || m_onFalse.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
+      return InterruptionBehavior.kCancelSelf;
+    } else {
+      return InterruptionBehavior.kCancelIncoming;
+    }
+  }
+
+  @Override
   public void initSendable(SendableBuilder builder) {
     super.initSendable(builder);
     builder.addStringProperty("onTrue", m_onTrue::getName, null);
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/DeferredCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/DeferredCommand.java
new file mode 100644
index 0000000..76a5276
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/DeferredCommand.java
@@ -0,0 +1,78 @@
+// 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.
+
+package edu.wpi.first.wpilibj2.command;
+
+import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
+
+import edu.wpi.first.util.sendable.SendableBuilder;
+import java.util.Set;
+import java.util.function.Supplier;
+
+/**
+ * Defers Command construction to runtime. Runs the command returned by the supplier when this
+ * command is initialized, and ends when it ends. Useful for performing runtime tasks before
+ * creating a new command. If this command is interrupted, it will cancel the command.
+ *
+ * <p>Note that the supplier <i>must</i> create a new Command each call. For selecting one of a
+ * preallocated set of commands, use {@link SelectCommand}.
+ *
+ * <p>This class is provided by the NewCommands VendorDep
+ */
+public class DeferredCommand extends Command {
+  private final Command m_nullCommand =
+      new PrintCommand("[DeferredCommand] Supplied command was null!");
+
+  private final Supplier<Command> m_supplier;
+  private Command m_command = m_nullCommand;
+
+  /**
+   * Creates a new DeferredCommand that runs the supplied command when initialized, and ends when it
+   * ends. Useful for lazily creating commands at runtime. The {@link Supplier} will be called each
+   * time this command is initialized. The Supplier <i>must</i> create a new Command each call.
+   *
+   * @param supplier The command supplier
+   * @param requirements The command requirements. This is a {@link Set} to prevent accidental
+   *     omission of command requirements. Use {@link Set#of()} to easily construct a requirement
+   *     set.
+   */
+  public DeferredCommand(Supplier<Command> supplier, Set<Subsystem> requirements) {
+    m_supplier = requireNonNullParam(supplier, "supplier", "DeferredCommand");
+    addRequirements(requirements.toArray(new Subsystem[0]));
+  }
+
+  @Override
+  public void initialize() {
+    var cmd = m_supplier.get();
+    if (cmd != null) {
+      m_command = cmd;
+      CommandScheduler.getInstance().registerComposedCommands(m_command);
+    }
+    m_command.initialize();
+  }
+
+  @Override
+  public void execute() {
+    m_command.execute();
+  }
+
+  @Override
+  public boolean isFinished() {
+    return m_command.isFinished();
+  }
+
+  @Override
+  public void end(boolean interrupted) {
+    m_command.end(interrupted);
+    m_command = m_nullCommand;
+  }
+
+  @Override
+  @SuppressWarnings("PMD.CompareObjectsWithEquals")
+  public void initSendable(SendableBuilder builder) {
+    super.initSendable(builder);
+    builder.addStringProperty(
+        "deferred", () -> m_command == m_nullCommand ? "null" : m_command.getName(), null);
+  }
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/FunctionalCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/FunctionalCommand.java
index fcd4bd7..dde5ae1 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/FunctionalCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/FunctionalCommand.java
@@ -17,7 +17,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class FunctionalCommand extends CommandBase {
+public class FunctionalCommand extends Command {
   protected final Runnable m_onInit;
   protected final Runnable m_onExecute;
   protected final Consumer<Boolean> m_onEnd;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommand.java
index f6b6668..16d9c8a 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommand.java
@@ -38,7 +38,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class MecanumControllerCommand extends CommandBase {
+public class MecanumControllerCommand extends Command {
   private final Timer m_timer = new Timer();
   private final boolean m_usePID;
   private final Trajectory m_trajectory;
@@ -332,8 +332,7 @@
     m_prevSpeeds =
         m_kinematics.toWheelSpeeds(new ChassisSpeeds(initialXVelocity, initialYVelocity, 0.0));
 
-    m_timer.reset();
-    m_timer.start();
+    m_timer.restart();
   }
 
   @Override
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/NotifierCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/NotifierCommand.java
index 730ef1b..ad1a12a 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/NotifierCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/NotifierCommand.java
@@ -17,7 +17,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class NotifierCommand extends CommandBase {
+public class NotifierCommand extends Command {
   protected final Notifier m_notifier;
   protected final double m_period;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDCommand.java
index ec184c8..c761f3f 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDCommand.java
@@ -18,7 +18,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class PIDCommand extends CommandBase {
+public class PIDCommand extends Command {
   protected final PIDController m_controller;
   protected DoubleSupplier m_measurement;
   protected DoubleSupplier m_setpoint;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelCommandGroup.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelCommandGroup.java
index 3b36f42..8604189 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelCommandGroup.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelCommandGroup.java
@@ -17,8 +17,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-@SuppressWarnings("removal")
-public class ParallelCommandGroup extends CommandGroupBase {
+public class ParallelCommandGroup extends Command {
   // maps commands in this composition to whether they are still running
   private final Map<Command, Boolean> m_commands = new HashMap<>();
   private boolean m_runWhenDisabled = true;
@@ -35,7 +34,11 @@
     addCommands(commands);
   }
 
-  @Override
+  /**
+   * Adds the given commands to the group.
+   *
+   * @param commands Commands to add to the group.
+   */
   public final void addCommands(Command... commands) {
     if (m_commands.containsValue(true)) {
       throw new IllegalStateException(
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelDeadlineGroup.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelDeadlineGroup.java
index 2e62a9f..6d263e8 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelDeadlineGroup.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelDeadlineGroup.java
@@ -20,8 +20,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-@SuppressWarnings("removal")
-public class ParallelDeadlineGroup extends CommandGroupBase {
+public class ParallelDeadlineGroup extends Command {
   // maps commands in this composition to whether they are still running
   private final Map<Command, Boolean> m_commands = new HashMap<>();
   private boolean m_runWhenDisabled = true;
@@ -59,7 +58,11 @@
     m_deadline = deadline;
   }
 
-  @Override
+  /**
+   * Adds the given commands to the group.
+   *
+   * @param commands Commands to add to the group.
+   */
   public final void addCommands(Command... commands) {
     if (!m_finished) {
       throw new IllegalStateException(
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelRaceGroup.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelRaceGroup.java
index e5ba80d..7195cd7 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelRaceGroup.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ParallelRaceGroup.java
@@ -18,8 +18,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-@SuppressWarnings("removal")
-public class ParallelRaceGroup extends CommandGroupBase {
+public class ParallelRaceGroup extends Command {
   private final Set<Command> m_commands = new HashSet<>();
   private boolean m_runWhenDisabled = true;
   private boolean m_finished = true;
@@ -36,7 +35,11 @@
     addCommands(commands);
   }
 
-  @Override
+  /**
+   * Adds the given commands to the group.
+   *
+   * @param commands Commands to add to the group.
+   */
   public final void addCommands(Command... commands) {
     if (!m_finished) {
       throw new IllegalStateException(
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PerpetualCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PerpetualCommand.java
deleted file mode 100644
index 9fb9019..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PerpetualCommand.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj2.command;
-
-/**
- * A command that runs another command in perpetuity, ignoring that command's end conditions. While
- * this class does not extend {@link CommandGroupBase}, it is still considered a composition, as it
- * allows one to compose another command within it; the command instances that are passed to it
- * cannot be added to any other groups, or scheduled individually.
- *
- * <p>As a rule, CommandGroups require the union of the requirements of their component commands.
- *
- * <p>This class is provided by the NewCommands VendorDep
- *
- * @deprecated PerpetualCommand violates the assumption that execute() doesn't get called after
- *     isFinished() returns true -- an assumption that should be valid. This was unsafe/undefined
- *     behavior from the start, and RepeatCommand provides an easy way to achieve similar end
- *     results with slightly different (and safe) semantics.
- */
-@Deprecated(forRemoval = true, since = "2023")
-public class PerpetualCommand extends CommandBase {
-  protected final Command m_command;
-
-  /**
-   * Creates a new PerpetualCommand. Will run another command in perpetuity, ignoring that command's
-   * end conditions, unless this command itself is interrupted.
-   *
-   * @param command the command to run perpetually
-   */
-  public PerpetualCommand(Command command) {
-    CommandScheduler.getInstance().registerComposedCommands(command);
-    m_command = command;
-    m_requirements.addAll(command.getRequirements());
-  }
-
-  @Override
-  public void initialize() {
-    m_command.initialize();
-  }
-
-  @Override
-  public void execute() {
-    m_command.execute();
-  }
-
-  @Override
-  public void end(boolean interrupted) {
-    m_command.end(interrupted);
-  }
-
-  @Override
-  public boolean runsWhenDisabled() {
-    return m_command.runsWhenDisabled();
-  }
-}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java
index 4344913..f7175fc 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java
@@ -20,7 +20,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class ProfiledPIDCommand extends CommandBase {
+public class ProfiledPIDCommand extends Command {
   protected final ProfiledPIDController m_controller;
   protected DoubleSupplier m_measurement;
   protected Supplier<State> m_goal;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProxyCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProxyCommand.java
index ef6ca5b..d6e93e8 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProxyCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProxyCommand.java
@@ -15,7 +15,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class ProxyCommand extends CommandBase {
+public class ProxyCommand extends Command {
   private final Supplier<Command> m_supplier;
   private Command m_command;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProxyScheduleCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProxyScheduleCommand.java
deleted file mode 100644
index eec1b18..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProxyScheduleCommand.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj2.command;
-
-import java.util.Set;
-
-/**
- * Schedules the given commands when this command is initialized, and ends when all the commands are
- * no longer scheduled. Useful for forking off from CommandGroups. If this command is interrupted,
- * it will cancel all the commands.
- *
- * <p>This class is provided by the NewCommands VendorDep
- */
-public class ProxyScheduleCommand extends CommandBase {
-  private final Set<Command> m_toSchedule;
-  private boolean m_finished;
-
-  /**
-   * Creates a new ProxyScheduleCommand that schedules the given commands when initialized, and ends
-   * when they are all no longer scheduled.
-   *
-   * @param toSchedule the commands to schedule
-   * @deprecated Replace with {@link ProxyCommand}, composing multiple of them in a {@link
-   *     ParallelRaceGroup} if needed.
-   */
-  @Deprecated
-  public ProxyScheduleCommand(Command... toSchedule) {
-    m_toSchedule = Set.of(toSchedule);
-  }
-
-  @Override
-  public void initialize() {
-    for (Command command : m_toSchedule) {
-      command.schedule();
-    }
-  }
-
-  @Override
-  public void end(boolean interrupted) {
-    if (interrupted) {
-      for (Command command : m_toSchedule) {
-        command.cancel();
-      }
-    }
-  }
-
-  @Override
-  public void execute() {
-    m_finished = true;
-    for (Command command : m_toSchedule) {
-      m_finished &= !command.isScheduled();
-    }
-  }
-
-  @Override
-  public boolean isFinished() {
-    return m_finished;
-  }
-}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RamseteCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RamseteCommand.java
index 69e271b..9f19e4a 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RamseteCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RamseteCommand.java
@@ -33,7 +33,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class RamseteCommand extends CommandBase {
+public class RamseteCommand extends Command {
   private final Timer m_timer = new Timer();
   private final boolean m_usePID;
   private final Trajectory m_trajectory;
@@ -45,7 +45,7 @@
   private final PIDController m_leftController;
   private final PIDController m_rightController;
   private final BiConsumer<Double, Double> m_output;
-  private DifferentialDriveWheelSpeeds m_prevSpeeds;
+  private DifferentialDriveWheelSpeeds m_prevSpeeds = new DifferentialDriveWheelSpeeds();
   private double m_prevTime;
 
   /**
@@ -143,8 +143,7 @@
                 initialState.velocityMetersPerSecond,
                 0,
                 initialState.curvatureRadPerMeter * initialState.velocityMetersPerSecond));
-    m_timer.reset();
-    m_timer.start();
+    m_timer.restart();
     if (m_usePID) {
       m_leftController.reset();
       m_rightController.reset();
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RepeatCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RepeatCommand.java
index 6de06c5..5b49ae0 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RepeatCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RepeatCommand.java
@@ -19,7 +19,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class RepeatCommand extends CommandBase {
+public class RepeatCommand extends Command {
   protected final Command m_command;
   private boolean m_ended;
 
@@ -63,7 +63,12 @@
 
   @Override
   public void end(boolean interrupted) {
-    m_command.end(interrupted);
+    // Make sure we didn't already call end() (which would happen if the command finished in the
+    // last call to our execute())
+    if (!m_ended) {
+      m_command.end(interrupted);
+      m_ended = true;
+    }
   }
 
   @Override
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ScheduleCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ScheduleCommand.java
index a61be2f..cc22341 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ScheduleCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ScheduleCommand.java
@@ -13,7 +13,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class ScheduleCommand extends CommandBase {
+public class ScheduleCommand extends Command {
   private final Set<Command> m_toSchedule;
 
   /**
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SelectCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SelectCommand.java
index d947701..28d06eb 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SelectCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SelectCommand.java
@@ -11,38 +11,41 @@
 import java.util.function.Supplier;
 
 /**
- * A command composition that runs one of a selection of commands, either using a selector and a key
- * to command mapping, or a supplier that returns the command directly at runtime.
+ * A command composition that runs one of a selection of commands using a selector and a key to
+ * command mapping.
  *
  * <p>The rules for command compositions apply: command instances that are passed to it cannot be
  * added to any other composition or scheduled individually, and the composition requires all
  * subsystems its components require.
  *
  * <p>This class is provided by the NewCommands VendorDep
+ *
+ * @param <K> The type of key used to select the command
  */
-public class SelectCommand extends CommandBase {
-  private final Map<Object, Command> m_commands;
-  private final Supplier<Object> m_selector;
-  private final Supplier<Command> m_toRun;
+public class SelectCommand<K> extends Command {
+  private final Map<K, Command> m_commands;
+  private final Supplier<? extends K> m_selector;
   private Command m_selectedCommand;
   private boolean m_runsWhenDisabled = true;
   private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
 
+  private final Command m_defaultCommand =
+      new PrintCommand("SelectCommand selector value does not correspond to any command!");
+
   /**
    * Creates a new SelectCommand.
    *
    * @param commands the map of commands to choose from
    * @param selector the selector to determine which command to run
    */
-  public SelectCommand(Map<Object, Command> commands, Supplier<Object> selector) {
+  public SelectCommand(Map<K, Command> commands, Supplier<? extends K> selector) {
     m_commands = requireNonNullParam(commands, "commands", "SelectCommand");
     m_selector = requireNonNullParam(selector, "selector", "SelectCommand");
 
+    CommandScheduler.getInstance().registerComposedCommands(m_defaultCommand);
     CommandScheduler.getInstance()
         .registerComposedCommands(commands.values().toArray(new Command[] {}));
 
-    m_toRun = null;
-
     for (Command command : m_commands.values()) {
       m_requirements.addAll(command.getRequirements());
       m_runsWhenDisabled &= command.runsWhenDisabled();
@@ -52,36 +55,9 @@
     }
   }
 
-  /**
-   * Creates a new SelectCommand.
-   *
-   * @param toRun a supplier providing the command to run
-   * @deprecated Replace with {@link ProxyCommand}
-   */
-  @Deprecated
-  public SelectCommand(Supplier<Command> toRun) {
-    m_commands = null;
-    m_selector = null;
-    m_toRun = requireNonNullParam(toRun, "toRun", "SelectCommand");
-
-    // we have no way of checking the underlying command, so default.
-    m_runsWhenDisabled = false;
-    m_interruptBehavior = InterruptionBehavior.kCancelSelf;
-  }
-
   @Override
   public void initialize() {
-    if (m_selector != null) {
-      if (!m_commands.containsKey(m_selector.get())) {
-        m_selectedCommand =
-            new PrintCommand(
-                "SelectCommand selector value does not correspond to" + " any command!");
-        return;
-      }
-      m_selectedCommand = m_commands.get(m_selector.get());
-    } else {
-      m_selectedCommand = m_toRun.get();
-    }
+    m_selectedCommand = m_commands.getOrDefault(m_selector.get(), m_defaultCommand);
     m_selectedCommand.initialize();
   }
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SequentialCommandGroup.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SequentialCommandGroup.java
index 892f94a..c3598cb 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SequentialCommandGroup.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SequentialCommandGroup.java
@@ -17,8 +17,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-@SuppressWarnings("removal")
-public class SequentialCommandGroup extends CommandGroupBase {
+public class SequentialCommandGroup extends Command {
   private final List<Command> m_commands = new ArrayList<>();
   private int m_currentCommandIndex = -1;
   private boolean m_runWhenDisabled = true;
@@ -34,7 +33,11 @@
     addCommands(commands);
   }
 
-  @Override
+  /**
+   * Adds the given commands to the group.
+   *
+   * @param commands Commands to add, in order of execution.
+   */
   public final void addCommands(Command... commands) {
     if (m_currentCommandIndex != -1) {
       throw new IllegalStateException(
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Subsystem.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Subsystem.java
index 0584ced..ac89dbd 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Subsystem.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Subsystem.java
@@ -4,6 +4,9 @@
 
 package edu.wpi.first.wpilibj2.command;
 
+import java.util.Set;
+import java.util.function.Supplier;
+
 /**
  * A robot subsystem. Subsystems are the basic unit of robot organization in the Command-based
  * framework; they encapsulate low-level hardware objects (motor controllers, sensors, etc.) and
@@ -51,6 +54,14 @@
   }
 
   /**
+   * Removes the default command for the subsystem. This will not cancel the default command if it
+   * is currently running.
+   */
+  default void removeDefaultCommand() {
+    CommandScheduler.getInstance().removeDefaultCommand(this);
+  }
+
+  /**
    * Gets the default command for this subsystem. Returns null if no default command is currently
    * associated with the subsystem.
    *
@@ -85,7 +96,7 @@
    * @return the command
    * @see InstantCommand
    */
-  default CommandBase runOnce(Runnable action) {
+  default Command runOnce(Runnable action) {
     return Commands.runOnce(action, this);
   }
 
@@ -97,7 +108,7 @@
    * @return the command
    * @see RunCommand
    */
-  default CommandBase run(Runnable action) {
+  default Command run(Runnable action) {
     return Commands.run(action, this);
   }
 
@@ -110,7 +121,7 @@
    * @return the command
    * @see StartEndCommand
    */
-  default CommandBase startEnd(Runnable start, Runnable end) {
+  default Command startEnd(Runnable start, Runnable end) {
     return Commands.startEnd(start, end, this);
   }
 
@@ -122,7 +133,19 @@
    * @param end the action to run on interrupt
    * @return the command
    */
-  default CommandBase runEnd(Runnable run, Runnable end) {
+  default Command runEnd(Runnable run, Runnable end) {
     return Commands.runEnd(run, end, this);
   }
+
+  /**
+   * Constructs a {@link DeferredCommand} with the provided supplier. This subsystem is added as a
+   * requirement.
+   *
+   * @param supplier the command supplier.
+   * @return the command.
+   * @see DeferredCommand
+   */
+  default Command defer(Supplier<Command> supplier) {
+    return Commands.defer(supplier, Set.of(this));
+  }
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommand.java
index 850699e..3eacac3 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommand.java
@@ -31,7 +31,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class SwerveControllerCommand extends CommandBase {
+public class SwerveControllerCommand extends Command {
   private final Timer m_timer = new Timer();
   private final Trajectory m_trajectory;
   private final Supplier<Pose2d> m_pose;
@@ -210,8 +210,7 @@
 
   @Override
   public void initialize() {
-    m_timer.reset();
-    m_timer.start();
+    m_timer.restart();
   }
 
   @Override
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileCommand.java
index 7c13973..57185a9 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileCommand.java
@@ -10,16 +10,19 @@
 import edu.wpi.first.math.trajectory.TrapezoidProfile;
 import edu.wpi.first.wpilibj.Timer;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 /**
  * A command that runs a {@link TrapezoidProfile}. Useful for smoothly controlling mechanism motion.
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class TrapezoidProfileCommand extends CommandBase {
+public class TrapezoidProfileCommand extends Command {
   private final TrapezoidProfile m_profile;
   private final Consumer<State> m_output;
-
+  private final Supplier<State> m_goal;
+  private final Supplier<State> m_currentState;
+  private final boolean m_newAPI; // TODO: Remove
   private final Timer m_timer = new Timer();
 
   /**
@@ -28,24 +31,58 @@
    *
    * @param profile The motion profile to execute.
    * @param output The consumer for the profile output.
+   * @param goal The supplier for the desired state
+   * @param currentState The current state
    * @param requirements The subsystems required by this command.
    */
   public TrapezoidProfileCommand(
+      TrapezoidProfile profile,
+      Consumer<State> output,
+      Supplier<State> goal,
+      Supplier<State> currentState,
+      Subsystem... requirements) {
+    m_profile = requireNonNullParam(profile, "profile", "TrapezoidProfileCommand");
+    m_output = requireNonNullParam(output, "output", "TrapezoidProfileCommand");
+    m_goal = goal;
+    m_currentState = currentState;
+    m_newAPI = true;
+    addRequirements(requirements);
+  }
+
+  /**
+   * Creates a new TrapezoidProfileCommand that will execute the given {@link TrapezoidProfile}.
+   * Output will be piped to the provided consumer function.
+   *
+   * @param profile The motion profile to execute.
+   * @param output The consumer for the profile output.
+   * @param requirements The subsystems required by this command.
+   * @deprecated The new constructor allows you to pass in a supplier for desired and current state.
+   *     This allows you to change goals at runtime.
+   */
+  @Deprecated(since = "2024", forRemoval = true)
+  public TrapezoidProfileCommand(
       TrapezoidProfile profile, Consumer<State> output, Subsystem... requirements) {
     m_profile = requireNonNullParam(profile, "profile", "TrapezoidProfileCommand");
     m_output = requireNonNullParam(output, "output", "TrapezoidProfileCommand");
+    m_newAPI = false;
+    m_goal = null;
+    m_currentState = null;
     addRequirements(requirements);
   }
 
   @Override
   public void initialize() {
-    m_timer.reset();
-    m_timer.start();
+    m_timer.restart();
   }
 
   @Override
+  @SuppressWarnings("removal")
   public void execute() {
-    m_output.accept(m_profile.calculate(m_timer.get()));
+    if (m_newAPI) {
+      m_output.accept(m_profile.calculate(m_timer.get(), m_goal.get(), m_currentState.get()));
+    } else {
+      m_output.accept(m_profile.calculate(m_timer.get()));
+    }
   }
 
   @Override
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java
index 1db6619..35c02e6 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java
@@ -16,7 +16,7 @@
  */
 public abstract class TrapezoidProfileSubsystem extends SubsystemBase {
   private final double m_period;
-  private final TrapezoidProfile.Constraints m_constraints;
+  private final TrapezoidProfile m_profile;
 
   private TrapezoidProfile.State m_state;
   private TrapezoidProfile.State m_goal;
@@ -33,7 +33,8 @@
    */
   public TrapezoidProfileSubsystem(
       TrapezoidProfile.Constraints constraints, double initialPosition, double period) {
-    m_constraints = requireNonNullParam(constraints, "constraints", "TrapezoidProfileSubsystem");
+    requireNonNullParam(constraints, "constraints", "TrapezoidProfileSubsystem");
+    m_profile = new TrapezoidProfile(constraints);
     m_state = new TrapezoidProfile.State(initialPosition, 0);
     setGoal(initialPosition);
     m_period = period;
@@ -62,8 +63,7 @@
 
   @Override
   public void periodic() {
-    var profile = new TrapezoidProfile(m_constraints, m_goal, m_state);
-    m_state = profile.calculate(m_period);
+    m_state = m_profile.calculate(m_period, m_goal, m_state);
     if (m_enabled) {
       useState(m_state);
     }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitCommand.java
index 647e87e..e7b32be 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitCommand.java
@@ -9,12 +9,11 @@
 import edu.wpi.first.wpilibj.Timer;
 
 /**
- * A command that does nothing but takes a specified amount of time to finish. Useful for
- * CommandGroups. Can also be subclassed to make a command with an internal timer.
+ * A command that does nothing but takes a specified amount of time to finish.
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class WaitCommand extends CommandBase {
+public class WaitCommand extends Command {
   protected Timer m_timer = new Timer();
   private final double m_duration;
 
@@ -30,8 +29,7 @@
 
   @Override
   public void initialize() {
-    m_timer.reset();
-    m_timer.start();
+    m_timer.restart();
   }
 
   @Override
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitUntilCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitUntilCommand.java
index 4d51e62..b96bc26 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitUntilCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitUntilCommand.java
@@ -15,7 +15,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-public class WaitUntilCommand extends CommandBase {
+public class WaitUntilCommand extends Command {
   private final BooleanSupplier m_condition;
 
   /**
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WrapperCommand.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WrapperCommand.java
index c48871c..c687f0a 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WrapperCommand.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WrapperCommand.java
@@ -14,7 +14,7 @@
  * added to any other composition or scheduled individually, and the composition requires all
  * subsystems its components require.
  */
-public abstract class WrapperCommand extends CommandBase {
+public abstract class WrapperCommand extends Command {
   protected final Command m_command;
 
   /**
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Button.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Button.java
deleted file mode 100644
index 4e4e11a..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Button.java
+++ /dev/null
@@ -1,169 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj2.command.button;
-
-import edu.wpi.first.wpilibj2.command.Command;
-import edu.wpi.first.wpilibj2.command.Subsystem;
-import java.util.function.BooleanSupplier;
-
-/**
- * This class provides an easy way to link commands to OI inputs.
- *
- * <p>It is very easy to link a button to a command. For instance, you could link the trigger button
- * of a joystick to a "score" command.
- *
- * <p>This class represents a subclass of Trigger that is specifically aimed at buttons on an
- * operator interface as a common use case of the more generalized Trigger objects. This is a simple
- * wrapper around Trigger with the method names renamed to fit the Button object use.
- *
- * @deprecated Replace with {@link Trigger}.
- */
-@Deprecated
-public class Button extends Trigger {
-  /**
-   * Default constructor; creates a button that is never pressed.
-   *
-   * @deprecated Replace with {@code new Button(() -> false) }.
-   */
-  @Deprecated(since = "2023")
-  public Button() {}
-
-  /**
-   * Creates a new button with the given condition determining whether it is pressed.
-   *
-   * @param isPressed returns whether the trigger should be active
-   * @deprecated Replace with Trigger.
-   */
-  @Deprecated
-  public Button(BooleanSupplier isPressed) {
-    super(isPressed);
-  }
-
-  /**
-   * Starts the given command whenever the button is newly pressed.
-   *
-   * @param command the command to start
-   * @return this button, so calls can be chained
-   * @deprecated Replace with {@link Trigger#onTrue(Command)}
-   */
-  @Deprecated
-  public Button whenPressed(final Command command) {
-    whenActive(command);
-    return this;
-  }
-
-  /**
-   * Runs the given runnable whenever the button is newly pressed.
-   *
-   * @param toRun the runnable to run
-   * @param requirements the required subsystems
-   * @return this button, so calls can be chained
-   * @deprecated Replace with {@link #onTrue(Command)}, creating the InstantCommand manually
-   */
-  @Deprecated
-  public Button whenPressed(final Runnable toRun, Subsystem... requirements) {
-    whenActive(toRun, requirements);
-    return this;
-  }
-
-  /**
-   * Constantly starts the given command while the button is held.
-   *
-   * <p>{@link Command#schedule()} will be called repeatedly while the button is held, and will be
-   * canceled when the button is released.
-   *
-   * @param command the command to start
-   * @return this button, so calls can be chained
-   * @deprecated Use {@link #whileTrue(Command)} with {@link
-   *     edu.wpi.first.wpilibj2.command.RepeatCommand RepeatCommand}.
-   */
-  @Deprecated
-  public Button whileHeld(final Command command) {
-    whileActiveContinuous(command);
-    return this;
-  }
-
-  /**
-   * Constantly runs the given runnable while the button is held.
-   *
-   * @param toRun the runnable to run
-   * @param requirements the required subsystems
-   * @return this button, so calls can be chained
-   * @deprecated Use {@link #whileTrue(Command)} and construct a RunCommand manually
-   */
-  @Deprecated
-  public Button whileHeld(final Runnable toRun, Subsystem... requirements) {
-    whileActiveContinuous(toRun, requirements);
-    return this;
-  }
-
-  /**
-   * Starts the given command when the button is first pressed, and cancels it when it is released,
-   * but does not start it again if it ends or is otherwise interrupted.
-   *
-   * @param command the command to start
-   * @return this button, so calls can be chained
-   * @deprecated Replace with {@link Trigger#whileTrue(Command)}
-   */
-  @Deprecated
-  public Button whenHeld(final Command command) {
-    whileActiveOnce(command);
-    return this;
-  }
-
-  /**
-   * Starts the command when the button is released. The command is set to be interruptible.
-   *
-   * @param command the command to start
-   * @return this button, so calls can be chained
-   * @deprecated Replace with {@link Trigger#onFalse(Command)}
-   */
-  @Deprecated
-  public Button whenReleased(final Command command) {
-    whenInactive(command);
-    return this;
-  }
-
-  /**
-   * Runs the given runnable when the button is released.
-   *
-   * @param toRun the runnable to run
-   * @param requirements the required subsystems
-   * @return this button, so calls can be chained
-   * @deprecated Replace with {@link Trigger#onFalse(Command)}, creating the InstantCommand manually
-   */
-  @Deprecated
-  public Button whenReleased(final Runnable toRun, Subsystem... requirements) {
-    whenInactive(toRun, requirements);
-    return this;
-  }
-
-  /**
-   * Toggles the command whenever the button is pressed (on, then off, then on). The command is set
-   * to be interruptible.
-   *
-   * @param command the command to start
-   * @return this button, so calls can be chained
-   * @deprecated Replace with {@link Trigger#toggleOnTrue(Command)}
-   */
-  @Deprecated
-  public Button toggleWhenPressed(final Command command) {
-    toggleWhenActive(command);
-    return this;
-  }
-
-  /**
-   * Cancels the command when the button is pressed.
-   *
-   * @param command the command to start
-   * @return this button, so calls can be chained
-   * @deprecated Instead, pass this as an end condition to {@link Command#until(BooleanSupplier)}.
-   */
-  @Deprecated
-  public Button cancelWhenPressed(final Command command) {
-    cancelWhenActive(command);
-    return this;
-  }
-}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandJoystick.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandJoystick.java
index 9f3d48a..f993609 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandJoystick.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandJoystick.java
@@ -9,7 +9,7 @@
 import edu.wpi.first.wpilibj2.command.CommandScheduler;
 
 /**
- * A subclass of {@link Joystick} with {@link Trigger} factories for command-based.
+ * A version of {@link Joystick} with {@link Trigger} factories for command-based.
  *
  * @see Joystick
  */
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandPS5Controller.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandPS5Controller.java
new file mode 100644
index 0000000..6b149d6
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandPS5Controller.java
@@ -0,0 +1,389 @@
+// 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.
+
+package edu.wpi.first.wpilibj2.command.button;
+
+import edu.wpi.first.wpilibj.PS5Controller;
+import edu.wpi.first.wpilibj.event.EventLoop;
+import edu.wpi.first.wpilibj2.command.CommandScheduler;
+
+/**
+ * A version of {@link PS5Controller} with {@link Trigger} factories for command-based.
+ *
+ * @see PS5Controller
+ */
+@SuppressWarnings("MethodName")
+public class CommandPS5Controller extends CommandGenericHID {
+  private final PS5Controller m_hid;
+
+  /**
+   * Construct an instance of a device.
+   *
+   * @param port The port index on the Driver Station that the device is plugged into.
+   */
+  public CommandPS5Controller(int port) {
+    super(port);
+    m_hid = new PS5Controller(port);
+  }
+
+  /**
+   * Get the underlying GenericHID object.
+   *
+   * @return the wrapped GenericHID object
+   */
+  @Override
+  public PS5Controller getHID() {
+    return m_hid;
+  }
+
+  /**
+   * Constructs an event instance around the L2 button's digital signal.
+   *
+   * @return an event instance representing the L2 button's digital signal attached to the {@link
+   *     CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger L2() {
+    return L2(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the L2 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L2 button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger L2(EventLoop loop) {
+    return m_hid.L2(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the R2 button's digital signal.
+   *
+   * @return an event instance representing the R2 button's digital signal attached to the {@link
+   *     CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger R2() {
+    return R2(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the R2 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R2 button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger R2(EventLoop loop) {
+    return m_hid.R2(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the L1 button's digital signal.
+   *
+   * @return an event instance representing the L1 button's digital signal attached to the {@link
+   *     CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger L1() {
+    return L1(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the L1 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L1 button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger L1(EventLoop loop) {
+    return m_hid.L1(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the R1 button's digital signal.
+   *
+   * @return an event instance representing the R1 button's digital signal attached to the {@link
+   *     CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger R1() {
+    return R1(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the R1 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R1 button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger R1(EventLoop loop) {
+    return m_hid.R1(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the L3 button's digital signal.
+   *
+   * @return an event instance representing the L3 button's digital signal attached to the {@link
+   *     CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger L3() {
+    return L3(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the L3 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L3 button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger L3(EventLoop loop) {
+    return m_hid.L3(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the R3 button's digital signal.
+   *
+   * @return an event instance representing the R3 button's digital signal attached to the {@link
+   *     CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger R3() {
+    return R3(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the R3 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R3 button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger R3(EventLoop loop) {
+    return m_hid.R3(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the square button's digital signal.
+   *
+   * @return an event instance representing the square button's digital signal attached to the
+   *     {@link CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger square() {
+    return square(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the square button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the square button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger square(EventLoop loop) {
+    return m_hid.square(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the cross button's digital signal.
+   *
+   * @return an event instance representing the cross button's digital signal attached to the {@link
+   *     CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger cross() {
+    return cross(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the cross button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the cross button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger cross(EventLoop loop) {
+    return m_hid.cross(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the triangle button's digital signal.
+   *
+   * @return an event instance representing the triangle button's digital signal attached to the
+   *     {@link CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger triangle() {
+    return triangle(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the triangle button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the triangle button's digital signal attached to the
+   *     given loop.
+   */
+  public Trigger triangle(EventLoop loop) {
+    return m_hid.triangle(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the circle button's digital signal.
+   *
+   * @return an event instance representing the circle button's digital signal attached to the
+   *     {@link CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger circle() {
+    return circle(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the circle button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the circle button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger circle(EventLoop loop) {
+    return m_hid.circle(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the create button's digital signal.
+   *
+   * @return an event instance representing the create button's digital signal attached to the
+   *     {@link CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger create() {
+    return create(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the create button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the create button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger create(EventLoop loop) {
+    return m_hid.create(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the PS button's digital signal.
+   *
+   * @return an event instance representing the PS button's digital signal attached to the {@link
+   *     CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger PS() {
+    return PS(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the PS button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the PS button's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger PS(EventLoop loop) {
+    return m_hid.PS(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the options button's digital signal.
+   *
+   * @return an event instance representing the options button's digital signal attached to the
+   *     {@link CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger options() {
+    return options(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the options button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the options button's digital signal attached to the
+   *     given loop.
+   */
+  public Trigger options(EventLoop loop) {
+    return m_hid.options(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Constructs an event instance around the touchpad's digital signal.
+   *
+   * @return an event instance representing the touchpad's digital signal attached to the {@link
+   *     CommandScheduler#getDefaultButtonLoop() default scheduler button loop}.
+   */
+  public Trigger touchpad() {
+    return touchpad(CommandScheduler.getInstance().getDefaultButtonLoop());
+  }
+
+  /**
+   * Constructs an event instance around the touchpad's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the touchpad's digital signal attached to the given
+   *     loop.
+   */
+  public Trigger touchpad(EventLoop loop) {
+    return m_hid.touchpad(loop).castTo(Trigger::new);
+  }
+
+  /**
+   * Get the X axis value of left side of the controller.
+   *
+   * @return the axis value.
+   */
+  public double getLeftX() {
+    return m_hid.getLeftX();
+  }
+
+  /**
+   * Get the X axis value of right side of the controller.
+   *
+   * @return the axis value.
+   */
+  public double getRightX() {
+    return m_hid.getRightX();
+  }
+
+  /**
+   * Get the Y axis value of left side of the controller.
+   *
+   * @return the axis value.
+   */
+  public double getLeftY() {
+    return m_hid.getLeftY();
+  }
+
+  /**
+   * Get the Y axis value of right side of the controller.
+   *
+   * @return the axis value.
+   */
+  public double getRightY() {
+    return m_hid.getRightY();
+  }
+
+  /**
+   * Get the L2 axis value of the controller. Note that this axis is bound to the range of [0, 1] as
+   * opposed to the usual [-1, 1].
+   *
+   * @return the axis value.
+   */
+  public double getL2Axis() {
+    return m_hid.getL2Axis();
+  }
+
+  /**
+   * Get the R2 axis value of the controller. Note that this axis is bound to the range of [0, 1] as
+   * opposed to the usual [-1, 1].
+   *
+   * @return the axis value.
+   */
+  public double getR2Axis() {
+    return m_hid.getR2Axis();
+  }
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandXboxController.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandXboxController.java
index 1e83e83..6969c98 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandXboxController.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/CommandXboxController.java
@@ -9,7 +9,7 @@
 import edu.wpi.first.wpilibj2.command.CommandScheduler;
 
 /**
- * A subclass of {@link XboxController} with {@link Trigger} factories for command-based.
+ * A version of {@link XboxController} with {@link Trigger} factories for command-based.
  *
  * @see XboxController
  */
@@ -261,13 +261,13 @@
    * Constructs a Trigger instance around the axis value of the left trigger. The returned trigger
    * will be true when the axis value is greater than {@code threshold}.
    *
-   * @param loop the event loop instance to attach the Trigger to.
    * @param threshold the minimum axis value for the returned {@link Trigger} to be true. This value
    *     should be in the range [0, 1] where 0 is the unpressed state of the axis.
+   * @param loop the event loop instance to attach the Trigger to.
    * @return a Trigger instance that is true when the left trigger's axis exceeds the provided
    *     threshold, attached to the given event loop
    */
-  public Trigger leftTrigger(EventLoop loop, double threshold) {
+  public Trigger leftTrigger(double threshold, EventLoop loop) {
     return m_hid.leftTrigger(threshold, loop).castTo(Trigger::new);
   }
 
@@ -282,7 +282,7 @@
    *     button loop}.
    */
   public Trigger leftTrigger(double threshold) {
-    return leftTrigger(CommandScheduler.getInstance().getDefaultButtonLoop(), threshold);
+    return leftTrigger(threshold, CommandScheduler.getInstance().getDefaultButtonLoop());
   }
 
   /**
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/InternalButton.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/InternalButton.java
index 3106264..f4897f2 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/InternalButton.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/InternalButton.java
@@ -12,8 +12,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-@SuppressWarnings("deprecation")
-public class InternalButton extends Button {
+public class InternalButton extends Trigger {
   // need to be references, so they can be mutated after being captured in the constructor.
   private final AtomicBoolean m_pressed;
   private final AtomicBoolean m_inverted;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/JoystickButton.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/JoystickButton.java
index f22d443..e85b666 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/JoystickButton.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/JoystickButton.java
@@ -9,12 +9,11 @@
 import edu.wpi.first.wpilibj.GenericHID;
 
 /**
- * A {@link Button} that gets its state from a {@link GenericHID}.
+ * A {@link Trigger} that gets its state from a {@link GenericHID}.
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-@SuppressWarnings("deprecation")
-public class JoystickButton extends Button {
+public class JoystickButton extends Trigger {
   /**
    * Creates a joystick button for triggering commands.
    *
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/NetworkButton.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/NetworkButton.java
index b21cd97..0fb5571 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/NetworkButton.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/NetworkButton.java
@@ -12,12 +12,11 @@
 import edu.wpi.first.networktables.NetworkTableInstance;
 
 /**
- * A {@link Button} that uses a {@link NetworkTable} boolean field.
+ * A {@link Trigger} that uses a {@link NetworkTable} boolean field.
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-@SuppressWarnings("deprecation")
-public class NetworkButton extends Button {
+public class NetworkButton extends Trigger {
   /**
    * Creates a NetworkButton that commands can be bound to.
    *
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/POVButton.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/POVButton.java
index 28087ae..b8a63e4 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/POVButton.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/POVButton.java
@@ -9,12 +9,11 @@
 import edu.wpi.first.wpilibj.GenericHID;
 
 /**
- * A {@link Button} that gets its state from a POV on a {@link GenericHID}.
+ * A {@link Trigger} that gets its state from a POV on a {@link GenericHID}.
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-@SuppressWarnings("deprecation")
-public class POVButton extends Button {
+public class POVButton extends Trigger {
   /**
    * Creates a POV button for triggering commands.
    *
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java
index 19b8b20..5bdee37 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java
@@ -7,12 +7,9 @@
 import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
 
 import edu.wpi.first.math.filter.Debouncer;
-import edu.wpi.first.wpilibj.event.BooleanEvent;
 import edu.wpi.first.wpilibj.event.EventLoop;
 import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.CommandScheduler;
-import edu.wpi.first.wpilibj2.command.InstantCommand;
-import edu.wpi.first.wpilibj2.command.Subsystem;
 import java.util.function.BooleanSupplier;
 
 /**
@@ -52,12 +49,6 @@
     this(CommandScheduler.getInstance().getDefaultButtonLoop(), condition);
   }
 
-  /** Creates a new trigger that is always `false`. */
-  @Deprecated
-  public Trigger() {
-    this(() -> false);
-  }
-
   /**
    * Starts the given command whenever the condition changes from `false` to `true`.
    *
@@ -65,7 +56,7 @@
    * @return this trigger, so calls can be chained
    */
   public Trigger onTrue(Command command) {
-    requireNonNullParam(command, "command", "onRising");
+    requireNonNullParam(command, "command", "onTrue");
     m_loop.bind(
         new Runnable() {
           private boolean m_pressedLast = m_condition.getAsBoolean();
@@ -91,7 +82,7 @@
    * @return this trigger, so calls can be chained
    */
   public Trigger onFalse(Command command) {
-    requireNonNullParam(command, "command", "onFalling");
+    requireNonNullParam(command, "command", "onFalse");
     m_loop.bind(
         new Runnable() {
           private boolean m_pressedLast = m_condition.getAsBoolean();
@@ -121,7 +112,7 @@
    * @return this trigger, so calls can be chained
    */
   public Trigger whileTrue(Command command) {
-    requireNonNullParam(command, "command", "whileHigh");
+    requireNonNullParam(command, "command", "whileTrue");
     m_loop.bind(
         new Runnable() {
           private boolean m_pressedLast = m_condition.getAsBoolean();
@@ -153,7 +144,7 @@
    * @return this trigger, so calls can be chained
    */
   public Trigger whileFalse(Command command) {
-    requireNonNullParam(command, "command", "whileLow");
+    requireNonNullParam(command, "command", "whileFalse");
     m_loop.bind(
         new Runnable() {
           private boolean m_pressedLast = m_condition.getAsBoolean();
@@ -181,7 +172,7 @@
    * @return this trigger, so calls can be chained
    */
   public Trigger toggleOnTrue(Command command) {
-    requireNonNullParam(command, "command", "toggleOnRising");
+    requireNonNullParam(command, "command", "toggleOnTrue");
     m_loop.bind(
         new Runnable() {
           private boolean m_pressedLast = m_condition.getAsBoolean();
@@ -205,13 +196,13 @@
   }
 
   /**
-   * Toggles a command when the condition changes from `true` to the low state.
+   * Toggles a command when the condition changes from `true` to `false`.
    *
    * @param command the command to toggle
    * @return this trigger, so calls can be chained
    */
   public Trigger toggleOnFalse(Command command) {
-    requireNonNullParam(command, "command", "toggleOnFalling");
+    requireNonNullParam(command, "command", "toggleOnFalse");
     m_loop.bind(
         new Runnable() {
           private boolean m_pressedLast = m_condition.getAsBoolean();
@@ -234,238 +225,6 @@
     return this;
   }
 
-  /**
-   * Starts the given command whenever the trigger just becomes active.
-   *
-   * @param command the command to start
-   * @return this trigger, so calls can be chained
-   * @deprecated Use {@link #onTrue(Command)} instead.
-   */
-  @Deprecated
-  public Trigger whenActive(final Command command) {
-    requireNonNullParam(command, "command", "whenActive");
-
-    m_loop.bind(
-        new Runnable() {
-          private boolean m_pressedLast = m_condition.getAsBoolean();
-
-          @Override
-          public void run() {
-            boolean pressed = m_condition.getAsBoolean();
-
-            if (!m_pressedLast && pressed) {
-              command.schedule();
-            }
-
-            m_pressedLast = pressed;
-          }
-        });
-    return this;
-  }
-
-  /**
-   * Runs the given runnable whenever the trigger just becomes active.
-   *
-   * @param toRun the runnable to run
-   * @param requirements the required subsystems
-   * @return this trigger, so calls can be chained
-   * @deprecated Replace with {@link #onTrue(Command)}, creating the InstantCommand manually
-   */
-  @Deprecated
-  public Trigger whenActive(final Runnable toRun, Subsystem... requirements) {
-    return whenActive(new InstantCommand(toRun, requirements));
-  }
-
-  /**
-   * Constantly starts the given command while the button is held.
-   *
-   * <p>{@link Command#schedule()} will be called repeatedly while the trigger is active, and will
-   * be canceled when the trigger becomes inactive.
-   *
-   * @param command the command to start
-   * @return this trigger, so calls can be chained
-   * @deprecated Use {@link #whileTrue(Command)} with {@link
-   *     edu.wpi.first.wpilibj2.command.RepeatCommand RepeatCommand}, or bind {@link
-   *     Command#schedule() command::schedule} to {@link BooleanEvent#ifHigh(Runnable)} (passing no
-   *     requirements).
-   */
-  @Deprecated
-  public Trigger whileActiveContinuous(final Command command) {
-    requireNonNullParam(command, "command", "whileActiveContinuous");
-
-    m_loop.bind(
-        new Runnable() {
-          private boolean m_pressedLast = m_condition.getAsBoolean();
-
-          @Override
-          public void run() {
-            boolean pressed = m_condition.getAsBoolean();
-
-            if (pressed) {
-              command.schedule();
-            } else if (m_pressedLast) {
-              command.cancel();
-            }
-
-            m_pressedLast = pressed;
-          }
-        });
-
-    return this;
-  }
-
-  /**
-   * Constantly runs the given runnable while the button is held.
-   *
-   * @param toRun the runnable to run
-   * @param requirements the required subsystems
-   * @return this trigger, so calls can be chained
-   * @deprecated Use {@link #whileTrue(Command)} and construct a RunCommand manually
-   */
-  @Deprecated
-  public Trigger whileActiveContinuous(final Runnable toRun, Subsystem... requirements) {
-    return whileActiveContinuous(new InstantCommand(toRun, requirements));
-  }
-
-  /**
-   * Starts the given command when the trigger initially becomes active, and ends it when it becomes
-   * inactive, but does not re-start it in-between.
-   *
-   * @param command the command to start
-   * @return this trigger, so calls can be chained
-   * @deprecated Use {@link #whileTrue(Command)} instead.
-   */
-  @Deprecated
-  public Trigger whileActiveOnce(final Command command) {
-    requireNonNullParam(command, "command", "whileActiveOnce");
-
-    m_loop.bind(
-        new Runnable() {
-          private boolean m_pressedLast = m_condition.getAsBoolean();
-
-          @Override
-          public void run() {
-            boolean pressed = m_condition.getAsBoolean();
-
-            if (!m_pressedLast && pressed) {
-              command.schedule();
-            } else if (m_pressedLast && !pressed) {
-              command.cancel();
-            }
-
-            m_pressedLast = pressed;
-          }
-        });
-    return this;
-  }
-
-  /**
-   * Starts the command when the trigger becomes inactive.
-   *
-   * @param command the command to start
-   * @return this trigger, so calls can be chained
-   * @deprecated Use {@link #onFalse(Command)} instead.
-   */
-  @Deprecated
-  public Trigger whenInactive(final Command command) {
-    requireNonNullParam(command, "command", "whenInactive");
-
-    m_loop.bind(
-        new Runnable() {
-          private boolean m_pressedLast = m_condition.getAsBoolean();
-
-          @Override
-          public void run() {
-            boolean pressed = m_condition.getAsBoolean();
-
-            if (m_pressedLast && !pressed) {
-              command.schedule();
-            }
-
-            m_pressedLast = pressed;
-          }
-        });
-
-    return this;
-  }
-
-  /**
-   * Runs the given runnable when the trigger becomes inactive.
-   *
-   * @param toRun the runnable to run
-   * @param requirements the required subsystems
-   * @return this trigger, so calls can be chained
-   * @deprecated Construct the InstantCommand manually and replace with {@link #onFalse(Command)}
-   */
-  @Deprecated
-  public Trigger whenInactive(final Runnable toRun, Subsystem... requirements) {
-    return whenInactive(new InstantCommand(toRun, requirements));
-  }
-
-  /**
-   * Toggles a command when the trigger becomes active.
-   *
-   * @param command the command to toggle
-   * @return this trigger, so calls can be chained
-   * @deprecated Use {@link #toggleOnTrue(Command)} instead.
-   */
-  @Deprecated
-  public Trigger toggleWhenActive(final Command command) {
-    requireNonNullParam(command, "command", "toggleWhenActive");
-
-    m_loop.bind(
-        new Runnable() {
-          private boolean m_pressedLast = m_condition.getAsBoolean();
-
-          @Override
-          public void run() {
-            boolean pressed = m_condition.getAsBoolean();
-
-            if (!m_pressedLast && pressed) {
-              if (command.isScheduled()) {
-                command.cancel();
-              } else {
-                command.schedule();
-              }
-            }
-
-            m_pressedLast = pressed;
-          }
-        });
-
-    return this;
-  }
-
-  /**
-   * Cancels a command when the trigger becomes active.
-   *
-   * @param command the command to cancel
-   * @return this trigger, so calls can be chained
-   * @deprecated Instead, pass this as an end condition to {@link Command#until(BooleanSupplier)}.
-   */
-  @Deprecated
-  public Trigger cancelWhenActive(final Command command) {
-    requireNonNullParam(command, "command", "cancelWhenActive");
-
-    m_loop.bind(
-        new Runnable() {
-          private boolean m_pressedLast = m_condition.getAsBoolean();
-
-          @Override
-          public void run() {
-            boolean pressed = m_condition.getAsBoolean();
-
-            if (!m_pressedLast && pressed) {
-              command.cancel();
-            }
-
-            m_pressedLast = pressed;
-          }
-        });
-
-    return this;
-  }
-
   @Override
   public boolean getAsBoolean() {
     return m_condition.getAsBoolean();
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp
index b173ada..66de555 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp
@@ -4,6 +4,9 @@
 
 #include "frc2/command/Command.h"
 
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
+
 #include "frc2/command/CommandHelper.h"
 #include "frc2/command/CommandScheduler.h"
 #include "frc2/command/ConditionalCommand.h"
@@ -11,7 +14,6 @@
 #include "frc2/command/ParallelCommandGroup.h"
 #include "frc2/command/ParallelDeadlineGroup.h"
 #include "frc2/command/ParallelRaceGroup.h"
-#include "frc2/command/PerpetualCommand.h"
 #include "frc2/command/RepeatCommand.h"
 #include "frc2/command/SequentialCommandGroup.h"
 #include "frc2/command/WaitCommand.h"
@@ -20,6 +22,10 @@
 
 using namespace frc2;
 
+Command::Command() {
+  wpi::SendableRegistry::Add(this, GetTypeName(*this));
+}
+
 Command::~Command() {
   CommandScheduler::GetInstance().Cancel(this);
 }
@@ -33,6 +39,38 @@
 void Command::Execute() {}
 void Command::End(bool interrupted) {}
 
+wpi::SmallSet<Subsystem*, 4> Command::GetRequirements() const {
+  return m_requirements;
+}
+
+void Command::AddRequirements(Requirements requirements) {
+  m_requirements.insert(requirements.begin(), requirements.end());
+}
+
+void Command::AddRequirements(wpi::SmallSet<Subsystem*, 4> requirements) {
+  m_requirements.insert(requirements.begin(), requirements.end());
+}
+
+void Command::AddRequirements(Subsystem* requirement) {
+  m_requirements.insert(requirement);
+}
+
+void Command::SetName(std::string_view name) {
+  wpi::SendableRegistry::SetName(this, name);
+}
+
+std::string Command::GetName() const {
+  return wpi::SendableRegistry::GetName(this);
+}
+
+std::string Command::GetSubsystem() const {
+  return wpi::SendableRegistry::GetSubsystem(this);
+}
+
+void Command::SetSubsystem(std::string_view subsystem) {
+  wpi::SendableRegistry::SetSubsystem(this, subsystem);
+}
+
 CommandPtr Command::WithTimeout(units::second_t duration) && {
   return std::move(*this).ToPtr().WithTimeout(duration);
 }
@@ -41,6 +79,10 @@
   return std::move(*this).ToPtr().Until(std::move(condition));
 }
 
+CommandPtr Command::OnlyWhile(std::function<bool()> condition) && {
+  return std::move(*this).ToPtr().OnlyWhile(std::move(condition));
+}
+
 CommandPtr Command::IgnoringDisable(bool doesRunWhenDisabled) && {
   return std::move(*this).ToPtr().IgnoringDisable(doesRunWhenDisabled);
 }
@@ -50,40 +92,17 @@
   return std::move(*this).ToPtr().WithInterruptBehavior(interruptBehavior);
 }
 
-CommandPtr Command::WithInterrupt(std::function<bool()> condition) && {
-  return std::move(*this).ToPtr().Until(std::move(condition));
-}
-
-CommandPtr Command::BeforeStarting(
-    std::function<void()> toRun,
-    std::initializer_list<Subsystem*> requirements) && {
-  return std::move(*this).BeforeStarting(
-      std::move(toRun), {requirements.begin(), requirements.end()});
-}
-
-CommandPtr Command::BeforeStarting(
-    std::function<void()> toRun, std::span<Subsystem* const> requirements) && {
+CommandPtr Command::BeforeStarting(std::function<void()> toRun,
+                                   Requirements requirements) && {
   return std::move(*this).ToPtr().BeforeStarting(std::move(toRun),
                                                  requirements);
 }
 
 CommandPtr Command::AndThen(std::function<void()> toRun,
-                            std::initializer_list<Subsystem*> requirements) && {
-  return std::move(*this).AndThen(std::move(toRun),
-                                  {requirements.begin(), requirements.end()});
-}
-
-CommandPtr Command::AndThen(std::function<void()> toRun,
-                            std::span<Subsystem* const> requirements) && {
+                            Requirements requirements) && {
   return std::move(*this).ToPtr().AndThen(std::move(toRun), requirements);
 }
 
-PerpetualCommand Command::Perpetually() && {
-  WPI_IGNORE_DEPRECATED
-  return PerpetualCommand(std::move(*this).TransferOwnership());
-  WPI_UNIGNORE_DEPRECATED
-}
-
 CommandPtr Command::Repeatedly() && {
   return std::move(*this).ToPtr().Repeatedly();
 }
@@ -96,11 +115,19 @@
   return std::move(*this).ToPtr().Unless(std::move(condition));
 }
 
+CommandPtr Command::OnlyIf(std::function<bool()> condition) && {
+  return std::move(*this).ToPtr().OnlyIf(std::move(condition));
+}
+
 CommandPtr Command::FinallyDo(std::function<void(bool)> end) && {
   return std::move(*this).ToPtr().FinallyDo(std::move(end));
 }
 
-CommandPtr Command::HandleInterrupt(std::function<void(void)> handler) && {
+CommandPtr Command::FinallyDo(std::function<void()> end) && {
+  return std::move(*this).ToPtr().FinallyDo(std::move(end));
+}
+
+CommandPtr Command::HandleInterrupt(std::function<void()> handler) && {
   return std::move(*this).ToPtr().HandleInterrupt(std::move(handler));
 }
 
@@ -128,12 +155,6 @@
   return hasRequirement;
 }
 
-std::string Command::GetName() const {
-  return GetTypeName(*this);
-}
-
-void Command::SetName(std::string_view name) {}
-
 bool Command::IsComposed() const {
   return m_isComposed;
 }
@@ -142,12 +163,37 @@
   m_isComposed = isComposed;
 }
 
-bool Command::IsGrouped() const {
-  return IsComposed();
-}
-
-void Command::SetGrouped(bool grouped) {
-  SetComposed(grouped);
+void Command::InitSendable(wpi::SendableBuilder& builder) {
+  builder.SetSmartDashboardType("Command");
+  builder.AddStringProperty(
+      ".name", [this] { return GetName(); }, nullptr);
+  builder.AddBooleanProperty(
+      "running", [this] { return IsScheduled(); },
+      [this](bool value) {
+        bool isScheduled = IsScheduled();
+        if (value && !isScheduled) {
+          Schedule();
+        } else if (!value && isScheduled) {
+          Cancel();
+        }
+      });
+  builder.AddBooleanProperty(
+      ".isParented", [this] { return IsComposed(); }, nullptr);
+  builder.AddStringProperty(
+      "interruptBehavior",
+      [this] {
+        switch (GetInterruptionBehavior()) {
+          case Command::InterruptionBehavior::kCancelIncoming:
+            return "kCancelIncoming";
+          case Command::InterruptionBehavior::kCancelSelf:
+            return "kCancelSelf";
+          default:
+            return "Invalid";
+        }
+      },
+      nullptr);
+  builder.AddBooleanProperty(
+      "runsWhenDisabled", [this] { return RunsWhenDisabled(); }, nullptr);
 }
 
 namespace frc2 {
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandBase.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandBase.cpp
index 416c78b..7e2fad1 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandBase.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandBase.cpp
@@ -3,82 +3,3 @@
 // the WPILib BSD license file in the root directory of this project.
 
 #include "frc2/command/CommandBase.h"
-
-#include <wpi/sendable/SendableBuilder.h>
-#include <wpi/sendable/SendableRegistry.h>
-
-using namespace frc2;
-
-CommandBase::CommandBase() {
-  wpi::SendableRegistry::Add(this, GetTypeName(*this));
-}
-
-void CommandBase::AddRequirements(
-    std::initializer_list<Subsystem*> requirements) {
-  m_requirements.insert(requirements.begin(), requirements.end());
-}
-
-void CommandBase::AddRequirements(std::span<Subsystem* const> requirements) {
-  m_requirements.insert(requirements.begin(), requirements.end());
-}
-
-void CommandBase::AddRequirements(wpi::SmallSet<Subsystem*, 4> requirements) {
-  m_requirements.insert(requirements.begin(), requirements.end());
-}
-
-void CommandBase::AddRequirements(Subsystem* requirement) {
-  m_requirements.insert(requirement);
-}
-
-wpi::SmallSet<Subsystem*, 4> CommandBase::GetRequirements() const {
-  return m_requirements;
-}
-
-void CommandBase::SetName(std::string_view name) {
-  wpi::SendableRegistry::SetName(this, name);
-}
-
-std::string CommandBase::GetName() const {
-  return wpi::SendableRegistry::GetName(this);
-}
-
-std::string CommandBase::GetSubsystem() const {
-  return wpi::SendableRegistry::GetSubsystem(this);
-}
-
-void CommandBase::SetSubsystem(std::string_view subsystem) {
-  wpi::SendableRegistry::SetSubsystem(this, subsystem);
-}
-
-void CommandBase::InitSendable(wpi::SendableBuilder& builder) {
-  builder.SetSmartDashboardType("Command");
-  builder.AddStringProperty(
-      ".name", [this] { return GetName(); }, nullptr);
-  builder.AddBooleanProperty(
-      "running", [this] { return IsScheduled(); },
-      [this](bool value) {
-        bool isScheduled = IsScheduled();
-        if (value && !isScheduled) {
-          Schedule();
-        } else if (!value && isScheduled) {
-          Cancel();
-        }
-      });
-  builder.AddBooleanProperty(
-      ".isParented", [this] { return IsComposed(); }, nullptr);
-  builder.AddStringProperty(
-      "interruptBehavior",
-      [this] {
-        switch (GetInterruptionBehavior()) {
-          case Command::InterruptionBehavior::kCancelIncoming:
-            return "kCancelIncoming";
-          case Command::InterruptionBehavior::kCancelSelf:
-            return "kCancelSelf";
-          default:
-            return "Invalid";
-        }
-      },
-      nullptr);
-  builder.AddBooleanProperty(
-      "runsWhenDisabled", [this] { return RunsWhenDisabled(); }, nullptr);
-}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandGroupBase.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandGroupBase.cpp
deleted file mode 100644
index eb9c293..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandGroupBase.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-// 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 "frc2/command/CommandGroupBase.h"
-
-using namespace frc2;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandPtr.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandPtr.cpp
index 44150a2..6f7e418 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandPtr.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandPtr.cpp
@@ -86,15 +86,7 @@
 }
 
 CommandPtr CommandPtr::AndThen(std::function<void()> toRun,
-                               std::span<Subsystem* const> requirements) && {
-  AssertValid();
-  return std::move(*this).AndThen(CommandPtr(
-      std::make_unique<InstantCommand>(std::move(toRun), requirements)));
-}
-
-CommandPtr CommandPtr::AndThen(
-    std::function<void()> toRun,
-    std::initializer_list<Subsystem*> requirements) && {
+                               Requirements requirements) && {
   AssertValid();
   return std::move(*this).AndThen(CommandPtr(
       std::make_unique<InstantCommand>(std::move(toRun), requirements)));
@@ -109,16 +101,8 @@
   return std::move(*this);
 }
 
-CommandPtr CommandPtr::BeforeStarting(
-    std::function<void()> toRun, std::span<Subsystem* const> requirements) && {
-  AssertValid();
-  return std::move(*this).BeforeStarting(CommandPtr(
-      std::make_unique<InstantCommand>(std::move(toRun), requirements)));
-}
-
-CommandPtr CommandPtr::BeforeStarting(
-    std::function<void()> toRun,
-    std::initializer_list<Subsystem*> requirements) && {
+CommandPtr CommandPtr::BeforeStarting(std::function<void()> toRun,
+                                      Requirements requirements) && {
   AssertValid();
   return std::move(*this).BeforeStarting(CommandPtr(
       std::make_unique<InstantCommand>(std::move(toRun), requirements)));
@@ -151,6 +135,11 @@
   return std::move(*this);
 }
 
+CommandPtr CommandPtr::OnlyWhile(std::function<bool()> condition) && {
+  AssertValid();
+  return std::move(*this).Until(std::not_fn(std::move(condition)));
+}
+
 CommandPtr CommandPtr::Unless(std::function<bool()> condition) && {
   AssertValid();
   m_ptr = std::make_unique<ConditionalCommand>(
@@ -159,6 +148,11 @@
   return std::move(*this);
 }
 
+CommandPtr CommandPtr::OnlyIf(std::function<bool()> condition) && {
+  AssertValid();
+  return std::move(*this).Unless(std::not_fn(std::move(condition)));
+}
+
 CommandPtr CommandPtr::DeadlineWith(CommandPtr&& parallel) && {
   AssertValid();
   std::vector<std::unique_ptr<Command>> vec;
@@ -209,7 +203,13 @@
   return std::move(*this);
 }
 
-CommandPtr CommandPtr::HandleInterrupt(std::function<void(void)> handler) && {
+CommandPtr CommandPtr::FinallyDo(std::function<void()> end) && {
+  AssertValid();
+  return std::move(*this).FinallyDo(
+      [endHandler = std::move(end)](bool interrupted) { endHandler(); });
+}
+
+CommandPtr CommandPtr::HandleInterrupt(std::function<void()> handler) && {
   AssertValid();
   return std::move(*this).FinallyDo(
       [handler = std::move(handler)](bool interrupted) {
@@ -226,37 +226,37 @@
   return std::move(wrapper).ToPtr();
 }
 
-CommandBase* CommandPtr::get() const {
+Command* CommandPtr::get() const& {
   AssertValid();
   return m_ptr.get();
 }
 
-std::unique_ptr<CommandBase> CommandPtr::Unwrap() && {
+std::unique_ptr<Command> CommandPtr::Unwrap() && {
   AssertValid();
   return std::move(m_ptr);
 }
 
-void CommandPtr::Schedule() const {
+void CommandPtr::Schedule() const& {
   AssertValid();
   CommandScheduler::GetInstance().Schedule(*this);
 }
 
-void CommandPtr::Cancel() const {
+void CommandPtr::Cancel() const& {
   AssertValid();
   CommandScheduler::GetInstance().Cancel(*this);
 }
 
-bool CommandPtr::IsScheduled() const {
+bool CommandPtr::IsScheduled() const& {
   AssertValid();
   return CommandScheduler::GetInstance().IsScheduled(*this);
 }
 
-bool CommandPtr::HasRequirement(Subsystem* requirement) const {
+bool CommandPtr::HasRequirement(Subsystem* requirement) const& {
   AssertValid();
   return m_ptr->HasRequirement(requirement);
 }
 
-CommandPtr::operator bool() const {
+CommandPtr::operator bool() const& {
   return m_ptr.operator bool();
 }
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp
index 5a4782f..d61ffbd 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp
@@ -13,10 +13,10 @@
 #include <hal/FRCUsageReporting.h>
 #include <hal/HALBase.h>
 #include <networktables/IntegerArrayTopic.h>
-#include <networktables/NTSendableBuilder.h>
 #include <networktables/StringArrayTopic.h>
 #include <wpi/DenseMap.h>
 #include <wpi/SmallVector.h>
+#include <wpi/sendable/SendableBuilder.h>
 #include <wpi/sendable/SendableRegistry.h>
 
 #include "frc2/command/CommandPtr.h"
@@ -48,15 +48,16 @@
   // every command.
   wpi::SmallVector<Action, 4> initActions;
   wpi::SmallVector<Action, 4> executeActions;
-  wpi::SmallVector<Action, 4> interruptActions;
+  wpi::SmallVector<InterruptAction, 4> interruptActions;
   wpi::SmallVector<Action, 4> finishActions;
 
   // Flag and queues for avoiding concurrent modification if commands are
   // scheduled/canceled during run
-
   bool inRunLoop = false;
   wpi::SmallVector<Command*, 4> toSchedule;
-  wpi::SmallVector<Command*, 4> toCancel;
+  wpi::SmallVector<Command*, 4> toCancelCommands;
+  wpi::SmallVector<std::optional<Command*>, 4> toCancelInterruptors;
+  wpi::SmallSet<Command*, 4> endingCommands;
 };
 
 template <typename TMap, typename TKey>
@@ -107,10 +108,6 @@
   return &(m_impl->defaultButtonLoop);
 }
 
-void CommandScheduler::ClearButtons() {
-  m_impl->activeButtonLoop->Clear();
-}
-
 void CommandScheduler::Schedule(Command* command) {
   if (m_impl->inRunLoop) {
     m_impl->toSchedule.emplace_back(command);
@@ -142,7 +139,7 @@
   if (isDisjoint || allInterruptible) {
     if (allInterruptible) {
       for (auto&& cmdToCancel : intersection) {
-        Cancel(cmdToCancel);
+        Cancel(cmdToCancel, std::make_optional(command));
       }
     }
     m_impl->scheduledCommands.insert(command);
@@ -197,10 +194,11 @@
   m_watchdog.AddEpoch("buttons.Run()");
 
   m_impl->inRunLoop = true;
+  bool isDisabled = frc::RobotState::IsDisabled();
   // Run scheduled commands, remove finished commands.
   for (Command* command : m_impl->scheduledCommands) {
-    if (!command->RunsWhenDisabled() && frc::RobotState::IsDisabled()) {
-      Cancel(command);
+    if (isDisabled && !command->RunsWhenDisabled()) {
+      Cancel(command, std::nullopt);
       continue;
     }
 
@@ -211,16 +209,18 @@
     m_watchdog.AddEpoch(command->GetName() + ".Execute()");
 
     if (command->IsFinished()) {
+      m_impl->endingCommands.insert(command);
       command->End(false);
       for (auto&& action : m_impl->finishActions) {
         action(*command);
       }
+      m_impl->endingCommands.erase(command);
 
+      m_impl->scheduledCommands.erase(command);
       for (auto&& requirement : command->GetRequirements()) {
         m_impl->requirements.erase(requirement);
       }
 
-      m_impl->scheduledCommands.erase(command);
       m_watchdog.AddEpoch(command->GetName() + ".End(false)");
     }
   }
@@ -230,12 +230,13 @@
     Schedule(command);
   }
 
-  for (auto&& command : m_impl->toCancel) {
-    Cancel(command);
+  for (size_t i = 0; i < m_impl->toCancelCommands.size(); i++) {
+    Cancel(m_impl->toCancelCommands[i], m_impl->toCancelInterruptors[i]);
   }
 
   m_impl->toSchedule.clear();
-  m_impl->toCancel.clear();
+  m_impl->toCancelCommands.clear();
+  m_impl->toCancelInterruptors.clear();
 
   // Add default commands for un-required registered subsystems.
   for (auto&& subsystem : m_impl->subsystems) {
@@ -295,6 +296,10 @@
   }
 }
 
+void CommandScheduler::UnregisterAllSubsystems() {
+  m_impl->subsystems.clear();
+}
+
 void CommandScheduler::SetDefaultCommand(Subsystem* subsystem,
                                          CommandPtr&& defaultCommand) {
   if (!defaultCommand.get()->HasRequirement(subsystem)) {
@@ -319,33 +324,41 @@
   }
 }
 
-void CommandScheduler::Cancel(Command* command) {
+void CommandScheduler::Cancel(Command* command,
+                              std::optional<Command*> interruptor) {
   if (!m_impl) {
     return;
   }
-
+  if (m_impl->endingCommands.contains(command)) {
+    return;
+  }
   if (m_impl->inRunLoop) {
-    m_impl->toCancel.emplace_back(command);
+    m_impl->toCancelCommands.emplace_back(command);
+    m_impl->toCancelInterruptors.emplace_back(interruptor);
     return;
   }
-
-  auto find = m_impl->scheduledCommands.find(command);
-  if (find == m_impl->scheduledCommands.end()) {
+  if (!IsScheduled(command)) {
     return;
   }
-  m_impl->scheduledCommands.erase(*find);
+  m_impl->endingCommands.insert(command);
+  command->End(true);
+  for (auto&& action : m_impl->interruptActions) {
+    action(*command, interruptor);
+  }
+  m_impl->endingCommands.erase(command);
+  m_impl->scheduledCommands.erase(command);
   for (auto&& requirement : m_impl->requirements) {
     if (requirement.second == command) {
       m_impl->requirements.erase(requirement.first);
     }
   }
-  command->End(true);
-  for (auto&& action : m_impl->interruptActions) {
-    action(*command);
-  }
   m_watchdog.AddEpoch(command->GetName() + ".End(true)");
 }
 
+void CommandScheduler::Cancel(Command* command) {
+  Cancel(command, std::nullopt);
+}
+
 void CommandScheduler::Cancel(const CommandPtr& command) {
   Cancel(command.get());
 }
@@ -424,6 +437,14 @@
 }
 
 void CommandScheduler::OnCommandInterrupt(Action action) {
+  m_impl->interruptActions.emplace_back(
+      [action = std::move(action)](const Command& command,
+                                   const std::optional<Command*>& interruptor) {
+        action(command);
+      });
+}
+
+void CommandScheduler::OnCommandInterrupt(InterruptAction action) {
   m_impl->interruptActions.emplace_back(std::move(action));
 }
 
@@ -433,9 +454,10 @@
 
 void CommandScheduler::RequireUngrouped(const Command* command) {
   if (command->IsComposed()) {
-    throw FRC_MakeError(
-        frc::err::CommandIllegalUse,
-        "Commands cannot be added to more than one CommandGroup");
+    throw FRC_MakeError(frc::err::CommandIllegalUse,
+                        "Commands that have been composed may not be added to "
+                        "another composition or scheduled "
+                        "individually!");
   }
 }
 
@@ -453,36 +475,40 @@
   }
 }
 
-void CommandScheduler::InitSendable(nt::NTSendableBuilder& builder) {
+void CommandScheduler::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Scheduler");
-  builder.SetUpdateTable(
-      [this,
-       namesPub = nt::StringArrayTopic{builder.GetTopic("Names")}.Publish(),
-       idsPub = nt::IntegerArrayTopic{builder.GetTopic("Ids")}.Publish(),
-       cancelEntry = nt::IntegerArrayTopic{builder.GetTopic("Cancel")}.GetEntry(
-           {})]() mutable {
-        auto toCancel = cancelEntry.Get();
-        if (!toCancel.empty()) {
-          for (auto cancel : cancelEntry.Get()) {
-            uintptr_t ptrTmp = static_cast<uintptr_t>(cancel);
-            Command* command = reinterpret_cast<Command*>(ptrTmp);
-            if (m_impl->scheduledCommands.find(command) !=
-                m_impl->scheduledCommands.end()) {
-              Cancel(command);
-            }
-          }
-          cancelEntry.Set({});
-        }
-
-        wpi::SmallVector<std::string, 8> names;
-        wpi::SmallVector<int64_t, 8> ids;
+  builder.AddStringArrayProperty(
+      "Names",
+      [this]() mutable {
+        std::vector<std::string> names;
         for (Command* command : m_impl->scheduledCommands) {
           names.emplace_back(command->GetName());
+        }
+        return names;
+      },
+      nullptr);
+  builder.AddIntegerArrayProperty(
+      "Ids",
+      [this]() mutable {
+        std::vector<int64_t> ids;
+        for (Command* command : m_impl->scheduledCommands) {
           uintptr_t ptrTmp = reinterpret_cast<uintptr_t>(command);
           ids.emplace_back(static_cast<int64_t>(ptrTmp));
         }
-        namesPub.Set(names);
-        idsPub.Set(ids);
+        return ids;
+      },
+      nullptr);
+  builder.AddIntegerArrayProperty(
+      "Cancel", []() { return std::vector<int64_t>{}; },
+      [this](std::span<const int64_t> toCancel) mutable {
+        for (auto cancel : toCancel) {
+          uintptr_t ptrTmp = static_cast<uintptr_t>(cancel);
+          Command* command = reinterpret_cast<Command*>(ptrTmp);
+          if (m_impl->scheduledCommands.find(command) !=
+              m_impl->scheduledCommands.end()) {
+            Cancel(command);
+          }
+        }
       });
 }
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Commands.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Commands.cpp
index 1932e45..31a4b1f 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Commands.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Commands.cpp
@@ -5,12 +5,14 @@
 #include "frc2/command/Commands.h"
 
 #include "frc2/command/ConditionalCommand.h"
+#include "frc2/command/DeferredCommand.h"
 #include "frc2/command/FunctionalCommand.h"
 #include "frc2/command/InstantCommand.h"
 #include "frc2/command/ParallelCommandGroup.h"
 #include "frc2/command/ParallelDeadlineGroup.h"
 #include "frc2/command/ParallelRaceGroup.h"
 #include "frc2/command/PrintCommand.h"
+#include "frc2/command/ProxyCommand.h"
 #include "frc2/command/RunCommand.h"
 #include "frc2/command/SequentialCommandGroup.h"
 #include "frc2/command/WaitCommand.h"
@@ -24,37 +26,21 @@
   return InstantCommand().ToPtr();
 }
 
-CommandPtr cmd::RunOnce(std::function<void()> action,
-                        std::initializer_list<Subsystem*> requirements) {
-  return InstantCommand(std::move(action), requirements).ToPtr();
+CommandPtr cmd::Idle(Requirements requirements) {
+  return Run([] {}, requirements);
 }
 
 CommandPtr cmd::RunOnce(std::function<void()> action,
-                        std::span<Subsystem* const> requirements) {
+                        Requirements requirements) {
   return InstantCommand(std::move(action), requirements).ToPtr();
 }
 
-CommandPtr cmd::Run(std::function<void()> action,
-                    std::initializer_list<Subsystem*> requirements) {
-  return RunCommand(std::move(action), requirements).ToPtr();
-}
-
-CommandPtr cmd::Run(std::function<void()> action,
-                    std::span<Subsystem* const> requirements) {
+CommandPtr cmd::Run(std::function<void()> action, Requirements requirements) {
   return RunCommand(std::move(action), requirements).ToPtr();
 }
 
 CommandPtr cmd::StartEnd(std::function<void()> start, std::function<void()> end,
-                         std::initializer_list<Subsystem*> requirements) {
-  return FunctionalCommand(
-             std::move(start), [] {},
-             [end = std::move(end)](bool interrupted) { end(); },
-             [] { return false; }, requirements)
-      .ToPtr();
-}
-
-CommandPtr cmd::StartEnd(std::function<void()> start, std::function<void()> end,
-                         std::span<Subsystem* const> requirements) {
+                         Requirements requirements) {
   return FunctionalCommand(
              std::move(start), [] {},
              [end = std::move(end)](bool interrupted) { end(); },
@@ -63,15 +49,7 @@
 }
 
 CommandPtr cmd::RunEnd(std::function<void()> run, std::function<void()> end,
-                       std::initializer_list<Subsystem*> requirements) {
-  return FunctionalCommand([] {}, std::move(run),
-                           [end = std::move(end)](bool interrupted) { end(); },
-                           [] { return false; }, requirements)
-      .ToPtr();
-}
-
-CommandPtr cmd::RunEnd(std::function<void()> run, std::function<void()> end,
-                       std::span<Subsystem* const> requirements) {
+                       Requirements requirements) {
   return FunctionalCommand([] {}, std::move(run),
                            [end = std::move(end)](bool interrupted) { end(); },
                            [] { return false; }, requirements)
@@ -82,6 +60,14 @@
   return PrintCommand(msg).ToPtr();
 }
 
+CommandPtr cmd::DeferredProxy(wpi::unique_function<Command*()> supplier) {
+  return ProxyCommand(std::move(supplier)).ToPtr();
+}
+
+CommandPtr cmd::DeferredProxy(wpi::unique_function<CommandPtr()> supplier) {
+  return ProxyCommand(std::move(supplier)).ToPtr();
+}
+
 CommandPtr cmd::Wait(units::second_t duration) {
   return WaitCommand(duration).ToPtr();
 }
@@ -97,6 +83,11 @@
       .ToPtr();
 }
 
+CommandPtr cmd::Defer(wpi::unique_function<CommandPtr()> supplier,
+                      Requirements requirements) {
+  return DeferredCommand(std::move(supplier), requirements).ToPtr();
+}
+
 CommandPtr cmd::Sequence(std::vector<CommandPtr>&& commands) {
   return SequentialCommandGroup(CommandPtr::UnwrapVector(std::move(commands)))
       .ToPtr();
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ConditionalCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ConditionalCommand.cpp
index 2610c96..4c07c7a 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ConditionalCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ConditionalCommand.cpp
@@ -53,8 +53,20 @@
   return m_runsWhenDisabled;
 }
 
+Command::InterruptionBehavior ConditionalCommand::GetInterruptionBehavior()
+    const {
+  if (m_onTrue->GetInterruptionBehavior() ==
+          InterruptionBehavior::kCancelSelf ||
+      m_onFalse->GetInterruptionBehavior() ==
+          InterruptionBehavior::kCancelSelf) {
+    return InterruptionBehavior::kCancelSelf;
+  } else {
+    return InterruptionBehavior::kCancelIncoming;
+  }
+}
+
 void ConditionalCommand::InitSendable(wpi::SendableBuilder& builder) {
-  CommandBase::InitSendable(builder);
+  Command::InitSendable(builder);
   builder.AddStringProperty(
       "onTrue", [this] { return m_onTrue->GetName(); }, nullptr);
   builder.AddStringProperty(
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/DeferredCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/DeferredCommand.cpp
new file mode 100644
index 0000000..2a7f162
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/DeferredCommand.cpp
@@ -0,0 +1,46 @@
+// 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 "frc2/command/DeferredCommand.h"
+
+#include <wpi/sendable/SendableBuilder.h>
+
+#include "frc2/command/Commands.h"
+
+using namespace frc2;
+
+DeferredCommand::DeferredCommand(wpi::unique_function<CommandPtr()> supplier,
+                                 Requirements requirements)
+    : m_supplier{std::move(supplier)} {
+  AddRequirements(requirements);
+}
+
+void DeferredCommand::Initialize() {
+  m_command = m_supplier().Unwrap();
+  CommandScheduler::GetInstance().RequireUngrouped(m_command.get());
+  m_command->SetComposed(true);
+  m_command->Initialize();
+}
+
+void DeferredCommand::Execute() {
+  m_command->Execute();
+}
+
+void DeferredCommand::End(bool interrupted) {
+  m_command->End(interrupted);
+  m_command =
+      cmd::Print("[DeferredCommand] Lifecycle function called out-of-order!")
+          .WithName("none")
+          .Unwrap();
+}
+
+bool DeferredCommand::IsFinished() {
+  return m_command->IsFinished();
+}
+
+void DeferredCommand::InitSendable(wpi::SendableBuilder& builder) {
+  Command::InitSendable(builder);
+  builder.AddStringProperty(
+      "deferred", [this] { return m_command->GetName(); }, nullptr);
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/FunctionalCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/FunctionalCommand.cpp
index 02e8338..e4ff6ea 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/FunctionalCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/FunctionalCommand.cpp
@@ -6,22 +6,11 @@
 
 using namespace frc2;
 
-FunctionalCommand::FunctionalCommand(
-    std::function<void()> onInit, std::function<void()> onExecute,
-    std::function<void(bool)> onEnd, std::function<bool()> isFinished,
-    std::initializer_list<Subsystem*> requirements)
-    : m_onInit{std::move(onInit)},
-      m_onExecute{std::move(onExecute)},
-      m_onEnd{std::move(onEnd)},
-      m_isFinished{std::move(isFinished)} {
-  AddRequirements(requirements);
-}
-
 FunctionalCommand::FunctionalCommand(std::function<void()> onInit,
                                      std::function<void()> onExecute,
                                      std::function<void(bool)> onEnd,
                                      std::function<bool()> isFinished,
-                                     std::span<Subsystem* const> requirements)
+                                     Requirements requirements)
     : m_onInit{std::move(onInit)},
       m_onExecute{std::move(onExecute)},
       m_onEnd{std::move(onEnd)},
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/InstantCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/InstantCommand.cpp
index 7b73239..b78d30e 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/InstantCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/InstantCommand.cpp
@@ -7,13 +7,7 @@
 using namespace frc2;
 
 InstantCommand::InstantCommand(std::function<void()> toRun,
-                               std::initializer_list<Subsystem*> requirements)
-    : CommandHelper(
-          std::move(toRun), [] {}, [](bool interrupted) {}, [] { return true; },
-          requirements) {}
-
-InstantCommand::InstantCommand(std::function<void()> toRun,
-                               std::span<Subsystem* const> requirements)
+                               Requirements requirements)
     : CommandHelper(
           std::move(toRun), [] {}, [](bool interrupted) {}, [] { return true; },
           requirements) {}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/MecanumControllerCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/MecanumControllerCommand.cpp
index d95d596..902c26b 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/MecanumControllerCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/MecanumControllerCommand.cpp
@@ -11,20 +11,20 @@
 MecanumControllerCommand::MecanumControllerCommand(
     frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
     frc::SimpleMotorFeedforward<units::meters> feedforward,
-    frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-    frc2::PIDController yController,
+    frc::MecanumDriveKinematics kinematics, frc::PIDController xController,
+    frc::PIDController yController,
     frc::ProfiledPIDController<units::radians> thetaController,
     std::function<frc::Rotation2d()> desiredRotation,
     units::meters_per_second_t maxWheelVelocity,
     std::function<frc::MecanumDriveWheelSpeeds()> currentWheelSpeeds,
-    frc2::PIDController frontLeftController,
-    frc2::PIDController rearLeftController,
-    frc2::PIDController frontRightController,
-    frc2::PIDController rearRightController,
+    frc::PIDController frontLeftController,
+    frc::PIDController rearLeftController,
+    frc::PIDController frontRightController,
+    frc::PIDController rearRightController,
     std::function<void(units::volt_t, units::volt_t, units::volt_t,
                        units::volt_t)>
         output,
-    std::initializer_list<Subsystem*> requirements)
+    Requirements requirements)
     : m_trajectory(std::move(trajectory)),
       m_pose(std::move(pose)),
       m_feedforward(feedforward),
@@ -33,13 +33,13 @@
       m_desiredRotation(std::move(desiredRotation)),
       m_maxWheelVelocity(maxWheelVelocity),
       m_frontLeftController(
-          std::make_unique<frc2::PIDController>(frontLeftController)),
+          std::make_unique<frc::PIDController>(frontLeftController)),
       m_rearLeftController(
-          std::make_unique<frc2::PIDController>(rearLeftController)),
+          std::make_unique<frc::PIDController>(rearLeftController)),
       m_frontRightController(
-          std::make_unique<frc2::PIDController>(frontRightController)),
+          std::make_unique<frc::PIDController>(frontRightController)),
       m_rearRightController(
-          std::make_unique<frc2::PIDController>(rearRightController)),
+          std::make_unique<frc::PIDController>(rearRightController)),
       m_currentWheelSpeeds(std::move(currentWheelSpeeds)),
       m_outputVolts(std::move(output)),
       m_usePID(true) {
@@ -49,19 +49,19 @@
 MecanumControllerCommand::MecanumControllerCommand(
     frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
     frc::SimpleMotorFeedforward<units::meters> feedforward,
-    frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-    frc2::PIDController yController,
+    frc::MecanumDriveKinematics kinematics, frc::PIDController xController,
+    frc::PIDController yController,
     frc::ProfiledPIDController<units::radians> thetaController,
     units::meters_per_second_t maxWheelVelocity,
     std::function<frc::MecanumDriveWheelSpeeds()> currentWheelSpeeds,
-    frc2::PIDController frontLeftController,
-    frc2::PIDController rearLeftController,
-    frc2::PIDController frontRightController,
-    frc2::PIDController rearRightController,
+    frc::PIDController frontLeftController,
+    frc::PIDController rearLeftController,
+    frc::PIDController frontRightController,
+    frc::PIDController rearRightController,
     std::function<void(units::volt_t, units::volt_t, units::volt_t,
                        units::volt_t)>
         output,
-    std::initializer_list<Subsystem*> requirements)
+    Requirements requirements)
     : m_trajectory(std::move(trajectory)),
       m_pose(std::move(pose)),
       m_feedforward(feedforward),
@@ -69,13 +69,13 @@
       m_controller(xController, yController, thetaController),
       m_maxWheelVelocity(maxWheelVelocity),
       m_frontLeftController(
-          std::make_unique<frc2::PIDController>(frontLeftController)),
+          std::make_unique<frc::PIDController>(frontLeftController)),
       m_rearLeftController(
-          std::make_unique<frc2::PIDController>(rearLeftController)),
+          std::make_unique<frc::PIDController>(rearLeftController)),
       m_frontRightController(
-          std::make_unique<frc2::PIDController>(frontRightController)),
+          std::make_unique<frc::PIDController>(frontRightController)),
       m_rearRightController(
-          std::make_unique<frc2::PIDController>(rearRightController)),
+          std::make_unique<frc::PIDController>(rearRightController)),
       m_currentWheelSpeeds(std::move(currentWheelSpeeds)),
       m_outputVolts(std::move(output)),
       m_usePID(true) {
@@ -84,89 +84,15 @@
 
 MecanumControllerCommand::MecanumControllerCommand(
     frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::SimpleMotorFeedforward<units::meters> feedforward,
-    frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-    frc2::PIDController yController,
-    frc::ProfiledPIDController<units::radians> thetaController,
-    std::function<frc::Rotation2d()> desiredRotation,
-    units::meters_per_second_t maxWheelVelocity,
-    std::function<frc::MecanumDriveWheelSpeeds()> currentWheelSpeeds,
-    frc2::PIDController frontLeftController,
-    frc2::PIDController rearLeftController,
-    frc2::PIDController frontRightController,
-    frc2::PIDController rearRightController,
-    std::function<void(units::volt_t, units::volt_t, units::volt_t,
-                       units::volt_t)>
-        output,
-    std::span<Subsystem* const> requirements)
-    : m_trajectory(std::move(trajectory)),
-      m_pose(std::move(pose)),
-      m_feedforward(feedforward),
-      m_kinematics(kinematics),
-      m_controller(xController, yController, thetaController),
-      m_desiredRotation(std::move(desiredRotation)),
-      m_maxWheelVelocity(maxWheelVelocity),
-      m_frontLeftController(
-          std::make_unique<frc2::PIDController>(frontLeftController)),
-      m_rearLeftController(
-          std::make_unique<frc2::PIDController>(rearLeftController)),
-      m_frontRightController(
-          std::make_unique<frc2::PIDController>(frontRightController)),
-      m_rearRightController(
-          std::make_unique<frc2::PIDController>(rearRightController)),
-      m_currentWheelSpeeds(std::move(currentWheelSpeeds)),
-      m_outputVolts(std::move(output)),
-      m_usePID(true) {
-  AddRequirements(requirements);
-}
-
-MecanumControllerCommand::MecanumControllerCommand(
-    frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::SimpleMotorFeedforward<units::meters> feedforward,
-    frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-    frc2::PIDController yController,
-    frc::ProfiledPIDController<units::radians> thetaController,
-    units::meters_per_second_t maxWheelVelocity,
-    std::function<frc::MecanumDriveWheelSpeeds()> currentWheelSpeeds,
-    frc2::PIDController frontLeftController,
-    frc2::PIDController rearLeftController,
-    frc2::PIDController frontRightController,
-    frc2::PIDController rearRightController,
-    std::function<void(units::volt_t, units::volt_t, units::volt_t,
-                       units::volt_t)>
-        output,
-    std::span<Subsystem* const> requirements)
-    : m_trajectory(std::move(trajectory)),
-      m_pose(std::move(pose)),
-      m_feedforward(feedforward),
-      m_kinematics(kinematics),
-      m_controller(xController, yController, thetaController),
-      m_maxWheelVelocity(maxWheelVelocity),
-      m_frontLeftController(
-          std::make_unique<frc2::PIDController>(frontLeftController)),
-      m_rearLeftController(
-          std::make_unique<frc2::PIDController>(rearLeftController)),
-      m_frontRightController(
-          std::make_unique<frc2::PIDController>(frontRightController)),
-      m_rearRightController(
-          std::make_unique<frc2::PIDController>(rearRightController)),
-      m_currentWheelSpeeds(std::move(currentWheelSpeeds)),
-      m_outputVolts(std::move(output)),
-      m_usePID(true) {
-  AddRequirements(requirements);
-}
-
-MecanumControllerCommand::MecanumControllerCommand(
-    frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-    frc2::PIDController yController,
+    frc::MecanumDriveKinematics kinematics, frc::PIDController xController,
+    frc::PIDController yController,
     frc::ProfiledPIDController<units::radians> thetaController,
     std::function<frc::Rotation2d()> desiredRotation,
     units::meters_per_second_t maxWheelVelocity,
     std::function<void(units::meters_per_second_t, units::meters_per_second_t,
                        units::meters_per_second_t, units::meters_per_second_t)>
         output,
-    std::initializer_list<Subsystem*> requirements)
+    Requirements requirements)
     : m_trajectory(std::move(trajectory)),
       m_pose(std::move(pose)),
       m_kinematics(kinematics),
@@ -180,56 +106,14 @@
 
 MecanumControllerCommand::MecanumControllerCommand(
     frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-    frc2::PIDController yController,
+    frc::MecanumDriveKinematics kinematics, frc::PIDController xController,
+    frc::PIDController yController,
     frc::ProfiledPIDController<units::radians> thetaController,
     units::meters_per_second_t maxWheelVelocity,
     std::function<void(units::meters_per_second_t, units::meters_per_second_t,
                        units::meters_per_second_t, units::meters_per_second_t)>
         output,
-    std::initializer_list<Subsystem*> requirements)
-    : m_trajectory(std::move(trajectory)),
-      m_pose(std::move(pose)),
-      m_kinematics(kinematics),
-      m_controller(xController, yController, thetaController),
-      m_maxWheelVelocity(maxWheelVelocity),
-      m_outputVel(std::move(output)),
-      m_usePID(false) {
-  AddRequirements(requirements);
-}
-
-MecanumControllerCommand::MecanumControllerCommand(
-    frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-    frc2::PIDController yController,
-    frc::ProfiledPIDController<units::radians> thetaController,
-    std::function<frc::Rotation2d()> desiredRotation,
-    units::meters_per_second_t maxWheelVelocity,
-    std::function<void(units::meters_per_second_t, units::meters_per_second_t,
-                       units::meters_per_second_t, units::meters_per_second_t)>
-        output,
-    std::span<Subsystem* const> requirements)
-    : m_trajectory(std::move(trajectory)),
-      m_pose(std::move(pose)),
-      m_kinematics(kinematics),
-      m_controller(xController, yController, thetaController),
-      m_desiredRotation(std::move(desiredRotation)),
-      m_maxWheelVelocity(maxWheelVelocity),
-      m_outputVel(std::move(output)),
-      m_usePID(false) {
-  AddRequirements(requirements);
-}
-
-MecanumControllerCommand::MecanumControllerCommand(
-    frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-    frc2::PIDController yController,
-    frc::ProfiledPIDController<units::radians> thetaController,
-    units::meters_per_second_t maxWheelVelocity,
-    std::function<void(units::meters_per_second_t, units::meters_per_second_t,
-                       units::meters_per_second_t, units::meters_per_second_t)>
-        output,
-    std::span<Subsystem* const> requirements)
+    Requirements requirements)
     : m_trajectory(std::move(trajectory)),
       m_pose(std::move(pose)),
       m_kinematics(kinematics),
@@ -257,8 +141,7 @@
   m_prevSpeeds = m_kinematics.ToWheelSpeeds(
       frc::ChassisSpeeds{initialXVelocity, initialYVelocity, 0_rad_per_s});
 
-  m_timer.Reset();
-  m_timer.Start();
+  m_timer.Restart();
   if (m_usePID) {
     m_frontLeftController->Reset();
     m_rearLeftController->Reset();
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/NotifierCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/NotifierCommand.cpp
index 2081d07..5dc5add 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/NotifierCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/NotifierCommand.cpp
@@ -8,14 +8,7 @@
 
 NotifierCommand::NotifierCommand(std::function<void()> toRun,
                                  units::second_t period,
-                                 std::initializer_list<Subsystem*> requirements)
-    : m_toRun(toRun), m_notifier{std::move(toRun)}, m_period{period} {
-  AddRequirements(requirements);
-}
-
-NotifierCommand::NotifierCommand(std::function<void()> toRun,
-                                 units::second_t period,
-                                 std::span<Subsystem* const> requirements)
+                                 Requirements requirements)
     : m_toRun(toRun), m_notifier{std::move(toRun)}, m_period{period} {
   AddRequirements(requirements);
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PIDCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PIDCommand.cpp
index 672fa47..361ec84 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PIDCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PIDCommand.cpp
@@ -8,11 +8,11 @@
 
 using namespace frc2;
 
-PIDCommand::PIDCommand(PIDController controller,
+PIDCommand::PIDCommand(frc::PIDController controller,
                        std::function<double()> measurementSource,
                        std::function<double()> setpointSource,
                        std::function<void(double)> useOutput,
-                       std::initializer_list<Subsystem*> requirements)
+                       Requirements requirements)
     : m_controller{std::move(controller)},
       m_measurement{std::move(measurementSource)},
       m_setpoint{std::move(setpointSource)},
@@ -20,30 +20,10 @@
   AddRequirements(requirements);
 }
 
-PIDCommand::PIDCommand(PIDController controller,
-                       std::function<double()> measurementSource,
-                       std::function<double()> setpointSource,
-                       std::function<void(double)> useOutput,
-                       std::span<Subsystem* const> requirements)
-    : m_controller{std::move(controller)},
-      m_measurement{std::move(measurementSource)},
-      m_setpoint{std::move(setpointSource)},
-      m_useOutput{std::move(useOutput)} {
-  AddRequirements(requirements);
-}
-
-PIDCommand::PIDCommand(PIDController controller,
+PIDCommand::PIDCommand(frc::PIDController controller,
                        std::function<double()> measurementSource,
                        double setpoint, std::function<void(double)> useOutput,
-                       std::initializer_list<Subsystem*> requirements)
-    : PIDCommand(
-          controller, measurementSource, [setpoint] { return setpoint; },
-          useOutput, requirements) {}
-
-PIDCommand::PIDCommand(PIDController controller,
-                       std::function<double()> measurementSource,
-                       double setpoint, std::function<void(double)> useOutput,
-                       std::span<Subsystem* const> requirements)
+                       Requirements requirements)
     : PIDCommand(
           controller, measurementSource, [setpoint] { return setpoint; },
           useOutput, requirements) {}
@@ -60,6 +40,6 @@
   m_useOutput(0);
 }
 
-PIDController& PIDCommand::GetController() {
+frc::PIDController& PIDCommand::GetController() {
   return m_controller;
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PIDSubsystem.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PIDSubsystem.cpp
index 8db032a..0764cb9 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PIDSubsystem.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PIDSubsystem.cpp
@@ -8,7 +8,8 @@
 
 using namespace frc2;
 
-PIDSubsystem::PIDSubsystem(PIDController controller, double initialPosition)
+PIDSubsystem::PIDSubsystem(frc::PIDController controller,
+                           double initialPosition)
     : m_controller{std::move(controller)} {
   SetSetpoint(initialPosition);
   AddChild("PID Controller", &m_controller);
@@ -42,6 +43,6 @@
   return m_enabled;
 }
 
-PIDController& PIDSubsystem::GetController() {
+frc::PIDController& PIDSubsystem::GetController() {
   return m_controller;
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelDeadlineGroup.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelDeadlineGroup.cpp
index 14c28c3..a6b5c1c 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelDeadlineGroup.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelDeadlineGroup.cpp
@@ -97,7 +97,7 @@
 }
 
 void ParallelDeadlineGroup::InitSendable(wpi::SendableBuilder& builder) {
-  CommandBase::InitSendable(builder);
+  Command::InitSendable(builder);
 
   builder.AddStringProperty(
       "deadline", [this] { return m_deadline->GetName(); }, nullptr);
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PerpetualCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PerpetualCommand.cpp
deleted file mode 100644
index 2d0af1e..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/PerpetualCommand.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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 "frc2/command/PerpetualCommand.h"
-
-using namespace frc2;
-
-PerpetualCommand::PerpetualCommand(std::unique_ptr<Command>&& command) {
-  CommandScheduler::GetInstance().RequireUngrouped(command.get());
-  m_command = std::move(command);
-  m_command->SetComposed(true);
-  AddRequirements(m_command->GetRequirements());
-}
-
-void PerpetualCommand::Initialize() {
-  m_command->Initialize();
-}
-
-void PerpetualCommand::Execute() {
-  m_command->Execute();
-}
-
-void PerpetualCommand::End(bool interrupted) {
-  m_command->End(interrupted);
-}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ProxyCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ProxyCommand.cpp
index cc1b815..eb4fb7c 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ProxyCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ProxyCommand.cpp
@@ -11,13 +11,22 @@
 ProxyCommand::ProxyCommand(wpi::unique_function<Command*()> supplier)
     : m_supplier(std::move(supplier)) {}
 
+ProxyCommand::ProxyCommand(wpi::unique_function<CommandPtr()> supplier)
+    : ProxyCommand([supplier = std::move(supplier),
+                    holder = std::optional<CommandPtr>{}]() mutable {
+        holder = supplier();
+        return holder->get();
+      }) {}
+
 ProxyCommand::ProxyCommand(Command* command)
-    : m_supplier([command] { return command; }) {
+    : ProxyCommand([command] { return command; }) {
   SetName(std::string{"Proxy("}.append(command->GetName()).append(")"));
 }
 
-ProxyCommand::ProxyCommand(std::unique_ptr<Command> command)
-    : m_supplier([command = std::move(command)] { return command.get(); }) {}
+ProxyCommand::ProxyCommand(std::unique_ptr<Command> command) {
+  SetName(std::string{"Proxy("}.append(command->GetName()).append(")"));
+  m_supplier = [command = std::move(command)] { return command.get(); };
+}
 
 void ProxyCommand::Initialize() {
   m_command = m_supplier();
@@ -31,8 +40,6 @@
   m_command = nullptr;
 }
 
-void ProxyCommand::Execute() {}
-
 bool ProxyCommand::IsFinished() {
   // because we're between `initialize` and `end`, `m_command` is necessarily
   // not null but if called otherwise and m_command is null, it's UB, so we can
@@ -41,7 +48,7 @@
 }
 
 void ProxyCommand::InitSendable(wpi::SendableBuilder& builder) {
-  CommandBase::InitSendable(builder);
+  Command::InitSendable(builder);
   builder.AddStringProperty(
       "proxied",
       [this] {
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ProxyScheduleCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ProxyScheduleCommand.cpp
deleted file mode 100644
index dd0e2a7..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ProxyScheduleCommand.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 "frc2/command/ProxyScheduleCommand.h"
-
-using namespace frc2;
-
-ProxyScheduleCommand::ProxyScheduleCommand(
-    std::span<Command* const> toSchedule) {
-  SetInsert(m_toSchedule, toSchedule);
-}
-
-ProxyScheduleCommand::ProxyScheduleCommand(Command* toSchedule) {
-  SetInsert(m_toSchedule, {&toSchedule, 1});
-}
-
-void ProxyScheduleCommand::Initialize() {
-  for (auto* command : m_toSchedule) {
-    command->Schedule();
-  }
-}
-
-void ProxyScheduleCommand::End(bool interrupted) {
-  if (interrupted) {
-    for (auto* command : m_toSchedule) {
-      command->Cancel();
-    }
-  }
-}
-
-void ProxyScheduleCommand::Execute() {
-  m_finished = true;
-  for (auto* command : m_toSchedule) {
-    m_finished &= !command->IsScheduled();
-  }
-}
-
-bool ProxyScheduleCommand::IsFinished() {
-  return m_finished;
-}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RamseteCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RamseteCommand.cpp
index d46cdd0..9145985 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RamseteCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RamseteCommand.cpp
@@ -16,39 +16,17 @@
     frc::SimpleMotorFeedforward<units::meters> feedforward,
     frc::DifferentialDriveKinematics kinematics,
     std::function<frc::DifferentialDriveWheelSpeeds()> wheelSpeeds,
-    frc2::PIDController leftController, frc2::PIDController rightController,
+    frc::PIDController leftController, frc::PIDController rightController,
     std::function<void(units::volt_t, units::volt_t)> output,
-    std::initializer_list<Subsystem*> requirements)
+    Requirements requirements)
     : m_trajectory(std::move(trajectory)),
       m_pose(std::move(pose)),
       m_controller(controller),
       m_feedforward(feedforward),
-      m_kinematics(kinematics),
+      m_kinematics(std::move(kinematics)),
       m_speeds(std::move(wheelSpeeds)),
-      m_leftController(std::make_unique<frc2::PIDController>(leftController)),
-      m_rightController(std::make_unique<frc2::PIDController>(rightController)),
-      m_outputVolts(std::move(output)),
-      m_usePID(true) {
-  AddRequirements(requirements);
-}
-
-RamseteCommand::RamseteCommand(
-    frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::RamseteController controller,
-    frc::SimpleMotorFeedforward<units::meters> feedforward,
-    frc::DifferentialDriveKinematics kinematics,
-    std::function<frc::DifferentialDriveWheelSpeeds()> wheelSpeeds,
-    frc2::PIDController leftController, frc2::PIDController rightController,
-    std::function<void(units::volt_t, units::volt_t)> output,
-    std::span<Subsystem* const> requirements)
-    : m_trajectory(std::move(trajectory)),
-      m_pose(std::move(pose)),
-      m_controller(controller),
-      m_feedforward(feedforward),
-      m_kinematics(kinematics),
-      m_speeds(std::move(wheelSpeeds)),
-      m_leftController(std::make_unique<frc2::PIDController>(leftController)),
-      m_rightController(std::make_unique<frc2::PIDController>(rightController)),
+      m_leftController(std::make_unique<frc::PIDController>(leftController)),
+      m_rightController(std::make_unique<frc::PIDController>(rightController)),
       m_outputVolts(std::move(output)),
       m_usePID(true) {
   AddRequirements(requirements);
@@ -60,27 +38,11 @@
     frc::DifferentialDriveKinematics kinematics,
     std::function<void(units::meters_per_second_t, units::meters_per_second_t)>
         output,
-    std::initializer_list<Subsystem*> requirements)
+    Requirements requirements)
     : m_trajectory(std::move(trajectory)),
       m_pose(std::move(pose)),
       m_controller(controller),
-      m_kinematics(kinematics),
-      m_outputVel(std::move(output)),
-      m_usePID(false) {
-  AddRequirements(requirements);
-}
-
-RamseteCommand::RamseteCommand(
-    frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::RamseteController controller,
-    frc::DifferentialDriveKinematics kinematics,
-    std::function<void(units::meters_per_second_t, units::meters_per_second_t)>
-        output,
-    std::span<Subsystem* const> requirements)
-    : m_trajectory(std::move(trajectory)),
-      m_pose(std::move(pose)),
-      m_controller(controller),
-      m_kinematics(kinematics),
+      m_kinematics(std::move(kinematics)),
       m_outputVel(std::move(output)),
       m_usePID(false) {
   AddRequirements(requirements);
@@ -92,8 +54,7 @@
   m_prevSpeeds = m_kinematics.ToWheelSpeeds(
       frc::ChassisSpeeds{initialState.velocity, 0_mps,
                          initialState.velocity * initialState.curvature});
-  m_timer.Reset();
-  m_timer.Start();
+  m_timer.Restart();
   if (m_usePID) {
     m_leftController->Reset();
     m_rightController->Reset();
@@ -162,7 +123,7 @@
 }
 
 void RamseteCommand::InitSendable(wpi::SendableBuilder& builder) {
-  CommandBase::InitSendable(builder);
+  Command::InitSendable(builder);
   builder.AddDoubleProperty(
       "leftVelocity", [this] { return m_prevSpeeds.left.value(); }, nullptr);
   builder.AddDoubleProperty(
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RepeatCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RepeatCommand.cpp
index 319a7af..2e74c8c 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RepeatCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RepeatCommand.cpp
@@ -39,7 +39,12 @@
 }
 
 void RepeatCommand::End(bool interrupted) {
-  m_command->End(interrupted);
+  // Make sure we didn't already call end() (which would happen if the command
+  // finished in the last call to our execute())
+  if (!m_ended) {
+    m_command->End(interrupted);
+    m_ended = true;
+  }
 }
 
 bool RepeatCommand::RunsWhenDisabled() const {
@@ -51,7 +56,7 @@
 }
 
 void RepeatCommand::InitSendable(wpi::SendableBuilder& builder) {
-  CommandBase::InitSendable(builder);
+  Command::InitSendable(builder);
   builder.AddStringProperty(
       "command", [this] { return m_command->GetName(); }, nullptr);
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RunCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RunCommand.cpp
index 342362f..c3378ea 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RunCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/RunCommand.cpp
@@ -6,12 +6,6 @@
 
 using namespace frc2;
 
-RunCommand::RunCommand(std::function<void()> toRun,
-                       std::initializer_list<Subsystem*> requirements)
-    : CommandHelper([] {}, std::move(toRun), [](bool interrupted) {},
-                    [] { return false; }, requirements) {}
-
-RunCommand::RunCommand(std::function<void()> toRun,
-                       std::span<Subsystem* const> requirements)
+RunCommand::RunCommand(std::function<void()> toRun, Requirements requirements)
     : CommandHelper([] {}, std::move(toRun), [](bool interrupted) {},
                     [] { return false; }, requirements) {}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ScheduleCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ScheduleCommand.cpp
index 54c3042..c96f218 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ScheduleCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/ScheduleCommand.cpp
@@ -7,11 +7,13 @@
 using namespace frc2;
 
 ScheduleCommand::ScheduleCommand(std::span<Command* const> toSchedule) {
-  SetInsert(m_toSchedule, toSchedule);
+  for (auto cmd : toSchedule) {
+    m_toSchedule.insert(cmd);
+  }
 }
 
 ScheduleCommand::ScheduleCommand(Command* toSchedule) {
-  SetInsert(m_toSchedule, {&toSchedule, 1});
+  m_toSchedule.insert(toSchedule);
 }
 
 void ScheduleCommand::Initialize() {
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/SequentialCommandGroup.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/SequentialCommandGroup.cpp
index 7888c1d..b9ea3d5 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/SequentialCommandGroup.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/SequentialCommandGroup.cpp
@@ -83,7 +83,7 @@
 }
 
 void SequentialCommandGroup::InitSendable(wpi::SendableBuilder& builder) {
-  CommandBase::InitSendable(builder);
+  Command::InitSendable(builder);
   builder.AddIntegerProperty(
       "index", [this] { return m_currentCommandIndex; }, nullptr);
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/StartEndCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/StartEndCommand.cpp
index 3d91ac5..2d257a3 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/StartEndCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/StartEndCommand.cpp
@@ -8,15 +8,7 @@
 
 StartEndCommand::StartEndCommand(std::function<void()> onInit,
                                  std::function<void()> onEnd,
-                                 std::initializer_list<Subsystem*> requirements)
-    : CommandHelper(
-          std::move(onInit), [] {},
-          [onEnd = std::move(onEnd)](bool interrupted) { onEnd(); },
-          [] { return false; }, requirements) {}
-
-StartEndCommand::StartEndCommand(std::function<void()> onInit,
-                                 std::function<void()> onEnd,
-                                 std::span<Subsystem* const> requirements)
+                                 Requirements requirements)
     : CommandHelper(
           std::move(onInit), [] {},
           [onEnd = std::move(onEnd)](bool interrupted) { onEnd(); },
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Subsystem.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Subsystem.cpp
index 3c388bb..4c06f1f 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Subsystem.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/Subsystem.cpp
@@ -21,6 +21,10 @@
                                                     std::move(defaultCommand));
 }
 
+void Subsystem::RemoveDefaultCommand() {
+  CommandScheduler::GetInstance().RemoveDefaultCommand(this);
+}
+
 Command* Subsystem::GetDefaultCommand() const {
   return CommandScheduler::GetInstance().GetDefaultCommand(this);
 }
@@ -50,3 +54,7 @@
                              std::function<void()> end) {
   return cmd::RunEnd(std::move(run), std::move(end), {this});
 }
+
+CommandPtr Subsystem::Defer(wpi::unique_function<CommandPtr()> supplier) {
+  return cmd::Defer(std::move(supplier), {this});
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/WaitCommand.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/WaitCommand.cpp
index 94b8b8a..36b15dd 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/WaitCommand.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/WaitCommand.cpp
@@ -5,7 +5,6 @@
 #include "frc2/command/WaitCommand.h"
 
 #include <fmt/format.h>
-#include <frc/fmt/Units.h>
 #include <wpi/sendable/SendableBuilder.h>
 
 using namespace frc2;
@@ -15,8 +14,7 @@
 }
 
 void WaitCommand::Initialize() {
-  m_timer.Reset();
-  m_timer.Start();
+  m_timer.Restart();
 }
 
 void WaitCommand::End(bool interrupted) {
@@ -32,7 +30,7 @@
 }
 
 void WaitCommand::InitSendable(wpi::SendableBuilder& builder) {
-  CommandBase::InitSendable(builder);
+  Command::InitSendable(builder);
   builder.AddDoubleProperty(
       "duration", [this] { return m_duration.value(); }, nullptr);
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Button.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Button.cpp
deleted file mode 100644
index 787fc09..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Button.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-// 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 "frc2/command/button/Button.h"
-
-using namespace frc2;
-
-Button::Button(std::function<bool()> isPressed) : Trigger(isPressed) {}
-
-Button Button::WhenPressed(Command* command) {
-  WPI_IGNORE_DEPRECATED
-  WhenActive(command);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::WhenPressed(std::function<void()> toRun,
-                           std::initializer_list<Subsystem*> requirements) {
-  WPI_IGNORE_DEPRECATED
-  WhenActive(std::move(toRun), requirements);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::WhenPressed(std::function<void()> toRun,
-                           std::span<Subsystem* const> requirements) {
-  WPI_IGNORE_DEPRECATED
-  WhenActive(std::move(toRun), requirements);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::WhileHeld(Command* command) {
-  WPI_IGNORE_DEPRECATED
-  WhileActiveContinous(command);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::WhileHeld(std::function<void()> toRun,
-                         std::initializer_list<Subsystem*> requirements) {
-  WPI_IGNORE_DEPRECATED
-  WhileActiveContinous(std::move(toRun), requirements);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::WhileHeld(std::function<void()> toRun,
-                         std::span<Subsystem* const> requirements) {
-  WPI_IGNORE_DEPRECATED
-  WhileActiveContinous(std::move(toRun), requirements);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::WhenHeld(Command* command) {
-  WPI_IGNORE_DEPRECATED
-  WhileActiveOnce(command);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::WhenReleased(Command* command) {
-  WPI_IGNORE_DEPRECATED
-  WhenInactive(command);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::WhenReleased(std::function<void()> toRun,
-                            std::initializer_list<Subsystem*> requirements) {
-  WPI_IGNORE_DEPRECATED
-  WhenInactive(std::move(toRun), requirements);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::WhenReleased(std::function<void()> toRun,
-                            std::span<Subsystem* const> requirements) {
-  WPI_IGNORE_DEPRECATED
-  WhenInactive(std::move(toRun), requirements);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::ToggleWhenPressed(Command* command) {
-  WPI_IGNORE_DEPRECATED
-  ToggleWhenActive(command);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
-
-Button Button::CancelWhenPressed(Command* command) {
-  WPI_IGNORE_DEPRECATED
-  CancelWhenActive(command);
-  WPI_UNIGNORE_DEPRECATED
-  return *this;
-}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/CommandPS5Controller.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/CommandPS5Controller.cpp
new file mode 100644
index 0000000..399a771
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/CommandPS5Controller.cpp
@@ -0,0 +1,63 @@
+// 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 "frc2/command/button/CommandPS5Controller.h"
+
+using namespace frc2;
+
+Trigger CommandPS5Controller::Button(int button, frc::EventLoop* loop) const {
+  return GenericHID::Button(button, loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::Square(frc::EventLoop* loop) const {
+  return PS5Controller::Square(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::Cross(frc::EventLoop* loop) const {
+  return PS5Controller::Cross(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::Circle(frc::EventLoop* loop) const {
+  return PS5Controller::Circle(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::Triangle(frc::EventLoop* loop) const {
+  return PS5Controller::Triangle(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::L1(frc::EventLoop* loop) const {
+  return PS5Controller::L1(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::R1(frc::EventLoop* loop) const {
+  return PS5Controller::R1(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::L2(frc::EventLoop* loop) const {
+  return PS5Controller::L2(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::R2(frc::EventLoop* loop) const {
+  return PS5Controller::R2(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::Options(frc::EventLoop* loop) const {
+  return PS5Controller::Options(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::L3(frc::EventLoop* loop) const {
+  return PS5Controller::L3(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::R3(frc::EventLoop* loop) const {
+  return PS5Controller::R3(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::PS(frc::EventLoop* loop) const {
+  return PS5Controller::PS(loop).CastTo<Trigger>();
+}
+
+Trigger CommandPS5Controller::Touchpad(frc::EventLoop* loop) const {
+  return PS5Controller::Touchpad(loop).CastTo<Trigger>();
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/NetworkButton.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/NetworkButton.cpp
index e26cd14..a0923a3 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/NetworkButton.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/NetworkButton.cpp
@@ -4,19 +4,15 @@
 
 #include "frc2/command/button/NetworkButton.h"
 
-#include <wpi/deprecated.h>
-
 using namespace frc2;
 
-WPI_IGNORE_DEPRECATED
 NetworkButton::NetworkButton(nt::BooleanTopic topic)
     : NetworkButton(topic.Subscribe(false)) {}
 
 NetworkButton::NetworkButton(nt::BooleanSubscriber sub)
-    : Button([sub = std::make_shared<nt::BooleanSubscriber>(std::move(sub))] {
+    : Trigger([sub = std::make_shared<nt::BooleanSubscriber>(std::move(sub))] {
         return sub->GetTopic().GetInstance().IsConnected() && sub->Get();
       }) {}
-WPI_UNIGNORE_DEPRECATED
 
 NetworkButton::NetworkButton(std::shared_ptr<nt::NetworkTable> table,
                              std::string_view field)
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp
index 3908daf..38ec741 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp
@@ -205,124 +205,6 @@
   return *this;
 }
 
-WPI_IGNORE_DEPRECATED
-Trigger Trigger::WhenActive(Command* command) {
-  return OnTrue(command);
-}
-
-Trigger Trigger::WhenActive(std::function<void()> toRun,
-                            std::initializer_list<Subsystem*> requirements) {
-  return WhenActive(std::move(toRun),
-                    {requirements.begin(), requirements.end()});
-}
-
-Trigger Trigger::WhenActive(std::function<void()> toRun,
-                            std::span<Subsystem* const> requirements) {
-  return WhenActive(InstantCommand(std::move(toRun), requirements));
-}
-
-Trigger Trigger::WhileActiveContinous(Command* command) {
-  m_loop->Bind([condition = m_condition, previous = m_condition(),
-                command = std::move(command)]() mutable {
-    bool current = condition();
-
-    if (current) {
-      command->Schedule();
-    } else if (previous && !current) {
-      command->Cancel();
-    }
-
-    previous = current;
-  });
-  return *this;
-}
-
-Trigger Trigger::WhileActiveContinous(
-    std::function<void()> toRun,
-    std::initializer_list<Subsystem*> requirements) {
-  return WhileActiveContinous(std::move(toRun),
-                              {requirements.begin(), requirements.end()});
-}
-
-Trigger Trigger::WhileActiveContinous(
-    std::function<void()> toRun, std::span<Subsystem* const> requirements) {
-  return WhileActiveContinous(InstantCommand(std::move(toRun), requirements));
-}
-
-Trigger Trigger::WhileActiveOnce(Command* command) {
-  m_loop->Bind(
-      [condition = m_condition, previous = m_condition(), command]() mutable {
-        bool current = condition();
-
-        if (!previous && current) {
-          command->Schedule();
-        } else if (previous && !current) {
-          command->Cancel();
-        }
-
-        previous = current;
-      });
-  return *this;
-}
-
-Trigger Trigger::WhenInactive(Command* command) {
-  m_loop->Bind(
-      [condition = m_condition, previous = m_condition(), command]() mutable {
-        bool current = condition();
-
-        if (previous && !current) {
-          command->Schedule();
-        }
-
-        previous = current;
-      });
-  return *this;
-}
-
-Trigger Trigger::WhenInactive(std::function<void()> toRun,
-                              std::initializer_list<Subsystem*> requirements) {
-  return WhenInactive(std::move(toRun),
-                      {requirements.begin(), requirements.end()});
-}
-
-Trigger Trigger::WhenInactive(std::function<void()> toRun,
-                              std::span<Subsystem* const> requirements) {
-  return WhenInactive(InstantCommand(std::move(toRun), requirements));
-}
-
-Trigger Trigger::ToggleWhenActive(Command* command) {
-  m_loop->Bind([condition = m_condition, previous = m_condition(),
-                command = command]() mutable {
-    bool current = condition();
-
-    if (!previous && current) {
-      if (command->IsScheduled()) {
-        command->Cancel();
-      } else {
-        command->Schedule();
-      }
-    }
-
-    previous = current;
-  });
-  return *this;
-}
-
-Trigger Trigger::CancelWhenActive(Command* command) {
-  m_loop->Bind([condition = m_condition, previous = m_condition(),
-                command = std::move(command)]() mutable {
-    bool current = condition();
-
-    if (!previous && current) {
-      command->Cancel();
-    }
-
-    previous = current;
-  });
-  return *this;
-}
-WPI_UNIGNORE_DEPRECATED
-
 Trigger Trigger::Debounce(units::second_t debounceTime,
                           frc::Debouncer::DebounceType type) {
   return Trigger(m_loop, [debouncer = frc::Debouncer(debounceTime, type),
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Command.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Command.h
index 189433b..9a48b2e 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Command.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Command.h
@@ -5,16 +5,15 @@
 #pragma once
 
 #include <functional>
-#include <initializer_list>
 #include <memory>
-#include <span>
 #include <string>
 
 #include <units/time.h>
 #include <wpi/Demangle.h>
 #include <wpi/SmallSet.h>
-#include <wpi/deprecated.h>
+#include <wpi/sendable/Sendable.h>
 
+#include "frc2/command/Requirements.h"
 #include "frc2/command/Subsystem.h"
 
 namespace frc2 {
@@ -24,8 +23,6 @@
   return wpi::Demangle(typeid(type).name());
 }
 
-class PerpetualCommand;
-
 /**
  * A state machine representing a complete action to be performed by the robot.
  * Commands are run by the CommandScheduler, and can be composed into
@@ -44,10 +41,9 @@
  * @see CommandScheduler
  * @see CommandHelper
  */
-class Command {
+class Command : public wpi::Sendable, public wpi::SendableHelper<Command> {
  public:
-  Command() = default;
-  virtual ~Command();
+  ~Command() override;
 
   Command(const Command&) = default;
   Command& operator=(const Command& rhs);
@@ -95,7 +91,83 @@
    * @return the set of subsystems that are required
    * @see InterruptionBehavior
    */
-  virtual wpi::SmallSet<Subsystem*, 4> GetRequirements() const = 0;
+  virtual wpi::SmallSet<Subsystem*, 4> GetRequirements() const;
+
+  /**
+   * Adds the specified Subsystem requirements to the command.
+   *
+   * The scheduler will prevent two commands that require the same subsystem
+   * from being scheduled simultaneously.
+   *
+   * Note that the scheduler determines the requirements of a command when it
+   * is scheduled, so this method should normally be called from the command's
+   * constructor.
+   *
+   * While this overload can be used with {@code AddRequirements({&subsystem1,
+   * &subsystem2})}, {@code AddRequirements({&subsystem})} selects the {@code
+   * AddRequirements(Subsystem*)} overload, which will function identically but
+   * may cause warnings about redundant braces.
+   *
+   * @param requirements the Subsystem requirements to add, which can be
+   * implicitly constructed from an initializer list or a span
+   */
+  void AddRequirements(Requirements requirements);
+
+  /**
+   * Adds the specified Subsystem requirements to the command.
+   *
+   * The scheduler will prevent two commands that require the same subsystem
+   * from being scheduled simultaneously.
+   *
+   * Note that the scheduler determines the requirements of a command when it
+   * is scheduled, so this method should normally be called from the command's
+   * constructor.
+   *
+   * @param requirements the Subsystem requirements to add
+   */
+  void AddRequirements(wpi::SmallSet<Subsystem*, 4> requirements);
+
+  /**
+   * Adds the specified Subsystem requirement to the command.
+   *
+   * The scheduler will prevent two commands that require the same subsystem
+   * from being scheduled simultaneously.
+   *
+   * Note that the scheduler determines the requirements of a command when it
+   * is scheduled, so this method should normally be called from the command's
+   * constructor.
+   *
+   * @param requirement the Subsystem requirement to add
+   */
+  void AddRequirements(Subsystem* requirement);
+
+  /**
+   * Gets the name of this Command.
+   *
+   * @return Name
+   */
+  std::string GetName() const;
+
+  /**
+   * Sets the name of this Command.
+   *
+   * @param name name
+   */
+  void SetName(std::string_view name);
+
+  /**
+   * Gets the subsystem name of this Command.
+   *
+   * @return Subsystem name
+   */
+  std::string GetSubsystem() const;
+
+  /**
+   * Sets the subsystem name of this Command.
+   *
+   * @param subsystem subsystem name
+   */
+  void SetSubsystem(std::string_view subsystem);
 
   /**
    * An enum describing the command's behavior when another command with a
@@ -116,39 +188,37 @@
   friend class CommandPtr;
 
   /**
-   * Decorates this command with a timeout.  If the specified timeout is
+   * Decorates this command with a timeout. If the specified timeout is
    * exceeded before the command finishes normally, the command will be
-   * interrupted and un-scheduled.  Note that the timeout only applies to the
-   * command returned by this method; the calling command is not itself changed.
+   * interrupted and un-scheduled.
    *
    * @param duration the timeout duration
    * @return the command with the timeout added
    */
-  [[nodiscard]] CommandPtr WithTimeout(units::second_t duration) &&;
+  [[nodiscard]]
+  CommandPtr WithTimeout(units::second_t duration) &&;
 
   /**
-   * Decorates this command with an interrupt condition.  If the specified
+   * Decorates this command with an interrupt condition. If the specified
    * condition becomes true before the command finishes normally, the command
-   * will be interrupted and un-scheduled. Note that this only applies to the
-   * command returned by this method; the calling command is not itself changed.
+   * will be interrupted and un-scheduled.
    *
    * @param condition the interrupt condition
    * @return the command with the interrupt condition added
    */
-  [[nodiscard]] CommandPtr Until(std::function<bool()> condition) &&;
+  [[nodiscard]]
+  CommandPtr Until(std::function<bool()> condition) &&;
 
   /**
-   * Decorates this command with an interrupt condition.  If the specified
-   * condition becomes true before the command finishes normally, the command
-   * will be interrupted and un-scheduled. Note that this only applies to the
-   * command returned by this method; the calling command is not itself changed.
+   * Decorates this command with a run condition. If the specified condition
+   * becomes false before the command finishes normally, the command will be
+   * interrupted and un-scheduled.
    *
-   * @param condition the interrupt condition
-   * @return the command with the interrupt condition added
-   * @deprecated Replace with Until()
+   * @param condition the run condition
+   * @return the command with the run condition added
    */
-  WPI_DEPRECATED("Replace with Until()")
-  [[nodiscard]] CommandPtr WithInterrupt(std::function<bool()> condition) &&;
+  [[nodiscard]]
+  CommandPtr OnlyWhile(std::function<bool()> condition) &&;
 
   /**
    * Decorates this command with a runnable to run before this command starts.
@@ -157,20 +227,9 @@
    * @param requirements the required subsystems
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr BeforeStarting(
-      std::function<void()> toRun,
-      std::initializer_list<Subsystem*> requirements) &&;
-
-  /**
-   * Decorates this command with a runnable to run before this command starts.
-   *
-   * @param toRun the Runnable to run
-   * @param requirements the required subsystems
-   * @return the decorated command
-   */
-  [[nodiscard]] CommandPtr BeforeStarting(
-      std::function<void()> toRun,
-      std::span<Subsystem* const> requirements = {}) &&;
+  [[nodiscard]]
+  CommandPtr BeforeStarting(std::function<void()> toRun,
+                            Requirements requirements = {}) &&;
 
   /**
    * Decorates this command with a runnable to run after the command finishes.
@@ -179,40 +238,9 @@
    * @param requirements the required subsystems
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr AndThen(
-      std::function<void()> toRun,
-      std::initializer_list<Subsystem*> requirements) &&;
-
-  /**
-   * Decorates this command with a runnable to run after the command finishes.
-   *
-   * @param toRun the Runnable to run
-   * @param requirements the required subsystems
-   * @return the decorated command
-   */
-  [[nodiscard]] CommandPtr AndThen(
-      std::function<void()> toRun,
-      std::span<Subsystem* const> requirements = {}) &&;
-
-  /**
-   * Decorates this command to run perpetually, ignoring its ordinary end
-   * conditions.  The decorated command can still be interrupted or canceled.
-   *
-   * @return the decorated command
-   * @deprecated PerpetualCommand violates the assumption that execute() doesn't
-get called after isFinished() returns true -- an assumption that should be
-valid. This was unsafe/undefined behavior from the start, and RepeatCommand
-provides an easy way to achieve similar end results with slightly different (and
-safe) semantics.
-   */
-  WPI_DEPRECATED(
-      "PerpetualCommand violates the assumption that execute() doesn't get "
-      "called after isFinished() returns true -- an assumption that should be "
-      "valid."
-      "This was unsafe/undefined behavior from the start, and RepeatCommand "
-      "provides an easy way to achieve similar end results with slightly "
-      "different (and safe) semantics.")
-  PerpetualCommand Perpetually() &&;
+  [[nodiscard]]
+  CommandPtr AndThen(std::function<void()> toRun,
+                     Requirements requirements = {}) &&;
 
   /**
    * Decorates this command to run repeatedly, restarting it when it ends, until
@@ -220,7 +248,8 @@
    *
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr Repeatedly() &&;
+  [[nodiscard]]
+  CommandPtr Repeatedly() &&;
 
   /**
    * Decorates this command to run "by proxy" by wrapping it in a
@@ -232,7 +261,8 @@
    *
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr AsProxy() &&;
+  [[nodiscard]]
+  CommandPtr AsProxy() &&;
 
   /**
    * Decorates this command to only run if this condition is not met. If the
@@ -243,7 +273,20 @@
    * @param condition the condition that will prevent the command from running
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr Unless(std::function<bool()> condition) &&;
+  [[nodiscard]]
+  CommandPtr Unless(std::function<bool()> condition) &&;
+
+  /**
+   * Decorates this command to only run if this condition is met. If the command
+   * is already running and the condition changes to false, the command will not
+   * stop running. The requirements of this command will be kept for the new
+   * conditional command.
+   *
+   * @param condition the condition that will allow the command to run
+   * @return the decorated command
+   */
+  [[nodiscard]]
+  CommandPtr OnlyIf(std::function<bool()> condition) &&;
 
   /**
    * Decorates this command to run or stop when disabled.
@@ -251,15 +294,17 @@
    * @param doesRunWhenDisabled true to run when disabled.
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr IgnoringDisable(bool doesRunWhenDisabled) &&;
+  [[nodiscard]]
+  CommandPtr IgnoringDisable(bool doesRunWhenDisabled) &&;
 
   /**
-   * Decorates this command to run or stop when disabled.
+   * Decorates this command to have a different interrupt behavior.
    *
-   * @param interruptBehavior true to run when disabled.
+   * @param interruptBehavior the desired interrupt behavior
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr WithInterruptBehavior(
+  [[nodiscard]]
+  CommandPtr WithInterruptBehavior(
       Command::InterruptionBehavior interruptBehavior) &&;
 
   /**
@@ -270,7 +315,20 @@
    * command was interrupted.
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr FinallyDo(std::function<void(bool)> end) &&;
+  [[nodiscard]]
+  CommandPtr FinallyDo(std::function<void(bool)> end) &&;
+
+  /**
+   * Decorates this command with a lambda to call on interrupt or end, following
+   * the command's inherent Command::End(bool) method. The provided lambda will
+   * run identically in both interrupt and end cases.
+   *
+   * @param end a lambda to run when the command ends, whether or not it was
+   * interrupted.
+   * @return the decorated command
+   */
+  [[nodiscard]]
+  CommandPtr FinallyDo(std::function<void()> end) &&;
 
   /**
    * Decorates this command with a lambda to call on interrupt, following the
@@ -279,7 +337,8 @@
    * @param handler a lambda to run when the command is interrupted
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr HandleInterrupt(std::function<void()> handler) &&;
+  [[nodiscard]]
+  CommandPtr HandleInterrupt(std::function<void()> handler) &&;
 
   /**
    * Decorates this Command with a name.
@@ -287,7 +346,8 @@
    * @param name name
    * @return the decorated Command
    */
-  [[nodiscard]] CommandPtr WithName(std::string_view name) &&;
+  [[nodiscard]]
+  CommandPtr WithName(std::string_view name) &&;
 
   /**
    * Schedules this command.
@@ -333,25 +393,6 @@
   void SetComposed(bool isComposed);
 
   /**
-   * Whether the command is currently grouped in a command group.  Used as extra
-   * insurance to prevent accidental independent use of grouped commands.
-   *
-   * @deprecated Moved to IsComposed()
-   */
-  WPI_DEPRECATED("Moved to IsComposed()")
-  bool IsGrouped() const;
-
-  /**
-   * Sets whether the command is currently grouped in a command group.  Can be
-   * used to "reclaim" a command if a group is no longer going to use it.  NOT
-   * ADVISED!
-   *
-   * @deprecated Moved to SetComposed()
-   */
-  WPI_DEPRECATED("Moved to SetComposed()")
-  void SetGrouped(bool grouped);
-
-  /**
    * Whether the given command should run when the robot is disabled.  Override
    * to return true if the command should run when disabled.
    *
@@ -370,27 +411,18 @@
   }
 
   /**
-   * Gets the name of this Command. Defaults to the simple class name if not
-   * overridden.
-   *
-   * @return The display name of the Command
-   */
-  virtual std::string GetName() const;
-
-  /**
-   * Sets the name of this Command. Nullop if not overridden.
-   *
-   * @param name The display name of the Command.
-   */
-  virtual void SetName(std::string_view name);
-
-  /**
    * Transfers ownership of this command to a unique pointer.  Used for
    * decorator methods.
    */
   virtual CommandPtr ToPtr() && = 0;
 
+  void InitSendable(wpi::SendableBuilder& builder) override;
+
  protected:
+  Command();
+
+  wpi::SmallSet<Subsystem*, 4> m_requirements;
+
   /**
    * Transfers ownership of this command to a unique pointer.  Used for
    * decorator methods.
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandBase.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandBase.h
index 64bd88b..ea24e42 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandBase.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandBase.h
@@ -4,14 +4,7 @@
 
 #pragma once
 
-#include <initializer_list>
-#include <span>
-#include <string>
-#include <string_view>
-
-#include <wpi/SmallSet.h>
-#include <wpi/sendable/Sendable.h>
-#include <wpi/sendable/SendableHelper.h>
+#include <wpi/deprecated.h>
 
 #include "frc2/command/Command.h"
 
@@ -20,78 +13,13 @@
  * A Sendable base class for Commands.
  *
  * This class is provided by the NewCommands VendorDep
+ *
+ * @deprecated All functionality provided by CommandBase has been merged into
+ * Command. Use Command instead.
  */
-class CommandBase : public Command,
-                    public wpi::Sendable,
-                    public wpi::SendableHelper<CommandBase> {
- public:
-  /**
-   * Adds the specified Subsystem requirements to the command.
-   *
-   * @param requirements the Subsystem requirements to add
-   */
-  void AddRequirements(std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Adds the specified Subsystem requirements to the command.
-   *
-   * @param requirements the Subsystem requirements to add
-   */
-  void AddRequirements(std::span<Subsystem* const> requirements);
-
-  /**
-   * Adds the specified Subsystem requirements to the command.
-   *
-   * @param requirements the Subsystem requirements to add
-   */
-  void AddRequirements(wpi::SmallSet<Subsystem*, 4> requirements);
-
-  /**
-   * Adds the specified Subsystem requirement to the command.
-   *
-   * @param requirement the Subsystem requirement to add
-   */
-  void AddRequirements(Subsystem* requirement);
-
-  /**
-   * Gets the Subsystem requirements of the command.
-   *
-   * @return the Command's Subsystem requirements
-   */
-  wpi::SmallSet<Subsystem*, 4> GetRequirements() const override;
-
-  /**
-   * Sets the name of this Command.
-   *
-   * @param name name
-   */
-  void SetName(std::string_view name) override;
-
-  /**
-   * Gets the name of this Command.
-   *
-   * @return Name
-   */
-  std::string GetName() const override;
-
-  /**
-   * Gets the subsystem name of this Command.
-   *
-   * @return Subsystem name
-   */
-  std::string GetSubsystem() const;
-
-  /**
-   * Sets the subsystem name of this Command.
-   *
-   * @param subsystem subsystem name
-   */
-  void SetSubsystem(std::string_view subsystem);
-
-  void InitSendable(wpi::SendableBuilder& builder) override;
-
+class [[deprecated("Use Command instead")]] CommandBase : public Command {
  protected:
+  WPI_DEPRECATED("Use Command instead")
   CommandBase();
-  wpi::SmallSet<Subsystem*, 4> m_requirements;
 };
 }  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandGroupBase.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandGroupBase.h
deleted file mode 100644
index 9fe65fa..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandGroupBase.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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 <vector>
-
-#include <wpi/deprecated.h>
-
-#include "frc2/command/CommandBase.h"
-
-namespace frc2 {
-
-/**
- * A base for CommandGroups.
- *
- * This class is provided by the NewCommands VendorDep
- * @deprecated This class is an empty abstraction. Inherit directly from
- * CommandBase.
- */
-class CommandGroupBase : public CommandBase {
- public:
-  /**
-   * Adds the given commands to the command group.
-   *
-   * @param commands The commands to add.
-   */
-  virtual void AddCommands(
-      std::vector<std::unique_ptr<Command>>&& commands) = 0;
-};
-}  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandHelper.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandHelper.h
index 76ee6cd..ed93b65 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandHelper.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandHelper.h
@@ -4,8 +4,8 @@
 
 #pragma once
 
+#include <concepts>
 #include <memory>
-#include <type_traits>
 #include <utility>
 
 #include "frc2/command/Command.h"
@@ -21,8 +21,7 @@
  *
  * This class is provided by the NewCommands VendorDep
  */
-template <typename Base, typename CRTP,
-          typename = std::enable_if_t<std::is_base_of_v<Command, Base>>>
+template <std::derived_from<Command> Base, typename CRTP>
 class CommandHelper : public Base {
   using Base::Base;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandPtr.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandPtr.h
index 0ab1e97..ae8c118 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandPtr.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandPtr.h
@@ -4,15 +4,15 @@
 
 #pragma once
 
+#include <concepts>
 #include <functional>
-#include <initializer_list>
 #include <memory>
-#include <span>
 #include <string>
 #include <utility>
 #include <vector>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
+#include "frc2/command/Requirements.h"
 
 namespace frc2 {
 /**
@@ -27,14 +27,14 @@
  */
 class CommandPtr final {
  public:
-  explicit CommandPtr(std::unique_ptr<CommandBase>&& command)
+  explicit CommandPtr(std::unique_ptr<Command>&& command)
       : m_ptr(std::move(command)) {}
 
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
+  template <std::derived_from<Command> T>
+  // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
   explicit CommandPtr(T&& command)
-      : CommandPtr(std::make_unique<std::remove_reference_t<T>>(
-            std::forward<T>(command))) {}
+      : CommandPtr(
+            std::make_unique<std::decay_t<T>>(std::forward<T>(command))) {}
 
   CommandPtr(CommandPtr&&) = default;
   CommandPtr& operator=(CommandPtr&&) = default;
@@ -45,7 +45,8 @@
    *
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr Repeatedly() &&;
+  [[nodiscard]]
+  CommandPtr Repeatedly() &&;
 
   /**
    * Decorates this command to run "by proxy" by wrapping it in a
@@ -55,23 +56,26 @@
    *
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr AsProxy() &&;
+  [[nodiscard]]
+  CommandPtr AsProxy() &&;
 
   /**
    * Decorates this command to run or stop when disabled.
    *
-   * @param doesRunWhenDisabled true to run when disabled.
+   * @param doesRunWhenDisabled true to run when disabled
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr IgnoringDisable(bool doesRunWhenDisabled) &&;
+  [[nodiscard]]
+  CommandPtr IgnoringDisable(bool doesRunWhenDisabled) &&;
 
   /**
-   * Decorates this command to run or stop when disabled.
+   * Decorates this command to have a different interrupt behavior.
    *
-   * @param interruptBehavior true to run when disabled.
+   * @param interruptBehavior the desired interrupt behavior
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr WithInterruptBehavior(
+  [[nodiscard]]
+  CommandPtr WithInterruptBehavior(
       Command::InterruptionBehavior interruptBehavior) &&;
 
   /**
@@ -81,20 +85,9 @@
    * @param requirements the required subsystems
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr AndThen(
-      std::function<void()> toRun,
-      std::span<Subsystem* const> requirements = {}) &&;
-
-  /**
-   * Decorates this command with a runnable to run after the command finishes.
-   *
-   * @param toRun the Runnable to run
-   * @param requirements the required subsystems
-   * @return the decorated command
-   */
-  [[nodiscard]] CommandPtr AndThen(
-      std::function<void()> toRun,
-      std::initializer_list<Subsystem*> requirements) &&;
+  [[nodiscard]]
+  CommandPtr AndThen(std::function<void()> toRun,
+                     Requirements requirements = {}) &&;
 
   /**
    * Decorates this command with a set of commands to run after it in sequence.
@@ -104,7 +97,8 @@
    * @param next the commands to run next
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr AndThen(CommandPtr&& next) &&;
+  [[nodiscard]]
+  CommandPtr AndThen(CommandPtr&& next) &&;
 
   /**
    * Decorates this command with a runnable to run before this command starts.
@@ -113,20 +107,9 @@
    * @param requirements the required subsystems
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr BeforeStarting(
-      std::function<void()> toRun,
-      std::initializer_list<Subsystem*> requirements) &&;
-
-  /**
-   * Decorates this command with a runnable to run before this command starts.
-   *
-   * @param toRun the Runnable to run
-   * @param requirements the required subsystems
-   * @return the decorated command
-   */
-  [[nodiscard]] CommandPtr BeforeStarting(
-      std::function<void()> toRun,
-      std::span<Subsystem* const> requirements = {}) &&;
+  [[nodiscard]]
+  CommandPtr BeforeStarting(std::function<void()> toRun,
+                            Requirements requirements = {}) &&;
 
   /**
    * Decorates this command with another command to run before this command
@@ -135,29 +118,41 @@
    * @param before the command to run before this one
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr BeforeStarting(CommandPtr&& before) &&;
+  [[nodiscard]]
+  CommandPtr BeforeStarting(CommandPtr&& before) &&;
 
   /**
-   * Decorates this command with a timeout.  If the specified timeout is
+   * Decorates this command with a timeout. If the specified timeout is
    * exceeded before the command finishes normally, the command will be
-   * interrupted and un-scheduled.  Note that the timeout only applies to the
-   * command returned by this method; the calling command is not itself changed.
+   * interrupted and un-scheduled.
    *
    * @param duration the timeout duration
    * @return the command with the timeout added
    */
-  [[nodiscard]] CommandPtr WithTimeout(units::second_t duration) &&;
+  [[nodiscard]]
+  CommandPtr WithTimeout(units::second_t duration) &&;
 
   /**
-   * Decorates this command with an interrupt condition.  If the specified
+   * Decorates this command with an interrupt condition. If the specified
    * condition becomes true before the command finishes normally, the command
-   * will be interrupted and un-scheduled. Note that this only applies to the
-   * command returned by this method; the calling command is not itself changed.
+   * will be interrupted and un-scheduled.
    *
    * @param condition the interrupt condition
    * @return the command with the interrupt condition added
    */
-  [[nodiscard]] CommandPtr Until(std::function<bool()> condition) &&;
+  [[nodiscard]]
+  CommandPtr Until(std::function<bool()> condition) &&;
+
+  /**
+   * Decorates this command with a run condition. If the specified condition
+   * becomes false before the command finishes normally, the command will be
+   * interrupted and un-scheduled.
+   *
+   * @param condition the run condition
+   * @return the command with the run condition added
+   */
+  [[nodiscard]]
+  CommandPtr OnlyWhile(std::function<bool()> condition) &&;
 
   /**
    * Decorates this command to only run if this condition is not met. If the
@@ -168,7 +163,20 @@
    * @param condition the condition that will prevent the command from running
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr Unless(std::function<bool()> condition) &&;
+  [[nodiscard]]
+  CommandPtr Unless(std::function<bool()> condition) &&;
+
+  /**
+   * Decorates this command to only run if this condition is met. If the command
+   * is already running and the condition changes to false, the command will not
+   * stop running. The requirements of this command will be kept for the new
+   * conditional command.
+   *
+   * @param condition the condition that will allow the command to run
+   * @return the decorated command
+   */
+  [[nodiscard]]
+  CommandPtr OnlyIf(std::function<bool()> condition) &&;
 
   /**
    * Decorates this command with a set of commands to run parallel to it, ending
@@ -179,7 +187,8 @@
    * @param parallel the commands to run in parallel
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr DeadlineWith(CommandPtr&& parallel) &&;
+  [[nodiscard]]
+  CommandPtr DeadlineWith(CommandPtr&& parallel) &&;
 
   /**
    * Decorates this command with a set of commands to run parallel to it, ending
@@ -189,7 +198,8 @@
    * @param parallel the commands to run in parallel
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr AlongWith(CommandPtr&& parallel) &&;
+  [[nodiscard]]
+  CommandPtr AlongWith(CommandPtr&& parallel) &&;
 
   /**
    * Decorates this command with a set of commands to run parallel to it, ending
@@ -199,17 +209,31 @@
    * @param parallel the commands to run in parallel
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr RaceWith(CommandPtr&& parallel) &&;
+  [[nodiscard]]
+  CommandPtr RaceWith(CommandPtr&& parallel) &&;
 
   /**
    * Decorates this command with a lambda to call on interrupt or end, following
    * the command's inherent Command::End(bool) method.
    *
    * @param end a lambda accepting a boolean parameter specifying whether the
-   * command was interrupted.
+   * command was interrupted
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr FinallyDo(std::function<void(bool)> end) &&;
+  [[nodiscard]]
+  CommandPtr FinallyDo(std::function<void(bool)> end) &&;
+
+  /**
+   * Decorates this command with a lambda to call on interrupt or end, following
+   * the command's inherent Command::End(bool) method. The provided lambda will
+   * run identically in both interrupt and end cases.
+   *
+   * @param end a lambda to run when the command ends, whether or not it was
+   * interrupted.
+   * @return the decorated command
+   */
+  [[nodiscard]]
+  CommandPtr FinallyDo(std::function<void()> end) &&;
 
   /**
    * Decorates this command with a lambda to call on interrupt, following the
@@ -218,7 +242,8 @@
    * @param handler a lambda to run when the command is interrupted
    * @return the decorated command
    */
-  [[nodiscard]] CommandPtr HandleInterrupt(std::function<void()> handler) &&;
+  [[nodiscard]]
+  CommandPtr HandleInterrupt(std::function<void()> handler) &&;
 
   /**
    * Decorates this Command with a name. Is an inline function for
@@ -227,28 +252,38 @@
    * @param name name
    * @return the decorated Command
    */
-  [[nodiscard]] CommandPtr WithName(std::string_view name) &&;
+  [[nodiscard]]
+  CommandPtr WithName(std::string_view name) &&;
 
   /**
    * Get a raw pointer to the held command.
    */
-  CommandBase* get() const;
+  Command* get() const&;
+
+  // Prevent calls on a temporary, as the returned pointer would be invalid
+  Command* get() && = delete;
 
   /**
    * Convert to the underlying unique_ptr.
    */
-  std::unique_ptr<CommandBase> Unwrap() &&;
+  std::unique_ptr<Command> Unwrap() &&;
 
   /**
    * Schedules this command.
    */
-  void Schedule() const;
+  void Schedule() const&;
+
+  // Prevent calls on a temporary, as the returned pointer would be invalid
+  void Schedule() && = delete;
 
   /**
    * Cancels this command. Will call End(true). Commands will be canceled
    * regardless of interruption behavior.
    */
-  void Cancel() const;
+  void Cancel() const&;
+
+  // Prevent calls on a temporary, as the returned pointer would be invalid
+  void Cancel() && = delete;
 
   /**
    * Whether or not the command is currently scheduled. Note that this does not
@@ -257,7 +292,10 @@
    *
    * @return Whether the command is scheduled.
    */
-  bool IsScheduled() const;
+  bool IsScheduled() const&;
+
+  // Prevent calls on a temporary, as the returned pointer would be invalid
+  void IsScheduled() && = delete;
 
   /**
    * Whether the command requires a given subsystem.  Named "HasRequirement"
@@ -267,12 +305,18 @@
    * @param requirement the subsystem to inquire about
    * @return whether the subsystem is required
    */
-  bool HasRequirement(Subsystem* requirement) const;
+  bool HasRequirement(Subsystem* requirement) const&;
+
+  // Prevent calls on a temporary, as the returned pointer would be invalid
+  void HasRequirement(Subsystem* requirement) && = delete;
 
   /**
    * Check if this CommandPtr object is valid and wasn't moved-from.
    */
-  explicit operator bool() const;
+  explicit operator bool() const&;
+
+  // Prevent calls on a temporary, as the returned pointer would be invalid
+  explicit operator bool() && = delete;
 
   /**
    * Convert a vector of CommandPtr objects to their underlying unique_ptrs.
@@ -281,7 +325,7 @@
       std::vector<CommandPtr>&& vec);
 
  private:
-  std::unique_ptr<CommandBase> m_ptr;
+  std::unique_ptr<Command> m_ptr;
   void AssertValid() const;
 };
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandScheduler.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandScheduler.h
index c0c09c1..ce4dc91 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandScheduler.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/CommandScheduler.h
@@ -4,18 +4,20 @@
 
 #pragma once
 
+#include <concepts>
+#include <functional>
 #include <initializer_list>
 #include <memory>
+#include <optional>
 #include <span>
 #include <utility>
 
 #include <frc/Errors.h>
 #include <frc/Watchdog.h>
 #include <frc/event/EventLoop.h>
-#include <networktables/NTSendable.h>
 #include <units/time.h>
 #include <wpi/FunctionExtras.h>
-#include <wpi/deprecated.h>
+#include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
 namespace frc2 {
@@ -32,7 +34,7 @@
  *
  * This class is provided by the NewCommands VendorDep
  */
-class CommandScheduler final : public nt::NTSendable,
+class CommandScheduler final : public wpi::Sendable,
                                public wpi::SendableHelper<CommandScheduler> {
  public:
   /**
@@ -47,6 +49,8 @@
   CommandScheduler& operator=(const CommandScheduler&) = delete;
 
   using Action = std::function<void(const Command&)>;
+  using InterruptAction =
+      std::function<void(const Command&, const std::optional<Command*>&)>;
 
   /**
    * Changes the period of the loop overrun watchdog. This should be kept in
@@ -78,12 +82,6 @@
   frc::EventLoop* GetDefaultButtonLoop() const;
 
   /**
-   * Removes all button bindings from the scheduler.
-   */
-  WPI_DEPRECATED("Call Clear on the EventLoop instance directly!")
-  void ClearButtons();
-
-  /**
    * Schedules a command for execution. Does nothing if the command is already
    * scheduled. If a command's requirements are not available, it will only be
    * started if all the commands currently using those requirements are
@@ -165,6 +163,13 @@
   void UnregisterSubsystem(std::span<Subsystem* const> subsystems);
 
   /**
+   * Un-registers all registered Subsystems with the scheduler. All currently
+   * registered subsystems will no longer have their periodic block called, and
+   * will not have their default command scheduled.
+   */
+  void UnregisterAllSubsystems();
+
+  /**
    * Sets the default command for a subsystem.  Registers that subsystem if it
    * is not already registered.  Default commands will run whenever there is no
    * other command currently scheduled that requires the subsystem.  Default
@@ -175,16 +180,14 @@
    * @param subsystem      the subsystem whose default command will be set
    * @param defaultCommand the default command to associate with the subsystem
    */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
+  template <std::derived_from<Command> T>
   void SetDefaultCommand(Subsystem* subsystem, T&& defaultCommand) {
     if (!defaultCommand.HasRequirement(subsystem)) {
       throw FRC_MakeError(frc::err::CommandIllegalUse,
                           "Default commands must require their subsystem!");
     }
-    SetDefaultCommandImpl(subsystem,
-                          std::make_unique<std::remove_reference_t<T>>(
-                              std::forward<T>(defaultCommand)));
+    SetDefaultCommandImpl(subsystem, std::make_unique<std::decay_t<T>>(
+                                         std::forward<T>(defaultCommand)));
   }
 
   /**
@@ -354,6 +357,16 @@
   void OnCommandInterrupt(Action action);
 
   /**
+   * Adds an action to perform on the interruption of any command by the
+   * scheduler. The action receives the interrupted command and an optional
+   * containing the interrupting command, or nullopt if it was not canceled by a
+   * command (e.g., by Cancel()).
+   *
+   * @param action the action to perform
+   */
+  void OnCommandInterrupt(InterruptAction action);
+
+  /**
    * Adds an action to perform on the finishing of any command by the scheduler.
    *
    * @param action the action to perform
@@ -388,7 +401,7 @@
    */
   void RequireUngrouped(std::initializer_list<const Command*> commands);
 
-  void InitSendable(nt::NTSendableBuilder& builder) override;
+  void InitSendable(wpi::SendableBuilder& builder) override;
 
  private:
   // Constructor; private as this is a singleton
@@ -397,6 +410,8 @@
   void SetDefaultCommandImpl(Subsystem* subsystem,
                              std::unique_ptr<Command> command);
 
+  void Cancel(Command* command, std::optional<Command*> interruptor);
+
   class Impl;
   std::unique_ptr<Impl> m_impl;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Commands.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Commands.h
index 8b38275..5c1d49a 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Commands.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Commands.h
@@ -4,14 +4,16 @@
 
 #pragma once
 
+#include <concepts>
 #include <functional>
-#include <initializer_list>
-#include <span>
+#include <memory>
 #include <string>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
 #include "frc2/command/CommandPtr.h"
+#include "frc2/command/Requirements.h"
 #include "frc2/command/SelectCommand.h"
 
 namespace frc2 {
@@ -25,7 +27,17 @@
 /**
  * Constructs a command that does nothing, finishing immediately.
  */
-[[nodiscard]] CommandPtr None();
+[[nodiscard]]
+CommandPtr None();
+
+/**
+ * Constructs a command that does nothing until interrupted.
+ *
+ * @param requirements Subsystems to require
+ * @return the command
+ */
+[[nodiscard]]
+CommandPtr Idle(Requirements requirements = {});
 
 // Action Commands
 
@@ -35,18 +47,9 @@
  * @param action the action to run
  * @param requirements subsystems the action requires
  */
-[[nodiscard]] CommandPtr RunOnce(
-    std::function<void()> action,
-    std::initializer_list<Subsystem*> requirements);
-
-/**
- * Constructs a command that runs an action once and finishes.
- *
- * @param action the action to run
- * @param requirements subsystems the action requires
- */
-[[nodiscard]] CommandPtr RunOnce(std::function<void()> action,
-                                 std::span<Subsystem* const> requirements = {});
+[[nodiscard]]
+CommandPtr RunOnce(std::function<void()> action,
+                   Requirements requirements = {});
 
 /**
  * Constructs a command that runs an action every iteration until interrupted.
@@ -54,17 +57,8 @@
  * @param action the action to run
  * @param requirements subsystems the action requires
  */
-[[nodiscard]] CommandPtr Run(std::function<void()> action,
-                             std::initializer_list<Subsystem*> requirements);
-
-/**
- * Constructs a command that runs an action every iteration until interrupted.
- *
- * @param action the action to run
- * @param requirements subsystems the action requires
- */
-[[nodiscard]] CommandPtr Run(std::function<void()> action,
-                             std::span<Subsystem* const> requirements = {});
+[[nodiscard]]
+CommandPtr Run(std::function<void()> action, Requirements requirements = {});
 
 /**
  * Constructs a command that runs an action once and another action when the
@@ -74,21 +68,9 @@
  * @param end the action to run on interrupt
  * @param requirements subsystems the action requires
  */
-[[nodiscard]] CommandPtr StartEnd(
-    std::function<void()> start, std::function<void()> end,
-    std::initializer_list<Subsystem*> requirements);
-
-/**
- * Constructs a command that runs an action once and another action when the
- * command is interrupted.
- *
- * @param start the action to run on start
- * @param end the action to run on interrupt
- * @param requirements subsystems the action requires
- */
-[[nodiscard]] CommandPtr StartEnd(
-    std::function<void()> start, std::function<void()> end,
-    std::span<Subsystem* const> requirements = {});
+[[nodiscard]]
+CommandPtr StartEnd(std::function<void()> start, std::function<void()> end,
+                    Requirements requirements = {});
 
 /**
  * Constructs a command that runs an action every iteration until interrupted,
@@ -98,28 +80,17 @@
  * @param end the action to run on interrupt
  * @param requirements subsystems the action requires
  */
-[[nodiscard]] CommandPtr RunEnd(std::function<void()> run,
-                                std::function<void()> end,
-                                std::initializer_list<Subsystem*> requirements);
-
-/**
- * Constructs a command that runs an action every iteration until interrupted,
- * and then runs a second action.
- *
- * @param run the action to run every iteration
- * @param end the action to run on interrupt
- * @param requirements subsystems the action requires
- */
-[[nodiscard]] CommandPtr RunEnd(std::function<void()> run,
-                                std::function<void()> end,
-                                std::span<Subsystem* const> requirements = {});
+[[nodiscard]]
+CommandPtr RunEnd(std::function<void()> run, std::function<void()> end,
+                  Requirements requirements = {});
 
 /**
  * Constructs a command that prints a message and finishes.
  *
  * @param msg the message to print
  */
-[[nodiscard]] CommandPtr Print(std::string_view msg);
+[[nodiscard]]
+CommandPtr Print(std::string_view msg);
 
 // Idling Commands
 
@@ -128,7 +99,8 @@
  *
  * @param duration after how long the command finishes
  */
-[[nodiscard]] CommandPtr Wait(units::second_t duration);
+[[nodiscard]]
+CommandPtr Wait(units::second_t duration);
 
 /**
  * Constructs a command that does nothing, finishing once a condition becomes
@@ -136,7 +108,8 @@
  *
  * @param condition the condition
  */
-[[nodiscard]] CommandPtr WaitUntil(std::function<bool()> condition);
+[[nodiscard]]
+CommandPtr WaitUntil(std::function<bool()> condition);
 
 // Selector Commands
 
@@ -147,8 +120,9 @@
  * @param onFalse the command to run if the selector function returns false
  * @param selector the selector function
  */
-[[nodiscard]] CommandPtr Either(CommandPtr&& onTrue, CommandPtr&& onFalse,
-                                std::function<bool()> selector);
+[[nodiscard]]
+CommandPtr Either(CommandPtr&& onTrue, CommandPtr&& onFalse,
+                  std::function<bool()> selector);
 
 /**
  * Runs one of several commands, based on the selector function.
@@ -156,15 +130,48 @@
  * @param selector the selector function
  * @param commands map of commands to select from
  */
-template <typename Key>
-[[nodiscard]] CommandPtr Select(
-    std::function<Key()> selector,
-    std::vector<std::pair<Key, CommandPtr>> commands) {
-  return SelectCommand(std::move(selector),
-                       CommandPtr::UnwrapVector(std::move(commands)))
-      .ToPtr();
+template <typename Key, std::convertible_to<CommandPtr>... CommandPtrs>
+[[nodiscard]]
+CommandPtr Select(std::function<Key()> selector,
+                  std::pair<Key, CommandPtrs>&&... commands) {
+  std::vector<std::pair<Key, std::unique_ptr<Command>>> vec;
+
+  ((void)vec.emplace_back(commands.first, std::move(commands.second).Unwrap()),
+   ...);
+
+  return SelectCommand(std::move(selector), std::move(vec)).ToPtr();
 }
 
+/**
+ * Runs the command supplied by the supplier.
+ *
+ * @param supplier the command supplier
+ * @param requirements the set of requirements for this command
+ */
+[[nodiscard]]
+CommandPtr Defer(wpi::unique_function<CommandPtr()> supplier,
+                 Requirements requirements);
+
+/**
+ * Constructs a command that schedules the command returned from the supplier
+ * when initialized, and ends when it is no longer scheduled. The supplier is
+ * called when the command is initialized.
+ *
+ * @param supplier the command supplier
+ */
+[[nodiscard]]
+CommandPtr DeferredProxy(wpi::unique_function<Command*()> supplier);
+
+/**
+ * Constructs a command that schedules the command returned from the supplier
+ * when initialized, and ends when it is no longer scheduled. The supplier is
+ * called when the command is initialized.
+ *
+ * @param supplier the command supplier
+ */
+[[nodiscard]]
+CommandPtr DeferredProxy(wpi::unique_function<CommandPtr()> supplier);
+
 // Command Groups
 
 namespace impl {
@@ -172,7 +179,7 @@
 /**
  * Create a vector of commands.
  */
-template <typename... Args>
+template <std::convertible_to<CommandPtr>... Args>
 std::vector<CommandPtr> MakeVector(Args&&... args) {
   std::vector<CommandPtr> data;
   data.reserve(sizeof...(Args));
@@ -185,76 +192,86 @@
 /**
  * Runs a group of commands in series, one after the other.
  */
-[[nodiscard]] CommandPtr Sequence(std::vector<CommandPtr>&& commands);
+[[nodiscard]]
+CommandPtr Sequence(std::vector<CommandPtr>&& commands);
 
 /**
  * Runs a group of commands in series, one after the other.
  */
-template <typename... Args>
-[[nodiscard]] CommandPtr Sequence(Args&&... commands) {
-  return Sequence(impl::MakeVector(std::forward<Args>(commands)...));
+template <std::convertible_to<CommandPtr>... CommandPtrs>
+[[nodiscard]]
+CommandPtr Sequence(CommandPtrs&&... commands) {
+  return Sequence(impl::MakeVector(std::forward<CommandPtrs>(commands)...));
 }
 
 /**
  * Runs a group of commands in series, one after the other. Once the last
  * command ends, the group is restarted.
  */
-[[nodiscard]] CommandPtr RepeatingSequence(std::vector<CommandPtr>&& commands);
+[[nodiscard]]
+CommandPtr RepeatingSequence(std::vector<CommandPtr>&& commands);
 
 /**
  * Runs a group of commands in series, one after the other. Once the last
  * command ends, the group is restarted.
  */
-template <typename... Args>
-[[nodiscard]] CommandPtr RepeatingSequence(Args&&... commands) {
-  return RepeatingSequence(impl::MakeVector(std::forward<Args>(commands)...));
+template <std::convertible_to<CommandPtr>... CommandPtrs>
+[[nodiscard]]
+CommandPtr RepeatingSequence(CommandPtrs&&... commands) {
+  return RepeatingSequence(
+      impl::MakeVector(std::forward<CommandPtrs>(commands)...));
 }
 
 /**
  * Runs a group of commands at the same time. Ends once all commands in the
  * group finish.
  */
-[[nodiscard]] CommandPtr Parallel(std::vector<CommandPtr>&& commands);
+[[nodiscard]]
+CommandPtr Parallel(std::vector<CommandPtr>&& commands);
 
 /**
  * Runs a group of commands at the same time. Ends once all commands in the
  * group finish.
  */
-template <typename... Args>
-[[nodiscard]] CommandPtr Parallel(Args&&... commands) {
-  return Parallel(impl::MakeVector(std::forward<Args>(commands)...));
+template <std::convertible_to<CommandPtr>... CommandPtrs>
+[[nodiscard]]
+CommandPtr Parallel(CommandPtrs&&... commands) {
+  return Parallel(impl::MakeVector(std::forward<CommandPtrs>(commands)...));
 }
 
 /**
  * Runs a group of commands at the same time. Ends once any command in the group
  * finishes, and cancels the others.
  */
-[[nodiscard]] CommandPtr Race(std::vector<CommandPtr>&& commands);
+[[nodiscard]]
+CommandPtr Race(std::vector<CommandPtr>&& commands);
 
 /**
  * Runs a group of commands at the same time. Ends once any command in the group
  * finishes, and cancels the others.
  */
-template <typename... Args>
-[[nodiscard]] CommandPtr Race(Args&&... commands) {
-  return Race(impl::MakeVector(std::forward<Args>(commands)...));
+template <std::convertible_to<CommandPtr>... CommandPtrs>
+[[nodiscard]]
+CommandPtr Race(CommandPtrs&&... commands) {
+  return Race(impl::MakeVector(std::forward<CommandPtrs>(commands)...));
 }
 
 /**
  * Runs a group of commands at the same time. Ends once a specific command
  * finishes, and cancels the others.
  */
-[[nodiscard]] CommandPtr Deadline(CommandPtr&& deadline,
-                                  std::vector<CommandPtr>&& others);
+[[nodiscard]]
+CommandPtr Deadline(CommandPtr&& deadline, std::vector<CommandPtr>&& others);
 
 /**
  * Runs a group of commands at the same time. Ends once a specific command
  * finishes, and cancels the others.
  */
-template <typename... Args>
-[[nodiscard]] CommandPtr Deadline(CommandPtr&& deadline, Args&&... commands) {
+template <std::convertible_to<CommandPtr>... CommandPtrs>
+[[nodiscard]]
+CommandPtr Deadline(CommandPtr&& deadline, CommandPtrs&&... commands) {
   return Deadline(std::move(deadline),
-                  impl::MakeVector(std::forward<Args>(commands)...));
+                  impl::MakeVector(std::forward<CommandPtrs>(commands)...));
 }
 
 }  // namespace cmd
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ConditionalCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ConditionalCommand.h
index 5957950..676ee0c 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ConditionalCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ConditionalCommand.h
@@ -4,11 +4,12 @@
 
 #pragma once
 
+#include <concepts>
 #include <functional>
 #include <memory>
 #include <utility>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
 
 namespace frc2 {
@@ -25,8 +26,7 @@
  *
  * @see ScheduleCommand
  */
-class ConditionalCommand
-    : public CommandHelper<CommandBase, ConditionalCommand> {
+class ConditionalCommand : public CommandHelper<Command, ConditionalCommand> {
  public:
   /**
    * Creates a new ConditionalCommand.
@@ -35,16 +35,14 @@
    * @param onFalse   the command to run if the condition is false
    * @param condition the condition to determine which command to run
    */
-  template <class T1, class T2,
-            typename = std::enable_if_t<
-                std::is_base_of_v<Command, std::remove_reference_t<T1>>>,
-            typename = std::enable_if_t<
-                std::is_base_of_v<Command, std::remove_reference_t<T2>>>>
-  ConditionalCommand(T1&& onTrue, T2&& onFalse, std::function<bool()> condition)
-      : ConditionalCommand(std::make_unique<std::remove_reference_t<T1>>(
-                               std::forward<T1>(onTrue)),
-                           std::make_unique<std::remove_reference_t<T2>>(
-                               std::forward<T2>(onFalse)),
+  template <std::derived_from<Command> Command1,
+            std::derived_from<Command> Command2>
+  ConditionalCommand(Command1&& onTrue, Command2&& onFalse,
+                     std::function<bool()> condition)
+      : ConditionalCommand(std::make_unique<std::decay_t<Command1>>(
+                               std::forward<Command1>(onTrue)),
+                           std::make_unique<std::decay_t<Command2>>(
+                               std::forward<Command2>(onFalse)),
                            condition) {}
 
   /**
@@ -73,6 +71,8 @@
 
   bool RunsWhenDisabled() const override;
 
+  InterruptionBehavior GetInterruptionBehavior() const override;
+
   void InitSendable(wpi::SendableBuilder& builder) override;
 
  private:
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/DeferredCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/DeferredCommand.h
new file mode 100644
index 0000000..442076d
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/DeferredCommand.h
@@ -0,0 +1,61 @@
+// 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 <span>
+
+#include <wpi/FunctionExtras.h>
+
+#include "frc2/command/Command.h"
+#include "frc2/command/CommandHelper.h"
+#include "frc2/command/PrintCommand.h"
+#include "frc2/command/Requirements.h"
+
+namespace frc2 {
+/**
+ * Defers Command construction to runtime. Runs the command returned by the
+ * supplier when this command is initialized, and ends when it ends. Useful for
+ * performing runtime tasks before creating a new command. If this command is
+ * interrupted, it will cancel the command.
+ *
+ * Note that the supplier <i>must</i> create a new Command each call. For
+ * selecting one of a preallocated set of commands, use SelectCommand.
+ *
+ * <p>This class is provided by the NewCommands VendorDep
+ */
+class DeferredCommand : public CommandHelper<Command, DeferredCommand> {
+ public:
+  /**
+   * Creates a new DeferredCommand that runs the supplied command when
+   * initialized, and ends when it ends. Useful for lazily
+   * creating commands at runtime. The supplier will be called each time this
+   * command is initialized. The supplier <i>must</i> create a new Command each
+   * call.
+   *
+   * @param supplier The command supplier
+   * @param requirements The command requirements.
+   *
+   */
+  DeferredCommand(wpi::unique_function<CommandPtr()> supplier,
+                  Requirements requirements);
+
+  DeferredCommand(DeferredCommand&& other) = default;
+
+  void Initialize() override;
+
+  void Execute() override;
+
+  void End(bool interrupted) override;
+
+  bool IsFinished() override;
+
+  void InitSendable(wpi::SendableBuilder& builder) override;
+
+ private:
+  wpi::unique_function<CommandPtr()> m_supplier;
+  std::unique_ptr<Command> m_command;
+};
+}  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/FunctionalCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/FunctionalCommand.h
index ef2b5b9..ac11765 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/FunctionalCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/FunctionalCommand.h
@@ -5,11 +5,10 @@
 #pragma once
 
 #include <functional>
-#include <initializer_list>
-#include <span>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
+#include "frc2/command/Requirements.h"
 
 namespace frc2 {
 /**
@@ -21,7 +20,7 @@
  *
  * This class is provided by the NewCommands VendorDep
  */
-class FunctionalCommand : public CommandHelper<CommandBase, FunctionalCommand> {
+class FunctionalCommand : public CommandHelper<Command, FunctionalCommand> {
  public:
   /**
    * Creates a new FunctionalCommand.
@@ -37,23 +36,7 @@
                     std::function<void()> onExecute,
                     std::function<void(bool)> onEnd,
                     std::function<bool()> isFinished,
-                    std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Creates a new FunctionalCommand.
-   *
-   * @param onInit       the function to run on command initialization
-   * @param onExecute    the function to run on command execution
-   * @param onEnd        the function to run on command end
-   * @param isFinished   the function that determines whether the command has
-   * finished
-   * @param requirements the subsystems required by this command
-   */
-  FunctionalCommand(std::function<void()> onInit,
-                    std::function<void()> onExecute,
-                    std::function<void(bool)> onEnd,
-                    std::function<bool()> isFinished,
-                    std::span<Subsystem* const> requirements = {});
+                    Requirements requirements = {});
 
   FunctionalCommand(FunctionalCommand&& other) = default;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/InstantCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/InstantCommand.h
index cae0b3e..3ac32ec 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/InstantCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/InstantCommand.h
@@ -5,11 +5,10 @@
 #pragma once
 
 #include <functional>
-#include <initializer_list>
-#include <span>
 
 #include "frc2/command/CommandHelper.h"
 #include "frc2/command/FunctionalCommand.h"
+#include "frc2/command/Requirements.h"
 
 namespace frc2 {
 /**
@@ -28,18 +27,8 @@
    * @param toRun        the Runnable to run
    * @param requirements the subsystems required by this command
    */
-  InstantCommand(std::function<void()> toRun,
-                 std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Creates a new InstantCommand that runs the given Runnable with the given
-   * requirements.
-   *
-   * @param toRun        the Runnable to run
-   * @param requirements the subsystems required by this command
-   */
   explicit InstantCommand(std::function<void()> toRun,
-                          std::span<Subsystem* const> requirements = {});
+                          Requirements requirements = {});
 
   InstantCommand(InstantCommand&& other) = default;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/MecanumControllerCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/MecanumControllerCommand.h
index 8ebdbe0..e189ef1 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/MecanumControllerCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/MecanumControllerCommand.h
@@ -4,9 +4,7 @@
 
 #include <cmath>
 #include <functional>
-#include <initializer_list>
 #include <memory>
-#include <span>
 
 #include <frc/Timer.h>
 #include <frc/controller/HolonomicDriveController.h>
@@ -23,8 +21,9 @@
 #include <units/velocity.h>
 #include <units/voltage.h>
 
-#include "CommandBase.h"
-#include "CommandHelper.h"
+#include "frc2/command/Command.h"
+#include "frc2/command/CommandHelper.h"
+#include "frc2/command/Requirements.h"
 
 #pragma once
 
@@ -51,7 +50,7 @@
  * This class is provided by the NewCommands VendorDep
  */
 class MecanumControllerCommand
-    : public CommandHelper<CommandBase, MecanumControllerCommand> {
+    : public CommandHelper<Command, MecanumControllerCommand> {
  public:
   /**
    * Constructs a new MecanumControllerCommand that when executed will follow
@@ -89,20 +88,20 @@
   MecanumControllerCommand(
       frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
       frc::SimpleMotorFeedforward<units::meters> feedforward,
-      frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-      frc2::PIDController yController,
+      frc::MecanumDriveKinematics kinematics, frc::PIDController xController,
+      frc::PIDController yController,
       frc::ProfiledPIDController<units::radians> thetaController,
       std::function<frc::Rotation2d()> desiredRotation,
       units::meters_per_second_t maxWheelVelocity,
       std::function<frc::MecanumDriveWheelSpeeds()> currentWheelSpeeds,
-      frc2::PIDController frontLeftController,
-      frc2::PIDController rearLeftController,
-      frc2::PIDController frontRightController,
-      frc2::PIDController rearRightController,
+      frc::PIDController frontLeftController,
+      frc::PIDController rearLeftController,
+      frc::PIDController frontRightController,
+      frc::PIDController rearRightController,
       std::function<void(units::volt_t, units::volt_t, units::volt_t,
                          units::volt_t)>
           output,
-      std::initializer_list<Subsystem*> requirements);
+      Requirements requirements = {});
 
   /**
    * Constructs a new MecanumControllerCommand that when executed will follow
@@ -143,123 +142,19 @@
   MecanumControllerCommand(
       frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
       frc::SimpleMotorFeedforward<units::meters> feedforward,
-      frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-      frc2::PIDController yController,
+      frc::MecanumDriveKinematics kinematics, frc::PIDController xController,
+      frc::PIDController yController,
       frc::ProfiledPIDController<units::radians> thetaController,
       units::meters_per_second_t maxWheelVelocity,
       std::function<frc::MecanumDriveWheelSpeeds()> currentWheelSpeeds,
-      frc2::PIDController frontLeftController,
-      frc2::PIDController rearLeftController,
-      frc2::PIDController frontRightController,
-      frc2::PIDController rearRightController,
+      frc::PIDController frontLeftController,
+      frc::PIDController rearLeftController,
+      frc::PIDController frontRightController,
+      frc::PIDController rearRightController,
       std::function<void(units::volt_t, units::volt_t, units::volt_t,
                          units::volt_t)>
           output,
-      std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Constructs a new MecanumControllerCommand that when executed will follow
-   * the provided trajectory. PID control and feedforward are handled
-   * internally. Outputs are scaled from -12 to 12 as a voltage output to the
-   * motor.
-   *
-   * <p>Note: The controllers will *not* set the outputVolts to zero upon
-   * completion of the path this is left to the user, since it is not
-   * appropriate for paths with nonstationary endstates.
-   *
-   * @param trajectory           The trajectory to follow.
-   * @param pose                 A function that supplies the robot pose,
-   *                             provided by the odometry class.
-   * @param feedforward          The feedforward to use for the drivetrain.
-   * @param kinematics           The kinematics for the robot drivetrain.
-   * @param xController          The Trajectory Tracker PID controller
-   *                             for the robot's x position.
-   * @param yController          The Trajectory Tracker PID controller
-   *                             for the robot's y position.
-   * @param thetaController      The Trajectory Tracker PID controller
-   *                             for angle for the robot.
-   * @param desiredRotation      The angle that the robot should be facing.
-   *                             This is sampled at each time step.
-   * @param maxWheelVelocity     The maximum velocity of a drivetrain wheel.
-   * @param frontLeftController  The front left wheel velocity PID.
-   * @param rearLeftController   The rear left wheel velocity PID.
-   * @param frontRightController The front right wheel velocity PID.
-   * @param rearRightController  The rear right wheel velocity PID.
-   * @param currentWheelSpeeds   A MecanumDriveWheelSpeeds object containing
-   *                             the current wheel speeds.
-   * @param output               The output of the velocity PIDs.
-   * @param requirements         The subsystems to require.
-   */
-  MecanumControllerCommand(
-      frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-      frc::SimpleMotorFeedforward<units::meters> feedforward,
-      frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-      frc2::PIDController yController,
-      frc::ProfiledPIDController<units::radians> thetaController,
-      std::function<frc::Rotation2d()> desiredRotation,
-      units::meters_per_second_t maxWheelVelocity,
-      std::function<frc::MecanumDriveWheelSpeeds()> currentWheelSpeeds,
-      frc2::PIDController frontLeftController,
-      frc2::PIDController rearLeftController,
-      frc2::PIDController frontRightController,
-      frc2::PIDController rearRightController,
-      std::function<void(units::volt_t, units::volt_t, units::volt_t,
-                         units::volt_t)>
-          output,
-      std::span<Subsystem* const> requirements = {});
-
-  /**
-   * Constructs a new MecanumControllerCommand that when executed will follow
-   * the provided trajectory. PID control and feedforward are handled
-   * internally. Outputs are scaled from -12 to 12 as a voltage output to the
-   * motor.
-   *
-   * <p>Note: The controllers will *not* set the outputVolts to zero upon
-   * completion of the path this is left to the user, since it is not
-   * appropriate for paths with nonstationary endstates.
-   *
-   * <p>Note 2: The final rotation of the robot will be set to the rotation of
-   * the final pose in the trajectory. The robot will not follow the rotations
-   * from the poses at each timestep. If alternate rotation behavior is desired,
-   * the other constructor with a supplier for rotation should be used.
-   *
-   * @param trajectory           The trajectory to follow.
-   * @param pose                 A function that supplies the robot pose,
-   *                             provided by the odometry class.
-   * @param feedforward          The feedforward to use for the drivetrain.
-   * @param kinematics           The kinematics for the robot drivetrain.
-   * @param xController          The Trajectory Tracker PID controller
-   *                             for the robot's x position.
-   * @param yController          The Trajectory Tracker PID controller
-   *                             for the robot's y position.
-   * @param thetaController      The Trajectory Tracker PID controller
-   *                             for angle for the robot.
-   * @param maxWheelVelocity     The maximum velocity of a drivetrain wheel.
-   * @param frontLeftController  The front left wheel velocity PID.
-   * @param rearLeftController   The rear left wheel velocity PID.
-   * @param frontRightController The front right wheel velocity PID.
-   * @param rearRightController  The rear right wheel velocity PID.
-   * @param currentWheelSpeeds   A MecanumDriveWheelSpeeds object containing
-   *                             the current wheel speeds.
-   * @param output               The output of the velocity PIDs.
-   * @param requirements         The subsystems to require.
-   */
-  MecanumControllerCommand(
-      frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-      frc::SimpleMotorFeedforward<units::meters> feedforward,
-      frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-      frc2::PIDController yController,
-      frc::ProfiledPIDController<units::radians> thetaController,
-      units::meters_per_second_t maxWheelVelocity,
-      std::function<frc::MecanumDriveWheelSpeeds()> currentWheelSpeeds,
-      frc2::PIDController frontLeftController,
-      frc2::PIDController rearLeftController,
-      frc2::PIDController frontRightController,
-      frc2::PIDController rearRightController,
-      std::function<void(units::volt_t, units::volt_t, units::volt_t,
-                         units::volt_t)>
-          output,
-      std::span<Subsystem* const> requirements = {});
+      Requirements requirements = {});
 
   /**
    * Constructs a new MecanumControllerCommand that when executed will follow
@@ -288,8 +183,8 @@
    */
   MecanumControllerCommand(
       frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-      frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-      frc2::PIDController yController,
+      frc::MecanumDriveKinematics kinematics, frc::PIDController xController,
+      frc::PIDController yController,
       frc::ProfiledPIDController<units::radians> thetaController,
       std::function<frc::Rotation2d()> desiredRotation,
       units::meters_per_second_t maxWheelVelocity,
@@ -297,7 +192,7 @@
                          units::meters_per_second_t,
                          units::meters_per_second_t)>
           output,
-      std::initializer_list<Subsystem*> requirements);
+      Requirements requirements);
 
   /**
    * Constructs a new MecanumControllerCommand that when executed will follow
@@ -329,93 +224,15 @@
    */
   MecanumControllerCommand(
       frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-      frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-      frc2::PIDController yController,
+      frc::MecanumDriveKinematics kinematics, frc::PIDController xController,
+      frc::PIDController yController,
       frc::ProfiledPIDController<units::radians> thetaController,
       units::meters_per_second_t maxWheelVelocity,
       std::function<void(units::meters_per_second_t, units::meters_per_second_t,
                          units::meters_per_second_t,
                          units::meters_per_second_t)>
           output,
-      std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Constructs a new MecanumControllerCommand that when executed will follow
-   * the provided trajectory. The user should implement a velocity PID on the
-   * desired output wheel velocities.
-   *
-   * <p>Note: The controllers will *not* set the outputVolts to zero upon
-   * completion of the path - this is left to the user, since it is not
-   * appropriate for paths with nonstationary end-states.
-   *
-   * @param trajectory       The trajectory to follow.
-   * @param pose             A function that supplies the robot pose - use one
-   * of the odometry classes to provide this.
-   * @param kinematics       The kinematics for the robot drivetrain.
-   * @param xController      The Trajectory Tracker PID controller
-   *                         for the robot's x position.
-   * @param yController      The Trajectory Tracker PID controller
-   *                         for the robot's y position.
-   * @param thetaController  The Trajectory Tracker PID controller
-   *                         for angle for the robot.
-   * @param desiredRotation  The angle that the robot should be facing.
-   *                         This is sampled at every time step.
-   * @param maxWheelVelocity The maximum velocity of a drivetrain wheel.
-   * @param output           The output of the position PIDs.
-   * @param requirements     The subsystems to require.
-   */
-  MecanumControllerCommand(
-      frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-      frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-      frc2::PIDController yController,
-      frc::ProfiledPIDController<units::radians> thetaController,
-      std::function<frc::Rotation2d()> desiredRotation,
-      units::meters_per_second_t maxWheelVelocity,
-      std::function<void(units::meters_per_second_t, units::meters_per_second_t,
-                         units::meters_per_second_t,
-                         units::meters_per_second_t)>
-          output,
-      std::span<Subsystem* const> requirements = {});
-
-  /**
-   * Constructs a new MecanumControllerCommand that when executed will follow
-   * the provided trajectory. The user should implement a velocity PID on the
-   * desired output wheel velocities.
-   *
-   * <p>Note: The controllers will *not* set the outputVolts to zero upon
-   * completion of the path - this is left to the user, since it is not
-   * appropriate for paths with nonstationary end-states.
-   *
-   * <p>Note2: The final rotation of the robot will be set to the rotation of
-   * the final pose in the trajectory. The robot will not follow the rotations
-   * from the poses at each timestep. If alternate rotation behavior is desired,
-   * the other constructor with a supplier for rotation should be used.
-   *
-   * @param trajectory       The trajectory to follow.
-   * @param pose             A function that supplies the robot pose - use one
-   * of the odometry classes to provide this.
-   * @param kinematics       The kinematics for the robot drivetrain.
-   * @param xController      The Trajectory Tracker PID controller
-   *                         for the robot's x position.
-   * @param yController      The Trajectory Tracker PID controller
-   *                         for the robot's y position.
-   * @param thetaController  The Trajectory Tracker PID controller
-   *                         for angle for the robot.
-   * @param maxWheelVelocity The maximum velocity of a drivetrain wheel.
-   * @param output           The output of the position PIDs.
-   * @param requirements     The subsystems to require.
-   */
-  MecanumControllerCommand(
-      frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-      frc::MecanumDriveKinematics kinematics, frc2::PIDController xController,
-      frc2::PIDController yController,
-      frc::ProfiledPIDController<units::radians> thetaController,
-      units::meters_per_second_t maxWheelVelocity,
-      std::function<void(units::meters_per_second_t, units::meters_per_second_t,
-                         units::meters_per_second_t,
-                         units::meters_per_second_t)>
-          output,
-      std::span<Subsystem* const> requirements = {});
+      Requirements requirements = {});
 
   void Initialize() override;
 
@@ -433,10 +250,10 @@
   frc::HolonomicDriveController m_controller;
   std::function<frc::Rotation2d()> m_desiredRotation;
   const units::meters_per_second_t m_maxWheelVelocity;
-  std::unique_ptr<frc2::PIDController> m_frontLeftController;
-  std::unique_ptr<frc2::PIDController> m_rearLeftController;
-  std::unique_ptr<frc2::PIDController> m_frontRightController;
-  std::unique_ptr<frc2::PIDController> m_rearRightController;
+  std::unique_ptr<frc::PIDController> m_frontLeftController;
+  std::unique_ptr<frc::PIDController> m_rearLeftController;
+  std::unique_ptr<frc::PIDController> m_frontRightController;
+  std::unique_ptr<frc::PIDController> m_rearRightController;
   std::function<frc::MecanumDriveWheelSpeeds()> m_currentWheelSpeeds;
   std::function<void(units::meters_per_second_t, units::meters_per_second_t,
                      units::meters_per_second_t, units::meters_per_second_t)>
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/NotifierCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/NotifierCommand.h
index 607799b..d17867b 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/NotifierCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/NotifierCommand.h
@@ -5,14 +5,13 @@
 #pragma once
 
 #include <functional>
-#include <initializer_list>
-#include <span>
 
 #include <frc/Notifier.h>
 #include <units/time.h>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
+#include "frc2/command/Requirements.h"
 
 namespace frc2 {
 /**
@@ -27,7 +26,7 @@
  *
  * This class is provided by the NewCommands VendorDep
  */
-class NotifierCommand : public CommandHelper<CommandBase, NotifierCommand> {
+class NotifierCommand : public CommandHelper<Command, NotifierCommand> {
  public:
   /**
    * Creates a new NotifierCommand.
@@ -37,17 +36,7 @@
    * @param requirements the subsystems required by this command
    */
   NotifierCommand(std::function<void()> toRun, units::second_t period,
-                  std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Creates a new NotifierCommand.
-   *
-   * @param toRun        the runnable for the notifier to run
-   * @param period       the period at which the notifier should run
-   * @param requirements the subsystems required by this command
-   */
-  NotifierCommand(std::function<void()> toRun, units::second_t period,
-                  std::span<Subsystem* const> requirements = {});
+                  Requirements requirements = {});
 
   NotifierCommand(NotifierCommand&& other);
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PIDCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PIDCommand.h
index 818c50b..bede6d0 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PIDCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PIDCommand.h
@@ -5,13 +5,12 @@
 #pragma once
 
 #include <functional>
-#include <initializer_list>
-#include <span>
 
 #include <frc/controller/PIDController.h>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
+#include "frc2/command/Requirements.h"
 
 namespace frc2 {
 /**
@@ -24,7 +23,7 @@
  *
  * @see PIDController
  */
-class PIDCommand : public CommandHelper<CommandBase, PIDCommand> {
+class PIDCommand : public CommandHelper<Command, PIDCommand> {
  public:
   /**
    * Creates a new PIDCommand, which controls the given output with a
@@ -36,27 +35,11 @@
    * @param useOutput         the controller's output
    * @param requirements      the subsystems required by this command
    */
-  PIDCommand(PIDController controller,
+  PIDCommand(frc::PIDController controller,
              std::function<double()> measurementSource,
              std::function<double()> setpointSource,
              std::function<void(double)> useOutput,
-             std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Creates a new PIDCommand, which controls the given output with a
-   * PIDController.
-   *
-   * @param controller        the controller that controls the output.
-   * @param measurementSource the measurement of the process variable
-   * @param setpointSource   the controller's reference (aka setpoint)
-   * @param useOutput         the controller's output
-   * @param requirements      the subsystems required by this command
-   */
-  PIDCommand(PIDController controller,
-             std::function<double()> measurementSource,
-             std::function<double()> setpointSource,
-             std::function<void(double)> useOutput,
-             std::span<Subsystem* const> requirements = {});
+             Requirements requirements = {});
 
   /**
    * Creates a new PIDCommand, which controls the given output with a
@@ -68,25 +51,10 @@
    * @param useOutput         the controller's output
    * @param requirements      the subsystems required by this command
    */
-  PIDCommand(PIDController controller,
+  PIDCommand(frc::PIDController controller,
              std::function<double()> measurementSource, double setpoint,
              std::function<void(double)> useOutput,
-             std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Creates a new PIDCommand, which controls the given output with a
-   * PIDController with a constant setpoint.
-   *
-   * @param controller        the controller that controls the output.
-   * @param measurementSource the measurement of the process variable
-   * @param setpoint         the controller's setpoint (aka setpoint)
-   * @param useOutput         the controller's output
-   * @param requirements      the subsystems required by this command
-   */
-  PIDCommand(PIDController controller,
-             std::function<double()> measurementSource, double setpoint,
-             std::function<void(double)> useOutput,
-             std::span<Subsystem* const> requirements = {});
+             Requirements requirements = {});
 
   PIDCommand(PIDCommand&& other) = default;
 
@@ -103,10 +71,10 @@
    *
    * @return The PIDController
    */
-  PIDController& GetController();
+  frc::PIDController& GetController();
 
  protected:
-  PIDController m_controller;
+  frc::PIDController m_controller;
   std::function<double()> m_measurement;
   std::function<double()> m_setpoint;
   std::function<void(double)> m_useOutput;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PIDSubsystem.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PIDSubsystem.h
index 426e3ec..af61430 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PIDSubsystem.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PIDSubsystem.h
@@ -25,7 +25,8 @@
    * @param controller the PIDController to use
    * @param initialPosition the initial setpoint of the subsystem
    */
-  explicit PIDSubsystem(PIDController controller, double initialPosition = 0);
+  explicit PIDSubsystem(frc::PIDController controller,
+                        double initialPosition = 0);
 
   void Periodic() override;
 
@@ -65,10 +66,10 @@
    *
    * @return The controller.
    */
-  PIDController& GetController();
+  frc::PIDController& GetController();
 
  protected:
-  PIDController m_controller;
+  frc::PIDController m_controller;
   bool m_enabled{false};
 
   /**
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelCommandGroup.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelCommandGroup.h
index 943b62f..bb33a05 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelCommandGroup.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelCommandGroup.h
@@ -9,11 +9,15 @@
 #pragma warning(disable : 4521)
 #endif
 
+#include <concepts>
 #include <memory>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
-#include "frc2/command/CommandGroupBase.h"
+#include <wpi/DecayedDerivedFrom.h>
+
+#include "frc2/command/CommandBase.h"
 #include "frc2/command/CommandHelper.h"
 
 namespace frc2 {
@@ -29,7 +33,7 @@
  * This class is provided by the NewCommands VendorDep
  */
 class ParallelCommandGroup
-    : public CommandHelper<CommandGroupBase, ParallelCommandGroup> {
+    : public CommandHelper<Command, ParallelCommandGroup> {
  public:
   /**
    * Creates a new ParallelCommandGroup. The given commands will be executed
@@ -50,11 +54,9 @@
    *
    * @param commands the commands to include in this composition.
    */
-  template <class... Types,
-            typename = std::enable_if_t<std::conjunction_v<
-                std::is_base_of<Command, std::remove_reference_t<Types>>...>>>
-  explicit ParallelCommandGroup(Types&&... commands) {
-    AddCommands(std::forward<Types>(commands)...);
+  template <wpi::DecayedDerivedFrom<Command>... Commands>
+  explicit ParallelCommandGroup(Commands&&... commands) {
+    AddCommands(std::forward<Commands>(commands)...);
   }
 
   ParallelCommandGroup(ParallelCommandGroup&& other) = default;
@@ -65,13 +67,16 @@
   // Prevent template expansion from emulating copy ctor
   ParallelCommandGroup(ParallelCommandGroup&) = delete;
 
-  template <class... Types,
-            typename = std::enable_if_t<std::conjunction_v<
-                std::is_base_of<Command, std::remove_reference_t<Types>>...>>>
-  void AddCommands(Types&&... commands) {
+  /**
+   * Adds the given commands to the group.
+   *
+   * @param commands Commands to add to the group.
+   */
+  template <wpi::DecayedDerivedFrom<Command>... Commands>
+  void AddCommands(Commands&&... commands) {
     std::vector<std::unique_ptr<Command>> foo;
-    ((void)foo.emplace_back(std::make_unique<std::remove_reference_t<Types>>(
-         std::forward<Types>(commands))),
+    ((void)foo.emplace_back(std::make_unique<std::decay_t<Commands>>(
+         std::forward<Commands>(commands))),
      ...);
     AddCommands(std::move(foo));
   }
@@ -89,7 +94,7 @@
   Command::InterruptionBehavior GetInterruptionBehavior() const override;
 
  private:
-  void AddCommands(std::vector<std::unique_ptr<Command>>&& commands) final;
+  void AddCommands(std::vector<std::unique_ptr<Command>>&& commands);
 
   std::vector<std::pair<std::unique_ptr<Command>, bool>> m_commands;
   bool m_runWhenDisabled{true};
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelDeadlineGroup.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelDeadlineGroup.h
index cfe4b3e..9e253fb 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelDeadlineGroup.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelDeadlineGroup.h
@@ -9,11 +9,15 @@
 #pragma warning(disable : 4521)
 #endif
 
+#include <concepts>
 #include <memory>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
-#include "frc2/command/CommandGroupBase.h"
+#include <wpi/DecayedDerivedFrom.h>
+
+#include "frc2/command/CommandBase.h"
 #include "frc2/command/CommandHelper.h"
 
 namespace frc2 {
@@ -30,7 +34,7 @@
  * This class is provided by the NewCommands VendorDep
  */
 class ParallelDeadlineGroup
-    : public CommandHelper<CommandGroupBase, ParallelDeadlineGroup> {
+    : public CommandHelper<Command, ParallelDeadlineGroup> {
  public:
   /**
    * Creates a new ParallelDeadlineGroup. The given commands (including the
@@ -54,15 +58,11 @@
    * @param deadline the command that determines when the composition ends
    * @param commands the commands to be executed
    */
-  template <class T, class... Types,
-            typename = std::enable_if_t<
-                std::is_base_of_v<Command, std::remove_reference_t<T>>>,
-            typename = std::enable_if_t<std::conjunction_v<
-                std::is_base_of<Command, std::remove_reference_t<Types>>...>>>
-  explicit ParallelDeadlineGroup(T&& deadline, Types&&... commands) {
-    SetDeadline(std::make_unique<std::remove_reference_t<T>>(
-        std::forward<T>(deadline)));
-    AddCommands(std::forward<Types>(commands)...);
+  template <wpi::DecayedDerivedFrom<Command> T,
+            wpi::DecayedDerivedFrom<Command>... Commands>
+  explicit ParallelDeadlineGroup(T&& deadline, Commands&&... commands) {
+    SetDeadline(std::make_unique<std::decay_t<T>>(std::forward<T>(deadline)));
+    AddCommands(std::forward<Commands>(commands)...);
   }
 
   ParallelDeadlineGroup(ParallelDeadlineGroup&& other) = default;
@@ -73,13 +73,16 @@
   // Prevent template expansion from emulating copy ctor
   ParallelDeadlineGroup(ParallelDeadlineGroup&) = delete;
 
-  template <class... Types,
-            typename = std::enable_if_t<std::conjunction_v<
-                std::is_base_of<Command, std::remove_reference_t<Types>>...>>>
-  void AddCommands(Types&&... commands) {
+  /**
+   * Adds the given commands to the group.
+   *
+   * @param commands Commands to add to the group.
+   */
+  template <wpi::DecayedDerivedFrom<Command>... Commands>
+  void AddCommands(Commands&&... commands) {
     std::vector<std::unique_ptr<Command>> foo;
-    ((void)foo.emplace_back(std::make_unique<std::remove_reference_t<Types>>(
-         std::forward<Types>(commands))),
+    ((void)foo.emplace_back(std::make_unique<std::decay_t<Commands>>(
+         std::forward<Commands>(commands))),
      ...);
     AddCommands(std::move(foo));
   }
@@ -99,7 +102,7 @@
   void InitSendable(wpi::SendableBuilder& builder) override;
 
  private:
-  void AddCommands(std::vector<std::unique_ptr<Command>>&& commands) final;
+  void AddCommands(std::vector<std::unique_ptr<Command>>&& commands);
 
   void SetDeadline(std::unique_ptr<Command>&& deadline);
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelRaceGroup.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelRaceGroup.h
index d5412cd..caefa48 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelRaceGroup.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ParallelRaceGroup.h
@@ -9,11 +9,15 @@
 #pragma warning(disable : 4521)
 #endif
 
+#include <concepts>
 #include <memory>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
-#include "frc2/command/CommandGroupBase.h"
+#include <wpi/DecayedDerivedFrom.h>
+
+#include "frc2/command/CommandBase.h"
 #include "frc2/command/CommandHelper.h"
 
 namespace frc2 {
@@ -28,8 +32,7 @@
  *
  * This class is provided by the NewCommands VendorDep
  */
-class ParallelRaceGroup
-    : public CommandHelper<CommandGroupBase, ParallelRaceGroup> {
+class ParallelRaceGroup : public CommandHelper<Command, ParallelRaceGroup> {
  public:
   /**
    * Creates a new ParallelCommandRace. The given commands will be executed
@@ -40,11 +43,9 @@
    */
   explicit ParallelRaceGroup(std::vector<std::unique_ptr<Command>>&& commands);
 
-  template <class... Types,
-            typename = std::enable_if_t<std::conjunction_v<
-                std::is_base_of<Command, std::remove_reference_t<Types>>...>>>
-  explicit ParallelRaceGroup(Types&&... commands) {
-    AddCommands(std::forward<Types>(commands)...);
+  template <wpi::DecayedDerivedFrom<Command>... Commands>
+  explicit ParallelRaceGroup(Commands&&... commands) {
+    AddCommands(std::forward<Commands>(commands)...);
   }
 
   ParallelRaceGroup(ParallelRaceGroup&& other) = default;
@@ -55,11 +56,16 @@
   // Prevent template expansion from emulating copy ctor
   ParallelRaceGroup(ParallelRaceGroup&) = delete;
 
-  template <class... Types>
-  void AddCommands(Types&&... commands) {
+  /**
+   * Adds the given commands to the group.
+   *
+   * @param commands Commands to add to the group.
+   */
+  template <wpi::DecayedDerivedFrom<Command>... Commands>
+  void AddCommands(Commands&&... commands) {
     std::vector<std::unique_ptr<Command>> foo;
-    ((void)foo.emplace_back(std::make_unique<std::remove_reference_t<Types>>(
-         std::forward<Types>(commands))),
+    ((void)foo.emplace_back(std::make_unique<std::decay_t<Commands>>(
+         std::forward<Commands>(commands))),
      ...);
     AddCommands(std::move(foo));
   }
@@ -77,7 +83,7 @@
   Command::InterruptionBehavior GetInterruptionBehavior() const override;
 
  private:
-  void AddCommands(std::vector<std::unique_ptr<Command>>&& commands) final;
+  void AddCommands(std::vector<std::unique_ptr<Command>>&& commands);
 
   std::vector<std::unique_ptr<Command>> m_commands;
   bool m_runWhenDisabled{true};
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PerpetualCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PerpetualCommand.h
deleted file mode 100644
index 297cb64..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/PerpetualCommand.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// 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
-
-#ifdef _WIN32
-#pragma warning(push)
-#pragma warning(disable : 4521)
-#endif
-
-#include <memory>
-#include <utility>
-
-#include "frc2/command/CommandBase.h"
-#include "frc2/command/CommandHelper.h"
-
-namespace frc2 {
-/**
- * A command that runs another command in perpetuity, ignoring that command's
- * end conditions.  While this class does not extend frc2::CommandGroupBase,
- * it is still considered a CommandGroup, as it allows one to compose another
- * command within it; the command instances that are passed to it cannot be
- * added to any other groups, or scheduled individually.
- *
- * <p>As a rule, CommandGroups require the union of the requirements of their
- * component commands.
- *
- * This class is provided by the NewCommands VendorDep
- *
- * @deprecated PerpetualCommand violates the assumption that execute() doesn't
-get called after isFinished() returns true -- an assumption that should be
-valid. This was unsafe/undefined behavior from the start, and RepeatCommand
-provides an easy way to achieve similar end results with slightly different (and
-safe) semantics.
- */
-class PerpetualCommand : public CommandHelper<CommandBase, PerpetualCommand> {
- public:
-  /**
-   * Creates a new PerpetualCommand.  Will run another command in perpetuity,
-   * ignoring that command's end conditions, unless this command itself is
-   * interrupted.
-   *
-   * @param command the command to run perpetually
-   */
-  WPI_DEPRECATED(
-      "PerpetualCommand violates the assumption that execute() doesn't get "
-      "called after isFinished() returns true -- an assumption that should be "
-      "valid."
-      "This was unsafe/undefined behavior from the start, and RepeatCommand "
-      "provides an easy way to achieve similar end results with slightly "
-      "different (and safe) semantics.")
-  explicit PerpetualCommand(std::unique_ptr<Command>&& command);
-  WPI_IGNORE_DEPRECATED
-
-  /**
-   * Creates a new PerpetualCommand.  Will run another command in perpetuity,
-   * ignoring that command's end conditions, unless this command itself is
-   * interrupted.
-   *
-   * @param command the command to run perpetually
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED(
-      "PerpetualCommand violates the assumption that execute() doesn't get "
-      "called after isFinished() returns true -- an assumption that should be "
-      "valid."
-      "This was unsafe/undefined behavior from the start, and RepeatCommand "
-      "provides an easy way to achieve similar end results with slightly "
-      "different (and safe) semantics.")
-  explicit PerpetualCommand(T&& command)
-      : PerpetualCommand(std::make_unique<std::remove_reference_t<T>>(
-            std::forward<T>(command))) {}
-  WPI_UNIGNORE_DEPRECATED
-
-  PerpetualCommand(PerpetualCommand&& other) = default;
-
-  // No copy constructors for command groups
-  PerpetualCommand(const PerpetualCommand& other) = delete;
-
-  // Prevent template expansion from emulating copy ctor
-  PerpetualCommand(PerpetualCommand&) = delete;
-
-  void Initialize() override;
-
-  void Execute() override;
-
-  void End(bool interrupted) override;
-
- private:
-  std::unique_ptr<Command> m_command;
-};
-}  // namespace frc2
-
-#ifdef _WIN32
-#pragma warning(pop)
-#endif
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDCommand.h
index 3fbe726..9ea5db5 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDCommand.h
@@ -5,15 +5,14 @@
 #pragma once
 
 #include <functional>
-#include <initializer_list>
-#include <span>
 #include <utility>
 
 #include <frc/controller/ProfiledPIDController.h>
 #include <units/time.h>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
+#include "frc2/command/Requirements.h"
 
 namespace frc2 {
 /**
@@ -28,7 +27,7 @@
  */
 template <class Distance>
 class ProfiledPIDCommand
-    : public CommandHelper<CommandBase, ProfiledPIDCommand<Distance>> {
+    : public CommandHelper<Command, ProfiledPIDCommand<Distance>> {
   using Distance_t = units::unit_t<Distance>;
   using Velocity =
       units::compound_unit<Distance, units::inverse<units::seconds>>;
@@ -50,29 +49,7 @@
                      std::function<Distance_t()> measurementSource,
                      std::function<State()> goalSource,
                      std::function<void(double, State)> useOutput,
-                     std::initializer_list<Subsystem*> requirements)
-      : m_controller{controller},
-        m_measurement{std::move(measurementSource)},
-        m_goal{std::move(goalSource)},
-        m_useOutput{std::move(useOutput)} {
-    this->AddRequirements(requirements);
-  }
-
-  /**
-   * Creates a new PIDCommand, which controls the given output with a
-   * ProfiledPIDController.
-   *
-   * @param controller        the controller that controls the output.
-   * @param measurementSource the measurement of the process variable
-   * @param goalSource   the controller's goal
-   * @param useOutput         the controller's output
-   * @param requirements      the subsystems required by this command
-   */
-  ProfiledPIDCommand(frc::ProfiledPIDController<Distance> controller,
-                     std::function<Distance_t()> measurementSource,
-                     std::function<State()> goalSource,
-                     std::function<void(double, State)> useOutput,
-                     std::span<Subsystem* const> requirements = {})
+                     Requirements requirements = {})
       : m_controller{controller},
         m_measurement{std::move(measurementSource)},
         m_goal{std::move(goalSource)},
@@ -94,29 +71,7 @@
                      std::function<Distance_t()> measurementSource,
                      std::function<Distance_t()> goalSource,
                      std::function<void(double, State)> useOutput,
-                     std::initializer_list<Subsystem*> requirements)
-      : ProfiledPIDCommand(
-            controller, measurementSource,
-            [goalSource = std::move(goalSource)]() {
-              return State{goalSource(), Velocity_t{0}};
-            },
-            useOutput, requirements) {}
-
-  /**
-   * Creates a new PIDCommand, which controls the given output with a
-   * ProfiledPIDController.
-   *
-   * @param controller        the controller that controls the output.
-   * @param measurementSource the measurement of the process variable
-   * @param goalSource   the controller's goal
-   * @param useOutput         the controller's output
-   * @param requirements      the subsystems required by this command
-   */
-  ProfiledPIDCommand(frc::ProfiledPIDController<Distance> controller,
-                     std::function<Distance_t()> measurementSource,
-                     std::function<Distance_t()> goalSource,
-                     std::function<void(double, State)> useOutput,
-                     std::span<Subsystem* const> requirements = {})
+                     Requirements requirements = {})
       : ProfiledPIDCommand(
             controller, measurementSource,
             [goalSource = std::move(goalSource)]() {
@@ -137,25 +92,7 @@
   ProfiledPIDCommand(frc::ProfiledPIDController<Distance> controller,
                      std::function<Distance_t()> measurementSource, State goal,
                      std::function<void(double, State)> useOutput,
-                     std::initializer_list<Subsystem*> requirements)
-      : ProfiledPIDCommand(
-            controller, measurementSource, [goal] { return goal; }, useOutput,
-            requirements) {}
-
-  /**
-   * Creates a new PIDCommand, which controls the given output with a
-   * ProfiledPIDController with a constant goal.
-   *
-   * @param controller        the controller that controls the output.
-   * @param measurementSource the measurement of the process variable
-   * @param goal         the controller's goal
-   * @param useOutput         the controller's output
-   * @param requirements      the subsystems required by this command
-   */
-  ProfiledPIDCommand(frc::ProfiledPIDController<Distance> controller,
-                     std::function<Distance_t()> measurementSource, State goal,
-                     std::function<void(double, State)> useOutput,
-                     std::span<Subsystem* const> requirements = {})
+                     Requirements requirements = {})
       : ProfiledPIDCommand(
             controller, measurementSource, [goal] { return goal; }, useOutput,
             requirements) {}
@@ -174,26 +111,7 @@
                      std::function<Distance_t()> measurementSource,
                      Distance_t goal,
                      std::function<void(double, State)> useOutput,
-                     std::initializer_list<Subsystem*> requirements)
-      : ProfiledPIDCommand(
-            controller, measurementSource, [goal] { return goal; }, useOutput,
-            requirements) {}
-
-  /**
-   * Creates a new PIDCommand, which controls the given output with a
-   * ProfiledPIDController with a constant goal.
-   *
-   * @param controller        the controller that controls the output.
-   * @param measurementSource the measurement of the process variable
-   * @param goal         the controller's goal
-   * @param useOutput         the controller's output
-   * @param requirements      the subsystems required by this command
-   */
-  ProfiledPIDCommand(frc::ProfiledPIDController<Distance> controller,
-                     std::function<Distance_t()> measurementSource,
-                     Distance_t goal,
-                     std::function<void(double, State)> useOutput,
-                     std::span<Subsystem* const> requirements = {})
+                     Requirements requirements = {})
       : ProfiledPIDCommand(
             controller, measurementSource, [goal] { return goal; }, useOutput,
             requirements) {}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProxyCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProxyCommand.h
index b4a00a5..3b7eecc 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProxyCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProxyCommand.h
@@ -5,13 +5,11 @@
 #pragma once
 
 #include <memory>
-#include <span>
 
 #include <wpi/FunctionExtras.h>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
-#include "frc2/command/SetUtilities.h"
 
 namespace frc2 {
 /**
@@ -21,7 +19,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-class ProxyCommand : public CommandHelper<CommandBase, ProxyCommand> {
+class ProxyCommand : public CommandHelper<Command, ProxyCommand> {
  public:
   /**
    * Creates a new ProxyCommand that schedules the supplied command when
@@ -33,6 +31,15 @@
   explicit ProxyCommand(wpi::unique_function<Command*()> supplier);
 
   /**
+   * Creates a new ProxyCommand that schedules the supplied command when
+   * initialized, and ends when it is no longer scheduled. Useful for lazily
+   * creating commands at runtime.
+   *
+   * @param supplier the command supplier
+   */
+  explicit ProxyCommand(wpi::unique_function<CommandPtr()> supplier);
+
+  /**
    * Creates a new ProxyCommand that schedules the given command when
    * initialized, and ends when it is no longer scheduled.
    *
@@ -57,8 +64,6 @@
 
   void End(bool interrupted) override;
 
-  void Execute() override;
-
   bool IsFinished() override;
 
   void InitSendable(wpi::SendableBuilder& builder) override;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProxyScheduleCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProxyScheduleCommand.h
deleted file mode 100644
index f79b549..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ProxyScheduleCommand.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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 <span>
-
-#include <wpi/SmallVector.h>
-#include <wpi/deprecated.h>
-
-#include "frc2/command/CommandBase.h"
-#include "frc2/command/CommandHelper.h"
-#include "frc2/command/SetUtilities.h"
-
-namespace frc2 {
-/**
- * Schedules the given commands when this command is initialized, and ends when
- * all the commands are no longer scheduled.  Useful for forking off from
- * CommandGroups.  If this command is interrupted, it will cancel all of the
- * commands.
- *
- * This class is provided by the NewCommands VendorDep
- */
-class ProxyScheduleCommand
-    : public CommandHelper<CommandBase, ProxyScheduleCommand> {
- public:
-  /**
-   * Creates a new ProxyScheduleCommand that schedules the given commands when
-   * initialized, and ends when they are all no longer scheduled.
-   *
-   * @param toSchedule the commands to schedule
-   * @deprecated Replace with {@link ProxyCommand},
-   * composing multiple of them in a {@link ParallelRaceGroup} if needed.
-   */
-  WPI_DEPRECATED("Replace with ProxyCommand")
-  explicit ProxyScheduleCommand(std::span<Command* const> toSchedule);
-
-  explicit ProxyScheduleCommand(Command* toSchedule);
-
-  ProxyScheduleCommand(ProxyScheduleCommand&& other) = default;
-
-  void Initialize() override;
-
-  void End(bool interrupted) override;
-
-  void Execute() override;
-
-  bool IsFinished() override;
-
- private:
-  wpi::SmallVector<Command*, 4> m_toSchedule;
-  std::unique_ptr<Command> m_owning;
-  bool m_finished{false};
-};
-}  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RamseteCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RamseteCommand.h
index d375e11..56fb8ff 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RamseteCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RamseteCommand.h
@@ -5,9 +5,7 @@
 #pragma once
 
 #include <functional>
-#include <initializer_list>
 #include <memory>
-#include <span>
 
 #include <frc/Timer.h>
 #include <frc/controller/PIDController.h>
@@ -19,8 +17,9 @@
 #include <units/length.h>
 #include <units/voltage.h>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
+#include "frc2/command/Requirements.h"
 
 namespace frc2 {
 /**
@@ -42,7 +41,7 @@
  * @see RamseteController
  * @see Trajectory
  */
-class RamseteCommand : public CommandHelper<CommandBase, RamseteCommand> {
+class RamseteCommand : public CommandHelper<Command, RamseteCommand> {
  public:
   /**
    * Constructs a new RamseteCommand that, when executed, will follow the
@@ -76,47 +75,10 @@
                  frc::SimpleMotorFeedforward<units::meters> feedforward,
                  frc::DifferentialDriveKinematics kinematics,
                  std::function<frc::DifferentialDriveWheelSpeeds()> wheelSpeeds,
-                 frc2::PIDController leftController,
-                 frc2::PIDController rightController,
+                 frc::PIDController leftController,
+                 frc::PIDController rightController,
                  std::function<void(units::volt_t, units::volt_t)> output,
-                 std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Constructs a new RamseteCommand that, when executed, will follow the
-   * provided trajectory. PID control and feedforward are handled internally,
-   * and outputs are scaled -12 to 12 representing units of volts.
-   *
-   * <p>Note: The controller will *not* set the outputVolts to zero upon
-   * completion of the path - this is left to the user, since it is not
-   * appropriate for paths with nonstationary endstates.
-   *
-   * @param trajectory      The trajectory to follow.
-   * @param pose            A function that supplies the robot pose - use one of
-   * the odometry classes to provide this.
-   * @param controller      The RAMSETE controller used to follow the
-   * trajectory.
-   * @param feedforward     A component for calculating the feedforward for the
-   * drive.
-   * @param kinematics      The kinematics for the robot drivetrain.
-   * @param wheelSpeeds     A function that supplies the speeds of the left
-   * and right sides of the robot drive.
-   * @param leftController  The PIDController for the left side of the robot
-   * drive.
-   * @param rightController The PIDController for the right side of the robot
-   * drive.
-   * @param output          A function that consumes the computed left and right
-   * outputs (in volts) for the robot drive.
-   * @param requirements    The subsystems to require.
-   */
-  RamseteCommand(frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-                 frc::RamseteController controller,
-                 frc::SimpleMotorFeedforward<units::meters> feedforward,
-                 frc::DifferentialDriveKinematics kinematics,
-                 std::function<frc::DifferentialDriveWheelSpeeds()> wheelSpeeds,
-                 frc2::PIDController leftController,
-                 frc2::PIDController rightController,
-                 std::function<void(units::volt_t, units::volt_t)> output,
-                 std::span<Subsystem* const> requirements = {});
+                 Requirements requirements = {});
 
   /**
    * Constructs a new RamseteCommand that, when executed, will follow the
@@ -140,31 +102,7 @@
                  std::function<void(units::meters_per_second_t,
                                     units::meters_per_second_t)>
                      output,
-                 std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Constructs a new RamseteCommand that, when executed, will follow the
-   * provided trajectory. Performs no PID control and calculates no
-   * feedforwards; outputs are the raw wheel speeds from the RAMSETE controller,
-   * and will need to be converted into a usable form by the user.
-   *
-   * @param trajectory      The trajectory to follow.
-   * @param pose            A function that supplies the robot pose - use one of
-   * the odometry classes to provide this.
-   * @param controller      The RAMSETE controller used to follow the
-   * trajectory.
-   * @param kinematics      The kinematics for the robot drivetrain.
-   * @param output          A function that consumes the computed left and right
-   * wheel speeds.
-   * @param requirements    The subsystems to require.
-   */
-  RamseteCommand(frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-                 frc::RamseteController controller,
-                 frc::DifferentialDriveKinematics kinematics,
-                 std::function<void(units::meters_per_second_t,
-                                    units::meters_per_second_t)>
-                     output,
-                 std::span<Subsystem* const> requirements = {});
+                 Requirements requirements = {});
 
   void Initialize() override;
 
@@ -183,8 +121,8 @@
   frc::SimpleMotorFeedforward<units::meters> m_feedforward;
   frc::DifferentialDriveKinematics m_kinematics;
   std::function<frc::DifferentialDriveWheelSpeeds()> m_speeds;
-  std::unique_ptr<frc2::PIDController> m_leftController;
-  std::unique_ptr<frc2::PIDController> m_rightController;
+  std::unique_ptr<frc::PIDController> m_leftController;
+  std::unique_ptr<frc::PIDController> m_rightController;
   std::function<void(units::volt_t, units::volt_t)> m_outputVolts;
   std::function<void(units::meters_per_second_t, units::meters_per_second_t)>
       m_outputVel;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RepeatCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RepeatCommand.h
index 5d353b9..9c1a5d1 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RepeatCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RepeatCommand.h
@@ -9,10 +9,11 @@
 #pragma warning(disable : 4521)
 #endif
 
+#include <concepts>
 #include <memory>
 #include <utility>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
 
 namespace frc2 {
@@ -28,7 +29,7 @@
  *
  * <p>This class is provided by the NewCommands VendorDep
  */
-class RepeatCommand : public CommandHelper<CommandBase, RepeatCommand> {
+class RepeatCommand : public CommandHelper<Command, RepeatCommand> {
  public:
   /**
    * Creates a new RepeatCommand. Will run another command repeatedly,
@@ -44,11 +45,11 @@
    *
    * @param command the command to run repeatedly
    */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
+  template <std::derived_from<Command> T>
+  // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
   explicit RepeatCommand(T&& command)
-      : RepeatCommand(std::make_unique<std::remove_reference_t<T>>(
-            std::forward<T>(command))) {}
+      : RepeatCommand(
+            std::make_unique<std::decay_t<T>>(std::forward<T>(command))) {}
 
   RepeatCommand(RepeatCommand&& other) = default;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Requirements.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Requirements.h
new file mode 100644
index 0000000..968917f
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Requirements.h
@@ -0,0 +1,46 @@
+// 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 <initializer_list>
+#include <span>
+#include <vector>
+
+#include "frc2/command/Subsystem.h"
+
+namespace frc2 {
+
+/**
+ * Represents requirements for a command, which is a set of (pointers to)
+ * subsystems. This class is implicitly convertible from std::initializer_list
+ * and std::span.
+ */
+class Requirements {
+ public:
+  // NOLINTNEXTLINE
+  /*implicit*/ Requirements(std::initializer_list<Subsystem*> requirements)
+      : m_subsystems{requirements.begin(), requirements.end()} {}
+
+  // NOLINTNEXTLINE
+  /*implicit*/ Requirements(std::span<Subsystem* const> requirements)
+      : m_subsystems{requirements.begin(), requirements.end()} {}
+
+  Requirements() = default;
+
+  Requirements(const Requirements&) = default;
+
+  std::vector<Subsystem*>::const_iterator begin() const {
+    return m_subsystems.begin();
+  }
+
+  std::vector<Subsystem*>::const_iterator end() const {
+    return m_subsystems.end();
+  }
+
+ private:
+  std::vector<Subsystem*> m_subsystems;
+};
+
+}  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RunCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RunCommand.h
index 3bfecf7..49e4be5 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RunCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/RunCommand.h
@@ -5,11 +5,10 @@
 #pragma once
 
 #include <functional>
-#include <initializer_list>
-#include <span>
 
 #include "frc2/command/CommandHelper.h"
 #include "frc2/command/FunctionalCommand.h"
+#include "frc2/command/Requirements.h"
 
 namespace frc2 {
 /**
@@ -29,18 +28,8 @@
    * @param toRun        the Runnable to run
    * @param requirements the subsystems to require
    */
-  RunCommand(std::function<void()> toRun,
-             std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Creates a new RunCommand.  The Runnable will be run continuously until the
-   * command ends.  Does not run when disabled.
-   *
-   * @param toRun        the Runnable to run
-   * @param requirements the subsystems to require
-   */
   explicit RunCommand(std::function<void()> toRun,
-                      std::span<Subsystem* const> requirements = {});
+                      Requirements requirements = {});
 
   RunCommand(RunCommand&& other) = default;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ScheduleCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ScheduleCommand.h
index 63cc3b3..3c960db 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ScheduleCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/ScheduleCommand.h
@@ -6,11 +6,10 @@
 
 #include <span>
 
-#include <wpi/SmallVector.h>
+#include <wpi/SmallSet.h>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
-#include "frc2/command/SetUtilities.h"
 
 namespace frc2 {
 /**
@@ -21,7 +20,7 @@
  *
  * This class is provided by the NewCommands VendorDep
  */
-class ScheduleCommand : public CommandHelper<CommandBase, ScheduleCommand> {
+class ScheduleCommand : public CommandHelper<Command, ScheduleCommand> {
  public:
   /**
    * Creates a new ScheduleCommand that schedules the given commands when
@@ -44,6 +43,6 @@
   bool RunsWhenDisabled() const override;
 
  private:
-  wpi::SmallVector<Command*, 4> m_toSchedule;
+  wpi::SmallSet<Command*, 4> m_toSchedule;
 };
 }  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SelectCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SelectCommand.h
index 7079afe..d425d76 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SelectCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SelectCommand.h
@@ -9,23 +9,23 @@
 #pragma warning(disable : 4521)
 #endif
 
+#include <concepts>
+#include <functional>
 #include <memory>
 #include <string>
-#include <type_traits>
 #include <unordered_map>
 #include <utility>
 #include <vector>
 
 #include <wpi/sendable/SendableBuilder.h>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/PrintCommand.h"
 
 namespace frc2 {
 /**
- * A command composition that runs one of a selection of commands, either using
- * a selector and a key to command mapping, or a supplier that returns the
- * command directly at runtime.
+ * A command composition that runs one of a selection of commands using a
+ * selector and a key to command mapping.
  *
  * <p>The rules for command compositions apply: command instances that are
  * passed to it are owned by the composition and cannot be added to any other
@@ -35,7 +35,7 @@
  * This class is provided by the NewCommands VendorDep
  */
 template <typename Key>
-class SelectCommand : public CommandHelper<CommandBase, SelectCommand<Key>> {
+class SelectCommand : public CommandHelper<Command, SelectCommand<Key>> {
  public:
   /**
    * Creates a new SelectCommand.
@@ -43,21 +43,21 @@
    * @param commands the map of commands to choose from
    * @param selector the selector to determine which command to run
    */
-  template <class... Types,
-            typename = std::enable_if_t<std::conjunction_v<
-                std::is_base_of<Command, std::remove_reference_t<Types>>...>>>
+  template <std::derived_from<Command>... Commands>
   explicit SelectCommand(std::function<Key()> selector,
-                         std::pair<Key, Types>... commands)
+                         std::pair<Key, Commands>... commands)
       : m_selector{std::move(selector)} {
     std::vector<std::pair<Key, std::unique_ptr<Command>>> foo;
 
-    ((void)foo.emplace_back(commands.first,
-                            std::make_unique<std::remove_reference_t<Types>>(
-                                std::move(commands.second))),
+    ((void)foo.emplace_back(
+         commands.first,
+         std::make_unique<std::decay_t<Commands>>(std::move(commands.second))),
      ...);
 
+    m_defaultCommand.SetComposed(true);
     for (auto&& command : foo) {
       CommandScheduler::GetInstance().RequireUngrouped(command.second.get());
+      command.second.get()->SetComposed(true);
     }
 
     for (auto&& command : foo) {
@@ -75,8 +75,10 @@
       std::function<Key()> selector,
       std::vector<std::pair<Key, std::unique_ptr<Command>>>&& commands)
       : m_selector{std::move(selector)} {
+    m_defaultCommand.SetComposed(true);
     for (auto&& command : commands) {
       CommandScheduler::GetInstance().RequireUngrouped(command.second.get());
+      command.second.get()->SetComposed(true);
     }
 
     for (auto&& command : commands) {
@@ -96,17 +98,6 @@
   // Prevent template expansion from emulating copy ctor
   SelectCommand(SelectCommand&) = delete;
 
-  /**
-   * Creates a new selectcommand.
-   *
-   * @param toRun a supplier providing the command to run
-   * @deprecated Replace with {@link ProxyCommand},
-   * composing multiple of them in a {@link ParallelRaceGroup} if needed.
-   */
-  WPI_DEPRECATED("Replace with ProxyCommand")
-  explicit SelectCommand(std::function<Command*()> toRun)
-      : m_toRun{std::move(toRun)} {}
-
   SelectCommand(SelectCommand&& other) = default;
 
   void Initialize() override;
@@ -126,7 +117,7 @@
   }
 
   void InitSendable(wpi::SendableBuilder& builder) override {
-    CommandBase::InitSendable(builder);
+    Command::InitSendable(builder);
 
     builder.AddStringProperty(
         "selected",
@@ -148,25 +139,22 @@
  private:
   std::unordered_map<Key, std::unique_ptr<Command>> m_commands;
   std::function<Key()> m_selector;
-  std::function<Command*()> m_toRun;
   Command* m_selectedCommand;
   bool m_runsWhenDisabled = true;
   Command::InterruptionBehavior m_interruptBehavior{
       Command::InterruptionBehavior::kCancelIncoming};
+
+  PrintCommand m_defaultCommand{
+      "SelectCommand selector value does not correspond to any command!"};
 };
 
 template <typename T>
 void SelectCommand<T>::Initialize() {
-  if (m_selector) {
-    auto find = m_commands.find(m_selector());
-    if (find == m_commands.end()) {
-      m_selectedCommand = new PrintCommand(
-          "SelectCommand selector value does not correspond to any command!");
-      return;
-    }
-    m_selectedCommand = find->second.get();
+  auto find = m_commands.find(m_selector());
+  if (find == m_commands.end()) {
+    m_selectedCommand = &m_defaultCommand;
   } else {
-    m_selectedCommand = m_toRun();
+    m_selectedCommand = find->second.get();
   }
   m_selectedCommand->Initialize();
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SequentialCommandGroup.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SequentialCommandGroup.h
index ba85b6a..fdcd1bc 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SequentialCommandGroup.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SequentialCommandGroup.h
@@ -9,14 +9,16 @@
 #pragma warning(disable : 4521)
 #endif
 
+#include <concepts>
 #include <limits>
 #include <memory>
-#include <span>
 #include <type_traits>
 #include <utility>
 #include <vector>
 
-#include "frc2/command/CommandGroupBase.h"
+#include <wpi/DecayedDerivedFrom.h>
+
+#include "frc2/command/CommandBase.h"
 #include "frc2/command/CommandHelper.h"
 
 namespace frc2 {
@@ -34,7 +36,7 @@
  * This class is provided by the NewCommands VendorDep
  */
 class SequentialCommandGroup
-    : public CommandHelper<CommandGroupBase, SequentialCommandGroup> {
+    : public CommandHelper<Command, SequentialCommandGroup> {
  public:
   /**
    * Creates a new SequentialCommandGroup. The given commands will be run
@@ -53,11 +55,9 @@
    *
    * @param commands the commands to include in this composition.
    */
-  template <class... Types,
-            typename = std::enable_if_t<std::conjunction_v<
-                std::is_base_of<Command, std::remove_reference_t<Types>>...>>>
-  explicit SequentialCommandGroup(Types&&... commands) {
-    AddCommands(std::forward<Types>(commands)...);
+  template <wpi::DecayedDerivedFrom<Command>... Commands>
+  explicit SequentialCommandGroup(Commands&&... commands) {
+    AddCommands(std::forward<Commands>(commands)...);
   }
 
   SequentialCommandGroup(SequentialCommandGroup&& other) = default;
@@ -68,13 +68,16 @@
   // Prevent template expansion from emulating copy ctor
   SequentialCommandGroup(SequentialCommandGroup&) = delete;
 
-  template <class... Types,
-            typename = std::enable_if_t<std::conjunction_v<
-                std::is_base_of<Command, std::remove_reference_t<Types>>...>>>
-  void AddCommands(Types&&... commands) {
+  /**
+   * Adds the given commands to the group.
+   *
+   * @param commands Commands to add, in order of execution.
+   */
+  template <wpi::DecayedDerivedFrom<Command>... Commands>
+  void AddCommands(Commands&&... commands) {
     std::vector<std::unique_ptr<Command>> foo;
-    ((void)foo.emplace_back(std::make_unique<std::remove_reference_t<Types>>(
-         std::forward<Types>(commands))),
+    ((void)foo.emplace_back(std::make_unique<std::decay_t<Commands>>(
+         std::forward<Commands>(commands))),
      ...);
     AddCommands(std::move(foo));
   }
@@ -94,7 +97,7 @@
   void InitSendable(wpi::SendableBuilder& builder) override;
 
  private:
-  void AddCommands(std::vector<std::unique_ptr<Command>>&& commands) final;
+  void AddCommands(std::vector<std::unique_ptr<Command>>&& commands);
 
   wpi::SmallVector<std::unique_ptr<Command>, 4> m_commands;
   size_t m_currentCommandIndex{invalid_index};
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SetUtilities.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SetUtilities.h
deleted file mode 100644
index e70e17b..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SetUtilities.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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 <span>
-
-#include <wpi/SmallVector.h>
-
-namespace frc2 {
-template <typename T>
-void SetInsert(wpi::SmallVectorImpl<T*>& vector, std::span<T* const> toAdd) {
-  for (auto addCommand : toAdd) {
-    bool exists = false;
-    for (auto existingCommand : vector) {
-      if (addCommand == existingCommand) {
-        exists = true;
-        break;
-      }
-    }
-    if (!exists) {
-      vector.emplace_back(addCommand);
-    }
-  }
-}
-}  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/StartEndCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/StartEndCommand.h
index b1f56b2..2367be8 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/StartEndCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/StartEndCommand.h
@@ -5,11 +5,10 @@
 #pragma once
 
 #include <functional>
-#include <initializer_list>
-#include <span>
 
 #include "frc2/command/CommandHelper.h"
 #include "frc2/command/FunctionalCommand.h"
+#include "frc2/command/Requirements.h"
 
 namespace frc2 {
 /**
@@ -33,18 +32,7 @@
    * @param requirements the subsystems required by this command
    */
   StartEndCommand(std::function<void()> onInit, std::function<void()> onEnd,
-                  std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Creates a new StartEndCommand.  Will run the given runnables when the
-   * command starts and when it ends.
-   *
-   * @param onInit       the Runnable to run on command init
-   * @param onEnd        the Runnable to run on command end
-   * @param requirements the subsystems required by this command
-   */
-  StartEndCommand(std::function<void()> onInit, std::function<void()> onEnd,
-                  std::span<Subsystem* const> requirements = {});
+                  Requirements requirements = {});
 
   StartEndCommand(StartEndCommand&& other) = default;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Subsystem.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Subsystem.h
index 0c3a2a3..cdac0c0 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Subsystem.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/Subsystem.h
@@ -4,9 +4,12 @@
 
 #pragma once
 
-#include <type_traits>
+#include <concepts>
+#include <functional>
 #include <utility>
 
+#include <wpi/FunctionExtras.h>
+
 #include "frc2/command/CommandScheduler.h"
 
 namespace frc2 {
@@ -65,8 +68,7 @@
    *
    * @param defaultCommand the default command to associate with this subsystem
    */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
+  template <std::derived_from<Command> T>
   void SetDefaultCommand(T&& defaultCommand) {
     CommandScheduler::GetInstance().SetDefaultCommand(
         this, std::forward<T>(defaultCommand));
@@ -84,6 +86,12 @@
   void SetDefaultCommand(CommandPtr&& defaultCommand);
 
   /**
+   * Removes the default command for the subsystem.  This will not cancel the
+   * default command if it is currently running.
+   */
+  void RemoveDefaultCommand();
+
+  /**
    * Gets the default command for this subsystem.  Returns null if no default
    * command is currently associated with the subsystem.
    *
@@ -111,7 +119,8 @@
    *
    * @param action the action to run
    */
-  [[nodiscard]] CommandPtr RunOnce(std::function<void()> action);
+  [[nodiscard]]
+  CommandPtr RunOnce(std::function<void()> action);
 
   /**
    * Constructs a command that runs an action every iteration until interrupted.
@@ -119,7 +128,8 @@
    *
    * @param action the action to run
    */
-  [[nodiscard]] CommandPtr Run(std::function<void()> action);
+  [[nodiscard]]
+  CommandPtr Run(std::function<void()> action);
 
   /**
    * Constructs a command that runs an action once and another action when the
@@ -128,8 +138,8 @@
    * @param start the action to run on start
    * @param end the action to run on interrupt
    */
-  [[nodiscard]] CommandPtr StartEnd(std::function<void()> start,
-                                    std::function<void()> end);
+  [[nodiscard]]
+  CommandPtr StartEnd(std::function<void()> start, std::function<void()> end);
 
   /**
    * Constructs a command that runs an action every iteration until interrupted,
@@ -138,7 +148,17 @@
    * @param run the action to run every iteration
    * @param end the action to run on interrupt
    */
-  [[nodiscard]] CommandPtr RunEnd(std::function<void()> run,
-                                  std::function<void()> end);
+  [[nodiscard]]
+  CommandPtr RunEnd(std::function<void()> run, std::function<void()> end);
+
+  /**
+   * Constructs a DeferredCommand with the provided supplier. This subsystem is
+   * added as a requirement.
+   *
+   * @param supplier the command supplier.
+   * @return the command.
+   */
+  [[nodiscard]]
+  CommandPtr Defer(wpi::unique_function<CommandPtr()> supplier);
 };
 }  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.h
index d51ae1e..ae928b8 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.h
@@ -4,9 +4,7 @@
 
 #include <cmath>
 #include <functional>
-#include <initializer_list>
 #include <memory>
-#include <span>
 
 #include <frc/Timer.h>
 #include <frc/controller/HolonomicDriveController.h>
@@ -21,8 +19,9 @@
 #include <units/time.h>
 #include <units/voltage.h>
 
-#include "CommandBase.h"
-#include "CommandHelper.h"
+#include "frc2/command/Command.h"
+#include "frc2/command/CommandHelper.h"
+#include "frc2/command/Requirements.h"
 
 #pragma once
 
@@ -51,7 +50,7 @@
  */
 template <size_t NumModules>
 class SwerveControllerCommand
-    : public CommandHelper<CommandBase, SwerveControllerCommand<NumModules>> {
+    : public CommandHelper<Command, SwerveControllerCommand<NumModules>> {
   using voltsecondspermeter =
       units::compound_unit<units::voltage::volt, units::second,
                            units::inverse<units::meter>>;
@@ -89,12 +88,12 @@
   SwerveControllerCommand(
       frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
       frc::SwerveDriveKinematics<NumModules> kinematics,
-      frc2::PIDController xController, frc2::PIDController yController,
+      frc::PIDController xController, frc::PIDController yController,
       frc::ProfiledPIDController<units::radians> thetaController,
       std::function<frc::Rotation2d()> desiredRotation,
       std::function<void(std::array<frc::SwerveModuleState, NumModules>)>
           output,
-      std::initializer_list<Subsystem*> requirements);
+      Requirements requirements = {});
 
   /**
    * Constructs a new SwerveControllerCommand that when executed will follow the
@@ -128,86 +127,11 @@
   SwerveControllerCommand(
       frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
       frc::SwerveDriveKinematics<NumModules> kinematics,
-      frc2::PIDController xController, frc2::PIDController yController,
+      frc::PIDController xController, frc::PIDController yController,
       frc::ProfiledPIDController<units::radians> thetaController,
       std::function<void(std::array<frc::SwerveModuleState, NumModules>)>
           output,
-      std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Constructs a new SwerveControllerCommand that when executed will follow the
-   * provided trajectory. This command will not return output voltages but
-   * rather raw module states from the position controllers which need to be put
-   * into a velocity PID.
-   *
-   * <p>Note: The controllers will *not* set the outputVolts to zero upon
-   * completion of the path- this is left to the user, since it is not
-   * appropriate for paths with nonstationary endstates.
-   *
-   *
-   * @param trajectory      The trajectory to follow.
-   * @param pose            A function that supplies the robot pose,
-   *                        provided by the odometry class.
-   * @param kinematics      The kinematics for the robot drivetrain.
-   * @param xController     The Trajectory Tracker PID controller
-   *                        for the robot's x position.
-   * @param yController     The Trajectory Tracker PID controller
-   *                        for the robot's y position.
-   * @param thetaController The Trajectory Tracker PID controller
-   *                        for angle for the robot.
-   * @param desiredRotation The angle that the drivetrain should be
-   *                        facing. This is sampled at each time step.
-   * @param output          The raw output module states from the
-   *                        position controllers.
-   * @param requirements    The subsystems to require.
-   */
-  SwerveControllerCommand(
-      frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-      frc::SwerveDriveKinematics<NumModules> kinematics,
-      frc2::PIDController xController, frc2::PIDController yController,
-      frc::ProfiledPIDController<units::radians> thetaController,
-      std::function<frc::Rotation2d()> desiredRotation,
-      std::function<void(std::array<frc::SwerveModuleState, NumModules>)>
-          output,
-      std::span<Subsystem* const> requirements = {});
-
-  /**
-   * Constructs a new SwerveControllerCommand that when executed will follow the
-   * provided trajectory. This command will not return output voltages but
-   * rather raw module states from the position controllers which need to be put
-   * into a velocity PID.
-   *
-   * <p>Note: The controllers will *not* set the outputVolts to zero upon
-   * completion of the path- this is left to the user, since it is not
-   * appropriate for paths with nonstationary endstates.
-   *
-   * <p>Note 2: The final rotation of the robot will be set to the rotation of
-   * the final pose in the trajectory. The robot will not follow the rotations
-   * from the poses at each timestep. If alternate rotation behavior is desired,
-   * the other constructor with a supplier for rotation should be used.
-   *
-   * @param trajectory      The trajectory to follow.
-   * @param pose            A function that supplies the robot pose,
-   *                        provided by the odometry class.
-   * @param kinematics      The kinematics for the robot drivetrain.
-   * @param xController     The Trajectory Tracker PID controller
-   *                        for the robot's x position.
-   * @param yController     The Trajectory Tracker PID controller
-   *                        for the robot's y position.
-   * @param thetaController The Trajectory Tracker PID controller
-   *                        for angle for the robot.
-   * @param output          The raw output module states from the
-   *                        position controllers.
-   * @param requirements    The subsystems to require.
-   */
-  SwerveControllerCommand(
-      frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-      frc::SwerveDriveKinematics<NumModules> kinematics,
-      frc2::PIDController xController, frc2::PIDController yController,
-      frc::ProfiledPIDController<units::radians> thetaController,
-      std::function<void(std::array<frc::SwerveModuleState, NumModules>)>
-          output,
-      std::span<Subsystem* const> requirements = {});
+      Requirements requirements = {});
 
   /**
    * Constructs a new SwerveControllerCommand that when executed will follow the
@@ -237,7 +161,7 @@
       std::function<frc::Rotation2d()> desiredRotation,
       std::function<void(std::array<frc::SwerveModuleState, NumModules>)>
           output,
-      std::initializer_list<Subsystem*> requirements);
+      Requirements requirements = {});
 
   /**
    * Constructs a new SwerveControllerCommand that when executed will follow the
@@ -269,70 +193,7 @@
       frc::HolonomicDriveController controller,
       std::function<void(std::array<frc::SwerveModuleState, NumModules>)>
           output,
-      std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Constructs a new SwerveControllerCommand that when executed will follow the
-   * provided trajectory. This command will not return output voltages but
-   * rather raw module states from the position controllers which need to be put
-   * into a velocity PID.
-   *
-   * <p>Note: The controllers will *not* set the outputVolts to zero upon
-   * completion of the path- this is left to the user, since it is not
-   * appropriate for paths with nonstationary endstates.
-   *
-   *
-   * @param trajectory      The trajectory to follow.
-   * @param pose            A function that supplies the robot pose,
-   *                        provided by the odometry class.
-   * @param kinematics      The kinematics for the robot drivetrain.
-   * @param controller     The HolonomicDriveController for the robot.
-   * @param desiredRotation The angle that the drivetrain should be
-   *                        facing. This is sampled at each time step.
-   * @param output          The raw output module states from the
-   *                        position controllers.
-   * @param requirements    The subsystems to require.
-   */
-  SwerveControllerCommand(
-      frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-      frc::SwerveDriveKinematics<NumModules> kinematics,
-      frc::HolonomicDriveController controller,
-      std::function<frc::Rotation2d()> desiredRotation,
-      std::function<void(std::array<frc::SwerveModuleState, NumModules>)>
-          output,
-      std::span<Subsystem* const> requirements = {});
-
-  /**
-   * Constructs a new SwerveControllerCommand that when executed will follow the
-   * provided trajectory. This command will not return output voltages but
-   * rather raw module states from the position controllers which need to be put
-   * into a velocity PID.
-   *
-   * <p>Note: The controllers will *not* set the outputVolts to zero upon
-   * completion of the path- this is left to the user, since it is not
-   * appropriate for paths with nonstationary endstates.
-   *
-   * <p>Note 2: The final rotation of the robot will be set to the rotation of
-   * the final pose in the trajectory. The robot will not follow the rotations
-   * from the poses at each timestep. If alternate rotation behavior is desired,
-   * the other constructor with a supplier for rotation should be used.
-   *
-   * @param trajectory      The trajectory to follow.
-   * @param pose            A function that supplies the robot pose,
-   *                        provided by the odometry class.
-   * @param kinematics      The kinematics for the robot drivetrain.
-   * @param controller      The HolonomicDriveController for the drivetrain.
-   * @param output          The raw output module states from the
-   *                        position controllers.
-   * @param requirements    The subsystems to require.
-   */
-  SwerveControllerCommand(
-      frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-      frc::SwerveDriveKinematics<NumModules> kinematics,
-      frc::HolonomicDriveController controller,
-      std::function<void(std::array<frc::SwerveModuleState, NumModules>)>
-          output,
-      std::span<Subsystem* const> requirements = {});
+      Requirements requirements = {});
 
   void Initialize() override;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.inc b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.inc
index a644057..d0c34af 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.inc
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.inc
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <utility>
 
@@ -15,11 +16,11 @@
 SwerveControllerCommand<NumModules>::SwerveControllerCommand(
     frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
     frc::SwerveDriveKinematics<NumModules> kinematics,
-    frc2::PIDController xController, frc2::PIDController yController,
+    frc::PIDController xController, frc::PIDController yController,
     frc::ProfiledPIDController<units::radians> thetaController,
     std::function<frc::Rotation2d()> desiredRotation,
     std::function<void(std::array<frc::SwerveModuleState, NumModules>)> output,
-    std::initializer_list<Subsystem*> requirements)
+    Requirements requirements)
     : m_trajectory(std::move(trajectory)),
       m_pose(std::move(pose)),
       m_kinematics(kinematics),
@@ -33,44 +34,10 @@
 SwerveControllerCommand<NumModules>::SwerveControllerCommand(
     frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
     frc::SwerveDriveKinematics<NumModules> kinematics,
-    frc2::PIDController xController, frc2::PIDController yController,
+    frc::PIDController xController, frc::PIDController yController,
     frc::ProfiledPIDController<units::radians> thetaController,
     std::function<void(std::array<frc::SwerveModuleState, NumModules>)> output,
-    std::initializer_list<Subsystem*> requirements)
-    : m_trajectory(std::move(trajectory)),
-      m_pose(std::move(pose)),
-      m_kinematics(kinematics),
-      m_controller(xController, yController, thetaController),
-      m_outputStates(output) {
-  this->AddRequirements(requirements);
-}
-
-template <size_t NumModules>
-SwerveControllerCommand<NumModules>::SwerveControllerCommand(
-    frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::SwerveDriveKinematics<NumModules> kinematics,
-    frc2::PIDController xController, frc2::PIDController yController,
-    frc::ProfiledPIDController<units::radians> thetaController,
-    std::function<frc::Rotation2d()> desiredRotation,
-    std::function<void(std::array<frc::SwerveModuleState, NumModules>)> output,
-    std::span<Subsystem* const> requirements)
-    : m_trajectory(std::move(trajectory)),
-      m_pose(std::move(pose)),
-      m_kinematics(kinematics),
-      m_controller(xController, yController, thetaController),
-      m_desiredRotation(std::move(desiredRotation)),
-      m_outputStates(output) {
-  this->AddRequirements(requirements);
-}
-
-template <size_t NumModules>
-SwerveControllerCommand<NumModules>::SwerveControllerCommand(
-    frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::SwerveDriveKinematics<NumModules> kinematics,
-    frc2::PIDController xController, frc2::PIDController yController,
-    frc::ProfiledPIDController<units::radians> thetaController,
-    std::function<void(std::array<frc::SwerveModuleState, NumModules>)> output,
-    std::span<Subsystem* const> requirements)
+    Requirements requirements)
     : m_trajectory(std::move(trajectory)),
       m_pose(std::move(pose)),
       m_kinematics(kinematics),
@@ -86,7 +53,7 @@
     frc::HolonomicDriveController controller,
     std::function<frc::Rotation2d()> desiredRotation,
     std::function<void(std::array<frc::SwerveModuleState, NumModules>)> output,
-    std::initializer_list<Subsystem*> requirements)
+    Requirements requirements)
     : m_trajectory(std::move(trajectory)),
       m_pose(std::move(pose)),
       m_kinematics(kinematics),
@@ -102,39 +69,7 @@
     frc::SwerveDriveKinematics<NumModules> kinematics,
     frc::HolonomicDriveController controller,
     std::function<void(std::array<frc::SwerveModuleState, NumModules>)> output,
-    std::initializer_list<Subsystem*> requirements)
-    : m_trajectory(std::move(trajectory)),
-      m_pose(std::move(pose)),
-      m_kinematics(kinematics),
-      m_controller(std::move(controller)),
-      m_outputStates(output) {
-  this->AddRequirements(requirements);
-}
-
-template <size_t NumModules>
-SwerveControllerCommand<NumModules>::SwerveControllerCommand(
-    frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::SwerveDriveKinematics<NumModules> kinematics,
-    frc::HolonomicDriveController controller,
-    std::function<frc::Rotation2d()> desiredRotation,
-    std::function<void(std::array<frc::SwerveModuleState, NumModules>)> output,
-    std::span<Subsystem* const> requirements)
-    : m_trajectory(std::move(trajectory)),
-      m_pose(std::move(pose)),
-      m_kinematics(kinematics),
-      m_controller(std::move(controller)),
-      m_desiredRotation(std::move(desiredRotation)),
-      m_outputStates(output) {
-  this->AddRequirements(requirements);
-}
-
-template <size_t NumModules>
-SwerveControllerCommand<NumModules>::SwerveControllerCommand(
-    frc::Trajectory trajectory, std::function<frc::Pose2d()> pose,
-    frc::SwerveDriveKinematics<NumModules> kinematics,
-    frc::HolonomicDriveController controller,
-    std::function<void(std::array<frc::SwerveModuleState, NumModules>)> output,
-    std::span<Subsystem* const> requirements)
+    Requirements requirements)
     : m_trajectory(std::move(trajectory)),
       m_pose(std::move(pose)),
       m_kinematics(kinematics),
@@ -150,8 +85,7 @@
       return m_trajectory.States().back().pose.Rotation();
     };
   }
-  m_timer.Reset();
-  m_timer.Start();
+  m_timer.Restart();
 }
 
 template <size_t NumModules>
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileCommand.h
index 413553b..69cc8d8 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileCommand.h
@@ -5,14 +5,13 @@
 #pragma once
 
 #include <functional>
-#include <initializer_list>
-#include <span>
 
 #include <frc/Timer.h>
 #include <frc/trajectory/TrapezoidProfile.h>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
+#include "frc2/command/Requirements.h"
 
 namespace frc2 {
 /**
@@ -25,7 +24,7 @@
  */
 template <class Distance>
 class TrapezoidProfileCommand
-    : public CommandHelper<CommandBase, TrapezoidProfileCommand<Distance>> {
+    : public CommandHelper<Command, TrapezoidProfileCommand<Distance>> {
   using Distance_t = units::unit_t<Distance>;
   using Velocity =
       units::compound_unit<Distance, units::inverse<units::seconds>>;
@@ -39,13 +38,21 @@
    *
    * @param profile      The motion profile to execute.
    * @param output       The consumer for the profile output.
+   * @param goal The supplier for the desired state
+   * @param currentState The current state
    * @param requirements The list of requirements.
    */
   TrapezoidProfileCommand(frc::TrapezoidProfile<Distance> profile,
                           std::function<void(State)> output,
-                          std::initializer_list<Subsystem*> requirements)
-      : m_profile(profile), m_output(output) {
+                          std::function<State()> goal,
+                          std::function<State()> currentState,
+                          Requirements requirements = {})
+      : m_profile(profile),
+        m_output(output),
+        m_goal(goal),
+        m_currentState(currentState) {
     this->AddRequirements(requirements);
+    m_newAPI = true;
   }
 
   /**
@@ -55,20 +62,25 @@
    * @param profile      The motion profile to execute.
    * @param output       The consumer for the profile output.
    * @param requirements The list of requirements.
+   * @deprecated The new constructor allows you to pass in a supplier for
+   * desired and current state. This allows you to change goals at runtime.
    */
+  WPI_DEPRECATED(
+      "The new constructor allows you to pass in a supplier for desired and "
+      "current state. This allows you to change goals at runtime.")
   TrapezoidProfileCommand(frc::TrapezoidProfile<Distance> profile,
                           std::function<void(State)> output,
-                          std::span<Subsystem* const> requirements = {})
+                          Requirements requirements = {})
       : m_profile(profile), m_output(output) {
     this->AddRequirements(requirements);
+    m_newAPI = false;
   }
 
-  void Initialize() override {
-    m_timer.Reset();
-    m_timer.Start();
-  }
+  void Initialize() override { m_timer.Restart(); }
 
-  void Execute() override { m_output(m_profile.Calculate(m_timer.Get())); }
+  void Execute() override {
+    m_output(m_profile.Calculate(m_timer.Get(), m_goal(), m_currentState()));
+  }
 
   void End(bool interrupted) override { m_timer.Stop(); }
 
@@ -79,7 +91,9 @@
  private:
   frc::TrapezoidProfile<Distance> m_profile;
   std::function<void(State)> m_output;
-
+  std::function<State()> m_goal;
+  std::function<State()> m_currentState;
+  bool m_newAPI;  // TODO: Remove
   frc::Timer m_timer;
 };
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileSubsystem.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileSubsystem.h
index abce33c..344aefc 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileSubsystem.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileSubsystem.h
@@ -39,15 +39,13 @@
   explicit TrapezoidProfileSubsystem(Constraints constraints,
                                      Distance_t initialPosition = Distance_t{0},
                                      units::second_t period = 20_ms)
-      : m_constraints(constraints),
+      : m_profile(constraints),
         m_state{initialPosition, Velocity_t(0)},
         m_goal{initialPosition, Velocity_t{0}},
         m_period(period) {}
 
   void Periodic() override {
-    auto profile =
-        frc::TrapezoidProfile<Distance>(m_constraints, m_goal, m_state);
-    m_state = profile.Calculate(m_period);
+    m_state = m_profile.Calculate(m_period, m_goal, m_state);
     if (m_enabled) {
       UseState(m_state);
     }
@@ -87,7 +85,7 @@
   void Disable() { m_enabled = false; }
 
  private:
-  Constraints m_constraints;
+  frc::TrapezoidProfile<Distance> m_profile;
   State m_state;
   State m_goal;
   units::second_t m_period;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WaitCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WaitCommand.h
index 54eddfb..e28615e 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WaitCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WaitCommand.h
@@ -7,18 +7,16 @@
 #include <frc/Timer.h>
 #include <units/time.h>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
 
 namespace frc2 {
 /**
  * A command that does nothing but takes a specified amount of time to finish.
- * Useful for CommandGroups.  Can also be subclassed to make a command with an
- * internal timer.
  *
  * This class is provided by the NewCommands VendorDep
  */
-class WaitCommand : public CommandHelper<CommandBase, WaitCommand> {
+class WaitCommand : public CommandHelper<Command, WaitCommand> {
  public:
   /**
    * Creates a new WaitCommand.  This command will do nothing, and end after the
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WaitUntilCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WaitUntilCommand.h
index 3b26999..377a1ba 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WaitUntilCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WaitUntilCommand.h
@@ -8,7 +8,7 @@
 
 #include <units/time.h>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
 
 namespace frc2 {
@@ -18,7 +18,7 @@
  *
  * This class is provided by the NewCommands VendorDep
  */
-class WaitUntilCommand : public CommandHelper<CommandBase, WaitUntilCommand> {
+class WaitUntilCommand : public CommandHelper<Command, WaitUntilCommand> {
  public:
   /**
    * Creates a new WaitUntilCommand that ends after a given condition becomes
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WrapperCommand.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WrapperCommand.h
index 71dd02f..98e8b20 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WrapperCommand.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/WrapperCommand.h
@@ -9,10 +9,11 @@
 #pragma warning(disable : 4521)
 #endif
 
+#include <concepts>
 #include <memory>
 #include <utility>
 
-#include "frc2/command/CommandBase.h"
+#include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
 
 namespace frc2 {
@@ -23,7 +24,7 @@
  * <p>Wrapped commands may only be used through the wrapper, trying to directly
  * schedule them or add them to a group will throw an exception.
  */
-class WrapperCommand : public CommandHelper<CommandBase, WrapperCommand> {
+class WrapperCommand : public CommandHelper<Command, WrapperCommand> {
  public:
   /**
    * Wrap a command.
@@ -39,11 +40,11 @@
    * @param command the command being wrapped. Trying to directly schedule this
    * command or add it to a group will throw an exception.
    */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
+  template <std::derived_from<Command> T>
+  // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
   explicit WrapperCommand(T&& command)
-      : WrapperCommand(std::make_unique<std::remove_reference_t<T>>(
-            std::forward<T>(command))) {}
+      : WrapperCommand(
+            std::make_unique<std::decay_t<T>>(std::forward<T>(command))) {}
 
   WrapperCommand(WrapperCommand&& other) = default;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/Button.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/Button.h
deleted file mode 100644
index 0142c44..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/Button.h
+++ /dev/null
@@ -1,274 +0,0 @@
-// 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 <functional>
-#include <initializer_list>
-#include <span>
-#include <utility>
-
-#include <wpi/deprecated.h>
-
-#include "Trigger.h"
-#include "frc2/command/CommandPtr.h"
-
-namespace frc2 {
-class Command;
-/**
- * A class used to bind command scheduling to button presses.  Can be composed
- * with other buttons with the operators in Trigger.
- *
- * This class is provided by the NewCommands VendorDep
- *
- * @see Trigger
- */
-class Button : public Trigger {
- public:
-  /**
-   * Create a new button that is pressed when the given condition is true.
-   *
-   * @param isPressed Whether the button is pressed.
-   * @deprecated Replace with Trigger
-   */
-  WPI_DEPRECATED("Replace with Trigger")
-  explicit Button(std::function<bool()> isPressed);
-
-  /**
-   * Create a new button that is pressed active (default constructor) - activity
-   *  can be further determined by subclass code.
-   * @deprecated Replace with Trigger
-   */
-  WPI_DEPRECATED("Replace with Trigger")
-  Button() = default;
-
-  /**
-   * Binds a command to start when the button is pressed.  Takes a
-   * raw pointer, and so is non-owning; users are responsible for the lifespan
-   * of the command.
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Replace with Trigger::OnTrue()
-   */
-  WPI_DEPRECATED("Replace with Trigger#OnTrue()")
-  Button WhenPressed(Command* command);
-
-  /**
-   * Binds a command to start when the button is pressed.  Transfers
-   * command ownership to the button scheduler, so the user does not have to
-   * worry about lifespan - rvalue refs will be *moved*, lvalue refs will be
-   * *copied.*
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Replace with Trigger::OnTrue()
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED("Replace with Trigger#OnTrue()")
-  Button WhenPressed(T&& command) {
-    WhenActive(std::forward<T>(command));
-    return *this;
-  }
-
-  /**
-   * Binds a runnable to execute when the button is pressed.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Replace with Trigger::OnTrue(cmd::RunOnce())
-   */
-  WPI_DEPRECATED("Replace with Trigger#OnTrue(cmd::RunOnce())")
-  Button WhenPressed(std::function<void()> toRun,
-                     std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Binds a runnable to execute when the button is pressed.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Replace with Trigger::OnTrue(cmd::RunOnce())
-   */
-  WPI_DEPRECATED("Replace with Trigger#OnTrue(cmd::RunOnce())")
-  Button WhenPressed(std::function<void()> toRun,
-                     std::span<Subsystem* const> requirements = {});
-
-  /**
-   * Binds a command to be started repeatedly while the button is pressed, and
-   * canceled when it is released.  Takes a raw pointer, and so is non-owning;
-   * users are responsible for the lifespan of the command.
-   *
-   * @param command The command to bind.
-   * @return The button, for chained calls.
-   * @deprecated Replace with Trigger::WhileTrue(command.Repeatedly())
-   */
-  WPI_DEPRECATED("Replace with Trigger#WhileTrue(command.Repeatedly())")
-  Button WhileHeld(Command* command);
-
-  /**
-   * Binds a command to be started repeatedly while the button is pressed, and
-   * canceled when it is released.  Transfers command ownership to the button
-   * scheduler, so the user does not have to worry about lifespan - rvalue refs
-   * will be *moved*, lvalue refs will be *copied.*
-   *
-   * @param command The command to bind.
-   * @return The button, for chained calls.
-   * @deprecated Replace with Trigger::WhileTrue(command.Repeatedly())
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED("Replace with Trigger#WhileTrue(command.Repeatedly())")
-  Button WhileHeld(T&& command) {
-    WhileActiveContinous(std::forward<T>(command));
-    return *this;
-  }
-
-  /**
-   * Binds a runnable to execute repeatedly while the button is pressed.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Replace with Trigger::WhileTrue(cmd::Run())
-   */
-  WPI_DEPRECATED("Replace with Trigger#WhileTrue(cmd::Run())")
-  Button WhileHeld(std::function<void()> toRun,
-                   std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Binds a runnable to execute repeatedly while the button is pressed.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Replace with Trigger::WhileTrue(cmd::Run())
-   */
-  WPI_DEPRECATED("Replace with Trigger#WhileTrue(cmd::Run())")
-  Button WhileHeld(std::function<void()> toRun,
-                   std::span<Subsystem* const> requirements = {});
-
-  /**
-   * Binds a command to be started when the button is pressed, and canceled
-   * when it is released.  Takes a raw pointer, and so is non-owning; users are
-   * responsible for the lifespan of the command.
-   *
-   * @param command The command to bind.
-   * @return The button, for chained calls.
-   * @deprecated Replace with Trigger::WhileTrue()
-   */
-  WPI_DEPRECATED("Replace with Trigger#WhileTrue()")
-  Button WhenHeld(Command* command);
-
-  /**
-   * Binds a command to be started when the button is pressed, and canceled
-   * when it is released.  Transfers command ownership to the button scheduler,
-   * so the user does not have to worry about lifespan - rvalue refs will be
-   * *moved*, lvalue refs will be *copied.*
-   *
-   * @param command The command to bind.
-   * @return The button, for chained calls.
-   * @deprecated Replace with Trigger::WhileTrue()
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED("Replace with Trigger#WhileTrue()")
-  Button WhenHeld(T&& command) {
-    WhileActiveOnce(std::forward<T>(command));
-    return *this;
-  }
-
-  /**
-   * Binds a command to start when the button is released.  Takes a
-   * raw pointer, and so is non-owning; users are responsible for the lifespan
-   * of the command.
-   *
-   * @param command The command to bind.
-   * @return The button, for chained calls.
-   * @deprecated Replace with Trigger::OnFalse()
-   */
-  WPI_DEPRECATED("Replace with Trigger#OnFalse()")
-  Button WhenReleased(Command* command);
-
-  /**
-   * Binds a command to start when the button is pressed.  Transfers
-   * command ownership to the button scheduler, so the user does not have to
-   * worry about lifespan - rvalue refs will be *moved*, lvalue refs will be
-   * *copied.*
-   *
-   * @param command The command to bind.
-   * @return The button, for chained calls.
-   * @deprecated Replace with Trigger::OnFalse()
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED("Replace with Trigger#OnFalse()")
-  Button WhenReleased(T&& command) {
-    WhenInactive(std::forward<T>(command));
-    return *this;
-  }
-
-  /**
-   * Binds a runnable to execute when the button is released.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Replace with Trigger::OnFalse(cmd::RunOnce())
-   */
-  WPI_DEPRECATED("Replace with Trigger#OnFalse(cmd::RunOnce())")
-  Button WhenReleased(std::function<void()> toRun,
-                      std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Binds a runnable to execute when the button is released.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Replace with Trigger::OnFalse(cmd::RunOnce())
-   */
-  WPI_DEPRECATED("Replace with Trigger#OnFalse(cmd::RunOnce())")
-  Button WhenReleased(std::function<void()> toRun,
-                      std::span<Subsystem* const> requirements = {});
-
-  /**
-   * Binds a command to start when the button is pressed, and be canceled when
-   * it is pressed again.  Takes a raw pointer, and so is non-owning; users are
-   * responsible for the lifespan of the command.
-   *
-   * @param command The command to bind.
-   * @return The button, for chained calls.
-   * @deprecated Replace with Trigger::ToggleOnTrue()
-   */
-  WPI_DEPRECATED("Replace with Trigger#ToggleOnTrue()")
-  Button ToggleWhenPressed(Command* command);
-
-  /**
-   * Binds a command to start when the button is pressed, and be canceled when
-   * it is pressed again.  Transfers command ownership to the button scheduler,
-   * so the user does not have to worry about lifespan - rvalue refs will be
-   * *moved*, lvalue refs will be *copied.*
-   *
-   * @param command The command to bind.
-   * @return The button, for chained calls.
-   * @deprecated Replace with Trigger::ToggleOnTrue()
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED("Replace with Trigger#ToggleOnTrue()")
-  Button ToggleWhenPressed(T&& command) {
-    ToggleWhenActive(std::forward<T>(command));
-    return *this;
-  }
-
-  /**
-   * Binds a command to be canceled when the button is pressed.  Takes a
-   * raw pointer, and so is non-owning; users are responsible for the lifespan
-   *  and scheduling of the command.
-   *
-   * @param command The command to bind.
-   * @return The button, for chained calls.
-   * @deprecated Pass this as a command end condition with Until() instead.
-   */
-  WPI_DEPRECATED("Pass this as a command end condition with Until() instead.")
-  Button CancelWhenPressed(Command* command);
-};
-}  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandJoystick.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandJoystick.h
index c335f89..5b51e86 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandJoystick.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandJoystick.h
@@ -10,7 +10,7 @@
 
 namespace frc2 {
 /**
- * A subclass of {@link Joystick} with {@link Trigger} factories for
+ * A version of {@link Joystick} with {@link Trigger} factories for
  * command-based.
  *
  * @see Joystick
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandPS4Controller.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandPS4Controller.h
index befb667..22c54ad 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandPS4Controller.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandPS4Controller.h
@@ -10,7 +10,7 @@
 
 namespace frc2 {
 /**
- * A subclass of {@link PS4Controller} with {@link Trigger} factories for
+ * A version of {@link PS4Controller} with {@link Trigger} factories for
  * command-based.
  *
  * @see PS4Controller
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandPS5Controller.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandPS5Controller.h
new file mode 100644
index 0000000..d86d383
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandPS5Controller.h
@@ -0,0 +1,178 @@
+// 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 <frc/PS5Controller.h>
+
+#include "Trigger.h"
+#include "frc2/command/CommandScheduler.h"
+
+namespace frc2 {
+/**
+ * A version of {@link PS5Controller} with {@link Trigger} factories for
+ * command-based.
+ *
+ * @see PS5Controller
+ */
+class CommandPS5Controller : public frc::PS5Controller {
+ public:
+  using PS5Controller::PS5Controller;
+
+  /**
+   * Constructs an event instance around this button's digital signal.
+   *
+   * @param button the button index
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the button's digital signal attached
+   * to the given loop.
+   */
+  Trigger Button(int button,
+                 frc::EventLoop* loop = CommandScheduler::GetInstance()
+                                            .GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the square button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the square button's digital signal
+   * attached to the given loop.
+   */
+  Trigger Square(frc::EventLoop* loop = CommandScheduler::GetInstance()
+                                            .GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the cross button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the cross button's digital signal
+   * attached to the given loop.
+   */
+  Trigger Cross(frc::EventLoop* loop = CommandScheduler::GetInstance()
+                                           .GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the circle button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the circle button's digital signal
+   * attached to the given loop.
+   */
+  Trigger Circle(frc::EventLoop* loop = CommandScheduler::GetInstance()
+                                            .GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the triangle button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the triangle button's digital signal
+   * attached to the given loop.
+   */
+  Trigger Triangle(frc::EventLoop* loop = CommandScheduler::GetInstance()
+                                              .GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the L1 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the L1 button's digital signal
+   * attached to the given loop.
+   */
+  Trigger L1(frc::EventLoop* loop =
+                 CommandScheduler::GetInstance().GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the R1 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the R1 button's digital signal
+   * attached to the given loop.
+   */
+  Trigger R1(frc::EventLoop* loop =
+                 CommandScheduler::GetInstance().GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the L2 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the L2 button's digital signal
+   * attached to the given loop.
+   */
+  Trigger L2(frc::EventLoop* loop =
+                 CommandScheduler::GetInstance().GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the R2 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the R2 button's digital signal
+   * attached to the given loop.
+   */
+  Trigger R2(frc::EventLoop* loop =
+                 CommandScheduler::GetInstance().GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the options button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the options button's digital signal
+   * attached to the given loop.
+   */
+  Trigger Options(frc::EventLoop* loop = CommandScheduler::GetInstance()
+                                             .GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the L3 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the L3 button's digital signal
+   * attached to the given loop.
+   */
+  Trigger L3(frc::EventLoop* loop =
+                 CommandScheduler::GetInstance().GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the R3 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the R3 button's digital signal
+   * attached to the given loop.
+   */
+  Trigger R3(frc::EventLoop* loop =
+                 CommandScheduler::GetInstance().GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the PS button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the PS button's digital signal
+   * attached to the given loop.
+   */
+  Trigger PS(frc::EventLoop* loop =
+                 CommandScheduler::GetInstance().GetDefaultButtonLoop()) const;
+
+  /**
+   * Constructs an event instance around the touchpad's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to. Defaults to the
+   * CommandScheduler's default loop.
+   * @return an event instance representing the touchpad's digital signal
+   * attached to the given loop.
+   */
+  Trigger Touchpad(frc::EventLoop* loop = CommandScheduler::GetInstance()
+                                              .GetDefaultButtonLoop()) const;
+};
+}  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandXboxController.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandXboxController.h
index 337ec2f..94a3c2c 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandXboxController.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/CommandXboxController.h
@@ -10,7 +10,7 @@
 
 namespace frc2 {
 /**
- * A subclass of {@link XboxController} with {@link Trigger} factories for
+ * A version of {@link XboxController} with {@link Trigger} factories for
  * command-based.
  *
  * @see XboxController
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/JoystickButton.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/JoystickButton.h
index b5280c0..a0d04e7 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/JoystickButton.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/JoystickButton.h
@@ -4,9 +4,8 @@
 
 #pragma once
 #include <frc/GenericHID.h>
-#include <wpi/deprecated.h>
 
-#include "Button.h"
+#include "Trigger.h"
 
 namespace frc2 {
 /**
@@ -17,7 +16,7 @@
  *
  * @see Trigger
  */
-class JoystickButton : public Button {
+class JoystickButton : public Trigger {
  public:
   /**
    * Creates a JoystickButton that commands can be bound to.
@@ -25,11 +24,9 @@
    * @param joystick The joystick on which the button is located.
    * @param buttonNumber The number of the button on the joystick.
    */
-  WPI_IGNORE_DEPRECATED
   explicit JoystickButton(frc::GenericHID* joystick, int buttonNumber)
-      : Button([joystick, buttonNumber] {
+      : Trigger([joystick, buttonNumber] {
           return joystick->GetRawButton(buttonNumber);
         }) {}
-  WPI_UNIGNORE_DEPRECATED
 };
 }  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/NetworkButton.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/NetworkButton.h
index 3d0378d..673821e 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/NetworkButton.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/NetworkButton.h
@@ -11,7 +11,7 @@
 #include <networktables/NetworkTable.h>
 #include <networktables/NetworkTableInstance.h>
 
-#include "Button.h"
+#include "Trigger.h"
 
 namespace frc2 {
 /**
@@ -19,7 +19,7 @@
  *
  * This class is provided by the NewCommands VendorDep
  */
-class NetworkButton : public Button {
+class NetworkButton : public Trigger {
  public:
   /**
    * Creates a NetworkButton that commands can be bound to.
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/POVButton.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/POVButton.h
index 7ee1590..00723be 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/POVButton.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/POVButton.h
@@ -3,10 +3,10 @@
 // the WPILib BSD license file in the root directory of this project.
 
 #pragma once
-#include <frc/GenericHID.h>
-#include <wpi/deprecated.h>
 
-#include "Button.h"
+#include <frc/GenericHID.h>
+
+#include "Trigger.h"
 
 namespace frc2 {
 /**
@@ -17,7 +17,7 @@
  *
  * @see Trigger
  */
-class POVButton : public Button {
+class POVButton : public Trigger {
  public:
   /**
    * Creates a POVButton that commands can be bound to.
@@ -26,11 +26,9 @@
    * @param angle The angle of the POV corresponding to a button press.
    * @param povNumber The number of the POV on the joystick.
    */
-  WPI_IGNORE_DEPRECATED
   POVButton(frc::GenericHID* joystick, int angle, int povNumber = 0)
-      : Button([joystick, angle, povNumber] {
+      : Trigger([joystick, angle, povNumber] {
           return joystick->GetPOV(povNumber) == angle;
         }) {}
-  WPI_UNIGNORE_DEPRECATED
 };
 }  // namespace frc2
diff --git a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h
index c3a5e86..533e0a6 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h
@@ -4,17 +4,15 @@
 
 #pragma once
 
+#include <concepts>
 #include <functional>
-#include <initializer_list>
 #include <memory>
-#include <span>
 #include <utility>
 
 #include <frc/event/BooleanEvent.h>
 #include <frc/event/EventLoop.h>
 #include <frc/filter/Debouncer.h>
 #include <units/time.h>
-#include <wpi/deprecated.h>
 
 #include "frc2/command/Command.h"
 #include "frc2/command/CommandScheduler.h"
@@ -194,8 +192,7 @@
   Trigger ToggleOnFalse(Command* command);
 
   /**
-   * Toggles a command when the condition changes from `true` to the low
-   * state.
+   * Toggles a command when the condition changes from `true` to `false`.
    *
    * <p>Takes a raw pointer, and so is non-owning; users are responsible for the
    * lifespan of the command.
@@ -206,308 +203,6 @@
   Trigger ToggleOnFalse(CommandPtr&& command);
 
   /**
-   * Binds a command to start when the trigger becomes active. Takes a
-   * raw pointer, and so is non-owning; users are responsible for the lifespan
-   * of the command.
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Use OnTrue(Command) instead
-   */
-  WPI_DEPRECATED("Use OnTrue(Command) instead")
-  Trigger WhenActive(Command* command);
-
-  /**
-   * Binds a command to start when the trigger becomes active. Transfers
-   * command ownership to the button scheduler, so the user does not have to
-   * worry about lifespan - rvalue refs will be *moved*, lvalue refs will be
-   * *copied.*
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Use OnTrue(Command) instead
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED("Use OnTrue(Command) instead")
-  Trigger WhenActive(T&& command) {
-    m_loop->Bind([condition = m_condition, previous = m_condition(),
-                  command = std::make_unique<std::remove_reference_t<T>>(
-                      std::forward<T>(command))]() mutable {
-      bool current = condition();
-
-      if (!previous && current) {
-        command->Schedule();
-      }
-
-      previous = current;
-    });
-    return *this;
-  }
-
-  /**
-   * Binds a runnable to execute when the trigger becomes active.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Use OnTrue(Command) instead and construct the InstantCommand
-   * manually
-   */
-  WPI_DEPRECATED(
-      "Use OnTrue(Command) instead and construct the InstantCommand manually")
-  Trigger WhenActive(std::function<void()> toRun,
-                     std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Binds a runnable to execute when the trigger becomes active.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Use OnTrue(Command) instead and construct the InstantCommand
-   * manually
-   */
-  WPI_DEPRECATED(
-      "Use OnTrue(Command) instead and construct the InstantCommand manually")
-  Trigger WhenActive(std::function<void()> toRun,
-                     std::span<Subsystem* const> requirements = {});
-
-  /**
-   * Binds a command to be started repeatedly while the trigger is active, and
-   * canceled when it becomes inactive.  Takes a raw pointer, and so is
-   * non-owning; users are responsible for the lifespan of the command.
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Use WhileTrue(Command) with RepeatCommand, or bind
-   command::Schedule with IfHigh(std::function<void()>).
-   */
-  WPI_DEPRECATED(
-      "Use WhileTrue(Command) with RepeatCommand, or bind command::Schedule "
-      "with IfHigh(std::function<void()>).")
-  Trigger WhileActiveContinous(Command* command);
-
-  /**
-   * Binds a command to be started repeatedly while the trigger is active, and
-   * canceled when it becomes inactive.  Transfers command ownership to the
-   * button scheduler, so the user does not have to worry about lifespan -
-   * rvalue refs will be *moved*, lvalue refs will be *copied.*
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Use WhileTrue(Command) with RepeatCommand, or bind
-   command::Schedule with IfHigh(std::function<void()>).
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED(
-      "Use WhileTrue(Command) with RepeatCommand, or bind command::Schedule "
-      "with IfHigh(std::function<void()>).")
-  Trigger WhileActiveContinous(T&& command) {
-    m_loop->Bind([condition = m_condition, previous = m_condition(),
-                  command = std::make_unique<std::remove_reference_t<T>>(
-                      std::forward<T>(command))]() mutable {
-      bool current = condition();
-
-      if (current) {
-        command->Schedule();
-      } else if (previous && !current) {
-        command->Cancel();
-      }
-
-      previous = current;
-    });
-
-    return *this;
-  }
-
-  /**
-   * Binds a runnable to execute repeatedly while the trigger is active.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Use WhileTrue(Command) and construct a RunCommand manually
-   */
-  WPI_DEPRECATED("Use WhileTrue(Command) and construct a RunCommand manually")
-  Trigger WhileActiveContinous(std::function<void()> toRun,
-                               std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Binds a runnable to execute repeatedly while the trigger is active.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Use WhileTrue(Command) and construct a RunCommand manually
-   */
-  WPI_DEPRECATED("Use WhileTrue(Command) and construct a RunCommand manually")
-  Trigger WhileActiveContinous(std::function<void()> toRun,
-                               std::span<Subsystem* const> requirements = {});
-
-  /**
-   * Binds a command to be started when the trigger becomes active, and
-   * canceled when it becomes inactive.  Takes a raw pointer, and so is
-   * non-owning; users are responsible for the lifespan of the command.
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Use WhileTrue(Command) instead.
-   */
-  WPI_DEPRECATED("Use WhileTrue(Command) instead.")
-  Trigger WhileActiveOnce(Command* command);
-
-  /**
-   * Binds a command to be started when the trigger becomes active, and
-   * canceled when it becomes inactive. Transfers command ownership to the
-   * button scheduler, so the user does not have to worry about lifespan -
-   * rvalue refs will be *moved*, lvalue refs will be *copied.*
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Use WhileTrue(Command) instead.
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED("Use WhileTrue(Command) instead.")
-  Trigger WhileActiveOnce(T&& command) {
-    m_loop->Bind([condition = m_condition, previous = m_condition(),
-                  command = std::make_unique<std::remove_reference_t<T>>(
-                      std::forward<T>(command))]() mutable {
-      bool current = condition();
-
-      if (!previous && current) {
-        command->Schedule();
-      } else if (previous && !current) {
-        command->Cancel();
-      }
-
-      previous = current;
-    });
-    return *this;
-  }
-
-  /**
-   * Binds a command to start when the trigger becomes inactive.  Takes a
-   * raw pointer, and so is non-owning; users are responsible for the lifespan
-   * of the command.
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Use OnFalse(Command) instead.
-   */
-  WPI_DEPRECATED("Use OnFalse(Command) instead.")
-  Trigger WhenInactive(Command* command);
-
-  /**
-   * Binds a command to start when the trigger becomes inactive.  Transfers
-   * command ownership to the button scheduler, so the user does not have to
-   * worry about lifespan - rvalue refs will be *moved*, lvalue refs will be
-   * *copied.*
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Use OnFalse(Command) instead.
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED("Use OnFalse(Command) instead.")
-  Trigger WhenInactive(T&& command) {
-    m_loop->Bind([condition = m_condition, previous = m_condition(),
-                  command = std::make_unique<std::remove_reference_t<T>>(
-                      std::forward<T>(command))]() mutable {
-      bool current = condition();
-
-      if (previous && !current) {
-        command->Schedule();
-      }
-
-      previous = current;
-    });
-    return *this;
-  }
-
-  /**
-   * Binds a runnable to execute when the trigger becomes inactive.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Use OnFalse(Command) instead and construct the InstantCommand
-   * manually
-   */
-  WPI_DEPRECATED(
-      "Use OnFalse(Command) instead and construct the InstantCommand manually")
-  Trigger WhenInactive(std::function<void()> toRun,
-                       std::initializer_list<Subsystem*> requirements);
-
-  /**
-   * Binds a runnable to execute when the trigger becomes inactive.
-   *
-   * @param toRun the runnable to execute.
-   * @param requirements the required subsystems.
-   * @deprecated Use OnFalse(Command) instead and construct the InstantCommand
-   * manually
-   */
-  WPI_DEPRECATED(
-      "Use OnFalse(Command) instead and construct the InstantCommand manually")
-  Trigger WhenInactive(std::function<void()> toRun,
-                       std::span<Subsystem* const> requirements = {});
-
-  /**
-   * Binds a command to start when the trigger becomes active, and be canceled
-   * when it again becomes active. Takes a raw pointer, and so is non-owning;
-   * users are responsible for the lifespan of the command.
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Use ToggleOnTrue(Command) instead.
-   */
-  WPI_DEPRECATED("Use ToggleOnTrue(Command) instead.")
-  Trigger ToggleWhenActive(Command* command);
-
-  /**
-   * Binds a command to start when the trigger becomes active, and be canceled
-   * when it again becomes active.  Transfers command ownership to the button
-   * scheduler, so the user does not have to worry about lifespan - rvalue refs
-   * will be *moved*, lvalue refs will be *copied.*
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Use ToggleOnTrue(Command) instead.
-   */
-  template <class T, typename = std::enable_if_t<std::is_base_of_v<
-                         Command, std::remove_reference_t<T>>>>
-  WPI_DEPRECATED("Use ToggleOnTrue(Command) instead.")
-  Trigger ToggleWhenActive(T&& command) {
-    m_loop->Bind([condition = m_condition, previous = m_condition(),
-                  command = std::make_unique<std::remove_reference_t<T>>(
-                      std::forward<T>(command))]() mutable {
-      bool current = condition();
-
-      if (!previous && current) {
-        if (command->IsScheduled()) {
-          command->Cancel();
-        } else {
-          command->Schedule();
-        }
-      }
-
-      previous = current;
-    });
-
-    return *this;
-  }
-
-  /**
-   * Binds a command to be canceled when the trigger becomes active.  Takes a
-   * raw pointer, and so is non-owning; users are responsible for the lifespan
-   *  and scheduling of the command.
-   *
-   * @param command The command to bind.
-   * @return The trigger, for chained calls.
-   * @deprecated Pass this as a command end condition with Until() instead.
-   */
-  WPI_DEPRECATED("Pass this as a command end condition with Until() instead.")
-  Trigger CancelWhenActive(Command* command);
-
-  /**
    * Composes two triggers with logical AND.
    *
    * @return A trigger which is active when both component triggers are active.
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandDecoratorTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandDecoratorTest.java
index 392c23c..0f66acd 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandDecoratorTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandDecoratorTest.java
@@ -58,6 +58,22 @@
   }
 
   @Test
+  void onlyWhileTest() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicBoolean condition = new AtomicBoolean(true);
+
+      Command command = new WaitCommand(10).onlyWhile(condition::get);
+
+      scheduler.schedule(command);
+      scheduler.run();
+      assertTrue(scheduler.isScheduled(command));
+      condition.set(false);
+      scheduler.run();
+      assertFalse(scheduler.isScheduled(command));
+    }
+  }
+
+  @Test
   void ignoringDisableTest() {
     try (CommandScheduler scheduler = new CommandScheduler()) {
       var command = new RunCommand(() -> {}).ignoringDisable(true);
@@ -184,23 +200,6 @@
     }
   }
 
-  @SuppressWarnings("removal") // Command.perpetually()
-  @Test
-  void perpetuallyTest() {
-    try (CommandScheduler scheduler = new CommandScheduler()) {
-      Command command = new InstantCommand();
-
-      Command perpetual = command.perpetually();
-
-      scheduler.schedule(perpetual);
-      scheduler.run();
-      scheduler.run();
-      scheduler.run();
-
-      assertTrue(scheduler.isScheduled(perpetual));
-    }
-  }
-
   @Test
   void unlessTest() {
     try (CommandScheduler scheduler = new CommandScheduler()) {
@@ -222,6 +221,26 @@
   }
 
   @Test
+  void onlyIfTest() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicBoolean onlyIfCondition = new AtomicBoolean(false);
+      AtomicBoolean hasRunCondition = new AtomicBoolean(false);
+
+      Command command =
+          new InstantCommand(() -> hasRunCondition.set(true)).onlyIf(onlyIfCondition::get);
+
+      scheduler.schedule(command);
+      scheduler.run();
+      assertFalse(hasRunCondition.get());
+
+      onlyIfCondition.set(true);
+      scheduler.schedule(command);
+      scheduler.run();
+      assertTrue(hasRunCondition.get());
+    }
+  }
+
+  @Test
   void finallyDoTest() {
     try (CommandScheduler scheduler = new CommandScheduler()) {
       AtomicInteger first = new AtomicInteger(0);
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandScheduleTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandScheduleTest.java
index c0295f5..793ad95 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandScheduleTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandScheduleTest.java
@@ -11,6 +11,8 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import edu.wpi.first.networktables.NetworkTableInstance;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
 import org.junit.jupiter.api.Test;
 
 class CommandScheduleTest extends CommandTestBase {
@@ -116,4 +118,27 @@
       assertDoesNotThrow(() -> scheduler.cancel(mockCommand));
     }
   }
+
+  @Test
+  void smartDashboardCancelTest() {
+    try (CommandScheduler scheduler = new CommandScheduler();
+        var inst = NetworkTableInstance.create()) {
+      SmartDashboard.setNetworkTableInstance(inst);
+      SmartDashboard.putData("Scheduler", scheduler);
+      SmartDashboard.updateValues();
+
+      MockCommandHolder holder = new MockCommandHolder(true);
+      Command mockCommand = holder.getMock();
+      scheduler.schedule(mockCommand);
+      scheduler.run();
+      SmartDashboard.updateValues();
+      assertTrue(scheduler.isScheduled(mockCommand));
+
+      var table = inst.getTable("SmartDashboard");
+      table.getEntry("Scheduler/Cancel").setIntegerArray(new long[] {mockCommand.hashCode()});
+      SmartDashboard.updateValues();
+      scheduler.run();
+      assertFalse(scheduler.isScheduled(mockCommand));
+    }
+  }
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandSendableButtonTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandSendableButtonTest.java
new file mode 100644
index 0000000..c89ed7f
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandSendableButtonTest.java
@@ -0,0 +1,114 @@
+// 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.
+
+package edu.wpi.first.wpilibj2.command;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import edu.wpi.first.networktables.BooleanPublisher;
+import edu.wpi.first.networktables.NetworkTableInstance;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class CommandSendableButtonTest extends CommandTestBase {
+  private NetworkTableInstance m_inst;
+  private AtomicInteger m_schedule;
+  private AtomicInteger m_cancel;
+  private BooleanPublisher m_publish;
+  private Command m_command;
+
+  @BeforeEach
+  void setUp() {
+    m_inst = NetworkTableInstance.create();
+    SmartDashboard.setNetworkTableInstance(m_inst);
+    m_schedule = new AtomicInteger();
+    m_cancel = new AtomicInteger();
+    m_command = Commands.startEnd(m_schedule::incrementAndGet, m_cancel::incrementAndGet);
+    m_publish = m_inst.getBooleanTopic("/SmartDashboard/command/running").publish();
+    SmartDashboard.putData("command", m_command);
+    SmartDashboard.updateValues();
+  }
+
+  @Test
+  void trueAndNotScheduledSchedules() {
+    // Not scheduled and true -> scheduled
+    CommandScheduler.getInstance().run();
+    SmartDashboard.updateValues();
+    assertFalse(m_command.isScheduled());
+    assertEquals(0, m_schedule.get());
+    assertEquals(0, m_cancel.get());
+
+    m_publish.set(true);
+    SmartDashboard.updateValues();
+    CommandScheduler.getInstance().run();
+    assertTrue(m_command.isScheduled());
+    assertEquals(1, m_schedule.get());
+    assertEquals(0, m_cancel.get());
+  }
+
+  @Test
+  void trueAndScheduledNoOp() {
+    // Scheduled and true -> no-op
+    m_command.schedule();
+    CommandScheduler.getInstance().run();
+    SmartDashboard.updateValues();
+    assertTrue(m_command.isScheduled());
+    assertEquals(1, m_schedule.get());
+    assertEquals(0, m_cancel.get());
+
+    m_publish.set(true);
+    SmartDashboard.updateValues();
+    CommandScheduler.getInstance().run();
+    assertTrue(m_command.isScheduled());
+    assertEquals(1, m_schedule.get());
+    assertEquals(0, m_cancel.get());
+  }
+
+  @Test
+  void falseAndNotScheduledNoOp() {
+    // Not scheduled and false -> no-op
+    CommandScheduler.getInstance().run();
+    SmartDashboard.updateValues();
+    assertFalse(m_command.isScheduled());
+    assertEquals(0, m_schedule.get());
+    assertEquals(0, m_cancel.get());
+
+    m_publish.set(false);
+    SmartDashboard.updateValues();
+    CommandScheduler.getInstance().run();
+    assertFalse(m_command.isScheduled());
+    assertEquals(0, m_schedule.get());
+    assertEquals(0, m_cancel.get());
+  }
+
+  @Test
+  void falseAndScheduledCancel() {
+    // Scheduled and false -> cancel
+    m_command.schedule();
+    CommandScheduler.getInstance().run();
+    SmartDashboard.updateValues();
+    assertTrue(m_command.isScheduled());
+    assertEquals(1, m_schedule.get());
+    assertEquals(0, m_cancel.get());
+
+    m_publish.set(false);
+    SmartDashboard.updateValues();
+    CommandScheduler.getInstance().run();
+    assertFalse(m_command.isScheduled());
+    assertEquals(1, m_schedule.get());
+    assertEquals(1, m_cancel.get());
+  }
+
+  @AfterEach
+  void tearDown() {
+    m_publish.close();
+    m_inst.close();
+    SmartDashboard.setNetworkTableInstance(NetworkTableInstance.getDefault());
+  }
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandTestBase.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandTestBase.java
index 1d46fb5..13f6597 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandTestBase.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandTestBase.java
@@ -23,6 +23,7 @@
     CommandScheduler.getInstance().enable();
     CommandScheduler.getInstance().getActiveButtonLoop().clear();
     CommandScheduler.getInstance().clearComposedCommands();
+    CommandScheduler.getInstance().unregisterAllSubsystems();
 
     setDSEnabled(true);
   }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/ConditionalCommandTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/ConditionalCommandTest.java
index 420a8a7..4659f0e 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/ConditionalCommandTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/ConditionalCommandTest.java
@@ -4,11 +4,19 @@
 
 package edu.wpi.first.wpilibj2.command;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import edu.wpi.first.wpilibj2.command.Command.InterruptionBehavior;
+import java.util.function.BooleanSupplier;
+import java.util.stream.Stream;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 
 class ConditionalCommandTest extends CommandTestBase {
   @Test
@@ -60,4 +68,92 @@
       verify(command2, never()).end(true);
     }
   }
+
+  static Stream<Arguments> interruptible() {
+    return Stream.of(
+        arguments(
+            "AllCancelSelf",
+            InterruptionBehavior.kCancelSelf,
+            new WaitUntilCommand(() -> false)
+                .withInterruptBehavior(InterruptionBehavior.kCancelSelf),
+            new WaitUntilCommand(() -> false)
+                .withInterruptBehavior(InterruptionBehavior.kCancelSelf),
+            (BooleanSupplier) () -> true),
+        arguments(
+            "AllCancelIncoming",
+            InterruptionBehavior.kCancelIncoming,
+            new WaitUntilCommand(() -> false)
+                .withInterruptBehavior(InterruptionBehavior.kCancelIncoming),
+            new WaitUntilCommand(() -> false)
+                .withInterruptBehavior(InterruptionBehavior.kCancelIncoming),
+            (BooleanSupplier) () -> true),
+        arguments(
+            "OneCancelSelfOneIncoming",
+            InterruptionBehavior.kCancelSelf,
+            new WaitUntilCommand(() -> false)
+                .withInterruptBehavior(InterruptionBehavior.kCancelSelf),
+            new WaitUntilCommand(() -> false)
+                .withInterruptBehavior(InterruptionBehavior.kCancelIncoming),
+            (BooleanSupplier) () -> true),
+        arguments(
+            "OneCancelIncomingOneSelf",
+            InterruptionBehavior.kCancelSelf,
+            new WaitUntilCommand(() -> false)
+                .withInterruptBehavior(InterruptionBehavior.kCancelIncoming),
+            new WaitUntilCommand(() -> false)
+                .withInterruptBehavior(InterruptionBehavior.kCancelSelf),
+            (BooleanSupplier) () -> true));
+  }
+
+  @MethodSource
+  @ParameterizedTest(name = "interruptible[{index}]: {0}")
+  void interruptible(
+      @SuppressWarnings("unused") String name,
+      InterruptionBehavior expected,
+      Command command1,
+      Command command2,
+      BooleanSupplier selector) {
+    var command = Commands.either(command1, command2, selector);
+    assertEquals(expected, command.getInterruptionBehavior());
+  }
+
+  static Stream<Arguments> runsWhenDisabled() {
+    return Stream.of(
+        arguments(
+            "AllFalse",
+            false,
+            new WaitUntilCommand(() -> false).ignoringDisable(false),
+            new WaitUntilCommand(() -> false).ignoringDisable(false),
+            (BooleanSupplier) () -> true),
+        arguments(
+            "AllTrue",
+            true,
+            new WaitUntilCommand(() -> false).ignoringDisable(true),
+            new WaitUntilCommand(() -> false).ignoringDisable(true),
+            (BooleanSupplier) () -> true),
+        arguments(
+            "OneTrueOneFalse",
+            false,
+            new WaitUntilCommand(() -> false).ignoringDisable(true),
+            new WaitUntilCommand(() -> false).ignoringDisable(false),
+            (BooleanSupplier) () -> true),
+        arguments(
+            "OneFalseOneTrue",
+            false,
+            new WaitUntilCommand(() -> false).ignoringDisable(false),
+            new WaitUntilCommand(() -> false).ignoringDisable(true),
+            (BooleanSupplier) () -> true));
+  }
+
+  @MethodSource
+  @ParameterizedTest(name = "runsWhenDisabled[{index}]: {0}")
+  void runsWhenDisabled(
+      @SuppressWarnings("unused") String name,
+      boolean expected,
+      Command command1,
+      Command command2,
+      BooleanSupplier selector) {
+    var command = Commands.either(command1, command2, selector);
+    assertEquals(expected, command.runsWhenDisabled());
+  }
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/DeferredCommandTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/DeferredCommandTest.java
new file mode 100644
index 0000000..259ba0f
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/DeferredCommandTest.java
@@ -0,0 +1,85 @@
+// 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.
+
+package edu.wpi.first.wpilibj2.command;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Set;
+import java.util.function.Supplier;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+class DeferredCommandTest extends CommandTestBase {
+  @ParameterizedTest
+  @ValueSource(booleans = {true, false})
+  void deferredFunctionsTest(boolean interrupted) {
+    MockCommandHolder innerCommand = new MockCommandHolder(false);
+    DeferredCommand command = new DeferredCommand(innerCommand::getMock, Set.of());
+
+    command.initialize();
+    verify(innerCommand.getMock()).initialize();
+
+    command.execute();
+    verify(innerCommand.getMock()).execute();
+
+    assertFalse(command.isFinished());
+    verify(innerCommand.getMock()).isFinished();
+
+    innerCommand.setFinished(true);
+    assertTrue(command.isFinished());
+    verify(innerCommand.getMock(), times(2)).isFinished();
+
+    command.end(interrupted);
+    verify(innerCommand.getMock()).end(interrupted);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  void deferredSupplierOnlyCalledDuringInit() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      Supplier<Command> supplier = (Supplier<Command>) mock(Supplier.class);
+      when(supplier.get()).thenReturn(Commands.none(), Commands.none());
+
+      DeferredCommand command = new DeferredCommand(supplier, Set.of());
+      verify(supplier, never()).get();
+
+      scheduler.schedule(command);
+      verify(supplier, only()).get();
+      scheduler.run();
+
+      scheduler.schedule(command);
+      verify(supplier, times(2)).get();
+    }
+  }
+
+  @Test
+  void deferredRequirementsTest() {
+    Subsystem subsystem = new Subsystem() {};
+    DeferredCommand command = new DeferredCommand(Commands::none, Set.of(subsystem));
+
+    assertTrue(command.getRequirements().contains(subsystem));
+  }
+
+  @Test
+  void deferredNullCommandTest() {
+    DeferredCommand command = new DeferredCommand(() -> null, Set.of());
+    assertDoesNotThrow(
+        () -> {
+          command.initialize();
+          command.execute();
+          command.isFinished();
+          command.end(false);
+        });
+  }
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommandTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommandTest.java
index b8ccea5..3ca1101 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommandTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommandTest.java
@@ -118,8 +118,7 @@
             this::setWheelSpeeds,
             subsystem);
 
-    m_timer.reset();
-    m_timer.start();
+    m_timer.restart();
 
     command.initialize();
     while (!command.isFinished()) {
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/PerpetualCommandTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/PerpetualCommandTest.java
deleted file mode 100644
index 6b9690b..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/PerpetualCommandTest.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj2.command;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import org.junit.jupiter.api.Test;
-
-class PerpetualCommandTest extends CommandTestBase {
-  @SuppressWarnings("removal") // PerpetualCommand
-  @Test
-  void perpetualCommandScheduleTest() {
-    try (CommandScheduler scheduler = new CommandScheduler()) {
-      PerpetualCommand command = new PerpetualCommand(new InstantCommand());
-
-      scheduler.schedule(command);
-      scheduler.run();
-
-      assertTrue(scheduler.isScheduled(command));
-    }
-  }
-}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/RepeatCommandTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/RepeatCommandTest.java
index f082ddb..53abdd8 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/RepeatCommandTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/RepeatCommandTest.java
@@ -14,54 +14,69 @@
     implements SingleCompositionTestBase<RepeatCommand> {
   @Test
   void callsMethodsCorrectly() {
-    var initCounter = new AtomicInteger(0);
-    var exeCounter = new AtomicInteger(0);
-    var isFinishedCounter = new AtomicInteger(0);
-    var endCounter = new AtomicInteger(0);
-    var isFinishedHook = new AtomicBoolean(false);
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      var initCounter = new AtomicInteger(0);
+      var exeCounter = new AtomicInteger(0);
+      var isFinishedCounter = new AtomicInteger(0);
+      var endCounter = new AtomicInteger(0);
+      var isFinishedHook = new AtomicBoolean(false);
 
-    final var command =
-        new FunctionalCommand(
-                initCounter::incrementAndGet,
-                exeCounter::incrementAndGet,
-                interrupted -> endCounter.incrementAndGet(),
-                () -> {
-                  isFinishedCounter.incrementAndGet();
-                  return isFinishedHook.get();
-                })
-            .repeatedly();
+      final var command =
+          new FunctionalCommand(
+                  initCounter::incrementAndGet,
+                  exeCounter::incrementAndGet,
+                  interrupted -> endCounter.incrementAndGet(),
+                  () -> {
+                    isFinishedCounter.incrementAndGet();
+                    return isFinishedHook.get();
+                  })
+              .repeatedly();
 
-    assertEquals(0, initCounter.get());
-    assertEquals(0, exeCounter.get());
-    assertEquals(0, isFinishedCounter.get());
-    assertEquals(0, endCounter.get());
+      assertEquals(0, initCounter.get());
+      assertEquals(0, exeCounter.get());
+      assertEquals(0, isFinishedCounter.get());
+      assertEquals(0, endCounter.get());
 
-    CommandScheduler.getInstance().schedule(command);
-    assertEquals(1, initCounter.get());
-    assertEquals(0, exeCounter.get());
-    assertEquals(0, isFinishedCounter.get());
-    assertEquals(0, endCounter.get());
+      scheduler.schedule(command);
+      assertEquals(1, initCounter.get());
+      assertEquals(0, exeCounter.get());
+      assertEquals(0, isFinishedCounter.get());
+      assertEquals(0, endCounter.get());
 
-    isFinishedHook.set(false);
-    CommandScheduler.getInstance().run();
-    assertEquals(1, initCounter.get());
-    assertEquals(1, exeCounter.get());
-    assertEquals(1, isFinishedCounter.get());
-    assertEquals(0, endCounter.get());
+      isFinishedHook.set(false);
+      scheduler.run();
+      assertEquals(1, initCounter.get());
+      assertEquals(1, exeCounter.get());
+      assertEquals(1, isFinishedCounter.get());
+      assertEquals(0, endCounter.get());
 
-    isFinishedHook.set(true);
-    CommandScheduler.getInstance().run();
-    assertEquals(1, initCounter.get());
-    assertEquals(2, exeCounter.get());
-    assertEquals(2, isFinishedCounter.get());
-    assertEquals(1, endCounter.get());
+      isFinishedHook.set(true);
+      scheduler.run();
+      assertEquals(1, initCounter.get());
+      assertEquals(2, exeCounter.get());
+      assertEquals(2, isFinishedCounter.get());
+      assertEquals(1, endCounter.get());
 
-    isFinishedHook.set(false);
-    CommandScheduler.getInstance().run();
-    assertEquals(2, initCounter.get());
-    assertEquals(3, exeCounter.get());
-    assertEquals(3, isFinishedCounter.get());
-    assertEquals(1, endCounter.get());
+      isFinishedHook.set(false);
+      scheduler.run();
+      assertEquals(2, initCounter.get());
+      assertEquals(3, exeCounter.get());
+      assertEquals(3, isFinishedCounter.get());
+      assertEquals(1, endCounter.get());
+
+      isFinishedHook.set(true);
+      scheduler.run();
+      assertEquals(2, initCounter.get());
+      assertEquals(4, exeCounter.get());
+      assertEquals(4, isFinishedCounter.get());
+      assertEquals(2, endCounter.get());
+
+      scheduler.cancel(command);
+      assertEquals(2, initCounter.get());
+      assertEquals(4, exeCounter.get());
+      assertEquals(4, isFinishedCounter.get());
+      assertEquals(2, endCounter.get());
+    }
   }
 
   @Override
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/RobotDisabledCommandTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/RobotDisabledCommandTest.java
index 0914c25..a248810 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/RobotDisabledCommandTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/RobotDisabledCommandTest.java
@@ -141,8 +141,8 @@
     MockCommandHolder command4Holder = new MockCommandHolder(false);
     Command command4 = command4Holder.getMock();
 
-    Command runWhenDisabled = new SelectCommand(Map.of(1, command1, 2, command2), () -> 1);
-    Command dontRunWhenDisabled = new SelectCommand(Map.of(1, command3, 2, command4), () -> 1);
+    Command runWhenDisabled = new SelectCommand<>(Map.of(1, command1, 2, command2), () -> 1);
+    Command dontRunWhenDisabled = new SelectCommand<>(Map.of(1, command3, 2, command4), () -> 1);
 
     try (CommandScheduler scheduler = new CommandScheduler()) {
       scheduler.schedule(runWhenDisabled, dontRunWhenDisabled);
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SchedulerTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SchedulerTest.java
index 7a62f5e..1e0b333 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SchedulerTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SchedulerTest.java
@@ -6,6 +6,9 @@
 
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.concurrent.atomic.AtomicInteger;
 import org.junit.jupiter.api.Test;
@@ -44,12 +47,110 @@
   }
 
   @Test
+  void schedulerInterruptNoCauseLambdaTest() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicInteger counter = new AtomicInteger();
+
+      scheduler.onCommandInterrupt(
+          (interrupted, cause) -> {
+            assertFalse(cause.isPresent());
+            counter.incrementAndGet();
+          });
+
+      Command command = Commands.run(() -> {});
+
+      scheduler.schedule(command);
+      scheduler.cancel(command);
+
+      assertEquals(1, counter.get());
+    }
+  }
+
+  @Test
+  void schedulerInterruptCauseLambdaTest() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicInteger counter = new AtomicInteger();
+
+      Subsystem subsystem = new Subsystem() {};
+      Command command = subsystem.run(() -> {});
+      Command interruptor = subsystem.runOnce(() -> {});
+
+      scheduler.onCommandInterrupt(
+          (interrupted, cause) -> {
+            assertTrue(cause.isPresent());
+            assertSame(interruptor, cause.get());
+            counter.incrementAndGet();
+          });
+
+      scheduler.schedule(command);
+      scheduler.schedule(interruptor);
+
+      assertEquals(1, counter.get());
+    }
+  }
+
+  @Test
+  void schedulerInterruptCauseLambdaInRunLoopTest() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicInteger counter = new AtomicInteger();
+
+      Subsystem subsystem = new Subsystem() {};
+      Command command = subsystem.run(() -> {});
+      Command interruptor = subsystem.runOnce(() -> {});
+      // This command will schedule interruptor in execute() inside the run loop
+      Command interruptorScheduler = Commands.runOnce(() -> scheduler.schedule(interruptor));
+
+      scheduler.onCommandInterrupt(
+          (interrupted, cause) -> {
+            assertTrue(cause.isPresent());
+            assertSame(interruptor, cause.get());
+            counter.incrementAndGet();
+          });
+
+      scheduler.schedule(command);
+      scheduler.schedule(interruptorScheduler);
+
+      scheduler.run();
+
+      assertEquals(1, counter.get());
+    }
+  }
+
+  @Test
+  void registerSubsystemTest() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicInteger counter = new AtomicInteger(0);
+      Subsystem system =
+          new SubsystemBase() {
+            @Override
+            public void periodic() {
+              counter.incrementAndGet();
+            }
+          };
+
+      assertDoesNotThrow(() -> scheduler.registerSubsystem(system));
+
+      scheduler.run();
+      assertEquals(1, counter.get());
+    }
+  }
+
+  @Test
   void unregisterSubsystemTest() {
     try (CommandScheduler scheduler = new CommandScheduler()) {
-      Subsystem system = new SubsystemBase() {};
-
+      AtomicInteger counter = new AtomicInteger(0);
+      Subsystem system =
+          new SubsystemBase() {
+            @Override
+            public void periodic() {
+              counter.incrementAndGet();
+            }
+          };
       scheduler.registerSubsystem(system);
       assertDoesNotThrow(() -> scheduler.unregisterSubsystem(system));
+
+      scheduler.run();
+      assertEquals(0, counter.get());
     }
   }
 
@@ -59,6 +160,7 @@
       AtomicInteger counter = new AtomicInteger();
 
       scheduler.onCommandInterrupt(command -> counter.incrementAndGet());
+      scheduler.onCommandInterrupt((command, interruptor) -> assertFalse(interruptor.isPresent()));
 
       Command command = new WaitCommand(10);
       Command command2 = new WaitCommand(10);
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SchedulingRecursionTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SchedulingRecursionTest.java
index 28e9e6f..5e8fd39 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SchedulingRecursionTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SchedulingRecursionTest.java
@@ -25,9 +25,10 @@
   void cancelFromInitialize(InterruptionBehavior interruptionBehavior) {
     try (CommandScheduler scheduler = new CommandScheduler()) {
       AtomicBoolean hasOtherRun = new AtomicBoolean();
+      AtomicInteger counter = new AtomicInteger();
       Subsystem requirement = new SubsystemBase() {};
       Command selfCancels =
-          new CommandBase() {
+          new Command() {
             {
               addRequirements(requirement);
             }
@@ -38,6 +39,11 @@
             }
 
             @Override
+            public void end(boolean interrupted) {
+              counter.incrementAndGet();
+            }
+
+            @Override
             public InterruptionBehavior getInterruptionBehavior() {
               return interruptionBehavior;
             }
@@ -52,6 +58,47 @@
           });
       assertFalse(scheduler.isScheduled(selfCancels));
       assertTrue(scheduler.isScheduled(other));
+      assertEquals(1, counter.get());
+      scheduler.run();
+      assertTrue(hasOtherRun.get());
+    }
+  }
+
+  @EnumSource(InterruptionBehavior.class)
+  @ParameterizedTest
+  void cancelFromInitializeAction(InterruptionBehavior interruptionBehavior) {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicBoolean hasOtherRun = new AtomicBoolean();
+      AtomicInteger counter = new AtomicInteger();
+      Subsystem requirement = new Subsystem() {};
+      Command selfCancels =
+          new Command() {
+            {
+              addRequirements(requirement);
+            }
+
+            @Override
+            public void end(boolean interrupted) {
+              counter.incrementAndGet();
+            }
+
+            @Override
+            public InterruptionBehavior getInterruptionBehavior() {
+              return interruptionBehavior;
+            }
+          };
+      Command other = new RunCommand(() -> hasOtherRun.set(true), requirement);
+
+      assertDoesNotThrow(
+          () -> {
+            scheduler.onCommandInitialize(cmd -> scheduler.cancel(selfCancels));
+            scheduler.schedule(selfCancels);
+            scheduler.run();
+            scheduler.schedule(other);
+          });
+      assertFalse(scheduler.isScheduled(selfCancels));
+      assertTrue(scheduler.isScheduled(other));
+      assertEquals(1, counter.get());
       scheduler.run();
       assertTrue(hasOtherRun.get());
     }
@@ -62,9 +109,10 @@
   void defaultCommandGetsRescheduledAfterSelfCanceling(InterruptionBehavior interruptionBehavior) {
     try (CommandScheduler scheduler = new CommandScheduler()) {
       AtomicBoolean hasOtherRun = new AtomicBoolean();
+      AtomicInteger counter = new AtomicInteger();
       Subsystem requirement = new SubsystemBase() {};
       Command selfCancels =
-          new CommandBase() {
+          new Command() {
             {
               addRequirements(requirement);
             }
@@ -75,6 +123,11 @@
             }
 
             @Override
+            public void end(boolean interrupted) {
+              counter.incrementAndGet();
+            }
+
+            @Override
             public InterruptionBehavior getInterruptionBehavior() {
               return interruptionBehavior;
             }
@@ -90,6 +143,7 @@
       scheduler.run();
       assertFalse(scheduler.isScheduled(selfCancels));
       assertTrue(scheduler.isScheduled(other));
+      assertEquals(1, counter.get());
       scheduler.run();
       assertTrue(hasOtherRun.get());
     }
@@ -100,7 +154,7 @@
     try (CommandScheduler scheduler = new CommandScheduler()) {
       AtomicInteger counter = new AtomicInteger();
       Command selfCancels =
-          new CommandBase() {
+          new Command() {
             @Override
             public void end(boolean interrupted) {
               counter.incrementAndGet();
@@ -116,23 +170,176 @@
   }
 
   @Test
+  void cancelFromInterruptAction() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicInteger counter = new AtomicInteger();
+      Command selfCancels = new RunCommand(() -> {});
+      scheduler.onCommandInterrupt(
+          cmd -> {
+            counter.incrementAndGet();
+            scheduler.cancel(selfCancels);
+          });
+      scheduler.schedule(selfCancels);
+
+      assertDoesNotThrow(() -> scheduler.cancel(selfCancels));
+      assertEquals(1, counter.get());
+      assertFalse(scheduler.isScheduled(selfCancels));
+    }
+  }
+
+  @Test
+  void cancelFromEndLoop() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicInteger counter = new AtomicInteger();
+      FunctionalCommand dCancelsAll =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.cancelAll();
+              },
+              () -> true);
+      FunctionalCommand cCancelsD =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.cancel(dCancelsAll);
+              },
+              () -> true);
+      FunctionalCommand bCancelsC =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.cancel(cCancelsD);
+              },
+              () -> true);
+      FunctionalCommand aCancelsB =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.cancel(bCancelsC);
+              },
+              () -> true);
+
+      scheduler.schedule(aCancelsB);
+      scheduler.schedule(bCancelsC);
+      scheduler.schedule(cCancelsD);
+      scheduler.schedule(dCancelsAll);
+
+      assertDoesNotThrow(() -> scheduler.cancel(aCancelsB));
+      assertEquals(4, counter.get());
+      assertFalse(scheduler.isScheduled(aCancelsB));
+      assertFalse(scheduler.isScheduled(bCancelsC));
+      assertFalse(scheduler.isScheduled(cCancelsD));
+      assertFalse(scheduler.isScheduled(dCancelsAll));
+    }
+  }
+
+  @Test
+  void cancelFromEndLoopWhileInRunLoop() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicInteger counter = new AtomicInteger();
+      FunctionalCommand dCancelsAll =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.cancelAll();
+              },
+              () -> true);
+      FunctionalCommand cCancelsD =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.cancel(dCancelsAll);
+              },
+              () -> true);
+      FunctionalCommand bCancelsC =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.cancel(cCancelsD);
+              },
+              () -> true);
+      FunctionalCommand aCancelsB =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.cancel(bCancelsC);
+              },
+              () -> true);
+
+      scheduler.schedule(aCancelsB);
+      scheduler.schedule(bCancelsC);
+      scheduler.schedule(cCancelsD);
+      scheduler.schedule(dCancelsAll);
+
+      assertDoesNotThrow(() -> scheduler.run());
+      assertEquals(4, counter.get());
+      assertFalse(scheduler.isScheduled(aCancelsB));
+      assertFalse(scheduler.isScheduled(bCancelsC));
+      assertFalse(scheduler.isScheduled(cCancelsD));
+      assertFalse(scheduler.isScheduled(dCancelsAll));
+    }
+  }
+
+  @Test
+  void multiCancelFromEnd() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicInteger counter = new AtomicInteger();
+      FunctionalCommand bIncrementsCounter =
+          new FunctionalCommand(
+              () -> {}, () -> {}, interrupted -> counter.incrementAndGet(), () -> true);
+      Command aCancelsB =
+          new Command() {
+            @Override
+            public void end(boolean interrupted) {
+              counter.incrementAndGet();
+              scheduler.cancel(bIncrementsCounter);
+              scheduler.cancel(this);
+            }
+          };
+
+      scheduler.schedule(aCancelsB);
+      scheduler.schedule(bIncrementsCounter);
+
+      assertDoesNotThrow(() -> scheduler.cancel(aCancelsB));
+      assertEquals(2, counter.get());
+      assertFalse(scheduler.isScheduled(aCancelsB));
+      assertFalse(scheduler.isScheduled(bIncrementsCounter));
+    }
+  }
+
+  @Test
   void scheduleFromEndCancel() {
     try (CommandScheduler scheduler = new CommandScheduler()) {
       AtomicInteger counter = new AtomicInteger();
       Subsystem requirement = new SubsystemBase() {};
       InstantCommand other = new InstantCommand(() -> {}, requirement);
-      Command selfCancels =
-          new CommandBase() {
-            {
-              addRequirements(requirement);
-            }
-
-            @Override
-            public void end(boolean interrupted) {
-              counter.incrementAndGet();
-              scheduler.schedule(other);
-            }
-          };
+      FunctionalCommand selfCancels =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.schedule(other);
+              },
+              () -> false,
+              requirement);
 
       scheduler.schedule(selfCancels);
 
@@ -148,19 +355,38 @@
       AtomicInteger counter = new AtomicInteger();
       Subsystem requirement = new SubsystemBase() {};
       InstantCommand other = new InstantCommand(() -> {}, requirement);
-      Command selfCancels =
-          new CommandBase() {
-            {
-              addRequirements(requirement);
-            }
+      FunctionalCommand selfCancels =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.schedule(other);
+              },
+              () -> false,
+              requirement);
 
-            @Override
-            public void end(boolean interrupted) {
-              counter.incrementAndGet();
-              scheduler.schedule(other);
-            }
-          };
+      scheduler.schedule(selfCancels);
 
+      assertDoesNotThrow(() -> scheduler.schedule(other));
+      assertEquals(1, counter.get());
+      assertFalse(scheduler.isScheduled(selfCancels));
+      assertTrue(scheduler.isScheduled(other));
+    }
+  }
+
+  @Test
+  void scheduleFromEndInterruptAction() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicInteger counter = new AtomicInteger();
+      Subsystem requirement = new Subsystem() {};
+      InstantCommand other = new InstantCommand(() -> {}, requirement);
+      InstantCommand selfCancels = new InstantCommand(() -> {}, requirement);
+      scheduler.onCommandInterrupt(
+          cmd -> {
+            counter.incrementAndGet();
+            scheduler.schedule(other);
+          });
       scheduler.schedule(selfCancels);
 
       assertDoesNotThrow(() -> scheduler.schedule(other));
@@ -178,18 +404,16 @@
       Subsystem requirement = new SubsystemBase() {};
       Command other =
           new InstantCommand(() -> {}, requirement).withInterruptBehavior(interruptionBehavior);
-      Command defaultCommand =
-          new CommandBase() {
-            {
-              addRequirements(requirement);
-            }
-
-            @Override
-            public void initialize() {
-              counter.incrementAndGet();
-              scheduler.schedule(other);
-            }
-          };
+      FunctionalCommand defaultCommand =
+          new FunctionalCommand(
+              () -> {
+                counter.incrementAndGet();
+                scheduler.schedule(other);
+              },
+              () -> {},
+              interrupted -> {},
+              () -> false,
+              requirement);
 
       scheduler.setDefaultCommand(requirement, defaultCommand);
 
@@ -201,4 +425,41 @@
       assertTrue(scheduler.isScheduled(other));
     }
   }
+
+  @Test
+  void cancelDefaultCommandFromEnd() {
+    try (CommandScheduler scheduler = new CommandScheduler()) {
+      AtomicInteger counter = new AtomicInteger();
+      Subsystem requirement = new Subsystem() {};
+      Command defaultCommand =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> counter.incrementAndGet(),
+              () -> false,
+              requirement);
+      Command other = new InstantCommand(() -> {}, requirement);
+      Command cancelDefaultCommand =
+          new FunctionalCommand(
+              () -> {},
+              () -> {},
+              interrupted -> {
+                counter.incrementAndGet();
+                scheduler.schedule(other);
+              },
+              () -> false);
+
+      assertDoesNotThrow(
+          () -> {
+            scheduler.schedule(cancelDefaultCommand);
+            scheduler.setDefaultCommand(requirement, defaultCommand);
+
+            scheduler.run();
+            scheduler.cancel(cancelDefaultCommand);
+          });
+      assertEquals(2, counter.get());
+      assertFalse(scheduler.isScheduled(defaultCommand));
+      assertTrue(scheduler.isScheduled(other));
+    }
+  }
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SelectCommandTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SelectCommandTest.java
index 736f120..0ce3b79 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SelectCommandTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SelectCommandTest.java
@@ -13,7 +13,8 @@
 import java.util.Map;
 import org.junit.jupiter.api.Test;
 
-class SelectCommandTest extends CommandTestBase implements MultiCompositionTestBase<SelectCommand> {
+class SelectCommandTest extends CommandTestBase
+    implements MultiCompositionTestBase<SelectCommand<Integer>> {
   @Test
   void selectCommandTest() {
     try (CommandScheduler scheduler = new CommandScheduler()) {
@@ -25,8 +26,8 @@
       MockCommandHolder command3Holder = new MockCommandHolder(true);
       Command command3 = command3Holder.getMock();
 
-      SelectCommand selectCommand =
-          new SelectCommand(
+      SelectCommand<String> selectCommand =
+          new SelectCommand<>(
               Map.ofEntries(
                   Map.entry("one", command1),
                   Map.entry("two", command2),
@@ -61,8 +62,8 @@
       MockCommandHolder command3Holder = new MockCommandHolder(true);
       Command command3 = command3Holder.getMock();
 
-      SelectCommand selectCommand =
-          new SelectCommand(
+      SelectCommand<String> selectCommand =
+          new SelectCommand<>(
               Map.ofEntries(
                   Map.entry("one", command1),
                   Map.entry("two", command2),
@@ -88,8 +89,8 @@
       MockCommandHolder command3Holder = new MockCommandHolder(true, system3, system4);
       Command command3 = command3Holder.getMock();
 
-      SelectCommand selectCommand =
-          new SelectCommand(
+      SelectCommand<String> selectCommand =
+          new SelectCommand<>(
               Map.ofEntries(
                   Map.entry("one", command1),
                   Map.entry("two", command2),
@@ -108,11 +109,11 @@
   }
 
   @Override
-  public SelectCommand compose(Command... members) {
-    var map = new HashMap<Object, Command>();
+  public SelectCommand<Integer> compose(Command... members) {
+    var map = new HashMap<Integer, Command>();
     for (int i = 0; i < members.length; i++) {
       map.put(i, members[i]);
     }
-    return new SelectCommand(map, () -> 0);
+    return new SelectCommand<>(map, () -> 0);
   }
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommandTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommandTest.java
index 1367e51..9dd03ae 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommandTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommandTest.java
@@ -114,8 +114,7 @@
             this::setModuleStates,
             subsystem);
 
-    m_timer.reset();
-    m_timer.start();
+    m_timer.restart();
 
     command.initialize();
     while (!command.isFinished()) {
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java
index 82d359c..87f8d16 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java
@@ -194,34 +194,6 @@
     assertEquals(1, endCounter.get());
   }
 
-  // Binding runnables directly is deprecated -- users should create the command manually
-  @SuppressWarnings("deprecation")
-  @Test
-  void runnableBindingTest() {
-    InternalButton buttonWhenActive = new InternalButton();
-    InternalButton buttonWhileActiveContinuous = new InternalButton();
-    InternalButton buttonWhenInactive = new InternalButton();
-
-    buttonWhenActive.setPressed(false);
-    buttonWhileActiveContinuous.setPressed(true);
-    buttonWhenInactive.setPressed(true);
-
-    AtomicInteger counter = new AtomicInteger(0);
-
-    buttonWhenActive.whenPressed(counter::incrementAndGet);
-    buttonWhileActiveContinuous.whileActiveContinuous(counter::incrementAndGet);
-    buttonWhenInactive.whenInactive(counter::incrementAndGet);
-
-    CommandScheduler scheduler = CommandScheduler.getInstance();
-
-    scheduler.run();
-    buttonWhenActive.setPressed(true);
-    buttonWhenInactive.setPressed(false);
-    scheduler.run();
-
-    assertEquals(counter.get(), 4);
-  }
-
   @Test
   void triggerCompositionTest() {
     InternalButton button1 = new InternalButton();
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/AddRequirementsTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/AddRequirementsTest.cpp
new file mode 100644
index 0000000..8ef5154
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/AddRequirementsTest.cpp
@@ -0,0 +1,144 @@
+// 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/array.h>
+
+#include "CommandTestBase.h"
+#include "frc2/command/Command.h"
+#include "frc2/command/RunCommand.h"
+
+using namespace frc2;
+
+// Class to verify the overload resolution of Command::AddRequirements. This
+// does not derive from Command because AddRequirements is non-virtual,
+// preventing overriding anyways.
+class MockAddRequirements {
+ public:
+  MOCK_METHOD(void, AddRequirements, (Requirements), ());
+  MOCK_METHOD(void, AddRequirements, ((wpi::SmallSet<Subsystem*, 4>)), ());
+  MOCK_METHOD(void, AddRequirements, (Subsystem*), ());
+};
+
+TEST(AddRequirementsTest, InitializerListOverloadResolution) {
+  TestSubsystem requirement;
+
+  MockAddRequirements overloadResolver;
+
+  EXPECT_CALL(overloadResolver, AddRequirements(testing::An<Requirements>()));
+
+  overloadResolver.AddRequirements({&requirement, &requirement});
+}
+
+TEST(AddRequirementsTest, SpanOverloadResolution) {
+  std::span<Subsystem* const> requirementsSpan;
+
+  MockAddRequirements overloadResolver;
+
+  EXPECT_CALL(overloadResolver, AddRequirements(testing::An<Requirements>()));
+
+  overloadResolver.AddRequirements(requirementsSpan);
+}
+
+TEST(AddRequirementsTest, SmallSetOverloadResolution) {
+  wpi::SmallSet<Subsystem*, 4> requirementsSet;
+
+  MockAddRequirements overloadResolver;
+
+  EXPECT_CALL(overloadResolver,
+              AddRequirements(testing::An<wpi::SmallSet<Subsystem*, 4>>()));
+
+  overloadResolver.AddRequirements(requirementsSet);
+}
+
+TEST(AddRequirementsTest, SubsystemOverloadResolution) {
+  TestSubsystem requirement;
+
+  MockAddRequirements overloadResolver;
+
+  EXPECT_CALL(overloadResolver, AddRequirements(testing::An<Subsystem*>()));
+
+  overloadResolver.AddRequirements(&requirement);
+}
+
+TEST(AddRequirementsTest, InitializerListSemantics) {
+  TestSubsystem requirement1;
+  TestSubsystem requirement2;
+
+  RunCommand command([] {});
+  command.AddRequirements({&requirement1, &requirement2});
+  EXPECT_TRUE(command.HasRequirement(&requirement1));
+  EXPECT_TRUE(command.HasRequirement(&requirement2));
+  EXPECT_EQ(command.GetRequirements().size(), 2u);
+}
+
+TEST(AddRequirementsTest, InitializerListDuplicatesSemantics) {
+  TestSubsystem requirement;
+
+  RunCommand command([] {});
+  command.AddRequirements({&requirement, &requirement});
+  EXPECT_TRUE(command.HasRequirement(&requirement));
+  EXPECT_EQ(command.GetRequirements().size(), 1u);
+}
+
+TEST(AddRequirementsTest, SpanSemantics) {
+  TestSubsystem requirement1;
+  TestSubsystem requirement2;
+
+  wpi::array<Subsystem* const, 2> requirementsArray(&requirement1,
+                                                    &requirement2);
+
+  RunCommand command([] {});
+  command.AddRequirements(std::span{requirementsArray});
+  EXPECT_TRUE(command.HasRequirement(&requirement1));
+  EXPECT_TRUE(command.HasRequirement(&requirement2));
+  EXPECT_EQ(command.GetRequirements().size(), 2u);
+}
+
+TEST(AddRequirementsTest, SpanDuplicatesSemantics) {
+  TestSubsystem requirement;
+
+  wpi::array<Subsystem* const, 2> requirementsArray(&requirement, &requirement);
+
+  RunCommand command([] {});
+  command.AddRequirements(std::span{requirementsArray});
+  EXPECT_TRUE(command.HasRequirement(&requirement));
+  EXPECT_EQ(command.GetRequirements().size(), 1u);
+}
+
+TEST(AddRequirementsTest, SmallSetSemantics) {
+  TestSubsystem requirement1;
+  TestSubsystem requirement2;
+
+  wpi::SmallSet<Subsystem*, 4> requirementsSet;
+  requirementsSet.insert(&requirement1);
+  requirementsSet.insert(&requirement2);
+
+  RunCommand command([] {});
+  command.AddRequirements(requirementsSet);
+  EXPECT_TRUE(command.HasRequirement(&requirement1));
+  EXPECT_TRUE(command.HasRequirement(&requirement2));
+  EXPECT_EQ(command.GetRequirements().size(), 2u);
+}
+
+TEST(AddRequirementsTest, SubsystemPointerSemantics) {
+  TestSubsystem requirement1;
+  TestSubsystem requirement2;
+
+  RunCommand command([] {});
+  command.AddRequirements(&requirement1);
+  command.AddRequirements(&requirement2);
+  EXPECT_TRUE(command.HasRequirement(&requirement1));
+  EXPECT_TRUE(command.HasRequirement(&requirement2));
+  EXPECT_EQ(command.GetRequirements().size(), 2u);
+}
+
+TEST(AddRequirementsTest, SubsystemPointerDuplicatesSemantics) {
+  TestSubsystem requirement;
+
+  RunCommand command([] {});
+  command.AddRequirements(&requirement);
+  command.AddRequirements(&requirement);
+  EXPECT_TRUE(command.HasRequirement(&requirement));
+  EXPECT_EQ(command.GetRequirements().size(), 1u);
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandDecoratorTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandDecoratorTest.cpp
index 5ab184c..884d504 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandDecoratorTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandDecoratorTest.cpp
@@ -9,7 +9,6 @@
 #include "frc2/command/FunctionalCommand.h"
 #include "frc2/command/InstantCommand.h"
 #include "frc2/command/ParallelRaceGroup.h"
-#include "frc2/command/PerpetualCommand.h"
 #include "frc2/command/RunCommand.h"
 #include "frc2/command/SequentialCommandGroup.h"
 
@@ -54,6 +53,24 @@
   EXPECT_FALSE(scheduler.IsScheduled(command));
 }
 
+TEST_F(CommandDecoratorTest, OnlyWhile) {
+  CommandScheduler scheduler = GetScheduler();
+
+  bool run = true;
+
+  auto command = RunCommand([] {}, {}).OnlyWhile([&run] { return run; });
+
+  scheduler.Schedule(command);
+
+  scheduler.Run();
+  EXPECT_TRUE(scheduler.IsScheduled(command));
+
+  run = false;
+
+  scheduler.Run();
+  EXPECT_FALSE(scheduler.IsScheduled(command));
+}
+
 TEST_F(CommandDecoratorTest, IgnoringDisable) {
   CommandScheduler scheduler = GetScheduler();
 
@@ -104,21 +121,6 @@
   EXPECT_TRUE(finished);
 }
 
-TEST_F(CommandDecoratorTest, Perpetually) {
-  CommandScheduler scheduler = GetScheduler();
-
-  WPI_IGNORE_DEPRECATED
-  auto command = InstantCommand([] {}, {}).Perpetually();
-  WPI_UNIGNORE_DEPRECATED
-
-  scheduler.Schedule(&command);
-
-  scheduler.Run();
-  scheduler.Run();
-
-  EXPECT_TRUE(scheduler.IsScheduled(&command));
-}
-
 TEST_F(CommandDecoratorTest, Unless) {
   CommandScheduler scheduler = GetScheduler();
 
@@ -140,6 +142,27 @@
   EXPECT_TRUE(hasRun);
 }
 
+TEST_F(CommandDecoratorTest, OnlyIf) {
+  CommandScheduler scheduler = GetScheduler();
+
+  bool hasRun = false;
+  bool onlyIfBool = false;
+
+  auto command =
+      InstantCommand([&hasRun] { hasRun = true; }, {}).OnlyIf([&onlyIfBool] {
+        return onlyIfBool;
+      });
+
+  scheduler.Schedule(command);
+  scheduler.Run();
+  EXPECT_FALSE(hasRun);
+
+  onlyIfBool = true;
+  scheduler.Schedule(command);
+  scheduler.Run();
+  EXPECT_TRUE(hasRun);
+}
+
 TEST_F(CommandDecoratorTest, FinallyDo) {
   CommandScheduler scheduler = GetScheduler();
   int first = 0;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandPtrTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandPtrTest.cpp
index 26077f2..6c57c7f 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandPtrTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandPtrTest.cpp
@@ -27,6 +27,7 @@
   EXPECT_NO_FATAL_FAILURE(scheduler.Cancel(movedTo));
 
   EXPECT_THROW(scheduler.Schedule(movedFrom), frc::RuntimeError);
+  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move)
   EXPECT_THROW(movedFrom.IsScheduled(), frc::RuntimeError);
   EXPECT_THROW(static_cast<void>(std::move(movedFrom).Repeatedly()),
                frc::RuntimeError);
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandRequirementsTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandRequirementsTest.cpp
index 79a472f..b46bb8b 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandRequirementsTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandRequirementsTest.cpp
@@ -12,7 +12,6 @@
 #include "frc2/command/ParallelCommandGroup.h"
 #include "frc2/command/ParallelDeadlineGroup.h"
 #include "frc2/command/ParallelRaceGroup.h"
-#include "frc2/command/SelectCommand.h"
 #include "frc2/command/SequentialCommandGroup.h"
 
 using namespace frc2;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandScheduleTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandScheduleTest.cpp
index cb16b48..9178677 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandScheduleTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandScheduleTest.cpp
@@ -2,6 +2,9 @@
 // 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 <frc/smartdashboard/SmartDashboard.h>
+#include <networktables/NetworkTableInstance.h>
+
 #include "CommandTestBase.h"
 
 using namespace frc2;
@@ -98,3 +101,24 @@
 
   EXPECT_NO_FATAL_FAILURE(scheduler.Cancel(&command));
 }
+
+TEST_F(CommandScheduleTest, SmartDashboardCancel) {
+  CommandScheduler scheduler = GetScheduler();
+  frc::SmartDashboard::PutData("Scheduler", &scheduler);
+  frc::SmartDashboard::UpdateValues();
+
+  MockCommand command;
+  scheduler.Schedule(&command);
+  scheduler.Run();
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_TRUE(scheduler.IsScheduled(&command));
+
+  uintptr_t ptrTmp = reinterpret_cast<uintptr_t>(&command);
+  nt::NetworkTableInstance::GetDefault()
+      .GetEntry("/SmartDashboard/Scheduler/Cancel")
+      .SetIntegerArray(
+          std::span<const int64_t>{{static_cast<int64_t>(ptrTmp)}});
+  frc::SmartDashboard::UpdateValues();
+  scheduler.Run();
+  EXPECT_FALSE(scheduler.IsScheduled(&command));
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandSendableButtonTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandSendableButtonTest.cpp
new file mode 100644
index 0000000..08caa5c
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandSendableButtonTest.cpp
@@ -0,0 +1,98 @@
+// 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 <frc2/command/Commands.h>
+
+#include <frc/smartdashboard/SmartDashboard.h>
+#include <networktables/BooleanTopic.h>
+#include <networktables/NetworkTableInstance.h>
+
+#include "CommandTestBase.h"
+
+using namespace frc2;
+
+class CommandSendableButtonTest : public CommandTestBase {
+ protected:
+  int m_schedule;
+  int m_cancel;
+  nt::BooleanPublisher m_publish;
+  std::optional<CommandPtr> m_command;
+
+  void SetUp() override {
+    m_schedule = 0;
+    m_cancel = 0;
+    m_command = cmd::StartEnd([this] { m_schedule++; }, [this] { m_cancel++; });
+    m_publish = nt::NetworkTableInstance::GetDefault()
+                    .GetBooleanTopic("/SmartDashboard/command/running")
+                    .Publish();
+    frc::SmartDashboard::PutData("command", m_command->get());
+    frc::SmartDashboard::UpdateValues();
+  }
+};
+
+TEST_F(CommandSendableButtonTest, trueAndNotScheduledSchedules) {
+  // Not scheduled and true -> scheduled
+  GetScheduler().Run();
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_FALSE(m_command->IsScheduled());
+  EXPECT_EQ(0, m_schedule);
+  EXPECT_EQ(0, m_cancel);
+
+  m_publish.Set(true);
+  frc::SmartDashboard::UpdateValues();
+  GetScheduler().Run();
+  EXPECT_TRUE(m_command->IsScheduled());
+  EXPECT_EQ(1, m_schedule);
+  EXPECT_EQ(0, m_cancel);
+}
+
+TEST_F(CommandSendableButtonTest, trueAndScheduledNoOp) {
+  // Scheduled and true -> no-op
+  m_command->Schedule();
+  GetScheduler().Run();
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_TRUE(m_command->IsScheduled());
+  EXPECT_EQ(1, m_schedule);
+  EXPECT_EQ(0, m_cancel);
+
+  m_publish.Set(true);
+  frc::SmartDashboard::UpdateValues();
+  GetScheduler().Run();
+  EXPECT_TRUE(m_command->IsScheduled());
+  EXPECT_EQ(1, m_schedule);
+  EXPECT_EQ(0, m_cancel);
+}
+
+TEST_F(CommandSendableButtonTest, falseAndNotScheduledNoOp) {
+  // Not scheduled and false -> no-op
+  GetScheduler().Run();
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_FALSE(m_command->IsScheduled());
+  EXPECT_EQ(0, m_schedule);
+  EXPECT_EQ(0, m_cancel);
+
+  m_publish.Set(false);
+  frc::SmartDashboard::UpdateValues();
+  GetScheduler().Run();
+  EXPECT_FALSE(m_command->IsScheduled());
+  EXPECT_EQ(0, m_schedule);
+  EXPECT_EQ(0, m_cancel);
+}
+
+TEST_F(CommandSendableButtonTest, falseAndScheduledCancel) {
+  // Scheduled and false -> cancel
+  m_command->Schedule();
+  GetScheduler().Run();
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_TRUE(m_command->IsScheduled());
+  EXPECT_EQ(1, m_schedule);
+  EXPECT_EQ(0, m_cancel);
+
+  m_publish.Set(false);
+  frc::SmartDashboard::UpdateValues();
+  GetScheduler().Run();
+  EXPECT_FALSE(m_command->IsScheduled());
+  EXPECT_EQ(1, m_schedule);
+  EXPECT_EQ(1, m_cancel);
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.cpp
index 6e27d24..1bd180c 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.cpp
@@ -11,22 +11,22 @@
   scheduler.CancelAll();
   scheduler.Enable();
   scheduler.GetActiveButtonLoop()->Clear();
+  scheduler.UnregisterAllSubsystems();
+
+  SetDSEnabled(true);
+}
+
+CommandTestBase::~CommandTestBase() {
+  CommandScheduler::GetInstance().GetActiveButtonLoop()->Clear();
+  CommandScheduler::GetInstance().UnregisterAllSubsystems();
 }
 
 CommandScheduler CommandTestBase::GetScheduler() {
   return CommandScheduler();
 }
 
-void CommandTestBase::SetUp() {
-  frc::sim::DriverStationSim::SetEnabled(true);
-  frc::sim::DriverStationSim::NotifyNewData();
-}
-
-void CommandTestBase::TearDown() {
-  CommandScheduler::GetInstance().GetActiveButtonLoop()->Clear();
-}
-
 void CommandTestBase::SetDSEnabled(bool enabled) {
+  frc::sim::DriverStationSim::SetDsAttached(true);
   frc::sim::DriverStationSim::SetEnabled(enabled);
   frc::sim::DriverStationSim::NotifyNewData();
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.h b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.h
index a1ab1de..586432d 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.h
@@ -4,92 +4,99 @@
 
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <utility>
 
 #include <frc/simulation/DriverStationSim.h>
+#include <gtest/gtest.h>
 
 #include "frc2/command/CommandHelper.h"
 #include "frc2/command/CommandScheduler.h"
-#include "frc2/command/SetUtilities.h"
+#include "frc2/command/Requirements.h"
 #include "frc2/command/SubsystemBase.h"
 #include "gmock/gmock.h"
-#include "gtest/gtest.h"
 #include "make_vector.h"
 
 namespace frc2 {
 
+class TestSubsystem : public SubsystemBase {
+ public:
+  explicit TestSubsystem(std::function<void()> periodic = [] {})
+      : m_periodic{periodic} {}
+  void Periodic() override { m_periodic(); }
+
+ private:
+  std::function<void()> m_periodic;
+};
+
+/**
+ * NOTE: Moving mock objects causes EXPECT_CALL to not work correctly!
+ */
+class MockCommand : public CommandHelper<Command, MockCommand> {
+ public:
+  MOCK_CONST_METHOD0(GetRequirements, wpi::SmallSet<Subsystem*, 4>());
+  MOCK_METHOD0(IsFinished, bool());
+  MOCK_CONST_METHOD0(RunsWhenDisabled, bool());
+  MOCK_METHOD0(Initialize, void());
+  MOCK_METHOD0(Execute, void());
+  MOCK_METHOD1(End, void(bool interrupted));
+
+  MockCommand() {
+    m_requirements = {};
+    EXPECT_CALL(*this, GetRequirements())
+        .WillRepeatedly(::testing::Return(m_requirements));
+    EXPECT_CALL(*this, IsFinished()).WillRepeatedly(::testing::Return(false));
+    EXPECT_CALL(*this, RunsWhenDisabled())
+        .WillRepeatedly(::testing::Return(true));
+  }
+
+  explicit MockCommand(Requirements requirements, bool finished = false,
+                       bool runWhenDisabled = true) {
+    m_requirements.insert(requirements.begin(), requirements.end());
+    EXPECT_CALL(*this, GetRequirements())
+        .WillRepeatedly(::testing::Return(m_requirements));
+    EXPECT_CALL(*this, IsFinished())
+        .WillRepeatedly(::testing::Return(finished));
+    EXPECT_CALL(*this, RunsWhenDisabled())
+        .WillRepeatedly(::testing::Return(runWhenDisabled));
+  }
+
+  MockCommand(MockCommand&& other) {
+    EXPECT_CALL(*this, IsFinished())
+        .WillRepeatedly(::testing::Return(other.IsFinished()));
+    EXPECT_CALL(*this, RunsWhenDisabled())
+        .WillRepeatedly(::testing::Return(other.RunsWhenDisabled()));
+    std::swap(m_requirements, other.m_requirements);
+    EXPECT_CALL(*this, GetRequirements())
+        .WillRepeatedly(::testing::Return(m_requirements));
+  }
+
+  MockCommand(const MockCommand& other) : CommandHelper{other} {}
+
+  void SetFinished(bool finished) {
+    EXPECT_CALL(*this, IsFinished())
+        .WillRepeatedly(::testing::Return(finished));
+  }
+
+  ~MockCommand() {  // NOLINT
+    auto& scheduler = CommandScheduler::GetInstance();
+    scheduler.Cancel(this);
+  }
+
+ private:
+  wpi::SmallSet<Subsystem*, 4> m_requirements;
+};
+
 class CommandTestBase : public ::testing::Test {
  public:
   CommandTestBase();
 
-  class TestSubsystem : public SubsystemBase {};
+  ~CommandTestBase() override;
 
  protected:
-  /**
-   * NOTE: Moving mock objects causes EXPECT_CALL to not work correctly!
-   */
-  class MockCommand : public CommandHelper<CommandBase, MockCommand> {
-   public:
-    MOCK_CONST_METHOD0(GetRequirements, wpi::SmallSet<Subsystem*, 4>());
-    MOCK_METHOD0(IsFinished, bool());
-    MOCK_CONST_METHOD0(RunsWhenDisabled, bool());
-    MOCK_METHOD0(Initialize, void());
-    MOCK_METHOD0(Execute, void());
-    MOCK_METHOD1(End, void(bool interrupted));
-
-    MockCommand() {
-      m_requirements = {};
-      EXPECT_CALL(*this, GetRequirements())
-          .WillRepeatedly(::testing::Return(m_requirements));
-      EXPECT_CALL(*this, IsFinished()).WillRepeatedly(::testing::Return(false));
-      EXPECT_CALL(*this, RunsWhenDisabled())
-          .WillRepeatedly(::testing::Return(true));
-    }
-
-    MockCommand(std::initializer_list<Subsystem*> requirements,
-                bool finished = false, bool runWhenDisabled = true) {
-      m_requirements.insert(requirements.begin(), requirements.end());
-      EXPECT_CALL(*this, GetRequirements())
-          .WillRepeatedly(::testing::Return(m_requirements));
-      EXPECT_CALL(*this, IsFinished())
-          .WillRepeatedly(::testing::Return(finished));
-      EXPECT_CALL(*this, RunsWhenDisabled())
-          .WillRepeatedly(::testing::Return(runWhenDisabled));
-    }
-
-    MockCommand(MockCommand&& other) {
-      EXPECT_CALL(*this, IsFinished())
-          .WillRepeatedly(::testing::Return(other.IsFinished()));
-      EXPECT_CALL(*this, RunsWhenDisabled())
-          .WillRepeatedly(::testing::Return(other.RunsWhenDisabled()));
-      std::swap(m_requirements, other.m_requirements);
-      EXPECT_CALL(*this, GetRequirements())
-          .WillRepeatedly(::testing::Return(m_requirements));
-    }
-
-    MockCommand(const MockCommand& other) : CommandHelper{other} {}
-
-    void SetFinished(bool finished) {
-      EXPECT_CALL(*this, IsFinished())
-          .WillRepeatedly(::testing::Return(finished));
-    }
-
-    ~MockCommand() {  // NOLINT
-      auto& scheduler = CommandScheduler::GetInstance();
-      scheduler.Cancel(this);
-    }
-
-   private:
-    wpi::SmallSet<Subsystem*, 4> m_requirements;
-  };
-
   CommandScheduler GetScheduler();
 
-  void SetUp() override;
-
-  void TearDown() override;
-
   void SetDSEnabled(bool enabled);
 };
 
@@ -101,81 +108,23 @@
     scheduler.CancelAll();
     scheduler.Enable();
     scheduler.GetActiveButtonLoop()->Clear();
+    scheduler.UnregisterAllSubsystems();
+
+    SetDSEnabled(true);
   }
 
-  class TestSubsystem : public SubsystemBase {};
+  ~CommandTestBaseWithParam() override {
+    CommandScheduler::GetInstance().GetActiveButtonLoop()->Clear();
+    CommandScheduler::GetInstance().UnregisterAllSubsystems();
+  }
 
  protected:
-  class MockCommand : public Command {
-   public:
-    MOCK_CONST_METHOD0(GetRequirements, wpi::SmallSet<Subsystem*, 4>());
-    MOCK_METHOD0(IsFinished, bool());
-    MOCK_CONST_METHOD0(RunsWhenDisabled, bool());
-    MOCK_METHOD0(Initialize, void());
-    MOCK_METHOD0(Execute, void());
-    MOCK_METHOD1(End, void(bool interrupted));
-
-    MockCommand() {
-      m_requirements = {};
-      EXPECT_CALL(*this, GetRequirements())
-          .WillRepeatedly(::testing::Return(m_requirements));
-      EXPECT_CALL(*this, IsFinished()).WillRepeatedly(::testing::Return(false));
-      EXPECT_CALL(*this, RunsWhenDisabled())
-          .WillRepeatedly(::testing::Return(true));
-    }
-
-    MockCommand(std::initializer_list<Subsystem*> requirements,
-                bool finished = false, bool runWhenDisabled = true) {
-      m_requirements.insert(requirements.begin(), requirements.end());
-      EXPECT_CALL(*this, GetRequirements())
-          .WillRepeatedly(::testing::Return(m_requirements));
-      EXPECT_CALL(*this, IsFinished())
-          .WillRepeatedly(::testing::Return(finished));
-      EXPECT_CALL(*this, RunsWhenDisabled())
-          .WillRepeatedly(::testing::Return(runWhenDisabled));
-    }
-
-    MockCommand(MockCommand&& other) {
-      EXPECT_CALL(*this, IsFinished())
-          .WillRepeatedly(::testing::Return(other.IsFinished()));
-      EXPECT_CALL(*this, RunsWhenDisabled())
-          .WillRepeatedly(::testing::Return(other.RunsWhenDisabled()));
-      std::swap(m_requirements, other.m_requirements);
-      EXPECT_CALL(*this, GetRequirements())
-          .WillRepeatedly(::testing::Return(m_requirements));
-    }
-
-    MockCommand(const MockCommand& other) : Command{other} {}
-
-    void SetFinished(bool finished) {
-      EXPECT_CALL(*this, IsFinished())
-          .WillRepeatedly(::testing::Return(finished));
-    }
-
-    ~MockCommand() {  // NOLINT
-      auto& scheduler = CommandScheduler::GetInstance();
-      scheduler.Cancel(this);
-    }
-
-   protected:
-    std::unique_ptr<Command> TransferOwnership() && {  // NOLINT
-      return std::make_unique<MockCommand>(std::move(*this));
-    }
-
-   private:
-    wpi::SmallSet<Subsystem*, 4> m_requirements;
-  };
-
   CommandScheduler GetScheduler() { return CommandScheduler(); }
 
-  void SetUp() override { frc::sim::DriverStationSim::SetEnabled(true); }
-
-  void TearDown() override {
-    CommandScheduler::GetInstance().GetActiveButtonLoop()->Clear();
-  }
-
   void SetDSEnabled(bool enabled) {
+    frc::sim::DriverStationSim::SetDsAttached(true);
     frc::sim::DriverStationSim::SetEnabled(enabled);
+    frc::sim::DriverStationSim::NotifyNewData();
   }
 };
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CompositionTestBase.h b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CompositionTestBase.h
index c12922f..58fbcc3 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CompositionTestBase.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/CompositionTestBase.h
@@ -7,9 +7,10 @@
 #include <memory>
 #include <utility>
 
+#include <gtest/gtest.h>
+
 #include "CommandTestBase.h"
 #include "frc2/command/Commands.h"
-#include "gtest/gtest.h"
 #include "make_vector.h"
 
 namespace frc2 {
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/ConditionalCommandTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/ConditionalCommandTest.cpp
index 27b89e5..d7bbd26 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/ConditionalCommandTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/ConditionalCommandTest.cpp
@@ -3,9 +3,9 @@
 // the WPILib BSD license file in the root directory of this project.
 
 #include "CommandTestBase.h"
+#include "frc2/command/Commands.h"
 #include "frc2/command/ConditionalCommand.h"
 #include "frc2/command/InstantCommand.h"
-#include "frc2/command/SelectCommand.h"
 
 using namespace frc2;
 class ConditionalCommandTest : public CommandTestBase {};
@@ -51,3 +51,87 @@
   EXPECT_TRUE(scheduler.IsScheduled(&command3));
   EXPECT_FALSE(scheduler.IsScheduled(&conditional));
 }
+
+TEST_F(ConditionalCommandTest, AllTrue) {
+  CommandPtr command =
+      cmd::Either(cmd::WaitUntil([] { return false; }).IgnoringDisable(true),
+                  cmd::WaitUntil([] { return false; }).IgnoringDisable(true),
+                  [] { return true; });
+  EXPECT_EQ(true, command.get()->RunsWhenDisabled());
+}
+
+TEST_F(ConditionalCommandTest, AllFalse) {
+  CommandPtr command =
+      cmd::Either(cmd::WaitUntil([] { return false; }).IgnoringDisable(false),
+                  cmd::WaitUntil([] { return false; }).IgnoringDisable(false),
+                  [] { return true; });
+  EXPECT_EQ(false, command.get()->RunsWhenDisabled());
+}
+
+TEST_F(ConditionalCommandTest, OneTrueOneFalse) {
+  CommandPtr command =
+      cmd::Either(cmd::WaitUntil([] { return false; }).IgnoringDisable(true),
+                  cmd::WaitUntil([] { return false; }).IgnoringDisable(false),
+                  [] { return true; });
+  EXPECT_EQ(false, command.get()->RunsWhenDisabled());
+}
+
+TEST_F(ConditionalCommandTest, TwoFalseOneTrue) {
+  CommandPtr command =
+      cmd::Either(cmd::WaitUntil([] { return false; }).IgnoringDisable(false),
+                  cmd::WaitUntil([] { return false; }).IgnoringDisable(true),
+                  [] { return true; });
+  EXPECT_EQ(false, command.get()->RunsWhenDisabled());
+}
+
+TEST_F(ConditionalCommandTest, AllCancelSelf) {
+  CommandPtr command = cmd::Either(
+      cmd::WaitUntil([] {
+        return false;
+      }).WithInterruptBehavior(Command::InterruptionBehavior::kCancelSelf),
+      cmd::WaitUntil([] {
+        return false;
+      }).WithInterruptBehavior(Command::InterruptionBehavior::kCancelSelf),
+      [] { return true; });
+  EXPECT_EQ(Command::InterruptionBehavior::kCancelSelf,
+            command.get()->GetInterruptionBehavior());
+}
+
+TEST_F(ConditionalCommandTest, AllCancelIncoming) {
+  CommandPtr command = cmd::Either(
+      cmd::WaitUntil([] {
+        return false;
+      }).WithInterruptBehavior(Command::InterruptionBehavior::kCancelIncoming),
+      cmd::WaitUntil([] {
+        return false;
+      }).WithInterruptBehavior(Command::InterruptionBehavior::kCancelIncoming),
+      [] { return false; });
+  EXPECT_EQ(Command::InterruptionBehavior::kCancelIncoming,
+            command.get()->GetInterruptionBehavior());
+}
+
+TEST_F(ConditionalCommandTest, OneCancelSelfOneIncoming) {
+  CommandPtr command = cmd::Either(
+      cmd::WaitUntil([] {
+        return false;
+      }).WithInterruptBehavior(Command::InterruptionBehavior::kCancelSelf),
+      cmd::WaitUntil([] {
+        return false;
+      }).WithInterruptBehavior(Command::InterruptionBehavior::kCancelIncoming),
+      [] { return false; });
+  EXPECT_EQ(Command::InterruptionBehavior::kCancelSelf,
+            command.get()->GetInterruptionBehavior());
+}
+
+TEST_F(ConditionalCommandTest, OneCancelIncomingOneSelf) {
+  CommandPtr command = cmd::Either(
+      cmd::WaitUntil([] {
+        return false;
+      }).WithInterruptBehavior(Command::InterruptionBehavior::kCancelIncoming),
+      cmd::WaitUntil([] {
+        return false;
+      }).WithInterruptBehavior(Command::InterruptionBehavior::kCancelSelf),
+      [] { return false; });
+  EXPECT_EQ(Command::InterruptionBehavior::kCancelSelf,
+            command.get()->GetInterruptionBehavior());
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/DeferredCommandTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/DeferredCommandTest.cpp
new file mode 100644
index 0000000..1af8ae4
--- /dev/null
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/DeferredCommandTest.cpp
@@ -0,0 +1,75 @@
+// 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 "CommandTestBase.h"
+#include "frc2/command/Commands.h"
+#include "frc2/command/DeferredCommand.h"
+#include "frc2/command/FunctionalCommand.h"
+
+using namespace frc2;
+
+class DeferredFunctionsTest : public CommandTestBaseWithParam<bool> {};
+
+TEST_P(DeferredFunctionsTest, DeferredFunctions) {
+  int initializeCount = 0;
+  int executeCount = 0;
+  int isFinishedCount = 0;
+  int endCount = 0;
+  bool finished = false;
+
+  DeferredCommand deferred{[&] {
+                             return FunctionalCommand{
+                                 [&] { initializeCount++; },
+                                 [&] { executeCount++; },
+                                 [&](bool interrupted) {
+                                   EXPECT_EQ(interrupted, GetParam());
+                                   endCount++;
+                                 },
+                                 [&] {
+                                   isFinishedCount++;
+                                   return finished;
+                                 }}
+                                 .ToPtr();
+                           },
+                           {}};
+
+  deferred.Initialize();
+  EXPECT_EQ(1, initializeCount);
+  deferred.Execute();
+  EXPECT_EQ(1, executeCount);
+  EXPECT_FALSE(deferred.IsFinished());
+  EXPECT_EQ(1, isFinishedCount);
+  finished = true;
+  EXPECT_TRUE(deferred.IsFinished());
+  EXPECT_EQ(2, isFinishedCount);
+  deferred.End(GetParam());
+  EXPECT_EQ(1, endCount);
+}
+
+INSTANTIATE_TEST_SUITE_P(DeferredCommandTests, DeferredFunctionsTest,
+                         testing::Values(true, false));
+
+TEST(DeferredCommandTest, DeferredSupplierOnlyCalledDuringInit) {
+  int count = 0;
+  DeferredCommand command{[&count] {
+                            count++;
+                            return cmd::None();
+                          },
+                          {}};
+
+  EXPECT_EQ(0, count);
+  command.Initialize();
+  EXPECT_EQ(1, count);
+  command.Execute();
+  command.IsFinished();
+  command.End(false);
+  EXPECT_EQ(1, count);
+}
+
+TEST(DeferredCommandTest, DeferredRequirements) {
+  TestSubsystem subsystem;
+  DeferredCommand command{cmd::None, {&subsystem}};
+
+  EXPECT_TRUE(command.GetRequirements().contains(&subsystem));
+}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/MecanumControllerCommandTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/MecanumControllerCommandTest.cpp
index 7f5b590..c79432d 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/MecanumControllerCommandTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/MecanumControllerCommandTest.cpp
@@ -16,8 +16,9 @@
 #include <frc/kinematics/MecanumDriveOdometry.h>
 #include <frc/simulation/SimHooks.h>
 #include <frc/trajectory/TrajectoryGenerator.h>
+#include <gtest/gtest.h>
 
-#include "gtest/gtest.h"
+#include "CommandTestBase.h"
 
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
   EXPECT_LE(units::math::abs(val1 - val2), eps)
@@ -87,7 +88,7 @@
 };
 
 TEST_F(MecanumControllerCommandTest, ReachesReference) {
-  frc2::Subsystem subsystem;
+  frc2::TestSubsystem subsystem;
 
   auto waypoints =
       std::vector{frc::Pose2d{0_m, 0_m, 0_rad}, frc::Pose2d{1_m, 5_m, 3_rad}};
@@ -99,7 +100,7 @@
   auto command = frc2::MecanumControllerCommand(
       trajectory, [&]() { return getRobotPose(); }, m_kinematics,
 
-      frc2::PIDController(0.6, 0, 0), frc2::PIDController(0.6, 0, 0),
+      frc::PIDController(0.6, 0, 0), frc::PIDController(0.6, 0, 0),
       m_rotController, 8.8_mps,
       [&](units::meters_per_second_t frontLeft,
           units::meters_per_second_t rearLeft,
@@ -112,8 +113,7 @@
       },
       {&subsystem});
 
-  m_timer.Reset();
-  m_timer.Start();
+  m_timer.Restart();
 
   command.Initialize();
   while (!command.IsFinished()) {
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/POVButtonTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/POVButtonTest.cpp
index bcb7dec..035fd49 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/POVButtonTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/POVButtonTest.cpp
@@ -4,13 +4,13 @@
 
 #include <frc/Joystick.h>
 #include <frc/simulation/JoystickSim.h>
+#include <gtest/gtest.h>
 
 #include "CommandTestBase.h"
 #include "frc2/command/CommandScheduler.h"
 #include "frc2/command/RunCommand.h"
 #include "frc2/command/WaitUntilCommand.h"
 #include "frc2/command/button/POVButton.h"
-#include "gtest/gtest.h"
 
 using namespace frc2;
 class POVButtonTest : public CommandTestBase {};
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/PerpetualCommandTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/PerpetualCommandTest.cpp
deleted file mode 100644
index b53835b..0000000
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/PerpetualCommandTest.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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 "CommandTestBase.h"
-#include "frc2/command/InstantCommand.h"
-#include "frc2/command/PerpetualCommand.h"
-
-using namespace frc2;
-class PerpetualCommandTest : public CommandTestBase {};
-
-TEST_F(PerpetualCommandTest, PerpetualCommandSchedule) {
-  CommandScheduler scheduler = GetScheduler();
-
-  bool check = false;
-
-  WPI_IGNORE_DEPRECATED
-  PerpetualCommand command{InstantCommand([&check] { check = true; }, {})};
-  WPI_UNIGNORE_DEPRECATED
-
-  scheduler.Schedule(&command);
-  scheduler.Run();
-  EXPECT_TRUE(scheduler.IsScheduled(&command));
-  EXPECT_TRUE(check);
-}
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/RepeatCommandTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/RepeatCommandTest.cpp
index b715983..b5456fa 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/RepeatCommandTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/RepeatCommandTest.cpp
@@ -60,6 +60,19 @@
   EXPECT_EQ(3, exeCounter);
   EXPECT_EQ(3, isFinishedCounter);
   EXPECT_EQ(1, endCounter);
+
+  isFinishedHook = true;
+  scheduler.Run();
+  EXPECT_EQ(2, initCounter);
+  EXPECT_EQ(4, exeCounter);
+  EXPECT_EQ(4, isFinishedCounter);
+  EXPECT_EQ(2, endCounter);
+
+  command.Cancel();
+  EXPECT_EQ(2, initCounter);
+  EXPECT_EQ(4, exeCounter);
+  EXPECT_EQ(4, isFinishedCounter);
+  EXPECT_EQ(2, endCounter);
 }
 
 INSTANTIATE_SINGLE_COMMAND_COMPOSITION_TEST_SUITE(RepeatCommandTest,
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SchedulerTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SchedulerTest.cpp
index ce02ba0..ef97bb0 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SchedulerTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SchedulerTest.cpp
@@ -43,14 +43,98 @@
   EXPECT_EQ(counter, 1);
 }
 
+TEST_F(SchedulerTest, SchedulerLambdaInterruptNoCause) {
+  CommandScheduler scheduler = GetScheduler();
+
+  int counter = 0;
+
+  scheduler.OnCommandInterrupt(
+      [&counter](const Command&, const std::optional<Command*>& interruptor) {
+        EXPECT_FALSE(interruptor);
+        counter++;
+      });
+
+  RunCommand command([] {});
+
+  scheduler.Schedule(&command);
+  scheduler.Cancel(&command);
+
+  EXPECT_EQ(1, counter);
+}
+
+TEST_F(SchedulerTest, SchedulerLambdaInterruptCause) {
+  CommandScheduler scheduler = GetScheduler();
+
+  int counter = 0;
+
+  TestSubsystem subsystem{};
+  RunCommand command([] {}, {&subsystem});
+  InstantCommand interruptor([] {}, {&subsystem});
+
+  scheduler.OnCommandInterrupt(
+      [&](const Command&, const std::optional<Command*>& cause) {
+        ASSERT_TRUE(cause);
+        EXPECT_EQ(&interruptor, *cause);
+        counter++;
+      });
+
+  scheduler.Schedule(&command);
+  scheduler.Schedule(&interruptor);
+
+  EXPECT_EQ(1, counter);
+}
+
+TEST_F(SchedulerTest, SchedulerLambdaInterruptCauseInRunLoop) {
+  CommandScheduler scheduler = GetScheduler();
+
+  int counter = 0;
+
+  TestSubsystem subsystem{};
+  RunCommand command([] {}, {&subsystem});
+  InstantCommand interruptor([] {}, {&subsystem});
+  // This command will schedule interruptor in execute() inside the run loop
+  InstantCommand interruptorScheduler(
+      [&] { scheduler.Schedule(&interruptor); });
+
+  scheduler.OnCommandInterrupt(
+      [&](const Command&, const std::optional<Command*>& cause) {
+        ASSERT_TRUE(cause);
+        EXPECT_EQ(&interruptor, *cause);
+        counter++;
+      });
+
+  scheduler.Schedule(&command);
+  scheduler.Schedule(&interruptorScheduler);
+
+  scheduler.Run();
+
+  EXPECT_EQ(1, counter);
+}
+
+TEST_F(SchedulerTest, RegisterSubsystem) {
+  CommandScheduler scheduler = GetScheduler();
+
+  int counter = 0;
+  TestSubsystem system{[&counter] { counter++; }};
+
+  EXPECT_NO_FATAL_FAILURE(scheduler.RegisterSubsystem(&system));
+
+  scheduler.Run();
+  EXPECT_EQ(counter, 1);
+}
+
 TEST_F(SchedulerTest, UnregisterSubsystem) {
   CommandScheduler scheduler = GetScheduler();
 
-  TestSubsystem system;
+  int counter = 0;
+  TestSubsystem system{[&counter] { counter++; }};
 
   scheduler.RegisterSubsystem(&system);
 
   EXPECT_NO_FATAL_FAILURE(scheduler.UnregisterSubsystem(&system));
+
+  scheduler.Run();
+  ASSERT_EQ(counter, 0);
 }
 
 TEST_F(SchedulerTest, SchedulerCancelAll) {
@@ -62,6 +146,10 @@
   int counter = 0;
 
   scheduler.OnCommandInterrupt([&counter](const Command&) { counter++; });
+  scheduler.OnCommandInterrupt(
+      [](const Command&, const std::optional<Command*>& interruptor) {
+        EXPECT_FALSE(interruptor);
+      });
 
   scheduler.Schedule(&command);
   scheduler.Schedule(&command2);
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SchedulingRecursionTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SchedulingRecursionTest.cpp
index 4aa5199..3735303 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SchedulingRecursionTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SchedulingRecursionTest.cpp
@@ -2,11 +2,13 @@
 // 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 <gtest/gtest.h>
+
 #include "CommandTestBase.h"
 #include "frc2/command/Command.h"
 #include "frc2/command/CommandHelper.h"
+#include "frc2/command/FunctionalCommand.h"
 #include "frc2/command/RunCommand.h"
-#include "gtest/gtest.h"
 
 using namespace frc2;
 
@@ -14,23 +16,29 @@
     : public CommandTestBaseWithParam<Command::InterruptionBehavior> {};
 
 class SelfCancellingCommand
-    : public CommandHelper<CommandBase, SelfCancellingCommand> {
+    : public CommandHelper<Command, SelfCancellingCommand> {
  public:
-  SelfCancellingCommand(CommandScheduler* scheduler, Subsystem* requirement,
+  SelfCancellingCommand(CommandScheduler* scheduler, int& counter,
+                        Subsystem* requirement,
                         Command::InterruptionBehavior interruptionBehavior =
                             Command::InterruptionBehavior::kCancelSelf)
-      : m_scheduler(scheduler), m_interrupt(interruptionBehavior) {
+      : m_scheduler(scheduler),
+        m_counter(counter),
+        m_interrupt(interruptionBehavior) {
     AddRequirements(requirement);
   }
 
   void Initialize() override { m_scheduler->Cancel(this); }
 
+  void End(bool interrupted) override { m_counter++; }
+
   InterruptionBehavior GetInterruptionBehavior() const override {
     return m_interrupt;
   }
 
  private:
   CommandScheduler* m_scheduler;
+  int& m_counter;
   InterruptionBehavior m_interrupt;
 };
 
@@ -38,13 +46,14 @@
  * Checks <a
  * href="https://github.com/wpilibsuite/allwpilib/issues/4259">wpilibsuite/allwpilib#4259</a>.
  */
-TEST_F(SchedulingRecursionTest, CancelFromInitialize) {
+TEST_P(SchedulingRecursionTest, CancelFromInitialize) {
   CommandScheduler scheduler = GetScheduler();
   bool hasOtherRun = false;
+  int counter = 0;
   TestSubsystem requirement;
-  auto selfCancels = SelfCancellingCommand(&scheduler, &requirement);
-  RunCommand other =
-      RunCommand([&hasOtherRun] { hasOtherRun = true; }, {&requirement});
+  SelfCancellingCommand selfCancels{&scheduler, counter, &requirement,
+                                    GetParam()};
+  RunCommand other{[&hasOtherRun] { hasOtherRun = true; }, {&requirement}};
 
   scheduler.Schedule(&selfCancels);
   scheduler.Run();
@@ -52,19 +61,45 @@
 
   EXPECT_FALSE(scheduler.IsScheduled(&selfCancels));
   EXPECT_TRUE(scheduler.IsScheduled(&other));
+  EXPECT_EQ(1, counter);
+  scheduler.Run();
+  EXPECT_TRUE(hasOtherRun);
+}
+
+TEST_F(SchedulingRecursionTest, CancelFromInitializeAction) {
+  CommandScheduler scheduler = GetScheduler();
+  bool hasOtherRun = false;
+  int counter = 0;
+  TestSubsystem requirement;
+  FunctionalCommand selfCancels{[] {},
+                                [] {},
+                                [&counter](bool) { counter++; },
+                                [] { return false; },
+                                {&requirement}};
+  RunCommand other{[&hasOtherRun] { hasOtherRun = true; }, {&requirement}};
+  scheduler.OnCommandInitialize([&scheduler, &selfCancels](const Command&) {
+    scheduler.Cancel(&selfCancels);
+  });
+  scheduler.Schedule(&selfCancels);
+  scheduler.Run();
+  scheduler.Schedule(&other);
+
+  EXPECT_FALSE(scheduler.IsScheduled(&selfCancels));
+  EXPECT_TRUE(scheduler.IsScheduled(&other));
+  EXPECT_EQ(1, counter);
   scheduler.Run();
   EXPECT_TRUE(hasOtherRun);
 }
 
 TEST_P(SchedulingRecursionTest,
-       DISABLED_DefaultCommandGetsRescheduledAfterSelfCanceling) {
+       DefaultCommandGetsRescheduledAfterSelfCanceling) {
   CommandScheduler scheduler = GetScheduler();
   bool hasOtherRun = false;
+  int counter = 0;
   TestSubsystem requirement;
-  auto selfCancels =
-      SelfCancellingCommand(&scheduler, &requirement, GetParam());
-  RunCommand other =
-      RunCommand([&hasOtherRun] { hasOtherRun = true; }, {&requirement});
+  SelfCancellingCommand selfCancels{&scheduler, counter, &requirement,
+                                    GetParam()};
+  RunCommand other{[&hasOtherRun] { hasOtherRun = true; }, {&requirement}};
   scheduler.SetDefaultCommand(&requirement, std::move(other));
 
   scheduler.Schedule(&selfCancels);
@@ -72,11 +107,12 @@
   scheduler.Run();
   EXPECT_FALSE(scheduler.IsScheduled(&selfCancels));
   EXPECT_TRUE(scheduler.IsScheduled(scheduler.GetDefaultCommand(&requirement)));
+  EXPECT_EQ(1, counter);
   scheduler.Run();
   EXPECT_TRUE(hasOtherRun);
 }
 
-class CancelEndCommand : public CommandHelper<CommandBase, CancelEndCommand> {
+class CancelEndCommand : public CommandHelper<Command, CancelEndCommand> {
  public:
   CancelEndCommand(CommandScheduler* scheduler, int& counter)
       : m_scheduler(scheduler), m_counter(counter) {}
@@ -103,6 +139,204 @@
   EXPECT_FALSE(scheduler.IsScheduled(&selfCancels));
 }
 
+TEST_F(SchedulingRecursionTest, CancelFromInterruptAction) {
+  CommandScheduler scheduler = GetScheduler();
+  int counter = 0;
+  FunctionalCommand selfCancels{[] {}, [] {}, [](bool) {},
+                                [] { return false; }};
+  scheduler.OnCommandInterrupt([&](const Command&) {
+    counter++;
+    scheduler.Cancel(&selfCancels);
+  });
+  scheduler.Schedule(&selfCancels);
+
+  EXPECT_NO_THROW({ scheduler.Cancel(&selfCancels); });
+  EXPECT_EQ(1, counter);
+  EXPECT_FALSE(scheduler.IsScheduled(&selfCancels));
+}
+
+class EndCommand : public CommandHelper<Command, EndCommand> {
+ public:
+  explicit EndCommand(std::function<void(bool)> end) : m_end(end) {}
+  void End(bool interrupted) override { m_end(interrupted); }
+  bool IsFinished() override { return true; }
+
+ private:
+  std::function<void(bool)> m_end;
+};
+
+TEST_F(SchedulingRecursionTest, CancelFromEndLoop) {
+  CommandScheduler scheduler = GetScheduler();
+  int counter = 0;
+  EndCommand dCancelsAll([&](bool) {
+    counter++;
+    scheduler.CancelAll();
+  });
+  EndCommand cCancelsD([&](bool) {
+    counter++;
+    scheduler.Cancel(&dCancelsAll);
+  });
+  EndCommand bCancelsC([&](bool) {
+    counter++;
+    scheduler.Cancel(&cCancelsD);
+  });
+  EndCommand aCancelsB([&](bool) {
+    counter++;
+    scheduler.Cancel(&bCancelsC);
+  });
+  scheduler.Schedule(&aCancelsB);
+  scheduler.Schedule(&bCancelsC);
+  scheduler.Schedule(&cCancelsD);
+  scheduler.Schedule(&dCancelsAll);
+
+  EXPECT_NO_THROW({ scheduler.Cancel(&aCancelsB); });
+  EXPECT_EQ(4, counter);
+  EXPECT_FALSE(scheduler.IsScheduled(&aCancelsB));
+  EXPECT_FALSE(scheduler.IsScheduled(&bCancelsC));
+  EXPECT_FALSE(scheduler.IsScheduled(&cCancelsD));
+  EXPECT_FALSE(scheduler.IsScheduled(&dCancelsAll));
+}
+
+TEST_F(SchedulingRecursionTest, CancelFromEndLoopWhileInRunLoop) {
+  CommandScheduler scheduler = GetScheduler();
+  int counter = 0;
+  EndCommand dCancelsAll([&](bool) {
+    counter++;
+    scheduler.CancelAll();
+  });
+  EndCommand cCancelsD([&](bool) {
+    counter++;
+    scheduler.Cancel(&dCancelsAll);
+  });
+  EndCommand bCancelsC([&](bool) {
+    counter++;
+    scheduler.Cancel(&cCancelsD);
+  });
+  EndCommand aCancelsB([&](bool) {
+    counter++;
+    scheduler.Cancel(&bCancelsC);
+  });
+  scheduler.Schedule(&aCancelsB);
+  scheduler.Schedule(&bCancelsC);
+  scheduler.Schedule(&cCancelsD);
+  scheduler.Schedule(&dCancelsAll);
+
+  EXPECT_NO_THROW({ scheduler.Run(); });
+  EXPECT_EQ(4, counter);
+  EXPECT_FALSE(scheduler.IsScheduled(&aCancelsB));
+  EXPECT_FALSE(scheduler.IsScheduled(&bCancelsC));
+  EXPECT_FALSE(scheduler.IsScheduled(&cCancelsD));
+  EXPECT_FALSE(scheduler.IsScheduled(&dCancelsAll));
+}
+
+class MultiCancelCommand : public CommandHelper<Command, MultiCancelCommand> {
+ public:
+  MultiCancelCommand(CommandScheduler* scheduler, int& counter,
+                     Command* command)
+      : m_scheduler(scheduler), m_counter(counter), m_command(command) {}
+
+  void End(bool interrupted) override {
+    m_counter++;
+    m_scheduler->Cancel(m_command);
+    m_scheduler->Cancel(this);
+  }
+
+ private:
+  CommandScheduler* m_scheduler;
+  int& m_counter;
+  Command* m_command;
+};
+
+TEST_F(SchedulingRecursionTest, MultiCancelFromEnd) {
+  CommandScheduler scheduler = GetScheduler();
+  int counter = 0;
+  EndCommand bIncrementsCounter([&counter](bool) { counter++; });
+  MultiCancelCommand aCancelsB{&scheduler, counter, &bIncrementsCounter};
+
+  scheduler.Schedule(&aCancelsB);
+  scheduler.Schedule(&bIncrementsCounter);
+
+  EXPECT_NO_THROW({ scheduler.Cancel(&aCancelsB); });
+  EXPECT_EQ(2, counter);
+  EXPECT_FALSE(scheduler.IsScheduled(&aCancelsB));
+  EXPECT_FALSE(scheduler.IsScheduled(&bIncrementsCounter));
+}
+
+TEST_P(SchedulingRecursionTest, ScheduleFromEndCancel) {
+  CommandScheduler scheduler = GetScheduler();
+  int counter = 0;
+  TestSubsystem requirement;
+  SelfCancellingCommand selfCancels{&scheduler, counter, &requirement,
+                                    GetParam()};
+  RunCommand other{[] {}, {&requirement}};
+
+  scheduler.Schedule(&selfCancels);
+  EXPECT_NO_THROW({ scheduler.Cancel(&selfCancels); });
+  EXPECT_EQ(1, counter);
+  EXPECT_FALSE(scheduler.IsScheduled(&selfCancels));
+}
+
+TEST_P(SchedulingRecursionTest, ScheduleFromEndInterrupt) {
+  CommandScheduler scheduler = GetScheduler();
+  int counter = 0;
+  TestSubsystem requirement;
+  SelfCancellingCommand selfCancels{&scheduler, counter, &requirement,
+                                    GetParam()};
+  RunCommand other{[] {}, {&requirement}};
+
+  scheduler.Schedule(&selfCancels);
+  EXPECT_NO_THROW({ scheduler.Schedule(&other); });
+  EXPECT_EQ(1, counter);
+  EXPECT_FALSE(scheduler.IsScheduled(&selfCancels));
+  EXPECT_TRUE(scheduler.IsScheduled(&other));
+}
+
+TEST_F(SchedulingRecursionTest, ScheduleFromEndInterruptAction) {
+  CommandScheduler scheduler = GetScheduler();
+  int counter = 0;
+  TestSubsystem requirement;
+  RunCommand selfCancels{[] {}, {&requirement}};
+  RunCommand other{[] {}, {&requirement}};
+  scheduler.OnCommandInterrupt([&](const Command&) {
+    counter++;
+    scheduler.Schedule(&other);
+  });
+  scheduler.Schedule(&selfCancels);
+  EXPECT_NO_THROW({ scheduler.Schedule(&other); });
+  EXPECT_EQ(1, counter);
+  EXPECT_FALSE(scheduler.IsScheduled(&selfCancels));
+  EXPECT_TRUE(scheduler.IsScheduled(&other));
+}
+
+TEST_F(SchedulingRecursionTest, CancelDefaultCommandFromEnd) {
+  CommandScheduler scheduler = GetScheduler();
+  int counter = 0;
+  TestSubsystem requirement;
+  FunctionalCommand defaultCommand{[] {},
+                                   [] {},
+                                   [&counter](bool) { counter++; },
+                                   [] { return false; },
+                                   {&requirement}};
+  RunCommand other{[] {}, {&requirement}};
+  FunctionalCommand cancelDefaultCommand{[] {}, [] {},
+                                         [&](bool) {
+                                           counter++;
+                                           scheduler.Schedule(&other);
+                                         },
+                                         [] { return false; }};
+
+  EXPECT_NO_THROW({
+    scheduler.Schedule(&cancelDefaultCommand);
+    scheduler.SetDefaultCommand(&requirement, std::move(defaultCommand));
+
+    scheduler.Run();
+    scheduler.Cancel(&cancelDefaultCommand);
+  });
+  EXPECT_EQ(2, counter);
+  EXPECT_FALSE(scheduler.IsScheduled(&defaultCommand));
+  EXPECT_TRUE(scheduler.IsScheduled(&other));
+}
+
 INSTANTIATE_TEST_SUITE_P(
     SchedulingRecursionTests, SchedulingRecursionTest,
     testing::Values(Command::InterruptionBehavior::kCancelSelf,
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SwerveControllerCommandTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SwerveControllerCommandTest.cpp
index 531e9d2..b96a1e1 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SwerveControllerCommandTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/SwerveControllerCommandTest.cpp
@@ -17,8 +17,9 @@
 #include <frc/kinematics/SwerveModuleState.h>
 #include <frc/simulation/SimHooks.h>
 #include <frc/trajectory/TrajectoryGenerator.h>
+#include <gtest/gtest.h>
 
-#include "gtest/gtest.h"
+#include "CommandTestBase.h"
 
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
   EXPECT_LE(units::math::abs(val1 - val2), eps)
@@ -72,7 +73,7 @@
 };
 
 TEST_F(SwerveControllerCommandTest, ReachesReference) {
-  frc2::Subsystem subsystem;
+  frc2::TestSubsystem subsystem;
 
   auto waypoints =
       std::vector{frc::Pose2d{0_m, 0_m, 0_rad}, frc::Pose2d{1_m, 5_m, 3_rad}};
@@ -84,12 +85,11 @@
   auto command = frc2::SwerveControllerCommand<4>(
       trajectory, [&]() { return getRobotPose(); }, m_kinematics,
 
-      frc2::PIDController(0.6, 0, 0), frc2::PIDController(0.6, 0, 0),
+      frc::PIDController(0.6, 0, 0), frc::PIDController(0.6, 0, 0),
       m_rotController,
       [&](auto moduleStates) { m_moduleStates = moduleStates; }, {&subsystem});
 
-  m_timer.Reset();
-  m_timer.Start();
+  m_timer.Restart();
 
   command.Initialize();
   while (!command.IsFinished()) {
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/button/NetworkButtonTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/button/NetworkButtonTest.cpp
index 51da1d2..3f969ac 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/button/NetworkButtonTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/button/NetworkButtonTest.cpp
@@ -2,6 +2,7 @@
 // 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 <gtest/gtest.h>
 #include <networktables/NetworkTableInstance.h>
 
 #include "../CommandTestBase.h"
@@ -9,7 +10,6 @@
 #include "frc2/command/RunCommand.h"
 #include "frc2/command/WaitUntilCommand.h"
 #include "frc2/command/button/NetworkButton.h"
-#include "gtest/gtest.h"
 
 using namespace frc2;
 
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp
index 1a0af51..4acaa66 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp
@@ -3,6 +3,7 @@
 // the WPILib BSD license file in the root directory of this project.
 
 #include <frc/simulation/SimHooks.h>
+#include <gtest/gtest.h>
 
 #include "../CommandTestBase.h"
 #include "frc2/command/CommandPtr.h"
@@ -11,7 +12,6 @@
 #include "frc2/command/RunCommand.h"
 #include "frc2/command/WaitUntilCommand.h"
 #include "frc2/command/button/Trigger.h"
-#include "gtest/gtest.h"
 
 using namespace frc2;
 class TriggerTest : public CommandTestBase {};
@@ -206,24 +206,6 @@
   EXPECT_TRUE(scheduler.IsScheduled(&command));
 }
 
-// this type of binding is deprecated and identical to OnTrue
-WPI_IGNORE_DEPRECATED
-TEST_F(TriggerTest, RValueTrigger) {
-  auto& scheduler = CommandScheduler::GetInstance();
-  int counter = 0;
-  bool pressed = false;
-
-  RunCommand command([&counter] { counter++; }, {});
-
-  Trigger([&pressed] { return pressed; }).WhenActive(std::move(command));
-  scheduler.Run();
-  EXPECT_EQ(counter, 0);
-  pressed = true;
-  scheduler.Run();
-  EXPECT_EQ(counter, 1);
-}
-WPI_UNIGNORE_DEPRECATED
-
 TEST_F(TriggerTest, Debounce) {
   auto& scheduler = CommandScheduler::GetInstance();
   bool pressed = false;
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/make_vector.h b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/make_vector.h
index 05adf8e..996ddba 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/make_vector.h
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/frc2/command/make_vector.h
@@ -34,8 +34,12 @@
           std::is_constructible_v<T, First> && std::is_convertible_v<First, T>,
           all_constructible_and_convertible<T, Rest...>, std::false_type> {};
 
-template <typename T, typename... Args,
-          typename std::enable_if_t<!std::is_trivially_copyable_v<T>, int> = 0>
+template <typename T, typename First, typename... Rest>
+inline constexpr bool all_constructible_and_convertible_v =
+    all_constructible_and_convertible<T, First, Rest...>::value;
+
+template <typename T, typename... Args>
+  requires(!std::is_trivially_copyable_v<T>)
 std::vector<T> make_vector_impl(Args&&... args) {
   std::vector<T> vec;
   vec.reserve(sizeof...(Args));
@@ -44,19 +48,17 @@
   return vec;
 }
 
-template <typename T, typename... Args,
-          typename std::enable_if_t<std::is_trivially_copyable_v<T>, int> = 0>
+template <typename T, typename... Args>
+  requires std::is_trivially_copyable_v<T>
 std::vector<T> make_vector_impl(Args&&... args) {
   return std::vector<T>{std::forward<Args>(args)...};
 }
 
 }  // namespace detail
 
-template <
-    typename T = void, typename... Args,
-    typename V = detail::vec_type_helper_t<T, Args...>,
-    typename std::enable_if_t<
-        detail::all_constructible_and_convertible<V, Args...>::value, int> = 0>
+template <typename T = void, typename... Args,
+          typename V = detail::vec_type_helper_t<T, Args...>>
+  requires detail::all_constructible_and_convertible_v<V, Args...>
 std::vector<V> make_vector(Args&&... args) {
   return detail::make_vector_impl<V>(std::forward<Args>(args)...);
 }
diff --git a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/main.cpp b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/main.cpp
index 6aea19a..d181e39 100644
--- a/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/wpilibNewCommands/src/test/native/cpp/main.cpp
@@ -2,10 +2,9 @@
 // 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 <gtest/gtest.h>
 #include <hal/HALBase.h>
 
-#include "gtest/gtest.h"
-
 int main(int argc, char** argv) {
   HAL_Initialize(500, 0);
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/third_party/allwpilib/wpilibNewCommands/wpilibNewCommands-config.cmake.in b/third_party/allwpilib/wpilibNewCommands/wpilibNewCommands-config.cmake.in
index 75aa6ad..8a8d8d8 100644
--- a/third_party/allwpilib/wpilibNewCommands/wpilibNewCommands-config.cmake.in
+++ b/third_party/allwpilib/wpilibNewCommands/wpilibNewCommands-config.cmake.in
@@ -1,5 +1,4 @@
 include(CMakeFindDependencyMacro)
- @FILENAME_DEP_REPLACE@
  @WPIUTIL_DEP_REPLACE@
  @NTCORE_DEP_REPLACE@
  @CSCORE_DEP_REPLACE@
@@ -8,4 +7,5 @@
  @WPILIBC_DEP_REPLACE@
  @WPIMATH_DEP_REPLACE@
 
+ @FILENAME_DEP_REPLACE@
  include(${SELF_DIR}/wpilibNewCommands.cmake)
diff --git a/third_party/allwpilib/wpilibc/CMakeLists.txt b/third_party/allwpilib/wpilibc/CMakeLists.txt
index 8c2c85c..ed85834 100644
--- a/third_party/allwpilib/wpilibc/CMakeLists.txt
+++ b/third_party/allwpilib/wpilibc/CMakeLists.txt
@@ -30,7 +30,7 @@
 
 set_property(TARGET wpilibc PROPERTY FOLDER "libraries")
 
-install(TARGETS wpilibc EXPORT wpilibc DESTINATION "${main_lib_dest}")
+install(TARGETS wpilibc EXPORT wpilibc)
 install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/wpilibc")
 
 if (WITH_FLAT_INSTALL)
diff --git a/third_party/allwpilib/wpilibc/build.gradle b/third_party/allwpilib/wpilibc/build.gradle
index 4cf4ab2..507ec44 100644
--- a/third_party/allwpilib/wpilibc/build.gradle
+++ b/third_party/allwpilib/wpilibc/build.gradle
@@ -152,7 +152,6 @@
                     lib project: ':cameraserver', library: 'cameraserver', linkage: 'shared'
                     nativeUtils.useRequiredLibrary(it, 'opencv_shared')
                 }
-
             }
             appendDebugPathToBinaries(binaries)
         }
diff --git a/third_party/allwpilib/wpilibc/publish.gradle b/third_party/allwpilib/wpilibc/publish.gradle
index d49f157..a565ba5 100644
--- a/third_party/allwpilib/wpilibc/publish.gradle
+++ b/third_party/allwpilib/wpilibc/publish.gradle
@@ -9,7 +9,7 @@
 task cppSourcesZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = zipBaseName
-    classifier = "sources"
+    archiveClassifier = "sources"
 
     from(licenseFile) {
         into '/'
@@ -28,7 +28,7 @@
 task cppHeadersZip(type: Zip) {
     destinationDirectory = outputsFolder
     archiveBaseName = zipBaseName
-    classifier = "headers"
+    archiveClassifier = "headers"
 
     from(licenseFile) {
         into '/'
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp
index 7048720..14fd37f 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp
@@ -26,7 +26,7 @@
 #include <string>
 
 #include <hal/HAL.h>
-#include <networktables/NTSendableBuilder.h>
+#include <wpi/sendable/SendableBuilder.h>
 #include <wpi/sendable/SendableRegistry.h>
 
 #include "frc/Errors.h"
@@ -285,7 +285,7 @@
   m_spi->SetAutoTransmitData({{GLOB_CMD}}, 27);
   // Configure auto stall time
   m_spi->ConfigureAutoStall(HAL_SPI_kMXP, 100, 1000, 255);
-  // Kick off DMA SPI (Note: Device configration impossible after SPI DMA is
+  // Kick off DMA SPI (Note: Device configuration impossible after SPI DMA is
   // activated)
   m_spi->StartAutoTrigger(*m_auto_interrupt, true, false);
   // Check to see if the acquire thread is running. If not, kick one off.
@@ -882,7 +882,7 @@
  * This function pushes the most recent angle estimates for all axes to the
  *driver station.
  **/
-void ADIS16448_IMU::InitSendable(nt::NTSendableBuilder& builder) {
+void ADIS16448_IMU::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("ADIS16448 IMU");
   builder.AddDoubleProperty(
       "Yaw Angle", [=, this] { return GetAngle().value(); }, nullptr);
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp
index e1cd986..05dac78 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp
@@ -23,7 +23,7 @@
 #include <string>
 
 #include <hal/HAL.h>
-#include <networktables/NTSendableBuilder.h>
+#include <wpi/sendable/SendableBuilder.h>
 #include <wpi/sendable/SendableRegistry.h>
 
 #include "frc/Errors.h"
@@ -279,7 +279,7 @@
   }
   // Configure auto stall time
   m_spi->ConfigureAutoStall(HAL_SPI_kOnboardCS0, 5, 1000, 1);
-  // Kick off DMA SPI (Note: Device configration impossible after SPI DMA is
+  // Kick off DMA SPI (Note: Device configuration impossible after SPI DMA is
   // activated) DR High = Data good (data capture should be triggered on the
   // rising edge)
   m_spi->StartAutoTrigger(*m_auto_interrupt, true, false);
@@ -816,7 +816,7 @@
  * This function pushes the most recent angle estimates for all axes to the
  *driver station.
  **/
-void ADIS16470_IMU::InitSendable(nt::NTSendableBuilder& builder) {
+void ADIS16470_IMU::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("ADIS16470 IMU");
   builder.AddDoubleProperty(
       "Yaw Angle", [=, this] { return GetAngle().value(); }, nullptr);
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/ADXL362.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/ADXL362.cpp
index fa0a8a3..425815e 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/ADXL362.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/ADXL362.cpp
@@ -93,7 +93,6 @@
       m_gsPerLSB = 0.002;
       break;
     case kRange_8G:
-    case kRange_16G:  // 16G not supported; treat as 8G
       m_gsPerLSB = 0.004;
       break;
   }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp
index 4d4b22d..c134b5c 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp
@@ -69,7 +69,7 @@
   return m_connected;
 }
 
-static bool CalcParity(int v) {
+static bool CalcParity(uint32_t v) {
   bool parity = false;
   while (v != 0) {
     parity = !parity;
@@ -87,7 +87,7 @@
 }
 
 uint16_t ADXRS450_Gyro::ReadRegister(int reg) {
-  int cmd = 0x80000000 | static_cast<int>(reg) << 17;
+  uint32_t cmd = 0x80000000 | static_cast<int>(reg) << 17;
   if (!CalcParity(cmd)) {
     cmd |= 1u;
   }
@@ -140,6 +140,10 @@
   m_spi.ResetAccumulator();
 }
 
+Rotation2d ADXRS450_Gyro::GetRotation2d() const {
+  return units::degree_t{-GetAngle()};
+}
+
 int ADXRS450_Gyro::GetPort() const {
   return m_port;
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/AddressableLED.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/AddressableLED.cpp
index 538dc10..0baae3c 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/AddressableLED.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/AddressableLED.cpp
@@ -65,14 +65,14 @@
   FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
-void AddressableLED::SetBitTiming(units::nanosecond_t lowTime0,
-                                  units::nanosecond_t highTime0,
-                                  units::nanosecond_t lowTime1,
-                                  units::nanosecond_t highTime1) {
+void AddressableLED::SetBitTiming(units::nanosecond_t highTime0,
+                                  units::nanosecond_t lowTime0,
+                                  units::nanosecond_t highTime1,
+                                  units::nanosecond_t lowTime1) {
   int32_t status = 0;
   HAL_SetAddressableLEDBitTiming(
-      m_handle, lowTime0.to<int32_t>(), highTime0.to<int32_t>(),
-      lowTime1.to<int32_t>(), highTime1.to<int32_t>(), &status);
+      m_handle, highTime0.to<int32_t>(), lowTime0.to<int32_t>(),
+      highTime1.to<int32_t>(), lowTime1.to<int32_t>(), &status);
   FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/AnalogGyro.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/AnalogGyro.cpp
index cab9f94..df6d33f 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/AnalogGyro.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/AnalogGyro.cpp
@@ -133,6 +133,10 @@
   FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
 }
 
+Rotation2d AnalogGyro::GetRotation2d() const {
+  return units::degree_t{-GetAngle()};
+}
+
 std::shared_ptr<AnalogInput> AnalogGyro::GetAnalogInput() const {
   return m_analog;
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp
index 4284e89..857643b 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp
@@ -22,11 +22,6 @@
 }
 
 void BuiltInAccelerometer::SetRange(Range range) {
-  if (range == kRange_16G) {
-    throw FRC_MakeError(err::ParameterOutOfRange,
-                        "16G range not supported (use k2G, k4G, or k8G)");
-  }
-
   HAL_SetAccelerometerActive(false);
   HAL_SetAccelerometerRange(static_cast<HAL_AccelerometerRange>(range));
   HAL_SetAccelerometerActive(true);
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/CAN.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/CAN.cpp
index fc4a821..aa0370f 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/CAN.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/CAN.cpp
@@ -131,3 +131,7 @@
     return true;
   }
 }
+
+uint64_t CAN::GetTimestampBaseTime() {
+  return HAL_GetCANPacketBaseTime();
+}
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/Compressor.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/Compressor.cpp
index 2479ffc..1f6c906 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/Compressor.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/Compressor.cpp
@@ -34,10 +34,6 @@
   }
 }
 
-bool Compressor::Enabled() const {
-  return IsEnabled();
-}
-
 bool Compressor::IsEnabled() const {
   return m_module->GetCompressor();
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/Counter.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/Counter.cpp
index 2e9c916..c33b69b 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/Counter.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/Counter.cpp
@@ -260,6 +260,18 @@
   return m_index;
 }
 
+void Counter::SetDistancePerPulse(double distancePerPulse) {
+  m_distancePerPulse = distancePerPulse;
+}
+
+double Counter::GetDistance() const {
+  return Get() * m_distancePerPulse;
+}
+
+double Counter::GetRate() const {
+  return m_distancePerPulse / GetPeriod().value();
+}
+
 int Counter::Get() const {
   int32_t status = 0;
   int value = HAL_GetCounter(m_counter, &status);
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/DataLogManager.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/DataLogManager.cpp
index b92faa3..afe9330 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/DataLogManager.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/DataLogManager.cpp
@@ -4,6 +4,8 @@
 
 #include "frc/DataLogManager.h"
 
+#include <frc/Errors.h>
+
 #include <algorithm>
 #include <ctime>
 #include <random>
@@ -19,6 +21,8 @@
 
 #include "frc/DriverStation.h"
 #include "frc/Filesystem.h"
+#include "frc/RobotBase.h"
+#include "frc/RobotController.h"
 
 using namespace frc;
 
@@ -26,6 +30,7 @@
 
 struct Thread final : public wpi::SafeThread {
   Thread(std::string_view dir, std::string_view filename, double period);
+  ~Thread() override;
 
   void Main() final;
 
@@ -66,8 +71,13 @@
       (s.permissions() & fs::perms::others_write) != fs::perms::none) {
     return std::string{usbDir};
   }
+  if (RobotBase::GetRuntimeType() == kRoboRIO) {
+    FRC_ReportError(warn::Warning,
+                    "DataLogManager: Logging to RoboRIO 1 internal storage is "
+                    "not recommended! Plug in a FAT32 formatted flash drive!");
+  }
 #endif
-  return frc::filesystem::GetOperatingDirectory();
+  return filesystem::GetOperatingDirectory();
 }
 
 static std::string MakeLogFilename(std::string_view filenameOverride) {
@@ -94,15 +104,25 @@
   StartNTLog();
 }
 
+Thread::~Thread() {
+  StopNTLog();
+}
+
 void Thread::Main() {
   // based on free disk space, scan for "old" FRC_*.wpilog files and remove
   {
-    uintmax_t freeSpace = fs::space(m_logDir).free;
+    std::error_code ec;
+    uintmax_t freeSpace;
+    auto freeSpaceInfo = fs::space(m_logDir, ec);
+    if (!ec) {
+      freeSpace = freeSpaceInfo.available;
+    } else {
+      freeSpace = UINTMAX_MAX;
+    }
     if (freeSpace < kFreeSpaceThreshold) {
       // Delete oldest FRC_*.wpilog files (ignore FRC_TBD_*.wpilog as we just
       // created one)
       std::vector<fs::directory_entry> entries;
-      std::error_code ec;
       for (auto&& entry : fs::directory_iterator{m_logDir, ec}) {
         auto stem = entry.path().stem().string();
         if (wpi::starts_with(stem, "FRC_") &&
@@ -124,6 +144,8 @@
         }
         auto size = entry.file_size();
         if (fs::remove(entry.path(), ec)) {
+          FRC_ReportError(warn::Warning, "DataLogManager: Deleted {}",
+                          entry.path().string());
           freeSpace += size;
           if (freeSpace >= kFreeSpaceThreshold) {
             break;
@@ -133,6 +155,13 @@
                      entry.path().string());
         }
       }
+    } else if (freeSpace < 2 * kFreeSpaceThreshold) {
+      FRC_ReportError(
+          warn::Warning,
+          "DataLogManager: Log storage device has {} MB of free space "
+          "remaining! Logs will get deleted below {} MB of free space. "
+          "Consider deleting logs off the storage device.",
+          freeSpace / 1000000, kFreeSpaceThreshold / 1000000);
     }
   }
 
@@ -182,11 +211,9 @@
         dsAttachCount = 0;
       }
       if (dsAttachCount > 50) {  // 1 second
-        std::time_t now = std::time(nullptr);
-        auto tm = std::gmtime(&now);
-        if (tm->tm_year > 100) {
-          // assume local clock is now synchronized to DS, so rename based on
-          // local time
+        if (RobotController::IsSystemTimeValid()) {
+          std::time_t now = std::time(nullptr);
+          auto tm = std::gmtime(&now);
           m_log.SetFilename(fmt::format("FRC_{:%Y%m%d_%H%M%S}.wpilog", *tm));
           dsRenamed = true;
         } else {
@@ -202,7 +229,7 @@
       } else {
         fmsAttachCount = 0;
       }
-      if (fmsAttachCount > 100) {  // 2 seconds
+      if (fmsAttachCount > 250) {  // 5 seconds
         // match info comes through TCP, so we need to double-check we've
         // actually received it
         auto matchType = DriverStation::GetMatchType();
@@ -238,7 +265,9 @@
     ++sysTimeCount;
     if (sysTimeCount >= 250) {
       sysTimeCount = 0;
-      sysTimeEntry.Append(wpi::GetSystemTime(), wpi::Now());
+      if (RobotController::IsSystemTimeValid()) {
+        sysTimeEntry.Append(wpi::GetSystemTime(), wpi::Now());
+      }
     }
   }
   DriverStation::RemoveRefreshedDataEventHandle(newDataEvent.GetHandle());
@@ -285,6 +314,9 @@
                              std::string_view filename = "",
                              double period = 0.25) {
   static Instance instance(dir, filename, period);
+  if (!instance.owner) {
+    instance.owner.Start(MakeLogDir(dir), filename, period);
+  }
   return instance;
 }
 
@@ -293,6 +325,12 @@
   GetInstance(dir, filename, period);
 }
 
+void DataLogManager::Stop() {
+  auto& inst = GetInstance();
+  inst.owner.GetThread()->m_log.Stop();
+  inst.owner.Stop();
+}
+
 void DataLogManager::Log(std::string_view message) {
   GetInstance().owner.GetThread()->m_messageLog.Append(message);
   fmt::print("{}\n", message);
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/DriverStation.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/DriverStation.cpp
index aead343..8f80427 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/DriverStation.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/DriverStation.cpp
@@ -484,6 +484,12 @@
   return controlWord.test;
 }
 
+bool DriverStation::IsTestEnabled() {
+  HAL_ControlWord controlWord;
+  HAL_GetControlWord(&controlWord);
+  return controlWord.test && controlWord.enabled;
+}
+
 bool DriverStation::IsDSAttached() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
@@ -527,7 +533,7 @@
   return info.replayNumber;
 }
 
-DriverStation::Alliance DriverStation::GetAlliance() {
+std::optional<DriverStation::Alliance> DriverStation::GetAlliance() {
   int32_t status = 0;
   auto allianceStationID = HAL_GetAllianceStation(&status);
   switch (allianceStationID) {
@@ -540,11 +546,11 @@
     case HAL_AllianceStationID_kBlue3:
       return kBlue;
     default:
-      return kInvalid;
+      return {};
   }
 }
 
-int DriverStation::GetLocation() {
+std::optional<int> DriverStation::GetLocation() {
   int32_t status = 0;
   auto allianceStationID = HAL_GetAllianceStation(&status);
   switch (allianceStationID) {
@@ -558,13 +564,27 @@
     case HAL_AllianceStationID_kBlue3:
       return 3;
     default:
-      return 0;
+      return {};
   }
 }
 
-double DriverStation::GetMatchTime() {
+bool DriverStation::WaitForDsConnection(units::second_t timeout) {
+  wpi::Event event{true, false};
+  HAL_ProvideNewDataEventHandle(event.GetHandle());
+  bool result = false;
+  if (timeout == 0_s) {
+    result = wpi::WaitForObject(event.GetHandle());
+  } else {
+    result = wpi::WaitForObject(event.GetHandle(), timeout.value(), nullptr);
+  }
+
+  HAL_RemoveNewDataEventHandle(event.GetHandle());
+  return result;
+}
+
+units::second_t DriverStation::GetMatchTime() {
   int32_t status = 0;
-  return HAL_GetMatchTime(&status);
+  return units::second_t{HAL_GetMatchTime(&status)};
 }
 
 double DriverStation::GetBatteryVoltage() {
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp
index b1c943e..f2ac77f 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp
@@ -164,7 +164,10 @@
   if (m_counter) {
     m_counter->Reset();
   }
-  m_positionOffset = m_dutyCycle->GetOutput();
+  if (m_simPosition) {
+    m_simPosition.Set(0);
+  }
+  m_positionOffset = GetAbsolutePosition();
 }
 
 bool DutyCycleEncoder::IsConnected() const {
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/Filesystem.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/Filesystem.cpp
index d497779..7947d75 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/Filesystem.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/Filesystem.cpp
@@ -13,7 +13,7 @@
 }
 
 std::string frc::filesystem::GetOperatingDirectory() {
-  if constexpr (RobotBase::IsReal()) {
+  if constexpr (!RobotBase::IsSimulation()) {
     return "/home/lvuser";
   } else {
     return frc::filesystem::GetLaunchDirectory();
@@ -21,7 +21,7 @@
 }
 
 std::string frc::filesystem::GetDeployDirectory() {
-  if constexpr (RobotBase::IsReal()) {
+  if constexpr (!RobotBase::IsSimulation()) {
     return "/home/lvuser/deploy";
   } else {
     return (fs::current_path() / "src" / "main" / "deploy").string();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp
index 0bccb0b..ee941b6 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp
@@ -24,6 +24,8 @@
 
 void IterativeRobotBase::RobotInit() {}
 
+void IterativeRobotBase::DriverStationConnected() {}
+
 void IterativeRobotBase::SimulationInit() {}
 
 void IterativeRobotBase::DisabledInit() {}
@@ -95,7 +97,7 @@
 }
 
 void IterativeRobotBase::EnableLiveWindowInTest(bool testLW) {
-  if (IsTest()) {
+  if (IsTestEnabled()) {
     throw FRC_MakeError(err::IncompatibleMode,
                         "Can't configure test mode while in test mode!");
   }
@@ -127,6 +129,11 @@
     mode = Mode::kTest;
   }
 
+  if (!m_calledDsConnected && word.IsDSAttached()) {
+    m_calledDsConnected = true;
+    DriverStationConnected();
+  }
+
   // If mode changed, call mode exit and entry functions
   if (m_lastMode != mode) {
     // Call last mode's exit function
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/Joystick.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/Joystick.cpp
index 0eff226..8a378c5 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/Joystick.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/Joystick.cpp
@@ -8,6 +8,9 @@
 #include <numbers>
 
 #include <hal/FRCUsageReporting.h>
+#include <units/dimensionless.h>
+#include <units/math.h>
+#include <wpi/deprecated.h>
 
 #include "frc/event/BooleanEvent.h"
 
@@ -124,5 +127,12 @@
 }
 
 double Joystick::GetDirectionDegrees() const {
+  WPI_IGNORE_DEPRECATED
   return (180 / std::numbers::pi) * GetDirectionRadians();
+  WPI_UNIGNORE_DEPRECATED
+}
+
+units::radian_t Joystick::GetDirection() const {
+  return units::math::atan2(units::dimensionless::scalar_t{GetX()},
+                            units::dimensionless::scalar_t{-GetY()});
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/Notifier.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/Notifier.cpp
index d837752..147beb4 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/Notifier.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/Notifier.cpp
@@ -17,11 +17,11 @@
 
 using namespace frc;
 
-Notifier::Notifier(std::function<void()> handler) {
-  if (!handler) {
-    throw FRC_MakeError(err::NullParameter, "handler");
+Notifier::Notifier(std::function<void()> callback) {
+  if (!callback) {
+    throw FRC_MakeError(err::NullParameter, "callback");
   }
-  m_handler = handler;
+  m_callback = callback;
   int32_t status = 0;
   m_notifier = HAL_InitializeNotifier(&status);
   FRC_CheckErrorStatus(status, "InitializeNotifier");
@@ -38,32 +38,32 @@
         break;
       }
 
-      std::function<void()> handler;
+      std::function<void()> callback;
       {
         std::scoped_lock lock(m_processMutex);
-        handler = m_handler;
+        callback = m_callback;
         if (m_periodic) {
           m_expirationTime += m_period;
           UpdateAlarm();
         } else {
-          // need to update the alarm to cause it to wait again
+          // Need to update the alarm to cause it to wait again
           UpdateAlarm(UINT64_MAX);
         }
       }
 
-      // call callback
-      if (handler) {
-        handler();
+      // Call callback
+      if (callback) {
+        callback();
       }
     }
   });
 }
 
-Notifier::Notifier(int priority, std::function<void()> handler) {
-  if (!handler) {
-    throw FRC_MakeError(err::NullParameter, "handler");
+Notifier::Notifier(int priority, std::function<void()> callback) {
+  if (!callback) {
+    throw FRC_MakeError(err::NullParameter, "callback");
   }
-  m_handler = handler;
+  m_callback = callback;
   int32_t status = 0;
   m_notifier = HAL_InitializeNotifier(&status);
   FRC_CheckErrorStatus(status, "InitializeNotifier");
@@ -81,10 +81,10 @@
         break;
       }
 
-      std::function<void()> handler;
+      std::function<void()> callback;
       {
         std::scoped_lock lock(m_processMutex);
-        handler = m_handler;
+        callback = m_callback;
         if (m_periodic) {
           m_expirationTime += m_period;
           UpdateAlarm();
@@ -95,9 +95,9 @@
       }
 
       // call callback
-      if (handler) {
+      if (callback) {
         try {
-          handler();
+          callback();
         } catch (const frc::RuntimeError& e) {
           e.Report();
           FRC_ReportError(
@@ -123,7 +123,7 @@
   HAL_StopNotifier(handle, &status);
   FRC_ReportError(status, "StopNotifier");
 
-  // Join the thread to ensure the handler has exited.
+  // Join the thread to ensure the callback has exited.
   if (m_thread.joinable()) {
     m_thread.join();
   }
@@ -134,7 +134,7 @@
 Notifier::Notifier(Notifier&& rhs)
     : m_thread(std::move(rhs.m_thread)),
       m_notifier(rhs.m_notifier.load()),
-      m_handler(std::move(rhs.m_handler)),
+      m_callback(std::move(rhs.m_callback)),
       m_expirationTime(std::move(rhs.m_expirationTime)),
       m_period(std::move(rhs.m_period)),
       m_periodic(std::move(rhs.m_periodic)) {
@@ -145,7 +145,7 @@
   m_thread = std::move(rhs.m_thread);
   m_notifier = rhs.m_notifier.load();
   rhs.m_notifier = HAL_kInvalidHandle;
-  m_handler = std::move(rhs.m_handler);
+  m_callback = std::move(rhs.m_callback);
   m_expirationTime = std::move(rhs.m_expirationTime);
   m_period = std::move(rhs.m_period);
   m_periodic = std::move(rhs.m_periodic);
@@ -161,9 +161,14 @@
   HAL_SetNotifierName(m_notifier, buf.data(), &status);
 }
 
-void Notifier::SetHandler(std::function<void()> handler) {
+void Notifier::SetHandler(std::function<void()> callback) {
   std::scoped_lock lock(m_processMutex);
-  m_handler = handler;
+  m_callback = callback;
+}
+
+void Notifier::SetCallback(std::function<void()> callback) {
+  std::scoped_lock lock(m_processMutex);
+  m_callback = callback;
 }
 
 void Notifier::StartSingle(units::second_t delay) {
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/PS4Controller.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/PS4Controller.cpp
index e59e18c..5ac3420 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/PS4Controller.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/PS4Controller.cpp
@@ -11,7 +11,8 @@
 using namespace frc;
 
 PS4Controller::PS4Controller(int port) : GenericHID(port) {
-  HAL_Report(HALUsageReporting::kResourceType_PS4Controller, port + 1);
+  // re-enable when PS4Controller is added to Usage Reporting
+  // HAL_Report(HALUsageReporting::kResourceType_PS4Controller, port + 1);
 }
 
 double PS4Controller::GetLeftX() const {
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/PS5Controller.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/PS5Controller.cpp
new file mode 100644
index 0000000..95685d6
--- /dev/null
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/PS5Controller.cpp
@@ -0,0 +1,263 @@
+// 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 "frc/PS5Controller.h"
+
+#include <hal/FRCUsageReporting.h>
+
+#include "frc/event/BooleanEvent.h"
+
+using namespace frc;
+
+PS5Controller::PS5Controller(int port) : GenericHID(port) {
+  // HAL_Report(HALUsageReporting::kResourceType_PS5Controller, port + 1);
+}
+
+double PS5Controller::GetLeftX() const {
+  return GetRawAxis(Axis::kLeftX);
+}
+
+double PS5Controller::GetRightX() const {
+  return GetRawAxis(Axis::kRightX);
+}
+
+double PS5Controller::GetLeftY() const {
+  return GetRawAxis(Axis::kLeftY);
+}
+
+double PS5Controller::GetRightY() const {
+  return GetRawAxis(Axis::kRightY);
+}
+
+double PS5Controller::GetL2Axis() const {
+  return GetRawAxis(Axis::kL2);
+}
+
+double PS5Controller::GetR2Axis() const {
+  return GetRawAxis(Axis::kR2);
+}
+
+bool PS5Controller::GetSquareButton() const {
+  return GetRawButton(Button::kSquare);
+}
+
+bool PS5Controller::GetSquareButtonPressed() {
+  return GetRawButtonPressed(Button::kSquare);
+}
+
+bool PS5Controller::GetSquareButtonReleased() {
+  return GetRawButtonReleased(Button::kSquare);
+}
+
+BooleanEvent PS5Controller::Square(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetSquareButton(); });
+}
+
+bool PS5Controller::GetCrossButton() const {
+  return GetRawButton(Button::kCross);
+}
+
+bool PS5Controller::GetCrossButtonPressed() {
+  return GetRawButtonPressed(Button::kCross);
+}
+
+bool PS5Controller::GetCrossButtonReleased() {
+  return GetRawButtonReleased(Button::kCross);
+}
+
+BooleanEvent PS5Controller::Cross(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetCrossButton(); });
+}
+
+bool PS5Controller::GetCircleButton() const {
+  return GetRawButton(Button::kCircle);
+}
+
+bool PS5Controller::GetCircleButtonPressed() {
+  return GetRawButtonPressed(Button::kCircle);
+}
+
+bool PS5Controller::GetCircleButtonReleased() {
+  return GetRawButtonReleased(Button::kCircle);
+}
+
+BooleanEvent PS5Controller::Circle(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetCircleButton(); });
+}
+
+bool PS5Controller::GetTriangleButton() const {
+  return GetRawButton(Button::kTriangle);
+}
+
+bool PS5Controller::GetTriangleButtonPressed() {
+  return GetRawButtonPressed(Button::kTriangle);
+}
+
+bool PS5Controller::GetTriangleButtonReleased() {
+  return GetRawButtonReleased(Button::kTriangle);
+}
+
+BooleanEvent PS5Controller::Triangle(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetTriangleButton(); });
+}
+
+bool PS5Controller::GetL1Button() const {
+  return GetRawButton(Button::kL1);
+}
+
+bool PS5Controller::GetL1ButtonPressed() {
+  return GetRawButtonPressed(Button::kL1);
+}
+
+bool PS5Controller::GetL1ButtonReleased() {
+  return GetRawButtonReleased(Button::kL1);
+}
+
+BooleanEvent PS5Controller::L1(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetL1Button(); });
+}
+
+bool PS5Controller::GetR1Button() const {
+  return GetRawButton(Button::kR1);
+}
+
+bool PS5Controller::GetR1ButtonPressed() {
+  return GetRawButtonPressed(Button::kR1);
+}
+
+bool PS5Controller::GetR1ButtonReleased() {
+  return GetRawButtonReleased(Button::kR1);
+}
+
+BooleanEvent PS5Controller::R1(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetR1Button(); });
+}
+
+bool PS5Controller::GetL2Button() const {
+  return GetRawButton(Button::kL2);
+}
+
+bool PS5Controller::GetL2ButtonPressed() {
+  return GetRawButtonPressed(Button::kL2);
+}
+
+bool PS5Controller::GetL2ButtonReleased() {
+  return GetRawButtonReleased(Button::kL2);
+}
+
+BooleanEvent PS5Controller::L2(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetL2Button(); });
+}
+
+bool PS5Controller::GetR2Button() const {
+  return GetRawButton(Button::kR2);
+}
+
+bool PS5Controller::GetR2ButtonPressed() {
+  return GetRawButtonPressed(Button::kR2);
+}
+
+bool PS5Controller::GetR2ButtonReleased() {
+  return GetRawButtonReleased(Button::kR2);
+}
+
+BooleanEvent PS5Controller::R2(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetR2Button(); });
+}
+
+bool PS5Controller::GetCreateButton() const {
+  return GetRawButton(Button::kCreate);
+}
+
+bool PS5Controller::GetCreateButtonPressed() {
+  return GetRawButtonPressed(Button::kCreate);
+}
+
+bool PS5Controller::GetCreateButtonReleased() {
+  return GetRawButtonReleased(Button::kCreate);
+}
+
+BooleanEvent PS5Controller::Create(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetCreateButton(); });
+}
+
+bool PS5Controller::GetOptionsButton() const {
+  return GetRawButton(Button::kOptions);
+}
+
+bool PS5Controller::GetOptionsButtonPressed() {
+  return GetRawButtonPressed(Button::kOptions);
+}
+
+bool PS5Controller::GetOptionsButtonReleased() {
+  return GetRawButtonReleased(Button::kOptions);
+}
+
+BooleanEvent PS5Controller::Options(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetOptionsButton(); });
+}
+
+bool PS5Controller::GetL3Button() const {
+  return GetRawButton(Button::kL3);
+}
+
+bool PS5Controller::GetL3ButtonPressed() {
+  return GetRawButtonPressed(Button::kL3);
+}
+
+bool PS5Controller::GetL3ButtonReleased() {
+  return GetRawButtonReleased(Button::kL3);
+}
+
+BooleanEvent PS5Controller::L3(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetL3Button(); });
+}
+
+bool PS5Controller::GetR3Button() const {
+  return GetRawButton(Button::kR3);
+}
+
+bool PS5Controller::GetR3ButtonPressed() {
+  return GetRawButtonPressed(Button::kR3);
+}
+
+bool PS5Controller::GetR3ButtonReleased() {
+  return GetRawButtonReleased(Button::kR3);
+}
+
+BooleanEvent PS5Controller::R3(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetR3Button(); });
+}
+
+bool PS5Controller::GetPSButton() const {
+  return GetRawButton(Button::kPS);
+}
+
+bool PS5Controller::GetPSButtonPressed() {
+  return GetRawButtonPressed(Button::kPS);
+}
+
+bool PS5Controller::GetPSButtonReleased() {
+  return GetRawButtonReleased(Button::kPS);
+}
+
+BooleanEvent PS5Controller::PS(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetPSButton(); });
+}
+
+bool PS5Controller::GetTouchpad() const {
+  return GetRawButton(Button::kTouchpad);
+}
+
+bool PS5Controller::GetTouchpadPressed() {
+  return GetRawButtonPressed(Button::kTouchpad);
+}
+
+bool PS5Controller::GetTouchpadReleased() {
+  return GetRawButtonReleased(Button::kTouchpad);
+}
+
+BooleanEvent PS5Controller::Touchpad(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetTouchpad(); });
+}
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/PWM.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/PWM.cpp
index 5c5e6f5..6a4c0f5 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/PWM.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/PWM.cpp
@@ -54,18 +54,18 @@
   FRC_ReportError(status, "Channel {}", m_channel);
 }
 
-void PWM::SetRaw(uint16_t value) {
+void PWM::SetPulseTime(units::microsecond_t time) {
   int32_t status = 0;
-  HAL_SetPWMRaw(m_handle, value, &status);
+  HAL_SetPWMPulseTimeMicroseconds(m_handle, time.value(), &status);
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
-uint16_t PWM::GetRaw() const {
+units::microsecond_t PWM::GetPulseTime() const {
   int32_t status = 0;
-  uint16_t value = HAL_GetPWMRaw(m_handle, &status);
+  double value = HAL_GetPWMPulseTimeMicroseconds(m_handle, &status);
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 
-  return value;
+  return units::microsecond_t{value};
 }
 
 void PWM::SetPosition(double pos) {
@@ -135,27 +135,37 @@
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
-void PWM::SetBounds(double max, double deadbandMax, double center,
-                    double deadbandMin, double min) {
+void PWM::SetBounds(units::microsecond_t max, units::microsecond_t deadbandMax,
+                    units::microsecond_t center,
+                    units::microsecond_t deadbandMin,
+                    units::microsecond_t min) {
   int32_t status = 0;
-  HAL_SetPWMConfig(m_handle, max, deadbandMax, center, deadbandMin, min,
-                   &status);
+  HAL_SetPWMConfigMicroseconds(m_handle, max.value(), deadbandMax.value(),
+                               center.value(), deadbandMin.value(), min.value(),
+                               &status);
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
-void PWM::SetRawBounds(int max, int deadbandMax, int center, int deadbandMin,
-                       int min) {
+void PWM::GetBounds(units::microsecond_t* max,
+                    units::microsecond_t* deadbandMax,
+                    units::microsecond_t* center,
+                    units::microsecond_t* deadbandMin,
+                    units::microsecond_t* min) {
   int32_t status = 0;
-  HAL_SetPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min,
-                      &status);
+  int32_t rawMax, rawDeadbandMax, rawCenter, rawDeadbandMin, rawMin;
+  HAL_GetPWMConfigMicroseconds(m_handle, &rawMax, &rawDeadbandMax, &rawCenter,
+                               &rawDeadbandMin, &rawMin, &status);
+  *max = units::microsecond_t{static_cast<double>(rawMax)};
+  *deadbandMax = units::microsecond_t{static_cast<double>(rawDeadbandMax)};
+  *center = units::microsecond_t{static_cast<double>(rawCenter)};
+  *deadbandMin = units::microsecond_t{static_cast<double>(rawDeadbandMin)};
+  *min = units::microsecond_t{static_cast<double>(rawMin)};
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
-void PWM::GetRawBounds(int* max, int* deadbandMax, int* center,
-                       int* deadbandMin, int* min) {
+void PWM::SetAlwaysHighMode() {
   int32_t status = 0;
-  HAL_GetPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min,
-                      &status);
+  HAL_SetPWMAlwaysHighMode(m_handle, &status);
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
@@ -168,6 +178,12 @@
   builder.SetActuator(true);
   builder.SetSafeState([=, this] { SetDisabled(); });
   builder.AddDoubleProperty(
-      "Value", [=, this] { return GetRaw(); },
-      [=, this](double value) { SetRaw(value); });
+      "Value", [=, this] { return GetPulseTime().value(); },
+      [=, this](double value) { SetPulseTime(units::millisecond_t{value}); });
+  builder.AddDoubleProperty(
+      "Speed", [=, this] { return GetSpeed(); },
+      [=, this](double value) { SetSpeed(value); });
+  builder.AddDoubleProperty(
+      "Position", [=, this] { return GetPosition(); },
+      [=, this](double value) { SetPosition(value); });
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/PneumaticHub.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/PneumaticHub.cpp
index ec008cf..bcbbd55 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/PneumaticHub.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/PneumaticHub.cpp
@@ -17,7 +17,6 @@
 #include "frc/RobotBase.h"
 #include "frc/SensorUtil.h"
 #include "frc/Solenoid.h"
-#include "frc/fmt/Units.h"
 
 using namespace frc;
 
@@ -161,9 +160,14 @@
                         "maxPressure must be between 0 and 120 PSI, got {}",
                         maxPressure);
   }
-  int32_t status = 0;
+
+  // Send the voltage as it would be if the 5V rail was at exactly 5V.
+  // The firmware will compensate for the real 5V rail voltage, which
+  // can fluctuate somewhat over time.
   units::volt_t minAnalogVoltage = PSIToVolts(minPressure, 5_V);
   units::volt_t maxAnalogVoltage = PSIToVolts(maxPressure, 5_V);
+
+  int32_t status = 0;
   HAL_SetREVPHClosedLoopControlAnalog(m_handle, minAnalogVoltage.value(),
                                       maxAnalogVoltage.value(), &status);
   FRC_ReportError(status, "Module {}", m_module);
@@ -186,9 +190,14 @@
                         "maxPressure must be between 0 and 120 PSI, got {}",
                         maxPressure);
   }
-  int32_t status = 0;
+
+  // Send the voltage as it would be if the 5V rail was at exactly 5V.
+  // The firmware will compensate for the real 5V rail voltage, which
+  // can fluctuate somewhat over time.
   units::volt_t minAnalogVoltage = PSIToVolts(minPressure, 5_V);
   units::volt_t maxAnalogVoltage = PSIToVolts(maxPressure, 5_V);
+
+  int32_t status = 0;
   HAL_SetREVPHClosedLoopControlHybrid(m_handle, minAnalogVoltage.value(),
                                       maxAnalogVoltage.value(), &status);
   FRC_ReportError(status, "Module {}", m_module);
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/RobotController.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/RobotController.cpp
index da6e364..8b2b496 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/RobotController.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/RobotController.cpp
@@ -41,6 +41,10 @@
   return std::string(comments, len);
 }
 
+int32_t RobotController::GetTeamNumber() {
+  return HAL_GetTeamNumber();
+}
+
 uint64_t RobotController::GetFPGATime() {
   int32_t status = 0;
   uint64_t time = HAL_GetFPGATime(&status);
@@ -76,6 +80,20 @@
   return retVal;
 }
 
+bool RobotController::GetRSLState() {
+  int32_t status = 0;
+  bool retVal = HAL_GetRSLState(&status);
+  FRC_CheckErrorStatus(status, "GetRSLState");
+  return retVal;
+}
+
+bool RobotController::IsSystemTimeValid() {
+  int32_t status = 0;
+  bool retVal = HAL_GetSystemTimeValid(&status);
+  FRC_CheckErrorStatus(status, "IsSystemTimeValid");
+  return retVal;
+}
+
 double RobotController::GetInputVoltage() {
   int32_t status = 0;
   double retVal = HAL_GetVinVoltage(&status);
@@ -104,6 +122,12 @@
   return retVal;
 }
 
+void RobotController::SetEnabled3V3(bool enabled) {
+  int32_t status = 0;
+  HAL_SetUserRailEnabled3V3(enabled, &status);
+  FRC_CheckErrorStatus(status, "SetEnabled3V3");
+}
+
 bool RobotController::GetEnabled3V3() {
   int32_t status = 0;
   bool retVal = HAL_GetUserActive3V3(&status);
@@ -132,6 +156,12 @@
   return retVal;
 }
 
+void RobotController::SetEnabled5V(bool enabled) {
+  int32_t status = 0;
+  HAL_SetUserRailEnabled5V(enabled, &status);
+  FRC_CheckErrorStatus(status, "SetEnabled5V");
+}
+
 bool RobotController::GetEnabled5V() {
   int32_t status = 0;
   bool retVal = HAL_GetUserActive5V(&status);
@@ -160,6 +190,12 @@
   return retVal;
 }
 
+void RobotController::SetEnabled6V(bool enabled) {
+  int32_t status = 0;
+  HAL_SetUserRailEnabled6V(enabled, &status);
+  FRC_CheckErrorStatus(status, "SetEnabled6V");
+}
+
 bool RobotController::GetEnabled6V() {
   int32_t status = 0;
   bool retVal = HAL_GetUserActive6V(&status);
@@ -187,6 +223,13 @@
   FRC_CheckErrorStatus(status, "SetBrownoutVoltage");
 }
 
+units::celsius_t RobotController::GetCPUTemp() {
+  int32_t status = 0;
+  double retVal = HAL_GetCPUTemp(&status);
+  FRC_CheckErrorStatus(status, "GetCPUTemp");
+  return units::celsius_t{retVal};
+}
+
 CANStatus RobotController::GetCANStatus() {
   int32_t status = 0;
   float percentBusUtilization = 0;
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/SPI.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/SPI.cpp
index 29cd006..26e26b7 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/SPI.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/SPI.cpp
@@ -177,44 +177,6 @@
   HAL_SetSPISpeed(m_port, hz);
 }
 
-void SPI::SetMSBFirst() {
-  FRC_ReportError(1, "SetMSBFirst not supported by roboRIO {}",
-                  static_cast<int>(m_port));
-}
-
-void SPI::SetLSBFirst() {
-  FRC_ReportError(1, "SetLSBFirst not supported by roboRIO {}",
-                  static_cast<int>(m_port));
-}
-
-void SPI::SetSampleDataOnLeadingEdge() {
-  int mode = m_mode;
-  mode &= 2;
-  m_mode = static_cast<HAL_SPIMode>(mode);
-  HAL_SetSPIMode(m_port, m_mode);
-}
-
-void SPI::SetSampleDataOnTrailingEdge() {
-  int mode = m_mode;
-  mode |= 2;
-  m_mode = static_cast<HAL_SPIMode>(mode);
-  HAL_SetSPIMode(m_port, m_mode);
-}
-
-void SPI::SetClockActiveLow() {
-  int mode = m_mode;
-  mode |= 1;
-  m_mode = static_cast<HAL_SPIMode>(mode);
-  HAL_SetSPIMode(m_port, m_mode);
-}
-
-void SPI::SetClockActiveHigh() {
-  int mode = m_mode;
-  mode &= 1;
-  m_mode = static_cast<HAL_SPIMode>(mode);
-  HAL_SetSPIMode(m_port, m_mode);
-}
-
 void SPI::SetMode(Mode mode) {
   m_mode = static_cast<HAL_SPIMode>(mode & 0x3);
   HAL_SetSPIMode(m_port, m_mode);
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/Servo.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/Servo.cpp
index 4a292b9..79f7f54 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/Servo.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/Servo.cpp
@@ -13,12 +13,12 @@
 constexpr double Servo::kMaxServoAngle;
 constexpr double Servo::kMinServoAngle;
 
-constexpr double Servo::kDefaultMaxServoPWM;
-constexpr double Servo::kDefaultMinServoPWM;
+constexpr units::millisecond_t Servo::kDefaultMaxServoPWM;
+constexpr units::millisecond_t Servo::kDefaultMinServoPWM;
 
 Servo::Servo(int channel) : PWM(channel) {
   // Set minimum and maximum PWM values supported by the servo
-  SetBounds(kDefaultMaxServoPWM, 0.0, 0.0, 0.0, kDefaultMinServoPWM);
+  SetBounds(kDefaultMaxServoPWM, 0.0_ms, 0.0_ms, 0.0_ms, kDefaultMinServoPWM);
 
   // Assign defaults for period multiplier for the servo PWM control signal
   SetPeriodMultiplier(kPeriodMultiplier_4X);
@@ -32,7 +32,7 @@
 }
 
 void Servo::SetOffline() {
-  SetRaw(0);
+  SetDisabled();
 }
 
 double Servo::Get() const {
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/Timer.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/Timer.cpp
index 3863de4..dfad620 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/Timer.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/Timer.cpp
@@ -54,6 +54,14 @@
   }
 }
 
+void Timer::Restart() {
+  if (m_running) {
+    Stop();
+  }
+  Reset();
+  Start();
+}
+
 void Timer::Stop() {
   if (m_running) {
     m_accumulatedTime = Get();
@@ -82,5 +90,5 @@
 }
 
 units::second_t Timer::GetMatchTime() {
-  return units::second_t{frc::DriverStation::GetMatchTime()};
+  return frc::DriverStation::GetMatchTime();
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/TimesliceRobot.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/TimesliceRobot.cpp
index d212c10..b817aa9 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/TimesliceRobot.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/TimesliceRobot.cpp
@@ -5,7 +5,6 @@
 #include "frc/TimesliceRobot.h"
 
 #include "frc/Errors.h"
-#include "frc/fmt/Units.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/Ultrasonic.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/Ultrasonic.cpp
index a034fc3..ec4c9c3 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/Ultrasonic.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/Ultrasonic.cpp
@@ -85,10 +85,7 @@
 }
 
 void Ultrasonic::Ping() {
-  if (m_automaticEnabled) {
-    throw FRC_MakeError(err::IncompatibleMode,
-                        "cannot call Ping() in automatic mode");
-  }
+  SetAutomaticMode(false);  // turn off automatic round-robin if pinging
 
   // Reset the counter to zero (invalid data now)
   m_counter.Reset();
@@ -138,7 +135,7 @@
 units::meter_t Ultrasonic::GetRange() const {
   if (IsRangeValid()) {
     if (m_simRange) {
-      return units::meter_t{m_simRange.Get()};
+      return units::inch_t{m_simRange.Get()};
     }
     return m_counter.GetPeriod() * kSpeedOfSound / 2.0;
   } else {
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp
index 5b8ce63..fd9c831 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp
@@ -7,56 +7,61 @@
 using namespace frc;
 
 BooleanEvent::BooleanEvent(EventLoop* loop, std::function<bool()> condition)
-    : m_loop(loop), m_condition(std::move(condition)) {}
+    : m_loop(loop), m_condition(std::move(condition)) {
+  m_state = std::make_shared<bool>(m_condition());
+  m_loop->Bind(
+      // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+      [condition = m_condition, state = m_state] { *state = condition(); });
+}
 
 BooleanEvent::operator std::function<bool()>() {
-  return m_condition;
+  return [state = m_state] { return *state; };
 }
 
 bool BooleanEvent::GetAsBoolean() const {
-  return m_condition();
+  return *m_state;
 }
 
 void BooleanEvent::IfHigh(std::function<void()> action) {
-  m_loop->Bind([condition = m_condition, action = std::move(action)] {
-    if (condition()) {
+  m_loop->Bind([state = m_state, action = std::move(action)] {
+    if (*state) {
       action();
     }
   });
 }
 
 BooleanEvent BooleanEvent::operator!() {
-  return BooleanEvent(this->m_loop, [lhs = m_condition] { return !lhs(); });
+  return BooleanEvent(this->m_loop, [state = m_state] { return !*state; });
 }
 
 BooleanEvent BooleanEvent::operator&&(std::function<bool()> rhs) {
   return BooleanEvent(this->m_loop,
-                      [lhs = m_condition, rhs] { return lhs() && rhs(); });
+                      [state = m_state, rhs] { return *state && rhs(); });
 }
 
 BooleanEvent BooleanEvent::operator||(std::function<bool()> rhs) {
   return BooleanEvent(this->m_loop,
-                      [lhs = m_condition, rhs] { return lhs() || rhs(); });
+                      [state = m_state, rhs] { return *state || rhs(); });
 }
 
 BooleanEvent BooleanEvent::Rising() {
-  return BooleanEvent(
-      this->m_loop, [lhs = m_condition, m_previous = m_condition()]() mutable {
-        bool present = lhs();
-        bool past = m_previous;
-        m_previous = present;
-        return !past && present;
-      });
+  return BooleanEvent(this->m_loop,
+                      [state = m_state, m_previous = *m_state]() mutable {
+                        bool present = *state;
+                        bool past = m_previous;
+                        m_previous = present;
+                        return !past && present;
+                      });
 }
 
 BooleanEvent BooleanEvent::Falling() {
-  return BooleanEvent(
-      this->m_loop, [lhs = m_condition, m_previous = m_condition()]() mutable {
-        bool present = lhs();
-        bool past = m_previous;
-        m_previous = present;
-        return past && !present;
-      });
+  return BooleanEvent(this->m_loop,
+                      [state = m_state, m_previous = *m_state]() mutable {
+                        bool present = *state;
+                        bool past = m_previous;
+                        m_previous = present;
+                        return past && !present;
+                      });
 }
 
 BooleanEvent BooleanEvent::Debounce(units::second_t debounceTime,
@@ -64,5 +69,5 @@
   return BooleanEvent(
       this->m_loop,
       [debouncer = frc::Debouncer(debounceTime, type),
-       lhs = m_condition]() mutable { return debouncer.Calculate(lhs()); });
+       state = m_state]() mutable { return debouncer.Calculate(*state); });
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp
index 81fa868..ec13698 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 DMC60::DMC60(int channel) : PWMMotorController("DMC60", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp
index c68ae0c..a152566 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 Jaguar::Jaguar(int channel) : PWMMotorController("Jaguar", channel) {
-  m_pwm.SetBounds(2.31, 1.55, 1.507, 1.454, 0.697);
+  m_pwm.SetBounds(2.31_ms, 1.55_ms, 1.507_ms, 1.454_ms, 0.697_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp
index 01c70d0..f25aa91 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp
@@ -30,7 +30,7 @@
   if (!m_disabled) {
     m_speed = speed;
     m_dio.UpdateDutyCycle(0.5 + 0.5 * (m_isInverted ? -speed : speed));
-    m_pwm.SetRaw(0xffff);
+    m_pwm.SetAlwaysHighMode();
   }
   Feed();
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp
index 608d452..56c3a45 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp
@@ -10,7 +10,7 @@
 
 PWMSparkMax::PWMSparkMax(int channel)
     : PWMMotorController("PWMSparkMax", channel) {
-  m_pwm.SetBounds(2.003, 1.55, 1.50, 1.46, 0.999);
+  m_pwm.SetBounds(2.003_ms, 1.55_ms, 1.50_ms, 1.46_ms, 0.999_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp
index 2c6982b..51327b0 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp
@@ -10,7 +10,7 @@
 
 PWMTalonFX::PWMTalonFX(int channel)
     : PWMMotorController("PWMTalonFX", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp
index b253412..ee579ed 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp
@@ -10,7 +10,7 @@
 
 PWMTalonSRX::PWMTalonSRX(int channel)
     : PWMMotorController("PWMTalonSRX", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp
index e558028..24041c8 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 PWMVenom::PWMVenom(int channel) : PWMMotorController("PWMVenom", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp
index 10ce992..4aeb399 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp
@@ -10,7 +10,7 @@
 
 PWMVictorSPX::PWMVictorSPX(int channel)
     : PWMMotorController("PWMVictorSPX", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp
index 3d5738f..7b6b838 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 SD540::SD540(int channel) : PWMMotorController("SD540", channel) {
-  m_pwm.SetBounds(2.05, 1.55, 1.50, 1.44, 0.94);
+  m_pwm.SetBounds(2.05_ms, 1.55_ms, 1.50_ms, 1.44_ms, 0.94_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp
index 45394df..05ecb89 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 Spark::Spark(int channel) : PWMMotorController("Spark", channel) {
-  m_pwm.SetBounds(2.003, 1.55, 1.50, 1.46, 0.999);
+  m_pwm.SetBounds(2.003_ms, 1.55_ms, 1.50_ms, 1.46_ms, 0.999_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp
index f4b3b69..4994baf 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 Talon::Talon(int channel) : PWMMotorController("Talon", channel) {
-  m_pwm.SetBounds(2.037, 1.539, 1.513, 1.487, 0.989);
+  m_pwm.SetBounds(2.037_ms, 1.539_ms, 1.513_ms, 1.487_ms, 0.989_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp
index 3ad29f7..a05900f 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 Victor::Victor(int channel) : PWMMotorController("Victor", channel) {
-  m_pwm.SetBounds(2.027, 1.525, 1.507, 1.49, 1.026);
+  m_pwm.SetBounds(2.027_ms, 1.525_ms, 1.507_ms, 1.49_ms, 1.026_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_2X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp
index 6dc888e..27419fb 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 VictorSP::VictorSP(int channel) : PWMMotorController("VictorSP", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp
index a315b90..a19cc6a 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp
@@ -20,12 +20,16 @@
   bool tabsChanged = false;
   std::shared_ptr<nt::NetworkTable> rootTable;
   std::shared_ptr<nt::NetworkTable> rootMetaTable;
+  nt::StringPublisher selectedTabPub;
 };
 
 ShuffleboardInstance::ShuffleboardInstance(nt::NetworkTableInstance ntInstance)
     : m_impl(new Impl) {
   m_impl->rootTable = ntInstance.GetTable(Shuffleboard::kBaseTableName);
   m_impl->rootMetaTable = m_impl->rootTable->GetSubTable(".metadata");
+  m_impl->selectedTabPub =
+      m_impl->rootMetaTable->GetStringTopic("Selected")
+          .Publish(nt::PubSubOptions{.keepDuplicates = true});
   HAL_Report(HALUsageReporting::kResourceType_Shuffleboard, 0);
 }
 
@@ -75,9 +79,9 @@
 }
 
 void ShuffleboardInstance::SelectTab(int index) {
-  m_impl->rootMetaTable->GetEntry("Selected").SetDouble(index);
+  m_impl->selectedTabPub.Set(std::to_string(index));
 }
 
 void ShuffleboardInstance::SelectTab(std::string_view title) {
-  m_impl->rootMetaTable->GetEntry("Selected").SetString(title);
+  m_impl->selectedTabPub.Set(title);
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DCMotorSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DCMotorSim.cpp
index bda2020..c0448ea 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DCMotorSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DCMotorSim.cpp
@@ -24,6 +24,11 @@
     : DCMotorSim(LinearSystemId::DCMotorSystem(gearbox, moi, gearing), gearbox,
                  gearing, measurementStdDevs) {}
 
+void DCMotorSim::SetState(units::radian_t angularPosition,
+                          units::radians_per_second_t angularVelocity) {
+  SetState(Vectord<2>{angularPosition, angularVelocity});
+}
+
 units::radian_t DCMotorSim::GetAngularPosition() const {
   return units::radian_t{GetOutput(0)};
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp
index b6c95dc..8c27cdf 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp
@@ -41,7 +41,8 @@
               driveMotor, mass, wheelRadius, trackWidth / 2.0, J, gearing),
           trackWidth, driveMotor, gearing, wheelRadius, measurementStdDevs) {}
 
-Vectord<2> DifferentialDrivetrainSim::ClampInput(const Vectord<2>& u) {
+Eigen::Vector2d DifferentialDrivetrainSim::ClampInput(
+    const Eigen::Vector2d& u) {
   return frc::DesaturateInputVector<2>(u,
                                        frc::RobotController::GetInputVoltage());
 }
@@ -128,7 +129,7 @@
 }
 
 Vectord<7> DifferentialDrivetrainSim::Dynamics(const Vectord<7>& x,
-                                               const Vectord<2>& u) {
+                                               const Eigen::Vector2d& u) {
   // Because G² can be factored out of A, we can divide by the old ratio
   // squared and multiply by the new ratio squared to get a new drivetrain
   // model.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp
index cb83ccb..3df775d 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp
@@ -9,17 +9,49 @@
 
 using namespace frc::sim;
 
-DutyCycleEncoderSim::DutyCycleEncoderSim(const frc::DutyCycleEncoder& encoder) {
-  frc::sim::SimDeviceSim deviceSim{"DutyCycle:DutyCycleEncoder",
-                                   encoder.GetSourceChannel()};
+DutyCycleEncoderSim::DutyCycleEncoderSim(const frc::DutyCycleEncoder& encoder)
+    : DutyCycleEncoderSim{encoder.GetSourceChannel()} {}
+
+DutyCycleEncoderSim::DutyCycleEncoderSim(int channel) {
+  frc::sim::SimDeviceSim deviceSim{"DutyCycle:DutyCycleEncoder", channel};
   m_simPosition = deviceSim.GetDouble("position");
   m_simDistancePerRotation = deviceSim.GetDouble("distance_per_rot");
+  m_simAbsolutePosition = deviceSim.GetDouble("absPosition");
+  m_simIsConnected = deviceSim.GetBoolean("connected");
+}
+
+double DutyCycleEncoderSim::Get() {
+  return m_simPosition.Get();
 }
 
 void DutyCycleEncoderSim::Set(units::turn_t turns) {
   m_simPosition.Set(turns.value());
 }
 
+double DutyCycleEncoderSim::GetDistance() {
+  return m_simPosition.Get() * m_simDistancePerRotation.Get();
+}
+
 void DutyCycleEncoderSim::SetDistance(double distance) {
   m_simPosition.Set(distance / m_simDistancePerRotation.Get());
 }
+
+double DutyCycleEncoderSim::GetAbsolutePosition() {
+  return m_simAbsolutePosition.Get();
+}
+
+void DutyCycleEncoderSim::SetAbsolutePosition(double position) {
+  m_simAbsolutePosition.Set(position);
+}
+
+double DutyCycleEncoderSim::GetDistancePerRotation() {
+  return m_simDistancePerRotation.Get();
+}
+
+bool DutyCycleEncoderSim::IsConnected() {
+  return m_simIsConnected.Get();
+}
+
+void DutyCycleEncoderSim::SetConnected(bool isConnected) {
+  m_simIsConnected.Set(isConnected);
+}
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp
index 529fb1a..0281730 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp
@@ -13,39 +13,54 @@
 using namespace frc::sim;
 
 ElevatorSim::ElevatorSim(const LinearSystem<2, 1, 1>& plant,
-                         const DCMotor& gearbox, double gearing,
-                         units::meter_t drumRadius, units::meter_t minHeight,
+                         const DCMotor& gearbox, units::meter_t minHeight,
                          units::meter_t maxHeight, bool simulateGravity,
+                         units::meter_t startingHeight,
                          const std::array<double, 1>& measurementStdDevs)
     : LinearSystemSim(plant, measurementStdDevs),
       m_gearbox(gearbox),
-      m_drumRadius(drumRadius),
       m_minHeight(minHeight),
       m_maxHeight(maxHeight),
-      m_gearing(gearing),
-      m_simulateGravity(simulateGravity) {}
+      m_simulateGravity(simulateGravity) {
+  SetState(startingHeight, 0_mps);
+}
 
 ElevatorSim::ElevatorSim(const DCMotor& gearbox, double gearing,
                          units::kilogram_t carriageMass,
                          units::meter_t drumRadius, units::meter_t minHeight,
                          units::meter_t maxHeight, bool simulateGravity,
+                         units::meter_t startingHeight,
                          const std::array<double, 1>& measurementStdDevs)
-    : LinearSystemSim(LinearSystemId::ElevatorSystem(gearbox, carriageMass,
-                                                     drumRadius, gearing),
-                      measurementStdDevs),
-      m_gearbox(gearbox),
-      m_drumRadius(drumRadius),
-      m_minHeight(minHeight),
-      m_maxHeight(maxHeight),
-      m_gearing(gearing),
-      m_simulateGravity(simulateGravity) {}
+    : ElevatorSim(LinearSystemId::ElevatorSystem(gearbox, carriageMass,
+                                                 drumRadius, gearing),
+                  gearbox, minHeight, maxHeight, simulateGravity,
+                  startingHeight, measurementStdDevs) {}
+
+template <typename Distance>
+  requires std::same_as<units::meter, Distance> ||
+           std::same_as<units::radian, Distance>
+ElevatorSim::ElevatorSim(decltype(1_V / Velocity_t<Distance>(1)) kV,
+                         decltype(1_V / Acceleration_t<Distance>(1)) kA,
+                         const DCMotor& gearbox, units::meter_t minHeight,
+                         units::meter_t maxHeight, bool simulateGravity,
+                         units::meter_t startingHeight,
+                         const std::array<double, 1>& measurementStdDevs)
+    : ElevatorSim(LinearSystemId::IdentifyPositionSystem(kV, kA), gearbox,
+                  minHeight, maxHeight, simulateGravity, startingHeight,
+                  measurementStdDevs) {}
+
+void ElevatorSim::SetState(units::meter_t position,
+                           units::meters_per_second_t velocity) {
+  SetState(
+      Vectord<2>{std::clamp(position, m_minHeight, m_maxHeight), velocity});
+}
 
 bool ElevatorSim::WouldHitLowerLimit(units::meter_t elevatorHeight) const {
-  return elevatorHeight < m_minHeight;
+  return elevatorHeight <= m_minHeight;
 }
 
 bool ElevatorSim::WouldHitUpperLimit(units::meter_t elevatorHeight) const {
-  return elevatorHeight > m_maxHeight;
+  return elevatorHeight >= m_maxHeight;
 }
 
 bool ElevatorSim::HasHitLowerLimit() const {
@@ -69,10 +84,12 @@
   // Reductions are greater than 1, so a reduction of 10:1 would mean the motor
   // is spinning 10x faster than the output.
 
-  // v = r w, so w = v / r
+  double kA = 1.0 / m_plant.B(1, 0);
+  using Kv_t = units::unit_t<units::compound_unit<
+      units::volt, units::inverse<units::meters_per_second>>>;
+  Kv_t Kv = Kv_t{kA * m_plant.A(1, 1)};
   units::meters_per_second_t velocity{m_x(1)};
-  units::radians_per_second_t motorVelocity =
-      velocity / m_drumRadius * m_gearing * 1_rad;
+  units::radians_per_second_t motorVelocity = velocity * Kv * m_gearbox.Kv;
 
   // Perform calculation and return.
   return m_gearbox.Current(motorVelocity, units::volt_t{m_u(0)}) *
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp
index 146328d..9709b4c 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp
@@ -191,7 +191,7 @@
   HALSIM_SetEncoderDistance(m_index, distance);
 }
 
-double EncoderSim::GetDistance() {
+double EncoderSim::GetDistance() const {
   return HALSIM_GetEncoderDistance(m_index);
 }
 
@@ -199,6 +199,6 @@
   HALSIM_SetEncoderRate(m_index, rate);
 }
 
-double EncoderSim::GetRate() {
+double EncoderSim::GetRate() const {
   return HALSIM_GetEncoderRate(m_index);
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp
index 95dcb9e..f4b9a81 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp
@@ -24,6 +24,10 @@
     : FlywheelSim(LinearSystemId::FlywheelSystem(gearbox, moi, gearing),
                   gearbox, gearing, measurementStdDevs) {}
 
+void FlywheelSim::SetState(units::radians_per_second_t velocity) {
+  LinearSystemSim::SetState(Vectord<1>{velocity.value()});
+}
+
 units::radians_per_second_t FlywheelSim::GetAngularVelocity() const {
   return units::radians_per_second_t{GetOutput(0)};
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/PS5ControllerSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/PS5ControllerSim.cpp
new file mode 100644
index 0000000..e20105c
--- /dev/null
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/PS5ControllerSim.cpp
@@ -0,0 +1,103 @@
+// 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 "frc/simulation/PS5ControllerSim.h"
+
+#include "frc/PS5Controller.h"
+
+using namespace frc;
+using namespace frc::sim;
+
+PS5ControllerSim::PS5ControllerSim(const PS5Controller& joystick)
+    : GenericHIDSim{joystick} {
+  SetAxisCount(6);
+  SetButtonCount(14);
+  SetPOVCount(1);
+}
+
+PS5ControllerSim::PS5ControllerSim(int port) : GenericHIDSim{port} {
+  SetAxisCount(6);
+  SetButtonCount(14);
+  SetPOVCount(1);
+}
+
+void PS5ControllerSim::SetLeftX(double value) {
+  SetRawAxis(PS5Controller::Axis::kLeftX, value);
+}
+
+void PS5ControllerSim::SetRightX(double value) {
+  SetRawAxis(PS5Controller::Axis::kRightX, value);
+}
+
+void PS5ControllerSim::SetLeftY(double value) {
+  SetRawAxis(PS5Controller::Axis::kLeftY, value);
+}
+
+void PS5ControllerSim::SetRightY(double value) {
+  SetRawAxis(PS5Controller::Axis::kRightY, value);
+}
+
+void PS5ControllerSim::SetL2Axis(double value) {
+  SetRawAxis(PS5Controller::Axis::kL2, value);
+}
+
+void PS5ControllerSim::SetR2Axis(double value) {
+  SetRawAxis(PS5Controller::Axis::kR2, value);
+}
+
+void PS5ControllerSim::SetSquareButton(bool value) {
+  SetRawButton(PS5Controller::Button::kSquare, value);
+}
+
+void PS5ControllerSim::SetCrossButton(bool value) {
+  SetRawButton(PS5Controller::Button::kCross, value);
+}
+
+void PS5ControllerSim::SetCircleButton(bool value) {
+  SetRawButton(PS5Controller::Button::kCircle, value);
+}
+
+void PS5ControllerSim::SetTriangleButton(bool value) {
+  SetRawButton(PS5Controller::Button::kTriangle, value);
+}
+
+void PS5ControllerSim::SetL1Button(bool value) {
+  SetRawButton(PS5Controller::Button::kL1, value);
+}
+
+void PS5ControllerSim::SetR1Button(bool value) {
+  SetRawButton(PS5Controller::Button::kR1, value);
+}
+
+void PS5ControllerSim::SetL2Button(bool value) {
+  SetRawButton(PS5Controller::Button::kL2, value);
+}
+
+void PS5ControllerSim::SetR2Button(bool value) {
+  SetRawButton(PS5Controller::Button::kR2, value);
+}
+
+void PS5ControllerSim::SetCreateButton(bool value) {
+  SetRawButton(PS5Controller::Button::kCreate, value);
+}
+
+void PS5ControllerSim::SetOptionsButton(bool value) {
+  SetRawButton(PS5Controller::Button::kOptions, value);
+}
+
+void PS5ControllerSim::SetL3Button(bool value) {
+  SetRawButton(PS5Controller::Button::kL3, value);
+}
+
+void PS5ControllerSim::SetR3Button(bool value) {
+  SetRawButton(PS5Controller::Button::kR3, value);
+}
+
+void PS5ControllerSim::SetPSButton(bool value) {
+  SetRawButton(PS5Controller::Button::kPS, value);
+}
+
+void PS5ControllerSim::SetTouchpad(bool value) {
+  SetRawButton(PS5Controller::Button::kTouchpad, value);
+}
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp
index 3fafa8e..9332c78 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp
@@ -39,21 +39,21 @@
   HALSIM_SetPWMInitialized(m_index, initialized);
 }
 
-std::unique_ptr<CallbackStore> PWMSim::RegisterRawValueCallback(
+std::unique_ptr<CallbackStore> PWMSim::RegisterPulseMicrosecondCallback(
     NotifyCallback callback, bool initialNotify) {
   auto store = std::make_unique<CallbackStore>(
-      m_index, -1, callback, &HALSIM_CancelPWMRawValueCallback);
-  store->SetUid(HALSIM_RegisterPWMRawValueCallback(m_index, &CallbackStoreThunk,
-                                                   store.get(), initialNotify));
+      m_index, -1, callback, &HALSIM_CancelPWMPulseMicrosecondCallback);
+  store->SetUid(HALSIM_RegisterPWMPulseMicrosecondCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
   return store;
 }
 
-int PWMSim::GetRawValue() const {
-  return HALSIM_GetPWMRawValue(m_index);
+int32_t PWMSim::GetPulseMicrosecond() const {
+  return HALSIM_GetPWMPulseMicrosecond(m_index);
 }
 
-void PWMSim::SetRawValue(int rawValue) {
-  HALSIM_SetPWMRawValue(m_index, rawValue);
+void PWMSim::SetPulseMicrosecond(int32_t microsecondPulseTime) {
+  HALSIM_SetPWMPulseMicrosecond(m_index, microsecondPulseTime);
 }
 
 std::unique_ptr<CallbackStore> PWMSim::RegisterSpeedCallback(
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp
index 6d0f809..90d9651 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp
@@ -284,6 +284,40 @@
   HALSIM_SetRoboRioBrownoutVoltage(vInVoltage.value());
 }
 
+std::unique_ptr<CallbackStore> RoboRioSim::RegisterCPUTempCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      -1, callback, &HALSIM_CancelRoboRioCPUTempCallback);
+  store->SetUid(HALSIM_RegisterRoboRioCPUTempCallback(
+      &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+units::celsius_t RoboRioSim::GetCPUTemp() {
+  return units::celsius_t{HALSIM_GetRoboRioCPUTemp()};
+}
+
+void RoboRioSim::SetCPUTemp(units::celsius_t cpuTemp) {
+  HALSIM_SetRoboRioCPUTemp(cpuTemp.value());
+}
+
+std::unique_ptr<CallbackStore> RoboRioSim::RegisterTeamNumberCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      -1, callback, &HALSIM_CancelRoboRioTeamNumberCallback);
+  store->SetUid(HALSIM_RegisterRoboRioTeamNumberCallback(
+      &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+int32_t RoboRioSim::GetTeamNumber() {
+  return HALSIM_GetRoboRioTeamNumber();
+}
+
+void RoboRioSim::SetTeamNumber(int32_t teamNumber) {
+  HALSIM_SetRoboRioTeamNumber(teamNumber);
+}
+
 std::string RoboRioSim::GetSerialNumber() {
   char serialNum[9];
   size_t len = HALSIM_GetRoboRioSerialNumber(serialNum, sizeof(serialNum));
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp
index 34fd1e3..70e43e7 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp
@@ -27,6 +27,12 @@
       fmt::format("{}[{},{}]", name, index, channel).c_str());
 }
 
+SimDeviceSim::SimDeviceSim(HAL_SimDeviceHandle handle) : m_handle(handle) {}
+
+std::string SimDeviceSim::GetName() const {
+  return std::string(HALSIM_GetSimDeviceName(m_handle));
+}
+
 hal::SimValue SimDeviceSim::GetValue(const char* name) const {
   return HALSIM_GetSimValueHandle(m_handle, name);
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp
index 88e45c6..d9969ca 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp
@@ -18,26 +18,34 @@
 SingleJointedArmSim::SingleJointedArmSim(
     const LinearSystem<2, 1, 1>& system, const DCMotor& gearbox, double gearing,
     units::meter_t armLength, units::radian_t minAngle,
-    units::radian_t maxAngle, units::kilogram_t armMass, bool simulateGravity,
+    units::radian_t maxAngle, bool simulateGravity,
+    units::radian_t startingAngle,
     const std::array<double, 1>& measurementStdDevs)
     : LinearSystemSim<2, 1, 1>(system, measurementStdDevs),
-      m_r(armLength),
+      m_armLen(armLength),
       m_minAngle(minAngle),
       m_maxAngle(maxAngle),
-      m_armMass(armMass),
       m_gearbox(gearbox),
       m_gearing(gearing),
-      m_simulateGravity(simulateGravity) {}
+      m_simulateGravity(simulateGravity) {
+  SetState(startingAngle, 0_rad_per_s);
+}
 
 SingleJointedArmSim::SingleJointedArmSim(
     const DCMotor& gearbox, double gearing, units::kilogram_square_meter_t moi,
     units::meter_t armLength, units::radian_t minAngle,
-    units::radian_t maxAngle, units::kilogram_t armMass, bool simulateGravity,
+    units::radian_t maxAngle, bool simulateGravity,
+    units::radian_t startingAngle,
     const std::array<double, 1>& measurementStdDevs)
     : SingleJointedArmSim(
           LinearSystemId::SingleJointedArmSystem(gearbox, moi, gearing),
-          gearbox, gearing, armLength, minAngle, maxAngle, armMass,
-          simulateGravity, measurementStdDevs) {}
+          gearbox, gearing, armLength, minAngle, maxAngle, simulateGravity,
+          startingAngle, measurementStdDevs) {}
+
+void SingleJointedArmSim::SetState(units::radian_t angle,
+                                   units::radians_per_second_t velocity) {
+  SetState(Vectord<2>{std::clamp(angle, m_minAngle, m_maxAngle), velocity});
+}
 
 bool SingleJointedArmSim::WouldHitLowerLimit(units::radian_t armAngle) const {
   return armAngle <= m_minAngle;
@@ -78,24 +86,37 @@
 Vectord<2> SingleJointedArmSim::UpdateX(const Vectord<2>& currentXhat,
                                         const Vectord<1>& u,
                                         units::second_t dt) {
-  // Horizontal case:
-  // Torque = F * r = I * alpha
-  // alpha = F * r / I
-  // Since F = mg,
-  // alpha = m * g * r / I
-  // Finally, multiply RHS by cos(theta) to account for the arm angle
-  // This acceleration is added to the linear system dynamics x-dot = Ax + Bu
-  // We therefore find that f(x, u) = Ax + Bu + [[0] [m * g * r / I *
-  // std::cos(theta)]]
+  // The torque on the arm is given by τ = F⋅r, where F is the force applied by
+  // gravity and r the distance from pivot to center of mass. Recall from
+  // dynamics that the sum of torques for a rigid body is τ = J⋅α, were τ is
+  // torque on the arm, J is the mass-moment of inertia about the pivot axis,
+  // and α is the angular acceleration in rad/s². Rearranging yields: α = F⋅r/J
+  //
+  // We substitute in F = m⋅g⋅cos(θ), where θ is the angle from horizontal:
+  //
+  //   α = (m⋅g⋅cos(θ))⋅r/J
+  //
+  // Multiply RHS by cos(θ) to account for the arm angle. Further, we know the
+  // arm mass-moment of inertia J of our arm is given by J=1/3 mL², modeled as a
+  // rod rotating about it's end, where L is the overall rod length. The mass
+  // distribution is assumed to be uniform. Substitute r=L/2 to find:
+  //
+  //   α = (m⋅g⋅cos(θ))⋅r/(1/3 mL²)
+  //   α = (m⋅g⋅cos(θ))⋅(L/2)/(1/3 mL²)
+  //   α = 3/2⋅g⋅cos(θ)/L
+  //
+  // This acceleration is next added to the linear system dynamics ẋ=Ax+Bu
+  //
+  //   f(x, u) = Ax + Bu + [0  α]ᵀ
+  //   f(x, u) = Ax + Bu + [0  3/2⋅g⋅cos(θ)/L]ᵀ
 
   Vectord<2> updatedXhat = RKDP(
       [&](const auto& x, const auto& u) -> Vectord<2> {
         Vectord<2> xdot = m_plant.A() * x + m_plant.B() * u;
 
         if (m_simulateGravity) {
-          xdot += Vectord<2>{0.0, (m_armMass * m_r * -9.8 * 3.0 /
-                                   (m_armMass * m_r * m_r) * std::cos(x(0)))
-                                      .value()};
+          xdot += Vectord<2>{
+              0.0, (3.0 / 2.0 * -9.8 / m_armLen * std::cos(x(0))).value()};
         }
         return xdot;
       },
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp
index d2d1687..30a5650 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp
@@ -9,8 +9,11 @@
 
 using namespace frc::sim;
 
-UltrasonicSim::UltrasonicSim(const frc::Ultrasonic& ultrasonic) {
-  frc::sim::SimDeviceSim deviceSim{"Ultrasonic", ultrasonic.GetEchoChannel()};
+UltrasonicSim::UltrasonicSim(const frc::Ultrasonic& ultrasonic)
+    : UltrasonicSim(0, ultrasonic.GetEchoChannel()) {}
+
+UltrasonicSim::UltrasonicSim(int ping, int echo) {
+  frc::sim::SimDeviceSim deviceSim{"Ultrasonic", echo};
   m_simRangeValid = deviceSim.GetBoolean("Range Valid");
   m_simRange = deviceSim.GetDouble("Range (in)");
 }
@@ -19,6 +22,6 @@
   m_simRangeValid.Set(isValid);
 }
 
-void UltrasonicSim::SetRange(units::meter_t range) {
+void UltrasonicSim::SetRange(units::inch_t range) {
   m_simRange.Set(range.value());
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp
index 355cbc6..1bee916 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp
@@ -4,7 +4,6 @@
 
 #include "frc/smartdashboard/Mechanism2d.h"
 
-#include <cstdio>
 #include <string_view>
 
 #include <networktables/NTSendableBuilder.h>
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp
index a43aa16..75ec44e 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp
@@ -4,7 +4,7 @@
 
 #include "frc/smartdashboard/MechanismLigament2d.h"
 
-#include <cstdio>
+#include <wpi/StringExtras.h>
 
 using namespace frc;
 
@@ -36,8 +36,10 @@
 
 void MechanismLigament2d::SetColor(const Color8Bit& color) {
   std::scoped_lock lock(m_mutex);
-  std::snprintf(m_color, sizeof(m_color), "#%02X%02X%02X", color.red,
-                color.green, color.blue);
+
+  wpi::format_to_n_c_str(m_color, sizeof(m_color), "#{:02X}{:02X}{:02X}",
+                         color.red, color.green, color.blue);
+
   if (m_colorEntry) {
     m_colorEntry.Set(m_color);
   }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp
index 79b0e6e..c063850 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp
@@ -141,6 +141,14 @@
   m_properties.emplace_back(std::move(prop));
 }
 
+template <typename Topic, typename Value>
+void SendableBuilderImpl::PublishConstImpl(Topic topic, Value value) {
+  auto prop = std::make_unique<PropertyImpl<Topic>>();
+  prop->pub = topic.Publish();
+  prop->pub.Set(value);
+  m_properties.emplace_back(std::move(prop));
+}
+
 void SendableBuilderImpl::AddBooleanProperty(std::string_view key,
                                              std::function<bool()> getter,
                                              std::function<void(bool)> setter) {
@@ -148,6 +156,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstBoolean(std::string_view key,
+                                              bool value) {
+  PublishConstImpl(m_table->GetBooleanTopic(key), value);
+}
+
 void SendableBuilderImpl::AddIntegerProperty(
     std::string_view key, std::function<int64_t()> getter,
     std::function<void(int64_t)> setter) {
@@ -155,6 +168,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstInteger(std::string_view key,
+                                              int64_t value) {
+  PublishConstImpl(m_table->GetIntegerTopic(key), value);
+}
+
 void SendableBuilderImpl::AddFloatProperty(std::string_view key,
                                            std::function<float()> getter,
                                            std::function<void(float)> setter) {
@@ -162,6 +180,10 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstFloat(std::string_view key, float value) {
+  PublishConstImpl(m_table->GetFloatTopic(key), value);
+}
+
 void SendableBuilderImpl::AddDoubleProperty(
     std::string_view key, std::function<double()> getter,
     std::function<void(double)> setter) {
@@ -169,6 +191,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstDouble(std::string_view key,
+                                             double value) {
+  PublishConstImpl(m_table->GetDoubleTopic(key), value);
+}
+
 void SendableBuilderImpl::AddStringProperty(
     std::string_view key, std::function<std::string()> getter,
     std::function<void(std::string_view)> setter) {
@@ -176,6 +203,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstString(std::string_view key,
+                                             std::string_view value) {
+  PublishConstImpl(m_table->GetStringTopic(key), value);
+}
+
 void SendableBuilderImpl::AddBooleanArrayProperty(
     std::string_view key, std::function<std::vector<int>()> getter,
     std::function<void(std::span<const int>)> setter) {
@@ -183,6 +215,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstBooleanArray(std::string_view key,
+                                                   std::span<const int> value) {
+  PublishConstImpl(m_table->GetBooleanArrayTopic(key), value);
+}
+
 void SendableBuilderImpl::AddIntegerArrayProperty(
     std::string_view key, std::function<std::vector<int64_t>()> getter,
     std::function<void(std::span<const int64_t>)> setter) {
@@ -190,6 +227,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstIntegerArray(
+    std::string_view key, std::span<const int64_t> value) {
+  PublishConstImpl(m_table->GetIntegerArrayTopic(key), value);
+}
+
 void SendableBuilderImpl::AddFloatArrayProperty(
     std::string_view key, std::function<std::vector<float>()> getter,
     std::function<void(std::span<const float>)> setter) {
@@ -197,6 +239,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstFloatArray(std::string_view key,
+                                                 std::span<const float> value) {
+  PublishConstImpl(m_table->GetFloatArrayTopic(key), value);
+}
+
 void SendableBuilderImpl::AddDoubleArrayProperty(
     std::string_view key, std::function<std::vector<double>()> getter,
     std::function<void(std::span<const double>)> setter) {
@@ -204,6 +251,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstDoubleArray(
+    std::string_view key, std::span<const double> value) {
+  PublishConstImpl(m_table->GetDoubleArrayTopic(key), value);
+}
+
 void SendableBuilderImpl::AddStringArrayProperty(
     std::string_view key, std::function<std::vector<std::string>()> getter,
     std::function<void(std::span<const std::string>)> setter) {
@@ -211,6 +263,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstStringArray(
+    std::string_view key, std::span<const std::string> value) {
+  PublishConstImpl(m_table->GetStringArrayTopic(key), value);
+}
+
 void SendableBuilderImpl::AddRawProperty(
     std::string_view key, std::string_view typeString,
     std::function<std::vector<uint8_t>()> getter,
@@ -235,6 +292,16 @@
   m_properties.emplace_back(std::move(prop));
 }
 
+void SendableBuilderImpl::PublishConstRaw(std::string_view key,
+                                          std::string_view typeString,
+                                          std::span<const uint8_t> value) {
+  auto topic = m_table->GetRawTopic(key);
+  auto prop = std::make_unique<PropertyImpl<nt::RawTopic>>();
+  prop->pub = topic.Publish(typeString);
+  prop->pub.Set(value);
+  m_properties.emplace_back(std::move(prop));
+}
+
 template <typename T, size_t Size, typename Topic, typename Getter,
           typename Setter>
 void SendableBuilderImpl::AddSmallPropertyImpl(Topic topic, Getter getter,
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp b/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp
index 9fa5b69..f19d9e4 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp
@@ -19,8 +19,6 @@
       m_defaultChoice(std::move(oth.m_defaultChoice)),
       m_selected(std::move(oth.m_selected)),
       m_haveSelected(std::move(oth.m_haveSelected)),
-      m_instancePubs(std::move(oth.m_instancePubs)),
-      m_activePubs(std::move(oth.m_activePubs)),
       m_instance(std::move(oth.m_instance)) {}
 
 SendableChooserBase& SendableChooserBase::operator=(SendableChooserBase&& oth) {
@@ -29,8 +27,6 @@
   m_defaultChoice = std::move(oth.m_defaultChoice);
   m_selected = std::move(oth.m_selected);
   m_haveSelected = std::move(oth.m_haveSelected);
-  m_instancePubs = std::move(oth.m_instancePubs);
-  m_activePubs = std::move(oth.m_activePubs);
   m_instance = std::move(oth.m_instance);
   return *this;
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/cppcs/RobotBase.cpp b/third_party/allwpilib/wpilibc/src/main/native/cppcs/RobotBase.cpp
index 535d20d..c3616b9 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/cppcs/RobotBase.cpp
+++ b/third_party/allwpilib/wpilibc/src/main/native/cppcs/RobotBase.cpp
@@ -15,6 +15,7 @@
 #include <hal/FRCUsageReporting.h>
 #include <hal/HALBase.h>
 #include <networktables/NetworkTableInstance.h>
+#include <wpi/timestamp.h>
 #include <wpimath/MathShared.h>
 
 #include "WPILibVersion.h"
@@ -41,6 +42,7 @@
     std::puts("FATAL ERROR: HAL could not be initialized");
     return -1;
   }
+  DriverStation::RefreshData();
   HAL_Report(HALUsageReporting::kResourceType_Language,
              HALUsageReporting::kLanguage_CPlusPlus, 0, GetWPILibVersion());
 
@@ -137,6 +139,10 @@
         break;
     }
   }
+
+  units::second_t GetTimestamp() override {
+    return units::second_t{wpi::Now() * 1.0e-6};
+  }
 };
 }  // namespace
 
@@ -204,6 +210,10 @@
   return DriverStation::IsTest();
 }
 
+bool RobotBase::IsTestEnabled() const {
+  return DriverStation::IsTestEnabled();
+}
+
 std::thread::id RobotBase::GetThreadId() {
   return m_threadId;
 }
@@ -219,13 +229,13 @@
   SetupMathShared();
 
   auto inst = nt::NetworkTableInstance::GetDefault();
-  // subscribe to "" to force persistent values to progagate to local
+  // subscribe to "" to force persistent values to propagate to local
   nt::SubscribeMultiple(inst.GetHandle(), {{std::string_view{}}});
-#ifdef __FRC_ROBORIO__
-  inst.StartServer("/home/lvuser/networktables.json");
-#else
-  inst.StartServer();
-#endif
+  if constexpr (!IsSimulation()) {
+    inst.StartServer("/home/lvuser/networktables.json");
+  } else {
+    inst.StartServer();
+  }
 
   // wait for the NT server to actually start
   int count = 0;
@@ -241,7 +251,7 @@
 
   SmartDashboard::init();
 
-  if (IsReal()) {
+  if constexpr (!IsSimulation()) {
     std::FILE* file = nullptr;
     file = std::fopen("/tmp/frc_versions/FRC_Lib_Version.ini", "w");
 
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h
index 60d57a9..3f4a1c2 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h
@@ -20,7 +20,6 @@
 #include <thread>
 
 #include <hal/SimDevice.h>
-#include <networktables/NTSendable.h>
 #include <units/acceleration.h>
 #include <units/angle.h>
 #include <units/angular_velocity.h>
@@ -29,6 +28,7 @@
 #include <units/temperature.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
+#include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/DigitalInput.h"
@@ -53,7 +53,7 @@
  * the RoboRIO MXP port.
  */
 
-class ADIS16448_IMU : public nt::NTSendable,
+class ADIS16448_IMU : public wpi::Sendable,
                       public wpi::SendableHelper<ADIS16448_IMU> {
  public:
   /* ADIS16448 Calibration Time Enum Class */
@@ -216,7 +216,7 @@
    */
   int GetPort() const;
 
-  void InitSendable(nt::NTSendableBuilder& builder) override;
+  void InitSendable(wpi::SendableBuilder& builder) override;
 
  private:
   /** @brief ADIS16448 Register Map Declaration */
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h
index 4619819..e3b521c 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h
@@ -20,12 +20,12 @@
 #include <thread>
 
 #include <hal/SimDevice.h>
-#include <networktables/NTSendable.h>
 #include <units/acceleration.h>
 #include <units/angle.h>
 #include <units/angular_velocity.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
+#include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/DigitalInput.h"
@@ -50,7 +50,7 @@
  * available on the RoboRIO.
  */
 
-class ADIS16470_IMU : public nt::NTSendable,
+class ADIS16470_IMU : public wpi::Sendable,
                       public wpi::SendableHelper<ADIS16470_IMU> {
  public:
   /* ADIS16470 Calibration Time Enum Class */
@@ -172,7 +172,7 @@
    */
   int GetPort() const;
 
-  void InitSendable(nt::NTSendableBuilder& builder) override;
+  void InitSendable(wpi::SendableBuilder& builder) override;
 
  private:
   /* ADIS16470 Register Map Declaration */
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL345_I2C.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL345_I2C.h
index 6b6b76c..2fe0da4 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL345_I2C.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL345_I2C.h
@@ -9,7 +9,6 @@
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/I2C.h"
-#include "frc/interfaces/Accelerometer.h"
 
 namespace frc {
 
@@ -24,10 +23,11 @@
  * href="https://docs.wpilib.org/en/stable/docs/yearly-overview/known-issues.html#onboard-i2c-causing-system-lockups">
  * WPILib Known Issues</a> page for details.
  */
-class ADXL345_I2C : public Accelerometer,
-                    public nt::NTSendable,
+class ADXL345_I2C : public nt::NTSendable,
                     public wpi::SendableHelper<ADXL345_I2C> {
  public:
+  enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2, kRange_16G = 3 };
+
   enum Axes { kAxis_X = 0x00, kAxis_Y = 0x02, kAxis_Z = 0x04 };
 
   struct AllAxes {
@@ -53,11 +53,34 @@
   I2C::Port GetI2CPort() const;
   int GetI2CDeviceAddress() const;
 
-  // Accelerometer interface
-  void SetRange(Range range) final;
-  double GetX() override;
-  double GetY() override;
-  double GetZ() override;
+  /**
+   * Set the measuring range of the accelerometer.
+   *
+   * @param range The maximum acceleration, positive or negative, that the
+   *     accelerometer will measure.
+   */
+  void SetRange(Range range);
+
+  /**
+   * Returns the acceleration along the X axis in g-forces.
+   *
+   * @return The acceleration along the X axis in g-forces.
+   */
+  double GetX();
+
+  /**
+   * Returns the acceleration along the Y axis in g-forces.
+   *
+   * @return The acceleration along the Y axis in g-forces.
+   */
+  double GetY();
+
+  /**
+   * Returns the acceleration along the Z axis in g-forces.
+   *
+   * @return The acceleration along the Z axis in g-forces.
+   */
+  double GetZ();
 
   /**
    * Get the acceleration of one axis in Gs.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL345_SPI.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL345_SPI.h
index 18d48a3..3f2d8ae 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL345_SPI.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL345_SPI.h
@@ -9,7 +9,6 @@
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/SPI.h"
-#include "frc/interfaces/Accelerometer.h"
 
 namespace frc {
 
@@ -19,10 +18,11 @@
  * This class allows access to an Analog Devices ADXL345 3-axis accelerometer
  * via SPI. This class assumes the sensor is wired in 4-wire SPI mode.
  */
-class ADXL345_SPI : public Accelerometer,
-                    public nt::NTSendable,
+class ADXL345_SPI : public nt::NTSendable,
                     public wpi::SendableHelper<ADXL345_SPI> {
  public:
+  enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2, kRange_16G = 3 };
+
   enum Axes { kAxis_X = 0x00, kAxis_Y = 0x02, kAxis_Z = 0x04 };
 
   struct AllAxes {
@@ -46,11 +46,34 @@
 
   SPI::Port GetSpiPort() const;
 
-  // Accelerometer interface
-  void SetRange(Range range) final;
-  double GetX() override;
-  double GetY() override;
-  double GetZ() override;
+  /**
+   * Set the measuring range of the accelerometer.
+   *
+   * @param range The maximum acceleration, positive or negative, that the
+   *     accelerometer will measure.
+   */
+  void SetRange(Range range);
+
+  /**
+   * Returns the acceleration along the X axis in g-forces.
+   *
+   * @return The acceleration along the X axis in g-forces.
+   */
+  double GetX();
+
+  /**
+   * Returns the acceleration along the Y axis in g-forces.
+   *
+   * @return The acceleration along the Y axis in g-forces.
+   */
+  double GetY();
+
+  /**
+   * Returns the acceleration along the Z axis in g-forces.
+   *
+   * @return The acceleration along the Z axis in g-forces.
+   */
+  double GetZ();
 
   /**
    * Get the acceleration of one axis in Gs.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL362.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL362.h
index 451b5fb..ddf6ebe 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL362.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXL362.h
@@ -9,7 +9,6 @@
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/SPI.h"
-#include "frc/interfaces/Accelerometer.h"
 
 namespace frc {
 
@@ -18,10 +17,10 @@
  *
  * This class allows access to an Analog Devices ADXL362 3-axis accelerometer.
  */
-class ADXL362 : public Accelerometer,
-                public nt::NTSendable,
-                public wpi::SendableHelper<ADXL362> {
+class ADXL362 : public nt::NTSendable, public wpi::SendableHelper<ADXL362> {
  public:
+  enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2 };
+
   enum Axes { kAxis_X = 0x00, kAxis_Y = 0x02, kAxis_Z = 0x04 };
   struct AllAxes {
     double XAxis;
@@ -52,11 +51,34 @@
 
   SPI::Port GetSpiPort() const;
 
-  // Accelerometer interface
-  void SetRange(Range range) final;
-  double GetX() override;
-  double GetY() override;
-  double GetZ() override;
+  /**
+   * Set the measuring range of the accelerometer.
+   *
+   * @param range The maximum acceleration, positive or negative, that the
+   *     accelerometer will measure.
+   */
+  void SetRange(Range range);
+
+  /**
+   * Returns the acceleration along the X axis in g-forces.
+   *
+   * @return The acceleration along the X axis in g-forces.
+   */
+  double GetX();
+
+  /**
+   * Returns the acceleration along the Y axis in g-forces.
+   *
+   * @return The acceleration along the Y axis in g-forces.
+   */
+  double GetY();
+
+  /**
+   * Returns the acceleration along the Z axis in g-forces.
+   *
+   * @return The acceleration along the Z axis in g-forces.
+   */
+  double GetZ();
 
   /**
    * Get the acceleration of one axis in Gs.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h
index 55414d4..d923e13 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h
@@ -11,7 +11,7 @@
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/SPI.h"
-#include "frc/interfaces/Gyro.h"
+#include "frc/geometry/Rotation2d.h"
 
 namespace frc {
 
@@ -28,8 +28,7 @@
  * This class is for the digital ADXRS450 gyro sensor that connects via SPI.
  * Only one instance of an ADXRS %Gyro is supported.
  */
-class ADXRS450_Gyro : public Gyro,
-                      public wpi::Sendable,
+class ADXRS450_Gyro : public wpi::Sendable,
                       public wpi::SendableHelper<ADXRS450_Gyro> {
  public:
   /**
@@ -61,7 +60,7 @@
    *
    * @return the current heading of the robot in degrees.
    */
-  double GetAngle() const override;
+  double GetAngle() const;
 
   /**
    * Return the rate of rotation of the gyro
@@ -70,7 +69,7 @@
    *
    * @return the current rate in degrees per second
    */
-  double GetRate() const override;
+  double GetRate() const;
 
   /**
    * Reset the gyro.
@@ -79,11 +78,9 @@
    * significant drift in the gyro and it needs to be recalibrated after it has
    * been running.
    */
-  void Reset() override;
+  void Reset();
 
   /**
-   * Initialize the gyro.
-   *
    * Calibrate the gyro by running for a number of samples and computing the
    * center value. Then use the center value as the Accumulator center value for
    * subsequent measurements.
@@ -93,7 +90,22 @@
    * robot is first turned on while it's sitting at rest before the competition
    * starts.
    */
-  void Calibrate() final;
+  void Calibrate();
+
+  /**
+   * Return the heading of the robot as a Rotation2d.
+   *
+   * The angle is continuous, that is it will continue from 360 to 361 degrees.
+   * This allows algorithms that wouldn't want to see a discontinuity in the
+   * gyro output as it sweeps past from 360 to 0 on the second time around.
+   *
+   * The angle is expected to increase as the gyro turns counterclockwise when
+   * looked at from the top. It needs to follow the NWU axis convention.
+   *
+   * @return the current heading of the robot as a Rotation2d. This heading is
+   *         based on integration of the returned rate from the gyro.
+   */
+  Rotation2d GetRotation2d() const;
 
   /**
    * Get the SPI port number.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/AddressableLED.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/AddressableLED.h
index e6adfca..198eb67 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/AddressableLED.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/AddressableLED.h
@@ -17,7 +17,10 @@
 namespace frc {
 
 /**
- * A class for driving addressable LEDs, such as WS2812s and NeoPixels.
+ * A class for driving addressable LEDs, such as WS2812Bs and NeoPixels.
+ *
+ * By default, the timing supports WS2812B LEDs, but is configurable using
+ * SetBitTiming()
  *
  * <p>Only 1 LED driver is currently supported by the roboRIO.
  */
@@ -122,25 +125,25 @@
   /**
    * Sets the bit timing.
    *
-   * <p>By default, the driver is set up to drive WS2812s, so nothing needs to
+   * <p>By default, the driver is set up to drive WS2812Bs, so nothing needs to
    * be set for those.
    *
-   * @param lowTime0 low time for 0 bit
-   * @param highTime0 high time for 0 bit
-   * @param lowTime1 low time for 1 bit
-   * @param highTime1 high time for 1 bit
+   * @param highTime0 high time for 0 bit (default 400ns)
+   * @param lowTime0 low time for 0 bit (default 900ns)
+   * @param highTime1 high time for 1 bit (default 900ns)
+   * @param lowTime1 low time for 1 bit (default 600ns)
    */
-  void SetBitTiming(units::nanosecond_t lowTime0, units::nanosecond_t highTime0,
-                    units::nanosecond_t lowTime1,
-                    units::nanosecond_t highTime1);
+  void SetBitTiming(units::nanosecond_t highTime0, units::nanosecond_t lowTime0,
+                    units::nanosecond_t highTime1,
+                    units::nanosecond_t lowTime1);
 
   /**
    * Sets the sync time.
    *
    * <p>The sync time is the time to hold output so LEDs enable. Default set for
-   * WS2812.
+   * WS2812B.
    *
-   * @param syncTime the sync time
+   * @param syncTime the sync time (default 280us)
    */
   void SetSyncTime(units::microsecond_t syncTime);
 
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/AnalogGyro.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/AnalogGyro.h
index 6565c93..0c472fb 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/AnalogGyro.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/AnalogGyro.h
@@ -10,7 +10,7 @@
 #include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
-#include "frc/interfaces/Gyro.h"
+#include "frc/geometry/Rotation2d.h"
 
 namespace frc {
 
@@ -29,8 +29,7 @@
  *
  * This class is for gyro sensors that connect to an analog input.
  */
-class AnalogGyro : public Gyro,
-                   public wpi::Sendable,
+class AnalogGyro : public wpi::Sendable,
                    public wpi::SendableHelper<AnalogGyro> {
  public:
   static constexpr int kOversampleBits = 10;
@@ -118,7 +117,7 @@
    * @return The current heading of the robot in degrees. This heading is based
    *         on integration of the returned rate from the gyro.
    */
-  double GetAngle() const override;
+  double GetAngle() const;
 
   /**
    * Return the rate of rotation of the gyro
@@ -127,7 +126,7 @@
    *
    * @return the current rate in degrees per second
    */
-  double GetRate() const override;
+  double GetRate() const;
 
   /**
    * Return the gyro center value. If run after calibration,
@@ -174,7 +173,7 @@
    * significant drift in the gyro and it needs to be recalibrated after it has
    * been running.
    */
-  void Reset() final;
+  void Reset();
 
   /**
    * Initialize the gyro.
@@ -183,7 +182,32 @@
    */
   void InitGyro();
 
-  void Calibrate() final;
+  /**
+   * Calibrate the gyro by running for a number of samples and computing the
+   * center value. Then use the center value as the Accumulator center value for
+   * subsequent measurements.
+   *
+   * It's important to make sure that the robot is not moving while the
+   * centering calculations are in progress, this is typically done when the
+   * robot is first turned on while it's sitting at rest before the competition
+   * starts.
+   */
+  void Calibrate();
+
+  /**
+   * Return the heading of the robot as a Rotation2d.
+   *
+   * The angle is continuous, that is it will continue from 360 to 361 degrees.
+   * This allows algorithms that wouldn't want to see a discontinuity in the
+   * gyro output as it sweeps past from 360 to 0 on the second time around.
+   *
+   * The angle is expected to increase as the gyro turns counterclockwise when
+   * looked at from the top. It needs to follow the NWU axis convention.
+   *
+   * @return the current heading of the robot as a Rotation2d. This heading is
+   *         based on integration of the returned rate from the gyro.
+   */
+  Rotation2d GetRotation2d() const;
 
   /**
    * Gets the analog input for the gyro.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/AnalogTriggerOutput.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/AnalogTriggerOutput.h
index 6f52cab..88bd6c3 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/AnalogTriggerOutput.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/AnalogTriggerOutput.h
@@ -30,7 +30,7 @@
  * range defined by the limits.
  *
  * The RisingPulse and FallingPulse outputs detect an instantaneous transition
- * from above the upper limit to below the lower limit, and vise versa. These
+ * from above the upper limit to below the lower limit, and vice versa. These
  * pulses represent a rollover condition of a sensor and can be routed to an up
  * / down counter or to interrupts. Because the outputs generate a pulse, they
  * cannot be read directly. To help ensure that a rollover condition is not
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h
index 0e5bee6..a8f0adc 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h
@@ -7,8 +7,6 @@
 #include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
-#include "frc/interfaces/Accelerometer.h"
-
 namespace frc {
 
 /**
@@ -16,10 +14,11 @@
  *
  * This class allows access to the roboRIO's internal accelerometer.
  */
-class BuiltInAccelerometer : public Accelerometer,
-                             public wpi::Sendable,
+class BuiltInAccelerometer : public wpi::Sendable,
                              public wpi::SendableHelper<BuiltInAccelerometer> {
  public:
+  enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2 };
+
   /**
    * Constructor.
    *
@@ -30,30 +29,28 @@
   BuiltInAccelerometer(BuiltInAccelerometer&&) = default;
   BuiltInAccelerometer& operator=(BuiltInAccelerometer&&) = default;
 
-  // Accelerometer interface
   /**
    * Set the measuring range of the accelerometer.
    *
    * @param range The maximum acceleration, positive or negative, that the
-   *              accelerometer will measure. Not all accelerometers support all
-   *              ranges.
+   *     accelerometer will measure.
    */
-  void SetRange(Range range) final;
+  void SetRange(Range range);
 
   /**
    * @return The acceleration of the roboRIO along the X axis in g-forces
    */
-  double GetX() override;
+  double GetX();
 
   /**
    * @return The acceleration of the roboRIO along the Y axis in g-forces
    */
-  double GetY() override;
+  double GetY();
 
   /**
    * @return The acceleration of the roboRIO along the Z axis in g-forces
    */
-  double GetZ() override;
+  double GetZ();
 
   void InitSendable(wpi::SendableBuilder& builder) override;
 };
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/CAN.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/CAN.h
index 4c9b9bd..dc04988 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/CAN.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/CAN.h
@@ -10,8 +10,11 @@
 
 namespace frc {
 struct CANData {
+  /** Contents of the CAN packet. */
   uint8_t data[8];
+  /** Length of packet in bytes. */
   int32_t length;
+  /** CAN frame timestamp in milliseconds. */
   uint64_t timestamp;
 };
 
@@ -158,6 +161,15 @@
    */
   bool ReadPacketTimeout(int apiId, int timeoutMs, CANData* data);
 
+  /**
+   * Reads the current value of the millisecond-resolution timer that CANData
+   * timestamps are based on
+   *
+   * @return Current value of timer used as a base time for CANData timestamps
+   * in milliseconds
+   */
+  static uint64_t GetTimestampBaseTime();
+
   static constexpr HAL_CANManufacturer kTeamManufacturer = HAL_CAN_Man_kTeamUse;
   static constexpr HAL_CANDeviceType kTeamDeviceType =
       HAL_CAN_Dev_kMiscellaneous;
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Compressor.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Compressor.h
index 1329a3b..ed1b3b9 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Compressor.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Compressor.h
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include <hal/Types.h>
-#include <wpi/deprecated.h>
 #include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
@@ -59,19 +58,6 @@
   Compressor& operator=(Compressor&&) = default;
 
   /**
-   * Check if compressor output is active.
-   * To (re)enable the compressor use EnableDigital() or EnableAnalog(...).
-   *
-   * @return true if the compressor is on.
-   * @deprecated To avoid confusion in thinking this (re)enables the compressor
-   * use IsEnabled().
-   */
-  WPI_DEPRECATED(
-      "To avoid confusion in thinking this (re)enables the compressor use "
-      "IsEnabled()")
-  bool Enabled() const;
-
-  /**
    * Returns whether the compressor is active or not.
    *
    * @return true if the compressor is on - otherwise false.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Counter.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Counter.h
index d501b7e..71eebca 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Counter.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Counter.h
@@ -343,6 +343,34 @@
 
   int GetFPGAIndex() const;
 
+  /**
+   * Set the distance per pulse for this counter. This sets the multiplier used
+   * to determine the distance driven based on the count value from the encoder.
+   * Set this value based on the Pulses per Revolution and factor in any gearing
+   * reductions. This distance can be in any units you like, linear or angular.
+   *
+   * @param distancePerPulse The scale factor that will be used to convert
+   * pulses to useful units.
+   */
+  void SetDistancePerPulse(double distancePerPulse);
+
+  /**
+   * Read the current scaled counter value. Read the value at this instant,
+   * scaled by the distance per pulse (defaults to 1).
+   *
+   * @return The distance since the last reset
+   */
+  double GetDistance() const;
+
+  /**
+   * Get the current rate of the Counter. Read the current rate of the counter
+   * accounting for the distance per pulse value. The default value for distance
+   * per pulse (1) yields units of pulses per second.
+   *
+   * @return The rate in units/sec
+   */
+  double GetRate() const;
+
   // CounterBase interface
   /**
    * Read the current counter value.
@@ -434,6 +462,7 @@
 
  private:
   int m_index = 0;  // The index of this counter.
+  double m_distancePerPulse = 1;
 
   friend class DigitalGlitchFilter;
 };
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/DataLogManager.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/DataLogManager.h
index fa7abba..336af4a 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/DataLogManager.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/DataLogManager.h
@@ -50,6 +50,11 @@
                     double period = 0.25);
 
   /**
+   * Stop data log manager.
+   */
+  static void Stop();
+
+  /**
    * Log a message to the "messages" entry. The message is also printed to
    * standard output (followed by a newline).
    *
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/DoubleSolenoid.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/DoubleSolenoid.h
index 381e3a1..f02ba5a 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/DoubleSolenoid.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/DoubleSolenoid.h
@@ -98,7 +98,7 @@
    * If a solenoid is shorted, it is added to the DisabledList and disabled
    * until power cycle, or until faults are cleared.
    *
-   * @see ClearAllPCMStickyFaults()
+   * @see ClearAllStickyFaults()
    * @return If solenoid is disabled due to short.
    */
   bool IsFwdSolenoidDisabled() const;
@@ -109,7 +109,7 @@
    * If a solenoid is shorted, it is added to the DisabledList and disabled
    * until power cycle, or until faults are cleared.
    *
-   * @see ClearAllPCMStickyFaults()
+   * @see ClearAllStickyFaults()
    * @return If solenoid is disabled due to short.
    */
   bool IsRevSolenoidDisabled() const;
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/DriverStation.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/DriverStation.h
index 4cbf6da..ea568a7 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/DriverStation.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/DriverStation.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <optional>
 #include <string>
 
 #include <units/time.h>
@@ -21,7 +22,7 @@
  */
 class DriverStation final {
  public:
-  enum Alliance { kRed, kBlue, kInvalid };
+  enum Alliance { kRed, kBlue };
   enum MatchType { kNone, kPractice, kQualification, kElimination };
 
   static constexpr int kJoystickPorts = 6;
@@ -210,6 +211,14 @@
   static bool IsTest();
 
   /**
+   * Check if the DS is commanding Test mode and if it has enabled the robot.
+   *
+   * @return True if the robot is being commanded to be in Test mode and
+   * enabled.
+   */
+  static bool IsTestEnabled();
+
+  /**
    * Check if the DS is attached.
    *
    * @return True if the DS is connected to the robot
@@ -227,6 +236,9 @@
   /**
    * Returns the game specific message provided by the FMS.
    *
+   * If the FMS is not connected, it is set from the game data setting on the
+   * driver station.
+   *
    * @return A string containing the game specific message.
    */
   static std::string GetGameSpecificMessage();
@@ -262,39 +274,58 @@
   static int GetReplayNumber();
 
   /**
-   * Return the alliance that the driver station says it is on.
+   * Return the alliance that the driver station says it is on from the FMS.
+   *
+   * If the FMS is not connected, it is set from the team alliance setting on
+   * the driver station.
    *
    * This could return kRed or kBlue.
    *
    * @return The Alliance enum (kRed, kBlue or kInvalid)
    */
-  static Alliance GetAlliance();
+  static std::optional<Alliance> GetAlliance();
 
   /**
-   * Return the driver station location on the field.
+   * Return the driver station location from the FMS.
+   *
+   * If the FMS is not connected, it is set from the team alliance setting on
+   * the driver station.
    *
    * This could return 1, 2, or 3.
    *
    * @return The location of the driver station (1-3, 0 for invalid)
    */
-  static int GetLocation();
+  static std::optional<int> GetLocation();
 
   /**
-   * Return the approximate match time.
+   * Wait for a DS connection.
    *
-   * The FMS does not send an official match time to the robots, but does send
-   * an approximate match time. The value will count down the time remaining in
-   * the current period (auto or teleop).
-   *
+   * @param timeout timeout in seconds. 0 for infinite.
+   * @return true if connected, false if timeout
+   */
+  static bool WaitForDsConnection(units::second_t timeout);
+
+  /**
+   * Return the approximate match time. The FMS does not send an official match
+   * time to the robots, but does send an approximate match time. The value will
+   * count down the time remaining in the current period (auto or teleop).
    * Warning: This is not an official time (so it cannot be used to dispute ref
    * calls or guarantee that a function will trigger before the match ends).
    *
-   * The Practice Match function of the DS approximates the behavior seen on
-   * the field.
+   * <p>When connected to the real field, this number only changes in full
+   * integer increments, and always counts down.
    *
-   * @return Time remaining in current match period (auto or teleop)
+   * <p>When the DS is in practice mode, this number is a floating point number,
+   * and counts down.
+   *
+   * <p>When the DS is in teleop or autonomous mode, this number is a floating
+   * point number, and counts up.
+   *
+   * <p>Simulation matches DS behavior without an FMS connected.
+   *
+   * @return Time remaining in current match period (auto or teleop) in seconds
    */
-  static double GetMatchTime();
+  static units::second_t GetMatchTime();
 
   /**
    * Read the battery voltage.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Encoder.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Encoder.h
index deb36c0..7315e16 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Encoder.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Encoder.h
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include <hal/Types.h>
-#include <wpi/deprecated.h>
 #include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
@@ -168,7 +167,7 @@
    * @return Period in seconds of the most recent pulse.
    * @deprecated Use getRate() in favor of this method.
    */
-  WPI_DEPRECATED("Use GetRate() in favor of this method")
+  [[deprecated("Use GetRate() in favor of this method")]]
   units::second_t GetPeriod() const override;
 
   /**
@@ -186,9 +185,9 @@
    *             periods and SetMinRate() scales using value from
    *             SetDistancePerPulse().
    */
-  WPI_DEPRECATED(
+  [[deprecated(
       "Use SetMinRate() in favor of this method.  This takes unscaled periods "
-      "and SetMinRate() scales using value from SetDistancePerPulse().")
+      "and SetMinRate() scales using value from SetDistancePerPulse().")]]
   void SetMaxPeriod(units::second_t maxPeriod) override;
 
   /**
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Errors.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Errors.h
index 252a335..4dae6c9 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Errors.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Errors.h
@@ -95,15 +95,16 @@
  * @param[in]  args error message format args
  * @return runtime error object
  */
-[[nodiscard]] RuntimeError MakeErrorV(int32_t status, const char* fileName,
-                                      int lineNumber, const char* funcName,
-                                      fmt::string_view format,
-                                      fmt::format_args args);
+[[nodiscard]]
+RuntimeError MakeErrorV(int32_t status, const char* fileName, int lineNumber,
+                        const char* funcName, fmt::string_view format,
+                        fmt::format_args args);
 
 template <typename... Args>
-[[nodiscard]] inline RuntimeError MakeError(
-    int32_t status, const char* fileName, int lineNumber, const char* funcName,
-    fmt::string_view format, Args&&... args) {
+[[nodiscard]]
+inline RuntimeError MakeError(int32_t status, const char* fileName,
+                              int lineNumber, const char* funcName,
+                              fmt::string_view format, Args&&... args) {
   return MakeErrorV(status, fileName, lineNumber, funcName, format,
                     fmt::make_format_args(args...));
 }
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/IterativeRobotBase.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/IterativeRobotBase.h
index 77ed197..42b0c71 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/IterativeRobotBase.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/IterativeRobotBase.h
@@ -23,6 +23,9 @@
  *
  * RobotInit() -- provide for initialization at robot power-on
  *
+ * DriverStationConnected() -- provide for initialization the first time the DS
+ * is connected
+ *
  * Init() functions -- each of the following functions is called once when the
  * appropriate mode is entered:
  *
@@ -68,6 +71,14 @@
   virtual void RobotInit();
 
   /**
+   * Code that needs to know the DS state should go here.
+   *
+   * Users should override this method for initialization that needs to occur
+   * after the DS is connected, such as needing the alliance information.
+   */
+  virtual void DriverStationConnected();
+
+  /**
    * Robot-wide simulation initialization code should go here.
    *
    * Users should override this method for default Robot-wide simulation
@@ -242,6 +253,7 @@
   Watchdog m_watchdog;
   bool m_ntFlushEnabled = true;
   bool m_lwEnabledInTest = true;
+  bool m_calledDsConnected = false;
 
   void PrintLoopOverrunMessage();
 };
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Joystick.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Joystick.h
index fc0df35..a955718 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Joystick.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Joystick.h
@@ -6,6 +6,8 @@
 
 #include <array>
 
+#include <units/angle.h>
+
 #include "frc/GenericHID.h"
 
 namespace frc {
@@ -226,7 +228,9 @@
    * in radians.
    *
    * @return The direction of the vector in radians
+   * @deprecated Use GetDirection() instead.
    */
+  [[deprecated("Use GetDirection() instead.")]]
   double GetDirectionRadians() const;
 
   /**
@@ -234,9 +238,18 @@
    * in degrees.
    *
    * @return The direction of the vector in degrees
+   * @deprecated Use GetDirection() instead.
    */
+  [[deprecated("Use GetDirection() instead.")]]
   double GetDirectionDegrees() const;
 
+  /**
+   * Get the direction of the vector formed by the joystick and its origin.
+   *
+   * @return The direction of the vector.
+   */
+  units::radian_t GetDirection() const;
+
  private:
   enum Axis { kX, kY, kZ, kTwist, kThrottle, kNumAxes };
   enum Button { kTrigger = 1, kTop = 2 };
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Notifier.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Notifier.h
index bda685c..0bd54d4 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Notifier.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Notifier.h
@@ -7,10 +7,10 @@
 #include <stdint.h>
 
 #include <atomic>
+#include <concepts>
 #include <functional>
 #include <string_view>
 #include <thread>
-#include <type_traits>
 #include <utility>
 
 #include <hal/Types.h>
@@ -20,7 +20,7 @@
 namespace frc {
 
 /**
- * Notifiers run a callback function on a separate thread at a specified period.
+ * Notifiers run a user-provided callback function on a separate thread.
  *
  * If StartSingle() is used, the callback will run once. If StartPeriodic() is
  * used, the callback will run repeatedly with the given period until stop() is
@@ -29,22 +29,25 @@
 class Notifier {
  public:
   /**
-   * Create a Notifier for timer event notification.
+   * Create a Notifier with the given callback.
    *
-   * @param handler The handler is called at the notification time which is set
-   *                using StartSingle or StartPeriodic.
+   * Configure when the callback runs with StartSingle() or StartPeriodic().
+   *
+   * @param callback The callback to run.
    */
-  explicit Notifier(std::function<void()> handler);
+  explicit Notifier(std::function<void()> callback);
 
-  template <
-      typename Callable, typename Arg, typename... Args,
-      typename = std::enable_if_t<std::is_invocable_v<Callable, Arg, Args...>>>
-  Notifier(Callable&& f, Arg&& arg, Args&&... args)
-      : Notifier(std::bind(std::forward<Callable>(f), std::forward<Arg>(arg),
+  template <typename Arg, typename... Args>
+  Notifier(std::invocable<Arg, Args...> auto&& callback, Arg&& arg,
+           Args&&... args)
+      : Notifier(std::bind(std::forward<decltype(callback)>(callback),
+                           std::forward<Arg>(arg),
                            std::forward<Args>(args)...)) {}
 
   /**
-   * Create a Notifier for timer event notification.
+   * Create a Notifier with the given callback.
+   *
+   * Configure when the callback runs with StartSingle() or StartPeriodic().
    *
    * This overload makes the underlying thread run with a real-time priority.
    * This is useful for reducing scheduling jitter on processes which are
@@ -53,16 +56,16 @@
    * @param priority The FIFO real-time scheduler priority ([1..99] where a
    *                 higher number represents higher priority). See "man 7
    *                 sched" for more details.
-   * @param handler  The handler is called at the notification time which is set
-   *                 using StartSingle or StartPeriodic.
+   * @param callback The callback to run.
    */
-  explicit Notifier(int priority, std::function<void()> handler);
+  explicit Notifier(int priority, std::function<void()> callback);
 
-  template <typename Callable, typename Arg, typename... Args>
-  Notifier(int priority, Callable&& f, Arg&& arg, Args&&... args)
-      : Notifier(priority,
-                 std::bind(std::forward<Callable>(f), std::forward<Arg>(arg),
-                           std::forward<Args>(args)...)) {}
+  template <typename Arg, typename... Args>
+  Notifier(int priority, std::invocable<Arg, Args...> auto&& callback,
+           Arg&& arg, Args&&... args)
+      : Notifier(priority, std::bind(std::forward<decltype(callback)>(callback),
+                                     std::forward<Arg>(arg),
+                                     std::forward<Args>(args)...)) {}
 
   /**
    * Free the resources for a timer event.
@@ -73,51 +76,54 @@
   Notifier& operator=(Notifier&& rhs);
 
   /**
-   * Sets the name of the notifier.  Used for debugging purposes only.
+   * Sets the name of the notifier. Used for debugging purposes only.
    *
    * @param name Name
    */
   void SetName(std::string_view name);
 
   /**
-   * Change the handler function.
+   * Change the callback function.
    *
-   * @param handler Handler
+   * @param callback The callback function.
+   * @deprecated Use SetCallback() instead.
    */
-  void SetHandler(std::function<void()> handler);
+  [[deprecated("Use SetCallback() instead.")]]
+  void SetHandler(std::function<void()> callback);
 
   /**
-   * Register for single event notification.
+   * Change the callback function.
    *
-   * A timer event is queued for a single event after the specified delay.
+   * @param callback The callback function.
+   */
+  void SetCallback(std::function<void()> callback);
+
+  /**
+   * Run the callback once after the given delay.
    *
-   * @param delay Amount of time to wait before the handler is called.
+   * @param delay Time to wait before the callback is called.
    */
   void StartSingle(units::second_t delay);
 
   /**
-   * Register for periodic event notification.
+   * Run the callback periodically with the given period.
    *
-   * A timer event is queued for periodic event notification. Each time the
-   * interrupt occurs, the event will be immediately requeued for the same time
-   * interval.
+   * The user-provided callback should be written so that it completes before
+   * the next time it's scheduled to run.
    *
-   * The user-provided callback should be written in a nonblocking manner so the
-   * callback can be recalled at the next periodic event notification.
-   *
-   * @param period Period to call the handler starting one period
-   *               after the call to this method.
+   * @param period Period after which to to call the callback starting one
+   *               period after the call to this method.
    */
   void StartPeriodic(units::second_t period);
 
   /**
-   * Stop timer events from occurring.
+   * Stop further callback invocations.
    *
-   * Stop any repeating timer events from occurring. This will also remove any
-   * single notification events from the queue.
+   * No further periodic callbacks will occur. Single invocations will also be
+   * cancelled if they haven't yet occurred.
    *
-   * If a timer-based call to the registered handler is in progress, this
-   * function will block until the handler call is complete.
+   * If a callback invocation is in progress, this function will block until the
+   * callback is complete.
    */
   void Stop();
 
@@ -154,22 +160,24 @@
   // The thread waiting on the HAL alarm
   std::thread m_thread;
 
-  // Held while updating process information
+  // The mutex held while updating process information
   wpi::mutex m_processMutex;
 
-  // HAL handle, atomic for proper destruction
+  // HAL handle (atomic for proper destruction)
   std::atomic<HAL_NotifierHandle> m_notifier{0};
 
-  // Address of the handler
-  std::function<void()> m_handler;
+  // The user-provided callback
+  std::function<void()> m_callback;
 
-  // The absolute expiration time
+  // The time at which the callback should be called. Has the same zero as
+  // Timer::GetFPGATimestamp().
   units::second_t m_expirationTime = 0_s;
 
-  // The relative time (either periodic or single)
+  // If periodic, stores the callback period; if single, stores the time until
+  // the callback call.
   units::second_t m_period = 0_s;
 
-  // True if this is a periodic event
+  // True if the callback is periodic
   bool m_periodic = false;
 };
 
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/PS5Controller.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/PS5Controller.h
new file mode 100644
index 0000000..2a24f5e
--- /dev/null
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/PS5Controller.h
@@ -0,0 +1,529 @@
+// 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 "frc/GenericHID.h"
+
+namespace frc {
+
+/**
+ * Handle input from PS5 controllers connected to the Driver Station.
+ *
+ * <p>This class handles PS5 input that comes from the Driver Station. Each time
+ * a value is requested the most recent value is returned. There is a single
+ * class instance for each controller and the mapping of ports to hardware
+ * buttons depends on the code in the Driver Station.
+ */
+class PS5Controller : public GenericHID {
+ public:
+  /**
+   * Construct an instance of an PS5 controller.
+   *
+   * The controller index is the USB port on the Driver Station.
+   *
+   * @param port The port on the Driver Station that the controller is plugged
+   *             into (0-5).
+   */
+  explicit PS5Controller(int port);
+
+  ~PS5Controller() override = default;
+
+  PS5Controller(PS5Controller&&) = default;
+  PS5Controller& operator=(PS5Controller&&) = default;
+
+  /**
+   * Get the X axis value of left side of the controller.
+   *
+   * @return the axis value.
+   */
+  double GetLeftX() const;
+
+  /**
+   * Get the X axis value of right side of the controller.
+   *
+   * @return the axis value.
+   */
+  double GetRightX() const;
+
+  /**
+   * Get the Y axis value of left side of the controller.
+   *
+   * @return the axis value.
+   */
+  double GetLeftY() const;
+
+  /**
+   * Get the Y axis value of right side of the controller.
+   *
+   * @return the axis value.
+   */
+  double GetRightY() const;
+
+  /**
+   * Get the L2 axis value of the controller. Note that this axis is bound to
+   * the range of [0, 1] as opposed to the usual [-1, 1].
+   *
+   * @return the axis value.
+   */
+  double GetL2Axis() const;
+
+  /**
+   * Get the R2 axis value of the controller. Note that this axis is bound to
+   * the range of [0, 1] as opposed to the usual [-1, 1].
+   *
+   * @return the axis value.
+   */
+  double GetR2Axis() const;
+
+  /**
+   * Read the value of the Square button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetSquareButton() const;
+
+  /**
+   * Whether the Square button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetSquareButtonPressed();
+
+  /**
+   * Whether the Square button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetSquareButtonReleased();
+
+  /**
+   * Constructs an event instance around the square button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the square button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Square(EventLoop* loop) const;
+
+  /**
+   * Read the value of the Cross button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetCrossButton() const;
+
+  /**
+   * Whether the Cross button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetCrossButtonPressed();
+
+  /**
+   * Whether the Cross button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetCrossButtonReleased();
+
+  /**
+   * Constructs an event instance around the cross button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the cross button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Cross(EventLoop* loop) const;
+
+  /**
+   * Read the value of the Circle button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetCircleButton() const;
+
+  /**
+   * Whether the Circle button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetCircleButtonPressed();
+
+  /**
+   * Whether the Circle button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetCircleButtonReleased();
+
+  /**
+   * Constructs an event instance around the circle button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the circle button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Circle(EventLoop* loop) const;
+
+  /**
+   * Read the value of the Triangle button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetTriangleButton() const;
+
+  /**
+   * Whether the Triangle button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetTriangleButtonPressed();
+
+  /**
+   * Whether the Triangle button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetTriangleButtonReleased();
+
+  /**
+   * Constructs an event instance around the triangle button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the triangle button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Triangle(EventLoop* loop) const;
+
+  /**
+   * Read the value of the L1 button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetL1Button() const;
+
+  /**
+   * Whether the L1 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetL1ButtonPressed();
+
+  /**
+   * Whether the L1 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetL1ButtonReleased();
+
+  /**
+   * Constructs an event instance around the L1 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L1 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent L1(EventLoop* loop) const;
+
+  /**
+   * Read the value of the R1 button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetR1Button() const;
+
+  /**
+   * Whether the R1 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetR1ButtonPressed();
+
+  /**
+   * Whether the R1 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetR1ButtonReleased();
+
+  /**
+   * Constructs an event instance around the R1 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R1 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent R1(EventLoop* loop) const;
+
+  /**
+   * Read the value of the L2 button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetL2Button() const;
+
+  /**
+   * Whether the L2 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetL2ButtonPressed();
+
+  /**
+   * Whether the L2 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetL2ButtonReleased();
+
+  /**
+   * Constructs an event instance around the L2 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L2 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent L2(EventLoop* loop) const;
+
+  /**
+   * Read the value of the R2 button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetR2Button() const;
+
+  /**
+   * Whether the R2 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetR2ButtonPressed();
+
+  /**
+   * Whether the R2 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetR2ButtonReleased();
+
+  /**
+   * Constructs an event instance around the R2 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R2 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent R2(EventLoop* loop) const;
+
+  /**
+   * Read the value of the Create button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetCreateButton() const;
+
+  /**
+   * Whether the Create button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetCreateButtonPressed();
+
+  /**
+   * Whether the Create button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetCreateButtonReleased();
+
+  /**
+   * Constructs an event instance around the Create button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the Create button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Create(EventLoop* loop) const;
+
+  /**
+   * Read the value of the Options button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetOptionsButton() const;
+
+  /**
+   * Whether the Options button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetOptionsButtonPressed();
+
+  /**
+   * Whether the Options button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetOptionsButtonReleased();
+
+  /**
+   * Constructs an event instance around the options button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the options button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Options(EventLoop* loop) const;
+
+  /**
+   * Read the value of the L3 button (pressing the left analog stick) on the
+   * controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetL3Button() const;
+
+  /**
+   * Whether the L3 (left stick) button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetL3ButtonPressed();
+
+  /**
+   * Whether the L3 (left stick) button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetL3ButtonReleased();
+
+  /**
+   * Constructs an event instance around the L3 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L3 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent L3(EventLoop* loop) const;
+
+  /**
+   * Read the value of the R3 button (pressing the right analog stick) on the
+   * controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetR3Button() const;
+
+  /**
+   * Whether the R3 (right stick) button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetR3ButtonPressed();
+
+  /**
+   * Whether the R3 (right stick) button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetR3ButtonReleased();
+
+  /**
+   * Constructs an event instance around the R3 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R3 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent R3(EventLoop* loop) const;
+
+  /**
+   * Read the value of the PS button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetPSButton() const;
+
+  /**
+   * Whether the PS button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetPSButtonPressed();
+
+  /**
+   * Whether the PS button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetPSButtonReleased();
+
+  /**
+   * Constructs an event instance around the PS button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the PS button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent PS(EventLoop* loop) const;
+
+  /**
+   * Read the value of the touchpad button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetTouchpad() const;
+
+  /**
+   * Whether the touchpad was pressed since the last check.
+   *
+   * @return Whether the touchpad was pressed since the last check.
+   */
+  bool GetTouchpadPressed();
+
+  /**
+   * Whether the touchpad was released since the last check.
+   *
+   * @return Whether the touchpad was released since the last check.
+   */
+  bool GetTouchpadReleased();
+
+  /**
+   * Constructs an event instance around the touchpad's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the touchpad's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Touchpad(EventLoop* loop) const;
+
+  struct Button {
+    static constexpr int kSquare = 3;
+    static constexpr int kCross = 1;
+    static constexpr int kCircle = 2;
+    static constexpr int kTriangle = 4;
+    static constexpr int kL1 = 5;
+    static constexpr int kR1 = 6;
+    static constexpr int kL2 = 7;
+    static constexpr int kR2 = 8;
+    static constexpr int kCreate = 9;
+    static constexpr int kOptions = 10;
+    static constexpr int kL3 = 12;
+    static constexpr int kR3 = 13;
+    static constexpr int kPS = 11;
+    static constexpr int kTouchpad = 14;
+  };
+
+  struct Axis {
+    static constexpr int kLeftX = 0;
+    static constexpr int kLeftY = 1;
+    static constexpr int kRightX = 3;
+    static constexpr int kRightY = 4;
+    static constexpr int kL2 = 2;
+    static constexpr int kR2 = 5;
+  };
+};
+
+}  // namespace frc
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/PWM.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/PWM.h
index e508915..0871c9c 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/PWM.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/PWM.h
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include <hal/Types.h>
+#include <units/time.h>
 #include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
@@ -18,18 +19,9 @@
  * Class implements the PWM generation in the FPGA.
  *
  * The values supplied as arguments for PWM outputs range from -1.0 to 1.0. They
- * are mapped to the hardware dependent values, in this case 0-2000 for the
- * FPGA. Changes are immediately sent to the FPGA, and the update occurs at the
- * next FPGA cycle (5.005ms). There is no delay.
- *
- * As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-2000 values as
- * follows:
- *   - 2000 = maximum pulse width
- *   - 1999 to 1001 = linear scaling from "full forward" to "center"
- *   - 1000 = center value
- *   - 999 to 2 = linear scaling from "center" to "full reverse"
- *   - 1 = minimum pulse width (currently 0.5ms)
- *   - 0 = disabled (i.e. PWM output is held low)
+ * are mapped to the microseconds to keep the pulse high, with a range of 0
+ * (off) to 4096. Changes are immediately sent to the FPGA, and the update
+ * occurs at the next FPGA cycle (5.05ms). There is no delay.
  */
 class PWM : public wpi::Sendable, public wpi::SendableHelper<PWM> {
  public:
@@ -40,15 +32,15 @@
    */
   enum PeriodMultiplier {
     /**
-     * Don't skip pulses. PWM pulses occur every 5.005 ms
+     * Don't skip pulses. PWM pulses occur every 5.05 ms
      */
     kPeriodMultiplier_1X = 1,
     /**
-     * Skip every other pulse. PWM pulses occur every 10.010 ms
+     * Skip every other pulse. PWM pulses occur every 10.10 ms
      */
     kPeriodMultiplier_2X = 2,
     /**
-     * Skip three out of four pulses. PWM pulses occur every 20.020 ms
+     * Skip three out of four pulses. PWM pulses occur every 20.20 ms
      */
     kPeriodMultiplier_4X = 4
   };
@@ -78,30 +70,29 @@
   PWM& operator=(PWM&&) = default;
 
   /**
-   * Set the PWM value directly to the hardware.
+   * Set the PWM pulse time directly to the hardware.
    *
-   * Write a raw value to a PWM channel.
+   * Write a microsecond value to a PWM channel.
    *
-   * @param value Raw PWM value.
+   * @param time Microsecond PWM value.
    */
-  virtual void SetRaw(uint16_t value);
+  virtual void SetPulseTime(units::microsecond_t time);
 
   /**
-   * Get the PWM value directly from the hardware.
+   * Get the PWM pulse time directly from the hardware.
    *
-   * Read a raw value from a PWM channel.
+   * Read a microsecond value from a PWM channel.
    *
-   * @return Raw PWM control value.
+   * @return Microsecond PWM control value.
    */
-  virtual uint16_t GetRaw() const;
+  virtual units::microsecond_t GetPulseTime() const;
 
   /**
    * Set the PWM value based on a position.
    *
    * This is intended to be used by servos.
    *
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre SetBounds() called.
    *
    * @param pos The position to set the servo between 0.0 and 1.0.
    */
@@ -112,8 +103,7 @@
    *
    * This is intended to be used by servos.
    *
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre SetBounds() called.
    *
    * @return The position the servo is set to between 0.0 and 1.0.
    */
@@ -124,11 +114,7 @@
    *
    * This is intended to be used by motor controllers.
    *
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinPositivePwm() called.
-   * @pre SetCenterPwm() called.
-   * @pre SetMaxNegativePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre SetBounds() called.
    *
    * @param speed The speed to set the motor controller between -1.0 and 1.0.
    */
@@ -139,17 +125,14 @@
    *
    * This is intended to be used by motor controllers.
    *
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinPositivePwm() called.
-   * @pre SetMaxNegativePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre SetBounds() called.
    *
    * @return The most recently set speed between -1.0 and 1.0.
    */
   virtual double GetSpeed() const;
 
   /**
-   * Temporarily disables the PWM output. The next set call will reenable
+   * Temporarily disables the PWM output. The next set call will re-enable
    * the output.
    */
   virtual void SetDisabled();
@@ -180,46 +163,38 @@
    * The values determine the upper and lower speeds as well as the deadband
    * bracket.
    *
-   * @param max         The max PWM pulse width in ms
-   * @param deadbandMax The high end of the deadband range pulse width in ms
-   * @param center      The center (off) pulse width in ms
-   * @param deadbandMin The low end of the deadband pulse width in ms
-   * @param min         The minimum pulse width in ms
+   * @param max         The max PWM pulse width in us
+   * @param deadbandMax The high end of the deadband range pulse width in us
+   * @param center      The center (off) pulse width in us
+   * @param deadbandMin The low end of the deadband pulse width in us
+   * @param min         The minimum pulse width in us
    */
-  void SetBounds(double max, double deadbandMax, double center,
-                 double deadbandMin, double min);
-
-  /**
-   * Set the bounds on the PWM values.
-   *
-   * This sets the bounds on the PWM values for a particular each type of
-   * controller. The values determine the upper and lower speeds as well as the
-   * deadband bracket.
-   *
-   * @param max         The Minimum pwm value
-   * @param deadbandMax The high end of the deadband range
-   * @param center      The center speed (off)
-   * @param deadbandMin The low end of the deadband range
-   * @param min         The minimum pwm value
-   */
-  void SetRawBounds(int max, int deadbandMax, int center, int deadbandMin,
-                    int min);
+  void SetBounds(units::microsecond_t max, units::microsecond_t deadbandMax,
+                 units::microsecond_t center, units::microsecond_t deadbandMin,
+                 units::microsecond_t min);
 
   /**
    * Get the bounds on the PWM values.
    *
-   * This Gets the bounds on the PWM values for a particular each type of
+   * This gets the bounds on the PWM values for a particular each type of
    * controller. The values determine the upper and lower speeds as well as the
    * deadband bracket.
    *
-   * @param max         The Minimum pwm value
+   * @param max         The maximum pwm value
    * @param deadbandMax The high end of the deadband range
    * @param center      The center speed (off)
    * @param deadbandMin The low end of the deadband range
    * @param min         The minimum pwm value
    */
-  void GetRawBounds(int32_t* max, int32_t* deadbandMax, int32_t* center,
-                    int32_t* deadbandMin, int32_t* min);
+  void GetBounds(units::microsecond_t* max, units::microsecond_t* deadbandMax,
+                 units::microsecond_t* center,
+                 units::microsecond_t* deadbandMin, units::microsecond_t* min);
+
+  /**
+   * Sets the PWM output to be a continuous high signal while enabled.
+   *
+   */
+  void SetAlwaysHighMode();
 
   int GetChannel() const;
 
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticHub.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticHub.h
index f876b7a..b69b3d5 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticHub.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticHub.h
@@ -46,9 +46,10 @@
    * and will turn off when the pressure reaches {@code maxPressure}.
    *
    * @param minPressure The minimum pressure. The compressor will turn on when
-   * the pressure drops below this value.
+   * the pressure drops below this value. Range 0 - 120 PSI.
    * @param maxPressure The maximum pressure. The compressor will turn off when
-   * the pressure reaches this value.
+   * the pressure reaches this value. Range 0 - 120 PSI. Must be larger then
+   * minPressure.
    */
   void EnableCompressorAnalog(
       units::pounds_per_square_inch_t minPressure,
@@ -74,10 +75,11 @@
    *
    * @param minPressure The minimum pressure. The compressor will turn on when
    * the pressure drops below this value and the pressure switch indicates that
-   * the system is not full.
+   * the system is not full.  Range 0 - 120 PSI.
    * @param maxPressure The maximum pressure. The compressor will turn off when
    * the pressure reaches this value or the pressure switch is disconnected or
-   * indicates that the system is full.
+   * indicates that the system is full. Range 0 - 120 PSI. Must be larger then
+   * minPressure.
    */
   void EnableCompressorHybrid(
       units::pounds_per_square_inch_t minPressure,
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticsBase.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticsBase.h
index b455dc6..59f899f 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticsBase.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticsBase.h
@@ -117,15 +117,15 @@
   /**
    * Sets solenoids on a pneumatics module.
    *
-   * @param mask mask
-   * @param values values
+   * @param mask bitmask to set
+   * @param values solenoid values
    */
   virtual void SetSolenoids(int mask, int values) = 0;
 
   /**
    * Gets a bitmask of solenoid values.
    *
-   * @return values
+   * @return solenoid values
    */
   virtual int GetSolenoids() const = 0;
 
@@ -183,8 +183,16 @@
    */
   virtual void UnreserveSolenoids(int mask) = 0;
 
+  /**
+   * Reserve the compressor.
+   *
+   * @return true if successful; false if compressor already reserved
+   */
   virtual bool ReserveCompressor() = 0;
 
+  /**
+   * Unreserve the compressor.
+   */
   virtual void UnreserveCompressor() = 0;
 
   /**
@@ -212,9 +220,29 @@
    */
   virtual units::pounds_per_square_inch_t GetPressure(int channel) const = 0;
 
+  /**
+   * Create a solenoid object for the specified channel.
+   *
+   * @param channel solenoid channel
+   * @return Solenoid object
+   */
   virtual Solenoid MakeSolenoid(int channel) = 0;
+
+  /**
+   * Create a double solenoid object for the specified channels.
+   *
+   * @param forwardChannel solenoid channel for forward
+   * @param reverseChannel solenoid channel for reverse
+   * @return DoubleSolenoid object
+   */
   virtual DoubleSolenoid MakeDoubleSolenoid(int forwardChannel,
                                             int reverseChannel) = 0;
+
+  /**
+   * Create a compressor object.
+   *
+   * @return Compressor object
+   */
   virtual Compressor MakeCompressor() = 0;
 
   /**
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticsControlModule.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticsControlModule.h
index acad5a1..3dd6d3a 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticsControlModule.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/PneumaticsControlModule.h
@@ -118,7 +118,7 @@
   /**
    * Returns whether the compressor has been disconnected since sticky faults
    * were last cleared. This fault is persistent and can be cleared by
-   * ClearAllStickyFaults()}
+   * ClearAllStickyFaults()
    *
    * @return True if the compressor has been disconnected since sticky faults
    * were last cleared, otherwise false.
@@ -126,7 +126,22 @@
    */
   bool GetCompressorNotConnectedStickyFault() const;
 
+  /**
+   * Returns whether the solenoid is currently reporting a voltage fault.
+   *
+   * @return True if solenoid is reporting a fault, otherwise false.
+   * @see GetSolenoidVoltageStickyFault()
+   */
   bool GetSolenoidVoltageFault() const;
+
+  /**
+   * Returns whether the solenoid has reported a voltage fault since sticky
+   * faults were last cleared. This fault is persistent and can be cleared by
+   * ClearAllStickyFaults()
+   *
+   * @return True if solenoid is reporting a fault, otherwise false.
+   * @see GetSolenoidVoltageFault()
+   */
   bool GetSolenoidVoltageStickyFault() const;
 
   /** Clears all sticky faults on this device. */
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Resource.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Resource.h
index 4109cc4..6d9dfa5 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Resource.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Resource.h
@@ -17,7 +17,7 @@
 /**
  * The Resource class is a convenient way to track allocated resources.
  *
- * It tracks them as indicies in the range [0 .. elements - 1]. E.g. the library
+ * It tracks them as indices in the range [0 .. elements - 1]. E.g. the library
  * uses this to track hardware channel allocation.
  *
  * The Resource class does not allocate the hardware channels or other
@@ -46,7 +46,7 @@
    * Allocate storage for a new instance of Resource.
    *
    * Allocate a bool array of values that will get initialized to indicate that
-   * no resources have been allocated yet. The indicies of the resources are
+   * no resources have been allocated yet. The indices of the resources are
    * [0 .. elements - 1].
    */
   explicit Resource(uint32_t size);
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/RobotBase.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/RobotBase.h
index 3ff4617..504ac81 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/RobotBase.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/RobotBase.h
@@ -115,28 +115,28 @@
 }
 
 /**
- * Implement a Robot Program framework.
+ * Implement a Robot Program framework. The RobotBase class is intended to be
+ * subclassed to create a robot program. The user must implement
+ * StartCompetition() which will be called once and is not expected to exit. The
+ * user must also implement EndCompetition(), which signals to the code in
+ * StartCompetition() that it should exit.
  *
- * The RobotBase class is intended to be subclassed by a user creating a robot
- * program. Overridden Autonomous() and OperatorControl() methods are called at
- * the appropriate time as the match proceeds. In the current implementation,
- * the Autonomous code will run to completion before the OperatorControl code
- * could start. In the future the Autonomous code might be spawned as a task,
- * then killed at the end of the Autonomous period.
+ * It is not recommended to subclass this class directly - instead subclass
+ * IterativeRobotBase or TimedRobot.
  */
 class RobotBase {
  public:
   /**
    * Determine if the Robot is currently enabled.
    *
-   * @return True if the Robot is currently enabled by the field controls.
+   * @return True if the Robot is currently enabled by the Driver Station.
    */
   bool IsEnabled() const;
 
   /**
    * Determine if the Robot is currently disabled.
    *
-   * @return True if the Robot is currently disabled by the field controls.
+   * @return True if the Robot is currently disabled by the Driver Station.
    */
   bool IsDisabled() const;
 
@@ -144,7 +144,7 @@
    * Determine if the robot is currently in Autonomous mode.
    *
    * @return True if the robot is currently operating Autonomously as determined
-   *         by the field controls.
+   *         by the Driver Station.
    */
   bool IsAutonomous() const;
 
@@ -152,7 +152,7 @@
    * Determine if the robot is currently in Autonomous mode and enabled.
    *
    * @return True if the robot us currently operating Autonomously while enabled
-   * as determined by the field controls.
+   * as determined by the Driver Station.
    */
   bool IsAutonomousEnabled() const;
 
@@ -160,7 +160,7 @@
    * Determine if the robot is currently in Operator Control mode.
    *
    * @return True if the robot is currently operating in Tele-Op mode as
-   *         determined by the field controls.
+   *         determined by the Driver Station.
    */
   bool IsTeleop() const;
 
@@ -168,25 +168,38 @@
    * Determine if the robot is current in Operator Control mode and enabled.
    *
    * @return True if the robot is currently operating in Tele-Op mode while
-   * wnabled as determined by the field-controls.
+   * enabled as determined by the Driver Station.
    */
   bool IsTeleopEnabled() const;
 
   /**
    * Determine if the robot is currently in Test mode.
    *
-   * @return True if the robot is currently running tests as determined by the
-   *         field controls.
+   * @return True if the robot is currently running in Test mode as determined
+   * by the Driver Station.
    */
   bool IsTest() const;
 
   /**
+   * Determine if the robot is current in Test mode and enabled.
+   *
+   * @return True if the robot is currently operating in Test mode while
+   * enabled as determined by the Driver Station.
+   */
+  bool IsTestEnabled() const;
+
+  /**
    * Gets the ID of the main robot thread.
    */
   static std::thread::id GetThreadId();
 
+  /**
+   * Start the main robot code. This function will be called once and should not
+   * exit until signalled by EndCompetition()
+   */
   virtual void StartCompetition() = 0;
 
+  /** Ends the main loop in StartCompetition(). */
   virtual void EndCompetition() = 0;
 
   /**
@@ -215,13 +228,17 @@
    * @return If the robot is running in simulation.
    */
   static constexpr bool IsSimulation() {
-    return !IsReal();
+#ifdef __FRC_ROBORIO__
+    return false;
+#else
+    return true;
+#endif
   }
 
   /**
    * Constructor for a generic robot program.
    *
-   * User code should be placed in the constructor that runs before the
+   * User code can be placed in the constructor that runs before the
    * Autonomous or Operator Control period starts. The constructor will run to
    * completion before Autonomous is entered.
    *
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/RobotController.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/RobotController.h
index c020d34..bf65a8c 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/RobotController.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/RobotController.h
@@ -8,6 +8,7 @@
 
 #include <string>
 
+#include <units/temperature.h>
 #include <units/voltage.h>
 
 namespace frc {
@@ -63,6 +64,13 @@
   static std::string GetComments();
 
   /**
+   * Returns the team number configured for the robot controller.
+   *
+   * @return team number, or 0 if not found.
+   */
+  static int32_t GetTeamNumber();
+
+  /**
    * Read the microsecond-resolution timer on the FPGA.
    *
    * @return The current time in microseconds according to the FPGA (since FPGA
@@ -102,6 +110,19 @@
   static bool IsBrownedOut();
 
   /**
+   * Gets the current state of the Robot Signal Light (RSL)
+   * @return The current state of the RSL- true if on, false if off
+   */
+  static bool GetRSLState();
+
+  /**
+   * Gets if the system time is valid.
+   *
+   * @return True if the system time is valid, false otherwise
+   */
+  static bool IsSystemTimeValid();
+
+  /**
    * Get the input voltage to the robot controller.
    *
    * @return The controller input voltage value in Volts
@@ -130,9 +151,16 @@
   static double GetCurrent3V3();
 
   /**
-   * Get the enabled state of the 3.3V rail. The rail may be disabled due to a
-   * controller brownout, a short circuit on the rail, or controller
-   * over-voltage.
+   * Enables or disables the 3.3V rail.
+   *
+   * @param enabled whether to enable the 3.3V rail.
+   */
+  static void SetEnabled3V3(bool enabled);
+
+  /**
+   * Get the enabled state of the 3.3V rail. The rail may be disabled due to
+   * calling SetEnabled3V3(), a controller brownout, a short circuit on the
+   * rail, or controller over-voltage.
    *
    * @return The controller 3.3V rail enabled value. True for enabled.
    */
@@ -161,9 +189,16 @@
   static double GetCurrent5V();
 
   /**
-   * Get the enabled state of the 5V rail. The rail may be disabled due to a
-   * controller brownout, a short circuit on the rail, or controller
-   * over-voltage.
+   * Enables or disables the 5V rail.
+   *
+   * @param enabled whether to enable the 5V rail.
+   */
+  static void SetEnabled5V(bool enabled);
+
+  /**
+   * Get the enabled state of the 5V rail. The rail may be disabled due to
+   * calling SetEnabled5V(), a controller brownout, a short circuit on the rail,
+   * or controller over-voltage.
    *
    * @return The controller 5V rail enabled value. True for enabled.
    */
@@ -192,9 +227,16 @@
   static double GetCurrent6V();
 
   /**
-   * Get the enabled state of the 6V rail. The rail may be disabled due to a
-   * controller brownout, a short circuit on the rail, or controller
-   * over-voltage.
+   * Enables or disables the 6V rail.
+   *
+   * @param enabled whether to enable the 6V rail.
+   */
+  static void SetEnabled6V(bool enabled);
+
+  /**
+   * Get the enabled state of the 6V rail. The rail may be disabled due to
+   * calling SetEnabled6V(), a controller brownout, a short circuit on the rail,
+   * or controller over-voltage.
    *
    * @return The controller 6V rail enabled value. True for enabled.
    */
@@ -226,6 +268,13 @@
   static void SetBrownoutVoltage(units::volt_t brownoutVoltage);
 
   /**
+   * Get the current CPU temperature.
+   *
+   * @return current CPU temperature
+   */
+  static units::celsius_t GetCPUTemp();
+
+  /**
    * Get the current status of the CAN bus.
    *
    * @return The status of the CAN bus
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/SPI.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/SPI.h
index 4063550..46061d4 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/SPI.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/SPI.h
@@ -11,7 +11,6 @@
 
 #include <hal/SPITypes.h>
 #include <units/time.h>
-#include <wpi/deprecated.h>
 
 namespace frc {
 
@@ -28,10 +27,12 @@
  public:
   enum Port { kOnboardCS0 = 0, kOnboardCS1, kOnboardCS2, kOnboardCS3, kMXP };
   enum Mode {
-    kMode0 = HAL_SPI_kMode0,
-    kMode1 = HAL_SPI_kMode1,
-    kMode2 = HAL_SPI_kMode2,
-    kMode3 = HAL_SPI_kMode3
+    kMode0 = HAL_SPI_kMode0, /*!< Clock idle low, data sampled on rising edge */
+    kMode1 =
+        HAL_SPI_kMode1, /*!< Clock idle low, data sampled on falling edge */
+    kMode2 =
+        HAL_SPI_kMode2, /*!< Clock idle high, data sampled on falling edge */
+    kMode3 = HAL_SPI_kMode3 /*!< Clock idle high, data sampled on rising edge */
   };
 
   /**
@@ -59,60 +60,6 @@
   void SetClockRate(int hz);
 
   /**
-   * Configure the order that bits are sent and received on the wire
-   * to be most significant bit first.
-   *
-   * @deprecated Does not work, will be removed.
-   */
-  WPI_DEPRECATED("Not supported by roboRIO.")
-  void SetMSBFirst();
-
-  /**
-   * Configure the order that bits are sent and received on the wire
-   * to be least significant bit first.
-   *
-   * @deprecated Does not work, will be removed.
-   */
-  WPI_DEPRECATED("Not supported by roboRIO.")
-  void SetLSBFirst();
-
-  /**
-   * Configure that the data is stable on the leading edge and the data
-   * changes on the trailing edge.
-   *
-   * @deprecated Use SetMode() instead.
-   */
-  WPI_DEPRECATED("Use SetMode() instead")
-  void SetSampleDataOnLeadingEdge();
-
-  /**
-   * Configure that the data is stable on the trailing edge and the data
-   * changes on the leading edge.
-   *
-   * @deprecated Use SetMode() instead.
-   */
-  WPI_DEPRECATED("Use SetMode() instead")
-  void SetSampleDataOnTrailingEdge();
-
-  /**
-   * Configure the clock output line to be active low.
-   * This is sometimes called clock polarity high or clock idle high.
-   *
-   * @deprecated Use SetMode() instead.
-   */
-  WPI_DEPRECATED("Use SetMode() instead")
-  void SetClockActiveLow();
-
-  /**
-   * Configure the clock output line to be active high.
-   * This is sometimes called clock polarity low or clock idle low.
-   *
-   * @deprecated Use SetMode() instead.
-   */
-  WPI_DEPRECATED("Use SetMode() instead")
-  void SetClockActiveHigh();
-
-  /**
    * Sets the mode for the SPI device.
    *
    * <p>Mode 0 is Clock idle low, data sampled on rising edge
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/SerialPort.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/SerialPort.h
index 7fff14a..dcc0a2e 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/SerialPort.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/SerialPort.h
@@ -16,7 +16,7 @@
  * Driver for the RS-232 serial port on the roboRIO.
  *
  * The current implementation uses the VISA formatted I/O mode.  This means that
- * all traffic goes through the fomatted buffers.  This allows the intermingled
+ * all traffic goes through the formatted buffers.  This allows the intermingled
  * use of Printf(), Scanf(), and the raw buffer accessors Read() and Write().
  *
  * More information can be found in the NI-VISA User Manual here:
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Servo.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Servo.h
index 96cbfa4..9383641 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Servo.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Servo.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <units/angle.h>
+
 #include "frc/PWM.h"
 
 namespace frc {
@@ -98,11 +100,11 @@
  private:
   double GetServoAngleRange() const;
 
-  static constexpr double kMaxServoAngle = 180.0;
+  static constexpr double kMaxServoAngle = 180.;
   static constexpr double kMinServoAngle = 0.0;
 
-  static constexpr double kDefaultMaxServoPWM = 2.4;
-  static constexpr double kDefaultMinServoPWM = 0.6;
+  static constexpr units::millisecond_t kDefaultMaxServoPWM = 2.4_ms;
+  static constexpr units::millisecond_t kDefaultMinServoPWM = 0.6_ms;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Solenoid.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Solenoid.h
index a09ffc0..7fa4953 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Solenoid.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Solenoid.h
@@ -87,18 +87,22 @@
   bool IsDisabled() const;
 
   /**
-   * Set the pulse duration in the PCM. This is used in conjunction with
-   * the startPulse method to allow the PCM to control the timing of a pulse.
-   * The timing can be controlled in 0.01 second increments.
+   * Set the pulse duration in the pneumatics module. This is used in
+   * conjunction with the startPulse method to allow the pneumatics module to
+   * control the timing of a pulse.
    *
-   * @param duration The duration of the pulse, from 0.01 to 2.55 seconds.
+   * On the PCM, the timing can be controlled in 0.01 second increments, with a
+   * maximum of 2.55 seconds. On the PH, the timing can be controlled in 0.001
+   * second increments, with a maximum of 65.534 seconds.
+   *
+   * @param duration The duration of the pulse.
    *
    * @see startPulse()
    */
   void SetPulseDuration(units::second_t duration);
 
   /**
-   * %Trigger the PCM to generate a pulse of the duration set in
+   * %Trigger the pneumatics module to generate a pulse of the duration set in
    * setPulseDuration.
    *
    * @see setPulseDuration()
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Timer.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Timer.h
index 14674ee..e163982 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/Timer.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/Timer.h
@@ -77,6 +77,14 @@
   void Start();
 
   /**
+   * Restart the timer by stopping the timer, if it is not already stopped,
+   * resetting the accumulated time, then starting the timer again. If you
+   * want an event to periodically reoccur at some time interval from the
+   * start time, consider using AdvanceIfElapsed() instead.
+   */
+  void Restart();
+
+  /**
    * Stop the timer.
    *
    * This computes the time as of now and clears the running flag, causing all
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/XboxController.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/XboxController.h
index 370e46e..3caba1a 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/XboxController.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/XboxController.h
@@ -36,63 +36,87 @@
 
   /**
    * Get the X axis value of left side of the controller.
+   *
+   * @return the axis value
    */
   double GetLeftX() const;
 
   /**
    * Get the X axis value of right side of the controller.
+   *
+   * @return the axis value
    */
   double GetRightX() const;
 
   /**
    * Get the Y axis value of left side of the controller.
+   *
+   * @return the axis value
    */
   double GetLeftY() const;
 
   /**
    * Get the Y axis value of right side of the controller.
+   *
+   * @return the axis value
    */
   double GetRightY() const;
 
   /**
    * Get the left trigger (LT) axis value of the controller. Note that this axis
    * is bound to the range of [0, 1] as opposed to the usual [-1, 1].
+   *
+   * @return the axis value
    */
   double GetLeftTriggerAxis() const;
 
   /**
    * Get the right trigger (RT) axis value of the controller. Note that this
    * axis is bound to the range of [0, 1] as opposed to the usual [-1, 1].
+   *
+   * @return the axis value
    */
   double GetRightTriggerAxis() const;
 
   /**
    * Read the value of the left bumper (LB) button on the controller.
+   *
+   * @return the state of the button
    */
   bool GetLeftBumper() const;
 
   /**
    * Read the value of the right bumper (RB) button on the controller.
+   *
+   * @return the state of the button
    */
   bool GetRightBumper() const;
 
   /**
    * Whether the left bumper (LB) was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check
    */
   bool GetLeftBumperPressed();
 
   /**
    * Whether the right bumper (RB) was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check
    */
   bool GetRightBumperPressed();
 
   /**
    * Whether the left bumper (LB) was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
    */
   bool GetLeftBumperReleased();
 
   /**
    * Whether the right bumper (RB) was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
    */
   bool GetRightBumperReleased();
 
@@ -116,31 +140,43 @@
 
   /**
    * Read the value of the left stick button (LSB) on the controller.
+   *
+   * @return the state of the button
    */
   bool GetLeftStickButton() const;
 
   /**
    * Read the value of the right stick button (RSB) on the controller.
+   *
+   * @return the state of the button
    */
   bool GetRightStickButton() const;
 
   /**
    * Whether the left stick button (LSB) was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
    */
   bool GetLeftStickButtonPressed();
 
   /**
    * Whether the right stick button (RSB) was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check
    */
   bool GetRightStickButtonPressed();
 
   /**
    * Whether the left stick button (LSB) was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
    */
   bool GetLeftStickButtonReleased();
 
   /**
    * Whether the right stick button (RSB) was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
    */
   bool GetRightStickButtonReleased();
 
@@ -283,9 +319,9 @@
   BooleanEvent Y(EventLoop* loop) const;
 
   /**
-   * Whether the Y button was released since the last check.
+   * Read the value of the back button on the controller.
    *
-   * @return Whether the button was released since the last check.
+   * @return The state of the button.
    */
   bool GetBackButton() const;
 
@@ -359,7 +395,7 @@
    * Constructs an event instance around the axis value of the left trigger.
    * The returned trigger will be true when the axis value is greater than 0.5.
    * @param loop the event loop instance to attach the event to.
-   * @return an event instance that is true when the right trigger's axis
+   * @return an event instance that is true when the left trigger's axis
    * exceeds 0.5, attached to the given event loop
    */
   BooleanEvent LeftTrigger(EventLoop* loop) const;
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/event/BooleanEvent.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/event/BooleanEvent.h
index 745a53c..7c19c90 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/event/BooleanEvent.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/event/BooleanEvent.h
@@ -40,9 +40,10 @@
   BooleanEvent(EventLoop* loop, std::function<bool()> condition);
 
   /**
-   * Check whether this event is active or not.
+   * Check whether this event is active or not as of the last loop poll.
    *
-   * @return true if active.
+   * @return true if active, false if not active. If the event was never polled,
+   * it returns the state at event construction.
    */
   bool GetAsBoolean() const;
 
@@ -69,7 +70,7 @@
                [](EventLoop* loop, std::function<bool()> condition) {
                  return T(loop, condition);
                }) {
-    return ctor(m_loop, m_condition);
+    return ctor(m_loop, [state = m_state] { return *state; });
   }
 
   /**
@@ -84,7 +85,8 @@
    * Composes this event with another event, returning a new event that is
    * active when both events are active.
    *
-   * <p>The new event will use this event's polling loop.
+   * <p>The events must use the same event loop. If the events use different
+   * event loops, the composed signal won't update until both loops are polled.
    *
    * @param rhs the event to compose with
    * @return the event that is active when both events are active
@@ -95,7 +97,8 @@
    * Composes this event with another event, returning a new event that is
    * active when either event is active.
    *
-   * <p>The new event will use this event's polling loop.
+   * <p>The events must use the same event loop. If the events use different
+   * event loops, the composed signal won't update until both loops are polled.
    *
    * @param rhs the event to compose with
    * @return the event that is active when either event is active
@@ -131,5 +134,6 @@
  private:
   EventLoop* m_loop;
   std::function<bool()> m_condition;
+  std::shared_ptr<bool> m_state;  // A programmer's worst nightmare.
 };
 }  // namespace frc
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/event/EventLoop.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/event/EventLoop.h
index d18fac3..224dd3b 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/event/EventLoop.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/event/EventLoop.h
@@ -10,8 +10,8 @@
 #include <wpi/FunctionExtras.h>
 
 namespace frc {
-/** The loop polling BooleanEvent objects and executing the actions bound to
- * them. */
+/** A declarative way to bind a set of actions to a loop and execute them when
+ * the loop is polled. */
 class EventLoop {
  public:
   EventLoop();
@@ -20,7 +20,7 @@
   EventLoop& operator=(const EventLoop&) = delete;
 
   /**
-   * Bind a new action to run.
+   * Bind a new action to run when the loop is polled.
    *
    * @param action the action to run.
    */
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h
index c95466a..77f1d5b 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h
@@ -8,8 +8,11 @@
 
 /**
  * Interface for 3-axis accelerometers.
+ *
+ * @deprecated This interface is being removed with no replacement.
  */
-class Accelerometer {
+class [[deprecated(
+    "This interface is being removed with no replacement.")]] Accelerometer {
  public:
   Accelerometer() = default;
   virtual ~Accelerometer() = default;
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/interfaces/Gyro.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/interfaces/Gyro.h
index b74a3cf..51fea7d 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/interfaces/Gyro.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/interfaces/Gyro.h
@@ -12,8 +12,11 @@
 
 /**
  * Interface for yaw rate gyros.
+ *
+ * @deprecated This interface is being removed with no replacement.
  */
-class Gyro {
+class [[deprecated(
+    "This interface is being removed with no replacement.")]] Gyro {
  public:
   Gyro() = default;
   virtual ~Gyro() = default;
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AddressableLEDSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AddressableLEDSim.h
index 4533086..2a5fcae 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AddressableLEDSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AddressableLEDSim.h
@@ -60,7 +60,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object storing this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -85,7 +86,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterOutputPortCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterOutputPortCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -110,8 +112,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterLengthCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterLengthCallback(NotifyCallback callback,
+                                                        bool initialNotify);
 
   /**
    * Get the length of the LED strip.
@@ -135,7 +138,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterRunningCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterRunningCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -160,7 +164,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterDataCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterDataCallback(
       ConstBufferCallback callback, bool initialNotify);
 
   /**
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogGyroSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogGyroSim.h
index faa11d7..0210818 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogGyroSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogGyroSim.h
@@ -40,8 +40,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterAngleCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAngleCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Get the current angle of the gyro.
@@ -64,8 +65,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterRateCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterRateCallback(NotifyCallback callback,
+                                                      bool initialNotify);
 
   /**
    * Get the rate of angle change on this gyro.
@@ -89,7 +91,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogInputSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogInputSim.h
index 03d7548..05c9898 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogInputSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogInputSim.h
@@ -41,7 +41,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -66,7 +67,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterAverageBitsCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAverageBitsCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -91,7 +93,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterOversampleBitsCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterOversampleBitsCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -116,7 +119,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterVoltageCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterVoltageCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -141,9 +145,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterAccumulatorInitializedCallback(NotifyCallback callback,
-                                         bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAccumulatorInitializedCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check if the accumulator has been initialized.
@@ -167,7 +171,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterAccumulatorValueCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAccumulatorValueCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -192,7 +197,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterAccumulatorCountCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAccumulatorCountCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -217,9 +223,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterAccumulatorCenterCallback(NotifyCallback callback,
-                                    bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAccumulatorCenterCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the accumulator center.
@@ -243,9 +249,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterAccumulatorDeadbandCallback(NotifyCallback callback,
-                                      bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAccumulatorDeadbandCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the accumulator deadband.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogOutputSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogOutputSim.h
index ffec03a..42eb6c1 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogOutputSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogOutputSim.h
@@ -40,7 +40,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterVoltageCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterVoltageCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -64,7 +65,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogTriggerSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogTriggerSim.h
index 04e9e9b..019a9a9 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogTriggerSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/AnalogTriggerSim.h
@@ -53,7 +53,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -78,9 +79,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterTriggerLowerBoundCallback(NotifyCallback callback,
-                                    bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterTriggerLowerBoundCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the lower bound.
@@ -104,9 +105,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterTriggerUpperBoundCallback(NotifyCallback callback,
-                                    bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterTriggerUpperBoundCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the upper bound.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/BuiltInAccelerometerSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/BuiltInAccelerometerSim.h
index 9ffcf5b..74a4f16 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/BuiltInAccelerometerSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/BuiltInAccelerometerSim.h
@@ -40,8 +40,9 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterActiveCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterActiveCallback(NotifyCallback callback,
+                                                        bool initialNotify);
 
   /**
    * Check whether the accelerometer is active.
@@ -64,8 +65,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterRangeCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterRangeCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Check the range of this accelerometer.
@@ -88,8 +90,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterXCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterXCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the X axis value.
@@ -112,8 +115,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterYCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterYCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the Y axis value.
@@ -136,8 +140,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterZCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterZCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the Z axis value.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/CTREPCMSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/CTREPCMSim.h
index 96959ed..37113db 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/CTREPCMSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/CTREPCMSim.h
@@ -33,21 +33,24 @@
 
   ~CTREPCMSim() override = default;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   bool GetInitialized() const override;
 
   void SetInitialized(bool initialized) override;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
       int channel, NotifyCallback callback, bool initialNotify) override;
 
   bool GetSolenoidOutput(int channel) const override;
 
   void SetSolenoidOutput(int channel, bool solenoidOutput) override;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   bool GetCompressorOn() const override;
@@ -62,9 +65,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterClosedLoopEnabledCallback(NotifyCallback callback,
-                                    bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterClosedLoopEnabledCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check whether the closed loop compressor control is active.
@@ -88,7 +91,8 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   /**
@@ -112,9 +116,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterCompressorCurrentCallback(NotifyCallback callback,
-                                    bool initialNotify) override;
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCompressorCurrentCallback(
+      NotifyCallback callback, bool initialNotify) override;
 
   /**
    * Read the compressor current.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DCMotorSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DCMotorSim.h
index b1388bd..be3a325 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DCMotorSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DCMotorSim.h
@@ -21,7 +21,9 @@
   /**
    * Creates a simulated DC motor mechanism.
    *
-   * @param plant              The linear system representing the DC motor.
+   * @param plant              The linear system representing the DC motor. This
+   *                           system can be created with
+   *                           LinearSystemId::DCMotorSystem().
    * @param gearbox            The type of and number of motors in the DC motor
    * gearbox.
    * @param gearing            The gearing of the DC motor (numbers greater than
@@ -46,6 +48,17 @@
              units::kilogram_square_meter_t moi,
              const std::array<double, 2>& measurementStdDevs = {0.0, 0.0});
 
+  using LinearSystemSim::SetState;
+
+  /**
+   * Sets the state of the DC motor.
+   *
+   * @param angularPosition The new position
+   * @param angularVelocity The new velocity
+   */
+  void SetState(units::radian_t angularPosition,
+                units::radians_per_second_t angularVelocity);
+
   /**
    * Returns the DC motor position.
    *
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DIOSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DIOSim.h
index 9bbd3fb..5264c02 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DIOSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DIOSim.h
@@ -48,7 +48,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -73,8 +74,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterValueCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterValueCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Read the value of the DIO port.
@@ -97,7 +99,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPulseLengthCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPulseLengthCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -122,7 +125,8 @@
    *                      initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterIsInputCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterIsInputCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -147,7 +151,8 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterFilterIndexCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterFilterIndexCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h
index c1cc1d7..78310d7 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h
@@ -19,7 +19,7 @@
 class DifferentialDrivetrainSim {
  public:
   /**
-   * Create a SimDrivetrain.
+   * Creates a simulated differential drivetrain.
    *
    * @param plant The LinearSystem representing the robot's drivetrain. This
    *              system can be created with
@@ -46,7 +46,7 @@
       const std::array<double, 7>& measurementStdDevs = {});
 
   /**
-   * Create a SimDrivetrain.
+   * Creates a simulated differential drivetrain.
    *
    * @param driveMotor  A DCMotor representing the left side of the drivetrain.
    * @param gearing     The gearing on the drive between motor and wheel, as
@@ -80,7 +80,7 @@
    * @param u The input vector.
    * @return The normalized input.
    */
-  Vectord<2> ClampInput(const Vectord<2>& u);
+  Eigen::Vector2d ClampInput(const Eigen::Vector2d& u);
 
   /**
    * Sets the applied voltage to the drivetrain. Note that positive voltage must
@@ -187,7 +187,7 @@
    */
   void SetPose(const frc::Pose2d& pose);
 
-  Vectord<7> Dynamics(const Vectord<7>& x, const Vectord<2>& u);
+  Vectord<7> Dynamics(const Vectord<7>& x, const Eigen::Vector2d& u);
 
   class State {
    public:
@@ -325,7 +325,7 @@
   double m_currentGearing;
 
   Vectord<7> m_x;
-  Vectord<2> m_u;
+  Eigen::Vector2d m_u;
   Vectord<7> m_y;
   std::array<double, 7> m_measurementStdDevs;
 };
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DigitalPWMSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DigitalPWMSim.h
index 53caee7..f9b974d 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DigitalPWMSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DigitalPWMSim.h
@@ -54,7 +54,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -78,7 +79,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterDutyCycleCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterDutyCycleCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -102,8 +104,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPinCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPinCallback(NotifyCallback callback,
+                                                     bool initialNotify);
 
   /**
    * Check the pin number.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DriverStationSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DriverStationSim.h
index 232c123..acc510c 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DriverStationSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DriverStationSim.h
@@ -26,7 +26,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore> RegisterEnabledCallback(
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterEnabledCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -51,8 +52,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterAutonomousCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterAutonomousCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check if the DS is in autonomous.
@@ -76,7 +78,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore> RegisterTestCallback(
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterTestCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -101,7 +104,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore> RegisterEStopCallback(
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterEStopCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -126,8 +130,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterFmsAttachedCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterFmsAttachedCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check if the FMS is connected.
@@ -151,8 +156,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterDsAttachedCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterDsAttachedCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check if the DS is attached.
@@ -176,9 +182,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterAllianceStationIdCallback(NotifyCallback callback,
-                                    bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterAllianceStationIdCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the alliance station ID (color + number).
@@ -202,7 +208,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore> RegisterMatchTimeCallback(
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterMatchTimeCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DutyCycleEncoderSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DutyCycleEncoderSim.h
index 19577bb..18c04f0 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DutyCycleEncoderSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DutyCycleEncoderSim.h
@@ -26,20 +26,82 @@
   explicit DutyCycleEncoderSim(const DutyCycleEncoder& encoder);
 
   /**
-   * Set the position tin turns.
+   * Constructs from a digital input channel.
+   *
+   * @param channel digital input channel
+   */
+  explicit DutyCycleEncoderSim(int channel);
+
+  /**
+   * Get the position in turns.
+   *
+   * @return The position.
+   */
+  double Get();
+
+  /**
+   * Set the position in turns.
    *
    * @param turns The position.
    */
   void Set(units::turn_t turns);
 
   /**
-   * Set the position.
+   * Get the distance.
+   *
+   * @return The distance.
+   */
+
+  double GetDistance();
+
+  /**
+   * Set the distance.
+   *
+   * @param distance The distance.
    */
   void SetDistance(double distance);
 
+  /**
+   * Get the absolute position.
+   *
+   * @return The absolute position
+   */
+  double GetAbsolutePosition();
+
+  /**
+   * Set the absolute position.
+   *
+   * @param position The absolute position
+   */
+  void SetAbsolutePosition(double position);
+
+  /**
+   * Get the distance per rotation for this encoder.
+   *
+   * @return The scale factor that will be used to convert rotation to useful
+   * units.
+   */
+  double GetDistancePerRotation();
+
+  /**
+   * Get if the encoder is connected.
+   *
+   * @return true if the encoder is connected.
+   */
+  bool IsConnected();
+
+  /**
+   * Set if the encoder is connected.
+   *
+   * @param isConnected Whether or not the sensor is connected.
+   */
+  void SetConnected(bool isConnected);
+
  private:
   hal::SimDouble m_simPosition;
   hal::SimDouble m_simDistancePerRotation;
+  hal::SimDouble m_simAbsolutePosition;
+  hal::SimBoolean m_simIsConnected;
 };
 
 }  // namespace sim
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DutyCycleSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DutyCycleSim.h
index ec7b48c..f6f89eb 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DutyCycleSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/DutyCycleSim.h
@@ -51,7 +51,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -75,7 +76,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterFrequencyCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterFrequencyCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -99,8 +101,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterOutputCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterOutputCallback(NotifyCallback callback,
+                                                        bool initialNotify);
 
   /**
    * Measure the output from this duty cycle port.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/ElevatorSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/ElevatorSim.h
index cf40810..3d5c433 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/ElevatorSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/ElevatorSim.h
@@ -19,25 +19,32 @@
  */
 class ElevatorSim : public LinearSystemSim<2, 1, 1> {
  public:
+  template <typename Distance>
+  using Velocity_t = units::unit_t<
+      units::compound_unit<Distance, units::inverse<units::seconds>>>;
+
+  template <typename Distance>
+  using Acceleration_t = units::unit_t<units::compound_unit<
+      units::compound_unit<Distance, units::inverse<units::seconds>>,
+      units::inverse<units::seconds>>>;
+
   /**
    * Constructs a simulated elevator mechanism.
    *
    * @param plant              The linear system that represents the elevator.
+   *                           This system can be created with
+   *                           LinearSystemId::ElevatorSystem().
    * @param gearbox            The type of and number of motors in your
    *                           elevator gearbox.
-   * @param gearing            The gearing of the elevator (numbers greater
-   *                           than 1 represent reductions).
-   * @param drumRadius         The radius of the drum that your cable is
-   *                           wrapped around.
    * @param minHeight          The minimum allowed height of the elevator.
    * @param maxHeight          The maximum allowed height of the elevator.
    * @param simulateGravity    Whether gravity should be simulated or not.
+   * @param startingHeight     The starting height of the elevator.
    * @param measurementStdDevs The standard deviation of the measurements.
    */
   ElevatorSim(const LinearSystem<2, 1, 1>& plant, const DCMotor& gearbox,
-              double gearing, units::meter_t drumRadius,
               units::meter_t minHeight, units::meter_t maxHeight,
-              bool simulateGravity,
+              bool simulateGravity, units::meter_t startingHeight,
               const std::array<double, 1>& measurementStdDevs = {0.0});
 
   /**
@@ -53,15 +60,48 @@
    * @param minHeight          The minimum allowed height of the elevator.
    * @param maxHeight          The maximum allowed height of the elevator.
    * @param simulateGravity    Whether gravity should be simulated or not.
+   * @param startingHeight     The starting height of the elevator.
    * @param measurementStdDevs The standard deviation of the measurements.
    */
   ElevatorSim(const DCMotor& gearbox, double gearing,
               units::kilogram_t carriageMass, units::meter_t drumRadius,
               units::meter_t minHeight, units::meter_t maxHeight,
-              bool simulateGravity,
+              bool simulateGravity, units::meter_t startingHeight,
               const std::array<double, 1>& measurementStdDevs = {0.0});
 
   /**
+   * Constructs a simulated elevator mechanism.
+   *
+   * @param kV                 The velocity gain.
+   * @param kA                 The acceleration gain.
+   * @param gearbox            The type of and number of motors in your
+   *                           elevator gearbox.
+   * @param minHeight          The minimum allowed height of the elevator.
+   * @param maxHeight          The maximum allowed height of the elevator.
+   * @param simulateGravity    Whether gravity should be simulated or not.
+   * @param startingHeight     The starting height of the elevator.
+   * @param measurementStdDevs The standard deviation of the measurements.
+   */
+  template <typename Distance>
+    requires std::same_as<units::meter, Distance> ||
+             std::same_as<units::radian, Distance>
+  ElevatorSim(decltype(1_V / Velocity_t<Distance>(1)) kV,
+              decltype(1_V / Acceleration_t<Distance>(1)) kA,
+              const DCMotor& gearbox, units::meter_t minHeight,
+              units::meter_t maxHeight, bool simulateGravity,
+              units::meter_t startingHeight,
+              const std::array<double, 1>& measurementStdDevs = {0.0});
+  using LinearSystemSim::SetState;
+
+  /**
+   * Sets the elevator's state. The new position will be limited between the
+   * minimum and maximum allowed heights.
+   * @param position The new position
+   * @param velocity The new velocity
+   */
+  void SetState(units::meter_t position, units::meters_per_second_t velocity);
+
+  /**
    * Returns whether the elevator would hit the lower limit.
    *
    * @param elevatorHeight The elevator height.
@@ -132,10 +172,8 @@
 
  private:
   DCMotor m_gearbox;
-  units::meter_t m_drumRadius;
   units::meter_t m_minHeight;
   units::meter_t m_maxHeight;
-  double m_gearing;
   bool m_simulateGravity;
 };
 }  // namespace frc::sim
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/EncoderSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/EncoderSim.h
index 0d578b4..80ad146 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/EncoderSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/EncoderSim.h
@@ -53,7 +53,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -78,8 +79,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterCountCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCountCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Read the count of the encoder.
@@ -103,8 +105,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPeriodCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPeriodCallback(NotifyCallback callback,
+                                                        bool initialNotify);
 
   /**
    * Read the period of the encoder.
@@ -127,8 +130,9 @@
    * @param initialNotify whether to run the callback on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterResetCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterResetCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Check if the encoder has been reset.
@@ -152,7 +156,8 @@
    * @param initialNotify whether to run the callback on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterMaxPeriodCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterMaxPeriodCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -177,7 +182,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterDirectionCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterDirectionCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -202,7 +208,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterReverseDirectionCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterReverseDirectionCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -227,7 +234,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterSamplesToAverageCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterSamplesToAverageCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -252,7 +260,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterDistancePerPulseCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterDistancePerPulseCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -286,7 +295,7 @@
    *
    * @return the encoder distance
    */
-  double GetDistance();
+  double GetDistance() const;
 
   /**
    * Change the rate of the encoder.
@@ -300,7 +309,7 @@
    *
    * @return the rate of change
    */
-  double GetRate();
+  double GetRate() const;
 
  private:
   explicit EncoderSim(int index) : m_index{index} {}
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/FlywheelSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/FlywheelSim.h
index 9f2272c..cc0eca9 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/FlywheelSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/FlywheelSim.h
@@ -20,7 +20,9 @@
   /**
    * Creates a simulated flywheel mechanism.
    *
-   * @param plant              The linear system representing the flywheel.
+   * @param plant              The linear system representing the flywheel. This
+   *                           system can be created with
+   *                           LinearSystemId::FlywheelSystem().
    * @param gearbox            The type of and number of motors in the flywheel
    *                           gearbox.
    * @param gearing            The gearing of the flywheel (numbers greater than
@@ -45,6 +47,15 @@
               units::kilogram_square_meter_t moi,
               const std::array<double, 1>& measurementStdDevs = {0.0});
 
+  using LinearSystemSim::SetState;
+
+  /**
+   * Sets the flywheel's state.
+   *
+   * @param velocity The new velocity
+   */
+  void SetState(units::radians_per_second_t velocity);
+
   /**
    * Returns the flywheel velocity.
    *
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PS5ControllerSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PS5ControllerSim.h
new file mode 100644
index 0000000..6b9e2c7
--- /dev/null
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PS5ControllerSim.h
@@ -0,0 +1,176 @@
+// 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 "frc/simulation/GenericHIDSim.h"
+
+namespace frc {
+
+class PS5Controller;
+
+namespace sim {
+
+/**
+ * Class to control a simulated PS5 controller.
+ */
+class PS5ControllerSim : public GenericHIDSim {
+ public:
+  /**
+   * Constructs from a PS5Controller object.
+   *
+   * @param joystick controller to simulate
+   */
+  explicit PS5ControllerSim(const PS5Controller& joystick);
+
+  /**
+   * Constructs from a joystick port number.
+   *
+   * @param port port number
+   */
+  explicit PS5ControllerSim(int port);
+
+  /**
+   * Change the X axis value of the controller's left stick.
+   *
+   * @param value the new value
+   */
+  void SetLeftX(double value);
+
+  /**
+   * Change the X axis value of the controller's right stick.
+   *
+   * @param value the new value
+   */
+  void SetRightX(double value);
+
+  /**
+   * Change the Y axis value of the controller's left stick.
+   *
+   * @param value the new value
+   */
+  void SetLeftY(double value);
+
+  /**
+   * Change the Y axis value of the controller's right stick.
+   *
+   * @param value the new value
+   */
+  void SetRightY(double value);
+
+  /**
+   * Change the L2 axis axis value of the controller.
+   *
+   * @param value the new value
+   */
+  void SetL2Axis(double value);
+
+  /**
+   * Change the R2 axis value of the controller.
+   *
+   * @param value the new value
+   */
+  void SetR2Axis(double value);
+
+  /**
+   * Change the value of the Square button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetSquareButton(bool value);
+
+  /**
+   * Change the value of the Cross button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetCrossButton(bool value);
+
+  /**
+   * Change the value of the Circle button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetCircleButton(bool value);
+
+  /**
+   * Change the value of the Triangle button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetTriangleButton(bool value);
+
+  /**
+   * Change the value of the L1 button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetL1Button(bool value);
+
+  /**
+   * Change the value of the R1 button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetR1Button(bool value);
+
+  /**
+   * Change the value of the L2 button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetL2Button(bool value);
+
+  /**
+   * Change the value of the R2 button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetR2Button(bool value);
+
+  /**
+   * Change the value of the Create button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetCreateButton(bool value);
+
+  /**
+   * Change the value of the Options button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetOptionsButton(bool value);
+
+  /**
+   * Change the value of the L3 (left stick) button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetL3Button(bool value);
+
+  /**
+   * Change the value of the R3 (right stick) button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetR3Button(bool value);
+
+  /**
+   * Change the value of the PS button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetPSButton(bool value);
+
+  /**
+   * Change the value of the touchpad button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetTouchpad(bool value);
+};
+
+}  // namespace sim
+}  // namespace frc
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PWMSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PWMSim.h
index 8b4156e..424b7979 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PWMSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PWMSim.h
@@ -48,7 +48,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -66,28 +67,29 @@
   void SetInitialized(bool initialized);
 
   /**
-   * Register a callback to be run when the PWM raw value changes.
+   * Register a callback to be run when the PWM pulse microsecond value changes.
    *
    * @param callback the callback
    * @param initialNotify whether to run the callback with the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterRawValueCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPulseMicrosecondCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
-   * Get the PWM raw value.
+   * Get the PWM pulse microsecond value.
    *
-   * @return the PWM raw value
+   * @return the PWM pulse microsecond value
    */
-  int GetRawValue() const;
+  int32_t GetPulseMicrosecond() const;
 
   /**
-   * Set the PWM raw value.
+   * Set the PWM pulse microsecond value.
    *
-   * @param rawValue the PWM raw value
+   * @param microsecondPulseTime the PWM pulse microsecond value
    */
-  void SetRawValue(int rawValue);
+  void SetPulseMicrosecond(int32_t microsecondPulseTime);
 
   /**
    * Register a callback to be run when the PWM speed changes.
@@ -96,8 +98,9 @@
    * @param initialNotify whether to run the callback with the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterSpeedCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterSpeedCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Get the PWM speed.
@@ -120,7 +123,8 @@
    * @param initialNotify whether to run the callback with the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPositionCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPositionCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -144,7 +148,8 @@
    * @param initialNotify whether to run the callback with the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPeriodScaleCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPeriodScaleCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -168,7 +173,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterZeroLatchCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterZeroLatchCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h
index 11110e3..510349f 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h
@@ -42,8 +42,9 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore>
-  RegisterInitializedCallback(NotifyCallback callback, bool initialNotify) = 0;
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+      NotifyCallback callback, bool initialNotify) = 0;
 
   /**
    * Check if the compressor is on.
@@ -68,8 +69,9 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore>
-  RegisterCompressorOnCallback(NotifyCallback callback, bool initialNotify) = 0;
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
+      NotifyCallback callback, bool initialNotify) = 0;
 
   /**
    * Check the solenoid output on a specific channel.
@@ -98,9 +100,9 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore>
-  RegisterSolenoidOutputCallback(int channel, NotifyCallback callback,
-                                 bool initialNotify) = 0;
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
+      int channel, NotifyCallback callback, bool initialNotify) = 0;
 
   /**
    * Check the value of the pressure switch.
@@ -126,9 +128,9 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore>
-  RegisterPressureSwitchCallback(NotifyCallback callback,
-                                 bool initialNotify) = 0;
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
+      NotifyCallback callback, bool initialNotify) = 0;
 
   /**
    * Read the compressor current.
@@ -153,9 +155,9 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore>
-  RegisterCompressorCurrentCallback(NotifyCallback callback,
-                                    bool initialNotify) = 0;
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterCompressorCurrentCallback(
+      NotifyCallback callback, bool initialNotify) = 0;
 
   /**
    * Get the current value of all solenoid outputs.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PowerDistributionSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PowerDistributionSim.h
index ac54da2..76fa7ba 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PowerDistributionSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/PowerDistributionSim.h
@@ -40,7 +40,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -65,7 +66,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterTemperatureCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterTemperatureCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -90,7 +92,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterVoltageCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterVoltageCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -116,7 +119,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterCurrentCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCurrentCallback(
       int channel, NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/REVPHSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/REVPHSim.h
index fe305ba..917eb1f 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/REVPHSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/REVPHSim.h
@@ -37,21 +37,24 @@
 
   ~REVPHSim() override = default;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   bool GetInitialized() const override;
 
   void SetInitialized(bool solenoidInitialized) override;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
       int channel, NotifyCallback callback, bool initialNotify) override;
 
   bool GetSolenoidOutput(int channel) const override;
 
   void SetSolenoidOutput(int channel, bool solenoidOutput) override;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   /**
@@ -76,9 +79,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterCompressorConfigTypeCallback(NotifyCallback callback,
-                                       bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCompressorConfigTypeCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check whether the closed loop compressor control is active.
@@ -94,16 +97,17 @@
    */
   void SetCompressorConfigType(int compressorConfigType);
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   bool GetPressureSwitch() const override;
 
   void SetPressureSwitch(bool pressureSwitch) override;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterCompressorCurrentCallback(NotifyCallback callback,
-                                    bool initialNotify) override;
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCompressorCurrentCallback(
+      NotifyCallback callback, bool initialNotify) override;
 
   double GetCompressorCurrent() const override;
 
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/RelaySim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/RelaySim.h
index 5034885..ecc4ded 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/RelaySim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/RelaySim.h
@@ -40,9 +40,9 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterInitializedForwardCallback(NotifyCallback callback,
-                                     bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedForwardCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check whether the forward direction has been initialized.
@@ -65,9 +65,9 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterInitializedReverseCallback(NotifyCallback callback,
-                                     bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedReverseCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check whether the reverse direction has been initialized.
@@ -90,7 +90,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterForwardCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterForwardCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -114,7 +115,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterReverseCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterReverseCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h
index ee959b66..64e81e1 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include <units/current.h>
+#include <units/temperature.h>
 #include <units/voltage.h>
 
 #include "frc/simulation/CallbackStore.h"
@@ -26,8 +27,9 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterFPGAButtonCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterFPGAButtonCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Query the state of the FPGA button.
@@ -50,8 +52,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterVInVoltageCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterVInVoltageCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the Vin voltage.
@@ -75,8 +78,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterVInCurrentCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterVInCurrentCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the Vin current.
@@ -100,8 +104,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserVoltage6VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserVoltage6VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 6V rail voltage.
@@ -125,8 +130,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserCurrent6VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserCurrent6VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 6V rail current.
@@ -150,8 +156,9 @@
    *                      initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserActive6VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserActive6VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 6V rail active state.
@@ -175,8 +182,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserVoltage5VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserVoltage5VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 5V rail voltage.
@@ -200,8 +208,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserCurrent5VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserCurrent5VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 5V rail current.
@@ -225,8 +234,9 @@
    *                      initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserActive5VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserActive5VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 5V rail active state.
@@ -250,8 +260,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserVoltage3V3Callback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserVoltage3V3Callback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 3.3V rail voltage.
@@ -275,8 +286,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserCurrent3V3Callback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserCurrent3V3Callback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 3.3V rail current.
@@ -300,8 +312,9 @@
    *                      initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserActive3V3Callback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserActive3V3Callback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 3.3V rail active state.
@@ -326,8 +339,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserFaults6VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserFaults6VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 6V rail number of faults.
@@ -352,8 +366,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserFaults5VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserFaults5VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 5V rail number of faults.
@@ -378,8 +393,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserFaults3V3Callback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserFaults3V3Callback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 3.3V rail number of faults.
@@ -402,8 +418,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterBrownoutVoltageCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterBrownoutVoltageCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the brownout voltage.
@@ -420,6 +437,56 @@
   static void SetBrownoutVoltage(units::volt_t brownoutVoltage);
 
   /**
+   * Register a callback to be run whenever the cpu temp changes.
+   *
+   * @param callback the callback
+   * @param initialNotify whether to call the callback with the initial state
+   * @return the CallbackStore object associated with this callback
+   */
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterCPUTempCallback(
+      NotifyCallback callback, bool initialNotify);
+
+  /**
+   * Get the cpu temp.
+   *
+   * @return the cpu temp.
+   */
+  static units::celsius_t GetCPUTemp();
+
+  /**
+   * Define the cpu temp.
+   *
+   * @param cpuTemp the new cpu temp.
+   */
+  static void SetCPUTemp(units::celsius_t cpuTemp);
+
+  /**
+   * Register a callback to be run whenever the team number changes.
+   *
+   * @param callback the callback
+   * @param initialNotify whether to call the callback with the initial state
+   * @return the CallbackStore object associated with this callback
+   */
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterTeamNumberCallback(
+      NotifyCallback callback, bool initialNotify);
+
+  /**
+   * Get the team number.
+   *
+   * @return the team number.
+   */
+  static int32_t GetTeamNumber();
+
+  /**
+   * Set the team number.
+   *
+   * @param teamNumber the new team number.
+   */
+  static void SetTeamNumber(int32_t teamNumber);
+
+  /**
    * Get the serial number.
    *
    * @return The serial number.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SPIAccelerometerSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SPIAccelerometerSim.h
index 9d31bd0..a84391e 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SPIAccelerometerSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SPIAccelerometerSim.h
@@ -25,8 +25,9 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterActiveCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterActiveCallback(NotifyCallback callback,
+                                                        bool initialNotify);
 
   /**
    * Check whether the accelerometer is active.
@@ -49,8 +50,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterRangeCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterRangeCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Check the range of this accelerometer.
@@ -73,8 +75,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterXCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterXCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the X axis value.
@@ -97,8 +100,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterYCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterYCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the Y axis value.
@@ -121,8 +125,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterZCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterZCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the Z axis value.
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SimDeviceSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SimDeviceSim.h
index 0a4e4d8..6e16116 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SimDeviceSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SimDeviceSim.h
@@ -43,6 +43,20 @@
   SimDeviceSim(const char* name, int index, int channel);
 
   /**
+   * Constructs a SimDeviceSim.
+   *
+   * @param handle the low level handle for the corresponding SimDevice.
+   */
+  explicit SimDeviceSim(HAL_SimDeviceHandle handle);
+
+  /**
+   * Get the name of this object.
+   *
+   * @return name
+   */
+  std::string GetName() const;
+
+  /**
    * Get the property object with the given name.
    *
    * @param name the property name
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SingleJointedArmSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SingleJointedArmSim.h
index 57ad6b1..12a719b 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SingleJointedArmSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SingleJointedArmSim.h
@@ -23,22 +23,24 @@
   /**
    * Creates a simulated arm mechanism.
    *
-   * @param system             The system representing this arm.
+   * @param system             The system representing this arm. This system can
+   *                           be created with
+   *                           LinearSystemId::SingleJointedArmSystem().
    * @param gearbox            The type and number of motors on the arm gearbox.
    * @param gearing            The gear ratio of the arm (numbers greater than 1
    *                           represent reductions).
    * @param armLength          The length of the arm.
    * @param minAngle           The minimum angle that the arm is capable of.
    * @param maxAngle           The maximum angle that the arm is capable of.
-   * @param armMass            The mass of the arm.
    * @param simulateGravity    Whether gravity should be simulated or not.
+   * @param startingAngle      The initial position of the arm.
    * @param measurementStdDevs The standard deviations of the measurements.
    */
   SingleJointedArmSim(const LinearSystem<2, 1, 1>& system,
                       const DCMotor& gearbox, double gearing,
                       units::meter_t armLength, units::radian_t minAngle,
-                      units::radian_t maxAngle, units::kilogram_t armMass,
-                      bool simulateGravity,
+                      units::radian_t maxAngle, bool simulateGravity,
+                      units::radian_t startingAngle,
                       const std::array<double, 1>& measurementStdDevs = {0.0});
   /**
    * Creates a simulated arm mechanism.
@@ -51,17 +53,28 @@
    * @param armLength          The length of the arm.
    * @param minAngle           The minimum angle that the arm is capable of.
    * @param maxAngle           The maximum angle that the arm is capable of.
-   * @param mass               The mass of the arm.
    * @param simulateGravity    Whether gravity should be simulated or not.
+   * @param startingAngle      The initial position of the arm.
    * @param measurementStdDevs The standard deviation of the measurement noise.
    */
   SingleJointedArmSim(const DCMotor& gearbox, double gearing,
                       units::kilogram_square_meter_t moi,
                       units::meter_t armLength, units::radian_t minAngle,
-                      units::radian_t maxAngle, units::kilogram_t mass,
-                      bool simulateGravity,
+                      units::radian_t maxAngle, bool simulateGravity,
+                      units::radian_t startingAngle,
                       const std::array<double, 1>& measurementStdDevs = {0.0});
 
+  using LinearSystemSim::SetState;
+
+  /**
+   * Sets the arm's state. The new angle will be limited between the minimum and
+   * maximum allowed limits.
+   *
+   * @param angle The new angle.
+   * @param velocity The new angular velocity.
+   */
+  void SetState(units::radian_t angle, units::radians_per_second_t velocity);
+
   /**
    * Returns whether the arm would hit the lower limit.
    *
@@ -146,10 +159,9 @@
                      units::second_t dt) override;
 
  private:
-  units::meter_t m_r;
+  units::meter_t m_armLen;
   units::radian_t m_minAngle;
   units::radian_t m_maxAngle;
-  units::kilogram_t m_armMass;
   const DCMotor m_gearbox;
   double m_gearing;
   bool m_simulateGravity;
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SolenoidSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SolenoidSim.h
index 66e3589..dd2e8e7 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SolenoidSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/SolenoidSim.h
@@ -16,6 +16,7 @@
   SolenoidSim(std::shared_ptr<PneumaticsBaseSim> moduleSim, int channel);
   SolenoidSim(int module, PneumaticsModuleType type, int channel);
   SolenoidSim(PneumaticsModuleType type, int channel);
+  virtual ~SolenoidSim() = default;
 
   bool GetOutput() const;
   void SetOutput(bool output);
@@ -29,7 +30,8 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore> RegisterOutputCallback(
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterOutputCallback(
       NotifyCallback callback, bool initialNotify);
 
   std::shared_ptr<PneumaticsBaseSim> GetModuleSim() const;
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h
index efa9b37..5db41f7 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h
@@ -14,16 +14,24 @@
 namespace sim {
 
 /**
- * Class to control a simulated ADXRS450 gyroscope.
+ * Class to control a simulated {@link Ultrasonic}.
  */
 class UltrasonicSim {
  public:
   /**
-   * Constructs from a ADXRS450_Gyro object.
+   * Constructor.
    *
-   * @param gyro ADXRS450_Gyro to simulate
+   * @param ultrasonic The real ultrasonic to simulate
    */
-  explicit UltrasonicSim(const Ultrasonic& gyro);
+  explicit UltrasonicSim(const Ultrasonic& ultrasonic);
+
+  /**
+   * Constructor.
+   *
+   * @param ping unused.
+   * @param echo the ultrasonic's echo channel.
+   */
+  UltrasonicSim(int ping, int echo);
 
   /**
    * Sets if the range measurement is valid.
@@ -37,7 +45,7 @@
    *
    * @param range The range
    */
-  void SetRange(units::meter_t range);
+  void SetRange(units::inch_t range);
 
  private:
   hal::SimBoolean m_simRangeValid;
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/MechanismObject2d.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/MechanismObject2d.h
index c4185e7..81cbc97 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/MechanismObject2d.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/MechanismObject2d.h
@@ -4,11 +4,11 @@
 
 #pragma once
 
+#include <concepts>
 #include <memory>
 #include <stdexcept>
 #include <string>
 #include <string_view>
-#include <type_traits>
 #include <utility>
 
 #include <networktables/NetworkTable.h>
@@ -62,9 +62,8 @@
    * assignments and call chaining.
    * @throw if an object with the given name already exists.
    */
-  template <typename T, typename... Args,
-            typename =
-                std::enable_if_t<std::is_convertible_v<T*, MechanismObject2d*>>>
+  template <typename T, typename... Args>
+    requires std::convertible_to<T*, MechanismObject2d*>
   T* Append(std::string_view name, Args&&... args) {
     std::scoped_lock lock(m_mutex);
     auto& obj = m_objects[name];
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h
index 4bd4289..baee6ff 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h
@@ -96,44 +96,73 @@
   void AddBooleanProperty(std::string_view key, std::function<bool()> getter,
                           std::function<void(bool)> setter) override;
 
+  void PublishConstBoolean(std::string_view key, bool value) override;
+
   void AddIntegerProperty(std::string_view key, std::function<int64_t()> getter,
                           std::function<void(int64_t)> setter) override;
 
+  void PublishConstInteger(std::string_view key, int64_t value) override;
+
   void AddFloatProperty(std::string_view key, std::function<float()> getter,
                         std::function<void(float)> setter) override;
 
+  void PublishConstFloat(std::string_view key, float value) override;
+
   void AddDoubleProperty(std::string_view key, std::function<double()> getter,
                          std::function<void(double)> setter) override;
 
+  void PublishConstDouble(std::string_view key, double value) override;
+
   void AddStringProperty(std::string_view key,
                          std::function<std::string()> getter,
                          std::function<void(std::string_view)> setter) override;
 
+  void PublishConstString(std::string_view key,
+                          std::string_view value) override;
+
   void AddBooleanArrayProperty(
       std::string_view key, std::function<std::vector<int>()> getter,
       std::function<void(std::span<const int>)> setter) override;
 
+  void PublishConstBooleanArray(std::string_view key,
+                                std::span<const int> value) override;
+
   void AddIntegerArrayProperty(
       std::string_view key, std::function<std::vector<int64_t>()> getter,
       std::function<void(std::span<const int64_t>)> setter) override;
 
+  void PublishConstIntegerArray(std::string_view key,
+                                std::span<const int64_t> value) override;
+
   void AddFloatArrayProperty(
       std::string_view key, std::function<std::vector<float>()> getter,
       std::function<void(std::span<const float>)> setter) override;
 
+  void PublishConstFloatArray(std::string_view key,
+                              std::span<const float> value) override;
+
   void AddDoubleArrayProperty(
       std::string_view key, std::function<std::vector<double>()> getter,
       std::function<void(std::span<const double>)> setter) override;
 
+  void PublishConstDoubleArray(std::string_view key,
+                               std::span<const double> value) override;
+
   void AddStringArrayProperty(
       std::string_view key, std::function<std::vector<std::string>()> getter,
       std::function<void(std::span<const std::string>)> setter) override;
 
+  void PublishConstStringArray(std::string_view key,
+                               std::span<const std::string> value) override;
+
   void AddRawProperty(
       std::string_view key, std::string_view typeString,
       std::function<std::vector<uint8_t>()> getter,
       std::function<void(std::span<const uint8_t>)> setter) override;
 
+  void PublishConstRaw(std::string_view key, std::string_view typeString,
+                       std::span<const uint8_t> value) override;
+
   void AddSmallStringProperty(
       std::string_view key,
       std::function<std::string_view(wpi::SmallVectorImpl<char>& buf)> getter,
@@ -198,6 +227,9 @@
   template <typename Topic, typename Getter, typename Setter>
   void AddPropertyImpl(Topic topic, Getter getter, Setter setter);
 
+  template <typename Topic, typename Value>
+  void PublishConstImpl(Topic topic, Value value);
+
   template <typename T, size_t Size, typename Topic, typename Getter,
             typename Setter>
   void AddSmallPropertyImpl(Topic topic, Getter getter, Setter setter);
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.h
index 7fe0b59..f06c252 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <concepts>
+#include <functional>
 #include <memory>
 #include <string_view>
 
@@ -27,9 +29,10 @@
  * @see SmartDashboard
  */
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 class SendableChooser : public SendableChooserBase {
   wpi::StringMap<T> m_choices;
-
+  std::function<void(T)> m_listener;
   template <class U>
   static U _unwrap_smart_ptr(const U& value);
 
@@ -80,7 +83,15 @@
    */
   auto GetSelected() -> decltype(_unwrap_smart_ptr(m_choices[""]));
 
-  void InitSendable(nt::NTSendableBuilder& builder) override;
+  /**
+   * Bind a listener that's called when the selected value changes.
+   * Only one listener can be bound. Calling this function will replace the
+   * previous listener.
+   * @param listener The function to call that accepts the new value
+   */
+  void OnChange(std::function<void(T)>);
+
+  void InitSendable(wpi::SendableBuilder& builder) override;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc
index e90542b..e40befd 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc
@@ -5,30 +5,34 @@
 #pragma once
 
 #include <algorithm>
+#include <functional>
 #include <memory>
 #include <string>
 #include <string_view>
 #include <utility>
 #include <vector>
 
-#include <networktables/NTSendableBuilder.h>
+#include <wpi/sendable/SendableBuilder.h>
 
 #include "frc/smartdashboard/SendableChooser.h"
 
 namespace frc {
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 void SendableChooser<T>::AddOption(std::string_view name, T object) {
   m_choices[name] = std::move(object);
 }
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 void SendableChooser<T>::SetDefaultOption(std::string_view name, T object) {
   m_defaultChoice = name;
   AddOption(name, std::move(object));
 }
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 auto SendableChooser<T>::GetSelected()
     -> decltype(_unwrap_smart_ptr(m_choices[""])) {
   std::string selected = m_defaultChoice;
@@ -46,16 +50,17 @@
 }
 
 template <class T>
-void SendableChooser<T>::InitSendable(nt::NTSendableBuilder& builder) {
+  requires std::copy_constructible<T> && std::default_initializable<T>
+void SendableChooser<T>::OnChange(std::function<void(T)> listener) {
+  std::scoped_lock lock(m_mutex);
+  m_listener = listener;
+}
+
+template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
+void SendableChooser<T>::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("String Chooser");
-  {
-    std::scoped_lock lock(m_mutex);
-    m_instancePubs.emplace_back(
-        nt::IntegerTopic{builder.GetTopic(kInstance)}.Publish());
-    m_instancePubs.back().Set(m_instance);
-    m_activePubs.emplace_back(
-        nt::StringTopic{builder.GetTopic(kActive)}.Publish());
-  }
+  builder.PublishConstInteger(kInstance, m_instance);
   builder.AddStringArrayProperty(
       kOptions,
       [=, this] {
@@ -91,28 +96,40 @@
       nullptr);
   builder.AddStringProperty(kSelected, nullptr,
                             [=, this](std::string_view val) {
-                              std::scoped_lock lock(m_mutex);
-                              m_haveSelected = true;
-                              m_selected = val;
-                              for (auto& pub : m_activePubs) {
-                                pub.Set(val);
+                              T choice{};
+                              std::function<void(T)> listener;
+                              {
+                                std::scoped_lock lock(m_mutex);
+                                m_haveSelected = true;
+                                m_selected = val;
+                                if (m_previousVal != val && m_listener) {
+                                  choice = m_choices[val];
+                                  listener = m_listener;
+                                }
+                                m_previousVal = val;
+                              }
+                              if (listener) {
+                                listener(choice);
                               }
                             });
 }
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 template <class U>
 U SendableChooser<T>::_unwrap_smart_ptr(const U& value) {
   return value;
 }
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 template <class U>
 U* SendableChooser<T>::_unwrap_smart_ptr(const std::unique_ptr<U>& value) {
   return value.get();
 }
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 template <class U>
 std::weak_ptr<U> SendableChooser<T>::_unwrap_smart_ptr(
     const std::shared_ptr<U>& value) {
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h
index b5df73c..f0b4fed 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h
@@ -7,11 +7,8 @@
 #include <atomic>
 #include <string>
 
-#include <networktables/IntegerTopic.h>
-#include <networktables/NTSendable.h>
-#include <networktables/StringTopic.h>
-#include <wpi/SmallVector.h>
 #include <wpi/mutex.h>
+#include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
 namespace frc {
@@ -22,7 +19,7 @@
  * It contains static, non-templated variables to avoid their duplication in the
  * template class.
  */
-class SendableChooserBase : public nt::NTSendable,
+class SendableChooserBase : public wpi::Sendable,
                             public wpi::SendableHelper<SendableChooserBase> {
  public:
   SendableChooserBase();
@@ -41,10 +38,9 @@
   std::string m_defaultChoice;
   std::string m_selected;
   bool m_haveSelected = false;
-  wpi::SmallVector<nt::IntegerPublisher, 2> m_instancePubs;
-  wpi::SmallVector<nt::StringPublisher, 2> m_activePubs;
   wpi::mutex m_mutex;
   int m_instance;
+  std::string m_previousVal;
   static std::atomic_int s_instances;
 };
 
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h
index aeb4947..04435f0 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <span>
 #include <string>
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/AnalogPotentiometerTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/AnalogPotentiometerTest.cpp
index de69894..c8d3068 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/AnalogPotentiometerTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/AnalogPotentiometerTest.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/AnalogPotentiometer.h"
 #include "frc/simulation/AnalogInputSim.h"
 #include "frc/simulation/RoboRioSim.h"
-#include "gtest/gtest.h"
 
 namespace frc {
 using namespace frc::sim;
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/DoubleSolenoidTestCTRE.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/DoubleSolenoidTestCTRE.cpp
index 2678395..7534945 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/DoubleSolenoidTestCTRE.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/DoubleSolenoidTestCTRE.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/DoubleSolenoid.h"
 #include "frc/PneumaticsControlModule.h"
 #include "frc/Solenoid.h"
-#include "gtest/gtest.h"
 
 namespace frc {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/DoubleSolenoidTestREV.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/DoubleSolenoidTestREV.cpp
index 23893c4..336d317 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/DoubleSolenoidTestREV.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/DoubleSolenoidTestREV.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/DoubleSolenoid.h"
 #include "frc/PneumaticsControlModule.h"
 #include "frc/Solenoid.h"
-#include "gtest/gtest.h"
 
 namespace frc {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/DriverStationTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/DriverStationTest.cpp
index c191e2b..b050233 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/DriverStationTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/DriverStationTest.cpp
@@ -5,11 +5,12 @@
 #include <string>
 #include <tuple>
 
+#include <gtest/gtest.h>
+
 #include "frc/DriverStation.h"
 #include "frc/Joystick.h"
 #include "frc/simulation/DriverStationSim.h"
 #include "frc/simulation/SimHooks.h"
-#include "gtest/gtest.h"
 
 class IsJoystickConnectedParametersTest
     : public ::testing::TestWithParam<std::tuple<int, int, int, bool>> {};
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/InterruptTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/InterruptTest.cpp
index 1bf0aa2..96af180 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/InterruptTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/InterruptTest.cpp
@@ -4,13 +4,13 @@
 
 #include <atomic>
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/AsynchronousInterrupt.h"
 #include "frc/DigitalInput.h"
 #include "frc/Timer.h"
 #include "frc/simulation/DIOSim.h"
-#include "gtest/gtest.h"
 
 namespace frc {
 using namespace frc::sim;
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/JoystickTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/JoystickTest.cpp
index 5697879..6aa75ec 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/JoystickTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/JoystickTest.cpp
@@ -4,12 +4,18 @@
 
 #include "frc/Joystick.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
+
 #include "JoystickTestMacros.h"
 #include "frc/simulation/JoystickSim.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
+// https://github.com/wpilibsuite/allwpilib/issues/1550
+TEST(JoystickTest, FastDeconstruction) {
+  Joystick joystick{0};
+}
+
 AXIS_TEST(Joystick, X)
 AXIS_TEST(Joystick, Y)
 AXIS_TEST(Joystick, Z)
@@ -18,3 +24,46 @@
 
 BUTTON_TEST(Joystick, Trigger)
 BUTTON_TEST(Joystick, Top)
+
+TEST(JoystickTest, GetMagnitude) {
+  Joystick joy{1};
+  sim::JoystickSim joysim{1};
+
+  joysim.SetX(0.5);
+  joysim.SetY(0);
+  joysim.NotifyNewData();
+  ASSERT_NEAR(0.5, joy.GetMagnitude(), 0.001);
+
+  joysim.SetX(0);
+  joysim.SetY(-.5);
+  joysim.NotifyNewData();
+  ASSERT_NEAR(0.5, joy.GetMagnitude(), 0.001);
+
+  joysim.SetX(0.5);
+  joysim.SetY(-0.5);
+  joysim.NotifyNewData();
+  ASSERT_NEAR(0.70710678118, joy.GetMagnitude(), 0.001);
+}
+
+TEST(JoystickTest, GetDirection) {
+  Joystick joy{1};
+  sim::JoystickSim joysim{1};
+
+  joysim.SetX(0.5);
+  joysim.SetY(0);
+  joysim.NotifyNewData();
+  ASSERT_NEAR(units::radian_t{90_deg}.value(), joy.GetDirection().value(),
+              0.001);
+
+  joysim.SetX(0);
+  joysim.SetY(-.5);
+  joysim.NotifyNewData();
+  ASSERT_NEAR(units::radian_t{0_deg}.value(), joy.GetDirection().value(),
+              0.001);
+
+  joysim.SetX(0.5);
+  joysim.SetY(-0.5);
+  joysim.NotifyNewData();
+  ASSERT_NEAR(units::radian_t{45_deg}.value(), joy.GetDirection().value(),
+              0.001);
+}
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/NotifierTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/NotifierTest.cpp
new file mode 100644
index 0000000..46e292b
--- /dev/null
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/NotifierTest.cpp
@@ -0,0 +1,53 @@
+// 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 <gtest/gtest.h>
+
+#include "frc/Notifier.h"
+#include "frc/simulation/SimHooks.h"
+
+using namespace frc;
+
+namespace {
+
+class NotifierTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    sim::PauseTiming();
+    sim::RestartTiming();
+  }
+
+  void TearDown() override { sim::ResumeTiming(); }
+};
+
+}  // namespace
+
+TEST_F(NotifierTest, StartPeriodicAndStop) {
+  std::atomic<uint32_t> counter{0};
+
+  Notifier notifier{[&] { ++counter; }};
+  notifier.StartPeriodic(1_s);
+
+  sim::StepTiming(10.5_s);
+
+  notifier.Stop();
+  EXPECT_EQ(10u, counter);
+
+  sim::StepTiming(3_s);
+
+  EXPECT_EQ(10u, counter);
+}
+
+TEST_F(NotifierTest, StartSingle) {
+  std::atomic<uint32_t> counter{0};
+
+  Notifier notifier{[&] { ++counter; }};
+  notifier.StartSingle(1_s);
+
+  sim::StepTiming(10.5_s);
+
+  EXPECT_EQ(1u, counter);
+}
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/PS4ControllerTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/PS4ControllerTest.cpp
index 329a165..4284aed 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/PS4ControllerTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/PS4ControllerTest.cpp
@@ -4,9 +4,10 @@
 
 #include "frc/PS4Controller.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
+
 #include "JoystickTestMacros.h"
 #include "frc/simulation/PS4ControllerSim.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/PS5ControllerTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/PS5ControllerTest.cpp
new file mode 100644
index 0000000..67dd0ca
--- /dev/null
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/PS5ControllerTest.cpp
@@ -0,0 +1,38 @@
+// 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 "frc/PS5Controller.h"  // NOLINT(build/include_order)
+
+#include <gtest/gtest.h>
+
+#include "JoystickTestMacros.h"
+#include "frc/simulation/PS5ControllerSim.h"
+
+using namespace frc;
+
+BUTTON_TEST(PS5Controller, SquareButton)
+BUTTON_TEST(PS5Controller, CrossButton)
+BUTTON_TEST(PS5Controller, CircleButton)
+BUTTON_TEST(PS5Controller, TriangleButton)
+
+BUTTON_TEST(PS5Controller, L1Button)
+BUTTON_TEST(PS5Controller, R1Button)
+BUTTON_TEST(PS5Controller, L2Button)
+BUTTON_TEST(PS5Controller, R2Button)
+
+BUTTON_TEST(PS5Controller, CreateButton)
+BUTTON_TEST(PS5Controller, OptionsButton)
+
+BUTTON_TEST(PS5Controller, L3Button)
+BUTTON_TEST(PS5Controller, R3Button)
+
+BUTTON_TEST(PS5Controller, PSButton)
+BUTTON_TEST(PS5Controller, Touchpad)
+
+AXIS_TEST(PS5Controller, LeftX)
+AXIS_TEST(PS5Controller, RightX)
+AXIS_TEST(PS5Controller, LeftY)
+AXIS_TEST(PS5Controller, RightY)
+AXIS_TEST(PS5Controller, L2Axis)
+AXIS_TEST(PS5Controller, R2Axis)
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/ScopedTracerTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/ScopedTracerTest.cpp
index c4ef317..f4107e7 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/ScopedTracerTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/ScopedTracerTest.cpp
@@ -4,13 +4,13 @@
 
 #include <string_view>
 
+#include <gtest/gtest.h>
 #include <wpi/SmallString.h>
 #include <wpi/StringExtras.h>
 #include <wpi/raw_ostream.h>
 
 #include "frc/ScopedTracer.h"
 #include "frc/simulation/SimHooks.h"
-#include "gtest/gtest.h"
 
 TEST(ScopedTracerTest, Timing) {
   wpi::SmallString<128> buf;
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/SolenoidTestCTRE.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/SolenoidTestCTRE.cpp
index 61e3fb6..f30a976 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/SolenoidTestCTRE.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/SolenoidTestCTRE.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/DoubleSolenoid.h"
 #include "frc/PneumaticsControlModule.h"
 #include "frc/Solenoid.h"
-#include "gtest/gtest.h"
 
 namespace frc {
 TEST(SolenoidCTRETest, ValidInitialization) {
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/SolenoidTestREV.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/SolenoidTestREV.cpp
index 75ea261..3f3dc40 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/SolenoidTestREV.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/SolenoidTestREV.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/DoubleSolenoid.h"
 #include "frc/PneumaticsControlModule.h"
 #include "frc/Solenoid.h"
-#include "gtest/gtest.h"
 
 namespace frc {
 TEST(SolenoidREVTest, ValidInitialization) {
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/TimedRobotTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/TimedRobotTest.cpp
index 08c262b..78d18b5 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/TimedRobotTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/TimedRobotTest.cpp
@@ -9,13 +9,16 @@
 #include <atomic>
 #include <thread>
 
+#include <gtest/gtest.h>
+
 #include "frc/livewindow/LiveWindow.h"
 #include "frc/simulation/DriverStationSim.h"
 #include "frc/simulation/SimHooks.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
+inline constexpr auto kPeriod = 20_ms;
+
 namespace {
 class TimedRobotTest : public ::testing::TestWithParam<bool> {
  protected:
@@ -45,6 +48,8 @@
   std::atomic<uint32_t> m_teleopPeriodicCount{0};
   std::atomic<uint32_t> m_testPeriodicCount{0};
 
+  MockRobot() : TimedRobot{kPeriod} {}
+
   void RobotInit() override { m_robotInitCount++; }
 
   void SimulationInit() override { m_simulationInitCount++; }
@@ -107,7 +112,7 @@
   EXPECT_EQ(0u, robot.m_teleopExitCount);
   EXPECT_EQ(0u, robot.m_testExitCount);
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
   EXPECT_EQ(1u, robot.m_simulationInitCount);
@@ -128,7 +133,7 @@
   EXPECT_EQ(0u, robot.m_teleopExitCount);
   EXPECT_EQ(0u, robot.m_testExitCount);
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
   EXPECT_EQ(1u, robot.m_simulationInitCount);
@@ -183,7 +188,7 @@
   EXPECT_EQ(0u, robot.m_teleopExitCount);
   EXPECT_EQ(0u, robot.m_testExitCount);
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
   EXPECT_EQ(1u, robot.m_simulationInitCount);
@@ -204,7 +209,7 @@
   EXPECT_EQ(0u, robot.m_teleopExitCount);
   EXPECT_EQ(0u, robot.m_testExitCount);
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
   EXPECT_EQ(1u, robot.m_simulationInitCount);
@@ -259,7 +264,7 @@
   EXPECT_EQ(0u, robot.m_teleopExitCount);
   EXPECT_EQ(0u, robot.m_testExitCount);
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
   EXPECT_EQ(1u, robot.m_simulationInitCount);
@@ -280,7 +285,7 @@
   EXPECT_EQ(0u, robot.m_teleopExitCount);
   EXPECT_EQ(0u, robot.m_testExitCount);
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
   EXPECT_EQ(1u, robot.m_simulationInitCount);
@@ -339,7 +344,7 @@
   EXPECT_EQ(0u, robot.m_teleopExitCount);
   EXPECT_EQ(0u, robot.m_testExitCount);
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
   EXPECT_EQ(1u, robot.m_simulationInitCount);
@@ -363,7 +368,7 @@
   EXPECT_EQ(0u, robot.m_teleopExitCount);
   EXPECT_EQ(0u, robot.m_testExitCount);
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
   EXPECT_EQ(1u, robot.m_simulationInitCount);
@@ -436,7 +441,7 @@
   EXPECT_EQ(0u, robot.m_teleopExitCount);
   EXPECT_EQ(0u, robot.m_testExitCount);
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_disabledInitCount);
   EXPECT_EQ(0u, robot.m_autonomousInitCount);
@@ -454,7 +459,7 @@
   frc::sim::DriverStationSim::SetTest(false);
   frc::sim::DriverStationSim::NotifyNewData();
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_disabledInitCount);
   EXPECT_EQ(1u, robot.m_autonomousInitCount);
@@ -472,7 +477,7 @@
   frc::sim::DriverStationSim::SetTest(false);
   frc::sim::DriverStationSim::NotifyNewData();
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_disabledInitCount);
   EXPECT_EQ(1u, robot.m_autonomousInitCount);
@@ -490,7 +495,7 @@
   frc::sim::DriverStationSim::SetTest(true);
   frc::sim::DriverStationSim::NotifyNewData();
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(1u, robot.m_disabledInitCount);
   EXPECT_EQ(1u, robot.m_autonomousInitCount);
@@ -508,7 +513,7 @@
   frc::sim::DriverStationSim::SetTest(false);
   frc::sim::DriverStationSim::NotifyNewData();
 
-  frc::sim::StepTiming(20_ms);
+  frc::sim::StepTiming(kPeriod);
 
   EXPECT_EQ(2u, robot.m_disabledInitCount);
   EXPECT_EQ(1u, robot.m_autonomousInitCount);
@@ -528,7 +533,7 @@
   MockRobot robot;
 
   std::atomic<uint32_t> callbackCount{0};
-  robot.AddPeriodic([&] { callbackCount++; }, 10_ms);
+  robot.AddPeriodic([&] { callbackCount++; }, kPeriod / 2.0);
 
   std::thread robotThread{[&] { robot.StartCompetition(); }};
 
@@ -540,13 +545,13 @@
   EXPECT_EQ(0u, robot.m_disabledPeriodicCount);
   EXPECT_EQ(0u, callbackCount);
 
-  frc::sim::StepTiming(10_ms);
+  frc::sim::StepTiming(kPeriod / 2.0);
 
   EXPECT_EQ(0u, robot.m_disabledInitCount);
   EXPECT_EQ(0u, robot.m_disabledPeriodicCount);
   EXPECT_EQ(1u, callbackCount);
 
-  frc::sim::StepTiming(10_ms);
+  frc::sim::StepTiming(kPeriod / 2.0);
 
   EXPECT_EQ(1u, robot.m_disabledInitCount);
   EXPECT_EQ(1u, robot.m_disabledPeriodicCount);
@@ -560,14 +565,14 @@
   MockRobot robot;
 
   std::atomic<uint32_t> callbackCount{0};
-  robot.AddPeriodic([&] { callbackCount++; }, 10_ms, 5_ms);
+  robot.AddPeriodic([&] { callbackCount++; }, kPeriod / 2.0, kPeriod / 4.0);
 
   // Expirations in this test (ms)
   //
   // Robot | Callback
   // ================
-  //    20 |      15
-  //    40 |      25
+  //     p |    0.75p
+  //    2p |    1.25p
 
   std::thread robotThread{[&] { robot.StartCompetition(); }};
 
@@ -579,25 +584,25 @@
   EXPECT_EQ(0u, robot.m_disabledPeriodicCount);
   EXPECT_EQ(0u, callbackCount);
 
-  frc::sim::StepTiming(7.5_ms);
+  frc::sim::StepTiming(kPeriod * 3.0 / 8.0);
 
   EXPECT_EQ(0u, robot.m_disabledInitCount);
   EXPECT_EQ(0u, robot.m_disabledPeriodicCount);
   EXPECT_EQ(0u, callbackCount);
 
-  frc::sim::StepTiming(7.5_ms);
+  frc::sim::StepTiming(kPeriod * 3.0 / 8.0);
 
   EXPECT_EQ(0u, robot.m_disabledInitCount);
   EXPECT_EQ(0u, robot.m_disabledPeriodicCount);
   EXPECT_EQ(1u, callbackCount);
 
-  frc::sim::StepTiming(5_ms);
+  frc::sim::StepTiming(kPeriod / 4.0);
 
   EXPECT_EQ(1u, robot.m_disabledInitCount);
   EXPECT_EQ(1u, robot.m_disabledPeriodicCount);
   EXPECT_EQ(1u, callbackCount);
 
-  frc::sim::StepTiming(5_ms);
+  frc::sim::StepTiming(kPeriod / 4.0);
 
   EXPECT_EQ(1u, robot.m_disabledInitCount);
   EXPECT_EQ(1u, robot.m_disabledPeriodicCount);
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/TimerTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/TimerTest.cpp
index a7adc45..fd66541 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/TimerTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/TimerTest.cpp
@@ -4,8 +4,9 @@
 
 #include "frc/Timer.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
+
 #include "frc/simulation/SimHooks.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/TimesliceRobotTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/TimesliceRobotTest.cpp
index a2e4e13..5ff9e1a 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/TimesliceRobotTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/TimesliceRobotTest.cpp
@@ -9,9 +9,10 @@
 #include <atomic>
 #include <thread>
 
+#include <gtest/gtest.h>
+
 #include "frc/simulation/DriverStationSim.h"
 #include "frc/simulation/SimHooks.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/UltrasonicTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/UltrasonicTest.cpp
index 72d72d2..ca901db 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/UltrasonicTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/UltrasonicTest.cpp
@@ -2,11 +2,12 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/Ultrasonic.h"
 #include "frc/simulation/UltrasonicSim.h"
-#include "gtest/gtest.h"
 
-namespace frc {
+using namespace frc;
 
 TEST(UltrasonicTest, SimDevices) {
   Ultrasonic ultrasonic{0, 1};
@@ -23,4 +24,19 @@
   EXPECT_EQ(0, ultrasonic.GetRange().value());
 }
 
-}  // namespace frc
+TEST(UltrasonicTest, AutomaticModeToggle) {
+  frc::Ultrasonic ultrasonic{0, 1};
+  EXPECT_NO_THROW({
+    frc::Ultrasonic::SetAutomaticMode(true);
+    frc::Ultrasonic::SetAutomaticMode(false);
+    frc::Ultrasonic::SetAutomaticMode(true);
+  });
+}
+
+TEST(UltrasonicTest, AutomaticModeOnWithZeroInstances) {
+  EXPECT_NO_THROW({ frc::Ultrasonic::SetAutomaticMode(true); });
+}
+
+TEST(UltrasonicTest, AutomaticModeOffWithZeroInstances) {
+  EXPECT_NO_THROW({ frc::Ultrasonic::SetAutomaticMode(false); });
+}
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/UnitNetworkTablesTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/UnitNetworkTablesTest.cpp
index 505f906..9b6d10e 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/UnitNetworkTablesTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/UnitNetworkTablesTest.cpp
@@ -2,13 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <networktables/DoubleTopic.h>
 #include <networktables/NetworkTableInstance.h>
 #include <networktables/UnitTopic.h>
 #include <units/length.h>
 
-#include "gtest/gtest.h"
-
 class UnitNetworkTablesTest : public ::testing::Test {
  public:
   UnitNetworkTablesTest() : inst{nt::NetworkTableInstance::Create()} {}
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/WatchdogTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/WatchdogTest.cpp
index 5f55c2f..55031d3 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/WatchdogTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/WatchdogTest.cpp
@@ -6,8 +6,9 @@
 
 #include <stdint.h>
 
+#include <gtest/gtest.h>
+
 #include "frc/simulation/SimHooks.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/XboxControllerTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/XboxControllerTest.cpp
index 0a8316b..3798fde 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/XboxControllerTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/XboxControllerTest.cpp
@@ -4,9 +4,10 @@
 
 #include "frc/XboxController.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
+
 #include "JoystickTestMacros.h"
 #include "frc/simulation/XboxControllerSim.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp
index 1f542be..f21f268 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/drive/DifferentialDrive.h"
-#include "gtest/gtest.h"
 #include "motorcontrol/MockMotorController.h"
 
 TEST(DifferentialDriveTest, ArcadeDriveIK) {
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp
index de55462..1bbf464 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/drive/MecanumDrive.h"
-#include "gtest/gtest.h"
 #include "motorcontrol/MockMotorController.h"
 
 TEST(MecanumDriveTest, CartesianIK) {
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/event/BooleanEventTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/event/BooleanEventTest.cpp
new file mode 100644
index 0000000..28209b4
--- /dev/null
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/event/BooleanEventTest.cpp
@@ -0,0 +1,487 @@
+// 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 <gtest/gtest.h>
+
+#include "frc/event/BooleanEvent.h"
+#include "frc/event/EventLoop.h"
+
+using namespace frc;
+
+TEST(BooleanEventTest, BinaryCompositions) {
+  EventLoop loop;
+  int andCounter = 0;
+  int orCounter = 0;
+
+  EXPECT_EQ(0, andCounter);
+  EXPECT_EQ(0, orCounter);
+
+  (BooleanEvent(&loop, [] { return true; }) && BooleanEvent(&loop, [] {
+     return false;
+   })).IfHigh([&] { ++andCounter; });
+  (BooleanEvent(&loop, [] { return true; }) || BooleanEvent(&loop, [] {
+     return false;
+   })).IfHigh([&] { ++orCounter; });
+
+  loop.Poll();
+
+  EXPECT_EQ(0, andCounter);
+  EXPECT_EQ(1, orCounter);
+}
+
+/**
+ * Tests that composed edge events only execute on edges (two
+ * rising edge events composed with and() should only execute when both signals
+ * are on the rising edge)
+ */
+TEST(BooleanEventTest, BinaryCompositionsWithEdgeDecorators) {
+  EventLoop loop;
+  bool boolean1 = false;
+  bool boolean2 = false;
+  bool boolean3 = false;
+  bool boolean4 = false;
+  int counter = 0;
+
+  auto event1 = BooleanEvent(&loop, [&] { return boolean1; }).Rising();
+  auto event2 = BooleanEvent(&loop, [&] { return boolean2; }).Rising();
+  auto event3 = BooleanEvent(&loop, [&] { return boolean3; }).Rising();
+  auto event4 = BooleanEvent(&loop, [&] { return boolean4; }).Rising();
+  (event1 && event2).IfHigh([&] { ++counter; });
+  (event3 || event4).IfHigh([&] { ++counter; });
+  EXPECT_EQ(0, counter);
+
+  boolean1 = true;
+  boolean2 = true;
+  boolean3 = true;
+  boolean4 = true;
+  loop.Poll();  // Both actions execute
+
+  EXPECT_EQ(2, counter);
+
+  loop.Poll();  // Nothing should happen since nothing is on rising edge
+
+  EXPECT_EQ(2, counter);
+
+  boolean1 = false;
+  boolean2 = false;
+  boolean3 = false;
+  boolean4 = false;
+  loop.Poll();  // Nothing should happen
+
+  EXPECT_EQ(2, counter);
+
+  boolean1 = true;
+  loop.Poll();  // Nothing should happen since only Bool 1 is on rising edge
+
+  EXPECT_EQ(2, counter);
+
+  boolean2 = true;
+  loop.Poll();  // Bool 2 is on rising edge, but Bool 1 isn't, nothing should
+                // happen
+
+  EXPECT_EQ(2, counter);
+
+  boolean1 = false;
+  boolean2 = false;
+  loop.Poll();  // Nothing should happen
+
+  EXPECT_EQ(2, counter);
+
+  boolean1 = true;
+  boolean2 = true;
+  loop.Poll();  // Bool 1 and 2 are on rising edge, increments counter once
+
+  EXPECT_EQ(3, counter);
+
+  boolean3 = true;
+  loop.Poll();  // Bool 3 is on rising edge, increments counter once
+
+  EXPECT_EQ(4, counter);
+
+  loop.Poll();  // Nothing should happen, Bool 3 isn't on rising edge
+
+  EXPECT_EQ(4, counter);
+
+  boolean4 = true;
+  loop.Poll();  // Bool 4 is on rising edge, increments counter once
+
+  EXPECT_EQ(5, counter);
+
+  loop.Poll();  // Nothing should happen, Bool 4 isn't on rising edge
+
+  EXPECT_EQ(5, counter);
+}
+
+TEST(BooleanEventTest, BinaryCompositionLoopSemantics) {
+  EventLoop loop1;
+  EventLoop loop2;
+  bool boolean1 = true;
+  bool boolean2 = true;
+  int counter1 = 0;
+  int counter2 = 0;
+
+  (BooleanEvent(&loop1, [&] { return boolean1; }) && BooleanEvent(&loop2, [&] {
+     return boolean2;
+   })).IfHigh([&] { ++counter1; });
+  (BooleanEvent(&loop2, [&] { return boolean2; }) && BooleanEvent(&loop1, [&] {
+     return boolean1;
+   })).IfHigh([&] { ++counter2; });
+
+  EXPECT_EQ(0, counter1);
+  EXPECT_EQ(0, counter2);
+
+  loop1
+      .Poll();  // 1st event executes, Bool 1 and 2 are true, increments counter
+
+  EXPECT_EQ(1, counter1);
+  EXPECT_EQ(0, counter2);
+
+  loop2
+      .Poll();  // 2nd event executes, Bool 1 and 2 are true, increments counter
+
+  EXPECT_EQ(1, counter1);
+  EXPECT_EQ(1, counter2);
+
+  boolean2 = false;
+  loop1.Poll();  // 1st event executes, Bool 2 is still true because loop 2
+                 // hasn't updated it, increments counter
+
+  EXPECT_EQ(2, counter1);
+  EXPECT_EQ(1, counter2);
+
+  loop2.Poll();  // 2nd event executes, Bool 2 is now false because this loop
+                 // updated it, does nothing
+
+  EXPECT_EQ(2, counter1);
+  EXPECT_EQ(1, counter2);
+
+  loop1.Poll();  // All bools are updated at this point, nothing should happen
+
+  EXPECT_EQ(2, counter1);
+  EXPECT_EQ(1, counter2);
+
+  boolean2 = true;
+  loop2.Poll();  // 2nd event executes, Bool 2 is true because this loop updated
+                 // it, increments counter
+
+  EXPECT_EQ(2, counter1);
+  EXPECT_EQ(2, counter2);
+
+  loop1.Poll();  // 1st event executes, Bool 2 is true because loop 2 updated
+                 // it, increments counter
+
+  EXPECT_EQ(3, counter1);
+  EXPECT_EQ(2, counter2);
+
+  boolean1 = false;
+  loop2.Poll();  // 2nd event executes, Bool 1 is still true because loop 1
+                 // hasn't updated it, increments counter
+
+  EXPECT_EQ(3, counter1);
+  EXPECT_EQ(3, counter2);
+
+  loop1.Poll();  // 1st event executes, Bool 1 is false because this loop
+                 // updated it, does nothing
+
+  EXPECT_EQ(3, counter1);
+  EXPECT_EQ(3, counter2);
+
+  loop2.Poll();  // All bools are updated at this point, nothing should happen
+
+  EXPECT_EQ(3, counter1);
+  EXPECT_EQ(3, counter2);
+}
+
+/** Tests the order of actions bound to an event loop. */
+TEST(BooleanEventTest, PollOrdering) {
+  EventLoop loop;
+  bool boolean1 = true;
+  bool boolean2 = true;
+  bool enableAssert = false;
+  int counter = 0;
+
+  (BooleanEvent(  // This event binds an action to the event loop first
+       &loop,
+       [&] {
+         if (enableAssert) {
+           ++counter;
+           EXPECT_EQ(1, counter % 3);
+         }
+         return boolean1;
+       }) &&  // The composed event binds an action to the event loop third
+              // This event binds an action to the event loop second
+   BooleanEvent(&loop, [&] {
+     if (enableAssert) {
+       ++counter;
+       EXPECT_EQ(2, counter % 3);
+     }
+     return boolean2;
+     // This binds an action to the event loop fourth
+   })).IfHigh([&] {
+    if (enableAssert) {
+      ++counter;
+      EXPECT_EQ(0, counter % 3);
+    }
+  });
+  enableAssert = true;
+  loop.Poll();
+  loop.Poll();
+  loop.Poll();
+  loop.Poll();
+}
+
+TEST(BooleanEventTest, EdgeDecorators) {
+  EventLoop loop;
+  bool boolean = false;
+  int counter = 0;
+
+  BooleanEvent(&loop, [&] { return boolean; }).Falling().IfHigh([&] {
+    --counter;
+  });
+  BooleanEvent(&loop, [&] { return boolean; }).Rising().IfHigh([&] {
+    ++counter;
+  });
+
+  EXPECT_EQ(0, counter);
+
+  boolean = false;
+  loop.Poll();
+
+  EXPECT_EQ(0, counter);
+
+  boolean = true;
+  loop.Poll();
+
+  EXPECT_EQ(1, counter);
+
+  boolean = true;
+  loop.Poll();
+
+  EXPECT_EQ(1, counter);
+
+  boolean = false;
+  loop.Poll();
+
+  EXPECT_EQ(0, counter);
+}
+
+/**
+ * Tests that binding actions to the same edge event will result in all actions
+ * executing.
+ */
+TEST(BooleanEventTest, EdgeReuse) {
+  EventLoop loop;
+  bool boolean = false;
+  int counter = 0;
+
+  auto event = BooleanEvent(&loop, [&] { return boolean; }).Rising();
+  event.IfHigh([&] { ++counter; });
+  event.IfHigh([&] { ++counter; });
+
+  EXPECT_EQ(0, counter);
+
+  loop.Poll();
+
+  EXPECT_EQ(0, counter);
+
+  boolean = true;
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+
+  boolean = false;
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+
+  boolean = true;
+  loop.Poll();
+
+  EXPECT_EQ(4, counter);
+}
+
+/**
+ * Tests that all actions execute on separate edge events constructed from the
+ * original event.
+ */
+TEST(BooleanEventTest, EdgeReconstruct) {
+  EventLoop loop;
+  bool boolean = false;
+  int counter = 0;
+
+  auto event = BooleanEvent(&loop, [&] { return boolean; });
+  event.Rising().IfHigh([&] { ++counter; });
+  event.Rising().IfHigh([&] { ++counter; });
+
+  EXPECT_EQ(0, counter);
+
+  loop.Poll();
+
+  EXPECT_EQ(0, counter);
+
+  boolean = true;
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+
+  boolean = false;
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+
+  boolean = true;
+  loop.Poll();
+
+  EXPECT_EQ(4, counter);
+}
+
+/** Tests that all actions bound to an event will still execute even if the
+ * signal is changed during the loop poll */
+TEST(BooleanEventTest, MidLoopBooleanChange) {
+  EventLoop loop;
+  bool boolean = false;
+  int counter = 0;
+
+  auto event = BooleanEvent(&loop, [&] { return boolean; }).Rising();
+  event.IfHigh([&] {
+    boolean = false;
+    ++counter;
+  });
+  event.IfHigh([&] { ++counter; });
+
+  EXPECT_EQ(0, counter);
+
+  loop.Poll();
+
+  EXPECT_EQ(0, counter);
+
+  boolean = true;
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+
+  boolean = false;
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+
+  boolean = true;
+  loop.Poll();
+
+  EXPECT_EQ(4, counter);
+}
+
+/**
+ * Tests that all actions bound to composed events will still execute even if
+ * the composed signal changes during the loop poll.
+ */
+TEST(BooleanEventTest, MidLoopBooleanChangeWithComposedEvents) {
+  EventLoop loop;
+  bool boolean1 = false;
+  bool boolean2 = false;
+  bool boolean3 = false;
+  bool boolean4 = false;
+  int counter = 0;
+
+  auto event1 = BooleanEvent(&loop, [&] { return boolean1; });
+  auto event2 = BooleanEvent(&loop, [&] { return boolean2; });
+  auto event3 = BooleanEvent(&loop, [&] { return boolean3; });
+  auto event4 = BooleanEvent(&loop, [&] { return boolean4; });
+  event1.IfHigh([&] {
+    boolean2 = false;
+    boolean3 = false;
+    ++counter;
+  });
+  (event3 || event4).IfHigh([&] {
+    boolean1 = false;
+    ++counter;
+  });
+  (event1 && event2).IfHigh([&] {
+    boolean4 = false;
+    ++counter;
+  });
+
+  EXPECT_EQ(0, counter);
+
+  boolean1 = true;
+  boolean2 = true;
+  boolean3 = true;
+  boolean4 = true;
+  loop.Poll();  // All three actions execute, incrementing the counter three
+                // times and setting all booleans to false
+
+  EXPECT_EQ(3, counter);
+
+  loop.Poll();  // Nothing should happen since everything was set to false
+
+  EXPECT_EQ(3, counter);
+
+  boolean1 = true;
+  boolean2 = true;
+  loop.Poll();  // Bool 1 and 2 are true, increments counter twice, Bool 2 gets
+                // set to false
+
+  EXPECT_EQ(5, counter);
+
+  boolean1 = false;
+  loop.Poll();  // Nothing should happen
+
+  EXPECT_EQ(5, counter);
+
+  boolean1 = true;
+  boolean3 = true;
+  loop.Poll();  // Bool 1 and 3 are true, increments counter twice, Bool 3 gets
+                // set to false
+
+  EXPECT_EQ(7, counter);
+
+  boolean1 = false;
+  boolean4 = true;
+  loop.Poll();  // Bool 4 is true, increments counter once
+
+  EXPECT_EQ(8, counter);
+}
+
+TEST(BooleanEventTest, Negation) {
+  EventLoop loop;
+  bool boolean = false;
+  int counter = 0;
+
+  (!BooleanEvent(&loop, [&] { return boolean; })).IfHigh([&] { ++counter; });
+
+  EXPECT_EQ(0, counter);
+
+  loop.Poll();
+
+  EXPECT_EQ(1, counter);
+
+  boolean = true;
+  loop.Poll();
+
+  EXPECT_EQ(1, counter);
+
+  boolean = false;
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+
+  boolean = true;
+  loop.Poll();
+
+  EXPECT_EQ(2, counter);
+}
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/event/NetworkBooleanEventTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/event/NetworkBooleanEventTest.cpp
index d2ac7c4..0d4ce49 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/event/NetworkBooleanEventTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/event/NetworkBooleanEventTest.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <networktables/BooleanTopic.h>
 #include <networktables/NetworkTableInstance.h>
 
 #include "frc/event/EventLoop.h"
 #include "frc/event/NetworkBooleanEvent.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/main.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/main.cpp
index ba29975..e0bad04 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/main.cpp
@@ -2,10 +2,9 @@
 // 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 <gtest/gtest.h>
 #include <hal/HALBase.h>
 
-#include "gtest/gtest.h"
-
 #ifndef __FRC_ROBORIO__
 namespace frc::impl {
 void ResetMotorSafety();
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/motorcontrol/MotorControllerGroupTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/motorcontrol/MotorControllerGroupTest.cpp
index 48cf700..4ff889a 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/motorcontrol/MotorControllerGroupTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/motorcontrol/MotorControllerGroupTest.cpp
@@ -7,7 +7,8 @@
 #include <memory>
 #include <vector>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "motorcontrol/MockMotorController.h"
 
 using namespace frc;
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardInstanceTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardInstanceTest.cpp
index 0b0c8df..cb2e002 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardInstanceTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardInstanceTest.cpp
@@ -6,10 +6,12 @@
 
 #include <string_view>
 
+#include <gtest/gtest.h>
 #include <networktables/NetworkTableInstance.h>
+#include <networktables/NetworkTableListener.h>
+#include <networktables/StringTopic.h>
 
 #include "frc/shuffleboard/ShuffleboardInstance.h"
-#include "gtest/gtest.h"
 #include "shuffleboard/MockActuatorSendable.h"
 
 class NTWrapper {
@@ -106,3 +108,24 @@
   EXPECT_FALSE(controllable)
       << "The nested actuator widget should have been disabled";
 }
+
+TEST(ShuffleboardInstanceTest, DuplicateSelectTabs) {
+  NTWrapper ntInst;
+  frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
+  std::atomic_int counter = 0;
+  auto subscriber =
+      ntInst.inst.GetStringTopic("/Shuffleboard/.metadata/Selected")
+          .Subscribe("", {.keepDuplicates = true});
+  ntInst.inst.AddListener(
+      subscriber, nt::EventFlags::kValueAll | nt::EventFlags::kImmediate,
+      [&counter](auto& event) { counter++; });
+
+  // There shouldn't be anything there
+  EXPECT_EQ(0, counter);
+
+  shuffleboardInst.SelectTab("tab1");
+  shuffleboardInst.SelectTab("tab1");
+  EXPECT_TRUE(ntInst.inst.WaitForListenerQueue(1.0))
+      << "Listener queue timed out!";
+  EXPECT_EQ(2, counter);
+}
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardTest.cpp
index 693aac4..db689db 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/shuffleboard/Shuffleboard.h"
-#include "gtest/gtest.h"
 
 TEST(ShuffleboardTest, TabObjectsCached) {
   auto& tab1 = frc::Shuffleboard::GetTab("testTabObjectsCached");
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/SuppliedValueWidgetTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/SuppliedValueWidgetTest.cpp
index d964639..37185dd 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/SuppliedValueWidgetTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/shuffleboard/SuppliedValueWidgetTest.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
 #include <networktables/NetworkTableEntry.h>
 #include <networktables/NetworkTableInstance.h>
 
 #include "frc/shuffleboard/ShuffleboardInstance.h"
 #include "frc/shuffleboard/ShuffleboardTab.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXL345SimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXL345SimTest.cpp
index 2bec0ed..2495ad0 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXL345SimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXL345SimTest.cpp
@@ -4,18 +4,18 @@
 
 #include "frc/simulation/ADXL345Sim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/ADXL345_I2C.h"
 #include "frc/ADXL345_SPI.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
 TEST(ADXL345SimTest, SetSpiAttributes) {
   HAL_Initialize(500, 0);
 
-  ADXL345_SPI accel(SPI::kMXP, Accelerometer::kRange_2G);
+  ADXL345_SPI accel(SPI::kMXP, ADXL345_SPI::kRange_2G);
   ADXL345Sim sim(accel);
 
   EXPECT_EQ(0, accel.GetX());
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXL362SimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXL362SimTest.cpp
index 13f6c00..3658ffd 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXL362SimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXL362SimTest.cpp
@@ -4,17 +4,17 @@
 
 #include "frc/simulation/ADXL362Sim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/ADXL362.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
 TEST(ADXL362SimTest, SetAttributes) {
   HAL_Initialize(500, 0);
 
-  ADXL362 accel(SPI::kMXP, Accelerometer::kRange_2G);
+  ADXL362 accel(SPI::kMXP, ADXL362::kRange_2G);
   ADXL362Sim sim(accel);
 
   EXPECT_EQ(0, accel.GetX());
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXRS450_GyroSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXRS450_GyroSimTest.cpp
index 57c4fbe..ea99f80 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXRS450_GyroSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ADXRS450_GyroSimTest.cpp
@@ -4,10 +4,10 @@
 
 #include "frc/simulation/ADXRS450_GyroSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/ADXRS450_Gyro.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AccelerometerSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AccelerometerSimTest.cpp
index 3dc8582..328645d 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AccelerometerSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AccelerometerSimTest.cpp
@@ -4,12 +4,12 @@
 
 #include "frc/simulation/BuiltInAccelerometerSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/Accelerometer.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/BuiltInAccelerometer.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
@@ -99,7 +99,7 @@
 
   EnumCallback callback;
 
-  Accelerometer::Range range = Accelerometer::kRange_4G;
+  BuiltInAccelerometer::Range range = BuiltInAccelerometer::kRange_4G;
   auto cb = sim.RegisterRangeCallback(callback.GetCallback(), false);
   BuiltInAccelerometer accel(range);
   EXPECT_TRUE(callback.WasTriggered());
@@ -108,7 +108,7 @@
 
   // 2G
   callback.Reset();
-  range = Accelerometer::kRange_2G;
+  range = BuiltInAccelerometer::kRange_2G;
   accel.SetRange(range);
   EXPECT_TRUE(callback.WasTriggered());
   EXPECT_EQ(static_cast<int>(range), sim.GetRange());
@@ -116,7 +116,7 @@
 
   // 4G
   callback.Reset();
-  range = Accelerometer::kRange_4G;
+  range = BuiltInAccelerometer::kRange_4G;
   accel.SetRange(range);
   EXPECT_TRUE(callback.WasTriggered());
   EXPECT_EQ(static_cast<int>(range), sim.GetRange());
@@ -124,15 +124,10 @@
 
   // 8G
   callback.Reset();
-  range = Accelerometer::kRange_8G;
+  range = BuiltInAccelerometer::kRange_8G;
   accel.SetRange(range);
   EXPECT_TRUE(callback.WasTriggered());
   EXPECT_EQ(static_cast<int>(range), sim.GetRange());
   EXPECT_EQ(static_cast<int>(range), callback.GetLastValue());
-
-  // 16G - Not supported
-  callback.Reset();
-  EXPECT_THROW(accel.SetRange(Accelerometer::kRange_16G), std::runtime_error);
-  EXPECT_FALSE(callback.WasTriggered());
 }
 }  // namespace frc::sim
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AddressableLEDSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AddressableLEDSimTest.cpp
index e450477..6907cf6 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AddressableLEDSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AddressableLEDSimTest.cpp
@@ -6,11 +6,11 @@
 
 #include <array>
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/AddressableLED.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogEncoderSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogEncoderSimTest.cpp
index 0a1b687..3f0ea45 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogEncoderSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogEncoderSimTest.cpp
@@ -4,13 +4,13 @@
 
 #include <numbers>
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 #include <units/math.h>
 
 #include "frc/AnalogEncoder.h"
 #include "frc/AnalogInput.h"
 #include "frc/simulation/AnalogEncoderSim.h"
-#include "gtest/gtest.h"
 
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
   EXPECT_LE(units::math::abs(val1 - val2), eps)
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogGyroSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogGyroSimTest.cpp
index f1baaca..e82fa04 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogGyroSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogGyroSimTest.cpp
@@ -4,12 +4,12 @@
 
 #include "frc/simulation/AnalogGyroSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/AnalogGyro.h"
 #include "frc/AnalogInput.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogInputSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogInputSimTest.cpp
index 8b4569e..2f963fa 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogInputSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogInputSimTest.cpp
@@ -4,11 +4,11 @@
 
 #include "frc/simulation/AnalogInputSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/AnalogInput.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogOutputSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogOutputSimTest.cpp
index 630f2c9..bbe2b81 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogOutputSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogOutputSimTest.cpp
@@ -4,11 +4,11 @@
 
 #include "frc/simulation/AnalogOutputSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/AnalogOutput.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogTriggerSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogTriggerSimTest.cpp
index 5e9e28e..c01f64f 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogTriggerSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/AnalogTriggerSimTest.cpp
@@ -4,11 +4,11 @@
 
 #include "frc/simulation/AnalogTriggerSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/AnalogTrigger.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/CTREPCMSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/CTREPCMSimTest.cpp
index 8082ed5..0d3e6ec 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/CTREPCMSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/CTREPCMSimTest.cpp
@@ -4,12 +4,12 @@
 
 #include "frc/simulation/CTREPCMSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/DoubleSolenoid.h"
 #include "frc/PneumaticsControlModule.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DCMotorSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DCMotorSimTest.cpp
index efed939..75b8a4b 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DCMotorSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DCMotorSimTest.cpp
@@ -2,6 +2,8 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/Encoder.h"
 #include "frc/RobotController.h"
 #include "frc/controller/PIDController.h"
@@ -10,7 +12,6 @@
 #include "frc/simulation/DCMotorSim.h"
 #include "frc/simulation/EncoderSim.h"
 #include "frc/simulation/RoboRioSim.h"
-#include "gtest/gtest.h"
 
 TEST(DCMotorSimTest, VoltageSteadyState) {
   frc::DCMotor gearbox = frc::DCMotor::NEO(1);
@@ -62,7 +63,7 @@
   frc::sim::DCMotorSim sim{gearbox, 1.0,
                            units::kilogram_square_meter_t{0.0005}};
 
-  frc2::PIDController controller{0.04, 0.0, 0.001};
+  frc::PIDController controller{0.04, 0.0, 0.001};
 
   frc::Encoder encoder{0, 1};
   frc::sim::EncoderSim encoderSim{encoder};
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DIOSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DIOSimTest.cpp
index 338d24e..3e67955 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DIOSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DIOSimTest.cpp
@@ -4,12 +4,12 @@
 
 #include "frc/simulation/DIOSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/DigitalInput.h"
 #include "frc/DigitalOutput.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DifferentialDrivetrainSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DifferentialDrivetrainSimTest.cpp
index 6edfde0..7e56426 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DifferentialDrivetrainSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DifferentialDrivetrainSimTest.cpp
@@ -2,6 +2,7 @@
 // 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 <gtest/gtest.h>
 #include <units/current.h>
 #include <units/math.h>
 #include <units/moment_of_inertia.h>
@@ -15,7 +16,6 @@
 #include "frc/system/plant/LinearSystemId.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
 #include "frc/trajectory/constraint/DifferentialDriveKinematicsConstraint.h"
-#include "gtest/gtest.h"
 
 TEST(DifferentialDrivetrainSimTest, Convergence) {
   auto motor = frc::DCMotor::NEO(2);
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DigitalPWMSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DigitalPWMSimTest.cpp
index fd62edc..27fb5cb 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DigitalPWMSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DigitalPWMSimTest.cpp
@@ -4,11 +4,11 @@
 
 #include "frc/simulation/DigitalPWMSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/DigitalOutput.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp
index 6aa31c6..2513252 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp
@@ -5,13 +5,14 @@
 #include <string>
 #include <tuple>
 
+#include <gtest/gtest.h>
+
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/DriverStation.h"
 #include "frc/Joystick.h"
 #include "frc/RobotState.h"
 #include "frc/simulation/DriverStationSim.h"
 #include "frc/simulation/SimHooks.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 using namespace frc::sim;
@@ -129,6 +130,17 @@
 
   auto cb = DriverStationSim::RegisterAllianceStationIdCallback(
       callback.GetCallback(), false);
+
+  // Unknown
+  allianceStation = HAL_AllianceStationID_kUnknown;
+  DriverStationSim::SetAllianceStationId(allianceStation);
+  frc::sim::DriverStationSim::NotifyNewData();
+  EXPECT_EQ(allianceStation, DriverStationSim::GetAllianceStationId());
+  EXPECT_FALSE(DriverStation::GetAlliance().has_value());
+  EXPECT_FALSE(DriverStation::GetLocation().has_value());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(allianceStation, callback.GetLastValue());
+
   // B1
   allianceStation = HAL_AllianceStationID_kBlue1;
   DriverStationSim::SetAllianceStationId(allianceStation);
@@ -219,7 +231,7 @@
   DriverStationSim::SetMatchTime(kTestTime);
   frc::sim::DriverStationSim::NotifyNewData();
   EXPECT_EQ(kTestTime, DriverStationSim::GetMatchTime());
-  EXPECT_EQ(kTestTime, DriverStation::GetMatchTime());
+  EXPECT_EQ(kTestTime, DriverStation::GetMatchTime().value());
   EXPECT_TRUE(callback.WasTriggered());
   EXPECT_EQ(kTestTime, callback.GetLastValue());
 }
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DutyCycleEncoderSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DutyCycleEncoderSimTest.cpp
index 8249499..e81c63e 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DutyCycleEncoderSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DutyCycleEncoderSimTest.cpp
@@ -4,11 +4,11 @@
 
 #include "frc/simulation/DutyCycleEncoderSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/DutyCycleEncoder.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
@@ -32,4 +32,45 @@
   EXPECT_EQ(19.1, enc.GetDistance());
 }
 
+TEST(DutyCycleEncoderSimTest, SetDistancePerRotation) {
+  HAL_Initialize(500, 0);
+
+  DutyCycleEncoder enc{0};
+  DutyCycleEncoderSim sim(enc);
+  sim.Set(units::turn_t{1.5});
+  enc.SetDistancePerRotation(42);
+  EXPECT_EQ(63, enc.GetDistance());
+}
+
+TEST(DutyCycleEncoderSimTest, SetAbsolutePosition) {
+  HAL_Initialize(500, 0);
+
+  DutyCycleEncoder enc{0};
+  DutyCycleEncoderSim sim(enc);
+  sim.SetAbsolutePosition(0.75);
+  EXPECT_EQ(0.75, enc.GetAbsolutePosition());
+}
+
+TEST(DutyCycleEncoderSimTest, SetIsConnected) {
+  HAL_Initialize(500, 0);
+
+  DutyCycleEncoder enc{0};
+  DutyCycleEncoderSim sim(enc);
+  sim.SetConnected(true);
+  EXPECT_TRUE(enc.IsConnected());
+  sim.SetConnected(false);
+  EXPECT_FALSE(enc.IsConnected());
+}
+
+TEST(DutyCycleEncoderSimTest, Reset) {
+  HAL_Initialize(500, 0);
+
+  DutyCycleEncoder enc{0};
+  DutyCycleEncoderSim sim(enc);
+  sim.SetDistance(2.5);
+  EXPECT_EQ(2.5, enc.GetDistance());
+  enc.Reset();
+  EXPECT_EQ(0, enc.GetDistance());
+}
+
 }  // namespace frc::sim
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DutyCycleSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DutyCycleSimTest.cpp
index 56e0592..297acc9 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DutyCycleSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/DutyCycleSimTest.cpp
@@ -4,12 +4,12 @@
 
 #include "frc/simulation/DutyCycleSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/DigitalInput.h"
 #include "frc/DutyCycle.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp
index 3c647e6..28777bd 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp
@@ -2,6 +2,7 @@
 // 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 <gtest/gtest.h>
 #include <units/math.h>
 #include <units/time.h>
 
@@ -14,15 +15,14 @@
 #include "frc/system/NumericalIntegration.h"
 #include "frc/system/plant/DCMotor.h"
 #include "frc/system/plant/LinearSystemId.h"
-#include "gtest/gtest.h"
 
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
   EXPECT_LE(units::math::abs(val1 - val2), eps)
 
 TEST(ElevatorSimTest, StateSpaceSim) {
   frc::sim::ElevatorSim sim(frc::DCMotor::Vex775Pro(4), 14.67, 8_kg, 0.75_in,
-                            0_m, 3_m, true, {0.01});
-  frc2::PIDController controller(10, 0.0, 0.0);
+                            0_m, 3_m, true, 0_m, {0.01});
+  frc::PIDController controller(10, 0.0, 0.0);
 
   frc::PWMVictorSPX motor(0);
   frc::Encoder encoder(0, 1);
@@ -46,7 +46,7 @@
 
 TEST(ElevatorSimTest, MinMax) {
   frc::sim::ElevatorSim sim(frc::DCMotor::Vex775Pro(4), 14.67, 8_kg, 0.75_in,
-                            0_m, 1_m, true, {0.01});
+                            0_m, 1_m, true, 0_m, {0.01});
   for (size_t i = 0; i < 100; ++i) {
     sim.SetInput(frc::Vectord<1>{0.0});
     sim.Update(20_ms);
@@ -66,7 +66,7 @@
 
 TEST(ElevatorSimTest, Stability) {
   frc::sim::ElevatorSim sim{
-      frc::DCMotor::Vex775Pro(4), 100, 4_kg, 0.5_in, 0_m, 10_m, true};
+      frc::DCMotor::Vex775Pro(4), 100, 4_kg, 0.5_in, 0_m, 10_m, false, 0_m};
 
   sim.SetState(frc::Vectord<2>{0.0, 0.0});
   sim.SetInput(frc::Vectord<1>{12.0});
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/EncoderSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/EncoderSimTest.cpp
index 7722ccc..2a763af 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/EncoderSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/EncoderSimTest.cpp
@@ -4,11 +4,12 @@
 
 #include "frc/simulation/EncoderSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
+#include <wpi/deprecated.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/Encoder.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
@@ -198,7 +199,7 @@
   DoubleCallback callback;
   auto cb = sim.RegisterDistancePerPulseCallback(callback.GetCallback(), false);
 
-  encoder.SetDistancePerPulse(.03405);
+  sim.SetDistancePerPulse(.03405);
   EXPECT_EQ(.03405, sim.GetDistancePerPulse());
   EXPECT_EQ(.03405, encoder.GetDistancePerPulse());
   EXPECT_TRUE(callback.WasTriggered());
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/PDPSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/PDPSimTest.cpp
index 78711b1..ad058de 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/PDPSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/PDPSimTest.cpp
@@ -4,11 +4,11 @@
 
 #include "frc/simulation/PowerDistributionSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/PowerDistribution.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/PWMSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/PWMSimTest.cpp
index 0df3590..4dcdbb1 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/PWMSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/PWMSimTest.cpp
@@ -4,14 +4,16 @@
 
 #include "frc/simulation/PWMSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/PWM.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
+constexpr double kPWMStepSize = 1.0 / 2000.0;
+
 TEST(PWMSimTest, Initialize) {
   HAL_Initialize(500, 0);
 
@@ -26,7 +28,7 @@
   EXPECT_TRUE(sim.GetInitialized());
 }
 
-TEST(PWMSimTest, SetRawValue) {
+TEST(PWMSimTest, SetPulseTime) {
   HAL_Initialize(500, 0);
 
   PWMSim sim{0};
@@ -35,13 +37,13 @@
 
   IntCallback callback;
 
-  auto cb = sim.RegisterRawValueCallback(callback.GetCallback(), false);
+  auto cb = sim.RegisterPulseMicrosecondCallback(callback.GetCallback(), false);
   PWM pwm{0};
-  sim.SetRawValue(229);
-  EXPECT_EQ(229, sim.GetRawValue());
-  EXPECT_EQ(229, pwm.GetRaw());
+  sim.SetPulseMicrosecond(2290);
+  EXPECT_EQ(2290, sim.GetPulseMicrosecond());
+  EXPECT_EQ(2290, std::lround(pwm.GetPulseTime().value()));
   EXPECT_TRUE(callback.WasTriggered());
-  EXPECT_EQ(229, callback.GetLastValue());
+  EXPECT_EQ(2290, callback.GetLastValue());
 }
 
 TEST(PWMSimTest, SetSpeed) {
@@ -55,13 +57,47 @@
 
   auto cb = sim.RegisterSpeedCallback(callback.GetCallback(), false);
   PWM pwm{0};
-  constexpr double kTestValue = 0.3504;
+  double kTestValue = 0.3504;
   pwm.SetSpeed(kTestValue);
 
-  EXPECT_EQ(kTestValue, sim.GetSpeed());
-  EXPECT_EQ(kTestValue, pwm.GetSpeed());
+  EXPECT_NEAR(kTestValue, sim.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, pwm.GetSpeed(), kPWMStepSize);
   EXPECT_TRUE(callback.WasTriggered());
-  EXPECT_EQ(kTestValue, callback.GetLastValue());
+  EXPECT_NEAR(kTestValue, callback.GetLastValue(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue / 2 + 0.5, sim.GetPosition(), kPWMStepSize * 2);
+  EXPECT_NEAR(kTestValue / 2 + 0.5, pwm.GetPosition(), kPWMStepSize * 2);
+
+  kTestValue = -1.0;
+  pwm.SetSpeed(kTestValue);
+
+  EXPECT_NEAR(kTestValue, sim.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, pwm.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(0.0, sim.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(0.0, pwm.GetPosition(), kPWMStepSize);
+
+  kTestValue = 0.0;
+  pwm.SetSpeed(kTestValue);
+
+  EXPECT_NEAR(kTestValue, sim.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, pwm.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(0.5, sim.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(0.5, pwm.GetPosition(), kPWMStepSize);
+
+  kTestValue = 1.0;
+  pwm.SetSpeed(kTestValue);
+
+  EXPECT_NEAR(kTestValue, sim.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, pwm.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, sim.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, pwm.GetPosition(), kPWMStepSize);
+
+  kTestValue = 1.1;
+  pwm.SetSpeed(kTestValue);
+
+  EXPECT_NEAR(1.0, sim.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(1.0, pwm.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(1.0, sim.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(1.0, pwm.GetPosition(), kPWMStepSize);
 }
 
 TEST(PWMSimTest, SetPosition) {
@@ -75,13 +111,47 @@
 
   auto cb = sim.RegisterPositionCallback(callback.GetCallback(), false);
   PWM pwm{0};
-  constexpr double kTestValue = 0.3504;
+  double kTestValue = 0.3504;
   pwm.SetPosition(kTestValue);
 
-  EXPECT_EQ(kTestValue, sim.GetPosition());
-  EXPECT_EQ(kTestValue, pwm.GetPosition());
+  EXPECT_NEAR(kTestValue, sim.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, pwm.GetPosition(), kPWMStepSize);
   EXPECT_TRUE(callback.WasTriggered());
-  EXPECT_EQ(kTestValue, callback.GetLastValue());
+  EXPECT_NEAR(kTestValue, callback.GetLastValue(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue * 2 - 1.0, sim.GetSpeed(), kPWMStepSize * 2);
+  EXPECT_NEAR(kTestValue * 2 - 1.0, pwm.GetSpeed(), kPWMStepSize * 2);
+
+  kTestValue = -1.0;
+  pwm.SetPosition(kTestValue);
+
+  EXPECT_NEAR(0.0, sim.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(0.0, pwm.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, sim.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, pwm.GetSpeed(), kPWMStepSize);
+
+  kTestValue = 0.0;
+  pwm.SetPosition(kTestValue);
+
+  EXPECT_NEAR(kTestValue, sim.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, pwm.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(-1.0, sim.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(-1.0, pwm.GetSpeed(), kPWMStepSize);
+
+  kTestValue = 1.0;
+  pwm.SetPosition(kTestValue);
+
+  EXPECT_NEAR(kTestValue, sim.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, pwm.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, sim.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(kTestValue, pwm.GetSpeed(), kPWMStepSize);
+
+  kTestValue = 1.1;
+  pwm.SetPosition(kTestValue);
+
+  EXPECT_NEAR(1.0, sim.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(1.0, pwm.GetPosition(), kPWMStepSize);
+  EXPECT_NEAR(1.0, sim.GetSpeed(), kPWMStepSize);
+  EXPECT_NEAR(1.0, pwm.GetSpeed(), kPWMStepSize);
 }
 
 TEST(PWMSimTest, SetPeriodScale) {
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/REVPHSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/REVPHSimTest.cpp
index 688f37e..952107b 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/REVPHSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/REVPHSimTest.cpp
@@ -4,12 +4,12 @@
 
 #include "frc/simulation/REVPHSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/DoubleSolenoid.h"
 #include "frc/PneumaticHub.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/RelaySimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/RelaySimTest.cpp
index 292d629..200ce11 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/RelaySimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/RelaySimTest.cpp
@@ -4,11 +4,11 @@
 
 #include "frc/simulation/RelaySim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/Relay.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp
index 98d4620..e42f38f 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp
@@ -4,12 +4,12 @@
 
 #include "frc/simulation/RoboRioSim.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 #include <hal/HALBase.h>
 
 #include "callback_helpers/TestCallbackHelpers.h"
 #include "frc/RobotController.h"
-#include "gtest/gtest.h"
 
 namespace frc::sim {
 
@@ -207,6 +207,36 @@
   EXPECT_EQ(kTestFaults, RobotController::GetFaultCount3V3());
 }
 
+TEST(RoboRioSimTest, SetCPUTemp) {
+  RoboRioSim::ResetData();
+
+  DoubleCallback callback;
+  auto cbHandle =
+      RoboRioSim::RegisterCPUTempCallback(callback.GetCallback(), false);
+  constexpr double kCPUTemp = 100.0;
+
+  RoboRioSim::SetCPUTemp(units::celsius_t{kCPUTemp});
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(kCPUTemp, callback.GetLastValue());
+  EXPECT_EQ(kCPUTemp, RoboRioSim::GetCPUTemp().value());
+  EXPECT_EQ(kCPUTemp, RobotController::GetCPUTemp().value());
+}
+
+TEST(RoboRioSimTest, SetTeamNumber) {
+  RoboRioSim::ResetData();
+
+  IntCallback callback;
+  auto cbHandle =
+      RoboRioSim::RegisterTeamNumberCallback(callback.GetCallback(), false);
+  constexpr int kTeamNumber = 9999;
+
+  RoboRioSim::SetTeamNumber(kTeamNumber);
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(kTeamNumber, callback.GetLastValue());
+  EXPECT_EQ(kTeamNumber, RoboRioSim::GetTeamNumber());
+  EXPECT_EQ(kTeamNumber, RobotController::GetTeamNumber());
+}
+
 TEST(RoboRioSimTest, SetSerialNumber) {
   const std::string kSerialNum = "Hello";
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SimDeviceSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SimDeviceSimTest.cpp
index 5ef2675..3cd2a63 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SimDeviceSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SimDeviceSimTest.cpp
@@ -4,10 +4,10 @@
 
 #include <string_view>
 
+#include <gtest/gtest.h>
 #include <hal/SimDevice.h>
 
 #include "frc/simulation/SimDeviceSim.h"
-#include "gtest/gtest.h"
 
 using namespace frc::sim;
 
@@ -20,6 +20,8 @@
   EXPECT_FALSE(simBool.Get());
   simBool.Set(true);
   EXPECT_TRUE(devBool.Get());
+
+  EXPECT_EQ(sim.GetName(), "test");
 }
 
 TEST(SimDeviceSimTest, EnumerateDevices) {
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SimInitializationTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SimInitializationTest.cpp
index e703912..51c5386 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SimInitializationTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SimInitializationTest.cpp
@@ -4,6 +4,7 @@
 
 #include <exception>
 
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/simulation/AddressableLEDSim.h"
@@ -23,7 +24,6 @@
 #include "frc/simulation/RelaySim.h"
 #include "frc/simulation/RoboRioSim.h"
 #include "frc/simulation/SPIAccelerometerSim.h"
-#include "gtest/gtest.h"
 
 using namespace frc::sim;
 
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SingleJointedArmSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SingleJointedArmSimTest.cpp
index 2e4c793..1311a0e 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SingleJointedArmSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/SingleJointedArmSimTest.cpp
@@ -4,12 +4,13 @@
 
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/simulation/SingleJointedArmSim.h"
-#include "gtest/gtest.h"
 
 TEST(SingleJointedArmTest, Disabled) {
-  frc::sim::SingleJointedArmSim sim(frc::DCMotor::Vex775Pro(2), 100, 3_kg_sq_m,
-                                    9.5_in, -180_deg, 0_deg, 10_lb, true);
+  frc::sim::SingleJointedArmSim sim(frc::DCMotor::Vex775Pro(2), 300, 3_kg_sq_m,
+                                    30_in, -180_deg, 0_deg, true, 90_deg);
   sim.SetState(frc::Vectord<2>{0.0, 0.0});
 
   for (size_t i = 0; i < 12 / 0.02; ++i) {
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp
index 7456adb..6d15fc4 100644
--- a/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp
@@ -2,6 +2,7 @@
 // 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 <gtest/gtest.h>
 #include <units/angular_acceleration.h>
 #include <units/angular_velocity.h>
 
@@ -18,16 +19,14 @@
 #include "frc/simulation/LinearSystemSim.h"
 #include "frc/simulation/PWMSim.h"
 #include "frc/simulation/RoboRioSim.h"
-#include "frc/simulation/SingleJointedArmSim.h"
 #include "frc/system/plant/LinearSystemId.h"
-#include "gtest/gtest.h"
 
 TEST(StateSpaceSimTest, FlywheelSim) {
   const frc::LinearSystem<1, 1, 1> plant =
       frc::LinearSystemId::IdentifyVelocitySystem<units::radian>(
           0.02_V / 1_rad_per_s, 0.01_V / 1_rad_per_s_sq);
   frc::sim::FlywheelSim sim{plant, frc::DCMotor::NEO(2), 1.0};
-  frc2::PIDController controller{0.2, 0.0, 0.0};
+  frc::PIDController controller{0.2, 0.0, 0.0};
   frc::SimpleMotorFeedforward<units::radian> feedforward{
       0_V, 0.02_V / 1_rad_per_s, 0.01_V / 1_rad_per_s_sq};
   frc::Encoder encoder{0, 1};
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/smartdashboard/Mechanism2dTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/smartdashboard/Mechanism2dTest.cpp
new file mode 100644
index 0000000..a500232
--- /dev/null
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/smartdashboard/Mechanism2dTest.cpp
@@ -0,0 +1,85 @@
+// 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 <frc/smartdashboard/Mechanism2d.h>
+#include <frc/smartdashboard/MechanismLigament2d.h>
+#include <frc/smartdashboard/SmartDashboard.h>
+#include <frc/util/Color8Bit.h>
+
+#include <gtest/gtest.h>
+#include <networktables/NetworkTableInstance.h>
+#include <units/angle.h>
+
+class Mechanism2dTest;
+
+TEST(Mechanism2dTest, Canvas) {
+  frc::Mechanism2d mechanism{5, 10};
+  auto dimsEntry = nt::NetworkTableInstance::GetDefault().GetEntry(
+      "/SmartDashboard/mechanism/dims");
+  auto colorEntry = nt::NetworkTableInstance::GetDefault().GetEntry(
+      "/SmartDashboard/mechanism/backgroundColor");
+  frc::SmartDashboard::PutData("mechanism", &mechanism);
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_EQ(5.0, dimsEntry.GetDoubleArray({})[0]);
+  EXPECT_EQ(10.0, dimsEntry.GetDoubleArray({})[1]);
+  EXPECT_EQ("#000020", colorEntry.GetString(""));
+  mechanism.SetBackgroundColor({255, 255, 255});
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_EQ("#FFFFFF", colorEntry.GetString(""));
+}
+
+TEST(Mechanism2dTest, Root) {
+  frc::Mechanism2d mechanism{5, 10};
+  auto xEntry = nt::NetworkTableInstance::GetDefault().GetEntry(
+      "/SmartDashboard/mechanism/root/x");
+  auto yEntry = nt::NetworkTableInstance::GetDefault().GetEntry(
+      "/SmartDashboard/mechanism/root/y");
+  frc::MechanismRoot2d* root = mechanism.GetRoot("root", 1, 2);
+  frc::SmartDashboard::PutData("mechanism", &mechanism);
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_EQ(1.0, xEntry.GetDouble(0.0));
+  EXPECT_EQ(2.0, yEntry.GetDouble(0.0));
+  root->SetPosition(2, 4);
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_EQ(2.0, xEntry.GetDouble(0.0));
+  EXPECT_EQ(4.0, yEntry.GetDouble(0.0));
+}
+
+TEST(Mechanism2dTest, Ligament) {
+  frc::Mechanism2d mechanism{5, 10};
+  auto angleEntry = nt::NetworkTableInstance::GetDefault().GetEntry(
+      "/SmartDashboard/mechanism/root/ligament/angle");
+  auto colorEntry = nt::NetworkTableInstance::GetDefault().GetEntry(
+      "/SmartDashboard/mechanism/root/ligament/color");
+  auto lengthEntry = nt::NetworkTableInstance::GetDefault().GetEntry(
+      "/SmartDashboard/mechanism/root/ligament/length");
+  auto weightEntry = nt::NetworkTableInstance::GetDefault().GetEntry(
+      "/SmartDashboard/mechanism/root/ligament/weight");
+  frc::MechanismRoot2d* root = mechanism.GetRoot("root", 1, 2);
+  frc::MechanismLigament2d* ligament = root->Append<frc::MechanismLigament2d>(
+      "ligament", 3, units::degree_t{90}, 1, frc::Color8Bit{255, 255, 255});
+  frc::SmartDashboard::PutData("mechanism", &mechanism);
+  EXPECT_EQ(ligament->GetAngle(), angleEntry.GetDouble(0.0));
+  EXPECT_EQ(ligament->GetColor().HexString(), colorEntry.GetString(""));
+  EXPECT_EQ(ligament->GetLength(), lengthEntry.GetDouble(0.0));
+  EXPECT_EQ(ligament->GetLineWeight(), weightEntry.GetDouble(0.0));
+  ligament->SetAngle(units::degree_t{45});
+  ligament->SetColor({0, 0, 0});
+  ligament->SetLength(2);
+  ligament->SetLineWeight(4);
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_EQ(ligament->GetAngle(), angleEntry.GetDouble(0.0));
+  EXPECT_EQ(ligament->GetColor().HexString(), colorEntry.GetString(""));
+  EXPECT_EQ(ligament->GetLength(), lengthEntry.GetDouble(0.0));
+  EXPECT_EQ(ligament->GetLineWeight(), weightEntry.GetDouble(0.0));
+  angleEntry.SetDouble(22.5);
+  colorEntry.SetString("#FF00FF");
+  lengthEntry.SetDouble(4.0);
+  weightEntry.SetDouble(6.0);
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_EQ(ligament->GetAngle(), angleEntry.GetDouble(0.0));
+  EXPECT_EQ(ligament->GetColor().HexString(), colorEntry.GetString(""));
+  EXPECT_EQ(ligament->GetLength(), lengthEntry.GetDouble(0.0));
+  EXPECT_EQ(ligament->GetLineWeight(), weightEntry.GetDouble(0.0));
+}
diff --git a/third_party/allwpilib/wpilibc/src/test/native/cpp/smartdashboard/SendableChooserTest.cpp b/third_party/allwpilib/wpilibc/src/test/native/cpp/smartdashboard/SendableChooserTest.cpp
new file mode 100644
index 0000000..610afb8
--- /dev/null
+++ b/third_party/allwpilib/wpilibc/src/test/native/cpp/smartdashboard/SendableChooserTest.cpp
@@ -0,0 +1,81 @@
+// 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 <frc/smartdashboard/SendableChooser.h>
+#include <frc/smartdashboard/SmartDashboard.h>
+
+#include <string>
+
+#include <fmt/core.h>
+#include <gtest/gtest.h>
+#include <networktables/NetworkTableInstance.h>
+#include <networktables/StringTopic.h>
+
+class SendableChooserTest : public ::testing::TestWithParam<int> {};
+
+TEST_P(SendableChooserTest, ReturnsSelected) {
+  frc::SendableChooser<int> chooser;
+
+  for (int i = 1; i <= 3; i++) {
+    chooser.AddOption(std::to_string(i), i);
+  }
+  chooser.SetDefaultOption("0", 0);
+
+  auto pub =
+      nt::NetworkTableInstance::GetDefault()
+          .GetStringTopic(fmt::format(
+              "/SmartDashboard/ReturnsSelectedChooser{}/selected", GetParam()))
+          .Publish();
+
+  frc::SmartDashboard::PutData(
+      fmt::format("ReturnsSelectedChooser{}", GetParam()), &chooser);
+  frc::SmartDashboard::UpdateValues();
+  pub.Set(std::to_string(GetParam()));
+  frc::SmartDashboard::UpdateValues();
+  EXPECT_EQ(GetParam(), chooser.GetSelected());
+}
+
+TEST(SendableChooserTest, DefaultIsReturnedOnNoSelect) {
+  frc::SendableChooser<int> chooser;
+
+  for (int i = 1; i <= 3; i++) {
+    chooser.AddOption(std::to_string(i), i);
+  }
+
+  // Use 4 here rather than 0 to make sure it's not default-init int.
+  chooser.SetDefaultOption("4", 4);
+
+  EXPECT_EQ(4, chooser.GetSelected());
+}
+
+TEST(SendableChooserTest,
+     DefaultConstructableIsReturnedOnNoSelectAndNoDefault) {
+  frc::SendableChooser<int> chooser;
+
+  for (int i = 1; i <= 3; i++) {
+    chooser.AddOption(std::to_string(i), i);
+  }
+
+  EXPECT_EQ(0, chooser.GetSelected());
+}
+
+TEST(SendableChooserTest, ChangeListener) {
+  frc::SendableChooser<int> chooser;
+
+  for (int i = 1; i <= 3; i++) {
+    chooser.AddOption(std::to_string(i), i);
+  }
+  int currentVal = 0;
+  chooser.OnChange([&](int val) { currentVal = val; });
+
+  frc::SmartDashboard::PutData("ChangeListenerChooser", &chooser);
+  frc::SmartDashboard::UpdateValues();
+  frc::SmartDashboard::PutString("ChangeListenerChooser/selected", "3");
+  frc::SmartDashboard::UpdateValues();
+
+  EXPECT_EQ(3, currentVal);
+}
+
+INSTANTIATE_TEST_SUITE_P(SendableChooserTests, SendableChooserTest,
+                         ::testing::Values(0, 1, 2, 3));
diff --git a/third_party/allwpilib/wpilibcExamples/.styleguide b/third_party/allwpilib/wpilibcExamples/.styleguide
index 38c02cc..50ebdc9 100644
--- a/third_party/allwpilib/wpilibcExamples/.styleguide
+++ b/third_party/allwpilib/wpilibcExamples/.styleguide
@@ -11,8 +11,10 @@
 includeOtherLibs {
   ^cameraserver/
   ^cscore
+  ^fmt/
   ^frc/
   ^frc2/
+  ^gtest/
   ^hal/
   ^networktables/
   ^ntcore
diff --git a/third_party/allwpilib/wpilibcExamples/CMakeLists.txt b/third_party/allwpilib/wpilibcExamples/CMakeLists.txt
index 3b2413d..f46d1c2 100644
--- a/third_party/allwpilib/wpilibcExamples/CMakeLists.txt
+++ b/third_party/allwpilib/wpilibcExamples/CMakeLists.txt
@@ -3,8 +3,8 @@
 include(AddTest)
 include(SubDirList)
 
-SUBDIR_LIST(TEMPLATES ${CMAKE_SOURCE_DIR}/wpilibcExamples/src/main/cpp/templates)
-SUBDIR_LIST(EXAMPLES  ${CMAKE_SOURCE_DIR}/wpilibcExamples/src/main/cpp/examples)
+subdir_list(TEMPLATES ${CMAKE_SOURCE_DIR}/wpilibcExamples/src/main/cpp/templates)
+subdir_list(EXAMPLES  ${CMAKE_SOURCE_DIR}/wpilibcExamples/src/main/cpp/examples)
 
 foreach(example ${EXAMPLES})
   file(GLOB_RECURSE sources src/main/cpp/examples/${example}/cpp/*.cpp
@@ -12,7 +12,7 @@
   add_executable(${example} ${sources})
   wpilib_target_warnings(${example})
   target_include_directories(${example} PUBLIC src/main/cpp/examples/${example}/include)
-  target_link_libraries(${example} wpilibc wpilibNewCommands)
+  target_link_libraries(${example} apriltag wpilibc wpilibNewCommands romiVendordep xrpVendordep)
 
   if (WITH_TESTS AND EXISTS ${CMAKE_SOURCE_DIR}/wpilibcExamples/src/test/cpp/examples/${example})
     wpilib_add_test(${example} src/test/cpp/examples/${example}/cpp)
@@ -21,7 +21,7 @@
                                src/main/cpp/examples/${example}/include
                                src/test/cpp/examples/${example}/include)
     target_compile_definitions(${example}_test PUBLIC RUNNING_FRC_TESTS)
-    target_link_libraries(${example}_test wpilibc wpilibNewCommands gmock_main)
+    target_link_libraries(${example}_test apriltag wpilibc wpilibNewCommands romiVendordep xrpVendordep gmock_main)
   endif()
 endforeach()
 
@@ -31,5 +31,5 @@
   add_executable(${template} ${sources})
   wpilib_target_warnings(${template})
   target_include_directories(${template} PUBLIC src/main/cpp/templates/${template}/include)
-  target_link_libraries(${template} wpilibc wpilibNewCommands)
+  target_link_libraries(${template} wpilibc wpilibNewCommands romiVendordep xrpVendordep)
 endforeach()
diff --git a/third_party/allwpilib/wpilibcExamples/build.gradle b/third_party/allwpilib/wpilibcExamples/build.gradle
index 2ca6038..eb1aca5 100644
--- a/third_party/allwpilib/wpilibcExamples/build.gradle
+++ b/third_party/allwpilib/wpilibcExamples/build.gradle
@@ -60,6 +60,8 @@
                     return
                 }
                 lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'shared'
+                lib project: ':romiVendordep', library: 'romiVendordep', linkage: 'shared'
+                lib project: ':xrpVendordep', library: 'xrpVendordep', linkage: 'shared'
                 lib project: ':apriltag', library: 'apriltag', linkage: 'shared'
                 lib project: ':wpilibc', library: 'wpilibc', linkage: 'shared'
                 lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
@@ -89,6 +91,8 @@
                 targetBuildTypes 'debug'
                 binaries.all { binary ->
                     lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'shared'
+                    lib project: ':romiVendordep', library: 'romiVendordep', linkage: 'shared'
+                    lib project: ':xrpVendordep', library: 'xrpVendordep', linkage: 'shared'
                     lib project: ':wpilibc', library: 'wpilibc', linkage: 'shared'
                     lib project: ':apriltag', library: 'apriltag', linkage: 'shared'
                     lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
@@ -138,6 +142,8 @@
                 targetBuildTypes 'debug'
                 binaries.all { binary ->
                     lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'shared'
+                    lib project: ':romiVendordep', library: 'romiVendordep', linkage: 'shared'
+                    lib project: ':xrpVendordep', library: 'xrpVendordep', linkage: 'shared'
                     lib project: ':wpilibc', library: 'wpilibc', linkage: 'shared'
                     lib project: ':apriltag', library: 'apriltag', linkage: 'shared'
                     lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
@@ -204,6 +210,8 @@
     binaries {
         withType(GoogleTestTestSuiteBinarySpec) {
             lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'shared'
+            lib project: ':romiVendordep', library: 'romiVendordep', linkage: 'shared'
+            lib project: ':xrpVendordep', library: 'xrpVendordep', linkage: 'shared'
             lib project: ':wpilibc', library: 'wpilibc', linkage: 'shared'
             lib project: ':apriltag', library: 'apriltag', linkage: 'shared'
             lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
diff --git a/third_party/allwpilib/wpilibcExamples/publish.gradle b/third_party/allwpilib/wpilibcExamples/publish.gradle
index 3bc1a09..e0fba64 100644
--- a/third_party/allwpilib/wpilibcExamples/publish.gradle
+++ b/third_party/allwpilib/wpilibcExamples/publish.gradle
@@ -22,6 +22,10 @@
     from('src/main/cpp/examples') {
         into 'examples'
     }
+
+    from('src/test/cpp/examples') {
+        into 'examples_test'
+    }
 }
 
 task cppTemplatesZip(type: Zip) {
@@ -35,6 +39,10 @@
     from('src/main/cpp/templates') {
         into 'templates'
     }
+
+    from('src/test/cpp/templates') {
+        into 'templates_test'
+    }
 }
 
 task cppCommandsZip(type: Zip) {
@@ -48,6 +56,10 @@
     from('src/main/cpp/commands') {
         into 'commands'
     }
+
+    from('src/test/cpp/commands') {
+        into 'commands_test'
+    }
 }
 
 build.dependsOn cppTemplatesZip
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/command2/ReplaceMeCommand2.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/command2/ReplaceMeCommand2.h
index 8a1e8e0..d3071eb 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/command2/ReplaceMeCommand2.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/command2/ReplaceMeCommand2.h
@@ -4,18 +4,18 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 /**
  * An example command.
  *
- * <p>Note that this extends CommandHelper, rather extending CommandBase
+ * <p>Note that this extends CommandHelper, rather extending Command
  * directly; this is crucially important, or else the decorator functions in
  * Command will *not* work!
  */
 class ReplaceMeCommand2
-    : public frc2::CommandHelper<frc2::CommandBase, ReplaceMeCommand2> {
+    : public frc2::CommandHelper<frc2::Command, ReplaceMeCommand2> {
  public:
   ReplaceMeCommand2();
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/pidcommand/ReplaceMePIDCommand.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/pidcommand/ReplaceMePIDCommand.cpp
index 4a7d0e1..0bc1d74 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/pidcommand/ReplaceMePIDCommand.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/pidcommand/ReplaceMePIDCommand.cpp
@@ -8,7 +8,7 @@
 // For more information, see:
 // https://docs.wpilib.org/en/stable/docs/software/commandbased/convenience-features.html
 ReplaceMePIDCommand::ReplaceMePIDCommand()
-    : CommandHelper{frc2::PIDController{0, 0, 0},
+    : CommandHelper{frc::PIDController{0, 0, 0},
                     // This should return the measurement
                     [] { return 0; },
                     // This should return the setpoint (can also be a constant)
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/pidsubsystem2/ReplaceMePIDSubsystem2.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/pidsubsystem2/ReplaceMePIDSubsystem2.cpp
index 749b106..fe289f6 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/pidsubsystem2/ReplaceMePIDSubsystem2.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/pidsubsystem2/ReplaceMePIDSubsystem2.cpp
@@ -6,7 +6,7 @@
 
 ReplaceMePIDSubsystem2::ReplaceMePIDSubsystem2()
     // The PIDController used by the subsystem
-    : PIDSubsystem{frc2::PIDController{0, 0, 0}} {}
+    : PIDSubsystem{frc::PIDController{0, 0, 0}} {}
 
 void ReplaceMePIDSubsystem2::UseOutput(double output, double setpoint) {
   // Use the output here
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.cpp
index 3928d3b..c813168 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.cpp
@@ -13,13 +13,18 @@
 ReplaceMeTrapezoidProfileCommand::ReplaceMeTrapezoidProfileCommand()
     : CommandHelper
       // The profile to execute
-      (frc::TrapezoidProfile<units::meters>(
-           // The maximum velocity and acceleration of the profile
-           {5_mps, 5_mps_sq},
-           // The goal state of the profile
-           {10_m, 0_mps},
-           // The initial state of the profile
-           {0_m, 0_mps}),
-       [](frc::TrapezoidProfile<units::meters>::State state) {
-         // Use the computed intermediate trajectory state here
-       }) {}
+      (
+          frc::TrapezoidProfile<units::meters>(
+              // The maximum velocity and acceleration of the profile
+              {5_mps, 5_mps_sq}),
+          [](frc::TrapezoidProfile<units::meters>::State state) {
+            // Use the computed intermediate trajectory state here
+          },
+          // The goal state of the profile
+          [] {
+            return frc::TrapezoidProfile<units::meters>::State{10_m, 0_mps};
+          },
+          // The initial state of the profile
+          [] {
+            return frc::TrapezoidProfile<units::meters>::State{0_m, 0_mps};
+          }) {}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/AprilTagsVision/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/AprilTagsVision/cpp/Robot.cpp
new file mode 100644
index 0000000..0ed53fd
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/AprilTagsVision/cpp/Robot.cpp
@@ -0,0 +1,165 @@
+// 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 <cstdio>
+#include <span>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <cameraserver/CameraServer.h>
+#include <fmt/format.h>
+#include <frc/TimedRobot.h>
+#include <frc/apriltag/AprilTagDetection.h>
+#include <frc/apriltag/AprilTagDetector.h>
+#include <frc/apriltag/AprilTagPoseEstimator.h>
+#include <frc/geometry/Transform3d.h>
+#include <networktables/IntegerArrayTopic.h>
+#include <networktables/NetworkTableInstance.h>
+#include <opencv2/core/core.hpp>
+#include <opencv2/core/types.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+#include <units/angle.h>
+#include <units/length.h>
+
+/**
+ * This is a demo program showing the detection of AprilTags.
+ * The image is acquired from the USB camera, then any detected AprilTags
+ * are marked up on the image and sent to the dashboard.
+ *
+ * Be aware that the performance on this is much worse than a coprocessor
+ * solution!
+ */
+class Robot : public frc::TimedRobot {
+#if defined(__linux__) || defined(_WIN32)
+
+ private:
+  static void VisionThread() {
+    frc::AprilTagDetector detector;
+    // look for tag16h5, don't correct any error bits
+    detector.AddFamily("tag16h5", 0);
+
+    // Set up Pose Estimator - parameters are for a Microsoft Lifecam HD-3000
+    // (https://www.chiefdelphi.com/t/wpilib-apriltagdetector-sample-code/421411/21)
+    frc::AprilTagPoseEstimator::Config poseEstConfig = {
+        .tagSize = units::length::inch_t(6.0),
+        .fx = 699.3778103158814,
+        .fy = 677.7161226393544,
+        .cx = 345.6059345433618,
+        .cy = 207.12741326228522};
+    frc::AprilTagPoseEstimator estimator(poseEstConfig);
+
+    // Get the USB camera from CameraServer
+    cs::UsbCamera camera = frc::CameraServer::StartAutomaticCapture();
+    // Set the resolution
+    camera.SetResolution(640, 480);
+
+    // Get a CvSink. This will capture Mats from the Camera
+    cs::CvSink cvSink = frc::CameraServer::GetVideo();
+    // Setup a CvSource. This will send images back to the Dashboard
+    cs::CvSource outputStream =
+        frc::CameraServer::PutVideo("Detected", 640, 480);
+
+    // Mats are very memory expensive. Lets reuse this Mat.
+    cv::Mat mat;
+    cv::Mat grayMat;
+
+    // Instantiate once
+    std::vector<int64_t> tags;
+    cv::Scalar outlineColor{0, 255, 0};
+    cv::Scalar crossColor{0, 0, 255};
+
+    // We'll output to NT
+    auto tagsTable =
+        nt::NetworkTableInstance::GetDefault().GetTable("apriltags");
+    auto pubTags = tagsTable->GetIntegerArrayTopic("tags").Publish();
+
+    while (true) {
+      // Tell the CvSink to grab a frame from the camera and
+      // put it in the source mat.  If there is an error notify the
+      // output.
+      if (cvSink.GrabFrame(mat) == 0) {
+        // Send the output the error.
+        outputStream.NotifyError(cvSink.GetError());
+        // skip the rest of the current iteration
+        continue;
+      }
+
+      cv::cvtColor(mat, grayMat, cv::COLOR_BGR2GRAY);
+
+      cv::Size g_size = grayMat.size();
+      frc::AprilTagDetector::Results detections =
+          detector.Detect(g_size.width, g_size.height, grayMat.data);
+
+      // have not seen any tags yet
+      tags.clear();
+
+      for (const frc::AprilTagDetection* detection : detections) {
+        // remember we saw this tag
+        tags.push_back(detection->GetId());
+
+        // draw lines around the tag
+        for (int i = 0; i <= 3; i++) {
+          int j = (i + 1) % 4;
+          const frc::AprilTagDetection::Point pti = detection->GetCorner(i);
+          const frc::AprilTagDetection::Point ptj = detection->GetCorner(j);
+          line(mat, cv::Point(pti.x, pti.y), cv::Point(ptj.x, ptj.y),
+               outlineColor, 2);
+        }
+
+        // mark the center of the tag
+        const frc::AprilTagDetection::Point c = detection->GetCenter();
+        int ll = 10;
+        line(mat, cv::Point(c.x - ll, c.y), cv::Point(c.x + ll, c.y),
+             crossColor, 2);
+        line(mat, cv::Point(c.x, c.y - ll), cv::Point(c.x, c.y + ll),
+             crossColor, 2);
+
+        // identify the tag
+        putText(mat, std::to_string(detection->GetId()),
+                cv::Point(c.x + ll, c.y), cv::FONT_HERSHEY_SIMPLEX, 1,
+                crossColor, 3);
+
+        // determine pose
+        frc::Transform3d pose = estimator.Estimate(*detection);
+
+        // put pose into NT
+        frc::Rotation3d rotation = pose.Rotation();
+        tagsTable->GetEntry(fmt::format("pose_{}", detection->GetId()))
+            .SetDoubleArray(
+                {{ pose.X().value(),
+                   pose.Y().value(),
+                   pose.Z().value(),
+                   rotation.X().value(),
+                   rotation.Y().value(),
+                   rotation.Z().value() }});
+      }
+
+      // put list of tags onto NT
+      pubTags.Set(tags);
+
+      // Give the output stream a new image to display
+      outputStream.PutFrame(mat);
+    }
+  }
+#endif
+
+  void RobotInit() override {
+    // We need to run our vision program in a separate thread. If not, our robot
+    // program will not run.
+#if defined(__linux__) || defined(_WIN32)
+    std::thread visionThread(VisionThread);
+    visionThread.detach();
+#else
+    std::fputs("Vision only available on Linux or Windows.\n", stderr);
+    std::fflush(stderr);
+#endif
+  }
+};
+
+#ifndef RUNNING_FRC_TESTS
+int main() {
+  return frc::StartRobot<Robot>();
+}
+#endif
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp
index 2027d25..8641f4c 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp
@@ -2,151 +2,30 @@
 // 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 <numbers>
+#include "Robot.h"
 
-#include <frc/Encoder.h>
-#include <frc/Joystick.h>
-#include <frc/Preferences.h>
-#include <frc/RobotController.h>
-#include <frc/TimedRobot.h>
-#include <frc/controller/PIDController.h>
-#include <frc/motorcontrol/PWMSparkMax.h>
-#include <frc/simulation/BatterySim.h>
-#include <frc/simulation/EncoderSim.h>
-#include <frc/simulation/RoboRioSim.h>
-#include <frc/simulation/SingleJointedArmSim.h>
-#include <frc/smartdashboard/Mechanism2d.h>
-#include <frc/smartdashboard/MechanismLigament2d.h>
-#include <frc/smartdashboard/MechanismRoot2d.h>
-#include <frc/smartdashboard/SmartDashboard.h>
-#include <frc/system/plant/LinearSystemId.h>
-#include <frc/util/Color.h>
-#include <frc/util/Color8Bit.h>
-#include <units/angle.h>
-#include <units/moment_of_inertia.h>
+void Robot::SimulationPeriodic() {
+  m_arm.SimulationPeriodic();
+}
 
-/**
- * This is a sample program to demonstrate how to use a state-space controller
- * to control an arm.
- */
-class Robot : public frc::TimedRobot {
-  static constexpr int kMotorPort = 0;
-  static constexpr int kEncoderAChannel = 0;
-  static constexpr int kEncoderBChannel = 1;
-  static constexpr int kJoystickPort = 0;
+void Robot::TeleopInit() {
+  m_arm.LoadPreferences();
+}
 
-  static constexpr std::string_view kArmPositionKey = "ArmPosition";
-  static constexpr std::string_view kArmPKey = "ArmP";
-
-  // The P gain for the PID controller that drives this arm.
-  double kArmKp = 50.0;
-
-  units::degree_t armPosition = 75.0_deg;
-
-  // distance per pulse = (angle per revolution) / (pulses per revolution)
-  //  = (2 * PI rads) / (4096 pulses)
-  static constexpr double kArmEncoderDistPerPulse =
-      2.0 * std::numbers::pi / 4096.0;
-
-  // The arm gearbox represents a gearbox containing two Vex 775pro motors.
-  frc::DCMotor m_armGearbox = frc::DCMotor::Vex775Pro(2);
-
-  // Standard classes for controlling our arm
-  frc2::PIDController m_controller{kArmKp, 0, 0};
-  frc::Encoder m_encoder{kEncoderAChannel, kEncoderBChannel};
-  frc::PWMSparkMax m_motor{kMotorPort};
-  frc::Joystick m_joystick{kJoystickPort};
-
-  // Simulation classes help us simulate what's going on, including gravity.
-  // This sim represents an arm with 2 775s, a 600:1 reduction, a mass of 5kg,
-  // 30in overall arm length, range of motion in [-75, 255] degrees, and noise
-  // with a standard deviation of 1 encoder tick.
-  frc::sim::SingleJointedArmSim m_armSim{
-      m_armGearbox,
-      600.0,
-      frc::sim::SingleJointedArmSim::EstimateMOI(30_in, 5_kg),
-      30_in,
-      -75_deg,
-      255_deg,
-      5_kg,
-      true,
-      {kArmEncoderDistPerPulse}};
-  frc::sim::EncoderSim m_encoderSim{m_encoder};
-
-  // Create a Mechanism2d display of an Arm
-  frc::Mechanism2d m_mech2d{60, 60};
-  frc::MechanismRoot2d* m_armBase = m_mech2d.GetRoot("ArmBase", 30, 30);
-  frc::MechanismLigament2d* m_armTower =
-      m_armBase->Append<frc::MechanismLigament2d>(
-          "Arm Tower", 30, -90_deg, 6, frc::Color8Bit{frc::Color::kBlue});
-  frc::MechanismLigament2d* m_arm = m_armBase->Append<frc::MechanismLigament2d>(
-      "Arm", 30, m_armSim.GetAngle(), 6, frc::Color8Bit{frc::Color::kYellow});
-
- public:
-  void RobotInit() override {
-    m_encoder.SetDistancePerPulse(kArmEncoderDistPerPulse);
-
-    // Put Mechanism 2d to SmartDashboard
-    frc::SmartDashboard::PutData("Arm Sim", &m_mech2d);
-
-    // Set the Arm position setpoint and P constant to Preferences if the keys
-    // don't already exist
-    if (!frc::Preferences::ContainsKey(kArmPositionKey)) {
-      frc::Preferences::SetDouble(kArmPositionKey, armPosition.value());
-    }
-    if (!frc::Preferences::ContainsKey(kArmPKey)) {
-      frc::Preferences::SetDouble(kArmPKey, kArmKp);
-    }
+void Robot::TeleopPeriodic() {
+  if (m_joystick.GetTrigger()) {
+    // Here, we run PID control like normal.
+    m_arm.ReachSetpoint();
+  } else {
+    // Otherwise, we disable the motor.
+    m_arm.Stop();
   }
+}
 
-  void SimulationPeriodic() override {
-    // In this method, we update our simulation of what our arm is doing
-    // First, we set our "inputs" (voltages)
-    m_armSim.SetInput(frc::Vectord<1>{m_motor.Get() *
-                                      frc::RobotController::GetInputVoltage()});
-
-    // Next, we update it. The standard loop time is 20ms.
-    m_armSim.Update(20_ms);
-
-    // Finally, we set our simulated encoder's readings and simulated battery
-    // voltage
-    m_encoderSim.SetDistance(m_armSim.GetAngle().value());
-    // SimBattery estimates loaded battery voltages
-    frc::sim::RoboRioSim::SetVInVoltage(
-        frc::sim::BatterySim::Calculate({m_armSim.GetCurrentDraw()}));
-
-    // Update the Mechanism Arm angle based on the simulated arm angle
-    m_arm->SetAngle(m_armSim.GetAngle());
-  }
-
-  void TeleopInit() override {
-    // Read Preferences for Arm setpoint and kP on entering Teleop
-    armPosition = units::degree_t{
-        frc::Preferences::GetDouble(kArmPositionKey, armPosition.value())};
-    if (kArmKp != frc::Preferences::GetDouble(kArmPKey, kArmKp)) {
-      kArmKp = frc::Preferences::GetDouble(kArmPKey, kArmKp);
-      m_controller.SetP(kArmKp);
-    }
-  }
-
-  void TeleopPeriodic() override {
-    if (m_joystick.GetTrigger()) {
-      // Here, we run PID control like normal, with a setpoint read from
-      // preferences in degrees.
-      double pidOutput = m_controller.Calculate(
-          m_encoder.GetDistance(), (units::radian_t{armPosition}.value()));
-      m_motor.SetVoltage(units::volt_t{pidOutput});
-    } else {
-      // Otherwise, we disable the motor.
-      m_motor.Set(0.0);
-    }
-  }
-
-  void DisabledInit() override {
-    // This just makes sure that our simulation code knows that the motor's off.
-    m_motor.Set(0.0);
-  }
-};
+void Robot::DisabledInit() {
+  // This just makes sure that our simulation code knows that the motor's off.
+  m_arm.Stop();
+}
 
 #ifndef RUNNING_FRC_TESTS
 int main() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/subsystems/Arm.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/subsystems/Arm.cpp
new file mode 100644
index 0000000..95934ce
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/subsystems/Arm.cpp
@@ -0,0 +1,64 @@
+// 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 "subsystems/Arm.h"
+
+#include <frc/Preferences.h>
+#include <frc/RobotController.h>
+#include <frc/StateSpaceUtil.h>
+#include <frc/smartdashboard/SmartDashboard.h>
+
+Arm::Arm() {
+  m_encoder.SetDistancePerPulse(kArmEncoderDistPerPulse);
+
+  // Put Mechanism 2d to SmartDashboard
+  frc::SmartDashboard::PutData("Arm Sim", &m_mech2d);
+
+  // Set the Arm position setpoint and P constant to Preferences if the keys
+  // don't already exist
+  frc::Preferences::InitDouble(kArmPositionKey, m_armSetpoint.value());
+  frc::Preferences::InitDouble(kArmPKey, m_armKp);
+}
+
+void Arm::SimulationPeriodic() {
+  // In this method, we update our simulation of what our arm is doing
+  // First, we set our "inputs" (voltages)
+  m_armSim.SetInput(
+      frc::Vectord<1>{m_motor.Get() * frc::RobotController::GetInputVoltage()});
+
+  // Next, we update it. The standard loop time is 20ms.
+  m_armSim.Update(20_ms);
+
+  // Finally, we set our simulated encoder's readings and simulated battery
+  // voltage
+  m_encoderSim.SetDistance(m_armSim.GetAngle().value());
+  // SimBattery estimates loaded battery voltages
+  frc::sim::RoboRioSim::SetVInVoltage(
+      frc::sim::BatterySim::Calculate({m_armSim.GetCurrentDraw()}));
+
+  // Update the Mechanism Arm angle based on the simulated arm angle
+  m_arm->SetAngle(m_armSim.GetAngle());
+}
+
+void Arm::LoadPreferences() {
+  // Read Preferences for Arm setpoint and kP on entering Teleop
+  m_armSetpoint = units::degree_t{
+      frc::Preferences::GetDouble(kArmPositionKey, m_armSetpoint.value())};
+  if (m_armKp != frc::Preferences::GetDouble(kArmPKey, m_armKp)) {
+    m_armKp = frc::Preferences::GetDouble(kArmPKey, m_armKp);
+    m_controller.SetP(m_armKp);
+  }
+}
+
+void Arm::ReachSetpoint() {
+  // Here, we run PID control like normal, with a setpoint read from
+  // preferences in degrees.
+  double pidOutput = m_controller.Calculate(
+      m_encoder.GetDistance(), (units::radian_t{m_armSetpoint}.value()));
+  m_motor.SetVoltage(units::volt_t{pidOutput});
+}
+
+void Arm::Stop() {
+  m_motor.Set(0.0);
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/Constants.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/Constants.h
new file mode 100644
index 0000000..0018a8a
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/Constants.h
@@ -0,0 +1,46 @@
+// 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 <numbers>
+
+#include <units/angle.h>
+#include <units/length.h>
+#include <units/mass.h>
+#include <units/time.h>
+#include <units/velocity.h>
+#include <units/voltage.h>
+
+/**
+ * The Constants header provides a convenient place for teams to hold robot-wide
+ * numerical or bool constants.  This should not be used for any other purpose.
+ *
+ * It is generally a good idea to place constants into subsystem- or
+ * command-specific namespaces within this header, which can then be used where
+ * they are needed.
+ */
+
+static constexpr int kMotorPort = 0;
+static constexpr int kEncoderAChannel = 0;
+static constexpr int kEncoderBChannel = 1;
+static constexpr int kJoystickPort = 0;
+
+static constexpr std::string_view kArmPositionKey = "ArmPosition";
+static constexpr std::string_view kArmPKey = "ArmP";
+
+static constexpr double kDefaultArmKp = 50.0;
+static constexpr units::degree_t kDefaultArmSetpoint = 75.0_deg;
+
+static constexpr units::radian_t kMinAngle = -75.0_deg;
+static constexpr units::radian_t kMaxAngle = 255.0_deg;
+
+static constexpr double kArmReduction = 200.0;
+static constexpr units::kilogram_t kArmMass = 8.0_kg;
+static constexpr units::meter_t kArmLength = 30.0_in;
+
+// distance per pulse = (angle per revolution) / (pulses per revolution)
+//  = (2 * PI rads) / (4096 pulses)
+static constexpr double kArmEncoderDistPerPulse =
+    2.0 * std::numbers::pi / 4096.0;
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/Robot.h
new file mode 100644
index 0000000..fdcb5ce
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/Robot.h
@@ -0,0 +1,26 @@
+// 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 <frc/Joystick.h>
+#include <frc/TimedRobot.h>
+
+#include "subsystems/Arm.h"
+
+/**
+ * This is a sample program to demonstrate the use of arm simulation.
+ */
+class Robot : public frc::TimedRobot {
+ public:
+  void RobotInit() override {}
+  void SimulationPeriodic() override;
+  void TeleopInit() override;
+  void TeleopPeriodic() override;
+  void DisabledInit() override;
+
+ private:
+  frc::Joystick m_joystick{kJoystickPort};
+  Arm m_arm;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/subsystems/Arm.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/subsystems/Arm.h
new file mode 100644
index 0000000..6c7a3a7
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/subsystems/Arm.h
@@ -0,0 +1,68 @@
+// 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 <frc/Encoder.h>
+#include <frc/controller/ArmFeedforward.h>
+#include <frc/controller/PIDController.h>
+#include <frc/motorcontrol/PWMSparkMax.h>
+#include <frc/simulation/BatterySim.h>
+#include <frc/simulation/EncoderSim.h>
+#include <frc/simulation/PWMSim.h>
+#include <frc/simulation/RoboRioSim.h>
+#include <frc/simulation/SingleJointedArmSim.h>
+#include <frc/smartdashboard/Mechanism2d.h>
+#include <frc/smartdashboard/MechanismLigament2d.h>
+#include <frc/smartdashboard/MechanismRoot2d.h>
+#include <units/length.h>
+
+#include "Constants.h"
+
+class Arm {
+ public:
+  Arm();
+  void SimulationPeriodic();
+  void LoadPreferences();
+  void ReachSetpoint();
+  void Stop();
+
+ private:
+  // The P gain for the PID controller that drives this arm.
+  double m_armKp = kDefaultArmKp;
+  units::degree_t m_armSetpoint = kDefaultArmSetpoint;
+
+  // The arm gearbox represents a gearbox containing two Vex 775pro motors.
+  frc::DCMotor m_armGearbox = frc::DCMotor::Vex775Pro(2);
+
+  // Standard classes for controlling our arm
+  frc::PIDController m_controller{m_armKp, 0, 0};
+  frc::Encoder m_encoder{kEncoderAChannel, kEncoderBChannel};
+  frc::PWMSparkMax m_motor{kMotorPort};
+
+  // Simulation classes help us simulate what's going on, including gravity.
+  // This sim represents an arm with 2 775s, a 600:1 reduction, a mass of 5kg,
+  // 30in overall arm length, range of motion in [-75, 255] degrees, and noise
+  // with a standard deviation of 1 encoder tick.
+  frc::sim::SingleJointedArmSim m_armSim{
+      m_armGearbox,
+      kArmReduction,
+      frc::sim::SingleJointedArmSim::EstimateMOI(kArmLength, kArmMass),
+      kArmLength,
+      kMinAngle,
+      kMaxAngle,
+      true,
+      0_deg,
+      {kArmEncoderDistPerPulse}};
+  frc::sim::EncoderSim m_encoderSim{m_encoder};
+
+  // Create a Mechanism2d display of an Arm
+  frc::Mechanism2d m_mech2d{60, 60};
+  frc::MechanismRoot2d* m_armBase = m_mech2d.GetRoot("ArmBase", 30, 30);
+  frc::MechanismLigament2d* m_armTower =
+      m_armBase->Append<frc::MechanismLigament2d>(
+          "Arm Tower", 30, -90_deg, 6, frc::Color8Bit{frc::Color::kBlue});
+  frc::MechanismLigament2d* m_arm = m_armBase->Append<frc::MechanismLigament2d>(
+      "Arm", 30, m_armSim.GetAngle(), 6, frc::Color8Bit{frc::Color::kYellow});
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h
index b4ad1db..54b2e26 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h
@@ -69,8 +69,8 @@
   frc::Encoder m_leftEncoder{0, 1};
   frc::Encoder m_rightEncoder{2, 3};
 
-  frc2::PIDController m_leftPIDController{1.0, 0.0, 0.0};
-  frc2::PIDController m_rightPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_leftPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_rightPIDController{1.0, 0.0, 0.0};
 
   frc::AnalogGyro m_gyro{0};
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp
index 866d62d..725074a 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp
@@ -4,10 +4,31 @@
 
 #include "Drivetrain.h"
 
-#include <frc/Timer.h>
-
 #include "ExampleGlobalMeasurementSensor.h"
 
+Drivetrain::Drivetrain() {
+  // We need to invert one side of the drivetrain so that positive voltages
+  // result in both sides moving forward. Depending on how your robot's
+  // gearbox is constructed, you might have to invert the left side instead.
+  m_rightGroup.SetInverted(true);
+
+  m_gyro.Reset();
+
+  // Set the distance per pulse for the drive encoders. We can simply use the
+  // distance traveled for one rotation of the wheel divided by the encoder
+  // resolution.
+  m_leftEncoder.SetDistancePerPulse(
+      (2 * std::numbers::pi * kWheelRadius / kEncoderResolution).value());
+  m_rightEncoder.SetDistancePerPulse(
+      (2 * std::numbers::pi * kWheelRadius / kEncoderResolution).value());
+
+  m_leftEncoder.Reset();
+  m_rightEncoder.Reset();
+
+  frc::SmartDashboard::PutData("FieldSim", &m_fieldSim);
+  frc::SmartDashboard::PutData("Approximation", &m_fieldApproximation);
+}
+
 void Drivetrain::SetSpeeds(const frc::DifferentialDriveWheelSpeeds& speeds) {
   const auto leftFeedforward = m_feedforward.Calculate(speeds.left);
   const auto rightFeedforward = m_feedforward.Calculate(speeds.right);
@@ -25,16 +46,86 @@
   SetSpeeds(m_kinematics.ToWheelSpeeds({xSpeed, 0_mps, rot}));
 }
 
+void Drivetrain::PublishCameraToObject(
+    frc::Pose3d objectInField, frc::Transform3d robotToCamera,
+    nt::DoubleArrayEntry& cameraToObjectEntry,
+    frc::sim::DifferentialDrivetrainSim drivetrainSimulator) {
+  frc::Pose3d robotInField{drivetrainSimulator.GetPose()};
+  frc::Pose3d cameraInField = robotInField + robotToCamera;
+  frc::Transform3d cameraToObject{cameraInField, objectInField};
+
+  // Publishes double array with Translation3D elements {x, y, z} and Rotation3D
+  // elements {w, x, y, z} which describe the cameraToObject transformation.
+  std::array<double, 7> val{cameraToObject.X().value(),
+                            cameraToObject.Y().value(),
+                            cameraToObject.Z().value(),
+                            cameraToObject.Rotation().GetQuaternion().W(),
+                            cameraToObject.Rotation().GetQuaternion().X(),
+                            cameraToObject.Rotation().GetQuaternion().Y(),
+                            cameraToObject.Rotation().GetQuaternion().Z()};
+  cameraToObjectEntry.Set(val);
+}
+
+frc::Pose3d Drivetrain::ObjectToRobotPose(
+    frc::Pose3d objectInField, frc::Transform3d robotToCamera,
+    nt::DoubleArrayEntry& cameraToObjectEntry) {
+  std::vector<double> val{cameraToObjectEntry.Get()};
+
+  // Reconstruct cameraToObject Transform3D from networktables.
+  frc::Translation3d translation{units::meter_t{val[0]}, units::meter_t{val[1]},
+                                 units::meter_t{val[2]}};
+  frc::Rotation3d rotation{frc::Quaternion{val[3], val[4], val[5], val[6]}};
+  frc::Transform3d cameraToObject{translation, rotation};
+
+  return frc::ObjectToRobotPose(objectInField, cameraToObject, robotToCamera);
+}
+
 void Drivetrain::UpdateOdometry() {
   m_poseEstimator.Update(m_gyro.GetRotation2d(),
                          units::meter_t{m_leftEncoder.GetDistance()},
                          units::meter_t{m_rightEncoder.GetDistance()});
 
-  // Also apply vision measurements. We use 0.3 seconds in the past as an
-  // example -- on a real robot, this must be calculated based either on latency
-  // or timestamps.
-  m_poseEstimator.AddVisionMeasurement(
-      ExampleGlobalMeasurementSensor::GetEstimatedGlobalPose(
-          m_poseEstimator.GetEstimatedPosition()),
-      frc::Timer::GetFPGATimestamp() - 0.3_s);
+  // Publish cameraToObject transformation to networktables --this would
+  // normally be handled by the computer vision solution.
+  PublishCameraToObject(m_objectInField, m_robotToCamera,
+                        m_cameraToObjectEntryRef, m_drivetrainSimulator);
+
+  // Compute the robot's field-relative position exclusively from vision
+  // measurements.
+  frc::Pose3d visionMeasurement3d = ObjectToRobotPose(
+      m_objectInField, m_robotToCamera, m_cameraToObjectEntryRef);
+
+  // Convert robot's pose from Pose3d to Pose2d needed to apply vision
+  // measurements.
+  frc::Pose2d visionMeasurement2d = visionMeasurement3d.ToPose2d();
+
+  // Apply vision measurements. For simulation purposes only, we don't input a
+  // latency delay -- on a real robot, this must be calculated based either on
+  // known latency or timestamps.
+  m_poseEstimator.AddVisionMeasurement(visionMeasurement2d,
+                                       frc::Timer::GetFPGATimestamp());
+}
+
+void Drivetrain::SimulationPeriodic() {
+  // To update our simulation, we set motor voltage inputs, update the
+  // simulation, and write the simulated positions and velocities to our
+  // simulated encoder and gyro.
+  m_drivetrainSimulator.SetInputs(units::volt_t{m_leftGroup.Get()} *
+                                      frc::RobotController::GetInputVoltage(),
+                                  units::volt_t{m_rightGroup.Get()} *
+                                      frc::RobotController::GetInputVoltage());
+  m_drivetrainSimulator.Update(20_ms);
+
+  m_leftEncoderSim.SetDistance(m_drivetrainSimulator.GetLeftPosition().value());
+  m_leftEncoderSim.SetRate(m_drivetrainSimulator.GetLeftVelocity().value());
+  m_rightEncoderSim.SetDistance(
+      m_drivetrainSimulator.GetRightPosition().value());
+  m_rightEncoderSim.SetRate(m_drivetrainSimulator.GetRightVelocity().value());
+  m_gyroSim.SetAngle(-m_drivetrainSimulator.GetHeading().Degrees().value());
+}
+
+void Drivetrain::Periodic() {
+  UpdateOdometry();
+  m_fieldSim.SetRobotPose(m_drivetrainSimulator.GetPose());
+  m_fieldApproximation.SetRobotPose(m_poseEstimator.GetEstimatedPosition());
 }
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Robot.cpp
index e9119c8..324e03f 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Robot.cpp
@@ -15,6 +15,8 @@
     m_drive.UpdateOdometry();
   }
 
+  void RobotPeriodic() override { m_drive.Periodic(); }
+
   void TeleopPeriodic() override {
     // Get the x speed. We are inverting this because Xbox controllers return
     // negative values when we push forward.
@@ -31,6 +33,8 @@
     m_drive.Drive(xSpeed, rot);
   }
 
+  void SimulationPeriodic() override { m_drive.SimulationPeriodic(); }
+
  private:
   frc::XboxController m_controller{0};
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h
index 3001763..ac4de4c 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h
@@ -7,13 +7,30 @@
 #include <numbers>
 
 #include <frc/AnalogGyro.h>
+#include <frc/ComputerVisionUtil.h>
 #include <frc/Encoder.h>
+#include <frc/RobotController.h>
+#include <frc/Timer.h>
+#include <frc/apriltag/AprilTagFieldLayout.h>
+#include <frc/apriltag/AprilTagFields.h>
 #include <frc/controller/PIDController.h>
 #include <frc/controller/SimpleMotorFeedforward.h>
 #include <frc/estimator/DifferentialDrivePoseEstimator.h>
+#include <frc/geometry/Pose2d.h>
+#include <frc/geometry/Pose3d.h>
+#include <frc/geometry/Quaternion.h>
+#include <frc/geometry/Transform3d.h>
 #include <frc/kinematics/DifferentialDriveKinematics.h>
 #include <frc/motorcontrol/MotorControllerGroup.h>
 #include <frc/motorcontrol/PWMSparkMax.h>
+#include <frc/simulation/AnalogGyroSim.h>
+#include <frc/simulation/DifferentialDrivetrainSim.h>
+#include <frc/simulation/EncoderSim.h>
+#include <frc/smartdashboard/Field2d.h>
+#include <frc/smartdashboard/SmartDashboard.h>
+#include <frc/system/plant/LinearSystemId.h>
+#include <networktables/DoubleArrayTopic.h>
+#include <networktables/NetworkTableInstance.h>
 #include <units/angle.h>
 #include <units/angular_velocity.h>
 #include <units/length.h>
@@ -24,40 +41,100 @@
  */
 class Drivetrain {
  public:
-  Drivetrain() {
-    // We need to invert one side of the drivetrain so that positive voltages
-    // result in both sides moving forward. Depending on how your robot's
-    // gearbox is constructed, you might have to invert the left side instead.
-    m_rightGroup.SetInverted(true);
-
-    m_gyro.Reset();
-    // Set the distance per pulse for the drive encoders. We can simply use the
-    // distance traveled for one rotation of the wheel divided by the encoder
-    // resolution.
-    m_leftEncoder.SetDistancePerPulse(
-        2 * std::numbers::pi * kWheelRadius.value() / kEncoderResolution);
-    m_rightEncoder.SetDistancePerPulse(
-        2 * std::numbers::pi * kWheelRadius.value() / kEncoderResolution);
-
-    m_leftEncoder.Reset();
-    m_rightEncoder.Reset();
-  }
+  Drivetrain();
 
   static constexpr units::meters_per_second_t kMaxSpeed =
       3.0_mps;  // 3 meters per second
   static constexpr units::radians_per_second_t kMaxAngularSpeed{
       std::numbers::pi};  // 1/2 rotation per second
 
+  /**
+   * Sets the desired wheel speeds.
+   *
+   * @param speeds The desired wheel speeds.
+   */
   void SetSpeeds(const frc::DifferentialDriveWheelSpeeds& speeds);
+
+  /** Drives the robot with the given linear velocity and angular velocity.
+   *
+   * @param xSpeed Linear velocity.
+   * @param rot Angular Velocity.
+   */
   void Drive(units::meters_per_second_t xSpeed,
              units::radians_per_second_t rot);
+
+  /**
+   * Updates the field-relative position.
+   */
   void UpdateOdometry();
 
+  /**
+   * This function is called periodically during simulation. */
+  void SimulationPeriodic();
+
+  /** This function is called periodically, regardless of mode. */
+  void Periodic();
+
+  /**
+   * Computes and publishes to a networktables topic the transformation from
+   * the camera's pose to the object's pose. This function exists solely for the
+   * purposes of simulation, and this would normally be handled by computer
+   * vision.
+   *
+   * <p>The object could be a target or a fiducial marker.
+   *
+   * @param objectInField The object's field-relative position.
+   * @param robotToCamera The transformation from the robot's pose to the
+   * camera's pose.
+   * @param cameraToObjectEntry The networktables entry publishing and querying
+   * example computer vision measurements.
+   * @param drivetrainSimulation A DifferentialDrivetrainSim modeling the
+   * robot's drivetrain.
+   */
+  void PublishCameraToObject(
+      frc::Pose3d objectInField, frc::Transform3d robotToCamera,
+      nt::DoubleArrayEntry& cameraToObjectEntry,
+      frc::sim::DifferentialDrivetrainSim drivetrainSimulator);
+
+  /**
+   * Queries the camera-to-object transformation from networktables to compute
+   * the robot's field-relative pose from vision measurements.
+   *
+   * <p>The object could be a target or a fiducial marker.
+   *
+   * @param objectInField The object's field-relative position.
+   * @param robotToCamera The transformation from the robot's pose to the
+   * camera's pose.
+   * @param cameraToObjectEntry The networktables entry publishing and querying
+   * example computer vision measurements.
+   */
+  frc::Pose3d ObjectToRobotPose(frc::Pose3d objectInField,
+                                frc::Transform3d robotToCamera,
+                                nt::DoubleArrayEntry& cameraToObjectEntry);
+
  private:
   static constexpr units::meter_t kTrackWidth = 0.381_m * 2;
   static constexpr units::meter_t kWheelRadius = 0.0508_m;
   static constexpr int kEncoderResolution = 4096;
 
+  static constexpr std::array<double, 7> kDefaultVal{0.0, 0.0, 0.0, 0.0,
+                                                     0.0, 0.0, 0.0};
+
+  frc::Transform3d m_robotToCamera{
+      frc::Translation3d{1_m, 1_m, 1_m},
+      frc::Rotation3d{0_rad, 0_rad, units::radian_t{std::numbers::pi / 2}}};
+
+  nt::NetworkTableInstance m_inst{nt::NetworkTableInstance::GetDefault()};
+  nt::DoubleArrayTopic m_cameraToObjectTopic{
+      m_inst.GetDoubleArrayTopic("m_cameraToObjectTopic")};
+  nt::DoubleArrayEntry m_cameraToObjectEntry =
+      m_cameraToObjectTopic.GetEntry(kDefaultVal);
+  nt::DoubleArrayEntry& m_cameraToObjectEntryRef = m_cameraToObjectEntry;
+
+  frc::AprilTagFieldLayout m_aprilTagFieldLayout{
+      frc::LoadAprilTagLayoutField(frc::AprilTagField::k2022RapidReact)};
+  frc::Pose3d m_objectInField{m_aprilTagFieldLayout.GetTagPose(0).value()};
+
   frc::PWMSparkMax m_leftLeader{1};
   frc::PWMSparkMax m_leftFollower{2};
   frc::PWMSparkMax m_rightLeader{3};
@@ -69,8 +146,8 @@
   frc::Encoder m_leftEncoder{0, 1};
   frc::Encoder m_rightEncoder{2, 3};
 
-  frc2::PIDController m_leftPIDController{1.0, 0.0, 0.0};
-  frc2::PIDController m_rightPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_leftPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_rightPIDController{1.0, 0.0, 0.0};
 
   frc::AnalogGyro m_gyro{0};
 
@@ -90,4 +167,16 @@
   // Gains are for example purposes only - must be determined for your own
   // robot!
   frc::SimpleMotorFeedforward<units::meters> m_feedforward{1_V, 3_V / 1_mps};
+
+  // Simulation classes
+  frc::sim::AnalogGyroSim m_gyroSim{m_gyro};
+  frc::sim::EncoderSim m_leftEncoderSim{m_leftEncoder};
+  frc::sim::EncoderSim m_rightEncoderSim{m_rightEncoder};
+  frc::Field2d m_fieldSim;
+  frc::Field2d m_fieldApproximation;
+  frc::LinearSystem<2, 2, 2> m_drivetrainSystem =
+      frc::LinearSystemId::IdentifyDrivetrainSystem(
+          1.98_V / 1_mps, 0.2_V / 1_mps_sq, 1.5_V / 1_mps, 0.3_V / 1_mps_sq);
+  frc::sim::DifferentialDrivetrainSim m_drivetrainSimulator{
+      m_drivetrainSystem, kTrackWidth, frc::DCMotor::CIM(2), 8, 2_in};
 };
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DigitalCommunication/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DigitalCommunication/cpp/Robot.cpp
index 037fb1d..e95a0d7 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DigitalCommunication/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DigitalCommunication/cpp/Robot.cpp
@@ -2,45 +2,28 @@
 // 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 <frc/DigitalOutput.h>
+#include "Robot.h"
+
 #include <frc/DriverStation.h>
-#include <frc/TimedRobot.h>
 
-/**
- * This is a sample program demonstrating how to communicate to a light
- * controller from the robot code using the roboRIO's DIO ports.
- */
-class Robot : public frc::TimedRobot {
- public:
-  void RobotPeriodic() override {
-    // pull alliance port high if on red alliance, pull low if on blue alliance
-    m_allianceOutput.Set(frc::DriverStation::GetAlliance() ==
-                         frc::DriverStation::kRed);
+void Robot::RobotPeriodic() {
+  // pull alliance port high if on red alliance, pull low if on blue alliance
+  m_allianceOutput.Set(frc::DriverStation::GetAlliance() ==
+                       frc::DriverStation::kRed);
 
-    // pull enabled port high if enabled, low if disabled
-    m_enabledOutput.Set(frc::DriverStation::IsEnabled());
+  // pull enabled port high if enabled, low if disabled
+  m_enabledOutput.Set(frc::DriverStation::IsEnabled());
 
-    // pull auto port high if in autonomous, low if in teleop (or disabled)
-    m_autonomousOutput.Set(frc::DriverStation::IsAutonomous());
+  // pull auto port high if in autonomous, low if in teleop (or disabled)
+  m_autonomousOutput.Set(frc::DriverStation::IsAutonomous());
 
-    // pull alert port high if match time remaining is between 30 and 25 seconds
-    auto matchTime = frc::DriverStation::GetMatchTime();
-    m_alertOutput.Set(matchTime <= 30 && matchTime >= 25);
-  }
+  // pull alert port high if match time remaining is between 30 and 25 seconds
+  auto matchTime = frc::DriverStation::GetMatchTime();
+  m_alertOutput.Set(matchTime <= 30_s && matchTime >= 25_s);
+}
 
- private:
-  // define ports for communication with light controller
-  static constexpr int kAlliancePort = 0;
-  static constexpr int kEnabledPort = 1;
-  static constexpr int kAutonomousPort = 2;
-  static constexpr int kAlertPort = 3;
-
-  frc::DigitalOutput m_allianceOutput{kAlliancePort};
-  frc::DigitalOutput m_enabledOutput{kEnabledPort};
-  frc::DigitalOutput m_autonomousOutput{kAutonomousPort};
-  frc::DigitalOutput m_alertOutput{kAlertPort};
-};
-
+#ifndef RUNNING_FRC_TESTS
 int main() {
   return frc::StartRobot<Robot>();
 }
+#endif
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DigitalCommunication/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DigitalCommunication/include/Robot.h
new file mode 100644
index 0000000..17ec680
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DigitalCommunication/include/Robot.h
@@ -0,0 +1,31 @@
+// 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 <array>
+
+#include <frc/DigitalOutput.h>
+#include <frc/TimedRobot.h>
+
+/**
+ * This is a sample program demonstrating how to communicate to a light
+ * controller from the robot code using the roboRIO's DIO ports.
+ */
+class Robot : public frc::TimedRobot {
+ public:
+  // define ports for communication with light controller
+  static constexpr int kAlliancePort = 0;
+  static constexpr int kEnabledPort = 1;
+  static constexpr int kAutonomousPort = 2;
+  static constexpr int kAlertPort = 3;
+
+  void RobotPeriodic() override;
+
+ private:
+  frc::DigitalOutput m_allianceOutput{kAlliancePort};
+  frc::DigitalOutput m_enabledOutput{kEnabledPort};
+  frc::DigitalOutput m_autonomousOutput{kAutonomousPort};
+  frc::DigitalOutput m_alertOutput{kAlertPort};
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/RobotContainer.cpp
index 8c2423b..bcb7e73 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/RobotContainer.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/RobotContainer.cpp
@@ -40,13 +40,17 @@
       frc2::TrapezoidProfileCommand<units::meters>(
           frc::TrapezoidProfile<units::meters>(
               // Limit the max acceleration and velocity
-              {DriveConstants::kMaxSpeed, DriveConstants::kMaxAcceleration},
-              // End at desired position in meters; implicitly starts at 0
-              {3_m, 0_mps}),
+              {DriveConstants::kMaxSpeed, DriveConstants::kMaxAcceleration}),
           // Pipe the profile state to the drive
           [this](auto setpointState) {
             m_drive.SetDriveStates(setpointState, setpointState);
           },
+          // End at desired position in meters; implicitly starts at 0
+          [] {
+            return frc::TrapezoidProfile<units::meters>::State{3_m, 0_mps};
+          },
+          // Current position
+          [] { return frc::TrapezoidProfile<units::meters>::State{}; },
           // Require the drive
           {&m_drive})
           // Convert to CommandPtr
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/commands/DriveDistanceProfiled.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/commands/DriveDistanceProfiled.cpp
index 6780c33..8d45c91 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/commands/DriveDistanceProfiled.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/commands/DriveDistanceProfiled.cpp
@@ -13,13 +13,16 @@
     : CommandHelper{
           frc::TrapezoidProfile<units::meters>{
               // Limit the max acceleration and velocity
-              {kMaxSpeed, kMaxAcceleration},
-              // End at desired position in meters; implicitly starts at 0
-              {distance, 0_mps}},
+              {kMaxSpeed, kMaxAcceleration}},
           // Pipe the profile state to the drive
           [drive](auto setpointState) {
             drive->SetDriveStates(setpointState, setpointState);
           },
+          // End at desired position in meters; implicitly starts at 0
+          [distance] {
+            return frc::TrapezoidProfile<units::meters>::State{distance, 0_mps};
+          },
+          [] { return frc::TrapezoidProfile<units::meters>::State{}; },
           // Require the drive
           {drive}} {
   // Reset drive encoders since we're starting at 0
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialProfile/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialProfile/cpp/Robot.cpp
new file mode 100644
index 0000000..5d65a5a
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialProfile/cpp/Robot.cpp
@@ -0,0 +1,68 @@
+// 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 <numbers>
+
+#include <frc/Joystick.h>
+#include <frc/TimedRobot.h>
+#include <frc/controller/SimpleMotorFeedforward.h>
+#include <frc/trajectory/ExponentialProfile.h>
+#include <units/acceleration.h>
+#include <units/length.h>
+#include <units/time.h>
+#include <units/velocity.h>
+#include <units/voltage.h>
+
+#include "ExampleSmartMotorController.h"
+
+class Robot : public frc::TimedRobot {
+ public:
+  static constexpr units::second_t kDt = 20_ms;
+
+  Robot() {
+    // Note: These gains are fake, and will have to be tuned for your robot.
+    m_motor.SetPID(1.3, 0.0, 0.7);
+  }
+
+  void TeleopPeriodic() override {
+    if (m_joystick.GetRawButtonPressed(2)) {
+      m_goal = {5_m, 0_mps};
+    } else if (m_joystick.GetRawButtonPressed(3)) {
+      m_goal = {0_m, 0_mps};
+    }
+
+    // Retrieve the profiled setpoint for the next timestep. This setpoint moves
+    // toward the goal while obeying the constraints.
+    auto next = m_profile.Calculate(kDt, m_goal, m_setpoint);
+
+    // Send setpoint to offboard controller PID
+    m_motor.SetSetpoint(
+        ExampleSmartMotorController::PIDMode::kPosition,
+        m_setpoint.position.value(),
+        m_feedforward.Calculate(m_setpoint.velocity, next.velocity, kDt) /
+            12_V);
+
+    m_setpoint = next;
+  }
+
+ private:
+  frc::Joystick m_joystick{1};
+  ExampleSmartMotorController m_motor{1};
+  frc::SimpleMotorFeedforward<units::meters> m_feedforward{
+      // Note: These gains are fake, and will have to be tuned for your robot.
+      1_V, 1_V / 1_mps, 1_V / 1_mps_sq};
+
+  // Create a motion profile with the given maximum velocity and maximum
+  // acceleration constraints for the next setpoint.
+  frc::ExponentialProfile<units::meters, units::volts> m_profile{
+      {10_V, 1_V / 1_mps, 1_V / 1_mps_sq}};
+  frc::ExponentialProfile<units::meters, units::volts>::State m_goal;
+  frc::ExponentialProfile<units::meters, units::volts>::State m_setpoint;
+};
+
+#ifndef RUNNING_FRC_TESTS
+int main() {
+  return frc::StartRobot<Robot>();
+}
+#endif
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialProfile/include/ExampleSmartMotorController.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialProfile/include/ExampleSmartMotorController.h
new file mode 100644
index 0000000..5d55839
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialProfile/include/ExampleSmartMotorController.h
@@ -0,0 +1,82 @@
+// 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 <frc/motorcontrol/MotorController.h>
+
+/**
+ * A simplified stub class that simulates the API of a common "smart" motor
+ * controller.
+ *
+ * <p>Has no actual functionality.
+ */
+class ExampleSmartMotorController : public frc::MotorController {
+ public:
+  enum PIDMode { kPosition, kVelocity, kMovementWitchcraft };
+
+  /**
+   * Creates a new ExampleSmartMotorController.
+   *
+   * @param port The port for the controller.
+   */
+  explicit ExampleSmartMotorController(int port) {}
+
+  /**
+   * Example method for setting the PID gains of the smart controller.
+   *
+   * @param kp The proportional gain.
+   * @param ki The integral gain.
+   * @param kd The derivative gain.
+   */
+  void SetPID(double kp, double ki, double kd) {}
+
+  /**
+   * Example method for setting the setpoint of the smart controller in PID
+   * mode.
+   *
+   * @param mode The mode of the PID controller.
+   * @param setpoint The controller setpoint.
+   * @param arbFeedforward An arbitrary feedforward output (from -1 to 1).
+   */
+  void SetSetpoint(PIDMode mode, double setpoint, double arbFeedforward) {}
+
+  /**
+   * Places this motor controller in follower mode.
+   *
+   * @param leader The leader to follow.
+   */
+  void Follow(ExampleSmartMotorController leader) {}
+
+  /**
+   * Returns the encoder distance.
+   *
+   * @return The current encoder distance.
+   */
+  double GetEncoderDistance() { return 0; }
+
+  /**
+   * Returns the encoder rate.
+   *
+   * @return The current encoder rate.
+   */
+  double GetEncoderRate() { return 0; }
+
+  /**
+   * Resets the encoder to zero distance.
+   */
+  void ResetEncoder() {}
+
+  void Set(double speed) override {}
+
+  double Get() const override { return 0; }
+
+  void SetInverted(bool isInverted) override {}
+
+  bool GetInverted() const override { return false; }
+
+  void Disable() override {}
+
+  void StopMotor() override {}
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/cpp/Robot.cpp
new file mode 100644
index 0000000..a6cb55d
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/cpp/Robot.cpp
@@ -0,0 +1,44 @@
+// 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 "Robot.h"
+
+#include "Constants.h"
+
+void Robot::RobotPeriodic() {
+  // Update the telemetry, including mechanism visualization, regardless of
+  // mode.
+  m_elevator.UpdateTelemetry();
+}
+
+void Robot::SimulationPeriodic() {
+  // Update the simulation model.
+  m_elevator.SimulationPeriodic();
+}
+
+void Robot::TeleopInit() {
+  // This just makes sure that our simulation code knows that the motor's off.
+  m_elevator.Reset();
+}
+
+void Robot::TeleopPeriodic() {
+  if (m_joystick.GetTrigger()) {
+    // Here, we set the constant setpoint of 0.75 meters.
+    m_elevator.ReachGoal(Constants::kSetpoint);
+  } else {
+    // Otherwise, we update the setpoint to 0.
+    m_elevator.ReachGoal(Constants::kLowerSetpoint);
+  }
+}
+
+void Robot::DisabledInit() {
+  // This just makes sure that our simulation code knows that the motor's off.
+  m_elevator.Stop();
+}
+
+#ifndef RUNNING_FRC_TESTS
+int main() {
+  return frc::StartRobot<Robot>();
+}
+#endif
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/cpp/subsystems/Elevator.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/cpp/subsystems/Elevator.cpp
new file mode 100644
index 0000000..27832a6
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/cpp/subsystems/Elevator.cpp
@@ -0,0 +1,64 @@
+// 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 "subsystems/Elevator.h"
+
+#include <frc/RobotController.h>
+#include <frc/StateSpaceUtil.h>
+#include <frc/smartdashboard/SmartDashboard.h>
+
+Elevator::Elevator() {
+  m_encoder.SetDistancePerPulse(Constants::kArmEncoderDistPerPulse);
+
+  // Put Mechanism 2d to SmartDashboard
+  // To view the Elevator visualization, select Network Tables -> SmartDashboard
+  // -> Elevator Sim
+  frc::SmartDashboard::PutData("Elevator Sim", &m_mech2d);
+}
+
+void Elevator::SimulationPeriodic() {
+  // In this method, we update our simulation of what our elevator is doing
+  // First, we set our "inputs" (voltages)
+  m_elevatorSim.SetInput(frc::Vectord<1>{
+      m_motorSim.GetSpeed() * frc::RobotController::GetInputVoltage()});
+
+  // Next, we update it. The standard loop time is 20ms.
+  m_elevatorSim.Update(20_ms);
+
+  // Finally, we set our simulated encoder's readings and simulated battery
+  // voltage
+  m_encoderSim.SetDistance(m_elevatorSim.GetPosition().value());
+  // SimBattery estimates loaded battery voltages
+  frc::sim::RoboRioSim::SetVInVoltage(
+      frc::sim::BatterySim::Calculate({m_elevatorSim.GetCurrentDraw()}));
+}
+
+void Elevator::UpdateTelemetry() {
+  // Update the Elevator length based on the simulated elevator height
+  m_elevatorMech2d->SetLength(m_encoder.GetDistance());
+}
+
+void Elevator::ReachGoal(units::meter_t goal) {
+  frc::ExponentialProfile<units::meters, units::volts>::State goalState{goal,
+                                                                        0_mps};
+
+  auto next = m_profile.Calculate(20_ms, m_setpoint, goalState);
+
+  auto pidOutput = m_controller.Calculate(m_encoder.GetDistance(),
+                                          m_setpoint.position / 1_m);
+  auto feedforwardOutput =
+      m_feedforward.Calculate(m_setpoint.velocity, next.velocity, 20_ms);
+
+  m_motor.SetVoltage(units::volt_t{pidOutput} + feedforwardOutput);
+
+  m_setpoint = next;
+}
+
+void Elevator::Reset() {
+  m_setpoint = {m_encoder.GetDistance() * 1_m, 0_mps};
+}
+
+void Elevator::Stop() {
+  m_motor.Set(0.0);
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/include/Constants.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/include/Constants.h
new file mode 100644
index 0000000..7c53018
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/include/Constants.h
@@ -0,0 +1,57 @@
+// 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 <numbers>
+
+#include <units/acceleration.h>
+#include <units/angle.h>
+#include <units/length.h>
+#include <units/mass.h>
+#include <units/time.h>
+#include <units/velocity.h>
+#include <units/voltage.h>
+
+/**
+ * The Constants header provides a convenient place for teams to hold robot-wide
+ * numerical or bool constants.  This should not be used for any other purpose.
+ *
+ * It is generally a good idea to place constants into subsystem- or
+ * command-specific namespaces within this header, which can then be used where
+ * they are needed.
+ */
+
+namespace Constants {
+
+static constexpr int kMotorPort = 0;
+static constexpr int kEncoderAChannel = 0;
+static constexpr int kEncoderBChannel = 1;
+static constexpr int kJoystickPort = 0;
+
+static constexpr double kElevatorKp = 0.75;
+static constexpr double kElevatorKi = 0.0;
+static constexpr double kElevatorKd = 0.0;
+
+static constexpr units::volt_t kElevatorMaxV = 10_V;
+static constexpr units::volt_t kElevatorkS = 0.0_V;
+static constexpr units::volt_t kElevatorkG = 0.62_V;
+static constexpr auto kElevatorkV = 3.9_V / 1_mps;
+static constexpr auto kElevatorkA = 0.06_V / 1_mps_sq;
+
+static constexpr double kElevatorGearing = 5.0;
+static constexpr units::meter_t kElevatorDrumRadius = 1_in;
+static constexpr units::kilogram_t kCarriageMass = 12_lb;
+
+static constexpr units::meter_t kSetpoint = 42.875_in;
+static constexpr units::meter_t kLowerSetpoint = 15_in;
+static constexpr units::meter_t kMinElevatorHeight = 0_cm;
+static constexpr units::meter_t kMaxElevatorHeight = 50_in;
+
+// distance per pulse = (distance per revolution) / (pulses per revolution)
+//  = (Pi * D) / ppr
+static constexpr double kArmEncoderDistPerPulse =
+    2.0 * std::numbers::pi * kElevatorDrumRadius.value() / 4096.0;
+
+}  // namespace Constants
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/include/Robot.h
new file mode 100644
index 0000000..f103104
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/include/Robot.h
@@ -0,0 +1,27 @@
+// 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 <frc/Joystick.h>
+#include <frc/TimedRobot.h>
+
+#include "subsystems/Elevator.h"
+
+/**
+ * This is a sample program to demonstrate the use of elevator simulation.
+ */
+class Robot : public frc::TimedRobot {
+ public:
+  void RobotInit() override {}
+  void RobotPeriodic() override;
+  void SimulationPeriodic() override;
+  void TeleopInit() override;
+  void TeleopPeriodic() override;
+  void DisabledInit() override;
+
+ private:
+  frc::Joystick m_joystick{Constants::kJoystickPort};
+  Elevator m_elevator;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/include/subsystems/Elevator.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/include/subsystems/Elevator.h
new file mode 100644
index 0000000..afc5267
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialSimulation/include/subsystems/Elevator.h
@@ -0,0 +1,74 @@
+// 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 <frc/Encoder.h>
+#include <frc/controller/ElevatorFeedforward.h>
+#include <frc/controller/PIDController.h>
+#include <frc/motorcontrol/PWMSparkMax.h>
+#include <frc/simulation/BatterySim.h>
+#include <frc/simulation/ElevatorSim.h>
+#include <frc/simulation/EncoderSim.h>
+#include <frc/simulation/PWMSim.h>
+#include <frc/simulation/RoboRioSim.h>
+#include <frc/smartdashboard/Mechanism2d.h>
+#include <frc/smartdashboard/MechanismLigament2d.h>
+#include <frc/smartdashboard/MechanismRoot2d.h>
+#include <frc/trajectory/ExponentialProfile.h>
+#include <units/length.h>
+
+#include "Constants.h"
+
+class Elevator {
+ public:
+  Elevator();
+  void SimulationPeriodic();
+  void UpdateTelemetry();
+  void ReachGoal(units::meter_t goal);
+  void Reset();
+  void Stop();
+
+ private:
+  // This gearbox represents a gearbox containing 4 Vex 775pro motors.
+  frc::DCMotor m_elevatorGearbox = frc::DCMotor::NEO(2);
+
+  // Standard classes for controlling our elevator
+  frc::ExponentialProfile<units::meters, units::volts>::Constraints
+      m_constraints{Constants::kElevatorMaxV, Constants::kElevatorkV,
+                    Constants::kElevatorkA};
+  frc::ExponentialProfile<units::meters, units::volts> m_profile{m_constraints};
+  frc::ExponentialProfile<units::meters, units::volts>::State m_setpoint;
+
+  frc::PIDController m_controller{
+      Constants::kElevatorKp, Constants::kElevatorKi, Constants::kElevatorKd};
+
+  frc::ElevatorFeedforward m_feedforward{
+      Constants::kElevatorkS, Constants::kElevatorkG, Constants::kElevatorkV,
+      Constants::kElevatorkA};
+  frc::Encoder m_encoder{Constants::kEncoderAChannel,
+                         Constants::kEncoderBChannel};
+  frc::PWMSparkMax m_motor{Constants::kMotorPort};
+  frc::sim::PWMSim m_motorSim{m_motor};
+
+  // Simulation classes help us simulate what's going on, including gravity.
+  frc::sim::ElevatorSim m_elevatorSim{m_elevatorGearbox,
+                                      Constants::kElevatorGearing,
+                                      Constants::kCarriageMass,
+                                      Constants::kElevatorDrumRadius,
+                                      Constants::kMinElevatorHeight,
+                                      Constants::kMaxElevatorHeight,
+                                      true,
+                                      0_m,
+                                      {0.005}};
+  frc::sim::EncoderSim m_encoderSim{m_encoder};
+
+  // Create a Mechanism2d display of an elevator
+  frc::Mechanism2d m_mech2d{10_in / 1_m, 51_in / 1_m};
+  frc::MechanismRoot2d* m_elevatorRoot =
+      m_mech2d.GetRoot("Elevator Root", 5_in / 1_m, 0.5_in / 1_m);
+  frc::MechanismLigament2d* m_elevatorMech2d =
+      m_elevatorRoot->Append<frc::MechanismLigament2d>(
+          "Elevator", m_elevatorSim.GetPosition().value(), 90_deg);
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp/Robot.cpp
index 518114a..51fc29e 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp/Robot.cpp
@@ -7,6 +7,7 @@
 #include <frc/Encoder.h>
 #include <frc/Joystick.h>
 #include <frc/TimedRobot.h>
+#include <frc/controller/ElevatorFeedforward.h>
 #include <frc/controller/ProfiledPIDController.h>
 #include <frc/motorcontrol/PWMSparkMax.h>
 #include <frc/trajectory/TrapezoidProfile.h>
@@ -14,6 +15,7 @@
 #include <units/length.h>
 #include <units/time.h>
 #include <units/velocity.h>
+#include <units/voltage.h>
 
 class Robot : public frc::TimedRobot {
  public:
@@ -31,21 +33,34 @@
     }
 
     // Run controller and update motor output
-    m_motor.Set(
-        m_controller.Calculate(units::meter_t{m_encoder.GetDistance()}));
+    m_motor.SetVoltage(
+        units::volt_t{
+            m_controller.Calculate(units::meter_t{m_encoder.GetDistance()})} +
+        m_feedforward.Calculate(m_controller.GetSetpoint().velocity));
   }
 
  private:
+  static constexpr units::meters_per_second_t kMaxVelocity = 1.75_mps;
+  static constexpr units::meters_per_second_squared_t kMaxAcceleration =
+      0.75_mps_sq;
+  static constexpr double kP = 1.3;
+  static constexpr double kI = 0.0;
+  static constexpr double kD = 0.7;
+  static constexpr units::volt_t kS = 1.1_V;
+  static constexpr units::volt_t kG = 1.2_V;
+  static constexpr auto kV = 1.3_V / 1_mps;
+
   frc::Joystick m_joystick{1};
   frc::Encoder m_encoder{1, 2};
   frc::PWMSparkMax m_motor{1};
 
   // Create a PID controller whose setpoint's change is subject to maximum
   // velocity and acceleration constraints.
-  frc::TrapezoidProfile<units::meters>::Constraints m_constraints{1.75_mps,
-                                                                  0.75_mps_sq};
-  frc::ProfiledPIDController<units::meters> m_controller{1.3, 0.0, 0.7,
+  frc::TrapezoidProfile<units::meters>::Constraints m_constraints{
+      kMaxVelocity, kMaxAcceleration};
+  frc::ProfiledPIDController<units::meters> m_controller{kP, kI, kD,
                                                          m_constraints, kDt};
+  frc::ElevatorFeedforward m_feedforward{kS, kG, kV};
 };
 
 #ifndef RUNNING_FRC_TESTS
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/Robot.cpp
index 039a499..1be347c 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/Robot.cpp
@@ -2,130 +2,35 @@
 // 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 <numbers>
+#include "Robot.h"
 
-#include <frc/Encoder.h>
-#include <frc/Joystick.h>
-#include <frc/RobotController.h>
-#include <frc/StateSpaceUtil.h>
-#include <frc/TimedRobot.h>
-#include <frc/controller/PIDController.h>
-#include <frc/motorcontrol/PWMSparkMax.h>
-#include <frc/simulation/BatterySim.h>
-#include <frc/simulation/ElevatorSim.h>
-#include <frc/simulation/EncoderSim.h>
-#include <frc/simulation/RoboRioSim.h>
-#include <frc/smartdashboard/Mechanism2d.h>
-#include <frc/smartdashboard/MechanismLigament2d.h>
-#include <frc/smartdashboard/MechanismRoot2d.h>
-#include <frc/smartdashboard/SmartDashboard.h>
-#include <frc/system/plant/LinearSystemId.h>
-#include <frc/util/Color.h>
-#include <frc/util/Color8Bit.h>
-#include <units/angle.h>
-#include <units/length.h>
-#include <units/moment_of_inertia.h>
+#include "Constants.h"
 
-/**
- * This is a sample program to demonstrate how to use a state-space controller
- * to control an arm.
- */
-class Robot : public frc::TimedRobot {
-  static constexpr int kMotorPort = 0;
-  static constexpr int kEncoderAChannel = 0;
-  static constexpr int kEncoderBChannel = 1;
-  static constexpr int kJoystickPort = 0;
+void Robot::RobotPeriodic() {
+  // Update the telemetry, including mechanism visualization, regardless of
+  // mode.
+  m_elevator.UpdateTelemetry();
+}
 
-  static constexpr double kElevatorKp = 5.0;
-  static constexpr double kElevatorGearing = 10.0;
-  static constexpr units::meter_t kElevatorDrumRadius = 2_in;
-  static constexpr units::kilogram_t kCarriageMass = 4.0_kg;
+void Robot::SimulationPeriodic() {
+  // Update the simulation model.
+  m_elevator.SimulationPeriodic();
+}
 
-  static constexpr units::meter_t kMinElevatorHeight = 2_in;
-  static constexpr units::meter_t kMaxElevatorHeight = 50_in;
-
-  // distance per pulse = (distance per revolution) / (pulses per revolution)
-  //  = (Pi * D) / ppr
-  static constexpr double kArmEncoderDistPerPulse =
-      2.0 * std::numbers::pi * kElevatorDrumRadius.value() / 4096.0;
-
-  // This gearbox represents a gearbox containing 4 Vex 775pro motors.
-  frc::DCMotor m_elevatorGearbox = frc::DCMotor::Vex775Pro(4);
-
-  // Standard classes for controlling our elevator
-  frc2::PIDController m_controller{kElevatorKp, 0, 0};
-  frc::Encoder m_encoder{kEncoderAChannel, kEncoderBChannel};
-  frc::PWMSparkMax m_motor{kMotorPort};
-  frc::Joystick m_joystick{kJoystickPort};
-
-  // Simulation classes help us simulate what's going on, including gravity.
-  frc::sim::ElevatorSim m_elevatorSim{m_elevatorGearbox,
-                                      kElevatorGearing,
-                                      kCarriageMass,
-                                      kElevatorDrumRadius,
-                                      kMinElevatorHeight,
-                                      kMaxElevatorHeight,
-                                      true,
-                                      {0.01}};
-  frc::sim::EncoderSim m_encoderSim{m_encoder};
-
-  // Create a Mechanism2d display of an elevator
-  frc::Mechanism2d m_mech2d{20, 50};
-  frc::MechanismRoot2d* m_elevatorRoot =
-      m_mech2d.GetRoot("Elevator Root", 10, 0);
-  frc::MechanismLigament2d* m_elevatorMech2d =
-      m_elevatorRoot->Append<frc::MechanismLigament2d>(
-          "Elevator", units::inch_t{m_elevatorSim.GetPosition()}.value(),
-          90_deg);
-
- public:
-  void RobotInit() override {
-    m_encoder.SetDistancePerPulse(kArmEncoderDistPerPulse);
-
-    // Put Mechanism 2d to SmartDashboard
-    // To view the Elevator Sim in the simulator, select Network Tables ->
-    // SmartDashboard -> Elevator Sim
-    frc::SmartDashboard::PutData("Elevator Sim", &m_mech2d);
+void Robot::TeleopPeriodic() {
+  if (m_joystick.GetTrigger()) {
+    // Here, we set the constant setpoint of 0.75 meters.
+    m_elevator.ReachGoal(Constants::kSetpoint);
+  } else {
+    // Otherwise, we update the setpoint to 0.
+    m_elevator.ReachGoal(0.0_m);
   }
+}
 
-  void SimulationPeriodic() override {
-    // In this method, we update our simulation of what our elevator is doing
-    // First, we set our "inputs" (voltages)
-    m_elevatorSim.SetInput(frc::Vectord<1>{
-        m_motor.Get() * frc::RobotController::GetInputVoltage()});
-
-    // Next, we update it. The standard loop time is 20ms.
-    m_elevatorSim.Update(20_ms);
-
-    // Finally, we set our simulated encoder's readings and simulated battery
-    // voltage
-    m_encoderSim.SetDistance(m_elevatorSim.GetPosition().value());
-    // SimBattery estimates loaded battery voltages
-    frc::sim::RoboRioSim::SetVInVoltage(
-        frc::sim::BatterySim::Calculate({m_elevatorSim.GetCurrentDraw()}));
-
-    // Update the Elevator length based on the simulated elevator height
-    m_elevatorMech2d->SetLength(
-        units::inch_t{m_elevatorSim.GetPosition()}.value());
-  }
-
-  void TeleopPeriodic() override {
-    if (m_joystick.GetTrigger()) {
-      // Here, we run PID control like normal, with a constant setpoint of 30in.
-      double pidOutput = m_controller.Calculate(m_encoder.GetDistance(),
-                                                units::meter_t{30_in}.value());
-      m_motor.SetVoltage(units::volt_t{pidOutput});
-    } else {
-      // Otherwise, we disable the motor.
-      m_motor.Set(0.0);
-    }
-  }
-
-  void DisabledInit() override {
-    // This just makes sure that our simulation code knows that the motor's off.
-    m_motor.Set(0.0);
-  }
-};
+void Robot::DisabledInit() {
+  // This just makes sure that our simulation code knows that the motor's off.
+  m_elevator.Stop();
+}
 
 #ifndef RUNNING_FRC_TESTS
 int main() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/subsystems/Elevator.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/subsystems/Elevator.cpp
new file mode 100644
index 0000000..35e11ff
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/subsystems/Elevator.cpp
@@ -0,0 +1,55 @@
+// 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 "subsystems/Elevator.h"
+
+#include <frc/RobotController.h>
+#include <frc/StateSpaceUtil.h>
+#include <frc/smartdashboard/SmartDashboard.h>
+
+Elevator::Elevator() {
+  m_encoder.SetDistancePerPulse(Constants::kArmEncoderDistPerPulse);
+
+  // Put Mechanism 2d to SmartDashboard
+  // To view the Elevator visualization, select Network Tables -> SmartDashboard
+  // -> Elevator Sim
+  frc::SmartDashboard::PutData("Elevator Sim", &m_mech2d);
+}
+
+void Elevator::SimulationPeriodic() {
+  // In this method, we update our simulation of what our elevator is doing
+  // First, we set our "inputs" (voltages)
+  m_elevatorSim.SetInput(frc::Vectord<1>{
+      m_motorSim.GetSpeed() * frc::RobotController::GetInputVoltage()});
+
+  // Next, we update it. The standard loop time is 20ms.
+  m_elevatorSim.Update(20_ms);
+
+  // Finally, we set our simulated encoder's readings and simulated battery
+  // voltage
+  m_encoderSim.SetDistance(m_elevatorSim.GetPosition().value());
+  // SimBattery estimates loaded battery voltages
+  frc::sim::RoboRioSim::SetVInVoltage(
+      frc::sim::BatterySim::Calculate({m_elevatorSim.GetCurrentDraw()}));
+}
+
+void Elevator::UpdateTelemetry() {
+  // Update the Elevator length based on the simulated elevator height
+  m_elevatorMech2d->SetLength(m_encoder.GetDistance());
+}
+
+void Elevator::ReachGoal(units::meter_t goal) {
+  m_controller.SetGoal(goal);
+  // With the setpoint value we run PID control like normal
+  double pidOutput =
+      m_controller.Calculate(units::meter_t{m_encoder.GetDistance()});
+  units::volt_t feedforwardOutput =
+      m_feedforward.Calculate(m_controller.GetSetpoint().velocity);
+  m_motor.SetVoltage(units::volt_t{pidOutput} + feedforwardOutput);
+}
+
+void Elevator::Stop() {
+  m_controller.SetGoal(0.0_m);
+  m_motor.Set(0.0);
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/include/Constants.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/include/Constants.h
new file mode 100644
index 0000000..7be706f
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/include/Constants.h
@@ -0,0 +1,55 @@
+// 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 <numbers>
+
+#include <units/acceleration.h>
+#include <units/angle.h>
+#include <units/length.h>
+#include <units/mass.h>
+#include <units/time.h>
+#include <units/velocity.h>
+#include <units/voltage.h>
+
+/**
+ * The Constants header provides a convenient place for teams to hold robot-wide
+ * numerical or bool constants.  This should not be used for any other purpose.
+ *
+ * It is generally a good idea to place constants into subsystem- or
+ * command-specific namespaces within this header, which can then be used where
+ * they are needed.
+ */
+
+namespace Constants {
+
+static constexpr int kMotorPort = 0;
+static constexpr int kEncoderAChannel = 0;
+static constexpr int kEncoderBChannel = 1;
+static constexpr int kJoystickPort = 0;
+
+static constexpr double kElevatorKp = 5.0;
+static constexpr double kElevatorKi = 0.0;
+static constexpr double kElevatorKd = 0.0;
+
+static constexpr units::volt_t kElevatorkS = 0.0_V;
+static constexpr units::volt_t kElevatorkG = 0.762_V;
+static constexpr auto kElevatorkV = 0.762_V / 1_mps;
+static constexpr auto kElevatorkA = 0.0_V / 1_mps_sq;
+
+static constexpr double kElevatorGearing = 10.0;
+static constexpr units::meter_t kElevatorDrumRadius = 2_in;
+static constexpr units::kilogram_t kCarriageMass = 4.0_kg;
+
+static constexpr units::meter_t kSetpoint = 75_cm;
+static constexpr units::meter_t kMinElevatorHeight = 0_cm;
+static constexpr units::meter_t kMaxElevatorHeight = 1.25_m;
+
+// distance per pulse = (distance per revolution) / (pulses per revolution)
+//  = (Pi * D) / ppr
+static constexpr double kArmEncoderDistPerPulse =
+    2.0 * std::numbers::pi * kElevatorDrumRadius.value() / 4096.0;
+
+}  // namespace Constants
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/include/Robot.h
new file mode 100644
index 0000000..53f50ea
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/include/Robot.h
@@ -0,0 +1,26 @@
+// 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 <frc/Joystick.h>
+#include <frc/TimedRobot.h>
+
+#include "subsystems/Elevator.h"
+
+/**
+ * This is a sample program to demonstrate the use of elevator simulation.
+ */
+class Robot : public frc::TimedRobot {
+ public:
+  void RobotInit() override {}
+  void RobotPeriodic() override;
+  void SimulationPeriodic() override;
+  void TeleopPeriodic() override;
+  void DisabledInit() override;
+
+ private:
+  frc::Joystick m_joystick{Constants::kJoystickPort};
+  Elevator m_elevator;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/include/subsystems/Elevator.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/include/subsystems/Elevator.h
new file mode 100644
index 0000000..4b07189
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/include/subsystems/Elevator.h
@@ -0,0 +1,70 @@
+// 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 <frc/Encoder.h>
+#include <frc/controller/ElevatorFeedforward.h>
+#include <frc/controller/PIDController.h>
+#include <frc/controller/ProfiledPIDController.h>
+#include <frc/motorcontrol/PWMSparkMax.h>
+#include <frc/simulation/BatterySim.h>
+#include <frc/simulation/ElevatorSim.h>
+#include <frc/simulation/EncoderSim.h>
+#include <frc/simulation/PWMSim.h>
+#include <frc/simulation/RoboRioSim.h>
+#include <frc/smartdashboard/Mechanism2d.h>
+#include <frc/smartdashboard/MechanismLigament2d.h>
+#include <frc/smartdashboard/MechanismRoot2d.h>
+#include <units/length.h>
+
+#include "Constants.h"
+
+class Elevator {
+ public:
+  Elevator();
+  void SimulationPeriodic();
+  void UpdateTelemetry();
+  void ReachGoal(units::meter_t goal);
+  void Stop();
+
+ private:
+  // This gearbox represents a gearbox containing 4 Vex 775pro motors.
+  frc::DCMotor m_elevatorGearbox = frc::DCMotor::Vex775Pro(4);
+
+  // Standard classes for controlling our elevator
+  frc::TrapezoidProfile<units::meters>::Constraints m_constraints{2.45_mps,
+                                                                  2.45_mps_sq};
+  frc::ProfiledPIDController<units::meters> m_controller{
+      Constants::kElevatorKp, Constants::kElevatorKi, Constants::kElevatorKd,
+      m_constraints};
+
+  frc::ElevatorFeedforward m_feedforward{
+      Constants::kElevatorkS, Constants::kElevatorkG, Constants::kElevatorkV,
+      Constants::kElevatorkA};
+  frc::Encoder m_encoder{Constants::kEncoderAChannel,
+                         Constants::kEncoderBChannel};
+  frc::PWMSparkMax m_motor{Constants::kMotorPort};
+  frc::sim::PWMSim m_motorSim{m_motor};
+
+  // Simulation classes help us simulate what's going on, including gravity.
+  frc::sim::ElevatorSim m_elevatorSim{m_elevatorGearbox,
+                                      Constants::kElevatorGearing,
+                                      Constants::kCarriageMass,
+                                      Constants::kElevatorDrumRadius,
+                                      Constants::kMinElevatorHeight,
+                                      Constants::kMaxElevatorHeight,
+                                      true,
+                                      0_m,
+                                      {0.01}};
+  frc::sim::EncoderSim m_encoderSim{m_encoder};
+
+  // Create a Mechanism2d display of an elevator
+  frc::Mechanism2d m_mech2d{20, 50};
+  frc::MechanismRoot2d* m_elevatorRoot =
+      m_mech2d.GetRoot("Elevator Root", 10, 0);
+  frc::MechanismLigament2d* m_elevatorMech2d =
+      m_elevatorRoot->Append<frc::MechanismLigament2d>(
+          "Elevator", m_elevatorSim.GetPosition().value(), 90_deg);
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/cpp/Robot.cpp
index f708143..fb3a70d 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/cpp/Robot.cpp
@@ -32,15 +32,9 @@
       m_goal = {0_m, 0_mps};
     }
 
-    // Create a motion profile with the given maximum velocity and maximum
-    // acceleration constraints for the next setpoint, the desired goal, and the
-    // current setpoint.
-    frc::TrapezoidProfile<units::meters> profile{m_constraints, m_goal,
-                                                 m_setpoint};
-
     // Retrieve the profiled setpoint for the next timestep. This setpoint moves
     // toward the goal while obeying the constraints.
-    m_setpoint = profile.Calculate(kDt);
+    m_setpoint = m_profile.Calculate(kDt, m_goal, m_setpoint);
 
     // Send setpoint to offboard controller PID
     m_motor.SetSetpoint(ExampleSmartMotorController::PIDMode::kPosition,
@@ -55,8 +49,9 @@
       // Note: These gains are fake, and will have to be tuned for your robot.
       1_V, 1.5_V * 1_s / 1_m};
 
-  frc::TrapezoidProfile<units::meters>::Constraints m_constraints{1.75_mps,
-                                                                  0.75_mps_sq};
+  // Create a motion profile with the given maximum velocity and maximum
+  // acceleration constraints for the next setpoint.
+  frc::TrapezoidProfile<units::meters> m_profile{{1.75_mps, 0.75_mps_sq}};
   frc::TrapezoidProfile<units::meters>::State m_goal;
   frc::TrapezoidProfile<units::meters>::State m_setpoint;
 };
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/FlywheelBangBangController/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/FlywheelBangBangController/cpp/Robot.cpp
new file mode 100644
index 0000000..86807e6
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/FlywheelBangBangController/cpp/Robot.cpp
@@ -0,0 +1,104 @@
+// 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 <frc/Encoder.h>
+#include <frc/Joystick.h>
+#include <frc/TimedRobot.h>
+#include <frc/controller/BangBangController.h>
+#include <frc/controller/SimpleMotorFeedforward.h>
+#include <frc/motorcontrol/PWMSparkMax.h>
+#include <frc/simulation/EncoderSim.h>
+#include <frc/simulation/FlywheelSim.h>
+#include <frc/smartdashboard/SmartDashboard.h>
+#include <units/moment_of_inertia.h>
+
+/**
+ * This is a sample program to demonstrate the use of a BangBangController with
+ * a flywheel to control speed.
+ */
+class Robot : public frc::TimedRobot {
+ public:
+  /**
+   * Controls flywheel to a set speed (RPM) controlled by a joystick.
+   */
+  void TeleopPeriodic() override {
+    // Scale setpoint value between 0 and maxSetpointValue
+    units::radians_per_second_t setpoint =
+        units::math::max(0_rpm, m_joystick.GetRawAxis(0) * kMaxSetpointValue);
+
+    // Set setpoint and measurement of the bang-bang controller
+    units::volt_t bangOutput =
+        m_bangBangControler.Calculate(m_encoder.GetRate(), setpoint.value()) *
+        12_V;
+
+    // Controls a motor with the output of the BangBang controller and a
+    // feedforward. The feedforward is reduced slightly to avoid overspeeding
+    // the shooter.
+    m_flywheelMotor.SetVoltage(bangOutput +
+                               0.9 * m_feedforward.Calculate(setpoint));
+  }
+
+  void RobotInit() override {
+    // Add bang-bang controler to SmartDashboard and networktables.
+    frc::SmartDashboard::PutData("BangBangControler", &m_bangBangControler);
+  }
+
+  /**
+   * Update our simulation. This should be run every robot loop in simulation.
+   */
+  void SimulationPeriodic() override {
+    // To update our simulation, we set motor voltage inputs, update the
+    // simulation, and write the simulated velocities to our simulated encoder
+    m_flywheelSim.SetInputVoltage(
+        m_flywheelMotor.Get() *
+        units::volt_t{frc::RobotController::GetInputVoltage()});
+    m_flywheelSim.Update(0.02_s);
+    m_encoderSim.SetRate(m_flywheelSim.GetAngularVelocity().value());
+  }
+
+ private:
+  static constexpr int kMotorPort = 0;
+  static constexpr int kEncoderAChannel = 0;
+  static constexpr int kEncoderBChannel = 1;
+
+  // Max setpoint for joystick control
+  static constexpr units::radians_per_second_t kMaxSetpointValue = 6000_rpm;
+
+  // Joystick to control setpoint
+  frc::Joystick m_joystick{0};
+
+  frc::PWMSparkMax m_flywheelMotor{kMotorPort};
+  frc::Encoder m_encoder{kEncoderAChannel, kEncoderBChannel};
+
+  frc::BangBangController m_bangBangControler;
+
+  // Gains are for example purposes only - must be determined for your own
+  // robot!
+  static constexpr units::volt_t kFlywheelKs = 0.0001_V;
+  static constexpr decltype(1_V / 1_rad_per_s) kFlywheelKv = 0.000195_V / 1_rpm;
+  static constexpr decltype(1_V / 1_rad_per_s_sq) kFlywheelKa =
+      0.0003_V / 1_rev_per_m_per_s;
+  frc::SimpleMotorFeedforward<units::radians> m_feedforward{
+      kFlywheelKs, kFlywheelKv, kFlywheelKa};
+
+  // Simulation classes help us simulate our robot
+
+  // Reduction between motors and encoder, as output over input. If the flywheel
+  // spins slower than the motors, this number should be greater than one.
+  static constexpr double kFlywheelGearing = 1.0;
+
+  // 1/2 MR²
+  static constexpr units::kilogram_square_meter_t kFlywheelMomentOfInertia =
+      0.5 * 1.5_lb * 4_in * 4_in;
+
+  frc::sim::FlywheelSim m_flywheelSim{frc::DCMotor::NEO(1), kFlywheelGearing,
+                                      kFlywheelMomentOfInertia};
+  frc::sim::EncoderSim m_encoderSim{m_encoder};
+};
+
+#ifndef RUNNING_FRC_TESTS
+int main() {
+  return frc::StartRobot<Robot>();
+}
+#endif
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Frisbeebot/cpp/subsystems/ShooterSubsystem.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Frisbeebot/cpp/subsystems/ShooterSubsystem.cpp
index aec84f1..cff73da 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Frisbeebot/cpp/subsystems/ShooterSubsystem.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Frisbeebot/cpp/subsystems/ShooterSubsystem.cpp
@@ -11,7 +11,7 @@
 using namespace ShooterConstants;
 
 ShooterSubsystem::ShooterSubsystem()
-    : PIDSubsystem{frc2::PIDController{kP, kI, kD}},
+    : PIDSubsystem{frc::PIDController{kP, kI, kD}},
       m_shooterMotor(kShooterMotorPort),
       m_feederMotor(kFeederMotorPort),
       m_shooterEncoder(kEncoderPorts[0], kEncoderPorts[1]),
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/CloseClaw.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/CloseClaw.cpp
index 75884a2..0b6b8b0 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/CloseClaw.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/CloseClaw.cpp
@@ -8,7 +8,7 @@
 
 CloseClaw::CloseClaw(Claw& claw) : m_claw(&claw) {
   SetName("CloseClaw");
-  AddRequirements({m_claw});
+  AddRequirements(m_claw);
 }
 
 // Called just before this Command runs the first time
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/DriveStraight.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/DriveStraight.cpp
index c1f5d88..6325307 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/DriveStraight.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/DriveStraight.cpp
@@ -10,7 +10,7 @@
 
 DriveStraight::DriveStraight(double distance, Drivetrain& drivetrain)
     : frc2::CommandHelper<frc2::PIDCommand, DriveStraight>{
-          frc2::PIDController{4, 0, 0},
+          frc::PIDController{4, 0, 0},
           [&drivetrain] { return drivetrain.GetDistance(); },
           distance,
           [&drivetrain](double output) { drivetrain.Drive(output, output); },
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/OpenClaw.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/OpenClaw.cpp
index e5bab77..371ba85 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/OpenClaw.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/OpenClaw.cpp
@@ -9,7 +9,7 @@
 OpenClaw::OpenClaw(Claw& claw)
     : frc2::CommandHelper<frc2::WaitCommand, OpenClaw>(1_s), m_claw(&claw) {
   SetName("OpenClaw");
-  AddRequirements({m_claw});
+  AddRequirements(m_claw);
 }
 
 // Called just before this Command runs the first time
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetDistanceToBox.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetDistanceToBox.cpp
index de8f1e7..1ef5cbb 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetDistanceToBox.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetDistanceToBox.cpp
@@ -10,7 +10,7 @@
 
 SetDistanceToBox::SetDistanceToBox(double distance, Drivetrain& drivetrain)
     : frc2::CommandHelper<frc2::PIDCommand, SetDistanceToBox>{
-          frc2::PIDController{-2, 0, 0},
+          frc::PIDController{-2, 0, 0},
           [&drivetrain] { return drivetrain.GetDistanceToObstacle(); },
           distance,
           [&drivetrain](double output) { drivetrain.Drive(output, output); },
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetElevatorSetpoint.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetElevatorSetpoint.cpp
index 08343fe..d8a2f08 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetElevatorSetpoint.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetElevatorSetpoint.cpp
@@ -11,7 +11,7 @@
 SetElevatorSetpoint::SetElevatorSetpoint(double setpoint, Elevator& elevator)
     : m_setpoint(setpoint), m_elevator(&elevator) {
   SetName("SetElevatorSetpoint");
-  AddRequirements({m_elevator});
+  AddRequirements(m_elevator);
 }
 
 // Called just before this Command runs the first time
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetWristSetpoint.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetWristSetpoint.cpp
index 9697745..bed2ffe 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetWristSetpoint.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/SetWristSetpoint.cpp
@@ -9,7 +9,7 @@
 SetWristSetpoint::SetWristSetpoint(double setpoint, Wrist& wrist)
     : m_setpoint(setpoint), m_wrist(&wrist) {
   SetName("SetWristSetpoint");
-  AddRequirements({m_wrist});
+  AddRequirements(m_wrist);
 }
 
 // Called just before this Command runs the first time
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/TankDrive.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/TankDrive.cpp
index cc34e53..7021f9d 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/TankDrive.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/commands/TankDrive.cpp
@@ -14,7 +14,7 @@
       m_right(std::move(right)),
       m_drivetrain(&drivetrain) {
   SetName("TankDrive");
-  AddRequirements({m_drivetrain});
+  AddRequirements(m_drivetrain);
 }
 
 // Called repeatedly when this Command is scheduled to run
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Elevator.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Elevator.cpp
index c75c7bc..03db314 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Elevator.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Elevator.cpp
@@ -9,7 +9,7 @@
 #include <frc/smartdashboard/SmartDashboard.h>
 
 Elevator::Elevator()
-    : frc2::PIDSubsystem{frc2::PIDController{kP_real, kI_real, 0}} {
+    : frc2::PIDSubsystem{frc::PIDController{kP_real, kI_real, 0}} {
 #ifdef SIMULATION  // Check for simulation and update PID values
   GetPIDController()->SetPID(kP_simulation, kI_simulation, 0, 0);
 #endif
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Wrist.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Wrist.cpp
index 8b24a7b..c0e06af 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Wrist.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Wrist.cpp
@@ -7,7 +7,7 @@
 #include <frc/controller/PIDController.h>
 #include <frc/smartdashboard/SmartDashboard.h>
 
-Wrist::Wrist() : frc2::PIDSubsystem{frc2::PIDController{kP, 0, 0}} {
+Wrist::Wrist() : frc2::PIDSubsystem{frc::PIDController{kP, 0, 0}} {
   m_controller.SetTolerance(2.5);
 
   SetName("Wrist");
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/CloseClaw.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/CloseClaw.h
index 0511604..31e2b92 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/CloseClaw.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/CloseClaw.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/Claw.h"
@@ -12,7 +12,7 @@
 /**
  * Closes the claw until the limit switch is tripped.
  */
-class CloseClaw : public frc2::CommandHelper<frc2::CommandBase, CloseClaw> {
+class CloseClaw : public frc2::CommandHelper<frc2::Command, CloseClaw> {
  public:
   explicit CloseClaw(Claw& claw);
   void Initialize() override;
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/SetElevatorSetpoint.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/SetElevatorSetpoint.h
index a106d62..3e8c18c 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/SetElevatorSetpoint.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/SetElevatorSetpoint.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/Elevator.h"
@@ -17,7 +17,7 @@
  * commands using the elevator should make sure they disable PID!
  */
 class SetElevatorSetpoint
-    : public frc2::CommandHelper<frc2::CommandBase, SetElevatorSetpoint> {
+    : public frc2::CommandHelper<frc2::Command, SetElevatorSetpoint> {
  public:
   explicit SetElevatorSetpoint(double setpoint, Elevator& elevator);
   void Initialize() override;
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/SetWristSetpoint.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/SetWristSetpoint.h
index 2f6ca21..1494ad6 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/SetWristSetpoint.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/SetWristSetpoint.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/Wrist.h"
@@ -15,7 +15,7 @@
  * Other commands using the wrist should make sure they disable PID!
  */
 class SetWristSetpoint
-    : public frc2::CommandHelper<frc2::CommandBase, SetWristSetpoint> {
+    : public frc2::CommandHelper<frc2::Command, SetWristSetpoint> {
  public:
   explicit SetWristSetpoint(double setpoint, Wrist& wrist);
   void Initialize() override;
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/TankDrive.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/TankDrive.h
index 5a81c11..932c427 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/TankDrive.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GearsBot/include/commands/TankDrive.h
@@ -4,7 +4,9 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <functional>
+
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/Drivetrain.h"
@@ -12,7 +14,7 @@
 /**
  * Have the robot drive tank style using the PS3 Joystick until interrupted.
  */
-class TankDrive : public frc2::CommandHelper<frc2::CommandBase, TankDrive> {
+class TankDrive : public frc2::CommandHelper<frc2::Command, TankDrive> {
  public:
   TankDrive(std::function<double()> left, std::function<double()> right,
             Drivetrain& drivetrain);
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp
index a49d4f6..01b7210 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp
@@ -19,10 +19,7 @@
     m_timer.Start();
   }
 
-  void AutonomousInit() override {
-    m_timer.Reset();
-    m_timer.Start();
-  }
+  void AutonomousInit() override { m_timer.Restart(); }
 
   void AutonomousPeriodic() override {
     // Drive for 2 seconds
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/RobotContainer.cpp
index 3404b77..50b9900 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/RobotContainer.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/RobotContainer.cpp
@@ -35,8 +35,8 @@
   frc2::JoystickButton(&m_driverController, frc::PS4Controller::Button::kL1)
       .WhileTrue(
           frc2::PIDCommand(
-              frc2::PIDController{dc::kStabilizationP, dc::kStabilizationI,
-                                  dc::kStabilizationD},
+              frc::PIDController{dc::kStabilizationP, dc::kStabilizationI,
+                                 dc::kStabilizationD},
               // Close the loop on the turn rate
               [this] { return m_drive.GetTurnRate(); },
               // Setpoint is 0
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngle.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngle.cpp
index 3038f79..6d1272d 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngle.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngle.cpp
@@ -9,7 +9,7 @@
 using namespace DriveConstants;
 
 TurnToAngle::TurnToAngle(units::degree_t target, DriveSubsystem* drive)
-    : CommandHelper{frc2::PIDController{kTurnP, kTurnI, kTurnD},
+    : CommandHelper{frc::PIDController{kTurnP, kTurnI, kTurnD},
                     // Close loop on heading
                     [drive] { return drive->GetHeading().value(); },
                     // Set reference to target
@@ -25,7 +25,7 @@
   // reference
   m_controller.SetTolerance(kTurnTolerance.value(), kTurnRateTolerance.value());
 
-  AddRequirements({drive});
+  AddRequirements(drive);
 }
 
 bool TurnToAngle::IsFinished() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngleProfiled.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngleProfiled.cpp
index df4e355..464e0cf 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngleProfiled.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngleProfiled.cpp
@@ -30,7 +30,7 @@
   // reference
   GetController().SetTolerance(kTurnTolerance, kTurnRateTolerance);
 
-  AddRequirements({drive});
+  AddRequirements(drive);
 }
 
 bool TurnToAngleProfiled::IsFinished() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c
index b0905f9..a273e33 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c
@@ -72,7 +72,7 @@
   }
 
   // Set PWM config to standard servo speeds
-  HAL_SetPWMConfig(pwmPort, 2.0, 1.501, 1.5, 1.499, 1.0, &status);
+  HAL_SetPWMConfigMicroseconds(pwmPort, 2000, 1501, 1500, 1499, 1000, &status);
 
   // Create an Input
   status = 0;
@@ -87,7 +87,7 @@
     return 1;
   }
 
-  WPI_EventHandle eventHandle = WPI_CreateEvent(1, 0);
+  WPI_EventHandle eventHandle = WPI_CreateEvent(0, 0);
   HAL_ProvideNewDataEventHandle(eventHandle);
 
   while (1) {
@@ -99,6 +99,8 @@
       continue;
     }
 
+    HAL_RefreshDSData();
+
     enum DriverStationMode dsMode = getDSMode();
     switch (dsMode) {
       case DisabledMode:
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/Robot.cpp
index c7eab48..72efd40 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/Robot.cpp
@@ -4,10 +4,19 @@
 
 #include "Robot.h"
 
+#include <frc/DataLogManager.h>
+#include <frc/DriverStation.h>
 #include <frc/smartdashboard/SmartDashboard.h>
 #include <frc2/command/CommandScheduler.h>
 
-void Robot::RobotInit() {}
+void Robot::RobotInit() {
+  // Start recording to data log
+  frc::DataLogManager::Start();
+
+  // Record DS control and joystick data.
+  // Change to `false` to not record joystick data.
+  frc::DriverStation::StartDataLog(frc::DataLogManager::GetLog(), true);
+}
 
 /**
  * This function is called every 20 ms, no matter the mode. Use
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/RobotContainer.cpp
index 5a0cdad..76714d8 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/RobotContainer.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/RobotContainer.cpp
@@ -16,6 +16,35 @@
 
   // Put the chooser on the dashboard
   frc::Shuffleboard::GetTab("Autonomous").Add(m_chooser);
+  // Put subsystems to dashboard.
+  frc::Shuffleboard::GetTab("Drivetrain").Add(m_drive);
+  frc::Shuffleboard::GetTab("HatchSubsystem").Add(m_hatch);
+
+  // Log Shuffleboard events for command initialize, execute, finish, interrupt
+  frc2::CommandScheduler::GetInstance().OnCommandInitialize(
+      [](const frc2::Command& command) {
+        frc::Shuffleboard::AddEventMarker(
+            "Command initialized", command.GetName(),
+            frc::ShuffleboardEventImportance::kNormal);
+      });
+  frc2::CommandScheduler::GetInstance().OnCommandExecute(
+      [](const frc2::Command& command) {
+        frc::Shuffleboard::AddEventMarker(
+            "Command executed", command.GetName(),
+            frc::ShuffleboardEventImportance::kNormal);
+      });
+  frc2::CommandScheduler::GetInstance().OnCommandFinish(
+      [](const frc2::Command& command) {
+        frc::Shuffleboard::AddEventMarker(
+            "Command finished", command.GetName(),
+            frc::ShuffleboardEventImportance::kNormal);
+      });
+  frc2::CommandScheduler::GetInstance().OnCommandInterrupt(
+      [](const frc2::Command& command) {
+        frc::Shuffleboard::AddEventMarker(
+            "Command interrupted", command.GetName(),
+            frc::ShuffleboardEventImportance::kNormal);
+      });
 
   // Configure the button bindings
   ConfigureButtonBindings();
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/DriveSubsystem.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/DriveSubsystem.cpp
index bd94b68..3372a4d 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/DriveSubsystem.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/DriveSubsystem.cpp
@@ -4,6 +4,8 @@
 
 #include "subsystems/DriveSubsystem.h"
 
+#include <wpi/sendable/SendableBuilder.h>
+
 using namespace DriveConstants;
 
 DriveSubsystem::DriveSubsystem()
@@ -40,14 +42,17 @@
   return (m_leftEncoder.GetDistance() + m_rightEncoder.GetDistance()) / 2.0;
 }
 
-frc::Encoder& DriveSubsystem::GetLeftEncoder() {
-  return m_leftEncoder;
-}
-
-frc::Encoder& DriveSubsystem::GetRightEncoder() {
-  return m_rightEncoder;
-}
-
 void DriveSubsystem::SetMaxOutput(double maxOutput) {
   m_drive.SetMaxOutput(maxOutput);
 }
+
+void DriveSubsystem::InitSendable(wpi::SendableBuilder& builder) {
+  SubsystemBase::InitSendable(builder);
+
+  // Publish encoder distances to telemetry.
+  builder.AddDoubleProperty(
+      "leftDistance", [this] { return m_leftEncoder.GetDistance(); }, nullptr);
+  builder.AddDoubleProperty(
+      "rightDistance", [this] { return m_rightEncoder.GetDistance(); },
+      nullptr);
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/HatchSubsystem.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/HatchSubsystem.cpp
index e766d43..449465b 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/HatchSubsystem.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/HatchSubsystem.cpp
@@ -4,6 +4,8 @@
 
 #include "subsystems/HatchSubsystem.h"
 
+#include <wpi/sendable/SendableBuilder.h>
+
 using namespace HatchConstants;
 
 HatchSubsystem::HatchSubsystem()
@@ -21,3 +23,13 @@
   return this->RunOnce(
       [this] { m_hatchSolenoid.Set(frc::DoubleSolenoid::kReverse); });
 }
+
+void HatchSubsystem::InitSendable(wpi::SendableBuilder& builder) {
+  SubsystemBase::InitSendable(builder);
+
+  // Publish the solenoid state to telemetry.
+  builder.AddBooleanProperty(
+      "extended",
+      [this] { return m_hatchSolenoid.Get() == frc::DoubleSolenoid::kForward; },
+      nullptr);
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h
index 47bf28e..5984a1a 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h
@@ -44,20 +44,6 @@
   double GetAverageEncoderDistance();
 
   /**
-   * Gets the left drive encoder.
-   *
-   * @return the left drive encoder
-   */
-  frc::Encoder& GetLeftEncoder();
-
-  /**
-   * Gets the right drive encoder.
-   *
-   * @return the right drive encoder
-   */
-  frc::Encoder& GetRightEncoder();
-
-  /**
    * Sets the max output of the drive.  Useful for scaling the drive to drive
    * more slowly.
    *
@@ -65,6 +51,8 @@
    */
   void SetMaxOutput(double maxOutput);
 
+  void InitSendable(wpi::SendableBuilder& builder) override;
+
  private:
   // Components (e.g. motor controllers and sensors) should generally be
   // declared private and exposed only through public methods.
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/HatchSubsystem.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/HatchSubsystem.h
index b21bb56..e81f3b2 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/HatchSubsystem.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/HatchSubsystem.h
@@ -27,6 +27,8 @@
    */
   frc2::CommandPtr ReleaseHatchCommand();
 
+  void InitSendable(wpi::SendableBuilder& builder) override;
+
  private:
   // Components (e.g. motor controllers and sensors) should generally be
   // declared private and exposed only through public methods.
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/Robot.cpp
index c7eab48..72efd40 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/Robot.cpp
@@ -4,10 +4,19 @@
 
 #include "Robot.h"
 
+#include <frc/DataLogManager.h>
+#include <frc/DriverStation.h>
 #include <frc/smartdashboard/SmartDashboard.h>
 #include <frc2/command/CommandScheduler.h>
 
-void Robot::RobotInit() {}
+void Robot::RobotInit() {
+  // Start recording to data log
+  frc::DataLogManager::Start();
+
+  // Record DS control and joystick data.
+  // Change to `false` to not record joystick data.
+  frc::DriverStation::StartDataLog(frc::DataLogManager::GetLog(), true);
+}
 
 /**
  * This function is called every 20 ms, no matter the mode. Use
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp
index 876a984..faf9d1f 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp
@@ -21,6 +21,35 @@
 
   // Put the chooser on the dashboard
   frc::Shuffleboard::GetTab("Autonomous").Add(m_chooser);
+  // Put subsystems to dashboard.
+  frc::Shuffleboard::GetTab("Drivetrain").Add(m_drive);
+  frc::Shuffleboard::GetTab("HatchSubsystem").Add(m_hatch);
+
+  // Log Shuffleboard events for command initialize, execute, finish, interrupt
+  frc2::CommandScheduler::GetInstance().OnCommandInitialize(
+      [](const frc2::Command& command) {
+        frc::Shuffleboard::AddEventMarker(
+            "Command initialized", command.GetName(),
+            frc::ShuffleboardEventImportance::kNormal);
+      });
+  frc2::CommandScheduler::GetInstance().OnCommandExecute(
+      [](const frc2::Command& command) {
+        frc::Shuffleboard::AddEventMarker(
+            "Command executed", command.GetName(),
+            frc::ShuffleboardEventImportance::kNormal);
+      });
+  frc2::CommandScheduler::GetInstance().OnCommandFinish(
+      [](const frc2::Command& command) {
+        frc::Shuffleboard::AddEventMarker(
+            "Command finished", command.GetName(),
+            frc::ShuffleboardEventImportance::kNormal);
+      });
+  frc2::CommandScheduler::GetInstance().OnCommandInterrupt(
+      [](const frc2::Command& command) {
+        frc::Shuffleboard::AddEventMarker(
+            "Command interrupted", command.GetName(),
+            frc::ShuffleboardEventImportance::kNormal);
+      });
 
   // Configure the button bindings
   ConfigureButtonBindings();
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/DefaultDrive.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/DefaultDrive.cpp
index 7ef404e..4047596 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/DefaultDrive.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/DefaultDrive.cpp
@@ -12,7 +12,7 @@
     : m_drive{subsystem},
       m_forward{std::move(forward)},
       m_rotation{std::move(rotation)} {
-  AddRequirements({subsystem});
+  AddRequirements(subsystem);
 }
 
 void DefaultDrive::Execute() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/DriveDistance.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/DriveDistance.cpp
index 7ee1b2c..9d9704d 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/DriveDistance.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/DriveDistance.cpp
@@ -9,7 +9,7 @@
 DriveDistance::DriveDistance(double inches, double speed,
                              DriveSubsystem* subsystem)
     : m_drive(subsystem), m_distance(inches), m_speed(speed) {
-  AddRequirements({subsystem});
+  AddRequirements(subsystem);
 }
 
 void DriveDistance::Initialize() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/ReleaseHatch.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/ReleaseHatch.cpp
index 7e6c9e8..12f36da 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/ReleaseHatch.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/ReleaseHatch.cpp
@@ -5,7 +5,7 @@
 #include "commands/ReleaseHatch.h"
 
 ReleaseHatch::ReleaseHatch(HatchSubsystem* subsystem) : m_hatch(subsystem) {
-  AddRequirements({subsystem});
+  AddRequirements(subsystem);
 }
 
 void ReleaseHatch::Initialize() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/DriveSubsystem.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/DriveSubsystem.cpp
index bd94b68..3372a4d 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/DriveSubsystem.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/DriveSubsystem.cpp
@@ -4,6 +4,8 @@
 
 #include "subsystems/DriveSubsystem.h"
 
+#include <wpi/sendable/SendableBuilder.h>
+
 using namespace DriveConstants;
 
 DriveSubsystem::DriveSubsystem()
@@ -40,14 +42,17 @@
   return (m_leftEncoder.GetDistance() + m_rightEncoder.GetDistance()) / 2.0;
 }
 
-frc::Encoder& DriveSubsystem::GetLeftEncoder() {
-  return m_leftEncoder;
-}
-
-frc::Encoder& DriveSubsystem::GetRightEncoder() {
-  return m_rightEncoder;
-}
-
 void DriveSubsystem::SetMaxOutput(double maxOutput) {
   m_drive.SetMaxOutput(maxOutput);
 }
+
+void DriveSubsystem::InitSendable(wpi::SendableBuilder& builder) {
+  SubsystemBase::InitSendable(builder);
+
+  // Publish encoder distances to telemetry.
+  builder.AddDoubleProperty(
+      "leftDistance", [this] { return m_leftEncoder.GetDistance(); }, nullptr);
+  builder.AddDoubleProperty(
+      "rightDistance", [this] { return m_rightEncoder.GetDistance(); },
+      nullptr);
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/HatchSubsystem.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/HatchSubsystem.cpp
index ba1c0dd..975141f 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/HatchSubsystem.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/HatchSubsystem.cpp
@@ -4,6 +4,8 @@
 
 #include "subsystems/HatchSubsystem.h"
 
+#include <wpi/sendable/SendableBuilder.h>
+
 using namespace HatchConstants;
 
 HatchSubsystem::HatchSubsystem()
@@ -17,3 +19,13 @@
 void HatchSubsystem::ReleaseHatch() {
   m_hatchSolenoid.Set(frc::DoubleSolenoid::kReverse);
 }
+
+void HatchSubsystem::InitSendable(wpi::SendableBuilder& builder) {
+  SubsystemBase::InitSendable(builder);
+
+  // Publish the solenoid state to telemetry.
+  builder.AddBooleanProperty(
+      "extended",
+      [this] { return m_hatchSolenoid.Get() == frc::DoubleSolenoid::kForward; },
+      nullptr);
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/DefaultDrive.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/DefaultDrive.h
index 2e7cac8..7d58f9e 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/DefaultDrive.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/DefaultDrive.h
@@ -4,7 +4,9 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <functional>
+
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/DriveSubsystem.h"
@@ -16,8 +18,7 @@
  *
  * @see RunCommand
  */
-class DefaultDrive
-    : public frc2::CommandHelper<frc2::CommandBase, DefaultDrive> {
+class DefaultDrive : public frc2::CommandHelper<frc2::Command, DefaultDrive> {
  public:
   /**
    * Creates a new DefaultDrive.
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/DriveDistance.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/DriveDistance.h
index 34d2577..5a0fad8 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/DriveDistance.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/DriveDistance.h
@@ -4,13 +4,12 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/DriveSubsystem.h"
 
-class DriveDistance
-    : public frc2::CommandHelper<frc2::CommandBase, DriveDistance> {
+class DriveDistance : public frc2::CommandHelper<frc2::Command, DriveDistance> {
  public:
   /**
    * Creates a new DriveDistance.
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/GrabHatch.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/GrabHatch.h
index 0cf5c5a..c8a36a4 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/GrabHatch.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/GrabHatch.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/HatchSubsystem.h"
@@ -16,7 +16,7 @@
  *
  * @see InstantCommand
  */
-class GrabHatch : public frc2::CommandHelper<frc2::CommandBase, GrabHatch> {
+class GrabHatch : public frc2::CommandHelper<frc2::Command, GrabHatch> {
  public:
   explicit GrabHatch(HatchSubsystem* subsystem);
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/HalveDriveSpeed.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/HalveDriveSpeed.h
index efc572d..52d8281 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/HalveDriveSpeed.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/HalveDriveSpeed.h
@@ -4,13 +4,13 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/DriveSubsystem.h"
 
 class HalveDriveSpeed
-    : public frc2::CommandHelper<frc2::CommandBase, HalveDriveSpeed> {
+    : public frc2::CommandHelper<frc2::Command, HalveDriveSpeed> {
  public:
   explicit HalveDriveSpeed(DriveSubsystem* subsystem);
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/ReleaseHatch.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/ReleaseHatch.h
index e3628cf..0b4b953 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/ReleaseHatch.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/ReleaseHatch.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/HatchSubsystem.h"
@@ -16,8 +16,7 @@
  *
  * @see InstantCommand
  */
-class ReleaseHatch
-    : public frc2::CommandHelper<frc2::CommandBase, ReleaseHatch> {
+class ReleaseHatch : public frc2::CommandHelper<frc2::Command, ReleaseHatch> {
  public:
   explicit ReleaseHatch(HatchSubsystem* subsystem);
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h
index 47bf28e..5984a1a 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h
@@ -44,20 +44,6 @@
   double GetAverageEncoderDistance();
 
   /**
-   * Gets the left drive encoder.
-   *
-   * @return the left drive encoder
-   */
-  frc::Encoder& GetLeftEncoder();
-
-  /**
-   * Gets the right drive encoder.
-   *
-   * @return the right drive encoder
-   */
-  frc::Encoder& GetRightEncoder();
-
-  /**
    * Sets the max output of the drive.  Useful for scaling the drive to drive
    * more slowly.
    *
@@ -65,6 +51,8 @@
    */
   void SetMaxOutput(double maxOutput);
 
+  void InitSendable(wpi::SendableBuilder& builder) override;
+
  private:
   // Components (e.g. motor controllers and sensors) should generally be
   // declared private and exposed only through public methods.
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/HatchSubsystem.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/HatchSubsystem.h
index bb06100..392e9b2 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/HatchSubsystem.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/HatchSubsystem.h
@@ -26,6 +26,8 @@
    */
   void ReleaseHatch();
 
+  void InitSendable(wpi::SendableBuilder& builder) override;
+
  private:
   // Components (e.g. motor controllers and sensors) should generally be
   // declared private and exposed only through public methods.
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/I2CCommunication/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/I2CCommunication/cpp/Robot.cpp
index fad7581..ecfb480 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/I2CCommunication/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/I2CCommunication/cpp/Robot.cpp
@@ -2,45 +2,37 @@
 // 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 <fmt/format.h>
+#include "Robot.h"
 
+#include <fmt/format.h>
 #include <frc/DriverStation.h>
-#include <frc/I2C.h>
-#include <frc/TimedRobot.h>
 #include <frc/Timer.h>
 
-/**
- * This is a sample program demonstrating how to communicate to a light
- * controller from the robot code using the roboRIO's I2C port.
- */
-class Robot : public frc::TimedRobot {
- public:
-  void RobotPeriodic() override {
-    // Creates a string to hold current robot state information, including
-    // alliance, enabled state, operation mode, and match time. The message
-    // is sent in format "AEM###" where A is the alliance color, (R)ed or
-    // (B)lue, E is the enabled state, (E)nabled or (D)isabled, M is the
-    // operation mode, (A)utonomous or (T)eleop, and ### is the zero-padded
-    // time remaining in the match.
-    //
-    // For example, "RET043" would indicate that the robot is on the red
-    // alliance, enabled in teleop mode, with 43 seconds left in the match.
-    auto string = fmt::format(
-        "{}{}{}{:03}",
-        frc::DriverStation::GetAlliance() == frc::DriverStation::Alliance::kRed
-            ? "R"
-            : "B",
-        frc::DriverStation::IsEnabled() ? "E" : "D",
-        frc::DriverStation::IsAutonomous() ? "A" : "T",
-        static_cast<int>(frc::Timer::GetMatchTime().value()));
+void Robot::RobotPeriodic() {
+  // Creates a string to hold current robot state information, including
+  // alliance, enabled state, operation mode, and match time. The message
+  // is sent in format "AEM###" where A is the alliance color, (R)ed or
+  // (B)lue, E is the enabled state, (E)nabled or (D)isabled, M is the
+  // operation mode, (A)utonomous or (T)eleop, and ### is the zero-padded
+  // time remaining in the match.
+  //
+  // For example, "RET043" would indicate that the robot is on the red
+  // alliance, enabled in teleop mode, with 43 seconds left in the match.
 
-    arduino.WriteBulk(reinterpret_cast<uint8_t*>(string.data()), string.size());
+  std::string allianceString = "U";
+  auto alliance = frc::DriverStation::GetAlliance();
+  if (alliance.has_value()) {
+    allianceString = alliance == frc::DriverStation::Alliance::kRed ? "R" : "B";
   }
 
- private:
-  static constexpr int deviceAddress = 4;
-  frc::I2C arduino{frc::I2C::Port::kOnboard, deviceAddress};
-};
+  auto string =
+      fmt::format("{}{}{}{:03}", allianceString,
+                  frc::DriverStation::IsEnabled() ? "E" : "D",
+                  frc::DriverStation::IsAutonomous() ? "A" : "T",
+                  static_cast<int>(frc::Timer::GetMatchTime().value()));
+
+  arduino.WriteBulk(reinterpret_cast<uint8_t*>(string.data()), string.size());
+}
 
 #ifndef RUNNING_FRC_TESTS
 int main() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/I2CCommunication/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/I2CCommunication/include/Robot.h
new file mode 100644
index 0000000..ede2624
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/I2CCommunication/include/Robot.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 <array>
+
+#include <frc/I2C.h>
+#include <frc/TimedRobot.h>
+
+/**
+ * This is a sample program demonstrating how to communicate to a light
+ * controller from the robot code using the roboRIO's I2C port.
+ */
+class Robot : public frc::TimedRobot {
+ public:
+  void RobotPeriodic() override;
+
+  static constexpr frc::I2C::Port kPort = frc::I2C::Port::kOnboard;
+
+ private:
+  static constexpr int deviceAddress = 4;
+  frc::I2C arduino{kPort, deviceAddress};
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/cpp/Drivetrain.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/cpp/Drivetrain.cpp
index a59b758..94b5feb 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/cpp/Drivetrain.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/cpp/Drivetrain.cpp
@@ -49,11 +49,13 @@
 
 void Drivetrain::Drive(units::meters_per_second_t xSpeed,
                        units::meters_per_second_t ySpeed,
-                       units::radians_per_second_t rot, bool fieldRelative) {
-  auto wheelSpeeds = m_kinematics.ToWheelSpeeds(
+                       units::radians_per_second_t rot, bool fieldRelative,
+                       units::second_t period) {
+  auto wheelSpeeds = m_kinematics.ToWheelSpeeds(frc::ChassisSpeeds::Discretize(
       fieldRelative ? frc::ChassisSpeeds::FromFieldRelativeSpeeds(
                           xSpeed, ySpeed, rot, m_gyro.GetRotation2d())
-                    : frc::ChassisSpeeds{xSpeed, ySpeed, rot});
+                    : frc::ChassisSpeeds{xSpeed, ySpeed, rot},
+      period));
   wheelSpeeds.Desaturate(kMaxSpeed);
   SetSpeeds(wheelSpeeds);
 }
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/cpp/Robot.cpp
index 4943184..18955f2 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/cpp/Robot.cpp
@@ -46,7 +46,7 @@
     const auto rot = -m_rotLimiter.Calculate(m_controller.GetRightX()) *
                      Drivetrain::kMaxAngularSpeed;
 
-    m_mecanum.Drive(xSpeed, ySpeed, rot, fieldRelative);
+    m_mecanum.Drive(xSpeed, ySpeed, rot, fieldRelative, GetPeriod());
   }
 };
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/include/Drivetrain.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/include/Drivetrain.h
index fd4ae7a..971fe4c 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/include/Drivetrain.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumBot/include/Drivetrain.h
@@ -35,7 +35,7 @@
   void SetSpeeds(const frc::MecanumDriveWheelSpeeds& wheelSpeeds);
   void Drive(units::meters_per_second_t xSpeed,
              units::meters_per_second_t ySpeed, units::radians_per_second_t rot,
-             bool fieldRelative);
+             bool fieldRelative, units::second_t period);
   void UpdateOdometry();
 
   static constexpr units::meters_per_second_t kMaxSpeed =
@@ -59,10 +59,10 @@
   frc::Translation2d m_backLeftLocation{-0.381_m, 0.381_m};
   frc::Translation2d m_backRightLocation{-0.381_m, -0.381_m};
 
-  frc2::PIDController m_frontLeftPIDController{1.0, 0.0, 0.0};
-  frc2::PIDController m_frontRightPIDController{1.0, 0.0, 0.0};
-  frc2::PIDController m_backLeftPIDController{1.0, 0.0, 0.0};
-  frc2::PIDController m_backRightPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_frontLeftPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_frontRightPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_backLeftPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_backRightPIDController{1.0, 0.0, 0.0};
 
   frc::AnalogGyro m_gyro{0};
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp
index 1e9a9ae..0f691e0 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp
@@ -71,8 +71,8 @@
       frc::SimpleMotorFeedforward<units::meters>(ks, kv, ka),
       DriveConstants::kDriveKinematics,
 
-      frc2::PIDController{AutoConstants::kPXController, 0, 0},
-      frc2::PIDController{AutoConstants::kPYController, 0, 0},
+      frc::PIDController{AutoConstants::kPXController, 0, 0},
+      frc::PIDController{AutoConstants::kPYController, 0, 0},
       frc::ProfiledPIDController<units::radians>(
           AutoConstants::kPThetaController, 0, 0,
           AutoConstants::kThetaControllerConstraints),
@@ -89,10 +89,10 @@
                 m_drive.GetRearRightEncoder().GetRate()}};
       },
 
-      frc2::PIDController{DriveConstants::kPFrontLeftVel, 0, 0},
-      frc2::PIDController{DriveConstants::kPRearLeftVel, 0, 0},
-      frc2::PIDController{DriveConstants::kPFrontRightVel, 0, 0},
-      frc2::PIDController{DriveConstants::kPRearRightVel, 0, 0},
+      frc::PIDController{DriveConstants::kPFrontLeftVel, 0, 0},
+      frc::PIDController{DriveConstants::kPRearLeftVel, 0, 0},
+      frc::PIDController{DriveConstants::kPFrontRightVel, 0, 0},
+      frc::PIDController{DriveConstants::kPRearRightVel, 0, 0},
 
       [this](units::volt_t frontLeft, units::volt_t rearLeft,
              units::volt_t frontRight, units::volt_t rearRight) {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp
index 9120fa6..292ad1f 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp
@@ -50,9 +50,9 @@
 void DriveSubsystem::Drive(double xSpeed, double ySpeed, double rot,
                            bool fieldRelative) {
   if (fieldRelative) {
-    m_drive.DriveCartesian(ySpeed, xSpeed, rot, m_gyro.GetRotation2d());
+    m_drive.DriveCartesian(xSpeed, ySpeed, rot, m_gyro.GetRotation2d());
   } else {
-    m_drive.DriveCartesian(ySpeed, xSpeed, rot);
+    m_drive.DriveCartesian(xSpeed, ySpeed, rot);
   }
 }
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp
index 2b57f3c..8d9d7ae 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp
@@ -21,7 +21,7 @@
   }
 
   void TeleopPeriodic() override {
-    /* Use the joystick X axis for forward movement, Y axis for lateral
+    /* Use the joystick Y axis for forward movement, X axis for lateral
      * movement, and Z axis for rotation.
      */
     m_robotDrive.DriveCartesian(-m_stick.GetY(), -m_stick.GetX(),
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/cpp/Drivetrain.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/cpp/Drivetrain.cpp
index c1046dd..9f1b12b 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/cpp/Drivetrain.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/cpp/Drivetrain.cpp
@@ -24,7 +24,7 @@
 
 void Drivetrain::SetSpeeds(const frc::MecanumDriveWheelSpeeds& wheelSpeeds) {
   std::function<void(units::meters_per_second_t, const frc::Encoder&,
-                     frc2::PIDController&, frc::PWMSparkMax&)>
+                     frc::PIDController&, frc::PWMSparkMax&)>
       calcAndSetSpeeds = [&m_feedforward = m_feedforward](
                              units::meters_per_second_t speed,
                              const auto& encoder, auto& controller,
@@ -46,11 +46,13 @@
 
 void Drivetrain::Drive(units::meters_per_second_t xSpeed,
                        units::meters_per_second_t ySpeed,
-                       units::radians_per_second_t rot, bool fieldRelative) {
-  auto wheelSpeeds = m_kinematics.ToWheelSpeeds(
+                       units::radians_per_second_t rot, bool fieldRelative,
+                       units::second_t period) {
+  auto wheelSpeeds = m_kinematics.ToWheelSpeeds(frc::ChassisSpeeds::Discretize(
       fieldRelative ? frc::ChassisSpeeds::FromFieldRelativeSpeeds(
                           xSpeed, ySpeed, rot, m_gyro.GetRotation2d())
-                    : frc::ChassisSpeeds{xSpeed, ySpeed, rot});
+                    : frc::ChassisSpeeds{xSpeed, ySpeed, rot},
+      period));
   wheelSpeeds.Desaturate(kMaxSpeed);
   SetSpeeds(wheelSpeeds);
 }
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/cpp/Robot.cpp
index 4943184..18955f2 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/cpp/Robot.cpp
@@ -46,7 +46,7 @@
     const auto rot = -m_rotLimiter.Calculate(m_controller.GetRightX()) *
                      Drivetrain::kMaxAngularSpeed;
 
-    m_mecanum.Drive(xSpeed, ySpeed, rot, fieldRelative);
+    m_mecanum.Drive(xSpeed, ySpeed, rot, fieldRelative, GetPeriod());
   }
 };
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/include/Drivetrain.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/include/Drivetrain.h
index eeaf7af..2dcc50c 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/include/Drivetrain.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/include/Drivetrain.h
@@ -36,7 +36,7 @@
   void SetSpeeds(const frc::MecanumDriveWheelSpeeds& wheelSpeeds);
   void Drive(units::meters_per_second_t xSpeed,
              units::meters_per_second_t ySpeed, units::radians_per_second_t rot,
-             bool fieldRelative);
+             bool fieldRelative, units::second_t period);
   void UpdateOdometry();
 
   static constexpr auto kMaxSpeed = 3.0_mps;  // 3 meters per second
@@ -59,10 +59,10 @@
   frc::Translation2d m_backLeftLocation{-0.381_m, 0.381_m};
   frc::Translation2d m_backRightLocation{-0.381_m, -0.381_m};
 
-  frc2::PIDController m_frontLeftPIDController{1.0, 0.0, 0.0};
-  frc2::PIDController m_frontRightPIDController{1.0, 0.0, 0.0};
-  frc2::PIDController m_backLeftPIDController{1.0, 0.0, 0.0};
-  frc2::PIDController m_backRightPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_frontLeftPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_frontRightPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_backLeftPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_backRightPIDController{1.0, 0.0, 0.0};
 
   frc::AnalogGyro m_gyro{0};
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/cpp/Robot.cpp
index 5b6b024..69cebaf 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/cpp/Robot.cpp
@@ -2,72 +2,32 @@
 // 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 <array>
+#include "Robot.h"
 
-#include <frc/AnalogInput.h>
-#include <frc/Joystick.h>
-#include <frc/TimedRobot.h>
-#include <frc/controller/PIDController.h>
-#include <frc/motorcontrol/PWMSparkMax.h>
+void Robot::TeleopInit() {
+  // Move to the bottom setpoint when teleop starts
+  m_index = 0;
+  m_pidController.SetSetpoint(kSetpoints[m_index].value());
+}
 
-/**
- * This is a sample program to demonstrate how to use a soft potentiometer and a
- * PID Controller to reach and maintain position setpoints on an elevator
- * mechanism.
- */
-class Robot : public frc::TimedRobot {
- public:
-  void RobotInit() override {
-    m_pidController.SetSetpoint(kSetPoints[m_index]);
+void Robot::TeleopPeriodic() {
+  // Read from the sensor
+  units::meter_t position = units::meter_t{m_potentiometer.Get()};
+
+  // Run the PID Controller
+  double pidOut = m_pidController.Calculate(position.value());
+
+  // Apply PID output
+  m_elevatorMotor.Set(pidOut);
+
+  // when the button is pressed once, the selected elevator setpoint is
+  // incremented
+  if (m_joystick.GetTriggerPressed()) {
+    // index of the elevator setpoint wraps around.
+    m_index = (m_index + 1) % kSetpoints.size();
+    m_pidController.SetSetpoint(kSetpoints[m_index].value());
   }
-
-  void TeleopPeriodic() override {
-    // When the button is pressed once, the selected elevator setpoint is
-    // incremented.
-    bool currentButtonValue = m_joystick.GetTrigger();
-    if (currentButtonValue && !m_previousButtonValue) {
-      // Index of the elevator setpoint wraps around
-      m_index = (m_index + 1) % (sizeof(kSetPoints) / 8);
-      m_pidController.SetSetpoint(kSetPoints[m_index]);
-    }
-    m_previousButtonValue = currentButtonValue;
-
-    double output =
-        m_pidController.Calculate(m_potentiometer.GetAverageVoltage());
-    m_elevatorMotor.Set(output);
-  }
-
- private:
-  static constexpr int kPotChannel = 1;
-  static constexpr int kMotorChannel = 7;
-  static constexpr int kJoystickChannel = 0;
-
-  // Bottom, middle, and top elevator setpoints
-  static constexpr std::array<double, 3> kSetPoints = {{1.0, 2.6, 4.3}};
-
-  /* Proportional, integral, and derivative speed constants; motor inverted.
-   *
-   * DANGER: When tuning PID constants, high/inappropriate values for pGain,
-   * iGain, and dGain may cause dangerous, uncontrollable, or undesired
-   * behavior!
-   *
-   * These may need to be positive for a non-inverted motor.
-   */
-  static constexpr double kP = -5.0;
-  static constexpr double kI = -0.02;
-  static constexpr double kD = -2.0;
-
-  int m_index = 0;
-  bool m_previousButtonValue = false;
-
-  frc::AnalogInput m_potentiometer{kPotChannel};
-  frc::Joystick m_joystick{kJoystickChannel};
-  frc::PWMSparkMax m_elevatorMotor{kMotorChannel};
-
-  frc2::PIDController m_pidController{kP, kI, kD};
-};
-
-constexpr std::array<double, 3> Robot::kSetPoints;
+}
 
 #ifndef RUNNING_FRC_TESTS
 int main() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/include/Robot.h
new file mode 100644
index 0000000..ab843a1
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/include/Robot.h
@@ -0,0 +1,55 @@
+// 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 <array>
+
+#include <frc/AnalogPotentiometer.h>
+#include <frc/Joystick.h>
+#include <frc/TimedRobot.h>
+#include <frc/controller/PIDController.h>
+#include <frc/motorcontrol/PWMSparkMax.h>
+#include <units/length.h>
+
+/**
+ * This is a sample program to demonstrate how to use a soft potentiometer and a
+ * PID controller to reach and maintain position setpoints on an elevator
+ * mechanism.
+ */
+class Robot : public frc::TimedRobot {
+ public:
+  void TeleopInit() override;
+  void TeleopPeriodic() override;
+
+  static constexpr int kPotChannel = 1;
+  static constexpr int kMotorChannel = 7;
+  static constexpr int kJoystickChannel = 3;
+
+  // The elevator can move 1.5 meters from top to bottom
+  static constexpr units::meter_t kFullHeight = 1.5_m;
+
+  // Bottom, middle, and top elevator setpoints
+  static constexpr std::array<units::meter_t, 3> kSetpoints = {
+      {0.2_m, 0.8_m, 1.4_m}};
+
+ private:
+  // proportional speed constant
+  // negative because applying positive voltage will bring us closer to the
+  // target
+  static constexpr double kP = 0.7;
+  // integral speed constant
+  static constexpr double kI = 0.35;
+  // derivative speed constant
+  static constexpr double kD = 0.25;
+
+  // Scaling is handled internally
+  frc::AnalogPotentiometer m_potentiometer{kPotChannel, kFullHeight.value()};
+
+  frc::PWMSparkMax m_elevatorMotor{kMotorChannel};
+  frc::PIDController m_pidController{kP, kI, kD};
+  frc::Joystick m_joystick{kJoystickChannel};
+
+  size_t m_index;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/Robot.cpp
index c7eab48..c4c1661 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/Robot.cpp
@@ -37,7 +37,7 @@
 void Robot::AutonomousInit() {
   m_autonomousCommand = m_container.GetAutonomousCommand();
 
-  if (m_autonomousCommand != nullptr) {
+  if (m_autonomousCommand) {
     m_autonomousCommand->Schedule();
   }
 }
@@ -49,9 +49,9 @@
   // teleop starts running. If you want the autonomous to
   // continue until interrupted by another command, remove
   // this line or comment it out.
-  if (m_autonomousCommand != nullptr) {
+  if (m_autonomousCommand) {
     m_autonomousCommand->Cancel();
-    m_autonomousCommand = nullptr;
+    m_autonomousCommand.reset();
   }
 }
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/RobotContainer.cpp
index a72b129..e880af2 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/RobotContainer.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/RobotContainer.cpp
@@ -12,9 +12,8 @@
 #include <frc/trajectory/Trajectory.h>
 #include <frc/trajectory/TrajectoryGenerator.h>
 #include <frc/trajectory/constraint/DifferentialDriveVoltageConstraint.h>
-#include <frc2/command/InstantCommand.h>
+#include <frc2/command/Commands.h>
 #include <frc2/command/RamseteCommand.h>
-#include <frc2/command/SequentialCommandGroup.h>
 #include <frc2/command/button/JoystickButton.h>
 
 #include "Constants.h"
@@ -26,7 +25,7 @@
   ConfigureButtonBindings();
 
   // Set up default drive command
-  m_drive.SetDefaultCommand(frc2::RunCommand(
+  m_drive.SetDefaultCommand(frc2::cmd::Run(
       [this] {
         m_drive.ArcadeDrive(-m_driverController.GetLeftY(),
                             -m_driverController.GetRightX());
@@ -43,7 +42,7 @@
       .OnFalse(&m_driveFullSpeed);
 }
 
-frc2::Command* RobotContainer::GetAutonomousCommand() {
+frc2::CommandPtr RobotContainer::GetAutonomousCommand() {
   // Create a voltage constraint to ensure we don't accelerate too fast
   frc::DifferentialDriveVoltageConstraint autoVoltageConstraint{
       frc::SimpleMotorFeedforward<units::meters>{
@@ -69,25 +68,23 @@
       // Pass the config
       config);
 
-  frc2::RamseteCommand ramseteCommand{
-      exampleTrajectory,
-      [this]() { return m_drive.GetPose(); },
+  frc2::CommandPtr ramseteCommand{frc2::RamseteCommand(
+      exampleTrajectory, [this] { return m_drive.GetPose(); },
       frc::RamseteController{AutoConstants::kRamseteB,
                              AutoConstants::kRamseteZeta},
       frc::SimpleMotorFeedforward<units::meters>{
           DriveConstants::ks, DriveConstants::kv, DriveConstants::ka},
       DriveConstants::kDriveKinematics,
       [this] { return m_drive.GetWheelSpeeds(); },
-      frc2::PIDController{DriveConstants::kPDriveVel, 0, 0},
-      frc2::PIDController{DriveConstants::kPDriveVel, 0, 0},
+      frc::PIDController{DriveConstants::kPDriveVel, 0, 0},
+      frc::PIDController{DriveConstants::kPDriveVel, 0, 0},
       [this](auto left, auto right) { m_drive.TankDriveVolts(left, right); },
-      {&m_drive}};
+      {&m_drive})};
 
   // Reset odometry to the starting pose of the trajectory.
   m_drive.ResetOdometry(exampleTrajectory.InitialPose());
 
-  // no auto
-  return new frc2::SequentialCommandGroup(
-      std::move(ramseteCommand),
-      frc2::InstantCommand([this] { m_drive.TankDriveVolts(0_V, 0_V); }, {}));
+  return std::move(ramseteCommand)
+      .BeforeStarting(
+          frc2::cmd::RunOnce([this] { m_drive.TankDriveVolts(0_V, 0_V); }, {}));
 }
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/subsystems/DriveSubsystem.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/subsystems/DriveSubsystem.cpp
index f8ec1db..0bc598e 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/subsystems/DriveSubsystem.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/subsystems/DriveSubsystem.cpp
@@ -23,8 +23,8 @@
   m_rightMotors.SetInverted(true);
 
   // Set the distance per pulse for the encoders
-  m_leftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse);
-  m_rightEncoder.SetDistancePerPulse(kEncoderDistancePerPulse);
+  m_leftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse.value());
+  m_rightEncoder.SetDistancePerPulse(kEncoderDistancePerPulse.value());
 
   ResetEncoders();
 }
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/Constants.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/Constants.h
index 18747d4..f7a061a 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/Constants.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/Constants.h
@@ -39,11 +39,10 @@
 extern const frc::DifferentialDriveKinematics kDriveKinematics;
 
 constexpr int kEncoderCPR = 1024;
-constexpr double kWheelDiameterInches = 6;
-constexpr double kEncoderDistancePerPulse =
+constexpr units::meter_t kWheelDiameter = 6_in;
+constexpr auto kEncoderDistancePerPulse =
     // Assumes the encoders are directly mounted on the wheel shafts
-    (kWheelDiameterInches * std::numbers::pi) /
-    static_cast<double>(kEncoderCPR);
+    (kWheelDiameter * std::numbers::pi) / static_cast<double>(kEncoderCPR);
 
 // These are example values only - DO NOT USE THESE FOR YOUR OWN ROBOT!
 // These characterization values MUST be determined either experimentally or
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/Robot.h
index a82f2ac..889bfdf 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/Robot.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/Robot.h
@@ -4,8 +4,10 @@
 
 #pragma once
 
+#include <optional>
+
 #include <frc/TimedRobot.h>
-#include <frc2/command/Command.h>
+#include <frc2/command/CommandPtr.h>
 
 #include "RobotContainer.h"
 
@@ -22,9 +24,8 @@
   void TestPeriodic() override;
 
  private:
-  // Have it null by default so that if testing teleop it
-  // doesn't have undefined behavior and potentially crash.
-  frc2::Command* m_autonomousCommand = nullptr;
+  // Have it empty by default
+  std::optional<frc2::CommandPtr> m_autonomousCommand;
 
   RobotContainer m_container;
 };
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/RobotContainer.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/RobotContainer.h
index 3d6a36f..b01e6e2 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/RobotContainer.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/RobotContainer.h
@@ -7,11 +7,8 @@
 #include <frc/XboxController.h>
 #include <frc/controller/PIDController.h>
 #include <frc/smartdashboard/SendableChooser.h>
-#include <frc2/command/Command.h>
+#include <frc2/command/CommandPtr.h>
 #include <frc2/command/InstantCommand.h>
-#include <frc2/command/PIDCommand.h>
-#include <frc2/command/ParallelRaceGroup.h>
-#include <frc2/command/RunCommand.h>
 
 #include "Constants.h"
 #include "subsystems/DriveSubsystem.h"
@@ -27,7 +24,7 @@
  public:
   RobotContainer();
 
-  frc2::Command* GetAutonomousCommand();
+  frc2::CommandPtr GetAutonomousCommand();
 
  private:
   // The driver's controller
@@ -38,13 +35,11 @@
   // The robot's subsystems
   DriveSubsystem m_drive;
 
+  // RobotContainer-owned commands
   frc2::InstantCommand m_driveHalfSpeed{[this] { m_drive.SetMaxOutput(0.5); },
                                         {}};
   frc2::InstantCommand m_driveFullSpeed{[this] { m_drive.SetMaxOutput(1); },
                                         {}};
 
-  // The chooser for the autonomous routines
-  frc::SendableChooser<frc2::Command*> m_chooser;
-
   void ConfigureButtonBindings();
 };
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h
index 40cc715..341cd38 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h
@@ -70,8 +70,8 @@
   frc::Encoder m_leftEncoder{0, 1};
   frc::Encoder m_rightEncoder{2, 3};
 
-  frc2::PIDController m_leftPIDController{1.0, 0.0, 0.0};
-  frc2::PIDController m_rightPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_leftPIDController{1.0, 0.0, 0.0};
+  frc::PIDController m_rightPIDController{1.0, 0.0, 0.0};
 
   frc::AnalogGyro m_gyro{0};
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/RapidReactCommandBot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/RapidReactCommandBot.cpp
index c25f0e8..1b46aff 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/RapidReactCommandBot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/RapidReactCommandBot.cpp
@@ -40,6 +40,10 @@
           m_storage.RunCommand())
           // Since we composed this inline we should give it a name
           .WithName("Shoot"));
+
+  // Toggle compressor with the Start button
+  m_driverController.Start().ToggleOnTrue(
+      m_pneumatics.DisableCompressorCommand());
 }
 
 frc2::CommandPtr RapidReactCommandBot::GetAutonomousCommand() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Pneumatics.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Pneumatics.cpp
new file mode 100644
index 0000000..e19040a
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Pneumatics.cpp
@@ -0,0 +1,27 @@
+// 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 "subsystems/Pneumatics.h"
+
+Pneumatics::Pneumatics() {}
+
+frc2::CommandPtr Pneumatics::DisableCompressorCommand() {
+  return StartEnd(
+      [&] {
+        // Disable closed-loop mode on the compressor.
+        m_compressor.Disable();
+      },
+      [&] {
+        // Enable closed-loop mode based on the digital pressure switch
+        // connected to the PCM/PH. The switch is open when the pressure is over
+        // ~120 PSI.
+        m_compressor.EnableDigital();
+      });
+}
+
+units::pounds_per_square_inch_t Pneumatics::GetPressure() {
+  // Get the pressure (in PSI) from an analog pressure sensor connected to
+  // the RIO.
+  return units::pounds_per_square_inch_t{m_pressureTransducer.Get()};
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/RapidReactCommandBot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/RapidReactCommandBot.h
index 4f733b7..0071a83 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/RapidReactCommandBot.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/RapidReactCommandBot.h
@@ -10,6 +10,7 @@
 #include "Constants.h"
 #include "subsystems/Drive.h"
 #include "subsystems/Intake.h"
+#include "subsystems/Pneumatics.h"
 #include "subsystems/Shooter.h"
 #include "subsystems/Storage.h"
 
@@ -45,6 +46,7 @@
   Intake m_intake;
   Shooter m_shooter;
   Storage m_storage;
+  Pneumatics m_pneumatics;
 
   // The driver's controller
   frc2::CommandXboxController m_driverController{
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Drive.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Drive.h
index 9a39a14..ac96c52 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Drive.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Drive.h
@@ -25,8 +25,9 @@
    * @param fwd the commanded forward movement
    * @param rot the commanded rotation
    */
-  [[nodiscard]] frc2::CommandPtr ArcadeDriveCommand(
-      std::function<double()> fwd, std::function<double()> rot);
+  [[nodiscard]]
+  frc2::CommandPtr ArcadeDriveCommand(std::function<double()> fwd,
+                                      std::function<double()> rot);
 
   /**
    * Returns a command that drives the robot forward a specified distance at a
@@ -35,8 +36,8 @@
    * @param distance The distance to drive forward in meters
    * @param speed The fraction of max speed at which to drive
    */
-  [[nodiscard]] frc2::CommandPtr DriveDistanceCommand(units::meter_t distance,
-                                                      double speed);
+  [[nodiscard]]
+  frc2::CommandPtr DriveDistanceCommand(units::meter_t distance, double speed);
 
  private:
   frc::PWMSparkMax m_leftLeader{DriveConstants::kLeftMotor1Port};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Intake.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Intake.h
index af8d39a..111353a 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Intake.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Intake.h
@@ -19,14 +19,18 @@
 
   /** Returns a command that deploys the intake, and then runs the intake motor
    * indefinitely. */
-  [[nodiscard]] frc2::CommandPtr IntakeCommand();
+  [[nodiscard]]
+  frc2::CommandPtr IntakeCommand();
 
   /** Returns a command that turns off and retracts the intake. */
-  [[nodiscard]] frc2::CommandPtr RetractCommand();
+  [[nodiscard]]
+  frc2::CommandPtr RetractCommand();
 
  private:
   frc::PWMSparkMax m_motor{IntakeConstants::kMotorPort};
-  frc::DoubleSolenoid m_piston{frc::PneumaticsModuleType::REVPH,
+
+  // Double solenoid connected to two channels of a PCM with the default CAN ID
+  frc::DoubleSolenoid m_piston{frc::PneumaticsModuleType::CTREPCM,
                                IntakeConstants::kSolenoidPorts[0],
                                IntakeConstants::kSolenoidPorts[1]};
 };
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Pneumatics.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Pneumatics.h
new file mode 100644
index 0000000..0c1fee9
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Pneumatics.h
@@ -0,0 +1,44 @@
+// 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 <frc/AnalogPotentiometer.h>
+#include <frc/Compressor.h>
+#include <frc/PneumaticsControlModule.h>
+#include <frc2/command/CommandPtr.h>
+#include <frc2/command/SubsystemBase.h>
+#include <units/pressure.h>
+
+#include "Constants.h"
+
+class Pneumatics : frc2::SubsystemBase {
+ public:
+  Pneumatics();
+  /** Returns a command that disables the compressor indefinitely. */
+  [[nodiscard]]
+  frc2::CommandPtr DisableCompressorCommand();
+
+  /**
+   * Query the analog pressure sensor.
+   *
+   * @return the measured pressure, in PSI
+   */
+  units::pounds_per_square_inch_t GetPressure();
+
+ private:
+  // External analog pressure sensor
+  // product-specific voltage->pressure conversion, see product manual
+  // in this case, 250(V/5)-25
+  // the scale parameter in the AnalogPotentiometer constructor is scaled from
+  // 1 instead of 5, so if r is the raw AnalogPotentiometer output, the
+  // pressure is 250r-25
+  static constexpr double kScale = 250;
+  static constexpr double kOffset = -25;
+  frc::AnalogPotentiometer m_pressureTransducer{/* the AnalogIn port*/ 2,
+                                                kScale, kOffset};
+
+  // Compressor connected to a PH with a default CAN ID
+  frc::Compressor m_compressor{frc::PneumaticsModuleType::CTREPCM};
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Shooter.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Shooter.h
index 5ab2a63..b311559 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Shooter.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Shooter.h
@@ -28,8 +28,8 @@
    *
    * @param setpointRotationsPerSecond The desired shooter velocity
    */
-  [[nodiscard]] frc2::CommandPtr ShootCommand(
-      units::turns_per_second_t setpoint);
+  [[nodiscard]]
+  frc2::CommandPtr ShootCommand(units::turns_per_second_t setpoint);
 
  private:
   frc::PWMSparkMax m_shooterMotor{ShooterConstants::kShooterMotorPort};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Storage.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Storage.h
index eab6da4..58694b3 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Storage.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Storage.h
@@ -15,7 +15,8 @@
  public:
   Storage();
   /** Returns a command that runs the storage motor indefinitely. */
-  [[nodiscard]] frc2::CommandPtr RunCommand();
+  [[nodiscard]]
+  frc2::CommandPtr RunCommand();
 
   /** Whether the ball storage is full. */
   bool IsFull() const;
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/RobotContainer.cpp
index f6e1fa4..f068a7d 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/RobotContainer.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/RobotContainer.cpp
@@ -6,7 +6,6 @@
 
 #include <frc/smartdashboard/SmartDashboard.h>
 #include <frc2/command/Commands.h>
-#include <frc2/command/button/Button.h>
 
 #include "commands/TeleopArcadeDrive.h"
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/commands/TeleopArcadeDrive.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/commands/TeleopArcadeDrive.cpp
index 0f58527..e457af9 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/commands/TeleopArcadeDrive.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/commands/TeleopArcadeDrive.cpp
@@ -12,7 +12,7 @@
     : m_drive{subsystem},
       m_xaxisSpeedSupplier{xaxisSpeedSupplier},
       m_zaxisRotateSupplier{zaxisRotateSuppplier} {
-  AddRequirements({subsystem});
+  AddRequirements(subsystem);
 }
 
 void TeleopArcadeDrive::Execute() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/sensors/RomiGyro.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/sensors/RomiGyro.cpp
deleted file mode 100644
index 08a537b..0000000
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/sensors/RomiGyro.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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 "sensors/RomiGyro.h"
-
-RomiGyro::RomiGyro() : m_simDevice("Gyro:RomiGyro") {
-  if (m_simDevice) {
-    m_simDevice.CreateBoolean("init", hal::SimDevice::kOutput, true);
-    m_simRateX =
-        m_simDevice.CreateDouble("rate_x", hal::SimDevice::kInput, 0.0);
-    m_simRateY =
-        m_simDevice.CreateDouble("rate_y", hal::SimDevice::kInput, 0.0);
-    m_simRateZ =
-        m_simDevice.CreateDouble("rate_z", hal::SimDevice::kInput, 0.0);
-    m_simAngleX =
-        m_simDevice.CreateDouble("angle_x", hal::SimDevice::kInput, 0.0);
-    m_simAngleY =
-        m_simDevice.CreateDouble("angle_y", hal::SimDevice::kInput, 0.0);
-    m_simAngleZ =
-        m_simDevice.CreateDouble("angle_z", hal::SimDevice::kInput, 0.0);
-  }
-}
-
-double RomiGyro::GetRateX() {
-  if (m_simRateX) {
-    return m_simRateX.Get();
-  }
-
-  return 0.0;
-}
-
-double RomiGyro::GetRateY() {
-  if (m_simRateY) {
-    return m_simRateY.Get();
-  }
-
-  return 0.0;
-}
-
-double RomiGyro::GetRateZ() {
-  if (m_simRateZ) {
-    return m_simRateZ.Get();
-  }
-
-  return 0.0;
-}
-
-double RomiGyro::GetAngleX() {
-  if (m_simAngleX) {
-    return m_simAngleX.Get() - m_angleXOffset;
-  }
-
-  return 0.0;
-}
-
-double RomiGyro::GetAngleY() {
-  if (m_simAngleY) {
-    return m_simAngleY.Get() - m_angleYOffset;
-  }
-
-  return 0.0;
-}
-
-double RomiGyro::GetAngleZ() {
-  if (m_simAngleZ) {
-    return m_simAngleZ.Get() - m_angleZOffset;
-  }
-
-  return 0.0;
-}
-
-void RomiGyro::Reset() {
-  if (m_simAngleX) {
-    m_angleXOffset = m_simAngleX.Get();
-    m_angleYOffset = m_simAngleY.Get();
-    m_angleZOffset = m_simAngleZ.Get();
-  }
-}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/subsystems/OnBoardIO.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/subsystems/OnBoardIO.cpp
deleted file mode 100644
index 1b76c74..0000000
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/subsystems/OnBoardIO.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-// 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 "subsystems/OnBoardIO.h"
-
-#include <frc/DigitalInput.h>
-#include <frc/DigitalOutput.h>
-#include <frc/Errors.h>
-#include <frc/Timer.h>
-
-OnBoardIO::OnBoardIO(OnBoardIO::ChannelMode dio1, OnBoardIO::ChannelMode dio2) {
-  if (dio1 == ChannelMode::INPUT) {
-    m_buttonB = std::make_unique<frc::DigitalInput>(1);
-  } else {
-    m_greenLed = std::make_unique<frc::DigitalOutput>(1);
-  }
-  if (dio2 == ChannelMode::INPUT) {
-    m_buttonC = std::make_unique<frc::DigitalInput>(2);
-  } else {
-    m_redLed = std::make_unique<frc::DigitalOutput>(2);
-  }
-}
-
-bool OnBoardIO::GetButtonAPressed() {
-  return m_buttonA.Get();
-}
-
-bool OnBoardIO::GetButtonBPressed() {
-  if (m_buttonB) {
-    return m_buttonB->Get();
-  }
-
-  auto currentTime = frc::Timer::GetFPGATimestamp();
-  if (currentTime > m_nextMessageTime) {
-    FRC_ReportError(frc::err::Error, "Button {} was not configured", "B");
-    m_nextMessageTime = currentTime + kMessageInterval;
-  }
-  return false;
-}
-
-bool OnBoardIO::GetButtonCPressed() {
-  if (m_buttonC) {
-    return m_buttonC->Get();
-  }
-
-  auto currentTime = frc::Timer::GetFPGATimestamp();
-  if (currentTime > m_nextMessageTime) {
-    FRC_ReportError(frc::err::Error, "Button {} was not configured", "C");
-    m_nextMessageTime = currentTime + kMessageInterval;
-  }
-  return false;
-}
-
-void OnBoardIO::SetGreenLed(bool value) {
-  if (m_greenLed) {
-    m_greenLed->Set(value);
-  } else {
-    auto currentTime = frc::Timer::GetFPGATimestamp();
-    if (currentTime > m_nextMessageTime) {
-      FRC_ReportError(frc::err::Error, "{} LED was not configured", "Green");
-      m_nextMessageTime = currentTime + kMessageInterval;
-    }
-  }
-}
-
-void OnBoardIO::SetRedLed(bool value) {
-  if (m_redLed) {
-    m_redLed->Set(value);
-  } else {
-    auto currentTime = frc::Timer::GetFPGATimestamp();
-    if (currentTime > m_nextMessageTime) {
-      FRC_ReportError(frc::err::Error, "{} LED was not configured", "Red");
-      m_nextMessageTime = currentTime + kMessageInterval;
-    }
-  }
-}
-
-void OnBoardIO::SetYellowLed(bool value) {
-  m_yellowLed.Set(value);
-}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/RobotContainer.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/RobotContainer.h
index ab88b15..0a77614 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/RobotContainer.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/RobotContainer.h
@@ -5,16 +5,16 @@
 #pragma once
 
 #include <frc/Joystick.h>
+#include <frc/romi/OnBoardIO.h>
 #include <frc/smartdashboard/SendableChooser.h>
 #include <frc2/command/Command.h>
 #include <frc2/command/CommandPtr.h>
-#include <frc2/command/button/Button.h>
+#include <frc2/command/button/Trigger.h>
 
 #include "Constants.h"
 #include "commands/AutonomousDistance.h"
 #include "commands/AutonomousTime.h"
 #include "subsystems/Drivetrain.h"
-#include "subsystems/OnBoardIO.h"
 
 /**
  * This class is where the bulk of the robot should be declared.  Since
@@ -41,14 +41,14 @@
   frc2::Command* GetAutonomousCommand();
 
  private:
-  // Assumes a gamepad plugged into channnel 0
+  // Assumes a gamepad plugged into channel 0
   frc::Joystick m_controller{0};
   frc::SendableChooser<frc2::Command*> m_chooser;
 
   // The robot's subsystems
   Drivetrain m_drive;
-  OnBoardIO m_onboardIO{OnBoardIO::ChannelMode::INPUT,
-                        OnBoardIO::ChannelMode::INPUT};
+  frc::OnBoardIO m_onboardIO{frc::OnBoardIO::ChannelMode::INPUT,
+                             frc::OnBoardIO::ChannelMode::INPUT};
 
   // Example button
   frc2::Trigger m_onboardButtonA{
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/DriveDistance.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/DriveDistance.h
index 34a1e93..6011531 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/DriveDistance.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/DriveDistance.h
@@ -4,18 +4,17 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 #include <units/length.h>
 
 #include "subsystems/Drivetrain.h"
 
-class DriveDistance
-    : public frc2::CommandHelper<frc2::CommandBase, DriveDistance> {
+class DriveDistance : public frc2::CommandHelper<frc2::Command, DriveDistance> {
  public:
   DriveDistance(double speed, units::meter_t distance, Drivetrain* drive)
       : m_speed(speed), m_distance(distance), m_drive(drive) {
-    AddRequirements({m_drive});
+    AddRequirements(m_drive);
   }
 
   void Initialize() override;
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/DriveTime.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/DriveTime.h
index a0135e5..9dfd240 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/DriveTime.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/DriveTime.h
@@ -5,17 +5,17 @@
 #pragma once
 
 #include <frc/Timer.h>
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 #include <units/time.h>
 
 #include "subsystems/Drivetrain.h"
 
-class DriveTime : public frc2::CommandHelper<frc2::CommandBase, DriveTime> {
+class DriveTime : public frc2::CommandHelper<frc2::Command, DriveTime> {
  public:
   DriveTime(double speed, units::second_t time, Drivetrain* drive)
       : m_speed(speed), m_duration(time), m_drive(drive) {
-    AddRequirements({m_drive});
+    AddRequirements(m_drive);
   }
 
   void Initialize() override;
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TeleopArcadeDrive.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TeleopArcadeDrive.h
index 859b6b7..fe2e241 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TeleopArcadeDrive.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TeleopArcadeDrive.h
@@ -4,13 +4,15 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <functional>
+
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/Drivetrain.h"
 
 class TeleopArcadeDrive
-    : public frc2::CommandHelper<frc2::CommandBase, TeleopArcadeDrive> {
+    : public frc2::CommandHelper<frc2::Command, TeleopArcadeDrive> {
  public:
   TeleopArcadeDrive(Drivetrain* subsystem,
                     std::function<double()> xaxisSpeedSupplier,
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TurnDegrees.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TurnDegrees.h
index 7ce65df..c0f25b1 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TurnDegrees.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TurnDegrees.h
@@ -4,18 +4,18 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 #include <units/angle.h>
 #include <units/length.h>
 
 #include "subsystems/Drivetrain.h"
 
-class TurnDegrees : public frc2::CommandHelper<frc2::CommandBase, TurnDegrees> {
+class TurnDegrees : public frc2::CommandHelper<frc2::Command, TurnDegrees> {
  public:
   TurnDegrees(double speed, units::degree_t angle, Drivetrain* drive)
       : m_speed(speed), m_angle(angle), m_drive(drive) {
-    AddRequirements({m_drive});
+    AddRequirements(m_drive);
   }
 
   void Initialize() override;
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TurnTime.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TurnTime.h
index 395825d..3c478a8 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TurnTime.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/commands/TurnTime.h
@@ -5,17 +5,17 @@
 #pragma once
 
 #include <frc/Timer.h>
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 #include <units/time.h>
 
 #include "subsystems/Drivetrain.h"
 
-class TurnTime : public frc2::CommandHelper<frc2::CommandBase, TurnTime> {
+class TurnTime : public frc2::CommandHelper<frc2::Command, TurnTime> {
  public:
   TurnTime(double speed, units::second_t time, Drivetrain* drive)
       : m_speed(speed), m_duration(time), m_drive(drive) {
-    AddRequirements({m_drive});
+    AddRequirements(m_drive);
   }
 
   void Initialize() override;
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/sensors/RomiGyro.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/sensors/RomiGyro.h
deleted file mode 100644
index 0e93d48..0000000
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/sensors/RomiGyro.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 <hal/SimDevice.h>
-
-class RomiGyro {
- public:
-  RomiGyro();
-
-  /**
-   * Gets the rate of turn in degrees-per-second around the X-axis
-   */
-  double GetRateX();
-
-  /**
-   * Gets the rate of turn in degrees-per-second around the Y-axis
-   */
-  double GetRateY();
-
-  /**
-   * Gets the rate of turn in degrees-per-second around the Z-axis
-   */
-  double GetRateZ();
-
-  /**
-   * Gets the currently reported angle around the X-axis
-   */
-  double GetAngleX();
-
-  /**
-   * Gets the currently reported angle around the X-axis
-   */
-  double GetAngleY();
-
-  /**
-   * Gets the currently reported angle around the X-axis
-   */
-  double GetAngleZ();
-
-  /**
-   * Resets the gyro
-   */
-  void Reset();
-
- private:
-  hal::SimDevice m_simDevice;
-  hal::SimDouble m_simRateX;
-  hal::SimDouble m_simRateY;
-  hal::SimDouble m_simRateZ;
-  hal::SimDouble m_simAngleX;
-  hal::SimDouble m_simAngleY;
-  hal::SimDouble m_simAngleZ;
-
-  double m_angleXOffset = 0;
-  double m_angleYOffset = 0;
-  double m_angleZOffset = 0;
-};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h
index 98ae957..ace7d33 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h
@@ -8,11 +8,10 @@
 #include <frc/Encoder.h>
 #include <frc/drive/DifferentialDrive.h>
 #include <frc/motorcontrol/Spark.h>
+#include <frc/romi/RomiGyro.h>
 #include <frc2/command/SubsystemBase.h>
 #include <units/length.h>
 
-#include "sensors/RomiGyro.h"
-
 class Drivetrain : public frc2::SubsystemBase {
  public:
   static constexpr double kCountsPerRevolution = 1440.0;
@@ -117,6 +116,6 @@
 
   frc::DifferentialDrive m_drive{m_leftMotor, m_rightMotor};
 
-  RomiGyro m_gyro;
+  frc::RomiGyro m_gyro;
   frc::BuiltInAccelerometer m_accelerometer;
 };
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/OnBoardIO.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/OnBoardIO.h
deleted file mode 100644
index 0bb5225..0000000
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/OnBoardIO.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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 <frc/DigitalInput.h>
-#include <frc/DigitalOutput.h>
-#include <frc2/command/SubsystemBase.h>
-
-/**
- * This class represents the onboard IO of the Romi
- * reference robot. This includes the pushbuttons and
- * LEDs.
- *
- * <p>DIO 0 - Button A (input only)
- * DIO 1 - Button B (input) or Green LED (output)
- * DIO 2 - Button C (input) or Red LED (output)
- * DIO 3 - Yellow LED (output only)
- */
-class OnBoardIO : public frc2::SubsystemBase {
- public:
-  enum ChannelMode { INPUT, OUTPUT };
-  OnBoardIO(OnBoardIO::ChannelMode dio1, OnBoardIO::ChannelMode dio2);
-
-  static constexpr auto kMessageInterval = 1_s;
-  units::second_t m_nextMessageTime = 0_s;
-
-  /**
-   * Gets if the A button is pressed.
-   */
-  bool GetButtonAPressed();
-
-  /**
-   * Gets if the B button is pressed.
-   */
-  bool GetButtonBPressed();
-
-  /**
-   * Gets if the C button is pressed.
-   */
-  bool GetButtonCPressed();
-
-  /**
-   * Sets the green LED.
-   */
-  void SetGreenLed(bool value);
-
-  /**
-   * Sets the red LED.
-   */
-  void SetRedLed(bool value);
-
-  /**
-   * Sets the yellow LED.
-   */
-  void SetYellowLed(bool value);
-
- private:
-  frc::DigitalInput m_buttonA{0};
-  frc::DigitalOutput m_yellowLed{3};
-
-  // DIO 1
-  std::unique_ptr<frc::DigitalInput> m_buttonB;
-  std::unique_ptr<frc::DigitalOutput> m_greenLed;
-
-  // DIO 2
-  std::unique_ptr<frc::DigitalInput> m_buttonC;
-  std::unique_ptr<frc::DigitalOutput> m_redLed;
-};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/cpp/Robot.cpp
deleted file mode 100644
index c7eab48..0000000
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/cpp/Robot.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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 "Robot.h"
-
-#include <frc/smartdashboard/SmartDashboard.h>
-#include <frc2/command/CommandScheduler.h>
-
-void Robot::RobotInit() {}
-
-/**
- * This function is called every 20 ms, no matter the mode. Use
- * this for items like diagnostics that you want to run during disabled,
- * autonomous, teleoperated and test.
- *
- * <p> This runs after the mode specific periodic functions, but before
- * LiveWindow and SmartDashboard integrated updating.
- */
-void Robot::RobotPeriodic() {
-  frc2::CommandScheduler::GetInstance().Run();
-}
-
-/**
- * This function is called once each time the robot enters Disabled mode. You
- * can use it to reset any subsystem information you want to clear when the
- * robot is disabled.
- */
-void Robot::DisabledInit() {}
-
-void Robot::DisabledPeriodic() {}
-
-/**
- * This autonomous runs the autonomous command selected by your {@link
- * RobotContainer} class.
- */
-void Robot::AutonomousInit() {
-  m_autonomousCommand = m_container.GetAutonomousCommand();
-
-  if (m_autonomousCommand != nullptr) {
-    m_autonomousCommand->Schedule();
-  }
-}
-
-void Robot::AutonomousPeriodic() {}
-
-void Robot::TeleopInit() {
-  // This makes sure that the autonomous stops running when
-  // teleop starts running. If you want the autonomous to
-  // continue until interrupted by another command, remove
-  // this line or comment it out.
-  if (m_autonomousCommand != nullptr) {
-    m_autonomousCommand->Cancel();
-    m_autonomousCommand = nullptr;
-  }
-}
-
-/**
- * This function is called periodically during operator control.
- */
-void Robot::TeleopPeriodic() {}
-
-/**
- * This function is called periodically during test mode.
- */
-void Robot::TestPeriodic() {}
-
-#ifndef RUNNING_FRC_TESTS
-int main() {
-  return frc::StartRobot<Robot>();
-}
-#endif
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/cpp/RobotContainer.cpp
deleted file mode 100644
index e54c42f..0000000
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/cpp/RobotContainer.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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 "RobotContainer.h"
-
-#include <frc/shuffleboard/Shuffleboard.h>
-#include <frc2/command/CommandScheduler.h>
-#include <frc2/command/button/JoystickButton.h>
-
-RobotContainer::RobotContainer() {
-  // Initialize all of your commands and subsystems here
-
-  // Set names of commands
-  m_instantCommand1.SetName("Instant Command 1");
-  m_instantCommand2.SetName("Instant Command 2");
-  m_waitCommand.SetName("Wait 5 Seconds Command");
-
-  // Set the scheduler to log Shuffleboard events for command initialize,
-  // interrupt, finish
-  frc2::CommandScheduler::GetInstance().OnCommandInitialize(
-      [](const frc2::Command& command) {
-        frc::Shuffleboard::AddEventMarker(
-            "Command Initialized", command.GetName(),
-            frc::ShuffleboardEventImportance::kNormal);
-      });
-  frc2::CommandScheduler::GetInstance().OnCommandInterrupt(
-      [](const frc2::Command& command) {
-        frc::Shuffleboard::AddEventMarker(
-            "Command Interrupted", command.GetName(),
-            frc::ShuffleboardEventImportance::kNormal);
-      });
-  frc2::CommandScheduler::GetInstance().OnCommandFinish(
-      [](const frc2::Command& command) {
-        frc::Shuffleboard::AddEventMarker(
-            "Command Finished", command.GetName(),
-            frc::ShuffleboardEventImportance::kNormal);
-      });
-
-  // Configure the button bindings
-  ConfigureButtonBindings();
-}
-
-void RobotContainer::ConfigureButtonBindings() {
-  // Configure your button bindings here
-
-  // Run instant command 1 when the 'A' button is pressed
-  frc2::JoystickButton(&m_driverController, frc::XboxController::Button::kA)
-      .OnTrue(&m_instantCommand1);
-  // Run instant command 2 when the 'X' button is pressed
-  frc2::JoystickButton(&m_driverController, frc::XboxController::Button::kX)
-      .OnTrue(&m_instantCommand2);
-  // Run instant command 3 when the 'Y' button is held; release early to
-  // interrupt
-  frc2::JoystickButton(&m_driverController, frc::XboxController::Button::kY)
-      .OnTrue(&m_waitCommand);
-}
-
-frc2::Command* RobotContainer::GetAutonomousCommand() {
-  // no auto
-  return nullptr;
-}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/include/Constants.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/include/Constants.h
deleted file mode 100644
index ae5d02d..0000000
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/include/Constants.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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
-
-/**
- * The Constants header provides a convenient place for teams to hold robot-wide
- * numerical or boolean constants.  This should not be used for any other
- * purpose.
- *
- * It is generally a good idea to place constants into subsystem- or
- * command-specific namespaces within this header, which can then be used where
- * they are needed.
- */
-
-namespace OIConstants {
-constexpr int kDriverControllerPort = 0;
-}  // namespace OIConstants
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/include/Robot.h
deleted file mode 100644
index a82f2ac..0000000
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/include/Robot.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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 <frc/TimedRobot.h>
-#include <frc2/command/Command.h>
-
-#include "RobotContainer.h"
-
-class Robot : public frc::TimedRobot {
- public:
-  void RobotInit() override;
-  void RobotPeriodic() override;
-  void DisabledInit() override;
-  void DisabledPeriodic() override;
-  void AutonomousInit() override;
-  void AutonomousPeriodic() override;
-  void TeleopInit() override;
-  void TeleopPeriodic() override;
-  void TestPeriodic() override;
-
- private:
-  // Have it null by default so that if testing teleop it
-  // doesn't have undefined behavior and potentially crash.
-  frc2::Command* m_autonomousCommand = nullptr;
-
-  RobotContainer m_container;
-};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/include/RobotContainer.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/include/RobotContainer.h
deleted file mode 100644
index 715b9c5..0000000
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SchedulerEventLogging/include/RobotContainer.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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 <frc/XboxController.h>
-#include <frc2/command/Command.h>
-#include <frc2/command/InstantCommand.h>
-#include <frc2/command/WaitCommand.h>
-
-#include "Constants.h"
-
-/**
- * This class is where the bulk of the robot should be declared.  Since
- * Command-based is a "declarative" paradigm, very little robot logic should
- * actually be handled in the {@link Robot} periodic methods (other than the
- * scheduler calls).  Instead, the structure of the robot (including subsystems,
- * commands, and button mappings) should be declared here.
- */
-class RobotContainer {
- public:
-  RobotContainer();
-
-  frc2::Command* GetAutonomousCommand();
-
- private:
-  // The robot's subsystems and commands are defined here...
-
-  // The driver's controller
-  frc::XboxController m_driverController{OIConstants::kDriverControllerPort};
-
-  // Three commands that do nothing; for demonstration purposes.
-  frc2::InstantCommand m_instantCommand1;
-  frc2::InstantCommand m_instantCommand2;
-  frc2::WaitCommand m_waitCommand{5_s};
-
-  void ConfigureButtonBindings();
-};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SelectCommand/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SelectCommand/cpp/RobotContainer.cpp
index 573fcac..69895cb 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SelectCommand/cpp/RobotContainer.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SelectCommand/cpp/RobotContainer.cpp
@@ -17,5 +17,5 @@
 
 frc2::Command* RobotContainer::GetAutonomousCommand() {
   // Run the select command in autonomous
-  return &m_exampleSelectCommand;
+  return m_exampleSelectCommand.get();
 }
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SelectCommand/include/RobotContainer.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SelectCommand/include/RobotContainer.h
index 40eea33..a1a2c86 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SelectCommand/include/RobotContainer.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SelectCommand/include/RobotContainer.h
@@ -5,8 +5,7 @@
 #pragma once
 
 #include <frc2/command/Command.h>
-#include <frc2/command/PrintCommand.h>
-#include <frc2/command/SelectCommand.h>
+#include <frc2/command/Commands.h>
 
 /**
  * This class is where the bulk of the robot should be declared.  Since
@@ -36,12 +35,12 @@
   // value returned by the selector method at runtime.  Note that selectcommand
   // takes a generic type, so the selector does not have to be an enum; it could
   // be any desired type (string, integer, boolean, double...)
-  frc2::SelectCommand<CommandSelector> m_exampleSelectCommand{
+  frc2::CommandPtr m_exampleSelectCommand = frc2::cmd::Select<CommandSelector>(
       [this] { return Select(); },
       // Maps selector values to commands
-      std::pair{ONE, frc2::PrintCommand{"Command one was selected!"}},
-      std::pair{TWO, frc2::PrintCommand{"Command two was selected!"}},
-      std::pair{THREE, frc2::PrintCommand{"Command three was selected!"}}};
+      std::pair{ONE, frc2::cmd::Print("Command one was selected!")},
+      std::pair{TWO, frc2::cmd::Print("Command two was selected!")},
+      std::pair{THREE, frc2::cmd::Print("Command three was selected!")});
 
   void ConfigureButtonBindings();
 };
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp
index 1417801..b951f18 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp
@@ -42,7 +42,7 @@
 
     // Put encoders in a list layout.
     frc::ShuffleboardLayout& encoders =
-        driveBaseTab.GetLayout("List Layout", "Encoders")
+        driveBaseTab.GetLayout("Encoders", frc::BuiltInLayouts::kList)
             .WithPosition(0, 0)
             .WithSize(2, 2);
     encoders.Add("Left Encoder", m_leftEncoder);
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/cpp/Robot.cpp
index 7ea7b09..523bb7b 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/cpp/Robot.cpp
@@ -22,8 +22,7 @@
   void RobotPeriodic() override { m_drive.Periodic(); }
 
   void AutonomousInit() override {
-    m_timer.Reset();
-    m_timer.Start();
+    m_timer.Restart();
     m_drive.ResetOdometry(m_trajectory.InitialPose());
   }
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h
index 80363b5..4c274fe 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h
@@ -86,8 +86,8 @@
   frc::Encoder m_leftEncoder{0, 1};
   frc::Encoder m_rightEncoder{2, 3};
 
-  frc2::PIDController m_leftPIDController{8.5, 0.0, 0.0};
-  frc2::PIDController m_rightPIDController{8.5, 0.0, 0.0};
+  frc::PIDController m_leftPIDController{8.5, 0.0, 0.0};
+  frc::PIDController m_rightPIDController{8.5, 0.0, 0.0};
 
   frc::AnalogGyro m_gyro{0};
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Solenoid/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Solenoid/cpp/Robot.cpp
index 82e481b..88a2dbf 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Solenoid/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Solenoid/cpp/Robot.cpp
@@ -2,69 +2,96 @@
 // 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 <frc/DoubleSolenoid.h>
-#include <frc/Joystick.h>
-#include <frc/PneumaticsControlModule.h>
-#include <frc/Solenoid.h>
-#include <frc/TimedRobot.h>
+#include "Robot.h"
 
-/**
- * This is a sample program showing the use of the solenoid classes during
- * operator control.
- *
- * Three buttons from a joystick will be used to control two solenoids: One
- * button to control the position of a single solenoid and the other two buttons
- * to control a double solenoid.
- *
- * Single solenoids can either be on or off, such that the air diverted through
- * them goes through either one channel or the other.
- *
- * Double solenoids have three states: Off, Forward, and Reverse. Forward and
- * Reverse divert the air through the two channels and correspond to the on and
- * off of a single solenoid, but a double solenoid can also be "off", where both
- * channels are diverted to exhaust such that there is no pressure in either
- * channel.
- *
- * Additionally, double solenoids take up two channels on your PCM whereas
- * single solenoids only take a single channel.
- */
-class Robot : public frc::TimedRobot {
- public:
-  void TeleopPeriodic() override {
-    /* The output of GetRawButton is true/false depending on whether the button
-     * is pressed; Set takes a boolean for for whether to use the default
-     * (false) channel or the other (true).
-     */
-    m_solenoid.Set(m_stick.GetRawButton(kSolenoidButton));
+#include <frc/shuffleboard/Shuffleboard.h>
+#include <units/pressure.h>
 
-    /* In order to set the double solenoid, we will say that if neither button
-     * is pressed, it is off, if just one button is pressed, set the solenoid to
-     * correspond to that button, and if both are pressed, set the solenoid to
-     * Forwards.
-     */
-    if (m_stick.GetRawButton(kDoubleSolenoidForward)) {
-      m_doubleSolenoid.Set(frc::DoubleSolenoid::kForward);
-    } else if (m_stick.GetRawButton(kDoubleSolenoidReverse)) {
-      m_doubleSolenoid.Set(frc::DoubleSolenoid::kReverse);
-    } else {
-      m_doubleSolenoid.Set(frc::DoubleSolenoid::kOff);
-    }
+void Robot::RobotInit() {
+  // Publish elements to shuffleboard.
+  frc::ShuffleboardTab& tab = frc::Shuffleboard::GetTab("Pneumatics");
+  tab.Add("Single Solenoid", m_solenoid);
+  tab.Add("Double Solenoid", m_doubleSolenoid);
+  tab.Add("Compressor", m_compressor);
+
+  // Also publish some raw data
+  tab.AddDouble("PH Pressure [PSI]", [&] {
+    // Get the pressure (in PSI) from the analog sensor connected to the PH.
+    // This function is supported only on the PH!
+    // On a PCM, this function will return 0.
+    units::pounds_per_square_inch_t pressure = m_compressor.GetPressure();
+    return pressure.value();
+  });
+  tab.AddDouble("Compressor Current", [&] {
+    // Get compressor current draw.
+    units::ampere_t compressorCurrent = m_compressor.GetCurrent();
+    return compressorCurrent.value();
+  });
+  tab.AddBoolean("Compressor Active", [&] {
+    // Get whether the compressor is active.
+    return m_compressor.IsEnabled();
+  });
+  tab.AddBoolean("Pressure Switch", [&] {
+    // Get the digital pressure switch connected to the PCM/PH.
+    // The switch is open when the pressure is over ~120 PSI.
+    return m_compressor.GetPressureSwitchValue();
+  });
+}
+
+void Robot::TeleopPeriodic() {
+  /*
+   * The output of GetRawButton is true/false depending on whether
+   * the button is pressed; Set takes a boolean for whether
+   * to retract the solenoid (false) or extend it (true).
+   */
+  m_solenoid.Set(m_stick.GetRawButton(kSolenoidButton));
+
+  /*
+   * GetRawButtonPressed will only return true once per press.
+   * If a button is pressed, set the solenoid to the respective channel.
+   */
+  if (m_stick.GetRawButtonPressed(kDoubleSolenoidForward)) {
+    m_doubleSolenoid.Set(frc::DoubleSolenoid::kForward);
+  } else if (m_stick.GetRawButtonPressed(kDoubleSolenoidReverse)) {
+    m_doubleSolenoid.Set(frc::DoubleSolenoid::kReverse);
   }
 
- private:
-  frc::Joystick m_stick{0};
-
-  // Solenoid corresponds to a single solenoid.
-  frc::Solenoid m_solenoid{frc::PneumaticsModuleType::CTREPCM, 0};
-
-  // DoubleSolenoid corresponds to a double solenoid.
-  frc::DoubleSolenoid m_doubleSolenoid{frc::PneumaticsModuleType::CTREPCM, 1,
-                                       2};
-
-  static constexpr int kSolenoidButton = 1;
-  static constexpr int kDoubleSolenoidForward = 2;
-  static constexpr int kDoubleSolenoidReverse = 3;
-};
+  // On button press, toggle the compressor with the mode selected from the
+  // dashboard.
+  if (m_stick.GetRawButtonPressed(kCompressorButton)) {
+    // Check whether the compressor is currently enabled.
+    bool isCompressorEnabled = m_compressor.IsEnabled();
+    if (isCompressorEnabled) {
+      // Disable closed-loop mode on the compressor.
+      m_compressor.Disable();
+    } else {
+      // Change the if directives to select the closed-loop mode you want to
+      // use:
+#if 0
+      // Enable closed-loop mode based on the digital pressure switch
+      // connected to the PCM/PH.
+      m_compressor.EnableDigital();
+#endif
+#if 1
+      // Enable closed-loop mode based on the analog pressure sensor connected
+      // to the PH. The compressor will run while the pressure reported by the
+      // sensor is in the specified range ([70 PSI, 120 PSI] in this example).
+      // Analog mode exists only on the PH! On the PCM, this enables digital
+      // control.
+      m_compressor.EnableAnalog(70_psi, 120_psi);
+#endif
+#if 0
+      // Enable closed-loop mode based on both the digital pressure switch AND the analog
+      // pressure sensor connected to the PH.
+      // The compressor will run while the pressure reported by the analog sensor is in the
+      // specified range ([70 PSI, 120 PSI] in this example) AND the digital switch reports
+      // that the system is not full.
+      // Hybrid mode exists only on the PH! On the PCM, this enables digital control.
+      m_compressor.EnableHybrid(70_psi, 120_psi);
+#endif
+    }
+  }
+}
 
 #ifndef RUNNING_FRC_TESTS
 int main() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Solenoid/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Solenoid/include/Robot.h
new file mode 100644
index 0000000..550e949
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Solenoid/include/Robot.h
@@ -0,0 +1,61 @@
+// 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 <frc/AnalogPotentiometer.h>
+#include <frc/Compressor.h>
+#include <frc/DoubleSolenoid.h>
+#include <frc/Joystick.h>
+#include <frc/PneumaticsControlModule.h>
+#include <frc/Solenoid.h>
+#include <frc/TimedRobot.h>
+
+/**
+ * This is a sample program showing the use of the solenoid classes during
+ * operator control.
+ *
+ * Three buttons from a joystick will be used to control two solenoids: One
+ * button to control the position of a single solenoid and the other two buttons
+ * to control a double solenoid.
+ *
+ * Single solenoids can either be on or off, such that the air diverted through
+ * them goes through either one channel or the other.
+ *
+ * Double solenoids have three states: Off, Forward, and Reverse. Forward and
+ * Reverse divert the air through the two channels and correspond to the on and
+ * off of a single solenoid, but a double solenoid can also be "off", where both
+ * channels are diverted to exhaust such that there is no pressure in either
+ * channel.
+ *
+ * Additionally, double solenoids take up two channels on your PCM whereas
+ * single solenoids only take a single channel.
+ */
+
+class Robot : public frc::TimedRobot {
+ public:
+  void RobotInit() override;
+  void TeleopPeriodic() override;
+
+ private:
+  frc::Joystick m_stick{0};
+
+  // Solenoid corresponds to a single solenoid.
+  // In this case, it's connected to channel 0 of a PH with the default CAN
+  // ID.
+  frc::Solenoid m_solenoid{frc::PneumaticsModuleType::REVPH, 0};
+
+  // DoubleSolenoid corresponds to a double solenoid.
+  // In this case, it's connected to channels 1 and 2 of a PH with the default
+  // CAN ID.
+  frc::DoubleSolenoid m_doubleSolenoid{frc::PneumaticsModuleType::REVPH, 1, 2};
+
+  // Compressor connected to a PH with a default CAN ID
+  frc::Compressor m_compressor{frc::PneumaticsModuleType::REVPH};
+
+  static constexpr int kSolenoidButton = 1;
+  static constexpr int kDoubleSolenoidForward = 2;
+  static constexpr int kDoubleSolenoidReverse = 3;
+  static constexpr int kCompressorButton = 4;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceArm/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceArm/cpp/Robot.cpp
index aec9738..f6a9eea 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceArm/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceArm/cpp/Robot.cpp
@@ -86,8 +86,8 @@
   frc::PWMSparkMax m_motor{kMotorPort};
   frc::XboxController m_joystick{kJoystickPort};
 
-  frc::TrapezoidProfile<units::radians>::Constraints m_constraints{
-      45_deg_per_s, 90_deg_per_s / 1_s};
+  frc::TrapezoidProfile<units::radians> m_profile{
+      {45_deg_per_s, 90_deg_per_s / 1_s}};
 
   frc::TrapezoidProfile<units::radians>::State m_lastProfiledReference;
 
@@ -117,9 +117,7 @@
       goal = {kLoweredPosition, 0_rad_per_s};
     }
     m_lastProfiledReference =
-        (frc::TrapezoidProfile<units::radians>(m_constraints, goal,
-                                               m_lastProfiledReference))
-            .Calculate(20_ms);
+        m_profile.Calculate(20_ms, goal, m_lastProfiledReference);
 
     m_loop.SetNextR(frc::Vectord<2>{m_lastProfiledReference.position.value(),
                                     m_lastProfiledReference.velocity.value()});
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/cpp/RobotContainer.cpp
index fec6de3..8c73dc8 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/cpp/RobotContainer.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/cpp/RobotContainer.cpp
@@ -91,8 +91,8 @@
           DriveConstants::ks, DriveConstants::kv, DriveConstants::ka),
       DriveConstants::kDriveKinematics,
       [this] { return m_drive.GetWheelSpeeds(); },
-      frc2::PIDController{DriveConstants::kPDriveVel, 0, 0},
-      frc2::PIDController{DriveConstants::kPDriveVel, 0, 0},
+      frc::PIDController{DriveConstants::kPDriveVel, 0, 0},
+      frc::PIDController{DriveConstants::kPDriveVel, 0, 0},
       [this](auto left, auto right) { m_drive.TankDriveVolts(left, right); },
       {&m_drive});
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceElevator/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceElevator/cpp/Robot.cpp
index 7decddd..3b83793 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceElevator/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/StateSpaceElevator/cpp/Robot.cpp
@@ -50,8 +50,10 @@
   // The observer fuses our encoder data and voltage inputs to reject noise.
   frc::KalmanFilter<2, 1, 1> m_observer{
       m_elevatorPlant,
-      {0.0508, 0.5},  // How accurate we think our model is
-      {0.001},        // How accurate we think our encoder position
+      {units::meter_t{2_in}.value(),
+       units::meters_per_second_t{40_in / 1_s}
+           .value()},  // How accurate we think our model is
+      {0.001},         // How accurate we think our encoder position
       // data is. In this case we very highly trust our encoder position
       // reading.
       20_ms};
@@ -62,7 +64,8 @@
       // qelms. State error tolerance, in meters and meters per second.
       // Decrease this to more heavily penalize state excursion, or make the
       // controller behave more aggressively.
-      {0.0254, 0.254},
+      {units::meter_t{1_in}.value(),
+       units::meters_per_second_t{10_in / 1_s}.value()},
       // relms. Control effort (voltage) tolerance. Decrease this to more
       // heavily penalize control effort, or make the controller less
       // aggressive. 12 is a good starting point because that is the
@@ -83,8 +86,7 @@
   frc::PWMSparkMax m_motor{kMotorPort};
   frc::XboxController m_joystick{kJoystickPort};
 
-  frc::TrapezoidProfile<units::meters>::Constraints m_constraints{3_fps,
-                                                                  6_fps_sq};
+  frc::TrapezoidProfile<units::meters> m_profile{{3_fps, 6_fps_sq}};
 
   frc::TrapezoidProfile<units::meters>::State m_lastProfiledReference;
 
@@ -115,9 +117,7 @@
       goal = {kLoweredPosition, 0_fps};
     }
     m_lastProfiledReference =
-        (frc::TrapezoidProfile<units::meters>(m_constraints, goal,
-                                              m_lastProfiledReference))
-            .Calculate(20_ms);
+        m_profile.Calculate(20_ms, goal, m_lastProfiledReference);
 
     m_loop.SetNextR(frc::Vectord<2>{m_lastProfiledReference.position.value(),
                                     m_lastProfiledReference.velocity.value()});
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/Drivetrain.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/Drivetrain.cpp
index 537da53..03cc93b 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/Drivetrain.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/Drivetrain.cpp
@@ -6,11 +6,14 @@
 
 void Drivetrain::Drive(units::meters_per_second_t xSpeed,
                        units::meters_per_second_t ySpeed,
-                       units::radians_per_second_t rot, bool fieldRelative) {
-  auto states = m_kinematics.ToSwerveModuleStates(
-      fieldRelative ? frc::ChassisSpeeds::FromFieldRelativeSpeeds(
-                          xSpeed, ySpeed, rot, m_gyro.GetRotation2d())
-                    : frc::ChassisSpeeds{xSpeed, ySpeed, rot});
+                       units::radians_per_second_t rot, bool fieldRelative,
+                       units::second_t period) {
+  auto states =
+      m_kinematics.ToSwerveModuleStates(frc::ChassisSpeeds::Discretize(
+          fieldRelative ? frc::ChassisSpeeds::FromFieldRelativeSpeeds(
+                              xSpeed, ySpeed, rot, m_gyro.GetRotation2d())
+                        : frc::ChassisSpeeds{xSpeed, ySpeed, rot},
+          period));
 
   m_kinematics.DesaturateWheelSpeeds(&states, kMaxSpeed);
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/Robot.cpp
index 39cfa14..b8edc6e 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/Robot.cpp
@@ -50,7 +50,7 @@
                          frc::ApplyDeadband(m_controller.GetRightX(), 0.02)) *
                      Drivetrain::kMaxAngularSpeed;
 
-    m_swerve.Drive(xSpeed, ySpeed, rot, fieldRelative);
+    m_swerve.Drive(xSpeed, ySpeed, rot, fieldRelative, GetPeriod());
   }
 };
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/SwerveModule.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/SwerveModule.cpp
index 0f9e27e..380f8cb 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/SwerveModule.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/cpp/SwerveModule.cpp
@@ -48,9 +48,17 @@
 
 void SwerveModule::SetDesiredState(
     const frc::SwerveModuleState& referenceState) {
+  frc::Rotation2d encoderRotation{
+      units::radian_t{m_turningEncoder.GetDistance()}};
+
   // Optimize the reference state to avoid spinning further than 90 degrees
-  const auto state = frc::SwerveModuleState::Optimize(
-      referenceState, units::radian_t{m_turningEncoder.GetDistance()});
+  auto state =
+      frc::SwerveModuleState::Optimize(referenceState, encoderRotation);
+
+  // Scale speed by cosine of angle error. This scales down movement
+  // perpendicular to the desired direction of travel that can occur when
+  // modules change directions. This results in smoother driving.
+  state.speed *= (state.angle - encoderRotation).Cos();
 
   // Calculate the drive output from the drive PID controller.
   const auto driveOutput = m_drivePIDController.Calculate(
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/Drivetrain.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/Drivetrain.h
index 87233d2..7f6f9cf 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/Drivetrain.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/Drivetrain.h
@@ -22,7 +22,7 @@
 
   void Drive(units::meters_per_second_t xSpeed,
              units::meters_per_second_t ySpeed, units::radians_per_second_t rot,
-             bool fieldRelative);
+             bool fieldRelative, units::second_t period);
   void UpdateOdometry();
 
   static constexpr units::meters_per_second_t kMaxSpeed =
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/SwerveModule.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/SwerveModule.h
index 1861498..3c2b2e4 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/SwerveModule.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/SwerveModule.h
@@ -42,7 +42,7 @@
   frc::Encoder m_driveEncoder;
   frc::Encoder m_turningEncoder;
 
-  frc2::PIDController m_drivePIDController{1.0, 0, 0};
+  frc::PIDController m_drivePIDController{1.0, 0, 0};
   frc::ProfiledPIDController<units::radians> m_turningPIDController{
       1.0,
       0.0,
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/RobotContainer.cpp
index 52b25b9..4bbbe0c 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/RobotContainer.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/RobotContainer.cpp
@@ -35,9 +35,13 @@
   m_drive.SetDefaultCommand(frc2::RunCommand(
       [this] {
         m_drive.Drive(
-            units::meters_per_second_t{m_driverController.GetLeftY()},
-            units::meters_per_second_t{m_driverController.GetLeftX()},
-            units::radians_per_second_t{m_driverController.GetRightX()}, false);
+            // Multiply by max speed to map the joystick unitless inputs to
+            // actual units. This will map the [-1, 1] to [max speed backwards,
+            // max speed forwards], converting them to actual units.
+            m_driverController.GetLeftY() * AutoConstants::kMaxSpeed,
+            m_driverController.GetLeftX() * AutoConstants::kMaxSpeed,
+            m_driverController.GetRightX() * AutoConstants::kMaxAngularSpeed,
+            false);
       },
       {&m_drive}));
 }
@@ -74,8 +78,8 @@
 
       m_drive.kDriveKinematics,
 
-      frc2::PIDController{AutoConstants::kPXController, 0, 0},
-      frc2::PIDController{AutoConstants::kPYController, 0, 0}, thetaController,
+      frc::PIDController{AutoConstants::kPXController, 0, 0},
+      frc::PIDController{AutoConstants::kPYController, 0, 0}, thetaController,
 
       [this](auto moduleStates) { m_drive.SetModuleStates(moduleStates); },
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/subsystems/DriveSubsystem.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/subsystems/DriveSubsystem.cpp
index 4b60372..a6b810a 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/subsystems/DriveSubsystem.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/subsystems/DriveSubsystem.cpp
@@ -51,12 +51,14 @@
 
 void DriveSubsystem::Drive(units::meters_per_second_t xSpeed,
                            units::meters_per_second_t ySpeed,
-                           units::radians_per_second_t rot,
-                           bool fieldRelative) {
-  auto states = kDriveKinematics.ToSwerveModuleStates(
-      fieldRelative ? frc::ChassisSpeeds::FromFieldRelativeSpeeds(
-                          xSpeed, ySpeed, rot, m_gyro.GetRotation2d())
-                    : frc::ChassisSpeeds{xSpeed, ySpeed, rot});
+                           units::radians_per_second_t rot, bool fieldRelative,
+                           units::second_t period) {
+  auto states =
+      kDriveKinematics.ToSwerveModuleStates(frc::ChassisSpeeds::Discretize(
+          fieldRelative ? frc::ChassisSpeeds::FromFieldRelativeSpeeds(
+                              xSpeed, ySpeed, rot, m_gyro.GetRotation2d())
+                        : frc::ChassisSpeeds{xSpeed, ySpeed, rot},
+          period));
 
   kDriveKinematics.DesaturateWheelSpeeds(&states, AutoConstants::kMaxSpeed);
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/subsystems/SwerveModule.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/subsystems/SwerveModule.cpp
index bb0362e..dc1cdb0 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/subsystems/SwerveModule.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/subsystems/SwerveModule.cpp
@@ -18,21 +18,25 @@
     : m_driveMotor(driveMotorChannel),
       m_turningMotor(turningMotorChannel),
       m_driveEncoder(driveEncoderPorts[0], driveEncoderPorts[1]),
-      m_turningEncoder(turningEncoderPorts[0], turningEncoderPorts[1]),
-      m_reverseDriveEncoder(driveEncoderReversed),
-      m_reverseTurningEncoder(turningEncoderReversed) {
+      m_turningEncoder(turningEncoderPorts[0], turningEncoderPorts[1]) {
   // Set the distance per pulse for the drive encoder. We can simply use the
   // distance traveled for one rotation of the wheel divided by the encoder
   // resolution.
   m_driveEncoder.SetDistancePerPulse(
       ModuleConstants::kDriveEncoderDistancePerPulse);
 
+  // Set whether drive encoder should be reversed or not
+  m_driveEncoder.SetReverseDirection(driveEncoderReversed);
+
   // Set the distance (in this case, angle) per pulse for the turning encoder.
   // This is the the angle through an entire rotation (2 * std::numbers::pi)
   // divided by the encoder resolution.
   m_turningEncoder.SetDistancePerPulse(
       ModuleConstants::kTurningEncoderDistancePerPulse);
 
+  // Set whether turning encoder should be reversed or not
+  m_turningEncoder.SetReverseDirection(turningEncoderReversed);
+
   // Limit the PID Controller's input range between -pi and pi and set the input
   // to be continuous.
   m_turningPIDController.EnableContinuousInput(
@@ -51,9 +55,17 @@
 
 void SwerveModule::SetDesiredState(
     const frc::SwerveModuleState& referenceState) {
+  frc::Rotation2d encoderRotation{
+      units::radian_t{m_turningEncoder.GetDistance()}};
+
   // Optimize the reference state to avoid spinning further than 90 degrees
-  const auto state = frc::SwerveModuleState::Optimize(
-      referenceState, units::radian_t{m_turningEncoder.GetDistance()});
+  auto state =
+      frc::SwerveModuleState::Optimize(referenceState, encoderRotation);
+
+  // Scale speed by cosine of angle error. This scales down movement
+  // perpendicular to the desired direction of travel that can occur when
+  // modules change directions. This results in smoother driving.
+  state.speed *= (state.angle - encoderRotation).Cos();
 
   // Calculate the drive output from the drive PID controller.
   const auto driveOutput = m_drivePIDController.Calculate(
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/Constants.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/Constants.h
index cacab87..779927d 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/Constants.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/Constants.h
@@ -4,6 +4,7 @@
 
 #include <numbers>
 
+#include <frc/TimedRobot.h>
 #include <frc/geometry/Translation2d.h>
 #include <frc/kinematics/SwerveDriveKinematics.h>
 #include <frc/trajectory/TrapezoidProfile.h>
@@ -58,6 +59,10 @@
 constexpr bool kFrontRightDriveEncoderReversed = false;
 constexpr bool kRearRightDriveEncoderReversed = true;
 
+// If you call DriveSubsystem::Drive with a different period make sure to update
+// this.
+constexpr units::second_t kDrivePeriod = frc::TimedRobot::kDefaultPeriod;
+
 // These are example values only - DO NOT USE THESE FOR YOUR OWN ROBOT!
 // These characterization values MUST be determined either experimentally or
 // theoretically for *your* robot's drive. The SysId tool provides a convenient
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/DriveSubsystem.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/DriveSubsystem.h
index e9ed17b..b7b47fc 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/DriveSubsystem.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/DriveSubsystem.h
@@ -43,7 +43,8 @@
    */
   void Drive(units::meters_per_second_t xSpeed,
              units::meters_per_second_t ySpeed, units::radians_per_second_t rot,
-             bool fieldRelative);
+             bool fieldRelative,
+             units::second_t period = DriveConstants::kDrivePeriod);
 
   /**
    * Resets the drive encoders to currently read a position of 0.
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/SwerveModule.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/SwerveModule.h
index f0d5ede..8b9cfa3 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/SwerveModule.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/SwerveModule.h
@@ -47,10 +47,7 @@
   frc::Encoder m_driveEncoder;
   frc::Encoder m_turningEncoder;
 
-  bool m_reverseDriveEncoder;
-  bool m_reverseTurningEncoder;
-
-  frc2::PIDController m_drivePIDController{
+  frc::PIDController m_drivePIDController{
       ModuleConstants::kPModuleDriveController, 0, 0};
   frc::ProfiledPIDController<units::radians> m_turningPIDController{
       ModuleConstants::kPModuleTurningController,
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/Drivetrain.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/Drivetrain.cpp
index 5402e87..a365f7f 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/Drivetrain.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/Drivetrain.cpp
@@ -10,11 +10,14 @@
 
 void Drivetrain::Drive(units::meters_per_second_t xSpeed,
                        units::meters_per_second_t ySpeed,
-                       units::radians_per_second_t rot, bool fieldRelative) {
-  auto states = m_kinematics.ToSwerveModuleStates(
-      fieldRelative ? frc::ChassisSpeeds::FromFieldRelativeSpeeds(
-                          xSpeed, ySpeed, rot, m_gyro.GetRotation2d())
-                    : frc::ChassisSpeeds{xSpeed, ySpeed, rot});
+                       units::radians_per_second_t rot, bool fieldRelative,
+                       units::second_t period) {
+  auto states =
+      m_kinematics.ToSwerveModuleStates(frc::ChassisSpeeds::Discretize(
+          fieldRelative ? frc::ChassisSpeeds::FromFieldRelativeSpeeds(
+                              xSpeed, ySpeed, rot, m_gyro.GetRotation2d())
+                        : frc::ChassisSpeeds{xSpeed, ySpeed, rot},
+          period));
 
   m_kinematics.DesaturateWheelSpeeds(&states, kMaxSpeed);
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/Robot.cpp
index 3f5f675..919dca6 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/Robot.cpp
@@ -46,7 +46,7 @@
     const auto rot = -m_rotLimiter.Calculate(m_controller.GetRightX()) *
                      Drivetrain::kMaxAngularSpeed;
 
-    m_swerve.Drive(xSpeed, ySpeed, rot, fieldRelative);
+    m_swerve.Drive(xSpeed, ySpeed, rot, fieldRelative, GetPeriod());
   }
 };
 
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/SwerveModule.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/SwerveModule.cpp
index c5a0839..2afaf55 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/SwerveModule.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/cpp/SwerveModule.cpp
@@ -48,9 +48,17 @@
 
 void SwerveModule::SetDesiredState(
     const frc::SwerveModuleState& referenceState) {
+  frc::Rotation2d encoderRotation{
+      units::radian_t{m_turningEncoder.GetDistance()}};
+
   // Optimize the reference state to avoid spinning further than 90 degrees
-  const auto state = frc::SwerveModuleState::Optimize(
-      referenceState, units::radian_t{m_turningEncoder.GetDistance()});
+  auto state =
+      frc::SwerveModuleState::Optimize(referenceState, encoderRotation);
+
+  // Scale speed by cosine of angle error. This scales down movement
+  // perpendicular to the desired direction of travel that can occur when
+  // modules change directions. This results in smoother driving.
+  state.speed *= (state.angle - encoderRotation).Cos();
 
   // Calculate the drive output from the drive PID controller.
   const auto driveOutput = m_drivePIDController.Calculate(
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/Drivetrain.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/Drivetrain.h
index e042291..e71dc43 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/Drivetrain.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/Drivetrain.h
@@ -23,7 +23,7 @@
 
   void Drive(units::meters_per_second_t xSpeed,
              units::meters_per_second_t ySpeed, units::radians_per_second_t rot,
-             bool fieldRelative);
+             bool fieldRelative, units::second_t period);
   void UpdateOdometry();
 
   static constexpr auto kMaxSpeed = 3.0_mps;  // 3 meters per second
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/SwerveModule.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/SwerveModule.h
index 049d507..a4adb2d 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/SwerveModule.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/SwerveModule.h
@@ -42,7 +42,7 @@
   frc::Encoder m_driveEncoder;
   frc::Encoder m_turningEncoder;
 
-  frc2::PIDController m_drivePIDController{1.0, 0, 0};
+  frc::PIDController m_drivePIDController{1.0, 0, 0};
   frc::ProfiledPIDController<units::radians> m_turningPIDController{
       1.0,
       0.0,
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Ultrasonic/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Ultrasonic/cpp/Robot.cpp
index e8f805d..ac47856 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Ultrasonic/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Ultrasonic/cpp/Robot.cpp
@@ -2,57 +2,54 @@
 // 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 <frc/AnalogInput.h>
-#include <frc/TimedRobot.h>
-#include <frc/drive/DifferentialDrive.h>
-#include <frc/filter/MedianFilter.h>
-#include <frc/motorcontrol/PWMSparkMax.h>
+#include "Robot.h"
 
-/**
- * This is a sample program demonstrating how to use an ultrasonic sensor and
- * proportional control to maintain a set distance from an object.
- */
-class Robot : public frc::TimedRobot {
- public:
-  /**
-   * Tells the robot to drive to a set distance (in inches) from an object using
-   * proportional control.
-   */
-  void TeleopPeriodic() override {
-    // Sensor returns a value from 0-4095 that is scaled to inches
-    // returned value is filtered with a rolling median filter, since
-    // ultrasonics tend to be quite noisy and susceptible to sudden outliers
-    double currentDistance =
-        m_filter.Calculate(m_ultrasonic.GetVoltage()) * kValueToInches;
-    // Convert distance error to a motor speed
-    double currentSpeed = (kHoldDistance - currentDistance) * kP;
-    // Drive robot
-    m_robotDrive.ArcadeDrive(currentSpeed, 0);
+#include <frc/shuffleboard/Shuffleboard.h>
+#include <frc/smartdashboard/SmartDashboard.h>
+#include <units/length.h>
+
+void Robot::RobotInit() {
+  // Add the ultrasonic on the "Sensors" tab of the dashboard
+  // Data will update automatically
+  frc::Shuffleboard::GetTab("Sensors").Add(m_rangeFinder);
+}
+
+void Robot::TeleopPeriodic() {
+  // We can read the distance
+  units::meter_t distance = m_rangeFinder.GetRange();
+  // units auto-convert
+  units::millimeter_t distanceMillimeters = distance;
+  units::inch_t distanceInches = distance;
+
+  // We can also publish the data itself periodically
+  frc::SmartDashboard::PutNumber("Distance[mm]", distanceMillimeters.value());
+  frc::SmartDashboard::PutNumber("Distance[inch]", distanceInches.value());
+}
+
+void Robot::TestInit() {
+  // By default, the Ultrasonic class polls all ultrasonic sensors in a
+  // round-robin to prevent them from interfering from one another. However,
+  // manual polling is also possible -- note that this disables automatic mode!
+  m_rangeFinder.Ping();
+}
+
+void Robot::TestPeriodic() {
+  if (m_rangeFinder.IsRangeValid()) {
+    // Data is valid, publish it
+    units::millimeter_t distanceMillimeters = m_rangeFinder.GetRange();
+    units::inch_t distanceInches = m_rangeFinder.GetRange();
+    frc::SmartDashboard::PutNumber("Distance[mm]", distanceMillimeters.value());
+    frc::SmartDashboard::PutNumber("Distance[inch]", distanceInches.value());
+
+    // Ping for next measurement
+    m_rangeFinder.Ping();
   }
+}
 
- private:
-  // Distance in inches the robot wants to stay from an object
-  static constexpr int kHoldDistance = 12;
-
-  // Factor to convert sensor values to a distance in inches
-  static constexpr double kValueToInches = 0.125;
-
-  // Proportional speed constant
-  static constexpr double kP = 0.05;
-
-  static constexpr int kLeftMotorPort = 0;
-  static constexpr int kRightMotorPort = 1;
-  static constexpr int kUltrasonicPort = 0;
-
-  // median filter to discard outliers; filters over 10 samples
-  frc::MedianFilter<double> m_filter{10};
-
-  frc::AnalogInput m_ultrasonic{kUltrasonicPort};
-
-  frc::PWMSparkMax m_left{kLeftMotorPort};
-  frc::PWMSparkMax m_right{kRightMotorPort};
-  frc::DifferentialDrive m_robotDrive{m_left, m_right};
-};
+void Robot::TestExit() {
+  // Enable automatic mode
+  frc::Ultrasonic::SetAutomaticMode(true);
+}
 
 #ifndef RUNNING_FRC_TESTS
 int main() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Ultrasonic/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Ultrasonic/include/Robot.h
new file mode 100644
index 0000000..2da0c6f
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/Ultrasonic/include/Robot.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 <frc/TimedRobot.h>
+#include <frc/Ultrasonic.h>
+
+/**
+ * This is a sample program demonstrating how to read from a ping-response
+ * ultrasonic sensor with the {@link Ultrasonic class}.
+ */
+class Robot : public frc::TimedRobot {
+ public:
+  void RobotInit() override;
+  void TeleopPeriodic() override;
+  void TestInit() override;
+  void TestPeriodic() override;
+  void TestExit() override;
+
+ private:
+  // Creates a ping-response Ultrasonic object on DIO 1 and 2.
+  frc::Ultrasonic m_rangeFinder{1, 2};
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp
index 62d4106..9d2b5c2 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp
@@ -2,65 +2,21 @@
 // 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 <frc/AnalogInput.h>
-#include <frc/TimedRobot.h>
-#include <frc/controller/PIDController.h>
-#include <frc/drive/DifferentialDrive.h>
-#include <frc/filter/MedianFilter.h>
-#include <frc/motorcontrol/PWMSparkMax.h>
+#include "Robot.h"
 
-/**
- * This is a sample program demonstrating how to use an ultrasonic sensor and
- * proportional control to maintain a set distance from an object.
- */
-class Robot : public frc::TimedRobot {
- public:
-  /**
-   * Drives the robot a set distance from an object using PID control and the
-   * ultrasonic sensor.
-   */
-  void TeleopInit() override {
-    // Set setpoint of the PID Controller
-    m_pidController.SetSetpoint(kHoldDistance * kValueToInches);
-  }
+void Robot::AutonomousInit() {
+  // Set setpoint of the pid controller
+  m_pidController.SetSetpoint(kHoldDistance.value());
+}
 
-  void TeleopPeriodic() override {
-    double output =
-        m_pidController.Calculate(m_filter.Calculate(m_ultrasonic.GetValue()));
-    m_robotDrive.ArcadeDrive(output, 0);
-  }
+void Robot::AutonomousPeriodic() {
+  units::millimeter_t measurement = m_ultrasonic.GetRange();
+  units::millimeter_t filteredMeasurement = m_filter.Calculate(measurement);
+  double pidOutput = m_pidController.Calculate(filteredMeasurement.value());
 
- private:
-  // Distance in inches the robot wants to stay from an object
-  static constexpr int kHoldDistance = 12;
-
-  // Factor to convert sensor values to a distance in inches
-  static constexpr double kValueToInches = 0.125;
-
-  // proportional speed constant
-  static constexpr double kP = 7.0;
-
-  // integral speed constant
-  static constexpr double kI = 0.018;
-
-  // derivative speed constant
-  static constexpr double kD = 1.5;
-
-  static constexpr int kLeftMotorPort = 0;
-  static constexpr int kRightMotorPort = 1;
-  static constexpr int kUltrasonicPort = 0;
-
-  // median filter to discard outliers; filters over 5 samples
-  frc::MedianFilter<double> m_filter{5};
-
-  frc::AnalogInput m_ultrasonic{kUltrasonicPort};
-
-  frc::PWMSparkMax m_left{kLeftMotorPort};
-  frc::PWMSparkMax m_right{kRightMotorPort};
-  frc::DifferentialDrive m_robotDrive{m_left, m_right};
-
-  frc2::PIDController m_pidController{kP, kI, kD};
-};
+  // disable input squaring -- PID output is linear
+  m_robotDrive.ArcadeDrive(pidOutput, 0, false);
+}
 
 #ifndef RUNNING_FRC_TESTS
 int main() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/include/Robot.h
new file mode 100644
index 0000000..4ff2700
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/include/Robot.h
@@ -0,0 +1,51 @@
+// 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 <frc/TimedRobot.h>
+#include <frc/Ultrasonic.h>
+#include <frc/controller/PIDController.h>
+#include <frc/drive/DifferentialDrive.h>
+#include <frc/filter/MedianFilter.h>
+#include <frc/motorcontrol/PWMSparkMax.h>
+#include <units/length.h>
+
+/**
+ * This is a sample program to demonstrate the use of a PIDController with an
+ * ultrasonic sensor to reach and maintain a set distance from an object.
+ */
+class Robot : public frc::TimedRobot {
+ public:
+  void AutonomousInit() override;
+  void AutonomousPeriodic() override;
+
+  // distance the robot wants to stay from an object
+  static constexpr units::millimeter_t kHoldDistance = 1_m;
+
+  static constexpr int kLeftMotorPort = 0;
+  static constexpr int kRightMotorPort = 1;
+  static constexpr int kUltrasonicPingPort = 0;
+  static constexpr int kUltrasonicEchoPort = 1;
+
+ private:
+  // proportional speed constant
+  // negative because applying positive voltage will bring us closer to the
+  // target
+  static constexpr double kP = -0.001;
+  // integral speed constant
+  static constexpr double kI = 0.0;
+  // derivative speed constant
+  static constexpr double kD = 0.0;
+
+  // Ultrasonic sensors tend to be quite noisy and susceptible to sudden
+  // outliers, so measurements are filtered with a 5-sample median filter
+  frc::MedianFilter<units::millimeter_t> m_filter{5};
+
+  frc::Ultrasonic m_ultrasonic{kUltrasonicPingPort, kUltrasonicEchoPort};
+  frc::PWMSparkMax m_left{kLeftMotorPort};
+  frc::PWMSparkMax m_right{kRightMotorPort};
+  frc::DifferentialDrive m_robotDrive{m_left, m_right};
+  frc::PIDController m_pidController{kP, kI, kD};
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/Robot.cpp
new file mode 100644
index 0000000..e39a8fd
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/Robot.cpp
@@ -0,0 +1,71 @@
+// 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 "Robot.h"
+
+#include <frc2/command/CommandScheduler.h>
+
+void Robot::RobotInit() {}
+
+/**
+ * This function is called every 20 ms, no matter the mode. Use
+ * this for items like diagnostics that you want to run during disabled,
+ * autonomous, teleoperated and test.
+ *
+ * <p> This runs after the mode specific periodic functions, but before
+ * LiveWindow and SmartDashboard integrated updating.
+ */
+void Robot::RobotPeriodic() {
+  frc2::CommandScheduler::GetInstance().Run();
+}
+
+/**
+ * This function is called once each time the robot enters Disabled mode. You
+ * can use it to reset any subsystem information you want to clear when the
+ * robot is disabled.
+ */
+void Robot::DisabledInit() {}
+
+void Robot::DisabledPeriodic() {}
+
+/**
+ * This autonomous runs the autonomous command selected by your {@link
+ * RobotContainer} class.
+ */
+void Robot::AutonomousInit() {
+  m_autonomousCommand = m_container.GetAutonomousCommand();
+
+  if (m_autonomousCommand != nullptr) {
+    m_autonomousCommand->Schedule();
+  }
+}
+
+void Robot::AutonomousPeriodic() {}
+
+void Robot::TeleopInit() {
+  // This makes sure that the autonomous stops running when
+  // teleop starts running. If you want the autonomous to
+  // continue until interrupted by another command, remove
+  // this line or comment it out.
+  if (m_autonomousCommand != nullptr) {
+    m_autonomousCommand->Cancel();
+    m_autonomousCommand = nullptr;
+  }
+}
+
+/**
+ * This function is called periodically during operator control.
+ */
+void Robot::TeleopPeriodic() {}
+
+/**
+ * This function is called periodically during test mode.
+ */
+void Robot::TestPeriodic() {}
+
+#ifndef RUNNING_FRC_TESTS
+int main() {
+  return frc::StartRobot<Robot>();
+}
+#endif
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/RobotContainer.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/RobotContainer.cpp
new file mode 100644
index 0000000..2b6cfa2
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/RobotContainer.cpp
@@ -0,0 +1,44 @@
+// 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 "RobotContainer.h"
+
+#include <frc/smartdashboard/SmartDashboard.h>
+#include <frc2/command/Commands.h>
+#include <frc2/command/button/JoystickButton.h>
+
+#include "commands/TeleopArcadeDrive.h"
+
+RobotContainer::RobotContainer() {
+  // Configure the button bindings
+  ConfigureButtonBindings();
+}
+
+void RobotContainer::ConfigureButtonBindings() {
+  // Also set default commands here
+  m_drive.SetDefaultCommand(TeleopArcadeDrive(
+      &m_drive, [this] { return -m_controller.GetRawAxis(1); },
+      [this] { return -m_controller.GetRawAxis(2); }));
+
+  // Example of how to use the onboard IO
+  m_userButton.OnTrue(frc2::cmd::Print("USER Button Pressed"))
+      .OnFalse(frc2::cmd::Print("USER Button Released"));
+
+  frc2::JoystickButton(&m_controller, 1)
+      .OnTrue(frc2::cmd::RunOnce([this] { m_arm.SetAngle(45.0); }, {}))
+      .OnFalse(frc2::cmd::RunOnce([this] { m_arm.SetAngle(0.0); }, {}));
+
+  frc2::JoystickButton(&m_controller, 2)
+      .OnTrue(frc2::cmd::RunOnce([this] { m_arm.SetAngle(90.0); }, {}))
+      .OnFalse(frc2::cmd::RunOnce([this] { m_arm.SetAngle(0.0); }, {}));
+
+  // Setup SmartDashboard options.
+  m_chooser.SetDefaultOption("Auto Routine Distance", &m_autoDistance);
+  m_chooser.AddOption("Auto Routine Time", &m_autoTime);
+  frc::SmartDashboard::PutData("Auto Selector", &m_chooser);
+}
+
+frc2::Command* RobotContainer::GetAutonomousCommand() {
+  return m_chooser.GetSelected();
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/DriveDistance.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/DriveDistance.cpp
new file mode 100644
index 0000000..dcb59e3
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/DriveDistance.cpp
@@ -0,0 +1,24 @@
+// 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 "commands/DriveDistance.h"
+
+#include <units/math.h>
+
+void DriveDistance::Initialize() {
+  m_drive->ArcadeDrive(0, 0);
+  m_drive->ResetEncoders();
+}
+
+void DriveDistance::Execute() {
+  m_drive->ArcadeDrive(m_speed, 0);
+}
+
+void DriveDistance::End(bool interrupted) {
+  m_drive->ArcadeDrive(0, 0);
+}
+
+bool DriveDistance::IsFinished() {
+  return units::math::abs(m_drive->GetAverageDistance()) >= m_distance;
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/DriveTime.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/DriveTime.cpp
new file mode 100644
index 0000000..52670a0
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/DriveTime.cpp
@@ -0,0 +1,24 @@
+// 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 "commands/DriveTime.h"
+
+void DriveTime::Initialize() {
+  m_timer.Start();
+  m_drive->ArcadeDrive(0, 0);
+}
+
+void DriveTime::Execute() {
+  m_drive->ArcadeDrive(m_speed, 0);
+}
+
+void DriveTime::End(bool interrupted) {
+  m_drive->ArcadeDrive(0, 0);
+  m_timer.Stop();
+  m_timer.Reset();
+}
+
+bool DriveTime::IsFinished() {
+  return m_timer.HasElapsed(m_duration);
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/TeleopArcadeDrive.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/TeleopArcadeDrive.cpp
new file mode 100644
index 0000000..e457af9
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/TeleopArcadeDrive.cpp
@@ -0,0 +1,20 @@
+// 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 "commands/TeleopArcadeDrive.h"
+
+#include "subsystems/Drivetrain.h"
+
+TeleopArcadeDrive::TeleopArcadeDrive(
+    Drivetrain* subsystem, std::function<double()> xaxisSpeedSupplier,
+    std::function<double()> zaxisRotateSuppplier)
+    : m_drive{subsystem},
+      m_xaxisSpeedSupplier{xaxisSpeedSupplier},
+      m_zaxisRotateSupplier{zaxisRotateSuppplier} {
+  AddRequirements(subsystem);
+}
+
+void TeleopArcadeDrive::Execute() {
+  m_drive->ArcadeDrive(m_xaxisSpeedSupplier(), m_zaxisRotateSupplier());
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/TurnDegrees.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/TurnDegrees.cpp
new file mode 100644
index 0000000..65fd255
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/TurnDegrees.cpp
@@ -0,0 +1,40 @@
+// 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 "commands/TurnDegrees.h"
+
+#include <numbers>
+
+#include <units/math.h>
+
+void TurnDegrees::Initialize() {
+  // Set motors to stop, read encoder values for starting point
+  m_drive->ArcadeDrive(0, 0);
+  m_drive->ResetEncoders();
+}
+
+void TurnDegrees::Execute() {
+  m_drive->ArcadeDrive(0, m_speed);
+}
+
+void TurnDegrees::End(bool interrupted) {
+  m_drive->ArcadeDrive(0, 0);
+}
+
+bool TurnDegrees::IsFinished() {
+  // Need to convert distance travelled to degrees. The Standard XRP Chassis
+  // found here https://www.sparkfun.com/products/22230, has a
+  // wheel placement diameter (163 mm) - width of the wheel (8 mm) = 155 mm
+  // or 6.102 inches. We then take into consideration the width of the tires.
+  static auto inchPerDegree = (6.102_in * std::numbers::pi) / 360_deg;
+
+  // Compare distance traveled from start to distance based on degree turn.
+  return GetAverageTurningDistance() >= inchPerDegree * m_angle;
+}
+
+units::meter_t TurnDegrees::GetAverageTurningDistance() {
+  auto l = units::math::abs(m_drive->GetLeftDistance());
+  auto r = units::math::abs(m_drive->GetRightDistance());
+  return (l + r) / 2;
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/TurnTime.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/TurnTime.cpp
new file mode 100644
index 0000000..80c4bd2
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/commands/TurnTime.cpp
@@ -0,0 +1,24 @@
+// 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 "commands/TurnTime.h"
+
+void TurnTime::Initialize() {
+  m_timer.Start();
+  m_drive->ArcadeDrive(0, 0);
+}
+
+void TurnTime::Execute() {
+  m_drive->ArcadeDrive(0, m_speed);
+}
+
+void TurnTime::End(bool interrupted) {
+  m_drive->ArcadeDrive(0, 0);
+  m_timer.Stop();
+  m_timer.Reset();
+}
+
+bool TurnTime::IsFinished() {
+  return m_timer.HasElapsed(m_duration);
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/subsystems/Arm.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/subsystems/Arm.cpp
new file mode 100644
index 0000000..cf75e52
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/subsystems/Arm.cpp
@@ -0,0 +1,13 @@
+// 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 "subsystems/Arm.h"
+
+void Arm::Periodic() {
+  // This method will be called once per scheduler run.
+}
+
+void Arm::SetAngle(double angleDeg) {
+  m_armServo.SetAngle(angleDeg);
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/subsystems/Drivetrain.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/subsystems/Drivetrain.cpp
new file mode 100644
index 0000000..bcec018
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/subsystems/Drivetrain.cpp
@@ -0,0 +1,89 @@
+// 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 "subsystems/Drivetrain.h"
+
+#include <numbers>
+
+#include "Constants.h"
+
+using namespace DriveConstants;
+
+// The XRP has the left and right motors set to
+// PWM channels 0 and 1 respectively
+// The XRP has onboard encoders that are hardcoded
+// to use DIO pins 4/5 and 6/7 for the left and right
+Drivetrain::Drivetrain() {
+  // We need to invert one side of the drivetrain so that positive voltages
+  // result in both sides moving forward. Depending on how your robot's
+  // gearbox is constructed, you might have to invert the left side instead.
+  m_rightMotor.SetInverted(true);
+
+  m_leftEncoder.SetDistancePerPulse(std::numbers::pi * kWheelDiameter.value() /
+                                    kCountsPerRevolution);
+  m_rightEncoder.SetDistancePerPulse(std::numbers::pi * kWheelDiameter.value() /
+                                     kCountsPerRevolution);
+  ResetEncoders();
+}
+
+void Drivetrain::Periodic() {
+  // This method will be called once per scheduler run.
+}
+
+void Drivetrain::ArcadeDrive(double xaxisSpeed, double zaxisRotate) {
+  m_drive.ArcadeDrive(xaxisSpeed, zaxisRotate);
+}
+
+void Drivetrain::ResetEncoders() {
+  m_leftEncoder.Reset();
+  m_rightEncoder.Reset();
+}
+
+int Drivetrain::GetLeftEncoderCount() {
+  return m_leftEncoder.Get();
+}
+
+int Drivetrain::GetRightEncoderCount() {
+  return m_rightEncoder.Get();
+}
+
+units::meter_t Drivetrain::GetLeftDistance() {
+  return units::meter_t{m_leftEncoder.GetDistance()};
+}
+
+units::meter_t Drivetrain::GetRightDistance() {
+  return units::meter_t{m_rightEncoder.GetDistance()};
+}
+
+units::meter_t Drivetrain::GetAverageDistance() {
+  return (GetLeftDistance() + GetRightDistance()) / 2.0;
+}
+
+double Drivetrain::GetAccelX() {
+  return m_accelerometer.GetX();
+}
+
+double Drivetrain::GetAccelY() {
+  return m_accelerometer.GetY();
+}
+
+double Drivetrain::GetAccelZ() {
+  return m_accelerometer.GetZ();
+}
+
+double Drivetrain::GetGyroAngleX() {
+  return m_gyro.GetAngleX();
+}
+
+double Drivetrain::GetGyroAngleY() {
+  return m_gyro.GetAngleY();
+}
+
+double Drivetrain::GetGyroAngleZ() {
+  return m_gyro.GetAngleZ();
+}
+
+void Drivetrain::ResetGyro() {
+  m_gyro.Reset();
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/Constants.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/Constants.h
new file mode 100644
index 0000000..e5cee33
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/Constants.h
@@ -0,0 +1,19 @@
+// 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
+
+/**
+ * The Constants header provides a convenient place for teams to hold robot-wide
+ * numerical or bool constants.  This should not be used for any other purpose.
+ *
+ * It is generally a good idea to place constants into subsystem- or
+ * command-specific namespaces within this header, which can then be used where
+ * they are needed.
+ */
+
+namespace DriveConstants {
+constexpr double kCountsPerRevolution = 1440.0;
+constexpr double kWheelDiameterInch = 2.75;
+}  // namespace DriveConstants
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/Robot.h
new file mode 100644
index 0000000..eb35fab
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/Robot.h
@@ -0,0 +1,29 @@
+// 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 <frc/TimedRobot.h>
+#include <frc2/command/Command.h>
+
+#include "RobotContainer.h"
+
+class Robot : public frc::TimedRobot {
+ public:
+  void RobotInit() override;
+  void RobotPeriodic() override;
+  void DisabledInit() override;
+  void DisabledPeriodic() override;
+  void AutonomousInit() override;
+  void AutonomousPeriodic() override;
+  void TeleopInit() override;
+  void TeleopPeriodic() override;
+  void TestPeriodic() override;
+
+ private:
+  // Have it null by default so that if testing teleop it
+  // doesn't have undefined behavior and potentially crash.
+  frc2::Command* m_autonomousCommand = nullptr;
+  RobotContainer m_container;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/RobotContainer.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/RobotContainer.h
new file mode 100644
index 0000000..51ea812
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/RobotContainer.h
@@ -0,0 +1,63 @@
+// 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 <frc/Joystick.h>
+#include <frc/smartdashboard/SendableChooser.h>
+#include <frc/xrp/XRPOnBoardIO.h>
+#include <frc2/command/Command.h>
+#include <frc2/command/CommandPtr.h>
+#include <frc2/command/button/Trigger.h>
+
+#include "Constants.h"
+#include "commands/AutonomousDistance.h"
+#include "commands/AutonomousTime.h"
+#include "subsystems/Arm.h"
+#include "subsystems/Drivetrain.h"
+
+/**
+ * This class is where the bulk of the robot should be declared.  Since
+ * Command-based is a "declarative" paradigm, very little robot logic should
+ * actually be handled in the {@link Robot} periodic methods (other than the
+ * scheduler calls).  Instead, the structure of the robot (including subsystems,
+ * commands, and button mappings) should be declared here.
+ */
+class RobotContainer {
+  // NOTE: The I/O pin functionality of the 5 exposed I/O pins depends on the
+  // hardware "overlay"
+  // that is specified when launching the wpilib-ws server on the Romi raspberry
+  // pi. By default, the following are available (listed in order from inside of
+  // the board to outside):
+  // - DIO 8 (mapped to Arduino pin 11, closest to the inside of the board)
+  // - Analog In 0 (mapped to Analog Channel 6 / Arduino Pin 4)
+  // - Analog In 1 (mapped to Analog Channel 2 / Arduino Pin 20)
+  // - PWM 2 (mapped to Arduino Pin 21)
+  // - PWM 3 (mapped to Arduino Pin 22)
+  //
+  // Your subsystem configuration should take the overlays into account
+ public:
+  RobotContainer();
+  frc2::Command* GetAutonomousCommand();
+
+ private:
+  // Assumes a gamepad plugged into channel 0
+  frc::Joystick m_controller{0};
+  frc::SendableChooser<frc2::Command*> m_chooser;
+
+  // The robot's subsystems
+  Drivetrain m_drive;
+  Arm m_arm;
+  frc::XRPOnBoardIO m_onboardIO;
+
+  // Example button
+  frc2::Trigger m_userButton{
+      [this] { return m_onboardIO.GetUserButtonPressed(); }};
+
+  // Autonomous commands.
+  AutonomousDistance m_autoDistance{&m_drive};
+  AutonomousTime m_autoTime{&m_drive};
+
+  void ConfigureButtonBindings();
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/AutonomousDistance.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/AutonomousDistance.h
new file mode 100644
index 0000000..8991ce6
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/AutonomousDistance.h
@@ -0,0 +1,23 @@
+// 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 <frc2/command/CommandHelper.h>
+#include <frc2/command/SequentialCommandGroup.h>
+
+#include "commands/DriveDistance.h"
+#include "commands/TurnDegrees.h"
+#include "subsystems/Drivetrain.h"
+
+class AutonomousDistance
+    : public frc2::CommandHelper<frc2::SequentialCommandGroup,
+                                 AutonomousDistance> {
+ public:
+  explicit AutonomousDistance(Drivetrain* drive) {
+    AddCommands(
+        DriveDistance(-0.5, 10_in, drive), TurnDegrees(-0.5, 180_deg, drive),
+        DriveDistance(-0.5, 10_in, drive), TurnDegrees(0.5, 180_deg, drive));
+  }
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/AutonomousTime.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/AutonomousTime.h
new file mode 100644
index 0000000..e25e5ea
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/AutonomousTime.h
@@ -0,0 +1,21 @@
+// 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 <frc2/command/CommandHelper.h>
+#include <frc2/command/SequentialCommandGroup.h>
+
+#include "commands/DriveTime.h"
+#include "commands/TurnTime.h"
+#include "subsystems/Drivetrain.h"
+
+class AutonomousTime
+    : public frc2::CommandHelper<frc2::SequentialCommandGroup, AutonomousTime> {
+ public:
+  explicit AutonomousTime(Drivetrain* drive) {
+    AddCommands(DriveTime(-0.6, 2_s, drive), TurnTime(-0.5, 1.3_s, drive),
+                DriveTime(-0.6, 2_s, drive), TurnTime(0.5, 1.3_s, drive));
+  }
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/DriveDistance.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/DriveDistance.h
new file mode 100644
index 0000000..6011531
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/DriveDistance.h
@@ -0,0 +1,29 @@
+// 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 <frc2/command/Command.h>
+#include <frc2/command/CommandHelper.h>
+#include <units/length.h>
+
+#include "subsystems/Drivetrain.h"
+
+class DriveDistance : public frc2::CommandHelper<frc2::Command, DriveDistance> {
+ public:
+  DriveDistance(double speed, units::meter_t distance, Drivetrain* drive)
+      : m_speed(speed), m_distance(distance), m_drive(drive) {
+    AddRequirements(m_drive);
+  }
+
+  void Initialize() override;
+  void Execute() override;
+  void End(bool interrupted) override;
+  bool IsFinished() override;
+
+ private:
+  double m_speed;
+  units::meter_t m_distance;
+  Drivetrain* m_drive;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/DriveTime.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/DriveTime.h
new file mode 100644
index 0000000..9dfd240
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/DriveTime.h
@@ -0,0 +1,31 @@
+// 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 <frc/Timer.h>
+#include <frc2/command/Command.h>
+#include <frc2/command/CommandHelper.h>
+#include <units/time.h>
+
+#include "subsystems/Drivetrain.h"
+
+class DriveTime : public frc2::CommandHelper<frc2::Command, DriveTime> {
+ public:
+  DriveTime(double speed, units::second_t time, Drivetrain* drive)
+      : m_speed(speed), m_duration(time), m_drive(drive) {
+    AddRequirements(m_drive);
+  }
+
+  void Initialize() override;
+  void Execute() override;
+  void End(bool interrupted) override;
+  bool IsFinished() override;
+
+ private:
+  double m_speed;
+  units::second_t m_duration;
+  Drivetrain* m_drive;
+  frc::Timer m_timer;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/TeleopArcadeDrive.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/TeleopArcadeDrive.h
new file mode 100644
index 0000000..fe2e241
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/TeleopArcadeDrive.h
@@ -0,0 +1,26 @@
+// 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 <functional>
+
+#include <frc2/command/Command.h>
+#include <frc2/command/CommandHelper.h>
+
+#include "subsystems/Drivetrain.h"
+
+class TeleopArcadeDrive
+    : public frc2::CommandHelper<frc2::Command, TeleopArcadeDrive> {
+ public:
+  TeleopArcadeDrive(Drivetrain* subsystem,
+                    std::function<double()> xaxisSpeedSupplier,
+                    std::function<double()> zaxisRotateSupplier);
+  void Execute() override;
+
+ private:
+  Drivetrain* m_drive;
+  std::function<double()> m_xaxisSpeedSupplier;
+  std::function<double()> m_zaxisRotateSupplier;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/TurnDegrees.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/TurnDegrees.h
new file mode 100644
index 0000000..c0f25b1
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/TurnDegrees.h
@@ -0,0 +1,32 @@
+// 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 <frc2/command/Command.h>
+#include <frc2/command/CommandHelper.h>
+#include <units/angle.h>
+#include <units/length.h>
+
+#include "subsystems/Drivetrain.h"
+
+class TurnDegrees : public frc2::CommandHelper<frc2::Command, TurnDegrees> {
+ public:
+  TurnDegrees(double speed, units::degree_t angle, Drivetrain* drive)
+      : m_speed(speed), m_angle(angle), m_drive(drive) {
+    AddRequirements(m_drive);
+  }
+
+  void Initialize() override;
+  void Execute() override;
+  void End(bool interrupted) override;
+  bool IsFinished() override;
+
+ private:
+  double m_speed;
+  units::degree_t m_angle;
+  Drivetrain* m_drive;
+
+  units::meter_t GetAverageTurningDistance();
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/TurnTime.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/TurnTime.h
new file mode 100644
index 0000000..3c478a8
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/commands/TurnTime.h
@@ -0,0 +1,31 @@
+// 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 <frc/Timer.h>
+#include <frc2/command/Command.h>
+#include <frc2/command/CommandHelper.h>
+#include <units/time.h>
+
+#include "subsystems/Drivetrain.h"
+
+class TurnTime : public frc2::CommandHelper<frc2::Command, TurnTime> {
+ public:
+  TurnTime(double speed, units::second_t time, Drivetrain* drive)
+      : m_speed(speed), m_duration(time), m_drive(drive) {
+    AddRequirements(m_drive);
+  }
+
+  void Initialize() override;
+  void Execute() override;
+  void End(bool interrupted) override;
+  bool IsFinished() override;
+
+ private:
+  double m_speed;
+  units::second_t m_duration;
+  Drivetrain* m_drive;
+  frc::Timer m_timer;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/subsystems/Arm.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/subsystems/Arm.h
new file mode 100644
index 0000000..aacb7c6
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/subsystems/Arm.h
@@ -0,0 +1,26 @@
+// 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 <frc/xrp/XRPServo.h>
+#include <frc2/command/SubsystemBase.h>
+
+class Arm : public frc2::SubsystemBase {
+ public:
+  /**
+   * Will be called periodically whenever the CommandScheduler runs.
+   */
+  void Periodic() override;
+
+  /**
+   * Set the current angle of the arm (0 - 180 degrees).
+   *
+   * @param angleDeg the commanded angle
+   */
+  void SetAngle(double angleDeg);
+
+ private:
+  frc::XRPServo m_armServo{4};
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/subsystems/Drivetrain.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/subsystems/Drivetrain.h
new file mode 100644
index 0000000..e665601
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/XRPReference/include/subsystems/Drivetrain.h
@@ -0,0 +1,125 @@
+// 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 <frc/BuiltInAccelerometer.h>
+#include <frc/Encoder.h>
+#include <frc/drive/DifferentialDrive.h>
+#include <frc/xrp/XRPGyro.h>
+#include <frc/xrp/XRPMotor.h>
+#include <frc2/command/SubsystemBase.h>
+#include <units/length.h>
+
+class Drivetrain : public frc2::SubsystemBase {
+ public:
+  static constexpr double kGearRatio =
+      (30.0 / 14.0) * (28.0 / 16.0) * (36.0 / 9.0) * (26.0 / 8.0);  // 48.75:1
+  static constexpr double kCountsPerMotorShaftRev = 12.0;
+  static constexpr double kCountsPerRevolution =
+      kCountsPerMotorShaftRev * kGearRatio;  // 585.0
+  static constexpr units::meter_t kWheelDiameter = 60_mm;
+
+  Drivetrain();
+
+  /**
+   * Will be called periodically whenever the CommandScheduler runs.
+   */
+  void Periodic() override;
+
+  /**
+   * Drives the robot using arcade controls.
+   *
+   * @param xaxisSpeed the commanded forward movement
+   * @param zaxisRotate the commanded rotation
+   */
+  void ArcadeDrive(double xaxisSpeed, double zaxisRotate);
+
+  /**
+   * Resets the drive encoders to currently read a position of 0.
+   */
+  void ResetEncoders();
+
+  /**
+   * Gets the left drive encoder count.
+   *
+   * @return the left drive encoder count
+   */
+  int GetLeftEncoderCount();
+
+  /**
+   * Gets the right drive encoder count.
+   *
+   * @return the right drive encoder count
+   */
+  int GetRightEncoderCount();
+
+  /**
+   * Gets the left distance driven.
+   *
+   * @return the left-side distance driven
+   */
+  units::meter_t GetLeftDistance();
+
+  /**
+   * Gets the right distance driven.
+   *
+   * @return the right-side distance driven
+   */
+  units::meter_t GetRightDistance();
+
+  /**
+   * Returns the average distance traveled by the left and right encoders.
+   *
+   * @return The average distance traveled by the left and right encoders.
+   */
+  units::meter_t GetAverageDistance();
+
+  /**
+   * Returns the acceleration along the X-axis, in Gs.
+   */
+  double GetAccelX();
+
+  /**
+   * Returns the acceleration along the Y-axis, in Gs.
+   */
+  double GetAccelY();
+
+  /**
+   * Returns the acceleration along the Z-axis, in Gs.
+   */
+  double GetAccelZ();
+
+  /**
+   * Returns the current angle of the Romi around the X-axis, in degrees.
+   */
+  double GetGyroAngleX();
+
+  /**
+   * Returns the current angle of the Romi around the Y-axis, in degrees.
+   */
+  double GetGyroAngleY();
+
+  /**
+   * Returns the current angle of the Romi around the Z-axis, in degrees.
+   */
+  double GetGyroAngleZ();
+
+  /**
+   * Reset the gyro.
+   */
+  void ResetGyro();
+
+ private:
+  frc::XRPMotor m_leftMotor{0};
+  frc::XRPMotor m_rightMotor{1};
+
+  frc::Encoder m_leftEncoder{4, 5};
+  frc::Encoder m_rightEncoder{6, 7};
+
+  frc::DifferentialDrive m_drive{m_leftMotor, m_rightMotor};
+
+  frc::XRPGyro m_gyro;
+  frc::BuiltInAccelerometer m_accelerometer;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/examples.json b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/examples.json
index aaec45d..8032909 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/examples.json
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/examples/examples.json
@@ -1,14 +1,12 @@
 [
   {
     "name": "Motor Control",
-    "description": "Demonstrate controlling a single motor with a Joystick and displaying the net movement of the motor using an encoder.",
+    "description": "Control a single motor with a joystick, displaying the movement of the motor using an encoder.",
     "tags": [
-      "Robot and Motor",
-      "Digital",
-      "Sensors",
-      "Actuators",
-      "Joystick",
-      "Complete List"
+      "Basic Robot",
+      "Encoder",
+      "SmartDashboard",
+      "Joystick"
     ],
     "foldername": "MotorControl",
     "gradlebase": "cpp",
@@ -16,11 +14,11 @@
   },
   {
     "name": "Relay",
-    "description": "Demonstrate controlling a Relay from Joystick buttons.",
+    "description": "Control a relay from joystick buttons.",
     "tags": [
-      "Actuators",
-      "Joystick",
-      "Complete List"
+      "Hardware",
+      "Relay",
+      "Joystick"
     ],
     "foldername": "Relay",
     "gradlebase": "cpp",
@@ -28,32 +26,40 @@
   },
   {
     "name": "PDP CAN Monitoring",
-    "description": "Demonstrate using CAN to monitor the voltage, current, and temperature in the Power Distribution Panel.",
+    "description": "Monitor Power Distribution data such as voltage, current, temperature, etc.",
     "tags": [
-      "Complete List",
-      "CAN",
-      "Sensors"
+      "Hardware",
+      "PDP",
+      "SmartDashboard"
     ],
     "foldername": "CANPDP",
     "gradlebase": "cpp",
     "commandversion": 2
   },
   {
-    "name":"Mechanism2d",
-    "foldername":"Mechanism2d",
-    "gradlebase":"cpp",
-    "description":"An example usage of Mechanism2d to display mechanism states on a dashboard.",
-    "tags":["Mechanism2d"],
+    "name": "Mechanism2d",
+    "foldername": "Mechanism2d",
+    "gradlebase": "cpp",
+    "description": "Display mechanism states on a dashboard with Mechanism2d.",
+    "tags": [
+      "Basic Robot",
+      "Elevator",
+      "Arm",
+      "Analog",
+      "Joystick",
+      "SmartDashboard",
+      "Mechanism2d"
+    ],
     "commandversion": 2
   },
   {
     "name": "Solenoids",
-    "description": "Demonstrate controlling a single and double solenoid from Joystick buttons.",
+    "description": "Control a single and double solenoid from joystick buttons.",
     "tags": [
-      "Actuators",
+      "Hardware",
       "Joystick",
-      "Pneumatics",
-      "Complete List"
+      "Shuffleboard",
+      "Pneumatics"
     ],
     "foldername": "Solenoid",
     "gradlebase": "cpp",
@@ -61,11 +67,11 @@
   },
   {
     "name": "Encoder",
-    "description": "Demonstrate displaying the value of a quadrature encoder on the SmartDashboard.",
+    "description": "View values from a quadrature encoder.",
     "tags": [
-      "Complete List",
-      "Digital",
-      "Sensors"
+      "Hardware",
+      "Encoder",
+      "SmartDashboard"
     ],
     "foldername": "Encoder",
     "gradlebase": "cpp",
@@ -73,8 +79,10 @@
   },
   {
     "name": "EventLoop",
-    "description": "Demonstrate managing a ball system using EventLoop and BooleanEvent.",
+    "description": "Manage a ball system using EventLoop and BooleanEvent.",
     "tags": [
+      "Basic Robot",
+      "Flywheel",
       "EventLoop"
     ],
     "foldername": "EventLoop",
@@ -83,12 +91,11 @@
   },
   {
     "name": "Arcade Drive",
-    "description": "An example program which demonstrates the use of Arcade Drive with the DifferentialDrive class",
+    "description": "Control a differential drivetrain with single-joystick arcade drive in teleop.",
     "tags": [
-      "Getting Started with C++",
-      "Robot and Motor",
-      "Joystick",
-      "Complete List"
+      "Basic Robot",
+      "Differential Drive",
+      "Joystick"
     ],
     "foldername": "ArcadeDrive",
     "gradlebase": "cpp",
@@ -96,12 +103,11 @@
   },
   {
     "name": "Tank Drive",
-    "description": "An example program which demonstrates the use of Tank Drive with the DifferentialDrive class",
+    "description": "Control a differential drive with twin-joystick tank drive in teleop.",
     "tags": [
-      "Getting Started with C++",
-      "Robot and Motor",
-      "Joystick",
-      "Complete List"
+      "Basic Robot",
+      "Differential Drive",
+      "Joystick"
     ],
     "foldername": "TankDrive",
     "gradlebase": "cpp",
@@ -109,12 +115,11 @@
   },
   {
     "name": "Mecanum Drive",
-    "description": "An example program which demonstrates the use of Mecanum Drive with the MecanumDrive class",
+    "description": "Control a mecanum drivetrain with a joystick in teleop.",
     "tags": [
-      "Getting Started with C++",
-      "Robot and Motor",
-      "Joystick",
-      "Complete List"
+      "Basic Robot",
+      "Mecanum Drive",
+      "Joystick"
     ],
     "foldername": "MecanumDrive",
     "gradlebase": "cpp",
@@ -122,12 +127,12 @@
   },
   {
     "name": "Ultrasonic",
-    "description": "Demonstrate maintaining a set distance using an ultrasonic sensor.",
+    "description": "View values from a ping-response ultrasonic sensor.",
     "tags": [
-      "Robot and Motor",
-      "Complete List",
-      "Sensors",
-      "Analog"
+      "Hardware",
+      "Ultrasonic",
+      "SmartDashboard",
+      "Shuffleboard"
     ],
     "foldername": "Ultrasonic",
     "gradlebase": "cpp",
@@ -135,12 +140,12 @@
   },
   {
     "name": "UltrasonicPID",
-    "description": "Demonstrate maintaining a set distance using an ultrasonic sensor and PID control.",
+    "description": "Maintain a set distance from an obstacle with an ultrasonic sensor and PID control.",
     "tags": [
-      "Robot and Motor",
-      "Complete List",
-      "Sensors",
-      "Analog"
+      "Basic Robot",
+      "Ultrasonic",
+      "PID",
+      "Differential Drive"
     ],
     "foldername": "UltrasonicPID",
     "gradlebase": "cpp",
@@ -148,11 +153,12 @@
   },
   {
     "name": "Gyro",
-    "description": "An example program showing how to drive straight with using a gyro sensor.",
+    "description": "Drive a differential drive straight with a gyro sensor.",
     "tags": [
-      "Robot and Motor",
-      "Complete List",
-      "Sensors",
+      "Basic Robot",
+      "Differential Drive",
+      "PID",
+      "Gyro",
       "Analog",
       "Joystick"
     ],
@@ -162,11 +168,11 @@
   },
   {
     "name": "Gyro Mecanum",
-    "description": "An example program showing how to perform mecanum drive with field oriented controls.",
+    "description": "Drive a mecanum drivetrain using field-oriented controls with a joystick.",
     "tags": [
-      "Robot and Motor",
-      "Complete List",
-      "Sensors",
+      "Basic Robot",
+      "Mecanum Drive",
+      "Gyro",
       "Analog",
       "Joystick"
     ],
@@ -176,9 +182,10 @@
   },
   {
     "name": "HID Rumble",
-    "description": "An example program showing how to make human interface devices rumble.",
+    "description": "Make human interface devices (HID) rumble.",
     "tags": [
-      "Joystick"
+      "Hardware",
+      "XboxController"
     ],
     "foldername": "HidRumble",
     "gradlebase": "cpp",
@@ -186,25 +193,40 @@
   },
   {
     "name": "PotentiometerPID",
-    "description": "An example to demonstrate the use of a potentiometer and PID control to reach elevator position setpoints.",
+    "description": "Maintain elevator position setpoints with a potentiometer and PID control.",
     "tags": [
-      "Joystick",
-      "Actuators",
-      "Complete List",
-      "Sensors",
-      "Analog"
+      "Basic Robot",
+      "Analog",
+      "Elevator",
+      "PID",
+      "Joystick"
     ],
     "foldername": "PotentiometerPID",
     "gradlebase": "cpp",
     "commandversion": 2
   },
   {
-    "name": "Elevator with trapezoid profiled PID",
-    "description": "An example to demonstrate the use of an encoder and trapezoid profiled PID control to reach elevator position setpoints.",
+    "name": "Elevator with exponential profiled PID",
+    "description": "Reach elevator position setpoints with exponential profiles and smart motor controller PID.",
     "tags": [
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "Basic Robot",
+      "Elevator",
+      "Exponential Profile",
+      "Smart Motor Controller",
+      "Joystick"
+    ],
+    "foldername": "ElevatorExponentialProfile",
+    "gradlebase": "cpp",
+    "commandversion": 2
+  },
+  {
+    "name": "Elevator with trapezoid profiled PID",
+    "description": "Reach elevator position setpoints with trapezoid profiles and smart motor controller PID.",
+    "tags": [
+      "Basic Robot",
+      "Elevator",
+      "Trapezoid Profile",
+      "Smart Motor Controller",
       "Joystick"
     ],
     "foldername": "ElevatorTrapezoidProfile",
@@ -213,11 +235,11 @@
   },
   {
     "name": "Elevator with profiled PID controller",
-    "description": "An example to demonstrate the use of an encoder and trapezoid profiled PID control to reach elevator position setpoints.",
+    "description": "Reach elevator position setpoints with an encoder and profiled PID control.",
     "tags": [
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "Basic Robot",
+      "Elevator",
+      "Profiled PID",
       "Joystick"
     ],
     "foldername": "ElevatorProfiledPID",
@@ -226,10 +248,9 @@
   },
   {
     "name": "Getting Started",
-    "description": "An example program which demonstrates the simplest autonomous and teleoperated routines.",
+    "description": "A differential-drive robot with split-stick Xbox arcade drive with a simple time-based autonomous.",
     "tags": [
-      "Getting Started with C++",
-      "Complete List"
+      "Basic Robot"
     ],
     "foldername": "GettingStarted",
     "gradlebase": "cpp",
@@ -237,10 +258,9 @@
   },
   {
     "name": "Simple Vision",
-    "description": "The minimal program to acquire images from an attached USB camera on the robot and send them to the dashboard.",
+    "description": "Use the CameraServer class to stream from a USB Webcam without processing the images.",
     "tags": [
-      "Vision",
-      "Complete List"
+      "Vision"
     ],
     "foldername": "QuickVision",
     "gradlebase": "cpp",
@@ -248,19 +268,30 @@
   },
   {
     "name": "Intermediate Vision",
-    "description": "An example program that acquires images from an attached USB camera and adds some annotation to the image as you might do for showing operators the result of some image recognition, and sends it to the dashboard for display.",
+    "description": "Acquire images from an attached USB camera and add some annotation to the image (as you might do for showing operators the result of some image recognition) and send it to the dashboard for display.",
     "tags": [
-      "Vision",
-      "Complete List"
+      "Vision"
     ],
     "foldername": "IntermediateVision",
     "gradlebase": "cpp",
     "commandversion": 2
   },
   {
-    "name": "I2C Communication",
-    "description": "An example program that communicates with external devices (such as an Arduino) using the roboRIO's I2C port",
+    "name": "AprilTags Vision",
+    "description": "On-roboRIO detection of AprilTags using an attached USB camera.",
     "tags": [
+      "Vision",
+      "AprilTags"
+    ],
+    "foldername": "AprilTagsVision",
+    "gradlebase": "cpp",
+    "commandversion": 2
+  },
+  {
+    "name": "I2C Communication",
+    "description": "Communicate with external devices (such as an Arduino) using the roboRIO's I2C port.",
+    "tags": [
+      "Hardware",
       "I2C"
     ],
     "foldername": "I2CCommunication",
@@ -269,21 +300,20 @@
   },
   {
     "name": "Digital Communication Sample",
-    "description": "An example program that communicates with external devices (such as an Arduino) using the roboRIO's DIO",
+    "description": "Communicates with external devices (such as an Arduino) using the roboRIO's DIO.",
     "tags": [
-      "Digital"
+      "Hardware",
+      "Digital Output"
     ],
     "foldername": "DigitalCommunication",
     "gradlebase": "cpp",
     "commandversion": 2
   },
-
   {
     "name": "Axis Camera Sample",
-    "description": "An example program that acquires images from an Axis network camera and adds some annotation to the image as you might do for showing operators the result of some image recognition, and sends it to the dashboard for display. This demonstrates the use of the AxisCamera class.",
+    "description": "Acquire images from an Axis network camera and adds some annotation to the image (as you might do for showing operators the result of some image recognition), and sends it to the dashboard for display.",
     "tags": [
-      "Vision",
-      "Complete List"
+      "Vision"
     ],
     "foldername": "AxisCameraSample",
     "gradlebase": "cpp",
@@ -291,10 +321,17 @@
   },
   {
     "name": "GearsBot",
-    "description": "A fully functional example CommandBased program for WPIs GearsBot robot, using the new command-based framework. This code can run on your computer if it supports simulation.",
+    "description": "A fully functional Command-Based program for WPI's GearsBot robot.",
     "tags": [
-      "CommandBased Robot",
-      "Complete List"
+      "Complete Robot",
+      "Command-based",
+      "Differential Drive",
+      "Elevator",
+      "Arm",
+      "Analog",
+      "Digital Input",
+      "SmartDashboard",
+      "XboxController"
     ],
     "foldername": "GearsBot",
     "gradlebase": "cpp",
@@ -302,8 +339,9 @@
   },
   {
     "name": "HAL",
-    "description": "A program created using the HAL exclusively. This example is for advanced users",
+    "description": "Use the low-level HAL C functions. This example is for advanced users.",
     "tags": [
+      "Basic Robot",
       "HAL"
     ],
     "foldername": "HAL",
@@ -311,10 +349,15 @@
     "commandversion": 2
   },
   {
-    "name": "ShuffleBoard",
-    "description": "An example program that uses ShuffleBoard with its Widgets and Tabs.",
+    "name": "Shuffleboard",
+    "description": "Present various data via the Shuffleboard API.",
     "tags": [
-      "ShuffleBoard"
+      "Basic Robot",
+      "Differential Drive",
+      "Elevator",
+      "Analog",
+      "Encoder",
+      "Shuffleboard"
     ],
     "foldername": "ShuffleBoard",
     "gradlebase": "cpp",
@@ -322,10 +365,17 @@
   },
   {
     "name": "'Traditional' Hatchbot",
-    "description": "A fully-functional command-based hatchbot for the 2019 game using the new command framework.  Written in the 'traditional' style, i.e. commands are given their own classes.",
+    "description": "A fully-functional command-based hatchbot for the 2019 game, written in the 'traditional' style, i.e. commands are given their own classes.",
     "tags": [
-      "Complete robot",
-      "Command-based"
+      "Complete Robot",
+      "Command-based",
+      "Differential Drive",
+      "Encoder",
+      "Shuffleboard",
+      "Sendable",
+      "DataLog",
+      "Pneumatics",
+      "XboxController"
     ],
     "foldername": "HatchbotTraditional",
     "gradlebase": "cpp",
@@ -333,11 +383,17 @@
   },
   {
     "name": "'Inlined' Hatchbot",
-    "description": "A fully-functional command-based hatchbot for the 2019 game using the new command framework.  Written in the 'inlined' style, i.e. many commands are defined inline with lambdas.",
+    "description": "A fully-functional command-based hatchbot for the 2019 game, written in the 'inlined' style, i.e. many commands are defined inline with lambdas.",
     "tags": [
-      "Complete robot",
+      "Complete Robot",
       "Command-based",
-      "Lambdas"
+      "Differential Drive",
+      "Encoder",
+      "Shuffleboard",
+      "Sendable",
+      "DataLog",
+      "Pneumatics",
+      "PS4Controller"
     ],
     "foldername": "HatchbotInlined",
     "gradlebase": "cpp",
@@ -345,11 +401,18 @@
   },
   {
     "name": "Rapid React Command Bot",
-    "description": "A fully-functional command-based fender bot for the 2022 game using the new command framework.",
+    "description": "A fully-functional command-based fender bot for the 2022 game, written in the 'inlined' style, i.e. many commands are defined inline with lambdas.",
     "tags": [
-      "Complete robot",
+      "Complete Robot",
       "Command-based",
-      "Lambdas"
+      "Differential Drive",
+      "Intake",
+      "Flywheel",
+      "Encoder",
+      "Pneumatics",
+      "Digital Input",
+      "PID",
+      "XboxController"
     ],
     "foldername": "RapidReactCommandBot",
     "gradlebase": "cpp",
@@ -357,7 +420,7 @@
   },
   {
     "name": "Select Command Example",
-    "description": "An example showing how to use the SelectCommand class from the new command framework.",
+    "description": "Use SelectCommand to select an autonomous routine.",
     "tags": [
       "Command-based"
     ],
@@ -366,21 +429,15 @@
     "commandversion": 2
   },
   {
-    "name": "Scheduler Event Logging",
-    "description": "An example showing how to use Shuffleboard to log Command events from the CommandScheduler in the new command framework",
-    "tags": [
-      "Command-based",
-      "Shuffleboard"
-    ],
-    "foldername": "SchedulerEventLogging",
-    "gradlebase": "cpp",
-    "commandversion": 2
-  },
-  {
     "name": "Frisbeebot",
-    "description": "An example robot project for a simple frisbee shooter for the 2013 FRC game, Ultimate Ascent, demonstrating use of PID functionality in the command framework",
+    "description": "A simple frisbee shooter for the 2013 game, demonstrating use of PIDSubsystem.",
     "tags": [
+      "Complete Robot",
       "Command-based",
+      "Differential Drive",
+      "Flywheel",
+      "Encoder",
+      "XboxController",
       "PID"
     ],
     "foldername": "Frisbeebot",
@@ -389,10 +446,14 @@
   },
   {
     "name": "Gyro Drive Commands",
-    "description": "An example command-based robot project demonstrating simple PID functionality utilizing a gyroscope to keep a robot driving straight and to turn to specified angles.",
+    "description": "Control a robot's angle with PID and a gyro, in command-based.",
     "tags": [
       "Command-based",
+      "Differential Drive",
+      "Encoder",
+      "PS4Controller",
       "PID",
+      "Profiled PID",
       "Gyro"
     ],
     "foldername": "GyroDriveCommands",
@@ -401,9 +462,13 @@
   },
   {
     "name": "SwerveBot",
-    "description": "An example program for a swerve drive that uses swerve drive kinematics and odometry.",
+    "description": "Use kinematics and odometry with a swerve drive.",
     "tags": [
-      "SwerveBot"
+      "Swerve Drive",
+      "Odometry",
+      "XboxController",
+      "Gyro",
+      "Encoder"
     ],
     "foldername": "SwerveBot",
     "gradlebase": "cpp",
@@ -411,9 +476,13 @@
   },
   {
     "name": "MecanumBot",
-    "description": "An example program for a mecanum drive that uses mecanum drive kinematics and odometry.",
+    "description": "Use kinematics and odometry with a mecanum drive.",
     "tags": [
-      "MecanumBot"
+      "Mecanum Drive",
+      "Odometry",
+      "Encoder",
+      "Gyro",
+      "XboxController"
     ],
     "foldername": "MecanumBot",
     "gradlebase": "cpp",
@@ -421,9 +490,13 @@
   },
   {
     "name": "DifferentialDriveBot",
-    "description": "An example program for a differential drive that uses differential drive kinematics and odometry.",
+    "description": "Use kinematics and odometry with a differential drive.",
     "tags": [
-      "DifferentialDriveBot"
+      "Differential Drive",
+      "Odometry",
+      "Encoder",
+      "Gyro",
+      "XboxController"
     ],
     "foldername": "DifferentialDriveBot",
     "gradlebase": "cpp",
@@ -431,13 +504,17 @@
   },
   {
     "name": "RamseteCommand",
-    "description": "An example command-based robot demonstrating the use of a RamseteCommand to follow a pregenerated trajectory.",
+    "description": "Follow a pre-generated trajectory with a differential drive using RamseteCommand.",
     "tags": [
-      "RamseteCommand",
-      "PID",
+      "Differential Drive",
+      "Command-based",
       "Ramsete",
       "Trajectory",
-      "Path following"
+      "Path Following",
+      "Odometry",
+      "Encoder",
+      "Gyro",
+      "XboxController"
     ],
     "foldername": "RamseteCommand",
     "gradlebase": "cpp",
@@ -445,12 +522,11 @@
   },
   {
     "name": "Arcade Drive Xbox Controller",
-    "description": "An example program which demonstrates the use of Arcade Drive with the DifferentialDrive class and an Xbox Controller.",
+    "description": "Control a differential drive with split-stick arcade drive in teleop.",
     "tags": [
-      "Getting Started with C++",
-      "Robot and Motor",
-      "XboxController",
-      "Complete List"
+      "Basic Robot",
+      "Differential Drive",
+      "XboxController"
     ],
     "foldername": "ArcadeDriveXboxController",
     "gradlebase": "cpp",
@@ -458,12 +534,11 @@
   },
   {
     "name": "Tank Drive Xbox Controller",
-    "description": "An example program which demonstrates the use of Tank Drive with the DifferentialDrive class and an Xbox Controller.",
+    "description": "Control a differential drive with Xbox tank drive in teleop.",
     "tags": [
-      "Getting Started with C++",
-      "Robot and Motor",
-      "XboxController",
-      "Complete List"
+      "Basic Robot",
+      "Differential Drive",
+      "XboxController"
     ],
     "foldername": "TankDriveXboxController",
     "gradlebase": "cpp",
@@ -471,9 +546,12 @@
   },
   {
     "name": "Duty Cycle Encoder",
-    "description": "Demonstrates the use of the Duty Cycle Encoder class",
+    "description": "View values from a duty-cycle encoder.",
     "tags": [
-      "Getting Started with C++"
+      "Hardware",
+      "Duty Cycle",
+      "Encoder",
+      "SmartDashboard"
     ],
     "foldername": "DutyCycleEncoder",
     "gradlebase": "cpp",
@@ -481,9 +559,11 @@
   },
   {
     "name": "Duty Cycle Input",
-    "description": "Demonstrates the use of the Duty Cycle class",
+    "description": "View duty-cycle input.",
     "tags": [
-      "Getting Started with C++"
+      "Hardware",
+      "Duty Cycle",
+      "SmartDashboard"
     ],
     "foldername": "DutyCycleInput",
     "gradlebase": "cpp",
@@ -491,9 +571,11 @@
   },
   {
     "name": "Addressable LED",
-    "description": "Demonstrates the use of the Addressable LED class",
+    "description": "Display a rainbow pattern on an addressable LED strip.",
     "tags": [
-      "Getting Started with C++"
+      "Hardware",
+      "Basic Robot",
+      "AddressableLEDs"
     ],
     "foldername": "AddressableLED",
     "gradlebase": "cpp",
@@ -501,9 +583,11 @@
   },
   {
     "name": "DMA",
-    "description": "Demonstrates the use of the DMA class",
+    "description": "Read various sensors using DMA.",
     "tags": [
-      "Advanced C++"
+      "Hardware",
+      "DMA",
+      "SmartDashboard"
     ],
     "foldername": "DMA",
     "gradlebase": "cpp",
@@ -511,13 +595,16 @@
   },
   {
     "name": "MecanumControllerCommand",
-    "description": "An example command-based robot demonstrating the use of a MecanumControllerCommand to follow a pregenerated trajectory.",
+    "description": "Follow a pre-generated trajectory with a mecanum drive using MecanumControllerCommand.",
     "tags": [
-      "MecanumControllerCommand",
-      "Mecanum",
-      "PID",
+      "Command-based",
+      "Mecanum Drive",
+      "Gyro",
+      "Encoder",
+      "Odometry",
       "Trajectory",
-      "Path following"
+      "Path Following",
+      "XboxController"
     ],
     "foldername": "MecanumControllerCommand",
     "gradlebase": "cpp",
@@ -525,13 +612,16 @@
   },
   {
     "name": "SwerveControllerCommand",
-    "description": "An example command-based robot demonstrating the use of a SwerveControllerCommand to follow a pregenerated trajectory.",
+    "description": "Follow a pre-generated trajectory with a swerve drive using SwerveControllerCommand.",
     "tags": [
-      "SwerveControllerCommand",
-      "Swerve",
-      "PID",
+      "Command-based",
+      "Swerve Drive",
+      "Gyro",
+      "Encoder",
+      "Odometry",
       "Trajectory",
-      "Path following"
+      "Path Following",
+      "XboxController"
     ],
     "foldername": "SwerveControllerCommand",
     "gradlebase": "cpp",
@@ -539,11 +629,14 @@
   },
   {
     "name": "ArmBot",
-    "description": "An example command-based robot demonstrating the use of a ProfiledPIDSubsystem to control an arm.",
+    "description": "Control an arm with ProfiledPIDSubsystem.",
     "tags": [
-      "ArmBot",
-      "PID",
-      "Motion Profile"
+      "Command-based",
+      "Arm",
+      "Encoder",
+      "Profiled PID",
+      "XboxController",
+      "Differential Drive"
     ],
     "foldername": "ArmBot",
     "gradlebase": "cpp",
@@ -551,11 +644,14 @@
   },
   {
     "name": "ArmBotOffboard",
-    "description": "An example command-based robot demonstrating the use of a TrapezoidProfileSubsystem to control an arm with an offboard PID.",
+    "description": "Control an arm with TrapezoidProfileSubsystem and smart motor controller PID.",
     "tags": [
-      "ArmBotOffboard",
-      "PID",
-      "Motion Profile"
+      "Command-based",
+      "Arm",
+      "Smart Motor Controller",
+      "Trapezoid Profile",
+      "XboxController",
+      "Differential Drive"
     ],
     "foldername": "ArmBotOffboard",
     "gradlebase": "cpp",
@@ -563,11 +659,13 @@
   },
   {
     "name": "DriveDistanceOffboard",
-    "description": "An example command-based robot demonstrating the use of a TrapezoidProfileCommand to drive a robot a set distance with offboard PID on the drive.",
+    "description": "Drive a differential drivetrain a set distance using TrapezoidProfileCommand and smart motor controller PID.",
     "tags": [
-      "DriveDistance",
-      "PID",
-      "Motion Profile"
+      "Command-based",
+      "Differential Drive",
+      "Trapezoid Profile",
+      "Smart Motor Controller",
+      "XboxController"
     ],
     "foldername": "DriveDistanceOffboard",
     "gradlebase": "cpp",
@@ -575,9 +673,16 @@
   },
   {
     "name": "RamseteController",
-    "description": "An example robot demonstrating the use of RamseteController.",
+    "description": "Follow a pre-generated trajectory with a differential drive using RamseteController.",
     "tags": [
-      "RamseteController"
+      "Basic Robot",
+      "Differential Drive",
+      "Ramsete",
+      "PID",
+      "Odometry",
+      "Path Following",
+      "Trajectory",
+      "XboxController"
     ],
     "foldername": "RamseteController",
     "gradlebase": "cpp",
@@ -587,24 +692,45 @@
     "name": "RomiReference",
     "description": "An example command-based robot program that can be used with the Romi reference robot design.",
     "tags": [
-      "Drivetrain",
-      "Romi"
+      "Romi",
+      "Command-based",
+      "Differential Drive",
+      "Digital Input",
+      "Joystick"
     ],
     "foldername": "RomiReference",
     "gradlebase": "cppromi",
-    "commandversion": 2
+    "commandversion": 2,
+    "extravendordeps": [
+      "romi"
+    ]
+  },
+  {
+    "name": "XRP Reference",
+    "description": "An example command-based robot program that can be used with the XRP reference robot design.",
+    "tags": [
+      "XRP",
+      "Command-based",
+      "Differential Drive",
+      "Digital Input",
+      "Joystick"
+    ],
+    "foldername": "XRPReference",
+    "gradlebase": "cppxrp",
+    "commandversion": 2,
+    "extravendordeps": [
+      "xrp"
+    ]
   },
   {
     "name": "StateSpaceFlywheel",
-    "description": "An example state-space controller for a flywheel.",
+    "description": "Control a flywheel using a state-space model (based on values from CAD), with a Kalman Filter and LQR.",
     "tags": [
-      "StateSpaceFlywheel",
+      "Basic Robot",
       "Flywheel",
-      "State Space",
-      "Model",
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "State-Space",
+      "LQR",
+      "Encoder",
       "Joystick"
     ],
     "foldername": "StateSpaceFlywheel",
@@ -613,16 +739,14 @@
   },
   {
     "name": "StateSpaceFlywheelSysId",
-    "description": "An example state-space controller demonstrating the use of FRC Characterization's System Identification for controlling a flywheel.",
+    "description": "Control a flywheel using a state-space model (based on values from SysId), with a Kalman Filter and LQR.",
     "tags": [
-      "StateSpaceFlywheelSysId",
-      "FRC Characterization",
+      "Basic Robot",
       "Flywheel",
-      "Characterization",
-      "State space",
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "SysId",
+      "State-Space",
+      "LQR",
+      "Encoder",
       "Joystick"
     ],
     "foldername": "StateSpaceFlywheelSysId",
@@ -631,13 +755,13 @@
   },
   {
     "name": "StateSpaceElevator",
-    "description": "An example state-space controller for controlling an elevator.",
+    "description": "Control an elevator using a state-space model (based on values from CAD), with a Kalman Filter and LQR.",
     "tags": [
+      "Basic Robot",
       "Elevator",
-      "State Space",
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "State-Space",
+      "LQR",
+      "Encoder",
       "Joystick"
     ],
     "foldername": "StateSpaceElevator",
@@ -646,13 +770,13 @@
   },
   {
     "name": "StateSpaceArm",
-    "description": "An example state-space controller for controlling an arm.",
+    "description": "Control an arm using a state-space model (based on values from CAD), with a Kalman Filter and LQR.",
     "tags": [
+      "Basic Robot",
       "Arm",
-      "State space",
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "State-Space",
+      "LQR",
+      "Encoder",
       "Joystick"
     ],
     "foldername": "StateSpaceArm",
@@ -661,30 +785,44 @@
   },
   {
     "name": "ElevatorSimulation",
-    "description": "Demonstrates the use of physics simulation with a simple elevator.",
+    "description": "Simulate an elevator.",
     "tags": [
+      "Basic Robot",
       "Elevator",
-      "State space",
-      "Digital",
-      "Sensors",
+      "State-Space",
       "Simulation",
-      "Physics",
-      "Mechanism2d"
+      "Mechanism2d",
+      "Profiled PID"
     ],
     "foldername": "ElevatorSimulation",
     "gradlebase": "cpp",
     "commandversion": 2
   },
   {
-    "name": "DifferentialDrivePoseEstimator",
-    "description": "Demonstrates the use of the DifferentialDrivePoseEstimator as a replacement for differential drive odometry.",
+    "name": "Elevator Exponential Profile Simulation",
+    "description": "Simulate an elevator.",
     "tags": [
-      "Drivetrain",
-      "State space",
+      "Basic Robot",
+      "Elevator",
+      "State-Space",
+      "Simulation",
+      "Mechanism2d",
+      "Profiled PID"
+    ],
+    "foldername": "ElevatorExponentialSimulation",
+    "gradlebase": "cpp",
+    "commandversion": 2
+  },
+  {
+    "name": "DifferentialDrivePoseEstimator",
+    "description": "Combine differential-drive odometry with vision data using DifferentialDrivePoseEstimator.",
+    "tags": [
+      "Differential Drive",
+      "State-Space",
+      "Pose Estimator",
       "Vision",
-      "Filter",
-      "Odometry",
-      "Pose"
+      "PID",
+      "XboxController"
     ],
     "foldername": "DifferentialDrivePoseEstimator",
     "gradlebase": "cpp",
@@ -692,14 +830,14 @@
   },
   {
     "name": "MecanumDrivePoseEstimator",
-    "description": "Demonstrates the use of the MecanumDrivePoseEstimator as a replacement for mecanum odometry.",
+    "description": "Combine mecanum-drive odometry with vision data using MecanumDrivePoseEstimator.",
     "tags": [
-      "Drivetrain",
-      "State space",
+      "Mecanum Drive",
+      "State-Space",
+      "Pose Estimator",
       "Vision",
-      "Filter",
-      "Odometry",
-      "Pose"
+      "PID",
+      "XboxController"
     ],
     "foldername": "MecanumDrivePoseEstimator",
     "gradlebase": "cpp",
@@ -707,14 +845,12 @@
   },
   {
     "name": "ArmSimulation",
-    "description": "Demonstrates the use of physics simulation with a simple single-jointed arm.",
+    "description": "Simulate a single-jointed arm.",
     "tags": [
+      "Basic Robot",
       "Arm",
-      "State space",
-      "Digital",
-      "Sensors",
+      "State-Space",
       "Simulation",
-      "Physics",
       "Mechanism2d",
       "Preferences"
     ],
@@ -724,26 +860,28 @@
   },
   {
     "name": "UnitTesting",
-    "description": "Demonstrates basic unit testing for a robot project.",
+    "description": "Test a robot project with basic unit tests in simulation.",
     "tags": [
-      "Testing"
+      "Intake",
+      "Pneumatics"
     ],
     "foldername": "UnitTest",
     "gradlebase": "cpp",
-    "commandversion": 2
+    "commandversion": 2,
+    "hasunittests": true
   },
   {
     "name": "SimpleDifferentialDriveSimulation",
-    "description": "An example of a minimal drivetrain simulation project without the command-based library.",
+    "description": "Simulate a differential drivetrain and follow trajectories with RamseteController (non-command-based).",
     "tags": [
       "Differential Drive",
-      "State space",
-      "Digital",
-      "Sensors",
-      "Simulation",
-      "Physics",
-      "Drivetrain",
-      "Field2d"
+      "State-Space",
+      "Ramsete",
+      "Path Following",
+      "Trajectory",
+      "Encoder",
+      "XboxController",
+      "Simulation"
     ],
     "foldername": "SimpleDifferentialDriveSimulation",
     "gradlebase": "cpp",
@@ -751,16 +889,13 @@
   },
   {
     "name": "StateSpaceDriveSimulation",
-    "description": "Demonstrates the use of physics simulation with a differential drivetrain and the Field2d class.",
+    "description": "Simulate a differential drivetrain and follow trajectories with RamseteCommand (command-based).",
     "tags": [
+      "Command-based",
       "Differential Drive",
-      "State space",
-      "Digital",
-      "Sensors",
-      "Simulation",
-      "Physics",
-      "Drivetrain",
-      "Field2d"
+      "State-Space",
+      "XboxController",
+      "Simulation"
     ],
     "foldername": "StateSpaceDifferentialDriveSimulation",
     "gradlebase": "cpp",
@@ -768,18 +903,29 @@
   },
   {
     "name": "SwerveDrivePoseEstimator",
-    "description": "Demonstrates the use of the SwerveDrivePoseEstimator as a replacement for mecanum drive odometry.",
+    "description": "Combine swerve-drive odometry with vision data using SwerveDrivePoseEstimator.",
     "tags": [
-      "Drivetrain",
-      "State space",
+      "Swerve Drive",
+      "State-Space",
+      "Pose Estimator",
       "Vision",
-      "Filter",
-      "Odometry",
-      "State",
-      "Swerve"
+      "PID",
+      "XboxController"
     ],
     "foldername": "SwerveDrivePoseEstimator",
     "gradlebase": "cpp",
     "commandversion": 2
+  },
+  {
+    "name": "Flywheel BangBangController",
+    "description": "A sample program to demonstrate the use of a BangBangController with a flywheel to control RPM",
+    "tags": [
+      "Flywheel",
+      "Simulation",
+      "Joystick"
+    ],
+    "foldername": "FlywheelBangBangController",
+    "gradlebase": "cpp",
+    "commandversion": 2
   }
 ]
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/commandbased/include/commands/ExampleCommand.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/commandbased/include/commands/ExampleCommand.h
index 8ecd864..9426538 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/commandbased/include/commands/ExampleCommand.h
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/commandbased/include/commands/ExampleCommand.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#include <frc2/command/CommandBase.h>
+#include <frc2/command/Command.h>
 #include <frc2/command/CommandHelper.h>
 
 #include "subsystems/ExampleSubsystem.h"
@@ -12,12 +12,12 @@
 /**
  * An example command that uses an example subsystem.
  *
- * <p>Note that this extends CommandHelper, rather extending CommandBase
+ * <p>Note that this extends CommandHelper, rather extending Command
  * directly; this is crucially important, or else the decorator functions in
  * Command will *not* work!
  */
 class ExampleCommand
-    : public frc2::CommandHelper<frc2::CommandBase, ExampleCommand> {
+    : public frc2::CommandHelper<frc2::Command, ExampleCommand> {
  public:
   /**
    * Creates a new ExampleCommand.
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/templates.json b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/templates.json
index 67e4438..5109361 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/templates.json
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/templates.json
@@ -1,7 +1,7 @@
 [
   {
     "name": "Command Robot",
-    "description": "Command style",
+    "description": "Command-based, with explanatory comments and example code.",
     "tags": [
       "Command"
     ],
@@ -11,9 +11,10 @@
   },
   {
     "name": "Command Robot Skeleton (Advanced)",
-    "description": "Skeleton (stub) code for Command Robot",
+    "description": "Skeleton (stub) code for Command-based, without explanatory comments and example code.",
     "tags": [
-      "Command", "Skeleton"
+      "Command",
+      "Skeleton"
     ],
     "foldername": "commandbasedskeleton",
     "gradlebase": "cpp",
@@ -21,7 +22,7 @@
   },
   {
     "name": "Timed Robot",
-    "description": "Timed style",
+    "description": "Timed style, with explanatory comments and example code.",
     "tags": [
       "Timed"
     ],
@@ -31,19 +32,42 @@
   },
   {
     "name": "Timed Skeleton (Advanced)",
-    "description": "Skeleton (stub) code for TimedRobot",
+    "description": "Skeleton (stub) code for TimedRobot, without explanatory comments and example code.",
     "tags": [
-      "Timed", "Skeleton"
+      "Timed",
+      "Skeleton"
     ],
     "foldername": "timedskeleton",
     "gradlebase": "cpp",
     "commandversion": 2
   },
   {
-    "name": "RobotBase Skeleton (Advanced)",
-    "description": "Skeleton (stub) code for RobotBase",
+    "name": "Timeslice Robot",
+    "description": "Timeslice style",
     "tags": [
-      "RobotBase", "Skeleton"
+      "Timeslice"
+    ],
+    "foldername": "timeslice",
+    "gradlebase": "cpp",
+    "commandversion": 2
+  },
+  {
+    "name": "Timeslice Skeleton (Advanced)",
+    "description": "Skeleton (stub) code for TimesliceRobot",
+    "tags": [
+      "Timeslice",
+      "Skeleton"
+    ],
+    "foldername": "timesliceskeleton",
+    "gradlebase": "cpp",
+    "commandversion": 2
+  },
+  {
+    "name": "RobotBase Skeleton (Advanced)",
+    "description": "Skeleton (stub) code for RobotBase - Not recommended for competition use",
+    "tags": [
+      "RobotBase",
+      "Skeleton"
     ],
     "foldername": "robotbaseskeleton",
     "gradlebase": "cpp",
@@ -53,20 +77,56 @@
     "name": "Romi - Command Robot",
     "description": "Romi - Command style",
     "tags": [
-      "Command", "Romi"
+      "Command",
+      "Romi"
     ],
     "foldername": "commandbased",
     "gradlebase": "cppromi",
-    "commandversion": 2
+    "commandversion": 2,
+    "extravendordeps": [
+      "romi"
+    ]
   },
   {
     "name": "Romi - Timed Robot",
     "description": "Romi - Timed style",
     "tags": [
-      "Timed", "Romi"
+      "Timed",
+      "Romi"
     ],
     "foldername": "timed",
     "gradlebase": "cppromi",
-    "commandversion": 2
+    "commandversion": 2,
+    "extravendordeps": [
+      "romi"
+    ]
+  },
+  {
+    "name": "XRP - Command Robot",
+    "description": "XRP - Command style",
+    "tags": [
+      "Command",
+      "XRP"
+    ],
+    "foldername": "commandbased",
+    "gradlebase": "cppxrp",
+    "commandversion": 2,
+    "extravendordeps": [
+      "xrp"
+    ]
+  },
+  {
+    "name": "XRP - Timed Robot",
+    "description": "XRP - Timed style",
+    "tags": [
+      "Timed",
+      "XRP"
+    ],
+    "foldername": "timed",
+    "gradlebase": "cppxrp",
+    "commandversion": 2,
+    "extravendordeps": [
+      "xrp"
+    ]
   }
 ]
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timed/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timed/cpp/Robot.cpp
index 2fcc80b..f74262b 100644
--- a/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timed/cpp/Robot.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timed/cpp/Robot.cpp
@@ -5,7 +5,6 @@
 #include "Robot.h"
 
 #include <fmt/core.h>
-
 #include <frc/smartdashboard/SmartDashboard.h>
 
 void Robot::RobotInit() {
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timeslice/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timeslice/cpp/Robot.cpp
new file mode 100644
index 0000000..548457b
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timeslice/cpp/Robot.cpp
@@ -0,0 +1,92 @@
+// 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 "Robot.h"
+
+#include <fmt/core.h>
+#include <frc/livewindow/LiveWindow.h>
+#include <frc/smartdashboard/SmartDashboard.h>
+
+// Run robot periodic() functions for 5 ms, and run controllers every 10 ms
+Robot::Robot() : frc::TimesliceRobot{5_ms, 10_ms} {
+  // LiveWindow causes drastic overruns in robot periodic functions that will
+  // interfere with controllers
+  frc::LiveWindow::DisableAllTelemetry();
+
+  // Runs for 2 ms after robot periodic functions
+  Schedule([=] {}, 2_ms);
+
+  // Runs for 2 ms after first controller function
+  Schedule([=] {}, 2_ms);
+
+  // Total usage:
+  // 5 ms (robot) + 2 ms (controller 1) + 2 ms (controller 2) = 9 ms
+  // 9 ms / 10 ms -> 90% allocated
+}
+
+void Robot::RobotInit() {
+  m_chooser.SetDefaultOption(kAutoNameDefault, kAutoNameDefault);
+  m_chooser.AddOption(kAutoNameCustom, kAutoNameCustom);
+  frc::SmartDashboard::PutData("Auto Modes", &m_chooser);
+}
+
+/**
+ * This function is called every robot packet, no matter the mode. Use
+ * this for items like diagnostics that you want ran during disabled,
+ * autonomous, teleoperated and test.
+ *
+ * <p> This runs after the mode specific periodic functions, but before
+ * LiveWindow and SmartDashboard integrated updating.
+ */
+void Robot::RobotPeriodic() {}
+
+/**
+ * This autonomous (along with the chooser code above) shows how to select
+ * between different autonomous modes using the dashboard. The sendable chooser
+ * code works with the Java SmartDashboard. If you prefer the LabVIEW Dashboard,
+ * remove all of the chooser code and uncomment the GetString line to get the
+ * auto name from the text box below the Gyro.
+ *
+ * You can add additional auto modes by adding additional comparisons to the
+ * if-else structure below with additional strings. If using the SendableChooser
+ * make sure to add them to the chooser code above as well.
+ */
+void Robot::AutonomousInit() {
+  m_autoSelected = m_chooser.GetSelected();
+  // m_autoSelected = SmartDashboard::GetString("Auto Selector",
+  //     kAutoNameDefault);
+  fmt::print("Auto selected: {}\n", m_autoSelected);
+
+  if (m_autoSelected == kAutoNameCustom) {
+    // Custom Auto goes here
+  } else {
+    // Default Auto goes here
+  }
+}
+
+void Robot::AutonomousPeriodic() {
+  if (m_autoSelected == kAutoNameCustom) {
+    // Custom Auto goes here
+  } else {
+    // Default Auto goes here
+  }
+}
+
+void Robot::TeleopInit() {}
+
+void Robot::TeleopPeriodic() {}
+
+void Robot::DisabledInit() {}
+
+void Robot::DisabledPeriodic() {}
+
+void Robot::TestInit() {}
+
+void Robot::TestPeriodic() {}
+
+#ifndef RUNNING_FRC_TESTS
+int main() {
+  return frc::StartRobot<Robot>();
+}
+#endif
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timeslice/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timeslice/include/Robot.h
new file mode 100644
index 0000000..aaafdf7
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timeslice/include/Robot.h
@@ -0,0 +1,32 @@
+// 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 <string>
+
+#include <frc/TimesliceRobot.h>
+#include <frc/smartdashboard/SendableChooser.h>
+
+class Robot : public frc::TimesliceRobot {
+ public:
+  Robot();
+
+  void RobotInit() override;
+  void RobotPeriodic() override;
+  void AutonomousInit() override;
+  void AutonomousPeriodic() override;
+  void TeleopInit() override;
+  void TeleopPeriodic() override;
+  void DisabledInit() override;
+  void DisabledPeriodic() override;
+  void TestInit() override;
+  void TestPeriodic() override;
+
+ private:
+  frc::SendableChooser<std::string> m_chooser;
+  const std::string kAutoNameDefault = "Default";
+  const std::string kAutoNameCustom = "My Auto";
+  std::string m_autoSelected;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timesliceskeleton/cpp/Robot.cpp b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timesliceskeleton/cpp/Robot.cpp
new file mode 100644
index 0000000..10f98dd
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timesliceskeleton/cpp/Robot.cpp
@@ -0,0 +1,45 @@
+// 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 "Robot.h"
+
+#include <frc/livewindow/LiveWindow.h>
+
+// Run robot periodic() functions for 5 ms, and run controllers every 10 ms
+Robot::Robot() : frc::TimesliceRobot{5_ms, 10_ms} {
+  // LiveWindow causes drastic overruns in robot periodic functions that will
+  // interfere with controllers
+  frc::LiveWindow::DisableAllTelemetry();
+
+  // Runs for 2 ms after robot periodic functions
+  Schedule([=] {}, 2_ms);
+
+  // Runs for 2 ms after first controller function
+  Schedule([=] {}, 2_ms);
+
+  // Total usage:
+  // 5 ms (robot) + 2 ms (controller 1) + 2 ms (controller 2) = 9 ms
+  // 9 ms / 10 ms -> 90% allocated
+}
+
+void Robot::RobotInit() {}
+void Robot::RobotPeriodic() {}
+
+void Robot::AutonomousInit() {}
+void Robot::AutonomousPeriodic() {}
+
+void Robot::TeleopInit() {}
+void Robot::TeleopPeriodic() {}
+
+void Robot::DisabledInit() {}
+void Robot::DisabledPeriodic() {}
+
+void Robot::TestInit() {}
+void Robot::TestPeriodic() {}
+
+#ifndef RUNNING_FRC_TESTS
+int main() {
+  return frc::StartRobot<Robot>();
+}
+#endif
diff --git a/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timesliceskeleton/include/Robot.h b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timesliceskeleton/include/Robot.h
new file mode 100644
index 0000000..6659bf9
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/main/cpp/templates/timesliceskeleton/include/Robot.h
@@ -0,0 +1,27 @@
+// 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 <frc/TimesliceRobot.h>
+
+class Robot : public frc::TimesliceRobot {
+ public:
+  Robot();
+
+  void RobotInit() override;
+  void RobotPeriodic() override;
+
+  void AutonomousInit() override;
+  void AutonomousPeriodic() override;
+
+  void TeleopInit() override;
+  void TeleopPeriodic() override;
+
+  void DisabledInit() override;
+  void DisabledPeriodic() override;
+
+  void TestInit() override;
+  void TestPeriodic() override;
+};
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/RainbowTest.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/RainbowTest.cpp
index 553a2bb..ca06df9 100644
--- a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/RainbowTest.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/RainbowTest.cpp
@@ -2,15 +2,14 @@
 // 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 <fmt/format.h>
-#include <gtest/gtest.h>
-
 #include <array>
 
+#include <fmt/format.h>
 #include <frc/AddressableLED.h>
 #include <frc/simulation/AddressableLEDSim.h>
 #include <frc/util/Color.h>
 #include <frc/util/Color8Bit.h>
+#include <gtest/gtest.h>
 #include <hal/AddressableLEDTypes.h>
 
 #include "Robot.h"
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/main.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/main.cpp
index 285c1d5..38d5cfa 100644
--- a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/main.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/main.cpp
@@ -2,10 +2,9 @@
 // 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 <gtest/gtest.h>
 #include <hal/HALBase.h>
 
-#include "gtest/gtest.h"
-
 /**
  * Runs all unit tests.
  */
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ArmSimulation/cpp/ArmSimulationTest.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ArmSimulation/cpp/ArmSimulationTest.cpp
new file mode 100644
index 0000000..af2c857
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ArmSimulation/cpp/ArmSimulationTest.cpp
@@ -0,0 +1,151 @@
+// 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 <string>
+#include <thread>
+
+#include <frc/Preferences.h>
+#include <frc/simulation/DriverStationSim.h>
+#include <frc/simulation/JoystickSim.h>
+#include <frc/simulation/PWMSim.h>
+#include <frc/simulation/SimHooks.h>
+#include <gtest/gtest.h>
+#include <hal/simulation/MockHooks.h>
+#include <units/length.h>
+#include <units/time.h>
+
+#include "Constants.h"
+#include "Robot.h"
+
+class ArmSimulationTest : public testing::TestWithParam<units::degree_t> {
+  Robot m_robot;
+  std::optional<std::thread> m_thread;
+
+ protected:
+  frc::sim::PWMSim m_motorSim{kMotorPort};
+  frc::sim::EncoderSim m_encoderSim =
+      frc::sim::EncoderSim::CreateForChannel(kEncoderAChannel);
+  frc::sim::JoystickSim m_joystickSim{kJoystickPort};
+
+ public:
+  void SetUp() override {
+    frc::sim::PauseTiming();
+
+    m_thread = std::thread([&] { m_robot.StartCompetition(); });
+    frc::sim::StepTiming(0.0_ms);  // Wait for Notifiers
+  }
+
+  void TearDown() override {
+    m_robot.EndCompetition();
+    m_thread->join();
+
+    m_encoderSim.ResetData();
+    m_motorSim.ResetData();
+    frc::sim::DriverStationSim::ResetData();
+    frc::Preferences::RemoveAll();
+  }
+};
+
+TEST_P(ArmSimulationTest, Teleop) {
+  EXPECT_TRUE(frc::Preferences::ContainsKey(kArmPositionKey));
+  EXPECT_TRUE(frc::Preferences::ContainsKey(kArmPKey));
+  EXPECT_DOUBLE_EQ(kDefaultArmSetpoint.value(),
+                   frc::Preferences::GetDouble(kArmPositionKey, NAN));
+
+  frc::Preferences::SetDouble(kArmPositionKey, GetParam().value());
+  units::degree_t setpoint = GetParam();
+  // teleop init
+  {
+    frc::sim::DriverStationSim::SetAutonomous(false);
+    frc::sim::DriverStationSim::SetEnabled(true);
+    frc::sim::DriverStationSim::NotifyNewData();
+
+    EXPECT_TRUE(m_motorSim.GetInitialized());
+    EXPECT_TRUE(m_encoderSim.GetInitialized());
+  }
+
+  {
+    frc::sim::StepTiming(3_s);
+
+    // Ensure elevator is still at 0.
+    EXPECT_NEAR(kMinAngle.value(), m_encoderSim.GetDistance(), 2.0);
+  }
+
+  {
+    // Press button to reach setpoint
+    m_joystickSim.SetTrigger(true);
+    m_joystickSim.NotifyNewData();
+
+    frc::sim::StepTiming(1.5_s);
+
+    EXPECT_NEAR(setpoint.value(),
+                units::radian_t(m_encoderSim.GetDistance())
+                    .convert<units::degree>()
+                    .value(),
+                2.0);
+
+    // see setpoint is held.
+    frc::sim::StepTiming(0.5_s);
+
+    EXPECT_NEAR(setpoint.value(),
+                units::radian_t(m_encoderSim.GetDistance())
+                    .convert<units::degree>()
+                    .value(),
+                2.0);
+  }
+
+  {
+    // Unpress the button to go back down
+    m_joystickSim.SetTrigger(false);
+    m_joystickSim.NotifyNewData();
+
+    frc::sim::StepTiming(3_s);
+
+    EXPECT_NEAR(kMinAngle.value(), m_encoderSim.GetDistance(), 2.0);
+  }
+
+  {
+    // Press button to go back up
+    m_joystickSim.SetTrigger(true);
+    m_joystickSim.NotifyNewData();
+
+    // advance 75 timesteps
+    frc::sim::StepTiming(1.5_s);
+
+    EXPECT_NEAR(setpoint.value(),
+                units::radian_t(m_encoderSim.GetDistance())
+                    .convert<units::degree>()
+                    .value(),
+                2.0);
+
+    // advance 25 timesteps to see setpoint is held.
+    frc::sim::StepTiming(0.5_s);
+
+    EXPECT_NEAR(setpoint.value(),
+                units::radian_t(m_encoderSim.GetDistance())
+                    .convert<units::degree>()
+                    .value(),
+                2.0);
+  }
+
+  {
+    // Disable
+    frc::sim::DriverStationSim::SetAutonomous(false);
+    frc::sim::DriverStationSim::SetEnabled(false);
+    frc::sim::DriverStationSim::NotifyNewData();
+
+    frc::sim::StepTiming(3_s);
+
+    ASSERT_NEAR(0.0, m_motorSim.GetSpeed(), 0.05);
+    EXPECT_NEAR(kMinAngle.value(), m_encoderSim.GetDistance(), 2.0);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ArmSimulationTests, ArmSimulationTest,
+    testing::Values(kDefaultArmSetpoint, 25.0_deg, 50.0_deg),
+    [](const testing::TestParamInfo<units::degree_t>& info) {
+      return testing::PrintToString(info.param.value())
+          .append(std::string(info.param.abbreviation()));
+    });
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ArmSimulation/cpp/main.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ArmSimulation/cpp/main.cpp
new file mode 100644
index 0000000..38d5cfa
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ArmSimulation/cpp/main.cpp
@@ -0,0 +1,16 @@
+// 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 <gtest/gtest.h>
+#include <hal/HALBase.h>
+
+/**
+ * Runs all unit tests.
+ */
+int main(int argc, char** argv) {
+  HAL_Initialize(500, 0);
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  return ret;
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/DigitalCommunication/cpp/DigitalCommunicationTest.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/DigitalCommunication/cpp/DigitalCommunicationTest.cpp
new file mode 100644
index 0000000..8979894
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/DigitalCommunication/cpp/DigitalCommunicationTest.cpp
@@ -0,0 +1,156 @@
+// 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 <string>
+#include <thread>
+
+#include <frc/simulation/DIOSim.h>
+#include <frc/simulation/DriverStationSim.h>
+#include <frc/simulation/SimHooks.h>
+#include <gtest/gtest.h>
+#include <units/time.h>
+
+#include "Robot.h"
+
+template <typename T>
+class DigitalCommunicationTest : public testing::TestWithParam<T> {
+ public:
+  frc::sim::DIOSim m_allianceOutput{Robot::kAlliancePort};
+  frc::sim::DIOSim m_enabledOutput{Robot::kEnabledPort};
+  frc::sim::DIOSim m_autonomousOutput{Robot::kAutonomousPort};
+  frc::sim::DIOSim m_alertOutput{Robot::kAlertPort};
+  Robot m_robot;
+  std::optional<std::thread> m_thread;
+
+  void SetUp() override {
+    frc::sim::PauseTiming();
+    frc::sim::DriverStationSim::ResetData();
+
+    m_thread = std::thread([&] { m_robot.StartCompetition(); });
+    frc::sim::StepTiming(0.0_ms);
+    // SimHooks.stepTiming(0.0); // Wait for Notifiers
+  }
+
+  void TearDown() override {
+    m_robot.EndCompetition();
+    m_thread->join();
+    m_allianceOutput.ResetData();
+    m_enabledOutput.ResetData();
+    m_autonomousOutput.ResetData();
+    m_alertOutput.ResetData();
+  }
+};
+
+class AllianceTest : public DigitalCommunicationTest<HAL_AllianceStationID> {};
+
+TEST_P(AllianceTest, Alliance) {
+  auto alliance = GetParam();
+  frc::sim::DriverStationSim::SetAllianceStationId(alliance);
+  frc::sim::DriverStationSim::NotifyNewData();
+
+  EXPECT_TRUE(m_allianceOutput.GetInitialized());
+  EXPECT_FALSE(m_allianceOutput.GetIsInput());
+
+  frc::sim::StepTiming(20_ms);
+
+  bool isRed = false;
+  switch (alliance) {
+    case HAL_AllianceStationID_kBlue1:
+    case HAL_AllianceStationID_kBlue2:
+    case HAL_AllianceStationID_kBlue3:
+    case HAL_AllianceStationID_kUnknown:
+      isRed = false;
+      break;
+    case HAL_AllianceStationID_kRed1:
+    case HAL_AllianceStationID_kRed2:
+    case HAL_AllianceStationID_kRed3:
+      isRed = true;
+      break;
+  }
+  EXPECT_EQ(isRed, m_allianceOutput.GetValue());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    DigitalCommunicationTests, AllianceTest,
+    testing::Values<HAL_AllianceStationID>(
+        HAL_AllianceStationID_kRed1, HAL_AllianceStationID_kRed2,
+        HAL_AllianceStationID_kRed3, HAL_AllianceStationID_kBlue1,
+        HAL_AllianceStationID_kBlue2, HAL_AllianceStationID_kBlue3,
+        HAL_AllianceStationID_kUnknown),
+    [](const testing::TestParamInfo<AllianceTest::ParamType>& info) {
+      switch (info.param) {
+        case HAL_AllianceStationID_kBlue1:
+          return std::string{"Blue1"};
+        case HAL_AllianceStationID_kBlue2:
+          return std::string{"Blue2"};
+        case HAL_AllianceStationID_kBlue3:
+          return std::string{"Blue3"};
+        case HAL_AllianceStationID_kRed1:
+          return std::string{"Red1"};
+        case HAL_AllianceStationID_kRed2:
+          return std::string{"Red2"};
+        case HAL_AllianceStationID_kRed3:
+          return std::string{"Red3"};
+        case HAL_AllianceStationID_kUnknown:
+          return std::string{"Unknown"};
+      }
+      return std::string{"Error"};
+    });
+
+class EnabledTest : public DigitalCommunicationTest<bool> {};
+
+TEST_P(EnabledTest, Enabled) {
+  auto enabled = GetParam();
+  frc::sim::DriverStationSim::SetEnabled(enabled);
+  frc::sim::DriverStationSim::NotifyNewData();
+
+  EXPECT_TRUE(m_enabledOutput.GetInitialized());
+  EXPECT_FALSE(m_enabledOutput.GetIsInput());
+
+  frc::sim::StepTiming(20_ms);
+
+  EXPECT_EQ(enabled, m_enabledOutput.GetValue());
+}
+
+INSTANTIATE_TEST_SUITE_P(DigitalCommunicationTests, EnabledTest,
+                         testing::Bool(), testing::PrintToStringParamName());
+
+class AutonomousTest : public DigitalCommunicationTest<bool> {};
+
+TEST_P(AutonomousTest, Autonomous) {
+  auto autonomous = GetParam();
+  frc::sim::DriverStationSim::SetAutonomous(autonomous);
+  frc::sim::DriverStationSim::NotifyNewData();
+
+  EXPECT_TRUE(m_autonomousOutput.GetInitialized());
+  EXPECT_FALSE(m_autonomousOutput.GetIsInput());
+
+  frc::sim::StepTiming(20_ms);
+
+  EXPECT_EQ(autonomous, m_autonomousOutput.GetValue());
+}
+
+INSTANTIATE_TEST_SUITE_P(DigitalCommunicationTests, AutonomousTest,
+                         testing::Bool(), testing::PrintToStringParamName());
+
+class AlertTest : public DigitalCommunicationTest<double> {};
+
+TEST_P(AlertTest, Alert) {
+  auto matchTime = GetParam();
+  frc::sim::DriverStationSim::SetMatchTime(matchTime);
+  frc::sim::DriverStationSim::NotifyNewData();
+
+  EXPECT_TRUE(m_alertOutput.GetInitialized());
+  EXPECT_FALSE(m_alertOutput.GetIsInput());
+
+  frc::sim::StepTiming(20_ms);
+
+  EXPECT_EQ(matchTime <= 30 && matchTime >= 25, m_alertOutput.GetValue());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    DigitalCommunicationTests, AlertTest, testing::Values(45.0, 27.0, 23.0),
+    [](const testing::TestParamInfo<double>& info) {
+      return testing::PrintToString(info.param).append("_s");
+    });
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/DigitalCommunication/cpp/main.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/DigitalCommunication/cpp/main.cpp
new file mode 100644
index 0000000..38d5cfa
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/DigitalCommunication/cpp/main.cpp
@@ -0,0 +1,16 @@
+// 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 <gtest/gtest.h>
+#include <hal/HALBase.h>
+
+/**
+ * Runs all unit tests.
+ */
+int main(int argc, char** argv) {
+  HAL_Initialize(500, 0);
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  return ret;
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ElevatorSimulation/cpp/ElevatorSimulationTest.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ElevatorSimulation/cpp/ElevatorSimulationTest.cpp
new file mode 100644
index 0000000..99876dd
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ElevatorSimulation/cpp/ElevatorSimulationTest.cpp
@@ -0,0 +1,125 @@
+// 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 <string>
+#include <thread>
+
+#include <frc/simulation/DriverStationSim.h>
+#include <frc/simulation/JoystickSim.h>
+#include <frc/simulation/PWMSim.h>
+#include <frc/simulation/SimHooks.h>
+#include <gtest/gtest.h>
+#include <hal/simulation/MockHooks.h>
+#include <units/length.h>
+#include <units/mass.h>
+#include <units/time.h>
+
+#include "Constants.h"
+#include "Robot.h"
+
+using namespace Constants;
+
+class ElevatorSimulationTest : public testing::Test {
+  Robot m_robot;
+  std::optional<std::thread> m_thread;
+
+ protected:
+  frc::sim::PWMSim m_motorSim{Constants::kMotorPort};
+  frc::sim::EncoderSim m_encoderSim =
+      frc::sim::EncoderSim::CreateForChannel(Constants::kEncoderAChannel);
+  frc::sim::JoystickSim m_joystickSim{Constants::kJoystickPort};
+
+ public:
+  void SetUp() override {
+    frc::sim::PauseTiming();
+
+    m_thread = std::thread([&] { m_robot.StartCompetition(); });
+    frc::sim::StepTiming(0.0_ms);  // Wait for Notifiers
+  }
+
+  void TearDown() override {
+    m_robot.EndCompetition();
+    m_thread->join();
+
+    m_encoderSim.ResetData();
+    m_motorSim.ResetData();
+    frc::sim::DriverStationSim::ResetData();
+  }
+};
+
+TEST_F(ElevatorSimulationTest, Teleop) {
+  // teleop init
+  {
+    frc::sim::DriverStationSim::SetAutonomous(false);
+    frc::sim::DriverStationSim::SetEnabled(true);
+    frc::sim::DriverStationSim::NotifyNewData();
+
+    EXPECT_TRUE(m_motorSim.GetInitialized());
+    EXPECT_TRUE(m_encoderSim.GetInitialized());
+  }
+
+  {
+    // advance 50 timesteps
+    frc::sim::StepTiming(1_s);
+
+    // Ensure elevator is still at 0.
+    EXPECT_NEAR(0.0, m_encoderSim.GetDistance(), 0.05);
+  }
+
+  {
+    // Press button to reach setpoint
+    m_joystickSim.SetTrigger(true);
+    m_joystickSim.NotifyNewData();
+
+    // advance 75 timesteps
+    frc::sim::StepTiming(1.5_s);
+
+    EXPECT_NEAR(kSetpoint.value(), m_encoderSim.GetDistance(), 0.05);
+
+    // advance 25 timesteps to see setpoint is held.
+    frc::sim::StepTiming(0.5_s);
+
+    EXPECT_NEAR(kSetpoint.value(), m_encoderSim.GetDistance(), 0.05);
+  }
+
+  {
+    // Unpress the button to go back down
+    m_joystickSim.SetTrigger(false);
+    m_joystickSim.NotifyNewData();
+
+    // advance 75 timesteps
+    frc::sim::StepTiming(1.5_s);
+
+    EXPECT_NEAR(0.0, m_encoderSim.GetDistance(), 0.05);
+  }
+
+  {
+    // Press button to go back up
+    m_joystickSim.SetTrigger(true);
+    m_joystickSim.NotifyNewData();
+
+    // advance 75 timesteps
+    frc::sim::StepTiming(1.5_s);
+
+    EXPECT_NEAR(kSetpoint.value(), m_encoderSim.GetDistance(), 0.05);
+
+    // advance 25 timesteps to see setpoint is held.
+    frc::sim::StepTiming(0.5_s);
+
+    EXPECT_NEAR(kSetpoint.value(), m_encoderSim.GetDistance(), 0.05);
+  }
+
+  {
+    // Disable
+    frc::sim::DriverStationSim::SetAutonomous(false);
+    frc::sim::DriverStationSim::SetEnabled(false);
+    frc::sim::DriverStationSim::NotifyNewData();
+
+    // advance 75 timesteps
+    frc::sim::StepTiming(1.5_s);
+
+    ASSERT_NEAR(0.0, m_motorSim.GetSpeed(), 0.05);
+    ASSERT_NEAR(0.0, m_encoderSim.GetDistance(), 0.05);
+  }
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ElevatorSimulation/cpp/main.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ElevatorSimulation/cpp/main.cpp
new file mode 100644
index 0000000..38d5cfa
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/ElevatorSimulation/cpp/main.cpp
@@ -0,0 +1,16 @@
+// 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 <gtest/gtest.h>
+#include <hal/HALBase.h>
+
+/**
+ * Runs all unit tests.
+ */
+int main(int argc, char** argv) {
+  HAL_Initialize(500, 0);
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  return ret;
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/I2CCommunication/cpp/I2CCommunicationTest.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/I2CCommunication/cpp/I2CCommunicationTest.cpp
new file mode 100644
index 0000000..3d84dbf
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/I2CCommunication/cpp/I2CCommunicationTest.cpp
@@ -0,0 +1,165 @@
+// 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 <string>
+#include <thread>
+
+#include <frc/simulation/DriverStationSim.h>
+#include <frc/simulation/SimHooks.h>
+#include <gtest/gtest.h>
+#include <hal/simulation/I2CData.h>
+#include <units/time.h>
+
+#include "Robot.h"
+
+static std::string gString;
+
+void callback(const char* name, void* param, const unsigned char* buffer,
+              unsigned int count) {
+  gString.assign(reinterpret_cast<const char*>(buffer),
+                 static_cast<int>(count));
+}
+
+template <typename T>
+class I2CCommunicationTest : public testing::TestWithParam<T> {
+ public:
+  Robot m_robot;
+  std::optional<std::thread> m_thread;
+  int32_t m_callback;
+  int32_t m_port;
+
+  void SetUp() override {
+    gString = std::string();
+    frc::sim::PauseTiming();
+    frc::sim::DriverStationSim::ResetData();
+    m_port = static_cast<int32_t>(Robot::kPort);
+
+    m_callback = HALSIM_RegisterI2CWriteCallback(m_port, &callback, nullptr);
+
+    m_thread = std::thread([&] { m_robot.StartCompetition(); });
+    frc::sim::StepTiming(0.0_ms);  // Wait for Notifiers
+  }
+
+  void TearDown() override {
+    m_robot.EndCompetition();
+    m_thread->join();
+
+    HALSIM_CancelI2CWriteCallback(m_port, m_callback);
+    HALSIM_ResetI2CData(m_port);
+  }
+};
+
+class AllianceTest : public I2CCommunicationTest<HAL_AllianceStationID> {};
+
+TEST_P(AllianceTest, Alliance) {
+  auto alliance = GetParam();
+  frc::sim::DriverStationSim::SetAllianceStationId(alliance);
+  frc::sim::DriverStationSim::NotifyNewData();
+
+  EXPECT_TRUE(HALSIM_GetI2CInitialized(m_port));
+
+  frc::sim::StepTiming(20_ms);
+
+  char expected = 'U';
+  switch (alliance) {
+    case HAL_AllianceStationID_kBlue1:
+    case HAL_AllianceStationID_kBlue2:
+    case HAL_AllianceStationID_kBlue3:
+      expected = 'B';
+      break;
+    case HAL_AllianceStationID_kRed1:
+    case HAL_AllianceStationID_kRed2:
+    case HAL_AllianceStationID_kRed3:
+      expected = 'R';
+      break;
+    case HAL_AllianceStationID_kUnknown:
+      expected = 'U';
+      break;
+  }
+  EXPECT_EQ(expected, gString.at(0));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    I2CCommunicationTests, AllianceTest,
+    testing::Values<HAL_AllianceStationID>(
+        HAL_AllianceStationID_kRed1, HAL_AllianceStationID_kRed2,
+        HAL_AllianceStationID_kRed3, HAL_AllianceStationID_kBlue1,
+        HAL_AllianceStationID_kBlue2, HAL_AllianceStationID_kBlue3,
+        HAL_AllianceStationID_kUnknown),
+    [](const testing::TestParamInfo<AllianceTest::ParamType>& info) {
+      switch (info.param) {
+        case HAL_AllianceStationID_kBlue1:
+          return std::string{"Blue1"};
+        case HAL_AllianceStationID_kBlue2:
+          return std::string{"Blue2"};
+        case HAL_AllianceStationID_kBlue3:
+          return std::string{"Blue3"};
+        case HAL_AllianceStationID_kRed1:
+          return std::string{"Red1"};
+        case HAL_AllianceStationID_kRed2:
+          return std::string{"Red2"};
+        case HAL_AllianceStationID_kRed3:
+          return std::string{"Red3"};
+        case HAL_AllianceStationID_kUnknown:
+          return std::string{"Unknown"};
+      }
+      return std::string{"Error"};
+    });
+
+class EnabledTest : public I2CCommunicationTest<bool> {};
+
+TEST_P(EnabledTest, Enabled) {
+  auto enabled = GetParam();
+  frc::sim::DriverStationSim::SetEnabled(enabled);
+  frc::sim::DriverStationSim::NotifyNewData();
+
+  EXPECT_TRUE(HALSIM_GetI2CInitialized(m_port));
+
+  frc::sim::StepTiming(20_ms);
+
+  char expected = enabled ? 'E' : 'D';
+  EXPECT_EQ(expected, gString.at(1));
+}
+
+INSTANTIATE_TEST_SUITE_P(I2CCommunicationTests, EnabledTest, testing::Bool(),
+                         testing::PrintToStringParamName());
+
+class AutonomousTest : public I2CCommunicationTest<bool> {};
+
+TEST_P(AutonomousTest, Autonomous) {
+  auto autonomous = GetParam();
+  frc::sim::DriverStationSim::SetAutonomous(autonomous);
+  frc::sim::DriverStationSim::NotifyNewData();
+
+  EXPECT_TRUE(HALSIM_GetI2CInitialized(m_port));
+
+  frc::sim::StepTiming(20_ms);
+
+  char expected = autonomous ? 'A' : 'T';
+  EXPECT_EQ(expected, gString.at(2));
+}
+
+INSTANTIATE_TEST_SUITE_P(I2CCommunicationTests, AutonomousTest, testing::Bool(),
+                         testing::PrintToStringParamName());
+
+class MatchTimeTest : public I2CCommunicationTest<int> {};
+
+TEST_P(MatchTimeTest, Alert) {
+  auto matchTime = GetParam();
+  frc::sim::DriverStationSim::SetMatchTime(matchTime);
+  frc::sim::DriverStationSim::NotifyNewData();
+
+  EXPECT_TRUE(HALSIM_GetI2CInitialized(m_port));
+
+  frc::sim::StepTiming(20_ms);
+
+  std::string expected = fmt::format("{:03}", matchTime);
+  EXPECT_EQ(expected, gString.substr(3));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    I2CCommunicationTests, MatchTimeTest, testing::Values(112, 45, 27, 23, 3),
+    [](const testing::TestParamInfo<int>& info) {
+      return testing::PrintToString(info.param).append("_s");
+    });
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/I2CCommunication/cpp/main.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/I2CCommunication/cpp/main.cpp
new file mode 100644
index 0000000..38d5cfa
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/I2CCommunication/cpp/main.cpp
@@ -0,0 +1,16 @@
+// 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 <gtest/gtest.h>
+#include <hal/HALBase.h>
+
+/**
+ * Runs all unit tests.
+ */
+int main(int argc, char** argv) {
+  HAL_Initialize(500, 0);
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  return ret;
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/PotentiometerPID/cpp/PotentiometerPIDTest.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/PotentiometerPID/cpp/PotentiometerPIDTest.cpp
new file mode 100644
index 0000000..9d2691c
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/PotentiometerPID/cpp/PotentiometerPIDTest.cpp
@@ -0,0 +1,167 @@
+// 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 <string>
+#include <thread>
+
+#include <frc/RobotController.h>
+#include <frc/simulation/AnalogInputSim.h>
+#include <frc/simulation/DriverStationSim.h>
+#include <frc/simulation/ElevatorSim.h>
+#include <frc/simulation/JoystickSim.h>
+#include <frc/simulation/PWMSim.h>
+#include <frc/simulation/SimHooks.h>
+#include <frc/system/plant/DCMotor.h>
+#include <gtest/gtest.h>
+#include <hal/simulation/MockHooks.h>
+#include <units/length.h>
+#include <units/mass.h>
+#include <units/time.h>
+
+#include "Robot.h"
+
+class PotentiometerPIDTest : public testing::Test {
+  frc::DCMotor m_elevatorGearbox = frc::DCMotor::Vex775Pro(4);
+  static constexpr double kElevatorGearing = 10.0;
+  static constexpr units::meter_t kElevatorDrumRadius = 2.0_in;
+  static constexpr units::kilogram_t kCarriageMass = 4.0_kg;
+
+  Robot m_robot;
+  std::optional<std::thread> m_thread;
+
+ protected:
+  frc::sim::ElevatorSim m_elevatorSim{m_elevatorGearbox,
+                                      kElevatorGearing,
+                                      kCarriageMass,
+                                      kElevatorDrumRadius,
+                                      0.0_m,
+                                      Robot::kFullHeight,
+                                      true,
+                                      0.0_m};
+  frc::sim::PWMSim m_motorSim{Robot::kMotorChannel};
+  frc::sim::AnalogInputSim m_analogSim{Robot::kPotChannel};
+  frc::sim::JoystickSim m_joystickSim{Robot::kJoystickChannel};
+  int32_t m_callback;
+  int32_t m_port;
+
+ public:
+  void SimPeriodicBefore() {
+    m_elevatorSim.SetInputVoltage(m_motorSim.GetSpeed() *
+                                  frc::RobotController::GetBatteryVoltage());
+    m_elevatorSim.Update(20_ms);
+
+    /*
+    meters = (v / 5v) * range
+    meters / range = v / 5v
+    5v * (meters / range) = v
+     */
+    m_analogSim.SetVoltage(
+        (frc::RobotController::GetVoltage5V() *
+         (m_elevatorSim.GetPosition().value() / Robot::kFullHeight))
+            .value());
+  }
+
+  static void CallSimPeriodicBefore(void* param) {
+    static_cast<PotentiometerPIDTest*>(param)->SimPeriodicBefore();
+  }
+
+  void SetUp() override {
+    frc::sim::PauseTiming();
+    frc::sim::DriverStationSim::ResetData();
+
+    m_joystickSim.SetButtonCount(12);
+
+    m_callback =
+        HALSIM_RegisterSimPeriodicBeforeCallback(CallSimPeriodicBefore, this);
+
+    m_thread = std::thread([&] { m_robot.StartCompetition(); });
+    frc::sim::StepTiming(0.0_ms);  // Wait for Notifiers
+  }
+
+  void TearDown() override {
+    m_robot.EndCompetition();
+    m_thread->join();
+
+    HALSIM_CancelSimPeriodicBeforeCallback(m_callback);
+    m_analogSim.ResetData();
+    m_motorSim.ResetData();
+  }
+};
+
+TEST_F(PotentiometerPIDTest, Teleop) {
+  // teleop init
+  {
+    frc::sim::DriverStationSim::SetAutonomous(false);
+    frc::sim::DriverStationSim::SetEnabled(true);
+    frc::sim::DriverStationSim::NotifyNewData();
+
+    EXPECT_TRUE(m_motorSim.GetInitialized());
+    EXPECT_TRUE(m_analogSim.GetInitialized());
+  }
+
+  // first setpoint
+  {
+    // advance 50 timesteps
+    frc::sim::StepTiming(1_s);
+
+    EXPECT_NEAR(Robot::kSetpoints[0].value(),
+                m_elevatorSim.GetPosition().value(), 0.1);
+  }
+
+  // second setpoint
+  {
+    // press button to advance setpoint
+    m_joystickSim.SetTrigger(true);
+    m_joystickSim.NotifyNewData();
+
+    // advance 50 timesteps
+    frc::sim::StepTiming(1_s);
+
+    EXPECT_NEAR(Robot::kSetpoints[1].value(),
+                m_elevatorSim.GetPosition().value(), 0.1);
+  }
+
+  // we need to unpress the button
+  {
+    m_joystickSim.SetTrigger(false);
+    m_joystickSim.NotifyNewData();
+
+    // advance 10 timesteps
+    frc::sim::StepTiming(0.2_s);
+  }
+
+  // third setpoint
+  {
+    // press button to advance setpoint
+    m_joystickSim.SetTrigger(true);
+    m_joystickSim.NotifyNewData();
+
+    // advance 50 timesteps
+    frc::sim::StepTiming(1_s);
+
+    EXPECT_NEAR(Robot::kSetpoints[2].value(),
+                m_elevatorSim.GetPosition().value(), 0.1);
+  }
+
+  // we need to unpress the button
+  {
+    m_joystickSim.SetTrigger(false);
+    m_joystickSim.NotifyNewData();
+
+    // advance 10 timesteps
+    frc::sim::StepTiming(0.2_s);
+  }
+
+  // rollover: first setpoint
+  {
+    // press button to advance setpoint
+    m_joystickSim.SetTrigger(true);
+    m_joystickSim.NotifyNewData();
+
+    // advance 60 timesteps
+    frc::sim::StepTiming(1.2_s);
+    EXPECT_NEAR(Robot::kSetpoints[0].value(),
+                m_elevatorSim.GetPosition().value(), 0.1);
+  }
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/PotentiometerPID/cpp/main.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/PotentiometerPID/cpp/main.cpp
new file mode 100644
index 0000000..38d5cfa
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/PotentiometerPID/cpp/main.cpp
@@ -0,0 +1,16 @@
+// 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 <gtest/gtest.h>
+#include <hal/HALBase.h>
+
+/**
+ * Runs all unit tests.
+ */
+int main(int argc, char** argv) {
+  HAL_Initialize(500, 0);
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  return ret;
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UltrasonicPID/cpp/UltrasonicPIDTest.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UltrasonicPID/cpp/UltrasonicPIDTest.cpp
new file mode 100644
index 0000000..a970a3a
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UltrasonicPID/cpp/UltrasonicPIDTest.cpp
@@ -0,0 +1,110 @@
+// 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 <string>
+#include <thread>
+
+#include <frc/RobotController.h>
+#include <frc/simulation/DifferentialDrivetrainSim.h>
+#include <frc/simulation/DriverStationSim.h>
+#include <frc/simulation/PWMSim.h>
+#include <frc/simulation/SimHooks.h>
+#include <frc/simulation/UltrasonicSim.h>
+#include <frc/system/plant/DCMotor.h>
+#include <frc/system/plant/LinearSystemId.h>
+#include <gtest/gtest.h>
+#include <hal/simulation/MockHooks.h>
+#include <units/angle.h>
+#include <units/length.h>
+#include <units/mass.h>
+#include <units/time.h>
+
+#include "Robot.h"
+
+class UltrasonicPIDTest : public testing::TestWithParam<double> {
+  frc::DCMotor m_gearbox = frc::DCMotor::Falcon500(2);
+  static constexpr auto kGearing =
+      frc::sim::DifferentialDrivetrainSim::KitbotGearing::k10p71;
+  static constexpr auto kvLinear = 1.98 * 1_V / 1_mps;
+  static constexpr auto kaLinear = 0.2 * 1_V / 1_mps_sq;
+  static constexpr auto kvVoltAngular = 1.5 * 1_V / 1_rad_per_s;
+  static constexpr auto kaAngular = 0.3 * 1_V / 1_rad_per_s_sq;
+  static constexpr auto kWheelDiameter = 0.15_m;
+  static constexpr auto kTrackwidth = 0.7_m;
+
+  Robot m_robot;
+  std::optional<std::thread> m_thread;
+
+ protected:
+  frc::sim::DifferentialDrivetrainSim m_driveSim{
+      frc::LinearSystemId::IdentifyDrivetrainSystem(
+          kvLinear, kaLinear, kvVoltAngular, kaAngular, kTrackwidth),
+      kTrackwidth, m_gearbox, kGearing, kWheelDiameter / 2.0};
+  frc::sim::PWMSim m_leftMotorSim{Robot::kLeftMotorPort};
+  frc::sim::PWMSim m_rightMotorSim{Robot::kRightMotorPort};
+  frc::sim::UltrasonicSim m_ultrasonicSim{Robot::kUltrasonicPingPort,
+                                          Robot::kUltrasonicEchoPort};
+  int32_t m_callback;
+
+  units::millimeter_t m_distance;
+
+ public:
+  void SimPeriodicBefore() {
+    m_driveSim.SetInputs(
+        m_leftMotorSim.GetSpeed() * frc::RobotController::GetBatteryVoltage(),
+        m_rightMotorSim.GetSpeed() * frc::RobotController::GetBatteryVoltage());
+    m_driveSim.Update(20_ms);
+
+    auto startingDistance = units::meter_t{GetParam()};
+    m_distance = startingDistance - m_driveSim.GetLeftPosition();
+
+    m_ultrasonicSim.SetRange(m_distance);
+  }
+
+  static void CallSimPeriodicBefore(void* param) {
+    static_cast<UltrasonicPIDTest*>(param)->SimPeriodicBefore();
+  }
+
+  void SetUp() override {
+    frc::sim::PauseTiming();
+    frc::sim::DriverStationSim::ResetData();
+
+    m_callback =
+        HALSIM_RegisterSimPeriodicBeforeCallback(CallSimPeriodicBefore, this);
+
+    m_thread = std::thread([&] { m_robot.StartCompetition(); });
+    frc::sim::StepTiming(0.0_ms);  // Wait for Notifiers
+  }
+
+  void TearDown() override {
+    m_robot.EndCompetition();
+    m_thread->join();
+
+    HALSIM_CancelSimPeriodicBeforeCallback(m_callback);
+    m_leftMotorSim.ResetData();
+    m_rightMotorSim.ResetData();
+  }
+};
+
+TEST_P(UltrasonicPIDTest, Auto) {
+  // auto init
+  {
+    frc::sim::DriverStationSim::SetAutonomous(true);
+    frc::sim::DriverStationSim::SetEnabled(true);
+    frc::sim::DriverStationSim::NotifyNewData();
+
+    EXPECT_TRUE(m_leftMotorSim.GetInitialized());
+    EXPECT_TRUE(m_rightMotorSim.GetInitialized());
+  }
+
+  {
+    // advance 100 timesteps
+    frc::sim::StepTiming(2_s);
+
+    EXPECT_NEAR(Robot::kHoldDistance.value(), m_distance.value(), 10.0);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(UltrasonicPIDTests, UltrasonicPIDTest,
+                         testing::Values(1.3, 0.5, 5.0));
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UltrasonicPID/cpp/main.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UltrasonicPID/cpp/main.cpp
new file mode 100644
index 0000000..38d5cfa
--- /dev/null
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UltrasonicPID/cpp/main.cpp
@@ -0,0 +1,16 @@
+// 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 <gtest/gtest.h>
+#include <hal/HALBase.h>
+
+/**
+ * Runs all unit tests.
+ */
+int main(int argc, char** argv) {
+  HAL_Initialize(500, 0);
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  return ret;
+}
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/main.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/main.cpp
index 285c1d5..38d5cfa 100644
--- a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/main.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/main.cpp
@@ -2,10 +2,9 @@
 // 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 <gtest/gtest.h>
 #include <hal/HALBase.h>
 
-#include "gtest/gtest.h"
-
 /**
  * Runs all unit tests.
  */
diff --git a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/subsystems/IntakeTest.cpp b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/subsystems/IntakeTest.cpp
index ae1d786..6b5650a 100644
--- a/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/subsystems/IntakeTest.cpp
+++ b/third_party/allwpilib/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/subsystems/IntakeTest.cpp
@@ -2,11 +2,10 @@
 // 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 <gtest/gtest.h>
-
 #include <frc/DoubleSolenoid.h>
 #include <frc/simulation/DoubleSolenoidSim.h>
 #include <frc/simulation/PWMSim.h>
+#include <gtest/gtest.h>
 
 #include "Constants.h"
 #include "subsystems/Intake.h"
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/AnalogLoopTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/AnalogLoopTest.cpp
index 3540095..cf3f30f 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/AnalogLoopTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/AnalogLoopTest.cpp
@@ -2,6 +2,7 @@
 // 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 <gtest/gtest.h>
 #include <units/time.h>
 
 #include "TestBench.h"
@@ -11,7 +12,6 @@
 #include "frc/AsynchronousInterrupt.h"
 #include "frc/Counter.h"
 #include "frc/Timer.h"
-#include "gtest/gtest.h"
 
 static constexpr auto kDelayTime = 10_ms;
 
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/AnalogPotentiometerTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/AnalogPotentiometerTest.cpp
index 107310f..39aee79 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/AnalogPotentiometerTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/AnalogPotentiometerTest.cpp
@@ -4,11 +4,12 @@
 
 #include "frc/AnalogPotentiometer.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
+
 #include "TestBench.h"
 #include "frc/AnalogOutput.h"
 #include "frc/RobotController.h"
 #include "frc/Timer.h"
-#include "gtest/gtest.h"
 
 static constexpr double kScale = 270.0;
 static constexpr double kAngle = 180.0;
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/BuiltInAccelerometerTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/BuiltInAccelerometerTest.cpp
index 00f5f00..9086afa 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/BuiltInAccelerometerTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/BuiltInAccelerometerTest.cpp
@@ -4,8 +4,9 @@
 
 #include "frc/BuiltInAccelerometer.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
+
 #include "frc/Timer.h"
-#include "gtest/gtest.h"
 
 static constexpr double kAccelerationTolerance = 0.1;
 
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/CounterTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/CounterTest.cpp
index 553fc96..e562d6a 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/CounterTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/CounterTest.cpp
@@ -4,6 +4,7 @@
 
 #include "frc/Counter.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <units/time.h>
 
 #include "TestBench.h"
@@ -11,7 +12,6 @@
 #include "frc/motorcontrol/Jaguar.h"
 #include "frc/motorcontrol/Talon.h"
 #include "frc/motorcontrol/Victor.h"
-#include "gtest/gtest.h"
 
 static constexpr auto kMotorDelay = 2.5_s;
 
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DIOLoopTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DIOLoopTest.cpp
index e230310..a810be9 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DIOLoopTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DIOLoopTest.cpp
@@ -6,6 +6,7 @@
 
 #include "frc/DigitalOutput.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <units/math.h>
 #include <units/time.h>
 
@@ -14,7 +15,6 @@
 #include "frc/Counter.h"
 #include "frc/SynchronousInterrupt.h"
 #include "frc/Timer.h"
-#include "gtest/gtest.h"
 
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
   EXPECT_LE(units::math::abs(val1 - val2), eps)
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DMATest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DMATest.cpp
index 39116db..30a1caf 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DMATest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DMATest.cpp
@@ -8,10 +8,11 @@
 #include <frc/Timer.h>
 #include <frc/motorcontrol/Jaguar.h>
 
+#include <gtest/gtest.h>
+
 #include "TestBench.h"
 #include "frc/DMA.h"
 #include "frc/DMASample.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DigitalGlitchFilterTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DigitalGlitchFilterTest.cpp
index d7c1a23..913f654 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DigitalGlitchFilterTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DigitalGlitchFilterTest.cpp
@@ -4,17 +4,18 @@
 
 #include "frc/DigitalGlitchFilter.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
+
 #include "frc/Counter.h"
 #include "frc/DigitalInput.h"
 #include "frc/Encoder.h"
-#include "gtest/gtest.h"
 
 /**
  * Tests that configuring inputs to be filtered succeeds.
  *
  * This test actually tests everything except that the actual FPGA
  * implementation works as intended.  We configure the FPGA and then query it to
- * make sure that the acutal configuration matches.
+ * make sure that the actual configuration matches.
  */
 TEST(DigitalGlitchFilterTest, Basic) {
   frc::DigitalInput input1{1};
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DriverStationTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DriverStationTest.cpp
index 0369717..68f8cb2 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DriverStationTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/DriverStationTest.cpp
@@ -4,13 +4,13 @@
 
 #include "frc/DriverStation.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/DriverStation.h>
 #include <units/math.h>
 #include <units/time.h>
 
 #include "TestBench.h"
 #include "frc/RobotController.h"
-#include "gtest/gtest.h"
 
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
   EXPECT_LE(units::math::abs(val1 - val2), eps)
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/FakeEncoderTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/FakeEncoderTest.cpp
index 3442b48..0d4999f 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/FakeEncoderTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/FakeEncoderTest.cpp
@@ -4,6 +4,7 @@
 
 #include "frc/Encoder.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <units/time.h>
 
 #include "TestBench.h"
@@ -11,7 +12,6 @@
 #include "frc/AnalogTrigger.h"
 #include "frc/DigitalOutput.h"
 #include "frc/Timer.h"
-#include "gtest/gtest.h"
 
 static constexpr auto kDelayTime = 1_ms;
 
@@ -62,7 +62,7 @@
 };
 
 /**
- * Test the encoder by reseting it to 0 and reading the value.
+ * Test the encoder by resetting it to 0 and reading the value.
  */
 TEST_F(FakeEncoderTest, DefaultState) {
   EXPECT_DOUBLE_EQ(0.0, m_encoder.Get()) << "The encoder did not start at 0.";
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/Main.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/Main.cpp
index 95bc5b5..70e9d83 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/Main.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/Main.cpp
@@ -2,7 +2,7 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp
index 46e2120..be7efb8 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp
@@ -4,6 +4,7 @@
 
 #include <algorithm>
 
+#include <gtest/gtest.h>
 #include <units/time.h>
 
 #include "TestBench.h"
@@ -15,7 +16,6 @@
 #include "frc/motorcontrol/Jaguar.h"
 #include "frc/motorcontrol/Talon.h"
 #include "frc/motorcontrol/Victor.h"
-#include "gtest/gtest.h"
 
 enum MotorEncoderTestType { TEST_VICTOR, TEST_JAGUAR, TEST_TALON };
 
@@ -139,7 +139,7 @@
 TEST_P(MotorEncoderTest, PositionPIDController) {
   Reset();
   double goal = 1000;
-  frc2::PIDController pidController(0.001, 0.01, 0.0);
+  frc::PIDController pidController(0.001, 0.01, 0.0);
   pidController.SetTolerance(50.0);
   pidController.SetIntegratorRange(-0.2, 0.2);
   pidController.SetSetpoint(goal);
@@ -166,7 +166,7 @@
 TEST_P(MotorEncoderTest, VelocityPIDController) {
   Reset();
 
-  frc2::PIDController pidController(1e-5, 0.0, 0.0006);
+  frc::PIDController pidController(1e-5, 0.0, 0.0006);
   pidController.SetTolerance(200.0);
   pidController.SetSetpoint(600);
 
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp
index 10ccd34..870f029 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp
@@ -2,6 +2,7 @@
 // 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 <gtest/gtest.h>
 #include <units/time.h>
 
 #include "TestBench.h"
@@ -10,7 +11,6 @@
 #include "frc/motorcontrol/Jaguar.h"
 #include "frc/motorcontrol/Talon.h"
 #include "frc/motorcontrol/Victor.h"
-#include "gtest/gtest.h"
 
 enum MotorInvertingTestType { TEST_VICTOR, TEST_JAGUAR, TEST_TALON };
 
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/NotifierTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/NotifierTest.cpp
index eaaaf7e..ca7fb84 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/NotifierTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/NotifierTest.cpp
@@ -5,9 +5,9 @@
 #include "frc/Notifier.h"  // NOLINT(build/include_order)
 
 #include <fmt/core.h>
+#include <gtest/gtest.h>
 
 #include "frc/Timer.h"
-#include "gtest/gtest.h"
 
 TEST(NotifierTest, StartPeriodicAndStop) {
   uint32_t counter = 0;
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PCMTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PCMTest.cpp
index ea271a5..4d14603 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PCMTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PCMTest.cpp
@@ -2,6 +2,8 @@
 // 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 <gtest/gtest.h>
+
 #include "TestBench.h"
 #include "frc/AnalogInput.h"
 #include "frc/DigitalInput.h"
@@ -10,7 +12,6 @@
 #include "frc/PneumaticsControlModule.h"
 #include "frc/Solenoid.h"
 #include "frc/Timer.h"
-#include "gtest/gtest.h"
 
 /* The PCM switches the compressor up to a couple seconds after the pressure
         switch changes. */
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp
index 4697553..bd73739 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp
@@ -4,13 +4,13 @@
 
 #include "frc/PowerDistribution.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <hal/Ports.h>
 #include <units/time.h>
 
 #include "TestBench.h"
 #include "frc/Timer.h"
 #include "frc/motorcontrol/Talon.h"
-#include "gtest/gtest.h"
 
 static constexpr auto kMotorTime = 0.25_s;
 
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PreferencesTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PreferencesTest.cpp
index 78d9c43..ef58bb4 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PreferencesTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PreferencesTest.cpp
@@ -7,13 +7,13 @@
 #include <cstdio>
 #include <fstream>
 
+#include <gtest/gtest.h>
 #include <networktables/MultiSubscriber.h>
 #include <networktables/NetworkTableInstance.h>
 #include <ntcore.h>
 #include <units/time.h>
 
 #include "frc/Timer.h"
-#include "gtest/gtest.h"
 
 static const char* kFileName = "networktables.json";
 static constexpr auto kSaveTime = 1.2_s;
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PriorityTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PriorityTest.cpp
index 3085bed..85a42a4 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PriorityTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/PriorityTest.cpp
@@ -4,11 +4,11 @@
 
 #include <thread>
 
+#include <gtest/gtest.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
 
 #include "frc/Threads.h"
-#include "gtest/gtest.h"
 
 TEST(PriorityTest, SetThreadPriority) {
   bool exited = false;
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/RelayTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/RelayTest.cpp
index cf3cfdf..7e5c8d2 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/RelayTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/RelayTest.cpp
@@ -4,12 +4,12 @@
 
 #include "frc/Relay.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <units/time.h>
 
 #include "TestBench.h"
 #include "frc/DigitalInput.h"
 #include "frc/Timer.h"
-#include "gtest/gtest.h"
 
 static constexpr auto kDelayTime = 10_ms;
 
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TestEnvironment.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TestEnvironment.cpp
index 59a3a58..91f27b9 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TestEnvironment.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TestEnvironment.cpp
@@ -6,11 +6,11 @@
 #include <thread>
 
 #include <fmt/core.h>
+#include <gtest/gtest.h>
 #include <hal/HAL.h>
 
 #include "frc/DriverStation.h"
 #include "frc/livewindow/LiveWindow.h"
-#include "gtest/gtest.h"
 #include "mockds/MockDS.h"
 
 using namespace std::chrono_literals;
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TiltPanCameraTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TiltPanCameraTest.cpp
index 42e52fc..dfa2664 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TiltPanCameraTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TiltPanCameraTest.cpp
@@ -4,6 +4,7 @@
 
 #include <cmath>
 
+#include <gtest/gtest.h>
 #include <units/time.h>
 
 #include "TestBench.h"
@@ -11,7 +12,6 @@
 #include "frc/AnalogGyro.h"
 #include "frc/Servo.h"
 #include "frc/Timer.h"
-#include "gtest/gtest.h"
 
 static constexpr double kTiltSetpoint0 = 0.22;
 static constexpr double kTiltSetpoint45 = 0.45;
diff --git a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TimerTest.cpp b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TimerTest.cpp
index 99ec9f5..e48425a 100644
--- a/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TimerTest.cpp
+++ b/third_party/allwpilib/wpilibcIntegrationTests/src/main/native/cpp/TimerTest.cpp
@@ -4,10 +4,9 @@
 
 #include "frc/Timer.h"  // NOLINT(build/include_order)
 
+#include <gtest/gtest.h>
 #include <units/math.h>
 
-#include "gtest/gtest.h"
-
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
   EXPECT_LE(units::math::abs(val1 - val2), eps)
 
diff --git a/third_party/allwpilib/wpilibj/CMakeLists.txt b/third_party/allwpilib/wpilibj/CMakeLists.txt
index cff7f22..5924f4d 100644
--- a/third_party/allwpilib/wpilibj/CMakeLists.txt
+++ b/third_party/allwpilib/wpilibj/CMakeLists.txt
@@ -32,3 +32,37 @@
 
     install(FILES wpilibj-config.cmake DESTINATION ${wpilibj_config_dir})
 endif()
+
+if (WITH_JAVA_SOURCE)
+    find_package(Java REQUIRED)
+    include(UseJava)
+    file(GLOB WPILIBJ_SOURCES src/main/java/edu/wpi/first/wpilibj/*.java)
+    file(GLOB WPILIBJ_COUNTER_SOURCES src/main/java/edu/wpi/first/wpilibj/counter/*.java)
+    file(GLOB WPILIBJ_DRIVE_SOURCES src/main/java/edu/wpi/first/wpilibj/drive/*.java)
+    file(GLOB WPILIBJ_EVENT_SOURCES src/main/java/edu/wpi/first/wpilibj/event/*.java)
+    file(GLOB WPILIBJ_INTERFACES_SOURCES src/main/java/edu/wpi/first/wpilibj/interfaces/*.java)
+    file(GLOB WPILIBJ_MOTORCONTROL_SOURCES src/main/java/edu/wpi/first/wpilibj/motorcontrol*.java)
+    file(GLOB WPILIBJ_SHUFFLEBOARD_SOURCES src/main/java/edu/wpi/first/wpilibj/shuffleboard*.java)
+    file(GLOB WPILIBJ_SIMULATION_SOURCES src/main/java/edu/wpi/first/wpilibj/simulation*.java)
+    file(GLOB WPILIBJ_SMARTDASHBOARD_SOURCES src/main/java/edu/wpi/first/wpilibj/*.java)
+    file(GLOB WPILIBJ_UTIL_SOURCES src/main/java/edu/wpi/first/wpilibj/*.java ${CMAKE_CURRENT_BINARY_DIR}/WPILibVersion.java)
+    add_jar(wpilibj_src_jar
+    RESOURCES NAMESPACE "edu/wpi/first/wpilibj" ${WPILIBJ_SOURCES}
+    NAMESPACE "edu/wpi/first/wpilibj/counter" ${WPILIBJ_COUNTER_SOURCES}
+    NAMESPACE "edu/wpi/first/wpilibj/drive" ${WPILIBJ_DRIVE_SOURCES}
+    NAMESPACE "edu/wpi/first/wpilibj/event" ${WPILIBJ_EVENT_SOURCES}
+    NAMESPACE "edu/wpi/first/wpilibj/interfaces" ${WPILIBJ_INTERFACES_SOURCES}
+    NAMESPACE "edu/wpi/first/wpilibj/internal" src/main/java/edu/wpi/first/wpilibj/internal/DriverStationModeThread.java
+    NAMESPACE "edu/wpi/first/wpilibj/livewindow" src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java
+    NAMESPACE "edu/wpi/first/wpilibj/motorcontrol" ${WPILIBJ_MOTORCONTROL_SOURCES}
+    NAMESPACE "edu/wpi/first/wpilibj/shuffleboard" ${WPILIBJ_SHUFFLEBOARD_SOURCES}
+    NAMESPACE "edu/wpi/first/wpilibj/simulation" ${WPILIBJ_SIMULATION_SOURCES}
+    NAMESPACE "edu/wpi/first/wpilibj/smartdashboard" ${WPILIBJ_SMARTDASHBOARD_SOURCES}
+    NAMESPACE "edu/wpi/first/wpilibj/util" ${WPILIBJ_UTIL_SOURCES}
+    OUTPUT_NAME wpilibj-sources)
+
+    get_property(WPILIBJ_SRC_JAR_FILE TARGET wpilibj_src_jar PROPERTY JAR_FILE)
+    install(FILES ${WPILIBJ_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+    set_property(TARGET wpilibj_src_jar PROPERTY FOLDER "java")
+endif()
diff --git a/third_party/allwpilib/wpilibj/build.gradle b/third_party/allwpilib/wpilibj/build.gradle
index 77eb349..b31630e 100644
--- a/third_party/allwpilib/wpilibj/build.gradle
+++ b/third_party/allwpilib/wpilibj/build.gradle
@@ -70,13 +70,6 @@
     implementation project(':cscore')
     implementation project(':cameraserver')
     testImplementation 'org.mockito:mockito-core:4.1.0'
-    devImplementation project(':hal')
-    devImplementation project(':wpiutil')
-    devImplementation project(':wpinet')
-    devImplementation project(':wpimath')
-    devImplementation project(':ntcore')
-    devImplementation project(':cscore')
-    devImplementation project(':cameraserver')
     devImplementation sourceSets.main.output
 }
 
@@ -151,10 +144,12 @@
                                 test.dependsOn it.tasks.install
                                 test.systemProperty 'java.library.path', filePath
                                 test.environment 'LD_LIBRARY_PATH', filePath
+                                test.environment 'DYLD_LIBRARY_PATH', filePath
                                 test.workingDir filePath
                                 run.dependsOn it.tasks.install
                                 run.systemProperty 'java.library.path', filePath
                                 run.environment 'LD_LIBRARY_PATH', filePath
+                                run.environment 'DYLD_LIBRARY_PATH', filePath
                                 run.workingDir filePath
 
                                 found = true
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16448_IMU.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16448_IMU.java
index a005b73..2eddf64 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16448_IMU.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16448_IMU.java
@@ -18,8 +18,8 @@
 import edu.wpi.first.hal.SimBoolean;
 import edu.wpi.first.hal.SimDevice;
 import edu.wpi.first.hal.SimDouble;
-import edu.wpi.first.networktables.NTSendable;
-import edu.wpi.first.networktables.NTSendableBuilder;
+import edu.wpi.first.util.sendable.Sendable;
+import edu.wpi.first.util.sendable.SendableBuilder;
 
 // CHECKSTYLE.OFF: TypeName
 // CHECKSTYLE.OFF: MemberName
@@ -50,7 +50,7 @@
   "PMD.EmptyIfStmt",
   "PMD.EmptyStatementNotInLoop"
 })
-public class ADIS16448_IMU implements AutoCloseable, NTSendable {
+public class ADIS16448_IMU implements AutoCloseable, Sendable {
   /** ADIS16448 Register Map Declaration */
   private static final int FLASH_CNT = 0x00; // Flash memory write count
 
@@ -455,7 +455,7 @@
     m_spi.setAutoTransmitData(new byte[] {GLOB_CMD}, 27);
     // Configure auto stall time
     m_spi.configureAutoStall(100, 1000, 255);
-    // Kick off auto SPI (Note: Device configration impossible after auto SPI is
+    // Kick off auto SPI (Note: Device configuration impossible after auto SPI is
     // activated)
     m_spi.startAutoTrigger(m_auto_interrupt, true, false);
 
@@ -656,6 +656,10 @@
         m_spi = null;
       }
     }
+    if (m_simDevice != null) {
+      m_simDevice.close();
+      m_simDevice = null;
+    }
     System.out.println("Finished cleaning up after the IMU driver.");
   }
 
@@ -1157,7 +1161,7 @@
   }
 
   @Override
-  public void initSendable(NTSendableBuilder builder) {
+  public void initSendable(SendableBuilder builder) {
     builder.setSmartDashboardType("Gyro");
     builder.addDoubleProperty("Value", this::getAngle, null);
   }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16470_IMU.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16470_IMU.java
index f287cab..2fad3f3 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16470_IMU.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16470_IMU.java
@@ -17,8 +17,8 @@
 import edu.wpi.first.hal.SimBoolean;
 import edu.wpi.first.hal.SimDevice;
 import edu.wpi.first.hal.SimDouble;
-import edu.wpi.first.networktables.NTSendable;
-import edu.wpi.first.networktables.NTSendableBuilder;
+import edu.wpi.first.util.sendable.Sendable;
+import edu.wpi.first.util.sendable.SendableBuilder;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
@@ -51,7 +51,7 @@
   "PMD.EmptyIfStmt",
   "PMD.EmptyStatementNotInLoop"
 })
-public class ADIS16470_IMU implements AutoCloseable, NTSendable {
+public class ADIS16470_IMU implements AutoCloseable, Sendable {
   /* ADIS16470 Register Map Declaration */
   private static final int FLASH_CNT = 0x00; // Flash memory write count
   private static final int DIAG_STAT = 0x02; // Diagnostic and operational status
@@ -511,7 +511,7 @@
     }
     // Configure auto stall time
     m_spi.configureAutoStall(5, 1000, 1);
-    // Kick off auto SPI (Note: Device configration impossible after auto SPI is
+    // Kick off auto SPI (Note: Device configuration impossible after auto SPI is
     // activated)
     // DR High = Data good (data capture should be triggered on the rising edge)
     m_spi.startAutoTrigger(m_auto_interrupt, true, false);
@@ -681,6 +681,10 @@
         m_spi = null;
       }
     }
+    if (m_simDevice != null) {
+      m_simDevice.close();
+      m_simDevice = null;
+    }
     System.out.println("Finished cleaning up after the IMU driver.");
   }
 
@@ -1042,7 +1046,7 @@
   }
 
   @Override
-  public void initSendable(NTSendableBuilder builder) {
+  public void initSendable(SendableBuilder builder) {
     builder.setSmartDashboardType("Gyro");
     builder.addDoubleProperty("Value", this::getAngle, null);
   }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java
index fddc01d..7ceff95 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java
@@ -15,7 +15,6 @@
 import edu.wpi.first.networktables.NTSendable;
 import edu.wpi.first.networktables.NTSendableBuilder;
 import edu.wpi.first.util.sendable.SendableRegistry;
-import edu.wpi.first.wpilibj.interfaces.Accelerometer;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
@@ -27,7 +26,7 @@
  * WPILib Known Issues</a> page for details.
  */
 @SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"})
-public class ADXL345_I2C implements Accelerometer, NTSendable, AutoCloseable {
+public class ADXL345_I2C implements NTSendable, AutoCloseable {
   private static final byte kAddress = 0x1D;
   private static final byte kPowerCtlRegister = 0x2D;
   private static final byte kDataFormatRegister = 0x31;
@@ -44,6 +43,13 @@
   private static final byte kDataFormat_FullRes = 0x08;
   private static final byte kDataFormat_Justify = 0x04;
 
+  public enum Range {
+    k2G,
+    k4G,
+    k8G,
+    k16G
+  }
+
   public enum Axes {
     kX((byte) 0x00),
     kY((byte) 0x02),
@@ -137,10 +143,14 @@
     }
   }
 
-  @Override
+  /**
+   * Set the measuring range of the accelerometer.
+   *
+   * @param range The maximum acceleration, positive or negative, that the accelerometer will
+   *     measure.
+   */
   public void setRange(Range range) {
     final byte value;
-
     switch (range) {
       case k2G:
         value = 0;
@@ -155,7 +165,7 @@
         value = 3;
         break;
       default:
-        throw new IllegalArgumentException(range + " unsupported range type");
+        throw new IllegalArgumentException("Missing case for range type " + range);
     }
 
     // Specify the data format to read
@@ -166,17 +176,29 @@
     }
   }
 
-  @Override
+  /**
+   * Returns the acceleration along the X axis in g-forces.
+   *
+   * @return The acceleration along the X axis in g-forces.
+   */
   public double getX() {
     return getAcceleration(Axes.kX);
   }
 
-  @Override
+  /**
+   * Returns the acceleration along the Y axis in g-forces.
+   *
+   * @return The acceleration along the Y axis in g-forces.
+   */
   public double getY() {
     return getAcceleration(Axes.kY);
   }
 
-  @Override
+  /**
+   * Returns the acceleration along the Z axis in g-forces.
+   *
+   * @return The acceleration along the Z axis in g-forces.
+   */
   public double getZ() {
     return getAcceleration(Axes.kZ);
   }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java
index 4806979..7291443 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java
@@ -15,13 +15,12 @@
 import edu.wpi.first.networktables.NTSendable;
 import edu.wpi.first.networktables.NTSendableBuilder;
 import edu.wpi.first.util.sendable.SendableRegistry;
-import edu.wpi.first.wpilibj.interfaces.Accelerometer;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
 /** ADXL345 SPI Accelerometer. */
 @SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"})
-public class ADXL345_SPI implements Accelerometer, NTSendable, AutoCloseable {
+public class ADXL345_SPI implements NTSendable, AutoCloseable {
   private static final int kPowerCtlRegister = 0x2D;
   private static final int kDataFormatRegister = 0x31;
   private static final int kDataRegister = 0x32;
@@ -41,6 +40,13 @@
   private static final int kDataFormat_FullRes = 0x08;
   private static final int kDataFormat_Justify = 0x04;
 
+  public enum Range {
+    k2G,
+    k4G,
+    k8G,
+    k16G
+  }
+
   public enum Axes {
     kX((byte) 0x00),
     kY((byte) 0x02),
@@ -133,10 +139,14 @@
     HAL.report(tResourceType.kResourceType_ADXL345, tInstances.kADXL345_SPI);
   }
 
-  @Override
+  /**
+   * Set the measuring range of the accelerometer.
+   *
+   * @param range The maximum acceleration, positive or negative, that the accelerometer will
+   *     measure.
+   */
   public void setRange(Range range) {
     final byte value;
-
     switch (range) {
       case k2G:
         value = 0;
@@ -151,7 +161,7 @@
         value = 3;
         break;
       default:
-        throw new IllegalArgumentException(range + " unsupported");
+        throw new IllegalArgumentException("Missing case for range type " + range);
     }
 
     // Specify the data format to read
@@ -163,17 +173,29 @@
     }
   }
 
-  @Override
+  /**
+   * Returns the acceleration along the X axis in g-forces.
+   *
+   * @return The acceleration along the X axis in g-forces.
+   */
   public double getX() {
     return getAcceleration(Axes.kX);
   }
 
-  @Override
+  /**
+   * Returns the acceleration along the Y axis in g-forces.
+   *
+   * @return The acceleration along the Y axis in g-forces.
+   */
   public double getY() {
     return getAcceleration(Axes.kY);
   }
 
-  @Override
+  /**
+   * Returns the acceleration along the Z axis in g-forces.
+   *
+   * @return The acceleration along the Z axis in g-forces.
+   */
   public double getZ() {
     return getAcceleration(Axes.kZ);
   }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL362.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL362.java
index afd76d6..b094be3 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL362.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL362.java
@@ -14,7 +14,6 @@
 import edu.wpi.first.networktables.NTSendable;
 import edu.wpi.first.networktables.NTSendableBuilder;
 import edu.wpi.first.util.sendable.SendableRegistry;
-import edu.wpi.first.wpilibj.interfaces.Accelerometer;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
@@ -23,7 +22,7 @@
  *
  * <p>This class allows access to an Analog Devices ADXL362 3-axis accelerometer.
  */
-public class ADXL362 implements Accelerometer, NTSendable, AutoCloseable {
+public class ADXL362 implements NTSendable, AutoCloseable {
   private static final byte kRegWrite = 0x0A;
   private static final byte kRegRead = 0x0B;
 
@@ -44,6 +43,12 @@
 
   private static final byte kPowerCtl_Measure = 0x02;
 
+  public enum Range {
+    k2G,
+    k4G,
+    k8G
+  }
+
   public enum Axes {
     kX((byte) 0x00),
     kY((byte) 0x02),
@@ -153,7 +158,12 @@
     }
   }
 
-  @Override
+  /**
+   * Set the measuring range of the accelerometer.
+   *
+   * @param range The maximum acceleration, positive or negative, that the accelerometer will
+   *     measure.
+   */
   public void setRange(Range range) {
     if (m_spi == null) {
       return;
@@ -170,12 +180,11 @@
         m_gsPerLSB = 0.002;
         break;
       case k8G:
-      case k16G: // 16G not supported; treat as 8G
         value = kFilterCtl_Range8G;
         m_gsPerLSB = 0.004;
         break;
       default:
-        throw new IllegalArgumentException(range + " unsupported");
+        throw new IllegalArgumentException("Missing case for range type " + range);
     }
 
     // Specify the data format to read
@@ -188,17 +197,29 @@
     }
   }
 
-  @Override
+  /**
+   * Returns the acceleration along the X axis in g-forces.
+   *
+   * @return The acceleration along the X axis in g-forces.
+   */
   public double getX() {
     return getAcceleration(Axes.kX);
   }
 
-  @Override
+  /**
+   * Returns the acceleration along the Y axis in g-forces.
+   *
+   * @return The acceleration along the Y axis in g-forces.
+   */
   public double getY() {
     return getAcceleration(Axes.kY);
   }
 
-  @Override
+  /**
+   * Returns the acceleration along the Z axis in g-forces.
+   *
+   * @return The acceleration along the Z axis in g-forces.
+   */
   public double getZ() {
     return getAcceleration(Axes.kZ);
   }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java
index 145e6e2..cbe34c8 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java
@@ -9,10 +9,10 @@
 import edu.wpi.first.hal.SimBoolean;
 import edu.wpi.first.hal.SimDevice;
 import edu.wpi.first.hal.SimDouble;
+import edu.wpi.first.math.geometry.Rotation2d;
 import edu.wpi.first.util.sendable.Sendable;
 import edu.wpi.first.util.sendable.SendableBuilder;
 import edu.wpi.first.util.sendable.SendableRegistry;
-import edu.wpi.first.wpilibj.interfaces.Gyro;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
@@ -27,7 +27,7 @@
  * an ADXRS Gyro is supported.
  */
 @SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"})
-public class ADXRS450_Gyro implements Gyro, Sendable {
+public class ADXRS450_Gyro implements Sendable, AutoCloseable {
   private static final double kSamplePeriod = 0.0005;
   private static final double kCalibrationSampleTime = 5.0;
   private static final double kDegreePerSecondPerLSB = 0.0125;
@@ -105,7 +105,14 @@
     return m_spi != null;
   }
 
-  @Override
+  /**
+   * Calibrate the gyro by running for a number of samples and computing the center value. Then use
+   * the center value as the Accumulator center value for subsequent measurements.
+   *
+   * <p>It's important to make sure that the robot is not moving while the centering calculations
+   * are in progress, this is typically done when the robot is first turned on while it's sitting at
+   * rest before the competition starts.
+   */
   public void calibrate() {
     if (m_spi == null) {
       return;
@@ -160,7 +167,12 @@
     return (buf.getInt(0) >> 5) & 0xffff;
   }
 
-  @Override
+  /**
+   * Reset the gyro.
+   *
+   * <p>Resets the gyro to a heading of zero. This can be used if there is significant drift in the
+   * gyro, and it needs to be recalibrated after it has been running.
+   */
   public void reset() {
     if (m_simAngle != null) {
       m_simAngle.reset();
@@ -184,7 +196,20 @@
     }
   }
 
-  @Override
+  /**
+   * Return the heading of the robot in degrees.
+   *
+   * <p>The angle is continuous, that is it will continue from 360 to 361 degrees. This allows
+   * algorithms that wouldn't want to see a discontinuity in the gyro output as it sweeps past from
+   * 360 to 0 on the second time around.
+   *
+   * <p>The angle is expected to increase as the gyro turns clockwise when looked at from the top.
+   * It needs to follow the NED axis convention.
+   *
+   * <p>This heading is based on integration of the returned rate from the gyro.
+   *
+   * @return the current heading of the robot in degrees.
+   */
   public double getAngle() {
     if (m_simAngle != null) {
       return m_simAngle.get();
@@ -195,7 +220,16 @@
     return m_spi.getAccumulatorIntegratedValue() * kDegreePerSecondPerLSB;
   }
 
-  @Override
+  /**
+   * Return the rate of rotation of the gyro.
+   *
+   * <p>The rate is based on the most recent reading of the gyro analog value
+   *
+   * <p>The rate is expected to be positive as the gyro turns clockwise when looked at from the top.
+   * It needs to follow the NED axis convention.
+   *
+   * @return the current rate in degrees per second
+   */
   public double getRate() {
     if (m_simRate != null) {
       return m_simRate.get();
@@ -206,6 +240,24 @@
     return m_spi.getAccumulatorLastValue() * kDegreePerSecondPerLSB;
   }
 
+  /**
+   * Return the heading of the robot as a {@link edu.wpi.first.math.geometry.Rotation2d}.
+   *
+   * <p>The angle is continuous, that is it will continue from 360 to 361 degrees. This allows
+   * algorithms that wouldn't want to see a discontinuity in the gyro output as it sweeps past from
+   * 360 to 0 on the second time around.
+   *
+   * <p>The angle is expected to increase as the gyro turns counterclockwise when looked at from the
+   * top. It needs to follow the NWU axis convention.
+   *
+   * <p>This heading is based on integration of the returned rate from the gyro.
+   *
+   * @return the current heading of the robot as a {@link edu.wpi.first.math.geometry.Rotation2d}.
+   */
+  public Rotation2d getRotation2d() {
+    return Rotation2d.fromDegrees(-getAngle());
+  }
+
   @Override
   public void initSendable(SendableBuilder builder) {
     builder.setSmartDashboardType("Gyro");
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLED.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLED.java
index 2b1f54b..61a2fa6 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLED.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLED.java
@@ -10,7 +10,9 @@
 import edu.wpi.first.hal.PWMJNI;
 
 /**
- * A class for driving addressable LEDs, such as WS2812s and NeoPixels.
+ * A class for driving addressable LEDs, such as WS2812Bs and NeoPixels.
+ *
+ * <p>By default, the timing supports WS2812B LEDs, but is configurable using setBitTiming()
  *
  * <p>Only 1 LED driver is currently supported by the roboRIO.
  */
@@ -67,32 +69,32 @@
   /**
    * Sets the bit timing.
    *
-   * <p>By default, the driver is set up to drive WS2812s, so nothing needs to be set for those.
+   * <p>By default, the driver is set up to drive WS2812Bs, so nothing needs to be set for those.
    *
-   * @param lowTime0NanoSeconds low time for 0 bit
-   * @param highTime0NanoSeconds high time for 0 bit
-   * @param lowTime1NanoSeconds low time for 1 bit
-   * @param highTime1NanoSeconds high time for 1 bit
+   * @param highTime0NanoSeconds high time for 0 bit (default 400ns)
+   * @param lowTime0NanoSeconds low time for 0 bit (default 900ns)
+   * @param highTime1NanoSeconds high time for 1 bit (default 900ns)
+   * @param lowTime1NanoSeconds low time for 1 bit (default 600ns)
    */
   public void setBitTiming(
-      int lowTime0NanoSeconds,
       int highTime0NanoSeconds,
-      int lowTime1NanoSeconds,
-      int highTime1NanoSeconds) {
+      int lowTime0NanoSeconds,
+      int highTime1NanoSeconds,
+      int lowTime1NanoSeconds) {
     AddressableLEDJNI.setBitTiming(
         m_handle,
-        lowTime0NanoSeconds,
         highTime0NanoSeconds,
-        lowTime1NanoSeconds,
-        highTime1NanoSeconds);
+        lowTime0NanoSeconds,
+        highTime1NanoSeconds,
+        lowTime1NanoSeconds);
   }
 
   /**
    * Sets the sync time.
    *
-   * <p>The sync time is the time to hold output so LEDs enable. Default set for WS2812.
+   * <p>The sync time is the time to hold output so LEDs enable. Default set for WS2812B.
    *
-   * @param syncTimeMicroSeconds the sync time
+   * @param syncTimeMicroSeconds the sync time (default 280us)
    */
   public void setSyncTime(int syncTimeMicroSeconds) {
     AddressableLEDJNI.setSyncTime(m_handle, syncTimeMicroSeconds);
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java
index 06c7e46..f5be4ad 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java
@@ -9,10 +9,10 @@
 import edu.wpi.first.hal.AnalogGyroJNI;
 import edu.wpi.first.hal.FRCNetComm.tResourceType;
 import edu.wpi.first.hal.HAL;
+import edu.wpi.first.math.geometry.Rotation2d;
 import edu.wpi.first.util.sendable.Sendable;
 import edu.wpi.first.util.sendable.SendableBuilder;
 import edu.wpi.first.util.sendable.SendableRegistry;
-import edu.wpi.first.wpilibj.interfaces.Gyro;
 
 /**
  * Use a rate gyro to return the robots heading relative to a starting position. The Gyro class
@@ -23,7 +23,7 @@
  *
  * <p>This class is for gyro sensors that connect to an analog input.
  */
-public class AnalogGyro implements Gyro, Sendable {
+public class AnalogGyro implements Sendable, AutoCloseable {
   private static final double kDefaultVoltsPerDegreePerSecond = 0.007;
   protected AnalogInput m_analog;
   private boolean m_channelAllocated;
@@ -42,12 +42,37 @@
     SendableRegistry.addLW(this, "AnalogGyro", m_analog.getChannel());
   }
 
-  @Override
+  /**
+   * Calibrate the gyro by running for a number of samples and computing the center value. Then use
+   * the center value as the Accumulator center value for subsequent measurements.
+   *
+   * <p>It's important to make sure that the robot is not moving while the centering calculations
+   * are in progress, this is typically done when the robot is first turned on while it's sitting at
+   * rest before the competition starts.
+   */
   public void calibrate() {
     AnalogGyroJNI.calibrateAnalogGyro(m_gyroHandle);
   }
 
   /**
+   * Return the heading of the robot as a {@link edu.wpi.first.math.geometry.Rotation2d}.
+   *
+   * <p>The angle is continuous, that is it will continue from 360 to 361 degrees. This allows
+   * algorithms that wouldn't want to see a discontinuity in the gyro output as it sweeps past from
+   * 360 to 0 on the second time around.
+   *
+   * <p>The angle is expected to increase as the gyro turns counterclockwise when looked at from the
+   * top. It needs to follow the NWU axis convention.
+   *
+   * <p>This heading is based on integration of the returned rate from the gyro.
+   *
+   * @return the current heading of the robot as a {@link edu.wpi.first.math.geometry.Rotation2d}.
+   */
+  public Rotation2d getRotation2d() {
+    return Rotation2d.fromDegrees(-getAngle());
+  }
+
+  /**
    * Gyro constructor using the channel number.
    *
    * @param channel The analog channel the gyro is connected to. Gyros can only be used on on-board
@@ -109,7 +134,12 @@
     reset();
   }
 
-  @Override
+  /**
+   * Reset the gyro.
+   *
+   * <p>Resets the gyro to a heading of zero. This can be used if there is significant drift in the
+   * gyro, and it needs to be recalibrated after it has been running.
+   */
   public void reset() {
     AnalogGyroJNI.resetAnalogGyro(m_gyroHandle);
   }
@@ -125,7 +155,20 @@
     AnalogGyroJNI.freeAnalogGyro(m_gyroHandle);
   }
 
-  @Override
+  /**
+   * Return the heading of the robot in degrees.
+   *
+   * <p>The angle is continuous, that is it will continue from 360 to 361 degrees. This allows
+   * algorithms that wouldn't want to see a discontinuity in the gyro output as it sweeps past from
+   * 360 to 0 on the second time around.
+   *
+   * <p>The angle is expected to increase as the gyro turns clockwise when looked at from the top.
+   * It needs to follow the NED axis convention.
+   *
+   * <p>This heading is based on integration of the returned rate from the gyro.
+   *
+   * @return the current heading of the robot in degrees.
+   */
   public double getAngle() {
     if (m_analog == null) {
       return 0.0;
@@ -134,7 +177,16 @@
     }
   }
 
-  @Override
+  /**
+   * Return the rate of rotation of the gyro.
+   *
+   * <p>The rate is based on the most recent reading of the gyro analog value
+   *
+   * <p>The rate is expected to be positive as the gyro turns clockwise when looked at from the top.
+   * It needs to follow the NED axis convention.
+   *
+   * @return the current rate in degrees per second
+   */
   public double getRate() {
     if (m_analog == null) {
       return 0.0;
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java
index 0f37edc..c80179b 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java
@@ -26,7 +26,7 @@
  * limits.
  *
  * <p>The RisingPulse and FallingPulse outputs detect an instantaneous transition from above the
- * upper limit to below the lower limit, and vise versa. These pulses represent a rollover condition
+ * upper limit to below the lower limit, and vice versa. These pulses represent a rollover condition
  * of a sensor and can be routed to an up / down counter or to interrupts. Because the outputs
  * generate a pulse, they cannot be read directly. To help ensure that a rollover condition is not
  * missed, there is an average rejection filter available that operates on the upper 8 bits of a 12
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometer.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometer.java
index 76c282a..1a6b103 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometer.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometer.java
@@ -10,14 +10,19 @@
 import edu.wpi.first.util.sendable.Sendable;
 import edu.wpi.first.util.sendable.SendableBuilder;
 import edu.wpi.first.util.sendable.SendableRegistry;
-import edu.wpi.first.wpilibj.interfaces.Accelerometer;
 
 /**
  * Built-in accelerometer.
  *
  * <p>This class allows access to the roboRIO's internal accelerometer.
  */
-public class BuiltInAccelerometer implements Accelerometer, Sendable, AutoCloseable {
+public class BuiltInAccelerometer implements Sendable, AutoCloseable {
+  public enum Range {
+    k2G,
+    k4G,
+    k8G
+  }
+
   /**
    * Constructor.
    *
@@ -39,7 +44,12 @@
     SendableRegistry.remove(this);
   }
 
-  @Override
+  /**
+   * Set the measuring range of the accelerometer.
+   *
+   * @param range The maximum acceleration, positive or negative, that the accelerometer will
+   *     measure.
+   */
   public void setRange(Range range) {
     AccelerometerJNI.setAccelerometerActive(false);
 
@@ -53,9 +63,8 @@
       case k8G:
         AccelerometerJNI.setAccelerometerRange(2);
         break;
-      case k16G:
       default:
-        throw new IllegalArgumentException(range + " range not supported (use k2G, k4G, or k8G)");
+        throw new IllegalArgumentException("Missing case for range type " + range);
     }
 
     AccelerometerJNI.setAccelerometerActive(true);
@@ -66,7 +75,6 @@
    *
    * @return The acceleration of the roboRIO along the X axis in g-forces
    */
-  @Override
   public double getX() {
     return AccelerometerJNI.getAccelerometerX();
   }
@@ -76,7 +84,6 @@
    *
    * @return The acceleration of the roboRIO along the Y axis in g-forces
    */
-  @Override
   public double getY() {
     return AccelerometerJNI.getAccelerometerY();
   }
@@ -86,7 +93,6 @@
    *
    * @return The acceleration of the roboRIO along the Z axis in g-forces
    */
-  @Override
   public double getZ() {
     return AccelerometerJNI.getAccelerometerZ();
   }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java
index 77ccb37..8808a4b 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java
@@ -172,4 +172,15 @@
   public boolean readPacketTimeout(int apiId, int timeoutMs, CANData data) {
     return CANAPIJNI.readCANPacketTimeout(m_handle, apiId, timeoutMs, data);
   }
+
+  /**
+   * Reads the current value of the millisecond-resolution timer that {@link CANData} timestamps are
+   * based on.
+   *
+   * @return Current value of timer used as a base time for {@link CANData} timestamps in
+   *     milliseconds
+   */
+  public static long getTimestampBaseTime() {
+    return CANAPIJNI.getCANPacketBaseTime();
+  }
 }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Compressor.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Compressor.java
index e26bd62..f2cfeb3 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Compressor.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Compressor.java
@@ -63,18 +63,6 @@
   }
 
   /**
-   * Get the status of the compressor. To (re)enable the compressor use enableDigital() or
-   * enableAnalog(...).
-   *
-   * @return true if the compressor is on
-   * @deprecated To avoid confusion in thinking this (re)enables the compressor use IsEnabled().
-   */
-  @Deprecated(since = "2023", forRemoval = true)
-  public boolean enabled() {
-    return isEnabled();
-  }
-
-  /**
    * Returns whether the compressor is active or not.
    *
    * @return true if the compressor is on - otherwise false.
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java
index 01483ab..d8c5015 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java
@@ -51,7 +51,7 @@
   private boolean m_allocatedDownSource;
   int m_counter; // /< The FPGA counter object.
   private int m_index; // /< The index of this counter.
-  private double m_distancePerPulse; // distance of travel for each tick
+  private double m_distancePerPulse = 1; // distance of travel for each tick
 
   /**
    * Create an instance of a counter with the given mode.
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DataLogManager.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DataLogManager.java
index 927c14d..8098ae9 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DataLogManager.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DataLogManager.java
@@ -41,9 +41,10 @@
  */
 public final class DataLogManager {
   private static DataLog m_log;
+  private static boolean m_stopped;
   private static String m_logDir;
   private static boolean m_filenameOverride;
-  private static final Thread m_thread;
+  private static Thread m_thread;
   private static final ZoneId m_utc = ZoneId.of("UTC");
   private static final DateTimeFormatter m_timeFormatter =
       DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss").withZone(m_utc);
@@ -59,11 +60,6 @@
 
   private DataLogManager() {}
 
-  static {
-    m_thread = new Thread(DataLogManager::logMain, "DataLogDS");
-    m_thread.setDaemon(true);
-  }
-
   /** Start data log manager with default directory location. */
   public static synchronized void start() {
     start("", "", 0.25);
@@ -100,33 +96,52 @@
    *     tradeoff
    */
   public static synchronized void start(String dir, String filename, double period) {
-    if (m_log != null) {
-      return;
-    }
-    m_logDir = makeLogDir(dir);
-    m_filenameOverride = !filename.isEmpty();
+    if (m_log == null) {
+      m_logDir = makeLogDir(dir);
+      m_filenameOverride = !filename.isEmpty();
 
-    // Delete all previously existing FRC_TBD_*.wpilog files. These only exist when the robot
-    // never connects to the DS, so they are very unlikely to have useful data and just clutter
-    // the filesystem.
-    File[] files =
-        new File(m_logDir)
-            .listFiles((d, name) -> name.startsWith("FRC_TBD_") && name.endsWith(".wpilog"));
-    if (files != null) {
-      for (File file : files) {
-        if (!file.delete()) {
-          System.err.println("DataLogManager: could not delete " + file);
+      // Delete all previously existing FRC_TBD_*.wpilog files. These only exist when the robot
+      // never connects to the DS, so they are very unlikely to have useful data and just clutter
+      // the filesystem.
+      File[] files =
+          new File(m_logDir)
+              .listFiles((d, name) -> name.startsWith("FRC_TBD_") && name.endsWith(".wpilog"));
+      if (files != null) {
+        for (File file : files) {
+          if (!file.delete()) {
+            System.err.println("DataLogManager: could not delete " + file);
+          }
         }
       }
+      m_log = new DataLog(m_logDir, makeLogFilename(filename), period);
+      m_messageLog = new StringLogEntry(m_log, "messages");
+
+      // Log all NT entries and connections
+      if (m_ntLoggerEnabled) {
+        startNtLog();
+      }
+    } else if (m_stopped) {
+      m_log.setFilename(makeLogFilename(filename));
+      m_log.resume();
+      m_stopped = false;
     }
 
-    m_log = new DataLog(m_logDir, makeLogFilename(filename), period);
-    m_messageLog = new StringLogEntry(m_log, "messages");
-    m_thread.start();
+    if (m_thread == null) {
+      m_thread = new Thread(DataLogManager::logMain, "DataLogDS");
+      m_thread.setDaemon(true);
+      m_thread.start();
+    }
+  }
 
-    // Log all NT entries and connections
-    if (m_ntLoggerEnabled) {
-      startNtLog();
+  /** Stop data log manager. */
+  public static synchronized void stop() {
+    if (m_thread != null) {
+      m_thread.interrupt();
+      m_thread = null;
+    }
+    if (m_log != null) {
+      m_log.stop();
+      m_stopped = true;
     }
   }
 
@@ -204,8 +219,13 @@
       } catch (IOException ex) {
         // ignored
       }
+      if (RobotBase.getRuntimeType() == RuntimeType.kRoboRIO) {
+        DriverStation.reportWarning(
+            "DataLogManager: Logging to RoboRIO 1 internal storage is not recommended!"
+                + " Plug in a FAT32 formatted flash drive!",
+            false);
+      }
     }
-
     return Filesystem.getOperatingDirectory().getAbsolutePath();
   }
 
@@ -257,6 +277,7 @@
             }
             long length = file.length();
             if (file.delete()) {
+              DriverStation.reportWarning("DataLogManager: Deleted " + file.getName(), false);
               freeSpace += length;
               if (freeSpace >= kFreeSpaceThreshold) {
                 break;
@@ -266,6 +287,15 @@
             }
           }
         }
+      } else if (freeSpace < 2 * kFreeSpaceThreshold) {
+        DriverStation.reportWarning(
+            "DataLogManager: Log storage device has "
+                + freeSpace / 1000000
+                + " MB of free space remaining! Logs will get deleted below "
+                + kFreeSpaceThreshold / 1000000
+                + " MB of free space."
+                + "Consider deleting logs off the storage device.",
+            false);
       }
     }
 
@@ -317,10 +347,8 @@
           dsAttachCount = 0;
         }
         if (dsAttachCount > 50) { // 1 second
-          LocalDateTime now = LocalDateTime.now(m_utc);
-          if (now.getYear() > 2000) {
-            // assume local clock is now synchronized to DS, so rename based on
-            // local time
+          if (RobotController.isSystemTimeValid()) {
+            LocalDateTime now = LocalDateTime.now(m_utc);
             m_log.setFilename("FRC_" + m_timeFormatter.format(now) + ".wpilog");
             dsRenamed = true;
           } else {
@@ -336,7 +364,7 @@
         } else {
           fmsAttachCount = 0;
         }
-        if (fmsAttachCount > 100) { // 2 seconds
+        if (fmsAttachCount > 250) { // 5 seconds
           // match info comes through TCP, so we need to double-check we've
           // actually received it
           DriverStation.MatchType matchType = DriverStation.getMatchType();
@@ -376,7 +404,9 @@
       sysTimeCount++;
       if (sysTimeCount >= 250) {
         sysTimeCount = 0;
-        sysTimeEntry.append(WPIUtilJNI.getSystemTime(), WPIUtilJNI.now());
+        if (RobotController.isSystemTimeValid()) {
+          sysTimeEntry.append(WPIUtilJNI.getSystemTime(), WPIUtilJNI.now());
+        }
       }
     }
     newDataEvent.close();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DriverStation.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DriverStation.java
index e9d593c..ce3a3e0 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DriverStation.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DriverStation.java
@@ -22,6 +22,9 @@
 import edu.wpi.first.util.datalog.FloatArrayLogEntry;
 import edu.wpi.first.util.datalog.IntegerArrayLogEntry;
 import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.Optional;
+import java.util.OptionalInt;
 import java.util.concurrent.locks.ReentrantLock;
 
 /** Provide access to the network communication data to / from the Driver Station. */
@@ -67,8 +70,7 @@
   /** The robot alliance that the robot is a part of. */
   public enum Alliance {
     Red,
-    Blue,
-    Invalid
+    Blue
   }
 
   public enum MatchType {
@@ -216,78 +218,97 @@
       m_logAxes = new FloatArrayLogEntry(log, "DS:joystick" + stick + "/axes", timestamp);
       m_logPOVs = new IntegerArrayLogEntry(log, "DS:joystick" + stick + "/povs", timestamp);
 
-      appendButtons(timestamp);
-      appendAxes(timestamp);
-      appendPOVs(timestamp);
+      appendButtons(m_joystickButtons[m_stick], timestamp);
+      appendAxes(m_joystickAxes[m_stick], timestamp);
+      appendPOVs(m_joystickPOVs[m_stick], timestamp);
     }
 
     public void send(long timestamp) {
-      if (m_joystickButtonsCache[m_stick].m_count != m_joystickButtons[m_stick].m_count
-          || m_joystickButtonsCache[m_stick].m_buttons != m_joystickButtons[m_stick].m_buttons) {
-        appendButtons(timestamp);
+      HALJoystickButtons buttons = m_joystickButtons[m_stick];
+      if (buttons.m_count != m_prevButtons.m_count
+          || buttons.m_buttons != m_prevButtons.m_buttons) {
+        appendButtons(buttons, timestamp);
       }
 
-      if (m_joystickAxesCache[m_stick].m_count != m_joystickAxes[m_stick].m_count) {
-        appendAxes(timestamp);
+      HALJoystickAxes axes = m_joystickAxes[m_stick];
+      int count = axes.m_count;
+      boolean needToLog = false;
+      if (count != m_prevAxes.m_count) {
+        needToLog = true;
       } else {
-        int count = m_joystickAxesCache[m_stick].m_count;
         for (int i = 0; i < count; i++) {
-          if (m_joystickAxesCache[m_stick].m_axes[i] != m_joystickAxes[m_stick].m_axes[i]) {
-            appendAxes(timestamp);
-            break;
+          if (axes.m_axes[i] != m_prevAxes.m_axes[i]) {
+            needToLog = true;
           }
         }
       }
+      if (needToLog) {
+        appendAxes(axes, timestamp);
+      }
 
-      if (m_joystickPOVsCache[m_stick].m_count != m_joystickPOVs[m_stick].m_count) {
-        appendPOVs(timestamp);
+      HALJoystickPOVs povs = m_joystickPOVs[m_stick];
+      count = m_joystickPOVs[m_stick].m_count;
+      needToLog = false;
+      if (count != m_prevPOVs.m_count) {
+        needToLog = true;
       } else {
-        int count = m_joystickPOVsCache[m_stick].m_count;
         for (int i = 0; i < count; i++) {
-          if (m_joystickPOVsCache[m_stick].m_povs[i] != m_joystickPOVs[m_stick].m_povs[i]) {
-            appendPOVs(timestamp);
-            break;
+          if (povs.m_povs[i] != m_prevPOVs.m_povs[i]) {
+            needToLog = true;
           }
         }
       }
+      if (needToLog) {
+        appendPOVs(povs, timestamp);
+      }
     }
 
-    void appendButtons(long timestamp) {
-      int count = m_joystickButtonsCache[m_stick].m_count;
+    void appendButtons(HALJoystickButtons buttons, long timestamp) {
+      byte count = buttons.m_count;
       if (m_sizedButtons == null || m_sizedButtons.length != count) {
         m_sizedButtons = new boolean[count];
       }
-      int buttons = m_joystickButtonsCache[m_stick].m_buttons;
+      int buttonsValue = buttons.m_buttons;
       for (int i = 0; i < count; i++) {
-        m_sizedButtons[i] = (buttons & (1 << i)) != 0;
+        m_sizedButtons[i] = (buttonsValue & (1 << i)) != 0;
       }
       m_logButtons.append(m_sizedButtons, timestamp);
+      m_prevButtons.m_count = count;
+      m_prevButtons.m_buttons = buttons.m_buttons;
     }
 
-    void appendAxes(long timestamp) {
-      int count = m_joystickAxesCache[m_stick].m_count;
+    void appendAxes(HALJoystickAxes axes, long timestamp) {
+      int count = axes.m_count;
       if (m_sizedAxes == null || m_sizedAxes.length != count) {
         m_sizedAxes = new float[count];
       }
-      System.arraycopy(m_joystickAxesCache[m_stick].m_axes, 0, m_sizedAxes, 0, count);
+      System.arraycopy(axes.m_axes, 0, m_sizedAxes, 0, count);
       m_logAxes.append(m_sizedAxes, timestamp);
+      m_prevAxes.m_count = count;
+      System.arraycopy(axes.m_axes, 0, m_prevAxes.m_axes, 0, count);
     }
 
-    void appendPOVs(long timestamp) {
-      int count = m_joystickPOVsCache[m_stick].m_count;
+    @SuppressWarnings("PMD.AvoidArrayLoops")
+    void appendPOVs(HALJoystickPOVs povs, long timestamp) {
+      int count = povs.m_count;
       if (m_sizedPOVs == null || m_sizedPOVs.length != count) {
         m_sizedPOVs = new long[count];
       }
       for (int i = 0; i < count; i++) {
-        m_sizedPOVs[i] = m_joystickPOVsCache[m_stick].m_povs[i];
+        m_sizedPOVs[i] = povs.m_povs[i];
       }
       m_logPOVs.append(m_sizedPOVs, timestamp);
+      m_prevPOVs.m_count = count;
+      System.arraycopy(povs.m_povs, 0, m_prevPOVs.m_povs, 0, count);
     }
 
     final int m_stick;
     boolean[] m_sizedButtons;
     float[] m_sizedAxes;
     long[] m_sizedPOVs;
+    final HALJoystickButtons m_prevButtons = new HALJoystickButtons();
+    final HALJoystickAxes m_prevAxes = new HALJoystickAxes(DriverStationJNI.kMaxJoystickAxes);
+    final HALJoystickPOVs m_prevPOVs = new HALJoystickPOVs(DriverStationJNI.kMaxJoystickPOVs);
     final BooleanArrayLogEntry m_logButtons;
     final FloatArrayLogEntry m_logAxes;
     final IntegerArrayLogEntry m_logPOVs;
@@ -927,7 +948,7 @@
   }
 
   /**
-   * Gets a value indicating whether the Driver Station requires the robot to be running in test
+   * Gets a value indicating whether the Driver Station requires the robot to be running in Test
    * mode.
    *
    * @return True if test mode should be enabled, false otherwise.
@@ -942,6 +963,21 @@
   }
 
   /**
+   * Gets a value indicating whether the Driver Station requires the robot to be running in Test
+   * mode and enabled.
+   *
+   * @return True if test mode should be set and the robot should be enabled.
+   */
+  public static boolean isTestEnabled() {
+    m_cacheDataMutex.lock();
+    try {
+      return m_controlWord.getTest() && m_controlWord.getEnabled();
+    } finally {
+      m_cacheDataMutex.unlock();
+    }
+  }
+
+  /**
    * Gets a value indicating whether the Driver Station is attached.
    *
    * @return True if Driver Station is attached, false otherwise.
@@ -970,7 +1006,9 @@
   }
 
   /**
-   * Get the game specific message.
+   * Get the game specific message from the FMS.
+   *
+   * <p>If the FMS is not connected, it is set from the game data setting on the driver station.
    *
    * @return the game specific message
    */
@@ -984,7 +1022,7 @@
   }
 
   /**
-   * Get the event name.
+   * Get the event name from the FMS.
    *
    * @return the event name
    */
@@ -998,7 +1036,7 @@
   }
 
   /**
-   * Get the match type.
+   * Get the match type from the FMS.
    *
    * @return the match type
    */
@@ -1023,7 +1061,7 @@
   }
 
   /**
-   * Get the match number.
+   * Get the match number from the FMS.
    *
    * @return the match number
    */
@@ -1037,7 +1075,7 @@
   }
 
   /**
-   * Get the replay number.
+   * Get the replay number from the FMS.
    *
    * @return the replay number
    */
@@ -1050,67 +1088,112 @@
     }
   }
 
+  private static Map<AllianceStationID, Optional<Alliance>> m_allianceMap =
+      Map.of(
+          AllianceStationID.Unknown, Optional.empty(),
+          AllianceStationID.Red1, Optional.of(Alliance.Red),
+          AllianceStationID.Red2, Optional.of(Alliance.Red),
+          AllianceStationID.Red3, Optional.of(Alliance.Red),
+          AllianceStationID.Blue1, Optional.of(Alliance.Blue),
+          AllianceStationID.Blue2, Optional.of(Alliance.Blue),
+          AllianceStationID.Blue3, Optional.of(Alliance.Blue));
+
+  private static Map<AllianceStationID, OptionalInt> m_stationMap =
+      Map.of(
+          AllianceStationID.Unknown, OptionalInt.empty(),
+          AllianceStationID.Red1, OptionalInt.of(1),
+          AllianceStationID.Red2, OptionalInt.of(2),
+          AllianceStationID.Red3, OptionalInt.of(3),
+          AllianceStationID.Blue1, OptionalInt.of(1),
+          AllianceStationID.Blue2, OptionalInt.of(2),
+          AllianceStationID.Blue3, OptionalInt.of(3));
+
   /**
    * Get the current alliance from the FMS.
    *
+   * <p>If the FMS is not connected, it is set from the team alliance setting on the driver station.
+   *
    * @return the current alliance
    */
-  public static Alliance getAlliance() {
+  public static Optional<Alliance> getAlliance() {
     AllianceStationID allianceStationID = DriverStationJNI.getAllianceStation();
     if (allianceStationID == null) {
-      return Alliance.Invalid;
+      allianceStationID = AllianceStationID.Unknown;
     }
 
-    switch (allianceStationID) {
-      case Red1:
-      case Red2:
-      case Red3:
-        return Alliance.Red;
-
-      case Blue1:
-      case Blue2:
-      case Blue3:
-        return Alliance.Blue;
-
-      default:
-        return Alliance.Invalid;
-    }
+    return m_allianceMap.get(allianceStationID);
   }
 
   /**
-   * Gets the location of the team's driver station controls.
+   * Gets the location of the team's driver station controls from the FMS.
+   *
+   * <p>If the FMS is not connected, it is set from the team alliance setting on the driver station.
    *
    * @return the location of the team's driver station controls: 1, 2, or 3
    */
-  public static int getLocation() {
+  public static OptionalInt getLocation() {
     AllianceStationID allianceStationID = DriverStationJNI.getAllianceStation();
     if (allianceStationID == null) {
-      return 0;
+      allianceStationID = AllianceStationID.Unknown;
     }
-    switch (allianceStationID) {
-      case Red1:
-      case Blue1:
-        return 1;
 
-      case Red2:
-      case Blue2:
-        return 2;
+    return m_stationMap.get(allianceStationID);
+  }
 
-      case Blue3:
-      case Red3:
-        return 3;
+  /**
+   * Gets the raw alliance station of the teams driver station.
+   *
+   * <p>This returns the raw low level value. Prefer getLocation or getAlliance unless necessary for
+   * performance.
+   *
+   * @return The raw alliance station id.
+   */
+  public static AllianceStationID getRawAllianceStation() {
+    return DriverStationJNI.getAllianceStation();
+  }
 
-      default:
-        return 0;
+  /**
+   * Wait for a DS connection.
+   *
+   * @param timeoutSeconds timeout in seconds. 0 for infinite.
+   * @return true if connected, false if timeout
+   */
+  public static boolean waitForDsConnection(double timeoutSeconds) {
+    int event = WPIUtilJNI.createEvent(true, false);
+    DriverStationJNI.provideNewDataEventHandle(event);
+    boolean result;
+    try {
+      if (timeoutSeconds == 0) {
+        WPIUtilJNI.waitForObject(event);
+        result = true;
+      } else {
+        result = !WPIUtilJNI.waitForObjectTimeout(event, timeoutSeconds);
+      }
+    } catch (InterruptedException ex) {
+      Thread.currentThread().interrupt();
+      result = false;
+    } finally {
+      DriverStationJNI.removeNewDataEventHandle(event);
+      WPIUtilJNI.destroyEvent(event);
     }
+    return result;
   }
 
   /**
    * Return the approximate match time. The FMS does not send an official match time to the robots,
    * but does send an approximate match time. The value will count down the time remaining in the
    * current period (auto or teleop). Warning: This is not an official time (so it cannot be used to
-   * dispute ref calls or guarantee that a function will trigger before the match ends) The Practice
-   * Match function of the DS approximates the behavior seen on the field.
+   * dispute ref calls or guarantee that a function will trigger before the match ends).
+   *
+   * <p>When connected to the real field, this number only changes in full integer increments, and
+   * always counts down.
+   *
+   * <p>When the DS is in practice mode, this number is a floating point number, and counts down.
+   *
+   * <p>When the DS is in teleop or autonomous mode, this number is a floating point number, and
+   * counts up.
+   *
+   * <p>Simulation matches DS behavior without an FMS connected.
    *
    * @return Time remaining in current match period (auto or teleop) in seconds
    */
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycleEncoder.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycleEncoder.java
index dd78860..afdcb3f 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycleEncoder.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycleEncoder.java
@@ -208,6 +208,9 @@
    */
   public void setDistancePerRotation(double distancePerRotation) {
     m_distancePerRotation = distancePerRotation;
+    if (m_simDistancePerRotation != null) {
+      m_simDistancePerRotation.set(distancePerRotation);
+    }
   }
 
   /**
@@ -243,7 +246,10 @@
     if (m_counter != null) {
       m_counter.reset();
     }
-    m_positionOffset = m_dutyCycle.getOutput();
+    if (m_simPosition != null) {
+      m_simPosition.set(0);
+    }
+    m_positionOffset = getAbsolutePosition();
   }
 
   /**
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java
index 2476272..2cbb5c7 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java
@@ -44,8 +44,10 @@
 
   /** The 'a' source. */
   protected DigitalSource m_aSource; // the A phase of the quad encoder
+
   /** The 'b' source. */
   protected DigitalSource m_bSource; // the B phase of the quad encoder
+
   /** The index source. */
   protected DigitalSource m_indexSource; // Index on some encoders
 
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Filesystem.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Filesystem.java
index 125c1e2..782538d 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Filesystem.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Filesystem.java
@@ -23,7 +23,13 @@
    * @return The current working directory (launch directory)
    */
   public static File getLaunchDirectory() {
-    return new File(System.getProperty("user.dir")).getAbsoluteFile();
+    // workaround for
+    // https://www.chiefdelphi.com/t/filesystem-getdeploydirectory-returning-wrong-location-how-to-fix/427292
+    String path =
+        System.getProperty("user.dir")
+            .replace(
+                File.separator + "build" + File.separator + "jni" + File.separator + "release", "");
+    return new File(path).getAbsoluteFile();
   }
 
   /**
@@ -33,7 +39,7 @@
    * @return The operating directory
    */
   public static File getOperatingDirectory() {
-    if (RobotBase.isReal()) {
+    if (!RobotBase.isSimulation()) {
       return new File("/home/lvuser");
     } else {
       return getLaunchDirectory();
@@ -48,7 +54,7 @@
    * @return The 'deploy' directory
    */
   public static File getDeployDirectory() {
-    if (RobotBase.isReal()) {
+    if (!RobotBase.isSimulation()) {
       return new File(getOperatingDirectory(), "deploy");
     } else {
       return new File(
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java
index e0fe034..4afece4 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java
@@ -24,6 +24,8 @@
  *
  * <p>robotInit() -- provide for initialization at robot power-on
  *
+ * <p>driverStationConnected() -- provide for initialization the first time the DS is connected
+ *
  * <p>init() functions -- each of the following functions is called once when the appropriate mode
  * is entered:
  *
@@ -69,6 +71,7 @@
   private final Watchdog m_watchdog;
   private boolean m_ntFlushEnabled = true;
   private boolean m_lwEnabledInTest = true;
+  private boolean m_calledDsConnected;
 
   /**
    * Constructor for IterativeRobotBase.
@@ -99,6 +102,14 @@
   public void robotInit() {}
 
   /**
+   * Code that needs to know the DS state should go here.
+   *
+   * <p>Users should override this method for initialization that needs to occur after the DS is
+   * connected, such as needing the alliance information.
+   */
+  public void driverStationConnected() {}
+
+  /**
    * Robot-wide simulation initialization code should go here.
    *
    * <p>Users should override this method for default Robot-wide simulation related initialization
@@ -253,7 +264,7 @@
    * @throws ConcurrentModificationException if this is called during test mode.
    */
   public void enableLiveWindowInTest(boolean testLW) {
-    if (isTest()) {
+    if (isTestEnabled()) {
       throw new ConcurrentModificationException("Can't configure test mode while in test mode!");
     }
     m_lwEnabledInTest = testLW;
@@ -295,6 +306,11 @@
       mode = Mode.kTest;
     }
 
+    if (!m_calledDsConnected && m_word.isDSAttached()) {
+      m_calledDsConnected = true;
+      driverStationConnected();
+    }
+
     // If mode changed, call mode exit and entry functions
     if (m_lastMode != mode) {
       // Call last mode's exit function
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Notifier.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Notifier.java
index 25657b3..7ed6162 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Notifier.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Notifier.java
@@ -11,7 +11,7 @@
 import java.util.concurrent.locks.ReentrantLock;
 
 /**
- * Notifiers run a callback function on a separate thread at a specified period.
+ * Notifiers run a user-provided callback function on a separate thread.
  *
  * <p>If startSingle() is used, the callback will run once. If startPeriodic() is used, the callback
  * will run repeatedly with the given period until stop() is called.
@@ -19,23 +19,27 @@
 public class Notifier implements AutoCloseable {
   // The thread waiting on the HAL alarm.
   private Thread m_thread;
-  // The lock for the process information.
+
+  // The lock held while updating process information.
   private final ReentrantLock m_processLock = new ReentrantLock();
-  // The C pointer to the notifier object. We don't use it directly, it is
-  // just passed to the JNI bindings.
+
+  // HAL handle passed to the JNI bindings (atomic for proper destruction).
   private final AtomicInteger m_notifier = new AtomicInteger();
-  // The time, in seconds, at which the corresponding handler should be
-  // called. Has the same zero as RobotController.getFPGATime().
+
+  // The user-provided callback.
+  private Runnable m_callback;
+
+  // The time, in seconds, at which the callback should be called. Has the same
+  // zero as RobotController.getFPGATime().
   private double m_expirationTimeSeconds;
-  // The handler passed in by the user which should be called at the
-  // appropriate interval.
-  private Runnable m_handler;
-  // Whether we are calling the handler just once or periodically.
-  private boolean m_periodic;
-  // If periodic, the period of the calling; if just once, stores how long it
-  // is until we call the handler.
+
+  // If periodic, stores the callback period; if single, stores the time until
+  // the callback call.
   private double m_periodSeconds;
 
+  // True if the callback is periodic
+  private boolean m_periodic;
+
   @Override
   public void close() {
     int handle = m_notifier.getAndSet(0);
@@ -43,7 +47,7 @@
       return;
     }
     NotifierJNI.stopNotifier(handle);
-    // Join the thread to ensure the handler has exited.
+    // Join the thread to ensure the callback has exited.
     if (m_thread.isAlive()) {
       try {
         m_thread.interrupt();
@@ -75,15 +79,16 @@
   }
 
   /**
-   * Create a Notifier for timer event notification.
+   * Create a Notifier with the given callback.
    *
-   * @param run The handler that is called at the notification time which is set using StartSingle
-   *     or StartPeriodic.
+   * <p>Configure when the callback runs with startSingle() or startPeriodic().
+   *
+   * @param callback The callback to run.
    */
-  public Notifier(Runnable run) {
-    requireNonNullParam(run, "run", "Notifier");
+  public Notifier(Runnable callback) {
+    requireNonNullParam(callback, "callback", "Notifier");
 
-    m_handler = run;
+    m_callback = callback;
     m_notifier.set(NotifierJNI.initializeNotifier());
 
     m_thread =
@@ -99,23 +104,24 @@
                   break;
                 }
 
-                Runnable handler;
+                Runnable threadHandler;
                 m_processLock.lock();
                 try {
-                  handler = m_handler;
+                  threadHandler = m_callback;
                   if (m_periodic) {
                     m_expirationTimeSeconds += m_periodSeconds;
                     updateAlarm();
                   } else {
-                    // need to update the alarm to cause it to wait again
+                    // Need to update the alarm to cause it to wait again
                     updateAlarm((long) -1);
                   }
                 } finally {
                   m_processLock.unlock();
                 }
 
-                if (handler != null) {
-                  handler.run();
+                // Call callback
+                if (threadHandler != null) {
+                  threadHandler.run();
                 }
               }
             });
@@ -150,24 +156,39 @@
   }
 
   /**
-   * Change the handler function.
+   * Change the callback function.
    *
-   * @param handler Handler
+   * @param callback The callback function.
+   * @deprecated Use setCallback() instead.
    */
-  public void setHandler(Runnable handler) {
+  @Deprecated(forRemoval = true, since = "2024")
+  public void setHandler(Runnable callback) {
     m_processLock.lock();
     try {
-      m_handler = handler;
+      m_callback = callback;
     } finally {
       m_processLock.unlock();
     }
   }
 
   /**
-   * Register for single event notification. A timer event is queued for a single event after the
-   * specified delay.
+   * Change the callback function.
    *
-   * @param delaySeconds Seconds to wait before the handler is called.
+   * @param callback The callback function.
+   */
+  public void setCallback(Runnable callback) {
+    m_processLock.lock();
+    try {
+      m_callback = callback;
+    } finally {
+      m_processLock.unlock();
+    }
+  }
+
+  /**
+   * Run the callback once after the given delay.
+   *
+   * @param delaySeconds Time in seconds to wait before the callback is called.
    */
   public void startSingle(double delaySeconds) {
     m_processLock.lock();
@@ -182,15 +203,13 @@
   }
 
   /**
-   * Register for periodic event notification. A timer event is queued for periodic event
-   * notification. Each time the interrupt occurs, the event will be immediately requeued for the
-   * same time interval.
+   * Run the callback periodically with the given period.
    *
-   * <p>The user-provided callback should be written in a nonblocking manner so the callback can be
-   * recalled at the next periodic event notification.
+   * <p>The user-provided callback should be written so that it completes before the next time it's
+   * scheduled to run.
    *
-   * @param periodSeconds Period in seconds to call the handler starting one period after the call
-   *     to this method.
+   * @param periodSeconds Period in seconds after which to to call the callback starting one period
+   *     after the call to this method.
    */
   public void startPeriodic(double periodSeconds) {
     m_processLock.lock();
@@ -205,9 +224,13 @@
   }
 
   /**
-   * Stop timer events from occurring. Stop any repeating timer events from occurring. This will
-   * also remove any single notification events from the queue. If a timer-based call to the
-   * registered handler is in progress, this function will block until the handler call is complete.
+   * Stop further callback invocations.
+   *
+   * <p>No further periodic callbacks will occur. Single invocations will also be cancelled if they
+   * haven't yet occurred.
+   *
+   * <p>If a callback invocation is in progress, this function will block until the callback is
+   * complete.
    */
   public void stop() {
     m_processLock.lock();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS4Controller.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS4Controller.java
index c418e54..2359b7b 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS4Controller.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS4Controller.java
@@ -4,8 +4,6 @@
 
 package edu.wpi.first.wpilibj;
 
-import edu.wpi.first.hal.FRCNetComm.tResourceType;
-import edu.wpi.first.hal.HAL;
 import edu.wpi.first.wpilibj.event.BooleanEvent;
 import edu.wpi.first.wpilibj.event.EventLoop;
 
@@ -24,7 +22,9 @@
    */
   public PS4Controller(int port) {
     super(port);
-    HAL.report(tResourceType.kResourceType_PS4Controller, port + 1);
+
+    // re-enable when PS4Controller is added to Usage Reporting
+    // HAL.report(tResourceType.kResourceType_PS4Controller, port + 1); /
   }
 
   /** Represents a digital button on a PS4Controller. */
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS5Controller.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS5Controller.java
new file mode 100644
index 0000000..8af98a5
--- /dev/null
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS5Controller.java
@@ -0,0 +1,697 @@
+// 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.
+
+package edu.wpi.first.wpilibj;
+
+import edu.wpi.first.wpilibj.event.BooleanEvent;
+import edu.wpi.first.wpilibj.event.EventLoop;
+
+/**
+ * Handle input from PS5 controllers connected to the Driver Station.
+ *
+ * <p>This class handles PS5 input that comes from the Driver Station. Each time a value is
+ * requested the most recent value is returned. There is a single class instance for each controller
+ * and the mapping of ports to hardware buttons depends on the code in the Driver Station.
+ */
+public class PS5Controller extends GenericHID {
+  /**
+   * Construct an instance of a device.
+   *
+   * @param port The port index on the Driver Station that the device is plugged into.
+   */
+  public PS5Controller(int port) {
+    super(port);
+    // HAL.report(tResourceType.kResourceType_PS5Controller, port + 1);
+  }
+
+  /** Represents a digital button on a PS5Controller. */
+  public enum Button {
+    kCross(1),
+    kCircle(2),
+    kSquare(3),
+    kTriangle(4),
+    kL1(5),
+    kR1(6),
+    kL2(7),
+    kR2(8),
+    kCreate(9),
+    kOptions(10),
+    kPS(11),
+    kL3(12),
+    kR3(13),
+    kTouchpad(14);
+
+    public final int value;
+
+    Button(int index) {
+      this.value = index;
+    }
+
+    /**
+     * Get the human-friendly name of the button, matching the relevant methods. This is done by
+     * stripping the leading `k`, and if not the touchpad append `Button`.
+     *
+     * <p>Primarily used for automated unit tests.
+     *
+     * @return the human-friendly name of the button.
+     */
+    @Override
+    public String toString() {
+      var name = this.name().substring(1); // Remove leading `k`
+      if (this == kTouchpad) {
+        return name;
+      }
+      return name + "Button";
+    }
+  }
+
+  /** Represents an axis on a PS5Controller. */
+  public enum Axis {
+    kLeftX(0),
+    kLeftY(1),
+    kL2(2),
+    kRightX(3),
+    kRightY(4),
+    kR2(5);
+
+    public final int value;
+
+    Axis(int index) {
+      value = index;
+    }
+
+    /**
+     * Get the human-friendly name of the axis, matching the relevant methods. This is done by
+     * stripping the leading `k`, and if one of L2/R2 append `Axis`.
+     *
+     * <p>Primarily used for automated unit tests.
+     *
+     * @return the human-friendly name of the axis.
+     */
+    @Override
+    public String toString() {
+      var name = this.name().substring(1); // Remove leading `k`
+      if (name.endsWith("2")) {
+        return name + "Axis";
+      }
+      return name;
+    }
+  }
+
+  /**
+   * Get the X axis value of left side of the controller.
+   *
+   * @return the axis value.
+   */
+  public double getLeftX() {
+    return getRawAxis(Axis.kLeftX.value);
+  }
+
+  /**
+   * Get the X axis value of right side of the controller.
+   *
+   * @return the axis value.
+   */
+  public double getRightX() {
+    return getRawAxis(Axis.kRightX.value);
+  }
+
+  /**
+   * Get the Y axis value of left side of the controller.
+   *
+   * @return the axis value.
+   */
+  public double getLeftY() {
+    return getRawAxis(Axis.kLeftY.value);
+  }
+
+  /**
+   * Get the Y axis value of right side of the controller.
+   *
+   * @return the axis value.
+   */
+  public double getRightY() {
+    return getRawAxis(Axis.kRightY.value);
+  }
+
+  /**
+   * Get the L2 axis value of the controller. Note that this axis is bound to the range of [0, 1] as
+   * opposed to the usual [-1, 1].
+   *
+   * @return the axis value.
+   */
+  public double getL2Axis() {
+    return getRawAxis(Axis.kL2.value);
+  }
+
+  /**
+   * Get the R2 axis value of the controller. Note that this axis is bound to the range of [0, 1] as
+   * opposed to the usual [-1, 1].
+   *
+   * @return the axis value.
+   */
+  public double getR2Axis() {
+    return getRawAxis(Axis.kR2.value);
+  }
+
+  /**
+   * Read the value of the left trigger button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getL2Button() {
+    return getRawButton(Button.kL2.value);
+  }
+
+  /**
+   * Read the value of the right trigger button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getR2Button() {
+    return getRawButton(Button.kR2.value);
+  }
+
+  /**
+   * Whether the L2 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getL2ButtonPressed() {
+    return getRawButtonPressed(Button.kL2.value);
+  }
+
+  /**
+   * Whether the R2 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getR2ButtonPressed() {
+    return getRawButtonPressed(Button.kR2.value);
+  }
+
+  /**
+   * Whether the L2 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getL2ButtonReleased() {
+    return getRawButtonReleased(Button.kL2.value);
+  }
+
+  /**
+   * Whether the R2 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getR2ButtonReleased() {
+    return getRawButtonReleased(Button.kR2.value);
+  }
+
+  /**
+   * Constructs an event instance around the L2 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L2 button's digital signal attached to the given
+   *     loop.
+   */
+  @SuppressWarnings("MethodName")
+  public BooleanEvent L2(EventLoop loop) {
+    return new BooleanEvent(loop, this::getL2Button);
+  }
+
+  /**
+   * Constructs an event instance around the R2 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R2 button's digital signal attached to the given
+   *     loop.
+   */
+  @SuppressWarnings("MethodName")
+  public BooleanEvent R2(EventLoop loop) {
+    return new BooleanEvent(loop, this::getR2Button);
+  }
+
+  /**
+   * Read the value of the L1 button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getL1Button() {
+    return getRawButton(Button.kL1.value);
+  }
+
+  /**
+   * Read the value of the R1 button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getR1Button() {
+    return getRawButton(Button.kR1.value);
+  }
+
+  /**
+   * Whether the L1 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getL1ButtonPressed() {
+    return getRawButtonPressed(Button.kL1.value);
+  }
+
+  /**
+   * Whether the R1 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getR1ButtonPressed() {
+    return getRawButtonPressed(Button.kR1.value);
+  }
+
+  /**
+   * Whether the L1 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getL1ButtonReleased() {
+    return getRawButtonReleased(Button.kL1.value);
+  }
+
+  /**
+   * Whether the R1 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getR1ButtonReleased() {
+    return getRawButtonReleased(Button.kR1.value);
+  }
+
+  /**
+   * Constructs an event instance around the L1 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L1 button's digital signal attached to the given
+   *     loop.
+   */
+  @SuppressWarnings("MethodName")
+  public BooleanEvent L1(EventLoop loop) {
+    return new BooleanEvent(loop, this::getL1Button);
+  }
+
+  /**
+   * Constructs an event instance around the R1 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R1 button's digital signal attached to the given
+   *     loop.
+   */
+  @SuppressWarnings("MethodName")
+  public BooleanEvent R1(EventLoop loop) {
+    return new BooleanEvent(loop, this::getR1Button);
+  }
+
+  /**
+   * Read the value of the L3 button (pressing the left analog stick) on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getL3Button() {
+    return getRawButton(Button.kL3.value);
+  }
+
+  /**
+   * Read the value of the R3 button (pressing the right analog stick) on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getR3Button() {
+    return getRawButton(Button.kR3.value);
+  }
+
+  /**
+   * Whether the L3 (left stick) button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getL3ButtonPressed() {
+    return getRawButtonPressed(Button.kL3.value);
+  }
+
+  /**
+   * Whether the R3 (right stick) button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getR3ButtonPressed() {
+    return getRawButtonPressed(Button.kR3.value);
+  }
+
+  /**
+   * Whether the L3 (left stick) button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getL3ButtonReleased() {
+    return getRawButtonReleased(Button.kL3.value);
+  }
+
+  /**
+   * Whether the R3 (right stick) button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getR3ButtonReleased() {
+    return getRawButtonReleased(Button.kR3.value);
+  }
+
+  /**
+   * Constructs an event instance around the L3 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L3 button's digital signal attached to the given
+   *     loop.
+   */
+  @SuppressWarnings("MethodName")
+  public BooleanEvent L3(EventLoop loop) {
+    return new BooleanEvent(loop, this::getL3Button);
+  }
+
+  /**
+   * Constructs an event instance around the R3 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R3 button's digital signal attached to the given
+   *     loop.
+   */
+  @SuppressWarnings("MethodName")
+  public BooleanEvent R3(EventLoop loop) {
+    return new BooleanEvent(loop, this::getR3Button);
+  }
+
+  /**
+   * Read the value of the Square button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getSquareButton() {
+    return getRawButton(Button.kSquare.value);
+  }
+
+  /**
+   * Whether the Square button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getSquareButtonPressed() {
+    return getRawButtonPressed(Button.kSquare.value);
+  }
+
+  /**
+   * Whether the Square button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getSquareButtonReleased() {
+    return getRawButtonReleased(Button.kSquare.value);
+  }
+
+  /**
+   * Constructs an event instance around the square button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the square button's digital signal attached to the given
+   *     loop.
+   */
+  public BooleanEvent square(EventLoop loop) {
+    return new BooleanEvent(loop, this::getSquareButton);
+  }
+
+  /**
+   * Read the value of the Cross button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getCrossButton() {
+    return getRawButton(Button.kCross.value);
+  }
+
+  /**
+   * Whether the Cross button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getCrossButtonPressed() {
+    return getRawButtonPressed(Button.kCross.value);
+  }
+
+  /**
+   * Whether the Cross button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getCrossButtonReleased() {
+    return getRawButtonReleased(Button.kCross.value);
+  }
+
+  /**
+   * Constructs an event instance around the cross button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the cross button's digital signal attached to the given
+   *     loop.
+   */
+  public BooleanEvent cross(EventLoop loop) {
+    return new BooleanEvent(loop, this::getCrossButton);
+  }
+
+  /**
+   * Read the value of the Triangle button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getTriangleButton() {
+    return getRawButton(Button.kTriangle.value);
+  }
+
+  /**
+   * Whether the Triangle button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getTriangleButtonPressed() {
+    return getRawButtonPressed(Button.kTriangle.value);
+  }
+
+  /**
+   * Whether the Triangle button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getTriangleButtonReleased() {
+    return getRawButtonReleased(Button.kTriangle.value);
+  }
+
+  /**
+   * Constructs an event instance around the triangle button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the triangle button's digital signal attached to the
+   *     given loop.
+   */
+  public BooleanEvent triangle(EventLoop loop) {
+    return new BooleanEvent(loop, this::getTriangleButton);
+  }
+
+  /**
+   * Read the value of the Circle button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getCircleButton() {
+    return getRawButton(Button.kCircle.value);
+  }
+
+  /**
+   * Whether the Circle button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getCircleButtonPressed() {
+    return getRawButtonPressed(Button.kCircle.value);
+  }
+
+  /**
+   * Whether the Circle button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getCircleButtonReleased() {
+    return getRawButtonReleased(Button.kCircle.value);
+  }
+
+  /**
+   * Constructs an event instance around the circle button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the circle button's digital signal attached to the given
+   *     loop.
+   */
+  public BooleanEvent circle(EventLoop loop) {
+    return new BooleanEvent(loop, this::getCircleButton);
+  }
+
+  /**
+   * Read the value of the share button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getCreateButton() {
+    return getRawButton(Button.kCreate.value);
+  }
+
+  /**
+   * Whether the share button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getCreateButtonPressed() {
+    return getRawButtonPressed(Button.kCreate.value);
+  }
+
+  /**
+   * Whether the share button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getCreateButtonReleased() {
+    return getRawButtonReleased(Button.kCreate.value);
+  }
+
+  /**
+   * Constructs an event instance around the share button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the share button's digital signal attached to the given
+   *     loop.
+   */
+  @SuppressWarnings("MethodName")
+  public BooleanEvent create(EventLoop loop) {
+    return new BooleanEvent(loop, this::getCreateButton);
+  }
+
+  /**
+   * Read the value of the PS button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getPSButton() {
+    return getRawButton(Button.kPS.value);
+  }
+
+  /**
+   * Whether the PS button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getPSButtonPressed() {
+    return getRawButtonPressed(Button.kPS.value);
+  }
+
+  /**
+   * Whether the PS button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getPSButtonReleased() {
+    return getRawButtonReleased(Button.kPS.value);
+  }
+
+  /**
+   * Constructs an event instance around the PS button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the PS button's digital signal attached to the given
+   *     loop.
+   */
+  @SuppressWarnings("MethodName")
+  public BooleanEvent PS(EventLoop loop) {
+    return new BooleanEvent(loop, this::getPSButton);
+  }
+
+  /**
+   * Read the value of the options button on the controller.
+   *
+   * @return The state of the button.
+   */
+  public boolean getOptionsButton() {
+    return getRawButton(Button.kOptions.value);
+  }
+
+  /**
+   * Whether the options button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  public boolean getOptionsButtonPressed() {
+    return getRawButtonPressed(Button.kOptions.value);
+  }
+
+  /**
+   * Whether the options button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  public boolean getOptionsButtonReleased() {
+    return getRawButtonReleased(Button.kOptions.value);
+  }
+
+  /**
+   * Constructs an event instance around the options button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the options button's digital signal attached to the
+   *     given loop.
+   */
+  public BooleanEvent options(EventLoop loop) {
+    return new BooleanEvent(loop, this::getOptionsButton);
+  }
+
+  /**
+   * Read the value of the touchpad on the controller.
+   *
+   * @return The state of the touchpad.
+   */
+  public boolean getTouchpad() {
+    return getRawButton(Button.kTouchpad.value);
+  }
+
+  /**
+   * Whether the touchpad was pressed since the last check.
+   *
+   * @return Whether the touchpad was pressed since the last check.
+   */
+  public boolean getTouchpadPressed() {
+    return getRawButtonPressed(Button.kTouchpad.value);
+  }
+
+  /**
+   * Whether the touchpad was released since the last check.
+   *
+   * @return Whether the touchpad was released since the last check.
+   */
+  public boolean getTouchpadReleased() {
+    return getRawButtonReleased(Button.kTouchpad.value);
+  }
+
+  /**
+   * Constructs an event instance around the touchpad's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the touchpad's digital signal attached to the given
+   *     loop.
+   */
+  public BooleanEvent touchpad(EventLoop loop) {
+    return new BooleanEvent(loop, this::getTouchpad);
+  }
+}
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java
index e1f0a96..b0b3810 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java
@@ -16,22 +16,17 @@
  * Class implements the PWM generation in the FPGA.
  *
  * <p>The values supplied as arguments for PWM outputs range from -1.0 to 1.0. They are mapped to
- * the hardware dependent values, in this case 0-2000 for the FPGA. Changes are immediately sent to
- * the FPGA, and the update occurs at the next FPGA cycle (5.005ms). There is no delay.
- *
- * <p>As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-2000 values as follows: - 2000 =
- * maximum pulse width - 1999 to 1001 = linear scaling from "full forward" to "center" - 1000 =
- * center value - 999 to 2 = linear scaling from "center" to "full reverse" - 1 = minimum pulse
- * width (currently .5ms) - 0 = disabled (i.e. PWM output is held low)
+ * the microseconds to keep the pulse high, with a range of 0 (off) to 4096. Changes are immediately
+ * sent to the FPGA, and the update occurs at the next FPGA cycle (5.05ms). There is no delay.
  */
 public class PWM implements Sendable, AutoCloseable {
   /** Represents the amount to multiply the minimum servo-pulse pwm period by. */
   public enum PeriodMultiplier {
-    /** Period Multiplier: don't skip pulses. PWM pulses occur every 5.005 ms */
+    /** Period Multiplier: don't skip pulses. PWM pulses occur every 5.05 ms */
     k1X,
-    /** Period Multiplier: skip every other pulse. PWM pulses occur every 10.010 ms */
+    /** Period Multiplier: skip every other pulse. PWM pulses occur every 10.10 ms */
     k2X,
-    /** Period Multiplier: skip three out of four pulses. PWM pulses occur every 20.020 ms */
+    /** Period Multiplier: skip three out of four pulses. PWM pulses occur every 20.20 ms */
     k4X
   }
 
@@ -103,15 +98,15 @@
    * type of controller. The values determine the upper and lower speeds as well as the deadband
    * bracket.
    *
-   * @param max The max PWM pulse width in ms
-   * @param deadbandMax The high end of the deadband range pulse width in ms
-   * @param center The center (off) pulse width in ms
-   * @param deadbandMin The low end of the deadband pulse width in ms
-   * @param min The minimum pulse width in ms
+   * @param max The max PWM pulse width in us
+   * @param deadbandMax The high end of the deadband range pulse width in us
+   * @param center The center (off) pulse width in us
+   * @param deadbandMin The low end of the deadband pulse width in us
+   * @param min The minimum pulse width in us
    */
-  public void setBounds(
-      double max, double deadbandMax, double center, double deadbandMin, double min) {
-    PWMJNI.setPWMConfig(m_handle, max, deadbandMax, center, deadbandMin, min);
+  public void setBoundsMicroseconds(
+      int max, int deadbandMax, int center, int deadbandMin, int min) {
+    PWMJNI.setPWMConfigMicroseconds(m_handle, max, deadbandMax, center, deadbandMin, min);
   }
 
   /**
@@ -121,8 +116,8 @@
    *
    * @return The bounds on the PWM pulse widths.
    */
-  public PWMConfigDataResult getRawBounds() {
-    return PWMJNI.getPWMConfigRaw(m_handle);
+  public PWMConfigDataResult getBoundsMicroseconds() {
+    return PWMJNI.getPWMConfigMicroseconds(m_handle);
   }
 
   /**
@@ -140,8 +135,7 @@
    * <p>This is intended to be used by servos.
    *
    * @param pos The position to set the servo between 0.0 and 1.0.
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre setBoundsMicroseconds() called.
    */
   public void setPosition(double pos) {
     PWMJNI.setPWMPosition(m_handle, pos);
@@ -153,8 +147,7 @@
    * <p>This is intended to be used by servos.
    *
    * @return The position the servo is set to between 0.0 and 1.0.
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre setBoundsMicroseconds() called.
    */
   public double getPosition() {
     return PWMJNI.getPWMPosition(m_handle);
@@ -166,11 +159,7 @@
    * <p>This is intended to be used by motor controllers.
    *
    * @param speed The speed to set the motor controller between -1.0 and 1.0.
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinPositivePwm() called.
-   * @pre SetCenterPwm() called.
-   * @pre SetMaxNegativePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre setBoundsMicroseconds() called.
    */
   public void setSpeed(double speed) {
     PWMJNI.setPWMSpeed(m_handle, speed);
@@ -182,10 +171,7 @@
    * <p>This is intended to be used by motor controllers.
    *
    * @return The most recently set speed between -1.0 and 1.0.
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinPositivePwm() called.
-   * @pre SetMaxNegativePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre setBoundsMicroseconds() called.
    */
   public double getSpeed() {
     return PWMJNI.getPWMSpeed(m_handle);
@@ -194,12 +180,12 @@
   /**
    * Set the PWM value directly to the hardware.
    *
-   * <p>Write a raw value to a PWM channel.
+   * <p>Write a microsecond pulse value to a PWM channel.
    *
-   * @param value Raw PWM value. Range 0 - 255.
+   * @param microsecondPulseTime Microsecond pulse PWM value. Range 0 - 4096.
    */
-  public void setRaw(int value) {
-    PWMJNI.setPWMRaw(m_handle, (short) value);
+  public void setPulseTimeMicroseconds(int microsecondPulseTime) {
+    PWMJNI.setPulseTimeMicroseconds(m_handle, microsecondPulseTime);
   }
 
   /**
@@ -207,10 +193,10 @@
    *
    * <p>Read a raw value from a PWM channel.
    *
-   * @return Raw PWM control value. Range: 0 - 255.
+   * @return Microsecond pulse PWM control value. Range: 0 - 4096.
    */
-  public int getRaw() {
-    return PWMJNI.getPWMRaw(m_handle);
+  public int getPulseTimeMicroseconds() {
+    return PWMJNI.getPulseTimeMicroseconds(m_handle);
   }
 
   /** Temporarily disables the PWM output. The next set call will re-enable the output. */
@@ -246,6 +232,11 @@
     PWMJNI.latchPWMZero(m_handle);
   }
 
+  /** Sets the PWM output to be a continuous high signal while enabled. */
+  public void setAlwaysHighMode() {
+    PWMJNI.setAlwaysHighMode(m_handle);
+  }
+
   /**
    * Get the underlying handle.
    *
@@ -260,6 +251,9 @@
     builder.setSmartDashboardType("PWM");
     builder.setActuator(true);
     builder.setSafeState(this::setDisabled);
-    builder.addDoubleProperty("Value", this::getRaw, value -> setRaw((int) value));
+    builder.addDoubleProperty(
+        "Value", this::getPulseTimeMicroseconds, value -> setPulseTimeMicroseconds((int) value));
+    builder.addDoubleProperty("Speed", this::getSpeed, this::setSpeed);
+    builder.addDoubleProperty("Position", this::getPosition, this::setPosition);
   }
 }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticHub.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticHub.java
index ff49b4a..b7df47f 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticHub.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticHub.java
@@ -277,9 +277,9 @@
    * below {@code minPressure} and will turn off when the pressure reaches {@code maxPressure}.
    *
    * @param minPressure The minimum pressure in PSI. The compressor will turn on when the pressure
-   *     drops below this value.
+   *     drops below this value. Range 0-120 PSI.
    * @param maxPressure The maximum pressure in PSI. The compressor will turn off when the pressure
-   *     reaches this value.
+   *     reaches this value. Range 0-120 PSI. Must be larger then minPressure.
    */
   @Override
   public void enableCompressorAnalog(double minPressure, double maxPressure) {
@@ -294,6 +294,10 @@
       throw new IllegalArgumentException(
           "maxPressure must be between 0 and 120 PSI, got " + maxPressure);
     }
+
+    // Send the voltage as it would be if the 5V rail was at exactly 5V.
+    // The firmware will compensate for the real 5V rail voltage, which
+    // can fluctuate somewhat over time.
     double minAnalogVoltage = psiToVolts(minPressure, 5);
     double maxAnalogVoltage = psiToVolts(maxPressure, 5);
     REVPHJNI.setClosedLoopControlAnalog(m_handle, minAnalogVoltage, maxAnalogVoltage);
@@ -320,10 +324,11 @@
    * </ul>
    *
    * @param minPressure The minimum pressure in PSI. The compressor will turn on when the pressure
-   *     drops below this value and the pressure switch indicates that the system is not full.
+   *     drops below this value and the pressure switch indicates that the system is not full. Range
+   *     0-120 PSI.
    * @param maxPressure The maximum pressure in PSI. The compressor will turn off when the pressure
    *     reaches this value or the pressure switch is disconnected or indicates that the system is
-   *     full.
+   *     full. Range 0-120 PSI. Must be larger then minPressure.
    */
   @Override
   public void enableCompressorHybrid(double minPressure, double maxPressure) {
@@ -338,6 +343,10 @@
       throw new IllegalArgumentException(
           "maxPressure must be between 0 and 120 PSI, got " + maxPressure);
     }
+
+    // Send the voltage as it would be if the 5V rail was at exactly 5V.
+    // The firmware will compensate for the real 5V rail voltage, which
+    // can fluctuate somewhat over time.
     double minAnalogVoltage = psiToVolts(minPressure, 5);
     double maxAnalogVoltage = psiToVolts(maxPressure, 5);
     REVPHJNI.setClosedLoopControlHybrid(m_handle, minAnalogVoltage, maxAnalogVoltage);
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsBase.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsBase.java
index 57dbfc6..d9b493b 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsBase.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsBase.java
@@ -211,16 +211,40 @@
    */
   void unreserveSolenoids(int mask);
 
+  /**
+   * Reserve the compressor.
+   *
+   * @return true if successful; false if compressor already reserved
+   */
   boolean reserveCompressor();
 
+  /** Unreserve the compressor. */
   void unreserveCompressor();
 
   @Override
   void close();
 
+  /**
+   * Create a solenoid object for the specified channel.
+   *
+   * @param channel solenoid channel
+   * @return Solenoid object
+   */
   Solenoid makeSolenoid(int channel);
 
+  /**
+   * Create a double solenoid object for the specified channels.
+   *
+   * @param forwardChannel solenoid channel for forward
+   * @param reverseChannel solenoid channel for reverse
+   * @return DoubleSolenoid object
+   */
   DoubleSolenoid makeDoubleSolenoid(int forwardChannel, int reverseChannel);
 
+  /**
+   * Create a compressor object.
+   *
+   * @return Compressor object
+   */
   Compressor makeCompressor();
 }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java
index a80fd45..087c156 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java
@@ -35,6 +35,7 @@
 public final class Preferences {
   /** The Preferences table name. */
   private static final String TABLE_NAME = "Preferences";
+
   /** The network table. */
   private static NetworkTable m_table;
 
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java
index e4fb962..2f59a3d 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java
@@ -16,6 +16,7 @@
 import edu.wpi.first.math.MathUsageId;
 import edu.wpi.first.networktables.MultiSubscriber;
 import edu.wpi.first.networktables.NetworkTableInstance;
+import edu.wpi.first.util.WPIUtilJNI;
 import edu.wpi.first.wpilibj.livewindow.LiveWindow;
 import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
 import edu.wpi.first.wpilibj.util.WPILibVersion;
@@ -28,11 +29,13 @@
 import java.util.function.Supplier;
 
 /**
- * Implement a Robot Program framework. The RobotBase class is intended to be subclassed by a user
- * creating a robot program. Overridden autonomous() and operatorControl() methods are called at the
- * appropriate time as the match proceeds. In the current implementation, the Autonomous code will
- * run to completion before the OperatorControl code could start. In the future the Autonomous code
- * might be spawned as a task, then killed at the end of the Autonomous period.
+ * Implement a Robot Program framework. The RobotBase class is intended to be subclassed to create a
+ * robot program. The user must implement {@link #startCompetition()}, which will be called once and
+ * is not expected to exit. The user must also implement {@link #endCompetition()}, which signals to
+ * the code in {@link #startCompetition()} that it should exit.
+ *
+ * <p>It is not recommended to subclass this class directly - instead subclass IterativeRobotBase or
+ * TimedRobot.
  */
 public abstract class RobotBase implements AutoCloseable {
   /** The ID of the main Java thread. */
@@ -71,7 +74,7 @@
 
           @Override
           public boolean isRoboRIO() {
-            return RobotBase.isReal();
+            return !RobotBase.isSimulation();
           }
         };
 
@@ -128,13 +131,18 @@
                 break;
             }
           }
+
+          @Override
+          public double getTimestamp() {
+            return WPIUtilJNI.now() * 1.0e-6;
+          }
         });
   }
 
   /**
-   * Constructor for a generic robot program. User code should be placed in the constructor that
-   * runs before the Autonomous or Operator Control period starts. The constructor will run to
-   * completion before Autonomous is entered.
+   * Constructor for a generic robot program. User code can be placed in the constructor that runs
+   * before the Autonomous or Operator Control period starts. The constructor will run to completion
+   * before Autonomous is entered.
    *
    * <p>This must be used to ensure that the communications code starts. In the future it would be
    * nice to put this code into its own task that loads on boot so ensure that it runs.
@@ -146,7 +154,7 @@
     setupMathShared();
     // subscribe to "" to force persistent values to propagate to local
     m_suball = new MultiSubscriber(inst, new String[] {""});
-    if (isReal()) {
+    if (!isSimulation()) {
       inst.startServer("/home/lvuser/networktables.json");
     } else {
       inst.startServer();
@@ -194,7 +202,7 @@
    * @return If the robot is running in simulation.
    */
   public static boolean isSimulation() {
-    return !isReal();
+    return getRuntimeType() == RuntimeType.kSimulation;
   }
 
   /**
@@ -210,7 +218,7 @@
   /**
    * Determine if the Robot is currently disabled.
    *
-   * @return True if the Robot is currently disabled by the field controls.
+   * @return True if the Robot is currently disabled by the Driver Station.
    */
   public boolean isDisabled() {
     return DriverStation.isDisabled();
@@ -219,14 +227,14 @@
   /**
    * Determine if the Robot is currently enabled.
    *
-   * @return True if the Robot is currently enabled by the field controls.
+   * @return True if the Robot is currently enabled by the Driver Station.
    */
   public boolean isEnabled() {
     return DriverStation.isEnabled();
   }
 
   /**
-   * Determine if the robot is currently in Autonomous mode as determined by the field controls.
+   * Determine if the robot is currently in Autonomous mode as determined by the Driver Station.
    *
    * @return True if the robot is currently operating Autonomously.
    */
@@ -235,8 +243,8 @@
   }
 
   /**
-   * Determine if the robot is current in Autonomous mode and enabled as determined by the field
-   * controls.
+   * Determine if the robot is currently in Autonomous mode and enabled as determined by the Driver
+   * Station.
    *
    * @return True if the robot is currently operating autonomously while enabled.
    */
@@ -245,7 +253,7 @@
   }
 
   /**
-   * Determine if the robot is currently in Test mode as determined by the driver station.
+   * Determine if the robot is currently in Test mode as determined by the Driver Station.
    *
    * @return True if the robot is currently operating in Test mode.
    */
@@ -254,8 +262,17 @@
   }
 
   /**
-   * Determine if the robot is currently in Operator Control mode as determined by the field
-   * controls.
+   * Determine if the robot is current in Test mode and enabled as determined by the Driver Station.
+   *
+   * @return True if the robot is currently operating in Test mode while enabled.
+   */
+  public boolean isTestEnabled() {
+    return DriverStation.isTestEnabled();
+  }
+
+  /**
+   * Determine if the robot is currently in Operator Control mode as determined by the Driver
+   * Station.
    *
    * @return True if the robot is currently operating in Tele-Op mode.
    */
@@ -264,8 +281,8 @@
   }
 
   /**
-   * Determine if the robot is current in Operator Control mode and enabled as determined by the
-   * field controls.
+   * Determine if the robot is currently in Operator Control mode and enabled as determined by the
+   * Driver Station.
    *
    * @return True if the robot is currently operating in Tele-Op mode while enabled.
    */
@@ -273,10 +290,13 @@
     return DriverStation.isTeleopEnabled();
   }
 
-  /** Provide an alternate "main loop" via startCompetition(). */
+  /**
+   * Start the main robot code. This function will be called once and should not exit until
+   * signalled by {@link #endCompetition()}
+   */
   public abstract void startCompetition();
 
-  /** Ends the main loop in startCompetition(). */
+  /** Ends the main loop in {@link #startCompetition()}. */
   public abstract void endCompetition();
 
   private static final ReentrantLock m_runMutex = new ReentrantLock();
@@ -317,7 +337,7 @@
     m_robotCopy = robot;
     m_runMutex.unlock();
 
-    if (isReal()) {
+    if (!isSimulation()) {
       final File file = new File("/tmp/frc_versions/FRC_Lib_Version.ini");
       try {
         if (file.exists() && !file.delete()) {
@@ -395,6 +415,9 @@
       throw new IllegalStateException("Failed to initialize. Terminating");
     }
 
+    // Force refresh DS data
+    DriverStation.refreshData();
+
     // Call a CameraServer JNI function to force OpenCV native library loading
     // Needed because all the OpenCV JNI functions don't have built in loading
     CameraServerJNI.enumerateSinks();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java
index 8bf9268..29bcad8 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java
@@ -58,6 +58,15 @@
   }
 
   /**
+   * Returns the team number configured for the robot controller.
+   *
+   * @return team number, or 0 if not found.
+   */
+  public static int getTeamNumber() {
+    return HALUtil.getTeamNumber();
+  }
+
+  /**
    * Read the microsecond timer from the FPGA.
    *
    * @return The current time in microseconds according to the FPGA.
@@ -104,6 +113,24 @@
   }
 
   /**
+   * Gets the current state of the Robot Signal Light (RSL).
+   *
+   * @return The current state of the RSL- true if on, false if off
+   */
+  public static boolean getRSLState() {
+    return HAL.getRSLState();
+  }
+
+  /**
+   * Gets if the system time is valid.
+   *
+   * @return True if the system time is valid, false otherwise
+   */
+  public static boolean isSystemTimeValid() {
+    return HAL.getSystemTimeValid();
+  }
+
+  /**
    * Get the input voltage to the robot controller.
    *
    * @return The controller input voltage value in Volts
@@ -140,6 +167,15 @@
   }
 
   /**
+   * Enables or disables the 3.3V rail.
+   *
+   * @param enabled whether to enable the 3.3V rail.
+   */
+  public static void setEnabled3V3(boolean enabled) {
+    PowerJNI.setUserEnabled3V3(enabled);
+  }
+
+  /**
    * Get the enabled state of the 3.3V rail. The rail may be disabled due to a controller brownout,
    * a short circuit on the rail, or controller over-voltage.
    *
@@ -177,6 +213,15 @@
   }
 
   /**
+   * Enables or disables the 5V rail.
+   *
+   * @param enabled whether to enable the 5V rail.
+   */
+  public static void setEnabled5V(boolean enabled) {
+    PowerJNI.setUserEnabled5V(enabled);
+  }
+
+  /**
    * Get the enabled state of the 5V rail. The rail may be disabled due to a controller brownout, a
    * short circuit on the rail, or controller over-voltage.
    *
@@ -214,6 +259,15 @@
   }
 
   /**
+   * Enables or disables the 6V rail.
+   *
+   * @param enabled whether to enable the 6V rail.
+   */
+  public static void setEnabled6V(boolean enabled) {
+    PowerJNI.setUserEnabled6V(enabled);
+  }
+
+  /**
    * Get the enabled state of the 6V rail. The rail may be disabled due to a controller brownout, a
    * short circuit on the rail, or controller over-voltage.
    *
@@ -253,6 +307,15 @@
   }
 
   /**
+   * Get the current CPU temperature in degrees Celsius.
+   *
+   * @return current CPU temperature in degrees Celsius
+   */
+  public static double getCPUTemp() {
+    return PowerJNI.getCPUTemp();
+  }
+
+  /**
    * Get the current status of the CAN bus.
    *
    * @return The status of the CAN bus
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/SPI.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/SPI.java
index 73a0b23..632770c 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/SPI.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/SPI.java
@@ -16,9 +16,9 @@
 public class SPI implements AutoCloseable {
   public enum Port {
     kOnboardCS0(SPIJNI.ONBOARD_CS0_PORT),
-    kOnboardCS1(SPIJNI.ONBOARD_CS0_PORT),
-    kOnboardCS2(SPIJNI.ONBOARD_CS0_PORT),
-    kOnboardCS3(SPIJNI.ONBOARD_CS0_PORT),
+    kOnboardCS1(SPIJNI.ONBOARD_CS1_PORT),
+    kOnboardCS2(SPIJNI.ONBOARD_CS2_PORT),
+    kOnboardCS3(SPIJNI.ONBOARD_CS3_PORT),
     kMXP(SPIJNI.MXP_PORT);
 
     public final int value;
@@ -29,9 +29,13 @@
   }
 
   public enum Mode {
+    /** Clock idle low, data sampled on rising edge. */
     kMode0(SPIJNI.SPI_MODE0),
+    /** Clock idle low, data sampled on falling edge. */
     kMode1(SPIJNI.SPI_MODE1),
+    /** Clock idle high, data sampled on falling edge. */
     kMode2(SPIJNI.SPI_MODE2),
+    /** Clock idle high, data sampled on rising edge. */
     kMode3(SPIJNI.SPI_MODE3);
 
     public final int value;
@@ -84,76 +88,6 @@
   }
 
   /**
-   * Configure the order that bits are sent and received on the wire to be the most significant bit
-   * first.
-   *
-   * @deprecated Does not work, will be removed.
-   */
-  @Deprecated(since = "2023", forRemoval = true)
-  public final void setMSBFirst() {
-    DriverStation.reportWarning("setMSBFirst not supported by roboRIO", false);
-  }
-
-  /**
-   * Configure the order that bits are sent and received on the wire to be the least significant bit
-   * first.
-   *
-   * @deprecated Does not work, will be removed.
-   */
-  @Deprecated(since = "2023", forRemoval = true)
-  public final void setLSBFirst() {
-    DriverStation.reportWarning("setLSBFirst not supported by roboRIO", false);
-  }
-
-  /**
-   * Configure the clock output line to be active low. This is sometimes called clock polarity high
-   * or clock idle high.
-   *
-   * @deprecated Use setMode() instead.
-   */
-  @Deprecated(since = "2023", forRemoval = true)
-  public final void setClockActiveLow() {
-    m_mode |= 1;
-    SPIJNI.spiSetMode(m_port, m_mode);
-  }
-
-  /**
-   * Configure the clock output line to be active high. This is sometimes called clock polarity low
-   * or clock idle low.
-   *
-   * @deprecated Use setMode() instead.
-   */
-  @Deprecated(since = "2023", forRemoval = true)
-  public final void setClockActiveHigh() {
-    m_mode &= 1;
-    SPIJNI.spiSetMode(m_port, m_mode);
-  }
-
-  /**
-   * Configure that the data is stable on the leading edge and the data changes on the trailing
-   * edge.
-   *
-   * @deprecated Use setMode() instead.
-   */
-  @Deprecated(since = "2023", forRemoval = true)
-  public final void setSampleDataOnLeadingEdge() {
-    m_mode &= 2;
-    SPIJNI.spiSetMode(m_port, m_mode);
-  }
-
-  /**
-   * Configure that the data is stable on the trailing edge and the data changes on the leading
-   * edge.
-   *
-   * @deprecated Use setMode() instead.
-   */
-  @Deprecated(since = "2023", forRemoval = true)
-  public final void setSampleDataOnTrailingEdge() {
-    m_mode |= 2;
-    SPIJNI.spiSetMode(m_port, m_mode);
-  }
-
-  /**
    * Sets the mode for the SPI device.
    *
    * <p>Mode 0 is Clock idle low, data sampled on rising edge.
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Servo.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Servo.java
index f45eec5..1f1c747 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Servo.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Servo.java
@@ -19,8 +19,8 @@
   private static final double kMaxServoAngle = 180.0;
   private static final double kMinServoAngle = 0.0;
 
-  protected static final double kDefaultMaxServoPWM = 2.4;
-  protected static final double kDefaultMinServoPWM = 0.6;
+  protected static final int kDefaultMaxServoPWM = 2400;
+  protected static final int kDefaultMinServoPWM = 600;
 
   /**
    * Constructor.<br>
@@ -33,7 +33,7 @@
    */
   public Servo(final int channel) {
     super(channel);
-    setBounds(kDefaultMaxServoPWM, 0, 0, 0, kDefaultMinServoPWM);
+    setBoundsMicroseconds(kDefaultMaxServoPWM, 0, 0, 0, kDefaultMinServoPWM);
     setPeriodMultiplier(PeriodMultiplier.k4X);
 
     HAL.report(tResourceType.kResourceType_Servo, getChannel() + 1);
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Solenoid.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Solenoid.java
index bb9be1a..be87444 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Solenoid.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Solenoid.java
@@ -117,11 +117,16 @@
   }
 
   /**
-   * Set the pulse duration in the PCM. This is used in conjunction with the startPulse method to
-   * allow the PCM to control the timing of a pulse. The timing can be controlled in 0.01 second
-   * increments.
+   * Set the pulse duration in the pneumatics module. This is used in conjunction with the
+   * startPulse method to allow the pneumatics module to control the timing of a pulse.
    *
-   * @param durationSeconds The duration of the pulse, from 0.01 to 2.55 seconds.
+   * <p>On the PCM, the timing can be controlled in 0.01 second increments, with a maximum of 2.55
+   * seconds.
+   *
+   * <p>On the PH, the timing can be controlled in 0.001 second increments, with a maximum of 65.534
+   * seconds.
+   *
+   * @param durationSeconds The duration of the pulse in seconds.
    * @see #startPulse()
    */
   public void setPulseDuration(double durationSeconds) {
@@ -130,7 +135,7 @@
   }
 
   /**
-   * Trigger the PCM to generate a pulse of the duration set in setPulseDuration.
+   * Trigger the pneumatics module to generate a pulse of the duration set in setPulseDuration.
    *
    * @see #setPulseDuration(double)
    */
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Timer.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Timer.java
index 6f9a361..4f948f5 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Timer.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Timer.java
@@ -101,6 +101,19 @@
   }
 
   /**
+   * Restart the timer by stopping the timer, if it is not already stopped, resetting the
+   * accumulated time, then starting the timer again. If you want an event to periodically reoccur
+   * at some time interval from the start time, consider using advanceIfElapsed() instead.
+   */
+  public void restart() {
+    if (m_running) {
+      stop();
+    }
+    reset();
+    start();
+  }
+
+  /**
    * Stop the timer. This computes the time as of now and clears the running flag, causing all
    * subsequent time requests to be read from the accumulated time rather than looking at the system
    * clock.
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java
index d7b326a..fd2b05b 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java
@@ -90,9 +90,6 @@
       m_pingChannel.setSimDevice(m_simDevice);
       m_echoChannel.setSimDevice(m_simDevice);
     }
-    if (m_task == null) {
-      m_task = new UltrasonicChecker();
-    }
     final boolean originalMode = m_automaticEnabled;
     setAutomaticMode(false); // kill task when adding a new sensor
     m_sensors.add(this);
@@ -202,7 +199,7 @@
    *     sensors fire at the same time. If another scheduling algorithm is preferred, it can be
    *     implemented by pinging the sensors manually and waiting for the results to come back.
    */
-  public static void setAutomaticMode(boolean enabling) {
+  public static synchronized void setAutomaticMode(boolean enabling) {
     if (enabling == m_automaticEnabled) {
       return; // ignore the case of no change
     }
@@ -217,14 +214,18 @@
       }
 
       // Start round robin task
+      m_task = new UltrasonicChecker();
       m_task.start();
     } else {
-      // Wait for background task to stop running
-      try {
-        m_task.join();
-      } catch (InterruptedException ex) {
-        Thread.currentThread().interrupt();
-        ex.printStackTrace();
+      if (m_task != null) {
+        // Wait for background task to stop running
+        try {
+          m_task.join();
+          m_task = null;
+        } catch (InterruptedException ex) {
+          Thread.currentThread().interrupt();
+          ex.printStackTrace();
+        }
       }
 
       /* Clear all the counters (data now invalid) since automatic mode is
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/XboxController.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/XboxController.java
index 47195eb..c346ae3 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/XboxController.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/XboxController.java
@@ -539,13 +539,13 @@
   }
 
   /**
-   * Constructs an event instance around the axis value of the right trigger. The returned trigger
+   * Constructs an event instance around the axis value of the left trigger. The returned trigger
    * will be true when the axis value is greater than {@code threshold}.
    *
    * @param threshold the minimum axis value for the returned {@link BooleanEvent} to be true. This
    *     value should be in the range [0, 1] where 0 is the unpressed state of the axis.
    * @param loop the event loop instance to attach the event to.
-   * @return an event instance that is true when the right trigger's axis exceeds the provided
+   * @return an event instance that is true when the left trigger's axis exceeds the provided
    *     threshold, attached to the given event loop
    */
   public BooleanEvent leftTrigger(double threshold, EventLoop loop) {
@@ -553,11 +553,11 @@
   }
 
   /**
-   * Constructs an event instance around the axis value of the right trigger. The returned trigger
+   * Constructs an event instance around the axis value of the left trigger. The returned trigger
    * will be true when the axis value is greater than 0.5.
    *
    * @param loop the event loop instance to attach the event to.
-   * @return an event instance that is true when the right trigger's axis exceeds the provided
+   * @return an event instance that is true when the left trigger's axis exceeds the provided
    *     threshold, attached to the given event loop
    */
   public BooleanEvent leftTrigger(EventLoop loop) {
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java
index 4b12682..9a1f2ba 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java
@@ -366,7 +366,6 @@
     builder.setActuator(true);
     builder.setSafeState(this::stopMotor);
     builder.addDoubleProperty("Left Motor Speed", m_leftMotor::get, m_leftMotor::set);
-    builder.addDoubleProperty(
-        "Right Motor Speed", () -> m_rightMotor.get(), x -> m_rightMotor.set(x));
+    builder.addDoubleProperty("Right Motor Speed", m_rightMotor::get, m_rightMotor::set);
   }
 }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java
index 1ee160c..181e275 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java
@@ -152,8 +152,8 @@
    * <p>Angles are measured counterclockwise from the positive X axis. The robot's speed is
    * independent of its angle or rotation rate.
    *
-   * @param xSpeed The robot's speed along the Y axis [-1.0..1.0]. Forward is positive.
-   * @param ySpeed The robot's speed along the X axis [-1.0..1.0]. Left is positive.
+   * @param xSpeed The robot's speed along the X axis [-1.0..1.0]. Forward is positive.
+   * @param ySpeed The robot's speed along the Y axis [-1.0..1.0]. Left is positive.
    * @param zRotation The robot's rotation rate around the Z axis [-1.0..1.0]. Counterclockwise is
    *     positive.
    * @param gyroAngle The gyro heading around the Z axis. Use this to implement field-oriented
@@ -275,13 +275,9 @@
     builder.addDoubleProperty(
         "Front Left Motor Speed", m_frontLeftMotor::get, m_frontLeftMotor::set);
     builder.addDoubleProperty(
-        "Front Right Motor Speed",
-        () -> m_frontRightMotor.get(),
-        value -> m_frontRightMotor.set(value));
+        "Front Right Motor Speed", m_frontRightMotor::get, m_frontRightMotor::set);
     builder.addDoubleProperty("Rear Left Motor Speed", m_rearLeftMotor::get, m_rearLeftMotor::set);
     builder.addDoubleProperty(
-        "Rear Right Motor Speed",
-        () -> m_rearRightMotor.get(),
-        value -> m_rearRightMotor.set(value));
+        "Rear Right Motor Speed", m_rearRightMotor::get, m_rearRightMotor::set);
   }
 }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/BooleanEvent.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/BooleanEvent.java
index a810006..c8f5846 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/BooleanEvent.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/BooleanEvent.java
@@ -7,6 +7,7 @@
 import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
 
 import edu.wpi.first.math.filter.Debouncer;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiFunction;
 import java.util.function.BooleanSupplier;
 
@@ -24,9 +25,13 @@
 public class BooleanEvent implements BooleanSupplier {
   /** Poller loop. */
   protected final EventLoop m_loop;
+
   /** Condition. */
   private final BooleanSupplier m_signal;
 
+  /** The state of the condition in the current loop poll. Nightmare to manage. */
+  private final AtomicBoolean m_state = new AtomicBoolean(false);
+
   /**
    * Creates a new event with the given signal determining whether it is active.
    *
@@ -36,16 +41,19 @@
   public BooleanEvent(EventLoop loop, BooleanSupplier signal) {
     m_loop = requireNonNullParam(loop, "loop", "BooleanEvent");
     m_signal = requireNonNullParam(signal, "signal", "BooleanEvent");
+    m_state.set(m_signal.getAsBoolean());
+    m_loop.bind(() -> m_state.set(m_signal.getAsBoolean()));
   }
 
   /**
-   * Check the state of this signal (high or low).
+   * Check the state of this signal (high or low) as of the last loop poll.
    *
-   * @return true for the high state, false for the low state.
+   * @return true for the high state, false for the low state. If the event was never polled, it
+   *     returns the state at event construction.
    */
   @Override
   public final boolean getAsBoolean() {
-    return m_signal.getAsBoolean();
+    return m_state.get();
   }
 
   /**
@@ -56,7 +64,7 @@
   public final void ifHigh(Runnable action) {
     m_loop.bind(
         () -> {
-          if (m_signal.getAsBoolean()) {
+          if (m_state.get()) {
             action.run();
           }
         });
@@ -71,11 +79,11 @@
     return new BooleanEvent(
         m_loop,
         new BooleanSupplier() {
-          private boolean m_previous = m_signal.getAsBoolean();
+          private boolean m_previous = m_state.get();
 
           @Override
           public boolean getAsBoolean() {
-            boolean present = m_signal.getAsBoolean();
+            boolean present = m_state.get();
             boolean ret = !m_previous && present;
             m_previous = present;
             return ret;
@@ -92,11 +100,11 @@
     return new BooleanEvent(
         m_loop,
         new BooleanSupplier() {
-          private boolean m_previous = m_signal.getAsBoolean();
+          private boolean m_previous = m_state.get();
 
           @Override
           public boolean getAsBoolean() {
-            boolean present = m_signal.getAsBoolean();
+            boolean present = m_state.get();
             boolean ret = m_previous && !present;
             m_previous = present;
             return ret;
@@ -131,7 +139,7 @@
 
           @Override
           public boolean getAsBoolean() {
-            return m_debouncer.calculate(m_signal.getAsBoolean());
+            return m_debouncer.calculate(m_state.get());
           }
         });
   }
@@ -143,35 +151,37 @@
    * @return the negated event
    */
   public BooleanEvent negate() {
-    return new BooleanEvent(m_loop, () -> !m_signal.getAsBoolean());
+    return new BooleanEvent(m_loop, () -> !m_state.get());
   }
 
   /**
    * Composes this event with another event, returning a new signal that is in the high state when
    * both signals are in the high state.
    *
-   * <p>The new event will use this event's polling loop.
+   * <p>The events must use the same event loop. If the events use different event loops, the
+   * composed signal won't update until both loops are polled.
    *
    * @param other the event to compose with
    * @return the event that is active when both events are active
    */
   public BooleanEvent and(BooleanSupplier other) {
     requireNonNullParam(other, "other", "and");
-    return new BooleanEvent(m_loop, () -> m_signal.getAsBoolean() && other.getAsBoolean());
+    return new BooleanEvent(m_loop, () -> m_state.get() && other.getAsBoolean());
   }
 
   /**
    * Composes this event with another event, returning a new signal that is high when either signal
    * is high.
    *
-   * <p>The new event will use this event's polling loop.
+   * <p>The events must use the same event loop. If the events use different event loops, the
+   * composed signal won't update until both loops are polled.
    *
    * @param other the event to compose with
    * @return a signal that is high when either signal is high.
    */
   public BooleanEvent or(BooleanSupplier other) {
     requireNonNullParam(other, "other", "or");
-    return new BooleanEvent(m_loop, () -> m_signal.getAsBoolean() || other.getAsBoolean());
+    return new BooleanEvent(m_loop, () -> m_state.get() || other.getAsBoolean());
   }
 
   /**
@@ -184,6 +194,6 @@
    * @return an instance of the subclass.
    */
   public <T extends BooleanSupplier> T castTo(BiFunction<EventLoop, BooleanSupplier, T> ctor) {
-    return ctor.apply(m_loop, m_signal);
+    return ctor.apply(m_loop, m_state::get);
   }
 }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java
index 3e92c01..3e220e0 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java
@@ -7,12 +7,14 @@
 import java.util.Collection;
 import java.util.LinkedHashSet;
 
-/** The loop polling {@link BooleanEvent} objects and executing the actions bound to them. */
+/**
+ * A declarative way to bind a set of actions to a loop and execute them when the loop is polled.
+ */
 public final class EventLoop {
   private final Collection<Runnable> m_bindings = new LinkedHashSet<>();
 
   /**
-   * Bind a new action to run whenever the condition is true.
+   * Bind a new action to run when the loop is polled.
    *
    * @param action the action to run.
    */
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Accelerometer.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Accelerometer.java
index 3cfd27d..d33b7ae 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Accelerometer.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Accelerometer.java
@@ -4,7 +4,12 @@
 
 package edu.wpi.first.wpilibj.interfaces;
 
-/** Interface for 3-axis accelerometers. */
+/**
+ * Interface for 3-axis accelerometers.
+ *
+ * @deprecated This interface is being removed with no replacement.
+ */
+@Deprecated(since = "2024", forRemoval = true)
 public interface Accelerometer {
   enum Range {
     k2G,
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java
index 660aad8..dad85ca 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java
@@ -6,7 +6,12 @@
 
 import edu.wpi.first.math.geometry.Rotation2d;
 
-/** Interface for yaw rate gyros. */
+/**
+ * Interface for yaw rate gyros.
+ *
+ * @deprecated This interface is being removed with no replacement.
+ */
+@Deprecated(since = "2024", forRemoval = true)
 public interface Gyro extends AutoCloseable {
   /**
    * Calibrate the gyro. It's important to make sure that the robot is not moving while the
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java
index a47f866..ac8ef1d 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java
@@ -86,7 +86,7 @@
    * enable all the components registered for LiveWindow. If it's being disabled, stop all the
    * registered components and re-enable the scheduler.
    *
-   * <p>TODO: add code to disable PID loops when enabling LiveWindow. The commands should reenable
+   * <p>TODO: add code to disable PID loops when enabling LiveWindow. The commands should re-enable
    * the PID loops themselves when they get rescheduled. This prevents arms from starting to move
    * around, etc. after a period of adjusting them in LiveWindow mode.
    *
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/DMC60.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/DMC60.java
index c003b56..5f0f26b 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/DMC60.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/DMC60.java
@@ -35,7 +35,7 @@
   public DMC60(final int channel) {
     super("DMC60", channel);
 
-    m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+    m_pwm.setBoundsMicroseconds(2004, 1520, 1500, 1480, 997);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Jaguar.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Jaguar.java
index 32e6417..f072067 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Jaguar.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Jaguar.java
@@ -34,7 +34,7 @@
   public Jaguar(final int channel) {
     super("Jaguar", channel);
 
-    m_pwm.setBounds(2.31, 1.55, 1.507, 1.454, 0.697);
+    m_pwm.setBoundsMicroseconds(2310, 1550, 1507, 1454, 697);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java
index 13daf68..25c29ac 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java
@@ -67,7 +67,7 @@
     if (!m_disabled) {
       m_speed = speed;
       m_dio.updateDutyCycle(0.5 + 0.5 * (m_isInverted ? -speed : speed));
-      m_pwm.setRaw(0xffff);
+      m_pwm.setAlwaysHighMode();
     }
 
     feed();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMSparkMax.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMSparkMax.java
index e90f60d..ede5f5d 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMSparkMax.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMSparkMax.java
@@ -34,7 +34,7 @@
   public PWMSparkMax(final int channel) {
     super("PWMSparkMax", channel);
 
-    m_pwm.setBounds(2.003, 1.55, 1.50, 1.46, 0.999);
+    m_pwm.setBoundsMicroseconds(2003, 1550, 1500, 1460, 999);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonFX.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonFX.java
index d521745..29ac766 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonFX.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonFX.java
@@ -35,7 +35,7 @@
   public PWMTalonFX(final int channel) {
     super("PWMTalonFX", channel);
 
-    m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+    m_pwm.setBoundsMicroseconds(2004, 1520, 1500, 1480, 997);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonSRX.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonSRX.java
index 81f1834..085cd86 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonSRX.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonSRX.java
@@ -35,7 +35,7 @@
   public PWMTalonSRX(final int channel) {
     super("PWMTalonSRX", channel);
 
-    m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+    m_pwm.setBoundsMicroseconds(2004, 1520, 1500, 1480, 997);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVenom.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVenom.java
index 9f7a885..a38e936 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVenom.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVenom.java
@@ -34,7 +34,7 @@
   public PWMVenom(final int channel) {
     super("PWMVenom", channel);
 
-    m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+    m_pwm.setBoundsMicroseconds(2004, 1520, 1500, 1480, 997);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVictorSPX.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVictorSPX.java
index 9880464..555614a 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVictorSPX.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVictorSPX.java
@@ -35,7 +35,7 @@
   public PWMVictorSPX(final int channel) {
     super("PWMVictorSPX", channel);
 
-    m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+    m_pwm.setBoundsMicroseconds(2004, 1520, 1500, 1480, 997);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/SD540.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/SD540.java
index 3876dfc..7cdb035 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/SD540.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/SD540.java
@@ -35,7 +35,7 @@
   public SD540(final int channel) {
     super("SD540", channel);
 
-    m_pwm.setBounds(2.05, 1.55, 1.50, 1.44, 0.94);
+    m_pwm.setBoundsMicroseconds(2050, 1550, 1500, 1440, 940);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Spark.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Spark.java
index 1b99228..cc92621 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Spark.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Spark.java
@@ -35,7 +35,7 @@
   public Spark(final int channel) {
     super("Spark", channel);
 
-    m_pwm.setBounds(2.003, 1.55, 1.50, 1.46, 0.999);
+    m_pwm.setBoundsMicroseconds(2003, 1550, 1500, 1460, 999);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Talon.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Talon.java
index 576ba6a..dd9e473 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Talon.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Talon.java
@@ -34,7 +34,7 @@
   public Talon(final int channel) {
     super("Talon", channel);
 
-    m_pwm.setBounds(2.037, 1.539, 1.513, 1.487, 0.989);
+    m_pwm.setBoundsMicroseconds(2037, 1539, 1513, 1487, 989);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Victor.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Victor.java
index fac6c4f..5265a8e 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Victor.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Victor.java
@@ -37,7 +37,7 @@
   public Victor(final int channel) {
     super("Victor", channel);
 
-    m_pwm.setBounds(2.027, 1.525, 1.507, 1.49, 1.026);
+    m_pwm.setBoundsMicroseconds(2027, 1525, 1507, 1490, 1026);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k2X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/VictorSP.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/VictorSP.java
index 10dba67..aa262d0 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/VictorSP.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/VictorSP.java
@@ -35,7 +35,7 @@
   public VictorSP(final int channel) {
     super("VictorSP", channel);
 
-    m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+    m_pwm.setBoundsMicroseconds(2004, 1520, 1500, 1480, 997);
     m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X);
     m_pwm.setSpeed(0.0);
     m_pwm.setZeroLatch();
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java
index 9c96a2f..c034a4a 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java
@@ -4,8 +4,6 @@
 
 package edu.wpi.first.wpilibj.shuffleboard;
 
-import edu.wpi.first.wpilibj.interfaces.Accelerometer.Range;
-
 /**
  * The types of the widgets bundled with Shuffleboard.
  *
@@ -362,7 +360,6 @@
    *
    * <table>
    * <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
-   * <tr><td>Range</td><td>{@link Range}</td><td>k16G</td><td>The accelerometer range</td></tr>
    * <tr><td>Show value</td><td>Boolean</td><td>true</td>
    * <td>Show or hide the acceleration values</td></tr>
    * <tr><td>Precision</td><td>Number</td><td>2</td>
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardInstance.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardInstance.java
index 6c03ded..df2f117 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardInstance.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardInstance.java
@@ -10,6 +10,7 @@
 import edu.wpi.first.hal.HAL;
 import edu.wpi.first.networktables.NetworkTable;
 import edu.wpi.first.networktables.NetworkTableInstance;
+import edu.wpi.first.networktables.PubSubOption;
 import edu.wpi.first.networktables.StringPublisher;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -32,7 +33,8 @@
     requireNonNullParam(ntInstance, "ntInstance", "ShuffleboardInstance");
     m_rootTable = ntInstance.getTable(Shuffleboard.kBaseTableName);
     m_rootMetaTable = m_rootTable.getSubTable(".metadata");
-    m_selectedTabPub = m_rootMetaTable.getStringTopic("Selected").publish();
+    m_selectedTabPub =
+        m_rootMetaTable.getStringTopic("Selected").publish(PubSubOption.keepDuplicates(true));
     HAL.report(tResourceType.kResourceType_Shuffleboard, 0);
   }
 
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DCMotorSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DCMotorSim.java
index 48c5a55..5561fc1 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DCMotorSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DCMotorSim.java
@@ -5,6 +5,7 @@
 package edu.wpi.first.wpilibj.simulation;
 
 import edu.wpi.first.math.Matrix;
+import edu.wpi.first.math.VecBuilder;
 import edu.wpi.first.math.numbers.N1;
 import edu.wpi.first.math.numbers.N2;
 import edu.wpi.first.math.system.LinearSystem;
@@ -23,7 +24,9 @@
   /**
    * Creates a simulated DC motor mechanism.
    *
-   * @param plant The linear system that represents the DC motor.
+   * @param plant The linear system representing the DC motor. This system can be created with
+   *     {@link edu.wpi.first.math.system.plant.LinearSystemId#createDCMotorSystem(DCMotor, double,
+   *     double)}.
    * @param gearbox The type of and number of motors in the DC motor gearbox.
    * @param gearing The gearing of the DC motor (numbers greater than 1 represent reductions).
    */
@@ -36,7 +39,7 @@
   /**
    * Creates a simulated DC motor mechanism.
    *
-   * @param plant The linear system that represents the DC motor.
+   * @param plant The linear system representing the DC motor. This system can be created with
    * @param gearbox The type of and number of motors in the DC motor gearbox.
    * @param gearing The gearing of the DC motor (numbers greater than 1 represent reductions).
    * @param measurementStdDevs The standard deviations of the measurements.
@@ -83,6 +86,16 @@
   }
 
   /**
+   * Sets the state of the DC motor.
+   *
+   * @param angularPositionRad The new position in radians.
+   * @param angularVelocityRadPerSec The new velocity in radians per second.
+   */
+  public void setState(double angularPositionRad, double angularVelocityRadPerSec) {
+    setState(VecBuilder.fill(angularPositionRad, angularVelocityRadPerSec));
+  }
+
+  /**
    * Returns the DC motor position.
    *
    * @return The DC motor position.
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java
index 7a746a2..94db6f6 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java
@@ -52,7 +52,7 @@
   private final LinearSystem<N2, N2, N2> m_plant;
 
   /**
-   * Create a SimDrivetrain.
+   * Creates a simulated differential drivetrain.
    *
    * @param driveMotor A {@link DCMotor} representing the left side of the drivetrain.
    * @param gearing The gearing ratio between motor and wheel, as output over input. This must be
@@ -91,10 +91,10 @@
   }
 
   /**
-   * Create a SimDrivetrain .
+   * Creates a simulated differential drivetrain.
    *
-   * @param drivetrainPlant The {@link LinearSystem} representing the robot's drivetrain. This
-   *     system can be created with {@link
+   * @param plant The {@link LinearSystem} representing the robot's drivetrain. This system can be
+   *     created with {@link
    *     edu.wpi.first.math.system.plant.LinearSystemId#createDrivetrainVelocitySystem(DCMotor,
    *     double, double, double, double, double)} or {@link
    *     edu.wpi.first.math.system.plant.LinearSystemId#identifyDrivetrainSystem(double, double,
@@ -112,13 +112,13 @@
    *     point.
    */
   public DifferentialDrivetrainSim(
-      LinearSystem<N2, N2, N2> drivetrainPlant,
+      LinearSystem<N2, N2, N2> plant,
       DCMotor driveMotor,
       double gearing,
       double trackWidthMeters,
       double wheelRadiusMeters,
       Matrix<N7, N1> measurementStdDevs) {
-    this.m_plant = drivetrainPlant;
+    this.m_plant = plant;
     this.m_rb = trackWidthMeters / 2.0;
     this.m_motor = driveMotor;
     this.m_originalGearing = gearing;
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DriverStationSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DriverStationSim.java
index a429e71..df1c2a2 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DriverStationSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DriverStationSim.java
@@ -229,20 +229,22 @@
    */
   public static AllianceStationID getAllianceStationId() {
     switch (DriverStationDataJNI.getAllianceStationId()) {
-      case 0:
+      case DriverStationJNI.kUnknownAllianceStation:
+        return AllianceStationID.Unknown;
+      case DriverStationJNI.kRed1AllianceStation:
         return AllianceStationID.Red1;
-      case 1:
+      case DriverStationJNI.kRed2AllianceStation:
         return AllianceStationID.Red2;
-      case 2:
+      case DriverStationJNI.kRed3AllianceStation:
         return AllianceStationID.Red3;
-      case 3:
+      case DriverStationJNI.kBlue1AllianceStation:
         return AllianceStationID.Blue1;
-      case 4:
+      case DriverStationJNI.kBlue2AllianceStation:
         return AllianceStationID.Blue2;
-      case 5:
+      case DriverStationJNI.kBlue3AllianceStation:
         return AllianceStationID.Blue3;
       default:
-        return null;
+        return AllianceStationID.Unknown;
     }
   }
 
@@ -254,23 +256,26 @@
   public static void setAllianceStationId(AllianceStationID allianceStationId) {
     int allianceStation;
     switch (allianceStationId) {
+      case Unknown:
+        allianceStation = DriverStationJNI.kUnknownAllianceStation;
+        break;
       case Red1:
-        allianceStation = 0;
+        allianceStation = DriverStationJNI.kRed1AllianceStation;
         break;
       case Red2:
-        allianceStation = 1;
+        allianceStation = DriverStationJNI.kRed2AllianceStation;
         break;
       case Red3:
-        allianceStation = 2;
+        allianceStation = DriverStationJNI.kRed3AllianceStation;
         break;
       case Blue1:
-        allianceStation = 3;
+        allianceStation = DriverStationJNI.kBlue1AllianceStation;
         break;
       case Blue2:
-        allianceStation = 4;
+        allianceStation = DriverStationJNI.kBlue2AllianceStation;
         break;
       case Blue3:
-        allianceStation = 5;
+        allianceStation = DriverStationJNI.kBlue3AllianceStation;
         break;
       default:
         return;
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DutyCycleEncoderSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DutyCycleEncoderSim.java
index f5c900c..3c2c741 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DutyCycleEncoderSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DutyCycleEncoderSim.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj.simulation;
 
+import edu.wpi.first.hal.SimBoolean;
 import edu.wpi.first.hal.SimDouble;
 import edu.wpi.first.wpilibj.DutyCycleEncoder;
 
@@ -11,6 +12,8 @@
 public class DutyCycleEncoderSim {
   private final SimDouble m_simPosition;
   private final SimDouble m_simDistancePerRotation;
+  private final SimDouble m_simAbsolutePosition;
+  private final SimBoolean m_simIsConnected;
 
   /**
    * Constructs from an DutyCycleEncoder object.
@@ -18,10 +21,29 @@
    * @param encoder DutyCycleEncoder to simulate
    */
   public DutyCycleEncoderSim(DutyCycleEncoder encoder) {
-    SimDeviceSim wrappedSimDevice =
-        new SimDeviceSim("DutyCycle:DutyCycleEncoder" + "[" + encoder.getSourceChannel() + "]");
+    this(encoder.getSourceChannel());
+  }
+
+  /**
+   * Constructs from a digital input channel.
+   *
+   * @param channel digital input channel.
+   */
+  public DutyCycleEncoderSim(int channel) {
+    SimDeviceSim wrappedSimDevice = new SimDeviceSim("DutyCycle:DutyCycleEncoder", channel);
     m_simPosition = wrappedSimDevice.getDouble("position");
     m_simDistancePerRotation = wrappedSimDevice.getDouble("distance_per_rot");
+    m_simAbsolutePosition = wrappedSimDevice.getDouble("absPosition");
+    m_simIsConnected = wrappedSimDevice.getBoolean("connected");
+  }
+
+  /**
+   * Get the position in turns.
+   *
+   * @return The position.
+   */
+  public double get() {
+    return m_simPosition.get();
   }
 
   /**
@@ -34,11 +56,65 @@
   }
 
   /**
-   * Set the position.
+   * Get the distance.
    *
-   * @param distance The position.
+   * @return The distance.
+   */
+  public double getDistance() {
+    return m_simPosition.get() * m_simDistancePerRotation.get();
+  }
+
+  /**
+   * Set the distance.
+   *
+   * @param distance The distance.
    */
   public void setDistance(double distance) {
     m_simPosition.set(distance / m_simDistancePerRotation.get());
   }
+
+  /**
+   * Get the absolute position.
+   *
+   * @return The absolute position
+   */
+  public double getAbsolutePosition() {
+    return m_simAbsolutePosition.get();
+  }
+
+  /**
+   * Set the absolute position.
+   *
+   * @param position The absolute position
+   */
+  public void setAbsolutePosition(double position) {
+    m_simAbsolutePosition.set(position);
+  }
+
+  /**
+   * Get the distance per rotation for this encoder.
+   *
+   * @return The scale factor that will be used to convert rotation to useful units.
+   */
+  public double getDistancePerRotation() {
+    return m_simDistancePerRotation.get();
+  }
+
+  /**
+   * Get if the encoder is connected.
+   *
+   * @return true if the encoder is connected.
+   */
+  public boolean getConnected() {
+    return m_simIsConnected.get();
+  }
+
+  /**
+   * Set if the encoder is connected.
+   *
+   * @param isConnected Whether or not the sensor is connected.
+   */
+  public void setConnected(boolean isConnected) {
+    m_simIsConnected.set(isConnected);
+  }
 }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ElevatorSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ElevatorSim.java
index 025260a..322d897 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ElevatorSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ElevatorSim.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj.simulation;
 
+import edu.wpi.first.math.MathUtil;
 import edu.wpi.first.math.Matrix;
 import edu.wpi.first.math.VecBuilder;
 import edu.wpi.first.math.numbers.N1;
@@ -18,12 +19,6 @@
   // Gearbox for the elevator.
   private final DCMotor m_gearbox;
 
-  // Gearing between the motors and the output.
-  private final double m_gearing;
-
-  // The radius of the drum that the elevator spool is wrapped around.
-  private final double m_drumRadius;
-
   // The min allowable height for the elevator.
   private final double m_minHeight;
 
@@ -36,61 +31,121 @@
   /**
    * Creates a simulated elevator mechanism.
    *
-   * @param plant The linear system that represents the elevator.
+   * @param plant The linear system that represents the elevator. This system can be created with
+   *     {@link edu.wpi.first.math.system.plant.LinearSystemId#createElevatorSystem(DCMotor, double,
+   *     double, double)}.
    * @param gearbox The type of and number of motors in the elevator gearbox.
-   * @param gearing The gearing of the elevator (numbers greater than 1 represent reductions).
-   * @param drumRadiusMeters The radius of the drum that the elevator spool is wrapped around.
    * @param minHeightMeters The min allowable height of the elevator.
    * @param maxHeightMeters The max allowable height of the elevator.
    * @param simulateGravity Whether gravity should be simulated or not.
+   * @param startingHeightMeters The starting height of the elevator.
+   * @param measurementStdDevs The standard deviations of the measurements.
+   */
+  public ElevatorSim(
+      LinearSystem<N2, N1, N1> plant,
+      DCMotor gearbox,
+      double minHeightMeters,
+      double maxHeightMeters,
+      boolean simulateGravity,
+      double startingHeightMeters,
+      Matrix<N1, N1> measurementStdDevs) {
+    super(plant, measurementStdDevs);
+    m_gearbox = gearbox;
+    m_minHeight = minHeightMeters;
+    m_maxHeight = maxHeightMeters;
+    m_simulateGravity = simulateGravity;
+
+    setState(startingHeightMeters, 0);
+  }
+
+  /**
+   * Creates a simulated elevator mechanism.
+   *
+   * @param plant The linear system that represents the elevator. This system can be created with
+   *     {@link edu.wpi.first.math.system.plant.LinearSystemId#createElevatorSystem(DCMotor, double,
+   *     double, double)}.
+   * @param gearbox The type of and number of motors in the elevator gearbox.
+   * @param minHeightMeters The min allowable height of the elevator.
+   * @param maxHeightMeters The max allowable height of the elevator.
+   * @param startingHeightMeters The starting height of the elevator.
+   * @param simulateGravity Whether gravity should be simulated or not.
    */
   public ElevatorSim(
       LinearSystem<N2, N1, N1> plant,
       DCMotor gearbox,
-      double gearing,
-      double drumRadiusMeters,
       double minHeightMeters,
       double maxHeightMeters,
-      boolean simulateGravity) {
+      boolean simulateGravity,
+      double startingHeightMeters) {
     this(
         plant,
         gearbox,
-        gearing,
-        drumRadiusMeters,
         minHeightMeters,
         maxHeightMeters,
         simulateGravity,
+        startingHeightMeters,
         null);
   }
 
   /**
    * Creates a simulated elevator mechanism.
    *
-   * @param plant The linear system that represents the elevator.
+   * @param kV The velocity gain.
+   * @param kA The acceleration gain.
    * @param gearbox The type of and number of motors in the elevator gearbox.
-   * @param gearing The gearing of the elevator (numbers greater than 1 represent reductions).
-   * @param drumRadiusMeters The radius of the drum that the elevator spool is wrapped around.
    * @param minHeightMeters The min allowable height of the elevator.
    * @param maxHeightMeters The max allowable height of the elevator.
    * @param simulateGravity Whether gravity should be simulated or not.
-   * @param measurementStdDevs The standard deviations of the measurements.
+   * @param startingHeightMeters The starting height of the elevator.
    */
   public ElevatorSim(
-      LinearSystem<N2, N1, N1> plant,
+      double kV,
+      double kA,
       DCMotor gearbox,
-      double gearing,
-      double drumRadiusMeters,
       double minHeightMeters,
       double maxHeightMeters,
       boolean simulateGravity,
+      double startingHeightMeters) {
+    this(
+        kV,
+        kA,
+        gearbox,
+        minHeightMeters,
+        maxHeightMeters,
+        simulateGravity,
+        startingHeightMeters,
+        null);
+  }
+
+  /**
+   * Creates a simulated elevator mechanism.
+   *
+   * @param kV The velocity gain.
+   * @param kA The acceleration gain.
+   * @param gearbox The type of and number of motors in the elevator gearbox.
+   * @param minHeightMeters The min allowable height of the elevator.
+   * @param maxHeightMeters The max allowable height of the elevator.
+   * @param simulateGravity Whether gravity should be simulated or not.
+   * @param startingHeightMeters The starting height of the elevator.
+   * @param measurementStdDevs The standard deviations of the measurements.
+   */
+  public ElevatorSim(
+      double kV,
+      double kA,
+      DCMotor gearbox,
+      double minHeightMeters,
+      double maxHeightMeters,
+      boolean simulateGravity,
+      double startingHeightMeters,
       Matrix<N1, N1> measurementStdDevs) {
-    super(plant, measurementStdDevs);
-    m_gearbox = gearbox;
-    m_gearing = gearing;
-    m_drumRadius = drumRadiusMeters;
-    m_minHeight = minHeightMeters;
-    m_maxHeight = maxHeightMeters;
-    m_simulateGravity = simulateGravity;
+    this(
+        LinearSystemId.identifyPositionSystem(kV, kA),
+        gearbox,
+        minHeightMeters,
+        maxHeightMeters,
+        simulateGravity,
+        startingHeightMeters,
+        measurementStdDevs);
   }
 
   /**
@@ -103,6 +158,8 @@
    * @param minHeightMeters The min allowable height of the elevator.
    * @param maxHeightMeters The max allowable height of the elevator.
    * @param simulateGravity Whether gravity should be simulated or not.
+   * @param startingHeightMeters The starting height of the elevator.
+   * @param measurementStdDevs The standard deviations of the measurements.
    */
   public ElevatorSim(
       DCMotor gearbox,
@@ -111,7 +168,40 @@
       double drumRadiusMeters,
       double minHeightMeters,
       double maxHeightMeters,
-      boolean simulateGravity) {
+      boolean simulateGravity,
+      double startingHeightMeters,
+      Matrix<N1, N1> measurementStdDevs) {
+    this(
+        LinearSystemId.createElevatorSystem(gearbox, carriageMassKg, drumRadiusMeters, gearing),
+        gearbox,
+        minHeightMeters,
+        maxHeightMeters,
+        simulateGravity,
+        startingHeightMeters,
+        measurementStdDevs);
+  }
+
+  /**
+   * Creates a simulated elevator mechanism.
+   *
+   * @param gearbox The type of and number of motors in the elevator gearbox.
+   * @param gearing The gearing of the elevator (numbers greater than 1 represent reductions).
+   * @param carriageMassKg The mass of the elevator carriage.
+   * @param drumRadiusMeters The radius of the drum that the elevator spool is wrapped around.
+   * @param minHeightMeters The min allowable height of the elevator.
+   * @param maxHeightMeters The max allowable height of the elevator.
+   * @param simulateGravity Whether gravity should be simulated or not.
+   * @param startingHeightMeters The starting height of the elevator.
+   */
+  public ElevatorSim(
+      DCMotor gearbox,
+      double gearing,
+      double carriageMassKg,
+      double drumRadiusMeters,
+      double minHeightMeters,
+      double maxHeightMeters,
+      boolean simulateGravity,
+      double startingHeightMeters) {
     this(
         gearbox,
         gearing,
@@ -120,39 +210,21 @@
         minHeightMeters,
         maxHeightMeters,
         simulateGravity,
+        startingHeightMeters,
         null);
   }
 
   /**
-   * Creates a simulated elevator mechanism.
+   * Sets the elevator's state. The new position will be limited between the minimum and maximum
+   * allowed heights.
    *
-   * @param gearbox The type of and number of motors in the elevator gearbox.
-   * @param gearing The gearing of the elevator (numbers greater than 1 represent reductions).
-   * @param carriageMassKg The mass of the elevator carriage.
-   * @param drumRadiusMeters The radius of the drum that the elevator spool is wrapped around.
-   * @param minHeightMeters The min allowable height of the elevator.
-   * @param maxHeightMeters The max allowable height of the elevator.
-   * @param simulateGravity Whether gravity should be simulated or not.
-   * @param measurementStdDevs The standard deviations of the measurements.
+   * @param positionMeters The new position in meters.
+   * @param velocityMetersPerSecond New velocity in meters per second.
    */
-  public ElevatorSim(
-      DCMotor gearbox,
-      double gearing,
-      double carriageMassKg,
-      double drumRadiusMeters,
-      double minHeightMeters,
-      double maxHeightMeters,
-      boolean simulateGravity,
-      Matrix<N1, N1> measurementStdDevs) {
-    super(
-        LinearSystemId.createElevatorSystem(gearbox, carriageMassKg, drumRadiusMeters, gearing),
-        measurementStdDevs);
-    m_gearbox = gearbox;
-    m_gearing = gearing;
-    m_drumRadius = drumRadiusMeters;
-    m_minHeight = minHeightMeters;
-    m_maxHeight = maxHeightMeters;
-    m_simulateGravity = simulateGravity;
+  public void setState(double positionMeters, double velocityMetersPerSecond) {
+    setState(
+        VecBuilder.fill(
+            MathUtil.clamp(positionMeters, m_minHeight, m_maxHeight), velocityMetersPerSecond));
   }
 
   /**
@@ -162,7 +234,7 @@
    * @return Whether the elevator would hit the lower limit.
    */
   public boolean wouldHitLowerLimit(double elevatorHeightMeters) {
-    return elevatorHeightMeters < this.m_minHeight;
+    return elevatorHeightMeters <= this.m_minHeight;
   }
 
   /**
@@ -172,7 +244,7 @@
    * @return Whether the elevator would hit the upper limit.
    */
   public boolean wouldHitUpperLimit(double elevatorHeightMeters) {
-    return elevatorHeightMeters > this.m_maxHeight;
+    return elevatorHeightMeters >= this.m_maxHeight;
   }
 
   /**
@@ -222,7 +294,10 @@
     // Reductions are greater than 1, so a reduction of 10:1 would mean the motor is
     // spinning 10x faster than the output
     // v = r w, so w = v/r
-    double motorVelocityRadPerSec = getVelocityMetersPerSecond() / m_drumRadius * m_gearing;
+    double kA = 1 / m_plant.getB().get(1, 0);
+    double kV = -m_plant.getA().get(1, 1) * kA;
+    double motorVelocityRadPerSec =
+        getVelocityMetersPerSecond() * kV * m_gearbox.KvRadPerSecPerVolt;
     var appliedVoltage = m_u.get(0, 0);
     return m_gearbox.getCurrent(motorVelocityRadPerSec, appliedVoltage)
         * Math.signum(appliedVoltage);
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/EncoderSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/EncoderSim.java
index 924ba99..310dbf6 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/EncoderSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/EncoderSim.java
@@ -304,6 +304,38 @@
   }
 
   /**
+   * Register a callback on the distance per pulse value of this encoder.
+   *
+   * @param callback the callback that will be called whenever the distance per pulse is changed
+   * @param initialNotify if true, the callback will be run on the initial value
+   * @return the {@link CallbackStore} object associated with this callback. Save a reference to
+   *     this object so GC doesn't cancel the callback.
+   */
+  public CallbackStore registerDistancePerPulseCallback(
+      NotifyCallback callback, boolean initialNotify) {
+    int uid = EncoderDataJNI.registerDistancePerPulseCallback(m_index, callback, initialNotify);
+    return new CallbackStore(m_index, uid, EncoderDataJNI::cancelDistancePerPulseCallback);
+  }
+
+  /**
+   * Get the distance per pulse value.
+   *
+   * @return the distance per pulse value
+   */
+  public double getDistancePerPulse() {
+    return EncoderDataJNI.getDistancePerPulse(m_index);
+  }
+
+  /**
+   * Set the distance per pulse value.
+   *
+   * @param samplesToAverage the new value
+   */
+  public void setDistancePerPulse(double samplesToAverage) {
+    EncoderDataJNI.setDistancePerPulse(m_index, samplesToAverage);
+  }
+
+  /**
    * Change the encoder distance.
    *
    * @param distance the new distance
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/FlywheelSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/FlywheelSim.java
index 90f911d..9c22488 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/FlywheelSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/FlywheelSim.java
@@ -5,6 +5,7 @@
 package edu.wpi.first.wpilibj.simulation;
 
 import edu.wpi.first.math.Matrix;
+import edu.wpi.first.math.VecBuilder;
 import edu.wpi.first.math.numbers.N1;
 import edu.wpi.first.math.system.LinearSystem;
 import edu.wpi.first.math.system.plant.DCMotor;
@@ -22,7 +23,9 @@
   /**
    * Creates a simulated flywheel mechanism.
    *
-   * @param plant The linear system that represents the flywheel.
+   * @param plant The linear system that represents the flywheel. This system can be created with
+   *     {@link edu.wpi.first.math.system.plant.LinearSystemId#createFlywheelSystem(DCMotor, double,
+   *     double)}.
    * @param gearbox The type of and number of motors in the flywheel gearbox.
    * @param gearing The gearing of the flywheel (numbers greater than 1 represent reductions).
    */
@@ -83,6 +86,15 @@
   }
 
   /**
+   * Sets the flywheel's state.
+   *
+   * @param velocityRadPerSec The new velocity in radians per second.
+   */
+  public void setState(double velocityRadPerSec) {
+    setState(VecBuilder.fill(velocityRadPerSec));
+  }
+
+  /**
    * Returns the flywheel velocity.
    *
    * @return The flywheel velocity.
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PS5ControllerSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PS5ControllerSim.java
new file mode 100644
index 0000000..d886bd6
--- /dev/null
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PS5ControllerSim.java
@@ -0,0 +1,214 @@
+// 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.
+
+package edu.wpi.first.wpilibj.simulation;
+
+import edu.wpi.first.wpilibj.PS5Controller;
+
+/** Class to control a simulated PS5 controller. */
+public class PS5ControllerSim extends GenericHIDSim {
+  /**
+   * Constructs from a PS5Controller object.
+   *
+   * @param joystick controller to simulate
+   */
+  public PS5ControllerSim(PS5Controller joystick) {
+    super(joystick);
+    setAxisCount(6);
+    setButtonCount(14);
+    setPOVCount(1);
+  }
+
+  /**
+   * Constructs from a joystick port number.
+   *
+   * @param port port number
+   */
+  public PS5ControllerSim(int port) {
+    super(port);
+    setAxisCount(6);
+    setButtonCount(14);
+    setPOVCount(1);
+  }
+
+  /**
+   * Change the X axis value of the controller's left stick.
+   *
+   * @param value the new value
+   */
+  public void setLeftX(double value) {
+    setRawAxis(PS5Controller.Axis.kLeftX.value, value);
+  }
+
+  /**
+   * Change the X axis value of the controller's right stick.
+   *
+   * @param value the new value
+   */
+  public void setRightX(double value) {
+    setRawAxis(PS5Controller.Axis.kRightX.value, value);
+  }
+
+  /**
+   * Change the Y axis value of the controller's left stick.
+   *
+   * @param value the new value
+   */
+  public void setLeftY(double value) {
+    setRawAxis(PS5Controller.Axis.kLeftY.value, value);
+  }
+
+  /**
+   * Change the Y axis value of the controller's right stick.
+   *
+   * @param value the new value
+   */
+  public void setRightY(double value) {
+    setRawAxis(PS5Controller.Axis.kRightY.value, value);
+  }
+
+  /**
+   * Change the L2 axis value of the controller.
+   *
+   * @param value the new value
+   */
+  public void setL2Axis(double value) {
+    setRawAxis(PS5Controller.Axis.kL2.value, value);
+  }
+
+  /**
+   * Change the R2 axis value of the controller.
+   *
+   * @param value the new value
+   */
+  public void setR2Axis(double value) {
+    setRawAxis(PS5Controller.Axis.kR2.value, value);
+  }
+
+  /**
+   * Change the value of the Square button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setSquareButton(boolean value) {
+    setRawButton(PS5Controller.Button.kSquare.value, value);
+  }
+
+  /**
+   * Change the value of the Cross button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setCrossButton(boolean value) {
+    setRawButton(PS5Controller.Button.kCross.value, value);
+  }
+
+  /**
+   * Change the value of the Circle button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setCircleButton(boolean value) {
+    setRawButton(PS5Controller.Button.kCircle.value, value);
+  }
+
+  /**
+   * Change the value of the Triangle button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setTriangleButton(boolean value) {
+    setRawButton(PS5Controller.Button.kTriangle.value, value);
+  }
+
+  /**
+   * Change the value of the L1 button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setL1Button(boolean value) {
+    setRawButton(PS5Controller.Button.kL1.value, value);
+  }
+
+  /**
+   * Change the value of the R1 button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setR1Button(boolean value) {
+    setRawButton(PS5Controller.Button.kR1.value, value);
+  }
+
+  /**
+   * Change the value of the L2 button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setL2Button(boolean value) {
+    setRawButton(PS5Controller.Button.kL2.value, value);
+  }
+
+  /**
+   * Change the value of the R2 button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setR2Button(boolean value) {
+    setRawButton(PS5Controller.Button.kR2.value, value);
+  }
+
+  /**
+   * Change the value of the Create button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setCreateButton(boolean value) {
+    setRawButton(PS5Controller.Button.kCreate.value, value);
+  }
+
+  /**
+   * Change the value of the Options button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setOptionsButton(boolean value) {
+    setRawButton(PS5Controller.Button.kOptions.value, value);
+  }
+
+  /**
+   * Change the value of the L3 (left stick) button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setL3Button(boolean value) {
+    setRawButton(PS5Controller.Button.kL3.value, value);
+  }
+
+  /**
+   * Change the value of the R3 (right stick) button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setR3Button(boolean value) {
+    setRawButton(PS5Controller.Button.kR3.value, value);
+  }
+
+  /**
+   * Change the value of the PS button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setPSButton(boolean value) {
+    setRawButton(PS5Controller.Button.kPS.value, value);
+  }
+
+  /**
+   * Change the value of the touchpad button on the controller.
+   *
+   * @param value the new value
+   */
+  public void setTouchpad(boolean value) {
+    setRawButton(PS5Controller.Button.kTouchpad.value, value);
+  }
+}
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PWMSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PWMSim.java
index 79781c4..ca98629 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PWMSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PWMSim.java
@@ -79,27 +79,28 @@
    * @return the {@link CallbackStore} object associated with this callback. Save a reference to
    *     this object so GC doesn't cancel the callback.
    */
-  public CallbackStore registerRawValueCallback(NotifyCallback callback, boolean initialNotify) {
-    int uid = PWMDataJNI.registerRawValueCallback(m_index, callback, initialNotify);
-    return new CallbackStore(m_index, uid, PWMDataJNI::cancelRawValueCallback);
+  public CallbackStore registerPulseMicrosecondCallback(
+      NotifyCallback callback, boolean initialNotify) {
+    int uid = PWMDataJNI.registerPulseMicrosecondCallback(m_index, callback, initialNotify);
+    return new CallbackStore(m_index, uid, PWMDataJNI::cancelPulseMicrosecondCallback);
   }
 
   /**
-   * Get the PWM raw value.
+   * Get the PWM pulse microsecond value.
    *
-   * @return the PWM raw value
+   * @return the PWM pulse microsecond value
    */
-  public int getRawValue() {
-    return PWMDataJNI.getRawValue(m_index);
+  public int getPulseMicrosecond() {
+    return PWMDataJNI.getPulseMicrosecond(m_index);
   }
 
   /**
-   * Set the PWM raw value.
+   * Set the PWM pulse microsecond value.
    *
-   * @param rawValue the PWM raw value
+   * @param microsecondPulseTime the PWM pulse microsecond value
    */
-  public void setRawValue(int rawValue) {
-    PWMDataJNI.setRawValue(m_index, rawValue);
+  public void setPulseMicrosecond(int microsecondPulseTime) {
+    PWMDataJNI.setPulseMicrosecond(m_index, microsecondPulseTime);
   }
 
   /**
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/RoboRioSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/RoboRioSim.java
index d128da8..8b28b6d 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/RoboRioSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/RoboRioSim.java
@@ -526,6 +526,70 @@
   }
 
   /**
+   * Register a callback to be run whenever the cpu temp changes.
+   *
+   * @param callback the callback
+   * @param initialNotify whether to call the callback with the initial state
+   * @return the {@link CallbackStore} object associated with this callback. Save a reference to
+   *     this object so GC doesn't cancel the callback.
+   */
+  public static CallbackStore registerCPUTempCallback(
+      NotifyCallback callback, boolean initialNotify) {
+    int uid = RoboRioDataJNI.registerCPUTempCallback(callback, initialNotify);
+    return new CallbackStore(uid, RoboRioDataJNI::cancelCPUTempCallback);
+  }
+
+  /**
+   * Get the cpu temp.
+   *
+   * @return the cpu temp.
+   */
+  public static double getCPUTemp() {
+    return RoboRioDataJNI.getCPUTemp();
+  }
+
+  /**
+   * Set the cpu temp.
+   *
+   * @param cpuTemp the new cpu temp.
+   */
+  public static void setCPUTemp(double cpuTemp) {
+    RoboRioDataJNI.setCPUTemp(cpuTemp);
+  }
+
+  /**
+   * Register a callback to be run whenever the team number changes.
+   *
+   * @param callback the callback
+   * @param initialNotify whether to call the callback with the initial state
+   * @return the {@link CallbackStore} object associated with this callback. Save a reference to
+   *     this object so GC doesn't cancel the callback.
+   */
+  public static CallbackStore registerTeamNumberCallback(
+      NotifyCallback callback, boolean initialNotify) {
+    int uid = RoboRioDataJNI.registerTeamNumberCallback(callback, initialNotify);
+    return new CallbackStore(uid, RoboRioDataJNI::cancelTeamNumberCallback);
+  }
+
+  /**
+   * Get the team number.
+   *
+   * @return the team number.
+   */
+  public static int getTeamNumber() {
+    return RoboRioDataJNI.getTeamNumber();
+  }
+
+  /**
+   * Set the team number.
+   *
+   * @param teamNumber the new team number.
+   */
+  public static void setTeamNumber(int teamNumber) {
+    RoboRioDataJNI.setTeamNumber(teamNumber);
+  }
+
+  /**
    * Get the serial number.
    *
    * @return The serial number.
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimDeviceSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimDeviceSim.java
index bca1f6f..a64e2f3 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimDeviceSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimDeviceSim.java
@@ -24,7 +24,7 @@
    * @param name name of the SimDevice
    */
   public SimDeviceSim(String name) {
-    m_handle = SimDeviceDataJNI.getSimDeviceHandle(name);
+    this(SimDeviceDataJNI.getSimDeviceHandle(name));
   }
 
   /**
@@ -49,6 +49,24 @@
   }
 
   /**
+   * Constructs a SimDeviceSim.
+   *
+   * @param handle the low level handle for the corresponding SimDevice
+   */
+  public SimDeviceSim(int handle) {
+    m_handle = handle;
+  }
+
+  /**
+   * Get the name of this object.
+   *
+   * @return name
+   */
+  public String getName() {
+    return SimDeviceDataJNI.getSimDeviceName(m_handle);
+  }
+
+  /**
    * Get the property object with the given name.
    *
    * @param name the property name
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSim.java
index b6a5e85..af2ee85 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSim.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj.simulation;
 
+import edu.wpi.first.math.MathUtil;
 import edu.wpi.first.math.Matrix;
 import edu.wpi.first.math.VecBuilder;
 import edu.wpi.first.math.numbers.N1;
@@ -22,7 +23,7 @@
   private final double m_gearing;
 
   // The length of the arm.
-  private final double m_r;
+  private final double m_armLenMeters;
 
   // The minimum angle that the arm is capable of.
   private final double m_minAngle;
@@ -30,56 +31,22 @@
   // The maximum angle that the arm is capable of.
   private final double m_maxAngle;
 
-  // The mass of the arm.
-  private final double m_armMass;
-
   // Whether the simulator should simulate gravity.
   private final boolean m_simulateGravity;
 
   /**
    * Creates a simulated arm mechanism.
    *
-   * @param plant The linear system that represents the arm.
+   * @param plant The linear system that represents the arm. This system can be created with {@link
+   *     edu.wpi.first.math.system.plant.LinearSystemId#createSingleJointedArmSystem(DCMotor,
+   *     double, double)}.
    * @param gearbox The type of and number of motors in the arm gearbox.
    * @param gearing The gearing of the arm (numbers greater than 1 represent reductions).
    * @param armLengthMeters The length of the arm.
    * @param minAngleRads The minimum angle that the arm is capable of.
    * @param maxAngleRads The maximum angle that the arm is capable of.
-   * @param armMassKg The mass of the arm.
    * @param simulateGravity Whether gravity should be simulated or not.
-   */
-  public SingleJointedArmSim(
-      LinearSystem<N2, N1, N1> plant,
-      DCMotor gearbox,
-      double gearing,
-      double armLengthMeters,
-      double minAngleRads,
-      double maxAngleRads,
-      double armMassKg,
-      boolean simulateGravity) {
-    this(
-        plant,
-        gearbox,
-        gearing,
-        armLengthMeters,
-        minAngleRads,
-        maxAngleRads,
-        armMassKg,
-        simulateGravity,
-        null);
-  }
-
-  /**
-   * Creates a simulated arm mechanism.
-   *
-   * @param plant The linear system that represents the arm.
-   * @param gearbox The type of and number of motors in the arm gearbox.
-   * @param gearing The gearing of the arm (numbers greater than 1 represent reductions).
-   * @param armLengthMeters The length of the arm.
-   * @param minAngleRads The minimum angle that the arm is capable of.
-   * @param maxAngleRads The maximum angle that the arm is capable of.
-   * @param armMassKg The mass of the arm.
-   * @param simulateGravity Whether gravity should be simulated or not.
+   * @param startingAngleRads The initial position of the Arm simulation in radians.
    * @param measurementStdDevs The standard deviations of the measurements.
    */
   public SingleJointedArmSim(
@@ -89,17 +56,53 @@
       double armLengthMeters,
       double minAngleRads,
       double maxAngleRads,
-      double armMassKg,
       boolean simulateGravity,
+      double startingAngleRads,
       Matrix<N1, N1> measurementStdDevs) {
     super(plant, measurementStdDevs);
     m_gearbox = gearbox;
     m_gearing = gearing;
-    m_r = armLengthMeters;
+    m_armLenMeters = armLengthMeters;
     m_minAngle = minAngleRads;
     m_maxAngle = maxAngleRads;
-    m_armMass = armMassKg;
     m_simulateGravity = simulateGravity;
+
+    setState(startingAngleRads, 0.0);
+  }
+
+  /**
+   * Creates a simulated arm mechanism.
+   *
+   * @param plant The linear system that represents the arm. This system can be created with {@link
+   *     edu.wpi.first.math.system.plant.LinearSystemId#createSingleJointedArmSystem(DCMotor,
+   *     double, double)}.
+   * @param gearbox The type of and number of motors in the arm gearbox.
+   * @param gearing The gearing of the arm (numbers greater than 1 represent reductions).
+   * @param armLengthMeters The length of the arm.
+   * @param minAngleRads The minimum angle that the arm is capable of.
+   * @param maxAngleRads The maximum angle that the arm is capable of.
+   * @param simulateGravity Whether gravity should be simulated or not.
+   * @param startingAngleRads The initial position of the Arm simulation in radians.
+   */
+  public SingleJointedArmSim(
+      LinearSystem<N2, N1, N1> plant,
+      DCMotor gearbox,
+      double gearing,
+      double armLengthMeters,
+      double minAngleRads,
+      double maxAngleRads,
+      boolean simulateGravity,
+      double startingAngleRads) {
+    this(
+        plant,
+        gearbox,
+        gearing,
+        armLengthMeters,
+        minAngleRads,
+        maxAngleRads,
+        simulateGravity,
+        startingAngleRads,
+        null);
   }
 
   /**
@@ -111,8 +114,8 @@
    * @param armLengthMeters The length of the arm.
    * @param minAngleRads The minimum angle that the arm is capable of.
    * @param maxAngleRads The maximum angle that the arm is capable of.
-   * @param armMassKg The mass of the arm.
    * @param simulateGravity Whether gravity should be simulated or not.
+   * @param startingAngleRads The initial position of the Arm simulation in radians.
    */
   public SingleJointedArmSim(
       DCMotor gearbox,
@@ -121,8 +124,8 @@
       double armLengthMeters,
       double minAngleRads,
       double maxAngleRads,
-      double armMassKg,
-      boolean simulateGravity) {
+      boolean simulateGravity,
+      double startingAngleRads) {
     this(
         gearbox,
         gearing,
@@ -130,8 +133,8 @@
         armLengthMeters,
         minAngleRads,
         maxAngleRads,
-        armMassKg,
         simulateGravity,
+        startingAngleRads,
         null);
   }
 
@@ -144,8 +147,8 @@
    * @param armLengthMeters The length of the arm.
    * @param minAngleRads The minimum angle that the arm is capable of.
    * @param maxAngleRads The maximum angle that the arm is capable of.
-   * @param armMassKg The mass of the arm.
    * @param simulateGravity Whether gravity should be simulated or not.
+   * @param startingAngleRads The initial position of the Arm simulation in radians.
    * @param measurementStdDevs The standard deviations of the measurements.
    */
   public SingleJointedArmSim(
@@ -155,19 +158,31 @@
       double armLengthMeters,
       double minAngleRads,
       double maxAngleRads,
-      double armMassKg,
       boolean simulateGravity,
+      double startingAngleRads,
       Matrix<N1, N1> measurementStdDevs) {
-    super(
+    this(
         LinearSystemId.createSingleJointedArmSystem(gearbox, jKgMetersSquared, gearing),
+        gearbox,
+        gearing,
+        armLengthMeters,
+        minAngleRads,
+        maxAngleRads,
+        simulateGravity,
+        startingAngleRads,
         measurementStdDevs);
-    m_gearbox = gearbox;
-    m_gearing = gearing;
-    m_r = armLengthMeters;
-    m_minAngle = minAngleRads;
-    m_maxAngle = maxAngleRads;
-    m_armMass = armMassKg;
-    m_simulateGravity = simulateGravity;
+  }
+
+  /**
+   * Sets the arm's state. The new angle will be limited between the minimum and maximum allowed
+   * limits.
+   *
+   * @param angleRadians The new angle in radians.
+   * @param velocityRadPerSec The new angular velocity in radians per second.
+   */
+  public void setState(double angleRadians, double velocityRadPerSec) {
+    setState(
+        VecBuilder.fill(MathUtil.clamp(angleRadians, m_minAngle, m_maxAngle), velocityRadPerSec));
   }
 
   /**
@@ -268,30 +283,37 @@
    */
   @Override
   protected Matrix<N2, N1> updateX(Matrix<N2, N1> currentXhat, Matrix<N1, N1> u, double dtSeconds) {
-    // Horizontal case:
-    // Torque = F * r = I * alpha
-    // alpha = F * r / I
-    // Since F = mg,
-    // alpha = m * g * r / I
-    // Finally, multiply RHS by cos(theta) to account for the arm angle
-    // This acceleration is added to the linear system dynamics x-dot = Ax + Bu
-    // We therefore find that f(x, u) = Ax + Bu + [[0] [m * g * r / I *
-    // cos(theta)]]
+    // The torque on the arm is given by τ = F⋅r, where F is the force applied by
+    // gravity and r the distance from pivot to center of mass. Recall from
+    // dynamics that the sum of torques for a rigid body is τ = J⋅α, were τ is
+    // torque on the arm, J is the mass-moment of inertia about the pivot axis,
+    // and α is the angular acceleration in rad/s². Rearranging yields: α = F⋅r/J
+    //
+    // We substitute in F = m⋅g⋅cos(θ), where θ is the angle from horizontal:
+    //
+    //   α = (m⋅g⋅cos(θ))⋅r/J
+    //
+    // Multiply RHS by cos(θ) to account for the arm angle. Further, we know the
+    // arm mass-moment of inertia J of our arm is given by J=1/3 mL², modeled as a
+    // rod rotating about it's end, where L is the overall rod length. The mass
+    // distribution is assumed to be uniform. Substitute r=L/2 to find:
+    //
+    //   α = (m⋅g⋅cos(θ))⋅r/(1/3 mL²)
+    //   α = (m⋅g⋅cos(θ))⋅(L/2)/(1/3 mL²)
+    //   α = 3/2⋅g⋅cos(θ)/L
+    //
+    // This acceleration is next added to the linear system dynamics ẋ=Ax+Bu
+    //
+    //   f(x, u) = Ax + Bu + [0  α]ᵀ
+    //   f(x, u) = Ax + Bu + [0  3/2⋅g⋅cos(θ)/L]ᵀ
+
     Matrix<N2, N1> updatedXhat =
         NumericalIntegration.rkdp(
             (Matrix<N2, N1> x, Matrix<N1, N1> _u) -> {
               Matrix<N2, N1> xdot = m_plant.getA().times(x).plus(m_plant.getB().times(_u));
               if (m_simulateGravity) {
-                xdot =
-                    xdot.plus(
-                        VecBuilder.fill(
-                            0,
-                            m_armMass
-                                * m_r
-                                * -9.8
-                                * 3.0
-                                / (m_armMass * m_r * m_r)
-                                * Math.cos(x.get(0, 0))));
+                double alphaGrav = 3.0 / 2.0 * -9.8 * Math.cos(x.get(0, 0)) / m_armLenMeters;
+                xdot = xdot.plus(VecBuilder.fill(0, alphaGrav));
               }
               return xdot;
             },
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/UltrasonicSim.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/UltrasonicSim.java
index 3781281..dc4ebaa 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/UltrasonicSim.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/UltrasonicSim.java
@@ -9,6 +9,7 @@
 import edu.wpi.first.math.util.Units;
 import edu.wpi.first.wpilibj.Ultrasonic;
 
+/** Class to control a simulated {@link edu.wpi.first.wpilibj.Ultrasonic}. */
 public class UltrasonicSim {
   private final SimBoolean m_simRangeValid;
   private final SimDouble m_simRange;
@@ -19,7 +20,18 @@
    * @param ultrasonic The real ultrasonic to simulate
    */
   public UltrasonicSim(Ultrasonic ultrasonic) {
-    SimDeviceSim simDevice = new SimDeviceSim("Ultrasonic", ultrasonic.getEchoChannel());
+    // ping parameter is unused
+    this(-1, ultrasonic.getEchoChannel());
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param ping unused.
+   * @param echo the ultrasonic's echo channel.
+   */
+  public UltrasonicSim(@SuppressWarnings("unused") int ping, int echo) {
+    SimDeviceSim simDevice = new SimDeviceSim("Ultrasonic", echo);
     m_simRangeValid = simDevice.getBoolean("Range Valid");
     m_simRange = simDevice.getDouble("Range (in)");
   }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismObject2d.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismObject2d.java
index 2d13ce0..754bd92 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismObject2d.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismObject2d.java
@@ -68,7 +68,7 @@
   }
 
   /**
-   * Update all entries with new ones from a new table.
+   * Update this object's entries with new ones from a new table.
    *
    * @param table the new table.
    */
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismRoot2d.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismRoot2d.java
index 6091132..5339cf4 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismRoot2d.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismRoot2d.java
@@ -6,8 +6,6 @@
 
 import edu.wpi.first.networktables.DoublePublisher;
 import edu.wpi.first.networktables.NetworkTable;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * Root Mechanism2d node.
@@ -19,10 +17,7 @@
  *
  * <p>Append other nodes by using {@link #append(MechanismObject2d)}.
  */
-public final class MechanismRoot2d implements AutoCloseable {
-  private final String m_name;
-  private NetworkTable m_table;
-  private final Map<String, MechanismObject2d> m_objects = new HashMap<>(1);
+public final class MechanismRoot2d extends MechanismObject2d {
   private double m_x;
   private DoublePublisher m_xPub;
   private double m_y;
@@ -36,7 +31,7 @@
    * @param y y coordinate of root (provide only when constructing a root node)
    */
   MechanismRoot2d(String name, double x, double y) {
-    m_name = name;
+    super(name);
     m_x = x;
     m_y = y;
   }
@@ -49,29 +44,7 @@
     if (m_yPub != null) {
       m_yPub.close();
     }
-    for (MechanismObject2d obj : m_objects.values()) {
-      obj.close();
-    }
-  }
-
-  /**
-   * Append a Mechanism object that is based on this one.
-   *
-   * @param <T> The object type.
-   * @param object the object to add.
-   * @return the object given as a parameter, useful for variable assignments and call chaining.
-   * @throws UnsupportedOperationException if the object's name is already used - object names must
-   *     be unique.
-   */
-  public synchronized <T extends MechanismObject2d> T append(T object) {
-    if (m_objects.containsKey(object.getName())) {
-      throw new UnsupportedOperationException("Mechanism object names must be unique!");
-    }
-    m_objects.put(object.getName(), object);
-    if (m_table != null) {
-      object.update(m_table.getSubTable(object.getName()));
-    }
-    return object;
+    super.close();
   }
 
   /**
@@ -86,24 +59,17 @@
     flush();
   }
 
-  synchronized void update(NetworkTable table) {
-    m_table = table;
+  @Override
+  protected synchronized void updateEntries(NetworkTable table) {
     if (m_xPub != null) {
       m_xPub.close();
     }
-    m_xPub = m_table.getDoubleTopic("x").publish();
+    m_xPub = table.getDoubleTopic("x").publish();
     if (m_yPub != null) {
       m_yPub.close();
     }
-    m_yPub = m_table.getDoubleTopic("y").publish();
+    m_yPub = table.getDoubleTopic("y").publish();
     flush();
-    for (MechanismObject2d obj : m_objects.values()) {
-      obj.update(m_table.getSubTable(obj.getName()));
-    }
-  }
-
-  public String getName() {
-    return m_name;
   }
 
   private void flush() {
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java
index 7bb266e..2c409e8 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java
@@ -57,6 +57,7 @@
 import java.util.function.LongSupplier;
 import java.util.function.Supplier;
 
+@SuppressWarnings("PMD.CompareObjectsWithEquals")
 public class SendableBuilderImpl implements NTSendableBuilder {
   @FunctionalInterface
   private interface TimedConsumer<T> {
@@ -328,6 +329,15 @@
     m_properties.add(property);
   }
 
+  @Override
+  public void publishConstBoolean(String key, boolean value) {
+    Property<BooleanPublisher, BooleanSubscriber> property = new Property<>();
+    BooleanTopic topic = m_table.getBooleanTopic(key);
+    property.m_pub = topic.publish();
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
+
   /**
    * Add an integer property.
    *
@@ -355,6 +365,15 @@
     m_properties.add(property);
   }
 
+  @Override
+  public void publishConstInteger(String key, long value) {
+    Property<IntegerPublisher, IntegerSubscriber> property = new Property<>();
+    IntegerTopic topic = m_table.getIntegerTopic(key);
+    property.m_pub = topic.publish();
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
+
   /**
    * Add a float property.
    *
@@ -382,6 +401,15 @@
     m_properties.add(property);
   }
 
+  @Override
+  public void publishConstFloat(String key, float value) {
+    Property<FloatPublisher, FloatSubscriber> property = new Property<>();
+    FloatTopic topic = m_table.getFloatTopic(key);
+    property.m_pub = topic.publish();
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
+
   /**
    * Add a double property.
    *
@@ -409,6 +437,15 @@
     m_properties.add(property);
   }
 
+  @Override
+  public void publishConstDouble(String key, double value) {
+    Property<DoublePublisher, DoubleSubscriber> property = new Property<>();
+    DoubleTopic topic = m_table.getDoubleTopic(key);
+    property.m_pub = topic.publish();
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
+
   /**
    * Add a string property.
    *
@@ -436,6 +473,15 @@
     m_properties.add(property);
   }
 
+  @Override
+  public void publishConstString(String key, String value) {
+    Property<StringPublisher, StringSubscriber> property = new Property<>();
+    StringTopic topic = m_table.getStringTopic(key);
+    property.m_pub = topic.publish();
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
+
   /**
    * Add a boolean array property.
    *
@@ -465,6 +511,15 @@
     m_properties.add(property);
   }
 
+  @Override
+  public void publishConstBooleanArray(String key, boolean[] value) {
+    Property<BooleanArrayPublisher, BooleanArraySubscriber> property = new Property<>();
+    BooleanArrayTopic topic = m_table.getBooleanArrayTopic(key);
+    property.m_pub = topic.publish();
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
+
   /**
    * Add an integer array property.
    *
@@ -494,6 +549,15 @@
     m_properties.add(property);
   }
 
+  @Override
+  public void publishConstIntegerArray(String key, long[] value) {
+    Property<IntegerArrayPublisher, IntegerArraySubscriber> property = new Property<>();
+    IntegerArrayTopic topic = m_table.getIntegerArrayTopic(key);
+    property.m_pub = topic.publish();
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
+
   /**
    * Add a float array property.
    *
@@ -523,6 +587,15 @@
     m_properties.add(property);
   }
 
+  @Override
+  public void publishConstFloatArray(String key, float[] value) {
+    Property<FloatArrayPublisher, FloatArraySubscriber> property = new Property<>();
+    FloatArrayTopic topic = m_table.getFloatArrayTopic(key);
+    property.m_pub = topic.publish();
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
+
   /**
    * Add a double array property.
    *
@@ -552,6 +625,15 @@
     m_properties.add(property);
   }
 
+  @Override
+  public void publishConstDoubleArray(String key, double[] value) {
+    Property<DoubleArrayPublisher, DoubleArraySubscriber> property = new Property<>();
+    DoubleArrayTopic topic = m_table.getDoubleArrayTopic(key);
+    property.m_pub = topic.publish();
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
+
   /**
    * Add a string array property.
    *
@@ -581,6 +663,15 @@
     m_properties.add(property);
   }
 
+  @Override
+  public void publishConstStringArray(String key, String[] value) {
+    Property<StringArrayPublisher, StringArraySubscriber> property = new Property<>();
+    StringArrayTopic topic = m_table.getStringArrayTopic(key);
+    property.m_pub = topic.publish();
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
+
   /**
    * Add a raw property.
    *
@@ -610,4 +701,13 @@
     }
     m_properties.add(property);
   }
+
+  @Override
+  public void publishConstRaw(String key, String typestring, byte[] value) {
+    Property<RawPublisher, RawSubscriber> property = new Property<>();
+    RawTopic topic = m_table.getRawTopic(key);
+    property.m_pub = topic.publish(typestring);
+    property.m_pub.set(value);
+    m_properties.add(property);
+  }
 }
diff --git a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooser.java b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooser.java
index c967e17..7f3b93c 100644
--- a/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooser.java
+++ b/third_party/allwpilib/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooser.java
@@ -6,19 +6,14 @@
 
 import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
 
-import edu.wpi.first.networktables.IntegerPublisher;
-import edu.wpi.first.networktables.IntegerTopic;
-import edu.wpi.first.networktables.NTSendable;
-import edu.wpi.first.networktables.NTSendableBuilder;
-import edu.wpi.first.networktables.StringPublisher;
-import edu.wpi.first.networktables.StringTopic;
+import edu.wpi.first.util.sendable.Sendable;
+import edu.wpi.first.util.sendable.SendableBuilder;
 import edu.wpi.first.util.sendable.SendableRegistry;
-import java.util.ArrayList;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
 
 /**
  * The {@link SendableChooser} class is a useful tool for presenting a selection of options to the
@@ -32,22 +27,29 @@
  *
  * @param <V> The type of the values to be stored
  */
-public class SendableChooser<V> implements NTSendable, AutoCloseable {
+public class SendableChooser<V> implements Sendable, AutoCloseable {
   /** The key for the default value. */
   private static final String DEFAULT = "default";
+
   /** The key for the selected option. */
   private static final String SELECTED = "selected";
+
   /** The key for the active option. */
   private static final String ACTIVE = "active";
+
   /** The key for the option array. */
   private static final String OPTIONS = "options";
+
   /** The key for the instance number. */
   private static final String INSTANCE = ".instance";
+
   /** A map linking strings to the objects they represent. */
   private final Map<String, V> m_map = new LinkedHashMap<>();
 
   private String m_defaultChoice = "";
   private final int m_instance;
+  private String m_previousVal;
+  private Consumer<V> m_listener;
   private static final AtomicInteger s_instances = new AtomicInteger();
 
   /** Instantiates a {@link SendableChooser}. */
@@ -59,14 +61,6 @@
   @Override
   public void close() {
     SendableRegistry.remove(this);
-    m_mutex.lock();
-    try {
-      for (StringPublisher pub : m_activePubs) {
-        pub.close();
-      }
-    } finally {
-      m_mutex.unlock();
-    }
   }
 
   /**
@@ -114,16 +108,26 @@
     }
   }
 
+  /**
+   * Bind a listener that's called when the selected value changes. Only one listener can be bound.
+   * Calling this function will replace the previous listener.
+   *
+   * @param listener The function to call that accepts the new value
+   */
+  public void onChange(Consumer<V> listener) {
+    requireNonNullParam(listener, "listener", "onChange");
+    m_mutex.lock();
+    m_listener = listener;
+    m_mutex.unlock();
+  }
+
   private String m_selected;
-  private final List<StringPublisher> m_activePubs = new ArrayList<>();
   private final ReentrantLock m_mutex = new ReentrantLock();
 
   @Override
-  public void initSendable(NTSendableBuilder builder) {
+  public void initSendable(SendableBuilder builder) {
     builder.setSmartDashboardType("String Chooser");
-    IntegerPublisher instancePub = new IntegerTopic(builder.getTopic(INSTANCE)).publish();
-    instancePub.set(m_instance);
-    builder.addCloseable(instancePub);
+    builder.publishConstInteger(INSTANCE, m_instance);
     builder.addStringProperty(DEFAULT, () -> m_defaultChoice, null);
     builder.addStringArrayProperty(OPTIONS, () -> m_map.keySet().toArray(new String[0]), null);
     builder.addStringProperty(
@@ -141,25 +145,29 @@
           }
         },
         null);
-    m_mutex.lock();
-    try {
-      m_activePubs.add(new StringTopic(builder.getTopic(ACTIVE)).publish());
-    } finally {
-      m_mutex.unlock();
-    }
     builder.addStringProperty(
         SELECTED,
         null,
         val -> {
+          V choice;
+          Consumer<V> listener;
           m_mutex.lock();
           try {
             m_selected = val;
-            for (StringPublisher pub : m_activePubs) {
-              pub.set(val);
+            if (!m_selected.equals(m_previousVal) && m_listener != null) {
+              choice = m_map.get(val);
+              listener = m_listener;
+            } else {
+              choice = null;
+              listener = null;
             }
+            m_previousVal = val;
           } finally {
             m_mutex.unlock();
           }
+          if (listener != null) {
+            listener.accept(choice);
+          }
         });
   }
 }
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/NotifierTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/NotifierTest.java
new file mode 100644
index 0000000..d6e16b6
--- /dev/null
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/NotifierTest.java
@@ -0,0 +1,63 @@
+// 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.
+
+package edu.wpi.first.wpilibj;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import edu.wpi.first.hal.HAL;
+import edu.wpi.first.wpilibj.simulation.SimHooks;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+/** Tests to see if the Notifier is working properly. */
+class NotifierTest {
+  @BeforeEach
+  void setup() {
+    HAL.initialize(500, 0);
+    SimHooks.pauseTiming();
+    SimHooks.restartTiming();
+  }
+
+  @AfterEach
+  void cleanup() {
+    SimHooks.resumeTiming();
+  }
+
+  @Test
+  @ResourceLock("timing")
+  void testStartPeriodicAndStop() {
+    AtomicInteger counter = new AtomicInteger();
+    Notifier notifier = new Notifier(counter::getAndIncrement);
+    notifier.startPeriodic(1.0);
+
+    SimHooks.stepTiming(10);
+
+    notifier.stop();
+    assertEquals(10, counter.get());
+
+    SimHooks.stepTiming(3.0);
+
+    assertEquals(10, counter.get());
+
+    notifier.close();
+  }
+
+  @Test
+  @ResourceLock("timing")
+  void testStartSingle() {
+    AtomicInteger counter = new AtomicInteger();
+    Notifier notifier = new Notifier(counter::getAndIncrement);
+    notifier.startSingle(1.0);
+
+    SimHooks.stepTiming(10.5);
+
+    assertEquals(1, counter.get());
+
+    notifier.close();
+  }
+}
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/PS5ControllerTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/PS5ControllerTest.java
new file mode 100644
index 0000000..cb24fbf
--- /dev/null
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/PS5ControllerTest.java
@@ -0,0 +1,79 @@
+// 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.
+
+package edu.wpi.first.wpilibj;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import edu.wpi.first.hal.HAL;
+import edu.wpi.first.wpilibj.simulation.PS5ControllerSim;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+class PS5ControllerTest {
+  @ParameterizedTest
+  @EnumSource(value = PS5Controller.Button.class)
+  void testButtons(PS5Controller.Button button)
+      throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+    HAL.initialize(500, 0);
+    PS5Controller joy = new PS5Controller(2);
+    PS5ControllerSim joysim = new PS5ControllerSim(joy);
+
+    var buttonName = button.toString();
+
+    String simSetMethodName = "set" + buttonName;
+    String joyGetMethodName = "get" + buttonName;
+    String joyPressedMethodName = "get" + buttonName + "Pressed";
+    String joyReleasedMethodName = "get" + buttonName + "Released";
+
+    final Method simSetMethod = joysim.getClass().getMethod(simSetMethodName, boolean.class);
+    final Method joyGetMethod = joy.getClass().getMethod(joyGetMethodName);
+    final Method joyPressedMethod = joy.getClass().getMethod(joyPressedMethodName);
+    final Method joyReleasedMethod = joy.getClass().getMethod(joyReleasedMethodName);
+
+    simSetMethod.invoke(joysim, false);
+    joysim.notifyNewData();
+    assertFalse((Boolean) joyGetMethod.invoke(joy));
+    // need to call pressed and released to clear flags
+    joyPressedMethod.invoke(joy);
+    joyReleasedMethod.invoke(joy);
+
+    simSetMethod.invoke(joysim, true);
+    joysim.notifyNewData();
+    assertTrue((Boolean) joyGetMethod.invoke(joy));
+    assertTrue((Boolean) joyPressedMethod.invoke(joy));
+    assertFalse((Boolean) joyReleasedMethod.invoke(joy));
+
+    simSetMethod.invoke(joysim, false);
+    joysim.notifyNewData();
+    assertFalse((Boolean) joyGetMethod.invoke(joy));
+    assertFalse((Boolean) joyPressedMethod.invoke(joy));
+    assertTrue((Boolean) joyReleasedMethod.invoke(joy));
+  }
+
+  @ParameterizedTest
+  @EnumSource(value = PS5Controller.Axis.class)
+  void testAxes(PS5Controller.Axis axis)
+      throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+    HAL.initialize(500, 0);
+    PS5Controller joy = new PS5Controller(2);
+    PS5ControllerSim joysim = new PS5ControllerSim(joy);
+
+    var axisName = axis.toString();
+
+    String simSetMethodName = "set" + axisName;
+    String joyGetMethodName = "get" + axisName;
+
+    Method simSetMethod = joysim.getClass().getMethod(simSetMethodName, double.class);
+    Method joyGetMethod = joy.getClass().getMethod(joyGetMethodName);
+
+    simSetMethod.invoke(joysim, 0.35);
+    joysim.notifyNewData();
+    assertEquals(0.35, (Double) joyGetMethod.invoke(joy), 0.001);
+  }
+}
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/PreferencesTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/PreferencesTest.java
index e3ccfcc..7b11a90 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/PreferencesTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/PreferencesTest.java
@@ -24,6 +24,7 @@
 import java.util.stream.Stream;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
@@ -71,6 +72,7 @@
     m_inst.close();
   }
 
+  @Disabled("Fails often with 'Preferences was not empty!'")
   @Test
   void removeAllTest() {
     Preferences.removeAll();
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/RobotControllerTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/RobotControllerTest.java
index 3c5647c..cdc27c7 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/RobotControllerTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/RobotControllerTest.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj;
 
+@SuppressWarnings("PMD.TestClassWithoutTestCases")
 class RobotControllerTest extends UtilityClassTest<RobotController> {
   RobotControllerTest() {
     super(RobotController.class);
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/TimedRobotTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/TimedRobotTest.java
index bf143e7..c1c5f3c 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/TimedRobotTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/TimedRobotTest.java
@@ -21,6 +21,8 @@
 import org.junit.jupiter.params.provider.ValueSource;
 
 class TimedRobotTest {
+  static final double kPeriod = 0.02;
+
   static class MockRobot extends TimedRobot {
     public final AtomicInteger m_robotInitCount = new AtomicInteger(0);
     public final AtomicInteger m_simulationInitCount = new AtomicInteger(0);
@@ -41,6 +43,10 @@
     public final AtomicInteger m_teleopExitCount = new AtomicInteger(0);
     public final AtomicInteger m_testExitCount = new AtomicInteger(0);
 
+    MockRobot() {
+      super(kPeriod);
+    }
+
     @Override
     public void robotInit() {
       m_robotInitCount.addAndGet(1);
@@ -168,7 +174,7 @@
     assertEquals(0, robot.m_teleopExitCount.get());
     assertEquals(0, robot.m_testExitCount.get());
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_robotInitCount.get());
     assertEquals(1, robot.m_simulationInitCount.get());
@@ -189,7 +195,7 @@
     assertEquals(0, robot.m_teleopExitCount.get());
     assertEquals(0, robot.m_testExitCount.get());
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_robotInitCount.get());
     assertEquals(1, robot.m_simulationInitCount.get());
@@ -257,7 +263,7 @@
     assertEquals(0, robot.m_teleopExitCount.get());
     assertEquals(0, robot.m_testExitCount.get());
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_robotInitCount.get());
     assertEquals(1, robot.m_simulationInitCount.get());
@@ -278,7 +284,7 @@
     assertEquals(0, robot.m_teleopExitCount.get());
     assertEquals(0, robot.m_testExitCount.get());
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_robotInitCount.get());
     assertEquals(1, robot.m_simulationInitCount.get());
@@ -346,7 +352,7 @@
     assertEquals(0, robot.m_teleopExitCount.get());
     assertEquals(0, robot.m_testExitCount.get());
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_robotInitCount.get());
     assertEquals(1, robot.m_simulationInitCount.get());
@@ -367,7 +373,7 @@
     assertEquals(0, robot.m_teleopExitCount.get());
     assertEquals(0, robot.m_testExitCount.get());
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_robotInitCount.get());
     assertEquals(1, robot.m_simulationInitCount.get());
@@ -438,7 +444,7 @@
     assertEquals(0, robot.m_teleopExitCount.get());
     assertEquals(0, robot.m_testExitCount.get());
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_robotInitCount.get());
     assertEquals(1, robot.m_simulationInitCount.get());
@@ -459,7 +465,7 @@
     assertEquals(0, robot.m_teleopExitCount.get());
     assertEquals(0, robot.m_testExitCount.get());
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_robotInitCount.get());
     assertEquals(1, robot.m_simulationInitCount.get());
@@ -549,7 +555,7 @@
     assertEquals(0, robot.m_teleopExitCount.get());
     assertEquals(0, robot.m_testExitCount.get());
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_disabledInitCount.get());
     assertEquals(0, robot.m_autonomousInitCount.get());
@@ -567,7 +573,7 @@
     DriverStationSim.setTest(false);
     DriverStationSim.notifyNewData();
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_disabledInitCount.get());
     assertEquals(1, robot.m_autonomousInitCount.get());
@@ -585,7 +591,7 @@
     DriverStationSim.setTest(false);
     DriverStationSim.notifyNewData();
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_disabledInitCount.get());
     assertEquals(1, robot.m_autonomousInitCount.get());
@@ -603,7 +609,7 @@
     DriverStationSim.setTest(true);
     DriverStationSim.notifyNewData();
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(1, robot.m_disabledInitCount.get());
     assertEquals(1, robot.m_autonomousInitCount.get());
@@ -621,7 +627,7 @@
     DriverStationSim.setTest(false);
     DriverStationSim.notifyNewData();
 
-    SimHooks.stepTiming(0.02);
+    SimHooks.stepTiming(kPeriod);
 
     assertEquals(2, robot.m_disabledInitCount.get());
     assertEquals(1, robot.m_autonomousInitCount.get());
@@ -653,7 +659,7 @@
         () -> {
           callbackCount.addAndGet(1);
         },
-        0.01);
+        kPeriod / 2.0);
 
     Thread robotThread =
         new Thread(
@@ -670,13 +676,13 @@
     assertEquals(0, robot.m_disabledPeriodicCount.get());
     assertEquals(0, callbackCount.get());
 
-    SimHooks.stepTiming(0.01);
+    SimHooks.stepTiming(kPeriod / 2.0);
 
     assertEquals(0, robot.m_disabledInitCount.get());
     assertEquals(0, robot.m_disabledPeriodicCount.get());
     assertEquals(1, callbackCount.get());
 
-    SimHooks.stepTiming(0.01);
+    SimHooks.stepTiming(kPeriod / 2.0);
 
     assertEquals(1, robot.m_disabledInitCount.get());
     assertEquals(1, robot.m_disabledPeriodicCount.get());
@@ -702,15 +708,17 @@
         () -> {
           callbackCount.addAndGet(1);
         },
-        0.01,
-        0.005);
+        kPeriod / 2.0,
+        kPeriod / 4.0);
 
     // Expirations in this test (ms)
     //
+    // Let p be period in ms.
+    //
     // Robot | Callback
     // ================
-    //    20 |      15
-    //    40 |      25
+    //     p |    0.75p
+    //    2p |    1.25p
 
     Thread robotThread =
         new Thread(
@@ -727,25 +735,25 @@
     assertEquals(0, robot.m_disabledPeriodicCount.get());
     assertEquals(0, callbackCount.get());
 
-    SimHooks.stepTiming(0.0075);
+    SimHooks.stepTiming(kPeriod * 3.0 / 8.0);
 
     assertEquals(0, robot.m_disabledInitCount.get());
     assertEquals(0, robot.m_disabledPeriodicCount.get());
     assertEquals(0, callbackCount.get());
 
-    SimHooks.stepTiming(0.0075);
+    SimHooks.stepTiming(kPeriod * 3.0 / 8.0);
 
     assertEquals(0, robot.m_disabledInitCount.get());
     assertEquals(0, robot.m_disabledPeriodicCount.get());
     assertEquals(1, callbackCount.get());
 
-    SimHooks.stepTiming(0.005);
+    SimHooks.stepTiming(kPeriod / 4.0);
 
     assertEquals(1, robot.m_disabledInitCount.get());
     assertEquals(1, robot.m_disabledPeriodicCount.get());
     assertEquals(1, callbackCount.get());
 
-    SimHooks.stepTiming(0.005);
+    SimHooks.stepTiming(kPeriod / 4.0);
 
     assertEquals(1, robot.m_disabledInitCount.get());
     assertEquals(1, robot.m_disabledPeriodicCount.get());
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/UltrasonicTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/UltrasonicTest.java
index a7ab0ca..da5eac7 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/UltrasonicTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/UltrasonicTest.java
@@ -4,12 +4,15 @@
 
 package edu.wpi.first.wpilibj;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import edu.wpi.first.wpilibj.simulation.UltrasonicSim;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 class UltrasonicTest {
   @Test
@@ -28,4 +31,23 @@
       assertEquals(0, ultrasonic.getRangeInches());
     }
   }
+
+  @Test
+  void automaticModeToggle() {
+    try (@SuppressWarnings("unused")
+        Ultrasonic ultrasonic = new Ultrasonic(0, 1)) {
+      assertDoesNotThrow(
+          () -> {
+            Ultrasonic.setAutomaticMode(true);
+            Ultrasonic.setAutomaticMode(false);
+            Ultrasonic.setAutomaticMode(true);
+          });
+    }
+  }
+
+  @ValueSource(booleans = {true, false})
+  @ParameterizedTest
+  void automaticModeWithZeroInstances(boolean enabling) {
+    assertDoesNotThrow(() -> Ultrasonic.setAutomaticMode(enabling));
+  }
 }
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/BooleanEventTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/BooleanEventTest.java
index 59caf23..834a0ef 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/BooleanEventTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/BooleanEventTest.java
@@ -30,43 +30,218 @@
     assertEquals(1, orCounter.get());
   }
 
+  /**
+   * Tests that composed edge events only execute on edges (two rising edge events composed with
+   * and() should only execute when both signals are on the rising edge).
+   */
+  @Test
+  void testBinaryCompositionsWithEdgeDecorators() {
+    var loop = new EventLoop();
+    var bool1 = new AtomicBoolean(false);
+    var bool2 = new AtomicBoolean(false);
+    var bool3 = new AtomicBoolean(false);
+    var bool4 = new AtomicBoolean(false);
+    var counter = new AtomicInteger(0);
+
+    var event1 = new BooleanEvent(loop, bool1::get).rising();
+    var event2 = new BooleanEvent(loop, bool2::get).rising();
+    var event3 = new BooleanEvent(loop, bool3::get).rising();
+    var event4 = new BooleanEvent(loop, bool4::get).rising();
+    event1.and(event2).ifHigh(counter::incrementAndGet);
+    event3.or(event4).ifHigh(counter::incrementAndGet);
+    assertEquals(0, counter.get());
+
+    bool1.set(true);
+    bool2.set(true);
+    bool3.set(true);
+    bool4.set(true);
+    loop.poll(); // Both actions execute
+
+    assertEquals(2, counter.get());
+
+    loop.poll(); // Nothing should happen since nothing is on rising edge
+
+    assertEquals(2, counter.get());
+
+    bool1.set(false);
+    bool2.set(false);
+    bool3.set(false);
+    bool4.set(false);
+    loop.poll(); // Nothing should happen
+
+    assertEquals(2, counter.get());
+
+    bool1.set(true);
+    loop.poll(); // Nothing should happen since only Bool 1 is on rising edge
+
+    assertEquals(2, counter.get());
+
+    bool2.set(true);
+    loop.poll(); // Bool 2 is on rising edge, but Bool 1 isn't, nothing should happen
+
+    assertEquals(2, counter.get());
+
+    bool1.set(false);
+    bool2.set(false);
+    loop.poll(); // Nothing should happen
+
+    assertEquals(2, counter.get());
+
+    bool1.set(true);
+    bool2.set(true);
+    loop.poll(); // Bool 1 and 2 are on rising edge, increments counter once
+
+    assertEquals(3, counter.get());
+
+    bool3.set(true);
+    loop.poll(); // Bool 3 is on rising edge, increments counter once
+
+    assertEquals(4, counter.get());
+
+    loop.poll(); // Nothing should happen, Bool 3 isn't on rising edge
+
+    assertEquals(4, counter.get());
+
+    bool4.set(true);
+    loop.poll(); // Bool 4 is on rising edge, increments counter once
+
+    assertEquals(5, counter.get());
+
+    loop.poll(); // Nothing should happen, Bool 4 isn't on rising edge
+
+    assertEquals(5, counter.get());
+  }
+
   @Test
   void testBinaryCompositionLoopSemantics() {
     var loop1 = new EventLoop();
     var loop2 = new EventLoop();
-
+    var bool1 = new AtomicBoolean(true);
+    var bool2 = new AtomicBoolean(true);
     var counter1 = new AtomicInteger(0);
     var counter2 = new AtomicInteger(0);
 
-    new BooleanEvent(loop1, () -> true)
-        .and(new BooleanEvent(loop2, () -> true))
+    new BooleanEvent(loop1, bool1::get)
+        .and(new BooleanEvent(loop2, bool2::get))
         .ifHigh(counter1::incrementAndGet);
 
-    new BooleanEvent(loop2, () -> true)
-        .and(new BooleanEvent(loop1, () -> true))
+    new BooleanEvent(loop2, bool2::get)
+        .and(new BooleanEvent(loop1, bool1::get))
         .ifHigh(counter2::incrementAndGet);
 
     assertEquals(0, counter1.get());
     assertEquals(0, counter2.get());
 
-    loop1.poll();
+    loop1.poll(); // 1st event executes, Bool 1 and 2 are true, increments counter
 
     assertEquals(1, counter1.get());
     assertEquals(0, counter2.get());
 
-    loop2.poll();
+    loop2.poll(); // 2nd event executes, Bool 1 and 2 are true, increments counter
 
     assertEquals(1, counter1.get());
     assertEquals(1, counter2.get());
+
+    bool2.set(false);
+    loop1.poll(); // 1st event executes, Bool 2 is still true because loop 2 hasn't updated it,
+    // increments counter
+
+    assertEquals(2, counter1.get());
+    assertEquals(1, counter2.get());
+
+    loop2.poll(); // 2nd event executes, Bool 2 is now false because this loop updated it, does
+    // nothing
+
+    assertEquals(2, counter1.get());
+    assertEquals(1, counter2.get());
+
+    loop1.poll(); // All bools are updated at this point, nothing should happen
+
+    assertEquals(2, counter1.get());
+    assertEquals(1, counter2.get());
+
+    bool2.set(true);
+    loop2.poll(); // 2nd event executes, Bool 2 is true because this loop updated it, increments
+    // counter
+
+    assertEquals(2, counter1.get());
+    assertEquals(2, counter2.get());
+
+    loop1
+        .poll(); // 1st event executes, Bool 2 is true because loop 2 updated it, increments counter
+
+    assertEquals(3, counter1.get());
+    assertEquals(2, counter2.get());
+
+    bool1.set(false);
+    loop2.poll(); // 2nd event executes, Bool 1 is still true because loop 1 hasn't updated it,
+    // increments counter
+
+    assertEquals(3, counter1.get());
+    assertEquals(3, counter2.get());
+
+    loop1.poll(); // 1st event executes, Bool 1 is false because this loop updated it, does nothing
+
+    assertEquals(3, counter1.get());
+    assertEquals(3, counter2.get());
+
+    loop2.poll(); // All bools are updated at this point, nothing should happen
+
+    assertEquals(3, counter1.get());
+    assertEquals(3, counter2.get());
+  }
+
+  /** Tests the order of actions bound to an event loop. */
+  @Test
+  void testPollOrdering() {
+    var loop = new EventLoop();
+    var bool1 = new AtomicBoolean(true);
+    var bool2 = new AtomicBoolean(true);
+    var enableAssert = new AtomicBoolean(false);
+    var counter = new AtomicInteger(0);
+    // This event binds an action to the event loop first
+    new BooleanEvent(
+            loop,
+            () -> {
+              if (enableAssert.get()) {
+                counter.incrementAndGet();
+                assertEquals(1, counter.get() % 3);
+              }
+              return bool1.get();
+            })
+        // The composed event binds an action to the event loop third
+        .and(
+            // This event binds an action to the event loop second
+            new BooleanEvent(
+                loop,
+                () -> {
+                  if (enableAssert.get()) {
+                    counter.incrementAndGet();
+                    assertEquals(2, counter.get() % 3);
+                  }
+                  return bool2.get();
+                }))
+        // This binds an action to the event loop fourth
+        .ifHigh(
+            () -> {
+              if (enableAssert.get()) {
+                counter.incrementAndGet();
+                assertEquals(0, counter.get() % 3);
+              }
+            });
+    enableAssert.set(true);
+    loop.poll();
+    loop.poll();
+    loop.poll();
+    loop.poll();
   }
 
   @Test
   void testEdgeDecorators() {
+    var loop = new EventLoop();
     var bool = new AtomicBoolean(false);
     var counter = new AtomicInteger(0);
 
-    var loop = new EventLoop();
-
     new BooleanEvent(loop, bool::get).falling().ifHigh(counter::decrementAndGet);
     new BooleanEvent(loop, bool::get).rising().ifHigh(counter::incrementAndGet);
 
@@ -93,6 +268,7 @@
     assertEquals(0, counter.get());
   }
 
+  /** Tests that binding actions to the same edge event will result in all actions executing. */
   @Test
   void testEdgeReuse() {
     var loop = new EventLoop();
@@ -112,23 +288,24 @@
     bool.set(true);
     loop.poll();
 
-    assertEquals(1, counter.get()); // FIXME?: natural sense dictates counter == 2!!
+    assertEquals(2, counter.get());
 
     loop.poll();
 
-    assertEquals(1, counter.get());
+    assertEquals(2, counter.get());
 
     bool.set(false);
     loop.poll();
 
-    assertEquals(1, counter.get());
+    assertEquals(2, counter.get());
 
     bool.set(true);
     loop.poll();
 
-    assertEquals(2, counter.get());
+    assertEquals(4, counter.get());
   }
 
+  /** Tests that all actions execute on separate edge events constructed from the original event. */
   @Test
   void testEdgeReconstruct() {
     var loop = new EventLoop();
@@ -148,8 +325,7 @@
     bool.set(true);
     loop.poll();
 
-    // unlike the previous test ...
-    assertEquals(2, counter.get()); // as natural sense dictates, counter == 2
+    assertEquals(2, counter.get());
 
     loop.poll();
 
@@ -165,4 +341,155 @@
 
     assertEquals(4, counter.get());
   }
+
+  /**
+   * Tests that all actions bound to an event will still execute even if the signal is changed
+   * during the loop poll.
+   */
+  @Test
+  void testMidLoopBooleanChange() {
+    var loop = new EventLoop();
+    var bool = new AtomicBoolean(false);
+    var counter = new AtomicInteger(0);
+
+    var event = new BooleanEvent(loop, bool::get).rising();
+    event.ifHigh(
+        () -> {
+          bool.set(false);
+          counter.incrementAndGet();
+        });
+    event.ifHigh(counter::incrementAndGet);
+
+    assertEquals(0, counter.get());
+
+    loop.poll();
+
+    assertEquals(0, counter.get());
+
+    bool.set(true);
+    loop.poll();
+
+    assertEquals(2, counter.get());
+
+    loop.poll();
+
+    assertEquals(2, counter.get());
+
+    bool.set(false);
+    loop.poll();
+
+    assertEquals(2, counter.get());
+
+    bool.set(true);
+    loop.poll();
+
+    assertEquals(4, counter.get());
+  }
+
+  /**
+   * Tests that all actions bound to composed events will still execute even if the composed signal
+   * changes during the loop poll.
+   */
+  @Test
+  void testMidLoopBooleanChangeWithComposedEvents() {
+    var loop = new EventLoop();
+    var bool1 = new AtomicBoolean(false);
+    var bool2 = new AtomicBoolean(false);
+    var bool3 = new AtomicBoolean(false);
+    var bool4 = new AtomicBoolean(false);
+    var counter = new AtomicInteger(0);
+
+    var event1 = new BooleanEvent(loop, bool1::get);
+    var event2 = new BooleanEvent(loop, bool2::get);
+    var event3 = new BooleanEvent(loop, bool3::get);
+    var event4 = new BooleanEvent(loop, bool4::get);
+    event1.ifHigh(
+        () -> {
+          bool2.set(false);
+          bool3.set(false);
+          counter.incrementAndGet();
+        });
+    event3
+        .or(event4)
+        .ifHigh(
+            () -> {
+              bool1.set(false);
+              counter.incrementAndGet();
+            });
+    event1
+        .and(event2)
+        .ifHigh(
+            () -> {
+              bool4.set(false);
+              counter.incrementAndGet();
+            });
+
+    assertEquals(0, counter.get());
+
+    bool1.set(true);
+    bool2.set(true);
+    bool3.set(true);
+    bool4.set(true);
+    loop.poll(); // All three actions execute, incrementing the counter three times and setting all
+    // booleans to false
+
+    assertEquals(3, counter.get());
+
+    loop.poll(); // Nothing should happen since everything was set to false
+
+    assertEquals(3, counter.get());
+
+    bool1.set(true);
+    bool2.set(true);
+    loop.poll(); // Bool 1 and 2 are true, increments counter twice, Bool 2 gets set to false
+
+    assertEquals(5, counter.get());
+
+    bool1.set(false);
+    loop.poll(); // Nothing should happen
+
+    assertEquals(5, counter.get());
+
+    bool1.set(true);
+    bool3.set(true);
+    loop.poll(); // Bool 1 and 3 are true, increments counter twice, Bool 3 gets set to false
+
+    assertEquals(7, counter.get());
+
+    bool1.set(false);
+    bool4.set(true);
+    loop.poll(); // Bool 4 is true, increments counter once
+
+    assertEquals(8, counter.get());
+  }
+
+  @Test
+  void testNegation() {
+    var loop = new EventLoop();
+    var bool = new AtomicBoolean(false);
+    var counter = new AtomicInteger(0);
+
+    new BooleanEvent(loop, bool::get).negate().ifHigh(counter::incrementAndGet);
+
+    assertEquals(0, counter.get());
+
+    loop.poll();
+
+    assertEquals(1, counter.get());
+
+    bool.set(true);
+    loop.poll();
+
+    assertEquals(1, counter.get());
+
+    bool.set(false);
+    loop.poll();
+
+    assertEquals(2, counter.get());
+
+    bool.set(true);
+    loop.poll();
+
+    assertEquals(2, counter.get());
+  }
 }
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/livewindow/LiveWindowTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/livewindow/LiveWindowTest.java
index 6d4a666..869fb8c 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/livewindow/LiveWindowTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/livewindow/LiveWindowTest.java
@@ -6,6 +6,7 @@
 
 import edu.wpi.first.wpilibj.UtilityClassTest;
 
+@SuppressWarnings("PMD.TestClassWithoutTestCases")
 class LiveWindowTest extends UtilityClassTest<LiveWindow> {
   LiveWindowTest() {
     super(LiveWindow.class);
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardInstanceTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardInstanceTest.java
index 53369f1..6283c68 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardInstanceTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardInstanceTest.java
@@ -11,9 +11,15 @@
 
 import edu.wpi.first.networktables.GenericEntry;
 import edu.wpi.first.networktables.NetworkTableEntry;
+import edu.wpi.first.networktables.NetworkTableEvent.Kind;
 import edu.wpi.first.networktables.NetworkTableInstance;
+import edu.wpi.first.networktables.PubSubOption;
+import edu.wpi.first.networktables.StringSubscriber;
+import java.util.EnumSet;
+import java.util.concurrent.atomic.AtomicInteger;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 class ShuffleboardInstanceTest {
@@ -121,4 +127,32 @@
     controllable = controllableEntry.getValue().getBoolean();
     assertFalse(controllable, "The nested actuator widget should have been disabled");
   }
+
+  @Disabled("Fails often at counter assertion 'expected: <2> but was: <1>'")
+  @Test
+  void testDuplicateSelectTabs() {
+    int listener = 0;
+    AtomicInteger counter = new AtomicInteger();
+    try (StringSubscriber subscriber =
+        m_ntInstance
+            .getStringTopic("/Shuffleboard/.metadata/Selected")
+            .subscribe("", PubSubOption.keepDuplicates(true)); ) {
+      listener =
+          m_ntInstance.addListener(
+              subscriber,
+              EnumSet.of(Kind.kValueAll, Kind.kImmediate),
+              event -> counter.incrementAndGet());
+
+      // There shouldn't be anything there
+      assertEquals(0, counter.get());
+
+      m_shuffleboardInstance.selectTab("tab1");
+      m_shuffleboardInstance.selectTab("tab1");
+      assertTrue(m_ntInstance.waitForListenerQueue(1.0), "Listener queue timed out!");
+      assertEquals(2, counter.get());
+
+    } finally {
+      m_ntInstance.removeListener(listener);
+    }
+  }
 }
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ADXL345SimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ADXL345SimTest.java
index 5595261..2917ec2 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ADXL345SimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ADXL345SimTest.java
@@ -11,14 +11,13 @@
 import edu.wpi.first.wpilibj.ADXL345_SPI;
 import edu.wpi.first.wpilibj.I2C;
 import edu.wpi.first.wpilibj.SPI;
-import edu.wpi.first.wpilibj.interfaces.Accelerometer;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.EnumSource;
 
 class ADXL345SimTest {
   @ParameterizedTest
-  @EnumSource(Accelerometer.Range.class)
-  void testInitI2C(Accelerometer.Range range) {
+  @EnumSource(ADXL345_I2C.Range.class)
+  void testInitI2C(ADXL345_I2C.Range range) {
     HAL.initialize(500, 0);
 
     try (ADXL345_I2C accel = new ADXL345_I2C(I2C.Port.kMXP, range)) {
@@ -40,8 +39,8 @@
   }
 
   @ParameterizedTest
-  @EnumSource(Accelerometer.Range.class)
-  void testInitSPi(Accelerometer.Range range) {
+  @EnumSource(ADXL345_SPI.Range.class)
+  void testInitSPi(ADXL345_SPI.Range range) {
     HAL.initialize(500, 0);
 
     try (ADXL345_SPI accel = new ADXL345_SPI(SPI.Port.kMXP, range)) {
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ADXL362SimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ADXL362SimTest.java
index 7ab6b6b..0f524b9 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ADXL362SimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ADXL362SimTest.java
@@ -9,14 +9,13 @@
 import edu.wpi.first.hal.HAL;
 import edu.wpi.first.wpilibj.ADXL362;
 import edu.wpi.first.wpilibj.SPI;
-import edu.wpi.first.wpilibj.interfaces.Accelerometer;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.EnumSource;
 
 class ADXL362SimTest {
   @ParameterizedTest
-  @EnumSource(Accelerometer.Range.class)
-  void testAccel(Accelerometer.Range range) {
+  @EnumSource(ADXL362.Range.class)
+  void testAccel(ADXL362.Range range) {
     HAL.initialize(500, 0);
 
     try (ADXL362 accel = new ADXL362(SPI.Port.kMXP, range)) {
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/AccelerometerSimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/AccelerometerSimTest.java
index 998b66a..d4baaed 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/AccelerometerSimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/AccelerometerSimTest.java
@@ -6,12 +6,10 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import edu.wpi.first.hal.HAL;
 import edu.wpi.first.wpilibj.BuiltInAccelerometer;
-import edu.wpi.first.wpilibj.interfaces.Accelerometer;
 import edu.wpi.first.wpilibj.simulation.testutils.BooleanCallback;
 import edu.wpi.first.wpilibj.simulation.testutils.DoubleCallback;
 import edu.wpi.first.wpilibj.simulation.testutils.EnumCallback;
@@ -102,7 +100,7 @@
 
     EnumCallback callback = new EnumCallback();
 
-    Accelerometer.Range range = Accelerometer.Range.k4G;
+    BuiltInAccelerometer.Range range = BuiltInAccelerometer.Range.k4G;
     try (CallbackStore cb = sim.registerRangeCallback(callback, false);
         BuiltInAccelerometer accel = new BuiltInAccelerometer(range)) {
       assertTrue(callback.wasTriggered());
@@ -111,7 +109,7 @@
 
       // 2G
       callback.reset();
-      range = Accelerometer.Range.k2G;
+      range = BuiltInAccelerometer.Range.k2G;
       accel.setRange(range);
       assertTrue(callback.wasTriggered());
       assertEquals(range.ordinal(), sim.getRange());
@@ -119,7 +117,7 @@
 
       // 4G
       callback.reset();
-      range = Accelerometer.Range.k4G;
+      range = BuiltInAccelerometer.Range.k4G;
       accel.setRange(range);
       assertTrue(callback.wasTriggered());
       assertEquals(range.ordinal(), sim.getRange());
@@ -127,16 +125,11 @@
 
       // 8G
       callback.reset();
-      range = Accelerometer.Range.k8G;
+      range = BuiltInAccelerometer.Range.k8G;
       accel.setRange(range);
       assertTrue(callback.wasTriggered());
       assertEquals(range.ordinal(), sim.getRange());
       assertEquals(range.ordinal(), callback.getSetValue());
-
-      // 16G - Not supported
-      callback.reset();
-      assertThrows(IllegalArgumentException.class, () -> accel.setRange(Accelerometer.Range.k16G));
-      assertFalse(callback.wasTriggered());
     }
   }
 }
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DriverStationSimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DriverStationSimTest.java
index 32cae28..9a324d5 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DriverStationSimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DriverStationSimTest.java
@@ -134,13 +134,23 @@
     DriverStationSim.setAllianceStationId(allianceStation);
 
     try (CallbackStore cb = DriverStationSim.registerAllianceStationIdCallback(callback, false)) {
+      // Unknown
+      allianceStation = AllianceStationID.Unknown;
+      DriverStationSim.setAllianceStationId(allianceStation);
+      DriverStationSim.notifyNewData();
+      assertEquals(allianceStation, DriverStationSim.getAllianceStationId());
+      assertFalse(DriverStation.getAlliance().isPresent());
+      assertFalse(DriverStation.getLocation().isPresent());
+      assertTrue(callback.wasTriggered());
+      assertEquals(allianceStation.ordinal(), callback.getSetValue());
+
       // B1
       allianceStation = AllianceStationID.Blue1;
       DriverStationSim.setAllianceStationId(allianceStation);
       DriverStationSim.notifyNewData();
       assertEquals(allianceStation, DriverStationSim.getAllianceStationId());
-      assertEquals(DriverStation.Alliance.Blue, DriverStation.getAlliance());
-      assertEquals(1, DriverStation.getLocation());
+      assertEquals(DriverStation.Alliance.Blue, DriverStation.getAlliance().get());
+      assertEquals(1, DriverStation.getLocation().getAsInt());
       assertTrue(callback.wasTriggered());
       assertEquals(allianceStation.ordinal(), callback.getSetValue());
 
@@ -149,8 +159,8 @@
       DriverStationSim.setAllianceStationId(allianceStation);
       DriverStationSim.notifyNewData();
       assertEquals(allianceStation, DriverStationSim.getAllianceStationId());
-      assertEquals(DriverStation.Alliance.Blue, DriverStation.getAlliance());
-      assertEquals(2, DriverStation.getLocation());
+      assertEquals(DriverStation.Alliance.Blue, DriverStation.getAlliance().get());
+      assertEquals(2, DriverStation.getLocation().getAsInt());
       assertTrue(callback.wasTriggered());
       assertEquals(allianceStation.ordinal(), callback.getSetValue());
 
@@ -159,8 +169,8 @@
       DriverStationSim.setAllianceStationId(allianceStation);
       DriverStationSim.notifyNewData();
       assertEquals(allianceStation, DriverStationSim.getAllianceStationId());
-      assertEquals(DriverStation.Alliance.Blue, DriverStation.getAlliance());
-      assertEquals(3, DriverStation.getLocation());
+      assertEquals(DriverStation.Alliance.Blue, DriverStation.getAlliance().get());
+      assertEquals(3, DriverStation.getLocation().getAsInt());
       assertTrue(callback.wasTriggered());
       assertEquals(allianceStation.ordinal(), callback.getSetValue());
 
@@ -169,8 +179,8 @@
       DriverStationSim.setAllianceStationId(allianceStation);
       DriverStationSim.notifyNewData();
       assertEquals(allianceStation, DriverStationSim.getAllianceStationId());
-      assertEquals(DriverStation.Alliance.Red, DriverStation.getAlliance());
-      assertEquals(1, DriverStation.getLocation());
+      assertEquals(DriverStation.Alliance.Red, DriverStation.getAlliance().get());
+      assertEquals(1, DriverStation.getLocation().getAsInt());
       assertTrue(callback.wasTriggered());
       assertEquals(allianceStation.ordinal(), callback.getSetValue());
 
@@ -179,8 +189,8 @@
       DriverStationSim.setAllianceStationId(allianceStation);
       DriverStationSim.notifyNewData();
       assertEquals(allianceStation, DriverStationSim.getAllianceStationId());
-      assertEquals(DriverStation.Alliance.Red, DriverStation.getAlliance());
-      assertEquals(2, DriverStation.getLocation());
+      assertEquals(DriverStation.Alliance.Red, DriverStation.getAlliance().get());
+      assertEquals(2, DriverStation.getLocation().getAsInt());
       assertTrue(callback.wasTriggered());
       assertEquals(allianceStation.ordinal(), callback.getSetValue());
 
@@ -189,8 +199,8 @@
       DriverStationSim.setAllianceStationId(allianceStation);
       DriverStationSim.notifyNewData();
       assertEquals(allianceStation, DriverStationSim.getAllianceStationId());
-      assertEquals(DriverStation.Alliance.Red, DriverStation.getAlliance());
-      assertEquals(3, DriverStation.getLocation());
+      assertEquals(DriverStation.Alliance.Red, DriverStation.getAlliance().get());
+      assertEquals(3, DriverStation.getLocation().getAsInt());
       assertTrue(callback.wasTriggered());
       assertEquals(allianceStation.ordinal(), callback.getSetValue());
     }
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DutyCycleEncoderSimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DutyCycleEncoderSimTest.java
index 49f8e77..42a6faa 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DutyCycleEncoderSimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DutyCycleEncoderSimTest.java
@@ -5,6 +5,8 @@
 package edu.wpi.first.wpilibj.simulation;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import edu.wpi.first.hal.HAL;
 import edu.wpi.first.wpilibj.DutyCycleEncoder;
@@ -32,4 +34,56 @@
       assertEquals(19.1, encoder.getDistance());
     }
   }
+
+  @Test
+  void setDistancePerRotationTest() {
+    HAL.initialize(500, 0);
+
+    try (DutyCycleEncoder encoder = new DutyCycleEncoder(0)) {
+      DutyCycleEncoderSim sim = new DutyCycleEncoderSim(encoder);
+      sim.set(1.5);
+      encoder.setDistancePerRotation(42);
+      assertEquals(63.0, encoder.getDistance());
+    }
+  }
+
+  @Test
+  void setAbsolutePositionTest() {
+    HAL.initialize(500, 0);
+
+    try (DutyCycleEncoder encoder = new DutyCycleEncoder(0)) {
+      DutyCycleEncoderSim sim = new DutyCycleEncoderSim(encoder);
+
+      sim.setAbsolutePosition(0.75);
+      assertEquals(0.75, encoder.getAbsolutePosition());
+    }
+  }
+
+  @Test
+  void setIsConnectedTest() {
+    HAL.initialize(500, 0);
+
+    try (DutyCycleEncoder encoder = new DutyCycleEncoder(0)) {
+      DutyCycleEncoderSim sim = new DutyCycleEncoderSim(encoder);
+
+      sim.setConnected(true);
+      assertTrue(encoder.isConnected());
+      sim.setConnected(false);
+      assertFalse(encoder.isConnected());
+    }
+  }
+
+  @Test
+  void resetTest() {
+    HAL.initialize(500, 0);
+
+    try (DutyCycleEncoder encoder = new DutyCycleEncoder(0)) {
+      DutyCycleEncoderSim sim = new DutyCycleEncoderSim(encoder);
+
+      sim.setDistance(2.5);
+      assertEquals(2.5, encoder.getDistance());
+      encoder.reset();
+      assertEquals(0.0, encoder.getDistance());
+    }
+  }
 }
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java
index fd2e7ba..336489a 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java
@@ -33,6 +33,7 @@
             0.0,
             3.0,
             true,
+            0.0,
             VecBuilder.fill(0.01));
 
     try (var motor = new PWMVictorSPX(0);
@@ -71,6 +72,7 @@
             0.0,
             1.0,
             true,
+            0.0,
             VecBuilder.fill(0.01));
 
     for (int i = 0; i < 100; i++) {
@@ -91,7 +93,8 @@
   @Test
   void testStability() {
     var sim =
-        new ElevatorSim(DCMotor.getVex775Pro(4), 100, 4, Units.inchesToMeters(0.5), 0, 10, true);
+        new ElevatorSim(
+            DCMotor.getVex775Pro(4), 100, 4, Units.inchesToMeters(0.5), 0, 10, false, 0.0);
 
     sim.setState(VecBuilder.fill(0, 0));
     sim.setInput(12);
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/EncoderSimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/EncoderSimTest.java
index eebabeb..7d3fe73 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/EncoderSimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/EncoderSimTest.java
@@ -89,4 +89,23 @@
       }
     }
   }
+
+  @Test
+  void testDistancePerPulse() {
+    HAL.initialize(500, 0);
+
+    try (Encoder encoder = new Encoder(0, 1)) {
+      EncoderSim sim = new EncoderSim(encoder);
+      sim.resetData();
+
+      DoubleCallback callback = new DoubleCallback();
+      try (CallbackStore cb = sim.registerDistancePerPulseCallback(callback, false)) {
+        sim.setDistancePerPulse(0.03405);
+        assertEquals(0.03405, sim.getDistancePerPulse());
+        assertEquals(0.03405, encoder.getDistancePerPulse());
+        assertTrue(callback.wasTriggered());
+        assertEquals(0.03405, callback.getSetValue());
+      }
+    }
+  }
 }
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/PWMSimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/PWMSimTest.java
index 2e76aa2..98a05af 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/PWMSimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/PWMSimTest.java
@@ -16,6 +16,8 @@
 import org.junit.jupiter.api.Test;
 
 class PWMSimTest {
+  private static final double PWM_STEP_SIZE = 1.0 / 2000.0;
+
   @Test
   void testInitialize() {
     HAL.initialize(500, 0);
@@ -33,7 +35,7 @@
   }
 
   @Test
-  void testSetRawValue() {
+  void testSetPulseTime() {
     HAL.initialize(500, 0);
 
     PWMSim sim = new PWMSim(0);
@@ -42,13 +44,13 @@
 
     IntCallback callback = new IntCallback();
 
-    try (CallbackStore cb = sim.registerRawValueCallback(callback, false);
+    try (CallbackStore cb = sim.registerPulseMicrosecondCallback(callback, false);
         PWM pwm = new PWM(0)) {
-      sim.setRawValue(229);
-      assertEquals(229, sim.getRawValue());
-      assertEquals(229, pwm.getRaw());
+      sim.setPulseMicrosecond(2290);
+      assertEquals(2290, sim.getPulseMicrosecond());
+      assertEquals(2290, pwm.getPulseTimeMicroseconds());
       assertTrue(callback.wasTriggered());
-      assertEquals(229, callback.getSetValue());
+      assertEquals(2290, callback.getSetValue());
     }
   }
 
@@ -64,13 +66,47 @@
 
     try (CallbackStore cb = sim.registerSpeedCallback(callback, false);
         PWM pwm = new PWM(0)) {
-      final double kTestValue = 0.3504;
+      double kTestValue = 0.3504;
       pwm.setSpeed(kTestValue);
 
-      assertEquals(kTestValue, sim.getSpeed());
-      assertEquals(kTestValue, pwm.getSpeed());
+      assertEquals(kTestValue, sim.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, pwm.getSpeed(), PWM_STEP_SIZE);
       assertTrue(callback.wasTriggered());
-      assertEquals(kTestValue, callback.getSetValue());
+      assertEquals(kTestValue, callback.getSetValue(), PWM_STEP_SIZE);
+      assertEquals(kTestValue / 2 + 0.5, sim.getPosition(), PWM_STEP_SIZE * 2);
+      assertEquals(kTestValue / 2 + 0.5, pwm.getPosition(), PWM_STEP_SIZE * 2);
+
+      kTestValue = -1.0;
+      pwm.setSpeed(kTestValue);
+
+      assertEquals(kTestValue, sim.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, pwm.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(0.0, sim.getPosition(), PWM_STEP_SIZE);
+      assertEquals(0.0, pwm.getPosition(), PWM_STEP_SIZE);
+
+      kTestValue = 0.0;
+      pwm.setSpeed(kTestValue);
+
+      assertEquals(kTestValue, sim.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, pwm.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(0.5, sim.getPosition(), PWM_STEP_SIZE);
+      assertEquals(0.5, pwm.getPosition(), PWM_STEP_SIZE);
+
+      kTestValue = 1.0;
+      pwm.setSpeed(kTestValue);
+
+      assertEquals(kTestValue, sim.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, pwm.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, sim.getPosition(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, pwm.getPosition(), PWM_STEP_SIZE);
+
+      kTestValue = 1.1;
+      pwm.setSpeed(kTestValue);
+
+      assertEquals(1.0, sim.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(1.0, pwm.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(1.0, sim.getPosition(), PWM_STEP_SIZE);
+      assertEquals(1.0, pwm.getPosition(), PWM_STEP_SIZE);
     }
   }
 
@@ -86,13 +122,47 @@
 
     try (CallbackStore cb = sim.registerPositionCallback(callback, false);
         PWM pwm = new PWM(0)) {
-      final double kTestValue = 0.3504;
+      double kTestValue = 0.3504;
       pwm.setPosition(kTestValue);
 
-      assertEquals(kTestValue, sim.getPosition());
-      assertEquals(kTestValue, pwm.getPosition());
+      assertEquals(kTestValue, sim.getPosition(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, pwm.getPosition(), PWM_STEP_SIZE);
       assertTrue(callback.wasTriggered());
-      assertEquals(kTestValue, callback.getSetValue());
+      assertEquals(kTestValue, callback.getSetValue(), PWM_STEP_SIZE);
+      assertEquals(kTestValue * 2 - 1.0, sim.getSpeed(), PWM_STEP_SIZE * 2);
+      assertEquals(kTestValue * 2 - 1.0, pwm.getSpeed(), PWM_STEP_SIZE * 2);
+
+      kTestValue = -1.0;
+      pwm.setPosition(kTestValue);
+
+      assertEquals(0.0, sim.getPosition(), PWM_STEP_SIZE);
+      assertEquals(0.0, pwm.getPosition(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, sim.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, pwm.getSpeed(), PWM_STEP_SIZE);
+
+      kTestValue = 0.0;
+      pwm.setPosition(kTestValue);
+
+      assertEquals(kTestValue, sim.getPosition(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, pwm.getPosition(), PWM_STEP_SIZE);
+      assertEquals(-1.0, sim.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(-1.0, pwm.getSpeed(), PWM_STEP_SIZE);
+
+      kTestValue = 1.0;
+      pwm.setPosition(kTestValue);
+
+      assertEquals(kTestValue, sim.getPosition(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, pwm.getPosition(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, sim.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(kTestValue, pwm.getSpeed(), PWM_STEP_SIZE);
+
+      kTestValue = 1.1;
+      pwm.setPosition(kTestValue);
+
+      assertEquals(1.0, sim.getPosition(), PWM_STEP_SIZE);
+      assertEquals(1.0, pwm.getPosition(), PWM_STEP_SIZE);
+      assertEquals(1.0, sim.getSpeed(), PWM_STEP_SIZE);
+      assertEquals(1.0, pwm.getSpeed(), PWM_STEP_SIZE);
     }
   }
 
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/RoboRioSimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/RoboRioSimTest.java
index 9b15b77..c016ec4 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/RoboRioSimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/RoboRioSimTest.java
@@ -210,6 +210,40 @@
   }
 
   @Test
+  void testCPUTemp() {
+    RoboRioSim.resetData();
+
+    DoubleCallback callback = new DoubleCallback();
+
+    try (CallbackStore cb = RoboRioSim.registerCPUTempCallback(callback, false)) {
+      final double kCPUTemp = 100.0;
+
+      RoboRioSim.setCPUTemp(kCPUTemp);
+      assertTrue(callback.wasTriggered());
+      assertEquals(kCPUTemp, callback.getSetValue());
+      assertEquals(kCPUTemp, RoboRioSim.getCPUTemp());
+      assertEquals(kCPUTemp, RobotController.getCPUTemp());
+    }
+  }
+
+  @Test
+  void testTeamNumber() {
+    RoboRioSim.resetData();
+
+    IntCallback callback = new IntCallback();
+
+    try (CallbackStore cb = RoboRioSim.registerTeamNumberCallback(callback, false)) {
+      final int kTeamNumber = 9999;
+
+      RoboRioSim.setTeamNumber(kTeamNumber);
+      assertTrue(callback.wasTriggered());
+      assertEquals(kTeamNumber, callback.getSetValue());
+      assertEquals(kTeamNumber, RoboRioSim.getTeamNumber());
+      assertEquals(kTeamNumber, RobotController.getTeamNumber());
+    }
+  }
+
+  @Test
   void testSerialNumber() {
     RoboRioSim.resetData();
 
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SimDeviceSimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SimDeviceSimTest.java
index 1daea96..62a24d1 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SimDeviceSimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SimDeviceSimTest.java
@@ -27,6 +27,8 @@
       assertFalse(simBool.get());
       simBool.set(true);
       assertTrue(devBool.get());
+
+      assertEquals(dev.getName(), "test");
     }
   }
 
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSimTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSimTest.java
index 1be29a9..928fd3e 100644
--- a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSimTest.java
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSimTest.java
@@ -15,16 +15,17 @@
   SingleJointedArmSim m_sim =
       new SingleJointedArmSim(
           DCMotor.getVex775Pro(2),
-          100,
+          300,
           3.0,
-          Units.inchesToMeters(19.0 / 2.0),
+          Units.inchesToMeters(30.0),
           -Math.PI,
           0.0,
-          10.0 / 2.2,
-          true);
+          true,
+          Math.PI / 2.0);
 
   @Test
   void testArmDisabled() {
+    // Reset Arm angle to 0
     m_sim.setState(VecBuilder.fill(0.0, 0.0));
 
     for (int i = 0; i < 12 / 0.02; i++) {
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/smartdashboard/Mechanism2dTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/smartdashboard/Mechanism2dTest.java
new file mode 100644
index 0000000..c946b08
--- /dev/null
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/smartdashboard/Mechanism2dTest.java
@@ -0,0 +1,99 @@
+// 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.
+
+package edu.wpi.first.wpilibj.smartdashboard;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import edu.wpi.first.networktables.NetworkTableInstance;
+import edu.wpi.first.wpilibj.util.Color8Bit;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class Mechanism2dTest {
+  private NetworkTableInstance m_inst;
+
+  @BeforeEach
+  void setUp() {
+    m_inst = NetworkTableInstance.create();
+    SmartDashboard.setNetworkTableInstance(m_inst);
+  }
+
+  @Test
+  void testCanvas() {
+    try (var mechanism = new Mechanism2d(5, 10);
+        var dimsEntry = m_inst.getEntry("/SmartDashboard/mechanism/dims");
+        var colorEntry = m_inst.getEntry("/SmartDashboard/mechanism/backgroundColor")) {
+      SmartDashboard.putData("mechanism", mechanism);
+      SmartDashboard.updateValues();
+      assertArrayEquals(new double[] {5, 10}, dimsEntry.getDoubleArray(new double[0]));
+      assertEquals("#000020", colorEntry.getString(""));
+      mechanism.setBackgroundColor(new Color8Bit(255, 255, 255));
+      SmartDashboard.updateValues();
+      assertEquals("#FFFFFF", colorEntry.getString(""));
+    }
+  }
+
+  @Test
+  void testRoot() {
+    try (var mechanism = new Mechanism2d(5, 10);
+        var xEntry = m_inst.getEntry("/SmartDashboard/mechanism/root/x");
+        var yEntry = m_inst.getEntry("/SmartDashboard/mechanism/root/y")) {
+      final var root = mechanism.getRoot("root", 1, 2);
+      SmartDashboard.putData("mechanism", mechanism);
+      SmartDashboard.updateValues();
+      assertEquals(1, xEntry.getDouble(0));
+      assertEquals(2, yEntry.getDouble(0));
+      root.setPosition(2, 4);
+      SmartDashboard.updateValues();
+      assertEquals(2, xEntry.getDouble(0));
+      assertEquals(4, yEntry.getDouble(0));
+    }
+  }
+
+  @Test
+  void testLigament() {
+    try (var mechanism = new Mechanism2d(5, 10);
+        var angleEntry = m_inst.getEntry("/SmartDashboard/mechanism/root/ligament/angle");
+        var colorEntry = m_inst.getEntry("/SmartDashboard/mechanism/root/ligament/color");
+        var lengthEntry = m_inst.getEntry("/SmartDashboard/mechanism/root/ligament/length");
+        var weightEntry = m_inst.getEntry("/SmartDashboard/mechanism/root/ligament/weight")) {
+      var root = mechanism.getRoot("root", 1, 2);
+      var ligament =
+          root.append(new MechanismLigament2d("ligament", 3, 90, 1, new Color8Bit(255, 255, 255)));
+      SmartDashboard.putData("mechanism", mechanism);
+      SmartDashboard.updateValues();
+      assertEquals(ligament.getAngle(), angleEntry.getDouble(0));
+      assertEquals(ligament.getColor().toHexString(), colorEntry.getString(""));
+      assertEquals(ligament.getLength(), lengthEntry.getDouble(0));
+      assertEquals(ligament.getLineWeight(), weightEntry.getDouble(0));
+      ligament.setAngle(45);
+      ligament.setColor(new Color8Bit(0, 0, 0));
+      ligament.setLength(2);
+      ligament.setLineWeight(4);
+      SmartDashboard.updateValues();
+      assertEquals(ligament.getAngle(), angleEntry.getDouble(0));
+      assertEquals(ligament.getColor().toHexString(), colorEntry.getString(""));
+      assertEquals(ligament.getLength(), lengthEntry.getDouble(0));
+      assertEquals(ligament.getLineWeight(), weightEntry.getDouble(0));
+      angleEntry.setDouble(22.5);
+      colorEntry.setString("#FF00FF");
+      lengthEntry.setDouble(4);
+      weightEntry.setDouble(6);
+      SmartDashboard.updateValues();
+      assertEquals(ligament.getAngle(), angleEntry.getDouble(0));
+      assertEquals(ligament.getColor().toHexString(), colorEntry.getString(""));
+      assertEquals(ligament.getLength(), lengthEntry.getDouble(0));
+      assertEquals(ligament.getLineWeight(), weightEntry.getDouble(0));
+    }
+  }
+
+  @AfterEach
+  void tearDown() {
+    m_inst.close();
+    SmartDashboard.setNetworkTableInstance(NetworkTableInstance.getDefault());
+  }
+}
diff --git a/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooserTest.java b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooserTest.java
new file mode 100644
index 0000000..07f09af
--- /dev/null
+++ b/third_party/allwpilib/wpilibj/src/test/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooserTest.java
@@ -0,0 +1,93 @@
+// 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.
+
+package edu.wpi.first.wpilibj.smartdashboard;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import edu.wpi.first.networktables.NetworkTableInstance;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+class SendableChooserTest {
+  private NetworkTableInstance m_inst;
+
+  @BeforeEach
+  void setUp() {
+    m_inst = NetworkTableInstance.create();
+    SmartDashboard.setNetworkTableInstance(m_inst);
+  }
+
+  @ValueSource(ints = {0, 1, 2, 3})
+  @ParameterizedTest
+  void returnsSelected(int toSelect) {
+    try (var chooser = new SendableChooser<Integer>();
+        var publisher =
+            m_inst
+                .getStringTopic("/SmartDashboard/returnsSelectedChooser" + toSelect + "/selected")
+                .publish()) {
+      for (int i = 1; i <= 3; i++) {
+        chooser.addOption(String.valueOf(i), i);
+      }
+      chooser.setDefaultOption(String.valueOf(0), 0);
+
+      SmartDashboard.putData("returnsSelectedChooser" + toSelect, chooser);
+      SmartDashboard.updateValues();
+      publisher.set(String.valueOf(toSelect));
+      SmartDashboard.updateValues();
+      assertEquals(toSelect, chooser.getSelected());
+    }
+  }
+
+  @Test
+  void defaultIsReturnedOnNoSelect() {
+    try (var chooser = new SendableChooser<Integer>()) {
+      for (int i = 1; i <= 3; i++) {
+        chooser.addOption(String.valueOf(i), i);
+      }
+      chooser.setDefaultOption(String.valueOf(0), 0);
+
+      assertEquals(0, chooser.getSelected());
+    }
+  }
+
+  @Test
+  void nullIsReturnedOnNoSelectAndNoDefault() {
+    try (var chooser = new SendableChooser<Integer>()) {
+      for (int i = 1; i <= 3; i++) {
+        chooser.addOption(String.valueOf(i), i);
+      }
+
+      assertNull(chooser.getSelected());
+    }
+  }
+
+  @Test
+  void testChangeListener() {
+    try (var chooser = new SendableChooser<Integer>()) {
+      for (int i = 1; i <= 3; i++) {
+        chooser.addOption(String.valueOf(i), i);
+      }
+      AtomicInteger currentVal = new AtomicInteger();
+      chooser.onChange(val -> currentVal.set(val));
+
+      SmartDashboard.putData("changeListenerChooser", chooser);
+      SmartDashboard.updateValues();
+      SmartDashboard.putString("changeListenerChooser/selected", "3");
+      SmartDashboard.updateValues();
+      assertEquals(3, currentVal.get());
+    }
+  }
+
+  @AfterEach
+  void tearDown() {
+    m_inst.close();
+    SmartDashboard.setNetworkTableInstance(NetworkTableInstance.getDefault());
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/build.gradle b/third_party/allwpilib/wpilibjExamples/build.gradle
index deb8af1..d5f71e9 100644
--- a/third_party/allwpilib/wpilibjExamples/build.gradle
+++ b/third_party/allwpilib/wpilibjExamples/build.gradle
@@ -11,20 +11,6 @@
 
 apply from: "${rootDir}/shared/opencv.gradle"
 
-test {
-    useJUnitPlatform()
-    systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true'
-    testLogging {
-        events "failed"
-        exceptionFormat "full"
-    }
-    finalizedBy jacocoTestReport
-}
-
-if (project.hasProperty('onlylinuxathena') || project.hasProperty('onlylinuxarm32') || project.hasProperty('onlylinuxarm64')) {
-    test.enabled = false
-}
-
 dependencies {
     implementation project(':wpilibj')
     implementation project(':apriltag')
@@ -36,14 +22,16 @@
     implementation project(':cscore')
     implementation project(':cameraserver')
     implementation project(':wpilibNewCommands')
+    implementation project(':romiVendordep')
+    implementation project(':xrpVendordep')
 
-    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
-    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'
-    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
+    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
+    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0'
+    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
 }
 
 jacoco {
-    toolVersion = "0.8.8"
+    toolVersion = "0.8.10"
 }
 
 jacocoTestReport {
@@ -108,6 +96,8 @@
             }
             binaries.all { binary ->
                 lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'shared'
+                lib project: ':romiVendordep', library: 'romiVendordep', linkage: 'shared'
+                lib project: ':xrpVendordep', library: 'xrpVendordep', linkage: 'shared'
                 lib project: ':apriltag', library: 'apriltag', linkage: 'shared'
                 lib project: ':wpilibc', library: 'wpilibc', linkage: 'shared'
                 lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
@@ -170,6 +160,7 @@
                                 test.dependsOn it.tasks.install
                                 test.systemProperty 'java.library.path', filePath
                                 test.environment 'LD_LIBRARY_PATH', filePath
+                                test.environment 'DYLD_LIBRARY_PATH', filePath
                                 test.workingDir filePath
                             }
 
@@ -181,6 +172,7 @@
                                     run.dependsOn it.tasks.install
                                     run.systemProperty 'java.library.path', filePath
                                     run.environment 'LD_LIBRARY_PATH', filePath
+                                    run.environment 'DYLD_LIBRARY_PATH', filePath
                                     run.workingDir filePath
                                     doFirst { doFirstTask(run) }
 
@@ -193,6 +185,14 @@
                                     testTask.useJUnitPlatform()
                                     testTask.filter {
                                         includeTestsMatching("edu.wpi.first.wpilibj.examples.${entry.foldername}.*")
+                                        // armsimulation regularly fails on CI Win64Debug
+                                        if (project.hasProperty('ciDebugOnly')) {
+                                            excludeTestsMatching("edu.wpi.first.wpilibj.examples.armsimulation.*")
+                                        }
+                                        setFailOnNoMatchingTests(false)
+                                    }
+                                    test.filter {
+                                        excludeTestsMatching("edu.wpi.first.wpilibj.examples.${entry.foldername}.*")
                                         setFailOnNoMatchingTests(false)
                                     }
                                     testTask.classpath = sourceSets.test.runtimeClasspath
@@ -205,7 +205,13 @@
                                     }
                                     testTask.systemProperty 'java.library.path', filePath
                                     testTask.environment 'LD_LIBRARY_PATH', filePath
+                                    testTask.environment 'DYLD_LIBRARY_PATH', filePath
                                     testTask.workingDir filePath
+
+                                    if (project.hasProperty('onlylinuxathena') || project.hasProperty('onlylinuxarm32') || project.hasProperty('onlylinuxarm64') || project.hasProperty('onlywindowsarm64')) {
+                                        testTask.enabled = false
+                                    }
+                                    test.dependsOn(testTask)
                                 }
                             }
 
diff --git a/third_party/allwpilib/wpilibjExamples/publish.gradle b/third_party/allwpilib/wpilibjExamples/publish.gradle
index a5ad1c6..fc90e78 100644
--- a/third_party/allwpilib/wpilibjExamples/publish.gradle
+++ b/third_party/allwpilib/wpilibjExamples/publish.gradle
@@ -22,6 +22,10 @@
     from('src/main/java/edu/wpi/first/wpilibj/examples') {
         into 'examples'
     }
+
+    from('src/test/java/edu/wpi/first/wpilibj/examples') {
+        into 'examples_test'
+    }
 }
 
 task javaTemplatesZip(type: Zip) {
@@ -35,6 +39,10 @@
     from('src/main/java/edu/wpi/first/wpilibj/templates') {
         into 'templates'
     }
+
+    from('src/test/java/edu/wpi/first/wpilibj/templates') {
+        into 'templates_test'
+    }
 }
 
 task javaCommandsZip(type: Zip) {
@@ -48,6 +56,10 @@
     from('src/main/java/edu/wpi/first/wpilibj/commands') {
         into 'commands'
     }
+
+    from('src/test/java/edu/wpi/first/wpilibj/commands') {
+        into 'commands_test'
+    }
 }
 
 build.dependsOn javaTemplatesZip
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/command2/ReplaceMeCommand.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/command2/ReplaceMeCommand.java
index a9a96ef..2d90dc2 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/command2/ReplaceMeCommand.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/command2/ReplaceMeCommand.java
@@ -4,9 +4,9 @@
 
 package edu.wpi.first.wpilibj.commands.command2;
 
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
-public class ReplaceMeCommand extends CommandBase {
+public class ReplaceMeCommand extends Command {
   /** Creates a new ReplaceMeCommand. */
   public ReplaceMeCommand() {
     // Use addRequirements() here to declare subsystem dependencies.
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/commands.json b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/commands.json
index f6f2c37..d8d57f7 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/commands.json
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/commands.json
@@ -131,7 +131,7 @@
   },
   {
     "name": "TrapezoidProfileSubsystem",
-    "description": "A subystem that executes a trapezoidal motion profile.",
+    "description": "A subsystem that executes a trapezoidal motion profile.",
     "tags": [
       "trapezoidprofilesubsystem"
     ],
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.java
index 7a55719..5ed9fda 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.java
@@ -17,13 +17,13 @@
         // The motion profile to be executed
         new TrapezoidProfile(
             // The motion profile constraints
-            new TrapezoidProfile.Constraints(0, 0),
-            // Goal state
-            new TrapezoidProfile.State(),
-            // Initial state
-            new TrapezoidProfile.State()),
+            new TrapezoidProfile.Constraints(0, 0)),
         state -> {
           // Use current trajectory state here
-        });
+        },
+        // Goal state
+        () -> new TrapezoidProfile.State(),
+        // Current state
+        () -> new TrapezoidProfile.State());
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/apriltagsvision/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/apriltagsvision/Main.java
new file mode 100644
index 0000000..0bb6251
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/apriltagsvision/Main.java
@@ -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.
+
+package edu.wpi.first.wpilibj.examples.apriltagsvision;
+
+import edu.wpi.first.wpilibj.RobotBase;
+
+/**
+ * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
+ * you are doing, do not modify this file except to change the parameter class to the startRobot
+ * call.
+ */
+public final class Main {
+  private Main() {}
+
+  /**
+   * Main initialization function. Do not perform any initialization here.
+   *
+   * <p>If you change your main robot class, change the parameter type.
+   */
+  public static void main(String... args) {
+    RobotBase.startRobot(Robot::new);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/apriltagsvision/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/apriltagsvision/Robot.java
new file mode 100644
index 0000000..a73ea0b
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/apriltagsvision/Robot.java
@@ -0,0 +1,147 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.apriltagsvision;
+
+import edu.wpi.first.apriltag.AprilTagDetection;
+import edu.wpi.first.apriltag.AprilTagDetector;
+import edu.wpi.first.apriltag.AprilTagPoseEstimator;
+import edu.wpi.first.cameraserver.CameraServer;
+import edu.wpi.first.cscore.CvSink;
+import edu.wpi.first.cscore.CvSource;
+import edu.wpi.first.cscore.UsbCamera;
+import edu.wpi.first.math.geometry.Rotation3d;
+import edu.wpi.first.math.geometry.Transform3d;
+import edu.wpi.first.networktables.IntegerArrayPublisher;
+import edu.wpi.first.networktables.NetworkTable;
+import edu.wpi.first.networktables.NetworkTableInstance;
+import edu.wpi.first.wpilibj.TimedRobot;
+import java.util.ArrayList;
+import org.opencv.core.Mat;
+import org.opencv.core.Point;
+import org.opencv.core.Scalar;
+import org.opencv.imgproc.Imgproc;
+
+/**
+ * This is a demo program showing the detection of AprilTags. The image is acquired from the USB
+ * camera, then any detected AprilTags are marked up on the image and sent to the dashboard.
+ *
+ * <p>Be aware that the performance on this is much worse than a coprocessor solution!
+ */
+public class Robot extends TimedRobot {
+  @Override
+  public void robotInit() {
+    var visionThread = new Thread(() -> apriltagVisionThreadProc());
+    visionThread.setDaemon(true);
+    visionThread.start();
+  }
+
+  void apriltagVisionThreadProc() {
+    var detector = new AprilTagDetector();
+    // look for tag16h5, don't correct any error bits
+    detector.addFamily("tag16h5", 0);
+
+    // Set up Pose Estimator - parameters are for a Microsoft Lifecam HD-3000
+    // (https://www.chiefdelphi.com/t/wpilib-apriltagdetector-sample-code/421411/21)
+    var poseEstConfig =
+        new AprilTagPoseEstimator.Config(
+            0.1524, 699.3778103158814, 677.7161226393544, 345.6059345433618, 207.12741326228522);
+    var estimator = new AprilTagPoseEstimator(poseEstConfig);
+
+    // Get the UsbCamera from CameraServer
+    UsbCamera camera = CameraServer.startAutomaticCapture();
+    // Set the resolution
+    camera.setResolution(640, 480);
+
+    // Get a CvSink. This will capture Mats from the camera
+    CvSink cvSink = CameraServer.getVideo();
+    // Setup a CvSource. This will send images back to the Dashboard
+    CvSource outputStream = CameraServer.putVideo("Detected", 640, 480);
+
+    // Mats are very memory expensive. Lets reuse these.
+    var mat = new Mat();
+    var grayMat = new Mat();
+
+    // Instantiate once
+    ArrayList<Long> tags = new ArrayList<>();
+    var outlineColor = new Scalar(0, 255, 0);
+    var crossColor = new Scalar(0, 0, 255);
+
+    // We'll output to NT
+    NetworkTable tagsTable = NetworkTableInstance.getDefault().getTable("apriltags");
+    IntegerArrayPublisher pubTags = tagsTable.getIntegerArrayTopic("tags").publish();
+
+    // This cannot be 'true'. The program will never exit if it is. This
+    // lets the robot stop this thread when restarting robot code or
+    // deploying.
+    while (!Thread.interrupted()) {
+      // Tell the CvSink to grab a frame from the camera and put it
+      // in the source mat.  If there is an error notify the output.
+      if (cvSink.grabFrame(mat) == 0) {
+        // Send the output the error.
+        outputStream.notifyError(cvSink.getError());
+        // skip the rest of the current iteration
+        continue;
+      }
+
+      Imgproc.cvtColor(mat, grayMat, Imgproc.COLOR_RGB2GRAY);
+
+      AprilTagDetection[] detections = detector.detect(grayMat);
+
+      // have not seen any tags yet
+      tags.clear();
+
+      for (AprilTagDetection detection : detections) {
+        // remember we saw this tag
+        tags.add((long) detection.getId());
+
+        // draw lines around the tag
+        for (var i = 0; i <= 3; i++) {
+          var j = (i + 1) % 4;
+          var pt1 = new Point(detection.getCornerX(i), detection.getCornerY(i));
+          var pt2 = new Point(detection.getCornerX(j), detection.getCornerY(j));
+          Imgproc.line(mat, pt1, pt2, outlineColor, 2);
+        }
+
+        // mark the center of the tag
+        var cx = detection.getCenterX();
+        var cy = detection.getCenterY();
+        var ll = 10;
+        Imgproc.line(mat, new Point(cx - ll, cy), new Point(cx + ll, cy), crossColor, 2);
+        Imgproc.line(mat, new Point(cx, cy - ll), new Point(cx, cy + ll), crossColor, 2);
+
+        // identify the tag
+        Imgproc.putText(
+            mat,
+            Integer.toString(detection.getId()),
+            new Point(cx + ll, cy),
+            Imgproc.FONT_HERSHEY_SIMPLEX,
+            1,
+            crossColor,
+            3);
+
+        // determine pose
+        Transform3d pose = estimator.estimate(detection);
+
+        // put pose into dashboard
+        Rotation3d rot = pose.getRotation();
+        tagsTable
+            .getEntry("pose_" + detection.getId())
+            .setDoubleArray(
+                new double[] {
+                  pose.getX(), pose.getY(), pose.getZ(), rot.getX(), rot.getY(), rot.getZ()
+                });
+      }
+
+      // put list of tags onto dashboard
+      pubTags.set(tags.stream().mapToLong(Long::longValue).toArray());
+
+      // Give the output stream a new image to display
+      outputStream.putFrame(mat);
+    }
+
+    pubTags.close();
+    detector.close();
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Constants.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Constants.java
new file mode 100644
index 0000000..68f8285
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Constants.java
@@ -0,0 +1,31 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.armsimulation;
+
+import edu.wpi.first.math.util.Units;
+
+public class Constants {
+  public static final int kMotorPort = 0;
+  public static final int kEncoderAChannel = 0;
+  public static final int kEncoderBChannel = 1;
+  public static final int kJoystickPort = 0;
+
+  public static final String kArmPositionKey = "ArmPosition";
+  public static final String kArmPKey = "ArmP";
+
+  // The P gain for the PID controller that drives this arm.
+  public static final double kDefaultArmKp = 50.0;
+  public static final double kDefaultArmSetpointDegrees = 75.0;
+
+  // distance per pulse = (angle per revolution) / (pulses per revolution)
+  //  = (2 * PI rads) / (4096 pulses)
+  public static final double kArmEncoderDistPerPulse = 2.0 * Math.PI / 4096;
+
+  public static final double kArmReduction = 200;
+  public static final double kArmMass = 8.0; // Kilograms
+  public static final double kArmLength = Units.inchesToMeters(30);
+  public static final double kMinAngleRads = Units.degreesToRadians(-75);
+  public static final double kMaxAngleRads = Units.degreesToRadians(255);
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java
index c84d922..80a1121 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java
@@ -4,151 +4,48 @@
 
 package edu.wpi.first.wpilibj.examples.armsimulation;
 
-import edu.wpi.first.math.VecBuilder;
-import edu.wpi.first.math.controller.PIDController;
-import edu.wpi.first.math.system.plant.DCMotor;
-import edu.wpi.first.math.util.Units;
-import edu.wpi.first.wpilibj.Encoder;
 import edu.wpi.first.wpilibj.Joystick;
-import edu.wpi.first.wpilibj.Preferences;
-import edu.wpi.first.wpilibj.RobotController;
 import edu.wpi.first.wpilibj.TimedRobot;
-import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
-import edu.wpi.first.wpilibj.simulation.BatterySim;
-import edu.wpi.first.wpilibj.simulation.EncoderSim;
-import edu.wpi.first.wpilibj.simulation.RoboRioSim;
-import edu.wpi.first.wpilibj.simulation.SingleJointedArmSim;
-import edu.wpi.first.wpilibj.smartdashboard.Mechanism2d;
-import edu.wpi.first.wpilibj.smartdashboard.MechanismLigament2d;
-import edu.wpi.first.wpilibj.smartdashboard.MechanismRoot2d;
-import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
-import edu.wpi.first.wpilibj.util.Color;
-import edu.wpi.first.wpilibj.util.Color8Bit;
+import edu.wpi.first.wpilibj.examples.armsimulation.subsystems.Arm;
 
 /** This is a sample program to demonstrate the use of arm simulation with existing code. */
 public class Robot extends TimedRobot {
-  private static final int kMotorPort = 0;
-  private static final int kEncoderAChannel = 0;
-  private static final int kEncoderBChannel = 1;
-  private static final int kJoystickPort = 0;
-
-  public static final String kArmPositionKey = "ArmPosition";
-  public static final String kArmPKey = "ArmP";
-
-  // The P gain for the PID controller that drives this arm.
-  private static double kArmKp = 50.0;
-
-  private static double armPositionDeg = 75.0;
-
-  // distance per pulse = (angle per revolution) / (pulses per revolution)
-  //  = (2 * PI rads) / (4096 pulses)
-  private static final double kArmEncoderDistPerPulse = 2.0 * Math.PI / 4096;
-
-  // The arm gearbox represents a gearbox containing two Vex 775pro motors.
-  private final DCMotor m_armGearbox = DCMotor.getVex775Pro(2);
-
-  // Standard classes for controlling our arm
-  private final PIDController m_controller = new PIDController(kArmKp, 0, 0);
-  private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel);
-  private final PWMSparkMax m_motor = new PWMSparkMax(kMotorPort);
-  private final Joystick m_joystick = new Joystick(kJoystickPort);
-
-  // Simulation classes help us simulate what's going on, including gravity.
-  private static final double m_armReduction = 600;
-  private static final double m_armMass = 5.0; // Kilograms
-  private static final double m_armLength = Units.inchesToMeters(30);
-  // This arm sim represents an arm that can travel from -75 degrees (rotated down front)
-  // to 255 degrees (rotated down in the back).
-  private final SingleJointedArmSim m_armSim =
-      new SingleJointedArmSim(
-          m_armGearbox,
-          m_armReduction,
-          SingleJointedArmSim.estimateMOI(m_armLength, m_armMass),
-          m_armLength,
-          Units.degreesToRadians(-75),
-          Units.degreesToRadians(255),
-          m_armMass,
-          true,
-          VecBuilder.fill(kArmEncoderDistPerPulse) // Add noise with a std-dev of 1 tick
-          );
-  private final EncoderSim m_encoderSim = new EncoderSim(m_encoder);
-
-  // Create a Mechanism2d display of an Arm with a fixed ArmTower and moving Arm.
-  private final Mechanism2d m_mech2d = new Mechanism2d(60, 60);
-  private final MechanismRoot2d m_armPivot = m_mech2d.getRoot("ArmPivot", 30, 30);
-  private final MechanismLigament2d m_armTower =
-      m_armPivot.append(new MechanismLigament2d("ArmTower", 30, -90));
-  private final MechanismLigament2d m_arm =
-      m_armPivot.append(
-          new MechanismLigament2d(
-              "Arm",
-              30,
-              Units.radiansToDegrees(m_armSim.getAngleRads()),
-              6,
-              new Color8Bit(Color.kYellow)));
+  private final Arm m_arm = new Arm();
+  private final Joystick m_joystick = new Joystick(Constants.kJoystickPort);
 
   @Override
-  public void robotInit() {
-    m_encoder.setDistancePerPulse(kArmEncoderDistPerPulse);
-
-    // Put Mechanism 2d to SmartDashboard
-    SmartDashboard.putData("Arm Sim", m_mech2d);
-    m_armTower.setColor(new Color8Bit(Color.kBlue));
-
-    // Set the Arm position setpoint and P constant to Preferences if the keys don't already exist
-    if (!Preferences.containsKey(kArmPositionKey)) {
-      Preferences.setDouble(kArmPositionKey, armPositionDeg);
-    }
-    if (!Preferences.containsKey(kArmPKey)) {
-      Preferences.setDouble(kArmPKey, kArmKp);
-    }
-  }
+  public void robotInit() {}
 
   @Override
   public void simulationPeriodic() {
-    // In this method, we update our simulation of what our arm is doing
-    // First, we set our "inputs" (voltages)
-    m_armSim.setInput(m_motor.get() * RobotController.getBatteryVoltage());
-
-    // Next, we update it. The standard loop time is 20ms.
-    m_armSim.update(0.020);
-
-    // Finally, we set our simulated encoder's readings and simulated battery voltage
-    m_encoderSim.setDistance(m_armSim.getAngleRads());
-    // SimBattery estimates loaded battery voltages
-    RoboRioSim.setVInVoltage(
-        BatterySim.calculateDefaultBatteryLoadedVoltage(m_armSim.getCurrentDrawAmps()));
-
-    // Update the Mechanism Arm angle based on the simulated arm angle
-    m_arm.setAngle(Units.radiansToDegrees(m_armSim.getAngleRads()));
+    m_arm.simulationPeriodic();
   }
 
   @Override
   public void teleopInit() {
-    // Read Preferences for Arm setpoint and kP on entering Teleop
-    armPositionDeg = Preferences.getDouble(kArmPositionKey, armPositionDeg);
-    if (kArmKp != Preferences.getDouble(kArmPKey, kArmKp)) {
-      kArmKp = Preferences.getDouble(kArmPKey, kArmKp);
-      m_controller.setP(kArmKp);
-    }
+    m_arm.loadPreferences();
   }
 
   @Override
   public void teleopPeriodic() {
     if (m_joystick.getTrigger()) {
-      // Here, we run PID control like normal, with a constant setpoint of 75 degrees.
-      var pidOutput =
-          m_controller.calculate(m_encoder.getDistance(), Units.degreesToRadians(armPositionDeg));
-      m_motor.setVoltage(pidOutput);
+      // Here, we run PID control like normal.
+      m_arm.reachSetpoint();
     } else {
       // Otherwise, we disable the motor.
-      m_motor.set(0.0);
+      m_arm.stop();
     }
   }
 
   @Override
+  public void close() {
+    m_arm.close();
+    super.close();
+  }
+
+  @Override
   public void disabledInit() {
     // This just makes sure that our simulation code knows that the motor's off.
-    m_motor.set(0.0);
+    m_arm.stop();
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/subsystems/Arm.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/subsystems/Arm.java
new file mode 100644
index 0000000..3cce47d
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/subsystems/Arm.java
@@ -0,0 +1,135 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.armsimulation.subsystems;
+
+import edu.wpi.first.math.VecBuilder;
+import edu.wpi.first.math.controller.PIDController;
+import edu.wpi.first.math.system.plant.DCMotor;
+import edu.wpi.first.math.util.Units;
+import edu.wpi.first.wpilibj.Encoder;
+import edu.wpi.first.wpilibj.Preferences;
+import edu.wpi.first.wpilibj.RobotController;
+import edu.wpi.first.wpilibj.examples.armsimulation.Constants;
+import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
+import edu.wpi.first.wpilibj.simulation.BatterySim;
+import edu.wpi.first.wpilibj.simulation.EncoderSim;
+import edu.wpi.first.wpilibj.simulation.RoboRioSim;
+import edu.wpi.first.wpilibj.simulation.SingleJointedArmSim;
+import edu.wpi.first.wpilibj.smartdashboard.Mechanism2d;
+import edu.wpi.first.wpilibj.smartdashboard.MechanismLigament2d;
+import edu.wpi.first.wpilibj.smartdashboard.MechanismRoot2d;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
+import edu.wpi.first.wpilibj.util.Color;
+import edu.wpi.first.wpilibj.util.Color8Bit;
+
+public class Arm implements AutoCloseable {
+  // The P gain for the PID controller that drives this arm.
+  private double m_armKp = Constants.kDefaultArmKp;
+  private double m_armSetpointDegrees = Constants.kDefaultArmSetpointDegrees;
+
+  // The arm gearbox represents a gearbox containing two Vex 775pro motors.
+  private final DCMotor m_armGearbox = DCMotor.getVex775Pro(2);
+
+  // Standard classes for controlling our arm
+  private final PIDController m_controller = new PIDController(m_armKp, 0, 0);
+  private final Encoder m_encoder =
+      new Encoder(Constants.kEncoderAChannel, Constants.kEncoderBChannel);
+  private final PWMSparkMax m_motor = new PWMSparkMax(Constants.kMotorPort);
+
+  // Simulation classes help us simulate what's going on, including gravity.
+  // This arm sim represents an arm that can travel from -75 degrees (rotated down front)
+  // to 255 degrees (rotated down in the back).
+  private final SingleJointedArmSim m_armSim =
+      new SingleJointedArmSim(
+          m_armGearbox,
+          Constants.kArmReduction,
+          SingleJointedArmSim.estimateMOI(Constants.kArmLength, Constants.kArmMass),
+          Constants.kArmLength,
+          Constants.kMinAngleRads,
+          Constants.kMaxAngleRads,
+          true,
+          0,
+          VecBuilder.fill(Constants.kArmEncoderDistPerPulse) // Add noise with a std-dev of 1 tick
+          );
+  private final EncoderSim m_encoderSim = new EncoderSim(m_encoder);
+
+  // Create a Mechanism2d display of an Arm with a fixed ArmTower and moving Arm.
+  private final Mechanism2d m_mech2d = new Mechanism2d(60, 60);
+  private final MechanismRoot2d m_armPivot = m_mech2d.getRoot("ArmPivot", 30, 30);
+  private final MechanismLigament2d m_armTower =
+      m_armPivot.append(new MechanismLigament2d("ArmTower", 30, -90));
+  private final MechanismLigament2d m_arm =
+      m_armPivot.append(
+          new MechanismLigament2d(
+              "Arm",
+              30,
+              Units.radiansToDegrees(m_armSim.getAngleRads()),
+              6,
+              new Color8Bit(Color.kYellow)));
+
+  /** Subsystem constructor. */
+  public Arm() {
+    m_encoder.setDistancePerPulse(Constants.kArmEncoderDistPerPulse);
+
+    // Put Mechanism 2d to SmartDashboard
+    SmartDashboard.putData("Arm Sim", m_mech2d);
+    m_armTower.setColor(new Color8Bit(Color.kBlue));
+
+    // Set the Arm position setpoint and P constant to Preferences if the keys don't already exist
+    Preferences.initDouble(Constants.kArmPositionKey, m_armSetpointDegrees);
+    Preferences.initDouble(Constants.kArmPKey, m_armKp);
+  }
+
+  /** Update the simulation model. */
+  public void simulationPeriodic() {
+    // In this method, we update our simulation of what our arm is doing
+    // First, we set our "inputs" (voltages)
+    m_armSim.setInput(m_motor.get() * RobotController.getBatteryVoltage());
+
+    // Next, we update it. The standard loop time is 20ms.
+    m_armSim.update(0.020);
+
+    // Finally, we set our simulated encoder's readings and simulated battery voltage
+    m_encoderSim.setDistance(m_armSim.getAngleRads());
+    // SimBattery estimates loaded battery voltages
+    RoboRioSim.setVInVoltage(
+        BatterySim.calculateDefaultBatteryLoadedVoltage(m_armSim.getCurrentDrawAmps()));
+
+    // Update the Mechanism Arm angle based on the simulated arm angle
+    m_arm.setAngle(Units.radiansToDegrees(m_armSim.getAngleRads()));
+  }
+
+  /** Load setpoint and kP from preferences. */
+  public void loadPreferences() {
+    // Read Preferences for Arm setpoint and kP on entering Teleop
+    m_armSetpointDegrees = Preferences.getDouble(Constants.kArmPositionKey, m_armSetpointDegrees);
+    if (m_armKp != Preferences.getDouble(Constants.kArmPKey, m_armKp)) {
+      m_armKp = Preferences.getDouble(Constants.kArmPKey, m_armKp);
+      m_controller.setP(m_armKp);
+    }
+  }
+
+  /** Run the control loop to reach and maintain the setpoint from the preferences. */
+  public void reachSetpoint() {
+    var pidOutput =
+        m_controller.calculate(
+            m_encoder.getDistance(), Units.degreesToRadians(m_armSetpointDegrees));
+    m_motor.setVoltage(pidOutput);
+  }
+
+  public void stop() {
+    m_motor.set(0.0);
+  }
+
+  @Override
+  public void close() {
+    m_motor.close();
+    m_encoder.close();
+    m_mech2d.close();
+    m_armPivot.close();
+    m_controller.close();
+    m_arm.close();
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java
index 1b1808e..ceceaf3 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java
@@ -4,21 +4,40 @@
 
 package edu.wpi.first.wpilibj.examples.differentialdriveposeestimator;
 
+import edu.wpi.first.apriltag.AprilTagFields;
+import edu.wpi.first.math.ComputerVisionUtil;
 import edu.wpi.first.math.VecBuilder;
 import edu.wpi.first.math.controller.PIDController;
 import edu.wpi.first.math.controller.SimpleMotorFeedforward;
 import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator;
 import edu.wpi.first.math.geometry.Pose2d;
+import edu.wpi.first.math.geometry.Pose3d;
+import edu.wpi.first.math.geometry.Quaternion;
+import edu.wpi.first.math.geometry.Rotation3d;
+import edu.wpi.first.math.geometry.Transform3d;
+import edu.wpi.first.math.geometry.Translation3d;
 import edu.wpi.first.math.kinematics.ChassisSpeeds;
 import edu.wpi.first.math.kinematics.DifferentialDriveKinematics;
 import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds;
+import edu.wpi.first.math.numbers.N2;
+import edu.wpi.first.math.system.LinearSystem;
+import edu.wpi.first.math.system.plant.DCMotor;
+import edu.wpi.first.math.system.plant.LinearSystemId;
 import edu.wpi.first.math.util.Units;
+import edu.wpi.first.networktables.DoubleArrayEntry;
+import edu.wpi.first.networktables.DoubleArrayTopic;
 import edu.wpi.first.wpilibj.AnalogGyro;
 import edu.wpi.first.wpilibj.Encoder;
+import edu.wpi.first.wpilibj.RobotController;
 import edu.wpi.first.wpilibj.Timer;
 import edu.wpi.first.wpilibj.motorcontrol.MotorController;
 import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
+import edu.wpi.first.wpilibj.simulation.AnalogGyroSim;
+import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim;
+import edu.wpi.first.wpilibj.simulation.EncoderSim;
+import edu.wpi.first.wpilibj.smartdashboard.Field2d;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
 
 /** Represents a differential drive style drivetrain. */
 public class Drivetrain {
@@ -50,6 +69,18 @@
   private final DifferentialDriveKinematics m_kinematics =
       new DifferentialDriveKinematics(kTrackWidth);
 
+  private final Pose3d m_objectInField;
+
+  private final Transform3d m_robotToCamera =
+      new Transform3d(new Translation3d(1, 1, 1), new Rotation3d(0, 0, Math.PI / 2));
+
+  private final DoubleArrayEntry m_cameraToObjectEntry;
+
+  private final double[] m_defaultVal = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
+
+  private final Field2d m_fieldSim = new Field2d();
+  private final Field2d m_fieldApproximation = new Field2d();
+
   /* Here we use DifferentialDrivePoseEstimator so that we can fuse odometry readings. The
   numbers used  below are robot specific, and should be tuned. */
   private final DifferentialDrivePoseEstimator m_poseEstimator =
@@ -65,11 +96,21 @@
   // Gains are for example purposes only - must be determined for your own robot!
   private final SimpleMotorFeedforward m_feedforward = new SimpleMotorFeedforward(1, 3);
 
+  // Simulation classes
+  private final AnalogGyroSim m_gyroSim = new AnalogGyroSim(m_gyro);
+  private final EncoderSim m_leftEncoderSim = new EncoderSim(m_leftEncoder);
+  private final EncoderSim m_rightEncoderSim = new EncoderSim(m_rightEncoder);
+  private final LinearSystem<N2, N2, N2> m_drivetrainSystem =
+      LinearSystemId.identifyDrivetrainSystem(1.98, 0.2, 1.5, 0.3);
+  private final DifferentialDrivetrainSim m_drivetrainSimulator =
+      new DifferentialDrivetrainSim(
+          m_drivetrainSystem, DCMotor.getCIM(2), 8, kTrackWidth, kWheelRadius, null);
+
   /**
    * Constructs a differential drive object. Sets the encoder distance per pulse and resets the
    * gyro.
    */
-  public Drivetrain() {
+  public Drivetrain(DoubleArrayTopic cameraToObjectTopic) {
     m_gyro.reset();
 
     // We need to invert one side of the drivetrain so that positive voltages
@@ -85,6 +126,13 @@
 
     m_leftEncoder.reset();
     m_rightEncoder.reset();
+
+    m_cameraToObjectEntry = cameraToObjectTopic.getEntry(m_defaultVal);
+
+    m_objectInField = AprilTagFields.k2022RapidReact.loadAprilTagLayoutField().getTagPose(0).get();
+
+    SmartDashboard.putData("Field", m_fieldSim);
+    SmartDashboard.putData("FieldEstimation", m_fieldApproximation);
   }
 
   /**
@@ -115,16 +163,109 @@
     setSpeeds(wheelSpeeds);
   }
 
+  /**
+   * Computes and publishes to a network tables topic the transformation from the camera's pose to
+   * the object's pose. This function exists solely for the purposes of simulation, and this would
+   * normally be handled by computer vision.
+   *
+   * <p>The object could be a target or a fiducial marker.
+   *
+   * @param objectInField The object's field-relative position.
+   * @param robotToCamera The transformation from the robot's pose to the camera's pose.
+   * @param cameraToObjectEntry The networktables entry publishing and querying example computer
+   *     vision measurements.
+   */
+  public void publishCameraToObject(
+      Pose3d objectInField,
+      Transform3d robotToCamera,
+      DoubleArrayEntry cameraToObjectEntry,
+      DifferentialDrivetrainSim drivetrainSimulator) {
+    Pose3d robotInField = new Pose3d(drivetrainSimulator.getPose());
+    Pose3d cameraInField = robotInField.plus(robotToCamera);
+    Transform3d cameraToObject = new Transform3d(cameraInField, objectInField);
+
+    // Publishes double array with Translation3D elements {x, y, z} and Rotation3D elements {w, x,
+    // y, z} which describe
+    // the cameraToObject transformation.
+    double[] val = {
+      cameraToObject.getX(),
+      cameraToObject.getY(),
+      cameraToObject.getZ(),
+      cameraToObject.getRotation().getQuaternion().getW(),
+      cameraToObject.getRotation().getQuaternion().getX(),
+      cameraToObject.getRotation().getQuaternion().getY(),
+      cameraToObject.getRotation().getQuaternion().getZ()
+    };
+    cameraToObjectEntry.set(val);
+  }
+
+  /**
+   * Queries the camera-to-object transformation from networktables to compute the robot's
+   * field-relative pose from vision measurements.
+   *
+   * <p>The object could be a target or a fiducial marker.
+   *
+   * @param objectInField The object's field-relative pose.
+   * @param robotToCamera The transformation from the robot's pose to the camera's pose.
+   * @param cameraToObjectEntry The networktables entry publishing and querying example computer
+   *     vision measurements.
+   */
+  public Pose3d objectToRobotPose(
+      Pose3d objectInField, Transform3d robotToCamera, DoubleArrayEntry cameraToObjectEntry) {
+    double[] val = cameraToObjectEntry.get();
+
+    // Reconstruct cameraToObject Transform3D from networktables.
+    Translation3d translation = new Translation3d(val[0], val[1], val[2]);
+    Rotation3d rotation = new Rotation3d(new Quaternion(val[3], val[4], val[5], val[6]));
+    Transform3d cameraToObject = new Transform3d(translation, rotation);
+
+    return ComputerVisionUtil.objectToRobotPose(objectInField, cameraToObject, robotToCamera);
+  }
+
   /** Updates the field-relative position. */
   public void updateOdometry() {
     m_poseEstimator.update(
         m_gyro.getRotation2d(), m_leftEncoder.getDistance(), m_rightEncoder.getDistance());
 
-    // Also apply vision measurements. We use 0.3 seconds in the past as an example -- on
-    // a real robot, this must be calculated based either on latency or timestamps.
-    m_poseEstimator.addVisionMeasurement(
-        ExampleGlobalMeasurementSensor.getEstimatedGlobalPose(
-            m_poseEstimator.getEstimatedPosition()),
-        Timer.getFPGATimestamp() - 0.3);
+    // Publish cameraToObject transformation to networktables --this would normally be handled by
+    // the
+    // computer vision solution.
+    publishCameraToObject(
+        m_objectInField, m_robotToCamera, m_cameraToObjectEntry, m_drivetrainSimulator);
+
+    // Compute the robot's field-relative position exclusively from vision measurements.
+    Pose3d visionMeasurement3d =
+        objectToRobotPose(m_objectInField, m_robotToCamera, m_cameraToObjectEntry);
+
+    // Convert robot pose from Pose3d to Pose2d needed to apply vision measurements.
+    Pose2d visionMeasurement2d = visionMeasurement3d.toPose2d();
+
+    // Apply vision measurements. For simulation purposes only, we don't input a latency delay -- on
+    // a real robot, this must be calculated based either on known latency or timestamps.
+    m_poseEstimator.addVisionMeasurement(visionMeasurement2d, Timer.getFPGATimestamp());
+  }
+
+  /** This function is called periodically during simulation. */
+  public void simulationPeriodic() {
+    // To update our simulation, we set motor voltage inputs, update the
+    // simulation, and write the simulated positions and velocities to our
+    // simulated encoder and gyro.
+    m_drivetrainSimulator.setInputs(
+        m_leftGroup.get() * RobotController.getInputVoltage(),
+        m_rightGroup.get() * RobotController.getInputVoltage());
+    m_drivetrainSimulator.update(0.02);
+
+    m_leftEncoderSim.setDistance(m_drivetrainSimulator.getLeftPositionMeters());
+    m_leftEncoderSim.setRate(m_drivetrainSimulator.getLeftVelocityMetersPerSecond());
+    m_rightEncoderSim.setDistance(m_drivetrainSimulator.getRightPositionMeters());
+    m_rightEncoderSim.setRate(m_drivetrainSimulator.getRightVelocityMetersPerSecond());
+    m_gyroSim.setAngle(-m_drivetrainSimulator.getHeading().getDegrees());
+  }
+
+  /** This function is called periodically, no matter the mode. */
+  public void periodic() {
+    updateOdometry();
+    m_fieldSim.setRobotPose(m_drivetrainSimulator.getPose());
+    m_fieldApproximation.setRobotPose(m_poseEstimator.getEstimatedPosition());
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Robot.java
index 1011724..56b13bd 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Robot.java
@@ -5,12 +5,18 @@
 package edu.wpi.first.wpilibj.examples.differentialdriveposeestimator;
 
 import edu.wpi.first.math.filter.SlewRateLimiter;
+import edu.wpi.first.networktables.DoubleArrayTopic;
+import edu.wpi.first.networktables.NetworkTableInstance;
 import edu.wpi.first.wpilibj.TimedRobot;
 import edu.wpi.first.wpilibj.XboxController;
 
 public class Robot extends TimedRobot {
+  private final NetworkTableInstance m_inst = NetworkTableInstance.getDefault();
+  private final DoubleArrayTopic m_doubleArrayTopic =
+      m_inst.getDoubleArrayTopic("m_doubleArrayTopic");
+
   private final XboxController m_controller = new XboxController(0);
-  private final Drivetrain m_drive = new Drivetrain();
+  private final Drivetrain m_drive = new Drivetrain(m_doubleArrayTopic);
 
   // Slew rate limiters to make joystick inputs more gentle; 1/3 sec from 0 to 1.
   private final SlewRateLimiter m_speedLimiter = new SlewRateLimiter(3);
@@ -23,6 +29,16 @@
   }
 
   @Override
+  public void simulationPeriodic() {
+    m_drive.simulationPeriodic();
+  }
+
+  @Override
+  public void robotPeriodic() {
+    m_drive.periodic();
+  }
+
+  @Override
   public void teleopPeriodic() {
     // Get the x speed. We are inverting this because Xbox controllers return
     // negative values when we push forward.
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/digitalcommunication/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/digitalcommunication/Robot.java
index 85ff8d6..7efa441 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/digitalcommunication/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/digitalcommunication/Robot.java
@@ -7,6 +7,7 @@
 import edu.wpi.first.wpilibj.DigitalOutput;
 import edu.wpi.first.wpilibj.DriverStation;
 import edu.wpi.first.wpilibj.TimedRobot;
+import java.util.Optional;
 
 /**
  * This is a sample program demonstrating how to communicate to a light controller from the robot
@@ -14,10 +15,10 @@
  */
 public class Robot extends TimedRobot {
   // define ports for digitalcommunication with light controller
-  private static final int kAlliancePort = 0;
-  private static final int kEnabledPort = 1;
-  private static final int kAutonomousPort = 2;
-  private static final int kAlertPort = 3;
+  static final int kAlliancePort = 0;
+  static final int kEnabledPort = 1;
+  static final int kAutonomousPort = 2;
+  static final int kAlertPort = 3;
 
   private final DigitalOutput m_allianceOutput = new DigitalOutput(kAlliancePort);
   private final DigitalOutput m_enabledOutput = new DigitalOutput(kEnabledPort);
@@ -26,8 +27,14 @@
 
   @Override
   public void robotPeriodic() {
+    boolean setAlliance = false;
+    Optional<DriverStation.Alliance> alliance = DriverStation.getAlliance();
+    if (alliance.isPresent()) {
+      setAlliance = alliance.get() == DriverStation.Alliance.Red;
+    }
+
     // pull alliance port high if on red alliance, pull low if on blue alliance
-    m_allianceOutput.set(DriverStation.getAlliance() == DriverStation.Alliance.Red);
+    m_allianceOutput.set(setAlliance);
 
     // pull enabled port high if enabled, low if disabled
     m_enabledOutput.set(DriverStation.isEnabled());
@@ -39,4 +46,14 @@
     var matchTime = DriverStation.getMatchTime();
     m_alertOutput.set(matchTime <= 30 && matchTime >= 25);
   }
+
+  /** Close all resources. */
+  @Override
+  public void close() {
+    m_allianceOutput.close();
+    m_enabledOutput.close();
+    m_autonomousOutput.close();
+    m_alertOutput.close();
+    super.close();
+  }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/RobotContainer.java
index 6bc4b13..ab71ff4 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/RobotContainer.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/RobotContainer.java
@@ -72,11 +72,13 @@
                         // Limit the max acceleration and velocity
                         new TrapezoidProfile.Constraints(
                             DriveConstants.kMaxSpeedMetersPerSecond,
-                            DriveConstants.kMaxAccelerationMetersPerSecondSquared),
-                        // End at desired position in meters; implicitly starts at 0
-                        new TrapezoidProfile.State(3, 0)),
+                            DriveConstants.kMaxAccelerationMetersPerSecondSquared)),
                     // Pipe the profile state to the drive
                     setpointState -> m_robotDrive.setDriveStates(setpointState, setpointState),
+                    // End at desired position in meters; implicitly starts at 0
+                    () -> new TrapezoidProfile.State(3, 0),
+                    // Current position
+                    () -> new TrapezoidProfile.State(),
                     // Require the drive
                     m_robotDrive)
                 .beforeStarting(m_robotDrive::resetEncoders)
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/commands/DriveDistanceProfiled.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/commands/DriveDistanceProfiled.java
index 5bfa10d..5c107d2 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/commands/DriveDistanceProfiled.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/commands/DriveDistanceProfiled.java
@@ -23,11 +23,13 @@
             // Limit the max acceleration and velocity
             new TrapezoidProfile.Constraints(
                 DriveConstants.kMaxSpeedMetersPerSecond,
-                DriveConstants.kMaxAccelerationMetersPerSecondSquared),
-            // End at desired position in meters; implicitly starts at 0
-            new TrapezoidProfile.State(meters, 0)),
+                DriveConstants.kMaxAccelerationMetersPerSecondSquared)),
         // Pipe the profile state to the drive
         setpointState -> drive.setDriveStates(setpointState, setpointState),
+        // End at desired position in meters; implicitly starts at 0
+        () -> new TrapezoidProfile.State(meters, 0),
+        // Current position
+        () -> new TrapezoidProfile.State(),
         // Require the drive
         drive);
     // Reset drive encoders since we're starting at 0
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java
index 124c1fe..520261d 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java
@@ -19,12 +19,14 @@
   private final ExampleSmartMotorController m_leftFollower =
       new ExampleSmartMotorController(DriveConstants.kLeftMotor2Port);
 
+  // The motors on the right side of the drive.
   private final ExampleSmartMotorController m_rightLeader =
       new ExampleSmartMotorController(DriveConstants.kRightMotor1Port);
 
   private final ExampleSmartMotorController m_rightFollower =
       new ExampleSmartMotorController(DriveConstants.kRightMotor2Port);
 
+  // The feedforward controller.
   private final SimpleMotorFeedforward m_feedforward =
       new SimpleMotorFeedforward(
           DriveConstants.ksVolts,
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/ExampleSmartMotorController.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/ExampleSmartMotorController.java
new file mode 100644
index 0000000..e252f85
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/ExampleSmartMotorController.java
@@ -0,0 +1,96 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.elevatorexponentialprofile;
+
+import edu.wpi.first.wpilibj.motorcontrol.MotorController;
+
+/**
+ * A simplified stub class that simulates the API of a common "smart" motor controller.
+ *
+ * <p>Has no actual functionality.
+ */
+public class ExampleSmartMotorController implements MotorController {
+  public enum PIDMode {
+    kPosition,
+    kVelocity,
+    kMovementWitchcraft
+  }
+
+  /**
+   * Creates a new ExampleSmartMotorController.
+   *
+   * @param port The port for the controller.
+   */
+  @SuppressWarnings("PMD.UnusedFormalParameter")
+  public ExampleSmartMotorController(int port) {}
+
+  /**
+   * Example method for setting the PID gains of the smart controller.
+   *
+   * @param kp The proportional gain.
+   * @param ki The integral gain.
+   * @param kd The derivative gain.
+   */
+  public void setPID(double kp, double ki, double kd) {}
+
+  /**
+   * Example method for setting the setpoint of the smart controller in PID mode.
+   *
+   * @param mode The mode of the PID controller.
+   * @param setpoint The controller setpoint.
+   * @param arbFeedforward An arbitrary feedforward output (from -1 to 1).
+   */
+  public void setSetpoint(PIDMode mode, double setpoint, double arbFeedforward) {}
+
+  /**
+   * Places this motor controller in follower mode.
+   *
+   * @param leader The leader to follow.
+   */
+  public void follow(ExampleSmartMotorController leader) {}
+
+  /**
+   * Returns the encoder distance.
+   *
+   * @return The current encoder distance.
+   */
+  public double getEncoderDistance() {
+    return 0;
+  }
+
+  /**
+   * Returns the encoder rate.
+   *
+   * @return The current encoder rate.
+   */
+  public double getEncoderRate() {
+    return 0;
+  }
+
+  /** Resets the encoder to zero distance. */
+  public void resetEncoder() {}
+
+  @Override
+  public void set(double speed) {}
+
+  @Override
+  public double get() {
+    return 0;
+  }
+
+  @Override
+  public void setInverted(boolean isInverted) {}
+
+  @Override
+  public boolean getInverted() {
+    return false;
+  }
+
+  @Override
+  public void disable() {}
+
+  @Override
+  public void stopMotor() {}
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/Main.java
new file mode 100644
index 0000000..7d89388
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/Main.java
@@ -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.
+
+package edu.wpi.first.wpilibj.examples.elevatorexponentialprofile;
+
+import edu.wpi.first.wpilibj.RobotBase;
+
+/**
+ * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
+ * you are doing, do not modify this file except to change the parameter class to the startRobot
+ * call.
+ */
+public final class Main {
+  private Main() {}
+
+  /**
+   * Main initialization function. Do not perform any initialization here.
+   *
+   * <p>If you change your main robot class, change the parameter type.
+   */
+  public static void main(String... args) {
+    RobotBase.startRobot(Robot::new);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/Robot.java
new file mode 100644
index 0000000..3aed7b9
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/Robot.java
@@ -0,0 +1,53 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.elevatorexponentialprofile;
+
+import edu.wpi.first.math.controller.SimpleMotorFeedforward;
+import edu.wpi.first.math.trajectory.ExponentialProfile;
+import edu.wpi.first.wpilibj.Joystick;
+import edu.wpi.first.wpilibj.TimedRobot;
+
+public class Robot extends TimedRobot {
+  private static double kDt = 0.02;
+
+  private final Joystick m_joystick = new Joystick(1);
+  private final ExampleSmartMotorController m_motor = new ExampleSmartMotorController(1);
+  // Note: These gains are fake, and will have to be tuned for your robot.
+  private final SimpleMotorFeedforward m_feedforward = new SimpleMotorFeedforward(1, 1, 1);
+
+  // Create a motion profile with the given maximum voltage and characteristics kV, kA
+  // These gains should match your feedforward kV, kA for best results.
+  private final ExponentialProfile m_profile =
+      new ExponentialProfile(ExponentialProfile.Constraints.fromCharacteristics(10, 1, 1));
+  private ExponentialProfile.State m_goal = new ExponentialProfile.State(0, 0);
+  private ExponentialProfile.State m_setpoint = new ExponentialProfile.State(0, 0);
+
+  @Override
+  public void robotInit() {
+    // Note: These gains are fake, and will have to be tuned for your robot.
+    m_motor.setPID(1.3, 0.0, 0.7);
+  }
+
+  @Override
+  public void teleopPeriodic() {
+    if (m_joystick.getRawButtonPressed(2)) {
+      m_goal = new ExponentialProfile.State(5, 0);
+    } else if (m_joystick.getRawButtonPressed(3)) {
+      m_goal = new ExponentialProfile.State(0, 0);
+    }
+
+    // Retrieve the profiled setpoint for the next timestep. This setpoint moves
+    // toward the goal while obeying the constraints.
+    ExponentialProfile.State next = m_profile.calculate(kDt, m_setpoint, m_goal);
+
+    // Send setpoint to offboard controller PID
+    m_motor.setSetpoint(
+        ExampleSmartMotorController.PIDMode.kPosition,
+        m_setpoint.position,
+        m_feedforward.calculate(m_setpoint.velocity, next.velocity, 0.02) / 12.0);
+
+    m_setpoint = next;
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/Constants.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/Constants.java
new file mode 100644
index 0000000..7a6a937
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/Constants.java
@@ -0,0 +1,39 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.elevatorexponentialsimulation;
+
+import edu.wpi.first.math.util.Units;
+
+public class Constants {
+  public static final int kMotorPort = 0;
+  public static final int kEncoderAChannel = 0;
+  public static final int kEncoderBChannel = 1;
+  public static final int kJoystickPort = 0;
+
+  public static final double kElevatorKp = 0.75;
+  public static final double kElevatorKi = 0;
+  public static final double kElevatorKd = 0;
+
+  public static final double kElevatorMaxV = 10.0; // volts (V)
+  public static final double kElevatorkS = 0.0; // volts (V)
+  public static final double kElevatorkG = 0.62; // volts (V)
+  public static final double kElevatorkV = 3.9; // volts (V)
+  public static final double kElevatorkA = 0.06; // volts (V)
+
+  public static final double kElevatorGearing = 5.0;
+  public static final double kElevatorDrumRadius = Units.inchesToMeters(1.0);
+  public static final double kCarriageMass = Units.lbsToKilograms(12); // kg
+
+  public static final double kSetpointMeters = Units.inchesToMeters(42.875);
+  public static final double kLowerkSetpointMeters = Units.inchesToMeters(15);
+  // Encoder is reset to measure 0 at the bottom, so minimum height is 0.
+  public static final double kMinElevatorHeightMeters = 0.0;
+  public static final double kMaxElevatorHeightMeters = Units.inchesToMeters(50);
+
+  // distance per pulse = (distance per revolution) / (pulses per revolution)
+  //  = (Pi * D) / ppr
+  public static final double kElevatorEncoderDistPerPulse =
+      2.0 * Math.PI * kElevatorDrumRadius / 4096;
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/Main.java
new file mode 100644
index 0000000..cfd4b9f
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/Main.java
@@ -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.
+
+package edu.wpi.first.wpilibj.examples.elevatorexponentialsimulation;
+
+import edu.wpi.first.wpilibj.RobotBase;
+
+/**
+ * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
+ * you are doing, do not modify this file except to change the parameter class to the startRobot
+ * call.
+ */
+public final class Main {
+  private Main() {}
+
+  /**
+   * Main initialization function. Do not perform any initialization here.
+   *
+   * <p>If you change your main robot class, change the parameter type.
+   */
+  public static void main(String... args) {
+    RobotBase.startRobot(Robot::new);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/Robot.java
new file mode 100644
index 0000000..a123414
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/Robot.java
@@ -0,0 +1,62 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.elevatorexponentialsimulation;
+
+import edu.wpi.first.wpilibj.Joystick;
+import edu.wpi.first.wpilibj.TimedRobot;
+import edu.wpi.first.wpilibj.examples.elevatorexponentialsimulation.subsystems.Elevator;
+
+/** This is a sample program to demonstrate the use of elevator simulation. */
+public class Robot extends TimedRobot {
+  private final Joystick m_joystick = new Joystick(Constants.kJoystickPort);
+  private final Elevator m_elevator = new Elevator();
+
+  public Robot() {
+    super(0.020);
+  }
+
+  @Override
+  public void robotInit() {}
+
+  @Override
+  public void robotPeriodic() {
+    // Update the telemetry, including mechanism visualization, regardless of mode.
+    m_elevator.updateTelemetry();
+  }
+
+  @Override
+  public void simulationPeriodic() {
+    // Update the simulation model.
+    m_elevator.simulationPeriodic();
+  }
+
+  @Override
+  public void teleopInit() {
+    m_elevator.reset();
+  }
+
+  @Override
+  public void teleopPeriodic() {
+    if (m_joystick.getTrigger()) {
+      // Here, we set the constant setpoint of 10 meters.
+      m_elevator.reachGoal(Constants.kSetpointMeters);
+    } else {
+      // Otherwise, we update the setpoint to 1 meter.
+      m_elevator.reachGoal(Constants.kLowerkSetpointMeters);
+    }
+  }
+
+  @Override
+  public void disabledInit() {
+    // This just makes sure that our simulation code knows that the motor's off.
+    m_elevator.stop();
+  }
+
+  @Override
+  public void close() {
+    m_elevator.close();
+    super.close();
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/subsystems/Elevator.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/subsystems/Elevator.java
new file mode 100644
index 0000000..6d176e8
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialsimulation/subsystems/Elevator.java
@@ -0,0 +1,142 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.elevatorexponentialsimulation.subsystems;
+
+import edu.wpi.first.math.VecBuilder;
+import edu.wpi.first.math.controller.ElevatorFeedforward;
+import edu.wpi.first.math.controller.PIDController;
+import edu.wpi.first.math.system.plant.DCMotor;
+import edu.wpi.first.math.trajectory.ExponentialProfile;
+import edu.wpi.first.math.util.Units;
+import edu.wpi.first.wpilibj.Encoder;
+import edu.wpi.first.wpilibj.RobotController;
+import edu.wpi.first.wpilibj.examples.elevatorexponentialsimulation.Constants;
+import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
+import edu.wpi.first.wpilibj.simulation.BatterySim;
+import edu.wpi.first.wpilibj.simulation.ElevatorSim;
+import edu.wpi.first.wpilibj.simulation.EncoderSim;
+import edu.wpi.first.wpilibj.simulation.PWMSim;
+import edu.wpi.first.wpilibj.simulation.RoboRioSim;
+import edu.wpi.first.wpilibj.smartdashboard.Mechanism2d;
+import edu.wpi.first.wpilibj.smartdashboard.MechanismLigament2d;
+import edu.wpi.first.wpilibj.smartdashboard.MechanismRoot2d;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
+
+public class Elevator implements AutoCloseable {
+  // This gearbox represents a gearbox containing 4 Vex 775pro motors.
+  private final DCMotor m_elevatorGearbox = DCMotor.getNEO(2);
+
+  private final ExponentialProfile m_profile =
+      new ExponentialProfile(
+          ExponentialProfile.Constraints.fromCharacteristics(
+              Constants.kElevatorMaxV, Constants.kElevatorkV, Constants.kElevatorkA));
+
+  private ExponentialProfile.State m_setpoint = new ExponentialProfile.State(0, 0);
+
+  // Standard classes for controlling our elevator
+  private final PIDController m_pidController =
+      new PIDController(Constants.kElevatorKp, Constants.kElevatorKi, Constants.kElevatorKd);
+
+  ElevatorFeedforward m_feedforward =
+      new ElevatorFeedforward(
+          Constants.kElevatorkS,
+          Constants.kElevatorkG,
+          Constants.kElevatorkV,
+          Constants.kElevatorkA);
+  private final Encoder m_encoder =
+      new Encoder(Constants.kEncoderAChannel, Constants.kEncoderBChannel);
+  private final PWMSparkMax m_motor = new PWMSparkMax(Constants.kMotorPort);
+
+  // Simulation classes help us simulate what's going on, including gravity.
+  private final ElevatorSim m_elevatorSim =
+      new ElevatorSim(
+          m_elevatorGearbox,
+          Constants.kElevatorGearing,
+          Constants.kCarriageMass,
+          Constants.kElevatorDrumRadius,
+          Constants.kMinElevatorHeightMeters,
+          Constants.kMaxElevatorHeightMeters,
+          true,
+          0,
+          VecBuilder.fill(0.005));
+  private final EncoderSim m_encoderSim = new EncoderSim(m_encoder);
+  private final PWMSim m_motorSim = new PWMSim(m_motor);
+
+  // Create a Mechanism2d visualization of the elevator
+  private final Mechanism2d m_mech2d =
+      new Mechanism2d(Units.inchesToMeters(10), Units.inchesToMeters(51));
+  private final MechanismRoot2d m_mech2dRoot =
+      m_mech2d.getRoot("Elevator Root", Units.inchesToMeters(5), Units.inchesToMeters(0.5));
+  private final MechanismLigament2d m_elevatorMech2d =
+      m_mech2dRoot.append(
+          new MechanismLigament2d("Elevator", m_elevatorSim.getPositionMeters(), 90));
+
+  /** Subsystem constructor. */
+  public Elevator() {
+    m_encoder.setDistancePerPulse(Constants.kElevatorEncoderDistPerPulse);
+
+    // Publish Mechanism2d to SmartDashboard
+    // To view the Elevator visualization, select Network Tables -> SmartDashboard -> Elevator Sim
+    SmartDashboard.putData("Elevator Sim", m_mech2d);
+  }
+
+  /** Advance the simulation. */
+  public void simulationPeriodic() {
+    // In this method, we update our simulation of what our elevator is doing
+    // First, we set our "inputs" (voltages)
+    m_elevatorSim.setInput(m_motorSim.getSpeed() * RobotController.getBatteryVoltage());
+
+    // Next, we update it. The standard loop time is 20ms.
+    m_elevatorSim.update(0.020);
+
+    // Finally, we set our simulated encoder's readings and simulated battery voltage
+    m_encoderSim.setDistance(m_elevatorSim.getPositionMeters());
+    // SimBattery estimates loaded battery voltages
+    RoboRioSim.setVInVoltage(
+        BatterySim.calculateDefaultBatteryLoadedVoltage(m_elevatorSim.getCurrentDrawAmps()));
+  }
+
+  /**
+   * Run control loop to reach and maintain goal.
+   *
+   * @param goal the position to maintain
+   */
+  public void reachGoal(double goal) {
+    var goalState = new ExponentialProfile.State(goal, 0);
+
+    var next = m_profile.calculate(0.020, m_setpoint, goalState);
+
+    // With the setpoint value we run PID control like normal
+    double pidOutput = m_pidController.calculate(m_encoder.getDistance(), m_setpoint.position);
+    double feedforwardOutput = m_feedforward.calculate(m_setpoint.velocity, next.velocity, 0.020);
+
+    m_motor.setVoltage(pidOutput + feedforwardOutput);
+
+    m_setpoint = next;
+  }
+
+  /** Stop the control loop and motor output. */
+  public void stop() {
+    m_motor.set(0.0);
+  }
+
+  /** Reset Exponential profile to begin from current position on enable. */
+  public void reset() {
+    m_setpoint = new ExponentialProfile.State(m_encoder.getDistance(), 0);
+  }
+
+  /** Update telemetry, including the mechanism visualization. */
+  public void updateTelemetry() {
+    // Update elevator visualization with position
+    m_elevatorMech2d.setLength(m_encoder.getDistance());
+  }
+
+  @Override
+  public void close() {
+    m_encoder.close();
+    m_motor.close();
+    m_mech2d.close();
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java
index 15d5e23..b6228c9 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj.examples.elevatorprofiledpid;
 
+import edu.wpi.first.math.controller.ElevatorFeedforward;
 import edu.wpi.first.math.controller.ProfiledPIDController;
 import edu.wpi.first.math.trajectory.TrapezoidProfile;
 import edu.wpi.first.wpilibj.Encoder;
@@ -12,8 +13,17 @@
 import edu.wpi.first.wpilibj.motorcontrol.MotorController;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
 
+@SuppressWarnings("PMD.RedundantFieldInitializer")
 public class Robot extends TimedRobot {
   private static double kDt = 0.02;
+  private static double kMaxVelocity = 1.75;
+  private static double kMaxAcceleration = 0.75;
+  private static double kP = 1.3;
+  private static double kI = 0.0;
+  private static double kD = 0.7;
+  private static double kS = 1.1;
+  private static double kG = 1.2;
+  private static double kV = 1.3;
 
   private final Joystick m_joystick = new Joystick(1);
   private final Encoder m_encoder = new Encoder(1, 2);
@@ -22,9 +32,10 @@
   // Create a PID controller whose setpoint's change is subject to maximum
   // velocity and acceleration constraints.
   private final TrapezoidProfile.Constraints m_constraints =
-      new TrapezoidProfile.Constraints(1.75, 0.75);
+      new TrapezoidProfile.Constraints(kMaxVelocity, kMaxAcceleration);
   private final ProfiledPIDController m_controller =
-      new ProfiledPIDController(1.3, 0.0, 0.7, m_constraints, kDt);
+      new ProfiledPIDController(kP, kI, kD, m_constraints, kDt);
+  private final ElevatorFeedforward m_feedforward = new ElevatorFeedforward(kS, kG, kV);
 
   @Override
   public void robotInit() {
@@ -40,6 +51,8 @@
     }
 
     // Run controller and update motor output
-    m_motor.set(m_controller.calculate(m_encoder.getDistance()));
+    m_motor.setVoltage(
+        m_controller.calculate(m_encoder.getDistance())
+            + m_feedforward.calculate(m_controller.getSetpoint().velocity));
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Constants.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Constants.java
new file mode 100644
index 0000000..f5a7567
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Constants.java
@@ -0,0 +1,37 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.elevatorsimulation;
+
+import edu.wpi.first.math.util.Units;
+
+public class Constants {
+  public static final int kMotorPort = 0;
+  public static final int kEncoderAChannel = 0;
+  public static final int kEncoderBChannel = 1;
+  public static final int kJoystickPort = 0;
+
+  public static final double kElevatorKp = 5;
+  public static final double kElevatorKi = 0;
+  public static final double kElevatorKd = 0;
+
+  public static final double kElevatorkS = 0.0; // volts (V)
+  public static final double kElevatorkG = 0.762; // volts (V)
+  public static final double kElevatorkV = 0.762; // volt per velocity (V/(m/s))
+  public static final double kElevatorkA = 0.0; // volt per acceleration (V/(m/s²))
+
+  public static final double kElevatorGearing = 10.0;
+  public static final double kElevatorDrumRadius = Units.inchesToMeters(2.0);
+  public static final double kCarriageMass = 4.0; // kg
+
+  public static final double kSetpointMeters = 0.75;
+  // Encoder is reset to measure 0 at the bottom, so minimum height is 0.
+  public static final double kMinElevatorHeightMeters = 0.0;
+  public static final double kMaxElevatorHeightMeters = 1.25;
+
+  // distance per pulse = (distance per revolution) / (pulses per revolution)
+  //  = (Pi * D) / ppr
+  public static final double kElevatorEncoderDistPerPulse =
+      2.0 * Math.PI * kElevatorDrumRadius / 4096;
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java
index 4bb6654..de39b88 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java
@@ -4,117 +4,50 @@
 
 package edu.wpi.first.wpilibj.examples.elevatorsimulation;
 
-import edu.wpi.first.math.VecBuilder;
-import edu.wpi.first.math.controller.PIDController;
-import edu.wpi.first.math.system.plant.DCMotor;
-import edu.wpi.first.math.util.Units;
-import edu.wpi.first.wpilibj.Encoder;
 import edu.wpi.first.wpilibj.Joystick;
-import edu.wpi.first.wpilibj.RobotController;
 import edu.wpi.first.wpilibj.TimedRobot;
-import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
-import edu.wpi.first.wpilibj.simulation.BatterySim;
-import edu.wpi.first.wpilibj.simulation.ElevatorSim;
-import edu.wpi.first.wpilibj.simulation.EncoderSim;
-import edu.wpi.first.wpilibj.simulation.RoboRioSim;
-import edu.wpi.first.wpilibj.smartdashboard.Mechanism2d;
-import edu.wpi.first.wpilibj.smartdashboard.MechanismLigament2d;
-import edu.wpi.first.wpilibj.smartdashboard.MechanismRoot2d;
-import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
+import edu.wpi.first.wpilibj.examples.elevatorsimulation.subsystems.Elevator;
 
-/** This is a sample program to demonstrate the use of elevator simulation with existing code. */
+/** This is a sample program to demonstrate the use of elevator simulation. */
 public class Robot extends TimedRobot {
-  private static final int kMotorPort = 0;
-  private static final int kEncoderAChannel = 0;
-  private static final int kEncoderBChannel = 1;
-  private static final int kJoystickPort = 0;
-
-  private static final double kElevatorKp = 5.0;
-  private static final double kElevatorGearing = 10.0;
-  private static final double kElevatorDrumRadius = Units.inchesToMeters(2.0);
-  private static final double kCarriageMass = 4.0; // kg
-
-  private static final double kMinElevatorHeight = Units.inchesToMeters(2);
-  private static final double kMaxElevatorHeight = Units.inchesToMeters(50);
-
-  // distance per pulse = (distance per revolution) / (pulses per revolution)
-  //  = (Pi * D) / ppr
-  private static final double kElevatorEncoderDistPerPulse =
-      2.0 * Math.PI * kElevatorDrumRadius / 4096;
-
-  private final DCMotor m_elevatorGearbox = DCMotor.getVex775Pro(4);
-
-  // Standard classes for controlling our elevator
-  private final PIDController m_controller = new PIDController(kElevatorKp, 0, 0);
-  private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel);
-  private final PWMSparkMax m_motor = new PWMSparkMax(kMotorPort);
-  private final Joystick m_joystick = new Joystick(kJoystickPort);
-
-  // Simulation classes help us simulate what's going on, including gravity.
-  private final ElevatorSim m_elevatorSim =
-      new ElevatorSim(
-          m_elevatorGearbox,
-          kElevatorGearing,
-          kCarriageMass,
-          kElevatorDrumRadius,
-          kMinElevatorHeight,
-          kMaxElevatorHeight,
-          true,
-          VecBuilder.fill(0.01));
-  private final EncoderSim m_encoderSim = new EncoderSim(m_encoder);
-
-  // Create a Mechanism2d visualization of the elevator
-  private final Mechanism2d m_mech2d = new Mechanism2d(20, 50);
-  private final MechanismRoot2d m_mech2dRoot = m_mech2d.getRoot("Elevator Root", 10, 0);
-  private final MechanismLigament2d m_elevatorMech2d =
-      m_mech2dRoot.append(
-          new MechanismLigament2d(
-              "Elevator", Units.metersToInches(m_elevatorSim.getPositionMeters()), 90));
+  private final Joystick m_joystick = new Joystick(Constants.kJoystickPort);
+  private final Elevator m_elevator = new Elevator();
 
   @Override
-  public void robotInit() {
-    m_encoder.setDistancePerPulse(kElevatorEncoderDistPerPulse);
+  public void robotInit() {}
 
-    // Publish Mechanism2d to SmartDashboard
-    // To view the Elevator Sim in the simulator, select Network Tables -> SmartDashboard ->
-    // Elevator Sim
-    SmartDashboard.putData("Elevator Sim", m_mech2d);
+  @Override
+  public void robotPeriodic() {
+    // Update the telemetry, including mechanism visualization, regardless of mode.
+    m_elevator.updateTelemetry();
   }
 
   @Override
   public void simulationPeriodic() {
-    // In this method, we update our simulation of what our elevator is doing
-    // First, we set our "inputs" (voltages)
-    m_elevatorSim.setInput(m_motor.get() * RobotController.getBatteryVoltage());
-
-    // Next, we update it. The standard loop time is 20ms.
-    m_elevatorSim.update(0.020);
-
-    // Finally, we set our simulated encoder's readings and simulated battery voltage
-    m_encoderSim.setDistance(m_elevatorSim.getPositionMeters());
-    // SimBattery estimates loaded battery voltages
-    RoboRioSim.setVInVoltage(
-        BatterySim.calculateDefaultBatteryLoadedVoltage(m_elevatorSim.getCurrentDrawAmps()));
-
-    // Update elevator visualization with simulated position
-    m_elevatorMech2d.setLength(Units.metersToInches(m_elevatorSim.getPositionMeters()));
+    // Update the simulation model.
+    m_elevator.simulationPeriodic();
   }
 
   @Override
   public void teleopPeriodic() {
     if (m_joystick.getTrigger()) {
-      // Here, we run PID control like normal, with a constant setpoint of 30in.
-      double pidOutput = m_controller.calculate(m_encoder.getDistance(), Units.inchesToMeters(30));
-      m_motor.setVoltage(pidOutput);
+      // Here, we set the constant setpoint of 0.75 meters.
+      m_elevator.reachGoal(Constants.kSetpointMeters);
     } else {
-      // Otherwise, we disable the motor.
-      m_motor.set(0.0);
+      // Otherwise, we update the setpoint to 0.
+      m_elevator.reachGoal(0.0);
     }
   }
 
   @Override
   public void disabledInit() {
     // This just makes sure that our simulation code knows that the motor's off.
-    m_motor.set(0.0);
+    m_elevator.stop();
+  }
+
+  @Override
+  public void close() {
+    m_elevator.close();
+    super.close();
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/subsystems/Elevator.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/subsystems/Elevator.java
new file mode 100644
index 0000000..1c9d876
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/subsystems/Elevator.java
@@ -0,0 +1,126 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.elevatorsimulation.subsystems;
+
+import edu.wpi.first.math.VecBuilder;
+import edu.wpi.first.math.controller.ElevatorFeedforward;
+import edu.wpi.first.math.controller.ProfiledPIDController;
+import edu.wpi.first.math.system.plant.DCMotor;
+import edu.wpi.first.math.trajectory.TrapezoidProfile;
+import edu.wpi.first.wpilibj.Encoder;
+import edu.wpi.first.wpilibj.RobotController;
+import edu.wpi.first.wpilibj.examples.elevatorsimulation.Constants;
+import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
+import edu.wpi.first.wpilibj.simulation.BatterySim;
+import edu.wpi.first.wpilibj.simulation.ElevatorSim;
+import edu.wpi.first.wpilibj.simulation.EncoderSim;
+import edu.wpi.first.wpilibj.simulation.PWMSim;
+import edu.wpi.first.wpilibj.simulation.RoboRioSim;
+import edu.wpi.first.wpilibj.smartdashboard.Mechanism2d;
+import edu.wpi.first.wpilibj.smartdashboard.MechanismLigament2d;
+import edu.wpi.first.wpilibj.smartdashboard.MechanismRoot2d;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
+
+public class Elevator implements AutoCloseable {
+  // This gearbox represents a gearbox containing 4 Vex 775pro motors.
+  private final DCMotor m_elevatorGearbox = DCMotor.getVex775Pro(4);
+
+  // Standard classes for controlling our elevator
+  private final ProfiledPIDController m_controller =
+      new ProfiledPIDController(
+          Constants.kElevatorKp,
+          Constants.kElevatorKi,
+          Constants.kElevatorKd,
+          new TrapezoidProfile.Constraints(2.45, 2.45));
+  ElevatorFeedforward m_feedforward =
+      new ElevatorFeedforward(
+          Constants.kElevatorkS,
+          Constants.kElevatorkG,
+          Constants.kElevatorkV,
+          Constants.kElevatorkA);
+  private final Encoder m_encoder =
+      new Encoder(Constants.kEncoderAChannel, Constants.kEncoderBChannel);
+  private final PWMSparkMax m_motor = new PWMSparkMax(Constants.kMotorPort);
+
+  // Simulation classes help us simulate what's going on, including gravity.
+  private final ElevatorSim m_elevatorSim =
+      new ElevatorSim(
+          m_elevatorGearbox,
+          Constants.kElevatorGearing,
+          Constants.kCarriageMass,
+          Constants.kElevatorDrumRadius,
+          Constants.kMinElevatorHeightMeters,
+          Constants.kMaxElevatorHeightMeters,
+          true,
+          0,
+          VecBuilder.fill(0.01));
+  private final EncoderSim m_encoderSim = new EncoderSim(m_encoder);
+  private final PWMSim m_motorSim = new PWMSim(m_motor);
+
+  // Create a Mechanism2d visualization of the elevator
+  private final Mechanism2d m_mech2d = new Mechanism2d(20, 50);
+  private final MechanismRoot2d m_mech2dRoot = m_mech2d.getRoot("Elevator Root", 10, 0);
+  private final MechanismLigament2d m_elevatorMech2d =
+      m_mech2dRoot.append(
+          new MechanismLigament2d("Elevator", m_elevatorSim.getPositionMeters(), 90));
+
+  /** Subsystem constructor. */
+  public Elevator() {
+    m_encoder.setDistancePerPulse(Constants.kElevatorEncoderDistPerPulse);
+
+    // Publish Mechanism2d to SmartDashboard
+    // To view the Elevator visualization, select Network Tables -> SmartDashboard -> Elevator Sim
+    SmartDashboard.putData("Elevator Sim", m_mech2d);
+  }
+
+  /** Advance the simulation. */
+  public void simulationPeriodic() {
+    // In this method, we update our simulation of what our elevator is doing
+    // First, we set our "inputs" (voltages)
+    m_elevatorSim.setInput(m_motorSim.getSpeed() * RobotController.getBatteryVoltage());
+
+    // Next, we update it. The standard loop time is 20ms.
+    m_elevatorSim.update(0.020);
+
+    // Finally, we set our simulated encoder's readings and simulated battery voltage
+    m_encoderSim.setDistance(m_elevatorSim.getPositionMeters());
+    // SimBattery estimates loaded battery voltages
+    RoboRioSim.setVInVoltage(
+        BatterySim.calculateDefaultBatteryLoadedVoltage(m_elevatorSim.getCurrentDrawAmps()));
+  }
+
+  /**
+   * Run control loop to reach and maintain goal.
+   *
+   * @param goal the position to maintain
+   */
+  public void reachGoal(double goal) {
+    m_controller.setGoal(goal);
+
+    // With the setpoint value we run PID control like normal
+    double pidOutput = m_controller.calculate(m_encoder.getDistance());
+    double feedforwardOutput = m_feedforward.calculate(m_controller.getSetpoint().velocity);
+    m_motor.setVoltage(pidOutput + feedforwardOutput);
+  }
+
+  /** Stop the control loop and motor output. */
+  public void stop() {
+    m_controller.setGoal(0.0);
+    m_motor.set(0.0);
+  }
+
+  /** Update telemetry, including the mechanism visualization. */
+  public void updateTelemetry() {
+    // Update elevator visualization with position
+    m_elevatorMech2d.setLength(m_encoder.getDistance());
+  }
+
+  @Override
+  public void close() {
+    m_encoder.close();
+    m_motor.close();
+    m_mech2d.close();
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/Robot.java
index d459eeb..2acb5bc 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/Robot.java
@@ -17,8 +17,10 @@
   // Note: These gains are fake, and will have to be tuned for your robot.
   private final SimpleMotorFeedforward m_feedforward = new SimpleMotorFeedforward(1, 1.5);
 
-  private final TrapezoidProfile.Constraints m_constraints =
-      new TrapezoidProfile.Constraints(1.75, 0.75);
+  // Create a motion profile with the given maximum velocity and maximum
+  // acceleration constraints for the next setpoint.
+  private final TrapezoidProfile m_profile =
+      new TrapezoidProfile(new TrapezoidProfile.Constraints(1.75, 0.75));
   private TrapezoidProfile.State m_goal = new TrapezoidProfile.State();
   private TrapezoidProfile.State m_setpoint = new TrapezoidProfile.State();
 
@@ -33,17 +35,12 @@
     if (m_joystick.getRawButtonPressed(2)) {
       m_goal = new TrapezoidProfile.State(5, 0);
     } else if (m_joystick.getRawButtonPressed(3)) {
-      m_goal = new TrapezoidProfile.State(0, 0);
+      m_goal = new TrapezoidProfile.State();
     }
 
-    // Create a motion profile with the given maximum velocity and maximum
-    // acceleration constraints for the next setpoint, the desired goal, and the
-    // current setpoint.
-    var profile = new TrapezoidProfile(m_constraints, m_goal, m_setpoint);
-
     // Retrieve the profiled setpoint for the next timestep. This setpoint moves
     // toward the goal while obeying the constraints.
-    m_setpoint = profile.calculate(kDt);
+    m_setpoint = m_profile.calculate(kDt, m_goal, m_setpoint);
 
     // Send setpoint to offboard controller PID
     m_motor.setSetpoint(
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json
index 01f79e7..750e4dc 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json
@@ -1,9 +1,9 @@
 [
   {
     "name": "Getting Started",
-    "description": "An example program which demonstrates the simplest autonomous and teleoperated routines.",
+    "description": "A differential-drive robot with split-stick Xbox arcade drive with a simple time-based autonomous.",
     "tags": [
-      "Getting Started with Java"
+      "Basic Robot"
     ],
     "foldername": "gettingstarted",
     "gradlebase": "java",
@@ -12,12 +12,11 @@
   },
   {
     "name": "Tank Drive",
-    "description": "Demonstrate the use of the DifferentialDrive class doing teleop driving with tank steering",
+    "description": "Control a differential drive with twin-joystick tank drive in teleop.",
     "tags": [
-      "Actuators",
-      "Joystick",
-      "Robot and Motor",
-      "Safety"
+      "Basic Robot",
+      "Differential Drive",
+      "Joystick"
     ],
     "foldername": "tankdrive",
     "gradlebase": "java",
@@ -26,9 +25,11 @@
   },
   {
     "name": "Arcade Drive",
-    "description": "Demonstrates the use of the DifferentialDrive class to drive a robot with Arcade Drive.",
+    "description": "Control a differential drivetrain with single-joystick arcade drive in teleop.",
     "tags": [
-      "Getting Started with Java"
+      "Basic Robot",
+      "Differential Drive",
+      "Joystick"
     ],
     "foldername": "arcadedrive",
     "gradlebase": "java",
@@ -37,12 +38,11 @@
   },
   {
     "name": "Mecanum Drive",
-    "description": "Demonstrate the use of the MecanumDrive class doing teleop driving with Mecanum steering",
+    "description": "Control a mecanum drivetrain with a joystick in teleop.",
     "tags": [
-      "Actuators",
-      "Joystick",
-      "Robot and Motor",
-      "Safety"
+      "Basic Robot",
+      "Mecanum Drive",
+      "Joystick"
     ],
     "foldername": "mecanumdrive",
     "gradlebase": "java",
@@ -51,11 +51,11 @@
   },
   {
     "name": "PDP CAN Monitoring",
-    "description": "Demonstrate using CAN to monitor the voltage, current, and temperature in the Power Distribution Panel.",
+    "description": "Monitor Power Distribution data such as voltage, current, temperature, etc.",
     "tags": [
-      "Complete List",
-      "CAN",
-      "Sensors"
+      "Hardware",
+      "PDP",
+      "SmartDashboard"
     ],
     "foldername": "canpdp",
     "gradlebase": "java",
@@ -64,12 +64,12 @@
   },
   {
     "name": "Solenoids",
-    "description": "Demonstrate controlling a single and double solenoid from Joystick buttons.",
+    "description": "Control a single and double solenoid from joystick buttons.",
     "tags": [
-      "Actuators",
+      "Hardware",
       "Joystick",
-      "Pneumatics",
-      "Complete List"
+      "Shuffleboard",
+      "Pneumatics"
     ],
     "foldername": "solenoid",
     "gradlebase": "java",
@@ -78,11 +78,11 @@
   },
   {
     "name": "Encoder",
-    "description": "Demonstrate displaying the value of a quadrature encoder on the SmartDashboard.",
+    "description": "View values from a quadrature encoder.",
     "tags": [
-      "Complete List",
-      "Digital",
-      "Sensors"
+      "Hardware",
+      "Encoder",
+      "SmartDashboard"
     ],
     "foldername": "encoder",
     "gradlebase": "java",
@@ -91,8 +91,10 @@
   },
   {
     "name": "EventLoop",
-    "description": "Demonstrate managing a ball system using EventLoop and BooleanEvent.",
+    "description": "Manage a ball system using EventLoop and BooleanEvent.",
     "tags": [
+      "Basic Robot",
+      "Flywheel",
       "EventLoop"
     ],
     "foldername": "eventloop",
@@ -102,11 +104,11 @@
   },
   {
     "name": "Relay",
-    "description": "Demonstrate controlling a Relay from Joystick buttons.",
+    "description": "Control a relay from joystick buttons.",
     "tags": [
-      "Actuators",
-      "Joystick",
-      "Complete List"
+      "Hardware",
+      "Relay",
+      "Joystick"
     ],
     "foldername": "relay",
     "gradlebase": "java",
@@ -115,11 +117,12 @@
   },
   {
     "name": "Ultrasonic",
-    "description": "Demonstrate maintaining a set distance using an ultrasonic sensor.",
+    "description": "View values from a ping-response ultrasonic sensor.",
     "tags": [
-      "Sensors",
-      "Robot and Motor",
-      "Analog"
+      "Hardware",
+      "Ultrasonic",
+      "SmartDashboard",
+      "Shuffleboard"
     ],
     "foldername": "ultrasonic",
     "gradlebase": "java",
@@ -128,11 +131,12 @@
   },
   {
     "name": "Ultrasonic PID",
-    "description": "Demonstrate maintaining a set distance using an ultrasonic sensor and PID Control.",
+    "description": "Maintain a set distance from an obstacle with an ultrasonic sensor and PID control.",
     "tags": [
-      "Sensors",
-      "Robot and Motor",
-      "Analog"
+      "Basic Robot",
+      "Ultrasonic",
+      "PID",
+      "Differential Drive"
     ],
     "foldername": "ultrasonicpid",
     "gradlebase": "java",
@@ -141,11 +145,12 @@
   },
   {
     "name": "Potentiometer PID",
-    "description": "An example to demonstrate the use of a potentiometer and PID control to reach elevator position setpoints.",
+    "description": "Maintain elevator position setpoints with a potentiometer and PID control.",
     "tags": [
-      "Sensors",
-      "Actuators",
+      "Basic Robot",
       "Analog",
+      "Elevator",
+      "PID",
       "Joystick"
     ],
     "foldername": "potentiometerpid",
@@ -155,11 +160,12 @@
   },
   {
     "name": "Elevator with trapezoid profiled PID",
-    "description": "An example to demonstrate the use of an encoder and trapezoid profiled PID control to reach elevator position setpoints.",
+    "description": "Reach elevator position setpoints with trapezoid profiles and smart motor controller PID.",
     "tags": [
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "Basic Robot",
+      "Elevator",
+      "Trapezoid Profile",
+      "Smart Motor Controller",
       "Joystick"
     ],
     "foldername": "elevatortrapezoidprofile",
@@ -168,12 +174,27 @@
     "commandversion": 2
   },
   {
-    "name": "Elevator with profiled PID controller",
-    "description": "An example to demonstrate the use of an encoder and trapezoid profiled PID control to reach elevator position setpoints.",
+    "name": "Elevator with exponential profile",
+    "description": "Reach elevator position setpoints with exponential profiles and smart motor controller PID.",
     "tags": [
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "Basic Robot",
+      "Elevator",
+      "Exponential Profile",
+      "Smart Motor Controller",
+      "Joystick"
+    ],
+    "foldername": "elevatorexponentialprofile",
+    "gradlebase": "java",
+    "mainclass": "Main",
+    "commandversion": 2
+  },
+  {
+    "name": "Elevator with profiled PID controller",
+    "description": "Reach elevator position setpoints with an encoder and profiled PID control.",
+    "tags": [
+      "Basic Robot",
+      "Elevator",
+      "Profiled PID",
       "Joystick"
     ],
     "foldername": "elevatorprofiledpid",
@@ -183,10 +204,12 @@
   },
   {
     "name": "Gyro",
-    "description": "An example program showing how to drive straight with using a gyro sensor.",
+    "description": "Drive a differential drive straight with a gyro sensor.",
     "tags": [
-      "Sensors",
-      "Robot and Motor",
+      "Basic Robot",
+      "Differential Drive",
+      "PID",
+      "Gyro",
       "Analog",
       "Joystick"
     ],
@@ -197,10 +220,11 @@
   },
   {
     "name": "Gyro Mecanum",
-    "description": "An example program showing how to perform mecanum drive with field oriented controls.",
+    "description": "Drive a mecanum drivetrain using field-oriented controls with a joystick.",
     "tags": [
-      "Sensors",
-      "Robot and Motor",
+      "Basic Robot",
+      "Mecanum Drive",
+      "Gyro",
       "Analog",
       "Joystick"
     ],
@@ -211,18 +235,28 @@
   },
   {
     "name": "HID Rumble",
-    "description": "An example program showing how to make human interface devices rumble.",
+    "description": "Make human interface devices (HID) rumble.",
     "tags": [
-      "Joystick"
+      "Hardware",
+      "XboxController"
     ],
     "foldername": "hidrumble",
     "gradlebase": "java",
     "mainclass": "Main",
     "commandversion": 2
   },
-  {"name": "Mechanism2d",
-    "description": "An example usage of Mechanism2d to display mechanism states on a dashboard.",
-    "tags": ["Mechanism2d"],
+  {
+    "name": "Mechanism2d",
+    "description": "Display mechanism states on a dashboard with Mechanism2d.",
+    "tags": [
+      "Basic Robot",
+      "Elevator",
+      "Arm",
+      "Analog",
+      "Joystick",
+      "SmartDashboard",
+      "Mechanism2d"
+    ],
     "foldername": "mechanism2d",
     "gradlebase": "java",
     "mainclass": "Main",
@@ -230,14 +264,12 @@
   },
   {
     "name": "Motor Control",
-    "description": "Demonstrate controlling a single motor with a Joystick and displaying the net movement of the motor using an encoder.",
+    "description": "Control a single motor with a joystick, displaying the movement of the motor using an encoder.",
     "tags": [
-      "Robot and Motor",
-      "Digital",
-      "Sensors",
-      "Actuators",
-      "Joystick",
-      "Complete List"
+      "Basic Robot",
+      "Encoder",
+      "SmartDashboard",
+      "Joystick"
     ],
     "foldername": "motorcontrol",
     "gradlebase": "java",
@@ -246,9 +278,17 @@
   },
   {
     "name": "GearsBot",
-    "description": "A fully functional example CommandBased program for WPIs GearsBot robot, ported to the new CommandBased library. This code can run on your computer if it supports simulation.",
+    "description": "A fully functional Command-Based program for WPI's GearsBot robot.",
     "tags": [
-      "Complete Robot"
+      "Complete Robot",
+      "Command-based",
+      "Differential Drive",
+      "Elevator",
+      "Arm",
+      "Analog",
+      "Digital Input",
+      "SmartDashboard",
+      "XboxController"
     ],
     "foldername": "gearsbot",
     "gradlebase": "java",
@@ -257,10 +297,9 @@
   },
   {
     "name": "Simple Vision",
-    "description": "Demonstrate the use of the CameraServer class to stream from a USB Webcam without processing the images.",
+    "description": "Use the CameraServer class to stream from a USB Webcam without processing the images.",
     "tags": [
-      "Vision",
-      "Complete List"
+      "Vision"
     ],
     "foldername": "quickvision",
     "gradlebase": "java",
@@ -269,10 +308,9 @@
   },
   {
     "name": "Intermediate Vision",
-    "description": "An example program that acquires images from an attached USB camera and adds some annotation to the image as you might do for showing operators the result of some image recognition, and sends it to the dashboard for display.",
+    "description": "Acquire images from an attached USB camera and add some annotation to the image (as you might do for showing operators the result of some image recognition) and send it to the dashboard for display.",
     "tags": [
-      "Vision",
-      "Complete List"
+      "Vision"
     ],
     "foldername": "intermediatevision",
     "gradlebase": "java",
@@ -281,7 +319,7 @@
   },
   {
     "name": "Axis Camera Sample",
-    "description": "An example program that acquires images from an Axis network camera and adds some annotation to the image as you might do for showing operators the result of some image recognition, and sends it to the dashboard for display. This demonstrates the use of the AxisCamera class.",
+    "description": "Acquire images from an Axis network camera and adds some annotation to the image (as you might do for showing operators the result of some image recognition), and sends it to the dashboard for display.",
     "tags": [
       "Vision"
     ],
@@ -291,11 +329,27 @@
     "commandversion": 2
   },
   {
-    "name": "Shuffleboard Sample",
-    "description": "An example program that adds data to various Shuffleboard tabs that demonstrates the Shuffleboard API",
+    "name": "AprilTags Vision",
+    "description": "On-roboRIO detection of AprilTags using an attached USB camera.",
     "tags": [
-      "Shuffleboard",
-      "Dashboards"
+      "Vision",
+      "AprilTags"
+    ],
+    "foldername": "apriltagsvision",
+    "gradlebase": "java",
+    "mainclass": "Main",
+    "commandversion": 2
+  },
+  {
+    "name": "Shuffleboard",
+    "description": "Present various data via the Shuffleboard API.",
+    "tags": [
+      "Basic Robot",
+      "Differential Drive",
+      "Elevator",
+      "Analog",
+      "Encoder",
+      "Shuffleboard"
     ],
     "foldername": "shuffleboard",
     "gradlebase": "java",
@@ -304,10 +358,17 @@
   },
   {
     "name": "'Traditional' Hatchbot",
-    "description": "A fully-functional command-based hatchbot for the 2019 game using the new command framework.  Written in the 'traditional' style, i.e. commands are given their own classes.",
+    "description": "A fully-functional command-based hatchbot for the 2019 game, written in the 'traditional' style, i.e. commands are given their own classes.",
     "tags": [
-      "Complete robot",
-      "Command-based"
+      "Complete Robot",
+      "Command-based",
+      "Differential Drive",
+      "Encoder",
+      "Pneumatics",
+      "Shuffleboard",
+      "Sendable",
+      "DataLog",
+      "XboxController"
     ],
     "foldername": "hatchbottraditional",
     "gradlebase": "java",
@@ -316,11 +377,17 @@
   },
   {
     "name": "'Inlined' Hatchbot",
-    "description": "A fully-functional command-based hatchbot for the 2019 game using the new command framework.  Written in the 'inlined' style, i.e. many commands are defined inline with lambdas.",
+    "description": "A fully-functional command-based hatchbot for the 2019 game, written in the 'inlined' style, i.e. many commands are defined inline with lambdas.",
     "tags": [
-      "Complete robot",
+      "Complete Robot",
       "Command-based",
-      "Lambdas"
+      "Differential Drive",
+      "Encoder",
+      "Pneumatics",
+      "Shuffleboard",
+      "Sendable",
+      "DataLog",
+      "PS4Controller"
     ],
     "foldername": "hatchbotinlined",
     "gradlebase": "java",
@@ -329,11 +396,18 @@
   },
   {
     "name": "Rapid React Command Bot",
-    "description": "A fully-functional command-based fender bot for the 2022 game using the new command framework.",
+    "description": "A fully-functional command-based fender bot for the 2022 game, written in the 'inlined' style, i.e. many commands are defined inline with lambdas.",
     "tags": [
-      "Complete robot",
+      "Complete Robot",
       "Command-based",
-      "Lambdas"
+      "Differential Drive",
+      "Intake",
+      "Flywheel",
+      "Encoder",
+      "Pneumatics",
+      "Digital Input",
+      "PID",
+      "XboxController"
     ],
     "foldername": "rapidreactcommandbot",
     "gradlebase": "java",
@@ -342,7 +416,7 @@
   },
   {
     "name": "Select Command Example",
-    "description": "An example showing how to use the SelectCommand class from the new command framework.",
+    "description": "Use SelectCommand to select an autonomous routine.",
     "tags": [
       "Command-based"
     ],
@@ -352,22 +426,15 @@
     "commandversion": 2
   },
   {
-    "name": "Scheduler Event Logging",
-    "description": "An example showing how to use Shuffleboard to log Command events from the CommandScheduler in the new command framework",
-    "tags": [
-      "Command-based",
-      "Shuffleboard"
-    ],
-    "foldername": "schedulereventlogging",
-    "gradlebase": "java",
-    "mainclass": "Main",
-    "commandversion": 2
-  },
-  {
     "name": "Frisbeebot",
-    "description": "An example robot project for a simple frisbee shooter for the 2013 FRC game, Ultimate Ascent, demonstrating use of PID functionality in the command framework",
+    "description": "A simple frisbee shooter for the 2013 game, demonstrating use of PIDSubsystem.",
     "tags": [
+      "Complete Robot",
       "Command-based",
+      "Differential Drive",
+      "Flywheel",
+      "Encoder",
+      "XboxController",
       "PID"
     ],
     "foldername": "frisbeebot",
@@ -377,10 +444,14 @@
   },
   {
     "name": "Gyro Drive Commands",
-    "description": "An example command-based robot project demonstrating simple PID functionality utilizing a gyroscope to keep a robot driving straight and to turn to specified angles.",
+    "description": "Control a robot's angle with PID and a gyro, in command-based.",
     "tags": [
       "Command-based",
+      "Differential Drive",
+      "Encoder",
+      "PS4Controller",
       "PID",
+      "Profiled PID",
       "Gyro"
     ],
     "foldername": "gyrodrivecommands",
@@ -390,9 +461,13 @@
   },
   {
     "name": "SwerveBot",
-    "description": "An example program for a swerve drive that uses swerve drive kinematics and odometry.",
+    "description": "Use kinematics and odometry with a swerve drive.",
     "tags": [
-      "SwerveBot"
+      "Swerve Drive",
+      "Odometry",
+      "XboxController",
+      "Gyro",
+      "Encoder"
     ],
     "foldername": "swervebot",
     "gradlebase": "java",
@@ -401,9 +476,13 @@
   },
   {
     "name": "MecanumBot",
-    "description": "An example program for a mecanum drive that uses mecanum drive kinematics and odometry.",
+    "description": "Use kinematics and odometry with a mecanum drive.",
     "tags": [
-      "MecanumBot"
+      "Mecanum Drive",
+      "Odometry",
+      "Encoder",
+      "Gyro",
+      "XboxController"
     ],
     "foldername": "mecanumbot",
     "gradlebase": "java",
@@ -412,9 +491,13 @@
   },
   {
     "name": "DifferentialDriveBot",
-    "description": "An example program for a differential drive that uses differential drive kinematics and odometry.",
+    "description": "Use kinematics and odometry with a differential drive.",
     "tags": [
-      "MecanumBot"
+      "Differential Drive",
+      "Odometry",
+      "Encoder",
+      "Gyro",
+      "XboxController"
     ],
     "foldername": "differentialdrivebot",
     "gradlebase": "java",
@@ -423,13 +506,17 @@
   },
   {
     "name": "RamseteCommand",
-    "description": "An example command-based robot demonstrating the use of a RamseteCommand to follow a pregenerated trajectory.",
+    "description": "Follow a pre-generated trajectory with a differential drive using RamseteCommand.",
     "tags": [
-      "RamseteCommand",
-      "PID",
+      "Differential Drive",
+      "Command-based",
       "Ramsete",
       "Trajectory",
-      "Path following"
+      "Path Following",
+      "Odometry",
+      "Encoder",
+      "Gyro",
+      "XboxController"
     ],
     "foldername": "ramsetecommand",
     "gradlebase": "java",
@@ -438,9 +525,11 @@
   },
   {
     "name": "Arcade Drive Xbox Controller",
-    "description": "Demonstrates the use of the DifferentialDrive class to drive a robot with Arcade Drive and an Xbox Controller",
+    "description": "Control a differential drive with split-stick arcade drive in teleop.",
     "tags": [
-      "Getting Started with Java"
+      "Basic Robot",
+      "Differential Drive",
+      "XboxController"
     ],
     "foldername": "arcadedrivexboxcontroller",
     "gradlebase": "java",
@@ -449,9 +538,11 @@
   },
   {
     "name": "Tank Drive Xbox Controller",
-    "description": "Demonstrates the use of the DifferentialDrive class to drive a robot with Tank Drive and an Xbox Controller",
+    "description": "Control a differential drive with Xbox tank drive in teleop.",
     "tags": [
-      "Getting Started with Java"
+      "Basic Robot",
+      "Differential Drive",
+      "XboxController"
     ],
     "foldername": "tankdrivexboxcontroller",
     "gradlebase": "java",
@@ -460,9 +551,12 @@
   },
   {
     "name": "Duty Cycle Encoder",
-    "description": "Demonstrates the use of the Duty Cycle Encoder class",
+    "description": "View values from a duty-cycle encoder.",
     "tags": [
-      "Getting Started with Java"
+      "Hardware",
+      "Duty Cycle",
+      "Encoder",
+      "SmartDashboard"
     ],
     "foldername": "dutycycleencoder",
     "gradlebase": "java",
@@ -471,9 +565,11 @@
   },
   {
     "name": "Duty Cycle Input",
-    "description": "Demonstrates the use of the Duty Cycle class",
+    "description": "View duty-cycle input.",
     "tags": [
-      "Getting Started with Java"
+      "Hardware",
+      "Duty Cycle",
+      "SmartDashboard"
     ],
     "foldername": "dutycycleinput",
     "gradlebase": "java",
@@ -482,9 +578,11 @@
   },
   {
     "name": "Addressable LED",
-    "description": "Demonstrates the use of the Addressable LED class",
+    "description": "Display a rainbow pattern on an addressable LED strip.",
     "tags": [
-      "Getting Started with Java"
+      "Hardware",
+      "Basic Robot",
+      "AddressableLEDs"
     ],
     "foldername": "addressableled",
     "gradlebase": "java",
@@ -493,9 +591,11 @@
   },
   {
     "name": "DMA",
-    "description": "Demonstrates the use of the DMA class",
+    "description": "Read various sensors using DMA.",
     "tags": [
-      "Advanced Java"
+      "Hardware",
+      "DMA",
+      "SmartDashboard"
     ],
     "foldername": "dma",
     "gradlebase": "java",
@@ -504,11 +604,14 @@
   },
   {
     "name": "ArmBot",
-    "description": "An example command-based robot demonstrating the use of a ProfiledPIDSubsystem to control an arm.",
+    "description": "Control an arm with ProfiledPIDSubsystem.",
     "tags": [
-      "ArmBot",
-      "PID",
-      "Motion Profile"
+      "Command-based",
+      "Arm",
+      "Encoder",
+      "Profiled PID",
+      "XboxController",
+      "Differential Drive"
     ],
     "foldername": "armbot",
     "gradlebase": "java",
@@ -517,11 +620,14 @@
   },
   {
     "name": "ArmBotOffboard",
-    "description": "An example command-based robot demonstrating the use of a TrapezoidProfileSubsystem to control an arm with an offboard PID.",
+    "description": "Control an arm with TrapezoidProfileSubsystem and smart motor controller PID.",
     "tags": [
-      "ArmBotOffboard",
-      "PID",
-      "Motion Profile"
+      "Command-based",
+      "Arm",
+      "Smart Motor Controller",
+      "Trapezoid Profile",
+      "XboxController",
+      "Differential Drive"
     ],
     "foldername": "armbotoffboard",
     "gradlebase": "java",
@@ -530,11 +636,13 @@
   },
   {
     "name": "DriveDistanceOffboard",
-    "description": "An example command-based robot demonstrating the use of a TrapezoidProfileCommand to drive a robot a set distance with offboard PID on the drive.",
+    "description": "Drive a differential drivetrain a set distance using TrapezoidProfileCommand and smart motor controller PID.",
     "tags": [
-      "DriveDistance",
-      "PID",
-      "Motion Profile"
+      "Command-based",
+      "Differential Drive",
+      "Trapezoid Profile",
+      "Smart Motor Controller",
+      "XboxController"
     ],
     "foldername": "drivedistanceoffboard",
     "gradlebase": "java",
@@ -543,13 +651,16 @@
   },
   {
     "name": "MecanumControllerCommand",
-    "description": "An example command-based robot demonstrating the use of a MecanumControllerCommand to follow a pregenerated trajectory.",
+    "description": "Follow a pre-generated trajectory with a mecanum drive using MecanumControllerCommand.",
     "tags": [
-      "MecanumControllerCommand",
-      "Mecanum",
-      "PID",
+      "Command-based",
+      "Mecanum Drive",
+      "Gyro",
+      "Encoder",
+      "Odometry",
       "Trajectory",
-      "Path following"
+      "Path Following",
+      "XboxController"
     ],
     "foldername": "mecanumcontrollercommand",
     "gradlebase": "java",
@@ -558,13 +669,16 @@
   },
   {
     "name": "SwerveControllerCommand",
-    "description": "An example command-based robot demonstrating the use of a SwerveControllerCommand to follow a pregenerated trajectory.",
+    "description": "Follow a pre-generated trajectory with a swerve drive using SwerveControllerCommand.",
     "tags": [
-      "SwerveControllerCommand",
-      "Swerve",
-      "PID",
+      "Command-based",
+      "Swerve Drive",
+      "Gyro",
+      "Encoder",
+      "Odometry",
       "Trajectory",
-      "Path following"
+      "Path Following",
+      "XboxController"
     ],
     "foldername": "swervecontrollercommand",
     "gradlebase": "java",
@@ -573,9 +687,16 @@
   },
   {
     "name": "RamseteController",
-    "description": "An example robot demonstrating the use of RamseteController.",
+    "description": "Follow a pre-generated trajectory with a differential drive using RamseteController.",
     "tags": [
-      "RamseteController"
+      "Basic Robot",
+      "Differential Drive",
+      "Ramsete",
+      "PID",
+      "Odometry",
+      "Path Following",
+      "Trajectory",
+      "XboxController"
     ],
     "foldername": "ramsetecontroller",
     "gradlebase": "java",
@@ -584,15 +705,13 @@
   },
   {
     "name": "StateSpaceFlywheel",
-    "description": "An example state-space controller for a flywheel.",
+    "description": "Control a flywheel using a state-space model (based on values from CAD), with a Kalman Filter and LQR.",
     "tags": [
-      "StateSpaceFlywheel",
+      "Basic Robot",
       "Flywheel",
-      "State Space",
-      "Model",
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "State-Space",
+      "LQR",
+      "Encoder",
       "Joystick"
     ],
     "foldername": "statespaceflywheel",
@@ -602,16 +721,14 @@
   },
   {
     "name": "StateSpaceFlywheelSysId",
-    "description": "An example state-space controller for controlling a flywheel with System Identification.",
+    "description": "Control a flywheel using a state-space model (based on values from SysId), with a Kalman Filter and LQR.",
     "tags": [
-      "StateSpaceFlywheelSysId",
-      "FRC Characterization",
+      "Basic Robot",
       "Flywheel",
-      "Characterization",
-      "State space",
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "SysId",
+      "State-Space",
+      "LQR",
+      "Encoder",
       "Joystick"
     ],
     "foldername": "statespaceflywheelsysid",
@@ -621,13 +738,13 @@
   },
   {
     "name": "StateSpaceElevator",
-    "description": "An example state-space controller for controlling an elevator.",
+    "description": "Control an elevator using a state-space model (based on values from CAD), with a Kalman Filter and LQR.",
     "tags": [
+      "Basic Robot",
       "Elevator",
-      "State Space",
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "State-Space",
+      "LQR",
+      "Encoder",
       "Joystick"
     ],
     "foldername": "statespaceelevator",
@@ -637,13 +754,13 @@
   },
   {
     "name": "StateSpaceArm",
-    "description": "An example state-space controller for controlling an arm.",
+    "description": "Control an arm using a state-space model (based on values from CAD), with a Kalman Filter and LQR.",
     "tags": [
+      "Basic Robot",
       "Arm",
-      "State space",
-      "Digital",
-      "Sensors",
-      "Actuators",
+      "State-Space",
+      "LQR",
+      "Encoder",
       "Joystick"
     ],
     "foldername": "statespacearm",
@@ -653,14 +770,15 @@
   },
   {
     "name": "SimpleDifferentialDriveSimulation",
-    "description": "An example of a minimal drivetrain simulation project without the command-based library.",
+    "description": "Simulate a differential drivetrain and follow trajectories with RamseteController (non-command-based).",
     "tags": [
-      "Drivetrain",
-      "State space",
-      "Digital",
-      "Sensors",
-      "Actuators",
-      "Joystick",
+      "Differential Drive",
+      "State-Space",
+      "Ramsete",
+      "Path Following",
+      "Trajectory",
+      "Encoder",
+      "XboxController",
       "Simulation"
     ],
     "foldername": "simpledifferentialdrivesimulation",
@@ -670,14 +788,12 @@
   },
   {
     "name": "StateSpaceDriveSimulation",
-    "description": "An example of drivetrain simulation in combination with a RAMSETE path following controller and the Field2d class.",
+    "description": "Simulate a differential drivetrain and follow trajectories with RamseteCommand (command-based).",
     "tags": [
-      "Drivetrain",
-      "State space",
-      "Digital",
-      "Sensors",
-      "Actuators",
-      "Joystick",
+      "Command-based",
+      "Differential Drive",
+      "State-Space",
+      "XboxController",
       "Simulation"
     ],
     "foldername": "statespacedifferentialdrivesimulation",
@@ -687,15 +803,14 @@
   },
   {
     "name": "ElevatorSimulation",
-    "description": "Demonstrates the use of physics simulation with a simple elevator.",
+    "description": "Simulate an elevator.",
     "tags": [
+      "Basic Robot",
       "Elevator",
-      "State space",
-      "Digital",
-      "Sensors",
+      "State-Space",
       "Simulation",
-      "Physics",
-      "Mechanism2d"
+      "Mechanism2d",
+      "Profiled PID"
     ],
     "foldername": "elevatorsimulation",
     "gradlebase": "java",
@@ -703,15 +818,30 @@
     "commandversion": 2
   },
   {
-    "name": "ArmSimulation",
-    "description": "Demonstrates the use of physics simulation with a simple single-jointedarm.",
+    "name": "Elevator Exponential Profile Simulation",
+    "description": "Simulate an elevator.",
     "tags": [
-      "Arm",
-      "State space",
-      "Digital",
-      "Sensors",
+      "Basic Robot",
+      "Elevator",
+      "State-Space",
       "Simulation",
-      "Physics",
+      "Mechanism2d",
+      "PID",
+      "Exponential Profile"
+    ],
+    "foldername": "elevatorexponentialsimulation",
+    "gradlebase": "java",
+    "mainclass": "Main",
+    "commandversion": 2
+  },
+  {
+    "name": "ArmSimulation",
+    "description": "Simulate a single-jointed arm.",
+    "tags": [
+      "Basic Robot",
+      "Arm",
+      "State-Space",
+      "Simulation",
       "Mechanism2d",
       "Preferences"
     ],
@@ -722,26 +852,27 @@
   },
   {
     "name": "UnitTesting",
-    "description": "Demonstrates basic unit testing for a robot project.",
+    "description": "Test a robot project with basic unit tests in simulation.",
     "tags": [
-      "Testing"
+      "Intake",
+      "Pneumatics"
     ],
     "foldername": "unittest",
     "gradlebase": "java",
     "mainclass": "Main",
-    "commandversion": 2
+    "commandversion": 2,
+    "hasunittests": true
   },
   {
     "name": "DifferentialDrivePoseEstimator",
-    "description": "Demonstrates the use of the DifferentialDrivePoseEstimator as a replacement for differential drive odometry.",
+    "description": "Combine differential-drive odometry with vision data using DifferentialDrivePoseEstimator.",
     "tags": [
-      "Drivetrain",
-      "State space",
+      "Differential Drive",
+      "State-Space",
+      "Pose Estimator",
       "Vision",
-      "Filter",
-      "Odometry",
-      "Pose",
-      "Differential drive"
+      "PID",
+      "XboxController"
     ],
     "foldername": "differentialdriveposeestimator",
     "gradlebase": "java",
@@ -750,15 +881,14 @@
   },
   {
     "name": "MecanumDrivePoseEstimator",
-    "description": "Demonstrates the use of the MecanumDrivePoseEstimator as a replacement for mecanum drive odometry.",
+    "description": "Combine mecanum-drive odometry with vision data using MecanumDrivePoseEstimator.",
     "tags": [
-      "Drivetrain",
-      "State space",
+      "Mecanum Drive",
+      "State-Space",
+      "Pose Estimator",
       "Vision",
-      "Filter",
-      "Odometry",
-      "Pose",
-      "Mecanum"
+      "PID",
+      "XboxController"
     ],
     "foldername": "mecanumdriveposeestimator",
     "gradlebase": "java",
@@ -767,15 +897,14 @@
   },
   {
     "name": "SwerveDrivePoseEstimator",
-    "description": "Demonstrates the use of the SwerveDrivePoseEstimator as a replacement for swerve drive odometry.",
+    "description": "Combine swerve-drive odometry with vision data using SwerveDrivePoseEstimator.",
     "tags": [
-      "Drivetrain",
-      "State space",
+      "Swerve Drive",
+      "State-Space",
+      "Pose Estimator",
       "Vision",
-      "Filter",
-      "Odometry",
-      "Pose",
-      "Swerve"
+      "PID",
+      "XboxController"
     ],
     "foldername": "swervedriveposeestimator",
     "gradlebase": "java",
@@ -786,19 +915,44 @@
     "name": "RomiReference",
     "description": "An example command-based robot program that can be used with the Romi reference robot design.",
     "tags": [
-      "Drivetrain",
-      "Romi"
+      "Romi",
+      "Command-based",
+      "Differential Drive",
+      "Digital Input",
+      "Joystick"
     ],
     "foldername": "romireference",
     "gradlebase": "javaromi",
     "mainclass": "Main",
-    "commandversion": 2
+    "commandversion": 2,
+    "extravendordeps": [
+      "romi"
+    ]
+  },
+  {
+    "name": "XRP Reference",
+    "description": "An example command-based robot program that can be used with the XRP reference robot design.",
+    "tags": [
+      "XRP",
+      "Command-based",
+      "Differential Drive",
+      "Digital Input",
+      "Joystick"
+    ],
+    "foldername": "xrpreference",
+    "gradlebase": "javaxrp",
+    "mainclass": "Main",
+    "commandversion": 2,
+    "extravendordeps": [
+      "xrp"
+    ]
   },
   {
     "name": "Digital Communication Sample",
-    "description": "An example program that communicates with external devices (such as an Arduino) using the roboRIO's DIO",
+    "description": "Communicates with external devices (such as an Arduino) using the roboRIO's DIO.",
     "tags": [
-      "Digital"
+      "Hardware",
+      "Digital Output"
     ],
     "foldername": "digitalcommunication",
     "gradlebase": "java",
@@ -807,13 +961,27 @@
   },
   {
     "name": "I2C Communication Sample",
-    "description": "An example program that communicates with external devices (such as an Arduino) using the roboRIO's I2C port",
+    "description": "Communicate with external devices (such as an Arduino) using the roboRIO's I2C port.",
     "tags": [
+      "Hardware",
       "I2C"
     ],
     "foldername": "i2ccommunication",
     "gradlebase": "java",
     "commandversion": 2,
     "mainclass": "Main"
+  },
+  {
+    "name": "Flywheel BangBangController",
+    "description": "A sample program to demonstrate the use of a BangBangController with a flywheel to control RPM",
+    "tags": [
+      "Flywheel",
+      "Simulation",
+      "Joystick"
+    ],
+    "foldername": "flywheelbangbangcontroller",
+    "gradlebase": "java",
+    "commandversion": 2,
+    "mainclass": "Main"
   }
 ]
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/flywheelbangbangcontroller/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/flywheelbangbangcontroller/Main.java
new file mode 100644
index 0000000..d114f8b
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/flywheelbangbangcontroller/Main.java
@@ -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.
+
+package edu.wpi.first.wpilibj.examples.flywheelbangbangcontroller;
+
+import edu.wpi.first.wpilibj.RobotBase;
+
+/**
+ * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
+ * you are doing, do not modify this file except to change the parameter class to the startRobot
+ * call.
+ */
+public final class Main {
+  private Main() {}
+
+  /**
+   * Main initialization function. Do not perform any initialization here.
+   *
+   * <p>If you change your main robot class, change the parameter type.
+   */
+  public static void main(String... args) {
+    RobotBase.startRobot(Robot::new);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/flywheelbangbangcontroller/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/flywheelbangbangcontroller/Robot.java
new file mode 100644
index 0000000..a025b6a
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/flywheelbangbangcontroller/Robot.java
@@ -0,0 +1,95 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.flywheelbangbangcontroller;
+
+import edu.wpi.first.math.controller.BangBangController;
+import edu.wpi.first.math.controller.SimpleMotorFeedforward;
+import edu.wpi.first.math.system.plant.DCMotor;
+import edu.wpi.first.math.util.Units;
+import edu.wpi.first.wpilibj.Encoder;
+import edu.wpi.first.wpilibj.Joystick;
+import edu.wpi.first.wpilibj.RobotController;
+import edu.wpi.first.wpilibj.TimedRobot;
+import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
+import edu.wpi.first.wpilibj.simulation.EncoderSim;
+import edu.wpi.first.wpilibj.simulation.FlywheelSim;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
+
+/**
+ * This is a sample program to demonstrate the use of a BangBangController with a flywheel to
+ * control RPM.
+ */
+public class Robot extends TimedRobot {
+  private static final int kMotorPort = 0;
+  private static final int kEncoderAChannel = 0;
+  private static final int kEncoderBChannel = 1;
+
+  // Max setpoint for joystick control in RPM
+  private static final double kMaxSetpointValue = 6000.0;
+
+  // Joystick to control setpoint
+  private final Joystick m_joystick = new Joystick(0);
+
+  private final PWMSparkMax m_flywheelMotor = new PWMSparkMax(kMotorPort);
+  private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel);
+
+  private final BangBangController m_bangBangControler = new BangBangController();
+
+  // Gains are for example purposes only - must be determined for your own robot!
+  public static final double kFlywheelKs = 0.0001; // V
+  public static final double kFlywheelKv = 0.000195; // V/RPM
+  public static final double kFlywheelKa = 0.0003; // V/(RPM/s)
+  private final SimpleMotorFeedforward m_feedforward =
+      new SimpleMotorFeedforward(kFlywheelKs, kFlywheelKv, kFlywheelKa);
+
+  // Simulation classes help us simulate our robot
+
+  // Reduction between motors and encoder, as output over input. If the flywheel
+  // spins slower than the motors, this number should be greater than one.
+  private static final double kFlywheelGearing = 1.0;
+
+  // 1/2 MR²
+  private static final double kFlywheelMomentOfInertia =
+      0.5 * Units.lbsToKilograms(1.5) * Math.pow(Units.inchesToMeters(4), 2);
+
+  private final FlywheelSim m_flywheelSim =
+      new FlywheelSim(DCMotor.getNEO(1), kFlywheelGearing, kFlywheelMomentOfInertia);
+  private final EncoderSim m_encoderSim = new EncoderSim(m_encoder);
+
+  @Override
+  public void robotInit() {
+    // Add bang-bang controler to SmartDashboard and networktables.
+    SmartDashboard.putData(m_bangBangControler);
+  }
+
+  /** Controls flywheel to a set speed (RPM) controlled by a joystick. */
+  @Override
+  public void teleopPeriodic() {
+    // Scale setpoint value between 0 and maxSetpointValue
+    double setpoint =
+        Math.max(
+            0.0,
+            m_joystick.getRawAxis(0)
+                * Units.rotationsPerMinuteToRadiansPerSecond(kMaxSetpointValue));
+
+    // Set setpoint and measurement of the bang-bang controller
+    double bangOutput = m_bangBangControler.calculate(m_encoder.getRate(), setpoint) * 12.0;
+
+    // Controls a motor with the output of the BangBang controller and a
+    // feedforward. The feedforward is reduced slightly to avoid overspeeding
+    // the shooter.
+    m_flywheelMotor.setVoltage(bangOutput + 0.9 * m_feedforward.calculate(setpoint));
+  }
+
+  /** Update our simulation. This should be run every robot loop in simulation. */
+  @Override
+  public void simulationPeriodic() {
+    // To update our simulation, we set motor voltage inputs, update the
+    // simulation, and write the simulated velocities to our simulated encoder
+    m_flywheelSim.setInputVoltage(m_flywheelMotor.get() * RobotController.getInputVoltage());
+    m_flywheelSim.update(0.02);
+    m_encoderSim.setRate(m_flywheelSim.getAngularVelocityRadPerSec());
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/Constants.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/Constants.java
new file mode 100644
index 0000000..6f80067
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/Constants.java
@@ -0,0 +1,94 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.gearsbot;
+
+public final class Constants {
+  public static final class DriveConstants {
+    public static final int kLeftMotorPort1 = 0;
+    public static final int kLeftMotorPort2 = 1;
+
+    public static final int kRightMotorPort1 = 2;
+    public static final int kRightMotorPort2 = 3;
+
+    public static final int[] kLeftEncoderPorts = {0, 1};
+    public static final int[] kRightEncoderPorts = {2, 3};
+    public static final boolean kLeftEncoderReversed = false;
+    public static final boolean kRightEncoderReversed = false;
+
+    public static final int kRangeFinderPort = 6;
+    public static final int kAnalogGyroPort = 1;
+
+    public static final int kEncoderCPR = 1024;
+    public static final double kWheelDiameterInches = 6;
+    public static final double kEncoderDistancePerPulse =
+        // Assumes the encoders are directly mounted on the wheel shafts
+        (kWheelDiameterInches * Math.PI) / (double) kEncoderCPR;
+  }
+
+  public static final class ClawConstants {
+    public static final int kMotorPort = 7;
+    public static final int kContactPort = 5;
+  }
+
+  public static final class WristConstants {
+    public static final int kMotorPort = 6;
+
+    // these pid constants are not real, and will need to be tuned
+    public static final double kP = 0.1;
+    public static final double kI = 0.0;
+    public static final double kD = 0.0;
+
+    public static final double kTolerance = 2.5;
+
+    public static final int kPotentiometerPort = 3;
+  }
+
+  public static final class ElevatorConstants {
+    public static final int kMotorPort = 5;
+    public static final int kPotentiometerPort = 2;
+
+    // these pid constants are not real, and will need to be tuned
+    public static final double kP_real = 4;
+    public static final double kI_real = 0.007;
+
+    public static final double kP_simulation = 18;
+    public static final double kI_simulation = 0.2;
+
+    public static final double kD = 0.0;
+
+    public static final double kTolerance = 0.005;
+  }
+
+  public static final class AutoConstants {
+    public static final double kDistToBox1 = 0.10;
+    public static final double kDistToBox2 = 0.60;
+
+    public static final double kWristSetpoint = -45.0;
+  }
+
+  public static final class DriveStraightConstants {
+    // these pid constants are not real, and will need to be tuned
+    public static final double kP = 4.0;
+    public static final double kI = 0.0;
+    public static final double kD = 0.0;
+  }
+
+  public static final class Positions {
+    public static final class Pickup {
+      public static final double kWristSetpoint = -45.0;
+      public static final double kElevatorSetpoint = 0.25;
+    }
+
+    public static final class Place {
+      public static final double kWristSetpoint = 0.0;
+      public static final double kElevatorSetpoint = 0.25;
+    }
+
+    public static final class PrepareToPickup {
+      public static final double kWristSetpoint = 0.0;
+      public static final double kElevatorSetpoint = 0.0;
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/RobotContainer.java
index 47710f9..28c76b7 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/RobotContainer.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/RobotContainer.java
@@ -20,7 +20,6 @@
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist;
 import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
 import edu.wpi.first.wpilibj2.command.Command;
-import edu.wpi.first.wpilibj2.command.CommandBase;
 import edu.wpi.first.wpilibj2.command.button.JoystickButton;
 
 /**
@@ -38,7 +37,7 @@
 
   private final XboxController m_joystick = new XboxController(0);
 
-  private final CommandBase m_autonomousCommand =
+  private final Command m_autonomousCommand =
       new Autonomous(m_drivetrain, m_claw, m_wrist, m_elevator);
 
   /** The container for the robot. Contains subsystems, OI devices, and commands. */
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Autonomous.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Autonomous.java
index 44a7ce7..63b55f6 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Autonomous.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Autonomous.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj.examples.gearsbot.commands;
 
+import edu.wpi.first.wpilibj.examples.gearsbot.Constants.AutoConstants;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Claw;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Drivetrain;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator;
@@ -18,11 +19,12 @@
     addCommands(
         new PrepareToPickup(claw, wrist, elevator),
         new Pickup(claw, wrist, elevator),
-        new SetDistanceToBox(0.10, drive),
+        new SetDistanceToBox(AutoConstants.kDistToBox1, drive),
         // new DriveStraight(4), // Use encoders if ultrasonic is broken
         new Place(claw, wrist, elevator),
-        new SetDistanceToBox(0.60, drive),
+        new SetDistanceToBox(AutoConstants.kDistToBox2, drive),
         // new DriveStraight(-2), // Use Encoders if ultrasonic is broken
-        Commands.parallel(new SetWristSetpoint(-45, wrist), new CloseClaw(claw)));
+        Commands.parallel(
+            new SetWristSetpoint(AutoConstants.kWristSetpoint, wrist), new CloseClaw(claw)));
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/CloseClaw.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/CloseClaw.java
index 8ef1deb..1110e57 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/CloseClaw.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/CloseClaw.java
@@ -6,10 +6,10 @@
 
 import edu.wpi.first.wpilibj.examples.gearsbot.Robot;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Claw;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
 /** Closes the claw until the limit switch is tripped. */
-public class CloseClaw extends CommandBase {
+public class CloseClaw extends Command {
   private final Claw m_claw;
 
   public CloseClaw(Claw claw) {
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/DriveStraight.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/DriveStraight.java
index ed47cc1..2430b61 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/DriveStraight.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/DriveStraight.java
@@ -5,6 +5,7 @@
 package edu.wpi.first.wpilibj.examples.gearsbot.commands;
 
 import edu.wpi.first.math.controller.PIDController;
+import edu.wpi.first.wpilibj.examples.gearsbot.Constants.DriveStraightConstants;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Drivetrain;
 import edu.wpi.first.wpilibj2.command.PIDCommand;
 
@@ -23,7 +24,11 @@
    */
   public DriveStraight(double distance, Drivetrain drivetrain) {
     super(
-        new PIDController(4, 0, 0), drivetrain::getDistance, distance, d -> drivetrain.drive(d, d));
+        new PIDController(
+            DriveStraightConstants.kP, DriveStraightConstants.kI, DriveStraightConstants.kD),
+        drivetrain::getDistance,
+        distance,
+        d -> drivetrain.drive(d, d));
 
     m_drivetrain = drivetrain;
     addRequirements(m_drivetrain);
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Pickup.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Pickup.java
index 4e6eafd..78f8879 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Pickup.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Pickup.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj.examples.gearsbot.commands;
 
+import edu.wpi.first.wpilibj.examples.gearsbot.Constants.Positions;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Claw;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist;
@@ -25,6 +26,7 @@
     addCommands(
         new CloseClaw(claw),
         Commands.parallel(
-            new SetWristSetpoint(-45, wrist), new SetElevatorSetpoint(0.25, elevator)));
+            new SetWristSetpoint(Positions.Pickup.kWristSetpoint, wrist),
+            new SetElevatorSetpoint(Positions.Pickup.kElevatorSetpoint, elevator)));
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Place.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Place.java
index 442744c..fad6de5 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Place.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/Place.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj.examples.gearsbot.commands;
 
+import edu.wpi.first.wpilibj.examples.gearsbot.Constants.Positions;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Claw;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist;
@@ -20,8 +21,8 @@
    */
   public Place(Claw claw, Wrist wrist, Elevator elevator) {
     addCommands(
-        new SetElevatorSetpoint(0.25, elevator),
-        new SetWristSetpoint(0, wrist),
+        new SetElevatorSetpoint(Positions.Place.kElevatorSetpoint, elevator),
+        new SetWristSetpoint(Positions.Place.kWristSetpoint, wrist),
         new OpenClaw(claw));
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/PrepareToPickup.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/PrepareToPickup.java
index 3c0661d..d9ae53c 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/PrepareToPickup.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/PrepareToPickup.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj.examples.gearsbot.commands;
 
+import edu.wpi.first.wpilibj.examples.gearsbot.Constants.Positions;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Claw;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator;
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist;
@@ -22,6 +23,8 @@
   public PrepareToPickup(Claw claw, Wrist wrist, Elevator elevator) {
     addCommands(
         new OpenClaw(claw),
-        Commands.parallel(new SetWristSetpoint(0, wrist), new SetElevatorSetpoint(0, elevator)));
+        Commands.parallel(
+            new SetWristSetpoint(Positions.PrepareToPickup.kWristSetpoint, wrist),
+            new SetElevatorSetpoint(Positions.PrepareToPickup.kElevatorSetpoint, elevator)));
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/SetElevatorSetpoint.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/SetElevatorSetpoint.java
index a323305..66790c2 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/SetElevatorSetpoint.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/SetElevatorSetpoint.java
@@ -5,14 +5,14 @@
 package edu.wpi.first.wpilibj.examples.gearsbot.commands;
 
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
 /**
  * Move the elevator to a given location. This command finishes when it is within the tolerance, but
  * leaves the PID loop running to maintain the position. Other commands using the elevator should
  * make sure they disable PID!
  */
-public class SetElevatorSetpoint extends CommandBase {
+public class SetElevatorSetpoint extends Command {
   private final Elevator m_elevator;
   private final double m_setpoint;
 
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/SetWristSetpoint.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/SetWristSetpoint.java
index 4c45ddf..a185ed5 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/SetWristSetpoint.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/SetWristSetpoint.java
@@ -5,14 +5,14 @@
 package edu.wpi.first.wpilibj.examples.gearsbot.commands;
 
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
 /**
  * Move the wrist to a given angle. This command finishes when it is within the tolerance, but
  * leaves the PID loop running to maintain the position. Other commands using the wrist should make
  * sure they disable PID!
  */
-public class SetWristSetpoint extends CommandBase {
+public class SetWristSetpoint extends Command {
   private final Wrist m_wrist;
   private final double m_setpoint;
 
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/TankDrive.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/TankDrive.java
index 009daad..3830f82 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/TankDrive.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/commands/TankDrive.java
@@ -5,11 +5,11 @@
 package edu.wpi.first.wpilibj.examples.gearsbot.commands;
 
 import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Drivetrain;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import java.util.function.DoubleSupplier;
 
 /** Have the robot drive tank style. */
-public class TankDrive extends CommandBase {
+public class TankDrive extends Command {
   private final Drivetrain m_drivetrain;
   private final DoubleSupplier m_left;
   private final DoubleSupplier m_right;
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Claw.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Claw.java
index d34d12a..06c627e 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Claw.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Claw.java
@@ -5,6 +5,7 @@
 package edu.wpi.first.wpilibj.examples.gearsbot.subsystems;
 
 import edu.wpi.first.wpilibj.DigitalInput;
+import edu.wpi.first.wpilibj.examples.gearsbot.Constants.ClawConstants;
 import edu.wpi.first.wpilibj.motorcontrol.Victor;
 import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
@@ -14,8 +15,8 @@
  * motors, you should probably use a sensor so that the motors don't stall.
  */
 public class Claw extends SubsystemBase {
-  private final Victor m_motor = new Victor(7);
-  private final DigitalInput m_contact = new DigitalInput(5);
+  private final Victor m_motor = new Victor(ClawConstants.kMotorPort);
+  private final DigitalInput m_contact = new DigitalInput(ClawConstants.kContactPort);
 
   /** Create a new claw subsystem. */
   public Claw() {
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Drivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Drivetrain.java
index 44f455d..ffde63d 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Drivetrain.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Drivetrain.java
@@ -8,6 +8,7 @@
 import edu.wpi.first.wpilibj.AnalogInput;
 import edu.wpi.first.wpilibj.Encoder;
 import edu.wpi.first.wpilibj.drive.DifferentialDrive;
+import edu.wpi.first.wpilibj.examples.gearsbot.Constants.DriveConstants;
 import edu.wpi.first.wpilibj.examples.gearsbot.Robot;
 import edu.wpi.first.wpilibj.motorcontrol.MotorController;
 import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup;
@@ -21,17 +22,29 @@
    * These include four drive motors, a left and right encoder and a gyro.
    */
   private final MotorController m_leftMotor =
-      new MotorControllerGroup(new PWMSparkMax(0), new PWMSparkMax(1));
+      new MotorControllerGroup(
+          new PWMSparkMax(DriveConstants.kLeftMotorPort1),
+          new PWMSparkMax(DriveConstants.kLeftMotorPort1));
 
   private final MotorController m_rightMotor =
-      new MotorControllerGroup(new PWMSparkMax(2), new PWMSparkMax(3));
+      new MotorControllerGroup(
+          new PWMSparkMax(DriveConstants.kRightMotorPort2),
+          new PWMSparkMax(DriveConstants.kLeftMotorPort2));
 
   private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotor, m_rightMotor);
 
-  private final Encoder m_leftEncoder = new Encoder(1, 2);
-  private final Encoder m_rightEncoder = new Encoder(3, 4);
-  private final AnalogInput m_rangefinder = new AnalogInput(6);
-  private final AnalogGyro m_gyro = new AnalogGyro(1);
+  private final Encoder m_leftEncoder =
+      new Encoder(
+          DriveConstants.kLeftEncoderPorts[0],
+          DriveConstants.kLeftEncoderPorts[1],
+          DriveConstants.kLeftEncoderReversed);
+  private final Encoder m_rightEncoder =
+      new Encoder(
+          DriveConstants.kRightEncoderPorts[0],
+          DriveConstants.kRightEncoderPorts[1],
+          DriveConstants.kRightEncoderReversed);
+  private final AnalogInput m_rangefinder = new AnalogInput(DriveConstants.kRangeFinderPort);
+  private final AnalogGyro m_gyro = new AnalogGyro(DriveConstants.kAnalogGyroPort);
 
   /** Create a new drivetrain subsystem. */
   public Drivetrain() {
@@ -48,8 +61,8 @@
     // simulate 360 tick encoders. This if statement allows for the
     // real robot to handle this difference in devices.
     if (Robot.isReal()) {
-      m_leftEncoder.setDistancePerPulse(0.042);
-      m_rightEncoder.setDistancePerPulse(0.042);
+      m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse);
+      m_rightEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse);
     } else {
       // Circumference = diameter in feet * pi. 360 tick simulated encoders.
       m_leftEncoder.setDistancePerPulse((4.0 / 12.0 * Math.PI) / 360.0);
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Elevator.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Elevator.java
index e174595..b3eba1f 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Elevator.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Elevator.java
@@ -6,6 +6,7 @@
 
 import edu.wpi.first.math.controller.PIDController;
 import edu.wpi.first.wpilibj.AnalogPotentiometer;
+import edu.wpi.first.wpilibj.examples.gearsbot.Constants.ElevatorConstants;
 import edu.wpi.first.wpilibj.examples.gearsbot.Robot;
 import edu.wpi.first.wpilibj.motorcontrol.Victor;
 import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
@@ -16,30 +17,32 @@
  * values for simulation are different than in the real world do to minor differences.
  */
 public class Elevator extends PIDSubsystem {
-  private final Victor m_motor;
+  private final Victor m_motor = new Victor(ElevatorConstants.kMotorPort);
   private final AnalogPotentiometer m_pot;
 
-  private static final double kP_real = 4;
-  private static final double kI_real = 0.07;
-  private static final double kP_simulation = 18;
-  private static final double kI_simulation = 0.2;
-
   /** Create a new elevator subsystem. */
   public Elevator() {
-    super(new PIDController(kP_real, kI_real, 0));
-    if (Robot.isSimulation()) { // Check for simulation and update PID values
-      getController().setPID(kP_simulation, kI_simulation, 0);
-    }
-    getController().setTolerance(0.005);
+    super(
+        new PIDController(
+            ElevatorConstants.kP_real, ElevatorConstants.kI_real, ElevatorConstants.kD));
 
-    m_motor = new Victor(5);
+    if (Robot.isSimulation()) { // Check for simulation and update PID values
+      getController()
+          .setPID(
+              ElevatorConstants.kP_simulation,
+              ElevatorConstants.kI_simulation,
+              ElevatorConstants.kD);
+    }
+    getController().setTolerance(ElevatorConstants.kTolerance);
 
     // Conversion value of potentiometer varies between the real world and
     // simulation
+
     if (Robot.isReal()) {
-      m_pot = new AnalogPotentiometer(2, -2.0 / 5);
+      m_pot = new AnalogPotentiometer(ElevatorConstants.kPotentiometerPort, -2.0 / 5);
     } else {
-      m_pot = new AnalogPotentiometer(2); // Defaults to meters
+      // Defaults to meters
+      m_pot = new AnalogPotentiometer(ElevatorConstants.kPotentiometerPort);
     }
 
     // Let's name everything on the LiveWindow
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Wrist.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Wrist.java
index e11e2d7..d13df64 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Wrist.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Wrist.java
@@ -6,6 +6,7 @@
 
 import edu.wpi.first.math.controller.PIDController;
 import edu.wpi.first.wpilibj.AnalogPotentiometer;
+import edu.wpi.first.wpilibj.examples.gearsbot.Constants.WristConstants;
 import edu.wpi.first.wpilibj.examples.gearsbot.Robot;
 import edu.wpi.first.wpilibj.motorcontrol.Victor;
 import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
@@ -15,24 +16,21 @@
  * The wrist subsystem is like the elevator, but with a rotational joint instead of a linear joint.
  */
 public class Wrist extends PIDSubsystem {
-  private final Victor m_motor;
+  private final Victor m_motor = new Victor(WristConstants.kMotorPort);
   private final AnalogPotentiometer m_pot;
 
-  private static final double kP = 1;
-
   /** Create a new wrist subsystem. */
   public Wrist() {
-    super(new PIDController(kP, 0, 0));
-    getController().setTolerance(2.5);
-
-    m_motor = new Victor(6);
+    super(new PIDController(WristConstants.kP, WristConstants.kI, WristConstants.kD));
+    getController().setTolerance(WristConstants.kTolerance);
 
     // Conversion value of potentiometer varies between the real world and
     // simulation
     if (Robot.isReal()) {
-      m_pot = new AnalogPotentiometer(3, -270.0 / 5);
+      m_pot = new AnalogPotentiometer(WristConstants.kPotentiometerPort, -270.0 / 5);
     } else {
-      m_pot = new AnalogPotentiometer(3); // Defaults to degrees
+      // Defaults to degrees
+      m_pot = new AnalogPotentiometer(WristConstants.kPotentiometerPort);
     }
 
     // Let's name everything on the LiveWindow
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java
index 45094fa..dd337e6 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java
@@ -38,8 +38,7 @@
   /** This function is run once each time the robot enters autonomous mode. */
   @Override
   public void autonomousInit() {
-    m_timer.reset();
-    m_timer.start();
+    m_timer.restart();
   }
 
   /** This function is called periodically during autonomous. */
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java
index a2fdf6e..9adb6ec 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java
@@ -8,7 +8,6 @@
 import edu.wpi.first.wpilibj.Encoder;
 import edu.wpi.first.wpilibj.drive.DifferentialDrive;
 import edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants;
-import edu.wpi.first.wpilibj.interfaces.Gyro;
 import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
@@ -44,7 +43,7 @@
           DriveConstants.kRightEncoderReversed);
 
   // The gyro sensor
-  private final Gyro m_gyro = new ADXRS450_Gyro();
+  private final ADXRS450_Gyro m_gyro = new ADXRS450_Gyro();
 
   /** Creates a new DriveSubsystem. */
   public DriveSubsystem() {
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/Robot.java
index ef49333..4fb6d1b 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/Robot.java
@@ -4,6 +4,8 @@
 
 package edu.wpi.first.wpilibj.examples.hatchbotinlined;
 
+import edu.wpi.first.wpilibj.DataLogManager;
+import edu.wpi.first.wpilibj.DriverStation;
 import edu.wpi.first.wpilibj.TimedRobot;
 import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.CommandScheduler;
@@ -28,6 +30,13 @@
     // Instantiate our RobotContainer.  This will perform all our button bindings, and put our
     // autonomous chooser on the dashboard.
     m_robotContainer = new RobotContainer();
+
+    // Start recording to data log
+    DataLogManager.start();
+
+    // Record DS control and joystick data.
+    // Change to `false` to not record joystick data.
+    DriverStation.startDataLog(DataLogManager.getLog(), true);
   }
 
   /**
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/RobotContainer.java
index 902ecbe..2d7cedf 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/RobotContainer.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/RobotContainer.java
@@ -9,9 +9,11 @@
 import edu.wpi.first.wpilibj.examples.hatchbotinlined.commands.Autos;
 import edu.wpi.first.wpilibj.examples.hatchbotinlined.subsystems.DriveSubsystem;
 import edu.wpi.first.wpilibj.examples.hatchbotinlined.subsystems.HatchSubsystem;
+import edu.wpi.first.wpilibj.shuffleboard.EventImportance;
 import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
 import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
 import edu.wpi.first.wpilibj2.command.Command;
+import edu.wpi.first.wpilibj2.command.CommandScheduler;
 import edu.wpi.first.wpilibj2.command.Commands;
 import edu.wpi.first.wpilibj2.command.button.CommandPS4Controller;
 
@@ -63,6 +65,27 @@
 
     // Put the chooser on the dashboard
     Shuffleboard.getTab("Autonomous").add(m_chooser);
+
+    // Put subsystems to dashboard.
+    Shuffleboard.getTab("Drivetrain").add(m_robotDrive);
+    Shuffleboard.getTab("HatchSubsystem").add(m_hatchSubsystem);
+
+    // Set the scheduler to log Shuffleboard events for command initialize, interrupt, finish
+    CommandScheduler.getInstance()
+        .onCommandInitialize(
+            command ->
+                Shuffleboard.addEventMarker(
+                    "Command initialized", command.getName(), EventImportance.kNormal));
+    CommandScheduler.getInstance()
+        .onCommandInterrupt(
+            command ->
+                Shuffleboard.addEventMarker(
+                    "Command interrupted", command.getName(), EventImportance.kNormal));
+    CommandScheduler.getInstance()
+        .onCommandFinish(
+            command ->
+                Shuffleboard.addEventMarker(
+                    "Command finished", command.getName(), EventImportance.kNormal));
   }
 
   /**
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java
index 115ee02..1376195 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj.examples.hatchbotinlined.subsystems;
 
+import edu.wpi.first.util.sendable.SendableBuilder;
 import edu.wpi.first.wpilibj.Encoder;
 import edu.wpi.first.wpilibj.drive.DifferentialDrive;
 import edu.wpi.first.wpilibj.examples.hatchbotinlined.Constants.DriveConstants;
@@ -79,24 +80,6 @@
   }
 
   /**
-   * Gets the left drive encoder.
-   *
-   * @return the left drive encoder
-   */
-  public Encoder getLeftEncoder() {
-    return m_leftEncoder;
-  }
-
-  /**
-   * Gets the right drive encoder.
-   *
-   * @return the right drive encoder
-   */
-  public Encoder getRightEncoder() {
-    return m_rightEncoder;
-  }
-
-  /**
    * Sets the max output of the drive. Useful for scaling the drive to drive more slowly.
    *
    * @param maxOutput the maximum output to which the drive will be constrained
@@ -104,4 +87,12 @@
   public void setMaxOutput(double maxOutput) {
     m_drive.setMaxOutput(maxOutput);
   }
+
+  @Override
+  public void initSendable(SendableBuilder builder) {
+    super.initSendable(builder);
+    // Publish encoder distances to telemetry.
+    builder.addDoubleProperty("leftDistance", m_leftEncoder::getDistance, null);
+    builder.addDoubleProperty("rightDistance", m_rightEncoder::getDistance, null);
+  }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/HatchSubsystem.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/HatchSubsystem.java
index b875f2c..ec489be 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/HatchSubsystem.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/HatchSubsystem.java
@@ -7,10 +7,11 @@
 import static edu.wpi.first.wpilibj.DoubleSolenoid.Value.kForward;
 import static edu.wpi.first.wpilibj.DoubleSolenoid.Value.kReverse;
 
+import edu.wpi.first.util.sendable.SendableBuilder;
 import edu.wpi.first.wpilibj.DoubleSolenoid;
 import edu.wpi.first.wpilibj.PneumaticsModuleType;
 import edu.wpi.first.wpilibj.examples.hatchbotinlined.Constants.HatchConstants;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
 
 /** A hatch mechanism actuated by a single {@link edu.wpi.first.wpilibj.DoubleSolenoid}. */
@@ -22,14 +23,21 @@
           HatchConstants.kHatchSolenoidPorts[1]);
 
   /** Grabs the hatch. */
-  public CommandBase grabHatchCommand() {
+  public Command grabHatchCommand() {
     // implicitly require `this`
     return this.runOnce(() -> m_hatchSolenoid.set(kForward));
   }
 
   /** Releases the hatch. */
-  public CommandBase releaseHatchCommand() {
+  public Command releaseHatchCommand() {
     // implicitly require `this`
     return this.runOnce(() -> m_hatchSolenoid.set(kReverse));
   }
+
+  @Override
+  public void initSendable(SendableBuilder builder) {
+    super.initSendable(builder);
+    // Publish the solenoid state to telemetry.
+    builder.addBooleanProperty("extended", () -> m_hatchSolenoid.get() == kForward, null);
+  }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Robot.java
index aa730c5..09617df 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Robot.java
@@ -4,6 +4,8 @@
 
 package edu.wpi.first.wpilibj.examples.hatchbottraditional;
 
+import edu.wpi.first.wpilibj.DataLogManager;
+import edu.wpi.first.wpilibj.DriverStation;
 import edu.wpi.first.wpilibj.TimedRobot;
 import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.CommandScheduler;
@@ -28,6 +30,13 @@
     // Instantiate our RobotContainer.  This will perform all our button bindings, and put our
     // autonomous chooser on the dashboard.
     m_robotContainer = new RobotContainer();
+
+    // Start recording to data log
+    DataLogManager.start();
+
+    // Record DS control and joystick data.
+    // Change to `false` to not record joystick data.
+    DriverStation.startDataLog(DataLogManager.getLog(), true);
   }
 
   /**
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java
index 19fc2c7..5bf05b4 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java
@@ -17,9 +17,11 @@
 import edu.wpi.first.wpilibj.examples.hatchbottraditional.commands.ReleaseHatch;
 import edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems.DriveSubsystem;
 import edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems.HatchSubsystem;
+import edu.wpi.first.wpilibj.shuffleboard.EventImportance;
 import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
 import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
 import edu.wpi.first.wpilibj2.command.Command;
+import edu.wpi.first.wpilibj2.command.CommandScheduler;
 import edu.wpi.first.wpilibj2.command.button.JoystickButton;
 
 /**
@@ -70,6 +72,31 @@
 
     // Put the chooser on the dashboard
     Shuffleboard.getTab("Autonomous").add(m_chooser);
+    // Put subsystems to dashboard.
+    Shuffleboard.getTab("Drivetrain").add(m_robotDrive);
+    Shuffleboard.getTab("HatchSubsystem").add(m_hatchSubsystem);
+
+    // Log Shuffleboard events for command initialize, execute, finish, interrupt
+    CommandScheduler.getInstance()
+        .onCommandInitialize(
+            command ->
+                Shuffleboard.addEventMarker(
+                    "Command initialized", command.getName(), EventImportance.kNormal));
+    CommandScheduler.getInstance()
+        .onCommandExecute(
+            command ->
+                Shuffleboard.addEventMarker(
+                    "Command executed", command.getName(), EventImportance.kNormal));
+    CommandScheduler.getInstance()
+        .onCommandFinish(
+            command ->
+                Shuffleboard.addEventMarker(
+                    "Command finished", command.getName(), EventImportance.kNormal));
+    CommandScheduler.getInstance()
+        .onCommandInterrupt(
+            command ->
+                Shuffleboard.addEventMarker(
+                    "Command interrupted", command.getName(), EventImportance.kNormal));
   }
 
   /**
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/DefaultDrive.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/DefaultDrive.java
index 460e787..7236426 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/DefaultDrive.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/DefaultDrive.java
@@ -5,7 +5,7 @@
 package edu.wpi.first.wpilibj.examples.hatchbottraditional.commands;
 
 import edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems.DriveSubsystem;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import java.util.function.DoubleSupplier;
 
 /**
@@ -13,7 +13,7 @@
  * explicitly for pedagogical purposes - actual code should inline a command this simple with {@link
  * edu.wpi.first.wpilibj2.command.RunCommand}.
  */
-public class DefaultDrive extends CommandBase {
+public class DefaultDrive extends Command {
   private final DriveSubsystem m_drive;
   private final DoubleSupplier m_forward;
   private final DoubleSupplier m_rotation;
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/DriveDistance.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/DriveDistance.java
index 04a3c0b..a766f80 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/DriveDistance.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/DriveDistance.java
@@ -5,9 +5,9 @@
 package edu.wpi.first.wpilibj.examples.hatchbottraditional.commands;
 
 import edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems.DriveSubsystem;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
-public class DriveDistance extends CommandBase {
+public class DriveDistance extends Command {
   private final DriveSubsystem m_drive;
   private final double m_distance;
   private final double m_speed;
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/GrabHatch.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/GrabHatch.java
index 9cd1550..e5175af 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/GrabHatch.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/GrabHatch.java
@@ -5,14 +5,14 @@
 package edu.wpi.first.wpilibj.examples.hatchbottraditional.commands;
 
 import edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems.HatchSubsystem;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
 /**
  * A simple command that grabs a hatch with the {@link HatchSubsystem}. Written explicitly for
  * pedagogical purposes. Actual code should inline a command this simple with {@link
  * edu.wpi.first.wpilibj2.command.InstantCommand}.
  */
-public class GrabHatch extends CommandBase {
+public class GrabHatch extends Command {
   // The subsystem the command runs on
   private final HatchSubsystem m_hatchSubsystem;
 
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/HalveDriveSpeed.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/HalveDriveSpeed.java
index c34d951..efaa285 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/HalveDriveSpeed.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/HalveDriveSpeed.java
@@ -5,9 +5,9 @@
 package edu.wpi.first.wpilibj.examples.hatchbottraditional.commands;
 
 import edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems.DriveSubsystem;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
-public class HalveDriveSpeed extends CommandBase {
+public class HalveDriveSpeed extends Command {
   private final DriveSubsystem m_drive;
 
   public HalveDriveSpeed(DriveSubsystem drive) {
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java
index d1236e2..8a5296d 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems;
 
+import edu.wpi.first.util.sendable.SendableBuilder;
 import edu.wpi.first.wpilibj.Encoder;
 import edu.wpi.first.wpilibj.drive.DifferentialDrive;
 import edu.wpi.first.wpilibj.examples.hatchbottraditional.Constants.DriveConstants;
@@ -79,24 +80,6 @@
   }
 
   /**
-   * Gets the left drive encoder.
-   *
-   * @return the left drive encoder
-   */
-  public Encoder getLeftEncoder() {
-    return m_leftEncoder;
-  }
-
-  /**
-   * Gets the right drive encoder.
-   *
-   * @return the right drive encoder
-   */
-  public Encoder getRightEncoder() {
-    return m_rightEncoder;
-  }
-
-  /**
    * Sets the max output of the drive. Useful for scaling the drive to drive more slowly.
    *
    * @param maxOutput the maximum output to which the drive will be constrained
@@ -104,4 +87,12 @@
   public void setMaxOutput(double maxOutput) {
     m_drive.setMaxOutput(maxOutput);
   }
+
+  @Override
+  public void initSendable(SendableBuilder builder) {
+    super.initSendable(builder);
+    // Publish encoder distances to telemetry.
+    builder.addDoubleProperty("leftDistance", m_leftEncoder::getDistance, null);
+    builder.addDoubleProperty("rightDistance", m_rightEncoder::getDistance, null);
+  }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/HatchSubsystem.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/HatchSubsystem.java
index 3526d17..fcef9e2 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/HatchSubsystem.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/HatchSubsystem.java
@@ -7,6 +7,7 @@
 import static edu.wpi.first.wpilibj.DoubleSolenoid.Value.kForward;
 import static edu.wpi.first.wpilibj.DoubleSolenoid.Value.kReverse;
 
+import edu.wpi.first.util.sendable.SendableBuilder;
 import edu.wpi.first.wpilibj.DoubleSolenoid;
 import edu.wpi.first.wpilibj.PneumaticsModuleType;
 import edu.wpi.first.wpilibj.examples.hatchbottraditional.Constants.HatchConstants;
@@ -29,4 +30,11 @@
   public void releaseHatch() {
     m_hatchSolenoid.set(kReverse);
   }
+
+  @Override
+  public void initSendable(SendableBuilder builder) {
+    super.initSendable(builder);
+    // Publish the solenoid state to telemetry.
+    builder.addBooleanProperty("extended", () -> m_hatchSolenoid.get() == kForward, null);
+  }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/i2ccommunication/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/i2ccommunication/Robot.java
index 9c1e623..cc1af03 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/i2ccommunication/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/i2ccommunication/Robot.java
@@ -6,16 +6,19 @@
 
 import edu.wpi.first.wpilibj.DriverStation;
 import edu.wpi.first.wpilibj.I2C;
+import edu.wpi.first.wpilibj.I2C.Port;
 import edu.wpi.first.wpilibj.TimedRobot;
+import java.util.Optional;
 
 /**
  * This is a sample program demonstrating how to communicate to a light controller from the robot
  * code using the roboRIO's I2C port.
  */
 public class Robot extends TimedRobot {
-  private static int kDeviceAddress = 4;
+  static final Port kPort = Port.kOnboard;
+  private static final int kDeviceAddress = 4;
 
-  private static I2C m_arduino = new I2C(I2C.Port.kOnboard, kDeviceAddress);
+  private final I2C m_arduino = new I2C(kPort, kDeviceAddress);
 
   private void writeString(String input) {
     // Creates a char array from the input string
@@ -30,7 +33,7 @@
     }
 
     // Writes bytes over I2C
-    m_arduino.transaction(data, data.length, null, 0);
+    m_arduino.transaction(data, data.length, new byte[] {}, 0);
   }
 
   @Override
@@ -46,12 +49,25 @@
     // alliance, enabled in teleop mode, with 43 seconds left in the match.
     StringBuilder stateMessage = new StringBuilder(6);
 
+    String allianceString = "U";
+    Optional<DriverStation.Alliance> alliance = DriverStation.getAlliance();
+    if (alliance.isPresent()) {
+      allianceString = alliance.get() == DriverStation.Alliance.Red ? "R" : "B";
+    }
+
     stateMessage
-        .append(DriverStation.getAlliance() == DriverStation.Alliance.Red ? "R" : "B")
+        .append(allianceString)
         .append(DriverStation.isEnabled() ? "E" : "D")
         .append(DriverStation.isAutonomous() ? "A" : "T")
         .append(String.format("%03d", (int) DriverStation.getMatchTime()));
 
     writeString(stateMessage.toString());
   }
+
+  /** Close all resources. */
+  @Override
+  public void close() {
+    m_arduino.close();
+    super.close();
+  }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java
index 8849598..15e813a 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java
@@ -128,12 +128,16 @@
    * @param rot Angular rate of the robot.
    * @param fieldRelative Whether the provided x and y speeds are relative to the field.
    */
-  public void drive(double xSpeed, double ySpeed, double rot, boolean fieldRelative) {
+  public void drive(
+      double xSpeed, double ySpeed, double rot, boolean fieldRelative, double periodSeconds) {
     var mecanumDriveWheelSpeeds =
         m_kinematics.toWheelSpeeds(
-            fieldRelative
-                ? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeed, ySpeed, rot, m_gyro.getRotation2d())
-                : new ChassisSpeeds(xSpeed, ySpeed, rot));
+            ChassisSpeeds.discretize(
+                fieldRelative
+                    ? ChassisSpeeds.fromFieldRelativeSpeeds(
+                        xSpeed, ySpeed, rot, m_gyro.getRotation2d())
+                    : new ChassisSpeeds(xSpeed, ySpeed, rot),
+                periodSeconds));
     mecanumDriveWheelSpeeds.desaturate(kMaxSpeed);
     setSpeeds(mecanumDriveWheelSpeeds);
   }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Robot.java
index e1d9a08..77f55d6 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Robot.java
@@ -44,6 +44,6 @@
     // the right by default.
     final var rot = -m_rotLimiter.calculate(m_controller.getRightX()) * Drivetrain.kMaxAngularSpeed;
 
-    m_mecanum.drive(xSpeed, ySpeed, rot, fieldRelative);
+    m_mecanum.drive(xSpeed, ySpeed, rot, fieldRelative, getPeriod());
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java
index bab80b6..74c088f 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java
@@ -103,7 +103,7 @@
             DriveConstants.kFeedforward,
             DriveConstants.kDriveKinematics,
 
-            // Position contollers
+            // Position controllers
             new PIDController(AutoConstants.kPXController, 0, 0),
             new PIDController(AutoConstants.kPYController, 0, 0),
             new ProfiledPIDController(
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java
index 50a23cd..6d731ba 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java
@@ -13,7 +13,6 @@
 import edu.wpi.first.wpilibj.Encoder;
 import edu.wpi.first.wpilibj.drive.MecanumDrive;
 import edu.wpi.first.wpilibj.examples.mecanumcontrollercommand.Constants.DriveConstants;
-import edu.wpi.first.wpilibj.interfaces.Gyro;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
 
@@ -55,7 +54,7 @@
           DriveConstants.kRearRightEncoderReversed);
 
   // The gyro sensor
-  private final Gyro m_gyro = new ADXRS450_Gyro();
+  private final ADXRS450_Gyro m_gyro = new ADXRS450_Gyro();
 
   // Odometry class for tracking robot pose
   MecanumDriveOdometry m_odometry =
@@ -113,9 +112,9 @@
    */
   public void drive(double xSpeed, double ySpeed, double rot, boolean fieldRelative) {
     if (fieldRelative) {
-      m_drive.driveCartesian(ySpeed, xSpeed, rot, m_gyro.getRotation2d());
+      m_drive.driveCartesian(xSpeed, ySpeed, rot, m_gyro.getRotation2d());
     } else {
-      m_drive.driveCartesian(ySpeed, xSpeed, rot);
+      m_drive.driveCartesian(xSpeed, ySpeed, rot);
     }
   }
 
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java
index c681a86..f376543 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java
@@ -40,7 +40,7 @@
 
   @Override
   public void teleopPeriodic() {
-    // Use the joystick X axis for forward movement, Y axis for lateral
+    // Use the joystick Y axis for forward movement, X axis for lateral
     // movement, and Z axis for rotation.
     m_robotDrive.driveCartesian(-m_stick.getY(), -m_stick.getX(), -m_stick.getZ());
   }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java
index 299fe68..4b643bd 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java
@@ -140,12 +140,16 @@
    * @param rot Angular rate of the robot.
    * @param fieldRelative Whether the provided x and y speeds are relative to the field.
    */
-  public void drive(double xSpeed, double ySpeed, double rot, boolean fieldRelative) {
+  public void drive(
+      double xSpeed, double ySpeed, double rot, boolean fieldRelative, double periodSeconds) {
     var mecanumDriveWheelSpeeds =
         m_kinematics.toWheelSpeeds(
-            fieldRelative
-                ? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeed, ySpeed, rot, m_gyro.getRotation2d())
-                : new ChassisSpeeds(xSpeed, ySpeed, rot));
+            ChassisSpeeds.discretize(
+                fieldRelative
+                    ? ChassisSpeeds.fromFieldRelativeSpeeds(
+                        xSpeed, ySpeed, rot, m_gyro.getRotation2d())
+                    : new ChassisSpeeds(xSpeed, ySpeed, rot),
+                periodSeconds));
     mecanumDriveWheelSpeeds.desaturate(kMaxSpeed);
     setSpeeds(mecanumDriveWheelSpeeds);
   }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Robot.java
index fb22388..581f744 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Robot.java
@@ -44,6 +44,6 @@
     // the right by default.
     final var rot = -m_rotLimiter.calculate(m_controller.getRightX()) * Drivetrain.kMaxAngularSpeed;
 
-    m_mecanum.drive(xSpeed, ySpeed, rot, fieldRelative);
+    m_mecanum.drive(xSpeed, ySpeed, rot, fieldRelative, getPeriod());
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/potentiometerpid/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/potentiometerpid/Robot.java
index a1bb38f..65549bb 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/potentiometerpid/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/potentiometerpid/Robot.java
@@ -5,10 +5,9 @@
 package edu.wpi.first.wpilibj.examples.potentiometerpid;
 
 import edu.wpi.first.math.controller.PIDController;
-import edu.wpi.first.wpilibj.AnalogInput;
+import edu.wpi.first.wpilibj.AnalogPotentiometer;
 import edu.wpi.first.wpilibj.Joystick;
 import edu.wpi.first.wpilibj.TimedRobot;
-import edu.wpi.first.wpilibj.motorcontrol.MotorController;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
 
 /**
@@ -16,53 +15,65 @@
  * reach and maintain position setpoints on an elevator mechanism.
  */
 public class Robot extends TimedRobot {
-  private static final int kPotChannel = 1;
-  private static final int kMotorChannel = 7;
-  private static final int kJoystickChannel = 0;
+  static final int kPotChannel = 1;
+  static final int kMotorChannel = 7;
+  static final int kJoystickChannel = 3;
 
-  // bottom, middle, and top elevator setpoints
-  private static final double[] kSetPoints = {1.0, 2.6, 4.3};
+  // The elevator can move 1.5 meters from top to bottom
+  static final double kFullHeightMeters = 1.5;
 
-  // proportional, integral, and derivative speed constants; motor inverted
+  // Bottom, middle, and top elevator setpoints
+  static final double[] kSetpointsMeters = {0.2, 0.8, 1.4};
+
+  // proportional, integral, and derivative speed constants
   // DANGER: when tuning PID constants, high/inappropriate values for kP, kI,
   // and kD may cause dangerous, uncontrollable, or undesired behavior!
-  // these may need to be positive for a non-inverted motor
-  private static final double kP = -5.0;
-  private static final double kI = -0.02;
-  private static final double kD = -2.0;
+  private static final double kP = 0.7;
+  private static final double kI = 0.35;
+  private static final double kD = 0.25;
 
-  private PIDController m_pidController;
-  private AnalogInput m_potentiometer;
-  private MotorController m_elevatorMotor;
-  private Joystick m_joystick;
+  private final PIDController m_pidController = new PIDController(kP, kI, kD);
+  // Scaling is handled internally
+  private final AnalogPotentiometer m_potentiometer =
+      new AnalogPotentiometer(kPotChannel, kFullHeightMeters);
+  private final PWMSparkMax m_elevatorMotor = new PWMSparkMax(kMotorChannel);
+  private final Joystick m_joystick = new Joystick(kJoystickChannel);
 
   private int m_index;
-  private boolean m_previousButtonValue;
 
   @Override
-  public void robotInit() {
-    m_potentiometer = new AnalogInput(kPotChannel);
-    m_elevatorMotor = new PWMSparkMax(kMotorChannel);
-    m_joystick = new Joystick(kJoystickChannel);
-
-    m_pidController = new PIDController(kP, kI, kD);
-    m_pidController.setSetpoint(kSetPoints[m_index]);
+  public void teleopInit() {
+    // Move to the bottom setpoint when teleop starts
+    m_index = 0;
+    m_pidController.setSetpoint(kSetpointsMeters[m_index]);
   }
 
   @Override
   public void teleopPeriodic() {
+    // Read from the sensor
+    double position = m_potentiometer.get();
+
     // Run the PID Controller
-    double pidOut = m_pidController.calculate(m_potentiometer.getAverageVoltage());
+    double pidOut = m_pidController.calculate(position);
+
+    // Apply PID output
     m_elevatorMotor.set(pidOut);
 
-    // when the button is pressed once, the selected elevator setpoint
-    // is incremented
-    boolean currentButtonValue = m_joystick.getTrigger();
-    if (currentButtonValue && !m_previousButtonValue) {
+    // when the button is pressed once, the selected elevator setpoint is incremented
+    if (m_joystick.getTriggerPressed()) {
       // index of the elevator setpoint wraps around.
-      m_index = (m_index + 1) % kSetPoints.length;
-      m_pidController.setSetpoint(kSetPoints[m_index]);
+      m_index = (m_index + 1) % kSetpointsMeters.length;
+      System.out.println("m_index = " + m_index);
+      m_pidController.setSetpoint(kSetpointsMeters[m_index]);
     }
-    m_previousButtonValue = currentButtonValue;
+  }
+
+  @Override
+  public void close() {
+    m_elevatorMotor.close();
+    m_potentiometer.close();
+    m_pidController.close();
+    m_index = 0;
+    super.close();
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java
index 90625ce..288baed 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java
@@ -11,7 +11,6 @@
 import edu.wpi.first.wpilibj.Encoder;
 import edu.wpi.first.wpilibj.drive.DifferentialDrive;
 import edu.wpi.first.wpilibj.examples.ramsetecommand.Constants.DriveConstants;
-import edu.wpi.first.wpilibj.interfaces.Gyro;
 import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
@@ -47,7 +46,7 @@
           DriveConstants.kRightEncoderReversed);
 
   // The gyro sensor
-  private final Gyro m_gyro = new ADXRS450_Gyro();
+  private final ADXRS450_Gyro m_gyro = new ADXRS450_Gyro();
 
   // Odometry class for tracking robot pose
   private final DifferentialDriveOdometry m_odometry;
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/Constants.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/Constants.java
index 6ee700c..5809616 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/Constants.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/Constants.java
@@ -63,7 +63,7 @@
 
   public static final class IntakeConstants {
     public static final int kMotorPort = 6;
-    public static final int[] kSolenoidPorts = {0, 1};
+    public static final int[] kSolenoidPorts = {2, 3};
   }
 
   public static final class StorageConstants {
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/RapidReactCommandBot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/RapidReactCommandBot.java
index 879f2c2..12d7f66 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/RapidReactCommandBot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/RapidReactCommandBot.java
@@ -11,9 +11,10 @@
 import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.ShooterConstants;
 import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.subsystems.Drive;
 import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.subsystems.Intake;
+import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.subsystems.Pneumatics;
 import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.subsystems.Shooter;
 import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.subsystems.Storage;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.button.CommandXboxController;
 import edu.wpi.first.wpilibj2.command.button.Trigger;
 
@@ -29,6 +30,7 @@
   private final Intake m_intake = new Intake();
   private final Storage m_storage = new Storage();
   private final Shooter m_shooter = new Shooter();
+  private final Pneumatics m_pneumatics = new Pneumatics();
 
   // The driver's controller
   CommandXboxController m_driverController =
@@ -69,6 +71,9 @@
                     m_storage.runCommand())
                 // Since we composed this inline we should give it a name
                 .withName("Shoot"));
+
+    // Toggle compressor with the Start button
+    m_driverController.start().toggleOnTrue(m_pneumatics.disableCompressorCommand());
   }
 
   /**
@@ -76,7 +81,7 @@
    *
    * <p>Scheduled during {@link Robot#autonomousInit()}.
    */
-  public CommandBase getAutonomousCommand() {
+  public Command getAutonomousCommand() {
     // Drive forward for 2 meters at half speed with a 3 second timeout
     return m_drive
         .driveDistanceCommand(AutoConstants.kDriveDistanceMeters, AutoConstants.kDriveSpeed)
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Drive.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Drive.java
index eaaa8ad..736ff44 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Drive.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Drive.java
@@ -9,7 +9,7 @@
 import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.DriveConstants;
 import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
 import java.util.function.DoubleSupplier;
 
@@ -61,7 +61,7 @@
    * @param fwd the commanded forward movement
    * @param rot the commanded rotation
    */
-  public CommandBase arcadeDriveCommand(DoubleSupplier fwd, DoubleSupplier rot) {
+  public Command arcadeDriveCommand(DoubleSupplier fwd, DoubleSupplier rot) {
     // A split-stick arcade command, with forward/backward controlled by the left
     // hand, and turning controlled by the right.
     return run(() -> m_drive.arcadeDrive(fwd.getAsDouble(), rot.getAsDouble()))
@@ -74,7 +74,7 @@
    * @param distanceMeters The distance to drive forward in meters
    * @param speed The fraction of max speed at which to drive
    */
-  public CommandBase driveDistanceCommand(double distanceMeters, double speed) {
+  public Command driveDistanceCommand(double distanceMeters, double speed) {
     return runOnce(
             () -> {
               // Reset encoders at the start of the command
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Intake.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Intake.java
index 533f596..0f242df 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Intake.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Intake.java
@@ -9,26 +9,28 @@
 import edu.wpi.first.wpilibj.DoubleSolenoid;
 import edu.wpi.first.wpilibj.PneumaticsModuleType;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
 
 public class Intake extends SubsystemBase {
   private final PWMSparkMax m_motor = new PWMSparkMax(IntakeConstants.kMotorPort);
+
+  // Double solenoid connected to two channels of a PCM with the default CAN ID
   private final DoubleSolenoid m_pistons =
       new DoubleSolenoid(
-          PneumaticsModuleType.REVPH,
+          PneumaticsModuleType.CTREPCM,
           IntakeConstants.kSolenoidPorts[0],
           IntakeConstants.kSolenoidPorts[1]);
 
   /** Returns a command that deploys the intake, and then runs the intake motor indefinitely. */
-  public CommandBase intakeCommand() {
+  public Command intakeCommand() {
     return runOnce(() -> m_pistons.set(DoubleSolenoid.Value.kForward))
         .andThen(run(() -> m_motor.set(1.0)))
         .withName("Intake");
   }
 
   /** Returns a command that turns off and retracts the intake. */
-  public CommandBase retractCommand() {
+  public Command retractCommand() {
     return runOnce(
             () -> {
               m_motor.disable();
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Pneumatics.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Pneumatics.java
new file mode 100644
index 0000000..69ec023
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Pneumatics.java
@@ -0,0 +1,65 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.rapidreactcommandbot.subsystems;
+
+import edu.wpi.first.wpilibj.AnalogPotentiometer;
+import edu.wpi.first.wpilibj.Compressor;
+import edu.wpi.first.wpilibj.PneumaticsModuleType;
+import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
+import edu.wpi.first.wpilibj2.command.Command;
+import edu.wpi.first.wpilibj2.command.SubsystemBase;
+
+/** Subsystem for managing the compressor, pressure sensor, etc. */
+public class Pneumatics extends SubsystemBase {
+  // External analog pressure sensor
+  // product-specific voltage->pressure conversion, see product manual
+  // in this case, 250(V/5)-25
+  // the scale parameter in the AnalogPotentiometer constructor is scaled from 1 instead of 5,
+  // so if r is the raw AnalogPotentiometer output, the pressure is 250r-25
+  static final double kScale = 250;
+  static final double kOffset = -25;
+  private final AnalogPotentiometer m_pressureTransducer =
+      new AnalogPotentiometer(/* the AnalogIn port*/ 2, kScale, kOffset);
+
+  // Compressor connected to a PCM with a default CAN ID (0)
+  private final Compressor m_compressor = new Compressor(PneumaticsModuleType.CTREPCM);
+
+  public Pneumatics() {
+    var tab = Shuffleboard.getTab("Pneumatics");
+    tab.addDouble("External Pressure [PSI]", this::getPressure);
+  }
+
+  /**
+   * Query the analog pressure sensor.
+   *
+   * @return the measured pressure, in PSI
+   */
+  private double getPressure() {
+    // Get the pressure (in PSI) from an analog pressure sensor connected to the RIO.
+    return m_pressureTransducer.get();
+  }
+
+  /**
+   * Disable the compressor closed-loop for as long as the command runs.
+   *
+   * <p>Structured this way as the compressor is enabled by default.
+   *
+   * @return command
+   */
+  public Command disableCompressorCommand() {
+    return startEnd(
+            () -> {
+              // Disable closed-loop mode on the compressor.
+              m_compressor.disable();
+            },
+            () -> {
+              // Enable closed-loop mode based on the digital pressure switch connected to the
+              // PCM/PH.
+              // The switch is open when the pressure is over ~120 PSI.
+              m_compressor.enableDigital();
+            })
+        .withName("Compressor Disabled");
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Shooter.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Shooter.java
index 9191fb6..0e7f9b5 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Shooter.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Shooter.java
@@ -12,7 +12,7 @@
 import edu.wpi.first.wpilibj.Encoder;
 import edu.wpi.first.wpilibj.examples.frisbeebot.Constants.ShooterConstants;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
 
 public class Shooter extends SubsystemBase {
@@ -50,7 +50,7 @@
    *
    * @param setpointRotationsPerSecond The desired shooter velocity
    */
-  public CommandBase shootCommand(double setpointRotationsPerSecond) {
+  public Command shootCommand(double setpointRotationsPerSecond) {
     return parallel(
             // Run the shooter flywheel at the desired setpoint using feedforward and feedback
             run(
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Storage.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Storage.java
index 9b45d66..dac61a4 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Storage.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Storage.java
@@ -8,7 +8,7 @@
 
 import edu.wpi.first.wpilibj.DigitalInput;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
 
 public class Storage extends SubsystemBase {
@@ -27,7 +27,7 @@
   }
 
   /** Returns a command that runs the storage motor indefinitely. */
-  public CommandBase runCommand() {
+  public Command runCommand() {
     return run(() -> m_motor.set(1)).withName("run");
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/RobotContainer.java
index a9dcacd..39c1a36 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/RobotContainer.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/RobotContainer.java
@@ -11,8 +11,8 @@
 import edu.wpi.first.wpilibj.examples.romireference.commands.AutonomousDistance;
 import edu.wpi.first.wpilibj.examples.romireference.commands.AutonomousTime;
 import edu.wpi.first.wpilibj.examples.romireference.subsystems.Drivetrain;
-import edu.wpi.first.wpilibj.examples.romireference.subsystems.OnBoardIO;
-import edu.wpi.first.wpilibj.examples.romireference.subsystems.OnBoardIO.ChannelMode;
+import edu.wpi.first.wpilibj.romi.OnBoardIO;
+import edu.wpi.first.wpilibj.romi.OnBoardIO.ChannelMode;
 import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
 import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
 import edu.wpi.first.wpilibj2.command.Command;
@@ -30,7 +30,7 @@
   private final Drivetrain m_drivetrain = new Drivetrain();
   private final OnBoardIO m_onboardIO = new OnBoardIO(ChannelMode.INPUT, ChannelMode.INPUT);
 
-  // Assumes a gamepad plugged into channnel 0
+  // Assumes a gamepad plugged into channel 0
   private final Joystick m_controller = new Joystick(0);
 
   // Create SmartDashboard chooser for autonomous routines
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/ArcadeDrive.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/ArcadeDrive.java
index 4a82b38..295a6f9 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/ArcadeDrive.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/ArcadeDrive.java
@@ -5,10 +5,10 @@
 package edu.wpi.first.wpilibj.examples.romireference.commands;
 
 import edu.wpi.first.wpilibj.examples.romireference.subsystems.Drivetrain;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import java.util.function.Supplier;
 
-public class ArcadeDrive extends CommandBase {
+public class ArcadeDrive extends Command {
   private final Drivetrain m_drivetrain;
   private final Supplier<Double> m_xaxisSpeedSupplier;
   private final Supplier<Double> m_zaxisRotateSupplier;
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/DriveDistance.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/DriveDistance.java
index f316ce6..d9c306b 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/DriveDistance.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/DriveDistance.java
@@ -5,9 +5,9 @@
 package edu.wpi.first.wpilibj.examples.romireference.commands;
 
 import edu.wpi.first.wpilibj.examples.romireference.subsystems.Drivetrain;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
-public class DriveDistance extends CommandBase {
+public class DriveDistance extends Command {
   private final Drivetrain m_drive;
   private final double m_distance;
   private final double m_speed;
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/DriveTime.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/DriveTime.java
index 80636f0..7fac2b8 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/DriveTime.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/DriveTime.java
@@ -5,9 +5,9 @@
 package edu.wpi.first.wpilibj.examples.romireference.commands;
 
 import edu.wpi.first.wpilibj.examples.romireference.subsystems.Drivetrain;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
-public class DriveTime extends CommandBase {
+public class DriveTime extends Command {
   private final double m_duration;
   private final double m_speed;
   private final Drivetrain m_drive;
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/TurnDegrees.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/TurnDegrees.java
index 78ffa6c..a438f01 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/TurnDegrees.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/TurnDegrees.java
@@ -5,9 +5,9 @@
 package edu.wpi.first.wpilibj.examples.romireference.commands;
 
 import edu.wpi.first.wpilibj.examples.romireference.subsystems.Drivetrain;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
-public class TurnDegrees extends CommandBase {
+public class TurnDegrees extends Command {
   private final Drivetrain m_drive;
   private final double m_degrees;
   private final double m_speed;
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/TurnTime.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/TurnTime.java
index 10341f9..f3a6aa7 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/TurnTime.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/commands/TurnTime.java
@@ -5,13 +5,13 @@
 package edu.wpi.first.wpilibj.examples.romireference.commands;
 
 import edu.wpi.first.wpilibj.examples.romireference.subsystems.Drivetrain;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
 /*
  * Creates a new TurnTime command. This command will turn your robot for a
  * desired rotational speed and time.
  */
-public class TurnTime extends CommandBase {
+public class TurnTime extends Command {
   private final double m_duration;
   private final double m_rotationalSpeed;
   private final Drivetrain m_drive;
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/sensors/RomiGyro.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/sensors/RomiGyro.java
deleted file mode 100644
index a0ee53d..0000000
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/sensors/RomiGyro.java
+++ /dev/null
@@ -1,132 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj.examples.romireference.sensors;
-
-import edu.wpi.first.hal.SimDevice;
-import edu.wpi.first.hal.SimDevice.Direction;
-import edu.wpi.first.hal.SimDouble;
-
-public class RomiGyro {
-  private final SimDouble m_simRateX;
-  private final SimDouble m_simRateY;
-  private final SimDouble m_simRateZ;
-  private final SimDouble m_simAngleX;
-  private final SimDouble m_simAngleY;
-  private final SimDouble m_simAngleZ;
-
-  private double m_angleXOffset;
-  private double m_angleYOffset;
-  private double m_angleZOffset;
-
-  /** Create a new RomiGyro. */
-  public RomiGyro() {
-    SimDevice gyroSimDevice = SimDevice.create("Gyro:RomiGyro");
-    if (gyroSimDevice != null) {
-      gyroSimDevice.createBoolean("init", Direction.kOutput, true);
-      m_simRateX = gyroSimDevice.createDouble("rate_x", Direction.kInput, 0.0);
-      m_simRateY = gyroSimDevice.createDouble("rate_y", Direction.kInput, 0.0);
-      m_simRateZ = gyroSimDevice.createDouble("rate_z", Direction.kInput, 0.0);
-
-      m_simAngleX = gyroSimDevice.createDouble("angle_x", Direction.kInput, 0.0);
-      m_simAngleY = gyroSimDevice.createDouble("angle_y", Direction.kInput, 0.0);
-      m_simAngleZ = gyroSimDevice.createDouble("angle_z", Direction.kInput, 0.0);
-    } else {
-      m_simRateX = null;
-      m_simRateY = null;
-      m_simRateZ = null;
-
-      m_simAngleX = null;
-      m_simAngleY = null;
-      m_simAngleZ = null;
-    }
-  }
-
-  /**
-   * Get the rate of turn in degrees-per-second around the X-axis.
-   *
-   * @return rate of turn in degrees-per-second
-   */
-  public double getRateX() {
-    if (m_simRateX != null) {
-      return m_simRateX.get();
-    }
-
-    return 0.0;
-  }
-
-  /**
-   * Get the rate of turn in degrees-per-second around the Y-axis.
-   *
-   * @return rate of turn in degrees-per-second
-   */
-  public double getRateY() {
-    if (m_simRateY != null) {
-      return m_simRateY.get();
-    }
-
-    return 0.0;
-  }
-
-  /**
-   * Get the rate of turn in degrees-per-second around the Z-axis.
-   *
-   * @return rate of turn in degrees-per-second
-   */
-  public double getRateZ() {
-    if (m_simRateZ != null) {
-      return m_simRateZ.get();
-    }
-
-    return 0.0;
-  }
-
-  /**
-   * Get the currently reported angle around the X-axis.
-   *
-   * @return current angle around X-axis in degrees
-   */
-  public double getAngleX() {
-    if (m_simAngleX != null) {
-      return m_simAngleX.get() - m_angleXOffset;
-    }
-
-    return 0.0;
-  }
-
-  /**
-   * Get the currently reported angle around the X-axis.
-   *
-   * @return current angle around Y-axis in degrees
-   */
-  public double getAngleY() {
-    if (m_simAngleY != null) {
-      return m_simAngleY.get() - m_angleYOffset;
-    }
-
-    return 0.0;
-  }
-
-  /**
-   * Get the currently reported angle around the Z-axis.
-   *
-   * @return current angle around Z-axis in degrees
-   */
-  public double getAngleZ() {
-    if (m_simAngleZ != null) {
-      return m_simAngleZ.get() - m_angleZOffset;
-    }
-
-    return 0.0;
-  }
-
-  /** Reset the gyro angles to 0. */
-  public void reset() {
-    if (m_simAngleX != null) {
-      m_angleXOffset = m_simAngleX.get();
-      m_angleYOffset = m_simAngleY.get();
-      m_angleZOffset = m_simAngleZ.get();
-    }
-  }
-}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java
index a7c8432..d805590 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java
@@ -7,8 +7,8 @@
 import edu.wpi.first.wpilibj.BuiltInAccelerometer;
 import edu.wpi.first.wpilibj.Encoder;
 import edu.wpi.first.wpilibj.drive.DifferentialDrive;
-import edu.wpi.first.wpilibj.examples.romireference.sensors.RomiGyro;
 import edu.wpi.first.wpilibj.motorcontrol.Spark;
+import edu.wpi.first.wpilibj.romi.RomiGyro;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
 
 public class Drivetrain extends SubsystemBase {
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/OnBoardIO.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/OnBoardIO.java
deleted file mode 100644
index 0199f6a..0000000
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/OnBoardIO.java
+++ /dev/null
@@ -1,132 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj.examples.romireference.subsystems;
-
-import edu.wpi.first.wpilibj.DigitalInput;
-import edu.wpi.first.wpilibj.DigitalOutput;
-import edu.wpi.first.wpilibj.DriverStation;
-import edu.wpi.first.wpilibj.Timer;
-import edu.wpi.first.wpilibj2.command.SubsystemBase;
-
-/**
- * This class represents the onboard IO of the Romi reference robot. This includes the pushbuttons
- * and LEDs.
- *
- * <p>DIO 0 - Button A (input only) DIO 1 - Button B (input) or Green LED (output) DIO 2 - Button C
- * (input) or Red LED (output) DIO 3 - Yellow LED (output only)
- */
-public class OnBoardIO extends SubsystemBase {
-  private final DigitalInput m_buttonA = new DigitalInput(0);
-  private final DigitalOutput m_yellowLed = new DigitalOutput(3);
-
-  // DIO 1
-  private final DigitalInput m_buttonB;
-  private final DigitalOutput m_greenLed;
-
-  // DIO 2
-  private final DigitalInput m_buttonC;
-  private final DigitalOutput m_redLed;
-
-  private static final double MESSAGE_INTERVAL = 1.0;
-  private double m_nextMessageTime;
-
-  public enum ChannelMode {
-    INPUT,
-    OUTPUT
-  }
-
-  /**
-   * Constructor.
-   *
-   * @param dio1 Mode for DIO 1 (input = Button B, output = green LED)
-   * @param dio2 Mode for DIO 2 (input = Button C, output = red LED)
-   */
-  public OnBoardIO(ChannelMode dio1, ChannelMode dio2) {
-    if (dio1 == ChannelMode.INPUT) {
-      m_buttonB = new DigitalInput(1);
-      m_greenLed = null;
-    } else {
-      m_buttonB = null;
-      m_greenLed = new DigitalOutput(1);
-    }
-
-    if (dio2 == ChannelMode.INPUT) {
-      m_buttonC = new DigitalInput(2);
-      m_redLed = null;
-    } else {
-      m_buttonC = null;
-      m_redLed = new DigitalOutput(2);
-    }
-  }
-
-  /** Gets if the A button is pressed. */
-  public boolean getButtonAPressed() {
-    return m_buttonA.get();
-  }
-
-  /** Gets if the B button is pressed. */
-  public boolean getButtonBPressed() {
-    if (m_buttonB != null) {
-      return m_buttonB.get();
-    }
-
-    double currentTime = Timer.getFPGATimestamp();
-    if (currentTime > m_nextMessageTime) {
-      DriverStation.reportError("Button B was not configured", true);
-      m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
-    }
-    return false;
-  }
-
-  /** Gets if the C button is pressed. */
-  public boolean getButtonCPressed() {
-    if (m_buttonC != null) {
-      return m_buttonC.get();
-    }
-
-    double currentTime = Timer.getFPGATimestamp();
-    if (currentTime > m_nextMessageTime) {
-      DriverStation.reportError("Button C was not configured", true);
-      m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
-    }
-    return false;
-  }
-
-  /** Sets the green LED. */
-  public void setGreenLed(boolean value) {
-    if (m_greenLed != null) {
-      m_greenLed.set(value);
-    } else {
-      double currentTime = Timer.getFPGATimestamp();
-      if (currentTime > m_nextMessageTime) {
-        DriverStation.reportError("Green LED was not configured", true);
-        m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
-      }
-    }
-  }
-
-  /** Sets the red LED. */
-  public void setRedLed(boolean value) {
-    if (m_redLed != null) {
-      m_redLed.set(value);
-    } else {
-      double currentTime = Timer.getFPGATimestamp();
-      if (currentTime > m_nextMessageTime) {
-        DriverStation.reportError("Red LED was not configured", true);
-        m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
-      }
-    }
-  }
-
-  /** Sets the yellow LED. */
-  public void setYellowLed(boolean value) {
-    m_yellowLed.set(value);
-  }
-
-  @Override
-  public void periodic() {
-    // This method will be called once per scheduler run
-  }
-}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/Constants.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/Constants.java
deleted file mode 100644
index 38217cd..0000000
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/Constants.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj.examples.schedulereventlogging;
-
-/**
- * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean
- * constants. This class should not be used for any other purpose. All constants should be declared
- * globally (i.e. public static). Do not put anything functional in this class.
- *
- * <p>It is advised to statically import this class (or one of its inner classes) wherever the
- * constants are needed, to reduce verbosity.
- */
-public final class Constants {
-  /**
-   * Example of an inner class. One can "import static [...].Constants.OIConstants.*" to gain access
-   * to the constants contained within without having to preface the names with the class, greatly
-   * reducing the amount of text required.
-   */
-  public static final class OIConstants {
-    // Example: the port of the driver's controller
-    public static final int kDriverControllerPort = 0;
-  }
-}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/Main.java
deleted file mode 100644
index 8e7a679..0000000
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/Main.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj.examples.schedulereventlogging;
-
-import edu.wpi.first.wpilibj.RobotBase;
-
-/**
- * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
- * you are doing, do not modify this file except to change the parameter class to the startRobot
- * call.
- */
-public final class Main {
-  private Main() {}
-
-  /**
-   * Main initialization function. Do not perform any initialization here.
-   *
-   * <p>If you change your main robot class, change the parameter type.
-   */
-  public static void main(String... args) {
-    RobotBase.startRobot(Robot::new);
-  }
-}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/Robot.java
deleted file mode 100644
index f53d587..0000000
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/Robot.java
+++ /dev/null
@@ -1,102 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj.examples.schedulereventlogging;
-
-import edu.wpi.first.wpilibj.TimedRobot;
-import edu.wpi.first.wpilibj2.command.Command;
-import edu.wpi.first.wpilibj2.command.CommandScheduler;
-
-/**
- * The VM is configured to automatically run this class, and to call the functions corresponding to
- * each mode, as described in the TimedRobot documentation. If you change the name of this class or
- * the package after creating this project, you must also update the build.gradle file in the
- * project.
- */
-public class Robot extends TimedRobot {
-  private Command m_autonomousCommand;
-
-  private RobotContainer m_robotContainer;
-
-  /**
-   * This function is run when the robot is first started up and should be used for any
-   * initialization code.
-   */
-  @Override
-  public void robotInit() {
-    // Instantiate our RobotContainer.  This will perform all our button bindings, and put our
-    // autonomous chooser on the dashboard.
-    m_robotContainer = new RobotContainer();
-  }
-
-  /**
-   * This function is called every 20 ms, no matter the mode. Use this for items like diagnostics
-   * that you want ran during disabled, autonomous, teleoperated and test.
-   *
-   * <p>This runs after the mode specific periodic functions, but before LiveWindow and
-   * SmartDashboard integrated updating.
-   */
-  @Override
-  public void robotPeriodic() {
-    // Runs the Scheduler.  This is responsible for polling buttons, adding newly-scheduled
-    // commands, running already-scheduled commands, removing finished or interrupted commands,
-    // and running subsystem periodic() methods.  This must be called from the robot's periodic
-    // block in order for anything in the Command-based framework to work.
-    CommandScheduler.getInstance().run();
-  }
-
-  /** This function is called once each time the robot enters Disabled mode. */
-  @Override
-  public void disabledInit() {}
-
-  @Override
-  public void disabledPeriodic() {}
-
-  /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */
-  @Override
-  public void autonomousInit() {
-    m_autonomousCommand = m_robotContainer.getAutonomousCommand();
-
-    /*
-     * String autoSelected = SmartDashboard.getString("Auto Selector",
-     * "Default"); switch(autoSelected) { case "My Auto": autonomousCommand
-     * = new MyAutoCommand(); break; case "Default Auto": default:
-     * autonomousCommand = new ExampleCommand(); break; }
-     */
-
-    // schedule the autonomous command (example)
-    if (m_autonomousCommand != null) {
-      m_autonomousCommand.schedule();
-    }
-  }
-
-  /** This function is called periodically during autonomous. */
-  @Override
-  public void autonomousPeriodic() {}
-
-  @Override
-  public void teleopInit() {
-    // This makes sure that the autonomous stops running when
-    // teleop starts running. If you want the autonomous to
-    // continue until interrupted by another command, remove
-    // this line or comment it out.
-    if (m_autonomousCommand != null) {
-      m_autonomousCommand.cancel();
-    }
-  }
-
-  /** This function is called periodically during operator control. */
-  @Override
-  public void teleopPeriodic() {}
-
-  @Override
-  public void testInit() {
-    // Cancels all running commands at the start of test mode.
-    CommandScheduler.getInstance().cancelAll();
-  }
-
-  /** This function is called periodically during test mode. */
-  @Override
-  public void testPeriodic() {}
-}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/RobotContainer.java
deleted file mode 100644
index fcb467c..0000000
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/schedulereventlogging/RobotContainer.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// 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.
-
-package edu.wpi.first.wpilibj.examples.schedulereventlogging;
-
-import static edu.wpi.first.wpilibj.XboxController.Button;
-
-import edu.wpi.first.wpilibj.GenericHID;
-import edu.wpi.first.wpilibj.XboxController;
-import edu.wpi.first.wpilibj.examples.schedulereventlogging.Constants.OIConstants;
-import edu.wpi.first.wpilibj.shuffleboard.EventImportance;
-import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
-import edu.wpi.first.wpilibj2.command.Command;
-import edu.wpi.first.wpilibj2.command.CommandBase;
-import edu.wpi.first.wpilibj2.command.CommandScheduler;
-import edu.wpi.first.wpilibj2.command.InstantCommand;
-import edu.wpi.first.wpilibj2.command.WaitCommand;
-import edu.wpi.first.wpilibj2.command.button.JoystickButton;
-
-/**
- * This class is where the bulk of the robot should be declared. Since Command-based is a
- * "declarative" paradigm, very little robot logic should actually be handled in the {@link Robot}
- * periodic methods (other than the scheduler calls). Instead, the structure of the robot (including
- * subsystems, commands, and button mappings) should be declared here.
- */
-public class RobotContainer {
-  // The driver's controller
-  private final XboxController m_driverController =
-      new XboxController(OIConstants.kDriverControllerPort);
-
-  // A few commands that do nothing, but will demonstrate the scheduler functionality
-  private final CommandBase m_instantCommand1 = new InstantCommand();
-  private final CommandBase m_instantCommand2 = new InstantCommand();
-  private final CommandBase m_waitCommand = new WaitCommand(5);
-
-  /** The container for the robot. Contains subsystems, OI devices, and commands. */
-  public RobotContainer() {
-    // Set names of commands
-    m_instantCommand1.setName("Instant Command 1");
-    m_instantCommand2.setName("Instant Command 2");
-    m_waitCommand.setName("Wait 5 Seconds Command");
-
-    // Set the scheduler to log Shuffleboard events for command initialize, interrupt, finish
-    CommandScheduler.getInstance()
-        .onCommandInitialize(
-            command ->
-                Shuffleboard.addEventMarker(
-                    "Command initialized", command.getName(), EventImportance.kNormal));
-    CommandScheduler.getInstance()
-        .onCommandInterrupt(
-            command ->
-                Shuffleboard.addEventMarker(
-                    "Command interrupted", command.getName(), EventImportance.kNormal));
-    CommandScheduler.getInstance()
-        .onCommandFinish(
-            command ->
-                Shuffleboard.addEventMarker(
-                    "Command finished", command.getName(), EventImportance.kNormal));
-
-    // Configure the button bindings
-    configureButtonBindings();
-  }
-
-  /**
-   * Use this method to define your button->command mappings. Buttons can be created by
-   * instantiating a {@link GenericHID} or one of its subclasses ({@link
-   * edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a {@link
-   * edu.wpi.first.wpilibj2.command.button.JoystickButton}.
-   */
-  private void configureButtonBindings() {
-    // Run instant command 1 when the 'A' button is pressed
-    new JoystickButton(m_driverController, Button.kA.value).onTrue(m_instantCommand1);
-    // Run instant command 2 when the 'X' button is pressed
-    new JoystickButton(m_driverController, Button.kX.value).onTrue(m_instantCommand2);
-    // Run instant command 3 when the 'Y' button is held; release early to interrupt
-    new JoystickButton(m_driverController, Button.kY.value).whileTrue(m_waitCommand);
-  }
-
-  /**
-   * Use this to pass the autonomous command to the main {@link Robot} class.
-   *
-   * @return the command to run in autonomous
-   */
-  public Command getAutonomousCommand() {
-    return new InstantCommand();
-  }
-}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/selectcommand/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/selectcommand/RobotContainer.java
index b6aed81..bd3e677 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/selectcommand/RobotContainer.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/selectcommand/RobotContainer.java
@@ -36,7 +36,7 @@
   // selector does not have to be an enum; it could be any desired type (string, integer,
   // boolean, double...)
   private final Command m_exampleSelectCommand =
-      new SelectCommand(
+      new SelectCommand<>(
           // Maps selector values to commands
           Map.ofEntries(
               Map.entry(CommandSelector.ONE, new PrintCommand("Command one was selected!")),
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java
index f9991f6..47109e0 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java
@@ -10,6 +10,7 @@
 import edu.wpi.first.wpilibj.TimedRobot;
 import edu.wpi.first.wpilibj.drive.DifferentialDrive;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
+import edu.wpi.first.wpilibj.shuffleboard.BuiltInLayouts;
 import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
 import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout;
 import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab;
@@ -41,7 +42,7 @@
     driveBaseTab.add("Tank Drive", m_tankDrive);
     // Put both encoders in a list layout
     ShuffleboardLayout encoders =
-        driveBaseTab.getLayout("List Layout", "Encoders").withPosition(0, 0).withSize(2, 2);
+        driveBaseTab.getLayout("Encoders", BuiltInLayouts.kList).withPosition(0, 0).withSize(2, 2);
     encoders.add("Left Encoder", m_leftEncoder);
     encoders.add("Right Encoder", m_rightEncoder);
 
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Robot.java
index 1beccce..3ad50a4 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Robot.java
@@ -47,8 +47,7 @@
 
   @Override
   public void autonomousInit() {
-    m_timer.reset();
-    m_timer.start();
+    m_timer.restart();
     m_drive.resetOdometry(m_trajectory.getInitialPose());
   }
 
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/solenoid/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/solenoid/Robot.java
index 98fb3b6..d6ae048 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/solenoid/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/solenoid/Robot.java
@@ -4,11 +4,14 @@
 
 package edu.wpi.first.wpilibj.examples.solenoid;
 
+import edu.wpi.first.wpilibj.Compressor;
 import edu.wpi.first.wpilibj.DoubleSolenoid;
 import edu.wpi.first.wpilibj.Joystick;
 import edu.wpi.first.wpilibj.PneumaticsModuleType;
 import edu.wpi.first.wpilibj.Solenoid;
 import edu.wpi.first.wpilibj.TimedRobot;
+import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
+import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab;
 
 /**
  * This is a sample program showing the use of the solenoid classes during operator control. Three
@@ -25,34 +28,111 @@
   private final Joystick m_stick = new Joystick(0);
 
   // Solenoid corresponds to a single solenoid.
-  private final Solenoid m_solenoid = new Solenoid(PneumaticsModuleType.CTREPCM, 0);
+  // In this case, it's connected to channel 0 of a PH with the default CAN ID.
+  private final Solenoid m_solenoid = new Solenoid(PneumaticsModuleType.REVPH, 0);
 
   // DoubleSolenoid corresponds to a double solenoid.
+  // In this case, it's connected to channels 1 and 2 of a PH with the default CAN ID.
   private final DoubleSolenoid m_doubleSolenoid =
-      new DoubleSolenoid(PneumaticsModuleType.CTREPCM, 1, 2);
+      new DoubleSolenoid(PneumaticsModuleType.REVPH, 1, 2);
 
-  private static final int kSolenoidButton = 1;
-  private static final int kDoubleSolenoidForward = 2;
-  private static final int kDoubleSolenoidReverse = 3;
+  // Compressor connected to a PH with a default CAN ID (1)
+  private final Compressor m_compressor = new Compressor(PneumaticsModuleType.REVPH);
 
+  static final int kSolenoidButton = 1;
+  static final int kDoubleSolenoidForwardButton = 2;
+  static final int kDoubleSolenoidReverseButton = 3;
+  static final int kCompressorButton = 4;
+
+  @Override
+  public void robotInit() {
+    // Publish elements to shuffleboard.
+    ShuffleboardTab tab = Shuffleboard.getTab("Pneumatics");
+    tab.add("Single Solenoid", m_solenoid);
+    tab.add("Double Solenoid", m_doubleSolenoid);
+    tab.add("Compressor", m_compressor);
+
+    // Also publish some raw data
+    tab.addDouble(
+        "PH Pressure [PSI]",
+        () -> {
+          // Get the pressure (in PSI) from the analog sensor connected to the PH.
+          // This function is supported only on the PH!
+          // On a PCM, this function will return 0.
+          return m_compressor.getPressure();
+        });
+    tab.addDouble(
+        "Compressor Current",
+        () -> {
+          // Get compressor current draw.
+          return m_compressor.getCurrent();
+        });
+    tab.addBoolean(
+        "Compressor Active",
+        () -> {
+          // Get whether the compressor is active.
+          return m_compressor.isEnabled();
+        });
+    tab.addBoolean(
+        "Pressure Switch",
+        () -> {
+          // Get the digital pressure switch connected to the PCM/PH.
+          // The switch is open when the pressure is over ~120 PSI.
+          return m_compressor.getPressureSwitchValue();
+        });
+  }
+
+  @SuppressWarnings("PMD.UnconditionalIfStatement")
   @Override
   public void teleopPeriodic() {
     /*
      * The output of GetRawButton is true/false depending on whether
      * the button is pressed; Set takes a boolean for whether
-     * to use the default (false) channel or the other (true).
+     * to retract the solenoid (false) or extend it (true).
      */
     m_solenoid.set(m_stick.getRawButton(kSolenoidButton));
 
     /*
-     * In order to set the double solenoid, if just one button
-     * is pressed, set the solenoid to correspond to that button.
-     * If both are pressed, set the solenoid will be set to Forwards.
+     * GetRawButtonPressed will only return true once per press.
+     * If a button is pressed, set the solenoid to the respective channel.
      */
-    if (m_stick.getRawButton(kDoubleSolenoidForward)) {
+    if (m_stick.getRawButtonPressed(kDoubleSolenoidForwardButton)) {
       m_doubleSolenoid.set(DoubleSolenoid.Value.kForward);
-    } else if (m_stick.getRawButton(kDoubleSolenoidReverse)) {
+    } else if (m_stick.getRawButtonPressed(kDoubleSolenoidReverseButton)) {
       m_doubleSolenoid.set(DoubleSolenoid.Value.kReverse);
     }
+
+    // On button press, toggle the compressor.
+    if (m_stick.getRawButtonPressed(kCompressorButton)) {
+      // Check whether the compressor is currently enabled.
+      boolean isCompressorEnabled = m_compressor.isEnabled();
+      if (isCompressorEnabled) {
+        // Disable closed-loop mode on the compressor.
+        m_compressor.disable();
+      } else {
+        // Change the if statements to select the closed-loop you want to use:
+        if (false) {
+          // Enable closed-loop mode based on the digital pressure switch connected to the PCM/PH.
+          // The switch is open when the pressure is over ~120 PSI.
+          m_compressor.enableDigital();
+        }
+        if (true) {
+          // Enable closed-loop mode based on the analog pressure sensor connected to the PH.
+          // The compressor will run while the pressure reported by the sensor is in the
+          // specified range ([70 PSI, 120 PSI] in this example).
+          // Analog mode exists only on the PH! On the PCM, this enables digital control.
+          m_compressor.enableAnalog(70, 120);
+        }
+        if (false) {
+          // Enable closed-loop mode based on both the digital pressure switch AND the analog
+          // pressure sensor connected to the PH.
+          // The compressor will run while the pressure reported by the analog sensor is in the
+          // specified range ([70 PSI, 120 PSI] in this example) AND the digital switch reports
+          // that the system is not full.
+          // Hybrid mode exists only on the PH! On the PCM, this enables digital control.
+          m_compressor.enableHybrid(70, 120);
+        }
+      }
+    }
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java
index 9877906..69997d1 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java
@@ -41,10 +41,11 @@
   // the motors, this number should be greater than one.
   private static final double kArmGearing = 10.0;
 
-  private final TrapezoidProfile.Constraints m_constraints =
-      new TrapezoidProfile.Constraints(
-          Units.degreesToRadians(45),
-          Units.degreesToRadians(90)); // Max arm speed and acceleration.
+  private final TrapezoidProfile m_profile =
+      new TrapezoidProfile(
+          new TrapezoidProfile.Constraints(
+              Units.degreesToRadians(45),
+              Units.degreesToRadians(90))); // Max arm speed and acceleration.
   private TrapezoidProfile.State m_lastProfiledReference = new TrapezoidProfile.State();
 
   // The plant holds a state-space model of our arm. This system has the following properties:
@@ -125,10 +126,8 @@
       goal = new TrapezoidProfile.State(kLoweredPosition, 0.0);
     }
     // Step our TrapezoidalProfile forward 20ms and set it as our next reference
-    m_lastProfiledReference =
-        (new TrapezoidProfile(m_constraints, goal, m_lastProfiledReference)).calculate(0.020);
+    m_lastProfiledReference = m_profile.calculate(0.020, goal, m_lastProfiledReference);
     m_loop.setNextR(m_lastProfiledReference.position, m_lastProfiledReference.velocity);
-
     // Correct our Kalman filter's state vector estimate with encoder data.
     m_loop.correct(VecBuilder.fill(m_encoder.getDistance()));
 
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java
index f1aca19..2069521 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java
@@ -43,9 +43,11 @@
   // the motors, this number should be greater than one.
   private static final double kElevatorGearing = 6.0;
 
-  private final TrapezoidProfile.Constraints m_constraints =
-      new TrapezoidProfile.Constraints(
-          Units.feetToMeters(3.0), Units.feetToMeters(6.0)); // Max elevator speed and acceleration.
+  private final TrapezoidProfile m_profile =
+      new TrapezoidProfile(
+          new TrapezoidProfile.Constraints(
+              Units.feetToMeters(3.0),
+              Units.feetToMeters(6.0))); // Max elevator speed and acceleration.
   private TrapezoidProfile.State m_lastProfiledReference = new TrapezoidProfile.State();
 
   /* The plant holds a state-space model of our elevator. This system has the following properties:
@@ -128,8 +130,7 @@
       goal = new TrapezoidProfile.State(kLowGoalPosition, 0.0);
     }
     // Step our TrapezoidalProfile forward 20ms and set it as our next reference
-    m_lastProfiledReference =
-        (new TrapezoidProfile(m_constraints, goal, m_lastProfiledReference)).calculate(0.020);
+    m_lastProfiledReference = m_profile.calculate(0.020, goal, m_lastProfiledReference);
     m_loop.setNextR(m_lastProfiledReference.position, m_lastProfiledReference.velocity);
 
     // Correct our Kalman filter's state vector estimate with encoder data.
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Drivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Drivetrain.java
index 6a45f6a..958e5a3 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Drivetrain.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Drivetrain.java
@@ -55,12 +55,16 @@
    * @param rot Angular rate of the robot.
    * @param fieldRelative Whether the provided x and y speeds are relative to the field.
    */
-  public void drive(double xSpeed, double ySpeed, double rot, boolean fieldRelative) {
+  public void drive(
+      double xSpeed, double ySpeed, double rot, boolean fieldRelative, double periodSeconds) {
     var swerveModuleStates =
         m_kinematics.toSwerveModuleStates(
-            fieldRelative
-                ? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeed, ySpeed, rot, m_gyro.getRotation2d())
-                : new ChassisSpeeds(xSpeed, ySpeed, rot));
+            ChassisSpeeds.discretize(
+                fieldRelative
+                    ? ChassisSpeeds.fromFieldRelativeSpeeds(
+                        xSpeed, ySpeed, rot, m_gyro.getRotation2d())
+                    : new ChassisSpeeds(xSpeed, ySpeed, rot),
+                periodSeconds));
     SwerveDriveKinematics.desaturateWheelSpeeds(swerveModuleStates, kMaxSpeed);
     m_frontLeft.setDesiredState(swerveModuleStates[0]);
     m_frontRight.setDesiredState(swerveModuleStates[1]);
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Robot.java
index b2d39ef..774c654 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Robot.java
@@ -51,6 +51,6 @@
         -m_rotLimiter.calculate(MathUtil.applyDeadband(m_controller.getRightX(), 0.02))
             * Drivetrain.kMaxAngularSpeed;
 
-    m_swerve.drive(xSpeed, ySpeed, rot, fieldRelative);
+    m_swerve.drive(xSpeed, ySpeed, rot, fieldRelative, getPeriod());
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java
index 27ea37f..65089f9 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java
@@ -109,9 +109,15 @@
    * @param desiredState Desired state with speed and angle.
    */
   public void setDesiredState(SwerveModuleState desiredState) {
+    var encoderRotation = new Rotation2d(m_turningEncoder.getDistance());
+
     // Optimize the reference state to avoid spinning further than 90 degrees
-    SwerveModuleState state =
-        SwerveModuleState.optimize(desiredState, new Rotation2d(m_turningEncoder.getDistance()));
+    SwerveModuleState state = SwerveModuleState.optimize(desiredState, encoderRotation);
+
+    // Scale speed by cosine of angle error. This scales down movement perpendicular to the desired
+    // direction of travel that can occur when modules change directions. This results in smoother
+    // driving.
+    state.speedMetersPerSecond *= state.angle.minus(encoderRotation).getCos();
 
     // Calculate the drive output from the drive PID controller.
     final double driveOutput =
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/Constants.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/Constants.java
index 02c04b8..0965db3 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/Constants.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/Constants.java
@@ -7,6 +7,7 @@
 import edu.wpi.first.math.geometry.Translation2d;
 import edu.wpi.first.math.kinematics.SwerveDriveKinematics;
 import edu.wpi.first.math.trajectory.TrapezoidProfile;
+import edu.wpi.first.wpilibj.TimedRobot;
 
 /**
  * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean
@@ -48,6 +49,9 @@
     public static final boolean kFrontRightDriveEncoderReversed = false;
     public static final boolean kRearRightDriveEncoderReversed = true;
 
+    // If you call DriveSubsystem.drive() with a different period make sure to update this.
+    public static final double kDrivePeriod = TimedRobot.kDefaultPeriod;
+
     public static final double kTrackWidth = 0.5;
     // Distance between centers of right and left wheels on robot
     public static final double kWheelBase = 0.7;
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/RobotContainer.java
index 4884a5d..cf0ff8c 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/RobotContainer.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/RobotContainer.java
@@ -15,6 +15,7 @@
 import edu.wpi.first.wpilibj.XboxController;
 import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.AutoConstants;
 import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.DriveConstants;
+import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.ModuleConstants;
 import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.OIConstants;
 import edu.wpi.first.wpilibj.examples.swervecontrollercommand.subsystems.DriveSubsystem;
 import edu.wpi.first.wpilibj2.command.Command;
@@ -48,9 +49,13 @@
         new RunCommand(
             () ->
                 m_robotDrive.drive(
-                    m_driverController.getLeftY(),
-                    m_driverController.getLeftX(),
-                    m_driverController.getRightX(),
+                    // Multiply by max speed to map the joystick unitless inputs to actual units.
+                    // This will map the [-1, 1] to [max speed backwards, max speed forwards],
+                    // converting them to actual units.
+                    m_driverController.getLeftY() * DriveConstants.kMaxSpeedMetersPerSecond,
+                    m_driverController.getLeftX() * DriveConstants.kMaxSpeedMetersPerSecond,
+                    m_driverController.getRightX()
+                        * ModuleConstants.kMaxModuleAngularSpeedRadiansPerSecond,
                     false),
             m_robotDrive));
   }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/DriveSubsystem.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/DriveSubsystem.java
index 326efee..04ee299 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/DriveSubsystem.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/DriveSubsystem.java
@@ -12,7 +12,6 @@
 import edu.wpi.first.math.kinematics.SwerveModuleState;
 import edu.wpi.first.wpilibj.ADXRS450_Gyro;
 import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.DriveConstants;
-import edu.wpi.first.wpilibj.interfaces.Gyro;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
 
 public class DriveSubsystem extends SubsystemBase {
@@ -54,7 +53,7 @@
           DriveConstants.kRearRightTurningEncoderReversed);
 
   // The gyro sensor
-  private final Gyro m_gyro = new ADXRS450_Gyro();
+  private final ADXRS450_Gyro m_gyro = new ADXRS450_Gyro();
 
   // Odometry class for tracking robot pose
   SwerveDriveOdometry m_odometry =
@@ -121,9 +120,12 @@
   public void drive(double xSpeed, double ySpeed, double rot, boolean fieldRelative) {
     var swerveModuleStates =
         DriveConstants.kDriveKinematics.toSwerveModuleStates(
-            fieldRelative
-                ? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeed, ySpeed, rot, m_gyro.getRotation2d())
-                : new ChassisSpeeds(xSpeed, ySpeed, rot));
+            ChassisSpeeds.discretize(
+                fieldRelative
+                    ? ChassisSpeeds.fromFieldRelativeSpeeds(
+                        xSpeed, ySpeed, rot, m_gyro.getRotation2d())
+                    : new ChassisSpeeds(xSpeed, ySpeed, rot),
+                DriveConstants.kDrivePeriod));
     SwerveDriveKinematics.desaturateWheelSpeeds(
         swerveModuleStates, DriveConstants.kMaxSpeedMetersPerSecond);
     m_frontLeft.setDesiredState(swerveModuleStates[0]);
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java
index 46825b2..27e800c 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java
@@ -105,9 +105,15 @@
    * @param desiredState Desired state with speed and angle.
    */
   public void setDesiredState(SwerveModuleState desiredState) {
+    var encoderRotation = new Rotation2d(m_turningEncoder.getDistance());
+
     // Optimize the reference state to avoid spinning further than 90 degrees
-    SwerveModuleState state =
-        SwerveModuleState.optimize(desiredState, new Rotation2d(m_turningEncoder.getDistance()));
+    SwerveModuleState state = SwerveModuleState.optimize(desiredState, encoderRotation);
+
+    // Scale speed by cosine of angle error. This scales down movement perpendicular to the desired
+    // direction of travel that can occur when modules change directions. This results in smoother
+    // driving.
+    state.speedMetersPerSecond *= state.angle.minus(encoderRotation).getCos();
 
     // Calculate the drive output from the drive PID controller.
     final double driveOutput =
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/Drivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/Drivetrain.java
index 8f4c5ce..923af5b 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/Drivetrain.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/Drivetrain.java
@@ -64,12 +64,16 @@
    * @param rot Angular rate of the robot.
    * @param fieldRelative Whether the provided x and y speeds are relative to the field.
    */
-  public void drive(double xSpeed, double ySpeed, double rot, boolean fieldRelative) {
+  public void drive(
+      double xSpeed, double ySpeed, double rot, boolean fieldRelative, double periodSeconds) {
     var swerveModuleStates =
         m_kinematics.toSwerveModuleStates(
-            fieldRelative
-                ? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeed, ySpeed, rot, m_gyro.getRotation2d())
-                : new ChassisSpeeds(xSpeed, ySpeed, rot));
+            ChassisSpeeds.discretize(
+                fieldRelative
+                    ? ChassisSpeeds.fromFieldRelativeSpeeds(
+                        xSpeed, ySpeed, rot, m_gyro.getRotation2d())
+                    : new ChassisSpeeds(xSpeed, ySpeed, rot),
+                periodSeconds));
     SwerveDriveKinematics.desaturateWheelSpeeds(swerveModuleStates, kMaxSpeed);
     m_frontLeft.setDesiredState(swerveModuleStates[0]);
     m_frontRight.setDesiredState(swerveModuleStates[1]);
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/Robot.java
index 7fbee8d..bb3a596 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/Robot.java
@@ -44,6 +44,6 @@
     // the right by default.
     final var rot = -m_rotLimiter.calculate(m_controller.getRightX()) * Drivetrain.kMaxAngularSpeed;
 
-    m_swerve.drive(xSpeed, ySpeed, rot, fieldRelative);
+    m_swerve.drive(xSpeed, ySpeed, rot, fieldRelative, getPeriod());
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/SwerveModule.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/SwerveModule.java
index afd7489..938b6c8 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/SwerveModule.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/SwerveModule.java
@@ -109,9 +109,15 @@
    * @param desiredState Desired state with speed and angle.
    */
   public void setDesiredState(SwerveModuleState desiredState) {
+    var encoderRotation = new Rotation2d(m_turningEncoder.getDistance());
+
     // Optimize the reference state to avoid spinning further than 90 degrees
-    SwerveModuleState state =
-        SwerveModuleState.optimize(desiredState, new Rotation2d(m_turningEncoder.getDistance()));
+    SwerveModuleState state = SwerveModuleState.optimize(desiredState, encoderRotation);
+
+    // Scale speed by cosine of angle error. This scales down movement perpendicular to the desired
+    // direction of travel that can occur when modules change directions. This results in smoother
+    // driving.
+    state.speedMetersPerSecond *= state.angle.minus(encoderRotation).getCos();
 
     // Calculate the drive output from the drive PID controller.
     final double driveOutput =
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java
index 199d6f6..edc81ab 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java
@@ -4,52 +4,61 @@
 
 package edu.wpi.first.wpilibj.examples.ultrasonic;
 
-import edu.wpi.first.math.filter.MedianFilter;
-import edu.wpi.first.wpilibj.AnalogInput;
 import edu.wpi.first.wpilibj.TimedRobot;
-import edu.wpi.first.wpilibj.drive.DifferentialDrive;
-import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
+import edu.wpi.first.wpilibj.Ultrasonic;
+import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
 
 /**
- * This is a sample program demonstrating how to use an ultrasonic sensor and proportional control
- * to maintain a set distance from an object.
+ * This is a sample program demonstrating how to read from a ping-response ultrasonic sensor with
+ * the {@link Ultrasonic class}.
  */
 public class Robot extends TimedRobot {
-  // distance in inches the robot wants to stay from an object
-  private static final double kHoldDistance = 12.0;
+  // Creates a ping-response Ultrasonic object on DIO 1 and 2.
+  Ultrasonic m_rangeFinder = new Ultrasonic(1, 2);
 
-  // factor to convert sensor values to a distance in inches
-  private static final double kValueToInches = 0.125;
+  @Override
+  public void robotInit() {
+    // Add the ultrasonic on the "Sensors" tab of the dashboard
+    // Data will update automatically
+    Shuffleboard.getTab("Sensors").add(m_rangeFinder);
+  }
 
-  // proportional speed constant
-  private static final double kP = 0.05;
-
-  private static final int kLeftMotorPort = 0;
-  private static final int kRightMotorPort = 1;
-  private static final int kUltrasonicPort = 0;
-
-  // median filter to discard outliers; filters over 10 samples
-  private final MedianFilter m_filter = new MedianFilter(10);
-
-  private final AnalogInput m_ultrasonic = new AnalogInput(kUltrasonicPort);
-  private final DifferentialDrive m_robotDrive =
-      new DifferentialDrive(new PWMSparkMax(kLeftMotorPort), new PWMSparkMax(kRightMotorPort));
-
-  /**
-   * Tells the robot to drive to a set distance (in inches) from an object using proportional
-   * control.
-   */
   @Override
   public void teleopPeriodic() {
-    // sensor returns a value from 0-4095 that is scaled to inches
-    // returned value is filtered with a rolling median filter, since ultrasonics
-    // tend to be quite noisy and susceptible to sudden outliers
-    double currentDistance = m_filter.calculate(m_ultrasonic.getValue()) * kValueToInches;
+    // We can read the distance in millimeters
+    double distanceMillimeters = m_rangeFinder.getRangeMM();
+    // ... or in inches
+    double distanceInches = m_rangeFinder.getRangeInches();
 
-    // convert distance error to a motor speed
-    double currentSpeed = (kHoldDistance - currentDistance) * kP;
+    // We can also publish the data itself periodically
+    SmartDashboard.putNumber("Distance[mm]", distanceMillimeters);
+    SmartDashboard.putNumber("Distance[inch]", distanceInches);
+  }
 
-    // drive robot
-    m_robotDrive.arcadeDrive(currentSpeed, 0);
+  @Override
+  public void testInit() {
+    // By default, the Ultrasonic class polls all ultrasonic sensors in a round-robin to prevent
+    // them from interfering from one another.
+    // However, manual polling is also possible -- note that this disables automatic mode!
+    m_rangeFinder.ping();
+  }
+
+  @Override
+  public void testPeriodic() {
+    if (m_rangeFinder.isRangeValid()) {
+      // Data is valid, publish it
+      SmartDashboard.putNumber("Distance[mm]", m_rangeFinder.getRangeMM());
+      SmartDashboard.putNumber("Distance[inch]", m_rangeFinder.getRangeInches());
+
+      // Ping for next measurement
+      m_rangeFinder.ping();
+    }
+  }
+
+  @Override
+  public void testExit() {
+    // Enable automatic mode
+    Ultrasonic.setAutomaticMode(true);
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java
index 526ac53..532e9ac 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java
@@ -6,8 +6,8 @@
 
 import edu.wpi.first.math.controller.PIDController;
 import edu.wpi.first.math.filter.MedianFilter;
-import edu.wpi.first.wpilibj.AnalogInput;
 import edu.wpi.first.wpilibj.TimedRobot;
+import edu.wpi.first.wpilibj.Ultrasonic;
 import edu.wpi.first.wpilibj.drive.DifferentialDrive;
 import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
 
@@ -16,45 +16,56 @@
  * reach and maintain a set distance from an object.
  */
 public class Robot extends TimedRobot {
-  // distance in inches the robot wants to stay from an object
-  private static final double kHoldDistance = 12.0;
-
-  // factor to convert sensor values to a distance in inches
-  private static final double kValueToInches = 0.125;
+  // distance the robot wants to stay from an object
+  // (one meter)
+  static final double kHoldDistanceMillimeters = 1.0e3;
 
   // proportional speed constant
-  private static final double kP = 7.0;
-
+  // negative because applying positive voltage will bring us closer to the target
+  private static final double kP = -0.001;
   // integral speed constant
-  private static final double kI = 0.018;
-
+  private static final double kI = 0.0;
   // derivative speed constant
-  private static final double kD = 1.5;
+  private static final double kD = 0.0;
 
-  private static final int kLeftMotorPort = 0;
-  private static final int kRightMotorPort = 1;
-  private static final int kUltrasonicPort = 0;
+  static final int kLeftMotorPort = 0;
+  static final int kRightMotorPort = 1;
 
-  // median filter to discard outliers; filters over 5 samples
+  static final int kUltrasonicPingPort = 0;
+  static final int kUltrasonicEchoPort = 1;
+
+  // Ultrasonic sensors tend to be quite noisy and susceptible to sudden outliers,
+  // so measurements are filtered with a 5-sample median filter
   private final MedianFilter m_filter = new MedianFilter(5);
 
-  private final AnalogInput m_ultrasonic = new AnalogInput(kUltrasonicPort);
-  private final DifferentialDrive m_robotDrive =
-      new DifferentialDrive(new PWMSparkMax(kLeftMotorPort), new PWMSparkMax(kRightMotorPort));
+  private final Ultrasonic m_ultrasonic = new Ultrasonic(kUltrasonicPingPort, kUltrasonicEchoPort);
+  private final PWMSparkMax m_leftMotor = new PWMSparkMax(kLeftMotorPort);
+  private final PWMSparkMax m_rightMotor = new PWMSparkMax(kRightMotorPort);
+  private final DifferentialDrive m_robotDrive = new DifferentialDrive(m_leftMotor, m_rightMotor);
   private final PIDController m_pidController = new PIDController(kP, kI, kD);
 
   @Override
-  public void teleopInit() {
+  public void autonomousInit() {
     // Set setpoint of the pid controller
-    m_pidController.setSetpoint(kHoldDistance * kValueToInches);
+    m_pidController.setSetpoint(kHoldDistanceMillimeters);
   }
 
   @Override
-  public void teleopPeriodic() {
-    // returned value is filtered with a rolling median filter, since ultrasonics
-    // tend to be quite noisy and susceptible to sudden outliers
-    double pidOutput = m_pidController.calculate(m_filter.calculate(m_ultrasonic.getVoltage()));
+  public void autonomousPeriodic() {
+    double measurement = m_ultrasonic.getRangeMM();
+    double filteredMeasurement = m_filter.calculate(measurement);
+    double pidOutput = m_pidController.calculate(filteredMeasurement);
 
-    m_robotDrive.arcadeDrive(pidOutput, 0);
+    // disable input squaring -- PID output is linear
+    m_robotDrive.arcadeDrive(pidOutput, 0, false);
+  }
+
+  @Override
+  public void close() {
+    m_leftMotor.close();
+    m_rightMotor.close();
+    m_ultrasonic.close();
+    m_robotDrive.close();
+    super.close();
   }
 }
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Robot.java
index 9f66c27..1333a1c 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Robot.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Robot.java
@@ -16,8 +16,8 @@
  * project.
  */
 public class Robot extends TimedRobot {
-  private Intake m_intake = new Intake();
-  private Joystick m_joystick = new Joystick(Constants.kJoystickIndex);
+  private final Intake m_intake = new Intake();
+  private final Joystick m_joystick = new Joystick(Constants.kJoystickIndex);
 
   /** This function is called periodically during operator control. */
   @Override
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/Constants.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/Constants.java
new file mode 100644
index 0000000..026efdd
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/Constants.java
@@ -0,0 +1,19 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference;
+
+/**
+ * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean
+ * constants. This class should not be used for any other purpose. All constants should be declared
+ * globally (i.e. public static). Do not put anything functional in this class.
+ *
+ * <p>It is advised to statically import this class (or one of its inner classes) wherever the
+ * constants are needed, to reduce verbosity.
+ */
+public final class Constants {
+  public static class OperatorConstants {
+    public static final int kDriverControllerPort = 0;
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/Main.java
new file mode 100644
index 0000000..125996d
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/Main.java
@@ -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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference;
+
+import edu.wpi.first.wpilibj.RobotBase;
+
+/**
+ * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
+ * you are doing, do not modify this file except to change the parameter class to the startRobot
+ * call.
+ */
+public final class Main {
+  private Main() {}
+
+  /**
+   * Main initialization function. Do not perform any initialization here.
+   *
+   * <p>If you change your main robot class, change the parameter type.
+   */
+  public static void main(String... args) {
+    RobotBase.startRobot(Robot::new);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/Robot.java
new file mode 100644
index 0000000..daf5d32
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/Robot.java
@@ -0,0 +1,103 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference;
+
+import edu.wpi.first.wpilibj.TimedRobot;
+import edu.wpi.first.wpilibj2.command.Command;
+import edu.wpi.first.wpilibj2.command.CommandScheduler;
+
+/**
+ * The VM is configured to automatically run this class, and to call the functions corresponding to
+ * each mode, as described in the TimedRobot documentation. If you change the name of this class or
+ * the package after creating this project, you must also update the build.gradle file in the
+ * project.
+ */
+public class Robot extends TimedRobot {
+  private Command m_autonomousCommand;
+
+  private RobotContainer m_robotContainer;
+
+  /**
+   * This function is run when the robot is first started up and should be used for any
+   * initialization code.
+   */
+  @Override
+  public void robotInit() {
+    // Instantiate our RobotContainer.  This will perform all our button bindings, and put our
+    // autonomous chooser on the dashboard.
+    m_robotContainer = new RobotContainer();
+  }
+
+  /**
+   * This function is called every 20 ms, no matter the mode. Use this for items like diagnostics
+   * that you want ran during disabled, autonomous, teleoperated and test.
+   *
+   * <p>This runs after the mode specific periodic functions, but before LiveWindow and
+   * SmartDashboard integrated updating.
+   */
+  @Override
+  public void robotPeriodic() {
+    // Runs the Scheduler.  This is responsible for polling buttons, adding newly-scheduled
+    // commands, running already-scheduled commands, removing finished or interrupted commands,
+    // and running subsystem periodic() methods.  This must be called from the robot's periodic
+    // block in order for anything in the Command-based framework to work.
+    CommandScheduler.getInstance().run();
+  }
+
+  /** This function is called once each time the robot enters Disabled mode. */
+  @Override
+  public void disabledInit() {}
+
+  @Override
+  public void disabledPeriodic() {}
+
+  /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */
+  @Override
+  public void autonomousInit() {
+    m_autonomousCommand = m_robotContainer.getAutonomousCommand();
+
+    // schedule the autonomous command (example)
+    if (m_autonomousCommand != null) {
+      m_autonomousCommand.schedule();
+    }
+  }
+
+  /** This function is called periodically during autonomous. */
+  @Override
+  public void autonomousPeriodic() {}
+
+  @Override
+  public void teleopInit() {
+    // This makes sure that the autonomous stops running when
+    // teleop starts running. If you want the autonomous to
+    // continue until interrupted by another command, remove
+    // this line or comment it out.
+    if (m_autonomousCommand != null) {
+      m_autonomousCommand.cancel();
+    }
+  }
+
+  /** This function is called periodically during operator control. */
+  @Override
+  public void teleopPeriodic() {}
+
+  @Override
+  public void testInit() {
+    // Cancels all running commands at the start of test mode.
+    CommandScheduler.getInstance().cancelAll();
+  }
+
+  /** This function is called periodically during test mode. */
+  @Override
+  public void testPeriodic() {}
+
+  /** This function is called once when the robot is first started up. */
+  @Override
+  public void simulationInit() {}
+
+  /** This function is called periodically whilst in simulation. */
+  @Override
+  public void simulationPeriodic() {}
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/RobotContainer.java
new file mode 100644
index 0000000..8c2746b
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/RobotContainer.java
@@ -0,0 +1,99 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference;
+
+import edu.wpi.first.wpilibj.GenericHID;
+import edu.wpi.first.wpilibj.Joystick;
+import edu.wpi.first.wpilibj.XboxController;
+import edu.wpi.first.wpilibj.examples.xrpreference.commands.ArcadeDrive;
+import edu.wpi.first.wpilibj.examples.xrpreference.commands.AutonomousDistance;
+import edu.wpi.first.wpilibj.examples.xrpreference.commands.AutonomousTime;
+import edu.wpi.first.wpilibj.examples.xrpreference.subsystems.Arm;
+import edu.wpi.first.wpilibj.examples.xrpreference.subsystems.Drivetrain;
+import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
+import edu.wpi.first.wpilibj.xrp.XRPOnBoardIO;
+import edu.wpi.first.wpilibj2.command.Command;
+import edu.wpi.first.wpilibj2.command.InstantCommand;
+import edu.wpi.first.wpilibj2.command.PrintCommand;
+import edu.wpi.first.wpilibj2.command.button.JoystickButton;
+import edu.wpi.first.wpilibj2.command.button.Trigger;
+
+/**
+ * This class is where the bulk of the robot should be declared. Since Command-based is a
+ * "declarative" paradigm, very little robot logic should actually be handled in the {@link Robot}
+ * periodic methods (other than the scheduler calls). Instead, the structure of the robot (including
+ * subsystems, commands, and button mappings) should be declared here.
+ */
+public class RobotContainer {
+  // The robot's subsystems and commands are defined here...
+  private final Drivetrain m_drivetrain = new Drivetrain();
+  private final XRPOnBoardIO m_onboardIO = new XRPOnBoardIO();
+  private final Arm m_arm = new Arm();
+
+  // Assumes a gamepad plugged into channel 0
+  private final Joystick m_controller = new Joystick(0);
+
+  // Create SmartDashboard chooser for autonomous routines
+  private final SendableChooser<Command> m_chooser = new SendableChooser<>();
+
+  /** The container for the robot. Contains subsystems, OI devices, and commands. */
+  public RobotContainer() {
+    // Configure the button bindings
+    configureButtonBindings();
+  }
+
+  /**
+   * Use this method to define your button->command mappings. Buttons can be created by
+   * instantiating a {@link GenericHID} or one of its subclasses ({@link
+   * edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a {@link
+   * edu.wpi.first.wpilibj2.command.button.JoystickButton}.
+   */
+  private void configureButtonBindings() {
+    // Default command is arcade drive. This will run unless another command
+    // is scheduled over it.
+    m_drivetrain.setDefaultCommand(getArcadeDriveCommand());
+
+    // Example of how to use the onboard IO
+    Trigger userButton = new Trigger(m_onboardIO::getUserButtonPressed);
+    userButton
+        .onTrue(new PrintCommand("USER Button Pressed"))
+        .onFalse(new PrintCommand("USER Button Released"));
+
+    JoystickButton joystickAButton = new JoystickButton(m_controller, 1);
+    joystickAButton
+        .onTrue(new InstantCommand(() -> m_arm.setAngle(45.0), m_arm))
+        .onFalse(new InstantCommand(() -> m_arm.setAngle(0.0), m_arm));
+
+    JoystickButton joystickBButton = new JoystickButton(m_controller, 2);
+    joystickBButton
+        .onTrue(new InstantCommand(() -> m_arm.setAngle(90.0), m_arm))
+        .onFalse(new InstantCommand(() -> m_arm.setAngle(0.0), m_arm));
+
+    // Setup SmartDashboard options
+    m_chooser.setDefaultOption("Auto Routine Distance", new AutonomousDistance(m_drivetrain));
+    m_chooser.addOption("Auto Routine Time", new AutonomousTime(m_drivetrain));
+    SmartDashboard.putData(m_chooser);
+  }
+
+  /**
+   * Use this to pass the autonomous command to the main {@link Robot} class.
+   *
+   * @return the command to run in autonomous
+   */
+  public Command getAutonomousCommand() {
+    return m_chooser.getSelected();
+  }
+
+  /**
+   * Use this to pass the teleop command to the main {@link Robot} class.
+   *
+   * @return the command to run in teleop
+   */
+  public Command getArcadeDriveCommand() {
+    return new ArcadeDrive(
+        m_drivetrain, () -> -m_controller.getRawAxis(1), () -> -m_controller.getRawAxis(2));
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/ArcadeDrive.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/ArcadeDrive.java
new file mode 100644
index 0000000..0269817
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/ArcadeDrive.java
@@ -0,0 +1,53 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference.commands;
+
+import edu.wpi.first.wpilibj.examples.xrpreference.subsystems.Drivetrain;
+import edu.wpi.first.wpilibj2.command.Command;
+import java.util.function.Supplier;
+
+public class ArcadeDrive extends Command {
+  private final Drivetrain m_drivetrain;
+  private final Supplier<Double> m_xaxisSpeedSupplier;
+  private final Supplier<Double> m_zaxisRotateSupplier;
+
+  /**
+   * Creates a new ArcadeDrive. This command will drive your robot according to the speed supplier
+   * lambdas. This command does not terminate.
+   *
+   * @param drivetrain The drivetrain subsystem on which this command will run
+   * @param xaxisSpeedSupplier Lambda supplier of forward/backward speed
+   * @param zaxisRotateSupplier Lambda supplier of rotational speed
+   */
+  public ArcadeDrive(
+      Drivetrain drivetrain,
+      Supplier<Double> xaxisSpeedSupplier,
+      Supplier<Double> zaxisRotateSupplier) {
+    m_drivetrain = drivetrain;
+    m_xaxisSpeedSupplier = xaxisSpeedSupplier;
+    m_zaxisRotateSupplier = zaxisRotateSupplier;
+    addRequirements(drivetrain);
+  }
+
+  // Called when the command is initially scheduled.
+  @Override
+  public void initialize() {}
+
+  // Called every time the scheduler runs while the command is scheduled.
+  @Override
+  public void execute() {
+    m_drivetrain.arcadeDrive(m_xaxisSpeedSupplier.get(), m_zaxisRotateSupplier.get());
+  }
+
+  // Called once the command ends or is interrupted.
+  @Override
+  public void end(boolean interrupted) {}
+
+  // Returns true when the command should end.
+  @Override
+  public boolean isFinished() {
+    return false;
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/AutonomousDistance.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/AutonomousDistance.java
new file mode 100644
index 0000000..8b8af83
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/AutonomousDistance.java
@@ -0,0 +1,24 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference.commands;
+
+import edu.wpi.first.wpilibj.examples.xrpreference.subsystems.Drivetrain;
+import edu.wpi.first.wpilibj2.command.SequentialCommandGroup;
+
+public class AutonomousDistance extends SequentialCommandGroup {
+  /**
+   * Creates a new Autonomous Drive based on distance. This will drive out for a specified distance,
+   * turn around and drive back.
+   *
+   * @param drivetrain The drivetrain subsystem on which this command will run
+   */
+  public AutonomousDistance(Drivetrain drivetrain) {
+    addCommands(
+        new DriveDistance(-0.5, 10, drivetrain),
+        new TurnDegrees(-0.5, 180, drivetrain),
+        new DriveDistance(-0.5, 10, drivetrain),
+        new TurnDegrees(0.5, 180, drivetrain));
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/AutonomousTime.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/AutonomousTime.java
new file mode 100644
index 0000000..4fb20d9
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/AutonomousTime.java
@@ -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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference.commands;
+
+import edu.wpi.first.wpilibj.examples.xrpreference.subsystems.Drivetrain;
+import edu.wpi.first.wpilibj2.command.SequentialCommandGroup;
+
+public class AutonomousTime extends SequentialCommandGroup {
+  /**
+   * Creates a new Autonomous Drive based on time. This will drive out for a period of time, turn
+   * around for time (equivalent to time to turn around) and drive forward again. This should mimic
+   * driving out, turning around and driving back.
+   *
+   * @param drivetrain The drive subsystem on which this command will run
+   */
+  public AutonomousTime(Drivetrain drivetrain) {
+    addCommands(
+        new DriveTime(-0.6, 2.0, drivetrain),
+        new TurnTime(-0.5, 1.3, drivetrain),
+        new DriveTime(-0.6, 2.0, drivetrain),
+        new TurnTime(0.5, 1.3, drivetrain));
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/DriveDistance.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/DriveDistance.java
new file mode 100644
index 0000000..b7d9640
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/DriveDistance.java
@@ -0,0 +1,55 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference.commands;
+
+import edu.wpi.first.wpilibj.examples.xrpreference.subsystems.Drivetrain;
+import edu.wpi.first.wpilibj2.command.Command;
+
+public class DriveDistance extends Command {
+  private final Drivetrain m_drive;
+  private final double m_distance;
+  private final double m_speed;
+
+  /**
+   * Creates a new DriveDistance. This command will drive your your robot for a desired distance at
+   * a desired speed.
+   *
+   * @param speed The speed at which the robot will drive
+   * @param inches The number of inches the robot will drive
+   * @param drive The drivetrain subsystem on which this command will run
+   */
+  public DriveDistance(double speed, double inches, Drivetrain drive) {
+    m_distance = inches;
+    m_speed = speed;
+    m_drive = drive;
+    addRequirements(drive);
+  }
+
+  // Called when the command is initially scheduled.
+  @Override
+  public void initialize() {
+    m_drive.arcadeDrive(0, 0);
+    m_drive.resetEncoders();
+  }
+
+  // Called every time the scheduler runs while the command is scheduled.
+  @Override
+  public void execute() {
+    m_drive.arcadeDrive(m_speed, 0);
+  }
+
+  // Called once the command ends or is interrupted.
+  @Override
+  public void end(boolean interrupted) {
+    m_drive.arcadeDrive(0, 0);
+  }
+
+  // Returns true when the command should end.
+  @Override
+  public boolean isFinished() {
+    // Compare distance travelled from start to desired distance
+    return Math.abs(m_drive.getAverageDistanceInch()) >= m_distance;
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/DriveTime.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/DriveTime.java
new file mode 100644
index 0000000..5608c0f
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/DriveTime.java
@@ -0,0 +1,54 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference.commands;
+
+import edu.wpi.first.wpilibj.examples.xrpreference.subsystems.Drivetrain;
+import edu.wpi.first.wpilibj2.command.Command;
+
+public class DriveTime extends Command {
+  private final double m_duration;
+  private final double m_speed;
+  private final Drivetrain m_drive;
+  private long m_startTime;
+
+  /**
+   * Creates a new DriveTime. This command will drive your robot for a desired speed and time.
+   *
+   * @param speed The speed which the robot will drive. Negative is in reverse.
+   * @param time How much time to drive in seconds
+   * @param drive The drivetrain subsystem on which this command will run
+   */
+  public DriveTime(double speed, double time, Drivetrain drive) {
+    m_speed = speed;
+    m_duration = time * 1000;
+    m_drive = drive;
+    addRequirements(drive);
+  }
+
+  // Called when the command is initially scheduled.
+  @Override
+  public void initialize() {
+    m_startTime = System.currentTimeMillis();
+    m_drive.arcadeDrive(0, 0);
+  }
+
+  // Called every time the scheduler runs while the command is scheduled.
+  @Override
+  public void execute() {
+    m_drive.arcadeDrive(m_speed, 0);
+  }
+
+  // Called once the command ends or is interrupted.
+  @Override
+  public void end(boolean interrupted) {
+    m_drive.arcadeDrive(0, 0);
+  }
+
+  // Returns true when the command should end.
+  @Override
+  public boolean isFinished() {
+    return (System.currentTimeMillis() - m_startTime) >= m_duration;
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/TurnDegrees.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/TurnDegrees.java
new file mode 100644
index 0000000..dfee189
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/TurnDegrees.java
@@ -0,0 +1,68 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference.commands;
+
+import edu.wpi.first.wpilibj.examples.xrpreference.subsystems.Drivetrain;
+import edu.wpi.first.wpilibj2.command.Command;
+
+public class TurnDegrees extends Command {
+  private final Drivetrain m_drive;
+  private final double m_degrees;
+  private final double m_speed;
+
+  /**
+   * Creates a new TurnDegrees. This command will turn your robot for a desired rotation (in
+   * degrees) and rotational speed.
+   *
+   * @param speed The speed which the robot will drive. Negative is in reverse.
+   * @param degrees Degrees to turn. Leverages encoders to compare distance.
+   * @param drive The drive subsystem on which this command will run
+   */
+  public TurnDegrees(double speed, double degrees, Drivetrain drive) {
+    m_degrees = degrees;
+    m_speed = speed;
+    m_drive = drive;
+    addRequirements(drive);
+  }
+
+  // Called when the command is initially scheduled.
+  @Override
+  public void initialize() {
+    // Set motors to stop, read encoder values for starting point
+    m_drive.arcadeDrive(0, 0);
+    m_drive.resetEncoders();
+  }
+
+  // Called every time the scheduler runs while the command is scheduled.
+  @Override
+  public void execute() {
+    m_drive.arcadeDrive(0, m_speed);
+  }
+
+  // Called once the command ends or is interrupted.
+  @Override
+  public void end(boolean interrupted) {
+    m_drive.arcadeDrive(0, 0);
+  }
+
+  // Returns true when the command should end.
+  @Override
+  public boolean isFinished() {
+    /* Need to convert distance travelled to degrees. The Standard
+       XRP Chassis found here, https://www.sparkfun.com/products/22230,
+       has a wheel placement diameter (163 mm) - width of the wheel (8 mm) = 155 mm
+       or 6.102 inches. We then take into consideration the width of the tires.
+    */
+    double inchPerDegree = Math.PI * 6.102 / 360;
+    // Compare distance travelled from start to distance based on degree turn
+    return getAverageTurningDistance() >= (inchPerDegree * m_degrees);
+  }
+
+  private double getAverageTurningDistance() {
+    double leftDistance = Math.abs(m_drive.getLeftDistanceInch());
+    double rightDistance = Math.abs(m_drive.getRightDistanceInch());
+    return (leftDistance + rightDistance) / 2.0;
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/TurnTime.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/TurnTime.java
new file mode 100644
index 0000000..030981e
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/commands/TurnTime.java
@@ -0,0 +1,58 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference.commands;
+
+import edu.wpi.first.wpilibj.examples.xrpreference.subsystems.Drivetrain;
+import edu.wpi.first.wpilibj2.command.Command;
+
+/*
+ * Creates a new TurnTime command. This command will turn your robot for a
+ * desired rotational speed and time.
+ */
+public class TurnTime extends Command {
+  private final double m_duration;
+  private final double m_rotationalSpeed;
+  private final Drivetrain m_drive;
+  private long m_startTime;
+
+  /**
+   * Creates a new TurnTime.
+   *
+   * @param speed The speed which the robot will turn. Negative is in reverse.
+   * @param time How much time to turn in seconds
+   * @param drive The drive subsystem on which this command will run
+   */
+  public TurnTime(double speed, double time, Drivetrain drive) {
+    m_rotationalSpeed = speed;
+    m_duration = time * 1000;
+    m_drive = drive;
+    addRequirements(drive);
+  }
+
+  // Called when the command is initially scheduled.
+  @Override
+  public void initialize() {
+    m_startTime = System.currentTimeMillis();
+    m_drive.arcadeDrive(0, 0);
+  }
+
+  // Called every time the scheduler runs while the command is scheduled.
+  @Override
+  public void execute() {
+    m_drive.arcadeDrive(0, m_rotationalSpeed);
+  }
+
+  // Called once the command ends or is interrupted.
+  @Override
+  public void end(boolean interrupted) {
+    m_drive.arcadeDrive(0, 0);
+  }
+
+  // Returns true when the command should end.
+  @Override
+  public boolean isFinished() {
+    return (System.currentTimeMillis() - m_startTime) >= m_duration;
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/subsystems/Arm.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/subsystems/Arm.java
new file mode 100644
index 0000000..ecf88b0
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/subsystems/Arm.java
@@ -0,0 +1,32 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference.subsystems;
+
+import edu.wpi.first.wpilibj.xrp.XRPServo;
+import edu.wpi.first.wpilibj2.command.SubsystemBase;
+
+public class Arm extends SubsystemBase {
+  private final XRPServo m_armServo;
+
+  /** Creates a new Arm. */
+  public Arm() {
+    // Device number 4 maps to the physical Servo 1 port on the XRP
+    m_armServo = new XRPServo(4);
+  }
+
+  @Override
+  public void periodic() {
+    // This method will be called once per scheduler run
+  }
+
+  /**
+   * Set the current angle of the arm (0 - 180 degrees).
+   *
+   * @param angleDeg Desired arm angle in degrees
+   */
+  public void setAngle(double angleDeg) {
+    m_armServo.setAngle(angleDeg);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/subsystems/Drivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/subsystems/Drivetrain.java
new file mode 100644
index 0000000..f618add
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/subsystems/Drivetrain.java
@@ -0,0 +1,145 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.xrpreference.subsystems;
+
+import edu.wpi.first.wpilibj.BuiltInAccelerometer;
+import edu.wpi.first.wpilibj.Encoder;
+import edu.wpi.first.wpilibj.drive.DifferentialDrive;
+import edu.wpi.first.wpilibj.xrp.XRPGyro;
+import edu.wpi.first.wpilibj.xrp.XRPMotor;
+import edu.wpi.first.wpilibj2.command.SubsystemBase;
+
+public class Drivetrain extends SubsystemBase {
+  private static final double kGearRatio =
+      (30.0 / 14.0) * (28.0 / 16.0) * (36.0 / 9.0) * (26.0 / 8.0); // 48.75:1
+  private static final double kCountsPerMotorShaftRev = 12.0;
+  private static final double kCountsPerRevolution = kCountsPerMotorShaftRev * kGearRatio; // 585.0
+  private static final double kWheelDiameterInch = 2.3622; // 60 mm
+
+  // The XRP has the left and right motors set to
+  // channels 0 and 1 respectively
+  private final XRPMotor m_leftMotor = new XRPMotor(0);
+  private final XRPMotor m_rightMotor = new XRPMotor(1);
+
+  // The XRP has onboard encoders that are hardcoded
+  // to use DIO pins 4/5 and 6/7 for the left and right
+  private final Encoder m_leftEncoder = new Encoder(4, 5);
+  private final Encoder m_rightEncoder = new Encoder(6, 7);
+
+  // Set up the differential drive controller
+  private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor);
+
+  // Set up the XRPGyro
+  private final XRPGyro m_gyro = new XRPGyro();
+
+  // Set up the BuiltInAccelerometer
+  private final BuiltInAccelerometer m_accelerometer = new BuiltInAccelerometer();
+
+  /** Creates a new Drivetrain. */
+  public Drivetrain() {
+    // We need to invert one side of the drivetrain so that positive voltages
+    // result in both sides moving forward. Depending on how your robot's
+    // gearbox is constructed, you might have to invert the left side instead.
+    m_rightMotor.setInverted(true);
+
+    // Use inches as unit for encoder distances
+    m_leftEncoder.setDistancePerPulse((Math.PI * kWheelDiameterInch) / kCountsPerRevolution);
+    m_rightEncoder.setDistancePerPulse((Math.PI * kWheelDiameterInch) / kCountsPerRevolution);
+    resetEncoders();
+  }
+
+  public void arcadeDrive(double xaxisSpeed, double zaxisRotate) {
+    m_diffDrive.arcadeDrive(xaxisSpeed, zaxisRotate);
+  }
+
+  public void resetEncoders() {
+    m_leftEncoder.reset();
+    m_rightEncoder.reset();
+  }
+
+  public int getLeftEncoderCount() {
+    return m_leftEncoder.get();
+  }
+
+  public int getRightEncoderCount() {
+    return m_rightEncoder.get();
+  }
+
+  public double getLeftDistanceInch() {
+    return m_leftEncoder.getDistance();
+  }
+
+  public double getRightDistanceInch() {
+    return m_rightEncoder.getDistance();
+  }
+
+  public double getAverageDistanceInch() {
+    return (getLeftDistanceInch() + getRightDistanceInch()) / 2.0;
+  }
+
+  /**
+   * The acceleration in the X-axis.
+   *
+   * @return The acceleration of the XRP along the X-axis in Gs
+   */
+  public double getAccelX() {
+    return m_accelerometer.getX();
+  }
+
+  /**
+   * The acceleration in the Y-axis.
+   *
+   * @return The acceleration of the XRP along the Y-axis in Gs
+   */
+  public double getAccelY() {
+    return m_accelerometer.getY();
+  }
+
+  /**
+   * The acceleration in the Z-axis.
+   *
+   * @return The acceleration of the XRP along the Z-axis in Gs
+   */
+  public double getAccelZ() {
+    return m_accelerometer.getZ();
+  }
+
+  /**
+   * Current angle of the XRP around the X-axis.
+   *
+   * @return The current angle of the XRP in degrees
+   */
+  public double getGyroAngleX() {
+    return m_gyro.getAngleX();
+  }
+
+  /**
+   * Current angle of the XRP around the Y-axis.
+   *
+   * @return The current angle of the XRP in degrees
+   */
+  public double getGyroAngleY() {
+    return m_gyro.getAngleY();
+  }
+
+  /**
+   * Current angle of the XRP around the Z-axis.
+   *
+   * @return The current angle of the XRP in degrees
+   */
+  public double getGyroAngleZ() {
+    return m_gyro.getAngleZ();
+  }
+
+  /** Reset the gyro. */
+  public void resetGyro() {
+    m_gyro.reset();
+  }
+
+  @Override
+  public void periodic() {
+    // This method will be called once per scheduler run
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/Autos.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/Autos.java
index 330a4ae..1126d8d 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/Autos.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/Autos.java
@@ -5,12 +5,12 @@
 package edu.wpi.first.wpilibj.templates.commandbased.commands;
 
 import edu.wpi.first.wpilibj.templates.commandbased.subsystems.ExampleSubsystem;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.Commands;
 
 public final class Autos {
   /** Example static factory for an autonomous command. */
-  public static CommandBase exampleAuto(ExampleSubsystem subsystem) {
+  public static Command exampleAuto(ExampleSubsystem subsystem) {
     return Commands.sequence(subsystem.exampleMethodCommand(), new ExampleCommand(subsystem));
   }
 
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/ExampleCommand.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/ExampleCommand.java
index 194cbf6..181de6d 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/ExampleCommand.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/ExampleCommand.java
@@ -5,10 +5,10 @@
 package edu.wpi.first.wpilibj.templates.commandbased.commands;
 
 import edu.wpi.first.wpilibj.templates.commandbased.subsystems.ExampleSubsystem;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
 /** An example command that uses an example subsystem. */
-public class ExampleCommand extends CommandBase {
+public class ExampleCommand extends Command {
   @SuppressWarnings({"PMD.UnusedPrivateField", "PMD.SingularField"})
   private final ExampleSubsystem m_subsystem;
 
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/subsystems/ExampleSubsystem.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/subsystems/ExampleSubsystem.java
index 57d86cc..1c62fdb 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/subsystems/ExampleSubsystem.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/subsystems/ExampleSubsystem.java
@@ -4,7 +4,7 @@
 
 package edu.wpi.first.wpilibj.templates.commandbased.subsystems;
 
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 import edu.wpi.first.wpilibj2.command.SubsystemBase;
 
 public class ExampleSubsystem extends SubsystemBase {
@@ -16,7 +16,7 @@
    *
    * @return a command
    */
-  public CommandBase exampleMethodCommand() {
+  public Command exampleMethodCommand() {
     // Inline construction of command goes here.
     // Subsystem::RunOnce implicitly requires `this` subsystem.
     return runOnce(
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/commands/ExampleCommand.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/commands/ExampleCommand.java
index 0ebb5d2..c05a53a 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/commands/ExampleCommand.java
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/commands/ExampleCommand.java
@@ -5,10 +5,10 @@
 package edu.wpi.first.wpilibj.templates.romicommandbased.commands;
 
 import edu.wpi.first.wpilibj.templates.romicommandbased.subsystems.RomiDrivetrain;
-import edu.wpi.first.wpilibj2.command.CommandBase;
+import edu.wpi.first.wpilibj2.command.Command;
 
 /** An example command that uses an example subsystem. */
-public class ExampleCommand extends CommandBase {
+public class ExampleCommand extends Command {
   @SuppressWarnings({"PMD.UnusedPrivateField", "PMD.SingularField"})
   private final RomiDrivetrain m_subsystem;
 
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/templates.json b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/templates.json
index 68e3bfb..c01dc17 100644
--- a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/templates.json
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/templates.json
@@ -1,7 +1,7 @@
 [
   {
     "name": "Command Robot",
-    "description": "Command style",
+    "description": "Command-based, with explanatory comments and example code.",
     "tags": [
       "Command"
     ],
@@ -12,9 +12,10 @@
   },
   {
     "name": "Command Robot Skeleton (Advanced)",
-    "description": "Skeleton (stub) code for Command Robot",
+    "description": "Skeleton (stub) code for Command-based, without explanatory comments and example code.",
     "tags": [
-      "Command", "Skeleton"
+      "Command",
+      "Skeleton"
     ],
     "foldername": "commandbasedskeleton",
     "gradlebase": "java",
@@ -23,7 +24,7 @@
   },
   {
     "name": "Timed Robot",
-    "description": "Timed style",
+    "description": "Timed style, with explanatory comments and example code.",
     "tags": [
       "Timed"
     ],
@@ -34,7 +35,7 @@
   },
   {
     "name": "Timed Skeleton (Advanced)",
-    "description": "Skeleton (stub) code for TimedRobot",
+    "description": "Skeleton (stub) code for TimedRobot, without explanatory comments and example code.",
     "tags": [
       "Timed",
       "Skeleton"
@@ -45,8 +46,31 @@
     "commandversion": 2
   },
   {
+    "name": "Timeslice Robot",
+    "description": "Timeslice style",
+    "tags": [
+      "Timeslice"
+    ],
+    "foldername": "timeslice",
+    "gradlebase": "java",
+    "mainclass": "Main",
+    "commandversion": 2
+  },
+  {
+    "name": "Timeslice Skeleton (Advanced)",
+    "description": "Skeleton (stub) code for TimesliceRobot",
+    "tags": [
+      "Timeslice",
+      "Skeleton"
+    ],
+    "foldername": "timesliceskeleton",
+    "gradlebase": "java",
+    "mainclass": "Main",
+    "commandversion": 2
+  },
+  {
     "name": "RobotBase Skeleton (Advanced)",
-    "description": "Skeleton (stub) code for RobotBase",
+    "description": "Skeleton (stub) code for RobotBase - Not recommended for competition use",
     "tags": [
       "RobotBase",
       "Skeleton"
@@ -66,7 +90,10 @@
     "foldername": "romicommandbased",
     "gradlebase": "javaromi",
     "mainclass": "Main",
-    "commandversion": 2
+    "commandversion": 2,
+    "extravendordeps": [
+      "romi"
+    ]
   },
   {
     "name": "Romi - Timed Robot",
@@ -78,7 +105,40 @@
     "foldername": "romitimed",
     "gradlebase": "javaromi",
     "mainclass": "Main",
-    "commandversion": 2
+    "commandversion": 2,
+    "extravendordeps": [
+      "romi"
+    ]
+  },
+  {
+    "name": "XRP - Command Robot",
+    "description": "XRP - Command style",
+    "tags": [
+      "Command",
+      "XRP"
+    ],
+    "foldername": "xrpcommandbased",
+    "gradlebase": "javaxrp",
+    "mainclass": "Main",
+    "commandversion": 2,
+    "extravendordeps": [
+      "xrp"
+    ]
+  },
+  {
+    "name": "XRP - Timed Robot",
+    "description": "XRP - Timed style",
+    "tags": [
+      "Timed",
+      "XRP"
+    ],
+    "foldername": "xrptimed",
+    "gradlebase": "javaxrp",
+    "mainclass": "Main",
+    "commandversion": 2,
+    "extravendordeps": [
+      "xrp"
+    ]
   },
   {
     "name": "Educational Robot",
@@ -101,6 +161,24 @@
     "foldername": "romieducational",
     "gradlebase": "javaromi",
     "mainclass": "Main",
-    "commandversion": 2
+    "commandversion": 2,
+    "extravendordeps": [
+      "romi"
+    ]
+  },
+  {
+    "name": "XRP - Educational Robot",
+    "description": "XRP - Educational Robot",
+    "tags": [
+      "Educational",
+      "XRP"
+    ],
+    "foldername": "xrpeducational",
+    "gradlebase": "javaxrp",
+    "mainclass": "Main",
+    "commandversion": 2,
+    "extravendordeps": [
+      "xrp"
+    ]
   }
 ]
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timeslice/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timeslice/Main.java
new file mode 100644
index 0000000..313d179
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timeslice/Main.java
@@ -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.
+
+package edu.wpi.first.wpilibj.templates.timeslice;
+
+import edu.wpi.first.wpilibj.RobotBase;
+
+/**
+ * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
+ * you are doing, do not modify this file except to change the parameter class to the startRobot
+ * call.
+ */
+public final class Main {
+  private Main() {}
+
+  /**
+   * Main initialization function. Do not perform any initialization here.
+   *
+   * <p>If you change your main robot class, change the parameter type.
+   */
+  public static void main(String... args) {
+    RobotBase.startRobot(Robot::new);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timeslice/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timeslice/Robot.java
new file mode 100644
index 0000000..661a06b
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timeslice/Robot.java
@@ -0,0 +1,118 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.timeslice;
+
+import edu.wpi.first.wpilibj.TimesliceRobot;
+import edu.wpi.first.wpilibj.livewindow.LiveWindow;
+import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
+
+/**
+ * The VM is configured to automatically run this class, and to call the functions corresponding to
+ * each mode, as described in the TimesliceRobot documentation. If you change the name of this class
+ * or the package after creating this project, you must also update the build.gradle file in the
+ * project.
+ */
+public class Robot extends TimesliceRobot {
+  private static final String kDefaultAuto = "Default";
+  private static final String kCustomAuto = "My Auto";
+  private String m_autoSelected;
+  private final SendableChooser<String> m_chooser = new SendableChooser<>();
+
+  /** Robot constructor. */
+  public Robot() {
+    // Run robot periodic() functions for 5 ms, and run controllers every 10 ms
+    super(0.005, 0.01);
+
+    // LiveWindow causes drastic overruns in robot periodic functions that will
+    // interfere with controllers
+    LiveWindow.disableAllTelemetry();
+
+    // Runs for 2 ms after robot periodic functions
+    schedule(() -> {}, 0.002);
+
+    // Runs for 2 ms after first controller function
+    schedule(() -> {}, 0.002);
+
+    // Total usage: 5 ms (robot) + 2 ms (controller 1) + 2 ms (controller 2)
+    // = 9 ms -> 90% allocated
+  }
+
+  /**
+   * This function is run when the robot is first started up and should be used for any
+   * initialization code.
+   */
+  @Override
+  public void robotInit() {
+    m_chooser.setDefaultOption("Default Auto", kDefaultAuto);
+    m_chooser.addOption("My Auto", kCustomAuto);
+    SmartDashboard.putData("Auto choices", m_chooser);
+  }
+
+  /**
+   * This function is called every robot packet, no matter the mode. Use this for items like
+   * diagnostics that you want ran during disabled, autonomous, teleoperated and test.
+   *
+   * <p>This runs after the mode specific periodic functions, but before LiveWindow and
+   * SmartDashboard integrated updating.
+   */
+  @Override
+  public void robotPeriodic() {}
+
+  /**
+   * This autonomous (along with the chooser code above) shows how to select between different
+   * autonomous modes using the dashboard. The sendable chooser code works with the Java
+   * SmartDashboard. If you prefer the LabVIEW Dashboard, remove all of the chooser code and
+   * uncomment the getString line to get the auto name from the text box below the Gyro
+   *
+   * <p>You can add additional auto modes by adding additional comparisons to the switch structure
+   * below with additional strings. If using the SendableChooser make sure to add them to the
+   * chooser code above as well.
+   */
+  @Override
+  public void autonomousInit() {
+    m_autoSelected = m_chooser.getSelected();
+    // m_autoSelected = SmartDashboard.getString("Auto Selector", kDefaultAuto);
+    System.out.println("Auto selected: " + m_autoSelected);
+  }
+
+  /** This function is called periodically during autonomous. */
+  @Override
+  public void autonomousPeriodic() {
+    switch (m_autoSelected) {
+      case kCustomAuto:
+        // Put custom auto code here
+        break;
+      case kDefaultAuto:
+      default:
+        // Put default auto code here
+        break;
+    }
+  }
+
+  /** This function is called once when teleop is enabled. */
+  @Override
+  public void teleopInit() {}
+
+  /** This function is called periodically during operator control. */
+  @Override
+  public void teleopPeriodic() {}
+
+  /** This function is called once when the robot is disabled. */
+  @Override
+  public void disabledInit() {}
+
+  /** This function is called periodically when disabled. */
+  @Override
+  public void disabledPeriodic() {}
+
+  /** This function is called once when test mode is enabled. */
+  @Override
+  public void testInit() {}
+
+  /** This function is called periodically during test mode. */
+  @Override
+  public void testPeriodic() {}
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timesliceskeleton/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timesliceskeleton/Main.java
new file mode 100644
index 0000000..bc92fc4
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timesliceskeleton/Main.java
@@ -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.
+
+package edu.wpi.first.wpilibj.templates.timesliceskeleton;
+
+import edu.wpi.first.wpilibj.RobotBase;
+
+/**
+ * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
+ * you are doing, do not modify this file except to change the parameter class to the startRobot
+ * call.
+ */
+public final class Main {
+  private Main() {}
+
+  /**
+   * Main initialization function. Do not perform any initialization here.
+   *
+   * <p>If you change your main robot class, change the parameter type.
+   */
+  public static void main(String... args) {
+    RobotBase.startRobot(Robot::new);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timesliceskeleton/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timesliceskeleton/Robot.java
new file mode 100644
index 0000000..37ea2b5
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timesliceskeleton/Robot.java
@@ -0,0 +1,70 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.timesliceskeleton;
+
+import edu.wpi.first.wpilibj.TimesliceRobot;
+import edu.wpi.first.wpilibj.livewindow.LiveWindow;
+
+/**
+ * The VM is configured to automatically run this class, and to call the functions corresponding to
+ * each mode, as described in the TimesliceRobot documentation. If you change the name of this class
+ * or the package after creating this project, you must also update the build.gradle file in the
+ * project.
+ */
+public class Robot extends TimesliceRobot {
+  /** Robot constructor. */
+  public Robot() {
+    // Run robot periodic() functions for 5 ms, and run controllers every 10 ms
+    super(0.005, 0.01);
+
+    // LiveWindow causes drastic overruns in robot periodic functions that will
+    // interfere with controllers
+    LiveWindow.disableAllTelemetry();
+
+    // Runs for 2 ms after robot periodic functions
+    schedule(() -> {}, 0.002);
+
+    // Runs for 2 ms after first controller function
+    schedule(() -> {}, 0.002);
+
+    // Total usage:
+    // 5 ms (robot) + 2 ms (controller 1) + 2 ms (controller 2) = 9 ms
+    // 9 ms / 10 ms -> 90% allocated
+  }
+
+  /**
+   * This function is run when the robot is first started up and should be used for any
+   * initialization code.
+   */
+  @Override
+  public void robotInit() {}
+
+  @Override
+  public void robotPeriodic() {}
+
+  @Override
+  public void autonomousInit() {}
+
+  @Override
+  public void autonomousPeriodic() {}
+
+  @Override
+  public void teleopInit() {}
+
+  @Override
+  public void teleopPeriodic() {}
+
+  @Override
+  public void disabledInit() {}
+
+  @Override
+  public void disabledPeriodic() {}
+
+  @Override
+  public void testInit() {}
+
+  @Override
+  public void testPeriodic() {}
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/Constants.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/Constants.java
new file mode 100644
index 0000000..cd72c67
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/Constants.java
@@ -0,0 +1,15 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.xrpcommandbased;
+
+/**
+ * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean
+ * constants. This class should not be used for any other purpose. All constants should be declared
+ * globally (i.e. public static). Do not put anything functional in this class.
+ *
+ * <p>It is advised to statically import this class (or one of its inner classes) wherever the
+ * constants are needed, to reduce verbosity.
+ */
+public final class Constants {}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/Main.java
new file mode 100644
index 0000000..babb187
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/Main.java
@@ -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.
+
+package edu.wpi.first.wpilibj.templates.xrpcommandbased;
+
+import edu.wpi.first.wpilibj.RobotBase;
+
+/**
+ * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
+ * you are doing, do not modify this file except to change the parameter class to the startRobot
+ * call.
+ */
+public final class Main {
+  private Main() {}
+
+  /**
+   * Main initialization function. Do not perform any initialization here.
+   *
+   * <p>If you change your main robot class, change the parameter type.
+   */
+  public static void main(String... args) {
+    RobotBase.startRobot(Robot::new);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/Robot.java
new file mode 100644
index 0000000..f7950f5
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/Robot.java
@@ -0,0 +1,95 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.xrpcommandbased;
+
+import edu.wpi.first.wpilibj.TimedRobot;
+import edu.wpi.first.wpilibj2.command.Command;
+import edu.wpi.first.wpilibj2.command.CommandScheduler;
+
+/**
+ * The VM is configured to automatically run this class, and to call the functions corresponding to
+ * each mode, as described in the TimedRobot documentation. If you change the name of this class or
+ * the package after creating this project, you must also update the build.gradle file in the
+ * project.
+ */
+public class Robot extends TimedRobot {
+  private Command m_autonomousCommand;
+
+  private RobotContainer m_robotContainer;
+
+  /**
+   * This function is run when the robot is first started up and should be used for any
+   * initialization code.
+   */
+  @Override
+  public void robotInit() {
+    // Instantiate our RobotContainer.  This will perform all our button bindings, and put our
+    // autonomous chooser on the dashboard.
+    m_robotContainer = new RobotContainer();
+  }
+
+  /**
+   * This function is called every 20 ms, no matter the mode. Use this for items like diagnostics
+   * that you want ran during disabled, autonomous, teleoperated and test.
+   *
+   * <p>This runs after the mode specific periodic functions, but before LiveWindow and
+   * SmartDashboard integrated updating.
+   */
+  @Override
+  public void robotPeriodic() {
+    // Runs the Scheduler.  This is responsible for polling buttons, adding newly-scheduled
+    // commands, running already-scheduled commands, removing finished or interrupted commands,
+    // and running subsystem periodic() methods.  This must be called from the robot's periodic
+    // block in order for anything in the Command-based framework to work.
+    CommandScheduler.getInstance().run();
+  }
+
+  /** This function is called once each time the robot enters Disabled mode. */
+  @Override
+  public void disabledInit() {}
+
+  @Override
+  public void disabledPeriodic() {}
+
+  /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */
+  @Override
+  public void autonomousInit() {
+    m_autonomousCommand = m_robotContainer.getAutonomousCommand();
+
+    // schedule the autonomous command (example)
+    if (m_autonomousCommand != null) {
+      m_autonomousCommand.schedule();
+    }
+  }
+
+  /** This function is called periodically during autonomous. */
+  @Override
+  public void autonomousPeriodic() {}
+
+  @Override
+  public void teleopInit() {
+    // This makes sure that the autonomous stops running when
+    // teleop starts running. If you want the autonomous to
+    // continue until interrupted by another command, remove
+    // this line or comment it out.
+    if (m_autonomousCommand != null) {
+      m_autonomousCommand.cancel();
+    }
+  }
+
+  /** This function is called periodically during operator control. */
+  @Override
+  public void teleopPeriodic() {}
+
+  @Override
+  public void testInit() {
+    // Cancels all running commands at the start of test mode.
+    CommandScheduler.getInstance().cancelAll();
+  }
+
+  /** This function is called periodically during test mode. */
+  @Override
+  public void testPeriodic() {}
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/RobotContainer.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/RobotContainer.java
new file mode 100644
index 0000000..e6059ca
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/RobotContainer.java
@@ -0,0 +1,47 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.xrpcommandbased;
+
+import edu.wpi.first.wpilibj.XboxController;
+import edu.wpi.first.wpilibj.templates.xrpcommandbased.commands.ExampleCommand;
+import edu.wpi.first.wpilibj.templates.xrpcommandbased.subsystems.XRPDrivetrain;
+import edu.wpi.first.wpilibj2.command.Command;
+
+/**
+ * This class is where the bulk of the robot should be declared. Since Command-based is a
+ * "declarative" paradigm, very little robot logic should actually be handled in the {@link Robot}
+ * periodic methods (other than the scheduler calls). Instead, the structure of the robot (including
+ * subsystems, commands, and button mappings) should be declared here.
+ */
+public class RobotContainer {
+  // The robot's subsystems and commands are defined here...
+  private final XRPDrivetrain m_xrpDrivetrain = new XRPDrivetrain();
+
+  private final ExampleCommand m_autoCommand = new ExampleCommand(m_xrpDrivetrain);
+
+  /** The container for the robot. Contains subsystems, OI devices, and commands. */
+  public RobotContainer() {
+    // Configure the button bindings
+    configureButtonBindings();
+  }
+
+  /**
+   * Use this method to define your button->command mappings. Buttons can be created by
+   * instantiating a {@link edu.wpi.first.wpilibj.GenericHID} or one of its subclasses ({@link
+   * edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a {@link
+   * edu.wpi.first.wpilibj2.command.button.JoystickButton}.
+   */
+  private void configureButtonBindings() {}
+
+  /**
+   * Use this to pass the autonomous command to the main {@link Robot} class.
+   *
+   * @return the command to run in autonomous
+   */
+  public Command getAutonomousCommand() {
+    // An ExampleCommand will run in autonomous
+    return m_autoCommand;
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/commands/ExampleCommand.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/commands/ExampleCommand.java
new file mode 100644
index 0000000..27f759a
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/commands/ExampleCommand.java
@@ -0,0 +1,43 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.xrpcommandbased.commands;
+
+import edu.wpi.first.wpilibj.templates.xrpcommandbased.subsystems.XRPDrivetrain;
+import edu.wpi.first.wpilibj2.command.Command;
+
+/** An example command that uses an example subsystem. */
+public class ExampleCommand extends Command {
+  @SuppressWarnings({"PMD.UnusedPrivateField", "PMD.SingularField"})
+  private final XRPDrivetrain m_subsystem;
+
+  /**
+   * Creates a new ExampleCommand.
+   *
+   * @param subsystem The subsystem used by this command.
+   */
+  public ExampleCommand(XRPDrivetrain subsystem) {
+    m_subsystem = subsystem;
+    // Use addRequirements() here to declare subsystem dependencies.
+    addRequirements(subsystem);
+  }
+
+  // Called when the command is initially scheduled.
+  @Override
+  public void initialize() {}
+
+  // Called every time the scheduler runs while the command is scheduled.
+  @Override
+  public void execute() {}
+
+  // Called once the command ends or is interrupted.
+  @Override
+  public void end(boolean interrupted) {}
+
+  // Returns true when the command should end.
+  @Override
+  public boolean isFinished() {
+    return false;
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/subsystems/XRPDrivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/subsystems/XRPDrivetrain.java
new file mode 100644
index 0000000..a3a1063
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/subsystems/XRPDrivetrain.java
@@ -0,0 +1,69 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.xrpcommandbased.subsystems;
+
+import edu.wpi.first.wpilibj.Encoder;
+import edu.wpi.first.wpilibj.drive.DifferentialDrive;
+import edu.wpi.first.wpilibj.xrp.XRPMotor;
+import edu.wpi.first.wpilibj2.command.SubsystemBase;
+
+public class XRPDrivetrain extends SubsystemBase {
+  private static final double kGearRatio =
+      (30.0 / 14.0) * (28.0 / 16.0) * (36.0 / 9.0) * (26.0 / 8.0); // 48.75:1
+  private static final double kCountsPerMotorShaftRev = 12.0;
+  private static final double kCountsPerRevolution = kCountsPerMotorShaftRev * kGearRatio; // 585.0
+  private static final double kWheelDiameterInch = 2.3622; // 60 mm
+
+  // The XRP has the left and right motors set to
+  // channels 0 and 1 respectively
+  private final XRPMotor m_leftMotor = new XRPMotor(0);
+  private final XRPMotor m_rightMotor = new XRPMotor(1);
+
+  // The XRP has onboard encoders that are hardcoded
+  // to use DIO pins 4/5 and 6/7 for the left and right
+  private final Encoder m_leftEncoder = new Encoder(4, 5);
+  private final Encoder m_rightEncoder = new Encoder(6, 7);
+
+  // Set up the differential drive controller
+  private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor);
+
+  /** Creates a new XRPDrivetrain. */
+  public XRPDrivetrain() {
+    // Use inches as unit for encoder distances
+    m_leftEncoder.setDistancePerPulse((Math.PI * kWheelDiameterInch) / kCountsPerRevolution);
+    m_rightEncoder.setDistancePerPulse((Math.PI * kWheelDiameterInch) / kCountsPerRevolution);
+    resetEncoders();
+
+    // Invert right side since motor is flipped
+    m_rightMotor.setInverted(true);
+  }
+
+  public void arcadeDrive(double xaxisSpeed, double zaxisRotate) {
+    m_diffDrive.arcadeDrive(xaxisSpeed, zaxisRotate);
+  }
+
+  public void resetEncoders() {
+    m_leftEncoder.reset();
+    m_rightEncoder.reset();
+  }
+
+  public double getLeftDistanceInch() {
+    return m_leftEncoder.getDistance();
+  }
+
+  public double getRightDistanceInch() {
+    return m_rightEncoder.getDistance();
+  }
+
+  @Override
+  public void periodic() {
+    // This method will be called once per scheduler run
+  }
+
+  @Override
+  public void simulationPeriodic() {
+    // This method will be called once per scheduler run during simulation
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/EducationalRobot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/EducationalRobot.java
new file mode 100644
index 0000000..4b44707
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/EducationalRobot.java
@@ -0,0 +1,104 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.xrpeducational;
+
+import edu.wpi.first.hal.DriverStationJNI;
+import edu.wpi.first.util.WPIUtilJNI;
+import edu.wpi.first.wpilibj.DriverStation;
+import edu.wpi.first.wpilibj.RobotBase;
+import edu.wpi.first.wpilibj.internal.DriverStationModeThread;
+
+/** Educational robot base class. */
+public class EducationalRobot extends RobotBase {
+  public void robotInit() {}
+
+  public void disabled() {}
+
+  public void run() {}
+
+  public void autonomous() {
+    run();
+  }
+
+  public void teleop() {
+    run();
+  }
+
+  public void test() {
+    run();
+  }
+
+  private volatile boolean m_exit;
+
+  @Override
+  public void startCompetition() {
+    robotInit();
+
+    DriverStationModeThread modeThread = new DriverStationModeThread();
+
+    int event = WPIUtilJNI.createEvent(false, false);
+
+    DriverStation.provideRefreshedDataEventHandle(event);
+
+    // Tell the DS that the robot is ready to be enabled
+    DriverStationJNI.observeUserProgramStarting();
+
+    while (!Thread.currentThread().isInterrupted() && !m_exit) {
+      if (isDisabled()) {
+        modeThread.inDisabled(true);
+        disabled();
+        modeThread.inDisabled(false);
+        while (isDisabled()) {
+          try {
+            WPIUtilJNI.waitForObject(event);
+          } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+          }
+        }
+      } else if (isAutonomous()) {
+        modeThread.inAutonomous(true);
+        autonomous();
+        modeThread.inAutonomous(false);
+        while (isAutonomousEnabled()) {
+          try {
+            WPIUtilJNI.waitForObject(event);
+          } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+          }
+        }
+      } else if (isTest()) {
+        modeThread.inTest(true);
+        test();
+        modeThread.inTest(false);
+        while (isTest() && isEnabled()) {
+          try {
+            WPIUtilJNI.waitForObject(event);
+          } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+          }
+        }
+      } else {
+        modeThread.inTeleop(true);
+        teleop();
+        modeThread.inTeleop(false);
+        while (isTeleopEnabled()) {
+          try {
+            WPIUtilJNI.waitForObject(event);
+          } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+          }
+        }
+      }
+    }
+
+    DriverStation.removeRefreshedDataEventHandle(event);
+    modeThread.close();
+  }
+
+  @Override
+  public void endCompetition() {
+    m_exit = true;
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/Main.java
new file mode 100644
index 0000000..dbb4d81
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/Main.java
@@ -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.
+
+package edu.wpi.first.wpilibj.templates.xrpeducational;
+
+import edu.wpi.first.wpilibj.RobotBase;
+
+/**
+ * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
+ * you are doing, do not modify this file except to change the parameter class to the startRobot
+ * call.
+ */
+public final class Main {
+  private Main() {}
+
+  /**
+   * Main initialization function. Do not perform any initialization here.
+   *
+   * <p>If you change your main robot class, change the parameter type.
+   */
+  public static void main(String... args) {
+    RobotBase.startRobot(Robot::new);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/Robot.java
new file mode 100644
index 0000000..66c2806
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/Robot.java
@@ -0,0 +1,23 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.xrpeducational;
+
+/**
+ * The VM is configured to automatically run this class, and to call the run() function when the
+ * robot is enabled. If you change the name of this class or the package after creating this
+ * project, you must also update the build.gradle file in the project.
+ */
+public class Robot extends EducationalRobot {
+  /**
+   * This function is run when the robot is first started up and should be used for any
+   * initialization code.
+   */
+  @Override
+  public void robotInit() {}
+
+  /** This function is run when the robot is enabled. */
+  @Override
+  public void run() {}
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/XRPDrivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/XRPDrivetrain.java
new file mode 100644
index 0000000..8f73bae
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/XRPDrivetrain.java
@@ -0,0 +1,58 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.xrpeducational;
+
+import edu.wpi.first.wpilibj.Encoder;
+import edu.wpi.first.wpilibj.drive.DifferentialDrive;
+import edu.wpi.first.wpilibj.xrp.XRPMotor;
+
+public class XRPDrivetrain {
+  private static final double kGearRatio =
+      (30.0 / 14.0) * (28.0 / 16.0) * (36.0 / 9.0) * (26.0 / 8.0); // 48.75:1
+  private static final double kCountsPerMotorShaftRev = 12.0;
+  private static final double kCountsPerRevolution = kCountsPerMotorShaftRev * kGearRatio; // 585.0
+  private static final double kWheelDiameterInch = 2.3622; // 60 mm
+
+  // The XRP has the left and right motors set to
+  // channels 0 and 1 respectively
+  private final XRPMotor m_leftMotor = new XRPMotor(0);
+  private final XRPMotor m_rightMotor = new XRPMotor(1);
+
+  // The XRP has onboard encoders that are hardcoded
+  // to use DIO pins 4/5 and 6/7 for the left and right
+  private final Encoder m_leftEncoder = new Encoder(4, 5);
+  private final Encoder m_rightEncoder = new Encoder(6, 7);
+
+  // Set up the differential drive controller
+  private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor);
+
+  /** Creates a new XRPDrivetrain. */
+  public XRPDrivetrain() {
+    // Use inches as unit for encoder distances
+    m_leftEncoder.setDistancePerPulse((Math.PI * kWheelDiameterInch) / kCountsPerRevolution);
+    m_rightEncoder.setDistancePerPulse((Math.PI * kWheelDiameterInch) / kCountsPerRevolution);
+    resetEncoders();
+
+    // Invert right side since motor is flipped
+    m_rightMotor.setInverted(true);
+  }
+
+  public void arcadeDrive(double xaxisSpeed, double zaxisRotate) {
+    m_diffDrive.arcadeDrive(xaxisSpeed, zaxisRotate);
+  }
+
+  public void resetEncoders() {
+    m_leftEncoder.reset();
+    m_rightEncoder.reset();
+  }
+
+  public double getLeftDistanceInch() {
+    return m_leftEncoder.getDistance();
+  }
+
+  public double getRightDistanceInch() {
+    return m_rightEncoder.getDistance();
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/Main.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/Main.java
new file mode 100644
index 0000000..6405cdc
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/Main.java
@@ -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.
+
+package edu.wpi.first.wpilibj.templates.xrptimed;
+
+import edu.wpi.first.wpilibj.RobotBase;
+
+/**
+ * Do NOT add any static variables to this class, or any initialization at all. Unless you know what
+ * you are doing, do not modify this file except to change the parameter class to the startRobot
+ * call.
+ */
+public final class Main {
+  private Main() {}
+
+  /**
+   * Main initialization function. Do not perform any initialization here.
+   *
+   * <p>If you change your main robot class, change the parameter type.
+   */
+  public static void main(String... args) {
+    RobotBase.startRobot(Robot::new);
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/Robot.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/Robot.java
new file mode 100644
index 0000000..f3b6d57
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/Robot.java
@@ -0,0 +1,102 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.xrptimed;
+
+import edu.wpi.first.wpilibj.TimedRobot;
+import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
+
+/**
+ * The VM is configured to automatically run this class, and to call the functions corresponding to
+ * each mode, as described in the TimedRobot documentation. If you change the name of this class or
+ * the package after creating this project, you must also update the build.gradle file in the
+ * project.
+ */
+public class Robot extends TimedRobot {
+  private static final String kDefaultAuto = "Default";
+  private static final String kCustomAuto = "My Auto";
+  private String m_autoSelected;
+  private final SendableChooser<String> m_chooser = new SendableChooser<>();
+
+  private final XRPDrivetrain m_drivetrain = new XRPDrivetrain();
+
+  /**
+   * This function is run when the robot is first started up and should be used for any
+   * initialization code.
+   */
+  @Override
+  public void robotInit() {
+    m_chooser.setDefaultOption("Default Auto", kDefaultAuto);
+    m_chooser.addOption("My Auto", kCustomAuto);
+    SmartDashboard.putData("Auto choices", m_chooser);
+  }
+
+  /**
+   * This function is called every 20 ms, no matter the mode. Use this for items like diagnostics
+   * that you want ran during disabled, autonomous, teleoperated and test.
+   *
+   * <p>This runs after the mode specific periodic functions, but before LiveWindow and
+   * SmartDashboard integrated updating.
+   */
+  @Override
+  public void robotPeriodic() {}
+
+  /**
+   * This autonomous (along with the chooser code above) shows how to select between different
+   * autonomous modes using the dashboard. The sendable chooser code works with the Java
+   * SmartDashboard. If you prefer the LabVIEW Dashboard, remove all of the chooser code and
+   * uncomment the getString line to get the auto name from the text box below the Gyro
+   *
+   * <p>You can add additional auto modes by adding additional comparisons to the switch structure
+   * below with additional strings. If using the SendableChooser make sure to add them to the
+   * chooser code above as well.
+   */
+  @Override
+  public void autonomousInit() {
+    m_autoSelected = m_chooser.getSelected();
+    // m_autoSelected = SmartDashboard.getString("Auto Selector", kDefaultAuto);
+    System.out.println("Auto selected: " + m_autoSelected);
+
+    m_drivetrain.resetEncoders();
+  }
+
+  /** This function is called periodically during autonomous. */
+  @Override
+  public void autonomousPeriodic() {
+    switch (m_autoSelected) {
+      case kCustomAuto:
+        // Put custom auto code here
+        break;
+      case kDefaultAuto:
+      default:
+        // Put default auto code here
+        break;
+    }
+  }
+
+  /** This function is called once when teleop is enabled. */
+  @Override
+  public void teleopInit() {}
+
+  /** This function is called periodically during operator control. */
+  @Override
+  public void teleopPeriodic() {}
+
+  /** This function is called once when the robot is disabled. */
+  @Override
+  public void disabledInit() {}
+
+  /** This function is called periodically when disabled. */
+  @Override
+  public void disabledPeriodic() {}
+
+  /** This function is called once when test mode is enabled. */
+  @Override
+  public void testInit() {}
+
+  /** This function is called periodically during test mode. */
+  @Override
+  public void testPeriodic() {}
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/XRPDrivetrain.java b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/XRPDrivetrain.java
new file mode 100644
index 0000000..e66d750
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/XRPDrivetrain.java
@@ -0,0 +1,58 @@
+// 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.
+
+package edu.wpi.first.wpilibj.templates.xrptimed;
+
+import edu.wpi.first.wpilibj.Encoder;
+import edu.wpi.first.wpilibj.drive.DifferentialDrive;
+import edu.wpi.first.wpilibj.xrp.XRPMotor;
+
+public class XRPDrivetrain {
+  private static final double kGearRatio =
+      (30.0 / 14.0) * (28.0 / 16.0) * (36.0 / 9.0) * (26.0 / 8.0); // 48.75:1
+  private static final double kCountsPerMotorShaftRev = 12.0;
+  private static final double kCountsPerRevolution = kCountsPerMotorShaftRev * kGearRatio; // 585.0
+  private static final double kWheelDiameterInch = 2.3622; // 60 mm
+
+  // The XRP has the left and right motors set to
+  // channels 0 and 1 respectively
+  private final XRPMotor m_leftMotor = new XRPMotor(0);
+  private final XRPMotor m_rightMotor = new XRPMotor(1);
+
+  // The XRP has onboard encoders that are hardcoded
+  // to use DIO pins 4/5 and 6/7 for the left and right
+  private final Encoder m_leftEncoder = new Encoder(4, 5);
+  private final Encoder m_rightEncoder = new Encoder(6, 7);
+
+  // Set up the differential drive controller
+  private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor);
+
+  /** Creates a new XRPDrivetrain. */
+  public XRPDrivetrain() {
+    // Use inches as unit for encoder distances
+    m_leftEncoder.setDistancePerPulse((Math.PI * kWheelDiameterInch) / kCountsPerRevolution);
+    m_rightEncoder.setDistancePerPulse((Math.PI * kWheelDiameterInch) / kCountsPerRevolution);
+    resetEncoders();
+
+    // Invert right side since motor is flipped
+    m_rightMotor.setInverted(true);
+  }
+
+  public void arcadeDrive(double xaxisSpeed, double zaxisRotate) {
+    m_diffDrive.arcadeDrive(xaxisSpeed, zaxisRotate);
+  }
+
+  public void resetEncoders() {
+    m_leftEncoder.reset();
+    m_rightEncoder.reset();
+  }
+
+  public double getLeftDistanceInch() {
+    return m_leftEncoder.getDistance();
+  }
+
+  public double getRightDistanceInch() {
+    return m_rightEncoder.getDistance();
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/armsimulation/ArmSimulationTest.java b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/armsimulation/ArmSimulationTest.java
new file mode 100644
index 0000000..eeb5e74
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/armsimulation/ArmSimulationTest.java
@@ -0,0 +1,153 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.armsimulation;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import edu.wpi.first.hal.HAL;
+import edu.wpi.first.math.util.Units;
+import edu.wpi.first.wpilibj.Preferences;
+import edu.wpi.first.wpilibj.simulation.DriverStationSim;
+import edu.wpi.first.wpilibj.simulation.EncoderSim;
+import edu.wpi.first.wpilibj.simulation.JoystickSim;
+import edu.wpi.first.wpilibj.simulation.PWMSim;
+import edu.wpi.first.wpilibj.simulation.RoboRioSim;
+import edu.wpi.first.wpilibj.simulation.SimHooks;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+@ResourceLock("timing")
+class ArmSimulationTest {
+  private Robot m_robot;
+  private Thread m_thread;
+
+  private PWMSim m_motorSim;
+  private EncoderSim m_encoderSim;
+  private JoystickSim m_joystickSim;
+
+  @BeforeEach
+  void startThread() {
+    HAL.initialize(500, 0);
+    SimHooks.pauseTiming();
+    DriverStationSim.resetData();
+    m_robot = new Robot();
+    m_thread = new Thread(m_robot::startCompetition);
+    m_encoderSim = EncoderSim.createForChannel(Constants.kEncoderAChannel);
+    m_motorSim = new PWMSim(Constants.kMotorPort);
+    m_joystickSim = new JoystickSim(Constants.kJoystickPort);
+
+    m_thread.start();
+    SimHooks.stepTiming(0.0); // Wait for Notifiers
+  }
+
+  @AfterEach
+  void stopThread() {
+    m_robot.endCompetition();
+    try {
+      m_thread.interrupt();
+      m_thread.join();
+    } catch (InterruptedException ex) {
+      Thread.currentThread().interrupt();
+    }
+    m_robot.close();
+    m_encoderSim.resetData();
+    m_motorSim.resetData();
+    Preferences.remove(Constants.kArmPKey);
+    Preferences.remove(Constants.kArmPositionKey);
+    Preferences.removeAll();
+    RoboRioSim.resetData();
+    DriverStationSim.resetData();
+    DriverStationSim.notifyNewData();
+  }
+
+  @ValueSource(doubles = {Constants.kDefaultArmSetpointDegrees, 25.0, 50.0})
+  @ParameterizedTest
+  void teleopTest(double setpoint) {
+    assertTrue(Preferences.containsKey(Constants.kArmPositionKey));
+    assertTrue(Preferences.containsKey(Constants.kArmPKey));
+    assertEquals(
+        Constants.kDefaultArmSetpointDegrees,
+        Preferences.getDouble(Constants.kArmPositionKey, Double.NaN));
+
+    Preferences.setDouble(Constants.kArmPositionKey, setpoint);
+    // teleop init
+    {
+      DriverStationSim.setAutonomous(false);
+      DriverStationSim.setEnabled(true);
+      DriverStationSim.notifyNewData();
+
+      assertTrue(m_motorSim.getInitialized());
+      assertTrue(m_encoderSim.getInitialized());
+    }
+
+    {
+      // advance 50 timesteps
+      SimHooks.stepTiming(3);
+
+      // Ensure elevator is still at 0.
+      assertEquals(Constants.kMinAngleRads, m_encoderSim.getDistance(), 2.0);
+    }
+
+    {
+      // Press button to reach setpoint
+      m_joystickSim.setTrigger(true);
+      m_joystickSim.notifyNewData();
+
+      // advance 75 timesteps
+      SimHooks.stepTiming(1.5);
+
+      assertEquals(setpoint, Units.radiansToDegrees(m_encoderSim.getDistance()), 2.0);
+
+      // advance 25 timesteps to see setpoint is held.
+      SimHooks.stepTiming(0.5);
+
+      assertEquals(setpoint, Units.radiansToDegrees(m_encoderSim.getDistance()), 2.0);
+    }
+
+    {
+      // Unpress the button to go back down
+      m_joystickSim.setTrigger(false);
+      m_joystickSim.notifyNewData();
+
+      // advance 75 timesteps
+      SimHooks.stepTiming(3.0);
+
+      assertEquals(Constants.kMinAngleRads, m_encoderSim.getDistance(), 2.0);
+    }
+
+    {
+      // Press button to go back up
+      m_joystickSim.setTrigger(true);
+      m_joystickSim.notifyNewData();
+
+      // advance 75 timesteps
+      SimHooks.stepTiming(1.5);
+
+      assertEquals(setpoint, Units.radiansToDegrees(m_encoderSim.getDistance()), 2.0);
+
+      // advance 25 timesteps to see setpoint is held.
+      SimHooks.stepTiming(0.5);
+
+      assertEquals(setpoint, Units.radiansToDegrees(m_encoderSim.getDistance()), 2.0);
+    }
+
+    {
+      // Disable
+      DriverStationSim.setAutonomous(false);
+      DriverStationSim.setEnabled(false);
+      DriverStationSim.notifyNewData();
+
+      // advance 75 timesteps
+      SimHooks.stepTiming(3.5);
+
+      assertEquals(0.0, m_motorSim.getSpeed(), 0.01);
+      assertEquals(Constants.kMinAngleRads, m_encoderSim.getDistance(), 2.0);
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/digitalcommunication/DigitalCommunicationTest.java b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/digitalcommunication/DigitalCommunicationTest.java
new file mode 100644
index 0000000..9f11dc9
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/digitalcommunication/DigitalCommunicationTest.java
@@ -0,0 +1,114 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.digitalcommunication;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import edu.wpi.first.hal.AllianceStationID;
+import edu.wpi.first.hal.HAL;
+import edu.wpi.first.wpilibj.simulation.DIOSim;
+import edu.wpi.first.wpilibj.simulation.DriverStationSim;
+import edu.wpi.first.wpilibj.simulation.SimHooks;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+@ResourceLock("timing")
+class DigitalCommunicationTest {
+  private Robot m_robot;
+  private Thread m_thread;
+  private final DIOSim m_allianceOutput = new DIOSim(Robot.kAlliancePort);
+  private final DIOSim m_enabledOutput = new DIOSim(Robot.kEnabledPort);
+  private final DIOSim m_autonomousOutput = new DIOSim(Robot.kAutonomousPort);
+  private final DIOSim m_alertOutput = new DIOSim(Robot.kAlertPort);
+
+  @BeforeEach
+  void startThread() {
+    HAL.initialize(500, 0);
+    SimHooks.pauseTiming();
+    DriverStationSim.resetData();
+    m_robot = new Robot();
+    m_thread = new Thread(m_robot::startCompetition);
+    m_thread.start();
+    SimHooks.stepTiming(0.0); // Wait for Notifiers
+  }
+
+  @AfterEach
+  void stopThread() {
+    m_robot.endCompetition();
+    try {
+      m_thread.interrupt();
+      m_thread.join();
+    } catch (InterruptedException ex) {
+      Thread.currentThread().interrupt();
+    }
+    m_robot.close();
+    m_allianceOutput.resetData();
+    m_enabledOutput.resetData();
+    m_autonomousOutput.resetData();
+    m_alertOutput.resetData();
+  }
+
+  @EnumSource(AllianceStationID.class)
+  @ParameterizedTest(name = "alliance[{index}]: {0}")
+  void allianceTest(AllianceStationID alliance) {
+    DriverStationSim.setAllianceStationId(alliance);
+    DriverStationSim.notifyNewData();
+
+    assertTrue(m_allianceOutput.getInitialized());
+    assertFalse(m_allianceOutput.getIsInput());
+
+    SimHooks.stepTiming(0.02);
+
+    assertEquals(alliance.name().startsWith("Red"), m_allianceOutput.getValue());
+  }
+
+  @ValueSource(booleans = {true, false})
+  @ParameterizedTest(name = "enabled[{index}]: {0}")
+  void enabledTest(boolean enabled) {
+    DriverStationSim.setEnabled(enabled);
+    DriverStationSim.notifyNewData();
+
+    assertTrue(m_enabledOutput.getInitialized());
+    assertFalse(m_enabledOutput.getIsInput());
+
+    SimHooks.stepTiming(0.02);
+
+    assertEquals(enabled, m_enabledOutput.getValue());
+  }
+
+  @ValueSource(booleans = {true, false})
+  @ParameterizedTest(name = "autonomous[{index}]: {0}")
+  void autonomousTest(boolean autonomous) {
+    DriverStationSim.setAutonomous(autonomous);
+    DriverStationSim.notifyNewData();
+
+    assertTrue(m_autonomousOutput.getInitialized());
+    assertFalse(m_autonomousOutput.getIsInput());
+
+    SimHooks.stepTiming(0.02);
+
+    assertEquals(autonomous, m_autonomousOutput.getValue());
+  }
+
+  @ValueSource(doubles = {45.0, 27.0, 23.0})
+  @ParameterizedTest(name = "alert[{index}]: {0}s")
+  void alertTest(double matchTime) {
+    DriverStationSim.setMatchTime(matchTime);
+    DriverStationSim.notifyNewData();
+
+    assertTrue(m_alertOutput.getInitialized());
+    assertFalse(m_alertOutput.getIsInput());
+
+    SimHooks.stepTiming(0.02);
+
+    assertEquals(matchTime <= 30 && matchTime >= 25, m_alertOutput.getValue());
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/ElevatorSimulationTest.java b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/ElevatorSimulationTest.java
new file mode 100644
index 0000000..7faeec6
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/ElevatorSimulationTest.java
@@ -0,0 +1,139 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.elevatorsimulation;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import edu.wpi.first.hal.HAL;
+import edu.wpi.first.wpilibj.simulation.DriverStationSim;
+import edu.wpi.first.wpilibj.simulation.EncoderSim;
+import edu.wpi.first.wpilibj.simulation.JoystickSim;
+import edu.wpi.first.wpilibj.simulation.PWMSim;
+import edu.wpi.first.wpilibj.simulation.RoboRioSim;
+import edu.wpi.first.wpilibj.simulation.SimHooks;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+@ResourceLock("timing")
+class ElevatorSimulationTest {
+  private Robot m_robot;
+  private Thread m_thread;
+
+  private PWMSim m_motorSim;
+  private EncoderSim m_encoderSim;
+  private JoystickSim m_joystickSim;
+
+  @BeforeEach
+  void startThread() {
+    HAL.initialize(500, 0);
+    SimHooks.pauseTiming();
+    DriverStationSim.resetData();
+    m_robot = new Robot();
+    m_thread = new Thread(m_robot::startCompetition);
+    m_encoderSim = EncoderSim.createForChannel(Constants.kEncoderAChannel);
+    m_motorSim = new PWMSim(Constants.kMotorPort);
+    m_joystickSim = new JoystickSim(Constants.kJoystickPort);
+
+    m_thread.start();
+    SimHooks.stepTiming(0.0); // Wait for Notifiers
+  }
+
+  @AfterEach
+  void stopThread() {
+    m_robot.endCompetition();
+    try {
+      m_thread.interrupt();
+      m_thread.join();
+    } catch (InterruptedException ex) {
+      Thread.currentThread().interrupt();
+    }
+    m_robot.close();
+    m_encoderSim.resetData();
+    m_motorSim.resetData();
+    RoboRioSim.resetData();
+    DriverStationSim.resetData();
+    DriverStationSim.notifyNewData();
+  }
+
+  @Test
+  void teleopTest() {
+    // teleop init
+    {
+      DriverStationSim.setAutonomous(false);
+      DriverStationSim.setEnabled(true);
+      DriverStationSim.notifyNewData();
+
+      assertTrue(m_motorSim.getInitialized());
+      assertTrue(m_encoderSim.getInitialized());
+    }
+
+    {
+      // advance 50 timesteps
+      SimHooks.stepTiming(1);
+
+      // Ensure elevator is still at 0.
+      assertEquals(0.0, m_encoderSim.getDistance(), 0.05);
+    }
+
+    {
+      // Press button to reach setpoint
+      m_joystickSim.setTrigger(true);
+      m_joystickSim.notifyNewData();
+
+      // advance 75 timesteps
+      SimHooks.stepTiming(1.5);
+
+      assertEquals(Constants.kSetpointMeters, m_encoderSim.getDistance(), 0.05);
+
+      // advance 25 timesteps to see setpoint is held.
+      SimHooks.stepTiming(0.5);
+
+      assertEquals(Constants.kSetpointMeters, m_encoderSim.getDistance(), 0.05);
+    }
+
+    {
+      // Unpress the button to go back down
+      m_joystickSim.setTrigger(false);
+      m_joystickSim.notifyNewData();
+
+      // advance 75 timesteps
+      SimHooks.stepTiming(1.5);
+
+      assertEquals(0.0, m_encoderSim.getDistance(), 0.05);
+    }
+
+    {
+      // Press button to go back up
+      m_joystickSim.setTrigger(true);
+      m_joystickSim.notifyNewData();
+
+      // advance 75 timesteps
+      SimHooks.stepTiming(1.5);
+
+      assertEquals(Constants.kSetpointMeters, m_encoderSim.getDistance(), 0.05);
+
+      // advance 25 timesteps to see setpoint is held.
+      SimHooks.stepTiming(0.5);
+
+      assertEquals(Constants.kSetpointMeters, m_encoderSim.getDistance(), 0.05);
+    }
+
+    {
+      // Disable
+      DriverStationSim.setAutonomous(false);
+      DriverStationSim.setEnabled(false);
+      DriverStationSim.notifyNewData();
+
+      // advance 75 timesteps
+      SimHooks.stepTiming(1.5);
+
+      assertEquals(0.0, m_motorSim.getSpeed(), 0.05);
+      assertEquals(0.0, m_encoderSim.getDistance(), 0.05);
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/i2ccommunication/I2CCommunicationTest.java b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/i2ccommunication/I2CCommunicationTest.java
new file mode 100644
index 0000000..278f97f
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/i2ccommunication/I2CCommunicationTest.java
@@ -0,0 +1,129 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.i2ccommunication;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import edu.wpi.first.hal.AllianceStationID;
+import edu.wpi.first.hal.HAL;
+import edu.wpi.first.wpilibj.DriverStation;
+import edu.wpi.first.wpilibj.simulation.CallbackStore;
+import edu.wpi.first.wpilibj.simulation.DriverStationSim;
+import edu.wpi.first.wpilibj.simulation.I2CSim;
+import edu.wpi.first.wpilibj.simulation.SimHooks;
+import java.time.Duration;
+import java.util.concurrent.CompletableFuture;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+@ResourceLock("timing")
+class I2CCommunicationTest {
+  private Robot m_robot;
+  private Thread m_thread;
+  private final I2CSim m_i2c = new I2CSim(Robot.kPort.value);
+  private CompletableFuture<String> m_future;
+  private CallbackStore m_callback;
+
+  @BeforeEach
+  void startThread() {
+    HAL.initialize(500, 0);
+    SimHooks.pauseTiming();
+    DriverStationSim.resetData();
+    m_future = new CompletableFuture<>();
+    m_callback =
+        m_i2c.registerWriteCallback(
+            (name, buffer, count) -> m_future.complete(new String(buffer, 0, count)));
+    m_robot = new Robot();
+    m_thread = new Thread(m_robot::startCompetition);
+    m_thread.start();
+    SimHooks.stepTiming(0.0); // Wait for Notifiers
+  }
+
+  @AfterEach
+  void stopThread() {
+    m_robot.endCompetition();
+    try {
+      m_thread.interrupt();
+      m_thread.join();
+    } catch (InterruptedException ex) {
+      Thread.currentThread().interrupt();
+    }
+    m_robot.close();
+    m_callback.close();
+    m_i2c.resetData();
+  }
+
+  @EnumSource(AllianceStationID.class)
+  @ParameterizedTest(name = "alliance[{index}]: {0}")
+  void allianceTest(AllianceStationID alliance) {
+    DriverStationSim.setAllianceStationId(alliance);
+    DriverStationSim.notifyNewData();
+
+    assertTrue(m_i2c.getInitialized());
+
+    SimHooks.stepTiming(0.02);
+
+    String str = assertTimeoutPreemptively(Duration.ofMillis(20L), () -> m_future.get());
+    char expected = alliance.name().startsWith("Red") ? 'R' : 'B';
+    if (alliance.name().startsWith("Unknown")) {
+      expected = 'U';
+    }
+
+    assertEquals(expected, str.charAt(0));
+  }
+
+  @ValueSource(booleans = {true, false})
+  @ParameterizedTest(name = "enabled[{index}]: {0}")
+  void enabledTest(boolean enabled) {
+    DriverStationSim.setEnabled(enabled);
+    DriverStationSim.notifyNewData();
+
+    assertTrue(m_i2c.getInitialized());
+
+    SimHooks.stepTiming(0.02);
+
+    String str = assertTimeoutPreemptively(Duration.ofMillis(20L), () -> m_future.get());
+    char expected = enabled ? 'E' : 'D';
+
+    assertEquals(expected, str.charAt(1));
+  }
+
+  @ValueSource(booleans = {true, false})
+  @ParameterizedTest(name = "autonomous[{index}]: {0}")
+  void autonomousTest(boolean autonomous) {
+    DriverStationSim.setAutonomous(autonomous);
+    DriverStationSim.notifyNewData();
+
+    assertTrue(m_i2c.getInitialized());
+
+    SimHooks.stepTiming(0.02);
+
+    String str = assertTimeoutPreemptively(Duration.ofMillis(20L), () -> m_future.get());
+    char expected = autonomous ? 'A' : 'T';
+
+    assertEquals(expected, str.charAt(2));
+  }
+
+  @ValueSource(doubles = {112.0, 45.0, 27.0, 23.0, 3.0})
+  @ParameterizedTest(name = "matchTime[{index}]: {0}s")
+  void matchTimeTest(double matchTime) {
+    DriverStationSim.setMatchTime(matchTime);
+    DriverStationSim.notifyNewData();
+    assertTrue(m_i2c.getInitialized());
+
+    SimHooks.stepTiming(0.02);
+
+    String str = assertTimeoutPreemptively(Duration.ofMillis(20L), () -> m_future.get());
+    String expected = String.format("%03d", (int) DriverStation.getMatchTime());
+
+    assertEquals(expected, str.substring(3));
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/potentiometerpid/PotentiometerPIDTest.java b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/potentiometerpid/PotentiometerPIDTest.java
new file mode 100644
index 0000000..0da5e3d
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/potentiometerpid/PotentiometerPIDTest.java
@@ -0,0 +1,174 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.potentiometerpid;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import edu.wpi.first.hal.HAL;
+import edu.wpi.first.hal.HAL.SimPeriodicBeforeCallback;
+import edu.wpi.first.math.system.plant.DCMotor;
+import edu.wpi.first.math.util.Units;
+import edu.wpi.first.wpilibj.RobotController;
+import edu.wpi.first.wpilibj.simulation.AnalogInputSim;
+import edu.wpi.first.wpilibj.simulation.DriverStationSim;
+import edu.wpi.first.wpilibj.simulation.ElevatorSim;
+import edu.wpi.first.wpilibj.simulation.JoystickSim;
+import edu.wpi.first.wpilibj.simulation.PWMSim;
+import edu.wpi.first.wpilibj.simulation.SimHooks;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+@ResourceLock("timing")
+class PotentiometerPIDTest {
+  private final DCMotor m_elevatorGearbox = DCMotor.getVex775Pro(4);
+  private static final double kElevatorGearing = 10.0;
+  private static final double kElevatorDrumRadius = Units.inchesToMeters(2.0);
+  private static final double kCarriageMassKg = 4.0; // kg
+
+  private Robot m_robot;
+  private Thread m_thread;
+
+  private ElevatorSim m_elevatorSim;
+  private PWMSim m_motorSim;
+  private AnalogInputSim m_analogSim;
+  private SimPeriodicBeforeCallback m_callback;
+  private JoystickSim m_joystickSim;
+
+  @BeforeEach
+  void startThread() {
+    HAL.initialize(500, 0);
+    SimHooks.pauseTiming();
+    DriverStationSim.resetData();
+    m_robot = new Robot();
+    m_thread = new Thread(m_robot::startCompetition);
+    m_elevatorSim =
+        new ElevatorSim(
+            m_elevatorGearbox,
+            kElevatorGearing,
+            kCarriageMassKg,
+            kElevatorDrumRadius,
+            0.0,
+            Robot.kFullHeightMeters,
+            true,
+            0,
+            null);
+    m_analogSim = new AnalogInputSim(Robot.kPotChannel);
+    m_motorSim = new PWMSim(Robot.kMotorChannel);
+    m_joystickSim = new JoystickSim(Robot.kJoystickChannel);
+
+    m_callback =
+        HAL.registerSimPeriodicBeforeCallback(
+            () -> {
+              m_elevatorSim.setInputVoltage(
+                  m_motorSim.getSpeed() * RobotController.getBatteryVoltage());
+              m_elevatorSim.update(0.02);
+
+              /*
+              meters = (v / 5v) * range
+              meters / range = v / 5v
+              5v * (meters / range) = v
+               */
+              m_analogSim.setVoltage(
+                  RobotController.getVoltage5V()
+                      * (m_elevatorSim.getPositionMeters() / Robot.kFullHeightMeters));
+            });
+
+    m_thread.start();
+    SimHooks.stepTiming(0.0); // Wait for Notifiers
+  }
+
+  @AfterEach
+  void stopThread() {
+    m_robot.endCompetition();
+    try {
+      m_thread.interrupt();
+      m_thread.join();
+    } catch (InterruptedException ex) {
+      Thread.currentThread().interrupt();
+    }
+    m_robot.close();
+    m_callback.close();
+    m_analogSim.resetData();
+    m_motorSim.resetData();
+  }
+
+  @Test
+  void teleopTest() {
+    // teleop init
+    {
+      DriverStationSim.setAutonomous(false);
+      DriverStationSim.setEnabled(true);
+      DriverStationSim.notifyNewData();
+
+      assertTrue(m_motorSim.getInitialized());
+      assertTrue(m_analogSim.getInitialized());
+    }
+
+    // first setpoint
+    {
+      // advance 50 timesteps
+      SimHooks.stepTiming(1);
+
+      assertEquals(Robot.kSetpointsMeters[0], m_elevatorSim.getPositionMeters(), 0.1);
+    }
+
+    // second setpoint
+    {
+      // press button to advance setpoint
+      m_joystickSim.setTrigger(true);
+      m_joystickSim.notifyNewData();
+
+      // advance 50 timesteps
+      SimHooks.stepTiming(1);
+
+      assertEquals(Robot.kSetpointsMeters[1], m_elevatorSim.getPositionMeters(), 0.1);
+    }
+
+    // we need to unpress the button
+    {
+      m_joystickSim.setTrigger(false);
+      m_joystickSim.notifyNewData();
+
+      // advance 10 timesteps
+      SimHooks.stepTiming(0.2);
+    }
+
+    // third setpoint
+    {
+      // press button to advance setpoint
+      m_joystickSim.setTrigger(true);
+      m_joystickSim.notifyNewData();
+
+      // advance 50 timesteps
+      SimHooks.stepTiming(1);
+
+      assertEquals(Robot.kSetpointsMeters[2], m_elevatorSim.getPositionMeters(), 0.1);
+    }
+
+    // we need to unpress the button
+    {
+      m_joystickSim.setTrigger(false);
+      m_joystickSim.notifyNewData();
+
+      // advance 10 timesteps
+      SimHooks.stepTiming(0.2);
+    }
+
+    // rollover: first setpoint
+    {
+      // press button to advance setpoint
+      m_joystickSim.setTrigger(true);
+      m_joystickSim.notifyNewData();
+
+      // advance 60 timesteps
+      SimHooks.stepTiming(1.2);
+
+      assertEquals(Robot.kSetpointsMeters[0], m_elevatorSim.getPositionMeters(), 0.1);
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/UltrasonicPIDTest.java b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/UltrasonicPIDTest.java
new file mode 100644
index 0000000..342daa2
--- /dev/null
+++ b/third_party/allwpilib/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/UltrasonicPIDTest.java
@@ -0,0 +1,135 @@
+// 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.
+
+package edu.wpi.first.wpilibj.examples.ultrasonicpid;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import edu.wpi.first.hal.HAL;
+import edu.wpi.first.hal.HAL.SimPeriodicBeforeCallback;
+import edu.wpi.first.math.system.plant.DCMotor;
+import edu.wpi.first.math.system.plant.LinearSystemId;
+import edu.wpi.first.wpilibj.RobotController;
+import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim;
+import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim.KitbotGearing;
+import edu.wpi.first.wpilibj.simulation.DriverStationSim;
+import edu.wpi.first.wpilibj.simulation.PWMSim;
+import edu.wpi.first.wpilibj.simulation.SimHooks;
+import edu.wpi.first.wpilibj.simulation.UltrasonicSim;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+@ResourceLock("timing")
+class UltrasonicPIDTest {
+  private final DCMotor m_gearbox = DCMotor.getFalcon500(2);
+  private static final double kGearing = KitbotGearing.k10p71.value;
+  public static final double kvVoltSecondsPerMeter = 1.98;
+  public static final double kaVoltSecondsSquaredPerMeter = 0.2;
+  private static final double kvVoltSecondsPerRadian = 1.5;
+  private static final double kaVoltSecondsSquaredPerRadian = 0.3;
+  private static final double kWheelDiameterMeters = 0.15;
+  private static final double kTrackwidthMeters = 0.7;
+
+  private Robot m_robot;
+  private Thread m_thread;
+
+  private DifferentialDrivetrainSim m_driveSim;
+  private PWMSim m_leftMotorSim;
+  private PWMSim m_rightMotorSim;
+  private UltrasonicSim m_ultrasonicSim;
+  private SimPeriodicBeforeCallback m_callback;
+
+  // distance between the robot's starting position and the object
+  // we will update this in a moment
+  private double m_startToObject = Double.POSITIVE_INFINITY;
+  private double m_distanceMM;
+
+  // We're not using @BeforeEach so m_startToObject gets initialized properly
+  private void startThread() {
+    HAL.initialize(500, 0);
+    SimHooks.pauseTiming();
+    DriverStationSim.resetData();
+    m_robot = new Robot();
+    m_thread = new Thread(m_robot::startCompetition);
+    m_driveSim =
+        new DifferentialDrivetrainSim(
+            LinearSystemId.identifyDrivetrainSystem(
+                kvVoltSecondsPerMeter,
+                kaVoltSecondsSquaredPerMeter,
+                kvVoltSecondsPerRadian,
+                kaVoltSecondsSquaredPerRadian),
+            m_gearbox,
+            kGearing,
+            kTrackwidthMeters,
+            kWheelDiameterMeters / 2.0,
+            null);
+    m_ultrasonicSim = new UltrasonicSim(Robot.kUltrasonicPingPort, Robot.kUltrasonicEchoPort);
+    m_leftMotorSim = new PWMSim(Robot.kLeftMotorPort);
+    m_rightMotorSim = new PWMSim(Robot.kRightMotorPort);
+
+    m_callback =
+        HAL.registerSimPeriodicBeforeCallback(
+            () -> {
+              m_driveSim.setInputs(
+                  m_leftMotorSim.getSpeed() * RobotController.getBatteryVoltage(),
+                  m_rightMotorSim.getSpeed() * RobotController.getBatteryVoltage());
+              m_driveSim.update(0.02);
+
+              double startingDistance = m_startToObject;
+              double range = startingDistance - m_driveSim.getLeftPositionMeters();
+
+              m_ultrasonicSim.setRangeMeters(range);
+              m_distanceMM = range * 1.0e3;
+            });
+
+    m_thread.start();
+    SimHooks.stepTiming(0.0); // Wait for Notifiers
+    SimHooks.stepTiming(0.02); // Have once iteration on disabled
+  }
+
+  @AfterEach
+  void stopThread() {
+    m_robot.endCompetition();
+    try {
+      m_thread.interrupt();
+      m_thread.join();
+    } catch (InterruptedException ex) {
+      Thread.currentThread().interrupt();
+    }
+    m_robot.close();
+    m_callback.close();
+    m_leftMotorSim.resetData();
+    m_rightMotorSim.resetData();
+  }
+
+  @ValueSource(doubles = {1.3, 0.5, 5.0})
+  @ParameterizedTest
+  void autoTest(double distance) {
+    // set up distance
+    {
+      m_startToObject = distance;
+    }
+    startThread();
+
+    // auto init
+    {
+      DriverStationSim.setAutonomous(true);
+      DriverStationSim.setEnabled(true);
+      DriverStationSim.notifyNewData();
+
+      assertTrue(m_leftMotorSim.getInitialized());
+      assertTrue(m_rightMotorSim.getInitialized());
+    }
+
+    {
+      // advance 100 timesteps
+      SimHooks.stepTiming(2.0);
+
+      assertEquals(Robot.kHoldDistanceMillimeters, m_distanceMM, 10);
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpilibjIntegrationTests/build.gradle b/third_party/allwpilib/wpilibjIntegrationTests/build.gradle
index 06d891a..b79337a 100644
--- a/third_party/allwpilib/wpilibjIntegrationTests/build.gradle
+++ b/third_party/allwpilib/wpilibjIntegrationTests/build.gradle
@@ -11,7 +11,9 @@
 
 apply from: "${rootDir}/shared/opencv.gradle"
 
-mainClassName = 'edu.wpi.first.wpilibj.test.AntJunitLauncher'
+application {
+    mainClass = 'edu.wpi.first.wpilibj.test.AntJunitLauncher'
+}
 
 apply plugin: 'com.github.johnrengelman.shadow'
 
@@ -44,7 +46,7 @@
 task copyWpilibJIntegrationTestJarToOutput(type: Copy) {
     destinationDir testOutputFolder
     dependsOn shadowJar
-    inputs.file shadowJar.archivePath
+    inputs.file shadowJar.archiveFile
     from(shadowJar) {
         into 'java'
     }
diff --git a/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometerTest.java b/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometerTest.java
index 8b1188d..e6585e4 100644
--- a/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometerTest.java
+++ b/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometerTest.java
@@ -6,7 +6,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import edu.wpi.first.wpilibj.interfaces.Accelerometer;
 import edu.wpi.first.wpilibj.test.AbstractComsSetup;
 import java.util.Arrays;
 import java.util.Collection;
@@ -23,7 +22,7 @@
   private static final double kAccelerationTolerance = 0.1;
   private final BuiltInAccelerometer m_accelerometer;
 
-  public BuiltInAccelerometerTest(Accelerometer.Range range) {
+  public BuiltInAccelerometerTest(BuiltInAccelerometer.Range range) {
     m_accelerometer = new BuiltInAccelerometer(range);
   }
 
@@ -38,10 +37,12 @@
 
   /** Test with all valid ranges to make sure unpacking is always done correctly. */
   @Parameters
-  public static Collection<Accelerometer.Range[]> generateData() {
+  public static Collection<BuiltInAccelerometer.Range[]> generateData() {
     return Arrays.asList(
-        new Accelerometer.Range[][] {
-          {Accelerometer.Range.k2G}, {Accelerometer.Range.k4G}, {Accelerometer.Range.k8G}
+        new BuiltInAccelerometer.Range[][] {
+          {BuiltInAccelerometer.Range.k2G},
+          {BuiltInAccelerometer.Range.k4G},
+          {BuiltInAccelerometer.Range.k8G}
         });
   }
 
diff --git a/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/SampleFixture.java b/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/SampleFixture.java
index 38ea638..29fc4da 100644
--- a/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/SampleFixture.java
+++ b/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/SampleFixture.java
@@ -13,8 +13,8 @@
   public void setup() {
     /*
      * If this fixture actually accessed the hardware, here is where it would
-     * set up the starting state of the test bench. For example, reseting
-     * encoders, ensuring motors are stopped, reseting any serial communication
+     * set up the starting state of the test bench. For example, resetting
+     * encoders, ensuring motors are stopped, resetting any serial communication
      * if necessary, etc.
      */
   }
diff --git a/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/mockhardware/FakeCounterSource.java b/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/mockhardware/FakeCounterSource.java
index ac1094c..5e0e02e 100644
--- a/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/mockhardware/FakeCounterSource.java
+++ b/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/mockhardware/FakeCounterSource.java
@@ -72,7 +72,7 @@
     }
   }
 
-  /** Common initailization code. */
+  /** Common initialization code. */
   private void initEncoder() {
     m_milliSec = 1;
     m_task = new EncoderThread(this);
@@ -95,7 +95,7 @@
     Timer.delay(0.01);
   }
 
-  /** Starts and completes a task set - does not return until thred has finished its operations. */
+  /** Starts and completes a task set - does not return until thread has finished its operations. */
   public void execute() {
     start();
     complete();
diff --git a/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/mockhardware/FakePotentiometerSource.java b/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/mockhardware/FakePotentiometerSource.java
index 59cc5c7..bddb3d5 100644
--- a/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/mockhardware/FakePotentiometerSource.java
+++ b/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/mockhardware/FakePotentiometerSource.java
@@ -75,7 +75,7 @@
     return voltage * (m_potMaxAngle / m_potMaxVoltage);
   }
 
-  /** Frees the resouce. */
+  /** Frees the resource. */
   @Override
   public void close() {
     if (m_initOutput) {
diff --git a/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/AntJunitLauncher.java b/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/AntJunitLauncher.java
index 4dfa6e1..07d0af7 100644
--- a/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/AntJunitLauncher.java
+++ b/third_party/allwpilib/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/AntJunitLauncher.java
@@ -60,9 +60,9 @@
         FormatterElement.TypeAttribute type = new FormatterElement.TypeAttribute();
         type.setValue("xml");
 
-        FormatterElement formater = new FormatterElement();
-        formater.setType(type);
-        task.addFormatter(formater);
+        FormatterElement formatter = new FormatterElement();
+        formatter.setType(type);
+        task.addFormatter(formatter);
 
         // Create the JUnitTest
         JUnitTest test = new JUnitTest(TestSuite.class.getName());
diff --git a/third_party/allwpilib/wpimath/.styleguide b/third_party/allwpilib/wpimath/.styleguide
index 6ae8bbf..257b50b 100644
--- a/third_party/allwpilib/wpimath/.styleguide
+++ b/third_party/allwpilib/wpimath/.styleguide
@@ -34,14 +34,14 @@
 }
 
 includeOtherLibs {
+  ^Eigen/
   ^fmt/
+  ^gcem/
+  ^gtest/
+  ^unsupported/
   ^wpi/
 }
 
 includeProject {
-  ^gcem/
-  ^drake/
-  ^Eigen/
   ^units/
-  ^unsupported/
 }
diff --git a/third_party/allwpilib/wpimath/BUILD b/third_party/allwpilib/wpimath/BUILD
index 45f1eef..aee069b 100644
--- a/third_party/allwpilib/wpimath/BUILD
+++ b/third_party/allwpilib/wpimath/BUILD
@@ -5,6 +5,7 @@
     hdrs = glob(
         [
             "src/main/native/include/frc/fmt/**",
+            "src/main/native/include/units/**",
         ],
     ),
     includes = [
diff --git a/third_party/allwpilib/wpimath/CMakeLists.txt b/third_party/allwpilib/wpimath/CMakeLists.txt
index e3d90cd..997ec46 100644
--- a/third_party/allwpilib/wpimath/CMakeLists.txt
+++ b/third_party/allwpilib/wpimath/CMakeLists.txt
@@ -3,8 +3,59 @@
 include(SubDirList)
 include(CompileWarnings)
 include(AddTest)
+include(DownloadAndCheck)
 
-file(GLOB wpimath_jni_src src/main/native/cpp/jni/WPIMathJNI.cpp)
+# workaround for makefiles - for some reason parent directories aren't created.
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/protobuf")
+file(GLOB wpimath_proto_src src/main/proto/*.proto)
+protobuf_generate_cpp(WPIMATH_PROTO_SRCS WPIMATH_PROTO_HDRS PROTOC_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/protobuf" PROTOS ${wpimath_proto_src})
+
+function(quickbuf_generate SRCS JAVA_PACKAGE)
+  if(NOT ARGN)
+    message(SEND_ERROR "Error: PROTOBUF_GENERATE_QUICKBUF() called without any proto files")
+    return()
+  endif()
+
+  set(_generated_srcs_all)
+  foreach(_proto ${ARGN})
+    get_filename_component(_abs_file ${_proto} ABSOLUTE)
+    get_filename_component(_abs_dir ${_abs_file} DIRECTORY)
+    get_filename_component(_basename ${_proto} NAME_WLE)
+    file(RELATIVE_PATH _rel_dir ${CMAKE_CURRENT_SOURCE_DIR} ${_abs_dir})
+
+    # convert to QuickBuffers Java case (geometry2d -> Geometry2D)
+    string(REGEX MATCHALL "[A-Za-z_]+|[0-9]+" _name_components ${_basename})
+    set(_name_components_out)
+    foreach(_part ${_name_components})
+      string(SUBSTRING ${_part} 0 1 _first_letter)
+      string(TOUPPER ${_first_letter} _first_letter)
+      string(REGEX REPLACE "^.(.*)" "${_first_letter}\\1" _part_out "${_part}")
+      list(APPEND _name_components_out ${_part_out})
+    endforeach()
+    list(JOIN _name_components_out "" _basename_title)
+
+    set(_generated_src "${CMAKE_CURRENT_BINARY_DIR}/quickbuf/${JAVA_PACKAGE}/${_basename_title}.java")
+
+    list(APPEND _generated_srcs_all ${_generated_src})
+
+    add_custom_command(
+      OUTPUT ${_generated_src}
+      COMMAND protobuf::protoc
+      ARGS --plugin=protoc-gen-quickbuf=${Quickbuf_EXECUTABLE} --quickbuf_out=gen_descriptors=true:${CMAKE_CURRENT_BINARY_DIR}/quickbuf -I${_abs_dir} ${_abs_file}
+      DEPENDS ${_abs_file} protobuf::protoc
+      COMMENT "Running quickbuf protocol buffer compiler on ${_proto}"
+      VERBATIM )
+  endforeach()
+
+  set(${SRCS} ${_generated_srcs_all} PARENT_SCOPE)
+endfunction()
+
+file(GLOB wpimath_jni_src src/main/native/cpp/jni/WPIMathJNI_DARE.cpp
+                          src/main/native/cpp/jni/WPIMathJNI_Eigen.cpp
+                          src/main/native/cpp/jni/WPIMathJNI_Exceptions.cpp
+                          src/main/native/cpp/jni/WPIMathJNI_Pose3d.cpp
+                          src/main/native/cpp/jni/WPIMathJNI_StateSpaceUtil.cpp
+                          src/main/native/cpp/jni/WPIMathJNI_Trajectory.cpp)
 
 # Java bindings
 if (WITH_JAVA)
@@ -13,34 +64,38 @@
   include(UseJava)
   set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
 
-  if(NOT EXISTS "${WPILIB_BINARY_DIR}/wpimath/thirdparty/ejml/ejml-simple-0.41.jar")
+  quickbuf_generate(WPIMATH_QUICKBUF_SRCS "edu/wpi/first/math/proto" ${wpimath_proto_src})
+
+  if(NOT EXISTS "${WPILIB_BINARY_DIR}/wpimath/thirdparty/ejml/ejml-simple-0.43.1.jar")
       set(BASE_URL "https://search.maven.org/remotecontent?filepath=")
       set(JAR_ROOT "${WPILIB_BINARY_DIR}/wpimath/thirdparty/ejml")
 
       message(STATUS "Downloading EJML jarfiles...")
 
-      file(DOWNLOAD "${BASE_URL}org/ejml/ejml-cdense/0.41/ejml-cdense-0.41.jar"
-          "${JAR_ROOT}/ejml-cdense-0.41.jar")
-      file(DOWNLOAD "${BASE_URL}org/ejml/ejml-core/0.41/ejml-core-0.41.jar"
-          "${JAR_ROOT}/ejml-core-0.41.jar")
-      file(DOWNLOAD "${BASE_URL}org/ejml/ejml-ddense/0.41/ejml-ddense-0.41.jar"
-          "${JAR_ROOT}/ejml-ddense-0.41.jar")
-      file(DOWNLOAD "${BASE_URL}org/ejml/ejml-dsparse/0.41/ejml-dsparse-0.41.jar"
-          "${JAR_ROOT}/ejml-dsparse-0.41.jar")
-      file(DOWNLOAD "${BASE_URL}org/ejml/ejml-fdense/0.41/ejml-fdense-0.41.jar"
-          "${JAR_ROOT}/ejml-fdense-0.41.jar")
-      file(DOWNLOAD "${BASE_URL}org/ejml/ejml-simple/0.41/ejml-simple-0.41.jar"
-          "${JAR_ROOT}/ejml-simple-0.41.jar")
-      file(DOWNLOAD "${BASE_URL}org/ejml/ejml-zdense/0.41/ejml-zdense-0.41.jar"
-          "${JAR_ROOT}/ejml-zdense-0.41.jar")
+      download_and_check("${BASE_URL}org/ejml/ejml-cdense/0.43.1/ejml-cdense-0.43.1.jar"
+          "${JAR_ROOT}/ejml-cdense-0.43.1.jar")
+      download_and_check("${BASE_URL}org/ejml/ejml-core/0.43.1/ejml-core-0.43.1.jar"
+          "${JAR_ROOT}/ejml-core-0.43.1.jar")
+      download_and_check("${BASE_URL}org/ejml/ejml-ddense/0.43.1/ejml-ddense-0.43.1.jar"
+          "${JAR_ROOT}/ejml-ddense-0.43.1.jar")
+      download_and_check("${BASE_URL}org/ejml/ejml-dsparse/0.43.1/ejml-dsparse-0.43.1.jar"
+          "${JAR_ROOT}/ejml-dsparse-0.43.1.jar")
+      download_and_check("${BASE_URL}org/ejml/ejml-fdense/0.43.1/ejml-fdense-0.43.1.jar"
+          "${JAR_ROOT}/ejml-fdense-0.43.1.jar")
+      download_and_check("${BASE_URL}org/ejml/ejml-simple/0.43.1/ejml-simple-0.43.1.jar"
+          "${JAR_ROOT}/ejml-simple-0.43.1.jar")
+      download_and_check("${BASE_URL}org/ejml/ejml-zdense/0.43.1/ejml-zdense-0.43.1.jar"
+          "${JAR_ROOT}/ejml-zdense-0.43.1.jar")
 
       message(STATUS "All files downloaded.")
   endif()
 
   file(GLOB EJML_JARS "${WPILIB_BINARY_DIR}/wpimath/thirdparty/ejml/*.jar")
   file(GLOB JACKSON_JARS "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson/*.jar")
+  file(GLOB QUICKBUF_JAR
+        ${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf/*.jar)
 
-  set(CMAKE_JAVA_INCLUDE_PATH wpimath.jar ${EJML_JARS} ${JACKSON_JARS})
+  set(CMAKE_JAVA_INCLUDE_PATH wpimath.jar ${EJML_JARS} ${JACKSON_JARS} ${QUICKBUF_JAR})
 
   execute_process(COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/generate_numbers.py ${WPILIB_BINARY_DIR}/wpimath RESULT_VARIABLE generateResult)
   if(NOT (generateResult EQUAL "0"))
@@ -55,12 +110,7 @@
 
   file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java ${WPILIB_BINARY_DIR}/wpimath/generated/*.java)
 
-  if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-    set(CMAKE_JAVA_COMPILE_FLAGS "-h" "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-    add_jar(wpimath_jar ${JAVA_SOURCES} INCLUDE_JARS ${EJML_JARS} wpiutil_jar OUTPUT_NAME wpimath)
-  else()
-    add_jar(wpimath_jar ${JAVA_SOURCES} INCLUDE_JARS ${EJML_JARS} wpiutil_jar OUTPUT_NAME wpimath GENERATE_NATIVE_HEADERS wpimath_jni_headers)
-  endif()
+  add_jar(wpimath_jar ${JAVA_SOURCES} ${WPIMATH_QUICKBUF_SRCS} INCLUDE_JARS ${EJML_JARS} wpiutil_jar OUTPUT_NAME wpimath GENERATE_NATIVE_HEADERS wpimath_jni_headers)
 
   get_property(WPIMATH_JAR_FILE TARGET wpimath_jar PROPERTY JAR_FILE)
   install(FILES ${WPIMATH_JAR_FILE} DESTINATION "${java_lib_dest}")
@@ -73,28 +123,56 @@
 
   set_property(TARGET wpimathjni PROPERTY FOLDER "libraries")
 
-  if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-    target_include_directories(wpimathjni PRIVATE ${JNI_INCLUDE_DIRS})
-    target_include_directories(wpimathjni PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-  else()
-    target_link_libraries(wpimathjni PRIVATE wpimath_jni_headers)
-  endif()
+  target_link_libraries(wpimathjni PRIVATE wpimath_jni_headers)
   add_dependencies(wpimathjni wpimath_jar)
 
-  if (MSVC)
-    install(TARGETS wpimathjni RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-  endif()
-
-  install(TARGETS wpimathjni EXPORT wpimathjni DESTINATION "${main_lib_dest}")
+  install(TARGETS wpimathjni EXPORT wpimathjni)
 
 endif()
 
-file(GLOB_RECURSE wpimath_native_src src/main/native/cpp/*.cpp
-                                     src/main/native/thirdparty/drake/src/*.cpp)
+if (WITH_JAVA_SOURCE)
+  find_package(Java REQUIRED)
+  include(UseJava)
+  file(GLOB WPIMATH_SOURCES src/main/java/edu/wpi/first/math/*.java ${WPILIB_BINARY_DIR}/wpimath/generated/main/java/edu/wpi/first/math/Nat.java)
+  file(GLOB WPIMATH_CONTROLLER_SOURCES src/main/java/edu/wpi/first/math/controller/*.java)
+  file(GLOB WPIMATH_ESTIMATOR_SOURCES src/main/java/edu/wpi/first/math/estimator/*.java)
+  file(GLOB WPIMATH_FILTER_SOURCES src/main/java/edu/wpi/first/math/filter/*.java)
+  file(GLOB WPIMATH_GEOMETRY_SOURCES src/main/java/edu/wpi/first/math/geometry/*.java)
+  file(GLOB WPIMATH_INTERPOLATION_SOURCES src/main/java/edu/wpi/first/math/interpolation/*.java)
+  file(GLOB WPIMATH_KINEMATICS_SOURCES src/main/java/edu/wpi/first/math/kinematics/*.java)
+  file(GLOB WPIMATH_NUMBERS_SOURCES ${WPILIB_BINARY_DIR}/wpimath/generated/main/java/edu/wpi/first/math/numbers/*.java)
+  file(GLOB WPIMATH_SPLINE_SOURCES src/main/java/edu/wpi/first/math/spline/*.java)
+  file(GLOB WPIMATH_SYSTEM_SOURCES src/main/java/edu/wpi/first/math/system/*.java)
+  file(GLOB WPIMATH_SYSTEM_PLANT_SOURCES src/main/java/edu/wpi/first/math/system/plant/*.java)
+  file(GLOB WPIMATH_TRAJECTORY_SOURCES src/main/java/edu/wpi/first/math/trajectory/*.java)
+  file(GLOB WPIMATH_TRAJECTORY_CONSTRAINT_SOURCES src/main/java/edu/wpi/first/math/trajectory/constraint/*.java)
+  add_jar(wpimath_src_jar
+  RESOURCES NAMESPACE "edu/wpi/first/math" ${WPIMATH_SOURCES}
+  NAMESPACE "edu/wpi/first/math/controller" ${WPIMATH_CONTROLLER_SOURCES}
+  NAMESPACE "edu/wpi/first/math/estimator" ${WPIMATH_ESTIMATOR_SOURCES}
+  NAMESPACE "edu/wpi/first/math/filter" ${WPIMATH_FILTER_SOURCES}
+  NAMESPACE "edu/wpi/first/math/geometry" ${WPIMATH_GEOMETRY_SOURCES}
+  NAMESPACE "edu/wpi/first/math/interpolation" ${WPIMATH_INTERPOLATION_SOURCES}
+  NAMESPACE "edu/wpi/first/math/kinematics" ${WPIMATH_KINEMATICS_SOURCES}
+  NAMESPACE "edu/wpi/first/math/spline" ${WPIMATH_SPLINE_SOURCES}
+  NAMESPACE "edu/wpi/first/math/system" ${WPIMATH_SYSTEM_SOURCES}
+  NAMESPACE "edu/wpi/first/math/system/plant" ${WPIMATH_SYSTEM_PLANT_SOURCES}
+  NAMESPACE "edu/wpi/first/math/trajectory" ${WPIMATH_TRAJECTORY_SOURCES}
+  NAMESPACE "edu/wpi/first/math/trajectory/constraint" ${WPIMATH_TRAJECTORY_CONSTRAINT_SOURCES}
+  NAMESPACE "edu/wpi/first/math/util" src/main/java/edu/wpi/first/math/util/Units.java
+  OUTPUT_NAME wpimath-sources)
+
+  get_property(WPIMATH_SRC_JAR_FILE TARGET wpimath_src_jar PROPERTY JAR_FILE)
+  install(FILES ${WPIMATH_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+  set_property(TARGET wpimath_src_jar PROPERTY FOLDER "java")
+endif()
+
+file(GLOB_RECURSE wpimath_native_src src/main/native/cpp/*.cpp)
 list(REMOVE_ITEM wpimath_native_src ${wpimath_jni_src})
 
 set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS FALSE)
-add_library(wpimath ${wpimath_native_src})
+add_library(wpimath ${wpimath_native_src} ${WPIMATH_PROTO_SRCS})
 set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
 set_target_properties(wpimath PROPERTIES DEBUG_POSTFIX "d")
 
@@ -118,10 +196,6 @@
     target_link_libraries (wpimath Eigen3::Eigen)
 endif()
 
-install(DIRECTORY src/main/native/thirdparty/drake/include/ DESTINATION "${include_dest}/wpimath")
-target_include_directories(wpimath SYSTEM PUBLIC
-                           $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/drake/include>)
-
 install(DIRECTORY src/main/native/thirdparty/gcem/include/ DESTINATION "${include_dest}/wpimath")
 target_include_directories(wpimath SYSTEM PUBLIC
                           $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/gcem/include>)
@@ -129,13 +203,10 @@
 install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/wpimath")
 target_include_directories(wpimath PUBLIC
                             $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
+                            $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/protobuf>
                             $<INSTALL_INTERFACE:${include_dest}/wpimath>)
 
-install(TARGETS wpimath EXPORT wpimath DESTINATION "${main_lib_dest}")
-
-if (WITH_JAVA AND MSVC)
-    install(TARGETS wpimath RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-endif()
+install(TARGETS wpimath EXPORT wpimath)
 
 if (WITH_FLAT_INSTALL)
     set (wpimath_config_dir ${wpilib_dest})
diff --git a/third_party/allwpilib/wpimath/algorithms.md b/third_party/allwpilib/wpimath/algorithms.md
index 04cdf94..fc2904c 100644
--- a/third_party/allwpilib/wpimath/algorithms.md
+++ b/third_party/allwpilib/wpimath/algorithms.md
@@ -39,7 +39,7 @@
   pr⁻¹p = q
   p²r⁻¹ = q
   p² = qr
-  p = sqrt(qr)
+  p = √(qr)
 ```
 
 Now solve for the Kalman gain.
@@ -48,16 +48,16 @@
   K = PCᵀ(CPCᵀ + R)⁻¹
   K = P(P + R)⁻¹
   k = p(p + r)⁻¹
-  k = sqrt(qr)(sqrt(qr) + r)⁻¹
-  k = sqrt(qr)/(sqrt(qr) + r)
+  k = √(qr)(√(qr) + r)⁻¹
+  k = √(qr)/(√(qr) + r)
 ```
 
-Multiply by sqrt(q/r)/sqrt(q/r).
+Multiply by √(q/r)/√(q/r).
 
 ```
-  k = q/(q + r sqrt(q/r))
-  k = q/(q + sqrt(qr²/r))
-  k = q/(q + sqrt(qr))
+  k = q/(q + r √(q/r))
+  k = q/(q + √(qr²/r))
+  k = q/(q + √(qr))
 ```
 
 ### Corner cases
@@ -65,16 +65,16 @@
 For q = 0 and r ≠ 0,
 
 ```
-  k = 0/(0 + sqrt(0))
+  k = 0/(0 + √0)
   k = 0/0
 ```
 
 Apply L'Hôpital's rule to k with respect to q.
 
 ```
-  k = 1/(1 + r/(2sqrt(qr)))
-  k = 2sqrt(qr)/(2sqrt(qr) + r)
-  k = 2sqrt(0)/(2sqrt(0) + r)
+  k = 1/(1 + r/(2 √(qr)))
+  k = 2 √(qr)/(2 √(qr) + r)
+  k = 2 √(0)/(2 √0 + r)
   k = 0/r
   k = 0
 ```
@@ -82,7 +82,424 @@
 For q ≠ 0 and r = 0,
 
 ```
-  k = q / (q + sqrt(0))
+  k = q / (q + √0)
   k = q / q
   k = 1
 ```
+
+## Quaternion to Euler angle conversion
+
+### Conventions
+
+We'll use the extrinsic X-Y-Z rotation order for Euler angles. The direction of rotation is CCW looking into the positive axis. If you point your right thumb along the positive axis direction, your fingers curl in the direction of rotation.
+
+The angles are a\_x around the X-axis, a\_y around the Y-axis, and a\_z around the Z-axis, with the following constraints:
+
+```
+  -π ≤ a_x ≤ π
+  -π/2 ≤ a_y ≤ π/2
+  -π ≤ a_z ≤ π
+```
+
+The coordinate system is right-handed. If you point your right thumb along the +Z axis, your fingers curl from the +X axis to the +Y axis.
+
+The quaternion imaginary numbers are defined as follows:
+
+```
+  îĵ = k̂
+  ĵk̂ = î
+  k̂î = ĵ
+  îĵ = -k̂
+  k̂ĵ = -î
+  îk̂ = -ĵ
+  î² = ĵ² = k̂² = -1
+```
+
+### Quaternion representation of axis rotations
+
+We will take it as given that a rotation by θ radians around a normalized vector v is represented with the quaternion cos(θ/2) + sin(θ/2) (v\_x î + v\_y ĵ + v\_z k̂).
+
+### Derivation
+
+For convenience, we'll define the following variables:
+
+```
+  c_x = cos(a_x/2)
+  s_x = sin(a_x/2)
+  c_y = cos(a_y/2)
+  s_y = sin(a_y/2)
+  c_z = cos(a_z/2)
+  s_z = sin(a_z/2)
+```
+
+We can calculate the quaternion corresponding to a set of Euler angles by applying each rotation in sequence. Recall that quaternions are composed with left multiplication, like matrices.
+
+```
+  q = (cos(a_z/2) + sin(a_z/2) k̂)(cos(a_y/2) + sin(a_y/2) ĵ)(cos(a_x/2) + sin(a_x/2) î)
+  q = (c_z + s_z k̂)(c_y + s_y ĵ)(c_x + s_x î)
+  q = (c_y c_z - s_y s_z î + s_y c_z ĵ + c_y s_z k̂)(c_x + s_x î)
+    = (c_x c_y c_z + s_x s_y s_z)
+      + (s_x c_y c_z - c_x s_y s_z) î
+      + (c_x s_y c_z + s_x c_y s_z) ĵ
+      + (c_x c_y s_z - s_x s_y c_z) k̂
+```
+
+Letting q = q\_w + q\_x î + q\_y ĵ + q\_z k̂, we can extract the components of the quaternion:
+
+```
+  q_w = c_x c_y c_z + s_x s_y s_z
+  q_x = s_x c_y c_z - c_x s_y s_z
+  q_y = c_x s_y c_z + s_x c_y s_z
+  q_z = c_x c_y s_z - s_x s_y c_z
+```
+
+### Solving for a\_y
+
+Solving for sin(a\_y):
+
+```
+  sin(a_y) = 2 c_y s_y
+  sin(a_y) = 2 (c_x² c_y s_y + s_x² c_y s_y)
+  sin(a_y) = 2 (c_x² c_y s_y c_z² + c_x² c_y s_y s_z²
+              + s_x² c_y s_y c_z² + s_x² c_y s_y s_z²)
+  sin(a_y) = 2 (c_x² c_y s_y c_z² + s_x² c_y s_y s_z²
+              + s_x² c_y s_y c_z² + c_x² c_y s_y s_z²)
+  sin(a_y) = 2 (c_x² c_y s_y c_z² + c_x s_x c_y² c_z s_z
+              + c_x s_x s_y² c_z s_z + s_x² c_y s_y s_z²
+              - c_x s_x c_y² c_z s_z + s_x² c_y s_y c_z²
+              + c_x² c_y s_y s_z² - c_x s_x s_y² c_z s_z)
+  sin(a_y) = 2 ((c_x c_y c_z + s_x s_y s_z)(c_x s_y c_z + s_x c_y s_z)
+              - (s_x c_y c_z - c_x s_y s_z)(c_x c_y s_z - s_x s_y c_z))
+  sin(a_y) = 2 (q_w q_y - q_x q_z)
+```
+
+Then solving for a\_y:
+
+```
+  a_y = sin⁻¹(sin(a_y))
+  a_y = sin⁻¹(2 (q_w q_y - q_x q_z))
+```
+
+### Solving for a\_x and a\_z
+
+Solving for cos(a\_x) cos(a\_y):
+
+```
+  cos(a_x) cos(a_y) = (cos²(a_x/2) - sin²(a_x/2))(cos²(a_y/2) - sin²(a_y/2))
+  cos(a_x) cos(a_y) = (c_x² - s_x²)(c_y² - s_y²)
+  cos(a_x) cos(a_y) = c_x² c_y² - c_x² s_y² - s_x² c_y² + s_x² s_y²
+  cos(a_x) cos(a_y) = c_x² (1 - s_y²) - c_x² s_y² - s_x² c_y² + s_x² (1 - c_y²)
+  cos(a_x) cos(a_y) = c_x² - c_x² s_y² - c_x² s_y² - s_x² c_y² + s_x² - s_x² c_y²
+  cos(a_x) cos(a_y) = c_x² + s_x² - 2 (c_x² s_y² + s_x² c_y²)
+  cos(a_x) cos(a_y) = 1 - 2 (c_x² s_y² + s_x² c_y²)
+  cos(a_x) cos(a_y) = 1 - 2 (c_x² s_y² c_z² + c_x² s_y² s_z²
+                           + s_x² c_y² c_z² + s_x² c_y² s_z²)
+  cos(a_x) cos(a_y) = 1 - 2 (s_x² c_y² c_z² + c_x² s_y² s_z²
+                           + c_x² s_y² c_z² + s_x² c_y² s_z²)
+  cos(a_x) cos(a_y) = 1 - 2 (s_x² c_y² c_z² - 2 c_x s_x c_y s_y c_z s_z + c_x² s_y² s_z²
+                           + c_x² s_y² c_z² + 2 c_x s_x c_y s_y c_z s_z + s_x² c_y² s_z²)
+  cos(a_x) cos(a_y) = 1 - 2 ((s_x c_y c_z - c_x s_y s_z)² + (c_x s_y c_z + s_x c_y s_z)²)
+  cos(a_x) cos(a_y) = 1 - 2 (q_x² + q_y²)
+```
+
+Solving for sin(a\_x) cos(a\_y):
+
+```
+  sin(a_x) cos(a_y) = (2 cos(a_x/2) sin(a_x/2))(cos²(a_y/2) - sin²(a_y/2))
+  sin(a_x) cos(a_y) = (2 c_x s_x)(c_y² - s_y²)
+  sin(a_x) cos(a_y) = 2 (c_x s_x c_y² - c_x s_x s_y²)
+  sin(a_x) cos(a_y) = 2 (c_x s_x c_y² c_z² + c_x s_x c_y² s_z²
+                       - c_x s_x s_y² c_z² - c_x s_x s_y² s_z²)
+  sin(a_x) cos(a_y) = 2 (c_s s_x c_y² c_z² - c_x s_x s_y² s_z²
+                       - c_x s_x s_y² c_z² + c_x s_x c_y² s_z²)
+  sin(a_x) cos(a_y) = 2 (c_x s_x c_y² c_z² - c_x² c_y s_y c_z s_z
+                       + s_x² c_y s_y c_z s_z - c_x s_x s_y² s_z²
+                       + c_x² c_y s_y c_z s_z - c_x s_x s_y² c_z²
+                       + c_x s_x c_y² s_z² - s_x² c_y s_y c_z s_z)
+  sin(a_x) cos(a_y) = 2 ((c_x c_y c_z + s_x s_y s_z)(s_x c_y c_z - c_x s_y s_z)
+                       + (c_x s_y c_z + s_x c_y s_z)(c_x c_y s_z - s_x s_y c_z))
+  sin(a_x) cos(a_y) = 2 (q_w q_x + q_y q_z)
+```
+
+Similarly, solving for cos(a\_z) cos(a\_y):
+
+```
+  cos(a_z) cos(a_y) = (cos²(a_z/2) - sin²(a_z/2))(cos²(a_y/2) - sin²(a_y/2))
+  cos(a_z) cos(a_y) = (c_z² - s_z²)(c_y² - s_y²)
+  cos(a_z) cos(a_y) = c_y² c_z² - s_y² c_z² - c_y² s_z² + s_y² s_z²
+  cos(a_z) cos(a_y) = c_y² (1 - s_z²) - s_y² c_z² - c_y² s_z² + s_y² (1 - c_z²)
+  cos(a_z) cos(a_y) = c_y² - c_y² s_z² - s_y² c_z² - c_y² s_z² + s_y² - s_y² c_z²
+  cos(a_z) cos(a_y) = c_y² + s_y² - 2 (c_y² s_z² + s_y² c_z²)
+  cos(a_z) cos(a_y) = 1 - 2 (c_y² s_z² + s_y² c_z²)
+  cos(a_z) cos(a_y) = 1 - 2 (c_x² c_y² s_z² + s_x² c_y² s_z²
+                           + c_x² s_y² c_z² + s_x² s_y² c_z²)
+  cos(a_z) cos(a_y) = 1 - 2 (c_x² s_y² c_z² + s_x² c_y² s_z²
+                           + c_x² c_y² s_z² + s_x² s_y² c_z²)
+  cos(a_z) cos(a_y) = 1 - 2 (c_x² s_y² c_z² + 2 c_x s_x c_y s_y c_z s_z + s_x² c_y² s_z²
+                           + c_x² c_y² s_z² - 2 c_x s_x c_y s_y c_z s_z + s_x² s_y² c_z²)
+  cos(a_z) cos(a_y) = 1 - 2 ((c_x s_y c_z + s_x c_y s_z)² + (c_x c_y s_z - s_x s_y c_z)²)
+  cos(a_z) cos(a_y) = 1 - 2 (q_y² + q_z²)
+```
+
+Similarly, solving for sin(a\_z) cos(a\_y):
+
+```
+  sin(a_z) cos(a_y) = (2 cos(a_z/2) sin(a_z/2))(cos²(a_y/2) - sin²(a_y/2))
+  sin(a_z) cos(a_y) = (2 c_z s_z)(c_y² - s_y²)
+  sin(a_z) cos(a_y) = 2 (c_y² c_z s_z - s_y² c_z s_z)
+  sin(a_z) cos(a_y) = 2 (c_x² c_y² c_z s_z + s_x² c_y² c_z s_z
+                       - c_x² s_y² c_z s_z - s_x² s_y² c_z s_z)
+  sin(a_z) cos(a_y) = 2 (c_x² c_y² c_z s_z - s_x² s_y² c_z s_z
+                       + s_x² c_y² c_z s_z - c_x² s_y² c_z s_z)
+  sin(a_z) cos(a_y) = 2 (c_x² c_y² c_z s_z - c_x s_x c_y s_y c_z²
+                       + c_x s_x c_y s_y s_z² - s_x² s_y² c_z s_z
+                       + c_x s_x c_y s_y c_z² + s_x² c_y² c_z s_z
+                       - c_x² s_y² c_z s_z - c_x s_x c_y s_y s_z²)
+  sin(a_z) cos(a_y) = 2 ((c_x c_y c_z + s_x s_y s_z)(c_x c_y s_z - s_x s_y c_z)
+                       + (s_x c_y c_z - c_x s_y s_z)(c_x s_y c_z + s_x c_y s_z))
+  sin(a_z) cos(a_y) = 2 (q_w q_z + q_x q_y)
+```
+
+Solving for a\_x and a\_z:
+
+```
+  a_x = atan2(sin(a_x), cos(a_x))
+  a_z = atan2(sin(a_z), cos(a_z))
+```
+
+If cos(a\_y) > 0:
+
+```
+  a_x = atan2(sin(a_x) cos(a_y), cos(a_x) cos(a_y))
+  a_z = atan2(sin(a_z) cos(a_y), cos(a_z) cos(a_y))
+  a_x = atan2(2 (q_w q_x + q_y q_z), 1 - 2 (q_x² + q_y²))
+  a_z = atan2(2 (q_w q_z + q_x q_y), 1 - 2 (q_y² + q_z²))
+```
+
+Because -π/2 ≤ a\_y ≤ π/2, cos(a\_y) ≥ 0. Therefore, the only remaining case is cos(a\_y) = 0, whose only solutions in that range are a\_y = ±π/2.
+
+```
+  a_y = ±π/2
+  a_y/2 = ±π/4
+  cos(a_y/2) = √2/2
+  c_y = √2/2
+  sin(a_y/2) = ±√2/2
+  s_y = ±√2/2
+```
+
+Plugging into the quaternion components:
+
+```
+  q_w = c_x c_y c_z + s_x s_y s_z
+  q_x = s_x c_y c_z - c_x s_y s_z
+  q_y = c_x s_y c_z + s_x c_y s_z
+  q_z = c_x c_y s_z - s_x s_y c_z
+  q_w = √2/2 c_x c_z ± √2/2 s_x s_z
+  q_x = √2/2 s_x c_z ∓ √2/2 c_x s_z
+  q_y = ±√2/2 c_x c_z + √2/2 s_x s_z
+  q_z = √2/2 c_x s_z ∓ √2/2 s_x c_z
+  q_w = √2/2 (c_x c_z ± s_x s_z)
+  q_x = √2/2 (s_x c_z ∓ c_x s_z)
+  q_y = √2/2 (± c_x c_z + s_x s_z)
+  q_z = √2/2 (c_x s_z ∓ s_x c_z)
+  q_w = √2/2 cos(a_z/2 ∓ a_x/2)
+  q_x = √2/2 sin(a_x/2 ∓ a_z/2)
+  q_y = √2/2 -cos(a_x/2 ∓ a_z/2)
+  q_z = √2/2 sin(a_z/2 ∓ a_x/2)
+```
+
+In either case only the sum or the difference between a\_x and a\_z can be determined. We'll pick the solution where a\_x = 0.
+
+```
+  q_w = √2/2 cos(a_z/2 ∓ 0)
+  q_w = √2/2 cos(a_z/2)
+  cos(a_z/2) = √2 q_w
+  q_z = √2/2 sin(a_z/2 ∓ 0)
+  q_z = √2/2 sin(a_z/2)
+  sin(a_z/2) = √2 q_z
+  cos(a_z) = cos²(a_z/2) - sin²(a_z/2)
+  cos(a_z) = (√2 q_w)² - (√2 q_z)²
+  cos(a_z) = 2 q_w² - 2 q_z²
+  cos(a_z) = 2 (q_w² - q_z²)
+  sin(a_z) = 2 cos(a_z/2) sin(a_z/2)
+  sin(a_z) = 2 (√2 q_w) (√2 q_z)
+  sin(a_z) = 4 q_w q_z
+  a_z = atan2(4 q_w q_z, 2 (q_w² - q_z²))
+  a_z = atan2(2 q_w q_z, q_w² - q_z²)
+```
+
+### Determining if cos(a\_y) ≈ 0
+
+When calculating a\_x:
+
+```
+  cos(a_y) ≈ 0
+  cos²(a_y) ≈ 0
+  cos²(a_x) cos²(a_y) + sin²(a_x) cos²(a_y) ≈ 0
+  (cos(a_x) cos(a_y))² + (sin(a_x) cos(a_y))² ≈ 0
+```
+
+Note that this reuses the cos(a\_x) cos(a\_y) and sin(a\_x) cos(a\_y) terms needed to calculate a\_x.
+
+When calculating a\_z:
+
+```
+  cos(a_y) ≈ 0
+  cos²(a_y) ≈ 0
+  cos²(a_y) cos²(a_z) + cos²(a_y) sin²(a_z) ≈ 0
+  (cos(a_y) cos(a_z))² + (cos(a_y) sin(a_z))² ≈ 0
+```
+
+Note that this reuses the cos(a\_y) cos(a\_z) and cos(a\_y) sin(a\_z) terms needed to calculate a\_z.
+
+## Quaternion Exponential
+
+We will take it as given that a quaternion has scalar and vector components `𝑞 = s + 𝑣⃗`, with vector component 𝑣⃗ consisting of a unit vector and magnitude `𝑣⃗ = θ * v̂`.
+
+```
+𝑞 = s + 𝑣⃗
+
+𝑣⃗ = θ * v̂
+
+exp(𝑞) = exp(s + 𝑣⃗)
+exp(𝑞) = exp(s) * exp(𝑣⃗)
+exp(𝑞) = exp(s) * exp(θ * v̂)
+```
+
+Applying euler's identity:
+
+```
+exp(θ * v̂) = cos(θ) + sin(θ) * v̂
+```
+
+Gives us:
+```
+exp(𝑞) = exp(s) * [cos(θ) + sin(θ) * v̂]
+```
+
+Rearranging `𝑣⃗ = θ * v̂` we can solve for v̂: `v̂ = 𝑣⃗ / θ`
+
+```
+exp(𝑞) = exp(s) * [cos(θ) + sin(θ) / θ * 𝑣⃗]
+```
+
+## Quaternion Logarithm
+
+We will take it as a given that for a given quaternion of the form `𝑞 = s + 𝑣⃗`, we can calculate the exponential: `exp(𝑞) = exp(s) * [cos(θ) + sin(θ) / θ * 𝑣⃗]` where `θ = ||𝑣⃗||`.
+
+Additionally, `exp(log(𝑞)) = q` for a given value of `log(𝑞)`. There are multiple solutions to `log(𝑞)` caused by the imaginary axes in 𝑣⃗, discussed here: https://en.wikipedia.org/wiki/Complex_logarithm
+
+We will demonstrate the principal solution of `log(𝑞)` satisfying `exp(log(𝑞)) = q`.
+This being `log(𝑞) = log(||𝑞||) + atan2(θ, s) / θ * 𝑣⃗`, is the principal solution to `log(𝑞)` because the function `atan2(θ, s)` returns the principal value corresponding to its arguments.
+
+Proof: `log(𝑞) = log(||𝑞||) + atan2(θ, s) / θ * 𝑣⃗` satisfies `exp(log(𝑞)) = q`.
+
+```
+exp(log(𝑞)) = exp(log(||𝑞||) + atan2(θ, s) / θ * 𝑣⃗)
+
+
+exp(log(𝑞)) = exp(log(||𝑞||)) * exp(atan2(θ, s) / θ * 𝑣⃗)
+
+Substitutions:
+𝑣⃗ = θ * v̂:
+exp(log(||𝑞||)) = ||𝑞||
+exp(log(𝑞)) = ||𝑞|| * exp(atan2(θ, s) * v̂)
+
+exp(log(𝑞)) = ||𝑞|| * [cos(atan2(θ, s)) + sin(atan2(θ, s)) * v̂]
+
+Substitutions:
+cos(atan2(θ, s)) = s / √(θ² + s²)
+sin(atan2(θ, s)) = θ / √(θ² + s²)
+
+exp(log(𝑞)) = ||𝑞|| * [s / √(θ² + s²) + θ / √(θ² + s²) * v̂]
+
+√(θ² + s²) = ||𝑞||
+
+exp(log(𝑞)) = ||𝑞|| * [s / ||𝑞|| + θ / ||𝑞|| * v̂]
+exp(log(𝑞)) = s + θ * v̂
+
+exp(log(𝑞)) = s + 𝑣⃗
+
+exp(log(𝑞)) = 𝑞
+```
+
+## Unit Quaternion in SO(3) from Rotation Vector in 𝖘𝖔(3)
+
+We will take it as a given that members of 𝖘𝖔(3) take the form `𝑣⃗ = θ * v̂`, representing a rotation θ around a unit axis v̂.
+
+We additionally take it as a given that quaternions in SO(3) are of the form `𝑞 = cos(θ / 2) + sin(θ / 2) * v̂`, representing a rotation of θ around unit axis v̂.
+
+```
+θ = ||𝑣⃗||
+v̂ = 𝑣⃗ / θ
+
+𝑞 = cos(θ / 2) + sin(θ / 2) * v̂
+𝑞 = cos(||𝑣⃗|| / 2) + sin(||𝑣⃗|| / 2) / ||𝑣⃗|| * 𝑣⃗
+```
+
+## Rotation vector in 𝖘𝖔(3) from Unit Quaternion in SO(3)
+
+We will take it as a given that members of 𝖘𝖔(3) take the form  `𝑟⃗ = θ * r̂`, representing a rotation θ around a unit axis r̂.
+
+We additionally take it as a given that quaternions in SO(3) are of the form `𝑞 = s + 𝑣⃗ = cos(θ / 2) + sin(θ / 2) * v̂`, representing a rotation of θ around unit axis v̂.
+
+```
+s + 𝑣⃗ = cos(θ / 2) + sin(θ / 2) * v̂
+s = cos(θ / 2)
+𝑣⃗ = sin(θ / 2) * v̂
+||𝑣⃗|| = sin(θ / 2)
+
+θ / 2 = atan2(||𝑣⃗||, s)
+θ = 2 * atan2(||𝑣⃗||, s)
+
+r̂ = 𝑣⃗ / ||𝑣⃗||
+
+𝑟⃗ = θ * r̂
+𝑟⃗ = 2 * atan2(||𝑣⃗||, s) / ||𝑣⃗|| * 𝑣⃗
+```
+
+## Closed form solution for an Exponential Motion Profile
+
+### [Derivation of continuous-time model](wpimath/algorithms/docs/ExponentialProfileModel.py)
+
+
+### Heuristic for input direction in Exponential Profile
+
+Demonstration: https://www.desmos.com/calculator/3jamollwrk
+
+The fastest path possible for an exponential profile (and the placement of the inflection point) depend on boundary conditions.
+
+Specifically, the placement (xf, vf) relative to the possible trajectories that cross through (x0, v0) decides this. There are two possible trajectories to take from the initial state. In the desmos demo these are colored Green and Purple, which arise from applying +input and -input from the initial state respectively. Red and Yellow trajectories arise from applying -input and +input respectively from terminal conditions.
+
+In order to reach the terminal state from the initial state by following Green in the +v direction, the second step is following Red in the -v direction.
+Likewise, Purple must be followed in the -v direction, and then Yellow must be followed in the +v direction.
+
+The specific conditions surrounding this decision are fourfold:
+- A: v0 >= 0
+- B: vf >= 0
+- C: vf >= x1_ps(vf, U)
+- D: vf >= x1_ps(vf, -U)
+
+Where x1_ps(v, U) follows the Green line, and x1_ps(v, -U) follows the Purple line.
+
+This creates a decision table:
+| v0>=0 | vf>=0 | vf>=x1_ps(vf,U) | vf>=x1_ps(vf,-U) | Output Sign |
+|-------|-------|-----------------|------------------|------------:|
+| False | False | False           | False            |          -1 |
+| False | False | False           | True             |           1 |
+| False | False | True            | False            |           1 |
+| False | False | True            | True             |           1 |
+| False | True  | False           | False            |          -1 |
+| False | True  | False           | True             |          -1 |
+| False | True  | True            | False            |           1 |
+| False | True  | True            | True             |           1 |
+| True  | False | False           | False            |          -1 |
+| True  | False | False           | True             |           1 |
+| True  | False | True            | False            |          -1 |
+| True  | False | True            | True             |           1 |
+| True  | True  | False           | False            |          -1 |
+| True  | True  | False           | True             |          -1 |
+| True  | True  | True            | False            |          -1 |
+| True  | True  | True            | True             |           1 |
+
+Which is equivalent to `-1 if (A & ~D) | (B & ~C) | (~C & ~D) else 1`.
diff --git a/third_party/allwpilib/wpimath/algorithms/ExponentialProfileModel.py b/third_party/allwpilib/wpimath/algorithms/ExponentialProfileModel.py
new file mode 100644
index 0000000..cc8fbc0
--- /dev/null
+++ b/third_party/allwpilib/wpimath/algorithms/ExponentialProfileModel.py
@@ -0,0 +1,98 @@
+from sympy import *
+from sympy.logic.boolalg import *
+
+init_printing()
+
+U, A, B, t, x0, xf, v0, vf, c1, c2, v, V, kV, kA = symbols(
+    "U, A, B t, x0, xf, v0, vf, C1, C2, v, V, kV, kA"
+)
+
+x = symbols("x", cls=Function)
+
+# Exponential profiles are derived from a differential equation: ẍ - A * ẋ = B * U
+diffeq = Eq(x(t).diff(t, t) - A * x(t).diff(t), B * U)
+
+x = dsolve(diffeq).rhs
+dx = x.diff(t)
+
+x = x.subs(
+    [
+        (c1, solve(Eq(x.subs(t, 0), x0), c1)[0]),
+        (c2, solve(Eq(dx.subs(t, 0), v0), c2)[0]),
+    ]
+)
+
+print(f"General Solution: {x}")
+
+# We need two specific solutions to this equation for an Exponential Profile:
+# One that passes through (x0, v0) and has input U
+# Another that passes through (xf, vf) and has input -U
+
+# x1 is for the accelerate step
+x1 = x.subs({x0: x0, v0: v0, U: U})
+
+dx1 = x1.diff(t)
+t1_eqn = solve(Eq(dx1, v), t)[0]
+# x1 in phase space (input v, output x)
+x1_ps = x1.subs(t, t1_eqn)
+
+
+# x2 is for the decelerate step
+x2 = x.subs({x0: xf, v0: vf, U: -U})
+
+dx2 = x2.diff(t)
+t2_eqn = solve(Eq(dx2, v), t)[0]
+# x2 in phase space (input v, output x)
+x2_ps = x2.subs(t, t2_eqn)
+
+# The point at which we switch from input U to -U is the inflection point.
+# In phase space, this is a point (x, v) where x1(v) = x2(v)
+# For now, we will just solve for +U and assume inflection velocity is positive.
+# The other possible solutions are -v_soln, and the solutions to v_equality.subs(U, -U)
+equality = simplify(Eq(x1_ps, x2_ps).expand()).expand()
+equality = Eq(equality.lhs - x0 + v0 / A - v / A, equality.rhs - x0 + v0 / A - v / A)
+equality = Eq(
+    equality.lhs
+    - B * U * log(A * v / (A * vf - B * U) - B * U / (A * vf - B * U)) / A**2,
+    equality.rhs
+    - B * U * log(A * v / (A * vf - B * U) - B * U / (A * vf - B * U)) / A**2,
+)
+equality = Eq(equality.lhs / (-B * U / A / A), equality.rhs / (-B * U / A / A))
+equality = Eq(exp(equality.lhs.simplify()), exp(equality.rhs.simplify()))
+equality = Eq(
+    equality.lhs * (A * v0 + B * U) * (A * vf - B * U),
+    equality.rhs * (A * v0 + B * U) * (A * vf - B * U),
+)
+equality = Eq(-equality.lhs.expand() + equality.rhs, 0)
+
+# This is a quadratic equation of the form ax^2 + c = 0
+v_equality = equality
+
+# solve, take positive result
+v_soln = solve(v_equality, v)[0]
+
+# With this information, we can calculate the inflection point (x, v)
+# and calculate the times that x1 and x2 reach the inflection point
+inflection_x = x1_ps.subs(v, v_soln)
+inflection_t1 = t1_eqn.subs(v, v_soln)
+inflection_t2 = t2_eqn.subs(v, v_soln)
+
+# inflection_t2 < 0 because in order for the profile to get to
+# the inflection point from the terminal state, it must go back in time.
+totalTime = inflection_t1 - inflection_t2
+
+print(f"x1: {expand(simplify(x1))}")
+print(f"x2: {expand(simplify(x2))}")
+print(f"dx1: {expand(simplify(dx1))}")
+print(f"dx2: {expand(simplify(dx2))}")
+print(f"t1: {expand(simplify(t1_eqn))}")
+print(f"t2: {expand(simplify(t2_eqn))}")
+print(f"x1 phase space: {expand(simplify(x1.subs(t, t1_eqn)))}")
+print(f"x2 phase space: {expand(simplify(x2.subs(t, t2_eqn)))}")
+print(f"vi equality: {v_equality}")
+
+
+a, b, c, d = symbols("a, b, c, d")
+
+expression = SOPform([a, b, c, d], minterms=[0, 4, 5, 8, 10, 12, 13, 14])
+print(f"Truth Table Expression: {expression}")
diff --git a/third_party/allwpilib/wpimath/build.gradle b/third_party/allwpilib/wpimath/build.gradle
index 9e7c3a8..09df80c 100644
--- a/third_party/allwpilib/wpimath/build.gradle
+++ b/third_party/allwpilib/wpimath/build.gradle
@@ -9,30 +9,11 @@
 
     nativeName = 'wpimath'
     devMain = 'edu.wpi.first.math.DevMain'
-
-    splitSetup = {
-        it.sources {
-            drakeCpp(CppSourceSet) {
-                source {
-                    srcDirs 'src/main/native/thirdparty/drake/src'
-                    include '**/*.cpp'
-                }
-                exportedHeaders {
-                    srcDirs 'src/main/native/thirdparty/drake/include',
-                            'src/main/native/thirdparty/eigen/include',
-                            'src/main/native/thirdparty/gcem/include'
-                }
-            }
-        }
-    }
 }
 
 apply from: "${rootDir}/shared/jni/setupBuild.gradle"
 
 cppHeadersZip {
-    from('src/main/native/thirdparty/drake/include') {
-        into '/'
-    }
     from('src/main/native/thirdparty/eigen/include') {
         into '/'
     }
@@ -47,7 +28,6 @@
             it.sources.each {
                 it.exportedHeaders {
                     srcDirs 'src/main/native/include',
-                            'src/main/native/thirdparty/drake/include',
                             'src/main/native/thirdparty/eigen/include',
                             'src/main/native/thirdparty/gcem/include'
                 }
@@ -57,10 +37,11 @@
 }
 
 dependencies {
-    api "org.ejml:ejml-simple:0.41"
-    api "com.fasterxml.jackson.core:jackson-annotations:2.12.4"
-    api "com.fasterxml.jackson.core:jackson-core:2.12.4"
-    api "com.fasterxml.jackson.core:jackson-databind:2.12.4"
+    api "org.ejml:ejml-simple:0.43.1"
+    api "com.fasterxml.jackson.core:jackson-annotations:2.15.2"
+    api "com.fasterxml.jackson.core:jackson-core:2.15.2"
+    api "com.fasterxml.jackson.core:jackson-databind:2.15.2"
+    api "us.hebi.quickbuf:quickbuf-runtime:1.3.2"
 }
 
 def wpilibNumberFileInput = file("src/generate/GenericNumber.java.jinja")
@@ -125,3 +106,25 @@
 sourceSets.main.java.srcDir "${buildDir}/generated/java"
 compileJava.dependsOn generateNumbers
 compileJava.dependsOn generateNat
+
+task unitsHeaders(type: Zip) {
+    destinationDirectory = file("$buildDir/outputs")
+    archiveBaseName = zipBaseName
+    archiveClassifier = "units"
+
+    from(licenseFile) {
+        into '/'
+    }
+
+    ext.includeDirs = [
+        project.file('src/main/native/include/units')
+    ]
+
+    ext.includeDirs.each {
+        from(it) {
+            into '/units'
+        }
+    }
+}
+
+addTaskToCopyAllOutputs(unitsHeaders)
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/DARE.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/DARE.java
new file mode 100644
index 0000000..ad07d05
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/DARE.java
@@ -0,0 +1,239 @@
+// 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.
+
+package edu.wpi.first.math;
+
+import org.ejml.simple.SimpleMatrix;
+
+public final class DARE {
+  private DARE() {
+    throw new UnsupportedOperationException("This is a utility class!");
+  }
+
+  /**
+   * Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
+   *
+   * <p>AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
+   *
+   * <p>This internal function skips expensive precondition checks for increased performance. The
+   * solver may hang if any of the following occur:
+   *
+   * <ul>
+   *   <li>Q isn't symmetric positive semidefinite
+   *   <li>R isn't symmetric positive definite
+   *   <li>The (A, B) pair isn't stabilizable
+   *   <li>The (A, C) pair where Q = CᵀC isn't detectable
+   * </ul>
+   *
+   * <p>Only use this function if you're sure the preconditions are met.
+   *
+   * @param <States> Number of states.
+   * @param <Inputs> Number of inputs.
+   * @param A System matrix.
+   * @param B Input matrix.
+   * @param Q State cost matrix.
+   * @param R Input cost matrix.
+   * @return Solution of DARE.
+   */
+  public static <States extends Num, Inputs extends Num> Matrix<States, States> dareDetail(
+      Matrix<States, States> A,
+      Matrix<States, Inputs> B,
+      Matrix<States, States> Q,
+      Matrix<Inputs, Inputs> R) {
+    var S = new Matrix<States, States>(new SimpleMatrix(A.getNumRows(), A.getNumCols()));
+    WPIMathJNI.dareDetailABQR(
+        A.getStorage().getDDRM().getData(),
+        B.getStorage().getDDRM().getData(),
+        Q.getStorage().getDDRM().getData(),
+        R.getStorage().getDDRM().getData(),
+        A.getNumCols(),
+        B.getNumCols(),
+        S.getStorage().getDDRM().getData());
+    return S;
+  }
+
+  /**
+   * Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
+   *
+   * <p>AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
+   *
+   * <p>This is equivalent to solving the original DARE:
+   *
+   * <p>A₂ᵀXA₂ − X − A₂ᵀXB(BᵀXB + R)⁻¹BᵀXA₂ + Q₂ = 0
+   *
+   * <p>where A₂ and Q₂ are a change of variables:
+   *
+   * <p>A₂ = A − BR⁻¹Nᵀ and Q₂ = Q − NR⁻¹Nᵀ
+   *
+   * <p>This overload of the DARE is useful for finding the control law uₖ that minimizes the
+   * following cost function subject to xₖ₊₁ = Axₖ + Buₖ.
+   *
+   * <pre>
+   *     ∞ [xₖ]ᵀ[Q  N][xₖ]
+   * J = Σ [uₖ] [Nᵀ R][uₖ] ΔT
+   *    k=0
+   * </pre>
+   *
+   * <p>This is a more general form of the following. The linear-quadratic regulator is the feedback
+   * control law uₖ that minimizes the following cost function subject to xₖ₊₁ = Axₖ + Buₖ:
+   *
+   * <pre>
+   *     ∞
+   * J = Σ (xₖᵀQxₖ + uₖᵀRuₖ) ΔT
+   *    k=0
+   * </pre>
+   *
+   * <p>This can be refactored as:
+   *
+   * <pre>
+   *     ∞ [xₖ]ᵀ[Q 0][xₖ]
+   * J = Σ [uₖ] [0 R][uₖ] ΔT
+   *    k=0
+   * </pre>
+   *
+   * <p>This internal function skips expensive precondition checks for increased performance. The
+   * solver may hang if any of the following occur:
+   *
+   * <ul>
+   *   <li>Q₂ isn't symmetric positive semidefinite
+   *   <li>R isn't symmetric positive definite
+   *   <li>The (A₂, B) pair isn't stabilizable
+   *   <li>The (A₂, C) pair where Q₂ = CᵀC isn't detectable
+   * </ul>
+   *
+   * <p>Only use this function if you're sure the preconditions are met.
+   *
+   * @param <States> Number of states.
+   * @param <Inputs> Number of inputs.
+   * @param A System matrix.
+   * @param B Input matrix.
+   * @param Q State cost matrix.
+   * @param R Input cost matrix.
+   * @param N State-input cross-term cost matrix.
+   * @return Solution of DARE.
+   */
+  public static <States extends Num, Inputs extends Num> Matrix<States, States> dareDetail(
+      Matrix<States, States> A,
+      Matrix<States, Inputs> B,
+      Matrix<States, States> Q,
+      Matrix<Inputs, Inputs> R,
+      Matrix<States, Inputs> N) {
+    var S = new Matrix<States, States>(new SimpleMatrix(A.getNumRows(), A.getNumCols()));
+    WPIMathJNI.dareDetailABQRN(
+        A.getStorage().getDDRM().getData(),
+        B.getStorage().getDDRM().getData(),
+        Q.getStorage().getDDRM().getData(),
+        R.getStorage().getDDRM().getData(),
+        N.getStorage().getDDRM().getData(),
+        A.getNumCols(),
+        B.getNumCols(),
+        S.getStorage().getDDRM().getData());
+    return S;
+  }
+
+  /**
+   * Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
+   *
+   * <p>AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
+   *
+   * @param <States> Number of states.
+   * @param <Inputs> Number of inputs.
+   * @param A System matrix.
+   * @param B Input matrix.
+   * @param Q State cost matrix.
+   * @param R Input cost matrix.
+   * @return Solution of DARE.
+   * @throws IllegalArgumentException if Q isn't symmetric positive semidefinite.
+   * @throws IllegalArgumentException if R isn't symmetric positive definite.
+   * @throws IllegalArgumentException if the (A, B) pair isn't stabilizable.
+   * @throws IllegalArgumentException if the (A, C) pair where Q = CᵀC isn't detectable.
+   */
+  public static <States extends Num, Inputs extends Num> Matrix<States, States> dare(
+      Matrix<States, States> A,
+      Matrix<States, Inputs> B,
+      Matrix<States, States> Q,
+      Matrix<Inputs, Inputs> R) {
+    var S = new Matrix<States, States>(new SimpleMatrix(A.getNumRows(), A.getNumCols()));
+    WPIMathJNI.dareABQR(
+        A.getStorage().getDDRM().getData(),
+        B.getStorage().getDDRM().getData(),
+        Q.getStorage().getDDRM().getData(),
+        R.getStorage().getDDRM().getData(),
+        A.getNumCols(),
+        B.getNumCols(),
+        S.getStorage().getDDRM().getData());
+    return S;
+  }
+
+  /**
+   * Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
+   *
+   * <p>AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
+   *
+   * <p>This is equivalent to solving the original DARE:
+   *
+   * <p>A₂ᵀXA₂ − X − A₂ᵀXB(BᵀXB + R)⁻¹BᵀXA₂ + Q₂ = 0
+   *
+   * <p>where A₂ and Q₂ are a change of variables:
+   *
+   * <p>A₂ = A − BR⁻¹Nᵀ and Q₂ = Q − NR⁻¹Nᵀ
+   *
+   * <p>This overload of the DARE is useful for finding the control law uₖ that minimizes the
+   * following cost function subject to xₖ₊₁ = Axₖ + Buₖ.
+   *
+   * <pre>
+   *     ∞ [xₖ]ᵀ[Q  N][xₖ]
+   * J = Σ [uₖ] [Nᵀ R][uₖ] ΔT
+   *    k=0
+   * </pre>
+   *
+   * <p>This is a more general form of the following. The linear-quadratic regulator is the feedback
+   * control law uₖ that minimizes the following cost function subject to xₖ₊₁ = Axₖ + Buₖ:
+   *
+   * <pre>
+   *     ∞
+   * J = Σ (xₖᵀQxₖ + uₖᵀRuₖ) ΔT
+   *    k=0
+   * </pre>
+   *
+   * <p>This can be refactored as:
+   *
+   * <pre>
+   *     ∞ [xₖ]ᵀ[Q 0][xₖ]
+   * J = Σ [uₖ] [0 R][uₖ] ΔT
+   *    k=0
+   * </pre>
+   *
+   * @param <States> Number of states.
+   * @param <Inputs> Number of inputs.
+   * @param A System matrix.
+   * @param B Input matrix.
+   * @param Q State cost matrix.
+   * @param R Input cost matrix.
+   * @param N State-input cross-term cost matrix.
+   * @return Solution of DARE.
+   * @throws IllegalArgumentException if Q₂ isn't symmetric positive semidefinite.
+   * @throws IllegalArgumentException if R isn't symmetric positive definite.
+   * @throws IllegalArgumentException if the (A₂, B) pair isn't stabilizable.
+   * @throws IllegalArgumentException if the (A₂, C) pair where Q₂ = CᵀC isn't detectable.
+   */
+  public static <States extends Num, Inputs extends Num> Matrix<States, States> dare(
+      Matrix<States, States> A,
+      Matrix<States, Inputs> B,
+      Matrix<States, States> Q,
+      Matrix<Inputs, Inputs> R,
+      Matrix<States, Inputs> N) {
+    var S = new Matrix<States, States>(new SimpleMatrix(A.getNumRows(), A.getNumCols()));
+    WPIMathJNI.dareABQRN(
+        A.getStorage().getDDRM().getData(),
+        B.getStorage().getDDRM().getData(),
+        Q.getStorage().getDDRM().getData(),
+        R.getStorage().getDDRM().getData(),
+        N.getStorage().getDDRM().getData(),
+        A.getNumCols(),
+        B.getNumCols(),
+        S.getStorage().getDDRM().getData());
+    return S;
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Drake.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Drake.java
deleted file mode 100644
index 55bc5b2..0000000
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Drake.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// 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.
-
-package edu.wpi.first.math;
-
-import org.ejml.simple.SimpleMatrix;
-
-public final class Drake {
-  private Drake() {}
-
-  /**
-   * Solves the discrete algebraic Riccati equation.
-   *
-   * @param A System matrix.
-   * @param B Input matrix.
-   * @param Q State cost matrix.
-   * @param R Input cost matrix.
-   * @return Solution of DARE.
-   */
-  public static SimpleMatrix discreteAlgebraicRiccatiEquation(
-      SimpleMatrix A, SimpleMatrix B, SimpleMatrix Q, SimpleMatrix R) {
-    var S = new SimpleMatrix(A.numRows(), A.numCols());
-    WPIMathJNI.discreteAlgebraicRiccatiEquation(
-        A.getDDRM().getData(),
-        B.getDDRM().getData(),
-        Q.getDDRM().getData(),
-        R.getDDRM().getData(),
-        A.numCols(),
-        B.numCols(),
-        S.getDDRM().getData());
-    return S;
-  }
-
-  /**
-   * Solves the discrete algebraic Riccati equation.
-   *
-   * @param <States> Number of states.
-   * @param <Inputs> Number of inputs.
-   * @param A System matrix.
-   * @param B Input matrix.
-   * @param Q State cost matrix.
-   * @param R Input cost matrix.
-   * @return Solution of DARE.
-   */
-  public static <States extends Num, Inputs extends Num>
-      Matrix<States, States> discreteAlgebraicRiccatiEquation(
-          Matrix<States, States> A,
-          Matrix<States, Inputs> B,
-          Matrix<States, States> Q,
-          Matrix<Inputs, Inputs> R) {
-    return new Matrix<>(
-        discreteAlgebraicRiccatiEquation(
-            A.getStorage(), B.getStorage(), Q.getStorage(), R.getStorage()));
-  }
-
-  /**
-   * Solves the discrete algebraic Riccati equation.
-   *
-   * @param A System matrix.
-   * @param B Input matrix.
-   * @param Q State cost matrix.
-   * @param R Input cost matrix.
-   * @param N State-input cross-term cost matrix.
-   * @return Solution of DARE.
-   */
-  public static SimpleMatrix discreteAlgebraicRiccatiEquation(
-      SimpleMatrix A, SimpleMatrix B, SimpleMatrix Q, SimpleMatrix R, SimpleMatrix N) {
-    // See
-    // https://en.wikipedia.org/wiki/Linear%E2%80%93quadratic_regulator#Infinite-horizon,_discrete-time_LQR
-    // for the change of variables used here.
-    var scrA = A.minus(B.mult(R.solve(N.transpose())));
-    var scrQ = Q.minus(N.mult(R.solve(N.transpose())));
-
-    var S = new SimpleMatrix(A.numRows(), A.numCols());
-    WPIMathJNI.discreteAlgebraicRiccatiEquation(
-        scrA.getDDRM().getData(),
-        B.getDDRM().getData(),
-        scrQ.getDDRM().getData(),
-        R.getDDRM().getData(),
-        A.numCols(),
-        B.numCols(),
-        S.getDDRM().getData());
-    return S;
-  }
-
-  /**
-   * Solves the discrete algebraic Riccati equation.
-   *
-   * @param <States> Number of states.
-   * @param <Inputs> Number of inputs.
-   * @param A System matrix.
-   * @param B Input matrix.
-   * @param Q State cost matrix.
-   * @param R Input cost matrix.
-   * @param N State-input cross-term cost matrix.
-   * @return Solution of DARE.
-   */
-  public static <States extends Num, Inputs extends Num>
-      Matrix<States, States> discreteAlgebraicRiccatiEquation(
-          Matrix<States, States> A,
-          Matrix<States, Inputs> B,
-          Matrix<States, States> Q,
-          Matrix<Inputs, Inputs> R,
-          Matrix<States, Inputs> N) {
-    // See
-    // https://en.wikipedia.org/wiki/Linear%E2%80%93quadratic_regulator#Infinite-horizon,_discrete-time_LQR
-    // for the change of variables used here.
-    var scrA = A.minus(B.times(R.solve(N.transpose())));
-    var scrQ = Q.minus(N.times(R.solve(N.transpose())));
-
-    return new Matrix<>(
-        discreteAlgebraicRiccatiEquation(
-            scrA.getStorage(), B.getStorage(), scrQ.getStorage(), R.getStorage()));
-  }
-}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathShared.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathShared.java
index 483dad3..3c2b843 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathShared.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathShared.java
@@ -20,4 +20,11 @@
    * @param count the usage count
    */
   void reportUsage(MathUsageId id, int count);
+
+  /**
+   * Get the current time.
+   *
+   * @return Time in seconds
+   */
+  double getTimestamp();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathSharedStore.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathSharedStore.java
index 0dbc03d..d6e3e96 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathSharedStore.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathSharedStore.java
@@ -4,6 +4,8 @@
 
 package edu.wpi.first.math;
 
+import edu.wpi.first.util.WPIUtilJNI;
+
 public final class MathSharedStore {
   private static MathShared mathShared;
 
@@ -23,6 +25,11 @@
 
             @Override
             public void reportUsage(MathUsageId id, int count) {}
+
+            @Override
+            public double getTimestamp() {
+              return WPIUtilJNI.now() * 1.0e-6;
+            }
           };
     }
     return mathShared;
@@ -56,4 +63,13 @@
   public static void reportUsage(MathUsageId id, int count) {
     getMathShared().reportUsage(id, count);
   }
+
+  /**
+   * Get the time.
+   *
+   * @return The time in seconds.
+   */
+  public static double getTimestamp() {
+    return getMathShared().getTimestamp();
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java
index 95ed5bf..3785780 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java
@@ -145,4 +145,67 @@
   public static double interpolate(double startValue, double endValue, double t) {
     return startValue + (endValue - startValue) * MathUtil.clamp(t, 0, 1);
   }
+
+  /**
+   * Return where within interpolation range [0, 1] q is between startValue and endValue.
+   *
+   * @param startValue Lower part of interpolation range.
+   * @param endValue Upper part of interpolation range.
+   * @param q Query.
+   * @return Interpolant in range [0, 1].
+   */
+  public static double inverseInterpolate(double startValue, double endValue, double q) {
+    double totalRange = endValue - startValue;
+    if (totalRange <= 0) {
+      return 0.0;
+    }
+    double queryToStart = q - startValue;
+    if (queryToStart <= 0) {
+      return 0.0;
+    }
+    return queryToStart / totalRange;
+  }
+
+  /**
+   * Checks if the given value matches an expected value within a certain tolerance.
+   *
+   * @param expected The expected value
+   * @param actual The actual value
+   * @param tolerance The allowed difference between the actual and the expected value
+   * @return Whether or not the actual value is within the allowed tolerance
+   */
+  public static boolean isNear(double expected, double actual, double tolerance) {
+    if (tolerance < 0) {
+      throw new IllegalArgumentException("Tolerance must be a non-negative number!");
+    }
+    return Math.abs(expected - actual) < tolerance;
+  }
+
+  /**
+   * Checks if the given value matches an expected value within a certain tolerance. Supports
+   * continuous input for cases like absolute encoders.
+   *
+   * <p>Continuous input means that the min and max value are considered to be the same point, and
+   * tolerances can be checked across them. A common example would be for absolute encoders: calling
+   * isNear(2, 359, 5, 0, 360) returns true because 359 is 1 away from 360 (which is treated as the
+   * same as 0) and 2 is 2 away from 0, adding up to an error of 3 degrees, which is within the
+   * given tolerance of 5.
+   *
+   * @param expected The expected value
+   * @param actual The actual value
+   * @param tolerance The allowed difference between the actual and the expected value
+   * @param min Smallest value before wrapping around to the largest value
+   * @param max Largest value before wrapping around to the smallest value
+   * @return Whether or not the actual value is within the allowed tolerance
+   */
+  public static boolean isNear(
+      double expected, double actual, double tolerance, double min, double max) {
+    if (tolerance < 0) {
+      throw new IllegalArgumentException("Tolerance must be a non-negative number!");
+    }
+    // Max error is exactly halfway between the min and max
+    double errorBound = (max - min) / 2.0;
+    double error = MathUtil.inputModulus(expected - actual, -errorBound, errorBound);
+    return Math.abs(error) < tolerance;
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Matrix.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Matrix.java
index b8e7c28..b7a86fa 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Matrix.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Matrix.java
@@ -42,6 +42,18 @@
    * Constructs a new {@link Matrix} with the given storage. Caller should make sure that the
    * provided generic bounds match the shape of the provided {@link Matrix}.
    *
+   * @param rows The number of rows of the matrix.
+   * @param columns The number of columns of the matrix.
+   * @param storage The double array to back this value.
+   */
+  public Matrix(Nat<R> rows, Nat<C> columns, double[] storage) {
+    this.m_storage = new SimpleMatrix(rows.getNum(), columns.getNum(), true, storage);
+  }
+
+  /**
+   * Constructs a new {@link Matrix} with the given storage. Caller should make sure that the
+   * provided generic bounds match the shape of the provided {@link Matrix}.
+   *
    * <p>NOTE:It is not recommend to use this constructor unless the {@link SimpleMatrix} API is
    * absolutely necessary due to the desired function not being accessible through the {@link
    * Matrix} wrapper.
@@ -80,7 +92,7 @@
    * @return The number of columns, according to the internal storage.
    */
   public final int getNumCols() {
-    return this.m_storage.numCols();
+    return this.m_storage.getNumCols();
   }
 
   /**
@@ -89,7 +101,7 @@
    * @return The number of rows, according to the internal storage.
    */
   public final int getNumRows() {
-    return this.m_storage.numRows();
+    return this.m_storage.getNumRows();
   }
 
   /**
@@ -222,7 +234,7 @@
    *
    * <p>c<sub>i,j</sub> = a<sub>i,j</sub>*other<sub>i,j</sub>
    *
-   * @param other The other {@link Matrix} to preform element multiplication on.
+   * @param other The other {@link Matrix} to perform element multiplication on.
    * @return The element by element multiplication of "this" and other.
    */
   public final Matrix<R, C> elementTimes(Matrix<R, C> other) {
@@ -582,7 +594,7 @@
     SimpleMatrix temp = m_storage.copy();
 
     CholeskyDecomposition_F64<DMatrixRMaj> chol =
-        DecompositionFactory_DDRM.chol(temp.numRows(), lowerTriangular);
+        DecompositionFactory_DDRM.chol(temp.getNumRows(), lowerTriangular);
     if (!chol.decompose(temp.getMatrix())) {
       // check that the input is not all zeros -- if they are, we special case and return all
       // zeros.
@@ -592,7 +604,7 @@
         isZeros &= Math.abs(matDatum) < 1e-6;
       }
       if (isZeros) {
-        return new Matrix<>(new SimpleMatrix(temp.numRows(), temp.numCols()));
+        return new Matrix<>(new SimpleMatrix(temp.getNumRows(), temp.getNumCols()));
       }
 
       throw new RuntimeException(
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/SimpleMatrixUtils.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/SimpleMatrixUtils.java
index e1680eb..487faf4 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/SimpleMatrixUtils.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/SimpleMatrixUtils.java
@@ -68,7 +68,7 @@
 
   private static Pair<SimpleMatrix, SimpleMatrix> pade3(SimpleMatrix A) {
     double[] b = new double[] {120, 60, 12, 1};
-    SimpleMatrix ident = eye(A.numRows(), A.numCols());
+    SimpleMatrix ident = eye(A.getNumRows(), A.getNumCols());
 
     SimpleMatrix A2 = A.mult(A);
     SimpleMatrix U = A.mult(A2.mult(ident.scale(b[1]).plus(b[3])));
@@ -78,7 +78,7 @@
 
   private static Pair<SimpleMatrix, SimpleMatrix> pade5(SimpleMatrix A) {
     double[] b = new double[] {30240, 15120, 3360, 420, 30, 1};
-    SimpleMatrix ident = eye(A.numRows(), A.numCols());
+    SimpleMatrix ident = eye(A.getNumRows(), A.getNumCols());
     SimpleMatrix A2 = A.mult(A);
     SimpleMatrix A4 = A2.mult(A2);
 
@@ -90,7 +90,7 @@
 
   private static Pair<SimpleMatrix, SimpleMatrix> pade7(SimpleMatrix A) {
     double[] b = new double[] {17297280, 8648640, 1995840, 277200, 25200, 1512, 56, 1};
-    SimpleMatrix ident = eye(A.numRows(), A.numCols());
+    SimpleMatrix ident = eye(A.getNumRows(), A.getNumCols());
     SimpleMatrix A2 = A.mult(A);
     SimpleMatrix A4 = A2.mult(A2);
     SimpleMatrix A6 = A4.mult(A2);
@@ -108,7 +108,7 @@
         new double[] {
           17643225600.0, 8821612800.0, 2075673600, 302702400, 30270240, 2162160, 110880, 3960, 90, 1
         };
-    SimpleMatrix ident = eye(A.numRows(), A.numCols());
+    SimpleMatrix ident = eye(A.getNumRows(), A.getNumCols());
     SimpleMatrix A2 = A.mult(A);
     SimpleMatrix A4 = A2.mult(A2);
     SimpleMatrix A6 = A4.mult(A2);
@@ -149,7 +149,7 @@
           182,
           1
         };
-    SimpleMatrix ident = eye(A.numRows(), A.numCols());
+    SimpleMatrix ident = eye(A.getNumRows(), A.getNumCols());
 
     SimpleMatrix A2 = A.mult(A);
     SimpleMatrix A4 = A2.mult(A2);
@@ -213,7 +213,7 @@
     SimpleMatrix temp = src.copy();
 
     CholeskyDecomposition_F64<DMatrixRMaj> chol =
-        DecompositionFactory_DDRM.chol(temp.numRows(), lowerTriangular);
+        DecompositionFactory_DDRM.chol(temp.getNumRows(), lowerTriangular);
     if (!chol.decompose(temp.getMatrix())) {
       // check that the input is not all zeros -- if they are, we special case and return all
       // zeros.
@@ -223,7 +223,7 @@
         isZeros &= Math.abs(matDatum) < 1e-6;
       }
       if (isZeros) {
-        return new SimpleMatrix(temp.numRows(), temp.numCols());
+        return new SimpleMatrix(temp.getNumRows(), temp.getNumCols());
       }
 
       throw new RuntimeException("Cholesky decomposition failed! Input matrix:\n" + src.toString());
@@ -239,8 +239,8 @@
    * @return the exponential of A.
    */
   public static SimpleMatrix exp(SimpleMatrix A) {
-    SimpleMatrix toReturn = new SimpleMatrix(A.numRows(), A.numRows());
-    WPIMathJNI.exp(A.getDDRM().getData(), A.numRows(), toReturn.getDDRM().getData());
+    SimpleMatrix toReturn = new SimpleMatrix(A.getNumRows(), A.getNumRows());
+    WPIMathJNI.exp(A.getDDRM().getData(), A.getNumRows(), toReturn.getDDRM().getData());
     return toReturn;
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/StateSpaceUtil.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/StateSpaceUtil.java
index a041845..27ed9e0 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/StateSpaceUtil.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/StateSpaceUtil.java
@@ -15,7 +15,7 @@
   private static Random rand = new Random();
 
   private StateSpaceUtil() {
-    // Utility class
+    throw new UnsupportedOperationException("This is a utility class!");
   }
 
   /**
@@ -90,7 +90,7 @@
    * Returns true if (A, B) is a stabilizable pair.
    *
    * <p>(A, B) is stabilizable if and only if the uncontrollable eigenvalues of A, if any, have
-   * absolute values less than one, where an eigenvalue is uncontrollable if rank(λI - A, B) %3C n
+   * absolute values less than one, where an eigenvalue is uncontrollable if rank([λI - A, B]) %3C n
    * where n is the number of states.
    *
    * @param <States> Num representing the size of A.
@@ -108,7 +108,7 @@
    * Returns true if (A, C) is a detectable pair.
    *
    * <p>(A, C) is detectable if and only if the unobservable eigenvalues of A, if any, have absolute
-   * values less than one, where an eigenvalue is unobservable if rank(λI - A; C) %3C n where n is
+   * values less than one, where an eigenvalue is unobservable if rank([λI - A; C]) %3C n where n is
    * the number of states.
    *
    * @param <States> Num representing the size of A.
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/VecBuilder.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/VecBuilder.java
index 670611a..9ba91b0 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/VecBuilder.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/VecBuilder.java
@@ -14,6 +14,8 @@
 import edu.wpi.first.math.numbers.N7;
 import edu.wpi.first.math.numbers.N8;
 import edu.wpi.first.math.numbers.N9;
+import java.util.Objects;
+import org.ejml.simple.SimpleMatrix;
 
 /**
  * A specialization of {@link MatBuilder} for constructing vectors (Nx1 matrices).
@@ -26,7 +28,15 @@
   }
 
   private Vector<N> fillVec(double... data) {
-    return new Vector<>(fill(data));
+    if (Objects.requireNonNull(data).length != this.m_rows.getNum()) {
+      throw new IllegalArgumentException(
+          "Invalid vector data provided. Wanted "
+              + this.m_rows.getNum()
+              + " vector, but got "
+              + data.length
+              + " elements");
+    }
+    return new Vector<>(new SimpleMatrix(this.m_rows.getNum(), 1, true, data));
   }
 
   /**
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Vector.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Vector.java
index 1aebdde..bc46741 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Vector.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/Vector.java
@@ -5,6 +5,7 @@
 package edu.wpi.first.math;
 
 import edu.wpi.first.math.numbers.N1;
+import java.util.Objects;
 import org.ejml.simple.SimpleMatrix;
 
 /**
@@ -63,6 +64,26 @@
   }
 
   /**
+   * Adds the given vector to this vector.
+   *
+   * @param value The vector to add.
+   * @return The resultant vector.
+   */
+  public final Vector<R> plus(Vector<R> value) {
+    return new Vector<>(this.m_storage.plus(Objects.requireNonNull(value).m_storage));
+  }
+
+  /**
+   * Subtracts the given vector to this vector.
+   *
+   * @param value The vector to add.
+   * @return The resultant vector.
+   */
+  public final Vector<R> minus(Vector<R> value) {
+    return new Vector<>(this.m_storage.minus(Objects.requireNonNull(value).m_storage));
+  }
+
+  /**
    * Returns the dot product of this vector with another.
    *
    * @param other The other vector.
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java
index 40a5c63..ef47a3b 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java
@@ -43,8 +43,25 @@
     libraryLoaded = true;
   }
 
+  // DARE wrappers
+
   /**
-   * Solves the discrete alegebraic Riccati equation.
+   * Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
+   *
+   * <p>AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
+   *
+   * <p>This internal function skips expensive precondition checks for increased performance. The
+   * solver may hang if any of the following occur:
+   *
+   * <ul>
+   *   <li>Q isn't symmetric positive semidefinite
+   *   <li>R isn't symmetric positive definite
+   *   <li>The (A, B) pair isn't stabilizable
+   *   <li>The (A, C) pair where Q = CᵀC isn't detectable
+   * </ul>
+   *
+   * <p>Only use this function if you're sure the preconditions are met. Solves the discrete
+   * alegebraic Riccati equation.
    *
    * @param A Array containing elements of A in row-major order.
    * @param B Array containing elements of B in row-major order.
@@ -54,10 +71,148 @@
    * @param inputs Number of inputs in B matrix.
    * @param S Array storage for DARE solution.
    */
-  public static native void discreteAlgebraicRiccatiEquation(
+  public static native void dareDetailABQR(
       double[] A, double[] B, double[] Q, double[] R, int states, int inputs, double[] S);
 
   /**
+   * Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
+   *
+   * <p>AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
+   *
+   * <p>This overload of the DARE is useful for finding the control law uₖ that minimizes the
+   * following cost function subject to xₖ₊₁ = Axₖ + Buₖ.
+   *
+   * <pre>
+   *     ∞ [xₖ]ᵀ[Q  N][xₖ]
+   * J = Σ [uₖ] [Nᵀ R][uₖ] ΔT
+   *    k=0
+   * </pre>
+   *
+   * <p>This is a more general form of the following. The linear-quadratic regulator is the feedback
+   * control law uₖ that minimizes the following cost function subject to xₖ₊₁ = Axₖ + Buₖ:
+   *
+   * <pre>
+   *     ∞
+   * J = Σ (xₖᵀQxₖ + uₖᵀRuₖ) ΔT
+   *    k=0
+   * </pre>
+   *
+   * <p>This can be refactored as:
+   *
+   * <pre>
+   *     ∞ [xₖ]ᵀ[Q 0][xₖ]
+   * J = Σ [uₖ] [0 R][uₖ] ΔT
+   *    k=0
+   * </pre>
+   *
+   * <p>This internal function skips expensive precondition checks for increased performance. The
+   * solver may hang if any of the following occur:
+   *
+   * <ul>
+   *   <li>Q − NR⁻¹Nᵀ isn't symmetric positive semidefinite
+   *   <li>R isn't symmetric positive definite
+   *   <li>The (A − BR⁻¹Nᵀ, B) pair isn't stabilizable
+   *   <li>The (A, C) pair where Q = CᵀC isn't detectable
+   * </ul>
+   *
+   * <p>Only use this function if you're sure the preconditions are met.
+   *
+   * @param A Array containing elements of A in row-major order.
+   * @param B Array containing elements of B in row-major order.
+   * @param Q Array containing elements of Q in row-major order.
+   * @param R Array containing elements of R in row-major order.
+   * @param N Array containing elements of N in row-major order.
+   * @param states Number of states in A matrix.
+   * @param inputs Number of inputs in B matrix.
+   * @param S Array storage for DARE solution.
+   */
+  public static native void dareDetailABQRN(
+      double[] A,
+      double[] B,
+      double[] Q,
+      double[] R,
+      double[] N,
+      int states,
+      int inputs,
+      double[] S);
+
+  /**
+   * Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
+   *
+   * <p>AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
+   *
+   * @param A Array containing elements of A in row-major order.
+   * @param B Array containing elements of B in row-major order.
+   * @param Q Array containing elements of Q in row-major order.
+   * @param R Array containing elements of R in row-major order.
+   * @param states Number of states in A matrix.
+   * @param inputs Number of inputs in B matrix.
+   * @param S Array storage for DARE solution.
+   * @throws IllegalArgumentException if Q isn't symmetric positive semidefinite.
+   * @throws IllegalArgumentException if R isn't symmetric positive definite.
+   * @throws IllegalArgumentException if the (A, B) pair isn't stabilizable.
+   * @throws IllegalArgumentException if the (A, C) pair where Q = CᵀC isn't detectable.
+   */
+  public static native void dareABQR(
+      double[] A, double[] B, double[] Q, double[] R, int states, int inputs, double[] S);
+
+  /**
+   * Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
+   *
+   * <p>AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
+   *
+   * <p>This overload of the DARE is useful for finding the control law uₖ that minimizes the
+   * following cost function subject to xₖ₊₁ = Axₖ + Buₖ.
+   *
+   * <pre>
+   *     ∞ [xₖ]ᵀ[Q  N][xₖ]
+   * J = Σ [uₖ] [Nᵀ R][uₖ] ΔT
+   *    k=0
+   * </pre>
+   *
+   * <p>This is a more general form of the following. The linear-quadratic regulator is the feedback
+   * control law uₖ that minimizes the following cost function subject to xₖ₊₁ = Axₖ + Buₖ:
+   *
+   * <pre>
+   *     ∞
+   * J = Σ (xₖᵀQxₖ + uₖᵀRuₖ) ΔT
+   *    k=0
+   * </pre>
+   *
+   * <p>This can be refactored as:
+   *
+   * <pre>
+   *     ∞ [xₖ]ᵀ[Q 0][xₖ]
+   * J = Σ [uₖ] [0 R][uₖ] ΔT
+   *    k=0
+   * </pre>
+   *
+   * @param A Array containing elements of A in row-major order.
+   * @param B Array containing elements of B in row-major order.
+   * @param Q Array containing elements of Q in row-major order.
+   * @param R Array containing elements of R in row-major order.
+   * @param N Array containing elements of N in row-major order.
+   * @param states Number of states in A matrix.
+   * @param inputs Number of inputs in B matrix.
+   * @param S Array storage for DARE solution.
+   * @throws IllegalArgumentException if Q − NR⁻¹Nᵀ isn't symmetric positive semidefinite.
+   * @throws IllegalArgumentException if R isn't symmetric positive definite.
+   * @throws IllegalArgumentException if the (A − BR⁻¹Nᵀ, B) pair isn't stabilizable.
+   * @throws IllegalArgumentException if the (A, C) pair where Q = CᵀC isn't detectable.
+   */
+  public static native void dareABQRN(
+      double[] A,
+      double[] B,
+      double[] Q,
+      double[] R,
+      double[] N,
+      int states,
+      int inputs,
+      double[] S);
+
+  // Eigen wrappers
+
+  /**
    * Computes the matrix exp.
    *
    * @param src Array of elements of the matrix to be exponentiated.
@@ -77,6 +232,109 @@
   public static native void pow(double[] src, int rows, double exponent, double[] dst);
 
   /**
+   * Performs an inplace rank one update (or downdate) of an upper triangular Cholesky decomposition
+   * matrix.
+   *
+   * @param mat Array of elements of the matrix to be updated.
+   * @param lowerTriangular Whether mat is lower triangular.
+   * @param rows How many rows there are.
+   * @param vec Vector to use for the rank update.
+   * @param sigma Sigma value to use for the rank update.
+   */
+  public static native void rankUpdate(
+      double[] mat, int rows, double[] vec, double sigma, boolean lowerTriangular);
+
+  /**
+   * Solves the least-squares problem Ax=B using a QR decomposition with full pivoting.
+   *
+   * @param A Array of elements of the A matrix.
+   * @param Arows Number of rows of the A matrix.
+   * @param Acols Number of rows of the A matrix.
+   * @param B Array of elements of the B matrix.
+   * @param Brows Number of rows of the B matrix.
+   * @param Bcols Number of rows of the B matrix.
+   * @param dst Array to store solution in. If A is m-n and B is m-p, dst is n-p.
+   */
+  public static native void solveFullPivHouseholderQr(
+      double[] A, int Arows, int Acols, double[] B, int Brows, int Bcols, double[] dst);
+
+  // Pose3d wrappers
+
+  /**
+   * Obtain a Pose3d from a (constant curvature) velocity.
+   *
+   * <p>The double array returned is of the form [dx, dy, dz, qx, qy, qz].
+   *
+   * @param poseX The pose's translational X component.
+   * @param poseY The pose's translational Y component.
+   * @param poseZ The pose's translational Z component.
+   * @param poseQw The pose quaternion's W component.
+   * @param poseQx The pose quaternion's X component.
+   * @param poseQy The pose quaternion's Y component.
+   * @param poseQz The pose quaternion's Z component.
+   * @param twistDx The twist's dx value.
+   * @param twistDy The twist's dy value.
+   * @param twistDz The twist's dz value.
+   * @param twistRx The twist's rx value.
+   * @param twistRy The twist's ry value.
+   * @param twistRz The twist's rz value.
+   * @return The new pose as a double array.
+   */
+  public static native double[] expPose3d(
+      double poseX,
+      double poseY,
+      double poseZ,
+      double poseQw,
+      double poseQx,
+      double poseQy,
+      double poseQz,
+      double twistDx,
+      double twistDy,
+      double twistDz,
+      double twistRx,
+      double twistRy,
+      double twistRz);
+
+  /**
+   * Returns a Twist3d that maps the starting pose to the end pose.
+   *
+   * <p>The double array returned is of the form [dx, dy, dz, rx, ry, rz].
+   *
+   * @param startX The starting pose's translational X component.
+   * @param startY The starting pose's translational Y component.
+   * @param startZ The starting pose's translational Z component.
+   * @param startQw The starting pose quaternion's W component.
+   * @param startQx The starting pose quaternion's X component.
+   * @param startQy The starting pose quaternion's Y component.
+   * @param startQz The starting pose quaternion's Z component.
+   * @param endX The ending pose's translational X component.
+   * @param endY The ending pose's translational Y component.
+   * @param endZ The ending pose's translational Z component.
+   * @param endQw The ending pose quaternion's W component.
+   * @param endQx The ending pose quaternion's X component.
+   * @param endQy The ending pose quaternion's Y component.
+   * @param endQz The ending pose quaternion's Z component.
+   * @return The twist that maps start to end as a double array.
+   */
+  public static native double[] logPose3d(
+      double startX,
+      double startY,
+      double startZ,
+      double startQw,
+      double startQx,
+      double startQy,
+      double startQz,
+      double endX,
+      double endY,
+      double endZ,
+      double endQw,
+      double endQx,
+      double endQy,
+      double endQz);
+
+  // StateSpaceUtil wrappers
+
+  /**
    * Returns true if (A, B) is a stabilizable pair.
    *
    * <p>(A, B) is stabilizable if and only if the uncontrollable eigenvalues of A, if any, have
@@ -91,6 +349,8 @@
    */
   public static native boolean isStabilizable(int states, int inputs, double[] A, double[] B);
 
+  // Trajectory wrappers
+
   /**
    * Loads a Pathweaver JSON.
    *
@@ -125,19 +385,6 @@
    */
   public static native String serializeTrajectory(double[] elements);
 
-  /**
-   * Performs an inplace rank one update (or downdate) of an upper triangular Cholesky decomposition
-   * matrix.
-   *
-   * @param mat Array of elements of the matrix to be updated.
-   * @param lowerTriangular Whether mat is lower triangular.
-   * @param rows How many rows there are.
-   * @param vec Vector to use for the rank update.
-   * @param sigma Sigma value to use for the rank update.
-   */
-  public static native void rankUpdate(
-      double[] mat, int rows, double[] vec, double sigma, boolean lowerTriangular);
-
   public static class Helper {
     private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
 
@@ -149,18 +396,4 @@
       extractOnStaticLoad.set(load);
     }
   }
-
-  /**
-   * Solves the least-squares problem Ax=B using a QR decomposition with full pivoting.
-   *
-   * @param A Array of elements of the A matrix.
-   * @param Arows Number of rows of the A matrix.
-   * @param Acols Number of rows of the A matrix.
-   * @param B Array of elements of the B matrix.
-   * @param Brows Number of rows of the B matrix.
-   * @param Bcols Number of rows of the B matrix.
-   * @param dst Array to store solution in. If A is m-n and B is m-p, dst is n-p.
-   */
-  public static native void solveFullPivHouseholderQr(
-      double[] A, int Arows, int Acols, double[] B, int Brows, int Bcols, double[] dst);
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java
index 385c5c0..a2a5944 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java
@@ -181,6 +181,9 @@
   public Matrix<Inputs, N1> calculate(Matrix<States, N1> r, Matrix<States, N1> nextR) {
     var rDot = (nextR.minus(r)).div(m_dt);
 
+    // ṙ = f(r) + Bu
+    // Bu = ṙ − f(r)
+    // u = B⁺(ṙ − f(r))
     m_uff = m_B.solve(rDot.minus(m_f.apply(r, new Matrix<>(m_inputs, Nat.N1()))));
 
     m_r = nextR;
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java
index 9f4dd86..cfe7c2b 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java
@@ -4,6 +4,10 @@
 
 package edu.wpi.first.math.controller;
 
+import edu.wpi.first.math.Matrix;
+import edu.wpi.first.math.Nat;
+import edu.wpi.first.math.system.plant.LinearSystemId;
+
 /**
  * A helper class that computes feedforward outputs for a simple elevator (modeled as a motor acting
  * against the force of gravity).
@@ -54,6 +58,48 @@
   }
 
   /**
+   * Calculates the feedforward from the gains and setpoints.
+   *
+   * <p>Note this method is inaccurate when the velocity crosses 0.
+   *
+   * @param currentVelocity The current velocity setpoint.
+   * @param nextVelocity The next velocity setpoint.
+   * @param dtSeconds Time between velocity setpoints in seconds.
+   * @return The computed feedforward.
+   */
+  public double calculate(double currentVelocity, double nextVelocity, double dtSeconds) {
+    // Discretize the affine model.
+    //
+    //   dx/dt = Ax + Bu + c
+    //   dx/dt = Ax + B(u + B⁺c)
+    //   xₖ₊₁ = eᴬᵀxₖ + A⁻¹(eᴬᵀ - I)B(uₖ + B⁺cₖ)
+    //   xₖ₊₁ = A_d xₖ + B_d (uₖ + B⁺cₖ)
+    //   xₖ₊₁ = A_d xₖ + B_duₖ + B_d B⁺cₖ
+    //
+    // Solve for uₖ.
+    //
+    //   B_duₖ = xₖ₊₁ − A_d xₖ − B_d B⁺cₖ
+    //   uₖ = B_d⁺(xₖ₊₁ − A_d xₖ − B_d B⁺cₖ)
+    //   uₖ = B_d⁺(xₖ₊₁ − A_d xₖ) − B⁺cₖ
+    //
+    // For an elevator with the model
+    // dx/dt = -Kv/Ka x + 1/Ka u - Kg/Ka - Ks/Ka sgn(x),
+    // A = -Kv/Ka, B = 1/Ka, and c = -(Kg/Ka + Ks/Ka sgn(x)). Substitute in B
+    // assuming sgn(x) is a constant for the duration of the step.
+    //
+    //   uₖ = B_d⁺(xₖ₊₁ − A_d xₖ) − Ka(-(Kg/Ka + Ks/Ka sgn(x)))
+    //   uₖ = B_d⁺(xₖ₊₁ − A_d xₖ) + Ka(Kg/Ka + Ks/Ka sgn(x))
+    //   uₖ = B_d⁺(xₖ₊₁ − A_d xₖ) + Kg + Ks sgn(x)
+    var plant = LinearSystemId.identifyVelocitySystem(this.kv, this.ka);
+    var feedforward = new LinearPlantInversionFeedforward<>(plant, dtSeconds);
+
+    var r = Matrix.mat(Nat.N1(), Nat.N1()).fill(currentVelocity);
+    var nextR = Matrix.mat(Nat.N1(), Nat.N1()).fill(nextVelocity);
+
+    return kg + ks * Math.signum(currentVelocity) + feedforward.calculate(r, nextR).get(0, 0);
+  }
+
+  /**
    * Calculates the feedforward from the gains and velocity setpoint (acceleration is assumed to be
    * zero).
    *
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/HolonomicDriveController.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/HolonomicDriveController.java
index e43c6ff..87148a4 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/HolonomicDriveController.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/HolonomicDriveController.java
@@ -139,4 +139,31 @@
   public void setEnabled(boolean enabled) {
     m_enabled = enabled;
   }
+
+  /**
+   * Returns the heading controller.
+   *
+   * @return heading ProfiledPIDController
+   */
+  public ProfiledPIDController getThetaController() {
+    return m_thetaController;
+  }
+
+  /**
+   * Returns the x controller.
+   *
+   * @return X PIDController
+   */
+  public PIDController getXController() {
+    return m_xController;
+  }
+
+  /**
+   * Returns the y controller.
+   *
+   * @return Y PIDController
+   */
+  public PIDController getYController() {
+    return m_yController;
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LTVDifferentialDriveController.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LTVDifferentialDriveController.java
index 46254a3..2be2a47 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LTVDifferentialDriveController.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LTVDifferentialDriveController.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.math.controller;
 
+import edu.wpi.first.math.DARE;
 import edu.wpi.first.math.InterpolatingMatrixTreeMap;
 import edu.wpi.first.math.MatBuilder;
 import edu.wpi.first.math.MathUtil;
@@ -15,14 +16,22 @@
 import edu.wpi.first.math.numbers.N1;
 import edu.wpi.first.math.numbers.N2;
 import edu.wpi.first.math.numbers.N5;
+import edu.wpi.first.math.system.Discretization;
 import edu.wpi.first.math.system.LinearSystem;
 import edu.wpi.first.math.trajectory.Trajectory;
 
 /**
  * The linear time-varying differential drive controller has a similar form to the LQR, but the
- * model used to compute the controller gain is the nonlinear model linearized around the
- * drivetrain's current state. We precomputed gains for important places in our state-space, then
- * interpolated between them with a LUT to save computational resources.
+ * model used to compute the controller gain is the nonlinear differential drive model linearized
+ * around the drivetrain's current state. We precompute gains for important places in our
+ * state-space, then interpolate between them with a lookup table to save computational resources.
+ *
+ * <p>This controller has a flat hierarchy with pose and wheel velocity references and voltage
+ * outputs. This is different from a Ramsete controller's nested hierarchy where the top-level
+ * controller has a pose reference and chassis velocity command outputs, and the low-level
+ * controller has wheel velocity references and voltage outputs. Flat hierarchies are easier to tune
+ * in one shot. Furthermore, this controller is more optimal in the "least-squares error" sense than
+ * a controller based on Ramsete.
  *
  * <p>See section 8.7 in Controls Engineering in FRC for a derivation of the control law we used
  * shown in theorem 8.7.4.
@@ -55,12 +64,18 @@
   /**
    * Constructs a linear time-varying differential drive controller.
    *
+   * <p>See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
+   * for how to select the tolerances.
+   *
    * @param plant The differential drive velocity plant.
    * @param trackwidth The distance between the differential drive's left and right wheels in
    *     meters.
    * @param qelems The maximum desired error tolerance for each state.
    * @param relems The maximum desired control effort for each input.
    * @param dt Discretization timestep in seconds.
+   * @throws IllegalArgumentException if max velocity of plant with 12 V input &lt;= 0 m/s or &gt;=
+   *     15 m/s.
    */
   public LTVDifferentialDriveController(
       LinearSystem<N2, N2, N2> plant,
@@ -127,15 +142,39 @@
             .times(-1.0)
             .get(0, 0);
 
+    if (maxV <= 0.0) {
+      throw new IllegalArgumentException(
+          "Max velocity of plant with 12 V input must be greater than 0 m/s.");
+    }
+    if (maxV >= 15.0) {
+      throw new IllegalArgumentException(
+          "Max velocity of plant with 12 V input must be less than 15 m/s.");
+    }
+
     for (double velocity = -maxV; velocity < maxV; velocity += 0.01) {
       // The DARE is ill-conditioned if the velocity is close to zero, so don't
       // let the system stop.
       if (Math.abs(velocity) < 1e-4) {
-        m_table.put(velocity, new Matrix<>(Nat.N2(), Nat.N5()));
+        A.set(State.kY.value, State.kHeading.value, 1e-4);
       } else {
         A.set(State.kY.value, State.kHeading.value, velocity);
-        m_table.put(velocity, new LinearQuadraticRegulator<N5, N2, N5>(A, B, Q, R, dt).getK());
       }
+
+      var discABPair = Discretization.discretizeAB(A, B, dt);
+      var discA = discABPair.getFirst();
+      var discB = discABPair.getSecond();
+
+      var S = DARE.dareDetail(discA, discB, Q, R);
+
+      // K = (BᵀSB + R)⁻¹BᵀSA
+      m_table.put(
+          velocity,
+          discB
+              .transpose()
+              .times(S)
+              .times(discB)
+              .plus(R)
+              .solve(discB.transpose().times(S).times(discA)));
     }
   }
 
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LTVUnicycleController.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LTVUnicycleController.java
index 701f21b..7e3391f 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LTVUnicycleController.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LTVUnicycleController.java
@@ -4,6 +4,7 @@
 
 package edu.wpi.first.math.controller;
 
+import edu.wpi.first.math.DARE;
 import edu.wpi.first.math.InterpolatingMatrixTreeMap;
 import edu.wpi.first.math.MatBuilder;
 import edu.wpi.first.math.Matrix;
@@ -15,12 +16,16 @@
 import edu.wpi.first.math.kinematics.ChassisSpeeds;
 import edu.wpi.first.math.numbers.N2;
 import edu.wpi.first.math.numbers.N3;
+import edu.wpi.first.math.system.Discretization;
 import edu.wpi.first.math.trajectory.Trajectory;
 
 /**
  * The linear time-varying unicycle controller has a similar form to the LQR, but the model used to
- * compute the controller gain is the nonlinear model linearized around the drivetrain's current
- * state.
+ * compute the controller gain is the nonlinear unicycle model linearized around the drivetrain's
+ * current state.
+ *
+ * <p>This controller is a roughly drop-in replacement for {@link RamseteController} with more
+ * optimal feedback gains in the "least-squares error" sense.
  *
  * <p>See section 8.9 in Controls Engineering in FRC for a derivation of the control law we used
  * shown in theorem 8.9.1.
@@ -66,6 +71,7 @@
    * @param dt Discretization timestep in seconds.
    * @param maxVelocity The maximum velocity in meters per second for the controller gain lookup
    *     table. The default is 9 m/s.
+   * @throws IllegalArgumentException if maxVelocity &lt;= 0.
    */
   public LTVUnicycleController(double dt, double maxVelocity) {
     this(VecBuilder.fill(0.0625, 0.125, 2.0), VecBuilder.fill(1.0, 2.0), dt, maxVelocity);
@@ -74,6 +80,10 @@
   /**
    * Constructs a linear time-varying unicycle controller.
    *
+   * <p>See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
+   * for how to select the tolerances.
+   *
    * @param qelems The maximum desired error tolerance for each state.
    * @param relems The maximum desired control effort for each input.
    * @param dt Discretization timestep in seconds.
@@ -85,14 +95,26 @@
   /**
    * Constructs a linear time-varying unicycle controller.
    *
+   * <p>See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
+   * for how to select the tolerances.
+   *
    * @param qelems The maximum desired error tolerance for each state.
    * @param relems The maximum desired control effort for each input.
    * @param dt Discretization timestep in seconds.
    * @param maxVelocity The maximum velocity in meters per second for the controller gain lookup
    *     table. The default is 9 m/s.
+   * @throws IllegalArgumentException if maxVelocity &lt;= 0 m/s or &gt;= 15 m/s.
    */
   public LTVUnicycleController(
       Vector<N3> qelems, Vector<N2> relems, double dt, double maxVelocity) {
+    if (maxVelocity <= 0.0) {
+      throw new IllegalArgumentException("Max velocity must be greater than 0 m/s.");
+    }
+    if (maxVelocity >= 15.0) {
+      throw new IllegalArgumentException("Max velocity must be less than 15 m/s.");
+    }
+
     // The change in global pose for a unicycle is defined by the following
     // three equations.
     //
@@ -135,11 +157,26 @@
       // The DARE is ill-conditioned if the velocity is close to zero, so don't
       // let the system stop.
       if (Math.abs(velocity) < 1e-4) {
-        m_table.put(velocity, new Matrix<>(Nat.N2(), Nat.N3()));
+        A.set(State.kY.value, State.kHeading.value, 1e-4);
       } else {
         A.set(State.kY.value, State.kHeading.value, velocity);
-        m_table.put(velocity, new LinearQuadraticRegulator<N3, N2, N3>(A, B, Q, R, dt).getK());
       }
+
+      var discABPair = Discretization.discretizeAB(A, B, dt);
+      var discA = discABPair.getFirst();
+      var discB = discABPair.getSecond();
+
+      var S = DARE.dareDetail(discA, discB, Q, R);
+
+      // K = (BᵀSB + R)⁻¹BᵀSA
+      m_table.put(
+          velocity,
+          discB
+              .transpose()
+              .times(S)
+              .times(discB)
+              .plus(R)
+              .solve(discB.transpose().times(S).times(discA)));
     }
   }
 
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java
index a762851..b0f23a8 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java
@@ -139,6 +139,9 @@
    * @return The calculated feedforward.
    */
   public Matrix<Inputs, N1> calculate(Matrix<States, N1> r, Matrix<States, N1> nextR) {
+    // rₖ₊₁ = Arₖ + Buₖ
+    // Buₖ = rₖ₊₁ − Arₖ
+    // uₖ = B⁺(rₖ₊₁ − Arₖ)
     m_uff = new Matrix<>(m_B.solve(nextR.minus(m_A.times(r))));
 
     m_r = nextR;
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java
index dce1748..658be69 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java
@@ -4,7 +4,7 @@
 
 package edu.wpi.first.math.controller;
 
-import edu.wpi.first.math.Drake;
+import edu.wpi.first.math.DARE;
 import edu.wpi.first.math.MathSharedStore;
 import edu.wpi.first.math.Matrix;
 import edu.wpi.first.math.Num;
@@ -35,6 +35,10 @@
   /**
    * Constructs a controller with the given coefficients and plant. Rho is defaulted to 1.
    *
+   * <p>See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
+   * for how to select the tolerances.
+   *
    * @param plant The plant being controlled.
    * @param qelms The maximum desired error tolerance for each state.
    * @param relms The maximum desired control effort for each input.
@@ -57,6 +61,10 @@
   /**
    * Constructs a controller with the given coefficients and plant.
    *
+   * <p>See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
+   * for how to select the tolerances.
+   *
    * @param A Continuous system matrix of the plant being controlled.
    * @param B Continuous input matrix of the plant being controlled.
    * @param qelms The maximum desired error tolerance for each state.
@@ -111,7 +119,7 @@
       throw new IllegalArgumentException(msg);
     }
 
-    var S = Drake.discreteAlgebraicRiccatiEquation(discA, discB, Q, R);
+    var S = DARE.dare(discA, discB, Q, R);
 
     // K = (BᵀSB + R)⁻¹BᵀSA
     m_K =
@@ -150,7 +158,7 @@
     var discA = discABPair.getFirst();
     var discB = discABPair.getSecond();
 
-    var S = Drake.discreteAlgebraicRiccatiEquation(discA, discB, Q, R, N);
+    var S = DARE.dare(discA, discB, Q, R, N);
 
     // K = (BᵀSB + R)⁻¹(BᵀSA + Nᵀ)
     m_K =
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/PIDController.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/PIDController.java
index 5e82909..1d98900 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/PIDController.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/PIDController.java
@@ -24,6 +24,9 @@
   // Factor for "derivative" control
   private double m_kd;
 
+  // The error range where "integral" control applies
+  private double m_iZone = Double.POSITIVE_INFINITY;
+
   // The period (in seconds) of the loop that calls the controller
   private final double m_period;
 
@@ -55,6 +58,9 @@
   private double m_setpoint;
   private double m_measurement;
 
+  private boolean m_haveMeasurement;
+  private boolean m_haveSetpoint;
+
   /**
    * Allocates a PIDController with the given constants for kp, ki, and kd and a default period of
    * 0.02 seconds.
@@ -139,6 +145,22 @@
   }
 
   /**
+   * Sets the IZone range. When the absolute value of the position error is greater than IZone, the
+   * total accumulated error will reset to zero, disabling integral gain until the absolute value of
+   * the position error is less than IZone. This is used to prevent integral windup. Must be
+   * non-negative. Passing a value of zero will effectively disable integral gain. Passing a value
+   * of {@link Double#POSITIVE_INFINITY} disables IZone functionality.
+   *
+   * @param iZone Maximum magnitude of error to allow integral control.
+   */
+  public void setIZone(double iZone) {
+    if (iZone < 0) {
+      throw new IllegalArgumentException("IZone must be a non-negative number!");
+    }
+    m_iZone = iZone;
+  }
+
+  /**
    * Get the Proportional coefficient.
    *
    * @return proportional coefficient
@@ -166,6 +188,15 @@
   }
 
   /**
+   * Get the IZone range.
+   *
+   * @return Maximum magnitude of error to allow integral control.
+   */
+  public double getIZone() {
+    return m_iZone;
+  }
+
+  /**
    * Returns the period of this controller.
    *
    * @return the period of the controller.
@@ -199,6 +230,7 @@
    */
   public void setSetpoint(double setpoint) {
     m_setpoint = setpoint;
+    m_haveSetpoint = true;
 
     if (m_continuous) {
       double errorBound = (m_maximumInput - m_minimumInput) / 2.0;
@@ -227,7 +259,9 @@
    * @return Whether the error is within the acceptable bounds.
    */
   public boolean atSetpoint() {
-    return Math.abs(m_positionError) < m_positionTolerance
+    return m_haveMeasurement
+        && m_haveSetpoint
+        && Math.abs(m_positionError) < m_positionTolerance
         && Math.abs(m_velocityError) < m_velocityTolerance;
   }
 
@@ -321,6 +355,7 @@
    */
   public double calculate(double measurement, double setpoint) {
     m_setpoint = setpoint;
+    m_haveSetpoint = true;
     return calculate(measurement);
   }
 
@@ -333,6 +368,7 @@
   public double calculate(double measurement) {
     m_measurement = measurement;
     m_prevError = m_positionError;
+    m_haveMeasurement = true;
 
     if (m_continuous) {
       double errorBound = (m_maximumInput - m_minimumInput) / 2.0;
@@ -343,7 +379,10 @@
 
     m_velocityError = (m_positionError - m_prevError) / m_period;
 
-    if (m_ki != 0) {
+    // If the absolute value of the position error is greater than IZone, reset the total error
+    if (Math.abs(m_positionError) > m_iZone) {
+      m_totalError = 0;
+    } else if (m_ki != 0) {
       m_totalError =
           MathUtil.clamp(
               m_totalError + m_positionError * m_period,
@@ -360,6 +399,7 @@
     m_prevError = 0;
     m_totalError = 0;
     m_velocityError = 0;
+    m_haveMeasurement = false;
   }
 
   @Override
@@ -368,6 +408,7 @@
     builder.addDoubleProperty("p", this::getP, this::setP);
     builder.addDoubleProperty("i", this::getI, this::setI);
     builder.addDoubleProperty("d", this::getD, this::setD);
+    builder.addDoubleProperty("izone", this::getIZone, this::setIZone);
     builder.addDoubleProperty("setpoint", this::getSetpoint, this::setSetpoint);
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ProfiledPIDController.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ProfiledPIDController.java
index 02cc17d..80311f5 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ProfiledPIDController.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/controller/ProfiledPIDController.java
@@ -22,9 +22,11 @@
   private PIDController m_controller;
   private double m_minimumInput;
   private double m_maximumInput;
+
+  private TrapezoidProfile.Constraints m_constraints;
+  private TrapezoidProfile m_profile;
   private TrapezoidProfile.State m_goal = new TrapezoidProfile.State();
   private TrapezoidProfile.State m_setpoint = new TrapezoidProfile.State();
-  private TrapezoidProfile.Constraints m_constraints;
 
   /**
    * Allocates a ProfiledPIDController with the given constants for Kp, Ki, and Kd.
@@ -52,6 +54,7 @@
       double Kp, double Ki, double Kd, TrapezoidProfile.Constraints constraints, double period) {
     m_controller = new PIDController(Kp, Ki, Kd, period);
     m_constraints = constraints;
+    m_profile = new TrapezoidProfile(m_constraints);
     instances++;
 
     SendableRegistry.add(this, "ProfiledPIDController", instances);
@@ -99,6 +102,19 @@
   }
 
   /**
+   * Sets the IZone range. When the absolute value of the position error is greater than IZone, the
+   * total accumulated error will reset to zero, disabling integral gain until the absolute value of
+   * the position error is less than IZone. This is used to prevent integral windup. Must be
+   * non-negative. Passing a value of zero will effectively disable integral gain. Passing a value
+   * of {@link Double#POSITIVE_INFINITY} disables IZone functionality.
+   *
+   * @param iZone Maximum magnitude of error to allow integral control.
+   */
+  public void setIZone(double iZone) {
+    m_controller.setIZone(iZone);
+  }
+
+  /**
    * Gets the proportional coefficient.
    *
    * @return proportional coefficient
@@ -126,6 +142,15 @@
   }
 
   /**
+   * Get the IZone range.
+   *
+   * @return Maximum magnitude of error to allow integral control.
+   */
+  public double getIZone() {
+    return m_controller.getIZone();
+  }
+
+  /**
    * Gets the period of this controller.
    *
    * @return The period of the controller.
@@ -197,6 +222,16 @@
    */
   public void setConstraints(TrapezoidProfile.Constraints constraints) {
     m_constraints = constraints;
+    m_profile = new TrapezoidProfile(m_constraints);
+  }
+
+  /**
+   * Get the velocity and acceleration constraints for this controller.
+   *
+   * @return Velocity and acceleration constraints.
+   */
+  public TrapezoidProfile.Constraints getConstraints() {
+    return m_constraints;
   }
 
   /**
@@ -312,8 +347,7 @@
       m_setpoint.position = setpointMinDistance + measurement;
     }
 
-    var profile = new TrapezoidProfile(m_constraints, m_goal, m_setpoint);
-    m_setpoint = profile.calculate(getPeriod());
+    m_setpoint = m_profile.calculate(getPeriod(), m_goal, m_setpoint);
     return m_controller.calculate(measurement, m_setpoint.position);
   }
 
@@ -391,6 +425,7 @@
     builder.addDoubleProperty("p", this::getP, this::setP);
     builder.addDoubleProperty("i", this::getI, this::setI);
     builder.addDoubleProperty("d", this::getD, this::setD);
+    builder.addDoubleProperty("izone", this::getIZone, this::setIZone);
     builder.addDoubleProperty("goal", () -> getGoal().position, this::setGoal);
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimator.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimator.java
index b5fe591..7918f9f 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimator.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimator.java
@@ -4,22 +4,15 @@
 
 package edu.wpi.first.math.estimator;
 
-import edu.wpi.first.math.MathUtil;
 import edu.wpi.first.math.Matrix;
-import edu.wpi.first.math.Nat;
 import edu.wpi.first.math.VecBuilder;
 import edu.wpi.first.math.geometry.Pose2d;
 import edu.wpi.first.math.geometry.Rotation2d;
-import edu.wpi.first.math.geometry.Twist2d;
-import edu.wpi.first.math.interpolation.Interpolatable;
-import edu.wpi.first.math.interpolation.TimeInterpolatableBuffer;
 import edu.wpi.first.math.kinematics.DifferentialDriveKinematics;
 import edu.wpi.first.math.kinematics.DifferentialDriveOdometry;
+import edu.wpi.first.math.kinematics.DifferentialDriveWheelPositions;
 import edu.wpi.first.math.numbers.N1;
 import edu.wpi.first.math.numbers.N3;
-import edu.wpi.first.util.WPIUtilJNI;
-import java.util.Map;
-import java.util.Objects;
 
 /**
  * This class wraps {@link DifferentialDriveOdometry Differential Drive Odometry} to fuse
@@ -34,15 +27,7 @@
  * <p>{@link DifferentialDrivePoseEstimator#addVisionMeasurement} can be called as infrequently as
  * you want; if you never call it then this class will behave exactly like regular encoder odometry.
  */
-public class DifferentialDrivePoseEstimator {
-  private final DifferentialDriveKinematics m_kinematics;
-  private final DifferentialDriveOdometry m_odometry;
-  private final Matrix<N3, N1> m_q = new Matrix<>(Nat.N3(), Nat.N1());
-  private Matrix<N3, N3> m_visionK = new Matrix<>(Nat.N3(), Nat.N3());
-
-  private final TimeInterpolatableBuffer<InterpolationRecord> m_poseBuffer =
-      TimeInterpolatableBuffer.createBuffer(1.5);
-
+public class DifferentialDrivePoseEstimator extends PoseEstimator<DifferentialDriveWheelPositions> {
   /**
    * Constructs a DifferentialDrivePoseEstimator with default standard deviations for the model and
    * vision measurements.
@@ -96,44 +81,12 @@
       Pose2d initialPoseMeters,
       Matrix<N3, N1> stateStdDevs,
       Matrix<N3, N1> visionMeasurementStdDevs) {
-    m_kinematics = kinematics;
-    m_odometry =
+    super(
+        kinematics,
         new DifferentialDriveOdometry(
-            gyroAngle, leftDistanceMeters, rightDistanceMeters, initialPoseMeters);
-
-    for (int i = 0; i < 3; ++i) {
-      m_q.set(i, 0, stateStdDevs.get(i, 0) * stateStdDevs.get(i, 0));
-    }
-
-    // Initialize vision R
-    setVisionMeasurementStdDevs(visionMeasurementStdDevs);
-  }
-
-  /**
-   * Sets the pose estimator's trust of global measurements. This might be used to change trust in
-   * vision measurements after the autonomous period, or to change trust as distance to a vision
-   * target increases.
-   *
-   * @param visionMeasurementStdDevs Standard deviations of the vision measurements. Increase these
-   *     numbers to trust global measurements from vision less. This matrix is in the form [x, y,
-   *     theta]ᵀ, with units in meters and radians.
-   */
-  public void setVisionMeasurementStdDevs(Matrix<N3, N1> visionMeasurementStdDevs) {
-    var r = new double[3];
-    for (int i = 0; i < 3; ++i) {
-      r[i] = visionMeasurementStdDevs.get(i, 0) * visionMeasurementStdDevs.get(i, 0);
-    }
-
-    // Solve for closed form Kalman gain for continuous Kalman filter with A = 0
-    // and C = I. See wpimath/algorithms.md.
-    for (int row = 0; row < 3; ++row) {
-      if (m_q.get(row, 0) == 0.0) {
-        m_visionK.set(row, row, 0.0);
-      } else {
-        m_visionK.set(
-            row, row, m_q.get(row, 0) / (m_q.get(row, 0) + Math.sqrt(m_q.get(row, 0) * r[row])));
-      }
-    }
+            gyroAngle, leftDistanceMeters, rightDistanceMeters, initialPoseMeters),
+        stateStdDevs,
+        visionMeasurementStdDevs);
   }
 
   /**
@@ -152,111 +105,10 @@
       double leftPositionMeters,
       double rightPositionMeters,
       Pose2d poseMeters) {
-    // Reset state estimate and error covariance
-    m_odometry.resetPosition(gyroAngle, leftPositionMeters, rightPositionMeters, poseMeters);
-    m_poseBuffer.clear();
-  }
-
-  /**
-   * Gets the estimated robot pose.
-   *
-   * @return The estimated robot pose in meters.
-   */
-  public Pose2d getEstimatedPosition() {
-    return m_odometry.getPoseMeters();
-  }
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate
-   * while still accounting for measurement noise.
-   *
-   * <p>This method can be called as infrequently as you want, as long as you are calling {@link
-   * DifferentialDrivePoseEstimator#update} every loop.
-   *
-   * <p>To promote stability of the pose estimate and make it robust to bad vision data, we
-   * recommend only adding vision measurements that are already within one meter or so of the
-   * current pose estimate.
-   *
-   * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera.
-   * @param timestampSeconds The timestamp of the vision measurement in seconds. Note that if you
-   *     don't use your own time source by calling {@link
-   *     DifferentialDrivePoseEstimator#updateWithTime(double,Rotation2d,double,double)} then you
-   *     must use a timestamp with an epoch since FPGA startup (i.e., the epoch of this timestamp is
-   *     the same epoch as {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()}.) This means that
-   *     you should use {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()} as your time source
-   *     or sync the epochs.
-   */
-  public void addVisionMeasurement(Pose2d visionRobotPoseMeters, double timestampSeconds) {
-    // Step 1: Get the pose odometry measured at the moment the vision measurement was made.
-    var sample = m_poseBuffer.getSample(timestampSeconds);
-
-    if (sample.isEmpty()) {
-      return;
-    }
-
-    // Step 2: Measure the twist between the odometry pose and the vision pose.
-    var twist = sample.get().poseMeters.log(visionRobotPoseMeters);
-
-    // Step 3: We should not trust the twist entirely, so instead we scale this twist by a Kalman
-    // gain matrix representing how much we trust vision measurements compared to our current pose.
-    var k_times_twist = m_visionK.times(VecBuilder.fill(twist.dx, twist.dy, twist.dtheta));
-
-    // Step 4: Convert back to Twist2d.
-    var scaledTwist =
-        new Twist2d(k_times_twist.get(0, 0), k_times_twist.get(1, 0), k_times_twist.get(2, 0));
-
-    // Step 5: Reset Odometry to state at sample with vision adjustment.
-    m_odometry.resetPosition(
-        sample.get().gyroAngle,
-        sample.get().leftMeters,
-        sample.get().rightMeters,
-        sample.get().poseMeters.exp(scaledTwist));
-
-    // Step 6: Replay odometry inputs between sample time and latest recorded sample to update the
-    // pose buffer and correct odometry.
-    for (Map.Entry<Double, InterpolationRecord> entry :
-        m_poseBuffer.getInternalBuffer().tailMap(timestampSeconds).entrySet()) {
-      updateWithTime(
-          entry.getKey(),
-          entry.getValue().gyroAngle,
-          entry.getValue().leftMeters,
-          entry.getValue().rightMeters);
-    }
-  }
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate
-   * while still accounting for measurement noise.
-   *
-   * <p>This method can be called as infrequently as you want, as long as you are calling {@link
-   * DifferentialDrivePoseEstimator#update} every loop.
-   *
-   * <p>To promote stability of the pose estimate and make it robust to bad vision data, we
-   * recommend only adding vision measurements that are already within one meter or so of the
-   * current pose estimate.
-   *
-   * <p>Note that the vision measurement standard deviations passed into this method will continue
-   * to apply to future measurements until a subsequent call to {@link
-   * DifferentialDrivePoseEstimator#setVisionMeasurementStdDevs(Matrix)} or this method.
-   *
-   * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera.
-   * @param timestampSeconds The timestamp of the vision measurement in seconds. Note that if you
-   *     don't use your own time source by calling {@link
-   *     DifferentialDrivePoseEstimator#updateWithTime(double,Rotation2d,double,double)}, then you
-   *     must use a timestamp with an epoch since FPGA startup (i.e., the epoch of this timestamp is
-   *     the same epoch as {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()}). This means that
-   *     you should use {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()} as your time source
-   *     in this case.
-   * @param visionMeasurementStdDevs Standard deviations of the vision pose measurement (x position
-   *     in meters, y position in meters, and heading in radians). Increase these numbers to trust
-   *     the vision pose measurement less.
-   */
-  public void addVisionMeasurement(
-      Pose2d visionRobotPoseMeters,
-      double timestampSeconds,
-      Matrix<N3, N1> visionMeasurementStdDevs) {
-    setVisionMeasurementStdDevs(visionMeasurementStdDevs);
-    addVisionMeasurement(visionRobotPoseMeters, timestampSeconds);
+    resetPosition(
+        gyroAngle,
+        new DifferentialDriveWheelPositions(leftPositionMeters, rightPositionMeters),
+        poseMeters);
   }
 
   /**
@@ -270,8 +122,8 @@
    */
   public Pose2d update(
       Rotation2d gyroAngle, double distanceLeftMeters, double distanceRightMeters) {
-    return updateWithTime(
-        WPIUtilJNI.now() * 1.0e-6, gyroAngle, distanceLeftMeters, distanceRightMeters);
+    return update(
+        gyroAngle, new DifferentialDriveWheelPositions(distanceLeftMeters, distanceRightMeters));
   }
 
   /**
@@ -289,98 +141,9 @@
       Rotation2d gyroAngle,
       double distanceLeftMeters,
       double distanceRightMeters) {
-    m_odometry.update(gyroAngle, distanceLeftMeters, distanceRightMeters);
-    m_poseBuffer.addSample(
+    return updateWithTime(
         currentTimeSeconds,
-        new InterpolationRecord(
-            getEstimatedPosition(), gyroAngle, distanceLeftMeters, distanceRightMeters));
-
-    return getEstimatedPosition();
-  }
-
-  /**
-   * Represents an odometry record. The record contains the inputs provided as well as the pose that
-   * was observed based on these inputs, as well as the previous record and its inputs.
-   */
-  private class InterpolationRecord implements Interpolatable<InterpolationRecord> {
-    // The pose observed given the current sensor inputs and the previous pose.
-    private final Pose2d poseMeters;
-
-    // The current gyro angle.
-    private final Rotation2d gyroAngle;
-
-    // The distance traveled by the left encoder.
-    private final double leftMeters;
-
-    // The distance traveled by the right encoder.
-    private final double rightMeters;
-
-    /**
-     * Constructs an Interpolation Record with the specified parameters.
-     *
-     * @param pose The pose observed given the current sensor inputs and the previous pose.
-     * @param gyro The current gyro angle.
-     * @param leftMeters The distance traveled by the left encoder.
-     * @param rightMeters The distanced traveled by the right encoder.
-     */
-    private InterpolationRecord(
-        Pose2d poseMeters, Rotation2d gyro, double leftMeters, double rightMeters) {
-      this.poseMeters = poseMeters;
-      this.gyroAngle = gyro;
-      this.leftMeters = leftMeters;
-      this.rightMeters = rightMeters;
-    }
-
-    /**
-     * Return the interpolated record. This object is assumed to be the starting position, or lower
-     * bound.
-     *
-     * @param endValue The upper bound, or end.
-     * @param t How far between the lower and upper bound we are. This should be bounded in [0, 1].
-     * @return The interpolated value.
-     */
-    @Override
-    public InterpolationRecord interpolate(InterpolationRecord endValue, double t) {
-      if (t < 0) {
-        return this;
-      } else if (t >= 1) {
-        return endValue;
-      } else {
-        // Find the new left distance.
-        var left_lerp = MathUtil.interpolate(this.leftMeters, endValue.leftMeters, t);
-
-        // Find the new right distance.
-        var right_lerp = MathUtil.interpolate(this.rightMeters, endValue.rightMeters, t);
-
-        // Find the new gyro angle.
-        var gyro_lerp = gyroAngle.interpolate(endValue.gyroAngle, t);
-
-        // Create a twist to represent this change based on the interpolated sensor inputs.
-        Twist2d twist = m_kinematics.toTwist2d(left_lerp - leftMeters, right_lerp - rightMeters);
-        twist.dtheta = gyro_lerp.minus(gyroAngle).getRadians();
-
-        return new InterpolationRecord(poseMeters.exp(twist), gyro_lerp, left_lerp, right_lerp);
-      }
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (this == obj) {
-        return true;
-      }
-      if (!(obj instanceof InterpolationRecord)) {
-        return false;
-      }
-      InterpolationRecord record = (InterpolationRecord) obj;
-      return Objects.equals(gyroAngle, record.gyroAngle)
-          && Double.compare(leftMeters, record.leftMeters) == 0
-          && Double.compare(rightMeters, record.rightMeters) == 0
-          && Objects.equals(poseMeters, record.poseMeters);
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(gyroAngle, leftMeters, rightMeters, poseMeters);
-    }
+        gyroAngle,
+        new DifferentialDriveWheelPositions(distanceLeftMeters, distanceRightMeters));
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java
index 5f9e52e..59323b2 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java
@@ -4,7 +4,7 @@
 
 package edu.wpi.first.math.estimator;
 
-import edu.wpi.first.math.Drake;
+import edu.wpi.first.math.DARE;
 import edu.wpi.first.math.Matrix;
 import edu.wpi.first.math.Nat;
 import edu.wpi.first.math.Num;
@@ -59,6 +59,10 @@
   /**
    * Constructs an extended Kalman filter.
    *
+   * <p>See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-observers.html#process-and-measurement-noise-covariance-matrices
+   * for how to select the standard deviations.
+   *
    * @param states a Nat representing the number of states.
    * @param inputs a Nat representing the number of inputs.
    * @param outputs a Nat representing the number of outputs.
@@ -93,6 +97,10 @@
   /**
    * Constructs an extended Kalman filter.
    *
+   * <p>See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-observers.html#process-and-measurement-noise-covariance-matrices
+   * for how to select the standard deviations.
+   *
    * @param states a Nat representing the number of states.
    * @param inputs a Nat representing the number of inputs.
    * @param outputs a Nat representing the number of outputs.
@@ -138,15 +146,14 @@
         NumericalJacobian.numericalJacobianX(
             outputs, states, h, m_xHat, new Matrix<>(inputs, Nat.N1()));
 
-    final var discPair = Discretization.discretizeAQTaylor(contA, m_contQ, dtSeconds);
+    final var discPair = Discretization.discretizeAQ(contA, m_contQ, dtSeconds);
     final var discA = discPair.getFirst();
     final var discQ = discPair.getSecond();
 
     final var discR = Discretization.discretizeR(m_contR, dtSeconds);
 
     if (StateSpaceUtil.isDetectable(discA, C) && outputs.getNum() <= states.getNum()) {
-      m_initP =
-          Drake.discreteAlgebraicRiccatiEquation(discA.transpose(), C.transpose(), discQ, discR);
+      m_initP = DARE.dare(discA.transpose(), C.transpose(), discQ, discR);
     } else {
       m_initP = new Matrix<>(states, states);
     }
@@ -260,7 +267,7 @@
     final var contA = NumericalJacobian.numericalJacobianX(m_states, m_states, f, m_xHat, u);
 
     // Find discrete A and Q
-    final var discPair = Discretization.discretizeAQTaylor(contA, m_contQ, dtSeconds);
+    final var discPair = Discretization.discretizeAQ(contA, m_contQ, dtSeconds);
     final var discA = discPair.getFirst();
     final var discQ = discPair.getSecond();
 
@@ -286,24 +293,14 @@
   /**
    * Correct the state estimate x-hat using the measurements in y.
    *
-   * <p>This is useful for when the measurements available during a timestep's Correct() call vary.
-   * The h(x, u) passed to the constructor is used if one is not provided (the two-argument version
-   * of this function).
+   * <p>This is useful for when the measurement noise covariances vary.
    *
-   * @param <Rows> Number of rows in the result of f(x, u).
-   * @param rows Number of rows in the result of f(x, u).
    * @param u Same control input used in the predict step.
    * @param y Measurement vector.
-   * @param h A vector-valued function of x and u that returns the measurement vector.
-   * @param contR Continuous measurement noise covariance matrix.
+   * @param R Continuous measurement noise covariance matrix.
    */
-  public <Rows extends Num> void correct(
-      Nat<Rows> rows,
-      Matrix<Inputs, N1> u,
-      Matrix<Rows, N1> y,
-      BiFunction<Matrix<States, N1>, Matrix<Inputs, N1>, Matrix<Rows, N1>> h,
-      Matrix<Rows, Rows> contR) {
-    correct(rows, u, y, h, contR, Matrix::minus, Matrix::plus);
+  public void correct(Matrix<Inputs, N1> u, Matrix<Outputs, N1> y, Matrix<Outputs, Outputs> R) {
+    correct(m_outputs, u, y, m_h, R, m_residualFuncY, m_addFuncX);
   }
 
   /**
@@ -318,7 +315,30 @@
    * @param u Same control input used in the predict step.
    * @param y Measurement vector.
    * @param h A vector-valued function of x and u that returns the measurement vector.
-   * @param contR Continuous measurement noise covariance matrix.
+   * @param R Continuous measurement noise covariance matrix.
+   */
+  public <Rows extends Num> void correct(
+      Nat<Rows> rows,
+      Matrix<Inputs, N1> u,
+      Matrix<Rows, N1> y,
+      BiFunction<Matrix<States, N1>, Matrix<Inputs, N1>, Matrix<Rows, N1>> h,
+      Matrix<Rows, Rows> R) {
+    correct(rows, u, y, h, R, Matrix::minus, Matrix::plus);
+  }
+
+  /**
+   * Correct the state estimate x-hat using the measurements in y.
+   *
+   * <p>This is useful for when the measurements available during a timestep's Correct() call vary.
+   * The h(x, u) passed to the constructor is used if one is not provided (the two-argument version
+   * of this function).
+   *
+   * @param <Rows> Number of rows in the result of f(x, u).
+   * @param rows Number of rows in the result of f(x, u).
+   * @param u Same control input used in the predict step.
+   * @param y Measurement vector.
+   * @param h A vector-valued function of x and u that returns the measurement vector.
+   * @param R Continuous measurement noise covariance matrix.
    * @param residualFuncY A function that computes the residual of two measurement vectors (i.e. it
    *     subtracts them.)
    * @param addFuncX A function that adds two state vectors.
@@ -328,11 +348,11 @@
       Matrix<Inputs, N1> u,
       Matrix<Rows, N1> y,
       BiFunction<Matrix<States, N1>, Matrix<Inputs, N1>, Matrix<Rows, N1>> h,
-      Matrix<Rows, Rows> contR,
+      Matrix<Rows, Rows> R,
       BiFunction<Matrix<Rows, N1>, Matrix<Rows, N1>, Matrix<Rows, N1>> residualFuncY,
       BiFunction<Matrix<States, N1>, Matrix<States, N1>, Matrix<States, N1>> addFuncX) {
     final var C = NumericalJacobian.numericalJacobianX(rows, m_states, h, m_xHat, u);
-    final var discR = Discretization.discretizeR(contR, m_dtSeconds);
+    final var discR = Discretization.discretizeR(R, m_dtSeconds);
 
     final var S = C.times(m_P).times(C.transpose()).plus(discR);
 
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java
index 24d6d91..08e3270 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java
@@ -4,7 +4,7 @@
 
 package edu.wpi.first.math.estimator;
 
-import edu.wpi.first.math.Drake;
+import edu.wpi.first.math.DARE;
 import edu.wpi.first.math.MathSharedStore;
 import edu.wpi.first.math.Matrix;
 import edu.wpi.first.math.Nat;
@@ -43,6 +43,10 @@
   /**
    * Constructs a state-space observer with the given plant.
    *
+   * <p>See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-observers.html#process-and-measurement-noise-covariance-matrices
+   * for how to select the standard deviations.
+   *
    * @param states A Nat representing the states of the system.
    * @param outputs A Nat representing the outputs of the system.
    * @param plant The plant used for the prediction step.
@@ -65,7 +69,7 @@
     var contQ = StateSpaceUtil.makeCovarianceMatrix(states, stateStdDevs);
     var contR = StateSpaceUtil.makeCovarianceMatrix(outputs, measurementStdDevs);
 
-    var pair = Discretization.discretizeAQTaylor(plant.getA(), contQ, dtSeconds);
+    var pair = Discretization.discretizeAQ(plant.getA(), contQ, dtSeconds);
     var discA = pair.getFirst();
     var discQ = pair.getSecond();
 
@@ -87,9 +91,7 @@
       throw new IllegalArgumentException(msg);
     }
 
-    var P =
-        new Matrix<>(
-            Drake.discreteAlgebraicRiccatiEquation(discA.transpose(), C.transpose(), discQ, discR));
+    var P = new Matrix<>(DARE.dare(discA.transpose(), C.transpose(), discQ, discR));
 
     // S = CPCᵀ + R
     var S = C.times(P).times(C.transpose()).plus(discR);
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java
index 7a100f5..7b14839 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java
@@ -8,7 +8,7 @@
 import edu.wpi.first.math.Num;
 import edu.wpi.first.math.numbers.N1;
 
-interface KalmanTypeFilter<States extends Num, Inputs extends Num, Outputs extends Num> {
+public interface KalmanTypeFilter<States extends Num, Inputs extends Num, Outputs extends Num> {
   Matrix<States, States> getP();
 
   double getP(int i, int j);
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimator.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimator.java
index 448b8d3..59bbb32 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimator.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimator.java
@@ -4,23 +4,15 @@
 
 package edu.wpi.first.math.estimator;
 
-import edu.wpi.first.math.MathUtil;
 import edu.wpi.first.math.Matrix;
-import edu.wpi.first.math.Nat;
 import edu.wpi.first.math.VecBuilder;
 import edu.wpi.first.math.geometry.Pose2d;
 import edu.wpi.first.math.geometry.Rotation2d;
-import edu.wpi.first.math.geometry.Twist2d;
-import edu.wpi.first.math.interpolation.Interpolatable;
-import edu.wpi.first.math.interpolation.TimeInterpolatableBuffer;
 import edu.wpi.first.math.kinematics.MecanumDriveKinematics;
 import edu.wpi.first.math.kinematics.MecanumDriveOdometry;
 import edu.wpi.first.math.kinematics.MecanumDriveWheelPositions;
 import edu.wpi.first.math.numbers.N1;
 import edu.wpi.first.math.numbers.N3;
-import edu.wpi.first.util.WPIUtilJNI;
-import java.util.Map;
-import java.util.Objects;
 
 /**
  * This class wraps {@link MecanumDriveOdometry Mecanum Drive Odometry} to fuse latency-compensated
@@ -33,15 +25,7 @@
  * <p>{@link MecanumDrivePoseEstimator#addVisionMeasurement} can be called as infrequently as you
  * want; if you never call it, then this class will behave mostly like regular encoder odometry.
  */
-public class MecanumDrivePoseEstimator {
-  private final MecanumDriveKinematics m_kinematics;
-  private final MecanumDriveOdometry m_odometry;
-  private final Matrix<N3, N1> m_q = new Matrix<>(Nat.N3(), Nat.N1());
-  private Matrix<N3, N3> m_visionK = new Matrix<>(Nat.N3(), Nat.N3());
-
-  private final TimeInterpolatableBuffer<InterpolationRecord> m_poseBuffer =
-      TimeInterpolatableBuffer.createBuffer(1.5);
-
+public class MecanumDrivePoseEstimator extends PoseEstimator<MecanumDriveWheelPositions> {
   /**
    * Constructs a MecanumDrivePoseEstimator with default standard deviations for the model and
    * vision measurements.
@@ -90,294 +74,10 @@
       Pose2d initialPoseMeters,
       Matrix<N3, N1> stateStdDevs,
       Matrix<N3, N1> visionMeasurementStdDevs) {
-    m_kinematics = kinematics;
-    m_odometry = new MecanumDriveOdometry(kinematics, gyroAngle, wheelPositions, initialPoseMeters);
-
-    for (int i = 0; i < 3; ++i) {
-      m_q.set(i, 0, stateStdDevs.get(i, 0) * stateStdDevs.get(i, 0));
-    }
-
-    // Initialize vision R
-    setVisionMeasurementStdDevs(visionMeasurementStdDevs);
-  }
-
-  /**
-   * Sets the pose estimator's trust of global measurements. This might be used to change trust in
-   * vision measurements after the autonomous period, or to change trust as distance to a vision
-   * target increases.
-   *
-   * @param visionMeasurementStdDevs Standard deviations of the vision measurements. Increase these
-   *     numbers to trust global measurements from vision less. This matrix is in the form [x, y,
-   *     theta]ᵀ, with units in meters and radians.
-   */
-  public void setVisionMeasurementStdDevs(Matrix<N3, N1> visionMeasurementStdDevs) {
-    var r = new double[3];
-    for (int i = 0; i < 3; ++i) {
-      r[i] = visionMeasurementStdDevs.get(i, 0) * visionMeasurementStdDevs.get(i, 0);
-    }
-
-    // Solve for closed form Kalman gain for continuous Kalman filter with A = 0
-    // and C = I. See wpimath/algorithms.md.
-    for (int row = 0; row < 3; ++row) {
-      if (m_q.get(row, 0) == 0.0) {
-        m_visionK.set(row, row, 0.0);
-      } else {
-        m_visionK.set(
-            row, row, m_q.get(row, 0) / (m_q.get(row, 0) + Math.sqrt(m_q.get(row, 0) * r[row])));
-      }
-    }
-  }
-
-  /**
-   * Resets the robot's position on the field.
-   *
-   * <p>The gyroscope angle does not need to be reset in the user's robot code. The library
-   * automatically takes care of offsetting the gyro angle.
-   *
-   * @param gyroAngle The angle reported by the gyroscope.
-   * @param wheelPositions The distances driven by each wheel.
-   * @param poseMeters The position on the field that your robot is at.
-   */
-  public void resetPosition(
-      Rotation2d gyroAngle, MecanumDriveWheelPositions wheelPositions, Pose2d poseMeters) {
-    // Reset state estimate and error covariance
-    m_odometry.resetPosition(gyroAngle, wheelPositions, poseMeters);
-    m_poseBuffer.clear();
-  }
-
-  /**
-   * Gets the estimated robot pose.
-   *
-   * @return The estimated robot pose in meters.
-   */
-  public Pose2d getEstimatedPosition() {
-    return m_odometry.getPoseMeters();
-  }
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate
-   * while still accounting for measurement noise.
-   *
-   * <p>This method can be called as infrequently as you want, as long as you are calling {@link
-   * MecanumDrivePoseEstimator#update} every loop.
-   *
-   * <p>To promote stability of the pose estimate and make it robust to bad vision data, we
-   * recommend only adding vision measurements that are already within one meter or so of the
-   * current pose estimate.
-   *
-   * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera.
-   * @param timestampSeconds The timestamp of the vision measurement in seconds. Note that if you
-   *     don't use your own time source by calling {@link
-   *     MecanumDrivePoseEstimator#updateWithTime(double,Rotation2d,MecanumDriveWheelPositions)}
-   *     then you must use a timestamp with an epoch since FPGA startup (i.e., the epoch of this
-   *     timestamp is the same epoch as {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()}.)
-   *     This means that you should use {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()} as
-   *     your time source or sync the epochs.
-   */
-  public void addVisionMeasurement(Pose2d visionRobotPoseMeters, double timestampSeconds) {
-    // Step 1: Get the pose odometry measured at the moment the vision measurement was made.
-    var sample = m_poseBuffer.getSample(timestampSeconds);
-
-    if (sample.isEmpty()) {
-      return;
-    }
-
-    // Step 2: Measure the twist between the odometry pose and the vision pose.
-    var twist = sample.get().poseMeters.log(visionRobotPoseMeters);
-
-    // Step 3: We should not trust the twist entirely, so instead we scale this twist by a Kalman
-    // gain matrix representing how much we trust vision measurements compared to our current pose.
-    var k_times_twist = m_visionK.times(VecBuilder.fill(twist.dx, twist.dy, twist.dtheta));
-
-    // Step 4: Convert back to Twist2d.
-    var scaledTwist =
-        new Twist2d(k_times_twist.get(0, 0), k_times_twist.get(1, 0), k_times_twist.get(2, 0));
-
-    // Step 5: Reset Odometry to state at sample with vision adjustment.
-    m_odometry.resetPosition(
-        sample.get().gyroAngle,
-        sample.get().wheelPositions,
-        sample.get().poseMeters.exp(scaledTwist));
-
-    // Step 6: Replay odometry inputs between sample time and latest recorded sample to update the
-    // pose buffer and correct odometry.
-    for (Map.Entry<Double, InterpolationRecord> entry :
-        m_poseBuffer.getInternalBuffer().tailMap(timestampSeconds).entrySet()) {
-      updateWithTime(entry.getKey(), entry.getValue().gyroAngle, entry.getValue().wheelPositions);
-    }
-  }
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate
-   * while still accounting for measurement noise.
-   *
-   * <p>This method can be called as infrequently as you want, as long as you are calling {@link
-   * MecanumDrivePoseEstimator#update} every loop.
-   *
-   * <p>To promote stability of the pose estimate and make it robust to bad vision data, we
-   * recommend only adding vision measurements that are already within one meter or so of the
-   * current pose estimate.
-   *
-   * <p>Note that the vision measurement standard deviations passed into this method will continue
-   * to apply to future measurements until a subsequent call to {@link
-   * MecanumDrivePoseEstimator#setVisionMeasurementStdDevs(Matrix)} or this method.
-   *
-   * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera.
-   * @param timestampSeconds The timestamp of the vision measurement in seconds. Note that if you
-   *     don't use your own time source by calling {@link
-   *     MecanumDrivePoseEstimator#updateWithTime(double,Rotation2d,MecanumDriveWheelPositions)},
-   *     then you must use a timestamp with an epoch since FPGA startup (i.e., the epoch of this
-   *     timestamp is the same epoch as {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()}).
-   *     This means that you should use {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()} as
-   *     your time source in this case.
-   * @param visionMeasurementStdDevs Standard deviations of the vision pose measurement (x position
-   *     in meters, y position in meters, and heading in radians). Increase these numbers to trust
-   *     the vision pose measurement less.
-   */
-  public void addVisionMeasurement(
-      Pose2d visionRobotPoseMeters,
-      double timestampSeconds,
-      Matrix<N3, N1> visionMeasurementStdDevs) {
-    setVisionMeasurementStdDevs(visionMeasurementStdDevs);
-    addVisionMeasurement(visionRobotPoseMeters, timestampSeconds);
-  }
-
-  /**
-   * Updates the pose estimator with wheel encoder and gyro information. This should be called every
-   * loop.
-   *
-   * @param gyroAngle The current gyro angle.
-   * @param wheelPositions The distances driven by each wheel.
-   * @return The estimated pose of the robot in meters.
-   */
-  public Pose2d update(Rotation2d gyroAngle, MecanumDriveWheelPositions wheelPositions) {
-    return updateWithTime(WPIUtilJNI.now() * 1.0e-6, gyroAngle, wheelPositions);
-  }
-
-  /**
-   * Updates the pose estimator with wheel encoder and gyro information. This should be called every
-   * loop.
-   *
-   * @param currentTimeSeconds Time at which this method was called, in seconds.
-   * @param gyroAngle The current gyroscope angle.
-   * @param wheelPositions The distances driven by each wheel.
-   * @return The estimated pose of the robot in meters.
-   */
-  public Pose2d updateWithTime(
-      double currentTimeSeconds, Rotation2d gyroAngle, MecanumDriveWheelPositions wheelPositions) {
-    m_odometry.update(gyroAngle, wheelPositions);
-
-    m_poseBuffer.addSample(
-        currentTimeSeconds,
-        new InterpolationRecord(
-            getEstimatedPosition(),
-            gyroAngle,
-            new MecanumDriveWheelPositions(
-                wheelPositions.frontLeftMeters,
-                wheelPositions.frontRightMeters,
-                wheelPositions.rearLeftMeters,
-                wheelPositions.rearRightMeters)));
-
-    return getEstimatedPosition();
-  }
-
-  /**
-   * Represents an odometry record. The record contains the inputs provided as well as the pose that
-   * was observed based on these inputs, as well as the previous record and its inputs.
-   */
-  private class InterpolationRecord implements Interpolatable<InterpolationRecord> {
-    // The pose observed given the current sensor inputs and the previous pose.
-    private final Pose2d poseMeters;
-
-    // The current gyro angle.
-    private final Rotation2d gyroAngle;
-
-    // The distances traveled by each wheel encoder.
-    private final MecanumDriveWheelPositions wheelPositions;
-
-    /**
-     * Constructs an Interpolation Record with the specified parameters.
-     *
-     * @param pose The pose observed given the current sensor inputs and the previous pose.
-     * @param gyro The current gyro angle.
-     * @param wheelPositions The distances traveled by each wheel encoder.
-     */
-    private InterpolationRecord(
-        Pose2d poseMeters, Rotation2d gyro, MecanumDriveWheelPositions wheelPositions) {
-      this.poseMeters = poseMeters;
-      this.gyroAngle = gyro;
-      this.wheelPositions = wheelPositions;
-    }
-
-    /**
-     * Return the interpolated record. This object is assumed to be the starting position, or lower
-     * bound.
-     *
-     * @param endValue The upper bound, or end.
-     * @param t How far between the lower and upper bound we are. This should be bounded in [0, 1].
-     * @return The interpolated value.
-     */
-    @Override
-    public InterpolationRecord interpolate(InterpolationRecord endValue, double t) {
-      if (t < 0) {
-        return this;
-      } else if (t >= 1) {
-        return endValue;
-      } else {
-        // Find the new wheel distances.
-        var wheels_lerp =
-            new MecanumDriveWheelPositions(
-                MathUtil.interpolate(
-                    this.wheelPositions.frontLeftMeters,
-                    endValue.wheelPositions.frontLeftMeters,
-                    t),
-                MathUtil.interpolate(
-                    this.wheelPositions.frontRightMeters,
-                    endValue.wheelPositions.frontRightMeters,
-                    t),
-                MathUtil.interpolate(
-                    this.wheelPositions.rearLeftMeters, endValue.wheelPositions.rearLeftMeters, t),
-                MathUtil.interpolate(
-                    this.wheelPositions.rearRightMeters,
-                    endValue.wheelPositions.rearRightMeters,
-                    t));
-
-        // Find the distance travelled between this measurement and the interpolated measurement.
-        var wheels_delta =
-            new MecanumDriveWheelPositions(
-                wheels_lerp.frontLeftMeters - this.wheelPositions.frontLeftMeters,
-                wheels_lerp.frontRightMeters - this.wheelPositions.frontRightMeters,
-                wheels_lerp.rearLeftMeters - this.wheelPositions.rearLeftMeters,
-                wheels_lerp.rearRightMeters - this.wheelPositions.rearRightMeters);
-
-        // Find the new gyro angle.
-        var gyro_lerp = gyroAngle.interpolate(endValue.gyroAngle, t);
-
-        // Create a twist to represent this change based on the interpolated sensor inputs.
-        Twist2d twist = m_kinematics.toTwist2d(wheels_delta);
-        twist.dtheta = gyro_lerp.minus(gyroAngle).getRadians();
-
-        return new InterpolationRecord(poseMeters.exp(twist), gyro_lerp, wheels_lerp);
-      }
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (this == obj) {
-        return true;
-      }
-      if (!(obj instanceof InterpolationRecord)) {
-        return false;
-      }
-      InterpolationRecord record = (InterpolationRecord) obj;
-      return Objects.equals(gyroAngle, record.gyroAngle)
-          && Objects.equals(wheelPositions, record.wheelPositions)
-          && Objects.equals(poseMeters, record.poseMeters);
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(gyroAngle, wheelPositions, poseMeters);
-    }
+    super(
+        kinematics,
+        new MecanumDriveOdometry(kinematics, gyroAngle, wheelPositions, initialPoseMeters),
+        stateStdDevs,
+        visionMeasurementStdDevs);
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/PoseEstimator.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/PoseEstimator.java
new file mode 100644
index 0000000..bb30f25
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/PoseEstimator.java
@@ -0,0 +1,333 @@
+// 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.
+
+package edu.wpi.first.math.estimator;
+
+import edu.wpi.first.math.MathSharedStore;
+import edu.wpi.first.math.Matrix;
+import edu.wpi.first.math.Nat;
+import edu.wpi.first.math.VecBuilder;
+import edu.wpi.first.math.geometry.Pose2d;
+import edu.wpi.first.math.geometry.Rotation2d;
+import edu.wpi.first.math.geometry.Twist2d;
+import edu.wpi.first.math.interpolation.Interpolatable;
+import edu.wpi.first.math.interpolation.TimeInterpolatableBuffer;
+import edu.wpi.first.math.kinematics.Kinematics;
+import edu.wpi.first.math.kinematics.Odometry;
+import edu.wpi.first.math.kinematics.WheelPositions;
+import edu.wpi.first.math.numbers.N1;
+import edu.wpi.first.math.numbers.N3;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+/**
+ * This class wraps {@link Odometry} to fuse latency-compensated vision measurements with encoder
+ * measurements. Robot code should not use this directly- Instead, use the particular type for your
+ * drivetrain (e.g., {@link DifferentialDrivePoseEstimator}). It is intended to be a drop-in
+ * replacement for {@link Odometry}; in fact, if you never call {@link
+ * PoseEstimator#addVisionMeasurement} and only call {@link PoseEstimator#update} then this will
+ * behave exactly the same as Odometry.
+ *
+ * <p>{@link PoseEstimator#update} should be called every robot loop.
+ *
+ * <p>{@link PoseEstimator#addVisionMeasurement} can be called as infrequently as you want; if you
+ * never call it then this class will behave exactly like regular encoder odometry.
+ */
+public class PoseEstimator<T extends WheelPositions<T>> {
+  private final Kinematics<?, T> m_kinematics;
+  private final Odometry<T> m_odometry;
+  private final Matrix<N3, N1> m_q = new Matrix<>(Nat.N3(), Nat.N1());
+  private final Matrix<N3, N3> m_visionK = new Matrix<>(Nat.N3(), Nat.N3());
+
+  private static final double kBufferDuration = 1.5;
+  private final TimeInterpolatableBuffer<InterpolationRecord> m_poseBuffer =
+      TimeInterpolatableBuffer.createBuffer(kBufferDuration);
+
+  /**
+   * Constructs a PoseEstimator.
+   *
+   * @param kinematics A correctly-configured kinematics object for your drivetrain.
+   * @param odometry A correctly-configured odometry object for your drivetrain.
+   * @param stateStdDevs Standard deviations of the pose estimate (x position in meters, y position
+   *     in meters, and heading in radians). Increase these numbers to trust your state estimate
+   *     less.
+   * @param visionMeasurementStdDevs Standard deviations of the vision pose measurement (x position
+   *     in meters, y position in meters, and heading in radians). Increase these numbers to trust
+   *     the vision pose measurement less.
+   */
+  public PoseEstimator(
+      Kinematics<?, T> kinematics,
+      Odometry<T> odometry,
+      Matrix<N3, N1> stateStdDevs,
+      Matrix<N3, N1> visionMeasurementStdDevs) {
+    m_kinematics = kinematics;
+    m_odometry = odometry;
+
+    for (int i = 0; i < 3; ++i) {
+      m_q.set(i, 0, stateStdDevs.get(i, 0) * stateStdDevs.get(i, 0));
+    }
+    setVisionMeasurementStdDevs(visionMeasurementStdDevs);
+  }
+
+  /**
+   * Sets the pose estimator's trust of global measurements. This might be used to change trust in
+   * vision measurements after the autonomous period, or to change trust as distance to a vision
+   * target increases.
+   *
+   * @param visionMeasurementStdDevs Standard deviations of the vision measurements. Increase these
+   *     numbers to trust global measurements from vision less. This matrix is in the form [x, y,
+   *     theta]ᵀ, with units in meters and radians.
+   */
+  public void setVisionMeasurementStdDevs(Matrix<N3, N1> visionMeasurementStdDevs) {
+    var r = new double[3];
+    for (int i = 0; i < 3; ++i) {
+      r[i] = visionMeasurementStdDevs.get(i, 0) * visionMeasurementStdDevs.get(i, 0);
+    }
+
+    // Solve for closed form Kalman gain for continuous Kalman filter with A = 0
+    // and C = I. See wpimath/algorithms.md.
+    for (int row = 0; row < 3; ++row) {
+      if (m_q.get(row, 0) == 0.0) {
+        m_visionK.set(row, row, 0.0);
+      } else {
+        m_visionK.set(
+            row, row, m_q.get(row, 0) / (m_q.get(row, 0) + Math.sqrt(m_q.get(row, 0) * r[row])));
+      }
+    }
+  }
+
+  /**
+   * Resets the robot's position on the field.
+   *
+   * <p>The gyroscope angle does not need to be reset here on the user's robot code. The library
+   * automatically takes care of offsetting the gyro angle.
+   *
+   * @param gyroAngle The angle reported by the gyroscope.
+   * @param wheelPositions The current encoder readings.
+   * @param poseMeters The position on the field that your robot is at.
+   */
+  public void resetPosition(Rotation2d gyroAngle, T wheelPositions, Pose2d poseMeters) {
+    // Reset state estimate and error covariance
+    m_odometry.resetPosition(gyroAngle, wheelPositions, poseMeters);
+    m_poseBuffer.clear();
+  }
+
+  /**
+   * Gets the estimated robot pose.
+   *
+   * @return The estimated robot pose in meters.
+   */
+  public Pose2d getEstimatedPosition() {
+    return m_odometry.getPoseMeters();
+  }
+
+  /**
+   * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate
+   * while still accounting for measurement noise.
+   *
+   * <p>This method can be called as infrequently as you want, as long as you are calling {@link
+   * PoseEstimator#update} every loop.
+   *
+   * <p>To promote stability of the pose estimate and make it robust to bad vision data, we
+   * recommend only adding vision measurements that are already within one meter or so of the
+   * current pose estimate.
+   *
+   * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera.
+   * @param timestampSeconds The timestamp of the vision measurement in seconds. Note that if you
+   *     don't use your own time source by calling {@link
+   *     PoseEstimator#updateWithTime(double,Rotation2d,WheelPositions)} then you must use a
+   *     timestamp with an epoch since FPGA startup (i.e., the epoch of this timestamp is the same
+   *     epoch as {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()}.) This means that you
+   *     should use {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()} as your time source or
+   *     sync the epochs.
+   */
+  public void addVisionMeasurement(Pose2d visionRobotPoseMeters, double timestampSeconds) {
+    // Step 0: If this measurement is old enough to be outside the pose buffer's timespan, skip.
+    try {
+      if (m_poseBuffer.getInternalBuffer().lastKey() - kBufferDuration > timestampSeconds) {
+        return;
+      }
+    } catch (NoSuchElementException ex) {
+      return;
+    }
+
+    // Step 1: Get the pose odometry measured at the moment the vision measurement was made.
+    var sample = m_poseBuffer.getSample(timestampSeconds);
+
+    if (sample.isEmpty()) {
+      return;
+    }
+
+    // Step 2: Measure the twist between the odometry pose and the vision pose.
+    var twist = sample.get().poseMeters.log(visionRobotPoseMeters);
+
+    // Step 3: We should not trust the twist entirely, so instead we scale this twist by a Kalman
+    // gain matrix representing how much we trust vision measurements compared to our current pose.
+    var k_times_twist = m_visionK.times(VecBuilder.fill(twist.dx, twist.dy, twist.dtheta));
+
+    // Step 4: Convert back to Twist2d.
+    var scaledTwist =
+        new Twist2d(k_times_twist.get(0, 0), k_times_twist.get(1, 0), k_times_twist.get(2, 0));
+
+    // Step 5: Reset Odometry to state at sample with vision adjustment.
+    m_odometry.resetPosition(
+        sample.get().gyroAngle,
+        sample.get().wheelPositions,
+        sample.get().poseMeters.exp(scaledTwist));
+
+    // Step 6: Record the current pose to allow multiple measurements from the same timestamp
+    m_poseBuffer.addSample(
+        timestampSeconds,
+        new InterpolationRecord(
+            getEstimatedPosition(), sample.get().gyroAngle, sample.get().wheelPositions));
+
+    // Step 7: Replay odometry inputs between sample time and latest recorded sample to update the
+    // pose buffer and correct odometry.
+    for (Map.Entry<Double, InterpolationRecord> entry :
+        m_poseBuffer.getInternalBuffer().tailMap(timestampSeconds).entrySet()) {
+      updateWithTime(entry.getKey(), entry.getValue().gyroAngle, entry.getValue().wheelPositions);
+    }
+  }
+
+  /**
+   * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate
+   * while still accounting for measurement noise.
+   *
+   * <p>This method can be called as infrequently as you want, as long as you are calling {@link
+   * PoseEstimator#update} every loop.
+   *
+   * <p>To promote stability of the pose estimate and make it robust to bad vision data, we
+   * recommend only adding vision measurements that are already within one meter or so of the
+   * current pose estimate.
+   *
+   * <p>Note that the vision measurement standard deviations passed into this method will continue
+   * to apply to future measurements until a subsequent call to {@link
+   * PoseEstimator#setVisionMeasurementStdDevs(Matrix)} or this method.
+   *
+   * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera.
+   * @param timestampSeconds The timestamp of the vision measurement in seconds. Note that if you
+   *     don't use your own time source by calling {@link #updateWithTime}, then you must use a
+   *     timestamp with an epoch since FPGA startup (i.e., the epoch of this timestamp is the same
+   *     epoch as {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()}). This means that you
+   *     should use {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()} as your time source in
+   *     this case.
+   * @param visionMeasurementStdDevs Standard deviations of the vision pose measurement (x position
+   *     in meters, y position in meters, and heading in radians). Increase these numbers to trust
+   *     the vision pose measurement less.
+   */
+  public void addVisionMeasurement(
+      Pose2d visionRobotPoseMeters,
+      double timestampSeconds,
+      Matrix<N3, N1> visionMeasurementStdDevs) {
+    setVisionMeasurementStdDevs(visionMeasurementStdDevs);
+    addVisionMeasurement(visionRobotPoseMeters, timestampSeconds);
+  }
+
+  /**
+   * Updates the pose estimator with wheel encoder and gyro information. This should be called every
+   * loop.
+   *
+   * @param gyroAngle The current gyro angle.
+   * @param wheelPositions The current encoder readings.
+   * @return The estimated pose of the robot in meters.
+   */
+  public Pose2d update(Rotation2d gyroAngle, T wheelPositions) {
+    return updateWithTime(MathSharedStore.getTimestamp(), gyroAngle, wheelPositions);
+  }
+
+  /**
+   * Updates the pose estimator with wheel encoder and gyro information. This should be called every
+   * loop.
+   *
+   * @param currentTimeSeconds Time at which this method was called, in seconds.
+   * @param gyroAngle The current gyro angle.
+   * @param wheelPositions The current encoder readings.
+   * @return The estimated pose of the robot in meters.
+   */
+  public Pose2d updateWithTime(double currentTimeSeconds, Rotation2d gyroAngle, T wheelPositions) {
+    m_odometry.update(gyroAngle, wheelPositions);
+    m_poseBuffer.addSample(
+        currentTimeSeconds,
+        new InterpolationRecord(getEstimatedPosition(), gyroAngle, wheelPositions.copy()));
+
+    return getEstimatedPosition();
+  }
+
+  /**
+   * Represents an odometry record. The record contains the inputs provided as well as the pose that
+   * was observed based on these inputs, as well as the previous record and its inputs.
+   */
+  private class InterpolationRecord implements Interpolatable<InterpolationRecord> {
+    // The pose observed given the current sensor inputs and the previous pose.
+    private final Pose2d poseMeters;
+
+    // The current gyro angle.
+    private final Rotation2d gyroAngle;
+
+    // The current encoder readings.
+    private final T wheelPositions;
+
+    /**
+     * Constructs an Interpolation Record with the specified parameters.
+     *
+     * @param poseMeters The pose observed given the current sensor inputs and the previous pose.
+     * @param gyro The current gyro angle.
+     * @param wheelPositions The current encoder readings.
+     */
+    private InterpolationRecord(Pose2d poseMeters, Rotation2d gyro, T wheelPositions) {
+      this.poseMeters = poseMeters;
+      this.gyroAngle = gyro;
+      this.wheelPositions = wheelPositions;
+    }
+
+    /**
+     * Return the interpolated record. This object is assumed to be the starting position, or lower
+     * bound.
+     *
+     * @param endValue The upper bound, or end.
+     * @param t How far between the lower and upper bound we are. This should be bounded in [0, 1].
+     * @return The interpolated value.
+     */
+    @Override
+    public InterpolationRecord interpolate(InterpolationRecord endValue, double t) {
+      if (t < 0) {
+        return this;
+      } else if (t >= 1) {
+        return endValue;
+      } else {
+        // Find the new wheel distances.
+        var wheelLerp = wheelPositions.interpolate(endValue.wheelPositions, t);
+
+        // Find the new gyro angle.
+        var gyroLerp = gyroAngle.interpolate(endValue.gyroAngle, t);
+
+        // Create a twist to represent the change based on the interpolated sensor inputs.
+        Twist2d twist = m_kinematics.toTwist2d(wheelPositions, wheelLerp);
+        twist.dtheta = gyroLerp.minus(gyroAngle).getRadians();
+
+        return new InterpolationRecord(poseMeters.exp(twist), gyroLerp, wheelLerp);
+      }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (!(obj instanceof PoseEstimator.InterpolationRecord)) {
+        return false;
+      }
+      var record = (PoseEstimator<?>.InterpolationRecord) obj;
+      return Objects.equals(gyroAngle, record.gyroAngle)
+          && Objects.equals(wheelPositions, record.wheelPositions)
+          && Objects.equals(poseMeters, record.poseMeters);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(gyroAngle, wheelPositions, poseMeters);
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimator.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimator.java
index 57451a0..f778b62 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimator.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimator.java
@@ -4,24 +4,16 @@
 
 package edu.wpi.first.math.estimator;
 
-import edu.wpi.first.math.MathUtil;
 import edu.wpi.first.math.Matrix;
-import edu.wpi.first.math.Nat;
 import edu.wpi.first.math.VecBuilder;
 import edu.wpi.first.math.geometry.Pose2d;
 import edu.wpi.first.math.geometry.Rotation2d;
-import edu.wpi.first.math.geometry.Twist2d;
-import edu.wpi.first.math.interpolation.Interpolatable;
-import edu.wpi.first.math.interpolation.TimeInterpolatableBuffer;
 import edu.wpi.first.math.kinematics.SwerveDriveKinematics;
 import edu.wpi.first.math.kinematics.SwerveDriveOdometry;
+import edu.wpi.first.math.kinematics.SwerveDriveWheelPositions;
 import edu.wpi.first.math.kinematics.SwerveModulePosition;
 import edu.wpi.first.math.numbers.N1;
 import edu.wpi.first.math.numbers.N3;
-import edu.wpi.first.util.WPIUtilJNI;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Objects;
 
 /**
  * This class wraps {@link SwerveDriveOdometry Swerve Drive Odometry} to fuse latency-compensated
@@ -33,15 +25,8 @@
  * <p>{@link SwerveDrivePoseEstimator#addVisionMeasurement} can be called as infrequently as you
  * want; if you never call it, then this class will behave as regular encoder odometry.
  */
-public class SwerveDrivePoseEstimator {
-  private final SwerveDriveKinematics m_kinematics;
-  private final SwerveDriveOdometry m_odometry;
-  private final Matrix<N3, N1> m_q = new Matrix<>(Nat.N3(), Nat.N1());
+public class SwerveDrivePoseEstimator extends PoseEstimator<SwerveDriveWheelPositions> {
   private final int m_numModules;
-  private Matrix<N3, N3> m_visionK = new Matrix<>(Nat.N3(), Nat.N3());
-
-  private final TimeInterpolatableBuffer<InterpolationRecord> m_poseBuffer =
-      TimeInterpolatableBuffer.createBuffer(1.5);
 
   /**
    * Constructs a SwerveDrivePoseEstimator with default standard deviations for the model and vision
@@ -91,43 +76,13 @@
       Pose2d initialPoseMeters,
       Matrix<N3, N1> stateStdDevs,
       Matrix<N3, N1> visionMeasurementStdDevs) {
-    m_kinematics = kinematics;
-    m_odometry = new SwerveDriveOdometry(kinematics, gyroAngle, modulePositions, initialPoseMeters);
-
-    for (int i = 0; i < 3; ++i) {
-      m_q.set(i, 0, stateStdDevs.get(i, 0) * stateStdDevs.get(i, 0));
-    }
+    super(
+        kinematics,
+        new SwerveDriveOdometry(kinematics, gyroAngle, modulePositions, initialPoseMeters),
+        stateStdDevs,
+        visionMeasurementStdDevs);
 
     m_numModules = modulePositions.length;
-
-    setVisionMeasurementStdDevs(visionMeasurementStdDevs);
-  }
-
-  /**
-   * Sets the pose estimator's trust of global measurements. This might be used to change trust in
-   * vision measurements after the autonomous period, or to change trust as distance to a vision
-   * target increases.
-   *
-   * @param visionMeasurementStdDevs Standard deviations of the vision measurements. Increase these
-   *     numbers to trust global measurements from vision less. This matrix is in the form [x, y,
-   *     theta]ᵀ, with units in meters and radians.
-   */
-  public void setVisionMeasurementStdDevs(Matrix<N3, N1> visionMeasurementStdDevs) {
-    var r = new double[3];
-    for (int i = 0; i < 3; ++i) {
-      r[i] = visionMeasurementStdDevs.get(i, 0) * visionMeasurementStdDevs.get(i, 0);
-    }
-
-    // Solve for closed form Kalman gain for continuous Kalman filter with A = 0
-    // and C = I. See wpimath/algorithms.md.
-    for (int row = 0; row < 3; ++row) {
-      if (m_q.get(row, 0) == 0.0) {
-        m_visionK.set(row, row, 0.0);
-      } else {
-        m_visionK.set(
-            row, row, m_q.get(row, 0) / (m_q.get(row, 0) + Math.sqrt(m_q.get(row, 0) * r[row])));
-      }
-    }
   }
 
   /**
@@ -142,106 +97,7 @@
    */
   public void resetPosition(
       Rotation2d gyroAngle, SwerveModulePosition[] modulePositions, Pose2d poseMeters) {
-    // Reset state estimate and error covariance
-    m_odometry.resetPosition(gyroAngle, modulePositions, poseMeters);
-    m_poseBuffer.clear();
-  }
-
-  /**
-   * Gets the estimated robot pose.
-   *
-   * @return The estimated robot pose in meters.
-   */
-  public Pose2d getEstimatedPosition() {
-    return m_odometry.getPoseMeters();
-  }
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate
-   * while still accounting for measurement noise.
-   *
-   * <p>This method can be called as infrequently as you want, as long as you are calling {@link
-   * SwerveDrivePoseEstimator#update} every loop.
-   *
-   * <p>To promote stability of the pose estimate and make it robust to bad vision data, we
-   * recommend only adding vision measurements that are already within one meter or so of the
-   * current pose estimate.
-   *
-   * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera.
-   * @param timestampSeconds The timestamp of the vision measurement in seconds. Note that if you
-   *     don't use your own time source by calling {@link
-   *     SwerveDrivePoseEstimator#updateWithTime(double,Rotation2d,SwerveModulePosition[])} then you
-   *     must use a timestamp with an epoch since FPGA startup (i.e., the epoch of this timestamp is
-   *     the same epoch as {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()}.) This means that
-   *     you should use {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()} as your time source
-   *     or sync the epochs.
-   */
-  public void addVisionMeasurement(Pose2d visionRobotPoseMeters, double timestampSeconds) {
-    // Step 1: Get the pose odometry measured at the moment the vision measurement was made.
-    var sample = m_poseBuffer.getSample(timestampSeconds);
-
-    if (sample.isEmpty()) {
-      return;
-    }
-
-    // Step 2: Measure the twist between the odometry pose and the vision pose.
-    var twist = sample.get().poseMeters.log(visionRobotPoseMeters);
-
-    // Step 3: We should not trust the twist entirely, so instead we scale this twist by a Kalman
-    // gain matrix representing how much we trust vision measurements compared to our current pose.
-    var k_times_twist = m_visionK.times(VecBuilder.fill(twist.dx, twist.dy, twist.dtheta));
-
-    // Step 4: Convert back to Twist2d.
-    var scaledTwist =
-        new Twist2d(k_times_twist.get(0, 0), k_times_twist.get(1, 0), k_times_twist.get(2, 0));
-
-    // Step 5: Reset Odometry to state at sample with vision adjustment.
-    m_odometry.resetPosition(
-        sample.get().gyroAngle,
-        sample.get().modulePositions,
-        sample.get().poseMeters.exp(scaledTwist));
-
-    // Step 6: Replay odometry inputs between sample time and latest recorded sample to update the
-    // pose buffer and correct odometry.
-    for (Map.Entry<Double, InterpolationRecord> entry :
-        m_poseBuffer.getInternalBuffer().tailMap(timestampSeconds).entrySet()) {
-      updateWithTime(entry.getKey(), entry.getValue().gyroAngle, entry.getValue().modulePositions);
-    }
-  }
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate
-   * while still accounting for measurement noise.
-   *
-   * <p>This method can be called as infrequently as you want, as long as you are calling {@link
-   * SwerveDrivePoseEstimator#update} every loop.
-   *
-   * <p>To promote stability of the pose estimate and make it robust to bad vision data, we
-   * recommend only adding vision measurements that are already within one meter or so of the
-   * current pose estimate.
-   *
-   * <p>Note that the vision measurement standard deviations passed into this method will continue
-   * to apply to future measurements until a subsequent call to {@link
-   * SwerveDrivePoseEstimator#setVisionMeasurementStdDevs(Matrix)} or this method.
-   *
-   * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera.
-   * @param timestampSeconds The timestamp of the vision measurement in seconds. Note that if you
-   *     don't use your own time source by calling {@link
-   *     SwerveDrivePoseEstimator#updateWithTime(double,Rotation2d,SwerveModulePosition[])}, then
-   *     you must use a timestamp with an epoch since FPGA startup (i.e., the epoch of this
-   *     timestamp is the same epoch as {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()}).
-   *     This means that you should use {@link edu.wpi.first.wpilibj.Timer#getFPGATimestamp()} as
-   *     your time source in this case.
-   * @param visionMeasurementStdDevs Standard deviations of the vision pose measurement (x position
-   *     in meters, y position in meters, and heading in radians). Increase these numbers to trust
-   *     the vision pose measurement less.
-   */
-  public void addVisionMeasurement(
-      Pose2d visionRobotPoseMeters,
-      double timestampSeconds,
-      Matrix<N3, N1> visionMeasurementStdDevs) {
-    setVisionMeasurementStdDevs(visionMeasurementStdDevs);
-    addVisionMeasurement(visionRobotPoseMeters, timestampSeconds);
+    resetPosition(gyroAngle, new SwerveDriveWheelPositions(modulePositions), poseMeters);
   }
 
   /**
@@ -253,7 +109,7 @@
    * @return The estimated pose of the robot in meters.
    */
   public Pose2d update(Rotation2d gyroAngle, SwerveModulePosition[] modulePositions) {
-    return updateWithTime(WPIUtilJNI.now() * 1.0e-6, gyroAngle, modulePositions);
+    return update(gyroAngle, new SwerveDriveWheelPositions(modulePositions));
   }
 
   /**
@@ -267,118 +123,19 @@
    */
   public Pose2d updateWithTime(
       double currentTimeSeconds, Rotation2d gyroAngle, SwerveModulePosition[] modulePositions) {
-    if (modulePositions.length != m_numModules) {
+    return updateWithTime(
+        currentTimeSeconds, gyroAngle, new SwerveDriveWheelPositions(modulePositions));
+  }
+
+  @Override
+  public Pose2d updateWithTime(
+      double currentTimeSeconds, Rotation2d gyroAngle, SwerveDriveWheelPositions wheelPositions) {
+    if (wheelPositions.positions.length != m_numModules) {
       throw new IllegalArgumentException(
           "Number of modules is not consistent with number of wheel locations provided in "
               + "constructor");
     }
 
-    var internalModulePositions = new SwerveModulePosition[m_numModules];
-
-    for (int i = 0; i < m_numModules; i++) {
-      internalModulePositions[i] =
-          new SwerveModulePosition(modulePositions[i].distanceMeters, modulePositions[i].angle);
-    }
-
-    m_odometry.update(gyroAngle, internalModulePositions);
-
-    m_poseBuffer.addSample(
-        currentTimeSeconds,
-        new InterpolationRecord(getEstimatedPosition(), gyroAngle, internalModulePositions));
-
-    return getEstimatedPosition();
-  }
-
-  /**
-   * Represents an odometry record. The record contains the inputs provided as well as the pose that
-   * was observed based on these inputs, as well as the previous record and its inputs.
-   */
-  private class InterpolationRecord implements Interpolatable<InterpolationRecord> {
-    // The pose observed given the current sensor inputs and the previous pose.
-    private final Pose2d poseMeters;
-
-    // The current gyro angle.
-    private final Rotation2d gyroAngle;
-
-    // The distances and rotations measured at each module.
-    private final SwerveModulePosition[] modulePositions;
-
-    /**
-     * Constructs an Interpolation Record with the specified parameters.
-     *
-     * @param pose The pose observed given the current sensor inputs and the previous pose.
-     * @param gyro The current gyro angle.
-     * @param wheelPositions The distances and rotations measured at each wheel.
-     */
-    private InterpolationRecord(
-        Pose2d poseMeters, Rotation2d gyro, SwerveModulePosition[] modulePositions) {
-      this.poseMeters = poseMeters;
-      this.gyroAngle = gyro;
-      this.modulePositions = modulePositions;
-    }
-
-    /**
-     * Return the interpolated record. This object is assumed to be the starting position, or lower
-     * bound.
-     *
-     * @param endValue The upper bound, or end.
-     * @param t How far between the lower and upper bound we are. This should be bounded in [0, 1].
-     * @return The interpolated value.
-     */
-    @Override
-    public InterpolationRecord interpolate(InterpolationRecord endValue, double t) {
-      if (t < 0) {
-        return this;
-      } else if (t >= 1) {
-        return endValue;
-      } else {
-        // Find the new wheel distances.
-        var modulePositions = new SwerveModulePosition[m_numModules];
-
-        // Find the distance travelled between this measurement and the interpolated measurement.
-        var moduleDeltas = new SwerveModulePosition[m_numModules];
-
-        for (int i = 0; i < m_numModules; i++) {
-          double ds =
-              MathUtil.interpolate(
-                  this.modulePositions[i].distanceMeters,
-                  endValue.modulePositions[i].distanceMeters,
-                  t);
-          Rotation2d theta =
-              this.modulePositions[i].angle.interpolate(endValue.modulePositions[i].angle, t);
-          modulePositions[i] = new SwerveModulePosition(ds, theta);
-          moduleDeltas[i] =
-              new SwerveModulePosition(ds - this.modulePositions[i].distanceMeters, theta);
-        }
-
-        // Find the new gyro angle.
-        var gyro_lerp = gyroAngle.interpolate(endValue.gyroAngle, t);
-
-        // Create a twist to represent this change based on the interpolated sensor inputs.
-        Twist2d twist = m_kinematics.toTwist2d(moduleDeltas);
-        twist.dtheta = gyro_lerp.minus(gyroAngle).getRadians();
-
-        return new InterpolationRecord(poseMeters.exp(twist), gyro_lerp, modulePositions);
-      }
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (this == obj) {
-        return true;
-      }
-      if (!(obj instanceof InterpolationRecord)) {
-        return false;
-      }
-      InterpolationRecord record = (InterpolationRecord) obj;
-      return Objects.equals(gyroAngle, record.gyroAngle)
-          && Arrays.equals(modulePositions, record.modulePositions)
-          && Objects.equals(poseMeters, record.poseMeters);
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(gyroAngle, Arrays.hashCode(modulePositions), poseMeters);
-    }
+    return super.updateWithTime(currentTimeSeconds, gyroAngle, wheelPositions);
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java
index d668564..a5de856 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java
@@ -64,6 +64,10 @@
   /**
    * Constructs an Unscented Kalman Filter.
    *
+   * <p>See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-observers.html#process-and-measurement-noise-covariance-matrices
+   * for how to select the standard deviations.
+   *
    * @param states A Nat representing the number of states.
    * @param outputs A Nat representing the number of outputs.
    * @param f A vector-valued function of x and u that returns the derivative of the state vector.
@@ -100,6 +104,10 @@
    * custom functions for arithmetic can be useful if you have angles in the state or measurements,
    * because they allow you to correctly account for the modular nature of angle arithmetic.
    *
+   * <p>See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-observers.html#process-and-measurement-noise-covariance-matrices
+   * for how to select the standard deviations.
+   *
    * @param states A Nat representing the number of states.
    * @param outputs A Nat representing the number of outputs.
    * @param f A vector-valued function of x and u that returns the derivative of the state vector.
@@ -331,7 +339,7 @@
     // Discretize Q before projecting mean and covariance forward
     Matrix<States, States> contA =
         NumericalJacobian.numericalJacobianX(m_states, m_states, m_f, m_xHat, u);
-    var discQ = Discretization.discretizeAQTaylor(contA, m_contQ, dtSeconds).getSecond();
+    var discQ = Discretization.discretizeAQ(contA, m_contQ, dtSeconds).getSecond();
     var squareRootDiscQ = discQ.lltDecompose(true);
 
     var sigmas = m_pts.squareRootSigmaPoints(m_xHat, m_S);
@@ -373,6 +381,19 @@
   /**
    * Correct the state estimate x-hat using the measurements in y.
    *
+   * <p>This is useful for when the measurement noise covariances vary.
+   *
+   * @param u Same control input used in the predict step.
+   * @param y Measurement vector.
+   * @param R Continuous measurement noise covariance matrix.
+   */
+  public void correct(Matrix<Inputs, N1> u, Matrix<Outputs, N1> y, Matrix<Outputs, Outputs> R) {
+    correct(m_outputs, u, y, m_h, R, m_meanFuncY, m_residualFuncY, m_residualFuncX, m_addFuncX);
+  }
+
+  /**
+   * Correct the state estimate x-hat using the measurements in y.
+   *
    * <p>This is useful for when the measurements available during a timestep's Correct() call vary.
    * The h(x, u) passed to the constructor is used if one is not provided (the two-argument version
    * of this function).
@@ -382,7 +403,7 @@
    * @param u Same control input used in the predict step.
    * @param y Measurement vector.
    * @param h A vector-valued function of x and u that returns the measurement vector.
-   * @param R Measurement noise covariance matrix (continuous-time).
+   * @param R Continuous measurement noise covariance matrix.
    */
   public <R extends Num> void correct(
       Nat<R> rows,
@@ -411,7 +432,7 @@
    * @param u Same control input used in the predict step.
    * @param y Measurement vector.
    * @param h A vector-valued function of x and u that returns the measurement vector.
-   * @param R Measurement noise covariance matrix (continuous-time).
+   * @param R Continuous measurement noise covariance matrix.
    * @param meanFuncY A function that computes the mean of 2 * States + 1 measurement vectors using
    *     a given set of weights.
    * @param residualFuncY A function that computes the residual of two measurement vectors (i.e. it
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/filter/Debouncer.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/filter/Debouncer.java
index 8da45e9..3472198 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/filter/Debouncer.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/filter/Debouncer.java
@@ -4,7 +4,7 @@
 
 package edu.wpi.first.math.filter;
 
-import edu.wpi.first.util.WPIUtilJNI;
+import edu.wpi.first.math.MathSharedStore;
 
 /**
  * A simple debounce filter for boolean streams. Requires that the boolean change value from
@@ -60,11 +60,11 @@
   }
 
   private void resetTimer() {
-    m_prevTimeSeconds = WPIUtilJNI.now() * 1e-6;
+    m_prevTimeSeconds = MathSharedStore.getTimestamp();
   }
 
   private boolean hasElapsed() {
-    return (WPIUtilJNI.now() * 1e-6) - m_prevTimeSeconds >= m_debounceTimeSeconds;
+    return MathSharedStore.getTimestamp() - m_prevTimeSeconds >= m_debounceTimeSeconds;
   }
 
   /**
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/filter/SlewRateLimiter.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/filter/SlewRateLimiter.java
index 668b8b1..6c34f35 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/filter/SlewRateLimiter.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/filter/SlewRateLimiter.java
@@ -4,8 +4,8 @@
 
 package edu.wpi.first.math.filter;
 
+import edu.wpi.first.math.MathSharedStore;
 import edu.wpi.first.math.MathUtil;
-import edu.wpi.first.util.WPIUtilJNI;
 
 /**
  * A class that limits the rate of change of an input value. Useful for implementing voltage,
@@ -33,21 +33,7 @@
     m_positiveRateLimit = positiveRateLimit;
     m_negativeRateLimit = negativeRateLimit;
     m_prevVal = initialValue;
-    m_prevTime = WPIUtilJNI.now() * 1e-6;
-  }
-
-  /**
-   * Creates a new SlewRateLimiter with the given positive rate limit and negative rate limit of
-   * -rateLimit and initial value.
-   *
-   * @param rateLimit The rate-of-change limit, in units per second.
-   * @param initalValue The initial value of the input.
-   * @deprecated Use SlewRateLimiter(double positiveRateLimit, double negativeRateLimit, double
-   *     initalValue) instead.
-   */
-  @Deprecated(since = "2023", forRemoval = true)
-  public SlewRateLimiter(double rateLimit, double initalValue) {
-    this(rateLimit, -rateLimit, initalValue);
+    m_prevTime = MathSharedStore.getTimestamp();
   }
 
   /**
@@ -67,7 +53,7 @@
    * @return The filtered value, which will not change faster than the slew rate.
    */
   public double calculate(double input) {
-    double currentTime = WPIUtilJNI.now() * 1e-6;
+    double currentTime = MathSharedStore.getTimestamp();
     double elapsedTime = currentTime - m_prevTime;
     m_prevVal +=
         MathUtil.clamp(
@@ -85,6 +71,6 @@
    */
   public void reset(double value) {
     m_prevVal = value;
-    m_prevTime = WPIUtilJNI.now() * 1e-6;
+    m_prevTime = MathSharedStore.getTimestamp();
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/CoordinateSystem.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/CoordinateSystem.java
index 5733177..9e827c3 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/CoordinateSystem.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/CoordinateSystem.java
@@ -124,7 +124,9 @@
    */
   public static Transform3d convert(
       Transform3d transform, CoordinateSystem from, CoordinateSystem to) {
+    var coordRot = from.m_rotation.minus(to.m_rotation);
     return new Transform3d(
-        convert(transform.getTranslation(), from, to), convert(transform.getRotation(), from, to));
+        convert(transform.getTranslation(), from, to),
+        coordRot.unaryMinus().plus(transform.getRotation().rotateBy(coordRot)));
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java
index bce832e..bc7f536 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java
@@ -9,7 +9,15 @@
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import edu.wpi.first.math.interpolation.Interpolatable;
+import edu.wpi.first.math.proto.Geometry2D.ProtobufPose2d;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
 import java.util.Objects;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
 /** Represents a 2D pose containing translational and rotational elements. */
 @JsonIgnoreProperties(ignoreUnknown = true)
@@ -136,6 +144,16 @@
   }
 
   /**
+   * Rotates the pose around the origin and returns the new pose.
+   *
+   * @param other The rotation to transform the pose by.
+   * @return The transformed pose.
+   */
+  public Pose2d rotateBy(Rotation2d other) {
+    return new Pose2d(m_translation.rotateBy(other), m_rotation.rotateBy(other));
+  }
+
+  /**
    * Transforms the pose by the given transformation and returns the new pose. See + operator for
    * the matrix multiplication performed.
    *
@@ -238,6 +256,23 @@
     return new Twist2d(translationPart.getX(), translationPart.getY(), dtheta);
   }
 
+  /**
+   * Returns the nearest Pose2d from a list of poses. If two or more poses in the list have the same
+   * distance from this pose, return the one with the closest rotation component.
+   *
+   * @param poses The list of poses to find the nearest.
+   * @return The nearest Pose2d from the list.
+   */
+  public Pose2d nearest(List<Pose2d> poses) {
+    return Collections.min(
+        poses,
+        Comparator.comparing(
+                (Pose2d other) -> this.getTranslation().getDistance(other.getTranslation()))
+            .thenComparing(
+                (Pose2d other) ->
+                    Math.abs(this.getRotation().minus(other.getRotation()).getRadians())));
+  }
+
   @Override
   public String toString() {
     return String.format("Pose2d(%s, %s)", m_translation, m_rotation);
@@ -275,4 +310,83 @@
       return this.exp(scaledTwist);
     }
   }
+
+  public static final class AStruct implements Struct<Pose2d> {
+    @Override
+    public Class<Pose2d> getTypeClass() {
+      return Pose2d.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Pose2d";
+    }
+
+    @Override
+    public int getSize() {
+      return Translation2d.struct.getSize() + Rotation2d.struct.getSize();
+    }
+
+    @Override
+    public String getSchema() {
+      return "Translation2d translation;Rotation2d rotation";
+    }
+
+    @Override
+    public Struct<?>[] getNested() {
+      return new Struct<?>[] {Translation2d.struct, Rotation2d.struct};
+    }
+
+    @Override
+    public Pose2d unpack(ByteBuffer bb) {
+      Translation2d translation = Translation2d.struct.unpack(bb);
+      Rotation2d rotation = Rotation2d.struct.unpack(bb);
+      return new Pose2d(translation, rotation);
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Pose2d value) {
+      Translation2d.struct.pack(bb, value.m_translation);
+      Rotation2d.struct.pack(bb, value.m_rotation);
+    }
+  }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Pose2d, ProtobufPose2d> {
+    @Override
+    public Class<Pose2d> getTypeClass() {
+      return Pose2d.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufPose2d.getDescriptor();
+    }
+
+    @Override
+    public Protobuf<?, ?>[] getNested() {
+      return new Protobuf<?, ?>[] {Translation2d.proto, Rotation2d.proto};
+    }
+
+    @Override
+    public ProtobufPose2d createMessage() {
+      return ProtobufPose2d.newInstance();
+    }
+
+    @Override
+    public Pose2d unpack(ProtobufPose2d msg) {
+      return new Pose2d(
+          Translation2d.proto.unpack(msg.getTranslation()),
+          Rotation2d.proto.unpack(msg.getRotation()));
+    }
+
+    @Override
+    public void pack(ProtobufPose2d msg, Pose2d value) {
+      Translation2d.proto.pack(msg.getMutableTranslation(), value.m_translation);
+      Rotation2d.proto.pack(msg.getMutableRotation(), value.m_rotation);
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java
index 8e3a3fe..2ea7094 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java
@@ -8,14 +8,14 @@
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import edu.wpi.first.math.MatBuilder;
-import edu.wpi.first.math.Matrix;
-import edu.wpi.first.math.Nat;
-import edu.wpi.first.math.VecBuilder;
-import edu.wpi.first.math.Vector;
+import edu.wpi.first.math.WPIMathJNI;
 import edu.wpi.first.math.interpolation.Interpolatable;
-import edu.wpi.first.math.numbers.N3;
+import edu.wpi.first.math.proto.Geometry3D.ProtobufPose3d;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
 import java.util.Objects;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
 /** Represents a 3D pose containing translational and rotational elements. */
 @JsonIgnoreProperties(ignoreUnknown = true)
@@ -68,7 +68,10 @@
   }
 
   /**
-   * Transforms the pose by the given transformation and returns the new transformed pose.
+   * Transforms the pose by the given transformation and returns the new transformed pose. The
+   * transform is applied relative to the pose's frame. Note that this differs from {@link
+   * Pose3d#rotateBy(Rotation3d)}, which is applied relative to the global frame and around the
+   * origin.
    *
    * @param other The transform to transform the pose by.
    * @return The transformed pose.
@@ -156,8 +159,21 @@
   }
 
   /**
-   * Transforms the pose by the given transformation and returns the new pose. See + operator for
-   * the matrix multiplication performed.
+   * Rotates the pose around the origin and returns the new pose.
+   *
+   * @param other The rotation to transform the pose by, which is applied extrinsically (from the
+   *     global frame).
+   * @return The rotated pose.
+   */
+  public Pose3d rotateBy(Rotation3d other) {
+    return new Pose3d(m_translation.rotateBy(other), m_rotation.rotateBy(other));
+  }
+
+  /**
+   * Transforms the pose by the given transformation and returns the new transformed pose. The
+   * transform is applied relative to the pose's frame. Note that this differs from {@link
+   * Pose3d#rotateBy(Rotation3d)}, which is applied relative to the global frame and around the
+   * origin.
    *
    * @param other The transform to transform the pose by.
    * @return The transformed pose.
@@ -200,36 +216,28 @@
    * @return The new pose of the robot.
    */
   public Pose3d exp(Twist3d twist) {
-    final var Omega = rotationVectorToMatrix(VecBuilder.fill(twist.rx, twist.ry, twist.rz));
-    final var OmegaSq = Omega.times(Omega);
-
-    double thetaSq = twist.rx * twist.rx + twist.ry * twist.ry + twist.rz * twist.rz;
-
-    // Get left Jacobian of SO3. See first line in right column of
-    // http://asrl.utias.utoronto.ca/~tdb/bib/barfoot_ser17_identities.pdf
-    Matrix<N3, N3> J;
-    if (thetaSq < 1E-9 * 1E-9) {
-      // J = I + 0.5ω
-      J = Matrix.eye(Nat.N3()).plus(Omega.times(0.5));
-    } else {
-      double theta = Math.sqrt(thetaSq);
-      // J = I + (1 − cos(θ))/θ² ω + (θ − sin(θ))/θ³ ω²
-      J =
-          Matrix.eye(Nat.N3())
-              .plus(Omega.times((1.0 - Math.cos(theta)) / thetaSq))
-              .plus(OmegaSq.times((theta - Math.sin(theta)) / (thetaSq * theta)));
-    }
-
-    // Get translation component
-    final var translation =
-        J.times(new MatBuilder<>(Nat.N3(), Nat.N1()).fill(twist.dx, twist.dy, twist.dz));
-
-    final var transform =
-        new Transform3d(
-            new Translation3d(translation.get(0, 0), translation.get(1, 0), translation.get(2, 0)),
-            new Rotation3d(twist.rx, twist.ry, twist.rz));
-
-    return this.plus(transform);
+    var quaternion = this.getRotation().getQuaternion();
+    double[] resultArray =
+        WPIMathJNI.expPose3d(
+            this.getX(),
+            this.getY(),
+            this.getZ(),
+            quaternion.getW(),
+            quaternion.getX(),
+            quaternion.getY(),
+            quaternion.getZ(),
+            twist.dx,
+            twist.dy,
+            twist.dz,
+            twist.rx,
+            twist.ry,
+            twist.rz);
+    return new Pose3d(
+        resultArray[0],
+        resultArray[1],
+        resultArray[2],
+        new Rotation3d(
+            new Quaternion(resultArray[3], resultArray[4], resultArray[5], resultArray[6])));
   }
 
   /**
@@ -240,50 +248,31 @@
    * @return The twist that maps this to end.
    */
   public Twist3d log(Pose3d end) {
-    final var transform = end.relativeTo(this);
-
-    final var rotVec = transform.getRotation().getQuaternion().toRotationVector();
-
-    final var Omega = rotationVectorToMatrix(rotVec);
-    final var OmegaSq = Omega.times(Omega);
-
-    double thetaSq =
-        rotVec.get(0, 0) * rotVec.get(0, 0)
-            + rotVec.get(1, 0) * rotVec.get(1, 0)
-            + rotVec.get(2, 0) * rotVec.get(2, 0);
-
-    // Get left Jacobian inverse of SO3. See fourth line in right column of
-    // http://asrl.utias.utoronto.ca/~tdb/bib/barfoot_ser17_identities.pdf
-    Matrix<N3, N3> Jinv;
-    if (thetaSq < 1E-9 * 1E-9) {
-      // J⁻¹ = I − 0.5ω + 1/12 ω²
-      Jinv = Matrix.eye(Nat.N3()).minus(Omega.times(0.5)).plus(OmegaSq.times(1.0 / 12.0));
-    } else {
-      double theta = Math.sqrt(thetaSq);
-      double halfTheta = 0.5 * theta;
-
-      // J⁻¹ = I − 0.5ω + (1 − 0.5θ cos(θ/2) / sin(θ/2))/θ² ω²
-      Jinv =
-          Matrix.eye(Nat.N3())
-              .minus(Omega.times(0.5))
-              .plus(
-                  OmegaSq.times(
-                      (1.0 - 0.5 * theta * Math.cos(halfTheta) / Math.sin(halfTheta)) / thetaSq));
-    }
-
-    // Get dtranslation component
-    final var dtranslation =
-        Jinv.times(
-            new MatBuilder<>(Nat.N3(), Nat.N1())
-                .fill(transform.getX(), transform.getY(), transform.getZ()));
-
+    var thisQuaternion = this.getRotation().getQuaternion();
+    var endQuaternion = end.getRotation().getQuaternion();
+    double[] resultArray =
+        WPIMathJNI.logPose3d(
+            this.getX(),
+            this.getY(),
+            this.getZ(),
+            thisQuaternion.getW(),
+            thisQuaternion.getX(),
+            thisQuaternion.getY(),
+            thisQuaternion.getZ(),
+            end.getX(),
+            end.getY(),
+            end.getZ(),
+            endQuaternion.getW(),
+            endQuaternion.getX(),
+            endQuaternion.getY(),
+            endQuaternion.getZ());
     return new Twist3d(
-        dtranslation.get(0, 0),
-        dtranslation.get(1, 0),
-        dtranslation.get(2, 0),
-        rotVec.get(0, 0),
-        rotVec.get(1, 0),
-        rotVec.get(2, 0));
+        resultArray[0],
+        resultArray[1],
+        resultArray[2],
+        resultArray[3],
+        resultArray[4],
+        resultArray[5]);
   }
 
   /**
@@ -335,30 +324,82 @@
     }
   }
 
-  /**
-   * Applies the hat operator to a rotation vector.
-   *
-   * <p>It takes a rotation vector and returns the corresponding matrix representation of the Lie
-   * algebra element (a 3x3 rotation matrix).
-   *
-   * @param rotation The rotation vector.
-   * @return The rotation vector as a 3x3 rotation matrix.
-   */
-  private Matrix<N3, N3> rotationVectorToMatrix(Vector<N3> rotation) {
-    // Given a rotation vector <a, b, c>,
-    //         [ 0 -c  b]
-    // Omega = [ c  0 -a]
-    //         [-b  a  0]
-    return new MatBuilder<>(Nat.N3(), Nat.N3())
-        .fill(
-            0.0,
-            -rotation.get(2, 0),
-            rotation.get(1, 0),
-            rotation.get(2, 0),
-            0.0,
-            -rotation.get(0, 0),
-            -rotation.get(1, 0),
-            rotation.get(0, 0),
-            0.0);
+  public static final class AStruct implements Struct<Pose3d> {
+    @Override
+    public Class<Pose3d> getTypeClass() {
+      return Pose3d.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Pose3d";
+    }
+
+    @Override
+    public int getSize() {
+      return Translation3d.struct.getSize() + Rotation3d.struct.getSize();
+    }
+
+    @Override
+    public String getSchema() {
+      return "Translation3d translation;Rotation3d rotation";
+    }
+
+    @Override
+    public Struct<?>[] getNested() {
+      return new Struct<?>[] {Translation3d.struct, Rotation3d.struct};
+    }
+
+    @Override
+    public Pose3d unpack(ByteBuffer bb) {
+      Translation3d translation = Translation3d.struct.unpack(bb);
+      Rotation3d rotation = Rotation3d.struct.unpack(bb);
+      return new Pose3d(translation, rotation);
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Pose3d value) {
+      Translation3d.struct.pack(bb, value.m_translation);
+      Rotation3d.struct.pack(bb, value.m_rotation);
+    }
   }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Pose3d, ProtobufPose3d> {
+    @Override
+    public Class<Pose3d> getTypeClass() {
+      return Pose3d.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufPose3d.getDescriptor();
+    }
+
+    @Override
+    public Protobuf<?, ?>[] getNested() {
+      return new Protobuf<?, ?>[] {Translation3d.proto, Rotation3d.proto};
+    }
+
+    @Override
+    public ProtobufPose3d createMessage() {
+      return ProtobufPose3d.newInstance();
+    }
+
+    @Override
+    public Pose3d unpack(ProtobufPose3d msg) {
+      return new Pose3d(
+          Translation3d.proto.unpack(msg.getTranslation()),
+          Rotation3d.proto.unpack(msg.getRotation()));
+    }
+
+    @Override
+    public void pack(ProtobufPose3d msg, Pose3d value) {
+      Translation3d.proto.pack(msg.getMutableTranslation(), value.m_translation);
+      Rotation3d.proto.pack(msg.getMutableRotation(), value.m_rotation);
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Quaternion.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Quaternion.java
index cadfaa4..23fd26b 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Quaternion.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Quaternion.java
@@ -11,18 +11,30 @@
 import edu.wpi.first.math.VecBuilder;
 import edu.wpi.first.math.Vector;
 import edu.wpi.first.math.numbers.N3;
+import edu.wpi.first.math.proto.Geometry3D.ProtobufQuaternion;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
 import java.util.Objects;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
 public class Quaternion {
-  private final double m_r;
-  private final Vector<N3> m_v;
+  // Scalar r in versor form
+  private final double m_w;
+
+  // Vector v in versor form
+  private final double m_x;
+  private final double m_y;
+  private final double m_z;
 
   /** Constructs a quaternion with a default angle of 0 degrees. */
   public Quaternion() {
-    m_r = 1.0;
-    m_v = VecBuilder.fill(0.0, 0.0, 0.0);
+    m_w = 1.0;
+    m_x = 0.0;
+    m_y = 0.0;
+    m_z = 0.0;
   }
 
   /**
@@ -39,8 +51,52 @@
       @JsonProperty(required = true, value = "X") double x,
       @JsonProperty(required = true, value = "Y") double y,
       @JsonProperty(required = true, value = "Z") double z) {
-    m_r = w;
-    m_v = VecBuilder.fill(x, y, z);
+    m_w = w;
+    m_x = x;
+    m_y = y;
+    m_z = z;
+  }
+
+  /**
+   * Adds another quaternion to this quaternion entrywise.
+   *
+   * @param other The other quaternion.
+   * @return The quaternion sum.
+   */
+  public Quaternion plus(Quaternion other) {
+    return new Quaternion(
+        getW() + other.getW(), getX() + other.getX(), getY() + other.getY(), getZ() + other.getZ());
+  }
+
+  /**
+   * Subtracts another quaternion from this quaternion entrywise.
+   *
+   * @param other The other quaternion.
+   * @return The quaternion difference.
+   */
+  public Quaternion minus(Quaternion other) {
+    return new Quaternion(
+        getW() - other.getW(), getX() - other.getX(), getY() - other.getY(), getZ() - other.getZ());
+  }
+
+  /**
+   * Divides by a scalar.
+   *
+   * @param scalar The value to scale each component by.
+   * @return The scaled quaternion.
+   */
+  public Quaternion divide(double scalar) {
+    return new Quaternion(getW() / scalar, getX() / scalar, getY() / scalar, getZ() / scalar);
+  }
+
+  /**
+   * Multiplies with a scalar.
+   *
+   * @param scalar The value to scale each component by.
+   * @return The scaled quaternion.
+   */
+  public Quaternion times(double scalar) {
+    return new Quaternion(getW() * scalar, getX() * scalar, getY() * scalar, getZ() * scalar);
   }
 
   /**
@@ -51,28 +107,29 @@
    */
   public Quaternion times(Quaternion other) {
     // https://en.wikipedia.org/wiki/Quaternion#Scalar_and_vector_parts
-    final var r1 = m_r;
-    final var v1 = m_v;
-    final var r2 = other.m_r;
-    final var v2 = other.m_v;
+    final var r1 = m_w;
+    final var r2 = other.m_w;
+
+    // v₁ ⋅ v₂
+    double dot = m_x * other.m_x + m_y * other.m_y + m_z * other.m_z;
 
     // v₁ x v₂
-    var cross =
-        VecBuilder.fill(
-            v1.get(1, 0) * v2.get(2, 0) - v2.get(1, 0) * v1.get(2, 0),
-            v2.get(0, 0) * v1.get(2, 0) - v1.get(0, 0) * v2.get(2, 0),
-            v1.get(0, 0) * v2.get(1, 0) - v2.get(0, 0) * v1.get(1, 0));
+    double cross_x = m_y * other.m_z - other.m_y * m_z;
+    double cross_y = other.m_x * m_z - m_x * other.m_z;
+    double cross_z = m_x * other.m_y - other.m_x * m_y;
 
-    // v = r₁v₂ + r₂v₁ + v₁ x v₂
-    final var v = v2.times(r1).plus(v1.times(r2)).plus(cross);
-
-    return new Quaternion(r1 * r2 - v1.dot(v2), v.get(0, 0), v.get(1, 0), v.get(2, 0));
+    return new Quaternion(
+        // r = r₁r₂ − v₁ ⋅ v₂
+        r1 * r2 - dot,
+        // v = r₁v₂ + r₂v₁ + v₁ x v₂
+        r1 * other.m_x + r2 * m_x + cross_x,
+        r1 * other.m_y + r2 * m_y + cross_y,
+        r1 * other.m_z + r2 * m_z + cross_z);
   }
 
   @Override
   public String toString() {
-    return String.format(
-        "Quaternion(%s, %s, %s, %s)", m_r, m_v.get(0, 0), m_v.get(1, 0), m_v.get(2, 0));
+    return String.format("Quaternion(%s, %s, %s, %s)", getW(), getX(), getY(), getZ());
   }
 
   /**
@@ -86,14 +143,37 @@
     if (obj instanceof Quaternion) {
       var other = (Quaternion) obj;
 
-      return Math.abs(m_r * other.m_r + m_v.dot(other.m_v)) > 1.0 - 1E-9;
+      return Math.abs(dot(other) - norm() * other.norm()) < 1e-9
+          && Math.abs(norm() - other.norm()) < 1e-9;
     }
     return false;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(m_r, m_v);
+    return Objects.hash(m_w, m_x, m_y, m_z);
+  }
+
+  /**
+   * Returns the conjugate of the quaternion.
+   *
+   * @return The conjugate quaternion.
+   */
+  public Quaternion conjugate() {
+    return new Quaternion(getW(), -getX(), -getY(), -getZ());
+  }
+
+  /**
+   * Returns the elementwise product of two quaternions.
+   *
+   * @param other The other quaternion.
+   * @return The dot product of two quaternions.
+   */
+  public double dot(final Quaternion other) {
+    return getW() * other.getW()
+        + getX() * other.getX()
+        + getY() * other.getY()
+        + getZ() * other.getZ();
   }
 
   /**
@@ -102,7 +182,17 @@
    * @return The inverse quaternion.
    */
   public Quaternion inverse() {
-    return new Quaternion(m_r, -m_v.get(0, 0), -m_v.get(1, 0), -m_v.get(2, 0));
+    var norm = norm();
+    return conjugate().divide(norm * norm);
+  }
+
+  /**
+   * Calculates the L2 norm of the quaternion.
+   *
+   * @return The L2 norm.
+   */
+  public double norm() {
+    return Math.sqrt(dot(this));
   }
 
   /**
@@ -111,7 +201,7 @@
    * @return The normalized quaternion.
    */
   public Quaternion normalize() {
-    double norm = Math.sqrt(getW() * getW() + getX() * getX() + getY() * getY() + getZ() * getZ());
+    double norm = norm();
     if (norm == 0.0) {
       return new Quaternion();
     } else {
@@ -120,13 +210,111 @@
   }
 
   /**
+   * Rational power of a quaternion.
+   *
+   * @param t the power to raise this quaternion to.
+   * @return The quaternion power
+   */
+  public Quaternion pow(double t) {
+    // q^t = e^(ln(q^t)) = e^(t * ln(q))
+    return this.log().times(t).exp();
+  }
+
+  /**
+   * Matrix exponential of a quaternion.
+   *
+   * @param adjustment the "Twist" that will be applied to this quaternion.
+   * @return The quaternion product of exp(adjustment) * this
+   */
+  public Quaternion exp(Quaternion adjustment) {
+    return adjustment.exp().times(this);
+  }
+
+  /**
+   * Matrix exponential of a quaternion.
+   *
+   * <p>source: wpimath/algorithms.md
+   *
+   * <p>If this quaternion is in 𝖘𝖔(3) and you are looking for an element of SO(3), use {@link
+   * fromRotationVector}
+   *
+   * @return The Matrix exponential of this quaternion.
+   */
+  public Quaternion exp() {
+    var scalar = Math.exp(getW());
+
+    var axial_magnitude = Math.sqrt(getX() * getX() + getY() * getY() + getZ() * getZ());
+    var cosine = Math.cos(axial_magnitude);
+
+    double axial_scalar;
+
+    if (axial_magnitude < 1e-9) {
+      // Taylor series of sin(θ) / θ near θ = 0: 1 − θ²/6 + θ⁴/120 + O(n⁶)
+      var axial_magnitude_sq = axial_magnitude * axial_magnitude;
+      var axial_magnitude_sq_sq = axial_magnitude_sq * axial_magnitude_sq;
+      axial_scalar = 1.0 - axial_magnitude_sq / 6.0 + axial_magnitude_sq_sq / 120.0;
+    } else {
+      axial_scalar = Math.sin(axial_magnitude) / axial_magnitude;
+    }
+
+    return new Quaternion(
+        cosine * scalar,
+        getX() * axial_scalar * scalar,
+        getY() * axial_scalar * scalar,
+        getZ() * axial_scalar * scalar);
+  }
+
+  /**
+   * Log operator of a quaternion.
+   *
+   * @param end The quaternion to map this quaternion onto.
+   * @return The "Twist" that maps this quaternion to the argument.
+   */
+  public Quaternion log(Quaternion end) {
+    return end.times(this.inverse()).log();
+  }
+
+  /**
+   * The Log operator of a general quaternion.
+   *
+   * <p>source: wpimath/algorithms.md
+   *
+   * <p>If this quaternion is in SO(3) and you are looking for an element of 𝖘𝖔(3), use {@link
+   * toRotationVector}
+   *
+   * @return The logarithm of this quaternion.
+   */
+  public Quaternion log() {
+    var scalar = Math.log(norm());
+
+    var v_norm = Math.sqrt(getX() * getX() + getY() * getY() + getZ() * getZ());
+
+    var s_norm = getW() / norm();
+
+    if (Math.abs(s_norm + 1) < 1e-9) {
+      return new Quaternion(scalar, -Math.PI, 0, 0);
+    }
+
+    double v_scalar;
+
+    if (v_norm < 1e-9) {
+      // Taylor series expansion of atan2(y / x) / y around y = 0 => 1/x - y²/3*x³ + O(y⁴)
+      v_scalar = 1.0 / getW() - 1.0 / 3.0 * v_norm * v_norm / (getW() * getW() * getW());
+    } else {
+      v_scalar = Math.atan2(v_norm, getW()) / v_norm;
+    }
+
+    return new Quaternion(scalar, v_scalar * getX(), v_scalar * getY(), v_scalar * getZ());
+  }
+
+  /**
    * Returns W component of the quaternion.
    *
    * @return W component of the quaternion.
    */
   @JsonProperty(value = "W")
   public double getW() {
-    return m_r;
+    return m_w;
   }
 
   /**
@@ -136,7 +324,7 @@
    */
   @JsonProperty(value = "X")
   public double getX() {
-    return m_v.get(0, 0);
+    return m_x;
   }
 
   /**
@@ -146,7 +334,7 @@
    */
   @JsonProperty(value = "Y")
   public double getY() {
-    return m_v.get(1, 0);
+    return m_y;
   }
 
   /**
@@ -156,7 +344,38 @@
    */
   @JsonProperty(value = "Z")
   public double getZ() {
-    return m_v.get(2, 0);
+    return m_z;
+  }
+
+  /**
+   * Returns the quaternion representation of this rotation vector.
+   *
+   * <p>This is also the exp operator of 𝖘𝖔(3).
+   *
+   * <p>source: wpimath/algorithms.md
+   *
+   * @param rvec The rotation vector.
+   * @return The quaternion representation of this rotation vector.
+   */
+  public static Quaternion fromRotationVector(Vector<N3> rvec) {
+    double theta = rvec.norm();
+
+    double cos = Math.cos(theta / 2);
+
+    double axial_scalar;
+
+    if (theta < 1e-9) {
+      // taylor series expansion of sin(θ/2) / θ = 1/2 - θ²/48 + O(θ⁴)
+      axial_scalar = 1.0 / 2.0 - theta * theta / 48.0;
+    } else {
+      axial_scalar = Math.sin(theta / 2) / theta;
+    }
+
+    return new Quaternion(
+        cos,
+        axial_scalar * rvec.get(0, 0),
+        axial_scalar * rvec.get(1, 0),
+        axial_scalar * rvec.get(2, 0));
   }
 
   /**
@@ -171,16 +390,89 @@
     // Sound State Representation through Encapsulation of Manifolds"
     //
     // https://arxiv.org/pdf/1107.1119.pdf
-    double norm = m_v.norm();
+    double norm = Math.sqrt(getX() * getX() + getY() * getY() + getZ() * getZ());
 
+    double coeff;
     if (norm < 1e-9) {
-      return m_v.times(2.0 / getW() - 2.0 / 3.0 * norm * norm / (getW() * getW() * getW()));
+      coeff = 2.0 / getW() - 2.0 / 3.0 * norm * norm / (getW() * getW() * getW());
     } else {
       if (getW() < 0.0) {
-        return m_v.times(2.0 * Math.atan2(-norm, -getW()) / norm);
+        coeff = 2.0 * Math.atan2(-norm, -getW()) / norm;
       } else {
-        return m_v.times(2.0 * Math.atan2(norm, getW()) / norm);
+        coeff = 2.0 * Math.atan2(norm, getW()) / norm;
       }
     }
+
+    return VecBuilder.fill(coeff * getX(), coeff * getY(), coeff * getZ());
   }
+
+  public static final class AStruct implements Struct<Quaternion> {
+    @Override
+    public Class<Quaternion> getTypeClass() {
+      return Quaternion.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Quaternion";
+    }
+
+    @Override
+    public int getSize() {
+      return kSizeDouble * 4;
+    }
+
+    @Override
+    public String getSchema() {
+      return "double w;double x;double y;double z";
+    }
+
+    @Override
+    public Quaternion unpack(ByteBuffer bb) {
+      double w = bb.getDouble();
+      double x = bb.getDouble();
+      double y = bb.getDouble();
+      double z = bb.getDouble();
+      return new Quaternion(w, x, y, z);
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Quaternion value) {
+      bb.putDouble(value.getW());
+      bb.putDouble(value.getX());
+      bb.putDouble(value.getY());
+      bb.putDouble(value.getZ());
+    }
+  }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Quaternion, ProtobufQuaternion> {
+    @Override
+    public Class<Quaternion> getTypeClass() {
+      return Quaternion.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufQuaternion.getDescriptor();
+    }
+
+    @Override
+    public ProtobufQuaternion createMessage() {
+      return ProtobufQuaternion.newInstance();
+    }
+
+    @Override
+    public Quaternion unpack(ProtobufQuaternion msg) {
+      return new Quaternion(msg.getW(), msg.getX(), msg.getY(), msg.getZ());
+    }
+
+    @Override
+    public void pack(ProtobufQuaternion msg, Quaternion value) {
+      msg.setW(value.getW()).setX(value.getX()).setY(value.getY()).setZ(value.getZ());
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java
index 5be6156..0556669 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java
@@ -10,8 +10,13 @@
 import com.fasterxml.jackson.annotation.JsonProperty;
 import edu.wpi.first.math.MathUtil;
 import edu.wpi.first.math.interpolation.Interpolatable;
+import edu.wpi.first.math.proto.Geometry2D.ProtobufRotation2d;
 import edu.wpi.first.math.util.Units;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
 import java.util.Objects;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
 /**
  * A rotation in a 2D coordinate frame represented by a point on the unit circle (cosine and sine).
@@ -256,4 +261,67 @@
   public Rotation2d interpolate(Rotation2d endValue, double t) {
     return plus(endValue.minus(this).times(MathUtil.clamp(t, 0, 1)));
   }
+
+  public static final class AStruct implements Struct<Rotation2d> {
+    @Override
+    public Class<Rotation2d> getTypeClass() {
+      return Rotation2d.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Rotation2d";
+    }
+
+    @Override
+    public int getSize() {
+      return kSizeDouble;
+    }
+
+    @Override
+    public String getSchema() {
+      return "double value";
+    }
+
+    @Override
+    public Rotation2d unpack(ByteBuffer bb) {
+      return new Rotation2d(bb.getDouble());
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Rotation2d value) {
+      bb.putDouble(value.m_value);
+    }
+  }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Rotation2d, ProtobufRotation2d> {
+    @Override
+    public Class<Rotation2d> getTypeClass() {
+      return Rotation2d.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufRotation2d.getDescriptor();
+    }
+
+    @Override
+    public ProtobufRotation2d createMessage() {
+      return ProtobufRotation2d.newInstance();
+    }
+
+    @Override
+    public Rotation2d unpack(ProtobufRotation2d msg) {
+      return new Rotation2d(msg.getValue());
+    }
+
+    @Override
+    public void pack(ProtobufRotation2d msg, Rotation2d value) {
+      msg.setValue(value.m_value);
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation3d.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation3d.java
index 3226e31..b434523 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation3d.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation3d.java
@@ -17,17 +17,24 @@
 import edu.wpi.first.math.Vector;
 import edu.wpi.first.math.interpolation.Interpolatable;
 import edu.wpi.first.math.numbers.N3;
+import edu.wpi.first.math.proto.Geometry3D.ProtobufRotation3d;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
 import java.util.Objects;
 import org.ejml.dense.row.factory.DecompositionFactory_DDRM;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
 /** A rotation in a 3D coordinate frame represented by a quaternion. */
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
 public class Rotation3d implements Interpolatable<Rotation3d> {
-  private Quaternion m_q = new Quaternion();
+  private final Quaternion m_q;
 
   /** Constructs a Rotation3d with a default angle of 0 degrees. */
-  public Rotation3d() {}
+  public Rotation3d() {
+    m_q = new Quaternion();
+  }
 
   /**
    * Constructs a Rotation3d from a quaternion.
@@ -73,6 +80,17 @@
   }
 
   /**
+   * Constructs a Rotation3d with the given rotation vector representation. This representation is
+   * equivalent to axis-angle, where the normalized axis is multiplied by the rotation around the
+   * axis in radians.
+   *
+   * @param rvec The rotation vector.
+   */
+  public Rotation3d(Vector<N3> rvec) {
+    this(rvec, rvec.norm());
+  }
+
+  /**
    * Constructs a Rotation3d with the given axis-angle representation. The axis doesn't have to be
    * normalized.
    *
@@ -82,6 +100,7 @@
   public Rotation3d(Vector<N3> axis, double angleRadians) {
     double norm = axis.norm();
     if (norm == 0.0) {
+      m_q = new Quaternion();
       return;
     }
 
@@ -175,6 +194,7 @@
     if (dotNorm > 1.0 - 1E-9) {
       // If the dot product is 1, the two vectors point in the same direction so
       // there's no rotation. The default initialization of m_q will work.
+      m_q = new Quaternion();
       return;
     } else if (dotNorm < -1.0 + 1E-9) {
       // If the dot product is -1, the two vectors point in opposite directions
@@ -267,9 +287,14 @@
   }
 
   /**
-   * Adds the new rotation to the current rotation.
+   * Adds the new rotation to the current rotation. The other rotation is applied extrinsically,
+   * which means that it rotates around the global axes. For example, {@code new
+   * Rotation3d(Units.degreesToRadians(90), 0, 0).rotateBy(new Rotation3d(0,
+   * Units.degreesToRadians(45), 0))} rotates by 90 degrees around the +X axis and then by 45
+   * degrees around the global +Y axis. (This is equivalent to {@code new
+   * Rotation3d(Units.degreesToRadians(90), Units.degreesToRadians(45), 0)})
    *
-   * @param other The rotation to rotate by.
+   * @param other The extrinsic rotation to rotate by.
    * @return The new rotated Rotation3d.
    */
   public Rotation3d rotateBy(Rotation3d other) {
@@ -297,8 +322,15 @@
     final var y = m_q.getY();
     final var z = m_q.getZ();
 
-    // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_conversion
-    return Math.atan2(2.0 * (w * x + y * z), 1.0 - 2.0 * (x * x + y * y));
+    // wpimath/algorithms.md
+    final var cxcy = 1.0 - 2.0 * (x * x + y * y);
+    final var sxcy = 2.0 * (w * x + y * z);
+    final var cy_sq = cxcy * cxcy + sxcy * sxcy;
+    if (cy_sq > 1e-20) {
+      return Math.atan2(sxcy, cxcy);
+    } else {
+      return 0.0;
+    }
   }
 
   /**
@@ -312,7 +344,7 @@
     final var y = m_q.getY();
     final var z = m_q.getZ();
 
-    // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_conversion
+    // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_(in_3-2-1_sequence)_conversion
     double ratio = 2.0 * (w * y - z * x);
     if (Math.abs(ratio) >= 1.0) {
       return Math.copySign(Math.PI / 2.0, ratio);
@@ -332,8 +364,15 @@
     final var y = m_q.getY();
     final var z = m_q.getZ();
 
-    // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_conversion
-    return Math.atan2(2.0 * (w * z + x * y), 1.0 - 2.0 * (y * y + z * z));
+    // wpimath/algorithms.md
+    final var cycz = 1.0 - 2.0 * (y * y + z * z);
+    final var cysz = 2.0 * (w * z + x * y);
+    final var cy_sq = cycz * cycz + cysz * cysz;
+    if (cy_sq > 1e-20) {
+      return Math.atan2(cysz, cycz);
+    } else {
+      return Math.atan2(2.0 * w * z, w * w - z * z);
+    }
   }
 
   /**
@@ -386,7 +425,7 @@
   public boolean equals(Object obj) {
     if (obj instanceof Rotation3d) {
       var other = (Rotation3d) obj;
-      return m_q.equals(other.m_q);
+      return Math.abs(Math.abs(m_q.dot(other.m_q)) - m_q.norm() * other.m_q.norm()) < 1e-9;
     }
     return false;
   }
@@ -400,4 +439,77 @@
   public Rotation3d interpolate(Rotation3d endValue, double t) {
     return plus(endValue.minus(this).times(MathUtil.clamp(t, 0, 1)));
   }
+
+  public static final class AStruct implements Struct<Rotation3d> {
+    @Override
+    public Class<Rotation3d> getTypeClass() {
+      return Rotation3d.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Rotation3d";
+    }
+
+    @Override
+    public int getSize() {
+      return Quaternion.struct.getSize();
+    }
+
+    @Override
+    public String getSchema() {
+      return "Quaternion q";
+    }
+
+    @Override
+    public Struct<?>[] getNested() {
+      return new Struct<?>[] {Quaternion.struct};
+    }
+
+    @Override
+    public Rotation3d unpack(ByteBuffer bb) {
+      return new Rotation3d(Quaternion.struct.unpack(bb));
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Rotation3d value) {
+      Quaternion.struct.pack(bb, value.m_q);
+    }
+  }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Rotation3d, ProtobufRotation3d> {
+    @Override
+    public Class<Rotation3d> getTypeClass() {
+      return Rotation3d.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufRotation3d.getDescriptor();
+    }
+
+    @Override
+    public Protobuf<?, ?>[] getNested() {
+      return new Protobuf<?, ?>[] {Quaternion.proto};
+    }
+
+    @Override
+    public ProtobufRotation3d createMessage() {
+      return ProtobufRotation3d.newInstance();
+    }
+
+    @Override
+    public Rotation3d unpack(ProtobufRotation3d msg) {
+      return new Rotation3d(Quaternion.proto.unpack(msg.getQ()));
+    }
+
+    @Override
+    public void pack(ProtobufRotation3d msg, Rotation3d value) {
+      Quaternion.proto.pack(msg.getMutableQ(), value.m_q);
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java
index c3c6b0c..c7959ba 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java
@@ -4,9 +4,14 @@
 
 package edu.wpi.first.math.geometry;
 
+import edu.wpi.first.math.proto.Geometry2D.ProtobufTransform2d;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
 import java.util.Objects;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
-/** Represents a transformation for a Pose2d. */
+/** Represents a transformation for a Pose2d in the pose's frame. */
 public class Transform2d {
   private final Translation2d m_translation;
   private final Rotation2d m_rotation;
@@ -40,6 +45,18 @@
     m_rotation = rotation;
   }
 
+  /**
+   * Constructs a transform with x and y translations instead of a separate Translation2d.
+   *
+   * @param x The x component of the translational component of the transform.
+   * @param y The y component of the translational component of the transform.
+   * @param rotation The rotational component of the transform.
+   */
+  public Transform2d(double x, double y, Rotation2d rotation) {
+    m_translation = new Translation2d(x, y);
+    m_rotation = rotation;
+  }
+
   /** Constructs the identity transform -- maps an initial pose to itself. */
   public Transform2d() {
     m_translation = new Translation2d();
@@ -67,7 +84,8 @@
   }
 
   /**
-   * Composes two transformations.
+   * Composes two transformations. The second transform is applied relative to the orientation of
+   * the first.
    *
    * @param other The transform to compose with this one.
    * @return The composition of the two transformations.
@@ -150,4 +168,83 @@
   public int hashCode() {
     return Objects.hash(m_translation, m_rotation);
   }
+
+  public static final class AStruct implements Struct<Transform2d> {
+    @Override
+    public Class<Transform2d> getTypeClass() {
+      return Transform2d.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Transform2d";
+    }
+
+    @Override
+    public int getSize() {
+      return Translation2d.struct.getSize() + Rotation2d.struct.getSize();
+    }
+
+    @Override
+    public String getSchema() {
+      return "Translation2d translation;Rotation2d rotation";
+    }
+
+    @Override
+    public Struct<?>[] getNested() {
+      return new Struct<?>[] {Translation2d.struct, Rotation2d.struct};
+    }
+
+    @Override
+    public Transform2d unpack(ByteBuffer bb) {
+      Translation2d translation = Translation2d.struct.unpack(bb);
+      Rotation2d rotation = Rotation2d.struct.unpack(bb);
+      return new Transform2d(translation, rotation);
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Transform2d value) {
+      Translation2d.struct.pack(bb, value.m_translation);
+      Rotation2d.struct.pack(bb, value.m_rotation);
+    }
+  }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Transform2d, ProtobufTransform2d> {
+    @Override
+    public Class<Transform2d> getTypeClass() {
+      return Transform2d.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufTransform2d.getDescriptor();
+    }
+
+    @Override
+    public Protobuf<?, ?>[] getNested() {
+      return new Protobuf<?, ?>[] {Translation2d.proto, Rotation2d.proto};
+    }
+
+    @Override
+    public ProtobufTransform2d createMessage() {
+      return ProtobufTransform2d.newInstance();
+    }
+
+    @Override
+    public Transform2d unpack(ProtobufTransform2d msg) {
+      return new Transform2d(
+          Translation2d.proto.unpack(msg.getTranslation()),
+          Rotation2d.proto.unpack(msg.getRotation()));
+    }
+
+    @Override
+    public void pack(ProtobufTransform2d msg, Transform2d value) {
+      Translation2d.proto.pack(msg.getMutableTranslation(), value.m_translation);
+      Rotation2d.proto.pack(msg.getMutableRotation(), value.m_rotation);
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform3d.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform3d.java
index 4920ef6..223a14b 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform3d.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform3d.java
@@ -4,9 +4,14 @@
 
 package edu.wpi.first.math.geometry;
 
+import edu.wpi.first.math.proto.Geometry3D.ProtobufTransform3d;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
 import java.util.Objects;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
-/** Represents a transformation for a Pose3d. */
+/** Represents a transformation for a Pose3d in the pose's frame. */
 public class Transform3d {
   private final Translation3d m_translation;
   private final Rotation3d m_rotation;
@@ -40,6 +45,19 @@
     m_rotation = rotation;
   }
 
+  /**
+   * Constructs a transform with x, y, and z translations instead of a separate Translation3d.
+   *
+   * @param x The x component of the translational component of the transform.
+   * @param y The y component of the translational component of the transform.
+   * @param z The z component of the translational component of the transform.
+   * @param rotation The rotational component of the transform.
+   */
+  public Transform3d(double x, double y, double z, Rotation3d rotation) {
+    m_translation = new Translation3d(x, y, z);
+    m_rotation = rotation;
+  }
+
   /** Constructs the identity transform -- maps an initial pose to itself. */
   public Transform3d() {
     m_translation = new Translation3d();
@@ -67,7 +85,8 @@
   }
 
   /**
-   * Composes two transformations.
+   * Composes two transformations. The second transform is applied relative to the orientation of
+   * the first.
    *
    * @param other The transform to compose with this one.
    * @return The composition of the two transformations.
@@ -159,4 +178,83 @@
   public int hashCode() {
     return Objects.hash(m_translation, m_rotation);
   }
+
+  public static final class AStruct implements Struct<Transform3d> {
+    @Override
+    public Class<Transform3d> getTypeClass() {
+      return Transform3d.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Transform3d";
+    }
+
+    @Override
+    public int getSize() {
+      return Translation3d.struct.getSize() + Rotation3d.struct.getSize();
+    }
+
+    @Override
+    public String getSchema() {
+      return "Translation3d translation;Rotation3d rotation";
+    }
+
+    @Override
+    public Struct<?>[] getNested() {
+      return new Struct<?>[] {Translation3d.struct, Rotation3d.struct};
+    }
+
+    @Override
+    public Transform3d unpack(ByteBuffer bb) {
+      Translation3d translation = Translation3d.struct.unpack(bb);
+      Rotation3d rotation = Rotation3d.struct.unpack(bb);
+      return new Transform3d(translation, rotation);
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Transform3d value) {
+      Translation3d.struct.pack(bb, value.m_translation);
+      Rotation3d.struct.pack(bb, value.m_rotation);
+    }
+  }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Transform3d, ProtobufTransform3d> {
+    @Override
+    public Class<Transform3d> getTypeClass() {
+      return Transform3d.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufTransform3d.getDescriptor();
+    }
+
+    @Override
+    public Protobuf<?, ?>[] getNested() {
+      return new Protobuf<?, ?>[] {Translation3d.proto, Rotation3d.proto};
+    }
+
+    @Override
+    public ProtobufTransform3d createMessage() {
+      return ProtobufTransform3d.newInstance();
+    }
+
+    @Override
+    public Transform3d unpack(ProtobufTransform3d msg) {
+      return new Transform3d(
+          Translation3d.proto.unpack(msg.getTranslation()),
+          Rotation3d.proto.unpack(msg.getRotation()));
+    }
+
+    @Override
+    public void pack(ProtobufTransform3d msg, Transform3d value) {
+      Translation3d.proto.pack(msg.getMutableTranslation(), value.m_translation);
+      Rotation3d.proto.pack(msg.getMutableRotation(), value.m_rotation);
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java
index 2d57edc..96e0001 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java
@@ -10,7 +10,15 @@
 import com.fasterxml.jackson.annotation.JsonProperty;
 import edu.wpi.first.math.MathUtil;
 import edu.wpi.first.math.interpolation.Interpolatable;
+import edu.wpi.first.math.proto.Geometry2D.ProtobufTranslation2d;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
 import java.util.Objects;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
 /**
  * Represents a translation in 2D space. This object can be used to represent a point or a vector.
@@ -185,6 +193,16 @@
     return new Translation2d(m_x / scalar, m_y / scalar);
   }
 
+  /**
+   * Returns the nearest Translation2d from a list of translations.
+   *
+   * @param translations The list of translations.
+   * @return The nearest Translation2d from the list.
+   */
+  public Translation2d nearest(List<Translation2d> translations) {
+    return Collections.min(translations, Comparator.comparing(this::getDistance));
+  }
+
   @Override
   public String toString() {
     return String.format("Translation2d(X: %.2f, Y: %.2f)", m_x, m_y);
@@ -216,4 +234,70 @@
         MathUtil.interpolate(this.getX(), endValue.getX(), t),
         MathUtil.interpolate(this.getY(), endValue.getY(), t));
   }
+
+  public static final class AStruct implements Struct<Translation2d> {
+    @Override
+    public Class<Translation2d> getTypeClass() {
+      return Translation2d.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Translation2d";
+    }
+
+    @Override
+    public int getSize() {
+      return kSizeDouble * 2;
+    }
+
+    @Override
+    public String getSchema() {
+      return "double x;double y";
+    }
+
+    @Override
+    public Translation2d unpack(ByteBuffer bb) {
+      double x = bb.getDouble();
+      double y = bb.getDouble();
+      return new Translation2d(x, y);
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Translation2d value) {
+      bb.putDouble(value.m_x);
+      bb.putDouble(value.m_y);
+    }
+  }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Translation2d, ProtobufTranslation2d> {
+    @Override
+    public Class<Translation2d> getTypeClass() {
+      return Translation2d.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufTranslation2d.getDescriptor();
+    }
+
+    @Override
+    public ProtobufTranslation2d createMessage() {
+      return ProtobufTranslation2d.newInstance();
+    }
+
+    @Override
+    public Translation2d unpack(ProtobufTranslation2d msg) {
+      return new Translation2d(msg.getX(), msg.getY());
+    }
+
+    @Override
+    public void pack(ProtobufTranslation2d msg, Translation2d value) {
+      msg.setX(value.m_x).setY(value.m_y);
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java
index 810f56c..bc55f65 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java
@@ -10,7 +10,12 @@
 import com.fasterxml.jackson.annotation.JsonProperty;
 import edu.wpi.first.math.MathUtil;
 import edu.wpi.first.math.interpolation.Interpolatable;
+import edu.wpi.first.math.proto.Geometry3D.ProtobufTranslation3d;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
 import java.util.Objects;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
 /**
  * Represents a translation in 3D space. This object can be used to represent a point or a vector.
@@ -231,4 +236,72 @@
         MathUtil.interpolate(this.getY(), endValue.getY(), t),
         MathUtil.interpolate(this.getZ(), endValue.getZ(), t));
   }
+
+  public static final class AStruct implements Struct<Translation3d> {
+    @Override
+    public Class<Translation3d> getTypeClass() {
+      return Translation3d.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Translation3d";
+    }
+
+    @Override
+    public int getSize() {
+      return kSizeDouble * 3;
+    }
+
+    @Override
+    public String getSchema() {
+      return "double x;double y;double z";
+    }
+
+    @Override
+    public Translation3d unpack(ByteBuffer bb) {
+      double x = bb.getDouble();
+      double y = bb.getDouble();
+      double z = bb.getDouble();
+      return new Translation3d(x, y, z);
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Translation3d value) {
+      bb.putDouble(value.m_x);
+      bb.putDouble(value.m_y);
+      bb.putDouble(value.m_z);
+    }
+  }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Translation3d, ProtobufTranslation3d> {
+    @Override
+    public Class<Translation3d> getTypeClass() {
+      return Translation3d.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufTranslation3d.getDescriptor();
+    }
+
+    @Override
+    public ProtobufTranslation3d createMessage() {
+      return ProtobufTranslation3d.newInstance();
+    }
+
+    @Override
+    public Translation3d unpack(ProtobufTranslation3d msg) {
+      return new Translation3d(msg.getX(), msg.getY(), msg.getZ());
+    }
+
+    @Override
+    public void pack(ProtobufTranslation3d msg, Translation3d value) {
+      msg.setX(value.m_x).setY(value.m_y).setZ(value.m_z);
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java
index be6831e..a4ae9f8 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java
@@ -4,11 +4,16 @@
 
 package edu.wpi.first.math.geometry;
 
+import edu.wpi.first.math.proto.Geometry2D.ProtobufTwist2d;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
 import java.util.Objects;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
 /**
  * A change in distance along a 2D arc since the last pose update. We can use ideas from
- * differential calculus to create new Pose2d objects from a Twist2d and vise versa.
+ * differential calculus to create new Pose2d objects from a Twist2d and vice versa.
  *
  * <p>A Twist can be used to represent a difference between two poses.
  */
@@ -62,4 +67,72 @@
   public int hashCode() {
     return Objects.hash(dx, dy, dtheta);
   }
+
+  public static final class AStruct implements Struct<Twist2d> {
+    @Override
+    public Class<Twist2d> getTypeClass() {
+      return Twist2d.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Twist2d";
+    }
+
+    @Override
+    public int getSize() {
+      return kSizeDouble * 3;
+    }
+
+    @Override
+    public String getSchema() {
+      return "double dx;double dy;double dtheta";
+    }
+
+    @Override
+    public Twist2d unpack(ByteBuffer bb) {
+      double dx = bb.getDouble();
+      double dy = bb.getDouble();
+      double dtheta = bb.getDouble();
+      return new Twist2d(dx, dy, dtheta);
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Twist2d value) {
+      bb.putDouble(value.dx);
+      bb.putDouble(value.dy);
+      bb.putDouble(value.dtheta);
+    }
+  }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Twist2d, ProtobufTwist2d> {
+    @Override
+    public Class<Twist2d> getTypeClass() {
+      return Twist2d.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufTwist2d.getDescriptor();
+    }
+
+    @Override
+    public ProtobufTwist2d createMessage() {
+      return ProtobufTwist2d.newInstance();
+    }
+
+    @Override
+    public Twist2d unpack(ProtobufTwist2d msg) {
+      return new Twist2d(msg.getDx(), msg.getDy(), msg.getDtheta());
+    }
+
+    @Override
+    public void pack(ProtobufTwist2d msg, Twist2d value) {
+      msg.setDx(value.dx).setDy(value.dy).setDtheta(value.dtheta);
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist3d.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist3d.java
index b78505e..d08d5cf 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist3d.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist3d.java
@@ -4,11 +4,16 @@
 
 package edu.wpi.first.math.geometry;
 
+import edu.wpi.first.math.proto.Geometry3D.ProtobufTwist3d;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
 import java.util.Objects;
+import us.hebi.quickbuf.Descriptors.Descriptor;
 
 /**
  * A change in distance along a 3D arc since the last pose update. We can use ideas from
- * differential calculus to create new Pose3d objects from a Twist3d and vise versa.
+ * differential calculus to create new Pose3d objects from a Twist3d and vice versa.
  *
  * <p>A Twist can be used to represent a difference between two poses.
  */
@@ -82,4 +87,84 @@
   public int hashCode() {
     return Objects.hash(dx, dy, dz, rx, ry, rz);
   }
+
+  public static final class AStruct implements Struct<Twist3d> {
+    @Override
+    public Class<Twist3d> getTypeClass() {
+      return Twist3d.class;
+    }
+
+    @Override
+    public String getTypeString() {
+      return "struct:Twist3d";
+    }
+
+    @Override
+    public int getSize() {
+      return kSizeDouble * 6;
+    }
+
+    @Override
+    public String getSchema() {
+      return "double dx;double dy;double dz;double rx;double ry;double rz";
+    }
+
+    @Override
+    public Twist3d unpack(ByteBuffer bb) {
+      double dx = bb.getDouble();
+      double dy = bb.getDouble();
+      double dz = bb.getDouble();
+      double rx = bb.getDouble();
+      double ry = bb.getDouble();
+      double rz = bb.getDouble();
+      return new Twist3d(dx, dy, dz, rx, ry, rz);
+    }
+
+    @Override
+    public void pack(ByteBuffer bb, Twist3d value) {
+      bb.putDouble(value.dx);
+      bb.putDouble(value.dy);
+      bb.putDouble(value.dz);
+      bb.putDouble(value.rx);
+      bb.putDouble(value.ry);
+      bb.putDouble(value.rz);
+    }
+  }
+
+  public static final AStruct struct = new AStruct();
+
+  public static final class AProto implements Protobuf<Twist3d, ProtobufTwist3d> {
+    @Override
+    public Class<Twist3d> getTypeClass() {
+      return Twist3d.class;
+    }
+
+    @Override
+    public Descriptor getDescriptor() {
+      return ProtobufTwist3d.getDescriptor();
+    }
+
+    @Override
+    public ProtobufTwist3d createMessage() {
+      return ProtobufTwist3d.newInstance();
+    }
+
+    @Override
+    public Twist3d unpack(ProtobufTwist3d msg) {
+      return new Twist3d(
+          msg.getDx(), msg.getDy(), msg.getDz(), msg.getRx(), msg.getRy(), msg.getRz());
+    }
+
+    @Override
+    public void pack(ProtobufTwist3d msg, Twist3d value) {
+      msg.setDx(value.dx)
+          .setDy(value.dy)
+          .setDz(value.dz)
+          .setRx(value.rx)
+          .setRy(value.ry)
+          .setRz(value.rz);
+    }
+  }
+
+  public static final AProto proto = new AProto();
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/InterpolatingDoubleTreeMap.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/InterpolatingDoubleTreeMap.java
new file mode 100644
index 0000000..bcd57a3
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/InterpolatingDoubleTreeMap.java
@@ -0,0 +1,15 @@
+// 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.
+
+package edu.wpi.first.math.interpolation;
+
+/**
+ * Interpolating Tree Maps are used to get values at points that are not defined by making a guess
+ * from points that are defined. This uses linear interpolation.
+ */
+public class InterpolatingDoubleTreeMap extends InterpolatingTreeMap<Double, Double> {
+  public InterpolatingDoubleTreeMap() {
+    super(InverseInterpolator.forDouble(), Interpolator.forDouble());
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/InterpolatingTreeMap.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/InterpolatingTreeMap.java
new file mode 100644
index 0000000..22eb232
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/InterpolatingTreeMap.java
@@ -0,0 +1,104 @@
+// 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.
+
+package edu.wpi.first.math.interpolation;
+
+import java.util.Comparator;
+import java.util.TreeMap;
+
+/**
+ * Interpolating Tree Maps are used to get values at points that are not defined by making a guess
+ * from points that are defined. This uses linear interpolation.
+ *
+ * <p>{@code K} must implement {@link Comparable}, or a {@link Comparator} on {@code K} can be
+ * provided.
+ *
+ * @param <K> The type of keys held in this map.
+ * @param <V> The type of values held in this map.
+ */
+public class InterpolatingTreeMap<K, V> {
+  private final TreeMap<K, V> m_map;
+
+  private final InverseInterpolator<K> m_inverseInterpolator;
+  private final Interpolator<V> m_interpolator;
+
+  /**
+   * Constructs an InterpolatingTreeMap.
+   *
+   * @param inverseInterpolator Function to use for inverse interpolation of the keys.
+   * @param interpolator Function to use for interpolation of the values.
+   */
+  public InterpolatingTreeMap(
+      InverseInterpolator<K> inverseInterpolator, Interpolator<V> interpolator) {
+    m_map = new TreeMap<>();
+    m_inverseInterpolator = inverseInterpolator;
+    m_interpolator = interpolator;
+  }
+
+  /**
+   * Constructs an InterpolatingTreeMap using {@code comparator}.
+   *
+   * @param inverseInterpolator Function to use for inverse interpolation of the keys.
+   * @param interpolator Function to use for interpolation of the values.
+   * @param comparator Comparator to use on keys.
+   */
+  public InterpolatingTreeMap(
+      InverseInterpolator<K> inverseInterpolator,
+      Interpolator<V> interpolator,
+      Comparator<K> comparator) {
+    m_inverseInterpolator = inverseInterpolator;
+    m_interpolator = interpolator;
+    m_map = new TreeMap<>(comparator);
+  }
+
+  /**
+   * Inserts a key-value pair.
+   *
+   * @param key The key.
+   * @param value The value.
+   */
+  public void put(K key, V value) {
+    m_map.put(key, value);
+  }
+
+  /**
+   * Returns the value associated with a given key.
+   *
+   * <p>If there's no matching key, the value returned will be an interpolation between the keys
+   * before and after the provided one, using the {@link Interpolator} and {@link
+   * InverseInterpolator} provided.
+   *
+   * @param key The key.
+   * @return The value associated with the given key.
+   */
+  public V get(K key) {
+    V val = m_map.get(key);
+    if (val == null) {
+      K ceilingKey = m_map.ceilingKey(key);
+      K floorKey = m_map.floorKey(key);
+
+      if (ceilingKey == null && floorKey == null) {
+        return null;
+      }
+      if (ceilingKey == null) {
+        return m_map.get(floorKey);
+      }
+      if (floorKey == null) {
+        return m_map.get(ceilingKey);
+      }
+      V floor = m_map.get(floorKey);
+      V ceiling = m_map.get(ceilingKey);
+
+      return m_interpolator.interpolate(
+          floor, ceiling, m_inverseInterpolator.inverseInterpolate(floorKey, ceilingKey, key));
+    } else {
+      return val;
+    }
+  }
+
+  /** Clears the contents. */
+  public void clear() {
+    m_map.clear();
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java
new file mode 100644
index 0000000..be6d8a2
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java
@@ -0,0 +1,30 @@
+// 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.
+
+package edu.wpi.first.math.interpolation;
+
+import edu.wpi.first.math.MathUtil;
+
+/**
+ * An interpolation function that returns a value interpolated between an upper and lower bound.
+ * This behavior can be linear or nonlinear.
+ *
+ * @param <T> The type that the {@link Interpolator} will operate on.
+ */
+@FunctionalInterface
+public interface Interpolator<T> {
+  /**
+   * Perform interpolation between two values.
+   *
+   * @param startValue The value to start at.
+   * @param endValue The value to end at.
+   * @param t How far between the two values to interpolate. This should be bounded to [0, 1].
+   * @return The interpolated value.
+   */
+  T interpolate(T startValue, T endValue, double t);
+
+  static Interpolator<Double> forDouble() {
+    return MathUtil::interpolate;
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java
new file mode 100644
index 0000000..8278af3
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java
@@ -0,0 +1,30 @@
+// 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.
+
+package edu.wpi.first.math.interpolation;
+
+import edu.wpi.first.math.MathUtil;
+
+/**
+ * An inverse interpolation function which determines where within an interpolation range an object
+ * lies. This behavior can be linear or nonlinear.
+ *
+ * @param <T> The type that the {@link InverseInterpolator} will operate on.
+ */
+@FunctionalInterface
+public interface InverseInterpolator<T> {
+  /**
+   * Return where within interpolation range [0, 1] q is between startValue and endValue.
+   *
+   * @param startValue Lower part of interpolation range.
+   * @param endValue Upper part of interpolation range.
+   * @param q Query.
+   * @return Interpolant in range [0, 1].
+   */
+  double inverseInterpolate(T startValue, T endValue, T q);
+
+  static InverseInterpolator<Double> forDouble() {
+    return MathUtil::inverseInterpolate;
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/TimeInterpolatableBuffer.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/TimeInterpolatableBuffer.java
index 7e0712d..f9f20c3 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/TimeInterpolatableBuffer.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/interpolation/TimeInterpolatableBuffer.java
@@ -19,11 +19,10 @@
  */
 public final class TimeInterpolatableBuffer<T> {
   private final double m_historySize;
-  private final InterpolateFunction<T> m_interpolatingFunc;
+  private final Interpolator<T> m_interpolatingFunc;
   private final NavigableMap<Double, T> m_pastSnapshots = new TreeMap<>();
 
-  private TimeInterpolatableBuffer(
-      InterpolateFunction<T> interpolateFunction, double historySizeSeconds) {
+  private TimeInterpolatableBuffer(Interpolator<T> interpolateFunction, double historySizeSeconds) {
     this.m_historySize = historySizeSeconds;
     this.m_interpolatingFunc = interpolateFunction;
   }
@@ -37,7 +36,7 @@
    * @return The new TimeInterpolatableBuffer.
    */
   public static <T> TimeInterpolatableBuffer<T> createBuffer(
-      InterpolateFunction<T> interpolateFunction, double historySizeSeconds) {
+      Interpolator<T> interpolateFunction, double historySizeSeconds) {
     return new TimeInterpolatableBuffer<>(interpolateFunction, historySizeSeconds);
   }
 
@@ -143,17 +142,4 @@
   public NavigableMap<Double, T> getInternalBuffer() {
     return m_pastSnapshots;
   }
-
-  public interface InterpolateFunction<T> {
-    /**
-     * Return the interpolated value. This object is assumed to be the starting position, or lower
-     * bound.
-     *
-     * @param start The lower bound, or start.
-     * @param end The upper bound, or end.
-     * @param t How far between the lower and upper bound we are. This should be bounded in [0, 1].
-     * @return The interpolated value.
-     */
-    T interpolate(T start, T end, double t);
-  }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java
index 6c98337..ffbce71 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java
@@ -4,23 +4,24 @@
 
 package edu.wpi.first.math.kinematics;
 
+import edu.wpi.first.math.geometry.Pose2d;
 import edu.wpi.first.math.geometry.Rotation2d;
+import edu.wpi.first.math.geometry.Translation2d;
 
 /**
- * Represents the speed of a robot chassis. Although this struct contains similar members compared
- * to a Twist2d, they do NOT represent the same thing. Whereas a Twist2d represents a change in pose
- * w.r.t to the robot frame of reference, this ChassisSpeeds struct represents a velocity w.r.t to
- * the robot frame of reference.
+ * Represents the speed of a robot chassis. Although this class contains similar members compared to
+ * a Twist2d, they do NOT represent the same thing. Whereas a Twist2d represents a change in pose
+ * w.r.t to the robot frame of reference, a ChassisSpeeds object represents a robot's velocity.
  *
  * <p>A strictly non-holonomic drivetrain, such as a differential drive, should never have a dy
  * component because it can never move sideways. Holonomic drivetrains such as swerve and mecanum
  * will often have all three components.
  */
 public class ChassisSpeeds {
-  /** Represents forward velocity w.r.t the robot frame of reference. (Fwd is +) */
+  /** Velocity along the x-axis. (Fwd is +) */
   public double vxMetersPerSecond;
 
-  /** Represents sideways velocity w.r.t the robot frame of reference. (Left is +) */
+  /** Velocity along the y-axis. (Left is +) */
   public double vyMetersPerSecond;
 
   /** Represents the angular velocity of the robot frame. (CCW is +) */
@@ -44,6 +45,60 @@
   }
 
   /**
+   * Discretizes a continuous-time chassis speed.
+   *
+   * <p>This function converts a continuous-time chassis speed into a discrete-time one such that
+   * when the discrete-time chassis speed is applied for one timestep, the robot moves as if the
+   * velocity components are independent (i.e., the robot moves v_x * dt along the x-axis, v_y * dt
+   * along the y-axis, and omega * dt around the z-axis).
+   *
+   * <p>This is useful for compensating for translational skew when translating and rotating a
+   * swerve drivetrain.
+   *
+   * @param vxMetersPerSecond Forward velocity.
+   * @param vyMetersPerSecond Sideways velocity.
+   * @param omegaRadiansPerSecond Angular velocity.
+   * @param dtSeconds The duration of the timestep the speeds should be applied for.
+   * @return Discretized ChassisSpeeds.
+   */
+  public static ChassisSpeeds discretize(
+      double vxMetersPerSecond,
+      double vyMetersPerSecond,
+      double omegaRadiansPerSecond,
+      double dtSeconds) {
+    var desiredDeltaPose =
+        new Pose2d(
+            vxMetersPerSecond * dtSeconds,
+            vyMetersPerSecond * dtSeconds,
+            new Rotation2d(omegaRadiansPerSecond * dtSeconds));
+    var twist = new Pose2d().log(desiredDeltaPose);
+    return new ChassisSpeeds(twist.dx / dtSeconds, twist.dy / dtSeconds, twist.dtheta / dtSeconds);
+  }
+
+  /**
+   * Discretizes a continuous-time chassis speed.
+   *
+   * <p>This function converts a continuous-time chassis speed into a discrete-time one such that
+   * when the discrete-time chassis speed is applied for one timestep, the robot moves as if the
+   * velocity components are independent (i.e., the robot moves v_x * dt along the x-axis, v_y * dt
+   * along the y-axis, and omega * dt around the z-axis).
+   *
+   * <p>This is useful for compensating for translational skew when translating and rotating a
+   * swerve drivetrain.
+   *
+   * @param continuousSpeeds The continuous speeds.
+   * @param dtSeconds The duration of the timestep the speeds should be applied for.
+   * @return Discretized ChassisSpeeds.
+   */
+  public static ChassisSpeeds discretize(ChassisSpeeds continuousSpeeds, double dtSeconds) {
+    return discretize(
+        continuousSpeeds.vxMetersPerSecond,
+        continuousSpeeds.vyMetersPerSecond,
+        continuousSpeeds.omegaRadiansPerSecond,
+        dtSeconds);
+  }
+
+  /**
    * Converts a user provided field-relative set of speeds into a robot-relative ChassisSpeeds
    * object.
    *
@@ -62,10 +117,10 @@
       double vyMetersPerSecond,
       double omegaRadiansPerSecond,
       Rotation2d robotAngle) {
-    return new ChassisSpeeds(
-        vxMetersPerSecond * robotAngle.getCos() + vyMetersPerSecond * robotAngle.getSin(),
-        -vxMetersPerSecond * robotAngle.getSin() + vyMetersPerSecond * robotAngle.getCos(),
-        omegaRadiansPerSecond);
+    // CW rotation into chassis frame
+    var rotated =
+        new Translation2d(vxMetersPerSecond, vyMetersPerSecond).rotateBy(robotAngle.unaryMinus());
+    return new ChassisSpeeds(rotated.getX(), rotated.getY(), omegaRadiansPerSecond);
   }
 
   /**
@@ -89,6 +144,119 @@
         robotAngle);
   }
 
+  /**
+   * Converts a user provided robot-relative set of speeds into a field-relative ChassisSpeeds
+   * object.
+   *
+   * @param vxMetersPerSecond The component of speed in the x direction relative to the robot.
+   *     Positive x is towards the robot's front.
+   * @param vyMetersPerSecond The component of speed in the y direction relative to the robot.
+   *     Positive y is towards the robot's left.
+   * @param omegaRadiansPerSecond The angular rate of the robot.
+   * @param robotAngle The angle of the robot as measured by a gyroscope. The robot's angle is
+   *     considered to be zero when it is facing directly away from your alliance station wall.
+   *     Remember that this should be CCW positive.
+   * @return ChassisSpeeds object representing the speeds in the field's frame of reference.
+   */
+  public static ChassisSpeeds fromRobotRelativeSpeeds(
+      double vxMetersPerSecond,
+      double vyMetersPerSecond,
+      double omegaRadiansPerSecond,
+      Rotation2d robotAngle) {
+    // CCW rotation out of chassis frame
+    var rotated = new Translation2d(vxMetersPerSecond, vyMetersPerSecond).rotateBy(robotAngle);
+    return new ChassisSpeeds(rotated.getX(), rotated.getY(), omegaRadiansPerSecond);
+  }
+
+  /**
+   * Converts a user provided robot-relative ChassisSpeeds object into a field-relative
+   * ChassisSpeeds object.
+   *
+   * @param robotRelativeSpeeds The ChassisSpeeds object representing the speeds in the robot frame
+   *     of reference. Positive x is towards the robot's front. Positive y is towards the robot's
+   *     left.
+   * @param robotAngle The angle of the robot as measured by a gyroscope. The robot's angle is
+   *     considered to be zero when it is facing directly away from your alliance station wall.
+   *     Remember that this should be CCW positive.
+   * @return ChassisSpeeds object representing the speeds in the field's frame of reference.
+   */
+  public static ChassisSpeeds fromRobotRelativeSpeeds(
+      ChassisSpeeds robotRelativeSpeeds, Rotation2d robotAngle) {
+    return fromRobotRelativeSpeeds(
+        robotRelativeSpeeds.vxMetersPerSecond,
+        robotRelativeSpeeds.vyMetersPerSecond,
+        robotRelativeSpeeds.omegaRadiansPerSecond,
+        robotAngle);
+  }
+
+  /**
+   * Adds two ChassisSpeeds and returns the sum.
+   *
+   * <p>For example, ChassisSpeeds{1.0, 0.5, 0.75} + ChassisSpeeds{2.0, 1.5, 0.25} =
+   * ChassisSpeeds{3.0, 2.0, 1.0}
+   *
+   * @param other The ChassisSpeeds to add.
+   * @return The sum of the ChassisSpeeds.
+   */
+  public ChassisSpeeds plus(ChassisSpeeds other) {
+    return new ChassisSpeeds(
+        vxMetersPerSecond + other.vxMetersPerSecond,
+        vyMetersPerSecond + other.vyMetersPerSecond,
+        omegaRadiansPerSecond + other.omegaRadiansPerSecond);
+  }
+
+  /**
+   * Subtracts the other ChassisSpeeds from the current ChassisSpeeds and returns the difference.
+   *
+   * <p>For example, ChassisSpeeds{5.0, 4.0, 2.0} - ChassisSpeeds{1.0, 2.0, 1.0} =
+   * ChassisSpeeds{4.0, 2.0, 1.0}
+   *
+   * @param other The ChassisSpeeds to subtract.
+   * @return The difference between the two ChassisSpeeds.
+   */
+  public ChassisSpeeds minus(ChassisSpeeds other) {
+    return new ChassisSpeeds(
+        vxMetersPerSecond - other.vxMetersPerSecond,
+        vyMetersPerSecond - other.vyMetersPerSecond,
+        omegaRadiansPerSecond - other.omegaRadiansPerSecond);
+  }
+
+  /**
+   * Returns the inverse of the current ChassisSpeeds. This is equivalent to negating all components
+   * of the ChassisSpeeds.
+   *
+   * @return The inverse of the current ChassisSpeeds.
+   */
+  public ChassisSpeeds unaryMinus() {
+    return new ChassisSpeeds(-vxMetersPerSecond, -vyMetersPerSecond, -omegaRadiansPerSecond);
+  }
+
+  /**
+   * Multiplies the ChassisSpeeds by a scalar and returns the new ChassisSpeeds.
+   *
+   * <p>For example, ChassisSpeeds{2.0, 2.5, 1.0} * 2 = ChassisSpeeds{4.0, 5.0, 1.0}
+   *
+   * @param scalar The scalar to multiply by.
+   * @return The scaled ChassisSpeeds.
+   */
+  public ChassisSpeeds times(double scalar) {
+    return new ChassisSpeeds(
+        vxMetersPerSecond * scalar, vyMetersPerSecond * scalar, omegaRadiansPerSecond * scalar);
+  }
+
+  /**
+   * Divides the ChassisSpeeds by a scalar and returns the new ChassisSpeeds.
+   *
+   * <p>For example, ChassisSpeeds{2.0, 2.5, 1.0} / 2 = ChassisSpeeds{1.0, 1.25, 0.5}
+   *
+   * @param scalar The scalar to divide by.
+   * @return The scaled ChassisSpeeds.
+   */
+  public ChassisSpeeds div(double scalar) {
+    return new ChassisSpeeds(
+        vxMetersPerSecond / scalar, vyMetersPerSecond / scalar, omegaRadiansPerSecond / scalar);
+  }
+
   @Override
   public String toString() {
     return String.format(
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java
index 0dfb016..a1fb2db 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java
@@ -16,7 +16,8 @@
  * whereas forward kinematics converts left and right component velocities into a linear and angular
  * chassis speed.
  */
-public class DifferentialDriveKinematics {
+public class DifferentialDriveKinematics
+    implements Kinematics<DifferentialDriveWheelSpeeds, DifferentialDriveWheelPositions> {
   public final double trackWidthMeters;
 
   /**
@@ -37,6 +38,7 @@
    * @param wheelSpeeds The left and right velocities.
    * @return The chassis speed.
    */
+  @Override
   public ChassisSpeeds toChassisSpeeds(DifferentialDriveWheelSpeeds wheelSpeeds) {
     return new ChassisSpeeds(
         (wheelSpeeds.leftMetersPerSecond + wheelSpeeds.rightMetersPerSecond) / 2,
@@ -51,6 +53,7 @@
    *     chassis' speed.
    * @return The left and right velocities.
    */
+  @Override
   public DifferentialDriveWheelSpeeds toWheelSpeeds(ChassisSpeeds chassisSpeeds) {
     return new DifferentialDriveWheelSpeeds(
         chassisSpeeds.vxMetersPerSecond
@@ -59,6 +62,12 @@
             + trackWidthMeters / 2 * chassisSpeeds.omegaRadiansPerSecond);
   }
 
+  @Override
+  public Twist2d toTwist2d(
+      DifferentialDriveWheelPositions start, DifferentialDriveWheelPositions end) {
+    return toTwist2d(end.leftMeters - start.leftMeters, end.rightMeters - start.rightMeters);
+  }
+
   /**
    * Performs forward kinematics to return the resulting Twist2d from the given left and right side
    * distance deltas. This method is often used for odometry -- determining the robot's position on
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveOdometry.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveOdometry.java
index e8f97f5..db5e8a3 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveOdometry.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveOdometry.java
@@ -8,7 +8,6 @@
 import edu.wpi.first.math.MathUsageId;
 import edu.wpi.first.math.geometry.Pose2d;
 import edu.wpi.first.math.geometry.Rotation2d;
-import edu.wpi.first.math.geometry.Twist2d;
 
 /**
  * Class for differential drive odometry. Odometry allows you to track the robot's position on the
@@ -20,15 +19,7 @@
  * <p>It is important that you reset your encoders to zero before using this class. Any subsequent
  * pose resets also require the encoders to be reset to zero.
  */
-public class DifferentialDriveOdometry {
-  private Pose2d m_poseMeters;
-
-  private Rotation2d m_gyroOffset;
-  private Rotation2d m_previousAngle;
-
-  private double m_prevLeftDistance;
-  private double m_prevRightDistance;
-
+public class DifferentialDriveOdometry extends Odometry<DifferentialDriveWheelPositions> {
   /**
    * Constructs a DifferentialDriveOdometry object.
    *
@@ -42,13 +33,11 @@
       double leftDistanceMeters,
       double rightDistanceMeters,
       Pose2d initialPoseMeters) {
-    m_poseMeters = initialPoseMeters;
-    m_gyroOffset = m_poseMeters.getRotation().minus(gyroAngle);
-    m_previousAngle = initialPoseMeters.getRotation();
-
-    m_prevLeftDistance = leftDistanceMeters;
-    m_prevRightDistance = rightDistanceMeters;
-
+    super(
+        new DifferentialDriveKinematics(1),
+        gyroAngle,
+        new DifferentialDriveWheelPositions(leftDistanceMeters, rightDistanceMeters),
+        initialPoseMeters);
     MathSharedStore.reportUsage(MathUsageId.kOdometry_DifferentialDrive, 1);
   }
 
@@ -80,21 +69,10 @@
       double leftDistanceMeters,
       double rightDistanceMeters,
       Pose2d poseMeters) {
-    m_poseMeters = poseMeters;
-    m_previousAngle = poseMeters.getRotation();
-    m_gyroOffset = m_poseMeters.getRotation().minus(gyroAngle);
-
-    m_prevLeftDistance = leftDistanceMeters;
-    m_prevRightDistance = rightDistanceMeters;
-  }
-
-  /**
-   * Returns the position of the robot on the field.
-   *
-   * @return The pose of the robot (x and y are in meters).
-   */
-  public Pose2d getPoseMeters() {
-    return m_poseMeters;
+    super.resetPosition(
+        gyroAngle,
+        new DifferentialDriveWheelPositions(leftDistanceMeters, rightDistanceMeters),
+        poseMeters);
   }
 
   /**
@@ -109,22 +87,7 @@
    */
   public Pose2d update(
       Rotation2d gyroAngle, double leftDistanceMeters, double rightDistanceMeters) {
-    double deltaLeftDistance = leftDistanceMeters - m_prevLeftDistance;
-    double deltaRightDistance = rightDistanceMeters - m_prevRightDistance;
-
-    m_prevLeftDistance = leftDistanceMeters;
-    m_prevRightDistance = rightDistanceMeters;
-
-    double averageDeltaDistance = (deltaLeftDistance + deltaRightDistance) / 2.0;
-    var angle = gyroAngle.plus(m_gyroOffset);
-
-    var newPose =
-        m_poseMeters.exp(
-            new Twist2d(averageDeltaDistance, 0.0, angle.minus(m_previousAngle).getRadians()));
-
-    m_previousAngle = angle;
-
-    m_poseMeters = new Pose2d(newPose.getTranslation(), angle);
-    return m_poseMeters;
+    return super.update(
+        gyroAngle, new DifferentialDriveWheelPositions(leftDistanceMeters, rightDistanceMeters));
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java
new file mode 100644
index 0000000..18866af
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java
@@ -0,0 +1,62 @@
+// 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.
+
+package edu.wpi.first.math.kinematics;
+
+import edu.wpi.first.math.MathUtil;
+import java.util.Objects;
+
+public class DifferentialDriveWheelPositions
+    implements WheelPositions<DifferentialDriveWheelPositions> {
+  /** Distance measured by the left side. */
+  public double leftMeters;
+
+  /** Distance measured by the right side. */
+  public double rightMeters;
+
+  /**
+   * Constructs a DifferentialDriveWheelPositions.
+   *
+   * @param leftMeters Distance measured by the left side.
+   * @param rightMeters Distance measured by the right side.
+   */
+  public DifferentialDriveWheelPositions(double leftMeters, double rightMeters) {
+    this.leftMeters = leftMeters;
+    this.rightMeters = rightMeters;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof DifferentialDriveWheelPositions) {
+      DifferentialDriveWheelPositions other = (DifferentialDriveWheelPositions) obj;
+      return Math.abs(other.leftMeters - leftMeters) < 1E-9
+          && Math.abs(other.rightMeters - rightMeters) < 1E-9;
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(leftMeters, rightMeters);
+  }
+
+  @Override
+  public String toString() {
+    return String.format(
+        "DifferentialDriveWheelPositions(Left: %.2f m, Right: %.2f m", leftMeters, rightMeters);
+  }
+
+  @Override
+  public DifferentialDriveWheelPositions copy() {
+    return new DifferentialDriveWheelPositions(leftMeters, rightMeters);
+  }
+
+  @Override
+  public DifferentialDriveWheelPositions interpolate(
+      DifferentialDriveWheelPositions endValue, double t) {
+    return new DifferentialDriveWheelPositions(
+        MathUtil.interpolate(this.leftMeters, endValue.leftMeters, t),
+        MathUtil.interpolate(this.rightMeters, endValue.rightMeters, t));
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java
index d4b235e..ec874b6 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java
@@ -46,6 +46,77 @@
     }
   }
 
+  /**
+   * Adds two DifferentialDriveWheelSpeeds and returns the sum.
+   *
+   * <p>For example, DifferentialDriveWheelSpeeds{1.0, 0.5} + DifferentialDriveWheelSpeeds{2.0, 1.5}
+   * = DifferentialDriveWheelSpeeds{3.0, 2.0}
+   *
+   * @param other The DifferentialDriveWheelSpeeds to add.
+   * @return The sum of the DifferentialDriveWheelSpeeds.
+   */
+  public DifferentialDriveWheelSpeeds plus(DifferentialDriveWheelSpeeds other) {
+    return new DifferentialDriveWheelSpeeds(
+        leftMetersPerSecond + other.leftMetersPerSecond,
+        rightMetersPerSecond + other.rightMetersPerSecond);
+  }
+
+  /**
+   * Subtracts the other DifferentialDriveWheelSpeeds from the current DifferentialDriveWheelSpeeds
+   * and returns the difference.
+   *
+   * <p>For example, DifferentialDriveWheelSpeeds{5.0, 4.0} - DifferentialDriveWheelSpeeds{1.0, 2.0}
+   * = DifferentialDriveWheelSpeeds{4.0, 2.0}
+   *
+   * @param other The DifferentialDriveWheelSpeeds to subtract.
+   * @return The difference between the two DifferentialDriveWheelSpeeds.
+   */
+  public DifferentialDriveWheelSpeeds minus(DifferentialDriveWheelSpeeds other) {
+    return new DifferentialDriveWheelSpeeds(
+        leftMetersPerSecond - other.leftMetersPerSecond,
+        rightMetersPerSecond - other.rightMetersPerSecond);
+  }
+
+  /**
+   * Returns the inverse of the current DifferentialDriveWheelSpeeds. This is equivalent to negating
+   * all components of the DifferentialDriveWheelSpeeds.
+   *
+   * @return The inverse of the current DifferentialDriveWheelSpeeds.
+   */
+  public DifferentialDriveWheelSpeeds unaryMinus() {
+    return new DifferentialDriveWheelSpeeds(-leftMetersPerSecond, -rightMetersPerSecond);
+  }
+
+  /**
+   * Multiplies the DifferentialDriveWheelSpeeds by a scalar and returns the new
+   * DifferentialDriveWheelSpeeds.
+   *
+   * <p>For example, DifferentialDriveWheelSpeeds{2.0, 2.5} * 2 = DifferentialDriveWheelSpeeds{4.0,
+   * 5.0}
+   *
+   * @param scalar The scalar to multiply by.
+   * @return The scaled DifferentialDriveWheelSpeeds.
+   */
+  public DifferentialDriveWheelSpeeds times(double scalar) {
+    return new DifferentialDriveWheelSpeeds(
+        leftMetersPerSecond * scalar, rightMetersPerSecond * scalar);
+  }
+
+  /**
+   * Divides the DifferentialDriveWheelSpeeds by a scalar and returns the new
+   * DifferentialDriveWheelSpeeds.
+   *
+   * <p>For example, DifferentialDriveWheelSpeeds{2.0, 2.5} / 2 = DifferentialDriveWheelSpeeds{1.0,
+   * 1.25}
+   *
+   * @param scalar The scalar to divide by.
+   * @return The scaled DifferentialDriveWheelSpeeds.
+   */
+  public DifferentialDriveWheelSpeeds div(double scalar) {
+    return new DifferentialDriveWheelSpeeds(
+        leftMetersPerSecond / scalar, rightMetersPerSecond / scalar);
+  }
+
   @Override
   public String toString() {
     return String.format(
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/Kinematics.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/Kinematics.java
new file mode 100644
index 0000000..35e2642
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/Kinematics.java
@@ -0,0 +1,47 @@
+// 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.
+
+package edu.wpi.first.math.kinematics;
+
+import edu.wpi.first.math.geometry.Twist2d;
+
+/**
+ * Helper class that converts a chassis velocity (dx and dtheta components) into wheel speeds. Robot
+ * code should not use this directly- Instead, use the particular type for your drivetrain (e.g.,
+ * {@link DifferentialDriveKinematics}).
+ *
+ * @param <S> The type of the wheel speeds.
+ * @param <P> The type of the wheel positions.
+ */
+public interface Kinematics<S, P> {
+  /**
+   * Performs forward kinematics to return the resulting chassis speed from the wheel speeds. This
+   * method is often used for odometry -- determining the robot's position on the field using data
+   * from the real-world speed of each wheel on the robot.
+   *
+   * @param wheelSpeeds The speeds of the wheels.
+   * @return The chassis speed.
+   */
+  ChassisSpeeds toChassisSpeeds(S wheelSpeeds);
+
+  /**
+   * Performs inverse kinematics to return the wheel speeds from a desired chassis velocity. This
+   * method is often used to convert joystick values into wheel speeds.
+   *
+   * @param chassisSpeeds The desired chassis speed.
+   * @return The wheel speeds.
+   */
+  S toWheelSpeeds(ChassisSpeeds chassisSpeeds);
+
+  /**
+   * Performs forward kinematics to return the resulting Twist2d from the given change in wheel
+   * positions. This method is often used for odometry -- determining the robot's position on the
+   * field using changes in the distance driven by each wheel on the robot.
+   *
+   * @param start The starting distances driven by the wheels.
+   * @param end The ending distances driven by the wheels.
+   * @return The resulting Twist2d in the robot's movement.
+   */
+  Twist2d toTwist2d(P start, P end);
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java
index 23204d4..76c857a 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java
@@ -30,7 +30,8 @@
  * <p>Forward kinematics is also used for odometry -- determining the position of the robot on the
  * field using encoders and a gyro.
  */
-public class MecanumDriveKinematics {
+public class MecanumDriveKinematics
+    implements Kinematics<MecanumDriveWheelSpeeds, MecanumDriveWheelPositions> {
   private final SimpleMatrix m_inverseKinematics;
   private final SimpleMatrix m_forwardKinematics;
 
@@ -125,6 +126,7 @@
    * @param chassisSpeeds The desired chassis speed.
    * @return The wheel speeds.
    */
+  @Override
   public MecanumDriveWheelSpeeds toWheelSpeeds(ChassisSpeeds chassisSpeeds) {
     return toWheelSpeeds(chassisSpeeds, new Translation2d());
   }
@@ -137,6 +139,7 @@
    * @param wheelSpeeds The current mecanum drive wheel speeds.
    * @return The resulting chassis speed.
    */
+  @Override
   public ChassisSpeeds toChassisSpeeds(MecanumDriveWheelSpeeds wheelSpeeds) {
     var wheelSpeedsVector = new SimpleMatrix(4, 1);
     wheelSpeedsVector.setColumn(
@@ -154,6 +157,20 @@
         chassisSpeedsVector.get(2, 0));
   }
 
+  @Override
+  public Twist2d toTwist2d(MecanumDriveWheelPositions start, MecanumDriveWheelPositions end) {
+    var wheelDeltasVector = new SimpleMatrix(4, 1);
+    wheelDeltasVector.setColumn(
+        0,
+        0,
+        end.frontLeftMeters - start.frontLeftMeters,
+        end.frontRightMeters - start.frontRightMeters,
+        end.rearLeftMeters - start.rearLeftMeters,
+        end.rearRightMeters - start.rearRightMeters);
+    var twist = m_forwardKinematics.mult(wheelDeltasVector);
+    return new Twist2d(twist.get(0, 0), twist.get(1, 0), twist.get(2, 0));
+  }
+
   /**
    * Performs forward kinematics to return the resulting Twist2d from the given wheel deltas. This
    * method is often used for odometry -- determining the robot's position on the field using
@@ -172,7 +189,6 @@
         wheelDeltas.rearLeftMeters,
         wheelDeltas.rearRightMeters);
     var twist = m_forwardKinematics.mult(wheelDeltasVector);
-
     return new Twist2d(twist.get(0, 0), twist.get(1, 0), twist.get(2, 0));
   }
 
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveOdometry.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveOdometry.java
index 40a77e2..32bc9bf 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveOdometry.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveOdometry.java
@@ -16,14 +16,7 @@
  * <p>Teams can use odometry during the autonomous period for complex tasks like path following.
  * Furthermore, odometry can be used for latency compensation when using computer-vision systems.
  */
-public class MecanumDriveOdometry {
-  private final MecanumDriveKinematics m_kinematics;
-  private Pose2d m_poseMeters;
-  private MecanumDriveWheelPositions m_previousWheelPositions;
-
-  private Rotation2d m_gyroOffset;
-  private Rotation2d m_previousAngle;
-
+public class MecanumDriveOdometry extends Odometry<MecanumDriveWheelPositions> {
   /**
    * Constructs a MecanumDriveOdometry object.
    *
@@ -37,16 +30,7 @@
       Rotation2d gyroAngle,
       MecanumDriveWheelPositions wheelPositions,
       Pose2d initialPoseMeters) {
-    m_kinematics = kinematics;
-    m_poseMeters = initialPoseMeters;
-    m_gyroOffset = m_poseMeters.getRotation().minus(gyroAngle);
-    m_previousAngle = initialPoseMeters.getRotation();
-    m_previousWheelPositions =
-        new MecanumDriveWheelPositions(
-            wheelPositions.frontLeftMeters,
-            wheelPositions.frontRightMeters,
-            wheelPositions.rearLeftMeters,
-            wheelPositions.rearRightMeters);
+    super(kinematics, gyroAngle, wheelPositions, initialPoseMeters);
     MathSharedStore.reportUsage(MathUsageId.kOdometry_MecanumDrive, 1);
   }
 
@@ -63,72 +47,4 @@
       MecanumDriveWheelPositions wheelPositions) {
     this(kinematics, gyroAngle, wheelPositions, new Pose2d());
   }
-
-  /**
-   * Resets the robot's position on the field.
-   *
-   * <p>The gyroscope angle does not need to be reset here on the user's robot code. The library
-   * automatically takes care of offsetting the gyro angle.
-   *
-   * @param gyroAngle The angle reported by the gyroscope.
-   * @param wheelPositions The distances driven by each wheel.
-   * @param poseMeters The position on the field that your robot is at.
-   */
-  public void resetPosition(
-      Rotation2d gyroAngle, MecanumDriveWheelPositions wheelPositions, Pose2d poseMeters) {
-    m_poseMeters = poseMeters;
-    m_previousAngle = poseMeters.getRotation();
-    m_gyroOffset = m_poseMeters.getRotation().minus(gyroAngle);
-    m_previousWheelPositions =
-        new MecanumDriveWheelPositions(
-            wheelPositions.frontLeftMeters,
-            wheelPositions.frontRightMeters,
-            wheelPositions.rearLeftMeters,
-            wheelPositions.rearRightMeters);
-  }
-
-  /**
-   * Returns the position of the robot on the field.
-   *
-   * @return The pose of the robot (x and y are in meters).
-   */
-  public Pose2d getPoseMeters() {
-    return m_poseMeters;
-  }
-
-  /**
-   * Updates the robot's position on the field using forward kinematics and integration of the pose
-   * over time. This method takes in an angle parameter which is used instead of the angular rate
-   * that is calculated from forward kinematics, in addition to the current distance measurement at
-   * each wheel.
-   *
-   * @param gyroAngle The angle reported by the gyroscope.
-   * @param wheelPositions The distances driven by each wheel.
-   * @return The new pose of the robot.
-   */
-  public Pose2d update(Rotation2d gyroAngle, MecanumDriveWheelPositions wheelPositions) {
-    var angle = gyroAngle.plus(m_gyroOffset);
-
-    var wheelDeltas =
-        new MecanumDriveWheelPositions(
-            wheelPositions.frontLeftMeters - m_previousWheelPositions.frontLeftMeters,
-            wheelPositions.frontRightMeters - m_previousWheelPositions.frontRightMeters,
-            wheelPositions.rearLeftMeters - m_previousWheelPositions.rearLeftMeters,
-            wheelPositions.rearRightMeters - m_previousWheelPositions.rearRightMeters);
-
-    var twist = m_kinematics.toTwist2d(wheelDeltas);
-    twist.dtheta = angle.minus(m_previousAngle).getRadians();
-    var newPose = m_poseMeters.exp(twist);
-
-    m_previousAngle = angle;
-    m_poseMeters = new Pose2d(newPose.getTranslation(), angle);
-    m_previousWheelPositions =
-        new MecanumDriveWheelPositions(
-            wheelPositions.frontLeftMeters,
-            wheelPositions.frontRightMeters,
-            wheelPositions.rearLeftMeters,
-            wheelPositions.rearRightMeters);
-
-    return m_poseMeters;
-  }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java
index 9ff3341..2b284cb 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java
@@ -4,7 +4,10 @@
 
 package edu.wpi.first.math.kinematics;
 
-public class MecanumDriveWheelPositions {
+import edu.wpi.first.math.MathUtil;
+import java.util.Objects;
+
+public class MecanumDriveWheelPositions implements WheelPositions<MecanumDriveWheelPositions> {
   /** Distance measured by the front left wheel. */
   public double frontLeftMeters;
 
@@ -40,10 +43,42 @@
   }
 
   @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof MecanumDriveWheelPositions) {
+      MecanumDriveWheelPositions other = (MecanumDriveWheelPositions) obj;
+      return Math.abs(other.frontLeftMeters - frontLeftMeters) < 1E-9
+          && Math.abs(other.frontRightMeters - frontRightMeters) < 1E-9
+          && Math.abs(other.rearLeftMeters - rearLeftMeters) < 1E-9
+          && Math.abs(other.rearRightMeters - rearRightMeters) < 1E-9;
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(frontLeftMeters, frontRightMeters, rearLeftMeters, rearRightMeters);
+  }
+
+  @Override
   public String toString() {
     return String.format(
         "MecanumDriveWheelPositions(Front Left: %.2f m, Front Right: %.2f m, "
             + "Rear Left: %.2f m, Rear Right: %.2f m)",
         frontLeftMeters, frontRightMeters, rearLeftMeters, rearRightMeters);
   }
+
+  @Override
+  public MecanumDriveWheelPositions copy() {
+    return new MecanumDriveWheelPositions(
+        frontLeftMeters, frontRightMeters, rearLeftMeters, rearRightMeters);
+  }
+
+  @Override
+  public MecanumDriveWheelPositions interpolate(MecanumDriveWheelPositions endValue, double t) {
+    return new MecanumDriveWheelPositions(
+        MathUtil.interpolate(this.frontLeftMeters, endValue.frontLeftMeters, t),
+        MathUtil.interpolate(this.frontRightMeters, endValue.frontRightMeters, t),
+        MathUtil.interpolate(this.rearLeftMeters, endValue.rearLeftMeters, t),
+        MathUtil.interpolate(this.rearRightMeters, endValue.rearRightMeters, t));
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java
index 1dcfc85..63cef18 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java
@@ -73,6 +73,89 @@
     }
   }
 
+  /**
+   * Adds two MecanumDriveWheelSpeeds and returns the sum.
+   *
+   * <p>For example, MecanumDriveWheelSpeeds{1.0, 0.5, 2.0, 1.5} + MecanumDriveWheelSpeeds{2.0, 1.5,
+   * 0.5, 1.0} = MecanumDriveWheelSpeeds{3.0, 2.0, 2.5, 2.5}
+   *
+   * @param other The MecanumDriveWheelSpeeds to add.
+   * @return The sum of the MecanumDriveWheelSpeeds.
+   */
+  public MecanumDriveWheelSpeeds plus(MecanumDriveWheelSpeeds other) {
+    return new MecanumDriveWheelSpeeds(
+        frontLeftMetersPerSecond + other.frontLeftMetersPerSecond,
+        frontRightMetersPerSecond + other.frontRightMetersPerSecond,
+        rearLeftMetersPerSecond + other.rearLeftMetersPerSecond,
+        rearRightMetersPerSecond + other.rearRightMetersPerSecond);
+  }
+
+  /**
+   * Subtracts the other MecanumDriveWheelSpeeds from the current MecanumDriveWheelSpeeds and
+   * returns the difference.
+   *
+   * <p>For example, MecanumDriveWheelSpeeds{5.0, 4.0, 6.0, 2.5} - MecanumDriveWheelSpeeds{1.0, 2.0,
+   * 3.0, 0.5} = MecanumDriveWheelSpeeds{4.0, 2.0, 3.0, 2.0}
+   *
+   * @param other The MecanumDriveWheelSpeeds to subtract.
+   * @return The difference between the two MecanumDriveWheelSpeeds.
+   */
+  public MecanumDriveWheelSpeeds minus(MecanumDriveWheelSpeeds other) {
+    return new MecanumDriveWheelSpeeds(
+        frontLeftMetersPerSecond - other.frontLeftMetersPerSecond,
+        frontRightMetersPerSecond - other.frontRightMetersPerSecond,
+        rearLeftMetersPerSecond - other.rearLeftMetersPerSecond,
+        rearRightMetersPerSecond - other.rearRightMetersPerSecond);
+  }
+
+  /**
+   * Returns the inverse of the current MecanumDriveWheelSpeeds. This is equivalent to negating all
+   * components of the MecanumDriveWheelSpeeds.
+   *
+   * @return The inverse of the current MecanumDriveWheelSpeeds.
+   */
+  public MecanumDriveWheelSpeeds unaryMinus() {
+    return new MecanumDriveWheelSpeeds(
+        -frontLeftMetersPerSecond,
+        -frontRightMetersPerSecond,
+        -rearLeftMetersPerSecond,
+        -rearRightMetersPerSecond);
+  }
+
+  /**
+   * Multiplies the MecanumDriveWheelSpeeds by a scalar and returns the new MecanumDriveWheelSpeeds.
+   *
+   * <p>For example, MecanumDriveWheelSpeeds{2.0, 2.5, 3.0, 3.5} * 2 = MecanumDriveWheelSpeeds{4.0,
+   * 5.0, 6.0, 7.0}
+   *
+   * @param scalar The scalar to multiply by.
+   * @return The scaled MecanumDriveWheelSpeeds.
+   */
+  public MecanumDriveWheelSpeeds times(double scalar) {
+    return new MecanumDriveWheelSpeeds(
+        frontLeftMetersPerSecond * scalar,
+        frontRightMetersPerSecond * scalar,
+        rearLeftMetersPerSecond * scalar,
+        rearRightMetersPerSecond * scalar);
+  }
+
+  /**
+   * Divides the MecanumDriveWheelSpeeds by a scalar and returns the new MecanumDriveWheelSpeeds.
+   *
+   * <p>For example, MecanumDriveWheelSpeeds{2.0, 2.5, 1.5, 1.0} / 2 = MecanumDriveWheelSpeeds{1.0,
+   * 1.25, 0.75, 0.5}
+   *
+   * @param scalar The scalar to divide by.
+   * @return The scaled MecanumDriveWheelSpeeds.
+   */
+  public MecanumDriveWheelSpeeds div(double scalar) {
+    return new MecanumDriveWheelSpeeds(
+        frontLeftMetersPerSecond / scalar,
+        frontRightMetersPerSecond / scalar,
+        rearLeftMetersPerSecond / scalar,
+        rearRightMetersPerSecond / scalar);
+  }
+
   @Override
   public String toString() {
     return String.format(
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/Odometry.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/Odometry.java
new file mode 100644
index 0000000..b2e5054
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/Odometry.java
@@ -0,0 +1,97 @@
+// 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.
+
+package edu.wpi.first.math.kinematics;
+
+import edu.wpi.first.math.geometry.Pose2d;
+import edu.wpi.first.math.geometry.Rotation2d;
+
+/**
+ * Class for odometry. Robot code should not use this directly- Instead, use the particular type for
+ * your drivetrain (e.g., {@link DifferentialDriveOdometry}). Odometry allows you to track the
+ * robot's position on the field over the course of a match using readings from encoders and a
+ * gyroscope.
+ *
+ * <p>Teams can use odometry during the autonomous period for complex tasks like path following.
+ * Furthermore, odometry can be used for latency compensation when using computer-vision systems.
+ */
+public class Odometry<T extends WheelPositions<T>> {
+  private final Kinematics<?, T> m_kinematics;
+  private Pose2d m_poseMeters;
+
+  private Rotation2d m_gyroOffset;
+  private Rotation2d m_previousAngle;
+  private T m_previousWheelPositions;
+
+  /**
+   * Constructs an Odometry object.
+   *
+   * @param kinematics The kinematics of the drivebase.
+   * @param gyroAngle The angle reported by the gyroscope.
+   * @param wheelPositions The current encoder readings.
+   * @param initialPoseMeters The starting position of the robot on the field.
+   */
+  public Odometry(
+      Kinematics<?, T> kinematics,
+      Rotation2d gyroAngle,
+      T wheelPositions,
+      Pose2d initialPoseMeters) {
+    m_kinematics = kinematics;
+    m_poseMeters = initialPoseMeters;
+    m_gyroOffset = m_poseMeters.getRotation().minus(gyroAngle);
+    m_previousAngle = m_poseMeters.getRotation();
+    m_previousWheelPositions = wheelPositions.copy();
+  }
+
+  /**
+   * Resets the robot's position on the field.
+   *
+   * <p>The gyroscope angle does not need to be reset here on the user's robot code. The library
+   * automatically takes care of offsetting the gyro angle.
+   *
+   * @param gyroAngle The angle reported by the gyroscope.
+   * @param wheelPositions The current encoder readings.
+   * @param poseMeters The position on the field that your robot is at.
+   */
+  public void resetPosition(Rotation2d gyroAngle, T wheelPositions, Pose2d poseMeters) {
+    m_poseMeters = poseMeters;
+    m_previousAngle = m_poseMeters.getRotation();
+    m_gyroOffset = m_poseMeters.getRotation().minus(gyroAngle);
+    m_previousWheelPositions = wheelPositions.copy();
+  }
+
+  /**
+   * Returns the position of the robot on the field.
+   *
+   * @return The pose of the robot (x and y are in meters).
+   */
+  public Pose2d getPoseMeters() {
+    return m_poseMeters;
+  }
+
+  /**
+   * Updates the robot's position on the field using forward kinematics and integration of the pose
+   * over time. This method takes in an angle parameter which is used instead of the angular rate
+   * that is calculated from forward kinematics, in addition to the current distance measurement at
+   * each wheel.
+   *
+   * @param gyroAngle The angle reported by the gyroscope.
+   * @param wheelPositions The current encoder readings.
+   * @return The new pose of the robot.
+   */
+  public Pose2d update(Rotation2d gyroAngle, T wheelPositions) {
+    var angle = gyroAngle.plus(m_gyroOffset);
+
+    var twist = m_kinematics.toTwist2d(m_previousWheelPositions, wheelPositions);
+    twist.dtheta = angle.minus(m_previousAngle).getRadians();
+
+    var newPose = m_poseMeters.exp(twist);
+
+    m_previousWheelPositions = wheelPositions.copy();
+    m_previousAngle = angle;
+    m_poseMeters = new Pose2d(newPose.getTranslation(), angle);
+
+    return m_poseMeters;
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java
index 98df547..aa5338e 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java
@@ -10,7 +10,6 @@
 import edu.wpi.first.math.geometry.Translation2d;
 import edu.wpi.first.math.geometry.Twist2d;
 import java.util.Arrays;
-import java.util.Collections;
 import org.ejml.simple.SimpleMatrix;
 
 /**
@@ -33,32 +32,50 @@
  * <p>Forward kinematics is also used for odometry -- determining the position of the robot on the
  * field using encoders and a gyro.
  */
-public class SwerveDriveKinematics {
+public class SwerveDriveKinematics
+    implements Kinematics<SwerveDriveKinematics.SwerveDriveWheelStates, SwerveDriveWheelPositions> {
+  public static class SwerveDriveWheelStates {
+    public SwerveModuleState[] states;
+
+    /**
+     * Creates a new SwerveDriveWheelStates instance.
+     *
+     * @param states The swerve module states. This will be deeply copied.
+     */
+    public SwerveDriveWheelStates(SwerveModuleState[] states) {
+      this.states = new SwerveModuleState[states.length];
+      for (int i = 0; i < states.length; i++) {
+        this.states[i] = new SwerveModuleState(states[i].speedMetersPerSecond, states[i].angle);
+      }
+    }
+  }
+
   private final SimpleMatrix m_inverseKinematics;
   private final SimpleMatrix m_forwardKinematics;
 
   private final int m_numModules;
   private final Translation2d[] m_modules;
-  private final SwerveModuleState[] m_moduleStates;
+  private Rotation2d[] m_moduleHeadings;
   private Translation2d m_prevCoR = new Translation2d();
 
   /**
-   * Constructs a swerve drive kinematics object. This takes in a variable number of wheel locations
-   * as Translation2d objects. The order in which you pass in the wheel locations is the same order
-   * that you will receive the module states when performing inverse kinematics. It is also expected
-   * that you pass in the module states in the same order when calling the forward kinematics
-   * methods.
+   * Constructs a swerve drive kinematics object. This takes in a variable number of module
+   * locations as Translation2d objects. The order in which you pass in the module locations is the
+   * same order that you will receive the module states when performing inverse kinematics. It is
+   * also expected that you pass in the module states in the same order when calling the forward
+   * kinematics methods.
    *
-   * @param wheelsMeters The locations of the wheels relative to the physical center of the robot.
+   * @param moduleTranslationsMeters The locations of the modules relative to the physical center of
+   *     the robot.
    */
-  public SwerveDriveKinematics(Translation2d... wheelsMeters) {
-    if (wheelsMeters.length < 2) {
+  public SwerveDriveKinematics(Translation2d... moduleTranslationsMeters) {
+    if (moduleTranslationsMeters.length < 2) {
       throw new IllegalArgumentException("A swerve drive requires at least two modules");
     }
-    m_numModules = wheelsMeters.length;
-    m_modules = Arrays.copyOf(wheelsMeters, m_numModules);
-    m_moduleStates = new SwerveModuleState[m_numModules];
-    Arrays.fill(m_moduleStates, new SwerveModuleState());
+    m_numModules = moduleTranslationsMeters.length;
+    m_modules = Arrays.copyOf(moduleTranslationsMeters, m_numModules);
+    m_moduleHeadings = new Rotation2d[m_numModules];
+    Arrays.fill(m_moduleHeadings, new Rotation2d());
     m_inverseKinematics = new SimpleMatrix(m_numModules * 2, 3);
 
     for (int i = 0; i < m_numModules; i++) {
@@ -71,6 +88,21 @@
   }
 
   /**
+   * Reset the internal swerve module headings.
+   *
+   * @param moduleHeadings The swerve module headings. The order of the module headings should be
+   *     same as passed into the constructor of this class.
+   */
+  public void resetHeadings(Rotation2d... moduleHeadings) {
+    if (moduleHeadings.length != m_numModules) {
+      throw new IllegalArgumentException(
+          "Number of headings is not consistent with number of module locations provided in "
+              + "constructor");
+    }
+    m_moduleHeadings = Arrays.copyOf(moduleHeadings, m_numModules);
+  }
+
+  /**
    * Performs inverse kinematics to return the module states from a desired chassis velocity. This
    * method is often used to convert joystick values into module speeds and angles.
    *
@@ -91,17 +123,18 @@
    *     attainable max velocity. Use the {@link #desaturateWheelSpeeds(SwerveModuleState[], double)
    *     DesaturateWheelSpeeds} function to rectify this issue.
    */
-  @SuppressWarnings("PMD.MethodReturnsInternalArray")
   public SwerveModuleState[] toSwerveModuleStates(
       ChassisSpeeds chassisSpeeds, Translation2d centerOfRotationMeters) {
+    var moduleStates = new SwerveModuleState[m_numModules];
+
     if (chassisSpeeds.vxMetersPerSecond == 0.0
         && chassisSpeeds.vyMetersPerSecond == 0.0
         && chassisSpeeds.omegaRadiansPerSecond == 0.0) {
       for (int i = 0; i < m_numModules; i++) {
-        m_moduleStates[i].speedMetersPerSecond = 0.0;
+        moduleStates[i] = new SwerveModuleState(0.0, m_moduleHeadings[i]);
       }
 
-      return m_moduleStates;
+      return moduleStates;
     }
 
     if (!centerOfRotationMeters.equals(m_prevCoR)) {
@@ -139,10 +172,11 @@
       double speed = Math.hypot(x, y);
       Rotation2d angle = new Rotation2d(x, y);
 
-      m_moduleStates[i] = new SwerveModuleState(speed, angle);
+      moduleStates[i] = new SwerveModuleState(speed, angle);
+      m_moduleHeadings[i] = angle;
     }
 
-    return m_moduleStates;
+    return moduleStates;
   }
 
   /**
@@ -156,26 +190,31 @@
     return toSwerveModuleStates(chassisSpeeds, new Translation2d());
   }
 
+  @Override
+  public SwerveDriveWheelStates toWheelSpeeds(ChassisSpeeds chassisSpeeds) {
+    return new SwerveDriveWheelStates(toSwerveModuleStates(chassisSpeeds));
+  }
+
   /**
    * Performs forward kinematics to return the resulting chassis state from the given module states.
    * This method is often used for odometry -- determining the robot's position on the field using
    * data from the real-world speed and angle of each module on the robot.
    *
-   * @param wheelStates The state of the modules (as a SwerveModuleState type) as measured from
+   * @param moduleStates The state of the modules (as a SwerveModuleState type) as measured from
    *     respective encoders and gyros. The order of the swerve module states should be same as
    *     passed into the constructor of this class.
    * @return The resulting chassis speed.
    */
-  public ChassisSpeeds toChassisSpeeds(SwerveModuleState... wheelStates) {
-    if (wheelStates.length != m_numModules) {
+  public ChassisSpeeds toChassisSpeeds(SwerveModuleState... moduleStates) {
+    if (moduleStates.length != m_numModules) {
       throw new IllegalArgumentException(
-          "Number of modules is not consistent with number of wheel locations provided in "
+          "Number of modules is not consistent with number of module locations provided in "
               + "constructor");
     }
     var moduleStatesMatrix = new SimpleMatrix(m_numModules * 2, 1);
 
     for (int i = 0; i < m_numModules; i++) {
-      var module = wheelStates[i];
+      var module = moduleStates[i];
       moduleStatesMatrix.set(i * 2, 0, module.speedMetersPerSecond * module.angle.getCos());
       moduleStatesMatrix.set(i * 2 + 1, module.speedMetersPerSecond * module.angle.getSin());
     }
@@ -187,26 +226,31 @@
         chassisSpeedsVector.get(2, 0));
   }
 
+  @Override
+  public ChassisSpeeds toChassisSpeeds(SwerveDriveWheelStates wheelStates) {
+    return toChassisSpeeds(wheelStates.states);
+  }
+
   /**
    * Performs forward kinematics to return the resulting chassis state from the given module states.
    * This method is often used for odometry -- determining the robot's position on the field using
    * data from the real-world speed and angle of each module on the robot.
    *
-   * @param wheelDeltas The latest change in position of the modules (as a SwerveModulePosition
+   * @param moduleDeltas The latest change in position of the modules (as a SwerveModulePosition
    *     type) as measured from respective encoders and gyros. The order of the swerve module states
    *     should be same as passed into the constructor of this class.
    * @return The resulting Twist2d.
    */
-  public Twist2d toTwist2d(SwerveModulePosition... wheelDeltas) {
-    if (wheelDeltas.length != m_numModules) {
+  public Twist2d toTwist2d(SwerveModulePosition... moduleDeltas) {
+    if (moduleDeltas.length != m_numModules) {
       throw new IllegalArgumentException(
-          "Number of modules is not consistent with number of wheel locations provided in "
+          "Number of modules is not consistent with number of module locations provided in "
               + "constructor");
     }
     var moduleDeltaMatrix = new SimpleMatrix(m_numModules * 2, 1);
 
     for (int i = 0; i < m_numModules; i++) {
-      var module = wheelDeltas[i];
+      var module = moduleDeltas[i];
       moduleDeltaMatrix.set(i * 2, 0, module.distanceMeters * module.angle.getCos());
       moduleDeltaMatrix.set(i * 2 + 1, module.distanceMeters * module.angle.getSin());
     }
@@ -216,6 +260,22 @@
         chassisDeltaVector.get(0, 0), chassisDeltaVector.get(1, 0), chassisDeltaVector.get(2, 0));
   }
 
+  @Override
+  public Twist2d toTwist2d(SwerveDriveWheelPositions start, SwerveDriveWheelPositions end) {
+    if (start.positions.length != end.positions.length) {
+      throw new IllegalArgumentException("Inconsistent number of modules!");
+    }
+    var newPositions = new SwerveModulePosition[start.positions.length];
+    for (int i = 0; i < start.positions.length; i++) {
+      var startModule = start.positions[i];
+      var endModule = end.positions[i];
+      newPositions[i] =
+          new SwerveModulePosition(
+              endModule.distanceMeters - startModule.distanceMeters, endModule.angle);
+    }
+    return toTwist2d(newPositions);
+  }
+
   /**
    * Renormalizes the wheel speeds if any individual speed is above the specified maximum.
    *
@@ -230,7 +290,10 @@
    */
   public static void desaturateWheelSpeeds(
       SwerveModuleState[] moduleStates, double attainableMaxSpeedMetersPerSecond) {
-    double realMaxSpeed = Collections.max(Arrays.asList(moduleStates)).speedMetersPerSecond;
+    double realMaxSpeed = 0;
+    for (SwerveModuleState moduleState : moduleStates) {
+      realMaxSpeed = Math.max(realMaxSpeed, Math.abs(moduleState.speedMetersPerSecond));
+    }
     if (realMaxSpeed > attainableMaxSpeedMetersPerSecond) {
       for (SwerveModuleState moduleState : moduleStates) {
         moduleState.speedMetersPerSecond =
@@ -250,7 +313,7 @@
    *
    * @param moduleStates Reference to array of module states. The array will be mutated with the
    *     normalized speeds!
-   * @param currentChassisSpeed The current speed of the robot
+   * @param desiredChassisSpeed The desired speed of the robot
    * @param attainableMaxModuleSpeedMetersPerSecond The absolute max speed that a module can reach
    * @param attainableMaxTranslationalSpeedMetersPerSecond The absolute max speed that your robot
    *     can reach while translating
@@ -259,11 +322,14 @@
    */
   public static void desaturateWheelSpeeds(
       SwerveModuleState[] moduleStates,
-      ChassisSpeeds currentChassisSpeed,
+      ChassisSpeeds desiredChassisSpeed,
       double attainableMaxModuleSpeedMetersPerSecond,
       double attainableMaxTranslationalSpeedMetersPerSecond,
       double attainableMaxRotationalVelocityRadiansPerSecond) {
-    double realMaxSpeed = Collections.max(Arrays.asList(moduleStates)).speedMetersPerSecond;
+    double realMaxSpeed = 0;
+    for (SwerveModuleState moduleState : moduleStates) {
+      realMaxSpeed = Math.max(realMaxSpeed, Math.abs(moduleState.speedMetersPerSecond));
+    }
 
     if (attainableMaxTranslationalSpeedMetersPerSecond == 0
         || attainableMaxRotationalVelocityRadiansPerSecond == 0
@@ -271,10 +337,10 @@
       return;
     }
     double translationalK =
-        Math.hypot(currentChassisSpeed.vxMetersPerSecond, currentChassisSpeed.vyMetersPerSecond)
+        Math.hypot(desiredChassisSpeed.vxMetersPerSecond, desiredChassisSpeed.vyMetersPerSecond)
             / attainableMaxTranslationalSpeedMetersPerSecond;
     double rotationalK =
-        Math.abs(currentChassisSpeed.omegaRadiansPerSecond)
+        Math.abs(desiredChassisSpeed.omegaRadiansPerSecond)
             / attainableMaxRotationalVelocityRadiansPerSecond;
     double k = Math.max(translationalK, rotationalK);
     double scale = Math.min(k * attainableMaxModuleSpeedMetersPerSecond / realMaxSpeed, 1);
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveOdometry.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveOdometry.java
index c2e188f..4f954e9 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveOdometry.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveOdometry.java
@@ -17,14 +17,8 @@
  * <p>Teams can use odometry during the autonomous period for complex tasks like path following.
  * Furthermore, odometry can be used for latency compensation when using computer-vision systems.
  */
-public class SwerveDriveOdometry {
-  private final SwerveDriveKinematics m_kinematics;
-  private Pose2d m_poseMeters;
-
-  private Rotation2d m_gyroOffset;
-  private Rotation2d m_previousAngle;
+public class SwerveDriveOdometry extends Odometry<SwerveDriveWheelPositions> {
   private final int m_numModules;
-  private SwerveModulePosition[] m_previousModulePositions;
 
   /**
    * Constructs a SwerveDriveOdometry object.
@@ -39,18 +33,9 @@
       Rotation2d gyroAngle,
       SwerveModulePosition[] modulePositions,
       Pose2d initialPose) {
-    m_kinematics = kinematics;
-    m_poseMeters = initialPose;
-    m_gyroOffset = m_poseMeters.getRotation().minus(gyroAngle);
-    m_previousAngle = initialPose.getRotation();
-    m_numModules = modulePositions.length;
+    super(kinematics, gyroAngle, new SwerveDriveWheelPositions(modulePositions), initialPose);
 
-    m_previousModulePositions = new SwerveModulePosition[m_numModules];
-    for (int index = 0; index < m_numModules; index++) {
-      m_previousModulePositions[index] =
-          new SwerveModulePosition(
-              modulePositions[index].distanceMeters, modulePositions[index].angle);
-    }
+    m_numModules = modulePositions.length;
 
     MathSharedStore.reportUsage(MathUsageId.kOdometry_SwerveDrive, 1);
   }
@@ -83,29 +68,18 @@
    */
   public void resetPosition(
       Rotation2d gyroAngle, SwerveModulePosition[] modulePositions, Pose2d pose) {
-    if (modulePositions.length != m_numModules) {
+    resetPosition(gyroAngle, new SwerveDriveWheelPositions(modulePositions), pose);
+  }
+
+  @Override
+  public void resetPosition(
+      Rotation2d gyroAngle, SwerveDriveWheelPositions modulePositions, Pose2d pose) {
+    if (modulePositions.positions.length != m_numModules) {
       throw new IllegalArgumentException(
           "Number of modules is not consistent with number of wheel locations provided in "
               + "constructor");
     }
-
-    m_poseMeters = pose;
-    m_previousAngle = pose.getRotation();
-    m_gyroOffset = m_poseMeters.getRotation().minus(gyroAngle);
-    for (int index = 0; index < m_numModules; index++) {
-      m_previousModulePositions[index] =
-          new SwerveModulePosition(
-              modulePositions[index].distanceMeters, modulePositions[index].angle);
-    }
-  }
-
-  /**
-   * Returns the position of the robot on the field.
-   *
-   * @return The pose of the robot (x and y are in meters).
-   */
-  public Pose2d getPoseMeters() {
-    return m_poseMeters;
+    super.resetPosition(gyroAngle, modulePositions, pose);
   }
 
   /**
@@ -121,32 +95,16 @@
    * @return The new pose of the robot.
    */
   public Pose2d update(Rotation2d gyroAngle, SwerveModulePosition[] modulePositions) {
-    if (modulePositions.length != m_numModules) {
+    return update(gyroAngle, new SwerveDriveWheelPositions(modulePositions));
+  }
+
+  @Override
+  public Pose2d update(Rotation2d gyroAngle, SwerveDriveWheelPositions modulePositions) {
+    if (modulePositions.positions.length != m_numModules) {
       throw new IllegalArgumentException(
           "Number of modules is not consistent with number of wheel locations provided in "
               + "constructor");
     }
-
-    var moduleDeltas = new SwerveModulePosition[m_numModules];
-    for (int index = 0; index < m_numModules; index++) {
-      var current = modulePositions[index];
-      var previous = m_previousModulePositions[index];
-
-      moduleDeltas[index] =
-          new SwerveModulePosition(current.distanceMeters - previous.distanceMeters, current.angle);
-      previous.distanceMeters = current.distanceMeters;
-    }
-
-    var angle = gyroAngle.plus(m_gyroOffset);
-
-    var twist = m_kinematics.toTwist2d(moduleDeltas);
-    twist.dtheta = angle.minus(m_previousAngle).getRadians();
-
-    var newPose = m_poseMeters.exp(twist);
-
-    m_previousAngle = angle;
-    m_poseMeters = new Pose2d(newPose.getTranslation(), angle);
-
-    return m_poseMeters;
+    return super.update(gyroAngle, modulePositions);
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveWheelPositions.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveWheelPositions.java
new file mode 100644
index 0000000..e88f044
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveWheelPositions.java
@@ -0,0 +1,61 @@
+// 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.
+
+package edu.wpi.first.math.kinematics;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+public class SwerveDriveWheelPositions implements WheelPositions<SwerveDriveWheelPositions> {
+  public SwerveModulePosition[] positions;
+
+  /**
+   * Creates a new SwerveDriveWheelPositions instance.
+   *
+   * @param positions The swerve module positions. This will be deeply copied.
+   */
+  public SwerveDriveWheelPositions(SwerveModulePosition[] positions) {
+    this.positions = new SwerveModulePosition[positions.length];
+    for (int i = 0; i < positions.length; i++) {
+      this.positions[i] = positions[i].copy();
+    }
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof SwerveDriveWheelPositions) {
+      SwerveDriveWheelPositions other = (SwerveDriveWheelPositions) obj;
+      return Arrays.equals(this.positions, other.positions);
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    // Cast to interpret positions as single argument, not array of the arguments
+    return Objects.hash((Object) positions);
+  }
+
+  @Override
+  public String toString() {
+    return String.format("SwerveDriveWheelPositions(%s)", Arrays.toString(positions));
+  }
+
+  @Override
+  public SwerveDriveWheelPositions copy() {
+    return new SwerveDriveWheelPositions(positions);
+  }
+
+  @Override
+  public SwerveDriveWheelPositions interpolate(SwerveDriveWheelPositions endValue, double t) {
+    if (endValue.positions.length != positions.length) {
+      throw new IllegalArgumentException("Inconsistent number of modules!");
+    }
+    var newPositions = new SwerveModulePosition[positions.length];
+    for (int i = 0; i < positions.length; i++) {
+      newPositions[i] = positions[i].interpolate(endValue.positions[i], t);
+    }
+    return new SwerveDriveWheelPositions(newPositions);
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java
index cdd7834..d058764 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java
@@ -4,11 +4,14 @@
 
 package edu.wpi.first.math.kinematics;
 
+import edu.wpi.first.math.MathUtil;
 import edu.wpi.first.math.geometry.Rotation2d;
+import edu.wpi.first.math.interpolation.Interpolatable;
 import java.util.Objects;
 
 /** Represents the state of one swerve module. */
-public class SwerveModulePosition implements Comparable<SwerveModulePosition> {
+public class SwerveModulePosition
+    implements Comparable<SwerveModulePosition>, Interpolatable<SwerveModulePosition> {
   /** Distance measured by the wheel of the module. */
   public double distanceMeters;
 
@@ -32,14 +35,15 @@
   @Override
   public boolean equals(Object obj) {
     if (obj instanceof SwerveModulePosition) {
-      return Double.compare(distanceMeters, ((SwerveModulePosition) obj).distanceMeters) == 0;
+      SwerveModulePosition other = (SwerveModulePosition) obj;
+      return Math.abs(other.distanceMeters - distanceMeters) < 1E-9 && angle.equals(other.angle);
     }
     return false;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(distanceMeters);
+    return Objects.hash(distanceMeters, angle);
   }
 
   /**
@@ -59,4 +63,20 @@
     return String.format(
         "SwerveModulePosition(Distance: %.2f m, Angle: %s)", distanceMeters, angle);
   }
+
+  /**
+   * Returns a copy of this swerve module position.
+   *
+   * @return A copy.
+   */
+  public SwerveModulePosition copy() {
+    return new SwerveModulePosition(distanceMeters, angle);
+  }
+
+  @Override
+  public SwerveModulePosition interpolate(SwerveModulePosition endValue, double t) {
+    return new SwerveModulePosition(
+        MathUtil.interpolate(this.distanceMeters, endValue.distanceMeters, t),
+        this.angle.interpolate(endValue.angle, t));
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java
index ec7fd9f..10dee4f 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java
@@ -32,15 +32,16 @@
   @Override
   public boolean equals(Object obj) {
     if (obj instanceof SwerveModuleState) {
-      return Double.compare(speedMetersPerSecond, ((SwerveModuleState) obj).speedMetersPerSecond)
-          == 0;
+      SwerveModuleState other = (SwerveModuleState) obj;
+      return Math.abs(other.speedMetersPerSecond - speedMetersPerSecond) < 1E-9
+          && angle.equals(other.angle);
     }
     return false;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(speedMetersPerSecond);
+    return Objects.hash(speedMetersPerSecond, angle);
   }
 
   /**
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/WheelPositions.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/WheelPositions.java
new file mode 100644
index 0000000..029a0ac
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/kinematics/WheelPositions.java
@@ -0,0 +1,16 @@
+// 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.
+
+package edu.wpi.first.math.kinematics;
+
+import edu.wpi.first.math.interpolation.Interpolatable;
+
+public interface WheelPositions<T extends WheelPositions<T>> extends Interpolatable<T> {
+  /**
+   * Returns a copy of this instance.
+   *
+   * @return A copy.
+   */
+  T copy();
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java
index c1a87f2..b2a34a5 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java
@@ -71,8 +71,8 @@
   private SplineParameterizer() {}
 
   /**
-   * Parameterizes the spline. This method breaks up the spline into various arcs until their dx,
-   * dy, and dtheta are within specific tolerances.
+   * Parametrizes the spline. This method breaks up the spline into various arcs until their dx, dy,
+   * and dtheta are within specific tolerances.
    *
    * @param spline The spline to parameterize.
    * @return A list of poses and curvatures that represents various points on the spline.
@@ -84,8 +84,8 @@
   }
 
   /**
-   * Parameterizes the spline. This method breaks up the spline into various arcs until their dx,
-   * dy, and dtheta are within specific tolerances.
+   * Parametrizes the spline. This method breaks up the spline into various arcs until their dx, dy,
+   * and dtheta are within specific tolerances.
    *
    * @param spline The spline to parameterize.
    * @param t0 Starting internal spline parameter. It is recommended to use 0.0.
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/Discretization.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/Discretization.java
index 1871803..b2fe9c8 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/Discretization.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/Discretization.java
@@ -109,93 +109,6 @@
   }
 
   /**
-   * Discretizes the given continuous A and Q matrices.
-   *
-   * <p>Rather than solving a 2N x 2N matrix exponential like in DiscretizeQ() (which is expensive),
-   * we take advantage of the structure of the block matrix of A and Q.
-   *
-   * <ul>
-   *   <li>eᴬᵀ, which is only N x N, is relatively cheap.
-   *   <li>The upper-right quarter of the 2N x 2N matrix, which we can approximate using a taylor
-   *       series to several terms and still be substantially cheaper than taking the big
-   *       exponential.
-   * </ul>
-   *
-   * @param <States> Nat representing the number of states.
-   * @param contA Continuous system matrix.
-   * @param contQ Continuous process noise covariance matrix.
-   * @param dtSeconds Discretization timestep.
-   * @return a pair representing the discrete system matrix and process noise covariance matrix.
-   */
-  public static <States extends Num>
-      Pair<Matrix<States, States>, Matrix<States, States>> discretizeAQTaylor(
-          Matrix<States, States> contA, Matrix<States, States> contQ, double dtSeconds) {
-    //       T
-    // Q_d = ∫ e^(Aτ) Q e^(Aᵀτ) dτ
-    //       0
-    //
-    // M = [−A  Q ]
-    //     [ 0  Aᵀ]
-    // ϕ = eᴹᵀ
-    // ϕ₁₂ = A_d⁻¹Q_d
-    //
-    // Taylor series of ϕ:
-    //
-    //   ϕ = eᴹᵀ = I + MT + 1/2 M²T² + 1/6 M³T³ + …
-    //   ϕ = eᴹᵀ = I + MT + 1/2 T²M² + 1/6 T³M³ + …
-    //
-    // Taylor series of ϕ expanded for ϕ₁₂:
-    //
-    //   ϕ₁₂ = 0 + QT + 1/2 T² (−AQ + QAᵀ) + 1/6 T³ (−A lastTerm + Q Aᵀ²) + …
-    //
-    // ```
-    // lastTerm = Q
-    // lastCoeff = T
-    // ATn = Aᵀ
-    // ϕ₁₂ = lastTerm lastCoeff = QT
-    //
-    // for i in range(2, 6):
-    //   // i = 2
-    //   lastTerm = −A lastTerm + Q ATn = −AQ + QAᵀ
-    //   lastCoeff *= T/i → lastCoeff *= T/2 = 1/2 T²
-    //   ATn *= Aᵀ = Aᵀ²
-    //
-    //   // i = 3
-    //   lastTerm = −A lastTerm + Q ATn = −A (−AQ + QAᵀ) + QAᵀ² = …
-    //   …
-    // ```
-
-    // Make continuous Q symmetric if it isn't already
-    Matrix<States, States> Q = contQ.plus(contQ.transpose()).div(2.0);
-
-    Matrix<States, States> lastTerm = Q.copy();
-    double lastCoeff = dtSeconds;
-
-    // Aᵀⁿ
-    Matrix<States, States> ATn = contA.transpose();
-
-    Matrix<States, States> phi12 = lastTerm.times(lastCoeff);
-
-    // i = 6 i.e. 5th order should be enough precision
-    for (int i = 2; i < 6; ++i) {
-      lastTerm = contA.times(-1).times(lastTerm).plus(Q.times(ATn));
-      lastCoeff *= dtSeconds / ((double) i);
-
-      phi12 = phi12.plus(lastTerm.times(lastCoeff));
-
-      ATn = ATn.times(contA.transpose());
-    }
-
-    var discA = discretizeA(contA, dtSeconds);
-    Q = discA.times(phi12);
-
-    // Make Q symmetric if it isn't already
-    var discQ = Q.plus(Q.transpose()).div(2.0);
-
-    return new Pair<>(discA, discQ);
-  }
-
-  /**
    * Returns a discretized version of the provided continuous measurement noise covariance matrix.
    * Note that dt=0.0 divides R by zero.
    *
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java
index 94be369..e9b46fc 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java
@@ -238,7 +238,11 @@
                     .times(h))
                 .normF();
 
-        h *= 0.9 * Math.pow(maxError / truncationError, 1.0 / 5.0);
+        if (truncationError == 0.0) {
+          h = dtSeconds - dtElapsed;
+        } else {
+          h *= 0.9 * Math.pow(maxError / truncationError, 1.0 / 5.0);
+        }
       } while (truncationError > maxError);
 
       dtElapsed += h;
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java
index cd431f7..0c6e334 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java
@@ -21,7 +21,7 @@
    * Constructs a DC motor.
    *
    * @param nominalVoltageVolts Voltage at which the motor constants were measured.
-   * @param stallTorqueNewtonMeters Current draw when stalled.
+   * @param stallTorqueNewtonMeters Torque when stalled.
    * @param stallCurrentAmps Current draw when stalled.
    * @param freeCurrentAmps Current draw under no load.
    * @param freeSpeedRadPerSec Angular velocity under no load.
@@ -47,10 +47,10 @@
   }
 
   /**
-   * Estimate the current being drawn by this motor.
+   * Calculate current drawn by motor with given speed and input voltage.
    *
-   * @param speedRadiansPerSec The speed of the motor.
-   * @param voltageInputVolts The input voltage.
+   * @param speedRadiansPerSec The current angular velocity of the motor.
+   * @param voltageInputVolts The voltage being applied to the motor.
    * @return The estimated current.
    */
   public double getCurrent(double speedRadiansPerSec, double voltageInputVolts) {
@@ -58,20 +58,20 @@
   }
 
   /**
-   * Calculate the torque produced by the motor for a given current.
+   * Calculate torque produced by the motor with a given current.
    *
    * @param currentAmpere The current drawn by the motor.
-   * @return The torque produced.
+   * @return The torque output.
    */
   public double getTorque(double currentAmpere) {
     return currentAmpere * KtNMPerAmp;
   }
 
   /**
-   * Calculate the voltage provided to the motor at a given torque and angular velocity.
+   * Calculate the voltage provided to the motor for a given torque and angular velocity.
    *
    * @param torqueNm The torque produced by the motor.
-   * @param speedRadiansPerSec The speed of the motor.
+   * @param speedRadiansPerSec The current angular velocity of the motor.
    * @return The voltage of the motor.
    */
   public double getVoltage(double torqueNm, double speedRadiansPerSec) {
@@ -79,14 +79,15 @@
   }
 
   /**
-   * Calculate the speed of the motor at a given torque and input voltage.
+   * Calculates the angular speed produced by the motor at a given torque and input voltage.
    *
    * @param torqueNm The torque produced by the motor.
    * @param voltageInputVolts The voltage applied to the motor.
-   * @return The speed of the motor.
+   * @return The angular speed of the motor.
    */
   public double getSpeed(double torqueNm, double voltageInputVolts) {
-    return voltageInputVolts - 1.0 / KtNMPerAmp * torqueNm * rOhms * KvRadPerSecPerVolt;
+    return voltageInputVolts * KvRadPerSecPerVolt
+        - 1.0 / KtNMPerAmp * torqueNm * rOhms * KvRadPerSecPerVolt;
   }
 
   /**
@@ -227,6 +228,18 @@
   }
 
   /**
+   * Return a gearbox of Falcon 500 motors with FOC (Field-Oriented Control) enabled.
+   *
+   * @param numMotors Number of motors in the gearbox.
+   * @return A gearbox of Falcon 500 FOC enabled motors.
+   */
+  public static DCMotor getFalcon500Foc(int numMotors) {
+    // https://store.ctr-electronics.com/falcon-500-powered-by-talon-fx/
+    return new DCMotor(
+        12, 5.84, 304, 1.5, Units.rotationsPerMinuteToRadiansPerSecond(6080.0), numMotors);
+  }
+
+  /**
    * Return a gearbox of Romi/TI_RSLK MAX motors.
    *
    * @param numMotors Number of motors in the gearbox.
@@ -237,4 +250,40 @@
     return new DCMotor(
         4.5, 0.1765, 1.25, 0.13, Units.rotationsPerMinuteToRadiansPerSecond(150.0), numMotors);
   }
+
+  /**
+   * Return a gearbox of Kraken X60 brushless motors.
+   *
+   * @param numMotors Number of motors in the gearbox.
+   * @return a gearbox of Kraken X60 motors.
+   */
+  public static DCMotor getKrakenX60(int numMotors) {
+    // From https://store.ctr-electronics.com/announcing-kraken-x60/
+    return new DCMotor(
+        12, 7.09, 366, 2, Units.rotationsPerMinuteToRadiansPerSecond(6000), numMotors);
+  }
+
+  /**
+   * Return a gearbox of Kraken X60 brushless motors with FOC (Field-Oriented Control) enabled.
+   *
+   * @param numMotors Number of motors in the gearbox.
+   * @return A gearbox of Kraken X60 FOC enabled motors.
+   */
+  public static DCMotor getKrakenX60Foc(int numMotors) {
+    // From https://store.ctr-electronics.com/announcing-kraken-x60/
+    return new DCMotor(
+        12, 9.37, 483, 2, Units.rotationsPerMinuteToRadiansPerSecond(5800), numMotors);
+  }
+
+  /**
+   * Return a gearbox of Neo Vortex brushless motors.
+   *
+   * @param numMotors Number of motors in the gearbox.
+   * @return a gearbox of Neo Vortex motors.
+   */
+  public static DCMotor getNeoVortex(int numMotors) {
+    // From https://www.revrobotics.com/next-generation-spark-neo/
+    return new DCMotor(
+        12, 3.60, 211, 3.6, Units.rotationsPerMinuteToRadiansPerSecond(6784), numMotors);
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java
index 332e690..8377022 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java
@@ -123,6 +123,39 @@
   }
 
   /**
+   * Create a state-space model of a DC motor system. The states of the system are [angular
+   * position, angular velocity], inputs are [voltage], and outputs are [angular position, angular
+   * velocity].
+   *
+   * <p>The distance unit you choose MUST be an SI unit (i.e. meters or radians). You can use the
+   * {@link edu.wpi.first.math.util.Units} class for converting between unit types.
+   *
+   * <p>The parameters provided by the user are from this feedforward model:
+   *
+   * <p>u = K_v v + K_a a
+   *
+   * @param kV The velocity gain, in volts/(unit/sec)
+   * @param kA The acceleration gain, in volts/(unit/sec^2)
+   * @return A LinearSystem representing the given characterized constants.
+   * @throws IllegalArgumentException if kV &lt;= 0 or kA &lt;= 0.
+   * @see <a href="https://github.com/wpilibsuite/sysid">https://github.com/wpilibsuite/sysid</a>
+   */
+  public static LinearSystem<N2, N1, N2> createDCMotorSystem(double kV, double kA) {
+    if (kV <= 0.0) {
+      throw new IllegalArgumentException("Kv must be greater than zero.");
+    }
+    if (kA <= 0.0) {
+      throw new IllegalArgumentException("Ka must be greater than zero.");
+    }
+
+    return new LinearSystem<>(
+        Matrix.mat(Nat.N2(), Nat.N2()).fill(0, 1, 0, -kV / kA),
+        VecBuilder.fill(0, 1 / kA),
+        Matrix.eye(Nat.N2()),
+        new Matrix<>(Nat.N2(), Nat.N1()));
+  }
+
+  /**
    * Create a state-space model of a differential drive drivetrain. In this model, the states are
    * [left velocity, right velocity]ᵀ, inputs are [left voltage, right voltage]ᵀ, and outputs are
    * [left velocity, right velocity]ᵀ.
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/ExponentialProfile.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/ExponentialProfile.java
new file mode 100644
index 0000000..1580e85
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/ExponentialProfile.java
@@ -0,0 +1,449 @@
+// 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.
+
+package edu.wpi.first.math.trajectory;
+
+import java.util.Objects;
+
+/**
+ * A exponential curve-shaped velocity profile.
+ *
+ * <p>While this class can be used for a profiled movement from start to finish, the intended usage
+ * is to filter a reference's dynamics based on state-space model dynamics. To compute the reference
+ * obeying this constraint, do the following.
+ *
+ * <p>Initialization:
+ *
+ * <pre><code>
+ * ExponentialProfile.Constraints constraints =
+ *   ExponentialProfile.Constraints.fromCharacteristics(kMaxV, kV, kA);
+ * ExponentialProfile.State previousProfiledReference =
+ *   new ExponentialProfile.State(initialReference, 0.0);
+ * ExponentialProfile profile = new ExponentialProfile(constraints);
+ * </code></pre>
+ *
+ * <p>Run on update:
+ *
+ * <pre><code>
+ * previousProfiledReference =
+ * profile.calculate(timeSincePreviousUpdate, previousProfiledReference, unprofiledReference);
+ * </code></pre>
+ *
+ * <p>where `unprofiledReference` is free to change between calls. Note that when the unprofiled
+ * reference is within the constraints, `calculate()` returns the unprofiled reference unchanged.
+ *
+ * <p>Otherwise, a timer can be started to provide monotonic values for `calculate()` and to
+ * determine when the profile has completed via `isFinished()`.
+ */
+public class ExponentialProfile {
+  private final Constraints m_constraints;
+
+  public static class ProfileTiming {
+    public final double inflectionTime;
+    public final double totalTime;
+
+    protected ProfileTiming(double inflectionTime, double totalTime) {
+      this.inflectionTime = inflectionTime;
+      this.totalTime = totalTime;
+    }
+
+    /**
+     * Decides if the profile is finished by time t.
+     *
+     * @param t The time since the beginning of the profile.
+     * @return if the profile is finished at time t.
+     */
+    public boolean isFinished(double t) {
+      return t > inflectionTime;
+    }
+  }
+
+  public static class Constraints {
+    public final double maxInput;
+
+    public final double A;
+    public final double B;
+
+    /**
+     * Construct constraints for an ExponentialProfile.
+     *
+     * @param maxInput maximum unsigned input voltage
+     * @param A The State-Space 1x1 system matrix.
+     * @param B The State-Space 1x1 input matrix.
+     */
+    private Constraints(double maxInput, double A, double B) {
+      this.maxInput = maxInput;
+      this.A = A;
+      this.B = B;
+    }
+
+    /**
+     * Computes the max achievable velocity for an Exponential Profile.
+     *
+     * @return The seady-state velocity achieved by this profile.
+     */
+    public double maxVelocity() {
+      return -maxInput * B / A;
+    }
+
+    /**
+     * Construct constraints for an ExponentialProfile from characteristics.
+     *
+     * @param maxInput maximum unsigned input voltage
+     * @param kV The velocity gain.
+     * @param kA The acceleration gain.
+     * @return The Constraints object.
+     */
+    public static Constraints fromCharacteristics(double maxInput, double kV, double kA) {
+      return new Constraints(maxInput, -kV / kA, 1.0 / kA);
+    }
+
+    /**
+     * Construct constraints for an ExponentialProfile from State-Space parameters.
+     *
+     * @param maxInput maximum unsigned input voltage
+     * @param A The State-Space 1x1 system matrix.
+     * @param B The State-Space 1x1 input matrix.
+     * @return The Constraints object.
+     */
+    public static Constraints fromStateSpace(double maxInput, double A, double B) {
+      return new Constraints(maxInput, A, B);
+    }
+  }
+
+  public static class State {
+    public final double position;
+
+    public final double velocity;
+
+    /**
+     * Construct a state within an exponential profile.
+     *
+     * @param position The position at this state.
+     * @param velocity The velocity at this state.
+     */
+    public State(double position, double velocity) {
+      this.position = position;
+      this.velocity = velocity;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other instanceof State) {
+        State rhs = (State) other;
+        return this.position == rhs.position && this.velocity == rhs.velocity;
+      } else {
+        return false;
+      }
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(position, velocity);
+    }
+  }
+
+  /**
+   * Construct an ExponentialProfile.
+   *
+   * @param constraints The constraints on the profile, like maximum input.
+   */
+  public ExponentialProfile(Constraints constraints) {
+    m_constraints = constraints;
+  }
+
+  /**
+   * Calculate the correct position and velocity for the profile at a time t where the current state
+   * is at time t = 0.
+   *
+   * @param t The time since the beginning of the profile.
+   * @param current The current state.
+   * @param goal The desired state when the profile is complete.
+   * @return The position and velocity of the profile at time t.
+   */
+  public State calculate(double t, State current, State goal) {
+    var direction = shouldFlipInput(current, goal) ? -1 : 1;
+    var u = direction * m_constraints.maxInput;
+
+    var inflectionPoint = calculateInflectionPoint(current, goal, u);
+    var timing = calculateProfileTiming(current, inflectionPoint, goal, u);
+
+    if (t < 0) {
+      return current;
+    } else if (t < timing.inflectionTime) {
+      return new State(
+          computeDistanceFromTime(t, u, current), computeVelocityFromTime(t, u, current));
+    } else if (t < timing.totalTime) {
+      return new State(
+          computeDistanceFromTime(t - timing.totalTime, -u, goal),
+          computeVelocityFromTime(t - timing.totalTime, -u, goal));
+    } else {
+      return goal;
+    }
+  }
+
+  /**
+   * Calculate the point after which the fastest way to reach the goal state is to apply input in
+   * the opposite direction.
+   *
+   * @param current The current state.
+   * @param goal The desired state when the profile is complete.
+   * @return The position and velocity of the profile at the inflection point.
+   */
+  public State calculateInflectionPoint(State current, State goal) {
+    var direction = shouldFlipInput(current, goal) ? -1 : 1;
+    var u = direction * m_constraints.maxInput;
+
+    return calculateInflectionPoint(current, goal, u);
+  }
+
+  /**
+   * Calculate the point after which the fastest way to reach the goal state is to apply input in
+   * the opposite direction.
+   *
+   * @param current The current state.
+   * @param goal The desired state when the profile is complete.
+   * @param input The signed input applied to this profile from the current state.
+   * @return The position and velocity of the profile at the inflection point.
+   */
+  private State calculateInflectionPoint(State current, State goal, double input) {
+    var u = input;
+
+    if (current.equals(goal)) {
+      return current;
+    }
+
+    var inflectionVelocity = solveForInflectionVelocity(u, current, goal);
+    var inflectionPosition = computeDistanceFromVelocity(inflectionVelocity, -u, goal);
+
+    return new State(inflectionPosition, inflectionVelocity);
+  }
+
+  /**
+   * Calculate the time it will take for this profile to reach the goal state.
+   *
+   * @param current The current state.
+   * @param goal The desired state when the profile is complete.
+   * @return The total duration of this profile.
+   */
+  public double timeLeftUntil(State current, State goal) {
+    var timing = calculateProfileTiming(current, goal);
+
+    return timing.totalTime;
+  }
+
+  /**
+   * Calculate the time it will take for this profile to reach the inflection point, and the time it
+   * will take for this profile to reach the goal state.
+   *
+   * @param current The current state.
+   * @param goal The desired state when the profile is complete.
+   * @return The timing information for this profile.
+   */
+  public ProfileTiming calculateProfileTiming(State current, State goal) {
+    var direction = shouldFlipInput(current, goal) ? -1 : 1;
+    var u = direction * m_constraints.maxInput;
+
+    var inflectionPoint = calculateInflectionPoint(current, goal, u);
+    return calculateProfileTiming(current, inflectionPoint, goal, u);
+  }
+
+  /**
+   * Calculate the time it will take for this profile to reach the inflection point, and the time it
+   * will take for this profile to reach the goal state.
+   *
+   * @param current The current state.
+   * @param inflectionPoint The inflection point of this profile.
+   * @param goal The desired state when the profile is complete.
+   * @param input The signed input applied to this profile from the current state.
+   * @return The timing information for this profile.
+   */
+  private ProfileTiming calculateProfileTiming(
+      State current, State inflectionPoint, State goal, double input) {
+    var u = input;
+
+    double inflectionT_forward;
+
+    // We need to handle 5 cases here:
+    //
+    // - Approaching -maxVelocity from below
+    // - Approaching -maxVelocity from above
+    // - Approaching maxVelocity from below
+    // - Approaching maxVelocity from above
+    // - At +-maxVelocity
+    //
+    // For cases 1 and 3, we want to subtract epsilon from the inflection point velocity.
+    // For cases 2 and 4, we want to add epsilon to the inflection point velocity.
+    // For case 5, we have reached inflection point velocity.
+    double epsilon = 1e-9;
+    if (Math.abs(Math.signum(input) * m_constraints.maxVelocity() - inflectionPoint.velocity)
+        < epsilon) {
+      double solvableV = inflectionPoint.velocity;
+      double t_to_solvable_v;
+      double x_at_solvable_v;
+      if (Math.abs(current.velocity - inflectionPoint.velocity) < epsilon) {
+        t_to_solvable_v = 0;
+        x_at_solvable_v = current.position;
+      } else {
+        if (Math.abs(current.velocity) > m_constraints.maxVelocity()) {
+          solvableV += Math.signum(u) * epsilon;
+        } else {
+          solvableV -= Math.signum(u) * epsilon;
+        }
+
+        t_to_solvable_v = computeTimeFromVelocity(solvableV, u, current.velocity);
+        x_at_solvable_v = computeDistanceFromVelocity(solvableV, u, current);
+      }
+
+      inflectionT_forward =
+          t_to_solvable_v
+              + Math.signum(input)
+                  * (inflectionPoint.position - x_at_solvable_v)
+                  / m_constraints.maxVelocity();
+    } else {
+      inflectionT_forward = computeTimeFromVelocity(inflectionPoint.velocity, u, current.velocity);
+    }
+
+    var inflectionT_backward = computeTimeFromVelocity(inflectionPoint.velocity, -u, goal.velocity);
+
+    return new ProfileTiming(inflectionT_forward, inflectionT_forward - inflectionT_backward);
+  }
+
+  /**
+   * Calculate the position reached after t seconds when applying an input from the initial state.
+   *
+   * @param t The time since the initial state.
+   * @param input The signed input applied to this profile from the initial state.
+   * @param initial The initial state.
+   * @return The distance travelled by this profile.
+   */
+  private double computeDistanceFromTime(double t, double input, State initial) {
+    var A = m_constraints.A;
+    var B = m_constraints.B;
+    var u = input;
+
+    return initial.position
+        + (-B * u * t + (initial.velocity + B * u / A) * (Math.exp(A * t) - 1)) / A;
+  }
+
+  /**
+   * Calculate the velocity reached after t seconds when applying an input from the initial state.
+   *
+   * @param t The time since the initial state.
+   * @param input The signed input applied to this profile from the initial state.
+   * @param initial The initial state.
+   * @return The distance travelled by this profile.
+   */
+  private double computeVelocityFromTime(double t, double input, State initial) {
+    var A = m_constraints.A;
+    var B = m_constraints.B;
+    var u = input;
+
+    return (initial.velocity + B * u / A) * Math.exp(A * t) - B * u / A;
+  }
+
+  /**
+   * Calculate the time required to reach a specified velocity given the initial velocity.
+   *
+   * @param velocity The goal velocity.
+   * @param input The signed input applied to this profile from the initial state.
+   * @param initial The initial velocity.
+   * @return The time required to reach the goal velocity.
+   */
+  private double computeTimeFromVelocity(double velocity, double input, double initial) {
+    var A = m_constraints.A;
+    var B = m_constraints.B;
+    var u = input;
+
+    return Math.log((A * velocity + B * u) / (A * initial + B * u)) / A;
+  }
+
+  /**
+   * Calculate the distance reached at the same time as the given velocity when applying the given
+   * input from the initial state.
+   *
+   * @param velocity The velocity reached by this profile
+   * @param input The signed input applied to this profile from the initial state.
+   * @param initial The initial state.
+   * @return The distance reached when the given velocity is reached.
+   */
+  private double computeDistanceFromVelocity(double velocity, double input, State initial) {
+    var A = m_constraints.A;
+    var B = m_constraints.B;
+    var u = input;
+
+    return initial.position
+        + (velocity - initial.velocity) / A
+        - B * u / (A * A) * Math.log((A * velocity + B * u) / (A * initial.velocity + B * u));
+  }
+
+  /**
+   * Calculate the velocity at which input should be reversed in order to reach the goal state from
+   * the current state.
+   *
+   * @param input The signed input applied to this profile from the current state.
+   * @param current The current state.
+   * @param goal The goal state.
+   * @return The inflection velocity.
+   */
+  private double solveForInflectionVelocity(double input, State current, State goal) {
+    var A = m_constraints.A;
+    var B = m_constraints.B;
+    var u = input;
+
+    var U_dir = Math.signum(u);
+
+    var position_delta = goal.position - current.position;
+    var velocity_delta = goal.velocity - current.velocity;
+
+    var scalar = (A * current.velocity + B * u) * (A * goal.velocity - B * u);
+    var power = -A / B / u * (A * position_delta - velocity_delta);
+
+    var a = -A * A;
+    var c = (B * B) * (u * u) + scalar * Math.exp(power);
+
+    if (-1e-9 < c && c < 0) {
+      // Numerical stability issue - the heuristic gets it right but c is around -1e-13
+      return 0;
+    }
+
+    return U_dir * Math.sqrt(-c / a);
+  }
+
+  /**
+   * Returns true if the profile should be inverted.
+   *
+   * <p>The profile is inverted if we should first apply negative input in order to reach the goal
+   * state.
+   *
+   * @param current The initial state (usually the current state).
+   * @param goal The desired state when the profile is complete.
+   */
+  @SuppressWarnings("UnnecessaryParentheses")
+  private boolean shouldFlipInput(State current, State goal) {
+    var u = m_constraints.maxInput;
+
+    var xf = goal.position;
+    var v0 = current.velocity;
+    var vf = goal.velocity;
+
+    var x_forward = computeDistanceFromVelocity(vf, u, current);
+    var x_reverse = computeDistanceFromVelocity(vf, -u, current);
+
+    if (v0 >= m_constraints.maxVelocity()) {
+      return xf < x_reverse;
+    }
+
+    if (v0 <= -m_constraints.maxVelocity()) {
+      return xf < x_forward;
+    }
+
+    var a = v0 >= 0;
+    var b = vf >= 0;
+    var c = xf >= x_forward;
+    var d = xf >= x_reverse;
+
+    return (a && !d) || (b && !c) || (!c && !d);
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java
index f716dc0..f7120be 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java
@@ -30,9 +30,15 @@
    * Constructs a trajectory from a vector of states.
    *
    * @param states A vector of states.
+   * @throws IllegalArgumentException if the vector of states is empty.
    */
   public Trajectory(final List<State> states) {
     m_states = states;
+
+    if (m_states.isEmpty()) {
+      throw new IllegalArgumentException("Trajectory manually created with no states.");
+    }
+
     m_totalTimeSeconds = m_states.get(m_states.size() - 1).timeSeconds;
   }
 
@@ -92,8 +98,13 @@
    *
    * @param timeSeconds The point in time since the beginning of the trajectory to sample.
    * @return The state at that point in time.
+   * @throws IllegalStateException if the trajectory has no states.
    */
   public State sample(double timeSeconds) {
+    if (m_states.isEmpty()) {
+      throw new IllegalStateException("Trajectory cannot be sampled if it has no states.");
+    }
+
     if (timeSeconds <= m_states.get(0).timeSeconds) {
       return m_states.get(0);
     }
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java
index cf9b6f8..a741aa0 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java
@@ -271,11 +271,7 @@
       if (minMaxAccel.minAccelerationMetersPerSecondSq
           > minMaxAccel.maxAccelerationMetersPerSecondSq) {
         throw new TrajectoryGenerationException(
-            "The constraint's min acceleration "
-                + "was greater than its max acceleration.\n Offending Constraint: "
-                + constraint.getClass().getName()
-                + "\n If the offending constraint was packaged with WPILib, please file a bug"
-                + " report.");
+            "Infeasible trajectory constraint: " + constraint.getClass().getName() + "\n");
       }
 
       state.minAccelerationMetersPerSecondSq =
diff --git a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java
index 3b6559e..fd7494c 100644
--- a/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java
+++ b/third_party/allwpilib/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java
@@ -22,14 +22,14 @@
  *   new TrapezoidProfile.Constraints(kMaxV, kMaxA);
  * TrapezoidProfile.State previousProfiledReference =
  *   new TrapezoidProfile.State(initialReference, 0.0);
+ * TrapezoidProfile profile = new TrapezoidProfile(constraints);
  * </code></pre>
  *
  * <p>Run on update:
  *
  * <pre><code>
- * TrapezoidProfile profile =
- *   new TrapezoidProfile(constraints, unprofiledReference, previousProfiledReference);
- * previousProfiledReference = profile.calculate(timeSincePreviousUpdate);
+ * previousProfiledReference =
+ * profile.calculate(timeSincePreviousUpdate, unprofiledReference, previousProfiledReference);
  * </code></pre>
  *
  * <p>where `unprofiledReference` is free to change between calls. Note that when the unprofiled
@@ -42,9 +42,10 @@
   // The direction of the profile, either 1 for forwards or -1 for inverted
   private int m_direction;
 
-  private Constraints m_constraints;
-  private State m_initial;
-  private State m_goal;
+  private final Constraints m_constraints;
+  private State m_current;
+  private State m_goal; // TODO: Remove
+  private final boolean m_newAPI; // TODO: Remove
 
   private double m_endAccel;
   private double m_endFullSpeed;
@@ -100,23 +101,36 @@
    * Construct a TrapezoidProfile.
    *
    * @param constraints The constraints on the profile, like maximum velocity.
+   */
+  public TrapezoidProfile(Constraints constraints) {
+    m_constraints = constraints;
+    m_newAPI = true;
+  }
+
+  /**
+   * Construct a TrapezoidProfile.
+   *
+   * @param constraints The constraints on the profile, like maximum velocity.
    * @param goal The desired state when the profile is complete.
    * @param initial The initial state (usually the current state).
+   * @deprecated Pass the desired and current state into calculate instead of constructing a new
+   *     TrapezoidProfile with the desired and current state
    */
+  @Deprecated(since = "2024", forRemoval = true)
   public TrapezoidProfile(Constraints constraints, State goal, State initial) {
     m_direction = shouldFlipAcceleration(initial, goal) ? -1 : 1;
     m_constraints = constraints;
-    m_initial = direct(initial);
+    m_current = direct(initial);
     m_goal = direct(goal);
-
-    if (m_initial.velocity > m_constraints.maxVelocity) {
-      m_initial.velocity = m_constraints.maxVelocity;
+    m_newAPI = false;
+    if (m_current.velocity > m_constraints.maxVelocity) {
+      m_current.velocity = m_constraints.maxVelocity;
     }
 
     // Deal with a possibly truncated motion profile (with nonzero initial or
     // final velocity) by calculating the parameters as if the profile began and
     // ended at zero velocity
-    double cutoffBegin = m_initial.velocity / m_constraints.maxAcceleration;
+    double cutoffBegin = m_current.velocity / m_constraints.maxAcceleration;
     double cutoffDistBegin = cutoffBegin * cutoffBegin * m_constraints.maxAcceleration / 2.0;
 
     double cutoffEnd = m_goal.velocity / m_constraints.maxAcceleration;
@@ -126,7 +140,7 @@
     // of a truncated one
 
     double fullTrapezoidDist =
-        cutoffDistBegin + (m_goal.position - m_initial.position) + cutoffDistEnd;
+        cutoffDistBegin + (m_goal.position - m_current.position) + cutoffDistEnd;
     double accelerationTime = m_constraints.maxVelocity / m_constraints.maxAcceleration;
 
     double fullSpeedDist =
@@ -148,7 +162,10 @@
    *
    * @param constraints The constraints on the profile, like maximum velocity.
    * @param goal The desired state when the profile is complete.
+   * @deprecated Pass the desired and current state into calculate instead of constructing a new
+   *     TrapezoidProfile with the desired and current state
    */
+  @Deprecated(since = "2024", forRemoval = true)
   public TrapezoidProfile(Constraints constraints, State goal) {
     this(constraints, goal, new State(0, 0));
   }
@@ -159,17 +176,23 @@
    *
    * @param t The time since the beginning of the profile.
    * @return The position and velocity of the profile at time t.
+   * @deprecated Pass the desired and current state into calculate instead of constructing a new
+   *     TrapezoidProfile with the desired and current state
    */
+  @Deprecated(since = "2024", forRemoval = true)
   public State calculate(double t) {
-    State result = new State(m_initial.position, m_initial.velocity);
+    if (m_newAPI) {
+      throw new RuntimeException("Cannot use new constructor with deprecated calculate()");
+    }
+    State result = new State(m_current.position, m_current.velocity);
 
     if (t < m_endAccel) {
       result.velocity += t * m_constraints.maxAcceleration;
-      result.position += (m_initial.velocity + t * m_constraints.maxAcceleration / 2.0) * t;
+      result.position += (m_current.velocity + t * m_constraints.maxAcceleration / 2.0) * t;
     } else if (t < m_endFullSpeed) {
       result.velocity = m_constraints.maxVelocity;
       result.position +=
-          (m_initial.velocity + m_endAccel * m_constraints.maxAcceleration / 2.0) * m_endAccel
+          (m_current.velocity + m_endAccel * m_constraints.maxAcceleration / 2.0) * m_endAccel
               + m_constraints.maxVelocity * (t - m_endAccel);
     } else if (t <= m_endDeccel) {
       result.velocity = m_goal.velocity + (m_endDeccel - t) * m_constraints.maxAcceleration;
@@ -185,14 +208,83 @@
   }
 
   /**
+   * Calculate the correct position and velocity for the profile at a time t where the beginning of
+   * the profile was at time t = 0.
+   *
+   * @param t The time since the beginning of the profile.
+   * @param goal The desired state when the profile is complete.
+   * @param current The current state.
+   * @return The position and velocity of the profile at time t.
+   */
+  public State calculate(double t, State goal, State current) {
+    m_direction = shouldFlipAcceleration(current, goal) ? -1 : 1;
+    m_current = direct(current);
+    goal = direct(goal);
+
+    if (m_current.velocity > m_constraints.maxVelocity) {
+      m_current.velocity = m_constraints.maxVelocity;
+    }
+
+    // Deal with a possibly truncated motion profile (with nonzero initial or
+    // final velocity) by calculating the parameters as if the profile began and
+    // ended at zero velocity
+    double cutoffBegin = m_current.velocity / m_constraints.maxAcceleration;
+    double cutoffDistBegin = cutoffBegin * cutoffBegin * m_constraints.maxAcceleration / 2.0;
+
+    double cutoffEnd = goal.velocity / m_constraints.maxAcceleration;
+    double cutoffDistEnd = cutoffEnd * cutoffEnd * m_constraints.maxAcceleration / 2.0;
+
+    // Now we can calculate the parameters as if it was a full trapezoid instead
+    // of a truncated one
+
+    double fullTrapezoidDist =
+        cutoffDistBegin + (goal.position - m_current.position) + cutoffDistEnd;
+    double accelerationTime = m_constraints.maxVelocity / m_constraints.maxAcceleration;
+
+    double fullSpeedDist =
+        fullTrapezoidDist - accelerationTime * accelerationTime * m_constraints.maxAcceleration;
+
+    // Handle the case where the profile never reaches full speed
+    if (fullSpeedDist < 0) {
+      accelerationTime = Math.sqrt(fullTrapezoidDist / m_constraints.maxAcceleration);
+      fullSpeedDist = 0;
+    }
+
+    m_endAccel = accelerationTime - cutoffBegin;
+    m_endFullSpeed = m_endAccel + fullSpeedDist / m_constraints.maxVelocity;
+    m_endDeccel = m_endFullSpeed + accelerationTime - cutoffEnd;
+    State result = new State(m_current.position, m_current.velocity);
+
+    if (t < m_endAccel) {
+      result.velocity += t * m_constraints.maxAcceleration;
+      result.position += (m_current.velocity + t * m_constraints.maxAcceleration / 2.0) * t;
+    } else if (t < m_endFullSpeed) {
+      result.velocity = m_constraints.maxVelocity;
+      result.position +=
+          (m_current.velocity + m_endAccel * m_constraints.maxAcceleration / 2.0) * m_endAccel
+              + m_constraints.maxVelocity * (t - m_endAccel);
+    } else if (t <= m_endDeccel) {
+      result.velocity = goal.velocity + (m_endDeccel - t) * m_constraints.maxAcceleration;
+      double timeLeft = m_endDeccel - t;
+      result.position =
+          goal.position
+              - (goal.velocity + timeLeft * m_constraints.maxAcceleration / 2.0) * timeLeft;
+    } else {
+      result = goal;
+    }
+
+    return direct(result);
+  }
+
+  /**
    * Returns the time left until a target distance in the profile is reached.
    *
    * @param target The target distance.
    * @return The time left until a target distance in the profile is reached.
    */
   public double timeLeftUntil(double target) {
-    double position = m_initial.position * m_direction;
-    double velocity = m_initial.velocity * m_direction;
+    double position = m_current.position * m_direction;
+    double velocity = m_current.velocity * m_direction;
 
     double endAccel = m_endAccel * m_direction;
     double endFullSpeed = m_endFullSpeed * m_direction - endAccel;
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/MathShared.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/MathShared.cpp
index 5252e87..b53d080 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/MathShared.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/MathShared.cpp
@@ -5,6 +5,9 @@
 #include "wpimath/MathShared.h"
 
 #include <wpi/mutex.h>
+#include <wpi/timestamp.h>
+
+#include "units/time.h"
 
 using namespace wpi::math;
 
@@ -15,6 +18,9 @@
   void ReportWarningV(fmt::string_view format, fmt::format_args args) override {
   }
   void ReportUsage(MathUsageId id, int count) override {}
+  units::second_t GetTimestamp() override {
+    return units::second_t{wpi::Now() * 1.0e-6};
+  }
 };
 }  // namespace
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/StateSpaceUtil.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/StateSpaceUtil.cpp
index fc532db..758d2e7 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/StateSpaceUtil.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/StateSpaceUtil.cpp
@@ -6,16 +6,16 @@
 
 namespace frc {
 
-Vectord<3> PoseTo3dVector(const Pose2d& pose) {
-  return Vectord<3>{pose.Translation().X().value(),
-                    pose.Translation().Y().value(),
-                    pose.Rotation().Radians().value()};
+Eigen::Vector3d PoseTo3dVector(const Pose2d& pose) {
+  return Eigen::Vector3d{pose.Translation().X().value(),
+                         pose.Translation().Y().value(),
+                         pose.Rotation().Radians().value()};
 }
 
-Vectord<4> PoseTo4dVector(const Pose2d& pose) {
-  return Vectord<4>{pose.Translation().X().value(),
-                    pose.Translation().Y().value(), pose.Rotation().Cos(),
-                    pose.Rotation().Sin()};
+Eigen::Vector4d PoseTo4dVector(const Pose2d& pose) {
+  return Eigen::Vector4d{pose.Translation().X().value(),
+                         pose.Translation().Y().value(), pose.Rotation().Cos(),
+                         pose.Rotation().Sin()};
 }
 
 template <>
@@ -28,9 +28,15 @@
   return detail::IsStabilizableImpl<2, 1>(A, B);
 }
 
-Vectord<3> PoseToVector(const Pose2d& pose) {
-  return Vectord<3>{pose.X().value(), pose.Y().value(),
-                    pose.Rotation().Radians().value()};
+template <>
+bool IsStabilizable<Eigen::Dynamic, Eigen::Dynamic>(const Eigen::MatrixXd& A,
+                                                    const Eigen::MatrixXd& B) {
+  return detail::IsStabilizableImpl<Eigen::Dynamic, Eigen::Dynamic>(A, B);
+}
+
+Eigen::Vector3d PoseToVector(const Pose2d& pose) {
+  return Eigen::Vector3d{pose.X().value(), pose.Y().value(),
+                         pose.Rotation().Radians().value()};
 }
 
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/BangBangController.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/BangBangController.cpp
index a4fda0a..4a093d6 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/BangBangController.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/BangBangController.cpp
@@ -11,10 +11,7 @@
 using namespace frc;
 
 BangBangController::BangBangController(double tolerance)
-    : m_tolerance(tolerance) {
-  static int instances = 0;
-  instances++;
-}
+    : m_tolerance(tolerance) {}
 
 void BangBangController::SetSetpoint(double setpoint) {
   m_setpoint = setpoint;
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/DifferentialDriveAccelerationLimiter.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/DifferentialDriveAccelerationLimiter.cpp
index 9788023..05be645 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/DifferentialDriveAccelerationLimiter.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/DifferentialDriveAccelerationLimiter.cpp
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "Eigen/QR"
+#include <Eigen/QR>
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/DifferentialDriveFeedforward.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/DifferentialDriveFeedforward.cpp
index e1b2732..257d7e1 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/DifferentialDriveFeedforward.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/DifferentialDriveFeedforward.cpp
@@ -4,7 +4,8 @@
 
 #include "frc/controller/DifferentialDriveFeedforward.h"
 
-#include "frc/EigenCore.h"
+#include <Eigen/Core>
+
 #include "frc/controller/LinearPlantInversionFeedforward.h"
 #include "frc/system/plant/LinearSystemId.h"
 
@@ -30,8 +31,8 @@
     units::meters_per_second_t nextRightVelocity, units::second_t dt) {
   frc::LinearPlantInversionFeedforward<2, 2> feedforward{m_plant, dt};
 
-  frc::Vectord<2> r{currentLeftVelocity, currentRightVelocity};
-  frc::Vectord<2> nextR{nextLeftVelocity, nextRightVelocity};
+  Eigen::Vector2d r{currentLeftVelocity, currentRightVelocity};
+  Eigen::Vector2d nextR{nextLeftVelocity, nextRightVelocity};
   auto u = feedforward.Calculate(r, nextR);
   return {units::volt_t{u(0)}, units::volt_t{u(1)}};
 }
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/HolonomicDriveController.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/HolonomicDriveController.cpp
index def7234..64210a7 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/HolonomicDriveController.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/HolonomicDriveController.cpp
@@ -11,7 +11,7 @@
 using namespace frc;
 
 HolonomicDriveController::HolonomicDriveController(
-    frc2::PIDController xController, frc2::PIDController yController,
+    PIDController xController, PIDController yController,
     ProfiledPIDController<units::radian> thetaController)
     : m_xController(std::move(xController)),
       m_yController(std::move(yController)),
@@ -79,3 +79,16 @@
 void HolonomicDriveController::SetEnabled(bool enabled) {
   m_enabled = enabled;
 }
+
+ProfiledPIDController<units::radian>&
+HolonomicDriveController::getThetaController() {
+  return m_thetaController;
+}
+
+PIDController& HolonomicDriveController::getXController() {
+  return m_xController;
+}
+
+PIDController& HolonomicDriveController::getYController() {
+  return m_yController;
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/LTVDifferentialDriveController.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/LTVDifferentialDriveController.cpp
index 17a0c56..173f2ea 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/LTVDifferentialDriveController.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/LTVDifferentialDriveController.cpp
@@ -5,10 +5,14 @@
 #include "frc/controller/LTVDifferentialDriveController.h"
 
 #include <cmath>
+#include <stdexcept>
 
+#include <Eigen/Cholesky>
+
+#include "frc/DARE.h"
 #include "frc/MathUtil.h"
 #include "frc/StateSpaceUtil.h"
-#include "frc/controller/LinearQuadraticRegulator.h"
+#include "frc/system/Discretization.h"
 
 using namespace frc;
 
@@ -63,18 +67,39 @@
   // Ax = -Bu
   // x = -A⁻¹Bu
   units::meters_per_second_t maxV{
+      // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
       -plant.A().householderQr().solve(plant.B() * Vectord<2>{12.0, 12.0})(0)};
 
+  if (maxV <= 0_mps) {
+    throw std::domain_error(
+        "Max velocity of plant with 12 V input must be greater than 0 m/s.");
+  }
+  if (maxV >= 15_mps) {
+    throw std::domain_error(
+        "Max velocity of plant with 12 V input must be less than 15 m/s.");
+  }
+
+  auto R_llt = R.llt();
+
   for (auto velocity = -maxV; velocity < maxV; velocity += 0.01_mps) {
     // The DARE is ill-conditioned if the velocity is close to zero, so don't
     // let the system stop.
     if (units::math::abs(velocity) < 1e-4_mps) {
-      m_table.insert(velocity, Matrixd<2, 5>::Zero());
+      A(State::kY, State::kHeading) = 1e-4;
     } else {
       A(State::kY, State::kHeading) = velocity.value();
-      m_table.insert(velocity,
-                     frc::LinearQuadraticRegulator<5, 2>{A, B, Q, R, dt}.K());
     }
+
+    Matrixd<5, 5> discA;
+    Matrixd<5, 2> discB;
+    DiscretizeAB(A, B, dt, &discA, &discB);
+
+    Matrixd<5, 5> S = detail::DARE<5, 2>(discA, discB, Q, R_llt);
+
+    // K = (BᵀSB + R)⁻¹BᵀSA
+    m_table.insert(velocity, (discB.transpose() * S * discB + R)
+                                 .llt()
+                                 .solve(discB.transpose() * S * discA));
   }
 }
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/LTVUnicycleController.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/LTVUnicycleController.cpp
index 256b757..aca317a 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/LTVUnicycleController.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/LTVUnicycleController.cpp
@@ -4,8 +4,13 @@
 
 #include "frc/controller/LTVUnicycleController.h"
 
+#include <stdexcept>
+
+#include <Eigen/Cholesky>
+
+#include "frc/DARE.h"
 #include "frc/StateSpaceUtil.h"
-#include "frc/controller/LinearQuadraticRegulator.h"
+#include "frc/system/Discretization.h"
 #include "units/math.h"
 
 using namespace frc;
@@ -37,6 +42,13 @@
 LTVUnicycleController::LTVUnicycleController(
     const wpi::array<double, 3>& Qelems, const wpi::array<double, 2>& Relems,
     units::second_t dt, units::meters_per_second_t maxVelocity) {
+  if (maxVelocity <= 0_mps) {
+    throw std::domain_error("Max velocity must be greater than 0 m/s.");
+  }
+  if (maxVelocity >= 15_mps) {
+    throw std::domain_error("Max velocity must be less than 15 m/s.");
+  }
+
   // The change in global pose for a unicycle is defined by the following three
   // equations.
   //
@@ -74,17 +86,28 @@
   Matrixd<3, 3> Q = frc::MakeCostMatrix(Qelems);
   Matrixd<2, 2> R = frc::MakeCostMatrix(Relems);
 
+  auto R_llt = R.llt();
+
   for (auto velocity = -maxVelocity; velocity < maxVelocity;
        velocity += 0.01_mps) {
     // The DARE is ill-conditioned if the velocity is close to zero, so don't
     // let the system stop.
     if (units::math::abs(velocity) < 1e-4_mps) {
-      m_table.insert(velocity, Matrixd<2, 3>::Zero());
+      A(State::kY, State::kHeading) = 1e-4;
     } else {
       A(State::kY, State::kHeading) = velocity.value();
-      m_table.insert(velocity,
-                     frc::LinearQuadraticRegulator<3, 2>{A, B, Q, R, dt}.K());
     }
+
+    Matrixd<3, 3> discA;
+    Matrixd<3, 2> discB;
+    DiscretizeAB(A, B, dt, &discA, &discB);
+
+    Matrixd<3, 3> S = detail::DARE<3, 2>(discA, discB, Q, R_llt);
+
+    // K = (BᵀSB + R)⁻¹BᵀSA
+    m_table.insert(velocity, (discB.transpose() * S * discB + R)
+                                 .llt()
+                                 .solve(discB.transpose() * S * discA));
   }
 }
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/PIDController.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/PIDController.cpp
index 6c89d25..2638f9e 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/controller/PIDController.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/controller/PIDController.cpp
@@ -13,7 +13,7 @@
 #include "frc/MathUtil.h"
 #include "wpimath/MathShared.h"
 
-using namespace frc2;
+using namespace frc;
 
 PIDController::PIDController(double Kp, double Ki, double Kd,
                              units::second_t period)
@@ -52,6 +52,14 @@
   m_Kd = Kd;
 }
 
+void PIDController::SetIZone(double iZone) {
+  if (iZone < 0) {
+    wpi::math::MathSharedStore::ReportError(
+        "IZone must be a non-negative number, got {}!", iZone);
+  }
+  m_iZone = iZone;
+}
+
 double PIDController::GetP() const {
   return m_Kp;
 }
@@ -64,6 +72,10 @@
   return m_Kd;
 }
 
+double PIDController::GetIZone() const {
+  return m_iZone;
+}
+
 units::second_t PIDController::GetPeriod() const {
   return m_period;
 }
@@ -78,11 +90,12 @@
 
 void PIDController::SetSetpoint(double setpoint) {
   m_setpoint = setpoint;
+  m_haveSetpoint = true;
 
   if (m_continuous) {
     double errorBound = (m_maximumInput - m_minimumInput) / 2.0;
     m_positionError =
-        frc::InputModulus(m_setpoint - m_measurement, -errorBound, errorBound);
+        InputModulus(m_setpoint - m_measurement, -errorBound, errorBound);
   } else {
     m_positionError = m_setpoint - m_measurement;
   }
@@ -95,7 +108,8 @@
 }
 
 bool PIDController::AtSetpoint() const {
-  return std::abs(m_positionError) < m_positionTolerance &&
+  return m_haveMeasurement && m_haveSetpoint &&
+         std::abs(m_positionError) < m_positionTolerance &&
          std::abs(m_velocityError) < m_velocityTolerance;
 }
 
@@ -137,18 +151,23 @@
 double PIDController::Calculate(double measurement) {
   m_measurement = measurement;
   m_prevError = m_positionError;
+  m_haveMeasurement = true;
 
   if (m_continuous) {
     double errorBound = (m_maximumInput - m_minimumInput) / 2.0;
     m_positionError =
-        frc::InputModulus(m_setpoint - m_measurement, -errorBound, errorBound);
+        InputModulus(m_setpoint - m_measurement, -errorBound, errorBound);
   } else {
     m_positionError = m_setpoint - m_measurement;
   }
 
   m_velocityError = (m_positionError - m_prevError) / m_period.value();
 
-  if (m_Ki != 0) {
+  // If the absolute value of the position error is outside of IZone, reset the
+  // total error
+  if (std::abs(m_positionError) > m_iZone) {
+    m_totalError = 0;
+  } else if (m_Ki != 0) {
     m_totalError =
         std::clamp(m_totalError + m_positionError * m_period.value(),
                    m_minimumIntegral / m_Ki, m_maximumIntegral / m_Ki);
@@ -159,6 +178,7 @@
 
 double PIDController::Calculate(double measurement, double setpoint) {
   m_setpoint = setpoint;
+  m_haveSetpoint = true;
   return Calculate(measurement);
 }
 
@@ -167,6 +187,7 @@
   m_prevError = 0;
   m_totalError = 0;
   m_velocityError = 0;
+  m_haveMeasurement = false;
 }
 
 void PIDController::InitSendable(wpi::SendableBuilder& builder) {
@@ -178,6 +199,9 @@
   builder.AddDoubleProperty(
       "d", [this] { return GetD(); }, [this](double value) { SetD(value); });
   builder.AddDoubleProperty(
+      "izone", [this] { return GetIZone(); },
+      [this](double value) { SetIZone(value); });
+  builder.AddDoubleProperty(
       "setpoint", [this] { return GetSetpoint(); },
       [this](double value) { SetSetpoint(value); });
 }
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/estimator/DifferentialDrivePoseEstimator.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/estimator/DifferentialDrivePoseEstimator.cpp
index 39e0d8c..213ba99 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/estimator/DifferentialDrivePoseEstimator.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/estimator/DifferentialDrivePoseEstimator.cpp
@@ -4,40 +4,8 @@
 
 #include "frc/estimator/DifferentialDrivePoseEstimator.h"
 
-#include <wpi/timestamp.h>
-
-#include "frc/StateSpaceUtil.h"
-#include "frc/estimator/AngleStatistics.h"
-
 using namespace frc;
 
-DifferentialDrivePoseEstimator::InterpolationRecord
-DifferentialDrivePoseEstimator::InterpolationRecord::Interpolate(
-    DifferentialDriveKinematics& kinematics, InterpolationRecord endValue,
-    double i) const {
-  if (i < 0) {
-    return *this;
-  } else if (i > 1) {
-    return endValue;
-  } else {
-    // Find the interpolated left distance.
-    auto left = wpi::Lerp(this->leftDistance, endValue.leftDistance, i);
-    // Find the interpolated right distance.
-    auto right = wpi::Lerp(this->rightDistance, endValue.rightDistance, i);
-
-    // Find the new gyro angle.
-    auto gyro = wpi::Lerp(this->gyroAngle, endValue.gyroAngle, i);
-
-    // Create a twist to represent this changed based on the interpolated
-    // sensor inputs.
-    auto twist =
-        kinematics.ToTwist2d(left - leftDistance, right - rightDistance);
-    twist.dtheta = (gyro - gyroAngle).Radians();
-
-    return {pose.Exp(twist), gyro, left, right};
-  }
-}
-
 DifferentialDrivePoseEstimator::DifferentialDrivePoseEstimator(
     DifferentialDriveKinematics& kinematics, const Rotation2d& gyroAngle,
     units::meter_t leftDistance, units::meter_t rightDistance,
@@ -51,114 +19,7 @@
     units::meter_t leftDistance, units::meter_t rightDistance,
     const Pose2d& initialPose, const wpi::array<double, 3>& stateStdDevs,
     const wpi::array<double, 3>& visionMeasurementStdDevs)
-    : m_kinematics{kinematics},
-      m_odometry{gyroAngle, leftDistance, rightDistance, initialPose} {
-  for (size_t i = 0; i < 3; ++i) {
-    m_q[i] = stateStdDevs[i] * stateStdDevs[i];
-  }
-
-  SetVisionMeasurementStdDevs(visionMeasurementStdDevs);
-}
-
-void DifferentialDrivePoseEstimator::SetVisionMeasurementStdDevs(
-    const wpi::array<double, 3>& visionMeasurementStdDevs) {
-  wpi::array<double, 3> r{wpi::empty_array};
-  for (size_t i = 0; i < 3; ++i) {
-    r[i] = visionMeasurementStdDevs[i] * visionMeasurementStdDevs[i];
-  }
-
-  // Solve for closed form Kalman gain for continuous Kalman filter with A = 0
-  // and C = I. See wpimath/algorithms.md.
-  for (size_t row = 0; row < 3; ++row) {
-    if (m_q[row] == 0.0) {
-      m_visionK(row, row) = 0.0;
-    } else {
-      m_visionK(row, row) =
-          m_q[row] / (m_q[row] + std::sqrt(m_q[row] * r[row]));
-    }
-  }
-}
-
-void DifferentialDrivePoseEstimator::ResetPosition(const Rotation2d& gyroAngle,
-                                                   units::meter_t leftDistance,
-                                                   units::meter_t rightDistance,
-                                                   const Pose2d& pose) {
-  // Reset state estimate and error covariance
-  m_odometry.ResetPosition(gyroAngle, leftDistance, rightDistance, pose);
-  m_poseBuffer.Clear();
-}
-
-Pose2d DifferentialDrivePoseEstimator::GetEstimatedPosition() const {
-  return m_odometry.GetPose();
-}
-
-void DifferentialDrivePoseEstimator::AddVisionMeasurement(
-    const Pose2d& visionRobotPose, units::second_t timestamp) {
-  // Step 1: Get the estimated pose from when the vision measurement was made.
-  auto sample = m_poseBuffer.Sample(timestamp);
-
-  if (!sample.has_value()) {
-    return;
-  }
-
-  // Step 2: Measure the twist between the odometry pose and the vision pose.
-  auto twist = sample.value().pose.Log(visionRobotPose);
-
-  // Step 3: We should not trust the twist entirely, so instead we scale this
-  // twist by a Kalman gain matrix representing how much we trust vision
-  // measurements compared to our current pose.
-  frc::Vectord<3> k_times_twist =
-      m_visionK *
-      frc::Vectord<3>{twist.dx.value(), twist.dy.value(), twist.dtheta.value()};
-
-  // Step 4: Convert back to Twist2d.
-  Twist2d scaledTwist{units::meter_t{k_times_twist(0)},
-                      units::meter_t{k_times_twist(1)},
-                      units::radian_t{k_times_twist(2)}};
-
-  // Step 5: Reset Odometry to state at sample with vision adjustment.
-  m_odometry.ResetPosition(
-      sample.value().gyroAngle, sample.value().leftDistance,
-      sample.value().rightDistance, sample.value().pose.Exp(scaledTwist));
-
-  // Step 6: Replay odometry inputs between sample time and latest recorded
-  // sample to update the pose buffer and correct odometry.
-  auto internal_buf = m_poseBuffer.GetInternalBuffer();
-
-  auto first_newer_record =
-      std::lower_bound(internal_buf.begin(), internal_buf.end(), timestamp,
-                       [](const auto& pair, auto t) { return t > pair.first; });
-
-  for (auto entry = first_newer_record + 1; entry != internal_buf.end();
-       entry++) {
-    UpdateWithTime(entry->first, entry->second.gyroAngle,
-                   entry->second.leftDistance, entry->second.rightDistance);
-  }
-}
-
-Pose2d DifferentialDrivePoseEstimator::Update(const Rotation2d& gyroAngle,
-                                              units::meter_t leftDistance,
-                                              units::meter_t rightDistance) {
-  return UpdateWithTime(units::microsecond_t(wpi::Now()), gyroAngle,
-                        leftDistance, rightDistance);
-}
-
-Pose2d DifferentialDrivePoseEstimator::UpdateWithTime(
-    units::second_t currentTime, const Rotation2d& gyroAngle,
-    units::meter_t leftDistance, units::meter_t rightDistance) {
-  m_odometry.Update(gyroAngle, leftDistance, rightDistance);
-
-  // fmt::print("odo, {}, {}, {}, {}, {}, {}\n",
-  //   gyroAngle.Radians(),
-  //   leftDistance,
-  //   rightDistance,
-  //   GetEstimatedPosition().X(),
-  //   GetEstimatedPosition().Y(),
-  //   GetEstimatedPosition().Rotation().Radians()
-  // );
-
-  m_poseBuffer.AddSample(currentTime, {GetEstimatedPosition(), gyroAngle,
-                                       leftDistance, rightDistance});
-
-  return GetEstimatedPosition();
-}
+    : PoseEstimator<DifferentialDriveWheelSpeeds,
+                    DifferentialDriveWheelPositions>(
+          kinematics, m_odometryImpl, stateStdDevs, visionMeasurementStdDevs),
+      m_odometryImpl{gyroAngle, leftDistance, rightDistance, initialPose} {}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/estimator/MecanumDrivePoseEstimator.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/estimator/MecanumDrivePoseEstimator.cpp
index 9642e08..7c75e71 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/estimator/MecanumDrivePoseEstimator.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/estimator/MecanumDrivePoseEstimator.cpp
@@ -8,49 +8,10 @@
 
 #include "frc/StateSpaceUtil.h"
 #include "frc/estimator/AngleStatistics.h"
+#include "wpimath/MathShared.h"
 
 using namespace frc;
 
-frc::MecanumDrivePoseEstimator::InterpolationRecord
-frc::MecanumDrivePoseEstimator::InterpolationRecord::Interpolate(
-    MecanumDriveKinematics& kinematics, InterpolationRecord endValue,
-    double i) const {
-  if (i < 0) {
-    return *this;
-  } else if (i > 1) {
-    return endValue;
-  } else {
-    // Find the new wheel distance measurements.
-    MecanumDriveWheelPositions wheels_lerp{
-        wpi::Lerp(this->wheelPositions.frontLeft,
-                  endValue.wheelPositions.frontLeft, i),
-        wpi::Lerp(this->wheelPositions.frontRight,
-                  endValue.wheelPositions.frontRight, i),
-        wpi::Lerp(this->wheelPositions.rearLeft,
-                  endValue.wheelPositions.rearLeft, i),
-        wpi::Lerp(this->wheelPositions.rearRight,
-                  endValue.wheelPositions.rearRight, i)};
-
-    // Find the distance between this measurement and the
-    // interpolated measurement.
-    MecanumDriveWheelPositions wheels_delta{
-        wheels_lerp.frontLeft - this->wheelPositions.frontLeft,
-        wheels_lerp.frontRight - this->wheelPositions.frontRight,
-        wheels_lerp.rearLeft - this->wheelPositions.rearLeft,
-        wheels_lerp.rearRight - this->wheelPositions.rearRight};
-
-    // Find the new gyro angle.
-    auto gyro = wpi::Lerp(this->gyroAngle, endValue.gyroAngle, i);
-
-    // Create a twist to represent this changed based on the interpolated
-    // sensor inputs.
-    auto twist = kinematics.ToTwist2d(wheels_delta);
-    twist.dtheta = (gyro - gyroAngle).Radians();
-
-    return {pose.Exp(twist), gyro, wheels_lerp};
-  }
-}
-
 frc::MecanumDrivePoseEstimator::MecanumDrivePoseEstimator(
     MecanumDriveKinematics& kinematics, const Rotation2d& gyroAngle,
     const MecanumDriveWheelPositions& wheelPositions, const Pose2d& initialPose)
@@ -63,107 +24,6 @@
     const MecanumDriveWheelPositions& wheelPositions, const Pose2d& initialPose,
     const wpi::array<double, 3>& stateStdDevs,
     const wpi::array<double, 3>& visionMeasurementStdDevs)
-    : m_kinematics{kinematics},
-      m_odometry{kinematics, gyroAngle, wheelPositions, initialPose} {
-  for (size_t i = 0; i < 3; ++i) {
-    m_q[i] = stateStdDevs[i] * stateStdDevs[i];
-  }
-
-  SetVisionMeasurementStdDevs(visionMeasurementStdDevs);
-}
-
-void frc::MecanumDrivePoseEstimator::SetVisionMeasurementStdDevs(
-    const wpi::array<double, 3>& visionMeasurementStdDevs) {
-  wpi::array<double, 3> r{wpi::empty_array};
-  for (size_t i = 0; i < 3; ++i) {
-    r[i] = visionMeasurementStdDevs[i] * visionMeasurementStdDevs[i];
-  }
-
-  // Solve for closed form Kalman gain for continuous Kalman filter with A = 0
-  // and C = I. See wpimath/algorithms.md.
-  for (size_t row = 0; row < 3; ++row) {
-    if (m_q[row] == 0.0) {
-      m_visionK(row, row) = 0.0;
-    } else {
-      m_visionK(row, row) =
-          m_q[row] / (m_q[row] + std::sqrt(m_q[row] * r[row]));
-    }
-  }
-}
-
-void frc::MecanumDrivePoseEstimator::ResetPosition(
-    const Rotation2d& gyroAngle,
-    const MecanumDriveWheelPositions& wheelPositions, const Pose2d& pose) {
-  // Reset state estimate and error covariance
-  m_odometry.ResetPosition(gyroAngle, wheelPositions, pose);
-  m_poseBuffer.Clear();
-}
-
-Pose2d frc::MecanumDrivePoseEstimator::GetEstimatedPosition() const {
-  return m_odometry.GetPose();
-}
-
-void frc::MecanumDrivePoseEstimator::AddVisionMeasurement(
-    const Pose2d& visionRobotPose, units::second_t timestamp) {
-  // Step 1: Get the estimated pose from when the vision measurement was made.
-  auto sample = m_poseBuffer.Sample(timestamp);
-
-  if (!sample.has_value()) {
-    return;
-  }
-
-  // Step 2: Measure the twist between the odometry pose and the vision pose
-  auto twist = sample.value().pose.Log(visionRobotPose);
-
-  // Step 3: We should not trust the twist entirely, so instead we scale this
-  // twist by a Kalman gain matrix representing how much we trust vision
-  // measurements compared to our current pose.
-  frc::Vectord<3> k_times_twist =
-      m_visionK *
-      frc::Vectord<3>{twist.dx.value(), twist.dy.value(), twist.dtheta.value()};
-
-  // Step 4: Convert back to Twist2d
-  Twist2d scaledTwist{units::meter_t{k_times_twist(0)},
-                      units::meter_t{k_times_twist(1)},
-                      units::radian_t{k_times_twist(2)}};
-
-  // Step 5: Reset Odometry to state at sample with vision adjustment.
-  m_odometry.ResetPosition(sample.value().gyroAngle,
-                           sample.value().wheelPositions,
-                           sample.value().pose.Exp(scaledTwist));
-
-  // Step 6: Replay odometry inputs between sample time and latest recorded
-  // sample to update the pose buffer and correct odometry.
-  auto internal_buf = m_poseBuffer.GetInternalBuffer();
-
-  auto upper_bound =
-      std::lower_bound(internal_buf.begin(), internal_buf.end(), timestamp,
-                       [](const auto& pair, auto t) { return t > pair.first; });
-
-  for (auto entry = upper_bound; entry != internal_buf.end(); entry++) {
-    UpdateWithTime(entry->first, entry->second.gyroAngle,
-                   entry->second.wheelPositions);
-  }
-}
-
-Pose2d frc::MecanumDrivePoseEstimator::Update(
-    const Rotation2d& gyroAngle,
-    const MecanumDriveWheelPositions& wheelPositions) {
-  return UpdateWithTime(units::microsecond_t(wpi::Now()), gyroAngle,
-                        wheelPositions);
-}
-
-Pose2d frc::MecanumDrivePoseEstimator::UpdateWithTime(
-    units::second_t currentTime, const Rotation2d& gyroAngle,
-    const MecanumDriveWheelPositions& wheelPositions) {
-  m_odometry.Update(gyroAngle, wheelPositions);
-
-  MecanumDriveWheelPositions internalWheelPositions{
-      wheelPositions.frontLeft, wheelPositions.frontRight,
-      wheelPositions.rearLeft, wheelPositions.rearRight};
-
-  m_poseBuffer.AddSample(
-      currentTime, {GetEstimatedPosition(), gyroAngle, internalWheelPositions});
-
-  return GetEstimatedPosition();
-}
+    : PoseEstimator<MecanumDriveWheelSpeeds, MecanumDriveWheelPositions>(
+          kinematics, m_odometryImpl, stateStdDevs, visionMeasurementStdDevs),
+      m_odometryImpl(kinematics, gyroAngle, wheelPositions, initialPose) {}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/filter/Debouncer.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/filter/Debouncer.cpp
index 4e909a2..841768e 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/filter/Debouncer.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/filter/Debouncer.cpp
@@ -4,6 +4,8 @@
 
 #include "frc/filter/Debouncer.h"
 
+#include "wpimath/MathShared.h"
+
 using namespace frc;
 
 Debouncer::Debouncer(units::second_t debounceTime, DebounceType type)
@@ -21,11 +23,12 @@
 }
 
 void Debouncer::ResetTimer() {
-  m_prevTime = units::microsecond_t(wpi::Now());
+  m_prevTime = wpi::math::MathSharedStore::GetTimestamp();
 }
 
 bool Debouncer::HasElapsed() const {
-  return units::microsecond_t(wpi::Now()) - m_prevTime >= m_debounceTime;
+  return wpi::math::MathSharedStore::GetTimestamp() - m_prevTime >=
+         m_debounceTime;
 }
 
 bool Debouncer::Calculate(bool input) {
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/CoordinateSystem.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/CoordinateSystem.cpp
index d1ee93d..2a8c9db 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/CoordinateSystem.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/CoordinateSystem.cpp
@@ -8,7 +8,8 @@
 #include <stdexcept>
 #include <utility>
 
-#include "Eigen/QR"
+#include <Eigen/Core>
+#include <Eigen/QR>
 
 using namespace frc;
 
@@ -18,7 +19,7 @@
   // Construct a change of basis matrix from the source coordinate system to the
   // NWU coordinate system. Each column vector in the change of basis matrix is
   // one of the old basis vectors mapped to its representation in the new basis.
-  Matrixd<3, 3> R;
+  Eigen::Matrix3d R;
   R.block<3, 1>(0, 0) = positiveX.m_axis;
   R.block<3, 1>(0, 1) = positiveY.m_axis;
   R.block<3, 1>(0, 2) = positiveZ.m_axis;
@@ -68,6 +69,8 @@
 Transform3d CoordinateSystem::Convert(const Transform3d& transform,
                                       const CoordinateSystem& from,
                                       const CoordinateSystem& to) {
-  return Transform3d{Convert(transform.Translation(), from, to),
-                     Convert(transform.Rotation(), from, to)};
+  const auto coordRot = from.m_rotation - to.m_rotation;
+  return Transform3d{
+      Convert(transform.Translation(), from, to),
+      (-coordRot).RotateBy(transform.Rotation().RotateBy(coordRot))};
 }
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Pose2d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Pose2d.cpp
index 2648a90..2e15216 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Pose2d.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Pose2d.cpp
@@ -8,6 +8,9 @@
 
 #include <wpi/json.h>
 
+#include "frc/MathUtil.h"
+#include "geometry2d.pb.h"
+
 using namespace frc;
 
 Transform2d Pose2d::operator-(const Pose2d& other) const {
@@ -67,6 +70,36 @@
   return {translationPart.X(), translationPart.Y(), units::radian_t{dtheta}};
 }
 
+Pose2d Pose2d::Nearest(std::span<const Pose2d> poses) const {
+  return *std::min_element(
+      poses.begin(), poses.end(), [this](const Pose2d& a, const Pose2d& b) {
+        auto aDistance = this->Translation().Distance(a.Translation());
+        auto bDistance = this->Translation().Distance(b.Translation());
+
+        // If the distances are equal sort by difference in rotation
+        if (aDistance == bDistance) {
+          return std::abs((this->Rotation() - a.Rotation()).Radians().value()) <
+                 std::abs((this->Rotation() - b.Rotation()).Radians().value());
+        }
+        return aDistance < bDistance;
+      });
+}
+
+Pose2d Pose2d::Nearest(std::initializer_list<Pose2d> poses) const {
+  return *std::min_element(
+      poses.begin(), poses.end(), [this](const Pose2d& a, const Pose2d& b) {
+        auto aDistance = this->Translation().Distance(a.Translation());
+        auto bDistance = this->Translation().Distance(b.Translation());
+
+        // If the distances are equal sort by difference in rotation
+        if (aDistance == bDistance) {
+          return std::abs((this->Rotation() - a.Rotation()).Radians().value()) <
+                 std::abs((this->Rotation() - b.Rotation()).Radians().value());
+        }
+        return aDistance < bDistance;
+      });
+}
+
 void frc::to_json(wpi::json& json, const Pose2d& pose) {
   json = wpi::json{{"translation", pose.Translation()},
                    {"rotation", pose.Rotation()}};
@@ -76,3 +109,23 @@
   pose = Pose2d{json.at("translation").get<Translation2d>(),
                 json.at("rotation").get<Rotation2d>()};
 }
+
+google::protobuf::Message* wpi::Protobuf<frc::Pose2d>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<wpi::proto::ProtobufPose2d>(
+      arena);
+}
+
+frc::Pose2d wpi::Protobuf<frc::Pose2d>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufPose2d*>(&msg);
+  return Pose2d{wpi::UnpackProtobuf<frc::Translation2d>(m->translation()),
+                wpi::UnpackProtobuf<frc::Rotation2d>(m->rotation())};
+}
+
+void wpi::Protobuf<frc::Pose2d>::Pack(google::protobuf::Message* msg,
+                                      const frc::Pose2d& value) {
+  auto m = static_cast<wpi::proto::ProtobufPose2d*>(msg);
+  wpi::PackProtobuf(m->mutable_translation(), value.Translation());
+  wpi::PackProtobuf(m->mutable_rotation(), value.Rotation());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Pose3d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Pose3d.cpp
index 4732c4d..ffbaecb 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Pose3d.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Pose3d.cpp
@@ -6,8 +6,11 @@
 
 #include <cmath>
 
+#include <Eigen/Core>
 #include <wpi/json.h>
 
+#include "geometry3d.pb.h"
+
 using namespace frc;
 
 namespace {
@@ -21,14 +24,14 @@
  * @param rotation The rotation vector.
  * @return The rotation vector as a 3x3 rotation matrix.
  */
-Matrixd<3, 3> RotationVectorToMatrix(const Vectord<3>& rotation) {
+Eigen::Matrix3d RotationVectorToMatrix(const Eigen::Vector3d& rotation) {
   // Given a rotation vector <a, b, c>,
   //         [ 0 -c  b]
   // Omega = [ c  0 -a]
   //         [-b  a  0]
-  return Matrixd<3, 3>{{0.0, -rotation(2), rotation(1)},
-                       {rotation(2), 0.0, -rotation(0)},
-                       {-rotation(1), rotation(0), 0.0}};
+  return Eigen::Matrix3d{{0.0, -rotation(2), rotation(1)},
+                         {rotation(2), 0.0, -rotation(0)},
+                         {-rotation(1), rotation(0), 0.0}};
 }
 }  // namespace
 
@@ -60,6 +63,10 @@
   return *this * (1.0 / scalar);
 }
 
+Pose3d Pose3d::RotateBy(const Rotation3d& other) const {
+  return {m_translation.RotateBy(other), m_rotation.RotateBy(other)};
+}
+
 Pose3d Pose3d::TransformBy(const Transform3d& other) const {
   return {m_translation + (other.Translation().RotateBy(m_rotation)),
           other.Rotation() + m_rotation};
@@ -71,73 +78,102 @@
 }
 
 Pose3d Pose3d::Exp(const Twist3d& twist) const {
-  Matrixd<3, 3> Omega = RotationVectorToMatrix(
-      Vectord<3>{twist.rx.value(), twist.ry.value(), twist.rz.value()});
-  Matrixd<3, 3> OmegaSq = Omega * Omega;
+  // Implementation from Section 3.2 of https://ethaneade.org/lie.pdf
+  Eigen::Vector3d u{twist.dx.value(), twist.dy.value(), twist.dz.value()};
+  Eigen::Vector3d rvec{twist.rx.value(), twist.ry.value(), twist.rz.value()};
+  Eigen::Matrix3d omega = RotationVectorToMatrix(rvec);
+  Eigen::Matrix3d omegaSq = omega * omega;
+  double theta = rvec.norm();
+  double thetaSq = theta * theta;
 
-  double thetaSq =
-      (twist.rx * twist.rx + twist.ry * twist.ry + twist.rz * twist.rz).value();
-
-  // Get left Jacobian of SO3. See first line in right column of
-  // http://asrl.utias.utoronto.ca/~tdb/bib/barfoot_ser17_identities.pdf
-  Matrixd<3, 3> J;
-  if (thetaSq < 1E-9 * 1E-9) {
-    // V = I + 0.5ω
-    J = Matrixd<3, 3>::Identity() + 0.5 * Omega;
+  double A;
+  double B;
+  double C;
+  if (std::abs(theta) < 1E-7) {
+    // Taylor Expansions around θ = 0
+    // A = 1/1! - θ²/3! + θ⁴/5!
+    // B = 1/2! - θ²/4! + θ⁴/6!
+    // C = 1/3! - θ²/5! + θ⁴/7!
+    // sources:
+    // A:
+    // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5Bsin%5C%2840%29x%5C%2841%29%2Cx%5D+at+x%3D0
+    // B:
+    // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5B1-cos%5C%2840%29x%5C%2841%29%2CPower%5Bx%2C2%5D%5D+at+x%3D0
+    // C:
+    // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5B1-Divide%5Bsin%5C%2840%29x%5C%2841%29%2Cx%5D%2CPower%5Bx%2C2%5D%5D+at+x%3D0
+    A = 1 - thetaSq / 6 + thetaSq * thetaSq / 120;
+    B = 1 / 2.0 - thetaSq / 24 + thetaSq * thetaSq / 720;
+    C = 1 / 6.0 - thetaSq / 120 + thetaSq * thetaSq / 5040;
   } else {
-    double theta = std::sqrt(thetaSq);
-    // J = I + (1 − std::cos(θ))/θ² ω + (θ − std::sin(θ))/θ³ ω²
-    J = Matrixd<3, 3>::Identity() + (1.0 - std::cos(theta)) / thetaSq * Omega +
-        (theta - std::sin(theta)) / (thetaSq * theta) * OmegaSq;
+    // A = std::sin(θ)/θ
+    // B = (1 - std::cos(θ)) / θ²
+    // C = (1 - A) / θ²
+    A = std::sin(theta) / theta;
+    B = (1 - std::cos(theta)) / thetaSq;
+    C = (1 - A) / thetaSq;
   }
 
-  // Get translation component
-  Vectord<3> translation =
-      J * Vectord<3>{twist.dx.value(), twist.dy.value(), twist.dz.value()};
+  Eigen::Matrix3d R = Eigen::Matrix3d::Identity() + A * omega + B * omegaSq;
+  Eigen::Matrix3d V = Eigen::Matrix3d::Identity() + B * omega + C * omegaSq;
 
-  const Transform3d transform{Translation3d{units::meter_t{translation(0)},
-                                            units::meter_t{translation(1)},
-                                            units::meter_t{translation(2)}},
-                              Rotation3d{twist.rx, twist.ry, twist.rz}};
+  auto translation_component = V * u;
+  const Transform3d transform{
+      Translation3d{units::meter_t{translation_component(0)},
+                    units::meter_t{translation_component(1)},
+                    units::meter_t{translation_component(2)}},
+      Rotation3d{R}};
 
   return *this + transform;
 }
 
 Twist3d Pose3d::Log(const Pose3d& end) const {
+  // Implementation from Section 3.2 of https://ethaneade.org/lie.pdf
   const auto transform = end.RelativeTo(*this);
 
-  Vectord<3> rotVec = transform.Rotation().GetQuaternion().ToRotationVector();
+  Eigen::Vector3d u{transform.X().value(), transform.Y().value(),
+                    transform.Z().value()};
+  Eigen::Vector3d rvec =
+      transform.Rotation().GetQuaternion().ToRotationVector();
 
-  Matrixd<3, 3> Omega = RotationVectorToMatrix(rotVec);
-  Matrixd<3, 3> OmegaSq = Omega * Omega;
+  Eigen::Matrix3d omega = RotationVectorToMatrix(rvec);
+  Eigen::Matrix3d omegaSq = omega * omega;
+  double theta = rvec.norm();
+  double thetaSq = theta * theta;
 
-  double thetaSq = rotVec.squaredNorm();
-
-  // Get left Jacobian inverse of SO3. See fourth line in right column of
-  // http://asrl.utias.utoronto.ca/~tdb/bib/barfoot_ser17_identities.pdf
-  Matrixd<3, 3> Jinv;
-  if (thetaSq < 1E-9 * 1E-9) {
-    // J⁻¹ = I − 0.5ω + 1/12 ω²
-    Jinv = Matrixd<3, 3>::Identity() - 0.5 * Omega + 1.0 / 12.0 * OmegaSq;
+  double C;
+  if (std::abs(theta) < 1E-7) {
+    // Taylor Expansions around θ = 0
+    // A = 1/1! - θ²/3! + θ⁴/5!
+    // B = 1/2! - θ²/4! + θ⁴/6!
+    // C = 1/6 * (1/2 + θ²/5! + θ⁴/7!)
+    // sources:
+    // A:
+    // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5Bsin%5C%2840%29x%5C%2841%29%2Cx%5D+at+x%3D0
+    // B:
+    // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5B1-cos%5C%2840%29x%5C%2841%29%2CPower%5Bx%2C2%5D%5D+at+x%3D0
+    // C:
+    // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5B1-Divide%5BDivide%5Bsin%5C%2840%29x%5C%2841%29%2Cx%5D%2C2Divide%5B1-cos%5C%2840%29x%5C%2841%29%2CPower%5Bx%2C2%5D%5D%5D%2CPower%5Bx%2C2%5D%5D+at+x%3D0
+    C = 1 / 12.0 + thetaSq / 720 + thetaSq * thetaSq / 30240;
   } else {
-    double theta = std::sqrt(thetaSq);
-    double halfTheta = 0.5 * theta;
-
-    // J⁻¹ = I − 0.5ω + (1 − 0.5θ std::cos(θ/2) / std::sin(θ/2))/θ² ω²
-    Jinv = Matrixd<3, 3>::Identity() - 0.5 * Omega +
-           (1.0 - 0.5 * theta * std::cos(halfTheta) / std::sin(halfTheta)) /
-               thetaSq * OmegaSq;
+    // A = std::sin(θ)/θ
+    // B = (1 - std::cos(θ)) / θ²
+    // C = (1 - A/(2*B)) / θ²
+    double A = std::sin(theta) / theta;
+    double B = (1 - std::cos(theta)) / thetaSq;
+    C = (1 - A / (2 * B)) / thetaSq;
   }
 
-  // Get dtranslation component
-  Vectord<3> dtranslation =
-      Jinv * Vectord<3>{transform.X().value(), transform.Y().value(),
-                        transform.Z().value()};
+  Eigen::Matrix3d V_inv =
+      Eigen::Matrix3d::Identity() - 0.5 * omega + C * omegaSq;
 
-  return Twist3d{
-      units::meter_t{dtranslation(0)}, units::meter_t{dtranslation(1)},
-      units::meter_t{dtranslation(2)}, units::radian_t{rotVec(0)},
-      units::radian_t{rotVec(1)},      units::radian_t{rotVec(2)}};
+  Eigen::Vector3d translation_component = V_inv * u;
+
+  return Twist3d{units::meter_t{translation_component(0)},
+                 units::meter_t{translation_component(1)},
+                 units::meter_t{translation_component(2)},
+                 units::radian_t{rvec(0)},
+                 units::radian_t{rvec(1)},
+                 units::radian_t{rvec(2)}};
 }
 
 Pose2d Pose3d::ToPose2d() const {
@@ -153,3 +189,23 @@
   pose = Pose3d{json.at("translation").get<Translation3d>(),
                 json.at("rotation").get<Rotation3d>()};
 }
+
+google::protobuf::Message* wpi::Protobuf<frc::Pose3d>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<wpi::proto::ProtobufPose3d>(
+      arena);
+}
+
+frc::Pose3d wpi::Protobuf<frc::Pose3d>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufPose3d*>(&msg);
+  return Pose3d{wpi::UnpackProtobuf<frc::Translation3d>(m->translation()),
+                wpi::UnpackProtobuf<frc::Rotation3d>(m->rotation())};
+}
+
+void wpi::Protobuf<frc::Pose3d>::Pack(google::protobuf::Message* msg,
+                                      const frc::Pose3d& value) {
+  auto m = static_cast<wpi::proto::ProtobufPose3d*>(msg);
+  wpi::PackProtobuf(m->mutable_translation(), value.Translation());
+  wpi::PackProtobuf(m->mutable_rotation(), value.Rotation());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Quaternion.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Quaternion.cpp
index 9c2ceda..37afbb8 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Quaternion.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Quaternion.cpp
@@ -4,13 +4,53 @@
 
 #include "frc/geometry/Quaternion.h"
 
+#include <numbers>
+
 #include <wpi/json.h>
 
+#include "geometry3d.pb.h"
+
 using namespace frc;
 
 Quaternion::Quaternion(double w, double x, double y, double z)
     : m_r{w}, m_v{x, y, z} {}
 
+Quaternion Quaternion::operator+(const Quaternion& other) const {
+  return Quaternion{
+      m_r + other.m_r,
+      m_v(0) + other.m_v(0),
+      m_v(1) + other.m_v(1),
+      m_v(2) + other.m_v(2),
+  };
+}
+
+Quaternion Quaternion::operator-(const Quaternion& other) const {
+  return Quaternion{
+      m_r - other.m_r,
+      m_v(0) - other.m_v(0),
+      m_v(1) - other.m_v(1),
+      m_v(2) - other.m_v(2),
+  };
+}
+
+Quaternion Quaternion::operator*(const double other) const {
+  return Quaternion{
+      m_r * other,
+      m_v(0) * other,
+      m_v(1) * other,
+      m_v(2) * other,
+  };
+}
+
+Quaternion Quaternion::operator/(const double other) const {
+  return Quaternion{
+      m_r / other,
+      m_v(0) / other,
+      m_v(1) / other,
+      m_v(2) / other,
+  };
+}
+
 Quaternion Quaternion::operator*(const Quaternion& other) const {
   // https://en.wikipedia.org/wiki/Quaternion#Scalar_and_vector_parts
   const auto& r1 = m_r;
@@ -26,26 +66,102 @@
   // v = r₁v₂ + r₂v₁ + v₁ x v₂
   Eigen::Vector3d v = r1 * v2 + r2 * v1 + cross;
 
-  return Quaternion{r1 * r2 - v1.dot(v2), v(0), v(1), v(2)};
+  return Quaternion{// r = r₁r₂ − v₁ ⋅ v₂
+                    r1 * r2 - v1.dot(v2),
+                    // v = r₁v₂ + r₂v₁ + v₁ x v₂
+                    v(0), v(1), v(2)};
 }
 
 bool Quaternion::operator==(const Quaternion& other) const {
-  return std::abs(m_r * other.m_r + m_v.dot(other.m_v)) > 1.0 - 1E-9;
+  return std::abs(Dot(other) - Norm() * other.Norm()) < 1e-9 &&
+         std::abs(Norm() - other.Norm()) < 1e-9;
+}
+
+Quaternion Quaternion::Conjugate() const {
+  return Quaternion{W(), -X(), -Y(), -Z()};
+}
+
+double Quaternion::Dot(const Quaternion& other) const {
+  return W() * other.W() + m_v.dot(other.m_v);
 }
 
 Quaternion Quaternion::Inverse() const {
-  return Quaternion{m_r, -m_v(0), -m_v(1), -m_v(2)};
+  double norm = Norm();
+  return Conjugate() / (norm * norm);
+}
+
+double Quaternion::Norm() const {
+  return std::sqrt(Dot(*this));
 }
 
 Quaternion Quaternion::Normalize() const {
-  double norm = std::sqrt(W() * W() + X() * X() + Y() * Y() + Z() * Z());
+  double norm = Norm();
   if (norm == 0.0) {
     return Quaternion{};
   } else {
-    return Quaternion{W() / norm, X() / norm, Y() / norm, Z() / norm};
+    return Quaternion{W(), X(), Y(), Z()} / norm;
   }
 }
 
+Quaternion Quaternion::Pow(const double other) const {
+  return (Log() * other).Exp();
+}
+
+Quaternion Quaternion::Exp(const Quaternion& other) const {
+  return other.Exp() * *this;
+}
+
+Quaternion Quaternion::Exp() const {
+  double scalar = std::exp(m_r);
+
+  double axial_magnitude = m_v.norm();
+  double cosine = std::cos(axial_magnitude);
+
+  double axial_scalar;
+
+  if (axial_magnitude < 1e-9) {
+    // Taylor series of sin(x)/x near x=0: 1 − x²/6 + x⁴/120 + O(n⁶)
+    double axial_magnitude_sq = axial_magnitude * axial_magnitude;
+    double axial_magnitude_sq_sq = axial_magnitude_sq * axial_magnitude_sq;
+    axial_scalar =
+        1.0 - axial_magnitude_sq / 6.0 + axial_magnitude_sq_sq / 120.0;
+  } else {
+    axial_scalar = std::sin(axial_magnitude) / axial_magnitude;
+  }
+
+  return Quaternion(cosine * scalar, X() * axial_scalar * scalar,
+                    Y() * axial_scalar * scalar, Z() * axial_scalar * scalar);
+}
+
+Quaternion Quaternion::Log(const Quaternion& other) const {
+  return (other * Inverse()).Log();
+}
+
+Quaternion Quaternion::Log() const {
+  double scalar = std::log(Norm());
+
+  double v_norm = m_v.norm();
+
+  double s_norm = W() / Norm();
+
+  if (std::abs(s_norm + 1) < 1e-9) {
+    return Quaternion{scalar, -std::numbers::pi, 0, 0};
+  }
+
+  double v_scalar;
+
+  if (v_norm < 1e-9) {
+    // Taylor series expansion of atan2(y / x) / y around y = 0 = 1/x -
+    // y^2/3*x^3 + O(y^4)
+    v_scalar = 1.0 / W() - 1.0 / 3.0 * v_norm * v_norm / (W() * W() * W());
+  } else {
+    v_scalar = std::atan2(v_norm, W()) / v_norm;
+  }
+
+  return Quaternion{scalar, v_scalar * m_v(0), v_scalar * m_v(1),
+                    v_scalar * m_v(2)};
+}
+
 double Quaternion::W() const {
   return m_r;
 }
@@ -80,6 +196,30 @@
   }
 }
 
+Quaternion Quaternion::FromRotationVector(const Eigen::Vector3d& rvec) {
+  // 𝑣⃗ = θ * v̂
+  // v̂ = 𝑣⃗ / θ
+
+  // 𝑞 = std::cos(θ/2) + std::sin(θ/2) * v̂
+  // 𝑞 = std::cos(θ/2) + std::sin(θ/2) / θ * 𝑣⃗
+
+  double theta = rvec.norm();
+  double cos = std::cos(theta / 2);
+
+  double axial_scalar;
+
+  if (theta < 1e-9) {
+    // taylor series expansion of sin(θ/2) / θ around θ = 0 = 1/2 - θ²/48 +
+    // O(θ⁴)
+    axial_scalar = 1.0 / 2.0 - theta * theta / 48.0;
+  } else {
+    axial_scalar = std::sin(theta / 2) / theta;
+  }
+
+  return Quaternion{cos, axial_scalar * rvec(0), axial_scalar * rvec(1),
+                    axial_scalar * rvec(2)};
+}
+
 void frc::to_json(wpi::json& json, const Quaternion& quaternion) {
   json = wpi::json{{"W", quaternion.W()},
                    {"X", quaternion.X()},
@@ -92,3 +232,24 @@
       Quaternion{json.at("W").get<double>(), json.at("X").get<double>(),
                  json.at("Y").get<double>(), json.at("Z").get<double>()};
 }
+
+google::protobuf::Message* wpi::Protobuf<frc::Quaternion>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<wpi::proto::ProtobufQuaternion>(
+      arena);
+}
+
+frc::Quaternion wpi::Protobuf<frc::Quaternion>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufQuaternion*>(&msg);
+  return frc::Quaternion{m->w(), m->x(), m->y(), m->z()};
+}
+
+void wpi::Protobuf<frc::Quaternion>::Pack(google::protobuf::Message* msg,
+                                          const frc::Quaternion& value) {
+  auto m = static_cast<wpi::proto::ProtobufQuaternion*>(msg);
+  m->set_w(value.W());
+  m->set_x(value.X());
+  m->set_y(value.Y());
+  m->set_z(value.Z());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp
index 921e1f8..05a644e 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp
@@ -8,6 +8,7 @@
 
 #include <wpi/json.h>
 
+#include "geometry2d.pb.h"
 #include "units/math.h"
 
 using namespace frc;
@@ -19,3 +20,21 @@
 void frc::from_json(const wpi::json& json, Rotation2d& rotation) {
   rotation = Rotation2d{units::radian_t{json.at("radians").get<double>()}};
 }
+
+google::protobuf::Message* wpi::Protobuf<frc::Rotation2d>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<wpi::proto::ProtobufRotation2d>(
+      arena);
+}
+
+frc::Rotation2d wpi::Protobuf<frc::Rotation2d>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufRotation2d*>(&msg);
+  return frc::Rotation2d{units::radian_t{m->value()}};
+}
+
+void wpi::Protobuf<frc::Rotation2d>::Pack(google::protobuf::Message* msg,
+                                          const frc::Rotation2d& value) {
+  auto m = static_cast<wpi::proto::ProtobufRotation2d*>(msg);
+  m->set_value(value.Radians().value());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Rotation3d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Rotation3d.cpp
index 298f0e6..3a68870 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Rotation3d.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Rotation3d.cpp
@@ -7,12 +7,13 @@
 #include <cmath>
 #include <numbers>
 
+#include <Eigen/Core>
+#include <Eigen/LU>
+#include <Eigen/QR>
 #include <wpi/json.h>
 
-#include "Eigen/Core"
-#include "Eigen/LU"
-#include "Eigen/QR"
 #include "frc/fmt/Eigen.h"
+#include "geometry3d.pb.h"
 #include "units/math.h"
 #include "wpimath/MathShared.h"
 
@@ -38,23 +39,26 @@
                    cr * sp * cy + sr * cp * sy, cr * cp * sy - sr * sp * cy};
 }
 
-Rotation3d::Rotation3d(const Vectord<3>& axis, units::radian_t angle) {
+Rotation3d::Rotation3d(const Eigen::Vector3d& rvec)
+    : Rotation3d{rvec, units::radian_t{rvec.norm()}} {}
+
+Rotation3d::Rotation3d(const Eigen::Vector3d& axis, units::radian_t angle) {
   double norm = axis.norm();
   if (norm == 0.0) {
     return;
   }
 
   // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Definition
-  Vectord<3> v = axis / norm * units::math::sin(angle / 2.0);
+  Eigen::Vector3d v = axis / norm * units::math::sin(angle / 2.0);
   m_q = Quaternion{units::math::cos(angle / 2.0), v(0), v(1), v(2)};
 }
 
-Rotation3d::Rotation3d(const Matrixd<3, 3>& rotationMatrix) {
+Rotation3d::Rotation3d(const Eigen::Matrix3d& rotationMatrix) {
   const auto& R = rotationMatrix;
 
   // Require that the rotation matrix is special orthogonal. This is true if the
   // matrix is orthogonal (RRᵀ = I) and normalized (determinant is 1).
-  if ((R * R.transpose() - Matrixd<3, 3>::Identity()).norm() > 1e-9) {
+  if ((R * R.transpose() - Eigen::Matrix3d::Identity()).norm() > 1e-9) {
     std::string msg =
         fmt::format("Rotation matrix isn't orthogonal\n\nR =\n{}\n", R);
 
@@ -109,7 +113,8 @@
   m_q = Quaternion{w, x, y, z};
 }
 
-Rotation3d::Rotation3d(const Vectord<3>& initial, const Vectord<3>& final) {
+Rotation3d::Rotation3d(const Eigen::Vector3d& initial,
+                       const Eigen::Vector3d& final) {
   double dot = initial.dot(final);
   double normProduct = initial.norm() * final.norm();
   double dotNorm = dot / normProduct;
@@ -170,6 +175,11 @@
   return *this * (1.0 / scalar);
 }
 
+bool Rotation3d::operator==(const Rotation3d& other) const {
+  return std::abs(std::abs(m_q.Dot(other.m_q)) -
+                  m_q.Norm() * other.m_q.Norm()) < 1e-9;
+}
+
 Rotation3d Rotation3d::RotateBy(const Rotation3d& other) const {
   return Rotation3d{other.m_q * m_q};
 }
@@ -184,9 +194,15 @@
   double y = m_q.Y();
   double z = m_q.Z();
 
-  // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_conversion
-  return units::radian_t{
-      std::atan2(2.0 * (w * x + y * z), 1.0 - 2.0 * (x * x + y * y))};
+  // wpimath/algorithms.md
+  double cxcy = 1.0 - 2.0 * (x * x + y * y);
+  double sxcy = 2.0 * (w * x + y * z);
+  double cy_sq = cxcy * cxcy + sxcy * sxcy;
+  if (cy_sq > 1e-20) {
+    return units::radian_t{std::atan2(sxcy, cxcy)};
+  } else {
+    return 0_rad;
+  }
 }
 
 units::radian_t Rotation3d::Y() const {
@@ -195,7 +211,7 @@
   double y = m_q.Y();
   double z = m_q.Z();
 
-  // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_conversion
+  // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_(in_3-2-1_sequence)_conversion
   double ratio = 2.0 * (w * y - z * x);
   if (std::abs(ratio) >= 1.0) {
     return units::radian_t{std::copysign(std::numbers::pi / 2.0, ratio)};
@@ -210,12 +226,18 @@
   double y = m_q.Y();
   double z = m_q.Z();
 
-  // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_conversion
-  return units::radian_t{
-      std::atan2(2.0 * (w * z + x * y), 1.0 - 2.0 * (y * y + z * z))};
+  // wpimath/algorithms.md
+  double cycz = 1.0 - 2.0 * (y * y + z * z);
+  double cysz = 2.0 * (w * z + x * y);
+  double cy_sq = cycz * cycz + cysz * cysz;
+  if (cy_sq > 1e-20) {
+    return units::radian_t{std::atan2(cysz, cycz)};
+  } else {
+    return units::radian_t{std::atan2(2.0 * w * z, w * w - z * z)};
+  }
 }
 
-Vectord<3> Rotation3d::Axis() const {
+Eigen::Vector3d Rotation3d::Axis() const {
   double norm = std::hypot(m_q.X(), m_q.Y(), m_q.Z());
   if (norm == 0.0) {
     return {0.0, 0.0, 0.0};
@@ -240,3 +262,21 @@
 void frc::from_json(const wpi::json& json, Rotation3d& rotation) {
   rotation = Rotation3d{json.at("quaternion").get<Quaternion>()};
 }
+
+google::protobuf::Message* wpi::Protobuf<frc::Rotation3d>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<wpi::proto::ProtobufRotation3d>(
+      arena);
+}
+
+frc::Rotation3d wpi::Protobuf<frc::Rotation3d>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufRotation3d*>(&msg);
+  return Rotation3d{wpi::UnpackProtobuf<frc::Quaternion>(m->q())};
+}
+
+void wpi::Protobuf<frc::Rotation3d>::Pack(google::protobuf::Message* msg,
+                                          const frc::Rotation3d& value) {
+  auto m = static_cast<wpi::proto::ProtobufRotation3d*>(msg);
+  wpi::PackProtobuf(m->mutable_q(), value.GetQuaternion());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Transform2d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Transform2d.cpp
index 25b0590..77f3cee 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Transform2d.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Transform2d.cpp
@@ -5,6 +5,7 @@
 #include "frc/geometry/Transform2d.h"
 
 #include "frc/geometry/Pose2d.h"
+#include "geometry2d.pb.h"
 
 using namespace frc;
 
@@ -21,3 +22,23 @@
 Transform2d Transform2d::operator+(const Transform2d& other) const {
   return Transform2d{Pose2d{}, Pose2d{}.TransformBy(*this).TransformBy(other)};
 }
+
+google::protobuf::Message* wpi::Protobuf<frc::Transform2d>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<
+      wpi::proto::ProtobufTransform2d>(arena);
+}
+
+frc::Transform2d wpi::Protobuf<frc::Transform2d>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufTransform2d*>(&msg);
+  return Transform2d{wpi::UnpackProtobuf<frc::Translation2d>(m->translation()),
+                     wpi::UnpackProtobuf<frc::Rotation2d>(m->rotation())};
+}
+
+void wpi::Protobuf<frc::Transform2d>::Pack(google::protobuf::Message* msg,
+                                           const frc::Transform2d& value) {
+  auto m = static_cast<wpi::proto::ProtobufTransform2d*>(msg);
+  wpi::PackProtobuf(m->mutable_translation(), value.Translation());
+  wpi::PackProtobuf(m->mutable_rotation(), value.Rotation());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Transform3d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Transform3d.cpp
index 1cfabaa..de6c253 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Transform3d.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Transform3d.cpp
@@ -5,6 +5,7 @@
 #include "frc/geometry/Transform3d.h"
 
 #include "frc/geometry/Pose3d.h"
+#include "geometry3d.pb.h"
 
 using namespace frc;
 
@@ -21,6 +22,10 @@
 Transform3d::Transform3d(Translation3d translation, Rotation3d rotation)
     : m_translation(std::move(translation)), m_rotation(std::move(rotation)) {}
 
+Transform3d::Transform3d(units::meter_t x, units::meter_t y, units::meter_t z,
+                         Rotation3d rotation)
+    : m_translation(x, y, z), m_rotation(std::move(rotation)) {}
+
 Transform3d Transform3d::Inverse() const {
   // We are rotating the difference between the translations
   // using a clockwise rotation matrix. This transforms the global
@@ -31,3 +36,23 @@
 Transform3d Transform3d::operator+(const Transform3d& other) const {
   return Transform3d{Pose3d{}, Pose3d{}.TransformBy(*this).TransformBy(other)};
 }
+
+google::protobuf::Message* wpi::Protobuf<frc::Transform3d>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<
+      wpi::proto::ProtobufTransform3d>(arena);
+}
+
+frc::Transform3d wpi::Protobuf<frc::Transform3d>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufTransform3d*>(&msg);
+  return Transform3d{wpi::UnpackProtobuf<frc::Translation3d>(m->translation()),
+                     wpi::UnpackProtobuf<frc::Rotation3d>(m->rotation())};
+}
+
+void wpi::Protobuf<frc::Transform3d>::Pack(google::protobuf::Message* msg,
+                                           const frc::Transform3d& value) {
+  auto m = static_cast<wpi::proto::ProtobufTransform3d*>(msg);
+  wpi::PackProtobuf(m->mutable_translation(), value.Translation());
+  wpi::PackProtobuf(m->mutable_rotation(), value.Rotation());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Translation2d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Translation2d.cpp
index d463696..6d5f315 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Translation2d.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Translation2d.cpp
@@ -6,6 +6,7 @@
 
 #include <wpi/json.h>
 
+#include "geometry2d.pb.h"
 #include "units/math.h"
 
 using namespace frc;
@@ -23,6 +24,22 @@
          units::math::abs(m_y - other.m_y) < 1E-9_m;
 }
 
+Translation2d Translation2d::Nearest(
+    std::span<const Translation2d> translations) const {
+  return *std::min_element(translations.begin(), translations.end(),
+                           [this](Translation2d a, Translation2d b) {
+                             return this->Distance(a) < this->Distance(b);
+                           });
+}
+
+Translation2d Translation2d::Nearest(
+    std::initializer_list<Translation2d> translations) const {
+  return *std::min_element(translations.begin(), translations.end(),
+                           [this](Translation2d a, Translation2d b) {
+                             return this->Distance(a) < this->Distance(b);
+                           });
+}
+
 void frc::to_json(wpi::json& json, const Translation2d& translation) {
   json =
       wpi::json{{"x", translation.X().value()}, {"y", translation.Y().value()}};
@@ -32,3 +49,22 @@
   translation = Translation2d{units::meter_t{json.at("x").get<double>()},
                               units::meter_t{json.at("y").get<double>()}};
 }
+
+google::protobuf::Message* wpi::Protobuf<frc::Translation2d>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<
+      wpi::proto::ProtobufTranslation2d>(arena);
+}
+
+frc::Translation2d wpi::Protobuf<frc::Translation2d>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufTranslation2d*>(&msg);
+  return frc::Translation2d{units::meter_t{m->x()}, units::meter_t{m->y()}};
+}
+
+void wpi::Protobuf<frc::Translation2d>::Pack(google::protobuf::Message* msg,
+                                             const frc::Translation2d& value) {
+  auto m = static_cast<wpi::proto::ProtobufTranslation2d*>(msg);
+  m->set_x(value.X().value());
+  m->set_y(value.Y().value());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Translation3d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Translation3d.cpp
index 2c53791..90e94ae 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Translation3d.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Translation3d.cpp
@@ -6,6 +6,7 @@
 
 #include <wpi/json.h>
 
+#include "geometry3d.pb.h"
 #include "units/length.h"
 #include "units/math.h"
 
@@ -52,3 +53,24 @@
                               units::meter_t{json.at("y").get<double>()},
                               units::meter_t{json.at("z").get<double>()}};
 }
+
+google::protobuf::Message* wpi::Protobuf<frc::Translation3d>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<
+      wpi::proto::ProtobufTranslation3d>(arena);
+}
+
+frc::Translation3d wpi::Protobuf<frc::Translation3d>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufTranslation3d*>(&msg);
+  return frc::Translation3d{units::meter_t{m->x()}, units::meter_t{m->y()},
+                            units::meter_t{m->z()}};
+}
+
+void wpi::Protobuf<frc::Translation3d>::Pack(google::protobuf::Message* msg,
+                                             const frc::Translation3d& value) {
+  auto m = static_cast<wpi::proto::ProtobufTranslation3d*>(msg);
+  m->set_x(value.X().value());
+  m->set_y(value.Y().value());
+  m->set_z(value.Z().value());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Twist2d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Twist2d.cpp
new file mode 100644
index 0000000..6c106eb
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Twist2d.cpp
@@ -0,0 +1,30 @@
+// 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 "frc/geometry/Twist2d.h"
+
+#include "geometry2d.pb.h"
+
+using namespace frc;
+
+google::protobuf::Message* wpi::Protobuf<frc::Twist2d>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<wpi::proto::ProtobufTwist2d>(
+      arena);
+}
+
+frc::Twist2d wpi::Protobuf<frc::Twist2d>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufTwist2d*>(&msg);
+  return frc::Twist2d{units::meter_t{m->dx()}, units::meter_t{m->dy()},
+                      units::radian_t{m->dtheta()}};
+}
+
+void wpi::Protobuf<frc::Twist2d>::Pack(google::protobuf::Message* msg,
+                                       const frc::Twist2d& value) {
+  auto m = static_cast<wpi::proto::ProtobufTwist2d*>(msg);
+  m->set_dx(value.dx.value());
+  m->set_dy(value.dy.value());
+  m->set_dtheta(value.dtheta.value());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Twist3d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Twist3d.cpp
new file mode 100644
index 0000000..4f4ce86
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/geometry/Twist3d.cpp
@@ -0,0 +1,34 @@
+// 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 "frc/geometry/Twist3d.h"
+
+#include "geometry3d.pb.h"
+
+using namespace frc;
+
+google::protobuf::Message* wpi::Protobuf<frc::Twist3d>::New(
+    google::protobuf::Arena* arena) {
+  return google::protobuf::Arena::CreateMessage<wpi::proto::ProtobufTwist3d>(
+      arena);
+}
+
+frc::Twist3d wpi::Protobuf<frc::Twist3d>::Unpack(
+    const google::protobuf::Message& msg) {
+  auto m = static_cast<const wpi::proto::ProtobufTwist3d*>(&msg);
+  return frc::Twist3d{units::meter_t{m->dx()},  units::meter_t{m->dy()},
+                      units::meter_t{m->dz()},  units::radian_t{m->rx()},
+                      units::radian_t{m->ry()}, units::radian_t{m->rz()}};
+}
+
+void wpi::Protobuf<frc::Twist3d>::Pack(google::protobuf::Message* msg,
+                                       const frc::Twist3d& value) {
+  auto m = static_cast<wpi::proto::ProtobufTwist3d*>(msg);
+  m->set_dx(value.dx.value());
+  m->set_dy(value.dy.value());
+  m->set_dz(value.dz.value());
+  m->set_rx(value.rx.value());
+  m->set_ry(value.ry.value());
+  m->set_rz(value.rz.value());
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp
deleted file mode 100644
index fd8a223..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp
+++ /dev/null
@@ -1,367 +0,0 @@
-// 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 <jni.h>
-
-#include <exception>
-
-#include <wpi/jni_util.h>
-
-#include "Eigen/Cholesky"
-#include "Eigen/Core"
-#include "Eigen/Eigenvalues"
-#include "Eigen/QR"
-#include "drake/math/discrete_algebraic_riccati_equation.h"
-#include "edu_wpi_first_math_WPIMathJNI.h"
-#include "frc/trajectory/TrajectoryUtil.h"
-#include "unsupported/Eigen/MatrixFunctions"
-
-using namespace wpi::java;
-
-/**
- * Returns true if (A, B) is a stabilizable pair.
- *
- * (A, B) is stabilizable if and only if the uncontrollable eigenvalues of A, if
- * any, have absolute values less than one, where an eigenvalue is
- * uncontrollable if rank(λI - A, B) < n where n is the number of states.
- *
- * @param A System matrix.
- * @param B Input matrix.
- */
-bool check_stabilizable(const Eigen::Ref<const Eigen::MatrixXd>& A,
-                        const Eigen::Ref<const Eigen::MatrixXd>& B) {
-  int states = B.rows();
-  int inputs = B.cols();
-  Eigen::EigenSolver<Eigen::MatrixXd> es{A};
-  for (int i = 0; i < states; ++i) {
-    if (es.eigenvalues()[i].real() * es.eigenvalues()[i].real() +
-            es.eigenvalues()[i].imag() * es.eigenvalues()[i].imag() <
-        1) {
-      continue;
-    }
-
-    Eigen::MatrixXcd E{states, states + inputs};
-    E << es.eigenvalues()[i] * Eigen::MatrixXcd::Identity(states, states) - A,
-        B;
-    Eigen::ColPivHouseholderQR<Eigen::MatrixXcd> qr{E};
-    if (qr.rank() < states) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-std::vector<double> GetElementsFromTrajectory(
-    const frc::Trajectory& trajectory) {
-  std::vector<double> elements;
-  elements.reserve(trajectory.States().size() * 7);
-
-  for (auto&& state : trajectory.States()) {
-    elements.push_back(state.t.value());
-    elements.push_back(state.velocity.value());
-    elements.push_back(state.acceleration.value());
-    elements.push_back(state.pose.X().value());
-    elements.push_back(state.pose.Y().value());
-    elements.push_back(state.pose.Rotation().Radians().value());
-    elements.push_back(state.curvature.value());
-  }
-
-  return elements;
-}
-
-frc::Trajectory CreateTrajectoryFromElements(std::span<const double> elements) {
-  // Make sure that the elements have the correct length.
-  assert(elements.size() % 7 == 0);
-
-  // Create a vector of states from the elements.
-  std::vector<frc::Trajectory::State> states;
-  states.reserve(elements.size() / 7);
-
-  for (size_t i = 0; i < elements.size(); i += 7) {
-    states.emplace_back(frc::Trajectory::State{
-        units::second_t{elements[i]},
-        units::meters_per_second_t{elements[i + 1]},
-        units::meters_per_second_squared_t{elements[i + 2]},
-        frc::Pose2d{units::meter_t{elements[i + 3]},
-                    units::meter_t{elements[i + 4]},
-                    units::radian_t{elements[i + 5]}},
-        units::curvature_t{elements[i + 6]}});
-  }
-
-  return frc::Trajectory(states);
-}
-
-extern "C" {
-
-/*
- * Class:     edu_wpi_first_math_WPIMathJNI
- * Method:    discreteAlgebraicRiccatiEquation
- * Signature: ([D[D[D[DII[D)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_math_WPIMathJNI_discreteAlgebraicRiccatiEquation
-  (JNIEnv* env, jclass, jdoubleArray A, jdoubleArray B, jdoubleArray Q,
-   jdoubleArray R, jint states, jint inputs, jdoubleArray S)
-{
-  jdouble* nativeA = env->GetDoubleArrayElements(A, nullptr);
-  jdouble* nativeB = env->GetDoubleArrayElements(B, nullptr);
-  jdouble* nativeQ = env->GetDoubleArrayElements(Q, nullptr);
-  jdouble* nativeR = env->GetDoubleArrayElements(R, nullptr);
-
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      Amat{nativeA, states, states};
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      Bmat{nativeB, states, inputs};
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      Qmat{nativeQ, states, states};
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      Rmat{nativeR, inputs, inputs};
-
-  try {
-    Eigen::MatrixXd result =
-        drake::math::DiscreteAlgebraicRiccatiEquation(Amat, Bmat, Qmat, Rmat);
-
-    env->ReleaseDoubleArrayElements(A, nativeA, 0);
-    env->ReleaseDoubleArrayElements(B, nativeB, 0);
-    env->ReleaseDoubleArrayElements(Q, nativeQ, 0);
-    env->ReleaseDoubleArrayElements(R, nativeR, 0);
-
-    env->SetDoubleArrayRegion(S, 0, states * states, result.data());
-  } catch (const std::runtime_error& e) {
-    jclass cls = env->FindClass("java/lang/RuntimeException");
-    if (cls) {
-      env->ThrowNew(cls, e.what());
-    }
-  }
-}
-
-/*
- * Class:     edu_wpi_first_math_WPIMathJNI
- * Method:    exp
- * Signature: ([DI[D)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_math_WPIMathJNI_exp
-  (JNIEnv* env, jclass, jdoubleArray src, jint rows, jdoubleArray dst)
-{
-  jdouble* arrayBody = env->GetDoubleArrayElements(src, nullptr);
-
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      Amat{arrayBody, rows, rows};
-  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Aexp =
-      Amat.exp();
-
-  env->ReleaseDoubleArrayElements(src, arrayBody, 0);
-  env->SetDoubleArrayRegion(dst, 0, rows * rows, Aexp.data());
-}
-
-/*
- * Class:     edu_wpi_first_math_WPIMathJNI
- * Method:    pow
- * Signature: ([DID[D)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_math_WPIMathJNI_pow
-  (JNIEnv* env, jclass, jdoubleArray src, jint rows, jdouble exponent,
-   jdoubleArray dst)
-{
-  jdouble* arrayBody = env->GetDoubleArrayElements(src, nullptr);
-
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      Amat{arrayBody, rows, rows};  // NOLINT
-  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Apow =
-      Amat.pow(exponent);
-
-  env->ReleaseDoubleArrayElements(src, arrayBody, 0);
-  env->SetDoubleArrayRegion(dst, 0, rows * rows, Apow.data());
-}
-
-/*
- * Class:     edu_wpi_first_math_WPIMathJNI
- * Method:    isStabilizable
- * Signature: (II[D[D)Z
- */
-JNIEXPORT jboolean JNICALL
-Java_edu_wpi_first_math_WPIMathJNI_isStabilizable
-  (JNIEnv* env, jclass, jint states, jint inputs, jdoubleArray aSrc,
-   jdoubleArray bSrc)
-{
-  jdouble* nativeA = env->GetDoubleArrayElements(aSrc, nullptr);
-  jdouble* nativeB = env->GetDoubleArrayElements(bSrc, nullptr);
-
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      A{nativeA, states, states};
-
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      B{nativeB, states, inputs};
-
-  bool isStabilizable = check_stabilizable(A, B);
-
-  env->ReleaseDoubleArrayElements(aSrc, nativeA, 0);
-  env->ReleaseDoubleArrayElements(bSrc, nativeB, 0);
-
-  return isStabilizable;
-}
-
-/*
- * Class:     edu_wpi_first_math_WPIMathJNI
- * Method:    fromPathweaverJson
- * Signature: (Ljava/lang/String;)[D
- */
-JNIEXPORT jdoubleArray JNICALL
-Java_edu_wpi_first_math_WPIMathJNI_fromPathweaverJson
-  (JNIEnv* env, jclass, jstring path)
-{
-  try {
-    auto trajectory =
-        frc::TrajectoryUtil::FromPathweaverJson(JStringRef{env, path}.c_str());
-    std::vector<double> elements = GetElementsFromTrajectory(trajectory);
-    return MakeJDoubleArray(env, elements);
-  } catch (std::exception& e) {
-    jclass cls = env->FindClass("java/io/IOException");
-    if (cls) {
-      env->ThrowNew(cls, e.what());
-    }
-    return nullptr;
-  }
-}
-
-/*
- * Class:     edu_wpi_first_math_WPIMathJNI
- * Method:    toPathweaverJson
- * Signature: ([DLjava/lang/String;)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_math_WPIMathJNI_toPathweaverJson
-  (JNIEnv* env, jclass, jdoubleArray elements, jstring path)
-{
-  try {
-    auto trajectory =
-        CreateTrajectoryFromElements(JDoubleArrayRef{env, elements});
-    frc::TrajectoryUtil::ToPathweaverJson(trajectory,
-                                          JStringRef{env, path}.c_str());
-  } catch (std::exception& e) {
-    jclass cls = env->FindClass("java/io/IOException");
-    if (cls) {
-      env->ThrowNew(cls, e.what());
-    }
-  }
-}
-
-/*
- * Class:     edu_wpi_first_math_WPIMathJNI
- * Method:    deserializeTrajectory
- * Signature: (Ljava/lang/String;)[D
- */
-JNIEXPORT jdoubleArray JNICALL
-Java_edu_wpi_first_math_WPIMathJNI_deserializeTrajectory
-  (JNIEnv* env, jclass, jstring json)
-{
-  try {
-    auto trajectory = frc::TrajectoryUtil::DeserializeTrajectory(
-        JStringRef{env, json}.c_str());
-    std::vector<double> elements = GetElementsFromTrajectory(trajectory);
-    return MakeJDoubleArray(env, elements);
-  } catch (std::exception& e) {
-    jclass cls = env->FindClass(
-        "edu/wpi/first/math/trajectory/TrajectoryUtil$"
-        "TrajectorySerializationException");
-    if (cls) {
-      env->ThrowNew(cls, e.what());
-    }
-    return nullptr;
-  }
-}
-
-/*
- * Class:     edu_wpi_first_math_WPIMathJNI
- * Method:    serializeTrajectory
- * Signature: ([D)Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL
-Java_edu_wpi_first_math_WPIMathJNI_serializeTrajectory
-  (JNIEnv* env, jclass, jdoubleArray elements)
-{
-  try {
-    auto trajectory =
-        CreateTrajectoryFromElements(JDoubleArrayRef{env, elements});
-    return MakeJString(env,
-                       frc::TrajectoryUtil::SerializeTrajectory(trajectory));
-  } catch (std::exception& e) {
-    jclass cls = env->FindClass(
-        "edu/wpi/first/math/trajectory/TrajectoryUtil$"
-        "TrajectorySerializationException");
-    if (cls) {
-      env->ThrowNew(cls, e.what());
-    }
-    return nullptr;
-  }
-}
-
-/*
- * Class:     edu_wpi_first_math_WPIMathJNI
- * Method:    rankUpdate
- * Signature: ([DI[DDZ)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_math_WPIMathJNI_rankUpdate
-  (JNIEnv* env, jclass, jdoubleArray mat, jint rows, jdoubleArray vec,
-   jdouble sigma, jboolean lowerTriangular)
-{
-  jdouble* matBody = env->GetDoubleArrayElements(mat, nullptr);
-  jdouble* vecBody = env->GetDoubleArrayElements(vec, nullptr);
-
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      L{matBody, rows, rows};
-  Eigen::Map<Eigen::Vector<double, Eigen::Dynamic>> v{vecBody, rows};
-
-  if (lowerTriangular == JNI_TRUE) {
-    Eigen::internal::llt_inplace<double, Eigen::Lower>::rankUpdate(L, v, sigma);
-  } else {
-    Eigen::internal::llt_inplace<double, Eigen::Upper>::rankUpdate(L, v, sigma);
-  }
-
-  env->ReleaseDoubleArrayElements(mat, matBody, 0);
-  env->ReleaseDoubleArrayElements(vec, vecBody, 0);
-}
-
-/*
- * Class:     edu_wpi_first_math_WPIMathJNI
- * Method:    solveFullPivHouseholderQr
- * Signature: ([DII[DII[D)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_math_WPIMathJNI_solveFullPivHouseholderQr
-  (JNIEnv* env, jclass, jdoubleArray A, jint Arows, jint Acols, jdoubleArray B,
-   jint Brows, jint Bcols, jdoubleArray dst)
-{
-  jdouble* nativeA = env->GetDoubleArrayElements(A, nullptr);
-  jdouble* nativeB = env->GetDoubleArrayElements(B, nullptr);
-
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      Amat{nativeA, Arows, Acols};
-  Eigen::Map<
-      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
-      Bmat{nativeB, Brows, Bcols};
-
-  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Xmat =
-      Amat.fullPivHouseholderQr().solve(Bmat);
-
-  env->ReleaseDoubleArrayElements(A, nativeA, 0);
-  env->ReleaseDoubleArrayElements(B, nativeB, 0);
-  env->SetDoubleArrayRegion(dst, 0, Brows * Bcols, Xmat.data());
-}
-
-}  // extern "C"
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_DARE.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_DARE.cpp
new file mode 100644
index 0000000..e5c3ac5
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_DARE.cpp
@@ -0,0 +1,177 @@
+// 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 <jni.h>
+
+#include <stdexcept>
+
+#include <wpi/jni_util.h>
+
+#include "WPIMathJNI_Exceptions.h"
+#include "edu_wpi_first_math_WPIMathJNI.h"
+#include "frc/DARE.h"
+
+using namespace wpi::java;
+
+extern "C" {
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    dareDetailABQR
+ * Signature: ([D[D[D[DII[D)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_dareDetailABQR
+  (JNIEnv* env, jclass, jdoubleArray A, jdoubleArray B, jdoubleArray Q,
+   jdoubleArray R, jint states, jint inputs, jdoubleArray S)
+{
+  JSpan<const jdouble> nativeA{env, A};
+  JSpan<const jdouble> nativeB{env, B};
+  JSpan<const jdouble> nativeQ{env, Q};
+  JSpan<const jdouble> nativeR{env, R};
+
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Amat{nativeA.data(), states, states};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Bmat{nativeB.data(), states, inputs};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Qmat{nativeQ.data(), states, states};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Rmat{nativeR.data(), inputs, inputs};
+
+  Eigen::MatrixXd RmatCopy{Rmat};
+  auto R_llt = RmatCopy.llt();
+
+  Eigen::MatrixXd result = frc::detail::DARE<Eigen::Dynamic, Eigen::Dynamic>(
+      Amat, Bmat, Qmat, R_llt);
+
+  env->SetDoubleArrayRegion(S, 0, states * states, result.data());
+}
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    dareDetailABQRN
+ * Signature: ([D[D[D[D[DII[D)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_dareDetailABQRN
+  (JNIEnv* env, jclass, jdoubleArray A, jdoubleArray B, jdoubleArray Q,
+   jdoubleArray R, jdoubleArray N, jint states, jint inputs, jdoubleArray S)
+{
+  JSpan<const jdouble> nativeA{env, A};
+  JSpan<const jdouble> nativeB{env, B};
+  JSpan<const jdouble> nativeQ{env, Q};
+  JSpan<const jdouble> nativeR{env, R};
+  JSpan<const jdouble> nativeN{env, N};
+
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Amat{nativeA.data(), states, states};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Bmat{nativeB.data(), states, inputs};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Qmat{nativeQ.data(), states, states};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Rmat{nativeR.data(), inputs, inputs};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Nmat{nativeN.data(), states, inputs};
+
+  Eigen::MatrixXd Rcopy{Rmat};
+  auto R_llt = Rcopy.llt();
+
+  Eigen::MatrixXd result = frc::detail::DARE<Eigen::Dynamic, Eigen::Dynamic>(
+      Amat, Bmat, Qmat, R_llt, Nmat);
+
+  env->SetDoubleArrayRegion(S, 0, states * states, result.data());
+}
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    dareABQR
+ * Signature: ([D[D[D[DII[D)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_dareABQR
+  (JNIEnv* env, jclass, jdoubleArray A, jdoubleArray B, jdoubleArray Q,
+   jdoubleArray R, jint states, jint inputs, jdoubleArray S)
+{
+  JSpan<const jdouble> nativeA{env, A};
+  JSpan<const jdouble> nativeB{env, B};
+  JSpan<const jdouble> nativeQ{env, Q};
+  JSpan<const jdouble> nativeR{env, R};
+
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Amat{nativeA.data(), states, states};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Bmat{nativeB.data(), states, inputs};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Qmat{nativeQ.data(), states, states};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Rmat{nativeR.data(), inputs, inputs};
+
+  try {
+    Eigen::MatrixXd result =
+        frc::DARE<Eigen::Dynamic, Eigen::Dynamic>(Amat, Bmat, Qmat, Rmat);
+
+    env->SetDoubleArrayRegion(S, 0, states * states, result.data());
+  } catch (const std::invalid_argument& e) {
+    illegalArgEx.Throw(env, e.what());
+  }
+}
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    dareABQRN
+ * Signature: ([D[D[D[D[DII[D)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_dareABQRN
+  (JNIEnv* env, jclass, jdoubleArray A, jdoubleArray B, jdoubleArray Q,
+   jdoubleArray R, jdoubleArray N, jint states, jint inputs, jdoubleArray S)
+{
+  JSpan<const jdouble> nativeA{env, A};
+  JSpan<const jdouble> nativeB{env, B};
+  JSpan<const jdouble> nativeQ{env, Q};
+  JSpan<const jdouble> nativeR{env, R};
+  JSpan<const jdouble> nativeN{env, N};
+
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Amat{nativeA.data(), states, states};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Bmat{nativeB.data(), states, inputs};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Qmat{nativeQ.data(), states, states};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Rmat{nativeR.data(), inputs, inputs};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Nmat{nativeN.data(), states, inputs};
+
+  try {
+    Eigen::MatrixXd result =
+        frc::DARE<Eigen::Dynamic, Eigen::Dynamic>(Amat, Bmat, Qmat, Rmat, Nmat);
+
+    env->SetDoubleArrayRegion(S, 0, states * states, result.data());
+  } catch (const std::invalid_argument& e) {
+    illegalArgEx.Throw(env, e.what());
+  }
+}
+
+}  // extern "C"
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Eigen.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Eigen.cpp
new file mode 100644
index 0000000..642a2ba
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Eigen.cpp
@@ -0,0 +1,112 @@
+// 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 <jni.h>
+
+#include <Eigen/Cholesky>
+#include <Eigen/Core>
+#include <Eigen/QR>
+#include <unsupported/Eigen/MatrixFunctions>
+#include <wpi/jni_util.h>
+
+#include "edu_wpi_first_math_WPIMathJNI.h"
+
+using namespace wpi::java;
+
+extern "C" {
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    exp
+ * Signature: ([DI[D)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_exp
+  (JNIEnv* env, jclass, jdoubleArray src, jint rows, jdoubleArray dst)
+{
+  JSpan<const jdouble> arrayBody{env, src};
+
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Amat{arrayBody.data(), rows, rows};
+  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Aexp =
+      Amat.exp();
+
+  env->SetDoubleArrayRegion(dst, 0, rows * rows, Aexp.data());
+}
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    pow
+ * Signature: ([DID[D)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_pow
+  (JNIEnv* env, jclass, jdoubleArray src, jint rows, jdouble exponent,
+   jdoubleArray dst)
+{
+  JSpan<const jdouble> arrayBody{env, src};
+
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Amat{arrayBody.data(), rows, rows};
+  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Apow =
+      Amat.pow(exponent);
+
+  env->SetDoubleArrayRegion(dst, 0, rows * rows, Apow.data());
+}
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    rankUpdate
+ * Signature: ([DI[DDZ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_rankUpdate
+  (JNIEnv* env, jclass, jdoubleArray mat, jint rows, jdoubleArray vec,
+   jdouble sigma, jboolean lowerTriangular)
+{
+  JSpan<jdouble> matBody{env, mat};
+  JSpan<const jdouble> vecBody{env, vec};
+
+  Eigen::Map<
+      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
+      L{matBody.data(), rows, rows};
+  Eigen::Map<const Eigen::Vector<double, Eigen::Dynamic>> v{vecBody.data(),
+                                                            rows};
+
+  if (lowerTriangular == JNI_TRUE) {
+    Eigen::internal::llt_inplace<double, Eigen::Lower>::rankUpdate(L, v, sigma);
+  } else {
+    Eigen::internal::llt_inplace<double, Eigen::Upper>::rankUpdate(L, v, sigma);
+  }
+}
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    solveFullPivHouseholderQr
+ * Signature: ([DII[DII[D)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_solveFullPivHouseholderQr
+  (JNIEnv* env, jclass, jdoubleArray A, jint Arows, jint Acols, jdoubleArray B,
+   jint Brows, jint Bcols, jdoubleArray dst)
+{
+  JSpan<const jdouble> nativeA{env, A};
+  JSpan<const jdouble> nativeB{env, B};
+
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Amat{nativeA.data(), Arows, Acols};
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      Bmat{nativeB.data(), Brows, Bcols};
+
+  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Xmat =
+      Amat.fullPivHouseholderQr().solve(Bmat);
+
+  env->SetDoubleArrayRegion(dst, 0, Brows * Bcols, Xmat.data());
+}
+
+}  // extern "C"
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Exceptions.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Exceptions.cpp
new file mode 100644
index 0000000..7f5a03f
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Exceptions.cpp
@@ -0,0 +1,56 @@
+// 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 "WPIMathJNI_Exceptions.h"
+
+#include <jni.h>
+
+using namespace wpi::java;
+
+//
+// Globals and load/unload
+//
+
+JException illegalArgEx;
+JException ioEx;
+JException trajectorySerializationEx;
+
+static const JExceptionInit exceptions[] = {
+    {"java/lang/IllegalArgumentException", &illegalArgEx},
+    {"java/io/IOException", &ioEx},
+    {"edu/wpi/first/math/trajectory/"
+     "TrajectoryUtil$TrajectorySerializationException",
+     &trajectorySerializationEx}};
+
+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;
+  }
+
+  // Cache references to exceptions
+  for (auto& c : exceptions) {
+    *c.cls = JException(env, c.name);
+    if (!*c.cls) {
+      return JNI_ERR;
+    }
+  }
+
+  return JNI_VERSION_1_6;
+}
+
+JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
+  JNIEnv* env;
+  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+    return;
+  }
+  // Delete global references
+  for (auto& c : exceptions) {
+    c.cls->free(env);
+  }
+}
+
+}  // extern "C"
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Exceptions.h b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Exceptions.h
new file mode 100644
index 0000000..cf0e149
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Exceptions.h
@@ -0,0 +1,11 @@
+// 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 <wpi/jni_util.h>
+
+extern wpi::java::JException illegalArgEx;
+extern wpi::java::JException ioEx;
+extern wpi::java::JException trajectorySerializationEx;
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Pose3d.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Pose3d.cpp
new file mode 100644
index 0000000..f985231
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Pose3d.cpp
@@ -0,0 +1,70 @@
+// 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 <jni.h>
+
+#include <wpi/jni_util.h>
+
+#include "edu_wpi_first_math_WPIMathJNI.h"
+#include "frc/geometry/Pose3d.h"
+
+using namespace wpi::java;
+
+extern "C" {
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    expPose3d
+ * Signature: (DDDDDDDDDDDDD)[D
+ */
+JNIEXPORT jdoubleArray JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_expPose3d
+  (JNIEnv* env, jclass, jdouble poseX, jdouble poseY, jdouble poseZ,
+   jdouble poseQw, jdouble poseQx, jdouble poseQy, jdouble poseQz,
+   jdouble twistDx, jdouble twistDy, jdouble twistDz, jdouble twistRx,
+   jdouble twistRy, jdouble twistRz)
+{
+  frc::Pose3d pose{
+      units::meter_t{poseX}, units::meter_t{poseY}, units::meter_t{poseZ},
+      frc::Rotation3d{frc::Quaternion{poseQw, poseQx, poseQy, poseQz}}};
+  frc::Twist3d twist{units::meter_t{twistDx},  units::meter_t{twistDy},
+                     units::meter_t{twistDz},  units::radian_t{twistRx},
+                     units::radian_t{twistRy}, units::radian_t{twistRz}};
+
+  frc::Pose3d result = pose.Exp(twist);
+
+  const auto& resultQuaternion = result.Rotation().GetQuaternion();
+  return MakeJDoubleArray(
+      env, {{result.X().value(), result.Y().value(), result.Z().value(),
+             resultQuaternion.W(), resultQuaternion.X(), resultQuaternion.Y(),
+             resultQuaternion.Z()}});
+}
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    logPose3d
+ * Signature: (DDDDDDDDDDDDDD)[D
+ */
+JNIEXPORT jdoubleArray JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_logPose3d
+  (JNIEnv* env, jclass, jdouble startX, jdouble startY, jdouble startZ,
+   jdouble startQw, jdouble startQx, jdouble startQy, jdouble startQz,
+   jdouble endX, jdouble endY, jdouble endZ, jdouble endQw, jdouble endQx,
+   jdouble endQy, jdouble endQz)
+{
+  frc::Pose3d startPose{
+      units::meter_t{startX}, units::meter_t{startY}, units::meter_t{startZ},
+      frc::Rotation3d{frc::Quaternion{startQw, startQx, startQy, startQz}}};
+  frc::Pose3d endPose{
+      units::meter_t{endX}, units::meter_t{endY}, units::meter_t{endZ},
+      frc::Rotation3d{frc::Quaternion{endQw, endQx, endQy, endQz}}};
+
+  frc::Twist3d result = startPose.Log(endPose);
+
+  return MakeJDoubleArray(
+      env, {{result.dx.value(), result.dy.value(), result.dz.value(),
+             result.rx.value(), result.ry.value(), result.rz.value()}});
+}
+
+}  // extern "C"
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_StateSpaceUtil.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_StateSpaceUtil.cpp
new file mode 100644
index 0000000..dc3dfdd
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_StateSpaceUtil.cpp
@@ -0,0 +1,44 @@
+// 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 <jni.h>
+
+#include <Eigen/Core>
+#include <wpi/jni_util.h>
+
+#include "edu_wpi_first_math_WPIMathJNI.h"
+#include "frc/StateSpaceUtil.h"
+
+using namespace wpi::java;
+
+extern "C" {
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    isStabilizable
+ * Signature: (II[D[D)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_isStabilizable
+  (JNIEnv* env, jclass, jint states, jint inputs, jdoubleArray aSrc,
+   jdoubleArray bSrc)
+{
+  JSpan<const jdouble> nativeA{env, aSrc};
+  JSpan<const jdouble> nativeB{env, bSrc};
+
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      A{nativeA.data(), states, states};
+
+  Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
+                                 Eigen::RowMajor>>
+      B{nativeB.data(), states, inputs};
+
+  bool isStabilizable =
+      frc::IsStabilizable<Eigen::Dynamic, Eigen::Dynamic>(A, B);
+
+  return isStabilizable;
+}
+
+}  // extern "C"
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Trajectory.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Trajectory.cpp
new file mode 100644
index 0000000..3359da9
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/jni/WPIMathJNI_Trajectory.cpp
@@ -0,0 +1,138 @@
+// 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 <jni.h>
+
+#include <exception>
+
+#include <wpi/jni_util.h>
+
+#include "WPIMathJNI_Exceptions.h"
+#include "edu_wpi_first_math_WPIMathJNI.h"
+#include "frc/trajectory/TrajectoryUtil.h"
+
+using namespace wpi::java;
+
+std::vector<double> GetElementsFromTrajectory(
+    const frc::Trajectory& trajectory) {
+  std::vector<double> elements;
+  elements.reserve(trajectory.States().size() * 7);
+
+  for (auto&& state : trajectory.States()) {
+    elements.push_back(state.t.value());
+    elements.push_back(state.velocity.value());
+    elements.push_back(state.acceleration.value());
+    elements.push_back(state.pose.X().value());
+    elements.push_back(state.pose.Y().value());
+    elements.push_back(state.pose.Rotation().Radians().value());
+    elements.push_back(state.curvature.value());
+  }
+
+  return elements;
+}
+
+frc::Trajectory CreateTrajectoryFromElements(std::span<const double> elements) {
+  // Make sure that the elements have the correct length.
+  assert(elements.size() % 7 == 0);
+
+  // Create a vector of states from the elements.
+  std::vector<frc::Trajectory::State> states;
+  states.reserve(elements.size() / 7);
+
+  for (size_t i = 0; i < elements.size(); i += 7) {
+    states.emplace_back(frc::Trajectory::State{
+        units::second_t{elements[i]},
+        units::meters_per_second_t{elements[i + 1]},
+        units::meters_per_second_squared_t{elements[i + 2]},
+        frc::Pose2d{units::meter_t{elements[i + 3]},
+                    units::meter_t{elements[i + 4]},
+                    units::radian_t{elements[i + 5]}},
+        units::curvature_t{elements[i + 6]}});
+  }
+
+  return frc::Trajectory(states);
+}
+
+extern "C" {
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    fromPathweaverJson
+ * Signature: (Ljava/lang/String;)[D
+ */
+JNIEXPORT jdoubleArray JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_fromPathweaverJson
+  (JNIEnv* env, jclass, jstring path)
+{
+  try {
+    auto trajectory =
+        frc::TrajectoryUtil::FromPathweaverJson(JStringRef{env, path}.c_str());
+    std::vector<double> elements = GetElementsFromTrajectory(trajectory);
+    return MakeJDoubleArray(env, elements);
+  } catch (std::exception& e) {
+    ioEx.Throw(env, e.what());
+    return nullptr;
+  }
+}
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    toPathweaverJson
+ * Signature: ([DLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_toPathweaverJson
+  (JNIEnv* env, jclass, jdoubleArray elements, jstring path)
+{
+  try {
+    auto trajectory =
+        CreateTrajectoryFromElements(JSpan<const jdouble>{env, elements});
+    frc::TrajectoryUtil::ToPathweaverJson(trajectory,
+                                          JStringRef{env, path}.c_str());
+  } catch (std::exception& e) {
+    ioEx.Throw(env, e.what());
+  }
+}
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    deserializeTrajectory
+ * Signature: (Ljava/lang/String;)[D
+ */
+JNIEXPORT jdoubleArray JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_deserializeTrajectory
+  (JNIEnv* env, jclass, jstring json)
+{
+  try {
+    auto trajectory = frc::TrajectoryUtil::DeserializeTrajectory(
+        JStringRef{env, json}.c_str());
+    std::vector<double> elements = GetElementsFromTrajectory(trajectory);
+    return MakeJDoubleArray(env, elements);
+  } catch (std::exception& e) {
+    trajectorySerializationEx.Throw(env, e.what());
+    return nullptr;
+  }
+}
+
+/*
+ * Class:     edu_wpi_first_math_WPIMathJNI
+ * Method:    serializeTrajectory
+ * Signature: ([D)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_serializeTrajectory
+  (JNIEnv* env, jclass, jdoubleArray elements)
+{
+  try {
+    auto trajectory =
+        CreateTrajectoryFromElements(JSpan<const jdouble>{env, elements});
+    return MakeJString(env,
+                       frc::TrajectoryUtil::SerializeTrajectory(trajectory));
+  } catch (std::exception& e) {
+    trajectorySerializationEx.Throw(env, e.what());
+    return nullptr;
+  }
+}
+
+}  // extern "C"
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/DifferentialDriveOdometry.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/DifferentialDriveOdometry.cpp
index 1ff7a8a..346a232 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/DifferentialDriveOdometry.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/DifferentialDriveOdometry.cpp
@@ -11,32 +11,9 @@
 DifferentialDriveOdometry::DifferentialDriveOdometry(
     const Rotation2d& gyroAngle, units::meter_t leftDistance,
     units::meter_t rightDistance, const Pose2d& initialPose)
-    : m_pose(initialPose),
-      m_prevLeftDistance(leftDistance),
-      m_prevRightDistance(rightDistance) {
-  m_previousAngle = m_pose.Rotation();
-  m_gyroOffset = m_pose.Rotation() - gyroAngle;
+    : Odometry<DifferentialDriveWheelSpeeds, DifferentialDriveWheelPositions>(
+          m_kinematicsImpl, gyroAngle, {leftDistance, rightDistance},
+          initialPose) {
   wpi::math::MathSharedStore::ReportUsage(
       wpi::math::MathUsageId::kOdometry_DifferentialDrive, 1);
 }
-
-const Pose2d& DifferentialDriveOdometry::Update(const Rotation2d& gyroAngle,
-                                                units::meter_t leftDistance,
-                                                units::meter_t rightDistance) {
-  auto deltaLeftDistance = leftDistance - m_prevLeftDistance;
-  auto deltaRightDistance = rightDistance - m_prevRightDistance;
-
-  m_prevLeftDistance = leftDistance;
-  m_prevRightDistance = rightDistance;
-
-  auto averageDeltaDistance = (deltaLeftDistance + deltaRightDistance) / 2.0;
-  auto angle = gyroAngle + m_gyroOffset;
-
-  auto newPose = m_pose.Exp(
-      {averageDeltaDistance, 0_m, (angle - m_previousAngle).Radians()});
-
-  m_previousAngle = angle;
-  m_pose = {newPose.Translation(), angle};
-
-  return m_pose;
-}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/MecanumDriveKinematics.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/MecanumDriveKinematics.cpp
index 298dd7f..c21a7b2 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/MecanumDriveKinematics.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/MecanumDriveKinematics.cpp
@@ -25,7 +25,7 @@
                                       chassisSpeeds.vy.value(),
                                       chassisSpeeds.omega.value()};
 
-  Vectord<4> wheelsVector = m_inverseKinematics * chassisSpeedsVector;
+  Eigen::Vector4d wheelsVector = m_inverseKinematics * chassisSpeedsVector;
 
   MecanumDriveWheelSpeeds wheelSpeeds;
   wheelSpeeds.frontLeft = units::meters_per_second_t{wheelsVector(0)};
@@ -37,7 +37,7 @@
 
 ChassisSpeeds MecanumDriveKinematics::ToChassisSpeeds(
     const MecanumDriveWheelSpeeds& wheelSpeeds) const {
-  Vectord<4> wheelSpeedsVector{
+  Eigen::Vector4d wheelSpeedsVector{
       wheelSpeeds.frontLeft.value(), wheelSpeeds.frontRight.value(),
       wheelSpeeds.rearLeft.value(), wheelSpeeds.rearRight.value()};
 
@@ -50,15 +50,30 @@
 }
 
 Twist2d MecanumDriveKinematics::ToTwist2d(
+    const MecanumDriveWheelPositions& start,
+    const MecanumDriveWheelPositions& end) const {
+  Eigen::Vector4d wheelDeltasVector{
+      end.frontLeft.value() - start.frontLeft.value(),
+      end.frontRight.value() - start.frontRight.value(),
+      end.rearLeft.value() - start.rearLeft.value(),
+      end.rearRight.value() - start.rearRight.value()};
+
+  Eigen::Vector3d twistVector = m_forwardKinematics.solve(wheelDeltasVector);
+
+  return {units::meter_t{twistVector(0)}, units::meter_t{twistVector(1)},
+          units::radian_t{twistVector(2)}};
+}
+
+Twist2d MecanumDriveKinematics::ToTwist2d(
     const MecanumDriveWheelPositions& wheelDeltas) const {
-  Vectord<4> wheelDeltasVector{
+  Eigen::Vector4d wheelDeltasVector{
       wheelDeltas.frontLeft.value(), wheelDeltas.frontRight.value(),
       wheelDeltas.rearLeft.value(), wheelDeltas.rearRight.value()};
 
   Eigen::Vector3d twistVector = m_forwardKinematics.solve(wheelDeltasVector);
 
-  return {units::meter_t{twistVector(0)},  // NOLINT
-          units::meter_t{twistVector(1)}, units::radian_t{twistVector(2)}};
+  return {units::meter_t{twistVector(0)}, units::meter_t{twistVector(1)},
+          units::radian_t{twistVector(2)}};
 }
 
 void MecanumDriveKinematics::SetInverseKinematics(Translation2d fl,
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/MecanumDriveOdometry.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/MecanumDriveOdometry.cpp
index 394adaf..55055be 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/MecanumDriveOdometry.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/MecanumDriveOdometry.cpp
@@ -11,35 +11,9 @@
 MecanumDriveOdometry::MecanumDriveOdometry(
     MecanumDriveKinematics kinematics, const Rotation2d& gyroAngle,
     const MecanumDriveWheelPositions& wheelPositions, const Pose2d& initialPose)
-    : m_kinematics(kinematics),
-      m_pose(initialPose),
-      m_previousWheelPositions(wheelPositions) {
-  m_previousAngle = m_pose.Rotation();
-  m_gyroOffset = m_pose.Rotation() - gyroAngle;
+    : Odometry<MecanumDriveWheelSpeeds, MecanumDriveWheelPositions>(
+          m_kinematicsImpl, gyroAngle, wheelPositions, initialPose),
+      m_kinematicsImpl(kinematics) {
   wpi::math::MathSharedStore::ReportUsage(
       wpi::math::MathUsageId::kOdometry_MecanumDrive, 1);
 }
-
-const Pose2d& MecanumDriveOdometry::Update(
-    const Rotation2d& gyroAngle,
-    const MecanumDriveWheelPositions& wheelPositions) {
-  auto angle = gyroAngle + m_gyroOffset;
-
-  MecanumDriveWheelPositions wheelDeltas{
-      wheelPositions.frontLeft - m_previousWheelPositions.frontLeft,
-      wheelPositions.frontRight - m_previousWheelPositions.frontRight,
-      wheelPositions.rearLeft - m_previousWheelPositions.rearLeft,
-      wheelPositions.rearRight - m_previousWheelPositions.rearRight,
-  };
-
-  auto twist = m_kinematics.ToTwist2d(wheelDeltas);
-  twist.dtheta = (angle - m_previousAngle).Radians();
-
-  auto newPose = m_pose.Exp(twist);
-
-  m_previousAngle = angle;
-  m_previousWheelPositions = wheelPositions;
-  m_pose = {newPose.Translation(), angle};
-
-  return m_pose;
-}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/SwerveModulePosition.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/SwerveModulePosition.cpp
new file mode 100644
index 0000000..5343de9
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/SwerveModulePosition.cpp
@@ -0,0 +1,15 @@
+// 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 "frc/kinematics/SwerveModulePosition.h"
+
+#include "frc/kinematics/SwerveModuleState.h"
+#include "units/math.h"
+
+using namespace frc;
+
+bool SwerveModulePosition::operator==(const SwerveModulePosition& other) const {
+  return units::math::abs(distance - other.distance) < 1E-9_m &&
+         angle == other.angle;
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/SwerveModuleState.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/SwerveModuleState.cpp
new file mode 100644
index 0000000..071d53a
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/kinematics/SwerveModuleState.cpp
@@ -0,0 +1,24 @@
+// 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 "frc/kinematics/SwerveModuleState.h"
+
+#include "units/math.h"
+
+using namespace frc;
+
+bool SwerveModuleState::operator==(const SwerveModuleState& other) const {
+  return units::math::abs(speed - other.speed) < 1E-9_mps &&
+         angle == other.angle;
+}
+
+SwerveModuleState SwerveModuleState::Optimize(
+    const SwerveModuleState& desiredState, const Rotation2d& currentAngle) {
+  auto delta = desiredState.angle - currentAngle;
+  if (units::math::abs(delta.Degrees()) > 90_deg) {
+    return {-desiredState.speed, desiredState.angle + Rotation2d{180_deg}};
+  } else {
+    return {desiredState.speed, desiredState.angle};
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/Trajectory.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/Trajectory.cpp
index de47547..5393f83 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/Trajectory.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/Trajectory.cpp
@@ -5,6 +5,7 @@
 #include "frc/trajectory/Trajectory.h"
 
 #include <algorithm>
+#include <stdexcept>
 
 #include <wpi/MathExtras.h>
 #include <wpi/json.h>
@@ -54,10 +55,20 @@
 }
 
 Trajectory::Trajectory(const std::vector<State>& states) : m_states(states) {
+  if (m_states.empty()) {
+    throw std::invalid_argument(
+        "Trajectory manually initialized with no states.");
+  }
+
   m_totalTime = states.back().t;
 }
 
 Trajectory::State Trajectory::Sample(units::second_t t) const {
+  if (m_states.empty()) {
+    throw std::runtime_error(
+        "Trajectory cannot be sampled if it has no states.");
+  }
+
   if (t <= m_states.front().t) {
     return m_states.front();
   }
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp
index 92d52ed..4c0a55e 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp
@@ -223,10 +223,9 @@
 
     if (minMaxAccel.minAcceleration > minMaxAccel.maxAcceleration) {
       throw std::runtime_error(
-          "The constraint's min acceleration was greater than its max "
-          "acceleration. To debug this, remove all constraints from the config "
-          "and add each one individually. If the offending constraint was "
-          "packaged with WPILib, please file a bug report.");
+          "There was an infeasible trajectory constraint. To determine which "
+          "one, remove all constraints from the TrajectoryConfig and add them "
+          "back one-by-one.");
     }
 
     state->minAcceleration = units::math::max(
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/TrajectoryUtil.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/TrajectoryUtil.cpp
index 169b642..da9c955 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/TrajectoryUtil.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/TrajectoryUtil.cpp
@@ -7,9 +7,8 @@
 #include <system_error>
 
 #include <fmt/format.h>
-#include <wpi/SmallString.h>
+#include <wpi/MemoryBuffer.h>
 #include <wpi/json.h>
-#include <wpi/raw_istream.h>
 #include <wpi/raw_ostream.h>
 
 using namespace frc;
@@ -29,15 +28,14 @@
 }
 
 Trajectory TrajectoryUtil::FromPathweaverJson(std::string_view path) {
-  std::error_code error_code;
-
-  wpi::raw_fd_istream input{path, error_code};
-  if (error_code) {
+  std::error_code ec;
+  std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+      wpi::MemoryBuffer::GetFile(path, ec);
+  if (fileBuffer == nullptr || ec) {
     throw std::runtime_error(fmt::format("Cannot open file: {}", path));
   }
 
-  wpi::json json;
-  input >> json;
+  wpi::json json = wpi::json::parse(fileBuffer->begin(), fileBuffer->end());
 
   return Trajectory{json.get<std::vector<Trajectory::State>>()};
 }
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/constraint/DifferentialDriveKinematicsConstraint.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/constraint/DifferentialDriveKinematicsConstraint.cpp
index 2a308db..460ff8b 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/constraint/DifferentialDriveKinematicsConstraint.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/constraint/DifferentialDriveKinematicsConstraint.cpp
@@ -7,9 +7,8 @@
 using namespace frc;
 
 DifferentialDriveKinematicsConstraint::DifferentialDriveKinematicsConstraint(
-    const DifferentialDriveKinematics& kinematics,
-    units::meters_per_second_t maxSpeed)
-    : m_kinematics(kinematics), m_maxSpeed(maxSpeed) {}
+    DifferentialDriveKinematics kinematics, units::meters_per_second_t maxSpeed)
+    : m_kinematics(std::move(kinematics)), m_maxSpeed(maxSpeed) {}
 
 units::meters_per_second_t DifferentialDriveKinematicsConstraint::MaxVelocity(
     const Pose2d& pose, units::curvature_t curvature,
diff --git a/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/constraint/DifferentialDriveVoltageConstraint.cpp b/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/constraint/DifferentialDriveVoltageConstraint.cpp
index d60b4b6..46c306e 100644
--- a/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/constraint/DifferentialDriveVoltageConstraint.cpp
+++ b/third_party/allwpilib/wpimath/src/main/native/cpp/trajectory/constraint/DifferentialDriveVoltageConstraint.cpp
@@ -15,9 +15,9 @@
 
 DifferentialDriveVoltageConstraint::DifferentialDriveVoltageConstraint(
     const SimpleMotorFeedforward<units::meter>& feedforward,
-    const DifferentialDriveKinematics& kinematics, units::volt_t maxVoltage)
+    DifferentialDriveKinematics kinematics, units::volt_t maxVoltage)
     : m_feedforward(feedforward),
-      m_kinematics(kinematics),
+      m_kinematics(std::move(kinematics)),
       m_maxVoltage(maxVoltage) {}
 
 units::meters_per_second_t DifferentialDriveVoltageConstraint::MaxVelocity(
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/DARE.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/DARE.h
new file mode 100644
index 0000000..6a3104e
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/DARE.h
@@ -0,0 +1,406 @@
+// 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 <stdexcept>
+#include <string>
+
+#include <Eigen/Cholesky>
+#include <Eigen/Core>
+#include <Eigen/LU>
+
+#include "frc/StateSpaceUtil.h"
+#include "frc/fmt/Eigen.h"
+
+// Works cited:
+//
+// [1] E. K.-W. Chu, H.-Y. Fan, W.-W. Lin & C.-S. Wang "Structure-Preserving
+//     Algorithms for Periodic Discrete-Time Algebraic Riccati Equations",
+//     International Journal of Control, 77:8, 767-788, 2004.
+//     DOI: 10.1080/00207170410001714988
+
+namespace frc {
+
+namespace detail {
+
+/**
+ * Checks the preconditions of A, B, and Q for the DARE solver.
+ *
+ * @tparam States Number of states.
+ * @tparam Inputs Number of inputs.
+ * @param A The system matrix.
+ * @param B The input matrix.
+ * @param Q The state cost matrix.
+ * @throws std::invalid_argument if Q isn't symmetric positive semidefinite.
+ * @throws std::invalid_argument if the (A, B) pair isn't stabilizable.
+ * @throws std::invalid_argument if the (A, C) pair where Q = CᵀC isn't
+ *   detectable.
+ */
+template <int States, int Inputs>
+void CheckDARE_ABQ(const Eigen::Matrix<double, States, States>& A,
+                   const Eigen::Matrix<double, States, Inputs>& B,
+                   const Eigen::Matrix<double, States, States>& Q) {
+  // Require Q be symmetric
+  if ((Q - Q.transpose()).norm() > 1e-10) {
+    std::string msg = fmt::format("Q isn't symmetric!\n\nQ =\n{}\n", Q);
+    throw std::invalid_argument(msg);
+  }
+
+  // Require Q be positive semidefinite
+  //
+  // If Q is a symmetric matrix with a decomposition LDLᵀ, the number of
+  // positive, negative, and zero diagonal entries in D equals the number of
+  // positive, negative, and zero eigenvalues respectively in Q (see
+  // https://en.wikipedia.org/wiki/Sylvester's_law_of_inertia).
+  //
+  // Therefore, D having no negative diagonal entries is sufficient to prove Q
+  // is positive semidefinite.
+  auto Q_ldlt = Q.ldlt();
+  if (Q_ldlt.info() != Eigen::Success ||
+      (Q_ldlt.vectorD().array() < 0.0).any()) {
+    std::string msg =
+        fmt::format("Q isn't positive semidefinite!\n\nQ =\n{}\n", Q);
+    throw std::invalid_argument(msg);
+  }
+
+  // Require (A, B) pair be stabilizable
+  if (!IsStabilizable<States, Inputs>(A, B)) {
+    std::string msg = fmt::format(
+        "The (A, B) pair isn't stabilizable!\n\nA =\n{}\nB =\n{}\n", A, B);
+    throw std::invalid_argument(msg);
+  }
+
+  // Require (A, C) pair be detectable where Q = CᵀC
+  //
+  // Q = CᵀC = PᵀLDLᵀP
+  // Cᵀ = PᵀL√(D)
+  // C = (PᵀL√(D))ᵀ
+  {
+    Eigen::Matrix<double, States, States> C =
+        (Q_ldlt.transpositionsP().transpose() *
+         Eigen::Matrix<double, States, States>{Q_ldlt.matrixL()} *
+         Q_ldlt.vectorD().cwiseSqrt().asDiagonal())
+            .transpose();
+
+    if (!IsDetectable<States, States>(A, C)) {
+      std::string msg = fmt::format(
+          "The (A, C) pair where Q = CᵀC isn't detectable!\n\nA =\n{}\nQ "
+          "=\n{}\n",
+          A, Q);
+      throw std::invalid_argument(msg);
+    }
+  }
+}
+
+/**
+ * Computes the unique stabilizing solution X to the discrete-time algebraic
+ * Riccati equation:
+ *
+ *   AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
+ *
+ * This internal function skips expensive precondition checks for increased
+ * performance. The solver may hang if any of the following occur:
+ * <ul>
+ *   <li>Q isn't symmetric positive semidefinite</li>
+ *   <li>R isn't symmetric positive definite</li>
+ *   <li>The (A, B) pair isn't stabilizable</li>
+ *   <li>The (A, C) pair where Q = CᵀC isn't detectable</li>
+ * </ul>
+ * Only use this function if you're sure the preconditions are met.
+ *
+ * @tparam States Number of states.
+ * @tparam Inputs Number of inputs.
+ * @param A The system matrix.
+ * @param B The input matrix.
+ * @param Q The state cost matrix.
+ * @param R_llt The LLT decomposition of the input cost matrix.
+ */
+template <int States, int Inputs>
+Eigen::Matrix<double, States, States> DARE(
+    const Eigen::Matrix<double, States, States>& A,
+    const Eigen::Matrix<double, States, Inputs>& B,
+    const Eigen::Matrix<double, States, States>& Q,
+    const Eigen::LLT<Eigen::Matrix<double, Inputs, Inputs>>& R_llt) {
+  using StateMatrix = Eigen::Matrix<double, States, States>;
+
+  // Implements the SDA algorithm on page 5 of [1].
+
+  // A₀ = A
+  StateMatrix A_k = A;
+
+  // G₀ = BR⁻¹Bᵀ
+  //
+  // See equation (4) of [1].
+  StateMatrix G_k = B * R_llt.solve(B.transpose());
+
+  // H₀ = Q
+  //
+  // See equation (4) of [1].
+  StateMatrix H_k;
+  StateMatrix H_k1 = Q;
+
+  do {
+    H_k = H_k1;
+
+    // W = I + GₖHₖ
+    StateMatrix W = StateMatrix::Identity(H_k.rows(), H_k.cols()) + G_k * H_k;
+
+    auto W_solver = W.lu();
+
+    // Solve WV₁ = Aₖ for V₁
+    StateMatrix V_1 = W_solver.solve(A_k);
+
+    // Solve V₂Wᵀ = Gₖ for V₂
+    //
+    // We want to put V₂Wᵀ = Gₖ into Ax = b form so we can solve it more
+    // efficiently.
+    //
+    // V₂Wᵀ = Gₖ
+    // (V₂Wᵀ)ᵀ = Gₖᵀ
+    // WV₂ᵀ = Gₖᵀ
+    //
+    // The solution of Ax = b can be found via x = A.solve(b).
+    //
+    // V₂ᵀ = W.solve(Gₖᵀ)
+    // V₂ = W.solve(Gₖᵀ)ᵀ
+    StateMatrix V_2 = W_solver.solve(G_k.transpose()).transpose();
+
+    // Gₖ₊₁ = Gₖ + AₖV₂Aₖᵀ
+    G_k += A_k * V_2 * A_k.transpose();
+
+    // Hₖ₊₁ = Hₖ + V₁ᵀHₖAₖ
+    H_k1 = H_k + V_1.transpose() * H_k * A_k;
+
+    // Aₖ₊₁ = AₖV₁
+    A_k *= V_1;
+
+    // while |Hₖ₊₁ − Hₖ| > ε |Hₖ₊₁|
+  } while ((H_k1 - H_k).norm() > 1e-10 * H_k1.norm());
+
+  return H_k1;
+}
+
+/**
+Computes the unique stabilizing solution X to the discrete-time algebraic
+Riccati equation:
+
+  AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
+
+This is equivalent to solving the original DARE:
+
+  A₂ᵀXA₂ − X − A₂ᵀXB(BᵀXB + R)⁻¹BᵀXA₂ + Q₂ = 0
+
+where A₂ and Q₂ are a change of variables:
+
+  A₂ = A − BR⁻¹Nᵀ and Q₂ = Q − NR⁻¹Nᵀ
+
+This overload of the DARE is useful for finding the control law uₖ that
+minimizes the following cost function subject to xₖ₊₁ = Axₖ + Buₖ.
+
+@verbatim
+    ∞ [xₖ]ᵀ[Q  N][xₖ]
+J = Σ [uₖ] [Nᵀ R][uₖ] ΔT
+   k=0
+@endverbatim
+
+This is a more general form of the following. The linear-quadratic regulator
+is the feedback control law uₖ that minimizes the following cost function
+subject to xₖ₊₁ = Axₖ + Buₖ:
+
+@verbatim
+    ∞
+J = Σ (xₖᵀQxₖ + uₖᵀRuₖ) ΔT
+   k=0
+@endverbatim
+
+This can be refactored as:
+
+@verbatim
+    ∞ [xₖ]ᵀ[Q 0][xₖ]
+J = Σ [uₖ] [0 R][uₖ] ΔT
+   k=0
+@endverbatim
+
+This internal function skips expensive precondition checks for increased
+performance. The solver may hang if any of the following occur:
+<ul>
+  <li>Q₂ isn't symmetric positive semidefinite</li>
+  <li>R isn't symmetric positive definite</li>
+  <li>The (A₂, B) pair isn't stabilizable</li>
+  <li>The (A₂, C) pair where Q₂ = CᵀC isn't detectable</li>
+</ul>
+Only use this function if you're sure the preconditions are met.
+
+@tparam States Number of states.
+@tparam Inputs Number of inputs.
+@param A The system matrix.
+@param B The input matrix.
+@param Q The state cost matrix.
+@param R_llt The LLT decomposition of the input cost matrix.
+@param N The state-input cross cost matrix.
+*/
+template <int States, int Inputs>
+Eigen::Matrix<double, States, States> DARE(
+    const Eigen::Matrix<double, States, States>& A,
+    const Eigen::Matrix<double, States, Inputs>& B,
+    const Eigen::Matrix<double, States, States>& Q,
+    const Eigen::LLT<Eigen::Matrix<double, Inputs, Inputs>>& R_llt,
+    const Eigen::Matrix<double, Inputs, Inputs>& N) {
+  // This is a change of variables to make the DARE that includes Q, R, and N
+  // cost matrices fit the form of the DARE that includes only Q and R cost
+  // matrices.
+  //
+  // This is equivalent to solving the original DARE:
+  //
+  //   A₂ᵀXA₂ − X − A₂ᵀXB(BᵀXB + R)⁻¹BᵀXA₂ + Q₂ = 0
+  //
+  // where A₂ and Q₂ are a change of variables:
+  //
+  //   A₂ = A − BR⁻¹Nᵀ and Q₂ = Q − NR⁻¹Nᵀ
+  return detail::DARE<States, Inputs>(A - B * R_llt.solve(N.transpose()), B,
+                                      Q - N * R_llt.solve(N.transpose()),
+                                      R_llt);
+}
+
+}  // namespace detail
+
+/**
+ * Computes the unique stabilizing solution X to the discrete-time algebraic
+ * Riccati equation:
+ *
+ *   AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
+ *
+ * @tparam States Number of states.
+ * @tparam Inputs Number of inputs.
+ * @param A The system matrix.
+ * @param B The input matrix.
+ * @param Q The state cost matrix.
+ * @param R The input cost matrix.
+ * @throws std::invalid_argument if Q isn't symmetric positive semidefinite.
+ * @throws std::invalid_argument if R isn't symmetric positive definite.
+ * @throws std::invalid_argument if the (A, B) pair isn't stabilizable.
+ * @throws std::invalid_argument if the (A, C) pair where Q = CᵀC isn't
+ *   detectable.
+ */
+template <int States, int Inputs>
+Eigen::Matrix<double, States, States> DARE(
+    const Eigen::Matrix<double, States, States>& A,
+    const Eigen::Matrix<double, States, Inputs>& B,
+    const Eigen::Matrix<double, States, States>& Q,
+    const Eigen::Matrix<double, Inputs, Inputs>& R) {
+  // Require R be symmetric
+  if ((R - R.transpose()).norm() > 1e-10) {
+    std::string msg = fmt::format("R isn't symmetric!\n\nR =\n{}\n", R);
+    throw std::invalid_argument(msg);
+  }
+
+  // Require R be positive definite
+  auto R_llt = R.llt();
+  if (R_llt.info() != Eigen::Success) {
+    std::string msg = fmt::format("R isn't positive definite!\n\nR =\n{}\n", R);
+    throw std::invalid_argument(msg);
+  }
+
+  detail::CheckDARE_ABQ<States, Inputs>(A, B, Q);
+
+  return detail::DARE<States, Inputs>(A, B, Q, R_llt);
+}
+
+/**
+Computes the unique stabilizing solution X to the discrete-time algebraic
+Riccati equation:
+
+  AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
+
+This is equivalent to solving the original DARE:
+
+  A₂ᵀXA₂ − X − A₂ᵀXB(BᵀXB + R)⁻¹BᵀXA₂ + Q₂ = 0
+
+where A₂ and Q₂ are a change of variables:
+
+  A₂ = A − BR⁻¹Nᵀ and Q₂ = Q − NR⁻¹Nᵀ
+
+This overload of the DARE is useful for finding the control law uₖ that
+minimizes the following cost function subject to xₖ₊₁ = Axₖ + Buₖ.
+
+@verbatim
+    ∞ [xₖ]ᵀ[Q  N][xₖ]
+J = Σ [uₖ] [Nᵀ R][uₖ] ΔT
+   k=0
+@endverbatim
+
+This is a more general form of the following. The linear-quadratic regulator
+is the feedback control law uₖ that minimizes the following cost function
+subject to xₖ₊₁ = Axₖ + Buₖ:
+
+@verbatim
+    ∞
+J = Σ (xₖᵀQxₖ + uₖᵀRuₖ) ΔT
+   k=0
+@endverbatim
+
+This can be refactored as:
+
+@verbatim
+    ∞ [xₖ]ᵀ[Q 0][xₖ]
+J = Σ [uₖ] [0 R][uₖ] ΔT
+   k=0
+@endverbatim
+
+@tparam States Number of states.
+@tparam Inputs Number of inputs.
+@param A The system matrix.
+@param B The input matrix.
+@param Q The state cost matrix.
+@param R The input cost matrix.
+@param N The state-input cross cost matrix.
+@throws std::invalid_argument if Q₂ isn't symmetric positive semidefinite.
+@throws std::invalid_argument if R isn't symmetric positive definite.
+@throws std::invalid_argument if the (A₂, B) pair isn't stabilizable.
+@throws std::invalid_argument if the (A₂, C) pair where Q₂ = CᵀC isn't
+  detectable.
+*/
+template <int States, int Inputs>
+Eigen::Matrix<double, States, States> DARE(
+    const Eigen::Matrix<double, States, States>& A,
+    const Eigen::Matrix<double, States, Inputs>& B,
+    const Eigen::Matrix<double, States, States>& Q,
+    const Eigen::Matrix<double, Inputs, Inputs>& R,
+    const Eigen::Matrix<double, States, Inputs>& N) {
+  // Require R be symmetric
+  if ((R - R.transpose()).norm() > 1e-10) {
+    std::string msg = fmt::format("R isn't symmetric!\n\nR =\n{}\n", R);
+    throw std::invalid_argument(msg);
+  }
+
+  // Require R be positive definite
+  auto R_llt = R.llt();
+  if (R_llt.info() != Eigen::Success) {
+    std::string msg = fmt::format("R isn't positive definite!\n\nR =\n{}\n", R);
+    throw std::invalid_argument(msg);
+  }
+
+  // This is a change of variables to make the DARE that includes Q, R, and N
+  // cost matrices fit the form of the DARE that includes only Q and R cost
+  // matrices.
+  //
+  // This is equivalent to solving the original DARE:
+  //
+  //   A₂ᵀXA₂ − X − A₂ᵀXB(BᵀXB + R)⁻¹BᵀXA₂ + Q₂ = 0
+  //
+  // where A₂ and Q₂ are a change of variables:
+  //
+  //   A₂ = A − BR⁻¹Nᵀ and Q₂ = Q − NR⁻¹Nᵀ
+  Eigen::Matrix<double, States, States> A_2 =
+      A - B * R_llt.solve(N.transpose());
+  Eigen::Matrix<double, States, States> Q_2 =
+      Q - N * R_llt.solve(N.transpose());
+
+  detail::CheckDARE_ABQ<States, Inputs>(A_2, B, Q_2);
+
+  return detail::DARE<States, Inputs>(A_2, B, Q_2, R_llt);
+}
+
+}  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/EigenCore.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/EigenCore.h
index b33e9e2..1604a0d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/EigenCore.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/EigenCore.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#include "Eigen/Core"
+#include <Eigen/Core>
 
 namespace frc {
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/MathUtil.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/MathUtil.h
index 24bf857..26f106e 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/MathUtil.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/MathUtil.h
@@ -5,6 +5,7 @@
 #pragma once
 
 #include <numbers>
+#include <type_traits>
 
 #include <wpi/SymbolExports.h>
 
@@ -25,12 +26,11 @@
  * be infinite.
  * @return The value after the deadband is applied.
  */
-template <typename T,
-          typename = std::enable_if_t<std::disjunction_v<
-              std::is_floating_point<T>, units::traits::is_unit_t<T>>>>
+template <typename T>
+  requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
 T ApplyDeadband(T value, T deadband, T maxMagnitude = T{1.0}) {
   T magnitude;
-  if constexpr (std::is_floating_point_v<T>) {
+  if constexpr (std::is_arithmetic_v<T>) {
     magnitude = std::abs(value);
   } else {
     magnitude = units::math::abs(value);
@@ -106,6 +106,58 @@
 }
 
 /**
+ * Checks if the given value matches an expected value within a certain
+ * tolerance.
+ *
+ * @param expected The expected value
+ * @param actual The actual value
+ * @param tolerance The allowed difference between the actual and the expected
+ * value
+ * @return Whether or not the actual value is within the allowed tolerance
+ */
+template <typename T>
+  requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
+constexpr bool IsNear(T expected, T actual, T tolerance) {
+  if constexpr (std::is_arithmetic_v<T>) {
+    return std::abs(expected - actual) < tolerance;
+  } else {
+    return units::math::abs(expected - actual) < tolerance;
+  }
+}
+
+/**
+ * Checks if the given value matches an expected value within a certain
+ * tolerance. Supports continuous input for cases like absolute encoders.
+ *
+ * Continuous input means that the min and max value are considered to be the
+ * same point, and tolerances can be checked across them. A common example
+ * would be for absolute encoders: calling isNear(2, 359, 5, 0, 360) returns
+ * true because 359 is 1 away from 360 (which is treated as the same as 0) and
+ * 2 is 2 away from 0, adding up to an error of 3 degrees, which is within the
+ * given tolerance of 5.
+ *
+ * @param expected The expected value
+ * @param actual The actual value
+ * @param tolerance The allowed difference between the actual and the expected
+ * value
+ * @param min Smallest value before wrapping around to the largest value
+ * @param max Largest value before wrapping around to the smallest value
+ * @return Whether or not the actual value is within the allowed tolerance
+ */
+template <typename T>
+  requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
+constexpr bool IsNear(T expected, T actual, T tolerance, T min, T max) {
+  T errorBound = (max - min) / 2.0;
+  T error = frc::InputModulus<T>(expected - actual, -errorBound, errorBound);
+
+  if constexpr (std::is_arithmetic_v<T>) {
+    return std::abs(error) < tolerance;
+  } else {
+    return units::math::abs(error) < tolerance;
+  }
+}
+
+/**
  * Wraps an angle to the range -pi to pi radians (-180 to 180 degrees).
  *
  * @param angle Angle to wrap.
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/StateSpaceUtil.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/StateSpaceUtil.h
index 0345b46..3aa2e75 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/StateSpaceUtil.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/StateSpaceUtil.h
@@ -6,31 +6,21 @@
 
 #include <array>
 #include <cmath>
+#include <concepts>
 #include <limits>
 #include <random>
-#include <type_traits>
 
+#include <Eigen/Eigenvalues>
+#include <Eigen/QR>
 #include <wpi/SymbolExports.h>
 #include <wpi/deprecated.h>
 
-#include "Eigen/Eigenvalues"
-#include "Eigen/QR"
 #include "frc/EigenCore.h"
 #include "frc/geometry/Pose2d.h"
 
 namespace frc {
 namespace detail {
 
-template <int Rows, int Cols, typename Matrix, typename T, typename... Ts>
-void MatrixImpl(Matrix& result, T elem, Ts... elems) {
-  constexpr int count = Rows * Cols - (sizeof...(Ts) + 1);
-
-  result(count / Cols, count % Cols) = elem;
-  if constexpr (sizeof...(Ts) > 0) {
-    MatrixImpl<Rows, Cols>(result, elems...);
-  }
-}
-
 template <typename Matrix, typename T, typename... Ts>
 void CostMatrixImpl(Matrix& result, T elem, Ts... elems) {
   if (elem == std::numeric_limits<double>::infinity()) {
@@ -66,26 +56,37 @@
 template <int States, int Inputs>
 bool IsStabilizableImpl(const Matrixd<States, States>& A,
                         const Matrixd<States, Inputs>& B) {
-  Eigen::EigenSolver<Matrixd<States, States>> es{A};
+  Eigen::EigenSolver<Matrixd<States, States>> es{A, false};
 
-  for (int i = 0; i < States; ++i) {
-    if (es.eigenvalues()[i].real() * es.eigenvalues()[i].real() +
-            es.eigenvalues()[i].imag() * es.eigenvalues()[i].imag() <
-        1) {
+  for (int i = 0; i < A.rows(); ++i) {
+    if (std::norm(es.eigenvalues()[i]) < 1) {
       continue;
     }
 
-    Eigen::Matrix<std::complex<double>, States, States + Inputs> E;
-    E << es.eigenvalues()[i] * Eigen::Matrix<std::complex<double>, States,
-                                             States>::Identity() -
-             A,
-        B;
+    if constexpr (States != Eigen::Dynamic && Inputs != Eigen::Dynamic) {
+      Eigen::Matrix<std::complex<double>, States, States + Inputs> E;
+      E << es.eigenvalues()[i] * Eigen::Matrix<std::complex<double>, States,
+                                               States>::Identity() -
+               A,
+          B;
 
-    Eigen::ColPivHouseholderQR<
-        Eigen::Matrix<std::complex<double>, States, States + Inputs>>
-        qr{E};
-    if (qr.rank() < States) {
-      return false;
+      Eigen::ColPivHouseholderQR<
+          Eigen::Matrix<std::complex<double>, States, States + Inputs>>
+          qr{E};
+      if (qr.rank() < States) {
+        return false;
+      }
+    } else {
+      Eigen::MatrixXcd E{A.rows(), A.rows() + B.cols()};
+      E << es.eigenvalues()[i] *
+                   Eigen::MatrixXcd::Identity(A.rows(), A.rows()) -
+               A,
+          B;
+
+      Eigen::ColPivHouseholderQR<Eigen::MatrixXcd> qr{E};
+      if (qr.rank() < A.rows()) {
+        return false;
+      }
     }
   }
   return true;
@@ -106,8 +107,7 @@
  *                   of the control inputs from no actuation.
  * @return State excursion or control effort cost matrix.
  */
-template <typename... Ts, typename = std::enable_if_t<
-                              std::conjunction_v<std::is_same<double, Ts>...>>>
+template <std::same_as<double>... Ts>
 Matrixd<sizeof...(Ts), sizeof...(Ts)> MakeCostMatrix(Ts... tolerances) {
   Eigen::DiagonalMatrix<double, sizeof...(Ts)> result;
   detail::CostMatrixImpl(result.diagonal(), tolerances...);
@@ -126,8 +126,7 @@
  *                output measurement.
  * @return Process noise or measurement noise covariance matrix.
  */
-template <typename... Ts, typename = std::enable_if_t<
-                              std::conjunction_v<std::is_same<double, Ts>...>>>
+template <std::same_as<double>... Ts>
 Matrixd<sizeof...(Ts), sizeof...(Ts)> MakeCovMatrix(Ts... stdDevs) {
   Eigen::DiagonalMatrix<double, sizeof...(Ts)> result;
   detail::CovMatrixImpl(result.diagonal(), stdDevs...);
@@ -138,7 +137,8 @@
  * Creates a cost matrix from the given vector for use with LQR.
  *
  * The cost matrix is constructed using Bryson's rule. The inverse square of
- * each element in the input is taken and placed on the cost matrix diagonal.
+ * each element in the input is placed on the cost matrix diagonal. If a
+ * tolerance is infinity, its cost matrix entry is set to zero.
  *
  * @param costs An array. For a Q matrix, its elements are the maximum allowed
  *              excursions of the states from the reference. For an R matrix,
@@ -151,7 +151,11 @@
   Eigen::DiagonalMatrix<double, N> result;
   auto& diag = result.diagonal();
   for (size_t i = 0; i < N; ++i) {
-    diag(i) = 1.0 / std::pow(costs[i], 2);
+    if (costs[i] == std::numeric_limits<double>::infinity()) {
+      diag(i) = 0.0;
+    } else {
+      diag(i) = 1.0 / std::pow(costs[i], 2);
+    }
   }
   return result;
 }
@@ -178,8 +182,7 @@
   return result;
 }
 
-template <typename... Ts, typename = std::enable_if_t<
-                              std::conjunction_v<std::is_same<double, Ts>...>>>
+template <std::same_as<double>... Ts>
 Matrixd<sizeof...(Ts), 1> MakeWhiteNoiseVector(Ts... stdDevs) {
   Matrixd<sizeof...(Ts), 1> result;
   detail::WhiteNoiseVectorImpl(result, stdDevs...);
@@ -221,7 +224,7 @@
  * @return The vector.
  */
 WPILIB_DLLEXPORT
-Vectord<3> PoseTo3dVector(const Pose2d& pose);
+Eigen::Vector3d PoseTo3dVector(const Pose2d& pose);
 
 /**
  * Converts a Pose2d into a vector of [x, y, std::cos(theta), std::sin(theta)].
@@ -231,14 +234,14 @@
  * @return The vector.
  */
 WPILIB_DLLEXPORT
-Vectord<4> PoseTo4dVector(const Pose2d& pose);
+Eigen::Vector4d PoseTo4dVector(const Pose2d& pose);
 
 /**
  * Returns true if (A, B) is a stabilizable pair.
  *
  * (A, B) is stabilizable if and only if the uncontrollable eigenvalues of A, if
  * any, have absolute values less than one, where an eigenvalue is
- * uncontrollable if rank(λI - A, B) < n where n is the number of states.
+ * uncontrollable if rank([λI - A, B]) < n where n is the number of states.
  *
  * @tparam States The number of states.
  * @tparam Inputs The number of inputs.
@@ -256,7 +259,7 @@
  *
  * (A, C) is detectable if and only if the unobservable eigenvalues of A, if
  * any, have absolute values less than one, where an eigenvalue is unobservable
- * if rank(λI - A; C) < n where n is the number of states.
+ * if rank([λI - A; C]) < n where n is the number of states.
  *
  * @tparam States The number of states.
  * @tparam Outputs The number of outputs.
@@ -282,6 +285,12 @@
 WPILIB_DLLEXPORT bool IsStabilizable<2, 1>(const Matrixd<2, 2>& A,
                                            const Matrixd<2, 1>& B);
 
+// Template specializations are used here to make common state-input pairs
+// compile faster.
+template <>
+WPILIB_DLLEXPORT bool IsStabilizable<Eigen::Dynamic, Eigen::Dynamic>(
+    const Eigen::MatrixXd& A, const Eigen::MatrixXd& B);
+
 /**
  * Converts a Pose2d into a vector of [x, y, theta].
  *
@@ -290,7 +299,7 @@
  * @return The vector.
  */
 WPILIB_DLLEXPORT
-Vectord<3> PoseToVector(const Pose2d& pose);
+Eigen::Vector3d PoseToVector(const Pose2d& pose);
 
 /**
  * Clamps input vector between system's minimum and maximum allowable input.
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h
index 21aad25..6306457 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h
@@ -7,7 +7,8 @@
 #include <array>
 #include <functional>
 
-#include "Eigen/QR"
+#include <Eigen/QR>
+
 #include "frc/EigenCore.h"
 #include "frc/system/NumericalJacobian.h"
 #include "units/time.h"
@@ -165,6 +166,9 @@
   InputVector Calculate(const StateVector& r, const StateVector& nextR) {
     StateVector rDot = (nextR - r) / m_dt.value();
 
+    // ṙ = f(r) + Bu
+    // Bu = ṙ − f(r)
+    // u = B⁺(ṙ − f(r))
     m_uff = m_B.householderQr().solve(rDot - m_f(r, InputVector::Zero()));
 
     m_r = nextR;
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/DifferentialDriveAccelerationLimiter.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/DifferentialDriveAccelerationLimiter.h
index 3a5148a..d84e3ce 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/DifferentialDriveAccelerationLimiter.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/DifferentialDriveAccelerationLimiter.h
@@ -4,9 +4,9 @@
 
 #pragma once
 
+#include <Eigen/Core>
 #include <wpi/SymbolExports.h>
 
-#include "Eigen/Core"
 #include "frc/controller/DifferentialDriveWheelVoltages.h"
 #include "frc/system/LinearSystem.h"
 #include "units/acceleration.h"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ElevatorFeedforward.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ElevatorFeedforward.h
index bbe7720..62a7bad 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ElevatorFeedforward.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ElevatorFeedforward.h
@@ -6,6 +6,9 @@
 
 #include <wpi/MathExtras.h>
 
+#include "frc/EigenCore.h"
+#include "frc/controller/LinearPlantInversionFeedforward.h"
+#include "frc/system/plant/LinearSystemId.h"
 #include "units/length.h"
 #include "units/time.h"
 #include "units/voltage.h"
@@ -54,6 +57,50 @@
     return kS * wpi::sgn(velocity) + kG + kV * velocity + kA * acceleration;
   }
 
+  /**
+   * Calculates the feedforward from the gains and setpoints.
+   *
+   * @param currentVelocity The current velocity setpoint, in distance per
+   *                        second.
+   * @param nextVelocity    The next velocity setpoint, in distance per second.
+   * @param dt              Time between velocity setpoints in seconds.
+   * @return The computed feedforward, in volts.
+   */
+  units::volt_t Calculate(units::unit_t<Velocity> currentVelocity,
+                          units::unit_t<Velocity> nextVelocity,
+                          units::second_t dt) const {
+    // Discretize the affine model.
+    //
+    //   dx/dt = Ax + Bu + c
+    //   dx/dt = Ax + B(u + B⁺c)
+    //   xₖ₊₁ = eᴬᵀxₖ + A⁻¹(eᴬᵀ - I)B(uₖ + B⁺cₖ)
+    //   xₖ₊₁ = A_d xₖ + B_d (uₖ + B⁺cₖ)
+    //   xₖ₊₁ = A_d xₖ + B_duₖ + B_d B⁺cₖ
+    //
+    // Solve for uₖ.
+    //
+    //   B_duₖ = xₖ₊₁ − A_d xₖ − B_d B⁺cₖ
+    //   uₖ = B_d⁺(xₖ₊₁ − A_d xₖ − B_d B⁺cₖ)
+    //   uₖ = B_d⁺(xₖ₊₁ − A_d xₖ) − B⁺cₖ
+    //
+    // For an elevator with the model
+    // dx/dt = -Kv/Ka x + 1/Ka u - Kg/Ka - Ks/Ka sgn(x),
+    // A = -Kv/Ka, B = 1/Ka, and c = -(Kg/Ka + Ks/Ka sgn(x)). Substitute in B
+    // assuming sgn(x) is a constant for the duration of the step.
+    //
+    //   uₖ = B_d⁺(xₖ₊₁ − A_d xₖ) − Ka(-(Kg/Ka + Ks/Ka sgn(x)))
+    //   uₖ = B_d⁺(xₖ₊₁ − A_d xₖ) + Ka(Kg/Ka + Ks/Ka sgn(x))
+    //   uₖ = B_d⁺(xₖ₊₁ − A_d xₖ) + Kg + Ks sgn(x)
+    auto plant = LinearSystemId::IdentifyVelocitySystem<Distance>(kV, kA);
+    LinearPlantInversionFeedforward<1, 1> feedforward{plant, dt};
+
+    Vectord<1> r{currentVelocity.value()};
+    Vectord<1> nextR{nextVelocity.value()};
+
+    return kG + kS * wpi::sgn(currentVelocity.value()) +
+           units::volt_t{feedforward.Calculate(r, nextR)(0)};
+  }
+
   // Rearranging the main equation from the calculate() method yields the
   // formulas for the methods below:
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/HolonomicDriveController.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/HolonomicDriveController.h
index 9a749c6..6f9568e 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/HolonomicDriveController.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/HolonomicDriveController.h
@@ -42,7 +42,7 @@
    * angle.
    */
   HolonomicDriveController(
-      frc2::PIDController xController, frc2::PIDController yController,
+      PIDController xController, PIDController yController,
       ProfiledPIDController<units::radian> thetaController);
 
   HolonomicDriveController(const HolonomicDriveController&) = default;
@@ -103,14 +103,29 @@
    */
   void SetEnabled(bool enabled);
 
+  /**
+   * Returns the rotation ProfiledPIDController
+   */
+  ProfiledPIDController<units::radian>& getThetaController();
+
+  /**
+   * Returns the X PIDController
+   */
+  PIDController& getXController();
+
+  /**
+   * Returns the Y PIDController
+   */
+  PIDController& getYController();
+
  private:
   Pose2d m_poseError;
   Rotation2d m_rotationError;
   Pose2d m_poseTolerance;
   bool m_enabled = true;
 
-  frc2::PIDController m_xController;
-  frc2::PIDController m_yController;
+  PIDController m_xController;
+  PIDController m_yController;
   ProfiledPIDController<units::radian> m_thetaController;
 
   bool m_firstRun = true;
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ImplicitModelFollower.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ImplicitModelFollower.h
index eef1fc0..3a1230d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ImplicitModelFollower.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ImplicitModelFollower.h
@@ -6,7 +6,8 @@
 
 #include <frc/system/LinearSystem.h>
 
-#include "Eigen/QR"
+#include <Eigen/QR>
+
 #include "frc/EigenCore.h"
 #include "units/time.h"
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LTVDifferentialDriveController.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LTVDifferentialDriveController.h
index 0a336aa..94f3fa3 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LTVDifferentialDriveController.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LTVDifferentialDriveController.h
@@ -23,9 +23,17 @@
 /**
  * The linear time-varying differential drive controller has a similar form to
  * the LQR, but the model used to compute the controller gain is the nonlinear
- * model linearized around the drivetrain's current state. We precomputed gains
- * for important places in our state-space, then interpolated between them with
- * a LUT to save computational resources.
+ * differential drive model linearized around the drivetrain's current state. We
+ * precompute gains for important places in our state-space, then interpolate
+ * between them with a lookup table to save computational resources.
+ *
+ * This controller has a flat hierarchy with pose and wheel velocity references
+ * and voltage outputs. This is different from a Ramsete controller's nested
+ * hierarchy where the top-level controller has a pose reference and chassis
+ * velocity command outputs, and the low-level controller has wheel velocity
+ * references and voltage outputs. Flat hierarchies are easier to tune in one
+ * shot. Furthermore, this controller is more optimal in the "least-squares
+ * error" sense than a controller based on Ramsete.
  *
  * See section 8.7 in Controls Engineering in FRC for a derivation of the
  * control law we used shown in theorem 8.7.4.
@@ -35,12 +43,18 @@
   /**
    * Constructs a linear time-varying differential drive controller.
    *
+   * See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
+   * for how to select the tolerances.
+   *
    * @param plant      The differential drive velocity plant.
    * @param trackwidth The distance between the differential drive's left and
    *                   right wheels.
    * @param Qelems     The maximum desired error tolerance for each state.
    * @param Relems     The maximum desired control effort for each input.
    * @param dt         Discretization timestep.
+   * @throws std::domain_error if max velocity of plant with 12 V input <= 0 m/s
+   *     or >= 15 m/s.
    */
   LTVDifferentialDriveController(const frc::LinearSystem<2, 2, 2>& plant,
                                  units::meter_t trackwidth,
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LTVUnicycleController.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LTVUnicycleController.h
index 83cfe4b..38c4287 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LTVUnicycleController.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LTVUnicycleController.h
@@ -20,8 +20,11 @@
 
 /**
  * The linear time-varying unicycle controller has a similar form to the LQR,
- * but the model used to compute the controller gain is the nonlinear model
- * linearized around the drivetrain's current state.
+ * but the model used to compute the controller gain is the nonlinear unicycle
+ * model linearized around the drivetrain's current state.
+ *
+ * This controller is a roughly drop-in replacement for RamseteController with
+ * more optimal feedback gains in the "least-squares error" sense.
  *
  * See section 8.9 in Controls Engineering in FRC for a derivation of the
  * control law we used shown in theorem 8.9.1.
@@ -36,6 +39,7 @@
    * @param dt Discretization timestep.
    * @param maxVelocity The maximum velocity for the controller gain lookup
    *                    table.
+   * @throws std::domain_error if maxVelocity &lt;= 0.
    */
   explicit LTVUnicycleController(
       units::second_t dt, units::meters_per_second_t maxVelocity = 9_mps);
@@ -43,11 +47,16 @@
   /**
    * Constructs a linear time-varying unicycle controller.
    *
+   * See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
+   * for how to select the tolerances.
+   *
    * @param Qelems The maximum desired error tolerance for each state.
    * @param Relems The maximum desired control effort for each input.
    * @param dt     Discretization timestep.
    * @param maxVelocity The maximum velocity for the controller gain lookup
    *                    table.
+   * @throws std::domain_error if maxVelocity <= 0 m/s or >= 15 m/s.
    */
   LTVUnicycleController(const wpi::array<double, 3>& Qelems,
                         const wpi::array<double, 2>& Relems, units::second_t dt,
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearPlantInversionFeedforward.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearPlantInversionFeedforward.h
index d4cc3c4..1d905e2 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearPlantInversionFeedforward.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearPlantInversionFeedforward.h
@@ -7,7 +7,8 @@
 #include <array>
 #include <functional>
 
-#include "Eigen/QR"
+#include <Eigen/QR>
+
 #include "frc/EigenCore.h"
 #include "frc/system/Discretization.h"
 #include "frc/system/LinearSystem.h"
@@ -137,6 +138,9 @@
    * @return The calculated feedforward.
    */
   InputVector Calculate(const StateVector& r, const StateVector& nextR) {
+    // rₖ₊₁ = Arₖ + Buₖ
+    // Buₖ = rₖ₊₁ − Arₖ
+    // uₖ = B⁺(rₖ₊₁ − Arₖ)
     m_uff = m_B.householderQr().solve(nextR - (m_A * r));
     m_r = nextR;
     return m_uff;
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.h
index 50d6566..979e98a 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.h
@@ -36,6 +36,10 @@
   /**
    * Constructs a controller with the given coefficients and plant.
    *
+   * See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
+   * for how to select the tolerances.
+   *
    * @param plant  The plant being controlled.
    * @param Qelems The maximum desired error tolerance for each state.
    * @param Relems The maximum desired control effort for each input.
@@ -50,6 +54,10 @@
   /**
    * Constructs a controller with the given coefficients and plant.
    *
+   * See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
+   * for how to select the tolerances.
+   *
    * @param A      Continuous system matrix of the plant being controlled.
    * @param B      Continuous input matrix of the plant being controlled.
    * @param Qelems The maximum desired error tolerance for each state.
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.inc
index 87d37ec..1871244 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.inc
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.inc
@@ -7,14 +7,14 @@
 #include <stdexcept>
 #include <string>
 
-#include "Eigen/Cholesky"
-#include "Eigen/Eigenvalues"
-#include "drake/math/discrete_algebraic_riccati_equation.h"
+#include <Eigen/Cholesky>
+#include <unsupported/Eigen/MatrixFunctions>
+
+#include "frc/DARE.h"
 #include "frc/StateSpaceUtil.h"
 #include "frc/controller/LinearQuadraticRegulator.h"
 #include "frc/fmt/Eigen.h"
 #include "frc/system/Discretization.h"
-#include "unsupported/Eigen/MatrixFunctions"
 #include "wpimath/MathShared.h"
 
 namespace frc {
@@ -52,8 +52,7 @@
     throw std::invalid_argument(msg);
   }
 
-  Matrixd<States, States> S =
-      drake::math::DiscreteAlgebraicRiccatiEquation(discA, discB, Q, R);
+  Matrixd<States, States> S = DARE<States, Inputs>(discA, discB, Q, R);
 
   // K = (BᵀSB + R)⁻¹BᵀSA
   m_K = (discB.transpose() * S * discB + R)
@@ -72,8 +71,7 @@
   Matrixd<States, Inputs> discB;
   DiscretizeAB<States, Inputs>(A, B, dt, &discA, &discB);
 
-  Matrixd<States, States> S =
-      drake::math::DiscreteAlgebraicRiccatiEquation(discA, discB, Q, R, N);
+  Matrixd<States, States> S = DARE<States, Inputs>(discA, discB, Q, R, N);
 
   // K = (BᵀSB + R)⁻¹(BᵀSA + Nᵀ)
   m_K = (discB.transpose() * S * discB + R)
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/PIDController.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/PIDController.h
index d6a41d1..0d5b0a3 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/PIDController.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/PIDController.h
@@ -13,7 +13,7 @@
 
 #include "units/time.h"
 
-namespace frc2 {
+namespace frc {
 
 /**
  * Implements a PID control loop.
@@ -74,6 +74,18 @@
   void SetD(double Kd);
 
   /**
+   * Sets the IZone range. When the absolute value of the position error is
+   * greater than IZone, the total accumulated error will reset to zero,
+   * disabling integral gain until the absolute value of the position error is
+   * less than IZone. This is used to prevent integral windup. Must be
+   * non-negative. Passing a value of zero will effectively disable integral
+   * gain. Passing a value of infinity disables IZone functionality.
+   *
+   * @param iZone Maximum magnitude of error to allow integral control.
+   */
+  void SetIZone(double iZone);
+
+  /**
    * Gets the proportional coefficient.
    *
    * @return proportional coefficient
@@ -95,6 +107,13 @@
   double GetD() const;
 
   /**
+   * Get the IZone range.
+   *
+   * @return Maximum magnitude of error to allow integral control.
+   */
+  double GetIZone() const;
+
+  /**
    * Gets the period of this controller.
    *
    * @return The period of the controller.
@@ -221,6 +240,9 @@
   // Factor for "derivative" control
   double m_Kd;
 
+  // The error range where "integral" control applies
+  double m_iZone = std::numeric_limits<double>::infinity();
+
   // The period (in seconds) of the control loop running this controller
   units::second_t m_period;
 
@@ -252,12 +274,9 @@
 
   double m_setpoint = 0;
   double m_measurement = 0;
+
+  bool m_haveSetpoint = false;
+  bool m_haveMeasurement = false;
 };
 
-}  // namespace frc2
-
-namespace frc {
-
-using frc2::PIDController;
-
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ProfiledPIDController.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ProfiledPIDController.h
index 8491118..8f211c6 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ProfiledPIDController.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/controller/ProfiledPIDController.h
@@ -58,7 +58,9 @@
    */
   ProfiledPIDController(double Kp, double Ki, double Kd,
                         Constraints constraints, units::second_t period = 20_ms)
-      : m_controller(Kp, Ki, Kd, period), m_constraints(constraints) {
+      : m_controller{Kp, Ki, Kd, period},
+        m_constraints{constraints},
+        m_profile{m_constraints} {
     int instances = detail::IncrementAndGetProfiledPIDControllerInstances();
     wpi::math::MathSharedStore::ReportUsage(
         wpi::math::MathUsageId::kController_ProfiledPIDController, instances);
@@ -107,6 +109,18 @@
   void SetD(double Kd) { m_controller.SetD(Kd); }
 
   /**
+   * Sets the IZone range. When the absolute value of the position error is
+   * greater than IZone, the total accumulated error will reset to zero,
+   * disabling integral gain until the absolute value of the position error is
+   * less than IZone. This is used to prevent integral windup. Must be
+   * non-negative. Passing a value of zero will effectively disable integral
+   * gain. Passing a value of infinity disables IZone functionality.
+   *
+   * @param iZone Maximum magnitude of error to allow integral control.
+   */
+  void SetIZone(double iZone) { m_controller.SetIZone(iZone); }
+
+  /**
    * Gets the proportional coefficient.
    *
    * @return proportional coefficient
@@ -128,6 +142,13 @@
   double GetD() const { return m_controller.GetD(); }
 
   /**
+   * Get the IZone range.
+   *
+   * @return Maximum magnitude of error to allow integral control.
+   */
+  double GetIZone() const { return m_controller.GetIZone(); }
+
+  /**
    * Gets the period of this controller.
    *
    * @return The period of the controller.
@@ -183,7 +204,16 @@
    *
    * @param constraints Velocity and acceleration constraints for goal.
    */
-  void SetConstraints(Constraints constraints) { m_constraints = constraints; }
+  void SetConstraints(Constraints constraints) {
+    m_constraints = constraints;
+    m_profile = TrapezoidProfile<Distance>{m_constraints};
+  }
+
+  /**
+   * Get the velocity and acceleration constraints for this controller.
+   * @return Velocity and acceleration constraints.
+   */
+  Constraints GetConstraints() { return m_constraints; }
 
   /**
    * Returns the current setpoint of the ProfiledPIDController.
@@ -292,8 +322,7 @@
       m_setpoint.position = setpointMinDistance + measurement;
     }
 
-    frc::TrapezoidProfile<Distance> profile{m_constraints, m_goal, m_setpoint};
-    m_setpoint = profile.Calculate(GetPeriod());
+    m_setpoint = m_profile.Calculate(GetPeriod(), m_goal, m_setpoint);
     return m_controller.Calculate(measurement.value(),
                                   m_setpoint.position.value());
   }
@@ -372,17 +401,22 @@
     builder.AddDoubleProperty(
         "d", [this] { return GetD(); }, [this](double value) { SetD(value); });
     builder.AddDoubleProperty(
+        "izone", [this] { return GetIZone(); },
+        [this](double value) { SetIZone(value); });
+    builder.AddDoubleProperty(
         "goal", [this] { return GetGoal().position.value(); },
         [this](double value) { SetGoal(Distance_t{value}); });
   }
 
  private:
-  frc2::PIDController m_controller;
+  PIDController m_controller;
   Distance_t m_minimumInput{0};
   Distance_t m_maximumInput{0};
+
+  typename frc::TrapezoidProfile<Distance>::Constraints m_constraints;
+  TrapezoidProfile<Distance> m_profile;
   typename frc::TrapezoidProfile<Distance>::State m_goal;
   typename frc::TrapezoidProfile<Distance>::State m_setpoint;
-  typename frc::TrapezoidProfile<Distance>::Constraints m_constraints;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/AngleStatistics.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/AngleStatistics.h
index 0512c68c..026cc67 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/AngleStatistics.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/AngleStatistics.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <functional>
 #include <numbers>
 
 #include "frc/EigenCore.h"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/DifferentialDrivePoseEstimator.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/DifferentialDrivePoseEstimator.h
index 339ccc9..89dcd35 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/DifferentialDrivePoseEstimator.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/DifferentialDrivePoseEstimator.h
@@ -7,13 +7,11 @@
 #include <wpi/SymbolExports.h>
 #include <wpi/array.h>
 
-#include "frc/EigenCore.h"
+#include "frc/estimator/PoseEstimator.h"
 #include "frc/geometry/Pose2d.h"
 #include "frc/geometry/Rotation2d.h"
-#include "frc/interpolation/TimeInterpolatableBuffer.h"
 #include "frc/kinematics/DifferentialDriveKinematics.h"
 #include "frc/kinematics/DifferentialDriveOdometry.h"
-#include "frc/kinematics/DifferentialDriveWheelSpeeds.h"
 #include "units/time.h"
 
 namespace frc {
@@ -32,7 +30,9 @@
  * AddVisionMeasurement() can be called as infrequently as you want; if you
  * never call it, then this class will behave like regular encoder odometry.
  */
-class WPILIB_DLLEXPORT DifferentialDrivePoseEstimator {
+class WPILIB_DLLEXPORT DifferentialDrivePoseEstimator
+    : public PoseEstimator<DifferentialDriveWheelSpeeds,
+                           DifferentialDriveWheelPositions> {
  public:
   /**
    * Constructs a DifferentialDrivePoseEstimator with default standard
@@ -80,19 +80,6 @@
       const wpi::array<double, 3>& visionMeasurementStdDevs);
 
   /**
-   * Sets the pose estimator's trust in vision measurements. This might be used
-   * to change trust in vision measurements after the autonomous period, or to
-   * change trust as distance to a vision target increases.
-   *
-   * @param visionMeasurementStdDevs Standard deviations of the vision pose
-   *     measurement (x position in meters, y position in meters, and heading in
-   *     radians). Increase these numbers to trust the vision pose measurement
-   *     less.
-   */
-  void SetVisionMeasurementStdDevs(
-      const wpi::array<double, 3>& visionMeasurementStdDevs);
-
-  /**
    * Resets the robot's position on the field.
    *
    * @param gyroAngle The current gyro angle.
@@ -101,71 +88,10 @@
    * @param pose The estimated pose of the robot on the field.
    */
   void ResetPosition(const Rotation2d& gyroAngle, units::meter_t leftDistance,
-                     units::meter_t rightDistance, const Pose2d& pose);
-
-  /**
-   * Gets the estimated robot pose.
-   *
-   * @return The estimated robot pose.
-   */
-  Pose2d GetEstimatedPosition() const;
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct
-   * the odometry pose estimate while still accounting for measurement noise.
-   *
-   * This method can be called as infrequently as you want, as long as you are
-   * calling Update() every loop.
-   *
-   * To promote stability of the pose estimate and make it robust to bad vision
-   * data, we recommend only adding vision measurements that are already within
-   * one meter or so of the current pose estimate.
-   *
-   * @param visionRobotPose The pose of the robot as measured by the vision
-   *     camera.
-   * @param timestamp The timestamp of the vision measurement in seconds. Note
-   *     that if you don't use your own time source by calling UpdateWithTime(),
-   *     then you must use a timestamp with an epoch since FPGA startup (i.e.,
-   *     the epoch of this timestamp is the same epoch as
-   *     frc::Timer::GetFPGATimestamp(). This means that you should use
-   *     frc::Timer::GetFPGATimestamp() as your time source in this case.
-   */
-  void AddVisionMeasurement(const Pose2d& visionRobotPose,
-                            units::second_t timestamp);
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct
-   * the odometry pose estimate while still accounting for measurement noise.
-   *
-   * This method can be called as infrequently as you want, as long as you are
-   * calling Update() every loop.
-   *
-   * To promote stability of the pose estimate and make it robust to bad vision
-   * data, we recommend only adding vision measurements that are already within
-   * one meter or so of the current pose estimate.
-   *
-   * Note that the vision measurement standard deviations passed into this
-   * method will continue to apply to future measurements until a subsequent
-   * call to SetVisionMeasurementStdDevs() or this method.
-   *
-   * @param visionRobotPose The pose of the robot as measured by the vision
-   *     camera.
-   * @param timestamp The timestamp of the vision measurement in seconds. Note
-   *     that if you don't use your own time source by calling UpdateWithTime(),
-   *     then you must use a timestamp with an epoch since FPGA startup (i.e.,
-   *     the epoch of this timestamp is the same epoch as
-   *     frc::Timer::GetFPGATimestamp(). This means that you should use
-   *     frc::Timer::GetFPGATimestamp() as your time source in this case.
-   * @param visionMeasurementStdDevs Standard deviations of the vision pose
-   *     measurement (x position in meters, y position in meters, and heading in
-   *     radians). Increase these numbers to trust the vision pose measurement
-   *     less.
-   */
-  void AddVisionMeasurement(
-      const Pose2d& visionRobotPose, units::second_t timestamp,
-      const wpi::array<double, 3>& visionMeasurementStdDevs) {
-    SetVisionMeasurementStdDevs(visionMeasurementStdDevs);
-    AddVisionMeasurement(visionRobotPose, timestamp);
+                     units::meter_t rightDistance, const Pose2d& pose) {
+    PoseEstimator<DifferentialDriveWheelSpeeds,
+                  DifferentialDriveWheelPositions>::
+        ResetPosition(gyroAngle, {leftDistance, rightDistance}, pose);
   }
 
   /**
@@ -179,7 +105,12 @@
    * @return The estimated pose of the robot.
    */
   Pose2d Update(const Rotation2d& gyroAngle, units::meter_t leftDistance,
-                units::meter_t rightDistance);
+                units::meter_t rightDistance) {
+    return PoseEstimator<
+        DifferentialDriveWheelSpeeds,
+        DifferentialDriveWheelPositions>::Update(gyroAngle,
+                                                 {leftDistance, rightDistance});
+  }
 
   /**
    * Updates the pose estimator with wheel encoder and gyro information. This
@@ -195,61 +126,16 @@
   Pose2d UpdateWithTime(units::second_t currentTime,
                         const Rotation2d& gyroAngle,
                         units::meter_t leftDistance,
-                        units::meter_t rightDistance);
+                        units::meter_t rightDistance) {
+    return PoseEstimator<
+        DifferentialDriveWheelSpeeds,
+        DifferentialDriveWheelPositions>::UpdateWithTime(currentTime, gyroAngle,
+                                                         {leftDistance,
+                                                          rightDistance});
+  }
 
  private:
-  struct InterpolationRecord {
-    // The pose observed given the current sensor inputs and the previous pose.
-    Pose2d pose;
-
-    // The current gyro angle.
-    Rotation2d gyroAngle;
-
-    // The distance traveled by the left encoder.
-    units::meter_t leftDistance;
-
-    // The distance traveled by the right encoder.
-    units::meter_t rightDistance;
-
-    /**
-     * Checks equality between this InterpolationRecord and another object.
-     *
-     * @param other The other object.
-     * @return Whether the two objects are equal.
-     */
-    bool operator==(const InterpolationRecord& other) const = default;
-
-    /**
-     * Checks inequality between this InterpolationRecord and another object.
-     *
-     * @param other The other object.
-     * @return Whether the two objects are not equal.
-     */
-    bool operator!=(const InterpolationRecord& other) const = default;
-
-    /**
-     * Interpolates between two InterpolationRecords.
-     *
-     * @param endValue The end value for the interpolation.
-     * @param i The interpolant (fraction).
-     *
-     * @return The interpolated state.
-     */
-    InterpolationRecord Interpolate(DifferentialDriveKinematics& kinematics,
-                                    InterpolationRecord endValue,
-                                    double i) const;
-  };
-
-  DifferentialDriveKinematics& m_kinematics;
-  DifferentialDriveOdometry m_odometry;
-  wpi::array<double, 3> m_q{wpi::empty_array};
-  Eigen::Matrix3d m_visionK = Eigen::Matrix3d::Zero();
-
-  TimeInterpolatableBuffer<InterpolationRecord> m_poseBuffer{
-      1.5_s, [this](const InterpolationRecord& start,
-                    const InterpolationRecord& end, double t) {
-        return start.Interpolate(this->m_kinematics, end, t);
-      }};
+  DifferentialDriveOdometry m_odometryImpl;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.h
index b09e8d9..32ed558 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.h
@@ -52,6 +52,10 @@
   /**
    * Constructs an extended Kalman filter.
    *
+   * See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-observers.html#process-and-measurement-noise-covariance-matrices
+   * for how to select the standard deviations.
+   *
    * @param f                  A vector-valued function of x and u that returns
    *                           the derivative of the state vector.
    * @param h                  A vector-valued function of x and u that returns
@@ -69,6 +73,10 @@
   /**
    * Constructs an extended Kalman filter.
    *
+   * See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-observers.html#process-and-measurement-noise-covariance-matrices
+   * for how to select the standard deviations.
+   *
    * @param f                  A vector-valued function of x and u that returns
    *                           the derivative of the state vector.
    * @param h                  A vector-valued function of x and u that returns
@@ -163,6 +171,33 @@
     Correct<Outputs>(u, y, m_h, m_contR, m_residualFuncY, m_addFuncX);
   }
 
+  /**
+   * Correct the state estimate x-hat using the measurements in y.
+   *
+   * This is useful for when the measurement noise covariances vary.
+   *
+   * @param u Same control input used in the predict step.
+   * @param y Measurement vector.
+   * @param R Continuous measurement noise covariance matrix.
+   */
+  void Correct(const InputVector& u, const OutputVector& y,
+               const Matrixd<Outputs, Outputs>& R) {
+    Correct<Outputs>(u, y, m_h, R, m_residualFuncY, m_addFuncX);
+  }
+
+  /**
+   * Correct the state estimate x-hat using the measurements in y.
+   *
+   * This is useful for when the measurements available during a timestep's
+   * Correct() call vary. The h(x, u) passed to the constructor is used if one
+   * is not provided (the two-argument version of this function).
+   *
+   * @param u Same control input used in the predict step.
+   * @param y Measurement vector.
+   * @param h A vector-valued function of x and u that returns the measurement
+   *          vector.
+   * @param R Continuous measurement noise covariance matrix.
+   */
   template <int Rows>
   void Correct(
       const InputVector& u, const Vectord<Rows>& y,
@@ -180,7 +215,7 @@
    * @param y             Measurement vector.
    * @param h             A vector-valued function of x and u that returns
    *                      the measurement vector.
-   * @param R             Discrete measurement noise covariance matrix.
+   * @param R             Continuous measurement noise covariance matrix.
    * @param residualFuncY A function that computes the residual of two
    *                      measurement vectors (i.e. it subtracts them.)
    * @param addFuncX      A function that adds two state vectors.
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.inc
index a56b8b5..0b0e9de 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.inc
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.inc
@@ -4,8 +4,11 @@
 
 #pragma once
 
-#include "Eigen/Cholesky"
-#include "drake/math/discrete_algebraic_riccati_equation.h"
+#include <functional>
+
+#include <Eigen/Cholesky>
+
+#include "frc/DARE.h"
 #include "frc/StateSpaceUtil.h"
 #include "frc/estimator/ExtendedKalmanFilter.h"
 #include "frc/system/Discretization.h"
@@ -36,13 +39,13 @@
 
   StateMatrix discA;
   StateMatrix discQ;
-  DiscretizeAQTaylor<States>(contA, m_contQ, dt, &discA, &discQ);
+  DiscretizeAQ<States>(contA, m_contQ, dt, &discA, &discQ);
 
   Matrixd<Outputs, Outputs> discR = DiscretizeR<Outputs>(m_contR, dt);
 
   if (IsDetectable<States, Outputs>(discA, C) && Outputs <= States) {
-    m_initP = drake::math::DiscreteAlgebraicRiccatiEquation(
-        discA.transpose(), C.transpose(), discQ, discR);
+    m_initP =
+        DARE<States, Outputs>(discA.transpose(), C.transpose(), discQ, discR);
   } else {
     m_initP = StateMatrix::Zero();
   }
@@ -72,13 +75,13 @@
 
   StateMatrix discA;
   StateMatrix discQ;
-  DiscretizeAQTaylor<States>(contA, m_contQ, dt, &discA, &discQ);
+  DiscretizeAQ<States>(contA, m_contQ, dt, &discA, &discQ);
 
   Matrixd<Outputs, Outputs> discR = DiscretizeR<Outputs>(m_contR, dt);
 
   if (IsDetectable<States, Outputs>(discA, C) && Outputs <= States) {
-    m_initP = drake::math::DiscreteAlgebraicRiccatiEquation(
-        discA.transpose(), C.transpose(), discQ, discR);
+    m_initP =
+        DARE<States, Outputs>(discA.transpose(), C.transpose(), discQ, discR);
   } else {
     m_initP = StateMatrix::Zero();
   }
@@ -95,7 +98,7 @@
   // Find discrete A and Q
   StateMatrix discA;
   StateMatrix discQ;
-  DiscretizeAQTaylor<States>(contA, m_contQ, dt, &discA, &discQ);
+  DiscretizeAQ<States>(contA, m_contQ, dt, &discA, &discQ);
 
   m_xHat = RK4(m_f, m_xHat, u, dt);
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/KalmanFilter.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/KalmanFilter.h
index 2121284..f143493 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/KalmanFilter.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/KalmanFilter.h
@@ -46,6 +46,10 @@
   /**
    * Constructs a state-space observer with the given plant.
    *
+   * See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-observers.html#process-and-measurement-noise-covariance-matrices
+   * for how to select the standard deviations.
+   *
    * @param plant              The plant used for the prediction step.
    * @param stateStdDevs       Standard deviations of model states.
    * @param measurementStdDevs Standard deviations of measurements.
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/KalmanFilter.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/KalmanFilter.inc
index ca4f37c..7506c0d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/KalmanFilter.inc
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/KalmanFilter.inc
@@ -10,8 +10,9 @@
 #include <stdexcept>
 #include <string>
 
-#include "Eigen/Cholesky"
-#include "drake/math/discrete_algebraic_riccati_equation.h"
+#include <Eigen/Cholesky>
+
+#include "frc/DARE.h"
 #include "frc/StateSpaceUtil.h"
 #include "frc/estimator/KalmanFilter.h"
 #include "frc/system/Discretization.h"
@@ -31,7 +32,7 @@
 
   Matrixd<States, States> discA;
   Matrixd<States, States> discQ;
-  DiscretizeAQTaylor<States>(plant.A(), contQ, dt, &discA, &discQ);
+  DiscretizeAQ<States>(plant.A(), contQ, dt, &discA, &discQ);
 
   auto discR = DiscretizeR<Outputs>(contR, dt);
 
@@ -47,8 +48,8 @@
     throw std::invalid_argument(msg);
   }
 
-  Matrixd<States, States> P = drake::math::DiscreteAlgebraicRiccatiEquation(
-      discA.transpose(), C.transpose(), discQ, discR);
+  Matrixd<States, States> P =
+      DARE<States, Outputs>(discA.transpose(), C.transpose(), discQ, discR);
 
   // S = CPCᵀ + R
   Matrixd<Outputs, Outputs> S = C * P * C.transpose() + discR;
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/MecanumDrivePoseEstimator.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/MecanumDrivePoseEstimator.h
index d1967e9..d748164 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/MecanumDrivePoseEstimator.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/MecanumDrivePoseEstimator.h
@@ -9,7 +9,7 @@
 #include <wpi/SymbolExports.h>
 #include <wpi/array.h>
 
-#include "frc/EigenCore.h"
+#include "frc/estimator/PoseEstimator.h"
 #include "frc/geometry/Pose2d.h"
 #include "frc/geometry/Rotation2d.h"
 #include "frc/interpolation/TimeInterpolatableBuffer.h"
@@ -32,7 +32,9 @@
  * never call it, then this class will behave mostly like regular encoder
  * odometry.
  */
-class WPILIB_DLLEXPORT MecanumDrivePoseEstimator {
+class WPILIB_DLLEXPORT MecanumDrivePoseEstimator
+    : public PoseEstimator<MecanumDriveWheelSpeeds,
+                           MecanumDriveWheelPositions> {
  public:
   /**
    * Constructs a MecanumDrivePoseEstimator with default standard deviations
@@ -76,172 +78,8 @@
       const Pose2d& initialPose, const wpi::array<double, 3>& stateStdDevs,
       const wpi::array<double, 3>& visionMeasurementStdDevs);
 
-  /**
-   * Sets the pose estimator's trust in vision measurements. This might be used
-   * to change trust in vision measurements after the autonomous period, or to
-   * change trust as distance to a vision target increases.
-   *
-   * @param visionMeasurementStdDevs Standard deviations of the vision pose
-   *     measurement (x position in meters, y position in meters, and heading in
-   *     radians). Increase these numbers to trust the vision pose measurement
-   *     less.
-   */
-  void SetVisionMeasurementStdDevs(
-      const wpi::array<double, 3>& visionMeasurementStdDevs);
-
-  /**
-   * Resets the robot's position on the field.
-   *
-   * The gyroscope angle does not need to be reset in the user's robot code.
-   * The library automatically takes care of offsetting the gyro angle.
-   *
-   * @param gyroAngle The angle reported by the gyroscope.
-   * @param wheelPositions The distances measured at each wheel.
-   * @param pose      The position on the field that your robot is at.
-   */
-  void ResetPosition(const Rotation2d& gyroAngle,
-                     const MecanumDriveWheelPositions& wheelPositions,
-                     const Pose2d& pose);
-
-  /**
-   * Gets the estimated robot pose.
-   *
-   * @return The estimated robot pose in meters.
-   */
-  Pose2d GetEstimatedPosition() const;
-
-  /**
-   * Add a vision measurement to the Kalman Filter. This will correct
-   * the odometry pose estimate while still accounting for measurement noise.
-   *
-   * This method can be called as infrequently as you want, as long as you are
-   * calling Update() every loop.
-   *
-   * To promote stability of the pose estimate and make it robust to bad vision
-   * data, we recommend only adding vision measurements that are already within
-   * one meter or so of the current pose estimate.
-   *
-   * @param visionRobotPose The pose of the robot as measured by the vision
-   *     camera.
-   * @param timestamp The timestamp of the vision measurement in seconds. Note
-   *     that if you don't use your own time source by calling UpdateWithTime()
-   *     then you must use a timestamp with an epoch since FPGA startup (i.e.,
-   *     the epoch of this timestamp is the same epoch as
-   *     frc::Timer::GetFPGATimestamp().) This means that you should use
-   *     frc::Timer::GetFPGATimestamp() as your time source or sync the epochs.
-   */
-  void AddVisionMeasurement(const Pose2d& visionRobotPose,
-                            units::second_t timestamp);
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct
-   * the odometry pose estimate while still accounting for measurement noise.
-   *
-   * This method can be called as infrequently as you want, as long as you are
-   * calling Update() every loop.
-   *
-   * To promote stability of the pose estimate and make it robust to bad vision
-   * data, we recommend only adding vision measurements that are already within
-   * one meter or so of the current pose estimate.
-   *
-   * Note that the vision measurement standard deviations passed into this
-   * method will continue to apply to future measurements until a subsequent
-   * call to SetVisionMeasurementStdDevs() or this method.
-   *
-   * @param visionRobotPose The pose of the robot as measured by the vision
-   *     camera.
-   * @param timestamp The timestamp of the vision measurement in seconds. Note
-   *     that if you don't use your own time source by calling UpdateWithTime(),
-   *     then you must use a timestamp with an epoch since FPGA startup (i.e.,
-   *     the epoch of this timestamp is the same epoch as
-   *     frc::Timer::GetFPGATimestamp(). This means that you should use
-   *     frc::Timer::GetFPGATimestamp() as your time source in this case.
-   * @param visionMeasurementStdDevs Standard deviations of the vision pose
-   *     measurement (x position in meters, y position in meters, and heading in
-   *     radians). Increase these numbers to trust the vision pose measurement
-   *     less.
-   */
-  void AddVisionMeasurement(
-      const Pose2d& visionRobotPose, units::second_t timestamp,
-      const wpi::array<double, 3>& visionMeasurementStdDevs) {
-    SetVisionMeasurementStdDevs(visionMeasurementStdDevs);
-    AddVisionMeasurement(visionRobotPose, timestamp);
-  }
-
-  /**
-   * Updates the pose estimator with wheel encoder and gyro information. This
-   * should be called every loop.
-   *
-   * @param gyroAngle   The current gyro angle.
-   * @param wheelPositions The distances measured at each wheel.
-   * @return The estimated pose of the robot in meters.
-   */
-  Pose2d Update(const Rotation2d& gyroAngle,
-                const MecanumDriveWheelPositions& wheelPositions);
-
-  /**
-   * Updates the pose estimator with wheel encoder and gyro information. This
-   * should be called every loop.
-   *
-   * @param currentTime Time at which this method was called, in seconds.
-   * @param gyroAngle   The current gyroscope angle.
-   * @param wheelPositions The distances measured at each wheel.
-   * @return The estimated pose of the robot in meters.
-   */
-  Pose2d UpdateWithTime(units::second_t currentTime,
-                        const Rotation2d& gyroAngle,
-                        const MecanumDriveWheelPositions& wheelPositions);
-
  private:
-  struct InterpolationRecord {
-    // The pose observed given the current sensor inputs and the previous pose.
-    Pose2d pose;
-
-    // The current gyroscope angle.
-    Rotation2d gyroAngle;
-
-    // The distances measured at each wheel.
-    MecanumDriveWheelPositions wheelPositions;
-
-    /**
-     * Checks equality between this InterpolationRecord and another object.
-     *
-     * @param other The other object.
-     * @return Whether the two objects are equal.
-     */
-    bool operator==(const InterpolationRecord& other) const = default;
-
-    /**
-     * Checks inequality between this InterpolationRecord and another object.
-     *
-     * @param other The other object.
-     * @return Whether the two objects are not equal.
-     */
-    bool operator!=(const InterpolationRecord& other) const = default;
-
-    /**
-     * Interpolates between two InterpolationRecords.
-     *
-     * @param endValue The end value for the interpolation.
-     * @param i The interpolant (fraction).
-     *
-     * @return The interpolated state.
-     */
-    InterpolationRecord Interpolate(MecanumDriveKinematics& kinematics,
-                                    InterpolationRecord endValue,
-                                    double i) const;
-  };
-
-  MecanumDriveKinematics& m_kinematics;
-  MecanumDriveOdometry m_odometry;
-  wpi::array<double, 3> m_q{wpi::empty_array};
-  Eigen::Matrix3d m_visionK = Eigen::Matrix3d::Zero();
-
-  TimeInterpolatableBuffer<InterpolationRecord> m_poseBuffer{
-      1.5_s, [this](const InterpolationRecord& start,
-                    const InterpolationRecord& end, double t) {
-        return start.Interpolate(this->m_kinematics, end, t);
-      }};
+  MecanumDriveOdometry m_odometryImpl;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/PoseEstimator.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/PoseEstimator.h
new file mode 100644
index 0000000..eb437a4
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/PoseEstimator.h
@@ -0,0 +1,228 @@
+// 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 <Eigen/Core>
+#include <wpi/SymbolExports.h>
+#include <wpi/array.h>
+
+#include "frc/geometry/Pose2d.h"
+#include "frc/geometry/Rotation2d.h"
+#include "frc/interpolation/TimeInterpolatableBuffer.h"
+#include "frc/kinematics/Kinematics.h"
+#include "frc/kinematics/Odometry.h"
+#include "frc/kinematics/WheelPositions.h"
+#include "units/time.h"
+#include "wpimath/MathShared.h"
+
+namespace frc {
+/**
+ * This class wraps odometry to fuse latency-compensated
+ * vision measurements with encoder measurements. Robot code should not use this
+ * directly- Instead, use the particular type for your drivetrain (e.g.,
+ * DifferentialDrivePoseEstimator). It will correct for noisy vision
+ * measurements and encoder drift. It is intended to be an easy drop-in for
+ * Odometry.
+ *
+ * Update() should be called every robot loop.
+ *
+ * AddVisionMeasurement() can be called as infrequently as you want; if you
+ * never call it, then this class will behave like regular encoder odometry.
+ */
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+class WPILIB_DLLEXPORT PoseEstimator {
+ public:
+  /**
+   * Constructs a PoseEstimator.
+   *
+   * @param kinematics A correctly-configured kinematics object for your
+   *     drivetrain.
+   * @param odometry A correctly-configured odometry object for your drivetrain.
+   * @param stateStdDevs Standard deviations of the pose estimate (x position in
+   *     meters, y position in meters, and heading in radians). Increase these
+   *     numbers to trust your state estimate less.
+   * @param visionMeasurementStdDevs Standard deviations of the vision pose
+   *     measurement (x position in meters, y position in meters, and heading in
+   *     radians). Increase these numbers to trust the vision pose measurement
+   *     less.
+   */
+  PoseEstimator(Kinematics<WheelSpeeds, WheelPositions>& kinematics,
+                Odometry<WheelSpeeds, WheelPositions>& odometry,
+                const wpi::array<double, 3>& stateStdDevs,
+                const wpi::array<double, 3>& visionMeasurementStdDevs);
+
+  /**
+   * Sets the pose estimator's trust in vision measurements. This might be used
+   * to change trust in vision measurements after the autonomous period, or to
+   * change trust as distance to a vision target increases.
+   *
+   * @param visionMeasurementStdDevs Standard deviations of the vision pose
+   *     measurement (x position in meters, y position in meters, and heading in
+   *     radians). Increase these numbers to trust the vision pose measurement
+   *     less.
+   */
+  void SetVisionMeasurementStdDevs(
+      const wpi::array<double, 3>& visionMeasurementStdDevs);
+
+  /**
+   * Resets the robot's position on the field.
+   *
+   * The gyroscope angle does not need to be reset in the user's robot code.
+   * The library automatically takes care of offsetting the gyro angle.
+   *
+   * @param gyroAngle The current gyro angle.
+   * @param wheelPositions The distances traveled by the encoders.
+   * @param pose The estimated pose of the robot on the field.
+   */
+  void ResetPosition(const Rotation2d& gyroAngle,
+                     const WheelPositions& wheelPositions, const Pose2d& pose);
+
+  /**
+   * Gets the estimated robot pose.
+   *
+   * @return The estimated robot pose in meters.
+   */
+  Pose2d GetEstimatedPosition() const;
+
+  /**
+   * Adds a vision measurement to the Kalman Filter. This will correct
+   * the odometry pose estimate while still accounting for measurement noise.
+   *
+   * This method can be called as infrequently as you want, as long as you are
+   * calling Update() every loop.
+   *
+   * To promote stability of the pose estimate and make it robust to bad vision
+   * data, we recommend only adding vision measurements that are already within
+   * one meter or so of the current pose estimate.
+   *
+   * @param visionRobotPose The pose of the robot as measured by the vision
+   *     camera.
+   * @param timestamp The timestamp of the vision measurement in seconds. Note
+   *     that if you don't use your own time source by calling UpdateWithTime(),
+   *     then you must use a timestamp with an epoch since FPGA startup (i.e.,
+   *     the epoch of this timestamp is the same epoch as
+   *     frc::Timer::GetFPGATimestamp(). This means that you should use
+   *     frc::Timer::GetFPGATimestamp() as your time source in this case.
+   */
+  void AddVisionMeasurement(const Pose2d& visionRobotPose,
+                            units::second_t timestamp);
+
+  /**
+   * Adds a vision measurement to the Kalman Filter. This will correct
+   * the odometry pose estimate while still accounting for measurement noise.
+   *
+   * This method can be called as infrequently as you want, as long as you are
+   * calling Update() every loop.
+   *
+   * To promote stability of the pose estimate and make it robust to bad vision
+   * data, we recommend only adding vision measurements that are already within
+   * one meter or so of the current pose estimate.
+   *
+   * Note that the vision measurement standard deviations passed into this
+   * method will continue to apply to future measurements until a subsequent
+   * call to SetVisionMeasurementStdDevs() or this method.
+   *
+   * @param visionRobotPose The pose of the robot as measured by the vision
+   *     camera.
+   * @param timestamp The timestamp of the vision measurement in seconds. Note
+   *     that if you don't use your own time source by calling UpdateWithTime(),
+   *     then you must use a timestamp with an epoch since FPGA startup (i.e.,
+   *     the epoch of this timestamp is the same epoch as
+   *     frc::Timer::GetFPGATimestamp(). This means that you should use
+   *     frc::Timer::GetFPGATimestamp() as your time source in this case.
+   * @param visionMeasurementStdDevs Standard deviations of the vision pose
+   *     measurement (x position in meters, y position in meters, and heading in
+   *     radians). Increase these numbers to trust the vision pose measurement
+   *     less.
+   */
+  void AddVisionMeasurement(
+      const Pose2d& visionRobotPose, units::second_t timestamp,
+      const wpi::array<double, 3>& visionMeasurementStdDevs) {
+    SetVisionMeasurementStdDevs(visionMeasurementStdDevs);
+    AddVisionMeasurement(visionRobotPose, timestamp);
+  }
+
+  /**
+   * Updates the pose estimator with wheel encoder and gyro information. This
+   * should be called every loop.
+   *
+   * @param gyroAngle      The current gyro angle.
+   * @param wheelPositions The distances traveled by the encoders.
+   *
+   * @return The estimated pose of the robot in meters.
+   */
+  Pose2d Update(const Rotation2d& gyroAngle,
+                const WheelPositions& wheelPositions);
+
+  /**
+   * Updates the pose estimator with wheel encoder and gyro information. This
+   * should be called every loop.
+   *
+   * @param currentTime   The time at which this method was called.
+   * @param gyroAngle     The current gyro angle.
+   * @param wheelPositions The distances traveled by the encoders.
+   *
+   * @return The estimated pose of the robot in meters.
+   */
+  Pose2d UpdateWithTime(units::second_t currentTime,
+                        const Rotation2d& gyroAngle,
+                        const WheelPositions& wheelPositions);
+
+ private:
+  struct InterpolationRecord {
+    // The pose observed given the current sensor inputs and the previous pose.
+    Pose2d pose;
+
+    // The current gyroscope angle.
+    Rotation2d gyroAngle;
+
+    // The distances traveled by the wheels.
+    WheelPositions wheelPositions;
+
+    /**
+     * Checks equality between this InterpolationRecord and another object.
+     *
+     * @param other The other object.
+     * @return Whether the two objects are equal.
+     */
+    bool operator==(const InterpolationRecord& other) const = default;
+
+    /**
+     * Checks inequality between this InterpolationRecord and another object.
+     *
+     * @param other The other object.
+     * @return Whether the two objects are not equal.
+     */
+    bool operator!=(const InterpolationRecord& other) const = default;
+
+    /**
+     * Interpolates between two InterpolationRecords.
+     *
+     * @param endValue The end value for the interpolation.
+     * @param i The interpolant (fraction).
+     *
+     * @return The interpolated state.
+     */
+    InterpolationRecord Interpolate(
+        Kinematics<WheelSpeeds, WheelPositions>& kinematics,
+        InterpolationRecord endValue, double i) const;
+  };
+
+  static constexpr units::second_t kBufferDuration = 1.5_s;
+
+  Kinematics<WheelSpeeds, WheelPositions>& m_kinematics;
+  Odometry<WheelSpeeds, WheelPositions>& m_odometry;
+  wpi::array<double, 3> m_q{wpi::empty_array};
+  Eigen::Matrix3d m_visionK = Eigen::Matrix3d::Zero();
+
+  TimeInterpolatableBuffer<InterpolationRecord> m_poseBuffer{
+      kBufferDuration, [this](const InterpolationRecord& start,
+                              const InterpolationRecord& end, double t) {
+        return start.Interpolate(this->m_kinematics, end, t);
+      }};
+};
+}  // namespace frc
+
+#include "frc/estimator/PoseEstimator.inc"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/PoseEstimator.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/PoseEstimator.inc
new file mode 100644
index 0000000..79d71bc
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/PoseEstimator.inc
@@ -0,0 +1,165 @@
+// 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 "frc/estimator/PoseEstimator.h"
+
+namespace frc {
+
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+PoseEstimator<WheelSpeeds, WheelPositions>::PoseEstimator(
+    Kinematics<WheelSpeeds, WheelPositions>& kinematics,
+    Odometry<WheelSpeeds, WheelPositions>& odometry,
+    const wpi::array<double, 3>& stateStdDevs,
+    const wpi::array<double, 3>& visionMeasurementStdDevs)
+    : m_kinematics(kinematics), m_odometry(odometry) {
+  for (size_t i = 0; i < 3; ++i) {
+    m_q[i] = stateStdDevs[i] * stateStdDevs[i];
+  }
+
+  SetVisionMeasurementStdDevs(visionMeasurementStdDevs);
+}
+
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+void PoseEstimator<WheelSpeeds, WheelPositions>::SetVisionMeasurementStdDevs(
+    const wpi::array<double, 3>& visionMeasurementStdDevs) {
+  wpi::array<double, 3> r{wpi::empty_array};
+  for (size_t i = 0; i < 3; ++i) {
+    r[i] = visionMeasurementStdDevs[i] * visionMeasurementStdDevs[i];
+  }
+
+  // Solve for closed form Kalman gain for continuous Kalman filter with A = 0
+  // and C = I. See wpimath/algorithms.md.
+  for (size_t row = 0; row < 3; ++row) {
+    if (m_q[row] == 0.0) {
+      m_visionK(row, row) = 0.0;
+    } else {
+      m_visionK(row, row) =
+          m_q[row] / (m_q[row] + std::sqrt(m_q[row] * r[row]));
+    }
+  }
+}
+
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+void PoseEstimator<WheelSpeeds, WheelPositions>::ResetPosition(
+    const Rotation2d& gyroAngle, const WheelPositions& wheelPositions,
+    const Pose2d& pose) {
+  // Reset state estimate and error covariance
+  m_odometry.ResetPosition(gyroAngle, wheelPositions, pose);
+  m_poseBuffer.Clear();
+}
+
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+Pose2d PoseEstimator<WheelSpeeds, WheelPositions>::GetEstimatedPosition()
+    const {
+  return m_odometry.GetPose();
+}
+
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+void PoseEstimator<WheelSpeeds, WheelPositions>::AddVisionMeasurement(
+    const Pose2d& visionRobotPose, units::second_t timestamp) {
+  // Step 0: If this measurement is old enough to be outside the pose buffer's
+  // timespan, skip.
+  if (!m_poseBuffer.GetInternalBuffer().empty() &&
+      m_poseBuffer.GetInternalBuffer().front().first - kBufferDuration >
+          timestamp) {
+    return;
+  }
+
+  // Step 1: Get the estimated pose from when the vision measurement was made.
+  auto sample = m_poseBuffer.Sample(timestamp);
+
+  if (!sample.has_value()) {
+    return;
+  }
+
+  // Step 2: Measure the twist between the odometry pose and the vision pose
+  auto twist = sample.value().pose.Log(visionRobotPose);
+
+  // Step 3: We should not trust the twist entirely, so instead we scale this
+  // twist by a Kalman gain matrix representing how much we trust vision
+  // measurements compared to our current pose.
+  Eigen::Vector3d k_times_twist =
+      m_visionK *
+      Eigen::Vector3d{twist.dx.value(), twist.dy.value(), twist.dtheta.value()};
+
+  // Step 4: Convert back to Twist2d
+  Twist2d scaledTwist{units::meter_t{k_times_twist(0)},
+                      units::meter_t{k_times_twist(1)},
+                      units::radian_t{k_times_twist(2)}};
+
+  // Step 5: Reset Odometry to state at sample with vision adjustment.
+  m_odometry.ResetPosition(sample.value().gyroAngle,
+                           sample.value().wheelPositions,
+                           sample.value().pose.Exp(scaledTwist));
+
+  // Step 6: Record the current pose to allow multiple measurements from the
+  // same timestamp
+  m_poseBuffer.AddSample(timestamp,
+                         {GetEstimatedPosition(), sample.value().gyroAngle,
+                          sample.value().wheelPositions});
+
+  // Step 7: Replay odometry inputs between sample time and latest recorded
+  // sample to update the pose buffer and correct odometry.
+  auto internal_buf = m_poseBuffer.GetInternalBuffer();
+
+  auto upper_bound =
+      std::lower_bound(internal_buf.begin(), internal_buf.end(), timestamp,
+                       [](const auto& pair, auto t) { return t > pair.first; });
+
+  for (auto entry = upper_bound; entry != internal_buf.end(); entry++) {
+    UpdateWithTime(entry->first, entry->second.gyroAngle,
+                   entry->second.wheelPositions);
+  }
+}
+
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+Pose2d PoseEstimator<WheelSpeeds, WheelPositions>::Update(
+    const Rotation2d& gyroAngle, const WheelPositions& wheelPositions) {
+  return UpdateWithTime(wpi::math::MathSharedStore::GetTimestamp(), gyroAngle,
+                        wheelPositions);
+}
+
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+Pose2d PoseEstimator<WheelSpeeds, WheelPositions>::UpdateWithTime(
+    units::second_t currentTime, const Rotation2d& gyroAngle,
+    const WheelPositions& wheelPositions) {
+  m_odometry.Update(gyroAngle, wheelPositions);
+
+  WheelPositions internalWheelPositions = wheelPositions;
+
+  m_poseBuffer.AddSample(
+      currentTime, {GetEstimatedPosition(), gyroAngle, internalWheelPositions});
+
+  return GetEstimatedPosition();
+}
+
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+typename PoseEstimator<WheelSpeeds, WheelPositions>::InterpolationRecord
+PoseEstimator<WheelSpeeds, WheelPositions>::InterpolationRecord::Interpolate(
+    Kinematics<WheelSpeeds, WheelPositions>& kinematics,
+    InterpolationRecord endValue, double i) const {
+  if (i < 0) {
+    return *this;
+  } else if (i > 1) {
+    return endValue;
+  } else {
+    // Find the new wheel distance measurements.
+    WheelPositions wheels_lerp =
+        this->wheelPositions.Interpolate(endValue.wheelPositions, i);
+
+    // Find the new gyro angle.
+    auto gyro = wpi::Lerp(this->gyroAngle, endValue.gyroAngle, i);
+
+    // Create a twist to represent the change based on the interpolated
+    // sensor inputs.
+    auto twist = kinematics.ToTwist2d(this->wheelPositions, wheels_lerp);
+    twist.dtheta = (gyro - gyroAngle).Radians();
+
+    return {pose.Exp(twist), gyro, wheels_lerp};
+  }
+}
+
+}  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/SwerveDrivePoseEstimator.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/SwerveDrivePoseEstimator.h
index d07bafe..43831d7 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/SwerveDrivePoseEstimator.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/SwerveDrivePoseEstimator.h
@@ -6,17 +6,15 @@
 
 #include <cmath>
 
-#include <fmt/format.h>
 #include <wpi/SymbolExports.h>
 #include <wpi/array.h>
-#include <wpi/timestamp.h>
 
-#include "frc/EigenCore.h"
+#include "frc/estimator/PoseEstimator.h"
 #include "frc/geometry/Pose2d.h"
 #include "frc/geometry/Rotation2d.h"
-#include "frc/interpolation/TimeInterpolatableBuffer.h"
 #include "frc/kinematics/SwerveDriveKinematics.h"
 #include "frc/kinematics/SwerveDriveOdometry.h"
+#include "frc/kinematics/SwerveDriveWheelPositions.h"
 #include "units/time.h"
 
 namespace frc {
@@ -33,7 +31,9 @@
  * odometry.
  */
 template <size_t NumModules>
-class SwerveDrivePoseEstimator {
+class SwerveDrivePoseEstimator
+    : public PoseEstimator<SwerveDriveWheelSpeeds<NumModules>,
+                           SwerveDriveWheelPositions<NumModules>> {
  public:
   /**
    * Constructs a SwerveDrivePoseEstimator with default standard deviations
@@ -83,14 +83,10 @@
       const wpi::array<SwerveModulePosition, NumModules>& modulePositions,
       const Pose2d& initialPose, const wpi::array<double, 3>& stateStdDevs,
       const wpi::array<double, 3>& visionMeasurementStdDevs)
-      : m_kinematics{kinematics},
-        m_odometry{kinematics, gyroAngle, modulePositions, initialPose} {
-    for (size_t i = 0; i < 3; ++i) {
-      m_q[i] = stateStdDevs[i] * stateStdDevs[i];
-    }
-
-    SetVisionMeasurementStdDevs(visionMeasurementStdDevs);
-  }
+      : PoseEstimator<SwerveDriveWheelSpeeds<NumModules>,
+                      SwerveDriveWheelPositions<NumModules>>(
+            kinematics, m_odometryImpl, stateStdDevs, visionMeasurementStdDevs),
+        m_odometryImpl{kinematics, gyroAngle, modulePositions, initialPose} {}
 
   /**
    * Resets the robot's position on the field.
@@ -107,143 +103,11 @@
       const Rotation2d& gyroAngle,
       const wpi::array<SwerveModulePosition, NumModules>& modulePositions,
       const Pose2d& pose) {
-    // Reset state estimate and error covariance
-    m_odometry.ResetPosition(gyroAngle, modulePositions, pose);
-    m_poseBuffer.Clear();
-  }
-
-  /**
-   * Gets the estimated robot pose.
-   *
-   * @return The estimated robot pose in meters.
-   */
-  Pose2d GetEstimatedPosition() const { return m_odometry.GetPose(); }
-
-  /**
-   * Sets the pose estimator's trust in vision measurements. This might be used
-   * to change trust in vision measurements after the autonomous period, or to
-   * change trust as distance to a vision target increases.
-   *
-   * @param visionMeasurementStdDevs Standard deviations of the vision pose
-   *     measurement (x position in meters, y position in meters, and heading in
-   *     radians). Increase these numbers to trust the vision pose measurement
-   *     less.
-   */
-  void SetVisionMeasurementStdDevs(
-      const wpi::array<double, 3>& visionMeasurementStdDevs) {
-    wpi::array<double, 3> r{wpi::empty_array};
-    for (size_t i = 0; i < 3; ++i) {
-      r[i] = visionMeasurementStdDevs[i] * visionMeasurementStdDevs[i];
-    }
-
-    // Solve for closed form Kalman gain for continuous Kalman filter with A = 0
-    // and C = I. See wpimath/algorithms.md.
-    for (size_t row = 0; row < 3; ++row) {
-      if (m_q[row] == 0.0) {
-        m_visionK(row, row) = 0.0;
-      } else {
-        m_visionK(row, row) =
-            m_q[row] / (m_q[row] + std::sqrt(m_q[row] * r[row]));
-      }
-    }
-  }
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct the
-   * odometry pose estimate while still accounting for measurement noise.
-   *
-   * This method can be called as infrequently as you want, as long as you are
-   * calling Update() every loop.
-   *
-   * To promote stability of the pose estimate and make it robust to bad vision
-   * data, we recommend only adding vision measurements that are already within
-   * one meter or so of the current pose estimate.
-   *
-   * @param visionRobotPose The pose of the robot as measured by the vision
-   *    camera.
-   * @param timestamp The timestamp of the vision measurement in seconds. Note
-   *     that if you don't use your own time source by calling UpdateWithTime()
-   *     then you must use a timestamp with an epoch since FPGA startup (i.e.,
-   *     the epoch of this timestamp is the same epoch as
-   *     frc::Timer::GetFPGATimestamp().) This means that you should use
-   *     frc::Timer::GetFPGATimestamp() as your time source or sync the epochs.
-   */
-  void AddVisionMeasurement(const Pose2d& visionRobotPose,
-                            units::second_t timestamp) {
-    // Step 1: Get the estimated pose from when the vision measurement was made.
-    auto sample = m_poseBuffer.Sample(timestamp);
-
-    if (!sample.has_value()) {
-      return;
-    }
-
-    // Step 2: Measure the twist between the odometry pose and the vision pose
-    auto twist = sample.value().pose.Log(visionRobotPose);
-
-    // Step 3: We should not trust the twist entirely, so instead we scale this
-    // twist by a Kalman gain matrix representing how much we trust vision
-    // measurements compared to our current pose.
-    frc::Vectord<3> k_times_twist =
-        m_visionK * frc::Vectord<3>{twist.dx.value(), twist.dy.value(),
-                                    twist.dtheta.value()};
-
-    // Step 4: Convert back to Twist2d
-    Twist2d scaledTwist{units::meter_t{k_times_twist(0)},
-                        units::meter_t{k_times_twist(1)},
-                        units::radian_t{k_times_twist(2)}};
-
-    // Step 5: Reset Odometry to state at sample with vision adjustment.
-    m_odometry.ResetPosition(sample.value().gyroAngle,
-                             sample.value().modulePostions,
-                             sample.value().pose.Exp(scaledTwist));
-
-    // Step 6: Replay odometry inputs between sample time and latest recorded
-    // sample to update the pose buffer and correct odometry.
-    auto internal_buf = m_poseBuffer.GetInternalBuffer();
-
-    auto upper_bound = std::lower_bound(
-        internal_buf.begin(), internal_buf.end(), timestamp,
-        [](const auto& pair, auto t) { return t > pair.first; });
-
-    for (auto entry = upper_bound; entry != internal_buf.end(); entry++) {
-      UpdateWithTime(entry->first, entry->second.gyroAngle,
-                     entry->second.modulePostions);
-    }
-  }
-
-  /**
-   * Adds a vision measurement to the Kalman Filter. This will correct the
-   * odometry pose estimate while still accounting for measurement noise.
-   *
-   * This method can be called as infrequently as you want, as long as you are
-   * calling Update() every loop.
-   *
-   * To promote stability of the pose estimate and make it robust to bad vision
-   * data, we recommend only adding vision measurements that are already within
-   * one meter or so of the current pose estimate.
-   *
-   * Note that the vision measurement standard deviations passed into this
-   * method will continue to apply to future measurements until a subsequent
-   * call to SetVisionMeasurementStdDevs() or this method.
-   *
-   * @param visionRobotPose The pose of the robot as measured by the vision
-   *     camera.
-   * @param timestamp The timestamp of the vision measurement in seconds. Note
-   *     that if you don't use your own time source by calling UpdateWithTime(),
-   *     then you must use a timestamp with an epoch since FPGA startup (i.e.,
-   *     the epoch of this timestamp is the same epoch as
-   *     frc::Timer::GetFPGATimestamp(). This means that you should use
-   *     frc::Timer::GetFPGATimestamp() as your time source in this case.
-   * @param visionMeasurementStdDevs Standard deviations of the vision pose
-   *     measurement (x position in meters, y position in meters, and heading in
-   *     radians). Increase these numbers to trust the vision pose measurement
-   *     less.
-   */
-  void AddVisionMeasurement(
-      const Pose2d& visionRobotPose, units::second_t timestamp,
-      const wpi::array<double, 3>& visionMeasurementStdDevs) {
-    SetVisionMeasurementStdDevs(visionMeasurementStdDevs);
-    AddVisionMeasurement(visionRobotPose, timestamp);
+    PoseEstimator<
+        SwerveDriveWheelSpeeds<NumModules>,
+        SwerveDriveWheelPositions<NumModules>>::ResetPosition(gyroAngle,
+                                                              {modulePositions},
+                                                              pose);
   }
 
   /**
@@ -258,8 +122,10 @@
   Pose2d Update(
       const Rotation2d& gyroAngle,
       const wpi::array<SwerveModulePosition, NumModules>& modulePositions) {
-    return UpdateWithTime(units::microsecond_t(wpi::Now()), gyroAngle,
-                          modulePositions);
+    return PoseEstimator<
+        SwerveDriveWheelSpeeds<NumModules>,
+        SwerveDriveWheelPositions<NumModules>>::Update(gyroAngle,
+                                                       {modulePositions});
   }
 
   /**
@@ -275,109 +141,13 @@
   Pose2d UpdateWithTime(
       units::second_t currentTime, const Rotation2d& gyroAngle,
       const wpi::array<SwerveModulePosition, NumModules>& modulePositions) {
-    m_odometry.Update(gyroAngle, modulePositions);
-
-    wpi::array<SwerveModulePosition, NumModules> internalModulePositions{
-        wpi::empty_array};
-
-    for (size_t i = 0; i < NumModules; i++) {
-      internalModulePositions[i].distance = modulePositions[i].distance;
-      internalModulePositions[i].angle = modulePositions[i].angle;
-    }
-
-    m_poseBuffer.AddSample(currentTime, {GetEstimatedPosition(), gyroAngle,
-                                         internalModulePositions});
-
-    return GetEstimatedPosition();
+    return PoseEstimator<SwerveDriveWheelSpeeds<NumModules>,
+                         SwerveDriveWheelPositions<NumModules>>::
+        UpdateWithTime(currentTime, gyroAngle, {modulePositions});
   }
 
  private:
-  struct InterpolationRecord {
-    // The pose observed given the current sensor inputs and the previous pose.
-    Pose2d pose;
-
-    // The current gyroscope angle.
-    Rotation2d gyroAngle;
-
-    // The distances traveled and rotations meaured at each module.
-    wpi::array<SwerveModulePosition, NumModules> modulePostions;
-
-    /**
-     * Checks equality between this InterpolationRecord and another object.
-     *
-     * @param other The other object.
-     * @return Whether the two objects are equal.
-     */
-    bool operator==(const InterpolationRecord& other) const = default;
-
-    /**
-     * Checks inequality between this InterpolationRecord and another object.
-     *
-     * @param other The other object.
-     * @return Whether the two objects are not equal.
-     */
-    bool operator!=(const InterpolationRecord& other) const = default;
-
-    /**
-     * Interpolates between two InterpolationRecords.
-     *
-     * @param endValue The end value for the interpolation.
-     * @param i The interpolant (fraction).
-     *
-     * @return The interpolated state.
-     */
-    InterpolationRecord Interpolate(
-        SwerveDriveKinematics<NumModules>& kinematics,
-        InterpolationRecord endValue, double i) const {
-      if (i < 0) {
-        return *this;
-      } else if (i > 1) {
-        return endValue;
-      } else {
-        // Find the new module distances.
-        wpi::array<SwerveModulePosition, NumModules> modulePositions{
-            wpi::empty_array};
-        // Find the distance between this measurement and the
-        // interpolated measurement.
-        wpi::array<SwerveModulePosition, NumModules> modulesDelta{
-            wpi::empty_array};
-
-        for (size_t i = 0; i < NumModules; i++) {
-          modulePositions[i].distance =
-              wpi::Lerp(this->modulePostions[i].distance,
-                        endValue.modulePostions[i].distance, i);
-          modulePositions[i].angle =
-              wpi::Lerp(this->modulePostions[i].angle,
-                        endValue.modulePostions[i].angle, i);
-
-          modulesDelta[i].distance =
-              modulePositions[i].distance - this->modulePostions[i].distance;
-          modulesDelta[i].angle = modulePositions[i].angle;
-        }
-
-        // Find the new gyro angle.
-        auto gyro = wpi::Lerp(this->gyroAngle, endValue.gyroAngle, i);
-
-        // Create a twist to represent this changed based on the interpolated
-        // sensor inputs.
-        auto twist = kinematics.ToTwist2d(modulesDelta);
-        twist.dtheta = (gyro - gyroAngle).Radians();
-
-        return {pose.Exp(twist), gyro, modulePositions};
-      }
-    }
-  };
-
-  SwerveDriveKinematics<NumModules>& m_kinematics;
-  SwerveDriveOdometry<NumModules> m_odometry;
-  wpi::array<double, 3> m_q{wpi::empty_array};
-  Eigen::Matrix3d m_visionK = Eigen::Matrix3d::Zero();
-
-  TimeInterpolatableBuffer<InterpolationRecord> m_poseBuffer{
-      1.5_s, [this](const InterpolationRecord& start,
-                    const InterpolationRecord& end, double t) {
-        return start.Interpolate(this->m_kinematics, end, t);
-      }};
+  SwerveDriveOdometry<NumModules> m_odometryImpl;
 };
 
 extern template class EXPORT_TEMPLATE_DECLARE(WPILIB_DLLEXPORT)
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.h
index 39ce615..9526f0c 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.h
@@ -58,6 +58,10 @@
   /**
    * Constructs an unscented Kalman filter.
    *
+   * See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-observers.html#process-and-measurement-noise-covariance-matrices
+   * for how to select the standard deviations.
+   *
    * @param f                  A vector-valued function of x and u that returns
    *                           the derivative of the state vector.
    * @param h                  A vector-valued function of x and u that returns
@@ -78,6 +82,10 @@
    * you have angles in the state or measurements, because they allow you to
    * correctly account for the modular nature of angle arithmetic.
    *
+   * See
+   * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-observers.html#process-and-measurement-noise-covariance-matrices
+   * for how to select the standard deviations.
+   *
    * @param f                  A vector-valued function of x and u that returns
    *                           the derivative of the state vector.
    * @param h                  A vector-valued function of x and u that returns
@@ -205,6 +213,21 @@
   /**
    * Correct the state estimate x-hat using the measurements in y.
    *
+   * This is useful for when the measurement noise covariances vary.
+   *
+   * @param u Same control input used in the predict step.
+   * @param y Measurement vector.
+   * @param R Continuous measurement noise covariance matrix.
+   */
+  void Correct(const InputVector& u, const OutputVector& y,
+               const Matrixd<Outputs, Outputs>& R) {
+    Correct<Outputs>(u, y, m_h, R, m_meanFuncY, m_residualFuncY,
+                     m_residualFuncX, m_addFuncX);
+  }
+
+  /**
+   * Correct the state estimate x-hat using the measurements in y.
+   *
    * This is useful for when the measurements available during a timestep's
    * Correct() call vary. The h(x, u) passed to the constructor is used if one
    * is not provided (the two-argument version of this function).
@@ -213,7 +236,7 @@
    * @param y Measurement vector.
    * @param h A vector-valued function of x and u that returns the measurement
    *          vector.
-   * @param R Measurement noise covariance matrix (continuous-time).
+   * @param R Continuous measurement noise covariance matrix.
    */
   template <int Rows>
   void Correct(
@@ -232,7 +255,7 @@
    * @param y             Measurement vector.
    * @param h             A vector-valued function of x and u that returns the
    *                      measurement vector.
-   * @param R             Measurement noise covariance matrix (continuous-time).
+   * @param R             Continuous measurement noise covariance matrix.
    * @param meanFuncY     A function that computes the mean of 2 * States + 1
    *                      measurement vectors using a given set of weights.
    * @param residualFuncY A function that computes the residual of two
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.inc
index a6744bf..c693197 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.inc
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.inc
@@ -4,7 +4,10 @@
 
 #pragma once
 
-#include "Eigen/Cholesky"
+#include <functional>
+
+#include <Eigen/Cholesky>
+
 #include "frc/StateSpaceUtil.h"
 #include "frc/estimator/UnscentedKalmanFilter.h"
 #include "frc/estimator/UnscentedTransform.h"
@@ -76,7 +79,7 @@
       NumericalJacobianX<States, States, Inputs>(m_f, m_xHat, u);
   StateMatrix discA;
   StateMatrix discQ;
-  DiscretizeAQTaylor<States>(contA, m_contQ, m_dt, &discA, &discQ);
+  DiscretizeAQ<States>(contA, m_contQ, m_dt, &discA, &discQ);
   Eigen::internal::llt_inplace<double, Eigen::Lower>::blocked(discQ);
 
   Matrixd<States, 2 * States + 1> sigmas =
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedTransform.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedTransform.h
index e28f094..bec3bc8 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedTransform.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/estimator/UnscentedTransform.h
@@ -4,9 +4,11 @@
 
 #pragma once
 
+#include <functional>
 #include <tuple>
 
-#include "Eigen/QR"
+#include <Eigen/QR>
+
 #include "frc/EigenCore.h"
 
 namespace frc {
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/filter/LinearFilter.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/filter/LinearFilter.h
index 0fb4f48..fc558df 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/filter/LinearFilter.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/filter/LinearFilter.h
@@ -11,10 +11,10 @@
 #include <stdexcept>
 #include <vector>
 
+#include <Eigen/QR>
 #include <wpi/array.h>
 #include <wpi/circular_buffer.h>
 
-#include "Eigen/QR"
 #include "frc/EigenCore.h"
 #include "units/time.h"
 #include "wpimath/MathShared.h"
@@ -95,7 +95,7 @@
     static int instances = 0;
     instances++;
     wpi::math::MathSharedStore::ReportUsage(
-        wpi::math::MathUsageId::kFilter_Linear, 1);
+        wpi::math::MathUsageId::kFilter_Linear, instances);
   }
 
   /**
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/filter/SlewRateLimiter.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/filter/SlewRateLimiter.h
index 542cd94..17d0d21 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/filter/SlewRateLimiter.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/filter/SlewRateLimiter.h
@@ -10,6 +10,7 @@
 #include <wpi/timestamp.h>
 
 #include "units/time.h"
+#include "wpimath/MathShared.h"
 
 namespace frc {
 /**
@@ -45,7 +46,8 @@
       : m_positiveRateLimit{positiveRateLimit},
         m_negativeRateLimit{negativeRateLimit},
         m_prevVal{initialValue},
-        m_prevTime{units::microsecond_t(wpi::Now())} {}
+        m_prevTime{
+            units::microsecond_t(wpi::math::MathSharedStore::GetTimestamp())} {}
 
   /**
    * Creates a new SlewRateLimiter with the given positive rate limit and
@@ -57,19 +59,6 @@
       : SlewRateLimiter(rateLimit, -rateLimit) {}
 
   /**
-   * Creates a new SlewRateLimiter with the given positive rate limit and
-   * negative rate limit of -rateLimit and initial value.
-   *
-   * @param rateLimit The rate-of-change limit.
-   * @param initialValue The initial value of the input.
-   */
-  WPI_DEPRECATED(
-      "Use SlewRateLimiter(Rate_t positiveRateLimit, Rate_t negativeRateLimit, "
-      "Unit_t initalValue) instead")
-  SlewRateLimiter(Rate_t rateLimit, Unit_t initialValue)
-      : SlewRateLimiter(rateLimit, -rateLimit, initialValue) {}
-
-  /**
    * Filters the input to limit its slew rate.
    *
    * @param input The input value whose slew rate is to be limited.
@@ -77,7 +66,7 @@
    * rate.
    */
   Unit_t Calculate(Unit_t input) {
-    units::second_t currentTime = units::microsecond_t(wpi::Now());
+    units::second_t currentTime = wpi::math::MathSharedStore::GetTimestamp();
     units::second_t elapsedTime = currentTime - m_prevTime;
     m_prevVal +=
         std::clamp(input - m_prevVal, m_negativeRateLimit * elapsedTime,
@@ -94,7 +83,7 @@
    */
   void Reset(Unit_t value) {
     m_prevVal = value;
-    m_prevTime = units::microsecond_t(wpi::Now());
+    m_prevTime = wpi::math::MathSharedStore::GetTimestamp();
   }
 
  private:
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/fmt/Eigen.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/fmt/Eigen.h
index c6b2ee6..2438b9f 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/fmt/Eigen.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/fmt/Eigen.h
@@ -4,120 +4,41 @@
 
 #pragma once
 
+#include <concepts>
+
+#include <Eigen/Core>
+#include <Eigen/SparseCore>
 #include <fmt/format.h>
 
-#include "Eigen/Core"
-#include "Eigen/SparseCore"
-
 /**
- * Formatter for Eigen::Matrix<double, Rows, Cols>.
- *
- * @tparam Rows Number of rows.
- * @tparam Cols Number of columns.
- * @tparam Args Defaulted template arguments to Eigen::Matrix<>.
+ * Formatter for classes derived from Eigen::MatrixBase<Derived> or
+ * Eigen::SparseCompressedBase<Derived>.
  */
-template <int Rows, int Cols, int... Args>
-struct fmt::formatter<Eigen::Matrix<double, Rows, Cols, Args...>> {
-  /**
-   * Storage for format specifier.
-   */
-  char presentation = 'f';
-
-  /**
-   * Format string parser.
-   *
-   * @param ctx Format string context.
-   */
+template <typename Derived, typename CharT>
+  requires std::derived_from<Derived, Eigen::MatrixBase<Derived>> ||
+           std::derived_from<Derived, Eigen::SparseCompressedBase<Derived>>
+struct fmt::formatter<Derived, CharT> {
   constexpr auto parse(fmt::format_parse_context& ctx) {
-    auto it = ctx.begin(), end = ctx.end();
-    if (it != end && (*it == 'f' || *it == 'e')) {
-      presentation = *it++;
-    }
-
-    if (it != end && *it != '}') {
-      throw fmt::format_error("invalid format");
-    }
-
-    return it;
+    return m_underlying.parse(ctx);
   }
 
-  /**
-   * Writes out a formatted matrix.
-   *
-   * @tparam FormatContext Format string context type.
-   * @param mat Matrix to format.
-   * @param ctx Format string context.
-   */
-  template <typename FormatContext>
-  auto format(const Eigen::Matrix<double, Rows, Cols, Args...>& mat,
-              FormatContext& ctx) {
+  auto format(const Derived& mat, fmt::format_context& ctx) const {
     auto out = ctx.out();
-    for (int i = 0; i < mat.rows(); ++i) {
-      for (int j = 0; j < mat.cols(); ++j) {
-        out = fmt::format_to(out, "  {:f}", mat(i, j));
+
+    for (int row = 0; row < mat.rows(); ++row) {
+      for (int col = 0; col < mat.cols(); ++col) {
+        out = fmt::format_to(out, "  ");
+        out = m_underlying.format(mat.coeff(row, col), ctx);
       }
 
-      if (i < mat.rows() - 1) {
+      if (row < mat.rows() - 1) {
         out = fmt::format_to(out, "\n");
       }
     }
 
     return out;
   }
-};
 
-/**
- * Formatter for Eigen::SparseMatrix<double>.
- *
- * @tparam Options Union of bit flags controlling the storage scheme.
- * @tparam StorageIndex The type of the indices.
- */
-template <int Options, typename StorageIndex>
-struct fmt::formatter<Eigen::SparseMatrix<double, Options, StorageIndex>> {
-  /**
-   * Storage for format specifier.
-   */
-  char presentation = 'f';
-
-  /**
-   * Format string parser.
-   *
-   * @param ctx Format string context.
-   */
-  constexpr auto parse(fmt::format_parse_context& ctx) {
-    auto it = ctx.begin(), end = ctx.end();
-    if (it != end && (*it == 'f' || *it == 'e')) {
-      presentation = *it++;
-    }
-
-    if (it != end && *it != '}') {
-      throw fmt::format_error("invalid format");
-    }
-
-    return it;
-  }
-
-  /**
-   * Writes out a formatted matrix.
-   *
-   * @tparam FormatContext Format string context type.
-   * @param mat Matrix to format.
-   * @param ctx Format string context.
-   */
-  template <typename FormatContext>
-  auto format(const Eigen::SparseMatrix<double, Options, StorageIndex>& mat,
-              FormatContext& ctx) {
-    auto out = ctx.out();
-    for (int i = 0; i < mat.rows(); ++i) {
-      for (int j = 0; j < mat.cols(); ++j) {
-        out = fmt::format_to(out, "  {:f}", mat.coeff(i, j));
-      }
-
-      if (i < mat.rows() - 1) {
-        out = fmt::format_to(out, "\n");
-      }
-    }
-
-    return out;
-  }
+ private:
+  fmt::formatter<typename Derived::Scalar, CharT> m_underlying;
 };
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/fmt/Units.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/fmt/Units.h
deleted file mode 100644
index 1ec61ca..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/fmt/Units.h
+++ /dev/null
@@ -1,218 +0,0 @@
-// 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 <fmt/format.h>
-
-#include "units/base.h"
-
-/**
- * Formatter for unit types.
- *
- * @tparam Units Unit tag for which type of units the `unit_t` represents (e.g.
- *               meters).
- * @tparam T Underlying type of the storage. Defaults to double.
- * @tparam NonLinearScale Optional scale class for the units. Defaults to linear
- *                        (i.e. does not scale the unit value). Examples of
- *                        non-linear scales could be logarithmic, decibel, or
- *                        richter scales. Non-linear scales must adhere to the
- *                        non-linear-scale concept.
- */
-template <class Units, typename T, template <typename> class NonLinearScale>
-struct fmt::formatter<units::unit_t<Units, T, NonLinearScale>>
-    : fmt::formatter<double> {
-  /**
-   * Writes out a formatted unit.
-   *
-   * @tparam FormatContext Format string context type.
-   * @param obj Unit instance.
-   * @param ctx Format string context.
-   */
-  template <typename FormatContext>
-  auto format(const units::unit_t<Units, T, NonLinearScale>& obj,
-              FormatContext& ctx) {
-    using BaseUnits =
-        units::unit<std::ratio<1>,
-                    typename units::traits::unit_traits<Units>::base_unit_type>;
-
-    auto out = ctx.out();
-
-    out = fmt::formatter<double>::format(
-        units::convert<Units, BaseUnits>(obj()), ctx);
-
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::meter_ratio::num != 0) {
-      out = fmt::format_to(out, " m");
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::meter_ratio::num != 0 &&
-                  units::traits::unit_traits<
-                      Units>::base_unit_type::meter_ratio::num != 1) {
-      out = fmt::format_to(
-          out, "^{}",
-          units::traits::unit_traits<Units>::base_unit_type::meter_ratio::num);
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::meter_ratio::den != 1) {
-      out = fmt::format_to(
-          out, "/{}",
-          units::traits::unit_traits<Units>::base_unit_type::meter_ratio::den);
-    }
-
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::kilogram_ratio::num != 0) {
-      out = fmt::format_to(out, " kg");
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::kilogram_ratio::num != 0 &&
-                  units::traits::unit_traits<
-                      Units>::base_unit_type::kilogram_ratio::num != 1) {
-      out = fmt::format_to(out, "^{}",
-                           units::traits::unit_traits<
-                               Units>::base_unit_type::kilogram_ratio::num);
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::kilogram_ratio::den != 1) {
-      out = fmt::format_to(out, "/{}",
-                           units::traits::unit_traits<
-                               Units>::base_unit_type::kilogram_ratio::den);
-    }
-
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::second_ratio::num != 0) {
-      out = fmt::format_to(out, " s");
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::second_ratio::num != 0 &&
-                  units::traits::unit_traits<
-                      Units>::base_unit_type::second_ratio::num != 1) {
-      out = fmt::format_to(
-          out, "^{}",
-          units::traits::unit_traits<Units>::base_unit_type::second_ratio::num);
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::second_ratio::den != 1) {
-      out = fmt::format_to(
-          out, "/{}",
-          units::traits::unit_traits<Units>::base_unit_type::second_ratio::den);
-    }
-
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::ampere_ratio::num != 0) {
-      out = fmt::format_to(out, " A");
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::ampere_ratio::num != 0 &&
-                  units::traits::unit_traits<
-                      Units>::base_unit_type::ampere_ratio::num != 1) {
-      out = fmt::format_to(
-          out, "^{}",
-          units::traits::unit_traits<Units>::base_unit_type::ampere_ratio::num);
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::ampere_ratio::den != 1) {
-      out = fmt::format_to(
-          out, "/{}",
-          units::traits::unit_traits<Units>::base_unit_type::ampere_ratio::den);
-    }
-
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::kelvin_ratio::num != 0) {
-      out = fmt::format_to(out, " K");
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::kelvin_ratio::num != 0 &&
-                  units::traits::unit_traits<
-                      Units>::base_unit_type::kelvin_ratio::num != 1) {
-      out = fmt::format_to(
-          out, "^{}",
-          units::traits::unit_traits<Units>::base_unit_type::kelvin_ratio::num);
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::kelvin_ratio::den != 1) {
-      out = fmt::format_to(
-          out, "/{}",
-          units::traits::unit_traits<Units>::base_unit_type::kelvin_ratio::den);
-    }
-
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::mole_ratio::num != 0) {
-      out = fmt::format_to(out, " mol");
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::mole_ratio::num != 0 &&
-                  units::traits::unit_traits<
-                      Units>::base_unit_type::mole_ratio::num != 1) {
-      out = fmt::format_to(
-          out, "^{}",
-          units::traits::unit_traits<Units>::base_unit_type::mole_ratio::num);
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::mole_ratio::den != 1) {
-      out = fmt::format_to(
-          out, "/{}",
-          units::traits::unit_traits<Units>::base_unit_type::mole_ratio::den);
-    }
-
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::candela_ratio::num != 0) {
-      out = fmt::format_to(out, " cd");
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::candela_ratio::num != 0 &&
-                  units::traits::unit_traits<
-                      Units>::base_unit_type::candela_ratio::num != 1) {
-      out = fmt::format_to(out, "^{}",
-                           units::traits::unit_traits<
-                               Units>::base_unit_type::candela_ratio::num);
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::candela_ratio::den != 1) {
-      out = fmt::format_to(out, "/{}",
-                           units::traits::unit_traits<
-                               Units>::base_unit_type::candela_ratio::den);
-    }
-
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::radian_ratio::num != 0) {
-      out = fmt::format_to(out, " rad");
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::radian_ratio::num != 0 &&
-                  units::traits::unit_traits<
-                      Units>::base_unit_type::radian_ratio::num != 1) {
-      out = fmt::format_to(
-          out, "^{}",
-          units::traits::unit_traits<Units>::base_unit_type::radian_ratio::num);
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::radian_ratio::den != 1) {
-      out = fmt::format_to(
-          out, "/{}",
-          units::traits::unit_traits<Units>::base_unit_type::radian_ratio::den);
-    }
-
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::byte_ratio::num != 0) {
-      out = fmt::format_to(out, " b");
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::byte_ratio::num != 0 &&
-                  units::traits::unit_traits<
-                      Units>::base_unit_type::byte_ratio::num != 1) {
-      out = fmt::format_to(
-          out, "^{}",
-          units::traits::unit_traits<Units>::base_unit_type::byte_ratio::num);
-    }
-    if constexpr (units::traits::unit_traits<
-                      Units>::base_unit_type::byte_ratio::den != 1) {
-      out = fmt::format_to(
-          out, "/{}",
-          units::traits::unit_traits<Units>::base_unit_type::byte_ratio::den);
-    }
-
-    return out;
-  }
-};
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/CoordinateAxis.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/CoordinateAxis.h
index 58b966a..2b471b7 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/CoordinateAxis.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/CoordinateAxis.h
@@ -4,9 +4,9 @@
 
 #pragma once
 
+#include <Eigen/Core>
 #include <wpi/SymbolExports.h>
 
-#include "frc/EigenCore.h"
 #include "frc/geometry/Pose3d.h"
 #include "frc/geometry/Rotation3d.h"
 
@@ -67,7 +67,7 @@
  private:
   friend class CoordinateSystem;
 
-  Vectord<3> m_axis;
+  Eigen::Vector3d m_axis;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/CoordinateSystem.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/CoordinateSystem.h
index 232455f..f5e71f2 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/CoordinateSystem.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/CoordinateSystem.h
@@ -6,7 +6,6 @@
 
 #include <wpi/SymbolExports.h>
 
-#include "frc/EigenCore.h"
 #include "frc/geometry/CoordinateAxis.h"
 #include "frc/geometry/Pose3d.h"
 #include "frc/geometry/Rotation3d.h"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose2d.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose2d.h
index d096e8c..970f792 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose2d.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose2d.h
@@ -4,15 +4,18 @@
 
 #pragma once
 
+#include <initializer_list>
+#include <span>
+
 #include <wpi/SymbolExports.h>
+#include <wpi/json_fwd.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
-#include "Transform2d.h"
-#include "Translation2d.h"
-#include "Twist2d.h"
-
-namespace wpi {
-class json;
-}  // namespace wpi
+#include "frc/geometry/Rotation2d.h"
+#include "frc/geometry/Transform2d.h"
+#include "frc/geometry/Translation2d.h"
+#include "frc/geometry/Twist2d.h"
 
 namespace frc {
 
@@ -120,6 +123,15 @@
   constexpr Pose2d operator/(double scalar) const;
 
   /**
+   * Rotates the pose around the origin and returns the new pose.
+   *
+   * @param other The rotation to transform the pose by.
+   *
+   * @return The rotated pose.
+   */
+  constexpr Pose2d RotateBy(const Rotation2d& other) const;
+
+  /**
    * Transforms the pose by the given transformation and returns the new pose.
    * See + operator for the matrix multiplication performed.
    *
@@ -176,6 +188,20 @@
    */
   Twist2d Log(const Pose2d& end) const;
 
+  /**
+   * Returns the nearest Pose2d from a collection of poses
+   * @param poses The collection of poses.
+   * @return The nearest Pose2d from the collection.
+   */
+  Pose2d Nearest(std::span<const Pose2d> poses) const;
+
+  /**
+   * Returns the nearest Pose2d from a collection of poses
+   * @param poses The collection of poses.
+   * @return The nearest Pose2d from the collection.
+   */
+  Pose2d Nearest(std::initializer_list<Pose2d> poses) const;
+
  private:
   Translation2d m_translation;
   Rotation2d m_rotation;
@@ -189,4 +215,38 @@
 
 }  // namespace frc
 
-#include "Pose2d.inc"
+template <>
+struct wpi::Struct<frc::Pose2d> {
+  static constexpr std::string_view kTypeString = "struct:Pose2d";
+  static constexpr size_t kSize = wpi::Struct<frc::Translation2d>::kSize +
+                                  wpi::Struct<frc::Rotation2d>::kSize;
+  static constexpr std::string_view kSchema =
+      "Translation2d translation;Rotation2d rotation";
+  static frc::Pose2d Unpack(std::span<const uint8_t, kSize> data) {
+    return {wpi::UnpackStruct<frc::Translation2d, 0>(data),
+            wpi::UnpackStruct<frc::Rotation2d, kRotationOff>(data)};
+  }
+  static void Pack(std::span<uint8_t, kSize> data, const frc::Pose2d& value) {
+    wpi::PackStruct<0>(data, value.Translation());
+    wpi::PackStruct<kRotationOff>(data, value.Rotation());
+  }
+  static void ForEachNested(
+      std::invocable<std::string_view, std::string_view> auto fn) {
+    wpi::ForEachStructSchema<frc::Translation2d>(fn);
+    wpi::ForEachStructSchema<frc::Rotation2d>(fn);
+  }
+
+ private:
+  static constexpr size_t kRotationOff = wpi::Struct<frc::Translation2d>::kSize;
+};
+
+static_assert(wpi::HasNestedStruct<frc::Pose2d>);
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Pose2d> {
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Pose2d Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg, const frc::Pose2d& value);
+};
+
+#include "frc/geometry/Pose2d.inc"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose2d.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose2d.inc
index c549f26..2764d16 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose2d.inc
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose2d.inc
@@ -31,6 +31,10 @@
   return *this * (1.0 / scalar);
 }
 
+constexpr Pose2d Pose2d::RotateBy(const Rotation2d& other) const {
+  return {m_translation.RotateBy(other), m_rotation.RotateBy(other)};
+}
+
 constexpr Pose2d Pose2d::TransformBy(const Transform2d& other) const {
   return {m_translation + (other.Translation().RotateBy(m_rotation)),
           other.Rotation() + m_rotation};
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose3d.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose3d.h
index 8c7ace3..b5a0e03 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose3d.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Pose3d.h
@@ -5,15 +5,15 @@
 #pragma once
 
 #include <wpi/SymbolExports.h>
+#include <wpi/json_fwd.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
-#include "Pose2d.h"
-#include "Transform3d.h"
-#include "Translation3d.h"
-#include "Twist3d.h"
-
-namespace wpi {
-class json;
-}  // namespace wpi
+#include "frc/geometry/Pose2d.h"
+#include "frc/geometry/Rotation3d.h"
+#include "frc/geometry/Transform3d.h"
+#include "frc/geometry/Translation3d.h"
+#include "frc/geometry/Twist3d.h"
 
 namespace frc {
 
@@ -56,7 +56,9 @@
 
   /**
    * Transforms the pose by the given transformation and returns the new
-   * transformed pose.
+   * transformed pose. The transform is applied relative to the pose's frame.
+   * Note that this differs from Pose3d::RotateBy(const Rotation3d&), which is
+   * applied relative to the global frame and around the origin.
    *
    * @param other The transform to transform the pose by.
    *
@@ -131,8 +133,20 @@
   Pose3d operator/(double scalar) const;
 
   /**
-   * Transforms the pose by the given transformation and returns the new pose.
-   * See + operator for the matrix multiplication performed.
+   * Rotates the pose around the origin and returns the new pose.
+   *
+   * @param other The rotation to transform the pose by, which is applied
+   * extrinsically (from the global frame).
+   *
+   * @return The rotated pose.
+   */
+  Pose3d RotateBy(const Rotation3d& other) const;
+
+  /**
+   * Transforms the pose by the given transformation and returns the new
+   * transformed pose. The transform is applied relative to the pose's frame.
+   * Note that this differs from Pose3d::RotateBy(const Rotation3d&), which is
+   * applied relative to the global frame and around the origin.
    *
    * @param other The transform to transform the pose by.
    *
@@ -202,3 +216,37 @@
 void from_json(const wpi::json& json, Pose3d& pose);
 
 }  // namespace frc
+
+template <>
+struct wpi::Struct<frc::Pose3d> {
+  static constexpr std::string_view kTypeString = "struct:Pose3d";
+  static constexpr size_t kSize = wpi::Struct<frc::Translation3d>::kSize +
+                                  wpi::Struct<frc::Rotation3d>::kSize;
+  static constexpr std::string_view kSchema =
+      "Translation3d translation;Rotation3d rotation";
+  static frc::Pose3d Unpack(std::span<const uint8_t, kSize> data) {
+    return {wpi::UnpackStruct<frc::Translation3d, 0>(data),
+            wpi::UnpackStruct<frc::Rotation3d, kRotationOff>(data)};
+  }
+  static void Pack(std::span<uint8_t, kSize> data, const frc::Pose3d& value) {
+    wpi::PackStruct<0>(data, value.Translation());
+    wpi::PackStruct<kRotationOff>(data, value.Rotation());
+  }
+  static void ForEachNested(
+      std::invocable<std::string_view, std::string_view> auto fn) {
+    wpi::ForEachStructSchema<frc::Translation3d>(fn);
+    wpi::ForEachStructSchema<frc::Rotation3d>(fn);
+  }
+
+ private:
+  static constexpr size_t kRotationOff = wpi::Struct<frc::Translation3d>::kSize;
+};
+
+static_assert(wpi::HasNestedStruct<frc::Pose3d>);
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Pose3d> {
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Pose3d Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg, const frc::Pose3d& value);
+};
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Quaternion.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Quaternion.h
index b5a318b..63a3af2 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Quaternion.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Quaternion.h
@@ -4,13 +4,11 @@
 
 #pragma once
 
+#include <Eigen/Core>
 #include <wpi/SymbolExports.h>
-
-#include "frc/EigenCore.h"
-
-namespace wpi {
-class json;
-}  // namespace wpi
+#include <wpi/json_fwd.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
 namespace frc {
 
@@ -32,6 +30,34 @@
   Quaternion(double w, double x, double y, double z);
 
   /**
+   * Adds with another quaternion.
+   *
+   * @param other the other quaternion
+   */
+  Quaternion operator+(const Quaternion& other) const;
+
+  /**
+   * Subtracts another quaternion.
+   *
+   * @param other the other quaternion
+   */
+  Quaternion operator-(const Quaternion& other) const;
+
+  /**
+   * Multiples with a scalar value.
+   *
+   * @param other the scalar value
+   */
+  Quaternion operator*(const double other) const;
+
+  /**
+   * Divides by a scalar value.
+   *
+   * @param other the scalar value
+   */
+  Quaternion operator/(const double other) const;
+
+  /**
    * Multiply with another quaternion.
    *
    * @param other The other quaternion.
@@ -47,6 +73,16 @@
   bool operator==(const Quaternion& other) const;
 
   /**
+   * Returns the elementwise product of two quaternions.
+   */
+  double Dot(const Quaternion& other) const;
+
+  /**
+   * Returns the conjugate of the quaternion.
+   */
+  Quaternion Conjugate() const;
+
+  /**
    * Returns the inverse of the quaternion.
    */
   Quaternion Inverse() const;
@@ -57,6 +93,52 @@
   Quaternion Normalize() const;
 
   /**
+   * Calculates the L2 norm of the quaternion.
+   */
+  double Norm() const;
+
+  /**
+   * Calculates this quaternion raised to a power.
+   *
+   * @param t the power to raise this quaternion to.
+   */
+  Quaternion Pow(const double t) const;
+
+  /**
+   * Matrix exponential of a quaternion.
+   *
+   * @param other the "Twist" that will be applied to this quaternion.
+   */
+  Quaternion Exp(const Quaternion& other) const;
+
+  /**
+   * Matrix exponential of a quaternion.
+   *
+   * source: wpimath/algorithms.md
+   *
+   *  If this quaternion is in 𝖘𝖔(3) and you are looking for an element of
+   * SO(3), use FromRotationVector
+   */
+  Quaternion Exp() const;
+
+  /**
+   * Log operator of a quaternion.
+   *
+   * @param other The quaternion to map this quaternion onto
+   */
+  Quaternion Log(const Quaternion& other) const;
+
+  /**
+   * Log operator of a quaternion.
+   *
+   * source:  wpimath/algorithms.md
+   *
+   * If this quaternion is in SO(3) and you are looking for an element of 𝖘𝖔(3),
+   * use ToRotationVector
+   */
+  Quaternion Log() const;
+
+  /**
    * Returns W component of the quaternion.
    */
   double W() const;
@@ -83,8 +165,20 @@
    */
   Eigen::Vector3d ToRotationVector() const;
 
+  /**
+   * Returns the quaternion representation of this rotation vector.
+   *
+   * This is also the exp operator of 𝖘𝖔(3).
+   *
+   * source: wpimath/algorithms.md
+   */
+  static Quaternion FromRotationVector(const Eigen::Vector3d& rvec);
+
  private:
+  // Scalar r in versor form
   double m_r = 1.0;
+
+  // Vector v in versor form
   Eigen::Vector3d m_v{0.0, 0.0, 0.0};
 };
 
@@ -95,3 +189,32 @@
 void from_json(const wpi::json& json, Quaternion& quaternion);
 
 }  // namespace frc
+
+template <>
+struct wpi::Struct<frc::Quaternion> {
+  static constexpr std::string_view kTypeString = "struct:Quaternion";
+  static constexpr size_t kSize = 32;
+  static constexpr std::string_view kSchema =
+      "double w;double x;double y;double z";
+  static frc::Quaternion Unpack(std::span<const uint8_t, 32> data) {
+    return {wpi::UnpackStruct<double, 0>(data),
+            wpi::UnpackStruct<double, 8>(data),
+            wpi::UnpackStruct<double, 16>(data),
+            wpi::UnpackStruct<double, 24>(data)};
+  }
+  static void Pack(std::span<uint8_t, 32> data, const frc::Quaternion& value) {
+    wpi::PackStruct<0>(data, value.W());
+    wpi::PackStruct<8>(data, value.X());
+    wpi::PackStruct<16>(data, value.Y());
+    wpi::PackStruct<24>(data, value.Z());
+  }
+};
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Quaternion> {
+  static constexpr std::string_view kTypeString = "proto:Quaternion";
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Quaternion Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg,
+                   const frc::Quaternion& value);
+};
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation2d.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation2d.h
index 406ef3c..96041a4 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation2d.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation2d.h
@@ -5,13 +5,12 @@
 #pragma once
 
 #include <wpi/SymbolExports.h>
+#include <wpi/json_fwd.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
 #include "units/angle.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace frc {
 
 /**
@@ -179,4 +178,25 @@
 
 }  // namespace frc
 
-#include "Rotation2d.inc"
+template <>
+struct wpi::Struct<frc::Rotation2d> {
+  static constexpr std::string_view kTypeString = "struct:Rotation2d";
+  static constexpr size_t kSize = 8;
+  static constexpr std::string_view kSchema = "double value";
+  static frc::Rotation2d Unpack(std::span<const uint8_t, 8> data) {
+    return units::radian_t{wpi::UnpackStruct<double>(data)};
+  }
+  static void Pack(std::span<uint8_t, 8> data, const frc::Rotation2d& value) {
+    wpi::PackStruct(data, value.Radians().value());
+  }
+};
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Rotation2d> {
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Rotation2d Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg,
+                   const frc::Rotation2d& value);
+};
+
+#include "frc/geometry/Rotation2d.inc"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc
index eb31ebd..dbe9e35 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc
@@ -23,7 +23,8 @@
     : Rotation2d(units::radian_t{value}) {}
 
 constexpr Rotation2d::Rotation2d(double x, double y) {
-  const auto magnitude = gcem::hypot(x, y);
+  double magnitude =
+      std::is_constant_evaluated() ? gcem::hypot(x, y) : std::hypot(x, y);
   if (magnitude > 1e-6) {
     m_sin = y / magnitude;
     m_cos = x / magnitude;
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation3d.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation3d.h
index 7c1a60d..6d04715 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation3d.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Rotation3d.h
@@ -4,17 +4,16 @@
 
 #pragma once
 
+#include <Eigen/Core>
 #include <wpi/SymbolExports.h>
+#include <wpi/json_fwd.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
-#include "Quaternion.h"
-#include "Rotation2d.h"
-#include "frc/EigenCore.h"
+#include "frc/geometry/Quaternion.h"
+#include "frc/geometry/Rotation2d.h"
 #include "units/angle.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace frc {
 
 /**
@@ -57,7 +56,16 @@
    * @param axis The rotation axis.
    * @param angle The rotation around the axis.
    */
-  Rotation3d(const Vectord<3>& axis, units::radian_t angle);
+  Rotation3d(const Eigen::Vector3d& axis, units::radian_t angle);
+
+  /**
+   * Constructs a Rotation3d with the given rotation vector representation. This
+   * representation is equivalent to axis-angle, where the normalized axis is
+   * multiplied by the rotation around the axis in radians.
+   *
+   * @param rvec The rotation vector.
+   */
+  explicit Rotation3d(const Eigen::Vector3d& rvec);
 
   /**
    * Constructs a Rotation3d from a rotation matrix.
@@ -65,7 +73,7 @@
    * @param rotationMatrix The rotation matrix.
    * @throws std::domain_error if the rotation matrix isn't special orthogonal.
    */
-  explicit Rotation3d(const Matrixd<3, 3>& rotationMatrix);
+  explicit Rotation3d(const Eigen::Matrix3d& rotationMatrix);
 
   /**
    * Constructs a Rotation3d that rotates the initial vector onto the final
@@ -77,7 +85,7 @@
    * @param initial The initial vector.
    * @param final The final vector.
    */
-  Rotation3d(const Vectord<3>& initial, const Vectord<3>& final);
+  Rotation3d(const Eigen::Vector3d& initial, const Eigen::Vector3d& final);
 
   /**
    * Adds two rotations together.
@@ -126,12 +134,16 @@
   /**
    * Checks equality between this Rotation3d and another object.
    */
-  bool operator==(const Rotation3d&) const = default;
+  bool operator==(const Rotation3d&) const;
 
   /**
-   * Adds the new rotation to the current rotation.
+   * Adds the new rotation to the current rotation. The other rotation is
+   * applied extrinsically, which means that it rotates around the global axes.
+   * For example, Rotation3d{90_deg, 0, 0}.RotateBy(Rotation3d{0, 45_deg, 0})
+   * rotates by 90 degrees around the +X axis and then by 45 degrees around the
+   * global +Y axis. (This is equivalent to Rotation3d{90_deg, 45_deg, 0})
    *
-   * @param other The rotation to rotate by.
+   * @param other The extrinsic rotation to rotate by.
    *
    * @return The new rotated Rotation3d.
    */
@@ -160,7 +172,7 @@
   /**
    * Returns the axis in the axis-angle representation of this rotation.
    */
-  Vectord<3> Axis() const;
+  Eigen::Vector3d Axis() const;
 
   /**
    * Returns the angle in the axis-angle representation of this rotation.
@@ -184,3 +196,31 @@
 void from_json(const wpi::json& json, Rotation3d& rotation);
 
 }  // namespace frc
+
+template <>
+struct wpi::Struct<frc::Rotation3d> {
+  static constexpr std::string_view kTypeString = "struct:Rotation3d";
+  static constexpr size_t kSize = wpi::Struct<frc::Quaternion>::kSize;
+  static constexpr std::string_view kSchema = "Quaternion q";
+  static frc::Rotation3d Unpack(std::span<const uint8_t, kSize> data) {
+    return frc::Rotation3d{wpi::UnpackStruct<frc::Quaternion, 0>(data)};
+  }
+  static void Pack(std::span<uint8_t, kSize> data,
+                   const frc::Rotation3d& value) {
+    wpi::PackStruct<0>(data, value.GetQuaternion());
+  }
+  static void ForEachNested(
+      std::invocable<std::string_view, std::string_view> auto fn) {
+    wpi::ForEachStructSchema<frc::Quaternion>(fn);
+  }
+};
+
+static_assert(wpi::HasNestedStruct<frc::Rotation3d>);
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Rotation3d> {
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Rotation3d Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg,
+                   const frc::Rotation3d& value);
+};
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform2d.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform2d.h
index 3c21ec1..593dce0 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform2d.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform2d.h
@@ -5,15 +5,17 @@
 #pragma once
 
 #include <wpi/SymbolExports.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
-#include "Translation2d.h"
+#include "frc/geometry/Translation2d.h"
 
 namespace frc {
 
 class WPILIB_DLLEXPORT Pose2d;
 
 /**
- * Represents a transformation for a Pose2d.
+ * Represents a transformation for a Pose2d in the pose's frame.
  */
 class WPILIB_DLLEXPORT Transform2d {
  public:
@@ -34,6 +36,17 @@
   constexpr Transform2d(Translation2d translation, Rotation2d rotation);
 
   /**
+   * Constructs a transform with x and y translations instead of a separate
+   * Translation2d.
+   *
+   * @param x The x component of the translational component of the transform.
+   * @param y The y component of the translational component of the transform.
+   * @param rotation The rotational component of the transform.
+   */
+  constexpr Transform2d(units::meter_t x, units::meter_t y,
+                        Rotation2d rotation);
+
+  /**
    * Constructs the identity transform -- maps an initial pose to itself.
    */
   constexpr Transform2d() = default;
@@ -94,7 +107,8 @@
   }
 
   /**
-   * Composes two transformations.
+   * Composes two transformations. The second transform is applied relative to
+   * the orientation of the first.
    *
    * @param other The transform to compose with this one.
    * @return The composition of the two transformations.
@@ -112,4 +126,40 @@
 };
 }  // namespace frc
 
-#include "Transform2d.inc"
+template <>
+struct wpi::Struct<frc::Transform2d> {
+  static constexpr std::string_view kTypeString = "struct:Transform2d";
+  static constexpr size_t kSize = wpi::Struct<frc::Translation2d>::kSize +
+                                  wpi::Struct<frc::Rotation2d>::kSize;
+  static constexpr std::string_view kSchema =
+      "Translation2d translation;Rotation2d rotation";
+  static frc::Transform2d Unpack(std::span<const uint8_t, kSize> data) {
+    return {wpi::UnpackStruct<frc::Translation2d, 0>(data),
+            wpi::UnpackStruct<frc::Rotation2d, kRotationOff>(data)};
+  }
+  static void Pack(std::span<uint8_t, kSize> data,
+                   const frc::Transform2d& value) {
+    wpi::PackStruct<0>(data, value.Translation());
+    wpi::PackStruct<kRotationOff>(data, value.Rotation());
+  }
+  static void ForEachNested(
+      std::invocable<std::string_view, std::string_view> auto fn) {
+    wpi::ForEachStructSchema<frc::Translation2d>(fn);
+    wpi::ForEachStructSchema<frc::Rotation2d>(fn);
+  }
+
+ private:
+  static constexpr size_t kRotationOff = wpi::Struct<frc::Translation2d>::kSize;
+};
+
+static_assert(wpi::HasNestedStruct<frc::Transform2d>);
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Transform2d> {
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Transform2d Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg,
+                   const frc::Transform2d& value);
+};
+
+#include "frc/geometry/Transform2d.inc"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform2d.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform2d.inc
index f851a05..862b8d5 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform2d.inc
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform2d.inc
@@ -16,6 +16,10 @@
                                    Rotation2d rotation)
     : m_translation(std::move(translation)), m_rotation(std::move(rotation)) {}
 
+constexpr Transform2d::Transform2d(units::meter_t x, units::meter_t y,
+                                   Rotation2d rotation)
+    : m_translation(x, y), m_rotation(std::move(rotation)) {}
+
 constexpr Transform2d Transform2d::Inverse() const {
   // We are rotating the difference between the translations
   // using a clockwise rotation matrix. This transforms the global
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform3d.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform3d.h
index 5f50ec2..d5af97d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform3d.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Transform3d.h
@@ -5,15 +5,17 @@
 #pragma once
 
 #include <wpi/SymbolExports.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
-#include "Translation3d.h"
+#include "frc/geometry/Translation3d.h"
 
 namespace frc {
 
 class WPILIB_DLLEXPORT Pose3d;
 
 /**
- * Represents a transformation for a Pose3d.
+ * Represents a transformation for a Pose3d in the pose's frame.
  */
 class WPILIB_DLLEXPORT Transform3d {
  public:
@@ -34,6 +36,18 @@
   Transform3d(Translation3d translation, Rotation3d rotation);
 
   /**
+   * Constructs a transform with x, y, and z translations instead of a separate
+   * Translation3d.
+   *
+   * @param x The x component of the translational component of the transform.
+   * @param y The y component of the translational component of the transform.
+   * @param z The z component of the translational component of the transform.
+   * @param rotation The rotational component of the transform.
+   */
+  Transform3d(units::meter_t x, units::meter_t y, units::meter_t z,
+              Rotation3d rotation);
+
+  /**
    * Constructs the identity transform -- maps an initial pose to itself.
    */
   constexpr Transform3d() = default;
@@ -99,7 +113,8 @@
   Transform3d operator/(double scalar) const { return *this * (1.0 / scalar); }
 
   /**
-   * Composes two transformations.
+   * Composes two transformations. The second transform is applied relative to
+   * the orientation of the first.
    *
    * @param other The transform to compose with this one.
    * @return The composition of the two transformations.
@@ -116,3 +131,39 @@
   Rotation3d m_rotation;
 };
 }  // namespace frc
+
+template <>
+struct wpi::Struct<frc::Transform3d> {
+  static constexpr std::string_view kTypeString = "struct:Transform3d";
+  static constexpr size_t kSize = wpi::Struct<frc::Translation3d>::kSize +
+                                  wpi::Struct<frc::Rotation3d>::kSize;
+  static constexpr std::string_view kSchema =
+      "Translation3d translation;Rotation3d rotation";
+  static frc::Transform3d Unpack(std::span<const uint8_t, kSize> data) {
+    return {wpi::UnpackStruct<frc::Translation3d, 0>(data),
+            wpi::UnpackStruct<frc::Rotation3d, kRotationOff>(data)};
+  }
+  static void Pack(std::span<uint8_t, kSize> data,
+                   const frc::Transform3d& value) {
+    wpi::PackStruct<0>(data, value.Translation());
+    wpi::PackStruct<kRotationOff>(data, value.Rotation());
+  }
+  static void ForEachNested(
+      std::invocable<std::string_view, std::string_view> auto fn) {
+    wpi::ForEachStructSchema<frc::Translation3d>(fn);
+    wpi::ForEachStructSchema<frc::Rotation3d>(fn);
+  }
+
+ private:
+  static constexpr size_t kRotationOff = wpi::Struct<frc::Translation3d>::kSize;
+};
+
+static_assert(wpi::HasNestedStruct<frc::Transform3d>);
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Transform3d> {
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Transform3d Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg,
+                   const frc::Transform3d& value);
+};
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Translation2d.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Translation2d.h
index e168510..1568586 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Translation2d.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Translation2d.h
@@ -4,15 +4,17 @@
 
 #pragma once
 
+#include <initializer_list>
+#include <span>
+
 #include <wpi/SymbolExports.h>
+#include <wpi/json_fwd.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
-#include "Rotation2d.h"
+#include "frc/geometry/Rotation2d.h"
 #include "units/length.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace frc {
 
 /**
@@ -170,6 +172,21 @@
    */
   bool operator==(const Translation2d& other) const;
 
+  /**
+   * Returns the nearest Translation2d from a collection of translations
+   * @param translations The collection of translations.
+   * @return The nearest Translation2d from the collection.
+   */
+  Translation2d Nearest(std::span<const Translation2d> translations) const;
+
+  /**
+   * Returns the nearest Translation2d from a collection of translations
+   * @param translations The collection of translations.
+   * @return The nearest Translation2d from the collection.
+   */
+  Translation2d Nearest(
+      std::initializer_list<Translation2d> translations) const;
+
  private:
   units::meter_t m_x = 0_m;
   units::meter_t m_y = 0_m;
@@ -183,4 +200,28 @@
 
 }  // namespace frc
 
-#include "Translation2d.inc"
+template <>
+struct wpi::Struct<frc::Translation2d> {
+  static constexpr std::string_view kTypeString = "struct:Translation2d";
+  static constexpr size_t kSize = 16;
+  static constexpr std::string_view kSchema = "double x;double y";
+  static frc::Translation2d Unpack(std::span<const uint8_t, 16> data) {
+    return {units::meter_t{wpi::UnpackStruct<double, 0>(data)},
+            units::meter_t{wpi::UnpackStruct<double, 8>(data)}};
+  }
+  static void Pack(std::span<uint8_t, 16> data,
+                   const frc::Translation2d& value) {
+    wpi::PackStruct<0>(data, value.X().value());
+    wpi::PackStruct<8>(data, value.Y().value());
+  }
+};
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Translation2d> {
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Translation2d Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg,
+                   const frc::Translation2d& value);
+};
+
+#include "frc/geometry/Translation2d.inc"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Translation3d.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Translation3d.h
index ab641fa..47ba1f6 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Translation3d.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Translation3d.h
@@ -5,15 +5,14 @@
 #pragma once
 
 #include <wpi/SymbolExports.h>
+#include <wpi/json_fwd.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
-#include "Rotation3d.h"
-#include "Translation2d.h"
+#include "frc/geometry/Rotation3d.h"
+#include "frc/geometry/Translation2d.h"
 #include "units/length.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace frc {
 
 /**
@@ -186,4 +185,30 @@
 
 }  // namespace frc
 
-#include "Translation3d.inc"
+template <>
+struct wpi::Struct<frc::Translation3d> {
+  static constexpr std::string_view kTypeString = "struct:Translation3d";
+  static constexpr size_t kSize = 24;
+  static constexpr std::string_view kSchema = "double x;double y;double z";
+  static frc::Translation3d Unpack(std::span<const uint8_t, 24> data) {
+    return {units::meter_t{wpi::UnpackStruct<double, 0>(data)},
+            units::meter_t{wpi::UnpackStruct<double, 8>(data)},
+            units::meter_t{wpi::UnpackStruct<double, 16>(data)}};
+  }
+  static void Pack(std::span<uint8_t, 24> data,
+                   const frc::Translation3d& value) {
+    wpi::PackStruct<0>(data, value.X().value());
+    wpi::PackStruct<8>(data, value.Y().value());
+    wpi::PackStruct<16>(data, value.Z().value());
+  }
+};
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Translation3d> {
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Translation3d Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg,
+                   const frc::Translation3d& value);
+};
+
+#include "frc/geometry/Translation3d.inc"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Twist2d.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Twist2d.h
index 620b688..257f1b6 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Twist2d.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Twist2d.h
@@ -5,6 +5,8 @@
 #pragma once
 
 #include <wpi/SymbolExports.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
 #include "units/angle.h"
 #include "units/length.h"
@@ -14,7 +16,7 @@
 /**
  * A change in distance along a 2D arc since the last pose update. We can use
  * ideas from differential calculus to create new Pose2ds from a Twist2d and
- * vise versa.
+ * vice versa.
  *
  * A Twist can be used to represent a difference between two poses.
  */
@@ -57,3 +59,28 @@
   }
 };
 }  // namespace frc
+
+template <>
+struct wpi::Struct<frc::Twist2d> {
+  static constexpr std::string_view kTypeString = "struct:Twist2d";
+  static constexpr size_t kSize = 24;
+  static constexpr std::string_view kSchema =
+      "double dx;double dy;double dtheta";
+  static frc::Twist2d Unpack(std::span<const uint8_t, 24> data) {
+    return {units::meter_t{wpi::UnpackStruct<double, 0>(data)},
+            units::meter_t{wpi::UnpackStruct<double, 8>(data)},
+            units::radian_t{wpi::UnpackStruct<double, 16>(data)}};
+  }
+  static void Pack(std::span<uint8_t, 24> data, const frc::Twist2d& value) {
+    wpi::PackStruct<0>(data, value.dx.value());
+    wpi::PackStruct<8>(data, value.dy.value());
+    wpi::PackStruct<16>(data, value.dtheta.value());
+  }
+};
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Twist2d> {
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Twist2d Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg, const frc::Twist2d& value);
+};
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Twist3d.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Twist3d.h
index 3040ab3..4d902df 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Twist3d.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/geometry/Twist3d.h
@@ -5,6 +5,8 @@
 #pragma once
 
 #include <wpi/SymbolExports.h>
+#include <wpi/protobuf/Protobuf.h>
+#include <wpi/struct/Struct.h>
 
 #include "frc/geometry/Rotation3d.h"
 #include "units/angle.h"
@@ -15,7 +17,7 @@
 /**
  * A change in distance along a 3D arc since the last pose update. We can use
  * ideas from differential calculus to create new Pose3ds from a Twist3d and
- * vise versa.
+ * vice versa.
  *
  * A Twist can be used to represent a difference between two poses.
  */
@@ -77,3 +79,35 @@
   }
 };
 }  // namespace frc
+
+template <>
+struct wpi::Struct<frc::Twist3d> {
+  static constexpr std::string_view kTypeString = "struct:Twist3d";
+  static constexpr size_t kSize = 48;
+  static constexpr std::string_view kSchema =
+      "double dx;double dy;double dz;double rx;double ry;double rz";
+  static frc::Twist3d Unpack(std::span<const uint8_t, 48> data) {
+    return {units::meter_t{wpi::UnpackStruct<double, 0>(data)},
+            units::meter_t{wpi::UnpackStruct<double, 8>(data)},
+            units::meter_t{wpi::UnpackStruct<double, 16>(data)},
+            units::radian_t{wpi::UnpackStruct<double, 24>(data)},
+            units::radian_t{wpi::UnpackStruct<double, 32>(data)},
+            units::radian_t{wpi::UnpackStruct<double, 40>(data)}};
+  }
+  static void Pack(std::span<uint8_t, 48> data, const frc::Twist3d& value) {
+    wpi::PackStruct<0>(data, value.dx.value());
+    wpi::PackStruct<8>(data, value.dy.value());
+    wpi::PackStruct<16>(data, value.dz.value());
+    wpi::PackStruct<24>(data, value.rx.value());
+    wpi::PackStruct<32>(data, value.ry.value());
+    wpi::PackStruct<40>(data, value.rz.value());
+  }
+};
+
+template <>
+struct WPILIB_DLLEXPORT wpi::Protobuf<frc::Twist3d> {
+  static constexpr std::string_view kTypeString = "proto:Twist3d";
+  static google::protobuf::Message* New(google::protobuf::Arena* arena);
+  static frc::Twist3d Unpack(const google::protobuf::Message& msg);
+  static void Pack(google::protobuf::Message* msg, const frc::Twist3d& value);
+};
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/interpolation/TimeInterpolatableBuffer.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/interpolation/TimeInterpolatableBuffer.h
index 771fe84..9dd8e62 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/interpolation/TimeInterpolatableBuffer.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/interpolation/TimeInterpolatableBuffer.h
@@ -68,11 +68,26 @@
     if (m_pastSnapshots.size() == 0 || time > m_pastSnapshots.back().first) {
       m_pastSnapshots.emplace_back(time, sample);
     } else {
-      m_pastSnapshots.insert(
-          std::upper_bound(
-              m_pastSnapshots.begin(), m_pastSnapshots.end(), time,
-              [](auto t, const auto& pair) { return t < pair.first; }),
-          std::pair(time, sample));
+      auto first_after = std::upper_bound(
+          m_pastSnapshots.begin(), m_pastSnapshots.end(), time,
+          [](auto t, const auto& pair) { return t < pair.first; });
+
+      // Don't access this before ensuring first_after isn't first.
+      auto last_not_greater_than = first_after - 1;
+
+      if (first_after == m_pastSnapshots.begin() ||
+          last_not_greater_than == m_pastSnapshots.begin() ||
+          last_not_greater_than->first < time) {
+        // Two cases handled together:
+        // 1. All entries come after the sample
+        // 2. Some entries come before the sample, but none are recorded with
+        // the same time
+        m_pastSnapshots.insert(first_after, std::pair(time, sample));
+      } else {
+        // Final case:
+        // 3. An entry exists with the same recorded time.
+        last_not_greater_than->second = sample;
+      }
     }
     while (time - m_pastSnapshots[0].first > m_historySize) {
       m_pastSnapshots.erase(m_pastSnapshots.begin());
@@ -112,6 +127,10 @@
         m_pastSnapshots.begin(), m_pastSnapshots.end(), time,
         [](const auto& pair, auto t) { return t > pair.first; });
 
+    if (upper_bound == m_pastSnapshots.begin()) {
+      return upper_bound->second;
+    }
+
     auto lower_bound = upper_bound - 1;
 
     double t = ((time - lower_bound->first) /
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/ChassisSpeeds.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/ChassisSpeeds.h
index 37fe768..93c9044 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/ChassisSpeeds.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/ChassisSpeeds.h
@@ -6,6 +6,7 @@
 
 #include <wpi/SymbolExports.h>
 
+#include "frc/geometry/Pose2d.h"
 #include "frc/geometry/Rotation2d.h"
 #include "units/angular_velocity.h"
 #include "units/velocity.h"
@@ -15,8 +16,7 @@
  * Represents the speed of a robot chassis. Although this struct contains
  * similar members compared to a Twist2d, they do NOT represent the same thing.
  * Whereas a Twist2d represents a change in pose w.r.t to the robot frame of
- * reference, this ChassisSpeeds struct represents a velocity w.r.t to the robot
- * frame of reference.
+ * reference, a ChassisSpeeds struct represents a robot's velocity.
  *
  * A strictly non-holonomic drivetrain, such as a differential drive, should
  * never have a dy component because it can never move sideways. Holonomic
@@ -24,12 +24,12 @@
  */
 struct WPILIB_DLLEXPORT ChassisSpeeds {
   /**
-   * Represents forward velocity w.r.t the robot frame of reference. (Fwd is +)
+   * Velocity along the x-axis. (Fwd is +)
    */
   units::meters_per_second_t vx = 0_mps;
 
   /**
-   * Represents strafe velocity w.r.t the robot frame of reference. (Left is +)
+   * Velocity along the y-axis. (Left is +)
    */
   units::meters_per_second_t vy = 0_mps;
 
@@ -39,6 +39,57 @@
   units::radians_per_second_t omega = 0_rad_per_s;
 
   /**
+   * Disretizes a continuous-time chassis speed.
+   *
+   * This function converts a continuous-time chassis speed into a discrete-time
+   * one such that when the discrete-time chassis speed is applied for one
+   * timestep, the robot moves as if the velocity components are independent
+   * (i.e., the robot moves v_x * dt along the x-axis, v_y * dt along the
+   * y-axis, and omega * dt around the z-axis).
+   *
+   * This is useful for compensating for translational skew when translating and
+   * rotating a swerve drivetrain.
+   *
+   * @param vx Forward velocity.
+   * @param vy Sideways velocity.
+   * @param omega Angular velocity.
+   * @param dt The duration of the timestep the speeds should be applied for.
+   *
+   * @return Discretized ChassisSpeeds.
+   */
+  static ChassisSpeeds Discretize(units::meters_per_second_t vx,
+                                  units::meters_per_second_t vy,
+                                  units::radians_per_second_t omega,
+                                  units::second_t dt) {
+    Pose2d desiredDeltaPose{vx * dt, vy * dt, omega * dt};
+    auto twist = Pose2d{}.Log(desiredDeltaPose);
+    return {twist.dx / dt, twist.dy / dt, twist.dtheta / dt};
+  }
+
+  /**
+   * Disretizes a continuous-time chassis speed.
+   *
+   * This function converts a continuous-time chassis speed into a discrete-time
+   * one such that when the discrete-time chassis speed is applied for one
+   * timestep, the robot moves as if the velocity components are independent
+   * (i.e., the robot moves v_x * dt along the x-axis, v_y * dt along the
+   * y-axis, and omega * dt around the z-axis).
+   *
+   * This is useful for compensating for translational skew when translating and
+   * rotating a swerve drivetrain.
+   *
+   * @param continuousSpeeds The continuous speeds.
+   * @param dt The duration of the timestep the speeds should be applied for.
+   *
+   * @return Discretized ChassisSpeeds.
+   */
+  static ChassisSpeeds Discretize(const ChassisSpeeds& continuousSpeeds,
+                                  units::second_t dt) {
+    return Discretize(continuousSpeeds.vx, continuousSpeeds.vy,
+                      continuousSpeeds.omega, dt);
+  }
+
+  /**
    * Converts a user provided field-relative set of speeds into a robot-relative
    * ChassisSpeeds object.
    *
@@ -57,8 +108,12 @@
   static ChassisSpeeds FromFieldRelativeSpeeds(
       units::meters_per_second_t vx, units::meters_per_second_t vy,
       units::radians_per_second_t omega, const Rotation2d& robotAngle) {
-    return {vx * robotAngle.Cos() + vy * robotAngle.Sin(),
-            -vx * robotAngle.Sin() + vy * robotAngle.Cos(), omega};
+    // CW rotation into chassis frame
+    auto rotated =
+        Translation2d{units::meter_t{vx.value()}, units::meter_t{vy.value()}}
+            .RotateBy(-robotAngle);
+    return {units::meters_per_second_t{rotated.X().value()},
+            units::meters_per_second_t{rotated.Y().value()}, omega};
   }
 
   /**
@@ -81,5 +136,118 @@
                                    fieldRelativeSpeeds.vy,
                                    fieldRelativeSpeeds.omega, robotAngle);
   }
+
+  /**
+   * Converts a user provided robot-relative set of speeds into a field-relative
+   * ChassisSpeeds object.
+   *
+   * @param vx The component of speed in the x direction relative to the robot.
+   * Positive x is towards the robot's front.
+   * @param vy The component of speed in the y direction relative to the robot.
+   * Positive y is towards the robot's left.
+   * @param omega The angular rate of the robot.
+   * @param robotAngle The angle of the robot as measured by a gyroscope. The
+   * robot's angle is considered to be zero when it is facing directly away from
+   * your alliance station wall. Remember that this should be CCW positive.
+   *
+   * @return ChassisSpeeds object representing the speeds in the field's frame
+   * of reference.
+   */
+  static ChassisSpeeds FromRobotRelativeSpeeds(
+      units::meters_per_second_t vx, units::meters_per_second_t vy,
+      units::radians_per_second_t omega, const Rotation2d& robotAngle) {
+    // CCW rotation out of chassis frame
+    auto rotated =
+        Translation2d{units::meter_t{vx.value()}, units::meter_t{vy.value()}}
+            .RotateBy(robotAngle);
+    return {units::meters_per_second_t{rotated.X().value()},
+            units::meters_per_second_t{rotated.Y().value()}, omega};
+  }
+
+  /**
+   * Converts a user provided robot-relative ChassisSpeeds object into a
+   * field-relative ChassisSpeeds object.
+   *
+   * @param robotRelativeSpeeds The ChassisSpeeds object representing the speeds
+   *    in the robot frame of reference. Positive x is the towards robot's
+   * front. Positive y is towards the robot's left.
+   * @param robotAngle The angle of the robot as measured by a gyroscope. The
+   *    robot's angle is considered to be zero when it is facing directly away
+   *    from your alliance station wall. Remember that this should be CCW
+   *    positive.
+   * @return ChassisSpeeds object representing the speeds in the field's frame
+   *    of reference.
+   */
+  static ChassisSpeeds FromRobotRelativeSpeeds(
+      const ChassisSpeeds& robotRelativeSpeeds, const Rotation2d& robotAngle) {
+    return FromRobotRelativeSpeeds(robotRelativeSpeeds.vx,
+                                   robotRelativeSpeeds.vy,
+                                   robotRelativeSpeeds.omega, robotAngle);
+  }
+
+  /**
+   * Adds two ChassisSpeeds and returns the sum.
+   *
+   * <p>For example, ChassisSpeeds{1.0, 0.5, 1.5} + ChassisSpeeds{2.0, 1.5, 0.5}
+   * = ChassisSpeeds{3.0, 2.0, 2.0}
+   *
+   * @param other The ChassisSpeeds to add.
+   *
+   * @return The sum of the ChassisSpeeds.
+   */
+  constexpr ChassisSpeeds operator+(const ChassisSpeeds& other) const {
+    return {vx + other.vx, vy + other.vy, omega + other.omega};
+  }
+
+  /**
+   * Subtracts the other ChassisSpeeds from the current ChassisSpeeds and
+   * returns the difference.
+   *
+   * <p>For example, ChassisSpeeds{5.0, 4.0, 2.0} - ChassisSpeeds{1.0, 2.0, 1.0}
+   * = ChassisSpeeds{4.0, 2.0, 1.0}
+   *
+   * @param other The ChassisSpeeds to subtract.
+   *
+   * @return The difference between the two ChassisSpeeds.
+   */
+  constexpr ChassisSpeeds operator-(const ChassisSpeeds& other) const {
+    return *this + -other;
+  }
+
+  /**
+   * Returns the inverse of the current ChassisSpeeds.
+   * This is equivalent to negating all components of the ChassisSpeeds.
+   *
+   * @return The inverse of the current ChassisSpeeds.
+   */
+  constexpr ChassisSpeeds operator-() const { return {-vx, -vy, -omega}; }
+
+  /**
+   * Multiplies the ChassisSpeeds by a scalar and returns the new ChassisSpeeds.
+   *
+   * <p>For example, ChassisSpeeds{2.0, 2.5, 1.0} * 2
+   * = ChassisSpeeds{4.0, 5.0, 1.0}
+   *
+   * @param scalar The scalar to multiply by.
+   *
+   * @return The scaled ChassisSpeeds.
+   */
+  constexpr ChassisSpeeds operator*(double scalar) const {
+    return {scalar * vx, scalar * vy, scalar * omega};
+  }
+
+  /**
+   * Divides the ChassisSpeeds by a scalar and returns the new ChassisSpeeds.
+   *
+   * <p>For example, ChassisSpeeds{2.0, 2.5, 1.0} / 2
+   * = ChassisSpeeds{1.0, 1.25, 0.5}
+   *
+   * @param scalar The scalar to divide by.
+   *
+   * @return The scaled ChassisSpeeds.
+   */
+  constexpr ChassisSpeeds operator/(double scalar) const {
+    return operator*(1.0 / scalar);
+  }
 };
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveKinematics.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveKinematics.h
index 930c7a6..95f53fa 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveKinematics.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveKinematics.h
@@ -8,7 +8,9 @@
 
 #include "frc/geometry/Twist2d.h"
 #include "frc/kinematics/ChassisSpeeds.h"
+#include "frc/kinematics/DifferentialDriveWheelPositions.h"
 #include "frc/kinematics/DifferentialDriveWheelSpeeds.h"
+#include "frc/kinematics/Kinematics.h"
 #include "units/angle.h"
 #include "units/length.h"
 #include "wpimath/MathShared.h"
@@ -22,7 +24,9 @@
  * velocity components whereas forward kinematics converts left and right
  * component velocities into a linear and angular chassis speed.
  */
-class WPILIB_DLLEXPORT DifferentialDriveKinematics {
+class WPILIB_DLLEXPORT DifferentialDriveKinematics
+    : public Kinematics<DifferentialDriveWheelSpeeds,
+                        DifferentialDriveWheelPositions> {
  public:
   /**
    * Constructs a differential drive kinematics object.
@@ -46,7 +50,7 @@
    * @return The chassis speed.
    */
   constexpr ChassisSpeeds ToChassisSpeeds(
-      const DifferentialDriveWheelSpeeds& wheelSpeeds) const {
+      const DifferentialDriveWheelSpeeds& wheelSpeeds) const override {
     return {(wheelSpeeds.left + wheelSpeeds.right) / 2.0, 0_mps,
             (wheelSpeeds.right - wheelSpeeds.left) / trackWidth * 1_rad};
   }
@@ -60,7 +64,7 @@
    * @return The left and right velocities.
    */
   constexpr DifferentialDriveWheelSpeeds ToWheelSpeeds(
-      const ChassisSpeeds& chassisSpeeds) const {
+      const ChassisSpeeds& chassisSpeeds) const override {
     return {chassisSpeeds.vx - trackWidth / 2 * chassisSpeeds.omega / 1_rad,
             chassisSpeeds.vx + trackWidth / 2 * chassisSpeeds.omega / 1_rad};
   }
@@ -79,6 +83,11 @@
             (rightDistance - leftDistance) / trackWidth * 1_rad};
   }
 
+  Twist2d ToTwist2d(const DifferentialDriveWheelPositions& start,
+                    const DifferentialDriveWheelPositions& end) const override {
+    return ToTwist2d(end.left - start.left, end.right - start.right);
+  }
+
   units::meter_t trackWidth;
 };
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveOdometry.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveOdometry.h
index cc198ac..279c1dc 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveOdometry.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveOdometry.h
@@ -7,6 +7,10 @@
 #include <wpi/SymbolExports.h>
 
 #include "frc/geometry/Pose2d.h"
+#include "frc/kinematics/DifferentialDriveKinematics.h"
+#include "frc/kinematics/DifferentialDriveWheelPositions.h"
+#include "frc/kinematics/DifferentialDriveWheelSpeeds.h"
+#include "frc/kinematics/Odometry.h"
 #include "units/length.h"
 
 namespace frc {
@@ -22,7 +26,9 @@
  * It is important that you reset your encoders to zero before using this class.
  * Any subsequent pose resets also require the encoders to be reset to zero.
  */
-class WPILIB_DLLEXPORT DifferentialDriveOdometry {
+class WPILIB_DLLEXPORT DifferentialDriveOdometry
+    : public Odometry<DifferentialDriveWheelSpeeds,
+                      DifferentialDriveWheelPositions> {
  public:
   /**
    * Constructs a DifferentialDriveOdometry object.
@@ -56,21 +62,14 @@
    */
   void ResetPosition(const Rotation2d& gyroAngle, units::meter_t leftDistance,
                      units::meter_t rightDistance, const Pose2d& pose) {
-    m_pose = pose;
-    m_previousAngle = pose.Rotation();
-    m_gyroOffset = m_pose.Rotation() - gyroAngle;
-
-    m_prevLeftDistance = leftDistance;
-    m_prevRightDistance = rightDistance;
+    Odometry<DifferentialDriveWheelSpeeds,
+             DifferentialDriveWheelPositions>::ResetPosition(gyroAngle,
+                                                             {leftDistance,
+                                                              rightDistance},
+                                                             pose);
   }
 
   /**
-   * Returns the position of the robot on the field.
-   * @return The pose of the robot.
-   */
-  const Pose2d& GetPose() const { return m_pose; }
-
-  /**
    * Updates the robot position on the field using distance measurements from
    * encoders. This method is more numerically accurate than using velocities to
    * integrate the pose and is also advantageous for teams that are using lower
@@ -82,15 +81,14 @@
    * @return The new pose of the robot.
    */
   const Pose2d& Update(const Rotation2d& gyroAngle, units::meter_t leftDistance,
-                       units::meter_t rightDistance);
+                       units::meter_t rightDistance) {
+    return Odometry<DifferentialDriveWheelSpeeds,
+                    DifferentialDriveWheelPositions>::Update(gyroAngle,
+                                                             {leftDistance,
+                                                              rightDistance});
+  }
 
  private:
-  Pose2d m_pose;
-
-  Rotation2d m_gyroOffset;
-  Rotation2d m_previousAngle;
-
-  units::meter_t m_prevLeftDistance = 0_m;
-  units::meter_t m_prevRightDistance = 0_m;
+  DifferentialDriveKinematics m_kinematicsImpl{units::meter_t{1}};
 };
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveWheelPositions.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveWheelPositions.h
new file mode 100644
index 0000000..4dddbea
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveWheelPositions.h
@@ -0,0 +1,51 @@
+// 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 <wpi/MathExtras.h>
+#include <wpi/SymbolExports.h>
+
+#include "units/length.h"
+
+namespace frc {
+/**
+ * Represents the wheel positions for a differential drive drivetrain.
+ */
+struct WPILIB_DLLEXPORT DifferentialDriveWheelPositions {
+  /**
+   * Distance driven by the left side.
+   */
+  units::meter_t left = 0_m;
+
+  /**
+   * Distance driven by the right side.
+   */
+  units::meter_t right = 0_m;
+
+  /**
+   * Checks equality between this DifferentialDriveWheelPositions and another
+   * object.
+   *
+   * @param other The other object.
+   * @return Whether the two objects are equal.
+   */
+  bool operator==(const DifferentialDriveWheelPositions& other) const = default;
+
+  /**
+   * Checks inequality between this DifferentialDriveWheelPositions and another
+   * object.
+   *
+   * @param other The other object.
+   * @return Whether the two objects are not equal.
+   */
+  bool operator!=(const DifferentialDriveWheelPositions& other) const = default;
+
+  DifferentialDriveWheelPositions Interpolate(
+      const DifferentialDriveWheelPositions& endValue, double t) const {
+    return {wpi::Lerp(left, endValue.left, t),
+            wpi::Lerp(right, endValue.right, t)};
+  }
+};
+}  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveWheelSpeeds.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveWheelSpeeds.h
index fce2b96..9d9e705 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveWheelSpeeds.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveWheelSpeeds.h
@@ -36,5 +36,79 @@
    * @param attainableMaxSpeed The absolute max speed that a wheel can reach.
    */
   void Desaturate(units::meters_per_second_t attainableMaxSpeed);
+
+  /**
+   * Adds two DifferentialDriveWheelSpeeds and returns the sum.
+   *
+   * <p>For example, DifferentialDriveWheelSpeeds{1.0, 0.5} +
+   * DifferentialDriveWheelSpeeds{2.0, 1.5} =
+   * DifferentialDriveWheelSpeeds{3.0, 2.0}
+   *
+   * @param other The DifferentialDriveWheelSpeeds to add.
+   *
+   * @return The sum of the DifferentialDriveWheelSpeeds.
+   */
+  constexpr DifferentialDriveWheelSpeeds operator+(
+      const DifferentialDriveWheelSpeeds& other) const {
+    return {left + other.left, right + other.right};
+  }
+
+  /**
+   * Subtracts the other DifferentialDriveWheelSpeeds from the current
+   * DifferentialDriveWheelSpeeds and returns the difference.
+   *
+   * <p>For example, DifferentialDriveWheelSpeeds{5.0, 4.0} -
+   * DifferentialDriveWheelSpeeds{1.0, 2.0} =
+   * DifferentialDriveWheelSpeeds{4.0, 2.0}
+   *
+   * @param other The DifferentialDriveWheelSpeeds to subtract.
+   *
+   * @return The difference between the two DifferentialDriveWheelSpeeds.
+   */
+  constexpr DifferentialDriveWheelSpeeds operator-(
+      const DifferentialDriveWheelSpeeds& other) const {
+    return *this + -other;
+  }
+
+  /**
+   * Returns the inverse of the current DifferentialDriveWheelSpeeds.
+   * This is equivalent to negating all components of the
+   * DifferentialDriveWheelSpeeds.
+   *
+   * @return The inverse of the current DifferentialDriveWheelSpeeds.
+   */
+  constexpr DifferentialDriveWheelSpeeds operator-() const {
+    return {-left, -right};
+  }
+
+  /**
+   * Multiplies the DifferentialDriveWheelSpeeds by a scalar and returns the new
+   * DifferentialDriveWheelSpeeds.
+   *
+   * <p>For example, DifferentialDriveWheelSpeeds{2.0, 2.5} * 2
+   * = DifferentialDriveWheelSpeeds{4.0, 5.0}
+   *
+   * @param scalar The scalar to multiply by.
+   *
+   * @return The scaled DifferentialDriveWheelSpeeds.
+   */
+  constexpr DifferentialDriveWheelSpeeds operator*(double scalar) const {
+    return {scalar * left, scalar * right};
+  }
+
+  /**
+   * Divides the DifferentialDriveWheelSpeeds by a scalar and returns the new
+   * DifferentialDriveWheelSpeeds.
+   *
+   * <p>For example, DifferentialDriveWheelSpeeds{2.0, 2.5} / 2
+   * = DifferentialDriveWheelSpeeds{1.0, 1.25}
+   *
+   * @param scalar The scalar to divide by.
+   *
+   * @return The scaled DifferentialDriveWheelSpeeds.
+   */
+  constexpr DifferentialDriveWheelSpeeds operator/(double scalar) const {
+    return operator*(1.0 / scalar);
+  }
 };
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/Kinematics.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/Kinematics.h
new file mode 100644
index 0000000..e27cfc6
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/Kinematics.h
@@ -0,0 +1,62 @@
+// 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 <wpi/SymbolExports.h>
+
+#include "frc/geometry/Twist2d.h"
+#include "frc/kinematics/ChassisSpeeds.h"
+
+namespace frc {
+/**
+ * Helper class that converts a chassis velocity (dx, dy, and dtheta components)
+ * into individual wheel speeds. Robot code should not use this directly-
+ * Instead, use the particular type for your drivetrain (e.g.,
+ * DifferentialDriveKinematics).
+ *
+ * Inverse kinematics converts a desired chassis speed into wheel speeds whereas
+ * forward kinematics converts wheel speeds into chassis speed.
+ */
+template <typename WheelSpeeds, typename WheelPositions>
+class WPILIB_DLLEXPORT Kinematics {
+ public:
+  /**
+   * Performs forward kinematics to return the resulting chassis speed from the
+   * wheel speeds. This method is often used for odometry -- determining the
+   * robot's position on the field using data from the real-world speed of each
+   * wheel on the robot.
+   *
+   * @param wheelSpeeds The speeds of the wheels.
+   * @return The chassis speed.
+   */
+  virtual ChassisSpeeds ToChassisSpeeds(
+      const WheelSpeeds& wheelSpeeds) const = 0;
+
+  /**
+   * Performs inverse kinematics to return the wheel speeds from a desired
+   * chassis velocity. This method is often used to convert joystick values into
+   * wheel speeds.
+   *
+   * @param chassisSpeeds The desired chassis speed.
+   * @return The wheel speeds.
+   */
+  virtual WheelSpeeds ToWheelSpeeds(
+      const ChassisSpeeds& chassisSpeeds) const = 0;
+
+  /**
+   * Performs forward kinematics to return the resulting Twist2d from the given
+   * change in wheel positions. This method is often used for odometry --
+   * determining the robot's position on the field using changes in the distance
+   * driven by each wheel on the robot.
+   *
+   * @param start The starting distances driven by the wheels.
+   * @param end The ending distances driven by the wheels.
+   *
+   * @return The resulting Twist2d in the robot's movement.
+   */
+  virtual Twist2d ToTwist2d(const WheelPositions& start,
+                            const WheelPositions& end) const = 0;
+};
+}  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveKinematics.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveKinematics.h
index 2880cef..fe6eb51 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveKinematics.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveKinematics.h
@@ -4,13 +4,14 @@
 
 #pragma once
 
+#include <Eigen/QR>
 #include <wpi/SymbolExports.h>
 
-#include "Eigen/QR"
 #include "frc/EigenCore.h"
 #include "frc/geometry/Translation2d.h"
 #include "frc/geometry/Twist2d.h"
 #include "frc/kinematics/ChassisSpeeds.h"
+#include "frc/kinematics/Kinematics.h"
 #include "frc/kinematics/MecanumDriveWheelPositions.h"
 #include "frc/kinematics/MecanumDriveWheelSpeeds.h"
 #include "wpimath/MathShared.h"
@@ -39,7 +40,8 @@
  * Forward kinematics is also used for odometry -- determining the position of
  * the robot on the field using encoders and a gyro.
  */
-class WPILIB_DLLEXPORT MecanumDriveKinematics {
+class WPILIB_DLLEXPORT MecanumDriveKinematics
+    : public Kinematics<MecanumDriveWheelSpeeds, MecanumDriveWheelPositions> {
  public:
   /**
    * Constructs a mecanum drive kinematics object.
@@ -101,7 +103,12 @@
    */
   MecanumDriveWheelSpeeds ToWheelSpeeds(
       const ChassisSpeeds& chassisSpeeds,
-      const Translation2d& centerOfRotation = Translation2d{}) const;
+      const Translation2d& centerOfRotation) const;
+
+  MecanumDriveWheelSpeeds ToWheelSpeeds(
+      const ChassisSpeeds& chassisSpeeds) const override {
+    return ToWheelSpeeds(chassisSpeeds, {});
+  }
 
   /**
    * Performs forward kinematics to return the resulting chassis state from the
@@ -114,7 +121,10 @@
    * @return The resulting chassis speed.
    */
   ChassisSpeeds ToChassisSpeeds(
-      const MecanumDriveWheelSpeeds& wheelSpeeds) const;
+      const MecanumDriveWheelSpeeds& wheelSpeeds) const override;
+
+  Twist2d ToTwist2d(const MecanumDriveWheelPositions& start,
+                    const MecanumDriveWheelPositions& end) const override;
 
   /**
    * Performs forward kinematics to return the resulting Twist2d from the
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveOdometry.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveOdometry.h
index 5e949ca..0a6f537 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveOdometry.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveOdometry.h
@@ -9,7 +9,9 @@
 
 #include "frc/geometry/Pose2d.h"
 #include "frc/kinematics/MecanumDriveKinematics.h"
+#include "frc/kinematics/MecanumDriveWheelPositions.h"
 #include "frc/kinematics/MecanumDriveWheelSpeeds.h"
+#include "frc/kinematics/Odometry.h"
 #include "units/time.h"
 
 namespace frc {
@@ -23,7 +25,8 @@
  * path following. Furthermore, odometry can be used for latency compensation
  * when using computer-vision systems.
  */
-class WPILIB_DLLEXPORT MecanumDriveOdometry {
+class WPILIB_DLLEXPORT MecanumDriveOdometry
+    : public Odometry<MecanumDriveWheelSpeeds, MecanumDriveWheelPositions> {
  public:
   /**
    * Constructs a MecanumDriveOdometry object.
@@ -38,52 +41,8 @@
       const MecanumDriveWheelPositions& wheelPositions,
       const Pose2d& initialPose = Pose2d{});
 
-  /**
-   * Resets the robot's position on the field.
-   *
-   * The gyroscope angle does not need to be reset here on the user's robot
-   * code. The library automatically takes care of offsetting the gyro angle.
-   *
-   * @param gyroAngle The angle reported by the gyroscope.
-   * @param wheelPositions The current distances measured by each wheel.
-   * @param pose The position on the field that your robot is at.
-   */
-  void ResetPosition(const Rotation2d& gyroAngle,
-                     const MecanumDriveWheelPositions& wheelPositions,
-                     const Pose2d& pose) {
-    m_pose = pose;
-    m_previousAngle = pose.Rotation();
-    m_gyroOffset = m_pose.Rotation() - gyroAngle;
-    m_previousWheelPositions = wheelPositions;
-  }
-
-  /**
-   * Returns the position of the robot on the field.
-   * @return The pose of the robot.
-   */
-  const Pose2d& GetPose() const { return m_pose; }
-
-  /**
-   * Updates the robot's position on the field using forward kinematics and
-   * integration of the pose over time. This method takes in an angle parameter
-   * which is used instead of the angular rate that is calculated from forward
-   * kinematics, in addition to the current distance measurement at each wheel.
-   *
-   * @param gyroAngle The angle reported by the gyroscope.
-   * @param wheelPositions The current distances measured by each wheel.
-   *
-   * @return The new pose of the robot.
-   */
-  const Pose2d& Update(const Rotation2d& gyroAngle,
-                       const MecanumDriveWheelPositions& wheelPositions);
-
  private:
-  MecanumDriveKinematics m_kinematics;
-  Pose2d m_pose;
-
-  MecanumDriveWheelPositions m_previousWheelPositions;
-  Rotation2d m_previousAngle;
-  Rotation2d m_gyroOffset;
+  MecanumDriveKinematics m_kinematicsImpl;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveWheelPositions.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveWheelPositions.h
index b69aceb..76c8b9d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveWheelPositions.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveWheelPositions.h
@@ -4,13 +4,14 @@
 
 #pragma once
 
+#include <wpi/MathExtras.h>
 #include <wpi/SymbolExports.h>
 
 #include "units/length.h"
 
 namespace frc {
 /**
- * Represents the wheel speeds for a mecanum drive drivetrain.
+ * Represents the wheel positions for a mecanum drive drivetrain.
  */
 struct WPILIB_DLLEXPORT MecanumDriveWheelPositions {
   /**
@@ -49,5 +50,13 @@
    * @return Whether the two objects are not equal.
    */
   bool operator!=(const MecanumDriveWheelPositions& other) const = default;
+
+  MecanumDriveWheelPositions Interpolate(
+      const MecanumDriveWheelPositions& endValue, double t) const {
+    return {wpi::Lerp(frontLeft, endValue.frontLeft, t),
+            wpi::Lerp(frontRight, endValue.frontRight, t),
+            wpi::Lerp(rearLeft, endValue.rearLeft, t),
+            wpi::Lerp(rearRight, endValue.rearRight, t)};
+  }
 };
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveWheelSpeeds.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveWheelSpeeds.h
index e698c5f..80e8460 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveWheelSpeeds.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/MecanumDriveWheelSpeeds.h
@@ -46,5 +46,77 @@
    * @param attainableMaxSpeed The absolute max speed that a wheel can reach.
    */
   void Desaturate(units::meters_per_second_t attainableMaxSpeed);
+
+  /**
+   * Adds two MecanumDriveWheelSpeeds and returns the sum.
+   *
+   * <p>For example, MecanumDriveWheelSpeeds{1.0, 0.5, 2.0, 1.5} +
+   * MecanumDriveWheelSpeeds{2.0, 1.5, 0.5, 1.0} =
+   * MecanumDriveWheelSpeeds{3.0, 2.0, 2.5, 2.5}
+   *
+   * @param other The MecanumDriveWheelSpeeds to add.
+   * @return The sum of the MecanumDriveWheelSpeeds.
+   */
+  constexpr MecanumDriveWheelSpeeds operator+(
+      const MecanumDriveWheelSpeeds& other) const {
+    return {frontLeft + other.frontLeft, frontRight + other.frontRight,
+            rearLeft + other.rearLeft, rearRight + other.rearRight};
+  }
+
+  /**
+   * Subtracts the other MecanumDriveWheelSpeeds from the current
+   * MecanumDriveWheelSpeeds and returns the difference.
+   *
+   * <p>For example, MecanumDriveWheelSpeeds{5.0, 4.0, 6.0, 2.5} -
+   * MecanumDriveWheelSpeeds{1.0, 2.0, 3.0, 0.5} =
+   * MecanumDriveWheelSpeeds{4.0, 2.0, 3.0, 2.0}
+   *
+   * @param other The MecanumDriveWheelSpeeds to subtract.
+   * @return The difference between the two MecanumDriveWheelSpeeds.
+   */
+  constexpr MecanumDriveWheelSpeeds operator-(
+      const MecanumDriveWheelSpeeds& other) const {
+    return *this + -other;
+  }
+
+  /**
+   * Returns the inverse of the current MecanumDriveWheelSpeeds.
+   * This is equivalent to negating all components of the
+   * MecanumDriveWheelSpeeds.
+   *
+   * @return The inverse of the current MecanumDriveWheelSpeeds.
+   */
+  constexpr MecanumDriveWheelSpeeds operator-() const {
+    return {-frontLeft, -frontRight, -rearLeft, -rearRight};
+  }
+
+  /**
+   * Multiplies the MecanumDriveWheelSpeeds by a scalar and returns the new
+   * MecanumDriveWheelSpeeds.
+   *
+   * <p>For example, MecanumDriveWheelSpeeds{2.0, 2.5, 3.0, 3.5} * 2 =
+   * MecanumDriveWheelSpeeds{4.0, 5.0, 6.0, 7.0}
+   *
+   * @param scalar The scalar to multiply by.
+   * @return The scaled MecanumDriveWheelSpeeds.
+   */
+  constexpr MecanumDriveWheelSpeeds operator*(double scalar) const {
+    return {scalar * frontLeft, scalar * frontRight, scalar * rearLeft,
+            scalar * rearRight};
+  }
+
+  /**
+   * Divides the MecanumDriveWheelSpeeds by a scalar and returns the new
+   * MecanumDriveWheelSpeeds.
+   *
+   * <p>For example, MecanumDriveWheelSpeeds{2.0, 2.5, 1.5, 1.0} / 2 =
+   * MecanumDriveWheelSpeeds{1.0, 1.25, 0.75, 0.5}
+   *
+   * @param scalar The scalar to divide by.
+   * @return The scaled MecanumDriveWheelSpeeds.
+   */
+  constexpr MecanumDriveWheelSpeeds operator/(double scalar) const {
+    return operator*(1.0 / scalar);
+  }
 };
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/Odometry.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/Odometry.h
new file mode 100644
index 0000000..55c074c
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/Odometry.h
@@ -0,0 +1,88 @@
+// 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 <wpi/SymbolExports.h>
+
+#include "frc/geometry/Pose2d.h"
+#include "frc/kinematics/Kinematics.h"
+#include "frc/kinematics/WheelPositions.h"
+
+namespace frc {
+/**
+ * Class for odometry. Robot code should not use this directly- Instead, use the
+ * particular type for your drivetrain (e.g., DifferentialDriveOdometry).
+ * Odometry allows you to track the robot's position on the field over a course
+ * of a match using readings from your wheel encoders.
+ *
+ * Teams can use odometry during the autonomous period for complex tasks like
+ * path following. Furthermore, odometry can be used for latency compensation
+ * when using computer-vision systems.
+ */
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+class WPILIB_DLLEXPORT Odometry {
+ public:
+  /**
+   * Constructs an Odometry object.
+   *
+   * @param kinematics The kinematics for your drivetrain.
+   * @param gyroAngle The angle reported by the gyroscope.
+   * @param wheelPositions The current distances measured by each wheel.
+   * @param initialPose The starting position of the robot on the field.
+   */
+  explicit Odometry(const Kinematics<WheelSpeeds, WheelPositions>& kinematics,
+                    const Rotation2d& gyroAngle,
+                    const WheelPositions& wheelPositions,
+                    const Pose2d& initialPose = Pose2d{});
+
+  /**
+   * Resets the robot's position on the field.
+   *
+   * The gyroscope angle does not need to be reset here on the user's robot
+   * code. The library automatically takes care of offsetting the gyro angle.
+   *
+   * @param gyroAngle The angle reported by the gyroscope.
+   * @param wheelPositions The current distances measured by each wheel.
+   * @param pose The position on the field that your robot is at.
+   */
+  void ResetPosition(const Rotation2d& gyroAngle,
+                     const WheelPositions& wheelPositions, const Pose2d& pose) {
+    m_pose = pose;
+    m_previousAngle = pose.Rotation();
+    m_gyroOffset = m_pose.Rotation() - gyroAngle;
+    m_previousWheelPositions = wheelPositions;
+  }
+
+  /**
+   * Returns the position of the robot on the field.
+   * @return The pose of the robot.
+   */
+  const Pose2d& GetPose() const { return m_pose; }
+
+  /**
+   * Updates the robot's position on the field using forward kinematics and
+   * integration of the pose over time. This method takes in an angle parameter
+   * which is used instead of the angular rate that is calculated from forward
+   * kinematics, in addition to the current distance measurement at each wheel.
+   *
+   * @param gyroAngle The angle reported by the gyroscope.
+   * @param wheelPositions The current distances measured by each wheel.
+   *
+   * @return The new pose of the robot.
+   */
+  const Pose2d& Update(const Rotation2d& gyroAngle,
+                       const WheelPositions& wheelPositions);
+
+ private:
+  const Kinematics<WheelSpeeds, WheelPositions>& m_kinematics;
+  Pose2d m_pose;
+
+  WheelPositions m_previousWheelPositions;
+  Rotation2d m_previousAngle;
+  Rotation2d m_gyroOffset;
+};
+}  // namespace frc
+
+#include "Odometry.inc"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/Odometry.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/Odometry.inc
new file mode 100644
index 0000000..688c1bd
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/Odometry.inc
@@ -0,0 +1,38 @@
+// 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 "frc/kinematics/Odometry.h"
+
+namespace frc {
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+Odometry<WheelSpeeds, WheelPositions>::Odometry(
+    const Kinematics<WheelSpeeds, WheelPositions>& kinematics,
+    const Rotation2d& gyroAngle, const WheelPositions& wheelPositions,
+    const Pose2d& initialPose)
+    : m_kinematics(kinematics),
+      m_pose(initialPose),
+      m_previousWheelPositions(wheelPositions) {
+  m_previousAngle = m_pose.Rotation();
+  m_gyroOffset = m_pose.Rotation() - gyroAngle;
+}
+
+template <typename WheelSpeeds, WheelPositions WheelPositions>
+const Pose2d& Odometry<WheelSpeeds, WheelPositions>::Update(
+    const Rotation2d& gyroAngle, const WheelPositions& wheelPositions) {
+  auto angle = gyroAngle + m_gyroOffset;
+
+  auto twist = m_kinematics.ToTwist2d(m_previousWheelPositions, wheelPositions);
+  twist.dtheta = (angle - m_previousAngle).Radians();
+
+  auto newPose = m_pose.Exp(twist);
+
+  m_previousAngle = angle;
+  m_previousWheelPositions = wheelPositions;
+  m_pose = {newPose.Translation(), angle};
+
+  return m_pose;
+}
+}  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.h
index 97ee233..f6d4e5d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.h
@@ -4,23 +4,30 @@
 
 #pragma once
 
+#include <concepts>
 #include <cstddef>
 
+#include <Eigen/QR>
 #include <wpi/SymbolExports.h>
 #include <wpi/array.h>
 
-#include "Eigen/QR"
 #include "frc/EigenCore.h"
 #include "frc/geometry/Rotation2d.h"
 #include "frc/geometry/Translation2d.h"
 #include "frc/geometry/Twist2d.h"
 #include "frc/kinematics/ChassisSpeeds.h"
+#include "frc/kinematics/Kinematics.h"
+#include "frc/kinematics/SwerveDriveWheelPositions.h"
 #include "frc/kinematics/SwerveModulePosition.h"
 #include "frc/kinematics/SwerveModuleState.h"
 #include "units/velocity.h"
 #include "wpimath/MathShared.h"
 
 namespace frc {
+
+template <size_t NumModules>
+using SwerveDriveWheelSpeeds = wpi::array<SwerveModuleState, NumModules>;
+
 /**
  * Helper class that converts a chassis velocity (dx, dy, and dtheta components)
  * into individual module states (speed and angle).
@@ -44,27 +51,25 @@
  * the robot on the field using encoders and a gyro.
  */
 template <size_t NumModules>
-class SwerveDriveKinematics {
+class SwerveDriveKinematics
+    : public Kinematics<SwerveDriveWheelSpeeds<NumModules>,
+                        SwerveDriveWheelPositions<NumModules>> {
  public:
   /**
    * Constructs a swerve drive kinematics object. This takes in a variable
-   * number of wheel locations as Translation2ds. The order in which you pass in
-   * the wheel locations is the same order that you will receive the module
+   * number of module locations as Translation2ds. The order in which you pass
+   * in the module locations is the same order that you will receive the module
    * states when performing inverse kinematics. It is also expected that you
    * pass in the module states in the same order when calling the forward
    * kinematics methods.
    *
-   * @param wheel  The location of the first wheel relative to the physical
-   *               center of the robot.
-   * @param wheels The locations of the other wheels relative to the physical
-   *               center of the robot.
+   * @param moduleTranslations The locations of the modules relative to the
+   *                           physical center of the robot.
    */
-  template <typename... Wheels>
-  explicit SwerveDriveKinematics(Translation2d wheel, Wheels&&... wheels)
-      : m_modules{wheel, wheels...}, m_moduleStates(wpi::empty_array) {
-    static_assert(sizeof...(wheels) >= 1,
-                  "A swerve drive requires at least two modules");
-
+  template <std::convertible_to<Translation2d>... ModuleTranslations>
+    requires(sizeof...(ModuleTranslations) == NumModules)
+  explicit SwerveDriveKinematics(ModuleTranslations&&... moduleTranslations)
+      : m_modules{moduleTranslations...}, m_moduleHeadings(wpi::empty_array) {
     for (size_t i = 0; i < NumModules; i++) {
       // clang-format off
       m_inverseKinematics.template block<2, 3>(i * 2, 0) <<
@@ -80,8 +85,8 @@
   }
 
   explicit SwerveDriveKinematics(
-      const wpi::array<Translation2d, NumModules>& wheels)
-      : m_modules{wheels}, m_moduleStates(wpi::empty_array) {
+      const wpi::array<Translation2d, NumModules>& modules)
+      : m_modules{modules}, m_moduleHeadings(wpi::empty_array) {
     for (size_t i = 0; i < NumModules; i++) {
       // clang-format off
       m_inverseKinematics.template block<2, 3>(i * 2, 0) <<
@@ -99,6 +104,25 @@
   SwerveDriveKinematics(const SwerveDriveKinematics&) = default;
 
   /**
+   * Reset the internal swerve module headings.
+   * @param moduleHeadings The swerve module headings. The order of the module
+   * headings should be same as passed into the constructor of this class.
+   */
+  template <std::convertible_to<Rotation2d>... ModuleHeadings>
+    requires(sizeof...(ModuleHeadings) == NumModules)
+  void ResetHeadings(ModuleHeadings&&... moduleHeadings) {
+    return this->ResetHeadings(
+        wpi::array<Rotation2d, NumModules>{moduleHeadings...});
+  }
+
+  /**
+   * Reset the internal swerve module headings.
+   * @param moduleHeadings The swerve module headings. The order of the module
+   * headings should be same as passed into the constructor of this class.
+   */
+  void ResetHeadings(wpi::array<Rotation2d, NumModules> moduleHeadings);
+
+  /**
    * Performs inverse kinematics to return the module states from a desired
    * chassis velocity. This method is often used to convert joystick values into
    * module speeds and angles.
@@ -133,20 +157,29 @@
       const ChassisSpeeds& chassisSpeeds,
       const Translation2d& centerOfRotation = Translation2d{}) const;
 
+  SwerveDriveWheelSpeeds<NumModules> ToWheelSpeeds(
+      const ChassisSpeeds& chassisSpeeds) const override {
+    return ToSwerveModuleStates(chassisSpeeds);
+  }
+
   /**
    * Performs forward kinematics to return the resulting chassis state from the
    * given module states. This method is often used for odometry -- determining
    * the robot's position on the field using data from the real-world speed and
    * angle of each module on the robot.
    *
-   * @param wheelStates The state of the modules (as a SwerveModuleState type)
+   * @param moduleStates The state of the modules (as a SwerveModuleState type)
    * as measured from respective encoders and gyros. The order of the swerve
    * module states should be same as passed into the constructor of this class.
    *
    * @return The resulting chassis speed.
    */
-  template <typename... ModuleStates>
-  ChassisSpeeds ToChassisSpeeds(ModuleStates&&... wheelStates) const;
+  template <std::convertible_to<SwerveModuleState>... ModuleStates>
+    requires(sizeof...(ModuleStates) == NumModules)
+  ChassisSpeeds ToChassisSpeeds(ModuleStates&&... moduleStates) const {
+    return this->ToChassisSpeeds(
+        wpi::array<SwerveModuleState, NumModules>{moduleStates...});
+  }
 
   /**
    * Performs forward kinematics to return the resulting chassis state from the
@@ -161,8 +194,8 @@
    *
    * @return The resulting chassis speed.
    */
-  ChassisSpeeds ToChassisSpeeds(
-      wpi::array<SwerveModuleState, NumModules> moduleStates) const;
+  ChassisSpeeds ToChassisSpeeds(const wpi::array<SwerveModuleState, NumModules>&
+                                    moduleStates) const override;
 
   /**
    * Performs forward kinematics to return the resulting Twist2d from the
@@ -170,15 +203,19 @@
    * determining the robot's position on the field using data from the
    * real-world position delta and angle of each module on the robot.
    *
-   * @param wheelDeltas The latest change in position of the modules (as a
+   * @param moduleDeltas The latest change in position of the modules (as a
    * SwerveModulePosition type) as measured from respective encoders and gyros.
    * The order of the swerve module states should be same as passed into the
    * constructor of this class.
    *
    * @return The resulting Twist2d.
    */
-  template <typename... ModuleDeltas>
-  Twist2d ToTwist2d(ModuleDeltas&&... wheelDeltas) const;
+  template <std::convertible_to<SwerveModulePosition>... ModuleDeltas>
+    requires(sizeof...(ModuleDeltas) == NumModules)
+  Twist2d ToTwist2d(ModuleDeltas&&... moduleDeltas) const {
+    return this->ToTwist2d(
+        wpi::array<SwerveModulePosition, NumModules>{moduleDeltas...});
+  }
 
   /**
    * Performs forward kinematics to return the resulting Twist2d from the
@@ -186,7 +223,7 @@
    * determining the robot's position on the field using data from the
    * real-world position delta and angle of each module on the robot.
    *
-   * @param wheelDeltas The latest change in position of the modules (as a
+   * @param moduleDeltas The latest change in position of the modules (as a
    * SwerveModulePosition type) as measured from respective encoders and gyros.
    * The order of the swerve module states should be same as passed into the
    * constructor of this class.
@@ -194,7 +231,20 @@
    * @return The resulting Twist2d.
    */
   Twist2d ToTwist2d(
-      wpi::array<SwerveModulePosition, NumModules> wheelDeltas) const;
+      wpi::array<SwerveModulePosition, NumModules> moduleDeltas) const;
+
+  Twist2d ToTwist2d(
+      const SwerveDriveWheelPositions<NumModules>& start,
+      const SwerveDriveWheelPositions<NumModules>& end) const override {
+    auto result =
+        wpi::array<SwerveModulePosition, NumModules>(wpi::empty_array);
+    for (size_t i = 0; i < NumModules; i++) {
+      auto startModule = start.positions[i];
+      auto endModule = end.positions[i];
+      result[i] = {endModule.distance - startModule.distance, endModule.angle};
+    }
+    return ToTwist2d(result);
+  }
 
   /**
    * Renormalizes the wheel speeds if any individual speed is above the
@@ -229,7 +279,7 @@
    *
    * @param moduleStates Reference to array of module states. The array will be
    * mutated with the normalized speeds!
-   * @param currentChassisSpeed Current speed of the robot
+   * @param desiredChassisSpeed The desired speed of the robot
    * @param attainableMaxModuleSpeed The absolute max speed a module can reach
    * @param attainableMaxRobotTranslationSpeed The absolute max speed the robot
    * can reach while translating
@@ -238,7 +288,7 @@
    */
   static void DesaturateWheelSpeeds(
       wpi::array<SwerveModuleState, NumModules>* moduleStates,
-      ChassisSpeeds currentChassisSpeed,
+      ChassisSpeeds desiredChassisSpeed,
       units::meters_per_second_t attainableMaxModuleSpeed,
       units::meters_per_second_t attainableMaxRobotTranslationSpeed,
       units::radians_per_second_t attainableMaxRobotRotationSpeed);
@@ -247,7 +297,7 @@
   mutable Matrixd<NumModules * 2, 3> m_inverseKinematics;
   Eigen::HouseholderQR<Matrixd<NumModules * 2, 3>> m_forwardKinematics;
   wpi::array<Translation2d, NumModules> m_modules;
-  mutable wpi::array<SwerveModuleState, NumModules> m_moduleStates;
+  mutable wpi::array<Rotation2d, NumModules> m_moduleHeadings;
 
   mutable Translation2d m_previousCoR;
 };
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.inc
index c7f26e0..db85d99 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.inc
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.inc
@@ -13,22 +13,32 @@
 
 namespace frc {
 
-template <class... Wheels>
-SwerveDriveKinematics(Translation2d, Wheels...)
-    -> SwerveDriveKinematics<1 + sizeof...(Wheels)>;
+template <typename ModuleTranslation, typename... ModuleTranslations>
+SwerveDriveKinematics(ModuleTranslation, ModuleTranslations...)
+    -> SwerveDriveKinematics<1 + sizeof...(ModuleTranslations)>;
+
+template <size_t NumModules>
+void SwerveDriveKinematics<NumModules>::ResetHeadings(
+    wpi::array<Rotation2d, NumModules> moduleHeadings) {
+  for (size_t i = 0; i < NumModules; i++) {
+    m_moduleHeadings[i] = moduleHeadings[i];
+  }
+}
 
 template <size_t NumModules>
 wpi::array<SwerveModuleState, NumModules>
 SwerveDriveKinematics<NumModules>::ToSwerveModuleStates(
     const ChassisSpeeds& chassisSpeeds,
     const Translation2d& centerOfRotation) const {
+  wpi::array<SwerveModuleState, NumModules> moduleStates(wpi::empty_array);
+
   if (chassisSpeeds.vx == 0_mps && chassisSpeeds.vy == 0_mps &&
       chassisSpeeds.omega == 0_rad_per_s) {
     for (size_t i = 0; i < NumModules; i++) {
-      m_moduleStates[i].speed = 0_mps;
+      moduleStates[i] = {0_mps, m_moduleHeadings[i]};
     }
 
-    return m_moduleStates;
+    return moduleStates;
   }
 
   // We have a new center of rotation. We need to compute the matrix again.
@@ -58,28 +68,16 @@
     auto speed = units::math::hypot(x, y);
     Rotation2d rotation{x.value(), y.value()};
 
-    m_moduleStates[i] = {speed, rotation};
+    moduleStates[i] = {speed, rotation};
+    m_moduleHeadings[i] = rotation;
   }
 
-  return m_moduleStates;
-}
-
-template <size_t NumModules>
-template <typename... ModuleStates>
-ChassisSpeeds SwerveDriveKinematics<NumModules>::ToChassisSpeeds(
-    ModuleStates&&... wheelStates) const {
-  static_assert(sizeof...(wheelStates) == NumModules,
-                "Number of modules is not consistent with number of wheel "
-                "locations provided in constructor.");
-
-  wpi::array<SwerveModuleState, NumModules> moduleStates{wheelStates...};
-
-  return this->ToChassisSpeeds(moduleStates);
+  return moduleStates;
 }
 
 template <size_t NumModules>
 ChassisSpeeds SwerveDriveKinematics<NumModules>::ToChassisSpeeds(
-    wpi::array<SwerveModuleState, NumModules> moduleStates) const {
+    const wpi::array<SwerveModuleState, NumModules>& moduleStates) const {
   Matrixd<NumModules * 2, 1> moduleStateMatrix;
 
   for (size_t i = 0; i < NumModules; ++i) {
@@ -97,19 +95,6 @@
 }
 
 template <size_t NumModules>
-template <typename... ModuleDeltas>
-Twist2d SwerveDriveKinematics<NumModules>::ToTwist2d(
-    ModuleDeltas&&... wheelDeltas) const {
-  static_assert(sizeof...(wheelDeltas) == NumModules,
-                "Number of modules is not consistent with number of wheel "
-                "locations provided in constructor.");
-
-  wpi::array<SwerveModulePosition, NumModules> moduleDeltas{wheelDeltas...};
-
-  return this->ToTwist2d(moduleDeltas);
-}
-
-template <size_t NumModules>
 Twist2d SwerveDriveKinematics<NumModules>::ToTwist2d(
     wpi::array<SwerveModulePosition, NumModules> moduleDeltas) const {
   Matrixd<NumModules * 2, 1> moduleDeltaMatrix;
@@ -134,12 +119,13 @@
     wpi::array<SwerveModuleState, NumModules>* moduleStates,
     units::meters_per_second_t attainableMaxSpeed) {
   auto& states = *moduleStates;
-  auto realMaxSpeed = std::max_element(states.begin(), states.end(),
-                                       [](const auto& a, const auto& b) {
-                                         return units::math::abs(a.speed) <
-                                                units::math::abs(b.speed);
-                                       })
-                          ->speed;
+  auto realMaxSpeed =
+      units::math::abs(std::max_element(states.begin(), states.end(),
+                                        [](const auto& a, const auto& b) {
+                                          return units::math::abs(a.speed) <
+                                                 units::math::abs(b.speed);
+                                        })
+                           ->speed);
 
   if (realMaxSpeed > attainableMaxSpeed) {
     for (auto& module : states) {
@@ -151,18 +137,19 @@
 template <size_t NumModules>
 void SwerveDriveKinematics<NumModules>::DesaturateWheelSpeeds(
     wpi::array<SwerveModuleState, NumModules>* moduleStates,
-    ChassisSpeeds currentChassisSpeed,
+    ChassisSpeeds desiredChassisSpeed,
     units::meters_per_second_t attainableMaxModuleSpeed,
     units::meters_per_second_t attainableMaxRobotTranslationSpeed,
     units::radians_per_second_t attainableMaxRobotRotationSpeed) {
   auto& states = *moduleStates;
 
-  auto realMaxSpeed = std::max_element(states.begin(), states.end(),
-                                       [](const auto& a, const auto& b) {
-                                         return units::math::abs(a.speed) <
-                                                units::math::abs(b.speed);
-                                       })
-                          ->speed;
+  auto realMaxSpeed =
+      units::math::abs(std::max_element(states.begin(), states.end(),
+                                        [](const auto& a, const auto& b) {
+                                          return units::math::abs(a.speed) <
+                                                 units::math::abs(b.speed);
+                                        })
+                           ->speed);
 
   if (attainableMaxRobotTranslationSpeed == 0_mps ||
       attainableMaxRobotRotationSpeed == 0_rad_per_s || realMaxSpeed == 0_mps) {
@@ -170,10 +157,10 @@
   }
 
   auto translationalK =
-      units::math::hypot(currentChassisSpeed.vx, currentChassisSpeed.vy) /
+      units::math::hypot(desiredChassisSpeed.vx, desiredChassisSpeed.vy) /
       attainableMaxRobotTranslationSpeed;
 
-  auto rotationalK = units::math::abs(currentChassisSpeed.omega) /
+  auto rotationalK = units::math::abs(desiredChassisSpeed.omega) /
                      attainableMaxRobotRotationSpeed;
 
   auto k = units::math::max(translationalK, rotationalK);
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveOdometry.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveOdometry.h
index 015c2c0..2e0e553 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveOdometry.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveOdometry.h
@@ -11,8 +11,11 @@
 #include <wpi/SymbolExports.h>
 #include <wpi/timestamp.h>
 
+#include "Odometry.h"
 #include "SwerveDriveKinematics.h"
+#include "SwerveDriveWheelPositions.h"
 #include "SwerveModulePosition.h"
+#include "SwerveModuleState.h"
 #include "frc/geometry/Pose2d.h"
 #include "units/time.h"
 
@@ -28,7 +31,9 @@
  * when using computer-vision systems.
  */
 template <size_t NumModules>
-class SwerveDriveOdometry {
+class SwerveDriveOdometry
+    : public Odometry<SwerveDriveWheelSpeeds<NumModules>,
+                      SwerveDriveWheelPositions<NumModules>> {
  public:
   /**
    * Constructs a SwerveDriveOdometry object.
@@ -56,13 +61,13 @@
   void ResetPosition(
       const Rotation2d& gyroAngle,
       const wpi::array<SwerveModulePosition, NumModules>& modulePositions,
-      const Pose2d& pose);
-
-  /**
-   * Returns the position of the robot on the field.
-   * @return The pose of the robot.
-   */
-  const Pose2d& GetPose() const { return m_pose; }
+      const Pose2d& pose) {
+    Odometry<
+        SwerveDriveWheelSpeeds<NumModules>,
+        SwerveDriveWheelPositions<NumModules>>::ResetPosition(gyroAngle,
+                                                              {modulePositions},
+                                                              pose);
+  }
 
   /**
    * Updates the robot's position on the field using forward kinematics and
@@ -79,17 +84,15 @@
    */
   const Pose2d& Update(
       const Rotation2d& gyroAngle,
-      const wpi::array<SwerveModulePosition, NumModules>& modulePositions);
+      const wpi::array<SwerveModulePosition, NumModules>& modulePositions) {
+    return Odometry<
+        SwerveDriveWheelSpeeds<NumModules>,
+        SwerveDriveWheelPositions<NumModules>>::Update(gyroAngle,
+                                                       {modulePositions});
+  }
 
  private:
-  SwerveDriveKinematics<NumModules> m_kinematics;
-  Pose2d m_pose;
-
-  Rotation2d m_previousAngle;
-  Rotation2d m_gyroOffset;
-
-  wpi::array<SwerveModulePosition, NumModules> m_previousModulePositions{
-      wpi::empty_array};
+  SwerveDriveKinematics<NumModules> m_kinematicsImpl;
 };
 
 extern template class EXPORT_TEMPLATE_DECLARE(WPILIB_DLLEXPORT)
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveOdometry.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveOdometry.inc
index 64b46c1..48ddfec 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveOdometry.inc
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveOdometry.inc
@@ -13,58 +13,11 @@
     SwerveDriveKinematics<NumModules> kinematics, const Rotation2d& gyroAngle,
     const wpi::array<SwerveModulePosition, NumModules>& modulePositions,
     const Pose2d& initialPose)
-    : m_kinematics(kinematics), m_pose(initialPose) {
-  m_previousAngle = m_pose.Rotation();
-  m_gyroOffset = m_pose.Rotation() - gyroAngle;
-
-  for (size_t i = 0; i < NumModules; i++) {
-    m_previousModulePositions[i] = {modulePositions[i].distance,
-                                    modulePositions[i].angle};
-  }
-
+    : Odometry<SwerveDriveWheelSpeeds<NumModules>,
+               SwerveDriveWheelPositions<NumModules>>(
+          m_kinematicsImpl, gyroAngle, {modulePositions}, initialPose),
+      m_kinematicsImpl(kinematics) {
   wpi::math::MathSharedStore::ReportUsage(
       wpi::math::MathUsageId::kOdometry_SwerveDrive, 1);
 }
-
-template <size_t NumModules>
-void SwerveDriveOdometry<NumModules>::ResetPosition(
-    const Rotation2d& gyroAngle,
-    const wpi::array<SwerveModulePosition, NumModules>& modulePositions,
-    const Pose2d& pose) {
-  m_pose = pose;
-  m_previousAngle = pose.Rotation();
-  m_gyroOffset = m_pose.Rotation() - gyroAngle;
-
-  for (size_t i = 0; i < NumModules; i++) {
-    m_previousModulePositions[i].distance = modulePositions[i].distance;
-  }
-}
-
-template <size_t NumModules>
-const Pose2d& frc::SwerveDriveOdometry<NumModules>::Update(
-    const Rotation2d& gyroAngle,
-    const wpi::array<SwerveModulePosition, NumModules>& modulePositions) {
-  auto moduleDeltas =
-      wpi::array<SwerveModulePosition, NumModules>(wpi::empty_array);
-  for (size_t index = 0; index < NumModules; index++) {
-    auto lastPosition = m_previousModulePositions[index];
-    auto currentPosition = modulePositions[index];
-    moduleDeltas[index] = {currentPosition.distance - lastPosition.distance,
-                           currentPosition.angle};
-
-    m_previousModulePositions[index].distance = modulePositions[index].distance;
-  }
-
-  auto angle = gyroAngle + m_gyroOffset;
-
-  auto twist = m_kinematics.ToTwist2d(moduleDeltas);
-  twist.dtheta = (angle - m_previousAngle).Radians();
-
-  auto newPose = m_pose.Exp(twist);
-
-  m_previousAngle = angle;
-  m_pose = {newPose.Translation(), angle};
-
-  return m_pose;
-}
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveWheelPositions.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveWheelPositions.h
new file mode 100644
index 0000000..c1686d2
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveDriveWheelPositions.h
@@ -0,0 +1,51 @@
+// 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 <wpi/MathExtras.h>
+#include <wpi/SymbolExports.h>
+#include <wpi/array.h>
+
+#include "frc/kinematics/SwerveModulePosition.h"
+
+namespace frc {
+/**
+ * Represents the wheel positions for a swerve drive drivetrain.
+ */
+template <size_t NumModules>
+struct WPILIB_DLLEXPORT SwerveDriveWheelPositions {
+  /**
+   * The distances driven by the wheels.
+   */
+  wpi::array<SwerveModulePosition, NumModules> positions;
+
+  /**
+   * Checks equality between this SwerveDriveWheelPositions and another object.
+   *
+   * @param other The other object.
+   * @return Whether the two objects are equal.
+   */
+  bool operator==(const SwerveDriveWheelPositions& other) const = default;
+
+  /**
+   * Checks inequality between this SwerveDriveWheelPositions and another
+   * object.
+   *
+   * @param other The other object.
+   * @return Whether the two objects are not equal.
+   */
+  bool operator!=(const SwerveDriveWheelPositions& other) const = default;
+
+  SwerveDriveWheelPositions<NumModules> Interpolate(
+      const SwerveDriveWheelPositions<NumModules>& endValue, double t) const {
+    auto result =
+        wpi::array<SwerveModulePosition, NumModules>(wpi::empty_array);
+    for (size_t i = 0; i < NumModules; i++) {
+      result[i] = positions[i].Interpolate(endValue.positions[i], t);
+    }
+    return {result};
+  }
+};
+}  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveModulePosition.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveModulePosition.h
index 18ed464..93f7465 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveModulePosition.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveModulePosition.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <wpi/MathExtras.h>
 #include <wpi/SymbolExports.h>
 
 #include "frc/geometry/Rotation2d.h"
@@ -25,5 +26,19 @@
    * Angle of the module.
    */
   Rotation2d angle;
+
+  /**
+   * Checks equality between this SwerveModulePosition and another object.
+   *
+   * @param other The other object.
+   * @return Whether the two objects are equal.
+   */
+  bool operator==(const SwerveModulePosition& other) const;
+
+  SwerveModulePosition Interpolate(const SwerveModulePosition& endValue,
+                                   double t) const {
+    return {wpi::Lerp(distance, endValue.distance, t),
+            wpi::Lerp(angle, endValue.angle, t)};
+  }
 };
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveModuleState.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveModuleState.h
index cae2d53..2f95d9b 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveModuleState.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/SwerveModuleState.h
@@ -27,6 +27,14 @@
   Rotation2d angle;
 
   /**
+   * Checks equality between this SwerveModuleState and another object.
+   *
+   * @param other The other object.
+   * @return Whether the two objects are equal.
+   */
+  bool operator==(const SwerveModuleState& other) const;
+
+  /**
    * Minimize the change in heading the desired swerve module state would
    * require by potentially reversing the direction the wheel spins. If this is
    * used with the PIDController class's continuous input functionality, the
@@ -36,13 +44,6 @@
    * @param currentAngle The current module angle.
    */
   static SwerveModuleState Optimize(const SwerveModuleState& desiredState,
-                                    const Rotation2d& currentAngle) {
-    auto delta = desiredState.angle - currentAngle;
-    if (units::math::abs(delta.Degrees()) > 90_deg) {
-      return {-desiredState.speed, desiredState.angle + Rotation2d{180_deg}};
-    } else {
-      return {desiredState.speed, desiredState.angle};
-    }
-  }
+                                    const Rotation2d& currentAngle);
 };
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/WheelPositions.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/WheelPositions.h
new file mode 100644
index 0000000..8867f66
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/kinematics/WheelPositions.h
@@ -0,0 +1,15 @@
+// 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 <concepts>
+
+namespace frc {
+template <typename T>
+concept WheelPositions =
+    std::copy_constructible<T> && requires(T a, T b, double t) {
+      { a.Interpolate(b, t) } -> std::convertible_to<T>;
+    };
+}  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/spline/CubicHermiteSpline.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/spline/CubicHermiteSpline.h
index 0636707..1d6aaeb 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/spline/CubicHermiteSpline.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/spline/CubicHermiteSpline.h
@@ -49,7 +49,7 @@
    * Returns the hermite basis matrix for cubic hermite spline interpolation.
    * @return The hermite basis matrix for cubic hermite spline interpolation.
    */
-  static Matrixd<4, 4> MakeHermiteBasis() {
+  static Eigen::Matrix4d MakeHermiteBasis() {
     // Given P(i), P'(i), P(i+1), P'(i+1), the control vectors, we want to find
     // the coefficients of the spline P(t) = a₃t³ + a₂t² + a₁t + a₀.
     //
@@ -71,10 +71,10 @@
     // [a₁] = [ 0  1  0  0][P(i+1) ]
     // [a₀] = [ 1  0  0  0][P'(i+1)]
 
-    static const Matrixd<4, 4> basis{{+2.0, +1.0, -2.0, +1.0},
-                                     {-3.0, -2.0, +3.0, -1.0},
-                                     {+0.0, +1.0, +0.0, +0.0},
-                                     {+1.0, +0.0, +0.0, +0.0}};
+    static const Eigen::Matrix4d basis{{+2.0, +1.0, -2.0, +1.0},
+                                       {-3.0, -2.0, +3.0, -1.0},
+                                       {+0.0, +1.0, +0.0, +0.0},
+                                       {+1.0, +0.0, +0.0, +0.0}};
     return basis;
   }
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/spline/SplineParameterizer.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/spline/SplineParameterizer.h
index 0720cd1..e1d2d52 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/spline/SplineParameterizer.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/spline/SplineParameterizer.h
@@ -56,7 +56,7 @@
   };
 
   /**
-   * Parameterizes the spline. This method breaks up the spline into various
+   * Parametrizes the spline. This method breaks up the spline into various
    * arcs until their dx, dy, and dtheta are within specific tolerances.
    *
    * @param spline The spline to parameterize.
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/system/Discretization.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/system/Discretization.h
index 957b875..72a3d43 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/system/Discretization.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/system/Discretization.h
@@ -4,9 +4,10 @@
 
 #pragma once
 
+#include <unsupported/Eigen/MatrixFunctions>
+
 #include "frc/EigenCore.h"
 #include "units/time.h"
-#include "unsupported/Eigen/MatrixFunctions"
 
 namespace frc {
 
@@ -44,9 +45,9 @@
   // M = [A  B]
   //     [0  0]
   Matrixd<States + Inputs, States + Inputs> M;
-  M.setZero();
   M.template block<States, States>(0, 0) = contA;
   M.template block<States, Inputs>(0, States) = contB;
+  M.template block<Inputs, States + Inputs>(States, 0).setZero();
 
   // ϕ = eᴹᵀ = [A_d  B_d]
   //           [ 0    I ]
@@ -101,95 +102,6 @@
 }
 
 /**
- * Discretizes the given continuous A and Q matrices.
- *
- * Rather than solving a 2N x 2N matrix exponential like in DiscretizeAQ()
- * (which is expensive), we take advantage of the structure of the block matrix
- * of A and Q.
- *
- * <ul>
- *   <li>eᴬᵀ, which is only N x N, is relatively cheap.
- *   <li>The upper-right quarter of the 2N x 2N matrix, which we can approximate
- *       using a taylor series to several terms and still be substantially
- *       cheaper than taking the big exponential.
- * </ul>
- *
- * @tparam States Number of states.
- * @param contA Continuous system matrix.
- * @param contQ Continuous process noise covariance matrix.
- * @param dt    Discretization timestep.
- * @param discA Storage for discrete system matrix.
- * @param discQ Storage for discrete process noise covariance matrix.
- */
-template <int States>
-void DiscretizeAQTaylor(const Matrixd<States, States>& contA,
-                        const Matrixd<States, States>& contQ,
-                        units::second_t dt, Matrixd<States, States>* discA,
-                        Matrixd<States, States>* discQ) {
-  //       T
-  // Q_d = ∫ e^(Aτ) Q e^(Aᵀτ) dτ
-  //       0
-  //
-  // M = [−A  Q ]
-  //     [ 0  Aᵀ]
-  // ϕ = eᴹᵀ
-  // ϕ₁₂ = A_d⁻¹Q_d
-  //
-  // Taylor series of ϕ:
-  //
-  //   ϕ = eᴹᵀ = I + MT + 1/2 M²T² + 1/6 M³T³ + …
-  //   ϕ = eᴹᵀ = I + MT + 1/2 T²M² + 1/6 T³M³ + …
-  //
-  // Taylor series of ϕ expanded for ϕ₁₂:
-  //
-  //   ϕ₁₂ = 0 + QT + 1/2 T² (−AQ + QAᵀ) + 1/6 T³ (−A lastTerm + Q Aᵀ²) + …
-  //
-  // ```
-  // lastTerm = Q
-  // lastCoeff = T
-  // ATn = Aᵀ
-  // ϕ₁₂ = lastTerm lastCoeff = QT
-  //
-  // for i in range(2, 6):
-  //   // i = 2
-  //   lastTerm = −A lastTerm + Q ATn = −AQ + QAᵀ
-  //   lastCoeff *= T/i → lastCoeff *= T/2 = 1/2 T²
-  //   ATn *= Aᵀ = Aᵀ²
-  //
-  //   // i = 3
-  //   lastTerm = −A lastTerm + Q ATn = −A (−AQ + QAᵀ) + QAᵀ² = …
-  //   …
-  // ```
-
-  // Make continuous Q symmetric if it isn't already
-  Matrixd<States, States> Q = (contQ + contQ.transpose()) / 2.0;
-
-  Matrixd<States, States> lastTerm = Q;
-  double lastCoeff = dt.value();
-
-  // Aᵀⁿ
-  Matrixd<States, States> ATn = contA.transpose();
-
-  Matrixd<States, States> phi12 = lastTerm * lastCoeff;
-
-  // i = 6 i.e. 5th order should be enough precision
-  for (int i = 2; i < 6; ++i) {
-    lastTerm = -contA * lastTerm + Q * ATn;
-    lastCoeff *= dt.value() / static_cast<double>(i);
-
-    phi12 += lastTerm * lastCoeff;
-
-    ATn *= contA.transpose();
-  }
-
-  DiscretizeA<States>(contA, dt, discA);
-  Q = *discA * phi12;
-
-  // Make discrete Q symmetric if it isn't already
-  *discQ = (Q + Q.transpose()) / 2.0;
-}
-
-/**
  * Returns a discretized version of the provided continuous measurement noise
  * covariance matrix.
  *
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/system/LinearSystemLoop.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/system/LinearSystemLoop.h
index 1300a82..c61531a 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/system/LinearSystemLoop.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/system/LinearSystemLoop.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <functional>
+
 #include <wpi/SymbolExports.h>
 
 #include "frc/EigenCore.h"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/system/NumericalIntegration.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/system/NumericalIntegration.h
index bb856ec..98b6cc3 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/system/NumericalIntegration.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/system/NumericalIntegration.h
@@ -4,12 +4,10 @@
 
 #pragma once
 
-#include <frc/StateSpaceUtil.h>
-
 #include <algorithm>
 #include <array>
+#include <cmath>
 
-#include "Eigen/Core"
 #include "units/time.h"
 
 namespace frc {
@@ -122,7 +120,11 @@
                               (b1[6] - b2[6]) * k7))
                             .norm();
 
-      h *= 0.9 * std::pow(maxError / truncationError, 1.0 / 5.0);
+      if (truncationError == 0.0) {
+        h = dt.value() - dtElapsed;
+      } else {
+        h *= 0.9 * std::pow(maxError / truncationError, 1.0 / 5.0);
+      }
     } while (truncationError > maxError);
 
     dtElapsed += h;
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/system/plant/DCMotor.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/system/plant/DCMotor.h
index 831f532..ad711ee 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/system/plant/DCMotor.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/system/plant/DCMotor.h
@@ -97,7 +97,7 @@
   }
 
   /**
-   * Returns the speed produced by the motor at a given torque and input
+   * Returns the angular speed produced by the motor at a given torque and input
    * voltage.
    *
    * @param torque        The torque produced by the motor.
@@ -119,89 +119,123 @@
   }
 
   /**
-   * Returns instance of CIM.
+   * Returns a gearbox of CIM motors.
    */
   static constexpr DCMotor CIM(int numMotors = 1) {
     return DCMotor(12_V, 2.42_Nm, 133_A, 2.7_A, 5310_rpm, numMotors);
   }
 
   /**
-   * Returns instance of MiniCIM.
+   * Returns a gearbox of MiniCIM motors.
    */
   static constexpr DCMotor MiniCIM(int numMotors = 1) {
     return DCMotor(12_V, 1.41_Nm, 89_A, 3_A, 5840_rpm, numMotors);
   }
 
   /**
-   * Returns instance of Bag motor.
+   * Returns a gearbox of Bag motor motors.
    */
   static constexpr DCMotor Bag(int numMotors = 1) {
     return DCMotor(12_V, 0.43_Nm, 53_A, 1.8_A, 13180_rpm, numMotors);
   }
 
   /**
-   * Returns instance of Vex 775 Pro.
+   * Returns a gearbox of Vex 775 Pro motors.
    */
   static constexpr DCMotor Vex775Pro(int numMotors = 1) {
     return DCMotor(12_V, 0.71_Nm, 134_A, 0.7_A, 18730_rpm, numMotors);
   }
 
   /**
-   * Returns instance of Andymark RS 775-125.
+   * Returns a gearbox of Andymark RS 775-125 motors.
    */
   static constexpr DCMotor RS775_125(int numMotors = 1) {
     return DCMotor(12_V, 0.28_Nm, 18_A, 1.6_A, 5800_rpm, numMotors);
   }
 
   /**
-   * Returns instance of Banebots RS 775.
+   * Returns a gearbox of Banebots RS 775 motors.
    */
   static constexpr DCMotor BanebotsRS775(int numMotors = 1) {
     return DCMotor(12_V, 0.72_Nm, 97_A, 2.7_A, 13050_rpm, numMotors);
   }
 
   /**
-   * Returns instance of Andymark 9015.
+   * Returns a gearbox of Andymark 9015 motors.
    */
   static constexpr DCMotor Andymark9015(int numMotors = 1) {
     return DCMotor(12_V, 0.36_Nm, 71_A, 3.7_A, 14270_rpm, numMotors);
   }
 
   /**
-   * Returns instance of Banebots RS 550.
+   * Returns a gearbox of Banebots RS 550 motors.
    */
   static constexpr DCMotor BanebotsRS550(int numMotors = 1) {
     return DCMotor(12_V, 0.38_Nm, 84_A, 0.4_A, 19000_rpm, numMotors);
   }
 
   /**
-   * Returns instance of NEO brushless motor.
+   * Returns a gearbox of NEO brushless motors.
    */
   static constexpr DCMotor NEO(int numMotors = 1) {
     return DCMotor(12_V, 2.6_Nm, 105_A, 1.8_A, 5676_rpm, numMotors);
   }
 
   /**
-   * Returns instance of NEO 550 brushless motor.
+   * Returns a gearbox of NEO 550 brushless motors.
    */
   static constexpr DCMotor NEO550(int numMotors = 1) {
     return DCMotor(12_V, 0.97_Nm, 100_A, 1.4_A, 11000_rpm, numMotors);
   }
 
   /**
-   * Returns instance of Falcon 500 brushless motor.
+   * Returns a gearbox of Falcon 500 brushless motors.
    */
   static constexpr DCMotor Falcon500(int numMotors = 1) {
     return DCMotor(12_V, 4.69_Nm, 257_A, 1.5_A, 6380_rpm, numMotors);
   }
 
   /**
+   * Return a gearbox of Falcon 500 motors with FOC (Field-Oriented Control)
+   * enabled.
+   */
+  static constexpr DCMotor Falcon500FOC(int numMotors = 1) {
+    // https://store.ctr-electronics.com/falcon-500-powered-by-talon-fx/
+    return DCMotor(12_V, 5.84_Nm, 304_A, 1.5_A, 6080_rpm, numMotors);
+  }
+
+  /**
    * Return a gearbox of Romi/TI_RSLK MAX motors.
    */
   static constexpr DCMotor RomiBuiltIn(int numMotors = 1) {
     // From https://www.pololu.com/product/1520/specs
     return DCMotor(4.5_V, 0.1765_Nm, 1.25_A, 0.13_A, 150_rpm, numMotors);
   }
+
+  /**
+   * Return a gearbox of Kraken X60 brushless motors.
+   */
+  static constexpr DCMotor KrakenX60(int numMotors = 1) {
+    // From https://store.ctr-electronics.com/announcing-kraken-x60/
+    return DCMotor(12_V, 7.09_Nm, 366_A, 2_A, 6000_rpm, numMotors);
+  }
+
+  /**
+   * Return a gearbox of Kraken X60 brushless motors with FOC (Field-Oriented
+   * Control) enabled.
+   */
+  static constexpr DCMotor KrakenX60FOC(int numMotors = 1) {
+    // From https://store.ctr-electronics.com/announcing-kraken-x60/
+    return DCMotor(12_V, 9.37_Nm, 483_A, 2_A, 5800_rpm, numMotors);
+  }
+
+  /**
+   * Return a gearbox of Neo Vortex brushless motors.
+   */
+  static constexpr DCMotor NeoVortex(int numMotors = 1) {
+    // From https://www.revrobotics.com/next-generation-spark-neo/
+    return DCMotor(12_V, 3.60_Nm, 211_A, 3.615_A, 6784_rpm, numMotors);
+  }
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/system/plant/LinearSystemId.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/system/plant/LinearSystemId.h
index 3e69545..64f3496 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/system/plant/LinearSystemId.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/system/plant/LinearSystemId.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <concepts>
 #include <stdexcept>
 
 #include <wpi/SymbolExports.h>
@@ -76,9 +77,9 @@
    * @param kA The acceleration gain, in volts/(unit/sec²).
    * @throws std::domain_error if kV <= 0 or kA <= 0.
    */
-  template <typename Distance, typename = std::enable_if_t<
-                                   std::is_same_v<units::meter, Distance> ||
-                                   std::is_same_v<units::radian, Distance>>>
+  template <typename Distance>
+    requires std::same_as<units::meter, Distance> ||
+             std::same_as<units::radian, Distance>
   static LinearSystem<1, 1, 1> IdentifyVelocitySystem(
       decltype(1_V / Velocity_t<Distance>(1)) kV,
       decltype(1_V / Acceleration_t<Distance>(1)) kA) {
@@ -117,9 +118,9 @@
    *
    * @throws std::domain_error if kV <= 0 or kA <= 0.
    */
-  template <typename Distance, typename = std::enable_if_t<
-                                   std::is_same_v<units::meter, Distance> ||
-                                   std::is_same_v<units::radian, Distance>>>
+  template <typename Distance>
+    requires std::same_as<units::meter, Distance> ||
+             std::same_as<units::radian, Distance>
   static LinearSystem<2, 1, 1> IdentifyPositionSystem(
       decltype(1_V / Velocity_t<Distance>(1)) kV,
       decltype(1_V / Acceleration_t<Distance>(1)) kA) {
@@ -217,6 +218,48 @@
   static LinearSystem<2, 1, 2> DCMotorSystem(DCMotor motor,
                                              units::kilogram_square_meter_t J,
                                              double G);
+
+  /**
+   * Create a state-space model of a DC motor system from its kV
+   * (volts/(unit/sec)) and kA (volts/(unit/sec²)). These constants can be
+   * found using SysId. the states of the system are [position, velocity],
+   * inputs are [voltage], and outputs are [position].
+   *
+   * You MUST use an SI unit (i.e. meters or radians) for the Distance template
+   * argument. You may still use non-SI units (such as feet or inches) for the
+   * actual method arguments; they will automatically be converted to SI
+   * internally.
+   *
+   * The parameters provided by the user are from this feedforward model:
+   *
+   * u = K_v v + K_a a
+   *
+   * @param kV The velocity gain, in volts/(unit/sec).
+   * @param kA The acceleration gain, in volts/(unit/sec²).
+   *
+   * @throws std::domain_error if kV <= 0 or kA <= 0.
+   */
+  template <typename Distance>
+    requires std::same_as<units::meter, Distance> ||
+             std::same_as<units::radian, Distance>
+  static LinearSystem<2, 1, 2> DCMotorSystem(
+      decltype(1_V / Velocity_t<Distance>(1)) kV,
+      decltype(1_V / Acceleration_t<Distance>(1)) kA) {
+    if (kV <= decltype(kV){0}) {
+      throw std::domain_error("Kv must be greater than zero.");
+    }
+    if (kA <= decltype(kA){0}) {
+      throw std::domain_error("Ka must be greater than zero.");
+    }
+
+    Matrixd<2, 2> A{{0.0, 1.0}, {0.0, -kV.value() / kA.value()}};
+    Matrixd<2, 1> B{0.0, 1.0 / kA.value()};
+    Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
+    Matrixd<2, 1> D{{0.0}, {0.0}};
+
+    return LinearSystem<2, 1, 2>(A, B, C, D);
+  }
+
   /**
    * Create a state-space model of differential drive drivetrain. In this model,
    * the states are [left velocity, right velocity], the inputs are [left
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.h
new file mode 100644
index 0000000..f45987b
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.h
@@ -0,0 +1,194 @@
+// 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 "units/time.h"
+#include "wpimath/MathShared.h"
+
+namespace frc {
+
+/**
+ * A Exponential-shaped velocity profile.
+ *
+ * While this class can be used for a profiled movement from start to finish,
+ * the intended usage is to filter a reference's dynamics based on
+ * ExponentialProfile velocity constraints. To compute the reference obeying
+ * this constraint, do the following.
+ *
+ * Initialization:
+ * @code{.cpp}
+ * ExponentialProfile::Constraints constraints{kMaxV, kV, kA};
+ * State previousProfiledReference = {initialReference, 0_mps};
+ * @endcode
+ *
+ * Run on update:
+ * @code{.cpp}
+ * previousProfiledReference = profile.Calculate(timeSincePreviousUpdate,
+ * previousProfiledReference, unprofiledReference);
+ * @endcode
+ *
+ * where `unprofiledReference` is free to change between calls. Note that when
+ * the unprofiled reference is within the constraints, `Calculate()` returns the
+ * unprofiled reference unchanged.
+ *
+ * Otherwise, a timer can be started to provide monotonic values for
+ * `Calculate()` and to determine when the profile has completed via
+ * `IsFinished()`.
+ */
+template <class Distance, class Input>
+class ExponentialProfile {
+ public:
+  using Distance_t = units::unit_t<Distance>;
+  using Velocity =
+      units::compound_unit<Distance, units::inverse<units::seconds>>;
+  using Velocity_t = units::unit_t<Velocity>;
+  using Acceleration =
+      units::compound_unit<Velocity, units::inverse<units::seconds>>;
+  using Input_t = units::unit_t<Input>;
+  using A_t = units::unit_t<units::inverse<units::seconds>>;
+  using B_t =
+      units::unit_t<units::compound_unit<Acceleration, units::inverse<Input>>>;
+  using KV = units::compound_unit<Input, units::inverse<Velocity>>;
+  using kV_t = units::unit_t<KV>;
+  using KA = units::compound_unit<Input, units::inverse<Acceleration>>;
+  using kA_t = units::unit_t<KA>;
+
+  class Constraints {
+   public:
+    Constraints(Input_t maxInput, A_t A, B_t B)
+        : maxInput{maxInput}, A{A}, B{B} {}
+    Constraints(Input_t maxInput, kV_t kV, kA_t kA)
+        : maxInput{maxInput}, A{-kV / kA}, B{1 / kA} {}
+    Velocity_t MaxVelocity() const { return -maxInput * B / A; }
+
+    Input_t maxInput{0};
+    A_t A{0};
+    B_t B{0};
+  };
+
+  class State {
+   public:
+    Distance_t position{0};
+    Velocity_t velocity{0};
+    bool operator==(const State&) const = default;
+  };
+
+  class ProfileTiming {
+   public:
+    units::second_t inflectionTime;
+    units::second_t totalTime;
+
+    bool IsFinished(const units::second_t& time) const {
+      return time > totalTime;
+    }
+  };
+
+  /**
+   * Construct a ExponentialProfile.
+   *
+   * @param constraints The constraints on the profile, like maximum input.
+   */
+  explicit ExponentialProfile(Constraints constraints);
+
+  ExponentialProfile(const ExponentialProfile&) = default;
+  ExponentialProfile& operator=(const ExponentialProfile&) = default;
+  ExponentialProfile(ExponentialProfile&&) = default;
+  ExponentialProfile& operator=(ExponentialProfile&&) = default;
+
+  /**
+   * Calculate the correct position and velocity for the profile at a time t
+   * where the current state is at time t = 0.
+   */
+  State Calculate(const units::second_t& t, const State& current,
+                  const State& goal) const;
+
+  /**
+   * Calculate the point after which the fastest way to reach the goal state is
+   * to apply input in the opposite direction.
+   */
+  State CalculateInflectionPoint(const State& current, const State& goal) const;
+
+  /**
+   * Calculate the time it will take for this profile to reach the goal state.
+   */
+  units::second_t TimeLeftUntil(const State& current, const State& goal) const;
+
+  /**
+   * Calculate the time it will take for this profile to reach the inflection
+   * point, and the time it will take for this profile to reach the goal state.
+   */
+  ProfileTiming CalculateProfileTiming(const State& current,
+                                       const State& goal) const;
+
+ private:
+  /**
+   * Calculate the point after which the fastest way to reach the goal state is
+   * to apply input in the opposite direction.
+   */
+  State CalculateInflectionPoint(const State& current, const State& goal,
+                                 const Input_t& input) const;
+
+  /**
+   * Calculate the time it will take for this profile to reach the inflection
+   * point, and the time it will take for this profile to reach the goal state.
+   */
+  ProfileTiming CalculateProfileTiming(const State& current,
+                                       const State& inflectionPoint,
+                                       const State& goal,
+                                       const Input_t& input) const;
+
+  /**
+   * Calculate the velocity reached after t seconds when applying an input from
+   * the initial state.
+   */
+  Velocity_t ComputeVelocityFromTime(const units::second_t& time,
+                                     const Input_t& input,
+                                     const State& initial) const;
+
+  /**
+   * Calculate the position reached after t seconds when applying an input from
+   * the initial state.
+   */
+  Distance_t ComputeDistanceFromTime(const units::second_t& time,
+                                     const Input_t& input,
+                                     const State& initial) const;
+
+  /**
+   * Calculate the distance reached at the same time as the given velocity when
+   * applying the given input from the initial state.
+   */
+  Distance_t ComputeDistanceFromVelocity(const Velocity_t& velocity,
+                                         const Input_t& input,
+                                         const State& initial) const;
+
+  /**
+   * Calculate the time required to reach a specified velocity given the initial
+   * velocity.
+   */
+  units::second_t ComputeTimeFromVelocity(const Velocity_t& velocity,
+                                          const Input_t& input,
+                                          const Velocity_t& initial) const;
+
+  /**
+   * Calculate the velocity at which input should be reversed in order to reach
+   * the goal state from the current state.
+   */
+  Velocity_t SolveForInflectionVelocity(const Input_t& input,
+                                        const State& current,
+                                        const State& goal) const;
+
+  /**
+   * Returns true if the profile should be inverted.
+   *
+   * <p>The profile is inverted if we should first apply negative input in order
+   * to reach the goal state.
+   */
+  bool ShouldFlipInput(const State& current, const State& goal) const;
+
+  Constraints m_constraints;
+};
+}  // namespace frc
+
+#include "ExponentialProfile.inc"
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.inc
new file mode 100644
index 0000000..ded5245
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.inc
@@ -0,0 +1,253 @@
+// 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 <algorithm>
+
+#include <fmt/core.h>
+
+#include "frc/trajectory/ExponentialProfile.h"
+#include "units/math.h"
+
+namespace frc {
+template <class Distance, class Input>
+ExponentialProfile<Distance, Input>::ExponentialProfile(Constraints constraints)
+    : m_constraints(constraints) {}
+
+template <class Distance, class Input>
+typename ExponentialProfile<Distance, Input>::State
+ExponentialProfile<Distance, Input>::Calculate(const units::second_t& t,
+                                               const State& current,
+                                               const State& goal) const {
+  auto direction = ShouldFlipInput(current, goal) ? -1 : 1;
+  auto u = direction * m_constraints.maxInput;
+
+  auto inflectionPoint = CalculateInflectionPoint(current, goal, u);
+  auto timing = CalculateProfileTiming(current, inflectionPoint, goal, u);
+
+  if (t < 0_s) {
+    return current;
+  } else if (t < timing.inflectionTime) {
+    return {ComputeDistanceFromTime(t, u, current),
+            ComputeVelocityFromTime(t, u, current)};
+  } else if (t < timing.totalTime) {
+    return {ComputeDistanceFromTime(t - timing.totalTime, -u, goal),
+            ComputeVelocityFromTime(t - timing.totalTime, -u, goal)};
+  } else {
+    return goal;
+  }
+}
+
+template <class Distance, class Input>
+typename ExponentialProfile<Distance, Input>::State
+ExponentialProfile<Distance, Input>::CalculateInflectionPoint(
+    const State& current, const State& goal) const {
+  auto direction = ShouldFlipInput(current, goal) ? -1 : 1;
+  auto u = direction * m_constraints.maxInput;
+
+  return CalculateInflectionPoint(current, goal, u);
+}
+
+template <class Distance, class Input>
+typename ExponentialProfile<Distance, Input>::State
+ExponentialProfile<Distance, Input>::CalculateInflectionPoint(
+    const State& current, const State& goal, const Input_t& input) const {
+  auto u = input;
+
+  if (current == goal) {
+    return current;
+  }
+
+  auto inflectionVelocity = SolveForInflectionVelocity(u, current, goal);
+  auto inflectionPosition =
+      ComputeDistanceFromVelocity(inflectionVelocity, -u, goal);
+
+  return {inflectionPosition, inflectionVelocity};
+}
+
+template <class Distance, class Input>
+units::second_t ExponentialProfile<Distance, Input>::TimeLeftUntil(
+    const State& current, const State& goal) const {
+  auto timing = CalculateProfileTiming(current, goal);
+
+  return timing.totalTime;
+}
+
+template <class Distance, class Input>
+typename ExponentialProfile<Distance, Input>::ProfileTiming
+ExponentialProfile<Distance, Input>::CalculateProfileTiming(
+    const State& current, const State& goal) const {
+  auto direction = ShouldFlipInput(current, goal) ? -1 : 1;
+  auto u = direction * m_constraints.maxInput;
+
+  auto inflectionPoint = CalculateInflectionPoint(current, goal, u);
+  return CalculateProfileTiming(current, inflectionPoint, goal, u);
+}
+
+template <class Distance, class Input>
+typename ExponentialProfile<Distance, Input>::ProfileTiming
+ExponentialProfile<Distance, Input>::CalculateProfileTiming(
+    const State& current, const State& inflectionPoint, const State& goal,
+    const Input_t& input) const {
+  auto u = input;
+  auto u_dir = units::math::abs(u) / u;
+
+  units::second_t inflectionT_forward;
+
+  // We need to handle 5 cases here:
+  //
+  // - Approaching -maxVelocity from below
+  // - Approaching -maxVelocity from above
+  // - Approaching maxVelocity from below
+  // - Approaching maxVelocity from above
+  // - At +-maxVelocity
+  //
+  // For cases 1 and 3, we want to subtract epsilon from the inflection point
+  // velocity For cases 2 and 4, we want to add epsilon to the inflection point
+  // velocity. For case 5, we have reached inflection point velocity.
+  auto epsilon = Velocity_t(1e-9);
+  if (units::math::abs(u_dir * m_constraints.MaxVelocity() -
+                       inflectionPoint.velocity) < epsilon) {
+    auto solvableV = inflectionPoint.velocity;
+    units::second_t t_to_solvable_v;
+    Distance_t x_at_solvable_v;
+    if (units::math::abs(current.velocity - inflectionPoint.velocity) <
+        epsilon) {
+      t_to_solvable_v = 0_s;
+      x_at_solvable_v = current.position;
+    } else {
+      if (units::math::abs(current.velocity) > m_constraints.MaxVelocity()) {
+        solvableV += u_dir * epsilon;
+      } else {
+        solvableV -= u_dir * epsilon;
+      }
+
+      t_to_solvable_v = ComputeTimeFromVelocity(solvableV, u, current.velocity);
+      x_at_solvable_v = ComputeDistanceFromVelocity(solvableV, u, current);
+    }
+
+    inflectionT_forward =
+        t_to_solvable_v + u_dir * (inflectionPoint.position - x_at_solvable_v) /
+                              m_constraints.MaxVelocity();
+  } else {
+    inflectionT_forward =
+        ComputeTimeFromVelocity(inflectionPoint.velocity, u, current.velocity);
+  }
+
+  auto inflectionT_backward =
+      ComputeTimeFromVelocity(inflectionPoint.velocity, -u, goal.velocity);
+
+  return {inflectionT_forward, inflectionT_forward - inflectionT_backward};
+}
+
+template <class Distance, class Input>
+typename ExponentialProfile<Distance, Input>::Distance_t
+ExponentialProfile<Distance, Input>::ComputeDistanceFromTime(
+    const units::second_t& time, const Input_t& input,
+    const State& initial) const {
+  auto A = m_constraints.A;
+  auto B = m_constraints.B;
+  auto u = input;
+
+  return initial.position +
+         (-B * u * time +
+          (initial.velocity + B * u / A) * (units::math::exp(A * time) - 1)) /
+             A;
+}
+
+template <class Distance, class Input>
+typename ExponentialProfile<Distance, Input>::Velocity_t
+ExponentialProfile<Distance, Input>::ComputeVelocityFromTime(
+    const units::second_t& time, const Input_t& input,
+    const State& initial) const {
+  auto A = m_constraints.A;
+  auto B = m_constraints.B;
+  auto u = input;
+
+  return (initial.velocity + B * u / A) * units::math::exp(A * time) -
+         B * u / A;
+}
+
+template <class Distance, class Input>
+units::second_t ExponentialProfile<Distance, Input>::ComputeTimeFromVelocity(
+    const Velocity_t& velocity, const Input_t& input,
+    const Velocity_t& initial) const {
+  auto A = m_constraints.A;
+  auto B = m_constraints.B;
+  auto u = input;
+
+  return units::math::log((A * velocity + B * u) / (A * initial + B * u)) / A;
+}
+
+template <class Distance, class Input>
+typename ExponentialProfile<Distance, Input>::Distance_t
+ExponentialProfile<Distance, Input>::ComputeDistanceFromVelocity(
+    const Velocity_t& velocity, const Input_t& input,
+    const State& initial) const {
+  auto A = m_constraints.A;
+  auto B = m_constraints.B;
+  auto u = input;
+
+  return initial.position + (velocity - initial.velocity) / A -
+         B * u / (A * A) *
+             units::math::log((A * velocity + B * u) /
+                              (A * initial.velocity + B * u));
+}
+
+template <class Distance, class Input>
+typename ExponentialProfile<Distance, Input>::Velocity_t
+ExponentialProfile<Distance, Input>::SolveForInflectionVelocity(
+    const Input_t& input, const State& current, const State& goal) const {
+  auto A = m_constraints.A;
+  auto B = m_constraints.B;
+  auto u = input;
+
+  auto u_dir = u / units::math::abs(u);
+
+  auto position_delta = goal.position - current.position;
+  auto velocity_delta = goal.velocity - current.velocity;
+
+  auto scalar = (A * current.velocity + B * u) * (A * goal.velocity - B * u);
+  auto power = -A / B / u * (A * position_delta - velocity_delta);
+
+  auto a = -A * A;
+  auto c = B * B * u * u + scalar * units::math::exp(power);
+
+  if (-1e-9 < c.value() && c.value() < 0) {
+    // numeric instability - the heuristic gets it right but c is around -1e-13
+    return Velocity_t(0);
+  }
+
+  return u_dir * units::math::sqrt(-c / a);
+}
+
+template <class Distance, class Input>
+bool ExponentialProfile<Distance, Input>::ShouldFlipInput(
+    const State& current, const State& goal) const {
+  auto u = m_constraints.maxInput;
+
+  auto v0 = current.velocity;
+  auto xf = goal.position;
+  auto vf = goal.velocity;
+
+  auto x_forward = ComputeDistanceFromVelocity(vf, u, current);
+  auto x_reverse = ComputeDistanceFromVelocity(vf, -u, current);
+
+  if (v0 >= m_constraints.MaxVelocity()) {
+    return xf < x_reverse;
+  }
+
+  if (v0 <= -m_constraints.MaxVelocity()) {
+    return xf < x_forward;
+  }
+
+  auto a = v0 >= Velocity_t(0);
+  auto b = vf >= Velocity_t(0);
+  auto c = xf >= x_forward;
+  auto d = xf >= x_reverse;
+
+  return (a && !d) || (b && !c) || (!c && !d);
+}
+}  // namespace frc
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/Trajectory.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/Trajectory.h
index f5ee79b..ca97593 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/Trajectory.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/Trajectory.h
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include <wpi/SymbolExports.h>
+#include <wpi/json_fwd.h>
 
 #include "frc/geometry/Pose2d.h"
 #include "frc/geometry/Transform2d.h"
@@ -15,10 +16,6 @@
 #include "units/time.h"
 #include "units/velocity.h"
 
-namespace wpi {
-class json;
-}  // namespace wpi
-
 namespace frc {
 /**
  * Represents a time-parameterized trajectory. The trajectory contains of
@@ -66,6 +63,8 @@
 
   /**
    * Constructs a trajectory from a vector of states.
+   *
+   * @throws std::invalid_argument if the vector of states is empty.
    */
   explicit Trajectory(const std::vector<State>& states);
 
@@ -77,6 +76,7 @@
 
   /**
    * Return the states of the trajectory.
+   *
    * @return The states of the trajectory.
    */
   const std::vector<State>& States() const { return m_states; }
@@ -86,6 +86,7 @@
    *
    * @param t The point in time since the beginning of the trajectory to sample.
    * @return The state at that point in time.
+   * @throws std::runtime_error if the trajectory has no states.
    */
   State Sample(units::second_t t) const;
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrajectoryConfig.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrajectoryConfig.h
index b1a0b52..4e6c1e6 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrajectoryConfig.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrajectoryConfig.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <concepts>
 #include <memory>
 #include <utility>
 #include <vector>
@@ -74,8 +75,7 @@
    * Adds a user-defined constraint to the trajectory.
    * @param constraint The user-defined constraint.
    */
-  template <typename Constraint, typename = std::enable_if_t<std::is_base_of_v<
-                                     TrajectoryConstraint, Constraint>>>
+  template <std::derived_from<TrajectoryConstraint> Constraint>
   void AddConstraint(Constraint constraint) {
     m_constraints.emplace_back(std::make_unique<Constraint>(constraint));
   }
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.h
index 24a8253..73aab38 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <wpi/deprecated.h>
+
 #include "units/time.h"
 #include "wpimath/MathShared.h"
 
@@ -21,13 +23,14 @@
  * @code{.cpp}
  * TrapezoidProfile::Constraints constraints{kMaxV, kMaxA};
  * double previousProfiledReference = initialReference;
+ * TrapezoidProfile profile{constraints};
  * @endcode
  *
  * Run on update:
  * @code{.cpp}
- * TrapezoidProfile profile{constraints, unprofiledReference,
- *                          previousProfiledReference};
- * previousProfiledReference = profile.Calculate(timeSincePreviousUpdate);
+ * previousProfiledReference = profile.Calculate(timeSincePreviousUpdate,
+ *                                               unprofiledReference,
+ *                                               previousProfiledReference);
  * @endcode
  *
  * where `unprofiledReference` is free to change between calls. Note that when
@@ -75,9 +78,22 @@
    * Construct a TrapezoidProfile.
    *
    * @param constraints The constraints on the profile, like maximum velocity.
+   */
+  TrapezoidProfile(Constraints constraints);  // NOLINT
+
+  /**
+   * Construct a TrapezoidProfile.
+   *
+   * @param constraints The constraints on the profile, like maximum velocity.
    * @param goal        The desired state when the profile is complete.
    * @param initial     The initial state (usually the current state).
+   * @deprecated Pass the desired and current state into calculate instead of
+   * constructing a new TrapezoidProfile with the desired and current state
    */
+  WPI_DEPRECATED(
+      "Pass the desired and current state into calculate instead of "
+      "constructing a new TrapezoidProfile with the desired and current "
+      "state")
   TrapezoidProfile(Constraints constraints, State goal,
                    State initial = State{Distance_t{0}, Velocity_t{0}});
 
@@ -91,10 +107,26 @@
    * where the beginning of the profile was at time t = 0.
    *
    * @param t The time since the beginning of the profile.
+   * @deprecated Pass the desired and current state into calculate instead of
+   * constructing a new TrapezoidProfile with the desired and current state
    */
+  [[deprecated(
+      "Pass the desired and current state into calculate instead of "
+      "constructing a new TrapezoidProfile with the desired and current "
+      "state")]]
   State Calculate(units::second_t t) const;
 
   /**
+   * Calculate the correct position and velocity for the profile at a time t
+   * where the beginning of the profile was at time t = 0.
+   *
+   * @param t The time since the beginning of the profile.
+   * @param goal        The desired state when the profile is complete.
+   * @param current     The initial state (usually the current state).
+   */
+  State Calculate(units::second_t t, State goal, State current);
+
+  /**
    * Returns the time left until a target distance in the profile is reached.
    *
    * @param target The target distance.
@@ -141,8 +173,9 @@
   int m_direction;
 
   Constraints m_constraints;
-  State m_initial;
-  State m_goal;
+  State m_current;
+  State m_goal;   // TODO: remove
+  bool m_newAPI;  // TODO: remove
 
   units::second_t m_endAccel;
   units::second_t m_endFullSpeed;
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.inc b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.inc
index 19eb1f3..24e0a46 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.inc
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.inc
@@ -11,21 +11,26 @@
 
 namespace frc {
 template <class Distance>
+TrapezoidProfile<Distance>::TrapezoidProfile(Constraints constraints)
+    : m_constraints(constraints), m_newAPI(true) {}
+
+template <class Distance>
 TrapezoidProfile<Distance>::TrapezoidProfile(Constraints constraints,
                                              State goal, State initial)
     : m_direction{ShouldFlipAcceleration(initial, goal) ? -1 : 1},
       m_constraints(constraints),
-      m_initial(Direct(initial)),
-      m_goal(Direct(goal)) {
-  if (m_initial.velocity > m_constraints.maxVelocity) {
-    m_initial.velocity = m_constraints.maxVelocity;
+      m_current(Direct(initial)),
+      m_goal(Direct(goal)),
+      m_newAPI(false) {
+  if (m_current.velocity > m_constraints.maxVelocity) {
+    m_current.velocity = m_constraints.maxVelocity;
   }
 
   // Deal with a possibly truncated motion profile (with nonzero initial or
   // final velocity) by calculating the parameters as if the profile began and
   // ended at zero velocity
   units::second_t cutoffBegin =
-      m_initial.velocity / m_constraints.maxAcceleration;
+      m_current.velocity / m_constraints.maxAcceleration;
   Distance_t cutoffDistBegin =
       cutoffBegin * cutoffBegin * m_constraints.maxAcceleration / 2.0;
 
@@ -37,7 +42,7 @@
   // of a truncated one
 
   Distance_t fullTrapezoidDist =
-      cutoffDistBegin + (m_goal.position - m_initial.position) + cutoffDistEnd;
+      cutoffDistBegin + (m_goal.position - m_current.position) + cutoffDistEnd;
   units::second_t accelerationTime =
       m_constraints.maxVelocity / m_constraints.maxAcceleration;
 
@@ -60,15 +65,19 @@
 template <class Distance>
 typename TrapezoidProfile<Distance>::State
 TrapezoidProfile<Distance>::Calculate(units::second_t t) const {
-  State result = m_initial;
+  if (m_newAPI) {
+    throw std::runtime_error(
+        "Cannot use new constructor with deprecated Calculate()");
+  }
+  State result = m_current;
 
   if (t < m_endAccel) {
     result.velocity += t * m_constraints.maxAcceleration;
     result.position +=
-        (m_initial.velocity + t * m_constraints.maxAcceleration / 2.0) * t;
+        (m_current.velocity + t * m_constraints.maxAcceleration / 2.0) * t;
   } else if (t < m_endFullSpeed) {
     result.velocity = m_constraints.maxVelocity;
-    result.position += (m_initial.velocity +
+    result.position += (m_current.velocity +
                         m_endAccel * m_constraints.maxAcceleration / 2.0) *
                            m_endAccel +
                        m_constraints.maxVelocity * (t - m_endAccel);
@@ -86,12 +95,83 @@
 
   return Direct(result);
 }
+template <class Distance>
+typename TrapezoidProfile<Distance>::State
+TrapezoidProfile<Distance>::Calculate(units::second_t t, State goal,
+                                      State current) {
+  m_direction = ShouldFlipAcceleration(current, goal) ? -1 : 1;
+  m_current = Direct(current);
+  goal = Direct(goal);
+  if (m_current.velocity > m_constraints.maxVelocity) {
+    m_current.velocity = m_constraints.maxVelocity;
+  }
+
+  // Deal with a possibly truncated motion profile (with nonzero initial or
+  // final velocity) by calculating the parameters as if the profile began and
+  // ended at zero velocity
+  units::second_t cutoffBegin =
+      m_current.velocity / m_constraints.maxAcceleration;
+  Distance_t cutoffDistBegin =
+      cutoffBegin * cutoffBegin * m_constraints.maxAcceleration / 2.0;
+
+  units::second_t cutoffEnd = goal.velocity / m_constraints.maxAcceleration;
+  Distance_t cutoffDistEnd =
+      cutoffEnd * cutoffEnd * m_constraints.maxAcceleration / 2.0;
+
+  // Now we can calculate the parameters as if it was a full trapezoid instead
+  // of a truncated one
+
+  Distance_t fullTrapezoidDist =
+      cutoffDistBegin + (goal.position - m_current.position) + cutoffDistEnd;
+  units::second_t accelerationTime =
+      m_constraints.maxVelocity / m_constraints.maxAcceleration;
+
+  Distance_t fullSpeedDist =
+      fullTrapezoidDist -
+      accelerationTime * accelerationTime * m_constraints.maxAcceleration;
+
+  // Handle the case where the profile never reaches full speed
+  if (fullSpeedDist < Distance_t{0}) {
+    accelerationTime =
+        units::math::sqrt(fullTrapezoidDist / m_constraints.maxAcceleration);
+    fullSpeedDist = Distance_t{0};
+  }
+
+  m_endAccel = accelerationTime - cutoffBegin;
+  m_endFullSpeed = m_endAccel + fullSpeedDist / m_constraints.maxVelocity;
+  m_endDeccel = m_endFullSpeed + accelerationTime - cutoffEnd;
+  State result = m_current;
+
+  if (t < m_endAccel) {
+    result.velocity += t * m_constraints.maxAcceleration;
+    result.position +=
+        (m_current.velocity + t * m_constraints.maxAcceleration / 2.0) * t;
+  } else if (t < m_endFullSpeed) {
+    result.velocity = m_constraints.maxVelocity;
+    result.position += (m_current.velocity +
+                        m_endAccel * m_constraints.maxAcceleration / 2.0) *
+                           m_endAccel +
+                       m_constraints.maxVelocity * (t - m_endAccel);
+  } else if (t <= m_endDeccel) {
+    result.velocity =
+        goal.velocity + (m_endDeccel - t) * m_constraints.maxAcceleration;
+    units::second_t timeLeft = m_endDeccel - t;
+    result.position =
+        goal.position -
+        (goal.velocity + timeLeft * m_constraints.maxAcceleration / 2.0) *
+            timeLeft;
+  } else {
+    result = goal;
+  }
+
+  return Direct(result);
+}
 
 template <class Distance>
 units::second_t TrapezoidProfile<Distance>::TimeLeftUntil(
     Distance_t target) const {
-  Distance_t position = m_initial.position * m_direction;
-  Velocity_t velocity = m_initial.velocity * m_direction;
+  Distance_t position = m_current.position * m_direction;
+  Velocity_t velocity = m_current.velocity * m_direction;
 
   units::second_t endAccel = m_endAccel * m_direction;
   units::second_t endFullSpeed = m_endFullSpeed * m_direction - endAccel;
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/DifferentialDriveKinematicsConstraint.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/DifferentialDriveKinematicsConstraint.h
index 0edd8cc..0d96893 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/DifferentialDriveKinematicsConstraint.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/DifferentialDriveKinematicsConstraint.h
@@ -20,9 +20,8 @@
 class WPILIB_DLLEXPORT DifferentialDriveKinematicsConstraint
     : public TrajectoryConstraint {
  public:
-  DifferentialDriveKinematicsConstraint(
-      const DifferentialDriveKinematics& kinematics,
-      units::meters_per_second_t maxSpeed);
+  DifferentialDriveKinematicsConstraint(DifferentialDriveKinematics kinematics,
+                                        units::meters_per_second_t maxSpeed);
 
   units::meters_per_second_t MaxVelocity(
       const Pose2d& pose, units::curvature_t curvature,
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/DifferentialDriveVoltageConstraint.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/DifferentialDriveVoltageConstraint.h
index 40a0d8e..8f7ba2c 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/DifferentialDriveVoltageConstraint.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/DifferentialDriveVoltageConstraint.h
@@ -34,7 +34,7 @@
    */
   DifferentialDriveVoltageConstraint(
       const SimpleMotorFeedforward<units::meter>& feedforward,
-      const DifferentialDriveKinematics& kinematics, units::volt_t maxVoltage);
+      DifferentialDriveKinematics kinematics, units::volt_t maxVoltage);
 
   units::meters_per_second_t MaxVelocity(
       const Pose2d& pose, units::curvature_t curvature,
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/EllipticalRegionConstraint.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/EllipticalRegionConstraint.h
index f9f0d2e..74f3c55 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/EllipticalRegionConstraint.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/EllipticalRegionConstraint.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <concepts>
 #include <limits>
 
 #include "frc/geometry/Rotation2d.h"
@@ -15,8 +16,7 @@
 /**
  * Enforces a particular constraint only within an elliptical region.
  */
-template <typename Constraint, typename = std::enable_if_t<std::is_base_of_v<
-                                   TrajectoryConstraint, Constraint>>>
+template <std::derived_from<TrajectoryConstraint> Constraint>
 class EllipticalRegionConstraint : public TrajectoryConstraint {
  public:
   /**
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/RectangularRegionConstraint.h b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/RectangularRegionConstraint.h
index 18522fe..f3b364b 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/RectangularRegionConstraint.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/frc/trajectory/constraint/RectangularRegionConstraint.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <concepts>
 #include <limits>
 
 #include "frc/geometry/Rotation2d.h"
@@ -14,8 +15,7 @@
 /**
  * Enforces a particular constraint only within a rectangular region.
  */
-template <typename Constraint, typename = std::enable_if_t<std::is_base_of_v<
-                                   TrajectoryConstraint, Constraint>>>
+template <std::derived_from<TrajectoryConstraint> Constraint>
 class RectangularRegionConstraint : public TrajectoryConstraint {
  public:
   /**
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/units/angular_acceleration.h b/third_party/allwpilib/wpimath/src/main/native/include/units/angular_acceleration.h
index 632982b..8174dac 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/units/angular_acceleration.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/units/angular_acceleration.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#include "units/angular_velocity.h"
+#include "units/angle.h"
 #include "units/base.h"
 #include "units/time.h"
 
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/units/angular_jerk.h b/third_party/allwpilib/wpimath/src/main/native/include/units/angular_jerk.h
new file mode 100644
index 0000000..b58bc16
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/units/angular_jerk.h
@@ -0,0 +1,34 @@
+// 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 "units/angle.h"
+#include "units/base.h"
+#include "units/time.h"
+
+namespace units {
+/**
+ * @namespace units::angular_jerk
+ * @brief namespace for unit types and containers representing angular
+ *        jerk values
+ * @details The SI unit for angular jerk is
+ *          `radians_per_second_cubed`, and the corresponding `base_unit`
+ *          category is`angular_jerk_unit`.
+ * @anchor angularJerkContainers
+ * @sa See unit_t for more information on unit type containers.
+ */
+UNIT_ADD(angular_jerk, radians_per_second_cubed, radians_per_second_cubed,
+         rad_per_s_cu, unit<std::ratio<1>, units::category::angular_jerk_unit>)
+UNIT_ADD(angular_jerk, degrees_per_second_cubed, degrees_per_second_cubed,
+         deg_per_s_cu,
+         compound_unit<angle::degrees, inverse<cubed<time::seconds>>>)
+UNIT_ADD(angular_jerk, turns_per_second_cubed, turns_per_second_cubed,
+         tr_per_s_cu,
+         compound_unit<angle::turns, inverse<cubed<time::seconds>>>)
+
+UNIT_ADD_CATEGORY_TRAIT(angular_jerk)
+
+using namespace angular_jerk;
+}  // namespace units
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/units/base.h b/third_party/allwpilib/wpimath/src/main/native/include/units/base.h
index 9d021b1..34cd59e 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/units/base.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/units/base.h
@@ -76,7 +76,7 @@
 	#include <locale>
 	#include <string>
 #endif
-#if !defined(UNIT_LIB_DISABLE_FMT)
+#if __has_include(<fmt/format.h>) && !defined(UNIT_LIB_DISABLE_FMT)
 	#include <locale>
 	#include <string>
 	#include <fmt/format.h>
@@ -176,7 +176,7 @@
  * @param		abbrev - abbreviated unit name, e.g. 'm'
  * @note		When UNIT_LIB_ENABLE_IOSTREAM isn't defined, the macro does not generate any code
  */
-#if !defined(UNIT_LIB_DISABLE_FMT)
+#if __has_include(<fmt/format.h>) && !defined(UNIT_LIB_DISABLE_FMT)
 	#define UNIT_ADD_IO(namespaceName, nameSingular, abbrev)\
 	}\
 	template <>\
@@ -463,13 +463,19 @@
 	//----------------------------------
 
 	/**
+	 * @defgroup 	Units Unit API
+	*/
+
+	/**
 	 * @defgroup	UnitContainers Unit Containers
+	 * @ingroup		Units
 	 * @brief		Defines a series of classes which contain dimensioned values. Unit containers
 	 *				store a value, and support various arithmetic operations.
 	 */
 
 	/**
 	 * @defgroup	UnitTypes Unit Types
+	 * @ingroup		Units
 	 * @brief		Defines a series of classes which represent units. These types are tags used by
 	 *				the conversion function, to create compound units, or to create `unit_t` types.
 	 *				By themselves, they are not containers and have no stored value.
@@ -477,6 +483,7 @@
 
 	/**
 	 * @defgroup	UnitManipulators Unit Manipulators
+	 * @ingroup		Units
 	 * @brief		Defines a series of classes used to manipulate unit types, such as `inverse<>`, `squared<>`, and metric prefixes.
 	 *				Unit manipulators can be chained together, e.g. `inverse<squared<pico<time::seconds>>>` to
 	 *				represent picoseconds^-2.
@@ -484,6 +491,7 @@
 
 	 /**
 	  * @defgroup	CompileTimeUnitManipulators Compile-time Unit Manipulators
+	  * @ingroup	Units
 	  * @brief		Defines a series of classes used to manipulate `unit_value_t` types at compile-time, such as `unit_value_add<>`, `unit_value_sqrt<>`, etc.
 	  *				Compile-time manipulators can be chained together, e.g. `unit_value_sqrt<unit_value_add<unit_value_power<a, 2>, unit_value_power<b, 2>>>` to
 	  *				represent `c = sqrt(a^2 + b^2).
@@ -491,17 +499,20 @@
 
 	 /**
 	 * @defgroup	UnitMath Unit Math
+	 * @ingroup		Units
 	 * @brief		Defines a collection of unit-enabled, strongly-typed versions of `<cmath>` functions.
 	 * @details		Includes most c++11 extensions.
 	 */
 
 	/**
 	 * @defgroup	Conversion Explicit Conversion
+	 * @ingroup		Units
 	 * @brief		Functions used to convert values of one logical type to another.
 	 */
 
 	/**
 	 * @defgroup	TypeTraits Type Traits
+	 * @ingroup		Units
 	 * @brief		Defines a series of classes to obtain unit type information at compile-time.
 	 */
 
@@ -810,6 +821,7 @@
 		typedef base_unit<detail::meter_ratio<0>,	std::ratio<0>,	std::ratio<-1>,	std::ratio<1>>																						angular_velocity_unit;			///< Represents an SI derived unit of angular velocity
 		typedef base_unit<detail::meter_ratio<1>,	std::ratio<0>,	std::ratio<-2>>																										acceleration_unit;				///< Represents an SI derived unit of acceleration
 		typedef base_unit<detail::meter_ratio<0>,	std::ratio<0>,	std::ratio<-2>,	std::ratio<1>>																						angular_acceleration_unit;			///< Represents an SI derived unit of angular acceleration
+		typedef base_unit<detail::meter_ratio<0>,	std::ratio<0>,	std::ratio<-3>,	std::ratio<1>>																						angular_jerk_unit;			    ///< Represents an SI derived unit of angular jerk
 		typedef base_unit<detail::meter_ratio<1>,	std::ratio<1>,	std::ratio<-2>>																										force_unit;						///< Represents an SI derived unit of force
 		typedef base_unit<detail::meter_ratio<-1>,	std::ratio<1>,	std::ratio<-2>>																										pressure_unit;					///< Represents an SI derived unit of pressure
 		typedef base_unit<detail::meter_ratio<0>,	std::ratio<0>,	std::ratio<1>,	std::ratio<0>,	std::ratio<1>>																		charge_unit;					///< Represents an SI derived unit of charge
@@ -1394,7 +1406,7 @@
 	 *					error. This value should be chosen to be as high as possible before
 	 *					integer overflow errors occur in the compiler.
 	 * @note		USE WITH CAUTION. The is an approximate value. In general, squared<sqrt<meter>> != meter,
-	 *				i.e. the operation is not reversible, and it will result in propogated approximations.
+	 *				i.e. the operation is not reversible, and it will result in propagated approximations.
 	 *				Use only when absolutely necessary.
 	 */
 	template<class U, std::intmax_t Eps = 10000000000>
@@ -1758,7 +1770,7 @@
 #ifdef FOR_DOXYGEN_PURPOSOES_ONLY
 		/**
 		* @ingroup		TypeTraits
-		* @brief		Trait for accessing the publically defined types of `units::unit_t`
+		* @brief		Trait for accessing the publicly defined types of `units::unit_t`
 		* @details		The units library determines certain properties of the unit_t types passed to them
 		*				and what they represent by using the members of the corresponding unit_t_traits instantiation.
 		*/
@@ -1788,7 +1800,7 @@
 
 		/**
 		 * @ingroup		TypeTraits
-		 * @brief		Trait for accessing the publically defined types of `units::unit_t`
+		 * @brief		Trait for accessing the publicly defined types of `units::unit_t`
 		 * @details
 		 */
 		template<typename T>
@@ -2875,7 +2887,7 @@
 	}
 #endif
 }
-#if !defined(UNIT_LIB_DISABLE_FMT)
+#if __has_include(<fmt/format.h>) && !defined(UNIT_LIB_DISABLE_FMT)
 template <>
 struct fmt::formatter<units::dimensionless::dB_t> : fmt::formatter<double>
 {
@@ -2972,7 +2984,7 @@
 #ifdef FOR_DOXYGEN_PURPOSES_ONLY
 		/**
 		* @ingroup		TypeTraits
-		* @brief		Trait for accessing the publically defined types of `units::unit_value_t_traits`
+		* @brief		Trait for accessing the publicly defined types of `units::unit_value_t_traits`
 		* @details		The units library determines certain properties of the `unit_value_t` types passed to
 		*				them and what they represent by using the members of the corresponding `unit_value_t_traits`
 		*				instantiation.
@@ -2999,7 +3011,7 @@
 
 		/**
 		 * @ingroup		TypeTraits
-		 * @brief		Trait for accessing the publically defined types of `units::unit_value_t_traits`
+		 * @brief		Trait for accessing the publicly defined types of `units::unit_value_t_traits`
 		 * @details
 		 */
 		template<typename T>
@@ -3440,6 +3452,6 @@
 using namespace units::literals;
 #endif  // UNIT_HAS_LITERAL_SUPPORT
 
-#if !defined(UNIT_LIB_DISABLE_FMT)
-#include "frc/fmt/Units.h"
+#if __has_include(<fmt/format.h>) && !defined(UNIT_LIB_DISABLE_FMT)
+#include "units/formatter.h"
 #endif
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/units/formatter.h b/third_party/allwpilib/wpimath/src/main/native/include/units/formatter.h
new file mode 100644
index 0000000..1c17b0a
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/native/include/units/formatter.h
@@ -0,0 +1,219 @@
+// 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 <type_traits>
+
+#include <fmt/format.h>
+
+#include "units/base.h"
+
+// FIXME: Replace enable_if with requires clause and remove <type_traits>
+// include once using GCC >= 12. GCC 11 incorrectly emits a struct redefinition
+// error because it doesn't use the requires clause to disambiguate.
+
+/**
+ * Formatter for unit types.
+ */
+template <typename Unit, typename CharT>
+struct fmt::formatter<Unit, CharT,
+                      std::enable_if_t<units::traits::is_unit_t_v<Unit>>> {
+  constexpr auto parse(fmt::format_parse_context& ctx) {
+    return m_underlying.parse(ctx);
+  }
+
+  /**
+   * Writes out a formatted unit.
+   *
+   * @param obj Unit instance.
+   * @param ctx Format string context.
+   */
+  auto format(const Unit& obj, fmt::format_context& ctx) const {
+    using Units = typename Unit::unit_type;
+    using BaseUnits =
+        units::unit<std::ratio<1>,
+                    typename units::traits::unit_traits<Units>::base_unit_type>;
+
+    auto out = ctx.out();
+
+    out = m_underlying.format(units::convert<Units, BaseUnits>(obj()), ctx);
+
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::meter_ratio::num != 0) {
+      out = fmt::format_to(out, " m");
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::meter_ratio::num != 0 &&
+                  units::traits::unit_traits<
+                      Units>::base_unit_type::meter_ratio::num != 1) {
+      out = fmt::format_to(
+          out, "^{}",
+          units::traits::unit_traits<Units>::base_unit_type::meter_ratio::num);
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::meter_ratio::den != 1) {
+      out = fmt::format_to(
+          out, "/{}",
+          units::traits::unit_traits<Units>::base_unit_type::meter_ratio::den);
+    }
+
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::kilogram_ratio::num != 0) {
+      out = fmt::format_to(out, " kg");
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::kilogram_ratio::num != 0 &&
+                  units::traits::unit_traits<
+                      Units>::base_unit_type::kilogram_ratio::num != 1) {
+      out = fmt::format_to(out, "^{}",
+                           units::traits::unit_traits<
+                               Units>::base_unit_type::kilogram_ratio::num);
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::kilogram_ratio::den != 1) {
+      out = fmt::format_to(out, "/{}",
+                           units::traits::unit_traits<
+                               Units>::base_unit_type::kilogram_ratio::den);
+    }
+
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::second_ratio::num != 0) {
+      out = fmt::format_to(out, " s");
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::second_ratio::num != 0 &&
+                  units::traits::unit_traits<
+                      Units>::base_unit_type::second_ratio::num != 1) {
+      out = fmt::format_to(
+          out, "^{}",
+          units::traits::unit_traits<Units>::base_unit_type::second_ratio::num);
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::second_ratio::den != 1) {
+      out = fmt::format_to(
+          out, "/{}",
+          units::traits::unit_traits<Units>::base_unit_type::second_ratio::den);
+    }
+
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::ampere_ratio::num != 0) {
+      out = fmt::format_to(out, " A");
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::ampere_ratio::num != 0 &&
+                  units::traits::unit_traits<
+                      Units>::base_unit_type::ampere_ratio::num != 1) {
+      out = fmt::format_to(
+          out, "^{}",
+          units::traits::unit_traits<Units>::base_unit_type::ampere_ratio::num);
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::ampere_ratio::den != 1) {
+      out = fmt::format_to(
+          out, "/{}",
+          units::traits::unit_traits<Units>::base_unit_type::ampere_ratio::den);
+    }
+
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::kelvin_ratio::num != 0) {
+      out = fmt::format_to(out, " K");
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::kelvin_ratio::num != 0 &&
+                  units::traits::unit_traits<
+                      Units>::base_unit_type::kelvin_ratio::num != 1) {
+      out = fmt::format_to(
+          out, "^{}",
+          units::traits::unit_traits<Units>::base_unit_type::kelvin_ratio::num);
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::kelvin_ratio::den != 1) {
+      out = fmt::format_to(
+          out, "/{}",
+          units::traits::unit_traits<Units>::base_unit_type::kelvin_ratio::den);
+    }
+
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::mole_ratio::num != 0) {
+      out = fmt::format_to(out, " mol");
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::mole_ratio::num != 0 &&
+                  units::traits::unit_traits<
+                      Units>::base_unit_type::mole_ratio::num != 1) {
+      out = fmt::format_to(
+          out, "^{}",
+          units::traits::unit_traits<Units>::base_unit_type::mole_ratio::num);
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::mole_ratio::den != 1) {
+      out = fmt::format_to(
+          out, "/{}",
+          units::traits::unit_traits<Units>::base_unit_type::mole_ratio::den);
+    }
+
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::candela_ratio::num != 0) {
+      out = fmt::format_to(out, " cd");
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::candela_ratio::num != 0 &&
+                  units::traits::unit_traits<
+                      Units>::base_unit_type::candela_ratio::num != 1) {
+      out = fmt::format_to(out, "^{}",
+                           units::traits::unit_traits<
+                               Units>::base_unit_type::candela_ratio::num);
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::candela_ratio::den != 1) {
+      out = fmt::format_to(out, "/{}",
+                           units::traits::unit_traits<
+                               Units>::base_unit_type::candela_ratio::den);
+    }
+
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::radian_ratio::num != 0) {
+      out = fmt::format_to(out, " rad");
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::radian_ratio::num != 0 &&
+                  units::traits::unit_traits<
+                      Units>::base_unit_type::radian_ratio::num != 1) {
+      out = fmt::format_to(
+          out, "^{}",
+          units::traits::unit_traits<Units>::base_unit_type::radian_ratio::num);
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::radian_ratio::den != 1) {
+      out = fmt::format_to(
+          out, "/{}",
+          units::traits::unit_traits<Units>::base_unit_type::radian_ratio::den);
+    }
+
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::byte_ratio::num != 0) {
+      out = fmt::format_to(out, " b");
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::byte_ratio::num != 0 &&
+                  units::traits::unit_traits<
+                      Units>::base_unit_type::byte_ratio::num != 1) {
+      out = fmt::format_to(
+          out, "^{}",
+          units::traits::unit_traits<Units>::base_unit_type::byte_ratio::num);
+    }
+    if constexpr (units::traits::unit_traits<
+                      Units>::base_unit_type::byte_ratio::den != 1) {
+      out = fmt::format_to(
+          out, "/{}",
+          units::traits::unit_traits<Units>::base_unit_type::byte_ratio::den);
+    }
+
+    return out;
+  }
+
+ private:
+  fmt::formatter<typename Unit::underlying_type, CharT> m_underlying;
+};
diff --git a/third_party/allwpilib/wpimath/src/main/native/include/wpimath/MathShared.h b/third_party/allwpilib/wpimath/src/main/native/include/wpimath/MathShared.h
index 3b9b3cd..8e0698d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/include/wpimath/MathShared.h
+++ b/third_party/allwpilib/wpimath/src/main/native/include/wpimath/MathShared.h
@@ -9,6 +9,8 @@
 #include <fmt/format.h>
 #include <wpi/SymbolExports.h>
 
+#include "units/time.h"
+
 namespace wpi::math {
 
 enum class MathUsageId {
@@ -31,6 +33,7 @@
   virtual void ReportWarningV(fmt::string_view format,
                               fmt::format_args args) = 0;
   virtual void ReportUsage(MathUsageId id, int count) = 0;
+  virtual units::second_t GetTimestamp() = 0;
 
   template <typename S, typename... Args>
   inline void ReportError(const S& format, Args&&... args) {
@@ -70,6 +73,10 @@
   static void ReportUsage(MathUsageId id, int count) {
     GetMathShared().ReportUsage(id, count);
   }
+
+  static units::second_t GetTimestamp() {
+    return GetMathShared().GetTimestamp();
+  }
 };
 
 }  // namespace wpi::math
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_assert.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_assert.h
deleted file mode 100644
index 47097ed..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_assert.h
+++ /dev/null
@@ -1,162 +0,0 @@
-#pragma once
-
-#include <type_traits>
-
-/// @file
-/// Provides Drake's assertion implementation.  This is intended to be used
-/// both within Drake and by other software.  Drake's asserts can be armed
-/// and disarmed independently from the system-wide asserts.
-
-#ifdef DRAKE_DOXYGEN_CXX
-/// @p DRAKE_ASSERT(condition) is similar to the built-in @p assert(condition)
-/// from the C++ system header @p <cassert>.  Unless Drake's assertions are
-/// disarmed by the pre-processor definitions listed below, @p DRAKE_ASSERT
-/// will evaluate @p condition and iff the value is false will trigger an
-/// assertion failure with a message showing at least the condition text,
-/// function name, file, and line.
-///
-/// By default, assertion failures will :abort() the program.  However, when
-/// using the pydrake python bindings, assertion failures will instead throw a
-/// C++ exception that causes a python SystemExit exception.
-///
-/// Assertions are enabled or disabled using the following pre-processor macros:
-///
-/// - If @p DRAKE_ENABLE_ASSERTS is defined, then @p DRAKE_ASSERT is armed.
-/// - If @p DRAKE_DISABLE_ASSERTS is defined, then @p DRAKE_ASSERT is disarmed.
-/// - If both macros are defined, then it is a compile-time error.
-/// - If neither are defined, then NDEBUG governs assertions as usual.
-///
-/// This header will define exactly one of either @p DRAKE_ASSERT_IS_ARMED or
-/// @p DRAKE_ASSERT_IS_DISARMED to indicate whether @p DRAKE_ASSERT is armed.
-///
-/// This header will define both `constexpr bool drake::kDrakeAssertIsArmed`
-/// and `constexpr bool drake::kDrakeAssertIsDisarmed` globals.
-///
-/// One difference versus the standard @p assert(condition) is that the
-/// @p condition within @p DRAKE_ASSERT is always syntax-checked, even if
-/// Drake's assertions are disarmed.
-///
-/// Treat @p DRAKE_ASSERT like a statement -- it must always be used
-/// in block scope, and must always be followed by a semicolon.
-#define DRAKE_ASSERT(condition)
-/// Like @p DRAKE_ASSERT, except that the expression must be void-valued; this
-/// allows for guarding expensive assertion-checking subroutines using the same
-/// macros as stand-alone assertions.
-#define DRAKE_ASSERT_VOID(expression)
-/// Evaluates @p condition and iff the value is false will trigger an assertion
-/// failure with a message showing at least the condition text, function name,
-/// file, and line.
-#define DRAKE_DEMAND(condition)
-/// Silences a "no return value" compiler warning by calling a function that
-/// always raises an exception or aborts (i.e., a function marked noreturn).
-/// Only use this macro at a point where (1) a point in the code is truly
-/// unreachable, (2) the fact that it's unreachable is knowable from only
-/// reading the function itself (and not, e.g., some larger design invariant),
-/// and (3) there is a compiler warning if this macro were removed.  The most
-/// common valid use is with a switch-case-return block where all cases are
-/// accounted for but the enclosing function is supposed to return a value.  Do
-/// *not* use this macro as a "logic error" assertion; it should *only* be used
-/// to silence false positive warnings.  When in doubt, throw an exception
-/// manually instead of using this macro.
-#define DRAKE_UNREACHABLE()
-#else  //  DRAKE_DOXYGEN_CXX
-
-// Users should NOT set these; only this header should set them.
-#ifdef DRAKE_ASSERT_IS_ARMED
-# error Unexpected DRAKE_ASSERT_IS_ARMED defined.
-#endif
-#ifdef DRAKE_ASSERT_IS_DISARMED
-# error Unexpected DRAKE_ASSERT_IS_DISARMED defined.
-#endif
-
-// Decide whether Drake assertions are enabled.
-#if defined(DRAKE_ENABLE_ASSERTS) && defined(DRAKE_DISABLE_ASSERTS)
-# error Conflicting assertion toggles.
-#elif defined(DRAKE_ENABLE_ASSERTS)
-# define DRAKE_ASSERT_IS_ARMED
-#elif defined(DRAKE_DISABLE_ASSERTS) || defined(NDEBUG)
-# define DRAKE_ASSERT_IS_DISARMED
-#else
-# define DRAKE_ASSERT_IS_ARMED
-#endif
-
-namespace drake {
-namespace internal {
-// Abort the program with an error message.
-[[noreturn]] void Abort(const char* condition, const char* func,
-                        const char* file, int line);
-// Report an assertion failure; will either Abort(...) or throw.
-[[noreturn]] void AssertionFailed(const char* condition, const char* func,
-                                  const char* file, int line);
-}  // namespace internal
-namespace assert {
-// Allows for specialization of how to bool-convert Conditions used in
-// assertions, in case they are not intrinsically convertible.  See
-// common/symbolic/expression/formula.h for an example use.  This is a public
-// interface to extend; it is intended to be specialized by unusual Scalar
-// types that require special handling.
-template <typename Condition>
-struct ConditionTraits {
-  static constexpr bool is_valid = std::is_convertible_v<Condition, bool>;
-  static bool Evaluate(const Condition& value) {
-    return value;
-  }
-};
-}  // namespace assert
-}  // namespace drake
-
-#define DRAKE_UNREACHABLE()                                             \
-  ::drake::internal::Abort(                                             \
-      "Unreachable code was reached?!", __func__, __FILE__, __LINE__)
-
-#define DRAKE_DEMAND(condition)                                              \
-  do {                                                                       \
-    typedef ::drake::assert::ConditionTraits<                                \
-        typename std::remove_cv_t<decltype(condition)>> Trait;               \
-    static_assert(Trait::is_valid, "Condition should be bool-convertible."); \
-    static_assert(                                                           \
-        !std::is_pointer_v<decltype(condition)>,                             \
-        "When using DRAKE_DEMAND on a raw pointer, always write out "        \
-        "DRAKE_DEMAND(foo != nullptr), do not write DRAKE_DEMAND(foo) "      \
-        "and rely on implicit pointer-to-bool conversion.");                 \
-    if (!Trait::Evaluate(condition)) {                                       \
-      ::drake::internal::AssertionFailed(                                    \
-           #condition, __func__, __FILE__, __LINE__);                        \
-    }                                                                        \
-  } while (0)
-
-#ifdef DRAKE_ASSERT_IS_ARMED
-// Assertions are enabled.
-namespace drake {
-constexpr bool kDrakeAssertIsArmed = true;
-constexpr bool kDrakeAssertIsDisarmed = false;
-}  // namespace drake
-# define DRAKE_ASSERT(condition) DRAKE_DEMAND(condition)
-# define DRAKE_ASSERT_VOID(expression) do {                     \
-    static_assert(                                              \
-        std::is_convertible_v<decltype(expression), void>,      \
-        "Expression should be void.");                          \
-    expression;                                                 \
-  } while (0)
-#else
-// Assertions are disabled, so just typecheck the expression.
-namespace drake {
-constexpr bool kDrakeAssertIsArmed = false;
-constexpr bool kDrakeAssertIsDisarmed = true;
-}  // namespace drake
-# define DRAKE_ASSERT(condition) do {                                        \
-    typedef ::drake::assert::ConditionTraits<                                \
-        typename std::remove_cv_t<decltype(condition)>> Trait;               \
-    static_assert(Trait::is_valid, "Condition should be bool-convertible."); \
-    static_assert(                                                           \
-        !std::is_pointer_v<decltype(condition)>,                             \
-        "When using DRAKE_ASSERT on a raw pointer, always write out "        \
-        "DRAKE_ASSERT(foo != nullptr), do not write DRAKE_ASSERT(foo) "      \
-        "and rely on implicit pointer-to-bool conversion.");                 \
-  } while (0)
-# define DRAKE_ASSERT_VOID(expression) static_assert(           \
-    std::is_convertible_v<decltype(expression), void>,          \
-    "Expression should be void.")
-#endif
-
-#endif  // DRAKE_DOXYGEN_CXX
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_assertion_error.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_assertion_error.h
deleted file mode 100644
index b428474..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_assertion_error.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include <stdexcept>
-#include <string>
-
-namespace drake {
-namespace internal {
-
-// This is what DRAKE_ASSERT and DRAKE_DEMAND throw when our assertions are
-// configured to throw.
-class assertion_error : public std::runtime_error {
- public:
-  explicit assertion_error(const std::string& what_arg)
-      : std::runtime_error(what_arg) {}
-};
-
-}  // namespace internal
-}  // namespace drake
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_copyable.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_copyable.h
deleted file mode 100644
index a96a6fb..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_copyable.h
+++ /dev/null
@@ -1,90 +0,0 @@
-#pragma once
-
-// ============================================================================
-// N.B. The spelling of the macro names between doc/Doxyfile_CXX.in and this
-// file must be kept in sync!
-// ============================================================================
-
-/** @file
-Provides careful macros to selectively enable or disable the special member
-functions for copy-construction, copy-assignment, move-construction, and
-move-assignment.
-
-http://en.cppreference.com/w/cpp/language/member_functions#Special_member_functions
-
-When enabled via these macros, the `= default` implementation is provided.
-Code that needs custom copy or move functions should not use these macros.
-*/
-
-/** DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN deletes the special member functions for
-copy-construction, copy-assignment, move-construction, and move-assignment.
-Drake's Doxygen is customized to render the deletions in detail, with
-appropriate comments.  Invoke this macro in the public section of the class
-declaration, e.g.:
-<pre>
-class Foo {
- public:
-  DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(Foo)
-
-  // ...
-};
-</pre>
-*/
-#define DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(Classname)      \
-  Classname(const Classname&) = delete;                 \
-  void operator=(const Classname&) = delete;            \
-  Classname(Classname&&) = delete;                      \
-  void operator=(Classname&&) = delete;
-
-/** DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN defaults the special member
-functions for copy-construction, copy-assignment, move-construction, and
-move-assignment.  This macro should be used only when copy-construction and
-copy-assignment defaults are well-formed.  Note that the defaulted move
-functions could conceivably still be ill-formed, in which case they will
-effectively not be declared or used -- but because the copy constructor exists
-the type will still be MoveConstructible.  Drake's Doxygen is customized to
-render the functions in detail, with appropriate comments.  Typically, you
-should invoke this macro in the public section of the class declaration, e.g.:
-<pre>
-class Foo {
- public:
-  DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(Foo)
-
-  // ...
-};
-</pre>
-
-However, if Foo has a virtual destructor (i.e., is subclassable), then
-typically you should use either DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN in the
-public section or else DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN in the
-protected section, to prevent
-<a href="https://en.wikipedia.org/wiki/Object_slicing">object slicing</a>.
-
-The macro contains a built-in self-check that copy-assignment is well-formed.
-This self-check proves that the Classname is CopyConstructible, CopyAssignable,
-MoveConstructible, and MoveAssignable (in all but the most arcane cases where a
-member of the Classname is somehow CopyAssignable but not CopyConstructible).
-Therefore, classes that use this macro typically will not need to have any unit
-tests that check for the presence nor correctness of these functions.
-
-However, the self-check does not provide any checks of the runtime efficiency
-of the functions.  If it is important for performance that the move functions
-actually move (instead of making a copy), then you should consider capturing
-that in a unit test.
-*/
-#define DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(Classname)       \
-  Classname(const Classname&) = default;                        \
-  Classname& operator=(const Classname&) = default;             \
-  Classname(Classname&&) = default;                             \
-  Classname& operator=(Classname&&) = default;                  \
-  /* Fails at compile-time if copy-assign doesn't compile. */   \
-  /* Note that we do not test the copy-ctor here, because  */   \
-  /* it will not exist when Classname is abstract.         */   \
-  static void DrakeDefaultCopyAndMoveAndAssign_DoAssign(        \
-      Classname* a, const Classname& b) { *a = b; }             \
-  static_assert(                                                \
-      &DrakeDefaultCopyAndMoveAndAssign_DoAssign ==             \
-      &DrakeDefaultCopyAndMoveAndAssign_DoAssign,               \
-      "This assertion is never false; its only purpose is to "  \
-      "generate 'use of deleted function: operator=' errors "   \
-      "when Classname is a template.");
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_throw.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_throw.h
deleted file mode 100644
index fdd07a2..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/drake_throw.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-#include <type_traits>
-
-#include "drake/common/drake_assert.h"
-
-/// @file
-/// Provides a convenient wrapper to throw an exception when a condition is
-/// unmet.  This is similar to an assertion, but uses exceptions instead of
-/// ::abort(), and cannot be disabled.
-
-namespace drake {
-namespace internal {
-// Throw an error message.
-[[noreturn]] void Throw(const char* condition, const char* func,
-                        const char* file, int line);
-}  // namespace internal
-}  // namespace drake
-
-/// Evaluates @p condition and iff the value is false will throw an exception
-/// with a message showing at least the condition text, function name, file,
-/// and line.
-///
-/// The condition must not be a pointer, where we'd implicitly rely on its
-/// nullness. Instead, always write out "!= nullptr" to be precise.
-///
-/// Correct: `DRAKE_THROW_UNLESS(foo != nullptr);`
-/// Incorrect: `DRAKE_THROW_UNLESS(foo);`
-///
-/// Because this macro is intended to provide a useful exception message to
-/// users, we should err on the side of extra detail about the failure. The
-/// meaning of "foo" isolated within error message text does not make it
-/// clear that a null pointer is the proximate cause of the problem.
-#define DRAKE_THROW_UNLESS(condition)                                         \
-  do {                                                                        \
-    typedef ::drake::assert::ConditionTraits<                                 \
-        typename std::remove_cv_t<decltype(condition)>> Trait;                \
-    static_assert(Trait::is_valid, "Condition should be bool-convertible.");  \
-    static_assert(                                                            \
-        !std::is_pointer_v<decltype(condition)>,                              \
-        "When using DRAKE_THROW_UNLESS on a raw pointer, always write out "   \
-        "DRAKE_THROW_UNLESS(foo != nullptr), do not write DRAKE_THROW_UNLESS" \
-        "(foo) and rely on implicit pointer-to-bool conversion.");            \
-    if (!Trait::Evaluate(condition)) {                                        \
-      ::drake::internal::Throw(#condition, __func__, __FILE__, __LINE__);     \
-    }                                                                         \
-  } while (0)
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/is_approx_equal_abstol.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/is_approx_equal_abstol.h
deleted file mode 100644
index b3f369c..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/is_approx_equal_abstol.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#pragma once
-
-#include <vector>
-
-#include <Eigen/Core>
-
-namespace drake {
-
-/// Returns true if and only if the two matrices are equal to within a certain
-/// absolute elementwise @p tolerance.  Special values (infinities, NaN, etc.)
-/// do not compare as equal elements.
-template <typename DerivedA, typename DerivedB>
-bool is_approx_equal_abstol(const Eigen::MatrixBase<DerivedA>& m1,
-                            const Eigen::MatrixBase<DerivedB>& m2,
-                            double tolerance) {
-  return (
-      (m1.rows() == m2.rows()) &&
-      (m1.cols() == m2.cols()) &&
-      ((m1 - m2).template lpNorm<Eigen::Infinity>() <= tolerance));
-}
-
-/// Returns true if and only if a simple greedy search reveals a permutation
-/// of the columns of m2 to make the matrix equal to m1 to within a certain
-/// absolute elementwise @p tolerance. E.g., there exists a P such that
-/// <pre>
-///    forall i,j,  |m1 - m2*P|_{i,j} <= tolerance
-///    where P is a permutation matrix:
-///       P(i,j)={0,1}, sum_i P(i,j)=1, sum_j P(i,j)=1.
-/// </pre>
-/// Note: Returns false for matrices of different sizes.
-/// Note: The current implementation is O(n^2) in the number of columns.
-/// Note: In marginal cases (with similar but not identical columns) this
-/// algorithm can fail to find a permutation P even if it exists because it
-/// accepts the first column match (m1(i),m2(j)) and removes m2(j) from the
-/// pool. It is possible that other columns of m2 would also match m1(i) but
-/// that m2(j) is the only match possible for a later column of m1.
-template <typename DerivedA, typename DerivedB>
-bool IsApproxEqualAbsTolWithPermutedColumns(
-    const Eigen::MatrixBase<DerivedA>& m1,
-    const Eigen::MatrixBase<DerivedB>& m2, double tolerance) {
-  if ((m1.cols() != m2.cols()) || (m1.rows() != m2.rows())) return false;
-
-  std::vector<bool> available(m2.cols());
-  for (int i = 0; i < m2.cols(); i++) available[i] = true;
-
-  for (int i = 0; i < m1.cols(); i++) {
-    bool found_match = false;
-    for (int j = 0; j < m2.cols(); j++) {
-      if (available[j] &&
-          is_approx_equal_abstol(m1.col(i), m2.col(j), tolerance)) {
-        found_match = true;
-        available[j] = false;
-        break;
-      }
-    }
-    if (!found_match) return false;
-  }
-  return true;
-}
-
-
-}  // namespace drake
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/never_destroyed.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/never_destroyed.h
deleted file mode 100644
index 024b355..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/common/never_destroyed.h
+++ /dev/null
@@ -1,103 +0,0 @@
-#pragma once
-
-#include <new>
-#include <type_traits>
-#include <utility>
-
-#include "drake/common/drake_copyable.h"
-
-namespace drake {
-
-/// Wraps an underlying type T such that its storage is a direct member field
-/// of this object (i.e., without any indirection into the heap), but *unlike*
-/// most member fields T's destructor is never invoked.
-///
-/// This is especially useful for function-local static variables that are not
-/// trivially destructable.  We shouldn't call their destructor at program exit
-/// because of the "indeterminate order of ... destruction" as mentioned in
-/// cppguide's
-/// <a href="https://drake.mit.edu/styleguide/cppguide.html#Static_and_Global_Variables">Static
-/// and Global Variables</a> section, but other solutions to this problem place
-///  the objects on the heap through an indirection.
-///
-/// Compared with other approaches, this mechanism more clearly describes the
-/// intent to readers, avoids "possible leak" warnings from memory-checking
-/// tools, and is probably slightly faster.
-///
-/// Example uses:
-///
-/// The singleton pattern:
-/// @code
-/// class Singleton {
-///  public:
-///   DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(Singleton)
-///   static Singleton& getInstance() {
-///     static never_destroyed<Singleton> instance;
-///     return instance.access();
-///   }
-///  private:
-///   friend never_destroyed<Singleton>;
-///   Singleton() = default;
-/// };
-/// @endcode
-///
-/// A lookup table, created on demand the first time its needed, and then
-/// reused thereafter:
-/// @code
-/// enum class Foo { kBar, kBaz };
-/// Foo ParseFoo(const std::string& foo_string) {
-///   using Dict = std::unordered_map<std::string, Foo>;
-///   static const drake::never_destroyed<Dict> string_to_enum{
-///     std::initializer_list<Dict::value_type>{
-///       {"bar", Foo::kBar},
-///       {"baz", Foo::kBaz},
-///     }
-///   };
-///   return string_to_enum.access().at(foo_string);
-/// }
-/// @endcode
-///
-/// In cases where computing the static data is more complicated than an
-/// initializer_list, you can use a temporary lambda to populate the value:
-/// @code
-/// const std::vector<double>& GetConstantMagicNumbers() {
-///   static const drake::never_destroyed<std::vector<double>> result{[]() {
-///     std::vector<double> prototype;
-///     std::mt19937 random_generator;
-///     for (int i = 0; i < 10; ++i) {
-///       double new_value = random_generator();
-///       prototype.push_back(new_value);
-///     }
-///     return prototype;
-///   }()};
-///   return result.access();
-/// }
-/// @endcode
-///
-/// Note in particular the `()` after the lambda. That causes it to be invoked.
-//
-// The above examples are repeated in the unit test; keep them in sync.
-template <typename T>
-class never_destroyed {
- public:
-  DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(never_destroyed)
-
-  /// Passes the constructor arguments along to T using perfect forwarding.
-  template <typename... Args>
-  explicit never_destroyed(Args&&... args) {
-    // Uses "placement new" to construct a `T` in `storage_`.
-    new (&storage_) T(std::forward<Args>(args)...);
-  }
-
-  /// Does nothing.  Guaranteed!
-  ~never_destroyed() = default;
-
-  /// Returns the underlying T reference.
-  T& access() { return *reinterpret_cast<T*>(&storage_); }
-  const T& access() const { return *reinterpret_cast<const T*>(&storage_); }
-
- private:
-  typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
-};
-
-}  // namespace drake
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/math/discrete_algebraic_riccati_equation.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/math/discrete_algebraic_riccati_equation.h
deleted file mode 100644
index 55b8442..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/include/drake/math/discrete_algebraic_riccati_equation.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#pragma once
-
-#include <cmath>
-#include <cstdlib>
-
-#include <Eigen/Core>
-#include <wpi/SymbolExports.h>
-
-namespace drake {
-namespace math {
-
-/**
-Computes the unique stabilizing solution X to the discrete-time algebraic
-Riccati equation:
-
-AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
-
-@throws std::exception if Q is not positive semi-definite.
-@throws std::exception if R is not positive definite.
-
-Based on the Schur Vector approach outlined in this paper:
-"On the Numerical Solution of the Discrete-Time Algebraic Riccati Equation"
-by Thrasyvoulos Pappas, Alan J. Laub, and Nils R. Sandell
-*/
-WPILIB_DLLEXPORT
-Eigen::MatrixXd DiscreteAlgebraicRiccatiEquation(
-    const Eigen::Ref<const Eigen::MatrixXd>& A,
-    const Eigen::Ref<const Eigen::MatrixXd>& B,
-    const Eigen::Ref<const Eigen::MatrixXd>& Q,
-    const Eigen::Ref<const Eigen::MatrixXd>& R);
-
-/**
-Computes the unique stabilizing solution X to the discrete-time algebraic
-Riccati equation:
-
-AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
-
-This is equivalent to solving the original DARE:
-
-A₂ᵀXA₂ − X − A₂ᵀXB(BᵀXB + R)⁻¹BᵀXA₂ + Q₂ = 0
-
-where A₂ and Q₂ are a change of variables:
-
-A₂ = A − BR⁻¹Nᵀ and Q₂ = Q − NR⁻¹Nᵀ
-
-This overload of the DARE is useful for finding the control law uₖ that
-minimizes the following cost function subject to xₖ₊₁ = Axₖ + Buₖ.
-
-@verbatim
-    ∞ [xₖ]ᵀ[Q  N][xₖ]
-J = Σ [uₖ] [Nᵀ R][uₖ] ΔT
-   k=0
-@endverbatim
-
-This is a more general form of the following. The linear-quadratic regulator
-is the feedback control law uₖ that minimizes the following cost function
-subject to xₖ₊₁ = Axₖ + Buₖ:
-
-@verbatim
-    ∞
-J = Σ (xₖᵀQxₖ + uₖᵀRuₖ) ΔT
-   k=0
-@endverbatim
-
-This can be refactored as:
-
-@verbatim
-    ∞ [xₖ]ᵀ[Q 0][xₖ]
-J = Σ [uₖ] [0 R][uₖ] ΔT
-   k=0
-@endverbatim
-
-@throws std::runtime_error if Q − NR⁻¹Nᵀ is not positive semi-definite.
-@throws std::runtime_error if R is not positive definite.
-*/
-WPILIB_DLLEXPORT
-Eigen::MatrixXd DiscreteAlgebraicRiccatiEquation(
-    const Eigen::Ref<const Eigen::MatrixXd>& A,
-    const Eigen::Ref<const Eigen::MatrixXd>& B,
-    const Eigen::Ref<const Eigen::MatrixXd>& Q,
-    const Eigen::Ref<const Eigen::MatrixXd>& R,
-    const Eigen::Ref<const Eigen::MatrixXd>& N);
-}  // namespace math
-}  // namespace drake
-
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/src/common/drake_assert_and_throw.cpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/src/common/drake_assert_and_throw.cpp
deleted file mode 100644
index 88e7e66..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/src/common/drake_assert_and_throw.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-// This file contains the implementation of both drake_assert and drake_throw.
-/* clang-format off to disable clang-format-includes */
-#include "drake/common/drake_assert.h"
-#include "drake/common/drake_throw.h"
-/* clang-format on */
-
-#include <atomic>
-#include <cstdlib>
-#include <iostream>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-
-#include "drake/common/drake_assertion_error.h"
-#include "drake/common/never_destroyed.h"
-
-namespace drake {
-namespace internal {
-namespace {
-
-// Singleton to manage assertion configuration.
-struct AssertionConfig {
-  static AssertionConfig& singleton() {
-    static never_destroyed<AssertionConfig> global;
-    return global.access();
-  }
-
-  std::atomic<bool> assertion_failures_are_exceptions;
-};
-
-// Stream into @p out the given failure details; only @p condition may be null.
-void PrintFailureDetailTo(std::ostream& out, const char* condition,
-                          const char* func, const char* file, int line) {
-  out << "Failure at " << file << ":" << line << " in " << func << "()";
-  if (condition) {
-    out << ": condition '" << condition << "' failed.";
-  } else {
-    out << ".";
-  }
-}
-}  // namespace
-
-// Declared in drake_assert.h.
-void Abort(const char* condition, const char* func, const char* file,
-           int line) {
-  std::cerr << "abort: ";
-  PrintFailureDetailTo(std::cerr, condition, func, file, line);
-  std::cerr << std::endl;
-  std::abort();
-}
-
-// Declared in drake_throw.h.
-void Throw(const char* condition, const char* func, const char* file,
-           int line) {
-  std::ostringstream what;
-  PrintFailureDetailTo(what, condition, func, file, line);
-  throw assertion_error(what.str().c_str());
-}
-
-// Declared in drake_assert.h.
-void AssertionFailed(const char* condition, const char* func, const char* file,
-                     int line) {
-  if (AssertionConfig::singleton().assertion_failures_are_exceptions) {
-    Throw(condition, func, file, line);
-  } else {
-    Abort(condition, func, file, line);
-  }
-}
-
-}  // namespace internal
-}  // namespace drake
-
-// Configures the DRAKE_ASSERT and DRAKE_DEMAND assertion failure handling
-// behavior.
-//
-// By default, assertion failures will result in an ::abort().  If this method
-// has ever been called, failures will result in a thrown exception instead.
-//
-// Assertion configuration has process-wide scope.  Changes here will affect
-// all assertions within the current process.
-//
-// This method is intended ONLY for use by pydrake bindings, and thus is not
-// declared in any header file, to discourage anyone from using it.
-extern "C" void drake_set_assertion_failure_to_throw_exception() {
-  drake::internal::AssertionConfig::singleton().
-      assertion_failures_are_exceptions = true;
-}
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/src/math/discrete_algebraic_riccati_equation.cpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/src/math/discrete_algebraic_riccati_equation.cpp
deleted file mode 100644
index 20ea2b7..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/drake/src/math/discrete_algebraic_riccati_equation.cpp
+++ /dev/null
@@ -1,475 +0,0 @@
-#include "drake/math/discrete_algebraic_riccati_equation.h"
-
-#include <Eigen/Eigenvalues>
-#include <Eigen/QR>
-
-#include "drake/common/drake_assert.h"
-#include "drake/common/drake_throw.h"
-#include "drake/common/is_approx_equal_abstol.h"
-
-namespace drake {
-namespace math {
-namespace {
-/* helper functions */
-template <typename T>
-int sgn(T val) {
-  return (T(0) < val) - (val < T(0));
-}
-void check_stabilizable(const Eigen::Ref<const Eigen::MatrixXd>& A,
-                        const Eigen::Ref<const Eigen::MatrixXd>& B) {
-  // This function checks if (A,B) is a stabilizable pair.
-  // (A,B) is stabilizable if and only if the uncontrollable eigenvalues of
-  // A, if any, have absolute values less than one, where an eigenvalue is
-  // uncontrollable if Rank[lambda * I - A, B] < n.
-  int n = B.rows(), m = B.cols();
-  Eigen::EigenSolver<Eigen::MatrixXd> es(A);
-  for (int i = 0; i < n; i++) {
-    if (es.eigenvalues()[i].real() * es.eigenvalues()[i].real() +
-            es.eigenvalues()[i].imag() * es.eigenvalues()[i].imag() <
-        1)
-      continue;
-    Eigen::MatrixXcd E(n, n + m);
-    E << es.eigenvalues()[i] * Eigen::MatrixXcd::Identity(n, n) - A, B;
-    Eigen::ColPivHouseholderQR<Eigen::MatrixXcd> qr(E);
-    DRAKE_THROW_UNLESS(qr.rank() == n);
-  }
-}
-void check_detectable(const Eigen::Ref<const Eigen::MatrixXd>& A,
-                      const Eigen::Ref<const Eigen::MatrixXd>& Q) {
-  // This function check if (A,C) is a detectable pair, where Q = C' * C.
-  // (A,C) is detectable if and only if the unobservable eigenvalues of A,
-  // if any, have absolute values less than one, where an eigenvalue is
-  // unobservable if Rank[lambda * I - A; C] < n.
-  // Also, (A,C) is detectable if and only if (A',C') is stabilizable.
-  int n = A.rows();
-  Eigen::LDLT<Eigen::MatrixXd> ldlt(Q);
-  Eigen::MatrixXd L = ldlt.matrixL();
-  Eigen::MatrixXd D = ldlt.vectorD();
-  Eigen::MatrixXd D_sqrt = Eigen::MatrixXd::Zero(n, n);
-  for (int i = 0; i < n; i++) {
-    D_sqrt(i, i) = sqrt(D(i));
-  }
-  Eigen::MatrixXd C = L * D_sqrt;
-  check_stabilizable(A.transpose(), C.transpose());
-}
-
-// "Givens rotation" computes an orthogonal 2x2 matrix R such that
-// it eliminates the 2nd coordinate of the vector [a,b]', i.e.,
-// R * [ a ] = [ a_hat ]
-//     [ b ]   [   0   ]
-// The implementation is based on
-// https://en.wikipedia.org/wiki/Givens_rotation#Stable_calculation
-void Givens_rotation(double a, double b, Eigen::Ref<Eigen::Matrix2d> R,
-                     double eps = 1e-10) {
-  double c, s;
-  if (fabs(b) < eps) {
-    c = (a < -eps ? -1 : 1);
-    s = 0;
-  } else if (fabs(a) < eps) {
-    c = 0;
-    s = -sgn(b);
-  } else if (fabs(a) > fabs(b)) {
-    double t = b / a;
-    double u = sgn(a) * fabs(sqrt(1 + t * t));
-    c = 1 / u;
-    s = -c * t;
-  } else {
-    double t = a / b;
-    double u = sgn(b) * fabs(sqrt(1 + t * t));
-    s = -1 / u;
-    c = -s * t;
-  }
-  R(0, 0) = c, R(0, 1) = -s, R(1, 0) = s, R(1, 1) = c;
-}
-
-// The arguments S, T, and Z will be changed.
-void swap_block_11(Eigen::Ref<Eigen::MatrixXd> S, Eigen::Ref<Eigen::MatrixXd> T,
-                   Eigen::Ref<Eigen::MatrixXd> Z, int p) {
-  // Dooren, Case I, p124-125.
-  int n2 = S.rows();
-  Eigen::Matrix2d A = S.block<2, 2>(p, p);
-  Eigen::Matrix2d B = T.block<2, 2>(p, p);
-  Eigen::MatrixXd Z1 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::Matrix2d H = A(1, 1) * B - B(1, 1) * A;
-  Givens_rotation(H(0, 1), H(0, 0), Z1.block<2, 2>(p, p));
-  S = (S * Z1).eval();
-  T = (T * Z1).eval();
-  Z = (Z * Z1).eval();
-  Eigen::MatrixXd Q = Eigen::MatrixXd::Identity(n2, n2);
-  Givens_rotation(T(p, p), T(p + 1, p), Q.block<2, 2>(p, p));
-  S = (Q * S).eval();
-  T = (Q * T).eval();
-  S(p + 1, p) = 0;
-  T(p + 1, p) = 0;
-}
-
-// The arguments S, T, and Z will be changed.
-void swap_block_21(Eigen::Ref<Eigen::MatrixXd> S, Eigen::Ref<Eigen::MatrixXd> T,
-                   Eigen::Ref<Eigen::MatrixXd> Z, int p) {
-  // Dooren, Case II, p126-127.
-  int n2 = S.rows();
-  Eigen::Matrix3d A = S.block<3, 3>(p, p);
-  Eigen::Matrix3d B = T.block<3, 3>(p, p);
-  // Compute H and eliminate H(1,0) by row operation.
-  Eigen::Matrix3d H = A(2, 2) * B - B(2, 2) * A;
-  Eigen::Matrix3d R = Eigen::MatrixXd::Identity(3, 3);
-  Givens_rotation(H(0, 0), H(1, 0), R.block<2, 2>(0, 0));
-  H = (R * H).eval();
-  Eigen::MatrixXd Z1 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::MatrixXd Z2 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::MatrixXd Q1 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::MatrixXd Q2 = Eigen::MatrixXd::Identity(n2, n2);
-  // Compute Z1, Z2, Q1, Q2.
-  Givens_rotation(H(1, 2), H(1, 1), Z1.block<2, 2>(p + 1, p + 1));
-  H = (H * Z1.block<3, 3>(p, p)).eval();
-  Givens_rotation(H(0, 1), H(0, 0), Z2.block<2, 2>(p, p));
-  S = (S * Z1).eval();
-  T = (T * Z1).eval();
-  Z = (Z * Z1 * Z2).eval();
-  Givens_rotation(T(p + 1, p + 1), T(p + 2, p + 1),
-                  Q1.block<2, 2>(p + 1, p + 1));
-  S = (Q1 * S * Z2).eval();
-  T = (Q1 * T * Z2).eval();
-  Givens_rotation(T(p, p), T(p + 1, p), Q2.block<2, 2>(p, p));
-  S = (Q2 * S).eval();
-  T = (Q2 * T).eval();
-  S(p + 1, p) = 0;
-  S(p + 2, p) = 0;
-  T(p + 1, p) = 0;
-  T(p + 2, p) = 0;
-  T(p + 2, p + 1) = 0;
-}
-
-// The arguments S, T, and Z will be changed.
-void swap_block_12(Eigen::Ref<Eigen::MatrixXd> S, Eigen::Ref<Eigen::MatrixXd> T,
-                   Eigen::Ref<Eigen::MatrixXd> Z, int p) {
-  int n2 = S.rows();
-  // Swap the role of S and T.
-  Eigen::MatrixXd Z1 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::MatrixXd Z2 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::MatrixXd Q0 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::MatrixXd Q1 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::MatrixXd Q2 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::MatrixXd Q3 = Eigen::MatrixXd::Identity(n2, n2);
-  Givens_rotation(S(p + 1, p + 1), S(p + 2, p + 1),
-                  Q0.block<2, 2>(p + 1, p + 1));
-  S = (Q0 * S).eval();
-  T = (Q0 * T).eval();
-  Eigen::Matrix3d A = S.block<3, 3>(p, p);
-  Eigen::Matrix3d B = T.block<3, 3>(p, p);
-  // Compute H and eliminate H(2,1) by column operation.
-  Eigen::Matrix3d H = B(0, 0) * A - A(0, 0) * B;
-  Eigen::Matrix3d R = Eigen::MatrixXd::Identity(3, 3);
-  Givens_rotation(H(2, 2), H(2, 1), R.block<2, 2>(1, 1));
-  H = (H * R).eval();
-  // Compute Q1, Q2, Z1, Z2.
-  Givens_rotation(H(0, 1), H(1, 1), Q1.block<2, 2>(p, p));
-  H = (Q1.block<3, 3>(p, p) * H).eval();
-  Givens_rotation(H(1, 2), H(2, 2), Q2.block<2, 2>(p + 1, p + 1));
-  S = (Q1 * S).eval();
-  T = (Q1 * T).eval();
-  Givens_rotation(S(p + 1, p + 1), S(p + 1, p), Z1.block<2, 2>(p, p));
-  S = (Q2 * S * Z1).eval();
-  T = (Q2 * T * Z1).eval();
-  Givens_rotation(S(p + 2, p + 2), S(p + 2, p + 1),
-                  Z2.block<2, 2>(p + 1, p + 1));
-  S = (S * Z2).eval();
-  T = (T * Z2).eval();
-  Z = (Z * Z1 * Z2).eval();
-  // Swap back the role of S and T.
-  Givens_rotation(T(p, p), T(p + 1, p), Q3.block<2, 2>(p, p));
-  S = (Q3 * S).eval();
-  T = (Q3 * T).eval();
-  S(p + 2, p) = 0;
-  S(p + 2, p + 1) = 0;
-  T(p + 1, p) = 0;
-  T(p + 2, p) = 0;
-  T(p + 2, p + 1) = 0;
-}
-
-// The arguments S, T, and Z will be changed.
-void swap_block_22(Eigen::Ref<Eigen::MatrixXd> S, Eigen::Ref<Eigen::MatrixXd> T,
-                   Eigen::Ref<Eigen::MatrixXd> Z, int p) {
-  // Direct Swapping Algorithm based on
-  // "Numerical Methods for General and Structured Eigenvalue Problems" by
-  // Daniel Kressner, p108-111.
-  // ( http://sma.epfl.ch/~anchpcommon/publications/kressner_eigenvalues.pdf ).
-  // Also relevant but not applicable here:
-  // "On Swapping Diagonal Blocks in Real Schur Form" by Zhaojun Bai and James
-  // W. Demmelt;
-  int n2 = S.rows();
-  Eigen::MatrixXd A = S.block<4, 4>(p, p);
-  Eigen::MatrixXd B = T.block<4, 4>(p, p);
-  // Solve
-  // A11 * X - Y A22 = A12
-  // B11 * X - Y B22 = B12
-  // Reduce to solve Cx=D, where x=[x1;...;x4;y1;...;y4].
-  Eigen::Matrix<double, 8, 8> C = Eigen::Matrix<double, 8, 8>::Zero();
-  Eigen::Matrix<double, 8, 1> D;
-  // clang-format off
-  C << A(0, 0), 0, A(0, 1), 0, -A(2, 2), -A(3, 2), 0, 0,
-       0, A(0, 0), 0, A(0, 1), -A(2, 3), -A(3, 3), 0, 0,
-       A(1, 0), 0, A(1, 1), 0, 0, 0, -A(2, 2), -A(3, 2),
-       0, A(1, 0), 0, A(1, 1), 0, 0, -A(2, 3), -A(3, 3),
-       B(0, 0), 0, B(0, 1), 0, -B(2, 2), -B(3, 2), 0, 0,
-       0, B(0, 0), 0, B(0, 1), -B(2, 3), -B(3, 3), 0, 0,
-       B(1, 0), 0, B(1, 1), 0, 0, 0, -B(2, 2), -B(3, 2),
-       0, B(1, 0), 0, B(1, 1), 0, 0, -B(2, 3), -B(3, 3);
-  // clang-format on
-  D << A(0, 2), A(0, 3), A(1, 2), A(1, 3), B(0, 2), B(0, 3), B(1, 2), B(1, 3);
-  Eigen::MatrixXd x = C.colPivHouseholderQr().solve(D);
-  // Q * [ -Y ] = [ R_Y ] ,  Z' * [ -X ] = [ R_X ] .
-  //     [ I  ]   [  0  ]         [ I  ] = [  0  ]
-  Eigen::Matrix<double, 4, 2> X, Y;
-  X << -x(0, 0), -x(1, 0), -x(2, 0), -x(3, 0), Eigen::Matrix2d::Identity();
-  Y << -x(4, 0), -x(5, 0), -x(6, 0), -x(7, 0), Eigen::Matrix2d::Identity();
-  Eigen::MatrixXd Q1 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::MatrixXd Z1 = Eigen::MatrixXd::Identity(n2, n2);
-  Eigen::ColPivHouseholderQR<Eigen::Matrix<double, 4, 2> > qr1(X);
-  Z1.block<4, 4>(p, p) = qr1.householderQ();
-  Eigen::ColPivHouseholderQR<Eigen::Matrix<double, 4, 2> > qr2(Y);
-  Q1.block<4, 4>(p, p) = qr2.householderQ().adjoint();
-  // Apply transform Q1 * (S,T) * Z1.
-  S = (Q1 * S * Z1).eval();
-  T = (Q1 * T * Z1).eval();
-  Z = (Z * Z1).eval();
-  // Eliminate the T(p+3,p+2) entry.
-  Eigen::MatrixXd Q2 = Eigen::MatrixXd::Identity(n2, n2);
-  Givens_rotation(T(p + 2, p + 2), T(p + 3, p + 2),
-                  Q2.block<2, 2>(p + 2, p + 2));
-  S = (Q2 * S).eval();
-  T = (Q2 * T).eval();
-  // Eliminate the T(p+1,p) entry.
-  Eigen::MatrixXd Q3 = Eigen::MatrixXd::Identity(n2, n2);
-  Givens_rotation(T(p, p), T(p + 1, p), Q3.block<2, 2>(p, p));
-  S = (Q3 * S).eval();
-  T = (Q3 * T).eval();
-  S(p + 2, p) = 0;
-  S(p + 2, p + 1) = 0;
-  S(p + 3, p) = 0;
-  S(p + 3, p + 1) = 0;
-  T(p + 1, p) = 0;
-  T(p + 2, p) = 0;
-  T(p + 2, p + 1) = 0;
-  T(p + 3, p) = 0;
-  T(p + 3, p + 1) = 0;
-  T(p + 3, p + 2) = 0;
-}
-
-// Functionality of "swap_block" function:
-// swap the 1x1 or 2x2 blocks pointed by p and q.
-// There are four cases: swapping 1x1 and 1x1 matrices, swapping 2x2 and 1x1
-// matrices, swapping 1x1 and 2x2 matrices, and swapping 2x2 and 2x2 matrices.
-// Algorithms are described in the papers
-// "A generalized eigenvalue approach for solving Riccati equations" by P. Van
-// Dooren, 1981 ( http://epubs.siam.org/doi/pdf/10.1137/0902010 ), and
-// "Numerical Methods for General and Structured Eigenvalue Problems" by
-// Daniel Kressner, 2005.
-void swap_block(Eigen::Ref<Eigen::MatrixXd> S, Eigen::Ref<Eigen::MatrixXd> T,
-                Eigen::Ref<Eigen::MatrixXd> Z, int p, int q, int q_block_size,
-                double eps = 1e-10) {
-  int p_tmp = q, p_block_size;
-  while (p_tmp-- > p) {
-    p_block_size = 1;
-    if (p_tmp >= 1 && fabs(S(p_tmp, p_tmp - 1)) > eps) {
-      p_block_size = 2;
-      p_tmp--;
-    }
-    switch (p_block_size * 10 + q_block_size) {
-      case 11:
-        swap_block_11(S, T, Z, p_tmp);
-        break;
-      case 21:
-        swap_block_21(S, T, Z, p_tmp);
-        break;
-      case 12:
-        swap_block_12(S, T, Z, p_tmp);
-        break;
-      case 22:
-        swap_block_22(S, T, Z, p_tmp);
-        break;
-    }
-  }
-}
-
-// Functionality of "reorder_eigen" function:
-// Reorder the eigenvalues of (S,T) such that the top-left n by n matrix has
-// stable eigenvalues by multiplying Q's and Z's on the left and the right,
-// respectively.
-// Stable eigenvalues are inside the unit disk.
-//
-// Algorithm:
-// Go along the diagonals of (S,T) from the top left to the bottom right.
-// Once find a stable eigenvalue, push it to top left.
-// In implementation, use two pointers, p and q.
-// p points to the current block (1x1 or 2x2) and q points to the block with the
-// stable eigenvalue(s).
-// Push the block pointed by q to the position pointed by p.
-// Finish when n stable eigenvalues are placed at the top-left n by n matrix.
-// The algorithm for swapping blocks is described in the papers
-// "A generalized eigenvalue approach for solving Riccati equations" by P. Van
-// Dooren, 1981, and "Numerical Methods for General and Structured Eigenvalue
-// Problems" by Daniel Kressner, 2005.
-void reorder_eigen(Eigen::Ref<Eigen::MatrixXd> S, Eigen::Ref<Eigen::MatrixXd> T,
-                   Eigen::Ref<Eigen::MatrixXd> Z, double eps = 1e-10) {
-  // abs(a) < eps => a = 0
-  int n2 = S.rows();
-  int n = n2 / 2, p = 0, q = 0;
-
-  // Find the first unstable p block.
-  while (p < n) {
-    if (fabs(S(p + 1, p)) < eps) {  // p block size = 1
-      if (fabs(T(p, p)) > eps && fabs(S(p, p)) <= fabs(T(p, p))) {  // stable
-        p++;
-        continue;
-      }
-    } else {  // p block size = 2
-      const double det_T =
-          T(p, p) * T(p + 1, p + 1) - T(p + 1, p) * T(p, p + 1);
-      if (fabs(det_T) > eps) {
-        const double det_S =
-            S(p, p) * S(p + 1, p + 1) - S(p + 1, p) * S(p, p + 1);
-        if (fabs(det_S) <= fabs(det_T)) {  // stable
-          p += 2;
-          continue;
-        }
-      }
-    }
-    break;
-  }
-  q = p;
-
-  // Make the first n generalized eigenvalues stable.
-  while (p < n && q < n2) {
-    // Update q.
-    int q_block_size = 0;
-    while (q < n2) {
-      if (q == n2 - 1 || fabs(S(q + 1, q)) < eps) {  // block size = 1
-        if (fabs(T(q, q)) > eps && fabs(S(q, q)) <= fabs(T(q, q))) {
-          q_block_size = 1;
-          break;
-        }
-        q++;
-      } else {  // block size = 2
-        const double det_T =
-            T(q, q) * T(q + 1, q + 1) - T(q + 1, q) * T(q, q + 1);
-        if (fabs(det_T) > eps) {
-          const double det_S =
-              S(q, q) * S(q + 1, q + 1) - S(q + 1, q) * S(q, q + 1);
-          if (fabs(det_S) <= fabs(det_T)) {
-            q_block_size = 2;
-            break;
-          }
-        }
-        q += 2;
-      }
-    }
-    if (q >= n2)
-      throw std::runtime_error("fail to find enough stable eigenvalues");
-    // Swap blocks pointed by p and q.
-    if (p != q) {
-      swap_block(S, T, Z, p, q, q_block_size);
-      p += q_block_size;
-      q += q_block_size;
-    }
-  }
-  if (p < n && q >= n2)
-    throw std::runtime_error("fail to find enough stable eigenvalues");
-}
-}  // namespace
-
-/**
- * DiscreteAlgebraicRiccatiEquation function
- * computes the unique stabilizing solution X to the discrete-time algebraic
- * Riccati equation:
- *
- * AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
- *
- * @throws std::exception if Q is not positive semi-definite.
- * @throws std::exception if R is not positive definite.
- *
- * Based on the Schur Vector approach outlined in this paper:
- * "On the Numerical Solution of the Discrete-Time Algebraic Riccati Equation"
- * by Thrasyvoulos Pappas, Alan J. Laub, and Nils R. Sandell, in TAC, 1980,
- * http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=1102434
- *
- * Note: When, for example, n = 100, m = 80, and entries of A, B, Q_half,
- * R_half are sampled from standard normal distributions, where
- * Q = Q_halfᵀ Q_half and similar for R, the absolute error of the solution
- * is 10⁻⁶, while the absolute error of the solution computed by Matlab is
- * 10⁻⁸.
- *
- * TODO(weiqiao.han): I may overwrite the RealQZ function to improve the
- * accuracy, together with more thorough tests.
- */
-
-Eigen::MatrixXd DiscreteAlgebraicRiccatiEquation(
-    const Eigen::Ref<const Eigen::MatrixXd>& A,
-    const Eigen::Ref<const Eigen::MatrixXd>& B,
-    const Eigen::Ref<const Eigen::MatrixXd>& Q,
-    const Eigen::Ref<const Eigen::MatrixXd>& R) {
-  int n = B.rows(), m = B.cols();
-
-  DRAKE_DEMAND(m <= n);
-  DRAKE_DEMAND(A.rows() == n && A.cols() == n);
-  DRAKE_DEMAND(Q.rows() == n && Q.cols() == n);
-  DRAKE_DEMAND(R.rows() == m && R.cols() == m);
-  DRAKE_DEMAND(is_approx_equal_abstol(Q, Q.transpose(), 1e-10));
-  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(Q);
-  for (int i = 0; i < n; i++) {
-    DRAKE_THROW_UNLESS(es.eigenvalues()[i] >= 0);
-  }
-  DRAKE_DEMAND(is_approx_equal_abstol(R, R.transpose(), 1e-10));
-  Eigen::LLT<Eigen::MatrixXd> R_cholesky(R);
-  DRAKE_THROW_UNLESS(R_cholesky.info() == Eigen::Success);
-  check_stabilizable(A, B);
-  check_detectable(A, Q);
-
-  Eigen::MatrixXd M(2 * n, 2 * n), L(2 * n, 2 * n);
-  M << A, Eigen::MatrixXd::Zero(n, n), -Q, Eigen::MatrixXd::Identity(n, n);
-  L << Eigen::MatrixXd::Identity(n, n), B * R.inverse() * B.transpose(),
-      Eigen::MatrixXd::Zero(n, n), A.transpose();
-
-  // QZ decomposition of M and L
-  // QMZ = S, QLZ = T
-  // where Q and Z are real orthogonal matrixes
-  // T is upper-triangular matrix, and S is upper quasi-triangular matrix
-  Eigen::RealQZ<Eigen::MatrixXd> qz(2 * n);
-  qz.compute(M, L);  // M = Q S Z,  L = Q T Z (Q and Z computed by Eigen package
-                     // are adjoints of Q and Z above)
-  Eigen::MatrixXd S = qz.matrixS(), T = qz.matrixT(),
-                  Z = qz.matrixZ().adjoint();
-
-  // Reorder the generalized eigenvalues of (S,T).
-  Eigen::MatrixXd Z2 = Eigen::MatrixXd::Identity(2 * n, 2 * n);
-  reorder_eigen(S, T, Z2);
-  Z = (Z * Z2).eval();
-
-  // The first n columns of Z is ( U1 ) .
-  //                             ( U2 )
-  //            -1
-  // X = U2 * U1   is a solution of the discrete time Riccati equation.
-  Eigen::MatrixXd U1 = Z.block(0, 0, n, n), U2 = Z.block(n, 0, n, n);
-  Eigen::MatrixXd X = U2 * U1.inverse();
-  X = (X + X.adjoint().eval()) / 2.0;
-  return X;
-}
-
-Eigen::MatrixXd DiscreteAlgebraicRiccatiEquation(
-    const Eigen::Ref<const Eigen::MatrixXd>& A,
-    const Eigen::Ref<const Eigen::MatrixXd>& B,
-    const Eigen::Ref<const Eigen::MatrixXd>& Q,
-    const Eigen::Ref<const Eigen::MatrixXd>& R,
-    const Eigen::Ref<const Eigen::MatrixXd>& N) {
-    DRAKE_DEMAND(N.rows() == B.rows() && N.cols() == B.cols());
-
-    // This is a change of variables to make the DARE that includes Q, R, and N
-    // cost matrices fit the form of the DARE that includes only Q and R cost
-    // matrices.
-    Eigen::MatrixXd A2 = A - B * R.llt().solve(N.transpose());
-    Eigen::MatrixXd Q2 = Q - N * R.llt().solve(N.transpose());
-    return DiscreteAlgebraicRiccatiEquation(A2, B, Q2, R);
-}
-
-}  // namespace math
-}  // namespace drake
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/StdDeque b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/StdDeque
deleted file mode 100644
index bc68397..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/StdDeque
+++ /dev/null
@@ -1,27 +0,0 @@
-// This file is part of Eigen, a lightweight C++ template library
-// for linear algebra.
-//
-// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
-// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
-//
-// This Source Code Form is subject to the terms of the Mozilla
-// Public License v. 2.0. If a copy of the MPL was not distributed
-// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef EIGEN_STDDEQUE_MODULE_H
-#define EIGEN_STDDEQUE_MODULE_H
-
-#include "Core"
-#include <deque>
-
-#if EIGEN_COMP_MSVC && EIGEN_OS_WIN64 && (EIGEN_MAX_STATIC_ALIGN_BYTES<=16) /* MSVC auto aligns up to 16 bytes in 64 bit builds */
-
-#define EIGEN_DEFINE_STL_DEQUE_SPECIALIZATION(...)
-
-#else
-
-#include "src/StlSupport/StdDeque.h"
-
-#endif
-
-#endif // EIGEN_STDDEQUE_MODULE_H
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/StdList b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/StdList
deleted file mode 100644
index 4c6262c..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/StdList
+++ /dev/null
@@ -1,26 +0,0 @@
-// This file is part of Eigen, a lightweight C++ template library
-// for linear algebra.
-//
-// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
-//
-// This Source Code Form is subject to the terms of the Mozilla
-// Public License v. 2.0. If a copy of the MPL was not distributed
-// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef EIGEN_STDLIST_MODULE_H
-#define EIGEN_STDLIST_MODULE_H
-
-#include "Core"
-#include <list>
-
-#if EIGEN_COMP_MSVC && EIGEN_OS_WIN64 && (EIGEN_MAX_STATIC_ALIGN_BYTES<=16) /* MSVC auto aligns up to 16 bytes in 64 bit builds */
-
-#define EIGEN_DEFINE_STL_LIST_SPECIALIZATION(...)
-
-#else
-
-#include "src/StlSupport/StdList.h"
-
-#endif
-
-#endif // EIGEN_STDLIST_MODULE_H
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/StdVector b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/StdVector
deleted file mode 100644
index 0c4697a..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/StdVector
+++ /dev/null
@@ -1,27 +0,0 @@
-// This file is part of Eigen, a lightweight C++ template library
-// for linear algebra.
-//
-// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
-// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
-//
-// This Source Code Form is subject to the terms of the Mozilla
-// Public License v. 2.0. If a copy of the MPL was not distributed
-// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef EIGEN_STDVECTOR_MODULE_H
-#define EIGEN_STDVECTOR_MODULE_H
-
-#include "Core"
-#include <vector>
-
-#if EIGEN_COMP_MSVC && EIGEN_OS_WIN64 && (EIGEN_MAX_STATIC_ALIGN_BYTES<=16) /* MSVC auto aligns up to 16 bytes in 64 bit builds */
-
-#define EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(...)
-
-#else
-
-#include "src/StlSupport/StdVector.h"
-
-#endif
-
-#endif // EIGEN_STDVECTOR_MODULE_H
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/util/DisableStupidWarnings.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/util/DisableStupidWarnings.h
index d973255..9a630e4 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/util/DisableStupidWarnings.h
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/util/DisableStupidWarnings.h
@@ -78,7 +78,7 @@
     // This warning is a false positive
     #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
   #endif
-  #if __GNUC__==12
+  #if __GNUC__>=12
     // This warning is a false positive
     #pragma GCC diagnostic ignored "-Warray-bounds"
   #endif
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/util/Macros.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/util/Macros.h
index 986c3d4..81986b9 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/util/Macros.h
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/util/Macros.h
@@ -58,6 +58,16 @@
 // Compiler identification, EIGEN_COMP_*
 //------------------------------------------------------------------------------------------
 
+/// \internal Disable NEON features in Intellisense
+#if __INTELLISENSE__
+#ifdef __ARM_NEON
+#undef __ARM_NEON
+#endif
+#ifdef __ARM_NEON__
+#undef __ARM_NEON__
+#endif
+#endif
+
 /// \internal EIGEN_COMP_GNUC set to 1 for all compilers compatible with GCC
 #ifdef __GNUC__
   #define EIGEN_COMP_GNUC (__GNUC__*10+__GNUC_MINOR__)
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseCore/TriangularSolver.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseCore/TriangularSolver.h
index f9c56ba..7cb2c26 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseCore/TriangularSolver.h
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseCore/TriangularSolver.h
@@ -270,11 +270,11 @@
       }
 
 
-      Index count = 0;
+//       Index count = 0;
       // FIXME compute a reference value to filter zeros
       for (typename AmbiVector<Scalar,StorageIndex>::Iterator it(tempVector/*,1e-12*/); it; ++it)
       {
-        ++ count;
+//         ++ count;
 //         std::cerr << "fill " << it.index() << ", " << col << "\n";
 //         std::cout << it.value() << "  ";
         // FIXME use insertBack
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h
index 6f75d50..7aecbca 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h
@@ -75,8 +75,6 @@
   // Identify the relaxed supernodes by postorder traversal of the etree
   Index snode_start; // beginning of a snode 
   StorageIndex k;
-  Index nsuper_et_post = 0; // Number of relaxed snodes in postordered etree 
-  Index nsuper_et = 0; // Number of relaxed snodes in the original etree 
   StorageIndex l; 
   for (j = 0; j < n; )
   {
@@ -88,7 +86,6 @@
       parent = et(j);
     }
     // Found a supernode in postordered etree, j is the last column 
-    ++nsuper_et_post;
     k = StorageIndex(n);
     for (Index i = snode_start; i <= j; ++i)
       k = (std::min)(k, inv_post(i));
@@ -97,7 +94,6 @@
     {
       // This is also a supernode in the original etree
       relax_end(k) = l; // Record last column 
-      ++nsuper_et; 
     }
     else 
     {
@@ -107,7 +103,6 @@
         if (descendants(i) == 0) 
         {
           relax_end(l) = l;
-          ++nsuper_et;
         }
       }
     }
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/StdDeque.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/StdDeque.h
deleted file mode 100644
index 6d47e75..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/StdDeque.h
+++ /dev/null
@@ -1,116 +0,0 @@
-// This file is part of Eigen, a lightweight C++ template library
-// for linear algebra.
-//
-// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
-// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
-//
-// This Source Code Form is subject to the terms of the Mozilla
-// Public License v. 2.0. If a copy of the MPL was not distributed
-// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef EIGEN_STDDEQUE_H
-#define EIGEN_STDDEQUE_H
-
-#include "details.h"
-
-/**
- * This section contains a convenience MACRO which allows an easy specialization of
- * std::deque such that for data types with alignment issues the correct allocator
- * is used automatically.
- */
-#define EIGEN_DEFINE_STL_DEQUE_SPECIALIZATION(...) \
-namespace std \
-{ \
-  template<> \
-  class deque<__VA_ARGS__, std::allocator<__VA_ARGS__> >           \
-    : public deque<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__> > \
-  { \
-    typedef deque<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__> > deque_base; \
-  public: \
-    typedef __VA_ARGS__ value_type; \
-    typedef deque_base::allocator_type allocator_type; \
-    typedef deque_base::size_type size_type;  \
-    typedef deque_base::iterator iterator;  \
-    explicit deque(const allocator_type& a = allocator_type()) : deque_base(a) {}  \
-    template<typename InputIterator> \
-    deque(InputIterator first, InputIterator last, const allocator_type& a = allocator_type()) : deque_base(first, last, a) {} \
-    deque(const deque& c) : deque_base(c) {}  \
-    explicit deque(size_type num, const value_type& val = value_type()) : deque_base(num, val) {} \
-    deque(iterator start_, iterator end_) : deque_base(start_, end_) {}  \
-    deque& operator=(const deque& x) {  \
-      deque_base::operator=(x);  \
-      return *this;  \
-    } \
-  }; \
-}
-
-// check whether we really need the std::deque specialization
-#if !EIGEN_HAS_CXX11_CONTAINERS && !(defined(_GLIBCXX_DEQUE) && (!EIGEN_GNUC_AT_LEAST(4,1))) /* Note that before gcc-4.1 we already have: std::deque::resize(size_type,const T&). */
-
-namespace std {
-
-#define EIGEN_STD_DEQUE_SPECIALIZATION_BODY \
-  public:  \
-    typedef T value_type; \
-    typedef typename deque_base::allocator_type allocator_type; \
-    typedef typename deque_base::size_type size_type;  \
-    typedef typename deque_base::iterator iterator;  \
-    typedef typename deque_base::const_iterator const_iterator;  \
-    explicit deque(const allocator_type& a = allocator_type()) : deque_base(a) {}  \
-    template<typename InputIterator> \
-    deque(InputIterator first, InputIterator last, const allocator_type& a = allocator_type()) \
-    : deque_base(first, last, a) {} \
-    deque(const deque& c) : deque_base(c) {}  \
-    explicit deque(size_type num, const value_type& val = value_type()) : deque_base(num, val) {} \
-    deque(iterator start_, iterator end_) : deque_base(start_, end_) {}  \
-    deque& operator=(const deque& x) {  \
-      deque_base::operator=(x);  \
-      return *this;  \
-    }
-
-  template<typename T>
-  class deque<T,EIGEN_ALIGNED_ALLOCATOR<T> >
-    : public deque<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T),
-                   Eigen::aligned_allocator_indirection<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T)> >
-{
-  typedef deque<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T),
-                Eigen::aligned_allocator_indirection<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T)> > deque_base;
-  EIGEN_STD_DEQUE_SPECIALIZATION_BODY
-
-  void resize(size_type new_size)
-  { resize(new_size, T()); }
-
-#if defined(_DEQUE_)
-  // workaround MSVC std::deque implementation
-  void resize(size_type new_size, const value_type& x)
-  {
-    if (deque_base::size() < new_size)
-      deque_base::_Insert_n(deque_base::end(), new_size - deque_base::size(), x);
-    else if (new_size < deque_base::size())
-      deque_base::erase(deque_base::begin() + new_size, deque_base::end());
-  }
-  void push_back(const value_type& x)
-  { deque_base::push_back(x); } 
-  void push_front(const value_type& x)
-  { deque_base::push_front(x); }
-  using deque_base::insert;  
-  iterator insert(const_iterator position, const value_type& x)
-  { return deque_base::insert(position,x); }
-  void insert(const_iterator position, size_type new_size, const value_type& x)
-  { deque_base::insert(position, new_size, x); }
-#else
-  // default implementation which should always work.
-  void resize(size_type new_size, const value_type& x)
-  {
-    if (new_size < deque_base::size())
-      deque_base::erase(deque_base::begin() + new_size, deque_base::end());
-    else if (new_size > deque_base::size())
-      deque_base::insert(deque_base::end(), new_size - deque_base::size(), x);
-  }
-#endif
-  };
-}
-
-#endif // check whether specialization is actually required
-
-#endif // EIGEN_STDDEQUE_H
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/StdList.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/StdList.h
deleted file mode 100644
index 8ba3fad..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/StdList.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// This file is part of Eigen, a lightweight C++ template library
-// for linear algebra.
-//
-// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
-//
-// This Source Code Form is subject to the terms of the Mozilla
-// Public License v. 2.0. If a copy of the MPL was not distributed
-// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef EIGEN_STDLIST_H
-#define EIGEN_STDLIST_H
-
-#include "details.h"
-
-/**
- * This section contains a convenience MACRO which allows an easy specialization of
- * std::list such that for data types with alignment issues the correct allocator
- * is used automatically.
- */
-#define EIGEN_DEFINE_STL_LIST_SPECIALIZATION(...) \
-namespace std \
-{ \
-  template<> \
-  class list<__VA_ARGS__, std::allocator<__VA_ARGS__> >           \
-    : public list<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__> > \
-  { \
-    typedef list<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__> > list_base; \
-  public: \
-    typedef __VA_ARGS__ value_type; \
-    typedef list_base::allocator_type allocator_type; \
-    typedef list_base::size_type size_type;  \
-    typedef list_base::iterator iterator;  \
-    explicit list(const allocator_type& a = allocator_type()) : list_base(a) {}  \
-    template<typename InputIterator> \
-    list(InputIterator first, InputIterator last, const allocator_type& a = allocator_type()) : list_base(first, last, a) {} \
-    list(const list& c) : list_base(c) {}  \
-    explicit list(size_type num, const value_type& val = value_type()) : list_base(num, val) {} \
-    list(iterator start_, iterator end_) : list_base(start_, end_) {}  \
-    list& operator=(const list& x) {  \
-      list_base::operator=(x);  \
-      return *this;  \
-    } \
-  }; \
-}
-
-// check whether we really need the std::list specialization
-#if !EIGEN_HAS_CXX11_CONTAINERS && !(defined(_GLIBCXX_LIST) && (!EIGEN_GNUC_AT_LEAST(4,1))) /* Note that before gcc-4.1 we already have: std::list::resize(size_type,const T&). */
-
-namespace std
-{
-
-#define EIGEN_STD_LIST_SPECIALIZATION_BODY \
-  public:  \
-    typedef T value_type; \
-    typedef typename list_base::allocator_type allocator_type; \
-    typedef typename list_base::size_type size_type;  \
-    typedef typename list_base::iterator iterator;  \
-    typedef typename list_base::const_iterator const_iterator;  \
-    explicit list(const allocator_type& a = allocator_type()) : list_base(a) {}  \
-    template<typename InputIterator> \
-    list(InputIterator first, InputIterator last, const allocator_type& a = allocator_type()) \
-    : list_base(first, last, a) {} \
-    list(const list& c) : list_base(c) {}  \
-    explicit list(size_type num, const value_type& val = value_type()) : list_base(num, val) {} \
-    list(iterator start_, iterator end_) : list_base(start_, end_) {}  \
-    list& operator=(const list& x) {  \
-    list_base::operator=(x);  \
-    return *this; \
-  }
-
-  template<typename T>
-  class list<T,EIGEN_ALIGNED_ALLOCATOR<T> >
-    : public list<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T),
-                  Eigen::aligned_allocator_indirection<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T)> >
-  {
-    typedef list<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T),
-                 Eigen::aligned_allocator_indirection<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T)> > list_base;
-    EIGEN_STD_LIST_SPECIALIZATION_BODY
-
-    void resize(size_type new_size)
-    { resize(new_size, T()); }
-
-    void resize(size_type new_size, const value_type& x)
-    {
-      if (list_base::size() < new_size)
-        list_base::insert(list_base::end(), new_size - list_base::size(), x);
-      else
-        while (new_size < list_base::size()) list_base::pop_back();
-    }
-
-#if defined(_LIST_)
-    // workaround MSVC std::list implementation
-    void push_back(const value_type& x)
-    { list_base::push_back(x); } 
-    using list_base::insert;  
-    iterator insert(const_iterator position, const value_type& x)
-    { return list_base::insert(position,x); }
-    void insert(const_iterator position, size_type new_size, const value_type& x)
-    { list_base::insert(position, new_size, x); }
-#endif
-  };
-}
-
-#endif // check whether specialization is actually required
-
-#endif // EIGEN_STDLIST_H
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/StdVector.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/StdVector.h
deleted file mode 100644
index 9fcf19b..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/StdVector.h
+++ /dev/null
@@ -1,131 +0,0 @@
-// This file is part of Eigen, a lightweight C++ template library
-// for linear algebra.
-//
-// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
-// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
-//
-// This Source Code Form is subject to the terms of the Mozilla
-// Public License v. 2.0. If a copy of the MPL was not distributed
-// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef EIGEN_STDVECTOR_H
-#define EIGEN_STDVECTOR_H
-
-#include "details.h"
-
-/**
- * This section contains a convenience MACRO which allows an easy specialization of
- * std::vector such that for data types with alignment issues the correct allocator
- * is used automatically.
- */
-#define EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(...) \
-namespace std \
-{ \
-  template<> \
-  class vector<__VA_ARGS__, std::allocator<__VA_ARGS__> >  \
-    : public vector<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__> > \
-  { \
-    typedef vector<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__> > vector_base; \
-  public: \
-    typedef __VA_ARGS__ value_type; \
-    typedef vector_base::allocator_type allocator_type; \
-    typedef vector_base::size_type size_type;  \
-    typedef vector_base::iterator iterator;  \
-    explicit vector(const allocator_type& a = allocator_type()) : vector_base(a) {}  \
-    template<typename InputIterator> \
-    vector(InputIterator first, InputIterator last, const allocator_type& a = allocator_type()) : vector_base(first, last, a) {} \
-    vector(const vector& c) : vector_base(c) {}  \
-    explicit vector(size_type num, const value_type& val = value_type()) : vector_base(num, val) {} \
-    vector(iterator start_, iterator end_) : vector_base(start_, end_) {}  \
-    vector& operator=(const vector& x) {  \
-      vector_base::operator=(x);  \
-      return *this;  \
-    } \
-  }; \
-}
-
-// Don't specialize if containers are implemented according to C++11
-#if !EIGEN_HAS_CXX11_CONTAINERS
-
-namespace std {
-
-#define EIGEN_STD_VECTOR_SPECIALIZATION_BODY \
-  public:  \
-    typedef T value_type; \
-    typedef typename vector_base::allocator_type allocator_type; \
-    typedef typename vector_base::size_type size_type;  \
-    typedef typename vector_base::iterator iterator;  \
-    typedef typename vector_base::const_iterator const_iterator;  \
-    explicit vector(const allocator_type& a = allocator_type()) : vector_base(a) {}  \
-    template<typename InputIterator> \
-    vector(InputIterator first, InputIterator last, const allocator_type& a = allocator_type()) \
-    : vector_base(first, last, a) {} \
-    vector(const vector& c) : vector_base(c) {}  \
-    explicit vector(size_type num, const value_type& val = value_type()) : vector_base(num, val) {} \
-    vector(iterator start_, iterator end_) : vector_base(start_, end_) {}  \
-    vector& operator=(const vector& x) {  \
-      vector_base::operator=(x);  \
-      return *this;  \
-    }
-
-  template<typename T>
-  class vector<T,EIGEN_ALIGNED_ALLOCATOR<T> >
-    : public vector<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T),
-                    Eigen::aligned_allocator_indirection<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T)> >
-{
-  typedef vector<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T),
-                 Eigen::aligned_allocator_indirection<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T)> > vector_base;
-  EIGEN_STD_VECTOR_SPECIALIZATION_BODY
-
-  void resize(size_type new_size)
-  { resize(new_size, T()); }
-
-#if defined(_VECTOR_)
-  // workaround MSVC std::vector implementation
-  void resize(size_type new_size, const value_type& x)
-  {
-    if (vector_base::size() < new_size)
-      vector_base::_Insert_n(vector_base::end(), new_size - vector_base::size(), x);
-    else if (new_size < vector_base::size())
-      vector_base::erase(vector_base::begin() + new_size, vector_base::end());
-  }
-  void push_back(const value_type& x)
-  { vector_base::push_back(x); } 
-  using vector_base::insert;  
-  iterator insert(const_iterator position, const value_type& x)
-  { return vector_base::insert(position,x); }
-  void insert(const_iterator position, size_type new_size, const value_type& x)
-  { vector_base::insert(position, new_size, x); }
-#elif defined(_GLIBCXX_VECTOR) && (!(EIGEN_GNUC_AT_LEAST(4,1)))
-  /* Note that before gcc-4.1 we already have: std::vector::resize(size_type,const T&).
-   * However, this specialization is still needed to make the above EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION trick to work. */
-  void resize(size_type new_size, const value_type& x)
-  {
-    vector_base::resize(new_size,x);
-  }
-#elif defined(_GLIBCXX_VECTOR) && EIGEN_GNUC_AT_LEAST(4,2)
-  // workaround GCC std::vector implementation
-  void resize(size_type new_size, const value_type& x)
-  {
-    if (new_size < vector_base::size())
-      vector_base::_M_erase_at_end(this->_M_impl._M_start + new_size);
-    else
-      vector_base::insert(vector_base::end(), new_size - vector_base::size(), x);
-  }
-#else
-  // either GCC 4.1 or non-GCC
-  // default implementation which should always work.
-  void resize(size_type new_size, const value_type& x)
-  {
-    if (new_size < vector_base::size())
-      vector_base::erase(vector_base::begin() + new_size, vector_base::end());
-    else if (new_size > vector_base::size())
-      vector_base::insert(vector_base::end(), new_size - vector_base::size(), x);
-  }
-#endif
-  };
-}
-#endif // !EIGEN_HAS_CXX11_CONTAINERS
-
-
-#endif // EIGEN_STDVECTOR_H
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/details.h b/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/details.h
deleted file mode 100644
index 2cfd13e..0000000
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/StlSupport/details.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// This file is part of Eigen, a lightweight C++ template library
-// for linear algebra.
-//
-// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
-// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
-//
-// This Source Code Form is subject to the terms of the Mozilla
-// Public License v. 2.0. If a copy of the MPL was not distributed
-// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef EIGEN_STL_DETAILS_H
-#define EIGEN_STL_DETAILS_H
-
-#ifndef EIGEN_ALIGNED_ALLOCATOR
-  #define EIGEN_ALIGNED_ALLOCATOR Eigen::aligned_allocator
-#endif
-
-namespace Eigen {
-
-  // This one is needed to prevent reimplementing the whole std::vector.
-  template <class T>
-  class aligned_allocator_indirection : public EIGEN_ALIGNED_ALLOCATOR<T>
-  {
-  public:
-    typedef std::size_t     size_type;
-    typedef std::ptrdiff_t  difference_type;
-    typedef T*              pointer;
-    typedef const T*        const_pointer;
-    typedef T&              reference;
-    typedef const T&        const_reference;
-    typedef T               value_type;
-
-    template<class U>
-    struct rebind
-    {
-      typedef aligned_allocator_indirection<U> other;
-    };
-
-    aligned_allocator_indirection() {}
-    aligned_allocator_indirection(const aligned_allocator_indirection& ) : EIGEN_ALIGNED_ALLOCATOR<T>() {}
-    aligned_allocator_indirection(const EIGEN_ALIGNED_ALLOCATOR<T>& ) {}
-    template<class U>
-    aligned_allocator_indirection(const aligned_allocator_indirection<U>& ) {}
-    template<class U>
-    aligned_allocator_indirection(const EIGEN_ALIGNED_ALLOCATOR<U>& ) {}
-    ~aligned_allocator_indirection() {}
-  };
-
-#if EIGEN_COMP_MSVC
-
-  // sometimes, MSVC detects, at compile time, that the argument x
-  // in std::vector::resize(size_t s,T x) won't be aligned and generate an error
-  // even if this function is never called. Whence this little wrapper.
-#define EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T) \
-  typename Eigen::internal::conditional< \
-    Eigen::internal::is_arithmetic<T>::value, \
-    T, \
-    Eigen::internal::workaround_msvc_stl_support<T> \
-  >::type
-
-  namespace internal {
-  template<typename T> struct workaround_msvc_stl_support : public T
-  {
-    inline workaround_msvc_stl_support() : T() {}
-    inline workaround_msvc_stl_support(const T& other) : T(other) {}
-    inline operator T& () { return *static_cast<T*>(this); }
-    inline operator const T& () const { return *static_cast<const T*>(this); }
-    template<typename OtherT>
-    inline T& operator=(const OtherT& other)
-    { T::operator=(other); return *this; }
-    inline workaround_msvc_stl_support& operator=(const workaround_msvc_stl_support& other)
-    { T::operator=(other); return *this; }
-  };
-  }
-
-#else
-
-#define EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T) T
-
-#endif
-
-}
-
-#endif // EIGEN_STL_DETAILS_H
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem.hpp
index cb28ff0..650d05d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/abs.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/abs.hpp
index 0a3ada6..6d7b66d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/abs.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/abs.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/acos.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/acos.hpp
index 9d3bc07..a47003d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/acos.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/acos.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/acosh.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/acosh.hpp
index 79579d7..8767200 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/acosh.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/acosh.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/asin.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/asin.hpp
index 210d9fc..6a79e87 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/asin.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/asin.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/asinh.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/asinh.hpp
index dfad57e..a5f3ff6 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/asinh.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/asinh.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atan.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atan.hpp
index 9aea85b..3f46974 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atan.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atan.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atan2.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atan2.hpp
index 97c8d6a..5ca55b7 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atan2.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atan2.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atanh.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atanh.hpp
index 2e1cb76..dfb4dc3 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atanh.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/atanh.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/beta.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/beta.hpp
index e888e47..e43d4fc 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/beta.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/beta.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/binomial_coef.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/binomial_coef.hpp
index fb050a2..0fc17f3 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/binomial_coef.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/binomial_coef.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/ceil.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/ceil.hpp
index e8570ab..ff1097b 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/ceil.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/ceil.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/copysign.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/copysign.hpp
index a3bab74..c1741f7 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/copysign.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/copysign.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/cos.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/cos.hpp
index 0e98012..82f4c60 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/cos.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/cos.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/cosh.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/cosh.hpp
index b3cebb8..fc89c0d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/cosh.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/cosh.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/erf.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/erf.hpp
index afec09e..d0bc83a 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/erf.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/erf.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/erf_inv.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/erf_inv.hpp
index a93f8db..412d686 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/erf_inv.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/erf_inv.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/exp.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/exp.hpp
index 0c66829..595ffc2 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/exp.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/exp.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
@@ -28,6 +28,30 @@
 namespace internal
 {
 
+// see https://en.wikipedia.org/wiki/Euler%27s_continued_fraction_formula
+
+#if __cplusplus >= 201402L // C++14 version
+
+template<typename T>
+constexpr
+T
+exp_cf_recur(const T x, const int depth_end)
+noexcept
+{
+    int depth = GCEM_EXP_MAX_ITER_SMALL - 1;
+    T res = T(1);
+
+    while (depth > depth_end - 1) {
+        res = T(1) + x/T(depth - 1) - x/depth/res;
+
+        --depth;
+    }
+
+    return res;
+}
+
+#else // C++11 version
+
 template<typename T>
 constexpr
 T
@@ -36,20 +60,20 @@
 {
     return( depth < GCEM_EXP_MAX_ITER_SMALL ? \
             // if
-                depth == 1 ? \
-                    T(1) - x/exp_cf_recur(x,depth+1) : 
-                    T(1) + x/T(depth - 1) - x/depth/exp_cf_recur(x,depth+1) : 
+                T(1) + x/T(depth - 1) - x/depth/exp_cf_recur(x,depth+1) : 
              // else
                 T(1) );
 }
 
+#endif
+
 template<typename T>
 constexpr
 T
 exp_cf(const T x)
 noexcept
 {
-    return( T(1)/exp_cf_recur(x,1) );
+    return( T(1) / (T(1) - x / exp_cf_recur(x,2)) );
 }
 
 template<typename T>
@@ -72,7 +96,7 @@
             //
             is_neginf(x) ? \
                 T(0) :
-            //
+            // indistinguishable from zero
             GCLIM<T>::min() > abs(x) ? \
                 T(1) : 
             //
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/expm1.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/expm1.hpp
index 11b2eb9..70c9ecf 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/expm1.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/expm1.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/factorial.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/factorial.hpp
index 539c3f3..ffb9c82 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/factorial.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/factorial.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_exponent.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_exponent.hpp
index 710adce..200e4e9 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_exponent.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_exponent.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
@@ -34,10 +34,20 @@
 find_exponent(const T x, const llint_t exponent)
 noexcept
 {
-    return( x < T(1)  ? \
-                find_exponent(x*T(10),exponent - llint_t(1)) :
+    return( // < 1
+            x < T(1e-03)  ? \
+                find_exponent(x * T(1e+04), exponent - llint_t(4)) :
+            x < T(1e-01)  ? \
+                find_exponent(x * T(1e+02), exponent - llint_t(2)) :
+            x < T(1)  ? \
+                find_exponent(x * T(10), exponent - llint_t(1)) :
+            // > 10
             x > T(10) ? \
-                find_exponent(x/T(10),exponent + llint_t(1)) :
+                find_exponent(x / T(10), exponent + llint_t(1)) :
+            x > T(1e+02) ? \
+                find_exponent(x / T(1e+02), exponent + llint_t(2)) :
+            x > T(1e+04) ? \
+                find_exponent(x / T(1e+04), exponent + llint_t(4)) :
             // else
                 exponent );
 }
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_fraction.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_fraction.hpp
index d9769e6..5ed3d26 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_fraction.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_fraction.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_whole.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_whole.hpp
index bd5e0b9..d193632 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_whole.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/find_whole.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/floor.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/floor.hpp
index c60ff6a..8b260ff 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/floor.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/floor.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/fmod.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/fmod.hpp
index c804ce6..02459ef 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/fmod.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/fmod.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/gcd.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/gcd.hpp
index 4a10bbe..1e277fb 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/gcd.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/gcd.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/gcem_options.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/gcem_options.hpp
index cd2747c..4113738 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/gcem_options.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/gcem_options.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
@@ -53,7 +53,7 @@
 #endif
 
 #ifndef GCEM_VERSION_MINOR
-    #define GCEM_VERSION_MINOR 16
+    #define GCEM_VERSION_MINOR 17
 #endif
 
 #ifndef GCEM_VERSION_PATCH
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/hypot.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/hypot.hpp
index 5a805ed..01ad4e9 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/hypot.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/hypot.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_beta.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_beta.hpp
index 5645dbe..dbb9f60 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_beta.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_beta.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_beta_inv.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_beta_inv.hpp
index f7fdfa0..9f575a3 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_beta_inv.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_beta_inv.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_gamma.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_gamma.hpp
index 38734a5..9ee4146 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_gamma.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_gamma.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_gamma_inv.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_gamma_inv.hpp
index 1e57fc1..e5976d0 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_gamma_inv.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/incomplete_gamma_inv.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/inv_sqrt.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/inv_sqrt.hpp
index 0200f11..d0e33fb 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/inv_sqrt.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/inv_sqrt.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_even.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_even.hpp
index fa925bb..de0641d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_even.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_even.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_finite.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_finite.hpp
index 25f2e3c..b632fa3 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_finite.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_finite.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_inf.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_inf.hpp
index 627c509..568614f 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_inf.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_inf.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_nan.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_nan.hpp
index a7a1af3..a3fcbc6 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_nan.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_nan.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_odd.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_odd.hpp
index e6da720..a74a8d3 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_odd.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/is_odd.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lbeta.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lbeta.hpp
index 2213849..60c87b4 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lbeta.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lbeta.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lcm.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lcm.hpp
index b0d8fb4..a7ca776 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lcm.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lcm.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lgamma.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lgamma.hpp
index 5d78eb3..507c6d4 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lgamma.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lgamma.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lmgamma.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lmgamma.hpp
index 76bf833..58915dc 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lmgamma.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/lmgamma.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log.hpp
index 0d83e97..c2e24b0 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
@@ -31,6 +31,28 @@
 // continued fraction seems to be a better approximation for small x
 // see http://functions.wolfram.com/ElementaryFunctions/Log/10/0005/
 
+#if __cplusplus >= 201402L // C++14 version
+
+template<typename T>
+constexpr
+T
+log_cf_main(const T xx, const int depth_end)
+noexcept
+{
+    int depth = GCEM_LOG_MAX_ITER_SMALL - 1;
+    T res = T(2*(depth+1) - 1);
+
+    while (depth > depth_end - 1) {
+        res = T(2*depth - 1) - T(depth*depth) * xx / res;
+
+        --depth;
+    }
+
+    return res;
+}
+
+#else // C++11 version
+
 template<typename T>
 constexpr
 T
@@ -39,11 +61,13 @@
 {
     return( depth < GCEM_LOG_MAX_ITER_SMALL ? \
             // if 
-                T(2*depth - 1) - T(depth*depth)*xx/log_cf_main(xx,depth+1) :
+                T(2*depth - 1) - T(depth*depth) * xx / log_cf_main(xx,depth+1) :
             // else 
                 T(2*depth - 1) );
 }
 
+#endif
+
 template<typename T>
 constexpr
 T
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log10.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log10.hpp
index 4a3c37d..cda8894 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log10.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log10.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log1p.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log1p.hpp
index 3883b22..ccd08b8 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log1p.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log1p.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log2.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log2.hpp
index 56b7f8e..a97fed4 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log2.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log2.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log_binomial_coef.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log_binomial_coef.hpp
index 7aa9a2b..2bcaadd 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log_binomial_coef.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/log_binomial_coef.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/mantissa.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/mantissa.hpp
index df6152b..af23ea2 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/mantissa.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/mantissa.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
@@ -35,9 +35,9 @@
 noexcept
 {
     return( x < T(1) ? \
-                mantissa(x*T(10)) : 
+                mantissa(x * T(10)) : 
             x > T(10) ? \
-                mantissa(x/T(10)) :
+                mantissa(x / T(10)) :
             // else
                 x );
 }
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/max.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/max.hpp
index 4aed84f..ddc3e4e 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/max.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/max.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/min.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/min.hpp
index d593dbc..5ce70b3 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/min.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/min.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/neg_zero.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/neg_zero.hpp
index db33f87..79d24a4 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/neg_zero.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/neg_zero.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/pow.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/pow.hpp
index 166a8c1..3891ede 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/pow.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/pow.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/pow_integral.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/pow_integral.hpp
index 3a902ca..4e67155 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/pow_integral.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/pow_integral.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/quadrature/gauss_legendre_30.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/quadrature/gauss_legendre_30.hpp
index e609b89..295f43d 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/quadrature/gauss_legendre_30.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/quadrature/gauss_legendre_30.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/quadrature/gauss_legendre_50.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/quadrature/gauss_legendre_50.hpp
index 44281f9..d4e448c 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/quadrature/gauss_legendre_50.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/quadrature/gauss_legendre_50.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/round.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/round.hpp
index 43d7a5e..9ac4a09 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/round.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/round.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sgn.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sgn.hpp
index 605a35a..e2eec9e 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sgn.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sgn.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/signbit.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/signbit.hpp
index e207a5a..282e244 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/signbit.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/signbit.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sin.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sin.hpp
index 128cd32..56c8dca 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sin.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sin.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sinh.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sinh.hpp
index 5355301..fe3ecdd 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sinh.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sinh.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sqrt.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sqrt.hpp
index 0fd559d..1b2753c 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sqrt.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/sqrt.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
@@ -37,16 +37,37 @@
     return( abs(xn - x/xn) / (T(1) + xn) < GCLIM<T>::min() ? \
             // if
                 xn :
-            count < GCEM_SQRT_MAX_ITER ? \
             // else
-                sqrt_recur(x, T(0.5)*(xn + x/xn), count+1) :
-                xn );
+                count < GCEM_SQRT_MAX_ITER ? \
+                // if
+                    sqrt_recur(x, T(0.5)*(xn + x/xn), count+1) :
+                // else
+                    xn );
 }
 
 template<typename T>
 constexpr
 T
-sqrt_check(const T x, const T m_val)
+sqrt_simplify(const T x, const T m_val)
+noexcept
+{
+    return( x > T(1e+08) ? \
+                sqrt_simplify(x / T(1e+08), T(1e+04) * m_val) :
+            x > T(1e+06) ? \
+                sqrt_simplify(x / T(1e+06), T(1e+03) * m_val) :
+            x > T(1e+04) ? \
+                sqrt_simplify(x / T(1e+04), T(1e+02) * m_val) :
+            x > T(100) ? \
+                sqrt_simplify(x / T(100), T(10) * m_val) :
+            x > T(4) ? \
+                sqrt_simplify(x / T(4), T(2) * m_val) :
+                m_val * sqrt_recur(x, x / T(2), 0) );
+}
+
+template<typename T>
+constexpr
+T
+sqrt_check(const T x)
 noexcept
 {
     return( is_nan(x) ? \
@@ -63,9 +84,7 @@
             GCLIM<T>::min() > abs(T(1) - x) ? \
                 x :
             // else
-            x > T(4) ? \
-                sqrt_check(x/T(4), T(2)*m_val) :
-                m_val * sqrt_recur(x, x/T(2), 0) );
+            sqrt_simplify(x, T(1)) );
 }
 
 }
@@ -84,7 +103,7 @@
 sqrt(const T x)
 noexcept
 {
-    return internal::sqrt_check( static_cast<return_t<T>>(x), return_t<T>(1) );
+    return internal::sqrt_check( static_cast<return_t<T>>(x) );
 }
 
 #endif
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tan.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tan.hpp
index e53f5c8..386cce0 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tan.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tan.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tanh.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tanh.hpp
index 109d751..30b4318 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tanh.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tanh.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tgamma.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tgamma.hpp
index 5a9ae97..deffd3a 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tgamma.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/tgamma.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/trunc.hpp b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/trunc.hpp
index 4e19ef9..af3f448 100644
--- a/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/trunc.hpp
+++ b/third_party/allwpilib/wpimath/src/main/native/thirdparty/gcem/include/gcem_incl/trunc.hpp
@@ -1,6 +1,6 @@
 /*################################################################################
   ##
-  ##   Copyright (C) 2016-2022 Keith O'Hara
+  ##   Copyright (C) 2016-2023 Keith O'Hara
   ##
   ##   This file is part of the GCE-Math C++ library.
   ##
diff --git a/third_party/allwpilib/wpimath/src/main/proto/controller.proto b/third_party/allwpilib/wpimath/src/main/proto/controller.proto
new file mode 100644
index 0000000..0d0d3fb
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/proto/controller.proto
@@ -0,0 +1,37 @@
+syntax = "proto3";
+
+package wpi.proto;
+
+option java_package = "edu.wpi.first.math.proto";
+
+message ProtobufArmFeedforward {
+  double ks = 1;
+  double kg = 2;
+  double kv = 3;
+  double ka = 4;
+}
+
+message ProtobufDifferentialDriveFeedforward {
+  double kv_linear = 1;
+  double ka_linear = 2;
+  double kv_angular = 3;
+  double ka_angular = 4;
+}
+
+message ProtobufElevatorFeedforward {
+  double ks = 1;
+  double kg = 2;
+  double kv = 3;
+  double ka = 4;
+}
+
+message ProtobufSimpleMotorFeedforward {
+  double ks = 1;
+  double kv = 2;
+  double ka = 3;
+}
+
+message ProtobufDifferentialDriveWheelVoltages {
+  double left = 1;
+  double right = 2;
+}
diff --git a/third_party/allwpilib/wpimath/src/main/proto/geometry2d.proto b/third_party/allwpilib/wpimath/src/main/proto/geometry2d.proto
new file mode 100644
index 0000000..d52da45
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/proto/geometry2d.proto
@@ -0,0 +1,30 @@
+syntax = "proto3";
+
+package wpi.proto;
+
+option java_package = "edu.wpi.first.math.proto";
+
+message ProtobufTranslation2d {
+  double x = 1;
+  double y = 2;
+}
+
+message ProtobufRotation2d {
+  double value = 1;
+}
+
+message ProtobufPose2d {
+  ProtobufTranslation2d translation = 1;
+  ProtobufRotation2d rotation = 2;
+}
+
+message ProtobufTransform2d {
+  ProtobufTranslation2d translation = 1;
+  ProtobufRotation2d rotation = 2;
+}
+
+message ProtobufTwist2d {
+  double dx = 1;
+  double dy = 2;
+  double dtheta = 3;
+}
diff --git a/third_party/allwpilib/wpimath/src/main/proto/geometry3d.proto b/third_party/allwpilib/wpimath/src/main/proto/geometry3d.proto
new file mode 100644
index 0000000..23b4d64
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/proto/geometry3d.proto
@@ -0,0 +1,41 @@
+syntax = "proto3";
+
+package wpi.proto;
+
+option java_package = "edu.wpi.first.math.proto";
+
+message ProtobufTranslation3d {
+  double x = 1;
+  double y = 2;
+  double z = 3;
+}
+
+message ProtobufQuaternion {
+  double w = 1;
+  double x = 2;
+  double y = 3;
+  double z = 4;
+}
+
+message ProtobufRotation3d {
+  ProtobufQuaternion q = 1;
+}
+
+message ProtobufPose3d {
+  ProtobufTranslation3d translation = 1;
+  ProtobufRotation3d rotation = 2;
+}
+
+message ProtobufTransform3d {
+  ProtobufTranslation3d translation = 1;
+  ProtobufRotation3d rotation = 2;
+}
+
+message ProtobufTwist3d {
+  double dx = 1;
+  double dy = 2;
+  double dz = 3;
+  double rx = 4;
+  double ry = 5;
+  double rz = 6;
+}
diff --git a/third_party/allwpilib/wpimath/src/main/proto/kinematics.proto b/third_party/allwpilib/wpimath/src/main/proto/kinematics.proto
new file mode 100644
index 0000000..8fdbf2d
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/proto/kinematics.proto
@@ -0,0 +1,64 @@
+syntax = "proto3";
+
+package wpi.proto;
+
+import "geometry2d.proto";
+
+option java_package = "edu.wpi.first.math.proto";
+
+message ProtobufChassisSpeeds {
+  double vx = 1;
+  double vy = 2;
+  double omega = 3;
+}
+
+message ProtobufDifferentialDriveKinematics {
+  double track_width = 1;
+}
+
+message ProtobufDifferentialDriveWheelSpeeds {
+  double left = 1;
+  double right = 2;
+}
+
+message ProtobufMecanumDriveKinematics {
+  ProtobufTranslation2d front_left = 1;
+  ProtobufTranslation2d front_right = 2;
+  ProtobufTranslation2d rear_left = 3;
+  ProtobufTranslation2d rear_right = 4;
+}
+
+message ProtobufMecanumDriveMotorVoltages {
+  double front_left = 1;
+  double front_right = 2;
+  double rear_left = 3;
+  double rear_right = 4;
+}
+
+message ProtobufMecanumDriveWheelPositions {
+  double front_left = 1;
+  double front_right = 2;
+  double rear_left = 3;
+  double rear_right = 4;
+}
+
+message ProtobufMecanumDriveWheelSpeeds {
+  double front_left = 1;
+  double front_right = 2;
+  double rear_left = 3;
+  double rear_right = 4;
+}
+
+message ProtobufSwerveDriveKinematics {
+  repeated ProtobufTranslation2d modules = 1;
+}
+
+message ProtobufSwerveModulePosition {
+  double distance = 1;
+  ProtobufRotation2d angle = 2;
+}
+
+message ProtobufSwerveModuleState {
+  double speed = 1;
+  ProtobufRotation2d angle = 2;
+}
diff --git a/third_party/allwpilib/wpimath/src/main/proto/plant.proto b/third_party/allwpilib/wpimath/src/main/proto/plant.proto
new file mode 100644
index 0000000..d0d9eab
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/proto/plant.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+
+package wpi.proto;
+
+option java_package = "edu.wpi.first.math.proto";
+
+message ProtobufDCMotor {
+  double nominal_voltage = 1;
+  double stall_torque = 2;
+  double stall_current = 3;
+  double free_current = 4;
+  double free_speed = 5;
+  double r = 6;
+  double kv = 7;
+  double kt = 8;
+}
diff --git a/third_party/allwpilib/wpimath/src/main/proto/spline.proto b/third_party/allwpilib/wpimath/src/main/proto/spline.proto
new file mode 100644
index 0000000..ef0a3c9
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/proto/spline.proto
@@ -0,0 +1,19 @@
+syntax = "proto3";
+
+package wpi.proto;
+
+option java_package = "edu.wpi.first.math.proto";
+
+message ProtobufCubicHermiteSpline {
+  repeated double x_initial = 1;
+  repeated double x_final = 2;
+  repeated double y_initial = 3;
+  repeated double y_final = 4;
+}
+
+message ProtobufQuinticHermiteSpline {
+  repeated double x_initial = 1;
+  repeated double x_final = 2;
+  repeated double y_initial = 3;
+  repeated double y_final = 4;
+}
diff --git a/third_party/allwpilib/wpimath/src/main/proto/system.proto b/third_party/allwpilib/wpimath/src/main/proto/system.proto
new file mode 100644
index 0000000..4818c19
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/proto/system.proto
@@ -0,0 +1,17 @@
+syntax = "proto3";
+
+package wpi.proto;
+
+import "wpimath.proto";
+
+option java_package = "edu.wpi.first.math.proto";
+
+message ProtobufLinearSystem {
+  uint32 num_states = 1;
+  uint32 num_inputs = 2;
+  uint32 num_outputs = 3;
+  ProtobufMatrix a = 4;
+  ProtobufMatrix b = 5;
+  ProtobufMatrix c = 6;
+  ProtobufMatrix d = 7;
+}
diff --git a/third_party/allwpilib/wpimath/src/main/proto/trajectory.proto b/third_party/allwpilib/wpimath/src/main/proto/trajectory.proto
new file mode 100644
index 0000000..a37a501
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/proto/trajectory.proto
@@ -0,0 +1,20 @@
+syntax = "proto3";
+
+package wpi.proto;
+
+import "geometry2d.proto";
+
+option java_package = "edu.wpi.first.math.proto";
+
+message ProtobufTrajectoryState {
+  double time = 1;
+  double velocity = 2;
+  double acceleration = 3;
+  ProtobufPose2d pose = 4;
+  double curvature = 5;
+}
+
+message ProtobufTrajectory {
+  double total_time = 1;
+  repeated ProtobufTrajectoryState states = 2;
+}
diff --git a/third_party/allwpilib/wpimath/src/main/proto/wpimath.proto b/third_party/allwpilib/wpimath/src/main/proto/wpimath.proto
new file mode 100644
index 0000000..06b993a
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/main/proto/wpimath.proto
@@ -0,0 +1,15 @@
+syntax = "proto3";
+
+package wpi.proto;
+
+option java_package = "edu.wpi.first.math.proto";
+
+message ProtobufMatrix {
+  uint32 num_rows = 1;
+  uint32 num_cols = 2;
+  repeated double data = 3;
+}
+
+message ProtobufVector {
+  repeated double rows = 1;
+}
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/ComputerVisionUtilTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/ComputerVisionUtilTest.java
index 2927847..3c281e5 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/ComputerVisionUtilTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/ComputerVisionUtilTest.java
@@ -11,9 +11,14 @@
 import edu.wpi.first.math.geometry.Transform3d;
 import edu.wpi.first.math.geometry.Translation3d;
 import edu.wpi.first.math.util.Units;
+import edu.wpi.first.wpilibj.UtilityClassTest;
 import org.junit.jupiter.api.Test;
 
-class ComputerVisionUtilTest {
+class ComputerVisionUtilTest extends UtilityClassTest<ComputerVisionUtil> {
+  ComputerVisionUtilTest() {
+    super(ComputerVisionUtil.class);
+  }
+
   @Test
   void testObjectToRobotPose() {
     var robot = new Pose3d(1.0, 2.0, 0.0, new Rotation3d(0.0, 0.0, Units.degreesToRadians(30.0)));
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/DARETest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/DARETest.java
new file mode 100644
index 0000000..0d697bd
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/DARETest.java
@@ -0,0 +1,335 @@
+// 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.
+
+package edu.wpi.first.math;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import edu.wpi.first.wpilibj.UtilityClassTest;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.jupiter.api.Test;
+
+class DARETest extends UtilityClassTest<DARE> {
+  DARETest() {
+    super(DARE.class);
+  }
+
+  public static <R extends Num, C extends Num> void assertMatrixEqual(
+      Matrix<R, C> A, Matrix<R, C> B) {
+    for (int i = 0; i < A.getNumRows(); i++) {
+      for (int j = 0; j < A.getNumCols(); j++) {
+        assertEquals(A.get(i, j), B.get(i, j), 1e-4);
+      }
+    }
+  }
+
+  <States extends Num, Inputs extends Num> void assertDARESolution(
+      Matrix<States, States> A,
+      Matrix<States, Inputs> B,
+      Matrix<States, States> Q,
+      Matrix<Inputs, Inputs> R,
+      Matrix<States, States> X) {
+    // Check that X is the solution to the DARE
+    // Y = AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
+    var Y =
+        (A.transpose().times(X).times(A))
+            .minus(X)
+            .minus(
+                (A.transpose().times(X).times(B))
+                    .times((B.transpose().times(X).times(B).plus(R)).inv())
+                    .times(B.transpose().times(X).times(A)))
+            .plus(Q);
+    assertMatrixEqual(
+        new Matrix<States, States>(new SimpleMatrix(Y.getNumRows(), Y.getNumCols())), Y);
+  }
+
+  <States extends Num, Inputs extends Num> void assertDARESolution(
+      Matrix<States, States> A,
+      Matrix<States, Inputs> B,
+      Matrix<States, States> Q,
+      Matrix<Inputs, Inputs> R,
+      Matrix<States, Inputs> N,
+      Matrix<States, States> X) {
+    // Check that X is the solution to the DARE
+    // Y = AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
+    var Y =
+        (A.transpose().times(X).times(A))
+            .minus(X)
+            .minus(
+                (A.transpose().times(X).times(B).plus(N))
+                    .times((B.transpose().times(X).times(B).plus(R)).inv())
+                    .times(B.transpose().times(X).times(A).plus(N.transpose())))
+            .plus(Q);
+    assertMatrixEqual(
+        new Matrix<States, States>(new SimpleMatrix(Y.getNumRows(), Y.getNumCols())), Y);
+  }
+
+  @Test
+  void testNonInvertibleA_ABQR() {
+    // Example 2 of "On the Numerical Solution of the Discrete-Time Algebraic
+    // Riccati Equation"
+
+    var A =
+        new Matrix<>(
+            Nat.N4(), Nat.N4(), new double[] {0.5, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0});
+    var B = new Matrix<>(Nat.N4(), Nat.N1(), new double[] {0, 0, 0, 1});
+    var Q =
+        new Matrix<>(
+            Nat.N4(), Nat.N4(), new double[] {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
+    var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {0.25});
+
+    var X = DARE.dare(A, B, Q, R);
+    assertMatrixEqual(X, X.transpose());
+    assertDARESolution(A, B, Q, R, X);
+  }
+
+  @Test
+  void testNonInvertibleA_ABQRN() {
+    // Example 2 of "On the Numerical Solution of the Discrete-Time Algebraic
+    // Riccati Equation"
+
+    var A =
+        new Matrix<>(
+            Nat.N4(), Nat.N4(), new double[] {0.5, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0});
+    var B = new Matrix<>(Nat.N4(), Nat.N1(), new double[] {0, 0, 0, 1});
+    var Q =
+        new Matrix<>(
+            Nat.N4(), Nat.N4(), new double[] {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
+    var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {0.25});
+
+    var Aref =
+        new Matrix<>(
+            Nat.N4(), Nat.N4(), new double[] {0.25, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0});
+    Q = A.minus(Aref).transpose().times(Q).times(A.minus(Aref));
+    R = B.transpose().times(Q).times(B).plus(R);
+    var N = A.minus(Aref).transpose().times(Q).times(B);
+
+    var X = DARE.dare(A, B, Q, R, N);
+    assertMatrixEqual(X, X.transpose());
+    assertDARESolution(A, B, Q, R, N, X);
+  }
+
+  @Test
+  void testInvertibleA_ABQR() {
+    var A = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 1, 0, 1});
+    var B = new Matrix<>(Nat.N2(), Nat.N1(), new double[] {0, 1});
+    var Q = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 0, 0, 0});
+    var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {0.3});
+
+    var X = DARE.dare(A, B, Q, R);
+    assertMatrixEqual(X, X.transpose());
+    assertDARESolution(A, B, Q, R, X);
+  }
+
+  @Test
+  void testInvertibleA_ABQRN() {
+    var A = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 1, 0, 1});
+    var B = new Matrix<>(Nat.N2(), Nat.N1(), new double[] {0, 1});
+    var Q = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 0, 0, 0});
+    var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {0.3});
+
+    var Aref = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {0.5, 1, 0, 1});
+    Q = A.minus(Aref).transpose().times(Q).times(A.minus(Aref));
+    R = B.transpose().times(Q).times(B).plus(R);
+    var N = A.minus(Aref).transpose().times(Q).times(B);
+
+    var X = DARE.dare(A, B, Q, R, N);
+    assertMatrixEqual(X, X.transpose());
+    assertDARESolution(A, B, Q, R, N, X);
+  }
+
+  @Test
+  void testFirstGeneralizedEigenvalueOfSTIsStable_ABQR() {
+    // The first generalized eigenvalue of (S, T) is stable
+
+    var A = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {0, 1, 0, 0});
+    var B = new Matrix<>(Nat.N2(), Nat.N1(), new double[] {0, 1});
+    var Q = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 0, 0, 1});
+    var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {1});
+
+    var X = DARE.dare(A, B, Q, R);
+    assertMatrixEqual(X, X.transpose());
+    assertDARESolution(A, B, Q, R, X);
+  }
+
+  @Test
+  void testFirstGeneralizedEigenvalueOfSTIsStable_ABQRN() {
+    // The first generalized eigenvalue of (S, T) is stable
+
+    var A = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {0, 1, 0, 0});
+    var B = new Matrix<>(Nat.N2(), Nat.N1(), new double[] {0, 1});
+    var Q = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 0, 0, 1});
+    var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {1});
+
+    var Aref = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {0, 0.5, 0, 0});
+    Q = A.minus(Aref).transpose().times(Q).times(A.minus(Aref));
+    R = B.transpose().times(Q).times(B).plus(R);
+    var N = A.minus(Aref).transpose().times(Q).times(B);
+
+    var X = DARE.dare(A, B, Q, R, N);
+    assertMatrixEqual(X, X.transpose());
+    assertDARESolution(A, B, Q, R, N, X);
+  }
+
+  @Test
+  void testIdentitySystem_ABQR() {
+    var A = Matrix.eye(Nat.N2());
+    var B = Matrix.eye(Nat.N2());
+    var Q = Matrix.eye(Nat.N2());
+    var R = Matrix.eye(Nat.N2());
+
+    var X = DARE.dare(A, B, Q, R);
+    assertMatrixEqual(X, X.transpose());
+    assertDARESolution(A, B, Q, R, X);
+  }
+
+  @Test
+  void testIdentitySystem_ABQRN() {
+    var A = Matrix.eye(Nat.N2());
+    var B = Matrix.eye(Nat.N2());
+    var Q = Matrix.eye(Nat.N2());
+    var R = Matrix.eye(Nat.N2());
+    var N = Matrix.eye(Nat.N2());
+
+    var X = DARE.dare(A, B, Q, R, N);
+    assertMatrixEqual(X, X.transpose());
+    assertDARESolution(A, B, Q, R, N, X);
+  }
+
+  @Test
+  void testMoreInputsThanStates_ABQR() {
+    var A = Matrix.eye(Nat.N2());
+    var B = new Matrix<>(Nat.N2(), Nat.N3(), new double[] {1, 0, 0, 0, 0.5, 0.3});
+    var Q = Matrix.eye(Nat.N2());
+    var R = Matrix.eye(Nat.N3());
+
+    var X = DARE.dare(A, B, Q, R);
+    assertMatrixEqual(X, X.transpose());
+    assertDARESolution(A, B, Q, R, X);
+  }
+
+  @Test
+  void testMoreInputsThanStates_ABQRN() {
+    var A = Matrix.eye(Nat.N2());
+    var B = new Matrix<>(Nat.N2(), Nat.N3(), new double[] {1, 0, 0, 0, 0.5, 0.3});
+    var Q = Matrix.eye(Nat.N2());
+    var R = Matrix.eye(Nat.N3());
+    var N = new Matrix<>(Nat.N2(), Nat.N3(), new double[] {1, 0, 0, 0, 1, 0});
+
+    var X = DARE.dare(A, B, Q, R, N);
+    assertMatrixEqual(X, X.transpose());
+    assertDARESolution(A, B, Q, R, N, X);
+  }
+
+  @Test
+  void testQNotSymmetricPositiveSemidefinite_ABQR() {
+    var A = Matrix.eye(Nat.N2());
+    var B = Matrix.eye(Nat.N2());
+    var Q = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {-1.0, 0.0, 0.0, -1.0});
+    var R = Matrix.eye(Nat.N2());
+
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R));
+  }
+
+  @Test
+  void testQNotSymmetricPositiveSemidefinite_ABQRN() {
+    var A = Matrix.eye(Nat.N2());
+    var B = Matrix.eye(Nat.N2());
+    var Q = Matrix.eye(Nat.N2());
+    var R = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {-1.0, 0.0, 0.0, -1.0});
+    var N = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {2.0, 0.0, 0.0, 2.0});
+
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R, N));
+  }
+
+  @Test
+  void testRNotSymmetricPositiveDefinite_ABQR() {
+    var A = Matrix.eye(Nat.N2());
+    var B = Matrix.eye(Nat.N2());
+    var Q = Matrix.eye(Nat.N2());
+
+    var R1 = new Matrix<>(Nat.N2(), Nat.N2());
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R1));
+
+    var R2 = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {-1.0, 0.0, 0.0, -1.0});
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R2));
+  }
+
+  @Test
+  void testRNotSymmetricPositiveDefinite_ABQRN() {
+    var A = Matrix.eye(Nat.N2());
+    var B = Matrix.eye(Nat.N2());
+    var Q = Matrix.eye(Nat.N2());
+    var N = Matrix.eye(Nat.N2());
+
+    var R1 = new Matrix<>(Nat.N2(), Nat.N2());
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R1, N));
+
+    var R2 = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {-1.0, 0.0, 0.0, -1.0});
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R2, N));
+  }
+
+  @Test
+  void testABNotStabilizable_ABQR() {
+    var A = Matrix.eye(Nat.N2());
+    var B = new Matrix<>(Nat.N2(), Nat.N2());
+    var Q = Matrix.eye(Nat.N2());
+    var R = Matrix.eye(Nat.N2());
+
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R));
+  }
+
+  @Test
+  void testABNotStabilizable_ABQRN() {
+    var A = Matrix.eye(Nat.N2());
+    var B = new Matrix<>(Nat.N2(), Nat.N2());
+    var Q = Matrix.eye(Nat.N2());
+    var R = Matrix.eye(Nat.N2());
+    var N = Matrix.eye(Nat.N2());
+
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R, N));
+  }
+
+  @Test
+  void testACNotDetectable_ABQR() {
+    var A = Matrix.eye(Nat.N2());
+    var B = Matrix.eye(Nat.N2());
+    var Q = new Matrix<>(Nat.N2(), Nat.N2());
+    var R = Matrix.eye(Nat.N2());
+
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R));
+  }
+
+  @Test
+  void testACNotDetectable_ABQRN() {
+    var A = Matrix.eye(Nat.N2());
+    var B = Matrix.eye(Nat.N2());
+    var Q = new Matrix<>(Nat.N2(), Nat.N2());
+    var R = Matrix.eye(Nat.N2());
+    var N = new Matrix<>(Nat.N2(), Nat.N2());
+
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R, N));
+  }
+
+  @Test
+  void testQDecomposition() {
+    // Ensures the decomposition of Q into CᵀC is correct
+
+    var A = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1.0, 0.0, 0.0, 0.0});
+    var B = Matrix.eye(Nat.N2());
+    var R = Matrix.eye(Nat.N2());
+
+    // (A, C₁) should be detectable pair
+    var C_1 = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {0.0, 0.0, 1.0, 0.0});
+    var Q_1 = C_1.transpose().times(C_1);
+    assertDoesNotThrow(() -> DARE.dare(A, B, Q_1, R));
+
+    // (A, C₂) shouldn't be detectable pair
+    var C_2 = C_1.transpose();
+    var Q_2 = C_2.transpose().times(C_2);
+    assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q_2, R));
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/DrakeTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/DrakeTest.java
deleted file mode 100644
index 3050867..0000000
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/DrakeTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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.
-
-package edu.wpi.first.math;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import org.ejml.simple.SimpleMatrix;
-import org.junit.jupiter.api.Test;
-
-class DrakeTest {
-  public static void assertMatrixEqual(SimpleMatrix A, SimpleMatrix B) {
-    for (int i = 0; i < A.numRows(); i++) {
-      for (int j = 0; j < A.numCols(); j++) {
-        assertEquals(A.get(i, j), B.get(i, j), 1e-4);
-      }
-    }
-  }
-
-  private boolean solveDAREandVerify(
-      SimpleMatrix A, SimpleMatrix B, SimpleMatrix Q, SimpleMatrix R) {
-    var X = Drake.discreteAlgebraicRiccatiEquation(A, B, Q, R);
-
-    // expect that x is the same as it's transpose
-    assertEquals(X.numRows(), X.numCols());
-    assertMatrixEqual(X, X.transpose());
-
-    // Verify that this is a solution to the DARE.
-    SimpleMatrix Y =
-        A.transpose()
-            .mult(X)
-            .mult(A)
-            .minus(X)
-            .minus(
-                A.transpose()
-                    .mult(X)
-                    .mult(B)
-                    .mult(((B.transpose().mult(X).mult(B)).plus(R)).invert())
-                    .mult(B.transpose())
-                    .mult(X)
-                    .mult(A))
-            .plus(Q);
-    assertMatrixEqual(Y, new SimpleMatrix(Y.numRows(), Y.numCols()));
-
-    return true;
-  }
-
-  @Test
-  void testDiscreteAlgebraicRicattiEquation() {
-    int n1 = 4;
-    int m1 = 1;
-
-    // we know from Scipy that this should be [[0.05048525 0.10097051 0.20194102 0.40388203]]
-    SimpleMatrix A1 =
-        new SimpleMatrix(
-                n1, n1, true, new double[] {0.5, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0})
-            .transpose();
-    SimpleMatrix B1 = new SimpleMatrix(n1, m1, true, new double[] {0, 0, 0, 1});
-    SimpleMatrix Q1 =
-        new SimpleMatrix(
-            n1, n1, true, new double[] {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
-    SimpleMatrix R1 = new SimpleMatrix(m1, m1, true, new double[] {0.25});
-    assertTrue(solveDAREandVerify(A1, B1, Q1, R1));
-
-    SimpleMatrix A2 = new SimpleMatrix(2, 2, true, new double[] {1, 1, 0, 1});
-    SimpleMatrix B2 = new SimpleMatrix(2, 1, true, new double[] {0, 1});
-    SimpleMatrix Q2 = new SimpleMatrix(2, 2, true, new double[] {1, 0, 0, 0});
-    SimpleMatrix R2 = new SimpleMatrix(1, 1, true, new double[] {0.3});
-    assertTrue(solveDAREandVerify(A2, B2, Q2, R2));
-  }
-}
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java
index a3de9cc..44f7c50 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java
@@ -5,10 +5,32 @@
 package edu.wpi.first.math;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import edu.wpi.first.wpilibj.UtilityClassTest;
 import org.junit.jupiter.api.Test;
 
-class MathUtilTest {
+class MathUtilTest extends UtilityClassTest<MathUtil> {
+  MathUtilTest() {
+    super(MathUtil.class);
+  }
+
+  @Test
+  void testClamp() {
+    // int
+    assertEquals(5, MathUtil.clamp(10, 1, 5));
+
+    // double
+    assertEquals(5.5, MathUtil.clamp(10.5, 1.5, 5.5));
+
+    // negative int
+    assertEquals(-5, MathUtil.clamp(-10, -5, -1));
+
+    // negative double
+    assertEquals(-5.5, MathUtil.clamp(-10.5, -5.5, -1.5));
+  }
+
   @Test
   void testApplyDeadbandUnityScale() {
     // < 0
@@ -88,4 +110,61 @@
     assertEquals(MathUtil.angleModulus(Math.PI / 2), Math.PI / 2);
     assertEquals(MathUtil.angleModulus(-Math.PI / 2), -Math.PI / 2);
   }
+
+  @Test
+  void testInterpolate() {
+    assertEquals(50, MathUtil.interpolate(0, 100, 0.5));
+    assertEquals(-50, MathUtil.interpolate(0, -100, 0.5));
+    assertEquals(0, MathUtil.interpolate(-50, 50, 0.5));
+    assertEquals(-25, MathUtil.interpolate(-50, 50, 0.25));
+    assertEquals(25, MathUtil.interpolate(-50, 50, 0.75));
+
+    assertEquals(0, MathUtil.interpolate(0, -100, -0.5));
+  }
+
+  @Test
+  void testIsNear() {
+    // The answer is always 42
+    // Positive integer checks
+    assertTrue(MathUtil.isNear(42, 42, 1));
+    assertTrue(MathUtil.isNear(42, 41, 2));
+    assertTrue(MathUtil.isNear(42, 43, 2));
+    assertFalse(MathUtil.isNear(42, 44, 1));
+
+    // Negative integer checks
+    assertTrue(MathUtil.isNear(-42, -42, 1));
+    assertTrue(MathUtil.isNear(-42, -41, 2));
+    assertTrue(MathUtil.isNear(-42, -43, 2));
+    assertFalse(MathUtil.isNear(-42, -44, 1));
+
+    // Mixed sign integer checks
+    assertFalse(MathUtil.isNear(-42, 42, 1));
+    assertFalse(MathUtil.isNear(-42, 41, 2));
+    assertFalse(MathUtil.isNear(-42, 43, 2));
+    assertFalse(MathUtil.isNear(42, -42, 1));
+    assertFalse(MathUtil.isNear(42, -41, 2));
+    assertFalse(MathUtil.isNear(42, -43, 2));
+
+    // Floating point checks
+    assertTrue(MathUtil.isNear(42, 41.5, 1));
+    assertTrue(MathUtil.isNear(42, 42.5, 1));
+    assertTrue(MathUtil.isNear(42, 41.5, 0.75));
+    assertTrue(MathUtil.isNear(42, 42.5, 0.75));
+
+    // Wraparound checks
+    assertTrue(MathUtil.isNear(0, 356, 5, 0, 360));
+    assertTrue(MathUtil.isNear(0, -356, 5, 0, 360));
+    assertTrue(MathUtil.isNear(0, 4, 5, 0, 360));
+    assertTrue(MathUtil.isNear(0, -4, 5, 0, 360));
+    assertTrue(MathUtil.isNear(400, 41, 5, 0, 360));
+    assertTrue(MathUtil.isNear(400, -319, 5, 0, 360));
+    assertTrue(MathUtil.isNear(400, 401, 5, 0, 360));
+    assertFalse(MathUtil.isNear(0, 356, 2.5, 0, 360));
+    assertFalse(MathUtil.isNear(0, -356, 2.5, 0, 360));
+    assertFalse(MathUtil.isNear(0, 4, 2.5, 0, 360));
+    assertFalse(MathUtil.isNear(0, -4, 2.5, 0, 360));
+    assertFalse(MathUtil.isNear(400, 35, 5, 0, 360));
+    assertFalse(MathUtil.isNear(400, -315, 5, 0, 360));
+    assertFalse(MathUtil.isNear(400, 395, 5, 0, 360));
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/StateSpaceUtilTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/StateSpaceUtilTest.java
index 054c29a..ee33f01 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/StateSpaceUtilTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/StateSpaceUtilTest.java
@@ -12,13 +12,18 @@
 import edu.wpi.first.math.geometry.Rotation2d;
 import edu.wpi.first.math.numbers.N1;
 import edu.wpi.first.math.numbers.N2;
+import edu.wpi.first.wpilibj.UtilityClassTest;
 import java.util.ArrayList;
 import java.util.List;
 import org.ejml.dense.row.MatrixFeatures_DDRM;
 import org.ejml.simple.SimpleMatrix;
 import org.junit.jupiter.api.Test;
 
-class StateSpaceUtilTest {
+class StateSpaceUtilTest extends UtilityClassTest<StateSpaceUtil> {
+  StateSpaceUtilTest() {
+    super(StateSpaceUtil.class);
+  }
+
   @Test
   void testCostArray() {
     var mat = StateSpaceUtil.makeCostMatrix(VecBuilder.fill(1.0, 2.0, 3.0));
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/VectorTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/VectorTest.java
index 5488b68..008953f 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/VectorTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/VectorTest.java
@@ -10,6 +10,44 @@
 
 class VectorTest {
   @Test
+  void testVectorPlus() {
+    var vec1 = VecBuilder.fill(1.0, 2.0, 3.0);
+    var vec2 = VecBuilder.fill(4.0, 5.0, 6.0);
+    var result1 = vec1.plus(vec2);
+
+    assertEquals(5.0, result1.get(0, 0));
+    assertEquals(7.0, result1.get(1, 0));
+    assertEquals(9.0, result1.get(2, 0));
+
+    var vec3 = VecBuilder.fill(-1.0, 2.0, -3.0);
+    var vec4 = VecBuilder.fill(4.0, -5.0, 6.0);
+    var result2 = vec3.plus(vec4);
+
+    assertEquals(3.0, result2.get(0, 0));
+    assertEquals(-3.0, result2.get(1, 0));
+    assertEquals(3.0, result2.get(2, 0));
+  }
+
+  @Test
+  void testVectorMinus() {
+    var vec1 = VecBuilder.fill(1.0, 2.0, 3.0);
+    var vec2 = VecBuilder.fill(4.0, 5.0, 6.0);
+    var result1 = vec1.minus(vec2);
+
+    assertEquals(-3.0, result1.get(0, 0));
+    assertEquals(-3.0, result1.get(1, 0));
+    assertEquals(-3.0, result1.get(2, 0));
+
+    var vec3 = VecBuilder.fill(-1.0, 2.0, -3.0);
+    var vec4 = VecBuilder.fill(4.0, -5.0, 6.0);
+    var result2 = vec3.minus(vec4);
+
+    assertEquals(-5.0, result2.get(0, 0));
+    assertEquals(7.0, result2.get(1, 0));
+    assertEquals(-9.0, result2.get(2, 0));
+  }
+
+  @Test
   void testVectorDot() {
     var vec1 = VecBuilder.fill(1.0, 2.0, 3.0);
     var vec2 = VecBuilder.fill(4.0, 5.0, 6.0);
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/ArmFeedforwardTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/ArmFeedforwardTest.java
new file mode 100644
index 0000000..7b5cf77
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/ArmFeedforwardTest.java
@@ -0,0 +1,39 @@
+// 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.
+
+package edu.wpi.first.math.controller;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class ArmFeedforwardTest {
+  private static final double ks = 0.5;
+  private static final double kg = 1;
+  private static final double kv = 1.5;
+  private static final double ka = 2;
+  private final ArmFeedforward m_armFF = new ArmFeedforward(ks, kg, kv, ka);
+
+  @Test
+  void testCalculate() {
+    assertEquals(0.5, m_armFF.calculate(Math.PI / 3, 0), 0.002);
+    assertEquals(2.5, m_armFF.calculate(Math.PI / 3, 1), 0.002);
+    assertEquals(6.5, m_armFF.calculate(Math.PI / 3, 1, 2), 0.002);
+    assertEquals(2.5, m_armFF.calculate(Math.PI / 3, -1, 2), 0.002);
+  }
+
+  @Test
+  void testAcheviableVelocity() {
+    assertEquals(6, m_armFF.maxAchievableVelocity(12, Math.PI / 3, 1), 0.002);
+    assertEquals(-9, m_armFF.minAchievableVelocity(11.5, Math.PI / 3, 1), 0.002);
+  }
+
+  @Test
+  void testAcheviableAcceleration() {
+    assertEquals(4.75, m_armFF.maxAchievableAcceleration(12, Math.PI / 3, 1), 0.002);
+    assertEquals(6.75, m_armFF.maxAchievableAcceleration(12, Math.PI / 3, -1), 0.002);
+    assertEquals(-7.25, m_armFF.minAchievableAcceleration(12, Math.PI / 3, 1), 0.002);
+    assertEquals(-5.25, m_armFF.minAchievableAcceleration(12, Math.PI / 3, -1), 0.002);
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/DifferentialDriveAccelerationLimiterTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/DifferentialDriveAccelerationLimiterTest.java
index b23ee1e..faf5563 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/DifferentialDriveAccelerationLimiterTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/DifferentialDriveAccelerationLimiterTest.java
@@ -231,7 +231,6 @@
               .times(xAccelLimiter)
               .plus(plant.getB().times(new MatBuilder<>(Nat.N2(), Nat.N1()).fill(12.0, 12.0)));
       final double a = (accels.get(0, 0) + accels.get(1, 0)) / 2.0;
-      System.out.println(a);
       assertTrue(Math.abs(a) > maxA);
       assertTrue(Math.abs(a) > -minA);
     }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/ElevatorFeedforwardTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/ElevatorFeedforwardTest.java
new file mode 100644
index 0000000..6c978ac
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/ElevatorFeedforwardTest.java
@@ -0,0 +1,56 @@
+// 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.
+
+package edu.wpi.first.math.controller;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import edu.wpi.first.math.Matrix;
+import edu.wpi.first.math.Nat;
+import edu.wpi.first.math.VecBuilder;
+import edu.wpi.first.math.numbers.N1;
+import org.junit.jupiter.api.Test;
+
+class ElevatorFeedforwardTest {
+  private static final double ks = 0.5;
+  private static final double kg = 1;
+  private static final double kv = 1.5;
+  private static final double ka = 2;
+
+  private final ElevatorFeedforward m_elevatorFF = new ElevatorFeedforward(ks, kg, kv, ka);
+
+  @Test
+  void testCalculate() {
+    assertEquals(1, m_elevatorFF.calculate(0), 0.002);
+    assertEquals(4.5, m_elevatorFF.calculate(2), 0.002);
+    assertEquals(6.5, m_elevatorFF.calculate(2, 1), 0.002);
+    assertEquals(-0.5, m_elevatorFF.calculate(-2, 1), 0.002);
+
+    var A = Matrix.mat(Nat.N1(), Nat.N1()).fill(-kv / ka);
+    var B = Matrix.mat(Nat.N1(), Nat.N1()).fill(1.0 / ka);
+    final double dt = 0.02;
+    var plantInversion = new LinearPlantInversionFeedforward<N1, N1, N1>(A, B, dt);
+
+    var r = VecBuilder.fill(2.0);
+    var nextR = VecBuilder.fill(3.0);
+    assertEquals(
+        plantInversion.calculate(r, nextR).get(0, 0) + ks + kg,
+        m_elevatorFF.calculate(2.0, 3.0, dt),
+        0.002);
+  }
+
+  @Test
+  void testAcheviableVelocity() {
+    assertEquals(5, m_elevatorFF.maxAchievableVelocity(11, 1), 0.002);
+    assertEquals(-9, m_elevatorFF.minAchievableVelocity(11, 1), 0.002);
+  }
+
+  @Test
+  void testAcheviableAcceleration() {
+    assertEquals(3.75, m_elevatorFF.maxAchievableAcceleration(12, 2), 0.002);
+    assertEquals(7.25, m_elevatorFF.maxAchievableAcceleration(12, -2), 0.002);
+    assertEquals(-8.25, m_elevatorFF.minAchievableAcceleration(12, 2), 0.002);
+    assertEquals(-4.75, m_elevatorFF.minAchievableAcceleration(12, -2), 0.002);
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java
index 31213f6..634ed1c 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java
@@ -148,10 +148,10 @@
     assertEquals(0.51182128351092726, K.get(0, 1), 1e-10);
 
     // QRN overload
-    var Aref = Matrix.mat(Nat.N2(), Nat.N2()).fill(0, 1, 0, -Kv / (Ka * 2.0));
+    var Aref = Matrix.mat(Nat.N2(), Nat.N2()).fill(0, 1, 0, -Kv / (Ka * 5.0));
     var Kimf = getImplicitModelFollowingK(A, B, Q, R, Aref, 0.005);
     assertEquals(0.0, Kimf.get(0, 0), 1e-10);
-    assertEquals(-5.367540084534802e-05, Kimf.get(0, 1), 1e-10);
+    assertEquals(-6.9190500116751458e-05, Kimf.get(0, 1), 1e-10);
   }
 
   @Test
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/LinearSystemLoopTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/LinearSystemLoopTest.java
index 7b8484d..7ea8caa 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/LinearSystemLoopTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/LinearSystemLoopTest.java
@@ -58,12 +58,12 @@
     TrapezoidProfile profile;
     TrapezoidProfile.State state;
     for (int i = 0; i < 1000; i++) {
-      profile =
-          new TrapezoidProfile(
-              constraints,
+      profile = new TrapezoidProfile(constraints);
+      state =
+          profile.calculate(
+              kDt,
               new TrapezoidProfile.State(m_loop.getXHat(0), m_loop.getXHat(1)),
               new TrapezoidProfile.State(references.get(0, 0), references.get(1, 0)));
-      state = profile.calculate(kDt);
       m_loop.setNextR(VecBuilder.fill(state.position, state.velocity));
 
       updateTwoState(m_plant, m_loop, (random.nextGaussian()) * kPositionStddev);
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/PIDInputOutputTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/PIDInputOutputTest.java
index 1fe4cb1..b25240e 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/PIDInputOutputTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/PIDInputOutputTest.java
@@ -55,4 +55,24 @@
 
     assertEquals(-0.01 / m_controller.getPeriod(), m_controller.calculate(0.0025, 0), 1e-5);
   }
+
+  @Test
+  void iZoneNoOutputTest() {
+    m_controller.setI(1);
+    m_controller.setIZone(1);
+
+    double out = m_controller.calculate(2, 0);
+
+    assertEquals(0, out, 1e-5);
+  }
+
+  @Test
+  void iZoneOutputTest() {
+    m_controller.setI(1);
+    m_controller.setIZone(1);
+
+    double out = m_controller.calculate(1, 0);
+
+    assertEquals(-1 * m_controller.getPeriod(), out, 1e-5);
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/PIDToleranceTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/PIDToleranceTest.java
index b525f49..4fdb867 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/PIDToleranceTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/controller/PIDToleranceTest.java
@@ -19,7 +19,7 @@
     try (var controller = new PIDController(0.05, 0.0, 0.0)) {
       controller.enableContinuousInput(-kRange / 2, kRange / 2);
 
-      assertTrue(controller.atSetpoint());
+      assertFalse(controller.atSetpoint());
     }
   }
 
@@ -28,10 +28,7 @@
     try (var controller = new PIDController(0.05, 0.0, 0.0)) {
       controller.enableContinuousInput(-kRange / 2, kRange / 2);
 
-      assertTrue(
-          controller.atSetpoint(),
-          "Error was not in tolerance when it should have been. Error was "
-              + controller.getPositionError());
+      assertFalse(controller.atSetpoint());
 
       controller.setTolerance(kTolerance);
       controller.setSetpoint(kSetpoint);
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimatorTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimatorTest.java
index c3906d4..e0e202f 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimatorTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimatorTest.java
@@ -143,8 +143,6 @@
 
     double t = 0.0;
 
-    System.out.print("time, est_x, est_y, est_theta, true_x, true_y, true_theta\n");
-
     final TreeMap<Double, Pose2d> visionUpdateQueue = new TreeMap<>();
 
     double maxError = Double.NEGATIVE_INFINITY;
@@ -191,16 +189,6 @@
               leftDistanceMeters,
               rightDistanceMeters);
 
-      System.out.printf(
-          "%f, %f, %f, %f, %f, %f, %f\n",
-          t,
-          xHat.getX(),
-          xHat.getY(),
-          xHat.getRotation().getRadians(),
-          groundTruthState.poseMeters.getX(),
-          groundTruthState.poseMeters.getY(),
-          groundTruthState.poseMeters.getRotation().getRadians());
-
       double error =
           groundTruthState.poseMeters.getTranslation().getDistance(xHat.getTranslation());
       if (error > maxError) {
@@ -227,4 +215,92 @@
       assertEquals(0.0, maxError, 0.2, "Incorrect max error");
     }
   }
+
+  @Test
+  void testSimultaneousVisionMeasurements() {
+    // This tests for multiple vision measurements appled at the same time. The expected behavior
+    // is that all measurements affect the estimated pose. The alternative result is that only one
+    // vision measurement affects the outcome. If that were the case, after 1000 measurements, the
+    // estimated pose would converge to that measurement.
+    var kinematics = new DifferentialDriveKinematics(1);
+
+    var estimator =
+        new DifferentialDrivePoseEstimator(
+            kinematics,
+            new Rotation2d(),
+            0,
+            0,
+            new Pose2d(1, 2, Rotation2d.fromDegrees(270)),
+            VecBuilder.fill(0.02, 0.02, 0.01),
+            VecBuilder.fill(0.1, 0.1, 0.1));
+
+    estimator.updateWithTime(0, new Rotation2d(), 0, 0);
+
+    var visionMeasurements =
+        new Pose2d[] {
+          new Pose2d(0, 0, Rotation2d.fromDegrees(0)),
+          new Pose2d(3, 1, Rotation2d.fromDegrees(90)),
+          new Pose2d(2, 4, Rotation2d.fromRadians(180)),
+        };
+
+    for (int i = 0; i < 1000; i++) {
+      for (var measurement : visionMeasurements) {
+        estimator.addVisionMeasurement(measurement, 0);
+      }
+    }
+
+    for (var measurement : visionMeasurements) {
+      var errorLog =
+          "Estimator converged to one vision measurement: "
+              + estimator.getEstimatedPosition().toString()
+              + " -> "
+              + measurement.toString();
+
+      var dx = Math.abs(measurement.getX() - estimator.getEstimatedPosition().getX());
+      var dy = Math.abs(measurement.getY() - estimator.getEstimatedPosition().getY());
+      var dtheta =
+          Math.abs(
+              measurement.getRotation().getDegrees()
+                  - estimator.getEstimatedPosition().getRotation().getDegrees());
+
+      assertEquals(dx > 0.08 || dy > 0.08 || dtheta > 0.08, true, errorLog);
+    }
+  }
+
+  @Test
+  void testDiscardsStaleVisionMeasurements() {
+    var kinematics = new DifferentialDriveKinematics(1);
+    var estimator =
+        new DifferentialDrivePoseEstimator(
+            kinematics,
+            new Rotation2d(),
+            0,
+            0,
+            new Pose2d(),
+            VecBuilder.fill(0.1, 0.1, 0.1),
+            VecBuilder.fill(0.9, 0.9, 0.9));
+
+    double time = 0;
+
+    // Add enough measurements to fill up the buffer
+    for (; time < 4; time += 0.02) {
+      estimator.updateWithTime(time, new Rotation2d(), 0, 0);
+    }
+
+    var odometryPose = estimator.getEstimatedPosition();
+
+    // Apply a vision measurement made 3 seconds ago
+    // This test passes if this does not cause a ConcurrentModificationException.
+    estimator.addVisionMeasurement(
+        new Pose2d(new Translation2d(10, 10), new Rotation2d(0.1)),
+        1,
+        VecBuilder.fill(0.1, 0.1, 0.1));
+
+    assertEquals(odometryPose.getX(), estimator.getEstimatedPosition().getX(), "Incorrect Final X");
+    assertEquals(odometryPose.getY(), estimator.getEstimatedPosition().getY(), "Incorrect Final Y");
+    assertEquals(
+        odometryPose.getRotation().getRadians(),
+        estimator.getEstimatedPosition().getRotation().getRadians(),
+        "Incorrect Final Theta");
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimatorTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimatorTest.java
index 02d2d52..d844c5d 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimatorTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimatorTest.java
@@ -151,8 +151,6 @@
 
     double t = 0.0;
 
-    System.out.print("time, est_x, est_y, est_theta, true_x, true_y, true_theta\n");
-
     final TreeMap<Double, Pose2d> visionUpdateQueue = new TreeMap<>();
 
     double maxError = Double.NEGATIVE_INFINITY;
@@ -200,16 +198,6 @@
                   .minus(trajectory.getInitialPose().getRotation()),
               wheelPositions);
 
-      System.out.printf(
-          "%f, %f, %f, %f, %f, %f, %f\n",
-          t,
-          xHat.getX(),
-          xHat.getY(),
-          xHat.getRotation().getRadians(),
-          groundTruthState.poseMeters.getX(),
-          groundTruthState.poseMeters.getY(),
-          groundTruthState.poseMeters.getRotation().getRadians());
-
       double error =
           groundTruthState.poseMeters.getTranslation().getDistance(xHat.getTranslation());
       if (error > maxError) {
@@ -236,4 +224,100 @@
       assertEquals(0.0, maxError, 0.2, "Incorrect max error");
     }
   }
+
+  @Test
+  void testSimultaneousVisionMeasurements() {
+    // This tests for multiple vision measurements appled at the same time. The expected behavior
+    // is that all measurements affect the estimated pose. The alternative result is that only one
+    // vision measurement affects the outcome. If that were the case, after 1000 measurements, the
+    // estimated pose would converge to that measurement.
+    var kinematics =
+        new MecanumDriveKinematics(
+            new Translation2d(1, 1), new Translation2d(1, -1),
+            new Translation2d(-1, -1), new Translation2d(-1, 1));
+
+    var wheelPositions = new MecanumDriveWheelPositions();
+
+    var estimator =
+        new MecanumDrivePoseEstimator(
+            kinematics,
+            new Rotation2d(),
+            wheelPositions,
+            new Pose2d(1, 2, Rotation2d.fromDegrees(270)),
+            VecBuilder.fill(0.1, 0.1, 0.1),
+            VecBuilder.fill(0.45, 0.45, 0.1));
+
+    estimator.updateWithTime(0, new Rotation2d(), wheelPositions);
+
+    var visionMeasurements =
+        new Pose2d[] {
+          new Pose2d(0, 0, Rotation2d.fromDegrees(0)),
+          new Pose2d(3, 1, Rotation2d.fromDegrees(90)),
+          new Pose2d(2, 4, Rotation2d.fromRadians(180)),
+        };
+
+    for (int i = 0; i < 1000; i++) {
+      for (var measurement : visionMeasurements) {
+        estimator.addVisionMeasurement(measurement, 0);
+      }
+    }
+
+    for (var measurement : visionMeasurements) {
+      var errorLog =
+          "Estimator converged to one vision measurement: "
+              + estimator.getEstimatedPosition().toString()
+              + " -> "
+              + measurement.toString();
+
+      var dx = Math.abs(measurement.getX() - estimator.getEstimatedPosition().getX());
+      var dy = Math.abs(measurement.getY() - estimator.getEstimatedPosition().getY());
+      var dtheta =
+          Math.abs(
+              measurement.getRotation().getDegrees()
+                  - estimator.getEstimatedPosition().getRotation().getDegrees());
+
+      assertEquals(dx > 0.08 || dy > 0.08 || dtheta > 0.08, true, errorLog);
+    }
+  }
+
+  @Test
+  void testDiscardsOldVisionMeasurements() {
+    var kinematics =
+        new MecanumDriveKinematics(
+            new Translation2d(1, 1),
+            new Translation2d(-1, 1),
+            new Translation2d(1, -1),
+            new Translation2d(-1, -1));
+    var estimator =
+        new MecanumDrivePoseEstimator(
+            kinematics,
+            new Rotation2d(),
+            new MecanumDriveWheelPositions(),
+            new Pose2d(),
+            VecBuilder.fill(0.1, 0.1, 0.1),
+            VecBuilder.fill(0.9, 0.9, 0.9));
+
+    double time = 0;
+
+    // Add enough measurements to fill up the buffer
+    for (; time < 4; time += 0.02) {
+      estimator.updateWithTime(time, new Rotation2d(), new MecanumDriveWheelPositions());
+    }
+
+    var odometryPose = estimator.getEstimatedPosition();
+
+    // Apply a vision measurement made 3 seconds ago
+    // This test passes if this does not cause a ConcurrentModificationException.
+    estimator.addVisionMeasurement(
+        new Pose2d(new Translation2d(10, 10), new Rotation2d(0.1)),
+        1,
+        VecBuilder.fill(0.1, 0.1, 0.1));
+
+    assertEquals(odometryPose.getX(), estimator.getEstimatedPosition().getX(), "Incorrect Final X");
+    assertEquals(odometryPose.getY(), estimator.getEstimatedPosition().getY(), "Incorrect Final Y");
+    assertEquals(
+        odometryPose.getRotation().getRadians(),
+        estimator.getEstimatedPosition().getRotation().getRadians(),
+        "Incorrect Final Theta");
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimatorTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimatorTest.java
index fde2c39..9fcd852 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimatorTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimatorTest.java
@@ -165,11 +165,6 @@
 
     double t = 0.0;
 
-    System.out.print(
-        "time, est_x, est_y, est_theta, true_x, true_y, true_theta, "
-            + "distance_1, distance_2, distance_3, distance_4, "
-            + "angle_1, angle_2, angle_3, angle_4\n");
-
     final TreeMap<Double, Pose2d> visionUpdateQueue = new TreeMap<>();
 
     double maxError = Double.NEGATIVE_INFINITY;
@@ -219,24 +214,6 @@
                   .minus(trajectory.getInitialPose().getRotation()),
               positions);
 
-      System.out.printf(
-          "%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f\n",
-          t,
-          xHat.getX(),
-          xHat.getY(),
-          xHat.getRotation().getRadians(),
-          groundTruthState.poseMeters.getX(),
-          groundTruthState.poseMeters.getY(),
-          groundTruthState.poseMeters.getRotation().getRadians(),
-          positions[0].distanceMeters,
-          positions[1].distanceMeters,
-          positions[2].distanceMeters,
-          positions[3].distanceMeters,
-          positions[0].angle.getRadians(),
-          positions[1].angle.getRadians(),
-          positions[2].angle.getRadians(),
-          positions[3].angle.getRadians());
-
       double error =
           groundTruthState.poseMeters.getTranslation().getDistance(xHat.getTranslation());
       if (error > maxError) {
@@ -263,4 +240,118 @@
       assertEquals(0.0, maxError, 0.2, "Incorrect max error");
     }
   }
+
+  @Test
+  void testSimultaneousVisionMeasurements() {
+    // This tests for multiple vision measurements appled at the same time. The expected behavior
+    // is that all measurements affect the estimated pose. The alternative result is that only one
+    // vision measurement affects the outcome. If that were the case, after 1000 measurements, the
+    // estimated pose would converge to that measurement.
+    var kinematics =
+        new SwerveDriveKinematics(
+            new Translation2d(1, 1),
+            new Translation2d(1, -1),
+            new Translation2d(-1, -1),
+            new Translation2d(-1, 1));
+
+    var fl = new SwerveModulePosition();
+    var fr = new SwerveModulePosition();
+    var bl = new SwerveModulePosition();
+    var br = new SwerveModulePosition();
+
+    var estimator =
+        new SwerveDrivePoseEstimator(
+            kinematics,
+            new Rotation2d(),
+            new SwerveModulePosition[] {fl, fr, bl, br},
+            new Pose2d(1, 2, Rotation2d.fromDegrees(270)),
+            VecBuilder.fill(0.1, 0.1, 0.1),
+            VecBuilder.fill(0.9, 0.9, 0.9));
+
+    estimator.updateWithTime(0, new Rotation2d(), new SwerveModulePosition[] {fl, fr, bl, br});
+
+    var visionMeasurements =
+        new Pose2d[] {
+          new Pose2d(0, 0, Rotation2d.fromDegrees(0)),
+          new Pose2d(3, 1, Rotation2d.fromDegrees(90)),
+          new Pose2d(2, 4, Rotation2d.fromRadians(180)),
+        };
+
+    for (int i = 0; i < 1000; i++) {
+      for (var measurement : visionMeasurements) {
+        estimator.addVisionMeasurement(measurement, 0);
+      }
+    }
+
+    for (var measurement : visionMeasurements) {
+      var errorLog =
+          "Estimator converged to one vision measurement: "
+              + estimator.getEstimatedPosition().toString()
+              + " -> "
+              + measurement.toString();
+
+      var dx = Math.abs(measurement.getX() - estimator.getEstimatedPosition().getX());
+      var dy = Math.abs(measurement.getY() - estimator.getEstimatedPosition().getY());
+      var dtheta =
+          Math.abs(
+              measurement.getRotation().getDegrees()
+                  - estimator.getEstimatedPosition().getRotation().getDegrees());
+
+      assertEquals(dx > 0.08 || dy > 0.08 || dtheta > 0.08, true, errorLog);
+    }
+  }
+
+  @Test
+  void testDiscardsOldVisionMeasurements() {
+    var kinematics =
+        new SwerveDriveKinematics(
+            new Translation2d(1, 1),
+            new Translation2d(-1, 1),
+            new Translation2d(1, -1),
+            new Translation2d(-1, -1));
+    var estimator =
+        new SwerveDrivePoseEstimator(
+            kinematics,
+            new Rotation2d(),
+            new SwerveModulePosition[] {
+              new SwerveModulePosition(),
+              new SwerveModulePosition(),
+              new SwerveModulePosition(),
+              new SwerveModulePosition()
+            },
+            new Pose2d(),
+            VecBuilder.fill(0.1, 0.1, 0.1),
+            VecBuilder.fill(0.9, 0.9, 0.9));
+
+    double time = 0;
+
+    // Add enough measurements to fill up the buffer
+    for (; time < 4; time += 0.02) {
+      estimator.updateWithTime(
+          time,
+          new Rotation2d(),
+          new SwerveModulePosition[] {
+            new SwerveModulePosition(),
+            new SwerveModulePosition(),
+            new SwerveModulePosition(),
+            new SwerveModulePosition()
+          });
+    }
+
+    var odometryPose = estimator.getEstimatedPosition();
+
+    // Apply a vision measurement made 3 seconds ago
+    // This test passes if this does not cause a ConcurrentModificationException.
+    estimator.addVisionMeasurement(
+        new Pose2d(new Translation2d(10, 10), new Rotation2d(0.1)),
+        1,
+        VecBuilder.fill(0.1, 0.1, 0.1));
+
+    assertEquals(odometryPose.getX(), estimator.getEstimatedPosition().getX(), "Incorrect Final X");
+    assertEquals(odometryPose.getY(), estimator.getEstimatedPosition().getY(), "Incorrect Final Y");
+    assertEquals(
+        odometryPose.getRotation().getRadians(),
+        estimator.getEstimatedPosition().getRotation().getRadians(),
+        "Incorrect Final Theta");
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/CoordinateSystemTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/CoordinateSystemTest.java
index 68babdd..4dc1826 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/CoordinateSystemTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/CoordinateSystemTest.java
@@ -38,18 +38,12 @@
     assertEquals(
         transformTo.getTranslation(),
         CoordinateSystem.convert(transformFrom.getTranslation(), coordFrom, coordTo));
-    assertEquals(
-        transformTo.getRotation(),
-        CoordinateSystem.convert(transformFrom.getRotation(), coordFrom, coordTo));
     assertEquals(transformTo, CoordinateSystem.convert(transformFrom, coordFrom, coordTo));
 
     // "to" to "from"
     assertEquals(
         transformFrom.getTranslation(),
         CoordinateSystem.convert(transformTo.getTranslation(), coordTo, coordFrom));
-    assertEquals(
-        transformFrom.getRotation(),
-        CoordinateSystem.convert(transformTo.getRotation(), coordTo, coordFrom));
     assertEquals(transformFrom, CoordinateSystem.convert(transformTo, coordTo, coordFrom));
   }
 
@@ -158,9 +152,7 @@
     // No rotation from EDN to NWU
     checkTransform3dConvert(
         new Transform3d(new Translation3d(1.0, 2.0, 3.0), new Rotation3d()),
-        new Transform3d(
-            new Translation3d(3.0, -1.0, -2.0),
-            new Rotation3d(Units.degreesToRadians(-90.0), 0.0, Units.degreesToRadians(-90.0))),
+        new Transform3d(new Translation3d(3.0, -1.0, -2.0), new Rotation3d()),
         CoordinateSystem.EDN(),
         CoordinateSystem.NWU());
 
@@ -171,7 +163,7 @@
             new Rotation3d(Units.degreesToRadians(45.0), 0.0, 0.0)),
         new Transform3d(
             new Translation3d(3.0, -1.0, -2.0),
-            new Rotation3d(Units.degreesToRadians(-45.0), 0.0, Units.degreesToRadians(-90.0))),
+            new Rotation3d(0.0, Units.degreesToRadians(-45.0), 0.0)),
         CoordinateSystem.EDN(),
         CoordinateSystem.NWU());
 
@@ -182,7 +174,7 @@
             new Rotation3d(0.0, Units.degreesToRadians(45.0), 0.0)),
         new Transform3d(
             new Translation3d(3.0, -1.0, -2.0),
-            new Rotation3d(Units.degreesToRadians(-90.0), 0.0, Units.degreesToRadians(-135.0))),
+            new Rotation3d(0.0, 0.0, Units.degreesToRadians(-45.0))),
         CoordinateSystem.EDN(),
         CoordinateSystem.NWU());
 
@@ -193,10 +185,7 @@
             new Rotation3d(0.0, 0.0, Units.degreesToRadians(45.0))),
         new Transform3d(
             new Translation3d(3.0, -1.0, -2.0),
-            new Rotation3d(
-                Units.degreesToRadians(-90.0),
-                Units.degreesToRadians(45.0),
-                Units.degreesToRadians(-90.0))),
+            new Rotation3d(Units.degreesToRadians(45.0), 0.0, 0.0)),
         CoordinateSystem.EDN(),
         CoordinateSystem.NWU());
   }
@@ -206,9 +195,7 @@
     // No rotation from EDN to NED
     checkTransform3dConvert(
         new Transform3d(new Translation3d(1.0, 2.0, 3.0), new Rotation3d()),
-        new Transform3d(
-            new Translation3d(3.0, 1.0, 2.0),
-            new Rotation3d(Units.degreesToRadians(90.0), 0.0, Units.degreesToRadians(90.0))),
+        new Transform3d(new Translation3d(3.0, 1.0, 2.0), new Rotation3d()),
         CoordinateSystem.EDN(),
         CoordinateSystem.NED());
 
@@ -219,7 +206,7 @@
             new Rotation3d(Units.degreesToRadians(45.0), 0.0, 0.0)),
         new Transform3d(
             new Translation3d(3.0, 1.0, 2.0),
-            new Rotation3d(Units.degreesToRadians(135.0), 0.0, Units.degreesToRadians(90.0))),
+            new Rotation3d(0.0, Units.degreesToRadians(45.0), 0.0)),
         CoordinateSystem.EDN(),
         CoordinateSystem.NED());
 
@@ -230,7 +217,7 @@
             new Rotation3d(0.0, Units.degreesToRadians(45.0), 0.0)),
         new Transform3d(
             new Translation3d(3.0, 1.0, 2.0),
-            new Rotation3d(Units.degreesToRadians(90.0), 0.0, Units.degreesToRadians(135.0))),
+            new Rotation3d(0.0, 0.0, Units.degreesToRadians(45.0))),
         CoordinateSystem.EDN(),
         CoordinateSystem.NED());
 
@@ -241,10 +228,7 @@
             new Rotation3d(0.0, 0.0, Units.degreesToRadians(45.0))),
         new Transform3d(
             new Translation3d(3.0, 1.0, 2.0),
-            new Rotation3d(
-                Units.degreesToRadians(90.0),
-                Units.degreesToRadians(-45.0),
-                Units.degreesToRadians(90.0))),
+            new Rotation3d(Units.degreesToRadians(45.0), 0.0, 0.0)),
         CoordinateSystem.EDN(),
         CoordinateSystem.NED());
   }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Pose2dTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Pose2dTest.java
index 780c816..d57362d 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Pose2dTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Pose2dTest.java
@@ -8,12 +8,35 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 
+import java.util.List;
 import org.junit.jupiter.api.Test;
 
 class Pose2dTest {
   private static final double kEpsilon = 1E-9;
 
   @Test
+  void testRotateBy() {
+    final double x = 1.0;
+    final double y = 2.0;
+    var initial = new Pose2d(new Translation2d(x, y), Rotation2d.fromDegrees(45.0));
+
+    var rotation = Rotation2d.fromDegrees(5.0);
+    var rotated = initial.rotateBy(rotation);
+
+    // Translation is rotated by CCW rotation matrix
+    double c = rotation.getCos();
+    double s = rotation.getSin();
+    assertAll(
+        () -> assertEquals(c * x - s * y, rotated.getX(), kEpsilon),
+        () -> assertEquals(s * x + c * y, rotated.getY(), kEpsilon),
+        () ->
+            assertEquals(
+                initial.getRotation().getDegrees() + rotation.getDegrees(),
+                rotated.getRotation().getDegrees(),
+                kEpsilon));
+  }
+
+  @Test
   void testTransformBy() {
     var initial = new Pose2d(new Translation2d(1.0, 2.0), Rotation2d.fromDegrees(45.0));
     var transformation = new Transform2d(new Translation2d(5.0, 0.0), Rotation2d.fromDegrees(5.0));
@@ -65,4 +88,50 @@
         () -> assertEquals(0.0, transform.getY(), kEpsilon),
         () -> assertEquals(0.0, transform.getRotation().getDegrees(), kEpsilon));
   }
+
+  @Test
+  void testNearest() {
+    var origin = new Pose2d();
+
+    // Distance sort
+    // each poseX is X units away from the origin at a random angle.
+    final var pose1 =
+        new Pose2d(new Translation2d(1, Rotation2d.fromDegrees(45)), new Rotation2d());
+    final var pose2 =
+        new Pose2d(new Translation2d(2, Rotation2d.fromDegrees(90)), new Rotation2d());
+    final var pose3 =
+        new Pose2d(new Translation2d(3, Rotation2d.fromDegrees(135)), new Rotation2d());
+    final var pose4 =
+        new Pose2d(new Translation2d(4, Rotation2d.fromDegrees(180)), new Rotation2d());
+    final var pose5 =
+        new Pose2d(new Translation2d(5, Rotation2d.fromDegrees(270)), new Rotation2d());
+
+    assertEquals(pose3, origin.nearest(List.of(pose5, pose3, pose4)));
+    assertEquals(pose1, origin.nearest(List.of(pose1, pose2, pose3)));
+    assertEquals(pose2, origin.nearest(List.of(pose4, pose2, pose3)));
+
+    // Rotation component sort (when distance is the same)
+    // Use the same translation because using different angles at the same distance can cause
+    // rounding error.
+    final var translation = new Translation2d(1, new Rotation2d());
+
+    final var poseA = new Pose2d(translation, Rotation2d.fromDegrees(0));
+    final var poseB = new Pose2d(translation, Rotation2d.fromDegrees(30));
+    final var poseC = new Pose2d(translation, Rotation2d.fromDegrees(120));
+    final var poseD = new Pose2d(translation, Rotation2d.fromDegrees(90));
+    final var poseE = new Pose2d(translation, Rotation2d.fromDegrees(-180));
+
+    assertEquals(
+        poseA, new Pose2d(0, 0, Rotation2d.fromDegrees(360)).nearest(List.of(poseA, poseB, poseD)));
+    assertEquals(
+        poseB,
+        new Pose2d(0, 0, Rotation2d.fromDegrees(-335)).nearest(List.of(poseB, poseC, poseD)));
+    assertEquals(
+        poseC,
+        new Pose2d(0, 0, Rotation2d.fromDegrees(-120)).nearest(List.of(poseB, poseC, poseD)));
+    assertEquals(
+        poseD, new Pose2d(0, 0, Rotation2d.fromDegrees(85)).nearest(List.of(poseA, poseC, poseD)));
+    assertEquals(
+        poseE, new Pose2d(0, 0, Rotation2d.fromDegrees(170)).nearest(List.of(poseA, poseD, poseE)));
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Pose3dTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Pose3dTest.java
index f13819f..ee1d98c 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Pose3dTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Pose3dTest.java
@@ -6,16 +6,50 @@
 
 import static org.junit.jupiter.api.Assertions.assertAll;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 
 import edu.wpi.first.math.VecBuilder;
 import edu.wpi.first.math.util.Units;
+import java.util.Arrays;
 import org.junit.jupiter.api.Test;
 
 class Pose3dTest {
   private static final double kEpsilon = 1E-9;
 
   @Test
+  void testRotateBy() {
+    final double x = 1.0;
+    final double y = 2.0;
+    var initial =
+        new Pose3d(
+            new Translation3d(x, y, 0.0),
+            new Rotation3d(
+                Units.degreesToRadians(0.0),
+                Units.degreesToRadians(0.0),
+                Units.degreesToRadians(45.0)));
+
+    double yaw = Units.degreesToRadians(5.0);
+    var rotation = new Rotation3d(Units.degreesToRadians(0.0), Units.degreesToRadians(0.0), yaw);
+    var rotated = initial.rotateBy(rotation);
+
+    // Translation is rotated by CCW rotation matrix
+    double c = Math.cos(yaw);
+    double s = Math.sin(yaw);
+    assertAll(
+        () -> assertEquals(c * x - s * y, rotated.getX(), kEpsilon),
+        () -> assertEquals(s * x + c * y, rotated.getY(), kEpsilon),
+        () -> assertEquals(0.0, rotated.getZ(), kEpsilon),
+        () -> assertEquals(0.0, rotated.getRotation().getX(), kEpsilon),
+        () -> assertEquals(0.0, rotated.getRotation().getY(), kEpsilon),
+        () ->
+            assertEquals(
+                initial.getRotation().getZ() + rotation.getZ(),
+                rotated.getRotation().getZ(),
+                kEpsilon));
+  }
+
+  @Test
   void testTransformByRotations() {
     var initialPose =
         new Pose3d(
@@ -49,10 +83,13 @@
                 Units.degreesToRadians(-45.0),
                 Units.degreesToRadians(0.0)));
 
-    // This sequence of rotations should diverge from the origin and eventually return to it. When
-    // each rotation occurs, it should be performed intrinsicly, i.e. 'from the viewpoint' of and
+    // This sequence of rotations should diverge from the origin and eventually
+    // return to it. When
+    // each rotation occurs, it should be performed intrinsicly, i.e. 'from the
+    // viewpoint' of and
     // with
-    // the axes of the pose that is being transformed, just like how the translation is done 'from
+    // the axes of the pose that is being transformed, just like how the translation
+    // is done 'from
     // the
     // viewpoint' of the pose that is being transformed.
     var finalPose =
@@ -153,4 +190,115 @@
 
     assertEquals(expected, pose.toPose2d());
   }
+
+  @Test
+  void testComplexTwists() {
+    var initial_poses =
+        Arrays.asList(
+            new Pose3d(
+                new Translation3d(0.698303, -0.959096, 0.271076),
+                new Rotation3d(new Quaternion(0.86403, -0.076866, 0.147234, 0.475254))),
+            new Pose3d(
+                new Translation3d(0.634892, -0.765209, 0.117543),
+                new Rotation3d(new Quaternion(0.84987, -0.070829, 0.162097, 0.496415))),
+            new Pose3d(
+                new Translation3d(0.584827, -0.590303, -0.02557),
+                new Rotation3d(new Quaternion(0.832743, -0.041991, 0.202188, 0.513708))),
+            new Pose3d(
+                new Translation3d(0.505038, -0.451479, -0.112835),
+                new Rotation3d(new Quaternion(0.816515, -0.002673, 0.226182, 0.531166))),
+            new Pose3d(
+                new Translation3d(0.428178, -0.329692, -0.189707),
+                new Rotation3d(new Quaternion(0.807886, 0.029298, 0.257788, 0.529157))));
+
+    var final_poses =
+        Arrays.asList(
+            new Pose3d(
+                new Translation3d(-0.230448, -0.511957, 0.198406),
+                new Rotation3d(new Quaternion(0.753984, 0.347016, 0.409105, 0.379106))),
+            new Pose3d(
+                new Translation3d(-0.088932, -0.343253, 0.095018),
+                new Rotation3d(new Quaternion(0.638738, 0.413016, 0.536281, 0.365833))),
+            new Pose3d(
+                new Translation3d(-0.107908, -0.317552, 0.133946),
+                new Rotation3d(new Quaternion(0.653444, 0.417069, 0.465505, 0.427046))),
+            new Pose3d(
+                new Translation3d(-0.123383, -0.156411, -0.047435),
+                new Rotation3d(new Quaternion(0.652983, 0.40644, 0.431566, 0.47135))),
+            new Pose3d(
+                new Translation3d(-0.084654, -0.019305, -0.030022),
+                new Rotation3d(new Quaternion(0.620243, 0.429104, 0.479384, 0.44873))));
+
+    final var eps = 1E-5;
+    for (int i = 0; i < initial_poses.size(); i++) {
+      var start = initial_poses.get(i);
+      var end = final_poses.get(i);
+
+      var twist = start.log(end);
+      var start_exp = start.exp(twist);
+
+      assertAll(
+          () -> assertEquals(start_exp.getX(), end.getX(), eps),
+          () -> assertEquals(start_exp.getY(), end.getY(), eps),
+          () -> assertEquals(start_exp.getZ(), end.getZ(), eps),
+          () ->
+              assertEquals(
+                  start_exp.getRotation().getQuaternion().getW(),
+                  end.getRotation().getQuaternion().getW(),
+                  eps),
+          () ->
+              assertEquals(
+                  start_exp.getRotation().getQuaternion().getX(),
+                  end.getRotation().getQuaternion().getX(),
+                  eps),
+          () ->
+              assertEquals(
+                  start_exp.getRotation().getQuaternion().getY(),
+                  end.getRotation().getQuaternion().getY(),
+                  eps),
+          () ->
+              assertEquals(
+                  start_exp.getRotation().getQuaternion().getZ(),
+                  end.getRotation().getQuaternion().getZ(),
+                  eps));
+    }
+  }
+
+  @Test
+  void testTwistNaN() {
+    var initial_poses =
+        Arrays.asList(
+            new Pose3d(
+                new Translation3d(6.32, 4.12, 0.00),
+                new Rotation3d(
+                    new Quaternion(-0.9999999999999999, 0.0, 0.0, 1.9208309264993548E-8))),
+            new Pose3d(
+                new Translation3d(3.75, 2.95, 0.00),
+                new Rotation3d(
+                    new Quaternion(0.9999999999999793, 0.0, 0.0, 2.0352360299846772E-7))));
+    var final_poses =
+        Arrays.asList(
+            new Pose3d(
+                new Translation3d(6.33, 4.15, 0.00),
+                new Rotation3d(
+                    new Quaternion(-0.9999999999999999, 0.0, 0.0, 2.416890209039172E-8))),
+            new Pose3d(
+                new Translation3d(3.66, 2.93, 0.00),
+                new Rotation3d(
+                    new Quaternion(0.9999999999999782, 0.0, 0.0, 2.0859477994905617E-7))));
+
+    for (int i = 0; i < initial_poses.size(); i++) {
+      var start = initial_poses.get(i);
+      var end = final_poses.get(i);
+
+      var twist = start.log(end);
+      assertAll(
+          () -> assertFalse(((Double) twist.dx).isNaN()),
+          () -> assertFalse(((Double) twist.dy).isNaN()),
+          () -> assertFalse(((Double) twist.dz).isNaN()),
+          () -> assertFalse(((Double) twist.rx).isNaN()),
+          () -> assertFalse(((Double) twist.ry).isNaN()),
+          () -> assertFalse(((Double) twist.rz).isNaN()));
+    }
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/QuaternionTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/QuaternionTest.java
index 7c7e103..458b14d 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/QuaternionTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/QuaternionTest.java
@@ -4,7 +4,9 @@
 
 package edu.wpi.first.math.geometry;
 
+import static org.junit.jupiter.api.Assertions.assertAll;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
 
 import edu.wpi.first.math.util.Units;
 import org.junit.jupiter.api.Test;
@@ -14,37 +16,91 @@
   void testInit() {
     // Identity
     var q1 = new Quaternion();
-    assertEquals(1.0, q1.getW());
-    assertEquals(0.0, q1.getX());
-    assertEquals(0.0, q1.getY());
-    assertEquals(0.0, q1.getZ());
+    assertAll(
+        () -> assertEquals(1.0, q1.getW()),
+        () -> assertEquals(0.0, q1.getX()),
+        () -> assertEquals(0.0, q1.getY()),
+        () -> assertEquals(0.0, q1.getZ()));
 
     // Normalized
     var q2 = new Quaternion(0.5, 0.5, 0.5, 0.5);
-    assertEquals(0.5, q2.getW());
-    assertEquals(0.5, q2.getX());
-    assertEquals(0.5, q2.getY());
-    assertEquals(0.5, q2.getZ());
+    assertAll(
+        () -> assertEquals(0.5, q2.getW()),
+        () -> assertEquals(0.5, q2.getX()),
+        () -> assertEquals(0.5, q2.getY()),
+        () -> assertEquals(0.5, q2.getZ()));
 
     // Unnormalized
     var q3 = new Quaternion(0.75, 0.3, 0.4, 0.5);
-    assertEquals(0.75, q3.getW());
-    assertEquals(0.3, q3.getX());
-    assertEquals(0.4, q3.getY());
-    assertEquals(0.5, q3.getZ());
+    assertAll(
+        () -> assertEquals(0.75, q3.getW()),
+        () -> assertEquals(0.3, q3.getX()),
+        () -> assertEquals(0.4, q3.getY()),
+        () -> assertEquals(0.5, q3.getZ()));
 
-    q3 = q3.normalize();
+    var q3_norm = q3.normalize();
     double norm = Math.sqrt(0.75 * 0.75 + 0.3 * 0.3 + 0.4 * 0.4 + 0.5 * 0.5);
-    assertEquals(0.75 / norm, q3.getW());
-    assertEquals(0.3 / norm, q3.getX());
-    assertEquals(0.4 / norm, q3.getY());
-    assertEquals(0.5 / norm, q3.getZ());
-    assertEquals(
-        1.0,
-        q3.getW() * q3.getW()
-            + q3.getX() * q3.getX()
-            + q3.getY() * q3.getY()
-            + q3.getZ() * q3.getZ());
+    assertAll(
+        () -> assertEquals(0.75 / norm, q3_norm.getW()),
+        () -> assertEquals(0.3 / norm, q3_norm.getX()),
+        () -> assertEquals(0.4 / norm, q3_norm.getY()),
+        () -> assertEquals(0.5 / norm, q3_norm.getZ()),
+        () -> assertEquals(1.0, q3_norm.dot(q3_norm)));
+  }
+
+  @Test
+  void testAddition() {
+    var q = new Quaternion(0.1, 0.2, 0.3, 0.4);
+    var p = new Quaternion(0.5, 0.6, 0.7, 0.8);
+
+    var sum = q.plus(p);
+    assertAll(
+        () -> assertEquals(q.getW() + p.getW(), sum.getW()),
+        () -> assertEquals(q.getX() + p.getX(), sum.getX()),
+        () -> assertEquals(q.getY() + p.getY(), sum.getY()),
+        () -> assertEquals(q.getZ() + p.getZ(), sum.getZ()));
+  }
+
+  @Test
+  void testSubtraction() {
+    var q = new Quaternion(0.1, 0.2, 0.3, 0.4);
+    var p = new Quaternion(0.5, 0.6, 0.7, 0.8);
+
+    var difference = q.minus(p);
+
+    assertAll(
+        () -> assertEquals(q.getW() - p.getW(), difference.getW()),
+        () -> assertEquals(q.getX() - p.getX(), difference.getX()),
+        () -> assertEquals(q.getY() - p.getY(), difference.getY()),
+        () -> assertEquals(q.getZ() - p.getZ(), difference.getZ()));
+  }
+
+  @Test
+  void testScalarMultiplication() {
+    var q = new Quaternion(0.1, 0.2, 0.3, 0.4);
+    var scalar = 2;
+
+    var product = q.times(scalar);
+
+    assertAll(
+        () -> assertEquals(q.getW() * scalar, product.getW()),
+        () -> assertEquals(q.getX() * scalar, product.getX()),
+        () -> assertEquals(q.getY() * scalar, product.getY()),
+        () -> assertEquals(q.getZ() * scalar, product.getZ()));
+  }
+
+  @Test
+  void testScalarDivision() {
+    var q = new Quaternion(0.1, 0.2, 0.3, 0.4);
+    var scalar = 2;
+
+    var product = q.divide(scalar);
+
+    assertAll(
+        () -> assertEquals(q.getW() / scalar, product.getW()),
+        () -> assertEquals(q.getX() / scalar, product.getX()),
+        () -> assertEquals(q.getY() / scalar, product.getY()),
+        () -> assertEquals(q.getZ() / scalar, product.getZ()));
   }
 
   @Test
@@ -59,31 +115,131 @@
     // 90° CCW X rotation, 90° CCW Y rotation, and 90° CCW Z rotation should
     // produce a 90° CCW Y rotation
     var expected = yRot;
-    var actual = zRot.times(yRot).times(xRot);
-    assertEquals(expected.getW(), actual.getW(), 1e-9);
-    assertEquals(expected.getX(), actual.getX(), 1e-9);
-    assertEquals(expected.getY(), actual.getY(), 1e-9);
-    assertEquals(expected.getZ(), actual.getZ(), 1e-9);
+    final var actual = zRot.times(yRot).times(xRot);
+    assertAll(
+        () -> assertEquals(expected.getW(), actual.getW(), 1e-9),
+        () -> assertEquals(expected.getX(), actual.getX(), 1e-9),
+        () -> assertEquals(expected.getY(), actual.getY(), 1e-9),
+        () -> assertEquals(expected.getZ(), actual.getZ(), 1e-9));
 
     // Identity
     var q =
         new Quaternion(
             0.72760687510899891, 0.29104275004359953, 0.38805700005813276, 0.48507125007266594);
-    actual = q.times(q.inverse());
-    assertEquals(1.0, actual.getW());
-    assertEquals(0.0, actual.getX());
-    assertEquals(0.0, actual.getY());
-    assertEquals(0.0, actual.getZ());
+    final var actual2 = q.times(q.inverse());
+    assertAll(
+        () -> assertEquals(1.0, actual2.getW()),
+        () -> assertEquals(0.0, actual2.getX()),
+        () -> assertEquals(0.0, actual2.getY()),
+        () -> assertEquals(0.0, actual2.getZ()));
+  }
+
+  @Test
+  void testConjugate() {
+    var q = new Quaternion(0.75, 0.3, 0.4, 0.5);
+    var inv = q.conjugate();
+
+    assertAll(
+        () -> assertEquals(q.getW(), inv.getW()),
+        () -> assertEquals(-q.getX(), inv.getX()),
+        () -> assertEquals(-q.getY(), inv.getY()),
+        () -> assertEquals(-q.getZ(), inv.getZ()));
   }
 
   @Test
   void testInverse() {
     var q = new Quaternion(0.75, 0.3, 0.4, 0.5);
     var inv = q.inverse();
+    var norm = q.norm();
 
-    assertEquals(q.getW(), inv.getW());
-    assertEquals(-q.getX(), inv.getX());
-    assertEquals(-q.getY(), inv.getY());
-    assertEquals(-q.getZ(), inv.getZ());
+    assertAll(
+        () -> assertEquals(q.getW() / (norm * norm), inv.getW(), 1e-10),
+        () -> assertEquals(-q.getX() / (norm * norm), inv.getX(), 1e-10),
+        () -> assertEquals(-q.getY() / (norm * norm), inv.getY(), 1e-10),
+        () -> assertEquals(-q.getZ() / (norm * norm), inv.getZ(), 1e-10));
+  }
+
+  @Test
+  void testNorm() {
+    var q = new Quaternion(3, 4, 12, 84);
+
+    // pythagorean triples (3, 4, 5), (5, 12, 13), (13, 84, 85)
+    assertEquals(q.norm(), 85, 1e-10);
+  }
+
+  @Test
+  void testExponential() {
+    var q = new Quaternion(1.1, 2.2, 3.3, 4.4);
+    var q_exp =
+        new Quaternion(
+            2.81211398529184, -0.392521193481878, -0.588781790222817, -0.785042386963756);
+
+    assertEquals(q_exp, q.exp());
+  }
+
+  @Test
+  void testLogarithm() {
+    var q = new Quaternion(1.1, 2.2, 3.3, 4.4);
+    var q_log =
+        new Quaternion(1.7959088706354, 0.515190292664085, 0.772785438996128, 1.03038058532817);
+
+    assertEquals(q_log, q.log());
+
+    var zero = new Quaternion(0, 0, 0, 0);
+    var one = new Quaternion();
+
+    assertEquals(zero, one.log());
+
+    var i = new Quaternion(0, 1, 0, 0);
+    assertEquals(i.times(Math.PI / 2), i.log());
+
+    var j = new Quaternion(0, 0, 1, 0);
+    assertEquals(j.times(Math.PI / 2), j.log());
+
+    var k = new Quaternion(0, 0, 0, 1);
+    assertEquals(k.times(Math.PI / 2), k.log());
+    assertEquals(i.times(-Math.PI), one.times(-1).log());
+
+    var ln_half = Math.log(0.5);
+    assertEquals(new Quaternion(ln_half, -Math.PI, 0, 0), one.times(-0.5).log());
+  }
+
+  @Test
+  void testLogarithmIsInverseOfExponential() {
+    var q = new Quaternion(1.1, 2.2, 3.3, 4.4);
+
+    // These operations are order-dependent: ln(exp(q)) is congruent
+    // but not necessarily equal to exp(ln(q)) due to the multi-valued nature of the complex
+    // logarithm.
+
+    var q_log_exp = q.log().exp();
+
+    assertEquals(q, q_log_exp);
+
+    var start = new Quaternion(1, 2, 3, 4);
+    var expect = new Quaternion(5, 6, 7, 8);
+
+    var twist = start.log(expect);
+    var actual = start.exp(twist);
+
+    assertEquals(expect, actual);
+  }
+
+  @Test
+  void testDotProduct() {
+    var q = new Quaternion(1.1, 2.2, 3.3, 4.4);
+    var p = new Quaternion(5.5, 6.6, 7.7, 8.8);
+
+    assertEquals(
+        q.getW() * p.getW() + q.getX() * p.getX() + q.getY() * p.getY() + q.getZ() * p.getZ(),
+        q.dot(p));
+  }
+
+  @Test
+  void testDotProductAsEquality() {
+    var q = new Quaternion(1.1, 2.2, 3.3, 4.4);
+    var q_conj = q.conjugate();
+
+    assertAll(() -> assertEquals(q, q), () -> assertNotEquals(q, q_conj));
   }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation3dTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation3dTest.java
index 072a2e6..d80344d 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation3dTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation3dTest.java
@@ -20,21 +20,60 @@
   private static final double kEpsilon = 1E-9;
 
   @Test
+  void testGimbalLockAccuracy() {
+    var rot1 = new Rotation3d(0, 0, Math.PI / 2);
+    var rot2 = new Rotation3d(Math.PI, 0, 0);
+    var rot3 = new Rotation3d(-Math.PI / 2, 0, 0);
+    final var result1 = rot1.plus(rot2).plus(rot3);
+    final var expected1 = new Rotation3d(0, -Math.PI / 2, Math.PI / 2);
+    assertAll(
+        () -> assertEquals(expected1, result1),
+        () -> assertEquals(Math.PI / 2, result1.getX() + result1.getZ(), kEpsilon),
+        () -> assertEquals(-Math.PI / 2, result1.getY(), kEpsilon));
+
+    rot1 = new Rotation3d(0, 0, Math.PI / 2);
+    rot2 = new Rotation3d(-Math.PI, 0, 0);
+    rot3 = new Rotation3d(Math.PI / 2, 0, 0);
+    final var result2 = rot1.plus(rot2).plus(rot3);
+    final var expected2 = new Rotation3d(0, Math.PI / 2, Math.PI / 2);
+    assertAll(
+        () -> assertEquals(expected2, result2),
+        () -> assertEquals(Math.PI / 2, result2.getZ() - result2.getX(), kEpsilon),
+        () -> assertEquals(Math.PI / 2, result2.getY(), kEpsilon));
+
+    rot1 = new Rotation3d(0, 0, Math.PI / 2);
+    rot2 = new Rotation3d(0, Math.PI / 3, 0);
+    rot3 = new Rotation3d(-Math.PI / 2, 0, 0);
+    final var result3 = rot1.plus(rot2).plus(rot3);
+    final var expected3 = new Rotation3d(0, Math.PI / 2, Math.PI / 6);
+    assertAll(
+        () -> assertEquals(expected3, result3),
+        () -> assertEquals(Math.PI / 6, result3.getZ() - result3.getX(), kEpsilon),
+        () -> assertEquals(Math.PI / 2, result3.getY(), kEpsilon));
+  }
+
+  @Test
   void testInitAxisAngleAndRollPitchYaw() {
     final var xAxis = VecBuilder.fill(1.0, 0.0, 0.0);
     final var rot1 = new Rotation3d(xAxis, Math.PI / 3);
     final var rot2 = new Rotation3d(Math.PI / 3, 0.0, 0.0);
+    final var rvec1 = new Rotation3d(xAxis.times(Math.PI / 3));
     assertEquals(rot1, rot2);
+    assertEquals(rot1, rvec1);
 
     final var yAxis = VecBuilder.fill(0.0, 1.0, 0.0);
     final var rot3 = new Rotation3d(yAxis, Math.PI / 3);
     final var rot4 = new Rotation3d(0.0, Math.PI / 3, 0.0);
+    final var rvec2 = new Rotation3d(yAxis.times(Math.PI / 3));
     assertEquals(rot3, rot4);
+    assertEquals(rot3, rvec2);
 
     final var zAxis = VecBuilder.fill(0.0, 0.0, 1.0);
     final var rot5 = new Rotation3d(zAxis, Math.PI / 3);
     final var rot6 = new Rotation3d(0.0, 0.0, Math.PI / 3);
+    final var rvec3 = new Rotation3d(zAxis.times(Math.PI / 3));
     assertEquals(rot5, rot6);
+    assertEquals(rot5, rvec3);
   }
 
   @Test
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Translation2dTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Translation2dTest.java
index c8f5690..6ed8a61 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Translation2dTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/geometry/Translation2dTest.java
@@ -8,6 +8,7 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 
+import java.util.List;
 import org.junit.jupiter.api.Test;
 
 class Translation2dTest {
@@ -114,4 +115,20 @@
         () -> assertEquals(1.0, two.getX(), kEpsilon),
         () -> assertEquals(Math.sqrt(3.0), two.getY(), kEpsilon));
   }
+
+  @Test
+  void testNearest() {
+    var origin = new Translation2d();
+
+    // each translationX is X units away from the origin at a random angle.
+    var translation1 = new Translation2d(1, Rotation2d.fromDegrees(45));
+    var translation2 = new Translation2d(2, Rotation2d.fromDegrees(90));
+    var translation3 = new Translation2d(3, Rotation2d.fromDegrees(135));
+    var translation4 = new Translation2d(4, Rotation2d.fromDegrees(180));
+    var translation5 = new Translation2d(5, Rotation2d.fromDegrees(270));
+
+    assertEquals(origin.nearest(List.of(translation5, translation3, translation4)), translation3);
+    assertEquals(origin.nearest(List.of(translation1, translation2, translation3)), translation1);
+    assertEquals(origin.nearest(List.of(translation4, translation2, translation3)), translation2);
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/interpolation/InterpolatingDoubleTreeMapTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/interpolation/InterpolatingDoubleTreeMapTest.java
new file mode 100644
index 0000000..fb74b92
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/interpolation/InterpolatingDoubleTreeMapTest.java
@@ -0,0 +1,76 @@
+// 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.
+
+package edu.wpi.first.math.interpolation;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class InterpolatingDoubleTreeMapTest {
+  @Test
+  void testInterpolationDouble() {
+    InterpolatingDoubleTreeMap table = new InterpolatingDoubleTreeMap();
+
+    table.put(125.0, 450.0);
+    table.put(200.0, 510.0);
+    table.put(268.0, 525.0);
+    table.put(312.0, 550.0);
+    table.put(326.0, 650.0);
+
+    // Key below minimum gives the smallest value
+    assertEquals(450.0, table.get(100.0));
+
+    // Minimum key gives exact value
+    assertEquals(450.0, table.get(125.0));
+
+    // Key gives interpolated value
+    assertEquals(480.0, table.get(162.5));
+
+    // Key at right of interpolation range gives exact value
+    assertEquals(510.0, table.get(200.0));
+
+    // Maximum key gives exact value
+    assertEquals(650.0, table.get(326.0));
+
+    // Key above maximum gives largest value
+    assertEquals(650.0, table.get(400.0));
+  }
+
+  @Test
+  void testInterpolationClear() {
+    InterpolatingDoubleTreeMap table = new InterpolatingDoubleTreeMap();
+
+    table.put(125.0, 450.0);
+    table.put(200.0, 510.0);
+    table.put(268.0, 525.0);
+    table.put(312.0, 550.0);
+    table.put(326.0, 650.0);
+
+    // Key below minimum gives the smallest value
+    assertEquals(450.0, table.get(100.0));
+
+    // Minimum key gives exact value
+    assertEquals(450.0, table.get(125.0));
+
+    // Key gives interpolated value
+    assertEquals(480.0, table.get(162.5));
+
+    // Key at right of interpolation range gives exact value
+    assertEquals(510.0, table.get(200.0));
+
+    // Maximum key gives exact value
+    assertEquals(650.0, table.get(326.0));
+
+    // Key above maximum gives largest value
+    assertEquals(650.0, table.get(400.0));
+
+    table.clear();
+
+    table.put(100.0, 250.0);
+    table.put(200.0, 500.0);
+
+    assertEquals(375.0, table.get(150.0));
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/interpolation/InterpolatingTreeMapTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/interpolation/InterpolatingTreeMapTest.java
new file mode 100644
index 0000000..b04b40a
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/interpolation/InterpolatingTreeMapTest.java
@@ -0,0 +1,48 @@
+// 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.
+
+package edu.wpi.first.math.interpolation;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class InterpolatingTreeMapTest {
+  @Test
+  void testInterpolation() {
+    InterpolatingTreeMap<Double, Double> table =
+        new InterpolatingTreeMap<>(InverseInterpolator.forDouble(), Interpolator.forDouble());
+
+    table.put(125.0, 450.0);
+    table.put(200.0, 510.0);
+    table.put(268.0, 525.0);
+    table.put(312.0, 550.0);
+    table.put(326.0, 650.0);
+
+    // Key below minimum gives the smallest value
+    assertEquals(450.0, table.get(100.0));
+
+    // Minimum key gives exact value
+    assertEquals(450.0, table.get(125.0));
+
+    // Key gives interpolated value
+    assertEquals(480.0, table.get(162.5));
+
+    // Key at right of interpolation range gives exact value
+    assertEquals(510.0, table.get(200.0));
+
+    // Maximum key gives exact value
+    assertEquals(650.0, table.get(326.0));
+
+    // Key above maximum gives largest value
+    assertEquals(650.0, table.get(400.0));
+
+    table.clear();
+
+    table.put(100.0, 250.0);
+    table.put(200.0, 500.0);
+
+    assertEquals(375.0, table.get(150.0));
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/ChassisSpeedsTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/ChassisSpeedsTest.java
index b9c3785..2401342 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/ChassisSpeedsTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/ChassisSpeedsTest.java
@@ -7,14 +7,45 @@
 import static org.junit.jupiter.api.Assertions.assertAll;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
+import edu.wpi.first.math.geometry.Pose2d;
 import edu.wpi.first.math.geometry.Rotation2d;
+import edu.wpi.first.math.geometry.Twist2d;
 import org.junit.jupiter.api.Test;
 
 class ChassisSpeedsTest {
   private static final double kEpsilon = 1E-9;
 
   @Test
-  void testFieldRelativeConstruction() {
+  void testDiscretize() {
+    final var target = new ChassisSpeeds(1.0, 0.0, 0.5);
+    final var duration = 1.0;
+    final var dt = 0.01;
+
+    final var speeds = ChassisSpeeds.discretize(target, duration);
+    final var twist =
+        new Twist2d(
+            speeds.vxMetersPerSecond * dt,
+            speeds.vyMetersPerSecond * dt,
+            speeds.omegaRadiansPerSecond * dt);
+
+    var pose = new Pose2d();
+    for (double time = 0; time < duration; time += dt) {
+      pose = pose.exp(twist);
+    }
+
+    final var result = pose; // For lambda capture
+    assertAll(
+        () -> assertEquals(target.vxMetersPerSecond * duration, result.getX(), kEpsilon),
+        () -> assertEquals(target.vyMetersPerSecond * duration, result.getY(), kEpsilon),
+        () ->
+            assertEquals(
+                target.omegaRadiansPerSecond * duration,
+                result.getRotation().getRadians(),
+                kEpsilon));
+  }
+
+  @Test
+  void testFromFieldRelativeSpeeds() {
     final var chassisSpeeds =
         ChassisSpeeds.fromFieldRelativeSpeeds(1.0, 0.0, 0.5, Rotation2d.fromDegrees(-90.0));
 
@@ -23,4 +54,71 @@
         () -> assertEquals(1.0, chassisSpeeds.vyMetersPerSecond, kEpsilon),
         () -> assertEquals(0.5, chassisSpeeds.omegaRadiansPerSecond, kEpsilon));
   }
+
+  @Test
+  void testFromRobotRelativeSpeeds() {
+    final var chassisSpeeds =
+        ChassisSpeeds.fromRobotRelativeSpeeds(1.0, 0.0, 0.5, Rotation2d.fromDegrees(45.0));
+
+    assertAll(
+        () -> assertEquals(1.0 / Math.sqrt(2.0), chassisSpeeds.vxMetersPerSecond, kEpsilon),
+        () -> assertEquals(1.0 / Math.sqrt(2.0), chassisSpeeds.vyMetersPerSecond, kEpsilon),
+        () -> assertEquals(0.5, chassisSpeeds.omegaRadiansPerSecond, kEpsilon));
+  }
+
+  @Test
+  void testPlus() {
+    final var left = new ChassisSpeeds(1.0, 0.5, 0.75);
+    final var right = new ChassisSpeeds(2.0, 1.5, 0.25);
+
+    final var chassisSpeeds = left.plus(right);
+
+    assertAll(
+        () -> assertEquals(3.0, chassisSpeeds.vxMetersPerSecond),
+        () -> assertEquals(2.0, chassisSpeeds.vyMetersPerSecond),
+        () -> assertEquals(1.0, chassisSpeeds.omegaRadiansPerSecond));
+  }
+
+  @Test
+  void testMinus() {
+    final var left = new ChassisSpeeds(1.0, 0.5, 0.75);
+    final var right = new ChassisSpeeds(2.0, 0.5, 0.25);
+
+    final var chassisSpeeds = left.minus(right);
+
+    assertAll(
+        () -> assertEquals(-1.0, chassisSpeeds.vxMetersPerSecond),
+        () -> assertEquals(0.0, chassisSpeeds.vyMetersPerSecond),
+        () -> assertEquals(0.5, chassisSpeeds.omegaRadiansPerSecond));
+  }
+
+  @Test
+  void testUnaryMinus() {
+    final var chassisSpeeds = (new ChassisSpeeds(1.0, 0.5, 0.75)).unaryMinus();
+
+    assertAll(
+        () -> assertEquals(-1.0, chassisSpeeds.vxMetersPerSecond),
+        () -> assertEquals(-0.5, chassisSpeeds.vyMetersPerSecond),
+        () -> assertEquals(-0.75, chassisSpeeds.omegaRadiansPerSecond));
+  }
+
+  @Test
+  void testMultiplication() {
+    final var chassisSpeeds = (new ChassisSpeeds(1.0, 0.5, 0.75)).times(2.0);
+
+    assertAll(
+        () -> assertEquals(2.0, chassisSpeeds.vxMetersPerSecond),
+        () -> assertEquals(1.0, chassisSpeeds.vyMetersPerSecond),
+        () -> assertEquals(1.5, chassisSpeeds.omegaRadiansPerSecond));
+  }
+
+  @Test
+  void testDivision() {
+    final var chassisSpeeds = (new ChassisSpeeds(1.0, 0.5, 0.75)).div(2.0);
+
+    assertAll(
+        () -> assertEquals(0.5, chassisSpeeds.vxMetersPerSecond),
+        () -> assertEquals(0.25, chassisSpeeds.vyMetersPerSecond),
+        () -> assertEquals(0.375, chassisSpeeds.omegaRadiansPerSecond));
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeedsTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeedsTest.java
new file mode 100644
index 0000000..7f26180
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeedsTest.java
@@ -0,0 +1,63 @@
+// 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.
+
+package edu.wpi.first.math.kinematics;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class DifferentialDriveWheelSpeedsTest {
+  @Test
+  void testPlus() {
+    final var left = new DifferentialDriveWheelSpeeds(1.0, 0.5);
+    final var right = new DifferentialDriveWheelSpeeds(2.0, 1.5);
+
+    final var wheelSpeeds = left.plus(right);
+
+    assertAll(
+        () -> assertEquals(3.0, wheelSpeeds.leftMetersPerSecond),
+        () -> assertEquals(2.0, wheelSpeeds.rightMetersPerSecond));
+  }
+
+  @Test
+  void testMinus() {
+    final var left = new DifferentialDriveWheelSpeeds(1.0, 0.5);
+    final var right = new DifferentialDriveWheelSpeeds(2.0, 0.5);
+
+    final var wheelSpeeds = left.minus(right);
+
+    assertAll(
+        () -> assertEquals(-1.0, wheelSpeeds.leftMetersPerSecond),
+        () -> assertEquals(0.0, wheelSpeeds.rightMetersPerSecond));
+  }
+
+  @Test
+  void testUnaryMinus() {
+    final var wheelSpeeds = new DifferentialDriveWheelSpeeds(1.0, 0.5).unaryMinus();
+
+    assertAll(
+        () -> assertEquals(-1.0, wheelSpeeds.leftMetersPerSecond),
+        () -> assertEquals(-0.5, wheelSpeeds.rightMetersPerSecond));
+  }
+
+  @Test
+  void testMultiplication() {
+    final var wheelSpeeds = new DifferentialDriveWheelSpeeds(1.0, 0.5).times(2.0);
+
+    assertAll(
+        () -> assertEquals(2.0, wheelSpeeds.leftMetersPerSecond),
+        () -> assertEquals(1.0, wheelSpeeds.rightMetersPerSecond));
+  }
+
+  @Test
+  void testDivision() {
+    final var wheelSpeeds = new DifferentialDriveWheelSpeeds(1.0, 0.5).div(2.0);
+
+    assertAll(
+        () -> assertEquals(0.5, wheelSpeeds.leftMetersPerSecond),
+        () -> assertEquals(0.25, wheelSpeeds.rightMetersPerSecond));
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeedsTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeedsTest.java
new file mode 100644
index 0000000..22efb44
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeedsTest.java
@@ -0,0 +1,73 @@
+// 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.
+
+package edu.wpi.first.math.kinematics;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class MecanumDriveWheelSpeedsTest {
+  @Test
+  void testPlus() {
+    final var left = new MecanumDriveWheelSpeeds(1.0, 0.5, 2.0, 1.5);
+    final var right = new MecanumDriveWheelSpeeds(2.0, 1.5, 0.5, 1.0);
+
+    final var wheelSpeeds = left.plus(right);
+
+    assertAll(
+        () -> assertEquals(3.0, wheelSpeeds.frontLeftMetersPerSecond),
+        () -> assertEquals(2.0, wheelSpeeds.frontRightMetersPerSecond),
+        () -> assertEquals(2.5, wheelSpeeds.rearLeftMetersPerSecond),
+        () -> assertEquals(2.5, wheelSpeeds.rearRightMetersPerSecond));
+  }
+
+  @Test
+  void testMinus() {
+    final var left = new MecanumDriveWheelSpeeds(1.0, 0.5, 2.0, 1.5);
+    final var right = new MecanumDriveWheelSpeeds(2.0, 0.5, 0.5, 1.0);
+
+    final var wheelSpeeds = left.minus(right);
+
+    assertAll(
+        () -> assertEquals(-1.0, wheelSpeeds.frontLeftMetersPerSecond),
+        () -> assertEquals(0.0, wheelSpeeds.frontRightMetersPerSecond),
+        () -> assertEquals(1.5, wheelSpeeds.rearLeftMetersPerSecond),
+        () -> assertEquals(0.5, wheelSpeeds.rearRightMetersPerSecond));
+  }
+
+  @Test
+  void testUnaryMinus() {
+    final var wheelSpeeds = new MecanumDriveWheelSpeeds(1.0, 0.5, 2.0, 1.5).unaryMinus();
+
+    assertAll(
+        () -> assertEquals(-1.0, wheelSpeeds.frontLeftMetersPerSecond),
+        () -> assertEquals(-0.5, wheelSpeeds.frontRightMetersPerSecond),
+        () -> assertEquals(-2.0, wheelSpeeds.rearLeftMetersPerSecond),
+        () -> assertEquals(-1.5, wheelSpeeds.rearRightMetersPerSecond));
+  }
+
+  @Test
+  void testMultiplication() {
+    final var wheelSpeeds = new MecanumDriveWheelSpeeds(1.0, 0.5, 2.0, 1.5).times(2.0);
+
+    assertAll(
+        () -> assertEquals(2.0, wheelSpeeds.frontLeftMetersPerSecond),
+        () -> assertEquals(1.0, wheelSpeeds.frontRightMetersPerSecond),
+        () -> assertEquals(4.0, wheelSpeeds.rearLeftMetersPerSecond),
+        () -> assertEquals(3.0, wheelSpeeds.rearRightMetersPerSecond));
+  }
+
+  @Test
+  void testDivision() {
+    final var wheelSpeeds = new MecanumDriveWheelSpeeds(1.0, 0.5, 2.0, 1.5).div(2.0);
+
+    assertAll(
+        () -> assertEquals(0.5, wheelSpeeds.frontLeftMetersPerSecond),
+        () -> assertEquals(0.25, wheelSpeeds.frontRightMetersPerSecond),
+        () -> assertEquals(1.0, wheelSpeeds.rearLeftMetersPerSecond),
+        () -> assertEquals(0.75, wheelSpeeds.rearRightMetersPerSecond));
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java
index 43dd02a..f429a68 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java
@@ -120,6 +120,28 @@
   }
 
   @Test
+  void testResetWheelAngle() {
+    Rotation2d fl = new Rotation2d(0);
+    Rotation2d fr = new Rotation2d(Math.PI / 2);
+    Rotation2d bl = new Rotation2d(Math.PI);
+    Rotation2d br = new Rotation2d(3 * Math.PI / 2);
+    m_kinematics.resetHeadings(fl, fr, bl, br);
+    var moduleStates = m_kinematics.toSwerveModuleStates(new ChassisSpeeds());
+
+    // Robot is stationary, but module angles are preserved.
+
+    assertAll(
+        () -> assertEquals(0.0, moduleStates[0].speedMetersPerSecond, kEpsilon),
+        () -> assertEquals(0.0, moduleStates[1].speedMetersPerSecond, kEpsilon),
+        () -> assertEquals(0.0, moduleStates[2].speedMetersPerSecond, kEpsilon),
+        () -> assertEquals(0.0, moduleStates[3].speedMetersPerSecond, kEpsilon),
+        () -> assertEquals(0.0, moduleStates[0].angle.getDegrees(), kEpsilon),
+        () -> assertEquals(90.0, moduleStates[1].angle.getDegrees(), kEpsilon),
+        () -> assertEquals(180.0, moduleStates[2].angle.getDegrees(), kEpsilon),
+        () -> assertEquals(270.0, moduleStates[3].angle.getDegrees(), kEpsilon));
+  }
+
+  @Test
   void testTurnInPlaceInverseKinematics() {
     ChassisSpeeds speeds = new ChassisSpeeds(0, 0, 2 * Math.PI);
     var moduleStates = m_kinematics.toSwerveModuleStates(speeds);
@@ -371,4 +393,21 @@
         () -> assertEquals(4.0 * factor, arr[2].speedMetersPerSecond, kEpsilon),
         () -> assertEquals(7.0 * factor, arr[3].speedMetersPerSecond, kEpsilon));
   }
+
+  @Test
+  void testDesaturateNegativeSpeed() {
+    SwerveModuleState fl = new SwerveModuleState(1, new Rotation2d());
+    SwerveModuleState fr = new SwerveModuleState(1, new Rotation2d());
+    SwerveModuleState bl = new SwerveModuleState(-2, new Rotation2d());
+    SwerveModuleState br = new SwerveModuleState(-2, new Rotation2d());
+
+    SwerveModuleState[] arr = {fl, fr, bl, br};
+    SwerveDriveKinematics.desaturateWheelSpeeds(arr, 1);
+
+    assertAll(
+        () -> assertEquals(0.5, arr[0].speedMetersPerSecond, kEpsilon),
+        () -> assertEquals(0.5, arr[1].speedMetersPerSecond, kEpsilon),
+        () -> assertEquals(-1.0, arr[2].speedMetersPerSecond, kEpsilon),
+        () -> assertEquals(-1.0, arr[3].speedMetersPerSecond, kEpsilon));
+  }
 }
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/system/DiscretizationTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/system/DiscretizationTest.java
index 2182674..7f62933 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/system/DiscretizationTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/system/DiscretizationTest.java
@@ -122,98 +122,6 @@
             + discQIntegrated);
   }
 
-  // Test that the Taylor series discretization produces nearly identical results.
-  @Test
-  void testDiscretizeSlowModelAQTaylor() {
-    final var contA = new MatBuilder<>(Nat.N2(), Nat.N2()).fill(0, 1, 0, 0);
-    final var contQ = new MatBuilder<>(Nat.N2(), Nat.N2()).fill(1, 0, 0, 1);
-
-    final var dt = 1.0;
-
-    // Continuous Q should be positive semidefinite
-    final var esCont = contQ.getStorage().eig();
-    for (int i = 0; i < contQ.getNumRows(); ++i) {
-      assertTrue(esCont.getEigenvalue(i).real >= 0);
-    }
-
-    //       T
-    // Q_d = ∫ e^(Aτ) Q e^(Aᵀτ) dτ
-    //       0
-    final var discQIntegrated =
-        RungeKuttaTimeVarying.rungeKuttaTimeVarying(
-            (Double t, Matrix<N2, N2> x) ->
-                contA.times(t).exp().times(contQ).times(contA.transpose().times(t).exp()),
-            0.0,
-            new Matrix<>(Nat.N2(), Nat.N2()),
-            dt);
-
-    var discA = Discretization.discretizeA(contA, dt);
-
-    var discAQPair = Discretization.discretizeAQ(contA, contQ, dt);
-    var discATaylor = discAQPair.getFirst();
-    var discQTaylor = discAQPair.getSecond();
-
-    assertTrue(
-        discQIntegrated.minus(discQTaylor).normF() < 1e-10,
-        "Expected these to be nearly equal:\ndiscQTaylor:\n"
-            + discQTaylor
-            + "\ndiscQIntegrated:\n"
-            + discQIntegrated);
-    assertTrue(discA.minus(discATaylor).normF() < 1e-10);
-
-    // Discrete Q should be positive semidefinite
-    final var esDisc = discQTaylor.getStorage().eig();
-    for (int i = 0; i < discQTaylor.getNumRows(); ++i) {
-      assertTrue(esDisc.getEigenvalue(i).real >= 0);
-    }
-  }
-
-  // Test that the Taylor series discretization produces nearly identical results.
-  @Test
-  void testDiscretizeFastModelAQTaylor() {
-    final var contA = new MatBuilder<>(Nat.N2(), Nat.N2()).fill(0, 1, 0, -1500);
-    final var contQ = new MatBuilder<>(Nat.N2(), Nat.N2()).fill(0.0025, 0, 0, 1);
-
-    final var dt = 0.005;
-
-    // Continuous Q should be positive semidefinite
-    final var esCont = contQ.getStorage().eig();
-    for (int i = 0; i < contQ.getNumRows(); ++i) {
-      assertTrue(esCont.getEigenvalue(i).real >= 0);
-    }
-
-    //       T
-    // Q_d = ∫ e^(Aτ) Q e^(Aᵀτ) dτ
-    //       0
-    final var discQIntegrated =
-        RungeKuttaTimeVarying.rungeKuttaTimeVarying(
-            (Double t, Matrix<N2, N2> x) ->
-                contA.times(t).exp().times(contQ).times(contA.transpose().times(t).exp()),
-            0.0,
-            new Matrix<>(Nat.N2(), Nat.N2()),
-            dt);
-
-    var discA = Discretization.discretizeA(contA, dt);
-
-    var discAQPair = Discretization.discretizeAQ(contA, contQ, dt);
-    var discATaylor = discAQPair.getFirst();
-    var discQTaylor = discAQPair.getSecond();
-
-    assertTrue(
-        discQIntegrated.minus(discQTaylor).normF() < 1e-3,
-        "Expected these to be nearly equal:\ndiscQTaylor:\n"
-            + discQTaylor
-            + "\ndiscQIntegrated:\n"
-            + discQIntegrated);
-    assertTrue(discA.minus(discATaylor).normF() < 1e-10);
-
-    // Discrete Q should be positive semidefinite
-    final var esDisc = discQTaylor.getStorage().eig();
-    for (int i = 0; i < discQTaylor.getNumRows(); ++i) {
-      assertTrue(esDisc.getEigenvalue(i).real >= 0);
-    }
-  }
-
   // Test that DiscretizeR() works
   @Test
   void testDiscretizeR() {
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/system/NumericalIntegrationTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/system/NumericalIntegrationTest.java
index 72fb5ea..9b47148 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/system/NumericalIntegrationTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/system/NumericalIntegrationTest.java
@@ -31,6 +31,20 @@
   }
 
   @Test
+  void testZeroRKDP() {
+    var y1 =
+        NumericalIntegration.rkdp(
+            (x, u) -> {
+              return VecBuilder.fill(0);
+            },
+            VecBuilder.fill(0),
+            VecBuilder.fill(0),
+            0.1);
+
+    assertEquals(0.0, y1.get(0, 0), 1e-3);
+  }
+
+  @Test
   void testExponentialRKDP() {
     Matrix<N1, N1> y0 = VecBuilder.fill(0.0);
 
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/trajectory/ExponentialProfileTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/trajectory/ExponentialProfileTest.java
new file mode 100644
index 0000000..e9c535e
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/trajectory/ExponentialProfileTest.java
@@ -0,0 +1,364 @@
+// 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.
+
+package edu.wpi.first.math.trajectory;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import edu.wpi.first.math.controller.SimpleMotorFeedforward;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class ExponentialProfileTest {
+  private static final double kDt = 0.01;
+  private static final SimpleMotorFeedforward feedforward =
+      new SimpleMotorFeedforward(0, 2.5629, 0.43277);
+  private static final ExponentialProfile.Constraints constraints =
+      ExponentialProfile.Constraints.fromCharacteristics(12, 2.5629, 0.43277);
+
+  /**
+   * Asserts "val1" is within "eps" of "val2".
+   *
+   * @param val1 First operand in comparison.
+   * @param val2 Second operand in comparison.
+   * @param eps Tolerance for whether values are near to each other.
+   */
+  private static void assertNear(double val1, double val2, double eps) {
+    assertTrue(
+        Math.abs(val1 - val2) <= eps,
+        "Difference between " + val1 + " and " + val2 + " is greater than " + eps);
+  }
+
+  private static void assertNear(
+      ExponentialProfile.State val1, ExponentialProfile.State val2, double eps) {
+    assertAll(
+        () -> assertNear(val1.position, val2.position, eps),
+        () -> assertNear(val1.position, val2.position, eps));
+  }
+
+  private static ExponentialProfile.State checkDynamics(
+      ExponentialProfile profile, ExponentialProfile.State current, ExponentialProfile.State goal) {
+    var next = profile.calculate(kDt, current, goal);
+
+    var signal = feedforward.calculate(current.velocity, next.velocity, kDt);
+
+    assertTrue(Math.abs(signal) < constraints.maxInput + 1e-9);
+
+    return next;
+  }
+
+  @Test
+  void reachesGoal() {
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+
+    ExponentialProfile.State goal = new ExponentialProfile.State(10, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 0);
+
+    for (int i = 0; i < 450; ++i) {
+      state = checkDynamics(profile, state, goal);
+    }
+    assertEquals(state, goal);
+  }
+
+  // Tests that decreasing the maximum velocity in the middle when it is already
+  // moving faster than the new max is handled correctly
+  @Test
+  void posContinuousUnderVelChange() {
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+
+    ExponentialProfile.State goal = new ExponentialProfile.State(10, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 0);
+
+    for (int i = 0; i < 300; ++i) {
+      if (i == 150) {
+        profile =
+            new ExponentialProfile(
+                ExponentialProfile.Constraints.fromStateSpace(9, constraints.A, constraints.B));
+      }
+
+      state = checkDynamics(profile, state, goal);
+    }
+    assertEquals(state, goal);
+  }
+
+  // Tests that decreasing the maximum velocity in the middle when it is already
+  // moving faster than the new max is handled correctly
+  @Test
+  void posContinuousUnderVelChangeBackward() {
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+
+    ExponentialProfile.State goal = new ExponentialProfile.State(-10, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 0);
+
+    for (int i = 0; i < 300; ++i) {
+      if (i == 150) {
+        profile =
+            new ExponentialProfile(
+                ExponentialProfile.Constraints.fromStateSpace(9, constraints.A, constraints.B));
+      }
+
+      state = checkDynamics(profile, state, goal);
+    }
+    assertEquals(state, goal);
+  }
+
+  // There is some somewhat tricky code for dealing with going backwards
+  @Test
+  void backwards() {
+    ExponentialProfile.State goal = new ExponentialProfile.State(-10, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 0);
+
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+
+    for (int i = 0; i < 400; ++i) {
+      state = checkDynamics(profile, state, goal);
+    }
+    assertEquals(state, goal);
+  }
+
+  @Test
+  void switchGoalInMiddle() {
+    ExponentialProfile.State goal = new ExponentialProfile.State(-10, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 0);
+
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+    for (int i = 0; i < 50; ++i) {
+      state = checkDynamics(profile, state, goal);
+    }
+    assertNotEquals(state, goal);
+
+    goal = new ExponentialProfile.State(0.0, 0.0);
+    for (int i = 0; i < 100; ++i) {
+      state = checkDynamics(profile, state, goal);
+    }
+    assertEquals(state, goal);
+  }
+
+  // Checks to make sure that it hits top speed
+  @Test
+  void topSpeed() {
+    ExponentialProfile.State goal = new ExponentialProfile.State(40, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 0);
+
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+    double maxSpeed = 0;
+    for (int i = 0; i < 900; ++i) {
+      state = checkDynamics(profile, state, goal);
+      maxSpeed = Math.max(maxSpeed, state.velocity);
+    }
+
+    assertNear(constraints.maxVelocity(), maxSpeed, 10e-5);
+    assertEquals(state, goal);
+  }
+
+  @Test
+  void topSpeedBackward() {
+    ExponentialProfile.State goal = new ExponentialProfile.State(-40, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 0);
+
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+    double maxSpeed = 0;
+    for (int i = 0; i < 900; ++i) {
+      state = checkDynamics(profile, state, goal);
+      maxSpeed = Math.min(maxSpeed, state.velocity);
+    }
+
+    assertNear(-constraints.maxVelocity(), maxSpeed, 10e-5);
+    assertEquals(state, goal);
+  }
+
+  @Test
+  void largeInitialVelocity() {
+    ExponentialProfile.State goal = new ExponentialProfile.State(40, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 8);
+
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+    for (int i = 0; i < 900; ++i) {
+      state = checkDynamics(profile, state, goal);
+    }
+
+    assertEquals(state, goal);
+  }
+
+  @Test
+  void largeNegativeInitialVelocity() {
+    ExponentialProfile.State goal = new ExponentialProfile.State(-40, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, -8);
+
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+    for (int i = 0; i < 900; ++i) {
+      state = checkDynamics(profile, state, goal);
+    }
+
+    assertEquals(state, goal);
+  }
+
+  @SuppressWarnings("PMD.TestClassWithoutTestCases")
+  static class TestCase {
+    public final ExponentialProfile.State initial;
+    public final ExponentialProfile.State goal;
+    public final ExponentialProfile.State inflectionPoint;
+
+    TestCase(
+        ExponentialProfile.State initial,
+        ExponentialProfile.State goal,
+        ExponentialProfile.State inflectionPoint) {
+      this.initial = initial;
+      this.goal = goal;
+      this.inflectionPoint = inflectionPoint;
+    }
+  }
+
+  @Test
+  void testHeuristic() {
+    List<TestCase> testCases =
+        List.of(
+            new TestCase(
+                new ExponentialProfile.State(0.0, -4),
+                new ExponentialProfile.State(0.75, -4),
+                new ExponentialProfile.State(1.3758, 4.4304)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, -4),
+                new ExponentialProfile.State(1.4103, 4),
+                new ExponentialProfile.State(1.3758, 4.4304)),
+            new TestCase(
+                new ExponentialProfile.State(0.6603, 4),
+                new ExponentialProfile.State(0.75, -4),
+                new ExponentialProfile.State(1.3758, 4.4304)),
+            new TestCase(
+                new ExponentialProfile.State(0.6603, 4),
+                new ExponentialProfile.State(1.4103, 4),
+                new ExponentialProfile.State(1.3758, 4.4304)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, -4),
+                new ExponentialProfile.State(0.5, -2),
+                new ExponentialProfile.State(0.4367, 3.7217)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, -4),
+                new ExponentialProfile.State(0.546, 2),
+                new ExponentialProfile.State(0.4367, 3.7217)),
+            new TestCase(
+                new ExponentialProfile.State(0.6603, 4),
+                new ExponentialProfile.State(0.5, -2),
+                new ExponentialProfile.State(0.5560, -2.9616)),
+            new TestCase(
+                new ExponentialProfile.State(0.6603, 4),
+                new ExponentialProfile.State(0.546, 2),
+                new ExponentialProfile.State(0.5560, -2.9616)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, -4),
+                new ExponentialProfile.State(-0.75, -4),
+                new ExponentialProfile.State(-0.7156, -4.4304)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, -4),
+                new ExponentialProfile.State(-0.0897, 4),
+                new ExponentialProfile.State(-0.7156, -4.4304)),
+            new TestCase(
+                new ExponentialProfile.State(0.6603, 4),
+                new ExponentialProfile.State(-0.75, -4),
+                new ExponentialProfile.State(-0.7156, -4.4304)),
+            new TestCase(
+                new ExponentialProfile.State(0.6603, 4),
+                new ExponentialProfile.State(-0.0897, 4),
+                new ExponentialProfile.State(-0.7156, -4.4304)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, -4),
+                new ExponentialProfile.State(-0.5, -4.5),
+                new ExponentialProfile.State(1.095, 4.314)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, -4),
+                new ExponentialProfile.State(1.0795, 4.5),
+                new ExponentialProfile.State(-0.5122, -4.351)),
+            new TestCase(
+                new ExponentialProfile.State(0.6603, 4),
+                new ExponentialProfile.State(-0.5, -4.5),
+                new ExponentialProfile.State(1.095, 4.314)),
+            new TestCase(
+                new ExponentialProfile.State(0.6603, 4),
+                new ExponentialProfile.State(1.0795, 4.5),
+                new ExponentialProfile.State(-0.5122, -4.351)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, -8),
+                new ExponentialProfile.State(0, 0),
+                new ExponentialProfile.State(-0.1384, 3.342)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, -8),
+                new ExponentialProfile.State(-1, 0),
+                new ExponentialProfile.State(-0.562, -6.792)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, 8),
+                new ExponentialProfile.State(1, 0),
+                new ExponentialProfile.State(0.562, 6.792)),
+            new TestCase(
+                new ExponentialProfile.State(0.0, 8),
+                new ExponentialProfile.State(-1, 0),
+                new ExponentialProfile.State(-0.785, -4.346)));
+
+    var profile = new ExponentialProfile(constraints);
+
+    for (var testCase : testCases) {
+      var state = profile.calculateInflectionPoint(testCase.initial, testCase.goal);
+      assertNear(testCase.inflectionPoint, state, 1e-3);
+    }
+  }
+
+  @Test
+  void timingToCurrent() {
+    ExponentialProfile.State goal = new ExponentialProfile.State(2, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 0);
+
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+    for (int i = 0; i < 400; i++) {
+      state = checkDynamics(profile, state, goal);
+      assertNear(profile.timeLeftUntil(state, state), 0, 2e-2);
+    }
+  }
+
+  @Test
+  void timingToGoal() {
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+
+    ExponentialProfile.State goal = new ExponentialProfile.State(2, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 0);
+
+    double predictedTimeLeft = profile.timeLeftUntil(state, goal);
+    boolean reachedGoal = false;
+    for (int i = 0; i < 400; i++) {
+      state = checkDynamics(profile, state, goal);
+
+      if (!reachedGoal && state.equals(goal)) {
+        // Expected value using for loop index is just an approximation since
+        // the time left in the profile doesn't increase linearly at the
+        // endpoints
+        assertNear(predictedTimeLeft, i / 100.0, 0.25);
+        reachedGoal = true;
+      }
+    }
+  }
+
+  @Test
+  void timingToNegativeGoal() {
+    ExponentialProfile profile = new ExponentialProfile(constraints);
+
+    ExponentialProfile.State goal = new ExponentialProfile.State(-2, 0);
+    ExponentialProfile.State state = new ExponentialProfile.State(0, 0);
+
+    double predictedTimeLeft = profile.timeLeftUntil(state, goal);
+    boolean reachedGoal = false;
+    for (int i = 0; i < 400; i++) {
+      state = checkDynamics(profile, state, goal);
+
+      if (!reachedGoal && state.equals(goal)) {
+        // Expected value using for loop index is just an approximation since
+        // the time left in the profile doesn't increase linearly at the
+        // endpoints
+        assertNear(predictedTimeLeft, i / 100.0, 0.25);
+        reachedGoal = true;
+      }
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrapezoidProfileTest.java b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrapezoidProfileTest.java
index ee6cc8f..fb28b69 100644
--- a/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrapezoidProfileTest.java
+++ b/third_party/allwpilib/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrapezoidProfileTest.java
@@ -57,9 +57,9 @@
     TrapezoidProfile.State goal = new TrapezoidProfile.State(3, 0);
     TrapezoidProfile.State state = new TrapezoidProfile.State();
 
+    TrapezoidProfile profile = new TrapezoidProfile(constraints);
     for (int i = 0; i < 450; ++i) {
-      TrapezoidProfile profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
     }
     assertEquals(state, goal);
   }
@@ -67,21 +67,21 @@
   // Tests that decreasing the maximum velocity in the middle when it is already
   // moving faster than the new max is handled correctly
   @Test
-  void posContinousUnderVelChange() {
+  void posContinuousUnderVelChange() {
     TrapezoidProfile.Constraints constraints = new TrapezoidProfile.Constraints(1.75, 0.75);
     TrapezoidProfile.State goal = new TrapezoidProfile.State(12, 0);
 
-    TrapezoidProfile profile = new TrapezoidProfile(constraints, goal);
-    TrapezoidProfile.State state = profile.calculate(kDt);
+    TrapezoidProfile profile = new TrapezoidProfile(constraints);
+    TrapezoidProfile.State state = profile.calculate(kDt, goal, new TrapezoidProfile.State());
 
     double lastPos = state.position;
     for (int i = 0; i < 1600; ++i) {
       if (i == 400) {
         constraints = new TrapezoidProfile.Constraints(0.75, 0.75);
+        profile = new TrapezoidProfile(constraints);
       }
 
-      profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
       double estimatedVel = (state.position - lastPos) / kDt;
 
       if (i >= 400) {
@@ -105,9 +105,9 @@
     TrapezoidProfile.State goal = new TrapezoidProfile.State(-2, 0);
     TrapezoidProfile.State state = new TrapezoidProfile.State();
 
+    TrapezoidProfile profile = new TrapezoidProfile(constraints);
     for (int i = 0; i < 400; ++i) {
-      TrapezoidProfile profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
     }
     assertEquals(state, goal);
   }
@@ -118,16 +118,16 @@
     TrapezoidProfile.State goal = new TrapezoidProfile.State(-2, 0);
     TrapezoidProfile.State state = new TrapezoidProfile.State();
 
+    TrapezoidProfile profile = new TrapezoidProfile(constraints);
     for (int i = 0; i < 200; ++i) {
-      TrapezoidProfile profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
     }
     assertNotEquals(state, goal);
 
     goal = new TrapezoidProfile.State(0.0, 0.0);
+    profile = new TrapezoidProfile(constraints);
     for (int i = 0; i < 550; ++i) {
-      TrapezoidProfile profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
     }
     assertEquals(state, goal);
   }
@@ -139,15 +139,15 @@
     TrapezoidProfile.State goal = new TrapezoidProfile.State(4, 0);
     TrapezoidProfile.State state = new TrapezoidProfile.State();
 
+    TrapezoidProfile profile = new TrapezoidProfile(constraints);
     for (int i = 0; i < 200; ++i) {
-      TrapezoidProfile profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
     }
     assertNear(constraints.maxVelocity, state.velocity, 10e-5);
 
+    profile = new TrapezoidProfile(constraints);
     for (int i = 0; i < 2000; ++i) {
-      TrapezoidProfile profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
     }
     assertEquals(state, goal);
   }
@@ -158,9 +158,9 @@
     TrapezoidProfile.State goal = new TrapezoidProfile.State(2, 0);
     TrapezoidProfile.State state = new TrapezoidProfile.State();
 
+    TrapezoidProfile profile = new TrapezoidProfile(constraints);
     for (int i = 0; i < 400; i++) {
-      TrapezoidProfile profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
       assertNear(profile.timeLeftUntil(state.position), 0, 2e-2);
     }
   }
@@ -170,14 +170,13 @@
     TrapezoidProfile.Constraints constraints = new TrapezoidProfile.Constraints(0.75, 0.75);
     TrapezoidProfile.State goal = new TrapezoidProfile.State(2, 0);
 
-    TrapezoidProfile profile = new TrapezoidProfile(constraints, goal);
-    TrapezoidProfile.State state = profile.calculate(kDt);
+    TrapezoidProfile profile = new TrapezoidProfile(constraints);
+    TrapezoidProfile.State state = profile.calculate(kDt, goal, new TrapezoidProfile.State());
 
     double predictedTimeLeft = profile.timeLeftUntil(goal.position);
     boolean reachedGoal = false;
     for (int i = 0; i < 400; i++) {
-      profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
       if (!reachedGoal && state.equals(goal)) {
         // Expected value using for loop index is just an approximation since
         // the time left in the profile doesn't increase linearly at the
@@ -193,14 +192,13 @@
     TrapezoidProfile.Constraints constraints = new TrapezoidProfile.Constraints(0.75, 0.75);
     TrapezoidProfile.State goal = new TrapezoidProfile.State(2, 0);
 
-    TrapezoidProfile profile = new TrapezoidProfile(constraints, goal);
-    TrapezoidProfile.State state = profile.calculate(kDt);
+    TrapezoidProfile profile = new TrapezoidProfile(constraints);
+    TrapezoidProfile.State state = profile.calculate(kDt, goal, new TrapezoidProfile.State());
 
     double predictedTimeLeft = profile.timeLeftUntil(1);
     boolean reachedGoal = false;
     for (int i = 0; i < 400; i++) {
-      profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
       if (!reachedGoal && Math.abs(state.velocity - 1) < 10e-5) {
         assertNear(predictedTimeLeft, i / 100.0, 2e-2);
         reachedGoal = true;
@@ -213,14 +211,13 @@
     TrapezoidProfile.Constraints constraints = new TrapezoidProfile.Constraints(0.75, 0.75);
     TrapezoidProfile.State goal = new TrapezoidProfile.State(-2, 0);
 
-    TrapezoidProfile profile = new TrapezoidProfile(constraints, goal);
-    TrapezoidProfile.State state = profile.calculate(kDt);
+    TrapezoidProfile profile = new TrapezoidProfile(constraints);
+    TrapezoidProfile.State state = profile.calculate(kDt, goal, new TrapezoidProfile.State());
 
     double predictedTimeLeft = profile.timeLeftUntil(goal.position);
     boolean reachedGoal = false;
     for (int i = 0; i < 400; i++) {
-      profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
       if (!reachedGoal && state.equals(goal)) {
         // Expected value using for loop index is just an approximation since
         // the time left in the profile doesn't increase linearly at the
@@ -236,14 +233,13 @@
     TrapezoidProfile.Constraints constraints = new TrapezoidProfile.Constraints(0.75, 0.75);
     TrapezoidProfile.State goal = new TrapezoidProfile.State(-2, 0);
 
-    TrapezoidProfile profile = new TrapezoidProfile(constraints, goal);
-    TrapezoidProfile.State state = profile.calculate(kDt);
+    TrapezoidProfile profile = new TrapezoidProfile(constraints);
+    TrapezoidProfile.State state = profile.calculate(kDt, goal, new TrapezoidProfile.State());
 
     double predictedTimeLeft = profile.timeLeftUntil(-1);
     boolean reachedGoal = false;
     for (int i = 0; i < 400; i++) {
-      profile = new TrapezoidProfile(constraints, goal, state);
-      state = profile.calculate(kDt);
+      state = profile.calculate(kDt, goal, state);
       if (!reachedGoal && Math.abs(state.velocity + 1) < 10e-5) {
         assertNear(predictedTimeLeft, i / 100.0, 2e-2);
         reachedGoal = true;
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/ComputerVisionUtilTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/ComputerVisionUtilTest.cpp
index e397af1..bbada9e 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/ComputerVisionUtilTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/ComputerVisionUtilTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/ComputerVisionUtil.h"
-#include "gtest/gtest.h"
 
 TEST(ComputerVisionUtilTest, ObjectToRobotPose) {
   frc::Pose3d robot{1_m, 2_m, 0_m, frc::Rotation3d{0_deg, 0_deg, 30_deg}};
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp
new file mode 100644
index 0000000..e19fa3c
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp
@@ -0,0 +1,299 @@
+// 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 <stdexcept>
+
+#include <Eigen/Core>
+#include <gtest/gtest.h>
+
+#include "DARETestUtil.h"
+#include "frc/DARE.h"
+#include "frc/EigenCore.h"
+#include "frc/fmt/Eigen.h"
+
+// 2x1
+extern template Eigen::Matrix<double, 2, 2> frc::DARE<2, 1>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 1>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 1, 1>& R);
+extern template Eigen::Matrix<double, 2, 2> frc::DARE<2, 1>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 1>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 1, 1>& R,
+    const Eigen::Matrix<double, 2, 1>& N);
+
+// 4x1
+extern template Eigen::Matrix<double, 4, 4> frc::DARE<4, 1>(
+    const Eigen::Matrix<double, 4, 4>& A, const Eigen::Matrix<double, 4, 1>& B,
+    const Eigen::Matrix<double, 4, 4>& Q, const Eigen::Matrix<double, 1, 1>& R);
+extern template Eigen::Matrix<double, 4, 4> frc::DARE<4, 1>(
+    const Eigen::Matrix<double, 4, 4>& A, const Eigen::Matrix<double, 4, 1>& B,
+    const Eigen::Matrix<double, 4, 4>& Q, const Eigen::Matrix<double, 1, 1>& R,
+    const Eigen::Matrix<double, 4, 1>& N);
+
+// 2x2
+extern template Eigen::Matrix<double, 2, 2> frc::DARE<2, 2>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 2>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 2, 2>& R);
+extern template Eigen::Matrix<double, 2, 2> frc::DARE<2, 2>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 2>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 2, 2>& R,
+    const Eigen::Matrix<double, 2, 2>& N);
+
+// 2x3
+extern template Eigen::Matrix<double, 2, 2> frc::DARE<2, 3>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 3>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 3, 3>& R);
+extern template Eigen::Matrix<double, 2, 2> frc::DARE<2, 3>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 3>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 3, 3>& R,
+    const Eigen::Matrix<double, 2, 3>& N);
+
+TEST(DARETest, NonInvertibleA_ABQR) {
+  // Example 2 of "On the Numerical Solution of the Discrete-Time Algebraic
+  // Riccati Equation"
+
+  frc::Matrixd<4, 4> A{
+      {0.5, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {0, 0, 0, 0}};
+  frc::Matrixd<4, 1> B{{0}, {0}, {0}, {1}};
+  frc::Matrixd<4, 4> Q{{1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
+  frc::Matrixd<1, 1> R{0.25};
+
+  frc::Matrixd<4, 4> X = frc::DARE<4, 1>(A, B, Q, R);
+  ExpectMatrixEqual(X, X.transpose(), 1e-10);
+  ExpectPositiveSemidefinite(X);
+  ExpectDARESolution(A, B, Q, R, X);
+}
+
+TEST(DARETest, NonInvertibleA_ABQRN) {
+  // Example 2 of "On the Numerical Solution of the Discrete-Time Algebraic
+  // Riccati Equation"
+
+  frc::Matrixd<4, 4> A{
+      {0.5, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {0, 0, 0, 0}};
+  frc::Matrixd<4, 1> B{{0}, {0}, {0}, {1}};
+  frc::Matrixd<4, 4> Q{{1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
+  frc::Matrixd<1, 1> R{0.25};
+
+  frc::Matrixd<4, 4> Aref{
+      {0.25, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {0, 0, 0, 0}};
+  Q = (A - Aref).transpose() * Q * (A - Aref);
+  R = B.transpose() * Q * B + R;
+  frc::Matrixd<4, 1> N = (A - Aref).transpose() * Q * B;
+
+  frc::Matrixd<4, 4> X = frc::DARE<4, 1>(A, B, Q, R, N);
+  ExpectMatrixEqual(X, X.transpose(), 1e-10);
+  ExpectPositiveSemidefinite(X);
+  ExpectDARESolution(A, B, Q, R, N, X);
+}
+
+TEST(DARETest, InvertibleA_ABQR) {
+  frc::Matrixd<2, 2> A{{1, 1}, {0, 1}};
+  frc::Matrixd<2, 1> B{{0}, {1}};
+  frc::Matrixd<2, 2> Q{{1, 0}, {0, 0}};
+  frc::Matrixd<1, 1> R{{0.3}};
+
+  frc::Matrixd<2, 2> X = frc::DARE<2, 1>(A, B, Q, R);
+  ExpectMatrixEqual(X, X.transpose(), 1e-10);
+  ExpectPositiveSemidefinite(X);
+  ExpectDARESolution(A, B, Q, R, X);
+}
+
+TEST(DARETest, InvertibleA_ABQRN) {
+  frc::Matrixd<2, 2> A{{1, 1}, {0, 1}};
+  frc::Matrixd<2, 1> B{{0}, {1}};
+  frc::Matrixd<2, 2> Q{{1, 0}, {0, 0}};
+  frc::Matrixd<1, 1> R{0.3};
+
+  frc::Matrixd<2, 2> Aref{{0.5, 1}, {0, 1}};
+  Q = (A - Aref).transpose() * Q * (A - Aref);
+  R = B.transpose() * Q * B + R;
+  frc::Matrixd<2, 1> N = (A - Aref).transpose() * Q * B;
+
+  frc::Matrixd<2, 2> X = frc::DARE<2, 1>(A, B, Q, R, N);
+  ExpectMatrixEqual(X, X.transpose(), 1e-10);
+  ExpectPositiveSemidefinite(X);
+  ExpectDARESolution(A, B, Q, R, N, X);
+}
+
+TEST(DARETest, FirstGeneralizedEigenvalueOfSTIsStable_ABQR) {
+  // The first generalized eigenvalue of (S, T) is stable
+
+  frc::Matrixd<2, 2> A{{0, 1}, {0, 0}};
+  frc::Matrixd<2, 1> B{{0}, {1}};
+  frc::Matrixd<2, 2> Q{{1, 0}, {0, 1}};
+  frc::Matrixd<1, 1> R{1};
+
+  frc::Matrixd<2, 2> X = frc::DARE<2, 1>(A, B, Q, R);
+  ExpectMatrixEqual(X, X.transpose(), 1e-10);
+  ExpectPositiveSemidefinite(X);
+  ExpectDARESolution(A, B, Q, R, X);
+}
+
+TEST(DARETest, FirstGeneralizedEigenvalueOfSTIsStable_ABQRN) {
+  // The first generalized eigenvalue of (S, T) is stable
+
+  frc::Matrixd<2, 2> A{{0, 1}, {0, 0}};
+  frc::Matrixd<2, 1> B{{0}, {1}};
+  frc::Matrixd<2, 2> Q{{1, 0}, {0, 1}};
+  frc::Matrixd<1, 1> R{1};
+
+  frc::Matrixd<2, 2> Aref{{0, 0.5}, {0, 0}};
+  Q = (A - Aref).transpose() * Q * (A - Aref);
+  R = B.transpose() * Q * B + R;
+  frc::Matrixd<2, 1> N = (A - Aref).transpose() * Q * B;
+
+  frc::Matrixd<2, 2> X = frc::DARE<2, 1>(A, B, Q, R, N);
+  ExpectMatrixEqual(X, X.transpose(), 1e-10);
+  ExpectPositiveSemidefinite(X);
+  ExpectDARESolution(A, B, Q, R, N, X);
+}
+
+TEST(DARETest, IdentitySystem_ABQR) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d R{Eigen::Matrix2d::Identity()};
+
+  Eigen::Matrix2d X = frc::DARE<2, 2>(A, B, Q, R);
+  ExpectMatrixEqual(X, X.transpose(), 1e-10);
+  ExpectPositiveSemidefinite(X);
+  ExpectDARESolution(A, B, Q, R, X);
+}
+
+TEST(DARETest, IdentitySystem_ABQRN) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d R{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d N{Eigen::Matrix2d::Identity()};
+
+  Eigen::Matrix2d X = frc::DARE<2, 2>(A, B, Q, R, N);
+  ExpectMatrixEqual(X, X.transpose(), 1e-10);
+  ExpectPositiveSemidefinite(X);
+  ExpectDARESolution(A, B, Q, R, N, X);
+}
+
+TEST(DARETest, MoreInputsThanStates_ABQR) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const frc::Matrixd<2, 3> B{{1.0, 0.0, 0.0}, {0.0, 0.5, 0.3}};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix3d R{Eigen::Matrix3d::Identity()};
+
+  Eigen::Matrix2d X = frc::DARE<2, 3>(A, B, Q, R);
+  ExpectMatrixEqual(X, X.transpose(), 1e-10);
+  ExpectPositiveSemidefinite(X);
+  ExpectDARESolution(A, B, Q, R, X);
+}
+
+TEST(DARETest, MoreInputsThanStates_ABQRN) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const frc::Matrixd<2, 3> B{{1.0, 0.0, 0.0}, {0.0, 0.5, 0.3}};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix3d R{Eigen::Matrix3d::Identity()};
+  const frc::Matrixd<2, 3> N{{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}};
+
+  Eigen::Matrix2d X = frc::DARE<2, 3>(A, B, Q, R, N);
+  ExpectMatrixEqual(X, X.transpose(), 1e-10);
+  ExpectPositiveSemidefinite(X);
+  ExpectDARESolution(A, B, Q, R, N, X);
+}
+
+TEST(DARETest, QNotSymmetricPositiveSemidefinite_ABQR) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d Q{-Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d R{Eigen::Matrix2d::Identity()};
+
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q, R)), std::invalid_argument);
+}
+
+TEST(DARETest, QNotSymmetricPositiveSemidefinite_ABQRN) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d R{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d N{2.0 * Eigen::Matrix2d::Identity()};
+
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q, R, N)), std::invalid_argument);
+}
+
+TEST(DARETest, RNotSymmetricPositiveDefinite_ABQR) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Identity()};
+
+  const Eigen::Matrix2d R1{Eigen::Matrix2d::Zero()};
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q, R1)), std::invalid_argument);
+
+  const Eigen::Matrix2d R2{-Eigen::Matrix2d::Identity()};
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q, R2)), std::invalid_argument);
+}
+
+TEST(DARETest, RNotSymmetricPositiveDefinite_ABQRN) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d N{Eigen::Matrix2d::Identity()};
+
+  const Eigen::Matrix2d R1{Eigen::Matrix2d::Zero()};
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q, R1, N)), std::invalid_argument);
+
+  const Eigen::Matrix2d R2{-Eigen::Matrix2d::Identity()};
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q, R2, N)), std::invalid_argument);
+}
+
+TEST(DARETest, ABNotStabilizable_ABQR) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Zero()};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d R{Eigen::Matrix2d::Identity()};
+
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q, R)), std::invalid_argument);
+}
+
+TEST(DARETest, ABNotStabilizable_ABQRN) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Zero()};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d R{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d N{Eigen::Matrix2d::Identity()};
+
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q, R, N)), std::invalid_argument);
+}
+
+TEST(DARETest, ACNotDetectable_ABQR) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Zero()};
+  const Eigen::Matrix2d R{Eigen::Matrix2d::Identity()};
+
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q, R)), std::invalid_argument);
+}
+
+TEST(DARETest, ACNotDetectable_ABQRN) {
+  const Eigen::Matrix2d A{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d Q{Eigen::Matrix2d::Zero()};
+  const Eigen::Matrix2d R{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d N{Eigen::Matrix2d::Zero()};
+
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q, R, N)), std::invalid_argument);
+}
+
+TEST(DARETest, QDecomposition) {
+  // Ensures the decomposition of Q into CᵀC is correct
+
+  const Eigen::Matrix2d A{{1.0, 0.0}, {0.0, 0.0}};
+  const Eigen::Matrix2d B{Eigen::Matrix2d::Identity()};
+  const Eigen::Matrix2d R{Eigen::Matrix2d::Identity()};
+
+  // (A, C₁) should be detectable pair
+  const Eigen::Matrix2d C_1{{0.0, 0.0}, {1.0, 0.0}};
+  const Eigen::Matrix2d Q_1 = C_1.transpose() * C_1;
+  EXPECT_NO_THROW((frc::DARE<2, 2>(A, B, Q_1, R)));
+
+  // (A, C₂) shouldn't be detectable pair
+  const Eigen::Matrix2d C_2 = C_1.transpose();
+  const Eigen::Matrix2d Q_2 = C_2.transpose() * C_2;
+  EXPECT_THROW((frc::DARE<2, 2>(A, B, Q_2, R)), std::invalid_argument);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/DARETestUtil.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETestUtil.cpp
new file mode 100644
index 0000000..452e6ce
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETestUtil.cpp
@@ -0,0 +1,72 @@
+// 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 "DARETestUtil.h"
+
+#include <Eigen/Eigenvalues>
+#include <fmt/core.h>
+#include <gtest/gtest.h>
+
+#include "frc/fmt/Eigen.h"
+
+void ExpectMatrixEqual(const Eigen::MatrixXd& lhs, const Eigen::MatrixXd& rhs,
+                       double tolerance) {
+  for (int row = 0; row < lhs.rows(); ++row) {
+    for (int col = 0; col < lhs.cols(); ++col) {
+      EXPECT_NEAR(lhs(row, col), rhs(row, col), tolerance)
+          << fmt::format("row = {}, col = {}", row, col);
+    }
+  }
+
+  if (::testing::Test::HasFailure()) {
+    fmt::print("lhs =\n{}\n", lhs);
+    fmt::print("rhs =\n{}\n", rhs);
+    fmt::print("delta =\n{}\n", Eigen::MatrixXd{lhs - rhs});
+  }
+}
+
+void ExpectPositiveSemidefinite(const Eigen::Ref<const Eigen::MatrixXd>& X) {
+  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eigX{X,
+                                                      Eigen::EigenvaluesOnly};
+  for (int i = 0; i < X.rows(); ++i) {
+    EXPECT_GE(eigX.eigenvalues()[i], 0.0);
+  }
+}
+
+void ExpectDARESolution(const Eigen::Ref<const Eigen::MatrixXd>& A,
+                        const Eigen::Ref<const Eigen::MatrixXd>& B,
+                        const Eigen::Ref<const Eigen::MatrixXd>& Q,
+                        const Eigen::Ref<const Eigen::MatrixXd>& R,
+                        const Eigen::Ref<const Eigen::MatrixXd>& X) {
+  // Check that X is the solution to the DARE
+  // Y = AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
+  // clang-format off
+  Eigen::MatrixXd Y =
+      A.transpose() * X * A
+      - X
+      - (A.transpose() * X * B * (B.transpose() * X * B + R).inverse()
+        * B.transpose() * X * A)
+      + Q;
+  // clang-format on
+  ExpectMatrixEqual(Y, Eigen::MatrixXd::Zero(X.rows(), X.cols()), 1e-10);
+}
+
+void ExpectDARESolution(const Eigen::Ref<const Eigen::MatrixXd>& A,
+                        const Eigen::Ref<const Eigen::MatrixXd>& B,
+                        const Eigen::Ref<const Eigen::MatrixXd>& Q,
+                        const Eigen::Ref<const Eigen::MatrixXd>& R,
+                        const Eigen::Ref<const Eigen::MatrixXd>& N,
+                        const Eigen::Ref<const Eigen::MatrixXd>& X) {
+  // Check that X is the solution to the DARE
+  // Y = AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
+  // clang-format off
+  Eigen::MatrixXd Y =
+      A.transpose() * X * A
+      - X
+      - ((A.transpose() * X * B + N) * (B.transpose() * X * B + R).inverse()
+        * (B.transpose() * X * A + N.transpose()))
+      + Q;
+  // clang-format on
+  ExpectMatrixEqual(Y, Eigen::MatrixXd::Zero(X.rows(), X.cols()), 1e-10);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/DARETestUtil.h b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETestUtil.h
new file mode 100644
index 0000000..f0d2f66
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETestUtil.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 <Eigen/Core>
+
+void ExpectMatrixEqual(const Eigen::MatrixXd& lhs, const Eigen::MatrixXd& rhs,
+                       double tolerance);
+
+void ExpectPositiveSemidefinite(const Eigen::Ref<const Eigen::MatrixXd>& X);
+
+void ExpectDARESolution(const Eigen::Ref<const Eigen::MatrixXd>& A,
+                        const Eigen::Ref<const Eigen::MatrixXd>& B,
+                        const Eigen::Ref<const Eigen::MatrixXd>& Q,
+                        const Eigen::Ref<const Eigen::MatrixXd>& R,
+                        const Eigen::Ref<const Eigen::MatrixXd>& X);
+
+void ExpectDARESolution(const Eigen::Ref<const Eigen::MatrixXd>& A,
+                        const Eigen::Ref<const Eigen::MatrixXd>& B,
+                        const Eigen::Ref<const Eigen::MatrixXd>& Q,
+                        const Eigen::Ref<const Eigen::MatrixXd>& R,
+                        const Eigen::Ref<const Eigen::MatrixXd>& N,
+                        const Eigen::Ref<const Eigen::MatrixXd>& X);
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_2x1.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_2x1.cpp
new file mode 100644
index 0000000..3ba3d71
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_2x1.cpp
@@ -0,0 +1,13 @@
+// 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 "frc/DARE.h"
+
+template Eigen::Matrix<double, 2, 2> frc::DARE<2, 1>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 1>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 1, 1>& R);
+template Eigen::Matrix<double, 2, 2> frc::DARE<2, 1>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 1>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 1, 1>& R,
+    const Eigen::Matrix<double, 2, 1>& N);
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_2x2.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_2x2.cpp
new file mode 100644
index 0000000..75b17b8
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_2x2.cpp
@@ -0,0 +1,13 @@
+// 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 "frc/DARE.h"
+
+template Eigen::Matrix<double, 2, 2> frc::DARE<2, 2>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 2>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 2, 2>& R);
+template Eigen::Matrix<double, 2, 2> frc::DARE<2, 2>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 2>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 2, 2>& R,
+    const Eigen::Matrix<double, 2, 2>& N);
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_2x3.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_2x3.cpp
new file mode 100644
index 0000000..8d98553
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_2x3.cpp
@@ -0,0 +1,13 @@
+// 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 "frc/DARE.h"
+
+template Eigen::Matrix<double, 2, 2> frc::DARE<2, 3>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 3>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 3, 3>& R);
+template Eigen::Matrix<double, 2, 2> frc::DARE<2, 3>(
+    const Eigen::Matrix<double, 2, 2>& A, const Eigen::Matrix<double, 2, 3>& B,
+    const Eigen::Matrix<double, 2, 2>& Q, const Eigen::Matrix<double, 3, 3>& R,
+    const Eigen::Matrix<double, 2, 3>& N);
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_4x1.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_4x1.cpp
new file mode 100644
index 0000000..840285c
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/DARETest_Inst_4x1.cpp
@@ -0,0 +1,13 @@
+// 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 "frc/DARE.h"
+
+template Eigen::Matrix<double, 4, 4> frc::DARE<4, 1>(
+    const Eigen::Matrix<double, 4, 4>& A, const Eigen::Matrix<double, 4, 1>& B,
+    const Eigen::Matrix<double, 4, 4>& Q, const Eigen::Matrix<double, 1, 1>& R);
+template Eigen::Matrix<double, 4, 4> frc::DARE<4, 1>(
+    const Eigen::Matrix<double, 4, 4>& A, const Eigen::Matrix<double, 4, 1>& B,
+    const Eigen::Matrix<double, 4, 4>& Q, const Eigen::Matrix<double, 1, 1>& R,
+    const Eigen::Matrix<double, 4, 1>& N);
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/EigenTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/EigenTest.cpp
index 03be9b3..74c8577 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/EigenTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/EigenTest.cpp
@@ -2,9 +2,10 @@
 // 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 "Eigen/LU"
+#include <Eigen/LU>
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
-#include "gtest/gtest.h"
 
 TEST(EigenTest, Multiplication) {
   frc::Matrixd<2, 2> m1{{2, 1}, {0, 1}};
@@ -29,17 +30,17 @@
 }
 
 TEST(EigenTest, Transpose) {
-  frc::Vectord<3> vec{1, 2, 3};
+  Eigen::Vector3d vec{1, 2, 3};
 
   const auto transpose = vec.transpose();
 
-  Eigen::RowVector<double, 3> expectedTranspose{1, 2, 3};
+  Eigen::RowVector3d expectedTranspose{1, 2, 3};
 
   EXPECT_TRUE(expectedTranspose.isApprox(transpose));
 }
 
 TEST(EigenTest, Inverse) {
-  frc::Matrixd<3, 3> mat{{1.0, 3.0, 2.0}, {5.0, 2.0, 1.5}, {0.0, 1.3, 2.5}};
+  Eigen::Matrix3d mat{{1.0, 3.0, 2.0}, {5.0, 2.0, 1.5}, {0.0, 1.3, 2.5}};
 
   const auto inverse = mat.inverse();
   const auto identity = Eigen::MatrixXd::Identity(3, 3);
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/FormatterTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/FormatterTest.cpp
index 0e2f77c..0b6d590 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/FormatterTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/FormatterTest.cpp
@@ -5,10 +5,9 @@
 #include <vector>
 
 #include <fmt/format.h>
+#include <gtest/gtest.h>
 
 #include "frc/fmt/Eigen.h"
-#include "frc/fmt/Units.h"
-#include "gtest/gtest.h"
 #include "units/velocity.h"
 
 TEST(FormatterTest, Eigen) {
@@ -17,14 +16,14 @@
       "  0.000000  1.000000\n"
       "  2.000000  3.000000\n"
       "  4.000000  5.000000",
-      fmt::format("{}", A));
+      fmt::format("{:f}", A));
 
   Eigen::MatrixXd B{{0.0, 1.0}, {2.0, 3.0}, {4.0, 5.0}};
   EXPECT_EQ(
       "  0.000000  1.000000\n"
       "  2.000000  3.000000\n"
       "  4.000000  5.000000",
-      fmt::format("{}", B));
+      fmt::format("{:f}", B));
 
   Eigen::SparseMatrix<double> C{3, 2};
   std::vector<Eigen::Triplet<double>> triplets;
@@ -38,7 +37,7 @@
       "  0.000000  1.000000\n"
       "  2.000000  3.000000\n"
       "  4.000000  5.000000",
-      fmt::format("{}", C));
+      fmt::format("{:f}", C));
 }
 
 TEST(FormatterTest, Units) {
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/MathUtilTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/MathUtilTest.cpp
index a836a77..cee9831 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/MathUtilTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/MathUtilTest.cpp
@@ -4,8 +4,9 @@
 
 #include <limits>
 
+#include <gtest/gtest.h>
+
 #include "frc/MathUtil.h"
-#include "gtest/gtest.h"
 #include "units/angle.h"
 
 #define EXPECT_UNITS_EQ(a, b) EXPECT_DOUBLE_EQ((a).value(), (b).value())
@@ -117,3 +118,49 @@
   EXPECT_UNITS_EQ(frc::AngleModulus(units::radian_t{-std::numbers::pi / 2}),
                   units::radian_t{-std::numbers::pi / 2});
 }
+
+TEST(MathUtilTest, IsNear) {
+  // The answer is always 42
+  // Positive integer checks
+  EXPECT_TRUE(frc::IsNear(42, 42, 1));
+  EXPECT_TRUE(frc::IsNear(42, 41, 2));
+  EXPECT_TRUE(frc::IsNear(42, 43, 2));
+  EXPECT_FALSE(frc::IsNear(42, 44, 1));
+
+  // Negative integer checks
+  EXPECT_TRUE(frc::IsNear(-42, -42, 1));
+  EXPECT_TRUE(frc::IsNear(-42, -41, 2));
+  EXPECT_TRUE(frc::IsNear(-42, -43, 2));
+  EXPECT_FALSE(frc::IsNear(-42, -44, 1));
+
+  // Mixed sign integer checks
+  EXPECT_FALSE(frc::IsNear(-42, 42, 1));
+  EXPECT_FALSE(frc::IsNear(-42, 41, 2));
+  EXPECT_FALSE(frc::IsNear(-42, 43, 2));
+  EXPECT_FALSE(frc::IsNear(42, -42, 1));
+  EXPECT_FALSE(frc::IsNear(42, -41, 2));
+  EXPECT_FALSE(frc::IsNear(42, -43, 2));
+
+  // Floating point checks
+  EXPECT_TRUE(frc::IsNear<double>(42, 41.5, 1));
+  EXPECT_TRUE(frc::IsNear<double>(42, 42.5, 1));
+  EXPECT_TRUE(frc::IsNear<double>(42, 41.5, 0.75));
+  EXPECT_TRUE(frc::IsNear<double>(42, 42.5, 0.75));
+
+  // Wraparound checks
+  EXPECT_TRUE(frc::IsNear(0_deg, 356_deg, 5_deg, 0_deg, 360_deg));
+  EXPECT_TRUE(frc::IsNear(0, -356, 5, 0, 360));
+  EXPECT_TRUE(frc::IsNear(0, 4, 5, 0, 360));
+  EXPECT_TRUE(frc::IsNear(0, -4, 5, 0, 360));
+  EXPECT_TRUE(frc::IsNear(400, 41, 5, 0, 360));
+  EXPECT_TRUE(frc::IsNear(400, -319, 5, 0, 360));
+  EXPECT_TRUE(frc::IsNear(400, 401, 5, 0, 360));
+  EXPECT_FALSE(frc::IsNear<double>(0, 356, 2.5, 0, 360));
+  EXPECT_FALSE(frc::IsNear<double>(0, -356, 2.5, 0, 360));
+  EXPECT_FALSE(frc::IsNear<double>(0, 4, 2.5, 0, 360));
+  EXPECT_FALSE(frc::IsNear<double>(0, -4, 2.5, 0, 360));
+  EXPECT_FALSE(frc::IsNear(400, 35, 5, 0, 360));
+  EXPECT_FALSE(frc::IsNear(400, -315, 5, 0, 360));
+  EXPECT_FALSE(frc::IsNear(400, 395, 5, 0, 360));
+  EXPECT_FALSE(frc::IsNear(0_deg, -4_deg, 2.5_deg, 0_deg, 360_deg));
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/StateSpaceTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/StateSpaceTest.cpp
index ee7842c..07907e2 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/StateSpaceTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/StateSpaceTest.cpp
@@ -2,11 +2,11 @@
 // 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 <gtest/gtest.h>
-
 #include <cmath>
 #include <random>
 
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
 #include "frc/controller/LinearPlantInversionFeedforward.h"
 #include "frc/controller/LinearQuadraticRegulator.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/StateSpaceUtilTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/StateSpaceUtilTest.cpp
index 105a434..758dc30 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/StateSpaceUtilTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/StateSpaceUtilTest.cpp
@@ -2,10 +2,10 @@
 // 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 <gtest/gtest.h>
-
 #include <array>
 
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
 #include "frc/StateSpaceUtil.h"
 #include "frc/system/NumericalIntegration.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/UnitsTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/UnitsTest.cpp
index 1059260..1464dcd 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/UnitsTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/UnitsTest.cpp
@@ -7,10 +7,12 @@
 #include <string>
 #include <type_traits>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "units/acceleration.h"
 #include "units/angle.h"
 #include "units/angular_acceleration.h"
+#include "units/angular_jerk.h"
 #include "units/angular_velocity.h"
 #include "units/area.h"
 #include "units/capacitance.h"
@@ -51,6 +53,7 @@
 using namespace units::acceleration;
 using namespace units::angle;
 using namespace units::angular_acceleration;
+using namespace units::angular_jerk;
 using namespace units::angular_velocity;
 using namespace units::area;
 using namespace units::capacitance;
@@ -1420,7 +1423,7 @@
 }
 #endif
 
-#if !defined(UNIT_LIB_DISABLE_FMT)
+#if __has_include(<fmt/format.h>) && !defined(UNIT_LIB_DISABLE_FMT)
 TEST_F(UnitContainer, fmtlib) {
   testing::internal::CaptureStdout();
   fmt::print("{}", degree_t(349.87));
@@ -2002,6 +2005,23 @@
   EXPECT_NEAR(1.537e-16, test, 5.0e-20);
 }
 
+TEST_F(UnitConversion, angular_jerk) {
+  double test;
+  bool same;
+
+  same =
+      std::is_same_v<radians_per_second_cubed,
+                   unit<std::ratio<1>, category::angular_jerk_unit>>;
+  EXPECT_TRUE(same);
+  same = traits::is_convertible_unit_v<deg_per_s_cu, radians_per_second_cubed>;
+  EXPECT_TRUE(same);
+
+  test = convert<degrees_per_second_cubed, radians_per_second_cubed>(1.0);
+  EXPECT_NEAR(0.0174533, test, 5.0e-8);
+  test = convert<turns_per_second_cubed, radians_per_second_cubed>(1.0);
+  EXPECT_NEAR(6.283185307, test, 5.0e-6);
+}
+
 TEST_F(UnitConversion, acceleration) {
   double test;
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ArmFeedforwardTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ArmFeedforwardTest.cpp
new file mode 100644
index 0000000..b7853b1
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ArmFeedforwardTest.cpp
@@ -0,0 +1,76 @@
+// 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 <cmath>
+#include <numbers>
+
+#include <gtest/gtest.h>
+
+#include "frc/controller/ArmFeedforward.h"
+#include "units/acceleration.h"
+#include "units/length.h"
+#include "units/time.h"
+
+static constexpr auto Ks = 0.5_V;
+static constexpr auto Kv = 1.5_V * 1_s / 1_rad;
+static constexpr auto Ka = 2_V * 1_s * 1_s / 1_rad;
+static constexpr auto Kg = 1_V;
+
+TEST(ArmFeedforwardTest, Calculate) {
+  frc::ArmFeedforward armFF{Ks, Kg, Kv, Ka};
+  EXPECT_NEAR(
+      armFF.Calculate(std::numbers::pi * 1_rad / 3, 0_rad / 1_s).value(), 0.5,
+      0.002);
+  EXPECT_NEAR(
+      armFF.Calculate(std::numbers::pi * 1_rad / 3, 1_rad / 1_s).value(), 2.5,
+      0.002);
+  EXPECT_NEAR(armFF
+                  .Calculate(std::numbers::pi * 1_rad / 3, 1_rad / 1_s,
+                             2_rad / 1_s / 1_s)
+                  .value(),
+              6.5, 0.002);
+  EXPECT_NEAR(armFF
+                  .Calculate(std::numbers::pi * 1_rad / 3, -1_rad / 1_s,
+                             2_rad / 1_s / 1_s)
+                  .value(),
+              2.5, 0.002);
+}
+
+TEST(ArmFeedforwardTest, AchievableVelocity) {
+  frc::ArmFeedforward armFF{Ks, Kg, Kv, Ka};
+  EXPECT_NEAR(armFF
+                  .MaxAchievableVelocity(12_V, std::numbers::pi * 1_rad / 3,
+                                         1_rad / 1_s / 1_s)
+                  .value(),
+              6, 0.002);
+  EXPECT_NEAR(armFF
+                  .MinAchievableVelocity(11.5_V, std::numbers::pi * 1_rad / 3,
+                                         1_rad / 1_s / 1_s)
+                  .value(),
+              -9, 0.002);
+}
+
+TEST(ArmFeedforwardTest, AchievableAcceleration) {
+  frc::ArmFeedforward armFF{Ks, Kg, Kv, Ka};
+  EXPECT_NEAR(armFF
+                  .MaxAchievableAcceleration(12_V, std::numbers::pi * 1_rad / 3,
+                                             1_rad / 1_s)
+                  .value(),
+              4.75, 0.002);
+  EXPECT_NEAR(armFF
+                  .MaxAchievableAcceleration(12_V, std::numbers::pi * 1_rad / 3,
+                                             -1_rad / 1_s)
+                  .value(),
+              6.75, 0.002);
+  EXPECT_NEAR(armFF
+                  .MinAchievableAcceleration(12_V, std::numbers::pi * 1_rad / 3,
+                                             1_rad / 1_s)
+                  .value(),
+              -7.25, 0.002);
+  EXPECT_NEAR(armFF
+                  .MinAchievableAcceleration(12_V, std::numbers::pi * 1_rad / 3,
+                                             -1_rad / 1_s)
+                  .value(),
+              -5.25, 0.002);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/BangBangInputOutputTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/BangBangInputOutputTest.cpp
index 4dfaa0e..f931590 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/BangBangInputOutputTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/BangBangInputOutputTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/controller/BangBangController.h"
-#include "gtest/gtest.h"
 
 class BangBangInputOutputTest : public testing::Test {
  protected:
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/BangBangToleranceTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/BangBangToleranceTest.cpp
index e582720..3484e6e 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/BangBangToleranceTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/BangBangToleranceTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/controller/BangBangController.h"
-#include "gtest/gtest.h"
 
 class BangBangToleranceTest : public testing::Test {
  protected:
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp
index b2bcee6..9db64da 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp
@@ -2,10 +2,10 @@
 // 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 <gtest/gtest.h>
-
 #include <cmath>
 
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
 #include "frc/controller/ControlAffinePlantInversionFeedforward.h"
 #include "units/time.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/DifferentialDriveFeedforwardTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/DifferentialDriveFeedforwardTest.cpp
index 74d945c..5428d84 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/DifferentialDriveFeedforwardTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/DifferentialDriveFeedforwardTest.cpp
@@ -2,11 +2,11 @@
 // 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 <gtest/gtest.h>
-
 #include <cmath>
 
-#include "frc/EigenCore.h"
+#include <Eigen/Core>
+#include <gtest/gtest.h>
+
 #include "frc/controller/DifferentialDriveFeedforward.h"
 #include "frc/controller/LinearPlantInversionFeedforward.h"
 #include "frc/system/plant/LinearSystemId.h"
@@ -38,9 +38,9 @@
           auto [left, right] = differentialDriveFeedforward.Calculate(
               currentLeftVelocity, nextLeftVelocity, currentRightVelocity,
               nextRightVelocity, dt);
-          frc::Matrixd<2, 1> nextX = plant.CalculateX(
-              frc::Vectord<2>{currentLeftVelocity, currentRightVelocity},
-              frc::Vectord<2>{left, right}, dt);
+          Eigen::Vector2d nextX = plant.CalculateX(
+              Eigen::Vector2d{currentLeftVelocity, currentRightVelocity},
+              Eigen::Vector2d{left, right}, dt);
           EXPECT_NEAR(nextX(0), nextLeftVelocity.value(), 1e-6);
           EXPECT_NEAR(nextX(1), nextRightVelocity.value(), 1e-6);
         }
@@ -72,9 +72,9 @@
           auto [left, right] = differentialDriveFeedforward.Calculate(
               currentLeftVelocity, nextLeftVelocity, currentRightVelocity,
               nextRightVelocity, dt);
-          frc::Matrixd<2, 1> nextX = plant.CalculateX(
-              frc::Vectord<2>{currentLeftVelocity, currentRightVelocity},
-              frc::Vectord<2>{left, right}, dt);
+          Eigen::Vector2d nextX = plant.CalculateX(
+              Eigen::Vector2d{currentLeftVelocity, currentRightVelocity},
+              Eigen::Vector2d{left, right}, dt);
           EXPECT_NEAR(nextX(0), nextLeftVelocity.value(), 1e-6);
           EXPECT_NEAR(nextX(1), nextRightVelocity.value(), 1e-6);
         }
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ElevatorFeedforwardTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ElevatorFeedforwardTest.cpp
new file mode 100644
index 0000000..a2db70d
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ElevatorFeedforwardTest.cpp
@@ -0,0 +1,60 @@
+// 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 <cmath>
+
+#include <gtest/gtest.h>
+
+#include "frc/EigenCore.h"
+#include "frc/controller/ElevatorFeedforward.h"
+#include "frc/controller/LinearPlantInversionFeedforward.h"
+#include "units/acceleration.h"
+#include "units/length.h"
+#include "units/time.h"
+
+static constexpr auto Ks = 0.5_V;
+static constexpr auto Kv = 1.5_V * 1_s / 1_m;
+static constexpr auto Ka = 2_V * 1_s * 1_s / 1_m;
+static constexpr auto Kg = 1_V;
+
+TEST(ElevatorFeedforwardTest, Calculate) {
+  frc::ElevatorFeedforward elevatorFF{Ks, Kg, Kv, Ka};
+
+  EXPECT_NEAR(elevatorFF.Calculate(0_m / 1_s).value(), Kg.value(), 0.002);
+  EXPECT_NEAR(elevatorFF.Calculate(2_m / 1_s).value(), 4.5, 0.002);
+  EXPECT_NEAR(elevatorFF.Calculate(2_m / 1_s, 1_m / 1_s / 1_s).value(), 6.5,
+              0.002);
+  EXPECT_NEAR(elevatorFF.Calculate(-2_m / 1_s, 1_m / 1_s / 1_s).value(), -0.5,
+              0.002);
+
+  frc::Matrixd<1, 1> A{-Kv.value() / Ka.value()};
+  frc::Matrixd<1, 1> B{1.0 / Ka.value()};
+  constexpr units::second_t dt = 20_ms;
+  frc::LinearPlantInversionFeedforward<1, 1> plantInversion{A, B, dt};
+
+  frc::Vectord<1> r{2.0};
+  frc::Vectord<1> nextR{3.0};
+  EXPECT_NEAR(plantInversion.Calculate(r, nextR)(0) + Ks.value() + Kg.value(),
+              elevatorFF.Calculate(2_mps, 3_mps, dt).value(), 0.002);
+}
+
+TEST(ElevatorFeedforwardTest, AchievableVelocity) {
+  frc::ElevatorFeedforward elevatorFF{Ks, Kg, Kv, Ka};
+  EXPECT_NEAR(elevatorFF.MaxAchievableVelocity(11_V, 1_m / 1_s / 1_s).value(),
+              5, 0.002);
+  EXPECT_NEAR(elevatorFF.MinAchievableVelocity(11_V, 1_m / 1_s / 1_s).value(),
+              -9, 0.002);
+}
+
+TEST(ElevatorFeedforwardTest, AchievableAcceleration) {
+  frc::ElevatorFeedforward elevatorFF{Ks, Kg, Kv, Ka};
+  EXPECT_NEAR(elevatorFF.MaxAchievableAcceleration(12_V, 2_m / 1_s).value(),
+              3.75, 0.002);
+  EXPECT_NEAR(elevatorFF.MaxAchievableAcceleration(12_V, -2_m / 1_s).value(),
+              7.25, 0.002);
+  EXPECT_NEAR(elevatorFF.MinAchievableAcceleration(12_V, 2_m / 1_s).value(),
+              -8.25, 0.002);
+  EXPECT_NEAR(elevatorFF.MinAchievableAcceleration(12_V, -2_m / 1_s).value(),
+              -4.75, 0.002);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/HolonomicDriveControllerTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/HolonomicDriveControllerTest.cpp
index 134a4e0..4b4a883 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/HolonomicDriveControllerTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/HolonomicDriveControllerTest.cpp
@@ -4,10 +4,11 @@
 
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/MathUtil.h"
 #include "frc/controller/HolonomicDriveController.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 #include "units/angular_acceleration.h"
 #include "units/math.h"
 #include "units/time.h"
@@ -21,7 +22,7 @@
 
 TEST(HolonomicDriveControllerTest, ReachesReference) {
   frc::HolonomicDriveController controller{
-      frc2::PIDController{1.0, 0.0, 0.0}, frc2::PIDController{1.0, 0.0, 0.0},
+      frc::PIDController{1.0, 0.0, 0.0}, frc::PIDController{1.0, 0.0, 0.0},
       frc::ProfiledPIDController<units::radian>{
           1.0, 0.0, 0.0,
           frc::TrapezoidProfile<units::radian>::Constraints{
@@ -53,7 +54,7 @@
 
 TEST(HolonomicDriveControllerTest, DoesNotRotateUnnecessarily) {
   frc::HolonomicDriveController controller{
-      frc2::PIDController{1, 0, 0}, frc2::PIDController{1, 0, 0},
+      frc::PIDController{1, 0, 0}, frc::PIDController{1, 0, 0},
       frc::ProfiledPIDController<units::radian>{
           1, 0, 0,
           frc::TrapezoidProfile<units::radian>::Constraints{
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LTVDifferentialDriveControllerTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LTVDifferentialDriveControllerTest.cpp
index 753ea34..d96c67b 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LTVDifferentialDriveControllerTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LTVDifferentialDriveControllerTest.cpp
@@ -4,12 +4,13 @@
 
 #include <cmath>
 
+#include <gtest/gtest.h>
+
 #include "frc/MathUtil.h"
 #include "frc/controller/LTVDifferentialDriveController.h"
 #include "frc/system/NumericalIntegration.h"
 #include "frc/system/plant/LinearSystemId.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 #include "units/math.h"
 
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LTVUnicycleControllerTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LTVUnicycleControllerTest.cpp
index 56faf1d..ff2c7d3 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LTVUnicycleControllerTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LTVUnicycleControllerTest.cpp
@@ -2,10 +2,11 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/MathUtil.h"
 #include "frc/controller/LTVUnicycleController.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 #include "units/math.h"
 
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LinearPlantInversionFeedforwardTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LinearPlantInversionFeedforwardTest.cpp
index e7107d0..a92d4f0 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LinearPlantInversionFeedforwardTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LinearPlantInversionFeedforwardTest.cpp
@@ -2,10 +2,10 @@
 // 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 <gtest/gtest.h>
-
 #include <cmath>
 
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
 #include "frc/controller/LinearPlantInversionFeedforward.h"
 #include "units/time.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LinearQuadraticRegulatorTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LinearQuadraticRegulatorTest.cpp
index 1127fc2..45f6473 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LinearQuadraticRegulatorTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/LinearQuadraticRegulatorTest.cpp
@@ -2,10 +2,10 @@
 // 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 <gtest/gtest.h>
-
 #include <cmath>
 
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
 #include "frc/controller/LinearQuadraticRegulator.h"
 #include "frc/system/LinearSystem.h"
@@ -158,10 +158,10 @@
   EXPECT_NEAR(0.51182128351092726, K(0, 1), 1e-10);
 
   // QRN overload
-  Matrixd<2, 2> Aref{{0, 1}, {0, -Kv / (Ka * 2.0)}};
+  Matrixd<2, 2> Aref{{0, 1}, {0, -Kv / (Ka * 5.0)}};
   Matrixd<1, 2> Kimf = GetImplicitModelFollowingK<2, 1>(A, B, Q, R, Aref, 5_ms);
   EXPECT_NEAR(0.0, Kimf(0, 0), 1e-10);
-  EXPECT_NEAR(-5.367540084534802e-05, Kimf(0, 1), 1e-10);
+  EXPECT_NEAR(-6.9190500116751458e-05, Kimf(0, 1), 1e-10);
 }
 
 TEST(LinearQuadraticRegulatorTest, LatencyCompensate) {
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/PIDInputOutputTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/PIDInputOutputTest.cpp
index f1034f5..7992622 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/PIDInputOutputTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/PIDInputOutputTest.cpp
@@ -2,14 +2,15 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/controller/PIDController.h"
-#include "gtest/gtest.h"
 
 class PIDInputOutputTest : public testing::Test {
  protected:
-  frc2::PIDController* controller;
+  frc::PIDController* controller;
 
-  void SetUp() override { controller = new frc2::PIDController{0, 0, 0}; }
+  void SetUp() override { controller = new frc::PIDController{0, 0, 0}; }
 
   void TearDown() override { delete controller; }
 };
@@ -49,3 +50,21 @@
   EXPECT_DOUBLE_EQ(-10_ms / controller->GetPeriod(),
                    controller->Calculate(0.0025, 0));
 }
+
+TEST_F(PIDInputOutputTest, IZoneNoOutput) {
+  controller->SetI(1);
+  controller->SetIZone(1);
+
+  double out = controller->Calculate(2, 0);
+
+  EXPECT_DOUBLE_EQ(0, out);
+}
+
+TEST_F(PIDInputOutputTest, IZoneOutput) {
+  controller->SetI(1);
+  controller->SetIZone(1);
+
+  double out = controller->Calculate(1, 0);
+
+  EXPECT_DOUBLE_EQ(-1 * controller->GetPeriod().value(), out);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/PIDToleranceTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/PIDToleranceTest.cpp
index 0aec438..f37be89 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/PIDToleranceTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/PIDToleranceTest.cpp
@@ -2,27 +2,26 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/controller/PIDController.h"
-#include "gtest/gtest.h"
 
 static constexpr double kSetpoint = 50.0;
 static constexpr double kRange = 200;
 static constexpr double kTolerance = 10.0;
 
 TEST(PIDToleranceTest, InitialTolerance) {
-  frc2::PIDController controller{0.5, 0.0, 0.0};
+  frc::PIDController controller{0.5, 0.0, 0.0};
   controller.EnableContinuousInput(-kRange / 2, kRange / 2);
 
-  EXPECT_TRUE(controller.AtSetpoint());
+  EXPECT_FALSE(controller.AtSetpoint());
 }
 
 TEST(PIDToleranceTest, AbsoluteTolerance) {
-  frc2::PIDController controller{0.5, 0.0, 0.0};
+  frc::PIDController controller{0.5, 0.0, 0.0};
   controller.EnableContinuousInput(-kRange / 2, kRange / 2);
 
-  EXPECT_TRUE(controller.AtSetpoint())
-      << "Error was not in tolerance when it should have been. Error was "
-      << controller.GetPositionError();
+  EXPECT_FALSE(controller.AtSetpoint());
 
   controller.SetTolerance(kTolerance);
   controller.SetSetpoint(kSetpoint);
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ProfiledPIDInputOutputTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ProfiledPIDInputOutputTest.cpp
index 44d1f41..5e4f01a 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ProfiledPIDInputOutputTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/ProfiledPIDInputOutputTest.cpp
@@ -4,8 +4,9 @@
 
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/controller/ProfiledPIDController.h"
-#include "gtest/gtest.h"
 #include "units/angle.h"
 #include "units/angular_acceleration.h"
 #include "units/angular_velocity.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/RamseteControllerTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/RamseteControllerTest.cpp
index 2fd26bb..d39c679 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/RamseteControllerTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/RamseteControllerTest.cpp
@@ -2,10 +2,11 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/MathUtil.h"
 #include "frc/controller/RamseteController.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 #include "units/math.h"
 
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/SimpleMotorFeedforwardTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/SimpleMotorFeedforwardTest.cpp
index 6d10e05..7878011 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/controller/SimpleMotorFeedforwardTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/controller/SimpleMotorFeedforwardTest.cpp
@@ -2,10 +2,10 @@
 // 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 <gtest/gtest.h>
-
 #include <cmath>
 
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
 #include "frc/controller/LinearPlantInversionFeedforward.h"
 #include "frc/controller/SimpleMotorFeedforward.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/drake/discrete_algebraic_riccati_equation_test.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/drake/discrete_algebraic_riccati_equation_test.cpp
deleted file mode 100644
index 8631d6f..0000000
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/drake/discrete_algebraic_riccati_equation_test.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-#include "drake/math/discrete_algebraic_riccati_equation.h"
-
-#include <Eigen/Eigenvalues>
-#include <gtest/gtest.h>
-
-#include "drake/common/test_utilities/eigen_matrix_compare.h"
-// #include "drake/math/autodiff.h"
-
-using Eigen::MatrixXd;
-
-namespace drake {
-namespace math {
-namespace {
-void SolveDAREandVerify(const Eigen::Ref<const MatrixXd>& A,
-                        const Eigen::Ref<const MatrixXd>& B,
-                        const Eigen::Ref<const MatrixXd>& Q,
-                        const Eigen::Ref<const MatrixXd>& R) {
-  MatrixXd X = DiscreteAlgebraicRiccatiEquation(A, B, Q, R);
-  // Check that X is positive semi-definite.
-  EXPECT_TRUE(
-      CompareMatrices(X, X.transpose(), 1E-10, MatrixCompareType::absolute));
-  int n = X.rows();
-  Eigen::SelfAdjointEigenSolver<MatrixXd> es(X);
-  for (int i = 0; i < n; i++) {
-    EXPECT_GE(es.eigenvalues()[i], 0);
-  }
-  // Check that X is the solution to the discrete time ARE.
-  // clang-format off
-  MatrixXd Y =
-      A.transpose() * X * A
-      - X
-      - (A.transpose() * X * B * (B.transpose() * X * B + R).inverse()
-        * B.transpose() * X * A)
-      + Q;
-  // clang-format on
-  EXPECT_TRUE(CompareMatrices(Y, MatrixXd::Zero(n, n), 1E-10,
-                              MatrixCompareType::absolute));
-}
-
-void SolveDAREandVerify(const Eigen::Ref<const MatrixXd>& A,
-                        const Eigen::Ref<const MatrixXd>& B,
-                        const Eigen::Ref<const MatrixXd>& Q,
-                        const Eigen::Ref<const MatrixXd>& R,
-                        const Eigen::Ref<const MatrixXd>& N) {
-  MatrixXd X = DiscreteAlgebraicRiccatiEquation(A, B, Q, R, N);
-  // Check that X is positive semi-definite.
-  EXPECT_TRUE(
-      CompareMatrices(X, X.transpose(), 1E-10, MatrixCompareType::absolute));
-  int n = X.rows();
-  Eigen::SelfAdjointEigenSolver<MatrixXd> es(X);
-  for (int i = 0; i < n; i++) {
-    EXPECT_GE(es.eigenvalues()[i], 0);
-  }
-  // Check that X is the solution to the discrete time ARE.
-  // clang-format off
-  MatrixXd Y =
-      A.transpose() * X * A
-      - X
-      - ((A.transpose() * X * B + N) * (B.transpose() * X * B + R).inverse()
-        * (B.transpose() * X * A + N.transpose()))
-      + Q;
-  // clang-format on
-  EXPECT_TRUE(CompareMatrices(Y, MatrixXd::Zero(n, n), 1E-10,
-                              MatrixCompareType::absolute));
-}
-
-GTEST_TEST(DARE, SolveDAREandVerify) {
-  // Test 1: non-invertible A
-  // Example 2 of "On the Numerical Solution of the Discrete-Time Algebraic
-  // Riccati Equation"
-  int n1 = 4, m1 = 1;
-  MatrixXd A1(n1, n1), B1(n1, m1), Q1(n1, n1), R1(m1, m1);
-  A1 << 0.5, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0;
-  B1 << 0, 0, 0, 1;
-  Q1 << 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
-  R1 << 0.25;
-  SolveDAREandVerify(A1, B1, Q1, R1);
-
-  MatrixXd Aref1(n1, n1);
-  Aref1 << 0.25, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0;
-  SolveDAREandVerify(A1, B1, (A1 - Aref1).transpose() * Q1 * (A1 - Aref1),
-      B1.transpose() * Q1 * B1 + R1, (A1 - Aref1).transpose() * Q1 * B1);
-
-  // Test 2: invertible A
-  int n2 = 2, m2 = 1;
-  MatrixXd A2(n2, n2), B2(n2, m2), Q2(n2, n2), R2(m2, m2);
-  A2 << 1, 1, 0, 1;
-  B2 << 0, 1;
-  Q2 << 1, 0, 0, 0;
-  R2 << 0.3;
-  SolveDAREandVerify(A2, B2, Q2, R2);
-
-  MatrixXd Aref2(n2, n2);
-  Aref2 << 0.5, 1, 0, 1;
-  SolveDAREandVerify(A2, B2, (A2 - Aref2).transpose() * Q2 * (A2 - Aref2),
-      B2.transpose() * Q2 * B2 + R2, (A2 - Aref2).transpose() * Q2 * B2);
-
-  // Test 3: the first generalized eigenvalue of (S,T) is stable
-  int n3 = 2, m3 = 1;
-  MatrixXd A3(n3, n3), B3(n3, m3), Q3(n3, n3), R3(m3, m3);
-  A3 << 0, 1, 0, 0;
-  B3 << 0, 1;
-  Q3 << 1, 0, 0, 1;
-  R3 << 1;
-  SolveDAREandVerify(A3, B3, Q3, R3);
-
-  MatrixXd Aref3(n3, n3);
-  Aref3 << 0, 0.5, 0, 0;
-  SolveDAREandVerify(A3, B3, (A3 - Aref3).transpose() * Q3 * (A3 - Aref3),
-      B3.transpose() * Q3 * B3 + R3, (A3 - Aref3).transpose() * Q3 * B3);
-
-  // Test 4: A = B = Q = R = I_2 (2-by-2 identity matrix)
-  const Eigen::MatrixXd A4{Eigen::Matrix2d::Identity()};
-  const Eigen::MatrixXd B4{Eigen::Matrix2d::Identity()};
-  const Eigen::MatrixXd Q4{Eigen::Matrix2d::Identity()};
-  const Eigen::MatrixXd R4{Eigen::Matrix2d::Identity()};
-  SolveDAREandVerify(A4, B4, Q4, R4);
-
-  const Eigen::MatrixXd N4{Eigen::Matrix2d::Identity()};
-  SolveDAREandVerify(A4, B4, Q4, R4, N4);
-}
-}  // namespace
-}  // namespace math
-}  // namespace drake
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/AngleStatisticsTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/AngleStatisticsTest.cpp
index b2ee87d..d62decd 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/AngleStatisticsTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/AngleStatisticsTest.cpp
@@ -2,10 +2,10 @@
 // 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 <gtest/gtest.h>
-
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
 #include "frc/estimator/AngleStatistics.h"
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/DifferentialDrivePoseEstimatorTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/DifferentialDrivePoseEstimatorTest.cpp
index 1d623ca..3103068 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/DifferentialDrivePoseEstimatorTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/DifferentialDrivePoseEstimatorTest.cpp
@@ -7,13 +7,14 @@
 #include <tuple>
 #include <utility>
 
+#include <gtest/gtest.h>
+
 #include "frc/StateSpaceUtil.h"
 #include "frc/estimator/DifferentialDrivePoseEstimator.h"
 #include "frc/geometry/Pose2d.h"
 #include "frc/geometry/Rotation2d.h"
 #include "frc/kinematics/DifferentialDriveKinematics.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 #include "units/angle.h"
 #include "units/length.h"
 #include "units/time.h"
@@ -138,6 +139,7 @@
               0.15);
 
   if (checkError) {
+    // NOLINTNEXTLINE(bugprone-integer-division)
     EXPECT_LT(errorSum / (trajectory.TotalTime() / dt), 0.05);
     EXPECT_LT(maxError, 0.2);
   }
@@ -207,3 +209,87 @@
     }
   }
 }
+
+TEST(DifferentialDrivePoseEstimatorTest, SimultaneousVisionMeasurements) {
+  // This tests for multiple vision measurements appled at the same time.
+  // The expected behavior is that all measurements affect the estimated pose.
+  // The alternative result is that only one vision measurement affects the
+  // outcome. If that were the case, after 1000 measurements, the estimated
+  // pose would converge to that measurement.
+  frc::DifferentialDriveKinematics kinematics{1.0_m};
+
+  frc::DifferentialDrivePoseEstimator estimator{
+      kinematics,
+      frc::Rotation2d{},
+      0_m,
+      0_m,
+      frc::Pose2d{1_m, 2_m, frc::Rotation2d{270_deg}},
+      {0.02, 0.02, 0.01},
+      {0.1, 0.1, 0.1}};
+
+  estimator.UpdateWithTime(0_s, frc::Rotation2d{}, 0_m, 0_m);
+
+  for (int i = 0; i < 1000; i++) {
+    estimator.AddVisionMeasurement(
+        frc::Pose2d{0_m, 0_m, frc::Rotation2d{0_deg}}, 0_s);
+    estimator.AddVisionMeasurement(
+        frc::Pose2d{3_m, 1_m, frc::Rotation2d{90_deg}}, 0_s);
+    estimator.AddVisionMeasurement(
+        frc::Pose2d{2_m, 4_m, frc::Rotation2d{180_deg}}, 0_s);
+  }
+
+  {
+    auto dx = units::math::abs(estimator.GetEstimatedPosition().X() - 0_m);
+    auto dy = units::math::abs(estimator.GetEstimatedPosition().Y() - 0_m);
+    auto dtheta = units::math::abs(
+        estimator.GetEstimatedPosition().Rotation().Radians() - 0_deg);
+
+    EXPECT_TRUE(dx > 0.08_m || dy > 0.08_m || dtheta > 0.08_rad);
+  }
+
+  {
+    auto dx = units::math::abs(estimator.GetEstimatedPosition().X() - 3_m);
+    auto dy = units::math::abs(estimator.GetEstimatedPosition().Y() - 1_m);
+    auto dtheta = units::math::abs(
+        estimator.GetEstimatedPosition().Rotation().Radians() - 90_deg);
+
+    EXPECT_TRUE(dx > 0.08_m || dy > 0.08_m || dtheta > 0.08_rad);
+  }
+
+  {
+    auto dx = units::math::abs(estimator.GetEstimatedPosition().X() - 2_m);
+    auto dy = units::math::abs(estimator.GetEstimatedPosition().Y() - 4_m);
+    auto dtheta = units::math::abs(
+        estimator.GetEstimatedPosition().Rotation().Radians() - 180_deg);
+
+    EXPECT_TRUE(dx > 0.08_m || dy > 0.08_m || dtheta > 0.08_rad);
+  }
+}
+
+TEST(DifferentialDrivePoseEstimatorTest, TestDiscardStaleVisionMeasurements) {
+  frc::DifferentialDriveKinematics kinematics{1_m};
+
+  frc::DifferentialDrivePoseEstimator estimator{
+      kinematics,      frc::Rotation2d{}, 0_m, 0_m, frc::Pose2d{},
+      {0.1, 0.1, 0.1}, {0.45, 0.45, 0.45}};
+
+  // Add enough measurements to fill up the buffer
+  for (auto time = 0.0_s; time < 4_s; time += 0.02_s) {
+    estimator.UpdateWithTime(time, frc::Rotation2d{}, 0_m, 0_m);
+  }
+
+  auto odometryPose = estimator.GetEstimatedPosition();
+
+  // Apply a vision measurement from 3 seconds ago
+  estimator.AddVisionMeasurement(
+      frc::Pose2d{frc::Translation2d{10_m, 10_m}, frc::Rotation2d{0.1_rad}},
+      1_s, {0.1, 0.1, 0.1});
+
+  EXPECT_NEAR(odometryPose.X().value(),
+              estimator.GetEstimatedPosition().X().value(), 1e-6);
+  EXPECT_NEAR(odometryPose.Y().value(),
+              estimator.GetEstimatedPosition().Y().value(), 1e-6);
+  EXPECT_NEAR(odometryPose.Rotation().Radians().value(),
+              estimator.GetEstimatedPosition().Rotation().Radians().value(),
+              1e-6);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/ExtendedKalmanFilterTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/ExtendedKalmanFilterTest.cpp
index 1924b73..2a5ce80 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/ExtendedKalmanFilterTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/ExtendedKalmanFilterTest.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
-
 #include <array>
 #include <cmath>
 
-#include "Eigen/QR"
+#include <Eigen/QR>
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
 #include "frc/StateSpaceUtil.h"
 #include "frc/estimator/ExtendedKalmanFilter.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/KalmanFilterTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/KalmanFilterTest.cpp
index fc373ca..6697ec0 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/KalmanFilterTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/KalmanFilterTest.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
-
 #include <array>
 #include <cmath>
 
-#include "Eigen/Core"
+#include <Eigen/Core>
+#include <gtest/gtest.h>
+
 #include "frc/estimator/KalmanFilter.h"
 #include "frc/system/plant/DCMotor.h"
 #include "frc/system/plant/LinearSystemId.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/MecanumDrivePoseEstimatorTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/MecanumDrivePoseEstimatorTest.cpp
index 13dc5aa..8b3a586 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/MecanumDrivePoseEstimatorTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/MecanumDrivePoseEstimatorTest.cpp
@@ -6,11 +6,12 @@
 #include <random>
 #include <tuple>
 
+#include <gtest/gtest.h>
+
 #include "frc/estimator/MecanumDrivePoseEstimator.h"
 #include "frc/geometry/Pose2d.h"
 #include "frc/kinematics/MecanumDriveKinematics.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 
 void testFollowTrajectory(
     const frc::MecanumDriveKinematics& kinematics,
@@ -129,6 +130,7 @@
               0.15);
 
   if (checkError) {
+    // NOLINTNEXTLINE(bugprone-integer-division)
     EXPECT_LT(errorSum / (trajectory.TotalTime() / dt), 0.051);
     EXPECT_LT(maxError, 0.2);
   }
@@ -206,3 +208,90 @@
     }
   }
 }
+
+TEST(MecanumDrivePoseEstimatorTest, SimultaneousVisionMeasurements) {
+  // This tests for multiple vision measurements appled at the same time.
+  // The expected behavior is that all measurements affect the estimated pose.
+  // The alternative result is that only one vision measurement affects the
+  // outcome. If that were the case, after 1000 measurements, the estimated
+  // pose would converge to that measurement.
+  frc::MecanumDriveKinematics kinematics{
+      frc::Translation2d{1_m, 1_m}, frc::Translation2d{1_m, -1_m},
+      frc::Translation2d{-1_m, -1_m}, frc::Translation2d{-1_m, 1_m}};
+
+  frc::MecanumDriveWheelPositions wheelPositions;
+
+  frc::MecanumDrivePoseEstimator estimator{
+      kinematics,      frc::Rotation2d{},
+      wheelPositions,  frc::Pose2d{1_m, 2_m, frc::Rotation2d{270_deg}},
+      {0.1, 0.1, 0.1}, {0.45, 0.45, 0.1}};
+
+  estimator.UpdateWithTime(0_s, frc::Rotation2d{}, wheelPositions);
+
+  for (int i = 0; i < 1000; i++) {
+    estimator.AddVisionMeasurement(
+        frc::Pose2d{0_m, 0_m, frc::Rotation2d{0_deg}}, 0_s);
+    estimator.AddVisionMeasurement(
+        frc::Pose2d{3_m, 1_m, frc::Rotation2d{90_deg}}, 0_s);
+    estimator.AddVisionMeasurement(
+        frc::Pose2d{2_m, 4_m, frc::Rotation2d{180_deg}}, 0_s);
+  }
+
+  {
+    auto dx = units::math::abs(estimator.GetEstimatedPosition().X() - 0_m);
+    auto dy = units::math::abs(estimator.GetEstimatedPosition().Y() - 0_m);
+    auto dtheta = units::math::abs(
+        estimator.GetEstimatedPosition().Rotation().Radians() - 0_deg);
+
+    EXPECT_TRUE(dx > 0.08_m || dy > 0.08_m || dtheta > 0.08_rad);
+  }
+
+  {
+    auto dx = units::math::abs(estimator.GetEstimatedPosition().X() - 3_m);
+    auto dy = units::math::abs(estimator.GetEstimatedPosition().Y() - 1_m);
+    auto dtheta = units::math::abs(
+        estimator.GetEstimatedPosition().Rotation().Radians() - 90_deg);
+
+    EXPECT_TRUE(dx > 0.08_m || dy > 0.08_m || dtheta > 0.08_rad);
+  }
+
+  {
+    auto dx = units::math::abs(estimator.GetEstimatedPosition().X() - 2_m);
+    auto dy = units::math::abs(estimator.GetEstimatedPosition().Y() - 4_m);
+    auto dtheta = units::math::abs(
+        estimator.GetEstimatedPosition().Rotation().Radians() - 180_deg);
+
+    EXPECT_TRUE(dx > 0.08_m || dy > 0.08_m || dtheta > 0.08_rad);
+  }
+}
+
+TEST(MecanumDrivePoseEstimatorTest, TestDiscardStaleVisionMeasurements) {
+  frc::MecanumDriveKinematics kinematics{
+      frc::Translation2d{1_m, 1_m}, frc::Translation2d{1_m, -1_m},
+      frc::Translation2d{-1_m, -1_m}, frc::Translation2d{-1_m, 1_m}};
+
+  frc::MecanumDrivePoseEstimator estimator{
+      kinematics,    frc::Rotation2d{}, frc::MecanumDriveWheelPositions{},
+      frc::Pose2d{}, {0.1, 0.1, 0.1},   {0.45, 0.45, 0.45}};
+
+  // Add enough measurements to fill up the buffer
+  for (auto time = 0.0_s; time < 4_s; time += 0.02_s) {
+    estimator.UpdateWithTime(time, frc::Rotation2d{},
+                             frc::MecanumDriveWheelPositions{});
+  }
+
+  auto odometryPose = estimator.GetEstimatedPosition();
+
+  // Apply a vision measurement from 3 seconds ago
+  estimator.AddVisionMeasurement(
+      frc::Pose2d{frc::Translation2d{10_m, 10_m}, frc::Rotation2d{0.1_rad}},
+      1_s, {0.1, 0.1, 0.1});
+
+  EXPECT_NEAR(odometryPose.X().value(),
+              estimator.GetEstimatedPosition().X().value(), 1e-6);
+  EXPECT_NEAR(odometryPose.Y().value(),
+              estimator.GetEstimatedPosition().Y().value(), 1e-6);
+  EXPECT_NEAR(odometryPose.Rotation().Radians().value(),
+              estimator.GetEstimatedPosition().Rotation().Radians().value(),
+              1e-6);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/MerweScaledSigmaPointsTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/MerweScaledSigmaPointsTest.cpp
index 19a734b..4471dc9 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/MerweScaledSigmaPointsTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/MerweScaledSigmaPointsTest.cpp
@@ -6,8 +6,6 @@
 
 #include "frc/estimator/MerweScaledSigmaPoints.h"
 
-namespace drake::math {
-namespace {
 TEST(MerweScaledSigmaPointsTest, ZeroMean) {
   frc::MerweScaledSigmaPoints<2> sigmaPoints;
   auto points = sigmaPoints.SquareRootSigmaPoints(
@@ -30,5 +28,3 @@
                                    {2.0, 2.0, 2.00548, 2.0, 1.99452}})
           .norm() < 1e-3);
 }
-}  // namespace
-}  // namespace drake::math
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/SwerveDrivePoseEstimatorTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/SwerveDrivePoseEstimatorTest.cpp
index 554ca59..f1024ef 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/SwerveDrivePoseEstimatorTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/SwerveDrivePoseEstimatorTest.cpp
@@ -8,12 +8,13 @@
 #include <tuple>
 
 #include <fmt/format.h>
+#include <gtest/gtest.h>
+#include <wpi/timestamp.h>
 
 #include "frc/estimator/SwerveDrivePoseEstimator.h"
 #include "frc/geometry/Pose2d.h"
 #include "frc/kinematics/SwerveDriveKinematics.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 
 void testFollowTrajectory(
     const frc::SwerveDriveKinematics<4>& kinematics,
@@ -132,6 +133,7 @@
               0.15);
 
   if (checkError) {
+    // NOLINTNEXTLINE(bugprone-integer-division)
     EXPECT_LT(errorSum / (trajectory.TotalTime() / dt), 0.058);
     EXPECT_LT(maxError, 0.2);
   }
@@ -215,3 +217,97 @@
     }
   }
 }
+
+TEST(SwerveDrivePoseEstimatorTest, SimultaneousVisionMeasurements) {
+  // This tests for multiple vision measurements appled at the same time.
+  // The expected behavior is that all measurements affect the estimated pose.
+  // The alternative result is that only one vision measurement affects the
+  // outcome. If that were the case, after 1000 measurements, the estimated
+  // pose would converge to that measurement.
+  frc::SwerveDriveKinematics<4> kinematics{
+      frc::Translation2d{1_m, 1_m}, frc::Translation2d{1_m, -1_m},
+      frc::Translation2d{-1_m, -1_m}, frc::Translation2d{-1_m, 1_m}};
+
+  frc::SwerveModulePosition fl;
+  frc::SwerveModulePosition fr;
+  frc::SwerveModulePosition bl;
+  frc::SwerveModulePosition br;
+
+  frc::SwerveDrivePoseEstimator<4> estimator{
+      kinematics,       frc::Rotation2d{},
+      {fl, fr, bl, br}, frc::Pose2d{1_m, 2_m, frc::Rotation2d{270_deg}},
+      {0.1, 0.1, 0.1},  {0.45, 0.45, 0.45}};
+
+  estimator.UpdateWithTime(0_s, frc::Rotation2d{}, {fl, fr, bl, br});
+
+  for (int i = 0; i < 1000; i++) {
+    estimator.AddVisionMeasurement(
+        frc::Pose2d{0_m, 0_m, frc::Rotation2d{0_deg}}, 0_s);
+    estimator.AddVisionMeasurement(
+        frc::Pose2d{3_m, 1_m, frc::Rotation2d{90_deg}}, 0_s);
+    estimator.AddVisionMeasurement(
+        frc::Pose2d{2_m, 4_m, frc::Rotation2d{180_deg}}, 0_s);
+  }
+
+  {
+    auto dx = units::math::abs(estimator.GetEstimatedPosition().X() - 0_m);
+    auto dy = units::math::abs(estimator.GetEstimatedPosition().Y() - 0_m);
+    auto dtheta = units::math::abs(
+        estimator.GetEstimatedPosition().Rotation().Radians() - 0_deg);
+
+    EXPECT_TRUE(dx > 0.08_m || dy > 0.08_m || dtheta > 0.08_rad);
+  }
+
+  {
+    auto dx = units::math::abs(estimator.GetEstimatedPosition().X() - 3_m);
+    auto dy = units::math::abs(estimator.GetEstimatedPosition().Y() - 1_m);
+    auto dtheta = units::math::abs(
+        estimator.GetEstimatedPosition().Rotation().Radians() - 90_deg);
+
+    EXPECT_TRUE(dx > 0.08_m || dy > 0.08_m || dtheta > 0.08_rad);
+  }
+
+  {
+    auto dx = units::math::abs(estimator.GetEstimatedPosition().X() - 2_m);
+    auto dy = units::math::abs(estimator.GetEstimatedPosition().Y() - 4_m);
+    auto dtheta = units::math::abs(
+        estimator.GetEstimatedPosition().Rotation().Radians() - 180_deg);
+
+    EXPECT_TRUE(dx > 0.08_m || dy > 0.08_m || dtheta > 0.08_rad);
+  }
+}
+
+TEST(SwerveDrivePoseEstimatorTest, TestDiscardStaleVisionMeasurements) {
+  frc::SwerveDriveKinematics<4> kinematics{
+      frc::Translation2d{1_m, 1_m}, frc::Translation2d{1_m, -1_m},
+      frc::Translation2d{-1_m, -1_m}, frc::Translation2d{-1_m, 1_m}};
+
+  frc::SwerveModulePosition fl;
+  frc::SwerveModulePosition fr;
+  frc::SwerveModulePosition bl;
+  frc::SwerveModulePosition br;
+
+  frc::SwerveDrivePoseEstimator<4> estimator{
+      kinematics,    frc::Rotation2d{}, {fl, fr, bl, br},
+      frc::Pose2d{}, {0.1, 0.1, 0.1},   {0.45, 0.45, 0.45}};
+
+  // Add enough measurements to fill up the buffer
+  for (auto time = 0.0_s; time < 4_s; time += 0.02_s) {
+    estimator.UpdateWithTime(time, frc::Rotation2d{}, {fl, fr, bl, br});
+  }
+
+  auto odometryPose = estimator.GetEstimatedPosition();
+
+  // Apply a vision measurement from 3 seconds ago
+  estimator.AddVisionMeasurement(
+      frc::Pose2d{frc::Translation2d{10_m, 10_m}, frc::Rotation2d{0.1_rad}},
+      1_s, {0.1, 0.1, 0.1});
+
+  EXPECT_NEAR(odometryPose.X().value(),
+              estimator.GetEstimatedPosition().X().value(), 1e-6);
+  EXPECT_NEAR(odometryPose.Y().value(),
+              estimator.GetEstimatedPosition().Y().value(), 1e-6);
+  EXPECT_NEAR(odometryPose.Rotation().Radians().value(),
+              estimator.GetEstimatedPosition().Rotation().Radians().value(),
+              1e-6);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/UnscentedKalmanFilterTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/UnscentedKalmanFilterTest.cpp
index 68f9c40..0f97a88 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/UnscentedKalmanFilterTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/estimator/UnscentedKalmanFilterTest.cpp
@@ -2,12 +2,12 @@
 // 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 <gtest/gtest.h>
-
 #include <array>
 #include <cmath>
 
-#include "Eigen/QR"
+#include <Eigen/QR>
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
 #include "frc/StateSpaceUtil.h"
 #include "frc/estimator/AngleStatistics.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/filter/DebouncerTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/filter/DebouncerTest.cpp
index 2f64908..d232739 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/filter/DebouncerTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/filter/DebouncerTest.cpp
@@ -2,10 +2,10 @@
 // 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 <gtest/gtest.h>
 #include <wpi/timestamp.h>
 
 #include "frc/filter/Debouncer.h"
-#include "gtest/gtest.h"
 #include "units/time.h"
 
 static units::second_t now = 0_s;
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/filter/LinearFilterNoiseTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/filter/LinearFilterNoiseTest.cpp
index 8299a71..e0dea2d 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/filter/LinearFilterNoiseTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/filter/LinearFilterNoiseTest.cpp
@@ -8,7 +8,8 @@
 #include <numbers>
 #include <random>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "units/time.h"
 
 // Filter constants
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/filter/LinearFilterOutputTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/filter/LinearFilterOutputTest.cpp
index 152737d..9195525 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/filter/LinearFilterOutputTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/filter/LinearFilterOutputTest.cpp
@@ -10,9 +10,9 @@
 #include <numbers>
 #include <random>
 
+#include <gtest/gtest.h>
 #include <wpi/array.h>
 
-#include "gtest/gtest.h"
 #include "units/time.h"
 
 // Filter constants
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/filter/MedianFilterTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/filter/MedianFilterTest.cpp
index 8151a45..f0f7d99 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/filter/MedianFilterTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/filter/MedianFilterTest.cpp
@@ -2,8 +2,9 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/filter/MedianFilter.h"
-#include "gtest/gtest.h"
 
 TEST(MedianFilterTest, MedianFilterNotFullTestEven) {
   frc::MedianFilter<double> filter{10};
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/filter/SlewRateLimiterTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/filter/SlewRateLimiterTest.cpp
index 5dbe8c8..9657543 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/filter/SlewRateLimiterTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/filter/SlewRateLimiterTest.cpp
@@ -2,10 +2,10 @@
 // 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 <gtest/gtest.h>
 #include <wpi/timestamp.h>
 
 #include "frc/filter/SlewRateLimiter.h"
-#include "gtest/gtest.h"
 #include "units/length.h"
 #include "units/time.h"
 #include "units/velocity.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/CoordinateSystemTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/CoordinateSystemTest.cpp
index fc44fa5..198f90f 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/CoordinateSystemTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/CoordinateSystemTest.cpp
@@ -2,10 +2,11 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/geometry/CoordinateSystem.h"
 #include "frc/geometry/Pose3d.h"
 #include "frc/geometry/Transform3d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
@@ -37,9 +38,6 @@
   EXPECT_EQ(transformTo.Translation(),
             CoordinateSystem::Convert(transformFrom.Translation(), coordFrom,
                                       coordTo));
-  EXPECT_EQ(
-      transformTo.Rotation(),
-      CoordinateSystem::Convert(transformFrom.Rotation(), coordFrom, coordTo));
   EXPECT_EQ(transformTo,
             CoordinateSystem::Convert(transformFrom, coordFrom, coordTo));
 
@@ -47,9 +45,6 @@
   EXPECT_EQ(
       transformFrom.Translation(),
       CoordinateSystem::Convert(transformTo.Translation(), coordTo, coordFrom));
-  EXPECT_EQ(
-      transformFrom.Rotation(),
-      CoordinateSystem::Convert(transformTo.Rotation(), coordTo, coordFrom));
   EXPECT_EQ(transformFrom,
             CoordinateSystem::Convert(transformTo, coordTo, coordFrom));
 }
@@ -106,29 +101,28 @@
   // No rotation from EDN to NWU
   CheckTransform3dConvert(
       Transform3d{Translation3d{1_m, 2_m, 3_m}, Rotation3d{}},
-      Transform3d{Translation3d{3_m, -1_m, -2_m},
-                  Rotation3d{-90_deg, 0_deg, -90_deg}},
+      Transform3d{Translation3d{3_m, -1_m, -2_m}, Rotation3d{}},
       CoordinateSystem::EDN(), CoordinateSystem::NWU());
 
   // 45° roll from EDN to NWU
   CheckTransform3dConvert(Transform3d{Translation3d{1_m, 2_m, 3_m},
                                       Rotation3d{45_deg, 0_deg, 0_deg}},
                           Transform3d{Translation3d{3_m, -1_m, -2_m},
-                                      Rotation3d{-45_deg, 0_deg, -90_deg}},
+                                      Rotation3d{0_deg, -45_deg, 0_deg}},
                           CoordinateSystem::EDN(), CoordinateSystem::NWU());
 
   // 45° pitch from EDN to NWU
   CheckTransform3dConvert(Transform3d{Translation3d{1_m, 2_m, 3_m},
                                       Rotation3d{0_deg, 45_deg, 0_deg}},
                           Transform3d{Translation3d{3_m, -1_m, -2_m},
-                                      Rotation3d{-90_deg, 0_deg, -135_deg}},
+                                      Rotation3d{0_deg, 0_deg, -45_deg}},
                           CoordinateSystem::EDN(), CoordinateSystem::NWU());
 
   // 45° yaw from EDN to NWU
   CheckTransform3dConvert(Transform3d{Translation3d{1_m, 2_m, 3_m},
                                       Rotation3d{0_deg, 0_deg, 45_deg}},
                           Transform3d{Translation3d{3_m, -1_m, -2_m},
-                                      Rotation3d{-90_deg, 45_deg, -90_deg}},
+                                      Rotation3d{45_deg, 0_deg, 0_deg}},
                           CoordinateSystem::EDN(), CoordinateSystem::NWU());
 }
 
@@ -136,28 +130,27 @@
   // No rotation from EDN to NED
   CheckTransform3dConvert(
       Transform3d{Translation3d{1_m, 2_m, 3_m}, Rotation3d{}},
-      Transform3d{Translation3d{3_m, 1_m, 2_m},
-                  Rotation3d{90_deg, 0_deg, 90_deg}},
+      Transform3d{Translation3d{3_m, 1_m, 2_m}, Rotation3d{}},
       CoordinateSystem::EDN(), CoordinateSystem::NED());
 
   // 45° roll from EDN to NED
   CheckTransform3dConvert(Transform3d{Translation3d{1_m, 2_m, 3_m},
                                       Rotation3d{45_deg, 0_deg, 0_deg}},
                           Transform3d{Translation3d{3_m, 1_m, 2_m},
-                                      Rotation3d{135_deg, 0_deg, 90_deg}},
+                                      Rotation3d{0_deg, 45_deg, 0_deg}},
                           CoordinateSystem::EDN(), CoordinateSystem::NED());
 
   // 45° pitch from EDN to NED
   CheckTransform3dConvert(Transform3d{Translation3d{1_m, 2_m, 3_m},
                                       Rotation3d{0_deg, 45_deg, 0_deg}},
                           Transform3d{Translation3d{3_m, 1_m, 2_m},
-                                      Rotation3d{90_deg, 0_deg, 135_deg}},
+                                      Rotation3d{0_deg, 0_deg, 45_deg}},
                           CoordinateSystem::EDN(), CoordinateSystem::NED());
 
   // 45° yaw from EDN to NED
   CheckTransform3dConvert(Transform3d{Translation3d{1_m, 2_m, 3_m},
                                       Rotation3d{0_deg, 0_deg, 45_deg}},
                           Transform3d{Translation3d{3_m, 1_m, 2_m},
-                                      Rotation3d{90_deg, -45_deg, 90_deg}},
+                                      Rotation3d{45_deg, 0_deg, 0_deg}},
                           CoordinateSystem::EDN(), CoordinateSystem::NED());
 }
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Pose2dTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Pose2dTest.cpp
index 5ce6819..bd5a9aa 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Pose2dTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Pose2dTest.cpp
@@ -3,12 +3,32 @@
 // the WPILib BSD license file in the root directory of this project.
 
 #include <cmath>
+#include <cstdlib>
+
+#include <gtest/gtest.h>
 
 #include "frc/geometry/Pose2d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
+TEST(Pose2dTest, RotateBy) {
+  constexpr auto x = 1_m;
+  constexpr auto y = 2_m;
+  const Pose2d initial{x, y, 45_deg};
+
+  const Rotation2d rotation{5_deg};
+  const auto rotated = initial.RotateBy(rotation);
+
+  // Translation is rotated by CCW rotation matrix
+  double c = rotation.Cos();
+  double s = rotation.Sin();
+  EXPECT_DOUBLE_EQ(c * x.value() - s * y.value(), rotated.X().value());
+  EXPECT_DOUBLE_EQ(s * x.value() + c * y.value(), rotated.Y().value());
+  EXPECT_DOUBLE_EQ(
+      initial.Rotation().Degrees().value() + rotation.Degrees().value(),
+      rotated.Rotation().Degrees().value());
+}
+
 TEST(Pose2dTest, TransformBy) {
   const Pose2d initial{1_m, 2_m, 45_deg};
   const Transform2d transform{Translation2d{5_m, 0_m}, 5_deg};
@@ -26,9 +46,9 @@
 
   const auto finalRelativeToInitial = final.RelativeTo(initial);
 
-  EXPECT_DOUBLE_EQ(5.0 * std::sqrt(2.0), finalRelativeToInitial.X().value());
+  EXPECT_NEAR(5.0 * std::sqrt(2.0), finalRelativeToInitial.X().value(), 1e-9);
   EXPECT_NEAR(0.0, finalRelativeToInitial.Y().value(), 1e-9);
-  EXPECT_DOUBLE_EQ(0.0, finalRelativeToInitial.Rotation().Degrees().value());
+  EXPECT_NEAR(0.0, finalRelativeToInitial.Rotation().Degrees().value(), 1e-9);
 }
 
 TEST(Pose2dTest, Equality) {
@@ -49,9 +69,76 @@
 
   const auto transform = final - initial;
 
-  EXPECT_DOUBLE_EQ(5.0 * std::sqrt(2.0), transform.X().value());
+  EXPECT_NEAR(5.0 * std::sqrt(2.0), transform.X().value(), 1e-9);
   EXPECT_NEAR(0.0, transform.Y().value(), 1e-9);
-  EXPECT_DOUBLE_EQ(0.0, transform.Rotation().Degrees().value());
+  EXPECT_NEAR(0.0, transform.Rotation().Degrees().value(), 1e-9);
+}
+
+TEST(Pose2dTest, Nearest) {
+  const Pose2d origin{0_m, 0_m, 0_deg};
+
+  const Pose2d pose1{Translation2d{1_m, Rotation2d{45_deg}}, 0_deg};
+  const Pose2d pose2{Translation2d{2_m, Rotation2d{90_deg}}, 0_deg};
+  const Pose2d pose3{Translation2d{3_m, Rotation2d{135_deg}}, 0_deg};
+  const Pose2d pose4{Translation2d{4_m, Rotation2d{180_deg}}, 0_deg};
+  const Pose2d pose5{Translation2d{5_m, Rotation2d{270_deg}}, 0_deg};
+
+  EXPECT_DOUBLE_EQ(pose3.X().value(),
+                   origin.Nearest({pose5, pose3, pose4}).X().value());
+  EXPECT_DOUBLE_EQ(pose3.Y().value(),
+                   origin.Nearest({pose5, pose3, pose4}).Y().value());
+
+  EXPECT_DOUBLE_EQ(pose1.X().value(),
+                   origin.Nearest({pose1, pose2, pose3}).X().value());
+  EXPECT_DOUBLE_EQ(pose1.Y().value(),
+                   origin.Nearest({pose1, pose2, pose3}).Y().value());
+
+  EXPECT_DOUBLE_EQ(pose2.X().value(),
+                   origin.Nearest({pose4, pose2, pose3}).X().value());
+  EXPECT_DOUBLE_EQ(pose2.Y().value(),
+                   origin.Nearest({pose4, pose2, pose3}).Y().value());
+
+  // Rotation component sort (when distance is the same)
+  // Use the same translation because using different angles at the same
+  // distance can cause rounding error.
+  const Translation2d translation{1_m, Rotation2d{0_deg}};
+
+  const Pose2d poseA{translation, 0_deg};
+  const Pose2d poseB{translation, Rotation2d{30_deg}};
+  const Pose2d poseC{translation, Rotation2d{120_deg}};
+  const Pose2d poseD{translation, Rotation2d{90_deg}};
+  const Pose2d poseE{translation, Rotation2d{-180_deg}};
+
+  EXPECT_DOUBLE_EQ(poseA.Rotation().Degrees().value(),
+                   Pose2d(0_m, 0_m, Rotation2d{360_deg})
+                       .Nearest({poseA, poseB, poseD})
+                       .Rotation()
+                       .Degrees()
+                       .value());
+  EXPECT_DOUBLE_EQ(poseB.Rotation().Degrees().value(),
+                   Pose2d(0_m, 0_m, Rotation2d{-335_deg})
+                       .Nearest({poseB, poseC, poseD})
+                       .Rotation()
+                       .Degrees()
+                       .value());
+  EXPECT_DOUBLE_EQ(poseC.Rotation().Degrees().value(),
+                   Pose2d(0_m, 0_m, Rotation2d{-120_deg})
+                       .Nearest({poseB, poseC, poseD})
+                       .Rotation()
+                       .Degrees()
+                       .value());
+  EXPECT_DOUBLE_EQ(poseD.Rotation().Degrees().value(),
+                   Pose2d(0_m, 0_m, Rotation2d{85_deg})
+                       .Nearest({poseA, poseC, poseD})
+                       .Rotation()
+                       .Degrees()
+                       .value());
+  EXPECT_DOUBLE_EQ(poseE.Rotation().Degrees().value(),
+                   Pose2d(0_m, 0_m, Rotation2d{170_deg})
+                       .Nearest({poseA, poseD, poseE})
+                       .Rotation()
+                       .Degrees()
+                       .value());
 }
 
 TEST(Pose2dTest, Constexpr) {
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Pose3dTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Pose3dTest.cpp
index 8c4452c..6d689db 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Pose3dTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Pose3dTest.cpp
@@ -4,11 +4,34 @@
 
 #include <cmath>
 
+#include <gtest/gtest.h>
+#include <wpi/array.h>
+
 #include "frc/geometry/Pose3d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
+TEST(Pose3dTest, RotateBy) {
+  constexpr auto x = 1_m;
+  constexpr auto y = 2_m;
+  const Pose3d initial{x, y, 0_m, Rotation3d{0_deg, 0_deg, 45_deg}};
+
+  constexpr units::radian_t yaw = 5_deg;
+  const Rotation3d rotation{0_deg, 0_deg, yaw};
+  const auto rotated = initial.RotateBy(rotation);
+
+  // Translation is rotated by CCW rotation matrix
+  double c = std::cos(yaw.value());
+  double s = std::sin(yaw.value());
+  EXPECT_DOUBLE_EQ(c * x.value() - s * y.value(), rotated.X().value());
+  EXPECT_DOUBLE_EQ(s * x.value() + c * y.value(), rotated.Y().value());
+  EXPECT_DOUBLE_EQ(0.0, rotated.Z().value());
+  EXPECT_DOUBLE_EQ(0.0, rotated.Rotation().X().value());
+  EXPECT_DOUBLE_EQ(0.0, rotated.Rotation().Y().value());
+  EXPECT_DOUBLE_EQ(initial.Rotation().Z().value() + rotation.Z().value(),
+                   rotated.Rotation().Z().value());
+}
+
 TEST(Pose3dTest, TestTransformByRotations) {
   const double kEpsilon = 1E-9;
 
@@ -95,3 +118,86 @@
 
   EXPECT_EQ(expected, pose.ToPose2d());
 }
+
+TEST(Pose3dTest, ComplexTwists) {
+  wpi::array<Pose3d, 5> initial_poses{
+      Pose3d{0.698303_m, -0.959096_m, 0.271076_m,
+             Rotation3d{Quaternion{0.86403, -0.076866, 0.147234, 0.475254}}},
+      Pose3d{0.634892_m, -0.765209_m, 0.117543_m,
+             Rotation3d{Quaternion{0.84987, -0.070829, 0.162097, 0.496415}}},
+      Pose3d{0.584827_m, -0.590303_m, -0.02557_m,
+             Rotation3d{Quaternion{0.832743, -0.041991, 0.202188, 0.513708}}},
+      Pose3d{0.505038_m, -0.451479_m, -0.112835_m,
+             Rotation3d{Quaternion{0.816515, -0.002673, 0.226182, 0.531166}}},
+      Pose3d{0.428178_m, -0.329692_m, -0.189707_m,
+             Rotation3d{Quaternion{0.807886, 0.029298, 0.257788, 0.529157}}},
+  };
+
+  wpi::array<Pose3d, 5> final_poses{
+      Pose3d{-0.230448_m, -0.511957_m, 0.198406_m,
+             Rotation3d{Quaternion{0.753984, 0.347016, 0.409105, 0.379106}}},
+      Pose3d{-0.088932_m, -0.343253_m, 0.095018_m,
+             Rotation3d{Quaternion{0.638738, 0.413016, 0.536281, 0.365833}}},
+      Pose3d{-0.107908_m, -0.317552_m, 0.133946_m,
+             Rotation3d{Quaternion{0.653444, 0.417069, 0.465505, 0.427046}}},
+      Pose3d{-0.123383_m, -0.156411_m, -0.047435_m,
+             Rotation3d{Quaternion{0.652983, 0.40644, 0.431566, 0.47135}}},
+      Pose3d{-0.084654_m, -0.019305_m, -0.030022_m,
+             Rotation3d{Quaternion{0.620243, 0.429104, 0.479384, 0.44873}}},
+  };
+
+  for (size_t i = 0; i < initial_poses.size(); i++) {
+    auto start = initial_poses[i];
+    auto end = final_poses[i];
+
+    auto twist = start.Log(end);
+    auto start_exp = start.Exp(twist);
+
+    auto eps = 1E-5;
+
+    EXPECT_NEAR(start_exp.X().value(), end.X().value(), eps);
+    EXPECT_NEAR(start_exp.Y().value(), end.Y().value(), eps);
+    EXPECT_NEAR(start_exp.Z().value(), end.Z().value(), eps);
+    EXPECT_NEAR(start_exp.Rotation().GetQuaternion().W(),
+                end.Rotation().GetQuaternion().W(), eps);
+    EXPECT_NEAR(start_exp.Rotation().GetQuaternion().X(),
+                end.Rotation().GetQuaternion().X(), eps);
+    EXPECT_NEAR(start_exp.Rotation().GetQuaternion().Y(),
+                end.Rotation().GetQuaternion().Y(), eps);
+    EXPECT_NEAR(start_exp.Rotation().GetQuaternion().Z(),
+                end.Rotation().GetQuaternion().Z(), eps);
+  }
+}
+
+TEST(Pose3dTest, TwistNaN) {
+  wpi::array<Pose3d, 2> initial_poses{
+      Pose3d{6.32_m, 4.12_m, 0.00_m,
+             Rotation3d{Quaternion{-0.9999999999999999, 0.0, 0.0,
+                                   1.9208309264993548E-8}}},
+      Pose3d{3.75_m, 2.95_m, 0.00_m,
+             Rotation3d{Quaternion{0.9999999999999793, 0.0, 0.0,
+                                   2.0352360299846772E-7}}},
+  };
+
+  wpi::array<Pose3d, 2> final_poses{
+      Pose3d{6.33_m, 4.15_m, 0.00_m,
+             Rotation3d{Quaternion{-0.9999999999999999, 0.0, 0.0,
+                                   2.416890209039172E-8}}},
+      Pose3d{3.66_m, 2.93_m, 0.00_m,
+             Rotation3d{Quaternion{0.9999999999999782, 0.0, 0.0,
+                                   2.0859477994905617E-7}}},
+  };
+
+  for (size_t i = 0; i < initial_poses.size(); i++) {
+    auto start = initial_poses[i];
+    auto end = final_poses[i];
+    auto twist = start.Log(end);
+
+    EXPECT_FALSE(std::isnan(twist.dx.value()));
+    EXPECT_FALSE(std::isnan(twist.dy.value()));
+    EXPECT_FALSE(std::isnan(twist.dz.value()));
+    EXPECT_FALSE(std::isnan(twist.rx.value()));
+    EXPECT_FALSE(std::isnan(twist.ry.value()));
+    EXPECT_FALSE(std::isnan(twist.rz.value()));
+  }
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/QuaternionTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/QuaternionTest.cpp
index 5b95abb..ef0e95e 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/QuaternionTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/QuaternionTest.cpp
@@ -4,8 +4,9 @@
 
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Quaternion.h"
-#include "gtest/gtest.h"
 #include "units/angle.h"
 #include "units/math.h"
 
@@ -43,6 +44,54 @@
                             q3.Z() * q3.Z());
 }
 
+TEST(QuaternionTest, Addition) {
+  Quaternion q{0.1, 0.2, 0.3, 0.4};
+  Quaternion p{0.5, 0.6, 0.7, 0.8};
+
+  auto sum = q + p;
+
+  EXPECT_DOUBLE_EQ(q.W() + p.W(), sum.W());
+  EXPECT_DOUBLE_EQ(q.X() + p.X(), sum.X());
+  EXPECT_DOUBLE_EQ(q.Y() + p.Y(), sum.Y());
+  EXPECT_DOUBLE_EQ(q.Z() + p.Z(), sum.Z());
+}
+
+TEST(QuaternionTest, Subtraction) {
+  Quaternion q{0.1, 0.2, 0.3, 0.4};
+  Quaternion p{0.5, 0.6, 0.7, 0.8};
+
+  auto difference = q - p;
+
+  EXPECT_DOUBLE_EQ(q.W() - p.W(), difference.W());
+  EXPECT_DOUBLE_EQ(q.X() - p.X(), difference.X());
+  EXPECT_DOUBLE_EQ(q.Y() - p.Y(), difference.Y());
+  EXPECT_DOUBLE_EQ(q.Z() - p.Z(), difference.Z());
+}
+
+TEST(QuaternionTest, ScalarMultiplication) {
+  Quaternion q{0.1, 0.2, 0.3, 0.4};
+  auto scalar = 2;
+
+  auto product = q * scalar;
+
+  EXPECT_DOUBLE_EQ(q.W() * scalar, product.W());
+  EXPECT_DOUBLE_EQ(q.X() * scalar, product.X());
+  EXPECT_DOUBLE_EQ(q.Y() * scalar, product.Y());
+  EXPECT_DOUBLE_EQ(q.Z() * scalar, product.Z());
+}
+
+TEST(QuaternionTest, ScalarDivision) {
+  Quaternion q{0.1, 0.2, 0.3, 0.4};
+  auto scalar = 2;
+
+  auto product = q / scalar;
+
+  EXPECT_DOUBLE_EQ(q.W() / scalar, product.W());
+  EXPECT_DOUBLE_EQ(q.X() / scalar, product.X());
+  EXPECT_DOUBLE_EQ(q.Y() / scalar, product.Y());
+  EXPECT_DOUBLE_EQ(q.Z() / scalar, product.Z());
+}
+
 TEST(QuaternionTest, Multiply) {
   // 90° CCW rotations around each axis
   double c = units::math::cos(90_deg / 2.0);
@@ -64,19 +113,110 @@
   Quaternion q{0.72760687510899891, 0.29104275004359953, 0.38805700005813276,
                0.48507125007266594};
   actual = q * q.Inverse();
-  EXPECT_DOUBLE_EQ(1.0, actual.W());
-  EXPECT_DOUBLE_EQ(0.0, actual.X());
-  EXPECT_DOUBLE_EQ(0.0, actual.Y());
-  EXPECT_DOUBLE_EQ(0.0, actual.Z());
+  EXPECT_NEAR(1.0, actual.W(), 1e-9);
+  EXPECT_NEAR(0.0, actual.X(), 1e-9);
+  EXPECT_NEAR(0.0, actual.Y(), 1e-9);
+  EXPECT_NEAR(0.0, actual.Z(), 1e-9);
+}
+
+TEST(QuaternionTest, Conjugate) {
+  Quaternion q{0.72760687510899891, 0.29104275004359953, 0.38805700005813276,
+               0.48507125007266594};
+  auto conj = q.Conjugate();
+
+  EXPECT_DOUBLE_EQ(q.W(), conj.W());
+  EXPECT_DOUBLE_EQ(-q.X(), conj.X());
+  EXPECT_DOUBLE_EQ(-q.Y(), conj.Y());
+  EXPECT_DOUBLE_EQ(-q.Z(), conj.Z());
 }
 
 TEST(QuaternionTest, Inverse) {
   Quaternion q{0.72760687510899891, 0.29104275004359953, 0.38805700005813276,
                0.48507125007266594};
+  auto norm = q.Norm();
+
   auto inv = q.Inverse();
 
-  EXPECT_DOUBLE_EQ(q.W(), inv.W());
-  EXPECT_DOUBLE_EQ(-q.X(), inv.X());
-  EXPECT_DOUBLE_EQ(-q.Y(), inv.Y());
-  EXPECT_DOUBLE_EQ(-q.Z(), inv.Z());
+  EXPECT_DOUBLE_EQ(q.W() / (norm * norm), inv.W());
+  EXPECT_DOUBLE_EQ(-q.X() / (norm * norm), inv.X());
+  EXPECT_DOUBLE_EQ(-q.Y() / (norm * norm), inv.Y());
+  EXPECT_DOUBLE_EQ(-q.Z() / (norm * norm), inv.Z());
+}
+
+TEST(QuaternionTest, Norm) {
+  Quaternion q{3, 4, 12, 84};
+  auto norm = q.Norm();
+
+  EXPECT_NEAR(85, norm, 1e-9);
+}
+
+TEST(QuaternionTest, Exponential) {
+  Quaternion q{1.1, 2.2, 3.3, 4.4};
+  Quaternion expect{2.81211398529184, -0.392521193481878, -0.588781790222817,
+                    -0.785042386963756};
+
+  auto q_exp = q.Exp();
+
+  EXPECT_EQ(expect, q_exp);
+}
+
+TEST(QuaternionTest, Logarithm) {
+  Quaternion q{1.1, 2.2, 3.3, 4.4};
+  Quaternion expect{1.7959088706354, 0.515190292664085, 0.772785438996128,
+                    1.03038058532817};
+
+  auto q_log = q.Log();
+
+  EXPECT_EQ(expect, q_log);
+
+  Quaternion zero{0, 0, 0, 0};
+  Quaternion one{1, 0, 0, 0};
+  Quaternion i{0, 1, 0, 0};
+  Quaternion j{0, 0, 1, 0};
+  Quaternion k{0, 0, 0, 1};
+  Quaternion ln_half{std::log(0.5), -std::numbers::pi, 0, 0};
+
+  EXPECT_EQ(zero, one.Log());
+  EXPECT_EQ(i * std::numbers::pi / 2, i.Log());
+  EXPECT_EQ(j * std::numbers::pi / 2, j.Log());
+  EXPECT_EQ(k * std::numbers::pi / 2, k.Log());
+
+  EXPECT_EQ(i * -std::numbers::pi, (one * -1).Log());
+  EXPECT_EQ(ln_half, (one * -0.5).Log());
+}
+
+TEST(QuaternionTest, LogarithmAndExponentialInverse) {
+  Quaternion q{1.1, 2.2, 3.3, 4.4};
+
+  // These operations are order-dependent: ln(exp(q)) is congruent but not
+  // necessarily equal to exp(ln(q)) due to the multi-valued nature of the
+  // complex logarithm.
+
+  auto q_log_exp = q.Log().Exp();
+
+  EXPECT_EQ(q, q_log_exp);
+
+  Quaternion start{1, 2, 3, 4};
+  Quaternion expect{5, 6, 7, 8};
+
+  auto twist = start.Log(expect);
+  auto actual = start.Exp(twist);
+
+  EXPECT_EQ(expect, actual);
+}
+
+TEST(QuaternionTest, DotProduct) {
+  Quaternion q{1.1, 2.2, 3.3, 4.4};
+  Quaternion p{5.5, 6.6, 7.7, 8.8};
+
+  EXPECT_NEAR(q.W() * p.W() + q.X() * p.X() + q.Y() * p.Y() + q.Z() * p.Z(),
+              q.Dot(p), 1e-9);
+}
+
+TEST(QuaternionTest, DotProductAsEquality) {
+  Quaternion q{1.1, 2.2, 3.3, 4.4};
+  auto q_conj = q.Conjugate();
+
+  EXPECT_NEAR(q.Dot(q), q.Norm() * q.Norm(), 1e-9);
+  EXPECT_GT(std::abs(q.Dot(q_conj) - q.Norm() * q_conj.Norm()), 1e-9);
 }
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp
index 4e2e683..de2fc17 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp
@@ -5,8 +5,9 @@
 #include <cmath>
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Rotation2d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Rotation3dTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Rotation3dTest.cpp
index 4709ed0..01b37e5 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Rotation3dTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Rotation3dTest.cpp
@@ -5,52 +5,93 @@
 #include <cmath>
 #include <numbers>
 
+#include <Eigen/Core>
+#include <gtest/gtest.h>
 #include <wpi/MathExtras.h>
 
-#include "frc/EigenCore.h"
 #include "frc/geometry/Rotation3d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
+TEST(Rotation3dTest, GimbalLockAccuracy) {
+  auto rot1 = Rotation3d{0_rad, 0_rad, units::radian_t{std::numbers::pi / 2}};
+  auto rot2 = Rotation3d{units::radian_t{std::numbers::pi}, 0_rad, 0_rad};
+  auto rot3 = Rotation3d{-units::radian_t{std::numbers::pi / 2}, 0_rad, 0_rad};
+  const auto result1 = rot1 + rot2 + rot3;
+  const auto expected1 =
+      Rotation3d{0_rad, -units::radian_t{std::numbers::pi / 2},
+                 units::radian_t{std::numbers::pi / 2}};
+  EXPECT_EQ(expected1, result1);
+  EXPECT_DOUBLE_EQ(std::numbers::pi / 2, (result1.X() + result1.Z()).value());
+  EXPECT_DOUBLE_EQ(-std::numbers::pi / 2, result1.Y().value());
+
+  rot1 = Rotation3d{0_rad, 0_rad, units::radian_t{std::numbers::pi / 2}};
+  rot2 = Rotation3d{units::radian_t{-std::numbers::pi}, 0_rad, 0_rad};
+  rot3 = Rotation3d{units::radian_t{std::numbers::pi / 2}, 0_rad, 0_rad};
+  const auto result2 = rot1 + rot2 + rot3;
+  const auto expected2 =
+      Rotation3d{0_rad, units::radian_t{std::numbers::pi / 2},
+                 units::radian_t{std::numbers::pi / 2}};
+  EXPECT_EQ(expected2, result2);
+  EXPECT_DOUBLE_EQ(std::numbers::pi / 2, (result2.Z() - result2.X()).value());
+  EXPECT_DOUBLE_EQ(std::numbers::pi / 2, result2.Y().value());
+
+  rot1 = Rotation3d{0_rad, 0_rad, units::radian_t{std::numbers::pi / 2}};
+  rot2 = Rotation3d{0_rad, units::radian_t{std::numbers::pi / 3}, 0_rad};
+  rot3 = Rotation3d{-units::radian_t{std::numbers::pi / 2}, 0_rad, 0_rad};
+  const auto result3 = rot1 + rot2 + rot3;
+  const auto expected3 =
+      Rotation3d{0_rad, units::radian_t{std::numbers::pi / 2},
+                 units::radian_t{std::numbers::pi / 6}};
+  EXPECT_EQ(expected3, result3);
+  EXPECT_DOUBLE_EQ(std::numbers::pi / 6, (result3.Z() - result3.X()).value());
+  EXPECT_DOUBLE_EQ(std::numbers::pi / 2, result3.Y().value());
+}
+
 TEST(Rotation3dTest, InitAxisAngleAndRollPitchYaw) {
   const Eigen::Vector3d xAxis{1.0, 0.0, 0.0};
   const Rotation3d rot1{xAxis, units::radian_t{std::numbers::pi / 3}};
   const Rotation3d rot2{units::radian_t{std::numbers::pi / 3}, 0_rad, 0_rad};
+  const Rotation3d rvec1{Eigen::Vector3d{xAxis * std::numbers::pi / 3}};
   EXPECT_EQ(rot1, rot2);
+  EXPECT_EQ(rot1, rvec1);
 
   const Eigen::Vector3d yAxis{0.0, 1.0, 0.0};
   const Rotation3d rot3{yAxis, units::radian_t{std::numbers::pi / 3}};
   const Rotation3d rot4{0_rad, units::radian_t{std::numbers::pi / 3}, 0_rad};
+  const Rotation3d rvec2{Eigen::Vector3d{yAxis * std::numbers::pi / 3}};
   EXPECT_EQ(rot3, rot4);
+  EXPECT_EQ(rot3, rvec2);
 
   const Eigen::Vector3d zAxis{0.0, 0.0, 1.0};
   const Rotation3d rot5{zAxis, units::radian_t{std::numbers::pi / 3}};
   const Rotation3d rot6{0_rad, 0_rad, units::radian_t{std::numbers::pi / 3}};
+  const Rotation3d rvec3{Eigen::Vector3d{zAxis * std::numbers::pi / 3}};
   EXPECT_EQ(rot5, rot6);
+  EXPECT_EQ(rot5, rvec3);
 }
 
 TEST(Rotation3dTest, InitRotationMatrix) {
   // No rotation
-  const Matrixd<3, 3> R1 = Matrixd<3, 3>::Identity();
+  const Eigen::Matrix3d R1 = Eigen::Matrix3d::Identity();
   const Rotation3d rot1{R1};
   EXPECT_EQ(Rotation3d{}, rot1);
 
   // 90 degree CCW rotation around z-axis
-  Matrixd<3, 3> R2;
-  R2.block<3, 1>(0, 0) = Vectord<3>{0.0, 1.0, 0.0};
-  R2.block<3, 1>(0, 1) = Vectord<3>{-1.0, 0.0, 0.0};
-  R2.block<3, 1>(0, 2) = Vectord<3>{0.0, 0.0, 1.0};
+  Eigen::Matrix3d R2;
+  R2.block<3, 1>(0, 0) = Eigen::Vector3d{0.0, 1.0, 0.0};
+  R2.block<3, 1>(0, 1) = Eigen::Vector3d{-1.0, 0.0, 0.0};
+  R2.block<3, 1>(0, 2) = Eigen::Vector3d{0.0, 0.0, 1.0};
   const Rotation3d rot2{R2};
   const Rotation3d expected2{0_deg, 0_deg, 90_deg};
   EXPECT_EQ(expected2, rot2);
 
   // Matrix that isn't orthogonal
-  const Matrixd<3, 3> R3{{1.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 0.0, 0.0}};
+  const Eigen::Matrix3d R3{{1.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 0.0, 0.0}};
   EXPECT_THROW(Rotation3d{R3}, std::domain_error);
 
   // Matrix that's orthogonal but not special orthogonal
-  const Matrixd<3, 3> R4 = Matrixd<3, 3>::Identity() * 2.0;
+  const Eigen::Matrix3d R4 = Eigen::Matrix3d::Identity() * 2.0;
   EXPECT_THROW(Rotation3d{R4}, std::domain_error);
 }
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Transform2dTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Transform2dTest.cpp
index 1b0934d..49c9466 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Transform2dTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Transform2dTest.cpp
@@ -4,11 +4,12 @@
 
 #include <cmath>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Pose2d.h"
 #include "frc/geometry/Rotation2d.h"
 #include "frc/geometry/Transform2d.h"
 #include "frc/geometry/Translation2d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Transform3dTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Transform3dTest.cpp
index 904ec9c..8a6bbc0 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Transform3dTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Transform3dTest.cpp
@@ -4,11 +4,12 @@
 
 #include <cmath>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Pose3d.h"
 #include "frc/geometry/Rotation3d.h"
 #include "frc/geometry/Transform3d.h"
 #include "frc/geometry/Translation3d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Translation2dTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Translation2dTest.cpp
index 5493c43..1c92c0a 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Translation2dTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Translation2dTest.cpp
@@ -4,8 +4,9 @@
 
 #include <cmath>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Translation2d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
@@ -94,6 +95,37 @@
   EXPECT_DOUBLE_EQ(std::sqrt(3.0), two.Y().value());
 }
 
+TEST(Translation2dTest, Nearest) {
+  const Translation2d origin{0_m, 0_m};
+
+  const Translation2d translation1{1_m, Rotation2d{45_deg}};
+  const Translation2d translation2{2_m, Rotation2d{90_deg}};
+  const Translation2d translation3{3_m, Rotation2d{135_deg}};
+  const Translation2d translation4{4_m, Rotation2d{180_deg}};
+  const Translation2d translation5{5_m, Rotation2d{270_deg}};
+
+  EXPECT_DOUBLE_EQ(
+      origin.Nearest({translation5, translation3, translation4}).X().value(),
+      translation3.X().value());
+  EXPECT_DOUBLE_EQ(
+      origin.Nearest({translation5, translation3, translation4}).Y().value(),
+      translation3.Y().value());
+
+  EXPECT_DOUBLE_EQ(
+      origin.Nearest({translation1, translation2, translation3}).X().value(),
+      translation1.X().value());
+  EXPECT_DOUBLE_EQ(
+      origin.Nearest({translation1, translation2, translation3}).Y().value(),
+      translation1.Y().value());
+
+  EXPECT_DOUBLE_EQ(
+      origin.Nearest({translation4, translation2, translation3}).X().value(),
+      translation2.X().value());
+  EXPECT_DOUBLE_EQ(
+      origin.Nearest({translation4, translation2, translation3}).Y().value(),
+      translation2.Y().value());
+}
+
 TEST(Translation2dTest, Constexpr) {
   constexpr Translation2d defaultCtor;
   constexpr Translation2d componentCtor{1_m, 2_m};
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Translation3dTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Translation3dTest.cpp
index 58c611e..b7aa8ce 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Translation3dTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Translation3dTest.cpp
@@ -4,8 +4,9 @@
 
 #include <cmath>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Translation3d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Twist2dTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Twist2dTest.cpp
index 33970cd..7f121c0 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Twist2dTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Twist2dTest.cpp
@@ -5,8 +5,9 @@
 #include <cmath>
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Pose2d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Twist3dTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Twist3dTest.cpp
index 0d8a8f4..245ec17 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Twist3dTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/geometry/Twist3dTest.cpp
@@ -5,8 +5,9 @@
 #include <cmath>
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Pose3d.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/interpolation/TimeInterpolatableBufferTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/interpolation/TimeInterpolatableBufferTest.cpp
index 8a920f6..e448db2 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/interpolation/TimeInterpolatableBufferTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/interpolation/TimeInterpolatableBufferTest.cpp
@@ -4,10 +4,11 @@
 
 #include <cmath>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Pose2d.h"
 #include "frc/geometry/Rotation2d.h"
 #include "frc/interpolation/TimeInterpolatableBuffer.h"
-#include "gtest/gtest.h"
 #include "units/time.h"
 
 TEST(TimeInterpolatableBufferTest, Interpolation) {
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/ChassisSpeedsTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/ChassisSpeedsTest.cpp
index 4e0b3e5..97de03b 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/ChassisSpeedsTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/ChassisSpeedsTest.cpp
@@ -2,12 +2,34 @@
 // 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 <cmath>
+
+#include <gtest/gtest.h>
+
 #include "frc/kinematics/ChassisSpeeds.h"
-#include "gtest/gtest.h"
 
 static constexpr double kEpsilon = 1E-9;
 
-TEST(ChassisSpeedsTest, FieldRelativeConstruction) {
+TEST(ChassisSpeedsTest, Discretize) {
+  constexpr frc::ChassisSpeeds target{1_mps, 0_mps, 0.5_rad_per_s};
+  constexpr units::second_t duration = 1_s;
+  constexpr units::second_t dt = 10_ms;
+
+  const auto speeds = frc::ChassisSpeeds::Discretize(target, duration);
+  const frc::Twist2d twist{speeds.vx * dt, speeds.vy * dt, speeds.omega * dt};
+
+  frc::Pose2d pose;
+  for (units::second_t time = 0_s; time < duration; time += dt) {
+    pose = pose.Exp(twist);
+  }
+
+  EXPECT_NEAR((target.vx * duration).value(), pose.X().value(), kEpsilon);
+  EXPECT_NEAR((target.vy * duration).value(), pose.Y().value(), kEpsilon);
+  EXPECT_NEAR((target.omega * duration).value(),
+              pose.Rotation().Radians().value(), kEpsilon);
+}
+
+TEST(ChassisSpeedsTest, FromFieldRelativeSpeeds) {
   const auto chassisSpeeds = frc::ChassisSpeeds::FromFieldRelativeSpeeds(
       1.0_mps, 0.0_mps, 0.5_rad_per_s, -90.0_deg);
 
@@ -15,3 +37,64 @@
   EXPECT_NEAR(1.0, chassisSpeeds.vy.value(), kEpsilon);
   EXPECT_NEAR(0.5, chassisSpeeds.omega.value(), kEpsilon);
 }
+
+TEST(ChassisSpeedsTest, FromRobotRelativeSpeeds) {
+  const auto chassisSpeeds = frc::ChassisSpeeds::FromRobotRelativeSpeeds(
+      1.0_mps, 0.0_mps, 0.5_rad_per_s, 45.0_deg);
+
+  EXPECT_NEAR(1.0 / std::sqrt(2.0), chassisSpeeds.vx.value(), kEpsilon);
+  EXPECT_NEAR(1.0 / std::sqrt(2.0), chassisSpeeds.vy.value(), kEpsilon);
+  EXPECT_NEAR(0.5, chassisSpeeds.omega.value(), kEpsilon);
+}
+
+TEST(ChassisSpeedsTest, Plus) {
+  const frc::ChassisSpeeds left{1.0_mps, 0.5_mps, 0.75_rad_per_s};
+  const frc::ChassisSpeeds right{2.0_mps, 1.5_mps, 0.25_rad_per_s};
+
+  const frc::ChassisSpeeds result = left + right;
+
+  EXPECT_NEAR(3.0, result.vx.value(), kEpsilon);
+  EXPECT_NEAR(2.0, result.vy.value(), kEpsilon);
+  EXPECT_NEAR(1.0, result.omega.value(), kEpsilon);
+}
+
+TEST(ChassisSpeedsTest, Minus) {
+  const frc::ChassisSpeeds left{1.0_mps, 0.5_mps, 0.75_rad_per_s};
+  const frc::ChassisSpeeds right{2.0_mps, 0.5_mps, 0.25_rad_per_s};
+
+  const frc::ChassisSpeeds result = left - right;
+
+  EXPECT_NEAR(-1.0, result.vx.value(), kEpsilon);
+  EXPECT_NEAR(0, result.vy.value(), kEpsilon);
+  EXPECT_NEAR(0.5, result.omega.value(), kEpsilon);
+}
+
+TEST(ChassisSpeedsTest, UnaryMinus) {
+  const frc::ChassisSpeeds speeds{1.0_mps, 0.5_mps, 0.75_rad_per_s};
+
+  const frc::ChassisSpeeds result = -speeds;
+
+  EXPECT_NEAR(-1.0, result.vx.value(), kEpsilon);
+  EXPECT_NEAR(-0.5, result.vy.value(), kEpsilon);
+  EXPECT_NEAR(-0.75, result.omega.value(), kEpsilon);
+}
+
+TEST(ChassisSpeedsTest, Multiplication) {
+  const frc::ChassisSpeeds speeds{1.0_mps, 0.5_mps, 0.75_rad_per_s};
+
+  const frc::ChassisSpeeds result = speeds * 2;
+
+  EXPECT_NEAR(2.0, result.vx.value(), kEpsilon);
+  EXPECT_NEAR(1.0, result.vy.value(), kEpsilon);
+  EXPECT_NEAR(1.5, result.omega.value(), kEpsilon);
+}
+
+TEST(ChassisSpeedsTest, Division) {
+  const frc::ChassisSpeeds speeds{1.0_mps, 0.5_mps, 0.75_rad_per_s};
+
+  const frc::ChassisSpeeds result = speeds / 2;
+
+  EXPECT_NEAR(0.5, result.vx.value(), kEpsilon);
+  EXPECT_NEAR(0.25, result.vy.value(), kEpsilon);
+  EXPECT_NEAR(0.375, result.omega.value(), kEpsilon);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveKinematicsTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveKinematicsTest.cpp
index 4af5ac8..782fed0 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveKinematicsTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveKinematicsTest.cpp
@@ -4,9 +4,10 @@
 
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/kinematics/ChassisSpeeds.h"
 #include "frc/kinematics/DifferentialDriveKinematics.h"
-#include "gtest/gtest.h"
 #include "units/angular_velocity.h"
 #include "units/length.h"
 #include "units/velocity.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveOdometryTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveOdometryTest.cpp
index e480941..a228357 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveOdometryTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveOdometryTest.cpp
@@ -4,9 +4,10 @@
 
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/kinematics/DifferentialDriveKinematics.h"
 #include "frc/kinematics/DifferentialDriveOdometry.h"
-#include "gtest/gtest.h"
 
 static constexpr double kEpsilon = 1E-9;
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveWheelSpeedsTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveWheelSpeedsTest.cpp
new file mode 100644
index 0000000..92cc583
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/DifferentialDriveWheelSpeedsTest.cpp
@@ -0,0 +1,54 @@
+// 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 <gtest/gtest.h>
+
+#include "frc/kinematics/DifferentialDriveWheelSpeeds.h"
+
+TEST(DifferentialDriveWheelSpeedsTest, Plus) {
+  const frc::DifferentialDriveWheelSpeeds left{1.0_mps, 0.5_mps};
+  const frc::DifferentialDriveWheelSpeeds right{2.0_mps, 1.5_mps};
+
+  const frc::DifferentialDriveWheelSpeeds result = left + right;
+
+  EXPECT_EQ(3.0, result.left.value());
+  EXPECT_EQ(2.0, result.right.value());
+}
+
+TEST(DifferentialDriveWheelSpeedsTest, Minus) {
+  const frc::DifferentialDriveWheelSpeeds left{1.0_mps, 0.5_mps};
+  const frc::DifferentialDriveWheelSpeeds right{2.0_mps, 0.5_mps};
+
+  const frc::DifferentialDriveWheelSpeeds result = left - right;
+
+  EXPECT_EQ(-1.0, result.left.value());
+  EXPECT_EQ(0, result.right.value());
+}
+
+TEST(DifferentialDriveWheelSpeedsTest, UnaryMinus) {
+  const frc::DifferentialDriveWheelSpeeds speeds{1.0_mps, 0.5_mps};
+
+  const frc::DifferentialDriveWheelSpeeds result = -speeds;
+
+  EXPECT_EQ(-1.0, result.left.value());
+  EXPECT_EQ(-0.5, result.right.value());
+}
+
+TEST(DifferentialDriveWheelSpeedsTest, Multiplication) {
+  const frc::DifferentialDriveWheelSpeeds speeds{1.0_mps, 0.5_mps};
+
+  const frc::DifferentialDriveWheelSpeeds result = speeds * 2;
+
+  EXPECT_EQ(2.0, result.left.value());
+  EXPECT_EQ(1.0, result.right.value());
+}
+
+TEST(DifferentialDriveWheelSpeedsTest, Division) {
+  const frc::DifferentialDriveWheelSpeeds speeds{1.0_mps, 0.5_mps};
+
+  const frc::DifferentialDriveWheelSpeeds result = speeds / 2;
+
+  EXPECT_EQ(0.5, result.left.value());
+  EXPECT_EQ(0.25, result.right.value());
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveKinematicsTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveKinematicsTest.cpp
index 364163e..04a3f1f 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveKinematicsTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveKinematicsTest.cpp
@@ -4,9 +4,10 @@
 
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Translation2d.h"
 #include "frc/kinematics/MecanumDriveKinematics.h"
-#include "gtest/gtest.h"
 #include "units/angular_velocity.h"
 
 using namespace frc;
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveOdometryTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveOdometryTest.cpp
index bfaf91b..be20655 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveOdometryTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveOdometryTest.cpp
@@ -5,9 +5,10 @@
 #include <limits>
 #include <random>
 
+#include <gtest/gtest.h>
+
 #include "frc/kinematics/MecanumDriveOdometry.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveWheelSpeedsTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveWheelSpeedsTest.cpp
new file mode 100644
index 0000000..c10cfb5
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/MecanumDriveWheelSpeedsTest.cpp
@@ -0,0 +1,64 @@
+// 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 <gtest/gtest.h>
+
+#include "frc/kinematics/MecanumDriveWheelSpeeds.h"
+
+TEST(MecanumDriveWheelSpeedsTest, Plus) {
+  const frc::MecanumDriveWheelSpeeds left{1.0_mps, 0.5_mps, 2.0_mps, 1.5_mps};
+  const frc::MecanumDriveWheelSpeeds right{2.0_mps, 1.5_mps, 0.5_mps, 1.0_mps};
+
+  const frc::MecanumDriveWheelSpeeds result = left + right;
+
+  EXPECT_EQ(3.0, result.frontLeft.value());
+  EXPECT_EQ(2.0, result.frontRight.value());
+  EXPECT_EQ(2.5, result.rearLeft.value());
+  EXPECT_EQ(2.5, result.rearRight.value());
+}
+
+TEST(MecanumDriveWheelSpeedsTest, Minus) {
+  const frc::MecanumDriveWheelSpeeds left{1.0_mps, 0.5_mps, 2.0_mps, 1.5_mps};
+  const frc::MecanumDriveWheelSpeeds right{2.0_mps, 1.5_mps, 0.5_mps, 1.0_mps};
+
+  const frc::MecanumDriveWheelSpeeds result = left - right;
+
+  EXPECT_EQ(-1.0, result.frontLeft.value());
+  EXPECT_EQ(-1.0, result.frontRight.value());
+  EXPECT_EQ(1.5, result.rearLeft.value());
+  EXPECT_EQ(0.5, result.rearRight.value());
+}
+
+TEST(MecanumDriveWheelSpeedsTest, UnaryMinus) {
+  const frc::MecanumDriveWheelSpeeds speeds{1.0_mps, 0.5_mps, 2.0_mps, 1.5_mps};
+
+  const frc::MecanumDriveWheelSpeeds result = -speeds;
+
+  EXPECT_EQ(-1.0, result.frontLeft.value());
+  EXPECT_EQ(-0.5, result.frontRight.value());
+  EXPECT_EQ(-2.0, result.rearLeft.value());
+  EXPECT_EQ(-1.5, result.rearRight.value());
+}
+
+TEST(MecanumDriveWheelSpeedsTest, Multiplication) {
+  const frc::MecanumDriveWheelSpeeds speeds{1.0_mps, 0.5_mps, 2.0_mps, 1.5_mps};
+
+  const frc::MecanumDriveWheelSpeeds result = speeds * 2;
+
+  EXPECT_EQ(2.0, result.frontLeft.value());
+  EXPECT_EQ(1.0, result.frontRight.value());
+  EXPECT_EQ(4.0, result.rearLeft.value());
+  EXPECT_EQ(3.0, result.rearRight.value());
+}
+
+TEST(MecanumDriveWheelSpeedsTest, Division) {
+  const frc::MecanumDriveWheelSpeeds speeds{1.0_mps, 0.5_mps, 2.0_mps, 1.5_mps};
+
+  const frc::MecanumDriveWheelSpeeds result = speeds / 2;
+
+  EXPECT_EQ(0.5, result.frontLeft.value());
+  EXPECT_EQ(0.25, result.frontRight.value());
+  EXPECT_EQ(1.0, result.rearLeft.value());
+  EXPECT_EQ(0.75, result.rearRight.value());
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveDriveKinematicsTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveDriveKinematicsTest.cpp
index 8e0dc8f..e7726e6 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveDriveKinematicsTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveDriveKinematicsTest.cpp
@@ -4,9 +4,10 @@
 
 #include <numbers>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Translation2d.h"
 #include "frc/kinematics/SwerveDriveKinematics.h"
-#include "gtest/gtest.h"
 #include "units/angular_velocity.h"
 
 using namespace frc;
@@ -125,6 +126,25 @@
   EXPECT_NEAR(bl.angle.Degrees().value(), -135.0, kEpsilon);
   EXPECT_NEAR(br.angle.Degrees().value(), -45.0, kEpsilon);
 }
+TEST_F(SwerveDriveKinematicsTest, ResetWheelAngle) {
+  Rotation2d fl = {0_deg};
+  Rotation2d fr = {90_deg};
+  Rotation2d bl = {180_deg};
+  Rotation2d br = {270_deg};
+  m_kinematics.ResetHeadings(fl, fr, bl, br);
+  auto [flMod, frMod, blMod, brMod] =
+      m_kinematics.ToSwerveModuleStates(ChassisSpeeds{});
+
+  EXPECT_NEAR(flMod.speed.value(), 0.0, kEpsilon);
+  EXPECT_NEAR(frMod.speed.value(), 0.0, kEpsilon);
+  EXPECT_NEAR(blMod.speed.value(), 0.0, kEpsilon);
+  EXPECT_NEAR(brMod.speed.value(), 0.0, kEpsilon);
+
+  EXPECT_NEAR(flMod.angle.Degrees().value(), 0.0, kEpsilon);
+  EXPECT_NEAR(frMod.angle.Degrees().value(), 90.0, kEpsilon);
+  EXPECT_NEAR(blMod.angle.Degrees().value(), 180.0, kEpsilon);
+  EXPECT_NEAR(brMod.angle.Degrees().value(), 270.0, kEpsilon);
+}
 
 TEST_F(SwerveDriveKinematicsTest, TurnInPlaceForwardKinematics) {
   SwerveModuleState fl{106.629_mps, 135_deg};
@@ -274,3 +294,18 @@
   EXPECT_NEAR(arr[2].speed.value(), 4.0 * kFactor, kEpsilon);
   EXPECT_NEAR(arr[3].speed.value(), 7.0 * kFactor, kEpsilon);
 }
+
+TEST_F(SwerveDriveKinematicsTest, DesaturateNegativeSpeed) {
+  SwerveModuleState state1{1.0_mps, 0_deg};
+  SwerveModuleState state2{1.0_mps, 0_deg};
+  SwerveModuleState state3{-2.0_mps, 0_deg};
+  SwerveModuleState state4{-2.0_mps, 0_deg};
+
+  wpi::array<SwerveModuleState, 4> arr{state1, state2, state3, state4};
+  SwerveDriveKinematics<4>::DesaturateWheelSpeeds(&arr, 1.0_mps);
+
+  EXPECT_NEAR(arr[0].speed.value(), 0.5, kEpsilon);
+  EXPECT_NEAR(arr[1].speed.value(), 0.5, kEpsilon);
+  EXPECT_NEAR(arr[2].speed.value(), -1.0, kEpsilon);
+  EXPECT_NEAR(arr[3].speed.value(), -1.0, kEpsilon);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveDriveOdometryTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveDriveOdometryTest.cpp
index 8c67ab5..265b0cc 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveDriveOdometryTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveDriveOdometryTest.cpp
@@ -5,12 +5,13 @@
 #include <limits>
 #include <random>
 
+#include <gtest/gtest.h>
+
 #include "frc/kinematics/SwerveDriveKinematics.h"
 #include "frc/kinematics/SwerveDriveOdometry.h"
 #include "frc/trajectory/Trajectory.h"
 #include "frc/trajectory/TrajectoryConfig.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 
 using namespace frc;
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveModulePositionTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveModulePositionTest.cpp
new file mode 100644
index 0000000..14155f1
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveModulePositionTest.cpp
@@ -0,0 +1,24 @@
+// 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 <gtest/gtest.h>
+
+#include "frc/geometry/Rotation2d.h"
+#include "frc/kinematics/SwerveModulePosition.h"
+
+TEST(SwerveModulePositionTest, Equality) {
+  frc::SwerveModulePosition position1{2_m, 90_deg};
+  frc::SwerveModulePosition position2{2_m, 90_deg};
+
+  EXPECT_EQ(position1, position2);
+}
+
+TEST(SwerveModulePositionTest, Inequality) {
+  frc::SwerveModulePosition position1{1_m, 90_deg};
+  frc::SwerveModulePosition position2{2_m, 90_deg};
+  frc::SwerveModulePosition position3{1_m, 89_deg};
+
+  EXPECT_NE(position1, position2);
+  EXPECT_NE(position1, position3);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveModuleStateTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveModuleStateTest.cpp
index 4880bef..efc3975 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveModuleStateTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/kinematics/SwerveModuleStateTest.cpp
@@ -2,9 +2,10 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/geometry/Rotation2d.h"
 #include "frc/kinematics/SwerveModuleState.h"
-#include "gtest/gtest.h"
 
 static constexpr double kEpsilon = 1E-9;
 
@@ -39,3 +40,19 @@
   EXPECT_NEAR(optimizedB.speed.value(), -2.0, kEpsilon);
   EXPECT_NEAR(optimizedB.angle.Degrees().value(), -2.0, kEpsilon);
 }
+
+TEST(SwerveModuleStateTest, Equality) {
+  frc::SwerveModuleState state1{2_mps, 90_deg};
+  frc::SwerveModuleState state2{2_mps, 90_deg};
+
+  EXPECT_EQ(state1, state2);
+}
+
+TEST(SwerveModuleStateTest, Inequality) {
+  frc::SwerveModuleState state1{1_mps, 90_deg};
+  frc::SwerveModuleState state2{2_mps, 90_deg};
+  frc::SwerveModuleState state3{1_mps, 89_deg};
+
+  EXPECT_NE(state1, state2);
+  EXPECT_NE(state1, state3);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/main.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/main.cpp
index 09072ee..e993c1f 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/main.cpp
@@ -2,7 +2,7 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/spline/CubicHermiteSplineTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/spline/CubicHermiteSplineTest.cpp
index 7422a7f..c98d190 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/spline/CubicHermiteSplineTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/spline/CubicHermiteSplineTest.cpp
@@ -5,12 +5,13 @@
 #include <chrono>
 #include <vector>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Pose2d.h"
 #include "frc/geometry/Rotation2d.h"
 #include "frc/spline/QuinticHermiteSpline.h"
 #include "frc/spline/SplineHelper.h"
 #include "frc/spline/SplineParameterizer.h"
-#include "gtest/gtest.h"
 #include "units/length.h"
 
 using namespace frc;
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/spline/QuinticHermiteSplineTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/spline/QuinticHermiteSplineTest.cpp
index e45df7b..a0df9bb 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/spline/QuinticHermiteSplineTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/spline/QuinticHermiteSplineTest.cpp
@@ -4,12 +4,13 @@
 
 #include <chrono>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Pose2d.h"
 #include "frc/geometry/Rotation2d.h"
 #include "frc/spline/QuinticHermiteSpline.h"
 #include "frc/spline/SplineHelper.h"
 #include "frc/spline/SplineParameterizer.h"
-#include "gtest/gtest.h"
 #include "units/angle.h"
 #include "units/length.h"
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/system/DiscretizationTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/system/DiscretizationTest.cpp
index d735338..c42fcf0 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/system/DiscretizationTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/system/DiscretizationTest.cpp
@@ -2,11 +2,11 @@
 // 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 <gtest/gtest.h>
-
 #include <functional>
 
-#include "Eigen/Eigenvalues"
+#include <Eigen/Eigenvalues>
+#include <gtest/gtest.h>
+
 #include "frc/EigenCore.h"
 #include "frc/system/Discretization.h"
 #include "frc/system/NumericalIntegration.h"
@@ -114,98 +114,6 @@
       << discQIntegrated;
 }
 
-// Test that the Taylor series discretization produces nearly identical results.
-TEST(DiscretizationTest, DiscretizeSlowModelAQTaylor) {
-  frc::Matrixd<2, 2> contA{{0, 1}, {0, 0}};
-  frc::Matrixd<2, 2> contQ{{1, 0}, {0, 1}};
-
-  constexpr auto dt = 1_s;
-
-  frc::Matrixd<2, 2> discQTaylor;
-  frc::Matrixd<2, 2> discA;
-  frc::Matrixd<2, 2> discATaylor;
-
-  // Continuous Q should be positive semidefinite
-  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esCont{contQ};
-  for (int i = 0; i < contQ.rows(); ++i) {
-    EXPECT_GE(esCont.eigenvalues()[i], 0);
-  }
-
-  //       T
-  // Q_d = ∫ e^(Aτ) Q e^(Aᵀτ) dτ
-  //       0
-  frc::Matrixd<2, 2> discQIntegrated = frc::RungeKuttaTimeVarying<
-      std::function<frc::Matrixd<2, 2>(units::second_t,
-                                       const frc::Matrixd<2, 2>&)>,
-      frc::Matrixd<2, 2>>(
-      [&](units::second_t t, const frc::Matrixd<2, 2>&) {
-        return frc::Matrixd<2, 2>((contA * t.value()).exp() * contQ *
-                                  (contA.transpose() * t.value()).exp());
-      },
-      0_s, frc::Matrixd<2, 2>::Zero(), dt);
-
-  frc::DiscretizeA<2>(contA, dt, &discA);
-  frc::DiscretizeAQTaylor<2>(contA, contQ, dt, &discATaylor, &discQTaylor);
-
-  EXPECT_LT((discQIntegrated - discQTaylor).norm(), 1e-10)
-      << "Expected these to be nearly equal:\ndiscQTaylor:\n"
-      << discQTaylor << "\ndiscQIntegrated:\n"
-      << discQIntegrated;
-  EXPECT_LT((discA - discATaylor).norm(), 1e-10);
-
-  // Discrete Q should be positive semidefinite
-  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esDisc{discQTaylor};
-  for (int i = 0; i < discQTaylor.rows(); ++i) {
-    EXPECT_GE(esDisc.eigenvalues()[i], 0);
-  }
-}
-
-// Test that the Taylor series discretization produces nearly identical results.
-TEST(DiscretizationTest, DiscretizeFastModelAQTaylor) {
-  frc::Matrixd<2, 2> contA{{0, 1}, {0, -1500}};
-  frc::Matrixd<2, 2> contQ{{0.0025, 0}, {0, 1}};
-
-  constexpr auto dt = 5_ms;
-
-  frc::Matrixd<2, 2> discQTaylor;
-  frc::Matrixd<2, 2> discA;
-  frc::Matrixd<2, 2> discATaylor;
-
-  // Continuous Q should be positive semidefinite
-  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esCont(contQ);
-  for (int i = 0; i < contQ.rows(); ++i) {
-    EXPECT_GE(esCont.eigenvalues()[i], 0);
-  }
-
-  //       T
-  // Q_d = ∫ e^(Aτ) Q e^(Aᵀτ) dτ
-  //       0
-  frc::Matrixd<2, 2> discQIntegrated = frc::RungeKuttaTimeVarying<
-      std::function<frc::Matrixd<2, 2>(units::second_t,
-                                       const frc::Matrixd<2, 2>&)>,
-      frc::Matrixd<2, 2>>(
-      [&](units::second_t t, const frc::Matrixd<2, 2>&) {
-        return frc::Matrixd<2, 2>((contA * t.value()).exp() * contQ *
-                                  (contA.transpose() * t.value()).exp());
-      },
-      0_s, frc::Matrixd<2, 2>::Zero(), dt);
-
-  frc::DiscretizeA<2>(contA, dt, &discA);
-  frc::DiscretizeAQTaylor<2>(contA, contQ, dt, &discATaylor, &discQTaylor);
-
-  EXPECT_LT((discQIntegrated - discQTaylor).norm(), 1e-3)
-      << "Expected these to be nearly equal:\ndiscQTaylor:\n"
-      << discQTaylor << "\ndiscQIntegrated:\n"
-      << discQIntegrated;
-  EXPECT_LT((discA - discATaylor).norm(), 1e-10);
-
-  // Discrete Q should be positive semidefinite
-  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esDisc(discQTaylor);
-  for (int i = 0; i < discQTaylor.rows(); ++i) {
-    EXPECT_GE(esDisc.eigenvalues()[i], 0);
-  }
-}
-
 // Test that DiscretizeR() works
 TEST(DiscretizationTest, DiscretizeR) {
   frc::Matrixd<2, 2> contR{{2.0, 0.0}, {0.0, 1.0}};
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/system/LinearSystemIDTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/system/LinearSystemIDTest.cpp
index dbc4284..4dc6263 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/system/LinearSystemIDTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/system/LinearSystemIDTest.cpp
@@ -5,6 +5,7 @@
 #include <frc/system/LinearSystem.h>
 #include <frc/system/plant/DCMotor.h>
 #include <frc/system/plant/LinearSystemId.h>
+
 #include <gtest/gtest.h>
 
 #include "frc/system/plant/LinearSystemId.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/system/NumericalIntegrationTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/system/NumericalIntegrationTest.cpp
index b1793ad..1c73195 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/system/NumericalIntegrationTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/system/NumericalIntegrationTest.cpp
@@ -2,10 +2,11 @@
 // 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 <gtest/gtest.h>
-
 #include <cmath>
 
+#include <gtest/gtest.h>
+
+#include "frc/EigenCore.h"
 #include "frc/system/NumericalIntegration.h"
 
 // Tests that integrating dx/dt = e^x works.
@@ -30,6 +31,16 @@
   EXPECT_NEAR(y1(0), std::exp(0.1) - std::exp(0), 1e-3);
 }
 
+// Tests that integrating dx/dt = 0 works with RKDP
+TEST(NumericalIntegrationTest, ZeroRKDP) {
+  frc::Vectord<1> y1 = frc::RKDP(
+      [](const frc::Vectord<1>& x, const frc::Vectord<1>& u) {
+        return frc::Vectord<1>::Zero();
+      },
+      frc::Vectord<1>{0.0}, frc::Vectord<1>{0.0}, 0.1_s);
+  EXPECT_NEAR(y1(0), 0.0, 1e-3);
+}
+
 // Tests that integrating dx/dt = e^x works with RKDP
 TEST(NumericalIntegrationTest, ExponentialRKDP) {
   frc::Vectord<1> y0{0.0};
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/system/RungeKuttaTimeVaryingTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/system/RungeKuttaTimeVaryingTest.cpp
index 70f1938..e502e95 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/system/RungeKuttaTimeVaryingTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/system/RungeKuttaTimeVaryingTest.cpp
@@ -2,10 +2,11 @@
 // 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 <gtest/gtest.h>
-
 #include <cmath>
 
+#include <gtest/gtest.h>
+
+#include "frc/EigenCore.h"
 #include "frc/system/RungeKuttaTimeVarying.h"
 
 namespace {
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/CentripetalAccelerationConstraintTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/CentripetalAccelerationConstraintTest.cpp
index e2f7112..fafcbd4 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/CentripetalAccelerationConstraintTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/CentripetalAccelerationConstraintTest.cpp
@@ -5,9 +5,10 @@
 #include <memory>
 #include <vector>
 
+#include <gtest/gtest.h>
+
 #include "frc/trajectory/constraint/CentripetalAccelerationConstraint.h"
 #include "frc/trajectory/constraint/TrajectoryConstraint.h"
-#include "gtest/gtest.h"
 #include "trajectory/TestTrajectory.h"
 #include "units/acceleration.h"
 #include "units/angle.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/DifferentialDriveKinematicsTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/DifferentialDriveKinematicsTest.cpp
index e3723b5..09abc76 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/DifferentialDriveKinematicsTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/DifferentialDriveKinematicsTest.cpp
@@ -5,9 +5,10 @@
 #include <memory>
 #include <vector>
 
+#include <gtest/gtest.h>
+
 #include "frc/kinematics/DifferentialDriveKinematics.h"
 #include "frc/trajectory/constraint/DifferentialDriveKinematicsConstraint.h"
-#include "gtest/gtest.h"
 #include "trajectory/TestTrajectory.h"
 #include "units/time.h"
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/DifferentialDriveVoltageTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/DifferentialDriveVoltageTest.cpp
index e3d6b7f..5282638 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/DifferentialDriveVoltageTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/DifferentialDriveVoltageTest.cpp
@@ -5,11 +5,12 @@
 #include <memory>
 #include <vector>
 
+#include <gtest/gtest.h>
+
 #include "frc/geometry/Pose2d.h"
 #include "frc/kinematics/DifferentialDriveKinematics.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
 #include "frc/trajectory/constraint/DifferentialDriveVoltageConstraint.h"
-#include "gtest/gtest.h"
 #include "trajectory/TestTrajectory.h"
 #include "units/acceleration.h"
 #include "units/length.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/EllipticalRegionConstraintTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/EllipticalRegionConstraintTest.cpp
index 8d9e221..c39bb15 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/EllipticalRegionConstraintTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/EllipticalRegionConstraintTest.cpp
@@ -4,10 +4,11 @@
 
 #include <vector>
 
+#include <gtest/gtest.h>
+
 #include "frc/kinematics/DifferentialDriveKinematics.h"
 #include "frc/trajectory/constraint/EllipticalRegionConstraint.h"
 #include "frc/trajectory/constraint/MaxVelocityConstraint.h"
-#include "gtest/gtest.h"
 #include "trajectory/TestTrajectory.h"
 #include "units/acceleration.h"
 #include "units/angle.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/ExponentialProfileTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/ExponentialProfileTest.cpp
new file mode 100644
index 0000000..8df3aa1
--- /dev/null
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/ExponentialProfileTest.cpp
@@ -0,0 +1,338 @@
+// 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 "frc/trajectory/ExponentialProfile.h"  // NOLINT(build/include_order)
+
+#include <chrono>
+#include <cmath>
+#include <tuple>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "frc/controller/SimpleMotorFeedforward.h"
+#include "units/acceleration.h"
+#include "units/frequency.h"
+#include "units/length.h"
+#include "units/math.h"
+#include "units/velocity.h"
+#include "units/voltage.h"
+
+static constexpr auto kDt = 10_ms;
+static constexpr auto kV = 2.5629_V / 1_mps;
+static constexpr auto kA = 0.43277_V / 1_mps_sq;
+
+#define EXPECT_NEAR_UNITS(val1, val2, eps) \
+  EXPECT_LE(units::math::abs(val1 - val2), eps)
+
+#define EXPECT_LT_OR_NEAR_UNITS(val1, val2, eps) \
+  if (val1 <= val2) {                            \
+    EXPECT_LE(val1, val2);                       \
+  } else {                                       \
+    EXPECT_NEAR_UNITS(val1, val2, eps);          \
+  }
+
+frc::ExponentialProfile<units::meter, units::volts>::State CheckDynamics(
+    frc::ExponentialProfile<units::meter, units::volts> profile,
+    frc::ExponentialProfile<units::meter, units::volts>::Constraints
+        constraints,
+    frc::SimpleMotorFeedforward<units::meter> feedforward,
+    frc::ExponentialProfile<units::meter, units::volts>::State current,
+    frc::ExponentialProfile<units::meter, units::volts>::State goal) {
+  auto next = profile.Calculate(kDt, current, goal);
+  auto signal = feedforward.Calculate(current.velocity, next.velocity, kDt);
+
+  EXPECT_LE(units::math::abs(signal), constraints.maxInput + 1e-9_V);
+
+  return next;
+}
+
+TEST(ExponentialProfileTest, ReachesGoal) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{10_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state{0_m, 0_mps};
+
+  for (int i = 0; i < 450; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+  }
+  EXPECT_EQ(state, goal);
+}
+
+// Tests that decreasing the maximum velocity in the middle when it is already
+// moving faster than the new max is handled correctly
+TEST(ExponentialProfileTest, PosContinousUnderVelChange) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{10_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state{0_m, 0_mps};
+
+  for (int i = 0; i < 300; ++i) {
+    if (i == 150) {
+      constraints.maxInput = 9_V;
+      profile =
+          frc::ExponentialProfile<units::meter, units::volts>{constraints};
+    }
+
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+  }
+  EXPECT_EQ(state, goal);
+}
+
+// Tests that decreasing the maximum velocity in the middle when it is already
+// moving faster than the new max is handled correctly
+TEST(ExponentialProfileTest, PosContinousUnderVelChangeBackward) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{-10_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state{0_m, 0_mps};
+
+  for (int i = 0; i < 300; ++i) {
+    if (i == 150) {
+      constraints.maxInput = 9_V;
+      profile =
+          frc::ExponentialProfile<units::meter, units::volts>{constraints};
+    }
+
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+  }
+  EXPECT_EQ(state, goal);
+}
+
+// There is some somewhat tricky code for dealing with going backwards
+TEST(ExponentialProfileTest, Backwards) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{-10_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state;
+
+  for (int i = 0; i < 400; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+  }
+  EXPECT_EQ(state, goal);
+}
+
+TEST(ExponentialProfileTest, SwitchGoalInMiddle) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{-10_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state{0_m, 0_mps};
+
+  for (int i = 0; i < 50; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+  }
+  EXPECT_NE(state, goal);
+
+  goal = {0.0_m, 0.0_mps};
+  for (int i = 0; i < 100; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+  }
+  EXPECT_EQ(state, goal);
+}
+
+// Checks to make sure that it hits top speed on long trajectories
+TEST(ExponentialProfileTest, TopSpeed) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{40_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state;
+
+  units::meters_per_second_t maxSpeed = 0_mps;
+
+  for (int i = 0; i < 900; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+    maxSpeed = units::math::max(state.velocity, maxSpeed);
+  }
+
+  EXPECT_NEAR_UNITS(constraints.MaxVelocity(), maxSpeed, 1e-5_mps);
+  EXPECT_EQ(state, goal);
+}
+
+// Checks to make sure that it hits top speed on long trajectories
+TEST(ExponentialProfileTest, TopSpeedBackward) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{-40_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state;
+
+  units::meters_per_second_t maxSpeed = 0_mps;
+
+  for (int i = 0; i < 900; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+    maxSpeed = units::math::min(state.velocity, maxSpeed);
+  }
+
+  EXPECT_NEAR_UNITS(-constraints.MaxVelocity(), maxSpeed, 1e-5_mps);
+  EXPECT_EQ(state, goal);
+}
+
+// Checks to make sure that it hits top speed on long trajectories
+TEST(ExponentialProfileTest, HighInitialSpeed) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{40_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state{0_m, 8_mps};
+
+  for (int i = 0; i < 900; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+  }
+
+  EXPECT_EQ(state, goal);
+}
+
+// Checks to make sure that it hits top speed on long trajectories
+TEST(ExponentialProfileTest, HighInitialSpeedBackward) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{-40_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state{0_m, -8_mps};
+
+  for (int i = 0; i < 900; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+  }
+
+  EXPECT_EQ(state, goal);
+}
+
+TEST(ExponentialProfileTest, TestHeuristic) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  std::vector<std::tuple<
+      frc::ExponentialProfile<units::meter, units::volts>::State,  // initial
+      frc::ExponentialProfile<units::meter, units::volts>::State,  // goal
+      frc::ExponentialProfile<units::meter, units::volts>::State>  // inflection
+                                                                   // point
+              >
+      testCases{
+          // red > green and purple => always positive => false
+          {{0_m, -4_mps}, {0.75_m, -4_mps}, {1.3758_m, 4.4304_mps}},
+          {{0_m, -4_mps}, {1.4103_m, 4_mps}, {1.3758_m, 4.4304_mps}},
+          {{0.6603_m, 4_mps}, {0.75_m, -4_mps}, {1.3758_m, 4.4304_mps}},
+          {{0.6603_m, 4_mps}, {1.4103_m, 4_mps}, {1.3758_m, 4.4304_mps}},
+
+          // purple > red > green => positive if v0 < 0 => c && !d && a
+          {{0_m, -4_mps}, {0.5_m, -2_mps}, {0.4367_m, 3.7217_mps}},
+          {{0_m, -4_mps}, {0.546_m, 2_mps}, {0.4367_m, 3.7217_mps}},
+          {{0.6603_m, 4_mps}, {0.5_m, -2_mps}, {0.5560_m, -2.9616_mps}},
+          {{0.6603_m, 4_mps}, {0.546_m, 2_mps}, {0.5560_m, -2.9616_mps}},
+
+          // red < green and purple => always negative => true => !c && !d
+          {{0_m, -4_mps}, {-0.75_m, -4_mps}, {-0.7156_m, -4.4304_mps}},
+          {{0_m, -4_mps}, {-0.0897_m, 4_mps}, {-0.7156_m, -4.4304_mps}},
+          {{0.6603_m, 4_mps}, {-0.75_m, -4_mps}, {-0.7156_m, -4.4304_mps}},
+          {{0.6603_m, 4_mps}, {-0.0897_m, 4_mps}, {-0.7156_m, -4.4304_mps}},
+
+          // green > red > purple => positive if vf < 0 => !c && d && b
+          {{0_m, -4_mps}, {-0.5_m, -4.5_mps}, {1.095_m, 4.314_mps}},
+          {{0_m, -4_mps}, {1.0795_m, 4.5_mps}, {-0.5122_m, -4.351_mps}},
+          {{0.6603_m, 4_mps}, {-0.5_m, -4.5_mps}, {1.095_m, 4.314_mps}},
+          {{0.6603_m, 4_mps}, {1.0795_m, 4.5_mps}, {-0.5122_m, -4.351_mps}},
+
+          // tests for initial velocity > V/kV
+          {{0_m, -8_mps}, {0_m, 0_mps}, {-0.1384_m, 3.342_mps}},
+          {{0_m, -8_mps}, {-1_m, 0_mps}, {-0.562_m, -6.792_mps}},
+          {{0_m, 8_mps}, {1_m, 0_mps}, {0.562_m, 6.792_mps}},
+          {{0_m, 8_mps}, {-1_m, 0_mps}, {-0.785_m, -4.346_mps}},
+      };
+
+  for (auto& testCase : testCases) {
+    auto state = profile.CalculateInflectionPoint(std::get<0>(testCase),
+                                                  std::get<1>(testCase));
+    EXPECT_NEAR_UNITS(std::get<2>(testCase).position / 1_m,
+                      state.position / 1_m, 1e-3);
+    EXPECT_NEAR_UNITS(std::get<2>(testCase).velocity / 1_mps,
+                      state.velocity / 1_mps, 1e-3);
+  }
+}
+
+TEST(ExponentialProfileTest, TimingToCurrent) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{2_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state{0_m, 0_mps};
+
+  for (int i = 0; i < 900; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+    EXPECT_NEAR_UNITS(profile.TimeLeftUntil(state, state), 0_s, 2e-2_s);
+  }
+
+  EXPECT_EQ(state, goal);
+}
+
+TEST(ExponentialProfileTest, TimingToGoal) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{2_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state{0_m, 0_mps};
+
+  auto prediction = profile.TimeLeftUntil(state, goal);
+  auto reachedGoal = false;
+
+  for (int i = 0; i < 900; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+    if (!reachedGoal && state == goal) {
+      EXPECT_NEAR_UNITS(prediction, i * 0.01_s, 0.25_s);
+      reachedGoal = true;
+    }
+  }
+
+  EXPECT_EQ(state, goal);
+}
+
+TEST(ExponentialProfileTest, TimingToNegativeGoal) {
+  frc::ExponentialProfile<units::meter, units::volts>::Constraints constraints{
+      12_V, -kV / kA, 1 / kA};
+  frc::ExponentialProfile<units::meter, units::volts> profile{constraints};
+  frc::SimpleMotorFeedforward<units::meter> feedforward{0_V, 2.5629_V / 1_mps,
+                                                        0.43277_V / 1_mps_sq};
+  frc::ExponentialProfile<units::meter, units::volts>::State goal{-2_m, 0_mps};
+  frc::ExponentialProfile<units::meter, units::volts>::State state{0_m, 0_mps};
+
+  auto prediction = profile.TimeLeftUntil(state, goal);
+  auto reachedGoal = false;
+
+  for (int i = 0; i < 900; ++i) {
+    state = CheckDynamics(profile, constraints, feedforward, state, goal);
+    if (!reachedGoal && state == goal) {
+      EXPECT_NEAR_UNITS(prediction, i * 0.01_s, 0.25_s);
+      reachedGoal = true;
+    }
+  }
+
+  EXPECT_EQ(state, goal);
+}
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/RectangularRegionConstraintTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/RectangularRegionConstraintTest.cpp
index 0bf6b1a..8ec3a70 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/RectangularRegionConstraintTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/RectangularRegionConstraintTest.cpp
@@ -4,10 +4,11 @@
 
 #include <vector>
 
+#include <gtest/gtest.h>
+
 #include "frc/kinematics/DifferentialDriveKinematics.h"
 #include "frc/trajectory/constraint/MaxVelocityConstraint.h"
 #include "frc/trajectory/constraint/RectangularRegionConstraint.h"
-#include "gtest/gtest.h"
 #include "trajectory/TestTrajectory.h"
 #include "units/acceleration.h"
 #include "units/length.h"
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryConcatenateTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryConcatenateTest.cpp
index 2b733a8..2bfd541 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryConcatenateTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryConcatenateTest.cpp
@@ -2,9 +2,10 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/trajectory/TrajectoryConfig.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 
 TEST(TrajectoryConcatenateTest, States) {
   auto t1 = frc::TrajectoryGenerator::GenerateTrajectory(
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryGeneratorTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryGeneratorTest.cpp
index 5892461..8b50160 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryGeneratorTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryGeneratorTest.cpp
@@ -4,11 +4,12 @@
 
 #include <vector>
 
+#include <gtest/gtest.h>
+
 #include "frc/trajectory/Trajectory.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
 #include "frc/trajectory/constraint/CentripetalAccelerationConstraint.h"
 #include "frc/trajectory/constraint/TrajectoryConstraint.h"
-#include "gtest/gtest.h"
 #include "trajectory/TestTrajectory.h"
 #include "units/math.h"
 
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryJsonTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryJsonTest.cpp
index 90c6dc0..9411696 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryJsonTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryJsonTest.cpp
@@ -2,9 +2,10 @@
 // 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 <gtest/gtest.h>
+
 #include "frc/trajectory/TrajectoryConfig.h"
 #include "frc/trajectory/TrajectoryUtil.h"
-#include "gtest/gtest.h"
 #include "trajectory/TestTrajectory.h"
 
 using namespace frc;
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryTransformTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryTransformTest.cpp
index 5c77a5b..f088fa3 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryTransformTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrajectoryTransformTest.cpp
@@ -4,10 +4,11 @@
 
 #include <vector>
 
+#include <gtest/gtest.h>
+
 #include "frc/trajectory/Trajectory.h"
 #include "frc/trajectory/TrajectoryConfig.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
 
 void TestSameShapedTrajectory(std::vector<frc::Trajectory::State> statesA,
                               std::vector<frc::Trajectory::State> statesB) {
diff --git a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrapezoidProfileTest.cpp b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrapezoidProfileTest.cpp
index 6a35261..b2e5b18 100644
--- a/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrapezoidProfileTest.cpp
+++ b/third_party/allwpilib/wpimath/src/test/native/cpp/trajectory/TrapezoidProfileTest.cpp
@@ -7,7 +7,8 @@
 #include <chrono>
 #include <cmath>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "units/acceleration.h"
 #include "units/length.h"
 #include "units/math.h"
@@ -31,9 +32,9 @@
   frc::TrapezoidProfile<units::meter>::State goal{3_m, 0_mps};
   frc::TrapezoidProfile<units::meter>::State state;
 
+  frc::TrapezoidProfile<units::meter> profile{constraints};
   for (int i = 0; i < 450; ++i) {
-    frc::TrapezoidProfile<units::meter> profile{constraints, goal, state};
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
   }
   EXPECT_EQ(state, goal);
 }
@@ -45,17 +46,18 @@
                                                                0.75_mps_sq};
   frc::TrapezoidProfile<units::meter>::State goal{12_m, 0_mps};
 
-  frc::TrapezoidProfile<units::meter> profile{constraints, goal};
-  auto state = profile.Calculate(kDt);
+  frc::TrapezoidProfile<units::meter> profile{constraints};
+  auto state = profile.Calculate(kDt, goal,
+                                 frc::TrapezoidProfile<units::meter>::State{});
 
   auto lastPos = state.position;
   for (int i = 0; i < 1600; ++i) {
     if (i == 400) {
       constraints.maxVelocity = 0.75_mps;
+      profile = frc::TrapezoidProfile<units::meter>{constraints};
     }
 
-    profile = frc::TrapezoidProfile<units::meter>{constraints, goal, state};
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
     auto estimatedVel = (state.position - lastPos) / kDt;
 
     if (i >= 400) {
@@ -79,9 +81,9 @@
   frc::TrapezoidProfile<units::meter>::State goal{-2_m, 0_mps};
   frc::TrapezoidProfile<units::meter>::State state;
 
+  frc::TrapezoidProfile<units::meter> profile{constraints};
   for (int i = 0; i < 400; ++i) {
-    frc::TrapezoidProfile<units::meter> profile{constraints, goal, state};
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
   }
   EXPECT_EQ(state, goal);
 }
@@ -92,16 +94,16 @@
   frc::TrapezoidProfile<units::meter>::State goal{-2_m, 0_mps};
   frc::TrapezoidProfile<units::meter>::State state;
 
+  frc::TrapezoidProfile<units::meter> profile{constraints};
   for (int i = 0; i < 200; ++i) {
-    frc::TrapezoidProfile<units::meter> profile{constraints, goal, state};
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
   }
   EXPECT_NE(state, goal);
 
   goal = {0.0_m, 0.0_mps};
+  profile = frc::TrapezoidProfile<units::meter>{constraints};
   for (int i = 0; i < 550; ++i) {
-    frc::TrapezoidProfile<units::meter> profile{constraints, goal, state};
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
   }
   EXPECT_EQ(state, goal);
 }
@@ -113,15 +115,15 @@
   frc::TrapezoidProfile<units::meter>::State goal{4_m, 0_mps};
   frc::TrapezoidProfile<units::meter>::State state;
 
+  frc::TrapezoidProfile<units::meter> profile{constraints};
   for (int i = 0; i < 200; ++i) {
-    frc::TrapezoidProfile<units::meter> profile{constraints, goal, state};
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
   }
   EXPECT_NEAR_UNITS(constraints.maxVelocity, state.velocity, 10e-5_mps);
 
+  profile = frc::TrapezoidProfile<units::meter>{constraints};
   for (int i = 0; i < 2000; ++i) {
-    frc::TrapezoidProfile<units::meter> profile{constraints, goal, state};
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
   }
   EXPECT_EQ(state, goal);
 }
@@ -132,9 +134,9 @@
   frc::TrapezoidProfile<units::meter>::State goal{2_m, 0_mps};
   frc::TrapezoidProfile<units::meter>::State state;
 
+  frc::TrapezoidProfile<units::meter> profile{constraints};
   for (int i = 0; i < 400; i++) {
-    frc::TrapezoidProfile<units::meter> profile{constraints, goal, state};
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
     EXPECT_NEAR_UNITS(profile.TimeLeftUntil(state.position), 0_s, 2e-2_s);
   }
 }
@@ -146,14 +148,14 @@
                                                                0.75_mps_sq};
   frc::TrapezoidProfile<units::meter>::State goal{2_m, 0_mps};
 
-  frc::TrapezoidProfile<units::meter> profile{constraints, goal};
-  auto state = profile.Calculate(kDt);
+  frc::TrapezoidProfile<units::meter> profile{constraints};
+  auto state = profile.Calculate(kDt, goal,
+                                 frc::TrapezoidProfile<units::meter>::State{});
 
   auto predictedTimeLeft = profile.TimeLeftUntil(goal.position);
   bool reachedGoal = false;
   for (int i = 0; i < 400; i++) {
-    profile = frc::TrapezoidProfile<units::meter>(constraints, goal, state);
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
     if (!reachedGoal && state == goal) {
       // Expected value using for loop index is just an approximation since the
       // time left in the profile doesn't increase linearly at the endpoints
@@ -170,14 +172,14 @@
                                                                0.75_mps_sq};
   frc::TrapezoidProfile<units::meter>::State goal{2_m, 0_mps};
 
-  frc::TrapezoidProfile<units::meter> profile{constraints, goal};
-  auto state = profile.Calculate(kDt);
+  frc::TrapezoidProfile<units::meter> profile{constraints};
+  auto state = profile.Calculate(kDt, goal,
+                                 frc::TrapezoidProfile<units::meter>::State{});
 
   auto predictedTimeLeft = profile.TimeLeftUntil(1_m);
   bool reachedGoal = false;
   for (int i = 0; i < 400; i++) {
-    profile = frc::TrapezoidProfile<units::meter>(constraints, goal, state);
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
     if (!reachedGoal &&
         (units::math::abs(state.velocity - 1_mps) < 10e-5_mps)) {
       EXPECT_NEAR(unit_cast<double>(predictedTimeLeft), i / 100.0, 2e-2);
@@ -193,14 +195,14 @@
                                                                0.75_mps_sq};
   frc::TrapezoidProfile<units::meter>::State goal{-2_m, 0_mps};
 
-  frc::TrapezoidProfile<units::meter> profile{constraints, goal};
-  auto state = profile.Calculate(kDt);
+  frc::TrapezoidProfile<units::meter> profile{constraints};
+  auto state = profile.Calculate(kDt, goal,
+                                 frc::TrapezoidProfile<units::meter>::State{});
 
   auto predictedTimeLeft = profile.TimeLeftUntil(goal.position);
   bool reachedGoal = false;
   for (int i = 0; i < 400; i++) {
-    profile = frc::TrapezoidProfile<units::meter>(constraints, goal, state);
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
     if (!reachedGoal && state == goal) {
       // Expected value using for loop index is just an approximation since the
       // time left in the profile doesn't increase linearly at the endpoints
@@ -217,14 +219,14 @@
                                                                0.75_mps_sq};
   frc::TrapezoidProfile<units::meter>::State goal{-2_m, 0_mps};
 
-  frc::TrapezoidProfile<units::meter> profile{constraints, goal};
-  auto state = profile.Calculate(kDt);
+  frc::TrapezoidProfile<units::meter> profile{constraints};
+  auto state = profile.Calculate(kDt, goal,
+                                 frc::TrapezoidProfile<units::meter>::State{});
 
   auto predictedTimeLeft = profile.TimeLeftUntil(-1_m);
   bool reachedGoal = false;
   for (int i = 0; i < 400; i++) {
-    profile = frc::TrapezoidProfile<units::meter>(constraints, goal, state);
-    state = profile.Calculate(kDt);
+    state = profile.Calculate(kDt, goal, state);
     if (!reachedGoal &&
         (units::math::abs(state.velocity + 1_mps) < 10e-5_mps)) {
       EXPECT_NEAR(unit_cast<double>(predictedTimeLeft), i / 100.0, 2e-2);
diff --git a/third_party/allwpilib/wpimath/src/test/native/include/drake/common/test_utilities/eigen_matrix_compare.h b/third_party/allwpilib/wpimath/src/test/native/include/drake/common/test_utilities/eigen_matrix_compare.h
deleted file mode 100644
index d6bcbb8..0000000
--- a/third_party/allwpilib/wpimath/src/test/native/include/drake/common/test_utilities/eigen_matrix_compare.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#pragma once
-
-#include <algorithm>
-#include <cmath>
-#include <limits>
-
-#include <Eigen/Core>
-#include <gtest/gtest.h>
-
-// #include "drake/common/text_logging.h"
-
-namespace drake {
-
-enum class MatrixCompareType { absolute, relative };
-
-/**
- * Compares two matrices to determine whether they are equal to within a certain
- * threshold.
- *
- * @param m1 The first matrix to compare.
- * @param m2 The second matrix to compare.
- * @param tolerance The tolerance for determining equivalence.
- * @param compare_type Whether the tolereance is absolute or relative.
- * @param explanation A pointer to a string variable for saving an explanation
- * of why @p m1 and @p m2 are unequal. This parameter is optional and defaults
- * to `nullptr`. If this is `nullptr` and @p m1 and @p m2 are not equal, an
- * explanation is logged as an error message.
- * @return true if the two matrices are equal based on the specified tolerance.
- */
-template <typename DerivedA, typename DerivedB>
-[[nodiscard]] ::testing::AssertionResult CompareMatrices(
-    const Eigen::MatrixBase<DerivedA>& m1,
-    const Eigen::MatrixBase<DerivedB>& m2, double tolerance = 0.0,
-    MatrixCompareType compare_type = MatrixCompareType::absolute) {
-  if (m1.rows() != m2.rows() || m1.cols() != m2.cols()) {
-    return ::testing::AssertionFailure()
-           << "Matrix size mismatch: (" << m1.rows() << " x " << m1.cols()
-           << " vs. " << m2.rows() << " x " << m2.cols() << ")";
-  }
-
-  for (int ii = 0; ii < m1.rows(); ii++) {
-    for (int jj = 0; jj < m1.cols(); jj++) {
-      // First handle the corner cases of positive infinity, negative infinity,
-      // and NaN
-      const auto both_positive_infinity =
-          m1(ii, jj) == std::numeric_limits<double>::infinity() &&
-          m2(ii, jj) == std::numeric_limits<double>::infinity();
-
-      const auto both_negative_infinity =
-          m1(ii, jj) == -std::numeric_limits<double>::infinity() &&
-          m2(ii, jj) == -std::numeric_limits<double>::infinity();
-
-      using std::isnan;
-      const auto both_nan = isnan(m1(ii, jj)) && isnan(m2(ii, jj));
-
-      if (both_positive_infinity || both_negative_infinity || both_nan)
-        continue;
-
-      // Check for case where one value is NaN and the other is not
-      if ((isnan(m1(ii, jj)) && !isnan(m2(ii, jj))) ||
-          (!isnan(m1(ii, jj)) && isnan(m2(ii, jj)))) {
-        return ::testing::AssertionFailure() << "NaN mismatch at (" << ii
-                                             << ", " << jj << "):\nm1 =\n"
-                                             << m1 << "\nm2 =\n"
-                                             << m2;
-      }
-
-      // Determine whether the difference between the two matrices is less than
-      // the tolerance.
-      using std::abs;
-      const auto delta = abs(m1(ii, jj) - m2(ii, jj));
-
-      if (compare_type == MatrixCompareType::absolute) {
-        // Perform comparison using absolute tolerance.
-
-        if (delta > tolerance) {
-          return ::testing::AssertionFailure()
-                 << "Values at (" << ii << ", " << jj
-                 << ") exceed tolerance: " << m1(ii, jj) << " vs. "
-                 << m2(ii, jj) << ", diff = " << delta
-                 << ", tolerance = " << tolerance << "\nm1 =\n"
-                 << m1 << "\nm2 =\n"
-                 << m2 << "\ndelta=\n"
-                 << (m1 - m2);
-        }
-      } else {
-        // Perform comparison using relative tolerance, see:
-        // http://realtimecollisiondetection.net/blog/?p=89
-        using std::max;
-        const auto max_value = max(abs(m1(ii, jj)), abs(m2(ii, jj)));
-        const auto relative_tolerance =
-            tolerance * max(decltype(max_value){1}, max_value);
-
-        if (delta > relative_tolerance) {
-          return ::testing::AssertionFailure()
-                 << "Values at (" << ii << ", " << jj
-                 << ") exceed tolerance: " << m1(ii, jj) << " vs. "
-                 << m2(ii, jj) << ", diff = " << delta
-                 << ", tolerance = " << tolerance
-                 << ", relative tolerance = " << relative_tolerance
-                 << "\nm1 =\n"
-                 << m1 << "\nm2 =\n"
-                 << m2 << "\ndelta=\n"
-                 << (m1 - m2);
-        }
-      }
-    }
-  }
-
-  return ::testing::AssertionSuccess() << "m1 =\n"
-                                       << m1
-                                       << "\nis approximately equal to m2 =\n"
-                                       << m2;
-}
-
-}  // namespace drake
diff --git a/third_party/allwpilib/wpimath/src/test/native/include/frc/system/RungeKuttaTimeVarying.h b/third_party/allwpilib/wpimath/src/test/native/include/frc/system/RungeKuttaTimeVarying.h
index 5e7b165..6273532 100644
--- a/third_party/allwpilib/wpimath/src/test/native/include/frc/system/RungeKuttaTimeVarying.h
+++ b/third_party/allwpilib/wpimath/src/test/native/include/frc/system/RungeKuttaTimeVarying.h
@@ -6,7 +6,6 @@
 
 #include <array>
 
-#include "frc/EigenCore.h"
 #include "units/time.h"
 
 namespace frc {
diff --git a/third_party/allwpilib/wpimath/wpimath-config.cmake.in b/third_party/allwpilib/wpimath/wpimath-config.cmake.in
index 4769e43..9100d79 100644
--- a/third_party/allwpilib/wpimath/wpimath-config.cmake.in
+++ b/third_party/allwpilib/wpimath/wpimath-config.cmake.in
@@ -2,5 +2,9 @@
 @FILENAME_DEP_REPLACE@
 @WPIUTIL_DEP_REPLACE@
 
+if(@USE_SYSTEM_EIGEN@)
+    find_dependency(Eigen3)
+endif()
+
 @FILENAME_DEP_REPLACE@
 include(${SELF_DIR}/wpimath.cmake)
diff --git a/third_party/allwpilib/wpinet/.styleguide b/third_party/allwpilib/wpinet/.styleguide
index ab55757..04f54eb 100644
--- a/third_party/allwpilib/wpinet/.styleguide
+++ b/third_party/allwpilib/wpinet/.styleguide
@@ -32,5 +32,7 @@
 
 includeOtherLibs {
   ^fmt/
+  ^gmock/
+  ^gtest/
   ^wpi/
 }
diff --git a/third_party/allwpilib/wpinet/CMakeLists.txt b/third_party/allwpilib/wpinet/CMakeLists.txt
index 3da7bf0..f1eda76 100644
--- a/third_party/allwpilib/wpinet/CMakeLists.txt
+++ b/third_party/allwpilib/wpinet/CMakeLists.txt
@@ -18,12 +18,7 @@
 
   file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
 
-  if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-    set(CMAKE_JAVA_COMPILE_FLAGS "-h" "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-    add_jar(wpinet_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar OUTPUT_NAME wpinet)
-  else()
-    add_jar(wpinet_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar OUTPUT_NAME wpinet GENERATE_NATIVE_HEADERS wpinet_jni_headers)
-  endif()
+  add_jar(wpinet_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar OUTPUT_NAME wpinet GENERATE_NATIVE_HEADERS wpinet_jni_headers)
 
   get_property(WPINET_JAR_FILE TARGET wpinet_jar PROPERTY JAR_FILE)
   install(FILES ${WPINET_JAR_FILE} DESTINATION "${java_lib_dest}")
@@ -36,20 +31,25 @@
 
   set_property(TARGET wpinetjni PROPERTY FOLDER "libraries")
 
-  if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-    target_include_directories(wpinetjni PRIVATE ${JNI_INCLUDE_DIRS})
-    target_include_directories(wpinetjni PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-  else()
-    target_link_libraries(wpinetjni PRIVATE wpinet_jni_headers)
-  endif()
+  target_link_libraries(wpinetjni PRIVATE wpinet_jni_headers)
   add_dependencies(wpinetjni wpinet_jar)
 
-  if (MSVC)
-    install(TARGETS wpinetjni RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-  endif()
+  install(TARGETS wpinetjni EXPORT wpinetjni)
 
-  install(TARGETS wpinetjni EXPORT wpinetjni DESTINATION "${main_lib_dest}")
+endif()
 
+if (WITH_JAVA_SOURCE)
+  find_package(Java REQUIRED)
+  include(UseJava)
+  file(GLOB WPINET_SOURCES src/main/java/edu/wpi/first/net/*.java)
+  add_jar(wpinet_src_jar
+  RESOURCES NAMESPACE "edu/wpi/first/net" ${WPINET_SOURCES}
+  OUTPUT_NAME wpinet-sources)
+
+  get_property(WPINET_SRC_JAR_FILE TARGET wpinet_src_jar PROPERTY JAR_FILE)
+  install(FILES ${WPINET_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+  set_property(TARGET wpinet_src_jar PROPERTY FOLDER "java")
 endif()
 
 set(THREADS_PREFER_PTHREAD_FLAG ON)
@@ -62,7 +62,7 @@
     endif()
 endif()
 
-GENERATE_RESOURCES(src/main/native/resources generated/main/cpp WPI wpi wpinet_resources_src)
+generate_resources(src/main/native/resources generated/main/cpp WPI wpi wpinet_resources_src)
 
 file(GLOB_RECURSE wpinet_native_src src/main/native/cpp/*.cpp src/main/native/thirdparty/tcpsockets/cpp/*.cpp)
 list(REMOVE_ITEM wpinet_native_src ${wpinet_jni_src})
@@ -108,10 +108,7 @@
 )
 
 set(uv_linux_src
-    src/main/native/thirdparty/libuv/src/unix/epoll.cpp
-    src/main/native/thirdparty/libuv/src/unix/linux-core.cpp
-    src/main/native/thirdparty/libuv/src/unix/linux-inotify.cpp
-    src/main/native/thirdparty/libuv/src/unix/linux-syscalls.cpp
+    src/main/native/thirdparty/libuv/src/unix/linux.cpp
     src/main/native/thirdparty/libuv/src/unix/procfs-exepath.cpp
     src/main/native/thirdparty/libuv/src/unix/proctitle.cpp
     src/main/native/thirdparty/libuv/src/unix/random-sysctl-linux.cpp
@@ -149,9 +146,8 @@
         endif()
     endif()
 else()
-    find_package(PkgConfig REQUIRED)
-    pkg_check_modules(libuv REQUIRED IMPORTED_TARGET libuv)
-    target_link_libraries(wpinet PUBLIC PkgConfig::libuv)
+    find_package(libuv CONFIG REQUIRED)
+    target_link_libraries(wpinet PUBLIC $<IF:$<TARGET_EXISTS:libuv::uv_a>,libuv::uv_a,libuv::uv>)
 endif()
 
 if (MSVC)
@@ -175,11 +171,7 @@
                             $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
                             $<INSTALL_INTERFACE:${include_dest}/wpinet>)
 
-install(TARGETS wpinet EXPORT wpinet DESTINATION "${main_lib_dest}")
-
-if (WITH_JAVA AND MSVC)
-    install(TARGETS wpinet RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-endif()
+install(TARGETS wpinet EXPORT wpinet)
 
 if (WITH_FLAT_INSTALL)
     set (wpinet_config_dir ${wpilib_dest})
@@ -191,7 +183,7 @@
 install(FILES ${WPILIB_BINARY_DIR}/wpinet-config.cmake DESTINATION ${wpinet_config_dir})
 install(EXPORT wpinet DESTINATION ${wpinet_config_dir})
 
-SUBDIR_LIST(wpinet_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
+subdir_list(wpinet_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
 foreach(example ${wpinet_examples})
     file(GLOB wpinet_example_src examples/${example}/*.cpp)
     if(wpinet_example_src)
@@ -223,6 +215,6 @@
 
 if (WITH_TESTS)
     wpilib_add_test(wpinet src/test/native/cpp)
-    target_include_directories(wpinet_test PRIVATE src/test/native/include)
-    target_link_libraries(wpinet_test wpinet ${LIBUTIL} gmock_main)
+    target_include_directories(wpinet_test PRIVATE src/test/native/include src/main/native/cpp)
+    target_link_libraries(wpinet_test wpinet ${LIBUTIL} gmock_main wpiutil_testlib)
 endif()
diff --git a/third_party/allwpilib/wpinet/build.gradle b/third_party/allwpilib/wpinet/build.gradle
index b149675..b8b86e4 100644
--- a/third_party/allwpilib/wpinet/build.gradle
+++ b/third_party/allwpilib/wpinet/build.gradle
@@ -147,10 +147,7 @@
                     source {
                         srcDirs 'src/main/native/thirdparty/libuv/src/unix'
                         includes = [
-                            'epoll.cpp',
-                            'linux-core.cpp',
-                            'linux-inotify.cpp',
-                            'linux-syscalls.cpp',
+                            'linux.cpp',
                             'procfs-exepath.cpp',
                             'proctitle.cpp',
                             'random-sysctl-linux.cpp',
@@ -234,6 +231,9 @@
                 binaries.all {
                     lib library: 'wpinet', linkage: 'shared'
                     lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                        nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
+                    }
                 }
                 sources {
                     cpp {
@@ -259,6 +259,9 @@
             binaries.all { binary ->
                 lib project: ':wpinet', library: 'wpinet', linkage: 'static'
                 lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    nativeUtils.useRequiredLibrary(binary, 'ni_link_libraries', 'ni_runtime_libraries')
+                }
                 if (binary.targetPlatform.operatingSystem.isLinux()) {
                     linker.args "-lutil"
                 }
@@ -278,6 +281,9 @@
             binaries.all { binary ->
                 lib project: ':wpinet', library: 'wpinet', linkage: 'static'
                 lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    nativeUtils.useRequiredLibrary(binary, 'ni_link_libraries', 'ni_runtime_libraries')
+                }
             }
         }
     }
diff --git a/third_party/allwpilib/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java b/third_party/allwpilib/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java
index 6dff454..9ccb322 100644
--- a/third_party/allwpilib/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java
+++ b/third_party/allwpilib/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java
@@ -4,11 +4,18 @@
 
 package edu.wpi.first.net;
 
+import edu.wpi.first.util.WPICleaner;
+import java.lang.ref.Cleaner.Cleanable;
 import java.util.Map;
 
 /** Class to announce over mDNS that a service is available. */
 public class MulticastServiceAnnouncer implements AutoCloseable {
   private final int m_handle;
+  private final Cleanable m_cleanable;
+
+  private static Runnable cleanupAction(int handle) {
+    return () -> WPINetJNI.freeMulticastServiceAnnouncer(handle);
+  }
 
   /**
    * Creates a MulticastServiceAnnouncer.
@@ -24,6 +31,7 @@
     String[] values = txt.values().toArray(String[]::new);
     m_handle =
         WPINetJNI.createMulticastServiceAnnouncer(serviceName, serviceType, port, keys, values);
+    m_cleanable = WPICleaner.register(this, cleanupAction(m_handle));
   }
 
   /**
@@ -36,11 +44,12 @@
   public MulticastServiceAnnouncer(String serviceName, String serviceType, int port) {
     m_handle =
         WPINetJNI.createMulticastServiceAnnouncer(serviceName, serviceType, port, null, null);
+    m_cleanable = WPICleaner.register(this, cleanupAction(m_handle));
   }
 
   @Override
   public void close() {
-    WPINetJNI.freeMulticastServiceAnnouncer(m_handle);
+    m_cleanable.clean();
   }
 
   public void start() {
diff --git a/third_party/allwpilib/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java b/third_party/allwpilib/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java
index 2426de2..b676f4c 100644
--- a/third_party/allwpilib/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java
+++ b/third_party/allwpilib/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java
@@ -4,9 +4,17 @@
 
 package edu.wpi.first.net;
 
+import edu.wpi.first.util.WPICleaner;
+import java.lang.ref.Cleaner.Cleanable;
+
 /** Class to resolve a service over mDNS. */
 public class MulticastServiceResolver implements AutoCloseable {
   private final int m_handle;
+  private final Cleanable m_cleanable;
+
+  private static Runnable cleanupAction(int handle) {
+    return () -> WPINetJNI.freeMulticastServiceResolver(handle);
+  }
 
   /**
    * Creates a MulticastServiceResolver.
@@ -15,11 +23,12 @@
    */
   public MulticastServiceResolver(String serviceType) {
     m_handle = WPINetJNI.createMulticastServiceResolver(serviceType);
+    m_cleanable = WPICleaner.register(this, cleanupAction(m_handle));
   }
 
   @Override
   public void close() {
-    WPINetJNI.freeMulticastServiceResolver(m_handle);
+    m_cleanable.clean();
   }
 
   public void start() {
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/DsClient.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/DsClient.cpp
index 86f8f00..97509cb 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/DsClient.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/DsClient.cpp
@@ -21,6 +21,9 @@
     : m_logger{logger},
       m_tcp{uv::Tcp::Create(loop)},
       m_timer{uv::Timer::Create(loop)} {
+  if (!m_tcp || !m_timer) {
+    return;
+  }
   m_tcp->end.connect([this] {
     WPI_DEBUG4(m_logger, "DS connection closed");
     clearIp();
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/EventLoopRunner.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/EventLoopRunner.cpp
index 7c7e79c..6c143ac 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/EventLoopRunner.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/EventLoopRunner.cpp
@@ -59,6 +59,7 @@
       h.SetLoopClosing(true);
       h.Close();
     });
+    loop.SetClosing();
   });
   m_owner.Join();
 }
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/MulticastHandleManager.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/MulticastHandleManager.cpp
index d249a1c..ab44da8 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/MulticastHandleManager.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/MulticastHandleManager.cpp
@@ -10,3 +10,17 @@
   static MulticastHandleManager manager;
   return manager;
 }
+
+#ifdef _WIN32
+MulticastHandleManager::~MulticastHandleManager() {
+  // Multicast handles cannot be safely destructed on windows during shutdown.
+  // Just leak all handles.
+  for (auto&& i : resolvers) {
+    i.second.release();
+  }
+
+  for (auto&& i : announcers) {
+    i.second.release();
+  }
+}
+#endif
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/MulticastHandleManager.h b/third_party/allwpilib/wpinet/src/main/native/cpp/MulticastHandleManager.h
index 8c070f7..9925e84 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/MulticastHandleManager.h
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/MulticastHandleManager.h
@@ -20,6 +20,9 @@
       resolvers;
   wpi::DenseMap<size_t, std::unique_ptr<wpi::MulticastServiceAnnouncer>>
       announcers;
+#ifdef _WIN32
+  ~MulticastHandleManager();
+#endif
 };
 
 MulticastHandleManager& GetMulticastManager();
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/ParallelTcpConnector.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/ParallelTcpConnector.cpp
index 317f0a2..5fb1dd5 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/ParallelTcpConnector.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/ParallelTcpConnector.cpp
@@ -4,6 +4,8 @@
 
 #include "wpinet/ParallelTcpConnector.h"
 
+#include <cstring>
+
 #include <fmt/format.h>
 #include <wpi/Logger.h>
 
@@ -24,6 +26,9 @@
       m_reconnectRate{reconnectRate},
       m_connected{std::move(connected)},
       m_reconnectTimer{uv::Timer::Create(loop)} {
+  if (!m_reconnectTimer) {
+    return;
+  }
   m_reconnectTimer->timeout.connect([this] {
     if (!IsConnected()) {
       WPI_DEBUG1(m_logger, "timed out, reconnecting");
@@ -62,6 +67,29 @@
   }
 }
 
+static bool AddressEquals(const sockaddr& a, const sockaddr& b) {
+  if (a.sa_family != b.sa_family) {
+    return false;
+  }
+  if (a.sa_family == AF_INET) {
+    return reinterpret_cast<const sockaddr_in&>(a).sin_addr.s_addr ==
+           reinterpret_cast<const sockaddr_in&>(b).sin_addr.s_addr;
+  }
+  if (a.sa_family == AF_INET6) {
+    return std::memcmp(&(reinterpret_cast<const sockaddr_in6&>(a).sin6_addr),
+                       &(reinterpret_cast<const sockaddr_in6&>(b).sin6_addr),
+                       sizeof(in6_addr)) == 0;
+  }
+  return false;
+}
+
+static inline sockaddr_storage CopyAddress(const sockaddr& addr,
+                                           socklen_t len) {
+  sockaddr_storage storage;
+  std::memcpy(&storage, &addr, len);
+  return storage;
+}
+
 void ParallelTcpConnector::Connect() {
   if (IsConnected()) {
     return;
@@ -85,8 +113,25 @@
 
           // kick off parallel connection attempts
           for (auto ai = &addrinfo; ai; ai = ai->ai_next) {
+            // check for duplicates
+            bool duplicate = false;
+            for (auto&& attempt : m_attempts) {
+              if (AddressEquals(*ai->ai_addr, reinterpret_cast<const sockaddr&>(
+                                                  attempt.first))) {
+                duplicate = true;
+                break;
+              }
+            }
+            if (duplicate) {
+              continue;
+            }
+
             auto tcp = uv::Tcp::Create(m_loop);
-            m_attempts.emplace_back(tcp);
+            if (!tcp) {
+              continue;
+            }
+            m_attempts.emplace_back(CopyAddress(*ai->ai_addr, ai->ai_addrlen),
+                                    tcp);
 
             auto connreq = std::make_shared<uv::TcpConnectReq>();
             connreq->connected.connect(
@@ -164,8 +209,8 @@
   }
   m_resolvers.clear();
 
-  for (auto&& tcpWeak : m_attempts) {
-    if (auto tcp = tcpWeak.lock()) {
+  for (auto&& attempt : m_attempts) {
+    if (auto tcp = attempt.second.lock()) {
       if (tcp.get() != except) {
         WPI_DEBUG4(m_logger, "canceling connection attempt ({})",
                    static_cast<void*>(tcp.get()));
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/PortForwarder.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/PortForwarder.cpp
index 257b620..67cd806 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/PortForwarder.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/PortForwarder.cpp
@@ -49,6 +49,9 @@
                         unsigned int remotePort) {
   m_impl->runner.ExecSync([&](uv::Loop& loop) {
     auto server = uv::Tcp::Create(loop);
+    if (!server) {
+      return;
+    }
 
     // bind to local port
     server->Bind("", port);
@@ -71,6 +74,10 @@
       client->SetData(connected);
 
       auto remote = uv::Tcp::Create(loop);
+      if (!remote) {
+        client->Close();
+        return;
+      }
       remote->error.connect(
           [remotePtr = remote.get(),
            clientWeak = std::weak_ptr<uv::Tcp>(client)](uv::Error err) {
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocket.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocket.cpp
index ba57925..43b901e 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocket.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocket.cpp
@@ -5,6 +5,9 @@
 #include "wpinet/WebSocket.h"
 
 #include <random>
+#include <span>
+#include <string>
+#include <string_view>
 
 #include <fmt/format.h>
 #include <wpi/Base64.h>
@@ -14,34 +17,91 @@
 #include <wpi/raw_ostream.h>
 #include <wpi/sha1.h>
 
+#include "WebSocketDebug.h"
+#include "WebSocketSerializer.h"
 #include "wpinet/HttpParser.h"
 #include "wpinet/raw_uv_ostream.h"
 #include "wpinet/uv/Stream.h"
 
 using namespace wpi;
 
-namespace {
-class WebSocketWriteReq : public uv::WriteReq {
+#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG
+static std::string DebugBinary(std::span<const uint8_t> val) {
+#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG_CONTENT
+  std::string str;
+  wpi::raw_string_ostream stros{str};
+  for (auto ch : val) {
+    stros << fmt::format("{:02x},", static_cast<unsigned int>(ch) & 0xff);
+  }
+  return str;
+#else
+  return "";
+#endif
+}
+
+static inline std::string_view DebugText(std::string_view val) {
+#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG_CONTENT
+  return val;
+#else
+  return "";
+#endif
+}
+#endif  // WPINET_WEBSOCKET_VERBOSE_DEBUG
+
+class WebSocket::WriteReq : public uv::WriteReq,
+                            public detail::WebSocketWriteReqBase {
  public:
-  explicit WebSocketWriteReq(
+  explicit WriteReq(
+      std::weak_ptr<WebSocket> ws,
       std::function<void(std::span<uv::Buffer>, uv::Error)> callback)
-      : m_callback{std::move(callback)} {
-    finish.connect([this](uv::Error err) {
-      for (auto&& buf : m_internalBufs) {
-        buf.Deallocate();
-      }
-      m_callback(m_userBufs, err);
-    });
+      : m_ws{std::move(ws)}, m_callback{std::move(callback)} {
+    finish.connect([this](uv::Error err) { Send(err); });
   }
 
+  void Send(uv::Error err) {
+    auto ws = m_ws.lock();
+    if (!ws || err) {
+      WS_DEBUG("no WS or error, calling callback\n");
+      m_frames.ReleaseBufs();
+      m_callback(m_userBufs, err);
+      return;
+    }
+
+    // Continue() is designed so this is *only* called on frame boundaries
+    if (m_controlCont) {
+      // We have a control frame; switch to it.  We will come back here via
+      // the control frame's m_cont when it's done.
+      WS_DEBUG("Continuing with a control write\n");
+      auto controlCont = std::move(m_controlCont);
+      m_controlCont.reset();
+      return controlCont->Send({});
+    }
+    int result = Continue(ws->m_stream, shared_from_this());
+    WS_DEBUG("Continue() -> {}\n", result);
+    if (result <= 0) {
+      m_frames.ReleaseBufs();
+      m_callback(m_userBufs, uv::Error{result});
+      if (result == 0 && m_cont) {
+        WS_DEBUG("Continuing with another write\n");
+        ws->m_curWriteReq = m_cont;
+        return m_cont->Send({});
+      } else {
+        ws->m_writeInProgress = false;
+        ws->m_curWriteReq.reset();
+        ws->m_lastWriteReq.reset();
+      }
+    }
+  }
+
+  std::weak_ptr<WebSocket> m_ws;
   std::function<void(std::span<uv::Buffer>, uv::Error)> m_callback;
-  SmallVector<uv::Buffer, 4> m_internalBufs;
-  SmallVector<uv::Buffer, 4> m_userBufs;
+  std::shared_ptr<WriteReq> m_cont;
+  std::shared_ptr<WriteReq> m_controlCont;
 };
-}  // namespace
 
 static constexpr uint8_t kFlagMasking = 0x80;
 static constexpr uint8_t kLenMask = 0x7f;
+static constexpr size_t kWriteAllocSize = 4096;
 
 class WebSocket::ClientHandshakeData {
  public:
@@ -154,7 +214,7 @@
 
   // Build client request
   SmallVector<uv::Buffer, 4> bufs;
-  raw_uv_ostream os{bufs, 4096};
+  raw_uv_ostream os{bufs, kWriteAllocSize};
 
   os << "GET " << uri << " HTTP/1.1\r\n";
   os << "Host: " << host << "\r\n";
@@ -258,11 +318,12 @@
 
   // 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;
+    if (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;
+    }
   }
 }
 
@@ -272,7 +333,7 @@
 
   // Build server response
   SmallVector<uv::Buffer, 4> bufs;
-  raw_uv_ostream os{bufs, 4096};
+  raw_uv_ostream os{bufs, kWriteAllocSize};
 
   // Handle unsupported version
   if (version != "13") {
@@ -320,13 +381,13 @@
 void WebSocket::SendClose(uint16_t code, std::string_view reason) {
   SmallVector<uv::Buffer, 4> bufs;
   if (code != 1005) {
-    raw_uv_ostream os{bufs, 4096};
+    raw_uv_ostream os{bufs, kWriteAllocSize};
     const uint8_t codeMsb[] = {static_cast<uint8_t>((code >> 8) & 0xff),
                                static_cast<uint8_t>(code & 0xff)};
     os << std::span{codeMsb};
     os << reason;
   }
-  Send(kFlagFin | kOpClose, bufs, [](auto bufs, uv::Error) {
+  SendControl(kFlagFin | kOpClose, bufs, [](auto bufs, uv::Error) {
     for (auto&& buf : bufs) {
       buf.Deallocate();
     }
@@ -345,6 +406,17 @@
   m_stream.Shutdown([this] { m_stream.Close(); });
 }
 
+static inline void Unmask(std::span<uint8_t> data,
+                          std::span<const uint8_t, 4> key) {
+  int n = 0;
+  for (uint8_t& ch : data) {
+    ch ^= key[n++];
+    if (n >= 4) {
+      n = 0;
+    }
+  }
+}
+
 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) {
@@ -445,32 +517,37 @@
         }
 
         // limit maximum size
-        if ((m_payload.size() + m_frameSize) > m_maxMessageSize) {
+        bool control = (m_header[0] & kFlagControl) != 0;
+        if (((control ? m_controlPayload.size() : 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();
+      bool control = (m_header[0] & kFlagControl) != 0;
+      size_t need;
+      if (control) {
+        need = m_frameSize - m_controlPayload.size();
+      } else {
+        need = m_frameStart + m_frameSize - m_payload.size();
+      }
       size_t toCopy = (std::min)(need, data.size());
-      m_payload.append(data.data(), data.data() + toCopy);
+      if (control) {
+        m_controlPayload.append(data.data(), data.data() + toCopy);
+      } else {
+        m_payload.append(data.data(), data.data() + toCopy);
+      }
       data.remove_prefix(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 : std::span{m_payload}.subspan(m_frameStart)) {
-            ch ^= key[n++];
-            if (n >= 4) {
-              n = 0;
-            }
-          }
+          Unmask(control ? std::span{m_controlPayload}
+                         : std::span{m_payload}.subspan(m_frameStart),
+                 std::span<const uint8_t, 4>{&m_header[m_headerSize - 4], 4});
         }
 
         // Handle message
@@ -478,17 +555,23 @@
         uint8_t opcode = m_header[0] & kOpMask;
         switch (opcode) {
           case kOpCont:
+            WS_DEBUG("WS Fragment {} [{}]\n", m_payload.size(),
+                     DebugBinary(m_payload));
             switch (m_fragmentOpcode) {
               case kOpText:
                 if (!m_combineFragments || fin) {
-                  text(std::string_view{reinterpret_cast<char*>(
-                                            m_payload.data()),
-                                        m_payload.size()},
-                       fin);
+                  std::string_view content{
+                      reinterpret_cast<char*>(m_payload.data()),
+                      m_payload.size()};
+                  WS_DEBUG("WS RecvText(Defrag) {} ({})\n", m_payload.size(),
+                           DebugText(content));
+                  text(content, fin);
                 }
                 break;
               case kOpBinary:
                 if (!m_combineFragments || fin) {
+                  WS_DEBUG("WS RecvBinary(Defrag) {} ({})\n", m_payload.size(),
+                           DebugBinary(m_payload));
                   binary(m_payload, fin);
                 }
                 break;
@@ -500,42 +583,38 @@
               m_fragmentOpcode = 0;
             }
             break;
-          case kOpText:
+          case kOpText: {
+            std::string_view content{reinterpret_cast<char*>(m_payload.data()),
+                                     m_payload.size()};
             if (m_fragmentOpcode != 0) {
+              WS_DEBUG("WS RecvText {} ({}) -> INCOMPLETE FRAGMENT\n",
+                       m_payload.size(), DebugText(content));
               return Fail(1002, "incomplete fragment");
             }
             if (!m_combineFragments || fin) {
-#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG
-              fmt::print(
-                  "WS RecvText({})\n",
-                  std::string_view{reinterpret_cast<char*>(m_payload.data()),
-                                   m_payload.size()});
-#endif
-              text(std::string_view{reinterpret_cast<char*>(m_payload.data()),
-                                    m_payload.size()},
-                   fin);
+              WS_DEBUG("WS RecvText {} ({})\n", m_payload.size(),
+                       DebugText(content));
+              text(content, fin);
             }
             if (!fin) {
+              WS_DEBUG("WS RecvText {} StartFrag\n", m_payload.size());
               m_fragmentOpcode = opcode;
             }
             break;
+          }
           case kOpBinary:
             if (m_fragmentOpcode != 0) {
+              WS_DEBUG("WS RecvBinary {} ({}) -> INCOMPLETE FRAGMENT\n",
+                       m_payload.size(), DebugBinary(m_payload));
               return Fail(1002, "incomplete fragment");
             }
             if (!m_combineFragments || fin) {
-#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG
-              SmallString<128> str;
-              raw_svector_ostream stros{str};
-              for (auto ch : m_payload) {
-                stros << fmt::format("{:02x},",
-                                     static_cast<unsigned int>(ch) & 0xff);
-              }
-              fmt::print("WS RecvBinary({})\n", str.str());
-#endif
+              WS_DEBUG("WS RecvBinary {} ({})\n", m_payload.size(),
+                       DebugBinary(m_payload));
               binary(m_payload, fin);
             }
             if (!fin) {
+              WS_DEBUG("WS RecvBinary {} StartFrag\n", m_payload.size());
               m_fragmentOpcode = opcode;
             }
             break;
@@ -545,14 +624,15 @@
             if (!fin) {
               code = 1002;
               reason = "cannot fragment control frames";
-            } else if (m_payload.size() < 2) {
+            } else if (m_controlPayload.size() < 2) {
               code = 1005;
             } else {
-              code = (static_cast<uint16_t>(m_payload[0]) << 8) |
-                     static_cast<uint16_t>(m_payload[1]);
-              reason = drop_front(
-                  {reinterpret_cast<char*>(m_payload.data()), m_payload.size()},
-                  2);
+              code = (static_cast<uint16_t>(m_controlPayload[0]) << 8) |
+                     static_cast<uint16_t>(m_controlPayload[1]);
+              reason =
+                  drop_front({reinterpret_cast<char*>(m_controlPayload.data()),
+                              m_controlPayload.size()},
+                             2);
             }
             // Echo the close if we didn't previously send it
             if (m_state != CLOSING) {
@@ -569,13 +649,30 @@
             if (!fin) {
               return Fail(1002, "cannot fragment control frames");
             }
-            ping(m_payload);
+            // If the connection is open, send a Pong in response
+            if (m_state == OPEN) {
+              SmallVector<uv::Buffer, 4> bufs;
+              {
+                raw_uv_ostream os{bufs, kWriteAllocSize};
+                os << m_controlPayload;
+              }
+              SendPong(bufs, [](auto bufs, uv::Error) {
+                for (auto&& buf : bufs) {
+                  buf.Deallocate();
+                }
+              });
+            }
+            WS_DEBUG("WS RecvPing() {} ({})\n", m_controlPayload.size(),
+                     DebugBinary(m_controlPayload));
+            ping(m_controlPayload);
             break;
           case kOpPong:
             if (!fin) {
               return Fail(1002, "cannot fragment control frames");
             }
-            pong(m_payload);
+            WS_DEBUG("WS RecvPong() {} ({})\n", m_controlPayload.size(),
+                     DebugBinary(m_controlPayload));
+            pong(m_controlPayload);
             break;
           default:
             return Fail(1002, "invalid message opcode");
@@ -585,7 +682,11 @@
         m_header.clear();
         m_headerSize = 0;
         if (!m_combineFragments || fin) {
-          m_payload.clear();
+          if (control) {
+            m_controlPayload.clear();
+          } else {
+            m_payload.clear();
+          }
         }
         m_frameStart = m_payload.size();
         m_frameSize = UINT64_MAX;
@@ -594,114 +695,157 @@
   }
 }
 
-static void WriteFrame(WebSocketWriteReq& req,
-                       SmallVectorImpl<uv::Buffer>& bufs, bool server,
-                       uint8_t opcode, std::span<const uv::Buffer> data) {
-  SmallVector<uv::Buffer, 4> internalBufs;
-  raw_uv_ostream os{internalBufs, 4096};
-
+static void VerboseDebug(const WebSocket::Frame& frame) {
 #ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG
-  if ((opcode & 0x7f) == 0x01) {
+  if ((frame.opcode & 0x7f) == 0x01) {
     SmallString<128> str;
-    for (auto&& d : data) {
+#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG_CONTENT
+    for (auto&& d : frame.data) {
       str.append(std::string_view(d.base, d.len));
     }
+#endif
     fmt::print("WS SendText({})\n", str.str());
-  } else if ((opcode & 0x7f) == 0x02) {
+  } else if ((frame.opcode & 0x7f) == 0x02) {
     SmallString<128> str;
+#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG_CONTENT
     raw_svector_ostream stros{str};
-    for (auto&& d : data) {
+    for (auto&& d : frame.data) {
       for (auto ch : d.data()) {
         stros << fmt::format("{:02x},", static_cast<unsigned int>(ch) & 0xff);
       }
     }
-    fmt::print("WS SendBinary({})\n", str.str());
-  }
 #endif
-
-  // 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>((server ? 0x00 : kFlagMasking) | size);
-  } else if (size <= 0xffff) {
-    os << static_cast<unsigned char>((server ? 0x00 : kFlagMasking) | 126);
-    const uint8_t sizeMsb[] = {static_cast<uint8_t>((size >> 8) & 0xff),
-                               static_cast<uint8_t>(size & 0xff)};
-    os << std::span{sizeMsb};
+    fmt::print("WS SendBinary({})\n", str.str());
   } else {
-    os << static_cast<unsigned char>((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 << std::span{sizeMsb};
-  }
-
-  // clients need to mask the input data
-  if (!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 << std::span<const 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;
-        }
+    SmallString<128> str;
+#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG_CONTENT
+    raw_svector_ostream stros{str};
+    for (auto&& d : frame.data) {
+      for (auto ch : d.data()) {
+        stros << fmt::format("{:02x},", static_cast<unsigned int>(ch) & 0xff);
       }
     }
-    bufs.append(internalBufs.begin(), internalBufs.end());
-    // don't send the user bufs as we copied their data
-  } else {
-    bufs.append(internalBufs.begin(), internalBufs.end());
-    // servers can just send the buffers directly without masking
-    bufs.append(data.begin(), data.end());
+#endif
+    fmt::print("WS SendOp({}, {})\n", frame.opcode, str.str());
   }
-  req.m_internalBufs.append(internalBufs.begin(), internalBufs.end());
-  req.m_userBufs.append(data.begin(), data.end());
+#endif
 }
 
 void WebSocket::SendFrames(
     std::span<const Frame> frames,
     std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
   // If we're not open, emit an error and don't send the data
+  WS_DEBUG("SendFrames({})\n", frames.size());
   if (m_state != OPEN) {
-    int err;
-    if (m_state == CONNECTING) {
-      err = UV_EAGAIN;
-    } else {
-      err = UV_ESHUTDOWN;
-    }
-    SmallVector<uv::Buffer, 4> bufs;
-    for (auto&& frame : frames) {
-      bufs.append(frame.data.begin(), frame.data.end());
-    }
-    callback(bufs, uv::Error{err});
+    SendError(frames, callback);
     return;
   }
 
-  auto req = std::make_shared<WebSocketWriteReq>(std::move(callback));
+  // Build request
+  auto req = std::make_shared<WriteReq>(weak_from_this(), std::move(callback));
+  int numBytes = 0;
+  for (auto&& frame : frames) {
+    VerboseDebug(frame);
+    numBytes += req->m_frames.AddFrame(frame, m_server);
+    req->m_continueFrameOffs.emplace_back(numBytes);
+    req->m_userBufs.append(frame.data.begin(), frame.data.end());
+  }
+
+  if (m_writeInProgress) {
+    if (auto lastReq = m_lastWriteReq.lock()) {
+      // if write currently in progress, process as a continuation of that
+      m_lastWriteReq = req;
+      // make sure we're really at the end
+      while (lastReq->m_cont) {
+        lastReq = lastReq->m_cont;
+      }
+      lastReq->m_cont = std::move(req);
+      return;
+    }
+  }
+
+  m_writeInProgress = true;
+  m_curWriteReq = req;
+  m_lastWriteReq = req;
+  req->Send({});
+}
+
+std::span<const WebSocket::Frame> WebSocket::TrySendFrames(
+    std::span<const Frame> frames,
+    std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+  // If we're not open, emit an error and don't send the data
+  if (m_state != WebSocket::OPEN) {
+    SendError(frames, callback);
+    return {};
+  }
+
+  // If something else is still in flight, don't send anything
+  if (m_writeInProgress) {
+    return frames;
+  }
+
+  return detail::TrySendFrames(
+      m_server, m_stream, frames,
+      [this](std::function<void(std::span<uv::Buffer>, uv::Error)>&& cb) {
+        auto req = std::make_shared<WriteReq>(weak_from_this(), std::move(cb));
+        m_writeInProgress = true;
+        m_curWriteReq = req;
+        m_lastWriteReq = req;
+        return req;
+      },
+      std::move(callback));
+}
+
+void WebSocket::SendControl(
+    uint8_t opcode, std::span<const uv::Buffer> data,
+    std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+  Frame frame{opcode, data};
+  // If we're not open, emit an error and don't send the data
+  if (m_state != WebSocket::OPEN) {
+    SendError({{frame}}, callback);
+    return;
+  }
+
+  // If nothing else is in flight, just use SendFrames()
+  std::shared_ptr<WriteReq> curReq = m_curWriteReq.lock();
+  if (!m_writeInProgress || !curReq) {
+    return SendFrames({{frame}}, std::move(callback));
+  }
+
+  // There's a write request in flight, but since this is a control frame, we
+  // want to send it as soon as we can, without waiting for all frames in that
+  // request (or any continuations) to be sent.
+  auto req = std::make_shared<WriteReq>(weak_from_this(), std::move(callback));
+  VerboseDebug(frame);
+  size_t numBytes = req->m_frames.AddFrame(frame, m_server);
+  req->m_userBufs.append(frame.data.begin(), frame.data.end());
+  req->m_continueFrameOffs.emplace_back(numBytes);
+  req->m_cont = curReq;
+  // There may be multiple control packets in flight; maintain in-order
+  // transmission. Linear search here is O(n^2), but should be pretty rare.
+  if (!curReq->m_controlCont) {
+    curReq->m_controlCont = std::move(req);
+  } else {
+    curReq = curReq->m_controlCont;
+    while (curReq->m_cont != req->m_cont) {
+      curReq = curReq->m_cont;
+    }
+    curReq->m_cont = std::move(req);
+  }
+}
+
+void WebSocket::SendError(
+    std::span<const Frame> frames,
+    const std::function<void(std::span<uv::Buffer>, uv::Error)>& callback) {
+  int err;
+  if (m_state == WebSocket::CONNECTING) {
+    err = UV_EAGAIN;
+  } else {
+    err = UV_ESHUTDOWN;
+  }
   SmallVector<uv::Buffer, 4> bufs;
   for (auto&& frame : frames) {
-    WriteFrame(*req, bufs, m_server, frame.opcode, frame.data);
+    bufs.append(frame.data.begin(), frame.data.end());
   }
-  m_stream.Write(bufs, req);
+  callback(bufs, uv::Error{err});
 }
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocketDebug.h b/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocketDebug.h
new file mode 100644
index 0000000..5653b5f
--- /dev/null
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocketDebug.h
@@ -0,0 +1,21 @@
+// 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 <fmt/format.h>
+
+// #define WPINET_WEBSOCKET_VERBOSE_DEBUG
+// #define WPINET_WEBSOCKET_VERBOSE_DEBUG_CONTENT
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG
+#define WS_DEBUG(format, ...) \
+  ::fmt::print(FMT_STRING(format) __VA_OPT__(, ) __VA_ARGS__)
+#else
+#define WS_DEBUG(fmt, ...)
+#endif
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocketSerializer.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocketSerializer.cpp
new file mode 100644
index 0000000..c5d9548
--- /dev/null
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocketSerializer.cpp
@@ -0,0 +1,108 @@
+// 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 "WebSocketSerializer.h"
+
+#include <random>
+
+using namespace wpi::detail;
+
+static constexpr uint8_t kFlagMasking = 0x80;
+static constexpr size_t kWriteAllocSize = 4096;
+
+static std::span<uint8_t> BuildHeader(std::span<uint8_t, 10> header,
+                                      bool server,
+                                      const wpi::WebSocket::Frame& frame) {
+  uint8_t* pHeader = header.data();
+
+  // opcode (includes FIN bit)
+  *pHeader++ = frame.opcode;
+
+  // payload length
+  uint64_t size = 0;
+  for (auto&& buf : frame.data) {
+    size += buf.len;
+  }
+  if (size < 126) {
+    *pHeader++ = (server ? 0x00 : kFlagMasking) | size;
+  } else if (size <= 0xffff) {
+    *pHeader++ = (server ? 0x00 : kFlagMasking) | 126;
+    *pHeader++ = (size >> 8) & 0xff;
+    *pHeader++ = size & 0xff;
+  } else {
+    *pHeader++ = (server ? 0x00 : kFlagMasking) | 127;
+    *pHeader++ = (size >> 56) & 0xff;
+    *pHeader++ = (size >> 48) & 0xff;
+    *pHeader++ = (size >> 40) & 0xff;
+    *pHeader++ = (size >> 32) & 0xff;
+    *pHeader++ = (size >> 24) & 0xff;
+    *pHeader++ = (size >> 16) & 0xff;
+    *pHeader++ = (size >> 8) & 0xff;
+    *pHeader++ = size & 0xff;
+  }
+  return header.subspan(0, pHeader - header.data());
+}
+
+size_t SerializedFrames::AddClientFrame(const WebSocket::Frame& frame) {
+  uint8_t headerBuf[10];
+  auto header = BuildHeader(headerBuf, false, frame);
+
+  // allocate a buffer per frame
+  size_t size = header.size() + 4;
+  for (auto&& buf : frame.data) {
+    size += buf.len;
+  }
+  m_allocBufs.emplace_back(uv::Buffer::Allocate(size));
+  m_bufs.emplace_back(m_allocBufs.back());
+
+  char* internalBuf = m_allocBufs.back().data().data();
+  std::memcpy(internalBuf, header.data(), header.size());
+  internalBuf += header.size();
+
+  // 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);
+  }
+  std::memcpy(internalBuf, key, 4);
+  internalBuf += 4;
+
+  // copy and mask data
+  int n = 0;
+  for (auto&& buf : frame.data) {
+    for (auto&& ch : buf.data()) {
+      *internalBuf++ = static_cast<uint8_t>(ch) ^ key[n++];
+      if (n >= 4) {
+        n = 0;
+      }
+    }
+  }
+  return size;
+}
+
+size_t SerializedFrames::AddServerFrame(const WebSocket::Frame& frame) {
+  uint8_t headerBuf[10];
+  auto header = BuildHeader(headerBuf, true, frame);
+
+  // manage allocBufs to efficiently store header
+  if (m_allocBufs.empty() ||
+      (m_allocBufPos + header.size()) > kWriteAllocSize) {
+    m_allocBufs.emplace_back(uv::Buffer::Allocate(kWriteAllocSize));
+    m_allocBufPos = 0;
+  }
+  char* internalBuf = m_allocBufs.back().data().data() + m_allocBufPos;
+  std::memcpy(internalBuf, header.data(), header.size());
+  m_bufs.emplace_back(internalBuf, header.size());
+  m_allocBufPos += header.size();
+  // servers can just send the buffers directly without masking
+  m_bufs.append(frame.data.begin(), frame.data.end());
+  size_t sent = header.size();
+  for (auto&& buf : frame.data) {
+    sent += buf.len;
+  }
+  return sent;
+}
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocketSerializer.h b/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocketSerializer.h
new file mode 100644
index 0000000..264b8f5
--- /dev/null
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/WebSocketSerializer.h
@@ -0,0 +1,304 @@
+// 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 <functional>
+#include <memory>
+#include <utility>
+
+#include <wpi/SmallVector.h>
+#include <wpi/SpanExtras.h>
+
+#include "WebSocketDebug.h"
+#include "wpinet/WebSocket.h"
+#include "wpinet/uv/Buffer.h"
+
+namespace wpi::detail {
+
+class SerializedFrames {
+ public:
+  SerializedFrames() = default;
+  SerializedFrames(const SerializedFrames&) = delete;
+  SerializedFrames& operator=(const SerializedFrames&) = delete;
+  ~SerializedFrames() { ReleaseBufs(); }
+
+  size_t AddFrame(const WebSocket::Frame& frame, bool server) {
+    if (server) {
+      return AddServerFrame(frame);
+    } else {
+      return AddClientFrame(frame);
+    }
+  }
+
+  size_t AddClientFrame(const WebSocket::Frame& frame);
+  size_t AddServerFrame(const WebSocket::Frame& frame);
+
+  void ReleaseBufs() {
+    for (auto&& buf : m_allocBufs) {
+      buf.Deallocate();
+    }
+    m_allocBufs.clear();
+  }
+
+  SmallVector<uv::Buffer, 4> m_allocBufs;
+  SmallVector<uv::Buffer, 4> m_bufs;
+  size_t m_allocBufPos = 0;
+};
+
+class WebSocketWriteReqBase {
+ public:
+  template <typename Stream, typename Req>
+  int Continue(Stream& stream, std::shared_ptr<Req> req);
+
+  SmallVector<uv::Buffer, 4> m_userBufs;
+  SerializedFrames m_frames;
+  SmallVector<int, 0> m_continueFrameOffs;
+  size_t m_continueBufPos = 0;
+  size_t m_continueFramePos = 0;
+};
+
+template <typename Stream, typename Req>
+int WebSocketWriteReqBase::Continue(Stream& stream, std::shared_ptr<Req> req) {
+  if (m_continueBufPos >= m_frames.m_bufs.size()) {
+    return 0;  // nothing more to send
+  }
+
+  // try writing everything remaining
+  std::span bufs = std::span{m_frames.m_bufs}.subspan(m_continueBufPos);
+  int numBytes = 0;
+  for (auto&& buf : bufs) {
+    numBytes += buf.len;
+  }
+
+  int sentBytes = stream.TryWrite(bufs);
+  WS_DEBUG("TryWrite({}) -> {} (expected {})\n", bufs.size(), sentBytes,
+           numBytes);
+  if (sentBytes < 0) {
+    return sentBytes;  // error
+  }
+
+  if (sentBytes == numBytes) {
+    m_continueBufPos = m_frames.m_bufs.size();
+    return 0;  // nothing more to send
+  }
+
+  // we didn't send everything; deal with the leftovers
+
+  // figure out what the last (partially) frame sent actually was
+  auto offIt = m_continueFrameOffs.begin() + m_continueFramePos;
+  auto offEnd = m_continueFrameOffs.end();
+  while (offIt != offEnd && *offIt < sentBytes) {
+    ++offIt;
+  }
+  assert(offIt != offEnd);
+
+  // build a list of buffers to send as a normal write:
+  SmallVector<uv::Buffer, 4> writeBufs;
+  auto bufIt = bufs.begin();
+  auto bufEnd = bufs.end();
+
+  // start with the remaining portion of the last buffer actually sent
+  int pos = 0;
+  while (bufIt != bufEnd && pos < sentBytes) {
+    pos += (bufIt++)->len;
+  }
+  if (bufIt != bufs.begin() && pos != sentBytes) {
+    writeBufs.emplace_back(
+        wpi::take_back((bufIt - 1)->bytes(), pos - sentBytes));
+  }
+
+  // continue through the last buffer of the last partial frame
+  while (bufIt != bufEnd && offIt != offEnd && pos < *offIt) {
+    pos += bufIt->len;
+    writeBufs.emplace_back(*bufIt++);
+  }
+  if (offIt != offEnd) {
+    ++offIt;
+  }
+
+  // if writeBufs is still empty, write all of the next frame
+  if (writeBufs.empty()) {
+    while (bufIt != bufEnd && offIt != offEnd && pos < *offIt) {
+      pos += bufIt->len;
+      writeBufs.emplace_back(*bufIt++);
+    }
+    if (offIt != offEnd) {
+      ++offIt;
+    }
+  }
+
+  m_continueFramePos = offIt - m_continueFrameOffs.begin();
+  m_continueBufPos += bufIt - bufs.begin();
+
+  if (writeBufs.empty()) {
+    WS_DEBUG("Write Done\n");
+    return 0;
+  }
+  WS_DEBUG("Write({})\n", writeBufs.size());
+  stream.Write(writeBufs, req);
+  return 1;
+}
+
+template <typename MakeReq, typename Stream>
+std::span<const WebSocket::Frame> TrySendFrames(
+    bool server, Stream& stream, std::span<const WebSocket::Frame> frames,
+    MakeReq&& makeReq,
+    std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
+  WS_DEBUG("TrySendFrames({})\n", frames.size());
+  auto frameIt = frames.begin();
+  auto frameEnd = frames.end();
+  while (frameIt != frameEnd) {
+    auto frameStart = frameIt;
+
+    // build buffers to send
+    SerializedFrames sendFrames;
+    SmallVector<int, 32> frameOffs;
+    int numBytes = 0;
+    while (frameIt != frameEnd) {
+      frameOffs.emplace_back(numBytes);
+      numBytes += sendFrames.AddFrame(*frameIt++, server);
+      if ((server && (numBytes >= 65536 || frameOffs.size() > 32)) ||
+          (!server && numBytes >= 8192)) {
+        // don't waste too much memory or effort on header generation or masking
+        break;
+      }
+    }
+
+    // try to send
+    int sentBytes = stream.TryWrite(sendFrames.m_bufs);
+    WS_DEBUG("TryWrite({}) -> {} (expected {})\n", sendFrames.m_bufs.size(),
+             sentBytes, numBytes);
+
+    if (sentBytes == 0) {
+      // we haven't started a frame yet; clean up any bufs that have actually
+      // sent, and return unsent frames
+      SmallVector<uv::Buffer, 4> bufs;
+      for (auto it = frames.begin(); it != frameStart; ++it) {
+        bufs.append(it->data.begin(), it->data.end());
+      }
+      callback(bufs, {});
+#ifdef __clang__
+      // work around clang bug
+      return {frames.data() + (frameStart - frames.begin()),
+              frames.data() + (frameEnd - frames.begin())};
+#else
+      return {frameStart, frameEnd};
+#endif
+    } else if (sentBytes < 0) {
+      // error
+      SmallVector<uv::Buffer, 4> bufs;
+      for (auto&& frame : frames) {
+        bufs.append(frame.data.begin(), frame.data.end());
+      }
+      callback(bufs, uv::Error{sentBytes});
+      return frames;
+    } else if (sentBytes != numBytes) {
+      // we didn't send everything; deal with the leftovers
+
+      // figure out what the last (partially) frame sent actually was
+      auto offIt = frameOffs.begin();
+      auto offEnd = frameOffs.end();
+      bool isFin = true;
+      while (offIt != offEnd && *offIt < sentBytes) {
+        ++offIt;
+        isFin = (frameStart->opcode & WebSocket::kFlagFin) != 0;
+        ++frameStart;
+      }
+
+      if (offIt != offEnd && *offIt == sentBytes && isFin) {
+        // we finished at a normal FIN frame boundary; no need for a Write()
+        SmallVector<uv::Buffer, 4> bufs;
+        for (auto it = frames.begin(); it != frameStart; ++it) {
+          bufs.append(it->data.begin(), it->data.end());
+        }
+        callback(bufs, {});
+#ifdef __clang__
+        // work around clang bug
+        return {frames.data() + (frameStart - frames.begin()),
+                frames.data() + (frameEnd - frames.begin())};
+#else
+        return {frameStart, frameEnd};
+#endif
+      }
+
+      // build a list of buffers to send as a normal write:
+      SmallVector<uv::Buffer, 4> writeBufs;
+      auto bufIt = sendFrames.m_bufs.begin();
+      auto bufEnd = sendFrames.m_bufs.end();
+
+      // start with the remaining portion of the last buffer actually sent
+      int pos = 0;
+      while (bufIt != bufEnd && pos < sentBytes) {
+        pos += (bufIt++)->len;
+      }
+      if (bufIt != sendFrames.m_bufs.begin() && pos != sentBytes) {
+        writeBufs.emplace_back(
+            wpi::take_back((bufIt - 1)->bytes(), pos - sentBytes));
+      }
+
+      // continue through the last buffer of the last partial frame
+      while (bufIt != bufEnd && offIt != offEnd && pos < *offIt) {
+        pos += bufIt->len;
+        writeBufs.emplace_back(*bufIt++);
+      }
+      if (offIt != offEnd) {
+        ++offIt;
+      }
+
+      // move allocated buffers into request
+      auto req = makeReq(std::move(callback));
+      req->m_frames.m_allocBufs = std::move(sendFrames.m_allocBufs);
+      req->m_frames.m_allocBufPos = sendFrames.m_allocBufPos;
+
+      // if partial frame was non-FIN, put any additional non-FIN frames into
+      // continuation (so the caller isn't responsible for doing this)
+      size_t continuePos = 0;
+      while (frameStart != frameEnd && !isFin) {
+        if (offIt != offEnd) {
+          // we already generated the wire buffers for this frame, use them
+          while (pos < *offIt && bufIt != bufEnd) {
+            pos += bufIt->len;
+            continuePos += bufIt->len;
+            req->m_frames.m_bufs.emplace_back(*bufIt++);
+          }
+          ++offIt;
+        } else {
+          // WS_DEBUG("generating frame for continuation {} {}\n",
+          //          frameStart->opcode, frameStart->data.size());
+          // need to generate and add this frame
+          continuePos += req->m_frames.AddFrame(*frameStart, server);
+        }
+        req->m_continueFrameOffs.emplace_back(continuePos);
+        isFin = (frameStart->opcode & WebSocket::kFlagFin) != 0;
+        ++frameStart;
+      }
+
+      // only the non-returned user buffers are added to the request
+      for (auto it = frames.begin(); it != frameStart; ++it) {
+        req->m_userBufs.append(it->data.begin(), it->data.end());
+      }
+
+      WS_DEBUG("Write({})\n", writeBufs.size());
+      stream.Write(writeBufs, req);
+#ifdef __clang__
+      // work around clang bug
+      return {frames.data() + (frameStart - frames.begin()),
+              frames.data() + (frameEnd - frames.begin())};
+#else
+      return {frameStart, frameEnd};
+#endif
+    }
+  }
+
+  // nothing left to send
+  SmallVector<uv::Buffer, 4> bufs;
+  for (auto&& frame : frames) {
+    bufs.append(frame.data.begin(), frame.data.end());
+  }
+  callback(bufs, {});
+  return {};
+}
+
+}  // namespace wpi::detail
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/http_parser.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/http_parser.cpp
index 2bec4a7..66fff6a 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/http_parser.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/http_parser.cpp
@@ -1859,7 +1859,7 @@
             && 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.
+         * the latter will automatically 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.
          */
@@ -2347,7 +2347,7 @@
       case s_dead:
         return 1;
 
-      /* Skip delimeters */
+      /* Skip delimiters */
       case s_req_schema_slash:
       case s_req_schema_slash_slash:
       case s_req_server_start:
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Async.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Async.cpp
index f84bb9b..58ef5f3 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Async.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Async.cpp
@@ -17,6 +17,9 @@
 }
 
 std::shared_ptr<Async<>> Async<>::Create(const std::shared_ptr<Loop>& loop) {
+  if (loop->IsClosing()) {
+    return nullptr;
+  }
   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);
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Check.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Check.cpp
index 13c2229..75ff47c 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Check.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Check.cpp
@@ -9,6 +9,9 @@
 namespace wpi::uv {
 
 std::shared_ptr<Check> Check::Create(Loop& loop) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Check>(private_init{});
   int err = uv_check_init(loop.GetRaw(), h->GetRaw());
   if (err < 0) {
@@ -20,6 +23,9 @@
 }
 
 void Check::Start() {
+  if (IsLoopClosing()) {
+    return;
+  }
   Invoke(&uv_check_start, GetRaw(), [](uv_check_t* handle) {
     Check& h = *static_cast<Check*>(handle->data);
     h.check();
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/FsEvent.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/FsEvent.cpp
index 044390e..d77bf37 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/FsEvent.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/FsEvent.cpp
@@ -13,6 +13,9 @@
 namespace wpi::uv {
 
 std::shared_ptr<FsEvent> FsEvent::Create(Loop& loop) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<FsEvent>(private_init{});
   int err = uv_fs_event_init(loop.GetRaw(), h->GetRaw());
   if (err < 0) {
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/GetAddrInfo.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/GetAddrInfo.cpp
index 14721f2..c3ec000 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/GetAddrInfo.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/GetAddrInfo.cpp
@@ -18,6 +18,9 @@
 void GetAddrInfo(Loop& loop, const std::shared_ptr<GetAddrInfoReq>& req,
                  std::string_view node, std::string_view service,
                  const addrinfo* hints) {
+  if (loop.IsClosing()) {
+    return;
+  }
   SmallString<128> nodeStr{node};
   SmallString<128> serviceStr{service};
   int err = uv_getaddrinfo(
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/GetNameInfo.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/GetNameInfo.cpp
index a6ad36d..9720cc3 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/GetNameInfo.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/GetNameInfo.cpp
@@ -15,6 +15,9 @@
 
 void GetNameInfo(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
                  const sockaddr& addr, int flags) {
+  if (loop.IsClosing()) {
+    return;
+  }
   int err = uv_getnameinfo(
       loop.GetRaw(), req->GetRaw(),
       [](uv_getnameinfo_t* req, int status, const char* hostname,
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Idle.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Idle.cpp
index 452bc7e..7b94b3f 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Idle.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Idle.cpp
@@ -9,6 +9,9 @@
 namespace wpi::uv {
 
 std::shared_ptr<Idle> Idle::Create(Loop& loop) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Idle>(private_init{});
   int err = uv_idle_init(loop.GetRaw(), h->GetRaw());
   if (err < 0) {
@@ -20,6 +23,9 @@
 }
 
 void Idle::Start() {
+  if (IsLoopClosing()) {
+    return;
+  }
   Invoke(&uv_idle_start, GetRaw(), [](uv_idle_t* handle) {
     Idle& h = *static_cast<Idle*>(handle->data);
     h.idle();
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/NetworkStream.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/NetworkStream.cpp
index 3538596..12750b2 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/NetworkStream.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/NetworkStream.cpp
@@ -11,6 +11,9 @@
 }
 
 void NetworkStream::Listen(int backlog) {
+  if (IsLoopClosing()) {
+    return;
+  }
   Invoke(&uv_listen, GetRawStream(), backlog,
          [](uv_stream_t* handle, int status) {
            auto& h = *static_cast<NetworkStream*>(handle->data);
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Pipe.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Pipe.cpp
index 9548874..7993604 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Pipe.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Pipe.cpp
@@ -11,6 +11,9 @@
 namespace wpi::uv {
 
 std::shared_ptr<Pipe> Pipe::Create(Loop& loop, bool ipc) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Pipe>(private_init{});
   int err = uv_pipe_init(loop.GetRaw(), h->GetRaw(), ipc ? 1 : 0);
   if (err < 0) {
@@ -22,7 +25,7 @@
 }
 
 void Pipe::Reuse(std::function<void()> callback, bool ipc) {
-  if (IsClosing()) {
+  if (IsLoopClosing() || IsClosing()) {
     return;
   }
   if (!m_reuseData) {
@@ -69,6 +72,9 @@
 
 void Pipe::Connect(std::string_view name,
                    const std::shared_ptr<PipeConnectReq>& req) {
+  if (IsLoopClosing()) {
+    return;
+  }
   SmallString<128> nameBuf{name};
   uv_pipe_connect(req->GetRaw(), GetRaw(), nameBuf.c_str(),
                   [](uv_connect_t* req, int status) {
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Poll.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Poll.cpp
index 3713453..7d35615 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Poll.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Poll.cpp
@@ -9,6 +9,9 @@
 namespace wpi::uv {
 
 std::shared_ptr<Poll> Poll::Create(Loop& loop, int fd) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Poll>(private_init{});
   int err = uv_poll_init(loop.GetRaw(), h->GetRaw(), fd);
   if (err < 0) {
@@ -20,6 +23,9 @@
 }
 
 std::shared_ptr<Poll> Poll::CreateSocket(Loop& loop, uv_os_sock_t sock) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Poll>(private_init{});
   int err = uv_poll_init_socket(loop.GetRaw(), h->GetRaw(), sock);
   if (err < 0) {
@@ -31,7 +37,7 @@
 }
 
 void Poll::Reuse(int fd, std::function<void()> callback) {
-  if (IsClosing()) {
+  if (IsLoopClosing() || IsClosing()) {
     return;
   }
   if (!m_reuseData) {
@@ -56,7 +62,7 @@
 }
 
 void Poll::ReuseSocket(uv_os_sock_t sock, std::function<void()> callback) {
-  if (IsClosing()) {
+  if (IsLoopClosing() || IsClosing()) {
     return;
   }
   if (!m_reuseData) {
@@ -81,6 +87,9 @@
 }
 
 void Poll::Start(int events) {
+  if (IsLoopClosing()) {
+    return;
+  }
   Invoke(&uv_poll_start, GetRaw(), events,
          [](uv_poll_t* handle, int status, int events) {
            Poll& h = *static_cast<Poll*>(handle->data);
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Prepare.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Prepare.cpp
index e4ca160..aa1a89d 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Prepare.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Prepare.cpp
@@ -9,6 +9,9 @@
 namespace wpi::uv {
 
 std::shared_ptr<Prepare> Prepare::Create(Loop& loop) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Prepare>(private_init{});
   int err = uv_prepare_init(loop.GetRaw(), h->GetRaw());
   if (err < 0) {
@@ -20,6 +23,9 @@
 }
 
 void Prepare::Start() {
+  if (IsLoopClosing()) {
+    return;
+  }
   Invoke(&uv_prepare_start, GetRaw(), [](uv_prepare_t* handle) {
     Prepare& h = *static_cast<Prepare*>(handle->data);
     h.prepare();
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Process.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Process.cpp
index 3c10db6..c872ff9 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Process.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Process.cpp
@@ -13,6 +13,10 @@
 
 std::shared_ptr<Process> Process::SpawnArray(Loop& loop, std::string_view file,
                                              std::span<const Option> options) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
+
   // convert Option array to libuv structure
   uv_process_options_t coptions;
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Signal.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Signal.cpp
index 10dd7b4..8f998e2 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Signal.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Signal.cpp
@@ -9,6 +9,9 @@
 namespace wpi::uv {
 
 std::shared_ptr<Signal> Signal::Create(Loop& loop) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Signal>(private_init{});
   int err = uv_signal_init(loop.GetRaw(), h->GetRaw());
   if (err < 0) {
@@ -20,6 +23,9 @@
 }
 
 void Signal::Start(int signum) {
+  if (IsLoopClosing()) {
+    return;
+  }
   Invoke(
       &uv_signal_start, GetRaw(),
       [](uv_signal_t* handle, int signum) {
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Stream.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Stream.cpp
index e7f6031..e054003 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Stream.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Stream.cpp
@@ -35,6 +35,9 @@
 }
 
 void Stream::Shutdown(const std::shared_ptr<ShutdownReq>& req) {
+  if (IsLoopClosing()) {
+    return;
+  }
   if (Invoke(&uv_shutdown, req->GetRaw(), GetRawStream(),
              [](uv_shutdown_t* req, int status) {
                auto& h = *static_cast<ShutdownReq*>(req->data);
@@ -50,6 +53,9 @@
 }
 
 void Stream::Shutdown(std::function<void()> callback) {
+  if (IsLoopClosing()) {
+    return;
+  }
   auto req = std::make_shared<ShutdownReq>();
   if (callback) {
     req->complete.connect(std::move(callback));
@@ -58,6 +64,9 @@
 }
 
 void Stream::StartRead() {
+  if (IsLoopClosing()) {
+    return;
+  }
   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);
@@ -79,14 +88,17 @@
 
 void Stream::Write(std::span<const Buffer> bufs,
                    const std::shared_ptr<WriteReq>& req) {
+  if (IsLoopClosing()) {
+    return;
+  }
   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);
                }
+               auto ptr = h.Release();  // one-shot, but finish() may Keep()
                h.finish(Error(status));
-               h.Release();  // this is always a one-shot
              })) {
     req->Keep();
   }
@@ -98,20 +110,32 @@
 }
 
 int Stream::TryWrite(std::span<const Buffer> bufs) {
+  if (IsLoopClosing()) {
+    return UV_ECANCELED;
+  }
   int val = uv_try_write(GetRawStream(), bufs.data(), bufs.size());
+  if (val == UV_EAGAIN) {
+    return 0;
+  }
   if (val < 0) {
     this->ReportError(val);
-    return 0;
+    return val;
   }
   return val;
 }
 
 int Stream::TryWrite2(std::span<const Buffer> bufs, Stream& send) {
+  if (IsLoopClosing()) {
+    return UV_ECANCELED;
+  }
   int val = uv_try_write2(GetRawStream(), bufs.data(), bufs.size(),
                           send.GetRawStream());
+  if (val == UV_EAGAIN) {
+    return 0;
+  }
   if (val < 0) {
     this->ReportError(val);
-    return 0;
+    return val;
   }
   return val;
 }
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Tcp.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Tcp.cpp
index ae01683..b163a0e 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Tcp.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Tcp.cpp
@@ -11,6 +11,9 @@
 namespace wpi::uv {
 
 std::shared_ptr<Tcp> Tcp::Create(Loop& loop, unsigned int flags) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Tcp>(private_init{});
   int err = uv_tcp_init_ex(loop.GetRaw(), h->GetRaw(), flags);
   if (err < 0) {
@@ -22,7 +25,7 @@
 }
 
 void Tcp::Reuse(std::function<void()> callback, unsigned int flags) {
-  if (IsClosing()) {
+  if (IsLoopClosing() || IsClosing()) {
     return;
   }
   if (!m_reuseData) {
@@ -103,6 +106,9 @@
 
 void Tcp::Connect(const sockaddr& addr,
                   const std::shared_ptr<TcpConnectReq>& req) {
+  if (IsLoopClosing()) {
+    return;
+  }
   if (Invoke(&uv_tcp_connect, req->GetRaw(), GetRaw(), &addr,
              [](uv_connect_t* req, int status) {
                auto& h = *static_cast<TcpConnectReq*>(req->data);
@@ -118,6 +124,9 @@
 }
 
 void Tcp::Connect(const sockaddr& addr, std::function<void()> callback) {
+  if (IsLoopClosing()) {
+    return;
+  }
   auto req = std::make_shared<TcpConnectReq>();
   req->connected.connect(std::move(callback));
   Connect(addr, req);
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Timer.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Timer.cpp
index 9d52173..e9b33fc 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Timer.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Timer.cpp
@@ -9,6 +9,9 @@
 namespace wpi::uv {
 
 std::shared_ptr<Timer> Timer::Create(Loop& loop) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Timer>(private_init{});
   int err = uv_timer_init(loop.GetRaw(), h->GetRaw());
   if (err < 0) {
@@ -32,6 +35,9 @@
 }
 
 void Timer::Start(Time timeout, Time repeat) {
+  if (IsLoopClosing()) {
+    return;
+  }
   Invoke(
       &uv_timer_start, GetRaw(),
       [](uv_timer_t* handle) {
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Tty.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Tty.cpp
index 6043a93..5e5756c 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Tty.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Tty.cpp
@@ -9,6 +9,9 @@
 namespace wpi::uv {
 
 std::shared_ptr<Tty> Tty::Create(Loop& loop, uv_file fd, bool readable) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Tty>(private_init{});
   int err = uv_tty_init(loop.GetRaw(), h->GetRaw(), fd, readable ? 1 : 0);
   if (err < 0) {
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Udp.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Udp.cpp
index 689d5a7..1922c57 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Udp.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Udp.cpp
@@ -38,6 +38,9 @@
 }
 
 std::shared_ptr<Udp> Udp::Create(Loop& loop, unsigned int flags) {
+  if (loop.IsClosing()) {
+    return nullptr;
+  }
   auto h = std::make_shared<Udp>(private_init{});
   int err = uv_udp_init_ex(loop.GetRaw(), h->GetRaw(), flags);
   if (err < 0) {
@@ -135,6 +138,9 @@
 
 void Udp::Send(const sockaddr& addr, std::span<const Buffer> bufs,
                const std::shared_ptr<UdpSendReq>& req) {
+  if (IsLoopClosing()) {
+    return;
+  }
   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);
@@ -150,12 +156,18 @@
 
 void Udp::Send(const sockaddr& addr, std::span<const Buffer> bufs,
                std::function<void(std::span<Buffer>, Error)> callback) {
+  if (IsLoopClosing()) {
+    return;
+  }
   Send(addr, bufs,
        std::make_shared<CallbackUdpSendReq>(bufs, std::move(callback)));
 }
 
 void Udp::Send(std::span<const Buffer> bufs,
                const std::shared_ptr<UdpSendReq>& req) {
+  if (IsLoopClosing()) {
+    return;
+  }
   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);
@@ -171,10 +183,16 @@
 
 void Udp::Send(std::span<const Buffer> bufs,
                std::function<void(std::span<Buffer>, Error)> callback) {
+  if (IsLoopClosing()) {
+    return;
+  }
   Send(bufs, std::make_shared<CallbackUdpSendReq>(bufs, std::move(callback)));
 }
 
 void Udp::StartRecv() {
+  if (IsLoopClosing()) {
+    return;
+  }
   Invoke(&uv_udp_recv_start, GetRaw(), &AllocBuf,
          [](uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf,
             const sockaddr* addr, unsigned flags) {
diff --git a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Work.cpp b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Work.cpp
index 818a93b..d94619f 100644
--- a/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Work.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/cpp/uv/Work.cpp
@@ -13,6 +13,9 @@
 }
 
 void QueueWork(Loop& loop, const std::shared_ptr<WorkReq>& req) {
+  if (loop.IsClosing()) {
+    return;
+  }
   int err = uv_queue_work(
       loop.GetRaw(), req->GetRaw(),
       [](uv_work_t* req) {
@@ -37,6 +40,9 @@
 
 void QueueWork(Loop& loop, std::function<void()> work,
                std::function<void()> afterWork) {
+  if (loop.IsClosing()) {
+    return;
+  }
   auto req = std::make_shared<WorkReq>();
   if (work) {
     req->work.connect(std::move(work));
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/ParallelTcpConnector.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/ParallelTcpConnector.h
index e7bc953..c3c869c 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/ParallelTcpConnector.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/ParallelTcpConnector.h
@@ -59,6 +59,9 @@
   static std::shared_ptr<ParallelTcpConnector> Create(
       wpi::uv::Loop& loop, wpi::uv::Timer::Time reconnectRate,
       wpi::Logger& logger, std::function<void(wpi::uv::Tcp& tcp)> connected) {
+    if (loop.IsClosing()) {
+      return nullptr;
+    }
     return std::make_shared<ParallelTcpConnector>(
         loop, reconnectRate, logger, std::move(connected), private_init{});
   }
@@ -112,7 +115,8 @@
   std::shared_ptr<wpi::uv::Timer> m_reconnectTimer;
   std::vector<std::pair<std::string, unsigned int>> m_servers;
   std::vector<std::weak_ptr<wpi::uv::GetAddrInfoReq>> m_resolvers;
-  std::vector<std::weak_ptr<wpi::uv::Tcp>> m_attempts;
+  std::vector<std::pair<sockaddr_storage, std::weak_ptr<wpi::uv::Tcp>>>
+      m_attempts;
   bool m_isConnected{false};
 };
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/WebSocket.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/WebSocket.h
index 1f295c9..297c123 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/WebSocket.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/WebSocket.h
@@ -34,6 +34,7 @@
 class WebSocket : public std::enable_shared_from_this<WebSocket> {
   struct private_init {};
 
+ public:
   static constexpr uint8_t kOpCont = 0x00;
   static constexpr uint8_t kOpText = 0x01;
   static constexpr uint8_t kOpBinary = 0x02;
@@ -42,8 +43,8 @@
   static constexpr uint8_t kOpPong = 0x0A;
   static constexpr uint8_t kOpMask = 0x0F;
   static constexpr uint8_t kFlagFin = 0x80;
+  static constexpr uint8_t kFlagControl = 0x08;
 
- public:
   WebSocket(uv::Stream& stream, bool server, const private_init&);
   WebSocket(const WebSocket&) = delete;
   WebSocket(WebSocket&&) = delete;
@@ -93,7 +94,7 @@
     static constexpr uint8_t kPing = kFlagFin | kOpPing;
     static constexpr uint8_t kPong = kFlagFin | kOpPong;
 
-    Frame(uint8_t opcode, std::span<const uv::Buffer> data)
+    constexpr Frame(uint8_t opcode, std::span<const uv::Buffer> data)
         : opcode{opcode}, data{data} {}
 
     uint8_t opcode;
@@ -339,7 +340,7 @@
   void SendPing(
       std::span<const uv::Buffer> data,
       std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
-    Send(kFlagFin | kOpPing, data, std::move(callback));
+    SendControl(kFlagFin | kOpPing, data, std::move(callback));
   }
 
   /**
@@ -376,7 +377,7 @@
   void SendPong(
       std::span<const uv::Buffer> data,
       std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
-    Send(kFlagFin | kOpPong, data, std::move(callback));
+    SendControl(kFlagFin | kOpPong, data, std::move(callback));
   }
 
   /**
@@ -402,6 +403,31 @@
       std::function<void(std::span<uv::Buffer>, uv::Error)> callback);
 
   /**
+   * Try to send multiple frames. Tries to send as many frames as possible
+   * immediately, and only queues the "last" frame it can (as the network queue
+   * will almost always fill partway through a frame). The frames following
+   * the last frame will NOT be queued for transmission; the caller is
+   * responsible for how to handle (e.g. re-send) those frames (e.g. when the
+   * callback is called).
+   *
+   * @param frames Frame type/data pairs
+   * @param callback Callback which is invoked when the write completes of the
+   *                 last frame that is not returned.
+   * @return Remaining frames that will not be sent
+   */
+  std::span<const Frame> TrySendFrames(
+      std::span<const Frame> frames,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback);
+
+  /**
+   * Returns whether or not a previous TrySendFrames is still in progress.
+   * Calling TrySendFrames if this returns true will return all frames.
+   *
+   * @return True if a TryWrite is in progress
+   */
+  bool IsWriteInProgress() const { return m_writeInProgress; }
+
+  /**
    * Fail the connection.
    */
   void Fail(uint16_t code = 1002, std::string_view reason = "protocol error");
@@ -460,7 +486,8 @@
   sig::Signal<std::span<const uint8_t>, bool> binary;
 
   /**
-   * Ping event.  Emitted when a ping message is received.
+   * Ping event.  Emitted when a ping message is received.  A pong message is
+   * automatically sent in response, so this is simply a notification.
    */
   sig::Signal<std::span<const uint8_t>> ping;
 
@@ -484,6 +511,12 @@
   size_t m_maxMessageSize = 128 * 1024;
   bool m_combineFragments = true;
 
+  // outgoing write request
+  bool m_writeInProgress = false;
+  class WriteReq;
+  std::weak_ptr<WriteReq> m_curWriteReq;
+  std::weak_ptr<WriteReq> m_lastWriteReq;
+
   // operating state
   State m_state = CONNECTING;
 
@@ -491,6 +524,7 @@
   SmallVector<uint8_t, 14> m_header;
   size_t m_headerSize = 0;
   SmallVector<uint8_t, 1024> m_payload;
+  SmallVector<uint8_t, 64> m_controlPayload;
   size_t m_frameStart = 0;
   uint64_t m_frameSize = UINT64_MAX;
   uint8_t m_fragmentOpcode = 0;
@@ -507,10 +541,16 @@
   void SendClose(uint16_t code, std::string_view reason);
   void SetClosed(uint16_t code, std::string_view reason, bool failed = false);
   void HandleIncoming(uv::Buffer& buf, size_t size);
+  void SendControl(
+      uint8_t opcode, std::span<const uv::Buffer> data,
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback);
   void Send(uint8_t opcode, std::span<const uv::Buffer> data,
             std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {
     SendFrames({{Frame{opcode, data}}}, std::move(callback));
   }
+  void SendError(
+      std::span<const Frame> frames,
+      const std::function<void(std::span<uv::Buffer>, uv::Error)>& callback);
 };
 
 }  // namespace wpi
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/http_parser.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/http_parser.h
index 2189b8f..3993a4f 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/http_parser.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/http_parser.h
@@ -36,7 +36,7 @@
 # define HTTP_PARSER_STRICT 1
 #endif
 
-/* Maximium header size allowed. If the macro is not defined
+/* Maximum header size allowed. If the macro is not defined
  * before including this header then the default is used. To
  * change the maximum header size, define the macro in the build
  * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Async.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Async.h
index eb3a005..2eb13d7 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Async.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Async.h
@@ -62,6 +62,9 @@
    * @param loop Loop object where this handle runs.
    */
   static std::shared_ptr<Async> Create(const std::shared_ptr<Loop>& loop) {
+    if (loop->IsClosing()) {
+      return nullptr;
+    }
     auto h = std::make_shared<Async>(loop, private_init{});
     int err =
         uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
@@ -89,6 +92,9 @@
   template <typename... U>
   void Send(U&&... u) {
     auto loop = m_loop.lock();
+    if (loop->IsClosing()) {
+      return;
+    }
     if (loop && loop->GetThreadId() == std::this_thread::get_id()) {
       // called from within the loop, just call the function directly
       wakeup(std::forward<U>(u)...);
@@ -161,6 +167,9 @@
    */
   void Send() {
     if (auto loop = m_loop.lock()) {
+      if (loop->IsClosing()) {
+        return;
+      }
       if (loop->GetThreadId() == std::this_thread::get_id()) {
         // called from within the loop, just call the function directly
         wakeup();
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/AsyncFunction.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/AsyncFunction.h
index 82a5913..f59875a 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/AsyncFunction.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/AsyncFunction.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 #include <uv.h>
 
+#include <concepts>
 #include <functional>
 #include <memory>
 #include <thread>
@@ -74,6 +75,9 @@
   static std::shared_ptr<AsyncFunction> Create(
       const std::shared_ptr<Loop>& loop,
       std::function<void(promise<R>, T...)> func = nullptr) {
+    if (loop->IsClosing()) {
+      return nullptr;
+    }
     auto h =
         std::make_shared<AsyncFunction>(loop, std::move(func), private_init{});
     int err =
@@ -123,6 +127,13 @@
     uint64_t req = m_promises.CreateRequest();
 
     auto loop = m_loop.lock();
+    if (loop->IsClosing()) {
+      if constexpr (std::same_as<R, void>) {
+        return m_promises.MakeReadyFuture();
+      } else {
+        return m_promises.MakeReadyFuture({});
+      }
+    }
     if (loop && loop->GetThreadId() == std::this_thread::get_id()) {
       // called from within the loop, just call the function directly
       wakeup(m_promises.CreatePromise(req), std::forward<U>(u)...);
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Buffer.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Buffer.h
index 4b58b0f..01dc881 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Buffer.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Buffer.h
@@ -42,10 +42,23 @@
     base = const_cast<char*>(base_);
     len = static_cast<decltype(len)>(len_);
   }
+  Buffer(uint8_t* base_, size_t len_) {
+    base = reinterpret_cast<char*>(base_);
+    len = static_cast<decltype(len)>(len_);
+  }
+  Buffer(const uint8_t* base_, size_t len_) {
+    base = reinterpret_cast<char*>(const_cast<uint8_t*>(base_));
+    len = static_cast<decltype(len)>(len_);
+  }
 
   std::span<const char> data() const { return {base, len}; }
   std::span<char> data() { return {base, len}; }
 
+  std::span<const uint8_t> bytes() const {
+    return {reinterpret_cast<const uint8_t*>(base), len};
+  }
+  std::span<uint8_t> bytes() { return {reinterpret_cast<uint8_t*>(base), len}; }
+
   operator std::span<const char>() const { return data(); }  // NOLINT
   operator std::span<char>() { return data(); }              // NOLINT
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Error.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Error.h
index cc2a5d5..f3f8d24 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Error.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Error.h
@@ -38,7 +38,7 @@
   const char* name() const { return uv_err_name(m_err); }
 
  private:
-  int m_err{UV_UNKNOWN};
+  int m_err{0};
 };
 
 }  // namespace wpi::uv
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Idle.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Idle.h
index 4ed6d07..5cb4625 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Idle.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Idle.h
@@ -28,7 +28,7 @@
  * for I/O.
  *
  * @warning Despite the name, idle handles will signal every loop iteration,
- * not when the loop is actually "idle".  This also means they can easly become
+ * not when the loop is actually "idle".  This also means they can easily become
  * CPU hogs.
  */
 class Idle final : public HandleImpl<Idle, uv_idle_t> {
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Loop.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Loop.h
index 129faf5..0897c87 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Loop.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Loop.h
@@ -71,6 +71,20 @@
   static std::shared_ptr<Loop> GetDefault();
 
   /**
+   * Set the loop closing flag.
+   *
+   * This will prevent new handles from being created on the loop.
+   */
+  void SetClosing() { m_closing = true; }
+
+  /**
+   * Return the loop closed flag.
+   *
+   * @return True if SetClosed() has been called.
+   */
+  bool IsClosing() const { return m_closing; }
+
+  /**
    * Release all internal loop resources.
    *
    * Call this function only when the loop has finished executing and all open
@@ -247,6 +261,7 @@
   uv_loop_t* m_loop;
   uv_loop_t m_loopStruct;
   std::atomic<std::thread::id> m_tid;
+  bool m_closing = false;
 };
 
 }  // namespace wpi::uv
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Poll.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Poll.h
index 1e63836..3ba9a2c 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Poll.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Poll.h
@@ -7,6 +7,7 @@
 
 #include <uv.h>
 
+#include <functional>
 #include <memory>
 
 #include <wpi/Signal.h>
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Request.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Request.h
index 3fbec6f..d16d289 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Request.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Request.h
@@ -9,6 +9,7 @@
 
 #include <functional>
 #include <memory>
+#include <utility>
 
 #include "wpinet/uv/Error.h"
 
@@ -92,8 +93,12 @@
    *
    * Derived classes can override this method for different memory management
    * approaches (e.g. pooled storage of requests).
+   *
+   * @return Previous shared pointer
    */
-  virtual void Release() noexcept { m_self.reset(); }
+  virtual std::shared_ptr<Request> Release() noexcept {
+    return std::move(m_self);
+  }
 
   /**
    * Error callback.  By default, this is set up to report errors to the handle
@@ -130,11 +135,11 @@
 class RequestImpl : public Request {
  public:
   std::shared_ptr<T> shared_from_this() {
-    return std::static_pointer_cast<T>(this->shared_from_this());
+    return std::static_pointer_cast<T>(Request::shared_from_this());
   }
 
   std::shared_ptr<const T> shared_from_this() const {
-    return std::static_pointer_cast<const T>(this->shared_from_this());
+    return std::static_pointer_cast<const T>(Request::shared_from_this());
   }
 
   /**
diff --git a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Stream.h b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Stream.h
index 9568455..29e5811 100644
--- a/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Stream.h
+++ b/third_party/allwpilib/wpinet/src/main/native/include/wpinet/uv/Stream.h
@@ -194,8 +194,9 @@
    * An error signal will be emitted in case of errors.
    *
    * @param bufs The buffers to be written to the stream.
-   * @return Number of bytes written.
+   * @return Number of bytes written, or negative (error code) on error
    */
+  [[nodiscard]]
   int TryWrite(std::span<const Buffer> bufs);
 
   /**
@@ -206,8 +207,9 @@
    * An error signal will be emitted in case of errors.
    *
    * @param bufs The buffers to be written to the stream.
-   * @return Number of bytes written.
+   * @return Number of bytes written, or negative (error code) on error
    */
+  [[nodiscard]]
   int TryWrite(std::initializer_list<Buffer> bufs) {
     return TryWrite({bufs.begin(), bufs.end()});
   }
@@ -221,8 +223,9 @@
    *
    * @param bufs The buffers to be written to the stream.
    * @param send send stream
-   * @return Number of bytes written.
+   * @return Number of bytes written, or negative (error code) on error
    */
+  [[nodiscard]]
   int TryWrite2(std::span<const Buffer> bufs, Stream& send);
 
   /**
@@ -234,8 +237,9 @@
    *
    * @param bufs The buffers to be written to the stream.
    * @param send send stream
-   * @return Number of bytes written.
+   * @return Number of bytes written, or negative (error code) on error
    */
+  [[nodiscard]]
   int TryWrite2(std::initializer_list<Buffer> bufs, Stream& send) {
     return TryWrite2({bufs.begin(), bufs.end()}, send);
   }
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv.h
index dbaeb1e..d5342b0 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv.h
@@ -28,6 +28,7 @@
 #error "Define either BUILDING_UV_SHARED or USING_UV_SHARED, not both."
 #endif
 
+#ifndef UV_EXTERN
 #ifdef _WIN32
   /* Windows - set up dll import/export decorators. */
 # if defined(BUILDING_UV_SHARED)
@@ -47,14 +48,20 @@
 #else
 # define UV_EXTERN /* nothing */
 #endif
+#endif /* UV_EXTERN */
 
 #include "uv/errno.h"
 #include "uv/version.h"
 #include <stddef.h>
 #include <stdio.h>
-
 #include <stdint.h>
 
+/* Internal type, do not use. */
+struct uv__queue {
+  struct uv__queue* next;
+  struct uv__queue* prev;
+};
+
 #if defined(_WIN32)
 # include "uv/win.h"
 #else
@@ -145,6 +152,8 @@
   XX(EFTYPE, "inappropriate file type or format")                             \
   XX(EILSEQ, "illegal byte sequence")                                         \
   XX(ESOCKTNOSUPPORT, "socket type not supported")                            \
+  XX(ENODATA, "no data available")                                            \
+  XX(EUNATCH, "protocol driver not attached")                                 \
 
 #define UV_HANDLE_TYPE_MAP(XX)                                                \
   XX(ASYNC, async)                                                            \
@@ -240,9 +249,12 @@
 typedef struct uv_interface_address_s uv_interface_address_t;
 typedef struct uv_dirent_s uv_dirent_t;
 typedef struct uv_passwd_s uv_passwd_t;
+typedef struct uv_group_s uv_group_t;
 typedef struct uv_utsname_s uv_utsname_t;
 typedef struct uv_statfs_s uv_statfs_t;
 
+typedef struct uv_metrics_s uv_metrics_t;
+
 typedef enum {
   UV_LOOP_BLOCK_SIGNAL = 0,
   UV_METRICS_IDLE_TIME
@@ -275,13 +287,13 @@
 UV_EXTERN int uv_loop_close(uv_loop_t* loop);
 /*
  * NOTE:
- *  This function is DEPRECATED (to be removed after 0.12), users should
+ *  This function is DEPRECATED, users should
  *  allocate the loop manually and use uv_loop_init instead.
  */
 UV_EXTERN uv_loop_t* uv_loop_new(void);
 /*
  * NOTE:
- *  This function is DEPRECATED (to be removed after 0.12). Users should use
+ *  This function is DEPRECATED. Users should use
  *  uv_loop_close and free the memory manually instead.
  */
 UV_EXTERN void uv_loop_delete(uv_loop_t*);
@@ -337,11 +349,32 @@
                              void* buf,
                              size_t buflen);
 
+typedef enum {
+  UV_CLOCK_MONOTONIC,
+  UV_CLOCK_REALTIME
+} uv_clock_id;
+
+/* XXX(bnoordhuis) not 2038-proof, https://github.com/libuv/libuv/issues/3864 */
 typedef struct {
   long tv_sec;
   long tv_nsec;
 } uv_timespec_t;
 
+typedef struct {
+  int64_t tv_sec;
+  int32_t tv_nsec;
+} uv_timespec64_t;
+
+/* XXX(bnoordhuis) not 2038-proof, https://github.com/libuv/libuv/issues/3864 */
+typedef struct {
+  long tv_sec;
+  long tv_usec;
+} uv_timeval_t;
+
+typedef struct {
+  int64_t tv_sec;
+  int32_t tv_usec;
+} uv_timeval64_t;
 
 typedef struct {
   uint64_t st_dev;
@@ -430,7 +463,7 @@
   uv_handle_type type;                                                        \
   /* private */                                                               \
   uv_close_cb close_cb;                                                       \
-  void* handle_queue[2];                                                      \
+  struct uv__queue handle_queue;                                              \
   union {                                                                     \
     int fd;                                                                   \
     void* reserved[4];                                                        \
@@ -766,6 +799,10 @@
 
 UV_EXTERN uv_handle_type uv_guess_handle(uv_file file);
 
+enum {
+  UV_PIPE_NO_TRUNCATE = 1u << 0
+};
+
 /*
  * uv_pipe_t is a subclass of uv_stream_t.
  *
@@ -782,10 +819,20 @@
 UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc);
 UV_EXTERN int uv_pipe_open(uv_pipe_t*, uv_file file);
 UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name);
+UV_EXTERN int uv_pipe_bind2(uv_pipe_t* handle,
+                            const char* name,
+                            size_t namelen,
+                            unsigned int flags);
 UV_EXTERN void uv_pipe_connect(uv_connect_t* req,
                                uv_pipe_t* handle,
                                const char* name,
                                uv_connect_cb cb);
+UV_EXTERN int uv_pipe_connect2(uv_connect_t* req,
+                               uv_pipe_t* handle,
+                               const char* name,
+                               size_t namelen,
+                               unsigned int flags,
+                               uv_connect_cb cb);
 UV_EXTERN int uv_pipe_getsockname(const uv_pipe_t* handle,
                                   char* buffer,
                                   size_t* size);
@@ -1126,6 +1173,12 @@
   char* homedir;
 };
 
+struct uv_group_s {
+  char* groupname;
+  unsigned long gid;
+  char** members;
+};
+
 struct uv_utsname_s {
   char sysname[256];
   char release[256];
@@ -1172,16 +1225,6 @@
 UV_EXTERN int uv_open_osfhandle(uv_os_fd_t os_fd);
 
 typedef struct {
-  long tv_sec;
-  long tv_usec;
-} uv_timeval_t;
-
-typedef struct {
-  int64_t tv_sec;
-  int32_t tv_usec;
-} uv_timeval64_t;
-
-typedef struct {
    uv_timeval_t ru_utime; /* user CPU time used */
    uv_timeval_t ru_stime; /* system CPU time used */
    uint64_t ru_maxrss;    /* maximum resident set size */
@@ -1206,6 +1249,9 @@
 UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size);
 UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd);
 UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd);
+UV_EXTERN int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid);
+UV_EXTERN int uv_os_get_group(uv_group_t* grp, uv_uid_t gid);
+UV_EXTERN void uv_os_free_group(uv_group_t* grp);
 UV_EXTERN uv_pid_t uv_os_getpid(void);
 UV_EXTERN uv_pid_t uv_os_getppid(void);
 
@@ -1232,6 +1278,7 @@
 UV_EXTERN unsigned int uv_available_parallelism(void);
 UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
 UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
+UV_EXTERN int uv_cpumask_size(void);
 
 UV_EXTERN int uv_interface_addresses(uv_interface_address_t** addresses,
                                      int* count);
@@ -1264,6 +1311,15 @@
 
 UV_EXTERN int uv_os_uname(uv_utsname_t* buffer);
 
+struct uv_metrics_s {
+  uint64_t loop_count;
+  uint64_t events;
+  uint64_t events_waiting;
+  /* private */
+  uint64_t* reserved[13];
+};
+
+UV_EXTERN int uv_metrics_info(uv_loop_t* loop, uv_metrics_t* metrics);
 UV_EXTERN uint64_t uv_metrics_idle_time(uv_loop_t* loop);
 
 typedef enum {
@@ -1697,7 +1753,9 @@
 UV_EXTERN uint64_t uv_get_free_memory(void);
 UV_EXTERN uint64_t uv_get_total_memory(void);
 UV_EXTERN uint64_t uv_get_constrained_memory(void);
+UV_EXTERN uint64_t uv_get_available_memory(void);
 
+UV_EXTERN int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts);
 UV_EXTERN uint64_t uv_hrtime(void);
 UV_EXTERN void uv_sleep(unsigned int msec);
 
@@ -1774,6 +1832,14 @@
                                   const uv_thread_options_t* params,
                                   uv_thread_cb entry,
                                   void* arg);
+UV_EXTERN int uv_thread_setaffinity(uv_thread_t* tid,
+                                    char* cpumask,
+                                    char* oldmask,
+                                    size_t mask_size);
+UV_EXTERN int uv_thread_getaffinity(uv_thread_t* tid,
+                                    char* cpumask,
+                                    size_t mask_size);
+UV_EXTERN int uv_thread_getcpu(void);
 UV_EXTERN uv_thread_t uv_thread_self(void);
 UV_EXTERN int uv_thread_join(uv_thread_t *tid);
 UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
@@ -1795,7 +1861,7 @@
   void* data;
   /* Loop reference counting. */
   unsigned int active_handles;
-  void* handle_queue[2];
+  struct uv__queue handle_queue;
   union {
     void* unused;
     unsigned int count;
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/darwin.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/darwin.h
index d226415..06962bf 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/darwin.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/darwin.h
@@ -40,7 +40,7 @@
   void* cf_state;                                                             \
   uv_mutex_t cf_mutex;                                                        \
   uv_sem_t cf_sem;                                                            \
-  void* cf_signals[2];                                                        \
+  struct uv__queue cf_signals;                                                \
 
 #define UV_PLATFORM_FS_EVENT_FIELDS                                           \
   uv__io_t event_watcher;                                                     \
@@ -48,8 +48,8 @@
   int realpath_len;                                                           \
   int cf_flags;                                                               \
   uv_async_t* cf_cb;                                                          \
-  void* cf_events[2];                                                         \
-  void* cf_member[2];                                                         \
+  struct uv__queue cf_events;                                                 \
+  struct uv__queue cf_member;                                                 \
   int cf_error;                                                               \
   uv_mutex_t cf_mutex;                                                        \
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/errno.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/errno.h
index 71906b3..127278e 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/errno.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/errno.h
@@ -413,7 +413,6 @@
 #elif defined(__APPLE__) || \
       defined(__DragonFly__) || \
       defined(__FreeBSD__) || \
-      defined(__FreeBSD_kernel__) || \
       defined(__NetBSD__) || \
       defined(__OpenBSD__)
 # define UV__EHOSTDOWN (-64)
@@ -457,4 +456,22 @@
 # define UV__ESOCKTNOSUPPORT (-4025)
 #endif
 
+/* FreeBSD defines ENODATA in /usr/include/c++/v1/errno.h which is only visible
+ * if C++ is being used. Define it directly to avoid problems when integrating
+ * libuv in a C++ project.
+ */
+#if defined(ENODATA) && !defined(_WIN32)
+# define UV__ENODATA UV__ERR(ENODATA)
+#elif defined(__FreeBSD__)
+# define UV__ENODATA (-9919)
+#else
+# define UV__ENODATA (-4024)
+#endif
+
+#if defined(EUNATCH) && !defined(_WIN32)
+# define UV__EUNATCH UV__ERR(EUNATCH)
+#else
+# define UV__EUNATCH (-4023)
+#endif
+
 #endif /* UV_ERRNO_H_ */
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/linux.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/linux.h
index 9b38405..9f22f8c 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/linux.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/linux.h
@@ -28,7 +28,7 @@
   int inotify_fd;                                                             \
 
 #define UV_PLATFORM_FS_EVENT_FIELDS                                           \
-  void* watchers[2];                                                          \
+  struct uv__queue watchers;                                                  \
   int wd;                                                                     \
 
 #endif /* UV_LINUX_H */
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/threadpool.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/threadpool.h
index 9708ebd..24ce916 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/threadpool.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/threadpool.h
@@ -31,7 +31,7 @@
   void (*work)(struct uv__work *w);
   void (*done)(struct uv__work *w, int status);
   struct uv_loop_s* loop;
-  void* wq[2];
+  struct uv__queue wq;
 };
 
 #endif /* UV_THREADPOOL_H_ */
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/unix.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/unix.h
index 256fef3..e334cab 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/unix.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/unix.h
@@ -51,7 +51,6 @@
 # include "uv/darwin.h"
 #elif defined(__DragonFly__)       || \
       defined(__FreeBSD__)         || \
-      defined(__FreeBSD_kernel__)  || \
       defined(__OpenBSD__)         || \
       defined(__NetBSD__)
 # include "uv/bsd.h"
@@ -85,8 +84,8 @@
 
 struct uv__io_s {
   uv__io_cb cb;
-  void* pending_queue[2];
-  void* watcher_queue[2];
+  struct uv__queue pending_queue;
+  struct uv__queue watcher_queue;
   unsigned int pevents; /* Pending event mask i.e. mask at next tick. */
   unsigned int events;  /* Current event mask. */
   int fd;
@@ -213,21 +212,21 @@
 #define UV_LOOP_PRIVATE_FIELDS                                                \
   unsigned long flags;                                                        \
   int backend_fd;                                                             \
-  void* pending_queue[2];                                                     \
-  void* watcher_queue[2];                                                     \
-  void** watchers;                                                            \
+  struct uv__queue pending_queue;                                             \
+  struct uv__queue watcher_queue;                                             \
+  uv__io_t** watchers;                                                        \
   unsigned int nwatchers;                                                     \
   unsigned int nfds;                                                          \
-  void* wq[2];                                                                \
+  struct uv__queue wq;                                                        \
   uv_mutex_t wq_mutex;                                                        \
   uv_async_t wq_async;                                                        \
   uv_rwlock_t cloexec_lock;                                                   \
   uv_handle_t* closing_handles;                                               \
-  void* process_handles[2];                                                   \
-  void* prepare_handles[2];                                                   \
-  void* check_handles[2];                                                     \
-  void* idle_handles[2];                                                      \
-  void* async_handles[2];                                                     \
+  struct uv__queue process_handles;                                           \
+  struct uv__queue prepare_handles;                                           \
+  struct uv__queue check_handles;                                             \
+  struct uv__queue idle_handles;                                              \
+  struct uv__queue async_handles;                                             \
   void (*async_unused)(void);  /* TODO(bnoordhuis) Remove in libuv v2. */     \
   uv__io_t async_io_watcher;                                                  \
   int async_wfd;                                                              \
@@ -250,7 +249,7 @@
 #define UV_PRIVATE_REQ_TYPES /* empty */
 
 #define UV_WRITE_PRIVATE_FIELDS                                               \
-  void* queue[2];                                                             \
+  struct uv__queue queue;                                                     \
   unsigned int write_index;                                                   \
   uv_buf_t* bufs;                                                             \
   unsigned int nbufs;                                                         \
@@ -258,12 +257,12 @@
   uv_buf_t bufsml[4];                                                         \
 
 #define UV_CONNECT_PRIVATE_FIELDS                                             \
-  void* queue[2];                                                             \
+  struct uv__queue queue;                                                     \
 
 #define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */
 
 #define UV_UDP_SEND_PRIVATE_FIELDS                                            \
-  void* queue[2];                                                             \
+  struct uv__queue queue;                                                     \
   struct sockaddr_storage addr;                                               \
   unsigned int nbufs;                                                         \
   uv_buf_t* bufs;                                                             \
@@ -279,8 +278,8 @@
   uv_connect_t *connect_req;                                                  \
   uv_shutdown_t *shutdown_req;                                                \
   uv__io_t io_watcher;                                                        \
-  void* write_queue[2];                                                       \
-  void* write_completed_queue[2];                                             \
+  struct uv__queue write_queue;                                               \
+  struct uv__queue write_completed_queue;                                     \
   uv_connection_cb connection_cb;                                             \
   int delayed_error;                                                          \
   int accepted_fd;                                                            \
@@ -293,30 +292,30 @@
   uv_alloc_cb alloc_cb;                                                       \
   uv_udp_recv_cb recv_cb;                                                     \
   uv__io_t io_watcher;                                                        \
-  void* write_queue[2];                                                       \
-  void* write_completed_queue[2];                                             \
+  struct uv__queue write_queue;                                               \
+  struct uv__queue write_completed_queue;                                     \
 
 #define UV_PIPE_PRIVATE_FIELDS                                                \
-  const char* pipe_fname; /* strdup'ed */
+  const char* pipe_fname; /* NULL or strdup'ed */
 
 #define UV_POLL_PRIVATE_FIELDS                                                \
   uv__io_t io_watcher;
 
 #define UV_PREPARE_PRIVATE_FIELDS                                             \
   uv_prepare_cb prepare_cb;                                                   \
-  void* queue[2];                                                             \
+  struct uv__queue queue;                                                     \
 
 #define UV_CHECK_PRIVATE_FIELDS                                               \
   uv_check_cb check_cb;                                                       \
-  void* queue[2];                                                             \
+  struct uv__queue queue;                                                     \
 
 #define UV_IDLE_PRIVATE_FIELDS                                                \
   uv_idle_cb idle_cb;                                                         \
-  void* queue[2];                                                             \
+  struct uv__queue queue;                                                     \
 
 #define UV_ASYNC_PRIVATE_FIELDS                                               \
   uv_async_cb async_cb;                                                       \
-  void* queue[2];                                                             \
+  struct uv__queue queue;                                                     \
   int pending;                                                                \
 
 #define UV_TIMER_PRIVATE_FIELDS                                               \
@@ -345,7 +344,7 @@
   int retcode;
 
 #define UV_PROCESS_PRIVATE_FIELDS                                             \
-  void* queue[2];                                                             \
+  struct uv__queue queue;                                                     \
   int status;                                                                 \
 
 #define UV_FS_PRIVATE_FIELDS                                                  \
@@ -410,6 +409,8 @@
 # define UV_FS_O_DIRECT       0x04000
 #elif defined(__linux__) && defined(__x86_64__)
 # define UV_FS_O_DIRECT       0x04000
+#elif defined(__linux__) && defined(__loongarch__)
+# define UV_FS_O_DIRECT       0x04000
 #elif defined(O_DIRECT)
 # define UV_FS_O_DIRECT       O_DIRECT
 #else
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/version.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/version.h
index 9c9d292..24fac8d 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/version.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/version.h
@@ -31,8 +31,8 @@
  */
 
 #define UV_VERSION_MAJOR 1
-#define UV_VERSION_MINOR 44
-#define UV_VERSION_PATCH 2
+#define UV_VERSION_MINOR 46
+#define UV_VERSION_PATCH 0
 #define UV_VERSION_IS_RELEASE 1
 #define UV_VERSION_SUFFIX ""
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/win.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/win.h
index 0a33366..613065d 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/win.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/include/uv/win.h
@@ -20,7 +20,7 @@
  */
 
 #ifndef _WIN32_WINNT
-# define _WIN32_WINNT   0x0600
+# define _WIN32_WINNT   0x0A00
 #endif
 
 #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
@@ -59,7 +59,6 @@
 #include <signal.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-
 #include <stdint.h>
 
 #include "uv/tree.h"
@@ -71,6 +70,11 @@
 # define S_IFLNK 0xA000
 #endif
 
+// Define missing in Windows Kit Include\{VERSION}\ucrt\sys\stat.h
+#if defined(_CRT_INTERNAL_NONSTDC_NAMES) && _CRT_INTERNAL_NONSTDC_NAMES && !defined(S_IFIFO)
+# define S_IFIFO _S_IFIFO
+#endif
+
 /* Additional signals supported by uv_signal and or uv_kill. The CRT defines
  * the following signals already:
  *
@@ -275,11 +279,12 @@
 } uv_rwlock_t;
 
 typedef struct {
-  unsigned int n;
-  unsigned int count;
+  unsigned threshold;
+  unsigned in;
   uv_mutex_t mutex;
-  uv_sem_t turnstile1;
-  uv_sem_t turnstile2;
+  /* TODO: in v2 make this a uv_cond_t, without unused_ */
+  CONDITION_VARIABLE cond;
+  unsigned out;
 } uv_barrier_t;
 
 typedef struct {
@@ -349,14 +354,14 @@
   uv_idle_t* next_idle_handle;                                                \
   /* This handle holds the peer sockets for the fast variant of uv_poll_t */  \
   SOCKET poll_peer_sockets[UV_MSAFD_PROVIDER_COUNT];                          \
-  /* Counter to keep track of active tcp streams */                           \
+  /* No longer used. */                                                       \
   unsigned int active_tcp_streams;                                            \
-  /* Counter to keep track of active udp streams */                           \
+  /* No longer used. */                                                       \
   unsigned int active_udp_streams;                                            \
   /* Counter to started timer */                                              \
   uint64_t timer_counter;                                                     \
   /* Threadpool */                                                            \
-  void* wq[2];                                                                \
+  struct uv__queue wq;                                                        \
   uv_mutex_t wq_mutex;                                                        \
   uv_async_t wq_async;
 
@@ -383,6 +388,7 @@
       ULONG_PTR result; /* overlapped.Internal is reused to hold the result */\
       HANDLE pipeHandle;                                                      \
       DWORD duplex_flags;                                                     \
+      WCHAR* name;                                                             \
     } connect;                                                                \
   } u;                                                                        \
   struct uv_req_s* next_req;
@@ -484,7 +490,7 @@
     uint32_t payload_remaining;                                               \
     uint64_t dummy; /* TODO: retained for ABI compat; remove this in v2.x. */ \
   } ipc_data_frame;                                                           \
-  void* ipc_xfer_queue[2];                                                    \
+  struct uv__queue ipc_xfer_queue;                                            \
   int ipc_xfer_queue_length;                                                  \
   uv_write_t* non_overlapped_writes_tail;                                     \
   CRITICAL_SECTION readfile_thread_lock;                                      \
@@ -498,7 +504,7 @@
     struct { uv_pipe_connection_fields } conn;                                \
   } pipe;
 
-/* TODO: put the parser states in an union - TTY handles are always half-duplex
+/* TODO: put the parser states in a union - TTY handles are always half-duplex
  * so read-state can safely overlap write-state. */
 #define UV_TTY_PRIVATE_FIELDS                                                 \
   HANDLE handle;                                                              \
@@ -606,7 +612,7 @@
   struct uv_process_exit_s {                                                  \
     UV_REQ_FIELDS                                                             \
   } exit_req;                                                                 \
-  BYTE* child_stdio_buffer;                                                   \
+  void* unused; /* TODO: retained for ABI compat; remove this in v2.x. */     \
   int exit_signal;                                                            \
   HANDLE wait_handle;                                                         \
   HANDLE process_handle;                                                      \
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/inet.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/inet.cpp
index 1b19025..71c9e5b 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/inet.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/inet.cpp
@@ -17,12 +17,7 @@
 
 #include <stdio.h>
 #include <string.h>
-
-#if defined(_MSC_VER) && _MSC_VER < 1600
-# include "uv/stdint-msvc2008.h"
-#else
-# include <stdint.h>
-#endif
+#include <stdint.h>
 
 #include "uv.h"
 #include "uv-common.h"
@@ -139,7 +134,7 @@
       tp += strlen(tp);
       break;
     }
-    tp += sprintf(tp, "%x", words[i]);
+    tp += snprintf(tp, sizeof tmp - (tp - tmp), "%x", words[i]);
   }
   /* Was it a trailing run of 0x00's? */
   if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words))
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/queue.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/queue.h
index ff3540a..5f8489e 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/queue.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/queue.h
@@ -18,91 +18,73 @@
 
 #include <stddef.h>
 
-typedef void *QUEUE[2];
+#define uv__queue_data(pointer, type, field)                                  \
+  ((type*) ((char*) (pointer) - offsetof(type, field)))
 
-/* Private macros. */
-#define QUEUE_NEXT(q)       (*(QUEUE **) &((*(q))[0]))
-#define QUEUE_PREV(q)       (*(QUEUE **) &((*(q))[1]))
-#define QUEUE_PREV_NEXT(q)  (QUEUE_NEXT(QUEUE_PREV(q)))
-#define QUEUE_NEXT_PREV(q)  (QUEUE_PREV(QUEUE_NEXT(q)))
+#define uv__queue_foreach(q, h)                                               \
+  for ((q) = (h)->next; (q) != (h); (q) = (q)->next)
 
-/* Public macros. */
-#define QUEUE_DATA(ptr, type, field)                                          \
-  ((type *) ((char *) (ptr) - offsetof(type, field)))
+static inline void uv__queue_init(struct uv__queue* q) {
+  q->next = q;
+  q->prev = q;
+}
 
-/* Important note: mutating the list while QUEUE_FOREACH is
- * iterating over its elements results in undefined behavior.
- */
-#define QUEUE_FOREACH(q, h)                                                   \
-  for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q))
+static inline int uv__queue_empty(const struct uv__queue* q) {
+  return q == q->next;
+}
 
-#define QUEUE_EMPTY(q)                                                        \
-  ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q))
+static inline struct uv__queue* uv__queue_head(const struct uv__queue* q) {
+  return q->next;
+}
 
-#define QUEUE_HEAD(q)                                                         \
-  (QUEUE_NEXT(q))
+static inline struct uv__queue* uv__queue_next(const struct uv__queue* q) {
+  return q->next;
+}
 
-#define QUEUE_INIT(q)                                                         \
-  do {                                                                        \
-    QUEUE_NEXT(q) = (q);                                                      \
-    QUEUE_PREV(q) = (q);                                                      \
-  }                                                                           \
-  while (0)
+static inline void uv__queue_add(struct uv__queue* h, struct uv__queue* n) {
+  h->prev->next = n->next;
+  n->next->prev = h->prev;
+  h->prev = n->prev;
+  h->prev->next = h;
+}
 
-#define QUEUE_ADD(h, n)                                                       \
-  do {                                                                        \
-    QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n);                                       \
-    QUEUE_NEXT_PREV(n) = QUEUE_PREV(h);                                       \
-    QUEUE_PREV(h) = QUEUE_PREV(n);                                            \
-    QUEUE_PREV_NEXT(h) = (h);                                                 \
-  }                                                                           \
-  while (0)
+static inline void uv__queue_split(struct uv__queue* h,
+                                   struct uv__queue* q,
+                                   struct uv__queue* n) {
+  n->prev = h->prev;
+  n->prev->next = n;
+  n->next = q;
+  h->prev = q->prev;
+  h->prev->next = h;
+  q->prev = n;
+}
 
-#define QUEUE_SPLIT(h, q, n)                                                  \
-  do {                                                                        \
-    QUEUE_PREV(n) = QUEUE_PREV(h);                                            \
-    QUEUE_PREV_NEXT(n) = (n);                                                 \
-    QUEUE_NEXT(n) = (q);                                                      \
-    QUEUE_PREV(h) = QUEUE_PREV(q);                                            \
-    QUEUE_PREV_NEXT(h) = (h);                                                 \
-    QUEUE_PREV(q) = (n);                                                      \
-  }                                                                           \
-  while (0)
+static inline void uv__queue_move(struct uv__queue* h, struct uv__queue* n) {
+  if (uv__queue_empty(h))
+    uv__queue_init(n);
+  else
+    uv__queue_split(h, h->next, n);
+}
 
-#define QUEUE_MOVE(h, n)                                                      \
-  do {                                                                        \
-    if (QUEUE_EMPTY(h))                                                       \
-      QUEUE_INIT(n);                                                          \
-    else {                                                                    \
-      QUEUE* q = QUEUE_HEAD(h);                                               \
-      QUEUE_SPLIT(h, q, n);                                                   \
-    }                                                                         \
-  }                                                                           \
-  while (0)
+static inline void uv__queue_insert_head(struct uv__queue* h,
+                                         struct uv__queue* q) {
+  q->next = h->next;
+  q->prev = h;
+  q->next->prev = q;
+  h->next = q;
+}
 
-#define QUEUE_INSERT_HEAD(h, q)                                               \
-  do {                                                                        \
-    QUEUE_NEXT(q) = QUEUE_NEXT(h);                                            \
-    QUEUE_PREV(q) = (h);                                                      \
-    QUEUE_NEXT_PREV(q) = (q);                                                 \
-    QUEUE_NEXT(h) = (q);                                                      \
-  }                                                                           \
-  while (0)
+static inline void uv__queue_insert_tail(struct uv__queue* h,
+                                         struct uv__queue* q) {
+  q->next = h;
+  q->prev = h->prev;
+  q->prev->next = q;
+  h->prev = q;
+}
 
-#define QUEUE_INSERT_TAIL(h, q)                                               \
-  do {                                                                        \
-    QUEUE_NEXT(q) = (h);                                                      \
-    QUEUE_PREV(q) = QUEUE_PREV(h);                                            \
-    QUEUE_PREV_NEXT(q) = (q);                                                 \
-    QUEUE_PREV(h) = (q);                                                      \
-  }                                                                           \
-  while (0)
-
-#define QUEUE_REMOVE(q)                                                       \
-  do {                                                                        \
-    QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q);                                       \
-    QUEUE_NEXT_PREV(q) = QUEUE_PREV(q);                                       \
-  }                                                                           \
-  while (0)
+static inline void uv__queue_remove(struct uv__queue* q) {
+  q->prev->next = q->next;
+  q->next->prev = q->prev;
+}
 
 #endif /* QUEUE_H_ */
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/thread-common.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/thread-common.cpp
new file mode 100644
index 0000000..c0e39b5
--- /dev/null
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/thread-common.cpp
@@ -0,0 +1,175 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * 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 "uv.h"
+#include "uv-common.h"
+
+#include <stdlib.h>
+#ifndef _WIN32
+#include <pthread.h>
+#endif
+
+#if defined(PTHREAD_BARRIER_SERIAL_THREAD)
+STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t));
+#endif
+
+/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */
+#if defined(_AIX) || \
+    defined(__OpenBSD__) || \
+    !defined(PTHREAD_BARRIER_SERIAL_THREAD)
+int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
+  int rc;
+#ifdef _WIN32
+  uv_barrier_t* b;
+  b = barrier;
+
+  if (barrier == NULL || count == 0)
+    return UV_EINVAL;
+#else
+  struct _uv_barrier* b;
+
+  if (barrier == NULL || count == 0)
+    return UV_EINVAL;
+
+  b = (struct _uv_barrier *)uv__malloc(sizeof(*b));
+  if (b == NULL)
+    return UV_ENOMEM;
+#endif
+
+  b->in = 0;
+  b->out = 0;
+  b->threshold = count;
+
+  rc = uv_mutex_init(&b->mutex);
+  if (rc != 0)
+    goto error2;
+
+  /* TODO(vjnash): remove these uv_cond_t casts in v2. */
+  rc = uv_cond_init((uv_cond_t*) &b->cond);
+  if (rc != 0)
+    goto error;
+
+#ifndef _WIN32
+  barrier->b = b;
+#endif
+  return 0;
+
+error:
+  uv_mutex_destroy(&b->mutex);
+error2:
+#ifndef _WIN32
+  uv__free(b);
+#endif
+  return rc;
+}
+
+
+int uv_barrier_wait(uv_barrier_t* barrier) {
+  int last;
+#ifdef _WIN32
+  uv_barrier_t* b;
+  b = barrier;
+#else
+  struct _uv_barrier* b;
+
+  if (barrier == NULL || barrier->b == NULL)
+    return UV_EINVAL;
+
+  b = barrier->b;
+#endif
+
+  uv_mutex_lock(&b->mutex);
+
+  while (b->out != 0)
+    uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex);
+
+  if (++b->in == b->threshold) {
+    b->in = 0;
+    b->out = b->threshold;
+    uv_cond_broadcast((uv_cond_t*) &b->cond);
+  } else {
+    do
+      uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex);
+    while (b->in != 0);
+  }
+
+  last = (--b->out == 0);
+  if (last)
+    uv_cond_broadcast((uv_cond_t*) &b->cond);
+
+  uv_mutex_unlock(&b->mutex);
+  return last;
+}
+
+
+void uv_barrier_destroy(uv_barrier_t* barrier) {
+#ifdef _WIN32
+  uv_barrier_t* b;
+  b = barrier;
+#else
+  struct _uv_barrier* b;
+  b = barrier->b;
+#endif
+
+  uv_mutex_lock(&b->mutex);
+
+  assert(b->in == 0);
+  while (b->out != 0)
+    uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex);
+
+  if (b->in != 0)
+    abort();
+
+  uv_mutex_unlock(&b->mutex);
+  uv_mutex_destroy(&b->mutex);
+  uv_cond_destroy((uv_cond_t*) &b->cond);
+
+#ifndef  _WIN32
+  uv__free(barrier->b);
+  barrier->b = NULL;
+#endif
+}
+
+#else
+
+int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
+  return UV__ERR(pthread_barrier_init(barrier, NULL, count));
+}
+
+
+int uv_barrier_wait(uv_barrier_t* barrier) {
+  int rc;
+
+  rc = pthread_barrier_wait(barrier);
+  if (rc != 0)
+    if (rc != PTHREAD_BARRIER_SERIAL_THREAD)
+      abort();
+
+  return rc == PTHREAD_BARRIER_SERIAL_THREAD;
+}
+
+
+void uv_barrier_destroy(uv_barrier_t* barrier) {
+  if (pthread_barrier_destroy(barrier))
+    abort();
+}
+
+#endif
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/threadpool.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/threadpool.cpp
index 718972c..aa282af 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/threadpool.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/threadpool.cpp
@@ -41,10 +41,10 @@
 static unsigned int nthreads;
 static uv_thread_t* threads;
 static uv_thread_t default_threads[4];
-static QUEUE exit_message;
-static QUEUE wq;
-static QUEUE run_slow_work_message;
-static QUEUE slow_io_pending_wq;
+static struct uv__queue exit_message;
+static struct uv__queue wq;
+static struct uv__queue run_slow_work_message;
+static struct uv__queue slow_io_pending_wq;
 
 static unsigned int slow_work_thread_threshold(void) {
   return (nthreads + 1) / 2;
@@ -60,7 +60,7 @@
  */
 static void worker(void* arg) {
   struct uv__work* w;
-  QUEUE* q;
+  struct uv__queue* q;
   int is_slow_work;
 
   uv_sem_post((uv_sem_t*) arg);
@@ -72,49 +72,49 @@
 
     /* Keep waiting while either no work is present or only slow I/O
        and we're at the threshold for that. */
-    while (QUEUE_EMPTY(&wq) ||
-           (QUEUE_HEAD(&wq) == &run_slow_work_message &&
-            QUEUE_NEXT(&run_slow_work_message) == &wq &&
+    while (uv__queue_empty(&wq) ||
+           (uv__queue_head(&wq) == &run_slow_work_message &&
+            uv__queue_next(&run_slow_work_message) == &wq &&
             slow_io_work_running >= slow_work_thread_threshold())) {
       idle_threads += 1;
       uv_cond_wait(&cond, &mutex);
       idle_threads -= 1;
     }
 
-    q = QUEUE_HEAD(&wq);
+    q = uv__queue_head(&wq);
     if (q == &exit_message) {
       uv_cond_signal(&cond);
       uv_mutex_unlock(&mutex);
       break;
     }
 
-    QUEUE_REMOVE(q);
-    QUEUE_INIT(q);  /* Signal uv_cancel() that the work req is executing. */
+    uv__queue_remove(q);
+    uv__queue_init(q);  /* Signal uv_cancel() that the work req is executing. */
 
     is_slow_work = 0;
     if (q == &run_slow_work_message) {
       /* If we're at the slow I/O threshold, re-schedule until after all
          other work in the queue is done. */
       if (slow_io_work_running >= slow_work_thread_threshold()) {
-        QUEUE_INSERT_TAIL(&wq, q);
+        uv__queue_insert_tail(&wq, q);
         continue;
       }
 
       /* If we encountered a request to run slow I/O work but there is none
          to run, that means it's cancelled => Start over. */
-      if (QUEUE_EMPTY(&slow_io_pending_wq))
+      if (uv__queue_empty(&slow_io_pending_wq))
         continue;
 
       is_slow_work = 1;
       slow_io_work_running++;
 
-      q = QUEUE_HEAD(&slow_io_pending_wq);
-      QUEUE_REMOVE(q);
-      QUEUE_INIT(q);
+      q = uv__queue_head(&slow_io_pending_wq);
+      uv__queue_remove(q);
+      uv__queue_init(q);
 
       /* If there is more slow I/O work, schedule it to be run as well. */
-      if (!QUEUE_EMPTY(&slow_io_pending_wq)) {
-        QUEUE_INSERT_TAIL(&wq, &run_slow_work_message);
+      if (!uv__queue_empty(&slow_io_pending_wq)) {
+        uv__queue_insert_tail(&wq, &run_slow_work_message);
         if (idle_threads > 0)
           uv_cond_signal(&cond);
       }
@@ -122,13 +122,13 @@
 
     uv_mutex_unlock(&mutex);
 
-    w = QUEUE_DATA(q, struct uv__work, wq);
+    w = uv__queue_data(q, struct uv__work, wq);
     w->work(w);
 
     uv_mutex_lock(&w->loop->wq_mutex);
     w->work = NULL;  /* Signal uv_cancel() that the work req is done
                         executing. */
-    QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq);
+    uv__queue_insert_tail(&w->loop->wq, &w->wq);
     uv_async_send(&w->loop->wq_async);
     uv_mutex_unlock(&w->loop->wq_mutex);
 
@@ -143,12 +143,12 @@
 }
 
 
-static void post(QUEUE* q, enum uv__work_kind kind) {
+static void post(struct uv__queue* q, enum uv__work_kind kind) {
   uv_mutex_lock(&mutex);
   if (kind == UV__WORK_SLOW_IO) {
     /* Insert into a separate queue. */
-    QUEUE_INSERT_TAIL(&slow_io_pending_wq, q);
-    if (!QUEUE_EMPTY(&run_slow_work_message)) {
+    uv__queue_insert_tail(&slow_io_pending_wq, q);
+    if (!uv__queue_empty(&run_slow_work_message)) {
       /* Running slow I/O tasks is already scheduled => Nothing to do here.
          The worker that runs said other task will schedule this one as well. */
       uv_mutex_unlock(&mutex);
@@ -157,7 +157,7 @@
     q = &run_slow_work_message;
   }
 
-  QUEUE_INSERT_TAIL(&wq, q);
+  uv__queue_insert_tail(&wq, q);
   if (idle_threads > 0)
     uv_cond_signal(&cond);
   uv_mutex_unlock(&mutex);
@@ -195,6 +195,7 @@
 
 
 static void init_threads(void) {
+  uv_thread_options_t config;
   unsigned int i;
   const char* val;
   uv_sem_t sem;
@@ -223,15 +224,18 @@
   if (uv_mutex_init(&mutex))
     abort();
 
-  QUEUE_INIT(&wq);
-  QUEUE_INIT(&slow_io_pending_wq);
-  QUEUE_INIT(&run_slow_work_message);
+  uv__queue_init(&wq);
+  uv__queue_init(&slow_io_pending_wq);
+  uv__queue_init(&run_slow_work_message);
 
   if (uv_sem_init(&sem, 0))
     abort();
 
+  config.flags = UV_THREAD_HAS_STACK_SIZE;
+  config.stack_size = 8u << 20;  /* 8 MB */
+
   for (i = 0; i < nthreads; i++)
-    if (uv_thread_create(threads + i, worker, &sem))
+    if (uv_thread_create_ex(threads + i, &config, worker, &sem))
       abort();
 
   for (i = 0; i < nthreads; i++)
@@ -275,15 +279,19 @@
 }
 
 
+/* TODO(bnoordhuis) teach libuv how to cancel file operations
+ * that go through io_uring instead of the thread pool.
+ */
 static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) {
   int cancelled;
 
+  uv_once(&once, init_once);  /* Ensure |mutex| is initialized. */
   uv_mutex_lock(&mutex);
   uv_mutex_lock(&w->loop->wq_mutex);
 
-  cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL;
+  cancelled = !uv__queue_empty(&w->wq) && w->work != NULL;
   if (cancelled)
-    QUEUE_REMOVE(&w->wq);
+    uv__queue_remove(&w->wq);
 
   uv_mutex_unlock(&w->loop->wq_mutex);
   uv_mutex_unlock(&mutex);
@@ -293,7 +301,7 @@
 
   w->work = uv__cancelled;
   uv_mutex_lock(&loop->wq_mutex);
-  QUEUE_INSERT_TAIL(&loop->wq, &w->wq);
+  uv__queue_insert_tail(&loop->wq, &w->wq);
   uv_async_send(&loop->wq_async);
   uv_mutex_unlock(&loop->wq_mutex);
 
@@ -304,22 +312,39 @@
 void uv__work_done(uv_async_t* handle) {
   struct uv__work* w;
   uv_loop_t* loop;
-  QUEUE* q;
-  QUEUE wq;
+  struct uv__queue* q;
+  struct uv__queue wq;
   int err;
+  int nevents;
 
   loop = container_of(handle, uv_loop_t, wq_async);
   uv_mutex_lock(&loop->wq_mutex);
-  QUEUE_MOVE(&loop->wq, &wq);
+  uv__queue_move(&loop->wq, &wq);
   uv_mutex_unlock(&loop->wq_mutex);
 
-  while (!QUEUE_EMPTY(&wq)) {
-    q = QUEUE_HEAD(&wq);
-    QUEUE_REMOVE(q);
+  nevents = 0;
+
+  while (!uv__queue_empty(&wq)) {
+    q = uv__queue_head(&wq);
+    uv__queue_remove(q);
 
     w = container_of(q, struct uv__work, wq);
     err = (w->work == uv__cancelled) ? UV_ECANCELED : 0;
     w->done(w, err);
+    nevents++;
+  }
+
+  /* This check accomplishes 2 things:
+   * 1. Even if the queue was empty, the call to uv__work_done() should count
+   *    as an event. Which will have been added by the event loop when
+   *    calling this callback.
+   * 2. Prevents accidental wrap around in case nevents == 0 events == 0.
+   */
+  if (nevents > 1) {
+    /* Subtract 1 to counter the call to uv__work_done(). */
+    uv__metrics_inc_events(loop, nevents - 1);
+    if (uv__get_internal_fields(loop)->current_timeout == 0)
+      uv__metrics_inc_events_waiting(loop, nevents - 1);
   }
 }
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/async.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/async.cpp
index e1805c3..fef4ae9 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/async.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/async.cpp
@@ -24,7 +24,6 @@
 
 #include "uv.h"
 #include "internal.h"
-#include "atomic-ops.h"
 
 #include <errno.h>
 #include <stdio.h>  /* snprintf() */
@@ -38,8 +37,11 @@
 #include <sys/eventfd.h>
 #endif
 
+#include <atomic>
+
 static void uv__async_send(uv_loop_t* loop);
 static int uv__async_start(uv_loop_t* loop);
+static void uv__cpu_relax(void);
 
 
 int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
@@ -52,8 +54,9 @@
   uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC);
   handle->async_cb = async_cb;
   handle->pending = 0;
+  handle->u.fd = 0; /* This will be used as a busy flag. */
 
-  QUEUE_INSERT_TAIL(&loop->async_handles, &handle->queue);
+  uv__queue_insert_tail(&loop->async_handles, &handle->queue);
   uv__handle_start(handle);
 
   return 0;
@@ -61,46 +64,54 @@
 
 
 int uv_async_send(uv_async_t* handle) {
+  std::atomic<int>* pending;
+  std::atomic<int>* busy;
+
+  pending = (std::atomic<int>*) &handle->pending;
+  busy = (std::atomic<int>*) &handle->u.fd;
+
   /* Do a cheap read first. */
-  if (ACCESS_ONCE(int, handle->pending) != 0)
+  if (atomic_load_explicit(pending, std::memory_order_relaxed) != 0)
     return 0;
 
-  /* Tell the other thread we're busy with the handle. */
-  if (cmpxchgi(&handle->pending, 0, 1) != 0)
-    return 0;
+  /* Set the loop to busy. */
+  atomic_fetch_add(busy, 1);
 
   /* Wake up the other thread's event loop. */
-  uv__async_send(handle->loop);
+  if (atomic_exchange(pending, 1) == 0)
+    uv__async_send(handle->loop);
 
-  /* Tell the other thread we're done. */
-  if (cmpxchgi(&handle->pending, 1, 2) != 1)
-    abort();
+  /* Set the loop to not-busy. */
+  atomic_fetch_add(busy, -1);
 
   return 0;
 }
 
 
-/* Only call this from the event loop thread. */
-static int uv__async_spin(uv_async_t* handle) {
+/* Wait for the busy flag to clear before closing.
+ * Only call this from the event loop thread. */
+static void uv__async_spin(uv_async_t* handle) {
+  std::atomic<int>* pending;
+  std::atomic<int>* busy;
   int i;
-  int rc;
+
+  pending = (std::atomic<int>*) &handle->pending;
+  busy = (std::atomic<int>*) &handle->u.fd;
+
+  /* Set the pending flag first, so no new events will be added by other
+   * threads after this function returns. */
+  atomic_store(pending, 1);
 
   for (;;) {
-    /* 997 is not completely chosen at random. It's a prime number, acyclical
-     * by nature, and should therefore hopefully dampen sympathetic resonance.
+    /* 997 is not completely chosen at random. It's a prime number, acyclic by
+     * nature, and should therefore hopefully dampen sympathetic resonance.
      */
     for (i = 0; i < 997; i++) {
-      /* rc=0 -- handle is not pending.
-       * rc=1 -- handle is pending, other thread is still working with it.
-       * rc=2 -- handle is pending, other thread is done.
-       */
-      rc = cmpxchgi(&handle->pending, 2, 0);
-
-      if (rc != 1)
-        return rc;
+      if (atomic_load(busy) == 0)
+        return;
 
       /* Other thread is busy with this handle, spin until it's done. */
-      cpu_relax();
+      uv__cpu_relax();
     }
 
     /* Yield the CPU. We may have preempted the other thread while it's
@@ -114,7 +125,7 @@
 
 void uv__async_close(uv_async_t* handle) {
   uv__async_spin(handle);
-  QUEUE_REMOVE(&handle->queue);
+  uv__queue_remove(&handle->queue);
   uv__handle_stop(handle);
 }
 
@@ -122,9 +133,10 @@
 static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
   char buf[1024];
   ssize_t r;
-  QUEUE queue;
-  QUEUE* q;
+  struct uv__queue queue;
+  struct uv__queue* q;
   uv_async_t* h;
+  std::atomic<int> *pending;
 
   assert(w == &loop->async_io_watcher);
 
@@ -146,16 +158,18 @@
     abort();
   }
 
-  QUEUE_MOVE(&loop->async_handles, &queue);
-  while (!QUEUE_EMPTY(&queue)) {
-    q = QUEUE_HEAD(&queue);
-    h = QUEUE_DATA(q, uv_async_t, queue);
+  uv__queue_move(&loop->async_handles, &queue);
+  while (!uv__queue_empty(&queue)) {
+    q = uv__queue_head(&queue);
+    h = uv__queue_data(q, uv_async_t, queue);
 
-    QUEUE_REMOVE(q);
-    QUEUE_INSERT_TAIL(&loop->async_handles, q);
+    uv__queue_remove(q);
+    uv__queue_insert_tail(&loop->async_handles, q);
 
-    if (0 == uv__async_spin(h))
-      continue;  /* Not pending. */
+    /* Atomically fetch and clear pending flag */
+    pending = (std::atomic<int>*) &h->pending;
+    if (atomic_exchange(pending, 0) == 0)
+      continue;
 
     if (h->async_cb == NULL)
       continue;
@@ -227,20 +241,28 @@
 }
 
 
-int uv__async_fork(uv_loop_t* loop) {
-  if (loop->async_io_watcher.fd == -1) /* never started */
-    return 0;
-
-  uv__async_stop(loop);
-
-  return uv__async_start(loop);
-}
-
-
 void uv__async_stop(uv_loop_t* loop) {
+  struct uv__queue queue;
+  struct uv__queue* q;
+  uv_async_t* h;
+
   if (loop->async_io_watcher.fd == -1)
     return;
 
+  /* Make sure no other thread is accessing the async handle fd after the loop
+   * cleanup.
+   */
+  uv__queue_move(&loop->async_handles, &queue);
+  while (!uv__queue_empty(&queue)) {
+    q = uv__queue_head(&queue);
+    h = uv__queue_data(q, uv_async_t, queue);
+
+    uv__queue_remove(q);
+    uv__queue_insert_tail(&loop->async_handles, q);
+
+    uv__async_spin(h);
+  }
+
   if (loop->async_wfd != -1) {
     if (loop->async_wfd != loop->async_io_watcher.fd)
       uv__close(loop->async_wfd);
@@ -251,3 +273,58 @@
   uv__close(loop->async_io_watcher.fd);
   loop->async_io_watcher.fd = -1;
 }
+
+
+int uv__async_fork(uv_loop_t* loop) {
+  struct uv__queue queue;
+  struct uv__queue* q;
+  uv_async_t* h;
+
+  if (loop->async_io_watcher.fd == -1) /* never started */
+    return 0;
+
+  uv__queue_move(&loop->async_handles, &queue);
+  while (!uv__queue_empty(&queue)) {
+    q = uv__queue_head(&queue);
+    h = uv__queue_data(q, uv_async_t, queue);
+
+    uv__queue_remove(q);
+    uv__queue_insert_tail(&loop->async_handles, q);
+
+    /* The state of any thread that set pending is now likely corrupt in this
+     * child because the user called fork, so just clear these flags and move
+     * on. Calling most libc functions after `fork` is declared to be undefined
+     * behavior anyways, unless async-signal-safe, for multithreaded programs
+     * like libuv, and nothing interesting in pthreads is async-signal-safe.
+     */
+    h->pending = 0;
+    /* This is the busy flag, and we just abruptly lost all other threads. */
+    h->u.fd = 0;
+  }
+
+  /* Recreate these, since they still exist, but belong to the wrong pid now. */
+  if (loop->async_wfd != -1) {
+    if (loop->async_wfd != loop->async_io_watcher.fd)
+      uv__close(loop->async_wfd);
+    loop->async_wfd = -1;
+  }
+
+  uv__io_stop(loop, &loop->async_io_watcher, POLLIN);
+  uv__close(loop->async_io_watcher.fd);
+  loop->async_io_watcher.fd = -1;
+
+  return uv__async_start(loop);
+}
+
+
+static void uv__cpu_relax(void) {
+#if defined(__i386__) || defined(__x86_64__)
+  __asm__ __volatile__ ("rep; nop" ::: "memory");  /* a.k.a. PAUSE */
+#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__)
+  __asm__ __volatile__ ("yield" ::: "memory");
+#elif (defined(__ppc__) || defined(__ppc64__)) && defined(__APPLE__)
+  __asm volatile ("" : : : "memory");
+#elif !defined(__APPLE__) && (defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__))
+  __asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory");
+#endif
+}
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/atomic-ops.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/atomic-ops.h
deleted file mode 100644
index 58043c4..0000000
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/atomic-ops.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef UV_ATOMIC_OPS_H_
-#define UV_ATOMIC_OPS_H_
-
-#include "internal.h"  /* UV_UNUSED */
-
-#if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-#include <atomic.h>
-#endif
-
-UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval));
-UV_UNUSED(static void cpu_relax(void));
-
-/* Prefer hand-rolled assembly over the gcc builtins because the latter also
- * issue full memory barriers.
- */
-UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) {
-#if defined(__i386__) || defined(__x86_64__)
-  int out;
-  __asm__ __volatile__ ("lock; cmpxchg %2, %1;"
-                        : "=a" (out), "+m" (*(volatile int*) ptr)
-                        : "r" (newval), "0" (oldval)
-                        : "memory");
-  return out;
-#elif defined(__MVS__)
-  /* Use hand-rolled assembly because codegen from builtin __plo_CSST results in
-   * a runtime bug.
-   */
-  __asm(" cs %0,%2,%1 \n " : "+r"(oldval), "+m"(*ptr) : "r"(newval) :);
-  return oldval;
-#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-  return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval);
-#else
-  return __sync_val_compare_and_swap(ptr, oldval, newval);
-#endif
-}
-
-UV_UNUSED(static void cpu_relax(void)) {
-#if defined(__i386__) || defined(__x86_64__)
-  __asm__ __volatile__ ("rep; nop" ::: "memory");  /* a.k.a. PAUSE */
-#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__)
-  __asm__ __volatile__ ("yield" ::: "memory");
-#elif (defined(__ppc__) || defined(__ppc64__)) && defined(__APPLE__)
-  __asm volatile ("" : : : "memory");
-#elif !defined(__APPLE__) && (defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__))
-  __asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory");
-#endif
-}
-
-#endif  /* UV_ATOMIC_OPS_H_ */
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/core.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/core.cpp
index 4c23f60..ce7fd2c 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/core.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/core.cpp
@@ -41,12 +41,14 @@
 #include <sys/uio.h> /* writev */
 #include <sys/resource.h> /* getrusage */
 #include <pwd.h>
+#include <grp.h>
 #include <sys/utsname.h>
 #include <sys/time.h>
+#include <time.h> /* clock_gettime */
+#include <atomic>
 
 #ifdef __sun
 # include <sys/filio.h>
-# include <sys/types.h>
 # include <sys/wait.h>
 #endif
 
@@ -66,13 +68,14 @@
 
 #if defined(__DragonFly__)      || \
     defined(__FreeBSD__)        || \
-    defined(__FreeBSD_kernel__) || \
     defined(__NetBSD__)         || \
     defined(__OpenBSD__)
 # include <sys/sysctl.h>
 # include <sys/filio.h>
 # include <sys/wait.h>
+# include <sys/param.h>
 # if defined(__FreeBSD__)
+#  include <sys/cpuset.h>
 #  define uv__accept4 accept4
 # endif
 # if defined(__NetBSD__)
@@ -107,6 +110,35 @@
 STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len));
 
 
+/* https://github.com/libuv/libuv/issues/1674 */
+int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) {
+  struct timespec t;
+  int r;
+
+  if (ts == NULL)
+    return UV_EFAULT;
+
+  switch (clock_id) {
+    default:
+      return UV_EINVAL;
+    case UV_CLOCK_MONOTONIC:
+      r = clock_gettime(CLOCK_MONOTONIC, &t);
+      break;
+    case UV_CLOCK_REALTIME:
+      r = clock_gettime(CLOCK_REALTIME, &t);
+      break;
+  }
+
+  if (r)
+    return UV__ERR(errno);
+
+  ts->tv_sec = t.tv_sec;
+  ts->tv_nsec = t.tv_nsec;
+
+  return 0;
+}
+
+
 uint64_t uv_hrtime(void) {
   return uv__hrtime(UV_CLOCK_PRECISE);
 }
@@ -232,10 +264,10 @@
 #if defined(IOV_MAX)
   return IOV_MAX;
 #elif defined(_SC_IOV_MAX)
-  static int iovmax_cached = -1;
+  static std::atomic<int> iovmax_cached = -1;
   int iovmax;
 
-  iovmax = uv__load_relaxed(&iovmax_cached);
+  iovmax = atomic_load_explicit(&iovmax_cached, memory_order_relaxed);
   if (iovmax != -1)
     return iovmax;
 
@@ -247,7 +279,7 @@
   if (iovmax == -1)
     iovmax = 1;
 
-  uv__store_relaxed(&iovmax_cached, iovmax);
+  atomic_store_explicit(&iovmax_cached, iovmax, memory_order_relaxed);
 
   return iovmax;
 #else
@@ -313,7 +345,7 @@
   }
 
   uv__handle_unref(handle);
-  QUEUE_REMOVE(&handle->handle_queue);
+  uv__queue_remove(&handle->handle_queue);
 
   if (handle->close_cb) {
     handle->close_cb(handle);
@@ -349,7 +381,7 @@
 static int uv__loop_alive(const uv_loop_t* loop) {
   return uv__has_active_handles(loop) ||
          uv__has_active_reqs(loop) ||
-         !QUEUE_EMPTY(&loop->pending_queue) ||
+         !uv__queue_empty(&loop->pending_queue) ||
          loop->closing_handles != NULL;
 }
 
@@ -358,8 +390,9 @@
   if (loop->stop_flag == 0 &&
       /* uv__loop_alive(loop) && */
       (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) &&
-      QUEUE_EMPTY(&loop->pending_queue) &&
-      QUEUE_EMPTY(&loop->idle_handles) &&
+      uv__queue_empty(&loop->pending_queue) &&
+      uv__queue_empty(&loop->idle_handles) &&
+      (loop->flags & UV_LOOP_REAP_CHILDREN) == 0 &&
       loop->closing_handles == NULL)
     return uv__next_timeout(loop);
   return 0;
@@ -367,7 +400,7 @@
 
 
 int uv_backend_timeout(const uv_loop_t* loop) {
-  if (QUEUE_EMPTY(&loop->watcher_queue))
+  if (uv__queue_empty(&loop->watcher_queue))
     return uv__backend_timeout(loop);
   /* Need to call uv_run to update the backend fd state. */
   return 0;
@@ -388,12 +421,19 @@
   if (!r)
     uv__update_time(loop);
 
-  while (r != 0 && loop->stop_flag == 0) {
+  /* Maintain backwards compatibility by processing timers before entering the
+   * while loop for UV_RUN_DEFAULT. Otherwise timers only need to be executed
+   * once, which should be done after polling in order to maintain proper
+   * execution order of the conceptual event loop. */
+  if (mode == UV_RUN_DEFAULT && r != 0 && loop->stop_flag == 0) {
     uv__update_time(loop);
     uv__run_timers(loop);
+  }
 
+  while (r != 0 && loop->stop_flag == 0) {
     can_sleep =
-        QUEUE_EMPTY(&loop->pending_queue) && QUEUE_EMPTY(&loop->idle_handles);
+        uv__queue_empty(&loop->pending_queue) &&
+        uv__queue_empty(&loop->idle_handles);
 
     uv__run_pending(loop);
     uv__run_idle(loop);
@@ -403,11 +443,13 @@
     if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
       timeout = uv__backend_timeout(loop);
 
+    uv__metrics_inc_loop_count(loop);
+
     uv__io_poll(loop, timeout);
 
     /* Process immediate callbacks (e.g. write_cb) a small fixed number of
      * times to avoid loop starvation.*/
-    for (r = 0; r < 8 && !QUEUE_EMPTY(&loop->pending_queue); r++)
+    for (r = 0; r < 8 && !uv__queue_empty(&loop->pending_queue); r++)
       uv__run_pending(loop);
 
     /* Run one final update on the provider_idle_time in case uv__io_poll
@@ -420,18 +462,8 @@
     uv__run_check(loop);
     uv__run_closing_handles(loop);
 
-    if (mode == UV_RUN_ONCE) {
-      /* UV_RUN_ONCE implies forward progress: at least one callback must have
-       * been invoked when it returns. uv__io_poll() can return without doing
-       * I/O (meaning: no callbacks) when its timeout expires - which means we
-       * have pending timers that satisfy the forward progress constraint.
-       *
-       * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
-       * the check.
-       */
-      uv__update_time(loop);
-      uv__run_timers(loop);
-    }
+    uv__update_time(loop);
+    uv__run_timers(loop);
 
     r = uv__loop_alive(loop);
     if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
@@ -805,17 +837,17 @@
 
 
 static void uv__run_pending(uv_loop_t* loop) {
-  QUEUE* q;
-  QUEUE pq;
+  struct uv__queue* q;
+  struct uv__queue pq;
   uv__io_t* w;
 
-  QUEUE_MOVE(&loop->pending_queue, &pq);
+  uv__queue_move(&loop->pending_queue, &pq);
 
-  while (!QUEUE_EMPTY(&pq)) {
-    q = QUEUE_HEAD(&pq);
-    QUEUE_REMOVE(q);
-    QUEUE_INIT(q);
-    w = QUEUE_DATA(q, uv__io_t, pending_queue);
+  while (!uv__queue_empty(&pq)) {
+    q = uv__queue_head(&pq);
+    uv__queue_remove(q);
+    uv__queue_init(q);
+    w = uv__queue_data(q, uv__io_t, pending_queue);
     w->cb(loop, w, POLLOUT);
   }
 }
@@ -862,7 +894,7 @@
   watchers[nwatchers] = fake_watcher_list;
   watchers[nwatchers + 1] = fake_watcher_count;
 
-  loop->watchers = watchers;
+  loop->watchers = (uv__io_t**)watchers;
   loop->nwatchers = nwatchers;
 }
 
@@ -870,17 +902,12 @@
 void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) {
   assert(cb != NULL);
   assert(fd >= -1);
-  QUEUE_INIT(&w->pending_queue);
-  QUEUE_INIT(&w->watcher_queue);
+  uv__queue_init(&w->pending_queue);
+  uv__queue_init(&w->watcher_queue);
   w->cb = cb;
   w->fd = fd;
   w->events = 0;
   w->pevents = 0;
-
-#if defined(UV_HAVE_KQUEUE)
-  w->rcount = 0;
-  w->wcount = 0;
-#endif /* defined(UV_HAVE_KQUEUE) */
 }
 
 
@@ -902,8 +929,8 @@
     return;
 #endif
 
-  if (QUEUE_EMPTY(&w->watcher_queue))
-    QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
+  if (uv__queue_empty(&w->watcher_queue))
+    uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue);
 
   if (loop->watchers[w->fd] == NULL) {
     loop->watchers[w->fd] = w;
@@ -928,8 +955,8 @@
   w->pevents &= ~events;
 
   if (w->pevents == 0) {
-    QUEUE_REMOVE(&w->watcher_queue);
-    QUEUE_INIT(&w->watcher_queue);
+    uv__queue_remove(&w->watcher_queue);
+    uv__queue_init(&w->watcher_queue);
     w->events = 0;
 
     if (w == loop->watchers[w->fd]) {
@@ -938,14 +965,14 @@
       loop->nfds--;
     }
   }
-  else if (QUEUE_EMPTY(&w->watcher_queue))
-    QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
+  else if (uv__queue_empty(&w->watcher_queue))
+    uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue);
 }
 
 
 void uv__io_close(uv_loop_t* loop, uv__io_t* w) {
   uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
-  QUEUE_REMOVE(&w->pending_queue);
+  uv__queue_remove(&w->pending_queue);
 
   /* Remove stale events for this file descriptor */
   if (w->fd != -1)
@@ -954,8 +981,8 @@
 
 
 void uv__io_feed(uv_loop_t* loop, uv__io_t* w) {
-  if (QUEUE_EMPTY(&w->pending_queue))
-    QUEUE_INSERT_TAIL(&loop->pending_queue, &w->pending_queue);
+  if (uv__queue_empty(&w->pending_queue))
+    uv__queue_insert_tail(&loop->pending_queue, &w->pending_queue);
 }
 
 
@@ -1000,6 +1027,15 @@
   rusage->ru_nivcsw = usage.ru_nivcsw;
 #endif
 
+  /* Most platforms report ru_maxrss in kilobytes; macOS and Solaris are
+   * the outliers because of course they are.
+   */
+#if defined(__APPLE__)
+  rusage->ru_maxrss /= 1024;                  /* macOS and iOS report bytes. */
+#elif defined(__sun)
+  rusage->ru_maxrss /= getpagesize() / 1024;  /* Solaris reports pages. */
+#endif
+
   return 0;
 }
 
@@ -1099,8 +1135,8 @@
   if (r != UV_ENOENT)
     return r;
 
-  /* HOME is not set, so call uv__getpwuid_r() */
-  r = uv__getpwuid_r(&pwd);
+  /* HOME is not set, so call uv_os_get_passwd() */
+  r = uv_os_get_passwd(&pwd);
 
   if (r != 0) {
     return r;
@@ -1173,11 +1209,10 @@
 }
 
 
-int uv__getpwuid_r(uv_passwd_t* pwd) {
+static int uv__getpwuid_r(uv_passwd_t *pwd, uid_t uid) {
   struct passwd pw;
   struct passwd* result;
   char* buf;
-  uid_t uid;
   size_t bufsize;
   size_t name_size;
   size_t homedir_size;
@@ -1187,8 +1222,6 @@
   if (pwd == NULL)
     return UV_EINVAL;
 
-  uid = geteuid();
-
   /* Calling sysconf(_SC_GETPW_R_SIZE_MAX) would get the suggested size, but it
    * is frequently 1024 or 4096, so we can just use that directly. The pwent
    * will not usually be large. */
@@ -1247,24 +1280,98 @@
 }
 
 
-void uv_os_free_passwd(uv_passwd_t* pwd) {
-  if (pwd == NULL)
-    return;
+int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) {
+#if defined(__ANDROID__) && __ANDROID_API__ < 24
+  /* This function getgrgid_r() was added in Android N (level 24) */
+  return UV_ENOSYS;
+#else
+  struct group gp;
+  struct group* result;
+  char* buf;
+  char* gr_mem;
+  size_t bufsize;
+  size_t name_size;
+  long members;
+  size_t mem_size;
+  int r;
 
-  /*
-    The memory for name, shell, and homedir are allocated in a single
-    uv__malloc() call. The base of the pointer is stored in pwd->username, so
-    that is the field that needs to be freed.
-  */
-  uv__free(pwd->username);
-  pwd->username = NULL;
-  pwd->shell = NULL;
-  pwd->homedir = NULL;
+  if (grp == NULL)
+    return UV_EINVAL;
+
+  /* Calling sysconf(_SC_GETGR_R_SIZE_MAX) would get the suggested size, but it
+   * is frequently 1024 or 4096, so we can just use that directly. The pwent
+   * will not usually be large. */
+  for (bufsize = 2000;; bufsize *= 2) {
+    buf = (char*)uv__malloc(bufsize);
+
+    if (buf == NULL)
+      return UV_ENOMEM;
+
+    do
+      r = getgrgid_r(gid, &gp, buf, bufsize, &result);
+    while (r == EINTR);
+
+    if (r != 0 || result == NULL)
+      uv__free(buf);
+
+    if (r != ERANGE)
+      break;
+  }
+
+  if (r != 0)
+    return UV__ERR(r);
+
+  if (result == NULL)
+    return UV_ENOENT;
+
+  /* Allocate memory for the groupname and members. */
+  name_size = strlen(gp.gr_name) + 1;
+  members = 0;
+  mem_size = sizeof(char*);
+  for (r = 0; gp.gr_mem[r] != NULL; r++) {
+    mem_size += strlen(gp.gr_mem[r]) + 1 + sizeof(char*);
+    members++;
+  }
+
+  gr_mem = (char*)uv__malloc(name_size + mem_size);
+  if (gr_mem == NULL) {
+    uv__free(buf);
+    return UV_ENOMEM;
+  }
+
+  /* Copy the members */
+  grp->members = (char**) gr_mem;
+  grp->members[members] = NULL;
+  gr_mem = (char*) &grp->members[members + 1];
+  for (r = 0; r < members; r++) {
+    grp->members[r] = gr_mem;
+    strcpy(gr_mem, gp.gr_mem[r]);
+    gr_mem += strlen(gr_mem) + 1;
+  }
+  assert(gr_mem == (char*)grp->members + mem_size);
+
+  /* Copy the groupname */
+  grp->groupname = gr_mem;
+  memcpy(grp->groupname, gp.gr_name, name_size);
+  gr_mem += name_size;
+
+  /* Copy the gid */
+  grp->gid = gp.gr_gid;
+
+  uv__free(buf);
+
+  return 0;
+#endif
 }
 
 
 int uv_os_get_passwd(uv_passwd_t* pwd) {
-  return uv__getpwuid_r(pwd);
+  return uv__getpwuid_r(pwd, geteuid());
+}
+
+
+int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) {
+  return uv__getpwuid_r(pwd, uid);
 }
 
 
@@ -1425,6 +1532,13 @@
   return getppid();
 }
 
+int uv_cpumask_size(void) {
+#if UV__CPU_AFFINITY_SUPPORTED
+  return CPU_SETSIZE;
+#else
+  return UV_ENOTSUP;
+#endif
+}
 
 int uv_os_getpriority(uv_pid_t pid, int* priority) {
   int r;
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/cygwin.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/cygwin.cpp
index 169958d..4e54139 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/cygwin.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/cygwin.cpp
@@ -51,3 +51,7 @@
 uint64_t uv_get_constrained_memory(void) {
   return 0;  /* Memory constraints are unknown. */
 }
+
+uint64_t uv_get_available_memory(void) {
+  return uv_get_free_memory();
+}
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin-stub.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin-stub.h
index 433e3ef..b93cf67 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin-stub.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin-stub.h
@@ -27,7 +27,6 @@
 struct CFArrayCallBacks;
 struct CFRunLoopSourceContext;
 struct FSEventStreamContext;
-struct CFRange;
 
 typedef double CFAbsoluteTime;
 typedef double CFTimeInterval;
@@ -43,23 +42,13 @@
 typedef void* CFAllocatorRef;
 typedef void* CFArrayRef;
 typedef void* CFBundleRef;
-typedef void* CFDataRef;
 typedef void* CFDictionaryRef;
-typedef void* CFMutableDictionaryRef;
-typedef struct CFRange CFRange;
 typedef void* CFRunLoopRef;
 typedef void* CFRunLoopSourceRef;
 typedef void* CFStringRef;
 typedef void* CFTypeRef;
 typedef void* FSEventStreamRef;
 
-typedef uint32_t IOOptionBits;
-typedef unsigned int io_iterator_t;
-typedef unsigned int io_object_t;
-typedef unsigned int io_service_t;
-typedef unsigned int io_registry_entry_t;
-
-
 typedef void (*FSEventStreamCallback)(const FSEventStreamRef,
                                       void*,
                                       size_t,
@@ -80,11 +69,6 @@
   void* pad[3];
 };
 
-struct CFRange {
-  CFIndex location;
-  CFIndex length;
-};
-
 static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100;
 static const OSStatus noErr = 0;
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin.cpp
index ed51a6a..9ee5cd8 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/darwin.cpp
@@ -33,13 +33,10 @@
 #include <sys/sysctl.h>
 #include <unistd.h>  /* sysconf */
 
-#include "darwin-stub.h"
-
 static uv_once_t once = UV_ONCE_INIT;
 static uint64_t (*time_func)(void);
 static mach_timebase_info_data_t timebase;
 
-typedef unsigned char UInt8;
 
 int uv__platform_loop_init(uv_loop_t* loop) {
   loop->cf_state = NULL;
@@ -110,7 +107,7 @@
 
   if (host_statistics(mach_host_self(), HOST_VM_INFO,
                       (host_info_t)&info, &count) != KERN_SUCCESS) {
-    return UV_EINVAL;  /* FIXME(bnoordhuis) Translate error. */
+    return 0;
   }
 
   return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE);
@@ -123,7 +120,7 @@
   size_t size = sizeof(info);
 
   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
-    return UV__ERR(errno);
+    return 0;
 
   return (uint64_t) info;
 }
@@ -134,6 +131,11 @@
 }
 
 
+uint64_t uv_get_available_memory(void) {
+  return uv_get_free_memory();
+}
+
+
 void uv_loadavg(double avg[3]) {
   struct loadavg info;
   size_t size = sizeof(info);
@@ -183,164 +185,17 @@
   return 0;
 }
 
-static int uv__get_cpu_speed(uint64_t* speed) {
-  /* IOKit */
-  void (*pIOObjectRelease)(io_object_t);
-  kern_return_t (*pIOMasterPort)(mach_port_t, mach_port_t*);
-  CFMutableDictionaryRef (*pIOServiceMatching)(const char*);
-  kern_return_t (*pIOServiceGetMatchingServices)(mach_port_t,
-                                                 CFMutableDictionaryRef,
-                                                 io_iterator_t*);
-  io_service_t (*pIOIteratorNext)(io_iterator_t);
-  CFTypeRef (*pIORegistryEntryCreateCFProperty)(io_registry_entry_t,
-                                                CFStringRef,
-                                                CFAllocatorRef,
-                                                IOOptionBits);
-
-  /* CoreFoundation */
-  CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
-                                            const char*,
-                                            CFStringEncoding);
-  CFStringEncoding (*pCFStringGetSystemEncoding)(void);
-  UInt8 *(*pCFDataGetBytePtr)(CFDataRef);
-  CFIndex (*pCFDataGetLength)(CFDataRef);
-  void (*pCFDataGetBytes)(CFDataRef, CFRange, UInt8*);
-  void (*pCFRelease)(CFTypeRef);
-
-  void* core_foundation_handle;
-  void* iokit_handle;
-  int err;
-
-  kern_return_t kr;
-  mach_port_t mach_port;
-  io_iterator_t it;
-  io_object_t service;
-
-  mach_port = 0;
-
-  err = UV_ENOENT;
-  core_foundation_handle = dlopen("/System/Library/Frameworks/"
-                                  "CoreFoundation.framework/"
-                                  "CoreFoundation",
-                                  RTLD_LAZY | RTLD_LOCAL);
-  iokit_handle = dlopen("/System/Library/Frameworks/IOKit.framework/"
-                        "IOKit",
-                        RTLD_LAZY | RTLD_LOCAL);
-
-  if (core_foundation_handle == NULL || iokit_handle == NULL)
-    goto out;
-
-#define V(handle, symbol)                                                     \
-  do {                                                                        \
-    *(void **)(&p ## symbol) = dlsym((handle), #symbol);                      \
-    if (p ## symbol == NULL)                                                  \
-      goto out;                                                               \
-  }                                                                           \
-  while (0)
-  V(iokit_handle, IOMasterPort);
-  V(iokit_handle, IOServiceMatching);
-  V(iokit_handle, IOServiceGetMatchingServices);
-  V(iokit_handle, IOIteratorNext);
-  V(iokit_handle, IOObjectRelease);
-  V(iokit_handle, IORegistryEntryCreateCFProperty);
-  V(core_foundation_handle, CFStringCreateWithCString);
-  V(core_foundation_handle, CFStringGetSystemEncoding);
-  V(core_foundation_handle, CFDataGetBytePtr);
-  V(core_foundation_handle, CFDataGetLength);
-  V(core_foundation_handle, CFDataGetBytes);
-  V(core_foundation_handle, CFRelease);
-#undef V
-
-#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)
-
-  // Braces ensure goto doesn't jump into device_type_str's and
-  // clock_frequency_str's lifetimes after their initialization
-  {
-    kr = pIOMasterPort(MACH_PORT_NULL, &mach_port);
-    (void) kr;
-    assert(kr == KERN_SUCCESS);
-    CFMutableDictionaryRef classes_to_match
-        = pIOServiceMatching("IOPlatformDevice");
-    kr = pIOServiceGetMatchingServices(mach_port, classes_to_match, &it);
-    assert(kr == KERN_SUCCESS);
-    service = pIOIteratorNext(it);
-
-    CFStringRef device_type_str = S("device_type");
-    CFStringRef clock_frequency_str = S("clock-frequency");
-
-    while (service != 0) {
-      CFDataRef data;
-      data = pIORegistryEntryCreateCFProperty(service,
-                                              device_type_str,
-                                              NULL,
-                                              0);
-      if (data) {
-        const UInt8* raw = pCFDataGetBytePtr(data);
-        if (strncmp((char*)raw, "cpu", 3) == 0 ||
-            strncmp((char*)raw, "processor", 9) == 0) {
-          CFDataRef freq_ref;
-          freq_ref = pIORegistryEntryCreateCFProperty(service,
-                                                      clock_frequency_str,
-                                                      NULL,
-                                                      0);
-          if (freq_ref) {
-            const UInt8* freq_ref_ptr = pCFDataGetBytePtr(freq_ref);
-            CFIndex len = pCFDataGetLength(freq_ref);
-            if (len == 8)
-              memcpy(speed, freq_ref_ptr, 8);
-            else if (len == 4) {
-              uint32_t v;
-              memcpy(&v, freq_ref_ptr, 4);
-              *speed = v;
-            } else {
-              *speed = 0;
-            }
-
-            pCFRelease(freq_ref);
-            pCFRelease(data);
-            break;
-          }
-        }
-        pCFRelease(data);
-      }
-
-      service = pIOIteratorNext(it);
-    }
-
-    pIOObjectRelease(it);
-
-    err = 0;
-
-    if (device_type_str != NULL)
-      pCFRelease(device_type_str);
-    if (clock_frequency_str != NULL)
-      pCFRelease(clock_frequency_str);
-  }
-
-out:
-  if (core_foundation_handle != NULL)
-    dlclose(core_foundation_handle);
-
-  if (iokit_handle != NULL)
-    dlclose(iokit_handle);
-
-  mach_port_deallocate(mach_task_self(), mach_port);
-
-  return err;
-}
-
 int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
   unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
                multiplier = ((uint64_t)1000L / ticks);
   char model[512];
+  uint64_t cpuspeed;
   size_t size;
   unsigned int i;
   natural_t numcpus;
   mach_msg_type_number_t msg_type;
   processor_cpu_load_info_data_t *info;
   uv_cpu_info_t* cpu_info;
-  uint64_t cpuspeed;
-  int err;
 
   size = sizeof(model);
   if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) &&
@@ -348,9 +203,13 @@
     return UV__ERR(errno);
   }
 
-  err = uv__get_cpu_speed(&cpuspeed);
-  if (err < 0)
-    return err;
+  cpuspeed = 0;
+  size = sizeof(cpuspeed);
+  sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0);
+  if (cpuspeed == 0)
+    /* If sysctl hw.cputype == CPU_TYPE_ARM64, the correct value is unavailable
+     * from Apple, but we can hard-code it here to a plausible value. */
+    cpuspeed = 2400000000;
 
   if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus,
                           (processor_info_array_t*)&info,
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/epoll.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/epoll.cpp
deleted file mode 100644
index 4c057fb..0000000
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/epoll.cpp
+++ /dev/null
@@ -1,422 +0,0 @@
-/* Copyright libuv contributors. All rights reserved.
- *
- * 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 "uv.h"
-#include "internal.h"
-#include <errno.h>
-#include <sys/epoll.h>
-
-int uv__epoll_init(uv_loop_t* loop) {
-  int fd;
-  fd = epoll_create1(O_CLOEXEC);
-
-  /* epoll_create1() can fail either because it's not implemented (old kernel)
-   * or because it doesn't understand the O_CLOEXEC flag.
-   */
-  if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) {
-    fd = epoll_create(256);
-
-    if (fd != -1)
-      uv__cloexec(fd, 1);
-  }
-
-  loop->backend_fd = fd;
-  if (fd == -1)
-    return UV__ERR(errno);
-
-  return 0;
-}
-
-
-void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
-  struct epoll_event* events;
-  struct epoll_event dummy;
-  uintptr_t i;
-  uintptr_t nfds;
-
-  assert(loop->watchers != NULL);
-  assert(fd >= 0);
-
-  events = (struct epoll_event*) loop->watchers[loop->nwatchers];
-  nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
-  if (events != NULL)
-    /* Invalidate events with same file descriptor */
-    for (i = 0; i < nfds; i++)
-      if (events[i].data.fd == fd)
-        events[i].data.fd = -1;
-
-  /* Remove the file descriptor from the epoll.
-   * This avoids a problem where the same file description remains open
-   * in another process, causing repeated junk epoll events.
-   *
-   * We pass in a dummy epoll_event, to work around a bug in old kernels.
-   */
-  if (loop->backend_fd >= 0) {
-    /* Work around a bug in kernels 3.10 to 3.19 where passing a struct that
-     * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings.
-     */
-    memset(&dummy, 0, sizeof(dummy));
-    epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy);
-  }
-}
-
-
-int uv__io_check_fd(uv_loop_t* loop, int fd) {
-  struct epoll_event e;
-  int rc;
-
-  memset(&e, 0, sizeof(e));
-  e.events = POLLIN;
-  e.data.fd = -1;
-
-  rc = 0;
-  if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e))
-    if (errno != EEXIST)
-      rc = UV__ERR(errno);
-
-  if (rc == 0)
-    if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e))
-      abort();
-
-  return rc;
-}
-
-
-void uv__io_poll(uv_loop_t* loop, int timeout) {
-  /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes
-   * effectively infinite on 32 bits architectures.  To avoid blocking
-   * indefinitely, we cap the timeout and poll again if necessary.
-   *
-   * Note that "30 minutes" is a simplification because it depends on
-   * the value of CONFIG_HZ.  The magic constant assumes CONFIG_HZ=1200,
-   * that being the largest value I have seen in the wild (and only once.)
-   */
-  static const int max_safe_timeout = 1789569;
-  static int no_epoll_pwait_cached;
-  static int no_epoll_wait_cached;
-  int no_epoll_pwait;
-  int no_epoll_wait;
-  struct epoll_event events[1024];
-  struct epoll_event* pe;
-  struct epoll_event e;
-  int real_timeout;
-  QUEUE* q;
-  uv__io_t* w;
-  sigset_t sigset;
-  uint64_t sigmask;
-  uint64_t base;
-  int have_signals;
-  int nevents;
-  int count;
-  int nfds;
-  int fd;
-  int op;
-  int i;
-  int user_timeout;
-  int reset_timeout;
-
-  if (loop->nfds == 0) {
-    assert(QUEUE_EMPTY(&loop->watcher_queue));
-    return;
-  }
-
-  memset(&e, 0, sizeof(e));
-
-  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
-    q = QUEUE_HEAD(&loop->watcher_queue);
-    QUEUE_REMOVE(q);
-    QUEUE_INIT(q);
-
-    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
-    assert(w->pevents != 0);
-    assert(w->fd >= 0);
-    assert(w->fd < (int) loop->nwatchers);
-
-    e.events = w->pevents;
-    e.data.fd = w->fd;
-
-    if (w->events == 0)
-      op = EPOLL_CTL_ADD;
-    else
-      op = EPOLL_CTL_MOD;
-
-    /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching
-     * events, skip the syscall and squelch the events after epoll_wait().
-     */
-    if (epoll_ctl(loop->backend_fd, op, w->fd, &e)) {
-      if (errno != EEXIST)
-        abort();
-
-      assert(op == EPOLL_CTL_ADD);
-
-      /* We've reactivated a file descriptor that's been watched before. */
-      if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, w->fd, &e))
-        abort();
-    }
-
-    w->events = w->pevents;
-  }
-
-  sigmask = 0;
-  if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
-    sigemptyset(&sigset);
-    sigaddset(&sigset, SIGPROF);
-    sigmask |= 1 << (SIGPROF - 1);
-  }
-
-  assert(timeout >= -1);
-  base = loop->time;
-  count = 48; /* Benchmarks suggest this gives the best throughput. */
-  real_timeout = timeout;
-
-  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
-    reset_timeout = 1;
-    user_timeout = timeout;
-    timeout = 0;
-  } else {
-    reset_timeout = 0;
-    user_timeout = 0;
-  }
-
-  /* You could argue there is a dependency between these two but
-   * ultimately we don't care about their ordering with respect
-   * to one another. Worst case, we make a few system calls that
-   * could have been avoided because another thread already knows
-   * they fail with ENOSYS. Hardly the end of the world.
-   */
-  no_epoll_pwait = uv__load_relaxed(&no_epoll_pwait_cached);
-  no_epoll_wait = uv__load_relaxed(&no_epoll_wait_cached);
-
-  for (;;) {
-    /* Only need to set the provider_entry_time if timeout != 0. The function
-     * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
-     */
-    if (timeout != 0)
-      uv__metrics_set_provider_entry_time(loop);
-
-    /* See the comment for max_safe_timeout for an explanation of why
-     * this is necessary.  Executive summary: kernel bug workaround.
-     */
-    if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout)
-      timeout = max_safe_timeout;
-
-    if (sigmask != 0 && no_epoll_pwait != 0)
-      if (pthread_sigmask(SIG_BLOCK, &sigset, NULL))
-        abort();
-
-    if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) {
-      nfds = epoll_pwait(loop->backend_fd,
-                         events,
-                         ARRAY_SIZE(events),
-                         timeout,
-                         &sigset);
-      if (nfds == -1 && errno == ENOSYS) {
-        uv__store_relaxed(&no_epoll_pwait_cached, 1);
-        no_epoll_pwait = 1;
-      }
-    } else {
-      nfds = epoll_wait(loop->backend_fd,
-                        events,
-                        ARRAY_SIZE(events),
-                        timeout);
-      if (nfds == -1 && errno == ENOSYS) {
-        uv__store_relaxed(&no_epoll_wait_cached, 1);
-        no_epoll_wait = 1;
-      }
-    }
-
-    if (sigmask != 0 && no_epoll_pwait != 0)
-      if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL))
-        abort();
-
-    /* Update loop->time unconditionally. It's tempting to skip the update when
-     * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
-     * operating system didn't reschedule our process while in the syscall.
-     */
-    SAVE_ERRNO(uv__update_time(loop));
-
-    if (nfds == 0) {
-      assert(timeout != -1);
-
-      if (reset_timeout != 0) {
-        timeout = user_timeout;
-        reset_timeout = 0;
-      }
-
-      if (timeout == -1)
-        continue;
-
-      if (timeout == 0)
-        return;
-
-      /* We may have been inside the system call for longer than |timeout|
-       * milliseconds so we need to update the timestamp to avoid drift.
-       */
-      goto update_timeout;
-    }
-
-    if (nfds == -1) {
-      if (errno == ENOSYS) {
-        /* epoll_wait() or epoll_pwait() failed, try the other system call. */
-        assert(no_epoll_wait == 0 || no_epoll_pwait == 0);
-        continue;
-      }
-
-      if (errno != EINTR)
-        abort();
-
-      if (reset_timeout != 0) {
-        timeout = user_timeout;
-        reset_timeout = 0;
-      }
-
-      if (timeout == -1)
-        continue;
-
-      if (timeout == 0)
-        return;
-
-      /* Interrupted by a signal. Update timeout and poll again. */
-      goto update_timeout;
-    }
-
-    have_signals = 0;
-    nevents = 0;
-
-    {
-      /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */
-      union {
-        struct epoll_event* events;
-        uv__io_t* watchers;
-      } x;
-
-      x.events = events;
-      assert(loop->watchers != NULL);
-      loop->watchers[loop->nwatchers] = x.watchers;
-      loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
-    }
-
-    for (i = 0; i < nfds; i++) {
-      pe = events + i;
-      fd = pe->data.fd;
-
-      /* Skip invalidated events, see uv__platform_invalidate_fd */
-      if (fd == -1)
-        continue;
-
-      assert(fd >= 0);
-      assert((unsigned) fd < loop->nwatchers);
-
-      w = (uv__io_t*)loop->watchers[fd];
-
-      if (w == NULL) {
-        /* File descriptor that we've stopped watching, disarm it.
-         *
-         * Ignore all errors because we may be racing with another thread
-         * when the file descriptor is closed.
-         */
-        epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, pe);
-        continue;
-      }
-
-      /* Give users only events they're interested in. Prevents spurious
-       * callbacks when previous callback invocation in this loop has stopped
-       * the current watcher. Also, filters out events that users has not
-       * requested us to watch.
-       */
-      pe->events &= w->pevents | POLLERR | POLLHUP;
-
-      /* Work around an epoll quirk where it sometimes reports just the
-       * EPOLLERR or EPOLLHUP event.  In order to force the event loop to
-       * move forward, we merge in the read/write events that the watcher
-       * is interested in; uv__read() and uv__write() will then deal with
-       * the error or hangup in the usual fashion.
-       *
-       * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user
-       * reads the available data, calls uv_read_stop(), then sometime later
-       * calls uv_read_start() again.  By then, libuv has forgotten about the
-       * hangup and the kernel won't report EPOLLIN again because there's
-       * nothing left to read.  If anything, libuv is to blame here.  The
-       * current hack is just a quick bandaid; to properly fix it, libuv
-       * needs to remember the error/hangup event.  We should get that for
-       * free when we switch over to edge-triggered I/O.
-       */
-      if (pe->events == POLLERR || pe->events == POLLHUP)
-        pe->events |=
-          w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
-
-      if (pe->events != 0) {
-        /* Run signal watchers last.  This also affects child process watchers
-         * because those are implemented in terms of signal watchers.
-         */
-        if (w == &loop->signal_io_watcher) {
-          have_signals = 1;
-        } else {
-          uv__metrics_update_idle_time(loop);
-          w->cb(loop, w, pe->events);
-        }
-
-        nevents++;
-      }
-    }
-
-    if (reset_timeout != 0) {
-      timeout = user_timeout;
-      reset_timeout = 0;
-    }
-
-    if (have_signals != 0) {
-      uv__metrics_update_idle_time(loop);
-      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
-    }
-
-    loop->watchers[loop->nwatchers] = NULL;
-    loop->watchers[loop->nwatchers + 1] = NULL;
-
-    if (have_signals != 0)
-      return;  /* Event loop should cycle now so don't poll again. */
-
-    if (nevents != 0) {
-      if (nfds == ARRAY_SIZE(events) && --count != 0) {
-        /* Poll for more events but don't block this time. */
-        timeout = 0;
-        continue;
-      }
-      return;
-    }
-
-    if (timeout == 0)
-      return;
-
-    if (timeout == -1)
-      continue;
-
-update_timeout:
-    assert(timeout > 0);
-
-    real_timeout -= (loop->time - base);
-    if (real_timeout <= 0)
-      return;
-
-    timeout = real_timeout;
-  }
-}
-
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/freebsd.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/freebsd.cpp
index 6700ff6..1bd6388 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/freebsd.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/freebsd.cpp
@@ -91,7 +91,7 @@
   size_t size = sizeof(freecount);
 
   if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0))
-    return UV__ERR(errno);
+    return 0;
 
   return (uint64_t) freecount * sysconf(_SC_PAGESIZE);
 
@@ -105,7 +105,7 @@
   size_t size = sizeof(info);
 
   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
-    return UV__ERR(errno);
+    return 0;
 
   return (uint64_t) info;
 }
@@ -116,6 +116,11 @@
 }
 
 
+uint64_t uv_get_available_memory(void) {
+  return uv_get_free_memory();
+}
+
+
 void uv_loadavg(double avg[3]) {
   struct loadavg info;
   size_t size = sizeof(info);
@@ -264,30 +269,6 @@
 }
 
 
-int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) {
-#if __FreeBSD__ >= 11 && !defined(__DragonFly__)
-  return sendmmsg(fd,
-                  (struct mmsghdr*) mmsg,
-                  vlen,
-                  0 /* flags */);
-#else
-  return errno = ENOSYS, -1;
-#endif
-}
-
-
-int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) {
-#if __FreeBSD__ >= 11 && !defined(__DragonFly__)
-  return recvmmsg(fd,
-                  (struct mmsghdr*) mmsg,
-                  vlen,
-                  0 /* flags */,
-                  NULL /* timeout */);
-#else
-  return errno = ENOSYS, -1;
-#endif
-}
-
 ssize_t
 uv__fs_copy_file_range(int fd_in,
                        off_t* off_in,
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/fs.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/fs.cpp
index 1a61524..aba190a 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/fs.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/fs.cpp
@@ -46,9 +46,10 @@
 #include <fcntl.h>
 #include <poll.h>
 
+#include <atomic>
+
 #if defined(__DragonFly__)        ||                                      \
     defined(__FreeBSD__)          ||                                      \
-    defined(__FreeBSD_kernel__)   ||                                      \
     defined(__OpenBSD__)          ||                                      \
     defined(__NetBSD__)
 # define HAVE_PREADV 1
@@ -56,11 +57,16 @@
 # define HAVE_PREADV 0
 #endif
 
-#if defined(__linux__)
-# include "sys/utsname.h"
+/* preadv() and pwritev() were added in Android N (level 24) */
+#if defined(__linux__) && !(defined(__ANDROID__) && __ANDROID_API__ < 24)
+# define TRY_PREADV 1
 #endif
 
-#if defined(__linux__) || defined(__sun)
+#if defined(__linux__)
+# include <sys/sendfile.h>
+#endif
+
+#if defined(__sun)
 # include <sys/sendfile.h>
 # include <sys/sysmacros.h>
 #endif
@@ -79,7 +85,6 @@
 #if defined(__APPLE__)            ||                                      \
     defined(__DragonFly__)        ||                                      \
     defined(__FreeBSD__)          ||                                      \
-    defined(__FreeBSD_kernel__)   ||                                      \
     defined(__OpenBSD__)          ||                                      \
     defined(__NetBSD__)
 # include <sys/param.h>
@@ -256,7 +261,6 @@
 #elif defined(__APPLE__)                                                      \
     || defined(__DragonFly__)                                                 \
     || defined(__FreeBSD__)                                                   \
-    || defined(__FreeBSD_kernel__)                                            \
     || defined(__NetBSD__)                                                    \
     || defined(__OpenBSD__)                                                   \
     || defined(__sun)
@@ -311,7 +315,7 @@
   static uv_once_t once = UV_ONCE_INIT;
   int r;
 #ifdef O_CLOEXEC
-  static int no_cloexec_support;
+  static std::atomic<int> no_cloexec_support;
 #endif
   static const char pattern[] = "XXXXXX";
   static const size_t pattern_size = sizeof(pattern) - 1;
@@ -336,7 +340,8 @@
   uv_once(&once, uv__mkostemp_initonce);
 
 #ifdef O_CLOEXEC
-  if (uv__load_relaxed(&no_cloexec_support) == 0 && uv__mkostemp != NULL) {
+  if (atomic_load_explicit(&no_cloexec_support, std::memory_order_relaxed) == 0 &&
+      uv__mkostemp != NULL) {
     r = uv__mkostemp(path, O_CLOEXEC);
 
     if (r >= 0)
@@ -349,7 +354,7 @@
 
     /* We set the static variable so that next calls don't even
        try to use mkostemp. */
-    uv__store_relaxed(&no_cloexec_support, 1);
+    atomic_store_explicit(&no_cloexec_support, 1, std::memory_order_relaxed);
   }
 #endif  /* O_CLOEXEC */
 
@@ -458,8 +463,8 @@
 
 
 static ssize_t uv__fs_read(uv_fs_t* req) {
-#if defined(__linux__)
-  static int no_preadv;
+#if TRY_PREADV
+  static std::atomic<int> no_preadv;
 #endif
   unsigned int iovmax;
   ssize_t result;
@@ -482,20 +487,20 @@
 #if HAVE_PREADV
     result = preadv(req->file, (struct iovec*) req->bufs, req->nbufs, req->off);
 #else
-# if defined(__linux__)
-    if (uv__load_relaxed(&no_preadv)) retry:
+# if TRY_PREADV
+    if (atomic_load_explicit(&no_preadv, std::memory_order_relaxed)) retry:
 # endif
     {
       result = uv__fs_preadv(req->file, req->bufs, req->nbufs, req->off);
     }
-# if defined(__linux__)
+# if TRY_PREADV
     else {
-      result = uv__preadv(req->file,
-                          (struct iovec*)req->bufs,
-                          req->nbufs,
-                          req->off);
+      result = preadv(req->file,
+                      (struct iovec*) req->bufs,
+                      req->nbufs,
+                      req->off);
       if (result == -1 && errno == ENOSYS) {
-        uv__store_relaxed(&no_preadv, 1);
+        atomic_store_explicit(&no_preadv, 1, std::memory_order_relaxed);
         goto retry;
       }
     }
@@ -516,7 +521,7 @@
   if (result == -1 && errno == EOPNOTSUPP) {
     struct stat buf;
     ssize_t rc;
-    rc = fstat(req->file, &buf);
+    rc = uv__fstat(req->file, &buf);
     if (rc == 0 && S_ISDIR(buf.st_mode)) {
       errno = EISDIR;
     }
@@ -527,19 +532,12 @@
 }
 
 
-#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_8)
-#define UV_CONST_DIRENT uv__dirent_t
-#else
-#define UV_CONST_DIRENT const uv__dirent_t
-#endif
-
-
-static int uv__fs_scandir_filter(UV_CONST_DIRENT* dent) {
+static int uv__fs_scandir_filter(const uv__dirent_t* dent) {
   return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0;
 }
 
 
-static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) {
+static int uv__fs_scandir_sort(const uv__dirent_t** a, const uv__dirent_t** b) {
   return strcmp((*a)->d_name, (*b)->d_name);
 }
 
@@ -715,7 +713,7 @@
   /* We may not have a real PATH_MAX.  Read size of link.  */
   struct stat st;
   int ret;
-  ret = lstat(req->path, &st);
+  ret = uv__lstat(req->path, &st);
   if (ret != 0)
     return -1;
   if (!S_ISLNK(st.st_mode)) {
@@ -907,31 +905,6 @@
 
 
 #ifdef __linux__
-static unsigned uv__kernel_version(void) {
-  static unsigned cached_version;
-  struct utsname u;
-  unsigned version;
-  unsigned major;
-  unsigned minor;
-  unsigned patch;
-
-  version = uv__load_relaxed(&cached_version);
-  if (version != 0)
-    return version;
-
-  if (-1 == uname(&u))
-    return 0;
-
-  if (3 != sscanf(u.release, "%u.%u.%u", &major, &minor, &patch))
-    return 0;
-
-  version = major * 65536 + minor * 256 + patch;
-  uv__store_relaxed(&cached_version, version);
-
-  return version;
-}
-
-
 /* Pre-4.20 kernels have a bug where CephFS uses the RADOS copy-from command
  * in copy_file_range() when it shouldn't. There is no workaround except to
  * fall back to a regular copy.
@@ -968,10 +941,10 @@
 
 static ssize_t uv__fs_try_copy_file_range(int in_fd, off_t* off,
                                           int out_fd, size_t len) {
-  static int no_copy_file_range_support;
+  static std::atomic<int> no_copy_file_range_support;
   ssize_t r;
 
-  if (uv__load_relaxed(&no_copy_file_range_support)) {
+  if (atomic_load_explicit(&no_copy_file_range_support, std::memory_order_relaxed)) {
     errno = ENOSYS;
     return -1;
   }
@@ -990,7 +963,7 @@
       errno = ENOSYS;  /* Use fallback. */
     break;
   case ENOSYS:
-    uv__store_relaxed(&no_copy_file_range_support, 1);
+    atomic_store_explicit(&no_copy_file_range_support, 1, std::memory_order_relaxed);
     break;
   case EPERM:
     /* It's been reported that CIFS spuriously fails.
@@ -1061,10 +1034,7 @@
 
     return -1;
   }
-#elif defined(__APPLE__)           || \
-      defined(__DragonFly__)       || \
-      defined(__FreeBSD__)         || \
-      defined(__FreeBSD_kernel__)
+#elif defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__)
   {
     off_t len;
     ssize_t r;
@@ -1088,15 +1058,6 @@
 #endif
     len = 0;
     r = sendfile(in_fd, out_fd, req->off, req->bufsml[0].len, NULL, &len, 0);
-#elif defined(__FreeBSD_kernel__)
-    len = 0;
-    r = bsd_sendfile(in_fd,
-                     out_fd,
-                     req->off,
-                     req->bufsml[0].len,
-                     NULL,
-                     &len,
-                     0);
 #else
     /* The darwin sendfile takes len as an input for the length to send,
      * so make sure to initialize it with the caller's value. */
@@ -1148,7 +1109,6 @@
 #elif defined(__APPLE__)                                                      \
     || defined(__DragonFly__)                                                 \
     || defined(__FreeBSD__)                                                   \
-    || defined(__FreeBSD_kernel__)                                            \
     || defined(__NetBSD__)                                                    \
     || defined(__OpenBSD__)
   struct timeval tv[2];
@@ -1190,7 +1150,6 @@
 #elif defined(__APPLE__)          ||                                          \
       defined(__DragonFly__)      ||                                          \
       defined(__FreeBSD__)        ||                                          \
-      defined(__FreeBSD_kernel__) ||                                          \
       defined(__NetBSD__)
   struct timeval tv[2];
   tv[0] = uv__fs_to_timeval(req->atime);
@@ -1204,8 +1163,8 @@
 
 
 static ssize_t uv__fs_write(uv_fs_t* req) {
-#if defined(__linux__)
-  static int no_pwritev;
+#if TRY_PREADV
+  static std::atomic<int> no_pwritev;
 #endif
   ssize_t r;
 
@@ -1233,20 +1192,20 @@
 #if HAVE_PREADV
     r = pwritev(req->file, (struct iovec*) req->bufs, req->nbufs, req->off);
 #else
-# if defined(__linux__)
-    if (no_pwritev) retry:
+# if TRY_PREADV
+    if (atomic_load_explicit(&no_pwritev, std::memory_order_relaxed)) retry:
 # endif
     {
       r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
     }
-# if defined(__linux__)
+# if TRY_PREADV
     else {
-      r = uv__pwritev(req->file,
-                      (struct iovec*) req->bufs,
-                      req->nbufs,
-                      req->off);
+      r = pwritev(req->file,
+                  (struct iovec*) req->bufs,
+                  req->nbufs,
+                  req->off);
       if (r == -1 && errno == ENOSYS) {
-        no_pwritev = 1;
+        atomic_store_explicit(&no_pwritev, 1, std::memory_order_relaxed);
         goto retry;
       }
     }
@@ -1288,7 +1247,7 @@
     return srcfd;
 
   /* Get the source file's mode. */
-  if (fstat(srcfd, &src_statsbuf)) {
+  if (uv__fstat(srcfd, &src_statsbuf)) {
     err = UV__ERR(errno);
     goto out;
   }
@@ -1316,7 +1275,7 @@
      destination are not the same file. If they are the same, bail out early. */
   if ((req->flags & UV_FS_COPYFILE_EXCL) == 0) {
     /* Get the destination file's mode. */
-    if (fstat(dstfd, &dst_statsbuf)) {
+    if (uv__fstat(dstfd, &dst_statsbuf)) {
       err = UV__ERR(errno);
       goto out;
     }
@@ -1330,7 +1289,19 @@
     /* Truncate the file in case the destination already existed. */
     if (ftruncate(dstfd, 0) != 0) {
       err = UV__ERR(errno);
-      goto out;
+
+      /* ftruncate() on ceph-fuse fails with EACCES when the file is created
+       * with read only permissions. Since ftruncate() on a newly created
+       * file is a meaningless operation anyway, detect that condition
+       * and squelch the error.
+       */
+      if (err != UV_EACCES)
+        goto out;
+
+      if (dst_statsbuf.st_size > 0)
+        goto out;
+
+      err = 0;
     }
   }
 
@@ -1514,14 +1485,14 @@
                         uv_stat_t* buf) {
   STATIC_ASSERT(UV_ENOSYS != -1);
 #ifdef __linux__
-  static int no_statx;
+  static std::atomic<int> no_statx;
   struct uv__statx statxbuf;
   int dirfd;
   int flags;
   int mode;
   int rc;
 
-  if (uv__load_relaxed(&no_statx))
+  if (atomic_load_explicit(&no_statx, std::memory_order_relaxed))
     return UV_ENOSYS;
 
   dirfd = AT_FDCWD;
@@ -1555,30 +1526,11 @@
      * implemented, rc might return 1 with 0 set as the error code in which
      * case we return ENOSYS.
      */
-    uv__store_relaxed(&no_statx, 1);
+    atomic_store_explicit(&no_statx, 1, std::memory_order_relaxed);
     return UV_ENOSYS;
   }
 
-  buf->st_dev = makedev(statxbuf.stx_dev_major, statxbuf.stx_dev_minor);
-  buf->st_mode = statxbuf.stx_mode;
-  buf->st_nlink = statxbuf.stx_nlink;
-  buf->st_uid = statxbuf.stx_uid;
-  buf->st_gid = statxbuf.stx_gid;
-  buf->st_rdev = makedev(statxbuf.stx_rdev_major, statxbuf.stx_rdev_minor);
-  buf->st_ino = statxbuf.stx_ino;
-  buf->st_size = statxbuf.stx_size;
-  buf->st_blksize = statxbuf.stx_blksize;
-  buf->st_blocks = statxbuf.stx_blocks;
-  buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec;
-  buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec;
-  buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec;
-  buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec;
-  buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec;
-  buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec;
-  buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec;
-  buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec;
-  buf->st_flags = 0;
-  buf->st_gen = 0;
+  uv__statx_to_stat(&statxbuf, buf);
 
   return 0;
 #else
@@ -1595,7 +1547,7 @@
   if (ret != UV_ENOSYS)
     return ret;
 
-  ret = stat(path, &pbuf);
+  ret = uv__stat(path, &pbuf);
   if (ret == 0)
     uv__to_stat(&pbuf, buf);
 
@@ -1611,7 +1563,7 @@
   if (ret != UV_ENOSYS)
     return ret;
 
-  ret = lstat(path, &pbuf);
+  ret = uv__lstat(path, &pbuf);
   if (ret == 0)
     uv__to_stat(&pbuf, buf);
 
@@ -1627,7 +1579,7 @@
   if (ret != UV_ENOSYS)
     return ret;
 
-  ret = fstat(fd, &pbuf);
+  ret = uv__fstat(fd, &pbuf);
   if (ret == 0)
     uv__to_stat(&pbuf, buf);
 
@@ -1822,6 +1774,9 @@
 int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
   INIT(CLOSE);
   req->file = file;
+  if (cb != NULL)
+    if (uv__iou_fs_close(loop, req))
+      return 0;
   POST;
 }
 
@@ -1869,6 +1824,9 @@
 int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
   INIT(FDATASYNC);
   req->file = file;
+  if (cb != NULL)
+    if (uv__iou_fs_fsync_or_fdatasync(loop, req, /* IORING_FSYNC_DATASYNC */ 1))
+      return 0;
   POST;
 }
 
@@ -1876,6 +1834,9 @@
 int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
   INIT(FSTAT);
   req->file = file;
+  if (cb != NULL)
+    if (uv__iou_fs_statx(loop, req, /* is_fstat */ 1, /* is_lstat */ 0))
+      return 0;
   POST;
 }
 
@@ -1883,6 +1844,9 @@
 int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
   INIT(FSYNC);
   req->file = file;
+  if (cb != NULL)
+    if (uv__iou_fs_fsync_or_fdatasync(loop, req, /* no flags */ 0))
+      return 0;
   POST;
 }
 
@@ -1929,6 +1893,9 @@
 int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
   INIT(LSTAT);
   PATH;
+  if (cb != NULL)
+    if (uv__iou_fs_statx(loop, req, /* is_fstat */ 0, /* is_lstat */ 1))
+      return 0;
   POST;
 }
 
@@ -1940,6 +1907,9 @@
                uv_fs_cb cb) {
   INIT(LINK);
   PATH2;
+  if (cb != NULL)
+    if (uv__iou_fs_link(loop, req))
+      return 0;
   POST;
 }
 
@@ -1952,6 +1922,9 @@
   INIT(MKDIR);
   PATH;
   req->mode = mode;
+  if (cb != NULL)
+    if (uv__iou_fs_mkdir(loop, req))
+      return 0;
   POST;
 }
 
@@ -1990,6 +1963,9 @@
   PATH;
   req->flags = flags;
   req->mode = mode;
+  if (cb != NULL)
+    if (uv__iou_fs_open(loop, req))
+      return 0;
   POST;
 }
 
@@ -2018,6 +1994,11 @@
   memcpy(req->bufs, bufs, nbufs * sizeof(*bufs));
 
   req->off = off;
+
+  if (cb != NULL)
+    if (uv__iou_fs_read_or_write(loop, req, /* is_read */ 1))
+      return 0;
+
   POST;
 }
 
@@ -2095,6 +2076,9 @@
                  uv_fs_cb cb) {
   INIT(RENAME);
   PATH2;
+  if (cb != NULL)
+    if (uv__iou_fs_rename(loop, req))
+      return 0;
   POST;
 }
 
@@ -2125,6 +2109,9 @@
 int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
   INIT(STAT);
   PATH;
+  if (cb != NULL)
+    if (uv__iou_fs_statx(loop, req, /* is_fstat */ 0, /* is_lstat */ 0))
+      return 0;
   POST;
 }
 
@@ -2138,6 +2125,9 @@
   INIT(SYMLINK);
   PATH2;
   req->flags = flags;
+  if (cb != NULL)
+    if (uv__iou_fs_symlink(loop, req))
+      return 0;
   POST;
 }
 
@@ -2145,6 +2135,9 @@
 int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
   INIT(UNLINK);
   PATH;
+  if (cb != NULL)
+    if (uv__iou_fs_unlink(loop, req))
+      return 0;
   POST;
 }
 
@@ -2188,6 +2181,11 @@
   memcpy(req->bufs, bufs, nbufs * sizeof(*bufs));
 
   req->off = off;
+
+  if (cb != NULL)
+    if (uv__iou_fs_read_or_write(loop, req, /* is_read */ 0))
+      return 0;
+
   POST;
 }
 
@@ -2196,7 +2194,7 @@
   if (req == NULL)
     return;
 
-  /* Only necessary for asychronous requests, i.e., requests with a callback.
+  /* Only necessary for asynchronous requests, i.e., requests with a callback.
    * Synchronous ones don't copy their arguments and have req->path and
    * req->new_path pointing to user-owned memory.  UV_FS_MKDTEMP and
    * UV_FS_MKSTEMP are the exception to the rule, they always allocate memory.
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/fsevents.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/fsevents.cpp
index 648c8a9..c31d08b 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/fsevents.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/fsevents.cpp
@@ -80,13 +80,13 @@
 typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t;
 
 struct uv__cf_loop_signal_s {
-  QUEUE member;
+  struct uv__queue member;
   uv_fs_event_t* handle;
   uv__cf_loop_signal_type_t type;
 };
 
 struct uv__fsevents_event_s {
-  QUEUE member;
+  struct uv__queue member;
   int events;
   char path[1];
 };
@@ -98,7 +98,7 @@
   FSEventStreamRef fsevent_stream;
   uv_sem_t fsevent_sem;
   uv_mutex_t fsevent_mutex;
-  void* fsevent_handles[2];
+  struct uv__queue fsevent_handles;
   unsigned int fsevent_handle_count;
 };
 
@@ -132,7 +132,6 @@
 static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)(
     CFAllocatorRef,
     const char*);
-static CFStringEncoding (*pCFStringGetSystemEncoding)(void);
 static CFStringRef (*pkCFRunLoopDefaultMode);
 static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef,
                                                 FSEventStreamCallback,
@@ -141,7 +140,6 @@
                                                 FSEventStreamEventId,
                                                 CFTimeInterval,
                                                 FSEventStreamCreateFlags);
-static void (*pFSEventStreamFlushSync)(FSEventStreamRef);
 static void (*pFSEventStreamInvalidate)(FSEventStreamRef);
 static void (*pFSEventStreamRelease)(FSEventStreamRef);
 static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef,
@@ -152,22 +150,22 @@
 
 #define UV__FSEVENTS_PROCESS(handle, block)                                   \
     do {                                                                      \
-      QUEUE events;                                                           \
-      QUEUE* q;                                                               \
+      struct uv__queue events;                                                \
+      struct uv__queue* q;                                                    \
       uv__fsevents_event_t* event;                                            \
       int err;                                                                \
       uv_mutex_lock(&(handle)->cf_mutex);                                     \
       /* Split-off all events and empty original queue */                     \
-      QUEUE_MOVE(&(handle)->cf_events, &events);                              \
+      uv__queue_move(&(handle)->cf_events, &events);                          \
       /* Get error (if any) and zero original one */                          \
       err = (handle)->cf_error;                                               \
       (handle)->cf_error = 0;                                                 \
       uv_mutex_unlock(&(handle)->cf_mutex);                                   \
       /* Loop through events, deallocating each after processing */           \
-      while (!QUEUE_EMPTY(&events)) {                                         \
-        q = QUEUE_HEAD(&events);                                              \
-        event = QUEUE_DATA(q, uv__fsevents_event_t, member);                  \
-        QUEUE_REMOVE(q);                                                      \
+      while (!uv__queue_empty(&events)) {                                     \
+        q = uv__queue_head(&events);                                          \
+        event = uv__queue_data(q, uv__fsevents_event_t, member);              \
+        uv__queue_remove(q);                                                  \
         /* NOTE: Checking uv__is_active() is required here, because handle    \
          * callback may close handle and invoking it after it will lead to    \
          * incorrect behaviour */                                             \
@@ -195,14 +193,14 @@
 
 /* Runs in CF thread, pushed event into handle's event list */
 static void uv__fsevents_push_event(uv_fs_event_t* handle,
-                                    QUEUE* events,
+                                    struct uv__queue* events,
                                     int err) {
   assert(events != NULL || err != 0);
   uv_mutex_lock(&handle->cf_mutex);
 
   /* Concatenate two queues */
   if (events != NULL)
-    QUEUE_ADD(&handle->cf_events, events);
+    uv__queue_add(&handle->cf_events, events);
 
   /* Propagate error */
   if (err != 0)
@@ -226,12 +224,12 @@
   char* path;
   char* pos;
   uv_fs_event_t* handle;
-  QUEUE* q;
+  struct uv__queue* q;
   uv_loop_t* loop;
   uv__cf_loop_state_t* state;
   uv__fsevents_event_t* event;
   FSEventStreamEventFlags flags;
-  QUEUE head;
+  struct uv__queue head;
 
   loop = (uv_loop_t*)info;
   state = (uv__cf_loop_state_t*)loop->cf_state;
@@ -240,9 +238,9 @@
 
   /* For each handle */
   uv_mutex_lock(&state->fsevent_mutex);
-  QUEUE_FOREACH(q, &state->fsevent_handles) {
-    handle = QUEUE_DATA(q, uv_fs_event_t, cf_member);
-    QUEUE_INIT(&head);
+  uv__queue_foreach(q, &state->fsevent_handles) {
+    handle = uv__queue_data(q, uv_fs_event_t, cf_member);
+    uv__queue_init(&head);
 
     /* Process and filter out events */
     for (i = 0; i < numEvents; i++) {
@@ -320,10 +318,10 @@
           event->events = UV_CHANGE;
       }
 
-      QUEUE_INSERT_TAIL(&head, &event->member);
+      uv__queue_insert_tail(&head, &event->member);
     }
 
-    if (!QUEUE_EMPTY(&head))
+    if (!uv__queue_empty(&head))
       uv__fsevents_push_event(handle, &head, 0);
   }
   uv_mutex_unlock(&state->fsevent_mutex);
@@ -331,8 +329,9 @@
 
 
 /* Runs in CF thread */
-static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
-  uv__cf_loop_state_t* state;
+static int uv__fsevents_create_stream(uv__cf_loop_state_t* state,
+                                      uv_loop_t* loop,
+                                      CFArrayRef paths) {
   FSEventStreamContext ctx;
   FSEventStreamRef ref;
   CFAbsoluteTime latency;
@@ -373,10 +372,7 @@
                              flags);
   assert(ref != NULL);
 
-  state = (uv__cf_loop_state_t*)loop->cf_state;
-  pFSEventStreamScheduleWithRunLoop(ref,
-                                    state->loop,
-                                    *pkCFRunLoopDefaultMode);
+  pFSEventStreamScheduleWithRunLoop(ref, state->loop, *pkCFRunLoopDefaultMode);
   if (!pFSEventStreamStart(ref)) {
     pFSEventStreamInvalidate(ref);
     pFSEventStreamRelease(ref);
@@ -389,11 +385,7 @@
 
 
 /* Runs in CF thread */
-static void uv__fsevents_destroy_stream(uv_loop_t* loop) {
-  uv__cf_loop_state_t* state;
-
-  state = (uv__cf_loop_state_t*)loop->cf_state;
-
+static void uv__fsevents_destroy_stream(uv__cf_loop_state_t* state) {
   if (state->fsevent_stream == NULL)
     return;
 
@@ -408,10 +400,10 @@
 
 
 /* Runs in CF thread, when there're new fsevent handles to add to stream */
-static void uv__fsevents_reschedule(uv_fs_event_t* handle,
+static void uv__fsevents_reschedule(uv__cf_loop_state_t* state,
+                                    uv_loop_t* loop,
                                     uv__cf_loop_signal_type_t type) {
-  uv__cf_loop_state_t* state;
-  QUEUE* q;
+  struct uv__queue* q;
   uv_fs_event_t* curr;
   CFArrayRef cf_paths;
   CFStringRef* paths;
@@ -419,7 +411,6 @@
   int err;
   unsigned int path_count;
 
-  state = (uv__cf_loop_state_t*)handle->loop->cf_state;
   paths = NULL;
   cf_paths = NULL;
   err = 0;
@@ -438,7 +429,7 @@
   uv_mutex_unlock(&state->fsevent_mutex);
 
   /* Destroy previous FSEventStream */
-  uv__fsevents_destroy_stream(handle->loop);
+  uv__fsevents_destroy_stream(state);
 
   /* Any failure below will be a memory failure */
   err = UV_ENOMEM;
@@ -455,9 +446,9 @@
 
     q = &state->fsevent_handles;
     for (; i < path_count; i++) {
-      q = QUEUE_NEXT(q);
+      q = uv__queue_next(q);
       assert(q != &state->fsevent_handles);
-      curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
+      curr = uv__queue_data(q, uv_fs_event_t, cf_member);
 
       assert(curr->realpath != NULL);
       paths[i] =
@@ -478,7 +469,7 @@
       err = UV_ENOMEM;
       goto final;
     }
-    err = uv__fsevents_create_stream(handle->loop, cf_paths);
+    err = uv__fsevents_create_stream(state, loop, cf_paths);
   }
 
 final:
@@ -495,8 +486,8 @@
 
     /* Broadcast error to all handles */
     uv_mutex_lock(&state->fsevent_mutex);
-    QUEUE_FOREACH(q, &state->fsevent_handles) {
-      curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
+    uv__queue_foreach(q, &state->fsevent_handles) {
+      curr = uv__queue_data(q, uv_fs_event_t, cf_member);
       uv__fsevents_push_event(curr, NULL, err);
     }
     uv_mutex_unlock(&state->fsevent_mutex);
@@ -563,10 +554,8 @@
   V(core_foundation_handle, CFRunLoopStop);
   V(core_foundation_handle, CFRunLoopWakeUp);
   V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation);
-  V(core_foundation_handle, CFStringGetSystemEncoding);
   V(core_foundation_handle, kCFRunLoopDefaultMode);
   V(core_services_handle, FSEventStreamCreate);
-  V(core_services_handle, FSEventStreamFlushSync);
   V(core_services_handle, FSEventStreamInvalidate);
   V(core_services_handle, FSEventStreamRelease);
   V(core_services_handle, FSEventStreamScheduleWithRunLoop);
@@ -617,7 +606,7 @@
   if (err)
     goto fail_sem_init;
 
-  QUEUE_INIT(&loop->cf_signals);
+  uv__queue_init(&loop->cf_signals);
 
   err = uv_sem_init(&state->fsevent_sem, 0);
   if (err)
@@ -627,7 +616,7 @@
   if (err)
     goto fail_fsevent_mutex_init;
 
-  QUEUE_INIT(&state->fsevent_handles);
+  uv__queue_init(&state->fsevent_handles);
   state->fsevent_need_reschedule = 0;
   state->fsevent_handle_count = 0;
 
@@ -686,7 +675,7 @@
 void uv__fsevents_loop_delete(uv_loop_t* loop) {
   uv__cf_loop_signal_t* s;
   uv__cf_loop_state_t* state;
-  QUEUE* q;
+  struct uv__queue* q;
 
   if (loop->cf_state == NULL)
     return;
@@ -699,10 +688,10 @@
   uv_mutex_destroy(&loop->cf_mutex);
 
   /* Free any remaining data */
-  while (!QUEUE_EMPTY(&loop->cf_signals)) {
-    q = QUEUE_HEAD(&loop->cf_signals);
-    s = QUEUE_DATA(q, uv__cf_loop_signal_t, member);
-    QUEUE_REMOVE(q);
+  while (!uv__queue_empty(&loop->cf_signals)) {
+    q = uv__queue_head(&loop->cf_signals);
+    s = uv__queue_data(q, uv__cf_loop_signal_t, member);
+    uv__queue_remove(q);
     uv__free(s);
   }
 
@@ -746,28 +735,28 @@
 static void uv__cf_loop_cb(void* arg) {
   uv_loop_t* loop;
   uv__cf_loop_state_t* state;
-  QUEUE* item;
-  QUEUE split_head;
+  struct uv__queue* item;
+  struct uv__queue split_head;
   uv__cf_loop_signal_t* s;
 
   loop = (uv_loop_t*)arg;
   state = (uv__cf_loop_state_t*)loop->cf_state;
 
   uv_mutex_lock(&loop->cf_mutex);
-  QUEUE_MOVE(&loop->cf_signals, &split_head);
+  uv__queue_move(&loop->cf_signals, &split_head);
   uv_mutex_unlock(&loop->cf_mutex);
 
-  while (!QUEUE_EMPTY(&split_head)) {
-    item = QUEUE_HEAD(&split_head);
-    QUEUE_REMOVE(item);
+  while (!uv__queue_empty(&split_head)) {
+    item = uv__queue_head(&split_head);
+    uv__queue_remove(item);
 
-    s = QUEUE_DATA(item, uv__cf_loop_signal_t, member);
+    s = uv__queue_data(item, uv__cf_loop_signal_t, member);
 
     /* This was a termination signal */
     if (s->handle == NULL)
       pCFRunLoopStop(state->loop);
     else
-      uv__fsevents_reschedule(s->handle, s->type);
+      uv__fsevents_reschedule(state, loop, s->type);
 
     uv__free(s);
   }
@@ -789,7 +778,7 @@
   item->type = type;
 
   uv_mutex_lock(&loop->cf_mutex);
-  QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);
+  uv__queue_insert_tail(&loop->cf_signals, &item->member);
 
   state = (uv__cf_loop_state_t*)loop->cf_state;
   assert(state != NULL);
@@ -818,7 +807,7 @@
   handle->realpath_len = strlen(handle->realpath);
 
   /* Initialize event queue */
-  QUEUE_INIT(&handle->cf_events);
+  uv__queue_init(&handle->cf_events);
   handle->cf_error = 0;
 
   /*
@@ -843,7 +832,7 @@
   /* Insert handle into the list */
   state = (uv__cf_loop_state_t*)handle->loop->cf_state;
   uv_mutex_lock(&state->fsevent_mutex);
-  QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member);
+  uv__queue_insert_tail(&state->fsevent_handles, &handle->cf_member);
   state->fsevent_handle_count++;
   state->fsevent_need_reschedule = 1;
   uv_mutex_unlock(&state->fsevent_mutex);
@@ -883,7 +872,7 @@
   /* Remove handle from  the list */
   state = (uv__cf_loop_state_t*)handle->loop->cf_state;
   uv_mutex_lock(&state->fsevent_mutex);
-  QUEUE_REMOVE(&handle->cf_member);
+  uv__queue_remove(&handle->cf_member);
   state->fsevent_handle_count--;
   state->fsevent_need_reschedule = 1;
   uv_mutex_unlock(&state->fsevent_mutex);
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/ibmi.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/ibmi.cpp
index 56af31e..5e0fa98 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/ibmi.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/ibmi.cpp
@@ -249,6 +249,11 @@
 }
 
 
+uint64_t uv_get_available_memory(void) {
+  return uv_get_free_memory();
+}
+
+
 void uv_loadavg(double avg[3]) {
   SSTS0200 rcvr;
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/internal.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/internal.h
index 2b65415..854d98a 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/internal.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/internal.h
@@ -26,21 +26,34 @@
 
 #include <assert.h>
 #include <limits.h> /* _POSIX_PATH_MAX, PATH_MAX */
+#include <stdint.h>
 #include <stdlib.h> /* abort */
 #include <string.h> /* strrchr */
 #include <fcntl.h>  /* O_CLOEXEC and O_NONBLOCK, if supported. */
 #include <stdio.h>
 #include <errno.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define uv__msan_unpoison(p, n)                                               \
+  do {                                                                        \
+    (void) (p);                                                               \
+    (void) (n);                                                               \
+  } while (0)
+
+#if defined(__has_feature)
+# if __has_feature(memory_sanitizer)
+#  include <sanitizer/msan_interface.h>
+#  undef uv__msan_unpoison
+#  define uv__msan_unpoison __msan_unpoison
+# endif
+#endif
 
 #if defined(__STRICT_ANSI__)
 # define inline __inline
 #endif
 
-#if defined(__linux__)
-# include "linux-syscalls.h"
-#endif /* __linux__ */
-
 #if defined(__MVS__)
 # include "os390-syscalls.h"
 #endif /* __MVS__ */
@@ -79,13 +92,11 @@
 # define UV__PATH_MAX 8192
 #endif
 
-#if defined(__ANDROID__)
-int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset);
-# ifdef pthread_sigmask
-# undef pthread_sigmask
-# endif
-# define pthread_sigmask(how, set, oldset) uv__pthread_sigmask(how, set, oldset)
-#endif
+union uv__sockaddr {
+  struct sockaddr_in6 in6;
+  struct sockaddr_in in;
+  struct sockaddr addr;
+};
 
 #define ACCESS_ONCE(type, var)                                                \
   (*(volatile type*) &(var))
@@ -166,12 +177,42 @@
   int fds[1];
 };
 
+#ifdef __linux__
+struct uv__statx_timestamp {
+  int64_t tv_sec;
+  uint32_t tv_nsec;
+  int32_t unused0;
+};
+
+struct uv__statx {
+  uint32_t stx_mask;
+  uint32_t stx_blksize;
+  uint64_t stx_attributes;
+  uint32_t stx_nlink;
+  uint32_t stx_uid;
+  uint32_t stx_gid;
+  uint16_t stx_mode;
+  uint16_t unused0;
+  uint64_t stx_ino;
+  uint64_t stx_size;
+  uint64_t stx_blocks;
+  uint64_t stx_attributes_mask;
+  struct uv__statx_timestamp stx_atime;
+  struct uv__statx_timestamp stx_btime;
+  struct uv__statx_timestamp stx_ctime;
+  struct uv__statx_timestamp stx_mtime;
+  uint32_t stx_rdev_major;
+  uint32_t stx_rdev_minor;
+  uint32_t stx_dev_major;
+  uint32_t stx_dev_minor;
+  uint64_t unused1[14];
+};
+#endif /* __linux__ */
 
 #if defined(_AIX) || \
     defined(__APPLE__) || \
     defined(__DragonFly__) || \
     defined(__FreeBSD__) || \
-    defined(__FreeBSD_kernel__) || \
     defined(__linux__) || \
     defined(__OpenBSD__) || \
     defined(__NetBSD__)
@@ -260,10 +301,10 @@
 /* platform specific */
 uint64_t uv__hrtime(uv_clocktype_t type);
 int uv__kqueue_init(uv_loop_t* loop);
-int uv__epoll_init(uv_loop_t* loop);
 int uv__platform_loop_init(uv_loop_t* loop);
 void uv__platform_loop_delete(uv_loop_t* loop);
 void uv__platform_invalidate_fd(uv_loop_t* loop, int fd);
+int uv__process_init(uv_loop_t* loop);
 
 /* various */
 void uv__async_close(uv_async_t* handle);
@@ -280,7 +321,6 @@
 void uv__udp_close(uv_udp_t* handle);
 void uv__udp_finish_close(uv_udp_t* handle);
 FILE* uv__open_file(const char* path);
-int uv__getpwuid_r(uv_passwd_t* pwd);
 int uv__search_path(const char* prog, char* buf, size_t* buflen);
 void uv__wait_children(uv_loop_t* loop);
 
@@ -291,6 +331,38 @@
 int uv__random_readpath(const char* path, void* buf, size_t buflen);
 int uv__random_sysctl(void* buf, size_t buflen);
 
+/* io_uring */
+#ifdef __linux__
+int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req);
+int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop,
+                                  uv_fs_t* req,
+                                  uint32_t fsync_flags);
+int uv__iou_fs_link(uv_loop_t* loop, uv_fs_t* req);
+int uv__iou_fs_mkdir(uv_loop_t* loop, uv_fs_t* req);
+int uv__iou_fs_open(uv_loop_t* loop, uv_fs_t* req);
+int uv__iou_fs_read_or_write(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             int is_read);
+int uv__iou_fs_rename(uv_loop_t* loop, uv_fs_t* req);
+int uv__iou_fs_statx(uv_loop_t* loop,
+                     uv_fs_t* req,
+                     int is_fstat,
+                     int is_lstat);
+int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req);
+int uv__iou_fs_unlink(uv_loop_t* loop, uv_fs_t* req);
+#else
+#define uv__iou_fs_close(loop, req) 0
+#define uv__iou_fs_fsync_or_fdatasync(loop, req, fsync_flags) 0
+#define uv__iou_fs_link(loop, req) 0
+#define uv__iou_fs_mkdir(loop, req) 0
+#define uv__iou_fs_open(loop, req) 0
+#define uv__iou_fs_read_or_write(loop, req, is_read) 0
+#define uv__iou_fs_rename(loop, req) 0
+#define uv__iou_fs_statx(loop, req, is_fstat, is_lstat) 0
+#define uv__iou_fs_symlink(loop, req) 0
+#define uv__iou_fs_unlink(loop, req) 0
+#endif
+
 #if defined(__APPLE__)
 int uv___stream_fd(const uv_stream_t* handle);
 #define uv__stream_fd(handle) (uv___stream_fd((const uv_stream_t*) (handle)))
@@ -324,8 +396,52 @@
   return s + 1;
 }
 
+UV_UNUSED(static int uv__fstat(int fd, struct stat* s)) {
+  int rc;
+
+  rc = fstat(fd, s);
+  if (rc >= 0)
+    uv__msan_unpoison(s, sizeof(*s));
+
+  return rc;
+}
+
+UV_UNUSED(static int uv__lstat(const char* path, struct stat* s)) {
+  int rc;
+
+  rc = lstat(path, s);
+  if (rc >= 0)
+    uv__msan_unpoison(s, sizeof(*s));
+
+  return rc;
+}
+
+UV_UNUSED(static int uv__stat(const char* path, struct stat* s)) {
+  int rc;
+
+  rc = stat(path, s);
+  if (rc >= 0)
+    uv__msan_unpoison(s, sizeof(*s));
+
+  return rc;
+}
+
 #if defined(__linux__)
-int uv__inotify_fork(uv_loop_t* loop, void* old_watchers);
+ssize_t
+uv__fs_copy_file_range(int fd_in,
+                       off_t* off_in,
+                       int fd_out,
+                       off_t* off_out,
+                       size_t len,
+                       unsigned int flags);
+int uv__statx(int dirfd,
+              const char* path,
+              int flags,
+              unsigned int mask,
+              struct uv__statx* statxbuf);
+void uv__statx_to_stat(const struct uv__statx* statxbuf, uv_stat_t* buf);
+ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags);
+unsigned uv__kernel_version(void);
 #endif
 
 typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*);
@@ -335,22 +451,6 @@
                         struct sockaddr* name,
                         int* namelen);
 
-#if defined(__linux__)            ||                                      \
-    defined(__FreeBSD__)          ||                                      \
-    defined(__FreeBSD_kernel__)   ||                                       \
-    defined(__DragonFly__)
-#define HAVE_MMSG 1
-struct uv__mmsghdr {
-  struct msghdr msg_hdr;
-  unsigned int msg_len;
-};
-
-int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen);
-int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen);
-#else
-#define HAVE_MMSG 0
-#endif
-
 #if defined(__sun)
 #if !defined(_POSIX_VERSION) || _POSIX_VERSION < 200809L
 size_t strnlen(const char* s, size_t maxlen);
@@ -367,5 +467,10 @@
                        unsigned int flags);
 #endif
 
+#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 1301000)
+#define UV__CPU_AFFINITY_SUPPORTED 1
+#else
+#define UV__CPU_AFFINITY_SUPPORTED 0
+#endif
 
 #endif /* UV_UNIX_INTERNAL_H_ */
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/kqueue.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/kqueue.cpp
index 86eb529..ffe0f91 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/kqueue.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/kqueue.cpp
@@ -34,6 +34,8 @@
 #include <fcntl.h>
 #include <time.h>
 
+#include <atomic>
+
 /*
  * Required on
  * - Until at least FreeBSD 11.0
@@ -60,7 +62,7 @@
 
 
 #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
-static int uv__has_forked_with_cfrunloop;
+static std::atomic<int> uv__has_forked_with_cfrunloop;
 #endif
 
 int uv__io_fork(uv_loop_t* loop) {
@@ -82,7 +84,9 @@
        process. So we sidestep the issue by pretending like we never
        started it in the first place.
     */
-    uv__store_relaxed(&uv__has_forked_with_cfrunloop, 1);
+    atomic_store_explicit(&uv__has_forked_with_cfrunloop,
+                          1,
+                          std::memory_order_relaxed);
     uv__free(loop->cf_state);
     loop->cf_state = NULL;
   }
@@ -109,13 +113,29 @@
 }
 
 
+static void uv__kqueue_delete(int kqfd, const struct kevent *ev) {
+  struct kevent change;
+
+  EV_SET(&change, ev->ident, ev->filter, EV_DELETE, 0, 0, 0);
+
+  if (0 == kevent(kqfd, &change, 1, NULL, 0, NULL))
+    return;
+
+  if (errno == EBADF || errno == ENOENT)
+    return;
+
+  abort();
+}
+
+
 void uv__io_poll(uv_loop_t* loop, int timeout) {
+  uv__loop_internal_fields_t* lfields;
   struct kevent events[1024];
   struct kevent* ev;
   struct timespec spec;
   unsigned int nevents;
   unsigned int revents;
-  QUEUE* q;
+  struct uv__queue* q;
   uv__io_t* w;
   uv_process_t* process;
   sigset_t* pset;
@@ -134,18 +154,19 @@
   int reset_timeout;
 
   if (loop->nfds == 0) {
-    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    assert(uv__queue_empty(&loop->watcher_queue));
     return;
   }
 
+  lfields = uv__get_internal_fields(loop);
   nevents = 0;
 
-  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
-    q = QUEUE_HEAD(&loop->watcher_queue);
-    QUEUE_REMOVE(q);
-    QUEUE_INIT(q);
+  while (!uv__queue_empty(&loop->watcher_queue)) {
+    q = uv__queue_head(&loop->watcher_queue);
+    uv__queue_remove(q);
+    uv__queue_init(q);
 
-    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+    w = uv__queue_data(q, uv__io_t, watcher_queue);
     assert(w->pevents != 0);
     assert(w->fd >= 0);
     assert(w->fd < (int) loop->nwatchers);
@@ -205,7 +226,7 @@
   base = loop->time;
   count = 48; /* Benchmarks suggest this gives the best throughput. */
 
-  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+  if (lfields->flags & UV_METRICS_IDLE_TIME) {
     reset_timeout = 1;
     user_timeout = timeout;
     timeout = 0;
@@ -228,6 +249,12 @@
     if (pset != NULL)
       pthread_sigmask(SIG_BLOCK, pset, NULL);
 
+    /* Store the current timeout in a location that's globally accessible so
+     * other locations like uv__work_done() can determine whether the queue
+     * of events in the callback were waiting when poll was called.
+     */
+    lfields->current_timeout = timeout;
+
     nfds = kevent(loop->backend_fd,
                   events,
                   nevents,
@@ -235,6 +262,9 @@
                   ARRAY_SIZE(events),
                   timeout == -1 ? NULL : &spec);
 
+    if (nfds == -1)
+      assert(errno == EINTR);
+
     if (pset != NULL)
       pthread_sigmask(SIG_UNBLOCK, pset, NULL);
 
@@ -242,36 +272,26 @@
      * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
      * operating system didn't reschedule our process while in the syscall.
      */
-    SAVE_ERRNO(uv__update_time(loop));
+    uv__update_time(loop);
 
-    if (nfds == 0) {
-      if (reset_timeout != 0) {
-        timeout = user_timeout;
-        reset_timeout = 0;
-        if (timeout == -1)
-          continue;
-        if (timeout > 0)
-          goto update_timeout;
+    if (nfds == 0 || nfds == -1) {
+      /* If kqueue is empty or interrupted, we might still have children ready
+       * to reap immediately. */
+      if (loop->flags & UV_LOOP_REAP_CHILDREN) {
+        loop->flags &= ~UV_LOOP_REAP_CHILDREN;
+        uv__wait_children(loop);
+        assert((reset_timeout == 0 ? timeout : user_timeout) == 0);
+        return; /* Equivalent to fall-through behavior. */
       }
 
-      assert(timeout != -1);
-      return;
-    }
-
-    if (nfds == -1) {
-      if (errno != EINTR)
-        abort();
-
       if (reset_timeout != 0) {
         timeout = user_timeout;
         reset_timeout = 0;
-      }
-
-      if (timeout == 0)
+      } else if (nfds == 0) {
+        /* Reached the user timeout value. */
+        assert(timeout != -1);
         return;
-
-      if (timeout == -1)
-        continue;
+      }
 
       /* Interrupted by a signal. Update timeout and poll again. */
       goto update_timeout;
@@ -289,8 +309,8 @@
 
       /* Handle kevent NOTE_EXIT results */
       if (ev->filter == EVFILT_PROC) {
-        QUEUE_FOREACH(q, &loop->process_handles) {
-          process = QUEUE_DATA(q, uv_process_t, queue);
+        uv__queue_foreach(q, &loop->process_handles) {
+          process = uv__queue_data(q, uv_process_t, queue);
           if (process->pid == fd) {
             process->flags |= UV_HANDLE_REAP;
             loop->flags |= UV_LOOP_REAP_CHILDREN;
@@ -307,15 +327,8 @@
       w = (uv__io_t*)loop->watchers[fd];
 
       if (w == NULL) {
-        /* File descriptor that we've stopped watching, disarm it.
-         * TODO: batch up. */
-        struct kevent events[1];
-
-        EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
-        if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
-          if (errno != EBADF && errno != ENOENT)
-            abort();
-
+        /* File descriptor that we've stopped watching, disarm it. */
+        uv__kqueue_delete(loop->backend_fd, ev);
         continue;
       }
 
@@ -331,47 +344,27 @@
       revents = 0;
 
       if (ev->filter == EVFILT_READ) {
-        if (w->pevents & POLLIN) {
+        if (w->pevents & POLLIN)
           revents |= POLLIN;
-          w->rcount = ev->data;
-        } else {
-          /* TODO batch up */
-          struct kevent events[1];
-          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
-          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
-            if (errno != ENOENT)
-              abort();
-        }
+        else
+          uv__kqueue_delete(loop->backend_fd, ev);
+
         if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP))
           revents |= UV__POLLRDHUP;
       }
 
       if (ev->filter == EV_OOBAND) {
-        if (w->pevents & UV__POLLPRI) {
+        if (w->pevents & UV__POLLPRI)
           revents |= UV__POLLPRI;
-          w->rcount = ev->data;
-        } else {
-          /* TODO batch up */
-          struct kevent events[1];
-          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
-          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
-            if (errno != ENOENT)
-              abort();
-        }
+        else
+          uv__kqueue_delete(loop->backend_fd, ev);
       }
 
       if (ev->filter == EVFILT_WRITE) {
-        if (w->pevents & POLLOUT) {
+        if (w->pevents & POLLOUT)
           revents |= POLLOUT;
-          w->wcount = ev->data;
-        } else {
-          /* TODO batch up */
-          struct kevent events[1];
-          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
-          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
-            if (errno != ENOENT)
-              abort();
-        }
+        else
+          uv__kqueue_delete(loop->backend_fd, ev);
       }
 
       if (ev->flags & EV_ERROR)
@@ -398,9 +391,11 @@
       uv__wait_children(loop);
     }
 
+    uv__metrics_inc_events(loop, nevents);
     if (reset_timeout != 0) {
       timeout = user_timeout;
       reset_timeout = 0;
+      uv__metrics_inc_events_waiting(loop, nevents);
     }
 
     if (have_signals != 0) {
@@ -423,13 +418,13 @@
       return;
     }
 
+update_timeout:
     if (timeout == 0)
       return;
 
     if (timeout == -1)
       continue;
 
-update_timeout:
     assert(timeout > 0);
 
     diff = loop->time - base;
@@ -541,13 +536,14 @@
   handle->realpath_len = 0;
   handle->cf_flags = flags;
 
-  if (fstat(fd, &statbuf))
+  if (uv__fstat(fd, &statbuf))
     goto fallback;
   /* FSEvents works only with directories */
   if (!(statbuf.st_mode & S_IFDIR))
     goto fallback;
 
-  if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) {
+  if (0 == atomic_load_explicit(&uv__has_forked_with_cfrunloop,
+                                std::memory_order_relaxed)) {
     int r;
     /* The fallback fd is no longer needed */
     uv__close_nocheckstdio(fd);
@@ -582,7 +578,8 @@
   uv__handle_stop(handle);
 
 #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
-  if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop))
+  if (0 == atomic_load_explicit(&uv__has_forked_with_cfrunloop,
+                                std::memory_order_relaxed))
     if (handle->cf_cb != NULL)
       r = uv__fsevents_close(handle);
 #endif
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-core.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-core.cpp
deleted file mode 100644
index 12ed7ff..0000000
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-core.cpp
+++ /dev/null
@@ -1,841 +0,0 @@
-/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
- * 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.
- */
-
-/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their
- * EPOLL* counterparts.  We use the POLL* variants in this file because that
- * is what libuv uses elsewhere.
- */
-
-#include "uv.h"
-#include "internal.h"
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <errno.h>
-
-#include <net/if.h>
-#include <sys/epoll.h>
-#include <sys/param.h>
-#include <sys/prctl.h>
-#include <sys/sysinfo.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <time.h>
-
-#define HAVE_IFADDRS_H 1
-
-# if defined(__ANDROID_API__) && __ANDROID_API__ < 24
-# undef HAVE_IFADDRS_H
-#endif
-
-#ifdef __UCLIBC__
-# if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32
-#  undef HAVE_IFADDRS_H
-# endif
-#endif
-
-#ifdef HAVE_IFADDRS_H
-# include <ifaddrs.h>
-# include <sys/socket.h>
-# include <net/ethernet.h>
-# include <netpacket/packet.h>
-#endif /* HAVE_IFADDRS_H */
-
-/* Available from 2.6.32 onwards. */
-#ifndef CLOCK_MONOTONIC_COARSE
-# define CLOCK_MONOTONIC_COARSE 6
-#endif
-
-#ifdef __FRC_ROBORIO__
-#include "wpi/timestamp.h"
-#endif
-
-/* This is rather annoying: CLOCK_BOOTTIME lives in <linux/time.h> but we can't
- * include that file because it conflicts with <time.h>. We'll just have to
- * define it ourselves.
- */
-#ifndef CLOCK_BOOTTIME
-# define CLOCK_BOOTTIME 7
-#endif
-
-static int read_models(unsigned int numcpus, uv_cpu_info_t* ci);
-static int read_times(FILE* statfile_fp,
-                      unsigned int numcpus,
-                      uv_cpu_info_t* ci);
-static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci);
-static uint64_t read_cpufreq(unsigned int cpunum);
-
-int uv__platform_loop_init(uv_loop_t* loop) {
-  
-  loop->inotify_fd = -1;
-  loop->inotify_watchers = NULL;
-
-  return uv__epoll_init(loop);
-}
-
-
-int uv__io_fork(uv_loop_t* loop) {
-  int err;
-  void* old_watchers;
-
-  old_watchers = loop->inotify_watchers;
-
-  uv__close(loop->backend_fd);
-  loop->backend_fd = -1;
-  uv__platform_loop_delete(loop);
-
-  err = uv__platform_loop_init(loop);
-  if (err)
-    return err;
-
-  return uv__inotify_fork(loop, old_watchers);
-}
-
-
-void uv__platform_loop_delete(uv_loop_t* loop) {
-  if (loop->inotify_fd == -1) return;
-  uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN);
-  uv__close(loop->inotify_fd);
-  loop->inotify_fd = -1;
-}
-
-
-uint64_t uv__hrtime(uv_clocktype_t type) {
-#ifdef __FRC_ROBORIO__
-  return wpi::Now() * 1000u;
-#else
-  static clock_t fast_clock_id = -1;
-  struct timespec t;
-  clock_t clock_id;
-
-  /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has
-   * millisecond granularity or better.  CLOCK_MONOTONIC_COARSE is
-   * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may
-   * decide to make a costly system call.
-   */
-  /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE
-   * when it has microsecond granularity or better (unlikely).
-   */
-  clock_id = CLOCK_MONOTONIC;
-  if (type != UV_CLOCK_FAST)
-    goto done;
-
-  clock_id = uv__load_relaxed(&fast_clock_id);
-  if (clock_id != -1)
-    goto done;
-
-  clock_id = CLOCK_MONOTONIC;
-  if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t))
-    if (t.tv_nsec <= 1 * 1000 * 1000)
-      clock_id = CLOCK_MONOTONIC_COARSE;
-
-  uv__store_relaxed(&fast_clock_id, clock_id);
-
-done:
-
-  if (clock_gettime(clock_id, &t))
-    return 0;  /* Not really possible. */
-
-  return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
-#endif
-}
-
-
-int uv_resident_set_memory(size_t* rss) {
-  char buf[1024];
-  const char* s;
-  ssize_t n;
-  long val;
-  int fd;
-  int i;
-
-  do
-    fd = open("/proc/self/stat", O_RDONLY);
-  while (fd == -1 && errno == EINTR);
-
-  if (fd == -1)
-    return UV__ERR(errno);
-
-  do
-    n = read(fd, buf, sizeof(buf) - 1);
-  while (n == -1 && errno == EINTR);
-
-  uv__close(fd);
-  if (n == -1)
-    return UV__ERR(errno);
-  buf[n] = '\0';
-
-  s = strchr(buf, ' ');
-  if (s == NULL)
-    goto err;
-
-  s += 1;
-  if (*s != '(')
-    goto err;
-
-  s = strchr(s, ')');
-  if (s == NULL)
-    goto err;
-
-  for (i = 1; i <= 22; i++) {
-    s = strchr(s + 1, ' ');
-    if (s == NULL)
-      goto err;
-  }
-
-  errno = 0;
-  val = strtol(s, NULL, 10);
-  if (errno != 0)
-    goto err;
-  if (val < 0)
-    goto err;
-
-  *rss = val * getpagesize();
-  return 0;
-
-err:
-  return UV_EINVAL;
-}
-
-int uv_uptime(double* uptime) {
-  static volatile int no_clock_boottime;
-  char buf[128];
-  struct timespec now;
-  int r;
-
-  /* Try /proc/uptime first, then fallback to clock_gettime(). */
-
-  if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf)))
-    if (1 == sscanf(buf, "%lf", uptime))
-      return 0;
-
-  /* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available
-   * (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system
-   * is suspended.
-   */
-  if (no_clock_boottime) {
-    retry_clock_gettime: r = clock_gettime(CLOCK_MONOTONIC, &now);
-  }
-  else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) {
-    no_clock_boottime = 1;
-    goto retry_clock_gettime;
-  }
-
-  if (r)
-    return UV__ERR(errno);
-
-  *uptime = now.tv_sec;
-  return 0;
-}
-
-
-static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) {
-  unsigned int num;
-  char buf[1024];
-
-  if (!fgets(buf, sizeof(buf), statfile_fp))
-    return UV_EIO;
-
-  num = 0;
-  while (fgets(buf, sizeof(buf), statfile_fp)) {
-    if (strncmp(buf, "cpu", 3))
-      break;
-    num++;
-  }
-
-  if (num == 0)
-    return UV_EIO;
-
-  *numcpus = num;
-  return 0;
-}
-
-
-int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
-  unsigned int numcpus;
-  uv_cpu_info_t* ci;
-  int err;
-  FILE* statfile_fp;
-
-  *cpu_infos = NULL;
-  *count = 0;
-
-  statfile_fp = uv__open_file("/proc/stat");
-  if (statfile_fp == NULL)
-    return UV__ERR(errno);
-
-  err = uv__cpu_num(statfile_fp, &numcpus);
-  if (err < 0)
-    goto out;
-
-  err = UV_ENOMEM;
-  ci = (uv_cpu_info_t*)uv__calloc(numcpus, sizeof(*ci));
-  if (ci == NULL)
-    goto out;
-
-  err = read_models(numcpus, ci);
-  if (err == 0)
-    err = read_times(statfile_fp, numcpus, ci);
-
-  if (err) {
-    uv_free_cpu_info(ci, numcpus);
-    goto out;
-  }
-
-  /* read_models() on x86 also reads the CPU speed from /proc/cpuinfo.
-   * We don't check for errors here. Worst case, the field is left zero.
-   */
-  if (ci[0].speed == 0)
-    read_speeds(numcpus, ci);
-
-  *cpu_infos = ci;
-  *count = numcpus;
-  err = 0;
-
-out:
-
-  if (fclose(statfile_fp))
-    if (errno != EINTR && errno != EINPROGRESS)
-      abort();
-
-  return err;
-}
-
-
-static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) {
-  unsigned int num;
-
-  for (num = 0; num < numcpus; num++)
-    ci[num].speed = read_cpufreq(num) / 1000;
-}
-
-
-/* Also reads the CPU frequency on ppc and x86. The other architectures only
- * have a BogoMIPS field, which may not be very accurate.
- *
- * Note: Simply returns on error, uv_cpu_info() takes care of the cleanup.
- */
-static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) {
-#if defined(__PPC__)
-  static const char model_marker[] = "cpu\t\t: ";
-  static const char speed_marker[] = "clock\t\t: ";
-#else
-  static const char model_marker[] = "model name\t: ";
-  static const char speed_marker[] = "cpu MHz\t\t: ";
-#endif
-  const char* inferred_model;
-  unsigned int model_idx;
-  unsigned int speed_idx;
-  unsigned int part_idx;
-  char buf[1024];
-  char* model;
-  FILE* fp;
-  int model_id;
-
-  /* Most are unused on non-ARM, non-MIPS and non-x86 architectures. */
-  (void) &model_marker;
-  (void) &speed_marker;
-  (void) &speed_idx;
-  (void) &part_idx;
-  (void) &model;
-  (void) &buf;
-  (void) &fp;
-  (void) &model_id;
-
-  model_idx = 0;
-  speed_idx = 0;
-  part_idx = 0;
-
-#if defined(__arm__) || \
-    defined(__i386__) || \
-    defined(__mips__) || \
-    defined(__aarch64__) || \
-    defined(__PPC__) || \
-    defined(__x86_64__)
-  fp = uv__open_file("/proc/cpuinfo");
-  if (fp == NULL)
-    return UV__ERR(errno);
-
-  while (fgets(buf, sizeof(buf), fp)) {
-    if (model_idx < numcpus) {
-      if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) {
-        model = buf + sizeof(model_marker) - 1;
-        model = uv__strndup(model, strlen(model) - 1);  /* Strip newline. */
-        if (model == NULL) {
-          fclose(fp);
-          return UV_ENOMEM;
-        }
-        ci[model_idx++].model = model;
-        continue;
-      }
-    }
-#if defined(__arm__) || defined(__mips__) || defined(__aarch64__)
-    if (model_idx < numcpus) {
-#if defined(__arm__)
-      /* Fallback for pre-3.8 kernels. */
-      static const char model_marker[] = "Processor\t: ";
-#elif defined(__aarch64__)
-      static const char part_marker[] = "CPU part\t: ";
-
-      /* Adapted from: https://github.com/karelzak/util-linux */
-      struct vendor_part {
-        const int id;
-        const char* name;
-      };
-
-      static const struct vendor_part arm_chips[] = {
-        { 0x811, "ARM810" },
-        { 0x920, "ARM920" },
-        { 0x922, "ARM922" },
-        { 0x926, "ARM926" },
-        { 0x940, "ARM940" },
-        { 0x946, "ARM946" },
-        { 0x966, "ARM966" },
-        { 0xa20, "ARM1020" },
-        { 0xa22, "ARM1022" },
-        { 0xa26, "ARM1026" },
-        { 0xb02, "ARM11 MPCore" },
-        { 0xb36, "ARM1136" },
-        { 0xb56, "ARM1156" },
-        { 0xb76, "ARM1176" },
-        { 0xc05, "Cortex-A5" },
-        { 0xc07, "Cortex-A7" },
-        { 0xc08, "Cortex-A8" },
-        { 0xc09, "Cortex-A9" },
-        { 0xc0d, "Cortex-A17" },  /* Originally A12 */
-        { 0xc0f, "Cortex-A15" },
-        { 0xc0e, "Cortex-A17" },
-        { 0xc14, "Cortex-R4" },
-        { 0xc15, "Cortex-R5" },
-        { 0xc17, "Cortex-R7" },
-        { 0xc18, "Cortex-R8" },
-        { 0xc20, "Cortex-M0" },
-        { 0xc21, "Cortex-M1" },
-        { 0xc23, "Cortex-M3" },
-        { 0xc24, "Cortex-M4" },
-        { 0xc27, "Cortex-M7" },
-        { 0xc60, "Cortex-M0+" },
-        { 0xd01, "Cortex-A32" },
-        { 0xd03, "Cortex-A53" },
-        { 0xd04, "Cortex-A35" },
-        { 0xd05, "Cortex-A55" },
-        { 0xd06, "Cortex-A65" },
-        { 0xd07, "Cortex-A57" },
-        { 0xd08, "Cortex-A72" },
-        { 0xd09, "Cortex-A73" },
-        { 0xd0a, "Cortex-A75" },
-        { 0xd0b, "Cortex-A76" },
-        { 0xd0c, "Neoverse-N1" },
-        { 0xd0d, "Cortex-A77" },
-        { 0xd0e, "Cortex-A76AE" },
-        { 0xd13, "Cortex-R52" },
-        { 0xd20, "Cortex-M23" },
-        { 0xd21, "Cortex-M33" },
-        { 0xd41, "Cortex-A78" },
-        { 0xd42, "Cortex-A78AE" },
-        { 0xd4a, "Neoverse-E1" },
-        { 0xd4b, "Cortex-A78C" },
-      };
-
-      if (strncmp(buf, part_marker, sizeof(part_marker) - 1) == 0) {
-        model = buf + sizeof(part_marker) - 1;
-
-        errno = 0;
-        model_id = strtol(model, NULL, 16);
-        if ((errno != 0) || model_id < 0) {
-          fclose(fp);
-          return UV_EINVAL;
-        }
-
-        for (part_idx = 0; part_idx < ARRAY_SIZE(arm_chips); part_idx++) {
-          if (model_id == arm_chips[part_idx].id) {
-            model = uv__strdup(arm_chips[part_idx].name);
-            if (model == NULL) {
-              fclose(fp);
-              return UV_ENOMEM;
-            }
-            ci[model_idx++].model = model;
-            break;
-          }
-        }
-      }
-#else	/* defined(__mips__) */
-      static const char model_marker[] = "cpu model\t\t: ";
-#endif
-      if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) {
-        model = buf + sizeof(model_marker) - 1;
-        model = uv__strndup(model, strlen(model) - 1);  /* Strip newline. */
-        if (model == NULL) {
-          fclose(fp);
-          return UV_ENOMEM;
-        }
-        ci[model_idx++].model = model;
-        continue;
-      }
-    }
-#else  /* !__arm__ && !__mips__ && !__aarch64__ */
-    if (speed_idx < numcpus) {
-      if (strncmp(buf, speed_marker, sizeof(speed_marker) - 1) == 0) {
-        ci[speed_idx++].speed = atoi(buf + sizeof(speed_marker) - 1);
-        continue;
-      }
-    }
-#endif  /* __arm__ || __mips__ || __aarch64__ */
-  }
-
-  fclose(fp);
-#endif  /* __arm__ || __i386__ || __mips__ || __PPC__ || __x86_64__ || __aarch__ */
-
-  /* Now we want to make sure that all the models contain *something* because
-   * it's not safe to leave them as null. Copy the last entry unless there
-   * isn't one, in that case we simply put "unknown" into everything.
-   */
-  inferred_model = "unknown";
-  if (model_idx > 0)
-    inferred_model = ci[model_idx - 1].model;
-
-  while (model_idx < numcpus) {
-    model = uv__strndup(inferred_model, strlen(inferred_model));
-    if (model == NULL)
-      return UV_ENOMEM;
-    ci[model_idx++].model = model;
-  }
-
-  return 0;
-}
-
-
-static int read_times(FILE* statfile_fp,
-                      unsigned int numcpus,
-                      uv_cpu_info_t* ci) {
-  struct uv_cpu_times_s ts;
-  unsigned int ticks;
-  unsigned int multiplier;
-  uint64_t user;
-  uint64_t nice;
-  uint64_t sys;
-  uint64_t idle;
-  uint64_t dummy;
-  uint64_t irq;
-  uint64_t num;
-  uint64_t len;
-  char buf[1024];
-
-  ticks = (unsigned int)sysconf(_SC_CLK_TCK);
-  assert(ticks != (unsigned int) -1);
-  assert(ticks != 0);
-  multiplier = ((uint64_t)1000L / ticks);
-
-  rewind(statfile_fp);
-
-  if (!fgets(buf, sizeof(buf), statfile_fp))
-    abort();
-
-  num = 0;
-
-  while (fgets(buf, sizeof(buf), statfile_fp)) {
-    if (num >= numcpus)
-      break;
-
-    if (strncmp(buf, "cpu", 3))
-      break;
-
-    /* skip "cpu<num> " marker */
-    {
-      unsigned int n;
-      int r = sscanf(buf, "cpu%u ", &n);
-      assert(r == 1);
-      (void) r;  /* silence build warning */
-      for (len = sizeof("cpu0"); n /= 10; len++);
-    }
-
-    /* Line contains user, nice, system, idle, iowait, irq, softirq, steal,
-     * guest, guest_nice but we're only interested in the first four + irq.
-     *
-     * Don't use %*s to skip fields or %ll to read straight into the uint64_t
-     * fields, they're not allowed in C89 mode.
-     */
-    if (6 != sscanf(buf + len,
-                    "%" PRIu64 " %" PRIu64 " %" PRIu64
-                    "%" PRIu64 " %" PRIu64 " %" PRIu64,
-                    &user,
-                    &nice,
-                    &sys,
-                    &idle,
-                    &dummy,
-                    &irq))
-      abort();
-
-    ts.user = user * multiplier;
-    ts.nice = nice * multiplier;
-    ts.sys  = sys * multiplier;
-    ts.idle = idle * multiplier;
-    ts.irq  = irq * multiplier;
-    ci[num++].cpu_times = ts;
-  }
-  assert(num == numcpus);
-
-  return 0;
-}
-
-
-static uint64_t read_cpufreq(unsigned int cpunum) {
-  uint64_t val;
-  char buf[1024];
-  FILE* fp;
-
-  snprintf(buf,
-           sizeof(buf),
-           "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq",
-           cpunum);
-
-  fp = uv__open_file(buf);
-  if (fp == NULL)
-    return 0;
-
-  if (fscanf(fp, "%" PRIu64, &val) != 1)
-    val = 0;
-
-  fclose(fp);
-
-  return val;
-}
-
-
-#ifdef HAVE_IFADDRS_H
-static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
-  if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
-    return 1;
-  if (ent->ifa_addr == NULL)
-    return 1;
-  /*
-   * On Linux getifaddrs returns information related to the raw underlying
-   * devices. We're not interested in this information yet.
-   */
-  if (ent->ifa_addr->sa_family == PF_PACKET)
-    return exclude_type;
-  return !exclude_type;
-}
-#endif
-
-int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
-#ifndef HAVE_IFADDRS_H
-  *count = 0;
-  *addresses = NULL;
-  return UV_ENOSYS;
-#else
-  struct ifaddrs *addrs, *ent;
-  uv_interface_address_t* address;
-  int i;
-  struct sockaddr_ll *sll;
-
-  *count = 0;
-  *addresses = NULL;
-
-  if (getifaddrs(&addrs))
-    return UV__ERR(errno);
-
-  /* Count the number of interfaces */
-  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
-    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
-      continue;
-
-    (*count)++;
-  }
-
-  if (*count == 0) {
-    freeifaddrs(addrs);
-    return 0;
-  }
-
-  /* Make sure the memory is initiallized to zero using calloc() */
-  *addresses = (uv_interface_address_t*)uv__calloc(*count, sizeof(**addresses));
-  if (!(*addresses)) {
-    freeifaddrs(addrs);
-    return UV_ENOMEM;
-  }
-
-  address = *addresses;
-
-  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
-    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
-      continue;
-
-    address->name = uv__strdup(ent->ifa_name);
-
-    if (ent->ifa_addr->sa_family == AF_INET6) {
-      address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
-    } else {
-      address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
-    }
-
-    if (ent->ifa_netmask->sa_family == AF_INET6) {
-      address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
-    } else {
-      address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
-    }
-
-    address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
-
-    address++;
-  }
-
-  /* Fill in physical addresses for each interface */
-  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
-    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
-      continue;
-
-    address = *addresses;
-
-    for (i = 0; i < (*count); i++) {
-      size_t namelen = strlen(ent->ifa_name);
-      /* Alias interface share the same physical address */
-      if (strncmp(address->name, ent->ifa_name, namelen) == 0 &&
-          (address->name[namelen] == 0 || address->name[namelen] == ':')) {
-        sll = (struct sockaddr_ll*)ent->ifa_addr;
-        memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr));
-      }
-      address++;
-    }
-  }
-
-  freeifaddrs(addrs);
-
-  return 0;
-#endif
-}
-
-
-void uv_free_interface_addresses(uv_interface_address_t* addresses,
-  int count) {
-  int i;
-
-  for (i = 0; i < count; i++) {
-    uv__free(addresses[i].name);
-  }
-
-  uv__free(addresses);
-}
-
-
-void uv__set_process_title(const char* title) {
-#if defined(PR_SET_NAME)
-  prctl(PR_SET_NAME, title);  /* Only copies first 16 characters. */
-#endif
-}
-
-
-static uint64_t uv__read_proc_meminfo(const char* what) {
-  uint64_t rc;
-  char* p;
-  char buf[4096];  /* Large enough to hold all of /proc/meminfo. */
-
-  if (uv__slurp("/proc/meminfo", buf, sizeof(buf)))
-    return 0;
-
-  p = strstr(buf, what);
-
-  if (p == NULL)
-    return 0;
-
-  p += strlen(what);
-
-  rc = 0;
-  sscanf(p, "%" PRIu64 " kB", &rc);
-
-  return rc * 1024;
-}
-
-
-uint64_t uv_get_free_memory(void) {
-  struct sysinfo info;
-  uint64_t rc;
-
-  rc = uv__read_proc_meminfo("MemAvailable:");
-
-  if (rc != 0)
-    return rc;
-
-  if (0 == sysinfo(&info))
-    return (uint64_t) info.freeram * info.mem_unit;
-
-  return 0;
-}
-
-
-uint64_t uv_get_total_memory(void) {
-  struct sysinfo info;
-  uint64_t rc;
-
-  rc = uv__read_proc_meminfo("MemTotal:");
-
-  if (rc != 0)
-    return rc;
-
-  if (0 == sysinfo(&info))
-    return (uint64_t) info.totalram * info.mem_unit;
-
-  return 0;
-}
-
-
-static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) {
-  char filename[256];
-  char buf[32];  /* Large enough to hold an encoded uint64_t. */
-  uint64_t rc;
-
-  rc = 0;
-  snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/%s", cgroup, param);
-  if (0 == uv__slurp(filename, buf, sizeof(buf)))
-    sscanf(buf, "%" PRIu64, &rc);
-
-  return rc;
-}
-
-
-uint64_t uv_get_constrained_memory(void) {
-  /*
-   * This might return 0 if there was a problem getting the memory limit from
-   * cgroups. This is OK because a return value of 0 signifies that the memory
-   * limit is unknown.
-   */
-  return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes");
-}
-
-
-void uv_loadavg(double avg[3]) {
-  struct sysinfo info;
-  char buf[128];  /* Large enough to hold all of /proc/loadavg. */
-
-  if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf)))
-    if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2]))
-      return;
-
-  if (sysinfo(&info) < 0)
-    return;
-
-  avg[0] = (double) info.loads[0] / 65536.0;
-  avg[1] = (double) info.loads[1] / 65536.0;
-  avg[2] = (double) info.loads[2] / 65536.0;
-}
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-inotify.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-inotify.cpp
deleted file mode 100644
index f5366e9..0000000
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-inotify.cpp
+++ /dev/null
@@ -1,327 +0,0 @@
-/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
- * 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 "uv.h"
-#include "uv/tree.h"
-#include "internal.h"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <errno.h>
-
-#include <sys/inotify.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-struct watcher_list {
-  RB_ENTRY(watcher_list) entry;
-  QUEUE watchers;
-  int iterating;
-  char* path;
-  int wd;
-};
-
-struct watcher_root {
-  struct watcher_list* rbh_root;
-};
-#define CAST(p) ((struct watcher_root*)(p))
-
-
-static int compare_watchers(const struct watcher_list* a,
-                            const struct watcher_list* b) {
-  if (a->wd < b->wd) return -1;
-  if (a->wd > b->wd) return 1;
-  return 0;
-}
-
-
-RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers)
-
-
-static void uv__inotify_read(uv_loop_t* loop,
-                             uv__io_t* w,
-                             unsigned int revents);
-
-static void maybe_free_watcher_list(struct watcher_list* w,
-                                    uv_loop_t* loop);
-
-static int init_inotify(uv_loop_t* loop) {
-  int fd;
-
-  if (loop->inotify_fd != -1)
-    return 0;
-
-  fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
-  if (fd < 0)
-    return UV__ERR(errno);
-
-  loop->inotify_fd = fd;
-  uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd);
-  uv__io_start(loop, &loop->inotify_read_watcher, POLLIN);
-
-  return 0;
-}
-
-
-int uv__inotify_fork(uv_loop_t* loop, void* old_watchers) {
-  /* Open the inotify_fd, and re-arm all the inotify watchers. */
-  int err;
-  struct watcher_list* tmp_watcher_list_iter;
-  struct watcher_list* watcher_list;
-  struct watcher_list tmp_watcher_list;
-  QUEUE queue;
-  QUEUE* q;
-  uv_fs_event_t* handle;
-  char* tmp_path;
-
-  if (old_watchers != NULL) {
-    /* We must restore the old watcher list to be able to close items
-     * out of it.
-     */
-    loop->inotify_watchers = old_watchers;
-
-    QUEUE_INIT(&tmp_watcher_list.watchers);
-    /* Note that the queue we use is shared with the start and stop()
-     * functions, making QUEUE_FOREACH unsafe to use. So we use the
-     * QUEUE_MOVE trick to safely iterate. Also don't free the watcher
-     * list until we're done iterating. c.f. uv__inotify_read.
-     */
-    RB_FOREACH_SAFE(watcher_list, watcher_root,
-                    CAST(&old_watchers), tmp_watcher_list_iter) {
-      watcher_list->iterating = 1;
-      QUEUE_MOVE(&watcher_list->watchers, &queue);
-      while (!QUEUE_EMPTY(&queue)) {
-        q = QUEUE_HEAD(&queue);
-        handle = QUEUE_DATA(q, uv_fs_event_t, watchers);
-        /* It's critical to keep a copy of path here, because it
-         * will be set to NULL by stop() and then deallocated by
-         * maybe_free_watcher_list
-         */
-        tmp_path = uv__strdup(handle->path);
-        assert(tmp_path != NULL);
-        QUEUE_REMOVE(q);
-        QUEUE_INSERT_TAIL(&watcher_list->watchers, q);
-        uv_fs_event_stop(handle);
-
-        QUEUE_INSERT_TAIL(&tmp_watcher_list.watchers, &handle->watchers);
-        handle->path = tmp_path;
-      }
-      watcher_list->iterating = 0;
-      maybe_free_watcher_list(watcher_list, loop);
-    }
-
-    QUEUE_MOVE(&tmp_watcher_list.watchers, &queue);
-    while (!QUEUE_EMPTY(&queue)) {
-        q = QUEUE_HEAD(&queue);
-        QUEUE_REMOVE(q);
-        handle = QUEUE_DATA(q, uv_fs_event_t, watchers);
-        tmp_path = handle->path;
-        handle->path = NULL;
-        err = uv_fs_event_start(handle, handle->cb, tmp_path, 0);
-        uv__free(tmp_path);
-        if (err)
-          return err;
-    }
-  }
-
-  return 0;
-}
-
-
-static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) {
-  struct watcher_list w;
-  w.wd = wd;
-  return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w);
-}
-
-static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) {
-  /* if the watcher_list->watchers is being iterated over, we can't free it. */
-  if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) {
-    /* No watchers left for this path. Clean up. */
-    RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w);
-    inotify_rm_watch(loop->inotify_fd, w->wd);
-    uv__free(w);
-  }
-}
-
-static void uv__inotify_read(uv_loop_t* loop,
-                             uv__io_t* dummy,
-                             unsigned int events) {
-  const struct inotify_event* e;
-  struct watcher_list* w;
-  uv_fs_event_t* h;
-  QUEUE queue;
-  QUEUE* q;
-  const char* path;
-  ssize_t size;
-  const char *p;
-  /* needs to be large enough for sizeof(inotify_event) + strlen(path) */
-  char buf[4096];
-
-  for (;;) {
-    do
-      size = read(loop->inotify_fd, buf, sizeof(buf));
-    while (size == -1 && errno == EINTR);
-
-    if (size == -1) {
-      assert(errno == EAGAIN || errno == EWOULDBLOCK);
-      break;
-    }
-
-    assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */
-
-    /* Now we have one or more inotify_event structs. */
-    for (p = buf; p < buf + size; p += sizeof(*e) + e->len) {
-      e = (const struct inotify_event*) p;
-
-      events = 0;
-      if (e->mask & (IN_ATTRIB|IN_MODIFY))
-        events |= UV_CHANGE;
-      if (e->mask & ~(IN_ATTRIB|IN_MODIFY))
-        events |= UV_RENAME;
-
-      w = find_watcher(loop, e->wd);
-      if (w == NULL)
-        continue; /* Stale event, no watchers left. */
-
-      /* inotify does not return the filename when monitoring a single file
-       * for modifications. Repurpose the filename for API compatibility.
-       * I'm not convinced this is a good thing, maybe it should go.
-       */
-      path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path);
-
-      /* We're about to iterate over the queue and call user's callbacks.
-       * What can go wrong?
-       * A callback could call uv_fs_event_stop()
-       * and the queue can change under our feet.
-       * So, we use QUEUE_MOVE() trick to safely iterate over the queue.
-       * And we don't free the watcher_list until we're done iterating.
-       *
-       * First,
-       * tell uv_fs_event_stop() (that could be called from a user's callback)
-       * not to free watcher_list.
-       */
-      w->iterating = 1;
-      QUEUE_MOVE(&w->watchers, &queue);
-      while (!QUEUE_EMPTY(&queue)) {
-        q = QUEUE_HEAD(&queue);
-        h = QUEUE_DATA(q, uv_fs_event_t, watchers);
-
-        QUEUE_REMOVE(q);
-        QUEUE_INSERT_TAIL(&w->watchers, q);
-
-        h->cb(h, path, events, 0);
-      }
-      /* done iterating, time to (maybe) free empty watcher_list */
-      w->iterating = 0;
-      maybe_free_watcher_list(w, loop);
-    }
-  }
-}
-
-
-int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
-  uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
-  return 0;
-}
-
-
-int uv_fs_event_start(uv_fs_event_t* handle,
-                      uv_fs_event_cb cb,
-                      const char* path,
-                      unsigned int flags) {
-  struct watcher_list* w;
-  size_t len;
-  int events;
-  int err;
-  int wd;
-
-  if (uv__is_active(handle))
-    return UV_EINVAL;
-
-  err = init_inotify(handle->loop);
-  if (err)
-    return err;
-
-  events = IN_ATTRIB
-         | IN_CREATE
-         | IN_MODIFY
-         | IN_DELETE
-         | IN_DELETE_SELF
-         | IN_MOVE_SELF
-         | IN_MOVED_FROM
-         | IN_MOVED_TO;
-
-  wd = inotify_add_watch(handle->loop->inotify_fd, path, events);
-  if (wd == -1)
-    return UV__ERR(errno);
-
-  w = find_watcher(handle->loop, wd);
-  if (w)
-    goto no_insert;
-
-  len = strlen(path) + 1;
-  w = (watcher_list*)uv__malloc(sizeof(*w) + len);
-  if (w == NULL)
-    return UV_ENOMEM;
-
-  w->wd = wd;
-  w->path = (char*)memcpy(w + 1, path, len);
-  QUEUE_INIT(&w->watchers);
-  w->iterating = 0;
-  RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w);
-
-no_insert:
-  uv__handle_start(handle);
-  QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers);
-  handle->path = w->path;
-  handle->cb = cb;
-  handle->wd = wd;
-
-  return 0;
-}
-
-
-int uv_fs_event_stop(uv_fs_event_t* handle) {
-  struct watcher_list* w;
-
-  if (!uv__is_active(handle))
-    return 0;
-
-  w = find_watcher(handle->loop, handle->wd);
-  assert(w != NULL);
-
-  handle->wd = -1;
-  handle->path = NULL;
-  uv__handle_stop(handle);
-  QUEUE_REMOVE(&handle->watchers);
-
-  maybe_free_watcher_list(w, handle->loop);
-
-  return 0;
-}
-
-
-void uv__fs_event_close(uv_fs_event_t* handle) {
-  uv_fs_event_stop(handle);
-}
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.cpp
deleted file mode 100644
index 5071cd5..0000000
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
- *
- * 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 "linux-syscalls.h"
-#include <unistd.h>
-#include <signal.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <errno.h>
-
-#if defined(__arm__)
-# if defined(__thumb__) || defined(__ARM_EABI__)
-#  define UV_SYSCALL_BASE 0
-# else
-#  define UV_SYSCALL_BASE 0x900000
-# endif
-#endif /* __arm__ */
-
-#ifndef __NR_recvmmsg
-# if defined(__x86_64__)
-#  define __NR_recvmmsg 299
-# elif defined(__arm__)
-#  define __NR_recvmmsg (UV_SYSCALL_BASE + 365)
-# endif
-#endif /* __NR_recvmsg */
-
-#ifndef __NR_sendmmsg
-# if defined(__x86_64__)
-#  define __NR_sendmmsg 307
-# elif defined(__arm__)
-#  define __NR_sendmmsg (UV_SYSCALL_BASE + 374)
-# endif
-#endif /* __NR_sendmmsg */
-
-#ifndef __NR_utimensat
-# if defined(__x86_64__)
-#  define __NR_utimensat 280
-# elif defined(__i386__)
-#  define __NR_utimensat 320
-# elif defined(__arm__)
-#  define __NR_utimensat (UV_SYSCALL_BASE + 348)
-# endif
-#endif /* __NR_utimensat */
-
-#ifndef __NR_preadv
-# if defined(__x86_64__)
-#  define __NR_preadv 295
-# elif defined(__i386__)
-#  define __NR_preadv 333
-# elif defined(__arm__)
-#  define __NR_preadv (UV_SYSCALL_BASE + 361)
-# endif
-#endif /* __NR_preadv */
-
-#ifndef __NR_pwritev
-# if defined(__x86_64__)
-#  define __NR_pwritev 296
-# elif defined(__i386__)
-#  define __NR_pwritev 334
-# elif defined(__arm__)
-#  define __NR_pwritev (UV_SYSCALL_BASE + 362)
-# endif
-#endif /* __NR_pwritev */
-
-#ifndef __NR_dup3
-# if defined(__x86_64__)
-#  define __NR_dup3 292
-# elif defined(__i386__)
-#  define __NR_dup3 330
-# elif defined(__arm__)
-#  define __NR_dup3 (UV_SYSCALL_BASE + 358)
-# endif
-#endif /* __NR_pwritev */
-
-#ifndef __NR_copy_file_range
-# if defined(__x86_64__)
-#  define __NR_copy_file_range 326
-# elif defined(__i386__)
-#  define __NR_copy_file_range 377
-# elif defined(__s390__)
-#  define __NR_copy_file_range 375
-# elif defined(__arm__)
-#  define __NR_copy_file_range (UV_SYSCALL_BASE + 391)
-# elif defined(__aarch64__)
-#  define __NR_copy_file_range 285
-# elif defined(__powerpc__)
-#  define __NR_copy_file_range 379
-# elif defined(__arc__)
-#  define __NR_copy_file_range 285
-# endif
-#endif /* __NR_copy_file_range */
-
-#ifndef __NR_statx
-# if defined(__x86_64__)
-#  define __NR_statx 332
-# elif defined(__i386__)
-#  define __NR_statx 383
-# elif defined(__aarch64__)
-#  define __NR_statx 397
-# elif defined(__arm__)
-#  define __NR_statx (UV_SYSCALL_BASE + 397)
-# elif defined(__ppc__)
-#  define __NR_statx 383
-# elif defined(__s390__)
-#  define __NR_statx 379
-# endif
-#endif /* __NR_statx */
-
-#ifndef __NR_getrandom
-# if defined(__x86_64__)
-#  define __NR_getrandom 318
-# elif defined(__i386__)
-#  define __NR_getrandom 355
-# elif defined(__aarch64__)
-#  define __NR_getrandom 384
-# elif defined(__arm__)
-#  define __NR_getrandom (UV_SYSCALL_BASE + 384)
-# elif defined(__ppc__)
-#  define __NR_getrandom 359
-# elif defined(__s390__)
-#  define __NR_getrandom 349
-# endif
-#endif /* __NR_getrandom */
-
-struct uv__mmsghdr;
-
-int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) {
-#if defined(__i386__)
-  unsigned long args[4];
-  int rc;
-
-  args[0] = (unsigned long) fd;
-  args[1] = (unsigned long) mmsg;
-  args[2] = (unsigned long) vlen;
-  args[3] = /* flags */ 0;
-
-  /* socketcall() raises EINVAL when SYS_SENDMMSG is not supported. */
-  rc = syscall(/* __NR_socketcall */ 102, 20 /* SYS_SENDMMSG */, args);
-  if (rc == -1)
-    if (errno == EINVAL)
-      errno = ENOSYS;
-
-  return rc;
-#elif defined(__NR_sendmmsg)
-  return syscall(__NR_sendmmsg, fd, mmsg, vlen, /* flags */ 0);
-#else
-  return errno = ENOSYS, -1;
-#endif
-}
-
-
-int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) {
-#if defined(__i386__)
-  unsigned long args[5];
-  int rc;
-
-  args[0] = (unsigned long) fd;
-  args[1] = (unsigned long) mmsg;
-  args[2] = (unsigned long) vlen;
-  args[3] = /* flags */ 0;
-  args[4] = /* timeout */ 0;
-
-  /* socketcall() raises EINVAL when SYS_RECVMMSG is not supported. */
-  rc = syscall(/* __NR_socketcall */ 102, 19 /* SYS_RECVMMSG */, args);
-  if (rc == -1)
-    if (errno == EINVAL)
-      errno = ENOSYS;
-
-  return rc;
-#elif defined(__NR_recvmmsg)
-  return syscall(__NR_recvmmsg, fd, mmsg, vlen, /* flags */ 0, /* timeout */ 0);
-#else
-  return errno = ENOSYS, -1;
-#endif
-}
-
-
-ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) {
-#if !defined(__NR_preadv) || defined(__ANDROID_API__) && __ANDROID_API__ < 24
-  return errno = ENOSYS, -1;
-#else
-  return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
-#endif
-}
-
-
-ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) {
-#if !defined(__NR_pwritev) || defined(__ANDROID_API__) && __ANDROID_API__ < 24
-  return errno = ENOSYS, -1;
-#else
-  return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
-#endif
-}
-
-
-int uv__dup3(int oldfd, int newfd, int flags) {
-#if !defined(__NR_dup3) || defined(__ANDROID_API__) && __ANDROID_API__ < 21
-  return errno = ENOSYS, -1;
-#else
-  return syscall(__NR_dup3, oldfd, newfd, flags);
-#endif
-}
-
-
-ssize_t
-uv__fs_copy_file_range(int fd_in,
-                       off_t* off_in,
-                       int fd_out,
-                       off_t* off_out,
-                       size_t len,
-                       unsigned int flags)
-{
-#ifdef __NR_copy_file_range
-  return syscall(__NR_copy_file_range,
-                 fd_in,
-                 off_in,
-                 fd_out,
-                 off_out,
-                 len,
-                 flags);
-#else
-  return errno = ENOSYS, -1;
-#endif
-}
-
-
-int uv__statx(int dirfd,
-              const char* path,
-              int flags,
-              unsigned int mask,
-              struct uv__statx* statxbuf) {
-#if !defined(__NR_statx) || defined(__ANDROID_API__) && __ANDROID_API__ < 30
-  return errno = ENOSYS, -1;
-#else
-  return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf);
-#endif
-}
-
-
-ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) {
-#if !defined(__NR_getrandom) || defined(__ANDROID_API__) && __ANDROID_API__ < 28
-  return errno = ENOSYS, -1;
-#else
-  return syscall(__NR_getrandom, buf, buflen, flags);
-#endif
-}
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.h
deleted file mode 100644
index b4d9082..0000000
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux-syscalls.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
- *
- * 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.
- */
-
-#ifndef UV_LINUX_SYSCALL_H_
-#define UV_LINUX_SYSCALL_H_
-
-#include <stdint.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-
-struct uv__statx_timestamp {
-  int64_t tv_sec;
-  uint32_t tv_nsec;
-  int32_t unused0;
-};
-
-struct uv__statx {
-  uint32_t stx_mask;
-  uint32_t stx_blksize;
-  uint64_t stx_attributes;
-  uint32_t stx_nlink;
-  uint32_t stx_uid;
-  uint32_t stx_gid;
-  uint16_t stx_mode;
-  uint16_t unused0;
-  uint64_t stx_ino;
-  uint64_t stx_size;
-  uint64_t stx_blocks;
-  uint64_t stx_attributes_mask;
-  struct uv__statx_timestamp stx_atime;
-  struct uv__statx_timestamp stx_btime;
-  struct uv__statx_timestamp stx_ctime;
-  struct uv__statx_timestamp stx_mtime;
-  uint32_t stx_rdev_major;
-  uint32_t stx_rdev_minor;
-  uint32_t stx_dev_major;
-  uint32_t stx_dev_minor;
-  uint64_t unused1[14];
-};
-
-ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
-ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
-int uv__dup3(int oldfd, int newfd, int flags);
-ssize_t
-uv__fs_copy_file_range(int fd_in,
-                       off_t* off_in,
-                       int fd_out,
-                       off_t* off_out,
-                       size_t len,
-                       unsigned int flags);
-int uv__statx(int dirfd,
-              const char* path,
-              int flags,
-              unsigned int mask,
-              struct uv__statx* statxbuf);
-ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags);
-
-#endif /* UV_LINUX_SYSCALL_H_ */
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux.cpp
new file mode 100644
index 0000000..d365b62
--- /dev/null
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux.cpp
@@ -0,0 +1,2526 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * 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.
+ */
+
+/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their
+ * EPOLL* counterparts.  We use the POLL* variants in this file because that
+ * is what libuv uses elsewhere.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <inttypes.h>
+#include <stddef.h>  /* offsetof */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <fcntl.h>
+#include <net/if.h>
+#include <sys/epoll.h>
+#include <sys/inotify.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysinfo.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef __NR_io_uring_setup
+# define __NR_io_uring_setup 425
+#endif
+
+#ifndef __NR_io_uring_enter
+# define __NR_io_uring_enter 426
+#endif
+
+#ifndef __NR_io_uring_register
+# define __NR_io_uring_register 427
+#endif
+
+#ifndef __NR_copy_file_range
+# if defined(__x86_64__)
+#  define __NR_copy_file_range 326
+# elif defined(__i386__)
+#  define __NR_copy_file_range 377
+# elif defined(__s390__)
+#  define __NR_copy_file_range 375
+# elif defined(__arm__)
+#  define __NR_copy_file_range 391
+# elif defined(__aarch64__)
+#  define __NR_copy_file_range 285
+# elif defined(__powerpc__)
+#  define __NR_copy_file_range 379
+# elif defined(__arc__)
+#  define __NR_copy_file_range 285
+# endif
+#endif /* __NR_copy_file_range */
+
+#ifndef __NR_statx
+# if defined(__x86_64__)
+#  define __NR_statx 332
+# elif defined(__i386__)
+#  define __NR_statx 383
+# elif defined(__aarch64__)
+#  define __NR_statx 397
+# elif defined(__arm__)
+#  define __NR_statx 397
+# elif defined(__ppc__)
+#  define __NR_statx 383
+# elif defined(__s390__)
+#  define __NR_statx 379
+# endif
+#endif /* __NR_statx */
+
+#ifndef __NR_getrandom
+# if defined(__x86_64__)
+#  define __NR_getrandom 318
+# elif defined(__i386__)
+#  define __NR_getrandom 355
+# elif defined(__aarch64__)
+#  define __NR_getrandom 384
+# elif defined(__arm__)
+#  define __NR_getrandom 384
+# elif defined(__ppc__)
+#  define __NR_getrandom 359
+# elif defined(__s390__)
+#  define __NR_getrandom 349
+# endif
+#endif /* __NR_getrandom */
+
+#define HAVE_IFADDRS_H 1
+
+# if defined(__ANDROID_API__) && __ANDROID_API__ < 24
+# undef HAVE_IFADDRS_H
+#endif
+
+#ifdef __UCLIBC__
+# if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32
+#  undef HAVE_IFADDRS_H
+# endif
+#endif
+
+#ifdef HAVE_IFADDRS_H
+# include <ifaddrs.h>
+# include <sys/socket.h>
+# include <net/ethernet.h>
+# include <netpacket/packet.h>
+#endif /* HAVE_IFADDRS_H */
+
+#include <atomic>
+
+enum {
+  UV__IORING_SETUP_SQPOLL = 2u,
+};
+
+enum {
+  UV__IORING_FEAT_SINGLE_MMAP = 1u,
+  UV__IORING_FEAT_NODROP = 2u,
+  UV__IORING_FEAT_RSRC_TAGS = 1024u,  /* linux v5.13 */
+};
+
+enum {
+  UV__IORING_OP_READV = 1,
+  UV__IORING_OP_WRITEV = 2,
+  UV__IORING_OP_FSYNC = 3,
+  UV__IORING_OP_OPENAT = 18,
+  UV__IORING_OP_CLOSE = 19,
+  UV__IORING_OP_STATX = 21,
+  UV__IORING_OP_EPOLL_CTL = 29,
+  UV__IORING_OP_RENAMEAT = 35,
+  UV__IORING_OP_UNLINKAT = 36,
+  UV__IORING_OP_MKDIRAT = 37,
+  UV__IORING_OP_SYMLINKAT = 38,
+  UV__IORING_OP_LINKAT = 39,
+};
+
+enum {
+  UV__IORING_ENTER_GETEVENTS = 1u,
+  UV__IORING_ENTER_SQ_WAKEUP = 2u,
+};
+
+enum {
+  UV__IORING_SQ_NEED_WAKEUP = 1u,
+  UV__IORING_SQ_CQ_OVERFLOW = 2u,
+};
+
+enum {
+  UV__MKDIRAT_SYMLINKAT_LINKAT = 1u,
+};
+
+struct uv__io_cqring_offsets {
+  uint32_t head;
+  uint32_t tail;
+  uint32_t ring_mask;
+  uint32_t ring_entries;
+  uint32_t overflow;
+  uint32_t cqes;
+  uint64_t reserved0;
+  uint64_t reserved1;
+};
+
+STATIC_ASSERT(40 == sizeof(struct uv__io_cqring_offsets));
+
+struct uv__io_sqring_offsets {
+  uint32_t head;
+  uint32_t tail;
+  uint32_t ring_mask;
+  uint32_t ring_entries;
+  uint32_t flags;
+  uint32_t dropped;
+  uint32_t array;
+  uint32_t reserved0;
+  uint64_t reserved1;
+};
+
+STATIC_ASSERT(40 == sizeof(struct uv__io_sqring_offsets));
+
+struct uv__io_uring_cqe {
+  uint64_t user_data;
+  int32_t res;
+  uint32_t flags;
+};
+
+STATIC_ASSERT(16 == sizeof(struct uv__io_uring_cqe));
+
+struct uv__io_uring_sqe {
+  uint8_t opcode;
+  uint8_t flags;
+  uint16_t ioprio;
+  int32_t fd;
+  union {
+    uint64_t off;
+    uint64_t addr2;
+  };
+  union {
+    uint64_t addr;
+  };
+  uint32_t len;
+  union {
+    uint32_t rw_flags;
+    uint32_t fsync_flags;
+    uint32_t open_flags;
+    uint32_t statx_flags;
+  };
+  uint64_t user_data;
+  union {
+    uint16_t buf_index;
+    uint64_t pad[3];
+  };
+};
+
+STATIC_ASSERT(64 == sizeof(struct uv__io_uring_sqe));
+STATIC_ASSERT(0 == offsetof(struct uv__io_uring_sqe, opcode));
+STATIC_ASSERT(1 == offsetof(struct uv__io_uring_sqe, flags));
+STATIC_ASSERT(2 == offsetof(struct uv__io_uring_sqe, ioprio));
+STATIC_ASSERT(4 == offsetof(struct uv__io_uring_sqe, fd));
+STATIC_ASSERT(8 == offsetof(struct uv__io_uring_sqe, off));
+STATIC_ASSERT(16 == offsetof(struct uv__io_uring_sqe, addr));
+STATIC_ASSERT(24 == offsetof(struct uv__io_uring_sqe, len));
+STATIC_ASSERT(28 == offsetof(struct uv__io_uring_sqe, rw_flags));
+STATIC_ASSERT(32 == offsetof(struct uv__io_uring_sqe, user_data));
+STATIC_ASSERT(40 == offsetof(struct uv__io_uring_sqe, buf_index));
+
+struct uv__io_uring_params {
+  uint32_t sq_entries;
+  uint32_t cq_entries;
+  uint32_t flags;
+  uint32_t sq_thread_cpu;
+  uint32_t sq_thread_idle;
+  uint32_t features;
+  uint32_t reserved[4];
+  struct uv__io_sqring_offsets sq_off;  /* 40 bytes */
+  struct uv__io_cqring_offsets cq_off;  /* 40 bytes */
+};
+
+STATIC_ASSERT(40 + 40 + 40 == sizeof(struct uv__io_uring_params));
+STATIC_ASSERT(40 == offsetof(struct uv__io_uring_params, sq_off));
+STATIC_ASSERT(80 == offsetof(struct uv__io_uring_params, cq_off));
+
+STATIC_ASSERT(EPOLL_CTL_ADD < 4);
+STATIC_ASSERT(EPOLL_CTL_DEL < 4);
+STATIC_ASSERT(EPOLL_CTL_MOD < 4);
+
+struct watcher_list {
+  RB_ENTRY(watcher_list) entry;
+  struct uv__queue watchers;
+  int iterating;
+  char* path;
+  int wd;
+};
+
+struct watcher_root {
+  struct watcher_list* rbh_root;
+};
+
+static int uv__inotify_fork(uv_loop_t* loop, struct watcher_list* root);
+static void uv__inotify_read(uv_loop_t* loop,
+                             uv__io_t* w,
+                             unsigned int revents);
+static int compare_watchers(const struct watcher_list* a,
+                            const struct watcher_list* b);
+static void maybe_free_watcher_list(struct watcher_list* w,
+                                    uv_loop_t* loop);
+
+static void uv__epoll_ctl_flush(int epollfd,
+                                struct uv__iou* ctl,
+                                struct epoll_event (*events)[256]);
+
+static void uv__epoll_ctl_prep(int epollfd,
+                               struct uv__iou* ctl,
+                               struct epoll_event (*events)[256],
+                               int op,
+                               int fd,
+                               struct epoll_event* e);
+
+RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers)
+
+
+static struct watcher_root* uv__inotify_watchers(uv_loop_t* loop) {
+  /* This cast works because watcher_root is a struct with a pointer as its
+   * sole member. Such type punning is unsafe in the presence of strict
+   * pointer aliasing (and is just plain nasty) but that is why libuv
+   * is compiled with -fno-strict-aliasing.
+   */
+  return (struct watcher_root*) &loop->inotify_watchers;
+}
+
+
+unsigned uv__kernel_version(void) {
+  static std::atomic<unsigned int> cached_version;
+  struct utsname u;
+  unsigned version;
+  unsigned major;
+  unsigned minor;
+  unsigned patch;
+
+  version = std::atomic_load_explicit(&cached_version, std::memory_order_relaxed);
+  if (version != 0)
+    return version;
+
+  if (-1 == uname(&u))
+    return 0;
+
+  if (3 != sscanf(u.release, "%u.%u.%u", &major, &minor, &patch))
+    return 0;
+
+  version = major * 65536 + minor * 256 + patch;
+  std::atomic_store_explicit(&cached_version, version, std::memory_order_relaxed);
+
+  return version;
+}
+
+
+ssize_t
+uv__fs_copy_file_range(int fd_in,
+                       off_t* off_in,
+                       int fd_out,
+                       off_t* off_out,
+                       size_t len,
+                       unsigned int flags)
+{
+#ifdef __NR_copy_file_range
+  return syscall(__NR_copy_file_range,
+                 fd_in,
+                 off_in,
+                 fd_out,
+                 off_out,
+                 len,
+                 flags);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__statx(int dirfd,
+              const char* path,
+              int flags,
+              unsigned int mask,
+              struct uv__statx* statxbuf) {
+#if !defined(__NR_statx) || defined(__ANDROID_API__) && __ANDROID_API__ < 30
+  return errno = ENOSYS, -1;
+#else
+  int rc;
+
+  rc = syscall(__NR_statx, dirfd, path, flags, mask, statxbuf);
+  if (rc >= 0)
+    uv__msan_unpoison(statxbuf, sizeof(*statxbuf));
+
+  return rc;
+#endif
+}
+
+
+ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) {
+#if !defined(__NR_getrandom) || defined(__ANDROID_API__) && __ANDROID_API__ < 28
+  return errno = ENOSYS, -1;
+#else
+  ssize_t rc;
+
+  rc = syscall(__NR_getrandom, buf, buflen, flags);
+  if (rc >= 0)
+    uv__msan_unpoison(buf, buflen);
+
+  return rc;
+#endif
+}
+
+
+int uv__io_uring_setup(int entries, struct uv__io_uring_params* params) {
+  return syscall(__NR_io_uring_setup, entries, params);
+}
+
+
+int uv__io_uring_enter(int fd,
+                       unsigned to_submit,
+                       unsigned min_complete,
+                       unsigned flags) {
+  /* io_uring_enter used to take a sigset_t but it's unused
+   * in newer kernels unless IORING_ENTER_EXT_ARG is set,
+   * in which case it takes a struct io_uring_getevents_arg.
+   */
+  return syscall(__NR_io_uring_enter,
+                 fd,
+                 to_submit,
+                 min_complete,
+                 flags,
+                 NULL,
+                 0L);
+}
+
+
+int uv__io_uring_register(int fd, unsigned opcode, void* arg, unsigned nargs) {
+  return syscall(__NR_io_uring_register, fd, opcode, arg, nargs);
+}
+
+
+static int uv__use_io_uring(void) {
+#if defined(__ANDROID_API__)
+  return 0;  /* Possibly available but blocked by seccomp. */
+#else
+  /* Ternary: unknown=0, yes=1, no=-1 */
+  static std::atomic<int> use_io_uring;
+  char* val;
+  int use;
+
+  use = std::atomic_load_explicit(&use_io_uring, std::memory_order_relaxed);
+
+  if (use == 0) {
+    val = getenv("UV_USE_IO_URING");
+    use = val == NULL || atoi(val) ? 1 : -1;
+    std::atomic_store_explicit(&use_io_uring, use, std::memory_order_relaxed);
+  }
+
+  return use > 0;
+#endif
+}
+
+
+static void uv__iou_init(int epollfd,
+                         struct uv__iou* iou,
+                         uint32_t entries,
+                         uint32_t flags) {
+  struct uv__io_uring_params params;
+  struct epoll_event e;
+  size_t cqlen;
+  size_t sqlen;
+  size_t maxlen;
+  size_t sqelen;
+  uint32_t i;
+  char* sq;
+  char* sqe;
+  int ringfd;
+
+  sq = (char*)MAP_FAILED;
+  sqe = (char*)MAP_FAILED;
+
+  if (!uv__use_io_uring())
+    return;
+
+  /* SQPOLL required CAP_SYS_NICE until linux v5.12 relaxed that requirement.
+   * Mostly academic because we check for a v5.13 kernel afterwards anyway.
+   */
+  memset(&params, 0, sizeof(params));
+  params.flags = flags;
+
+  if (flags & UV__IORING_SETUP_SQPOLL)
+    params.sq_thread_idle = 10;  /* milliseconds */
+
+  /* Kernel returns a file descriptor with O_CLOEXEC flag set. */
+  ringfd = uv__io_uring_setup(entries, &params);
+  if (ringfd == -1)
+    return;
+
+  /* IORING_FEAT_RSRC_TAGS is used to detect linux v5.13 but what we're
+   * actually detecting is whether IORING_OP_STATX works with SQPOLL.
+   */
+  if (!(params.features & UV__IORING_FEAT_RSRC_TAGS))
+    goto fail;
+
+  /* Implied by IORING_FEAT_RSRC_TAGS but checked explicitly anyway. */
+  if (!(params.features & UV__IORING_FEAT_SINGLE_MMAP))
+    goto fail;
+
+  /* Implied by IORING_FEAT_RSRC_TAGS but checked explicitly anyway. */
+  if (!(params.features & UV__IORING_FEAT_NODROP))
+    goto fail;
+
+  sqlen = params.sq_off.array + params.sq_entries * sizeof(uint32_t);
+  cqlen =
+      params.cq_off.cqes + params.cq_entries * sizeof(struct uv__io_uring_cqe);
+  maxlen = sqlen < cqlen ? cqlen : sqlen;
+  sqelen = params.sq_entries * sizeof(struct uv__io_uring_sqe);
+
+  sq = (char*)mmap(0,
+            maxlen,
+            PROT_READ | PROT_WRITE,
+            MAP_SHARED | MAP_POPULATE,
+            ringfd,
+            0);  /* IORING_OFF_SQ_RING */
+
+  sqe = (char*)mmap(0,
+             sqelen,
+             PROT_READ | PROT_WRITE,
+             MAP_SHARED | MAP_POPULATE,
+             ringfd,
+             0x10000000ull);  /* IORING_OFF_SQES */
+
+  if (sq == MAP_FAILED || sqe == MAP_FAILED)
+    goto fail;
+
+  if (flags & UV__IORING_SETUP_SQPOLL) {
+    /* Only interested in completion events. To get notified when
+     * the kernel pulls items from the submission ring, add POLLOUT.
+     */
+    memset(&e, 0, sizeof(e));
+    e.events = POLLIN;
+    e.data.fd = ringfd;
+
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ringfd, &e))
+      goto fail;
+  }
+
+  iou->sqhead = (uint32_t*) (sq + params.sq_off.head);
+  iou->sqtail = (uint32_t*) (sq + params.sq_off.tail);
+  iou->sqmask = *(uint32_t*) (sq + params.sq_off.ring_mask);
+  iou->sqarray = (uint32_t*) (sq + params.sq_off.array);
+  iou->sqflags = (uint32_t*) (sq + params.sq_off.flags);
+  iou->cqhead = (uint32_t*) (sq + params.cq_off.head);
+  iou->cqtail = (uint32_t*) (sq + params.cq_off.tail);
+  iou->cqmask = *(uint32_t*) (sq + params.cq_off.ring_mask);
+  iou->sq = sq;
+  iou->cqe = sq + params.cq_off.cqes;
+  iou->sqe = sqe;
+  iou->sqlen = sqlen;
+  iou->cqlen = cqlen;
+  iou->maxlen = maxlen;
+  iou->sqelen = sqelen;
+  iou->ringfd = ringfd;
+  iou->in_flight = 0;
+  iou->flags = 0;
+
+  if (uv__kernel_version() >= /* 5.15.0 */ 0x050F00)
+    iou->flags |= UV__MKDIRAT_SYMLINKAT_LINKAT;
+
+  for (i = 0; i <= iou->sqmask; i++)
+    iou->sqarray[i] = i;  /* Slot -> sqe identity mapping. */
+
+  return;
+
+fail:
+  if (sq != MAP_FAILED)
+    munmap(sq, maxlen);
+
+  if (sqe != MAP_FAILED)
+    munmap(sqe, sqelen);
+
+  uv__close(ringfd);
+}
+
+
+static void uv__iou_delete(struct uv__iou* iou) {
+  if (iou->ringfd != -1) {
+    munmap(iou->sq, iou->maxlen);
+    munmap(iou->sqe, iou->sqelen);
+    uv__close(iou->ringfd);
+    iou->ringfd = -1;
+  }
+}
+
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  uv__loop_internal_fields_t* lfields;
+
+  lfields = uv__get_internal_fields(loop);
+  lfields->ctl.ringfd = -1;
+  lfields->iou.ringfd = -1;
+
+  loop->inotify_watchers = NULL;
+  loop->inotify_fd = -1;
+  loop->backend_fd = epoll_create1(O_CLOEXEC);
+
+  if (loop->backend_fd == -1)
+    return UV__ERR(errno);
+
+  uv__iou_init(loop->backend_fd, &lfields->iou, 64, UV__IORING_SETUP_SQPOLL);
+  uv__iou_init(loop->backend_fd, &lfields->ctl, 256, 0);
+
+  return 0;
+}
+
+
+int uv__io_fork(uv_loop_t* loop) {
+  int err;
+  struct watcher_list* root;
+
+  root = uv__inotify_watchers(loop)->rbh_root;
+
+  uv__close(loop->backend_fd);
+  loop->backend_fd = -1;
+
+  /* TODO(bnoordhuis) Loses items from the submission and completion rings. */
+  uv__platform_loop_delete(loop);
+
+  err = uv__platform_loop_init(loop);
+  if (err)
+    return err;
+
+  return uv__inotify_fork(loop, root);
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+  uv__loop_internal_fields_t* lfields;
+
+  lfields = uv__get_internal_fields(loop);
+  uv__iou_delete(&lfields->ctl);
+  uv__iou_delete(&lfields->iou);
+
+  if (loop->inotify_fd != -1) {
+    uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN);
+    uv__close(loop->inotify_fd);
+    loop->inotify_fd = -1;
+  }
+}
+
+
+struct uv__invalidate {
+  struct epoll_event (*prep)[256];
+  struct epoll_event* events;
+  int nfds;
+};
+
+
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+  uv__loop_internal_fields_t* lfields;
+  struct uv__invalidate* inv;
+  struct epoll_event dummy;
+  int i;
+
+  lfields = uv__get_internal_fields(loop);
+  inv = (uv__invalidate*)lfields->inv;
+
+  /* Invalidate events with same file descriptor */
+  if (inv != NULL)
+    for (i = 0; i < inv->nfds; i++)
+      if (inv->events[i].data.fd == fd)
+        inv->events[i].data.fd = -1;
+
+  /* Remove the file descriptor from the epoll.
+   * This avoids a problem where the same file description remains open
+   * in another process, causing repeated junk epoll events.
+   *
+   * We pass in a dummy epoll_event, to work around a bug in old kernels.
+   *
+   * Work around a bug in kernels 3.10 to 3.19 where passing a struct that
+   * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings.
+   */
+  memset(&dummy, 0, sizeof(dummy));
+
+  if (inv == NULL) {
+    epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy);
+  } else {
+    uv__epoll_ctl_prep(loop->backend_fd,
+                       &lfields->ctl,
+                       inv->prep,
+                       EPOLL_CTL_DEL,
+                       fd,
+                       &dummy);
+  }
+}
+
+
+int uv__io_check_fd(uv_loop_t* loop, int fd) {
+  struct epoll_event e;
+  int rc;
+
+  memset(&e, 0, sizeof(e));
+  e.events = POLLIN;
+  e.data.fd = -1;
+
+  rc = 0;
+  if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e))
+    if (errno != EEXIST)
+      rc = UV__ERR(errno);
+
+  if (rc == 0)
+    if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e))
+      abort();
+
+  return rc;
+}
+
+
+/* Caller must initialize SQE and call uv__iou_submit(). */
+static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
+                                                uv_loop_t* loop,
+                                                uv_fs_t* req) {
+  struct uv__io_uring_sqe* sqe;
+  uint32_t head;
+  uint32_t tail;
+  uint32_t mask;
+  uint32_t slot;
+
+  if (iou->ringfd == -1)
+    return NULL;
+
+  head = std::atomic_load_explicit((std::atomic<uint32_t>*) iou->sqhead,
+                              std::memory_order_acquire);
+  tail = *iou->sqtail;
+  mask = iou->sqmask;
+
+  if ((head & mask) == ((tail + 1) & mask))
+    return NULL;  /* No room in ring buffer. TODO(bnoordhuis) maybe flush it? */
+
+  slot = tail & mask;
+  sqe = (uv__io_uring_sqe*)iou->sqe;
+  sqe = &sqe[slot];
+  memset(sqe, 0, sizeof(*sqe));
+  sqe->user_data = (uintptr_t) req;
+
+  /* Pacify uv_cancel(). */
+  req->work_req.loop = loop;
+  req->work_req.work = NULL;
+  req->work_req.done = NULL;
+  uv__queue_init(&req->work_req.wq);
+
+  uv__req_register(loop, req);
+  iou->in_flight++;
+
+  return sqe;
+}
+
+
+static void uv__iou_submit(struct uv__iou* iou) {
+  uint32_t flags;
+
+  std::atomic_store_explicit((std::atomic<uint32_t>*) iou->sqtail,
+                        *iou->sqtail + 1,
+                        std::memory_order_release);
+
+  flags = std::atomic_load_explicit((std::atomic<uint32_t>*) iou->sqflags,
+                               std::memory_order_acquire);
+
+  if (flags & UV__IORING_SQ_NEED_WAKEUP)
+    if (uv__io_uring_enter(iou->ringfd, 0, 0, UV__IORING_ENTER_SQ_WAKEUP))
+      if (errno != EOWNERDEAD)  /* Kernel bug. Harmless, ignore. */
+        perror("libuv: io_uring_enter(wakeup)");  /* Can't happen. */
+}
+
+
+int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req) {
+  struct uv__io_uring_sqe* sqe;
+  struct uv__iou* iou;
+
+  /* Work around a poorly understood bug in older kernels where closing a file
+   * descriptor pointing to /foo/bar results in ETXTBSY errors when trying to
+   * execve("/foo/bar") later on. The bug seems to have been fixed somewhere
+   * between 5.15.85 and 5.15.90. I couldn't pinpoint the responsible commit
+   * but good candidates are the several data race fixes. Interestingly, it
+   * seems to manifest only when running under Docker so the possibility of
+   * a Docker bug can't be completely ruled out either. Yay, computers.
+   */
+  if (uv__kernel_version() < /* 5.15.90 */ 0x050F5A)
+    return 0;
+
+  iou = &uv__get_internal_fields(loop)->iou;
+
+  sqe = uv__iou_get_sqe(iou, loop, req);
+  if (sqe == NULL)
+    return 0;
+
+  sqe->fd = req->file;
+  sqe->opcode = UV__IORING_OP_CLOSE;
+
+  uv__iou_submit(iou);
+
+  return 1;
+}
+
+
+int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop,
+                                  uv_fs_t* req,
+                                  uint32_t fsync_flags) {
+  struct uv__io_uring_sqe* sqe;
+  struct uv__iou* iou;
+
+  iou = &uv__get_internal_fields(loop)->iou;
+
+  sqe = uv__iou_get_sqe(iou, loop, req);
+  if (sqe == NULL)
+    return 0;
+
+  /* Little known fact: setting seq->off and seq->len turns
+   * it into an asynchronous sync_file_range() operation.
+   */
+  sqe->fd = req->file;
+  sqe->fsync_flags = fsync_flags;
+  sqe->opcode = UV__IORING_OP_FSYNC;
+
+  uv__iou_submit(iou);
+
+  return 1;
+}
+
+
+int uv__iou_fs_link(uv_loop_t* loop, uv_fs_t* req) {
+  struct uv__io_uring_sqe* sqe;
+  struct uv__iou* iou;
+
+  iou = &uv__get_internal_fields(loop)->iou;
+
+  if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
+    return 0;
+
+  sqe = uv__iou_get_sqe(iou, loop, req);
+  if (sqe == NULL)
+    return 0;
+
+  sqe->addr = (uintptr_t) req->path;
+  sqe->fd = AT_FDCWD;
+  sqe->addr2 = (uintptr_t) req->new_path;
+  sqe->len = AT_FDCWD;
+  sqe->opcode = UV__IORING_OP_LINKAT;
+
+  uv__iou_submit(iou);
+
+  return 1;
+}
+
+
+int uv__iou_fs_mkdir(uv_loop_t* loop, uv_fs_t* req) {
+  struct uv__io_uring_sqe* sqe;
+  struct uv__iou* iou;
+
+  iou = &uv__get_internal_fields(loop)->iou;
+
+  if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
+    return 0;
+
+  sqe = uv__iou_get_sqe(iou, loop, req);
+  if (sqe == NULL)
+    return 0;
+
+  sqe->addr = (uintptr_t) req->path;
+  sqe->fd = AT_FDCWD;
+  sqe->len = req->mode;
+  sqe->opcode = UV__IORING_OP_MKDIRAT;
+
+  uv__iou_submit(iou);
+
+  return 1;
+}
+
+
+int uv__iou_fs_open(uv_loop_t* loop, uv_fs_t* req) {
+  struct uv__io_uring_sqe* sqe;
+  struct uv__iou* iou;
+
+  iou = &uv__get_internal_fields(loop)->iou;
+
+  sqe = uv__iou_get_sqe(iou, loop, req);
+  if (sqe == NULL)
+    return 0;
+
+  sqe->addr = (uintptr_t) req->path;
+  sqe->fd = AT_FDCWD;
+  sqe->len = req->mode;
+  sqe->opcode = UV__IORING_OP_OPENAT;
+  sqe->open_flags = req->flags | O_CLOEXEC;
+
+  uv__iou_submit(iou);
+
+  return 1;
+}
+
+
+int uv__iou_fs_rename(uv_loop_t* loop, uv_fs_t* req) {
+  struct uv__io_uring_sqe* sqe;
+  struct uv__iou* iou;
+
+  iou = &uv__get_internal_fields(loop)->iou;
+
+  sqe = uv__iou_get_sqe(iou, loop, req);
+  if (sqe == NULL)
+    return 0;
+
+  sqe->addr = (uintptr_t) req->path;
+  sqe->fd = AT_FDCWD;
+  sqe->addr2 = (uintptr_t) req->new_path;
+  sqe->len = AT_FDCWD;
+  sqe->opcode = UV__IORING_OP_RENAMEAT;
+
+  uv__iou_submit(iou);
+
+  return 1;
+}
+
+
+int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req) {
+  struct uv__io_uring_sqe* sqe;
+  struct uv__iou* iou;
+
+  iou = &uv__get_internal_fields(loop)->iou;
+
+  if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
+    return 0;
+
+  sqe = uv__iou_get_sqe(iou, loop, req);
+  if (sqe == NULL)
+    return 0;
+
+  sqe->addr = (uintptr_t) req->path;
+  sqe->fd = AT_FDCWD;
+  sqe->addr2 = (uintptr_t) req->new_path;
+  sqe->opcode = UV__IORING_OP_SYMLINKAT;
+
+  uv__iou_submit(iou);
+
+  return 1;
+}
+
+
+int uv__iou_fs_unlink(uv_loop_t* loop, uv_fs_t* req) {
+  struct uv__io_uring_sqe* sqe;
+  struct uv__iou* iou;
+
+  iou = &uv__get_internal_fields(loop)->iou;
+
+  sqe = uv__iou_get_sqe(iou, loop, req);
+  if (sqe == NULL)
+    return 0;
+
+  sqe->addr = (uintptr_t) req->path;
+  sqe->fd = AT_FDCWD;
+  sqe->opcode = UV__IORING_OP_UNLINKAT;
+
+  uv__iou_submit(iou);
+
+  return 1;
+}
+
+
+int uv__iou_fs_read_or_write(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             int is_read) {
+  struct uv__io_uring_sqe* sqe;
+  struct uv__iou* iou;
+
+  /* If iovcnt is greater than IOV_MAX, cap it to IOV_MAX on reads and fallback
+   * to the threadpool on writes */
+  if (req->nbufs > IOV_MAX) {
+    if (is_read)
+      req->nbufs = IOV_MAX;
+    else
+      return 0;
+  }
+
+  iou = &uv__get_internal_fields(loop)->iou;
+
+  sqe = uv__iou_get_sqe(iou, loop, req);
+  if (sqe == NULL)
+    return 0;
+
+  sqe->addr = (uintptr_t) req->bufs;
+  sqe->fd = req->file;
+  sqe->len = req->nbufs;
+  sqe->off = req->off < 0 ? -1 : req->off;
+  sqe->opcode = is_read ? UV__IORING_OP_READV : UV__IORING_OP_WRITEV;
+
+  uv__iou_submit(iou);
+
+  return 1;
+}
+
+
+int uv__iou_fs_statx(uv_loop_t* loop,
+                     uv_fs_t* req,
+                     int is_fstat,
+                     int is_lstat) {
+  struct uv__io_uring_sqe* sqe;
+  struct uv__statx* statxbuf;
+  struct uv__iou* iou;
+
+  statxbuf = (struct uv__statx*)uv__malloc(sizeof(*statxbuf));
+  if (statxbuf == NULL)
+    return 0;
+
+  iou = &uv__get_internal_fields(loop)->iou;
+
+  sqe = uv__iou_get_sqe(iou, loop, req);
+  if (sqe == NULL) {
+    uv__free(statxbuf);
+    return 0;
+  }
+
+  req->ptr = statxbuf;
+
+  sqe->addr = (uintptr_t) req->path;
+  sqe->addr2 = (uintptr_t) statxbuf;
+  sqe->fd = AT_FDCWD;
+  sqe->len = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */
+  sqe->opcode = UV__IORING_OP_STATX;
+
+  if (is_fstat) {
+    sqe->addr = (uintptr_t) "";
+    sqe->fd = req->file;
+    sqe->statx_flags |= 0x1000; /* AT_EMPTY_PATH */
+  }
+
+  if (is_lstat)
+    sqe->statx_flags |= AT_SYMLINK_NOFOLLOW;
+
+  uv__iou_submit(iou);
+
+  return 1;
+}
+
+
+void uv__statx_to_stat(const struct uv__statx* statxbuf, uv_stat_t* buf) {
+  buf->st_dev = makedev(statxbuf->stx_dev_major, statxbuf->stx_dev_minor);
+  buf->st_mode = statxbuf->stx_mode;
+  buf->st_nlink = statxbuf->stx_nlink;
+  buf->st_uid = statxbuf->stx_uid;
+  buf->st_gid = statxbuf->stx_gid;
+  buf->st_rdev = makedev(statxbuf->stx_rdev_major, statxbuf->stx_rdev_minor);
+  buf->st_ino = statxbuf->stx_ino;
+  buf->st_size = statxbuf->stx_size;
+  buf->st_blksize = statxbuf->stx_blksize;
+  buf->st_blocks = statxbuf->stx_blocks;
+  buf->st_atim.tv_sec = statxbuf->stx_atime.tv_sec;
+  buf->st_atim.tv_nsec = statxbuf->stx_atime.tv_nsec;
+  buf->st_mtim.tv_sec = statxbuf->stx_mtime.tv_sec;
+  buf->st_mtim.tv_nsec = statxbuf->stx_mtime.tv_nsec;
+  buf->st_ctim.tv_sec = statxbuf->stx_ctime.tv_sec;
+  buf->st_ctim.tv_nsec = statxbuf->stx_ctime.tv_nsec;
+  buf->st_birthtim.tv_sec = statxbuf->stx_btime.tv_sec;
+  buf->st_birthtim.tv_nsec = statxbuf->stx_btime.tv_nsec;
+  buf->st_flags = 0;
+  buf->st_gen = 0;
+}
+
+
+static void uv__iou_fs_statx_post(uv_fs_t* req) {
+  struct uv__statx* statxbuf;
+  uv_stat_t* buf;
+
+  buf = &req->statbuf;
+  statxbuf = (struct uv__statx*)req->ptr;
+  req->ptr = NULL;
+
+  if (req->result == 0) {
+    uv__msan_unpoison(statxbuf, sizeof(*statxbuf));
+    uv__statx_to_stat(statxbuf, buf);
+    req->ptr = buf;
+  }
+
+  uv__free(statxbuf);
+}
+
+
+static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) {
+  struct uv__io_uring_cqe* cqe;
+  struct uv__io_uring_cqe* e;
+  uv_fs_t* req;
+  uint32_t head;
+  uint32_t tail;
+  uint32_t mask;
+  uint32_t i;
+  uint32_t flags;
+  int nevents;
+  int rc;
+
+  head = *iou->cqhead;
+  tail = std::atomic_load_explicit((std::atomic<uint32_t>*) iou->cqtail,
+                              std::memory_order_acquire);
+  mask = iou->cqmask;
+  cqe = (uv__io_uring_cqe*)iou->cqe;
+  nevents = 0;
+
+  for (i = head; i != tail; i++) {
+    e = &cqe[i & mask];
+
+    req = (uv_fs_t*) (uintptr_t) e->user_data;
+    assert(req->type == UV_FS);
+
+    uv__req_unregister(loop, req);
+    iou->in_flight--;
+
+    /* io_uring stores error codes as negative numbers, same as libuv. */
+    req->result = e->res;
+
+    switch (req->fs_type) {
+      case UV_FS_FSTAT:
+      case UV_FS_LSTAT:
+      case UV_FS_STAT:
+        uv__iou_fs_statx_post(req);
+        break;
+      default:  /* Squelch -Wswitch warnings. */
+        break;
+    }
+
+    uv__metrics_update_idle_time(loop);
+    req->cb(req);
+    nevents++;
+  }
+
+  std::atomic_store_explicit((std::atomic<uint32_t>*) iou->cqhead,
+                        tail,
+                        std::memory_order_release);
+
+  /* Check whether CQE's overflowed, if so enter the kernel to make them
+   * available. Don't grab them immediately but in the next loop iteration to
+   * avoid loop starvation. */
+  flags = std::atomic_load_explicit((std::atomic<uint32_t>*) iou->sqflags,
+                               std::memory_order_acquire);
+
+  if (flags & UV__IORING_SQ_CQ_OVERFLOW) {
+    do
+      rc = uv__io_uring_enter(iou->ringfd, 0, 0, UV__IORING_ENTER_GETEVENTS);
+    while (rc == -1 && errno == EINTR);
+
+    if (rc < 0)
+      perror("libuv: io_uring_enter(getevents)");  /* Can't happen. */
+  }
+
+  uv__metrics_inc_events(loop, nevents);
+  if (uv__get_internal_fields(loop)->current_timeout == 0)
+    uv__metrics_inc_events_waiting(loop, nevents);
+}
+
+
+static void uv__epoll_ctl_prep(int epollfd,
+                               struct uv__iou* ctl,
+                               struct epoll_event (*events)[256],
+                               int op,
+                               int fd,
+                               struct epoll_event* e) {
+  struct uv__io_uring_sqe* sqe;
+  struct epoll_event* pe;
+  uint32_t mask;
+  uint32_t slot;
+
+  if (ctl->ringfd == -1) {
+    if (!epoll_ctl(epollfd, op, fd, e))
+      return;
+
+    if (op == EPOLL_CTL_DEL)
+      return;  /* Ignore errors, may be racing with another thread. */
+
+    if (op != EPOLL_CTL_ADD)
+      abort();
+
+    if (errno != EEXIST)
+      abort();
+
+    /* File descriptor that's been watched before, update event mask. */
+    if (!epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, e))
+      return;
+
+    abort();
+  } else {
+    mask = ctl->sqmask;
+    slot = (*ctl->sqtail)++ & mask;
+
+    pe = &(*events)[slot];
+    *pe = *e;
+
+    sqe = (uv__io_uring_sqe*)ctl->sqe;
+    sqe = &sqe[slot];
+
+    memset(sqe, 0, sizeof(*sqe));
+    sqe->addr = (uintptr_t) pe;
+    sqe->fd = epollfd;
+    sqe->len = op;
+    sqe->off = fd;
+    sqe->opcode = UV__IORING_OP_EPOLL_CTL;
+    sqe->user_data = op | slot << 2 | (int64_t) fd << 32;
+
+    if ((*ctl->sqhead & mask) == (*ctl->sqtail & mask))
+      uv__epoll_ctl_flush(epollfd, ctl, events);
+  }
+}
+
+
+static void uv__epoll_ctl_flush(int epollfd,
+                                struct uv__iou* ctl,
+                                struct epoll_event (*events)[256]) {
+  struct epoll_event oldevents[256];
+  struct uv__io_uring_cqe* cqe;
+  uint32_t oldslot;
+  uint32_t slot;
+  uint32_t n;
+  int fd;
+  int op;
+  int rc;
+
+  STATIC_ASSERT(sizeof(oldevents) == sizeof(*events));
+  assert(ctl->ringfd != -1);
+  assert(*ctl->sqhead != *ctl->sqtail);
+
+  n = *ctl->sqtail - *ctl->sqhead;
+  do
+    rc = uv__io_uring_enter(ctl->ringfd, n, n, UV__IORING_ENTER_GETEVENTS);
+  while (rc == -1 && errno == EINTR);
+
+  if (rc < 0)
+    perror("libuv: io_uring_enter(getevents)");  /* Can't happen. */
+
+  if (rc != (int) n)
+    abort();
+
+  assert(*ctl->sqhead == *ctl->sqtail);
+
+  memcpy(oldevents, *events, sizeof(*events));
+
+  /* Failed submissions are either EPOLL_CTL_DEL commands for file descriptors
+   * that have been closed, or EPOLL_CTL_ADD commands for file descriptors
+   * that we are already watching. Ignore the former and retry the latter
+   * with EPOLL_CTL_MOD.
+   */
+  while (*ctl->cqhead != *ctl->cqtail) {
+    slot = (*ctl->cqhead)++ & ctl->cqmask;
+
+    cqe = (uv__io_uring_cqe*)ctl->cqe;
+    cqe = &cqe[slot];
+
+    if (cqe->res == 0)
+      continue;
+
+    fd = cqe->user_data >> 32;
+    op = 3 & cqe->user_data;
+    oldslot = 255 & (cqe->user_data >> 2);
+
+    if (op == EPOLL_CTL_DEL)
+      continue;
+
+    if (op != EPOLL_CTL_ADD)
+      abort();
+
+    if (cqe->res != -EEXIST)
+      abort();
+
+    uv__epoll_ctl_prep(epollfd,
+                       ctl,
+                       events,
+                       EPOLL_CTL_MOD,
+                       fd,
+                       &oldevents[oldslot]);
+  }
+}
+
+
+void uv__io_poll(uv_loop_t* loop, int timeout) {
+  uv__loop_internal_fields_t* lfields;
+  struct epoll_event events[1024];
+  struct epoll_event prep[256];
+  struct uv__invalidate inv;
+  struct epoll_event* pe;
+  struct epoll_event e;
+  struct uv__iou* ctl;
+  struct uv__iou* iou;
+  int real_timeout;
+  struct uv__queue* q;
+  uv__io_t* w;
+  sigset_t* sigmask;
+  sigset_t sigset;
+  uint64_t base;
+  int have_iou_events;
+  int have_signals;
+  int nevents;
+  int epollfd;
+  int count;
+  int nfds;
+  int fd;
+  int op;
+  int i;
+  int user_timeout;
+  int reset_timeout;
+
+  lfields = uv__get_internal_fields(loop);
+  ctl = &lfields->ctl;
+  iou = &lfields->iou;
+
+  sigmask = NULL;
+  if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
+    sigemptyset(&sigset);
+    sigaddset(&sigset, SIGPROF);
+    sigmask = &sigset;
+  }
+
+  assert(timeout >= -1);
+  base = loop->time;
+  count = 48; /* Benchmarks suggest this gives the best throughput. */
+  real_timeout = timeout;
+
+  if (lfields->flags & UV_METRICS_IDLE_TIME) {
+    reset_timeout = 1;
+    user_timeout = timeout;
+    timeout = 0;
+  } else {
+    reset_timeout = 0;
+    user_timeout = 0;
+  }
+
+  epollfd = loop->backend_fd;
+
+  memset(&e, 0, sizeof(e));
+
+  while (!uv__queue_empty(&loop->watcher_queue)) {
+    q = uv__queue_head(&loop->watcher_queue);
+    w = uv__queue_data(q, uv__io_t, watcher_queue);
+    uv__queue_remove(q);
+    uv__queue_init(q);
+
+    op = EPOLL_CTL_MOD;
+    if (w->events == 0)
+      op = EPOLL_CTL_ADD;
+
+    w->events = w->pevents;
+    e.events = w->pevents;
+    e.data.fd = w->fd;
+
+    uv__epoll_ctl_prep(epollfd, ctl, &prep, op, w->fd, &e);
+  }
+
+  inv.events = events;
+  inv.prep = &prep;
+  inv.nfds = -1;
+
+  for (;;) {
+    if (loop->nfds == 0)
+      if (iou->in_flight == 0)
+        break;
+
+    /* All event mask mutations should be visible to the kernel before
+     * we enter epoll_pwait().
+     */
+    if (ctl->ringfd != -1)
+      while (*ctl->sqhead != *ctl->sqtail)
+        uv__epoll_ctl_flush(epollfd, ctl, &prep);
+
+    /* Only need to set the provider_entry_time if timeout != 0. The function
+     * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
+     */
+    if (timeout != 0)
+      uv__metrics_set_provider_entry_time(loop);
+
+    /* Store the current timeout in a location that's globally accessible so
+     * other locations like uv__work_done() can determine whether the queue
+     * of events in the callback were waiting when poll was called.
+     */
+    lfields->current_timeout = timeout;
+
+    nfds = epoll_pwait(epollfd, events, ARRAY_SIZE(events), timeout, sigmask);
+
+    /* Update loop->time unconditionally. It's tempting to skip the update when
+     * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
+     * operating system didn't reschedule our process while in the syscall.
+     */
+    SAVE_ERRNO(uv__update_time(loop));
+
+    if (nfds == 0) {
+      assert(timeout != -1);
+
+      if (reset_timeout != 0) {
+        timeout = user_timeout;
+        reset_timeout = 0;
+      }
+
+      if (timeout == -1)
+        continue;
+
+      if (timeout == 0)
+        break;
+
+      /* We may have been inside the system call for longer than |timeout|
+       * milliseconds so we need to update the timestamp to avoid drift.
+       */
+      goto update_timeout;
+    }
+
+    if (nfds == -1) {
+      if (errno != EINTR)
+        abort();
+
+      if (reset_timeout != 0) {
+        timeout = user_timeout;
+        reset_timeout = 0;
+      }
+
+      if (timeout == -1)
+        continue;
+
+      if (timeout == 0)
+        break;
+
+      /* Interrupted by a signal. Update timeout and poll again. */
+      goto update_timeout;
+    }
+
+    have_iou_events = 0;
+    have_signals = 0;
+    nevents = 0;
+
+    inv.nfds = nfds;
+    lfields->inv = &inv;
+
+    for (i = 0; i < nfds; i++) {
+      pe = events + i;
+      fd = pe->data.fd;
+
+      /* Skip invalidated events, see uv__platform_invalidate_fd */
+      if (fd == -1)
+        continue;
+
+      if (fd == iou->ringfd) {
+        uv__poll_io_uring(loop, iou);
+        have_iou_events = 1;
+        continue;
+      }
+
+      assert(fd >= 0);
+      assert((unsigned) fd < loop->nwatchers);
+
+      w = loop->watchers[fd];
+
+      if (w == NULL) {
+        /* File descriptor that we've stopped watching, disarm it.
+         *
+         * Ignore all errors because we may be racing with another thread
+         * when the file descriptor is closed.
+         */
+        uv__epoll_ctl_prep(epollfd, ctl, &prep, EPOLL_CTL_DEL, fd, pe);
+        continue;
+      }
+
+      /* Give users only events they're interested in. Prevents spurious
+       * callbacks when previous callback invocation in this loop has stopped
+       * the current watcher. Also, filters out events that users has not
+       * requested us to watch.
+       */
+      pe->events &= w->pevents | POLLERR | POLLHUP;
+
+      /* Work around an epoll quirk where it sometimes reports just the
+       * EPOLLERR or EPOLLHUP event.  In order to force the event loop to
+       * move forward, we merge in the read/write events that the watcher
+       * is interested in; uv__read() and uv__write() will then deal with
+       * the error or hangup in the usual fashion.
+       *
+       * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user
+       * reads the available data, calls uv_read_stop(), then sometime later
+       * calls uv_read_start() again.  By then, libuv has forgotten about the
+       * hangup and the kernel won't report EPOLLIN again because there's
+       * nothing left to read.  If anything, libuv is to blame here.  The
+       * current hack is just a quick bandaid; to properly fix it, libuv
+       * needs to remember the error/hangup event.  We should get that for
+       * free when we switch over to edge-triggered I/O.
+       */
+      if (pe->events == POLLERR || pe->events == POLLHUP)
+        pe->events |=
+          w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
+
+      if (pe->events != 0) {
+        /* Run signal watchers last.  This also affects child process watchers
+         * because those are implemented in terms of signal watchers.
+         */
+        if (w == &loop->signal_io_watcher) {
+          have_signals = 1;
+        } else {
+          uv__metrics_update_idle_time(loop);
+          w->cb(loop, w, pe->events);
+        }
+
+        nevents++;
+      }
+    }
+
+    uv__metrics_inc_events(loop, nevents);
+    if (reset_timeout != 0) {
+      timeout = user_timeout;
+      reset_timeout = 0;
+      uv__metrics_inc_events_waiting(loop, nevents);
+    }
+
+    if (have_signals != 0) {
+      uv__metrics_update_idle_time(loop);
+      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
+    }
+
+    lfields->inv = NULL;
+
+    if (have_iou_events != 0)
+      break;  /* Event loop should cycle now so don't poll again. */
+
+    if (have_signals != 0)
+      break;  /* Event loop should cycle now so don't poll again. */
+
+    if (nevents != 0) {
+      if (nfds == ARRAY_SIZE(events) && --count != 0) {
+        /* Poll for more events but don't block this time. */
+        timeout = 0;
+        continue;
+      }
+      break;
+    }
+
+    if (timeout == 0)
+      break;
+
+    if (timeout == -1)
+      continue;
+
+update_timeout:
+    assert(timeout > 0);
+
+    real_timeout -= (loop->time - base);
+    if (real_timeout <= 0)
+      break;
+
+    timeout = real_timeout;
+  }
+
+  if (ctl->ringfd != -1)
+    while (*ctl->sqhead != *ctl->sqtail)
+      uv__epoll_ctl_flush(epollfd, ctl, &prep);
+}
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  static std::atomic<clock_t> fast_clock_id = -1;
+  struct timespec t;
+  clock_t clock_id;
+
+  /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has
+   * millisecond granularity or better.  CLOCK_MONOTONIC_COARSE is
+   * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may
+   * decide to make a costly system call.
+   */
+  /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE
+   * when it has microsecond granularity or better (unlikely).
+   */
+  clock_id = CLOCK_MONOTONIC;
+  if (type != UV_CLOCK_FAST)
+    goto done;
+
+  clock_id = std::atomic_load_explicit(&fast_clock_id, std::memory_order_relaxed);
+  if (clock_id != -1)
+    goto done;
+
+  clock_id = CLOCK_MONOTONIC;
+  if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t))
+    if (t.tv_nsec <= 1 * 1000 * 1000)
+      clock_id = CLOCK_MONOTONIC_COARSE;
+
+  std::atomic_store_explicit(&fast_clock_id, clock_id, std::memory_order_relaxed);
+
+done:
+
+  if (clock_gettime(clock_id, &t))
+    return 0;  /* Not really possible. */
+
+  return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  char buf[1024];
+  const char* s;
+  ssize_t n;
+  long val;
+  int fd;
+  int i;
+
+  do
+    fd = open("/proc/self/stat", O_RDONLY);
+  while (fd == -1 && errno == EINTR);
+
+  if (fd == -1)
+    return UV__ERR(errno);
+
+  do
+    n = read(fd, buf, sizeof(buf) - 1);
+  while (n == -1 && errno == EINTR);
+
+  uv__close(fd);
+  if (n == -1)
+    return UV__ERR(errno);
+  buf[n] = '\0';
+
+  s = strchr(buf, ' ');
+  if (s == NULL)
+    goto err;
+
+  s += 1;
+  if (*s != '(')
+    goto err;
+
+  s = strchr(s, ')');
+  if (s == NULL)
+    goto err;
+
+  for (i = 1; i <= 22; i++) {
+    s = strchr(s + 1, ' ');
+    if (s == NULL)
+      goto err;
+  }
+
+  errno = 0;
+  val = strtol(s, NULL, 10);
+  if (errno != 0)
+    goto err;
+  if (val < 0)
+    goto err;
+
+  *rss = val * getpagesize();
+  return 0;
+
+err:
+  return UV_EINVAL;
+}
+
+int uv_uptime(double* uptime) {
+  struct timespec now;
+  char buf[128];
+
+  /* Consult /proc/uptime when present (common case), or fall back to
+   * clock_gettime. Why not always clock_gettime? It doesn't always return the
+   * right result under OpenVZ and possibly other containerized environments.
+   */
+  if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf)))
+    if (1 == sscanf(buf, "%lf", uptime))
+      return 0;
+
+  if (clock_gettime(CLOCK_BOOTTIME, &now))
+    return UV__ERR(errno);
+
+  *uptime = now.tv_sec;
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
+#if defined(__PPC__)
+  static const char model_marker[] = "cpu\t\t: ";
+#elif defined(__arm__)
+  static const char model_marker[] = "Processor\t: ";
+#elif defined(__aarch64__)
+  static const char model_marker[] = "CPU part\t: ";
+#elif defined(__mips__)
+  static const char model_marker[] = "cpu model\t\t: ";
+#elif defined(__loongarch__)
+  static const char model_marker[] = "cpu family\t\t: ";
+#else
+  static const char model_marker[] = "model name\t: ";
+#endif
+  static const char parts[] =
+#ifdef __aarch64__
+    "0x811\nARM810\n"       "0x920\nARM920\n"      "0x922\nARM922\n"
+    "0x926\nARM926\n"       "0x940\nARM940\n"      "0x946\nARM946\n"
+    "0x966\nARM966\n"       "0xa20\nARM1020\n"      "0xa22\nARM1022\n"
+    "0xa26\nARM1026\n"      "0xb02\nARM11 MPCore\n" "0xb36\nARM1136\n"
+    "0xb56\nARM1156\n"      "0xb76\nARM1176\n"      "0xc05\nCortex-A5\n"
+    "0xc07\nCortex-A7\n"    "0xc08\nCortex-A8\n"    "0xc09\nCortex-A9\n"
+    "0xc0d\nCortex-A17\n"   /* Originally A12 */
+    "0xc0f\nCortex-A15\n"   "0xc0e\nCortex-A17\n"   "0xc14\nCortex-R4\n"
+    "0xc15\nCortex-R5\n"    "0xc17\nCortex-R7\n"    "0xc18\nCortex-R8\n"
+    "0xc20\nCortex-M0\n"    "0xc21\nCortex-M1\n"    "0xc23\nCortex-M3\n"
+    "0xc24\nCortex-M4\n"    "0xc27\nCortex-M7\n"    "0xc60\nCortex-M0+\n"
+    "0xd01\nCortex-A32\n"   "0xd03\nCortex-A53\n"   "0xd04\nCortex-A35\n"
+    "0xd05\nCortex-A55\n"   "0xd06\nCortex-A65\n"   "0xd07\nCortex-A57\n"
+    "0xd08\nCortex-A72\n"   "0xd09\nCortex-A73\n"   "0xd0a\nCortex-A75\n"
+    "0xd0b\nCortex-A76\n"   "0xd0c\nNeoverse-N1\n"  "0xd0d\nCortex-A77\n"
+    "0xd0e\nCortex-A76AE\n" "0xd13\nCortex-R52\n"   "0xd20\nCortex-M23\n"
+    "0xd21\nCortex-M33\n"   "0xd41\nCortex-A78\n"   "0xd42\nCortex-A78AE\n"
+    "0xd4a\nNeoverse-E1\n"  "0xd4b\nCortex-A78C\n"
+#endif
+    "";
+  struct cpu {
+    unsigned long long freq, user, nice, sys, idle, irq;
+    unsigned model;
+  };
+  FILE* fp;
+  char* p;
+  int found;
+  int n;
+  unsigned i;
+  unsigned cpu;
+  unsigned maxcpu;
+  unsigned size;
+  unsigned long long skip;
+  struct cpu (*cpus)[8192];  /* Kernel maximum. */
+  struct cpu* c;
+  struct cpu t;
+  char (*model)[64];
+  unsigned char bitmap[ARRAY_SIZE(*cpus) / 8];
+  /* Assumption: even big.LITTLE systems will have only a handful
+   * of different CPU models. Most systems will just have one.
+   */
+  char models[8][64];
+  char buf[1024];
+
+  memset(bitmap, 0, sizeof(bitmap));
+  memset(models, 0, sizeof(models));
+  snprintf(*models, sizeof(*models), "unknown");
+  maxcpu = 0;
+
+  cpus = (decltype(cpus))uv__calloc(ARRAY_SIZE(*cpus), sizeof(**cpus));
+  if (cpus == NULL)
+    return UV_ENOMEM;
+
+  fp = uv__open_file("/proc/stat");
+  if (fp == NULL) {
+    uv__free(cpus);
+    return UV__ERR(errno);
+  }
+
+  /* Skip first line. */
+  if (!fgets(buf, sizeof(buf), fp)) {
+    uv__free(cpus);
+    return UV__ERR(errno);
+  }
+
+  for (;;) {
+    memset(&t, 0, sizeof(t));
+
+    n = fscanf(fp, "cpu%u %llu %llu %llu %llu %llu %llu",
+               &cpu, &t.user, &t.nice, &t.sys, &t.idle, &skip, &t.irq);
+
+    if (n != 7)
+      break;
+
+    /* Skip rest of line. */
+    if (!fgets(buf, sizeof(buf), fp)) {
+      break;
+    }
+
+    if (cpu >= ARRAY_SIZE(*cpus))
+      continue;
+
+    (*cpus)[cpu] = t;
+
+    bitmap[cpu >> 3] |= 1 << (cpu & 7);
+
+    if (cpu >= maxcpu)
+      maxcpu = cpu + 1;
+  }
+
+  fclose(fp);
+
+  fp = uv__open_file("/proc/cpuinfo");
+  if (fp == NULL)
+    goto nocpuinfo;
+
+  for (;;) {
+    if (1 != fscanf(fp, "processor\t: %u\n", &cpu))
+      break;  /* Parse error. */
+
+    found = 0;
+    while (!found && fgets(buf, sizeof(buf), fp))
+      found = !strncmp(buf, model_marker, sizeof(model_marker) - 1);
+
+    if (!found)
+      goto next;
+
+    p = buf + sizeof(model_marker) - 1;
+    n = (int) strcspn(p, "\n");
+
+    /* arm64: translate CPU part code to model name. */
+    if (*parts) {
+      p = (char*)memmem(parts, sizeof(parts) - 1, p, n + 1);
+      if (p == NULL)
+        p = const_cast<char*>("unknown");
+      else
+        p += n + 1;
+      n = (int) strcspn(p, "\n");
+    }
+
+    found = 0;
+    for (model = models; !found && model < ARRAY_END(models); model++)
+      found = !strncmp(p, *model, strlen(*model));
+
+    if (!found)
+      goto next;
+
+    if (**model == '\0')
+      snprintf(*model, sizeof(*model), "%.*s", n, p);
+
+    if (cpu < maxcpu)
+      (*cpus)[cpu].model = model - models;
+
+next:
+    while (fgets(buf, sizeof(buf), fp))
+      if (*buf == '\n')
+        break;
+  }
+
+  fclose(fp);
+  fp = NULL;
+
+nocpuinfo:
+
+  n = 0;
+  for (cpu = 0; cpu < maxcpu; cpu++) {
+    if (!(bitmap[cpu >> 3] & (1 << (cpu & 7))))
+      continue;
+
+    n++;
+    snprintf(buf, sizeof(buf),
+             "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", cpu);
+
+    fp = uv__open_file(buf);
+    if (fp == NULL)
+      continue;
+
+    if (0 > fscanf(fp, "%llu", &(*cpus)[cpu].freq)) {
+      (*cpus)[cpu].freq = 0llu;
+    }
+    fclose(fp);
+    fp = NULL;
+  }
+
+  size = n * sizeof(**ci) + sizeof(models);
+  *ci = (uv_cpu_info_t*)uv__malloc(size);
+  *count = 0;
+
+  if (*ci == NULL) {
+    uv__free(cpus);
+    return UV_ENOMEM;
+  }
+
+  *count = n;
+  p = (char*)memcpy(*ci + n, models, sizeof(models));
+
+  i = 0;
+  for (cpu = 0; cpu < maxcpu; cpu++) {
+    if (!(bitmap[cpu >> 3] & (1 << (cpu & 7))))
+      continue;
+
+    c = *cpus + cpu;
+
+    (*ci)[i++] = uv_cpu_info_t{
+      .model     = p + c->model * sizeof(*model),
+      .speed     = (int)(c->freq / 1000),
+      /* Note: sysconf(_SC_CLK_TCK) is fixed at 100 Hz,
+       * therefore the multiplier is always 1000/100 = 10.
+       */
+      .cpu_times = {
+        .user = 10 * c->user,
+        .nice = 10 * c->nice,
+        .sys  = 10 * c->sys,
+        .idle = 10 * c->idle,
+        .irq  = 10 * c->irq,
+      }
+    };
+  }
+
+  uv__free(cpus);
+
+  return 0;
+}
+
+
+#ifdef HAVE_IFADDRS_H
+static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
+  if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
+    return 1;
+  if (ent->ifa_addr == NULL)
+    return 1;
+  /*
+   * On Linux getifaddrs returns information related to the raw underlying
+   * devices. We're not interested in this information yet.
+   */
+  if (ent->ifa_addr->sa_family == PF_PACKET)
+    return exclude_type;
+  return !exclude_type;
+}
+#endif
+
+int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+#ifndef HAVE_IFADDRS_H
+  *count = 0;
+  *addresses = NULL;
+  return UV_ENOSYS;
+#else
+  struct ifaddrs *addrs, *ent;
+  uv_interface_address_t* address;
+  int i;
+  struct sockaddr_ll *sll;
+
+  *count = 0;
+  *addresses = NULL;
+
+  if (getifaddrs(&addrs))
+    return UV__ERR(errno);
+
+  /* Count the number of interfaces */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
+      continue;
+
+    (*count)++;
+  }
+
+  if (*count == 0) {
+    freeifaddrs(addrs);
+    return 0;
+  }
+
+  /* Make sure the memory is initiallized to zero using calloc() */
+  *addresses = (uv_interface_address_t*)uv__calloc(*count, sizeof(**addresses));
+  if (!(*addresses)) {
+    freeifaddrs(addrs);
+    return UV_ENOMEM;
+  }
+
+  address = *addresses;
+
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
+      continue;
+
+    address->name = uv__strdup(ent->ifa_name);
+
+    if (ent->ifa_addr->sa_family == AF_INET6) {
+      address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
+    } else {
+      address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
+    }
+
+    if (ent->ifa_netmask->sa_family == AF_INET6) {
+      address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
+    } else {
+      address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
+    }
+
+    address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
+
+    address++;
+  }
+
+  /* Fill in physical addresses for each interface */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
+      continue;
+
+    address = *addresses;
+
+    for (i = 0; i < (*count); i++) {
+      size_t namelen = strlen(ent->ifa_name);
+      /* Alias interface share the same physical address */
+      if (strncmp(address->name, ent->ifa_name, namelen) == 0 &&
+          (address->name[namelen] == 0 || address->name[namelen] == ':')) {
+        sll = (struct sockaddr_ll*)ent->ifa_addr;
+        memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr));
+      }
+      address++;
+    }
+  }
+
+  freeifaddrs(addrs);
+
+  return 0;
+#endif
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses,
+  int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    uv__free(addresses[i].name);
+  }
+
+  uv__free(addresses);
+}
+
+
+void uv__set_process_title(const char* title) {
+#if defined(PR_SET_NAME)
+  prctl(PR_SET_NAME, title);  /* Only copies first 16 characters. */
+#endif
+}
+
+
+static uint64_t uv__read_proc_meminfo(const char* what) {
+  uint64_t rc;
+  char* p;
+  char buf[4096];  /* Large enough to hold all of /proc/meminfo. */
+
+  if (uv__slurp("/proc/meminfo", buf, sizeof(buf)))
+    return 0;
+
+  p = strstr(buf, what);
+
+  if (p == NULL)
+    return 0;
+
+  p += strlen(what);
+
+  rc = 0;
+  sscanf(p, "%" PRIu64 " kB", &rc);
+
+  return rc * 1024;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  struct sysinfo info;
+  uint64_t rc;
+
+  rc = uv__read_proc_meminfo("MemAvailable:");
+
+  if (rc != 0)
+    return rc;
+
+  if (0 == sysinfo(&info))
+    return (uint64_t) info.freeram * info.mem_unit;
+
+  return 0;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  struct sysinfo info;
+  uint64_t rc;
+
+  rc = uv__read_proc_meminfo("MemTotal:");
+
+  if (rc != 0)
+    return rc;
+
+  if (0 == sysinfo(&info))
+    return (uint64_t) info.totalram * info.mem_unit;
+
+  return 0;
+}
+
+
+static uint64_t uv__read_uint64(const char* filename) {
+  char buf[32];  /* Large enough to hold an encoded uint64_t. */
+  uint64_t rc;
+
+  rc = 0;
+  if (0 == uv__slurp(filename, buf, sizeof(buf)))
+    if (1 != sscanf(buf, "%" PRIu64, &rc))
+      if (0 == strcmp(buf, "max\n"))
+        rc = UINT64_MAX;
+
+  return rc;
+}
+
+
+/* Given a buffer with the contents of a cgroup1 /proc/self/cgroups,
+ * finds the location and length of the memory controller mount path.
+ * This disregards the leading / for easy concatenation of paths.
+ * Returns NULL if the memory controller wasn't found. */
+static char* uv__cgroup1_find_memory_controller(char buf[1024],
+                                                int* n) {
+  char* p;
+
+  /* Seek to the memory controller line. */
+  p = strchr(buf, ':');
+  while (p != NULL && strncmp(p, ":memory:", 8)) {
+    p = strchr(p, '\n');
+    if (p != NULL)
+      p = strchr(p, ':');
+  }
+
+  if (p != NULL) {
+    /* Determine the length of the mount path. */
+    p = p + strlen(":memory:/");
+    *n = (int) strcspn(p, "\n");
+  }
+
+  return p;
+}
+
+static void uv__get_cgroup1_memory_limits(char buf[1024], uint64_t* high,
+                                          uint64_t* max) {
+  char filename[4097];
+  char* p;
+  int n;
+  uint64_t cgroup1_max;
+
+  /* Find out where the controller is mounted. */
+  p = uv__cgroup1_find_memory_controller(buf, &n);
+  if (p != NULL) {
+    snprintf(filename, sizeof(filename),
+             "/sys/fs/cgroup/memory/%.*s/memory.soft_limit_in_bytes", n, p);
+    *high = uv__read_uint64(filename);
+
+    snprintf(filename, sizeof(filename),
+             "/sys/fs/cgroup/memory/%.*s/memory.limit_in_bytes", n, p);
+    *max = uv__read_uint64(filename);
+
+    /* If the controller wasn't mounted, the reads above will have failed,
+     * as indicated by uv__read_uint64 returning 0.
+     */
+     if (*high != 0 && *max != 0)
+       goto update_limits;
+  }
+
+  /* Fall back to the limits of the global memory controller. */
+  *high = uv__read_uint64("/sys/fs/cgroup/memory/memory.soft_limit_in_bytes");
+  *max = uv__read_uint64("/sys/fs/cgroup/memory/memory.limit_in_bytes");
+
+  /* uv__read_uint64 detects cgroup2's "max", so we need to separately detect
+   * cgroup1's maximum value (which is derived from LONG_MAX and PAGE_SIZE).
+   */
+update_limits:
+  cgroup1_max = LONG_MAX & ~(sysconf(_SC_PAGESIZE) - 1);
+  if (*high == cgroup1_max)
+    *high = UINT64_MAX;
+  if (*max == cgroup1_max)
+    *max = UINT64_MAX;
+}
+
+static void uv__get_cgroup2_memory_limits(char buf[1024], uint64_t* high,
+                                          uint64_t* max) {
+  char filename[4097];
+  char* p;
+  int n;
+
+  /* Find out where the controller is mounted. */
+  p = buf + strlen("0::/");
+  n = (int) strcspn(p, "\n");
+
+  /* Read the memory limits of the controller. */
+  snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%.*s/memory.max", n, p);
+  *max = uv__read_uint64(filename);
+  snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%.*s/memory.high", n, p);
+  *high = uv__read_uint64(filename);
+}
+
+static uint64_t uv__get_cgroup_constrained_memory(char buf[1024]) {
+  uint64_t high;
+  uint64_t max;
+
+  /* In the case of cgroupv2, we'll only have a single entry. */
+  if (strncmp(buf, "0::/", 4))
+    uv__get_cgroup1_memory_limits(buf, &high, &max);
+  else
+    uv__get_cgroup2_memory_limits(buf, &high, &max);
+
+  if (high == 0 || max == 0)
+    return 0;
+
+  return high < max ? high : max;
+}
+
+uint64_t uv_get_constrained_memory(void) {
+  char buf[1024];
+
+  if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf)))
+    return 0;
+
+  return uv__get_cgroup_constrained_memory(buf);
+}
+
+
+static uint64_t uv__get_cgroup1_current_memory(char buf[1024]) {
+  char filename[4097];
+  uint64_t current;
+  char* p;
+  int n;
+
+  /* Find out where the controller is mounted. */
+  p = uv__cgroup1_find_memory_controller(buf, &n);
+  if (p != NULL) {
+    snprintf(filename, sizeof(filename),
+            "/sys/fs/cgroup/memory/%.*s/memory.usage_in_bytes", n, p);
+    current = uv__read_uint64(filename);
+
+    /* If the controller wasn't mounted, the reads above will have failed,
+     * as indicated by uv__read_uint64 returning 0.
+     */
+    if (current != 0)
+      return current;
+  }
+
+  /* Fall back to the usage of the global memory controller. */
+  return uv__read_uint64("/sys/fs/cgroup/memory/memory.usage_in_bytes");
+}
+
+static uint64_t uv__get_cgroup2_current_memory(char buf[1024]) {
+  char filename[4097];
+  char* p;
+  int n;
+
+  /* Find out where the controller is mounted. */
+  p = buf + strlen("0::/");
+  n = (int) strcspn(p, "\n");
+
+  snprintf(filename, sizeof(filename),
+           "/sys/fs/cgroup/%.*s/memory.current", n, p);
+  return uv__read_uint64(filename);
+}
+
+uint64_t uv_get_available_memory(void) {
+  char buf[1024];
+  uint64_t constrained;
+  uint64_t current;
+  uint64_t total;
+
+  if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf)))
+    return 0;
+
+  constrained = uv__get_cgroup_constrained_memory(buf);
+  if (constrained == 0)
+    return uv_get_free_memory();
+
+  total = uv_get_total_memory();
+  if (constrained > total)
+    return uv_get_free_memory();
+
+  /* In the case of cgroupv2, we'll only have a single entry. */
+  if (strncmp(buf, "0::/", 4))
+    current = uv__get_cgroup1_current_memory(buf);
+  else
+    current = uv__get_cgroup2_current_memory(buf);
+
+  /* memory usage can be higher than the limit (for short bursts of time) */
+  if (constrained < current)
+    return 0;
+
+  return constrained - current;
+}
+
+
+void uv_loadavg(double avg[3]) {
+  struct sysinfo info;
+  char buf[128];  /* Large enough to hold all of /proc/loadavg. */
+
+  if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf)))
+    if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2]))
+      return;
+
+  if (sysinfo(&info) < 0)
+    return;
+
+  avg[0] = (double) info.loads[0] / 65536.0;
+  avg[1] = (double) info.loads[1] / 65536.0;
+  avg[2] = (double) info.loads[2] / 65536.0;
+}
+
+
+static int compare_watchers(const struct watcher_list* a,
+                            const struct watcher_list* b) {
+  if (a->wd < b->wd) return -1;
+  if (a->wd > b->wd) return 1;
+  return 0;
+}
+
+
+static int init_inotify(uv_loop_t* loop) {
+  int fd;
+
+  if (loop->inotify_fd != -1)
+    return 0;
+
+  fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+  if (fd < 0)
+    return UV__ERR(errno);
+
+  loop->inotify_fd = fd;
+  uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd);
+  uv__io_start(loop, &loop->inotify_read_watcher, POLLIN);
+
+  return 0;
+}
+
+
+static int uv__inotify_fork(uv_loop_t* loop, struct watcher_list* root) {
+  /* Open the inotify_fd, and re-arm all the inotify watchers. */
+  int err;
+  struct watcher_list* tmp_watcher_list_iter;
+  struct watcher_list* watcher_list;
+  struct watcher_list tmp_watcher_list;
+  struct uv__queue queue;
+  struct uv__queue* q;
+  uv_fs_event_t* handle;
+  char* tmp_path;
+
+  if (root == NULL)
+    return 0;
+
+  /* We must restore the old watcher list to be able to close items
+   * out of it.
+   */
+  loop->inotify_watchers = root;
+
+  uv__queue_init(&tmp_watcher_list.watchers);
+  /* Note that the queue we use is shared with the start and stop()
+   * functions, making uv__queue_foreach unsafe to use. So we use the
+   * uv__queue_move trick to safely iterate. Also don't free the watcher
+   * list until we're done iterating. c.f. uv__inotify_read.
+   */
+  RB_FOREACH_SAFE(watcher_list, watcher_root,
+                  uv__inotify_watchers(loop), tmp_watcher_list_iter) {
+    watcher_list->iterating = 1;
+    uv__queue_move(&watcher_list->watchers, &queue);
+    while (!uv__queue_empty(&queue)) {
+      q = uv__queue_head(&queue);
+      handle = uv__queue_data(q, uv_fs_event_t, watchers);
+      /* It's critical to keep a copy of path here, because it
+       * will be set to NULL by stop() and then deallocated by
+       * maybe_free_watcher_list
+       */
+      tmp_path = uv__strdup(handle->path);
+      assert(tmp_path != NULL);
+      uv__queue_remove(q);
+      uv__queue_insert_tail(&watcher_list->watchers, q);
+      uv_fs_event_stop(handle);
+
+      uv__queue_insert_tail(&tmp_watcher_list.watchers, &handle->watchers);
+      handle->path = tmp_path;
+    }
+    watcher_list->iterating = 0;
+    maybe_free_watcher_list(watcher_list, loop);
+  }
+
+  uv__queue_move(&tmp_watcher_list.watchers, &queue);
+  while (!uv__queue_empty(&queue)) {
+      q = uv__queue_head(&queue);
+      uv__queue_remove(q);
+      handle = uv__queue_data(q, uv_fs_event_t, watchers);
+      tmp_path = handle->path;
+      handle->path = NULL;
+      err = uv_fs_event_start(handle, handle->cb, tmp_path, 0);
+      uv__free(tmp_path);
+      if (err)
+        return err;
+  }
+
+  return 0;
+}
+
+
+static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) {
+  struct watcher_list w;
+  w.wd = wd;
+  return RB_FIND(watcher_root, uv__inotify_watchers(loop), &w);
+}
+
+
+static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) {
+  /* if the watcher_list->watchers is being iterated over, we can't free it. */
+  if ((!w->iterating) && uv__queue_empty(&w->watchers)) {
+    /* No watchers left for this path. Clean up. */
+    RB_REMOVE(watcher_root, uv__inotify_watchers(loop), w);
+    inotify_rm_watch(loop->inotify_fd, w->wd);
+    uv__free(w);
+  }
+}
+
+
+static void uv__inotify_read(uv_loop_t* loop,
+                             uv__io_t* dummy,
+                             unsigned int events) {
+  const struct inotify_event* e;
+  struct watcher_list* w;
+  uv_fs_event_t* h;
+  struct uv__queue queue;
+  struct uv__queue* q;
+  const char* path;
+  ssize_t size;
+  const char *p;
+  /* needs to be large enough for sizeof(inotify_event) + strlen(path) */
+  char buf[4096];
+
+  for (;;) {
+    do
+      size = read(loop->inotify_fd, buf, sizeof(buf));
+    while (size == -1 && errno == EINTR);
+
+    if (size == -1) {
+      assert(errno == EAGAIN || errno == EWOULDBLOCK);
+      break;
+    }
+
+    assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */
+
+    /* Now we have one or more inotify_event structs. */
+    for (p = buf; p < buf + size; p += sizeof(*e) + e->len) {
+      e = (const struct inotify_event*) p;
+
+      events = 0;
+      if (e->mask & (IN_ATTRIB|IN_MODIFY))
+        events |= UV_CHANGE;
+      if (e->mask & ~(IN_ATTRIB|IN_MODIFY))
+        events |= UV_RENAME;
+
+      w = find_watcher(loop, e->wd);
+      if (w == NULL)
+        continue; /* Stale event, no watchers left. */
+
+      /* inotify does not return the filename when monitoring a single file
+       * for modifications. Repurpose the filename for API compatibility.
+       * I'm not convinced this is a good thing, maybe it should go.
+       */
+      path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path);
+
+      /* We're about to iterate over the queue and call user's callbacks.
+       * What can go wrong?
+       * A callback could call uv_fs_event_stop()
+       * and the queue can change under our feet.
+       * So, we use uv__queue_move() trick to safely iterate over the queue.
+       * And we don't free the watcher_list until we're done iterating.
+       *
+       * First,
+       * tell uv_fs_event_stop() (that could be called from a user's callback)
+       * not to free watcher_list.
+       */
+      w->iterating = 1;
+      uv__queue_move(&w->watchers, &queue);
+      while (!uv__queue_empty(&queue)) {
+        q = uv__queue_head(&queue);
+        h = uv__queue_data(q, uv_fs_event_t, watchers);
+
+        uv__queue_remove(q);
+        uv__queue_insert_tail(&w->watchers, q);
+
+        h->cb(h, path, events, 0);
+      }
+      /* done iterating, time to (maybe) free empty watcher_list */
+      w->iterating = 0;
+      maybe_free_watcher_list(w, loop);
+    }
+  }
+}
+
+
+int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
+  return 0;
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle,
+                      uv_fs_event_cb cb,
+                      const char* path,
+                      unsigned int flags) {
+  struct watcher_list* w;
+  uv_loop_t* loop;
+  size_t len;
+  int events;
+  int err;
+  int wd;
+
+  if (uv__is_active(handle))
+    return UV_EINVAL;
+
+  loop = handle->loop;
+
+  err = init_inotify(loop);
+  if (err)
+    return err;
+
+  events = IN_ATTRIB
+         | IN_CREATE
+         | IN_MODIFY
+         | IN_DELETE
+         | IN_DELETE_SELF
+         | IN_MOVE_SELF
+         | IN_MOVED_FROM
+         | IN_MOVED_TO;
+
+  wd = inotify_add_watch(loop->inotify_fd, path, events);
+  if (wd == -1)
+    return UV__ERR(errno);
+
+  w = find_watcher(loop, wd);
+  if (w)
+    goto no_insert;
+
+  len = strlen(path) + 1;
+  w = (watcher_list*)uv__malloc(sizeof(*w) + len);
+  if (w == NULL)
+    return UV_ENOMEM;
+
+  w->wd = wd;
+  w->path = (char*)memcpy(w + 1, path, len);
+  uv__queue_init(&w->watchers);
+  w->iterating = 0;
+  RB_INSERT(watcher_root, uv__inotify_watchers(loop), w);
+
+no_insert:
+  uv__handle_start(handle);
+  uv__queue_insert_tail(&w->watchers, &handle->watchers);
+  handle->path = w->path;
+  handle->cb = cb;
+  handle->wd = wd;
+
+  return 0;
+}
+
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  struct watcher_list* w;
+
+  if (!uv__is_active(handle))
+    return 0;
+
+  w = find_watcher(handle->loop, handle->wd);
+  assert(w != NULL);
+
+  handle->wd = -1;
+  handle->path = NULL;
+  uv__handle_stop(handle);
+  uv__queue_remove(&handle->watchers);
+
+  maybe_free_watcher_list(w, handle->loop);
+
+  return 0;
+}
+
+
+void uv__fs_event_close(uv_fs_event_t* handle) {
+  uv_fs_event_stop(handle);
+}
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/loop-watcher.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/loop-watcher.cpp
index b8c1c2a..2db8b51 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/loop-watcher.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/loop-watcher.cpp
@@ -32,7 +32,7 @@
   int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) {           \
     if (uv__is_active(handle)) return 0;                                      \
     if (cb == NULL) return UV_EINVAL;                                         \
-    QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue);         \
+    uv__queue_insert_head(&handle->loop->name##_handles, &handle->queue);     \
     handle->name##_cb = cb;                                                   \
     uv__handle_start(handle);                                                 \
     return 0;                                                                 \
@@ -40,21 +40,21 @@
                                                                               \
   int uv_##name##_stop(uv_##name##_t* handle) {                               \
     if (!uv__is_active(handle)) return 0;                                     \
-    QUEUE_REMOVE(&handle->queue);                                             \
+    uv__queue_remove(&handle->queue);                                         \
     uv__handle_stop(handle);                                                  \
     return 0;                                                                 \
   }                                                                           \
                                                                               \
   void uv__run_##name(uv_loop_t* loop) {                                      \
     uv_##name##_t* h;                                                         \
-    QUEUE queue;                                                              \
-    QUEUE* q;                                                                 \
-    QUEUE_MOVE(&loop->name##_handles, &queue);                                \
-    while (!QUEUE_EMPTY(&queue)) {                                            \
-      q = QUEUE_HEAD(&queue);                                                 \
-      h = QUEUE_DATA(q, uv_##name##_t, queue);                                \
-      QUEUE_REMOVE(q);                                                        \
-      QUEUE_INSERT_TAIL(&loop->name##_handles, q);                            \
+    struct uv__queue queue;                                                   \
+    struct uv__queue* q;                                                      \
+    uv__queue_move(&loop->name##_handles, &queue);                            \
+    while (!uv__queue_empty(&queue)) {                                        \
+      q = uv__queue_head(&queue);                                             \
+      h = uv__queue_data(q, uv_##name##_t, queue);                            \
+      uv__queue_remove(q);                                                    \
+      uv__queue_insert_tail(&loop->name##_handles, q);                        \
       h->name##_cb(h);                                                        \
     }                                                                         \
   }                                                                           \
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/loop.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/loop.cpp
index 2e819cd..3babe4d 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/loop.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/loop.cpp
@@ -45,22 +45,25 @@
   err = uv_mutex_init(&lfields->loop_metrics.lock);
   if (err)
     goto fail_metrics_mutex_init;
+  memset(&lfields->loop_metrics.metrics,
+         0,
+         sizeof(lfields->loop_metrics.metrics));
 
   heap_init((struct heap*) &loop->timer_heap);
-  QUEUE_INIT(&loop->wq);
-  QUEUE_INIT(&loop->idle_handles);
-  QUEUE_INIT(&loop->async_handles);
-  QUEUE_INIT(&loop->check_handles);
-  QUEUE_INIT(&loop->prepare_handles);
-  QUEUE_INIT(&loop->handle_queue);
+  uv__queue_init(&loop->wq);
+  uv__queue_init(&loop->idle_handles);
+  uv__queue_init(&loop->async_handles);
+  uv__queue_init(&loop->check_handles);
+  uv__queue_init(&loop->prepare_handles);
+  uv__queue_init(&loop->handle_queue);
 
   loop->active_handles = 0;
   loop->active_reqs.count = 0;
   loop->nfds = 0;
   loop->watchers = NULL;
   loop->nwatchers = 0;
-  QUEUE_INIT(&loop->pending_queue);
-  QUEUE_INIT(&loop->watcher_queue);
+  uv__queue_init(&loop->pending_queue);
+  uv__queue_init(&loop->watcher_queue);
 
   loop->closing_handles = NULL;
   uv__update_time(loop);
@@ -79,13 +82,10 @@
     goto fail_platform_init;
 
   uv__signal_global_once_init();
-  err = uv_signal_init(loop, &loop->child_watcher);
+  err = uv__process_init(loop);
   if (err)
     goto fail_signal_init;
-
-  uv__handle_unref(&loop->child_watcher);
-  loop->child_watcher.flags |= UV_HANDLE_INTERNAL;
-  QUEUE_INIT(&loop->process_handles);
+  uv__queue_init(&loop->process_handles);
 
   err = uv_rwlock_init(&loop->cloexec_lock);
   if (err)
@@ -152,9 +152,9 @@
     if (w == NULL)
       continue;
 
-    if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) {
+    if (w->pevents != 0 && uv__queue_empty(&w->watcher_queue)) {
       w->events = 0; /* Force re-registration in uv__io_poll. */
-      QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
+      uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue);
     }
   }
 
@@ -180,7 +180,7 @@
   }
 
   uv_mutex_lock(&loop->wq_mutex);
-  assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!");
+  assert(uv__queue_empty(&loop->wq) && "thread pool work queue not empty!");
   assert(!uv__has_active_reqs(loop));
   uv_mutex_unlock(&loop->wq_mutex);
   uv_mutex_destroy(&loop->wq_mutex);
@@ -192,8 +192,8 @@
   uv_rwlock_destroy(&loop->cloexec_lock);
 
 #if 0
-  assert(QUEUE_EMPTY(&loop->pending_queue));
-  assert(QUEUE_EMPTY(&loop->watcher_queue));
+  assert(uv__queue_empty(&loop->pending_queue));
+  assert(uv__queue_empty(&loop->watcher_queue));
   assert(loop->nfds == 0);
 #endif
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/netbsd.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/netbsd.cpp
index b6886a1..4c6d5a2 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/netbsd.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/netbsd.cpp
@@ -103,7 +103,7 @@
   int which[] = {CTL_VM, VM_UVMEXP};
 
   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
-    return UV__ERR(errno);
+    return 0;
 
   return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
 }
@@ -120,7 +120,7 @@
   size_t size = sizeof(info);
 
   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
-    return UV__ERR(errno);
+    return 0;
 
   return (uint64_t) info;
 }
@@ -131,6 +131,11 @@
 }
 
 
+uint64_t uv_get_available_memory(void) {
+  return uv_get_free_memory();
+}
+
+
 int uv_resident_set_memory(size_t* rss) {
   kvm_t *kd = NULL;
   struct kinfo_proc2 *kinfo = NULL;
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/openbsd.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/openbsd.cpp
index 62740f7..2aa61e2 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/openbsd.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/openbsd.cpp
@@ -116,7 +116,7 @@
   int which[] = {CTL_VM, VM_UVMEXP};
 
   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
-    return UV__ERR(errno);
+    return 0;
 
   return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
 }
@@ -128,7 +128,7 @@
   size_t size = sizeof(info);
 
   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
-    return UV__ERR(errno);
+    return 0;
 
   return (uint64_t) info;
 }
@@ -139,6 +139,11 @@
 }
 
 
+uint64_t uv_get_available_memory(void) {
+  return uv_get_free_memory();
+}
+
+
 int uv_resident_set_memory(size_t* rss) {
   struct kinfo_proc kinfo;
   size_t page_size = getpagesize();
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/pipe.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/pipe.cpp
index c8ba31d..a60b1a0 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/pipe.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/pipe.cpp
@@ -41,26 +41,60 @@
 
 
 int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
+  return uv_pipe_bind2(handle, name, strlen(name), 0);
+}
+
+
+int uv_pipe_bind2(uv_pipe_t* handle,
+                  const char* name,
+                  size_t namelen,
+                  unsigned int flags) {
   struct sockaddr_un saddr;
-  const char* pipe_fname;
+  char* pipe_fname;
   int sockfd;
   int err;
 
   pipe_fname = NULL;
 
+  if (flags & ~UV_PIPE_NO_TRUNCATE)
+    return UV_EINVAL;
+
+  if (name == NULL)
+    return UV_EINVAL;
+
+  if (namelen == 0)
+    return UV_EINVAL;
+
+#ifndef __linux__
+  /* Abstract socket namespace only works on Linux. */
+  if (*name == '\0')
+    return UV_EINVAL;
+#endif
+
+  if (flags & UV_PIPE_NO_TRUNCATE)
+    if (namelen > sizeof(saddr.sun_path))
+      return UV_EINVAL;
+
+  /* Truncate long paths. Documented behavior. */
+  if (namelen > sizeof(saddr.sun_path))
+    namelen = sizeof(saddr.sun_path);
+
   /* Already bound? */
   if (uv__stream_fd(handle) >= 0)
     return UV_EINVAL;
-  if (uv__is_closing(handle)) {
-    return UV_EINVAL;
-  }
-  /* Make a copy of the file name, it outlives this function's scope. */
-  pipe_fname = uv__strdup(name);
-  if (pipe_fname == NULL)
-    return UV_ENOMEM;
 
-  /* We've got a copy, don't touch the original any more. */
-  name = NULL;
+  if (uv__is_closing(handle))
+    return UV_EINVAL;
+
+  /* Make a copy of the file path unless it is an abstract socket.
+   * We unlink the file later but abstract sockets disappear
+   * automatically since they're not real file system entities.
+   */
+  if (*name != '\0') {
+    pipe_fname = uv__strdup(name);
+    if (pipe_fname == NULL)
+      return UV_ENOMEM;
+  }
 
   err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
   if (err < 0)
@@ -68,7 +102,7 @@
   sockfd = err;
 
   memset(&saddr, 0, sizeof saddr);
-  uv__strscpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path));
+  memcpy(&saddr.sun_path, name, namelen);
   saddr.sun_family = AF_UNIX;
 
   if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) {
@@ -83,12 +117,12 @@
 
   /* Success. */
   handle->flags |= UV_HANDLE_BOUND;
-  handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */
+  handle->pipe_fname = pipe_fname; /* NULL or a strdup'ed copy. */
   handle->io_watcher.fd = sockfd;
   return 0;
 
 err_socket:
-  uv__free((void*)pipe_fname);
+  uv__free(pipe_fname);
   return err;
 }
 
@@ -176,11 +210,44 @@
                     uv_pipe_t* handle,
                     const char* name,
                     uv_connect_cb cb) {
+  uv_pipe_connect2(req, handle, name, strlen(name), 0, cb);
+}
+
+
+int uv_pipe_connect2(uv_connect_t* req,
+                     uv_pipe_t* handle,
+                     const char* name,
+                     size_t namelen,
+                     unsigned int flags,
+                     uv_connect_cb cb) {
   struct sockaddr_un saddr;
   int new_sock;
   int err;
   int r;
 
+  if (flags & ~UV_PIPE_NO_TRUNCATE)
+    return UV_EINVAL;
+
+  if (name == NULL)
+    return UV_EINVAL;
+
+  if (namelen == 0)
+    return UV_EINVAL;
+
+#ifndef __linux__
+  /* Abstract socket namespace only works on Linux. */
+  if (*name == '\0')
+    return UV_EINVAL;
+#endif
+
+  if (flags & UV_PIPE_NO_TRUNCATE)
+    if (namelen > sizeof(saddr.sun_path))
+      return UV_EINVAL;
+
+  /* Truncate long paths. Documented behavior. */
+  if (namelen > sizeof(saddr.sun_path))
+    namelen = sizeof(saddr.sun_path);
+
   new_sock = (uv__stream_fd(handle) == -1);
 
   if (new_sock) {
@@ -191,7 +258,7 @@
   }
 
   memset(&saddr, 0, sizeof saddr);
-  uv__strscpy(saddr.sun_path, name, sizeof(saddr.sun_path));
+  memcpy(&saddr.sun_path, name, namelen);
   saddr.sun_family = AF_UNIX;
 
   do {
@@ -230,12 +297,13 @@
   uv__req_init(handle->loop, req, UV_CONNECT);
   req->handle = (uv_stream_t*)handle;
   req->cb = cb;
-  QUEUE_INIT(&req->queue);
+  uv__queue_init(&req->queue);
 
   /* Force callback to run on next tick in case of error. */
   if (err)
     uv__io_feed(handle->loop, &handle->io_watcher);
 
+  return 0;
 }
 
 
@@ -357,7 +425,7 @@
   }
 
   /* stat must be used as fstat has a bug on Darwin */
-  if (stat(name_buffer, &pipe_stat) == -1) {
+  if (uv__stat(name_buffer, &pipe_stat) == -1) {
     uv__free(name_buffer);
     return -errno;
   }
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/poll.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/poll.cpp
index 7364731..c21722b 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/poll.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/poll.cpp
@@ -125,7 +125,7 @@
                       UV_PRIORITIZED)) == 0);
   assert(!uv__is_closing(handle));
 
-  watchers = handle->loop->watchers;
+  watchers = (void**)handle->loop->watchers;
   w = &handle->io_watcher;
 
   if (uv__fd_exists(handle->loop, w->fd))
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-hrtime.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-hrtime.cpp
index 323dfc2..7b45c01 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-hrtime.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-hrtime.cpp
@@ -23,13 +23,14 @@
 #include "internal.h"
 
 #include <stdint.h>
+#include <stdlib.h>
 #include <time.h>
 
-#undef NANOSEC
-#define NANOSEC ((uint64_t) 1e9)
-
 uint64_t uv__hrtime(uv_clocktype_t type) {
-  struct timespec ts;
-  clock_gettime(CLOCK_MONOTONIC, &ts);
-  return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
+  struct timespec t;
+
+  if (clock_gettime(CLOCK_MONOTONIC, &t))
+    abort();
+
+  return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
 }
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-poll.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-poll.cpp
index 8da038d..b71eee3 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-poll.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/posix-poll.cpp
@@ -132,11 +132,12 @@
 
 
 void uv__io_poll(uv_loop_t* loop, int timeout) {
+  uv__loop_internal_fields_t* lfields;
   sigset_t* pset;
   sigset_t set;
   uint64_t time_base;
   uint64_t time_diff;
-  QUEUE* q;
+  struct uv__queue* q;
   uv__io_t* w;
   size_t i;
   unsigned int nevents;
@@ -148,17 +149,19 @@
   int reset_timeout;
 
   if (loop->nfds == 0) {
-    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    assert(uv__queue_empty(&loop->watcher_queue));
     return;
   }
 
-  /* Take queued watchers and add their fds to our poll fds array.  */
-  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
-    q = QUEUE_HEAD(&loop->watcher_queue);
-    QUEUE_REMOVE(q);
-    QUEUE_INIT(q);
+  lfields = uv__get_internal_fields(loop);
 
-    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+  /* Take queued watchers and add their fds to our poll fds array.  */
+  while (!uv__queue_empty(&loop->watcher_queue)) {
+    q = uv__queue_head(&loop->watcher_queue);
+    uv__queue_remove(q);
+    uv__queue_init(q);
+
+    w = uv__queue_data(q, uv__io_t, watcher_queue);
     assert(w->pevents != 0);
     assert(w->fd >= 0);
     assert(w->fd < (int) loop->nwatchers);
@@ -179,7 +182,7 @@
   assert(timeout >= -1);
   time_base = loop->time;
 
-  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+  if (lfields->flags & UV_METRICS_IDLE_TIME) {
     reset_timeout = 1;
     user_timeout = timeout;
     timeout = 0;
@@ -198,6 +201,12 @@
     if (timeout != 0)
       uv__metrics_set_provider_entry_time(loop);
 
+    /* Store the current timeout in a location that's globally accessible so
+     * other locations like uv__work_done() can determine whether the queue
+     * of events in the callback were waiting when poll was called.
+     */
+    lfields->current_timeout = timeout;
+
     if (pset != NULL)
       if (pthread_sigmask(SIG_BLOCK, pset, NULL))
         abort();
@@ -292,9 +301,11 @@
       }
     }
 
+    uv__metrics_inc_events(loop, nevents);
     if (reset_timeout != 0) {
       timeout = user_timeout;
       reset_timeout = 0;
+      uv__metrics_inc_events_waiting(loop, nevents);
     }
 
     if (have_signals != 0) {
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/process.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/process.cpp
index 0916aa4..2d622c9 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/process.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/process.cpp
@@ -55,7 +55,7 @@
 extern char **environ;
 #endif
 
-#if defined(__linux__) || defined(__GLIBC__)
+#if defined(__linux__)
 # include <grp.h>
 #endif
 
@@ -79,8 +79,28 @@
   assert(signum == SIGCHLD);
   uv__wait_children(handle->loop);
 }
+
+
+int uv__process_init(uv_loop_t* loop) {
+  int err;
+
+  err = uv_signal_init(loop, &loop->child_watcher);
+  if (err)
+    return err;
+  uv__handle_unref(&loop->child_watcher);
+  loop->child_watcher.flags |= UV_HANDLE_INTERNAL;
+  return 0;
+}
+
+
+#else
+int uv__process_init(uv_loop_t* loop) {
+  memset(&loop->child_watcher, 0, sizeof(loop->child_watcher));
+  return 0;
+}
 #endif
 
+
 void uv__wait_children(uv_loop_t* loop) {
   uv_process_t* process;
   int exit_status;
@@ -88,23 +108,24 @@
   int status;
   int options;
   pid_t pid;
-  QUEUE pending;
-  QUEUE* q;
-  QUEUE* h;
+  struct uv__queue pending;
+  struct uv__queue* q;
+  struct uv__queue* h;
 
-  QUEUE_INIT(&pending);
+  uv__queue_init(&pending);
 
   h = &loop->process_handles;
-  q = QUEUE_HEAD(h);
+  q = uv__queue_head(h);
   while (q != h) {
-    process = QUEUE_DATA(q, uv_process_t, queue);
-    q = QUEUE_NEXT(q);
+    process = uv__queue_data(q, uv_process_t, queue);
+    q = uv__queue_next(q);
 
 #ifndef UV_USE_SIGCHLD
     if ((process->flags & UV_HANDLE_REAP) == 0)
       continue;
     options = 0;
     process->flags &= ~UV_HANDLE_REAP;
+    loop->nfds--;
 #else
     options = WNOHANG;
 #endif
@@ -128,18 +149,18 @@
 
     assert(pid == process->pid);
     process->status = status;
-    QUEUE_REMOVE(&process->queue);
-    QUEUE_INSERT_TAIL(&pending, &process->queue);
+    uv__queue_remove(&process->queue);
+    uv__queue_insert_tail(&pending, &process->queue);
   }
 
   h = &pending;
-  q = QUEUE_HEAD(h);
+  q = uv__queue_head(h);
   while (q != h) {
-    process = QUEUE_DATA(q, uv_process_t, queue);
-    q = QUEUE_NEXT(q);
+    process = uv__queue_data(q, uv_process_t, queue);
+    q = uv__queue_next(q);
 
-    QUEUE_REMOVE(&process->queue);
-    QUEUE_INIT(&process->queue);
+    uv__queue_remove(&process->queue);
+    uv__queue_init(&process->queue);
     uv__handle_stop(process);
 
     if (process->exit_cb == NULL)
@@ -155,13 +176,18 @@
 
     process->exit_cb(process, exit_status, term_signal);
   }
-  assert(QUEUE_EMPTY(&pending));
+  assert(uv__queue_empty(&pending));
 }
 
 /*
  * Used for initializing stdio streams like options.stdin_stream. Returns
  * zero on success. See also the cleanup section in uv_spawn().
  */
+#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH))
+/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be
+ * avoided. Since this isn't called on those targets, the function
+ * doesn't even need to be defined for them.
+ */
 static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
   int mask;
   int fd;
@@ -248,11 +274,6 @@
 }
 
 
-#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH))
-/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be
- * avoided. Since this isn't called on those targets, the function
- * doesn't even need to be defined for them.
- */
 static void uv__process_child_init(const uv_process_options_t* options,
                                    int stdio_count,
                                    int (*pipes)[2],
@@ -384,7 +405,6 @@
 
   uv__write_errno(error_fd);
 }
-#endif
 
 
 #if defined(__APPLE__)
@@ -665,7 +685,7 @@
   if (options->file == NULL)
     return ENOENT;
 
-  /* The environment for the child process is that of the parent unless overriden
+  /* The environment for the child process is that of the parent unless overridden
    * by options->env */
   char** env = environ;
   if (options->env != NULL)
@@ -931,6 +951,7 @@
 
   return err;
 }
+#endif /* ISN'T TARGET_OS_TV || TARGET_OS_WATCH */
 
 int uv_spawn(uv_loop_t* loop,
              uv_process_t* process,
@@ -957,7 +978,7 @@
                               UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
 
   uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
-  QUEUE_INIT(&process->queue);
+  uv__queue_init(&process->queue);
   process->status = 0;
 
   stdio_count = options->stdio_count;
@@ -1012,11 +1033,15 @@
       process->flags |= UV_HANDLE_REAP;
       loop->flags |= UV_LOOP_REAP_CHILDREN;
     }
+    /* This prevents uv__io_poll() from bailing out prematurely, being unaware
+     * that we added an event here for it to react to. We will decrement this
+     * again after the waitpid call succeeds. */
+    loop->nfds++;
 #endif
 
     process->pid = pid;
     process->exit_cb = options->exit_cb;
-    QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue);
+    uv__queue_insert_tail(&loop->process_handles, &process->queue);
     uv__handle_start(process);
   }
 
@@ -1078,8 +1103,10 @@
 
 
 void uv__process_close(uv_process_t* handle) {
-  QUEUE_REMOVE(&handle->queue);
+  uv__queue_remove(&handle->queue);
   uv__handle_stop(handle);
-  if (QUEUE_EMPTY(&handle->loop->process_handles))
+#ifdef UV_USE_SIGCHLD
+  if (uv__queue_empty(&handle->loop->process_handles))
     uv_signal_stop(&handle->loop->child_watcher);
+#endif
 }
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/pthread-fixes.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/pthread-fixes.cpp
deleted file mode 100644
index 022d79c..0000000
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/pthread-fixes.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright (c) 2013, Sony Mobile Communications AB
- * Copyright (c) 2012, Google Inc.
-   All rights reserved.
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-     * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-       * 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.
-       * Neither the name of Google Inc. nor the names of its
-   contributors may be used to endorse or promote products derived from
-   this software without specific prior written permission.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS 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 COPYRIGHT
-   OWNER OR 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.
-*/
-
-/* Android versions < 4.1 have a broken pthread_sigmask. */
-#include "uv-common.h"
-
-#include <errno.h>
-#include <pthread.h>
-#include <signal.h>
-
-int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset) {
-  static int workaround;
-  int err;
-
-  if (uv__load_relaxed(&workaround)) {
-    return sigprocmask(how, set, oset);
-  } else {
-    err = pthread_sigmask(how, set, oset);
-    if (err) {
-      if (err == EINVAL && sigprocmask(how, set, oset) == 0) {
-        uv__store_relaxed(&workaround, 1);
-        return 0;
-      } else {
-        return -1;
-      }
-    }
-  }
-
-  return 0;
-}
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/random-devurandom.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/random-devurandom.cpp
index 05e52a5..d6336f2 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/random-devurandom.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/random-devurandom.cpp
@@ -40,7 +40,7 @@
   if (fd < 0)
     return fd;
 
-  if (fstat(fd, &s)) {
+  if (uv__fstat(fd, &s)) {
     uv__close(fd);
     return UV__ERR(errno);
   }
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/random-getrandom.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/random-getrandom.cpp
index bcc9408..054eccf 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/random-getrandom.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/random-getrandom.cpp
@@ -24,8 +24,6 @@
 
 #ifdef __linux__
 
-#include "linux-syscalls.h"
-
 #define uv__random_getrandom_init() 0
 
 #else  /* !__linux__ */
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/signal.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/signal.cpp
index 1133c73..63aba5a 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/signal.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/signal.cpp
@@ -279,6 +279,8 @@
 
 
 int uv__signal_loop_fork(uv_loop_t* loop) {
+  if (loop->signal_pipefd[0] == -1)
+    return 0;
   uv__io_stop(loop, &loop->signal_io_watcher, POLLIN);
   uv__close(loop->signal_pipefd[0]);
   uv__close(loop->signal_pipefd[1]);
@@ -289,16 +291,16 @@
 
 
 void uv__signal_loop_cleanup(uv_loop_t* loop) {
-  QUEUE* q;
+  struct uv__queue* q;
 
   /* Stop all the signal watchers that are still attached to this loop. This
    * ensures that the (shared) signal tree doesn't contain any invalid entries
    * entries, and that signal handlers are removed when appropriate.
-   * It's safe to use QUEUE_FOREACH here because the handles and the handle
+   * It's safe to use uv__queue_foreach here because the handles and the handle
    * queue are not modified by uv__signal_stop().
    */
-  QUEUE_FOREACH(q, &loop->handle_queue) {
-    uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue);
+  uv__queue_foreach(q, &loop->handle_queue) {
+    uv_handle_t* handle = uv__queue_data(q, uv_handle_t, handle_queue);
 
     if (handle->type == UV_SIGNAL)
       uv__signal_stop((uv_signal_t*) handle);
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/spinlock.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/spinlock.h
deleted file mode 100644
index a20c83c..0000000
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/spinlock.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef UV_SPINLOCK_H_
-#define UV_SPINLOCK_H_
-
-#include "internal.h"  /* ACCESS_ONCE, UV_UNUSED */
-#include "atomic-ops.h"
-
-#define UV_SPINLOCK_INITIALIZER { 0 }
-
-typedef struct {
-  int lock;
-} uv_spinlock_t;
-
-UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock));
-UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock));
-UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock));
-UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock));
-
-UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)) {
-  ACCESS_ONCE(int, spinlock->lock) = 0;
-}
-
-UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)) {
-  while (!uv_spinlock_trylock(spinlock)) cpu_relax();
-}
-
-UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)) {
-  ACCESS_ONCE(int, spinlock->lock) = 0;
-}
-
-UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)) {
-  /* TODO(bnoordhuis) Maybe change to a ticket lock to guarantee fair queueing.
-   * Not really critical until we have locks that are (frequently) contended
-   * for by several threads.
-   */
-  return 0 == cmpxchgi(&spinlock->lock, 0, 1);
-}
-
-#endif  /* UV_SPINLOCK_H_ */
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/stream.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/stream.cpp
index fa25812..265ddad 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/stream.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/stream.cpp
@@ -60,6 +60,16 @@
 };
 #endif /* defined(__APPLE__) */
 
+union uv__cmsg {
+  struct cmsghdr hdr;
+  /* This cannot be larger because of the IBMi PASE limitation that
+   * the total size of control messages cannot exceed 256 bytes.
+   */
+  char pad[256];
+};
+
+STATIC_ASSERT(256 == sizeof(union uv__cmsg));
+
 static void uv__stream_connect(uv_stream_t*);
 static void uv__write(uv_stream_t* stream);
 static void uv__read(uv_stream_t* stream);
@@ -84,8 +94,8 @@
   stream->accepted_fd = -1;
   stream->queued_fds = NULL;
   stream->delayed_error = 0;
-  QUEUE_INIT(&stream->write_queue);
-  QUEUE_INIT(&stream->write_completed_queue);
+  uv__queue_init(&stream->write_queue);
+  uv__queue_init(&stream->write_completed_queue);
   stream->write_queue_size = 0;
 
   if (loop->emfile_fd == -1) {
@@ -429,15 +439,15 @@
 
 void uv__stream_flush_write_queue(uv_stream_t* stream, int error) {
   uv_write_t* req;
-  QUEUE* q;
-  while (!QUEUE_EMPTY(&stream->write_queue)) {
-    q = QUEUE_HEAD(&stream->write_queue);
-    QUEUE_REMOVE(q);
+  struct uv__queue* q;
+  while (!uv__queue_empty(&stream->write_queue)) {
+    q = uv__queue_head(&stream->write_queue);
+    uv__queue_remove(q);
 
-    req = QUEUE_DATA(q, uv_write_t, queue);
+    req = uv__queue_data(q, uv_write_t, queue);
     req->error = error;
 
-    QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue);
+    uv__queue_insert_tail(&stream->write_completed_queue, &req->queue);
   }
 }
 
@@ -495,76 +505,34 @@
 }
 
 
-#if defined(UV_HAVE_KQUEUE)
-# define UV_DEC_BACKLOG(w) w->rcount--;
-#else
-# define UV_DEC_BACKLOG(w) /* no-op */
-#endif /* defined(UV_HAVE_KQUEUE) */
-
-
 void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
   uv_stream_t* stream;
   int err;
+  int fd;
 
   stream = container_of(w, uv_stream_t, io_watcher);
   assert(events & POLLIN);
   assert(stream->accepted_fd == -1);
   assert(!(stream->flags & UV_HANDLE_CLOSING));
 
-  uv__io_start(stream->loop, &stream->io_watcher, POLLIN);
+  fd = uv__stream_fd(stream);
+  err = uv__accept(fd);
 
-  /* connection_cb can close the server socket while we're
-   * in the loop so check it on each iteration.
-   */
-  while (uv__stream_fd(stream) != -1) {
-    assert(stream->accepted_fd == -1);
+  if (err == UV_EMFILE || err == UV_ENFILE)
+    err = uv__emfile_trick(loop, fd);  /* Shed load. */
 
-#if defined(UV_HAVE_KQUEUE)
-    if (w->rcount <= 0)
-      return;
-#endif /* defined(UV_HAVE_KQUEUE) */
+  if (err < 0)
+    return;
 
-    err = uv__accept(uv__stream_fd(stream));
-    if (err < 0) {
-      if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))
-        return;  /* Not an error. */
+  stream->accepted_fd = err;
+  stream->connection_cb(stream, 0);
 
-      if (err == UV_ECONNABORTED)
-        continue;  /* Ignore. Nothing we can do about that. */
-
-      if (err == UV_EMFILE || err == UV_ENFILE) {
-        err = uv__emfile_trick(loop, uv__stream_fd(stream));
-        if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))
-          break;
-      }
-
-      stream->connection_cb(stream, err);
-      continue;
-    }
-
-    UV_DEC_BACKLOG(w)
-    stream->accepted_fd = err;
-    stream->connection_cb(stream, 0);
-
-    if (stream->accepted_fd != -1) {
-      /* The user hasn't yet accepted called uv_accept() */
-      uv__io_stop(loop, &stream->io_watcher, POLLIN);
-      return;
-    }
-
-    if (stream->type == UV_TCP &&
-        (stream->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) {
-      /* Give other processes a chance to accept connections. */
-      struct timespec timeout = { 0, 1 };
-      nanosleep(&timeout, NULL);
-    }
-  }
+  if (stream->accepted_fd != -1)
+    /* The user hasn't yet accepted called uv_accept() */
+    uv__io_stop(loop, &stream->io_watcher, POLLIN);
 }
 
 
-#undef UV_DEC_BACKLOG
-
-
 int uv_accept(uv_stream_t* server, uv_stream_t* client) {
   int err;
 
@@ -659,13 +627,13 @@
   uv_shutdown_t* req;
   int err;
 
-  assert(QUEUE_EMPTY(&stream->write_queue));
+  assert(uv__queue_empty(&stream->write_queue));
   if (!(stream->flags & UV_HANDLE_CLOSING)) {
     uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
     uv__stream_osx_interrupt_select(stream);
   }
 
-  if (!(stream->flags & UV_HANDLE_SHUTTING))
+  if (!uv__is_stream_shutting(stream))
     return;
 
   req = stream->shutdown_req;
@@ -674,7 +642,6 @@
   if ((stream->flags & UV_HANDLE_CLOSING) ||
       !(stream->flags & UV_HANDLE_SHUT)) {
     stream->shutdown_req = NULL;
-    stream->flags &= ~UV_HANDLE_SHUTTING;
     uv__req_unregister(stream->loop, req);
 
     err = 0;
@@ -747,7 +714,7 @@
   uv_stream_t* stream = req->handle;
 
   /* Pop the req off tcp->write_queue. */
-  QUEUE_REMOVE(&req->queue);
+  uv__queue_remove(&req->queue);
 
   /* Only free when there was no error. On error, we touch up write_queue_size
    * right before making the callback. The reason we don't do that right away
@@ -764,7 +731,7 @@
   /* Add it to the write_completed_queue where it will have its
    * callback called in the near future.
    */
-  QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue);
+  uv__queue_insert_tail(&stream->write_completed_queue, &req->queue);
   uv__io_feed(stream->loop, &stream->io_watcher);
 }
 
@@ -812,18 +779,14 @@
   if (send_handle != NULL) {
     int fd_to_send;
     struct msghdr msg;
-    struct cmsghdr *cmsg;
-    union {
-      char data[64];
-      struct cmsghdr alias;
-    } scratch;
+    union uv__cmsg cmsg;
 
     if (uv__is_closing(send_handle))
       return UV_EBADF;
 
     fd_to_send = uv__handle_fd((uv_handle_t*) send_handle);
 
-    memset(&scratch, 0, sizeof(scratch));
+    memset(&cmsg, 0, sizeof(cmsg));
 
     assert(fd_to_send >= 0);
 
@@ -833,20 +796,13 @@
     msg.msg_iovlen = iovcnt;
     msg.msg_flags = 0;
 
-    msg.msg_control = &scratch.alias;
+    msg.msg_control = &cmsg.hdr;
     msg.msg_controllen = CMSG_SPACE(sizeof(fd_to_send));
 
-    cmsg = CMSG_FIRSTHDR(&msg);
-    cmsg->cmsg_level = SOL_SOCKET;
-    cmsg->cmsg_type = SCM_RIGHTS;
-    cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send));
-
-    /* silence aliasing warning */
-    {
-      void* pv = CMSG_DATA(cmsg);
-      int* pi = (int*)pv;
-      *pi = fd_to_send;
-    }
+    cmsg.hdr.cmsg_level = SOL_SOCKET;
+    cmsg.hdr.cmsg_type = SCM_RIGHTS;
+    cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(fd_to_send));
+    memcpy(CMSG_DATA(&cmsg.hdr), &fd_to_send, sizeof(fd_to_send));
 
     do
       n = sendmsg(uv__stream_fd(stream), &msg, 0);
@@ -881,18 +837,25 @@
 }
 
 static void uv__write(uv_stream_t* stream) {
-  QUEUE* q;
+  struct uv__queue* q;
   uv_write_t* req;
   ssize_t n;
+  int count;
 
   assert(uv__stream_fd(stream) >= 0);
 
+  /* Prevent loop starvation when the consumer of this stream read as fast as
+   * (or faster than) we can write it. This `count` mechanism does not need to
+   * change even if we switch to edge-triggered I/O.
+   */
+  count = 32;
+
   for (;;) {
-    if (QUEUE_EMPTY(&stream->write_queue))
+    if (uv__queue_empty(&stream->write_queue))
       return;
 
-    q = QUEUE_HEAD(&stream->write_queue);
-    req = QUEUE_DATA(q, uv_write_t, queue);
+    q = uv__queue_head(&stream->write_queue);
+    req = uv__queue_data(q, uv_write_t, queue);
     assert(req->handle == stream);
 
     n = uv__try_write(stream,
@@ -905,10 +868,13 @@
       req->send_handle = NULL;
       if (uv__write_req_update(stream, req, n)) {
         uv__write_req_finish(req);
-        return;  /* TODO(bnoordhuis) Start trying to write the next request. */
+        if (count-- > 0)
+          continue; /* Start trying to write the next request. */
+
+        return;
       }
     } else if (n != UV_EAGAIN)
-      break;
+      goto error;
 
     /* If this is a blocking stream, try again. */
     if (stream->flags & UV_HANDLE_BLOCKING_WRITES)
@@ -923,6 +889,7 @@
     return;
   }
 
+error:
   req->error = n;
   uv__write_req_finish(req);
   uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
@@ -932,28 +899,19 @@
 
 static void uv__write_callbacks(uv_stream_t* stream) {
   uv_write_t* req;
-  QUEUE* q;
-  QUEUE pq;
+  struct uv__queue* q;
+  struct uv__queue pq;
 
-  if (QUEUE_EMPTY(&stream->write_completed_queue))
+  if (uv__queue_empty(&stream->write_completed_queue))
     return;
 
-// FIXME: GCC 12.1 gives a possibly real warning, but we don't know how to fix
-// it
-#if __GNUC__ >= 12
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdangling-pointer="
-#endif  // __GNUC__ >= 12
-  QUEUE_MOVE(&stream->write_completed_queue, &pq);
-#if __GNUC__ >= 12
-#pragma GCC diagnostic pop
-#endif  // __GNUC__ >= 12
+  uv__queue_move(&stream->write_completed_queue, &pq);
 
-  while (!QUEUE_EMPTY(&pq)) {
+  while (!uv__queue_empty(&pq)) {
     /* Pop a req off write_completed_queue. */
-    q = QUEUE_HEAD(&pq);
-    req = QUEUE_DATA(q, uv_write_t, queue);
-    QUEUE_REMOVE(q);
+    q = uv__queue_head(&pq);
+    req = uv__queue_data(q, uv_write_t, queue);
+    uv__queue_remove(q);
     uv__req_unregister(stream->loop, req);
 
     if (req->bufs != NULL) {
@@ -1020,57 +978,38 @@
 }
 
 
-#if defined(__PASE__)
-/* on IBMi PASE the control message length can not exceed 256. */
-# define UV__CMSG_FD_COUNT 60
-#else
-# define UV__CMSG_FD_COUNT 64
-#endif
-#define UV__CMSG_FD_SIZE (UV__CMSG_FD_COUNT * sizeof(int))
-
-
 static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
   struct cmsghdr* cmsg;
+  int fd;
+  int err;
+  size_t i;
+  size_t count;
 
   for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
-    char* start;
-    char* end;
-    int err;
-    void* pv;
-    int* pi;
-    unsigned int i;
-    unsigned int count;
-
     if (cmsg->cmsg_type != SCM_RIGHTS) {
       fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n",
           cmsg->cmsg_type);
       continue;
     }
 
-    /* silence aliasing warning */
-    pv = CMSG_DATA(cmsg);
-    pi = (int*)pv;
-
-    /* Count available fds */
-    start = (char*) cmsg;
-    end = (char*) cmsg + cmsg->cmsg_len;
-    count = 0;
-    while (start + CMSG_LEN(count * sizeof(*pi)) < end)
-      count++;
-    assert(start + CMSG_LEN(count * sizeof(*pi)) == end);
+    assert(cmsg->cmsg_len >= CMSG_LEN(0));
+    count = cmsg->cmsg_len - CMSG_LEN(0);
+    assert(count % sizeof(fd) == 0);
+    count /= sizeof(fd);
 
     for (i = 0; i < count; i++) {
+      memcpy(&fd, (char*) CMSG_DATA(cmsg) + i * sizeof(fd), sizeof(fd));
       /* Already has accepted fd, queue now */
       if (stream->accepted_fd != -1) {
-        err = uv__stream_queue_fd(stream, pi[i]);
+        err = uv__stream_queue_fd(stream, fd);
         if (err != 0) {
           /* Close rest */
           for (; i < count; i++)
-            uv__close(pi[i]);
+            uv__close(fd);
           return err;
         }
       } else {
-        stream->accepted_fd = pi[i];
+        stream->accepted_fd = fd;
       }
     }
   }
@@ -1079,17 +1018,11 @@
 }
 
 
-#ifdef __clang__
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wgnu-folding-constant"
-# pragma clang diagnostic ignored "-Wvla-extension"
-#endif
-
 static void uv__read(uv_stream_t* stream) {
   uv_buf_t buf;
   ssize_t nread;
   struct msghdr msg;
-  char cmsg_space[CMSG_SPACE(UV__CMSG_FD_SIZE)];
+  union uv__cmsg cmsg;
   int count;
   int err;
   int is_ipc;
@@ -1135,8 +1068,8 @@
       msg.msg_name = NULL;
       msg.msg_namelen = 0;
       /* Set up to receive a descriptor even if one isn't in the message */
-      msg.msg_controllen = sizeof(cmsg_space);
-      msg.msg_control = cmsg_space;
+      msg.msg_controllen = sizeof(cmsg);
+      msg.msg_control = &cmsg.hdr;
 
       do {
         nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0);
@@ -1220,14 +1153,6 @@
 }
 
 
-#ifdef __clang__
-# pragma clang diagnostic pop
-#endif
-
-#undef UV__CMSG_FD_COUNT
-#undef UV__CMSG_FD_SIZE
-
-
 int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
   assert(stream->type == UV_TCP ||
          stream->type == UV_TTY ||
@@ -1235,7 +1160,7 @@
 
   if (!(stream->flags & UV_HANDLE_WRITABLE) ||
       stream->flags & UV_HANDLE_SHUT ||
-      stream->flags & UV_HANDLE_SHUTTING ||
+      uv__is_stream_shutting(stream) ||
       uv__is_closing(stream)) {
     return UV_ENOTCONN;
   }
@@ -1248,10 +1173,9 @@
   req->handle = stream;
   req->cb = cb;
   stream->shutdown_req = req;
-  stream->flags |= UV_HANDLE_SHUTTING;
   stream->flags &= ~UV_HANDLE_WRITABLE;
 
-  if (QUEUE_EMPTY(&stream->write_queue))
+  if (uv__queue_empty(&stream->write_queue))
     uv__io_feed(stream->loop, &stream->io_watcher);
 
   return 0;
@@ -1304,7 +1228,7 @@
     uv__write_callbacks(stream);
 
     /* Write queue drained. */
-    if (QUEUE_EMPTY(&stream->write_queue))
+    if (uv__queue_empty(&stream->write_queue))
       uv__drain(stream);
   }
 }
@@ -1347,7 +1271,7 @@
   stream->connect_req = NULL;
   uv__req_unregister(stream->loop, req);
 
-  if (error < 0 || QUEUE_EMPTY(&stream->write_queue)) {
+  if (error < 0 || uv__queue_empty(&stream->write_queue)) {
     uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
   }
 
@@ -1429,7 +1353,7 @@
   req->handle = stream;
   req->error = 0;
   req->send_handle = send_handle;
-  QUEUE_INIT(&req->queue);
+  uv__queue_init(&req->queue);
 
   req->bufs = req->bufsml;
   if (nbufs > ARRAY_SIZE(req->bufsml))
@@ -1444,7 +1368,7 @@
   stream->write_queue_size += uv__count_bufs(bufs, nbufs);
 
   /* Append the request to write_queue. */
-  QUEUE_INSERT_TAIL(&stream->write_queue, &req->queue);
+  uv__queue_insert_tail(&stream->write_queue, &req->queue);
 
   /* If the queue was empty when this function began, we should attempt to
    * do the write immediately. Otherwise start the write_watcher and wait
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/tcp.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/tcp.cpp
index 73fc657..d6c848f 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/tcp.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/tcp.cpp
@@ -28,16 +28,39 @@
 #include <errno.h>
 
 
-static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {
-  struct sockaddr_storage saddr;
+static int maybe_bind_socket(int fd) {
+  union uv__sockaddr s;
   socklen_t slen;
+
+  slen = sizeof(s);
+  memset(&s, 0, sizeof(s));
+
+  if (getsockname(fd, &s.addr, &slen))
+    return UV__ERR(errno);
+
+  if (s.addr.sa_family == AF_INET)
+    if (s.in.sin_port != 0)
+      return 0;  /* Already bound to a port. */
+
+  if (s.addr.sa_family == AF_INET6)
+    if (s.in6.sin6_port != 0)
+      return 0;  /* Already bound to a port. */
+
+  /* Bind to an arbitrary port. */
+  if (bind(fd, &s.addr, slen))
+    return UV__ERR(errno);
+
+  return 0;
+}
+
+
+static int new_socket(uv_tcp_t* handle, int domain, unsigned int flags) {
   int sockfd;
   int err;
 
-  err = uv__socket(domain, SOCK_STREAM, 0);
-  if (err < 0)
-    return err;
-  sockfd = err;
+  sockfd = uv__socket(domain, SOCK_STREAM, 0);
+  if (sockfd < 0)
+    return sockfd;
 
   err = uv__stream_open((uv_stream_t*) handle, sockfd, flags);
   if (err) {
@@ -45,74 +68,44 @@
     return err;
   }
 
-  if (flags & UV_HANDLE_BOUND) {
-    /* Bind this new socket to an arbitrary port */
-    slen = sizeof(saddr);
-    memset(&saddr, 0, sizeof(saddr));
-    if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) {
-      uv__close(sockfd);
-      return UV__ERR(errno);
-    }
-
-    if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) {
-      uv__close(sockfd);
-      return UV__ERR(errno);
-    }
-  }
+  if (flags & UV_HANDLE_BOUND)
+    return maybe_bind_socket(sockfd);
 
   return 0;
 }
 
 
-static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {
-  struct sockaddr_storage saddr;
-  socklen_t slen;
+static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned int flags) {
+  int sockfd;
+  int err;
 
-  if (domain == AF_UNSPEC) {
-    handle->flags |= flags;
-    return 0;
-  }
+  if (domain == AF_UNSPEC)
+    goto out;
 
-  if (uv__stream_fd(handle) != -1) {
+  sockfd = uv__stream_fd(handle);
+  if (sockfd == -1)
+    return new_socket(handle, domain, flags);
 
-    if (flags & UV_HANDLE_BOUND) {
+  if (!(flags & UV_HANDLE_BOUND))
+    goto out;
 
-      if (handle->flags & UV_HANDLE_BOUND) {
-        /* It is already bound to a port. */
-        handle->flags |= flags;
-        return 0;
-      }
+  if (handle->flags & UV_HANDLE_BOUND)
+    goto out;  /* Already bound to a port. */
 
-      /* Query to see if tcp socket is bound. */
-      slen = sizeof(saddr);
-      memset(&saddr, 0, sizeof(saddr));
-      if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen))
-        return UV__ERR(errno);
+  err = maybe_bind_socket(sockfd);
+  if (err)
+    return err;
 
-      if ((saddr.ss_family == AF_INET6 &&
-          ((struct sockaddr_in6*) &saddr)->sin6_port != 0) ||
-          (saddr.ss_family == AF_INET &&
-          ((struct sockaddr_in*) &saddr)->sin_port != 0)) {
-        /* Handle is already bound to a port. */
-        handle->flags |= flags;
-        return 0;
-      }
+out:
 
-      /* Bind to arbitrary port */
-      if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen))
-        return UV__ERR(errno);
-    }
-
-    handle->flags |= flags;
-    return 0;
-  }
-
-  return new_socket(handle, domain, flags);
+  handle->flags |= flags;
+  return 0;
 }
 
 
 int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) {
   int domain;
+  int err;
 
   /* Use the lower 8 bits for the domain */
   domain = flags & 0xFF;
@@ -129,9 +122,12 @@
    */
 
   if (domain != AF_UNSPEC) {
-    int err = maybe_new_socket(tcp, domain, 0);
+    err = new_socket(tcp, domain, 0);
     if (err) {
-      QUEUE_REMOVE(&tcp->handle_queue);
+      uv__queue_remove(&tcp->handle_queue);
+      if (tcp->io_watcher.fd != -1)
+        uv__close(tcp->io_watcher.fd);
+      tcp->io_watcher.fd = -1;
       return err;
     }
   }
@@ -256,7 +252,7 @@
   uv__req_init(handle->loop, req, UV_CONNECT);
   req->cb = cb;
   req->handle = (uv_stream_t*) handle;
-  QUEUE_INIT(&req->queue);
+  uv__queue_init(&req->queue);
   handle->connect_req = req;
 
   uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
@@ -317,7 +313,7 @@
   struct linger l = { 1, 0 };
 
   /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */
-  if (handle->flags & UV_HANDLE_SHUTTING)
+  if (uv__is_stream_shutting(handle))
     return UV_EINVAL;
 
   fd = uv__stream_fd(handle);
@@ -338,24 +334,12 @@
 
 
 int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
-  static int single_accept_cached = -1;
-  unsigned long flags;
-  int single_accept;
+  unsigned int flags;
   int err;
 
   if (tcp->delayed_error)
     return tcp->delayed_error;
 
-  single_accept = uv__load_relaxed(&single_accept_cached);
-  if (single_accept == -1) {
-    const char* val = getenv("UV_TCP_SINGLE_ACCEPT");
-    single_accept = (val != NULL && atoi(val) != 0);  /* Off by default. */
-    uv__store_relaxed(&single_accept_cached, single_accept);
-  }
-
-  if (single_accept)
-    tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
-
   flags = 0;
 #if defined(__MVS__)
   /* on zOS the listen call does not bind automatically
@@ -460,10 +444,6 @@
 
 
 int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
-  if (enable)
-    handle->flags &= ~UV_HANDLE_TCP_SINGLE_ACCEPT;
-  else
-    handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
   return 0;
 }
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/thread.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/thread.cpp
index 392a071..f860094 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/thread.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/thread.cpp
@@ -41,126 +41,20 @@
 #include <gnu/libc-version.h>  /* gnu_get_libc_version() */
 #endif
 
+#if defined(__linux__)
+# include <sched.h>
+# define uv__cpu_set_t cpu_set_t
+#elif defined(__FreeBSD__)
+# include <sys/param.h>
+# include <sys/cpuset.h>
+# include <pthread_np.h>
+# define uv__cpu_set_t cpuset_t
+#endif
+
+
 #undef NANOSEC
 #define NANOSEC ((uint64_t) 1e9)
 
-#if defined(PTHREAD_BARRIER_SERIAL_THREAD)
-STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t));
-#endif
-
-/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */
-#if defined(_AIX) || \
-    defined(__OpenBSD__) || \
-    !defined(PTHREAD_BARRIER_SERIAL_THREAD)
-int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
-  struct _uv_barrier* b;
-  int rc;
-
-  if (barrier == NULL || count == 0)
-    return UV_EINVAL;
-
-  b = (_uv_barrier*)uv__malloc(sizeof(*b));
-  if (b == NULL)
-    return UV_ENOMEM;
-
-  b->in = 0;
-  b->out = 0;
-  b->threshold = count;
-
-  rc = uv_mutex_init(&b->mutex);
-  if (rc != 0)
-    goto error2;
-
-  rc = uv_cond_init(&b->cond);
-  if (rc != 0)
-    goto error;
-
-  barrier->b = b;
-  return 0;
-
-error:
-  uv_mutex_destroy(&b->mutex);
-error2:
-  uv__free(b);
-  return rc;
-}
-
-int uv_barrier_wait(uv_barrier_t* barrier) {
-  struct _uv_barrier* b;
-  int last;
-
-  if (barrier == NULL || barrier->b == NULL)
-    return UV_EINVAL;
-
-  b = barrier->b;
-  /* Lock the mutex*/
-  uv_mutex_lock(&b->mutex);
-
-  if (++b->in == b->threshold) {
-    b->in = 0;
-    b->out = b->threshold;
-    uv_cond_signal(&b->cond);
-  } else {
-    do
-      uv_cond_wait(&b->cond, &b->mutex);
-    while (b->in != 0);
-  }
-
-  last = (--b->out == 0);
-  uv_cond_signal(&b->cond);
-
-  uv_mutex_unlock(&b->mutex);
-  return last;
-}
-
-void uv_barrier_destroy(uv_barrier_t* barrier) {
-  struct _uv_barrier* b;
-
-  b = barrier->b;
-  uv_mutex_lock(&b->mutex);
-
-  assert(b->in == 0);
-  while (b->out != 0)
-    uv_cond_wait(&b->cond, &b->mutex);
-
-  if (b->in != 0)
-    abort();
-
-  uv_mutex_unlock(&b->mutex);
-  uv_mutex_destroy(&b->mutex);
-  uv_cond_destroy(&b->cond);
-
-  uv__free(barrier->b);
-  barrier->b = NULL;
-}
-
-#else
-
-int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
-  return UV__ERR(pthread_barrier_init(barrier, NULL, count));
-}
-
-
-int uv_barrier_wait(uv_barrier_t* barrier) {
-  int rc;
-
-  rc = pthread_barrier_wait(barrier);
-  if (rc != 0)
-    if (rc != PTHREAD_BARRIER_SERIAL_THREAD)
-      abort();
-
-  return rc == PTHREAD_BARRIER_SERIAL_THREAD;
-}
-
-
-void uv_barrier_destroy(uv_barrier_t* barrier) {
-  if (pthread_barrier_destroy(barrier))
-    abort();
-}
-
-#endif
-
-
 /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is
  * too small to safely receive signals on.
  *
@@ -276,6 +170,106 @@
   return UV__ERR(err);
 }
 
+#if UV__CPU_AFFINITY_SUPPORTED
+
+int uv_thread_setaffinity(uv_thread_t* tid,
+                          char* cpumask,
+                          char* oldmask,
+                          size_t mask_size) {
+  int i;
+  int r;
+  uv__cpu_set_t cpuset;
+  int cpumasksize;
+
+  cpumasksize = uv_cpumask_size();
+  if (cpumasksize < 0)
+    return cpumasksize;
+  if (mask_size < (size_t)cpumasksize)
+    return UV_EINVAL;
+
+  if (oldmask != NULL) {
+    r = uv_thread_getaffinity(tid, oldmask, mask_size);
+    if (r < 0)
+      return r;
+  }
+
+  CPU_ZERO(&cpuset);
+  for (i = 0; i < cpumasksize; i++)
+    if (cpumask[i])
+      CPU_SET(i, &cpuset);
+
+#if defined(__ANDROID__)
+  if (sched_setaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset))
+    r = errno;
+  else
+    r = 0;
+#else
+  r = pthread_setaffinity_np(*tid, sizeof(cpuset), &cpuset);
+#endif
+
+  return UV__ERR(r);
+}
+
+
+int uv_thread_getaffinity(uv_thread_t* tid,
+                          char* cpumask,
+                          size_t mask_size) {
+  int r;
+  int i;
+  uv__cpu_set_t cpuset;
+  int cpumasksize;
+
+  cpumasksize = uv_cpumask_size();
+  if (cpumasksize < 0)
+    return cpumasksize;
+  if (mask_size < (size_t)cpumasksize)
+    return UV_EINVAL;
+
+  CPU_ZERO(&cpuset);
+#if defined(__ANDROID__)
+  if (sched_getaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset))
+    r = errno;
+  else
+    r = 0;
+#else
+  r = pthread_getaffinity_np(*tid, sizeof(cpuset), &cpuset);
+#endif
+  if (r)
+    return UV__ERR(r);
+  for (i = 0; i < cpumasksize; i++)
+    cpumask[i] = !!CPU_ISSET(i, &cpuset);
+
+  return 0;
+}
+#else
+int uv_thread_setaffinity(uv_thread_t* tid,
+                          char* cpumask,
+                          char* oldmask,
+                          size_t mask_size) {
+  return UV_ENOTSUP;
+}
+
+
+int uv_thread_getaffinity(uv_thread_t* tid,
+                          char* cpumask,
+                          size_t mask_size) {
+  return UV_ENOTSUP;
+}
+#endif /* defined(__linux__) || defined(UV_BSD_H) */
+
+int uv_thread_getcpu(void) {
+#if UV__CPU_AFFINITY_SUPPORTED
+  int cpu;
+
+  cpu = sched_getcpu();
+  if (cpu < 0)
+    return UV__ERR(errno);
+
+  return cpu;
+#else
+  return UV_ENOTSUP;
+#endif
+}
 
 uv_thread_t uv_thread_self(void) {
   return pthread_self();
@@ -577,7 +571,7 @@
   uv_mutex_lock(&sem->mutex);
   sem->value++;
   if (sem->value == 1)
-    uv_cond_signal(&sem->cond);
+    uv_cond_signal(&sem->cond); /* Release one to replace us. */
   uv_mutex_unlock(&sem->mutex);
 }
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/tty.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/tty.cpp
index ed81e26..1304c6d 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/tty.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/tty.cpp
@@ -21,7 +21,6 @@
 
 #include "uv.h"
 #include "internal.h"
-#include "spinlock.h"
 
 #include <stdlib.h>
 #include <assert.h>
@@ -30,6 +29,8 @@
 #include <errno.h>
 #include <sys/ioctl.h>
 
+#include <atomic>
+
 #if defined(__MVS__) && !defined(IMAXBEL)
 #define IMAXBEL 0
 #endif
@@ -64,7 +65,7 @@
 
 static int orig_termios_fd = -1;
 static struct termios orig_termios;
-static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER;
+static std::atomic<int> termios_spinlock;
 
 int uv__tcsetattr(int fd, int how, const struct termios *term) {
   int rc;
@@ -81,7 +82,7 @@
 
 static int uv__tty_is_peripheral(const int fd) {
   int result;
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#if defined(__linux__) || defined(__FreeBSD__)
   int dummy;
 
   result = ioctl(fd, TIOCGPTN, &dummy) != 0;
@@ -114,7 +115,7 @@
   }
 
   /* Lookup stat structure behind the file descriptor. */
-  if (fstat(fd, &sb) != 0)
+  if (uv__fstat(fd, &sb) != 0)
     abort();
 
   /* Assert character device. */
@@ -223,7 +224,7 @@
     int rc = r;
     if (newfd != -1)
       uv__close(newfd);
-    QUEUE_REMOVE(&tty->handle_queue);
+    uv__queue_remove(&tty->handle_queue);
     do
       r = fcntl(fd, F_SETFL, saved_flags);
     while (r == -1 && errno == EINTR);
@@ -281,6 +282,7 @@
 
 int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
   struct termios tmp;
+  int expected;
   int fd;
   int rc;
 
@@ -297,12 +299,16 @@
       return UV__ERR(errno);
 
     /* This is used for uv_tty_reset_mode() */
-    uv_spinlock_lock(&termios_spinlock);
+    do
+      expected = 0;
+    while (!atomic_compare_exchange_strong(&termios_spinlock, &expected, 1));
+
     if (orig_termios_fd == -1) {
       orig_termios = tty->orig_termios;
       orig_termios_fd = fd;
     }
-    uv_spinlock_unlock(&termios_spinlock);
+
+    atomic_store(&termios_spinlock, 0);
   }
 
   tmp = tty->orig_termios;
@@ -361,7 +367,7 @@
   if (isatty(file))
     return UV_TTY;
 
-  if (fstat(file, &s)) {
+  if (uv__fstat(file, &s)) {
 #if defined(__PASE__)
     /* On ibmi receiving RST from TCP instead of FIN immediately puts fd into
      * an error state. fstat will return EINVAL, getsockname will also return
@@ -446,14 +452,15 @@
   int err;
 
   saved_errno = errno;
-  if (!uv_spinlock_trylock(&termios_spinlock))
+
+  if (atomic_exchange(&termios_spinlock, 1))
     return UV_EBUSY;  /* In uv_tty_set_mode(). */
 
   err = 0;
   if (orig_termios_fd != -1)
     err = uv__tcsetattr(orig_termios_fd, TCSANOW, &orig_termios);
 
-  uv_spinlock_unlock(&termios_spinlock);
+  atomic_store(&termios_spinlock, 0);
   errno = saved_errno;
 
   return err;
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/udp.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/udp.cpp
index a130aea..cbee16b 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/udp.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/udp.cpp
@@ -40,12 +40,6 @@
 # define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
 #endif
 
-union uv__sockaddr {
-  struct sockaddr_in6 in6;
-  struct sockaddr_in in;
-  struct sockaddr addr;
-};
-
 static void uv__udp_run_completed(uv_udp_t* handle);
 static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
 static void uv__udp_recvmsg(uv_udp_t* handle);
@@ -54,36 +48,6 @@
                                        int domain,
                                        unsigned int flags);
 
-#if HAVE_MMSG
-
-#define UV__MMSG_MAXWIDTH 20
-
-static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf);
-static void uv__udp_sendmmsg(uv_udp_t* handle);
-
-static int uv__recvmmsg_avail;
-static int uv__sendmmsg_avail;
-static uv_once_t once = UV_ONCE_INIT;
-
-static void uv__udp_mmsg_init(void) {
-  int ret;
-  int s;
-  s = uv__socket(AF_INET, SOCK_DGRAM, 0);
-  if (s < 0)
-    return;
-  ret = uv__sendmmsg(s, NULL, 0);
-  if (ret == 0 || errno != ENOSYS) {
-    uv__sendmmsg_avail = 1;
-    uv__recvmmsg_avail = 1;
-  } else {
-    ret = uv__recvmmsg(s, NULL, 0);
-    if (ret == 0 || errno != ENOSYS)
-      uv__recvmmsg_avail = 1;
-  }
-  uv__close(s);
-}
-
-#endif
 
 void uv__udp_close(uv_udp_t* handle) {
   uv__io_close(handle->loop, &handle->io_watcher);
@@ -98,18 +62,18 @@
 
 void uv__udp_finish_close(uv_udp_t* handle) {
   uv_udp_send_t* req;
-  QUEUE* q;
+  struct uv__queue* q;
 
   assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT));
   assert(handle->io_watcher.fd == -1);
 
-  while (!QUEUE_EMPTY(&handle->write_queue)) {
-    q = QUEUE_HEAD(&handle->write_queue);
-    QUEUE_REMOVE(q);
+  while (!uv__queue_empty(&handle->write_queue)) {
+    q = uv__queue_head(&handle->write_queue);
+    uv__queue_remove(q);
 
-    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    req = uv__queue_data(q, uv_udp_send_t, queue);
     req->status = UV_ECANCELED;
-    QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
+    uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
   }
 
   uv__udp_run_completed(handle);
@@ -126,16 +90,16 @@
 
 static void uv__udp_run_completed(uv_udp_t* handle) {
   uv_udp_send_t* req;
-  QUEUE* q;
+  struct uv__queue* q;
 
   assert(!(handle->flags & UV_HANDLE_UDP_PROCESSING));
   handle->flags |= UV_HANDLE_UDP_PROCESSING;
 
-  while (!QUEUE_EMPTY(&handle->write_completed_queue)) {
-    q = QUEUE_HEAD(&handle->write_completed_queue);
-    QUEUE_REMOVE(q);
+  while (!uv__queue_empty(&handle->write_completed_queue)) {
+    q = uv__queue_head(&handle->write_completed_queue);
+    uv__queue_remove(q);
 
-    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    req = uv__queue_data(q, uv_udp_send_t, queue);
     uv__req_unregister(handle->loop, req);
 
     handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs);
@@ -157,7 +121,7 @@
       req->send_cb(req, req->status);
   }
 
-  if (QUEUE_EMPTY(&handle->write_queue)) {
+  if (uv__queue_empty(&handle->write_queue)) {
     /* Pending queue and completion queue empty, stop watcher. */
     uv__io_stop(handle->loop, &handle->io_watcher, POLLOUT);
     if (!uv__io_active(&handle->io_watcher, POLLIN))
@@ -183,11 +147,11 @@
   }
 }
 
-#if HAVE_MMSG
 static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
-  struct sockaddr_in6 peers[UV__MMSG_MAXWIDTH];
-  struct iovec iov[UV__MMSG_MAXWIDTH];
-  struct uv__mmsghdr msgs[UV__MMSG_MAXWIDTH];
+#if defined(__linux__) || defined(__FreeBSD__)
+  struct sockaddr_in6 peers[20];
+  struct iovec iov[ARRAY_SIZE(peers)];
+  struct mmsghdr msgs[ARRAY_SIZE(peers)];
   ssize_t nread;
   uv_buf_t chunk_buf;
   size_t chunks;
@@ -212,7 +176,7 @@
   }
 
   do
-    nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks);
+    nread = recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL);
   while (nread == -1 && errno == EINTR);
 
   if (nread < 1) {
@@ -240,8 +204,10 @@
       handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE);
   }
   return nread;
+#else  /* __linux__ || ____FreeBSD__ */
+  return UV_ENOSYS;
+#endif  /* __linux__ || ____FreeBSD__ */
 }
-#endif
 
 static void uv__udp_recvmsg(uv_udp_t* handle) {
   struct sockaddr_storage peer;
@@ -268,14 +234,12 @@
     }
     assert(buf.base != NULL);
 
-#if HAVE_MMSG
     if (uv_udp_using_recvmmsg(handle)) {
       nread = uv__udp_recvmmsg(handle, &buf);
       if (nread > 0)
         count -= nread;
       continue;
     }
-#endif
 
     memset(&h, 0, sizeof(h));
     memset(&peer, 0, sizeof(peer));
@@ -311,25 +275,25 @@
       && handle->recv_cb != NULL);
 }
 
-#if HAVE_MMSG
-static void uv__udp_sendmmsg(uv_udp_t* handle) {
+static void uv__udp_sendmsg(uv_udp_t* handle) {
+#if defined(__linux__) || defined(__FreeBSD__)
   uv_udp_send_t* req;
-  struct uv__mmsghdr h[UV__MMSG_MAXWIDTH];
-  struct uv__mmsghdr *p;
-  QUEUE* q;
+  struct mmsghdr h[20];
+  struct mmsghdr* p;
+  struct uv__queue* q;
   ssize_t npkts;
   size_t pkts;
   size_t i;
 
-  if (QUEUE_EMPTY(&handle->write_queue))
+  if (uv__queue_empty(&handle->write_queue))
     return;
 
 write_queue_drain:
-  for (pkts = 0, q = QUEUE_HEAD(&handle->write_queue);
-       pkts < UV__MMSG_MAXWIDTH && q != &handle->write_queue;
-       ++pkts, q = QUEUE_HEAD(q)) {
+  for (pkts = 0, q = uv__queue_head(&handle->write_queue);
+       pkts < ARRAY_SIZE(h) && q != &handle->write_queue;
+       ++pkts, q = uv__queue_head(q)) {
     assert(q != NULL);
-    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    req = uv__queue_data(q, uv_udp_send_t, queue);
     assert(req != NULL);
 
     p = &h[pkts];
@@ -355,22 +319,22 @@
   }
 
   do
-    npkts = uv__sendmmsg(handle->io_watcher.fd, h, pkts);
+    npkts = sendmmsg(handle->io_watcher.fd, h, pkts, 0);
   while (npkts == -1 && errno == EINTR);
 
   if (npkts < 1) {
     if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
       return;
-    for (i = 0, q = QUEUE_HEAD(&handle->write_queue);
+    for (i = 0, q = uv__queue_head(&handle->write_queue);
          i < pkts && q != &handle->write_queue;
-         ++i, q = QUEUE_HEAD(&handle->write_queue)) {
+         ++i, q = uv__queue_head(&handle->write_queue)) {
       assert(q != NULL);
-      req = QUEUE_DATA(q, uv_udp_send_t, queue);
+      req = uv__queue_data(q, uv_udp_send_t, queue);
       assert(req != NULL);
 
       req->status = UV__ERR(errno);
-      QUEUE_REMOVE(&req->queue);
-      QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
+      uv__queue_remove(&req->queue);
+      uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
     }
     uv__io_feed(handle->loop, &handle->io_watcher);
     return;
@@ -379,11 +343,11 @@
   /* Safety: npkts known to be >0 below. Hence cast from ssize_t
    * to size_t safe.
    */
-  for (i = 0, q = QUEUE_HEAD(&handle->write_queue);
+  for (i = 0, q = uv__queue_head(&handle->write_queue);
        i < (size_t)npkts && q != &handle->write_queue;
-       ++i, q = QUEUE_HEAD(&handle->write_queue)) {
+       ++i, q = uv__queue_head(&handle->write_queue)) {
     assert(q != NULL);
-    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    req = uv__queue_data(q, uv_udp_send_t, queue);
     assert(req != NULL);
 
     req->status = req->bufs[0].len;
@@ -393,37 +357,25 @@
      * why we don't handle partial writes. Just pop the request
      * off the write queue and onto the completed queue, done.
      */
-    QUEUE_REMOVE(&req->queue);
-    QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
+    uv__queue_remove(&req->queue);
+    uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
   }
 
   /* couldn't batch everything, continue sending (jump to avoid stack growth) */
-  if (!QUEUE_EMPTY(&handle->write_queue))
+  if (!uv__queue_empty(&handle->write_queue))
     goto write_queue_drain;
   uv__io_feed(handle->loop, &handle->io_watcher);
-  return;
-}
-#endif
-
-static void uv__udp_sendmsg(uv_udp_t* handle) {
+#else  /* __linux__ || ____FreeBSD__ */
   uv_udp_send_t* req;
   struct msghdr h;
-  QUEUE* q;
+  struct uv__queue* q;
   ssize_t size;
 
-#if HAVE_MMSG
-  uv_once(&once, uv__udp_mmsg_init);
-  if (uv__sendmmsg_avail) {
-    uv__udp_sendmmsg(handle);
-    return;
-  }
-#endif
-
-  while (!QUEUE_EMPTY(&handle->write_queue)) {
-    q = QUEUE_HEAD(&handle->write_queue);
+  while (!uv__queue_empty(&handle->write_queue)) {
+    q = uv__queue_head(&handle->write_queue);
     assert(q != NULL);
 
-    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    req = uv__queue_data(q, uv_udp_send_t, queue);
     assert(req != NULL);
 
     memset(&h, 0, sizeof h);
@@ -462,10 +414,11 @@
      * why we don't handle partial writes. Just pop the request
      * off the write queue and onto the completed queue, done.
      */
-    QUEUE_REMOVE(&req->queue);
-    QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
+    uv__queue_remove(&req->queue);
+    uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
     uv__io_feed(handle->loop, &handle->io_watcher);
   }
+#endif  /* __linux__ || ____FreeBSD__ */
 }
 
 /* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional
@@ -495,7 +448,8 @@
     if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
        return UV__ERR(errno);
   }
-#elif defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__GNU__)
+#elif defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__GNU__) && \
+	!defined(__sun__)
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
     return UV__ERR(errno);
 #else
@@ -775,7 +729,7 @@
   memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0]));
   handle->send_queue_size += uv__count_bufs(req->bufs, req->nbufs);
   handle->send_queue_count++;
-  QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue);
+  uv__queue_insert_tail(&handle->write_queue, &req->queue);
   uv__handle_start(handle);
 
   if (empty_queue && !(handle->flags & UV_HANDLE_UDP_PROCESSING)) {
@@ -785,7 +739,7 @@
      * away. In such cases the `io_watcher` has to be queued for asynchronous
      * write.
      */
-    if (!QUEUE_EMPTY(&handle->write_queue))
+    if (!uv__queue_empty(&handle->write_queue))
       uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
   } else {
     uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
@@ -1053,19 +1007,17 @@
   handle->send_queue_size = 0;
   handle->send_queue_count = 0;
   uv__io_init(&handle->io_watcher, uv__udp_io, fd);
-  QUEUE_INIT(&handle->write_queue);
-  QUEUE_INIT(&handle->write_completed_queue);
+  uv__queue_init(&handle->write_queue);
+  uv__queue_init(&handle->write_completed_queue);
 
   return 0;
 }
 
 
 int uv_udp_using_recvmmsg(const uv_udp_t* handle) {
-#if HAVE_MMSG
-  if (handle->flags & UV_HANDLE_UDP_RECVMMSG) {
-    uv_once(&once, uv__udp_mmsg_init);
-    return uv__recvmmsg_avail;
-  }
+#if defined(__linux__) || defined(__FreeBSD__)
+  if (handle->flags & UV_HANDLE_UDP_RECVMMSG)
+    return 1;
 #endif
   return 0;
 }
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/uv-common.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/uv-common.cpp
index 8ab600d..5c6d841 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/uv-common.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/uv-common.cpp
@@ -128,6 +128,39 @@
   return 0;
 }
 
+
+void uv_os_free_passwd(uv_passwd_t* pwd) {
+  if (pwd == NULL)
+    return;
+
+  /* On unix, the memory for name, shell, and homedir are allocated in a single
+   * uv__malloc() call. The base of the pointer is stored in pwd->username, so
+   * that is the field that needs to be freed.
+   */
+  uv__free(pwd->username);
+#ifdef _WIN32
+  uv__free(pwd->homedir);
+#endif
+  pwd->username = NULL;
+  pwd->shell = NULL;
+  pwd->homedir = NULL;
+}
+
+
+void uv_os_free_group(uv_group_t *grp) {
+  if (grp == NULL)
+    return;
+
+  /* The memory for is allocated in a single uv__malloc() call. The base of the
+   * pointer is stored in grp->members, so that is the only field that needs to
+   * be freed.
+   */
+  uv__free(grp->members);
+  grp->members = NULL;
+  grp->groupname = NULL;
+}
+
+
 #define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t);
 
 size_t uv_handle_size(uv_handle_type type) {
@@ -500,26 +533,17 @@
 
 
 void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) {
-  QUEUE queue;
-  QUEUE* q;
+  struct uv__queue queue;
+  struct uv__queue* q;
   uv_handle_t* h;
 
-// FIXME: GCC 12.1 gives a possibly real warning, but we don't know how to fix
-// it
-#if __GNUC__ >= 12
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdangling-pointer="
-#endif  // __GNUC__ >= 12
-  QUEUE_MOVE(&loop->handle_queue, &queue);
-#if __GNUC__ >= 12
-#pragma GCC diagnostic pop
-#endif  // __GNUC__ >= 12
-  while (!QUEUE_EMPTY(&queue)) {
-    q = QUEUE_HEAD(&queue);
-    h = QUEUE_DATA(q, uv_handle_t, handle_queue);
+  uv__queue_move(&loop->handle_queue, &queue);
+  while (!uv__queue_empty(&queue)) {
+    q = uv__queue_head(&queue);
+    h = uv__queue_data(q, uv_handle_t, handle_queue);
 
-    QUEUE_REMOVE(q);
-    QUEUE_INSERT_TAIL(&loop->handle_queue, q);
+    uv__queue_remove(q);
+    uv__queue_insert_tail(&loop->handle_queue, q);
 
     if (h->flags & UV_HANDLE_INTERNAL) continue;
     walk_cb(h, arg);
@@ -529,14 +553,14 @@
 
 static void uv__print_handles(uv_loop_t* loop, int only_active, FILE* stream) {
   const char* type;
-  QUEUE* q;
+  struct uv__queue* q;
   uv_handle_t* h;
 
   if (loop == NULL)
     loop = uv_default_loop();
 
-  QUEUE_FOREACH(q, &loop->handle_queue) {
-    h = QUEUE_DATA(q, uv_handle_t, handle_queue);
+  uv__queue_foreach(q, &loop->handle_queue) {
+    h = uv__queue_data(q, uv_handle_t, handle_queue);
 
     if (only_active && !uv__is_active(h))
       continue;
@@ -659,14 +683,22 @@
 
 void uv__fs_scandir_cleanup(uv_fs_t* req) {
   uv__dirent_t** dents;
+  unsigned int* nbufs;
+  unsigned int i;
+  unsigned int n;
 
-  unsigned int* nbufs = uv__get_nbufs(req);
+  if (req->result >= 0) {
+    dents = (uv__dirent_t**)(req->ptr);
+    nbufs = uv__get_nbufs(req);
 
-  dents = (uv__dirent_t**)(req->ptr);
-  if (*nbufs > 0 && *nbufs != (unsigned int) req->result)
-    (*nbufs)--;
-  for (; *nbufs < (unsigned int) req->result; (*nbufs)++)
-    uv__fs_scandir_free(dents[*nbufs]);
+    i = 0;
+    if (*nbufs > 0)
+      i = *nbufs - 1;
+
+    n = (unsigned int) req->result;
+    for (; i < n; i++)
+      uv__fs_scandir_free(dents[i]);
+  }
 
   uv__fs_scandir_free(req->ptr);
   req->ptr = NULL;
@@ -823,7 +855,7 @@
 
 
 int uv_loop_close(uv_loop_t* loop) {
-  QUEUE* q;
+  struct uv__queue* q;
   uv_handle_t* h;
 #ifndef NDEBUG
   void* saved_data;
@@ -832,8 +864,8 @@
   if (uv__has_active_reqs(loop))
     return UV_EBUSY;
 
-  QUEUE_FOREACH(q, &loop->handle_queue) {
-    h = QUEUE_DATA(q, uv_handle_t, handle_queue);
+  uv__queue_foreach(q, &loop->handle_queue) {
+    h = uv__queue_data(q, uv_handle_t, handle_queue);
     if (!(h->flags & UV_HANDLE_INTERNAL))
       return UV_EBUSY;
   }
@@ -897,12 +929,17 @@
 
 
 void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
+#ifdef __linux__
+  (void) &count;
+  uv__free(cpu_infos);
+#else
   int i;
 
   for (i = 0; i < count; i++)
     uv__free(cpu_infos[i].model);
 
   uv__free(cpu_infos);
+#endif  /* __linux__ */
 }
 
 
@@ -914,9 +951,9 @@
 __attribute__((destructor))
 #endif
 void uv_library_shutdown(void) {
-  static int was_shutdown;
+  static std::atomic<int> was_shutdown;
 
-  if (uv__load_relaxed(&was_shutdown))
+  if (uv__exchange_int_relaxed(&was_shutdown, 1))
     return;
 
   uv__process_title_cleanup();
@@ -927,7 +964,6 @@
 #else
   uv__threadpool_cleanup();
 #endif
-  uv__store_relaxed(&was_shutdown, 1);
 }
 
 
@@ -973,6 +1009,15 @@
 }
 
 
+int uv_metrics_info(uv_loop_t* loop, uv_metrics_t* metrics) {
+  memcpy(metrics,
+         &uv__get_loop_metrics(loop)->metrics,
+         sizeof(*metrics));
+
+  return 0;
+}
+
+
 uint64_t uv_metrics_idle_time(uv_loop_t* loop) {
   uv__loop_metrics_t* loop_metrics;
   uint64_t entry_time;
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/uv-common.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/uv-common.h
index 6001b0c..5dce8ea 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/uv-common.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/uv-common.h
@@ -30,12 +30,9 @@
 #include <assert.h>
 #include <stdarg.h>
 #include <stddef.h>
+#include <stdint.h>
 
-#if defined(_MSC_VER) && _MSC_VER < 1600
-# include "uv/stdint-msvc2008.h"
-#else
-# include <stdint.h>
-#endif
+#include <atomic>
 
 #include "uv.h"
 #include "uv/tree.h"
@@ -53,19 +50,25 @@
 #endif
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#define ARRAY_END(a)  ((a) + ARRAY_SIZE(a))
 
 #define container_of(ptr, type, member) \
   ((type *) ((char *) (ptr) - offsetof(type, member)))
 
+/* C11 defines static_assert to be a macro which calls _Static_assert. */
+#if defined(static_assert)
+#define STATIC_ASSERT(expr) static_assert(expr, #expr)
+#else
 #define STATIC_ASSERT(expr)                                                   \
   void uv__static_assert(int static_assert_failed[1 - 2 * !(expr)])
+#endif
 
-#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 7)
-#define uv__load_relaxed(p) __atomic_load_n(p, __ATOMIC_RELAXED)
-#define uv__store_relaxed(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED)
+#ifdef _MSC_VER
+#define uv__exchange_int_relaxed(p, v)                                        \
+  InterlockedExchangeNoFence((LONG volatile*)(p), v)
 #else
-#define uv__load_relaxed(p) (*p)
-#define uv__store_relaxed(p, v) do *p = v; while (0)
+#define uv__exchange_int_relaxed(p, v)                                        \
+  std::atomic_exchange_explicit((std::atomic<int>*)(p), v, std::memory_order_relaxed)
 #endif
 
 #define UV__UDP_DGRAM_MAXSIZE (64 * 1024)
@@ -83,7 +86,6 @@
   /* Used by streams. */
   UV_HANDLE_LISTENING                   = 0x00000040,
   UV_HANDLE_CONNECTION                  = 0x00000080,
-  UV_HANDLE_SHUTTING                    = 0x00000100,
   UV_HANDLE_SHUT                        = 0x00000200,
   UV_HANDLE_READ_PARTIAL                = 0x00000400,
   UV_HANDLE_READ_EOF                    = 0x00000800,
@@ -263,6 +265,14 @@
 #define uv__is_closing(h)                                                     \
   (((h)->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED)) != 0)
 
+#if defined(_WIN32)
+# define uv__is_stream_shutting(h)                                            \
+  (h->stream.conn.shutdown_req != NULL)
+#else
+# define uv__is_stream_shutting(h)                                            \
+  (h->shutdown_req != NULL)
+#endif
+
 #define uv__handle_start(h)                                                   \
   do {                                                                        \
     if (((h)->flags & UV_HANDLE_ACTIVE) != 0) break;                          \
@@ -311,7 +321,7 @@
     (h)->loop = (loop_);                                                      \
     (h)->type = (type_);                                                      \
     (h)->flags = UV_HANDLE_REF;  /* Ref the loop when active. */              \
-    QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue);            \
+    uv__queue_insert_tail(&(loop_)->handle_queue, &(h)->handle_queue);        \
     uv__handle_platform_init(h);                                              \
   }                                                                           \
   while (0)
@@ -347,6 +357,21 @@
 #define uv__get_loop_metrics(loop)                                            \
   (&uv__get_internal_fields(loop)->loop_metrics)
 
+#define uv__metrics_inc_loop_count(loop)                                      \
+  do {                                                                        \
+    uv__get_loop_metrics(loop)->metrics.loop_count++;                         \
+  } while (0)
+
+#define uv__metrics_inc_events(loop, e)                                       \
+  do {                                                                        \
+    uv__get_loop_metrics(loop)->metrics.events += (e);                        \
+  } while (0)
+
+#define uv__metrics_inc_events_waiting(loop, e)                               \
+  do {                                                                        \
+    uv__get_loop_metrics(loop)->metrics.events_waiting += (e);                \
+  } while (0)
+
 /* Allocator prototypes */
 void *uv__calloc(size_t count, size_t size);
 char *uv__strdup(const char* s);
@@ -360,6 +385,7 @@
 typedef struct uv__loop_internal_fields_s uv__loop_internal_fields_t;
 
 struct uv__loop_metrics_s {
+  uv_metrics_t metrics;
   uint64_t provider_entry_time;
   uint64_t provider_idle_time;
   uv_mutex_t lock;
@@ -368,9 +394,38 @@
 void uv__metrics_update_idle_time(uv_loop_t* loop);
 void uv__metrics_set_provider_entry_time(uv_loop_t* loop);
 
+#ifdef __linux__
+struct uv__iou {
+  uint32_t* sqhead;
+  uint32_t* sqtail;
+  uint32_t* sqarray;
+  uint32_t sqmask;
+  uint32_t* sqflags;
+  uint32_t* cqhead;
+  uint32_t* cqtail;
+  uint32_t cqmask;
+  void* sq;   /* pointer to munmap() on event loop teardown */
+  void* cqe;  /* pointer to array of struct uv__io_uring_cqe */
+  void* sqe;  /* pointer to array of struct uv__io_uring_sqe */
+  size_t sqlen;
+  size_t cqlen;
+  size_t maxlen;
+  size_t sqelen;
+  int ringfd;
+  uint32_t in_flight;
+  uint32_t flags;
+};
+#endif  /* __linux__ */
+
 struct uv__loop_internal_fields_s {
   unsigned int flags;
   uv__loop_metrics_t loop_metrics;
+  int current_timeout;
+#ifdef __linux__
+  struct uv__iou ctl;
+  struct uv__iou iou;
+  void* inv;  /* used by uv__platform_invalidate_fd() */
+#endif  /* __linux__ */
 };
 
 #endif /* UV_COMMON_H_ */
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/core.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/core.cpp
index 0752edf..87ade7a 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/core.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/core.cpp
@@ -247,6 +247,9 @@
   err = uv_mutex_init(&lfields->loop_metrics.lock);
   if (err)
     goto fail_metrics_mutex_init;
+  memset(&lfields->loop_metrics.metrics,
+         0,
+         sizeof(lfields->loop_metrics.metrics));
 
   /* To prevent uninitialized memory access, loop->time must be initialized
    * to zero before calling uv_update_time for the first time.
@@ -254,8 +257,8 @@
   loop->time = 0;
   uv_update_time(loop);
 
-  QUEUE_INIT(&loop->wq);
-  QUEUE_INIT(&loop->handle_queue);
+  uv__queue_init(&loop->wq);
+  uv__queue_init(&loop->handle_queue);
   loop->active_reqs.count = 0;
   loop->active_handles = 0;
 
@@ -281,9 +284,6 @@
 
   memset(&loop->poll_peer_sockets, 0, sizeof loop->poll_peer_sockets);
 
-  loop->active_tcp_streams = 0;
-  loop->active_udp_streams = 0;
-
   loop->timer_counter = 0;
   loop->stop_flag = 0;
 
@@ -360,7 +360,7 @@
   }
 
   uv_mutex_lock(&loop->wq_mutex);
-  assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!");
+  assert(uv__queue_empty(&loop->wq) && "thread pool work queue not empty!");
   assert(!uv__has_active_reqs(loop));
   uv_mutex_unlock(&loop->wq_mutex);
   uv_mutex_destroy(&loop->wq_mutex);
@@ -426,6 +426,7 @@
 
 
 static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
+  uv__loop_internal_fields_t* lfields;
   DWORD bytes;
   ULONG_PTR key;
   OVERLAPPED* overlapped;
@@ -435,9 +436,10 @@
   uint64_t user_timeout;
   int reset_timeout;
 
+  lfields = uv__get_internal_fields(loop);
   timeout_time = loop->time + timeout;
 
-  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+  if (lfields->flags & UV_METRICS_IDLE_TIME) {
     reset_timeout = 1;
     user_timeout = timeout;
     timeout = 0;
@@ -452,6 +454,12 @@
     if (timeout != 0)
       uv__metrics_set_provider_entry_time(loop);
 
+    /* Store the current timeout in a location that's globally accessible so
+     * other locations like uv__work_done() can determine whether the queue
+     * of events in the callback were waiting when poll was called.
+     */
+    lfields->current_timeout = timeout;
+
     GetQueuedCompletionStatus(loop->iocp,
                               &bytes,
                               &key,
@@ -459,6 +467,8 @@
                               timeout);
 
     if (reset_timeout != 0) {
+      if (overlapped && timeout == 0)
+        uv__metrics_inc_events_waiting(loop, 1);
       timeout = user_timeout;
       reset_timeout = 0;
     }
@@ -471,6 +481,8 @@
     uv__metrics_update_idle_time(loop);
 
     if (overlapped) {
+      uv__metrics_inc_events(loop, 1);
+
       /* Package was dequeued */
       req = uv__overlapped_to_req(overlapped);
       uv__insert_pending_req(loop, req);
@@ -505,6 +517,7 @@
 
 
 static void uv__poll(uv_loop_t* loop, DWORD timeout) {
+  uv__loop_internal_fields_t* lfields;
   BOOL success;
   uv_req_t* req;
   OVERLAPPED_ENTRY overlappeds[128];
@@ -513,11 +526,13 @@
   int repeat;
   uint64_t timeout_time;
   uint64_t user_timeout;
+  uint64_t actual_timeout;
   int reset_timeout;
 
+  lfields = uv__get_internal_fields(loop);
   timeout_time = loop->time + timeout;
 
-  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+  if (lfields->flags & UV_METRICS_IDLE_TIME) {
     reset_timeout = 1;
     user_timeout = timeout;
     timeout = 0;
@@ -526,12 +541,20 @@
   }
 
   for (repeat = 0; ; repeat++) {
+    actual_timeout = timeout;
+
     /* Only need to set the provider_entry_time if timeout != 0. The function
      * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
      */
     if (timeout != 0)
       uv__metrics_set_provider_entry_time(loop);
 
+    /* Store the current timeout in a location that's globally accessible so
+     * other locations like uv__work_done() can determine whether the queue
+     * of events in the callback were waiting when poll was called.
+     */
+    lfields->current_timeout = timeout;
+
     success = pGetQueuedCompletionStatusEx(loop->iocp,
                                            overlappeds,
                                            ARRAY_SIZE(overlappeds),
@@ -545,9 +568,9 @@
     }
 
     /* Placed here because on success the loop will break whether there is an
-     * empty package or not, or if GetQueuedCompletionStatus returned early then
-     * the timeout will be updated and the loop will run again. In either case
-     * the idle time will need to be updated.
+     * empty package or not, or if pGetQueuedCompletionStatusEx returned early
+     * then the timeout will be updated and the loop will run again. In either
+     * case the idle time will need to be updated.
      */
     uv__metrics_update_idle_time(loop);
 
@@ -557,6 +580,10 @@
          * meant only to wake us up.
          */
         if (overlappeds[i].lpOverlapped) {
+          uv__metrics_inc_events(loop, 1);
+          if (actual_timeout == 0)
+            uv__metrics_inc_events_waiting(loop, 1);
+
           req = uv__overlapped_to_req(overlappeds[i].lpOverlapped);
           uv__insert_pending_req(loop, req);
         }
@@ -600,10 +627,16 @@
   if (!r)
     uv_update_time(loop);
 
-  while (r != 0 && loop->stop_flag == 0) {
+  /* Maintain backwards compatibility by processing timers before entering the
+   * while loop for UV_RUN_DEFAULT. Otherwise timers only need to be executed
+   * once, which should be done after polling in order to maintain proper
+   * execution order of the conceptual event loop. */
+  if (mode == UV_RUN_DEFAULT && r != 0 && loop->stop_flag == 0) {
     uv_update_time(loop);
     uv__run_timers(loop);
+  }
 
+  while (r != 0 && loop->stop_flag == 0) {
     can_sleep = loop->pending_reqs_tail == NULL && loop->idle_handles == NULL;
 
     uv__process_reqs(loop);
@@ -614,6 +647,8 @@
     if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
       timeout = uv_backend_timeout(loop);
 
+    uv__metrics_inc_loop_count(loop);
+
     if (pGetQueuedCompletionStatusEx)
       uv__poll(loop, timeout);
     else
@@ -634,18 +669,8 @@
     uv__check_invoke(loop);
     uv__process_endgames(loop);
 
-    if (mode == UV_RUN_ONCE) {
-      /* UV_RUN_ONCE implies forward progress: at least one callback must have
-       * been invoked when it returns. uv__io_poll() can return without doing
-       * I/O (meaning: no callbacks) when its timeout expires - which means we
-       * have pending timers that satisfy the forward progress constraint.
-       *
-       * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
-       * the check.
-       */
-      uv_update_time(loop);
-      uv__run_timers(loop);
-    }
+    uv_update_time(loop);
+    uv__run_timers(loop);
 
     r = uv__loop_alive(loop);
     if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/fs.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/fs.cpp
index 71c9b16..f415ddc 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/fs.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/fs.cpp
@@ -38,6 +38,8 @@
 #include "handle-inl.h"
 #include "fs-fd-hash-inl.h"
 
+#include <winioctl.h>
+
 #pragma comment(lib, "Advapi32.lib")
 
 #define UV_FS_FREE_PATHS         0x0002
@@ -145,26 +147,97 @@
 }
 
 
+static int32_t fs__decode_wtf8_char(const char** input) {
+  uint32_t code_point;
+  uint8_t b1;
+  uint8_t b2;
+  uint8_t b3;
+  uint8_t b4;
+
+  b1 = **input;
+  if (b1 <= 0x7F)
+    return b1; /* ASCII code point */
+  if (b1 < 0xC2)
+    return -1; /* invalid: continuation byte */
+  code_point = b1;
+
+  b2 = *++*input;
+  if ((b2 & 0xC0) != 0x80)
+    return -1; /* invalid: not a continuation byte */
+  code_point = (code_point << 6) | (b2 & 0x3F);
+  if (b1 <= 0xDF)
+    return 0x7FF & code_point; /* two-byte character */
+
+  b3 = *++*input;
+  if ((b3 & 0xC0) != 0x80)
+    return -1; /* invalid: not a continuation byte */
+  code_point = (code_point << 6) | (b3 & 0x3F);
+  if (b1 <= 0xEF)
+    return 0xFFFF & code_point; /* three-byte character */
+
+  b4 = *++*input;
+  if ((b4 & 0xC0) != 0x80)
+    return -1; /* invalid: not a continuation byte */
+  code_point = (code_point << 6) | (b4 & 0x3F);
+  if (b1 <= 0xF4)
+    if (code_point <= 0x10FFFF)
+      return code_point; /* four-byte character */
+
+  /* code point too large */
+  return -1;
+}
+
+
+static ssize_t fs__get_length_wtf8(const char* source_ptr) {
+  size_t w_target_len = 0;
+  int32_t code_point;
+
+  do {
+    code_point = fs__decode_wtf8_char(&source_ptr);
+    if (code_point < 0)
+      return -1;
+    if (code_point > 0xFFFF)
+      w_target_len++;
+    w_target_len++;
+  } while (*source_ptr++);
+  return w_target_len;
+}
+
+
+static void fs__wtf8_to_wide(const char* source_ptr, WCHAR* w_target) {
+  int32_t code_point;
+
+  do {
+    code_point = fs__decode_wtf8_char(&source_ptr);
+    /* fs__get_length_wtf8 should have been called and checked first. */
+    assert(code_point >= 0);
+    if (code_point > 0x10000) {
+      assert(code_point < 0x10FFFF);
+      *w_target++ = (((code_point - 0x10000) >> 10) + 0xD800);
+      *w_target++ = ((code_point - 0x10000) & 0x3FF) + 0xDC00;
+    } else {
+      *w_target++ = code_point;
+    }
+  } while (*source_ptr++);
+}
+
+
 INLINE static int fs__capture_path(uv_fs_t* req, const char* path,
     const char* new_path, const int copy_path) {
-  char* buf;
-  char* pos;
-  ssize_t buf_sz = 0, path_len = 0, pathw_len = 0, new_pathw_len = 0;
+  WCHAR* buf;
+  WCHAR* pos;
+  size_t buf_sz = 0;
+  size_t path_len = 0;
+  ssize_t pathw_len = 0;
+  ssize_t new_pathw_len = 0;
 
   /* new_path can only be set if path is also set. */
   assert(new_path == NULL || path != NULL);
 
   if (path != NULL) {
-    pathw_len = MultiByteToWideChar(CP_UTF8,
-                                    0,
-                                    path,
-                                    -1,
-                                    NULL,
-                                    0);
-    if (pathw_len == 0) {
-      return GetLastError();
-    }
-
+    pathw_len = fs__get_length_wtf8(path);
+    if (pathw_len < 0)
+      return ERROR_INVALID_NAME;
     buf_sz += pathw_len * sizeof(WCHAR);
   }
 
@@ -174,16 +247,9 @@
   }
 
   if (new_path != NULL) {
-    new_pathw_len = MultiByteToWideChar(CP_UTF8,
-                                        0,
-                                        new_path,
-                                        -1,
-                                        NULL,
-                                        0);
-    if (new_pathw_len == 0) {
-      return GetLastError();
-    }
-
+    new_pathw_len = fs__get_length_wtf8(new_path);
+    if (new_pathw_len < 0)
+      return ERROR_INVALID_NAME;
     buf_sz += new_pathw_len * sizeof(WCHAR);
   }
 
@@ -195,7 +261,7 @@
     return 0;
   }
 
-  buf = (char*) uv__malloc(buf_sz);
+  buf = (WCHAR *)uv__malloc(buf_sz);
   if (buf == NULL) {
     return ERROR_OUTOFMEMORY;
   }
@@ -203,29 +269,17 @@
   pos = buf;
 
   if (path != NULL) {
-    DWORD r = MultiByteToWideChar(CP_UTF8,
-                                  0,
-                                  path,
-                                  -1,
-                                  (WCHAR*) pos,
-                                  pathw_len);
-    assert(r == (DWORD) pathw_len);
-    req->file.pathw = (WCHAR*) pos;
-    pos += r * sizeof(WCHAR);
+    fs__wtf8_to_wide(path, pos);
+    req->file.pathw = pos;
+    pos += pathw_len;
   } else {
     req->file.pathw = NULL;
   }
 
   if (new_path != NULL) {
-    DWORD r = MultiByteToWideChar(CP_UTF8,
-                                  0,
-                                  new_path,
-                                  -1,
-                                  (WCHAR*) pos,
-                                  new_pathw_len);
-    assert(r == (DWORD) new_pathw_len);
-    req->fs.info.new_pathw = (WCHAR*) pos;
-    pos += r * sizeof(WCHAR);
+    fs__wtf8_to_wide(new_path, pos);
+    req->fs.info.new_pathw = pos;
+    pos += new_pathw_len;
   } else {
     req->fs.info.new_pathw = NULL;
   }
@@ -233,8 +287,8 @@
   req->path = path;
   if (path != NULL && copy_path) {
     memcpy(pos, path, path_len);
-    assert(path_len == buf_sz - (pos - buf));
-    req->path = pos;
+    assert(path_len == buf_sz - (pos - buf) * sizeof(WCHAR));
+    req->path = (char*) pos;
   }
 
   req->flags |= UV_FS_FREE_PATHS;
@@ -260,57 +314,115 @@
 }
 
 
-static int fs__wide_to_utf8(WCHAR* w_source_ptr,
-                               DWORD w_source_len,
-                               char** target_ptr,
-                               uint64_t* target_len_ptr) {
-  int r;
-  int target_len;
+static int32_t fs__get_surrogate_value(const WCHAR* w_source_ptr,
+                                       size_t w_source_len) {
+  WCHAR u;
+  WCHAR next;
+
+  u = w_source_ptr[0];
+  if (u >= 0xD800 && u <= 0xDBFF && w_source_len > 1) {
+    next = w_source_ptr[1];
+    if (next >= 0xDC00 && next <= 0xDFFF)
+      return 0x10000 + ((u - 0xD800) << 10) + (next - 0xDC00);
+  }
+  return u;
+}
+
+
+static size_t fs__get_length_wide(const WCHAR* w_source_ptr,
+                                  size_t w_source_len) {
+  size_t target_len;
+  int32_t code_point;
+
+  target_len = 0;
+  for (; w_source_len; w_source_len--, w_source_ptr++) {
+    code_point = fs__get_surrogate_value(w_source_ptr, w_source_len);
+    /* Can be invalid UTF-8 but must be valid WTF-8. */
+    assert(code_point >= 0);
+    if (code_point < 0x80)
+      target_len += 1;
+    else if (code_point < 0x800)
+      target_len += 2;
+    else if (code_point < 0x10000)
+      target_len += 3;
+    else {
+      target_len += 4;
+      w_source_ptr++;
+      w_source_len--;
+    }
+  }
+  return target_len;
+}
+
+
+static int fs__wide_to_wtf8(WCHAR* w_source_ptr,
+                            size_t w_source_len,
+                            char** target_ptr,
+                            size_t* target_len_ptr) {
+  size_t target_len;
   char* target;
-  target_len = WideCharToMultiByte(CP_UTF8,
-                                   0,
-                                   w_source_ptr,
-                                   w_source_len,
-                                   NULL,
-                                   0,
-                                   NULL,
-                                   NULL);
+  int32_t code_point;
 
-  if (target_len == 0) {
-    return -1;
+  /* If *target_ptr is provided, then *target_len_ptr must be its length
+   * (excluding space for null), otherwise we will compute the target_len_ptr
+   * length and may return a new allocation in *target_ptr if target_ptr is
+   * provided. */
+  if (target_ptr == NULL || *target_ptr == NULL) {
+    target_len = fs__get_length_wide(w_source_ptr, w_source_len);
+    if (target_len_ptr != NULL)
+      *target_len_ptr = target_len;
+  } else {
+    target_len = *target_len_ptr;
   }
 
-  if (target_len_ptr != NULL) {
-    *target_len_ptr = target_len;
-  }
-
-  if (target_ptr == NULL) {
+  if (target_ptr == NULL)
     return 0;
+
+  if (*target_ptr == NULL) {
+    target = (char *)uv__malloc(target_len + 1);
+    if (target == NULL) {
+      SetLastError(ERROR_OUTOFMEMORY);
+      return -1;
+    }
+    *target_ptr = target;
+  } else {
+    target = *target_ptr;
   }
 
-  target = (char*)uv__malloc(target_len + 1);
-  if (target == NULL) {
-    SetLastError(ERROR_OUTOFMEMORY);
-    return -1;
-  }
+  for (; w_source_len; w_source_len--, w_source_ptr++) {
+    code_point = fs__get_surrogate_value(w_source_ptr, w_source_len);
+    /* Can be invalid UTF-8 but must be valid WTF-8. */
+    assert(code_point >= 0);
 
-  r = WideCharToMultiByte(CP_UTF8,
-                          0,
-                          w_source_ptr,
-                          w_source_len,
-                          target,
-                          target_len,
-                          NULL,
-                          NULL);
-  assert(r == target_len);
-  target[target_len] = '\0';
-  *target_ptr = target;
+    if (code_point < 0x80) {
+      *target++ = code_point;
+    } else if (code_point < 0x800) {
+      *target++ = 0xC0 | (code_point >> 6);
+      *target++ = 0x80 | (code_point & 0x3F);
+    } else if (code_point < 0x10000) {
+      *target++ = 0xE0 | (code_point >> 12);
+      *target++ = 0x80 | ((code_point >> 6) & 0x3F);
+      *target++ = 0x80 | (code_point & 0x3F);
+    } else {
+      *target++ = 0xF0 | (code_point >> 18);
+      *target++ = 0x80 | ((code_point >> 12) & 0x3F);
+      *target++ = 0x80 | ((code_point >> 6) & 0x3F);
+      *target++ = 0x80 | (code_point & 0x3F);
+      w_source_ptr++;
+      w_source_len--;
+    }
+  }
+  assert((size_t) (target - *target_ptr) == target_len);
+
+  *target++ = '\0';
+
   return 0;
 }
 
 
-INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr,
-    uint64_t* target_len_ptr) {
+INLINE static int fs__readlink_handle(HANDLE handle,
+                                      char** target_ptr,
+                                      size_t* target_len_ptr) {
   char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
   REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer;
   WCHAR* w_target;
@@ -440,7 +552,8 @@
     return -1;
   }
 
-  return fs__wide_to_utf8(w_target, w_target_len, target_ptr, target_len_ptr);
+  assert(target_ptr == NULL || *target_ptr == NULL);
+  return fs__wide_to_wtf8(w_target, w_target_len, target_ptr, target_len_ptr);
 }
 
 
@@ -1430,7 +1543,8 @@
       uv__dirent_t* dirent;
 
       size_t wchar_len;
-      size_t utf8_len;
+      size_t wtf8_len;
+      char* wtf8;
 
       /* Obtain a pointer to the current directory entry. */
       position += next_entry_offset;
@@ -1457,11 +1571,8 @@
           info->FileName[1] == L'.')
         continue;
 
-      /* Compute the space required to store the filename as UTF-8. */
-      utf8_len = WideCharToMultiByte(
-          CP_UTF8, 0, &info->FileName[0], wchar_len, NULL, 0, NULL, NULL);
-      if (utf8_len == 0)
-        goto win32_error;
+      /* Compute the space required to store the filename as WTF-8. */
+      wtf8_len = fs__get_length_wide(&info->FileName[0], wchar_len);
 
       /* Resize the dirent array if needed. */
       if (dirents_used >= dirents_size) {
@@ -1481,26 +1592,17 @@
        * includes room for the first character of the filename, but `utf8_len`
        * doesn't count the NULL terminator at this point.
        */
-      dirent = (uv__dirent_t*)uv__malloc(sizeof *dirent + utf8_len);
+      dirent = (uv__dirent_t*)uv__malloc(sizeof *dirent + wtf8_len);
       if (dirent == NULL)
         goto out_of_memory_error;
 
       dirents[dirents_used++] = dirent;
 
       /* Convert file name to UTF-8. */
-      if (WideCharToMultiByte(CP_UTF8,
-                              0,
-                              &info->FileName[0],
-                              wchar_len,
-                              &dirent->d_name[0],
-                              utf8_len,
-                              NULL,
-                              NULL) == 0)
+      wtf8 = &dirent->d_name[0];
+      if (fs__wide_to_wtf8(&info->FileName[0], wchar_len, &wtf8, &wtf8_len) == -1)
         goto win32_error;
 
-      /* Add a null terminator to the filename. */
-      dirent->d_name[utf8_len] = '\0';
-
       /* Fill out the type field. */
       if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE)
         dirent->d_type = UV__DT_CHAR;
@@ -1709,11 +1811,37 @@
 
 INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
     int do_lstat) {
+  size_t target_length = 0;
+  FILE_FS_DEVICE_INFORMATION device_info;
   FILE_ALL_INFORMATION file_info;
   FILE_FS_VOLUME_INFORMATION volume_info;
   NTSTATUS nt_status;
   IO_STATUS_BLOCK io_status;
 
+  nt_status = pNtQueryVolumeInformationFile(handle,
+                                            &io_status,
+                                            &device_info,
+                                            sizeof device_info,
+                                            FileFsDeviceInformation);
+
+  /* Buffer overflow (a warning status code) is expected here. */
+  if (NT_ERROR(nt_status)) {
+    SetLastError(pRtlNtStatusToDosError(nt_status));
+    return -1;
+  }
+
+  /* If it's NUL device set fields as reasonable as possible and return. */
+  if (device_info.DeviceType == FILE_DEVICE_NULL) {
+    memset(statbuf, 0, sizeof(uv_stat_t));
+    statbuf->st_mode = _S_IFCHR;
+    statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
+                        ((_S_IREAD | _S_IWRITE) >> 6);
+    statbuf->st_nlink = 1;
+    statbuf->st_blksize = 4096;    
+    statbuf->st_rdev = FILE_DEVICE_NULL << 16;
+    return 0;
+  }
+
   nt_status = pNtQueryInformationFile(handle,
                                       &io_status,
                                       &file_info,
@@ -1779,9 +1907,10 @@
      * to be treated as a regular file. The higher level lstat function will
      * detect this failure and retry without do_lstat if appropriate.
      */
-    if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0)
+    if (fs__readlink_handle(handle, NULL, &target_length) != 0)
       return -1;
     statbuf->st_mode |= S_IFLNK;
+    statbuf->st_size = target_length;
   }
 
   if (statbuf->st_mode == 0) {
@@ -1918,6 +2047,37 @@
 }
 
 
+INLINE static int fs__fstat_handle(int fd, HANDLE handle, uv_stat_t* statbuf) {
+  DWORD file_type;
+
+  /* Each file type is processed differently. */
+  file_type = uv_guess_handle(fd);
+  switch (file_type) {
+  /* Disk files use the existing logic from fs__stat_handle. */
+  case UV_FILE:
+    return fs__stat_handle(handle, statbuf, 0);
+
+  /* Devices and pipes are processed identically. There is no more information
+   * for them from any API. Fields are set as reasonably as possible and the
+   * function returns. */
+  case UV_TTY:
+  case UV_NAMED_PIPE:
+    memset(statbuf, 0, sizeof(uv_stat_t));
+    statbuf->st_mode = file_type == UV_TTY ? _S_IFCHR : _S_IFIFO;
+    statbuf->st_nlink = 1;
+    statbuf->st_rdev = (file_type == UV_TTY ? FILE_DEVICE_CONSOLE : FILE_DEVICE_NAMED_PIPE) << 16;
+    statbuf->st_ino = (uintptr_t) handle;
+    return 0;
+
+  /* If file type is unknown it is an error. */
+  case UV_UNKNOWN_HANDLE:
+  default:
+    SetLastError(ERROR_INVALID_HANDLE);
+    return -1;
+  }
+}
+
+
 static void fs__stat(uv_fs_t* req) {
   fs__stat_prepare_path(req->file.pathw);
   fs__stat_impl(req, 0);
@@ -1943,7 +2103,7 @@
     return;
   }
 
-  if (fs__stat_handle(handle, &req->statbuf, 0) != 0) {
+  if (fs__fstat_handle(fd, handle, &req->statbuf) != 0) {
     SET_REQ_WIN32_ERROR(req, GetLastError());
     return;
   }
@@ -2224,7 +2384,7 @@
         SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
         goto fchmod_cleanup;
       }
-      /* Remeber to clear the flag later on */
+      /* Remember to clear the flag later on */
       clear_archive_flag = 1;
   } else {
       clear_archive_flag = 0;
@@ -2606,8 +2766,12 @@
     return;
   }
 
+  assert(req->ptr == NULL);
   if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) {
-    SET_REQ_WIN32_ERROR(req, GetLastError());
+    DWORD error = GetLastError();
+    SET_REQ_WIN32_ERROR(req, error);
+    if (error == ERROR_NOT_A_REPARSE_POINT)
+      req->result = UV_EINVAL;
     CloseHandle(handle);
     return;
   }
@@ -2662,7 +2826,8 @@
     return -1;
   }
 
-  r = fs__wide_to_utf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL);
+  assert(*realpath_ptr == NULL);
+  r = fs__wide_to_wtf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL);
   uv__free(w_realpath_buf);
   return r;
 }
@@ -2682,6 +2847,7 @@
     return;
   }
 
+  assert(req->ptr == NULL);
   if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) {
     CloseHandle(handle);
     SET_REQ_WIN32_ERROR(req, GetLastError());
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/handle-inl.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/handle-inl.h
index 5c843c2..4722e85 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/handle-inl.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/handle-inl.h
@@ -75,7 +75,7 @@
 
 #define uv__handle_close(handle)                                        \
   do {                                                                  \
-    QUEUE_REMOVE(&(handle)->handle_queue);                              \
+    uv__queue_remove(&(handle)->handle_queue);                          \
     uv__active_handle_rm((uv_handle_t*) (handle));                      \
                                                                         \
     (handle)->flags |= UV_HANDLE_CLOSED;                                \
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/internal.h b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/internal.h
index 89c72b8..9672fbc 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/internal.h
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/internal.h
@@ -168,18 +168,8 @@
     uv_req_t* req);
 void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
     uv_write_t* req);
-/*
- * uv__process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
- * TODO: find a way to remove it
- */
-void uv__process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
-    uv_req_t* raw_req);
-/*
- * uv__process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
- * TODO: find a way to remove it
- */
-void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
-    uv_connect_t* req);
+#define uv__process_tty_accept_req(loop, handle, req) abort()
+#define uv__process_tty_connect_req(loop, handle, req) abort()
 void uv__process_tty_shutdown_req(uv_loop_t* loop,
                                   uv_tty_t* stream,
                                   uv_shutdown_t* req);
@@ -267,7 +257,6 @@
 
 uint64_t uv__hrtime(unsigned int scale);
 __declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall);
-int uv__getpwuid_r(uv_passwd_t* pwd);
 int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8);
 int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16);
 
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/pipe.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/pipe.cpp
index f413a72..258d6a6 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/pipe.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/pipe.cpp
@@ -57,7 +57,7 @@
 typedef struct {
   uv__ipc_socket_xfer_type_t xfer_type;
   uv__ipc_socket_xfer_info_t xfer_info;
-  QUEUE member;
+  struct uv__queue member;
 } uv__ipc_xfer_queue_item_t;
 
 /* IPC frame header flags. */
@@ -113,7 +113,7 @@
   handle->name = NULL;
   handle->pipe.conn.ipc_remote_pid = 0;
   handle->pipe.conn.ipc_data_frame.payload_remaining = 0;
-  QUEUE_INIT(&handle->pipe.conn.ipc_xfer_queue);
+  uv__queue_init(&handle->pipe.conn.ipc_xfer_queue);
   handle->pipe.conn.ipc_xfer_queue_length = 0;
   handle->ipc = ipc;
   handle->pipe.conn.non_overlapped_writes_tail = NULL;
@@ -639,13 +639,13 @@
 
   if (handle->flags & UV_HANDLE_CONNECTION) {
     /* Free pending sockets */
-    while (!QUEUE_EMPTY(&handle->pipe.conn.ipc_xfer_queue)) {
-      QUEUE* q;
+    while (!uv__queue_empty(&handle->pipe.conn.ipc_xfer_queue)) {
+      struct uv__queue* q;
       SOCKET socket;
 
-      q = QUEUE_HEAD(&handle->pipe.conn.ipc_xfer_queue);
-      QUEUE_REMOVE(q);
-      xfer_queue_item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member);
+      q = uv__queue_head(&handle->pipe.conn.ipc_xfer_queue);
+      uv__queue_remove(q);
+      xfer_queue_item = uv__queue_data(q, uv__ipc_xfer_queue_item_t, member);
 
       /* Materialize socket and close it */
       socket = WSASocketW(FROM_PROTOCOL_INFO,
@@ -696,20 +696,48 @@
 
 /* Creates a pipe server. */
 int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
+  return uv_pipe_bind2(handle, name, strlen(name), 0);
+}
+
+
+int uv_pipe_bind2(uv_pipe_t* handle,
+                  const char* name,
+                  size_t namelen,
+                  unsigned int flags) {
   uv_loop_t* loop = handle->loop;
   int i, err, nameSize;
   uv_pipe_accept_t* req;
 
+  if (flags & ~UV_PIPE_NO_TRUNCATE) {
+    return UV_EINVAL;
+  }
+
+  if (name == NULL) {
+    return UV_EINVAL;
+  }
+
+  if (namelen == 0) {
+    return UV_EINVAL;
+  }
+
+  if (*name == '\0') {
+    return UV_EINVAL;
+  }
+
+  if (flags & UV_PIPE_NO_TRUNCATE) {
+    if (namelen > 256) {
+      return UV_EINVAL;
+    }
+  }
+
   if (handle->flags & UV_HANDLE_BOUND) {
     return UV_EINVAL;
   }
 
-  if (!name) {
-    return UV_EINVAL;
-  }
   if (uv__is_closing(handle)) {
     return UV_EINVAL;
   }
+
   if (!(handle->flags & UV_HANDLE_PIPESERVER)) {
     handle->pipe.serv.pending_instances = default_pending_pipe_instances;
   }
@@ -794,15 +822,17 @@
 
   /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait
    * up to 30 seconds for the pipe to become available with WaitNamedPipe. */
-  while (WaitNamedPipeW(handle->name, 30000)) {
+  while (WaitNamedPipeW(req->u.connect.name, 30000)) {
     /* The pipe is now available, try to connect. */
-    pipeHandle = open_named_pipe(handle->name, &duplex_flags);
+    pipeHandle = open_named_pipe(req->u.connect.name, &duplex_flags);
     if (pipeHandle != INVALID_HANDLE_VALUE)
       break;
 
     SwitchToThread();
   }
 
+  uv__free(req->u.connect.name);
+  req->u.connect.name = NULL;
   if (pipeHandle != INVALID_HANDLE_VALUE) {
     SET_REQ_SUCCESS(req);
     req->u.connect.pipeHandle = pipeHandle;
@@ -818,18 +848,53 @@
 }
 
 
-void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle,
-    const char* name, uv_connect_cb cb) {
+void uv_pipe_connect(uv_connect_t* req,
+                    uv_pipe_t* handle,
+                    const char* name,
+                    uv_connect_cb cb) {
+  uv_pipe_connect2(req, handle, name, strlen(name), 0, cb);
+}
+
+
+int uv_pipe_connect2(uv_connect_t* req,
+                     uv_pipe_t* handle,
+                     const char* name,
+                     size_t namelen,
+                     unsigned int flags,
+                     uv_connect_cb cb) {
   uv_loop_t* loop = handle->loop;
   int err, nameSize;
   HANDLE pipeHandle = INVALID_HANDLE_VALUE;
   DWORD duplex_flags;
 
+  if (flags & ~UV_PIPE_NO_TRUNCATE) {
+    return UV_EINVAL;
+  }
+
+  if (name == NULL) {
+    return UV_EINVAL;
+  }
+
+  if (namelen == 0) {
+    return UV_EINVAL;
+  }
+
+  if (*name == '\0') {
+    return UV_EINVAL;
+  }
+
+  if (flags & UV_PIPE_NO_TRUNCATE) {
+    if (namelen > 256) {
+      return UV_EINVAL;
+    }
+  }
+
   UV_REQ_INIT(req, UV_CONNECT);
   req->handle = (uv_stream_t*) handle;
   req->cb = cb;
   req->u.connect.pipeHandle = INVALID_HANDLE_VALUE;
   req->u.connect.duplex_flags = 0;
+  req->u.connect.name = NULL;
 
   if (handle->flags & UV_HANDLE_PIPESERVER) {
     err = ERROR_INVALID_PARAMETER;
@@ -861,10 +926,19 @@
   pipeHandle = open_named_pipe(handle->name, &duplex_flags);
   if (pipeHandle == INVALID_HANDLE_VALUE) {
     if (GetLastError() == ERROR_PIPE_BUSY) {
+      req->u.connect.name = (WCHAR *)uv__malloc(nameSize);
+      if (!req->u.connect.name) {
+        uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+      }
+
+      memcpy(req->u.connect.name, handle->name, nameSize);
+
       /* Wait for the server to make a pipe instance available. */
       if (!QueueUserWorkItem(&pipe_connect_thread_proc,
                              req,
                              WT_EXECUTELONGFUNCTION)) {
+        uv__free(req->u.connect.name);
+        req->u.connect.name = NULL;
         err = GetLastError();
         goto error;
       }
@@ -872,7 +946,7 @@
       REGISTER_HANDLE_REQ(loop, handle, req);
       handle->reqs_pending++;
 
-      return;
+      return 0;
     }
 
     err = GetLastError();
@@ -885,7 +959,7 @@
   uv__insert_pending_req(loop, (uv_req_t*) req);
   handle->reqs_pending++;
   REGISTER_HANDLE_REQ(loop, handle, req);
-  return;
+  return 0;
 
 error:
   if (handle->name) {
@@ -901,7 +975,7 @@
   uv__insert_pending_req(loop, (uv_req_t*) req);
   handle->reqs_pending++;
   REGISTER_HANDLE_REQ(loop, handle, req);
-  return;
+  return 0;
 }
 
 
@@ -1052,28 +1126,29 @@
   uv_loop_t* loop = server->loop;
   uv_pipe_t* pipe_client;
   uv_pipe_accept_t* req;
-  QUEUE* q;
+  struct uv__queue* q;
   uv__ipc_xfer_queue_item_t* item;
   int err;
 
   if (server->ipc) {
-    if (QUEUE_EMPTY(&server->pipe.conn.ipc_xfer_queue)) {
+    if (uv__queue_empty(&server->pipe.conn.ipc_xfer_queue)) {
       /* No valid pending sockets. */
       return WSAEWOULDBLOCK;
     }
 
-    q = QUEUE_HEAD(&server->pipe.conn.ipc_xfer_queue);
-    QUEUE_REMOVE(q);
+    q = uv__queue_head(&server->pipe.conn.ipc_xfer_queue);
+    uv__queue_remove(q);
     server->pipe.conn.ipc_xfer_queue_length--;
-    item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member);
+    item = uv__queue_data(q, uv__ipc_xfer_queue_item_t, member);
 
     err = uv__tcp_xfer_import(
         (uv_tcp_t*) client, item->xfer_type, &item->xfer_info);
+    
+    uv__free(item);
+    
     if (err != 0)
       return err;
 
-    uv__free(item);
-
   } else {
     pipe_client = (uv_pipe_t*) client;
     uv__pipe_connection_init(pipe_client);
@@ -1640,9 +1715,13 @@
   /* If the both ends of the IPC pipe are owned by the same process,
    * the remote end pid may not yet be set. If so, do it here.
    * TODO: this is weird; it'd probably better to use a handshake. */
-  if (*pid == 0)
-    *pid = GetCurrentProcessId();
-
+  if (*pid == 0) {
+    GetNamedPipeClientProcessId(handle->handle, pid);
+    if (*pid == GetCurrentProcessId()) {
+      GetNamedPipeServerProcessId(handle->handle, pid);
+    }
+  }
+  
   return *pid;
 }
 
@@ -1814,7 +1893,7 @@
   item->xfer_type = xfer_type;
   item->xfer_info = *xfer_info;
 
-  QUEUE_INSERT_TAIL(&handle->pipe.conn.ipc_xfer_queue, &item->member);
+  uv__queue_insert_tail(&handle->pipe.conn.ipc_xfer_queue, &item->member);
   handle->pipe.conn.ipc_xfer_queue_length++;
 }
 
@@ -2071,9 +2150,9 @@
     uv__queue_non_overlapped_write(handle);
   }
 
-  if (handle->stream.conn.write_reqs_pending == 0)
-    if (handle->flags & UV_HANDLE_SHUTTING)
-      uv__pipe_shutdown(loop, handle, handle->stream.conn.shutdown_req);
+  if (handle->stream.conn.write_reqs_pending == 0 &&
+      uv__is_stream_shutting(handle))
+    uv__pipe_shutdown(loop, handle, handle->stream.conn.shutdown_req);
 
   DECREASE_PENDING_REQ_COUNT(handle);
 }
@@ -2128,7 +2207,10 @@
   if (REQ_SUCCESS(req)) {
     pipeHandle = req->u.connect.pipeHandle;
     duplex_flags = req->u.connect.duplex_flags;
-    err = uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags);
+    if (handle->flags & UV_HANDLE_CLOSING)
+      err = UV_ECANCELED;
+    else
+      err = uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags);
     if (err)
       CloseHandle(pipeHandle);
   } else {
@@ -2151,7 +2233,6 @@
 
   /* Clear the shutdown_req field so we don't go here again. */
   handle->stream.conn.shutdown_req = NULL;
-  handle->flags &= ~UV_HANDLE_SHUTTING;
   UNREGISTER_HANDLE_REQ(loop, handle, req);
 
   if (handle->flags & UV_HANDLE_CLOSING) {
@@ -2344,7 +2425,10 @@
 
   if (pipe->ipc) {
     assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE));
-    pipe->pipe.conn.ipc_remote_pid = uv_os_getppid();
+    GetNamedPipeClientProcessId(os_handle, &pipe->pipe.conn.ipc_remote_pid);
+    if (pipe->pipe.conn.ipc_remote_pid == GetCurrentProcessId()) {
+      GetNamedPipeServerProcessId(os_handle, &pipe->pipe.conn.ipc_remote_pid);
+    }
     assert(pipe->pipe.conn.ipc_remote_pid != (DWORD)(uv_pid_t) -1);
   }
   return 0;
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/poll.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/poll.cpp
index bd531b0..7fec2b9 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/poll.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/poll.cpp
@@ -425,9 +425,8 @@
     return uv_translate_sys_error(WSAGetLastError());
 
 /* Try to obtain a base handle for the socket. This increases this chances that
- * we find an AFD handle and are able to use the fast poll mechanism. This will
- * always fail on windows XP/2k3, since they don't support the. SIO_BASE_HANDLE
- * ioctl. */
+ * we find an AFD handle and are able to use the fast poll mechanism.
+ */
 #ifndef NDEBUG
   base_socket = INVALID_SOCKET;
 #endif
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/process.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/process.cpp
index 8e7835a..18816d3 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/process.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/process.cpp
@@ -145,7 +145,6 @@
   handle->exit_signal = 0;
   handle->wait_handle = INVALID_HANDLE_VALUE;
   handle->process_handle = INVALID_HANDLE_VALUE;
-  handle->child_stdio_buffer = NULL;
   handle->exit_cb_pending = 0;
 
   UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT);
@@ -948,9 +947,11 @@
   STARTUPINFOW startup;
   PROCESS_INFORMATION info;
   DWORD process_flags;
+  BYTE* child_stdio_buffer;
 
   uv__process_init(loop, process);
   process->exit_cb = options->exit_cb;
+  child_stdio_buffer = NULL;
 
   if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
     return UV_ENOTSUP;
@@ -1041,7 +1042,7 @@
     }
   }
 
-  err = uv__stdio_create(loop, options, &process->child_stdio_buffer);
+  err = uv__stdio_create(loop, options, &child_stdio_buffer);
   if (err)
     goto done;
 
@@ -1060,12 +1061,12 @@
   startup.lpTitle = NULL;
   startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
 
-  startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer);
-  startup.lpReserved2 = (BYTE*) process->child_stdio_buffer;
+  startup.cbReserved2 = uv__stdio_size(child_stdio_buffer);
+  startup.lpReserved2 = (BYTE*) child_stdio_buffer;
 
-  startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0);
-  startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1);
-  startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2);
+  startup.hStdInput = uv__stdio_handle(child_stdio_buffer, 0);
+  startup.hStdOutput = uv__stdio_handle(child_stdio_buffer, 1);
+  startup.hStdError = uv__stdio_handle(child_stdio_buffer, 2);
 
   process_flags = CREATE_UNICODE_ENVIRONMENT;
 
@@ -1179,10 +1180,10 @@
   uv__free(env);
   uv__free(alloc_path);
 
-  if (process->child_stdio_buffer != NULL) {
+  if (child_stdio_buffer != NULL) {
     /* Clean up child stdio handles. */
-    uv__stdio_destroy(process->child_stdio_buffer);
-    process->child_stdio_buffer = NULL;
+    uv__stdio_destroy(child_stdio_buffer);
+    child_stdio_buffer = NULL;
   }
 
   return uv_translate_sys_error(err);
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/stream.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/stream.cpp
index 292bf58..7bf9ca3 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/stream.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/stream.cpp
@@ -204,7 +204,7 @@
   uv_loop_t* loop = handle->loop;
 
   if (!(handle->flags & UV_HANDLE_WRITABLE) ||
-      handle->flags & UV_HANDLE_SHUTTING ||
+      uv__is_stream_shutting(handle) ||
       uv__is_closing(handle)) {
     return UV_ENOTCONN;
   }
@@ -214,7 +214,6 @@
   req->cb = cb;
 
   handle->flags &= ~UV_HANDLE_WRITABLE;
-  handle->flags |= UV_HANDLE_SHUTTING;
   handle->stream.conn.shutdown_req = req;
   handle->reqs_pending++;
   REGISTER_HANDLE_REQ(loop, handle, req);
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/tcp.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/tcp.cpp
index 4cccee4..d8da4d9 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/tcp.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/tcp.cpp
@@ -30,14 +30,6 @@
 
 
 /*
- * Threshold of active tcp streams for which to preallocate tcp read buffers.
- * (Due to node slab allocator performing poorly under this pattern,
- *  the optimization is temporarily disabled (threshold=0).  This will be
- *  revisited once node allocator is improved.)
- */
-const unsigned int uv_active_tcp_streams_threshold = 0;
-
-/*
  * Number of simultaneous pending AcceptEx calls.
  */
 const unsigned int uv_simultaneous_server_accepts = 32;
@@ -183,14 +175,14 @@
     sock = socket(domain, SOCK_STREAM, 0);
     if (sock == INVALID_SOCKET) {
       err = WSAGetLastError();
-      QUEUE_REMOVE(&handle->handle_queue);
+      uv__queue_remove(&handle->handle_queue);
       return uv_translate_sys_error(err);
     }
 
     err = uv__tcp_set_socket(handle->loop, handle, sock, domain, 0);
     if (err) {
       closesocket(sock);
-      QUEUE_REMOVE(&handle->handle_queue);
+      uv__queue_remove(&handle->handle_queue);
       return uv_translate_sys_error(err);
     }
 
@@ -214,7 +206,6 @@
   assert(stream->flags & UV_HANDLE_CONNECTION);
 
   stream->stream.conn.shutdown_req = NULL;
-  stream->flags &= ~UV_HANDLE_SHUTTING;
   UNREGISTER_HANDLE_REQ(loop, stream, req);
 
   err = 0;
@@ -274,7 +265,6 @@
   }
 
   uv__handle_close(handle);
-  loop->active_tcp_streams--;
 }
 
 
@@ -484,26 +474,9 @@
   req = &handle->read_req;
   memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
 
-  /*
-   * Preallocate a read buffer if the number of active streams is below
-   * the threshold.
-  */
-  if (loop->active_tcp_streams < uv_active_tcp_streams_threshold) {
-    handle->flags &= ~UV_HANDLE_ZERO_READ;
-    handle->tcp.conn.read_buffer = uv_buf_init(NULL, 0);
-    handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->tcp.conn.read_buffer);
-    if (handle->tcp.conn.read_buffer.base == NULL ||
-        handle->tcp.conn.read_buffer.len == 0) {
-      handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &handle->tcp.conn.read_buffer);
-      return;
-    }
-    assert(handle->tcp.conn.read_buffer.base != NULL);
-    buf = handle->tcp.conn.read_buffer;
-  } else {
-    handle->flags |= UV_HANDLE_ZERO_READ;
-    buf.base = (char*) &uv_zero_;
-    buf.len = 0;
-  }
+  handle->flags |= UV_HANDLE_ZERO_READ;
+  buf.base = (char*) &uv_zero_;
+  buf.len = 0;
 
   /* Prepare the overlapped structure. */
   memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
@@ -550,7 +523,7 @@
   struct linger l = { 1, 0 };
 
   /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */
-  if (handle->flags & UV_HANDLE_SHUTTING)
+  if (uv__is_stream_shutting(handle))
     return UV_EINVAL;
 
   if (0 != setsockopt(handle->socket, SOL_SOCKET, SO_LINGER, (const char*)&l, sizeof(l)))
@@ -654,7 +627,6 @@
 
 
 int uv__tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
-  uv_loop_t* loop = server->loop;
   int err = 0;
   int family;
 
@@ -716,8 +688,6 @@
     }
   }
 
-  loop->active_tcp_streams++;
-
   return err;
 }
 
@@ -1163,7 +1133,7 @@
       closesocket(handle->socket);
       handle->socket = INVALID_SOCKET;
     }
-    if (handle->flags & UV_HANDLE_SHUTTING)
+    if (uv__is_stream_shutting(handle))
       uv__process_tcp_shutdown_req(loop,
                                    handle,
                                    handle->stream.conn.shutdown_req);
@@ -1248,7 +1218,6 @@
                           0) == 0) {
       uv__connection_init((uv_stream_t*)handle);
       handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
-      loop->active_tcp_streams++;
     } else {
       err = WSAGetLastError();
     }
@@ -1331,7 +1300,6 @@
     tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
   }
 
-  tcp->loop->active_tcp_streams++;
   return 0;
 }
 
@@ -1432,7 +1400,7 @@
                                                 uv_tcp_non_ifs_lsp_ipv4;
 
   /* If there are non-ifs LSPs then try to obtain a base handle for the socket.
-   * This will always fail on Windows XP/3k. */
+   */
   if (non_ifs_lsp) {
     DWORD bytes;
     if (WSAIoctl(socket,
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/thread.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/thread.cpp
index 9ad60c9..03b33e9 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/thread.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/thread.cpp
@@ -180,6 +180,81 @@
   return UV_EIO;
 }
 
+int uv_thread_setaffinity(uv_thread_t* tid,
+                          char* cpumask,
+                          char* oldmask,
+                          size_t mask_size) {
+  int i;
+  HANDLE hproc;
+  DWORD_PTR procmask;
+  DWORD_PTR sysmask;
+  DWORD_PTR threadmask;
+  DWORD_PTR oldthreadmask;
+  int cpumasksize;
+
+  cpumasksize = uv_cpumask_size();
+  assert(cpumasksize > 0);
+  if (mask_size < (size_t)cpumasksize)
+    return UV_EINVAL;
+
+  hproc = GetCurrentProcess();
+  if (!GetProcessAffinityMask(hproc, &procmask, &sysmask))
+    return uv_translate_sys_error(GetLastError());
+
+  threadmask = 0;
+  for (i = 0; i < cpumasksize; i++) {
+    if (cpumask[i]) {
+      if (procmask & (1LL << i))
+        threadmask |= 1LL << i;
+      else
+        return UV_EINVAL;
+    }
+  }
+
+  oldthreadmask = SetThreadAffinityMask(*tid, threadmask);
+  if (oldthreadmask == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  if (oldmask != NULL) {
+    for (i = 0; i < cpumasksize; i++)
+      oldmask[i] = (oldthreadmask >> i) & 1;
+  }
+
+  return 0;
+}
+
+int uv_thread_getaffinity(uv_thread_t* tid,
+                          char* cpumask,
+                          size_t mask_size) {
+  int i;
+  HANDLE hproc;
+  DWORD_PTR procmask;
+  DWORD_PTR sysmask;
+  DWORD_PTR threadmask;
+  int cpumasksize;
+
+  cpumasksize = uv_cpumask_size();
+  assert(cpumasksize > 0);
+  if (mask_size < (size_t)cpumasksize)
+    return UV_EINVAL;
+
+  hproc = GetCurrentProcess();
+  if (!GetProcessAffinityMask(hproc, &procmask, &sysmask))
+    return uv_translate_sys_error(GetLastError());
+
+  threadmask = SetThreadAffinityMask(*tid, procmask);
+  if (threadmask == 0 || SetThreadAffinityMask(*tid, threadmask) == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  for (i = 0; i < cpumasksize; i++)
+    cpumask[i] = (threadmask >> i) & 1;
+
+  return 0;
+}
+
+int uv_thread_getcpu(void) {
+  return GetCurrentProcessorNumber();
+}
 
 uv_thread_t uv_thread_self(void) {
   uv_thread_t key;
@@ -374,6 +449,7 @@
     abort();
 }
 
+
 int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
   if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6)))
     return 0;
@@ -383,69 +459,6 @@
 }
 
 
-int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
-  int err;
-
-  barrier->n = count;
-  barrier->count = 0;
-
-  err = uv_mutex_init(&barrier->mutex);
-  if (err)
-    return err;
-
-  err = uv_sem_init(&barrier->turnstile1, 0);
-  if (err)
-    goto error2;
-
-  err = uv_sem_init(&barrier->turnstile2, 1);
-  if (err)
-    goto error;
-
-  return 0;
-
-error:
-  uv_sem_destroy(&barrier->turnstile1);
-error2:
-  uv_mutex_destroy(&barrier->mutex);
-  return err;
-
-}
-
-
-void uv_barrier_destroy(uv_barrier_t* barrier) {
-  uv_sem_destroy(&barrier->turnstile2);
-  uv_sem_destroy(&barrier->turnstile1);
-  uv_mutex_destroy(&barrier->mutex);
-}
-
-
-int uv_barrier_wait(uv_barrier_t* barrier) {
-  int serial_thread;
-
-  uv_mutex_lock(&barrier->mutex);
-  if (++barrier->count == barrier->n) {
-    uv_sem_wait(&barrier->turnstile2);
-    uv_sem_post(&barrier->turnstile1);
-  }
-  uv_mutex_unlock(&barrier->mutex);
-
-  uv_sem_wait(&barrier->turnstile1);
-  uv_sem_post(&barrier->turnstile1);
-
-  uv_mutex_lock(&barrier->mutex);
-  serial_thread = (--barrier->count == 0);
-  if (serial_thread) {
-    uv_sem_wait(&barrier->turnstile1);
-    uv_sem_post(&barrier->turnstile2);
-  }
-  uv_mutex_unlock(&barrier->mutex);
-
-  uv_sem_wait(&barrier->turnstile2);
-  uv_sem_post(&barrier->turnstile2);
-  return serial_thread;
-}
-
-
 int uv_key_create(uv_key_t* key) {
   key->tls_index = TlsAlloc();
   if (key->tls_index == TLS_OUT_OF_INDEXES)
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/tty.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/tty.cpp
index 9753784..9bb3d9e 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/tty.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/tty.cpp
@@ -25,12 +25,7 @@
 #include <io.h>
 #include <string.h>
 #include <stdlib.h>
-
-#if defined(_MSC_VER) && _MSC_VER < 1600
-# include "uv/stdint-msvc2008.h"
-#else
-# include <stdint.h>
-#endif
+#include <stdint.h>
 
 #ifndef COMMON_LVB_REVERSE_VIDEO
 # define COMMON_LVB_REVERSE_VIDEO 0x4000
@@ -179,14 +174,14 @@
                                        0);
   if (uv__tty_console_handle != INVALID_HANDLE_VALUE) {
     CONSOLE_SCREEN_BUFFER_INFO sb_info;
-    QueueUserWorkItem(uv__tty_console_resize_message_loop_thread,
-                      NULL,
-                      WT_EXECUTELONGFUNCTION);
     uv_mutex_init(&uv__tty_console_resize_mutex);
     if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) {
       uv__tty_console_width = sb_info.dwSize.X;
       uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
     }
+    QueueUserWorkItem(uv__tty_console_resize_message_loop_thread,
+                      NULL,
+                      WT_EXECUTELONGFUNCTION);
   }
 }
 
@@ -2243,11 +2238,11 @@
 
 
   handle->stream.conn.write_reqs_pending--;
-  if (handle->stream.conn.write_reqs_pending == 0)
-    if (handle->flags & UV_HANDLE_SHUTTING)
-      uv__process_tty_shutdown_req(loop,
-                                   handle,
-                                   handle->stream.conn.shutdown_req);
+  if (handle->stream.conn.write_reqs_pending == 0 &&
+      uv__is_stream_shutting(handle))
+    uv__process_tty_shutdown_req(loop,
+                                 handle,
+                                 handle->stream.conn.shutdown_req);
 
   DECREASE_PENDING_REQ_COUNT(handle);
 }
@@ -2278,7 +2273,6 @@
   assert(req);
 
   stream->stream.conn.shutdown_req = NULL;
-  stream->flags &= ~UV_HANDLE_SHUTTING;
   UNREGISTER_HANDLE_REQ(loop, stream, req);
 
   /* TTY shutdown is really just a no-op */
@@ -2308,26 +2302,6 @@
 }
 
 
-/*
- * uv__process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
- * TODO: find a way to remove it
- */
-void uv__process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
-    uv_req_t* raw_req) {
-  abort();
-}
-
-
-/*
- * uv__process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
- * TODO: find a way to remove it
- */
-void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
-    uv_connect_t* req) {
-  abort();
-}
-
-
 int uv_tty_reset_mode(void) {
   /* Not necessary to do anything. */
   return 0;
@@ -2433,7 +2407,6 @@
   height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
 
   uv_mutex_lock(&uv__tty_console_resize_mutex);
-  assert(uv__tty_console_width != -1 && uv__tty_console_height != -1);
   if (width != uv__tty_console_width || height != uv__tty_console_height) {
     uv__tty_console_width = width;
     uv__tty_console_height = height;
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/udp.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/udp.cpp
index eaebc1e..eab5384 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/udp.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/udp.cpp
@@ -29,11 +29,6 @@
 #include "req-inl.h"
 
 
-/*
- * Threshold of active udp streams for which to preallocate udp read buffers.
- */
-const unsigned int uv_active_udp_streams_threshold = 0;
-
 /* A zero-size buffer for use by uv_udp_read */
 static char uv_zero_[] = "";
 int uv_udp_getpeername(const uv_udp_t* handle,
@@ -151,14 +146,14 @@
     sock = socket(domain, SOCK_DGRAM, 0);
     if (sock == INVALID_SOCKET) {
       err = WSAGetLastError();
-      QUEUE_REMOVE(&handle->handle_queue);
+      uv__queue_remove(&handle->handle_queue);
       return uv_translate_sys_error(err);
     }
 
     err = uv__udp_set_socket(handle->loop, handle, sock, domain);
     if (err) {
       closesocket(sock);
-      QUEUE_REMOVE(&handle->handle_queue);
+      uv__queue_remove(&handle->handle_queue);
       return uv_translate_sys_error(err);
     }
   }
@@ -276,84 +271,35 @@
   req = &handle->recv_req;
   memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
 
-  /*
-   * Preallocate a read buffer if the number of active streams is below
-   * the threshold.
-  */
-  if (loop->active_udp_streams < uv_active_udp_streams_threshold) {
-    handle->flags &= ~UV_HANDLE_ZERO_READ;
+  handle->flags |= UV_HANDLE_ZERO_READ;
 
-    handle->recv_buffer = uv_buf_init(NULL, 0);
-    handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &handle->recv_buffer);
-    if (handle->recv_buffer.base == NULL || handle->recv_buffer.len == 0) {
-      handle->recv_cb(handle, UV_ENOBUFS, &handle->recv_buffer, NULL, 0);
-      return;
-    }
-    assert(handle->recv_buffer.base != NULL);
+  buf.base = (char*) uv_zero_;
+  buf.len = 0;
+  flags = MSG_PEEK;
 
-    buf = handle->recv_buffer;
-    memset(&handle->recv_from, 0, sizeof handle->recv_from);
-    handle->recv_from_len = sizeof handle->recv_from;
-    flags = 0;
+  result = handle->func_wsarecv(handle->socket,
+                                (WSABUF*) &buf,
+                                1,
+                                &bytes,
+                                &flags,
+                                &req->u.io.overlapped,
+                                NULL);
 
-    result = handle->func_wsarecvfrom(handle->socket,
-                                      (WSABUF*) &buf,
-                                      1,
-                                      &bytes,
-                                      &flags,
-                                      (struct sockaddr*) &handle->recv_from,
-                                      &handle->recv_from_len,
-                                      &req->u.io.overlapped,
-                                      NULL);
-
-    if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
-      /* Process the req without IOCP. */
-      handle->flags |= UV_HANDLE_READ_PENDING;
-      req->u.io.overlapped.InternalHigh = bytes;
-      handle->reqs_pending++;
-      uv__insert_pending_req(loop, req);
-    } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
-      /* The req will be processed with IOCP. */
-      handle->flags |= UV_HANDLE_READ_PENDING;
-      handle->reqs_pending++;
-    } else {
-      /* Make this req pending reporting an error. */
-      SET_REQ_ERROR(req, WSAGetLastError());
-      uv__insert_pending_req(loop, req);
-      handle->reqs_pending++;
-    }
-
+  if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+    /* Process the req without IOCP. */
+    handle->flags |= UV_HANDLE_READ_PENDING;
+    req->u.io.overlapped.InternalHigh = bytes;
+    handle->reqs_pending++;
+    uv__insert_pending_req(loop, req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+    /* The req will be processed with IOCP. */
+    handle->flags |= UV_HANDLE_READ_PENDING;
+    handle->reqs_pending++;
   } else {
-    handle->flags |= UV_HANDLE_ZERO_READ;
-
-    buf.base = (char*) uv_zero_;
-    buf.len = 0;
-    flags = MSG_PEEK;
-
-    result = handle->func_wsarecv(handle->socket,
-                                  (WSABUF*) &buf,
-                                  1,
-                                  &bytes,
-                                  &flags,
-                                  &req->u.io.overlapped,
-                                  NULL);
-
-    if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
-      /* Process the req without IOCP. */
-      handle->flags |= UV_HANDLE_READ_PENDING;
-      req->u.io.overlapped.InternalHigh = bytes;
-      handle->reqs_pending++;
-      uv__insert_pending_req(loop, req);
-    } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
-      /* The req will be processed with IOCP. */
-      handle->flags |= UV_HANDLE_READ_PENDING;
-      handle->reqs_pending++;
-    } else {
-      /* Make this req pending reporting an error. */
-      SET_REQ_ERROR(req, WSAGetLastError());
-      uv__insert_pending_req(loop, req);
-      handle->reqs_pending++;
-    }
+    /* Make this req pending reporting an error. */
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv__insert_pending_req(loop, req);
+    handle->reqs_pending++;
   }
 }
 
@@ -376,7 +322,6 @@
 
   handle->flags |= UV_HANDLE_READING;
   INCREASE_ACTIVE_COUNT(loop, handle);
-  loop->active_udp_streams++;
 
   handle->recv_cb = recv_cb;
   handle->alloc_cb = alloc_cb;
@@ -393,7 +338,6 @@
 int uv__udp_recv_stop(uv_udp_t* handle) {
   if (handle->flags & UV_HANDLE_READING) {
     handle->flags &= ~UV_HANDLE_READING;
-    handle->loop->active_udp_streams--;
     DECREASE_ACTIVE_COUNT(loop, handle);
   }
 
@@ -497,57 +441,68 @@
     DWORD bytes, err, flags;
     struct sockaddr_storage from;
     int from_len;
+    int count;
 
-    /* Do a nonblocking receive.
-     * TODO: try to read multiple datagrams at once. FIONREAD maybe? */
-    buf = uv_buf_init(NULL, 0);
-    handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf);
-    if (buf.base == NULL || buf.len == 0) {
-      handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
-      goto done;
-    }
-    assert(buf.base != NULL);
+    /* Prevent loop starvation when the data comes in as fast as
+     * (or faster than) we can read it. */
+    count = 32;
 
-    memset(&from, 0, sizeof from);
-    from_len = sizeof from;
+    do {
+      /* Do at most `count` nonblocking receive. */
+      buf = uv_buf_init(NULL, 0);
+      handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf);
+      if (buf.base == NULL || buf.len == 0) {
+        handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
+        goto done;
+      }
 
-    flags = 0;
+      memset(&from, 0, sizeof from);
+      from_len = sizeof from;
 
-    if (WSARecvFrom(handle->socket,
-                    (WSABUF*)&buf,
-                    1,
-                    &bytes,
-                    &flags,
-                    (struct sockaddr*) &from,
-                    &from_len,
-                    NULL,
-                    NULL) != SOCKET_ERROR) {
+      flags = 0;
 
-      /* Message received */
-      handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 0);
-    } else {
-      err = WSAGetLastError();
-      if (err == WSAEMSGSIZE) {
-        /* Message truncated */
-        handle->recv_cb(handle,
-                        bytes,
-                        &buf,
-                        (const struct sockaddr*) &from,
-                        UV_UDP_PARTIAL);
-      } else if (err == WSAEWOULDBLOCK) {
-        /* Kernel buffer empty */
-        handle->recv_cb(handle, 0, &buf, NULL, 0);
-      } else if (err == WSAECONNRESET || err == WSAENETRESET) {
-        /* WSAECONNRESET/WSANETRESET is ignored because this just indicates
-         * that a previous sendto operation failed.
-         */
-        handle->recv_cb(handle, 0, &buf, NULL, 0);
+      if (WSARecvFrom(handle->socket,
+                      (WSABUF*)&buf,
+                      1,
+                      &bytes,
+                      &flags,
+                      (struct sockaddr*) &from,
+                      &from_len,
+                      NULL,
+                      NULL) != SOCKET_ERROR) {
+
+        /* Message received */
+        err = ERROR_SUCCESS;
+        handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 0);
       } else {
-        /* Any other error that we want to report back to the user. */
-        uv_udp_recv_stop(handle);
-        handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0);
+        err = WSAGetLastError();
+        if (err == WSAEMSGSIZE) {
+          /* Message truncated */
+          handle->recv_cb(handle,
+                          bytes,
+                          &buf,
+                          (const struct sockaddr*) &from,
+                          UV_UDP_PARTIAL);
+        } else if (err == WSAEWOULDBLOCK) {
+          /* Kernel buffer empty */
+          handle->recv_cb(handle, 0, &buf, NULL, 0);
+        } else if (err == WSAECONNRESET || err == WSAENETRESET) {
+          /* WSAECONNRESET/WSANETRESET is ignored because this just indicates
+           * that a previous sendto operation failed.
+           */
+          handle->recv_cb(handle, 0, &buf, NULL, 0);
+        } else {
+          /* Any other error that we want to report back to the user. */
+          uv_udp_recv_stop(handle);
+          handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0);
+        }
       }
     }
+    while (err == ERROR_SUCCESS &&
+           count-- > 0 &&
+           /* The recv_cb callback may decide to pause or close the handle. */
+           (handle->flags & UV_HANDLE_READING) &&
+           !(handle->flags & UV_HANDLE_READ_PENDING));
   }
 
 done:
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/util.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/util.cpp
index d9888ae..4b76417 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/util.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/win/util.cpp
@@ -31,6 +31,7 @@
 #include "internal.h"
 
 /* clang-format off */
+#include <sysinfoapi.h>
 #include <winsock2.h>
 #include <winperf.h>
 #include <iphlpapi.h>
@@ -72,7 +73,9 @@
 static CRITICAL_SECTION process_title_lock;
 
 #pragma comment(lib, "Advapi32.lib")
+#pragma comment(lib, "Dbghelp.lib")
 #pragma comment(lib, "IPHLPAPI.lib")
+#pragma comment(lib, "Ole32.lib")
 #pragma comment(lib, "Psapi.lib")
 #pragma comment(lib, "Userenv.lib")
 #pragma comment(lib, "kernel32.lib")
@@ -129,9 +132,6 @@
     goto error;
   }
 
-  /* utf16_len contains the length, *not* including the terminating null. */
-  utf16_buffer[utf16_len] = L'\0';
-
   /* Convert to UTF-8 */
   utf8_len = WideCharToMultiByte(CP_UTF8,
                                  0,
@@ -159,6 +159,51 @@
 }
 
 
+static int uv__cwd(WCHAR** buf, DWORD *len) {
+  WCHAR* p;
+  DWORD n;
+  DWORD t;
+
+  t = GetCurrentDirectoryW(0, NULL);
+  for (;;) {
+    if (t == 0)
+      return uv_translate_sys_error(GetLastError());
+
+    /* |t| is the size of the buffer _including_ nul. */
+    p = (WCHAR *)uv__malloc(t * sizeof(*p));
+    if (p == NULL)
+      return UV_ENOMEM;
+
+    /* |n| is the size of the buffer _excluding_ nul but _only on success_.
+     * If |t| was too small because another thread changed the working
+     * directory, |n| is the size the buffer should be _including_ nul.
+     * It therefore follows we must resize when n >= t and fail when n == 0.
+     */
+    n = GetCurrentDirectoryW(t, p);
+    if (n > 0)
+      if (n < t)
+        break;
+
+    uv__free(p);
+    t = n;
+  }
+
+  /* The returned directory should not have a trailing slash, unless it points
+   * at a drive root, like c:\. Remove it if needed.
+   */
+  t = n - 1;
+  if (p[t] == L'\\' && !(n == 3 && p[1] == L':')) {
+    p[t] = L'\0';
+    n = t;
+  }
+
+  *buf = p;
+  *len = n;
+
+  return 0;
+}
+
+
 int uv_cwd(char* buffer, size_t* size) {
   DWORD utf16_len;
   WCHAR *utf16_buffer;
@@ -168,30 +213,9 @@
     return UV_EINVAL;
   }
 
-  utf16_len = GetCurrentDirectoryW(0, NULL);
-  if (utf16_len == 0) {
-    return uv_translate_sys_error(GetLastError());
-  }
-  utf16_buffer = (WCHAR*)uv__malloc(utf16_len * sizeof(WCHAR));
-  if (utf16_buffer == NULL) {
-    return UV_ENOMEM;
-  }
-
-  utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
-  if (utf16_len == 0) {
-    uv__free(utf16_buffer);
-    return uv_translate_sys_error(GetLastError());
-  }
-
-  /* utf16_len contains the length, *not* including the terminating null. */
-  utf16_buffer[utf16_len] = L'\0';
-
-  /* The returned directory should not have a trailing slash, unless it points
-   * at a drive root, like c:\. Remove it if needed. */
-  if (utf16_buffer[utf16_len - 1] == L'\\' &&
-      !(utf16_len == 3 && utf16_buffer[1] == L':')) {
-    utf16_len--;
-    utf16_buffer[utf16_len] = L'\0';
+  r = uv__cwd(&utf16_buffer, &utf16_len);
+  if (r < 0) {
+    return r;
   }
 
   /* Check how much space we need */
@@ -234,8 +258,9 @@
 
 int uv_chdir(const char* dir) {
   WCHAR *utf16_buffer;
-  size_t utf16_len, new_utf16_len;
+  DWORD utf16_len;
   WCHAR drive_letter, env_var[4];
+  int r;
 
   if (dir == NULL) {
     return UV_EINVAL;
@@ -270,32 +295,22 @@
     return uv_translate_sys_error(GetLastError());
   }
 
+  /* uv__cwd() will return a new buffer. */
+  uv__free(utf16_buffer);
+  utf16_buffer = NULL;
+
   /* Windows stores the drive-local path in an "hidden" environment variable,
    * which has the form "=C:=C:\Windows". SetCurrentDirectory does not update
    * this, so we'll have to do it. */
-  new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
-  if (new_utf16_len > utf16_len ) {
-    uv__free(utf16_buffer);
-    utf16_buffer = (WCHAR*)uv__malloc(new_utf16_len * sizeof(WCHAR));
-    if (utf16_buffer == NULL) {
-      /* When updating the environment variable fails, return UV_OK anyway.
-       * We did successfully change current working directory, only updating
-       * hidden env variable failed. */
-      return 0;
-    }
-    new_utf16_len = GetCurrentDirectoryW(new_utf16_len, utf16_buffer);
-  }
-  if (utf16_len == 0) {
-    uv__free(utf16_buffer);
+  r = uv__cwd(&utf16_buffer, &utf16_len);
+  if (r == UV_ENOMEM) {
+    /* When updating the environment variable fails, return UV_OK anyway.
+     * We did successfully change current working directory, only updating
+     * hidden env variable failed. */
     return 0;
   }
-
-  /* The returned directory should not have a trailing slash, unless it points
-   * at a drive root, like c:\. Remove it if needed. */
-  if (utf16_buffer[utf16_len - 1] == L'\\' &&
-      !(utf16_len == 3 && utf16_buffer[1] == L':')) {
-    utf16_len--;
-    utf16_buffer[utf16_len] = L'\0';
+  if (r < 0) {
+    return r;
   }
 
   if (utf16_len < 2 || utf16_buffer[1] != L':') {
@@ -338,7 +353,7 @@
   memory_status.dwLength = sizeof(memory_status);
 
   if (!GlobalMemoryStatusEx(&memory_status)) {
-     return -1;
+     return 0;
   }
 
   return (uint64_t)memory_status.ullAvailPhys;
@@ -350,7 +365,7 @@
   memory_status.dwLength = sizeof(memory_status);
 
   if (!GlobalMemoryStatusEx(&memory_status)) {
-    return -1;
+    return 0;
   }
 
   return (uint64_t)memory_status.ullTotalPhys;
@@ -362,6 +377,11 @@
 }
 
 
+uint64_t uv_get_available_memory(void) {
+  return uv_get_free_memory();
+}
+
+
 uv_pid_t uv_os_getpid(void) {
   return GetCurrentProcessId();
 }
@@ -495,11 +515,43 @@
 }
 
 
+/* https://github.com/libuv/libuv/issues/1674 */
+int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) {
+  FILETIME ft;
+  int64_t t;
+
+  if (ts == NULL)
+    return UV_EFAULT;
+
+  switch (clock_id) {
+    case UV_CLOCK_MONOTONIC:
+      uv__once_init();
+      t = uv__hrtime(UV__NANOSEC);
+      ts->tv_sec = t / 1000000000;
+      ts->tv_nsec = t % 1000000000;
+      return 0;
+    case UV_CLOCK_REALTIME:
+      GetSystemTimePreciseAsFileTime(&ft);
+      /* In 100-nanosecond increments from 1601-01-01 UTC because why not? */
+      t = (int64_t) ft.dwHighDateTime << 32 | ft.dwLowDateTime;
+      /* Convert to UNIX epoch, 1970-01-01. Still in 100 ns increments. */
+      t -= 116444736000000000ll;
+      /* Now convert to seconds and nanoseconds. */
+      ts->tv_sec = t / 10000000;
+      ts->tv_nsec = t % 10000000 * 100;
+      return 0;
+  }
+
+  return UV_EINVAL;
+}
+
+
 uint64_t uv_hrtime(void) {
   uv__once_init();
   return uv__hrtime(UV__NANOSEC);
 }
 
+
 uint64_t uv__hrtime(unsigned int scale) {
   LARGE_INTEGER counter;
   double scaled_freq;
@@ -686,71 +738,6 @@
 }
 
 
-static int is_windows_version_or_greater(DWORD os_major,
-                                         DWORD os_minor,
-                                         WORD service_pack_major,
-                                         WORD service_pack_minor) {
-  OSVERSIONINFOEX osvi;
-  DWORDLONG condition_mask = 0;
-  int op = VER_GREATER_EQUAL;
-
-  /* Initialize the OSVERSIONINFOEX structure. */
-  ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
-  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
-  osvi.dwMajorVersion = os_major;
-  osvi.dwMinorVersion = os_minor;
-  osvi.wServicePackMajor = service_pack_major;
-  osvi.wServicePackMinor = service_pack_minor;
-
-  /* Initialize the condition mask. */
-  VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, op);
-  VER_SET_CONDITION(condition_mask, VER_MINORVERSION, op);
-  VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, op);
-  VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, op);
-
-  /* Perform the test. */
-  return (int) VerifyVersionInfo(
-    &osvi,
-    VER_MAJORVERSION | VER_MINORVERSION |
-    VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
-    condition_mask);
-}
-
-
-static int address_prefix_match(int family,
-                                struct sockaddr* address,
-                                struct sockaddr* prefix_address,
-                                int prefix_len) {
-  uint8_t* address_data;
-  uint8_t* prefix_address_data;
-  int i;
-
-  assert(address->sa_family == family);
-  assert(prefix_address->sa_family == family);
-
-  if (family == AF_INET6) {
-    address_data = (uint8_t*) &(((struct sockaddr_in6 *) address)->sin6_addr);
-    prefix_address_data =
-      (uint8_t*) &(((struct sockaddr_in6 *) prefix_address)->sin6_addr);
-  } else {
-    address_data = (uint8_t*) &(((struct sockaddr_in *) address)->sin_addr);
-    prefix_address_data =
-      (uint8_t*) &(((struct sockaddr_in *) prefix_address)->sin_addr);
-  }
-
-  for (i = 0; i < prefix_len >> 3; i++) {
-    if (address_data[i] != prefix_address_data[i])
-      return 0;
-  }
-
-  if (prefix_len % 8)
-    return prefix_address_data[i] ==
-      (address_data[i] & (0xff << (8 - prefix_len % 8)));
-
-  return 1;
-}
-
-
 int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
     int* count_ptr) {
   IP_ADAPTER_ADDRESSES* win_address_buf;
@@ -763,26 +750,13 @@
   uv_interface_address_t* uv_address;
 
   int count;
-
-  int is_vista_or_greater;
   ULONG flags;
 
   *addresses_ptr = NULL;
   *count_ptr = 0;
 
-  is_vista_or_greater = is_windows_version_or_greater(6, 0, 0, 0);
-  if (is_vista_or_greater) {
-    flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
-      GAA_FLAG_SKIP_DNS_SERVER;
-  } else {
-    /* We need at least XP SP1. */
-    if (!is_windows_version_or_greater(5, 1, 1, 0))
-      return UV_ENOTSUP;
-
-    flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
-      GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX;
-  }
-
+  flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
+    GAA_FLAG_SKIP_DNS_SERVER;
 
   /* Fetch the size of the adapters reported by windows, and then get the list
    * itself. */
@@ -947,37 +921,8 @@
 
       sa = unicast_address->Address.lpSockaddr;
 
-      /* XP has no OnLinkPrefixLength field. */
-      if (is_vista_or_greater) {
-        prefix_len =
-          ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength;
-      } else {
-        /* Prior to Windows Vista the FirstPrefix pointed to the list with
-         * single prefix for each IP address assigned to the adapter.
-         * Order of FirstPrefix does not match order of FirstUnicastAddress,
-         * so we need to find corresponding prefix.
-         */
-        IP_ADAPTER_PREFIX* prefix;
-        prefix_len = 0;
-
-        for (prefix = adapter->FirstPrefix; prefix; prefix = prefix->Next) {
-          /* We want the longest matching prefix. */
-          if (prefix->Address.lpSockaddr->sa_family != sa->sa_family ||
-              prefix->PrefixLength <= prefix_len)
-            continue;
-
-          if (address_prefix_match(sa->sa_family, sa,
-              prefix->Address.lpSockaddr, prefix->PrefixLength)) {
-            prefix_len = prefix->PrefixLength;
-          }
-        }
-
-        /* If there is no matching prefix information, return a single-host
-         * subnet mask (e.g. 255.255.255.255 for IPv4).
-         */
-        if (!prefix_len)
-          prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32;
-      }
+      prefix_len =
+        ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength;
 
       memset(uv_address, 0, sizeof *uv_address);
 
@@ -1102,8 +1047,8 @@
   if (r != UV_ENOENT)
     return r;
 
-  /* USERPROFILE is not set, so call uv__getpwuid_r() */
-  r = uv__getpwuid_r(&pwd);
+  /* USERPROFILE is not set, so call uv_os_get_passwd() */
+  r = uv_os_get_passwd(&pwd);
 
   if (r != 0) {
     return r;
@@ -1190,17 +1135,6 @@
 }
 
 
-void uv_os_free_passwd(uv_passwd_t* pwd) {
-  if (pwd == NULL)
-    return;
-
-  uv__free(pwd->username);
-  uv__free(pwd->homedir);
-  pwd->username = NULL;
-  pwd->homedir = NULL;
-}
-
-
 /*
  * Converts a UTF-16 string into a UTF-8 one. The resulting string is
  * null-terminated.
@@ -1297,7 +1231,7 @@
 }
 
 
-int uv__getpwuid_r(uv_passwd_t* pwd) {
+static int uv__getpwuid_r(uv_passwd_t* pwd) {
   HANDLE token;
   wchar_t username[UNLEN + 1];
   wchar_t *path;
@@ -1375,6 +1309,16 @@
 }
 
 
+int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) {
+  return UV_ENOTSUP;
+}
+
+
+int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) {
+  return UV_ENOTSUP;
+}
+
+
 int uv_os_environ(uv_env_item_t** envitems, int* count) {
   wchar_t* env;
   wchar_t* penv;
@@ -1778,6 +1722,22 @@
     RegCloseKey(registry_key);
 
     if (r == ERROR_SUCCESS) {
+      /* Windows 11 shares dwMajorVersion with Windows 10
+       * this workaround tries to disambiguate that by checking
+       * if the dwBuildNumber is from Windows 11 releases (>= 22000).
+       *
+       * This workaround replaces the ProductName key value
+       * from "Windows 10 *" to "Windows 11 *" */
+      if (os_info.dwMajorVersion == 10 &&
+          os_info.dwBuildNumber >= 22000 &&
+          product_name_w_size >= ARRAY_SIZE(L"Windows 10")) {
+        /* If ProductName starts with "Windows 10" */
+        if (wcsncmp(product_name_w, L"Windows 10", ARRAY_SIZE(L"Windows 10") - 1) == 0) {
+          /* Bump 10 to 11 */
+          product_name_w[9] = '1';
+        }
+      }
+
       version_size = WideCharToMultiByte(CP_UTF8,
                                          0,
                                          product_name_w,
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPConnector_parallel.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPConnector_parallel.cpp
index 1d979cb..32654e9 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPConnector_parallel.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPConnector_parallel.cpp
@@ -15,14 +15,6 @@
 
 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(
     std::span<const std::pair<const char*, int>> servers, Logger& logger,
     int timeout) {
@@ -33,18 +25,10 @@
   // 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
@@ -63,12 +47,8 @@
   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
diff --git a/third_party/allwpilib/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPStream.cpp b/third_party/allwpilib/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPStream.cpp
index 920f7b1..ccd7591 100644
--- a/third_party/allwpilib/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPStream.cpp
+++ b/third_party/allwpilib/wpinet/src/main/native/thirdparty/tcpsockets/cpp/TCPStream.cpp
@@ -36,6 +36,8 @@
 
 #include <cerrno>
 
+#include <wpi/StringExtras.h>
+
 using namespace wpi;
 
 TCPStream::TCPStream(int sd, sockaddr_in* address)
@@ -85,12 +87,9 @@
   }
   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
+    wpi::format_to_n_c_str(Buffer, sizeof(Buffer),
+                           "Send() failed: WSA error={}\n", WSAGetLastError());
+
     OutputDebugStringA(Buffer);
     *err = kConnectionReset;
     return 0;
diff --git a/third_party/allwpilib/wpinet/src/netconsoleServer/native/cpp/main.cpp b/third_party/allwpilib/wpinet/src/netconsoleServer/native/cpp/main.cpp
index 0271e36..29c1491 100644
--- a/third_party/allwpilib/wpinet/src/netconsoleServer/native/cpp/main.cpp
+++ b/third_party/allwpilib/wpinet/src/netconsoleServer/native/cpp/main.cpp
@@ -14,6 +14,7 @@
 #include <wpi/MathExtras.h>
 #include <wpi/SmallVector.h>
 #include <wpi/StringExtras.h>
+#include <wpi/bit.h>
 #include <wpi/timestamp.h>
 
 #include "wpinet/raw_uv_ostream.h"
@@ -47,7 +48,8 @@
   std::string_view toCopy = wpi::slice(str, 0, idx + 1);
   if (tcp) {
     // Header is 2 byte len, 1 byte type, 4 byte timestamp, 2 byte sequence num
-    uint32_t ts = wpi::FloatToBits((wpi::Now() - startTime) * 1.0e-6);
+    uint32_t ts =
+        wpi::bit_cast<uint32_t, float>((wpi::Now() - startTime) * 1.0e-6);
     uint16_t len = rem.size() + toCopy.size() + 1 + 4 + 2;
     const uint8_t header[] = {static_cast<uint8_t>((len >> 8) & 0xff),
                               static_cast<uint8_t>(len & 0xff),
@@ -67,6 +69,10 @@
   return true;
 }
 
+// FIXME: clang-tidy reports a false positive for leaking a captured shared_ptr
+//        (clang-analyzer-cplusplus.NewDeleteLeaks)
+
+// NOLINTBEGIN
 static void CopyUdp(uv::Stream& in, std::shared_ptr<uv::Udp> out,
                     bool broadcast) {
   sockaddr_in addr;
@@ -131,6 +137,7 @@
     });
   });
 }
+// NOLINTEND
 
 int main(int argc, char* argv[]) {
   // parse arguments
diff --git a/third_party/allwpilib/wpinet/src/netconsoleTee/native/cpp/main.cpp b/third_party/allwpilib/wpinet/src/netconsoleTee/native/cpp/main.cpp
index a6bdff4..1028992 100644
--- a/third_party/allwpilib/wpinet/src/netconsoleTee/native/cpp/main.cpp
+++ b/third_party/allwpilib/wpinet/src/netconsoleTee/native/cpp/main.cpp
@@ -8,6 +8,7 @@
 #include <wpi/MathExtras.h>
 #include <wpi/SmallVector.h>
 #include <wpi/StringExtras.h>
+#include <wpi/bit.h>
 #include <wpi/timestamp.h>
 
 #include "wpinet/raw_uv_ostream.h"
@@ -38,7 +39,8 @@
   std::string_view toCopy = wpi::slice(str, 0, idx + 1);
   if (tcp) {
     // Header is 2 byte len, 1 byte type, 4 byte timestamp, 2 byte sequence num
-    uint32_t ts = wpi::FloatToBits((wpi::Now() - startTime) * 1.0e-6);
+    uint32_t ts =
+        wpi::bit_cast<uint32_t, float>((wpi::Now() - startTime) * 1.0e-6);
     uint16_t len = rem.size() + toCopy.size() + 1 + 4 + 2;
     const uint8_t header[] = {static_cast<uint8_t>((len >> 8) & 0xff),
                               static_cast<uint8_t>(len & 0xff),
@@ -58,6 +60,10 @@
   return true;
 }
 
+// FIXME: clang-tidy reports a false positive for leaking a captured shared_ptr
+//        (clang-analyzer-cplusplus.NewDeleteLeaks)
+
+// NOLINTBEGIN
 static void CopyUdp(uv::Stream& in, std::shared_ptr<uv::Udp> out, int port,
                     bool broadcast) {
   sockaddr_in addr;
@@ -110,6 +116,7 @@
       },
       out);
 }
+// NOLINTEND
 
 static void CopyStream(uv::Stream& in, std::shared_ptr<uv::Stream> out) {
   in.data.connect([out](uv::Buffer& buf, size_t len) {
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/HttpParserTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/HttpParserTest.cpp
index a9d927a..02a17d9 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/HttpParserTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/HttpParserTest.cpp
@@ -4,7 +4,7 @@
 
 #include "wpinet/HttpParser.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 namespace wpi {
 
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/HttpUtilTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/HttpUtilTest.cpp
index 4417b7c..f36235b 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/HttpUtilTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/HttpUtilTest.cpp
@@ -4,7 +4,7 @@
 
 #include "wpinet/HttpUtil.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 namespace wpi {
 
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/MulticastTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/MulticastTest.cpp
index bbec538..412dd48 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/MulticastTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/MulticastTest.cpp
@@ -12,10 +12,9 @@
 #include <thread>
 #include <utility>
 
+#include <gtest/gtest.h>
 #include <wpi/timestamp.h>
 
-#include "gtest/gtest.h"
-
 TEST(MulticastServiceAnnouncerTest, EmptyText) {
   const std::string_view serviceName = "TestServiceNoText";
   const std::string_view serviceType = "_wpinotxt";
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp
index 5f6c8a5..6b74d82 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp
@@ -147,4 +147,56 @@
   ASSERT_EQ(gotData, 1);
 }
 
+TEST_F(WebSocketIntegrationTest, ServerSendPing) {
+  int gotPing = 0;
+  int gotPong = 0;
+  int gotData = 0;
+
+  serverPipe->Listen([&]() {
+    auto conn = serverPipe->Accept();
+    auto server = WebSocketServer::Create(*conn);
+    server->connected.connect([&](std::string_view, WebSocket& ws) {
+      ws.SendText({{"hello"}}, [&](auto, uv::Error) {});
+      ws.SendPing({uv::Buffer{"\x03\x04", 2}}, [&](auto, uv::Error) {});
+      ws.SendPing({uv::Buffer{"\x03\x04", 2}}, [&](auto, uv::Error) {});
+      ws.SendText({{"hello"}}, [&](auto, uv::Error) {});
+      ws.pong.connect([&](auto data) {
+        ++gotPong;
+        std::vector<uint8_t> recvData{data.begin(), data.end()};
+        std::vector<uint8_t> expectData{0x03, 0x04};
+        ASSERT_EQ(recvData, expectData);
+        if (gotPong == 2) {
+          ws.Close();
+        }
+      });
+    });
+  });
+
+  clientPipe->Connect(pipeName, [&] {
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
+    ws->closed.connect([&](uint16_t code, std::string_view reason) {
+      Finish();
+      if (code != 1005 && code != 1006) {
+        FAIL() << "Code: " << code << " Reason: " << reason;
+      }
+    });
+    ws->ping.connect([&](auto data) {
+      ++gotPing;
+      std::vector<uint8_t> recvData{data.begin(), data.end()};
+      std::vector<uint8_t> expectData{0x03, 0x04};
+      ASSERT_EQ(recvData, expectData);
+    });
+    ws->text.connect([&](std::string_view data, bool) {
+      ++gotData;
+      ASSERT_EQ(data, "hello");
+    });
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotPing, 2);
+  ASSERT_EQ(gotPong, 2);
+  ASSERT_EQ(gotData, 2);
+}
+
 }  // namespace wpi
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketSerializerTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketSerializerTest.cpp
new file mode 100644
index 0000000..6767a23
--- /dev/null
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketSerializerTest.cpp
@@ -0,0 +1,379 @@
+// 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 "WebSocketSerializer.h"  // NOLINT(build/include_order)
+
+#include <algorithm>
+#include <array>
+#include <ostream>
+#include <span>
+
+#include <gmock/gmock.h>
+#include <wpi/SpanMatcher.h>
+
+#include "WebSocketTest.h"
+#include "wpinet/uv/Buffer.h"
+
+using ::testing::_;
+using ::testing::AnyOf;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Pointee;
+using ::testing::Return;
+
+namespace wpi::uv {
+inline bool operator==(const Buffer& lhs, const Buffer& rhs) {
+  return lhs.len == rhs.len &&
+         std::equal(lhs.base, lhs.base + lhs.len, rhs.base);
+}
+inline void PrintTo(const Buffer& buf, ::std::ostream* os) {
+  ::wpi::PrintTo(buf.bytes(), os);
+}
+}  // namespace wpi::uv
+
+namespace wpi {
+inline bool operator==(const WebSocket::Frame& lhs,
+                       const WebSocket::Frame& rhs) {
+  return lhs.opcode == rhs.opcode &&
+         std::equal(lhs.data.begin(), lhs.data.end(), rhs.data.begin());
+}
+inline void PrintTo(const WebSocket::Frame& frame, ::std::ostream* os) {
+  *os << frame.opcode << ": ";
+  ::wpi::PrintTo(frame.data, os);
+}
+}  // namespace wpi
+
+namespace wpi::detail {
+
+class MockWebSocketWriteReq
+    : public std::enable_shared_from_this<MockWebSocketWriteReq>,
+      public detail::WebSocketWriteReqBase {
+ public:
+  explicit MockWebSocketWriteReq(
+      std::function<void(std::span<uv::Buffer>, uv::Error)> callback) {}
+};
+
+class MockStream {
+ public:
+  MOCK_METHOD(int, TryWrite, (std::span<const uv::Buffer>));
+  void Write(std::span<const uv::Buffer> bufs,
+             const std::shared_ptr<MockWebSocketWriteReq>& req) {
+    // std::cout << "Write(";
+    // PrintTo(bufs, &std::cout);
+    // std::cout << ")\n";
+    DoWrite(bufs, req);
+  }
+  MOCK_METHOD(void, DoWrite,
+              (std::span<const uv::Buffer> bufs,
+               const std::shared_ptr<MockWebSocketWriteReq>& req));
+};
+
+class WebSocketWriteReqTest : public ::testing::Test {
+ public:
+  WebSocketWriteReqTest() {
+    req->m_frames.m_bufs.emplace_back(m_buf0);
+    req->m_frames.m_bufs.emplace_back(m_buf1);
+    req->m_frames.m_bufs.emplace_back(m_buf2);
+    req->m_continueFrameOffs.emplace_back(5);  // frame 0: first 2 buffers
+    req->m_continueFrameOffs.emplace_back(9);  // frame 1: last buffer
+  }
+
+  std::shared_ptr<MockWebSocketWriteReq> req =
+      std::make_shared<MockWebSocketWriteReq>([](auto, auto) {});
+  ::testing::StrictMock<MockStream> stream;
+  static const uint8_t m_buf0[3];
+  static const uint8_t m_buf1[2];
+  static const uint8_t m_buf2[4];
+};
+
+const uint8_t WebSocketWriteReqTest::m_buf0[3] = {1, 2, 3};
+const uint8_t WebSocketWriteReqTest::m_buf1[2] = {4, 5};
+const uint8_t WebSocketWriteReqTest::m_buf2[4] = {6, 7, 8, 9};
+
+TEST_F(WebSocketWriteReqTest, ContinueDone) {
+  req->m_continueBufPos = 3;
+  ASSERT_EQ(req->Continue(stream, req), 0);
+}
+
+TEST_F(WebSocketWriteReqTest, ContinueTryWriteComplete) {
+  EXPECT_CALL(stream, TryWrite(wpi::SpanEq(req->m_frames.m_bufs)))
+      .WillOnce(Return(9));
+  ASSERT_EQ(req->Continue(stream, req), 0);
+}
+
+TEST_F(WebSocketWriteReqTest, ContinueTryWriteNoProgress) {
+  // if TryWrite returns 0
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(0));
+  // Write should get called for all of next frame - make forward progress
+  uv::Buffer remBufs[2] = {uv::Buffer{m_buf0}, uv::Buffer{m_buf1}};
+  EXPECT_CALL(stream,
+              DoWrite(wpi::SpanEq(std::span<const uv::Buffer>(remBufs)), _));
+  ASSERT_EQ(req->Continue(stream, req), 1);
+}
+
+TEST_F(WebSocketWriteReqTest, ContinueTryWriteError) {
+  // if TryWrite returns -1, the error is passed along
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(-1));
+  ASSERT_EQ(req->Continue(stream, req), -1);
+}
+
+TEST_F(WebSocketWriteReqTest, ContinueTryWritePartialMidFrameMidBuf1) {
+  // stop partway through buf 0
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(2));
+  // Write should get called for remainder of buf 0 and all of buf 1
+  uv::Buffer remBufs[2] = {uv::Buffer{&m_buf0[2], 1}, uv::Buffer{m_buf1}};
+  EXPECT_CALL(stream,
+              DoWrite(wpi::SpanEq(std::span<const uv::Buffer>(remBufs)), _));
+  ASSERT_EQ(req->Continue(stream, req), 1);
+}
+
+TEST_F(WebSocketWriteReqTest, ContinueTryWritePartialMidFrameBufBoundary) {
+  // stop at end of buf 0
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(3));
+  // Write should get called for all of buf 1
+  uv::Buffer remBufs[1] = {uv::Buffer{m_buf1}};
+  EXPECT_CALL(stream,
+              DoWrite(wpi::SpanEq(std::span<const uv::Buffer>(remBufs)), _));
+  ASSERT_EQ(req->Continue(stream, req), 1);
+}
+
+TEST_F(WebSocketWriteReqTest, ContinueTryWritePartialMidFrameMidBuf2) {
+  // stop partway through buf 1
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(4));
+  // Write should get called for remainder of buf 1
+  uv::Buffer remBufs[1] = {uv::Buffer{&m_buf1[1], 1}};
+  EXPECT_CALL(stream,
+              DoWrite(wpi::SpanEq(std::span<const uv::Buffer>(remBufs)), _));
+  ASSERT_EQ(req->Continue(stream, req), 1);
+}
+
+TEST_F(WebSocketWriteReqTest, ContinueTryWritePartialFrameBoundary) {
+  // stop at end of buf 1
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(5));
+  // Write should get called for all of next frame
+  uv::Buffer remBufs[1] = {uv::Buffer{m_buf2}};
+  EXPECT_CALL(stream,
+              DoWrite(wpi::SpanEq(std::span<const uv::Buffer>(remBufs)), _));
+  ASSERT_EQ(req->Continue(stream, req), 1);
+}
+
+TEST_F(WebSocketWriteReqTest, ContinueTryWritePartialMidFrameMidBuf3) {
+  // stop partway through buf 2
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(6));
+  // Write should get called for remainder of buf 2
+  uv::Buffer remBufs[1] = {uv::Buffer{&m_buf2[1], 3}};
+  EXPECT_CALL(stream,
+              DoWrite(wpi::SpanEq(std::span<const uv::Buffer>(remBufs)), _));
+  ASSERT_EQ(req->Continue(stream, req), 1);
+}
+
+class WebSocketTrySendTest : public ::testing::Test {
+ public:
+  ::testing::StrictMock<MockStream> stream;
+  std::shared_ptr<MockWebSocketWriteReq> req;
+  static const std::array<uint8_t, 3> m_buf0data;
+  static const std::array<uint8_t, 2> m_buf1data;
+  static const std::array<uint8_t, 4> m_buf2data;
+  static const std::array<uint8_t, 4> m_buf3data;
+  static const std::array<uv::Buffer, 4> m_bufs;
+  static const std::array<uint8_t, 5> m_frame0data;
+  static const std::array<uv::Buffer, 2> m_frame0bufs;
+  static const std::array<uv::Buffer, 1> m_frame1bufs;
+  static const std::array<uv::Buffer, 1> m_frame2bufs;
+  static const std::array<WebSocket::Frame, 3> m_frames;
+  static const std::array<std::vector<uint8_t>, 3> m_serialized;
+  static const std::array<uv::Buffer, 3> m_frameHeaders;
+
+  int makeReqCalled = 0;
+  int callbackCalled = 0;
+  void CheckTrySendFrames(std::span<const uv::Buffer> expectCbBufs,
+                          std::span<const WebSocket::Frame> expectRet,
+                          int expectErr = 0);
+};
+
+const std::array<uint8_t, 3> WebSocketTrySendTest::m_buf0data{1, 2, 3};
+const std::array<uint8_t, 2> WebSocketTrySendTest::m_buf1data{4, 5};
+const std::array<uint8_t, 4> WebSocketTrySendTest::m_buf2data{6, 7, 8, 9};
+const std::array<uint8_t, 4> WebSocketTrySendTest::m_buf3data{10, 11, 12, 13};
+const std::array<uv::Buffer, 4> WebSocketTrySendTest::m_bufs{
+    uv::Buffer{m_buf0data}, uv::Buffer{m_buf1data}, uv::Buffer{m_buf2data},
+    uv::Buffer{m_buf3data}};
+const std::array<uint8_t, 5> WebSocketTrySendTest::m_frame0data{1, 2, 3, 4, 5};
+const std::array<uv::Buffer, 2> WebSocketTrySendTest::m_frame0bufs{m_bufs[0],
+                                                                   m_bufs[1]};
+const std::array<uv::Buffer, 1> WebSocketTrySendTest::m_frame1bufs{m_bufs[2]};
+const std::array<uv::Buffer, 1> WebSocketTrySendTest::m_frame2bufs{m_bufs[3]};
+const std::array<WebSocket::Frame, 3> WebSocketTrySendTest::m_frames{
+    WebSocket::Frame{WebSocket::Frame::kBinaryFragment, m_frame0bufs},
+    WebSocket::Frame{WebSocket::Frame::kBinary, m_frame1bufs},
+    WebSocket::Frame{WebSocket::Frame::kText, m_frame2bufs},
+};
+const std::array<std::vector<uint8_t>, 3> WebSocketTrySendTest::m_serialized{
+    WebSocketTest::BuildMessage(m_frames[0].opcode, false, false, m_frame0data),
+    WebSocketTest::BuildMessage(m_frames[1].opcode, true, false, m_buf2data),
+    WebSocketTest::BuildMessage(m_frames[2].opcode, true, false, m_buf3data),
+};
+const std::array<uv::Buffer, 3> WebSocketTrySendTest::m_frameHeaders{
+    uv::Buffer{m_serialized[0].data(),
+               m_serialized[0].size() - m_frame0data.size()},
+    uv::Buffer{m_serialized[1].data(),
+               m_serialized[1].size() - m_buf2data.size()},
+    uv::Buffer{m_serialized[2].data(),
+               m_serialized[2].size() - m_buf3data.size()},
+};
+
+void WebSocketTrySendTest::CheckTrySendFrames(
+    std::span<const uv::Buffer> expectCbBufs,
+    std::span<const WebSocket::Frame> expectRet, int expectErr) {
+  ASSERT_THAT(
+      TrySendFrames(
+          true, stream, m_frames,
+          [&](std::function<void(std::span<uv::Buffer>, uv::Error)>&& cb) {
+            ++makeReqCalled;
+            req = std::make_shared<MockWebSocketWriteReq>(std::move(cb));
+            return req;
+          },
+          [&](auto bufs, auto err) {
+            ++callbackCalled;
+            ASSERT_THAT(bufs,
+                        SpanEq(std::span<const uv::Buffer>(expectCbBufs)));
+            ASSERT_EQ(err.code(), expectErr);
+          }),
+      SpanEq(expectRet));
+}
+
+TEST_F(WebSocketTrySendTest, ServerComplete) {
+  // if trywrite sends everything
+  EXPECT_CALL(stream, TryWrite(_))
+      .WillOnce(Return(m_serialized[0].size() + m_serialized[1].size() +
+                       m_serialized[2].size()));
+  // return nothing, and call callback immediately
+  CheckTrySendFrames(m_bufs, {});
+  ASSERT_EQ(makeReqCalled, 0);
+  ASSERT_EQ(callbackCalled, 1);
+}
+
+TEST_F(WebSocketTrySendTest, ServerNoProgress) {
+  // if trywrite sends nothing
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(0));
+  // we should get all the frames back (the callback may be called with an empty
+  // set of buffers)
+  CheckTrySendFrames({}, m_frames);
+  ASSERT_EQ(makeReqCalled, 0);
+  ASSERT_THAT(callbackCalled, AnyOf(0, 1));
+}
+
+TEST_F(WebSocketTrySendTest, ServerError) {
+  // if TryWrite returns -1, the error is passed along
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(-1));
+  CheckTrySendFrames(m_bufs, m_frames, -1);
+  ASSERT_EQ(makeReqCalled, 0);
+  ASSERT_EQ(callbackCalled, 1);
+}
+
+TEST_F(WebSocketTrySendTest, ServerPartialMidFrameMidBuf0) {
+  // stop partway through buf 0 (first buf of frame 0)
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(m_frameHeaders[0].len + 2));
+  // Write should get called for remainder of buf 0 and all of buf 1
+  // buf 2 should get put into continuation because frame 0 is a fragment
+  // return will be frame 2 only
+  std::array<uv::Buffer, 2> remBufs{std::span{m_buf0data}.subspan(2),
+                                    m_bufs[1]};
+  std::array<uv::Buffer, 2> contBufs{m_frameHeaders[1], m_bufs[2]};
+  std::array<int, 1> contFrameOffs{static_cast<int>(m_serialized[1].size())};
+  EXPECT_CALL(stream, DoWrite(wpi::SpanEq(remBufs), _));
+  CheckTrySendFrames({}, std::span{m_frames}.subspan(2));
+  ASSERT_EQ(makeReqCalled, 1);
+  ASSERT_THAT(req->m_frames.m_bufs, SpanEq(contBufs));
+  ASSERT_EQ(req->m_continueBufPos, 0u);
+  ASSERT_EQ(req->m_continueFramePos, 0u);
+  ASSERT_THAT(req->m_continueFrameOffs, SpanEq(contFrameOffs));
+  ASSERT_EQ(callbackCalled, 0);
+}
+
+TEST_F(WebSocketTrySendTest, ServerPartialMidFrameBufBoundary) {
+  // stop at end of buf 0 (first buf of frame 0)
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(m_frameHeaders[0].len + 3));
+  // Write should get called for all of buf 1
+  // buf 2 should get put into continuation because frame 0 is a fragment
+  // return will be frame 2 only
+  std::array<uv::Buffer, 1> remBufs{m_bufs[1]};
+  std::array<uv::Buffer, 2> contBufs{m_frameHeaders[1], m_bufs[2]};
+  EXPECT_CALL(stream, DoWrite(wpi::SpanEq(remBufs), _));
+  CheckTrySendFrames({}, std::span{m_frames}.subspan(2));
+  ASSERT_EQ(makeReqCalled, 1);
+  ASSERT_THAT(req->m_frames.m_bufs, SpanEq(contBufs));
+  ASSERT_EQ(callbackCalled, 0);
+}
+
+TEST_F(WebSocketTrySendTest, ServerPartialMidFrameMidBuf1) {
+  // stop partway through buf 1 (second buf of frame 0)
+  EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(m_frameHeaders[0].len + 4));
+  // Write should get called for remainder of buf 1
+  // buf 2 should get put into continuation because frame 0 is a fragment
+  // return will be frame 2 only
+  std::array<uv::Buffer, 1> remBufs{std::span{m_buf1data}.subspan(1)};
+  std::array<uv::Buffer, 2> contBufs{m_frameHeaders[1], m_bufs[2]};
+  EXPECT_CALL(stream, DoWrite(wpi::SpanEq(remBufs), _));
+  CheckTrySendFrames({}, std::span{m_frames}.subspan(2));
+  ASSERT_EQ(makeReqCalled, 1);
+  ASSERT_THAT(req->m_frames.m_bufs, SpanEq(contBufs));
+  ASSERT_EQ(callbackCalled, 0);
+}
+
+TEST_F(WebSocketTrySendTest, ServerPartialFrameBoundary) {
+  // stop at end of buf 1 (end of frame 0)
+  EXPECT_CALL(stream, TryWrite(_))
+      .WillOnce(Return(m_frameHeaders[0].len + m_frameHeaders[1].len + 5));
+  // Write should get called for all of buf 2 because frame 0 is a fragment
+  // no continuation
+  // return will be frame 2 only
+  std::array<uv::Buffer, 1> remBufs{m_bufs[2]};
+  EXPECT_CALL(stream, DoWrite(wpi::SpanEq(remBufs), _));
+  CheckTrySendFrames({}, std::span{m_frames}.subspan(2));
+  ASSERT_EQ(makeReqCalled, 1);
+  ASSERT_TRUE(req->m_frames.m_bufs.empty());
+  ASSERT_EQ(callbackCalled, 0);
+}
+
+TEST_F(WebSocketTrySendTest, ServerPartialMidFrameMidBuf2) {
+  // stop partway through buf 2 (frame 1 buf)
+  EXPECT_CALL(stream, TryWrite(_))
+      .WillOnce(Return(m_frameHeaders[0].len + m_frameHeaders[1].len + 6));
+  // Write should get called for remainder of buf 2; no continuation
+  // return will be frame 2 only
+  std::array<uv::Buffer, 1> remBufs{std::span{m_buf2data}.subspan(1)};
+  EXPECT_CALL(stream, DoWrite(wpi::SpanEq(remBufs), _));
+  CheckTrySendFrames({}, std::span{m_frames}.subspan(2));
+  ASSERT_EQ(makeReqCalled, 1);
+  ASSERT_TRUE(req->m_frames.m_bufs.empty());
+  ASSERT_EQ(callbackCalled, 0);
+}
+
+TEST_F(WebSocketTrySendTest, ServerFrameBoundary) {
+  // stop at end of buf 2 (end of frame 1)
+  EXPECT_CALL(stream, TryWrite(_))
+      .WillOnce(Return(m_frameHeaders[0].len + m_frameHeaders[1].len + 9));
+  // call callback immediately for bufs 0-2 and return frame 2
+  CheckTrySendFrames(std::span{m_bufs}.subspan(0, 3),
+                     std::span{m_frames}.subspan(2));
+  ASSERT_EQ(makeReqCalled, 0);
+  ASSERT_EQ(callbackCalled, 1);
+}
+
+TEST_F(WebSocketTrySendTest, ServerPartialLastFrame) {
+  // stop partway through buf 3
+  EXPECT_CALL(stream, TryWrite(_))
+      .WillOnce(Return(m_frameHeaders[0].len + m_frameHeaders[1].len +
+                       m_frameHeaders[2].len + 10));
+  // Write called for remainder of buf 3; no continuation
+  std::array<uv::Buffer, 1> remBufs{std::span{m_buf3data}.subspan(1)};
+  EXPECT_CALL(stream, DoWrite(wpi::SpanEq(remBufs), _));
+  CheckTrySendFrames({}, {});
+  ASSERT_EQ(makeReqCalled, 1);
+  ASSERT_TRUE(req->m_frames.m_bufs.empty());
+  ASSERT_EQ(callbackCalled, 0);
+}
+
+}  // namespace wpi::detail
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketServerTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketServerTest.cpp
index 18dd50b..7d33fa2 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketServerTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketServerTest.cpp
@@ -396,7 +396,14 @@
   std::vector<uint8_t> data2(4, 0x04);
   std::vector<uint8_t> data3(4, 0x05);
   std::vector<uint8_t> combData{data};
+#if __GNUC__ == 11
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-overread"
+#endif  // __GNUC__ == 11
   combData.insert(combData.end(), data2.begin(), data2.end());
+#if __GNUC__ == 11
+#pragma GCC diagnostic pop
+#endif  // __GNUC__ == 11
   combData.insert(combData.end(), data3.begin(), data3.end());
 
   setupWebSocket = [&] {
@@ -471,6 +478,56 @@
   ASSERT_EQ(gotCallback, 3);
 }
 
+// Control frames can happen in the middle of a fragmented message
+TEST_F(WebSocketServerTest, ReceiveFragmentWithControl) {
+  int gotCallback = 0;
+  int gotPongCallback = 0;
+
+  std::vector<uint8_t> data(4, 0x03);
+  std::vector<uint8_t> data2(4, 0x04);
+  std::vector<uint8_t> data3(4, 0x05);
+  std::vector<uint8_t> data4(4, 0x06);
+  std::vector<uint8_t> combData{data};
+#if __GNUC__ == 11
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-overread"
+#endif  // __GNUC__ == 11
+  combData.insert(combData.end(), data2.begin(), data2.end());
+#if __GNUC__ == 11
+#pragma GCC diagnostic pop
+#endif  // __GNUC__ == 11
+  combData.insert(combData.end(), data4.begin(), data4.end());
+
+  setupWebSocket = [&] {
+    ws->binary.connect([&](auto inData, bool fin) {
+      ASSERT_TRUE(gotPongCallback);
+      ++gotCallback;
+      ws->Terminate();
+      ASSERT_TRUE(fin);
+      std::vector<uint8_t> recvData{inData.begin(), inData.end()};
+      ASSERT_EQ(combData, recvData);
+    });
+    ws->pong.connect([&](auto inData) {
+      ASSERT_FALSE(gotCallback);
+      ++gotPongCallback;
+    });
+  };
+
+  auto message = BuildMessage(0x02, false, true, data);
+  auto message2 = BuildMessage(0x00, false, true, data2);
+  auto message3 = BuildMessage(0x0a, true, true, data3);
+  auto message4 = BuildMessage(0x00, true, true, data4);
+  resp.headersComplete.connect([&](bool) {
+    clientPipe->Write({{message}, {message2}, {message3}, {message4}},
+                      [&](auto bufs, uv::Error) {});
+  });
+
+  loop->Run();
+
+  ASSERT_EQ(gotCallback, 1);
+  ASSERT_EQ(gotPongCallback, 1);
+}
+
 //
 // Maximum message size is limited.
 //
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketTest.h b/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketTest.h
index 903ce00..1c904a8 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketTest.h
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/WebSocketTest.h
@@ -9,7 +9,8 @@
 #include <span>
 #include <vector>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "wpinet/uv/Loop.h"
 #include "wpinet/uv/Pipe.h"
 #include "wpinet/uv/Timer.h"
@@ -48,9 +49,7 @@
     failTimer->Unreference();
   }
 
-  ~WebSocketTest() override {
-    Finish();
-  }
+  ~WebSocketTest() override { Finish(); }
 
   void Finish() {
     loop->Walk([](uv::Handle& it) { it.Close(); });
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/WorkerThreadTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/WorkerThreadTest.cpp
index 8279cb1..d4f462d 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/WorkerThreadTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/WorkerThreadTest.cpp
@@ -4,10 +4,9 @@
 
 #include "wpinet/WorkerThread.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
-
 #include <thread>
 
+#include <gtest/gtest.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
 
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/hostname.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/hostname.cpp
index 51a29a2..df7e2f0 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/hostname.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/hostname.cpp
@@ -4,11 +4,10 @@
 
 #include "wpinet/hostname.h"
 
+#include <gtest/gtest.h>
 #include <wpi/SmallString.h>
 #include <wpi/SmallVector.h>
 
-#include "gtest/gtest.h"
-
 namespace wpi {
 TEST(HostNameTest, HostNameNotEmpty) {
   ASSERT_NE(GetHostname(), "");
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/main.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/main.cpp
index 09072ee..e993c1f 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/main.cpp
@@ -2,7 +2,7 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/raw_uv_stream_test.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/raw_uv_stream_test.cpp
index 541da5a..4b33b17 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/raw_uv_stream_test.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/raw_uv_stream_test.cpp
@@ -4,7 +4,7 @@
 
 #include "wpinet/raw_uv_ostream.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 namespace wpi {
 
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp
index 19c1faf..4369dc0 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp
@@ -4,10 +4,10 @@
 
 #include "wpinet/uv/AsyncFunction.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
-
 #include <thread>
 
+#include <gtest/gtest.h>
+
 #include "wpinet/uv/Loop.h"
 #include "wpinet/uv/Prepare.h"
 
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvAsyncTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvAsyncTest.cpp
index 5dd0f76..ed4324c 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvAsyncTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvAsyncTest.cpp
@@ -25,11 +25,10 @@
 
 #include "wpinet/uv/Async.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
-
 #include <atomic>
 #include <thread>
 
+#include <gtest/gtest.h>
 #include <wpi/mutex.h>
 
 #include "wpinet/uv/Loop.h"
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvBufferTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvBufferTest.cpp
index da6a63c..f349d51 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvBufferTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvBufferTest.cpp
@@ -4,7 +4,7 @@
 
 #include "wpinet/uv/Buffer.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
+#include <gtest/gtest.h>
 
 namespace wpi::uv {
 
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp
index 5ad33b2..78bb4c2 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp
@@ -25,7 +25,7 @@
 
 #include "wpinet/uv/GetAddrInfo.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
+#include <gtest/gtest.h>
 
 #include "wpinet/uv/Loop.h"
 
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvGetNameInfoTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvGetNameInfoTest.cpp
index 707f037..dfb813b 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvGetNameInfoTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvGetNameInfoTest.cpp
@@ -25,7 +25,7 @@
 
 #include "wpinet/uv/GetNameInfo.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
+#include <gtest/gtest.h>
 
 #include "wpinet/uv/Loop.h"
 
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvLoopWalkTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvLoopWalkTest.cpp
index eee0f99..3cb1c9f 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvLoopWalkTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvLoopWalkTest.cpp
@@ -25,7 +25,7 @@
 
 #include "wpinet/uv/Loop.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
+#include <gtest/gtest.h>
 
 #include "wpinet/uv/Timer.h"
 
diff --git a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvTimerTest.cpp b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvTimerTest.cpp
index 7377ab1..ea6188b 100644
--- a/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvTimerTest.cpp
+++ b/third_party/allwpilib/wpinet/src/test/native/cpp/uv/UvTimerTest.cpp
@@ -4,7 +4,7 @@
 
 #include "wpinet/uv/Timer.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 namespace wpi::uv {
 
diff --git a/third_party/allwpilib/wpiunits/build.gradle b/third_party/allwpilib/wpiunits/build.gradle
new file mode 100644
index 0000000..e10033d
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/build.gradle
@@ -0,0 +1,14 @@
+ext {
+    useJava = true
+    useCpp = false
+    baseId = 'wpiunits'
+    groupId = 'edu.wpi.first.wpiunits'
+
+    nativeName = 'wpiunits'
+    devMain = 'edu.wpi.first.units.DevMain'
+}
+
+apply from: "${rootDir}/shared/java/javacommon.gradle"
+
+dependencies {
+}
diff --git a/third_party/allwpilib/wpiunits/src/dev/java/edu/wpi/first/units/DevMain.java b/third_party/allwpilib/wpiunits/src/dev/java/edu/wpi/first/units/DevMain.java
new file mode 100644
index 0000000..0d4dcdd
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/dev/java/edu/wpi/first/units/DevMain.java
@@ -0,0 +1,14 @@
+// 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.
+
+package edu.wpi.first.units;
+
+public final class DevMain {
+  /** Main entry point. */
+  public static void main(String[] args) {
+    System.out.println(Units.Inches.of(-5.0).in(Units.Meters));
+  }
+
+  private DevMain() {}
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Angle.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Angle.java
new file mode 100644
index 0000000..105a935
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Angle.java
@@ -0,0 +1,26 @@
+// 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.
+
+package edu.wpi.first.units;
+
+// technically, angles are unitless dimensions
+// eg Mass * Distance * Velocity<Angle> is equivalent to (Mass * Distance) / Time - otherwise known
+// as Power - in other words, Velocity<Angle> is /actually/ Frequency
+public class Angle extends Unit<Angle> {
+  /**
+   * Creates a new unit with the given name and multiplier to the base unit.
+   *
+   * @param baseUnitEquivalent the multiplier to convert this unit to the base unit of this type
+   * @param name the name of the angle measure
+   * @param symbol the symbol of the angle measure
+   */
+  Angle(double baseUnitEquivalent, String name, String symbol) {
+    super(Angle.class, baseUnitEquivalent, name, symbol);
+  }
+
+  Angle(
+      UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super(Angle.class, toBaseConverter, fromBaseConverter, name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/BaseUnits.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/BaseUnits.java
new file mode 100644
index 0000000..5e342da
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/BaseUnits.java
@@ -0,0 +1,45 @@
+// 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.
+
+package edu.wpi.first.units;
+
+/** The base units of measure. */
+public final class BaseUnits {
+  private BaseUnits() {
+    // Prevent instantiation
+  }
+
+  /** The standard unit of distance, meters. */
+  public static final Distance Distance = new Distance(1, "Meter", "m");
+
+  /** The standard unit of time, seconds. */
+  public static final Time Time = new Time(1, "Second", "s");
+
+  /** The standard unit of velocity, meters per second. */
+  public static final Velocity<Distance> Velocity =
+      new Velocity<>(Distance, Time, "Meter per Second", "m/s");
+
+  /** The standard unit of mass, grams. */
+  public static final Mass Mass = new Mass(1, "Kilogram", "Kg");
+
+  /** The standard unit of angles, revolutions. */
+  public static final Angle Angle = new Angle(1, "Revolution", "R");
+
+  /** The standard "unitless" unit. */
+  public static final Dimensionless Value = new Dimensionless(1, "<?>", "<?>");
+
+  /** The standard unit of voltage, volts. */
+  public static final Voltage Voltage = new Voltage(1, "Volt", "V");
+
+  /** The standard unit of electric current, amperes. */
+  public static final Current Current = new Current(1, "Amp", "A");
+
+  /** The standard unit of energy, joules. */
+  public static final Energy Energy = new Energy(1, "Joule", "J");
+
+  /** The standard unit of power, watts. */
+  public static final Power Power = new Power(1, "Watt", "W");
+
+  public static final Temperature Temperature = new Temperature(x -> x, x -> x, "Kelvin", "K");
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Current.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Current.java
new file mode 100644
index 0000000..88418c5
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Current.java
@@ -0,0 +1,20 @@
+// 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.
+
+package edu.wpi.first.units;
+
+public class Current extends Unit<Current> {
+  Current(double baseUnitEquivalent, String name, String symbol) {
+    super(Current.class, baseUnitEquivalent, name, symbol);
+  }
+
+  Current(
+      UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super(Current.class, toBaseConverter, fromBaseConverter, name, symbol);
+  }
+
+  public Power times(Unit<Voltage> voltage, String name, String symbol) {
+    return new Power(this.toBaseUnits(1) * voltage.toBaseUnits(1), name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java
new file mode 100644
index 0000000..5d1772f
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java
@@ -0,0 +1,26 @@
+// 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.
+
+package edu.wpi.first.units;
+
+/**
+ * A type of unit that corresponds to raw values and not any physical dimension, such as percentage.
+ */
+public class Dimensionless extends Unit<Dimensionless> {
+  /**
+   * Creates a new unit with the given name and multiplier to the base unit.
+   *
+   * @param baseUnitEquivalent the multiplier to convert this unit to the base unit of this type.
+   * @param name the name of the unit
+   * @param symbol the symbol of the unit
+   */
+  protected Dimensionless(double baseUnitEquivalent, String name, String symbol) {
+    super(Dimensionless.class, baseUnitEquivalent, name, symbol);
+  }
+
+  Dimensionless(
+      UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super(Dimensionless.class, toBaseConverter, fromBaseConverter, name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Distance.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Distance.java
new file mode 100644
index 0000000..d5512dd
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Distance.java
@@ -0,0 +1,17 @@
+// 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.
+
+package edu.wpi.first.units;
+
+public class Distance extends Unit<Distance> {
+  /** Creates a new unit with the given name and multiplier to the base unit. */
+  Distance(double baseUnitEquivalent, String name, String symbol) {
+    super(Distance.class, baseUnitEquivalent, name, symbol);
+  }
+
+  Distance(
+      UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super(Distance.class, toBaseConverter, fromBaseConverter, name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Energy.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Energy.java
new file mode 100644
index 0000000..d43adf9
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Energy.java
@@ -0,0 +1,16 @@
+// 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.
+
+package edu.wpi.first.units;
+
+public class Energy extends Unit<Energy> {
+  protected Energy(
+      UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super(Energy.class, toBaseConverter, fromBaseConverter, name, symbol);
+  }
+
+  protected Energy(double baseUnitEquivalent, String name, String symbol) {
+    super(Energy.class, baseUnitEquivalent, name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/ImmutableMeasure.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/ImmutableMeasure.java
new file mode 100644
index 0000000..84fb732
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/ImmutableMeasure.java
@@ -0,0 +1,110 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import java.util.Objects;
+
+/**
+ * A measure holds the magnitude and unit of some dimension, such as distance, time, or speed. A
+ * measure is <i>immutable</i> and <i>type safe</i>, making it easy to use in concurrent situations
+ * and gives compile-time safety. Two measures with the same <i>unit</i> and <i>magnitude</i> are
+ * effectively the same object.
+ *
+ * @param <U> the unit type of the measure
+ */
+public class ImmutableMeasure<U extends Unit<U>> implements Measure<U> {
+  private final double m_magnitude;
+  private final double m_baseUnitMagnitude;
+  private final U m_unit;
+
+  /**
+   * Creates a new immutable measure instance. This shouldn't be used directly; prefer one of the
+   * factory methods instead.
+   *
+   * @param magnitude the magnitude of this measure
+   * @param unit the unit of this measure.
+   */
+  @SuppressWarnings("unchecked")
+  ImmutableMeasure(double magnitude, double baseUnitMagnitude, Unit<U> unit) {
+    Objects.requireNonNull(unit, "Unit cannot be null");
+    m_magnitude = magnitude;
+    m_baseUnitMagnitude = baseUnitMagnitude;
+    m_unit = (U) unit;
+  }
+
+  /**
+   * Creates a new measure in the given unit with a magnitude equal to the given one in base units.
+   *
+   * @param <U> the type of the units of measure
+   * @param baseUnitMagnitude the magnitude of the measure, in terms of the base unit of measure
+   * @param unit the unit of measure
+   * @return a new measure
+   */
+  public static <U extends Unit<U>> ImmutableMeasure<U> ofBaseUnits(
+      double baseUnitMagnitude, Unit<U> unit) {
+    return new ImmutableMeasure<>(unit.fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, unit);
+  }
+
+  /**
+   * Creates a new measure in the given unit with a magnitude in terms of that unit.
+   *
+   * @param <U> the type of the units of measure
+   * @param relativeMagnitude the magnitude of the measure
+   * @param unit the unit of measure
+   * @return a new measure
+   */
+  public static <U extends Unit<U>> ImmutableMeasure<U> ofRelativeUnits(
+      double relativeMagnitude, Unit<U> unit) {
+    return new ImmutableMeasure<>(relativeMagnitude, unit.toBaseUnits(relativeMagnitude), unit);
+  }
+
+  /** Gets the unitless magnitude of this measure. */
+  @Override
+  public double magnitude() {
+    return m_magnitude;
+  }
+
+  @Override
+  public double baseUnitMagnitude() {
+    return m_baseUnitMagnitude;
+  }
+
+  /** Gets the units of this measure. */
+  @Override
+  public U unit() {
+    return m_unit;
+  }
+
+  /**
+   * Checks for <i>object equality</i>. To check if two measures are <i>equivalent</i>, use {@link
+   * #isEquivalent(Measure) isEquivalent}.
+   */
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof Measure)) {
+      return false;
+    }
+    Measure<?> that = (Measure<?>) o;
+    return Objects.equals(m_unit, that.unit()) && m_baseUnitMagnitude == that.baseUnitMagnitude();
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(m_magnitude, m_unit);
+  }
+
+  @Override
+  public Measure<U> copy() {
+    return this; // already immutable, no need to allocate a new object
+  }
+
+  @Override
+  public String toString() {
+    return toShortString();
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Mass.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Mass.java
new file mode 100644
index 0000000..c9dc0bb
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Mass.java
@@ -0,0 +1,16 @@
+// 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.
+
+package edu.wpi.first.units;
+
+public class Mass extends Unit<Mass> {
+  /** Creates a new unit with the given name and multiplier to the base unit. */
+  Mass(double baseUnitEquivalent, String name, String symbol) {
+    super(Mass.class, baseUnitEquivalent, name, symbol);
+  }
+
+  Mass(UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super(Mass.class, toBaseConverter, fromBaseConverter, name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Measure.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Measure.java
new file mode 100644
index 0000000..908eb2d
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Measure.java
@@ -0,0 +1,383 @@
+// 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.
+
+package edu.wpi.first.units;
+
+/**
+ * A measure holds the magnitude and unit of some dimension, such as distance, time, or speed. Two
+ * measures with the same <i>unit</i> and <i>magnitude</i> are effectively the same object.
+ *
+ * @param <U> the unit type of the measure
+ */
+public interface Measure<U extends Unit<U>> extends Comparable<Measure<U>> {
+  /**
+   * The threshold for two measures to be considered equivalent if converted to the same unit. This
+   * is only needed due to floating-point error.
+   */
+  double EQUIVALENCE_THRESHOLD = 1e-12;
+
+  /**
+   * Gets the unitless magnitude of this measure.
+   *
+   * @return the magnitude in terms of {@link #unit() the unit}.
+   */
+  double magnitude();
+
+  /**
+   * Gets the magnitude of this measure in terms of the base unit. If the unit is the base unit for
+   * its system of measure, then the value will be equivalent to {@link #magnitude()}.
+   *
+   * @return the magnitude in terms of the base unit
+   */
+  double baseUnitMagnitude();
+
+  /**
+   * Gets the units of this measure.
+   *
+   * @return the unit
+   */
+  U unit();
+
+  /**
+   * Converts this measure to a measure with a different unit of the same type, eg minutes to
+   * seconds. Converting to the same unit is equivalent to calling {@link #magnitude()}.
+   *
+   * <pre>
+   *   Meters.of(12).in(Feet) // 39.3701
+   *   Seconds.of(15).in(Minutes) // 0.25
+   * </pre>
+   *
+   * @param unit the unit to convert this measure to
+   * @return the value of this measure in the given unit
+   */
+  default double in(Unit<U> unit) {
+    if (this.unit().equals(unit)) {
+      return magnitude();
+    } else {
+      return unit.fromBaseUnits(baseUnitMagnitude());
+    }
+  }
+
+  /**
+   * Multiplies this measurement by some constant multiplier and returns the result. The magnitude
+   * of the result will be the <i>base</i> magnitude multiplied by the scalar value. If the measure
+   * uses a unit with a non-linear relation to its base unit (such as Fahrenheit for temperature),
+   * then the result will only be a multiple <i>in terms of the base unit</i>.
+   *
+   * @param multiplier the constant to multiply by
+   * @return the resulting measure
+   */
+  default Measure<U> times(double multiplier) {
+    return ImmutableMeasure.ofBaseUnits(baseUnitMagnitude() * multiplier, unit());
+  }
+
+  /**
+   * Generates a new measure that is equal to this measure multiplied by another. Some dimensional
+   * analysis is performed to reduce the units down somewhat; for example, multiplying a {@code
+   * Measure<Time>} by a {@code Measure<Velocity<Distance>>} will return just a {@code
+   * Measure<Distance>} instead of the naive {@code Measure<Mult<Time, Velocity<Distance>>}. This is
+   * not guaranteed to perform perfect dimensional analysis.
+   *
+   * @param <U2> the type of the other measure to multiply by
+   * @param other the unit to multiply by
+   * @return the multiplicative unit
+   */
+  @SuppressWarnings("unchecked")
+  default <U2 extends Unit<U2>> Measure<?> times(Measure<U2> other) {
+    if (other.unit() instanceof Dimensionless) {
+      // scalar multiplication
+      return times(other.baseUnitMagnitude());
+    }
+
+    if (unit() instanceof Per
+        && other.unit().m_baseType.equals(((Per<?, ?>) unit()).denominator().m_baseType)) {
+      // denominator of the Per cancels out, return with just the units of the numerator
+      Unit<?> numerator = ((Per<?, ?>) unit()).numerator();
+      return numerator.ofBaseUnits(baseUnitMagnitude() * other.baseUnitMagnitude());
+    } else if (unit() instanceof Velocity && other.unit().m_baseType.equals(Time.class)) {
+      // Multiplying a velocity by a time, return the scalar unit (eg Distance)
+      Unit<?> numerator = ((Velocity<?>) unit()).getUnit();
+      return numerator.ofBaseUnits(baseUnitMagnitude() * other.baseUnitMagnitude());
+    } else if (other.unit() instanceof Per
+        && unit().m_baseType.equals(((Per<?, ?>) other.unit()).denominator().m_baseType)) {
+      Unit<?> numerator = ((Per<?, ?>) other.unit()).numerator();
+      return numerator.ofBaseUnits(baseUnitMagnitude() * other.baseUnitMagnitude());
+    } else if (unit() instanceof Per
+        && other.unit() instanceof Per
+        && ((Per<?, ?>) unit())
+            .denominator()
+            .m_baseType
+            .equals(((Per<?, U>) other.unit()).numerator().m_baseType)
+        && ((Per<?, ?>) unit())
+            .numerator()
+            .m_baseType
+            .equals(((Per<?, ?>) other.unit()).denominator().m_baseType)) {
+      // multiplying eg meters per second * milliseconds per foot
+      // return a scalar
+      return Units.Value.of(baseUnitMagnitude() * other.baseUnitMagnitude());
+    }
+
+    // Dimensional analysis fallthrough, do a basic unit multiplication
+    return unit().mult(other.unit()).ofBaseUnits(baseUnitMagnitude() * other.baseUnitMagnitude());
+  }
+
+  /**
+   * Divides this measurement by some constant divisor and returns the result. This is equivalent to
+   * {@code times(1 / divisor)}
+   *
+   * @param divisor the constant to divide by
+   * @return the resulting measure
+   * @see #times(double)
+   */
+  default Measure<U> divide(double divisor) {
+    return times(1 / divisor);
+  }
+
+  /**
+   * Divides this measurement by some constant divisor and returns the result. This is equivalent to
+   * {@code divide(divisor.baseUnitMagnitude())}
+   *
+   * @param divisor the dimensionless measure to divide by
+   * @return the resulting measure
+   * @see #divide(double)
+   * @see #times(double)
+   */
+  default Measure<U> divide(Measure<Dimensionless> divisor) {
+    return divide(divisor.baseUnitMagnitude());
+  }
+
+  /**
+   * Creates a velocity measure by dividing this one by a time period measure.
+   *
+   * <pre>
+   *   Meters.of(1).per(Second) // Measure&lt;Velocity&lt;Distance&gt;&gt;
+   * </pre>
+   *
+   * @param period the time period to divide by.
+   * @return the velocity result
+   */
+  default Measure<Velocity<U>> per(Measure<Time> period) {
+    var newUnit = unit().per(period.unit());
+    return ImmutableMeasure.ofBaseUnits(baseUnitMagnitude() / period.baseUnitMagnitude(), newUnit);
+  }
+
+  /**
+   * Creates a relational measure equivalent to this one per some other unit.
+   *
+   * <pre>
+   *   Volts.of(1.05).per(Meter) // V/m, potential PID constant
+   * </pre>
+   *
+   * @param <U2> the type of the denominator unit
+   * @param denominator the denominator unit being divided by
+   * @return the relational measure
+   */
+  default <U2 extends Unit<U2>> Measure<Per<U, U2>> per(U2 denominator) {
+    var newUnit = unit().per(denominator);
+    return newUnit.of(magnitude());
+  }
+
+  /**
+   * Adds another measure to this one. The resulting measure has the same unit as this one.
+   *
+   * @param other the measure to add to this one
+   * @return a new measure containing the result
+   */
+  default Measure<U> plus(Measure<U> other) {
+    return unit().ofBaseUnits(baseUnitMagnitude() + other.baseUnitMagnitude());
+  }
+
+  /**
+   * Subtracts another measure from this one. The resulting measure has the same unit as this one.
+   *
+   * @param other the measure to subtract from this one
+   * @return a new measure containing the result
+   */
+  default Measure<U> minus(Measure<U> other) {
+    return unit().ofBaseUnits(baseUnitMagnitude() - other.baseUnitMagnitude());
+  }
+
+  /**
+   * Negates this measure and returns the result.
+   *
+   * @return the resulting measure
+   */
+  default Measure<U> negate() {
+    return times(-1);
+  }
+
+  /**
+   * Returns an immutable copy of this measure. The copy can be used freely and is guaranteed never
+   * to change.
+   *
+   * @return the copied measure
+   */
+  Measure<U> copy();
+
+  /**
+   * Creates a new mutable copy of this measure.
+   *
+   * @return a mutable measure initialized to be identical to this measure
+   */
+  default MutableMeasure<U> mutableCopy() {
+    return MutableMeasure.mutable(this);
+  }
+
+  /**
+   * Checks if this measure is near another measure of the same unit. Provide a variance threshold
+   * for use for a +/- scalar, such as 0.05 for +/- 5%.
+   *
+   * <pre>
+   *   Inches.of(11).isNear(Inches.of(10), 0.1) // true
+   *   Inches.of(12).isNear(Inches.of(10), 0.1) // false
+   * </pre>
+   *
+   * @param other the other measurement to compare against
+   * @param varianceThreshold the acceptable variance threshold, in terms of an acceptable +/- error
+   *     range multiplier. Checking if a value is within 10% means a value of 0.1 should be passed;
+   *     checking if a value is within 1% means a value of 0.01 should be passed, and so on.
+   * @return true if this unit is near the other measure, otherwise false
+   */
+  default boolean isNear(Measure<?> other, double varianceThreshold) {
+    if (this.unit().m_baseType != other.unit().m_baseType) {
+      return false; // Disjoint units, not compatible
+    }
+
+    // abs so negative inputs are calculated correctly
+    var allowedVariance = Math.abs(varianceThreshold);
+
+    return other.baseUnitMagnitude() * (1 - allowedVariance) <= this.baseUnitMagnitude()
+        && other.baseUnitMagnitude() * (1 + allowedVariance) >= this.baseUnitMagnitude();
+  }
+
+  /**
+   * Checks if this measure is equivalent to another measure of the same unit.
+   *
+   * @param other the measure to compare to
+   * @return true if this measure is equivalent, false otherwise
+   */
+  default boolean isEquivalent(Measure<?> other) {
+    if (this.unit().m_baseType != other.unit().m_baseType) {
+      return false; // Disjoint units, not compatible
+    }
+
+    return Math.abs(baseUnitMagnitude() - other.baseUnitMagnitude()) <= EQUIVALENCE_THRESHOLD;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  default int compareTo(Measure<U> o) {
+    return Double.compare(this.baseUnitMagnitude(), o.baseUnitMagnitude());
+  }
+
+  /**
+   * Checks if this measure is greater than another measure of the same unit.
+   *
+   * @param o the other measure to compare to
+   * @return true if this measure has a greater equivalent magnitude, false otherwise
+   */
+  default boolean gt(Measure<U> o) {
+    return compareTo(o) > 0;
+  }
+
+  /**
+   * Checks if this measure is greater than or equivalent to another measure of the same unit.
+   *
+   * @param o the other measure to compare to
+   * @return true if this measure has an equal or greater equivalent magnitude, false otherwise
+   */
+  default boolean gte(Measure<U> o) {
+    return compareTo(o) > 0 || isEquivalent(o);
+  }
+
+  /**
+   * Checks if this measure is less than another measure of the same unit.
+   *
+   * @param o the other measure to compare to
+   * @return true if this measure has a lesser equivalent magnitude, false otherwise
+   */
+  default boolean lt(Measure<U> o) {
+    return compareTo(o) < 0;
+  }
+
+  /**
+   * Checks if this measure is less than or equivalent to another measure of the same unit.
+   *
+   * @param o the other measure to compare to
+   * @return true if this measure has an equal or lesser equivalent magnitude, false otherwise
+   */
+  default boolean lte(Measure<U> o) {
+    return compareTo(o) < 0 || isEquivalent(o);
+  }
+
+  /**
+   * Returns the measure with the absolute value closest to positive infinity.
+   *
+   * @param <U> the type of the units of the measures
+   * @param measures the set of measures to compare
+   * @return the measure with the greatest positive magnitude, or null if no measures were provided
+   */
+  @SafeVarargs
+  static <U extends Unit<U>> Measure<U> max(Measure<U>... measures) {
+    if (measures.length == 0) {
+      return null; // nothing to compare
+    }
+
+    Measure<U> max = null;
+    for (Measure<U> measure : measures) {
+      if (max == null || measure.gt(max)) {
+        max = measure;
+      }
+    }
+
+    return max;
+  }
+
+  /**
+   * Returns the measure with the absolute value closest to negative infinity.
+   *
+   * @param <U> the type of the units of the measures
+   * @param measures the set of measures to compare
+   * @return the measure with the greatest negative magnitude
+   */
+  @SafeVarargs
+  static <U extends Unit<U>> Measure<U> min(Measure<U>... measures) {
+    if (measures.length == 0) {
+      return null; // nothing to compare
+    }
+
+    Measure<U> max = null;
+    for (Measure<U> measure : measures) {
+      if (max == null || measure.lt(max)) {
+        max = measure;
+      }
+    }
+
+    return max;
+  }
+
+  /**
+   * Returns a string representation of this measurement in a shorthand form. The symbol of the
+   * backing unit is used, rather than the full name, and the magnitude is represented in scientific
+   * notation.
+   *
+   * @return the short form representation of this measurement
+   */
+  default String toShortString() {
+    // eg 1.234e+04 V/m (1234 Volt per Meter in long form)
+    return String.format("%.3e %s", magnitude(), unit().symbol());
+  }
+
+  /**
+   * Returns a string representation of this measurement in a longhand form. The name of the backing
+   * unit is used, rather than its symbol, and the magnitude is represented in a full string, no
+   * scientific notation. (Very large values may be represented in scientific notation, however)
+   *
+   * @return the long form representation of this measurement
+   */
+  default String toLongString() {
+    // eg 1234 Volt per Meter (1.234e+04 V/m in short form)
+    return String.format("%s %s", magnitude(), unit().name());
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Mult.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Mult.java
new file mode 100644
index 0000000..7b6beb3
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Mult.java
@@ -0,0 +1,95 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import edu.wpi.first.units.collections.LongToObjectHashMap;
+import java.util.Objects;
+
+/**
+ * A combinatory unit type that is equivalent to the product of two other others. For example,
+ * Newton * Meters for torque could be represented as a unit of <code>
+ * Mult&lt;Force, Distance, Torque&gt;</code>
+ *
+ * @param <A> the type of the first unit in the result
+ * @param <B> the type of the second unit in the result
+ */
+public class Mult<A extends Unit<A>, B extends Unit<B>> extends Unit<Mult<A, B>> {
+  private final A m_unitA;
+  private final B m_unitB;
+
+  @SuppressWarnings("rawtypes")
+  private static final LongToObjectHashMap<Mult> cache = new LongToObjectHashMap<>();
+
+  protected Mult(Class<? extends Mult<A, B>> baseType, A a, B b) {
+    super(
+        baseType,
+        a.toBaseUnits(1) * b.toBaseUnits(1),
+        a.name() + "-" + b.name(),
+        a.symbol() + "*" + b.symbol());
+    m_unitA = a;
+    m_unitB = b;
+  }
+
+  /**
+   * Creates a new Mult unit derived from two arbitrary units multiplied together.
+   *
+   * <pre>
+   *   Mult.combine(Volts, Meters) // Volt-Meters
+   * </pre>
+   *
+   * <p>It's recommended to use the convenience function {@link Unit#mult(Unit)} instead of calling
+   * this factory directly.
+   *
+   * @param <A> the type of the first unit
+   * @param <B> the type of the second unit
+   * @param a the first unit
+   * @param b the second unit
+   * @return the combined unit
+   */
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  public static <A extends Unit<A>, B extends Unit<B>> Mult<A, B> combine(A a, B b) {
+    final long key = ((long) a.hashCode()) << 32L | ((long) b.hashCode()) & 0xFFFFFFFFL;
+    if (cache.containsKey(key)) {
+      return cache.get(key);
+    }
+
+    var mult = new Mult<A, B>((Class) Mult.class, a, b);
+    cache.put(key, mult);
+    return mult;
+  }
+
+  public A unitA() {
+    return m_unitA;
+  }
+
+  public B unitB() {
+    return m_unitB;
+  }
+
+  @Override
+  public String toString() {
+    return "(" + m_unitA.toString() + " * " + m_unitB.toString() + ")";
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    if (!super.equals(o)) {
+      return false;
+    }
+    Mult<?, ?> mult = (Mult<?, ?>) o;
+    return Objects.equals(m_unitA, mult.m_unitA) && Objects.equals(m_unitB, mult.m_unitB);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(super.hashCode(), m_unitA, m_unitB);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/MutableMeasure.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/MutableMeasure.java
new file mode 100644
index 0000000..ad035f7
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/MutableMeasure.java
@@ -0,0 +1,303 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import java.util.Objects;
+
+/**
+ * A specialization of {@link Measure} that allows for mutability. This is intended to be used for
+ * memory use reasons (such as on the memory-restricted RoboRIO 1 or 2 or SBC coprocessors) and
+ * should NOT be exposed in the public API for a class that uses it.
+ *
+ * <p>The advantage of using this class is that only one instance of a measurement object will exist
+ * at a time, as opposed to instantiating a new immutable instance every time a value is fetched.
+ * This can greatly reduce memory pressure, but comes at the cost of increased code complexity and
+ * sensitivity to race conditions if used poorly.
+ *
+ * <p>Any unsafe methods are prefixed with {@code mut_*}, such as {@link #mut_plus(Measure)} or
+ * {@link #mut_replace(Measure)}. These methods will change the internal state of the measurement
+ * object, and as such can be dangerous to use. They are primarily intended for use to track
+ * internal state of things like sensors
+ *
+ * @param <U> the type of the unit of measure
+ */
+public final class MutableMeasure<U extends Unit<U>> implements Measure<U> {
+  private double m_magnitude;
+  private double m_baseUnitMagnitude;
+  private U m_unit;
+
+  private MutableMeasure(double initialMagnitude, double baseUnitMagnitude, U unit) {
+    m_magnitude = initialMagnitude;
+    m_baseUnitMagnitude = baseUnitMagnitude;
+    m_unit = unit;
+  }
+
+  /**
+   * Creates a new mutable measure that is a copy of the given one.
+   *
+   * @param <U> the type of the units of measure
+   * @param measure the measure to create a mutable copy of
+   * @return a new mutable measure with an initial state equal to the given measure
+   */
+  public static <U extends Unit<U>> MutableMeasure<U> mutable(Measure<U> measure) {
+    return new MutableMeasure<>(measure.magnitude(), measure.baseUnitMagnitude(), measure.unit());
+  }
+
+  /**
+   * Creates a new mutable measure with a magnitude of 0 in the given unit.
+   *
+   * @param <U> the type of the units of measure
+   * @param unit the unit of measure
+   * @return a new mutable measure
+   */
+  public static <U extends Unit<U>> MutableMeasure<U> zero(U unit) {
+    return mutable(unit.zero());
+  }
+
+  /**
+   * Creates a new mutable measure in the given unit with a magnitude equal to the given one in base
+   * units.
+   *
+   * @param <U> the type of the units of measure
+   * @param baseUnitMagnitude the magnitude of the measure, in terms of the base unit of measure
+   * @param unit the unit of measure
+   * @return a new mutable measure
+   */
+  public static <U extends Unit<U>> MutableMeasure<U> ofBaseUnits(
+      double baseUnitMagnitude, U unit) {
+    return new MutableMeasure<>(unit.fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, unit);
+  }
+
+  /**
+   * Creates a new mutable measure in the given unit with a magnitude in terms of that unit.
+   *
+   * @param <U> the type of the units of measure
+   * @param relativeMagnitude the magnitude of the measure
+   * @param unit the unit of measure
+   * @return a new mutable measure
+   */
+  public static <U extends Unit<U>> MutableMeasure<U> ofRelativeUnits(
+      double relativeMagnitude, U unit) {
+    return new MutableMeasure<>(relativeMagnitude, unit.toBaseUnits(relativeMagnitude), unit);
+  }
+
+  @Override
+  public double magnitude() {
+    return m_magnitude;
+  }
+
+  @Override
+  public double baseUnitMagnitude() {
+    return m_baseUnitMagnitude;
+  }
+
+  @Override
+  public U unit() {
+    return m_unit;
+  }
+
+  // UNSAFE
+
+  /**
+   * Sets the new magnitude of the measurement. The magnitude must be in terms of the {@link
+   * #unit()}.
+   *
+   * @param magnitude the new magnitude of the measurement
+   */
+  public void mut_setMagnitude(double magnitude) {
+    m_magnitude = magnitude;
+    m_baseUnitMagnitude = m_unit.toBaseUnits(magnitude);
+  }
+
+  /**
+   * Sets the new magnitude of the measurement. The magnitude must be in terms of the base unit of
+   * the current unit.
+   *
+   * @param baseUnitMagnitude the new magnitude of the measurement
+   */
+  public void mut_setBaseUnitMagnitude(double baseUnitMagnitude) {
+    m_baseUnitMagnitude = baseUnitMagnitude;
+    m_magnitude = m_unit.fromBaseUnits(baseUnitMagnitude);
+  }
+
+  /**
+   * Overwrites the state of this measure and replaces it with values from the given one.
+   *
+   * @param other the other measure to copy values from
+   * @return this measure
+   */
+  public MutableMeasure<U> mut_replace(Measure<U> other) {
+    m_magnitude = other.magnitude();
+    m_baseUnitMagnitude = other.baseUnitMagnitude();
+    m_unit = other.unit();
+    return this;
+  }
+
+  /**
+   * Overwrites the state of this measure with new values.
+   *
+   * @param magnitude the new magnitude in terms of the new unit
+   * @param unit the new unit
+   * @return this measure
+   */
+  public MutableMeasure<U> mut_replace(double magnitude, U unit) {
+    this.m_magnitude = magnitude;
+    this.m_baseUnitMagnitude = unit.toBaseUnits(magnitude);
+    this.m_unit = unit;
+    return this;
+  }
+
+  /**
+   * Increments the current magnitude of the measure by the given value. The value must be in terms
+   * of the current {@link #unit() unit}.
+   *
+   * @param raw the raw value to accumulate by
+   * @return the measure
+   */
+  public MutableMeasure<U> mut_acc(double raw) {
+    this.m_magnitude += raw;
+    this.m_baseUnitMagnitude += m_unit.toBaseUnits(raw);
+    return this;
+  }
+
+  /**
+   * Increments the current magnitude of the measure by the amount of the given measure.
+   *
+   * @param other the measure whose value should be added to this one
+   * @return the measure
+   */
+  public MutableMeasure<U> mut_acc(Measure<U> other) {
+    m_baseUnitMagnitude += other.baseUnitMagnitude();
+
+    // can't naively use m_magnitude += other.in(m_unit) because the units may not
+    // be scalar multiples (eg adding 0C to 100K should result in 373.15K, not 100K)
+    m_magnitude = m_unit.fromBaseUnits(m_baseUnitMagnitude);
+    return this;
+  }
+
+  // Math
+
+  /**
+   * Adds another measurement to this one. This will mutate the object instead of generating a new
+   * measurement object.
+   *
+   * @param other the measurement to add
+   * @return this measure
+   */
+  public MutableMeasure<U> mut_plus(Measure<U> other) {
+    return mut_plus(other.magnitude(), other.unit());
+  }
+
+  /**
+   * Adds another measurement to this one. This will mutate the object instead of generating a new
+   * measurement object. This is a denormalized version of {@link #mut_plus(Measure)} to avoid
+   * having to wrap raw numbers in a {@code Measure} object and pay for an object allocation.
+   *
+   * @param magnitude the magnitude of the other measurement.
+   * @param unit the unit of the other measurement
+   * @return this measure
+   */
+  public MutableMeasure<U> mut_plus(double magnitude, U unit) {
+    mut_setBaseUnitMagnitude(m_baseUnitMagnitude + unit.toBaseUnits(magnitude));
+    return this;
+  }
+
+  /**
+   * Subtracts another measurement to this one. This will mutate the object instead of generating a
+   * new measurement object.
+   *
+   * @param other the measurement to add
+   * @return this measure
+   */
+  public MutableMeasure<U> mut_minus(Measure<U> other) {
+    return mut_minus(other.magnitude(), other.unit());
+  }
+
+  /**
+   * Subtracts another measurement to this one. This will mutate the object instead of generating a
+   * new measurement object. This is a denormalized version of {@link #mut_minus(Measure)} to avoid
+   * having to wrap raw numbers in a {@code Measure} object and pay for an object allocation.
+   *
+   * @param magnitude the magnitude of the other measurement.
+   * @param unit the unit of the other measurement
+   * @return this measure
+   */
+  public MutableMeasure<U> mut_minus(double magnitude, U unit) {
+    return mut_plus(-magnitude, unit);
+  }
+
+  /**
+   * Multiplies this measurement by some constant value. This will mutate the object instead of
+   * generating a new measurement object.
+   *
+   * @param multiplier the multiplier to scale the measurement by
+   * @return this measure
+   */
+  public MutableMeasure<U> mut_times(double multiplier) {
+    mut_setBaseUnitMagnitude(m_baseUnitMagnitude * multiplier);
+    return this;
+  }
+
+  /**
+   * Multiplies this measurement by some constant value. This will mutate the object instead of
+   * generating a new measurement object.
+   *
+   * @param multiplier the multiplier to scale the measurement by
+   * @return this measure
+   */
+  public MutableMeasure<U> mut_times(Measure<? extends Dimensionless> multiplier) {
+    return mut_times(multiplier.baseUnitMagnitude());
+  }
+
+  /**
+   * Divides this measurement by some constant value. This will mutate the object instead of
+   * generating a new measurement object.
+   *
+   * @param divisor the divisor to scale the measurement by
+   * @return this measure
+   */
+  public MutableMeasure<U> mut_divide(double divisor) {
+    mut_setBaseUnitMagnitude(m_baseUnitMagnitude / divisor);
+    return this;
+  }
+
+  /**
+   * Divides this measurement by some constant value. This will mutate the object instead of
+   * generating a new measurement object.
+   *
+   * @param divisor the divisor to scale the measurement by
+   * @return this measure
+   */
+  public MutableMeasure<U> mut_divide(Measure<? extends Dimensionless> divisor) {
+    return mut_divide(divisor.baseUnitMagnitude());
+  }
+
+  @Override
+  public Measure<U> copy() {
+    return new ImmutableMeasure<>(m_magnitude, m_baseUnitMagnitude, m_unit);
+  }
+
+  @Override
+  public String toString() {
+    return toShortString();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof Measure)) {
+      return false;
+    }
+    Measure<?> that = (Measure<?>) o;
+    return Objects.equals(m_unit, that.unit()) && this.isEquivalent(that);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(m_magnitude, m_unit);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Per.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Per.java
new file mode 100644
index 0000000..3d8099f
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Per.java
@@ -0,0 +1,103 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import edu.wpi.first.units.collections.LongToObjectHashMap;
+import java.util.Objects;
+
+/**
+ * Generic combinatory unit type that represents the proportion of one unit to another, such as
+ * Meters per Second or Radians per Celsius.
+ *
+ * <p>Note that due to restrictions with the Java type system, velocities (change per unit time) are
+ * represented by the {@link Velocity} class. Accelerations are represented by {@code
+ * Velocity<Velocity<X>>}, and so on.
+ *
+ * @param <N> the type of the numerator unit
+ * @param <D> the type of the denominator unit
+ */
+public class Per<N extends Unit<N>, D extends Unit<D>> extends Unit<Per<N, D>> {
+  private final N m_numerator;
+  private final D m_denominator;
+
+  /**
+   * Keep a cache of created instances so expressions like Volts.per(Meter) don't do any allocations
+   * after the first.
+   */
+  @SuppressWarnings("rawtypes")
+  private static final LongToObjectHashMap<Per> cache = new LongToObjectHashMap<>();
+
+  protected Per(Class<Per<N, D>> baseType, N numerator, D denominator) {
+    super(
+        baseType,
+        numerator.toBaseUnits(1) / denominator.toBaseUnits(1),
+        numerator.name() + " per " + denominator.name(),
+        numerator.symbol() + "/" + denominator.symbol());
+    m_numerator = numerator;
+    m_denominator = denominator;
+  }
+
+  /**
+   * Creates a new Per unit derived from an arbitrary numerator and time denominator units. Using a
+   * denominator with a unit of time is discouraged; use {@link Velocity} instead.
+   *
+   * <pre>
+   *   Per.combine(Volts, Meters) // possible PID constant
+   * </pre>
+   *
+   * <p>It's recommended to use the convenience function {@link Unit#per(Unit)} instead of calling
+   * this factory directly.
+   *
+   * @param <N> the type of the numerator unit
+   * @param <D> the type of the denominator unit
+   * @param numerator the numerator unit
+   * @param denominator the denominator for unit time
+   * @return the combined unit
+   */
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  public static <N extends Unit<N>, D extends Unit<D>> Per<N, D> combine(
+      N numerator, D denominator) {
+    final long key =
+        ((long) numerator.hashCode()) << 32L | ((long) denominator.hashCode()) & 0xFFFFFFFFL;
+
+    var existing = cache.get(key);
+    if (existing != null) {
+      return existing;
+    }
+
+    var newUnit = new Per<N, D>((Class) Per.class, numerator, denominator);
+    cache.put(key, newUnit);
+    return newUnit;
+  }
+
+  public N numerator() {
+    return m_numerator;
+  }
+
+  public D denominator() {
+    return m_denominator;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    if (!super.equals(o)) {
+      return false;
+    }
+    Per<?, ?> per = (Per<?, ?>) o;
+    return Objects.equals(m_numerator, per.m_numerator)
+        && Objects.equals(m_denominator, per.m_denominator);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(super.hashCode(), m_numerator, m_denominator);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Power.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Power.java
new file mode 100644
index 0000000..cb93c66
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Power.java
@@ -0,0 +1,16 @@
+// 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.
+
+package edu.wpi.first.units;
+
+public class Power extends Unit<Power> {
+  Power(double baseUnitEquivalent, String name, String symbol) {
+    super(Power.class, baseUnitEquivalent, name, symbol);
+  }
+
+  Power(
+      UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super(Power.class, toBaseConverter, fromBaseConverter, name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Temperature.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Temperature.java
new file mode 100644
index 0000000..0a82e40
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Temperature.java
@@ -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.
+
+package edu.wpi.first.units;
+
+public class Temperature extends Unit<Temperature> {
+  Temperature(
+      UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super(Temperature.class, toBaseConverter, fromBaseConverter, name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Time.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Time.java
new file mode 100644
index 0000000..ad91343
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Time.java
@@ -0,0 +1,16 @@
+// 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.
+
+package edu.wpi.first.units;
+
+public class Time extends Unit<Time> {
+  /** Creates a new unit with the given name and multiplier to the base unit. */
+  Time(double baseUnitEquivalent, String name, String symbol) {
+    super(Time.class, baseUnitEquivalent, name, symbol);
+  }
+
+  Time(UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super(Time.class, toBaseConverter, fromBaseConverter, name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/UnaryFunction.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/UnaryFunction.java
new file mode 100644
index 0000000..ca5c8a2
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/UnaryFunction.java
@@ -0,0 +1,115 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import java.util.Objects;
+
+@FunctionalInterface
+public interface UnaryFunction {
+  /**
+   * Applies this function to the input value and returns the result.
+   *
+   * @param input the input value to the function
+   * @return the result
+   */
+  double apply(double input);
+
+  /**
+   * Constructs a new function that first calls this function, then passes the result to another as
+   * input.
+   *
+   * <pre>
+   * f = x -&gt; x + 1 // f(x) = x + 1
+   * g = x -&gt; 2 * x // g(x) = 2x
+   *
+   * h = f.pipeTo(g) // h(x) = g(f(x))
+   * </pre>
+   *
+   * @param next the next operation to pipe to
+   * @return the composite function g(f(x))
+   */
+  default UnaryFunction pipeTo(UnaryFunction next) {
+    Objects.requireNonNull(next, "The next operation in the chain must be provided");
+
+    return x -> next.apply(this.apply(x));
+  }
+
+  /**
+   * Creates a composite function h(x) such that h(x) = f(x) * g(x).
+   *
+   * @param multiplier the function to multiply this one by
+   * @return the composite function f(x) * g(x)
+   */
+  default UnaryFunction mult(UnaryFunction multiplier) {
+    Objects.requireNonNull(multiplier, "A multiplier function must be provided");
+
+    return x -> this.apply(x) * multiplier.apply(x);
+  }
+
+  /**
+   * Creates a composite function h(x) such that h(x) = k * f(x).
+   *
+   * @param multiplier the constant value to multiply this function's results by
+   * @return the composite function k * f(x)
+   */
+  default UnaryFunction mult(double multiplier) {
+    return x -> this.apply(x) * multiplier;
+  }
+
+  /**
+   * Creates a composite function h(x) such that h(x) = f(x) / g(x).
+   *
+   * @param divisor the function to divide this one by
+   * @return the composite function f(x) / g(x)
+   */
+  default UnaryFunction div(UnaryFunction divisor) {
+    Objects.requireNonNull(divisor, "A divisor function must be provided");
+
+    return x -> {
+      double numerator = this.apply(x);
+
+      // fast-track to avoid another function call
+      // avoids returning NaN if divisor is also zero
+      if (numerator == 0) {
+        return 0;
+      }
+
+      double div = divisor.apply(x);
+      return numerator / div; // NOTE: returns +Infinity or -Infinity if div is zero
+    };
+  }
+
+  /**
+   * Creates a composite function h(x) such that h(x) = 1/k * f(x).
+   *
+   * @param divisor the constant value to divide this function's results by
+   * @return the composite function 1/k * f(x)
+   */
+  default UnaryFunction div(double divisor) {
+    return x -> this.apply(x) / divisor;
+  }
+
+  /**
+   * Creates a composite function h(x) such that h(x) = f(x) ^ g(x).
+   *
+   * @param exponent the function to exponentiate this function's results by
+   * @return the composite function f(x) ^ g(x)
+   */
+  default UnaryFunction exp(UnaryFunction exponent) {
+    Objects.requireNonNull(exponent, "An exponent function must be provided");
+
+    return x -> Math.pow(this.apply(x), exponent.apply(x));
+  }
+
+  /**
+   * Creates a composite function h(x) such that h(x) = f(x) ^ k.
+   *
+   * @param exponent the constant value to exponentiate this function's results by
+   * @return the composite function f(x) ^ k
+   */
+  default UnaryFunction exp(double exponent) {
+    return x -> Math.pow(this.apply(x), exponent);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Unit.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Unit.java
new file mode 100644
index 0000000..df960ea
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Unit.java
@@ -0,0 +1,298 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import java.util.Objects;
+
+/**
+ * A unit is some unit of measurement that defines a quantity, such as grams, meters, or seconds.
+ *
+ * @param <U> the self type, e.g. {@code class SomeUnit extends Unit<SomeUnit>}
+ */
+public class Unit<U extends Unit<U>> {
+  private final UnaryFunction m_toBaseConverter;
+  private final UnaryFunction m_fromBaseConverter;
+
+  final Class<? extends U> m_baseType; // package-private for the builder
+
+  private Measure<U> m_zero;
+  private Measure<U> m_one;
+
+  private final String m_name;
+  private final String m_symbol;
+
+  /**
+   * Creates a new unit defined by its relationship to some base unit.
+   *
+   * @param baseType the base type of the unit, e.g. Distance.class for the distance unit
+   * @param toBaseConverter a function for converting units of this type to the base unit
+   * @param fromBaseConverter a function for converting units of the base unit to this one
+   * @param name the name of the unit. This should be a singular noun (so "Meter", not "Meters")
+   * @param symbol the short symbol for the unit, such as "m" for meters or "lb." for pounds
+   */
+  protected Unit(
+      Class<? extends U> baseType,
+      UnaryFunction toBaseConverter,
+      UnaryFunction fromBaseConverter,
+      String name,
+      String symbol) {
+    m_baseType = Objects.requireNonNull(baseType);
+    m_toBaseConverter = Objects.requireNonNull(toBaseConverter);
+    m_fromBaseConverter = Objects.requireNonNull(fromBaseConverter);
+    m_name = Objects.requireNonNull(name);
+    m_symbol = Objects.requireNonNull(symbol);
+  }
+
+  /**
+   * Creates a new unit with the given name and multiplier to the base unit.
+   *
+   * @param baseType the base type of the unit, e.g. Distance.class for the distance unit
+   * @param baseUnitEquivalent the multiplier to convert this unit to the base unit of this type.
+   *     For example, meters has a multiplier of 1, mm has a multiplier of 1e3, and km has
+   *     multiplier of 1e-3.
+   * @param name the name of the unit. This should be a singular noun (so "Meter", not "Meters")
+   * @param symbol the short symbol for the unit, such as "m" for meters or "lb." for pounds
+   */
+  protected Unit(
+      Class<? extends U> baseType, double baseUnitEquivalent, String name, String symbol) {
+    this(baseType, x -> x * baseUnitEquivalent, x -> x / baseUnitEquivalent, name, symbol);
+  }
+
+  /**
+   * Converts a value in terms of base units to a value in terms of this unit.
+   *
+   * @param valueInBaseUnits the value in base units to convert
+   * @return the equivalent value in terms of this unit
+   */
+  public double fromBaseUnits(double valueInBaseUnits) {
+    return m_fromBaseConverter.apply(valueInBaseUnits);
+  }
+
+  /**
+   * Converts a value in terms of this unit to a value in terms of the base unit.
+   *
+   * @param valueInNativeUnits the value in terms of this unit to convert
+   * @return the equivalent value in terms of the base unit
+   */
+  public double toBaseUnits(double valueInNativeUnits) {
+    return m_toBaseConverter.apply(valueInNativeUnits);
+  }
+
+  /**
+   * Converts a magnitude in terms of another unit of the same dimension to a magnitude in terms of
+   * this unit.
+   *
+   * <pre>
+   *   Inches.convertFrom(12, Feet) // 144.0
+   *   Kilograms.convertFrom(2.2, Pounds) // 0.9979024
+   * </pre>
+   *
+   * @param magnitude a magnitude measured in another unit
+   * @param otherUnit the unit to convert the magnitude to
+   * @return the corresponding value in terms of this unit.
+   */
+  public double convertFrom(double magnitude, Unit<U> otherUnit) {
+    if (this.equivalent(otherUnit)) {
+      // same unit, don't bother converting
+      return magnitude;
+    }
+    return this.fromBaseUnits(otherUnit.toBaseUnits(magnitude));
+  }
+
+  /**
+   * Gets the conversion function used to convert values to base unit terms. This generally
+   * shouldn't need to be used directly; prefer {@link #toBaseUnits(double)} instead.
+   *
+   * @return the conversion function
+   */
+  public UnaryFunction getConverterToBase() {
+    return m_toBaseConverter;
+  }
+
+  /**
+   * Gets the conversion function used to convert values to terms of this unit. This generally
+   * shouldn't need to be used directly; prefer {@link #fromBaseUnits(double)} instead.
+   *
+   * @return the conversion function
+   */
+  public UnaryFunction getConverterFromBase() {
+    return m_fromBaseConverter;
+  }
+
+  /**
+   * Creates a new measure of this unit with the given value. The resulting measure is
+   * <i>immutable</i> and cannot have its value modified.
+   *
+   * @param magnitude the magnitude of the measure to create
+   * @return the measure
+   */
+  public Measure<U> of(double magnitude) {
+    if (magnitude == 0) {
+      // reuse static object
+      return zero();
+    }
+    if (magnitude == 1) {
+      // reuse static object
+      return one();
+    }
+    return ImmutableMeasure.ofRelativeUnits(magnitude, this);
+  }
+
+  /**
+   * Creates a new measure with a magnitude equal to the given base unit magnitude, converted to be
+   * in terms of this unit.
+   *
+   * @param baseUnitMagnitude the magnitude of the measure in terms of the base unit
+   * @return the measure
+   */
+  public Measure<U> ofBaseUnits(double baseUnitMagnitude) {
+    return ImmutableMeasure.ofBaseUnits(baseUnitMagnitude, this);
+  }
+
+  /**
+   * Gets a measure with a magnitude of 0 in terms of this unit.
+   *
+   * @return the zero-valued measure
+   */
+  public Measure<U> zero() {
+    // lazy init because 'this' is null in object initialization
+    if (m_zero == null) {
+      m_zero = ImmutableMeasure.ofRelativeUnits(0, this);
+    }
+    return m_zero;
+  }
+
+  /**
+   * Gets a measure with a magnitude of 1 in terms of this unit.
+   *
+   * @return the 1-valued measure
+   */
+  public Measure<U> one() {
+    // lazy init because 'this' is null in object initialization
+    if (m_one == null) {
+      m_one = ImmutableMeasure.ofRelativeUnits(1, this);
+    }
+    return m_one;
+  }
+
+  /**
+   * Creates a velocity unit derived from this one. Can be chained to denote velocity, acceleration,
+   * jerk, etc.
+   *
+   * <pre>
+   *   Meters.per(Second) // linear velocity
+   *   Kilograms.per(Second) // mass flow
+   *   Feet.per(Second).per(Second).of(32) // roughly 1G of acceleration
+   * </pre>
+   *
+   * @param period the time period of the velocity, such as seconds or milliseconds
+   * @return a velocity unit corresponding to the rate of change of this unit over time
+   */
+  public Velocity<U> per(Time period) {
+    return Velocity.combine(this, period);
+  }
+
+  /**
+   * Takes this unit and creates a new proportional unit where this unit is the numerator and the
+   * given denominator is the denominator.
+   *
+   * <pre>
+   *   Volts.per(Meter) // V/m
+   * </pre>
+   *
+   * @param <D> the type of the denominator units
+   * @param denominator the denominator of the proportional unit
+   * @return a combined proportional unit
+   */
+  @SuppressWarnings("unchecked")
+  public <D extends Unit<D>> Per<U, D> per(D denominator) {
+    return Per.combine((U) this, denominator);
+  }
+
+  /**
+   * Takes this unit and creates a new combinatory unit equivalent to this unit multiplied by
+   * another.
+   *
+   * <pre>
+   *   Volts.mult(Meter) // V*m
+   * </pre>
+   *
+   * @param <U2> the type of the unit to multiply by
+   * @param other the unit to multiply by
+   * @return a combined unit equivalent to this unit multiplied by the other
+   */
+  @SuppressWarnings("unchecked")
+  public <U2 extends Unit<U2>> Mult<U, U2> mult(U2 other) {
+    return Mult.combine((U) this, other);
+  }
+
+  /**
+   * Checks if this unit is equivalent to another one. Equivalence is determined by both units
+   * having the same base type and treat the same base unit magnitude as the same magnitude in their
+   * own units, to within {@link Measure#EQUIVALENCE_THRESHOLD}.
+   *
+   * @param other the unit to compare to.
+   * @return true if both units are equivalent, false if not
+   */
+  public boolean equivalent(Unit<?> other) {
+    if (this.m_baseType != other.m_baseType) {
+      // different unit types, not compatible
+      return false;
+    }
+
+    double arbitrary = 16_777.214; // 2^24 / 1e3
+
+    return Math.abs(
+                this.m_fromBaseConverter.apply(arbitrary)
+                    - other.m_fromBaseConverter.apply(arbitrary))
+            <= Measure.EQUIVALENCE_THRESHOLD
+        && Math.abs(
+                this.m_toBaseConverter.apply(arbitrary) - other.m_toBaseConverter.apply(arbitrary))
+            <= Measure.EQUIVALENCE_THRESHOLD;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof Unit)) {
+      return false;
+    }
+    Unit<?> that = (Unit<?>) o;
+    return m_baseType.equals(that.m_baseType)
+        && m_name.equals(that.m_name)
+        && m_symbol.equals(that.m_symbol)
+        && this.equivalent(that);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(m_toBaseConverter, m_fromBaseConverter, m_baseType, m_name, m_symbol);
+  }
+
+  /**
+   * Gets the name of this unit.
+   *
+   * @return the unit's name
+   */
+  public String name() {
+    return m_name;
+  }
+
+  /**
+   * Gets the symbol of this unit.
+   *
+   * @return the unit's symbol
+   */
+  public String symbol() {
+    return m_symbol;
+  }
+
+  @Override
+  public String toString() {
+    return name();
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/UnitBuilder.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/UnitBuilder.java
new file mode 100644
index 0000000..b8dc0b9
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/UnitBuilder.java
@@ -0,0 +1,217 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Objects;
+
+/**
+ * Builder used for easily deriving new units from existing ones.
+ *
+ * @param <U> the type of the unit
+ */
+public final class UnitBuilder<U extends Unit<U>> {
+  private final U m_base;
+  private UnaryFunction m_fromBase;
+  private UnaryFunction m_toBase;
+  private String m_name;
+  private String m_symbol;
+
+  public UnitBuilder(U base) {
+    this.m_base = Objects.requireNonNull(base, "Base unit cannot be null");
+  }
+
+  /**
+   * Sets the unit conversions based on a simple offset. The new unit will have its values equal to
+   * (base value - offset).
+   *
+   * @param offset the offset
+   * @return this builder
+   */
+  public UnitBuilder<U> offset(double offset) {
+    m_toBase = derivedValue -> derivedValue + offset;
+    m_fromBase = baseValue -> baseValue - offset;
+    return this;
+  }
+
+  /**
+   * Maps a value {@code value} in the range {@code [inMin..inMax]} to an output in the range {@code
+   * [outMin..outMax]}. Inputs outside the bounds will be mapped correspondingly to outputs outside
+   * the output bounds. Inputs equal to {@code inMin} will be mapped to {@code outMin}, and inputs
+   * equal to {@code inMax} will similarly be mapped to {@code outMax}.
+   *
+   * @param value the value to map
+   * @param inMin the minimum input value (does not have to be absolute)
+   * @param inMax the maximum input value (does not have to be absolute)
+   * @param outMin the minimum output value (does not have to be absolute)
+   * @param outMax the maximum output value (does not have to be absolute)
+   * @return the mapped output
+   */
+  // NOTE: This method lives here instead of in MappingBuilder because inner classes can't
+  // define static methods prior to Java 16.
+  private static double mapValue(
+      double value, double inMin, double inMax, double outMin, double outMax) {
+    return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
+  }
+
+  /** Helper class used for safely chaining mapping builder calls. */
+  public class MappingBuilder {
+    private final double m_minInput;
+    private final double m_maxInput;
+
+    private MappingBuilder(double minInput, double maxInput) {
+      this.m_minInput = minInput;
+      this.m_maxInput = maxInput;
+    }
+
+    /**
+     * Finalizes the mapping by defining the output range.
+     *
+     * @param minOutput the minimum output value (does not have to be absolute)
+     * @param maxOutput the maximum output value (does not have to be absolute)
+     * @return the unit builder, for continued chaining
+     */
+    public UnitBuilder<U> toOutputRange(double minOutput, double maxOutput) {
+      UnitBuilder.this.m_fromBase = x -> mapValue(x, m_minInput, m_maxInput, minOutput, maxOutput);
+      UnitBuilder.this.m_toBase = y -> mapValue(y, minOutput, maxOutput, m_minInput, m_maxInput);
+      return UnitBuilder.this;
+    }
+  }
+
+  /**
+   * Defines a mapping for values within the given input range. This method call should be
+   * immediately followed by {@code .toOutputRange}, eg {@code mappingInputRange(1,
+   * 2).toOutputRange(3, 4)}, which will return the unit builder for continued chaining.
+   *
+   * @param minBase the minimum input value (does not have to be absolute)
+   * @param maxBase the maximum output value (does not have to be absolute)
+   * @return a builder object used to define the output range
+   */
+  public MappingBuilder mappingInputRange(double minBase, double maxBase) {
+    return new MappingBuilder(minBase, maxBase);
+  }
+
+  /**
+   * Sets the conversion function to transform values in the base unit to values in the derived
+   * unit.
+   *
+   * @param fromBase the conversion function
+   * @return the unit builder, for continued chaining
+   */
+  public UnitBuilder<U> fromBase(UnaryFunction fromBase) {
+    this.m_fromBase = Objects.requireNonNull(fromBase, "fromBase function cannot be null");
+    return this;
+  }
+
+  /**
+   * Sets the conversion function to transform values in the derived unit to values in the base
+   * unit.
+   *
+   * @param toBase the conversion function
+   * @return the unit builder, for continued chaining
+   */
+  public UnitBuilder<U> toBase(UnaryFunction toBase) {
+    this.m_toBase = Objects.requireNonNull(toBase, "toBase function cannot be null");
+    return this;
+  }
+
+  /**
+   * Sets the name of the new unit.
+   *
+   * @param name the new name
+   * @return the unit builder, for continued chaining
+   */
+  public UnitBuilder<U> named(String name) {
+    this.m_name = name;
+    return this;
+  }
+
+  /**
+   * Sets the symbol of the new unit.
+   *
+   * @param symbol the new symbol
+   * @return the unit builder, for continued chaining
+   */
+  public UnitBuilder<U> symbol(String symbol) {
+    this.m_symbol = symbol;
+    return this;
+  }
+
+  /**
+   * Helper for defining units that are a scalar fraction of the base unit, such as centimeters
+   * being 1/100th of the base unit (meters). The fraction value is specified as the denominator of
+   * the fraction, so a centimeter definition would use {@code splitInto(100)} instead of {@code
+   * splitInto(1/100.0)}.
+   *
+   * @param fraction the denominator portion of the fraction of the base unit that a value of 1 in
+   *     the derived unit corresponds to
+   * @return the unit builder, for continued chaining
+   */
+  public UnitBuilder<U> splitInto(double fraction) {
+    if (fraction == 0) {
+      throw new IllegalArgumentException("Fraction must be nonzero");
+    }
+
+    return toBase(x -> x / fraction).fromBase(b -> b * fraction);
+  }
+
+  /**
+   * Helper for defining units that are a scalar multiple of the base unit, such as kilometers being
+   * 1000x of the base unit (meters).
+   *
+   * @param aggregation the magnitude required for a measure in the base unit to equal a magnitude
+   *     of 1 in the derived unit
+   * @return the unit builder, for continued chaining
+   */
+  public UnitBuilder<U> aggregate(double aggregation) {
+    if (aggregation == 0) {
+      throw new IllegalArgumentException("Aggregation amount must be nonzero");
+    }
+
+    return toBase(x -> x * aggregation).fromBase(b -> b / aggregation);
+  }
+
+  /**
+   * Creates the new unit based off of the builder methods called prior.
+   *
+   * @return the new derived unit
+   * @throws NullPointerException if the unit conversions, unit name, or unit symbol were not set
+   * @throws RuntimeException if the base unit does not define a constructor accepting the
+   *     conversion functions, unit name, and unit symbol - in that order
+   */
+  @SuppressWarnings("PMD.AvoidAccessibilityAlteration")
+  public U make() {
+    Objects.requireNonNull(m_fromBase, "fromBase function was not set");
+    Objects.requireNonNull(m_toBase, "toBase function was not set");
+    Objects.requireNonNull(m_name, "new unit name was not set");
+    Objects.requireNonNull(m_symbol, "new unit symbol was not set");
+    Class<? extends U> baseType = m_base.m_baseType;
+    try {
+      Constructor<? extends U> ctor =
+          baseType.getDeclaredConstructor(
+              UnaryFunction.class, // toBaseUnits
+              UnaryFunction.class, // fromBaseUnits
+              String.class, // name
+              String.class); // symbol
+      // need to flag the constructor as accessible so we can use private, package-private, and
+      // protected constructors
+      ctor.setAccessible(true);
+      return ctor.newInstance(
+          m_toBase.pipeTo(m_base.getConverterToBase()),
+          m_base.getConverterFromBase().pipeTo(m_fromBase),
+          m_name,
+          m_symbol);
+    } catch (InstantiationException e) {
+      throw new RuntimeException("Could not instantiate class " + baseType.getName(), e);
+    } catch (IllegalAccessException e) {
+      throw new RuntimeException("Could not access constructor", e);
+    } catch (InvocationTargetException e) {
+      throw new RuntimeException("Constructing " + baseType.getName() + " raised an exception", e);
+    } catch (NoSuchMethodException e) {
+      throw new RuntimeException("No compatible constructor", e);
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Units.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Units.java
new file mode 100644
index 0000000..ecf0768
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Units.java
@@ -0,0 +1,213 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import java.util.Locale;
+
+public final class Units {
+  private Units() {
+    // Prevent instantiation
+  }
+
+  // Pseudo-classes describing the more common units of measure.
+
+  @SuppressWarnings("rawtypes")
+  public static final Unit AnonymousBaseUnit = new Dimensionless(1, "<?>", "<?>");
+
+  // Distance
+  public static final Distance Meters = BaseUnits.Distance;
+  public static final Distance Millimeters = Milli(Meters, "Millimeter", "mm");
+  public static final Distance Centimeters =
+      derive(Meters).splitInto(100).named("Centimeter").symbol("cm").make();
+  public static final Distance Inches =
+      derive(Millimeters).aggregate(25.4).named("Inch").symbol("in").make();
+  public static final Distance Feet =
+      derive(Inches).aggregate(12).named("Foot").symbol("ft").make();
+
+  // Time
+  public static final Time Seconds = BaseUnits.Time;
+  public static final Time Second = Seconds; // singularized alias
+  public static final Time Milliseconds = Milli(Seconds);
+  public static final Time Millisecond = Milliseconds; // singularized alias
+  public static final Time Microseconds = Micro(Seconds);
+  public static final Time Microsecond = Microseconds; // singularized alias
+  public static final Time Minutes =
+      derive(Seconds).aggregate(60).named("Minute").symbol("min").make();
+  public static final Time Minute = Minutes; // singularized alias
+
+  // Angle
+  public static final Angle Revolutions = BaseUnits.Angle;
+  public static final Angle Rotations = new Angle(1, "Rotation", "R"); // alias
+  public static final Angle Radians =
+      derive(Revolutions).splitInto(2 * Math.PI).named("Radian").symbol("rad").make();
+  public static final Angle Degrees =
+      derive(Revolutions).splitInto(360).named("Degree").symbol("°").make();
+
+  // Velocity
+  public static final Velocity<Distance> MetersPerSecond = Meters.per(Second);
+  public static final Velocity<Distance> FeetPerSecond = Feet.per(Second);
+  public static final Velocity<Distance> InchesPerSecond = Inches.per(Second);
+
+  public static final Velocity<Angle> RevolutionsPerSecond = Revolutions.per(Second);
+  public static final Velocity<Angle> RotationsPerSecond = Rotations.per(Second);
+  public static final Velocity<Angle> RPM = Rotations.per(Minute);
+  public static final Velocity<Angle> RadiansPerSecond = Radians.per(Second);
+  public static final Velocity<Angle> DegreesPerSecond = Degrees.per(Second);
+
+  // Acceleration
+  public static final Velocity<Velocity<Distance>> MetersPerSecondPerSecond =
+      MetersPerSecond.per(Second);
+  public static final Velocity<Velocity<Distance>> Gs =
+      derive(MetersPerSecondPerSecond).aggregate(9.80665).named("G").symbol("G").make();
+
+  // Mass
+  public static final Mass Kilograms = BaseUnits.Mass;
+  public static final Mass Grams = Milli(Kilograms, "Gram", "g");
+  public static final Mass Pounds =
+      derive(Grams).aggregate(453.592).named("Pound").symbol("lb.").make();
+  public static final Mass Ounces =
+      derive(Pounds).splitInto(16).named("Ounce").symbol("oz.").make();
+
+  // Unitless
+  public static final Dimensionless Value = BaseUnits.Value;
+  public static final Dimensionless Percent =
+      derive(Value).splitInto(100).named("Percent").symbol("%").make();
+
+  // Voltage
+  public static final Voltage Volts = BaseUnits.Voltage;
+  public static final Voltage Millivolts = Milli(Volts);
+
+  // Current
+  public static final Current Amps = BaseUnits.Current;
+  public static final Current Milliamps = Milli(Amps);
+
+  // Energy
+  public static final Energy Joules = BaseUnits.Energy;
+  public static final Energy Millijoules = Milli(Joules);
+  public static final Energy Kilojoules = Kilo(Joules);
+
+  // Power
+  public static final Power Watts = BaseUnits.Power;
+  public static final Power Milliwatts = Milli(Watts);
+  public static final Power Horsepower =
+      derive(Watts).aggregate(745.7).named("Horsepower").symbol("HP").make();
+
+  // Temperature
+  public static final Temperature Kelvin = BaseUnits.Temperature;
+  public static final Temperature Celsius =
+      derive(Kelvin).offset(+273.15).named("Celsius").symbol("°C").make();
+
+  public static final Temperature Fahrenheit =
+      derive(Celsius)
+          .mappingInputRange(0, 100)
+          .toOutputRange(32, 212)
+          .named("Fahrenheit")
+          .symbol("°F")
+          .make();
+
+  // Standard feedforward units for kV and kA.
+  // kS and kG are just volts, which is already defined earlier
+  public static final Per<Voltage, Velocity<Distance>> VoltsPerMeterPerSecond =
+      Volts.per(MetersPerSecond);
+  public static final Per<Voltage, Velocity<Velocity<Distance>>> VoltsPerMeterPerSecondSquared =
+      Volts.per(MetersPerSecondPerSecond);
+
+  public static final Per<Voltage, Velocity<Angle>> VoltsPerRadianPerSecond =
+      Volts.per(RadiansPerSecond);
+  public static final Per<Voltage, Velocity<Velocity<Angle>>> VoltsPerRadianPerSecondSquared =
+      Volts.per(RadiansPerSecond.per(Second));
+
+  /**
+   * Creates a unit equal to a thousandth of the base unit, eg Milliseconds = Milli(Units.Seconds).
+   *
+   * @param <U> the type of the unit
+   * @param baseUnit the unit being derived from. This does not have to be the base unit of measure
+   * @param name the name of the new derived unit
+   * @param symbol the symbol of the new derived unit
+   * @return the milli-unit
+   */
+  @SuppressWarnings({"PMD.MethodName", "checkstyle:methodname"})
+  public static <U extends Unit<U>> U Milli(Unit<U> baseUnit, String name, String symbol) {
+    return derive(baseUnit).splitInto(1000).named(name).symbol(symbol).make();
+  }
+
+  /**
+   * Creates a unit equal to a thousandth of the base unit, eg Milliseconds = Milli(Units.Seconds).
+   *
+   * @param <U> the type of the unit
+   * @param baseUnit the unit being derived from. This does not have to be the base unit of measure
+   * @return the milli-unit
+   */
+  @SuppressWarnings({"PMD.MethodName", "checkstyle:methodname"})
+  public static <U extends Unit<U>> U Milli(Unit<U> baseUnit) {
+    return Milli(
+        baseUnit, "Milli" + baseUnit.name().toLowerCase(Locale.ROOT), "m" + baseUnit.symbol());
+  }
+
+  /**
+   * Creates a unit equal to a millionth of the base unit, eg {@code Microseconds =
+   * Micro(Units.Seconds, "Microseconds", 'us")}.
+   *
+   * @param <U> the type of the unit
+   * @param baseUnit the unit being derived from. This does not have to be the base unit of measure
+   * @param name the name of the new derived unit
+   * @param symbol the symbol of the new derived unit
+   * @return the micro-unit
+   */
+  @SuppressWarnings({"PMD.MethodName", "checkstyle:methodname"})
+  public static <U extends Unit<U>> U Micro(Unit<U> baseUnit, String name, String symbol) {
+    return derive(baseUnit).splitInto(1_000_000).named(name).symbol(symbol).make();
+  }
+
+  /**
+   * Creates a unit equal to a millionth of the base unit, eg Microseconds = Micro(Units.Seconds).
+   *
+   * @param <U> the type of the unit
+   * @param baseUnit the unit being derived from. This does not have to be the base unit of measure
+   * @return the micro-unit
+   */
+  @SuppressWarnings({"PMD.MethodName", "checkstyle:methodname"})
+  public static <U extends Unit<U>> U Micro(Unit<U> baseUnit) {
+    return Micro(
+        baseUnit, "Micro" + baseUnit.name().toLowerCase(Locale.ROOT), "u" + baseUnit.symbol());
+  }
+
+  /**
+   * Creates a unit equal to a thousand of the base unit, eg Kilograms = Kilo(Units.Grams).
+   *
+   * @param <U> the type of the unit
+   * @param baseUnit the unit being derived from. This does not have to be the base unit of measure
+   * @param name the name of the new derived unit
+   * @param symbol the symbol of the new derived unit
+   * @return the kilo-unit
+   */
+  @SuppressWarnings({"PMD.MethodName", "checkstyle:methodname"})
+  public static <U extends Unit<U>> U Kilo(Unit<U> baseUnit, String name, String symbol) {
+    return derive(baseUnit).aggregate(1000).named(name).symbol(symbol).make();
+  }
+
+  /**
+   * Creates a unit equal to a thousand of the base unit, eg Kilograms = Kilo(Units.Grams).
+   *
+   * @param <U> the type of the unit
+   * @param baseUnit the unit being derived from. This does not have to be the base unit of measure
+   * @return the kilo-unit
+   */
+  @SuppressWarnings({"PMD.MethodName", "checkstyle:methodname"})
+  public static <U extends Unit<U>> U Kilo(Unit<U> baseUnit) {
+    return Kilo(
+        baseUnit, "Kilo" + baseUnit.name().toLowerCase(Locale.ROOT), "K" + baseUnit.symbol());
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <U extends Unit<U>> UnitBuilder<U> derive(Unit<U> unit) {
+    return new UnitBuilder<>((U) unit);
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <U extends Unit<U>> U anonymous() {
+    return (U) AnonymousBaseUnit;
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Velocity.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Velocity.java
new file mode 100644
index 0000000..87cffb7
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Velocity.java
@@ -0,0 +1,156 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import edu.wpi.first.units.collections.LongToObjectHashMap;
+import java.util.Objects;
+
+public class Velocity<D extends Unit<D>> extends Unit<Velocity<D>> {
+  private final D m_unit;
+  private final Time m_period;
+
+  /**
+   * Stores velocity units that were created ad-hoc using {@link #combine(Unit, Time, String,
+   * String)}. Does not store objects created directly by constructors.
+   */
+  @SuppressWarnings("rawtypes")
+  private static final LongToObjectHashMap<Velocity> cache = new LongToObjectHashMap<>();
+
+  /** Generates a cache key used for cache lookups. */
+  private static long cacheKey(Unit<?> numerator, Unit<?> denominator) {
+    return ((long) numerator.hashCode()) << 32L | ((long) denominator.hashCode()) & 0xFFFFFFFFL;
+  }
+
+  /**
+   * Creates a new velocity unit derived from an arbitrary numerator and time period units.
+   *
+   * <p>Results of this method are cached so future invocations with the same arguments will return
+   * the pre-existing units instead of generating new identical ones.
+   *
+   * <pre>
+   *   Velocity.combine(Kilograms, Second) // mass flow
+   *   Velocity.combine(Feet, Millisecond) // linear speed
+   *   Velocity.combine(Radians, Second) // angular speed
+   *
+   *   Velocity.combine(Feet.per(Second), Second) // linear acceleration in ft/s/s
+   *   Velocity.combine(Radians.per(Second), Second) // angular acceleration
+   * </pre>
+   *
+   * <p>It's recommended to use the convenience function {@link Unit#per(Time)} instead of calling
+   * this factory directly.
+   *
+   * @param <D> the type of the numerator unit
+   * @param numerator the numerator unit
+   * @param period the period for unit time
+   * @param name the name of the new velocity unit
+   * @param symbol the symbol of the new velocity unit
+   * @return the new unit
+   */
+  @SuppressWarnings("unchecked")
+  public static <D extends Unit<D>> Velocity<D> combine(
+      Unit<D> numerator, Time period, String name, String symbol) {
+    long key = cacheKey(numerator, period);
+    if (cache.containsKey(key)) {
+      return cache.get(key);
+    }
+
+    Velocity<D> velocity = new Velocity<>((D) numerator, period, name, symbol);
+    cache.put(key, velocity);
+    return velocity;
+  }
+
+  /**
+   * Creates a new velocity unit derived from an arbitrary numerator and time period units.
+   *
+   * <p>Results of this method are cached so future invocations with the same arguments will return
+   * the pre-existing units instead of generating new identical ones.
+   *
+   * <p>This method automatically generates a new name and symbol for the new velocity unit.
+   *
+   * <pre>
+   *   Velocity.combine(Kilograms, Second) // mass flow
+   *   Velocity.combine(Feet, Millisecond) // linear speed
+   *   Velocity.combine(Radians, Second) // angular speed
+   *
+   *   Velocity.combine(Feet.per(Second), Second) // linear acceleration in ft/s/s
+   *   Velocity.combine(Radians.per(Second), Second) // angular acceleration
+   * </pre>
+   *
+   * <p>It's recommended to use the convenience function {@link Unit#per(Time)} instead of calling
+   * this factory directly.
+   *
+   * @param <D> the type of the numerator unit
+   * @param numerator the numerator unit
+   * @param period the period for unit time
+   * @return the new unit
+   */
+  @SuppressWarnings("unchecked")
+  public static <D extends Unit<D>> Velocity<D> combine(Unit<D> numerator, Time period) {
+    long key = cacheKey(numerator, period);
+    if (cache.containsKey(key)) {
+      return cache.get(key);
+    }
+
+    var name = numerator.name() + " per " + period.name();
+    var symbol = numerator.symbol() + "/" + period.symbol();
+
+    Velocity<D> velocity = new Velocity<>((D) numerator, period, name, symbol);
+    cache.put(key, velocity);
+    return velocity;
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  Velocity(D unit, Time period, String name, String symbol) {
+    super((Class) Velocity.class, unit.toBaseUnits(1) / period.toBaseUnits(1), name, symbol);
+    this.m_unit = unit;
+    this.m_period = period;
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  Velocity(
+      UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super((Class) Velocity.class, toBaseConverter, fromBaseConverter, name, symbol);
+    this.m_unit = Units.anonymous();
+    this.m_period = Units.Seconds;
+  }
+
+  /**
+   * Gets the major unit being measured (eg Meters for Meters per Second).
+   *
+   * @return the major unit
+   */
+  public D getUnit() {
+    return m_unit;
+  }
+
+  /**
+   * Gets the period unit of the velocity, eg Seconds or Milliseconds.
+   *
+   * @return the period unit
+   */
+  public Time getPeriod() {
+    return m_period;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    if (!super.equals(o)) {
+      return false;
+    }
+    Velocity<?> velocity = (Velocity<?>) o;
+    return m_unit.equals(velocity.m_unit) && m_period.equals(velocity.m_period);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(super.hashCode(), m_unit, m_period);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Voltage.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Voltage.java
new file mode 100644
index 0000000..3c30203
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/Voltage.java
@@ -0,0 +1,20 @@
+// 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.
+
+package edu.wpi.first.units;
+
+public class Voltage extends Unit<Voltage> {
+  Voltage(double baseUnitEquivalent, String name, String symbol) {
+    super(Voltage.class, baseUnitEquivalent, name, symbol);
+  }
+
+  Voltage(
+      UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
+    super(Voltage.class, toBaseConverter, fromBaseConverter, name, symbol);
+  }
+
+  public Power times(Unit<Current> current, String name, String symbol) {
+    return new Power(toBaseUnits(1) * current.toBaseUnits(1), name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/collections/LongToObjectHashMap.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/collections/LongToObjectHashMap.java
new file mode 100644
index 0000000..ff2e94d
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/collections/LongToObjectHashMap.java
@@ -0,0 +1,332 @@
+// 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.
+
+package edu.wpi.first.units.collections;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * A variant on {@code java.util.HashMap<K, V>} that uses primitive long ints for map keys instead
+ * of autoboxed Long objects like would be used for a {@code Map<Long, V>}.
+ *
+ * @param <V> the type of the values stored in the map
+ */
+public class LongToObjectHashMap<V> {
+  private static final int kInitialSize = 0;
+  private static final int kInitialCapacity = 8; // NOTE: must be a power of two
+
+  /**
+   * The default load factor of the hashmap. If the ratio of the number of entries to the map's
+   * capacity exceeds this value, the map will be resized (doubled capacity) in order for more
+   * values to be easily inserted.
+   */
+  private static final double kLoadFactor = 75.00 / 100;
+
+  /** The current number of key-value pairs in the map. */
+  private int m_size = kInitialSize;
+
+  /**
+   * The current maximum capacity of the map. Note that it will be resized before m_size reaches
+   * this value.
+   */
+  private int m_capacity = kInitialCapacity;
+
+  /**
+   * The keys in the map. This is a sparse array, and the location of a key may not be equal to the
+   * result of calling {@link #bucket(long)} on that key. To handle hash collisions, if a bucket is
+   * already in use when trying to insert a value, the bucket number is incremented (wrapping around
+   * to 0 if it's equal to m_capacity) and <i>that</i> bucket is checked to see if it's available.
+   * This process continues until an empty bucket is found (which is guaranteed because m_size is
+   * always less than m_capacity).
+   */
+  private long[] m_keys = new long[m_capacity];
+
+  /** Tracks which buckets are actually used (have a key-value mapping). */
+  private boolean[] m_uses = new boolean[m_capacity];
+
+  /**
+   * The values in the map. See the documentation for m_keys for how indexing into this array works.
+   */
+  @SuppressWarnings("unchecked")
+  private V[] m_values = (V[]) new Object[m_capacity];
+
+  /**
+   * Puts a value {@code value} corresponding to key {@code key} in the map.
+   *
+   * @param key the associated key
+   * @param value the value to insert
+   * @return the previous value that was mapped to the key, or null if no such value existed
+   */
+  public V put(long key, V value) {
+    int bucket = bucket(key);
+
+    // Increment the bucket until we hit an open space (there's always going to be at least one)
+    while (m_uses[bucket]) {
+      if (m_keys[bucket] == key) {
+        // replace the existing value
+        var oldValue = m_values[bucket];
+        m_values[bucket] = value;
+        return oldValue;
+      }
+      bucket = safeIncrement(bucket);
+    }
+
+    m_uses[bucket] = true;
+    m_keys[bucket] = key;
+    m_values[bucket] = value;
+    m_size++;
+
+    if (m_size > maxSize()) {
+      grow();
+    }
+    return null;
+  }
+
+  /**
+   * Gets the value associated with the given key.
+   *
+   * @param key the key to retrieve the value for
+   * @return the value mapped to the key, or null if the key is not in the map
+   */
+  public V get(long key) {
+    int bucket = bucket(key);
+    while (m_uses[bucket]) {
+      if (m_keys[bucket] == key) {
+        // found it
+        return m_values[bucket];
+      }
+      bucket = safeIncrement(bucket);
+    }
+    return null;
+  }
+
+  /**
+   * Removes the value associated with the given key and returns it.
+   *
+   * @param key the key to remove
+   * @return the value corresponding to the key, or null if the key is not in the map
+   */
+  public V remove(long key) {
+    int bucket = bucket(key);
+    while (m_uses[bucket]) {
+      if (m_keys[bucket] == key) {
+        // found it
+        // TODO: Shrink the map when below a certain load factor
+        //       Current use cases don't remove elements from the map, so there's not much use
+        //       for shrinking at the moment.
+        m_size--;
+        m_keys[bucket] = 0L;
+        m_uses[bucket] = false;
+
+        var oldValue = m_values[bucket];
+        m_values[bucket] = null;
+        return oldValue;
+      }
+      bucket = safeIncrement(bucket);
+    }
+
+    return null;
+  }
+
+  /**
+   * Checks if a key is contained in the map.
+   *
+   * @param key the key to check
+   * @return true if the key has an associated value, false if not
+   */
+  public boolean containsKey(long key) {
+    int bucket = bucket(key);
+    while (m_uses[bucket]) {
+      if (m_keys[bucket] == key) {
+        return true;
+      }
+      bucket = safeIncrement(bucket);
+    }
+    return false;
+  }
+
+  /** Clears and removes all entries from the map. */
+  public void clear() {
+    if (m_size == 0) {
+      // Nothing to do
+      return;
+    }
+    m_size = 0;
+
+    Arrays.fill(m_uses, false);
+    Arrays.fill(m_keys, 0L);
+    Arrays.fill(m_values, null);
+  }
+
+  /**
+   * Gets the number of key-value pairs currently contained in the map.
+   *
+   * @return the current size of the map
+   */
+  public int size() {
+    return m_size;
+  }
+
+  // package-private for tests
+  int capacity() {
+    return m_capacity;
+  }
+
+  /**
+   * Checks if the map contains any entries.
+   *
+   * @return true if at least one entry is present, false otherwise
+   */
+  public boolean isEmpty() {
+    return m_size == 0;
+  }
+
+  /**
+   * Gets the keys contained in the map. Ordering is not guaranteed. The returned set is read-only
+   * and immutable. This uses a custom class for primitive long values to avoid unnecessary
+   * autoboxing to {@code java.lang.Long}.
+   *
+   * @return a read-only set of keys
+   */
+  public ReadOnlyPrimitiveLongSet keySet() {
+    // copy the sparse key array into a compact array
+    final long[] keys = new long[m_size];
+    int i = 0;
+    for (int bucket = 0; bucket < m_capacity; bucket++) {
+      if (m_uses[bucket]) {
+        keys[i] = m_keys[bucket];
+        i++;
+      }
+    }
+
+    return new ReadOnlyPrimitiveLongSet(keys);
+  }
+
+  /**
+   * Gets the values contained in the map. Ordering is not guaranteed. The returned collection is
+   * read-only and immutable.
+   *
+   * @return a read-only collection of values
+   */
+  public Collection<V> values() {
+    Collection<V> values = new ArrayList<>();
+    for (int bucket = 0; bucket < m_capacity; bucket++) {
+      if (m_uses[bucket]) {
+        values.add(m_values[bucket]);
+      }
+    }
+    return List.copyOf(values); // return a readonly copy
+  }
+
+  @FunctionalInterface
+  public interface IteratorFunction<V> {
+    void accept(long key, V value);
+  }
+
+  /**
+   * Iterates over every key-value pair in the map and passes them to the given function.
+   *
+   * @param function the function to apply to every key-value pair.
+   */
+  public void forEach(IteratorFunction<? super V> function) {
+    for (int bucket = 0; bucket < m_capacity; bucket++) {
+      if (m_uses[bucket]) {
+        function.accept(m_keys[bucket], m_values[bucket]);
+      }
+    }
+  }
+
+  private void grow() {
+    final int currentSize = m_size;
+    final int oldCapacity = m_capacity;
+    if (oldCapacity * kLoadFactor >= currentSize) {
+      // We're below the maximum allowed size for the current capacity
+      // Nothing to do
+      return;
+    }
+
+    final int newCapacity = oldCapacity * 2;
+    final int newMask = newCapacity - 1;
+
+    final boolean[] oldUses = m_uses;
+    final long[] oldKeys = m_keys;
+    final V[] oldValues = m_values;
+
+    final boolean[] newUses = new boolean[newCapacity];
+    final long[] newKeys = new long[newCapacity];
+    @SuppressWarnings("unchecked")
+    final V[] newValues = (V[]) new Object[newCapacity];
+
+    for (int oldBucket = 0; oldBucket < oldCapacity; oldBucket++) {
+      if (!oldUses[oldBucket]) {
+        // Bucket is empty, skip
+        continue;
+      }
+      final long key = oldKeys[oldBucket];
+      final V value = oldValues[oldBucket];
+
+      int newBucket = (int) (hash(key) & newMask);
+      while (newUses[newBucket]) {
+        newBucket = (newBucket + 1) & newMask;
+      }
+
+      newUses[newBucket] = true;
+      newKeys[newBucket] = key;
+      newValues[newBucket] = value;
+    }
+
+    m_capacity = newCapacity;
+    m_uses = newUses;
+    m_keys = newKeys;
+    m_values = newValues;
+  }
+
+  private int maxSize() {
+    return (int) (m_capacity * kLoadFactor);
+  }
+
+  /**
+   * Calculates a hashcode for an input key. Does some bit shuffling to account for poor hash
+   * functions.
+   *
+   * @param key the key to hash
+   * @return a hashcode for the input key
+   */
+  private long hash(long key) {
+    return 31 + (key ^ (key >>> 15) ^ (key >>> 31) ^ (key << 31));
+  }
+
+  /**
+   * The mask to use when translating a hashcode to a bucket index. Relies on m_capacity being a
+   * power of two.
+   */
+  private int mask() {
+    return m_capacity - 1;
+  }
+
+  /**
+   * Calculates the desired bucket index for a particular key. Does nothing to handle the case where
+   * the calculated index is already in use by another key.
+   *
+   * @param key the key to get the bucket for
+   * @return the desired bucket index
+   */
+  private int bucket(long key) {
+    var hash = hash(key);
+    return (int) (hash & mask());
+  }
+
+  /**
+   * Increments a bucket index by 1, wrapping around to 0 if the index is already at the maximum.
+   *
+   * @param bucket the index to increment
+   * @return the incremented bucket index
+   */
+  private int safeIncrement(int bucket) {
+    return (bucket + 1) & mask();
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/collections/ReadOnlyPrimitiveLongSet.java b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/collections/ReadOnlyPrimitiveLongSet.java
new file mode 100644
index 0000000..0b13824
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/main/java/edu/wpi/first/units/collections/ReadOnlyPrimitiveLongSet.java
@@ -0,0 +1,131 @@
+// 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.
+
+package edu.wpi.first.units.collections;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.stream.LongStream;
+
+/** A read-only set of unique primitive {@code long} values. */
+public class ReadOnlyPrimitiveLongSet implements Iterable<Long> {
+  private final long[] m_values;
+
+  /**
+   * Creates a new set from the given values. These values do not have to be unique.
+   *
+   * @param values the values that belong to the set.
+   */
+  @SuppressWarnings({"PMD.ForLoopCanBeForeach", "ForLoopReplaceableByForEach"})
+  public ReadOnlyPrimitiveLongSet(long... values) {
+    // initial size is the upper limit
+    long[] uniqueValues = new long[values.length];
+    int numUniqueValues = 0;
+    boolean seenZero = false;
+
+    // copy the set of unique values to our array
+    // using indexed for-loops to avoid allocations
+    copyLoop:
+    for (int i = 0; i < values.length; i++) {
+      long value = values[i];
+      if (value == 0 && !seenZero) {
+        // special case to support zero
+        seenZero = true;
+      } else {
+        for (int j = 0; j < uniqueValues.length; j++) {
+          long uniqueValue = uniqueValues[j];
+          if (uniqueValue == value) {
+            continue copyLoop;
+          }
+        }
+      }
+      uniqueValues[numUniqueValues] = value;
+      numUniqueValues++;
+    }
+
+    if (numUniqueValues == values.length) {
+      // all input values were unique, no need to truncate
+      m_values = uniqueValues;
+    } else {
+      // truncate the array to remove trailing empty space
+      m_values = Arrays.copyOf(uniqueValues, numUniqueValues);
+    }
+  }
+
+  /**
+   * Checks if the set contains a particular value.
+   *
+   * @param value the value to check for
+   * @return true if the value is in the set, false if not
+   */
+  public boolean contains(long value) {
+    for (long mValue : m_values) {
+      if (mValue == value) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Retrieves the number of elements in the set.
+   *
+   * @return the number of elements in the set
+   */
+  public int size() {
+    return m_values.length;
+  }
+
+  /**
+   * Checks if the set is empty, i.e. contains no values.
+   *
+   * @return true if there are no values in the set, false otherwise.
+   */
+  public boolean isEmpty() {
+    return size() == 0;
+  }
+
+  /**
+   * Creates a stream of primitive long values for the set.
+   *
+   * @return a sequential Stream over the elements in this collection
+   * @see Set#stream()
+   */
+  public LongStream stream() {
+    return Arrays.stream(m_values);
+  }
+
+  /**
+   * Creates a new array that contains all of the values in the set.
+   *
+   * @return an array containing all the values in the set
+   */
+  public long[] toArray() {
+    return Arrays.copyOf(m_values, m_values.length);
+  }
+
+  @Override
+  public Iterator<Long> iterator() {
+    return new Iterator<>() {
+      @SuppressWarnings("PMD.RedundantFieldInitializer")
+      private int m_index = 0;
+
+      @Override
+      public boolean hasNext() {
+        return m_index < ReadOnlyPrimitiveLongSet.this.size();
+      }
+
+      @Override
+      public Long next() {
+        if (!hasNext()) {
+          throw new NoSuchElementException();
+        }
+
+        return ReadOnlyPrimitiveLongSet.this.m_values[m_index++];
+      }
+    };
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/CurrentTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/CurrentTest.java
new file mode 100644
index 0000000..d383094
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/CurrentTest.java
@@ -0,0 +1,26 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class CurrentTest {
+  @Test
+  void testAmpsTimesVolts() {
+    Power combined = Units.Amps.times(Units.Volts, "Watt", "w");
+
+    assertTrue(combined.equivalent(Units.Watts));
+  }
+
+  @Test
+  void testMilliAmpsTimesMilliVolts() {
+    // results in microwatts
+    assertTrue(
+        Units.Milliamps.times(Units.Millivolts, "Microwatt", "uW")
+            .equivalent(Units.Milli(Units.Milliwatts)));
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/DistanceTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/DistanceTest.java
new file mode 100644
index 0000000..65ad291
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/DistanceTest.java
@@ -0,0 +1,34 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class DistanceTest {
+  @Test
+  void testBaseUnitDistancePerTime() {
+    Velocity<Distance> anonBaseUnit = new Distance(1, "D", "d").per(new Time(1, "T", "t"));
+
+    assertTrue(BaseUnits.Velocity.equivalent(anonBaseUnit));
+  }
+
+  @Test
+  void testFeetPerSecond() {
+    Velocity<Distance> feetPerMillisecond = Units.Feet.per(Units.Milliseconds);
+
+    // one foot per millisecond
+    // = (1 / 3.28084) meters per (1 / 1000) seconds
+    // = (1000 / 3.28084) meters per second
+    double asBaseMeasure = feetPerMillisecond.of(1).in(Units.MetersPerSecond);
+    assertEquals(1000 / 3.28084, asBaseMeasure, 1e-3);
+
+    // one meter per second = 1 mm per millisecond = 0.00328084 feet per millisecond
+    double asContrivedMeasure = Units.MetersPerSecond.of(1).in(feetPerMillisecond);
+    assertEquals(3.28084 / 1000, asContrivedMeasure, 1e-8);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/EncoderTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/EncoderTest.java
new file mode 100644
index 0000000..1a4b568
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/EncoderTest.java
@@ -0,0 +1,92 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static edu.wpi.first.units.Units.Inches;
+import static edu.wpi.first.units.Units.Revolutions;
+import static edu.wpi.first.units.Units.Second;
+import static edu.wpi.first.units.Units.Value;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class EncoderTest {
+  static class Encoder<U extends Unit<U>> {
+    int m_ticks; // = 0
+    private Measure<U> m_distancePerPulse;
+    private MutableMeasure<U> m_distance;
+    private MutableMeasure<Velocity<U>> m_rate;
+
+    void setDistancePerPulse(Measure<U> distancePerPulse) {
+      m_distancePerPulse = distancePerPulse;
+      m_distance = MutableMeasure.zero(distancePerPulse.unit());
+      m_rate = MutableMeasure.zero(distancePerPulse.unit().per(Second));
+    }
+
+    Measure<U> getDistance() {
+      return m_distance;
+    }
+
+    Measure<Velocity<U>> getRate() {
+      return m_rate;
+    }
+
+    void setTicks(int ticks) {
+      // pretend we read from JNI here instead of being passed a specific value
+      var change = ticks - m_ticks;
+      m_ticks = ticks;
+      m_distance.mut_setMagnitude(m_distancePerPulse.magnitude() * ticks);
+
+      // assumes the last update was 1 second ago - fine for tests
+      m_rate.mut_setMagnitude(m_distancePerPulse.magnitude() * change);
+    }
+  }
+
+  @Test
+  void testAsDistance() {
+    var ticksPerRevolution = Value.of(2048);
+
+    var encoder = new Encoder<Distance>();
+
+    // distance per rotation = (wheel circumference / gear ratio)
+    // distance per tick = distance per rotation / ticks per rotation
+    var wheelDiameter = Inches.of(6);
+    var gearRatio = Value.of(10); // 10:1 ratio
+    Measure<Distance> distancePerPulse =
+        wheelDiameter.times(Math.PI).divide(gearRatio).divide(ticksPerRevolution);
+    encoder.setDistancePerPulse(distancePerPulse);
+
+    encoder.m_ticks = 0;
+    assertEquals(0, encoder.getDistance().in(Inches), Measure.EQUIVALENCE_THRESHOLD);
+    assertEquals(0, encoder.getRate().in(Inches.per(Second)), Measure.EQUIVALENCE_THRESHOLD);
+
+    // one full encoder turn, 1/10th of a wheel rotation
+    encoder.setTicks(2048);
+    assertEquals(6 * Math.PI / 10, encoder.getDistance().in(Inches), Measure.EQUIVALENCE_THRESHOLD);
+
+    // one full encoder turn back, 1/10th of a wheel rotation - rate should be negative
+    encoder.setTicks(0);
+    assertEquals(
+        -6 * Math.PI / 10, encoder.getRate().in(Inches.per(Second)), Measure.EQUIVALENCE_THRESHOLD);
+  }
+
+  @Test
+  void testAsRevolutions() {
+    var ticksPerRevolution = Value.of(2048);
+
+    var encoder = new Encoder<Angle>();
+
+    Measure<Angle> distancePerPulse = Revolutions.of(1).divide(ticksPerRevolution);
+    encoder.setDistancePerPulse(distancePerPulse);
+
+    encoder.m_ticks = 0;
+    assertEquals(0, encoder.getDistance().in(Revolutions), Measure.EQUIVALENCE_THRESHOLD);
+    assertEquals(0, encoder.getRate().in(Revolutions.per(Second)), Measure.EQUIVALENCE_THRESHOLD);
+
+    encoder.setTicks(2048); // one full encoder turn, 1/10th of a wheel rotation
+    assertEquals(1, encoder.getDistance().in(Revolutions), Measure.EQUIVALENCE_THRESHOLD);
+    assertEquals(1, encoder.getRate().in(Revolutions.per(Second)), Measure.EQUIVALENCE_THRESHOLD);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/ExampleUnit.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/ExampleUnit.java
new file mode 100644
index 0000000..3e40e3f2
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/ExampleUnit.java
@@ -0,0 +1,19 @@
+// 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.
+
+package edu.wpi.first.units;
+
+class ExampleUnit extends Unit<ExampleUnit> {
+  ExampleUnit(double baseUnitEquivalent) {
+    this(baseUnitEquivalent, "Example", "ex");
+  }
+
+  ExampleUnit(UnaryFunction toBase, UnaryFunction fromBase, String name, String symbol) {
+    super(ExampleUnit.class, toBase, fromBase, name, symbol);
+  }
+
+  ExampleUnit(double baseUnitEquivalent, String name, String symbol) {
+    super(ExampleUnit.class, baseUnitEquivalent, name, symbol);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/MeasureTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/MeasureTest.java
new file mode 100644
index 0000000..6bc867d
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/MeasureTest.java
@@ -0,0 +1,312 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class MeasureTest {
+  @Test
+  void testBasics() {
+    Unit<Distance> unit = Units.Feet;
+    double magnitude = 10;
+    Measure<Distance> m = unit.of(magnitude);
+    assertEquals(unit, m.unit(), "Wrong units");
+    assertEquals(magnitude, m.magnitude(), 0, "Wrong magnitude");
+  }
+
+  @Test
+  void testMultiply() {
+    Measure<Distance> m = Units.Feet.of(1);
+    Measure<Distance> m2 = m.times(10);
+    assertEquals(10, m2.magnitude(), 1e-12);
+    assertNotSame(m2, m); // make sure state wasn't changed
+  }
+
+  @Test
+  void testDivide() {
+    Measure<Distance> m = Units.Meters.of(1);
+    Measure<Distance> m2 = m.divide(10);
+    assertEquals(0.1, m2.magnitude(), 0);
+    assertNotSame(m2, m);
+  }
+
+  @Test
+  void testAdd() {
+    Measure<Distance> m1 = Units.Feet.of(1);
+    Measure<Distance> m2 = Units.Inches.of(2);
+    assertTrue(m1.plus(m2).isEquivalent(Units.Feet.of(1 + 2 / 12d)));
+    assertTrue(m2.plus(m1).isEquivalent(Units.Inches.of(14)));
+  }
+
+  @Test
+  void testSubtract() {
+    Measure<Distance> m1 = Units.Feet.of(1);
+    Measure<Distance> m2 = Units.Inches.of(2);
+    assertTrue(m1.minus(m2).isEquivalent(Units.Feet.of(1 - 2 / 12d)));
+    assertTrue(m2.minus(m1).isEquivalent(Units.Inches.of(-10)));
+  }
+
+  @Test
+  void testNegate() {
+    Measure<Distance> m = Units.Feet.of(123);
+    Measure<Distance> n = m.negate();
+    assertEquals(-m.magnitude(), n.magnitude(), 1e-12);
+    assertEquals(m.unit(), n.unit());
+  }
+
+  @Test
+  void testEquivalency() {
+    Measure<Distance> inches = Units.Inches.of(12);
+    Measure<Distance> feet = Units.Feet.of(1);
+    assertTrue(inches.isEquivalent(feet));
+    assertTrue(feet.isEquivalent(inches));
+  }
+
+  @Test
+  void testAs() {
+    Measure<Distance> m = Units.Inches.of(12);
+    assertEquals(1, m.in(Units.Feet), Measure.EQUIVALENCE_THRESHOLD);
+  }
+
+  @Test
+  void testPerUnitTime() {
+    var measure = Units.Kilograms.of(144);
+    var dt = Units.Milliseconds.of(53);
+
+    // 144 Kg / (53 ms) = (1000 / 53) * 144 Kg/s = (144,000 / 53) Kg/s
+
+    var result = measure.per(dt);
+    assertEquals(144_000.0 / 53, result.baseUnitMagnitude(), 1e-5);
+    assertEquals(Units.Kilograms.per(Units.Milliseconds), result.unit());
+  }
+
+  @Test
+  void testTimesMeasure() {
+    var m1 = Units.Volts.of(1.567);
+    var m2 = Units.Kilograms.of(8.4e-5);
+
+    assertEquals(Units.Volts.mult(Units.Kilograms).of(1.567 * 8.4e-5), m1.times(m2));
+  }
+
+  @Test
+  void testTimesUnitless() {
+    var unit = new ExampleUnit(6);
+    var measure = unit.of(2.5);
+    var multiplier = Units.Percent.of(125); // 125% or 1.25x
+    Measure<?> result = measure.times(multiplier);
+    assertSame(unit, result.unit());
+
+    assertEquals(2.5 * 1.25, result.magnitude());
+    assertEquals(2.5 * 1.25 * 6, result.baseUnitMagnitude());
+  }
+
+  @Test
+  void testTimesPerWithDimensionalAnalysis() {
+    var measureA = Units.Feet.of(62); // 62 feet
+    var measureB = Units.Radians.of(6).per(Units.Inches); // 6 radians per inch
+    Measure<?> aTimesB = measureA.times(measureB); // (62 feet) * (6 rad/inch) = 4464 rad
+    assertEquals(Units.Radians, aTimesB.unit());
+    assertEquals(4464, aTimesB.magnitude(), 1e-12);
+
+    Measure<?> bTimesA = measureB.times(measureA); // should be identical to the above
+    assertTrue(bTimesA.isEquivalent(aTimesB));
+    assertTrue(aTimesB.isEquivalent(bTimesA));
+  }
+
+  @Test
+  void testPerTimesPerWithDimensionalAnalysis() {
+    var measureA = Units.Inches.of(16).per(Units.Volts);
+    var measureB = Units.Millivolts.of(14).per(Units.Meters);
+    Measure<?> aTimesB = measureA.times(measureB);
+    assertEquals(Units.Value, aTimesB.unit());
+    assertEquals((16 * 25.4 / 1000) * (14 / 1000.0), aTimesB.magnitude());
+    assertEquals((16 * 25.4 / 1000) * (14 / 1000.0), aTimesB.baseUnitMagnitude());
+
+    Measure<?> bTimesA = measureB.times(measureA); // should be identical to the above
+    assertTrue(bTimesA.isEquivalent(aTimesB));
+    assertTrue(aTimesB.isEquivalent(bTimesA));
+  }
+
+  @Test
+  void testPerTimesMeasure() {
+    var m1 = Units.Feet.per(Units.Milliseconds).of(19);
+    var m2 = Units.Seconds.of(44);
+
+    // 19 ft/ms = 19,000 ft/s
+    // 19,000 ft/s * 44s = 836,000 ft
+    assertTrue(Units.Feet.of(836_000).isNear(m1.times(m2), 1e-12));
+
+    // 42 ex per foot * 17mm = 42 ex * 17mm / (304.8mm/ft) = 42 * 17 / 304.8 = 2.34252
+    var exampleUnit = new ExampleUnit(1);
+    var m3 = exampleUnit.per(Units.Feet).of(42);
+    var m4 = Units.Millimeters.of(17);
+    assertEquals(exampleUnit.of(42 * 17 / (12 * 25.4)), m3.times(m4));
+  }
+
+  @Test
+  void testToShortString() {
+    var measure = Units.Volts.of(343);
+    assertEquals("3.430e+02 V", measure.toShortString());
+  }
+
+  @Test
+  void testToLongString() {
+    var measure = Units.Volts.of(343);
+    assertEquals("343.0 Volt", measure.toLongString());
+    assertEquals("343.0001 Volt", Units.Volts.of(343.0001).toLongString());
+    assertEquals("1.2345678912345679E8 Volt", Units.Volts.of(123456789.123456789).toLongString());
+  }
+
+  @Test
+  void testOfBaseUnits() {
+    var unit = new ExampleUnit(16);
+    var measure = unit.ofBaseUnits(1);
+    assertEquals(unit, measure.unit());
+    assertEquals(1, measure.baseUnitMagnitude());
+    assertEquals(1 / 16.0, measure.magnitude());
+  }
+
+  @Test
+  void testCompare() {
+    var unit = new ExampleUnit(7);
+    var base = unit.of(1);
+    var less = unit.of(0.5);
+    var more = unit.of(2);
+
+    assertEquals(0, base.compareTo(base));
+    assertEquals(-1, base.compareTo(more));
+    assertEquals(1, base.compareTo(less));
+
+    // lt, lte, gt, gte helper functions
+
+    assertTrue(base.lt(more));
+    assertTrue(base.lte(more));
+    assertFalse(base.gt(more));
+    assertFalse(base.gte(more));
+
+    assertTrue(base.gt(less));
+    assertTrue(base.gte(less));
+    assertFalse(base.lt(less));
+    assertFalse(base.lte(less));
+
+    assertTrue(base.lte(base));
+    assertTrue(base.gte(base));
+    assertFalse(base.lt(base));
+    assertFalse(base.gt(base));
+  }
+
+  @Test
+  void testTimesScalar() {
+    var unit = new ExampleUnit(42);
+    var measure = unit.of(4.2);
+    var scalar = 18;
+    var result = measure.times(scalar);
+    assertNotSame(measure, result);
+    assertSame(unit, result.unit());
+    assertEquals(4.2 * 18, result.magnitude());
+    assertEquals(4.2 * 42 * 18, result.baseUnitMagnitude());
+  }
+
+  @Test
+  void testPerUnit() {
+    var unitA = new ExampleUnit(10);
+    var unitB = new ExampleUnit(12);
+    var measure = unitA.of(1.2);
+    var result = measure.per(unitB);
+    assertEquals(Per.combine(unitA, unitB), result.unit()); // A/B has base equivalent of 10/12
+    assertEquals(1, result.baseUnitMagnitude()); // 10/12 * 12/10 = 1
+    assertEquals(measure.magnitude(), result.magnitude());
+  }
+
+  @Test
+  void testAddMeasureSameUnit() {
+    var unit = new ExampleUnit(8.2);
+    var measureA = unit.of(3.1);
+    var measureB = unit.of(91.6);
+    var result = measureA.plus(measureB);
+    assertEquals(unit, result.unit());
+    assertEquals(94.7, result.magnitude(), 1e-12);
+  }
+
+  @Test
+  void testAddMeasuresDifferentUnits() {
+    var unitA = new ExampleUnit(8.2);
+    var unitB = new ExampleUnit(7.3);
+    var measureA = unitA.of(5);
+    var measureB = unitB.of(16);
+    var aPlusB = measureA.plus(measureB);
+
+    assertEquals(unitA, aPlusB.unit());
+    assertEquals(8.2 * 5 + 7.3 * 16, aPlusB.baseUnitMagnitude(), 1e-12);
+    assertEquals(5 + (16 * 7.3 / 8.2), aPlusB.magnitude(), 1e-12);
+
+    var bPlusA = measureB.plus(measureA);
+    assertEquals(unitB, bPlusA.unit());
+    assertEquals(8.2 * 5 + 7.3 * 16, bPlusA.baseUnitMagnitude(), 1e-12);
+    assertEquals(16 + (5 * 8.2 / 7.3), bPlusA.magnitude(), 1e-12);
+  }
+
+  @Test
+  void testMinNoArgs() {
+    var min = Measure.min();
+    assertNull(min);
+  }
+
+  @Test
+  void testMin() {
+    var unit = new ExampleUnit(56.1);
+    var one = unit.of(1);
+    var two = unit.of(2);
+    var zero = unit.of(0);
+    var veryNegative = unit.of(-12839712);
+
+    var min = Measure.min(one, two, zero, veryNegative);
+    assertSame(veryNegative, min);
+  }
+
+  @Test
+  void testMaxNoArgs() {
+    var min = Measure.max();
+    assertNull(min);
+  }
+
+  @Test
+  void testMax() {
+    var unit = new ExampleUnit(6.551);
+    var one = unit.of(1);
+    var two = unit.of(2);
+    var zero = unit.of(0);
+    var veryLarge = unit.of(8217234);
+
+    var max = Measure.max(one, two, zero, veryLarge);
+    assertSame(veryLarge, max);
+  }
+
+  @Test
+  void testIsNear() {
+    var unit = new ExampleUnit(92);
+    var measureA = unit.of(1.21);
+    var measureB = unit.ofBaseUnits(64);
+    // A = 1.21 * 92 base units, or 111.32
+    // B = 64 base units
+    // ratio = 111.32 / 64 = 1.739375 = 173.9375%
+
+    assertTrue(measureA.isNear(measureA, 0));
+    assertTrue(measureB.isNear(measureB, 0));
+
+    assertFalse(measureA.isNear(measureB, 0));
+    assertFalse(measureA.isNear(measureB, 0.50));
+    assertFalse(measureA.isNear(measureB, 0.739370));
+    assertTrue(measureA.isNear(measureB, 0.739375));
+    assertTrue(measureA.isNear(measureB, 100)); // some stupidly large range +/- 10000%
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/MultTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/MultTest.java
new file mode 100644
index 0000000..383ea16
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/MultTest.java
@@ -0,0 +1,37 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import org.junit.jupiter.api.Test;
+
+class MultTest {
+  @Test
+  void testAutomaticNames() {
+    var unitA = new ExampleUnit(1, "Ay", "a");
+    var unitB = new ExampleUnit(1, "Bee", "b");
+    var mult = Mult.combine(unitA, unitB);
+    assertEquals("Ay-Bee", mult.name());
+    assertEquals("a*b", mult.symbol());
+  }
+
+  @Test
+  void testCombine() {
+    var unitA = new ExampleUnit(100);
+    var unitB = new ExampleUnit(0.912);
+    var mult = Mult.combine(unitA, unitB);
+    assertEquals(91.2, mult.toBaseUnits(1));
+  }
+
+  @Test
+  void testCaches() {
+    var unitA = new ExampleUnit(1);
+    var unitB = new ExampleUnit(2);
+    var mult = Mult.combine(unitA, unitB);
+    assertSame(mult, Mult.combine(unitA, unitB));
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/MutableMeasureTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/MutableMeasureTest.java
new file mode 100644
index 0000000..5008884
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/MutableMeasureTest.java
@@ -0,0 +1,133 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static edu.wpi.first.units.Units.Centimeters;
+import static edu.wpi.first.units.Units.FeetPerSecond;
+import static edu.wpi.first.units.Units.InchesPerSecond;
+import static edu.wpi.first.units.Units.Meters;
+import static edu.wpi.first.units.Units.Millisecond;
+import static edu.wpi.first.units.Units.Millivolts;
+import static edu.wpi.first.units.Units.Second;
+import static edu.wpi.first.units.Units.Volts;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import org.junit.jupiter.api.Test;
+
+class MutableMeasureTest {
+  @Test
+  void testWrapper() {
+    var unit = new ExampleUnit(1);
+    var measure = unit.of(1234);
+    var mutable = MutableMeasure.mutable(measure);
+    assertEquals(measure.magnitude(), mutable.magnitude(), 0);
+    assertEquals(measure.unit(), mutable.unit());
+  }
+
+  @Test
+  void testSetMagnitude() {
+    var measure = MutableMeasure.ofRelativeUnits(0, FeetPerSecond);
+    double newMagnitude = 48.123;
+    measure.mut_setMagnitude(newMagnitude);
+    assertEquals(newMagnitude, measure.magnitude(), 0); // should be an exact match
+    double feetToMeters = 0.3048;
+    assertEquals(newMagnitude * feetToMeters, measure.baseUnitMagnitude(), 1e-12);
+  }
+
+  @Test
+  void testMut_times() {
+    var measure = MutableMeasure.ofRelativeUnits(18, FeetPerSecond);
+    double scalar = Math.PI;
+    var result = measure.mut_times(scalar);
+    assertSame(measure, result, "mut_times should return the same object");
+    assertEquals(18 * Math.PI, measure.magnitude(), 1e-12);
+  }
+
+  @Test
+  void testMut_divide() {
+    var measure = MutableMeasure.ofRelativeUnits(18, FeetPerSecond);
+    double scalar = Math.PI;
+    var result = measure.mut_divide(scalar);
+    assertSame(measure, result, "mut_times should return the same object");
+    assertEquals(18 / Math.PI, measure.magnitude(), 1e-12);
+  }
+
+  @Test
+  void testReplaceMeasure() {
+    var measure = MutableMeasure.ofRelativeUnits(17.6, FeetPerSecond);
+    var replacer = Meters.per(Millisecond).of(94.872);
+    var result = measure.mut_replace(replacer);
+    assertSame(measure, result, "Replacing should return the mutable measure");
+    assertSame(replacer.unit(), measure.unit());
+    assertEquals(replacer.magnitude(), measure.magnitude(), 0);
+  }
+
+  @Test
+  void testReplaceRaw() {
+    var measure = MutableMeasure.ofRelativeUnits(-542, FeetPerSecond);
+    var result = measure.mut_replace(62, Meters.per(Millisecond));
+    assertSame(measure, result, "Replacing should return the mutable measure");
+    assertSame(Meters.per(Millisecond), measure.unit());
+    assertEquals(62, measure.magnitude(), 0);
+  }
+
+  @Test
+  void testAccMeasure() {
+    var measure = MutableMeasure.ofRelativeUnits(8.5431, FeetPerSecond);
+    var acc = Meters.per(Millisecond).of(-23.62);
+    var result = measure.mut_acc(acc);
+    assertSame(measure, result, "Acc should return the mutable measure");
+    assertSame(FeetPerSecond, measure.unit(), "Unit shouldn't change");
+    assertEquals(8.5431 - (23.62 * (1 / 0.3048) * 1000), measure.magnitude(), 1e-10);
+  }
+
+  @Test
+  void testAccRaw() {
+    var measure = MutableMeasure.ofRelativeUnits(99.999999, FeetPerSecond);
+    var result = measure.mut_acc(22.981);
+    assertSame(measure, result);
+    assertSame(FeetPerSecond, measure.unit());
+    assertEquals(122.980999, measure.magnitude(), 0);
+  }
+
+  @Test
+  void testMutPlusMeasure() {
+    var measure = MutableMeasure.ofRelativeUnits(400, InchesPerSecond);
+    var other = Centimeters.per(Second).of(41.312);
+    var result = measure.mut_plus(other);
+    assertSame(measure, result);
+    assertSame(InchesPerSecond, result.unit());
+    assertEquals(416.2645669291339, measure.magnitude(), 1e-12);
+  }
+
+  @Test
+  void testMutPlusRaw() {
+    var measure = MutableMeasure.ofRelativeUnits(31.51, Volts);
+    var result = measure.mut_plus(66.641, Millivolts);
+    assertSame(measure, result);
+    assertSame(Volts, result.unit());
+    assertEquals(31.576641, result.magnitude(), 1e-10);
+  }
+
+  @Test
+  void testMutMinusMeasure() {
+    var measure = MutableMeasure.ofRelativeUnits(400, InchesPerSecond);
+    var other = Centimeters.per(Second).of(41.312);
+    var result = measure.mut_minus(other);
+    assertSame(measure, result);
+    assertSame(InchesPerSecond, result.unit());
+    assertEquals(383.7354330708662, measure.magnitude(), 1e-12);
+  }
+
+  @Test
+  void testMutMinusRaw() {
+    var measure = MutableMeasure.ofRelativeUnits(31.51, Volts);
+    var result = measure.mut_minus(66.641, Millivolts);
+    assertSame(measure, result);
+    assertSame(Volts, result.unit());
+    assertEquals(31.443359, result.magnitude(), 1e-10);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/UnaryFunctionTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/UnaryFunctionTest.java
new file mode 100644
index 0000000..e711fa6
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/UnaryFunctionTest.java
@@ -0,0 +1,102 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class UnaryFunctionTest {
+  @Test
+  void testApply() {
+    UnaryFunction f = x -> x * x;
+    assertEquals(1, f.apply(1));
+    assertEquals(4, f.apply(2));
+    assertEquals(2, f.apply(1.414), 1e-3);
+    assertEquals(64, f.apply(-8));
+  }
+
+  @Test
+  void testPipeTo() {
+    UnaryFunction f = x -> x * x;
+    UnaryFunction g = x -> x + 1;
+    var h = f.pipeTo(g); // h(x) = g(f(x)) = x^2 + 1
+    assertEquals(2, h.apply(1));
+    assertEquals(65, h.apply(8));
+  }
+
+  @Test
+  void testMult() {
+    UnaryFunction f = x -> x * x;
+    UnaryFunction g = x -> x + 1;
+    var h = f.mult(g); // h(x) = f(x) * g(x) = (x^2)(x + 1) = x^3 + x^2
+    assertEquals(2, h.apply(1));
+    assertEquals(216 + 36, h.apply(6));
+    assertEquals(12, h.apply(2));
+  }
+
+  @Test
+  void testMultScalar() {
+    UnaryFunction f = x -> x * x;
+    var scalar = 2.5;
+    var h = f.mult(scalar); // h(x) = 2.5 x^2
+    assertEquals(250, h.apply(10));
+    assertEquals(2.5, h.apply(1));
+    assertEquals(2.5, h.apply(-1));
+    assertEquals(10, h.apply(2));
+  }
+
+  @Test
+  void testDivZeroDenominator() {
+    UnaryFunction f = x -> x;
+    UnaryFunction g = x -> 0;
+    var h = f.div(g);
+    assertEquals(0, h.apply(0));
+    assertEquals(Double.POSITIVE_INFINITY, h.apply(1));
+    assertEquals(Double.NEGATIVE_INFINITY, h.apply(-1));
+  }
+
+  @Test
+  void testDivNonzeroDenominator() {
+    UnaryFunction f = x -> 2 * x;
+    UnaryFunction g = x -> x + 1;
+    var h = f.div(g);
+    assertEquals(0, h.apply(0));
+    assertEquals(4, h.apply(-2));
+    assertEquals(16.0 / 9, h.apply(8));
+  }
+
+  @Test
+  void testDivScalar() {
+    UnaryFunction f = x -> x * x;
+    var scalar = 2.5;
+    var h = f.div(scalar); // h(x) = x^2 / 2.5
+    assertEquals(10, h.apply(5));
+  }
+
+  @Test
+  void testExp() {
+    UnaryFunction f = x -> x * x;
+    UnaryFunction g = x -> x + 2;
+    var h = f.exp(g); // h(x) = f(x) ^ g(x) = (x^2)^(x + 2)
+    assertEquals(1, h.apply(1));
+    assertEquals(0, h.apply(0));
+    assertEquals(1, h.apply(-2));
+    assertEquals(256, h.apply(2)); // (2^2)^(2 + 2) = 4^4
+    assertEquals(59049, h.apply(3)); // (3^2)^(3 + 2) = 9^5
+  }
+
+  @Test
+  void testExpScalar() {
+    UnaryFunction f = x -> x * x;
+    var scalar = 4;
+    var h = f.exp(scalar); // h(x) = f(x)^4 = x^8
+    assertEquals(0, h.apply(0));
+    assertEquals(1, h.apply(1));
+    assertEquals(1, h.apply(-1));
+    assertEquals(256, h.apply(2));
+    assertEquals(6561, h.apply(3));
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/UnitTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/UnitTest.java
new file mode 100644
index 0000000..46453dd
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/UnitTest.java
@@ -0,0 +1,19 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class UnitTest { // :)
+  @Test
+  void testOf() {
+    ExampleUnit u = new ExampleUnit(1);
+    Measure<ExampleUnit> fiveOfSomething = u.of(5);
+    assertEquals(5, fiveOfSomething.magnitude(), 0);
+    assertEquals(u, fiveOfSomething.unit());
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/UnitsTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/UnitsTest.java
new file mode 100644
index 0000000..4659111
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/UnitsTest.java
@@ -0,0 +1,348 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static edu.wpi.first.units.Units.Amps;
+import static edu.wpi.first.units.Units.Celsius;
+import static edu.wpi.first.units.Units.Centimeters;
+import static edu.wpi.first.units.Units.Degrees;
+import static edu.wpi.first.units.Units.Fahrenheit;
+import static edu.wpi.first.units.Units.Feet;
+import static edu.wpi.first.units.Units.FeetPerSecond;
+import static edu.wpi.first.units.Units.Grams;
+import static edu.wpi.first.units.Units.Gs;
+import static edu.wpi.first.units.Units.Horsepower;
+import static edu.wpi.first.units.Units.Inches;
+import static edu.wpi.first.units.Units.Kelvin;
+import static edu.wpi.first.units.Units.Kilo;
+import static edu.wpi.first.units.Units.Kilograms;
+import static edu.wpi.first.units.Units.Meters;
+import static edu.wpi.first.units.Units.MetersPerSecond;
+import static edu.wpi.first.units.Units.MetersPerSecondPerSecond;
+import static edu.wpi.first.units.Units.Microseconds;
+import static edu.wpi.first.units.Units.Milli;
+import static edu.wpi.first.units.Units.Milliamps;
+import static edu.wpi.first.units.Units.Millimeters;
+import static edu.wpi.first.units.Units.Milliseconds;
+import static edu.wpi.first.units.Units.Millivolts;
+import static edu.wpi.first.units.Units.Milliwatts;
+import static edu.wpi.first.units.Units.Minutes;
+import static edu.wpi.first.units.Units.Ounces;
+import static edu.wpi.first.units.Units.Percent;
+import static edu.wpi.first.units.Units.Pounds;
+import static edu.wpi.first.units.Units.Radians;
+import static edu.wpi.first.units.Units.Revolutions;
+import static edu.wpi.first.units.Units.Second;
+import static edu.wpi.first.units.Units.Seconds;
+import static edu.wpi.first.units.Units.Value;
+import static edu.wpi.first.units.Units.Volts;
+import static edu.wpi.first.units.Units.Watts;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class UnitsTest {
+  // Be accurate to 0.01%
+  private static final double thresh = 1e-5;
+
+  void testBaseUnit(Unit<?> baseUnit) {
+    assertEquals(0, baseUnit.of(0).baseUnitMagnitude(), 0);
+    assertEquals(1, baseUnit.of(1).baseUnitMagnitude(), 0);
+    assertEquals(-1, baseUnit.of(-1).baseUnitMagnitude(), 0);
+    assertEquals(100, baseUnit.of(100).baseUnitMagnitude(), 0);
+    assertEquals(8.281723, baseUnit.of(8.281723).baseUnitMagnitude(), 0);
+    assertEquals(Float.MAX_VALUE, baseUnit.of(Float.MAX_VALUE).baseUnitMagnitude(), 0);
+    assertEquals(Float.MIN_VALUE, baseUnit.of(Float.MIN_VALUE).baseUnitMagnitude(), 0);
+  }
+
+  // Distances
+
+  @Test
+  void testMeters() {
+    testBaseUnit(Meters);
+    assertEquals("Meter", Meters.name());
+    assertEquals("m", Meters.symbol());
+  }
+
+  @Test
+  void testMillimeters() {
+    assertEquals(1000, Millimeters.convertFrom(1, Meters), thresh);
+    assertEquals(1, Meters.convertFrom(1000, Millimeters), thresh);
+    assertEquals("Millimeter", Millimeters.name());
+    assertEquals("mm", Millimeters.symbol());
+  }
+
+  @Test
+  void testCentimeters() {
+    assertEquals(100, Centimeters.convertFrom(1, Meters), thresh);
+    assertEquals(1, Meters.convertFrom(100, Centimeters), thresh);
+    assertEquals("Centimeter", Centimeters.name());
+    assertEquals("cm", Centimeters.symbol());
+  }
+
+  @Test
+  void testInches() {
+    assertEquals(1, Meters.convertFrom(39.3701, Inches), thresh);
+    assertEquals(39.3701, Inches.convertFrom(1, Meters), 1e-4);
+    assertEquals(1 / 25.4, Inches.convertFrom(1, Millimeters), 0); // should be exact
+    assertEquals(12, Inches.convertFrom(1, Feet), thresh);
+    assertEquals("Inch", Inches.name());
+    assertEquals("in", Inches.symbol());
+  }
+
+  @Test
+  void testFeet() {
+    assertEquals(3.28084, Feet.convertFrom(1, Meters), thresh);
+    assertEquals(1 / 12.0, Feet.convertFrom(1, Inches), thresh);
+    assertEquals("Foot", Feet.name());
+    assertEquals("ft", Feet.symbol());
+  }
+
+  // Velocities
+
+  @Test
+  void testMetersPerSecond() {
+    testBaseUnit(MetersPerSecond);
+    assertEquals("Meter per Second", MetersPerSecond.name());
+    assertEquals("m/s", MetersPerSecond.symbol());
+  }
+
+  @Test
+  void testFeetPerSecond() {
+    assertEquals(3.28084, FeetPerSecond.convertFrom(1, MetersPerSecond), thresh);
+    assertEquals(1 / 3.28084, MetersPerSecond.convertFrom(1, FeetPerSecond), thresh);
+    assertEquals("Foot per Second", FeetPerSecond.name());
+    assertEquals("ft/s", FeetPerSecond.symbol());
+  }
+
+  // Accelerations
+
+  @Test
+  void testMetersPerSecondPerSecond() {
+    testBaseUnit(MetersPerSecondPerSecond);
+    assertEquals("Meter per Second per Second", MetersPerSecondPerSecond.name());
+    assertEquals("m/s/s", MetersPerSecondPerSecond.symbol());
+    assertEquals(MetersPerSecond, MetersPerSecondPerSecond.getUnit());
+    assertEquals(Seconds, MetersPerSecondPerSecond.getPeriod());
+  }
+
+  @Test
+  void testGs() {
+    assertEquals(32.17405, Gs.of(1).in(FeetPerSecond.per(Second)), thresh);
+    assertEquals(9.80665, Gs.of(1).in(MetersPerSecondPerSecond), thresh);
+    assertEquals("G", Gs.name());
+    assertEquals("G", Gs.symbol());
+    assertEquals(Units.AnonymousBaseUnit, Gs.getUnit());
+    assertEquals(Seconds, Gs.getPeriod());
+  }
+
+  // Time
+
+  @Test
+  void testSeconds() {
+    testBaseUnit(Seconds);
+    assertEquals("Second", Seconds.name());
+    assertEquals("s", Seconds.symbol());
+  }
+
+  @Test
+  void testMillisecond() {
+    assertEquals(1000, Milliseconds.convertFrom(1, Seconds), thresh);
+    assertEquals("Millisecond", Milliseconds.name());
+    assertEquals("ms", Milliseconds.symbol());
+  }
+
+  @Test
+  void testMicrosecond() {
+    assertEquals(1e6, Microseconds.convertFrom(1, Second), 0);
+    assertEquals("Microsecond", Microseconds.name());
+    assertEquals("us", Microseconds.symbol());
+  }
+
+  @Test
+  void testMinutes() {
+    assertEquals(60, Seconds.convertFrom(1, Minutes), thresh);
+    assertEquals("Minute", Minutes.name());
+    assertEquals("min", Minutes.symbol());
+  }
+
+  // Mass
+
+  @Test
+  void testKilograms() {
+    testBaseUnit(Kilograms);
+    assertEquals("Kilogram", Kilograms.name());
+    assertEquals("Kg", Kilograms.symbol());
+  }
+
+  @Test
+  void testGrams() {
+    assertEquals(1000, Grams.convertFrom(1, Kilograms), thresh);
+    assertEquals("Gram", Grams.name());
+    assertEquals("g", Grams.symbol());
+  }
+
+  @Test
+  void testPounds() {
+    assertEquals(453.592, Grams.convertFrom(1, Pounds), thresh);
+    assertEquals("Pound", Pounds.name());
+    assertEquals("lb.", Pounds.symbol());
+  }
+
+  @Test
+  void testOunces() {
+    assertEquals(16, Ounces.convertFrom(1, Pounds), thresh);
+    assertEquals("Ounce", Ounces.name());
+    assertEquals("oz.", Ounces.symbol());
+  }
+
+  // Angle
+
+  @Test
+  void testRevolutions() {
+    testBaseUnit(Revolutions);
+    assertEquals("Revolution", Revolutions.name());
+    assertEquals("R", Revolutions.symbol());
+  }
+
+  @Test
+  void testRadians() {
+    assertEquals(2 * Math.PI, Radians.convertFrom(1, Revolutions), thresh);
+    assertEquals(2 * Math.PI, Radians.convertFrom(360, Degrees), thresh);
+    assertEquals("Radian", Radians.name());
+    assertEquals("rad", Radians.symbol());
+  }
+
+  @Test
+  void testDegrees() {
+    assertEquals(360, Degrees.convertFrom(1, Revolutions), thresh);
+    assertEquals(360, Degrees.convertFrom(2 * Math.PI, Radians), thresh);
+    assertEquals("Degree", Degrees.name());
+    assertEquals("°", Degrees.symbol());
+  }
+
+  // Unitless
+
+  @Test
+  void testValue() {
+    testBaseUnit(Value);
+    assertEquals("<?>", Value.name());
+    assertEquals("<?>", Value.symbol());
+  }
+
+  @Test
+  void testPercent() {
+    assertEquals(100, Percent.convertFrom(1, Value), thresh);
+    assertEquals("Percent", Percent.name());
+    assertEquals("%", Percent.symbol());
+  }
+
+  // Electric potential
+
+  @Test
+  void testVolts() {
+    testBaseUnit(Volts);
+    assertEquals("Volt", Volts.name());
+    assertEquals("V", Volts.symbol());
+  }
+
+  @Test
+  void testMillivolts() {
+    assertEquals(1000, Millivolts.convertFrom(1, Volts), thresh);
+    assertEquals("Millivolt", Millivolts.name());
+    assertEquals("mV", Millivolts.symbol());
+  }
+
+  // Electric current
+
+  @Test
+  void testAmps() {
+    testBaseUnit(Amps);
+    assertEquals("Amp", Amps.name());
+    assertEquals("A", Amps.symbol());
+  }
+
+  @Test
+  void testMilliamps() {
+    assertEquals(1000, Milliamps.convertFrom(1, Amps), thresh);
+    assertEquals("Milliamp", Milliamps.name());
+    assertEquals("mA", Milliamps.symbol());
+  }
+
+  // Power
+
+  @Test
+  void testWatts() {
+    testBaseUnit(Watts);
+    assertEquals("Watt", Watts.name());
+    assertEquals("W", Watts.symbol());
+  }
+
+  @Test
+  void testMilliwatts() {
+    assertEquals(1000, Milliwatts.convertFrom(1, Watts), thresh);
+    assertEquals("Milliwatt", Milliwatts.name());
+    assertEquals("mW", Milliwatts.symbol());
+  }
+
+  @Test
+  void testHorsepower() {
+    assertEquals(745.7, Watts.convertFrom(1, Horsepower), thresh);
+    assertEquals("Horsepower", Horsepower.name());
+    assertEquals("HP", Horsepower.symbol());
+  }
+
+  // Temperature
+
+  @Test
+  void testKelvin() {
+    testBaseUnit(Kelvin);
+    assertEquals("Kelvin", Kelvin.name());
+    assertEquals("K", Kelvin.symbol()); // note: there's no degree symbol for Kelvin!
+  }
+
+  @Test
+  void testCelsius() {
+    assertEquals(0, Celsius.of(-273.15).in(Kelvin), thresh);
+    assertEquals(273.15, Celsius.of(0).in(Kelvin), thresh);
+    assertEquals(0, Kelvin.of(273.15).in(Celsius), thresh);
+    assertTrue(Celsius.of(0).isEquivalent(Kelvin.of(273.15)));
+    assertTrue(Celsius.of(-273.15).isEquivalent(Kelvin.of(0)));
+    assertEquals("Celsius", Celsius.name());
+    assertEquals("°C", Celsius.symbol());
+  }
+
+  @Test
+  void testFahrenheit() {
+    assertEquals(0, Fahrenheit.of(32).in(Celsius), thresh);
+    assertEquals(100, Fahrenheit.of(212).in(Celsius), thresh);
+    assertEquals(-459.67, Kelvin.of(0).in(Fahrenheit), thresh);
+    assertEquals(273.15, Fahrenheit.of(32).in(Kelvin), thresh);
+    assertEquals(32, Kelvin.of(273.15).in(Fahrenheit), thresh);
+    assertTrue(Fahrenheit.of(32).isEquivalent(Celsius.of(0)));
+    assertTrue(Fahrenheit.of(212).isEquivalent(Celsius.of(100)));
+    assertEquals("Fahrenheit", Fahrenheit.name());
+    assertEquals("°F", Fahrenheit.symbol());
+  }
+
+  // Helpers
+
+  @Test
+  void testKilo() {
+    ExampleUnit unit = new ExampleUnit(1);
+    ExampleUnit kiloUnit = Kilo(unit);
+    assertEquals(1e3, unit.convertFrom(1, kiloUnit), 0);
+    assertEquals(1e-3, kiloUnit.convertFrom(1, unit), 0);
+  }
+
+  @Test
+  void testMilli() {
+    ExampleUnit unit = new ExampleUnit(1);
+    ExampleUnit milliUnit = Milli(unit);
+    assertEquals(1e-3, unit.convertFrom(1, milliUnit), 0);
+    assertEquals(1e3, milliUnit.convertFrom(1, unit), 0);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/VelocityTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/VelocityTest.java
new file mode 100644
index 0000000..5a378da
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/VelocityTest.java
@@ -0,0 +1,58 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static edu.wpi.first.units.Units.Feet;
+import static edu.wpi.first.units.Units.FeetPerSecond;
+import static edu.wpi.first.units.Units.Meters;
+import static edu.wpi.first.units.Units.MetersPerSecond;
+import static edu.wpi.first.units.Units.MetersPerSecondPerSecond;
+import static edu.wpi.first.units.Units.Millisecond;
+import static edu.wpi.first.units.Units.Second;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class VelocityTest {
+  @Test
+  void testBaseUnit() {
+    assertTrue(MetersPerSecond.equivalent(BaseUnits.Velocity));
+    assertTrue(Meters.per(Second).equivalent(BaseUnits.Velocity));
+  }
+
+  @Test
+  void testToAcceleration() {
+    Velocity<Velocity<Distance>> metersPerSecondPerMillisecond = MetersPerSecond.per(Millisecond);
+
+    assertEquals(1000, metersPerSecondPerMillisecond.of(1).in(MetersPerSecondPerSecond), 0);
+    assertEquals(0, metersPerSecondPerMillisecond.of(0).in(MetersPerSecondPerSecond), 0);
+  }
+
+  @Test
+  void testCache() {
+    assertSame(
+        FeetPerSecond, Feet.per(Second), "Feet.per(Second) should return a cached object instance");
+
+    // completely arbitrary units chosen because they won't have already been cached
+    var someDistance = new ExampleUnit(5);
+    var someTime = new ExampleUnit(600);
+    var firstInvocation = someDistance.per(someTime);
+    var secondInvocation = someDistance.per(someTime);
+    assertSame(
+        firstInvocation,
+        secondInvocation,
+        firstInvocation + " was not the same object as " + secondInvocation);
+  }
+
+  @Test
+  void testMult() {
+    var baseUnit = new ExampleUnit(92);
+    var vel = baseUnit.per(Millisecond);
+    var mult = vel.mult(Second);
+    assertEquals(92_000, mult.toBaseUnits(1), 1e-5);
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/VoltageTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/VoltageTest.java
new file mode 100644
index 0000000..d3d1ef3
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/VoltageTest.java
@@ -0,0 +1,23 @@
+// 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.
+
+package edu.wpi.first.units;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class VoltageTest {
+  @Test
+  void testVoltsTimesAmps() {
+    assertTrue(Units.Volts.times(Units.Amps, "", "").equivalent(Units.Watts));
+  }
+
+  @Test
+  void testMilliVoltsTimesMilliAmps() {
+    // results in microwatts
+    assertTrue(
+        Units.Millivolts.times(Units.Milliamps, "", "").equivalent(Units.Milli(Units.Milliwatts)));
+  }
+}
diff --git a/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/collections/LongToObjectHashMapTest.java b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/collections/LongToObjectHashMapTest.java
new file mode 100644
index 0000000..6194691
--- /dev/null
+++ b/third_party/allwpilib/wpiunits/src/test/java/edu/wpi/first/units/collections/LongToObjectHashMapTest.java
@@ -0,0 +1,96 @@
+// 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.
+
+package edu.wpi.first.units.collections;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class LongToObjectHashMapTest {
+  @Test
+  void put() {
+    var map = new LongToObjectHashMap<String>();
+    assertEquals(0, map.size());
+
+    long key = 42;
+    var previousValue = map.put(key, "The Meaning of Life");
+    assertNull(previousValue);
+    assertEquals(1, map.size());
+    assertEquals("The Meaning of Life", map.get(key));
+    assertTrue(map.containsKey(key));
+
+    previousValue = map.put(key, "Addendum: Live Long and Prosper");
+    assertEquals("The Meaning of Life", previousValue);
+    assertEquals(1, map.size(), "The map should not have grown when changing the value for a key");
+    assertTrue(map.containsKey(key));
+
+    var removed = map.remove(key);
+    assertEquals("Addendum: Live Long and Prosper", removed);
+    assertEquals(0, map.size());
+    assertFalse(map.containsKey(key));
+  }
+
+  @Test
+  void testGrowth() {
+    var map = new LongToObjectHashMap<String>();
+    int numInserts = 8000;
+    for (int i = 0; i < numInserts; i++) {
+      map.put(i, "value-" + i);
+    }
+    assertEquals(numInserts, map.size());
+    assertEquals(16384, map.capacity());
+
+    for (int i = 0; i < numInserts; i++) {
+      assertEquals("value-" + i, map.get(i), "key " + i + " had an unexpected value");
+      assertTrue(map.containsKey(i));
+    }
+
+    for (int i = 0; i < numInserts; i++) {
+      map.remove(i);
+    }
+
+    // all items should have been removed
+    assertEquals(0, map.size());
+
+    // ... but the map shouldn't resize
+    assertEquals(16384, map.capacity());
+  }
+
+  @Test
+  void testClear() {
+    var map = new LongToObjectHashMap<>();
+    for (int i = 0; i < 10_000; i++) {
+      map.put(i, i * i);
+    }
+    assertEquals(10_000, map.size());
+    assertEquals(16384, map.capacity());
+
+    map.clear();
+    assertEquals(0, map.size());
+    assertEquals(16384, map.capacity());
+
+    map.clear(); // should be a NOP
+    assertEquals(0, map.size());
+    assertEquals(16384, map.capacity());
+  }
+
+  @Test
+  void testKeySet() {
+    var map = new LongToObjectHashMap<String>();
+    int numInserts = 8000;
+    for (int i = 0; i < numInserts; i++) {
+      map.put(i, "value-" + i);
+    }
+
+    var keySet = map.keySet();
+    assertEquals(map.size(), keySet.size());
+    for (int i = 0; i < numInserts; i++) {
+      assertTrue(keySet.contains(i), "keySet does not contain key " + i);
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/.styleguide b/third_party/allwpilib/wpiutil/.styleguide
index 9c1196e..4932e16 100644
--- a/third_party/allwpilib/wpiutil/.styleguide
+++ b/third_party/allwpilib/wpiutil/.styleguide
@@ -39,6 +39,13 @@
   wpiutil
 }
 
+includeOtherLibs {
+  ^fmt/
+  ^google/
+  ^gmock/
+  ^gtest/
+}
+
 includeGuardRoots {
   wpiutil/src/main/native/cpp/
   wpiutil/src/main/native/include/
diff --git a/third_party/allwpilib/wpiutil/BUILD b/third_party/allwpilib/wpiutil/BUILD
index d85ccbc..048e9b3 100644
--- a/third_party/allwpilib/wpiutil/BUILD
+++ b/third_party/allwpilib/wpiutil/BUILD
@@ -32,4 +32,5 @@
     ],
     target_compatible_with = ["//tools/platforms/hardware:roborio"],
     visibility = ["//visibility:public"],
+    deps = ["@allwpilib_ni_libraries//:ni-libraries"],
 )
diff --git a/third_party/allwpilib/wpiutil/CMakeLists.txt b/third_party/allwpilib/wpiutil/CMakeLists.txt
index 9b86e14..a319293 100644
--- a/third_party/allwpilib/wpiutil/CMakeLists.txt
+++ b/third_party/allwpilib/wpiutil/CMakeLists.txt
@@ -4,6 +4,7 @@
 include(GenResources)
 include(CompileWarnings)
 include(AddTest)
+include(DownloadAndCheck)
 
 file(GLOB wpiutil_jni_src src/main/native/cpp/jni/WPIUtilJNI.cpp src/main/native/cpp/jni/DataLogJNI.cpp)
 
@@ -14,18 +15,18 @@
   include(UseJava)
   set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
 
-  if(NOT EXISTS "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson/jackson-core-2.12.4.jar")
+  if(NOT EXISTS "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson/jackson-core-2.15.2.jar")
         set(BASE_URL "https://search.maven.org/remotecontent?filepath=")
         set(JAR_ROOT "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson")
 
         message(STATUS "Downloading Jackson jarfiles...")
 
-        file(DOWNLOAD "${BASE_URL}com/fasterxml/jackson/core/jackson-core/2.12.4/jackson-core-2.12.4.jar"
-            "${JAR_ROOT}/jackson-core-2.12.4.jar")
-        file(DOWNLOAD "${BASE_URL}com/fasterxml/jackson/core/jackson-databind/2.12.4/jackson-databind-2.12.4.jar"
-            "${JAR_ROOT}/jackson-databind-2.12.4.jar")
-        file(DOWNLOAD "${BASE_URL}com/fasterxml/jackson/core/jackson-annotations/2.12.4/jackson-annotations-2.12.4.jar"
-            "${JAR_ROOT}/jackson-annotations-2.12.4.jar")
+        download_and_check("${BASE_URL}com/fasterxml/jackson/core/jackson-core/2.15.2/jackson-core-2.15.2.jar"
+            "${JAR_ROOT}/jackson-core-2.15.2.jar")
+        download_and_check("${BASE_URL}com/fasterxml/jackson/core/jackson-databind/2.15.2/jackson-databind-2.15.2.jar"
+            "${JAR_ROOT}/jackson-databind-2.15.2.jar")
+        download_and_check("${BASE_URL}com/fasterxml/jackson/core/jackson-annotations/2.15.2/jackson-annotations-2.15.2.jar"
+            "${JAR_ROOT}/jackson-annotations-2.15.2.jar")
 
         message(STATUS "All files downloaded.")
     endif()
@@ -33,18 +34,27 @@
   file(GLOB JACKSON_JARS
         ${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson/*.jar)
 
-  set(CMAKE_JAVA_INCLUDE_PATH wpiutil.jar ${JACKSON_JARS})
+  if(NOT EXISTS "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf/quickbuf-runtime-1.3.2.jar")
+        set(BASE_URL "https://search.maven.org/remotecontent?filepath=")
+        set(JAR_ROOT "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf")
+
+        message(STATUS "Downloading Quickbuf jarfile...")
+        file(DOWNLOAD "${BASE_URL}us/hebi/quickbuf/quickbuf-runtime/1.3.2/quickbuf-runtime-1.3.2.jar"
+            "${JAR_ROOT}/quickbuf-runtime-1.3.2.jar")
+
+        message(STATUS "Downloaded.")
+  endif()
+
+  file(GLOB QUICKBUF_JAR
+        ${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf/*.jar)
+
+  set(CMAKE_JAVA_INCLUDE_PATH wpiutil.jar ${JACKSON_JARS} ${QUICKBUF_JAR})
 
   set(CMAKE_JNI_TARGET true)
 
   file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
 
-  if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-    set(CMAKE_JAVA_COMPILE_FLAGS "-h" "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-    add_jar(wpiutil_jar ${JAVA_SOURCES} INCLUDE_JARS ${JACKSON_JARS} OUTPUT_NAME wpiutil)
-  else()
-    add_jar(wpiutil_jar ${JAVA_SOURCES} INCLUDE_JARS ${JACKSON_JARS} OUTPUT_NAME wpiutil GENERATE_NATIVE_HEADERS wpiutil_jni_headers)
-  endif()
+  add_jar(wpiutil_jar ${JAVA_SOURCES} INCLUDE_JARS ${JACKSON_JARS} ${QUICKBUF_JAR} OUTPUT_NAME wpiutil GENERATE_NATIVE_HEADERS wpiutil_jni_headers)
 
   get_property(WPIUTIL_JAR_FILE TARGET wpiutil_jar PROPERTY JAR_FILE)
   install(FILES ${WPIUTIL_JAR_FILE} DESTINATION "${java_lib_dest}")
@@ -57,20 +67,35 @@
 
   set_property(TARGET wpiutiljni PROPERTY FOLDER "libraries")
 
-  if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
-    target_include_directories(wpiutiljni PRIVATE ${JNI_INCLUDE_DIRS})
-    target_include_directories(wpiutiljni PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
-  else()
-    target_link_libraries(wpiutiljni PRIVATE wpiutil_jni_headers)
-  endif()
+  target_link_libraries(wpiutiljni PRIVATE wpiutil_jni_headers)
   add_dependencies(wpiutiljni wpiutil_jar)
 
-  if (MSVC)
-    install(TARGETS wpiutiljni RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-  endif()
+  install(TARGETS wpiutiljni EXPORT wpiutiljni)
 
-  install(TARGETS wpiutiljni EXPORT wpiutiljni DESTINATION "${main_lib_dest}")
+endif()
 
+if (WITH_JAVA_SOURCE)
+  find_package(Java REQUIRED)
+  include(UseJava)
+  file(GLOB WPIUTIL_SOURCES src/main/java/edu/wpi/first/util/*.java)
+  file(GLOB WPIUTIL_CLEANUP_SOURCES src/main/java/edu/wpi/first/util/cleanup/*.java)
+  file(GLOB WPIUTIL_CONCURRENT_SOURCES src/main/java/edu/wpi/first/util/concurrent/*.java)
+  file(GLOB WPIUTIL_DATALOG_SOURCES src/main/java/edu/wpi/first/util/datalog/*.java)
+  file(GLOB WPIUTIL_FUNCTION_SOURCES src/main/java/edu/wpi/first/util/function/*.java)
+  file(GLOB WPIUTIL_SENDABLE_SOURCES src/main/java/edu/wpi/first/util/sendable/*.java)
+  add_jar(wpiutil_src_jar
+  RESOURCES NAMESPACE "edu/wpi/first/util" ${WPIUTIL_SOURCES}
+  NAMESPACE "edu/wpi/first/util/cleanup" ${WPIUTIL_CLEANUP_SOURCES}
+  NAMESPACE "edu/wpi/first/util/concurrent" ${WPIUTIL_CONCURRENT_SOURCES}
+  NAMESPACE "edu/wpi/first/util/datalog" ${WPIUTIL_DATALOG_SOURCES}
+  NAMESPACE "edu/wpi/first/util/function" ${WPIUTIL_FUNCTION_SOURCES}
+  NAMESPACE "edu/wpi/first/util/sendable" ${WPIUTIL_SENDABLE_SOURCES}
+  OUTPUT_NAME wpiutil-sources)
+
+  get_property(WPIUTIL_SRC_JAR_FILE TARGET wpiutil_src_jar PROPERTY JAR_FILE)
+  install(FILES ${WPIUTIL_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+  set_property(TARGET wpiutil_src_jar PROPERTY FOLDER "java")
 endif()
 
 set(THREADS_PREFER_PTHREAD_FLAG ON)
@@ -85,7 +110,7 @@
     endif()
 endif()
 
-GENERATE_RESOURCES(src/main/native/resources generated/main/cpp WPI wpi wpiutil_resources_src)
+generate_resources(src/main/native/resources generated/main/cpp WPI wpi wpiutil_resources_src)
 
 file(GLOB_RECURSE wpiutil_native_src src/main/native/cpp/*.cpp
                                      src/main/native/thirdparty/json/cpp/*.cpp
@@ -100,18 +125,18 @@
 file(GLOB fmtlib_native_src src/main/native/thirdparty/fmtlib/src/*.cpp)
 file(GLOB_RECURSE memory_native_src src/main/native/thirdparty/memory/src/*.cpp)
 
-add_library(wpiutil ${wpiutil_native_src} ${fmtlib_native_src} ${memory_native_src} ${wpiutil_resources_src})
+add_library(wpiutil ${wpiutil_native_src} ${memory_native_src} ${wpiutil_resources_src})
 set_target_properties(wpiutil PROPERTIES DEBUG_POSTFIX "d")
 
 set_property(TARGET wpiutil PROPERTY FOLDER "libraries")
 
 target_compile_features(wpiutil PUBLIC cxx_std_20)
 if (MSVC)
-    target_compile_options(wpiutil PUBLIC /permissive- /Zc:throwingNew /MP /bigobj)
+    target_compile_options(wpiutil PUBLIC /permissive- /Zc:preprocessor /Zc:throwingNew /MP /bigobj)
     target_compile_definitions(wpiutil PRIVATE -D_CRT_SECURE_NO_WARNINGS)
 endif()
 wpilib_target_warnings(wpiutil)
-target_link_libraries(wpiutil Threads::Threads ${CMAKE_DL_LIBS})
+target_link_libraries(wpiutil protobuf::libprotobuf Threads::Threads ${CMAKE_DL_LIBS})
 
 if (ATOMIC)
     target_link_libraries(wpiutil ${ATOMIC})
@@ -127,6 +152,13 @@
 else()
     find_package(fmt CONFIG REQUIRED)
     target_link_libraries(wpiutil fmt::fmt)
+    if(MSVC)
+        get_target_property(fmt_includes fmt::fmt INTERFACE_INCLUDE_DIRECTORIES)
+        foreach(dir ${fmt_includes})
+            target_compile_options(wpiutil PUBLIC /external:I "${dir}")
+        endforeach()
+        target_compile_options(wpiutil PUBLIC /external:W0)
+    endif()
 endif()
 
 if (MSVC)
@@ -146,11 +178,6 @@
                             $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/memory/include>
                             $<INSTALL_INTERFACE:${include_dest}/wpiutil>)
 
-install(DIRECTORY src/main/native/thirdparty/ghc/include/ DESTINATION "${include_dest}/wpiutil")
-target_include_directories(wpiutil PUBLIC
-                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/ghc/include>
-                            $<INSTALL_INTERFACE:${include_dest}/wpiutil>)
-
 install(DIRECTORY src/main/native/thirdparty/json/include/ DESTINATION "${include_dest}/wpiutil")
 target_include_directories(wpiutil PUBLIC
                             $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/json/include>
@@ -176,11 +203,7 @@
                             $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
                             $<INSTALL_INTERFACE:${include_dest}/wpiutil>)
 
-install(TARGETS wpiutil EXPORT wpiutil DESTINATION "${main_lib_dest}")
-
-if (WITH_JAVA AND MSVC)
-    install(TARGETS wpiutil RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
-endif()
+install(TARGETS wpiutil EXPORT wpiutil)
 
 if (WITH_FLAT_INSTALL)
     set (wpiutil_config_dir ${wpilib_dest})
@@ -192,7 +215,7 @@
 install(FILES ${WPILIB_BINARY_DIR}/wpiutil-config.cmake DESTINATION ${wpiutil_config_dir})
 install(EXPORT wpiutil DESTINATION ${wpiutil_config_dir})
 
-SUBDIR_LIST(wpiutil_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
+subdir_list(wpiutil_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
 foreach(example ${wpiutil_examples})
     file(GLOB wpiutil_example_src examples/${example}/*.cpp)
     if(wpiutil_example_src)
@@ -204,7 +227,10 @@
 endforeach()
 
 if (WITH_TESTS)
+    file(GLOB_RECURSE wpiutil_testlib_src src/test/native/include/*.h)
+    add_library(wpiutil_testlib INTERFACE ${wpiutil_test_src})
+    target_include_directories(wpiutil_testlib INTERFACE src/test/native/include)
+
     wpilib_add_test(wpiutil src/test/native/cpp)
-    target_include_directories(wpiutil_test PRIVATE src/test/native/include)
-    target_link_libraries(wpiutil_test wpiutil gmock_main)
+    target_link_libraries(wpiutil_test wpiutil gmock_main wpiutil_testlib)
 endif()
diff --git a/third_party/allwpilib/wpiutil/build.gradle b/third_party/allwpilib/wpiutil/build.gradle
index 501176a..ab13092 100644
--- a/third_party/allwpilib/wpiutil/build.gradle
+++ b/third_party/allwpilib/wpiutil/build.gradle
@@ -26,18 +26,13 @@
                     srcDirs 'src/main/native/thirdparty/fmtlib/include'
                 }
             }
-            ghcCpp(CppSourceSet) {
-                exportedHeaders {
-                    srcDirs 'src/main/native/thirdparty/ghc/include'
-                }
-            }
             jsonCpp(CppSourceSet) {
                 source {
                     srcDirs 'src/main/native/thirdparty/json/cpp'
                     include '*.cpp'
                 }
                 exportedHeaders {
-                    srcDirs 'src/main/native/include', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/tcb_span/include', 'src/main/native/thirdparty/fmtlib/include'
+                    srcDirs 'src/main/native/include', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/fmtlib/include'
                 }
             }
             llvmCpp(CppSourceSet) {
@@ -46,7 +41,7 @@
                     include '**/*.cpp'
                 }
                 exportedHeaders {
-                    srcDirs 'src/main/native/include', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/tcb_span/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/ghc/include'
+                    srcDirs 'src/main/native/include', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/fmtlib/include'
                 }
             }
             mpackCpp(CppSourceSet) {
@@ -67,11 +62,6 @@
                     srcDirs 'src/main/native/thirdparty/sigslot/include'
                 }
             }
-            tcbSpanCpp(CppSourceSet) {
-                exportedHeaders {
-                    srcDirs 'src/main/native/thirdparty/tcb_span/include'
-                }
-            }
             memoryCpp(CppSourceSet) {
                 source {
                     srcDirs 'src/main/native/thirdparty/memory/src', 'src/main/native/thirdparty/memory/include/wpi/memory'
@@ -82,6 +72,15 @@
                     include '**/*.hpp'
                 }
             }
+            protobufCpp(CppSourceSet) {
+                source {
+                    srcDirs 'src/main/native/thirdparty/protobuf/src'
+                    include '**/*.cpp'
+                }
+                exportedHeaders {
+                    srcDirs 'src/main/native/thirdparty/protobuf/include'
+                }
+            }
             resourcesCpp(CppSourceSet) {
                 source {
                     srcDirs "$buildDir/generated/main/cpp", "$rootDir/shared/singlelib"
@@ -101,7 +100,7 @@
                         include '**/*.cpp'
                     }
                     exportedHeaders {
-                        srcDirs 'src/main/native/include', 'src/main/native/cpp', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/tcb_span/include', 'src/main/native/thirdparty/mpack/include'
+                        srcDirs 'src/main/native/include', 'src/main/native/cpp', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/mpack/include'
                         include '**/*.h'
                     }
                 }
@@ -115,7 +114,7 @@
                         include '**/*.cpp'
                     }
                     exportedHeaders {
-                        srcDirs 'src/main/native/include', 'src/main/native/cpp', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/tcb_span/include', 'src/main/native/thirdparty/mpack/include'
+                        srcDirs 'src/main/native/include', 'src/main/native/cpp', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/mpack/include'
                         include '**/*.h'
                     }
                 }
@@ -128,7 +127,7 @@
                         include '**/*.cpp'
                     }
                     exportedHeaders {
-                        srcDirs 'src/main/native/include', 'src/main/native/cpp', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/tcb_span/include', 'src/main/native/thirdparty/mpack/include'
+                        srcDirs 'src/main/native/include', 'src/main/native/cpp', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/mpack/include'
                         include '**/*.h'
                     }
                 }
@@ -141,13 +140,19 @@
                         include '**/*.cpp'
                     }
                     exportedHeaders {
-                        srcDirs 'src/main/native/include', 'src/main/native/cpp', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/tcb_span/include', 'src/main/native/thirdparty/mpack/include'
+                        srcDirs 'src/main/native/include', 'src/main/native/cpp', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/mpack/include'
                         include '**/*.h'
                     }
                 }
             }
         }
     }
+
+    exeSplitSetup = {
+        if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+            nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
+        }
+    }
 }
 
 def examplesMap = [:];
@@ -184,9 +189,6 @@
     from('src/main/native/thirdparty/fmtlib/include') {
         into '/'
     }
-    from('src/main/native/thirdparty/ghc/include') {
-        into '/'
-    }
     from('src/main/native/thirdparty/json/include') {
         into '/'
     }
@@ -199,10 +201,10 @@
     from('src/main/native/thirdparty/sigslot/include') {
         into '/'
     }
-    from('src/main/native/thirdparty/tcb_span/include') {
+    from('src/main/native/thirdparty/memory/include') {
         into '/'
     }
-    from('src/main/native/thirdparty/memory/include') {
+    from('src/main/native/thirdparty/protobuf/include') {
         into '/'
     }
 }
@@ -223,6 +225,9 @@
     from('src/main/native/thirdparty/mpack/src') {
         into '/'
     }
+    from('src/main/native/thirdparty/protobuf/src') {
+        into '/'
+    }
     from('src/main/native/thirdparty/sigslot/src') {
         into '/'
     }
@@ -233,7 +238,7 @@
         all {
             it.sources.each {
                 it.exportedHeaders {
-                    srcDirs 'src/main/native/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/tcb_span/include', 'src/main/native/thirdparty/ghc/include', 'src/main/native/thirdparty/memory/include', 'src/main/native/thirdparty/mpack/include'
+                    srcDirs 'src/main/native/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/memory/include', 'src/main/native/thirdparty/mpack/include', 'src/main/native/thirdparty/protobuf/include'
                 }
             }
         }
@@ -247,6 +252,9 @@
                 targetBuildTypes 'debug'
                 binaries.all {
                     lib library: 'wpiutil', linkage: 'shared'
+                    if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                        nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
+                    }
                 }
                 sources {
                     cpp {
@@ -261,6 +269,17 @@
     }
 }
 
+model {
+    binaries {
+        all {
+            if (!(it instanceof NativeBinarySpec)) return
+                if (it.component.name != 'wpiutil' && it.component.name != 'wpiutilBase') return
+                if (it.targetPlatform.name != nativeUtils.wpi.platforms.roborio) return
+                nativeUtils.useRequiredLibrary(it, 'ni_link_libraries')
+        }
+    }
+}
+
 sourceSets {
     printlog
 }
@@ -272,9 +291,10 @@
 }
 
 dependencies {
-    api "com.fasterxml.jackson.core:jackson-annotations:2.12.4"
-    api "com.fasterxml.jackson.core:jackson-core:2.12.4"
-    api "com.fasterxml.jackson.core:jackson-databind:2.12.4"
+    api "com.fasterxml.jackson.core:jackson-annotations:2.15.2"
+    api "com.fasterxml.jackson.core:jackson-core:2.15.2"
+    api "com.fasterxml.jackson.core:jackson-databind:2.15.2"
+    api 'us.hebi.quickbuf:quickbuf-runtime:1.3.2'
 
     printlogImplementation sourceSets.main.output
 }
diff --git a/third_party/allwpilib/wpiutil/doc/struct.adoc b/third_party/allwpilib/wpiutil/doc/struct.adoc
new file mode 100644
index 0000000..cac4d4a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/doc/struct.adoc
@@ -0,0 +1,311 @@
+= WPILib Packed Struct Serialization Specification, Version 1.0
+WPILib Developers <wpilib@wpi.edu>
+Revision 1.0 (0x0100), 6/8/2023
+:toc:
+:toc-placement: preamble
+:sectanchors:
+
+A simple format and schema for serialization of packed fixed size structured data.
+
+[[motivation]]
+== Motivation
+
+Schema-based serialization formats such as Protobuf and Flatbuffers are extremely flexible and can handle data type evolution, complex nested data structures, variable size / repeated data, optional fields, etc. However, this flexibility comes at a cost in both serialized data size and processing overhead. Many simple data structures, such as screen coordinates or robot poses, are fixed in size and can be stored much more compactly and serialized/deserialized much more quickly, especially on embedded or real-time platforms.
+
+Simply storing a C-style packed structure is very compact and fast, but information about the layout of the structure and the meaning of each member must be separately communicated for introspection by other tools such as interactive dashboards for data analysis of individual structure members. The motivation for this standard layout and schema is to provide a standardized means to communicate this information and enable dynamic decoding.
+
+Python's struct module uses a character-based approach to describe data layout of structures, but has no provisions for naming each member to communicate intent/meaning.
+
+[[references]]
+== References
+
+[[c-struct-declaration]]
+* Struct declaration, https://en.cppreference.com/w/c/language/struct
+
+[[definitions]]
+== Definitions
+
+[[schema]]
+== Schema
+
+The schema is a text-based format with similar syntax to the list of variable declarations in a C structure. The C syntax is flexible, easy to parse, and matches the intent of specifying a fixed size structure.
+
+Each member of the struct is defined by a single declaration. Each declaration is either a standard declaration or a bit-field declaration. Declarations are separated by semicolons. The last declaration may optionally have a trailing semicolon. Empty declarations (e.g. two semicolons back-to-back or separated by only whitespace) are allowed but are ignored. Unlike C structures, every declaration must be separated by a semicolon; commas cannot be used to declare multiple members with the same type. Declarations may also start and end with whitespace.
+
+[[variable]]
+=== Standard Declaration
+
+Standard declarations declare a member of a certain type or a fixed-size array of that type. The structure of a standard declaration is:
+
+* optional enum specification (integer data types only)
+* optional whitespace
+* type name
+* whitespace
+* identifier name
+* optional array size, consisting of:
+  * optional whitespace
+  * `[`
+  * optional whitespace
+  * size of array
+  * optional whitespace
+  * `]`
+
+The type name may be one of these:
+
+[cols="1,1,3", options="header"]
+|===
+|Type Name|Description|Payload Data Contents
+|`bool`|boolean|single byte (0=false, 1=true)
+|`char`|character|single byte (assumed UTF-8)
+|`int8`|integer|1-byte (8-bit) signed value
+|`int16`|integer|2-byte (16-bit) signed value
+|`int32`|integer|4-byte (32-bit) signed value
+|`int64`|integer|8-byte (64-bit) signed value
+|`uint8`|unsigned integer|1-byte (8-bit) unsigned value
+|`uint16`|unsigned integer|2-byte (16-bit) unsigned value
+|`uint32`|unsigned integer|4-byte (32-bit) unsigned value
+|`uint64`|unsigned integer|8-byte (64-bit) unsigned value
+|`float` or `float32`|float|4-byte (32-bit) IEEE-754 value
+|`double` or `float64`|double|8-byte (64-bit) IEEE-754 value
+|===
+
+If it is not one of the above, the type name must be the name of another struct.
+
+Examples of valid standard declarations:
+
+* `bool value` (boolean value, 1 byte)
+* `double arr[4]` (array of 4 doubles, 32 bytes total)
+* `enum {a=1, b=2} int8 val` (enumerated value, 1 byte)
+
+[[enum]]
+==== Enum Specification
+
+Integer declarations may have an enum specification to provide meaning to specific values. Values that are not specified may be communicated, but have no specific defined meaning. The structure of an enum specification is:
+
+* optional `enum`
+* optional whitespace
+* `{`
+* zero or more enum values, consisting of:
+  * optional whitespace
+  * identifier
+  * optional whitespace
+  * `=`
+  * optional whitespace
+  * integer value
+  * optional whitespace
+  * comma (optional for last value)
+* optional whitespace
+* `}`
+
+Examples of valid enum specifications:
+
+* `enum{}`
+* `enum { a = 1 }`
+* `enum{a=1,b=2,}`
+* `{a=1}`
+
+Examples of invalid enum specifications:
+
+* `enum` (no `{}`)
+* `enum{=2}` (missing identifier)
+* `enum{a=1,b,c}` (missing values)
+
+[[]]
+=== Bit-field Declaration
+
+Bit-field declarations declare a member with an explicit width in bits. The structure of a bit-field declaration is:
+
+* optional enum specification (integer data types only)
+* optional whitespace
+* type name; must be boolean or one of the integer data types
+* whitespace
+* identifier name
+* optional whitespace
+* colon (`:`)
+* optional whitespace
+* integer number of bits; minimum 1; maximum 1 for boolean types; for integer types, maximum is the width of the type (e.g. 32 for int32)
+
+As with non-bit-field integer variable declarations, an enum can be specified for integer bit-fields (e.g. `enum {a=1, b=2} uint32 value : 2`).
+
+It is not possible to have an array of bit-fields.
+
+Examples of valid bit-field declarations:
+
+* `bool value : 1`
+* `enum{a=1,b=2}int8 value:2`
+
+Examples of invalid bit-field declarations:
+
+* `double val:2` (must be integer or boolean)
+* `int32 val[2]:2` (cannot be array)
+* `bool val:3` (bool must be 1 bit)
+* `int16 val:17` (bit field larger than storage size)
+
+[[layout]]
+== Data Layout
+
+Members are stored in the same order they appear in the schema. Individual members are stored in little-endian order. Members are not aligned to any particular boundary; no byte-level padding is present in the data.
+
+[source]
+----
+bool b;
+int16 i;
+----
+
+results in a 3-byte encoding:
+
+`bbbbbbbb iiiiiiii iiiiiiii`
+
+where the first `iiiiiiii` is the least significant byte of `i`.
+
+[[layout-array]]
+=== Array Data Layout
+
+For array members, the individual items of the array are stored consecutively with no padding between each item.
+
+[source]
+----
+int16 i[2];
+----
+
+results in a 4-byte encoding:
+
+`i0i0i0i0 i0i0i0i0 i1i1i1i1 i1i1i1i1`
+
+where `i0` is the first element of the array, `i1` is the second element.
+
+[[layout-nested-structure]]
+
+Nested structures also have no surrounding padding.
+
+Given the Inner schema
+
+[source]
+----
+int16 i;
+int8 x;
+----
+
+and an outer schema of
+
+[source]
+----
+char c;
+Inner s;
+bool b;
+----
+
+results in a 5-byte encoding:
+
+`cccccccc iiiiiiii iiiiiiii xxxxxxxx bbbbbbbb`
+
+[[layout-bit-field]]
+=== Bit-Field Data Layout
+
+Multiple adjacant bit-fields of the same integer type width are packed together to fit in the minimum number of multiples of that type. The bit-fields are packed, starting from the least significant bit, in the order they appear in the schema. Individual bit-fields must not span across multiple underlying types; if a bit-field is larger than the remaining space in the data type, a new element of that type is started and the bit-field starts from the least significant bit of the new element. Unused bits should be set to 0 during serialization and must be ignored during deserialization.
+
+Boolean bit-fields are always a single bit wide. The underlying data type is by default uint8, but if a boolean bit-field immediately follows a bit-field of another integer type (and fits), it is packed into that type.
+
+[source]
+----
+int8 a:4;
+int16 b:4;
+----
+
+results in a 3-byte encoding:
+
+`0000aaaa 0000bbbb 00000000`
+
+as the integer type widths are different, even though the bits would fit.
+
+[source]
+----
+int16 a:4;
+uint16 b:5;
+bool c:1;
+int16 d:7;
+----
+
+results in a 4-byte encoding:
+
+`bbbbaaaa 000000cb 0ddddddd 00000000`
+
+As `c` is packed into the preceding int16, and `d` is too large to fit in the remaining bits of the first type.
+
+[source]
+----
+uint8 a:4;
+int8 b:2;
+bool c:1;
+int16 d:1;
+----
+
+results in a 3-byte encoding:
+
+`0cbbaaaa 0000000d 00000000`
+
+as `d` is int16, versus the `int8` of the previous values.
+
+[source]
+----
+bool a:1;
+bool b:1;
+int8 c:2;
+----
+
+results in a 1-byte encoding:
+
+`0000ccba`
+
+as `c` is an int8.
+
+[source]
+----
+bool a:1;
+bool b:1;
+int16 c:2;
+----
+
+results in a 3-byte encoding:
+
+`000000ba 000000cc 00000000`
+
+as `c` is an int16.
+
+Bit-fields do not "look inside" of nested structures. Given Inner
+
+[source]
+----
+int8 a:1;
+----
+
+and outer
+
+[source]
+----
+int8 b:1;
+Outer s;
+int8 c:1;
+----
+
+the result is a 3-byte encoding:
+
+`0000000b 0000000a 0000000c`
+
+[[layout-character-arrays]]
+=== Character Array (String) Data Layout
+
+Character arrays, as with other arrays, must be fixed length. The text they contain should be UTF-8. If a string is shorter than the length of the character array, the string starts at the first byte of the array, and any unused bytes at the end of the array must be filled with 0.
+
+[source]
+----
+char s[4];
+----
+
+with a string of "a" results in:
+
+`01100001 00000000 00000000 00000000`
+
+with a string of "abcd" results in:
+
+`01100001 01100010 01100011 01100100`
diff --git a/third_party/allwpilib/wpiutil/examples/printlog/datalog.py b/third_party/allwpilib/wpiutil/examples/printlog/datalog.py
index b1035dd..0f725fd 100755
--- a/third_party/allwpilib/wpiutil/examples/printlog/datalog.py
+++ b/third_party/allwpilib/wpiutil/examples/printlog/datalog.py
@@ -5,6 +5,7 @@
 
 import array
 import struct
+import msgpack
 from typing import List, SupportsBytes
 
 __all__ = ["StartRecordData", "MetadataRecordData", "DataLogRecord", "DataLogReader"]
@@ -128,6 +129,9 @@
     def getString(self) -> str:
         return str(self.data, encoding="utf-8")
 
+    def getMsgPack(self):
+        return msgpack.unpackb(self.data)
+
     def getBooleanArray(self) -> List[bool]:
         return [x != 0 for x in self.data]
 
@@ -326,6 +330,8 @@
                         print(f"  {record.getInteger()}")
                     elif entry.type in ("string", "json"):
                         print(f"  '{record.getString()}'")
+                    elif entry.type == "msgpack":
+                        print(f"  '{record.getMsgPack()}'")
                     elif entry.type == "boolean":
                         print(f"  {record.getBoolean()}")
                     elif entry.type == "boolean[]":
diff --git a/third_party/allwpilib/wpiutil/examples/printlog/printlog.cpp b/third_party/allwpilib/wpiutil/examples/printlog/printlog.cpp
index 073a247..cb89934 100644
--- a/third_party/allwpilib/wpiutil/examples/printlog/printlog.cpp
+++ b/third_party/allwpilib/wpiutil/examples/printlog/printlog.cpp
@@ -4,8 +4,9 @@
 
 #include <ctime>
 
-#include "fmt/chrono.h"
-#include "fmt/format.h"
+#include <fmt/chrono.h>
+#include <fmt/format.h>
+
 #include "wpi/DataLogReader.h"
 #include "wpi/DenseMap.h"
 #include "wpi/MemoryBuffer.h"
diff --git a/third_party/allwpilib/wpiutil/examples/writelog/writelog.cpp b/third_party/allwpilib/wpiutil/examples/writelog/writelog.cpp
index 6fdf763..bd5e14f 100644
--- a/third_party/allwpilib/wpiutil/examples/writelog/writelog.cpp
+++ b/third_party/allwpilib/wpiutil/examples/writelog/writelog.cpp
@@ -8,7 +8,8 @@
 #include <string>
 #include <vector>
 
-#include "fmt/format.h"
+#include <fmt/format.h>
+
 #include "wpi/DataLog.h"
 
 int main(int argc, char** argv) {
diff --git a/third_party/allwpilib/wpiutil/src/dev/native/cpp/main.cpp b/third_party/allwpilib/wpiutil/src/dev/native/cpp/main.cpp
index 14ddda2..fb2a4b0 100644
--- a/third_party/allwpilib/wpiutil/src/dev/native/cpp/main.cpp
+++ b/third_party/allwpilib/wpiutil/src/dev/native/cpp/main.cpp
@@ -2,7 +2,8 @@
 // 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 "fmt/core.h"
+#include <fmt/core.h>
+
 #include "wpi/SmallString.h"
 
 int main() {
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/ClassPreloader.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/ClassPreloader.java
new file mode 100644
index 0000000..6d8e2a9
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/ClassPreloader.java
@@ -0,0 +1,119 @@
+// 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.
+
+package edu.wpi.first.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Paths;
+
+/**
+ * Loads classes by name. Can be used at any time, but is most commonly used to preload classes at
+ * the start of the program to avoid unpredictable delays due to lazy classloading later in program
+ * execution.
+ */
+public final class ClassPreloader {
+  private ClassPreloader() {}
+
+  /**
+   * Loads classes from an iterable.
+   *
+   * @param classNames iterable of class names
+   * @return Number of classes loaded.
+   */
+  public static int preload(Iterable<String> classNames) {
+    int count = 0;
+    for (String i : classNames) {
+      try {
+        Class.forName(i);
+        count++;
+      } catch (ClassNotFoundException e) {
+        System.out.println("Could not preload " + i);
+      }
+    }
+    return count;
+  }
+
+  /**
+   * Loads classes.
+   *
+   * @param classNames array of class names
+   * @return Number of classes loaded.
+   */
+  public static int preload(String... classNames) {
+    int count = 0;
+    for (String i : classNames) {
+      try {
+        Class.forName(i);
+        count++;
+      } catch (ClassNotFoundException e) {
+        System.out.println("Could not preload " + i);
+      }
+    }
+    return count;
+  }
+
+  /**
+   * Loads classes from a buffered reader. The input is expected to be one class name per line.
+   * Blank lines and lines starting with a semicolon are ignored.
+   *
+   * @param reader Reader
+   * @return Number of classes loaded.
+   */
+  public static int preload(BufferedReader reader) {
+    int count = 0;
+    try {
+      String line = reader.readLine();
+      while (line != null) {
+        if (!line.isEmpty() && !line.startsWith(";")) {
+          try {
+            Class.forName(line);
+            count++;
+          } catch (ClassNotFoundException e) {
+            System.out.println("Could not preload " + line);
+          }
+        }
+        line = reader.readLine();
+      }
+    } catch (IOException e) {
+      System.out.println("Error when reading preload file: " + e);
+    }
+    return count;
+  }
+
+  /**
+   * Loads classes from an input stream. The input is expected to be one class name per line. Blank
+   * lines and lines starting with a semicolon are ignored.
+   *
+   * @param stream input stream
+   * @return Number of classes loaded.
+   */
+  public static int preload(InputStream stream) {
+    return preload(new BufferedReader(new InputStreamReader(stream)));
+  }
+
+  /**
+   * Loads classes from a file. The input is expected to be one class name per line. Blank lines and
+   * lines starting with a semicolon are ignored.
+   *
+   * @param filename filename
+   * @return Number of classes loaded.
+   */
+  public static int preloadFile(String filename) {
+    try (BufferedReader reader =
+        Files.newBufferedReader(Paths.get(filename), StandardCharsets.UTF_8)) {
+      return preload(reader);
+    } catch (NoSuchFileException e) {
+      System.out.println("Could not open preload file " + filename + ": " + e);
+    } catch (IOException e) {
+      System.out.println("Could not close preload file " + filename + ": " + e);
+    }
+    return 0;
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java
index a85379e..4d2c800 100644
--- a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java
@@ -9,8 +9,8 @@
 import java.util.concurrent.locks.ReentrantLock;
 
 public class EventVector {
-  private ReentrantLock m_lock = new ReentrantLock();
-  private List<Integer> m_events = new ArrayList<>();
+  private final ReentrantLock m_lock = new ReentrantLock();
+  private final List<Integer> m_events = new ArrayList<>();
 
   /**
    * Adds an event to the event vector.
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java
index ade2e5a..2c54d00 100644
--- a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java
@@ -9,7 +9,10 @@
 /**
  * Interpolating Tree Maps are used to get values at points that are not defined by making a guess
  * from points that are defined. This uses linear interpolation.
+ *
+ * @deprecated Use {@link edu.wpi.first.math.interpolation.InterpolatingDoubleTreeMap} instead
  */
+@Deprecated(forRemoval = true, since = "2024")
 public class InterpolatingTreeMap<K extends Number, V extends Number> {
   private final TreeMap<K, V> m_map = new TreeMap<>();
 
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/WPICleaner.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/WPICleaner.java
new file mode 100644
index 0000000..0c497d4
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/WPICleaner.java
@@ -0,0 +1,28 @@
+// 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.
+
+package edu.wpi.first.util;
+
+import java.lang.ref.Cleaner;
+import java.lang.ref.Cleaner.Cleanable;
+
+/** Cleaner object for WPILib objects. */
+public final class WPICleaner {
+  private WPICleaner() {
+    throw new UnsupportedOperationException("This is a utility class!");
+  }
+
+  private static final Cleaner instance = Cleaner.create();
+
+  /**
+   * Register an object with the cleaner.
+   *
+   * @param object The object to register.
+   * @param runnable The runnable to call on cleanup.
+   * @return The registered Cleanable.
+   */
+  public static Cleanable register(Object object, Runnable runnable) {
+    return instance.register(object, runnable);
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java
new file mode 100644
index 0000000..fab8316
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java
@@ -0,0 +1,55 @@
+// 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.
+
+package edu.wpi.first.util.cleanup;
+
+import edu.wpi.first.util.ErrorMessages;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+/**
+ * An object containing a Stack of AutoCloseable objects that are closed when this object is closed.
+ */
+public class CleanupPool implements AutoCloseable {
+  // Use a Deque instead of a Stack, as Stack's iterators go the wrong way, and docs
+  // state ArrayDeque is faster anyway.
+  private final Deque<AutoCloseable> m_closers = new ArrayDeque<AutoCloseable>();
+
+  /**
+   * Registers an object in the object stack for cleanup.
+   *
+   * @param <T> The object type
+   * @param object The object to register
+   * @return The registered object
+   */
+  public <T extends AutoCloseable> T register(T object) {
+    ErrorMessages.requireNonNullParam(object, "object", "register");
+    m_closers.addFirst(object);
+    return object;
+  }
+
+  /**
+   * Removes an object from the cleanup stack.
+   *
+   * @param object the object to remove
+   */
+  public void remove(AutoCloseable object) {
+    m_closers.remove(object);
+  }
+
+  /** Closes all objects in the stack. */
+  @Override
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  public void close() {
+    for (AutoCloseable autoCloseable : m_closers) {
+      try {
+        autoCloseable.close();
+      } catch (Exception e) {
+        // Swallow any exceptions on close
+        e.printStackTrace();
+      }
+    }
+    m_closers.clear();
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/cleanup/ReflectionCleanup.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/cleanup/ReflectionCleanup.java
new file mode 100644
index 0000000..0803028
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/cleanup/ReflectionCleanup.java
@@ -0,0 +1,50 @@
+// 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.
+
+package edu.wpi.first.util.cleanup;
+
+import java.lang.reflect.Field;
+
+/**
+ * Implement this interface to have access to a `reflectionCleanup` method that can be called from
+ * your `close` method, that will use reflection to find all `AutoCloseable` instance members and
+ * close them.
+ */
+public interface ReflectionCleanup extends AutoCloseable {
+  /**
+   * Default implementation that uses reflection to find all AutoCloseable fields not marked
+   * SkipCleanup and call close() on them. Call this from your `close()` method with the class level
+   * you want to close.
+   *
+   * @param cls the class level to clean up
+   */
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  default void reflectionCleanup(Class<? extends ReflectionCleanup> cls) {
+    if (!cls.isAssignableFrom(getClass())) {
+      System.out.println("Passed in class is not assignable from \"this\"");
+      System.out.println("Expected something in the hierarchy of" + cls.getName());
+      System.out.println("This is " + getClass().getName());
+      return;
+    }
+    for (Field field : cls.getDeclaredFields()) {
+      if (field.isAnnotationPresent(SkipCleanup.class)) {
+        continue;
+      }
+      if (!AutoCloseable.class.isAssignableFrom(field.getType())) {
+        continue;
+      }
+      if (field.trySetAccessible()) {
+        try {
+          AutoCloseable c = (AutoCloseable) field.get(this);
+          if (c != null) {
+            c.close();
+          }
+        } catch (Exception e) {
+          // Ignore any exceptions
+          e.printStackTrace();
+        }
+      }
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java
new file mode 100644
index 0000000..e2bc72e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java
@@ -0,0 +1,14 @@
+// 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.
+
+package edu.wpi.first.util.cleanup;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SkipCleanup {}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java
index 784b8d6..97c629f 100644
--- a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java
@@ -4,6 +4,15 @@
 
 package edu.wpi.first.util.datalog;
 
+import edu.wpi.first.util.WPIUtilJNI;
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.struct.Struct;
+import java.nio.ByteBuffer;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
 /**
  * A data log. The log file is created immediately upon construction with a temporary filename. The
  * file may be renamed at any time using the setFilename() function.
@@ -97,11 +106,144 @@
     DataLogJNI.pause(m_impl);
   }
 
-  /** Resumes appending of data records to the log. */
+  /**
+   * Resumes appending of data records to the log. If called after stop(), opens a new file (with
+   * random name if SetFilename was not called after stop()) and appends Start records and schema
+   * data values for all previously started entries and schemas.
+   */
   public void resume() {
     DataLogJNI.resume(m_impl);
   }
 
+  /** Stops appending all records to the log, and closes the log file. */
+  public void stop() {
+    DataLogJNI.stop(m_impl);
+  }
+
+  /**
+   * Returns whether there is a data schema already registered with the given name.
+   *
+   * @param name Name (the string passed as the data type for records using this schema)
+   * @return True if schema already registered
+   */
+  public boolean hasSchema(String name) {
+    return m_schemaMap.containsKey(name);
+  }
+
+  /**
+   * Registers a data schema. Data schemas provide information for how a certain data type string
+   * can be decoded. The type string of a data schema indicates the type of the schema itself (e.g.
+   * "protobuf" for protobuf schemas, "struct" for struct schemas, etc). In the data log, schemas
+   * are saved just like normal records, with the name being generated from the provided name:
+   * "/.schema/name". Duplicate calls to this function with the same name are silently ignored.
+   *
+   * @param name Name (the string passed as the data type for records using this schema)
+   * @param type Type of schema (e.g. "protobuf", "struct", etc)
+   * @param schema Schema data
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
+  public void addSchema(String name, String type, byte[] schema, long timestamp) {
+    if (m_schemaMap.putIfAbsent(name, 1) != null) {
+      return;
+    }
+    DataLogJNI.addSchema(m_impl, name, type, schema, timestamp);
+  }
+
+  /**
+   * Registers a data schema. Data schemas provide information for how a certain data type string
+   * can be decoded. The type string of a data schema indicates the type of the schema itself (e.g.
+   * "protobuf" for protobuf schemas, "struct" for struct schemas, etc). In the data log, schemas
+   * are saved just like normal records, with the name being generated from the provided name:
+   * "/.schema/name". Duplicate calls to this function with the same name are silently ignored.
+   *
+   * @param name Name (the string passed as the data type for records using this schema)
+   * @param type Type of schema (e.g. "protobuf", "struct", etc)
+   * @param schema Schema data
+   */
+  public void addSchema(String name, String type, byte[] schema) {
+    addSchema(name, type, schema, 0);
+  }
+
+  /**
+   * Registers a data schema. Data schemas provide information for how a certain data type string
+   * can be decoded. The type string of a data schema indicates the type of the schema itself (e.g.
+   * "protobuf" for protobuf schemas, "struct" for struct schemas, etc). In the data log, schemas
+   * are saved just like normal records, with the name being generated from the provided name:
+   * "/.schema/name". Duplicate calls to this function with the same name are silently ignored.
+   *
+   * @param name Name (the string passed as the data type for records using this schema)
+   * @param type Type of schema (e.g. "protobuf", "struct", etc)
+   * @param schema Schema data
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
+  public void addSchema(String name, String type, String schema, long timestamp) {
+    if (m_schemaMap.putIfAbsent(name, 1) != null) {
+      return;
+    }
+    DataLogJNI.addSchemaString(m_impl, name, type, schema, timestamp);
+  }
+
+  /**
+   * Registers a data schema. Data schemas provide information for how a certain data type string
+   * can be decoded. The type string of a data schema indicates the type of the schema itself (e.g.
+   * "protobuf" for protobuf schemas, "struct" for struct schemas, etc). In the data log, schemas
+   * are saved just like normal records, with the name being generated from the provided name:
+   * "/.schema/name". Duplicate calls to this function with the same name are silently ignored.
+   *
+   * @param name Name (the string passed as the data type for records using this schema)
+   * @param type Type of schema (e.g. "protobuf", "struct", etc)
+   * @param schema Schema data
+   */
+  public void addSchema(String name, String type, String schema) {
+    addSchema(name, type, schema, 0);
+  }
+
+  /**
+   * Registers a protobuf schema. Duplicate calls to this function with the same name are silently
+   * ignored.
+   *
+   * @param proto protobuf serialization object
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void addSchema(Protobuf<?, ?> proto, long timestamp) {
+    final long actualTimestamp = timestamp == 0 ? WPIUtilJNI.now() : timestamp;
+    proto.forEachDescriptor(
+        this::hasSchema,
+        (typeString, schema) ->
+            addSchema(typeString, "proto:FileDescriptorProto", schema, actualTimestamp));
+  }
+
+  /**
+   * Registers a protobuf schema. Duplicate calls to this function with the same name are silently
+   * ignored.
+   *
+   * @param proto protobuf serialization object
+   */
+  public void addSchema(Protobuf<?, ?> proto) {
+    addSchema(proto, 0);
+  }
+
+  /**
+   * Registers a struct schema. Duplicate calls to this function with the same name are silently
+   * ignored.
+   *
+   * @param struct struct serialization object
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void addSchema(Struct<?> struct, long timestamp) {
+    addSchemaImpl(struct, timestamp == 0 ? WPIUtilJNI.now() : timestamp, new HashSet<>());
+  }
+
+  /**
+   * Registers a struct schema. Duplicate calls to this function with the same name are silently
+   * ignored.
+   *
+   * @param struct struct serialization object
+   */
+  public void addSchema(Struct<?> struct) {
+    addSchema(struct, 0);
+  }
+
   /**
    * Start an entry. Duplicate names are allowed (with the same type), and result in the same index
    * being returned (start/finish are reference counted). A duplicate name with a different type
@@ -188,14 +330,52 @@
   }
 
   /**
-   * Appends a record to the log.
+   * Appends a raw record to the log.
    *
-   * @param entry Entry index, as returned by Start()
-   * @param data Data to record
+   * @param entry Entry index, as returned by start()
+   * @param data Byte array to record; will send entire array contents
    * @param timestamp Time stamp (0 to indicate now)
    */
   public void appendRaw(int entry, byte[] data, long timestamp) {
-    DataLogJNI.appendRaw(m_impl, entry, data, timestamp);
+    appendRaw(entry, data, 0, data.length, timestamp);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param data Byte array to record
+   * @param start Start position of data (in byte array)
+   * @param len Length of data (must be less than or equal to data.length - start)
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void appendRaw(int entry, byte[] data, int start, int len, long timestamp) {
+    DataLogJNI.appendRaw(m_impl, entry, data, start, len, timestamp);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param data Buffer to record; will send from data.position() to data.limit()
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void appendRaw(int entry, ByteBuffer data, long timestamp) {
+    int pos = data.position();
+    appendRaw(entry, data, pos, data.limit() - pos, timestamp);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param data Buffer to record
+   * @param start Start position of data (in buffer)
+   * @param len Length of data (must be less than or equal to data.capacity() - start)
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void appendRaw(int entry, ByteBuffer data, int start, int len, long timestamp) {
+    DataLogJNI.appendRaw(m_impl, entry, data, start, len, timestamp);
   }
 
   @Override
@@ -204,42 +384,112 @@
     m_impl = 0;
   }
 
+  /**
+   * Appends a boolean record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param value Boolean value to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
   public void appendBoolean(int entry, boolean value, long timestamp) {
     DataLogJNI.appendBoolean(m_impl, entry, value, timestamp);
   }
 
+  /**
+   * Appends an integer record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param value Integer value to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
   public void appendInteger(int entry, long value, long timestamp) {
     DataLogJNI.appendInteger(m_impl, entry, value, timestamp);
   }
 
+  /**
+   * Appends a float record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param value Float value to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
   public void appendFloat(int entry, float value, long timestamp) {
     DataLogJNI.appendFloat(m_impl, entry, value, timestamp);
   }
 
+  /**
+   * Appends a double record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param value Double value to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
   public void appendDouble(int entry, double value, long timestamp) {
     DataLogJNI.appendDouble(m_impl, entry, value, timestamp);
   }
 
+  /**
+   * Appends a string record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param value String value to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
   public void appendString(int entry, String value, long timestamp) {
     DataLogJNI.appendString(m_impl, entry, value, timestamp);
   }
 
+  /**
+   * Appends a boolean array record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param arr Boolean array to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
   public void appendBooleanArray(int entry, boolean[] arr, long timestamp) {
     DataLogJNI.appendBooleanArray(m_impl, entry, arr, timestamp);
   }
 
+  /**
+   * Appends an integer array record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param arr Integer array to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
   public void appendIntegerArray(int entry, long[] arr, long timestamp) {
     DataLogJNI.appendIntegerArray(m_impl, entry, arr, timestamp);
   }
 
+  /**
+   * Appends a float array record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param arr Float array to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
   public void appendFloatArray(int entry, float[] arr, long timestamp) {
     DataLogJNI.appendFloatArray(m_impl, entry, arr, timestamp);
   }
 
+  /**
+   * Appends a double array record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param arr Double array to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
   public void appendDoubleArray(int entry, double[] arr, long timestamp) {
     DataLogJNI.appendDoubleArray(m_impl, entry, arr, timestamp);
   }
 
+  /**
+   * Appends a string array record to the log.
+   *
+   * @param entry Entry index, as returned by start()
+   * @param arr String array to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
   public void appendStringArray(int entry, String[] arr, long timestamp) {
     DataLogJNI.appendStringArray(m_impl, entry, arr, timestamp);
   }
@@ -248,5 +498,21 @@
     return m_impl;
   }
 
+  private void addSchemaImpl(Struct<?> struct, long timestamp, Set<String> seen) {
+    String typeString = struct.getTypeString();
+    if (hasSchema(typeString)) {
+      return;
+    }
+    if (!seen.add(typeString)) {
+      throw new UnsupportedOperationException(typeString + ": circular reference with " + seen);
+    }
+    addSchema(typeString, "structschema", struct.getSchema(), timestamp);
+    for (Struct<?> inner : struct.getNested()) {
+      addSchemaImpl(inner, timestamp, seen);
+    }
+    seen.remove(typeString);
+  }
+
   private long m_impl;
+  private final ConcurrentMap<String, Integer> m_schemaMap = new ConcurrentHashMap<>();
 }
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java
index 724b3d9..f94a86f 100644
--- a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java
@@ -5,6 +5,7 @@
 package edu.wpi.first.util.datalog;
 
 import edu.wpi.first.util.WPIUtilJNI;
+import java.nio.ByteBuffer;
 
 public class DataLogJNI extends WPIUtilJNI {
   static native long create(String dir, String filename, double period, String extraHeader);
@@ -17,6 +18,13 @@
 
   static native void resume(long impl);
 
+  static native void stop(long impl);
+
+  static native void addSchema(long impl, String name, String type, byte[] schema, long timestamp);
+
+  static native void addSchemaString(
+      long impl, String name, String type, String schema, long timestamp);
+
   static native int start(long impl, String name, String type, String metadata, long timestamp);
 
   static native void finish(long impl, int entry, long timestamp);
@@ -25,7 +33,30 @@
 
   static native void close(long impl);
 
-  static native void appendRaw(long impl, int entry, byte[] data, long timestamp);
+  static native void appendRaw(
+      long impl, int entry, byte[] data, int start, int len, long timestamp);
+
+  static void appendRaw(long impl, int entry, ByteBuffer data, int start, int len, long timestamp) {
+    if (data.isDirect()) {
+      if (start < 0) {
+        throw new IndexOutOfBoundsException("start must be >= 0");
+      }
+      if (len < 0) {
+        throw new IndexOutOfBoundsException("len must be >= 0");
+      }
+      if ((start + len) > data.capacity()) {
+        throw new IndexOutOfBoundsException("start + len must be smaller than buffer capacity");
+      }
+      appendRawBuffer(impl, entry, data, start, len, timestamp);
+    } else if (data.hasArray()) {
+      appendRaw(impl, entry, data.array(), data.arrayOffset() + start, len, timestamp);
+    } else {
+      throw new UnsupportedOperationException("ByteBuffer must be direct or have a backing array");
+    }
+  }
+
+  private static native void appendRawBuffer(
+      long impl, int entry, ByteBuffer data, int start, int len, long timestamp);
 
   static native void appendBoolean(long impl, int entry, boolean value, long timestamp);
 
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/ProtobufLogEntry.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/ProtobufLogEntry.java
new file mode 100644
index 0000000..1db2647
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/ProtobufLogEntry.java
@@ -0,0 +1,117 @@
+// 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.
+
+package edu.wpi.first.util.datalog;
+
+import edu.wpi.first.util.protobuf.Protobuf;
+import edu.wpi.first.util.protobuf.ProtobufBuffer;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import us.hebi.quickbuf.ProtoMessage;
+
+/**
+ * Log protobuf-encoded values.
+ *
+ * @param <T> value class
+ */
+public final class ProtobufLogEntry<T> extends DataLogEntry {
+  private ProtobufLogEntry(
+      DataLog log, String name, Protobuf<T, ?> proto, String metadata, long timestamp) {
+    super(log, name, proto.getTypeString(), metadata, timestamp);
+    m_buf = ProtobufBuffer.create(proto);
+    log.addSchema(proto, timestamp);
+  }
+
+  /**
+   * Creates a protobuf-encoded log entry.
+   *
+   * @param <T> value class (inferred from proto)
+   * @param <MessageType> protobuf message type (inferred from proto)
+   * @param log datalog
+   * @param name name of the entry
+   * @param proto protobuf serialization implementation
+   * @param metadata metadata
+   * @param timestamp entry creation timestamp (0=now)
+   * @return ProtobufLogEntry
+   */
+  public static <T, MessageType extends ProtoMessage<?>> ProtobufLogEntry<T> create(
+      DataLog log, String name, Protobuf<T, MessageType> proto, String metadata, long timestamp) {
+    return new ProtobufLogEntry<T>(log, name, proto, metadata, timestamp);
+  }
+
+  /**
+   * Creates a protobuf-encoded log entry.
+   *
+   * @param <T> value class (inferred from proto)
+   * @param <MessageType> protobuf message type (inferred from proto)
+   * @param log datalog
+   * @param name name of the entry
+   * @param proto protobuf serialization implementation
+   * @param metadata metadata
+   * @return ProtobufLogEntry
+   */
+  public static <T, MessageType extends ProtoMessage<?>> ProtobufLogEntry<T> create(
+      DataLog log, String name, Protobuf<T, MessageType> proto, String metadata) {
+    return create(log, name, proto, metadata, 0);
+  }
+
+  /**
+   * Creates a protobuf-encoded log entry.
+   *
+   * @param <T> value class (inferred from proto)
+   * @param <MessageType> protobuf message type (inferred from proto)
+   * @param log datalog
+   * @param name name of the entry
+   * @param proto protobuf serialization implementation
+   * @param timestamp entry creation timestamp (0=now)
+   * @return ProtobufLogEntry
+   */
+  public static <T, MessageType extends ProtoMessage<?>> ProtobufLogEntry<T> create(
+      DataLog log, String name, Protobuf<T, MessageType> proto, long timestamp) {
+    return create(log, name, proto, "", timestamp);
+  }
+
+  /**
+   * Creates a protobuf-encoded log entry.
+   *
+   * @param <T> value class (inferred from proto)
+   * @param <MessageType> protobuf message type (inferred from proto)
+   * @param log datalog
+   * @param name name of the entry
+   * @param proto protobuf serialization implementation
+   * @return ProtobufLogEntry
+   */
+  public static <T, MessageType extends ProtoMessage<?>> ProtobufLogEntry<T> create(
+      DataLog log, String name, Protobuf<T, MessageType> proto) {
+    return create(log, name, proto, 0);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Value to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void append(T value, long timestamp) {
+    try {
+      synchronized (m_buf) {
+        ByteBuffer bb = m_buf.write(value);
+        m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp);
+      }
+    } catch (IOException e) {
+      // ignore
+    }
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Value to record
+   */
+  public void append(T value) {
+    append(value, 0);
+  }
+
+  private final ProtobufBuffer<T, ?> m_buf;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java
index 160f734..972fc03 100644
--- a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java
@@ -4,6 +4,8 @@
 
 package edu.wpi.first.util.datalog;
 
+import java.nio.ByteBuffer;
+
 /** Log raw byte array values. */
 public class RawLogEntry extends DataLogEntry {
   public static final String kDataType = "raw";
@@ -35,7 +37,7 @@
   /**
    * Appends a record to the log.
    *
-   * @param value Value to record
+   * @param value Value to record; will send entire array contents
    * @param timestamp Time stamp (0 to indicate now)
    */
   public void append(byte[] value, long timestamp) {
@@ -45,9 +47,74 @@
   /**
    * Appends a record to the log.
    *
-   * @param value Value to record
+   * @param value Value to record; will send entire array contents
    */
   public void append(byte[] value) {
-    m_log.appendRaw(m_entry, value, 0);
+    append(value, 0);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Data to record
+   * @param start Start position of data (in byte array)
+   * @param len Length of data (must be less than or equal to value.length - offset)
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void append(byte[] value, int start, int len, long timestamp) {
+    m_log.appendRaw(m_entry, value, start, len, timestamp);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Data to record
+   * @param start Start position of data (in byte array)
+   * @param len Length of data (must be less than or equal to value.length - offset)
+   */
+  public void append(byte[] value, int start, int len) {
+    append(value, start, len, 0);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Data to record; will send from value.position() to value.capacity()
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void append(ByteBuffer value, long timestamp) {
+    m_log.appendRaw(m_entry, value, timestamp);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Data to record; will send from value.position() to value.capacity()
+   */
+  public void append(ByteBuffer value) {
+    append(value, 0);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Data to record
+   * @param start Start position of data (in value buffer)
+   * @param len Length of data (must be less than or equal to value.length - offset)
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void append(ByteBuffer value, int start, int len, long timestamp) {
+    m_log.appendRaw(m_entry, value, start, len, timestamp);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Data to record
+   * @param start Start position of data (in value buffer)
+   * @param len Length of data (must be less than or equal to value.length - offset)
+   */
+  public void append(ByteBuffer value, int start, int len) {
+    append(value, start, len, 0);
   }
 }
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java
new file mode 100644
index 0000000..0f6cb2e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java
@@ -0,0 +1,140 @@
+// 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.
+
+package edu.wpi.first.util.datalog;
+
+import edu.wpi.first.util.struct.Struct;
+import edu.wpi.first.util.struct.StructBuffer;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+
+/**
+ * Log struct-encoded array values.
+ *
+ * @param <T> value class
+ */
+public final class StructArrayLogEntry<T> extends DataLogEntry {
+  private StructArrayLogEntry(
+      DataLog log, String name, Struct<T> struct, String metadata, long timestamp) {
+    super(log, name, struct.getTypeString() + "[]", metadata, timestamp);
+    m_buf = StructBuffer.create(struct);
+    log.addSchema(struct, timestamp);
+  }
+
+  /**
+   * Creates a struct-encoded array log entry.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param log datalog
+   * @param name name of the entry
+   * @param struct struct serialization implementation
+   * @param metadata metadata
+   * @param timestamp entry creation timestamp (0=now)
+   * @return StructArrayLogEntry
+   */
+  public static <T> StructArrayLogEntry<T> create(
+      DataLog log, String name, Struct<T> struct, String metadata, long timestamp) {
+    return new StructArrayLogEntry<T>(log, name, struct, metadata, timestamp);
+  }
+
+  /**
+   * Creates a struct-encoded array log entry.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param log datalog
+   * @param name name of the entry
+   * @param struct struct serialization implementation
+   * @param metadata metadata
+   * @return StructArrayLogEntry
+   */
+  public static <T> StructArrayLogEntry<T> create(
+      DataLog log, String name, Struct<T> struct, String metadata) {
+    return create(log, name, struct, metadata, 0);
+  }
+
+  /**
+   * Creates a struct-encoded array log entry.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param log datalog
+   * @param name name of the entry
+   * @param struct struct serialization implementation
+   * @param timestamp entry creation timestamp (0=now)
+   * @return StructArrayLogEntry
+   */
+  public static <T> StructArrayLogEntry<T> create(
+      DataLog log, String name, Struct<T> struct, long timestamp) {
+    return create(log, name, struct, "", timestamp);
+  }
+
+  /**
+   * Creates a struct-encoded array log entry.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param log datalog
+   * @param name name of the entry
+   * @param struct struct serialization implementation
+   * @return StructArrayLogEntry
+   */
+  public static <T> StructArrayLogEntry<T> create(DataLog log, String name, Struct<T> struct) {
+    return create(log, name, struct, 0);
+  }
+
+  /**
+   * Ensures sufficient buffer space is available for the given number of elements.
+   *
+   * @param nelem number of elements
+   */
+  public void reserve(int nelem) {
+    synchronized (m_buf) {
+      m_buf.reserve(nelem);
+    }
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Value to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void append(T[] value, long timestamp) {
+    synchronized (this) {
+      ByteBuffer bb = m_buf.writeArray(value);
+      m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp);
+    }
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Value to record
+   */
+  public void append(T[] value) {
+    append(value, 0);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Value to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void append(Collection<T> value, long timestamp) {
+    synchronized (m_buf) {
+      ByteBuffer bb = m_buf.writeArray(value);
+      m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp);
+    }
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Value to record
+   */
+  public void append(Collection<T> value) {
+    append(value, 0);
+  }
+
+  private final StructBuffer<T> m_buf;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructLogEntry.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructLogEntry.java
new file mode 100644
index 0000000..a227c32
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructLogEntry.java
@@ -0,0 +1,106 @@
+// 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.
+
+package edu.wpi.first.util.datalog;
+
+import edu.wpi.first.util.struct.Struct;
+import edu.wpi.first.util.struct.StructBuffer;
+import java.nio.ByteBuffer;
+
+/**
+ * Log struct-encoded values.
+ *
+ * @param <T> value class
+ */
+public final class StructLogEntry<T> extends DataLogEntry {
+  private StructLogEntry(
+      DataLog log, String name, Struct<T> struct, String metadata, long timestamp) {
+    super(log, name, struct.getTypeString(), metadata, timestamp);
+    m_buf = StructBuffer.create(struct);
+    log.addSchema(struct, timestamp);
+  }
+
+  /**
+   * Creates a struct-encoded log entry.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param log datalog
+   * @param name name of the entry
+   * @param struct struct serialization implementation
+   * @param metadata metadata
+   * @param timestamp entry creation timestamp (0=now)
+   * @return StructLogEntry
+   */
+  public static <T> StructLogEntry<T> create(
+      DataLog log, String name, Struct<T> struct, String metadata, long timestamp) {
+    return new StructLogEntry<T>(log, name, struct, metadata, timestamp);
+  }
+
+  /**
+   * Creates a struct-encoded log entry.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param log datalog
+   * @param name name of the entry
+   * @param struct struct serialization implementation
+   * @param metadata metadata
+   * @return StructLogEntry
+   */
+  public static <T> StructLogEntry<T> create(
+      DataLog log, String name, Struct<T> struct, String metadata) {
+    return create(log, name, struct, metadata, 0);
+  }
+
+  /**
+   * Creates a struct-encoded log entry.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param log datalog
+   * @param name name of the entry
+   * @param struct struct serialization implementation
+   * @param timestamp entry creation timestamp (0=now)
+   * @return StructLogEntry
+   */
+  public static <T> StructLogEntry<T> create(
+      DataLog log, String name, Struct<T> struct, long timestamp) {
+    return create(log, name, struct, "", timestamp);
+  }
+
+  /**
+   * Creates a struct-encoded log entry.
+   *
+   * @param <T> value class (inferred from struct)
+   * @param log datalog
+   * @param name name of the entry
+   * @param struct struct serialization implementation
+   * @return StructLogEntry
+   */
+  public static <T> StructLogEntry<T> create(DataLog log, String name, Struct<T> struct) {
+    return create(log, name, struct, 0);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Value to record
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  public void append(T value, long timestamp) {
+    synchronized (m_buf) {
+      ByteBuffer bb = m_buf.write(value);
+      m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp);
+    }
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param value Value to record
+   */
+  public void append(T value) {
+    append(value, 0);
+  }
+
+  private final StructBuffer<T> m_buf;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/protobuf/Protobuf.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/protobuf/Protobuf.java
new file mode 100644
index 0000000..b77db7a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/protobuf/Protobuf.java
@@ -0,0 +1,121 @@
+// 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.
+
+package edu.wpi.first.util.protobuf;
+
+import java.util.function.BiConsumer;
+import java.util.function.Predicate;
+import us.hebi.quickbuf.Descriptors.Descriptor;
+import us.hebi.quickbuf.Descriptors.FileDescriptor;
+import us.hebi.quickbuf.ProtoMessage;
+
+/**
+ * Interface for Protobuf serialization.
+ *
+ * <p>This is designed for serialization of more complex data structures including forward/backwards
+ * compatibility and repeated/nested/variable length members, etc. Serialization and deserialization
+ * code is auto-generated from .proto interface descriptions (the MessageType generic parameter).
+ *
+ * <p>Idiomatically, classes that support protobuf serialization should provide a static final
+ * member named "proto" that provides an instance of an implementation of this interface.
+ *
+ * @param <T> object type
+ * @param <MessageType> protobuf message type
+ */
+public interface Protobuf<T, MessageType extends ProtoMessage<?>> {
+  /**
+   * Gets the Class object for the stored value.
+   *
+   * @return Class
+   */
+  Class<T> getTypeClass();
+
+  /**
+   * Gets the type string (e.g. for NetworkTables). This should be globally unique and start with
+   * "proto:".
+   *
+   * @return type string
+   */
+  default String getTypeString() {
+    return "proto:" + getDescriptor().getFullName();
+  }
+
+  /**
+   * Gets the protobuf descriptor.
+   *
+   * @return descriptor
+   */
+  Descriptor getDescriptor();
+
+  /**
+   * Gets the list of protobuf types referenced by this protobuf.
+   *
+   * @return list of protobuf types
+   */
+  default Protobuf<?, ?>[] getNested() {
+    return new Protobuf<?, ?>[] {};
+  }
+
+  /**
+   * Creates protobuf message.
+   *
+   * @return protobuf message
+   */
+  MessageType createMessage();
+
+  /**
+   * Deserializes an object from a protobuf message.
+   *
+   * @param msg protobuf message
+   * @return New object
+   */
+  T unpack(MessageType msg);
+
+  /**
+   * Copies the object contents into a protobuf message. Implementations should call either
+   * msg.setMember(member) or member.copyToProto(msg.getMutableMember()) for each member.
+   *
+   * @param msg protobuf message
+   * @param value object to serialize
+   */
+  void pack(MessageType msg, T value);
+
+  /**
+   * Updates the object contents from a protobuf message. Implementations should call
+   * msg.getMember(member), MemberClass.makeFromProto(msg.getMember()), or
+   * member.updateFromProto(msg.getMember()) for each member.
+   *
+   * <p>Immutable classes cannot and should not implement this function. The default implementation
+   * throws UnsupportedOperationException.
+   *
+   * @param out object to update
+   * @param msg protobuf message
+   * @throws UnsupportedOperationException if the object is immutable
+   */
+  default void unpackInto(T out, MessageType msg) {
+    throw new UnsupportedOperationException("object does not support unpackInto");
+  }
+
+  /**
+   * Loops over all protobuf descriptors including nested/referenced descriptors.
+   *
+   * @param exists function that returns false if fn should be called for the given type string
+   * @param fn function to call for each descriptor
+   */
+  default void forEachDescriptor(Predicate<String> exists, BiConsumer<String, byte[]> fn) {
+    forEachDescriptorImpl(getDescriptor().getFile(), exists, fn);
+  }
+
+  private static void forEachDescriptorImpl(
+      FileDescriptor desc, Predicate<String> exists, BiConsumer<String, byte[]> fn) {
+    String name = "proto:" + desc.getFullName();
+    if (exists.test(name)) {
+      return;
+    }
+    for (FileDescriptor dep : desc.getDependencies()) {
+      forEachDescriptorImpl(dep, exists, fn);
+    }
+    fn.accept(name, desc.toProtoBytes());
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java
new file mode 100644
index 0000000..af3e466
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java
@@ -0,0 +1,164 @@
+// 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.
+
+package edu.wpi.first.util.protobuf;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import us.hebi.quickbuf.ProtoMessage;
+import us.hebi.quickbuf.ProtoSink;
+import us.hebi.quickbuf.ProtoSource;
+
+/**
+ * Reusable buffer for serialization/deserialization to/from a protobuf.
+ *
+ * @param <T> object type
+ * @param <MessageType> protobuf message type
+ */
+public final class ProtobufBuffer<T, MessageType extends ProtoMessage<?>> {
+  private ProtobufBuffer(Protobuf<T, MessageType> proto) {
+    m_buf = ByteBuffer.allocateDirect(1024);
+    m_sink = ProtoSink.newDirectSink();
+    m_sink.setOutput(m_buf);
+    m_source = ProtoSource.newDirectSource();
+    m_msg = proto.createMessage();
+    m_proto = proto;
+  }
+
+  public static <T, MessageType extends ProtoMessage<?>> ProtobufBuffer<T, MessageType> create(
+      Protobuf<T, MessageType> proto) {
+    return new ProtobufBuffer<T, MessageType>(proto);
+  }
+
+  /**
+   * Gets the protobuf object of the stored type.
+   *
+   * @return protobuf object
+   */
+  public Protobuf<T, MessageType> getProto() {
+    return m_proto;
+  }
+
+  /**
+   * Gets the type string.
+   *
+   * @return type string
+   */
+  public String getTypeString() {
+    return m_proto.getTypeString();
+  }
+
+  /**
+   * Serializes a value to a ByteBuffer. The returned ByteBuffer is a direct byte buffer with the
+   * position set to the end of the serialized data.
+   *
+   * @param value value
+   * @return byte buffer
+   * @throws IOException if serialization failed
+   */
+  public ByteBuffer write(T value) throws IOException {
+    m_msg.clearQuick();
+    m_proto.pack(m_msg, value);
+    int size = m_msg.getSerializedSize();
+    if (size < m_buf.capacity()) {
+      m_buf = ByteBuffer.allocateDirect(size * 2);
+      m_sink.setOutput(m_buf);
+    }
+    m_sink.reset();
+    m_msg.writeTo(m_sink);
+    m_buf.position(m_sink.getTotalBytesWritten());
+    return m_buf;
+  }
+
+  /**
+   * Deserializes a value from a byte array, creating a new object.
+   *
+   * @param buf byte array
+   * @param start starting location within byte array
+   * @param len length of serialized data
+   * @return new object
+   * @throws IOException if deserialization failed
+   */
+  public T read(byte[] buf, int start, int len) throws IOException {
+    m_msg.clearQuick();
+    m_source.setInput(buf, start, len);
+    m_msg.mergeFrom(m_source);
+    return m_proto.unpack(m_msg);
+  }
+
+  /**
+   * Deserializes a value from a byte array, creating a new object.
+   *
+   * @param buf byte array
+   * @return new object
+   * @throws IOException if deserialization failed
+   */
+  public T read(byte[] buf) throws IOException {
+    return read(buf, 0, buf.length);
+  }
+
+  /**
+   * Deserializes a value from a ByteBuffer, creating a new object.
+   *
+   * @param buf byte buffer
+   * @return new object
+   * @throws IOException if deserialization failed
+   */
+  public T read(ByteBuffer buf) throws IOException {
+    m_msg.clearQuick();
+    m_source.setInput(buf);
+    m_msg.mergeFrom(m_source);
+    return m_proto.unpack(m_msg);
+  }
+
+  /**
+   * Deserializes a value from a byte array into a mutable object.
+   *
+   * @param out object (will be updated with deserialized contents)
+   * @param buf byte array
+   * @param start starting location within byte array
+   * @param len length of serialized data
+   * @throws IOException if deserialization failed
+   * @throws UnsupportedOperationException if the object is immutable
+   */
+  public void readInto(T out, byte[] buf, int start, int len) throws IOException {
+    m_msg.clearQuick();
+    m_source.setInput(buf, start, len);
+    m_msg.mergeFrom(m_source);
+    m_proto.unpackInto(out, m_msg);
+  }
+
+  /**
+   * Deserializes a value from a byte array into a mutable object.
+   *
+   * @param out object (will be updated with deserialized contents)
+   * @param buf byte array
+   * @throws IOException if deserialization failed
+   * @throws UnsupportedOperationException if the object is immutable
+   */
+  public void readInto(T out, byte[] buf) throws IOException {
+    readInto(out, buf, 0, buf.length);
+  }
+
+  /**
+   * Deserializes a value from a ByteBuffer into a mutable object.
+   *
+   * @param out object (will be updated with deserialized contents)
+   * @param buf byte buffer
+   * @throws IOException if deserialization failed
+   * @throws UnsupportedOperationException if the object is immutable
+   */
+  public void readInto(T out, ByteBuffer buf) throws IOException {
+    m_msg.clearQuick();
+    m_source.setInput(buf);
+    m_msg.mergeFrom(m_source);
+    m_proto.unpackInto(out, m_msg);
+  }
+
+  private ByteBuffer m_buf;
+  private final ProtoSink m_sink;
+  private final ProtoSource m_source;
+  private final MessageType m_msg;
+  private final Protobuf<T, MessageType> m_proto;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java
index a58c3a3..db822ce 100644
--- a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java
@@ -56,6 +56,14 @@
   void addBooleanProperty(String key, BooleanSupplier getter, BooleanConsumer setter);
 
   /**
+   * Add a constant boolean property.
+   *
+   * @param key property name
+   * @param value the value
+   */
+  void publishConstBoolean(String key, boolean value);
+
+  /**
    * Add an integer property.
    *
    * @param key property name
@@ -65,6 +73,14 @@
   void addIntegerProperty(String key, LongSupplier getter, LongConsumer setter);
 
   /**
+   * Add a constant integer property.
+   *
+   * @param key property name
+   * @param value the value
+   */
+  void publishConstInteger(String key, long value);
+
+  /**
    * Add a float property.
    *
    * @param key property name
@@ -74,6 +90,14 @@
   void addFloatProperty(String key, FloatSupplier getter, FloatConsumer setter);
 
   /**
+   * Add a constant float property.
+   *
+   * @param key property name
+   * @param value the value
+   */
+  void publishConstFloat(String key, float value);
+
+  /**
    * Add a double property.
    *
    * @param key property name
@@ -83,6 +107,14 @@
   void addDoubleProperty(String key, DoubleSupplier getter, DoubleConsumer setter);
 
   /**
+   * Add a constant double property.
+   *
+   * @param key property name
+   * @param value the value
+   */
+  void publishConstDouble(String key, double value);
+
+  /**
    * Add a string property.
    *
    * @param key property name
@@ -92,6 +124,14 @@
   void addStringProperty(String key, Supplier<String> getter, Consumer<String> setter);
 
   /**
+   * Add a constant string property.
+   *
+   * @param key property name
+   * @param value the value
+   */
+  void publishConstString(String key, String value);
+
+  /**
    * Add a boolean array property.
    *
    * @param key property name
@@ -101,6 +141,14 @@
   void addBooleanArrayProperty(String key, Supplier<boolean[]> getter, Consumer<boolean[]> setter);
 
   /**
+   * Add a constant boolean array property.
+   *
+   * @param key property name
+   * @param value the value
+   */
+  void publishConstBooleanArray(String key, boolean[] value);
+
+  /**
    * Add an integer array property.
    *
    * @param key property name
@@ -110,6 +158,14 @@
   void addIntegerArrayProperty(String key, Supplier<long[]> getter, Consumer<long[]> setter);
 
   /**
+   * Add a constant integer property.
+   *
+   * @param key property name
+   * @param value the value
+   */
+  void publishConstIntegerArray(String key, long[] value);
+
+  /**
    * Add a float array property.
    *
    * @param key property name
@@ -119,6 +175,14 @@
   void addFloatArrayProperty(String key, Supplier<float[]> getter, Consumer<float[]> setter);
 
   /**
+   * Add a constant float array property.
+   *
+   * @param key property name
+   * @param value the value
+   */
+  void publishConstFloatArray(String key, float[] value);
+
+  /**
    * Add a double array property.
    *
    * @param key property name
@@ -128,6 +192,14 @@
   void addDoubleArrayProperty(String key, Supplier<double[]> getter, Consumer<double[]> setter);
 
   /**
+   * Add a constant double array property.
+   *
+   * @param key property name
+   * @param value the value
+   */
+  void publishConstDoubleArray(String key, double[] value);
+
+  /**
    * Add a string array property.
    *
    * @param key property name
@@ -137,6 +209,14 @@
   void addStringArrayProperty(String key, Supplier<String[]> getter, Consumer<String[]> setter);
 
   /**
+   * Add a constant string array property.
+   *
+   * @param key property name
+   * @param value the value
+   */
+  void publishConstStringArray(String key, String[] value);
+
+  /**
    * Add a raw property.
    *
    * @param key property name
@@ -148,6 +228,15 @@
       String key, String typeString, Supplier<byte[]> getter, Consumer<byte[]> setter);
 
   /**
+   * Add a constant raw property.
+   *
+   * @param key property name
+   * @param typeString type string
+   * @param value the value
+   */
+  void publishConstRaw(String key, String typeString, byte[] value);
+
+  /**
    * Gets the kind of backend being used.
    *
    * @return Backend kind
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java
new file mode 100644
index 0000000..6ff2236
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java
@@ -0,0 +1,43 @@
+// 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.
+
+package edu.wpi.first.util.struct;
+
+public class BadSchemaException extends Exception {
+  private final String m_field;
+
+  public BadSchemaException(String s) {
+    super(s);
+    m_field = "";
+  }
+
+  public BadSchemaException(String message, Throwable cause) {
+    super(message, cause);
+    m_field = "";
+  }
+
+  public BadSchemaException(Throwable cause) {
+    super(cause);
+    m_field = "";
+  }
+
+  public BadSchemaException(String field, String s) {
+    super(s);
+    m_field = field;
+  }
+
+  public BadSchemaException(String field, String message, Throwable cause) {
+    super(message, cause);
+    m_field = field;
+  }
+
+  public String getField() {
+    return m_field;
+  }
+
+  @Override
+  public String toString() {
+    return m_field.isEmpty() ? getMessage() : "field " + m_field + ": " + getMessage();
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java
new file mode 100644
index 0000000..165f7db
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java
@@ -0,0 +1,632 @@
+// 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.
+
+package edu.wpi.first.util.struct;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ReadOnlyBufferException;
+import java.nio.charset.StandardCharsets;
+
+/** Dynamic (run-time) access to a serialized raw struct. */
+public final class DynamicStruct {
+  private DynamicStruct(StructDescriptor desc, ByteBuffer data) {
+    m_desc = desc;
+    m_data = data.order(ByteOrder.LITTLE_ENDIAN);
+  }
+
+  /**
+   * Constructs a new dynamic struct object with internal storage. The descriptor must be valid. The
+   * internal storage is allocated using ByteBuffer.allocate().
+   *
+   * @param desc struct descriptor
+   * @return dynamic struct object
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public static DynamicStruct allocate(StructDescriptor desc) {
+    return new DynamicStruct(desc, ByteBuffer.allocate(desc.getSize()));
+  }
+
+  /**
+   * Constructs a new dynamic struct object with internal storage. The descriptor must be valid. The
+   * internal storage is allocated using ByteBuffer.allocateDirect().
+   *
+   * @param desc struct descriptor
+   * @return dynamic struct object
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public static DynamicStruct allocateDirect(StructDescriptor desc) {
+    return new DynamicStruct(desc, ByteBuffer.allocateDirect(desc.getSize()));
+  }
+
+  /**
+   * Constructs a new dynamic struct object. Note: the passed data buffer is not copied.
+   * Modifications to the passed buffer will be reflected in the struct and vice-versa.
+   *
+   * @param desc struct descriptor
+   * @param data byte buffer containing serialized data starting at current position
+   * @return dynamic struct object
+   */
+  public static DynamicStruct wrap(StructDescriptor desc, ByteBuffer data) {
+    return new DynamicStruct(desc, data.slice());
+  }
+
+  /**
+   * Gets the struct descriptor.
+   *
+   * @return struct descriptor
+   */
+  public StructDescriptor getDescriptor() {
+    return m_desc;
+  }
+
+  /**
+   * Gets the serialized backing data buffer.
+   *
+   * @return data buffer
+   */
+  public ByteBuffer getBuffer() {
+    return m_data.duplicate().position(0);
+  }
+
+  /**
+   * Overwrites the entire serialized struct by copying data from a byte array.
+   *
+   * @param data replacement data for the struct
+   * @throws BufferUnderflowException if data is smaller than the struct size
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public void setData(byte[] data) {
+    if (data.length < m_desc.getSize()) {
+      throw new BufferUnderflowException();
+    }
+    m_data.position(0).put(data);
+  }
+
+  /**
+   * Overwrites the entire serialized struct by copying data from a byte buffer.
+   *
+   * @param data replacement data for the struct; copy starts from current position
+   * @throws BufferUnderflowException if remaining data is smaller than the struct size
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public void setData(ByteBuffer data) {
+    if (data.remaining() < m_desc.getSize()) {
+      throw new BufferUnderflowException();
+    }
+    int oldLimit = data.limit();
+    m_data.position(0).put(data.limit(m_desc.getSize()));
+    data.limit(oldLimit);
+  }
+
+  /**
+   * Gets a struct field descriptor by name.
+   *
+   * @param name field name
+   * @return field descriptor, or null if no field with that name exists
+   */
+  public StructFieldDescriptor findField(String name) {
+    return m_desc.findFieldByName(name);
+  }
+
+  /**
+   * Gets the value of a boolean field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return boolean field value
+   * @throws UnsupportedOperationException if field is not bool type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
+   */
+  public boolean getBoolField(StructFieldDescriptor field, int arrIndex) {
+    if (field.getType() != StructFieldType.kBool) {
+      throw new UnsupportedOperationException("field is not bool type");
+    }
+    return getFieldImpl(field, arrIndex) != 0;
+  }
+
+  /**
+   * Gets the value of a boolean field.
+   *
+   * @param field field descriptor
+   * @return boolean field value
+   * @throws UnsupportedOperationException if field is not bool type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public boolean getBoolField(StructFieldDescriptor field) {
+    return getBoolField(field, 0);
+  }
+
+  /**
+   * Sets the value of a boolean field.
+   *
+   * @param field field descriptor
+   * @param value boolean value
+   * @param arrIndex array index (must be less than field array size)
+   * @throws UnsupportedOperationException if field is not bool type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   */
+  public void setBoolField(StructFieldDescriptor field, boolean value, int arrIndex) {
+    if (field.getType() != StructFieldType.kBool) {
+      throw new UnsupportedOperationException("field is not bool type");
+    }
+    setFieldImpl(field, value ? 1 : 0, arrIndex);
+  }
+
+  /**
+   * Sets the value of a boolean field.
+   *
+   * @param field field descriptor
+   * @param value boolean value
+   * @throws UnsupportedOperationException if field is not bool type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   */
+  public void setBoolField(StructFieldDescriptor field, boolean value) {
+    setBoolField(field, value, 0);
+  }
+
+  /**
+   * Gets the value of an integer field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return integer field value
+   * @throws UnsupportedOperationException if field is not integer type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
+   */
+  public long getIntField(StructFieldDescriptor field, int arrIndex) {
+    if (!field.isInt() && !field.isUint()) {
+      throw new UnsupportedOperationException("field is not integer type");
+    }
+    return getFieldImpl(field, arrIndex);
+  }
+
+  /**
+   * Gets the value of an integer field.
+   *
+   * @param field field descriptor
+   * @return integer field value
+   * @throws UnsupportedOperationException if field is not integer type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public long getIntField(StructFieldDescriptor field) {
+    return getIntField(field, 0);
+  }
+
+  /**
+   * Sets the value of an integer field.
+   *
+   * @param field field descriptor
+   * @param value integer value
+   * @param arrIndex array index (must be less than field array size)
+   * @throws UnsupportedOperationException if field is not integer type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   */
+  public void setIntField(StructFieldDescriptor field, long value, int arrIndex) {
+    if (!field.isInt() && !field.isUint()) {
+      throw new UnsupportedOperationException("field is not integer type");
+    }
+    setFieldImpl(field, value, arrIndex);
+  }
+
+  /**
+   * Sets the value of an integer field.
+   *
+   * @param field field descriptor
+   * @param value integer value
+   * @throws UnsupportedOperationException if field is not integer type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   */
+  public void setIntField(StructFieldDescriptor field, long value) {
+    setIntField(field, value, 0);
+  }
+
+  /**
+   * Gets the value of a float field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return float field value
+   * @throws UnsupportedOperationException if field is not float type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
+   */
+  public float getFloatField(StructFieldDescriptor field, int arrIndex) {
+    if (field.getType() != StructFieldType.kFloat) {
+      throw new UnsupportedOperationException("field is not float type");
+    }
+    return Float.intBitsToFloat((int) getFieldImpl(field, arrIndex));
+  }
+
+  /**
+   * Gets the value of a float field.
+   *
+   * @param field field descriptor
+   * @return float field value
+   * @throws UnsupportedOperationException if field is not float type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public float getFloatField(StructFieldDescriptor field) {
+    return getFloatField(field, 0);
+  }
+
+  /**
+   * Sets the value of a float field.
+   *
+   * @param field field descriptor
+   * @param value float value
+   * @param arrIndex array index (must be less than field array size)
+   * @throws UnsupportedOperationException if field is not float type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   */
+  public void setFloatField(StructFieldDescriptor field, float value, int arrIndex) {
+    if (field.getType() != StructFieldType.kFloat) {
+      throw new UnsupportedOperationException("field is not float type");
+    }
+    setFieldImpl(field, Float.floatToIntBits(value), arrIndex);
+  }
+
+  /**
+   * Sets the value of a float field.
+   *
+   * @param field field descriptor
+   * @param value float value
+   * @throws UnsupportedOperationException if field is not float type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   */
+  public void setFloatField(StructFieldDescriptor field, float value) {
+    setFloatField(field, value, 0);
+  }
+
+  /**
+   * Gets the value of a double field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return double field value
+   * @throws UnsupportedOperationException if field is not double type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
+   */
+  public double getDoubleField(StructFieldDescriptor field, int arrIndex) {
+    if (field.getType() != StructFieldType.kDouble) {
+      throw new UnsupportedOperationException("field is not double type");
+    }
+    return Double.longBitsToDouble(getFieldImpl(field, arrIndex));
+  }
+
+  /**
+   * Gets the value of a double field.
+   *
+   * @param field field descriptor
+   * @return double field value
+   * @throws UnsupportedOperationException if field is not double type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public double getDoubleField(StructFieldDescriptor field) {
+    return getDoubleField(field, 0);
+  }
+
+  /**
+   * Sets the value of a double field.
+   *
+   * @param field field descriptor
+   * @param value double value
+   * @param arrIndex array index (must be less than field array size)
+   * @throws UnsupportedOperationException if field is not double type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   */
+  public void setDoubleField(StructFieldDescriptor field, double value, int arrIndex) {
+    if (field.getType() != StructFieldType.kDouble) {
+      throw new UnsupportedOperationException("field is not double type");
+    }
+    setFieldImpl(field, Double.doubleToLongBits(value), arrIndex);
+  }
+
+  /**
+   * Sets the value of a double field.
+   *
+   * @param field field descriptor
+   * @param value double value
+   * @throws UnsupportedOperationException if field is not double type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   */
+  public void setDoubleField(StructFieldDescriptor field, double value) {
+    setDoubleField(field, value, 0);
+  }
+
+  /**
+   * Gets the value of a character or character array field.
+   *
+   * @param field field descriptor
+   * @return field value
+   * @throws UnsupportedOperationException if field is not char type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public String getStringField(StructFieldDescriptor field) {
+    if (field.getType() != StructFieldType.kChar) {
+      throw new UnsupportedOperationException("field is not char type");
+    }
+    if (!field.getParent().equals(m_desc)) {
+      throw new IllegalArgumentException("field is not part of this struct");
+    }
+    if (!m_desc.isValid()) {
+      throw new IllegalStateException("struct descriptor is not valid");
+    }
+    byte[] bytes = new byte[field.m_arraySize];
+    m_data.position(field.m_offset).get(bytes, 0, field.m_arraySize);
+    return new String(bytes, StandardCharsets.UTF_8);
+  }
+
+  /**
+   * Sets the value of a character or character array field.
+   *
+   * @param field field descriptor
+   * @param value field value
+   * @throws UnsupportedOperationException if field is not char type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public void setStringField(StructFieldDescriptor field, String value) {
+    if (field.getType() != StructFieldType.kChar) {
+      throw new UnsupportedOperationException("field is not char type");
+    }
+    if (!field.getParent().equals(m_desc)) {
+      throw new IllegalArgumentException("field is not part of this struct");
+    }
+    if (!m_desc.isValid()) {
+      throw new IllegalStateException("struct descriptor is not valid");
+    }
+    ByteBuffer bb = StandardCharsets.UTF_8.encode(value);
+    int len = Math.min(bb.remaining(), field.m_arraySize);
+    m_data.position(field.m_offset).put(bb.limit(len));
+    for (int i = len; i < field.m_arraySize; i++) {
+      m_data.put((byte) 0);
+    }
+  }
+
+  /**
+   * Gets the value of a struct field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return field value
+   * @throws UnsupportedOperationException if field is not of struct type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
+   */
+  public DynamicStruct getStructField(StructFieldDescriptor field, int arrIndex) {
+    if (field.getType() != StructFieldType.kStruct) {
+      throw new UnsupportedOperationException("field is not struct type");
+    }
+    if (!field.getParent().equals(m_desc)) {
+      throw new IllegalArgumentException("field is not part of this struct");
+    }
+    if (!m_desc.isValid()) {
+      throw new IllegalStateException("struct descriptor is not valid");
+    }
+    if (arrIndex < 0 || arrIndex >= field.m_arraySize) {
+      throw new ArrayIndexOutOfBoundsException(
+          "arrIndex (" + arrIndex + ") is larger than array size (" + field.m_arraySize + ")");
+    }
+    StructDescriptor struct = field.getStruct();
+    return wrap(struct, m_data.position(field.m_offset + arrIndex * struct.m_size));
+  }
+
+  /**
+   * Gets the value of a struct field.
+   *
+   * @param field field descriptor
+   * @return field value
+   * @throws UnsupportedOperationException if field is not of struct type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   */
+  public DynamicStruct getStructField(StructFieldDescriptor field) {
+    return getStructField(field, 0);
+  }
+
+  /**
+   * Sets the value of a struct field.
+   *
+   * @param field field descriptor
+   * @param value struct value
+   * @param arrIndex array index (must be less than field array size)
+   * @throws UnsupportedOperationException if field is not struct type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   */
+  public void setStructField(StructFieldDescriptor field, DynamicStruct value, int arrIndex) {
+    if (field.getType() != StructFieldType.kStruct) {
+      throw new UnsupportedOperationException("field is not struct type");
+    }
+    if (!field.getParent().equals(m_desc)) {
+      throw new IllegalArgumentException("field is not part of this struct");
+    }
+    if (!m_desc.isValid()) {
+      throw new IllegalStateException("struct descriptor is not valid");
+    }
+    StructDescriptor struct = field.getStruct();
+    if (!value.getDescriptor().equals(struct)) {
+      throw new IllegalArgumentException("value's struct type does not match field struct type");
+    }
+    if (!value.getDescriptor().isValid()) {
+      throw new IllegalStateException("value's struct descriptor is not valid");
+    }
+    if (arrIndex < 0 || arrIndex >= field.m_arraySize) {
+      throw new ArrayIndexOutOfBoundsException(
+          "arrIndex (" + arrIndex + ") is larger than array size (" + field.m_arraySize + ")");
+    }
+    m_data
+        .position(field.m_offset + arrIndex * struct.m_size)
+        .put(value.m_data.position(0).limit(value.getDescriptor().getSize()));
+  }
+
+  /**
+   * Sets the value of a struct field.
+   *
+   * @param field field descriptor
+   * @param value struct value
+   * @throws UnsupportedOperationException if field is not struct type
+   * @throws IllegalArgumentException if field is not a member of this struct
+   * @throws IllegalStateException if struct descriptor is invalid
+   * @throws ReadOnlyBufferException if the underlying buffer is read-only
+   */
+  public void setStructField(StructFieldDescriptor field, DynamicStruct value) {
+    setStructField(field, value, 0);
+  }
+
+  private long getFieldImpl(StructFieldDescriptor field, int arrIndex) {
+    if (!field.getParent().equals(m_desc)) {
+      throw new IllegalArgumentException("field is not part of this struct");
+    }
+    if (!m_desc.isValid()) {
+      throw new IllegalStateException("struct descriptor is not valid");
+    }
+    if (arrIndex < 0 || arrIndex >= field.m_arraySize) {
+      throw new ArrayIndexOutOfBoundsException(
+          "arrIndex (" + arrIndex + ") is larger than array size (" + field.m_arraySize + ")");
+    }
+
+    long val;
+    switch (field.m_size) {
+      case 1:
+        val = m_data.get(field.m_offset + arrIndex);
+        break;
+      case 2:
+        val = m_data.getShort(field.m_offset + arrIndex * 2);
+        break;
+      case 4:
+        val = m_data.getInt(field.m_offset + arrIndex * 4);
+        break;
+      case 8:
+        val = m_data.getLong(field.m_offset + arrIndex * 8);
+        break;
+      default:
+        throw new IllegalStateException("invalid field size");
+    }
+
+    if (field.isUint() || field.getType() == StructFieldType.kBool) {
+      // for unsigned fields, we can simply logical shift and mask
+      return (val >>> field.m_bitShift) & field.getBitMask();
+    } else {
+      // to get sign extension, shift so the sign bit within the bitfield goes to the long's sign
+      // bit (also clearing all higher bits), then shift back down (also clearing all lower bits);
+      // since upper and lower bits are cleared with the shifts, the bitmask is unnecessary
+      return (val << (64 - field.m_bitShift - field.getBitWidth())) >> (64 - field.getBitWidth());
+    }
+  }
+
+  private void setFieldImpl(StructFieldDescriptor field, long value, int arrIndex) {
+    if (!field.getParent().equals(m_desc)) {
+      throw new IllegalArgumentException("field is not part of this struct");
+    }
+    if (!m_desc.isValid()) {
+      throw new IllegalStateException("struct descriptor is not valid");
+    }
+    if (arrIndex < 0 || arrIndex >= field.m_arraySize) {
+      throw new ArrayIndexOutOfBoundsException(
+          "arrIndex (" + arrIndex + ") is larger than array size (" + field.m_arraySize + ")");
+    }
+
+    // common case is no bit shift and no masking
+    if (!field.isBitField()) {
+      switch (field.m_size) {
+        case 1:
+          m_data.put(field.m_offset + arrIndex, (byte) value);
+          break;
+        case 2:
+          m_data.putShort(field.m_offset + arrIndex * 2, (short) value);
+          break;
+        case 4:
+          m_data.putInt(field.m_offset + arrIndex * 4, (int) value);
+          break;
+        case 8:
+          m_data.putLong(field.m_offset + arrIndex * 8, value);
+          break;
+        default:
+          throw new IllegalStateException("invalid field size");
+      }
+      return;
+    }
+
+    // handle bit shifting and masking into current value
+    switch (field.m_size) {
+      case 1:
+        {
+          byte val = m_data.get(field.m_offset + arrIndex);
+          val &= ~(field.getBitMask() << field.m_bitShift);
+          val |= (value & field.getBitMask()) << field.m_bitShift;
+          m_data.put(field.m_offset + arrIndex, val);
+          break;
+        }
+      case 2:
+        {
+          short val = m_data.getShort(field.m_offset + arrIndex * 2);
+          val &= ~(field.getBitMask() << field.m_bitShift);
+          val |= (value & field.getBitMask()) << field.m_bitShift;
+          m_data.putShort(field.m_offset + arrIndex * 2, val);
+          break;
+        }
+      case 4:
+        {
+          int val = m_data.getInt(field.m_offset + arrIndex * 4);
+          val &= ~(field.getBitMask() << field.m_bitShift);
+          val |= (value & field.getBitMask()) << field.m_bitShift;
+          m_data.putInt(field.m_offset + arrIndex * 4, val);
+          break;
+        }
+      case 8:
+        {
+          long val = m_data.getLong(field.m_offset + arrIndex * 8);
+          val &= ~(field.getBitMask() << field.m_bitShift);
+          val |= (value & field.getBitMask()) << field.m_bitShift;
+          m_data.putLong(field.m_offset + arrIndex * 8, val);
+          break;
+        }
+      default:
+        throw new IllegalStateException("invalid field size");
+    }
+  }
+
+  private final StructDescriptor m_desc;
+  private final ByteBuffer m_data;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/Struct.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/Struct.java
new file mode 100644
index 0000000..630ef8c
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/Struct.java
@@ -0,0 +1,116 @@
+// 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.
+
+package edu.wpi.first.util.struct;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Interface for raw struct serialization.
+ *
+ * <p>This is designed for serializing small fixed-size data structures in the fastest and most
+ * compact means possible. Serialization consists of making relative put() calls to a ByteBuffer and
+ * deserialization consists of making relative get() calls from a ByteBuffer.
+ *
+ * <p>Idiomatically, classes that support raw struct serialization should provide a static final
+ * member named "struct" that provides an instance of an implementation of this interface.
+ *
+ * @param <T> object type
+ */
+public interface Struct<T> {
+  /** Serialized size of a "bool" value. */
+  int kSizeBool = 1;
+
+  /** Serialized size of an "int8" or "uint8" value. */
+  int kSizeInt8 = 1;
+
+  /** Serialized size of an "int16" or "uint16" value. */
+  int kSizeInt16 = 2;
+
+  /** Serialized size of an "int32" or "uint32" value. */
+  int kSizeInt32 = 4;
+
+  /** Serialized size of an "int64" or "uint64" value. */
+  int kSizeInt64 = 8;
+
+  /** Serialized size of an "float" or "float32" value. */
+  int kSizeFloat = 4;
+
+  /** Serialized size of an "double" or "float64" value. */
+  int kSizeDouble = 8;
+
+  /**
+   * Gets the Class object for the stored value.
+   *
+   * @return Class
+   */
+  Class<T> getTypeClass();
+
+  /**
+   * Gets the type string (e.g. for NetworkTables). This should be globally unique and start with
+   * "struct:".
+   *
+   * @return type string
+   */
+  String getTypeString();
+
+  /**
+   * Gets the serialized size (in bytes). This should always be a constant.
+   *
+   * @return serialized size
+   */
+  int getSize();
+
+  /**
+   * Gets the schema.
+   *
+   * @return schema
+   */
+  String getSchema();
+
+  /**
+   * Gets the list of struct types referenced by this struct.
+   *
+   * @return list of struct types
+   */
+  default Struct<?>[] getNested() {
+    return new Struct<?>[] {};
+  }
+
+  /**
+   * Deserializes an object from a raw struct serialized ByteBuffer starting at the current
+   * position. Will increment the ByteBuffer position by getStructSize() bytes. Will not otherwise
+   * modify the ByteBuffer (e.g. byte order will not be changed).
+   *
+   * @param bb ByteBuffer
+   * @return New object
+   */
+  T unpack(ByteBuffer bb);
+
+  /**
+   * Puts object contents to a ByteBuffer starting at the current position. Will increment the
+   * ByteBuffer position by getStructSize() bytes. Will not otherwise modify the ByteBuffer (e.g.
+   * byte order will not be changed).
+   *
+   * @param bb ByteBuffer
+   * @param value object to serialize
+   */
+  void pack(ByteBuffer bb, T value);
+
+  /**
+   * Updates object contents from a raw struct serialized ByteBuffer starting at the current
+   * position. Will increment the ByteBuffer position by getStructSize() bytes. Will not otherwise
+   * modify the ByteBuffer (e.g. byte order will not be changed).
+   *
+   * <p>Immutable classes cannot and should not implement this function. The default implementation
+   * throws UnsupportedOperationException.
+   *
+   * @param out object to update
+   * @param bb ByteBuffer
+   * @throws UnsupportedOperationException if the object is immutable
+   */
+  default void unpackInto(T out, ByteBuffer bb) {
+    throw new UnsupportedOperationException("object does not support unpackInto");
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java
new file mode 100644
index 0000000..0e8aa18
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java
@@ -0,0 +1,224 @@
+// 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.
+
+package edu.wpi.first.util.struct;
+
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Collection;
+
+/**
+ * Reusable buffer for serialization/deserialization to/from a raw struct.
+ *
+ * @param <T> object type
+ */
+public final class StructBuffer<T> {
+  private StructBuffer(Struct<T> struct) {
+    m_structSize = struct.getSize();
+    m_buf = ByteBuffer.allocateDirect(m_structSize).order(ByteOrder.LITTLE_ENDIAN);
+    m_struct = struct;
+  }
+
+  public static <T> StructBuffer<T> create(Struct<T> struct) {
+    return new StructBuffer<T>(struct);
+  }
+
+  /**
+   * Gets the struct object of the stored type.
+   *
+   * @return struct object
+   */
+  public Struct<T> getStruct() {
+    return m_struct;
+  }
+
+  /**
+   * Gets the type string.
+   *
+   * @return type string
+   */
+  public String getTypeString() {
+    return m_struct.getTypeString();
+  }
+
+  /**
+   * Ensures sufficient buffer space is available for the given number of elements.
+   *
+   * @param nelem number of elements
+   */
+  public void reserve(int nelem) {
+    if ((nelem * m_structSize) > m_buf.capacity()) {
+      m_buf = ByteBuffer.allocateDirect(nelem * m_structSize).order(ByteOrder.LITTLE_ENDIAN);
+    }
+  }
+
+  /**
+   * Serializes a value to a ByteBuffer. The returned ByteBuffer is a direct byte buffer with the
+   * position set to the end of the serialized data.
+   *
+   * @param value value
+   * @return byte buffer
+   */
+  public ByteBuffer write(T value) {
+    m_buf.position(0);
+    m_struct.pack(m_buf, value);
+    return m_buf;
+  }
+
+  /**
+   * Deserializes a value from a byte array, creating a new object.
+   *
+   * @param buf byte array
+   * @param start starting location within byte array
+   * @param len length of serialized data
+   * @return new object
+   */
+  public T read(byte[] buf, int start, int len) {
+    return read(ByteBuffer.wrap(buf, start, len));
+  }
+
+  /**
+   * Deserializes a value from a byte array, creating a new object.
+   *
+   * @param buf byte array
+   * @return new object
+   */
+  public T read(byte[] buf) {
+    return read(buf, 0, buf.length);
+  }
+
+  /**
+   * Deserializes a value from a ByteBuffer, creating a new object.
+   *
+   * @param buf byte buffer
+   * @return new object
+   */
+  public T read(ByteBuffer buf) {
+    buf.order(ByteOrder.LITTLE_ENDIAN);
+    return m_struct.unpack(buf);
+  }
+
+  /**
+   * Deserializes a value from a byte array into a mutable object.
+   *
+   * @param out object (will be updated with deserialized contents)
+   * @param buf byte array
+   * @param start starting location within byte array
+   * @param len length of serialized data
+   * @throws UnsupportedOperationException if T is immutable
+   */
+  public void readInto(T out, byte[] buf, int start, int len) {
+    readInto(out, ByteBuffer.wrap(buf, start, len));
+  }
+
+  /**
+   * Deserializes a value from a byte array into a mutable object.
+   *
+   * @param out object (will be updated with deserialized contents)
+   * @param buf byte array
+   * @throws UnsupportedOperationException if T is immutable
+   */
+  public void readInto(T out, byte[] buf) {
+    readInto(out, buf, 0, buf.length);
+  }
+
+  /**
+   * Deserializes a value from a ByteBuffer into a mutable object.
+   *
+   * @param out object (will be updated with deserialized contents)
+   * @param buf byte buffer
+   * @throws UnsupportedOperationException if T is immutable
+   */
+  public void readInto(T out, ByteBuffer buf) {
+    m_struct.unpackInto(out, buf);
+  }
+
+  /**
+   * Serializes a collection of values to a ByteBuffer. The returned ByteBuffer is a direct byte
+   * buffer with the position set to the end of the serialized data.
+   *
+   * @param values values
+   * @return byte buffer
+   */
+  public ByteBuffer writeArray(Collection<T> values) {
+    m_buf.position(0);
+    if ((values.size() * m_structSize) > m_buf.capacity()) {
+      m_buf =
+          ByteBuffer.allocateDirect(values.size() * m_structSize * 2)
+              .order(ByteOrder.LITTLE_ENDIAN);
+    }
+    for (T v : values) {
+      m_struct.pack(m_buf, v);
+    }
+    return m_buf;
+  }
+
+  /**
+   * Serializes an array of values to a ByteBuffer. The returned ByteBuffer is a direct byte buffer
+   * with the position set to the end of the serialized data.
+   *
+   * @param values values
+   * @return byte buffer
+   */
+  public ByteBuffer writeArray(T[] values) {
+    m_buf.position(0);
+    if ((values.length * m_structSize) > m_buf.capacity()) {
+      m_buf =
+          ByteBuffer.allocateDirect(values.length * m_structSize * 2)
+              .order(ByteOrder.LITTLE_ENDIAN);
+    }
+    for (T v : values) {
+      m_struct.pack(m_buf, v);
+    }
+    return m_buf;
+  }
+
+  /**
+   * Deserializes an array of values from a byte array, creating an array of new objects.
+   *
+   * @param buf byte array
+   * @param start starting location within byte array
+   * @param len length of serialized data
+   * @return new object array
+   */
+  public T[] readArray(byte[] buf, int start, int len) {
+    return readArray(ByteBuffer.wrap(buf, start, len));
+  }
+
+  /**
+   * Deserializes an array of values from a byte array, creating an array of new objects.
+   *
+   * @param buf byte array
+   * @return new object array
+   */
+  public T[] readArray(byte[] buf) {
+    return readArray(buf, 0, buf.length);
+  }
+
+  /**
+   * Deserializes an array of values from a ByteBuffer, creating an array of new objects.
+   *
+   * @param buf byte buffer
+   * @return new object array
+   */
+  public T[] readArray(ByteBuffer buf) {
+    buf.order(ByteOrder.LITTLE_ENDIAN);
+    int len = buf.limit() - buf.position();
+    if ((len % m_structSize) != 0) {
+      throw new RuntimeException("buffer size not a multiple of struct size");
+    }
+    int nelem = len / m_structSize;
+    @SuppressWarnings("unchecked")
+    T[] arr = (T[]) Array.newInstance(m_struct.getTypeClass(), nelem);
+    for (int i = 0; i < nelem; i++) {
+      arr[i] = m_struct.unpack(buf);
+    }
+    return arr;
+  }
+
+  private ByteBuffer m_buf;
+  private final Struct<T> m_struct;
+  private final int m_structSize;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptor.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptor.java
new file mode 100644
index 0000000..438db43
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptor.java
@@ -0,0 +1,156 @@
+// 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.
+
+package edu.wpi.first.util.struct;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+/** Raw struct dynamic struct descriptor. */
+public class StructDescriptor {
+  StructDescriptor(String name) {
+    m_name = name;
+  }
+
+  /**
+   * Gets the struct name.
+   *
+   * @return name
+   */
+  public String getName() {
+    return m_name;
+  }
+
+  /**
+   * Gets the struct schema.
+   *
+   * @return schema
+   */
+  public String getSchema() {
+    return m_schema;
+  }
+
+  /**
+   * Returns whether the struct is valid (e.g. the struct is fully defined and field offsets
+   * computed).
+   *
+   * @return true if valid
+   */
+  public boolean isValid() {
+    return m_valid;
+  }
+
+  /**
+   * Returns the struct size, in bytes. Not valid unless IsValid() is true.
+   *
+   * @return size in bytes
+   * @throws IllegalStateException if descriptor is invalid
+   */
+  public int getSize() {
+    if (!m_valid) {
+      throw new IllegalStateException("descriptor is invalid");
+    }
+    return m_size;
+  }
+
+  /**
+   * Gets a field descriptor by name. Note the field cannot be accessed until the struct is valid.
+   *
+   * @param name field name
+   * @return field descriptor, or nullptr if not found
+   */
+  public StructFieldDescriptor findFieldByName(String name) {
+    return m_fieldsByName.get(name);
+  }
+
+  /**
+   * Gets all field descriptors. Note fields cannot be accessed until the struct is valid.
+   *
+   * @return field descriptors
+   */
+  public List<StructFieldDescriptor> getFields() {
+    return m_fields;
+  }
+
+  boolean checkCircular(Stack<StructDescriptor> stack) {
+    stack.push(this);
+    for (StructDescriptor ref : m_references) {
+      if (stack.contains(ref)) {
+        return false;
+      }
+      if (!ref.checkCircular(stack)) {
+        return false;
+      }
+    }
+    stack.pop();
+    return true;
+  }
+
+  void calculateOffsets(Stack<StructDescriptor> stack) {
+    int offset = 0;
+    int shift = 0;
+    int prevBitfieldSize = 0;
+    for (StructFieldDescriptor field : m_fields) {
+      if (!field.isBitField()) {
+        shift = 0; // reset shift on non-bitfield element
+        offset += prevBitfieldSize; // finish bitfield if active
+        prevBitfieldSize = 0; // previous is now not bitfield
+        field.m_offset = offset;
+        StructDescriptor struct = field.getStruct();
+        if (struct != null) {
+          if (!struct.isValid()) {
+            m_valid = false;
+            return;
+          }
+          field.m_size = struct.m_size;
+        }
+        offset += field.m_size * field.m_arraySize;
+      } else {
+        int bitWidth = field.getBitWidth();
+        if (field.getType() == StructFieldType.kBool
+            && prevBitfieldSize != 0
+            && (shift + 1) <= (prevBitfieldSize * 8)) {
+          // bool takes on size of preceding bitfield type (if it fits)
+          field.m_size = prevBitfieldSize;
+        } else if (field.m_size != prevBitfieldSize || (shift + bitWidth) > (field.m_size * 8)) {
+          shift = 0;
+          offset += prevBitfieldSize;
+        }
+        prevBitfieldSize = field.m_size;
+        field.m_offset = offset;
+        field.m_bitShift = shift;
+        shift += bitWidth;
+      }
+    }
+
+    // update struct size
+    m_size = offset + prevBitfieldSize;
+    m_valid = true;
+
+    // now that we're valid, referring types may be too
+    stack.push(this);
+    for (StructDescriptor ref : m_references) {
+      if (stack.contains(ref)) {
+        throw new IllegalStateException(
+            "internal error (inconsistent data): circular struct reference between "
+                + m_name
+                + " and "
+                + ref.m_name);
+      }
+      ref.calculateOffsets(stack);
+    }
+    stack.pop();
+  }
+
+  private final String m_name;
+  String m_schema;
+  final List<StructDescriptor> m_references = new ArrayList<>();
+  final List<StructFieldDescriptor> m_fields = new ArrayList<>();
+  final Map<String, StructFieldDescriptor> m_fieldsByName = new HashMap<>();
+  int m_size;
+  boolean m_valid;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java
new file mode 100644
index 0000000..be7343f
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java
@@ -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.
+
+package edu.wpi.first.util.struct;
+
+import edu.wpi.first.util.struct.parser.ParseException;
+import edu.wpi.first.util.struct.parser.ParsedDeclaration;
+import edu.wpi.first.util.struct.parser.ParsedSchema;
+import edu.wpi.first.util.struct.parser.Parser;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+/** Database of raw struct dynamic descriptors. */
+public class StructDescriptorDatabase {
+  /**
+   * Adds a structure schema to the database. If the struct references other structs that have not
+   * yet been added, it will not be valid until those structs are also added.
+   *
+   * @param name structure name
+   * @param schema structure schema
+   * @return Added struct dynamic descriptor
+   * @throws BadSchemaException if schema invalid
+   */
+  public StructDescriptor add(String name, String schema) throws BadSchemaException {
+    Parser parser = new Parser(schema);
+    ParsedSchema parsed;
+    try {
+      parsed = parser.parse();
+    } catch (ParseException e) {
+      throw new BadSchemaException("parse error", e);
+    }
+
+    // turn parsed schema into descriptors
+    StructDescriptor theStruct = m_structs.computeIfAbsent(name, k -> new StructDescriptor(k));
+    theStruct.m_schema = schema;
+    theStruct.m_fields.clear();
+    boolean isValid = true;
+    for (ParsedDeclaration decl : parsed.declarations) {
+      StructFieldType type = StructFieldType.fromString(decl.typeString);
+      int size = type.size;
+
+      // bitfield checks
+      if (decl.bitWidth != 0) {
+        // only integer or boolean types are allowed
+        if (!type.isInt && !type.isUint && type != StructFieldType.kBool) {
+          throw new BadSchemaException(
+              decl.name, "type " + decl.typeString + " cannot be bitfield");
+        }
+
+        // bit width cannot be larger than field size
+        if (decl.bitWidth > (size * 8)) {
+          throw new BadSchemaException(
+              decl.name, "bit width " + decl.bitWidth + " exceeds type size");
+        }
+
+        // bit width must be 1 for booleans
+        if (type == StructFieldType.kBool && decl.bitWidth != 1) {
+          throw new BadSchemaException(decl.name, "bit width must be 1 for bool type");
+        }
+
+        // cannot combine array and bitfield (shouldn't parse, but double-check)
+        if (decl.arraySize > 1) {
+          throw new BadSchemaException(decl.name, "cannot combine array and bitfield");
+        }
+      }
+
+      // struct handling
+      StructDescriptor structDesc = null;
+      if (type == StructFieldType.kStruct) {
+        // recursive definitions are not allowed
+        if (decl.typeString.equals(name)) {
+          throw new BadSchemaException(decl.name, "recursive struct reference");
+        }
+
+        // cross-reference struct, creating a placeholder if necessary
+        StructDescriptor aStruct =
+            m_structs.computeIfAbsent(decl.typeString, k -> new StructDescriptor(k));
+
+        // if the struct isn't valid, we can't be valid either
+        if (aStruct.isValid()) {
+          size = aStruct.getSize();
+        } else {
+          isValid = false;
+        }
+
+        // add to cross-references for when the struct does become valid
+        aStruct.m_references.add(theStruct);
+        structDesc = aStruct;
+      }
+
+      // create field
+      StructFieldDescriptor fieldDesc =
+          new StructFieldDescriptor(
+              theStruct,
+              decl.name,
+              type,
+              size,
+              decl.arraySize,
+              decl.bitWidth,
+              decl.enumValues,
+              structDesc);
+      if (theStruct.m_fieldsByName.put(decl.name, fieldDesc) != null) {
+        throw new BadSchemaException(decl.name, "duplicate field name");
+      }
+
+      theStruct.m_fields.add(fieldDesc);
+    }
+
+    theStruct.m_valid = isValid;
+    Stack<StructDescriptor> stack = new Stack<>();
+    if (isValid) {
+      // we have all the info needed, so calculate field offset & shift
+      theStruct.calculateOffsets(stack);
+    } else {
+      // check for circular reference
+      if (!theStruct.checkCircular(stack)) {
+        StringBuilder builder = new StringBuilder();
+        builder.append("circular struct reference: ");
+        boolean first = true;
+        for (StructDescriptor elem : stack) {
+          if (!first) {
+            builder.append(" <- ");
+          } else {
+            first = false;
+          }
+          builder.append(elem.getName());
+        }
+        throw new BadSchemaException(builder.toString());
+      }
+    }
+
+    return theStruct;
+  }
+
+  /**
+   * Finds a structure in the database by name.
+   *
+   * @param name structure name
+   * @return struct descriptor, or null if not found
+   */
+  public StructDescriptor find(String name) {
+    return m_structs.get(name);
+  }
+
+  private final Map<String, StructDescriptor> m_structs = new HashMap<>();
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldDescriptor.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldDescriptor.java
new file mode 100644
index 0000000..25a69a5
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldDescriptor.java
@@ -0,0 +1,241 @@
+// 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.
+
+package edu.wpi.first.util.struct;
+
+import java.util.Map;
+
+/** Raw struct dynamic field descriptor. */
+public class StructFieldDescriptor {
+  private static int toBitWidth(int size, int bitWidth) {
+    if (bitWidth == 0) {
+      return size * 8;
+    } else {
+      return bitWidth;
+    }
+  }
+
+  private static long toBitMask(int size, int bitWidth) {
+    if (size == 0) {
+      return 0;
+    } else {
+      return -1L >>> (64 - toBitWidth(size, bitWidth));
+    }
+  }
+
+  // does not fill in offset, shift
+  StructFieldDescriptor(
+      StructDescriptor parent,
+      String name,
+      StructFieldType type,
+      int size,
+      int arraySize,
+      int bitWidth,
+      Map<String, Long> enumValues,
+      StructDescriptor structDesc) {
+    m_parent = parent;
+    m_name = name;
+    m_size = size;
+    m_arraySize = arraySize;
+    m_enum = enumValues;
+    m_struct = structDesc;
+    m_bitMask = toBitMask(size, bitWidth);
+    m_type = type;
+    m_bitWidth = toBitWidth(size, bitWidth);
+  }
+
+  /**
+   * Gets the dynamic struct this field is contained in.
+   *
+   * @return struct descriptor
+   */
+  public StructDescriptor getParent() {
+    return m_parent;
+  }
+
+  /**
+   * Gets the field name.
+   *
+   * @return field name
+   */
+  public String getName() {
+    return m_name;
+  }
+
+  /**
+   * Gets the field type.
+   *
+   * @return field type
+   */
+  public StructFieldType getType() {
+    return m_type;
+  }
+
+  /**
+   * Returns whether the field type is a signed integer.
+   *
+   * @return true if signed integer, false otherwise
+   */
+  public boolean isInt() {
+    return m_type.isInt;
+  }
+
+  /**
+   * Returns whether the field type is an unsigned integer.
+   *
+   * @return true if unsigned integer, false otherwise
+   */
+  public boolean isUint() {
+    return m_type.isUint;
+  }
+
+  /**
+   * Gets the underlying storage size of the field, in bytes.
+   *
+   * @return number of bytes
+   */
+  public int getSize() {
+    return m_size;
+  }
+
+  /**
+   * Gets the storage offset of the field, in bytes.
+   *
+   * @return number of bytes from the start of the struct
+   */
+  public int getOffset() {
+    return m_offset;
+  }
+
+  /**
+   * Gets the bit width of the field, in bits.
+   *
+   * @return number of bits
+   */
+  public int getBitWidth() {
+    return m_bitWidth == 0 ? m_size * 8 : m_bitWidth;
+  }
+
+  /**
+   * Gets the bit mask for the field. The mask is always the least significant bits (it is not
+   * shifted).
+   *
+   * @return bit mask
+   */
+  public long getBitMask() {
+    return m_bitMask;
+  }
+
+  /**
+   * Gets the bit shift for the field (LSB=0).
+   *
+   * @return number of bits
+   */
+  public int getBitShift() {
+    return m_bitShift;
+  }
+
+  /**
+   * Returns whether the field is an array.
+   *
+   * @return true if array
+   */
+  public boolean isArray() {
+    return m_arraySize > 1;
+  }
+
+  /**
+   * Gets the array size. Returns 1 if non-array.
+   *
+   * @return number of elements
+   */
+  public int getArraySize() {
+    return m_arraySize;
+  }
+
+  /**
+   * Returns whether the field has enumerated values.
+   *
+   * @return true if there are enumerated values
+   */
+  public boolean hasEnum() {
+    return m_enum != null;
+  }
+
+  /**
+   * Gets the enumerated values.
+   *
+   * @return set of enumerated values
+   */
+  public Map<String, Long> getEnumValues() {
+    return m_enum;
+  }
+
+  /**
+   * Gets the struct descriptor for a struct data type.
+   *
+   * @return struct descriptor; returns null for non-struct
+   */
+  public StructDescriptor getStruct() {
+    return m_struct;
+  }
+
+  /**
+   * Gets the minimum unsigned integer value that can be stored in this field.
+   *
+   * @return minimum value
+   */
+  public long getUintMin() {
+    return 0;
+  }
+
+  /**
+   * Gets the maximum unsigned integer value that can be stored in this field. Note this is not the
+   * actual maximum for uint64 (due to Java lacking support for 64-bit unsigned integers).
+   *
+   * @return maximum value
+   */
+  public long getUintMax() {
+    return m_bitMask;
+  }
+
+  /**
+   * Gets the minimum signed integer value that can be stored in this field.
+   *
+   * @return minimum value
+   */
+  public long getIntMin() {
+    return (-(m_bitMask >> 1)) - 1;
+  }
+
+  /**
+   * Gets the maximum signed integer value that can be stored in this field.
+   *
+   * @return maximum value
+   */
+  public long getIntMax() {
+    return m_bitMask >> 1;
+  }
+
+  /**
+   * Returns whether the field is a bitfield.
+   *
+   * @return true if bitfield
+   */
+  public boolean isBitField() {
+    return m_bitShift != 0 || m_bitWidth != (m_size * 8);
+  }
+
+  private final StructDescriptor m_parent;
+  private final String m_name;
+  int m_size;
+  int m_offset;
+  final int m_arraySize; // 1 for non-arrays
+  private final Map<String, Long> m_enum;
+  private final StructDescriptor m_struct; // null for non-structs
+  private final long m_bitMask;
+  private final StructFieldType m_type;
+  private final int m_bitWidth;
+  int m_bitShift;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java
new file mode 100644
index 0000000..28d5d8e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java
@@ -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.
+
+package edu.wpi.first.util.struct;
+
+/** Known data types for raw struct dynamic fields (see StructFieldDescriptor). */
+public enum StructFieldType {
+  kBool("bool", false, false, 1),
+  kChar("char", false, false, 1),
+  kInt8("int8", true, false, 1),
+  kInt16("int16", true, false, 2),
+  kInt32("int32", true, false, 4),
+  kInt64("int64", true, false, 8),
+  kUint8("uint8", false, true, 1),
+  kUint16("uint16", false, true, 2),
+  kUint32("uint32", false, true, 4),
+  kUint64("uint64", false, true, 8),
+  kFloat("float", false, false, 4),
+  kDouble("double", false, false, 8),
+  kStruct("struct", false, false, 0);
+
+  @SuppressWarnings("MemberName")
+  public final String name;
+
+  @SuppressWarnings("MemberName")
+  public final boolean isInt;
+
+  @SuppressWarnings("MemberName")
+  public final boolean isUint;
+
+  @SuppressWarnings("MemberName")
+  public final int size;
+
+  StructFieldType(String name, boolean isInt, boolean isUint, int size) {
+    this.name = name;
+    this.isInt = isInt;
+    this.isUint = isUint;
+    this.size = size;
+  }
+
+  @Override
+  public String toString() {
+    return name;
+  }
+
+  /**
+   * Get field type from string.
+   *
+   * @param str string
+   * @return field type
+   */
+  public static StructFieldType fromString(String str) {
+    for (StructFieldType type : StructFieldType.values()) {
+      if (type.name.equals(str)) {
+        return type;
+      }
+    }
+    if ("float32".equals(str)) {
+      return kFloat;
+    } else if ("float64".equals(str)) {
+      return kDouble;
+    } else {
+      return kStruct;
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/Lexer.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/Lexer.java
new file mode 100644
index 0000000..6e3cd5d
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/Lexer.java
@@ -0,0 +1,132 @@
+// 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.
+
+package edu.wpi.first.util.struct.parser;
+
+/** Raw struct schema lexer. */
+public class Lexer {
+  /**
+   * Construct a raw struct schema lexer.
+   *
+   * @param in schema
+   */
+  public Lexer(String in) {
+    m_in = in;
+  }
+
+  /**
+   * Gets the next token.
+   *
+   * @return Token kind; the token text can be retrieved using getTokenText()
+   */
+  public TokenKind scan() {
+    // skip whitespace
+    do {
+      get();
+    } while (m_current == ' ' || m_current == '\t' || m_current == '\n' || m_current == '\r');
+    m_tokenStart = m_pos - 1;
+
+    switch (m_current) {
+      case '[':
+        return TokenKind.kLeftBracket;
+      case ']':
+        return TokenKind.kRightBracket;
+      case '{':
+        return TokenKind.kLeftBrace;
+      case '}':
+        return TokenKind.kRightBrace;
+      case ':':
+        return TokenKind.kColon;
+      case ';':
+        return TokenKind.kSemicolon;
+      case ',':
+        return TokenKind.kComma;
+      case '=':
+        return TokenKind.kEquals;
+      case '-':
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+        return scanInteger();
+      case '\0':
+        return TokenKind.kEndOfInput;
+      default:
+        if (Character.isLetter(m_current) || m_current == '_') {
+          return scanIdentifier();
+        }
+        return TokenKind.kUnknown;
+    }
+  }
+
+  /**
+   * Gets the text of the last lexed token.
+   *
+   * @return token text
+   */
+  public String getTokenText() {
+    if (m_tokenStart >= m_in.length()) {
+      return "";
+    }
+    return m_in.substring(m_tokenStart, m_pos);
+  }
+
+  /**
+   * Gets the starting position of the last lexed token.
+   *
+   * @return position (0 = first character)
+   */
+  public int getPosition() {
+    return m_tokenStart;
+  }
+
+  private TokenKind scanInteger() {
+    do {
+      get();
+    } while (Character.isDigit(m_current));
+    unget();
+    return TokenKind.kInteger;
+  }
+
+  private TokenKind scanIdentifier() {
+    do {
+      get();
+    } while (Character.isLetterOrDigit(m_current) || m_current == '_');
+    unget();
+    return TokenKind.kIdentifier;
+  }
+
+  private void get() {
+    if (m_pos < m_in.length()) {
+      m_current = m_in.charAt(m_pos);
+    } else {
+      m_current = '\0';
+    }
+    ++m_pos;
+  }
+
+  private void unget() {
+    if (m_pos > 0) {
+      m_pos--;
+      if (m_pos < m_in.length()) {
+        m_current = m_in.charAt(m_pos);
+      } else {
+        m_current = '\0';
+      }
+    } else {
+      m_current = '\0';
+    }
+  }
+
+  final String m_in;
+  char m_current;
+  int m_tokenStart;
+  int m_pos;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java
new file mode 100644
index 0000000..9fa843c
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java
@@ -0,0 +1,33 @@
+// 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.
+
+package edu.wpi.first.util.struct.parser;
+
+public class ParseException extends Exception {
+  private final int m_pos;
+
+  public ParseException(int pos, String s) {
+    super(s);
+    m_pos = pos;
+  }
+
+  public ParseException(int pos, String message, Throwable cause) {
+    super(message, cause);
+    m_pos = pos;
+  }
+
+  public ParseException(int pos, Throwable cause) {
+    super(cause);
+    m_pos = pos;
+  }
+
+  public int getPosition() {
+    return m_pos;
+  }
+
+  @Override
+  public String toString() {
+    return m_pos + ": " + getMessage();
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java
new file mode 100644
index 0000000..8184ae5
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java
@@ -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.
+
+package edu.wpi.first.util.struct.parser;
+
+import java.util.Map;
+
+/** Raw struct schema declaration. */
+public class ParsedDeclaration {
+  @SuppressWarnings("MemberName")
+  public String typeString;
+
+  @SuppressWarnings("MemberName")
+  public String name;
+
+  @SuppressWarnings("MemberName")
+  public Map<String, Long> enumValues;
+
+  @SuppressWarnings("MemberName")
+  public int arraySize = 1;
+
+  @SuppressWarnings("MemberName")
+  public int bitWidth;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java
new file mode 100644
index 0000000..2ca1753
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java
@@ -0,0 +1,14 @@
+// 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.
+
+package edu.wpi.first.util.struct.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Raw struct schema. */
+public class ParsedSchema {
+  @SuppressWarnings("MemberName")
+  public List<ParsedDeclaration> declarations = new ArrayList<>();
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/Parser.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/Parser.java
new file mode 100644
index 0000000..7aba1ad
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/Parser.java
@@ -0,0 +1,157 @@
+// 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.
+
+package edu.wpi.first.util.struct.parser;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Raw struct schema parser. */
+public class Parser {
+  /**
+   * Construct a raw struct schema parser.
+   *
+   * @param in schema
+   */
+  public Parser(String in) {
+    m_lexer = new Lexer(in);
+  }
+
+  /**
+   * Parses the schema.
+   *
+   * @return parsed schema object
+   * @throws ParseException on parse error
+   */
+  public ParsedSchema parse() throws ParseException {
+    ParsedSchema schema = new ParsedSchema();
+    do {
+      getNextToken();
+      if (m_token == TokenKind.kSemicolon) {
+        continue;
+      }
+      if (m_token == TokenKind.kEndOfInput) {
+        break;
+      }
+      schema.declarations.add(parseDeclaration());
+    } while (m_token != TokenKind.kEndOfInput);
+    return schema;
+  }
+
+  private ParsedDeclaration parseDeclaration() throws ParseException {
+    ParsedDeclaration decl = new ParsedDeclaration();
+
+    // optional enum specification
+    if (m_token == TokenKind.kIdentifier && "enum".equals(m_lexer.getTokenText())) {
+      getNextToken();
+      expect(TokenKind.kLeftBrace);
+      decl.enumValues = parseEnum();
+      getNextToken();
+    } else if (m_token == TokenKind.kLeftBrace) {
+      decl.enumValues = parseEnum();
+      getNextToken();
+    }
+
+    // type name
+    expect(TokenKind.kIdentifier);
+    decl.typeString = m_lexer.getTokenText();
+    getNextToken();
+
+    // identifier name
+    expect(TokenKind.kIdentifier);
+    decl.name = m_lexer.getTokenText();
+    getNextToken();
+
+    // array or bit field
+    if (m_token == TokenKind.kLeftBracket) {
+      getNextToken();
+      expect(TokenKind.kInteger);
+      String valueStr = m_lexer.getTokenText();
+      int value;
+      try {
+        value = Integer.parseInt(valueStr);
+      } catch (NumberFormatException e) {
+        value = 0;
+      }
+      if (value > 0) {
+        decl.arraySize = value;
+      } else {
+        throw new ParseException(
+            m_lexer.m_pos, "array size '" + valueStr + "' is not a positive integer");
+      }
+      getNextToken();
+      expect(TokenKind.kRightBracket);
+      getNextToken();
+    } else if (m_token == TokenKind.kColon) {
+      getNextToken();
+      expect(TokenKind.kInteger);
+      String valueStr = m_lexer.getTokenText();
+      int value;
+      try {
+        value = Integer.parseInt(valueStr);
+      } catch (NumberFormatException e) {
+        value = 0;
+      }
+      if (value > 0) {
+        decl.bitWidth = value;
+      } else {
+        throw new ParseException(
+            m_lexer.m_pos, "bitfield width '" + valueStr + "' is not a positive integer");
+      }
+      getNextToken();
+    }
+
+    // declaration must end with EOF or semicolon
+    if (m_token != TokenKind.kEndOfInput) {
+      expect(TokenKind.kSemicolon);
+    }
+
+    return decl;
+  }
+
+  private Map<String, Long> parseEnum() throws ParseException {
+    Map<String, Long> map = new HashMap<>();
+
+    // we start with current = '{'
+    getNextToken();
+    while (m_token != TokenKind.kRightBrace) {
+      expect(TokenKind.kIdentifier);
+      final String name = m_lexer.getTokenText();
+      getNextToken();
+      expect(TokenKind.kEquals);
+      getNextToken();
+      expect(TokenKind.kInteger);
+      String valueStr = m_lexer.getTokenText();
+      long value;
+      try {
+        value = Long.parseLong(valueStr);
+      } catch (NumberFormatException e) {
+        throw new ParseException(m_lexer.m_pos, "could not parse enum value '" + valueStr + "'");
+      }
+      map.put(name, value);
+      getNextToken();
+      if (m_token == TokenKind.kRightBrace) {
+        break;
+      }
+      expect(TokenKind.kComma);
+      getNextToken();
+    }
+    return map;
+  }
+
+  private TokenKind getNextToken() {
+    m_token = m_lexer.scan();
+    return m_token;
+  }
+
+  private void expect(TokenKind kind) throws ParseException {
+    if (m_token != kind) {
+      throw new ParseException(
+          m_lexer.m_pos, "expected " + kind + ", got '" + m_lexer.getTokenText() + "'");
+    }
+  }
+
+  final Lexer m_lexer;
+  TokenKind m_token;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java
new file mode 100644
index 0000000..85afa4a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java
@@ -0,0 +1,32 @@
+// 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.
+
+package edu.wpi.first.util.struct.parser;
+
+/** A lexed raw struct schema token. */
+public enum TokenKind {
+  kUnknown("unknown"),
+  kInteger("integer"),
+  kIdentifier("identifier"),
+  kLeftBracket("'['"),
+  kRightBracket("']'"),
+  kLeftBrace("'{'"),
+  kRightBrace("'}'"),
+  kColon("':'"),
+  kSemicolon("';'"),
+  kComma("','"),
+  kEquals("'='"),
+  kEndOfInput("<EOF>");
+
+  private final String m_name;
+
+  TokenKind(String name) {
+    this.m_name = name;
+  }
+
+  @Override
+  public String toString() {
+    return m_name;
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/DataLog.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/DataLog.cpp
index 7009628..d05a49e 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/cpp/DataLog.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/DataLog.cpp
@@ -26,17 +26,37 @@
 #include <random>
 #include <vector>
 
-#include "fmt/format.h"
+#include <fmt/format.h>
+
 #include "wpi/Endian.h"
 #include "wpi/Logger.h"
 #include "wpi/MathExtras.h"
+#include "wpi/SmallString.h"
 #include "wpi/fs.h"
 #include "wpi/timestamp.h"
 
 using namespace wpi::log;
 
 static constexpr size_t kBlockSize = 16 * 1024;
+static constexpr size_t kMaxBufferCount = 1024 * 1024 / kBlockSize;
+static constexpr size_t kMaxFreeCount = 256 * 1024 / kBlockSize;
 static constexpr size_t kRecordMaxHeaderSize = 17;
+static constexpr uintmax_t kMinFreeSpace = 5 * 1024 * 1024;
+
+static std::string FormatBytesSize(uintmax_t value) {
+  static constexpr uintmax_t kKiB = 1024;
+  static constexpr uintmax_t kMiB = kKiB * 1024;
+  static constexpr uintmax_t kGiB = kMiB * 1024;
+  if (value >= kGiB) {
+    return fmt::format("{:.1f} GiB", static_cast<double>(value) / kGiB);
+  } else if (value >= kMiB) {
+    return fmt::format("{:.1f} MiB", static_cast<double>(value) / kMiB);
+  } else if (value >= kKiB) {
+    return fmt::format("{:.1f} KiB", static_cast<double>(value) / kKiB);
+  } else {
+    return fmt::format("{} B", value);
+  }
+}
 
 template <typename T>
 static unsigned int WriteVarInt(uint8_t* buf, T val) {
@@ -159,7 +179,7 @@
 DataLog::~DataLog() {
   {
     std::scoped_lock lock{m_mutex};
-    m_active = false;
+    m_state = kShutdown;
     m_doFlush = true;
   }
   m_cond.notify_all();
@@ -184,12 +204,56 @@
 
 void DataLog::Pause() {
   std::scoped_lock lock{m_mutex};
-  m_paused = true;
+  m_state = kPaused;
 }
 
 void DataLog::Resume() {
   std::scoped_lock lock{m_mutex};
-  m_paused = false;
+  if (m_state == kPaused) {
+    m_state = kActive;
+  } else if (m_state == kStopped) {
+    m_state = kStart;
+  }
+}
+
+void DataLog::Stop() {
+  {
+    std::scoped_lock lock{m_mutex};
+    m_state = kStopped;
+    m_newFilename.clear();
+  }
+  m_cond.notify_all();
+}
+
+bool DataLog::HasSchema(std::string_view name) const {
+  std::scoped_lock lock{m_mutex};
+  wpi::SmallString<128> fullName{"/.schema/"};
+  fullName += name;
+  auto it = m_entries.find(fullName);
+  return it != m_entries.end();
+}
+
+void DataLog::AddSchema(std::string_view name, std::string_view type,
+                        std::span<const uint8_t> schema, int64_t timestamp) {
+  std::scoped_lock lock{m_mutex};
+  wpi::SmallString<128> fullName{"/.schema/"};
+  fullName += name;
+  auto& entryInfo = m_entries[fullName];
+  if (entryInfo.id != 0) {
+    return;  // don't add duplicates
+  }
+  entryInfo.schemaData.assign(schema.begin(), schema.end());
+  int entry = StartImpl(fullName, type, {}, timestamp);
+
+  // inline AppendRaw() without releasing lock
+  if (entry <= 0) {
+    [[unlikely]] return;  // should never happen, but check anyway
+  }
+  if (m_state != kActive && m_state != kPaused) {
+    [[unlikely]] return;
+  }
+  StartRecord(entry, timestamp, schema.size(), 0);
+  AppendImpl(schema);
 }
 
 static void WriteToFile(fs::file_t f, std::span<const uint8_t> data,
@@ -236,93 +300,201 @@
   return filename;
 }
 
-void DataLog::WriterThreadMain(std::string_view dir) {
-  std::chrono::duration<double> periodTime{m_period};
+struct DataLog::WriterThreadState {
+  explicit WriterThreadState(std::string_view dir) : dirPath{dir} {}
+  WriterThreadState(const WriterThreadState&) = delete;
+  WriterThreadState& operator=(const WriterThreadState&) = delete;
+  ~WriterThreadState() { Close(); }
 
-  std::error_code ec;
-  fs::path dirPath{dir};
-  std::string filename;
-
-  {
-    std::scoped_lock lock{m_mutex};
-    filename = std::move(m_newFilename);
-    m_newFilename.clear();
-  }
-
-  if (filename.empty()) {
-    filename = MakeRandomFilename();
-  }
-
-  // try preferred filename, or randomize it a few times, before giving up
-  fs::file_t f;
-  for (int i = 0; i < 5; ++i) {
-    // open file for append
-#ifdef _WIN32
-    // WIN32 doesn't allow combination of CreateNew and Append
-    f = fs::OpenFileForWrite(dirPath / filename, ec, fs::CD_CreateNew,
-                             fs::OF_None);
-#else
-    f = fs::OpenFileForWrite(dirPath / filename, ec, fs::CD_CreateNew,
-                             fs::OF_Append);
-#endif
-    if (ec) {
-      WPI_ERROR(m_msglog, "Could not open log file '{}': {}",
-                (dirPath / filename).string(), ec.message());
-      // try again with random filename
-      filename = MakeRandomFilename();
-    } else {
-      break;
+  void Close() {
+    if (f != fs::kInvalidFile) {
+      fs::CloseFile(f);
+      f = fs::kInvalidFile;
     }
   }
 
-  if (f == fs::kInvalidFile) {
-    WPI_ERROR(m_msglog, "Could not open log file, no log being saved");
+  void SetFilename(std::string_view fn) {
+    baseFilename = fn;
+    filename = fn;
+    path = dirPath / filename;
+    segmentCount = 1;
+  }
+
+  void IncrementFilename() {
+    fs::path basePath{baseFilename};
+    filename = fmt::format("{}.{}{}", basePath.stem().string(), ++segmentCount,
+                           basePath.extension().string());
+    path = dirPath / filename;
+  }
+
+  fs::path dirPath;
+  std::string baseFilename;
+  std::string filename;
+  fs::path path;
+  fs::file_t f = fs::kInvalidFile;
+  uintmax_t freeSpace = UINTMAX_MAX;
+  int segmentCount = 1;
+};
+
+void DataLog::StartLogFile(WriterThreadState& state) {
+  std::error_code ec;
+
+  if (state.filename.empty()) {
+    state.SetFilename(MakeRandomFilename());
+  }
+
+  // get free space
+  auto freeSpaceInfo = fs::space(state.dirPath, ec);
+  if (!ec) {
+    state.freeSpace = freeSpaceInfo.available;
   } else {
-    WPI_INFO(m_msglog, "Logging to '{}'", (dirPath / filename).string());
+    state.freeSpace = UINTMAX_MAX;
+  }
+  if (state.freeSpace < kMinFreeSpace) {
+    WPI_ERROR(m_msglog,
+              "Insufficient free space ({} available), no log being saved",
+              FormatBytesSize(state.freeSpace));
+  } else {
+    // try preferred filename, or randomize it a few times, before giving up
+    for (int i = 0; i < 5; ++i) {
+      // open file for append
+#ifdef _WIN32
+      // WIN32 doesn't allow combination of CreateNew and Append
+      state.f =
+          fs::OpenFileForWrite(state.path, ec, fs::CD_CreateNew, fs::OF_None);
+#else
+      state.f =
+          fs::OpenFileForWrite(state.path, ec, fs::CD_CreateNew, fs::OF_Append);
+#endif
+      if (ec) {
+        WPI_ERROR(m_msglog, "Could not open log file '{}': {}",
+                  state.path.string(), ec.message());
+        // try again with random filename
+        state.SetFilename(MakeRandomFilename());
+      } else {
+        break;
+      }
+    }
+
+    if (state.f == fs::kInvalidFile) {
+      WPI_ERROR(m_msglog, "Could not open log file, no log being saved");
+    } else {
+      WPI_INFO(m_msglog, "Logging to '{}' ({} free space)", state.path.string(),
+               FormatBytesSize(state.freeSpace));
+    }
   }
 
   // write header (version 1.0)
-  if (f != fs::kInvalidFile) {
+  if (state.f != fs::kInvalidFile) {
     const uint8_t header[] = {'W', 'P', 'I', 'L', 'O', 'G', 0, 1};
-    WriteToFile(f, header, filename, m_msglog);
+    WriteToFile(state.f, header, state.filename, m_msglog);
     uint8_t extraLen[4];
     support::endian::write32le(extraLen, m_extraHeader.size());
-    WriteToFile(f, extraLen, filename, m_msglog);
+    WriteToFile(state.f, extraLen, state.filename, m_msglog);
     if (m_extraHeader.size() > 0) {
-      WriteToFile(f,
+      WriteToFile(state.f,
                   {reinterpret_cast<const uint8_t*>(m_extraHeader.data()),
                    m_extraHeader.size()},
-                  filename, m_msglog);
+                  state.filename, m_msglog);
     }
   }
+}
 
+void DataLog::WriterThreadMain(std::string_view dir) {
+  std::chrono::duration<double> periodTime{m_period};
+
+  WriterThreadState state{dir};
+  {
+    std::scoped_lock lock{m_mutex};
+    state.SetFilename(m_newFilename);
+    m_newFilename.clear();
+  }
+  StartLogFile(state);
+
+  std::error_code ec;
   std::vector<Buffer> toWrite;
+  int freeSpaceCount = 0;
+  int checkExistCount = 0;
+  bool blocked = false;
+  uintmax_t written = 0;
 
   std::unique_lock lock{m_mutex};
-  while (m_active) {
+  while (m_state != kShutdown) {
     bool doFlush = false;
     auto timeoutTime = std::chrono::steady_clock::now() + periodTime;
     if (m_cond.wait_until(lock, timeoutTime) == std::cv_status::timeout) {
       doFlush = true;
     }
 
-    if (!m_newFilename.empty()) {
+    if (m_state == kStopped) {
+      state.Close();
+      continue;
+    }
+
+    bool doStart = false;
+
+    // if file was deleted, recreate it with the same name
+    if (++checkExistCount >= 10) {
+      checkExistCount = 0;
+      lock.unlock();
+      bool exists = fs::exists(state.path, ec);
+      lock.lock();
+      if (!ec && !exists) {
+        state.Close();
+        state.IncrementFilename();
+        WPI_INFO(m_msglog, "Log file deleted, recreating as fresh log '{}'",
+                 state.filename);
+        doStart = true;
+      }
+    }
+
+    // start new file if file exceeds 1.8 GB
+    if (written > 1800000000ull) {
+      state.Close();
+      state.IncrementFilename();
+      WPI_INFO(m_msglog, "Log file reached 1.8 GB, starting new file '{}'",
+               state.filename);
+      doStart = true;
+    }
+
+    if (m_state == kStart || doStart) {
+      lock.unlock();
+      StartLogFile(state);
+      lock.lock();
+      if (state.f != fs::kInvalidFile) {
+        // Emit start and schema data records
+        for (auto&& entryInfo : m_entries) {
+          AppendStartRecord(entryInfo.second.id, entryInfo.first(),
+                            entryInfo.second.type,
+                            m_entryIds[entryInfo.second.id].metadata, 0);
+          if (!entryInfo.second.schemaData.empty()) {
+            StartRecord(entryInfo.second.id, 0,
+                        entryInfo.second.schemaData.size(), 0);
+            AppendImpl(entryInfo.second.schemaData);
+          }
+        }
+      }
+      m_state = kActive;
+      written = 0;
+    }
+
+    if (!m_newFilename.empty() && state.f != fs::kInvalidFile) {
       auto newFilename = std::move(m_newFilename);
       m_newFilename.clear();
-      lock.unlock();
       // rename
-      if (filename != newFilename) {
-        fs::rename(dirPath / filename, dirPath / newFilename, ec);
+      if (state.filename != newFilename) {
+        lock.unlock();
+        fs::rename(state.path, state.dirPath / newFilename, ec);
+        lock.lock();
       }
       if (ec) {
         WPI_ERROR(m_msglog, "Could not rename log file from '{}' to '{}': {}",
-                  filename, newFilename, ec.message());
+                  state.filename, newFilename, ec.message());
       } else {
-        WPI_INFO(m_msglog, "Renamed log file from '{}' to '{}'", filename,
+        WPI_INFO(m_msglog, "Renamed log file from '{}' to '{}'", state.filename,
                  newFilename);
       }
-      filename = std::move(newFilename);
-      lock.lock();
+      state.SetFilename(newFilename);
     }
 
     if (doFlush || m_doFlush) {
@@ -334,34 +506,58 @@
       // swap outgoing with empty vector
       toWrite.swap(m_outgoing);
 
-      if (f != fs::kInvalidFile) {
+      if (state.f != fs::kInvalidFile && !blocked) {
         lock.unlock();
+
+        // update free space every 10 flushes (in case other things are writing)
+        if (++freeSpaceCount >= 10) {
+          freeSpaceCount = 0;
+          auto freeSpaceInfo = fs::space(state.dirPath, ec);
+          if (!ec) {
+            state.freeSpace = freeSpaceInfo.available;
+          } else {
+            state.freeSpace = UINTMAX_MAX;
+          }
+        }
+
         // write buffers to file
         for (auto&& buf : toWrite) {
-          WriteToFile(f, buf.GetData(), filename, m_msglog);
+          // stop writing when we go below the minimum free space
+          state.freeSpace -= buf.GetData().size();
+          written += buf.GetData().size();
+          if (state.freeSpace < kMinFreeSpace) {
+            [[unlikely]] WPI_ERROR(
+                m_msglog,
+                "Stopped logging due to low free space ({} available)",
+                FormatBytesSize(state.freeSpace));
+            blocked = true;
+            break;
+          }
+          WriteToFile(state.f, buf.GetData(), state.filename, m_msglog);
         }
 
         // sync to storage
 #if defined(__linux__)
-        ::fdatasync(f);
+        ::fdatasync(state.f);
 #elif defined(__APPLE__)
-        ::fsync(f);
+        ::fsync(state.f);
 #endif
         lock.lock();
+        if (blocked) {
+          [[unlikely]] m_state = kPaused;
+        }
       }
 
       // release buffers back to free list
       for (auto&& buf : toWrite) {
         buf.Clear();
-        m_free.emplace_back(std::move(buf));
+        if (m_free.size() < kMaxFreeCount) {
+          [[likely]] m_free.emplace_back(std::move(buf));
+        }
       }
       toWrite.resize(0);
     }
   }
-
-  if (f != fs::kInvalidFile) {
-    fs::CloseFile(f);
-  }
 }
 
 void DataLog::WriterThreadMain(
@@ -384,7 +580,7 @@
   std::vector<Buffer> toWrite;
 
   std::unique_lock lock{m_mutex};
-  while (m_active) {
+  while (m_state != kShutdown) {
     bool doFlush = false;
     auto timeoutTime = std::chrono::steady_clock::now() + periodTime;
     if (m_cond.wait_until(lock, timeoutTime) == std::cv_status::timeout) {
@@ -412,7 +608,9 @@
       // release buffers back to free list
       for (auto&& buf : toWrite) {
         buf.Clear();
-        m_free.emplace_back(std::move(buf));
+        if (m_free.size() < kMaxFreeCount) {
+          [[likely]] m_free.emplace_back(std::move(buf));
+        }
       }
       toWrite.resize(0);
     }
@@ -429,13 +627,18 @@
 int DataLog::Start(std::string_view name, std::string_view type,
                    std::string_view metadata, int64_t timestamp) {
   std::scoped_lock lock{m_mutex};
+  return StartImpl(name, type, metadata, timestamp);
+}
+
+int DataLog::StartImpl(std::string_view name, std::string_view type,
+                       std::string_view metadata, int64_t timestamp) {
   auto& entryInfo = m_entries[name];
   if (entryInfo.id == 0) {
     entryInfo.id = ++m_lastId;
   }
-  auto& savedCount = m_entryCounts[entryInfo.id];
-  ++savedCount;
-  if (savedCount > 1) {
+  auto& entryInfo2 = m_entryIds[entryInfo.id];
+  ++entryInfo2.count;
+  if (entryInfo2.count > 1) {
     if (entryInfo.type != type) {
       WPI_ERROR(m_msglog,
                 "type mismatch for '{}': was '{}', requested '{}'; ignoring",
@@ -445,15 +648,26 @@
     return entryInfo.id;
   }
   entryInfo.type = type;
+  entryInfo2.metadata = metadata;
+
+  if (m_state != kActive && m_state != kPaused) {
+    [[unlikely]] return entryInfo.id;
+  }
+
+  AppendStartRecord(entryInfo.id, name, type, metadata, timestamp);
+  return entryInfo.id;
+}
+
+void DataLog::AppendStartRecord(int id, std::string_view name,
+                                std::string_view type,
+                                std::string_view metadata, int64_t timestamp) {
   size_t strsize = name.size() + type.size() + metadata.size();
   uint8_t* buf = StartRecord(0, timestamp, 5 + 12 + strsize, 5);
   *buf++ = impl::kControlStart;
-  wpi::support::endian::write32le(buf, entryInfo.id);
+  wpi::support::endian::write32le(buf, id);
   AppendStringImpl(name);
   AppendStringImpl(type);
   AppendStringImpl(metadata);
-
-  return entryInfo.id;
 }
 
 void DataLog::Finish(int entry, int64_t timestamp) {
@@ -461,15 +675,18 @@
     return;
   }
   std::scoped_lock lock{m_mutex};
-  auto& savedCount = m_entryCounts[entry];
-  if (savedCount == 0) {
+  auto& entryInfo2 = m_entryIds[entry];
+  if (entryInfo2.count == 0) {
     return;
   }
-  --savedCount;
-  if (savedCount != 0) {
+  --entryInfo2.count;
+  if (entryInfo2.count != 0) {
     return;
   }
-  m_entryCounts.erase(entry);
+  m_entryIds.erase(entry);
+  if (m_state != kActive && m_state != kPaused) {
+    [[unlikely]] return;
+  }
   uint8_t* buf = StartRecord(0, timestamp, 5, 5);
   *buf++ = impl::kControlFinish;
   wpi::support::endian::write32le(buf, entry);
@@ -481,6 +698,10 @@
     return;
   }
   std::scoped_lock lock{m_mutex};
+  m_entryIds[entry].metadata = metadata;
+  if (m_state != kActive && m_state != kPaused) {
+    [[unlikely]] return;
+  }
   uint8_t* buf = StartRecord(0, timestamp, 5 + 4 + metadata.size(), 5);
   *buf++ = impl::kControlSetMetadata;
   wpi::support::endian::write32le(buf, entry);
@@ -491,6 +712,13 @@
   assert(size <= kBlockSize);
   if (m_outgoing.empty() || size > m_outgoing.back().GetRemaining()) {
     if (m_free.empty()) {
+      if (m_outgoing.size() >= kMaxBufferCount) {
+        [[unlikely]] WPI_ERROR(
+            m_msglog,
+            "outgoing buffers exceeded threshold, pausing logging--"
+            "consider flushing to disk more frequently (smaller period)");
+        m_state = kPaused;
+      }
       m_outgoing.emplace_back();
     } else {
       m_outgoing.emplace_back(std::move(m_free.back()));
@@ -531,8 +759,8 @@
     return;
   }
   std::scoped_lock lock{m_mutex};
-  if (m_paused) {
-    return;
+  if (m_state != kActive) {
+    [[unlikely]] return;
   }
   StartRecord(entry, timestamp, data.size(), 0);
   AppendImpl(data);
@@ -545,8 +773,8 @@
     return;
   }
   std::scoped_lock lock{m_mutex};
-  if (m_paused) {
-    return;
+  if (m_state != kActive) {
+    [[unlikely]] return;
   }
   size_t size = 0;
   for (auto&& chunk : data) {
@@ -563,8 +791,8 @@
     return;
   }
   std::scoped_lock lock{m_mutex};
-  if (m_paused) {
-    return;
+  if (m_state != kActive) {
+    [[unlikely]] return;
   }
   uint8_t* buf = StartRecord(entry, timestamp, 1, 1);
   buf[0] = value ? 1 : 0;
@@ -575,8 +803,8 @@
     return;
   }
   std::scoped_lock lock{m_mutex};
-  if (m_paused) {
-    return;
+  if (m_state != kActive) {
+    [[unlikely]] return;
   }
   uint8_t* buf = StartRecord(entry, timestamp, 8, 8);
   wpi::support::endian::write64le(buf, value);
@@ -587,15 +815,15 @@
     return;
   }
   std::scoped_lock lock{m_mutex};
-  if (m_paused) {
-    return;
+  if (m_state != kActive) {
+    [[unlikely]] return;
   }
   uint8_t* buf = StartRecord(entry, timestamp, 4, 4);
   if constexpr (wpi::support::endian::system_endianness() ==
                 wpi::support::little) {
     std::memcpy(buf, &value, 4);
   } else {
-    wpi::support::endian::write32le(buf, wpi::FloatToBits(value));
+    wpi::support::endian::write32le(buf, wpi::bit_cast<uint32_t>(value));
   }
 }
 
@@ -604,15 +832,15 @@
     return;
   }
   std::scoped_lock lock{m_mutex};
-  if (m_paused) {
-    return;
+  if (m_state != kActive) {
+    [[unlikely]] return;
   }
   uint8_t* buf = StartRecord(entry, timestamp, 8, 8);
   if constexpr (wpi::support::endian::system_endianness() ==
                 wpi::support::little) {
     std::memcpy(buf, &value, 8);
   } else {
-    wpi::support::endian::write64le(buf, wpi::DoubleToBits(value));
+    wpi::support::endian::write64le(buf, wpi::bit_cast<uint64_t>(value));
   }
 }
 
@@ -629,8 +857,8 @@
     return;
   }
   std::scoped_lock lock{m_mutex};
-  if (m_paused) {
-    return;
+  if (m_state != kActive) {
+    [[unlikely]] return;
   }
   StartRecord(entry, timestamp, arr.size(), 0);
   uint8_t* buf;
@@ -653,8 +881,8 @@
     return;
   }
   std::scoped_lock lock{m_mutex};
-  if (m_paused) {
-    return;
+  if (m_state != kActive) {
+    [[unlikely]] return;
   }
   StartRecord(entry, timestamp, arr.size(), 0);
   uint8_t* buf;
@@ -688,8 +916,8 @@
       return;
     }
     std::scoped_lock lock{m_mutex};
-    if (m_paused) {
-      return;
+    if (m_state != kActive) {
+      [[unlikely]] return;
     }
     StartRecord(entry, timestamp, arr.size() * 8, 0);
     uint8_t* buf;
@@ -721,22 +949,22 @@
       return;
     }
     std::scoped_lock lock{m_mutex};
-    if (m_paused) {
-      return;
+    if (m_state != kActive) {
+      [[unlikely]] return;
     }
     StartRecord(entry, timestamp, arr.size() * 4, 0);
     uint8_t* buf;
     while ((arr.size() * 4) > kBlockSize) {
       buf = Reserve(kBlockSize);
       for (auto val : arr.subspan(0, kBlockSize / 4)) {
-        wpi::support::endian::write32le(buf, wpi::FloatToBits(val));
+        wpi::support::endian::write32le(buf, wpi::bit_cast<uint32_t>(val));
         buf += 4;
       }
       arr = arr.subspan(kBlockSize / 4);
     }
     buf = Reserve(arr.size() * 4);
     for (auto val : arr) {
-      wpi::support::endian::write32le(buf, wpi::FloatToBits(val));
+      wpi::support::endian::write32le(buf, wpi::bit_cast<uint32_t>(val));
       buf += 4;
     }
   }
@@ -754,22 +982,22 @@
       return;
     }
     std::scoped_lock lock{m_mutex};
-    if (m_paused) {
-      return;
+    if (m_state != kActive) {
+      [[unlikely]] return;
     }
     StartRecord(entry, timestamp, arr.size() * 8, 0);
     uint8_t* buf;
     while ((arr.size() * 8) > kBlockSize) {
       buf = Reserve(kBlockSize);
       for (auto val : arr.subspan(0, kBlockSize / 8)) {
-        wpi::support::endian::write64le(buf, wpi::DoubleToBits(val));
+        wpi::support::endian::write64le(buf, wpi::bit_cast<uint64_t>(val));
         buf += 8;
       }
       arr = arr.subspan(kBlockSize / 8);
     }
     buf = Reserve(arr.size() * 8);
     for (auto val : arr) {
-      wpi::support::endian::write64le(buf, wpi::DoubleToBits(val));
+      wpi::support::endian::write64le(buf, wpi::bit_cast<uint64_t>(val));
       buf += 8;
     }
   }
@@ -787,8 +1015,8 @@
     size += 4 + str.size();
   }
   std::scoped_lock lock{m_mutex};
-  if (m_paused) {
-    return;
+  if (m_state != kActive) {
+    [[unlikely]] return;
   }
   uint8_t* buf = StartRecord(entry, timestamp, size, 4);
   wpi::support::endian::write32le(buf, arr.size());
@@ -810,12 +1038,169 @@
     size += 4 + str.size();
   }
   std::scoped_lock lock{m_mutex};
-  if (m_paused) {
-    return;
+  if (m_state != kActive) {
+    [[unlikely]] return;
   }
   uint8_t* buf = StartRecord(entry, timestamp, size, 4);
   wpi::support::endian::write32le(buf, arr.size());
-  for (auto sv : arr) {
+  for (auto&& sv : arr) {
     AppendStringImpl(sv);
   }
 }
+
+void DataLog::AppendStringArray(int entry,
+                                std::span<const WPI_DataLog_String> arr,
+                                int64_t timestamp) {
+  if (entry <= 0) {
+    return;
+  }
+  // storage: 4-byte array length, each string prefixed by 4-byte length
+  // calculate total size
+  size_t size = 4;
+  for (auto&& str : arr) {
+    size += 4 + str.len;
+  }
+  std::scoped_lock lock{m_mutex};
+  if (m_state != kActive) {
+    [[unlikely]] return;
+  }
+  uint8_t* buf = StartRecord(entry, timestamp, size, 4);
+  wpi::support::endian::write32le(buf, arr.size());
+  for (auto&& sv : arr) {
+    AppendStringImpl(sv.str);
+  }
+}
+
+extern "C" {
+
+struct WPI_DataLog* WPI_DataLog_Create(const char* dir, const char* filename,
+                                       double period, const char* extraHeader) {
+  return reinterpret_cast<WPI_DataLog*>(
+      new DataLog{dir, filename, period, extraHeader});
+}
+
+struct WPI_DataLog* WPI_DataLog_Create_Func(
+    void (*write)(void* ptr, const uint8_t* data, size_t len), void* ptr,
+    double period, const char* extraHeader) {
+  return reinterpret_cast<WPI_DataLog*>(
+      new DataLog{[=](auto data) { write(ptr, data.data(), data.size()); },
+                  period, extraHeader});
+}
+
+void WPI_DataLog_Release(struct WPI_DataLog* datalog) {
+  delete reinterpret_cast<DataLog*>(datalog);
+}
+
+void WPI_DataLog_SetFilename(struct WPI_DataLog* datalog,
+                             const char* filename) {
+  reinterpret_cast<DataLog*>(datalog)->SetFilename(filename);
+}
+
+void WPI_DataLog_Flush(struct WPI_DataLog* datalog) {
+  reinterpret_cast<DataLog*>(datalog)->Flush();
+}
+
+void WPI_DataLog_Pause(struct WPI_DataLog* datalog) {
+  reinterpret_cast<DataLog*>(datalog)->Pause();
+}
+
+void WPI_DataLog_Resume(struct WPI_DataLog* datalog) {
+  reinterpret_cast<DataLog*>(datalog)->Resume();
+}
+
+void WPI_DataLog_Stop(struct WPI_DataLog* datalog) {
+  reinterpret_cast<DataLog*>(datalog)->Stop();
+}
+
+int WPI_DataLog_Start(struct WPI_DataLog* datalog, const char* name,
+                      const char* type, const char* metadata,
+                      int64_t timestamp) {
+  return reinterpret_cast<DataLog*>(datalog)->Start(name, type, metadata,
+                                                    timestamp);
+}
+
+void WPI_DataLog_Finish(struct WPI_DataLog* datalog, int entry,
+                        int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->Finish(entry, timestamp);
+}
+
+void WPI_DataLog_SetMetadata(struct WPI_DataLog* datalog, int entry,
+                             const char* metadata, int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->SetMetadata(entry, metadata, timestamp);
+}
+
+void WPI_DataLog_AppendRaw(struct WPI_DataLog* datalog, int entry,
+                           const uint8_t* data, size_t len, int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendRaw(entry, {data, len}, timestamp);
+}
+
+void WPI_DataLog_AppendBoolean(struct WPI_DataLog* datalog, int entry,
+                               int value, int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendBoolean(entry, value, timestamp);
+}
+
+void WPI_DataLog_AppendInteger(struct WPI_DataLog* datalog, int entry,
+                               int64_t value, int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendInteger(entry, value, timestamp);
+}
+
+void WPI_DataLog_AppendFloat(struct WPI_DataLog* datalog, int entry,
+                             float value, int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendFloat(entry, value, timestamp);
+}
+
+void WPI_DataLog_AppendDouble(struct WPI_DataLog* datalog, int entry,
+                              double value, int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendDouble(entry, value, timestamp);
+}
+
+void WPI_DataLog_AppendString(struct WPI_DataLog* datalog, int entry,
+                              const char* value, size_t len,
+                              int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendString(entry, {value, len},
+                                                    timestamp);
+}
+
+void WPI_DataLog_AppendBooleanArray(struct WPI_DataLog* datalog, int entry,
+                                    const int* arr, size_t len,
+                                    int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendBooleanArray(entry, {arr, len},
+                                                          timestamp);
+}
+
+void WPI_DataLog_AppendBooleanArrayByte(struct WPI_DataLog* datalog, int entry,
+                                        const uint8_t* arr, size_t len,
+                                        int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendBooleanArray(entry, {arr, len},
+                                                          timestamp);
+}
+
+void WPI_DataLog_AppendIntegerArray(struct WPI_DataLog* datalog, int entry,
+                                    const int64_t* arr, size_t len,
+                                    int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendIntegerArray(entry, {arr, len},
+                                                          timestamp);
+}
+
+void WPI_DataLog_AppendFloatArray(struct WPI_DataLog* datalog, int entry,
+                                  const float* arr, size_t len,
+                                  int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendFloatArray(entry, {arr, len},
+                                                        timestamp);
+}
+
+void WPI_DataLog_AppendDoubleArray(struct WPI_DataLog* datalog, int entry,
+                                   const double* arr, size_t len,
+                                   int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendDoubleArray(entry, {arr, len},
+                                                         timestamp);
+}
+
+void WPI_DataLog_AppendStringArray(struct WPI_DataLog* datalog, int entry,
+                                   const WPI_DataLog_String* arr, size_t len,
+                                   int64_t timestamp) {
+  reinterpret_cast<DataLog*>(datalog)->AppendStringArray(entry, {arr, len},
+                                                         timestamp);
+}
+
+}  // extern "C"
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/DataLogReader.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/DataLogReader.cpp
index 96f6689..c2e1192 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/cpp/DataLogReader.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/DataLogReader.cpp
@@ -95,7 +95,7 @@
   if (m_data.size() != 4) {
     return false;
   }
-  *value = wpi::BitsToFloat(wpi::support::endian::read32le(m_data.data()));
+  *value = wpi::bit_cast<float>(wpi::support::endian::read32le(m_data.data()));
   return true;
 }
 
@@ -103,7 +103,7 @@
   if (m_data.size() != 8) {
     return false;
   }
-  *value = wpi::BitsToDouble(wpi::support::endian::read64le(m_data.data()));
+  *value = wpi::bit_cast<double>(wpi::support::endian::read64le(m_data.data()));
   return true;
 }
 
@@ -141,7 +141,7 @@
   arr->reserve(m_data.size() / 4);
   for (size_t pos = 0; pos < m_data.size(); pos += 4) {
     arr->push_back(
-        wpi::BitsToFloat(wpi::support::endian::read32le(&m_data[pos])));
+        wpi::bit_cast<float>(wpi::support::endian::read32le(&m_data[pos])));
   }
   return true;
 }
@@ -154,7 +154,7 @@
   arr->reserve(m_data.size() / 8);
   for (size_t pos = 0; pos < m_data.size(); pos += 8) {
     arr->push_back(
-        wpi::BitsToDouble(wpi::support::endian::read64le(&m_data[pos])));
+        wpi::bit_cast<double>(wpi::support::endian::read64le(&m_data[pos])));
   }
   return true;
 }
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/fs.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/fs.cpp
index fad6a66..ed68297 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/cpp/fs.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/fs.cpp
@@ -43,26 +43,6 @@
 
 #endif  // _WIN32
 
-#if defined(__APPLE__)
-#include <Availability.h>
-#endif
-#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) \
-     || (defined(__cplusplus) && __cplusplus >= 201703L)) \
-    && defined(__has_include)
-#if __has_include(<filesystem>) \
-    && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
-        || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) \
-    && (defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 10 \
-        || (__GNUC__ >= 9 && __GNUC_MINOR__ >= 1))
-#define GHC_USE_STD_FS
-#endif
-#endif
-#ifndef GHC_USE_STD_FS
-// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
-#define GHC_FILESYSTEM_IMPLEMENTATION
-#include "wpi/ghc/filesystem.hpp"
-#endif
-
 #include "wpi/Errno.h"
 #include "wpi/ErrorHandling.h"
 #include "wpi/WindowsError.h"
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp
index 997b90b..c78c891 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp
@@ -4,6 +4,9 @@
 
 #include <jni.h>
 
+#include <fmt/format.h>
+
+#include "WPIUtilJNI.h"
 #include "edu_wpi_first_util_datalog_DataLogJNI.h"
 #include "wpi/DataLog.h"
 #include "wpi/jni_util.h"
@@ -23,6 +26,18 @@
   (JNIEnv* env, jclass, jstring dir, jstring filename, jdouble period,
    jstring extraHeader)
 {
+  if (!dir) {
+    wpi::ThrowNullPointerException(env, "dir is null");
+    return 0;
+  }
+  if (!filename) {
+    wpi::ThrowNullPointerException(env, "filename is null");
+    return 0;
+  }
+  if (!extraHeader) {
+    wpi::ThrowNullPointerException(env, "extraHeader is null");
+    return 0;
+  }
   return reinterpret_cast<jlong>(new DataLog{JStringRef{env, dir},
                                              JStringRef{env, filename}, period,
                                              JStringRef{env, extraHeader}});
@@ -38,6 +53,11 @@
   (JNIEnv* env, jclass, jlong impl, jstring filename)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
+    return;
+  }
+  if (!filename) {
+    wpi::ThrowNullPointerException(env, "filename is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->SetFilename(JStringRef{env, filename});
@@ -50,9 +70,10 @@
  */
 JNIEXPORT void JNICALL
 Java_edu_wpi_first_util_datalog_DataLogJNI_flush
-  (JNIEnv*, jclass, jlong impl)
+  (JNIEnv* env, jclass, jlong impl)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->Flush();
@@ -65,9 +86,10 @@
  */
 JNIEXPORT void JNICALL
 Java_edu_wpi_first_util_datalog_DataLogJNI_pause
-  (JNIEnv*, jclass, jlong impl)
+  (JNIEnv* env, jclass, jlong impl)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->Pause();
@@ -80,9 +102,10 @@
  */
 JNIEXPORT void JNICALL
 Java_edu_wpi_first_util_datalog_DataLogJNI_resume
-  (JNIEnv*, jclass, jlong impl)
+  (JNIEnv* env, jclass, jlong impl)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->Resume();
@@ -90,6 +113,63 @@
 
 /*
  * Class:     edu_wpi_first_util_datalog_DataLogJNI
+ * Method:    stop
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_stop
+  (JNIEnv* env, jclass, jlong impl)
+{
+  if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
+    return;
+  }
+  reinterpret_cast<DataLog*>(impl)->Stop();
+}
+
+/*
+ * Class:     edu_wpi_first_util_datalog_DataLogJNI
+ * Method:    addSchema
+ * Signature: (JLjava/lang/String;Ljava/lang/String;[BJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_addSchema
+  (JNIEnv* env, jclass, jlong impl, jstring name, jstring type,
+   jbyteArray schema, jlong timestamp)
+{
+  if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
+    return;
+  }
+  reinterpret_cast<DataLog*>(impl)->AddSchema(
+      JStringRef{env, name}, JStringRef{env, type},
+      JSpan<const jbyte>{env, schema}.uarray(), timestamp);
+}
+
+/*
+ * Class:     edu_wpi_first_util_datalog_DataLogJNI
+ * Method:    addSchemaString
+ * Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_addSchemaString
+  (JNIEnv* env, jclass, jlong impl, jstring name, jstring type, jstring schema,
+   jlong timestamp)
+{
+  if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
+    return;
+  }
+  JStringRef schemaStr{env, schema};
+  std::string_view schemaView = schemaStr.str();
+  reinterpret_cast<DataLog*>(impl)->AddSchema(
+      JStringRef{env, name}, JStringRef{env, type},
+      {reinterpret_cast<const uint8_t*>(schemaView.data()), schemaView.size()},
+      timestamp);
+}
+
+/*
+ * Class:     edu_wpi_first_util_datalog_DataLogJNI
  * Method:    start
  * Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;J)I
  */
@@ -99,6 +179,7 @@
    jstring metadata, jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return 0;
   }
   return reinterpret_cast<DataLog*>(impl)->Start(
@@ -113,9 +194,10 @@
  */
 JNIEXPORT void JNICALL
 Java_edu_wpi_first_util_datalog_DataLogJNI_finish
-  (JNIEnv*, jclass, jlong impl, jint entry, jlong timestamp)
+  (JNIEnv* env, jclass, jlong impl, jint entry, jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->Finish(entry, timestamp);
@@ -132,6 +214,7 @@
    jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->SetMetadata(
@@ -153,21 +236,73 @@
 /*
  * Class:     edu_wpi_first_util_datalog_DataLogJNI
  * Method:    appendRaw
- * Signature: (JI[BJ)V
+ * Signature: (JI[BIIJ)V
  */
 JNIEXPORT void JNICALL
 Java_edu_wpi_first_util_datalog_DataLogJNI_appendRaw
-  (JNIEnv* env, jclass, jlong impl, jint entry, jbyteArray value,
-   jlong timestamp)
+  (JNIEnv* env, jclass, jlong impl, jint entry, jbyteArray value, jint start,
+   jint length, jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
-  JByteArrayRef cvalue{env, value};
+  if (!value) {
+    wpi::ThrowNullPointerException(env, "value is null");
+    return;
+  }
+  if (start < 0) {
+    wpi::ThrowIndexOobException(env, "start must be >= 0");
+    return;
+  }
+  if (length < 0) {
+    wpi::ThrowIndexOobException(env, "length must be >= 0");
+    return;
+  }
+  CriticalJSpan<const jbyte> cvalue{env, value};
+  if (static_cast<unsigned int>(start + length) > cvalue.size()) {
+    wpi::ThrowIndexOobException(
+        env, "start + len must be smaller than array length");
+    return;
+  }
   reinterpret_cast<DataLog*>(impl)->AppendRaw(
-      entry,
-      {reinterpret_cast<const uint8_t*>(cvalue.array().data()), cvalue.size()},
-      timestamp);
+      entry, cvalue.uarray().subspan(start, length), timestamp);
+}
+
+/*
+ * Class:     edu_wpi_first_util_datalog_DataLogJNI
+ * Method:    appendRawBuffer
+ * Signature: (JILjava/lang/Object;IIJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendRawBuffer
+  (JNIEnv* env, jclass, jlong impl, jint entry, jobject value, jint start,
+   jint length, jlong timestamp)
+{
+  if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
+    return;
+  }
+  if (!value) {
+    wpi::ThrowNullPointerException(env, "value is null");
+    return;
+  }
+  if (start < 0) {
+    wpi::ThrowIndexOobException(env, "start must be >= 0");
+    return;
+  }
+  if (length < 0) {
+    wpi::ThrowIndexOobException(env, "length must be >= 0");
+    return;
+  }
+  JSpan<const jbyte> cvalue{env, value, static_cast<size_t>(start + length)};
+  if (!cvalue) {
+    wpi::ThrowIllegalArgumentException(env,
+                                       "value must be a native ByteBuffer");
+    return;
+  }
+  reinterpret_cast<DataLog*>(impl)->AppendRaw(
+      entry, cvalue.uarray().subspan(start, length), timestamp);
 }
 
 /*
@@ -177,9 +312,10 @@
  */
 JNIEXPORT void JNICALL
 Java_edu_wpi_first_util_datalog_DataLogJNI_appendBoolean
-  (JNIEnv*, jclass, jlong impl, jint entry, jboolean value, jlong timestamp)
+  (JNIEnv* env, jclass, jlong impl, jint entry, jboolean value, jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->AppendBoolean(entry, value, timestamp);
@@ -192,9 +328,10 @@
  */
 JNIEXPORT void JNICALL
 Java_edu_wpi_first_util_datalog_DataLogJNI_appendInteger
-  (JNIEnv*, jclass, jlong impl, jint entry, jlong value, jlong timestamp)
+  (JNIEnv* env, jclass, jlong impl, jint entry, jlong value, jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->AppendInteger(entry, value, timestamp);
@@ -207,9 +344,10 @@
  */
 JNIEXPORT void JNICALL
 Java_edu_wpi_first_util_datalog_DataLogJNI_appendFloat
-  (JNIEnv*, jclass, jlong impl, jint entry, jfloat value, jlong timestamp)
+  (JNIEnv* env, jclass, jlong impl, jint entry, jfloat value, jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->AppendFloat(entry, value, timestamp);
@@ -222,9 +360,10 @@
  */
 JNIEXPORT void JNICALL
 Java_edu_wpi_first_util_datalog_DataLogJNI_appendDouble
-  (JNIEnv*, jclass, jlong impl, jint entry, jdouble value, jlong timestamp)
+  (JNIEnv* env, jclass, jlong impl, jint entry, jdouble value, jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->AppendDouble(entry, value, timestamp);
@@ -240,6 +379,7 @@
   (JNIEnv* env, jclass, jlong impl, jint entry, jstring value, jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->AppendString(entry, JStringRef{env, value},
@@ -257,10 +397,15 @@
    jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
+    return;
+  }
+  if (!value) {
+    wpi::ThrowNullPointerException(env, "value is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->AppendBooleanArray(
-      entry, JBooleanArrayRef{env, value}, timestamp);
+      entry, JSpan<const jboolean>{env, value}, timestamp);
 }
 
 /*
@@ -274,19 +419,22 @@
    jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
     return;
   }
-  JLongArrayRef jarr{env, value};
+  if (!value) {
+    wpi::ThrowNullPointerException(env, "value is null");
+    return;
+  }
+  JSpan<const jlong> jarr{env, value};
   if constexpr (sizeof(jlong) == sizeof(int64_t)) {
     reinterpret_cast<DataLog*>(impl)->AppendIntegerArray(
-        entry,
-        {reinterpret_cast<const int64_t*>(jarr.array().data()),
-         jarr.array().size()},
+        entry, {reinterpret_cast<const int64_t*>(jarr.data()), jarr.size()},
         timestamp);
   } else {
     wpi::SmallVector<int64_t, 16> arr;
     arr.reserve(jarr.size());
-    for (auto v : jarr.array()) {
+    for (auto v : jarr) {
       arr.push_back(v);
     }
     reinterpret_cast<DataLog*>(impl)->AppendIntegerArray(entry, arr, timestamp);
@@ -304,10 +452,15 @@
    jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
+    return;
+  }
+  if (!value) {
+    wpi::ThrowNullPointerException(env, "value is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->AppendFloatArray(
-      entry, JFloatArrayRef{env, value}, timestamp);
+      entry, JSpan<const jfloat>{env, value}, timestamp);
 }
 
 /*
@@ -321,10 +474,15 @@
    jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
+    return;
+  }
+  if (!value) {
+    wpi::ThrowNullPointerException(env, "value is null");
     return;
   }
   reinterpret_cast<DataLog*>(impl)->AppendDoubleArray(
-      entry, JDoubleArrayRef{env, value}, timestamp);
+      entry, JSpan<const jdouble>{env, value}, timestamp);
 }
 
 /*
@@ -338,6 +496,11 @@
    jlong timestamp)
 {
   if (impl == 0) {
+    wpi::ThrowNullPointerException(env, "impl is null");
+    return;
+  }
+  if (!value) {
+    wpi::ThrowNullPointerException(env, "value is null");
     return;
   }
   size_t len = env->GetArrayLength(value);
@@ -347,6 +510,8 @@
     JLocal<jstring> elem{
         env, static_cast<jstring>(env->GetObjectArrayElement(value, i))};
     if (!elem) {
+      wpi::ThrowNullPointerException(
+          env, fmt::format("string at element {} is null", i));
       return;
     }
     arr.emplace_back(JStringRef{env, elem}.str());
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
index 5fa4ddf..eb55fd0 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
@@ -2,10 +2,13 @@
 // 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 "WPIUtilJNI.h"
+
 #include <jni.h>
 
+#include <fmt/format.h>
+
 #include "edu_wpi_first_util_WPIUtilJNI.h"
-#include "fmt/format.h"
 #include "wpi/Synchronization.h"
 #include "wpi/jni_util.h"
 #include "wpi/timestamp.h"
@@ -15,7 +18,28 @@
 static bool mockTimeEnabled = false;
 static uint64_t mockNow = 0;
 
+static JException illegalArgEx;
+static JException indexOobEx;
 static JException interruptedEx;
+static JException nullPointerEx;
+
+static const JExceptionInit exceptions[] = {
+    {"java/lang/IllegalArgumentException", &illegalArgEx},
+    {"java/lang/IndexOutOfBoundsException", &indexOobEx},
+    {"java/lang/InterruptedException", &interruptedEx},
+    {"java/lang/NullPointerException", &nullPointerEx}};
+
+void wpi::ThrowIllegalArgumentException(JNIEnv* env, std::string_view msg) {
+  illegalArgEx.Throw(env, msg);
+}
+
+void wpi::ThrowIndexOobException(JNIEnv* env, std::string_view msg) {
+  indexOobEx.Throw(env, msg);
+}
+
+void wpi::ThrowNullPointerException(JNIEnv* env, std::string_view msg) {
+  nullPointerEx.Throw(env, msg);
+}
 
 extern "C" {
 
@@ -25,9 +49,11 @@
     return JNI_ERR;
   }
 
-  interruptedEx = JException(env, "java/lang/InterruptedException");
-  if (!interruptedEx) {
-    return JNI_ERR;
+  for (auto& c : exceptions) {
+    *c.cls = JException(env, c.name);
+    if (!*c.cls) {
+      return JNI_ERR;
+    }
   }
 
   return JNI_VERSION_1_6;
@@ -39,7 +65,9 @@
     return;
   }
 
-  interruptedEx.free(env);
+  for (auto& c : exceptions) {
+    c.cls->free(env);
+  }
 }
 
 /*
@@ -51,7 +79,7 @@
 Java_edu_wpi_first_util_WPIUtilJNI_writeStderr
   (JNIEnv* env, jclass, jstring str)
 {
-  fmt::print(stderr, "{}", JStringRef{env, str});
+  fmt::print(stderr, "{}", JStringRef{env, str}.str());
 }
 
 /*
@@ -63,8 +91,12 @@
 Java_edu_wpi_first_util_WPIUtilJNI_enableMockTime
   (JNIEnv*, jclass)
 {
+#ifdef __FRC_ROBORIO__
+  fmt::print(stderr, "WPIUtil: Mocking time is not available on the Rio\n");
+#else
   mockTimeEnabled = true;
   wpi::SetNowImpl([] { return mockNow; });
+#endif
 }
 
 /*
@@ -244,11 +276,11 @@
 Java_edu_wpi_first_util_WPIUtilJNI_waitForObjects
   (JNIEnv* env, jclass, jintArray handles)
 {
-  JIntArrayRef handlesArr{env, handles};
+  JSpan<const jint> handlesArr{env, handles};
   wpi::SmallVector<WPI_Handle, 8> signaledBuf;
   signaledBuf.resize(handlesArr.size());
   std::span<const WPI_Handle> handlesArr2{
-      reinterpret_cast<const WPI_Handle*>(handlesArr.array().data()),
+      reinterpret_cast<const WPI_Handle*>(handlesArr.data()),
       handlesArr.size()};
 
   auto signaled = wpi::WaitForObjects(handlesArr2, signaledBuf);
@@ -268,11 +300,11 @@
 Java_edu_wpi_first_util_WPIUtilJNI_waitForObjectsTimeout
   (JNIEnv* env, jclass, jintArray handles, jdouble timeout)
 {
-  JIntArrayRef handlesArr{env, handles};
+  JSpan<const jint> handlesArr{env, handles};
   wpi::SmallVector<WPI_Handle, 8> signaledBuf;
   signaledBuf.resize(handlesArr.size());
   std::span<const WPI_Handle> handlesArr2{
-      reinterpret_cast<const WPI_Handle*>(handlesArr.array().data()),
+      reinterpret_cast<const WPI_Handle*>(handlesArr.data()),
       handlesArr.size()};
 
   bool timedOut;
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.h b/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.h
new file mode 100644
index 0000000..541064f
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.h
@@ -0,0 +1,17 @@
+// 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 <jni.h>
+
+#include <string_view>
+
+namespace wpi {
+
+void ThrowIllegalArgumentException(JNIEnv* env, std::string_view msg);
+void ThrowIndexOobException(JNIEnv* env, std::string_view msg);
+void ThrowNullPointerException(JNIEnv* env, std::string_view msg);
+
+}  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/protobuf/Protobuf.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/protobuf/Protobuf.cpp
new file mode 100644
index 0000000..4f2a616
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/protobuf/Protobuf.cpp
@@ -0,0 +1,194 @@
+// 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/protobuf/Protobuf.h"
+
+#include <fmt/format.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/message.h>
+
+#include "wpi/SmallVector.h"
+
+using namespace wpi;
+
+using google::protobuf::Arena;
+using google::protobuf::FileDescriptor;
+using google::protobuf::FileDescriptorProto;
+
+namespace {
+class VectorOutputStream final
+    : public google::protobuf::io::ZeroCopyOutputStream {
+ public:
+  // Create a StringOutputStream which appends bytes to the given string.
+  // The string remains property of the caller, but it is mutated in arbitrary
+  // ways and MUST NOT be accessed in any way until you're done with the
+  // stream. Either be sure there's no further usage, or (safest) destroy the
+  // stream before using the contents.
+  //
+  // Hint:  If you call target->reserve(n) before creating the stream,
+  //   the first call to Next() will return at least n bytes of buffer
+  //   space.
+  explicit VectorOutputStream(std::vector<uint8_t>& target) : target_{target} {}
+  VectorOutputStream(const VectorOutputStream&) = delete;
+  ~VectorOutputStream() override = default;
+
+  VectorOutputStream& operator=(const VectorOutputStream&) = delete;
+
+  // implements ZeroCopyOutputStream ---------------------------------
+  bool Next(void** data, int* size) override;
+  void BackUp(int count) override { target_.resize(target_.size() - count); }
+  int64_t ByteCount() const override { return target_.size(); }
+
+ private:
+  static constexpr size_t kMinimumSize = 16;
+
+  std::vector<uint8_t>& target_;
+};
+
+class SmallVectorOutputStream final
+    : public google::protobuf::io::ZeroCopyOutputStream {
+ public:
+  // Create a StringOutputStream which appends bytes to the given string.
+  // The string remains property of the caller, but it is mutated in arbitrary
+  // ways and MUST NOT be accessed in any way until you're done with the
+  // stream. Either be sure there's no further usage, or (safest) destroy the
+  // stream before using the contents.
+  //
+  // Hint:  If you call target->reserve(n) before creating the stream,
+  //   the first call to Next() will return at least n bytes of buffer
+  //   space.
+  explicit SmallVectorOutputStream(wpi::SmallVectorImpl<uint8_t>& target)
+      : target_{target} {
+    target.resize(0);
+  }
+  SmallVectorOutputStream(const SmallVectorOutputStream&) = delete;
+  ~SmallVectorOutputStream() override = default;
+
+  SmallVectorOutputStream& operator=(const SmallVectorOutputStream&) = delete;
+
+  // implements ZeroCopyOutputStream ---------------------------------
+  bool Next(void** data, int* size) override;
+  void BackUp(int count) override { target_.resize(target_.size() - count); }
+  int64_t ByteCount() const override { return target_.size(); }
+
+ private:
+  static constexpr size_t kMinimumSize = 16;
+
+  wpi::SmallVectorImpl<uint8_t>& target_;
+};
+}  // namespace
+
+bool VectorOutputStream::Next(void** data, int* size) {
+  size_t old_size = target_.size();
+
+  // Grow the string.
+  size_t new_size;
+  if (old_size < target_.capacity()) {
+    // Resize to match its capacity, since we can get away
+    // without a memory allocation this way.
+    new_size = target_.capacity();
+  } else {
+    // Size has reached capacity, try to double it.
+    new_size = old_size * 2;
+  }
+  // Avoid integer overflow in returned '*size'.
+  new_size = (std::min)(new_size, old_size + (std::numeric_limits<int>::max)());
+  // Increase the size, also make sure that it is at least kMinimumSize.
+  target_.resize((std::max)(new_size, kMinimumSize));
+
+  *data = target_.data() + old_size;
+  *size = target_.size() - old_size;
+  return true;
+}
+
+bool SmallVectorOutputStream::Next(void** data, int* size) {
+  size_t old_size = target_.size();
+
+  // Grow the string.
+  size_t new_size;
+  if (old_size < target_.capacity()) {
+    // Resize to match its capacity, since we can get away
+    // without a memory allocation this way.
+    new_size = target_.capacity();
+  } else {
+    // Size has reached capacity, try to double it.
+    new_size = old_size * 2;
+  }
+  // Avoid integer overflow in returned '*size'.
+  new_size = (std::min)(new_size, old_size + (std::numeric_limits<int>::max)());
+  // Increase the size, also make sure that it is at least kMinimumSize.
+  target_.resize_for_overwrite((std::max)(new_size, kMinimumSize));
+
+  *data = target_.data() + old_size;
+  *size = target_.size() - old_size;
+  return true;
+}
+
+void detail::DeleteProtobuf(google::protobuf::Message* msg) {
+  if (msg && !msg->GetArena()) {
+    delete msg;
+  }
+}
+
+bool detail::ParseProtobuf(google::protobuf::Message* msg,
+                           std::span<const uint8_t> data) {
+  return msg->ParseFromArray(data.data(), data.size());
+}
+
+bool detail::SerializeProtobuf(wpi::SmallVectorImpl<uint8_t>& out,
+                               const google::protobuf::Message& msg) {
+  SmallVectorOutputStream stream{out};
+  return msg.SerializeToZeroCopyStream(&stream);
+}
+
+bool detail::SerializeProtobuf(std::vector<uint8_t>& out,
+                               const google::protobuf::Message& msg) {
+  VectorOutputStream stream{out};
+  return msg.SerializeToZeroCopyStream(&stream);
+}
+
+std::string detail::GetTypeString(const google::protobuf::Message& msg) {
+  return fmt::format("proto:{}", msg.GetDescriptor()->full_name());
+}
+
+static void ForEachProtobufDescriptorImpl(
+    const FileDescriptor* desc,
+    function_ref<bool(std::string_view typeString)> exists,
+    function_ref<void(std::string_view typeString,
+                      std::span<const uint8_t> schema)>
+        fn,
+    Arena* arena, FileDescriptorProto** descproto) {
+  std::string name = fmt::format("proto:{}", desc->name());
+  if (exists(name)) {
+    return;
+  }
+  for (int i = 0, ndep = desc->dependency_count(); i < ndep; ++i) {
+    ForEachProtobufDescriptorImpl(desc->dependency(i), exists, fn, arena,
+                                  descproto);
+  }
+  if (!*descproto) {
+    *descproto = Arena::CreateMessage<FileDescriptorProto>(arena);
+  }
+  (*descproto)->Clear();
+  desc->CopyTo(*descproto);
+  SmallVector<uint8_t, 128> buf;
+  detail::SerializeProtobuf(buf, **descproto);
+  fn(name, buf);
+}
+
+void detail::ForEachProtobufDescriptor(
+    const google::protobuf::Message& msg,
+    function_ref<bool(std::string_view filename)> exists,
+    function_ref<void(std::string_view filename,
+                      std::span<const uint8_t> descriptor)>
+        fn) {
+  FileDescriptorProto* descproto = nullptr;
+  ForEachProtobufDescriptorImpl(msg.GetDescriptor()->file(), exists, fn,
+                                msg.GetArena(), &descproto);
+  if (descproto && !msg.GetArena()) {
+    delete descproto;
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/protobuf/ProtobufMessageDatabase.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/protobuf/ProtobufMessageDatabase.cpp
new file mode 100644
index 0000000..144bd03
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/protobuf/ProtobufMessageDatabase.cpp
@@ -0,0 +1,131 @@
+// 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/protobuf/ProtobufMessageDatabase.h"
+
+#include <google/protobuf/descriptor.h>
+
+using namespace wpi;
+
+using google::protobuf::Arena;
+using google::protobuf::FileDescriptorProto;
+using google::protobuf::Message;
+
+bool ProtobufMessageDatabase::Add(std::string_view filename,
+                                  std::span<const uint8_t> data) {
+  auto& file = m_files[filename];
+  bool needsRebuild = false;
+  if (file.complete) {
+    file.complete = false;
+
+    m_msgs.clear();
+    m_factory.reset();
+
+    // rebuild the pool EXCEPT for this descriptor
+    m_pool = std::make_unique<google::protobuf::DescriptorPool>();
+
+    for (auto&& p : m_files) {
+      p.second.inPool = false;
+    }
+
+    needsRebuild = true;
+  }
+
+  if (!file.proto) {
+    file.proto = std::unique_ptr<FileDescriptorProto>{
+        Arena::CreateMessage<FileDescriptorProto>(nullptr)};
+  } else {
+    // replacing an existing one; remove any previously existing refs
+    for (auto&& dep : file.proto->dependency()) {
+      auto& depFile = m_files[dep];
+      std::erase(depFile.uses, filename);
+    }
+    file.proto->Clear();
+  }
+
+  // parse data
+  if (!file.proto->ParseFromArray(data.data(), data.size())) {
+    return false;
+  }
+
+  // rebuild if necessary; we do this after the parse due to dependencies
+  if (needsRebuild) {
+    for (auto&& p : m_files) {
+      if (p.second.complete && !p.second.inPool) {
+        Rebuild(p.second);
+      }
+    }
+
+    // clear messages and reset factory; Find() will recreate as needed
+    m_factory = std::make_unique<google::protobuf::DynamicMessageFactory>();
+  }
+
+  // build this one
+  Build(filename, file);
+  return true;
+}
+
+Message* ProtobufMessageDatabase::Find(std::string_view name) const {
+  // cached
+  auto& msg = m_msgs[name];
+  if (msg) {
+    return msg.get();
+  }
+
+  // need to create it
+  auto desc = m_pool->FindMessageTypeByName(std::string{name});
+  if (!desc) {
+    return nullptr;
+  }
+  msg = std::unique_ptr<Message>{m_factory->GetPrototype(desc)->New(nullptr)};
+  return msg.get();
+}
+
+void ProtobufMessageDatabase::Build(std::string_view filename,
+                                    ProtoFile& file) {
+  if (file.complete) {
+    return;
+  }
+  // are all of the dependencies complete?
+  bool complete = true;
+  for (auto&& dep : file.proto->dependency()) {
+    auto& depFile = m_files[dep];
+    if (!depFile.complete) {
+      complete = false;
+    }
+    depFile.uses.emplace_back(filename);
+  }
+  if (!complete) {
+    return;
+  }
+
+  // add to pool
+  if (!m_pool->BuildFile(*file.proto)) {
+    return;
+  }
+  file.inPool = true;
+  file.complete = true;
+
+  // recursively validate all uses
+  for (auto&& use : file.uses) {
+    Build(use, m_files[use]);
+  }
+}
+
+bool ProtobufMessageDatabase::Rebuild(ProtoFile& file) {
+  for (auto&& dep : file.proto->dependency()) {
+    auto& depFile = m_files[dep];
+    if (!depFile.inPool) {
+      if (!Rebuild(depFile)) {
+        return false;
+      }
+    }
+  }
+  if (!m_pool->BuildFile(*file.proto)) {
+    return false;
+  }
+  file.inPool = true;
+  file.complete = true;
+  return true;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp
index 2c591e9..7a1e0b0 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp
@@ -6,7 +6,8 @@
 
 #include <memory>
 
-#include "fmt/format.h"
+#include <fmt/format.h>
+
 #include "wpi/DenseMap.h"
 #include "wpi/SmallVector.h"
 #include "wpi/UidVector.h"
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/struct/DynamicStruct.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/struct/DynamicStruct.cpp
new file mode 100644
index 0000000..7ae9271
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/struct/DynamicStruct.cpp
@@ -0,0 +1,444 @@
+// 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/struct/DynamicStruct.h"
+
+#include <algorithm>
+
+#include <fmt/format.h>
+
+#include "wpi/Endian.h"
+#include "wpi/SmallString.h"
+#include "wpi/SmallVector.h"
+#include "wpi/raw_ostream.h"
+#include "wpi/struct/SchemaParser.h"
+
+using namespace wpi;
+
+static size_t TypeToSize(StructFieldType type) {
+  switch (type) {
+    case StructFieldType::kBool:
+    case StructFieldType::kChar:
+    case StructFieldType::kInt8:
+    case StructFieldType::kUint8:
+      return 1;
+    case StructFieldType::kInt16:
+    case StructFieldType::kUint16:
+      return 2;
+    case StructFieldType::kInt32:
+    case StructFieldType::kUint32:
+    case StructFieldType::kFloat:
+      return 4;
+    case StructFieldType::kInt64:
+    case StructFieldType::kUint64:
+    case StructFieldType::kDouble:
+      return 8;
+    default:
+      return 0;
+  }
+}
+
+static StructFieldType TypeStringToType(std::string_view str) {
+  if (str == "bool") {
+    return StructFieldType::kBool;
+  } else if (str == "char") {
+    return StructFieldType::kChar;
+  } else if (str == "int8") {
+    return StructFieldType::kInt8;
+  } else if (str == "int16") {
+    return StructFieldType::kInt16;
+  } else if (str == "int32") {
+    return StructFieldType::kInt32;
+  } else if (str == "int64") {
+    return StructFieldType::kInt64;
+  } else if (str == "uint8") {
+    return StructFieldType::kUint8;
+  } else if (str == "uint16") {
+    return StructFieldType::kUint16;
+  } else if (str == "uint32") {
+    return StructFieldType::kUint32;
+  } else if (str == "uint64") {
+    return StructFieldType::kUint64;
+  } else if (str == "float" || str == "float32") {
+    return StructFieldType::kFloat;
+  } else if (str == "double" || str == "float64") {
+    return StructFieldType::kDouble;
+  } else {
+    return StructFieldType::kStruct;
+  }
+}
+
+static inline unsigned int ToBitWidth(size_t size, unsigned int bitWidth) {
+  if (bitWidth == 0) {
+    return size * 8;
+  } else {
+    return bitWidth;
+  }
+}
+
+static inline uint64_t ToBitMask(size_t size, unsigned int bitWidth) {
+  if (size == 0) {
+    return 0;
+  } else {
+    return UINT64_MAX >> (64 - ToBitWidth(size, bitWidth));
+  }
+}
+
+StructFieldDescriptor::StructFieldDescriptor(
+    const StructDescriptor* parent, std::string_view name, StructFieldType type,
+    size_t size, size_t arraySize, unsigned int bitWidth, EnumValues enumValues,
+    const StructDescriptor* structDesc, const private_init&)
+    : m_parent{parent},
+      m_name{name},
+      m_size{size},
+      m_arraySize{arraySize},
+      m_enum{std::move(enumValues)},
+      m_struct{structDesc},
+      m_bitMask{ToBitMask(size, bitWidth)},
+      m_type{type},
+      m_bitWidth{ToBitWidth(size, bitWidth)} {}
+
+const StructFieldDescriptor* StructDescriptor::FindFieldByName(
+    std::string_view name) const {
+  auto it = m_fieldsByName.find(name);
+  if (it == m_fieldsByName.end()) {
+    return nullptr;
+  }
+  return &m_fields[it->second];
+}
+
+bool StructDescriptor::CheckCircular(
+    wpi::SmallVectorImpl<const StructDescriptor*>& stack) const {
+  stack.emplace_back(this);
+  for (auto&& ref : m_references) {
+    if (std::find(stack.begin(), stack.end(), ref) != stack.end()) {
+      [[unlikely]] return false;
+    }
+    if (!ref->CheckCircular(stack)) {
+      [[unlikely]] return false;
+    }
+  }
+  stack.pop_back();
+  return true;
+}
+
+std::string StructDescriptor::CalculateOffsets(
+    wpi::SmallVectorImpl<const StructDescriptor*>& stack) {
+  size_t offset = 0;
+  unsigned int shift = 0;
+  size_t prevBitfieldSize = 0;
+  for (auto&& field : m_fields) {
+    if (!field.IsBitField()) {
+      [[likely]] shift = 0;        // reset shift on non-bitfield element
+      offset += prevBitfieldSize;  // finish bitfield if active
+      prevBitfieldSize = 0;        // previous is now not bitfield
+      field.m_offset = offset;
+      if (field.m_struct) {
+        if (!field.m_struct->IsValid()) {
+          m_valid = false;
+          [[unlikely]] return {};
+        }
+        field.m_size = field.m_struct->m_size;
+      }
+      offset += field.m_size * field.m_arraySize;
+    } else {
+      if (field.m_type == StructFieldType::kBool && prevBitfieldSize != 0 &&
+          (shift + 1) <= (prevBitfieldSize * 8)) {
+        // bool takes on size of preceding bitfield type (if it fits)
+        field.m_size = prevBitfieldSize;
+      } else if (field.m_size != prevBitfieldSize ||
+                 (shift + field.m_bitWidth) > (field.m_size * 8)) {
+        shift = 0;
+        offset += prevBitfieldSize;
+      }
+      prevBitfieldSize = field.m_size;
+      field.m_offset = offset;
+      field.m_bitShift = shift;
+      shift += field.m_bitWidth;
+    }
+  }
+
+  // update struct size
+  m_size = offset + prevBitfieldSize;
+  m_valid = true;
+
+  // now that we're valid, referring types may be too
+  stack.emplace_back(this);
+  for (auto&& ref : m_references) {
+    if (std::find(stack.begin(), stack.end(), ref) != stack.end()) {
+      [[unlikely]] return fmt::format(
+          "internal error (inconsistent data): circular struct reference "
+          "between {} and {}",
+          m_name, ref->m_name);
+    }
+    auto err = ref->CalculateOffsets(stack);
+    if (!err.empty()) {
+      [[unlikely]] return err;
+    }
+  }
+  stack.pop_back();
+  return {};
+}
+
+const StructDescriptor* StructDescriptorDatabase::Add(std::string_view name,
+                                                      std::string_view schema,
+                                                      std::string* err) {
+  structparser::Parser parser{schema};
+  structparser::ParsedSchema parsed;
+  if (!parser.Parse(&parsed)) {
+    *err = fmt::format("parse error: {}", parser.GetError());
+    [[unlikely]] return nullptr;
+  }
+
+  // turn parsed schema into descriptors
+  auto& theStruct = m_structs[name];
+  if (!theStruct) {
+    theStruct = std::make_unique<StructDescriptor>(
+        name, StructDescriptor::private_init{});
+  }
+  theStruct->m_schema = schema;
+  theStruct->m_fields.clear();
+  theStruct->m_fields.reserve(parsed.declarations.size());
+  bool isValid = true;
+  for (auto&& decl : parsed.declarations) {
+    auto type = TypeStringToType(decl.typeString);
+    size_t size = TypeToSize(type);
+
+    // bitfield checks
+    if (decl.bitWidth != 0) {
+      // only integer or boolean types are allowed
+      if (type == StructFieldType::kChar || type == StructFieldType::kFloat ||
+          type == StructFieldType::kDouble ||
+          type == StructFieldType::kStruct) {
+        *err = fmt::format("field {}: type {} cannot be bitfield", decl.name,
+                           decl.typeString);
+        [[unlikely]] return nullptr;
+      }
+
+      // bit width cannot be larger than field size
+      if (decl.bitWidth > (size * 8)) {
+        *err = fmt::format("field {}: bit width {} exceeds type size",
+                           decl.name, decl.bitWidth);
+        [[unlikely]] return nullptr;
+      }
+
+      // bit width must be 1 for booleans
+      if (type == StructFieldType::kBool && decl.bitWidth != 1) {
+        *err = fmt::format("field {}: bit width must be 1 for bool type",
+                           decl.name);
+        [[unlikely]] return nullptr;
+      }
+
+      // cannot combine array and bitfield (shouldn't parse, but double-check)
+      if (decl.arraySize > 1) {
+        *err = fmt::format("field {}: cannot combine array and bitfield",
+                           decl.name);
+        [[unlikely]] return nullptr;
+      }
+    }
+
+    // struct handling
+    const StructDescriptor* structDesc = nullptr;
+    if (type == StructFieldType::kStruct) {
+      // recursive definitions are not allowed
+      if (decl.typeString == name) {
+        *err = fmt::format("field {}: recursive struct reference", decl.name);
+        [[unlikely]] return nullptr;
+      }
+
+      // cross-reference struct, creating a placeholder if necessary
+      auto& aStruct = m_structs[decl.typeString];
+      if (!aStruct) {
+        aStruct = std::make_unique<StructDescriptor>(
+            decl.typeString, StructDescriptor::private_init{});
+      }
+
+      // if the struct isn't valid, we can't be valid either
+      if (aStruct->IsValid()) {
+        size = aStruct->GetSize();
+      } else {
+        isValid = false;
+      }
+
+      // add to cross-references for when the struct does become valid
+      aStruct->m_references.emplace_back(theStruct.get());
+      structDesc = aStruct.get();
+    }
+
+    // create field
+    if (!theStruct->m_fieldsByName
+             .insert({decl.name, theStruct->m_fields.size()})
+             .second) {
+      *err = fmt::format("duplicate field {}", decl.name);
+      [[unlikely]] return nullptr;
+    }
+
+    theStruct->m_fields.emplace_back(theStruct.get(), decl.name, type, size,
+                                     decl.arraySize, decl.bitWidth,
+                                     std::move(decl.enumValues), structDesc,
+                                     StructFieldDescriptor::private_init{});
+  }
+
+  theStruct->m_valid = isValid;
+  if (isValid) {
+    // we have all the info needed, so calculate field offset & shift
+    wpi::SmallVector<const StructDescriptor*, 16> stack;
+    auto err2 = theStruct->CalculateOffsets(stack);
+    if (!err2.empty()) {
+      *err = std::move(err2);
+      [[unlikely]] return nullptr;
+    }
+  } else {
+    // check for circular reference
+    wpi::SmallVector<const StructDescriptor*, 16> stack;
+    if (!theStruct->CheckCircular(stack)) {
+      wpi::SmallString<128> buf;
+      wpi::raw_svector_ostream os{buf};
+      for (auto&& elem : stack) {
+        if (!buf.empty()) {
+          os << " <- ";
+        }
+        os << elem->GetName();
+      }
+      *err = fmt::format("circular struct reference: {}", os.str());
+      [[unlikely]] return nullptr;
+    }
+  }
+
+  return theStruct.get();
+}
+
+const StructDescriptor* StructDescriptorDatabase::Find(
+    std::string_view name) const {
+  auto it = m_structs.find(name);
+  if (it == m_structs.end()) {
+    return nullptr;
+  }
+  return it->second.get();
+}
+
+uint64_t DynamicStruct::GetFieldImpl(const StructFieldDescriptor* field,
+                                     size_t arrIndex) const {
+  assert(field->m_parent == m_desc);
+  assert(m_desc->IsValid());
+  assert(arrIndex < field->m_arraySize);
+  uint64_t val;
+  switch (field->m_size) {
+    case 1:
+      val = m_data[field->m_offset + arrIndex];
+      break;
+    case 2:
+      val = support::endian::read16le(&m_data[field->m_offset + arrIndex * 2]);
+      break;
+    case 4:
+      val = support::endian::read32le(&m_data[field->m_offset + arrIndex * 4]);
+      break;
+    case 8:
+      val = support::endian::read64le(&m_data[field->m_offset + arrIndex * 8]);
+      break;
+    default:
+      assert(false && "invalid field size");
+      return 0;
+  }
+  return (val >> field->m_bitShift) & field->m_bitMask;
+}
+
+void MutableDynamicStruct::SetData(std::span<const uint8_t> data) {
+  assert(data.size() >= m_desc->GetSize());
+  std::copy(data.begin(), data.begin() + m_desc->GetSize(), m_data.begin());
+}
+
+void MutableDynamicStruct::SetStringField(const StructFieldDescriptor* field,
+                                          std::string_view value) {
+  assert(field->m_type == StructFieldType::kChar);
+  assert(field->m_parent == m_desc);
+  assert(m_desc->IsValid());
+  size_t len = (std::min)(field->m_arraySize, value.size());
+  std::copy(value.begin(), value.begin() + len,
+            reinterpret_cast<char*>(&m_data[field->m_offset]));
+  std::fill(&m_data[field->m_offset + len],
+            &m_data[field->m_offset + field->m_arraySize], 0);
+}
+
+void MutableDynamicStruct::SetStructField(const StructFieldDescriptor* field,
+                                          const DynamicStruct& value,
+                                          size_t arrIndex) {
+  assert(field->m_type == StructFieldType::kStruct);
+  assert(field->m_parent == m_desc);
+  assert(m_desc->IsValid());
+  assert(value.GetDescriptor() == field->m_struct);
+  assert(value.GetDescriptor()->IsValid());
+  assert(arrIndex < field->m_arraySize);
+  auto source = value.GetData();
+  size_t len = field->m_struct->GetSize();
+  std::copy(source.begin(), source.begin() + len,
+            m_data.begin() + field->m_offset + arrIndex * len);
+}
+
+void MutableDynamicStruct::SetFieldImpl(const StructFieldDescriptor* field,
+                                        uint64_t value, size_t arrIndex) {
+  assert(field->m_parent == m_desc);
+  assert(m_desc->IsValid());
+  assert(arrIndex < field->m_arraySize);
+
+  // common case is no bit shift and no masking
+  if (!field->IsBitField()) {
+    switch (field->m_size) {
+      case 1:
+        m_data[field->m_offset + arrIndex] = value;
+        break;
+      case 2:
+        support::endian::write16le(&m_data[field->m_offset + arrIndex * 2],
+                                   value);
+        break;
+      case 4:
+        support::endian::write32le(&m_data[field->m_offset + arrIndex * 4],
+                                   value);
+        break;
+      case 8:
+        support::endian::write64le(&m_data[field->m_offset + arrIndex * 8],
+                                   value);
+        break;
+      default:
+        assert(false && "invalid field size");
+    }
+    return;
+  }
+
+  // handle bit shifting and masking into current value
+  switch (field->m_size) {
+    case 1: {
+      uint8_t* data = &m_data[field->m_offset + arrIndex];
+      *data &= ~(field->m_bitMask << field->m_bitShift);
+      *data |= (value & field->m_bitMask) << field->m_bitShift;
+      break;
+    }
+    case 2: {
+      uint8_t* data = &m_data[field->m_offset + arrIndex * 2];
+      uint16_t val = support::endian::read16le(data);
+      val &= ~(field->m_bitMask << field->m_bitShift);
+      val |= (value & field->m_bitMask) << field->m_bitShift;
+      support::endian::write16le(data, val);
+      break;
+    }
+    case 4: {
+      uint8_t* data = &m_data[field->m_offset + arrIndex * 4];
+      uint32_t val = support::endian::read32le(data);
+      val &= ~(field->m_bitMask << field->m_bitShift);
+      val |= (value & field->m_bitMask) << field->m_bitShift;
+      support::endian::write32le(data, val);
+      break;
+    }
+    case 8: {
+      uint8_t* data = &m_data[field->m_offset + arrIndex * 8];
+      uint64_t val = support::endian::read64le(data);
+      val &= ~(field->m_bitMask << field->m_bitShift);
+      val |= (value & field->m_bitMask) << field->m_bitShift;
+      support::endian::write64le(data, val);
+      break;
+    }
+    default:
+      assert(false && "invalid field size");
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/struct/SchemaParser.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/struct/SchemaParser.cpp
new file mode 100644
index 0000000..347019e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/struct/SchemaParser.cpp
@@ -0,0 +1,238 @@
+// 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/struct/SchemaParser.h"
+
+#include <fmt/format.h>
+
+#include "wpi/StringExtras.h"
+
+using namespace wpi::structparser;
+
+std::string_view wpi::structparser::ToString(Token::Kind kind) {
+  switch (kind) {
+    case Token::kInteger:
+      return "integer";
+    case Token::kIdentifier:
+      return "identifier";
+    case Token::kLeftBracket:
+      return "'['";
+    case Token::kRightBracket:
+      return "']'";
+    case Token::kLeftBrace:
+      return "'{'";
+    case Token::kRightBrace:
+      return "'}'";
+    case Token::kColon:
+      return "':'";
+    case Token::kSemicolon:
+      return "';'";
+    case Token::kComma:
+      return "','";
+    case Token::kEquals:
+      return "'='";
+    case Token::kEndOfInput:
+      return "<EOF>";
+    default:
+      return "unknown";
+  }
+}
+
+Token Lexer::Scan() {
+  // skip whitespace
+  do {
+    Get();
+  } while (m_current == ' ' || m_current == '\t' || m_current == '\n' ||
+           m_current == '\r');
+  m_tokenStart = m_pos - 1;
+
+  switch (m_current) {
+    case '[':
+      return MakeToken(Token::kLeftBracket);
+    case ']':
+      return MakeToken(Token::kRightBracket);
+    case '{':
+      return MakeToken(Token::kLeftBrace);
+    case '}':
+      return MakeToken(Token::kRightBrace);
+    case ':':
+      return MakeToken(Token::kColon);
+    case ';':
+      return MakeToken(Token::kSemicolon);
+    case ',':
+      return MakeToken(Token::kComma);
+    case '=':
+      return MakeToken(Token::kEquals);
+    case '-':
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      return ScanInteger();
+    case -1:
+      return {Token::kEndOfInput, {}};
+    default:
+      if (isAlpha(m_current) || m_current == '_') {
+        [[likely]] return ScanIdentifier();
+      }
+      return MakeToken(Token::kUnknown);
+  }
+}
+
+Token Lexer::ScanInteger() {
+  do {
+    Get();
+  } while (isDigit(m_current));
+  Unget();
+  return MakeToken(Token::kInteger);
+}
+
+Token Lexer::ScanIdentifier() {
+  do {
+    Get();
+  } while (isAlnum(m_current) || m_current == '_');
+  Unget();
+  return MakeToken(Token::kIdentifier);
+}
+
+void Parser::FailExpect(Token::Kind desired) {
+  Fail(fmt::format("expected {}, got '{}'", ToString(desired), m_token.text));
+}
+
+void Parser::Fail(std::string_view msg) {
+  m_error = fmt::format("{}: {}", m_lexer.GetPosition(), msg);
+}
+
+bool Parser::Parse(ParsedSchema* out) {
+  do {
+    GetNextToken();
+    if (m_token.Is(Token::kSemicolon)) {
+      continue;
+    }
+    if (m_token.Is(Token::kEndOfInput)) {
+      break;
+    }
+    if (!ParseDeclaration(&out->declarations.emplace_back())) {
+      [[unlikely]] return false;
+    }
+  } while (m_token.kind != Token::kEndOfInput);
+  return true;
+}
+
+bool Parser::ParseDeclaration(ParsedDeclaration* out) {
+  // optional enum specification
+  if (m_token.Is(Token::kIdentifier) && m_token.text == "enum") {
+    GetNextToken();
+    if (!Expect(Token::kLeftBrace)) {
+      [[unlikely]] return false;
+    }
+    if (!ParseEnum(&out->enumValues)) {
+      [[unlikely]] return false;
+    }
+    GetNextToken();
+  } else if (m_token.Is(Token::kLeftBrace)) {
+    if (!ParseEnum(&out->enumValues)) {
+      [[unlikely]] return false;
+    }
+    GetNextToken();
+  }
+
+  // type name
+  if (!Expect(Token::kIdentifier)) {
+    [[unlikely]] return false;
+  }
+  out->typeString = m_token.text;
+  GetNextToken();
+
+  // identifier name
+  if (!Expect(Token::kIdentifier)) {
+    [[unlikely]] return false;
+  }
+  out->name = m_token.text;
+  GetNextToken();
+
+  // array or bit field
+  if (m_token.Is(Token::kLeftBracket)) {
+    GetNextToken();
+    if (!Expect(Token::kInteger)) {
+      [[unlikely]] return false;
+    }
+    auto val = parse_integer<uint64_t>(m_token.text, 10);
+    if (val && *val > 0) {
+      out->arraySize = *val;
+    } else {
+      Fail(fmt::format("array size '{}' is not a positive integer",
+                       m_token.text));
+      [[unlikely]] return false;
+    }
+    GetNextToken();
+    if (!Expect(Token::kRightBracket)) {
+      [[unlikely]] return false;
+    }
+    GetNextToken();
+  } else if (m_token.Is(Token::kColon)) {
+    GetNextToken();
+    if (!Expect(Token::kInteger)) {
+      [[unlikely]] return false;
+    }
+    auto val = parse_integer<unsigned int>(m_token.text, 10);
+    if (val && *val > 0) {
+      out->bitWidth = *val;
+    } else {
+      Fail(fmt::format("bitfield width '{}' is not a positive integer",
+                       m_token.text));
+      [[unlikely]] return false;
+    }
+    GetNextToken();
+  }
+
+  // declaration must end with EOF or semicolon
+  if (m_token.Is(Token::kEndOfInput)) {
+    return true;
+  }
+  return Expect(Token::kSemicolon);
+}
+
+bool Parser::ParseEnum(EnumValues* out) {
+  // we start with current = '{'
+  GetNextToken();
+  while (!m_token.Is(Token::kRightBrace)) {
+    if (!Expect(Token::kIdentifier)) {
+      [[unlikely]] return false;
+    }
+    std::string name;
+    name = m_token.text;
+    GetNextToken();
+    if (!Expect(Token::kEquals)) {
+      [[unlikely]] return false;
+    }
+    GetNextToken();
+    if (!Expect(Token::kInteger)) {
+      [[unlikely]] return false;
+    }
+    int64_t value;
+    if (auto val = parse_integer<int64_t>(m_token.text, 10)) {
+      value = *val;
+    } else {
+      Fail(fmt::format("could not parse enum value '{}'", m_token.text));
+      [[unlikely]] return false;
+    }
+    out->emplace_back(std::move(name), value);
+    GetNextToken();
+    if (m_token.Is(Token::kRightBrace)) {
+      break;
+    }
+    if (!Expect(Token::kComma)) {
+      [[unlikely]] return false;
+    }
+    GetNextToken();
+  }
+  return true;
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/native/cpp/timestamp.cpp b/third_party/allwpilib/wpiutil/src/main/native/cpp/timestamp.cpp
index 521197f..c7e2fa9 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/cpp/timestamp.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/cpp/timestamp.cpp
@@ -6,6 +6,25 @@
 
 #include <atomic>
 
+#ifdef __FRC_ROBORIO__
+#include <stdint.h>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#pragma GCC diagnostic ignored "-Wignored-qualifiers"
+#include <FRC_FPGA_ChipObject/RoboRIO_FRC_ChipObject_Aliases.h>
+#include <FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/nInterfaceGlobals.h>
+#include <FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tHMB.h>
+#include <FRC_NetworkCommunication/LoadOut.h>
+#pragma GCC diagnostic pop
+namespace fpga {
+using namespace nFPGA;
+using namespace nRoboRIO_FPGANamespace;
+}  // namespace fpga
+#include <memory>
+
+#include "dlfcn.h"
+#endif
+
 #ifdef _WIN32
 #include <windows.h>
 
@@ -15,6 +34,85 @@
 #include <chrono>
 #endif
 
+#include <cstdio>
+
+#include <fmt/format.h>
+
+#ifdef __FRC_ROBORIO__
+namespace {
+static constexpr const char hmbName[] = "HMB_0_RAM";
+static constexpr int timestampLowerOffset = 0xF0;
+static constexpr int timestampUpperOffset = 0xF1;
+static constexpr int hmbTimestampOffset = 5;  // 5 us offset
+using NiFpga_CloseHmbFunc = NiFpga_Status (*)(const NiFpga_Session session,
+                                              const char* memoryName);
+using NiFpga_OpenHmbFunc = NiFpga_Status (*)(const NiFpga_Session session,
+                                             const char* memoryName,
+                                             size_t* memorySize,
+                                             void** virtualAddress);
+struct HMBHolder {
+  ~HMBHolder() {
+    if (hmb) {
+      closeHmb(hmb->getSystemInterface()->getHandle(), hmbName);
+      dlclose(niFpga);
+    }
+  }
+  explicit operator bool() const { return hmb != nullptr; }
+  void Configure() {
+    nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass =
+        nLoadOut::getTargetClass();
+    int32_t status = 0;
+    hmb.reset(fpga::tHMB::create(&status));
+    niFpga = dlopen("libNiFpga.so", RTLD_LAZY);
+    if (!niFpga) {
+      hmb = nullptr;
+      return;
+    }
+    NiFpga_OpenHmbFunc openHmb = reinterpret_cast<NiFpga_OpenHmbFunc>(
+        dlsym(niFpga, "NiFpgaDll_OpenHmb"));
+    closeHmb = reinterpret_cast<NiFpga_CloseHmbFunc>(
+        dlsym(niFpga, "NiFpgaDll_CloseHmb"));
+    if (openHmb == nullptr || closeHmb == nullptr) {
+      closeHmb = nullptr;
+      dlclose(niFpga);
+      hmb = nullptr;
+      return;
+    }
+    size_t hmbBufferSize = 0;
+    status =
+        openHmb(hmb->getSystemInterface()->getHandle(), hmbName, &hmbBufferSize,
+                reinterpret_cast<void**>(const_cast<uint32_t**>(&hmbBuffer)));
+    if (status != 0) {
+      closeHmb = nullptr;
+      dlclose(niFpga);
+      hmb = nullptr;
+      return;
+    }
+    auto cfg = hmb->readConfig(&status);
+    cfg.Enables_Timestamp = 1;
+    hmb->writeConfig(cfg, &status);
+  }
+  void Reset() {
+    if (hmb) {
+      std::unique_ptr<fpga::tHMB> oldHmb;
+      oldHmb.swap(hmb);
+      closeHmb(oldHmb->getSystemInterface()->getHandle(), hmbName);
+      closeHmb = nullptr;
+      hmbBuffer = nullptr;
+      oldHmb.reset();
+      dlclose(niFpga);
+      niFpga = nullptr;
+    }
+  }
+  std::unique_ptr<fpga::tHMB> hmb;
+  void* niFpga = nullptr;
+  NiFpga_CloseHmbFunc closeHmb = nullptr;
+  volatile uint32_t* hmbBuffer = nullptr;
+};
+static HMBHolder hmb;
+}  // namespace
+#endif
+
 // offset in microseconds
 static uint64_t time_since_epoch() noexcept {
 #ifdef _WIN32
@@ -22,7 +120,7 @@
   uint64_t tmpres = 0;
   // 100-nanosecond intervals since January 1, 1601 (UTC)
   // which means 0.1 us
-  GetSystemTimeAsFileTime(&ft);
+  GetSystemTimePreciseAsFileTime(&ft);
   tmpres |= ft.dwHighDateTime;
   tmpres <<= 32;
   tmpres |= ft.dwLowDateTime;
@@ -88,12 +186,56 @@
 
 static std::atomic<uint64_t (*)()> now_impl{wpi::NowDefault};
 
+void wpi::impl::SetupNowRio() {
+#ifdef __FRC_ROBORIO__
+  if (!hmb) {
+    hmb.Configure();
+  }
+#endif
+}
+
+void wpi::impl::ShutdownNowRio() {
+#ifdef __FRC_ROBORIO__
+  hmb.Reset();
+#endif
+}
+
 void wpi::SetNowImpl(uint64_t (*func)(void)) {
   now_impl = func ? func : NowDefault;
 }
 
 uint64_t wpi::Now() {
+#ifdef __FRC_ROBORIO__
+  // Same code as HAL_GetFPGATime()
+  if (!hmb) {
+    std::fputs(
+        "FPGA not yet configured in wpi::Now(). Time will not be correct",
+        stderr);
+    std::fflush(stderr);
+    return 0;
+  }
+
+  asm("dmb");
+  uint64_t upper1 = hmb.hmbBuffer[timestampUpperOffset];
+  asm("dmb");
+  uint32_t lower = hmb.hmbBuffer[timestampLowerOffset];
+  asm("dmb");
+  uint64_t upper2 = hmb.hmbBuffer[timestampUpperOffset];
+
+  if (upper1 != upper2) {
+    // Rolled over between the lower call, reread lower
+    asm("dmb");
+    lower = hmb.hmbBuffer[timestampLowerOffset];
+  }
+  // 5 is added here because the time to write from the FPGA
+  // to the HMB buffer is longer then the time to read
+  // from the time register. This would cause register based
+  // timestamps to be ahead of HMB timestamps, which could
+  // be very bad.
+  return (upper2 << 32) + lower + hmbTimestampOffset;
+#else
   return (now_impl.load())();
+#endif
 }
 
 uint64_t wpi::GetSystemTime() {
@@ -102,6 +244,14 @@
 
 extern "C" {
 
+void WPI_Impl_SetupNowRio(void) {
+  return wpi::impl::SetupNowRio();
+}
+
+void WPI_Impl_ShutdownNowRio(void) {
+  return wpi::impl::ShutdownNowRio();
+}
+
 uint64_t WPI_NowDefault(void) {
   return wpi::NowDefault();
 }
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Base64.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Base64.h
index c410bd0..858ff73 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Base64.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Base64.h
@@ -5,6 +5,8 @@
 #ifndef WPIUTIL_WPI_BASE64_H_
 #define WPIUTIL_WPI_BASE64_H_
 
+#include <stdint.h>
+
 #include <cstddef>
 #include <span>
 #include <string>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/DataLog.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/DataLog.h
index cac273c..99db964 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/DataLog.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/DataLog.h
@@ -6,19 +6,42 @@
 
 #include <stdint.h>
 
+#ifdef __cplusplus
+#include <concepts>
 #include <functional>
 #include <initializer_list>
 #include <memory>
+#include <ranges>
 #include <span>
 #include <string>
 #include <string_view>
 #include <thread>
+#include <utility>
 #include <vector>
+#include <version>
 
 #include "wpi/DenseMap.h"
+#include "wpi/SmallVector.h"
 #include "wpi/StringMap.h"
 #include "wpi/condition_variable.h"
 #include "wpi/mutex.h"
+#include "wpi/protobuf/Protobuf.h"
+#include "wpi/struct/Struct.h"
+#include "wpi/timestamp.h"
+#endif  // __cplusplus
+
+/**
+ * A datalog string (for use with string array).
+ */
+struct WPI_DataLog_String {
+  /** Contents. */
+  const char* str;
+
+  /** Length. */
+  size_t len;
+};
+
+#ifdef __cplusplus
 
 namespace wpi {
 class Logger;
@@ -151,11 +174,108 @@
   void Pause();
 
   /**
-   * Resumes appending of data records to the log.
+   * Resumes appending of data records to the log.  If called after Stop(),
+   * opens a new file (with random name if SetFilename was not called after
+   * Stop()) and appends Start records and schema data values for all previously
+   * started entries and schemas.
    */
   void Resume();
 
   /**
+   * Stops appending all records to the log, and closes the log file.
+   */
+  void Stop();
+
+  /**
+   * Returns whether there is a data schema already registered with the given
+   * name.
+   *
+   * @param name Name (the string passed as the data type for records using this
+   *             schema)
+   * @return True if schema already registered
+   */
+  bool HasSchema(std::string_view name) const;
+
+  /**
+   * Registers a data schema.  Data schemas provide information for how a
+   * certain data type string can be decoded.  The type string of a data schema
+   * indicates the type of the schema itself (e.g. "protobuf" for protobuf
+   * schemas, "struct" for struct schemas, etc). In the data log, schemas are
+   * saved just like normal records, with the name being generated from the
+   * provided name: "/.schema/<name>".  Duplicate calls to this function with
+   * the same name are silently ignored.
+   *
+   * @param name Name (the string passed as the data type for records using this
+   *             schema)
+   * @param type Type of schema (e.g. "protobuf", "struct", etc)
+   * @param schema Schema data
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
+  void AddSchema(std::string_view name, std::string_view type,
+                 std::span<const uint8_t> schema, int64_t timestamp = 0);
+
+  /**
+   * Registers a data schema.  Data schemas provide information for how a
+   * certain data type string can be decoded.  The type string of a data schema
+   * indicates the type of the schema itself (e.g. "protobuf" for protobuf
+   * schemas, "struct" for struct schemas, etc). In the data log, schemas are
+   * saved just like normal records, with the name being generated from the
+   * provided name: "/.schema/<name>".  Duplicate calls to this function with
+   * the same name are silently ignored.
+   *
+   * @param name Name (the string passed as the data type for records using this
+   *             schema)
+   * @param type Type of schema (e.g. "protobuf", "struct", etc)
+   * @param schema Schema data
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
+  void AddSchema(std::string_view name, std::string_view type,
+                 std::string_view schema, int64_t timestamp = 0) {
+    AddSchema(
+        name, type,
+        std::span<const uint8_t>{
+            reinterpret_cast<const uint8_t*>(schema.data()), schema.size()},
+        timestamp);
+  }
+
+  /**
+   * Registers a protobuf schema. Duplicate calls to this function with the same
+   * name are silently ignored.
+   *
+   * @tparam T protobuf serializable type
+   * @param msg protobuf message
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  template <ProtobufSerializable T>
+  void AddProtobufSchema(ProtobufMessage<T>& msg, int64_t timestamp = 0) {
+    if (timestamp == 0) {
+      timestamp = Now();
+    }
+    msg.ForEachProtobufDescriptor(
+        [this](auto typeString) { return HasSchema(typeString); },
+        [this, timestamp](auto typeString, auto schema) {
+          AddSchema(typeString, "proto:FileDescriptorProto", schema, timestamp);
+        });
+  }
+
+  /**
+   * Registers a struct schema. Duplicate calls to this function with the same
+   * name are silently ignored.
+   *
+   * @tparam T struct serializable type
+   * @param timestamp Time stamp (0 to indicate now)
+   */
+  template <StructSerializable T>
+  void AddStructSchema(int64_t timestamp = 0) {
+    if (timestamp == 0) {
+      timestamp = Now();
+    }
+    ForEachStructSchema<T>([this, timestamp](auto typeString, auto schema) {
+      AddSchema(typeString, "structschema", schema, timestamp);
+    });
+  }
+
+  /**
    * Start an entry.  Duplicate names are allowed (with the same type), and
    * result in the same index being returned (Start/Finish are reference
    * counted).  A duplicate name with a different type will result in an error
@@ -190,64 +310,189 @@
   void SetMetadata(int entry, std::string_view metadata, int64_t timestamp = 0);
 
   /**
-   * Appends a record to the log.
+   * Appends a raw record to the log.
    *
    * @param entry Entry index, as returned by Start()
-   * @param data Data to record
+   * @param data Byte array to record
    * @param timestamp Time stamp (may be 0 to indicate now)
    */
   void AppendRaw(int entry, std::span<const uint8_t> data, int64_t timestamp);
 
   /**
-   * Appends a record to the log.
+   * Appends a raw record to the log.
    *
    * @param entry Entry index, as returned by Start()
-   * @param data Data to record
+   * @param data Byte array to record
    * @param timestamp Time stamp (may be 0 to indicate now)
    */
   void AppendRaw2(int entry, std::span<const std::span<const uint8_t>> data,
                   int64_t timestamp);
 
+  /**
+   * Appends a boolean record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param value Boolean value to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendBoolean(int entry, bool value, int64_t timestamp);
+
+  /**
+   * Appends an integer record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param value Integer value to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendInteger(int entry, int64_t value, int64_t timestamp);
+
+  /**
+   * Appends a float record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param value Float value to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendFloat(int entry, float value, int64_t timestamp);
+
+  /**
+   * Appends a double record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param value Double value to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendDouble(int entry, double value, int64_t timestamp);
+
+  /**
+   * Appends a string record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param value String value to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendString(int entry, std::string_view value, int64_t timestamp);
+
+  /**
+   * Appends a boolean array record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param arr Boolean array to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendBooleanArray(int entry, std::span<const bool> arr,
                           int64_t timestamp);
+
+  /**
+   * Appends a boolean array record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param arr Boolean array to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendBooleanArray(int entry, std::span<const int> arr,
                           int64_t timestamp);
+
+  /**
+   * Appends a boolean array record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param arr Boolean array to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendBooleanArray(int entry, std::span<const uint8_t> arr,
                           int64_t timestamp);
+
+  /**
+   * Appends an integer array record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param arr Integer array to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendIntegerArray(int entry, std::span<const int64_t> arr,
                           int64_t timestamp);
+
+  /**
+   * Appends a float array record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param arr Float array to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendFloatArray(int entry, std::span<const float> arr,
                         int64_t timestamp);
+
+  /**
+   * Appends a double array record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param arr Double array to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendDoubleArray(int entry, std::span<const double> arr,
                          int64_t timestamp);
+
+  /**
+   * Appends a string array record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param arr String array to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendStringArray(int entry, std::span<const std::string> arr,
                          int64_t timestamp);
+
+  /**
+   * Appends a string array record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param arr String array to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
   void AppendStringArray(int entry, std::span<const std::string_view> arr,
                          int64_t timestamp);
 
+  /**
+   * Appends a string array record to the log.
+   *
+   * @param entry Entry index, as returned by Start()
+   * @param arr String array to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
+  void AppendStringArray(int entry, std::span<const WPI_DataLog_String> arr,
+                         int64_t timestamp);
+
  private:
+  struct WriterThreadState;
+
+  void StartLogFile(WriterThreadState& state);
   void WriterThreadMain(std::string_view dir);
   void WriterThreadMain(
       std::function<void(std::span<const uint8_t> data)> write);
 
   // must be called with m_mutex held
+  int StartImpl(std::string_view name, std::string_view type,
+                std::string_view metadata, int64_t timestamp);
   uint8_t* StartRecord(uint32_t entry, uint64_t timestamp, uint32_t payloadSize,
                        size_t reserveSize);
   uint8_t* Reserve(size_t size);
   void AppendImpl(std::span<const uint8_t> data);
   void AppendStringImpl(std::string_view str);
+  void AppendStartRecord(int id, std::string_view name, std::string_view type,
+                         std::string_view metadata, int64_t timestamp);
 
   wpi::Logger& m_msglog;
   mutable wpi::mutex m_mutex;
   wpi::condition_variable m_cond;
-  bool m_active{true};
   bool m_doFlush{false};
-  bool m_paused{false};
+  enum State {
+    kStart,
+    kActive,
+    kPaused,
+    kStopped,
+    kShutdown,
+  } m_state = kActive;
   double m_period;
   std::string m_extraHeader;
   std::string m_newFilename;
@@ -256,10 +501,15 @@
   std::vector<Buffer> m_outgoing;
   struct EntryInfo {
     std::string type;
+    std::vector<uint8_t> schemaData;  // only set for schema entries
     int id{0};
   };
   wpi::StringMap<EntryInfo> m_entries;
-  wpi::DenseMap<int, unsigned int> m_entryCounts;
+  struct EntryInfo2 {
+    std::string metadata;
+    unsigned int count;
+  };
+  wpi::DenseMap<int, EntryInfo2> m_entryIds;
   int m_lastId = 0;
   std::thread m_thread;
 };
@@ -693,4 +943,401 @@
   }
 };
 
+/**
+ * Log raw struct serializable objects.
+ */
+template <StructSerializable T>
+class StructLogEntry : public DataLogEntry {
+  using S = Struct<T>;
+
+ public:
+  StructLogEntry() = default;
+  StructLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
+      : StructLogEntry{log, name, {}, timestamp} {}
+  StructLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
+                 int64_t timestamp = 0) {
+    m_log = &log;
+    log.AddStructSchema<T>(timestamp);
+    m_entry = log.Start(name, S::kTypeString, metadata, timestamp);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param data Data to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
+  void Append(const T& data, int64_t timestamp = 0) {
+    uint8_t buf[S::kSize];
+    S::Pack(buf, data);
+    m_log->AppendRaw(m_entry, buf, timestamp);
+  }
+};
+
+/**
+ * Log raw struct serializable array of objects.
+ */
+template <StructSerializable T>
+class StructArrayLogEntry : public DataLogEntry {
+  using S = Struct<T>;
+
+ public:
+  StructArrayLogEntry() = default;
+  StructArrayLogEntry(DataLog& log, std::string_view name,
+                      int64_t timestamp = 0)
+      : StructArrayLogEntry{log, name, {}, timestamp} {}
+  StructArrayLogEntry(DataLog& log, std::string_view name,
+                      std::string_view metadata, int64_t timestamp = 0) {
+    m_log = &log;
+    log.AddStructSchema<T>(timestamp);
+    m_entry =
+        log.Start(name, MakeStructArrayTypeString<T, std::dynamic_extent>(),
+                  metadata, timestamp);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param data Data to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
+  template <typename U>
+#if __cpp_lib_ranges >= 201911L
+    requires std::ranges::range<U> &&
+             std::convertible_to<std::ranges::range_value_t<U>, T>
+#endif
+  void Append(U&& data, int64_t timestamp = 0) {
+    m_buf.Write(std::forward<U>(data), [&](auto bytes) {
+      m_log->AppendRaw(m_entry, bytes, timestamp);
+    });
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param data Data to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
+  void Append(std::span<const T> data, int64_t timestamp = 0) {
+    m_buf.Write(
+        data, [&](auto bytes) { m_log->AppendRaw(m_entry, bytes, timestamp); });
+  }
+
+ private:
+  StructArrayBuffer<T> m_buf;
+};
+
+/**
+ * Log protobuf serializable objects.
+ */
+template <ProtobufSerializable T>
+class ProtobufLogEntry : public DataLogEntry {
+  using P = Protobuf<T>;
+
+ public:
+  ProtobufLogEntry() = default;
+  ProtobufLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
+      : ProtobufLogEntry{log, name, {}, timestamp} {}
+  ProtobufLogEntry(DataLog& log, std::string_view name,
+                   std::string_view metadata, int64_t timestamp = 0) {
+    m_log = &log;
+    log.AddProtobufSchema<T>(m_msg, timestamp);
+    m_entry = log.Start(name, m_msg.GetTypeString(), metadata, timestamp);
+  }
+
+  /**
+   * Appends a record to the log.
+   *
+   * @param data Data to record
+   * @param timestamp Time stamp (may be 0 to indicate now)
+   */
+  void Append(const T& data, int64_t timestamp = 0) {
+    SmallVector<uint8_t, 128> buf;
+    {
+      std::scoped_lock lock{m_mutex};
+      m_msg.Pack(buf, data);
+    }
+    m_log->AppendRaw(m_entry, buf, timestamp);
+  }
+
+ private:
+  wpi::mutex m_mutex;
+  ProtobufMessage<T> m_msg;
+};
+
 }  // namespace wpi::log
+
+extern "C" {
+#endif  // __cplusplus
+
+/** C-compatible data log (opaque struct). */
+struct WPI_DataLog;
+
+/**
+ * Construct a new Data Log.  The log will be initially created with a
+ * temporary filename.
+ *
+ * @param dir directory to store the log
+ * @param filename filename to use; if none provided, a random filename is
+ *                 generated of the form "wpilog_{}.wpilog"
+ * @param period time between automatic flushes to disk, in seconds;
+ *               this is a time/storage tradeoff
+ * @param extraHeader extra header data
+ */
+struct WPI_DataLog* WPI_DataLog_Create(const char* dir, const char* filename,
+                                       double period, const char* extraHeader);
+
+/**
+ * Construct a new Data Log that passes its output to the provided function
+ * rather than a file.  The write function will be called on a separate
+ * background thread and may block.  The write function is called with an
+ * empty data array (data=NULL, len=0) when the thread is terminating.
+ *
+ * @param write write function
+ * @param ptr pointer to pass to write function ptr parameter
+ * @param period time between automatic calls to write, in seconds;
+ *               this is a time/storage tradeoff
+ * @param extraHeader extra header data
+ */
+struct WPI_DataLog* WPI_DataLog_Create_Func(
+    void (*write)(void* ptr, const uint8_t* data, size_t len), void* ptr,
+    double period, const char* extraHeader);
+
+/**
+ * Releases a data log object. Closes the file and returns resources to the
+ * system.
+ *
+ * @param datalog data log
+ */
+void WPI_DataLog_Release(struct WPI_DataLog* datalog);
+
+/**
+ * Change log filename.
+ *
+ * @param datalog data log
+ * @param filename filename
+ */
+void WPI_DataLog_SetFilename(struct WPI_DataLog* datalog, const char* filename);
+
+/**
+ * Explicitly flushes the log data to disk.
+ *
+ * @param datalog data log
+ */
+void WPI_DataLog_Flush(struct WPI_DataLog* datalog);
+
+/**
+ * Pauses appending of data records to the log.  While paused, no data records
+ * are saved (e.g. AppendX is a no-op).  Has no effect on entry starts /
+ * finishes / metadata changes.
+ *
+ * @param datalog data log
+ */
+void WPI_DataLog_Pause(struct WPI_DataLog* datalog);
+
+/**
+ * Resumes appending of data records to the log.  If called after Stop(),
+ * opens a new file (with random name if SetFilename was not called after
+ * Stop()) and appends Start records and schema data values for all previously
+ * started entries and schemas.
+ *
+ * @param datalog data log
+ */
+void WPI_DataLog_Resume(struct WPI_DataLog* datalog);
+
+/**
+ * Stops appending all records to the log, and closes the log file.
+ *
+ * @param datalog data log
+ */
+void WPI_DataLog_Stop(struct WPI_DataLog* datalog);
+
+/**
+ * Start an entry.  Duplicate names are allowed (with the same type), and
+ * result in the same index being returned (Start/Finish are reference
+ * counted).  A duplicate name with a different type will result in an error
+ * message being printed to the console and 0 being returned (which will be
+ * ignored by the Append functions).
+ *
+ * @param datalog data log
+ * @param name Name
+ * @param type Data type
+ * @param metadata Initial metadata (e.g. data properties)
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ *
+ * @return Entry index
+ */
+int WPI_DataLog_Start(struct WPI_DataLog* datalog, const char* name,
+                      const char* type, const char* metadata,
+                      int64_t timestamp);
+
+/**
+ * Finish an entry.
+ *
+ * @param datalog data log
+ * @param entry Entry index
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_Finish(struct WPI_DataLog* datalog, int entry,
+                        int64_t timestamp);
+
+/**
+ * Updates the metadata for an entry.
+ *
+ * @param datalog data log
+ * @param entry Entry index
+ * @param metadata New metadata for the entry
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_SetMetadata(struct WPI_DataLog* datalog, int entry,
+                             const char* metadata, int64_t timestamp);
+
+/**
+ * Appends a raw record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param data Byte array to record
+ * @param len Length of byte array
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendRaw(struct WPI_DataLog* datalog, int entry,
+                           const uint8_t* data, size_t len, int64_t timestamp);
+
+/**
+ * Appends a boolean record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param value Boolean value to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendBoolean(struct WPI_DataLog* datalog, int entry,
+                               int value, int64_t timestamp);
+
+/**
+ * Appends an integer record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param value Integer value to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendInteger(struct WPI_DataLog* datalog, int entry,
+                               int64_t value, int64_t timestamp);
+
+/**
+ * Appends a float record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param value Float value to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendFloat(struct WPI_DataLog* datalog, int entry,
+                             float value, int64_t timestamp);
+
+/**
+ * Appends a double record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param value Double value to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendDouble(struct WPI_DataLog* datalog, int entry,
+                              double value, int64_t timestamp);
+
+/**
+ * Appends a string record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param value String value to record
+ * @param len Length of string
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendString(struct WPI_DataLog* datalog, int entry,
+                              const char* value, size_t len, int64_t timestamp);
+
+/**
+ * Appends a boolean array record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param arr Boolean array to record
+ * @param len Number of elements in array
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendBooleanArray(struct WPI_DataLog* datalog, int entry,
+                                    const int* arr, size_t len,
+                                    int64_t timestamp);
+
+/**
+ * Appends a boolean array record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param arr Boolean array to record
+ * @param len Number of elements in array
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendBooleanArrayByte(struct WPI_DataLog* datalog, int entry,
+                                        const uint8_t* arr, size_t len,
+                                        int64_t timestamp);
+
+/**
+ * Appends an integer array record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param arr Integer array to record
+ * @param len Number of elements in array
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendIntegerArray(struct WPI_DataLog* datalog, int entry,
+                                    const int64_t* arr, size_t len,
+                                    int64_t timestamp);
+
+/**
+ * Appends a float array record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param arr Float array to record
+ * @param len Number of elements in array
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendFloatArray(struct WPI_DataLog* datalog, int entry,
+                                  const float* arr, size_t len,
+                                  int64_t timestamp);
+
+/**
+ * Appends a double array record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param arr Double array to record
+ * @param len Number of elements in array
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendDoubleArray(struct WPI_DataLog* datalog, int entry,
+                                   const double* arr, size_t len,
+                                   int64_t timestamp);
+
+/**
+ * Appends a string array record to the log.
+ *
+ * @param datalog data log
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param arr String array to record
+ * @param len Number of elements in array
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
+void WPI_DataLog_AppendStringArray(struct WPI_DataLog* datalog, int entry,
+                                   const WPI_DataLog_String* arr, size_t len,
+                                   int64_t timestamp);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/DecayedDerivedFrom.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/DecayedDerivedFrom.h
new file mode 100644
index 0000000..c776c9c
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/DecayedDerivedFrom.h
@@ -0,0 +1,17 @@
+// 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 <concepts>
+#include <type_traits>
+
+namespace wpi {
+
+template <class Derived, class Base>
+concept DecayedDerivedFrom =
+    std::derived_from<std::decay_t<Derived>, std::decay_t<Base>> &&
+    std::convertible_to<std::decay_t<Derived>*, std::decay_t<Base>*>;
+
+}  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Logger.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Logger.h
index 883336f..01a02fd 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Logger.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Logger.h
@@ -8,7 +8,7 @@
 #include <functional>
 #include <utility>
 
-#include "fmt/format.h"
+#include <fmt/format.h>
 
 namespace wpi {
 
@@ -79,24 +79,6 @@
   WPI_LOG(inst, ::wpi::WPI_LOG_WARNING, format __VA_OPT__(, ) __VA_ARGS__)
 #define WPI_INFO(inst, format, ...) \
   WPI_LOG(inst, ::wpi::WPI_LOG_INFO, format __VA_OPT__(, ) __VA_ARGS__)
-
-#ifdef NDEBUG
-#define WPI_DEBUG(inst, format, ...) \
-  do {                               \
-  } while (0)
-#define WPI_DEBUG1(inst, format, ...) \
-  do {                                \
-  } while (0)
-#define WPI_DEBUG2(inst, format, ...) \
-  do {                                \
-  } while (0)
-#define WPI_DEBUG3(inst, format, ...) \
-  do {                                \
-  } while (0)
-#define WPI_DEBUG4(inst, format, ...) \
-  do {                                \
-  } while (0)
-#else
 #define WPI_DEBUG(inst, format, ...) \
   WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG, format __VA_OPT__(, ) __VA_ARGS__)
 #define WPI_DEBUG1(inst, format, ...) \
@@ -107,7 +89,6 @@
   WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG3, format __VA_OPT__(, ) __VA_ARGS__)
 #define WPI_DEBUG4(inst, format, ...) \
   WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG4, format __VA_OPT__(, ) __VA_ARGS__)
-#endif
 
 }  // namespace wpi
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/MappedFileRegion.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/MappedFileRegion.h
index 05e487e..33d3bb9 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/MappedFileRegion.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/MappedFileRegion.h
@@ -59,19 +59,13 @@
     return *this;
   }
 
-  explicit operator bool() const {
-    return m_mapping != nullptr;
-  }
+  explicit operator bool() const { return m_mapping != nullptr; }
 
   void Flush();
   void Unmap();
 
-  uint64_t size() const {
-    return m_size;
-  }
-  uint8_t* data() const {
-    return static_cast<uint8_t*>(m_mapping);
-  }
+  uint64_t size() const { return m_size; }
+  uint8_t* data() const { return static_cast<uint8_t*>(m_mapping); }
   const uint8_t* const_data() const {
     return static_cast<const uint8_t*>(m_mapping);
   }
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/SpanExtras.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/SpanExtras.h
index aa7b9ab..790a66f 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/SpanExtras.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/SpanExtras.h
@@ -10,19 +10,49 @@
 namespace wpi {
 
 /// Drop the first \p N elements of the array.
-template <typename T>
-constexpr std::span<T> drop_front(std::span<T> in,
+template <typename T, size_t N>
+constexpr std::span<T> drop_front(std::span<T, N> in,
                                   typename std::span<T>::size_type n = 1) {
   assert(in.size() >= n && "Dropping more elements than exist");
   return in.subspan(n, in.size() - n);
 }
 
 /// Drop the last \p N elements of the array.
-template <typename T>
-constexpr std::span<T> drop_back(std::span<T> in,
+template <typename T, size_t N>
+constexpr std::span<T> drop_back(std::span<T, N> in,
                                  typename std::span<T>::size_type n = 1) {
   assert(in.size() >= n && "Dropping more elements than exist");
   return in.subspan(0, in.size() - n);
 }
 
+/**
+ * Returns a span equal to @p in but with only the first @p n
+ * elements remaining.  If @p n is greater than the length of the
+ * span, the entire span is returned.
+ */
+template <typename T, size_t N>
+constexpr std::span<T> take_front(std::span<T, N> in,
+                                  typename std::span<T>::size_type n = 1) {
+  auto length = in.size();
+  if (n >= length) {
+    return in;
+  }
+  return drop_back(in, length - n);
+}
+
+/**
+ * Returns a span equal to @p in but with only the last @p n
+ * elements remaining.  If @p n is greater than the length of the
+ * span, the entire span is returned.
+ */
+template <typename T, size_t N>
+constexpr std::span<T> take_back(std::span<T, N> in,
+                                 typename std::span<T>::size_type n = 1) {
+  auto length = in.size();
+  if (n >= length) {
+    return in;
+  }
+  return drop_front(in, length - n);
+}
+
 }  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/SymbolExports.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/SymbolExports.h
index 9ffc936..41495ad 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/SymbolExports.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/SymbolExports.h
@@ -5,7 +5,9 @@
 #pragma once
 
 #ifdef _WIN32
+#ifdef _MSC_VER
 #pragma warning(disable : 4251)
+#endif
 
 #ifdef WPILIB_EXPORTS
 #ifdef __GNUC__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Synchronization.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Synchronization.h
index 4ac3c8c..62a79ef 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Synchronization.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/Synchronization.h
@@ -209,10 +209,9 @@
  * Sets up signaling for an arbitrary handle.  With this function, any handle
  * can operate like an event handle.
  *
- * @param handle handle
+ * @param handle Event handle
  * @param manualReset true for manual reset, false for automatic reset
  * @param initialState true to make the handle initially in signaled state
- * @return Event handle
  */
 void CreateSignalObject(WPI_Handle handle, bool manualReset = false,
                         bool initialState = false);
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/array.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/array.h
index cfbb7b0..d6711f1 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/array.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/array.h
@@ -5,6 +5,7 @@
 #pragma once
 
 #include <array>
+#include <concepts>
 #include <cstddef>
 #include <tuple>
 #include <utility>
@@ -26,11 +27,10 @@
  public:
   constexpr explicit array(empty_array_t) {}
 
-  template <typename... Ts>
+  template <std::convertible_to<T>... Ts>
+    requires(1 + sizeof...(Ts) == N)
   constexpr array(T arg, Ts&&... args)  // NOLINT
-      : std::array<T, N>{std::forward<T>(arg), std::forward<Ts>(args)...} {
-    static_assert(1 + sizeof...(args) == N, "Dimension mismatch");
-  }
+      : std::array<T, N>{std::forward<T>(arg), std::forward<Ts>(args)...} {}
 
   constexpr array(const array<T, N>&) = default;
   constexpr array& operator=(const array<T, N>&) = default;
@@ -56,33 +56,32 @@
   }
 };
 
-template <typename T, typename... Ts>
-array(T, Ts...) -> array<std::enable_if_t<(std::is_same_v<T, Ts> && ...), T>,
-                         1 + sizeof...(Ts)>;
+template <typename T, std::convertible_to<T>... Ts>
+array(T, Ts...) -> array<T, 1 + sizeof...(Ts)>;
 
 }  // namespace wpi
 
 template <size_t I, typename T, size_t N>
+  requires(I < N)
 constexpr T& get(wpi::array<T, N>& arr) noexcept {
-  static_assert(I < N, "array index is within bounds");
   return std::get<I>(static_cast<std::array<T, N>>(arr));
 }
 
 template <size_t I, typename T, size_t N>
+  requires(I < N)
 constexpr T&& get(wpi::array<T, N>&& arr) noexcept {
-  static_assert(I < N, "array index is within bounds");
   return std::move(std::get<I>(arr));
 }
 
 template <size_t I, typename T, size_t N>
+  requires(I < N)
 constexpr const T& get(const wpi::array<T, N>& arr) noexcept {
-  static_assert(I < N, "array index is within bounds");
   return std::get<I>(static_cast<std::array<T, N>>(arr));
 }
 
 template <size_t I, typename T, size_t N>
+  requires(I < N)
 constexpr const T&& get(const wpi::array<T, N>&& arr) noexcept {
-  static_assert(I < N, "array index is within bounds");
   return std::move(std::get<I>(arr));
 }
 
@@ -94,8 +93,8 @@
 
 // Partial specialization for wpi::array
 template <size_t I, typename T, size_t N>
+  requires(I < N)
 struct tuple_element<I, wpi::array<T, N>> {
-  static_assert(I < N, "index is out of bounds");
   using type = T;
 };
 }  // namespace std
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/ct_string.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/ct_string.h
new file mode 100644
index 0000000..9f0ef90
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/ct_string.h
@@ -0,0 +1,168 @@
+// 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 <stdint.h>
+
+#include <array>
+#include <limits>
+#include <stdexcept>
+#include <string>
+#include <string_view>
+
+namespace wpi {
+
+// derived from:
+// https://codereview.stackexchange.com/questions/282514/string-literals-concatenation-with-support-for-dynamic-strings
+
+/**
+ * Fixed length string (array of character) for compile time use.
+ *
+ * @tparam N number of characters
+ * @tparam Char character type
+ * @tparam Traits character traits
+ */
+template <typename Char, typename Traits, size_t N>
+  requires(N < (std::numeric_limits<size_t>::max)())
+struct ct_string {
+  std::array<Char, N + 1> chars;
+
+  template <size_t M>
+    requires(M <= (N + 1))
+  consteval ct_string(Char const (&s)[M]) {  // NOLINT
+    if constexpr (M == (N + 1)) {
+      if (s[N] != Char{}) {
+        throw std::logic_error{"char array not null terminated"};
+      }
+    }
+
+    // avoid dependency on <algorithm>
+    // auto p = std::ranges::copy(s, chars.begin()).out;
+    auto p = chars.begin();
+    for (auto c : s) {
+      *p++ = c;
+    }
+    // std::ranges::fill() isn't constexpr on GCC 11
+    while (p != chars.end()) {
+      *p++ = Char{};
+    }
+  }
+
+  explicit consteval ct_string(std::basic_string_view<Char, Traits> s) {
+    // avoid dependency on <algorithm>
+    // auto p = std::ranges::copy(s, chars.begin()).out;
+    auto p = chars.begin();
+    for (auto c : s) {
+      *p++ = c;
+    }
+    // std::ranges::fill() isn't constexpr on GCC 11
+    while (p != chars.end()) {
+      *p++ = Char{};
+    }
+  }
+
+  constexpr auto size() const noexcept { return N; }
+
+  constexpr auto begin() const noexcept { return chars.begin(); }
+  constexpr auto end() const noexcept { return chars.begin() + N; }
+
+  constexpr auto data() const noexcept { return chars.data(); }
+  constexpr auto c_str() const noexcept { return chars.data(); }
+
+  constexpr operator std::basic_string_view<Char, Traits>()  // NOLINT
+      const noexcept {
+    return std::basic_string_view<Char, Traits>{chars.data(), N};
+  }
+};
+
+template <typename Char, size_t M>
+ct_string(Char const (&s)[M]) -> ct_string<Char, std::char_traits<Char>, M - 1>;
+
+inline namespace literals {
+template <ct_string S>
+consteval auto operator""_ct_string() {
+  return S;
+}
+}  // namespace literals
+
+template <typename Char, typename Traits, size_t N1, size_t N2>
+consteval auto operator+(ct_string<Char, Traits, N1> const& s1,
+                         ct_string<Char, Traits, N2> const& s2) noexcept {
+  return Concat(s1, s2);
+}
+
+/**
+ * Concatenates multiple fixed_strings into a larger fixed_string at compile
+ * time.
+ *
+ * @param s1 first string
+ * @param s second and later strings
+ * @return concatenated string
+ */
+template <typename Char, typename Traits, size_t N1, size_t... N>
+consteval auto Concat(ct_string<Char, Traits, N1> const& s1,
+                      ct_string<Char, Traits, N> const&... s) {
+  // Need a dummy array to instantiate a ct_string.
+  constexpr Char dummy[1] = {};
+  auto res = ct_string<Char, Traits, (N1 + ... + N)>{dummy};
+
+  auto p = res.chars.begin();
+  auto append = [&p](auto&& s) {
+    // avoid dependency on <algorithm>
+    // p = std::ranges::copy(s, p).out;
+    for (auto c : s) {
+      *p++ = c;
+    }
+  };
+
+  (append(s1), ..., append(s));
+
+  return res;
+}
+
+// derived from:
+// https://github.com/tcsullivan/constexpr-to-string/blob/master/to_string.hpp
+
+/**
+ * Converts any integral to a ct_string at compile-time.
+ *
+ * @tparam N number to convert
+ * @tparam Base desired base, can be from 2 to 36
+ * @tparam Char character type
+ * @tparam Traits character traits
+ */
+template <intmax_t N, int Base = 10, typename Char = char,
+          typename Traits = std::char_traits<Char>>
+  requires(Base >= 2 && Base <= 36)
+consteval auto NumToCtString() {
+  constexpr char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+  auto buflen = [] {
+    size_t len = N > 0 ? 0 : 1;
+    for (auto n = N; n; len++, n /= Base) {
+    }
+    return len;
+  };
+  constexpr size_t size = buflen();
+
+  constexpr Char dummy[1] = {};
+  auto res = ct_string<Char, Traits, size>{dummy};
+
+  auto ptr = res.chars.data() + size;
+  if (N != 0) {
+    for (auto n = N; n; n /= Base) {
+      *--ptr = digits[(N < 0 ? -1 : 1) * (n % Base)];
+    }
+    if (N < 0) {
+      *--ptr = '-';
+    }
+  } else {
+    res.chars[0] = '0';
+  }
+
+  return res;
+}
+
+}  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/deprecated.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/deprecated.h
index c5b4af2..ccd6bba 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/deprecated.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/deprecated.h
@@ -16,6 +16,8 @@
       _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
 #elif defined(_WIN32)
 #define WPI_IGNORE_DEPRECATED _Pragma("warning(disable : 4996)")
+#else
+#define WPI_IGNORE_DEPRECATED
 #endif
 
 #endif
@@ -25,6 +27,8 @@
 #define WPI_UNIGNORE_DEPRECATED _Pragma("GCC diagnostic pop")
 #elif defined(_WIN32)
 #define WPI_UNIGNORE_DEPRECATED _Pragma("warning(default : 4996)")
+#else
+#define WPI_UNIGNORE_DEPRECATED
 #endif
 #endif
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/fmt/raw_ostream.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/fmt/raw_ostream.h
index 024e04c..6177859 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/fmt/raw_ostream.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/fmt/raw_ostream.h
@@ -5,7 +5,8 @@
 #ifndef WPIUTIL_WPI_FMT_RAW_OSTREAM_H_
 #define WPIUTIL_WPI_FMT_RAW_OSTREAM_H_
 
-#include "fmt/format.h"
+#include <fmt/format.h>
+
 #include "wpi/raw_ostream.h"
 
 FMT_BEGIN_NAMESPACE
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/fs.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/fs.h
index 587a23c..47de1d5 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/fs.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/fs.h
@@ -13,43 +13,17 @@
 
 #pragma once
 
+#include <filesystem>
+#include <fstream>
 #include <string_view>
 #include <system_error>
 
-#if defined(__APPLE__)
-#include <Availability.h>
-#endif
-#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) \
-     || (defined(__cplusplus) && __cplusplus >= 201703L)) \
-    && defined(__has_include)
-#if __has_include(<filesystem>) \
-    && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
-        || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) \
-    && (defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 10 \
-        || (__GNUC__ >= 9 && __GNUC_MINOR__ >= 1))
-#define GHC_USE_STD_FS
-#include <filesystem>
 namespace fs {
+
 using namespace std::filesystem;
 using ifstream = std::ifstream;
 using ofstream = std::ofstream;
 using fstream = std::fstream;
-}  // namespace fs
-#endif
-#endif
-#ifndef GHC_USE_STD_FS
-// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
-#define GHC_FILESYSTEM_FWD
-#include "wpi/ghc/filesystem.hpp"
-namespace fs {
-using namespace ghc::filesystem;
-using ifstream = ghc::filesystem::ifstream;
-using ofstream = ghc::filesystem::ofstream;
-using fstream = ghc::filesystem::fstream;
-}  // namespace fs
-#endif
-
-namespace fs {
 
 #if defined(_WIN32)
 // A Win32 HANDLE is a typedef of void*
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/jni_util.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/jni_util.h
index 8d50fdd..31bdf93 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/jni_util.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/jni_util.h
@@ -7,14 +7,16 @@
 
 #include <jni.h>
 
+#include <concepts>
 #include <queue>
 #include <span>
 #include <string>
 #include <string_view>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
+#include <fmt/core.h>
+
 #include "wpi/ConvertUTF.h"
 #include "wpi/SafeThread.h"
 #include "wpi/SmallString.h"
@@ -158,8 +160,8 @@
         env->ReleaseStringCritical(str, chars);
       }
     } else {
-      errs() << "JStringRef was passed a null pointer at \n"
-             << GetJavaStackTrace(env);
+      fmt::print(stderr, "JStringRef was passed a null pointer at\n",
+                 GetJavaStackTrace(env));
     }
     // Ensure str is null-terminated.
     m_str.push_back('\0');
@@ -175,208 +177,242 @@
   SmallString<128> m_str;
 };
 
-// Details for J*ArrayRef and CriticalJ*ArrayRef
 namespace detail {
 
-template <typename C, typename T>
-class JArrayRefInner {
+template <typename T>
+struct ArrayHelper {};
+
+#define WPI_JNI_ARRAYHELPER(T, F)                                             \
+  template <>                                                                 \
+  struct ArrayHelper<T> {                                                     \
+    using jarray_type = T##Array;                                             \
+    static T* GetArrayElements(JNIEnv* env, jarray_type jarr) {               \
+      return env->Get##F##ArrayElements(jarr, nullptr);                       \
+    }                                                                         \
+    static void ReleaseArrayElements(JNIEnv* env, jarray_type jarr, T* elems, \
+                                     jint mode) {                             \
+      env->Release##F##ArrayElements(jarr, elems, mode);                      \
+    }                                                                         \
+  };
+
+WPI_JNI_ARRAYHELPER(jboolean, Boolean)
+WPI_JNI_ARRAYHELPER(jbyte, Byte)
+WPI_JNI_ARRAYHELPER(jshort, Short)
+WPI_JNI_ARRAYHELPER(jint, Int)
+WPI_JNI_ARRAYHELPER(jlong, Long)
+WPI_JNI_ARRAYHELPER(jfloat, Float)
+WPI_JNI_ARRAYHELPER(jdouble, Double)
+
+#undef WPI_JNI_ARRAYHELPER
+
+template <typename T>
+concept JArrayType =
+    requires { typename ArrayHelper<std::remove_cv_t<T>>::jarray_type; };
+
+template <typename CvSrc, typename Dest>
+struct copy_cv {
+ private:
+  using U0 = std::remove_cv_t<Dest>;
+  using U1 = std::conditional_t<std::is_const_v<CvSrc>, const U0, U0>;
+  using U2 = std::conditional_t<std::is_volatile_v<CvSrc>, volatile U1, U1>;
+
  public:
-  operator std::span<const T>() const {  // NOLINT
-    return static_cast<const C*>(this)->array();
-  }
+  using type = U2;
 };
 
-/**
- * Specialization of JArrayRefBase to provide std::string_view conversion
- * and span<const uint8_t> conversion.
- */
-template <typename C>
-class JArrayRefInner<C, jbyte> {
- public:
-  operator std::string_view() const { return str(); }
+template <typename CvSrc, typename Dest>
+using copy_cv_t = typename copy_cv<CvSrc, Dest>::type;
 
-  std::string_view str() const {
-    auto arr = static_cast<const C*>(this)->array();
+template <typename From, typename To>
+constexpr bool is_qualification_convertible_v =
+    !(std::is_const_v<From> && !std::is_const_v<To>)&&!(
+        std::is_volatile_v<From> && !std::is_volatile_v<To>);
+
+/**
+ * Helper class for working with JNI arrays.
+ *
+ * This class exposes an is_valid() member and an explicit conversion to bool
+ * which indicate if the span is valid. Operations on invalid spans are
+ * undefined.
+ *
+ * Note that Set<PrimitiveType>ArrayRegion may be faster for pure writes since
+ * it avoids copying the elements from Java to C++.
+ *
+ * @tparam T The element type of the array (e.g., jdouble).
+ * @tparam IsCritical If true, Get/ReleasePrimitiveArrayCritical will be used
+ * instead of Get/Release\<PrimitiveType\>ArrayElements.
+ * @tparam Size The number of elements in the span.
+ */
+template <JArrayType T, bool IsCritical, size_t Size = std::dynamic_extent>
+class JSpanBase {
+  using ArrHelper = ArrayHelper<std::remove_cv_t<T>>;
+  using jarray_type = typename ArrHelper::jarray_type;
+
+ public:
+  JSpanBase(const JSpanBase&) = delete;
+  JSpanBase& operator=(const JSpanBase&) = delete;
+
+  JSpanBase(JSpanBase&& other)
+      : m_valid{other.m_valid},
+        m_env{other.m_env},
+        m_jarr{other.m_jarr},
+        m_size{other.m_size},
+        m_elements{other.m_elements} {
+    other.m_jarr = nullptr;
+    other.m_elements = nullptr;
+  }
+
+  JSpanBase& operator=(JSpanBase&& other) {
+    m_valid = other.m_valid;
+    m_env = other.m_env;
+    m_jarr = other.m_jarr;
+    m_size = other.m_size;
+    m_elements = other.m_elements;
+    other.m_valid = false;
+    other.m_jarr = nullptr;
+    other.m_elements = nullptr;
+    return *this;
+  }
+
+  JSpanBase(JNIEnv* env, jobject bb, size_t size)
+    requires(!IsCritical)
+      : m_valid{Size == std::dynamic_extent || size == Size},
+        m_env{env},
+        m_jarr{nullptr},
+        m_size{size},
+        m_elements{static_cast<std::remove_cv_t<T>*>(
+            bb ? env->GetDirectBufferAddress(bb) : nullptr)} {
+    if (!bb) {
+      fmt::print(stderr, "JSpan was passed a null pointer at\n",
+                 GetJavaStackTrace(env));
+    }
+  }
+
+  JSpanBase(JNIEnv* env, jarray_type jarr, size_t size)
+      : m_valid{Size == std::dynamic_extent || size == Size},
+        m_env{env},
+        m_jarr{jarr},
+        m_size{size},
+        m_elements{nullptr} {
+    if (jarr) {
+      if constexpr (IsCritical) {
+        m_elements = static_cast<std::remove_cv_t<T>*>(
+            env->GetPrimitiveArrayCritical(jarr, nullptr));
+      } else {
+        m_elements = ArrHelper::GetArrayElements(env, jarr);
+      }
+    } else {
+      fmt::print(stderr, "JSpan was passed a null pointer at\n",
+                 GetJavaStackTrace(env));
+    }
+  }
+
+  JSpanBase(JNIEnv* env, jarray_type jarr)
+      : JSpanBase(env, jarr, jarr ? env->GetArrayLength(jarr) : 0) {}
+
+  ~JSpanBase() {
+    if (m_jarr && m_elements) {
+      constexpr jint mode = std::is_const_v<T> ? JNI_ABORT : 0;
+      if constexpr (IsCritical) {
+        m_env->ReleasePrimitiveArrayCritical(m_jarr, m_elements, mode);
+      } else {
+        ArrHelper::ReleaseArrayElements(m_env, m_jarr, m_elements, mode);
+      }
+    }
+  }
+
+  operator std::span<T, Size>() const { return array(); }
+
+  std::span<T, Size> array() const {
+    // If Size is dynamic_extent, can return empty span
+    // Unfortunately, sized spans will return a span over nullptr if m_elements
+    // is nullptr
+    if constexpr (Size == std::dynamic_extent) {
+      if (!m_elements) {
+        return {};
+      }
+    }
+    return std::span<T, Size>{m_elements, m_size};
+  }
+
+  T* begin() const { return m_elements; }
+
+  T* end() const { return m_elements + m_size; }
+
+  bool is_valid() const { return m_valid && m_elements != nullptr; }
+
+  explicit operator bool() const { return is_valid(); }
+
+  T* data() const { return m_elements; }
+
+  size_t size() const { return m_size; }
+
+  const T& operator[](size_t i) const { return m_elements[i]; }
+
+  T& operator[](size_t i)
+    requires(!std::is_const_v<T>)
+  {
+    return m_elements[i];
+  }
+
+  // Provide std::string_view and span<const uint8_t> conversions for jbyte
+
+  operator std::string_view() const
+    requires std::is_same_v<std::remove_cv_t<T>, jbyte>
+  {
+    return str();
+  }
+
+  std::string_view str() const
+    requires std::is_same_v<std::remove_cv_t<T>, jbyte>
+  {
+    auto arr = array();
     if (arr.empty()) {
       return {};
     }
     return {reinterpret_cast<const char*>(arr.data()), arr.size()};
   }
 
-  std::span<const uint8_t> uarray() const {
-    auto arr = static_cast<const C*>(this)->array();
+  std::span<copy_cv_t<T, uint8_t>, Size> uarray() const
+    requires std::is_same_v<std::remove_cv_t<T>, jbyte>
+  {
+    auto arr = array();
     if (arr.empty()) {
       return {};
     }
     return {reinterpret_cast<const uint8_t*>(arr.data()), arr.size()};
   }
-};
 
-/**
- * Specialization of JArrayRefBase to handle both "long long" and "long" on
- * 64-bit systems.
- */
-template <typename C>
-class JArrayRefInner<C, jlong> {
- public:
-  template <typename U,
-            typename = std::enable_if_t<sizeof(U) == sizeof(jlong) &&
-                                        std::is_integral_v<U>>>
-  operator std::span<const U>() const {  // NOLINT
-    auto arr = static_cast<const C*>(this)->array();
+  // Support both "long long" and "long" on 64-bit systems
+
+  template <typename U>
+    requires(sizeof(U) == sizeof(jlong) && std::integral<U> &&
+             is_qualification_convertible_v<T, U>)
+  operator std::span<U, Size>() const
+    requires std::is_same_v<std::remove_cv_t<T>, jlong>
+  {
+    auto arr = array();
     if (arr.empty()) {
       return {};
     }
-    return {reinterpret_cast<const U*>(arr.data()), arr.size()};
-  }
-};
-
-/**
- * Base class for J*ArrayRef and CriticalJ*ArrayRef
- */
-template <typename T>
-class JArrayRefBase : public JArrayRefInner<JArrayRefBase<T>, T> {
- public:
-  explicit operator bool() const { return this->m_elements != nullptr; }
-
-  std::span<const T> array() const {
-    if (!this->m_elements) {
-      return {};
-    }
-    return {this->m_elements, this->m_size};
+    return {reinterpret_cast<U*>(arr.data()), arr.size()};
   }
 
-  size_t size() const { return this->m_size; }
-  T& operator[](size_t i) { return this->m_elements[i]; }
-  const T& operator[](size_t i) const { return this->m_elements[i]; }
-
-  JArrayRefBase(const JArrayRefBase&) = delete;
-  JArrayRefBase& operator=(const JArrayRefBase&) = delete;
-
-  JArrayRefBase(JArrayRefBase&& oth)
-      : m_env(oth.m_env),
-        m_jarr(oth.m_jarr),
-        m_size(oth.m_size),
-        m_elements(oth.m_elements) {
-    oth.m_jarr = nullptr;
-    oth.m_elements = nullptr;
-  }
-
-  JArrayRefBase& operator=(JArrayRefBase&& oth) {
-    this->m_env = oth.m_env;
-    this->m_jarr = oth.m_jarr;
-    this->m_size = oth.m_size;
-    this->m_elements = oth.m_elements;
-    oth.m_jarr = nullptr;
-    oth.m_elements = nullptr;
-    return *this;
-  }
-
- protected:
-  JArrayRefBase(JNIEnv* env, T* elements, size_t size) {
-    this->m_env = env;
-    this->m_jarr = nullptr;
-    this->m_size = size;
-    this->m_elements = elements;
-  }
-
-  JArrayRefBase(JNIEnv* env, jarray jarr, size_t size) {
-    this->m_env = env;
-    this->m_jarr = jarr;
-    this->m_size = size;
-    this->m_elements = nullptr;
-  }
-
-  JArrayRefBase(JNIEnv* env, jarray jarr)
-      : JArrayRefBase(env, jarr, jarr ? env->GetArrayLength(jarr) : 0) {}
-
+ private:
+  bool m_valid;
   JNIEnv* m_env;
-  jarray m_jarr = nullptr;
+  jarray_type m_jarr = nullptr;
   size_t m_size;
-  T* m_elements;
+  std::remove_cv_t<T>* m_elements;
 };
 
 }  // namespace detail
 
-// Java array / DirectBuffer reference.
+template <typename T, size_t Extent = std::dynamic_extent>
+using JSpan = detail::JSpanBase<T, false, Extent>;
 
-#define WPI_JNI_JARRAYREF(T, F)                                                \
-  class J##F##ArrayRef : public detail::JArrayRefBase<T> {                     \
-   public:                                                                     \
-    J##F##ArrayRef(JNIEnv* env, jobject bb, int len)                           \
-        : detail::JArrayRefBase<T>(                                            \
-              env,                                                             \
-              static_cast<T*>(bb ? env->GetDirectBufferAddress(bb) : nullptr), \
-              len) {                                                           \
-      if (!bb) {                                                               \
-        errs() << "JArrayRef was passed a null pointer at \n"                  \
-               << GetJavaStackTrace(env);                                      \
-      }                                                                        \
-    }                                                                          \
-    J##F##ArrayRef(JNIEnv* env, T##Array jarr, int len)                        \
-        : detail::JArrayRefBase<T>(env, jarr, len) {                           \
-      if (jarr) {                                                              \
-        m_elements = env->Get##F##ArrayElements(jarr, nullptr);                \
-      } else {                                                                 \
-        errs() << "JArrayRef was passed a null pointer at \n"                  \
-               << GetJavaStackTrace(env);                                      \
-      }                                                                        \
-    }                                                                          \
-    J##F##ArrayRef(JNIEnv* env, T##Array jarr)                                 \
-        : detail::JArrayRefBase<T>(env, jarr) {                                \
-      if (jarr) {                                                              \
-        m_elements = env->Get##F##ArrayElements(jarr, nullptr);                \
-      } else {                                                                 \
-        errs() << "JArrayRef was passed a null pointer at \n"                  \
-               << GetJavaStackTrace(env);                                      \
-      }                                                                        \
-    }                                                                          \
-    ~J##F##ArrayRef() {                                                        \
-      if (m_jarr && m_elements) {                                              \
-        m_env->Release##F##ArrayElements(static_cast<T##Array>(m_jarr),        \
-                                         m_elements, JNI_ABORT);               \
-      }                                                                        \
-    }                                                                          \
-  };                                                                           \
-                                                                               \
-  class CriticalJ##F##ArrayRef : public detail::JArrayRefBase<T> {             \
-   public:                                                                     \
-    CriticalJ##F##ArrayRef(JNIEnv* env, T##Array jarr, int len)                \
-        : detail::JArrayRefBase<T>(env, jarr, len) {                           \
-      if (jarr) {                                                              \
-        m_elements =                                                           \
-            static_cast<T*>(env->GetPrimitiveArrayCritical(jarr, nullptr));    \
-      } else {                                                                 \
-        errs() << "JArrayRef was passed a null pointer at \n"                  \
-               << GetJavaStackTrace(env);                                      \
-      }                                                                        \
-    }                                                                          \
-    CriticalJ##F##ArrayRef(JNIEnv* env, T##Array jarr)                         \
-        : detail::JArrayRefBase<T>(env, jarr) {                                \
-      if (jarr) {                                                              \
-        m_elements =                                                           \
-            static_cast<T*>(env->GetPrimitiveArrayCritical(jarr, nullptr));    \
-      } else {                                                                 \
-        errs() << "JArrayRef was passed a null pointer at \n"                  \
-               << GetJavaStackTrace(env);                                      \
-      }                                                                        \
-    }                                                                          \
-    ~CriticalJ##F##ArrayRef() {                                                \
-      if (m_jarr && m_elements) {                                              \
-        m_env->ReleasePrimitiveArrayCritical(m_jarr, m_elements, JNI_ABORT);   \
-      }                                                                        \
-    }                                                                          \
-  };
-
-WPI_JNI_JARRAYREF(jboolean, Boolean)
-WPI_JNI_JARRAYREF(jbyte, Byte)
-WPI_JNI_JARRAYREF(jshort, Short)
-WPI_JNI_JARRAYREF(jint, Int)
-WPI_JNI_JARRAYREF(jlong, Long)
-WPI_JNI_JARRAYREF(jfloat, Float)
-WPI_JNI_JARRAYREF(jdouble, Double)
-
-#undef WPI_JNI_JARRAYREF
+template <typename T, size_t Extent = std::dynamic_extent>
+using CriticalJSpan = detail::JSpanBase<T, true, Extent>;
 
 //
 // Conversions from C++ to Java objects
@@ -397,46 +433,38 @@
 // details for MakeJIntArray
 namespace detail {
 
-/**
- * Slow path (get primitive array and set individual elements).
- *
- * This is used if the input type is not an integer of the same size (note
- * signed/unsigned is ignored).
- */
-template <typename T,
-          bool = (std::is_integral<T>::value && sizeof(jint) == sizeof(T))>
+template <typename T>
 struct ConvertIntArray {
   static jintArray ToJava(JNIEnv* env, std::span<const T> arr) {
-    jintArray jarr = env->NewIntArray(arr.size());
-    if (!jarr) {
-      return nullptr;
+    if constexpr (sizeof(T) == sizeof(jint) && std::integral<T>) {
+      // Fast path (use SetIntArrayRegion).
+      jintArray jarr = env->NewIntArray(arr.size());
+      if (!jarr) {
+        return nullptr;
+      }
+      env->SetIntArrayRegion(jarr, 0, arr.size(),
+                             reinterpret_cast<const jint*>(arr.data()));
+      return jarr;
+    } else {
+      // Slow path (get primitive array and set individual elements).
+      //
+      // This is used if the input type is not an integer of the same size (note
+      // signed/unsigned is ignored).
+      jintArray jarr = env->NewIntArray(arr.size());
+      if (!jarr) {
+        return nullptr;
+      }
+      jint* elements =
+          static_cast<jint*>(env->GetPrimitiveArrayCritical(jarr, nullptr));
+      if (!elements) {
+        return nullptr;
+      }
+      for (size_t i = 0; i < arr.size(); ++i) {
+        elements[i] = static_cast<jint>(arr[i]);
+      }
+      env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
+      return jarr;
     }
-    jint* elements =
-        static_cast<jint*>(env->GetPrimitiveArrayCritical(jarr, nullptr));
-    if (!elements) {
-      return nullptr;
-    }
-    for (size_t i = 0; i < arr.size(); ++i) {
-      elements[i] = static_cast<jint>(arr[i]);
-    }
-    env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
-    return jarr;
-  }
-};
-
-/**
- * Fast path (use SetIntArrayRegion).
- */
-template <typename T>
-struct ConvertIntArray<T, true> {
-  static jintArray ToJava(JNIEnv* env, std::span<const T> arr) {
-    jintArray jarr = env->NewIntArray(arr.size());
-    if (!jarr) {
-      return nullptr;
-    }
-    env->SetIntArrayRegion(jarr, 0, arr.size(),
-                           reinterpret_cast<const jint*>(arr.data()));
-    return jarr;
   }
 };
 
@@ -574,9 +602,9 @@
 
 #undef WPI_JNI_MAKEJARRAY
 
-template <class T, typename = std::enable_if_t<
-                       sizeof(typename T::value_type) == sizeof(jlong) &&
-                       std::is_integral_v<typename T::value_type>>>
+template <class T>
+  requires(sizeof(typename T::value_type) == sizeof(jlong) &&
+           std::integral<typename T::value_type>)
 inline jlongArray MakeJLongArray(JNIEnv* env, const T& arr) {
   jlongArray jarr = env->NewLongArray(arr.size());
   if (!jarr) {
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/priority_queue.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/priority_queue.h
index 4610aba..af68374 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/priority_queue.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/priority_queue.h
@@ -6,6 +6,7 @@
 #define WPIUTIL_WPI_PRIORITY_QUEUE_H_
 
 #include <algorithm>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <vector>
@@ -22,11 +23,9 @@
  */
 template <typename T, typename Sequence = std::vector<T>,
           typename Compare = std::less<typename Sequence::value_type>>
+  requires std::same_as<T, typename Sequence::value_type>
 class priority_queue {
  public:
-  static_assert(std::is_same_v<T, typename Sequence::value_type>,
-                "value_type must be the same as the underlying container");
-
   using value_type = typename Sequence::value_type;
   using reference = typename Sequence::reference;
   using const_reference = typename Sequence::const_reference;
@@ -34,10 +33,9 @@
   using container_type = Sequence;
   using value_compare = Compare;
 
-  template <typename Seq = Sequence,
-            typename Requires = typename std::enable_if_t<
-                std::is_default_constructible<Compare>{} &&
-                std::is_default_constructible<Seq>{}>>
+  template <typename Seq = Sequence>
+    requires std::default_initializable<Compare> &&
+             std::default_initializable<Seq>
   priority_queue() {}
 
   priority_queue(const Compare& comp, const Sequence& c) : c(c), comp(comp) {
@@ -65,7 +63,10 @@
     std::make_heap(c.begin(), c.end(), comp);
   }
 
-  [[nodiscard]] bool empty() const { return c.empty(); }
+  [[nodiscard]]
+  bool empty() const {
+    return c.empty();
+  }
 
   size_type size() const { return c.size(); }
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/protobuf/Protobuf.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/protobuf/Protobuf.h
new file mode 100644
index 0000000..4eedcca
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/protobuf/Protobuf.h
@@ -0,0 +1,260 @@
+// 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 <stdint.h>
+
+#include <concepts>
+#include <optional>
+#include <span>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "wpi/function_ref.h"
+
+namespace google::protobuf {
+class Arena;
+class Message;
+}  // namespace google::protobuf
+
+namespace wpi {
+
+template <typename T>
+class SmallVectorImpl;
+
+/**
+ * Protobuf serialization template. Unspecialized class has no members; only
+ * specializations of this class are useful, and only if they meet the
+ * ProtobufSerializable concept.
+ *
+ * @tparam T type to serialize/deserialize
+ */
+template <typename T>
+struct Protobuf {};
+
+/**
+ * Specifies that a type is capable of protobuf serialization and
+ * deserialization.
+ *
+ * This is designed for serializing complex flexible data structures using
+ * code generated from a .proto file. Serialization consists of writing
+ * values into a mutable protobuf Message and deserialization consists of
+ * reading values from an immutable protobuf Message.
+ *
+ * Implementations must define a template specialization for wpi::Protobuf with
+ * T being the type that is being serialized/deserialized, with the following
+ * static members (as enforced by this concept):
+ * - google::protobuf::Message* New(google::protobuf::Arena*): create a protobuf
+ *   message
+ * - T Unpack(const google::protobuf::Message&): function for deserialization
+ * - void Pack(google::protobuf::Message*, T&& value): function for
+ *   serialization
+ *
+ * To avoid pulling in the protobuf headers, these functions use
+ * google::protobuf::Message instead of a more specific type; implementations
+ * will need to static_cast to the correct type as created by New().
+ *
+ * Additionally: In a static block, call StructRegistry.registerClass() to
+ * register the class
+ */
+template <typename T>
+concept ProtobufSerializable = requires(
+    google::protobuf::Arena* arena, const google::protobuf::Message& inmsg,
+    google::protobuf::Message* outmsg, const T& value) {
+  typename Protobuf<typename std::remove_cvref_t<T>>;
+  {
+    Protobuf<typename std::remove_cvref_t<T>>::New(arena)
+  } -> std::same_as<google::protobuf::Message*>;
+  {
+    Protobuf<typename std::remove_cvref_t<T>>::Unpack(inmsg)
+  } -> std::same_as<typename std::remove_cvref_t<T>>;
+  Protobuf<typename std::remove_cvref_t<T>>::Pack(outmsg, value);
+};
+
+/**
+ * Specifies that a type is capable of in-place protobuf deserialization.
+ *
+ * In addition to meeting ProtobufSerializable, implementations must define a
+ * wpi::Protobuf<T> static member
+ * `void UnpackInto(T*, const google::protobuf::Message&)` to update the
+ * pointed-to T with the contents of the message.
+ */
+template <typename T>
+concept MutableProtobufSerializable =
+    ProtobufSerializable<T> &&
+    requires(T* out, const google::protobuf::Message& msg) {
+      Protobuf<typename std::remove_cvref_t<T>>::UnpackInto(out, msg);
+    };
+
+/**
+ * Unpack a serialized protobuf message.
+ *
+ * @tparam T object type
+ * @param msg protobuf message
+ * @return Deserialized object
+ */
+template <ProtobufSerializable T>
+inline T UnpackProtobuf(const google::protobuf::Message& msg) {
+  return Protobuf<T>::Unpack(msg);
+}
+
+/**
+ * Pack a serialized protobuf message.
+ *
+ * @param msg protobuf message (mutable, output)
+ * @param value object
+ */
+template <ProtobufSerializable T>
+inline void PackProtobuf(google::protobuf::Message* msg, const T& value) {
+  Protobuf<typename std::remove_cvref_t<T>>::Pack(msg, value);
+}
+
+/**
+ * Unpack a serialized struct into an existing object, overwriting its contents.
+ *
+ * @param out object (output)
+ * @param msg protobuf message
+ */
+template <ProtobufSerializable T>
+inline void UnpackProtobufInto(T* out, const google::protobuf::Message& msg) {
+  if constexpr (MutableProtobufSerializable<T>) {
+    Protobuf<T>::UnpackInto(out, msg);
+  } else {
+    *out = UnpackProtobuf<T>(msg);
+  }
+}
+
+// these detail functions avoid the need to include protobuf headers
+namespace detail {
+void DeleteProtobuf(google::protobuf::Message* msg);
+bool ParseProtobuf(google::protobuf::Message* msg,
+                   std::span<const uint8_t> data);
+bool SerializeProtobuf(wpi::SmallVectorImpl<uint8_t>& out,
+                       const google::protobuf::Message& msg);
+bool SerializeProtobuf(std::vector<uint8_t>& out,
+                       const google::protobuf::Message& msg);
+std::string GetTypeString(const google::protobuf::Message& msg);
+void ForEachProtobufDescriptor(
+    const google::protobuf::Message& msg,
+    function_ref<bool(std::string_view filename)> wants,
+    function_ref<void(std::string_view filename,
+                      std::span<const uint8_t> descriptor)>
+        fn);
+}  // namespace detail
+
+/**
+ * Owning wrapper (ala std::unique_ptr) for google::protobuf::Message* that does
+ * not require the protobuf headers be included. Note this object is not thread
+ * safe; users of this object are required to provide any necessary thread
+ * safety.
+ *
+ * @tparam T serialized object type
+ */
+template <ProtobufSerializable T>
+class ProtobufMessage {
+ public:
+  explicit ProtobufMessage(google::protobuf::Arena* arena = nullptr)
+      : m_msg{Protobuf<T>::New(arena)} {}
+  ~ProtobufMessage() { detail::DeleteProtobuf(m_msg); }
+  ProtobufMessage(const ProtobufMessage&) = delete;
+  ProtobufMessage& operator=(const ProtobufMessage&) = delete;
+  ProtobufMessage(ProtobufMessage&& rhs) : m_msg{rhs.m_msg} {
+    rhs.m_msg = nullptr;
+  }
+  ProtobufMessage& operator=(ProtobufMessage&& rhs) {
+    std::swap(m_msg, rhs.m_msg);
+    return *this;
+  }
+
+  /**
+   * Gets the stored message object.
+   *
+   * @return google::protobuf::Message*
+   */
+  google::protobuf::Message* GetMessage() { return m_msg; }
+  const google::protobuf::Message* GetMessage() const { return m_msg; }
+
+  /**
+   * Unpacks from a byte array.
+   *
+   * @param data byte array
+   * @return Optional; empty if parsing failed
+   */
+  std::optional<T> Unpack(std::span<const uint8_t> data) {
+    if (!detail::ParseProtobuf(m_msg, data)) {
+      return std::nullopt;
+    }
+    return Protobuf<T>::Unpack(*m_msg);
+  }
+
+  /**
+   * Unpacks from a byte array into an existing object.
+   *
+   * @param[out] out output object
+   * @param[in] data byte array
+   * @return true if successful
+   */
+  bool UnpackInto(T* out, std::span<const uint8_t> data) {
+    if (!detail::ParseProtobuf(m_msg, data)) {
+      return false;
+    }
+    UnpackProtobufInto(out, *m_msg);
+    return true;
+  }
+
+  /**
+   * Packs object into a SmallVector.
+   *
+   * @param[out] out output bytes
+   * @param[in] value value
+   * @return true if successful
+   */
+  bool Pack(wpi::SmallVectorImpl<uint8_t>& out, const T& value) {
+    Protobuf<T>::Pack(m_msg, value);
+    return detail::SerializeProtobuf(out, *m_msg);
+  }
+
+  /**
+   * Packs object into a std::vector.
+   *
+   * @param[out] out output bytes
+   * @param[in] value value
+   * @return true if successful
+   */
+  bool Pack(std::vector<uint8_t>& out, const T& value) {
+    Protobuf<T>::Pack(m_msg, value);
+    return detail::SerializeProtobuf(out, *m_msg);
+  }
+
+  /**
+   * Gets the type string for the message.
+   *
+   * @return type string
+   */
+  std::string GetTypeString() const { return detail::GetTypeString(*m_msg); }
+
+  /**
+   * Loops over all protobuf descriptors including nested/referenced
+   * descriptors.
+   *
+   * @param exists function that returns false if fn should be called for the
+   *               given type string
+   * @param fn function to call for each descriptor
+   */
+  void ForEachProtobufDescriptor(
+      function_ref<bool(std::string_view filename)> exists,
+      function_ref<void(std::string_view filename,
+                        std::span<const uint8_t> descriptor)>
+          fn) {
+    detail::ForEachProtobufDescriptor(*m_msg, exists, fn);
+  }
+
+ private:
+  google::protobuf::Message* m_msg = nullptr;
+};
+
+}  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/protobuf/ProtobufMessageDatabase.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/protobuf/ProtobufMessageDatabase.h
new file mode 100644
index 0000000..f1ab105
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/protobuf/ProtobufMessageDatabase.h
@@ -0,0 +1,77 @@
+// 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 <span>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/descriptor_database.h>
+#include <google/protobuf/dynamic_message.h>
+
+#include "wpi/StringMap.h"
+
+namespace wpi {
+
+/**
+ * Database of protobuf dynamic messages. Unlike the protobuf descriptor pools
+ * and databases, this gracefully handles adding descriptors out of order and
+ * descriptors being replaced.
+ */
+class ProtobufMessageDatabase {
+ public:
+  /**
+   * Adds a file descriptor to the database. If the file references other
+   * files that have not yet been added, no messages in that file will be
+   * available until those files are added. Replaces any existing file with
+   * the same name.
+   *
+   * @param filename filename
+   * @param data FileDescriptorProto serialized data
+   * @return False if could not parse data
+   */
+  bool Add(std::string_view filename, std::span<const uint8_t> data);
+
+  /**
+   * Finds a message in the database by name.
+   *
+   * @param name type name
+   * @return protobuf message, or nullptr if not found
+   */
+  google::protobuf::Message* Find(std::string_view name) const;
+
+  /**
+   * Gets message factory.
+   *
+   * @return message factory
+   */
+  google::protobuf::MessageFactory* GetMessageFactory() {
+    return m_factory.get();
+  }
+
+ private:
+  struct ProtoFile {
+    std::unique_ptr<google::protobuf::FileDescriptorProto> proto;
+    std::vector<std::string> uses;
+    bool complete = false;
+    bool inPool = false;
+  };
+
+  void Build(std::string_view filename, ProtoFile& file);
+  bool Rebuild(ProtoFile& file);
+
+  std::unique_ptr<google::protobuf::DescriptorPool> m_pool =
+      std::make_unique<google::protobuf::DescriptorPool>();
+  std::unique_ptr<google::protobuf::DynamicMessageFactory> m_factory =
+      std::make_unique<google::protobuf::DynamicMessageFactory>();
+  wpi::StringMap<ProtoFile> m_files;  // indexed by filename
+  // indexed by type string
+  mutable wpi::StringMap<std::unique_ptr<google::protobuf::Message>> m_msgs;
+};
+
+}  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h
index 2287b73..4115420 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h
@@ -60,6 +60,14 @@
                                   std::function<void(bool)> setter) = 0;
 
   /**
+   * Add a constant boolean property.
+   *
+   * @param key     property name
+   * @param value   the value
+   */
+  virtual void PublishConstBoolean(std::string_view key, bool value) = 0;
+
+  /**
    * Add an integer property.
    *
    * @param key     property name
@@ -71,6 +79,14 @@
                                   std::function<void(int64_t)> setter) = 0;
 
   /**
+   * Add a constant integer property.
+   *
+   * @param key     property name
+   * @param value   the value
+   */
+  virtual void PublishConstInteger(std::string_view key, int64_t value) = 0;
+
+  /**
    * Add a float property.
    *
    * @param key     property name
@@ -82,6 +98,14 @@
                                 std::function<void(float)> setter) = 0;
 
   /**
+   * Add a constant float property.
+   *
+   * @param key     property name
+   * @param value   the value
+   */
+  virtual void PublishConstFloat(std::string_view key, float value) = 0;
+
+  /**
    * Add a double property.
    *
    * @param key     property name
@@ -93,6 +117,14 @@
                                  std::function<void(double)> setter) = 0;
 
   /**
+   * Add a constant double property.
+   *
+   * @param key     property name
+   * @param value   the value
+   */
+  virtual void PublishConstDouble(std::string_view key, double value) = 0;
+
+  /**
    * Add a string property.
    *
    * @param key     property name
@@ -104,6 +136,15 @@
       std::function<void(std::string_view)> setter) = 0;
 
   /**
+   * Add a constant string property.
+   *
+   * @param key     property name
+   * @param value   the value
+   */
+  virtual void PublishConstString(std::string_view key,
+                                  std::string_view value) = 0;
+
+  /**
    * Add a boolean array property.
    *
    * @param key     property name
@@ -115,6 +156,15 @@
       std::function<void(std::span<const int>)> setter) = 0;
 
   /**
+   * Add a constant boolean array property.
+   *
+   * @param key     property name
+   * @param value   the value
+   */
+  virtual void PublishConstBooleanArray(std::string_view key,
+                                        std::span<const int> value) = 0;
+
+  /**
    * Add an integer array property.
    *
    * @param key     property name
@@ -126,6 +176,15 @@
       std::function<void(std::span<const int64_t>)> setter) = 0;
 
   /**
+   * Add a constant integer array property.
+   *
+   * @param key     property name
+   * @param value   the value
+   */
+  virtual void PublishConstIntegerArray(std::string_view key,
+                                        std::span<const int64_t> value) = 0;
+
+  /**
    * Add a float array property.
    *
    * @param key     property name
@@ -137,6 +196,15 @@
       std::function<void(std::span<const float>)> setter) = 0;
 
   /**
+   * Add a constant float array property.
+   *
+   * @param key     property name
+   * @param value   the value
+   */
+  virtual void PublishConstFloatArray(std::string_view key,
+                                      std::span<const float> value) = 0;
+
+  /**
    * Add a double array property.
    *
    * @param key     property name
@@ -148,6 +216,15 @@
       std::function<void(std::span<const double>)> setter) = 0;
 
   /**
+   * Add a constant double array property.
+   *
+   * @param key     property name
+   * @param value   the value
+   */
+  virtual void PublishConstDoubleArray(std::string_view key,
+                                       std::span<const double> value) = 0;
+
+  /**
    * Add a string array property.
    *
    * @param key     property name
@@ -159,6 +236,15 @@
       std::function<void(std::span<const std::string>)> setter) = 0;
 
   /**
+   * Add a constant string array property.
+   *
+   * @param key     property name
+   * @param value   the value
+   */
+  virtual void PublishConstStringArray(std::string_view key,
+                                       std::span<const std::string> value) = 0;
+
+  /**
    * Add a raw property.
    *
    * @param key         property name
@@ -172,6 +258,17 @@
       std::function<void(std::span<const uint8_t>)> setter) = 0;
 
   /**
+   * Add a constant raw property.
+   *
+   * @param key     property name
+   * @param typeString  type string
+   * @param value   the value
+   */
+  virtual void PublishConstRaw(std::string_view key,
+                               std::string_view typeString,
+                               std::span<const uint8_t> value) = 0;
+
+  /**
    * Add a string property (SmallString form).
    *
    * @param key     property name
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h
new file mode 100644
index 0000000..b88894c
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h
@@ -0,0 +1,682 @@
+// 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 <stdint.h>
+
+#include <cassert>
+#include <memory>
+#include <span>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "wpi/MathExtras.h"
+#include "wpi/StringMap.h"
+#include "wpi/bit.h"
+
+namespace wpi {
+
+template <typename T>
+class SmallVectorImpl;
+
+class DynamicStruct;
+class MutableDynamicStruct;
+class StructDescriptor;
+class StructDescriptorDatabase;
+
+/**
+ * Known data types for raw struct dynamic fields (see StructFieldDescriptor).
+ */
+enum class StructFieldType {
+  kBool,
+  kChar,
+  kInt8,
+  kInt16,
+  kInt32,
+  kInt64,
+  kUint8,
+  kUint16,
+  kUint32,
+  kUint64,
+  kFloat,
+  kDouble,
+  kStruct
+};
+
+/**
+ * Raw struct dynamic field descriptor.
+ */
+class StructFieldDescriptor {
+  struct private_init {};
+  friend class DynamicStruct;
+  friend class MutableDynamicStruct;
+  friend class StructDescriptor;
+  friend class StructDescriptorDatabase;
+
+ public:
+  /**
+   * Set of enumerated values.
+   */
+  using EnumValues = std::vector<std::pair<std::string, int64_t>>;
+
+  StructFieldDescriptor(const StructDescriptor* parent, std::string_view name,
+                        StructFieldType type, size_t size, size_t arraySize,
+                        unsigned int bitWidth, EnumValues enumValues,
+                        const StructDescriptor* structDesc,
+                        const private_init&);
+
+  /**
+   * Gets the dynamic struct this field is contained in.
+   *
+   * @return struct descriptor
+   */
+  const StructDescriptor* GetParent() const { return m_parent; }
+
+  /**
+   * Gets the field name.
+   *
+   * @return field name
+   */
+  const std::string& GetName() const { return m_name; }
+
+  /**
+   * Gets the field type.
+   *
+   * @return field type
+   */
+  StructFieldType GetType() const { return m_type; }
+
+  /**
+   * Returns whether the field type is a signed integer.
+   *
+   * @return true if signed integer, false otherwise
+   */
+  bool IsInt() const {
+    return m_type == StructFieldType::kInt8 ||
+           m_type == StructFieldType::kInt16 ||
+           m_type == StructFieldType::kInt32 ||
+           m_type == StructFieldType::kInt64;
+  }
+
+  /**
+   * Returns whether the field type is an unsigned integer.
+   *
+   * @return true if unsigned integer, false otherwise
+   */
+  bool IsUint() const {
+    return m_type == StructFieldType::kUint8 ||
+           m_type == StructFieldType::kUint16 ||
+           m_type == StructFieldType::kUint32 ||
+           m_type == StructFieldType::kUint64;
+  }
+
+  /**
+   * Gets the underlying storage size of the field, in bytes.
+   *
+   * @return number of bytes
+   */
+  size_t GetSize() const { return m_size; }
+
+  /**
+   * Gets the storage offset of the field, in bytes.
+   *
+   * @return number of bytes from the start of the struct
+   */
+  size_t GetOffset() const { return m_offset; }
+
+  /**
+   * Gets the bit width of the field, in bits.
+   *
+   * @return number of bits
+   */
+  unsigned int GetBitWidth() const {
+    return m_bitWidth == 0 ? m_size * 8 : m_bitWidth;
+  }
+
+  /**
+   * Gets the bit mask for the field. The mask is always the least significant
+   * bits (it is not shifted).
+   *
+   * @return bit mask
+   */
+  uint64_t GetBitMask() const { return m_bitMask; }
+
+  /**
+   * Gets the bit shift for the field (LSB=0).
+   *
+   * @return number of bits
+   */
+  unsigned int GetBitShift() const { return m_bitShift; }
+
+  /**
+   * Returns whether the field is an array.
+   *
+   * @return true if array
+   */
+  bool IsArray() const { return m_arraySize > 1; }
+
+  /**
+   * Gets the array size. Returns 1 if non-array.
+   *
+   * @return number of elements
+   */
+  size_t GetArraySize() const { return m_arraySize; }
+
+  /**
+   * Returns whether the field has enumerated values.
+   *
+   * @return true if there are enumerated values
+   */
+  bool HasEnum() const { return !m_enum.empty(); }
+
+  /**
+   * Gets the enumerated values.
+   *
+   * @return set of enumerated values
+   */
+  const EnumValues& GetEnumValues() { return m_enum; }
+
+  /**
+   * Gets the struct descriptor for a struct data type.
+   *
+   * @return struct descriptor; returns null for non-struct
+   */
+  const StructDescriptor* GetStruct() const { return m_struct; }
+
+  /**
+   * Gets the minimum unsigned integer value that can be stored in this field.
+   *
+   * @return minimum value
+   */
+  uint64_t GetUintMin() const { return 0; }
+
+  /**
+   * Gets the maximum unsigned integer value that can be stored in this field.
+   *
+   * @return maximum value
+   */
+  uint64_t GetUintMax() const { return m_bitMask; }
+
+  /**
+   * Gets the minimum signed integer value that can be stored in this field.
+   *
+   * @return minimum value
+   */
+  int64_t GetIntMin() const {
+    return static_cast<int64_t>(-(m_bitMask >> 1)) - 1;
+  }
+
+  /**
+   * Gets the maximum signed integer value that can be stored in this field.
+   *
+   * @return maximum value
+   */
+  int64_t GetIntMax() const { return m_bitMask >> 1; }
+
+  /**
+   * Returns whether the field is a bitfield.
+   *
+   * @return true if bitfield
+   */
+  bool IsBitField() const {
+    return m_bitShift != 0 || m_bitWidth != (m_size * 8);
+  }
+
+ private:
+  // note: constructor fills in everything except offset and shift
+  const StructDescriptor* m_parent;
+  std::string m_name;
+  size_t m_size;
+  size_t m_offset = 0;
+  size_t m_arraySize;  // 1 for non-arrays
+  EnumValues m_enum;
+  const StructDescriptor* m_struct;  // nullptr for non-structs
+  uint64_t m_bitMask;
+  StructFieldType m_type;
+  unsigned int m_bitWidth;
+  unsigned int m_bitShift = 0;
+};
+
+/**
+ * Raw struct dynamic struct descriptor.
+ */
+class StructDescriptor {
+  struct private_init {};
+  friend class StructDescriptorDatabase;
+
+ public:
+  StructDescriptor(std::string_view name, const private_init&) : m_name{name} {}
+
+  /**
+   * Gets the struct name.
+   *
+   * @return name
+   */
+  const std::string& GetName() const { return m_name; }
+
+  /**
+   * Gets the struct schema.
+   *
+   * @return schema
+   */
+  const std::string& GetSchema() const { return m_schema; }
+
+  /**
+   * Returns whether the struct is valid (e.g. the struct is fully defined and
+   * field offsets computed).
+   *
+   * @return true if valid
+   */
+  bool IsValid() const { return m_valid; }
+
+  /**
+   * Returns the struct size, in bytes. Not valid unless IsValid() is true.
+   *
+   * @return size in bytes
+   */
+  size_t GetSize() const {
+    assert(m_valid);
+    return m_size;
+  }
+
+  /**
+   * Gets a field descriptor by name. Note the field cannot be accessed until
+   * the struct is valid.
+   *
+   * @param name field name
+   * @return field descriptor, or nullptr if not found
+   */
+  const StructFieldDescriptor* FindFieldByName(std::string_view name) const;
+
+  /**
+   * Gets all field descriptors. Note fields cannot be accessed until the struct
+   * is valid.
+   *
+   * @return field descriptors
+   */
+  const std::vector<StructFieldDescriptor>& GetFields() const {
+    return m_fields;
+  }
+
+ private:
+  bool CheckCircular(
+      wpi::SmallVectorImpl<const StructDescriptor*>& stack) const;
+  std::string CalculateOffsets(
+      wpi::SmallVectorImpl<const StructDescriptor*>& stack);
+
+  std::string m_name;
+  std::string m_schema;
+  std::vector<StructDescriptor*> m_references;
+  std::vector<StructFieldDescriptor> m_fields;
+  StringMap<size_t> m_fieldsByName;
+  size_t m_size = 0;
+  bool m_valid = false;
+};
+
+/**
+ * Database of raw struct dynamic descriptors.
+ */
+class StructDescriptorDatabase {
+ public:
+  /**
+   * Adds a structure schema to the database. If the struct references other
+   * structs that have not yet been added, it will not be valid until those
+   * structs are also added.
+   *
+   * @param[in] name structure name
+   * @param[in] schema structure schema
+   * @param[out] err detailed error, if nullptr is returned
+   * @return Added struct, or nullptr on error
+   */
+  const StructDescriptor* Add(std::string_view name, std::string_view schema,
+                              std::string* err);
+
+  /**
+   * Finds a structure in the database by name.
+   *
+   * @param name structure name
+   * @return struct descriptor, or nullptr if not found
+   */
+  const StructDescriptor* Find(std::string_view name) const;
+
+ private:
+  StringMap<std::unique_ptr<StructDescriptor>> m_structs;
+};
+
+/**
+ * Dynamic (run-time) read-only access to a serialized raw struct.
+ */
+class DynamicStruct {
+ public:
+  /**
+   * Constructs a new dynamic struct. Note: the passed data is a span; no copy
+   * is made, so it's necessary for the lifetime of the referenced data to be
+   * longer than this object.
+   *
+   * @param desc struct descriptor
+   * @param data serialized data
+   */
+  DynamicStruct(const StructDescriptor* desc, std::span<const uint8_t> data)
+      : m_desc{desc}, m_data{data} {}
+
+  /**
+   * Gets the struct descriptor.
+   *
+   * @return struct descriptor
+   */
+  const StructDescriptor* GetDescriptor() const { return m_desc; }
+
+  /**
+   * Gets the serialized data.
+   *
+   * @return data
+   */
+  std::span<const uint8_t> GetData() const { return m_data; }
+
+  /**
+   * Gets a struct field descriptor by name.
+   *
+   * @param name field name
+   * @return field descriptor, or nullptr if no field with that name exists
+   */
+  const StructFieldDescriptor* FindField(std::string_view name) const {
+    return m_desc->FindFieldByName(name);
+  }
+
+  /**
+   * Gets the value of a boolean field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return field value
+   */
+  bool GetBoolField(const StructFieldDescriptor* field,
+                    size_t arrIndex = 0) const {
+    assert(field->m_type == StructFieldType::kBool);
+    return GetFieldImpl(field, arrIndex);
+  }
+
+  /**
+   * Gets the value of a signed integer field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return field value
+   */
+  int64_t GetIntField(const StructFieldDescriptor* field,
+                      size_t arrIndex = 0) const {
+    assert(field->IsInt());
+    return GetFieldImpl(field, arrIndex);
+  }
+
+  /**
+   * Gets the value of an unsigned integer field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return field value
+   */
+  uint64_t GetUintField(const StructFieldDescriptor* field,
+                        size_t arrIndex = 0) const {
+    assert(field->IsUint());
+    return GetFieldImpl(field, arrIndex);
+  }
+
+  /**
+   * Gets the value of a float field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return field value
+   */
+  float GetFloatField(const StructFieldDescriptor* field,
+                      size_t arrIndex = 0) const {
+    assert(field->m_type == StructFieldType::kFloat);
+    return bit_cast<float>(
+        static_cast<uint32_t>(GetFieldImpl(field, arrIndex)));
+  }
+
+  /**
+   * Gets the value of a double field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return field value
+   */
+  double GetDoubleField(const StructFieldDescriptor* field,
+                        size_t arrIndex = 0) const {
+    assert(field->m_type == StructFieldType::kDouble);
+    return bit_cast<double>(GetFieldImpl(field, arrIndex));
+  }
+
+  /**
+   * Gets the value of a char or char array field.
+   *
+   * @param field field descriptor
+   * @return field value
+   */
+  std::string_view GetStringField(const StructFieldDescriptor* field) const {
+    assert(field->m_type == StructFieldType::kChar);
+    assert(field->m_parent == m_desc);
+    assert(m_desc->IsValid());
+    return {reinterpret_cast<const char*>(&m_data[field->m_offset]),
+            field->m_arraySize};
+  }
+
+  /**
+   * Gets the value of a struct field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return field value
+   */
+  DynamicStruct GetStructField(const StructFieldDescriptor* field,
+                               size_t arrIndex = 0) const {
+    assert(field->m_type == StructFieldType::kStruct);
+    assert(field->m_parent == m_desc);
+    assert(m_desc->IsValid());
+    assert(arrIndex < field->m_arraySize);
+    return DynamicStruct{field->m_struct,
+                         m_data.subspan(field->m_offset +
+                                        arrIndex * field->m_struct->GetSize())};
+  }
+
+ protected:
+  const StructDescriptor* m_desc;
+
+ private:
+  uint64_t GetFieldImpl(const StructFieldDescriptor* field,
+                        size_t arrIndex) const;
+
+  std::span<const uint8_t> m_data;
+};
+
+/**
+ * Dynamic (run-time) mutable access to a serialized raw struct.
+ */
+class MutableDynamicStruct : public DynamicStruct {
+ public:
+  /**
+   * Constructs a new dynamic struct. Note: the passed data is a span; no copy
+   * is made, so it's necessary for the lifetime of the referenced data to be
+   * longer than this object.
+   *
+   * @param desc struct descriptor
+   * @param data serialized data
+   */
+  MutableDynamicStruct(const StructDescriptor* desc, std::span<uint8_t> data)
+      : DynamicStruct{desc, data}, m_data{data} {}
+
+  /**
+   * Gets the serialized data.
+   *
+   * @return data
+   */
+  std::span<uint8_t> GetData() { return m_data; }
+
+  using DynamicStruct::GetData;
+
+  /**
+   * Overwrites the entire serialized struct by copying data from a span.
+   *
+   * @param data replacement data for the struct
+   */
+  void SetData(std::span<const uint8_t> data);
+
+  /**
+   * Sets the value of a boolean field.
+   *
+   * @param field field descriptor
+   * @param value field value
+   * @param arrIndex array index (must be less than field array size)
+   */
+  void SetBoolField(const StructFieldDescriptor* field, bool value,
+                    size_t arrIndex = 0) {
+    assert(field->m_type == StructFieldType::kBool);
+    SetFieldImpl(field, value ? 1 : 0, arrIndex);
+  }
+
+  /**
+   * Sets the value of a signed integer field.
+   *
+   * @param field field descriptor
+   * @param value field value
+   * @param arrIndex array index (must be less than field array size)
+   */
+  void SetIntField(const StructFieldDescriptor* field, int64_t value,
+                   size_t arrIndex = 0) {
+    assert(field->IsInt());
+    SetFieldImpl(field, value, arrIndex);
+  }
+
+  /**
+   * Sets the value of an unsigned integer field.
+   *
+   * @param field field descriptor
+   * @param value field value
+   * @param arrIndex array index (must be less than field array size)
+   */
+  void SetUintField(const StructFieldDescriptor* field, uint64_t value,
+                    size_t arrIndex = 0) {
+    assert(field->IsUint());
+    SetFieldImpl(field, value, arrIndex);
+  }
+
+  /**
+   * Sets the value of a float field.
+   *
+   * @param field field descriptor
+   * @param value field value
+   * @param arrIndex array index (must be less than field array size)
+   */
+  void SetFloatField(const StructFieldDescriptor* field, float value,
+                     size_t arrIndex = 0) {
+    assert(field->m_type == StructFieldType::kFloat);
+    SetFieldImpl(field, bit_cast<uint32_t>(value), arrIndex);
+  }
+
+  /**
+   * Sets the value of a double field.
+   *
+   * @param field field descriptor
+   * @param value field value
+   * @param arrIndex array index (must be less than field array size)
+   */
+  void SetDoubleField(const StructFieldDescriptor* field, double value,
+                      size_t arrIndex = 0) {
+    assert(field->m_type == StructFieldType::kDouble);
+    SetFieldImpl(field, bit_cast<uint64_t>(value), arrIndex);
+  }
+
+  /**
+   * Sets the value of a char or char array field.
+   *
+   * @param field field descriptor
+   * @param value field value
+   */
+  void SetStringField(const StructFieldDescriptor* field,
+                      std::string_view value);
+
+  /**
+   * Sets the value of a struct field.
+   *
+   * @param field field descriptor
+   * @param value field value
+   * @param arrIndex array index (must be less than field array size)
+   */
+  void SetStructField(const StructFieldDescriptor* field,
+                      const DynamicStruct& value, size_t arrIndex = 0);
+
+  /**
+   * Gets the value of a struct field.
+   *
+   * @param field field descriptor
+   * @param arrIndex array index (must be less than field array size)
+   * @return field value
+   */
+  MutableDynamicStruct GetStructField(const StructFieldDescriptor* field,
+                                      size_t arrIndex = 0) {
+    assert(field->m_type == StructFieldType::kStruct);
+    assert(field->m_parent == m_desc);
+    assert(m_desc->IsValid());
+    assert(arrIndex < field->m_arraySize);
+    return MutableDynamicStruct{
+        field->m_struct, m_data.subspan(field->m_offset +
+                                        arrIndex * field->m_struct->GetSize())};
+  }
+
+  using DynamicStruct::GetStructField;
+
+ private:
+  void SetFieldImpl(const StructFieldDescriptor* field, uint64_t value,
+                    size_t arrIndex);
+
+  std::span<uint8_t> m_data;
+};
+
+namespace impl {
+struct DSOData {
+  explicit DSOData(size_t size) : m_dataStore(size) {}
+  explicit DSOData(std::span<const uint8_t> data)
+      : m_dataStore{data.begin(), data.end()} {}
+
+  std::vector<uint8_t> m_dataStore;
+};
+}  // namespace impl
+
+/**
+ * Dynamic (run-time) mutable access to a serialized raw struct, with internal
+ * data storage.
+ */
+class DynamicStructObject : private impl::DSOData, public MutableDynamicStruct {
+  /**
+   * Constructs a new dynamic struct object. The descriptor must be valid.
+   *
+   * @param desc struct descriptor
+   */
+  explicit DynamicStructObject(const StructDescriptor* desc)
+      : DSOData{desc->GetSize()}, MutableDynamicStruct{desc, m_dataStore} {}
+
+  /**
+   * Constructs a new dynamic struct object. Makes a copy of the serialized
+   * data so there are no lifetime constraints on the data parameter.
+   *
+   * @param desc struct descriptor
+   * @param data serialized data
+   */
+  DynamicStructObject(const StructDescriptor* desc,
+                      std::span<const uint8_t> data)
+      : DSOData{data}, MutableDynamicStruct{desc, m_dataStore} {
+    assert(data.size() >= desc->GetSize());
+  }
+
+  // can't be movable due to span references
+  DynamicStructObject(DynamicStructObject&&) = delete;
+  DynamicStructObject& operator=(DynamicStructObject&&) = delete;
+};
+
+}  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h
new file mode 100644
index 0000000..a8cf1a4
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h
@@ -0,0 +1,186 @@
+// 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 <stdint.h>
+
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace wpi::structparser {
+
+/**
+ * A lexed raw struct schema token.
+ */
+struct Token {
+  enum Kind {
+    kUnknown,
+    kInteger,
+    kIdentifier,
+    kLeftBracket,
+    kRightBracket,
+    kLeftBrace,
+    kRightBrace,
+    kColon,
+    kSemicolon,
+    kComma,
+    kEquals,
+    kEndOfInput,
+  };
+
+  Token() = default;
+  Token(Kind kind, std::string_view text) : kind{kind}, text{text} {}
+
+  bool Is(Kind k) const { return kind == k; }
+
+  Kind kind = kUnknown;
+  std::string_view text;
+};
+
+std::string_view ToString(Token::Kind kind);
+
+/**
+ * Raw struct schema lexer.
+ */
+class Lexer {
+ public:
+  /**
+   * Construct a raw struct schema lexer.
+   *
+   * @param in schema
+   */
+  explicit Lexer(std::string_view in) : m_in{in} {}
+
+  /**
+   * Gets the next token.
+   *
+   * @return Token
+   */
+  [[nodiscard]]
+  Token Scan();
+
+  /**
+   * Gets the starting position of the last lexed token.
+   *
+   * @return position (0 = first character)
+   */
+  size_t GetPosition() const { return m_tokenStart; }
+
+ private:
+  Token ScanInteger();
+  Token ScanIdentifier();
+
+  void Get() {
+    if (m_pos < m_in.size()) {
+      [[likely]] m_current = m_in[m_pos];
+    } else {
+      m_current = -1;
+    }
+    ++m_pos;
+  }
+
+  void Unget() {
+    if (m_pos > 0) {
+      [[likely]] m_pos--;
+      if (m_pos < m_in.size()) {
+        [[likely]] m_current = m_in[m_pos];
+      } else {
+        m_current = -1;
+      }
+    } else {
+      m_current = -1;
+    }
+  }
+
+  Token MakeToken(Token::Kind kind) {
+    return {kind, m_in.substr(m_tokenStart, m_pos - m_tokenStart)};
+  }
+
+  std::string_view m_in;
+  int m_current = -1;
+  size_t m_tokenStart = 0;
+  size_t m_pos = 0;
+};
+
+/**
+ * Raw struct set of enumerated values.
+ */
+using EnumValues = std::vector<std::pair<std::string, int64_t>>;
+
+/**
+ * Raw struct schema declaration.
+ */
+struct ParsedDeclaration {
+  std::string typeString;
+  std::string name;
+  EnumValues enumValues;
+  size_t arraySize = 1;
+  unsigned int bitWidth = 0;
+};
+
+/**
+ * Raw struct schema.
+ */
+struct ParsedSchema {
+  std::vector<ParsedDeclaration> declarations;
+};
+
+/**
+ * Raw struct schema parser.
+ */
+class Parser {
+ public:
+  /**
+   * Construct a raw struct schema parser.
+   *
+   * @param in schema
+   */
+  explicit Parser(std::string_view in) : m_lexer{in} {}
+
+  /**
+   * Parses the schema.
+   *
+   * @param[out] out parsed schema object
+   * @return true on success, false on failure (use GetError() to get error)
+   */
+  [[nodiscard]]
+  bool Parse(ParsedSchema* out);
+
+  /**
+   * Gets the parser error if one occurred.
+   *
+   * @return parser error; blank if no error occurred
+   */
+  const std::string& GetError() const { return m_error; }
+
+ private:
+  [[nodiscard]]
+  bool ParseDeclaration(ParsedDeclaration* out);
+  [[nodiscard]]
+  bool ParseEnum(EnumValues* out);
+
+  Token::Kind GetNextToken() {
+    m_token = m_lexer.Scan();
+    return m_token.kind;
+  }
+  [[nodiscard]]
+  bool Expect(Token::Kind kind) {
+    if (m_token.Is(kind)) {
+      [[likely]] return true;
+    }
+    FailExpect(kind);
+    return false;
+  }
+  void FailExpect(Token::Kind desired);
+  void Fail(std::string_view msg);
+
+  Lexer m_lexer;
+  Token m_token;
+  std::string m_error;
+};
+
+}  // namespace wpi::structparser
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/struct/Struct.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/struct/Struct.h
new file mode 100644
index 0000000..1336707
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/struct/Struct.h
@@ -0,0 +1,533 @@
+// 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 <stdint.h>
+
+#include <concepts>
+#include <span>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "wpi/Endian.h"
+#include "wpi/MathExtras.h"
+#include "wpi/bit.h"
+#include "wpi/ct_string.h"
+#include "wpi/function_ref.h"
+#include "wpi/mutex.h"
+
+namespace wpi {
+
+/**
+ * Struct serialization template. Unspecialized class has no members; only
+ * specializations of this class are useful, and only if they meet the
+ * StructSerializable concept.
+ *
+ * @tparam T type to serialize/deserialize
+ */
+template <typename T>
+struct Struct {};
+
+/**
+ * Specifies that a type is capable of raw struct serialization and
+ * deserialization.
+ *
+ * This is designed for serializing small fixed-size data structures in the
+ * fastest and most compact means possible. Serialization consists of writing
+ * values into a mutable std::span and deserialization consists of reading
+ * values from an immutable std::span.
+ *
+ * Implementations must define a template specialization for wpi::Struct with
+ * T being the type that is being serialized/deserialized, with the following
+ * static members (as enforced by this concept):
+ * - std::string_view kTypeString: the type string
+ * - size_t kSize: the structure size in bytes
+ * - std::string_view kSchema: the struct schema
+ * - T Unpack(std::span<const uint8_t, kSize>): function for deserialization
+ * - void Pack(std::span<uint8_t, kSize>, T&& value): function for
+ *   serialization
+ *
+ * If the struct has nested structs, implementations should also meet the
+ * requirements of HasNestedStruct<T>.
+ */
+template <typename T>
+concept StructSerializable =
+    requires(std::span<const uint8_t> in, std::span<uint8_t> out, T&& value) {
+      typename Struct<typename std::remove_cvref_t<T>>;
+      {
+        Struct<typename std::remove_cvref_t<T>>::kTypeString
+      } -> std::convertible_to<std::string_view>;
+      {
+        Struct<typename std::remove_cvref_t<T>>::kSize
+      } -> std::convertible_to<size_t>;
+      {
+        Struct<typename std::remove_cvref_t<T>>::kSchema
+      } -> std::convertible_to<std::string_view>;
+      {
+        Struct<typename std::remove_cvref_t<T>>::Unpack(
+            in.subspan<0, Struct<typename std::remove_cvref_t<T>>::kSize>())
+      } -> std::same_as<typename std::remove_cvref_t<T>>;
+      Struct<typename std::remove_cvref_t<T>>::Pack(
+          out.subspan<0, Struct<typename std::remove_cvref_t<T>>::kSize>(),
+          std::forward<T>(value));
+    };
+
+/**
+ * Specifies that a type is capable of in-place raw struct deserialization.
+ *
+ * In addition to meeting StructSerializable, implementations must define a
+ * wpi::Struct<T> static member `void UnpackInto(T*, std::span<const uint8_t>)`
+ * to update the pointed-to T with the contents of the span.
+ */
+template <typename T>
+concept MutableStructSerializable =
+    StructSerializable<T> && requires(T* out, std::span<const uint8_t> in) {
+      Struct<typename std::remove_cvref_t<T>>::UnpackInto(
+          out, in.subspan<0, Struct<typename std::remove_cvref_t<T>>::kSize>());
+    };
+
+/**
+ * Specifies that a struct type has nested struct declarations.
+ *
+ * In addition to meeting StructSerializable, implementations must define a
+ * wpi::Struct<T> static member
+ * `void ForEachNested(std::invocable<std::string_view, std::string_view) auto
+ * fn)` (or equivalent) and call ForEachNestedStruct<Type> on each nested struct
+ * type.
+ */
+template <typename T>
+concept HasNestedStruct =
+    StructSerializable<T> &&
+    requires(function_ref<void(std::string_view, std::string_view)> fn) {
+      Struct<typename std::remove_cvref_t<T>>::ForEachNested(fn);
+    };
+
+/**
+ * Unpack a serialized struct.
+ *
+ * @tparam T object type
+ * @param data raw struct data
+ * @return Deserialized object
+ */
+template <StructSerializable T>
+inline T UnpackStruct(std::span<const uint8_t, Struct<T>::kSize> data) {
+  return Struct<T>::Unpack(data);
+}
+
+/**
+ * Unpack a serialized struct starting at a given offset within the data.
+ * This is primarily useful in unpack implementations to unpack nested structs.
+ *
+ * @tparam T object type
+ * @tparam Offset starting offset
+ * @param data raw struct data
+ * @return Deserialized object
+ */
+template <StructSerializable T, size_t Offset>
+inline T UnpackStruct(std::span<const uint8_t> data) {
+  return Struct<T>::Unpack(data.template subspan<Offset, Struct<T>::kSize>());
+}
+
+/**
+ * Pack a serialized struct.
+ *
+ * @param data struct storage (mutable, output)
+ * @param value object
+ */
+template <StructSerializable T>
+inline void PackStruct(
+    std::span<uint8_t, Struct<typename std::remove_cvref_t<T>>::kSize> data,
+    T&& value) {
+  Struct<typename std::remove_cvref_t<T>>::Pack(data, std::forward<T>(value));
+}
+
+/**
+ * Pack a serialized struct starting at a given offset within the data. This is
+ * primarily useful in pack implementations to pack nested structs.
+ *
+ * @tparam Offset starting offset
+ * @param data struct storage (mutable, output)
+ * @param value object
+ */
+template <size_t Offset, StructSerializable T>
+inline void PackStruct(std::span<uint8_t> data, T&& value) {
+  Struct<typename std::remove_cvref_t<T>>::Pack(
+      data.template subspan<Offset,
+                            Struct<typename std::remove_cvref_t<T>>::kSize>(),
+      std::forward<T>(value));
+}
+
+/**
+ * Unpack a serialized struct into an existing object, overwriting its contents.
+ *
+ * @param out object (output)
+ * @param data raw struct data
+ */
+template <StructSerializable T>
+inline void UnpackStructInto(T* out,
+                             std::span<const uint8_t, Struct<T>::kSize> data) {
+  if constexpr (MutableStructSerializable<T>) {
+    Struct<T>::UnpackInto(out, data);
+  } else {
+    *out = UnpackStruct<T>(data);
+  }
+}
+
+/**
+ * Unpack a serialized struct into an existing object, overwriting its contents,
+ * and starting at a given offset within the data.
+ * This is primarily useful in unpack implementations to unpack nested structs.
+ *
+ * @tparam Offset starting offset
+ * @param out object (output)
+ * @param data raw struct data
+ */
+template <size_t Offset, StructSerializable T>
+inline void UnpackStructInto(T* out, std::span<const uint8_t> data) {
+  if constexpr (MutableStructSerializable<T>) {
+    Struct<T>::UnpackInto(out,
+                          data.template subspan<Offset, Struct<T>::kSize>());
+  } else {
+    *out = UnpackStruct<T, Offset>(data);
+  }
+}
+
+/**
+ * Get the type string for a raw struct serializable type
+ *
+ * @tparam T serializable type
+ * @return type string
+ */
+template <StructSerializable T>
+constexpr auto GetStructTypeString() {
+  return Struct<T>::kTypeString;
+}
+
+template <StructSerializable T, size_t N>
+consteval auto MakeStructArrayTypeString() {
+  using namespace literals;
+  if constexpr (N == std::dynamic_extent) {
+    return Concat(
+        ct_string<char, std::char_traits<char>, Struct<T>::kTypeString.size()>{
+            Struct<T>::kTypeString},
+        "[]"_ct_string);
+  } else {
+    return Concat(
+        ct_string<char, std::char_traits<char>, Struct<T>::kTypeString.size()>{
+            Struct<T>::kTypeString},
+        "["_ct_string, NumToCtString<N>(), "]"_ct_string);
+  }
+}
+
+template <StructSerializable T, size_t N>
+consteval auto MakeStructArraySchema() {
+  using namespace literals;
+  if constexpr (N == std::dynamic_extent) {
+    return Concat(
+        ct_string<char, std::char_traits<char>, Struct<T>::kSchema.size()>{
+            Struct<T>::kSchema},
+        "[]"_ct_string);
+  } else {
+    return Concat(
+        ct_string<char, std::char_traits<char>, Struct<T>::kSchema.size()>{
+            Struct<T>::kSchema},
+        "["_ct_string, NumToCtString<N>(), "]"_ct_string);
+  }
+}
+
+template <StructSerializable T>
+constexpr std::string_view GetStructSchema() {
+  return Struct<T>::kSchema;
+}
+
+template <StructSerializable T>
+constexpr std::span<const uint8_t> GetStructSchemaBytes() {
+  return {reinterpret_cast<const uint8_t*>(Struct<T>::kSchema.data()),
+          Struct<T>::kSchema.size()};
+}
+
+template <StructSerializable T>
+void ForEachStructSchema(
+    std::invocable<std::string_view, std::string_view> auto fn) {
+  if constexpr (HasNestedStruct<T>) {
+    Struct<T>::ForEachNested(fn);
+  }
+  fn(Struct<T>::kTypeString, Struct<T>::kSchema);
+}
+
+template <StructSerializable T>
+class StructArrayBuffer {
+  using S = Struct<T>;
+
+ public:
+  StructArrayBuffer() = default;
+  StructArrayBuffer(const StructArrayBuffer&) = delete;
+  StructArrayBuffer& operator=(const StructArrayBuffer&) = delete;
+  StructArrayBuffer(StructArrayBuffer&& rhs) : m_buf{std::move(rhs.m_buf)} {}
+  StructArrayBuffer& operator=(StructArrayBuffer&& rhs) {
+    m_buf = std::move(rhs.m_buf);
+    return *this;
+  }
+
+  template <typename U, typename F>
+    requires
+#if __cpp_lib_ranges >= 201911L
+      std::ranges::range<U> &&
+      std::convertible_to<std::ranges::range_value_t<U>, T> &&
+#endif
+      std::invocable<F, std::span<const uint8_t>>
+    void Write(U&& data, F&& func) {
+    if ((std::size(data) * S::kSize) < 256) {
+      // use the stack
+      uint8_t buf[256];
+      auto out = buf;
+      for (auto&& val : data) {
+        S::Pack(std::span<uint8_t, S::kSize>{out, out + S::kSize},
+                std::forward<decltype(val)>(val));
+        out += S::kSize;
+      }
+      func(std::span<uint8_t>{buf, out});
+    } else {
+      std::scoped_lock lock{m_mutex};
+      m_buf.resize(std::size(data) * S::kSize);
+      auto out = m_buf.begin();
+      for (auto&& val : data) {
+        S::Pack(std::span<uint8_t, S::kSize>{out, out + S::kSize},
+                std::forward<decltype(val)>(val));
+        out += S::kSize;
+      }
+      func(m_buf);
+    }
+  }
+
+ private:
+  wpi::mutex m_mutex;
+  std::vector<uint8_t> m_buf;
+};
+
+/**
+ * Raw struct support for fixed-size arrays of other structs.
+ */
+template <StructSerializable T, size_t N>
+struct Struct<std::array<T, N>> {
+  static constexpr auto kTypeString = MakeStructArrayTypeString<T, N>();
+  static constexpr size_t kSize = N * Struct<T>::kSize;
+  static constexpr auto kSchema = MakeStructArraySchema<T, N>();
+  static std::array<T, N> Unpack(std::span<const uint8_t, kSize> data) {
+    std::array<T, N> result;
+    for (size_t i = 0; i < N; ++i) {
+      result[i] = UnpackStruct<T, 0>(data);
+      data = data.subspan(Struct<T>::kSize);
+    }
+    return result;
+  }
+  static void Pack(std::span<uint8_t, kSize> data,
+                   std::span<const T, N> values) {
+    std::span<uint8_t> unsizedData = data;
+    for (auto&& val : values) {
+      PackStruct<0>(unsizedData, val);
+      unsizedData = unsizedData.subspan(Struct<T>::kSize);
+    }
+  }
+  static void UnpackInto(std::array<T, N>* out,
+                         std::span<const uint8_t, kSize> data) {
+    UnpackInto(std::span{*out}, data);
+  }
+  // alternate span-based function
+  static void UnpackInto(std::span<T, N> out,
+                         std::span<const uint8_t, kSize> data) {
+    std::span<const uint8_t> unsizedData = data;
+    for (size_t i = 0; i < N; ++i) {
+      UnpackStructInto<0, T>(&out[i], unsizedData);
+      unsizedData = unsizedData.subspan(Struct<T>::kSize);
+    }
+  }
+};
+
+/**
+ * Raw struct support for boolean values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<bool> {
+  static constexpr std::string_view kTypeString = "struct:bool";
+  static constexpr size_t kSize = 1;
+  static constexpr std::string_view kSchema = "bool value";
+  static bool Unpack(std::span<const uint8_t, 1> data) { return data[0]; }
+  static void Pack(std::span<uint8_t, 1> data, bool value) {
+    data[0] = static_cast<char>(value ? 1 : 0);
+  }
+};
+
+/**
+ * Raw struct support for uint8_t values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<uint8_t> {
+  static constexpr std::string_view kTypeString = "struct:uint8";
+  static constexpr size_t kSize = 1;
+  static constexpr std::string_view kSchema = "uint8 value";
+  static uint8_t Unpack(std::span<const uint8_t, 1> data) { return data[0]; }
+  static void Pack(std::span<uint8_t, 1> data, uint8_t value) {
+    data[0] = value;
+  }
+};
+
+/**
+ * Raw struct support for int8_t values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<int8_t> {
+  static constexpr std::string_view kTypeString = "struct:int8";
+  static constexpr size_t kSize = 1;
+  static constexpr std::string_view kSchema = "int8 value";
+  static int8_t Unpack(std::span<const uint8_t, 1> data) { return data[0]; }
+  static void Pack(std::span<uint8_t, 1> data, int8_t value) {
+    data[0] = value;
+  }
+};
+
+/**
+ * Raw struct support for uint16_t values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<uint16_t> {
+  static constexpr std::string_view kTypeString = "struct:uint16";
+  static constexpr size_t kSize = 2;
+  static constexpr std::string_view kSchema = "uint16 value";
+  static uint16_t Unpack(std::span<const uint8_t, 2> data) {
+    return support::endian::read16le(data.data());
+  }
+  static void Pack(std::span<uint8_t, 2> data, uint16_t value) {
+    support::endian::write16le(data.data(), value);
+  }
+};
+
+/**
+ * Raw struct support for int16_t values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<int16_t> {
+  static constexpr std::string_view kTypeString = "struct:int16";
+  static constexpr size_t kSize = 2;
+  static constexpr std::string_view kSchema = "int16 value";
+  static int16_t Unpack(std::span<const uint8_t, 2> data) {
+    return support::endian::read16le(data.data());
+  }
+  static void Pack(std::span<uint8_t, 2> data, int16_t value) {
+    support::endian::write16le(data.data(), value);
+  }
+};
+
+/**
+ * Raw struct support for uint32_t values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<uint32_t> {
+  static constexpr std::string_view kTypeString = "struct:uint32";
+  static constexpr size_t kSize = 4;
+  static constexpr std::string_view kSchema = "uint32 value";
+  static uint32_t Unpack(std::span<const uint8_t, 4> data) {
+    return support::endian::read32le(data.data());
+  }
+  static void Pack(std::span<uint8_t, 4> data, uint32_t value) {
+    support::endian::write32le(data.data(), value);
+  }
+};
+
+/**
+ * Raw struct support for int32_t values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<int32_t> {
+  static constexpr std::string_view kTypeString = "struct:int32";
+  static constexpr size_t kSize = 4;
+  static constexpr std::string_view kSchema = "int32 value";
+  static int32_t Unpack(std::span<const uint8_t, 4> data) {
+    return support::endian::read32le(data.data());
+  }
+  static void Pack(std::span<uint8_t, 4> data, int32_t value) {
+    support::endian::write32le(data.data(), value);
+  }
+};
+
+/**
+ * Raw struct support for uint64_t values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<uint64_t> {
+  static constexpr std::string_view kTypeString = "struct:uint64";
+  static constexpr size_t kSize = 8;
+  static constexpr std::string_view kSchema = "uint64 value";
+  static uint64_t Unpack(std::span<const uint8_t, 8> data) {
+    return support::endian::read64le(data.data());
+  }
+  static void Pack(std::span<uint8_t, 8> data, uint64_t value) {
+    support::endian::write64le(data.data(), value);
+  }
+};
+
+/**
+ * Raw struct support for int64_t values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<int64_t> {
+  static constexpr std::string_view kTypeString = "struct:int64";
+  static constexpr size_t kSize = 8;
+  static constexpr std::string_view kSchema = "int64 value";
+  static int64_t Unpack(std::span<const uint8_t, 8> data) {
+    return support::endian::read64le(data.data());
+  }
+  static void Pack(std::span<uint8_t, 8> data, int64_t value) {
+    support::endian::write64le(data.data(), value);
+  }
+};
+
+/**
+ * Raw struct support for float values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<float> {
+  static constexpr std::string_view kTypeString = "struct:float";
+  static constexpr size_t kSize = 4;
+  static constexpr std::string_view kSchema = "float value";
+  static float Unpack(std::span<const uint8_t, 4> data) {
+    return bit_cast<float>(support::endian::read32le(data.data()));
+  }
+  static void Pack(std::span<uint8_t, 4> data, float value) {
+    support::endian::write32le(data.data(), bit_cast<uint32_t>(value));
+  }
+};
+
+/**
+ * Raw struct support for double values.
+ * Primarily useful for higher level struct implementations.
+ */
+template <>
+struct Struct<double> {
+  static constexpr std::string_view kTypeString = "struct:double";
+  static constexpr size_t kSize = 8;
+  static constexpr std::string_view kSchema = "double value";
+  static double Unpack(std::span<const uint8_t, 8> data) {
+    return bit_cast<double>(support::endian::read64le(data.data()));
+  }
+  static void Pack(std::span<uint8_t, 8> data, double value) {
+    support::endian::write64le(data.data(), bit_cast<uint64_t>(value));
+  }
+};
+
+}  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/timestamp.h b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/timestamp.h
index c50117c..c232481 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/include/wpi/timestamp.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/include/wpi/timestamp.h
@@ -12,6 +12,19 @@
 #endif
 
 /**
+ * Initialize the on-Rio Now() implementation to use the FPGA timestamp.
+ * No effect on non-Rio platforms. This is called by HAL_Initialize() and
+ * thus should generally not be called by user code.
+ */
+void WPI_Impl_SetupNowRio(void);
+
+/**
+ * De-initialize the on-Rio Now() implementation. No effect on non-Rio
+ * platforms.
+ */
+void WPI_Impl_ShutdownNowRio(void);
+
+/**
  * The default implementation used for Now().
  * In general this is the time returned by the operating system.
  * @return Time in microseconds.
@@ -48,12 +61,27 @@
 #ifdef __cplusplus
 namespace wpi {
 
+namespace impl {
+/**
+ * Initialize the on-Rio Now() implementation to use the FPGA timestamp.
+ * No effect on non-Rio platforms. This is called by HAL_Initialize() and
+ * thus should generally not be called by user code.
+ */
+void SetupNowRio();
+
+/**
+ * De-initialize the on-Rio Now() implementation. No effect on non-Rio
+ * platforms.
+ */
+void ShutdownNowRio();
+}  // namespace impl
+
 /**
  * The default implementation used for Now().
  * In general this is the time returned by the operating system.
  * @return Time in microseconds.
  */
-uint64_t NowDefault(void);
+uint64_t NowDefault();
 
 /**
  * Set the implementation used by Now().
@@ -68,7 +96,7 @@
  * This is a monotonic clock with an undefined epoch.
  * @return Time in microseconds.
  */
-uint64_t Now(void);
+uint64_t Now();
 
 /**
  * Return the current system time in microseconds since the Unix epoch
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/args.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/args.h
index a3966d1..2d684e7 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/args.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/args.h
@@ -1,4 +1,4 @@
-// Formatting library for C++ - dynamic format arguments
+// Formatting library for C++ - dynamic argument lists
 //
 // Copyright (c) 2012 - present, Victor Zverovich
 // All rights reserved.
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/chrono.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/chrono.h
index b112f76..ff3e144 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/chrono.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/chrono.h
@@ -22,6 +22,24 @@
 
 FMT_BEGIN_NAMESPACE
 
+// Check if std::chrono::local_t is available.
+#ifndef FMT_USE_LOCAL_TIME
+#  ifdef __cpp_lib_chrono
+#    define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
+#  else
+#    define FMT_USE_LOCAL_TIME 0
+#  endif
+#endif
+
+// Check if std::chrono::utc_timestamp is available.
+#ifndef FMT_USE_UTC_TIME
+#  ifdef __cpp_lib_chrono
+#    define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
+#  else
+#    define FMT_USE_UTC_TIME 0
+#  endif
+#endif
+
 // Enable tzset.
 #ifndef FMT_USE_TZSET
 // UWP doesn't provide _tzset.
@@ -203,7 +221,8 @@
     }
     const auto min1 =
         (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
-    if (!std::is_unsigned<IntermediateRep>::value && count < min1) {
+    if (detail::const_check(!std::is_unsigned<IntermediateRep>::value) &&
+        count < min1) {
       ec = 1;
       return {};
     }
@@ -358,37 +377,11 @@
     unit_t unit;
     write_codecvt(unit, in, loc);
     // In UTF-8 is used one to four one-byte code units.
-    auto&& buf = basic_memory_buffer<char, unit_t::max_size * 4>();
-    for (code_unit* p = unit.buf; p != unit.end; ++p) {
-      uint32_t c = static_cast<uint32_t>(*p);
-      if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
-        // surrogate pair
-        ++p;
-        if (p == unit.end || (c & 0xfc00) != 0xd800 ||
-            (*p & 0xfc00) != 0xdc00) {
-          FMT_THROW(format_error("failed to format time"));
-        }
-        c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
-      }
-      if (c < 0x80) {
-        buf.push_back(static_cast<char>(c));
-      } else if (c < 0x800) {
-        buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
-        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
-      } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
-        buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
-        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
-        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
-      } else if (c >= 0x10000 && c <= 0x10ffff) {
-        buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
-        buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
-        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
-        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
-      } else {
-        FMT_THROW(format_error("failed to format time"));
-      }
-    }
-    return copy_str<char>(buf.data(), buf.data() + buf.size(), out);
+    auto u =
+        to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>();
+    if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
+      FMT_THROW(format_error("failed to format time"));
+    return copy_str<char>(u.c_str(), u.c_str() + u.size(), out);
   }
   return copy_str<char>(in.data(), in.data() + in.size(), out);
 }
@@ -427,7 +420,7 @@
            char format, char modifier = 0) -> OutputIt {
   auto&& buf = get_buffer<Char>(out);
   do_write<Char>(buf, time, loc, format, modifier);
-  return buf.out();
+  return get_iterator(buf, out);
 }
 
 template <typename Char, typename OutputIt,
@@ -441,7 +434,7 @@
 
 }  // namespace detail
 
-FMT_MODULE_EXPORT_BEGIN
+FMT_BEGIN_EXPORT
 
 /**
   Converts given time since epoch as ``std::time_t`` value into calendar time,
@@ -484,10 +477,13 @@
   return lt.tm_;
 }
 
-inline std::tm localtime(
-    std::chrono::time_point<std::chrono::system_clock> time_point) {
-  return localtime(std::chrono::system_clock::to_time_t(time_point));
+#if FMT_USE_LOCAL_TIME
+template <typename Duration>
+inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
+  return localtime(std::chrono::system_clock::to_time_t(
+      std::chrono::current_zone()->to_sys(time)));
 }
+#endif
 
 /**
   Converts given time since epoch as ``std::time_t`` value into calendar time,
@@ -523,7 +519,7 @@
     }
 #endif
   };
-  dispatcher gt(time);
+  auto gt = dispatcher(time);
   // Too big time values may be unsupported.
   if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
   return gt.tm_;
@@ -534,7 +530,7 @@
   return gmtime(std::chrono::system_clock::to_time_t(time_point));
 }
 
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
 
 // Writes two-digit numbers a, b and c separated by sep to buf.
 // The method by Pavel Novikov based on
@@ -599,12 +595,39 @@
   alternative
 };
 
+// Glibc extensions for formatting numeric values.
+enum class pad_type {
+  unspecified,
+  // Do not pad a numeric result string.
+  none,
+  // Pad a numeric result string with zeros even if the conversion specifier
+  // character uses space-padding by default.
+  zero,
+  // Pad a numeric result string with spaces.
+  space,
+};
+
+template <typename OutputIt>
+auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt {
+  if (pad == pad_type::none) return out;
+  return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0');
+}
+
+template <typename OutputIt>
+auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
+  if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0';
+  return out;
+}
+
 // Parses a put_time-like format string and invokes handler actions.
 template <typename Char, typename Handler>
 FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
                                               const Char* end,
                                               Handler&& handler) {
+  if (begin == end || *begin == '}') return begin;
+  if (*begin != '%') FMT_THROW(format_error("invalid format"));
   auto ptr = begin;
+  pad_type pad = pad_type::unspecified;
   while (ptr != end) {
     auto c = *ptr;
     if (c == '}') break;
@@ -615,6 +638,22 @@
     if (begin != ptr) handler.on_text(begin, ptr);
     ++ptr;  // consume '%'
     if (ptr == end) FMT_THROW(format_error("invalid format"));
+    c = *ptr;
+    switch (c) {
+    case '_':
+      pad = pad_type::space;
+      ++ptr;
+      break;
+    case '-':
+      pad = pad_type::none;
+      ++ptr;
+      break;
+    case '0':
+      pad = pad_type::zero;
+      ++ptr;
+      break;
+    }
+    if (ptr == end) FMT_THROW(format_error("invalid format"));
     c = *ptr++;
     switch (c) {
     case '%':
@@ -691,16 +730,16 @@
       break;
     // Hour, minute, second:
     case 'H':
-      handler.on_24_hour(numeric_system::standard);
+      handler.on_24_hour(numeric_system::standard, pad);
       break;
     case 'I':
-      handler.on_12_hour(numeric_system::standard);
+      handler.on_12_hour(numeric_system::standard, pad);
       break;
     case 'M':
-      handler.on_minute(numeric_system::standard);
+      handler.on_minute(numeric_system::standard, pad);
       break;
     case 'S':
-      handler.on_second(numeric_system::standard);
+      handler.on_second(numeric_system::standard, pad);
       break;
     // Other:
     case 'c':
@@ -737,7 +776,7 @@
       handler.on_duration_unit();
       break;
     case 'z':
-      handler.on_utc_offset();
+      handler.on_utc_offset(numeric_system::standard);
       break;
     case 'Z':
       handler.on_tz_name();
@@ -765,6 +804,9 @@
       case 'X':
         handler.on_loc_time(numeric_system::alternative);
         break;
+      case 'z':
+        handler.on_utc_offset(numeric_system::alternative);
+        break;
       default:
         FMT_THROW(format_error("invalid format"));
       }
@@ -802,16 +844,19 @@
         handler.on_dec1_weekday(numeric_system::alternative);
         break;
       case 'H':
-        handler.on_24_hour(numeric_system::alternative);
+        handler.on_24_hour(numeric_system::alternative, pad);
         break;
       case 'I':
-        handler.on_12_hour(numeric_system::alternative);
+        handler.on_12_hour(numeric_system::alternative, pad);
         break;
       case 'M':
-        handler.on_minute(numeric_system::alternative);
+        handler.on_minute(numeric_system::alternative, pad);
         break;
       case 'S':
-        handler.on_second(numeric_system::alternative);
+        handler.on_second(numeric_system::alternative, pad);
+        break;
+      case 'z':
+        handler.on_utc_offset(numeric_system::alternative);
         break;
       default:
         FMT_THROW(format_error("invalid format"));
@@ -864,7 +909,7 @@
   FMT_CONSTEXPR void on_am_pm() { unsupported(); }
   FMT_CONSTEXPR void on_duration_value() { unsupported(); }
   FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
-  FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
+  FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); }
   FMT_CONSTEXPR void on_tz_name() { unsupported(); }
 };
 
@@ -892,10 +937,10 @@
   FMT_CONSTEXPR void on_day_of_year() {}
   FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
   FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
-  FMT_CONSTEXPR void on_24_hour(numeric_system) {}
-  FMT_CONSTEXPR void on_12_hour(numeric_system) {}
-  FMT_CONSTEXPR void on_minute(numeric_system) {}
-  FMT_CONSTEXPR void on_second(numeric_system) {}
+  FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_second(numeric_system, pad_type) {}
   FMT_CONSTEXPR void on_datetime(numeric_system) {}
   FMT_CONSTEXPR void on_loc_date(numeric_system) {}
   FMT_CONSTEXPR void on_loc_time(numeric_system) {}
@@ -905,7 +950,7 @@
   FMT_CONSTEXPR void on_24_hour_time() {}
   FMT_CONSTEXPR void on_iso_time() {}
   FMT_CONSTEXPR void on_am_pm() {}
-  FMT_CONSTEXPR void on_utc_offset() {}
+  FMT_CONSTEXPR void on_utc_offset(numeric_system) {}
   FMT_CONSTEXPR void on_tz_name() {}
 };
 
@@ -957,13 +1002,130 @@
 }
 #endif
 
-template <typename OutputIt, typename Char> class tm_writer {
+// Converts value to Int and checks that it's in the range [0, upper).
+template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline Int to_nonnegative_int(T value, Int upper) {
+  FMT_ASSERT(std::is_unsigned<Int>::value ||
+                 (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
+             "invalid value");
+  (void)upper;
+  return static_cast<Int>(value);
+}
+template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+inline Int to_nonnegative_int(T value, Int upper) {
+  if (value < 0 || value > static_cast<T>(upper))
+    FMT_THROW(format_error("invalid value"));
+  return static_cast<Int>(value);
+}
+
+constexpr long long pow10(std::uint32_t n) {
+  return n == 0 ? 1 : 10 * pow10(n - 1);
+}
+
+// Counts the number of fractional digits in the range [0, 18] according to the
+// C++20 spec. If more than 18 fractional digits are required then returns 6 for
+// microseconds precision.
+template <long long Num, long long Den, int N = 0,
+          bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
+struct count_fractional_digits {
+  static constexpr int value =
+      Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
+};
+
+// Base case that doesn't instantiate any more templates
+// in order to avoid overflow.
+template <long long Num, long long Den, int N>
+struct count_fractional_digits<Num, Den, N, false> {
+  static constexpr int value = (Num % Den == 0) ? N : 6;
+};
+
+// Format subseconds which are given as an integer type with an appropriate
+// number of digits.
+template <typename Char, typename OutputIt, typename Duration>
+void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
+  constexpr auto num_fractional_digits =
+      count_fractional_digits<Duration::period::num,
+                              Duration::period::den>::value;
+
+  using subsecond_precision = std::chrono::duration<
+      typename std::common_type<typename Duration::rep,
+                                std::chrono::seconds::rep>::type,
+      std::ratio<1, detail::pow10(num_fractional_digits)>>;
+
+  const auto fractional =
+      d - std::chrono::duration_cast<std::chrono::seconds>(d);
+  const auto subseconds =
+      std::chrono::treat_as_floating_point<
+          typename subsecond_precision::rep>::value
+          ? fractional.count()
+          : std::chrono::duration_cast<subsecond_precision>(fractional).count();
+  auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
+  const int num_digits = detail::count_digits(n);
+
+  int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits);
+  if (precision < 0) {
+    FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
+    if (std::ratio_less<typename subsecond_precision::period,
+                        std::chrono::seconds::period>::value) {
+      *out++ = '.';
+      out = std::fill_n(out, leading_zeroes, '0');
+      out = format_decimal<Char>(out, n, num_digits).end;
+    }
+  } else {
+    *out++ = '.';
+    leading_zeroes = (std::min)(leading_zeroes, precision);
+    out = std::fill_n(out, leading_zeroes, '0');
+    int remaining = precision - leading_zeroes;
+    if (remaining != 0 && remaining < num_digits) {
+      n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining)));
+      out = format_decimal<Char>(out, n, remaining).end;
+      return;
+    }
+    out = format_decimal<Char>(out, n, num_digits).end;
+    remaining -= num_digits;
+    out = std::fill_n(out, remaining, '0');
+  }
+}
+
+// Format subseconds which are given as a floating point type with an
+// appropriate number of digits. We cannot pass the Duration here, as we
+// explicitly need to pass the Rep value in the chrono_formatter.
+template <typename Duration>
+void write_floating_seconds(memory_buffer& buf, Duration duration,
+                            int num_fractional_digits = -1) {
+  using rep = typename Duration::rep;
+  FMT_ASSERT(std::is_floating_point<rep>::value, "");
+
+  auto val = duration.count();
+
+  if (num_fractional_digits < 0) {
+    // For `std::round` with fallback to `round`:
+    // On some toolchains `std::round` is not available (e.g. GCC 6).
+    using namespace std;
+    num_fractional_digits =
+        count_fractional_digits<Duration::period::num,
+                                Duration::period::den>::value;
+    if (num_fractional_digits < 6 && static_cast<rep>(round(val)) != val)
+      num_fractional_digits = 6;
+  }
+
+  format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
+            std::fmod(val * static_cast<rep>(Duration::period::num) /
+                          static_cast<rep>(Duration::period::den),
+                      static_cast<rep>(60)),
+            num_fractional_digits);
+}
+
+template <typename OutputIt, typename Char,
+          typename Duration = std::chrono::seconds>
+class tm_writer {
  private:
   static constexpr int days_per_week = 7;
 
   const std::locale& loc_;
   const bool is_classic_;
   OutputIt out_;
+  const Duration* subsecs_;
   const std::tm& tm_;
 
   auto tm_sec() const noexcept -> int {
@@ -1051,6 +1213,17 @@
     *out_++ = *d++;
     *out_++ = *d;
   }
+  void write2(int value, pad_type pad) {
+    unsigned int v = to_unsigned(value) % 100;
+    if (v >= 10) {
+      const char* d = digits2(v);
+      *out_++ = *d++;
+      *out_++ = *d;
+    } else {
+      out_ = detail::write_padding(out_, pad);
+      *out_++ = static_cast<char>('0' + v);
+    }
+  }
 
   void write_year_extended(long long year) {
     // At least 4 characters.
@@ -1074,7 +1247,7 @@
     }
   }
 
-  void write_utc_offset(long offset) {
+  void write_utc_offset(long offset, numeric_system ns) {
     if (offset < 0) {
       *out_++ = '-';
       offset = -offset;
@@ -1083,14 +1256,15 @@
     }
     offset /= 60;
     write2(static_cast<int>(offset / 60));
+    if (ns != numeric_system::standard) *out_++ = ':';
     write2(static_cast<int>(offset % 60));
   }
   template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
-  void format_utc_offset_impl(const T& tm) {
-    write_utc_offset(tm.tm_gmtoff);
+  void format_utc_offset_impl(const T& tm, numeric_system ns) {
+    write_utc_offset(tm.tm_gmtoff, ns);
   }
   template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
-  void format_utc_offset_impl(const T& tm) {
+  void format_utc_offset_impl(const T& tm, numeric_system ns) {
 #if defined(_WIN32) && defined(_UCRT)
 #  if FMT_USE_TZSET
     tzset_once();
@@ -1102,10 +1276,17 @@
       _get_dstbias(&dstbias);
       offset += dstbias;
     }
-    write_utc_offset(-offset);
+    write_utc_offset(-offset, ns);
 #else
-    ignore_unused(tm);
-    format_localized('z');
+    if (ns == numeric_system::standard) return format_localized('z');
+
+    // Extract timezone offset from timezone conversion functions.
+    std::tm gtm = tm;
+    std::time_t gt = std::mktime(&gtm);
+    std::tm ltm = gmtime(gt);
+    std::time_t lt = std::mktime(&ltm);
+    long offset = gt - lt;
+    write_utc_offset(offset, ns);
 #endif
   }
 
@@ -1126,10 +1307,12 @@
   }
 
  public:
-  tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm)
+  tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm,
+            const Duration* subsecs = nullptr)
       : loc_(loc),
         is_classic_(loc_ == get_classic_locale()),
         out_(out),
+        subsecs_(subsecs),
         tm_(tm) {}
 
   OutputIt out() const { return out_; }
@@ -1227,7 +1410,7 @@
     out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
   }
 
-  void on_utc_offset() { format_utc_offset_impl(tm_); }
+  void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
   void on_tz_name() { format_tz_name_impl(tm_); }
 
   void on_year(numeric_system ns) {
@@ -1315,22 +1498,41 @@
     }
   }
 
-  void on_24_hour(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour());
+  void on_24_hour(numeric_system ns, pad_type pad) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(tm_hour(), pad);
     format_localized('H', 'O');
   }
-  void on_12_hour(numeric_system ns) {
+  void on_12_hour(numeric_system ns, pad_type pad) {
     if (is_classic_ || ns == numeric_system::standard)
-      return write2(tm_hour12());
+      return write2(tm_hour12(), pad);
     format_localized('I', 'O');
   }
-  void on_minute(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) return write2(tm_min());
+  void on_minute(numeric_system ns, pad_type pad) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(tm_min(), pad);
     format_localized('M', 'O');
   }
-  void on_second(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec());
-    format_localized('S', 'O');
+
+  void on_second(numeric_system ns, pad_type pad) {
+    if (is_classic_ || ns == numeric_system::standard) {
+      write2(tm_sec(), pad);
+      if (subsecs_) {
+        if (std::is_floating_point<typename Duration::rep>::value) {
+          auto buf = memory_buffer();
+          write_floating_seconds(buf, *subsecs_);
+          if (buf.size() > 1) {
+            // Remove the leading "0", write something like ".123".
+            out_ = std::copy(buf.begin() + 1, buf.end(), out_);
+          }
+        } else {
+          write_fractional_seconds<Char>(out_, *subsecs_);
+        }
+      }
+    } else {
+      // Currently no formatting of subseconds when a locale is set.
+      format_localized('S', 'O');
+    }
   }
 
   void on_12_hour_time() {
@@ -1351,10 +1553,9 @@
     write2(tm_min());
   }
   void on_iso_time() {
-    char buf[8];
-    write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()),
-                           to_unsigned(tm_sec()), ':');
-    out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
+    on_24_hour_time();
+    *out_++ = ':';
+    on_second(numeric_system::standard, pad_type::unspecified);
   }
 
   void on_am_pm() {
@@ -1372,43 +1573,34 @@
 };
 
 struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
+  bool has_precision_integral = false;
+
   FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
 
   template <typename Char>
   FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
-  FMT_CONSTEXPR void on_24_hour(numeric_system) {}
-  FMT_CONSTEXPR void on_12_hour(numeric_system) {}
-  FMT_CONSTEXPR void on_minute(numeric_system) {}
-  FMT_CONSTEXPR void on_second(numeric_system) {}
+  FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_second(numeric_system, pad_type) {}
   FMT_CONSTEXPR void on_12_hour_time() {}
   FMT_CONSTEXPR void on_24_hour_time() {}
   FMT_CONSTEXPR void on_iso_time() {}
   FMT_CONSTEXPR void on_am_pm() {}
-  FMT_CONSTEXPR void on_duration_value() {}
+  FMT_CONSTEXPR void on_duration_value() const {
+    if (has_precision_integral) {
+      FMT_THROW(format_error("precision not allowed for this argument type"));
+    }
+  }
   FMT_CONSTEXPR void on_duration_unit() {}
 };
 
-template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+template <typename T,
+          FMT_ENABLE_IF(std::is_integral<T>::value&& has_isfinite<T>::value)>
 inline bool isfinite(T) {
   return true;
 }
 
-// Converts value to Int and checks that it's in the range [0, upper).
-template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
-inline Int to_nonnegative_int(T value, Int upper) {
-  FMT_ASSERT(std::is_unsigned<Int>::value ||
-             (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
-             "invalid value");
-  (void)upper;
-  return static_cast<Int>(value);
-}
-template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-inline Int to_nonnegative_int(T value, Int upper) {
-  if (value < 0 || value > static_cast<T>(upper))
-    FMT_THROW(format_error("invalid value"));
-  return static_cast<Int>(value);
-}
-
 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
 inline T mod(T x, int y) {
   return x % static_cast<T>(y);
@@ -1463,47 +1655,6 @@
 #endif
 }
 
-// Counts the number of fractional digits in the range [0, 18] according to the
-// C++20 spec. If more than 18 fractional digits are required then returns 6 for
-// microseconds precision.
-template <long long Num, long long Den, int N = 0,
-          bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
-struct count_fractional_digits {
-  static constexpr int value =
-      Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
-};
-
-// Base case that doesn't instantiate any more templates
-// in order to avoid overflow.
-template <long long Num, long long Den, int N>
-struct count_fractional_digits<Num, Den, N, false> {
-  static constexpr int value = (Num % Den == 0) ? N : 6;
-};
-
-constexpr long long pow10(std::uint32_t n) {
-  return n == 0 ? 1 : 10 * pow10(n - 1);
-}
-
-template <class Rep, class Period,
-          FMT_ENABLE_IF(std::numeric_limits<Rep>::is_signed)>
-constexpr std::chrono::duration<Rep, Period> abs(
-    std::chrono::duration<Rep, Period> d) {
-  // We need to compare the duration using the count() method directly
-  // due to a compiler bug in clang-11 regarding the spaceship operator,
-  // when -Wzero-as-null-pointer-constant is enabled.
-  // In clang-12 the bug has been fixed. See
-  // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
-  // https://www.godbolt.org/z/Knbb5joYx.
-  return d.count() >= d.zero().count() ? d : -d;
-}
-
-template <class Rep, class Period,
-          FMT_ENABLE_IF(!std::numeric_limits<Rep>::is_signed)>
-constexpr std::chrono::duration<Rep, Period> abs(
-    std::chrono::duration<Rep, Period> d) {
-  return d;
-}
-
 template <typename Char, typename Rep, typename OutputIt,
           FMT_ENABLE_IF(std::is_integral<Rep>::value)>
 OutputIt format_duration_value(OutputIt out, Rep val, int) {
@@ -1513,7 +1664,7 @@
 template <typename Char, typename Rep, typename OutputIt,
           FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
 OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
-  auto specs = basic_format_specs<Char>();
+  auto specs = format_specs<Char>();
   specs.precision = precision;
   specs.type = precision >= 0 ? presentation_type::fixed_lower
                               : presentation_type::general_lower;
@@ -1654,44 +1805,16 @@
     }
   }
 
-  void write(Rep value, int width) {
+  void write(Rep value, int width, pad_type pad = pad_type::unspecified) {
     write_sign();
     if (isnan(value)) return write_nan();
     uint32_or_64_or_128_t<int> n =
         to_unsigned(to_nonnegative_int(value, max_value<int>()));
     int num_digits = detail::count_digits(n);
-    if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
-    out = format_decimal<char_type>(out, n, num_digits).end;
-  }
-
-  template <typename Duration> void write_fractional_seconds(Duration d) {
-    FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
-    constexpr auto num_fractional_digits =
-        count_fractional_digits<Duration::period::num,
-                                Duration::period::den>::value;
-
-    using subsecond_precision = std::chrono::duration<
-        typename std::common_type<typename Duration::rep,
-                                  std::chrono::seconds::rep>::type,
-        std::ratio<1, detail::pow10(num_fractional_digits)>>;
-    if (std::ratio_less<typename subsecond_precision::period,
-                        std::chrono::seconds::period>::value) {
-      *out++ = '.';
-      auto fractional =
-          detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
-      auto subseconds =
-          std::chrono::treat_as_floating_point<
-              typename subsecond_precision::rep>::value
-              ? fractional.count()
-              : std::chrono::duration_cast<subsecond_precision>(fractional)
-                    .count();
-      uint32_or_64_or_128_t<long long> n =
-          to_unsigned(to_nonnegative_int(subseconds, max_value<long long>()));
-      int num_digits = detail::count_digits(n);
-      if (num_fractional_digits > num_digits)
-        out = std::fill_n(out, num_fractional_digits - num_digits, '0');
-      out = format_decimal<char_type>(out, n, num_digits).end;
+    if (width > num_digits) {
+      out = detail::write_padding(out, pad, width - num_digits);
     }
+    out = format_decimal<char_type>(out, n, num_digits).end;
   }
 
   void write_nan() { std::copy_n("nan", 3, out); }
@@ -1723,7 +1846,7 @@
   void on_loc_time(numeric_system) {}
   void on_us_date() {}
   void on_iso_date() {}
-  void on_utc_offset() {}
+  void on_utc_offset(numeric_system) {}
   void on_tz_name() {}
   void on_year(numeric_system) {}
   void on_short_year(numeric_system) {}
@@ -1739,58 +1862,56 @@
   void on_day_of_month(numeric_system) {}
   void on_day_of_month_space(numeric_system) {}
 
-  void on_24_hour(numeric_system ns) {
+  void on_24_hour(numeric_system ns, pad_type pad) {
     if (handle_nan_inf()) return;
 
-    if (ns == numeric_system::standard) return write(hour(), 2);
+    if (ns == numeric_system::standard) return write(hour(), 2, pad);
     auto time = tm();
     time.tm_hour = to_nonnegative_int(hour(), 24);
-    format_tm(time, &tm_writer_type::on_24_hour, ns);
+    format_tm(time, &tm_writer_type::on_24_hour, ns, pad);
   }
 
-  void on_12_hour(numeric_system ns) {
+  void on_12_hour(numeric_system ns, pad_type pad) {
     if (handle_nan_inf()) return;
 
-    if (ns == numeric_system::standard) return write(hour12(), 2);
+    if (ns == numeric_system::standard) return write(hour12(), 2, pad);
     auto time = tm();
     time.tm_hour = to_nonnegative_int(hour12(), 12);
-    format_tm(time, &tm_writer_type::on_12_hour, ns);
+    format_tm(time, &tm_writer_type::on_12_hour, ns, pad);
   }
 
-  void on_minute(numeric_system ns) {
+  void on_minute(numeric_system ns, pad_type pad) {
     if (handle_nan_inf()) return;
 
-    if (ns == numeric_system::standard) return write(minute(), 2);
+    if (ns == numeric_system::standard) return write(minute(), 2, pad);
     auto time = tm();
     time.tm_min = to_nonnegative_int(minute(), 60);
-    format_tm(time, &tm_writer_type::on_minute, ns);
+    format_tm(time, &tm_writer_type::on_minute, ns, pad);
   }
 
-  void on_second(numeric_system ns) {
+  void on_second(numeric_system ns, pad_type pad) {
     if (handle_nan_inf()) return;
 
     if (ns == numeric_system::standard) {
       if (std::is_floating_point<rep>::value) {
-        constexpr auto num_fractional_digits =
-            count_fractional_digits<Period::num, Period::den>::value;
         auto buf = memory_buffer();
-        format_to(std::back_inserter(buf), runtime("{:.{}f}"),
-                  std::fmod(val * static_cast<rep>(Period::num) /
-                                static_cast<rep>(Period::den),
-                            static_cast<rep>(60)),
-                  num_fractional_digits);
+        write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
+                               precision);
         if (negative) *out++ = '-';
-        if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
+        if (buf.size() < 2 || buf[1] == '.') {
+          out = detail::write_padding(out, pad);
+        }
         out = std::copy(buf.begin(), buf.end(), out);
       } else {
-        write(second(), 2);
-        write_fractional_seconds(std::chrono::duration<rep, Period>(val));
+        write(second(), 2, pad);
+        write_fractional_seconds<char_type>(
+            out, std::chrono::duration<rep, Period>(val), precision);
       }
       return;
     }
     auto time = tm();
     time.tm_sec = to_nonnegative_int(second(), 60);
-    format_tm(time, &tm_writer_type::on_second, ns);
+    format_tm(time, &tm_writer_type::on_second, ns, pad);
   }
 
   void on_12_hour_time() {
@@ -1814,7 +1935,7 @@
     on_24_hour_time();
     *out++ = ':';
     if (handle_nan_inf()) return;
-    on_second(numeric_system::standard);
+    on_second(numeric_system::standard, pad_type::unspecified);
   }
 
   void on_am_pm() {
@@ -1833,7 +1954,7 @@
   }
 };
 
-FMT_END_DETAIL_NAMESPACE
+}  // namespace detail
 
 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
 using weekday = std::chrono::weekday;
@@ -1883,118 +2004,67 @@
 template <typename Rep, typename Period, typename Char>
 struct formatter<std::chrono::duration<Rep, Period>, Char> {
  private:
-  basic_format_specs<Char> specs;
-  int precision = -1;
-  using arg_ref_type = detail::arg_ref<Char>;
-  arg_ref_type width_ref;
-  arg_ref_type precision_ref;
-  bool localized = false;
-  basic_string_view<Char> format_str;
-  using duration = std::chrono::duration<Rep, Period>;
-
-  struct spec_handler {
-    formatter& f;
-    basic_format_parse_context<Char>& context;
-    basic_string_view<Char> format_str;
-
-    template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
-      context.check_arg_id(arg_id);
-      return arg_ref_type(arg_id);
-    }
-
-    FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
-      context.check_arg_id(arg_id);
-      return arg_ref_type(arg_id);
-    }
-
-    FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
-      return arg_ref_type(context.next_arg_id());
-    }
-
-    void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
-    FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
-      f.specs.fill = fill;
-    }
-    FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
-    FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
-    FMT_CONSTEXPR void on_precision(int _precision) {
-      f.precision = _precision;
-    }
-    FMT_CONSTEXPR void end_precision() {}
-
-    template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
-      f.width_ref = make_arg_ref(arg_id);
-    }
-
-    template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
-      f.precision_ref = make_arg_ref(arg_id);
-    }
-  };
-
-  using iterator = typename basic_format_parse_context<Char>::iterator;
-  struct parse_range {
-    iterator begin;
-    iterator end;
-  };
-
-  FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
-    auto begin = ctx.begin(), end = ctx.end();
-    if (begin == end || *begin == '}') return {begin, begin};
-    spec_handler handler{*this, ctx, format_str};
-    begin = detail::parse_align(begin, end, handler);
-    if (begin == end) return {begin, begin};
-    begin = detail::parse_width(begin, end, handler);
-    if (begin == end) return {begin, begin};
-    if (*begin == '.') {
-      if (std::is_floating_point<Rep>::value)
-        begin = detail::parse_precision(begin, end, handler);
-      else
-        handler.on_error("precision not allowed for this argument type");
-    }
-    if (begin != end && *begin == 'L') {
-      ++begin;
-      localized = true;
-    }
-    end = detail::parse_chrono_format(begin, end,
-                                      detail::chrono_format_checker());
-    return {begin, end};
-  }
+  format_specs<Char> specs_;
+  detail::arg_ref<Char> width_ref_;
+  detail::arg_ref<Char> precision_ref_;
+  bool localized_ = false;
+  basic_string_view<Char> format_str_;
 
  public:
   FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
       -> decltype(ctx.begin()) {
-    auto range = do_parse(ctx);
-    format_str = basic_string_view<Char>(
-        &*range.begin, detail::to_unsigned(range.end - range.begin));
-    return range.end;
+    auto it = ctx.begin(), end = ctx.end();
+    if (it == end || *it == '}') return it;
+
+    it = detail::parse_align(it, end, specs_);
+    if (it == end) return it;
+
+    it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
+    if (it == end) return it;
+
+    auto checker = detail::chrono_format_checker();
+    if (*it == '.') {
+      checker.has_precision_integral = !std::is_floating_point<Rep>::value;
+      it = detail::parse_precision(it, end, specs_.precision, precision_ref_,
+                                   ctx);
+    }
+    if (it != end && *it == 'L') {
+      localized_ = true;
+      ++it;
+    }
+    end = detail::parse_chrono_format(it, end, checker);
+    format_str_ = {it, detail::to_unsigned(end - it)};
+    return end;
   }
 
   template <typename FormatContext>
-  auto format(const duration& d, FormatContext& ctx) const
+  auto format(std::chrono::duration<Rep, Period> d, FormatContext& ctx) const
       -> decltype(ctx.out()) {
-    auto specs_copy = specs;
-    auto precision_copy = precision;
-    auto begin = format_str.begin(), end = format_str.end();
+    auto specs = specs_;
+    auto precision = specs.precision;
+    specs.precision = -1;
+    auto begin = format_str_.begin(), end = format_str_.end();
     // As a possible future optimization, we could avoid extra copying if width
     // is not specified.
-    basic_memory_buffer<Char> buf;
+    auto buf = basic_memory_buffer<Char>();
     auto out = std::back_inserter(buf);
-    detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
-                                                       width_ref, ctx);
-    detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
-                                                           precision_ref, ctx);
+    detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
+                                                       ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(precision,
+                                                           precision_ref_, ctx);
     if (begin == end || *begin == '}') {
-      out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
+      out = detail::format_duration_value<Char>(out, d.count(), precision);
       detail::format_duration_unit<Char, Period>(out);
     } else {
-      detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
-          ctx, out, d);
-      f.precision = precision_copy;
-      f.localized = localized;
+      using chrono_formatter =
+          detail::chrono_formatter<FormatContext, decltype(out), Rep, Period>;
+      auto f = chrono_formatter(ctx, out, d);
+      f.precision = precision;
+      f.localized = localized_;
       detail::parse_chrono_format(begin, end, f);
     }
     return detail::write(
-        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
+        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
   }
 };
 
@@ -2002,68 +2072,137 @@
 struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
                  Char> : formatter<std::tm, Char> {
   FMT_CONSTEXPR formatter() {
-    basic_string_view<Char> default_specs =
-        detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
-    this->do_parse(default_specs.begin(), default_specs.end());
+    this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
   }
 
   template <typename FormatContext>
-  auto format(std::chrono::time_point<std::chrono::system_clock> val,
+  auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val,
               FormatContext& ctx) const -> decltype(ctx.out()) {
-    return formatter<std::tm, Char>::format(localtime(val), ctx);
+    using period = typename Duration::period;
+    if (detail::const_check(
+            period::num != 1 || period::den != 1 ||
+            std::is_floating_point<typename Duration::rep>::value)) {
+      const auto epoch = val.time_since_epoch();
+      auto subsecs = std::chrono::duration_cast<Duration>(
+          epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
+
+      if (subsecs.count() < 0) {
+        auto second =
+            std::chrono::duration_cast<Duration>(std::chrono::seconds(1));
+        if (epoch.count() < ((Duration::min)() + second).count())
+          FMT_THROW(format_error("duration is too small"));
+        subsecs += second;
+        val -= second;
+      }
+
+      return formatter<std::tm, Char>::do_format(
+          gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx,
+          &subsecs);
+    }
+
+    return formatter<std::tm, Char>::format(
+        gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx);
   }
 };
 
+#if FMT_USE_LOCAL_TIME
+template <typename Char, typename Duration>
+struct formatter<std::chrono::local_time<Duration>, Char>
+    : formatter<std::tm, Char> {
+  FMT_CONSTEXPR formatter() {
+    this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
+  }
+
+  template <typename FormatContext>
+  auto format(std::chrono::local_time<Duration> val, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    using period = typename Duration::period;
+    if (period::num != 1 || period::den != 1 ||
+        std::is_floating_point<typename Duration::rep>::value) {
+      const auto epoch = val.time_since_epoch();
+      const auto subsecs = std::chrono::duration_cast<Duration>(
+          epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
+
+      return formatter<std::tm, Char>::do_format(
+          localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
+          ctx, &subsecs);
+    }
+
+    return formatter<std::tm, Char>::format(
+        localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
+        ctx);
+  }
+};
+#endif
+
+#if FMT_USE_UTC_TIME
+template <typename Char, typename Duration>
+struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
+                 Char>
+    : formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
+                Char> {
+  template <typename FormatContext>
+  auto format(std::chrono::time_point<std::chrono::utc_clock, Duration> val,
+              FormatContext& ctx) const -> decltype(ctx.out()) {
+    return formatter<
+        std::chrono::time_point<std::chrono::system_clock, Duration>,
+        Char>::format(std::chrono::utc_clock::to_sys(val), ctx);
+  }
+};
+#endif
+
 template <typename Char> struct formatter<std::tm, Char> {
  private:
-  enum class spec {
-    unknown,
-    year_month_day,
-    hh_mm_ss,
-  };
-  spec spec_ = spec::unknown;
-  basic_string_view<Char> specs;
+  format_specs<Char> specs_;
+  detail::arg_ref<Char> width_ref_;
 
  protected:
-  template <typename It> FMT_CONSTEXPR auto do_parse(It begin, It end) -> It {
-    if (begin != end && *begin == ':') ++begin;
-    end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
-    // Replace default spec only if the new spec is not empty.
-    if (end != begin) specs = {begin, detail::to_unsigned(end - begin)};
-    return end;
+  basic_string_view<Char> format_str_;
+
+  template <typename FormatContext, typename Duration>
+  auto do_format(const std::tm& tm, FormatContext& ctx,
+                 const Duration* subsecs) const -> decltype(ctx.out()) {
+    auto specs = specs_;
+    auto buf = basic_memory_buffer<Char>();
+    auto out = std::back_inserter(buf);
+    detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
+                                                       ctx);
+
+    auto loc_ref = ctx.locale();
+    detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
+    auto w =
+        detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
+    detail::parse_chrono_format(format_str_.begin(), format_str_.end(), w);
+    return detail::write(
+        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
   }
 
  public:
   FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
       -> decltype(ctx.begin()) {
-    auto end = this->do_parse(ctx.begin(), ctx.end());
-    // basic_string_view<>::compare isn't constexpr before C++17.
-    if (specs.size() == 2 && specs[0] == Char('%')) {
-      if (specs[1] == Char('F'))
-        spec_ = spec::year_month_day;
-      else if (specs[1] == Char('T'))
-        spec_ = spec::hh_mm_ss;
-    }
+    auto it = ctx.begin(), end = ctx.end();
+    if (it == end || *it == '}') return it;
+
+    it = detail::parse_align(it, end, specs_);
+    if (it == end) return it;
+
+    it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
+    if (it == end) return it;
+
+    end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
+    // Replace the default format_str only if the new spec is not empty.
+    if (end != it) format_str_ = {it, detail::to_unsigned(end - it)};
     return end;
   }
 
   template <typename FormatContext>
   auto format(const std::tm& tm, FormatContext& ctx) const
       -> decltype(ctx.out()) {
-    const auto loc_ref = ctx.locale();
-    detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
-    auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), tm);
-    if (spec_ == spec::year_month_day)
-      w.on_iso_date();
-    else if (spec_ == spec::hh_mm_ss)
-      w.on_iso_time();
-    else
-      detail::parse_chrono_format(specs.begin(), specs.end(), w);
-    return w.out();
+    return do_format<FormatContext, std::chrono::seconds>(tm, ctx, nullptr);
   }
 };
 
-FMT_MODULE_EXPORT_END
+FMT_END_EXPORT
 FMT_END_NAMESPACE
 
 #endif  // FMT_CHRONO_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/color.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/color.h
index 4c16327..8697e1c 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/color.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/color.h
@@ -11,7 +11,7 @@
 #include "format.h"
 
 FMT_BEGIN_NAMESPACE
-FMT_MODULE_EXPORT_BEGIN
+FMT_BEGIN_EXPORT
 
 enum class color : uint32_t {
   alice_blue = 0xF0F8FF,               // rgb(240,248,255)
@@ -203,7 +203,7 @@
   uint8_t b;
 };
 
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
 
 // color is a struct of either a rgb color or a terminal color.
 struct color_type {
@@ -225,8 +225,7 @@
     uint32_t rgb_color;
   } value;
 };
-
-FMT_END_DETAIL_NAMESPACE
+}  // namespace detail
 
 /** A text style consisting of foreground and background colors and emphasis. */
 class text_style {
@@ -323,7 +322,7 @@
   return text_style(lhs) | rhs;
 }
 
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
 
 template <typename Char> struct ansi_color_escape {
   FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@@ -423,26 +422,6 @@
   return ansi_color_escape<Char>(em);
 }
 
-template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
-  int result = std::fputs(chars, stream);
-  if (result < 0)
-    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
-}
-
-template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
-  int result = std::fputws(chars, stream);
-  if (result < 0)
-    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
-}
-
-template <typename Char> inline void reset_color(FILE* stream) {
-  fputs("\x1b[0m", stream);
-}
-
-template <> inline void reset_color<wchar_t>(FILE* stream) {
-  fputs(L"\x1b[0m", stream);
-}
-
 template <typename Char> inline void reset_color(buffer<Char>& buffer) {
   auto reset_color = string_view("\x1b[0m");
   buffer.append(reset_color.begin(), reset_color.end());
@@ -477,19 +456,21 @@
   if (has_style) detail::reset_color<Char>(buf);
 }
 
-FMT_END_DETAIL_NAMESPACE
+}  // namespace detail
 
-template <typename S, typename Char = char_t<S>>
-void vprint(std::FILE* f, const text_style& ts, const S& format,
-            basic_format_args<buffer_context<type_identity_t<Char>>> args) {
-  basic_memory_buffer<Char> buf;
-  detail::vformat_to(buf, ts, detail::to_string_view(format), args);
+inline void vprint(std::FILE* f, const text_style& ts, string_view fmt,
+                   format_args args) {
+  // Legacy wide streams are not supported.
+  auto buf = memory_buffer();
+  detail::vformat_to(buf, ts, fmt, args);
   if (detail::is_utf8()) {
-    detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
-  } else {
-    buf.push_back(Char(0));
-    detail::fputs(buf.data(), f);
+    detail::print(f, string_view(buf.begin(), buf.size()));
+    return;
   }
+  buf.push_back('\0');
+  int result = std::fputs(buf.data(), f);
+  if (result < 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
 }
 
 /**
@@ -566,7 +547,7 @@
     basic_format_args<buffer_context<type_identity_t<Char>>> args) {
   auto&& buf = detail::get_buffer<Char>(out);
   detail::vformat_to(buf, ts, format_str, args);
-  return detail::get_iterator(buf);
+  return detail::get_iterator(buf, out);
 }
 
 /**
@@ -645,7 +626,7 @@
   return detail::styled_arg<remove_cvref_t<T>>{value, ts};
 }
 
-FMT_MODULE_EXPORT_END
+FMT_END_EXPORT
 FMT_END_NAMESPACE
 
 #endif  // FMT_COLOR_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/compile.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/compile.h
index 933668c..a4c7e49 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/compile.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/compile.h
@@ -19,84 +19,6 @@
   return it + (end - begin);
 }
 
-template <typename OutputIt> class truncating_iterator_base {
- protected:
-  OutputIt out_;
-  size_t limit_;
-  size_t count_ = 0;
-
-  truncating_iterator_base() : out_(), limit_(0) {}
-
-  truncating_iterator_base(OutputIt out, size_t limit)
-      : out_(out), limit_(limit) {}
-
- public:
-  using iterator_category = std::output_iterator_tag;
-  using value_type = typename std::iterator_traits<OutputIt>::value_type;
-  using difference_type = std::ptrdiff_t;
-  using pointer = void;
-  using reference = void;
-  FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
-
-  OutputIt base() const { return out_; }
-  size_t count() const { return count_; }
-};
-
-// An output iterator that truncates the output and counts the number of objects
-// written to it.
-template <typename OutputIt,
-          typename Enable = typename std::is_void<
-              typename std::iterator_traits<OutputIt>::value_type>::type>
-class truncating_iterator;
-
-template <typename OutputIt>
-class truncating_iterator<OutputIt, std::false_type>
-    : public truncating_iterator_base<OutputIt> {
-  mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
-
- public:
-  using value_type = typename truncating_iterator_base<OutputIt>::value_type;
-
-  truncating_iterator() = default;
-
-  truncating_iterator(OutputIt out, size_t limit)
-      : truncating_iterator_base<OutputIt>(out, limit) {}
-
-  truncating_iterator& operator++() {
-    if (this->count_++ < this->limit_) ++this->out_;
-    return *this;
-  }
-
-  truncating_iterator operator++(int) {
-    auto it = *this;
-    ++*this;
-    return it;
-  }
-
-  value_type& operator*() const {
-    return this->count_ < this->limit_ ? *this->out_ : blackhole_;
-  }
-};
-
-template <typename OutputIt>
-class truncating_iterator<OutputIt, std::true_type>
-    : public truncating_iterator_base<OutputIt> {
- public:
-  truncating_iterator() = default;
-
-  truncating_iterator(OutputIt out, size_t limit)
-      : truncating_iterator_base<OutputIt>(out, limit) {}
-
-  template <typename T> truncating_iterator& operator=(T val) {
-    if (this->count_++ < this->limit_) *this->out_++ = val;
-    return *this;
-  }
-
-  truncating_iterator& operator++() { return *this; }
-  truncating_iterator& operator++(int) { return *this; }
-  truncating_iterator& operator*() { return *this; }
-};
-
 // A compile-time string which is compiled into fast formatting code.
 class compiled_string {};
 
@@ -196,7 +118,8 @@
 
   template <typename OutputIt, typename... Args>
   constexpr OutputIt format(OutputIt out, const Args&...) const {
-    return write<Char>(out, value);
+    *out++ = value;
+    return out;
   }
 };
 
@@ -220,7 +143,12 @@
 
   template <typename OutputIt, typename... Args>
   constexpr OutputIt format(OutputIt out, const Args&... args) const {
-    return write<Char>(out, get_arg_checked<T, N>(args...));
+    const T& arg = get_arg_checked<T, N>(args...);
+    if constexpr (std::is_convertible_v<T, basic_string_view<Char>>) {
+      auto s = basic_string_view<Char>(arg);
+      return copy_str<Char>(s.begin(), s.end(), out);
+    }
+    return write<Char>(out, arg);
   }
 };
 
@@ -331,14 +259,14 @@
   int next_arg_id;
 };
 
-constexpr int manual_indexing_id = -1;
+enum { manual_indexing_id = -1 };
 
 template <typename T, typename Char>
 constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
                                                   size_t pos, int next_arg_id) {
   str.remove_prefix(pos);
-  auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {},
-                                         next_arg_id);
+  auto ctx =
+      compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
   auto f = formatter<T, Char>();
   auto end = f.parse(ctx);
   return {f, pos + fmt::detail::to_unsigned(end - str.data()),
@@ -348,22 +276,18 @@
 template <typename Char> struct arg_id_handler {
   arg_ref<Char> arg_id;
 
-  constexpr int operator()() {
+  constexpr int on_auto() {
     FMT_ASSERT(false, "handler cannot be used with automatic indexing");
     return 0;
   }
-  constexpr int operator()(int id) {
+  constexpr int on_index(int id) {
     arg_id = arg_ref<Char>(id);
     return 0;
   }
-  constexpr int operator()(basic_string_view<Char> id) {
+  constexpr int on_name(basic_string_view<Char> id) {
     arg_id = arg_ref<Char>(id);
     return 0;
   }
-
-  constexpr void on_error(const char* message) {
-    FMT_THROW(format_error(message));
-  }
 };
 
 template <typename Char> struct parse_arg_id_result {
@@ -452,20 +376,18 @@
       } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
         constexpr auto arg_index =
             get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
-        if constexpr (arg_index != invalid_arg_index) {
+        if constexpr (arg_index >= 0) {
           constexpr auto next_id =
               ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
           return parse_replacement_field_then_tail<
               decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
               arg_index, next_id>(format_str);
-        } else {
-          if constexpr (c == '}') {
-            return parse_tail<Args, arg_id_end_pos + 1, ID>(
-                runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
-                format_str);
-          } else if constexpr (c == ':') {
-            return unknown_format();  // no type info for specs parsing
-          }
+        } else if constexpr (c == '}') {
+          return parse_tail<Args, arg_id_end_pos + 1, ID>(
+              runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
+              format_str);
+        } else if constexpr (c == ':') {
+          return unknown_format();  // no type info for specs parsing
         }
       }
     }
@@ -501,7 +423,7 @@
 #endif  // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
 }  // namespace detail
 
-FMT_MODULE_EXPORT_BEGIN
+FMT_BEGIN_EXPORT
 
 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
 
@@ -568,9 +490,10 @@
           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
 format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
                                          const S& format_str, Args&&... args) {
-  auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
-                           format_str, std::forward<Args>(args)...);
-  return {it.base(), it.count()};
+  using traits = detail::fixed_buffer_traits;
+  auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
+  format_to(std::back_inserter(buf), format_str, std::forward<Args>(args)...);
+  return {buf.out(), buf.count()};
 }
 
 template <typename S, typename... Args,
@@ -605,7 +528,7 @@
 }  // namespace literals
 #endif
 
-FMT_MODULE_EXPORT_END
+FMT_END_EXPORT
 FMT_END_NAMESPACE
 
 #endif  // FMT_COMPILE_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/core.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/core.h
index 5c210bc..1fe1388 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/core.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/core.h
@@ -13,11 +13,12 @@
 #include <cstring>  // std::strlen
 #include <iterator>
 #include <limits>
+#include <memory>  // std::addressof
 #include <string>
 #include <type_traits>
 
 // The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 90100
+#define FMT_VERSION 100100
 
 #if defined(__clang__) && !defined(__ibmxl__)
 #  define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
@@ -69,9 +70,7 @@
 #  define FMT_HAS_FEATURE(x) 0
 #endif
 
-#if (defined(__has_include) || FMT_ICC_VERSION >= 1600 || \
-     FMT_MSC_VERSION > 1900) &&                           \
-    !defined(__INTELLISENSE__)
+#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900
 #  define FMT_HAS_INCLUDE(x) __has_include(x)
 #else
 #  define FMT_HAS_INCLUDE(x) 0
@@ -94,7 +93,7 @@
 #ifndef FMT_USE_CONSTEXPR
 #  if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \
        (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) &&             \
-      !FMT_ICC_VERSION && !defined(__NVCC__)
+      !FMT_ICC_VERSION && (!defined(__NVCC__) || FMT_CPLUSPLUS >= 202002L)
 #    define FMT_USE_CONSTEXPR 1
 #  else
 #    define FMT_USE_CONSTEXPR 0
@@ -140,22 +139,7 @@
 #  endif
 #endif
 
-#ifndef FMT_DEPRECATED
-#  if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
-#    define FMT_DEPRECATED [[deprecated]]
-#  else
-#    if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
-#      define FMT_DEPRECATED __attribute__((deprecated))
-#    elif FMT_MSC_VERSION
-#      define FMT_DEPRECATED __declspec(deprecated)
-#    else
-#      define FMT_DEPRECATED /* deprecated */
-#    endif
-#  endif
-#endif
-
-// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
-// warnings.
+// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings.
 #if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \
     !defined(__NVCC__)
 #  define FMT_NORETURN [[noreturn]]
@@ -163,17 +147,6 @@
 #  define FMT_NORETURN
 #endif
 
-#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
-#  define FMT_FALLTHROUGH [[fallthrough]]
-#elif defined(__clang__)
-#  define FMT_FALLTHROUGH [[clang::fallthrough]]
-#elif FMT_GCC_VERSION >= 700 && \
-    (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
-#  define FMT_FALLTHROUGH [[gnu::fallthrough]]
-#else
-#  define FMT_FALLTHROUGH
-#endif
-
 #ifndef FMT_NODISCARD
 #  if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
 #    define FMT_NODISCARD [[nodiscard]]
@@ -182,16 +155,6 @@
 #  endif
 #endif
 
-#ifndef FMT_USE_FLOAT
-#  define FMT_USE_FLOAT 1
-#endif
-#ifndef FMT_USE_DOUBLE
-#  define FMT_USE_DOUBLE 1
-#endif
-#ifndef FMT_USE_LONG_DOUBLE
-#  define FMT_USE_LONG_DOUBLE 1
-#endif
-
 #ifndef FMT_INLINE
 #  if FMT_GCC_VERSION || FMT_CLANG_VERSION
 #    define FMT_INLINE inline __attribute__((always_inline))
@@ -200,9 +163,6 @@
 #  endif
 #endif
 
-// An inline std::forward replacement.
-#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
-
 #ifdef _MSC_VER
 #  define FMT_UNCHECKED_ITERATOR(It) \
     using _Unchecked_type = It  // Mark iterator as checked.
@@ -213,30 +173,26 @@
 #ifndef FMT_BEGIN_NAMESPACE
 #  define FMT_BEGIN_NAMESPACE \
     namespace fmt {           \
-    inline namespace v9 {
+    inline namespace v10 {
 #  define FMT_END_NAMESPACE \
     }                       \
     }
 #endif
 
-#ifndef FMT_MODULE_EXPORT
-#  define FMT_MODULE_EXPORT
-#  define FMT_MODULE_EXPORT_BEGIN
-#  define FMT_MODULE_EXPORT_END
-#  define FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
-#  define FMT_END_DETAIL_NAMESPACE }
+#ifndef FMT_EXPORT
+#  define FMT_EXPORT
+#  define FMT_BEGIN_EXPORT
+#  define FMT_END_EXPORT
 #endif
 
 #if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
-#  define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275)
-#  ifdef FMT_EXPORT
+#  ifdef FMT_LIB_EXPORT
 #    define FMT_API __declspec(dllexport)
 #  elif defined(FMT_SHARED)
 #    define FMT_API __declspec(dllimport)
 #  endif
 #else
-#  define FMT_CLASS_API
-#  if defined(FMT_EXPORT) || defined(FMT_SHARED)
+#  if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
 #    if defined(__GNUC__) || defined(__clang__)
 #      define FMT_API __attribute__((visibility("default")))
 #    endif
@@ -261,11 +217,13 @@
 #endif
 
 #ifndef FMT_CONSTEVAL
-#  if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) &&         \
-       FMT_CPLUSPLUS >= 202002L && !defined(__apple_build_version__)) || \
-      (defined(__cpp_consteval) &&                                       \
+#  if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
+       (!defined(__apple_build_version__) ||                     \
+        __apple_build_version__ >= 14000029L) &&                 \
+       FMT_CPLUSPLUS >= 202002L) ||                              \
+      (defined(__cpp_consteval) &&                               \
        (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
-// consteval is broken in MSVC before VS2022 and Apple clang 13.
+// consteval is broken in MSVC before VS2022 and Apple clang before 14.
 #    define FMT_CONSTEVAL consteval
 #    define FMT_HAS_CONSTEVAL
 #  else
@@ -277,7 +235,7 @@
 #  if defined(__cpp_nontype_template_args) &&                  \
       ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
        __cpp_nontype_template_args >= 201911L) &&              \
-      !defined(__NVCOMPILER)
+      !defined(__NVCOMPILER) && !defined(__LCC__)
 #    define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
 #  else
 #    define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
@@ -286,12 +244,12 @@
 
 // Enable minimal optimizations for more compact code in debug mode.
 FMT_GCC_PRAGMA("GCC push_options")
-#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER)
+#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \
+    !defined(__CUDACC__)
 FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
 #endif
 
 FMT_BEGIN_NAMESPACE
-FMT_MODULE_EXPORT_BEGIN
 
 // Implementations of enable_if_t and other metafunctions for older systems.
 template <bool B, typename T = void>
@@ -310,17 +268,10 @@
 template <typename T>
 using underlying_t = typename std::underlying_type<T>::type;
 
-template <typename...> struct disjunction : std::false_type {};
-template <typename P> struct disjunction<P> : P {};
-template <typename P1, typename... Pn>
-struct disjunction<P1, Pn...>
-    : conditional_t<bool(P1::value), P1, disjunction<Pn...>> {};
-
-template <typename...> struct conjunction : std::true_type {};
-template <typename P> struct conjunction<P> : P {};
-template <typename P1, typename... Pn>
-struct conjunction<P1, Pn...>
-    : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
+// Checks whether T is a container with contiguous storage.
+template <typename T> struct is_contiguous : std::false_type {};
+template <typename Char>
+struct is_contiguous<std::basic_string<Char>> : std::true_type {};
 
 struct monostate {
   constexpr monostate() {}
@@ -332,11 +283,19 @@
 #ifdef FMT_DOC
 #  define FMT_ENABLE_IF(...)
 #else
-#  define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
+#  define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
 #endif
 
-FMT_BEGIN_DETAIL_NAMESPACE
+// This is defined in core.h instead of format.h to avoid injecting in std.
+// It is a template to avoid undesirable implicit conversions to std::byte.
+#ifdef __cpp_lib_byte
+template <typename T, FMT_ENABLE_IF(std::is_same<T, std::byte>::value)>
+inline auto format_as(T b) -> unsigned char {
+  return static_cast<unsigned char>(b);
+}
+#endif
 
+namespace detail {
 // Suppresses "unused variable" warnings with the method described in
 // https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
 // (void)var does not work on many Intel compilers.
@@ -344,7 +303,15 @@
 
 constexpr FMT_INLINE auto is_constant_evaluated(
     bool default_value = false) noexcept -> bool {
-#ifdef __cpp_lib_is_constant_evaluated
+// Workaround for incompatibility between libstdc++ consteval-based
+// std::is_constant_evaluated() implementation and clang-14.
+// https://github.com/fmtlib/fmt/issues/3247
+#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \
+    _GLIBCXX_RELEASE >= 12 &&                                \
+    (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
+  ignore_unused(default_value);
+  return __builtin_is_constant_evaluated();
+#elif defined(__cpp_lib_is_constant_evaluated)
   ignore_unused(default_value);
   return std::is_constant_evaluated();
 #else
@@ -364,12 +331,12 @@
 #  ifdef NDEBUG
 // FMT_ASSERT is not empty to avoid -Wempty-body.
 #    define FMT_ASSERT(condition, message) \
-      ::fmt::detail::ignore_unused((condition), (message))
+      fmt::detail::ignore_unused((condition), (message))
 #  else
 #    define FMT_ASSERT(condition, message)                                    \
       ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
            ? (void)0                                                          \
-           : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
+           : fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
 #  endif
 #endif
 
@@ -410,15 +377,15 @@
   return static_cast<typename std::make_unsigned<Int>::type>(value);
 }
 
-FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5";
+FMT_CONSTEXPR inline auto is_utf8() -> bool {
+  FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7";
 
-constexpr auto is_utf8() -> bool {
   // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
   using uchar = unsigned char;
-  return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 &&
-                         uchar(micro[1]) == 0xB5);
+  return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 &&
+                         uchar(section[1]) == 0xA7);
 }
-FMT_END_DETAIL_NAMESPACE
+}  // namespace detail
 
 /**
   An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
@@ -427,6 +394,7 @@
   compiled with a different ``-std`` option than the client code (which is not
   recommended).
  */
+FMT_EXPORT
 template <typename Char> class basic_string_view {
  private:
   const Char* data_;
@@ -486,6 +454,18 @@
     size_ -= n;
   }
 
+  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(
+      basic_string_view<Char> sv) const noexcept {
+    return size_ >= sv.size_ &&
+           std::char_traits<Char>::compare(data_, sv.data_, sv.size_) == 0;
+  }
+  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept {
+    return size_ >= 1 && std::char_traits<Char>::eq(*data_, c);
+  }
+  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const {
+    return starts_with(basic_string_view<Char>(s));
+  }
+
   // Lexicographically compare this string reference to other.
   FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
     size_t str_size = size_ < other.size_ ? size_ : other.size_;
@@ -517,13 +497,15 @@
   }
 };
 
+FMT_EXPORT
 using string_view = basic_string_view<char>;
 
 /** Specifies if ``T`` is a character type. Can be specialized by users. */
+FMT_EXPORT
 template <typename T> struct is_char : std::false_type {};
 template <> struct is_char<char> : std::true_type {};
 
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
 
 // A base class for compile-time strings.
 struct compile_string {};
@@ -531,7 +513,6 @@
 template <typename S>
 struct is_compile_string : std::is_base_of<compile_string, S> {};
 
-// Returns a string view of `s`.
 template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
 FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
   return s;
@@ -561,10 +542,10 @@
 // Specifies whether S is a string type convertible to fmt::basic_string_view.
 // It should be a constexpr function but MSVC 2017 fails to compile it in
 // enable_if and MSVC 2015 fails to compile it as an alias template.
-// ADL invocation of to_string_view is DEPRECATED!
+// ADL is intentionally disabled as to_string_view is not an extension point.
 template <typename S>
-struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
-};
+struct is_string
+    : std::is_class<decltype(detail::to_string_view(std::declval<S>()))> {};
 
 template <typename S, typename = void> struct char_t_impl {};
 template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
@@ -622,23 +603,44 @@
 constexpr bool is_integral_type(type t) {
   return t > type::none_type && t <= type::last_integer_type;
 }
-
 constexpr bool is_arithmetic_type(type t) {
   return t > type::none_type && t <= type::last_numeric_type;
 }
 
+constexpr auto set(type rhs) -> int { return 1 << static_cast<int>(rhs); }
+constexpr auto in(type t, int set) -> bool {
+  return ((set >> static_cast<int>(t)) & 1) != 0;
+}
+
+// Bitsets of types.
+enum {
+  sint_set =
+      set(type::int_type) | set(type::long_long_type) | set(type::int128_type),
+  uint_set = set(type::uint_type) | set(type::ulong_long_type) |
+             set(type::uint128_type),
+  bool_set = set(type::bool_type),
+  char_set = set(type::char_type),
+  float_set = set(type::float_type) | set(type::double_type) |
+              set(type::long_double_type),
+  string_set = set(type::string_type),
+  cstring_set = set(type::cstring_type),
+  pointer_set = set(type::pointer_type)
+};
+
 FMT_NORETURN FMT_API void throw_format_error(const char* message);
 
 struct error_handler {
   constexpr error_handler() = default;
-  constexpr error_handler(const error_handler&) = default;
 
   // This function is intentionally not constexpr to give a compile-time error.
   FMT_NORETURN void on_error(const char* message) {
     throw_format_error(message);
   }
 };
-FMT_END_DETAIL_NAMESPACE
+}  // namespace detail
+
+/** Throws ``format_error`` with a given message. */
+using detail::throw_format_error;
 
 /** String's character type. */
 template <typename S> using char_t = typename detail::char_t_impl<S>::type;
@@ -650,8 +652,8 @@
   You can use the ``format_parse_context`` type alias for ``char`` instead.
   \endrst
  */
-template <typename Char, typename ErrorHandler = detail::error_handler>
-class basic_format_parse_context : private ErrorHandler {
+FMT_EXPORT
+template <typename Char> class basic_format_parse_context {
  private:
   basic_string_view<Char> format_str_;
   int next_arg_id_;
@@ -660,12 +662,11 @@
 
  public:
   using char_type = Char;
-  using iterator = typename basic_string_view<Char>::iterator;
+  using iterator = const Char*;
 
   explicit constexpr basic_format_parse_context(
-      basic_string_view<Char> format_str, ErrorHandler eh = {},
-      int next_arg_id = 0)
-      : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
+      basic_string_view<Char> format_str, int next_arg_id = 0)
+      : format_str_(format_str), next_arg_id_(next_arg_id) {}
 
   /**
     Returns an iterator to the beginning of the format string range being
@@ -691,7 +692,8 @@
    */
   FMT_CONSTEXPR auto next_arg_id() -> int {
     if (next_arg_id_ < 0) {
-      on_error("cannot switch from manual to automatic argument indexing");
+      detail::throw_format_error(
+          "cannot switch from manual to automatic argument indexing");
       return 0;
     }
     int id = next_arg_id_++;
@@ -705,7 +707,8 @@
    */
   FMT_CONSTEXPR void check_arg_id(int id) {
     if (next_arg_id_ > 0) {
-      on_error("cannot switch from automatic to manual argument indexing");
+      detail::throw_format_error(
+          "cannot switch from automatic to manual argument indexing");
       return;
     }
     next_arg_id_ = -1;
@@ -713,116 +716,49 @@
   }
   FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
   FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
-
-  FMT_CONSTEXPR void on_error(const char* message) {
-    ErrorHandler::on_error(message);
-  }
-
-  constexpr auto error_handler() const -> ErrorHandler { return *this; }
 };
 
+FMT_EXPORT
 using format_parse_context = basic_format_parse_context<char>;
 
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
 // A parse context with extra data used only in compile-time checks.
-template <typename Char, typename ErrorHandler = detail::error_handler>
-class compile_parse_context
-    : public basic_format_parse_context<Char, ErrorHandler> {
+template <typename Char>
+class compile_parse_context : public basic_format_parse_context<Char> {
  private:
   int num_args_;
   const type* types_;
-  using base = basic_format_parse_context<Char, ErrorHandler>;
+  using base = basic_format_parse_context<Char>;
 
  public:
   explicit FMT_CONSTEXPR compile_parse_context(
       basic_string_view<Char> format_str, int num_args, const type* types,
-      ErrorHandler eh = {}, int next_arg_id = 0)
-      : base(format_str, eh, next_arg_id), num_args_(num_args), types_(types) {}
+      int next_arg_id = 0)
+      : base(format_str, next_arg_id), num_args_(num_args), types_(types) {}
 
   constexpr auto num_args() const -> int { return num_args_; }
   constexpr auto arg_type(int id) const -> type { return types_[id]; }
 
   FMT_CONSTEXPR auto next_arg_id() -> int {
     int id = base::next_arg_id();
-    if (id >= num_args_) this->on_error("argument not found");
+    if (id >= num_args_) throw_format_error("argument not found");
     return id;
   }
 
   FMT_CONSTEXPR void check_arg_id(int id) {
     base::check_arg_id(id);
-    if (id >= num_args_) this->on_error("argument not found");
+    if (id >= num_args_) throw_format_error("argument not found");
   }
   using base::check_arg_id;
 
   FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
+    detail::ignore_unused(arg_id);
+#if !defined(__LCC__)
     if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
-      this->on_error("width/precision is not integer");
+      throw_format_error("width/precision is not integer");
+#endif
   }
 };
-FMT_END_DETAIL_NAMESPACE
-
-template <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR void
-basic_format_parse_context<Char, ErrorHandler>::do_check_arg_id(int id) {
-  // Argument id is only checked at compile-time during parsing because
-  // formatting has its own validation.
-  if (detail::is_constant_evaluated() && FMT_GCC_VERSION >= 1200) {
-    using context = detail::compile_parse_context<Char, ErrorHandler>;
-    if (id >= static_cast<context*>(this)->num_args())
-      on_error("argument not found");
-  }
-}
-
-template <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR void
-basic_format_parse_context<Char, ErrorHandler>::check_dynamic_spec(int arg_id) {
-  if (detail::is_constant_evaluated()) {
-    using context = detail::compile_parse_context<Char, ErrorHandler>;
-    static_cast<context*>(this)->check_dynamic_spec(arg_id);
-  }
-}
-
-template <typename Context> class basic_format_arg;
-template <typename Context> class basic_format_args;
-template <typename Context> class dynamic_format_arg_store;
-
-// A formatter for objects of type T.
-template <typename T, typename Char = char, typename Enable = void>
-struct formatter {
-  // A deleted default constructor indicates a disabled formatter.
-  formatter() = delete;
-};
-
-// Specifies if T has an enabled formatter specialization. A type can be
-// formattable even if it doesn't have a formatter e.g. via a conversion.
-template <typename T, typename Context>
-using has_formatter =
-    std::is_constructible<typename Context::template formatter_type<T>>;
-
-// Checks whether T is a container with contiguous storage.
-template <typename T> struct is_contiguous : std::false_type {};
-template <typename Char>
-struct is_contiguous<std::basic_string<Char>> : std::true_type {};
-
-class appender;
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-template <typename Context, typename T>
-constexpr auto has_const_formatter_impl(T*)
-    -> decltype(typename Context::template formatter_type<T>().format(
-                    std::declval<const T&>(), std::declval<Context&>()),
-                true) {
-  return true;
-}
-template <typename Context>
-constexpr auto has_const_formatter_impl(...) -> bool {
-  return false;
-}
-template <typename T, typename Context>
-constexpr auto has_const_formatter() -> bool {
-  return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
-}
 
 // Extracts a reference to the container from back_insert_iterator.
 template <typename Container>
@@ -849,7 +785,7 @@
 FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
   if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out);
   auto size = to_unsigned(end - begin);
-  memcpy(out, begin, size * sizeof(U));
+  if (size > 0) memcpy(out, begin, size * sizeof(U));
   return out + size;
 }
 
@@ -892,11 +828,11 @@
   buffer(const buffer&) = delete;
   void operator=(const buffer&) = delete;
 
-  auto begin() noexcept -> T* { return ptr_; }
-  auto end() noexcept -> T* { return ptr_ + size_; }
+  FMT_INLINE auto begin() noexcept -> T* { return ptr_; }
+  FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; }
 
-  auto begin() const noexcept -> const T* { return ptr_; }
-  auto end() const noexcept -> const T* { return ptr_ + size_; }
+  FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; }
+  FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; }
 
   /** Returns the size of this buffer. */
   constexpr auto size() const noexcept -> size_t { return size_; }
@@ -904,10 +840,8 @@
   /** Returns the capacity of this buffer. */
   constexpr auto capacity() const noexcept -> size_t { return capacity_; }
 
-  /** Returns a pointer to the buffer data. */
+  /** Returns a pointer to the buffer data (not null-terminated). */
   FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
-
-  /** Returns a pointer to the buffer data. */
   FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
 
   /** Clears this buffer. */
@@ -1100,6 +1034,79 @@
 
   auto count() -> size_t { return count_ + this->size(); }
 };
+}  // namespace detail
+
+template <typename Char>
+FMT_CONSTEXPR void basic_format_parse_context<Char>::do_check_arg_id(int id) {
+  // Argument id is only checked at compile-time during parsing because
+  // formatting has its own validation.
+  if (detail::is_constant_evaluated() &&
+      (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
+    using context = detail::compile_parse_context<Char>;
+    if (id >= static_cast<context*>(this)->num_args())
+      detail::throw_format_error("argument not found");
+  }
+}
+
+template <typename Char>
+FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec(
+    int arg_id) {
+  if (detail::is_constant_evaluated() &&
+      (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
+    using context = detail::compile_parse_context<Char>;
+    static_cast<context*>(this)->check_dynamic_spec(arg_id);
+  }
+}
+
+FMT_EXPORT template <typename Context> class basic_format_arg;
+FMT_EXPORT template <typename Context> class basic_format_args;
+FMT_EXPORT template <typename Context> class dynamic_format_arg_store;
+
+// A formatter for objects of type T.
+FMT_EXPORT
+template <typename T, typename Char = char, typename Enable = void>
+struct formatter {
+  // A deleted default constructor indicates a disabled formatter.
+  formatter() = delete;
+};
+
+// Specifies if T has an enabled formatter specialization. A type can be
+// formattable even if it doesn't have a formatter e.g. via a conversion.
+template <typename T, typename Context>
+using has_formatter =
+    std::is_constructible<typename Context::template formatter_type<T>>;
+
+// An output iterator that appends to a buffer.
+// It is used to reduce symbol sizes for the common case.
+class appender : public std::back_insert_iterator<detail::buffer<char>> {
+  using base = std::back_insert_iterator<detail::buffer<char>>;
+
+ public:
+  using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
+  appender(base it) noexcept : base(it) {}
+  FMT_UNCHECKED_ITERATOR(appender);
+
+  auto operator++() noexcept -> appender& { return *this; }
+  auto operator++(int) noexcept -> appender { return *this; }
+};
+
+namespace detail {
+
+template <typename Context, typename T>
+constexpr auto has_const_formatter_impl(T*)
+    -> decltype(typename Context::template formatter_type<T>().format(
+                    std::declval<const T&>(), std::declval<Context&>()),
+                true) {
+  return true;
+}
+template <typename Context>
+constexpr auto has_const_formatter_impl(...) -> bool {
+  return false;
+}
+template <typename T, typename Context>
+constexpr auto has_const_formatter() -> bool {
+  return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
+}
 
 template <typename T>
 using buffer_appender = conditional_t<std::is_same<T, char>::value, appender,
@@ -1110,29 +1117,21 @@
 auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
   return iterator_buffer<OutputIt, T>(out);
 }
+template <typename T, typename Buf,
+          FMT_ENABLE_IF(std::is_base_of<buffer<char>, Buf>::value)>
+auto get_buffer(std::back_insert_iterator<Buf> out) -> buffer<char>& {
+  return get_container(out);
+}
 
-template <typename Buffer>
-auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
+template <typename Buf, typename OutputIt>
+FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
   return buf.out();
 }
-template <typename T> auto get_iterator(buffer<T>& buf) -> buffer_appender<T> {
-  return buffer_appender<T>(buf);
+template <typename T, typename OutputIt>
+auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
+  return out;
 }
 
-template <typename T, typename Char = char, typename Enable = void>
-struct fallback_formatter {
-  fallback_formatter() = delete;
-};
-
-// Specifies if T has an enabled fallback_formatter specialization.
-template <typename T, typename Char>
-using has_fallback_formatter =
-#ifdef FMT_DEPRECATED_OSTREAM
-    std::is_constructible<fallback_formatter<T, Char>>;
-#else
-    std::false_type;
-#endif
-
 struct view {};
 
 template <typename Char, typename T> struct named_arg : view {
@@ -1217,7 +1216,6 @@
 
 struct unformattable {};
 struct unformattable_char : unformattable {};
-struct unformattable_const : unformattable {};
 struct unformattable_pointer : unformattable {};
 
 template <typename Char> struct string_value {
@@ -1284,21 +1282,17 @@
   FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
       : named_args{args, size} {}
 
-  template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
-    using value_type = remove_cvref_t<T>;
-    custom.value = const_cast<value_type*>(&val);
+  template <typename T> FMT_CONSTEXPR20 FMT_INLINE value(T& val) {
+    using value_type = remove_const_t<T>;
+    custom.value = const_cast<value_type*>(std::addressof(val));
     // Get the formatter type through the context to allow different contexts
     // have different extension points, e.g. `formatter<T>` for `format` and
     // `printf_formatter<T>` for `printf`.
     custom.format = format_custom_arg<
-        value_type,
-        conditional_t<has_formatter<value_type, Context>::value,
-                      typename Context::template formatter_type<value_type>,
-                      fallback_formatter<value_type, char_type>>>;
+        value_type, typename Context::template formatter_type<value_type>>;
   }
   value(unformattable);
   value(unformattable_char);
-  value(unformattable_const);
   value(unformattable_pointer);
 
  private:
@@ -1315,29 +1309,25 @@
   }
 };
 
-template <typename Context, typename T>
-FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
-
 // To minimize the number of types we need to deal with, long is translated
 // either to int or to long long depending on its size.
 enum { long_short = sizeof(long) == sizeof(int) };
 using long_type = conditional_t<long_short, int, long long>;
 using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
 
-#ifdef __cpp_lib_byte
-inline auto format_as(std::byte b) -> unsigned char {
-  return static_cast<unsigned char>(b);
-}
-#endif
+template <typename T> struct format_as_result {
+  template <typename U,
+            FMT_ENABLE_IF(std::is_enum<U>::value || std::is_class<U>::value)>
+  static auto map(U*) -> decltype(format_as(std::declval<U>()));
+  static auto map(...) -> void;
 
-template <typename T> struct has_format_as {
-  template <typename U, typename V = decltype(format_as(U())),
-            FMT_ENABLE_IF(std::is_enum<U>::value&& std::is_integral<V>::value)>
-  static auto check(U*) -> std::true_type;
-  static auto check(...) -> std::false_type;
-
-  enum { value = decltype(check(static_cast<T*>(nullptr)))::value };
+  using type = decltype(map(static_cast<T*>(nullptr)));
 };
+template <typename T> using format_as_t = typename format_as_result<T>::type;
+
+template <typename T>
+struct has_format_as
+    : bool_constant<!std::is_same<format_as_t<T>, void>::value> {};
 
 // Maps formatting arguments to core types.
 // arg_mapper reports errors by returning unformattable instead of using
@@ -1414,25 +1404,6 @@
   FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
     return {};
   }
-  template <typename T,
-            FMT_ENABLE_IF(
-                std::is_convertible<T, basic_string_view<char_type>>::value &&
-                !is_string<T>::value && !has_formatter<T, Context>::value &&
-                !has_fallback_formatter<T, char_type>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-      -> basic_string_view<char_type> {
-    return basic_string_view<char_type>(val);
-  }
-  template <typename T,
-            FMT_ENABLE_IF(
-                std::is_convertible<T, std_string_view<char_type>>::value &&
-                !std::is_convertible<T, basic_string_view<char_type>>::value &&
-                !is_string<T>::value && !has_formatter<T, Context>::value &&
-                !has_fallback_formatter<T, char_type>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-      -> basic_string_view<char_type> {
-    return std_string_view<char_type>(val);
-  }
 
   FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
   FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
@@ -1442,16 +1413,15 @@
     return val;
   }
 
-  // We use SFINAE instead of a const T* parameter to avoid conflicting with
-  // the C array overload.
+  // Use SFINAE instead of a const T* parameter to avoid a conflict with the
+  // array overload.
   template <
       typename T,
       FMT_ENABLE_IF(
           std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
           std::is_function<typename std::remove_pointer<T>::type>::value ||
-          (std::is_convertible<const T&, const void*>::value &&
-           !std::is_convertible<const T&, const char_type*>::value &&
-           !has_formatter<T, Context>::value))>
+          (std::is_array<T>::value &&
+           !std::is_convertible<T, const char_type*>::value))>
   FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
     return {};
   }
@@ -1462,62 +1432,40 @@
     return values;
   }
 
-  template <typename T,
-            FMT_ENABLE_IF(
-                std::is_enum<T>::value&& std::is_convertible<T, int>::value &&
-                !has_format_as<T>::value && !has_formatter<T, Context>::value &&
-                !has_fallback_formatter<T, char_type>::value)>
-  FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-      -> decltype(std::declval<arg_mapper>().map(
-          static_cast<underlying_t<T>>(val))) {
-    return map(static_cast<underlying_t<T>>(val));
-  }
-
-  template <typename T, FMT_ENABLE_IF(has_format_as<T>::value &&
-                                      !has_formatter<T, Context>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-      -> decltype(std::declval<arg_mapper>().map(format_as(T()))) {
+  // Only map owning types because mapping views can be unsafe.
+  template <typename T, typename U = format_as_t<T>,
+            FMT_ENABLE_IF(std::is_arithmetic<U>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) {
     return map(format_as(val));
   }
 
-  template <typename T, typename U = remove_cvref_t<T>>
-  struct formattable
-      : bool_constant<has_const_formatter<U, Context>() ||
-                      !std::is_const<remove_reference_t<T>>::value ||
-                      has_fallback_formatter<U, char_type>::value> {};
+  template <typename T, typename U = remove_const_t<T>>
+  struct formattable : bool_constant<has_const_formatter<U, Context>() ||
+                                     (has_formatter<U, Context>::value &&
+                                      !std::is_const<T>::value)> {};
 
-#if (FMT_MSC_VERSION != 0 && FMT_MSC_VERSION < 1910) || \
-    FMT_ICC_VERSION != 0 || defined(__NVCC__)
-  // Workaround a bug in MSVC and Intel (Issue 2746).
-  template <typename T> FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
-    return val;
-  }
-#else
   template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
+  FMT_CONSTEXPR FMT_INLINE auto do_map(T& val) -> T& {
     return val;
   }
   template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const {
+  FMT_CONSTEXPR FMT_INLINE auto do_map(T&) -> unformattable {
     return {};
   }
-#endif
 
-  template <typename T, typename U = remove_cvref_t<T>,
-            FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value &&
-                          !std::is_array<U>::value &&
-                          !std::is_pointer<U>::value &&
-                          !has_format_as<U>::value &&
-                          (has_formatter<U, Context>::value ||
-                           has_fallback_formatter<U, char_type>::value))>
-  FMT_CONSTEXPR FMT_INLINE auto map(T&& val)
-      -> decltype(this->do_map(std::forward<T>(val))) {
-    return do_map(std::forward<T>(val));
+  template <typename T, typename U = remove_const_t<T>,
+            FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value ||
+                           std::is_union<U>::value) &&
+                          !is_string<U>::value && !is_char<U>::value &&
+                          !is_named_arg<U>::value &&
+                          !std::is_arithmetic<format_as_t<U>>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) {
+    return do_map(val);
   }
 
   template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
   FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
-      -> decltype(std::declval<arg_mapper>().map(named_arg.value)) {
+      -> decltype(this->map(named_arg.value)) {
     return map(named_arg.value);
   }
 
@@ -1536,27 +1484,120 @@
 enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
 enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
 
-FMT_END_DETAIL_NAMESPACE
+template <typename Char, typename InputIt>
+auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
+  get_container(out).append(begin, end);
+  return out;
+}
+template <typename Char, typename InputIt>
+auto copy_str(InputIt begin, InputIt end,
+              std::back_insert_iterator<std::string> out)
+    -> std::back_insert_iterator<std::string> {
+  get_container(out).append(begin, end);
+  return out;
+}
 
-// An output iterator that appends to a buffer.
-// It is used to reduce symbol sizes for the common case.
-class appender : public std::back_insert_iterator<detail::buffer<char>> {
-  using base = std::back_insert_iterator<detail::buffer<char>>;
+template <typename Char, typename R, typename OutputIt>
+FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
+  return detail::copy_str<Char>(rng.begin(), rng.end(), out);
+}
 
-  template <typename T>
-  friend auto get_buffer(appender out) -> detail::buffer<char>& {
-    return detail::get_container(out);
-  }
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
+// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
+template <typename...> struct void_t_impl { using type = void; };
+template <typename... T> using void_t = typename void_t_impl<T...>::type;
+#else
+template <typename...> using void_t = void;
+#endif
+
+template <typename It, typename T, typename Enable = void>
+struct is_output_iterator : std::false_type {};
+
+template <typename It, typename T>
+struct is_output_iterator<
+    It, T,
+    void_t<typename std::iterator_traits<It>::iterator_category,
+           decltype(*std::declval<It>() = std::declval<T>())>>
+    : std::true_type {};
+
+template <typename It> struct is_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_back_insert_iterator<std::back_insert_iterator<Container>>
+    : std::true_type {};
+
+// A type-erased reference to an std::locale to avoid a heavy <locale> include.
+class locale_ref {
+ private:
+  const void* locale_;  // A type-erased pointer to std::locale.
 
  public:
-  using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
-  appender(base it) noexcept : base(it) {}
-  FMT_UNCHECKED_ITERATOR(appender);
+  constexpr FMT_INLINE locale_ref() : locale_(nullptr) {}
+  template <typename Locale> explicit locale_ref(const Locale& loc);
 
-  auto operator++() noexcept -> appender& { return *this; }
-  auto operator++(int) noexcept -> appender { return *this; }
+  explicit operator bool() const noexcept { return locale_ != nullptr; }
+
+  template <typename Locale> auto get() const -> Locale;
 };
 
+template <typename> constexpr auto encode_types() -> unsigned long long {
+  return 0;
+}
+
+template <typename Context, typename Arg, typename... Args>
+constexpr auto encode_types() -> unsigned long long {
+  return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
+         (encode_types<Context, Args...>() << packed_arg_bits);
+}
+
+#if defined(__cpp_if_constexpr)
+// This type is intentionally undefined, only used for errors
+template <typename T, typename Char> struct type_is_unformattable_for;
+#endif
+
+template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(PACKED)>
+FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value<Context> {
+  using arg_type = remove_cvref_t<decltype(arg_mapper<Context>().map(val))>;
+
+  constexpr bool formattable_char =
+      !std::is_same<arg_type, unformattable_char>::value;
+  static_assert(formattable_char, "Mixing character types is disallowed.");
+
+  // Formatting of arbitrary pointers is disallowed. If you want to format a
+  // pointer cast it to `void*` or `const void*`. In particular, this forbids
+  // formatting of `[const] volatile char*` printed as bool by iostreams.
+  constexpr bool formattable_pointer =
+      !std::is_same<arg_type, unformattable_pointer>::value;
+  static_assert(formattable_pointer,
+                "Formatting of non-void pointers is disallowed.");
+
+  constexpr bool formattable = !std::is_same<arg_type, unformattable>::value;
+#if defined(__cpp_if_constexpr)
+  if constexpr (!formattable) {
+    type_is_unformattable_for<T, typename Context::char_type> _;
+  }
+#endif
+  static_assert(
+      formattable,
+      "Cannot format an argument. To make type T formattable provide a "
+      "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
+  return {arg_mapper<Context>().map(val)};
+}
+
+template <typename Context, typename T>
+FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg<Context> {
+  auto arg = basic_format_arg<Context>();
+  arg.type_ = mapped_type_constant<T, Context>::value;
+  arg.value_ = make_arg<true, Context>(val);
+  return arg;
+}
+
+template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(!PACKED)>
+FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg<Context> {
+  return make_arg<Context>(val);
+}
+}  // namespace detail
+FMT_BEGIN_EXPORT
+
 // A formatting argument. It is a trivially copyable/constructible type to
 // allow storage in basic_memory_buffer.
 template <typename Context> class basic_format_arg {
@@ -1565,7 +1606,7 @@
   detail::type type_;
 
   template <typename ContextType, typename T>
-  friend FMT_CONSTEXPR auto detail::make_arg(T&& value)
+  friend FMT_CONSTEXPR auto detail::make_arg(T& value)
       -> basic_format_arg<ContextType>;
 
   template <typename Visitor, typename Ctx>
@@ -1619,6 +1660,7 @@
   ``vis(value)`` will be called with the value of type ``double``.
   \endrst
  */
+// DEPRECATED!
 template <typename Visitor, typename Context>
 FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
     Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
@@ -1660,136 +1702,8 @@
   return vis(monostate());
 }
 
-FMT_BEGIN_DETAIL_NAMESPACE
-
-template <typename Char, typename InputIt>
-auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
-  get_container(out).append(begin, end);
-  return out;
-}
-
-template <typename Char, typename R, typename OutputIt>
-FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
-  return detail::copy_str<Char>(rng.begin(), rng.end(), out);
-}
-
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
-// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
-template <typename... Ts> struct void_t_impl { using type = void; };
-template <typename... Ts>
-using void_t = typename detail::void_t_impl<Ts...>::type;
-#else
-template <typename...> using void_t = void;
-#endif
-
-template <typename It, typename T, typename Enable = void>
-struct is_output_iterator : std::false_type {};
-
-template <typename It, typename T>
-struct is_output_iterator<
-    It, T,
-    void_t<typename std::iterator_traits<It>::iterator_category,
-           decltype(*std::declval<It>() = std::declval<T>())>>
-    : std::true_type {};
-
-template <typename OutputIt>
-struct is_back_insert_iterator : std::false_type {};
-template <typename Container>
-struct is_back_insert_iterator<std::back_insert_iterator<Container>>
-    : std::true_type {};
-
-template <typename OutputIt>
-struct is_contiguous_back_insert_iterator : std::false_type {};
-template <typename Container>
-struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
-    : is_contiguous<Container> {};
-template <>
-struct is_contiguous_back_insert_iterator<appender> : std::true_type {};
-
-// A type-erased reference to an std::locale to avoid a heavy <locale> include.
-class locale_ref {
- private:
-  const void* locale_;  // A type-erased pointer to std::locale.
-
- public:
-  constexpr locale_ref() : locale_(nullptr) {}
-  template <typename Locale> explicit locale_ref(const Locale& loc);
-
-  explicit operator bool() const noexcept { return locale_ != nullptr; }
-
-  template <typename Locale> auto get() const -> Locale;
-};
-
-template <typename> constexpr auto encode_types() -> unsigned long long {
-  return 0;
-}
-
-template <typename Context, typename Arg, typename... Args>
-constexpr auto encode_types() -> unsigned long long {
-  return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
-         (encode_types<Context, Args...>() << packed_arg_bits);
-}
-
-template <typename Context, typename T>
-FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
-  const auto& arg = arg_mapper<Context>().map(FMT_FORWARD(val));
-
-  constexpr bool formattable_char =
-      !std::is_same<decltype(arg), const unformattable_char&>::value;
-  static_assert(formattable_char, "Mixing character types is disallowed.");
-
-  constexpr bool formattable_const =
-      !std::is_same<decltype(arg), const unformattable_const&>::value;
-  static_assert(formattable_const, "Cannot format a const argument.");
-
-  // Formatting of arbitrary pointers is disallowed. If you want to output
-  // a pointer cast it to "void *" or "const void *". In particular, this
-  // forbids formatting of "[const] volatile char *" which is printed as bool
-  // by iostreams.
-  constexpr bool formattable_pointer =
-      !std::is_same<decltype(arg), const unformattable_pointer&>::value;
-  static_assert(formattable_pointer,
-                "Formatting of non-void pointers is disallowed.");
-
-  constexpr bool formattable =
-      !std::is_same<decltype(arg), const unformattable&>::value;
-  static_assert(
-      formattable,
-      "Cannot format an argument. To make type T formattable provide a "
-      "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
-  return {arg};
-}
-
-template <typename Context, typename T>
-FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
-  basic_format_arg<Context> arg;
-  arg.type_ = mapped_type_constant<T, Context>::value;
-  arg.value_ = make_value<Context>(value);
-  return arg;
-}
-
-// The type template parameter is there to avoid an ODR violation when using
-// a fallback formatter in one translation unit and an implicit conversion in
-// another (not recommended).
-template <bool IS_PACKED, typename Context, type, typename T,
-          FMT_ENABLE_IF(IS_PACKED)>
-FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
-  return make_value<Context>(val);
-}
-
-template <bool IS_PACKED, typename Context, type, typename T,
-          FMT_ENABLE_IF(!IS_PACKED)>
-FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
-  return make_arg<Context>(value);
-}
-FMT_END_DETAIL_NAMESPACE
-
 // Formatting context.
 template <typename OutputIt, typename Char> class basic_format_context {
- public:
-  /** The character type for the output. */
-  using char_type = Char;
-
  private:
   OutputIt out_;
   basic_format_args<basic_format_context> args_;
@@ -1798,31 +1712,32 @@
  public:
   using iterator = OutputIt;
   using format_arg = basic_format_arg<basic_format_context>;
+  using format_args = basic_format_args<basic_format_context>;
   using parse_context_type = basic_format_parse_context<Char>;
-  template <typename T> using formatter_type = formatter<T, char_type>;
+  template <typename T> using formatter_type = formatter<T, Char>;
+
+  /** The character type for the output. */
+  using char_type = Char;
 
   basic_format_context(basic_format_context&&) = default;
   basic_format_context(const basic_format_context&) = delete;
   void operator=(const basic_format_context&) = delete;
   /**
-   Constructs a ``basic_format_context`` object. References to the arguments are
-   stored in the object so make sure they have appropriate lifetimes.
+    Constructs a ``basic_format_context`` object. References to the arguments
+    are stored in the object so make sure they have appropriate lifetimes.
    */
-  constexpr basic_format_context(
-      OutputIt out, basic_format_args<basic_format_context> ctx_args,
-      detail::locale_ref loc = detail::locale_ref())
+  constexpr basic_format_context(OutputIt out, format_args ctx_args,
+                                 detail::locale_ref loc = {})
       : out_(out), args_(ctx_args), loc_(loc) {}
 
   constexpr auto arg(int id) const -> format_arg { return args_.get(id); }
-  FMT_CONSTEXPR auto arg(basic_string_view<char_type> name) -> format_arg {
+  FMT_CONSTEXPR auto arg(basic_string_view<Char> name) -> format_arg {
     return args_.get(name);
   }
-  FMT_CONSTEXPR auto arg_id(basic_string_view<char_type> name) -> int {
+  FMT_CONSTEXPR auto arg_id(basic_string_view<Char> name) -> int {
     return args_.get_id(name);
   }
-  auto args() const -> const basic_format_args<basic_format_context>& {
-    return args_;
-  }
+  auto args() const -> const format_args& { return args_; }
 
   FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
   void on_error(const char* message) { error_handler().on_error(message); }
@@ -1843,16 +1758,10 @@
     basic_format_context<detail::buffer_appender<Char>, Char>;
 using format_context = buffer_context<char>;
 
-// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
-#define FMT_BUFFER_CONTEXT(Char) \
-  basic_format_context<detail::buffer_appender<Char>, Char>
-
 template <typename T, typename Char = char>
-using is_formattable = bool_constant<
-    !std::is_base_of<detail::unformattable,
-                     decltype(detail::arg_mapper<buffer_context<Char>>().map(
-                         std::declval<T>()))>::value &&
-    !detail::has_fallback_formatter<T, Char>::value>;
+using is_formattable = bool_constant<!std::is_base_of<
+    detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>()
+                                        .map(std::declval<T&>()))>::value>;
 
 /**
   \rst
@@ -1870,7 +1779,7 @@
 {
  private:
   static const size_t num_args = sizeof...(Args);
-  static const size_t num_named_args = detail::count_named_args<Args...>();
+  static constexpr size_t num_named_args = detail::count_named_args<Args...>();
   static const bool is_packed = num_args <= detail::max_packed_args;
 
   using value_type = conditional_t<is_packed, detail::value<Context>,
@@ -1891,16 +1800,14 @@
 
  public:
   template <typename... T>
-  FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args)
+  FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args)
       :
 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
         basic_format_args<Context>(*this),
 #endif
-        data_{detail::make_arg<
-            is_packed, Context,
-            detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
-            FMT_FORWARD(args))...} {
-    detail::init_named_args(data_.named_args(), 0, 0, args...);
+        data_{detail::make_arg<is_packed, Context>(args)...} {
+    if (detail::const_check(num_named_args != 0))
+      detail::init_named_args(data_.named_args(), 0, 0, args...);
   }
 };
 
@@ -1908,14 +1815,15 @@
   \rst
   Constructs a `~fmt::format_arg_store` object that contains references to
   arguments and can be implicitly converted to `~fmt::format_args`. `Context`
-  can be omitted in which case it defaults to `~fmt::context`.
+  can be omitted in which case it defaults to `~fmt::format_context`.
   See `~fmt::arg` for lifetime considerations.
   \endrst
  */
-template <typename Context = format_context, typename... Args>
-constexpr auto make_format_args(Args&&... args)
-    -> format_arg_store<Context, remove_cvref_t<Args>...> {
-  return {FMT_FORWARD(args)...};
+// Arguments are taken by lvalue references to avoid some lifetime issues.
+template <typename Context = format_context, typename... T>
+constexpr auto make_format_args(T&... args)
+    -> format_arg_store<Context, remove_cvref_t<T>...> {
+  return {args...};
 }
 
 /**
@@ -1934,6 +1842,7 @@
   static_assert(!detail::is_named_arg<T>(), "nested named arguments");
   return {name, arg};
 }
+FMT_END_EXPORT
 
 /**
   \rst
@@ -1942,7 +1851,7 @@
   ``vformat``::
 
     void vlog(string_view format_str, format_args args);  // OK
-    format_args args = make_format_args(42);  // Error: dangling reference
+    format_args args = make_format_args();  // Error: dangling reference
   \endrst
  */
 template <typename Context> class basic_format_args {
@@ -2059,7 +1968,7 @@
 /** An alias to ``basic_format_args<format_context>``. */
 // A separate type would result in shorter symbols but break ABI compatibility
 // between clang and gcc on ARM (#1919).
-using format_args = basic_format_args<format_context>;
+FMT_EXPORT using format_args = basic_format_args<format_context>;
 
 // We cannot use enum classes as bit fields because of a gcc bug, so we put them
 // in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
@@ -2080,7 +1989,7 @@
 }
 using sign_t = sign::type;
 
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
 
 // Workaround an array initialization issue in gcc 4.8.
 template <typename Char> struct fill_t {
@@ -2092,7 +2001,7 @@
  public:
   FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
     auto size = s.size();
-    if (size > max_size) return throw_format_error("invalid fill");
+    FMT_ASSERT(size <= max_size, "invalid fill");
     for (size_t i = 0; i < size; ++i) data_[i] = s[i];
     size_ = static_cast<unsigned char>(size);
   }
@@ -2105,11 +2014,10 @@
     return data_[index];
   }
 };
-FMT_END_DETAIL_NAMESPACE
+}  // namespace detail
 
 enum class presentation_type : unsigned char {
   none,
-  // Integer types should go first,
   dec,             // 'd'
   oct,             // 'o'
   hex_lower,       // 'x'
@@ -2131,7 +2039,7 @@
 };
 
 // Format specifiers for built-in and string types.
-template <typename Char> struct basic_format_specs {
+template <typename Char = char> struct format_specs {
   int width;
   int precision;
   presentation_type type;
@@ -2141,7 +2049,7 @@
   bool localized : 1;
   detail::fill_t<Char> fill;
 
-  constexpr basic_format_specs()
+  constexpr format_specs()
       : width(0),
         precision(-1),
         type(presentation_type::none),
@@ -2151,9 +2059,7 @@
         localized(false) {}
 };
 
-using format_specs = basic_format_specs<char>;
-
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
 
 enum class arg_id_kind { none, index, name };
 
@@ -2174,7 +2080,7 @@
 
   arg_id_kind kind;
   union value {
-    FMT_CONSTEXPR value(int id = 0) : index{id} {}
+    FMT_CONSTEXPR value(int idx = 0) : index(idx) {}
     FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
 
     int index;
@@ -2183,134 +2089,30 @@
 };
 
 // Format specifiers with width and precision resolved at formatting rather
-// than parsing time to allow re-using the same parsed specifiers with
+// than parsing time to allow reusing the same parsed specifiers with
 // different sets of arguments (precompilation of format strings).
-template <typename Char>
-struct dynamic_format_specs : basic_format_specs<Char> {
+template <typename Char = char>
+struct dynamic_format_specs : format_specs<Char> {
   arg_ref<Char> width_ref;
   arg_ref<Char> precision_ref;
 };
 
-struct auto_id {};
-
-// A format specifier handler that sets fields in basic_format_specs.
-template <typename Char> class specs_setter {
- protected:
-  basic_format_specs<Char>& specs_;
-
- public:
-  explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& specs)
-      : specs_(specs) {}
-
-  FMT_CONSTEXPR specs_setter(const specs_setter& other)
-      : specs_(other.specs_) {}
-
-  FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; }
-  FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
-    specs_.fill = fill;
-  }
-  FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; }
-  FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
-  FMT_CONSTEXPR void on_localized() { specs_.localized = true; }
-
-  FMT_CONSTEXPR void on_zero() {
-    if (specs_.align == align::none) specs_.align = align::numeric;
-    specs_.fill[0] = Char('0');
-  }
-
-  FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
-  FMT_CONSTEXPR void on_precision(int precision) {
-    specs_.precision = precision;
-  }
-  FMT_CONSTEXPR void end_precision() {}
-
-  FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; }
-};
-
-// Format spec handler that saves references to arguments representing dynamic
-// width and precision to be resolved at formatting time.
-template <typename ParseContext>
-class dynamic_specs_handler
-    : public specs_setter<typename ParseContext::char_type> {
- public:
-  using char_type = typename ParseContext::char_type;
-
-  FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
-                                      ParseContext& ctx)
-      : specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
-
-  FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
-      : specs_setter<char_type>(other),
-        specs_(other.specs_),
-        context_(other.context_) {}
-
-  template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
-    specs_.width_ref = make_arg_ref(arg_id);
-  }
-
-  template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
-    specs_.precision_ref = make_arg_ref(arg_id);
-  }
-
-  FMT_CONSTEXPR void on_error(const char* message) {
-    context_.on_error(message);
-  }
-
- private:
-  dynamic_format_specs<char_type>& specs_;
-  ParseContext& context_;
-
-  using arg_ref_type = arg_ref<char_type>;
-
-  FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type {
-    context_.check_arg_id(arg_id);
-    context_.check_dynamic_spec(arg_id);
-    return arg_ref_type(arg_id);
-  }
-
-  FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type {
-    int arg_id = context_.next_arg_id();
-    context_.check_dynamic_spec(arg_id);
-    return arg_ref_type(arg_id);
-  }
-
-  FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id)
-      -> arg_ref_type {
-    context_.check_arg_id(arg_id);
-    basic_string_view<char_type> format_str(
-        context_.begin(), to_unsigned(context_.end() - context_.begin()));
-    return arg_ref_type(arg_id);
-  }
-};
-
-template <typename Char> constexpr bool is_ascii_letter(Char c) {
-  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
-}
-
-// Converts a character to ASCII. Returns a number > 127 on conversion failure.
+// Converts a character to ASCII. Returns '\0' on conversion failure.
 template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
-constexpr auto to_ascii(Char c) -> Char {
-  return c;
+constexpr auto to_ascii(Char c) -> char {
+  return c <= 0xff ? static_cast<char>(c) : '\0';
 }
 template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
-constexpr auto to_ascii(Char c) -> underlying_t<Char> {
-  return c;
+constexpr auto to_ascii(Char c) -> char {
+  return c <= 0xff ? static_cast<char>(c) : '\0';
 }
 
-FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int {
-  return "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"
-      [static_cast<unsigned char>(c) >> 3];
-}
-
+// Returns the number of code units in a code point or 1 on error.
 template <typename Char>
 FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
   if (const_check(sizeof(Char) != 1)) return 1;
-  int len = code_point_length_impl(static_cast<char>(*begin));
-
-  // Compute the pointer to the next character early so that the next
-  // iteration can start working on the next character. Neither Clang
-  // nor GCC figure out this reordering on their own.
-  return len + !len;
+  auto c = static_cast<unsigned char>(*begin);
+  return static_cast<int>((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1;
 }
 
 // Return the result via the out param to workaround gcc bug 77539.
@@ -2355,279 +2157,284 @@
              : error_value;
 }
 
-// Parses fill and alignment.
-template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
-                               Handler&& handler) -> const Char* {
-  FMT_ASSERT(begin != end, "");
-  auto align = align::none;
-  auto p = begin + code_point_length(begin);
-  if (end - p <= 0) p = begin;
-  for (;;) {
-    switch (to_ascii(*p)) {
-    case '<':
-      align = align::left;
-      break;
-    case '>':
-      align = align::right;
-      break;
-    case '^':
-      align = align::center;
-      break;
-    default:
-      break;
-    }
-    if (align != align::none) {
-      if (p != begin) {
-        auto c = *begin;
-        if (c == '{')
-          return handler.on_error("invalid fill character '{'"), begin;
-        handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
-        begin = p + 1;
-      } else
-        ++begin;
-      handler.on_align(align);
-      break;
-    } else if (p == begin) {
-      break;
-    }
-    p = begin;
+FMT_CONSTEXPR inline auto parse_align(char c) -> align_t {
+  switch (c) {
+  case '<':
+    return align::left;
+  case '>':
+    return align::right;
+  case '^':
+    return align::center;
   }
-  return begin;
+  return align::none;
 }
 
-template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
-  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
+template <typename Char> constexpr auto is_name_start(Char c) -> bool {
+  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
 }
 
-template <typename Char, typename IDHandler>
+template <typename Char, typename Handler>
 FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
-                                   IDHandler&& handler) -> const Char* {
-  FMT_ASSERT(begin != end, "");
+                                   Handler&& handler) -> const Char* {
   Char c = *begin;
   if (c >= '0' && c <= '9') {
     int index = 0;
+    constexpr int max = (std::numeric_limits<int>::max)();
     if (c != '0')
-      index =
-          parse_nonnegative_int(begin, end, (std::numeric_limits<int>::max)());
+      index = parse_nonnegative_int(begin, end, max);
     else
       ++begin;
     if (begin == end || (*begin != '}' && *begin != ':'))
-      handler.on_error("invalid format string");
+      throw_format_error("invalid format string");
     else
-      handler(index);
+      handler.on_index(index);
     return begin;
   }
   if (!is_name_start(c)) {
-    handler.on_error("invalid format string");
+    throw_format_error("invalid format string");
     return begin;
   }
   auto it = begin;
   do {
     ++it;
-  } while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9')));
-  handler(basic_string_view<Char>(begin, to_unsigned(it - begin)));
+  } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9')));
+  handler.on_name({begin, to_unsigned(it - begin)});
   return it;
 }
 
-template <typename Char, typename IDHandler>
+template <typename Char, typename Handler>
 FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
-                                           IDHandler&& handler) -> const Char* {
+                                           Handler&& handler) -> const Char* {
+  FMT_ASSERT(begin != end, "");
   Char c = *begin;
   if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
-  handler();
+  handler.on_auto();
   return begin;
 }
 
-template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end,
-                               Handler&& handler) -> const Char* {
-  using detail::auto_id;
-  struct width_adapter {
-    Handler& handler;
+template <typename Char> struct dynamic_spec_id_handler {
+  basic_format_parse_context<Char>& ctx;
+  arg_ref<Char>& ref;
 
-    FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
-    FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
-    FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
-      handler.on_dynamic_width(id);
-    }
-    FMT_CONSTEXPR void on_error(const char* message) {
-      if (message) handler.on_error(message);
-    }
-  };
+  FMT_CONSTEXPR void on_auto() {
+    int id = ctx.next_arg_id();
+    ref = arg_ref<Char>(id);
+    ctx.check_dynamic_spec(id);
+  }
+  FMT_CONSTEXPR void on_index(int id) {
+    ref = arg_ref<Char>(id);
+    ctx.check_arg_id(id);
+    ctx.check_dynamic_spec(id);
+  }
+  FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
+    ref = arg_ref<Char>(id);
+    ctx.check_arg_id(id);
+  }
+};
 
+// Parses [integer | "{" [arg_id] "}"].
+template <typename Char>
+FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
+                                      int& value, arg_ref<Char>& ref,
+                                      basic_format_parse_context<Char>& ctx)
+    -> const Char* {
   FMT_ASSERT(begin != end, "");
   if ('0' <= *begin && *begin <= '9') {
-    int width = parse_nonnegative_int(begin, end, -1);
-    if (width != -1)
-      handler.on_width(width);
+    int val = parse_nonnegative_int(begin, end, -1);
+    if (val != -1)
+      value = val;
     else
-      handler.on_error("number is too big");
+      throw_format_error("number is too big");
   } else if (*begin == '{') {
     ++begin;
-    if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler});
-    if (begin == end || *begin != '}')
-      return handler.on_error("invalid format string"), begin;
-    ++begin;
+    auto handler = dynamic_spec_id_handler<Char>{ctx, ref};
+    if (begin != end) begin = parse_arg_id(begin, end, handler);
+    if (begin != end && *begin == '}') return ++begin;
+    throw_format_error("invalid format string");
   }
   return begin;
 }
 
-template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
-                                   Handler&& handler) -> const Char* {
-  using detail::auto_id;
-  struct precision_adapter {
-    Handler& handler;
-
-    FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
-    FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
-    FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
-      handler.on_dynamic_precision(id);
-    }
-    FMT_CONSTEXPR void on_error(const char* message) {
-      if (message) handler.on_error(message);
-    }
-  };
-
-  ++begin;
-  auto c = begin != end ? *begin : Char();
-  if ('0' <= c && c <= '9') {
-    auto precision = parse_nonnegative_int(begin, end, -1);
-    if (precision != -1)
-      handler.on_precision(precision);
-    else
-      handler.on_error("number is too big");
-  } else if (c == '{') {
-    ++begin;
-    if (begin != end)
-      begin = parse_arg_id(begin, end, precision_adapter{handler});
-    if (begin == end || *begin++ != '}')
-      return handler.on_error("invalid format string"), begin;
-  } else {
-    return handler.on_error("missing precision specifier"), begin;
-  }
-  handler.end_precision();
-  return begin;
-}
-
 template <typename Char>
-FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type {
-  switch (to_ascii(type)) {
-  case 'd':
-    return presentation_type::dec;
-  case 'o':
-    return presentation_type::oct;
-  case 'x':
-    return presentation_type::hex_lower;
-  case 'X':
-    return presentation_type::hex_upper;
-  case 'b':
-    return presentation_type::bin_lower;
-  case 'B':
-    return presentation_type::bin_upper;
-  case 'a':
-    return presentation_type::hexfloat_lower;
-  case 'A':
-    return presentation_type::hexfloat_upper;
-  case 'e':
-    return presentation_type::exp_lower;
-  case 'E':
-    return presentation_type::exp_upper;
-  case 'f':
-    return presentation_type::fixed_lower;
-  case 'F':
-    return presentation_type::fixed_upper;
-  case 'g':
-    return presentation_type::general_lower;
-  case 'G':
-    return presentation_type::general_upper;
-  case 'c':
-    return presentation_type::chr;
-  case 's':
-    return presentation_type::string;
-  case 'p':
-    return presentation_type::pointer;
-  case '?':
-    return presentation_type::debug;
-  default:
-    return presentation_type::none;
-  }
-}
-
-// Parses standard format specifiers and sends notifications about parsed
-// components to handler.
-template <typename Char, typename SpecHandler>
-FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin,
-                                                 const Char* end,
-                                                 SpecHandler&& handler)
+FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
+                                   int& value, arg_ref<Char>& ref,
+                                   basic_format_parse_context<Char>& ctx)
     -> const Char* {
-  if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) &&
-      *begin != 'L') {
-    presentation_type type = parse_presentation_type(*begin++);
-    if (type == presentation_type::none)
-      handler.on_error("invalid type specifier");
-    handler.on_type(type);
+  ++begin;
+  if (begin == end || *begin == '}') {
+    throw_format_error("invalid precision");
     return begin;
   }
+  return parse_dynamic_spec(begin, end, value, ref, ctx);
+}
 
-  if (begin == end) return begin;
+enum class state { start, align, sign, hash, zero, width, precision, locale };
 
-  begin = parse_align(begin, end, handler);
-  if (begin == end) return begin;
-
-  // Parse sign.
-  switch (to_ascii(*begin)) {
-  case '+':
-    handler.on_sign(sign::plus);
-    ++begin;
-    break;
-  case '-':
-    handler.on_sign(sign::minus);
-    ++begin;
-    break;
-  case ' ':
-    handler.on_sign(sign::space);
-    ++begin;
-    break;
-  default:
-    break;
-  }
-  if (begin == end) return begin;
-
-  if (*begin == '#') {
-    handler.on_hash();
-    if (++begin == end) return begin;
-  }
-
-  // Parse zero flag.
-  if (*begin == '0') {
-    handler.on_zero();
-    if (++begin == end) return begin;
-  }
-
-  begin = parse_width(begin, end, handler);
-  if (begin == end) return begin;
-
-  // Parse precision.
-  if (*begin == '.') {
-    begin = parse_precision(begin, end, handler);
+// Parses standard format specifiers.
+template <typename Char>
+FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
+    const Char* begin, const Char* end, dynamic_format_specs<Char>& specs,
+    basic_format_parse_context<Char>& ctx, type arg_type) -> const Char* {
+  auto c = '\0';
+  if (end - begin > 1) {
+    auto next = to_ascii(begin[1]);
+    c = parse_align(next) == align::none ? to_ascii(*begin) : '\0';
+  } else {
     if (begin == end) return begin;
+    c = to_ascii(*begin);
   }
 
-  if (*begin == 'L') {
-    handler.on_localized();
-    ++begin;
-  }
+  struct {
+    state current_state = state::start;
+    FMT_CONSTEXPR void operator()(state s, bool valid = true) {
+      if (current_state >= s || !valid)
+        throw_format_error("invalid format specifier");
+      current_state = s;
+    }
+  } enter_state;
 
-  // Parse type.
-  if (begin != end && *begin != '}') {
-    presentation_type type = parse_presentation_type(*begin++);
-    if (type == presentation_type::none)
-      handler.on_error("invalid type specifier");
-    handler.on_type(type);
+  using pres = presentation_type;
+  constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
+  struct {
+    const Char*& begin;
+    dynamic_format_specs<Char>& specs;
+    type arg_type;
+
+    FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* {
+      if (!in(arg_type, set)) throw_format_error("invalid format specifier");
+      specs.type = type;
+      return begin + 1;
+    }
+  } parse_presentation_type{begin, specs, arg_type};
+
+  for (;;) {
+    switch (c) {
+    case '<':
+    case '>':
+    case '^':
+      enter_state(state::align);
+      specs.align = parse_align(c);
+      ++begin;
+      break;
+    case '+':
+    case '-':
+    case ' ':
+      enter_state(state::sign, in(arg_type, sint_set | float_set));
+      switch (c) {
+      case '+':
+        specs.sign = sign::plus;
+        break;
+      case '-':
+        specs.sign = sign::minus;
+        break;
+      case ' ':
+        specs.sign = sign::space;
+        break;
+      }
+      ++begin;
+      break;
+    case '#':
+      enter_state(state::hash, is_arithmetic_type(arg_type));
+      specs.alt = true;
+      ++begin;
+      break;
+    case '0':
+      enter_state(state::zero);
+      if (!is_arithmetic_type(arg_type))
+        throw_format_error("format specifier requires numeric argument");
+      if (specs.align == align::none) {
+        // Ignore 0 if align is specified for compatibility with std::format.
+        specs.align = align::numeric;
+        specs.fill[0] = Char('0');
+      }
+      ++begin;
+      break;
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+    case '{':
+      enter_state(state::width);
+      begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
+      break;
+    case '.':
+      enter_state(state::precision,
+                  in(arg_type, float_set | string_set | cstring_set));
+      begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
+                              ctx);
+      break;
+    case 'L':
+      enter_state(state::locale, is_arithmetic_type(arg_type));
+      specs.localized = true;
+      ++begin;
+      break;
+    case 'd':
+      return parse_presentation_type(pres::dec, integral_set);
+    case 'o':
+      return parse_presentation_type(pres::oct, integral_set);
+    case 'x':
+      return parse_presentation_type(pres::hex_lower, integral_set);
+    case 'X':
+      return parse_presentation_type(pres::hex_upper, integral_set);
+    case 'b':
+      return parse_presentation_type(pres::bin_lower, integral_set);
+    case 'B':
+      return parse_presentation_type(pres::bin_upper, integral_set);
+    case 'a':
+      return parse_presentation_type(pres::hexfloat_lower, float_set);
+    case 'A':
+      return parse_presentation_type(pres::hexfloat_upper, float_set);
+    case 'e':
+      return parse_presentation_type(pres::exp_lower, float_set);
+    case 'E':
+      return parse_presentation_type(pres::exp_upper, float_set);
+    case 'f':
+      return parse_presentation_type(pres::fixed_lower, float_set);
+    case 'F':
+      return parse_presentation_type(pres::fixed_upper, float_set);
+    case 'g':
+      return parse_presentation_type(pres::general_lower, float_set);
+    case 'G':
+      return parse_presentation_type(pres::general_upper, float_set);
+    case 'c':
+      return parse_presentation_type(pres::chr, integral_set);
+    case 's':
+      return parse_presentation_type(pres::string,
+                                     bool_set | string_set | cstring_set);
+    case 'p':
+      return parse_presentation_type(pres::pointer, pointer_set | cstring_set);
+    case '?':
+      return parse_presentation_type(pres::debug,
+                                     char_set | string_set | cstring_set);
+    case '}':
+      return begin;
+    default: {
+      if (*begin == '}') return begin;
+      // Parse fill and alignment.
+      auto fill_end = begin + code_point_length(begin);
+      if (end - fill_end <= 0) {
+        throw_format_error("invalid format specifier");
+        return begin;
+      }
+      if (*begin == '{') {
+        throw_format_error("invalid fill character '{'");
+        return begin;
+      }
+      auto align = parse_align(to_ascii(*fill_end));
+      enter_state(state::align, align != align::none);
+      specs.fill = {begin, to_unsigned(fill_end - begin)};
+      specs.align = align;
+      begin = fill_end + 1;
+    }
+    }
+    if (begin == end) return begin;
+    c = to_ascii(*begin);
   }
-  return begin;
 }
 
 template <typename Char, typename Handler>
@@ -2637,14 +2444,11 @@
     Handler& handler;
     int arg_id;
 
-    FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
-    FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
-    FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
+    FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); }
+    FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); }
+    FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
       arg_id = handler.on_arg_id(id);
     }
-    FMT_CONSTEXPR void on_error(const char* message) {
-      if (message) handler.on_error(message);
-    }
   };
 
   ++begin;
@@ -2673,9 +2477,6 @@
 template <bool IS_CONSTEXPR, typename Char, typename Handler>
 FMT_CONSTEXPR FMT_INLINE void parse_format_string(
     basic_string_view<Char> format_str, Handler&& handler) {
-  // Workaround a name-lookup bug in MSVC's modules implementation.
-  using detail::find;
-
   auto begin = format_str.data();
   auto end = begin + format_str.size();
   if (end - begin < 32) {
@@ -2735,189 +2536,46 @@
     -> decltype(ctx.begin()) {
   using char_type = typename ParseContext::char_type;
   using context = buffer_context<char_type>;
-  using stripped_type = typename strip_named_arg<T>::type;
   using mapped_type = conditional_t<
       mapped_type_constant<T, context>::value != type::custom_type,
       decltype(arg_mapper<context>().map(std::declval<const T&>())),
-      stripped_type>;
-  auto f = conditional_t<has_formatter<mapped_type, context>::value,
-                         formatter<mapped_type, char_type>,
-                         fallback_formatter<stripped_type, char_type>>();
-  return f.parse(ctx);
+      typename strip_named_arg<T>::type>;
+#if defined(__cpp_if_constexpr)
+  if constexpr (std::is_default_constructible_v<
+                    formatter<mapped_type, char_type>>) {
+    return formatter<mapped_type, char_type>().parse(ctx);
+  } else {
+    type_is_unformattable_for<T, char_type> _;
+    return ctx.begin();
+  }
+#else
+  return formatter<mapped_type, char_type>().parse(ctx);
+#endif
 }
 
-template <typename ErrorHandler>
-FMT_CONSTEXPR void check_int_type_spec(presentation_type type,
-                                       ErrorHandler&& eh) {
-  if (type > presentation_type::bin_upper && type != presentation_type::chr)
-    eh.on_error("invalid type specifier");
-}
-
-// Checks char specs and returns true if the type spec is char (and not int).
-template <typename Char, typename ErrorHandler = error_handler>
-FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& specs,
-                                    ErrorHandler&& eh = {}) -> bool {
+// Checks char specs and returns true iff the presentation type is char-like.
+template <typename Char>
+FMT_CONSTEXPR auto check_char_specs(const format_specs<Char>& specs) -> bool {
   if (specs.type != presentation_type::none &&
       specs.type != presentation_type::chr &&
       specs.type != presentation_type::debug) {
-    check_int_type_spec(specs.type, eh);
     return false;
   }
   if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
-    eh.on_error("invalid format specifier for char");
+    throw_format_error("invalid format specifier for char");
   return true;
 }
 
-// A floating-point presentation format.
-enum class float_format : unsigned char {
-  general,  // General: exponent notation or fixed point based on magnitude.
-  exp,      // Exponent notation with the default precision of 6, e.g. 1.2e-3.
-  fixed,    // Fixed point with the default precision of 6, e.g. 0.0012.
-  hex
-};
-
-struct float_specs {
-  int precision;
-  float_format format : 8;
-  sign_t sign : 8;
-  bool upper : 1;
-  bool locale : 1;
-  bool binary32 : 1;
-  bool showpoint : 1;
-};
-
-template <typename ErrorHandler = error_handler, typename Char>
-FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs<Char>& specs,
-                                         ErrorHandler&& eh = {})
-    -> float_specs {
-  auto result = float_specs();
-  result.showpoint = specs.alt;
-  result.locale = specs.localized;
-  switch (specs.type) {
-  case presentation_type::none:
-    result.format = float_format::general;
-    break;
-  case presentation_type::general_upper:
-    result.upper = true;
-    FMT_FALLTHROUGH;
-  case presentation_type::general_lower:
-    result.format = float_format::general;
-    break;
-  case presentation_type::exp_upper:
-    result.upper = true;
-    FMT_FALLTHROUGH;
-  case presentation_type::exp_lower:
-    result.format = float_format::exp;
-    result.showpoint |= specs.precision != 0;
-    break;
-  case presentation_type::fixed_upper:
-    result.upper = true;
-    FMT_FALLTHROUGH;
-  case presentation_type::fixed_lower:
-    result.format = float_format::fixed;
-    result.showpoint |= specs.precision != 0;
-    break;
-  case presentation_type::hexfloat_upper:
-    result.upper = true;
-    FMT_FALLTHROUGH;
-  case presentation_type::hexfloat_lower:
-    result.format = float_format::hex;
-    break;
-  default:
-    eh.on_error("invalid type specifier");
-    break;
-  }
-  return result;
-}
-
-template <typename ErrorHandler = error_handler>
-FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type,
-                                           ErrorHandler&& eh = {}) -> bool {
-  if (type == presentation_type::none || type == presentation_type::string ||
-      type == presentation_type::debug)
-    return true;
-  if (type != presentation_type::pointer) eh.on_error("invalid type specifier");
-  return false;
-}
-
-template <typename ErrorHandler = error_handler>
-FMT_CONSTEXPR void check_string_type_spec(presentation_type type,
-                                          ErrorHandler&& eh = {}) {
-  if (type != presentation_type::none && type != presentation_type::string &&
-      type != presentation_type::debug)
-    eh.on_error("invalid type specifier");
-}
-
-template <typename ErrorHandler>
-FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type,
-                                           ErrorHandler&& eh) {
-  if (type != presentation_type::none && type != presentation_type::pointer)
-    eh.on_error("invalid type specifier");
-}
-
-// A parse_format_specs handler that checks if specifiers are consistent with
-// the argument type.
-template <typename Handler> class specs_checker : public Handler {
- private:
-  detail::type arg_type_;
-
-  FMT_CONSTEXPR void require_numeric_argument() {
-    if (!is_arithmetic_type(arg_type_))
-      this->on_error("format specifier requires numeric argument");
-  }
-
- public:
-  FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type)
-      : Handler(handler), arg_type_(arg_type) {}
-
-  FMT_CONSTEXPR void on_align(align_t align) {
-    if (align == align::numeric) require_numeric_argument();
-    Handler::on_align(align);
-  }
-
-  FMT_CONSTEXPR void on_sign(sign_t s) {
-    require_numeric_argument();
-    if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
-        arg_type_ != type::long_long_type && arg_type_ != type::int128_type &&
-        arg_type_ != type::char_type) {
-      this->on_error("format specifier requires signed argument");
-    }
-    Handler::on_sign(s);
-  }
-
-  FMT_CONSTEXPR void on_hash() {
-    require_numeric_argument();
-    Handler::on_hash();
-  }
-
-  FMT_CONSTEXPR void on_localized() {
-    require_numeric_argument();
-    Handler::on_localized();
-  }
-
-  FMT_CONSTEXPR void on_zero() {
-    require_numeric_argument();
-    Handler::on_zero();
-  }
-
-  FMT_CONSTEXPR void end_precision() {
-    if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
-      this->on_error("precision not allowed for this argument type");
-  }
-};
-
-constexpr int invalid_arg_index = -1;
-
 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
 template <int N, typename T, typename... Args, typename Char>
 constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
-  if constexpr (detail::is_statically_named_arg<T>()) {
+  if constexpr (is_statically_named_arg<T>()) {
     if (name == T::name) return N;
   }
   if constexpr (sizeof...(Args) > 0)
     return get_arg_index_by_name<N + 1, Args...>(name);
   (void)name;  // Workaround an MSVC bug about "unused" parameter.
-  return invalid_arg_index;
+  return -1;
 }
 #endif
 
@@ -2928,34 +2586,29 @@
     return get_arg_index_by_name<0, Args...>(name);
 #endif
   (void)name;
-  return invalid_arg_index;
+  return -1;
 }
 
-template <typename Char, typename ErrorHandler, typename... Args>
-class format_string_checker {
+template <typename Char, typename... Args> class format_string_checker {
  private:
-  // In the future basic_format_parse_context will replace compile_parse_context
-  // here and will use is_constant_evaluated and downcasting to access the data
-  // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
-  using parse_context_type = compile_parse_context<Char, ErrorHandler>;
+  using parse_context_type = compile_parse_context<Char>;
   static constexpr int num_args = sizeof...(Args);
 
   // Format specifier parsing function.
+  // In the future basic_format_parse_context will replace compile_parse_context
+  // here and will use is_constant_evaluated and downcasting to access the data
+  // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
   using parse_func = const Char* (*)(parse_context_type&);
 
+  type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
   parse_context_type context_;
   parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
-  type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
 
  public:
-  explicit FMT_CONSTEXPR format_string_checker(
-      basic_string_view<Char> format_str, ErrorHandler eh)
-      : context_(format_str, num_args, types_, eh),
-        parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
-        types_{  // NOLINT(clang-analyzer-optin.cplusplus.UninitializedObject)
-            mapped_type_constant<Args,
-                                 basic_format_context<Char*, Char>>::value...} {
-  }
+  explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt)
+      : types_{mapped_type_constant<Args, buffer_context<Char>>::value...},
+        context_(fmt, num_args, types_),
+        parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
 
   FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
 
@@ -2966,8 +2619,8 @@
   FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
     auto index = get_arg_index_by_name<Args...>(id);
-    if (index == invalid_arg_index) on_error("named argument is not found");
-    return context_.check_arg_id(index), index;
+    if (index < 0) on_error("named argument is not found");
+    return index;
 #else
     (void)id;
     on_error("compile-time checks for named arguments require C++20 support");
@@ -2975,17 +2628,19 @@
 #endif
   }
 
-  FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
+  FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) {
+    on_format_specs(id, begin, begin);  // Call parse() on empty specs.
+  }
 
   FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
       -> const Char* {
-    context_.advance_to(context_.begin() + (begin - &*context_.begin()));
+    context_.advance_to(begin);
     // id >= 0 check is a workaround for gcc 10 bug (#2065).
     return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
   }
 
   FMT_CONSTEXPR void on_error(const char* message) {
-    context_.on_error(message);
+    throw_format_error(message);
   }
 };
 
@@ -3001,28 +2656,33 @@
 template <typename... Args, typename S,
           FMT_ENABLE_IF(is_compile_string<S>::value)>
 void check_format_string(S format_str) {
-  FMT_CONSTEXPR auto s = basic_string_view<typename S::char_type>(format_str);
-  using checker = format_string_checker<typename S::char_type, error_handler,
-                                        remove_cvref_t<Args>...>;
-  FMT_CONSTEXPR bool invalid_format =
-      (parse_format_string<true>(s, checker(s, {})), true);
-  ignore_unused(invalid_format);
+  using char_t = typename S::char_type;
+  FMT_CONSTEXPR auto s = basic_string_view<char_t>(format_str);
+  using checker = format_string_checker<char_t, remove_cvref_t<Args>...>;
+  FMT_CONSTEXPR bool error = (parse_format_string<true>(s, checker(s)), true);
+  ignore_unused(error);
 }
 
+template <typename Char = char> struct vformat_args {
+  using type = basic_format_args<
+      basic_format_context<std::back_insert_iterator<buffer<Char>>, Char>>;
+};
+template <> struct vformat_args<char> { using type = format_args; };
+
+// Use vformat_args and avoid type_identity to keep symbols short.
 template <typename Char>
-void vformat_to(
-    buffer<Char>& buf, basic_string_view<Char> fmt,
-    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
-    locale_ref loc = {});
+void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
+                typename vformat_args<Char>::type args, locale_ref loc = {});
 
 FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
 #ifndef _WIN32
 inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
 #endif
-FMT_END_DETAIL_NAMESPACE
+}  // namespace detail
 
-// A formatter specialization for the core types corresponding to detail::type
-// constants.
+FMT_BEGIN_EXPORT
+
+// A formatter specialization for natively supported types.
 template <typename T, typename Char>
 struct formatter<T, Char,
                  enable_if_t<detail::type_constant<T, Char>::value !=
@@ -3031,81 +2691,21 @@
   detail::dynamic_format_specs<Char> specs_;
 
  public:
-  // Parses format specifiers stopping either at the end of the range or at the
-  // terminating '}'.
   template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    auto begin = ctx.begin(), end = ctx.end();
-    if (begin == end) return begin;
-    using handler_type = detail::dynamic_specs_handler<ParseContext>;
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
     auto type = detail::type_constant<T, Char>::value;
-    auto checker =
-        detail::specs_checker<handler_type>(handler_type(specs_, ctx), type);
-    auto it = detail::parse_format_specs(begin, end, checker);
-    auto eh = ctx.error_handler();
-    switch (type) {
-    case detail::type::none_type:
-      FMT_ASSERT(false, "invalid argument type");
-      break;
-    case detail::type::bool_type:
-      if (specs_.type == presentation_type::none ||
-          specs_.type == presentation_type::string) {
-        break;
-      }
-      FMT_FALLTHROUGH;
-    case detail::type::int_type:
-    case detail::type::uint_type:
-    case detail::type::long_long_type:
-    case detail::type::ulong_long_type:
-    case detail::type::int128_type:
-    case detail::type::uint128_type:
-      detail::check_int_type_spec(specs_.type, eh);
-      break;
-    case detail::type::char_type:
-      detail::check_char_specs(specs_, eh);
-      break;
-    case detail::type::float_type:
-      if (detail::const_check(FMT_USE_FLOAT))
-        detail::parse_float_type_spec(specs_, eh);
-      else
-        FMT_ASSERT(false, "float support disabled");
-      break;
-    case detail::type::double_type:
-      if (detail::const_check(FMT_USE_DOUBLE))
-        detail::parse_float_type_spec(specs_, eh);
-      else
-        FMT_ASSERT(false, "double support disabled");
-      break;
-    case detail::type::long_double_type:
-      if (detail::const_check(FMT_USE_LONG_DOUBLE))
-        detail::parse_float_type_spec(specs_, eh);
-      else
-        FMT_ASSERT(false, "long double support disabled");
-      break;
-    case detail::type::cstring_type:
-      detail::check_cstring_type_spec(specs_.type, eh);
-      break;
-    case detail::type::string_type:
-      detail::check_string_type_spec(specs_.type, eh);
-      break;
-    case detail::type::pointer_type:
-      detail::check_pointer_type_spec(specs_.type, eh);
-      break;
-    case detail::type::custom_type:
-      // Custom format specifiers are checked in parse functions of
-      // formatter specializations.
-      break;
-    }
-    return it;
+    auto end =
+        detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type);
+    if (type == detail::type::char_type) detail::check_char_specs(specs_);
+    return end;
   }
 
   template <detail::type U = detail::type_constant<T, Char>::value,
-            enable_if_t<(U == detail::type::string_type ||
-                         U == detail::type::cstring_type ||
-                         U == detail::type::char_type),
-                        int> = 0>
-  FMT_CONSTEXPR void set_debug_format() {
-    specs_.type = presentation_type::debug;
+            FMT_ENABLE_IF(U == detail::type::string_type ||
+                          U == detail::type::cstring_type ||
+                          U == detail::type::char_type)>
+  FMT_CONSTEXPR void set_debug_format(bool set = true) {
+    specs_.type = set ? presentation_type::debug : presentation_type::none;
   }
 
   template <typename FormatContext>
@@ -3113,28 +2713,9 @@
       -> decltype(ctx.out());
 };
 
-#define FMT_FORMAT_AS(Type, Base)                                        \
-  template <typename Char>                                               \
-  struct formatter<Type, Char> : formatter<Base, Char> {                 \
-    template <typename FormatContext>                                    \
-    auto format(Type const& val, FormatContext& ctx) const               \
-        -> decltype(ctx.out()) {                                         \
-      return formatter<Base, Char>::format(static_cast<Base>(val), ctx); \
-    }                                                                    \
-  }
-
-FMT_FORMAT_AS(signed char, int);
-FMT_FORMAT_AS(unsigned char, unsigned);
-FMT_FORMAT_AS(short, int);
-FMT_FORMAT_AS(unsigned short, unsigned);
-FMT_FORMAT_AS(long, long long);
-FMT_FORMAT_AS(unsigned long, unsigned long long);
-FMT_FORMAT_AS(Char*, const Char*);
-FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
-FMT_FORMAT_AS(std::nullptr_t, const void*);
-FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
-
-template <typename Char> struct basic_runtime { basic_string_view<Char> str; };
+template <typename Char = char> struct runtime_format_string {
+  basic_string_view<Char> str;
+};
 
 /** A compile-time format string. */
 template <typename Char, typename... Args> class basic_format_string {
@@ -3154,17 +2735,18 @@
 #ifdef FMT_HAS_CONSTEVAL
     if constexpr (detail::count_named_args<Args...>() ==
                   detail::count_statically_named_args<Args...>()) {
-      using checker = detail::format_string_checker<Char, detail::error_handler,
-                                                    remove_cvref_t<Args>...>;
-      detail::parse_format_string<true>(str_, checker(s, {}));
+      using checker =
+          detail::format_string_checker<Char, remove_cvref_t<Args>...>;
+      detail::parse_format_string<true>(str_, checker(s));
     }
 #else
     detail::check_format_string<Args...>(s);
 #endif
   }
-  basic_format_string(basic_runtime<Char> r) : str_(r.str) {}
+  basic_format_string(runtime_format_string<Char> fmt) : str_(fmt.str) {}
 
   FMT_INLINE operator basic_string_view<Char>() const { return str_; }
+  FMT_INLINE auto get() const -> basic_string_view<Char> { return str_; }
 };
 
 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
@@ -3184,7 +2766,7 @@
     fmt::print(fmt::runtime("{:d}"), "I am not a number");
   \endrst
  */
-inline auto runtime(string_view s) -> basic_runtime<char> { return {{s}}; }
+inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
 #endif
 
 FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
@@ -3210,10 +2792,9 @@
 template <typename OutputIt,
           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
 auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
-  using detail::get_buffer;
-  auto&& buf = get_buffer<char>(out);
+  auto&& buf = detail::get_buffer<char>(out);
   detail::vformat_to(buf, fmt, args, {});
-  return detail::get_iterator(buf);
+  return detail::get_iterator(buf, out);
 }
 
 /**
@@ -3272,7 +2853,7 @@
 FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
                                              T&&... args) -> size_t {
   auto buf = detail::counting_buffer<>();
-  detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {});
+  detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...), {});
   return buf.count();
 }
 
@@ -3313,7 +2894,25 @@
                            : detail::vprint_mojibake(f, fmt, vargs);
 }
 
-FMT_MODULE_EXPORT_END
+/**
+  Formats ``args`` according to specifications in ``fmt`` and writes the
+  output to the file ``f`` followed by a newline.
+ */
+template <typename... T>
+FMT_INLINE void println(std::FILE* f, format_string<T...> fmt, T&&... args) {
+  return fmt::print(f, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
+}
+
+/**
+  Formats ``args`` according to specifications in ``fmt`` and writes the output
+  to ``stdout`` followed by a newline.
+ */
+template <typename... T>
+FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
+  return fmt::println(stdout, fmt, std::forward<T>(args)...);
+}
+
+FMT_END_EXPORT
 FMT_GCC_PRAGMA("GCC pop_options")
 FMT_END_NAMESPACE
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/format-inl.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/format-inl.h
index abe4ff1..af6ba74 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/format-inl.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/format-inl.h
@@ -9,13 +9,9 @@
 #define FMT_FORMAT_INL_H_
 
 #include <algorithm>
-#include <cctype>
 #include <cerrno>  // errno
 #include <climits>
 #include <cmath>
-#include <cstdarg>
-#include <cstring>  // std::memmove
-#include <cwchar>
 #include <exception>
 
 #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
@@ -113,16 +109,43 @@
   return '.';
 }
 #endif
+
+FMT_FUNC auto write_loc(appender out, loc_value value,
+                        const format_specs<>& specs, locale_ref loc) -> bool {
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+  auto locale = loc.get<std::locale>();
+  // We cannot use the num_put<char> facet because it may produce output in
+  // a wrong encoding.
+  using facet = format_facet<std::locale>;
+  if (std::has_facet<facet>(locale))
+    return std::use_facet<facet>(locale).put(out, value, specs);
+  return facet(locale).put(out, value, specs);
+#endif
+  return false;
+}
 }  // namespace detail
 
-#if !FMT_MSC_VERSION
-FMT_API FMT_FUNC format_error::~format_error() noexcept = default;
+template <typename Locale> typename Locale::id format_facet<Locale>::id;
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
+  auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
+  grouping_ = numpunct.grouping();
+  if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
+}
+
+template <>
+FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
+    appender out, loc_value val, const format_specs<>& specs) const -> bool {
+  return val.visit(
+      detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_});
+}
 #endif
 
-FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str,
+FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt,
                                          format_args args) {
   auto ec = std::error_code(error_code, std::generic_category());
-  return std::system_error(ec, vformat(format_str, args));
+  return std::system_error(ec, vformat(fmt, args));
 }
 
 namespace detail {
@@ -141,58 +164,8 @@
   return (n >> r) | (n << (64 - r));
 }
 
-// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
-inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
-#if FMT_USE_INT128
-  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
-  return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
-#elif defined(_MSC_VER) && defined(_M_X64)
-  auto result = uint128_fallback();
-  result.lo_ = _umul128(x, y, &result.hi_);
-  return result;
-#else
-  const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
-
-  uint64_t a = x >> 32;
-  uint64_t b = x & mask;
-  uint64_t c = y >> 32;
-  uint64_t d = y & mask;
-
-  uint64_t ac = a * c;
-  uint64_t bc = b * c;
-  uint64_t ad = a * d;
-  uint64_t bd = b * d;
-
-  uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
-
-  return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
-          (intermediate << 32) + (bd & mask)};
-#endif
-}
-
 // Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
 namespace dragonbox {
-// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
-inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
-#if FMT_USE_INT128
-  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
-  return static_cast<uint64_t>(p >> 64);
-#elif defined(_MSC_VER) && defined(_M_X64)
-  return __umulh(x, y);
-#else
-  return umul128(x, y).high();
-#endif
-}
-
-// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
-// 128-bit unsigned integer.
-inline uint128_fallback umul192_upper128(uint64_t x,
-                                         uint128_fallback y) noexcept {
-  uint128_fallback r = umul128(x, y.high());
-  r += umul128_upper64(x, y.low());
-  return r;
-}
-
 // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
 // 64-bit unsigned integer.
 inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
@@ -214,25 +187,13 @@
   return x * y;
 }
 
-// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
-// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
-inline int floor_log10_pow2(int e) noexcept {
-  FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
-  static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
-  return (e * 315653) >> 20;
-}
-
 // Various fast log computations.
-inline int floor_log2_pow10(int e) noexcept {
-  FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
-  return (e * 1741647) >> 19;
-}
 inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept {
   FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
   return (e * 631305 - 261663) >> 21;
 }
 
-static constexpr struct {
+FMT_INLINE_VARIABLE constexpr struct {
   uint32_t divisor;
   int shift_amount;
 } div_small_pow10_infos[] = {{10, 16}, {100, 16}};
@@ -286,7 +247,7 @@
 }
 
 // Various subroutines using pow10 cache
-template <class T> struct cache_accessor;
+template <typename T> struct cache_accessor;
 
 template <> struct cache_accessor<float> {
   using carrier_uint = float_info<float>::carrier_uint;
@@ -1007,8 +968,23 @@
       {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
       {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
       {0xc5a05277621be293, 0xc7098b7305241886},
-      { 0xf70867153aa2db38,
-        0xb8cbee4fc66d1ea8 }
+      {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8},
+      {0x9a65406d44a5c903, 0x737f74f1dc043329},
+      {0xc0fe908895cf3b44, 0x505f522e53053ff3},
+      {0xf13e34aabb430a15, 0x647726b9e7c68ff0},
+      {0x96c6e0eab509e64d, 0x5eca783430dc19f6},
+      {0xbc789925624c5fe0, 0xb67d16413d132073},
+      {0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890},
+      {0x933e37a534cbaae7, 0x8e91b962f7b6f15a},
+      {0xb80dc58e81fe95a1, 0x723627bbb5a4adb1},
+      {0xe61136f2227e3b09, 0xcec3b1aaa30dd91d},
+      {0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2},
+      {0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e},
+      {0xe0accfa875af45a7, 0x93eb1b80a33b8606},
+      {0x8c6c01c9498d8b88, 0xbc72f130660533c4},
+      {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
+      { 0xdb68c2ca82ed2a05,
+        0xa67398db9f6820e2 }
 #else
       {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
       {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
@@ -1032,8 +1008,8 @@
       {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
       {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
       {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
-      { 0x95527a5202df0ccb,
-        0x0f37801e0c43ebc9 }
+      {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
+      {0xf13e34aabb430a15, 0x647726b9e7c68ff0}
 #endif
     };
 
@@ -1136,8 +1112,12 @@
   }
 };
 
+FMT_FUNC uint128_fallback get_cached_power(int k) noexcept {
+  return cache_accessor<double>::get_cached_power(k);
+}
+
 // Various integer checks
-template <class T>
+template <typename T>
 bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
   const int case_shorter_interval_left_endpoint_lower_threshold = 2;
   const int case_shorter_interval_left_endpoint_upper_threshold = 3;
@@ -1146,12 +1126,12 @@
 }
 
 // Remove trailing zeros from n and return the number of zeros removed (float)
-FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
+FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
   FMT_ASSERT(n != 0, "");
-  const uint32_t mod_inv_5 = 0xcccccccd;
-  const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
+  // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
+  constexpr uint32_t mod_inv_5 = 0xcccccccd;
+  constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5
 
-  int s = 0;
   while (true) {
     auto q = rotr(n * mod_inv_25, 2);
     if (q > max_value<uint32_t>() / 100) break;
@@ -1163,7 +1143,6 @@
     n = q;
     s |= 1;
   }
-
   return s;
 }
 
@@ -1177,32 +1156,17 @@
 
   // Is n is divisible by 10^8?
   if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
-    // If yes, work with the quotient.
+    // If yes, work with the quotient...
     auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
-
-    const uint32_t mod_inv_5 = 0xcccccccd;
-    const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
-
-    int s = 8;
-    while (true) {
-      auto q = rotr(n32 * mod_inv_25, 2);
-      if (q > max_value<uint32_t>() / 100) break;
-      n32 = q;
-      s += 2;
-    }
-    auto q = rotr(n32 * mod_inv_5, 1);
-    if (q <= max_value<uint32_t>() / 10) {
-      n32 = q;
-      s |= 1;
-    }
-
+    // ... and use the 32 bit variant of the function
+    int s = remove_trailing_zeros(n32, 8);
     n = n32;
     return s;
   }
 
   // If n is not divisible by 10^8, work with n itself.
-  const uint64_t mod_inv_5 = 0xcccccccccccccccd;
-  const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5;
+  constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd;
+  constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // = mod_inv_5 * mod_inv_5
 
   int s = 0;
   while (true) {
@@ -1221,7 +1185,7 @@
 }
 
 // The main algorithm for shorter interval case
-template <class T>
+template <typename T>
 FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
   decimal_fp<T> ret_value;
   // Compute k and beta
@@ -1392,17 +1356,6 @@
   return ret_value;
 }
 }  // namespace dragonbox
-
-#ifdef _MSC_VER
-FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...)
-    -> int {
-  auto args = va_list();
-  va_start(args, fmt);
-  int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args);
-  va_end(args);
-  return result;
-}
-#endif
 }  // namespace detail
 
 template <> struct formatter<detail::bigint> {
@@ -1411,9 +1364,8 @@
     return ctx.begin();
   }
 
-  template <typename FormatContext>
-  auto format(const detail::bigint& n, FormatContext& ctx) const ->
-      typename FormatContext::iterator {
+  auto format(const detail::bigint& n, format_context& ctx) const
+      -> format_context::iterator {
     auto out = ctx.out();
     bool first = true;
     for (auto i = n.bigits_.size(); i > 0; --i) {
@@ -1472,57 +1424,44 @@
 }
 
 namespace detail {
-#ifdef _WIN32
+#ifndef _WIN32
+FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
+#else
 using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
 extern "C" __declspec(dllimport) int __stdcall WriteConsoleW(  //
     void*, const void*, dword, dword*, void*);
 
 FMT_FUNC bool write_console(std::FILE* f, string_view text) {
   auto fd = _fileno(f);
-  if (_isatty(fd)) {
-    detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
-    auto written = detail::dword();
-    if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
-                              u16.c_str(), static_cast<uint32_t>(u16.size()),
-                              &written, nullptr)) {
-      return true;
-    }
-  }
-  // We return false if the file descriptor was not TTY, or it was but
-  // SetConsoleW failed which can happen if the output has been redirected to
-  // NUL. In both cases when we return false, we should attempt to do regular
-  // write via fwrite or std::ostream::write.
-  return false;
-}
-#endif
-
-FMT_FUNC void print(std::FILE* f, string_view text) {
-#ifdef _WIN32
-  if (write_console(f, text)) return;
-#endif
-  detail::fwrite_fully(text.data(), 1, text.size(), f);
-}
-}  // namespace detail
-
-FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
-  memory_buffer buffer;
-  detail::vformat_to(buffer, format_str, args);
-  detail::print(f, {buffer.data(), buffer.size()});
+  if (!_isatty(fd)) return false;
+  auto u16 = utf8_to_utf16(text);
+  auto written = dword();
+  return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
+                       static_cast<uint32_t>(u16.size()), &written, nullptr) != 0;
 }
 
-#ifdef _WIN32
 // Print assuming legacy (non-Unicode) encoding.
-FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
-                                      format_args args) {
-  memory_buffer buffer;
-  detail::vformat_to(buffer, format_str,
+FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
+  auto buffer = memory_buffer();
+  detail::vformat_to(buffer, fmt,
                      basic_format_args<buffer_context<char>>(args));
   fwrite_fully(buffer.data(), 1, buffer.size(), f);
 }
 #endif
 
-FMT_FUNC void vprint(string_view format_str, format_args args) {
-  vprint(stdout, format_str, args);
+FMT_FUNC void print(std::FILE* f, string_view text) {
+  if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f);
+}
+}  // namespace detail
+
+FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
+  auto buffer = memory_buffer();
+  detail::vformat_to(buffer, fmt, args);
+  detail::print(f, {buffer.data(), buffer.size()});
+}
+
+FMT_FUNC void vprint(string_view fmt, format_args args) {
+  vprint(stdout, fmt, args);
 }
 
 namespace detail {
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/format.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/format.h
index 7c607db..ac0f52d 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/format.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/format.h
@@ -33,13 +33,14 @@
 #ifndef FMT_FORMAT_H_
 #define FMT_FORMAT_H_
 
-#include <cmath>         // std::signbit
-#include <cstdint>       // uint32_t
-#include <cstring>       // std::memcpy
-#include <limits>        // std::numeric_limits
-#include <memory>        // std::uninitialized_copy
-#include <stdexcept>     // std::runtime_error
-#include <system_error>  // std::system_error
+#include <cmath>             // std::signbit
+#include <cstdint>           // uint32_t
+#include <cstring>           // std::memcpy
+#include <initializer_list>  // std::initializer_list
+#include <limits>            // std::numeric_limits
+#include <memory>            // std::uninitialized_copy
+#include <stdexcept>         // std::runtime_error
+#include <system_error>      // std::system_error
 
 #ifdef __cpp_lib_bit_cast
 #  include <bit>  // std::bitcast
@@ -47,16 +48,55 @@
 
 #include "core.h"
 
-#if FMT_GCC_VERSION
-#  define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
+#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L
+#  define FMT_INLINE_VARIABLE inline
 #else
-#  define FMT_GCC_VISIBILITY_HIDDEN
+#  define FMT_INLINE_VARIABLE
 #endif
 
-#ifdef __NVCC__
-#  define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__)
+#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
+#  define FMT_FALLTHROUGH [[fallthrough]]
+#elif defined(__clang__)
+#  define FMT_FALLTHROUGH [[clang::fallthrough]]
+#elif FMT_GCC_VERSION >= 700 && \
+    (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
+#  define FMT_FALLTHROUGH [[gnu::fallthrough]]
 #else
-#  define FMT_CUDA_VERSION 0
+#  define FMT_FALLTHROUGH
+#endif
+
+#ifndef FMT_DEPRECATED
+#  if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
+#    define FMT_DEPRECATED [[deprecated]]
+#  else
+#    if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
+#      define FMT_DEPRECATED __attribute__((deprecated))
+#    elif FMT_MSC_VERSION
+#      define FMT_DEPRECATED __declspec(deprecated)
+#    else
+#      define FMT_DEPRECATED /* deprecated */
+#    endif
+#  endif
+#endif
+
+#ifndef FMT_NO_UNIQUE_ADDRESS
+#  if FMT_CPLUSPLUS >= 202002L
+#    if FMT_HAS_CPP_ATTRIBUTE(no_unique_address)
+#      define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]]
+// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485)
+#    elif (FMT_MSC_VERSION >= 1929) && !FMT_CLANG_VERSION
+#      define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
+#    endif
+#  endif
+#endif
+#ifndef FMT_NO_UNIQUE_ADDRESS
+#  define FMT_NO_UNIQUE_ADDRESS
+#endif
+
+#if FMT_GCC_VERSION || defined(__clang__)
+#  define FMT_VISIBILITY(value) __attribute__((visibility(value)))
+#else
+#  define FMT_VISIBILITY(value)
 #endif
 
 #ifdef __has_builtin
@@ -71,12 +111,6 @@
 #  define FMT_NOINLINE
 #endif
 
-#if FMT_MSC_VERSION
-#  define FMT_MSC_DEFAULT = default
-#else
-#  define FMT_MSC_DEFAULT
-#endif
-
 #ifndef FMT_THROW
 #  if FMT_EXCEPTIONS
 #    if FMT_MSC_VERSION || defined(__NVCC__)
@@ -95,10 +129,8 @@
 #      define FMT_THROW(x) throw x
 #    endif
 #  else
-#    define FMT_THROW(x)               \
-      do {                             \
-        FMT_ASSERT(false, (x).what()); \
-      } while (false)
+#    define FMT_THROW(x) \
+      ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what())
 #  endif
 #endif
 
@@ -200,7 +232,8 @@
   _BitScanReverse64(&r, x);
 #  else
   // Scan the high 32 bits.
-  if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32))) return 63 ^ (r + 32);
+  if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
+    return 63 ^ static_cast<int>(r + 32);
   // Scan the low 32 bits.
   _BitScanReverse(&r, static_cast<uint32_t>(x));
 #  endif
@@ -240,6 +273,19 @@
 #endif
 
 FMT_BEGIN_NAMESPACE
+
+template <typename...> struct disjunction : std::false_type {};
+template <typename P> struct disjunction<P> : P {};
+template <typename P1, typename... Pn>
+struct disjunction<P1, Pn...>
+    : conditional_t<bool(P1::value), P1, disjunction<Pn...>> {};
+
+template <typename...> struct conjunction : std::true_type {};
+template <typename P> struct conjunction<P> : P {};
+template <typename P1, typename... Pn>
+struct conjunction<P1, Pn...>
+    : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
+
 namespace detail {
 
 FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
@@ -323,8 +369,6 @@
  private:
   uint64_t lo_, hi_;
 
-  friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept;
-
  public:
   constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
   constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
@@ -359,6 +403,10 @@
       -> uint128_fallback {
     return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
   }
+  friend constexpr auto operator~(const uint128_fallback& n)
+      -> uint128_fallback {
+    return {~n.hi_, ~n.lo_};
+  }
   friend auto operator+(const uint128_fallback& lhs,
                         const uint128_fallback& rhs) -> uint128_fallback {
     auto result = uint128_fallback(lhs);
@@ -397,6 +445,10 @@
     lo_ = new_lo;
     hi_ = new_hi;
   }
+  FMT_CONSTEXPR void operator&=(uint128_fallback n) {
+    lo_ &= n.lo_;
+    hi_ &= n.hi_;
+  }
 
   FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept {
     if (is_constant_evaluated()) {
@@ -463,10 +515,34 @@
   return result;
 }
 
+template <typename UInt>
+FMT_CONSTEXPR20 inline auto countl_zero_fallback(UInt n) -> int {
+  int lz = 0;
+  constexpr UInt msb_mask = static_cast<UInt>(1) << (num_bits<UInt>() - 1);
+  for (; (n & msb_mask) == 0; n <<= 1) lz++;
+  return lz;
+}
+
+FMT_CONSTEXPR20 inline auto countl_zero(uint32_t n) -> int {
+#ifdef FMT_BUILTIN_CLZ
+  if (!is_constant_evaluated()) return FMT_BUILTIN_CLZ(n);
+#endif
+  return countl_zero_fallback(n);
+}
+
+FMT_CONSTEXPR20 inline auto countl_zero(uint64_t n) -> int {
+#ifdef FMT_BUILTIN_CLZLL
+  if (!is_constant_evaluated()) return FMT_BUILTIN_CLZLL(n);
+#endif
+  return countl_zero_fallback(n);
+}
+
 FMT_INLINE void assume(bool condition) {
   (void)condition;
 #if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
   __builtin_assume(condition);
+#elif FMT_GCC_VERSION
+  if (!condition) __builtin_unreachable();
 #endif
 }
 
@@ -485,20 +561,6 @@
   return c.data();
 }
 
-#if defined(_SECURE_SCL) && _SECURE_SCL
-// Make a checked iterator to avoid MSVC warnings.
-template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
-template <typename T>
-constexpr auto make_checked(T* p, size_t size) -> checked_ptr<T> {
-  return {p, size};
-}
-#else
-template <typename T> using checked_ptr = T*;
-template <typename T> constexpr auto make_checked(T* p, size_t) -> T* {
-  return p;
-}
-#endif
-
 // Attempts to reserve space for n extra characters in the output range.
 // Returns a pointer to the reserved range or a reference to it.
 template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
@@ -506,12 +568,12 @@
 __attribute__((no_sanitize("undefined")))
 #endif
 inline auto
-reserve(std::back_insert_iterator<Container> it, size_t n)
-    -> checked_ptr<typename Container::value_type> {
+reserve(std::back_insert_iterator<Container> it, size_t n) ->
+    typename Container::value_type* {
   Container& c = get_container(it);
   size_t size = c.size();
   c.resize(size + n);
-  return make_checked(get_data(c) + size, n);
+  return get_data(c) + size;
 }
 
 template <typename T>
@@ -543,8 +605,8 @@
 }
 
 template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
-inline auto base_iterator(std::back_insert_iterator<Container>& it,
-                          checked_ptr<typename Container::value_type>)
+inline auto base_iterator(std::back_insert_iterator<Container> it,
+                          typename Container::value_type*)
     -> std::back_insert_iterator<Container> {
   return it;
 }
@@ -607,7 +669,8 @@
   constexpr const int shiftc[] = {0, 18, 12, 6, 0};
   constexpr const int shifte[] = {0, 6, 4, 2, 0};
 
-  int len = code_point_length_impl(*s);
+  int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"
+      [static_cast<unsigned char>(*s) >> 3];
   // Compute the pointer to the next character early so that the next
   // iteration can start working on the next character. Neither Clang
   // nor GCC figure out this reordering on their own.
@@ -636,7 +699,7 @@
   return next;
 }
 
-constexpr uint32_t invalid_code_point = ~uint32_t();
+constexpr FMT_INLINE_VARIABLE uint32_t invalid_code_point = ~uint32_t();
 
 // Invokes f(cp, sv) for every code point cp in s with sv being the string view
 // corresponding to the code point. cp is invalid_code_point on error.
@@ -706,6 +769,7 @@
       return true;
     }
   };
+  // We could avoid branches by using utf8_decode directly.
   for_each_codepoint(s, count_code_points{&num_code_points});
   return num_code_points;
 }
@@ -737,13 +801,48 @@
       string_view(reinterpret_cast<const char*>(s.data()), s.size()), n);
 }
 
+template <typename T> struct is_integral : std::is_integral<T> {};
+template <> struct is_integral<int128_opt> : std::true_type {};
+template <> struct is_integral<uint128_t> : std::true_type {};
+
+template <typename T>
+using is_signed =
+    std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
+                                     std::is_same<T, int128_opt>::value>;
+
+template <typename T>
+using is_integer =
+    bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
+                  !std::is_same<T, char>::value &&
+                  !std::is_same<T, wchar_t>::value>;
+
+#ifndef FMT_USE_FLOAT
+#  define FMT_USE_FLOAT 1
+#endif
+#ifndef FMT_USE_DOUBLE
+#  define FMT_USE_DOUBLE 1
+#endif
+#ifndef FMT_USE_LONG_DOUBLE
+#  define FMT_USE_LONG_DOUBLE 1
+#endif
+
 #ifndef FMT_USE_FLOAT128
-#  ifdef __SIZEOF_FLOAT128__
-#    define FMT_USE_FLOAT128 1
-#  else
+#  ifdef __clang__
+// Clang emulates GCC, so it has to appear early.
+#    if FMT_HAS_INCLUDE(<quadmath.h>)
+#      define FMT_USE_FLOAT128 1
+#    endif
+#  elif defined(__GNUC__)
+// GNU C++:
+#    if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__)
+#      define FMT_USE_FLOAT128 1
+#    endif
+#  endif
+#  ifndef FMT_USE_FLOAT128
 #    define FMT_USE_FLOAT128 0
 #  endif
 #endif
+
 #if FMT_USE_FLOAT128
 using float128 = __float128;
 #else
@@ -775,7 +874,7 @@
     try_reserve(size_ + count);
     auto free_cap = capacity_ - size_;
     if (free_cap < count) count = free_cap;
-    std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count));
+    std::uninitialized_copy_n(begin, count, ptr_ + size_);
     size_ += count;
     begin += count;
   }
@@ -787,7 +886,7 @@
 struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {};
 }  // namespace detail
 
-FMT_MODULE_EXPORT_BEGIN
+FMT_BEGIN_EXPORT
 
 // The number of characters to store in the basic_memory_buffer object itself
 // to avoid dynamic memory allocation.
@@ -820,8 +919,8 @@
  private:
   T store_[SIZE];
 
-  // Don't inherit from Allocator avoid generating type_info for it.
-  Allocator alloc_;
+  // Don't inherit from Allocator to avoid generating type_info for it.
+  FMT_NO_UNIQUE_ADDRESS Allocator alloc_;
 
   // Deallocate memory allocated by the buffer.
   FMT_CONSTEXPR20 void deallocate() {
@@ -830,7 +929,28 @@
   }
 
  protected:
-  FMT_CONSTEXPR20 void grow(size_t size) override;
+  FMT_CONSTEXPR20 void grow(size_t size) override {
+    detail::abort_fuzzing_if(size > 5000);
+    const size_t max_size = std::allocator_traits<Allocator>::max_size(alloc_);
+    size_t old_capacity = this->capacity();
+    size_t new_capacity = old_capacity + old_capacity / 2;
+    if (size > new_capacity)
+      new_capacity = size;
+    else if (new_capacity > max_size)
+      new_capacity = size > max_size ? size : max_size;
+    T* old_data = this->data();
+    T* new_data =
+        std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
+    // Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481).
+    detail::assume(this->size() <= new_capacity);
+    // The following code doesn't throw, so the raw pointer above doesn't leak.
+    std::uninitialized_copy_n(old_data, this->size(), new_data);
+    this->set(new_data, new_capacity);
+    // deallocate must not throw according to the standard, but even if it does,
+    // the buffer already uses the new storage and will deallocate it in
+    // destructor.
+    if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
+  }
 
  public:
   using value_type = T;
@@ -852,8 +972,7 @@
     size_t size = other.size(), capacity = other.capacity();
     if (data == other.store_) {
       this->set(store_, capacity);
-      detail::copy_str<T>(other.store_, other.store_ + size,
-                          detail::make_checked(store_, capacity));
+      detail::copy_str<T>(other.store_, other.store_ + size, store_);
     } else {
       this->set(data, capacity);
       // Set pointer to the inline array so that delete is not called
@@ -907,55 +1026,29 @@
   }
 };
 
-template <typename T, size_t SIZE, typename Allocator>
-FMT_CONSTEXPR20 void basic_memory_buffer<T, SIZE, Allocator>::grow(
-    size_t size) {
-  detail::abort_fuzzing_if(size > 5000);
-  const size_t max_size = std::allocator_traits<Allocator>::max_size(alloc_);
-  size_t old_capacity = this->capacity();
-  size_t new_capacity = old_capacity + old_capacity / 2;
-  if (size > new_capacity)
-    new_capacity = size;
-  else if (new_capacity > max_size)
-    new_capacity = size > max_size ? size : max_size;
-  T* old_data = this->data();
-  T* new_data =
-      std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
-  // The following code doesn't throw, so the raw pointer above doesn't leak.
-  std::uninitialized_copy(old_data, old_data + this->size(),
-                          detail::make_checked(new_data, new_capacity));
-  this->set(new_data, new_capacity);
-  // deallocate must not throw according to the standard, but even if it does,
-  // the buffer already uses the new storage and will deallocate it in
-  // destructor.
-  if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
-}
-
 using memory_buffer = basic_memory_buffer<char>;
 
 template <typename T, size_t SIZE, typename Allocator>
 struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
 };
 
+FMT_END_EXPORT
 namespace detail {
-#ifdef _WIN32
 FMT_API bool write_console(std::FILE* f, string_view text);
-#endif
 FMT_API void print(std::FILE*, string_view);
 }  // namespace detail
 
-/** A formatting error such as invalid format string. */
-FMT_CLASS_API
-class FMT_API format_error : public std::runtime_error {
+FMT_BEGIN_EXPORT
+
+// Suppress a misleading warning in older versions of clang.
+#if FMT_CLANG_VERSION
+#  pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+/** An error reported from a formatting function. */
+class FMT_VISIBILITY("default") format_error : public std::runtime_error {
  public:
-  explicit format_error(const char* message) : std::runtime_error(message) {}
-  explicit format_error(const std::string& message)
-      : std::runtime_error(message) {}
-  format_error(const format_error&) = default;
-  format_error& operator=(const format_error&) = default;
-  format_error(format_error&&) = default;
-  format_error& operator=(format_error&&) = default;
-  ~format_error() noexcept override FMT_MSC_DEFAULT;
+  using std::runtime_error::runtime_error;
 };
 
 namespace detail_exported {
@@ -984,16 +1077,52 @@
 }
 }  // namespace detail_exported
 
-FMT_BEGIN_DETAIL_NAMESPACE
+class loc_value {
+ private:
+  basic_format_arg<format_context> value_;
 
-template <typename T> struct is_integral : std::is_integral<T> {};
-template <> struct is_integral<int128_opt> : std::true_type {};
-template <> struct is_integral<uint128_t> : std::true_type {};
+ public:
+  template <typename T, FMT_ENABLE_IF(!detail::is_float128<T>::value)>
+  loc_value(T value) : value_(detail::make_arg<format_context>(value)) {}
 
-template <typename T>
-using is_signed =
-    std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
-                                     std::is_same<T, int128_opt>::value>;
+  template <typename T, FMT_ENABLE_IF(detail::is_float128<T>::value)>
+  loc_value(T) {}
+
+  template <typename Visitor> auto visit(Visitor&& vis) -> decltype(vis(0)) {
+    return visit_format_arg(vis, value_);
+  }
+};
+
+// A locale facet that formats values in UTF-8.
+// It is parameterized on the locale to avoid the heavy <locale> include.
+template <typename Locale> class format_facet : public Locale::facet {
+ private:
+  std::string separator_;
+  std::string grouping_;
+  std::string decimal_point_;
+
+ protected:
+  virtual auto do_put(appender out, loc_value val,
+                      const format_specs<>& specs) const -> bool;
+
+ public:
+  static FMT_API typename Locale::id id;
+
+  explicit format_facet(Locale& loc);
+  explicit format_facet(string_view sep = "",
+                        std::initializer_list<unsigned char> g = {3},
+                        std::string decimal_point = ".")
+      : separator_(sep.data(), sep.size()),
+        grouping_(g.begin(), g.end()),
+        decimal_point_(decimal_point) {}
+
+  auto put(appender out, loc_value val, const format_specs<>& specs) const
+      -> bool {
+    return do_put(out, val, specs);
+  }
+};
+
+namespace detail {
 
 // Returns true if value is negative, false otherwise.
 // Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
@@ -1122,7 +1251,7 @@
 FMT_INLINE auto do_count_digits(uint32_t n) -> int {
 // An optimization by Kendall Willets from https://bit.ly/3uOIQrB.
 // This increments the upper 32 bits (log10(T) - 1) when >= T is added.
-#  define FMT_INC(T) (((sizeof(#  T) - 1ull) << 32) - T)
+#  define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T)
   static constexpr uint64_t table[] = {
       FMT_INC(0),          FMT_INC(0),          FMT_INC(0),           // 8
       FMT_INC(10),         FMT_INC(10),         FMT_INC(10),          // 64
@@ -1195,7 +1324,14 @@
 template <typename Char>
 FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) {
   if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) {
+#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1000
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
     memcpy(dst, src, 2);
+#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1000
+#  pragma GCC diagnostic pop
+#endif
     return;
   }
   *dst++ = static_cast<Char>(*src++);
@@ -1238,7 +1374,7 @@
 FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size)
     -> format_decimal_result<Iterator> {
   // Buffer is large enough to hold all digits (digits10 + 1).
-  Char buffer[digits10<UInt>() + 1];
+  Char buffer[digits10<UInt>() + 1] = {};
   auto end = format_decimal(buffer, value, size).end;
   return {out, detail::copy_str_noinline<Char>(buffer, end, out)};
 }
@@ -1258,8 +1394,8 @@
 }
 
 template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
-inline auto format_uint(It out, UInt value, int num_digits, bool upper = false)
-    -> It {
+FMT_CONSTEXPR inline auto format_uint(It out, UInt value, int num_digits,
+                                      bool upper = false) -> It {
   if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
     format_uint<BASE_BITS>(ptr, value, num_digits, upper);
     return out;
@@ -1283,7 +1419,139 @@
   auto str() const -> std::wstring { return {&buffer_[0], size()}; }
 };
 
+enum class to_utf8_error_policy { abort, replace };
+
+// A converter from UTF-16/UTF-32 (host endian) to UTF-8.
+template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
+ private:
+  Buffer buffer_;
+
+ public:
+  to_utf8() {}
+  explicit to_utf8(basic_string_view<WChar> s,
+                   to_utf8_error_policy policy = to_utf8_error_policy::abort) {
+    static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4,
+                  "Expect utf16 or utf32");
+    if (!convert(s, policy))
+      FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16"
+                                                      : "invalid utf32"));
+  }
+  operator string_view() const { return string_view(&buffer_[0], size()); }
+  size_t size() const { return buffer_.size() - 1; }
+  const char* c_str() const { return &buffer_[0]; }
+  std::string str() const { return std::string(&buffer_[0], size()); }
+
+  // Performs conversion returning a bool instead of throwing exception on
+  // conversion error. This method may still throw in case of memory allocation
+  // error.
+  bool convert(basic_string_view<WChar> s,
+               to_utf8_error_policy policy = to_utf8_error_policy::abort) {
+    if (!convert(buffer_, s, policy)) return false;
+    buffer_.push_back(0);
+    return true;
+  }
+  static bool convert(
+      Buffer& buf, basic_string_view<WChar> s,
+      to_utf8_error_policy policy = to_utf8_error_policy::abort) {
+    for (auto p = s.begin(); p != s.end(); ++p) {
+      uint32_t c = static_cast<uint32_t>(*p);
+      if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) {
+        // Handle a surrogate pair.
+        ++p;
+        if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
+          if (policy == to_utf8_error_policy::abort) return false;
+          buf.append(string_view("\xEF\xBF\xBD"));
+          --p;
+        } else {
+          c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
+        }
+      } else if (c < 0x80) {
+        buf.push_back(static_cast<char>(c));
+      } else if (c < 0x800) {
+        buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
+        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+      } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
+        buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
+        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
+        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+      } else if (c >= 0x10000 && c <= 0x10ffff) {
+        buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
+        buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
+        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
+        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+      } else {
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
+// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
+inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
+#if FMT_USE_INT128
+  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
+  return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
+#elif defined(_MSC_VER) && defined(_M_X64)
+  auto hi = uint64_t();
+  auto lo = _umul128(x, y, &hi);
+  return {hi, lo};
+#else
+  const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
+
+  uint64_t a = x >> 32;
+  uint64_t b = x & mask;
+  uint64_t c = y >> 32;
+  uint64_t d = y & mask;
+
+  uint64_t ac = a * c;
+  uint64_t bc = b * c;
+  uint64_t ad = a * d;
+  uint64_t bd = b * d;
+
+  uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
+
+  return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
+          (intermediate << 32) + (bd & mask)};
+#endif
+}
+
 namespace dragonbox {
+// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
+// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
+inline int floor_log10_pow2(int e) noexcept {
+  FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
+  static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
+  return (e * 315653) >> 20;
+}
+
+inline int floor_log2_pow10(int e) noexcept {
+  FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
+  return (e * 1741647) >> 19;
+}
+
+// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
+inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
+#if FMT_USE_INT128
+  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
+  return static_cast<uint64_t>(p >> 64);
+#elif defined(_MSC_VER) && defined(_M_X64)
+  return __umulh(x, y);
+#else
+  return umul128(x, y).high();
+#endif
+}
+
+// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
+// 128-bit unsigned integer.
+inline uint128_fallback umul192_upper128(uint64_t x,
+                                         uint128_fallback y) noexcept {
+  uint128_fallback r = umul128(x, y.high());
+  r += umul128_upper64(x, y.low());
+  return r;
+}
+
+FMT_API uint128_fallback get_cached_power(int k) noexcept;
 
 // Type-specific information that Dragonbox uses.
 template <typename T, typename Enable = void> struct float_info;
@@ -1307,7 +1575,7 @@
   static const int big_divisor = 1000;
   static const int small_divisor = 100;
   static const int min_k = -292;
-  static const int max_k = 326;
+  static const int max_k = 341;
   static const int shorter_interval_tie_lower_threshold = -77;
   static const int shorter_interval_tie_upper_threshold = -77;
 };
@@ -1354,8 +1622,8 @@
 template <typename Float>
 constexpr auto exponent_mask() ->
     typename dragonbox::float_info<Float>::carrier_uint {
-  using uint = typename dragonbox::float_info<Float>::carrier_uint;
-  return ((uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1)
+  using float_uint = typename dragonbox::float_info<Float>::carrier_uint;
+  return ((float_uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1)
          << num_significand_bits<Float>();
 }
 template <typename Float> constexpr auto exponent_bias() -> int {
@@ -1476,157 +1744,31 @@
 }
 
 template <typename T = void> struct basic_data {
-  // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
-  // These are generated by support/compute-powers.py.
-  static constexpr uint64_t pow10_significands[87] = {
-      0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
-      0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
-      0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
-      0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
-      0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
-      0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
-      0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
-      0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
-      0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
-      0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
-      0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
-      0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
-      0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
-      0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
-      0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
-      0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
-      0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
-      0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
-      0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
-      0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
-      0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
-      0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
-      0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
-      0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
-      0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
-      0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
-      0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
-      0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
-      0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
+  // For checking rounding thresholds.
+  // The kth entry is chosen to be the smallest integer such that the
+  // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k.
+  static constexpr uint32_t fractional_part_rounding_thresholds[8] = {
+      2576980378U,  // ceil(2^31 + 2^32/10^1)
+      2190433321U,  // ceil(2^31 + 2^32/10^2)
+      2151778616U,  // ceil(2^31 + 2^32/10^3)
+      2147913145U,  // ceil(2^31 + 2^32/10^4)
+      2147526598U,  // ceil(2^31 + 2^32/10^5)
+      2147487943U,  // ceil(2^31 + 2^32/10^6)
+      2147484078U,  // ceil(2^31 + 2^32/10^7)
+      2147483691U   // ceil(2^31 + 2^32/10^8)
   };
-
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-#  pragma GCC diagnostic push
-#  pragma GCC diagnostic ignored "-Wnarrowing"
-#endif
-  // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
-  // to significands above.
-  static constexpr int16_t pow10_exponents[87] = {
-      -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
-      -927,  -901,  -874,  -847,  -821,  -794,  -768,  -741,  -715,  -688, -661,
-      -635,  -608,  -582,  -555,  -529,  -502,  -475,  -449,  -422,  -396, -369,
-      -343,  -316,  -289,  -263,  -236,  -210,  -183,  -157,  -130,  -103, -77,
-      -50,   -24,   3,     30,    56,    83,    109,   136,   162,   189,  216,
-      242,   269,   295,   322,   348,   375,   402,   428,   455,   481,  508,
-      534,   561,   588,   614,   641,   667,   694,   720,   747,   774,  800,
-      827,   853,   880,   907,   933,   960,   986,   1013,  1039,  1066};
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-#  pragma GCC diagnostic pop
-#endif
-
-  static constexpr uint64_t power_of_10_64[20] = {
-      1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL),
-      10000000000000000000ULL};
 };
-
-#if FMT_CPLUSPLUS < 201703L
-template <typename T> constexpr uint64_t basic_data<T>::pow10_significands[];
-template <typename T> constexpr int16_t basic_data<T>::pow10_exponents[];
-template <typename T> constexpr uint64_t basic_data<T>::power_of_10_64[];
-#endif
-
 // This is a struct rather than an alias to avoid shadowing warnings in gcc.
 struct data : basic_data<> {};
 
-// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
-// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
-FMT_CONSTEXPR inline fp get_cached_power(int min_exponent,
-                                         int& pow10_exponent) {
-  const int shift = 32;
-  // log10(2) = 0x0.4d104d427de7fbcc...
-  const int64_t significand = 0x4d104d427de7fbcc;
-  int index = static_cast<int>(
-      ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) +
-       ((int64_t(1) << shift) - 1))  // ceil
-      >> 32                          // arithmetic shift
-  );
-  // Decimal exponent of the first (smallest) cached power of 10.
-  const int first_dec_exp = -348;
-  // Difference between 2 consecutive decimal exponents in cached powers of 10.
-  const int dec_exp_step = 8;
-  index = (index - first_dec_exp - 1) / dec_exp_step + 1;
-  pow10_exponent = first_dec_exp + index * dec_exp_step;
-  // Using *(x + index) instead of x[index] avoids an issue with some compilers
-  // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode).
-  return {*(data::pow10_significands + index),
-          *(data::pow10_exponents + index)};
-}
-
-#ifndef _MSC_VER
-#  define FMT_SNPRINTF snprintf
-#else
-FMT_API auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) -> int;
-#  define FMT_SNPRINTF fmt_snprintf
-#endif  // _MSC_VER
-
-// Formats a floating-point number with snprintf using the hexfloat format.
+#if FMT_CPLUSPLUS < 201703L
 template <typename T>
-auto snprintf_float(T value, int precision, float_specs specs,
-                    buffer<char>& buf) -> int {
-  // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
-  FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
-  FMT_ASSERT(specs.format == float_format::hex, "");
-  static_assert(!std::is_same<T, float>::value, "");
+constexpr uint32_t basic_data<T>::fractional_part_rounding_thresholds[];
+#endif
 
-  // Build the format string.
-  char format[7];  // The longest format is "%#.*Le".
-  char* format_ptr = format;
-  *format_ptr++ = '%';
-  if (specs.showpoint) *format_ptr++ = '#';
-  if (precision >= 0) {
-    *format_ptr++ = '.';
-    *format_ptr++ = '*';
-  }
-  if (std::is_same<T, long double>()) *format_ptr++ = 'L';
-  *format_ptr++ = specs.upper ? 'A' : 'a';
-  *format_ptr = '\0';
-
-  // Format using snprintf.
-  auto offset = buf.size();
-  for (;;) {
-    auto begin = buf.data() + offset;
-    auto capacity = buf.capacity() - offset;
-    abort_fuzzing_if(precision > 100000);
-    // Suppress the warning about a nonliteral format string.
-    // Cannot use auto because of a bug in MinGW (#1532).
-    int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
-    int result = precision >= 0
-                     ? snprintf_ptr(begin, capacity, format, precision, value)
-                     : snprintf_ptr(begin, capacity, format, value);
-    if (result < 0) {
-      // The buffer will grow exponentially.
-      buf.try_reserve(buf.capacity() + 1);
-      continue;
-    }
-    auto size = to_unsigned(result);
-    // Size equal to capacity means that the last character was truncated.
-    if (size < capacity) {
-      buf.try_resize(size + offset);
-      return 0;
-    }
-    buf.try_reserve(size + offset + 1);  // Add 1 for the terminating '\0'.
-  }
-}
-
-template <typename T>
+template <typename T, bool doublish = num_bits<T>() == num_bits<double>()>
 using convert_float_result =
-    conditional_t<std::is_same<T, float>::value || sizeof(T) == sizeof(double),
-                  double, T>;
+    conditional_t<std::is_same<T, float>::value || doublish, double, T>;
 
 template <typename T>
 constexpr auto convert_float(T value) -> convert_float_result<T> {
@@ -1649,8 +1791,7 @@
 // width: output display width in (terminal) column positions.
 template <align::type align = align::left, typename OutputIt, typename Char,
           typename F>
-FMT_CONSTEXPR auto write_padded(OutputIt out,
-                                const basic_format_specs<Char>& specs,
+FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs<Char>& specs,
                                 size_t size, size_t width, F&& f) -> OutputIt {
   static_assert(align == align::left || align == align::right, "");
   unsigned spec_width = to_unsigned(specs.width);
@@ -1669,15 +1810,14 @@
 
 template <align::type align = align::left, typename OutputIt, typename Char,
           typename F>
-constexpr auto write_padded(OutputIt out, const basic_format_specs<Char>& specs,
+constexpr auto write_padded(OutputIt out, const format_specs<Char>& specs,
                             size_t size, F&& f) -> OutputIt {
   return write_padded<align>(out, specs, size, size, f);
 }
 
 template <align::type align = align::left, typename Char, typename OutputIt>
 FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
-                               const basic_format_specs<Char>& specs)
-    -> OutputIt {
+                               const format_specs<Char>& specs) -> OutputIt {
   return write_padded<align>(
       out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) {
         const char* data = bytes.data();
@@ -1686,8 +1826,8 @@
 }
 
 template <typename Char, typename OutputIt, typename UIntPtr>
-auto write_ptr(OutputIt out, UIntPtr value,
-               const basic_format_specs<Char>* specs) -> OutputIt {
+auto write_ptr(OutputIt out, UIntPtr value, const format_specs<Char>* specs)
+    -> OutputIt {
   int num_digits = count_digits<4>(value);
   auto size = to_unsigned(num_digits) + size_t(2);
   auto write = [=](reserve_iterator<OutputIt> it) {
@@ -1749,7 +1889,7 @@
   [] {                                                                        \
     /* Use the hidden visibility as a workaround for a GCC bug (#1973). */    \
     /* Use a macro-like name to avoid shadowing warnings. */                  \
-    struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base {              \
+    struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base {               \
       using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \
       FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit                                 \
       operator fmt::basic_string_view<char_type>() const {                    \
@@ -1806,16 +1946,14 @@
     *out++ = static_cast<Char>('\\');
     break;
   default:
-    if (is_utf8()) {
-      if (escape.cp < 0x100) {
-        return write_codepoint<2, Char>(out, 'x', escape.cp);
-      }
-      if (escape.cp < 0x10000) {
-        return write_codepoint<4, Char>(out, 'u', escape.cp);
-      }
-      if (escape.cp < 0x110000) {
-        return write_codepoint<8, Char>(out, 'U', escape.cp);
-      }
+    if (escape.cp < 0x100) {
+      return write_codepoint<2, Char>(out, 'x', escape.cp);
+    }
+    if (escape.cp < 0x10000) {
+      return write_codepoint<4, Char>(out, 'u', escape.cp);
+    }
+    if (escape.cp < 0x110000) {
+      return write_codepoint<8, Char>(out, 'U', escape.cp);
     }
     for (Char escape_char : basic_string_view<Char>(
              escape.begin, to_unsigned(escape.end - escape.begin))) {
@@ -1860,8 +1998,7 @@
 
 template <typename Char, typename OutputIt>
 FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
-                              const basic_format_specs<Char>& specs)
-    -> OutputIt {
+                              const format_specs<Char>& specs) -> OutputIt {
   bool is_debug = specs.type == presentation_type::debug;
   return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
     if (is_debug) return write_escaped_char(it, value);
@@ -1871,11 +2008,14 @@
 }
 template <typename Char, typename OutputIt>
 FMT_CONSTEXPR auto write(OutputIt out, Char value,
-                         const basic_format_specs<Char>& specs,
-                         locale_ref loc = {}) -> OutputIt {
+                         const format_specs<Char>& specs, locale_ref loc = {})
+    -> OutputIt {
+  // char is formatted as unsigned char for consistency across platforms.
+  using unsigned_type =
+      conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>;
   return check_char_specs(specs)
              ? write_char(out, value, specs)
-             : write(out, static_cast<int>(value), specs, loc);
+             : write(out, static_cast<unsigned_type>(value), specs, loc);
 }
 
 // Data for write_int that doesn't depend on output iterator type. It is used to
@@ -1885,7 +2025,7 @@
   size_t padding;
 
   FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
-                               const basic_format_specs<Char>& specs)
+                               const format_specs<Char>& specs)
       : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
     if (specs.align == align::numeric) {
       auto width = to_unsigned(specs.width);
@@ -1907,7 +2047,7 @@
 template <typename OutputIt, typename Char, typename W>
 FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
                                         unsigned prefix,
-                                        const basic_format_specs<Char>& specs,
+                                        const format_specs<Char>& specs,
                                         W write_digits) -> OutputIt {
   // Slightly faster check for specs.width == 0 && specs.precision == -1.
   if ((specs.width | (specs.precision + 1)) == 0) {
@@ -1930,19 +2070,19 @@
 
 template <typename Char> class digit_grouping {
  private:
-  thousands_sep_result<Char> sep_;
+  std::string grouping_;
+  std::basic_string<Char> thousands_sep_;
 
   struct next_state {
     std::string::const_iterator group;
     int pos;
   };
-  next_state initial_state() const { return {sep_.grouping.begin(), 0}; }
+  next_state initial_state() const { return {grouping_.begin(), 0}; }
 
   // Returns the next digit group separator position.
   int next(next_state& state) const {
-    if (!sep_.thousands_sep) return max_value<int>();
-    if (state.group == sep_.grouping.end())
-      return state.pos += sep_.grouping.back();
+    if (thousands_sep_.empty()) return max_value<int>();
+    if (state.group == grouping_.end()) return state.pos += grouping_.back();
     if (*state.group <= 0 || *state.group == max_value<char>())
       return max_value<int>();
     state.pos += *state.group++;
@@ -1951,14 +2091,15 @@
 
  public:
   explicit digit_grouping(locale_ref loc, bool localized = true) {
-    if (localized)
-      sep_ = thousands_sep<Char>(loc);
-    else
-      sep_.thousands_sep = Char();
+    if (!localized) return;
+    auto sep = thousands_sep<Char>(loc);
+    grouping_ = sep.grouping;
+    if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep);
   }
-  explicit digit_grouping(thousands_sep_result<Char> sep) : sep_(sep) {}
+  digit_grouping(std::string grouping, std::basic_string<Char> sep)
+      : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {}
 
-  Char separator() const { return sep_.thousands_sep; }
+  bool has_separator() const { return !thousands_sep_.empty(); }
 
   int count_separators(int num_digits) const {
     int count = 0;
@@ -1981,7 +2122,9 @@
     for (int i = 0, sep_index = static_cast<int>(separators.size() - 1);
          i < num_digits; ++i) {
       if (num_digits - i == separators[sep_index]) {
-        *out++ = separator();
+        out =
+            copy_str<Char>(thousands_sep_.data(),
+                           thousands_sep_.data() + thousands_sep_.size(), out);
         --sep_index;
       }
       *out++ = static_cast<Char>(digits[to_unsigned(i)]);
@@ -1990,10 +2133,11 @@
   }
 };
 
+// Writes a decimal integer with digit grouping.
 template <typename OutputIt, typename UInt, typename Char>
-auto write_int_localized(OutputIt out, UInt value, unsigned prefix,
-                         const basic_format_specs<Char>& specs,
-                         const digit_grouping<Char>& grouping) -> OutputIt {
+auto write_int(OutputIt out, UInt value, unsigned prefix,
+               const format_specs<Char>& specs,
+               const digit_grouping<Char>& grouping) -> OutputIt {
   static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
   int num_digits = count_digits(value);
   char digits[40];
@@ -2010,13 +2154,13 @@
       });
 }
 
-template <typename OutputIt, typename UInt, typename Char>
-auto write_int_localized(OutputIt& out, UInt value, unsigned prefix,
-                         const basic_format_specs<Char>& specs, locale_ref loc)
-    -> bool {
-  auto grouping = digit_grouping<Char>(loc);
-  out = write_int_localized(out, value, prefix, specs, grouping);
-  return true;
+// Writes a localized value.
+FMT_API auto write_loc(appender out, loc_value value,
+                       const format_specs<>& specs, locale_ref loc) -> bool;
+template <typename OutputIt, typename Char>
+inline auto write_loc(OutputIt, loc_value, const format_specs<Char>&,
+                      locale_ref) -> bool {
+  return false;
 }
 
 FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
@@ -2045,21 +2189,37 @@
   return {abs_value, prefix};
 }
 
+template <typename Char = char> struct loc_writer {
+  buffer_appender<Char> out;
+  const format_specs<Char>& specs;
+  std::basic_string<Char> sep;
+  std::string grouping;
+  std::basic_string<Char> decimal_point;
+
+  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+  auto operator()(T value) -> bool {
+    auto arg = make_write_int_arg(value, specs.sign);
+    write_int(out, static_cast<uint64_or_128_t<T>>(arg.abs_value), arg.prefix,
+              specs, digit_grouping<Char>(grouping, sep));
+    return true;
+  }
+
+  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+  auto operator()(T) -> bool {
+    return false;
+  }
+};
+
 template <typename Char, typename OutputIt, typename T>
 FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
-                                        const basic_format_specs<Char>& specs,
-                                        locale_ref loc) -> OutputIt {
+                                        const format_specs<Char>& specs,
+                                        locale_ref) -> OutputIt {
   static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
   auto abs_value = arg.abs_value;
   auto prefix = arg.prefix;
   switch (specs.type) {
   case presentation_type::none:
   case presentation_type::dec: {
-    if (specs.localized &&
-        write_int_localized(out, static_cast<uint64_or_128_t<T>>(abs_value),
-                            prefix, specs, loc)) {
-      return out;
-    }
     auto num_digits = count_digits(abs_value);
     return write_int(
         out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
@@ -2102,13 +2262,13 @@
   case presentation_type::chr:
     return write_char(out, static_cast<Char>(abs_value), specs);
   default:
-    throw_format_error("invalid type specifier");
+    throw_format_error("invalid format specifier");
   }
   return out;
 }
 template <typename Char, typename OutputIt, typename T>
 FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(
-    OutputIt out, write_int_arg<T> arg, const basic_format_specs<Char>& specs,
+    OutputIt out, write_int_arg<T> arg, const format_specs<Char>& specs,
     locale_ref loc) -> OutputIt {
   return write_int(out, arg, specs, loc);
 }
@@ -2117,8 +2277,9 @@
                         !std::is_same<T, bool>::value &&
                         std::is_same<OutputIt, buffer_appender<Char>>::value)>
 FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
-                                    const basic_format_specs<Char>& specs,
+                                    const format_specs<Char>& specs,
                                     locale_ref loc) -> OutputIt {
+  if (specs.localized && write_loc(out, value, specs, loc)) return out;
   return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs,
                             loc);
 }
@@ -2128,8 +2289,9 @@
                         !std::is_same<T, bool>::value &&
                         !std::is_same<OutputIt, buffer_appender<Char>>::value)>
 FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
-                                    const basic_format_specs<Char>& specs,
+                                    const format_specs<Char>& specs,
                                     locale_ref loc) -> OutputIt {
+  if (specs.localized && write_loc(out, value, specs, loc)) return out;
   return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
 }
 
@@ -2175,7 +2337,7 @@
 
 template <typename Char, typename OutputIt>
 FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
-                         const basic_format_specs<Char>& specs) -> OutputIt {
+                         const format_specs<Char>& specs) -> OutputIt {
   auto data = s.data();
   auto size = s.size();
   if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
@@ -2197,16 +2359,15 @@
 template <typename Char, typename OutputIt>
 FMT_CONSTEXPR auto write(OutputIt out,
                          basic_string_view<type_identity_t<Char>> s,
-                         const basic_format_specs<Char>& specs, locale_ref)
+                         const format_specs<Char>& specs, locale_ref)
     -> OutputIt {
-  check_string_type_spec(specs.type);
   return write(out, s, specs);
 }
 template <typename Char, typename OutputIt>
 FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
-                         const basic_format_specs<Char>& specs, locale_ref)
+                         const format_specs<Char>& specs, locale_ref)
     -> OutputIt {
-  return check_cstring_type_spec(specs.type)
+  return specs.type != presentation_type::pointer
              ? write(out, basic_string_view<Char>(s), specs, {})
              : write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
 }
@@ -2233,9 +2394,114 @@
   return base_iterator(out, it);
 }
 
+// DEPRECATED!
+template <typename Char>
+FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
+                               format_specs<Char>& specs) -> const Char* {
+  FMT_ASSERT(begin != end, "");
+  auto align = align::none;
+  auto p = begin + code_point_length(begin);
+  if (end - p <= 0) p = begin;
+  for (;;) {
+    switch (to_ascii(*p)) {
+    case '<':
+      align = align::left;
+      break;
+    case '>':
+      align = align::right;
+      break;
+    case '^':
+      align = align::center;
+      break;
+    }
+    if (align != align::none) {
+      if (p != begin) {
+        auto c = *begin;
+        if (c == '}') return begin;
+        if (c == '{') {
+          throw_format_error("invalid fill character '{'");
+          return begin;
+        }
+        specs.fill = {begin, to_unsigned(p - begin)};
+        begin = p + 1;
+      } else {
+        ++begin;
+      }
+      break;
+    } else if (p == begin) {
+      break;
+    }
+    p = begin;
+  }
+  specs.align = align;
+  return begin;
+}
+
+// A floating-point presentation format.
+enum class float_format : unsigned char {
+  general,  // General: exponent notation or fixed point based on magnitude.
+  exp,      // Exponent notation with the default precision of 6, e.g. 1.2e-3.
+  fixed,    // Fixed point with the default precision of 6, e.g. 0.0012.
+  hex
+};
+
+struct float_specs {
+  int precision;
+  float_format format : 8;
+  sign_t sign : 8;
+  bool upper : 1;
+  bool locale : 1;
+  bool binary32 : 1;
+  bool showpoint : 1;
+};
+
+template <typename ErrorHandler = error_handler, typename Char>
+FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs,
+                                         ErrorHandler&& eh = {})
+    -> float_specs {
+  auto result = float_specs();
+  result.showpoint = specs.alt;
+  result.locale = specs.localized;
+  switch (specs.type) {
+  case presentation_type::none:
+    result.format = float_format::general;
+    break;
+  case presentation_type::general_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::general_lower:
+    result.format = float_format::general;
+    break;
+  case presentation_type::exp_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::exp_lower:
+    result.format = float_format::exp;
+    result.showpoint |= specs.precision != 0;
+    break;
+  case presentation_type::fixed_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::fixed_lower:
+    result.format = float_format::fixed;
+    result.showpoint |= specs.precision != 0;
+    break;
+  case presentation_type::hexfloat_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::hexfloat_lower:
+    result.format = float_format::hex;
+    break;
+  default:
+    eh.on_error("invalid format specifier");
+    break;
+  }
+  return result;
+}
+
 template <typename Char, typename OutputIt>
 FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
-                                     basic_format_specs<Char> specs,
+                                     format_specs<Char> specs,
                                      const float_specs& fspecs) -> OutputIt {
   auto str =
       isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf");
@@ -2281,7 +2547,7 @@
 FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
                                        int significand_size, int exponent,
                                        const Grouping& grouping) -> OutputIt {
-  if (!grouping.separator()) {
+  if (!grouping.has_separator()) {
     out = write_significand<Char>(out, significand, significand_size);
     return detail::fill_n(out, exponent, static_cast<Char>('0'));
   }
@@ -2343,7 +2609,7 @@
                                        int significand_size, int integral_size,
                                        Char decimal_point,
                                        const Grouping& grouping) -> OutputIt {
-  if (!grouping.separator()) {
+  if (!grouping.has_separator()) {
     return write_significand(out, significand, significand_size, integral_size,
                              decimal_point);
   }
@@ -2359,7 +2625,7 @@
 template <typename OutputIt, typename DecimalFP, typename Char,
           typename Grouping = digit_grouping<Char>>
 FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
-                                    const basic_format_specs<Char>& specs,
+                                    const format_specs<Char>& specs,
                                     float_specs fspecs, locale_ref loc)
     -> OutputIt {
   auto significand = f.significand;
@@ -2418,7 +2684,7 @@
     abort_fuzzing_if(num_zeros > 5000);
     if (fspecs.showpoint) {
       ++size;
-      if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1;
+      if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0;
       if (num_zeros > 0) size += to_unsigned(num_zeros);
     }
     auto grouping = Grouping(loc, fspecs.locale);
@@ -2436,7 +2702,7 @@
     int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
     size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
     auto grouping = Grouping(loc, fspecs.locale);
-    size += to_unsigned(grouping.count_separators(significand_size));
+    size += to_unsigned(grouping.count_separators(exp));
     return write_padded<align::right>(out, specs, size, [&](iterator it) {
       if (sign) *it++ = detail::sign<Char>(sign);
       it = write_significand(it, significand, significand_size, exp,
@@ -2466,7 +2732,7 @@
  public:
   constexpr fallback_digit_grouping(locale_ref, bool) {}
 
-  constexpr Char separator() const { return Char(); }
+  constexpr bool has_separator() const { return false; }
 
   constexpr int count_separators(int) const { return 0; }
 
@@ -2478,7 +2744,7 @@
 
 template <typename OutputIt, typename DecimalFP, typename Char>
 FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
-                                 const basic_format_specs<Char>& specs,
+                                 const format_specs<Char>& specs,
                                  float_specs fspecs, locale_ref loc)
     -> OutputIt {
   if (is_constant_evaluated()) {
@@ -2506,14 +2772,14 @@
 FMT_CONSTEXPR20 bool isfinite(T value) {
   constexpr T inf = T(std::numeric_limits<double>::infinity());
   if (is_constant_evaluated())
-    return !detail::isnan(value) && value != inf && value != -inf;
+    return !detail::isnan(value) && value < inf && value > -inf;
   return std::isfinite(value);
 }
 template <typename T, FMT_ENABLE_IF(!has_isfinite<T>::value)>
 FMT_CONSTEXPR bool isfinite(T value) {
   T inf = T(std::numeric_limits<double>::infinity());
   // std::isfinite doesn't support __float128.
-  return !detail::isnan(value) && value != inf && value != -inf;
+  return !detail::isnan(value) && value < inf && value > -inf;
 }
 
 template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
@@ -2529,78 +2795,6 @@
   return std::signbit(static_cast<double>(value));
 }
 
-enum class round_direction { unknown, up, down };
-
-// Given the divisor (normally a power of 10), the remainder = v % divisor for
-// some number v and the error, returns whether v should be rounded up, down, or
-// whether the rounding direction can't be determined due to error.
-// error should be less than divisor / 2.
-FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
-                                                         uint64_t remainder,
-                                                         uint64_t error) {
-  FMT_ASSERT(remainder < divisor, "");  // divisor - remainder won't overflow.
-  FMT_ASSERT(error < divisor, "");      // divisor - error won't overflow.
-  FMT_ASSERT(error < divisor - error, "");  // error * 2 won't overflow.
-  // Round down if (remainder + error) * 2 <= divisor.
-  if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
-    return round_direction::down;
-  // Round up if (remainder - error) * 2 >= divisor.
-  if (remainder >= error &&
-      remainder - error >= divisor - (remainder - error)) {
-    return round_direction::up;
-  }
-  return round_direction::unknown;
-}
-
-namespace digits {
-enum result {
-  more,  // Generate more digits.
-  done,  // Done generating digits.
-  error  // Digit generation cancelled due to an error.
-};
-}
-
-struct gen_digits_handler {
-  char* buf;
-  int size;
-  int precision;
-  int exp10;
-  bool fixed;
-
-  FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor,
-                                        uint64_t remainder, uint64_t error,
-                                        bool integral) {
-    FMT_ASSERT(remainder < divisor, "");
-    buf[size++] = digit;
-    if (!integral && error >= remainder) return digits::error;
-    if (size < precision) return digits::more;
-    if (!integral) {
-      // Check if error * 2 < divisor with overflow prevention.
-      // The check is not needed for the integral part because error = 1
-      // and divisor > (1 << 32) there.
-      if (error >= divisor || error >= divisor - error) return digits::error;
-    } else {
-      FMT_ASSERT(error == 1 && divisor > 2, "");
-    }
-    auto dir = get_round_direction(divisor, remainder, error);
-    if (dir != round_direction::up)
-      return dir == round_direction::down ? digits::done : digits::error;
-    ++buf[size - 1];
-    for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
-      buf[i] = '0';
-      ++buf[i - 1];
-    }
-    if (buf[0] > '9') {
-      buf[0] = '1';
-      if (fixed)
-        buf[size++] = '0';
-      else
-        ++exp10;
-    }
-    return digits::done;
-  }
-};
-
 inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) {
   // Adjust fixed precision by exponent because it is relative to decimal
   // point.
@@ -2609,101 +2803,6 @@
   precision += exp10;
 }
 
-// Generates output using the Grisu digit-gen algorithm.
-// error: the size of the region (lower, upper) outside of which numbers
-// definitely do not round to value (Delta in Grisu3).
-FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error,
-                                                 int& exp,
-                                                 gen_digits_handler& handler)
-    -> digits::result {
-  const fp one(1ULL << -value.e, value.e);
-  // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
-  // zero because it contains a product of two 64-bit numbers with MSB set (due
-  // to normalization) - 1, shifted right by at most 60 bits.
-  auto integral = static_cast<uint32_t>(value.f >> -one.e);
-  FMT_ASSERT(integral != 0, "");
-  FMT_ASSERT(integral == value.f >> -one.e, "");
-  // The fractional part of scaled value (p2 in Grisu) c = value % one.
-  uint64_t fractional = value.f & (one.f - 1);
-  exp = count_digits(integral);  // kappa in Grisu.
-  // Non-fixed formats require at least one digit and no precision adjustment.
-  if (handler.fixed) {
-    adjust_precision(handler.precision, exp + handler.exp10);
-    // Check if precision is satisfied just by leading zeros, e.g.
-    // format("{:.2f}", 0.001) gives "0.00" without generating any digits.
-    if (handler.precision <= 0) {
-      if (handler.precision < 0) return digits::done;
-      // Divide by 10 to prevent overflow.
-      uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e;
-      auto dir = get_round_direction(divisor, value.f / 10, error * 10);
-      if (dir == round_direction::unknown) return digits::error;
-      handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
-      return digits::done;
-    }
-  }
-  // Generate digits for the integral part. This can produce up to 10 digits.
-  do {
-    uint32_t digit = 0;
-    auto divmod_integral = [&](uint32_t divisor) {
-      digit = integral / divisor;
-      integral %= divisor;
-    };
-    // This optimization by Milo Yip reduces the number of integer divisions by
-    // one per iteration.
-    switch (exp) {
-    case 10:
-      divmod_integral(1000000000);
-      break;
-    case 9:
-      divmod_integral(100000000);
-      break;
-    case 8:
-      divmod_integral(10000000);
-      break;
-    case 7:
-      divmod_integral(1000000);
-      break;
-    case 6:
-      divmod_integral(100000);
-      break;
-    case 5:
-      divmod_integral(10000);
-      break;
-    case 4:
-      divmod_integral(1000);
-      break;
-    case 3:
-      divmod_integral(100);
-      break;
-    case 2:
-      divmod_integral(10);
-      break;
-    case 1:
-      digit = integral;
-      integral = 0;
-      break;
-    default:
-      FMT_ASSERT(false, "invalid number of digits");
-    }
-    --exp;
-    auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
-    auto result = handler.on_digit(static_cast<char>('0' + digit),
-                                   data::power_of_10_64[exp] << -one.e,
-                                   remainder, error, true);
-    if (result != digits::more) return result;
-  } while (exp > 0);
-  // Generate digits for the fractional part.
-  for (;;) {
-    fractional *= 10;
-    error *= 10;
-    char digit = static_cast<char>('0' + (fractional >> -one.e));
-    fractional &= one.f - 1;
-    --exp;
-    auto result = handler.on_digit(digit, one.f, fractional, error, false);
-    if (result != digits::more) return result;
-  }
-}
-
 class bigint {
  private:
   // A bigint is stored as an array of bigits (big digits), with bigit at index
@@ -2804,7 +2903,7 @@
     auto size = other.bigits_.size();
     bigits_.resize(size);
     auto data = other.bigits_.data();
-    std::copy(data, data + size, make_checked(bigits_.data(), size));
+    copy_str<bigit>(data, data + size, bigits_.data());
     exp_ = other.exp_;
   }
 
@@ -3018,6 +3117,7 @@
   }
   int even = static_cast<int>((value.f & 1) == 0);
   if (!upper) upper = &lower;
+  bool shortest = num_digits < 0;
   if ((flags & dragon::fixup) != 0) {
     if (add_compare(numerator, *upper, denominator) + even <= 0) {
       --exp10;
@@ -3030,7 +3130,7 @@
     if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1);
   }
   // Invariant: value == (numerator / denominator) * pow(10, exp10).
-  if (num_digits < 0) {
+  if (shortest) {
     // Generate the shortest representation.
     num_digits = 0;
     char* data = buf.data();
@@ -3060,7 +3160,7 @@
   }
   // Generate the given number of digits.
   exp10 -= num_digits - 1;
-  if (num_digits == 0) {
+  if (num_digits <= 0) {
     denominator *= 10;
     auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
     buf.push_back(digit);
@@ -3085,7 +3185,8 @@
       }
       if (buf[0] == overflow) {
         buf[0] = '1';
-        ++exp10;
+        if ((flags & dragon::fixed) != 0) buf.push_back('0');
+        else ++exp10;
       }
       return;
     }
@@ -3094,6 +3195,94 @@
   buf[num_digits - 1] = static_cast<char>('0' + digit);
 }
 
+// Formats a floating-point number using the hexfloat format.
+template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
+FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
+                                     float_specs specs, buffer<char>& buf) {
+  // float is passed as double to reduce the number of instantiations and to
+  // simplify implementation.
+  static_assert(!std::is_same<Float, float>::value, "");
+
+  using info = dragonbox::float_info<Float>;
+
+  // Assume Float is in the format [sign][exponent][significand].
+  using carrier_uint = typename info::carrier_uint;
+
+  constexpr auto num_float_significand_bits =
+      detail::num_significand_bits<Float>();
+
+  basic_fp<carrier_uint> f(value);
+  f.e += num_float_significand_bits;
+  if (!has_implicit_bit<Float>()) --f.e;
+
+  constexpr auto num_fraction_bits =
+      num_float_significand_bits + (has_implicit_bit<Float>() ? 1 : 0);
+  constexpr auto num_xdigits = (num_fraction_bits + 3) / 4;
+
+  constexpr auto leading_shift = ((num_xdigits - 1) * 4);
+  const auto leading_mask = carrier_uint(0xF) << leading_shift;
+  const auto leading_xdigit =
+      static_cast<uint32_t>((f.f & leading_mask) >> leading_shift);
+  if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1);
+
+  int print_xdigits = num_xdigits - 1;
+  if (precision >= 0 && print_xdigits > precision) {
+    const int shift = ((print_xdigits - precision - 1) * 4);
+    const auto mask = carrier_uint(0xF) << shift;
+    const auto v = static_cast<uint32_t>((f.f & mask) >> shift);
+
+    if (v >= 8) {
+      const auto inc = carrier_uint(1) << (shift + 4);
+      f.f += inc;
+      f.f &= ~(inc - 1);
+    }
+
+    // Check long double overflow
+    if (!has_implicit_bit<Float>()) {
+      const auto implicit_bit = carrier_uint(1) << num_float_significand_bits;
+      if ((f.f & implicit_bit) == implicit_bit) {
+        f.f >>= 4;
+        f.e += 4;
+      }
+    }
+
+    print_xdigits = precision;
+  }
+
+  char xdigits[num_bits<carrier_uint>() / 4];
+  detail::fill_n(xdigits, sizeof(xdigits), '0');
+  format_uint<4>(xdigits, f.f, num_xdigits, specs.upper);
+
+  // Remove zero tail
+  while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits;
+
+  buf.push_back('0');
+  buf.push_back(specs.upper ? 'X' : 'x');
+  buf.push_back(xdigits[0]);
+  if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision)
+    buf.push_back('.');
+  buf.append(xdigits + 1, xdigits + 1 + print_xdigits);
+  for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0');
+
+  buf.push_back(specs.upper ? 'P' : 'p');
+
+  uint32_t abs_e;
+  if (f.e < 0) {
+    buf.push_back('-');
+    abs_e = static_cast<uint32_t>(-f.e);
+  } else {
+    buf.push_back('+');
+    abs_e = static_cast<uint32_t>(f.e);
+  }
+  format_decimal<char>(appender(buf), abs_e, detail::count_digits(abs_e));
+}
+
+template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
+FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
+                                     float_specs specs, buffer<char>& buf) {
+  format_hexfloat(static_cast<double>(value), precision, specs, buf);
+}
+
 template <typename Float>
 FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
                                   buffer<char>& buf) -> int {
@@ -3116,7 +3305,7 @@
   int exp = 0;
   bool use_dragon = true;
   unsigned dragon_flags = 0;
-  if (!is_fast_float<Float>()) {
+  if (!is_fast_float<Float>() || is_constant_evaluated()) {
     const auto inv_log2_10 = 0.3010299956639812;  // 1 / log2(10)
     using info = dragonbox::float_info<decltype(converted_value)>;
     const auto f = basic_fp<typename info::carrier_uint>(converted_value);
@@ -3124,10 +3313,11 @@
     //   10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
     // This is based on log10(value) == log2(value) / log2(10) and approximation
     // of log2(value) by e + num_fraction_bits idea from double-conversion.
-    exp = static_cast<int>(
-        std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10));
+    auto e = (f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10;
+    exp = static_cast<int>(e);
+    if (e > exp) ++exp;  // Compute ceil.
     dragon_flags = dragon::fixup;
-  } else if (!is_constant_evaluated() && precision < 0) {
+  } else if (precision < 0) {
     // Use Dragonbox for the shortest format.
     if (specs.binary32) {
       auto dec = dragonbox::to_decimal(static_cast<float>(value));
@@ -3138,23 +3328,244 @@
     write<char>(buffer_appender<char>(buf), dec.significand);
     return dec.exponent;
   } else {
-    // Use Grisu + Dragon4 for the given precision:
-    // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
-    const int min_exp = -60;  // alpha in Grisu.
-    int cached_exp10 = 0;     // K in Grisu.
-    fp normalized = normalize(fp(converted_value));
-    const auto cached_pow = get_cached_power(
-        min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
-    normalized = normalized * cached_pow;
-    gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
-    if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error &&
-        !is_constant_evaluated()) {
-      exp += handler.exp10;
-      buf.try_resize(to_unsigned(handler.size));
-      use_dragon = false;
+    // Extract significand bits and exponent bits.
+    using info = dragonbox::float_info<double>;
+    auto br = bit_cast<uint64_t>(static_cast<double>(value));
+
+    const uint64_t significand_mask =
+        (static_cast<uint64_t>(1) << num_significand_bits<double>()) - 1;
+    uint64_t significand = (br & significand_mask);
+    int exponent = static_cast<int>((br & exponent_mask<double>()) >>
+                                    num_significand_bits<double>());
+
+    if (exponent != 0) {  // Check if normal.
+      exponent -= exponent_bias<double>() + num_significand_bits<double>();
+      significand |=
+          (static_cast<uint64_t>(1) << num_significand_bits<double>());
+      significand <<= 1;
     } else {
-      exp += handler.size - cached_exp10 - 1;
-      precision = handler.precision;
+      // Normalize subnormal inputs.
+      FMT_ASSERT(significand != 0, "zeros should not appear here");
+      int shift = countl_zero(significand);
+      FMT_ASSERT(shift >= num_bits<uint64_t>() - num_significand_bits<double>(),
+                 "");
+      shift -= (num_bits<uint64_t>() - num_significand_bits<double>() - 2);
+      exponent = (std::numeric_limits<double>::min_exponent -
+                  num_significand_bits<double>()) -
+                 shift;
+      significand <<= shift;
+    }
+
+    // Compute the first several nonzero decimal significand digits.
+    // We call the number we get the first segment.
+    const int k = info::kappa - dragonbox::floor_log10_pow2(exponent);
+    exp = -k;
+    const int beta = exponent + dragonbox::floor_log2_pow10(k);
+    uint64_t first_segment;
+    bool has_more_segments;
+    int digits_in_the_first_segment;
+    {
+      const auto r = dragonbox::umul192_upper128(
+          significand << beta, dragonbox::get_cached_power(k));
+      first_segment = r.high();
+      has_more_segments = r.low() != 0;
+
+      // The first segment can have 18 ~ 19 digits.
+      if (first_segment >= 1000000000000000000ULL) {
+        digits_in_the_first_segment = 19;
+      } else {
+        // When it is of 18-digits, we align it to 19-digits by adding a bogus
+        // zero at the end.
+        digits_in_the_first_segment = 18;
+        first_segment *= 10;
+      }
+    }
+
+    // Compute the actual number of decimal digits to print.
+    if (fixed) adjust_precision(precision, exp + digits_in_the_first_segment);
+
+    // Use Dragon4 only when there might be not enough digits in the first
+    // segment.
+    if (digits_in_the_first_segment > precision) {
+      use_dragon = false;
+
+      if (precision <= 0) {
+        exp += digits_in_the_first_segment;
+
+        if (precision < 0) {
+          // Nothing to do, since all we have are just leading zeros.
+          buf.try_resize(0);
+        } else {
+          // We may need to round-up.
+          buf.try_resize(1);
+          if ((first_segment | static_cast<uint64_t>(has_more_segments)) >
+              5000000000000000000ULL) {
+            buf[0] = '1';
+          } else {
+            buf[0] = '0';
+          }
+        }
+      }  // precision <= 0
+      else {
+        exp += digits_in_the_first_segment - precision;
+
+        // When precision > 0, we divide the first segment into three
+        // subsegments, each with 9, 9, and 0 ~ 1 digits so that each fits
+        // in 32-bits which usually allows faster calculation than in
+        // 64-bits. Since some compiler (e.g. MSVC) doesn't know how to optimize
+        // division-by-constant for large 64-bit divisors, we do it here
+        // manually. The magic number 7922816251426433760 below is equal to
+        // ceil(2^(64+32) / 10^10).
+        const uint32_t first_subsegment = static_cast<uint32_t>(
+            dragonbox::umul128_upper64(first_segment, 7922816251426433760ULL) >>
+            32);
+        const uint64_t second_third_subsegments =
+            first_segment - first_subsegment * 10000000000ULL;
+
+        uint64_t prod;
+        uint32_t digits;
+        bool should_round_up;
+        int number_of_digits_to_print = precision > 9 ? 9 : precision;
+
+        // Print a 9-digits subsegment, either the first or the second.
+        auto print_subsegment = [&](uint32_t subsegment, char* buffer) {
+          int number_of_digits_printed = 0;
+
+          // If we want to print an odd number of digits from the subsegment,
+          if ((number_of_digits_to_print & 1) != 0) {
+            // Convert to 64-bit fixed-point fractional form with 1-digit
+            // integer part. The magic number 720575941 is a good enough
+            // approximation of 2^(32 + 24) / 10^8; see
+            // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case
+            // for details.
+            prod = ((subsegment * static_cast<uint64_t>(720575941)) >> 24) + 1;
+            digits = static_cast<uint32_t>(prod >> 32);
+            *buffer = static_cast<char>('0' + digits);
+            number_of_digits_printed++;
+          }
+          // If we want to print an even number of digits from the
+          // first_subsegment,
+          else {
+            // Convert to 64-bit fixed-point fractional form with 2-digits
+            // integer part. The magic number 450359963 is a good enough
+            // approximation of 2^(32 + 20) / 10^7; see
+            // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case
+            // for details.
+            prod = ((subsegment * static_cast<uint64_t>(450359963)) >> 20) + 1;
+            digits = static_cast<uint32_t>(prod >> 32);
+            copy2(buffer, digits2(digits));
+            number_of_digits_printed += 2;
+          }
+
+          // Print all digit pairs.
+          while (number_of_digits_printed < number_of_digits_to_print) {
+            prod = static_cast<uint32_t>(prod) * static_cast<uint64_t>(100);
+            digits = static_cast<uint32_t>(prod >> 32);
+            copy2(buffer + number_of_digits_printed, digits2(digits));
+            number_of_digits_printed += 2;
+          }
+        };
+
+        // Print first subsegment.
+        print_subsegment(first_subsegment, buf.data());
+
+        // Perform rounding if the first subsegment is the last subsegment to
+        // print.
+        if (precision <= 9) {
+          // Rounding inside the subsegment.
+          // We round-up if:
+          //  - either the fractional part is strictly larger than 1/2, or
+          //  - the fractional part is exactly 1/2 and the last digit is odd.
+          // We rely on the following observations:
+          //  - If fractional_part >= threshold, then the fractional part is
+          //    strictly larger than 1/2.
+          //  - If the MSB of fractional_part is set, then the fractional part
+          //    must be at least 1/2.
+          //  - When the MSB of fractional_part is set, either
+          //    second_third_subsegments being nonzero or has_more_segments
+          //    being true means there are further digits not printed, so the
+          //    fractional part is strictly larger than 1/2.
+          if (precision < 9) {
+            uint32_t fractional_part = static_cast<uint32_t>(prod);
+            should_round_up = fractional_part >=
+                                  data::fractional_part_rounding_thresholds
+                                      [8 - number_of_digits_to_print] ||
+                              ((fractional_part >> 31) &
+                               ((digits & 1) | (second_third_subsegments != 0) |
+                                has_more_segments)) != 0;
+          }
+          // Rounding at the subsegment boundary.
+          // In this case, the fractional part is at least 1/2 if and only if
+          // second_third_subsegments >= 5000000000ULL, and is strictly larger
+          // than 1/2 if we further have either second_third_subsegments >
+          // 5000000000ULL or has_more_segments == true.
+          else {
+            should_round_up = second_third_subsegments > 5000000000ULL ||
+                              (second_third_subsegments == 5000000000ULL &&
+                               ((digits & 1) != 0 || has_more_segments));
+          }
+        }
+        // Otherwise, print the second subsegment.
+        else {
+          // Compilers are not aware of how to leverage the maximum value of
+          // second_third_subsegments to find out a better magic number which
+          // allows us to eliminate an additional shift. 1844674407370955162 =
+          // ceil(2^64/10) < ceil(2^64*(10^9/(10^10 - 1))).
+          const uint32_t second_subsegment =
+              static_cast<uint32_t>(dragonbox::umul128_upper64(
+                  second_third_subsegments, 1844674407370955162ULL));
+          const uint32_t third_subsegment =
+              static_cast<uint32_t>(second_third_subsegments) -
+              second_subsegment * 10;
+
+          number_of_digits_to_print = precision - 9;
+          print_subsegment(second_subsegment, buf.data() + 9);
+
+          // Rounding inside the subsegment.
+          if (precision < 18) {
+            // The condition third_subsegment != 0 implies that the segment was
+            // of 19 digits, so in this case the third segment should be
+            // consisting of a genuine digit from the input.
+            uint32_t fractional_part = static_cast<uint32_t>(prod);
+            should_round_up = fractional_part >=
+                                  data::fractional_part_rounding_thresholds
+                                      [8 - number_of_digits_to_print] ||
+                              ((fractional_part >> 31) &
+                               ((digits & 1) | (third_subsegment != 0) |
+                                has_more_segments)) != 0;
+          }
+          // Rounding at the subsegment boundary.
+          else {
+            // In this case, the segment must be of 19 digits, thus
+            // the third subsegment should be consisting of a genuine digit from
+            // the input.
+            should_round_up = third_subsegment > 5 ||
+                              (third_subsegment == 5 &&
+                               ((digits & 1) != 0 || has_more_segments));
+          }
+        }
+
+        // Round-up if necessary.
+        if (should_round_up) {
+          ++buf[precision - 1];
+          for (int i = precision - 1; i > 0 && buf[i] > '9'; --i) {
+            buf[i] = '0';
+            ++buf[i - 1];
+          }
+          if (buf[0] > '9') {
+            buf[0] = '1';
+            if (fixed)
+              buf[precision++] = '0';
+            else
+              ++exp;
+          }
+        }
+        buf.try_resize(to_unsigned(precision));
+      }
+    }  // if (digits_in_the_first_segment > precision)
+    else {
+      // Adjust the exponent for its use in Dragon4.
+      exp += digits_in_the_first_segment - 1;
     }
   }
   if (use_dragon) {
@@ -3181,13 +3592,10 @@
   }
   return exp;
 }
-
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(is_floating_point<T>::value)>
-FMT_CONSTEXPR20 auto write(OutputIt out, T value,
-                           basic_format_specs<Char> specs, locale_ref loc = {})
+template <typename Char, typename OutputIt, typename T>
+FMT_CONSTEXPR20 auto write_float(OutputIt out, T value,
+                                 format_specs<Char> specs, locale_ref loc)
     -> OutputIt {
-  if (const_check(!is_supported_floating_point(value))) return out;
   float_specs fspecs = parse_float_type_spec(specs);
   fspecs.sign = specs.sign;
   if (detail::signbit(value)) {  // value < 0 is false for NaN so use signbit.
@@ -3211,7 +3619,7 @@
   memory_buffer buffer;
   if (fspecs.format == float_format::hex) {
     if (fspecs.sign) buffer.push_back(detail::sign<char>(fspecs.sign));
-    snprintf_float(convert_float(value), specs.precision, fspecs, buffer);
+    format_hexfloat(convert_float(value), specs.precision, fspecs, buffer);
     return write_bytes<align::right>(out, {buffer.data(), buffer.size()},
                                      specs);
   }
@@ -3234,10 +3642,19 @@
 }
 
 template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_floating_point<T>::value)>
+FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs<Char> specs,
+                           locale_ref loc = {}) -> OutputIt {
+  if (const_check(!is_supported_floating_point(value))) return out;
+  return specs.localized && write_loc(out, value, specs, loc)
+             ? out
+             : write_float(out, value, specs, loc);
+}
+
+template <typename Char, typename OutputIt, typename T,
           FMT_ENABLE_IF(is_fast_float<T>::value)>
 FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
-  if (is_constant_evaluated())
-    return write(out, value, basic_format_specs<Char>());
+  if (is_constant_evaluated()) return write(out, value, format_specs<Char>());
   if (const_check(!is_supported_floating_point(value))) return out;
 
   auto fspecs = float_specs();
@@ -3246,11 +3663,11 @@
     value = -value;
   }
 
-  constexpr auto specs = basic_format_specs<Char>();
+  constexpr auto specs = format_specs<Char>();
   using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
-  using uint = typename dragonbox::float_info<floaty>::carrier_uint;
-  uint mask = exponent_mask<floaty>();
-  if ((bit_cast<uint>(value) & mask) == mask)
+  using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint;
+  floaty_uint mask = exponent_mask<floaty>();
+  if ((bit_cast<floaty_uint>(value) & mask) == mask)
     return write_nonfinite(out, std::isnan(value), specs, fspecs);
 
   auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
@@ -3261,12 +3678,12 @@
           FMT_ENABLE_IF(is_floating_point<T>::value &&
                         !is_fast_float<T>::value)>
 inline auto write(OutputIt out, T value) -> OutputIt {
-  return write(out, value, basic_format_specs<Char>());
+  return write(out, value, format_specs<Char>());
 }
 
 template <typename Char, typename OutputIt>
-auto write(OutputIt out, monostate, basic_format_specs<Char> = {},
-           locale_ref = {}) -> OutputIt {
+auto write(OutputIt out, monostate, format_specs<Char> = {}, locale_ref = {})
+    -> OutputIt {
   FMT_ASSERT(false, "");
   return out;
 }
@@ -3300,8 +3717,8 @@
 template <typename Char, typename OutputIt, typename T,
           FMT_ENABLE_IF(std::is_same<T, bool>::value)>
 FMT_CONSTEXPR auto write(OutputIt out, T value,
-                         const basic_format_specs<Char>& specs = {},
-                         locale_ref = {}) -> OutputIt {
+                         const format_specs<Char>& specs = {}, locale_ref = {})
+    -> OutputIt {
   return specs.type != presentation_type::none &&
                  specs.type != presentation_type::string
              ? write(out, value ? 1 : 0, specs, {})
@@ -3318,20 +3735,15 @@
 template <typename Char, typename OutputIt>
 FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value)
     -> OutputIt {
-  if (!value) {
-    throw_format_error("string pointer is null");
-  } else {
-    out = write(out, basic_string_view<Char>(value));
-  }
+  if (value) return write(out, basic_string_view<Char>(value));
+  throw_format_error("string pointer is null");
   return out;
 }
 
 template <typename Char, typename OutputIt, typename T,
           FMT_ENABLE_IF(std::is_same<T, void>::value)>
-auto write(OutputIt out, const T* value,
-           const basic_format_specs<Char>& specs = {}, locale_ref = {})
-    -> OutputIt {
-  check_pointer_type_spec(specs.type, error_handler());
+auto write(OutputIt out, const T* value, const format_specs<Char>& specs = {},
+           locale_ref = {}) -> OutputIt {
   return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
 }
 
@@ -3341,8 +3753,8 @@
 FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
     std::is_class<T>::value && !is_string<T>::value &&
         !is_floating_point<T>::value && !std::is_same<T, Char>::value &&
-        !std::is_same<const T&,
-                      decltype(arg_mapper<Context>().map(value))>::value,
+        !std::is_same<T, remove_cvref_t<decltype(arg_mapper<Context>().map(
+                             value))>>::value,
     OutputIt> {
   return write<Char>(out, arg_mapper<Context>().map(value));
 }
@@ -3352,12 +3764,8 @@
 FMT_CONSTEXPR auto write(OutputIt out, const T& value)
     -> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type,
                    OutputIt> {
-  using formatter_type =
-      conditional_t<has_formatter<T, Context>::value,
-                    typename Context::template formatter_type<T>,
-                    fallback_formatter<T, Char>>;
   auto ctx = Context(out, {}, {});
-  return formatter_type().format(value, ctx);
+  return typename Context::template formatter_type<T>().format(value, ctx);
 }
 
 // An argument visitor that formats the argument and writes it via the output
@@ -3386,7 +3794,7 @@
   using context = buffer_context<Char>;
 
   iterator out;
-  const basic_format_specs<Char>& specs;
+  const format_specs<Char>& specs;
   locale_ref locale;
 
   template <typename T>
@@ -3411,12 +3819,6 @@
   template <typename T> void operator()(T) const {}
 };
 
-template <typename T>
-using is_integer =
-    bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
-                  !std::is_same<T, char>::value &&
-                  !std::is_same<T, wchar_t>::value>;
-
 template <typename ErrorHandler> class width_checker {
  public:
   explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
@@ -3466,55 +3868,12 @@
 }
 
 template <typename Context, typename ID>
-FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) ->
-    typename Context::format_arg {
+FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) {
   auto arg = ctx.arg(id);
   if (!arg) ctx.on_error("argument not found");
   return arg;
 }
 
-// The standard format specifier handler with checking.
-template <typename Char> class specs_handler : public specs_setter<Char> {
- private:
-  basic_format_parse_context<Char>& parse_context_;
-  buffer_context<Char>& context_;
-
-  // This is only needed for compatibility with gcc 4.4.
-  using format_arg = basic_format_arg<buffer_context<Char>>;
-
-  FMT_CONSTEXPR auto get_arg(auto_id) -> format_arg {
-    return detail::get_arg(context_, parse_context_.next_arg_id());
-  }
-
-  FMT_CONSTEXPR auto get_arg(int arg_id) -> format_arg {
-    parse_context_.check_arg_id(arg_id);
-    return detail::get_arg(context_, arg_id);
-  }
-
-  FMT_CONSTEXPR auto get_arg(basic_string_view<Char> arg_id) -> format_arg {
-    parse_context_.check_arg_id(arg_id);
-    return detail::get_arg(context_, arg_id);
-  }
-
- public:
-  FMT_CONSTEXPR specs_handler(basic_format_specs<Char>& specs,
-                              basic_format_parse_context<Char>& parse_ctx,
-                              buffer_context<Char>& ctx)
-      : specs_setter<Char>(specs), parse_context_(parse_ctx), context_(ctx) {}
-
-  template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
-    this->specs_.width = get_dynamic_spec<width_checker>(
-        get_arg(arg_id), context_.error_handler());
-  }
-
-  template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
-    this->specs_.precision = get_dynamic_spec<precision_checker>(
-        get_arg(arg_id), context_.error_handler());
-  }
-
-  void on_error(const char* message) { context_.on_error(message); }
-};
-
 template <template <typename> class Handler, typename Context>
 FMT_CONSTEXPR void handle_dynamic_spec(int& value,
                                        arg_ref<typename Context::char_type> ref,
@@ -3523,26 +3882,17 @@
   case arg_id_kind::none:
     break;
   case arg_id_kind::index:
-    value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
+    value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index),
                                               ctx.error_handler());
     break;
   case arg_id_kind::name:
-    value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
+    value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name),
                                               ctx.error_handler());
     break;
   }
 }
 
 #if FMT_USE_USER_DEFINED_LITERALS
-template <typename Char> struct udl_formatter {
-  basic_string_view<Char> str;
-
-  template <typename... T>
-  auto operator()(T&&... args) const -> std::basic_string<Char> {
-    return vformat(str, fmt::make_format_args<buffer_context<Char>>(args...));
-  }
-};
-
 #  if FMT_USE_NONTYPE_TEMPLATE_ARGS
 template <typename T, typename Char, size_t N,
           fmt::detail_exported::fixed_string<Char, N> Str>
@@ -3581,12 +3931,12 @@
 #endif  // FMT_USE_USER_DEFINED_LITERALS
 
 template <typename Locale, typename Char>
-auto vformat(const Locale& loc, basic_string_view<Char> format_str,
+auto vformat(const Locale& loc, basic_string_view<Char> fmt,
              basic_format_args<buffer_context<type_identity_t<Char>>> args)
     -> std::basic_string<Char> {
-  basic_memory_buffer<Char> buffer;
-  detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
-  return {buffer.data(), buffer.size()};
+  auto buf = basic_memory_buffer<Char>();
+  detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
+  return {buf.data(), buf.size()};
 }
 
 using format_func = void (*)(detail::buffer<char>&, int, const char*);
@@ -3596,28 +3946,28 @@
 
 FMT_API void report_error(format_func func, int error_code,
                           const char* message) noexcept;
-FMT_END_DETAIL_NAMESPACE
+}  // namespace detail
 
 FMT_API auto vsystem_error(int error_code, string_view format_str,
                            format_args args) -> std::system_error;
 
 /**
- \rst
- Constructs :class:`std::system_error` with a message formatted with
- ``fmt::format(fmt, args...)``.
+  \rst
+  Constructs :class:`std::system_error` with a message formatted with
+  ``fmt::format(fmt, args...)``.
   *error_code* is a system error code as given by ``errno``.
 
- **Example**::
+  **Example**::
 
-   // This throws std::system_error with the description
-   //   cannot open file 'madeup': No such file or directory
-   // or similar (system message may vary).
-   const char* filename = "madeup";
-   std::FILE* file = std::fopen(filename, "r");
-   if (!file)
-     throw fmt::system_error(errno, "cannot open file '{}'", filename);
- \endrst
-*/
+    // This throws std::system_error with the description
+    //   cannot open file 'madeup': No such file or directory
+    // or similar (system message may vary).
+    const char* filename = "madeup";
+    std::FILE* file = std::fopen(filename, "r");
+    if (!file)
+      throw fmt::system_error(errno, "cannot open file '{}'", filename);
+  \endrst
+ */
 template <typename... T>
 auto system_error(int error_code, format_string<T...> fmt, T&&... args)
     -> std::system_error {
@@ -3708,93 +4058,35 @@
 };
 
 template <typename T, typename Char>
-template <typename FormatContext>
-FMT_CONSTEXPR FMT_INLINE auto
-formatter<T, Char,
-          enable_if_t<detail::type_constant<T, Char>::value !=
-                      detail::type::custom_type>>::format(const T& val,
-                                                          FormatContext& ctx)
-    const -> decltype(ctx.out()) {
-  if (specs_.width_ref.kind != detail::arg_id_kind::none ||
-      specs_.precision_ref.kind != detail::arg_id_kind::none) {
-    auto specs = specs_;
-    detail::handle_dynamic_spec<detail::width_checker>(specs.width,
-                                                       specs.width_ref, ctx);
-    detail::handle_dynamic_spec<detail::precision_checker>(
-        specs.precision, specs.precision_ref, ctx);
-    return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
-  }
-  return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
-}
+struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
+    : private formatter<detail::format_as_t<T>, Char> {
+  using base = formatter<detail::format_as_t<T>, Char>;
+  using base::parse;
 
-template <typename Char>
-struct formatter<void*, Char> : formatter<const void*, Char> {
   template <typename FormatContext>
-  auto format(void* val, FormatContext& ctx) const -> decltype(ctx.out()) {
-    return formatter<const void*, Char>::format(val, ctx);
+  auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
+    return base::format(format_as(value), ctx);
   }
 };
 
+#define FMT_FORMAT_AS(Type, Base) \
+  template <typename Char>        \
+  struct formatter<Type, Char> : formatter<Base, Char> {}
+
+FMT_FORMAT_AS(signed char, int);
+FMT_FORMAT_AS(unsigned char, unsigned);
+FMT_FORMAT_AS(short, int);
+FMT_FORMAT_AS(unsigned short, unsigned);
+FMT_FORMAT_AS(long, detail::long_type);
+FMT_FORMAT_AS(unsigned long, detail::ulong_type);
+FMT_FORMAT_AS(Char*, const Char*);
+FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(std::nullptr_t, const void*);
+FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(void*, const void*);
+
 template <typename Char, size_t N>
-struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
-  template <typename FormatContext>
-  FMT_CONSTEXPR auto format(const Char* val, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    return formatter<basic_string_view<Char>, Char>::format(val, ctx);
-  }
-};
-
-// A formatter for types known only at run time such as variant alternatives.
-//
-// Usage:
-//   using variant = std::variant<int, std::string>;
-//   template <>
-//   struct formatter<variant>: dynamic_formatter<> {
-//     auto format(const variant& v, format_context& ctx) {
-//       return visit([&](const auto& val) {
-//           return dynamic_formatter<>::format(val, ctx);
-//       }, v);
-//     }
-//   };
-template <typename Char = char> class dynamic_formatter {
- private:
-  detail::dynamic_format_specs<Char> specs_;
-  const Char* format_str_;
-
-  struct null_handler : detail::error_handler {
-    void on_align(align_t) {}
-    void on_sign(sign_t) {}
-    void on_hash() {}
-  };
-
-  template <typename Context> void handle_specs(Context& ctx) {
-    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
-                                                       specs_.width_ref, ctx);
-    detail::handle_dynamic_spec<detail::precision_checker>(
-        specs_.precision, specs_.precision_ref, ctx);
-  }
-
- public:
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    format_str_ = ctx.begin();
-    // Checks are deferred to formatting time when the argument type is known.
-    detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
-    return detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
-  }
-
-  template <typename T, typename FormatContext>
-  auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
-    handle_specs(ctx);
-    detail::specs_checker<null_handler> checker(
-        null_handler(), detail::mapped_type_constant<T, FormatContext>::value);
-    checker.on_align(specs_.align);
-    if (specs_.sign != sign::none) checker.on_sign(specs_.sign);
-    if (specs_.alt) checker.on_hash();
-    if (specs_.precision >= 0) checker.end_precision();
-    return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
-  }
-};
+struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {};
 
 /**
   \rst
@@ -3809,7 +4101,8 @@
   static_assert(std::is_pointer<T>::value, "");
   return detail::bit_cast<const void*>(p);
 }
-template <typename T> auto ptr(const std::unique_ptr<T>& p) -> const void* {
+template <typename T, typename Deleter>
+auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
   return p.get();
 }
 template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
@@ -3849,17 +4142,13 @@
 
 template <> struct formatter<bytes> {
  private:
-  detail::dynamic_format_specs<char> specs_;
+  detail::dynamic_format_specs<> specs_;
 
  public:
   template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    using handler_type = detail::dynamic_specs_handler<ParseContext>;
-    detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
-                                                detail::type::string_type);
-    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
-    detail::check_string_type_spec(specs_.type, ctx.error_handler());
-    return it;
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
+    return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
+                              detail::type::string_type);
   }
 
   template <typename FormatContext>
@@ -3873,7 +4162,9 @@
 };
 
 // group_digits_view is not derived from view because it copies the argument.
-template <typename T> struct group_digits_view { T value; };
+template <typename T> struct group_digits_view {
+  T value;
+};
 
 /**
   \rst
@@ -3892,17 +4183,13 @@
 
 template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
  private:
-  detail::dynamic_format_specs<char> specs_;
+  detail::dynamic_format_specs<> specs_;
 
  public:
   template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    using handler_type = detail::dynamic_specs_handler<ParseContext>;
-    detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
-                                                detail::type::int_type);
-    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
-    detail::check_string_type_spec(specs_.type, ctx.error_handler());
-    return it;
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
+    return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
+                              detail::type::int_type);
   }
 
   template <typename FormatContext>
@@ -3912,12 +4199,13 @@
                                                        specs_.width_ref, ctx);
     detail::handle_dynamic_spec<detail::precision_checker>(
         specs_.precision, specs_.precision_ref, ctx);
-    return detail::write_int_localized(
+    return detail::write_int(
         ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
-        detail::digit_grouping<char>({"\3", ','}));
+        detail::digit_grouping<char>("\3", ","));
   }
 };
 
+// DEPRECATED! join_view will be moved to ranges.h.
 template <typename It, typename Sentinel, typename Char = char>
 struct join_view : detail::view {
   It begin;
@@ -3937,30 +4225,11 @@
 #else
       typename std::iterator_traits<It>::value_type;
 #endif
-  using context = buffer_context<Char>;
-  using mapper = detail::arg_mapper<context>;
-
-  template <typename T, FMT_ENABLE_IF(has_formatter<T, context>::value)>
-  static auto map(const T& value) -> const T& {
-    return value;
-  }
-  template <typename T, FMT_ENABLE_IF(!has_formatter<T, context>::value)>
-  static auto map(const T& value) -> decltype(mapper().map(value)) {
-    return mapper().map(value);
-  }
-
-  using formatter_type =
-      conditional_t<is_formattable<value_type, Char>::value,
-                    formatter<remove_cvref_t<decltype(map(
-                                  std::declval<const value_type&>()))>,
-                              Char>,
-                    detail::fallback_formatter<value_type, Char>>;
-
-  formatter_type value_formatter_;
+  formatter<remove_cvref_t<value_type>, Char> value_formatter_;
 
  public:
   template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
     return value_formatter_.parse(ctx);
   }
 
@@ -3970,12 +4239,12 @@
     auto it = value.begin;
     auto out = ctx.out();
     if (it != value.end) {
-      out = value_formatter_.format(map(*it), ctx);
+      out = value_formatter_.format(*it, ctx);
       ++it;
       while (it != value.end) {
         out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
         ctx.advance_to(out);
-        out = value_formatter_.format(map(*it), ctx);
+        out = value_formatter_.format(*it, ctx);
         ++it;
       }
     }
@@ -4025,11 +4294,12 @@
     std::string answer = fmt::to_string(42);
   \endrst
  */
-template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
+                                    !detail::has_format_as<T>::value)>
 inline auto to_string(const T& value) -> std::string {
-  auto result = std::string();
-  detail::write<char>(std::back_inserter(result), value);
-  return result;
+  auto buffer = memory_buffer();
+  detail::write<char>(appender(buffer), value);
+  return {buffer.data(), buffer.size()};
 }
 
 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
@@ -4050,27 +4320,19 @@
   return std::basic_string<Char>(buf.data(), size);
 }
 
-FMT_BEGIN_DETAIL_NAMESPACE
+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
+                                    detail::has_format_as<T>::value)>
+inline auto to_string(const T& value) -> std::string {
+  return to_string(format_as(value));
+}
+
+FMT_END_EXPORT
+
+namespace detail {
 
 template <typename Char>
-void vformat_to(
-    buffer<Char>& buf, basic_string_view<Char> fmt,
-    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
-    locale_ref loc) {
-  // workaround for msvc bug regarding name-lookup in module
-  // link names into function scope
-  using detail::arg_formatter;
-  using detail::buffer_appender;
-  using detail::custom_formatter;
-  using detail::default_arg_formatter;
-  using detail::get_arg;
-  using detail::locale_ref;
-  using detail::parse_format_specs;
-  using detail::specs_checker;
-  using detail::specs_handler;
-  using detail::to_unsigned;
-  using detail::type;
-  using detail::write;
+void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
+                typename vformat_args<Char>::type args, locale_ref loc) {
   auto out = buffer_appender<Char>(buf);
   if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
     auto arg = args.get(0);
@@ -4117,15 +4379,16 @@
         -> const Char* {
       auto arg = get_arg(context, id);
       if (arg.type() == type::custom_type) {
-        parse_context.advance_to(parse_context.begin() +
-                                 (begin - &*parse_context.begin()));
+        parse_context.advance_to(begin);
         visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
         return parse_context.begin();
       }
-      auto specs = basic_format_specs<Char>();
-      specs_checker<specs_handler<Char>> handler(
-          specs_handler<Char>(specs, parse_context, context), arg.type());
-      begin = parse_format_specs(begin, end, handler);
+      auto specs = detail::dynamic_format_specs<Char>();
+      begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
+      detail::handle_dynamic_spec<detail::width_checker>(
+          specs.width, specs.width_ref, context);
+      detail::handle_dynamic_spec<detail::precision_checker>(
+          specs.precision, specs.precision_ref, context);
       if (begin == end || *begin != '}')
         on_error("missing '}' in format string");
       auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
@@ -4136,7 +4399,12 @@
   detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
 }
 
+FMT_BEGIN_EXPORT
+
 #ifndef FMT_HEADER_ONLY
+extern template FMT_API void vformat_to(buffer<char>&, string_view,
+                                        typename vformat_args<>::type,
+                                        locale_ref);
 extern template FMT_API auto thousands_sep_impl<char>(locale_ref)
     -> thousands_sep_result<char>;
 extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref)
@@ -4145,7 +4413,7 @@
 extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
 #endif  // FMT_HEADER_ONLY
 
-FMT_END_DETAIL_NAMESPACE
+}  // namespace detail
 
 #if FMT_USE_USER_DEFINED_LITERALS
 inline namespace literals {
@@ -4182,7 +4450,7 @@
           FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
 inline auto format(const Locale& loc, format_string<T...> fmt, T&&... args)
     -> std::string {
-  return vformat(loc, string_view(fmt), fmt::make_format_args(args...));
+  return fmt::vformat(loc, string_view(fmt), fmt::make_format_args(args...));
 }
 
 template <typename OutputIt, typename Locale,
@@ -4193,7 +4461,7 @@
   using detail::get_buffer;
   auto&& buf = get_buffer<char>(out);
   detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
-  return detail::get_iterator(buf);
+  return detail::get_iterator(buf, out);
 }
 
 template <typename OutputIt, typename Locale, typename... T,
@@ -4204,7 +4472,39 @@
   return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
 }
 
-FMT_MODULE_EXPORT_END
+template <typename Locale, typename... T,
+          FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
+FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc,
+                                             format_string<T...> fmt,
+                                             T&&... args) -> size_t {
+  auto buf = detail::counting_buffer<>();
+  detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...),
+                           detail::locale_ref(loc));
+  return buf.count();
+}
+
+FMT_END_EXPORT
+
+template <typename T, typename Char>
+template <typename FormatContext>
+FMT_CONSTEXPR FMT_INLINE auto
+formatter<T, Char,
+          enable_if_t<detail::type_constant<T, Char>::value !=
+                      detail::type::custom_type>>::format(const T& val,
+                                                          FormatContext& ctx)
+    const -> decltype(ctx.out()) {
+  if (specs_.width_ref.kind != detail::arg_id_kind::none ||
+      specs_.precision_ref.kind != detail::arg_id_kind::none) {
+    auto specs = specs_;
+    detail::handle_dynamic_spec<detail::width_checker>(specs.width,
+                                                       specs.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
+        specs.precision, specs.precision_ref, ctx);
+    return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
+  }
+  return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
+}
+
 FMT_END_NAMESPACE
 
 #ifdef FMT_HEADER_ONLY
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/os.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/os.h
index d82be11..2126424 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/os.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/os.h
@@ -71,7 +71,7 @@
 #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
 
 FMT_BEGIN_NAMESPACE
-FMT_MODULE_EXPORT_BEGIN
+FMT_BEGIN_EXPORT
 
 /**
   \rst
@@ -120,51 +120,13 @@
 using cstring_view = basic_cstring_view<char>;
 using wcstring_view = basic_cstring_view<wchar_t>;
 
-template <typename Char> struct formatter<std::error_code, Char> {
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    return ctx.begin();
-  }
-
-  template <typename FormatContext>
-  FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    auto out = ctx.out();
-    out = detail::write_bytes(out, ec.category().name(),
-                              basic_format_specs<Char>());
-    out = detail::write<Char>(out, Char(':'));
-    out = detail::write<Char>(out, ec.value());
-    return out;
-  }
-};
-
 #ifdef _WIN32
 FMT_API const std::error_category& system_category() noexcept;
 
-FMT_BEGIN_DETAIL_NAMESPACE
-// A converter from UTF-16 to UTF-8.
-// It is only provided for Windows since other systems support UTF-8 natively.
-class utf16_to_utf8 {
- private:
-  memory_buffer buffer_;
-
- public:
-  utf16_to_utf8() {}
-  FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
-  operator string_view() const { return string_view(&buffer_[0], size()); }
-  size_t size() const { return buffer_.size() - 1; }
-  const char* c_str() const { return &buffer_[0]; }
-  std::string str() const { return std::string(&buffer_[0], size()); }
-
-  // Performs conversion returning a system error code instead of
-  // throwing exception on conversion error. This method may still throw
-  // in case of memory allocation error.
-  FMT_API int convert(basic_string_view<wchar_t> s);
-};
-
+namespace detail {
 FMT_API void format_windows_error(buffer<char>& out, int error_code,
                                   const char* message) noexcept;
-FMT_END_DETAIL_NAMESPACE
+}
 
 FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
                                          format_args args);
@@ -355,12 +317,18 @@
   // Creates a buffered_file object associated with this file and detaches
   // this file object from the file.
   buffered_file fdopen(const char* mode);
+
+#  if defined(_WIN32) && !defined(__MINGW32__)
+  // Opens a file and constructs a file object representing this file by
+  // wcstring_view filename. Windows only.
+  static file open_windows_file(wcstring_view path, int oflag);
+#  endif
 };
 
 // Returns the memory page size.
 long getpagesize();
 
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
 
 struct buffer_size {
   buffer_size() = default;
@@ -397,56 +365,61 @@
 #  endif
 };
 
-FMT_END_DETAIL_NAMESPACE
+class file_buffer final : public buffer<char> {
+  file file_;
+
+  FMT_API void grow(size_t) override;
+
+ public:
+  FMT_API file_buffer(cstring_view path, const ostream_params& params);
+  FMT_API file_buffer(file_buffer&& other);
+  FMT_API ~file_buffer();
+
+  void flush() {
+    if (size() == 0) return;
+    file_.write(data(), size() * sizeof(data()[0]));
+    clear();
+  }
+
+  void close() {
+    flush();
+    file_.close();
+  }
+};
+
+}  // namespace detail
 
 // Added {} below to work around default constructor error known to
 // occur in Xcode versions 7.2.1 and 8.2.1.
 constexpr detail::buffer_size buffer_size{};
 
 /** A fast output stream which is not thread-safe. */
-class FMT_API ostream final : private detail::buffer<char> {
+class FMT_API ostream {
  private:
-  file file_;
-
-  void grow(size_t) override;
+  FMT_MSC_WARNING(suppress : 4251)
+  detail::file_buffer buffer_;
 
   ostream(cstring_view path, const detail::ostream_params& params)
-      : file_(path, params.oflag) {
-    set(new char[params.buffer_size], params.buffer_size);
-  }
+      : buffer_(path, params) {}
 
  public:
-  ostream(ostream&& other)
-      : detail::buffer<char>(other.data(), other.size(), other.capacity()),
-        file_(std::move(other.file_)) {
-    other.clear();
-    other.set(nullptr, 0);
-  }
-  ~ostream() {
-    flush();
-    delete[] data();
-  }
+  ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
 
-  void flush() {
-    if (size() == 0) return;
-    file_.write(data(), size());
-    clear();
-  }
+  ~ostream();
+
+  void flush() { buffer_.flush(); }
 
   template <typename... T>
   friend ostream output_file(cstring_view path, T... params);
 
-  void close() {
-    flush();
-    file_.close();
-  }
+  void close() { buffer_.close(); }
 
   /**
     Formats ``args`` according to specifications in ``fmt`` and writes the
     output to the file.
    */
   template <typename... T> void print(format_string<T...> fmt, T&&... args) {
-    vformat_to(detail::buffer_appender<char>(*this), fmt,
+    vformat_to(detail::buffer_appender<char>(buffer_), fmt,
                fmt::make_format_args(args...));
   }
 };
@@ -472,7 +445,7 @@
 }
 #endif  // FMT_USE_FCNTL
 
-FMT_MODULE_EXPORT_END
+FMT_END_EXPORT
 FMT_END_NAMESPACE
 
 #endif  // FMT_OS_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/ostream.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/ostream.h
index c3cdd4a..a112fe7 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/ostream.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/ostream.h
@@ -8,8 +8,8 @@
 #ifndef FMT_OSTREAM_H_
 #define FMT_OSTREAM_H_
 
-#include <fstream>
-#include <ostream>
+#include <fstream>  // std::filebuf
+
 #if defined(_WIN32) && defined(__GLIBCXX__)
 #  include <ext/stdio_filebuf.h>
 #  include <ext/stdio_sync_filebuf.h>
@@ -21,48 +21,14 @@
 
 FMT_BEGIN_NAMESPACE
 
-template <typename OutputIt, typename Char> class basic_printf_context;
-
 namespace detail {
 
-// Checks if T has a user-defined operator<<.
-template <typename T, typename Char, typename Enable = void>
-class is_streamable {
- private:
-  template <typename U>
-  static auto test(int)
-      -> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
-                              << std::declval<U>()) != 0>;
-
-  template <typename> static auto test(...) -> std::false_type;
-
-  using result = decltype(test<T>(0));
-
- public:
-  is_streamable() = default;
-
-  static const bool value = result::value;
-};
-
-// Formatting of built-in types and arrays is intentionally disabled because
-// it's handled by standard (non-ostream) formatters.
-template <typename T, typename Char>
-struct is_streamable<
-    T, Char,
-    enable_if_t<
-        std::is_arithmetic<T>::value || std::is_array<T>::value ||
-        std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
-        std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
-        std::is_same<T, std_string_view<Char>>::value ||
-        (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
-    : std::false_type {};
-
 // Generate a unique explicit instantion in every translation unit using a tag
 // type in an anonymous namespace.
 namespace {
 struct file_access_tag {};
 }  // namespace
-template <class Tag, class BufType, FILE* BufType::*FileMemberPtr>
+template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
 class file_access {
   friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
 };
@@ -84,8 +50,8 @@
 #elif defined(_WIN32) && defined(__GLIBCXX__)
   auto* rdbuf = os.rdbuf();
   FILE* c_file;
-  if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
-    c_file = fbuf->file();
+  if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
+    c_file = sfbuf->file();
   else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
     c_file = fbuf->file();
   else
@@ -145,7 +111,7 @@
   auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
       -> OutputIt {
     auto buffer = basic_memory_buffer<Char>();
-    format_value(buffer, value, ctx.locale());
+    detail::format_value(buffer, value, ctx.locale());
     return formatter<basic_string_view<Char>, Char>::format(
         {buffer.data(), buffer.size()}, ctx);
   }
@@ -180,13 +146,6 @@
 
 namespace detail {
 
-// Formats an object of type T that has an overloaded ostream operator<<.
-template <typename T, typename Char>
-struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
-    : basic_ostream_formatter<Char> {
-  using basic_ostream_formatter<Char>::format;
-};
-
 inline void vprint_directly(std::ostream& os, string_view format_str,
                             format_args args) {
   auto buffer = memory_buffer();
@@ -196,7 +155,7 @@
 
 }  // namespace detail
 
-FMT_MODULE_EXPORT template <typename Char>
+FMT_EXPORT template <typename Char>
 void vprint(std::basic_ostream<Char>& os,
             basic_string_view<type_identity_t<Char>> format_str,
             basic_format_args<buffer_context<type_identity_t<Char>>> args) {
@@ -215,7 +174,7 @@
     fmt::print(cerr, "Don't {}!", "panic");
   \endrst
  */
-FMT_MODULE_EXPORT template <typename... T>
+FMT_EXPORT template <typename... T>
 void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
   const auto& vargs = fmt::make_format_args(args...);
   if (detail::is_utf8())
@@ -224,7 +183,7 @@
     detail::vprint_directly(os, fmt, vargs);
 }
 
-FMT_MODULE_EXPORT
+FMT_EXPORT
 template <typename... Args>
 void print(std::wostream& os,
            basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
@@ -232,6 +191,19 @@
   vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
 }
 
+FMT_EXPORT template <typename... T>
+void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
+  fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
+}
+
+FMT_EXPORT
+template <typename... Args>
+void println(std::wostream& os,
+             basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
+             Args&&... args) {
+  print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
+}
+
 FMT_END_NAMESPACE
 
 #endif  // FMT_OSTREAM_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/printf.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/printf.h
index 70a592d..adef6ad 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/printf.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/printf.h
@@ -14,24 +14,18 @@
 #include "format.h"
 
 FMT_BEGIN_NAMESPACE
-FMT_MODULE_EXPORT_BEGIN
+FMT_BEGIN_EXPORT
 
 template <typename T> struct printf_formatter { printf_formatter() = delete; };
 
-template <typename Char>
-class basic_printf_parse_context : public basic_format_parse_context<Char> {
-  using basic_format_parse_context<Char>::basic_format_parse_context;
-};
-
-template <typename OutputIt, typename Char> class basic_printf_context {
+template <typename Char> class basic_printf_context {
  private:
-  OutputIt out_;
+  detail::buffer_appender<Char> out_;
   basic_format_args<basic_printf_context> args_;
 
  public:
   using char_type = Char;
-  using format_arg = basic_format_arg<basic_printf_context>;
-  using parse_context_type = basic_printf_parse_context<Char>;
+  using parse_context_type = basic_format_parse_context<Char>;
   template <typename T> using formatter_type = printf_formatter<T>;
 
   /**
@@ -40,68 +34,68 @@
     stored in the context object so make sure they have appropriate lifetimes.
     \endrst
    */
-  basic_printf_context(OutputIt out,
+  basic_printf_context(detail::buffer_appender<Char> out,
                        basic_format_args<basic_printf_context> args)
       : out_(out), args_(args) {}
 
-  OutputIt out() { return out_; }
-  void advance_to(OutputIt it) { out_ = it; }
+  auto out() -> detail::buffer_appender<Char> { return out_; }
+  void advance_to(detail::buffer_appender<Char>) {}
 
-  detail::locale_ref locale() { return {}; }
+  auto locale() -> detail::locale_ref { return {}; }
 
-  format_arg arg(int id) const { return args_.get(id); }
+  auto arg(int id) const -> basic_format_arg<basic_printf_context> {
+    return args_.get(id);
+  }
 
   FMT_CONSTEXPR void on_error(const char* message) {
     detail::error_handler().on_error(message);
   }
 };
 
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
 
 // Checks if a value fits in int - used to avoid warnings about comparing
 // signed and unsigned integers.
 template <bool IsSigned> struct int_checker {
-  template <typename T> static bool fits_in_int(T value) {
+  template <typename T> static auto fits_in_int(T value) -> bool {
     unsigned max = max_value<int>();
     return value <= max;
   }
-  static bool fits_in_int(bool) { return true; }
+  static auto fits_in_int(bool) -> bool { return true; }
 };
 
 template <> struct int_checker<true> {
-  template <typename T> static bool fits_in_int(T value) {
+  template <typename T> static auto fits_in_int(T value) -> bool {
     return value >= (std::numeric_limits<int>::min)() &&
            value <= max_value<int>();
   }
-  static bool fits_in_int(int) { return true; }
+  static auto fits_in_int(int) -> bool { return true; }
 };
 
-class printf_precision_handler {
- public:
+struct printf_precision_handler {
   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-  int operator()(T value) {
+  auto operator()(T value) -> int {
     if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
-      FMT_THROW(format_error("number is too big"));
+      throw_format_error("number is too big");
     return (std::max)(static_cast<int>(value), 0);
   }
 
   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-  int operator()(T) {
-    FMT_THROW(format_error("precision is not integer"));
+  auto operator()(T) -> int {
+    throw_format_error("precision is not integer");
     return 0;
   }
 };
 
 // An argument visitor that returns true iff arg is a zero integer.
-class is_zero_int {
- public:
+struct is_zero_int {
   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-  bool operator()(T value) {
+  auto operator()(T value) -> bool {
     return value == 0;
   }
 
   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-  bool operator()(T) {
+  auto operator()(T) -> bool {
     return false;
   }
 };
@@ -132,22 +126,23 @@
     if (const_check(sizeof(target_type) <= sizeof(int))) {
       // Extra casts are used to silence warnings.
       if (is_signed) {
-        arg_ = detail::make_arg<Context>(
-            static_cast<int>(static_cast<target_type>(value)));
+        auto n = static_cast<int>(static_cast<target_type>(value));
+        arg_ = detail::make_arg<Context>(n);
       } else {
         using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
-        arg_ = detail::make_arg<Context>(
-            static_cast<unsigned>(static_cast<unsigned_type>(value)));
+        auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
+        arg_ = detail::make_arg<Context>(n);
       }
     } else {
       if (is_signed) {
         // glibc's printf doesn't sign extend arguments of smaller types:
         //   std::printf("%lld", -42);  // prints "4294967254"
         // but we don't have to do the same because it's a UB.
-        arg_ = detail::make_arg<Context>(static_cast<long long>(value));
+        auto n = static_cast<long long>(value);
+        arg_ = detail::make_arg<Context>(n);
       } else {
-        arg_ = detail::make_arg<Context>(
-            static_cast<typename make_unsigned_or_bool<U>::type>(value));
+        auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
+        arg_ = detail::make_arg<Context>(n);
       }
     }
   }
@@ -175,8 +170,8 @@
 
   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
   void operator()(T value) {
-    arg_ = detail::make_arg<Context>(
-        static_cast<typename Context::char_type>(value));
+    auto c = static_cast<typename Context::char_type>(value);
+    arg_ = detail::make_arg<Context>(c);
   }
 
   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@@ -186,122 +181,131 @@
 // An argument visitor that return a pointer to a C string if argument is a
 // string or null otherwise.
 template <typename Char> struct get_cstring {
-  template <typename T> const Char* operator()(T) { return nullptr; }
-  const Char* operator()(const Char* s) { return s; }
+  template <typename T> auto operator()(T) -> const Char* { return nullptr; }
+  auto operator()(const Char* s) -> const Char* { return s; }
 };
 
 // Checks if an argument is a valid printf width specifier and sets
 // left alignment if it is negative.
 template <typename Char> class printf_width_handler {
  private:
-  using format_specs = basic_format_specs<Char>;
-
-  format_specs& specs_;
+  format_specs<Char>& specs_;
 
  public:
-  explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
+  explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
 
   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-  unsigned operator()(T value) {
+  auto operator()(T value) -> unsigned {
     auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
     if (detail::is_negative(value)) {
       specs_.align = align::left;
       width = 0 - width;
     }
     unsigned int_max = max_value<int>();
-    if (width > int_max) FMT_THROW(format_error("number is too big"));
+    if (width > int_max) throw_format_error("number is too big");
     return static_cast<unsigned>(width);
   }
 
   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-  unsigned operator()(T) {
-    FMT_THROW(format_error("width is not integer"));
+  auto operator()(T) -> unsigned {
+    throw_format_error("width is not integer");
     return 0;
   }
 };
 
+// Workaround for a bug with the XL compiler when initializing
+// printf_arg_formatter's base class.
+template <typename Char>
+auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
+    -> arg_formatter<Char> {
+  return {iter, s, locale_ref()};
+}
+
 // The ``printf`` argument formatter.
-template <typename OutputIt, typename Char>
+template <typename Char>
 class printf_arg_formatter : public arg_formatter<Char> {
  private:
   using base = arg_formatter<Char>;
-  using context_type = basic_printf_context<OutputIt, Char>;
-  using format_specs = basic_format_specs<Char>;
+  using context_type = basic_printf_context<Char>;
 
   context_type& context_;
 
-  OutputIt write_null_pointer(bool is_string = false) {
+  void write_null_pointer(bool is_string = false) {
     auto s = this->specs;
     s.type = presentation_type::none;
-    return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
+    write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
   }
 
  public:
-  printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
-      : base{iter, s, locale_ref()}, context_(ctx) {}
+  printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
+                       context_type& ctx)
+      : base(make_arg_formatter(iter, s)), context_(ctx) {}
 
-  OutputIt operator()(monostate value) { return base::operator()(value); }
+  void operator()(monostate value) { base::operator()(value); }
 
   template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
-  OutputIt operator()(T value) {
+  void operator()(T value) {
     // MSVC2013 fails to compile separate overloads for bool and Char so use
     // std::is_same instead.
-    if (std::is_same<T, Char>::value) {
-      format_specs fmt_specs = this->specs;
-      if (fmt_specs.type != presentation_type::none &&
-          fmt_specs.type != presentation_type::chr) {
-        return (*this)(static_cast<int>(value));
-      }
-      fmt_specs.sign = sign::none;
-      fmt_specs.alt = false;
-      fmt_specs.fill[0] = ' ';  // Ignore '0' flag for char types.
-      // align::numeric needs to be overwritten here since the '0' flag is
-      // ignored for non-numeric types
-      if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
-        fmt_specs.align = align::right;
-      return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
+    if (!std::is_same<T, Char>::value) {
+      base::operator()(value);
+      return;
     }
-    return base::operator()(value);
+    format_specs<Char> fmt_specs = this->specs;
+    if (fmt_specs.type != presentation_type::none &&
+        fmt_specs.type != presentation_type::chr) {
+      return (*this)(static_cast<int>(value));
+    }
+    fmt_specs.sign = sign::none;
+    fmt_specs.alt = false;
+    fmt_specs.fill[0] = ' ';  // Ignore '0' flag for char types.
+    // align::numeric needs to be overwritten here since the '0' flag is
+    // ignored for non-numeric types
+    if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
+      fmt_specs.align = align::right;
+    write<Char>(this->out, static_cast<Char>(value), fmt_specs);
   }
 
   template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
-  OutputIt operator()(T value) {
-    return base::operator()(value);
+  void operator()(T value) {
+    base::operator()(value);
   }
 
   /** Formats a null-terminated C string. */
-  OutputIt operator()(const char* value) {
-    if (value) return base::operator()(value);
-    return write_null_pointer(this->specs.type != presentation_type::pointer);
+  void operator()(const char* value) {
+    if (value)
+      base::operator()(value);
+    else
+      write_null_pointer(this->specs.type != presentation_type::pointer);
   }
 
   /** Formats a null-terminated wide C string. */
-  OutputIt operator()(const wchar_t* value) {
-    if (value) return base::operator()(value);
-    return write_null_pointer(this->specs.type != presentation_type::pointer);
+  void operator()(const wchar_t* value) {
+    if (value)
+      base::operator()(value);
+    else
+      write_null_pointer(this->specs.type != presentation_type::pointer);
   }
 
-  OutputIt operator()(basic_string_view<Char> value) {
-    return base::operator()(value);
-  }
+  void operator()(basic_string_view<Char> value) { base::operator()(value); }
 
   /** Formats a pointer. */
-  OutputIt operator()(const void* value) {
-    return value ? base::operator()(value) : write_null_pointer();
+  void operator()(const void* value) {
+    if (value)
+      base::operator()(value);
+    else
+      write_null_pointer();
   }
 
   /** Formats an argument of a custom (user-defined) type. */
-  OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
-    auto parse_ctx =
-        basic_printf_parse_context<Char>(basic_string_view<Char>());
+  void operator()(typename basic_format_arg<context_type>::handle handle) {
+    auto parse_ctx = basic_format_parse_context<Char>({});
     handle.format(parse_ctx, context_);
-    return this->out;
   }
 };
 
 template <typename Char>
-void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
-                 const Char* end) {
+void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
   for (; it != end; ++it) {
     switch (*it) {
     case '-':
@@ -314,9 +318,7 @@
       specs.fill[0] = '0';
       break;
     case ' ':
-      if (specs.sign != sign::plus) {
-        specs.sign = sign::space;
-      }
+      if (specs.sign != sign::plus) specs.sign = sign::space;
       break;
     case '#':
       specs.alt = true;
@@ -328,8 +330,8 @@
 }
 
 template <typename Char, typename GetArg>
-int parse_header(const Char*& it, const Char* end,
-                 basic_format_specs<Char>& specs, GetArg get_arg) {
+auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
+                  GetArg get_arg) -> int {
   int arg_index = -1;
   Char c = *it;
   if (c >= '0' && c <= '9') {
@@ -344,7 +346,7 @@
       if (value != 0) {
         // Nonzero value means that we parsed width and don't need to
         // parse it or flags again, so return now.
-        if (value == -1) FMT_THROW(format_error("number is too big"));
+        if (value == -1) throw_format_error("number is too big");
         specs.width = value;
         return arg_index;
       }
@@ -355,7 +357,7 @@
   if (it != end) {
     if (*it >= '0' && *it <= '9') {
       specs.width = parse_nonnegative_int(it, end, -1);
-      if (specs.width == -1) FMT_THROW(format_error("number is too big"));
+      if (specs.width == -1) throw_format_error("number is too big");
     } else if (*it == '*') {
       ++it;
       specs.width = static_cast<int>(visit_format_arg(
@@ -365,13 +367,53 @@
   return arg_index;
 }
 
+inline auto parse_printf_presentation_type(char c, type t)
+    -> presentation_type {
+  using pt = presentation_type;
+  constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
+  switch (c) {
+  case 'd':
+    return in(t, integral_set) ? pt::dec : pt::none;
+  case 'o':
+    return in(t, integral_set) ? pt::oct : pt::none;
+  case 'x':
+    return in(t, integral_set) ? pt::hex_lower : pt::none;
+  case 'X':
+    return in(t, integral_set) ? pt::hex_upper : pt::none;
+  case 'a':
+    return in(t, float_set) ? pt::hexfloat_lower : pt::none;
+  case 'A':
+    return in(t, float_set) ? pt::hexfloat_upper : pt::none;
+  case 'e':
+    return in(t, float_set) ? pt::exp_lower : pt::none;
+  case 'E':
+    return in(t, float_set) ? pt::exp_upper : pt::none;
+  case 'f':
+    return in(t, float_set) ? pt::fixed_lower : pt::none;
+  case 'F':
+    return in(t, float_set) ? pt::fixed_upper : pt::none;
+  case 'g':
+    return in(t, float_set) ? pt::general_lower : pt::none;
+  case 'G':
+    return in(t, float_set) ? pt::general_upper : pt::none;
+  case 'c':
+    return in(t, integral_set) ? pt::chr : pt::none;
+  case 's':
+    return in(t, string_set | cstring_set) ? pt::string : pt::none;
+  case 'p':
+    return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
+  default:
+    return pt::none;
+  }
+}
+
 template <typename Char, typename Context>
 void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
              basic_format_args<Context> args) {
-  using OutputIt = buffer_appender<Char>;
-  auto out = OutputIt(buf);
-  auto context = basic_printf_context<OutputIt, Char>(out, args);
-  auto parse_ctx = basic_printf_parse_context<Char>(format);
+  using iterator = buffer_appender<Char>;
+  auto out = iterator(buf);
+  auto context = basic_printf_context<Char>(out, args);
+  auto parse_ctx = basic_format_parse_context<Char>(format);
 
   // Returns the argument with specified index or, if arg_index is -1, the next
   // argument.
@@ -387,26 +429,24 @@
   const Char* end = parse_ctx.end();
   auto it = start;
   while (it != end) {
-    if (!detail::find<false, Char>(it, end, '%', it)) {
-      it = end;  // detail::find leaves it == nullptr if it doesn't find '%'
+    if (!find<false, Char>(it, end, '%', it)) {
+      it = end;  // find leaves it == nullptr if it doesn't find '%'.
       break;
     }
     Char c = *it++;
     if (it != end && *it == c) {
-      out = detail::write(
-          out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
+      write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
       start = ++it;
       continue;
     }
-    out = detail::write(out, basic_string_view<Char>(
-                                 start, detail::to_unsigned(it - 1 - start)));
+    write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
 
-    basic_format_specs<Char> specs;
+    auto specs = format_specs<Char>();
     specs.align = align::right;
 
     // Parse argument index, flags and width.
     int arg_index = parse_header(it, end, specs, get_arg);
-    if (arg_index == 0) parse_ctx.on_error("argument not found");
+    if (arg_index == 0) throw_format_error("argument not found");
 
     // Parse precision.
     if (it != end && *it == '.') {
@@ -417,7 +457,7 @@
       } else if (c == '*') {
         ++it;
         specs.precision = static_cast<int>(
-            visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
+            visit_format_arg(printf_precision_handler(), get_arg(-1)));
       } else {
         specs.precision = 0;
       }
@@ -426,20 +466,19 @@
     auto arg = get_arg(arg_index);
     // For d, i, o, u, x, and X conversion specifiers, if a precision is
     // specified, the '0' flag is ignored
-    if (specs.precision >= 0 && arg.is_integral())
-      specs.fill[0] =
-          ' ';  // Ignore '0' flag for non-numeric types or if '-' present.
-    if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
-      auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
+    if (specs.precision >= 0 && arg.is_integral()) {
+      // Ignore '0' for non-numeric types or if '-' present.
+      specs.fill[0] = ' ';
+    }
+    if (specs.precision >= 0 && arg.type() == type::cstring_type) {
+      auto str = visit_format_arg(get_cstring<Char>(), arg);
       auto str_end = str + specs.precision;
       auto nul = std::find(str, str_end, Char());
-      arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
-          basic_string_view<Char>(
-              str, detail::to_unsigned(nul != str_end ? nul - str
-                                                      : specs.precision)));
+      auto sv = basic_string_view<Char>(
+          str, to_unsigned(nul != str_end ? nul - str : specs.precision));
+      arg = make_arg<basic_printf_context<Char>>(sv);
     }
-    if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
-      specs.alt = false;
+    if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
     if (specs.fill[0] == '0') {
       if (arg.is_arithmetic() && specs.align != align::left)
         specs.align = align::numeric;
@@ -451,7 +490,6 @@
     // Parse length and convert the argument to the required type.
     c = it != end ? *it++ : 0;
     Char t = it != end ? *it : 0;
-    using detail::convert_arg;
     switch (c) {
     case 'h':
       if (t == 'h') {
@@ -490,7 +528,7 @@
     }
 
     // Parse type.
-    if (it == end) FMT_THROW(format_error("invalid format string"));
+    if (it == end) throw_format_error("invalid format string");
     char type = static_cast<char>(*it++);
     if (arg.is_integral()) {
       // Normalize type.
@@ -500,32 +538,25 @@
         type = 'd';
         break;
       case 'c':
-        visit_format_arg(
-            detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
-            arg);
+        visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
         break;
       }
     }
-    specs.type = parse_presentation_type(type);
+    specs.type = parse_printf_presentation_type(type, arg.type());
     if (specs.type == presentation_type::none)
-      parse_ctx.on_error("invalid type specifier");
+      throw_format_error("invalid format specifier");
 
     start = it;
 
     // Format argument.
-    out = visit_format_arg(
-        detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
+    visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
   }
-  detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
+  write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
 }
-FMT_END_DETAIL_NAMESPACE
+}  // namespace detail
 
-template <typename Char>
-using basic_printf_context_t =
-    basic_printf_context<detail::buffer_appender<Char>, Char>;
-
-using printf_context = basic_printf_context_t<char>;
-using wprintf_context = basic_printf_context_t<wchar_t>;
+using printf_context = basic_printf_context<char>;
+using wprintf_context = basic_printf_context<wchar_t>;
 
 using printf_args = basic_format_args<printf_context>;
 using wprintf_args = basic_format_args<wprintf_context>;
@@ -542,26 +573,21 @@
   return {args...};
 }
 
-/**
-  \rst
-  Constructs an `~fmt::format_arg_store` object that contains references to
-  arguments and can be implicitly converted to `~fmt::wprintf_args`.
-  \endrst
- */
+// DEPRECATED!
 template <typename... T>
 inline auto make_wprintf_args(const T&... args)
     -> format_arg_store<wprintf_context, T...> {
   return {args...};
 }
 
-template <typename S, typename Char = char_t<S>>
+template <typename Char>
 inline auto vsprintf(
-    const S& fmt,
-    basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+    basic_string_view<Char> fmt,
+    basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
     -> std::basic_string<Char> {
-  basic_memory_buffer<Char> buffer;
-  vprintf(buffer, detail::to_string_view(fmt), args);
-  return to_string(buffer);
+  auto buf = basic_memory_buffer<Char>();
+  detail::vprintf(buf, fmt, args);
+  return to_string(buf);
 }
 
 /**
@@ -576,20 +602,19 @@
 template <typename S, typename... T,
           typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
 inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
-  using context = basic_printf_context_t<Char>;
   return vsprintf(detail::to_string_view(fmt),
-                  fmt::make_format_args<context>(args...));
+                  fmt::make_format_args<basic_printf_context<Char>>(args...));
 }
 
-template <typename S, typename Char = char_t<S>>
+template <typename Char>
 inline auto vfprintf(
-    std::FILE* f, const S& fmt,
-    basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+    std::FILE* f, basic_string_view<Char> fmt,
+    basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
     -> int {
-  basic_memory_buffer<Char> buffer;
-  vprintf(buffer, detail::to_string_view(fmt), args);
-  size_t size = buffer.size();
-  return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
+  auto buf = basic_memory_buffer<Char>();
+  detail::vprintf(buf, fmt, args);
+  size_t size = buf.size();
+  return std::fwrite(buf.data(), sizeof(Char), size, f) < size
              ? -1
              : static_cast<int>(size);
 }
@@ -605,17 +630,16 @@
  */
 template <typename S, typename... T, typename Char = char_t<S>>
 inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
-  using context = basic_printf_context_t<Char>;
   return vfprintf(f, detail::to_string_view(fmt),
-                  fmt::make_format_args<context>(args...));
+                  fmt::make_format_args<basic_printf_context<Char>>(args...));
 }
 
-template <typename S, typename Char = char_t<S>>
-inline auto vprintf(
-    const S& fmt,
-    basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+template <typename Char>
+FMT_DEPRECATED inline auto vprintf(
+    basic_string_view<Char> fmt,
+    basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
     -> int {
-  return vfprintf(stdout, detail::to_string_view(fmt), args);
+  return vfprintf(stdout, fmt, args);
 }
 
 /**
@@ -627,14 +651,17 @@
     fmt::printf("Elapsed time: %.2f seconds", 1.23);
   \endrst
  */
-template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
-inline auto printf(const S& fmt, const T&... args) -> int {
-  return vprintf(
-      detail::to_string_view(fmt),
-      fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
+template <typename... T>
+inline auto printf(string_view fmt, const T&... args) -> int {
+  return vfprintf(stdout, fmt, make_printf_args(args...));
+}
+template <typename... T>
+FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
+                                  const T&... args) -> int {
+  return vfprintf(stdout, fmt, make_wprintf_args(args...));
 }
 
-FMT_MODULE_EXPORT_END
+FMT_END_EXPORT
 FMT_END_NAMESPACE
 
 #endif  // FMT_PRINTF_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/ranges.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/ranges.h
index dea7d60..65beba5 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/ranges.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/ranges.h
@@ -22,27 +22,25 @@
 
 namespace detail {
 
-template <typename RangeT, typename OutputIterator>
-OutputIterator copy(const RangeT& range, OutputIterator out) {
+template <typename Range, typename OutputIt>
+auto copy(const Range& range, OutputIt out) -> OutputIt {
   for (auto it = range.begin(), end = range.end(); it != end; ++it)
     *out++ = *it;
   return out;
 }
 
-template <typename OutputIterator>
-OutputIterator copy(const char* str, OutputIterator out) {
+template <typename OutputIt>
+auto copy(const char* str, OutputIt out) -> OutputIt {
   while (*str) *out++ = *str++;
   return out;
 }
 
-template <typename OutputIterator>
-OutputIterator copy(char ch, OutputIterator out) {
+template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
   *out++ = ch;
   return out;
 }
 
-template <typename OutputIterator>
-OutputIterator copy(wchar_t ch, OutputIterator out) {
+template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
   *out++ = ch;
   return out;
 }
@@ -69,7 +67,7 @@
   template <typename> static void check(...);
 
  public:
-#ifdef FMT_FORMAT_MAP_AS_LIST
+#ifdef FMT_FORMAT_MAP_AS_LIST  // DEPRECATED!
   static constexpr const bool value = false;
 #else
   static constexpr const bool value =
@@ -82,7 +80,7 @@
   template <typename> static void check(...);
 
  public:
-#ifdef FMT_FORMAT_SET_AS_LIST
+#ifdef FMT_FORMAT_SET_AS_LIST  // DEPRECATED!
   static constexpr const bool value = false;
 #else
   static constexpr const bool value =
@@ -157,8 +155,9 @@
 struct has_mutable_begin_end<
     T, void_t<decltype(detail::range_begin(std::declval<T>())),
               decltype(detail::range_end(std::declval<T>())),
-              enable_if_t<std::is_copy_constructible<T>::value>>>
-    : std::true_type {};
+              // the extra int here is because older versions of MSVC don't
+              // SFINAE properly unless there are distinct types
+              int>> : std::true_type {};
 
 template <typename T>
 struct is_range_<T, void>
@@ -211,41 +210,61 @@
   static constexpr const bool value = false;
 };
 template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
-  template <std::size_t... I>
-  static std::true_type check2(index_sequence<I...>,
-                               integer_sequence<bool, (I == I)...>);
+  template <std::size_t... Is>
+  static std::true_type check2(index_sequence<Is...>,
+                               integer_sequence<bool, (Is == Is)...>);
   static std::false_type check2(...);
-  template <std::size_t... I>
+  template <std::size_t... Is>
   static decltype(check2(
-      index_sequence<I...>{},
+      index_sequence<Is...>{},
       integer_sequence<
-          bool, (is_formattable<typename std::tuple_element<I, T>::type,
-                                C>::value)...>{})) check(index_sequence<I...>);
+          bool, (is_formattable<typename std::tuple_element<Is, T>::type,
+                                C>::value)...>{})) check(index_sequence<Is...>);
 
  public:
   static constexpr const bool value =
       decltype(check(tuple_index_sequence<T>{}))::value;
 };
 
-template <class Tuple, class F, size_t... Is>
-void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
+template <typename Tuple, typename F, size_t... Is>
+FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
   using std::get;
-  // using free function get<I>(T) now.
-  const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
-  (void)_;  // blocks warnings
+  // Using a free function get<Is>(Tuple) now.
+  const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
+  ignore_unused(unused);
 }
 
-template <class T>
-FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
-    T const&) {
-  return {};
+template <typename Tuple, typename F>
+FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
+  for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
+           std::forward<Tuple>(t), std::forward<F>(f));
 }
 
-template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
-  const auto indexes = get_indexes(tup);
-  for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
+template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
+void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
+  using std::get;
+  const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
+  ignore_unused(unused);
 }
 
+template <typename Tuple1, typename Tuple2, typename F>
+void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
+  for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
+            std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
+            std::forward<F>(f));
+}
+
+namespace tuple {
+// Workaround a bug in MSVC 2019 (v140).
+template <typename Char, typename... T>
+using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
+
+using std::get;
+template <typename Tuple, typename Char, std::size_t... Is>
+auto get_formatters(index_sequence<Is...>)
+    -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
+}  // namespace tuple
+
 #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
 // Older MSVC doesn't get the reference type correctly for arrays.
 template <typename R> struct range_reference_type_impl {
@@ -269,45 +288,37 @@
 template <typename Range>
 using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
 
-template <typename Range>
-using uncvref_first_type =
-    remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>;
-
-template <typename Range>
-using uncvref_second_type = remove_cvref_t<
-    decltype(std::declval<range_reference_type<Range>>().second)>;
-
-template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
-  *out++ = ',';
-  *out++ = ' ';
-  return out;
+template <typename Formatter>
+FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
+    -> decltype(f.set_debug_format(set)) {
+  f.set_debug_format(set);
 }
+template <typename Formatter>
+FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
 
-template <typename Char, typename OutputIt>
-auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
-  return write_escaped_string(out, str);
-}
+// These are not generic lambdas for compatibility with C++11.
+template <typename ParseContext> struct parse_empty_specs {
+  template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
+    f.parse(ctx);
+    detail::maybe_set_debug_format(f, true);
+  }
+  ParseContext& ctx;
+};
+template <typename FormatContext> struct format_tuple_element {
+  using char_type = typename FormatContext::char_type;
 
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
-inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
-  auto sv = std_string_view<Char>(str);
-  return write_range_entry<Char>(out, basic_string_view<Char>(sv));
-}
+  template <typename T>
+  void operator()(const formatter<T, char_type>& f, const T& v) {
+    if (i > 0)
+      ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
+    ctx.advance_to(f.format(v, ctx));
+    ++i;
+  }
 
-template <typename Char, typename OutputIt, typename Arg,
-          FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
-OutputIt write_range_entry(OutputIt out, const Arg v) {
-  return write_escaped_char(out, v);
-}
-
-template <
-    typename Char, typename OutputIt, typename Arg,
-    FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
-                  !std::is_same<Arg, Char>::value)>
-OutputIt write_range_entry(OutputIt out, const Arg& v) {
-  return write<Char>(out, v);
-}
+  int i;
+  FormatContext& ctx;
+  basic_string_view<char_type> separator;
+};
 
 }  // namespace detail
 
@@ -321,29 +332,20 @@
       detail::is_tuple_formattable_<T, C>::value;
 };
 
-template <typename TupleT, typename Char>
-struct formatter<TupleT, Char,
-                 enable_if_t<fmt::is_tuple_like<TupleT>::value &&
-                             fmt::is_tuple_formattable<TupleT, Char>::value>> {
+template <typename Tuple, typename Char>
+struct formatter<Tuple, Char,
+                 enable_if_t<fmt::is_tuple_like<Tuple>::value &&
+                             fmt::is_tuple_formattable<Tuple, Char>::value>> {
  private:
+  decltype(detail::tuple::get_formatters<Tuple, Char>(
+      detail::tuple_index_sequence<Tuple>())) formatters_;
+
   basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
   basic_string_view<Char> opening_bracket_ =
       detail::string_literal<Char, '('>{};
   basic_string_view<Char> closing_bracket_ =
       detail::string_literal<Char, ')'>{};
 
-  // C++11 generic lambda for format().
-  template <typename FormatContext> struct format_each {
-    template <typename T> void operator()(const T& v) {
-      if (i > 0) out = detail::copy_str<Char>(separator, out);
-      out = detail::write_range_entry<Char>(out, v);
-      ++i;
-    }
-    int i;
-    typename FormatContext::iterator& out;
-    basic_string_view<Char> separator;
-  };
-
  public:
   FMT_CONSTEXPR formatter() {}
 
@@ -359,17 +361,21 @@
 
   template <typename ParseContext>
   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    return ctx.begin();
+    auto it = ctx.begin();
+    if (it != ctx.end() && *it != '}')
+      FMT_THROW(format_error("invalid format specifier"));
+    detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
+    return it;
   }
 
-  template <typename FormatContext = format_context>
-  auto format(const TupleT& values, FormatContext& ctx) const
+  template <typename FormatContext>
+  auto format(const Tuple& value, FormatContext& ctx) const
       -> decltype(ctx.out()) {
-    auto out = ctx.out();
-    out = detail::copy_str<Char>(opening_bracket_, out);
-    detail::for_each(values, format_each<FormatContext>{0, out, separator_});
-    out = detail::copy_str<Char>(closing_bracket_, out);
-    return out;
+    ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
+    detail::for_each2(
+        formatters_, value,
+        detail::format_tuple_element<FormatContext>{0, ctx, separator_});
+    return detail::copy_str<Char>(closing_bracket_, ctx.out());
   }
 };
 
@@ -398,12 +404,10 @@
 };
 
 template <typename Char, typename Element>
-using range_formatter_type = conditional_t<
-    is_formattable<Element, Char>::value,
+using range_formatter_type =
     formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
                   std::declval<Element>()))>,
-              Char>,
-    fallback_formatter<Element, Char>>;
+              Char>;
 
 template <typename R>
 using maybe_const_range =
@@ -413,11 +417,8 @@
 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
 template <typename R, typename Char>
 struct is_formattable_delayed
-    : disjunction<
-          is_formattable<uncvref_type<maybe_const_range<R>>, Char>,
-          has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {};
+    : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
 #endif
-
 }  // namespace detail
 
 template <typename T, typename Char, typename Enable = void>
@@ -426,32 +427,16 @@
 template <typename T, typename Char>
 struct range_formatter<
     T, Char,
-    enable_if_t<conjunction<
-        std::is_same<T, remove_cvref_t<T>>,
-        disjunction<is_formattable<T, Char>,
-                    detail::has_fallback_formatter<T, Char>>>::value>> {
+    enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
+                            is_formattable<T, Char>>::value>> {
  private:
   detail::range_formatter_type<Char, T> underlying_;
-  bool custom_specs_ = false;
   basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
   basic_string_view<Char> opening_bracket_ =
       detail::string_literal<Char, '['>{};
   basic_string_view<Char> closing_bracket_ =
       detail::string_literal<Char, ']'>{};
 
-  template <class U>
-  FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int)
-      -> decltype(u.set_debug_format()) {
-    u.set_debug_format();
-  }
-
-  template <class U>
-  FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
-
-  FMT_CONSTEXPR void maybe_set_debug_format() {
-    maybe_set_debug_format(underlying_, 0);
-  }
-
  public:
   FMT_CONSTEXPR range_formatter() {}
 
@@ -473,31 +458,24 @@
   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
     auto it = ctx.begin();
     auto end = ctx.end();
-    if (it == end || *it == '}') {
-      maybe_set_debug_format();
-      return it;
-    }
 
-    if (*it == 'n') {
+    if (it != end && *it == 'n') {
       set_brackets({}, {});
       ++it;
     }
 
-    if (*it == '}') {
-      maybe_set_debug_format();
-      return it;
+    if (it != end && *it != '}') {
+      if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
+      ++it;
+    } else {
+      detail::maybe_set_debug_format(underlying_, true);
     }
 
-    if (*it != ':')
-      FMT_THROW(format_error("no other top-level range formatters supported"));
-
-    custom_specs_ = true;
-    ++it;
     ctx.advance_to(it);
     return underlying_.parse(ctx);
   }
 
-  template <typename R, class FormatContext>
+  template <typename R, typename FormatContext>
   auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
     detail::range_mapper<buffer_context<Char>> mapper;
     auto out = ctx.out();
@@ -507,7 +485,6 @@
     auto end = detail::range_end(range);
     for (; it != end; ++it) {
       if (i > 0) out = detail::copy_str<Char>(separator_, out);
-      ;
       ctx.advance_to(out);
       out = underlying_.format(mapper.map(*it), ctx);
       ++i;
@@ -520,13 +497,14 @@
 enum class range_format { disabled, map, set, sequence, string, debug_string };
 
 namespace detail {
-template <typename T> struct range_format_kind_ {
-  static constexpr auto value = std::is_same<range_reference_type<T>, T>::value
-                                    ? range_format::disabled
-                                : is_map<T>::value ? range_format::map
-                                : is_set<T>::value ? range_format::set
-                                                   : range_format::sequence;
-};
+template <typename T>
+struct range_format_kind_
+    : std::integral_constant<range_format,
+                             std::is_same<uncvref_type<T>, T>::value
+                                 ? range_format::disabled
+                             : is_map<T>::value ? range_format::map
+                             : is_set<T>::value ? range_format::set
+                                                : range_format::sequence> {};
 
 template <range_format K, typename R, typename Char, typename Enable = void>
 struct range_default_formatter;
@@ -601,9 +579,6 @@
       : tuple(t), sep{s} {}
 };
 
-template <typename Char, typename... T>
-using tuple_arg_join = tuple_join_view<Char, T...>;
-
 // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
 // support in tuple_join. It is disabled by default because of issues with
 // the dynamic width and precision.
@@ -673,7 +648,45 @@
   }
 };
 
-FMT_MODULE_EXPORT_BEGIN
+namespace detail {
+// Check if T has an interface like a container adaptor (e.g. std::stack,
+// std::queue, std::priority_queue).
+template <typename T> class is_container_adaptor_like {
+  template <typename U> static auto check(U* p) -> typename U::container_type;
+  template <typename> static void check(...);
+
+ public:
+  static constexpr const bool value =
+      !std::is_void<decltype(check<T>(nullptr))>::value;
+};
+
+template <typename Container> struct all {
+  const Container& c;
+  auto begin() const -> typename Container::const_iterator { return c.begin(); }
+  auto end() const -> typename Container::const_iterator { return c.end(); }
+};
+}  // namespace detail
+
+template <typename T, typename Char>
+struct formatter<
+    T, Char,
+    enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
+                            bool_constant<range_format_kind<T, Char>::value ==
+                                          range_format::disabled>>::value>>
+    : formatter<detail::all<typename T::container_type>, Char> {
+  using all = detail::all<typename T::container_type>;
+  template <typename FormatContext>
+  auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
+    struct getter : T {
+      static auto get(const T& t) -> all {
+        return {t.*(&getter::c)};  // Access c through the derived class.
+      }
+    };
+    return formatter<all>::format(getter::get(t), ctx);
+  }
+};
+
+FMT_BEGIN_EXPORT
 
 /**
   \rst
@@ -716,7 +729,7 @@
   return join(std::begin(list), std::end(list), sep);
 }
 
-FMT_MODULE_EXPORT_END
+FMT_END_EXPORT
 FMT_END_NAMESPACE
 
 #endif  // FMT_RANGES_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/std.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/std.h
index 41d2b28..b4e055c 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/std.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/std.h
@@ -8,10 +8,18 @@
 #ifndef FMT_STD_H_
 #define FMT_STD_H_
 
+#include <atomic>
+#include <bitset>
+#include <cstdlib>
+#include <exception>
+#include <memory>
 #include <thread>
 #include <type_traits>
+#include <typeinfo>
 #include <utility>
+#include <vector>
 
+#include "format.h"
 #include "ostream.h"
 
 #if FMT_HAS_INCLUDE(<version>)
@@ -25,6 +33,30 @@
 #  if FMT_HAS_INCLUDE(<variant>)
 #    include <variant>
 #  endif
+#  if FMT_HAS_INCLUDE(<optional>)
+#    include <optional>
+#  endif
+#endif
+
+// GCC 4 does not support FMT_HAS_INCLUDE.
+#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
+#  include <cxxabi.h>
+// Android NDK with gabi++ library on some architectures does not implement
+// abi::__cxa_demangle().
+#  ifndef __GABIXX_CXXABI_H__
+#    define FMT_HAS_ABI_CXA_DEMANGLE
+#  endif
+#endif
+
+// Check if typeid is available.
+#ifndef FMT_USE_TYPEID
+// __RTTI is for EDG compilers. In MSVC typeid is available without RTTI.
+#  if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \
+      defined(__INTEL_RTTI__) || defined(__RTTI)
+#    define FMT_USE_TYPEID 1
+#  else
+#    define FMT_USE_TYPEID 0
+#  endif
 #endif
 
 #ifdef __cpp_lib_filesystem
@@ -32,21 +64,32 @@
 
 namespace detail {
 
+template <typename Char> auto get_path_string(const std::filesystem::path& p) {
+  return p.string<Char>();
+}
+
 template <typename Char>
 void write_escaped_path(basic_memory_buffer<Char>& quoted,
                         const std::filesystem::path& p) {
   write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
 }
+
 #  ifdef _WIN32
 template <>
-inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted,
-                                     const std::filesystem::path& p) {
-  auto s = p.u8string();
-  write_escaped_string<char>(
-      std::back_inserter(quoted),
-      string_view(reinterpret_cast<const char*>(s.c_str()), s.size()));
+inline auto get_path_string<char>(const std::filesystem::path& p) {
+  return to_utf8<wchar_t>(p.native(), to_utf8_error_policy::replace);
 }
-#  endif
+
+template <>
+inline void write_escaped_path<char>(memory_buffer& quoted,
+                                     const std::filesystem::path& p) {
+  auto buf = basic_memory_buffer<wchar_t>();
+  write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
+  bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
+  FMT_ASSERT(valid, "invalid utf16");
+}
+#  endif  // _WIN32
+
 template <>
 inline void write_escaped_path<std::filesystem::path::value_type>(
     basic_memory_buffer<std::filesystem::path::value_type>& quoted,
@@ -57,62 +100,118 @@
 
 }  // namespace detail
 
-template <typename Char>
-struct formatter<std::filesystem::path, Char>
-    : formatter<basic_string_view<Char>> {
+FMT_EXPORT
+template <typename Char> struct formatter<std::filesystem::path, Char> {
+ private:
+  format_specs<Char> specs_;
+  detail::arg_ref<Char> width_ref_;
+  bool debug_ = false;
+
+ public:
+  FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
+
+  template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
+    auto it = ctx.begin(), end = ctx.end();
+    if (it == end) return it;
+
+    it = detail::parse_align(it, end, specs_);
+    if (it == end) return it;
+
+    it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
+    if (it != end && *it == '?') {
+      debug_ = true;
+      ++it;
+    }
+    return it;
+  }
+
   template <typename FormatContext>
-  auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
-      typename FormatContext::iterator {
-    basic_memory_buffer<Char> quoted;
+  auto format(const std::filesystem::path& p, FormatContext& ctx) const {
+    auto specs = specs_;
+    detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
+                                                       ctx);
+    if (!debug_) {
+      auto s = detail::get_path_string<Char>(p);
+      return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
+    }
+    auto quoted = basic_memory_buffer<Char>();
     detail::write_escaped_path(quoted, p);
-    return formatter<basic_string_view<Char>>::format(
-        basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
+    return detail::write(ctx.out(),
+                         basic_string_view<Char>(quoted.data(), quoted.size()),
+                         specs);
   }
 };
 FMT_END_NAMESPACE
 #endif
 
 FMT_BEGIN_NAMESPACE
+FMT_EXPORT
 template <typename Char>
 struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
 FMT_END_NAMESPACE
 
-#ifdef __cpp_lib_variant
+#ifdef __cpp_lib_optional
 FMT_BEGIN_NAMESPACE
-template <typename Char> struct formatter<std::monostate, Char> {
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    return ctx.begin();
+FMT_EXPORT
+template <typename T, typename Char>
+struct formatter<std::optional<T>, Char,
+                 std::enable_if_t<is_formattable<T, Char>::value>> {
+ private:
+  formatter<T, Char> underlying_;
+  static constexpr basic_string_view<Char> optional =
+      detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
+                             '('>{};
+  static constexpr basic_string_view<Char> none =
+      detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
+
+  template <class U>
+  FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
+      -> decltype(u.set_debug_format(set)) {
+    u.set_debug_format(set);
+  }
+
+  template <class U>
+  FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
+
+ public:
+  template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
+    maybe_set_debug_format(underlying_, true);
+    return underlying_.parse(ctx);
   }
 
   template <typename FormatContext>
-  auto format(const std::monostate&, FormatContext& ctx) const
+  auto format(std::optional<T> const& opt, FormatContext& ctx) const
       -> decltype(ctx.out()) {
+    if (!opt) return detail::write<Char>(ctx.out(), none);
+
     auto out = ctx.out();
-    out = detail::write<Char>(out, "monostate");
-    return out;
+    out = detail::write<Char>(out, optional);
+    ctx.advance_to(out);
+    out = underlying_.format(*opt, ctx);
+    return detail::write(out, ')');
   }
 };
+FMT_END_NAMESPACE
+#endif  // __cpp_lib_optional
 
+#ifdef __cpp_lib_variant
+FMT_BEGIN_NAMESPACE
 namespace detail {
 
 template <typename T>
 using variant_index_sequence =
     std::make_index_sequence<std::variant_size<T>::value>;
 
-// variant_size and variant_alternative check.
-template <typename T, typename U = void>
-struct is_variant_like_ : std::false_type {};
-template <typename T>
-struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
-    : std::true_type {};
+template <typename> struct is_variant_like_ : std::false_type {};
+template <typename... Types>
+struct is_variant_like_<std::variant<Types...>> : std::true_type {};
 
-// formattable element check
+// formattable element check.
 template <typename T, typename C> class is_variant_formattable_ {
-  template <std::size_t... I>
+  template <std::size_t... Is>
   static std::conjunction<
-      is_formattable<std::variant_alternative_t<I, T>, C>...>
-      check(std::index_sequence<I...>);
+      is_formattable<std::variant_alternative_t<Is, T>, C>...>
+      check(std::index_sequence<Is...>);
 
  public:
   static constexpr const bool value =
@@ -140,6 +239,21 @@
       detail::is_variant_formattable_<T, C>::value;
 };
 
+FMT_EXPORT
+template <typename Char> struct formatter<std::monostate, Char> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename FormatContext>
+  auto format(const std::monostate&, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return detail::write<Char>(ctx.out(), "monostate");
+  }
+};
+
+FMT_EXPORT
 template <typename Variant, typename Char>
 struct formatter<
     Variant, Char,
@@ -156,16 +270,196 @@
     auto out = ctx.out();
 
     out = detail::write<Char>(out, "variant(");
-    std::visit(
-        [&](const auto& v) {
-          out = detail::write_variant_alternative<Char>(out, v);
-        },
-        value);
+    FMT_TRY {
+      std::visit(
+          [&](const auto& v) {
+            out = detail::write_variant_alternative<Char>(out, v);
+          },
+          value);
+    }
+    FMT_CATCH(const std::bad_variant_access&) {
+      detail::write<Char>(out, "valueless by exception");
+    }
     *out++ = ')';
     return out;
   }
 };
 FMT_END_NAMESPACE
+#endif  // __cpp_lib_variant
+
+FMT_BEGIN_NAMESPACE
+FMT_EXPORT
+template <typename Char> struct formatter<std::error_code, Char> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename FormatContext>
+  FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    auto out = ctx.out();
+    out = detail::write_bytes(out, ec.category().name(), format_specs<Char>());
+    out = detail::write<Char>(out, Char(':'));
+    out = detail::write<Char>(out, ec.value());
+    return out;
+  }
+};
+
+FMT_EXPORT
+template <typename T, typename Char>
+struct formatter<
+    T, Char,
+    typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
+ private:
+  bool with_typename_ = false;
+
+ public:
+  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
+      -> decltype(ctx.begin()) {
+    auto it = ctx.begin();
+    auto end = ctx.end();
+    if (it == end || *it == '}') return it;
+    if (*it == 't') {
+      ++it;
+      with_typename_ = FMT_USE_TYPEID != 0;
+    }
+    return it;
+  }
+
+  template <typename OutputIt>
+  auto format(const std::exception& ex,
+              basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
+    format_specs<Char> spec;
+    auto out = ctx.out();
+    if (!with_typename_)
+      return detail::write_bytes(out, string_view(ex.what()), spec);
+
+#if FMT_USE_TYPEID
+    const std::type_info& ti = typeid(ex);
+#  ifdef FMT_HAS_ABI_CXA_DEMANGLE
+    int status = 0;
+    std::size_t size = 0;
+    std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr(
+        abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
+
+    string_view demangled_name_view;
+    if (demangled_name_ptr) {
+      demangled_name_view = demangled_name_ptr.get();
+
+      // Normalization of stdlib inline namespace names.
+      // libc++ inline namespaces.
+      //  std::__1::*       -> std::*
+      //  std::__1::__fs::* -> std::*
+      // libstdc++ inline namespaces.
+      //  std::__cxx11::*             -> std::*
+      //  std::filesystem::__cxx11::* -> std::filesystem::*
+      if (demangled_name_view.starts_with("std::")) {
+        char* begin = demangled_name_ptr.get();
+        char* to = begin + 5;  // std::
+        for (char *from = to, *end = begin + demangled_name_view.size();
+             from < end;) {
+          // This is safe, because demangled_name is NUL-terminated.
+          if (from[0] == '_' && from[1] == '_') {
+            char* next = from + 1;
+            while (next < end && *next != ':') next++;
+            if (next[0] == ':' && next[1] == ':') {
+              from = next + 2;
+              continue;
+            }
+          }
+          *to++ = *from++;
+        }
+        demangled_name_view = {begin, detail::to_unsigned(to - begin)};
+      }
+    } else {
+      demangled_name_view = string_view(ti.name());
+    }
+    out = detail::write_bytes(out, demangled_name_view, spec);
+#  elif FMT_MSC_VERSION
+    string_view demangled_name_view(ti.name());
+    if (demangled_name_view.starts_with("class "))
+      demangled_name_view.remove_prefix(6);
+    else if (demangled_name_view.starts_with("struct "))
+      demangled_name_view.remove_prefix(7);
+    out = detail::write_bytes(out, demangled_name_view, spec);
+#  else
+    out = detail::write_bytes(out, string_view(ti.name()), spec);
+#  endif
+    *out++ = ':';
+    *out++ = ' ';
+    return detail::write_bytes(out, string_view(ex.what()), spec);
+#endif
+  }
+};
+
+namespace detail {
+
+template <typename T, typename Enable = void>
+struct has_flip : std::false_type {};
+
+template <typename T>
+struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
+    : std::true_type {};
+
+template <typename T> struct is_bit_reference_like {
+  static constexpr const bool value =
+      std::is_convertible<T, bool>::value &&
+      std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
+};
+
+#ifdef _LIBCPP_VERSION
+
+// Workaround for libc++ incompatibility with C++ standard.
+// According to the Standard, `bitset::operator[] const` returns bool.
+template <typename C>
+struct is_bit_reference_like<std::__bit_const_reference<C>> {
+  static constexpr const bool value = true;
+};
+
 #endif
 
+}  // namespace detail
+
+// We can't use std::vector<bool, Allocator>::reference and
+// std::bitset<N>::reference because the compiler can't deduce Allocator and N
+// in partial specialization.
+FMT_EXPORT
+template <typename BitRef, typename Char>
+struct formatter<BitRef, Char,
+                 enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
+    : formatter<bool, Char> {
+  template <typename FormatContext>
+  FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return formatter<bool, Char>::format(v, ctx);
+  }
+};
+
+FMT_EXPORT
+template <typename T, typename Char>
+struct formatter<std::atomic<T>, Char,
+                 enable_if_t<is_formattable<T, Char>::value>>
+    : formatter<T, Char> {
+  template <typename FormatContext>
+  auto format(const std::atomic<T>& v, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return formatter<T, Char>::format(v.load(), ctx);
+  }
+};
+
+#ifdef __cpp_lib_atomic_flag_test
+FMT_EXPORT
+template <typename Char>
+struct formatter<std::atomic_flag, Char>
+    : formatter<bool, Char> {
+  template <typename FormatContext>
+  auto format(const std::atomic_flag& v, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return formatter<bool, Char>::format(v.test(), ctx);
+  }
+};
+#endif // __cpp_lib_atomic_flag_test
+
+FMT_END_NAMESPACE
 #endif  // FMT_STD_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/xchar.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/xchar.h
index fc3c67f..0f79c17 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/xchar.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/xchar.h
@@ -12,13 +12,32 @@
 
 #include "format.h"
 
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+#  include <locale>
+#endif
+
 FMT_BEGIN_NAMESPACE
 namespace detail {
+
 template <typename T>
 using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
-}
 
-FMT_MODULE_EXPORT_BEGIN
+inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
+                      loc_value value, const format_specs<wchar_t>& specs,
+                      locale_ref loc) -> bool {
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+  auto& numpunct =
+      std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
+  auto separator = std::wstring();
+  auto grouping = numpunct.grouping();
+  if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
+  return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
+#endif
+  return false;
+}
+}  // namespace detail
+
+FMT_BEGIN_EXPORT
 
 using wstring_view = basic_string_view<wchar_t>;
 using wformat_parse_context = basic_format_parse_context<wchar_t>;
@@ -33,7 +52,9 @@
 #else
 template <typename... Args>
 using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
-inline auto runtime(wstring_view s) -> basic_runtime<wchar_t> { return {{s}}; }
+inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
+  return {{s}};
+}
 #endif
 
 template <> struct is_char<wchar_t> : std::true_type {};
@@ -41,9 +62,9 @@
 template <> struct is_char<char16_t> : std::true_type {};
 template <> struct is_char<char32_t> : std::true_type {};
 
-template <typename... Args>
-constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
-    const Args&... args) {
+template <typename... T>
+constexpr format_arg_store<wformat_context, T...> make_wformat_args(
+    const T&... args) {
   return {args...};
 }
 
@@ -78,9 +99,9 @@
 auto vformat(basic_string_view<Char> format_str,
              basic_format_args<buffer_context<type_identity_t<Char>>> args)
     -> std::basic_string<Char> {
-  basic_memory_buffer<Char> buffer;
-  detail::vformat_to(buffer, format_str, args);
-  return to_string(buffer);
+  auto buf = basic_memory_buffer<Char>();
+  detail::vformat_to(buf, format_str, args);
+  return to_string(buf);
 }
 
 template <typename... T>
@@ -90,10 +111,10 @@
 
 // Pass char_t as a default template parameter instead of using
 // std::basic_string<char_t<S>> to reduce the symbol size.
-template <typename S, typename... Args, typename Char = char_t<S>,
+template <typename S, typename... T, typename Char = char_t<S>,
           FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
                         !std::is_same<Char, wchar_t>::value)>
-auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
+auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
   return vformat(detail::to_string_view(format_str),
                  fmt::make_format_args<buffer_context<Char>>(args...));
 }
@@ -108,11 +129,10 @@
   return detail::vformat(loc, detail::to_string_view(format_str), args);
 }
 
-template <typename Locale, typename S, typename... Args,
-          typename Char = char_t<S>,
+template <typename Locale, typename S, typename... T, typename Char = char_t<S>,
           FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
                             detail::is_exotic_char<Char>::value)>
-inline auto format(const Locale& loc, const S& format_str, Args&&... args)
+inline auto format(const Locale& loc, const S& format_str, T&&... args)
     -> std::basic_string<Char> {
   return detail::vformat(loc, detail::to_string_view(format_str),
                          fmt::make_format_args<buffer_context<Char>>(args...));
@@ -126,14 +146,14 @@
     -> OutputIt {
   auto&& buf = detail::get_buffer<Char>(out);
   detail::vformat_to(buf, detail::to_string_view(format_str), args);
-  return detail::get_iterator(buf);
+  return detail::get_iterator(buf, out);
 }
 
-template <typename OutputIt, typename S, typename... Args,
+template <typename OutputIt, typename S, typename... T,
           typename Char = char_t<S>,
           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
                             detail::is_exotic_char<Char>::value)>
-inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
+inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
   return vformat_to(out, detail::to_string_view(fmt),
                     fmt::make_format_args<buffer_context<Char>>(args...));
 }
@@ -149,18 +169,18 @@
   auto&& buf = detail::get_buffer<Char>(out);
   vformat_to(buf, detail::to_string_view(format_str), args,
              detail::locale_ref(loc));
-  return detail::get_iterator(buf);
+  return detail::get_iterator(buf, out);
 }
 
 template <
-    typename OutputIt, typename Locale, typename S, typename... Args,
+    typename OutputIt, typename Locale, typename S, typename... T,
     typename Char = char_t<S>,
     bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
         detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
 inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
-                      Args&&... args) ->
+                      T&&... args) ->
     typename std::enable_if<enable, OutputIt>::type {
-  return vformat_to(out, loc, to_string_view(format_str),
+  return vformat_to(out, loc, detail::to_string_view(format_str),
                     fmt::make_format_args<buffer_context<Char>>(args...));
 }
 
@@ -171,36 +191,36 @@
     OutputIt out, size_t n, basic_string_view<Char> format_str,
     basic_format_args<buffer_context<type_identity_t<Char>>> args)
     -> format_to_n_result<OutputIt> {
-  detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
-                                                                           n);
+  using traits = detail::fixed_buffer_traits;
+  auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
   detail::vformat_to(buf, format_str, args);
   return {buf.out(), buf.count()};
 }
 
-template <typename OutputIt, typename S, typename... Args,
+template <typename OutputIt, typename S, typename... T,
           typename Char = char_t<S>,
           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
                             detail::is_exotic_char<Char>::value)>
-inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
-                        const Args&... args) -> format_to_n_result<OutputIt> {
+inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
+    -> format_to_n_result<OutputIt> {
   return vformat_to_n(out, n, detail::to_string_view(fmt),
                       fmt::make_format_args<buffer_context<Char>>(args...));
 }
 
-template <typename S, typename... Args, typename Char = char_t<S>,
+template <typename S, typename... T, typename Char = char_t<S>,
           FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
-inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
-  detail::counting_buffer<Char> buf;
+inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
+  auto buf = detail::counting_buffer<Char>();
   detail::vformat_to(buf, detail::to_string_view(fmt),
                      fmt::make_format_args<buffer_context<Char>>(args...));
   return buf.count();
 }
 
 inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
-  wmemory_buffer buffer;
-  detail::vformat_to(buffer, fmt, args);
-  buffer.push_back(L'\0');
-  std::fputws(buffer.data(), f);
+  auto buf = wmemory_buffer();
+  detail::vformat_to(buf, fmt, args);
+  buf.push_back(L'\0');
+  std::fputws(buf.data(), f);
 }
 
 inline void vprint(wstring_view fmt, wformat_args args) {
@@ -216,13 +236,22 @@
   return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
 }
 
+template <typename... T>
+void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
+  return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
+}
+
+template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
+  return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
+}
+
 /**
   Converts *value* to ``std::wstring`` using the default format for type *T*.
  */
 template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
   return format(FMT_STRING(L"{}"), value);
 }
-FMT_MODULE_EXPORT_END
+FMT_END_EXPORT
 FMT_END_NAMESPACE
 
 #endif  // FMT_XCHAR_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/src/format.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/src/format.cpp
index 99b7e9d..391d3a2 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/src/format.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/src/format.cpp
@@ -28,12 +28,8 @@
 
 template FMT_API void buffer<char>::append(const char*, const char*);
 
-// DEPRECATED!
-// There is no correspondent extern template in format.h because of
-// incompatibility between clang and gcc (#2377).
 template FMT_API void vformat_to(buffer<char>&, string_view,
-                                 basic_format_args<FMT_BUFFER_CONTEXT(char)>,
-                                 locale_ref);
+                                 typename vformat_args<>::type, locale_ref);
 
 // Explicit instantiations for wchar_t.
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/src/os.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/src/os.cpp
index 2c49951..d7ded50 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/src/os.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/fmtlib/src/os.cpp
@@ -18,6 +18,10 @@
 #  include <sys/stat.h>
 #  include <sys/types.h>
 
+#  ifdef _WRS_KERNEL   // VxWorks7 kernel
+#    include <ioLib.h> // getpagesize
+#  endif
+
 #  ifndef _WIN32
 #    include <unistd.h>
 #  else
@@ -72,34 +76,6 @@
 FMT_BEGIN_NAMESPACE
 
 #ifdef _WIN32
-detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
-  if (int error_code = convert(s)) {
-    FMT_THROW(windows_error(error_code,
-                            "cannot convert string from UTF-16 to UTF-8"));
-  }
-}
-
-int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) {
-  if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
-  int s_size = static_cast<int>(s.size());
-  if (s_size == 0) {
-    // WideCharToMultiByte does not support zero length, handle separately.
-    buffer_.resize(1);
-    buffer_[0] = 0;
-    return 0;
-  }
-
-  int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
-                                   nullptr, nullptr);
-  if (length == 0) return GetLastError();
-  buffer_.resize(length + 1);
-  length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
-                               length, nullptr, nullptr);
-  if (length == 0) return GetLastError();
-  buffer_[length] = 0;
-  return 0;
-}
-
 namespace detail {
 
 class system_message {
@@ -138,10 +114,10 @@
  public:
   const char* name() const noexcept override { return "system"; }
   std::string message(int error_code) const override {
-    system_message msg(error_code);
+    auto&& msg = system_message(error_code);
     if (msg) {
-      utf16_to_utf8 utf8_message;
-      if (utf8_message.convert(msg) == ERROR_SUCCESS) {
+      auto utf8_message = to_utf8<wchar_t>();
+      if (utf8_message.convert(msg)) {
         return utf8_message.str();
       }
     }
@@ -165,11 +141,12 @@
 void detail::format_windows_error(detail::buffer<char>& out, int error_code,
                                   const char* message) noexcept {
   FMT_TRY {
-    system_message msg(error_code);
+    auto&& msg = system_message(error_code);
     if (msg) {
-      utf16_to_utf8 utf8_message;
-      if (utf8_message.convert(msg) == ERROR_SUCCESS) {
-        fmt::format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
+      auto utf8_message = to_utf8<wchar_t>();
+      if (utf8_message.convert(msg)) {
+        fmt::format_to(appender(out), FMT_STRING("{}: {}"), message,
+                       string_view(utf8_message));
         return;
       }
     }
@@ -192,37 +169,47 @@
   FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
                 nullptr);
   if (!file_)
-    FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
+    FMT_THROW(system_error(errno, FMT_STRING("cannot open file {}"),
+                           filename.c_str()));
 }
 
 void buffered_file::close() {
   if (!file_) return;
   int result = FMT_SYSTEM(fclose(file_));
   file_ = nullptr;
-  if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
+  if (result != 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
 }
 
 int buffered_file::descriptor() const {
+#ifdef fileno  // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
+  int fd = fileno(file_);
+#else
   int fd = FMT_POSIX_CALL(fileno(file_));
-  if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
+#endif
+  if (fd == -1)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor")));
   return fd;
 }
 
 #if FMT_USE_FCNTL
-file::file(cstring_view path, int oflag) {
 #  ifdef _WIN32
-  using mode_t = int;
+using mode_t = int;
 #  endif
-  constexpr mode_t mode =
-      S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+constexpr mode_t default_open_mode =
+    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+
+file::file(cstring_view path, int oflag) {
 #  if defined(_WIN32) && !defined(__MINGW32__)
   fd_ = -1;
-  FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
+  auto converted = detail::utf8_to_utf16(string_view(path.c_str()));
+  *this = file::open_windows_file(converted.c_str(), oflag);
 #  else
-  FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
-#  endif
+  FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, default_open_mode)));
   if (fd_ == -1)
-    FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
+    FMT_THROW(
+        system_error(errno, FMT_STRING("cannot open file {}"), path.c_str()));
+#  endif
 }
 
 file::~file() noexcept {
@@ -238,7 +225,8 @@
   // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
   int result = FMT_POSIX_CALL(close(fd_));
   fd_ = -1;
-  if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
+  if (result != 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
 }
 
 long long file::size() const {
@@ -260,7 +248,7 @@
   using Stat = struct stat;
   Stat file_stat = Stat();
   if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
-    FMT_THROW(system_error(errno, "cannot get file attributes"));
+    FMT_THROW(system_error(errno, FMT_STRING("cannot get file attributes")));
   static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
                 "return type of file::size is not large enough");
   return file_stat.st_size;
@@ -270,14 +258,15 @@
 std::size_t file::read(void* buffer, std::size_t count) {
   rwresult result = 0;
   FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
-  if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
-  return detail::to_unsigned(result);
+  return count;
 }
 
 std::size_t file::write(const void* buffer, std::size_t count) {
   rwresult result = 0;
   FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
-  return count;
+  if (result < 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+  return detail::to_unsigned(result);
 }
 
 file file::dup(int fd) {
@@ -285,7 +274,8 @@
   // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
   int new_fd = FMT_POSIX_CALL(dup(fd));
   if (new_fd == -1)
-    FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
+    FMT_THROW(system_error(
+        errno, FMT_STRING("cannot duplicate file descriptor {}"), fd));
   return file(new_fd);
 }
 
@@ -293,8 +283,9 @@
   int result = 0;
   FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
   if (result == -1) {
-    FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
-                           fd_, fd));
+    FMT_THROW(system_error(
+        errno, FMT_STRING("cannot duplicate file descriptor {} to {}"), fd_,
+        fd));
   }
 }
 
@@ -319,7 +310,8 @@
   // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
   int result = FMT_POSIX_CALL(pipe(fds));
 #  endif
-  if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
+  if (result != 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe")));
   // The following assignments don't throw because read_fd and write_fd
   // are closed.
   read_end = file(fds[0]);
@@ -333,28 +325,72 @@
 #  else
   FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
 #  endif
-  if (!f)
-    FMT_THROW(
-        system_error(errno, "cannot associate stream with file descriptor"));
+  if (!f) {
+    FMT_THROW(system_error(
+        errno, FMT_STRING("cannot associate stream with file descriptor")));
+  }
   buffered_file bf(f);
   fd_ = -1;
   return bf;
 }
 
+#  if defined(_WIN32) && !defined(__MINGW32__)
+file file::open_windows_file(wcstring_view path, int oflag) {
+  int fd = -1;
+  auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode);
+  if (fd == -1) {
+    FMT_THROW(system_error(err, FMT_STRING("cannot open file {}"),
+                           detail::to_utf8<wchar_t>(path.c_str()).c_str()));
+  }
+  return file(fd);
+}
+#  endif
+
+#  if !defined(__MSDOS__)
 long getpagesize() {
-#  ifdef _WIN32
+#    ifdef _WIN32
   SYSTEM_INFO si;
   GetSystemInfo(&si);
   return si.dwPageSize;
-#  else
+#    else
+#      ifdef _WRS_KERNEL
+  long size = FMT_POSIX_CALL(getpagesize());
+#      else
   long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
-  if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
-  return size;
-#  endif
-}
+#      endif
 
-FMT_API void ostream::grow(size_t) {
+  if (size < 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size")));
+  return size;
+#    endif
+}
+#  endif
+
+namespace detail {
+
+void file_buffer::grow(size_t) {
   if (this->size() == this->capacity()) flush();
 }
+
+file_buffer::file_buffer(cstring_view path,
+                         const detail::ostream_params& params)
+    : file_(path, params.oflag) {
+  set(new char[params.buffer_size], params.buffer_size);
+}
+
+file_buffer::file_buffer(file_buffer&& other)
+    : detail::buffer<char>(other.data(), other.size(), other.capacity()),
+      file_(std::move(other.file_)) {
+  other.clear();
+  other.set(nullptr, 0);
+}
+
+file_buffer::~file_buffer() {
+  flush();
+  delete[] data();
+}
+}  // namespace detail
+
+ostream::~ostream() = default;
 #endif  // FMT_USE_FCNTL
 FMT_END_NAMESPACE
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/ghc/include/wpi/ghc/filesystem.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/ghc/include/wpi/ghc/filesystem.hpp
deleted file mode 100644
index 6cbc892..0000000
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/ghc/include/wpi/ghc/filesystem.hpp
+++ /dev/null
@@ -1,5942 +0,0 @@
-//---------------------------------------------------------------------------------------
-//
-// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14/C++17/C++20
-//
-//---------------------------------------------------------------------------------------
-//
-// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
-//
-// 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.
-//
-//---------------------------------------------------------------------------------------
-//
-// To dynamically select std::filesystem where available on most platforms,
-// you could use:
-//
-// #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
-// #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
-// #define GHC_USE_STD_FS
-// #include <filesystem>
-// namespace fs = std::filesystem;
-// #endif
-// #endif
-// #ifndef GHC_USE_STD_FS
-// #include <ghc/filesystem.hpp>
-// namespace fs = ghc::filesystem;
-// #endif
-//
-//---------------------------------------------------------------------------------------
-#ifndef GHC_FILESYSTEM_H
-#define GHC_FILESYSTEM_H
-
-// #define BSD manifest constant only in
-// sys/param.h
-#ifndef _WIN32
-#include <sys/param.h>
-#endif
-
-#ifndef GHC_OS_DETECTED
-#if defined(__APPLE__) && defined(__MACH__)
-#define GHC_OS_MACOS
-#elif defined(__linux__)
-#define GHC_OS_LINUX
-#if defined(__ANDROID__)
-#define GHC_OS_ANDROID
-#endif
-#elif defined(_WIN64)
-#define GHC_OS_WINDOWS
-#define GHC_OS_WIN64
-#elif defined(_WIN32)
-#define GHC_OS_WINDOWS
-#define GHC_OS_WIN32
-#elif defined(__CYGWIN__)
-#define GHC_OS_CYGWIN
-#elif defined(__svr4__)
-#define GHC_OS_SYS5R4
-#elif defined(BSD)
-#define GHC_OS_BSD
-#elif defined(__EMSCRIPTEN__)
-#define GHC_OS_WEB
-#include <wasi/api.h>
-#elif defined(__QNX__)
-#define GHC_OS_QNX
-#define GHC_NO_DIRENT_D_TYPE
-#else
-#error "Operating system currently not supported!"
-#endif
-#define GHC_OS_DETECTED
-#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
-#if _MSVC_LANG == 201703L
-#define GHC_FILESYSTEM_RUNNING_CPP17
-#else
-#define GHC_FILESYSTEM_RUNNING_CPP20
-#endif
-#elif (defined(__cplusplus) && __cplusplus >= 201703L)
-#if __cplusplus == 201703L
-#define GHC_FILESYSTEM_RUNNING_CPP17
-#else
-#define GHC_FILESYSTEM_RUNNING_CPP20
-#endif
-#endif
-#endif
-
-#if defined(GHC_FILESYSTEM_IMPLEMENTATION)
-#define GHC_EXPAND_IMPL
-#define GHC_INLINE
-#ifdef GHC_OS_WINDOWS
-#ifndef GHC_FS_API
-#define GHC_FS_API
-#endif
-#ifndef GHC_FS_API_CLASS
-#define GHC_FS_API_CLASS
-#endif
-#else
-#ifndef GHC_FS_API
-#define GHC_FS_API __attribute__((visibility("default")))
-#endif
-#ifndef GHC_FS_API_CLASS
-#define GHC_FS_API_CLASS __attribute__((visibility("default")))
-#endif
-#endif
-#elif defined(GHC_FILESYSTEM_FWD)
-#define GHC_INLINE
-#ifdef GHC_OS_WINDOWS
-#ifndef GHC_FS_API
-#define GHC_FS_API extern
-#endif
-#ifndef GHC_FS_API_CLASS
-#define GHC_FS_API_CLASS
-#endif
-#else
-#ifndef GHC_FS_API
-#define GHC_FS_API extern
-#endif
-#ifndef GHC_FS_API_CLASS
-#define GHC_FS_API_CLASS
-#endif
-#endif
-#else
-#define GHC_EXPAND_IMPL
-#define GHC_INLINE inline
-#ifndef GHC_FS_API
-#define GHC_FS_API
-#endif
-#ifndef GHC_FS_API_CLASS
-#define GHC_FS_API_CLASS
-#endif
-#endif
-
-#ifdef GHC_EXPAND_IMPL
-
-#ifdef GHC_OS_WINDOWS
-#include <windows.h>
-// additional includes
-#include <shellapi.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <wchar.h>
-#include <winioctl.h>
-#else
-#include <dirent.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
-#ifdef GHC_OS_ANDROID
-#include <android/api-level.h>
-#if __ANDROID_API__ < 12
-#include <sys/syscall.h>
-#endif
-#include <sys/vfs.h>
-#define statvfs statfs
-#else
-#include <sys/statvfs.h>
-#endif
-#ifdef GHC_OS_CYGWIN
-#include <strings.h>
-#endif
-#if !defined(__ANDROID__) || __ANDROID_API__ >= 26
-#include <langinfo.h>
-#endif
-#endif
-#ifdef GHC_OS_MACOS
-#include <Availability.h>
-#endif
-
-#if defined(__cpp_impl_three_way_comparison) && defined(__has_include)
-#if __has_include(<compare>)
-#define GHC_HAS_THREEWAY_COMP
-#include <compare>
-#endif
-#endif
-
-#include <algorithm>
-#include <cctype>
-#include <chrono>
-#include <clocale>
-#include <cstdlib>
-#include <cstring>
-#include <fstream>
-#include <functional>
-#include <memory>
-#include <stack>
-#include <stdexcept>
-#include <string>
-#include <system_error>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#else  // GHC_EXPAND_IMPL
-
-#if defined(__cpp_impl_three_way_comparison) && defined(__has_include)
-#if __has_include(<compare>)
-#define GHC_HAS_THREEWAY_COMP
-#include <compare>
-#endif
-#endif
-#include <chrono>
-#include <fstream>
-#include <memory>
-#include <stack>
-#include <stdexcept>
-#include <string>
-#include <system_error>
-#ifdef GHC_OS_WINDOWS
-#include <vector>
-#endif
-#endif  // GHC_EXPAND_IMPL
-
-// After standard library includes.
-// Standard library support for std::string_view.
-#if defined(__cpp_lib_string_view)
-#define GHC_HAS_STD_STRING_VIEW
-#elif defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 4000) && (__cplusplus >= 201402)
-#define GHC_HAS_STD_STRING_VIEW
-#elif defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 7) && (__cplusplus >= 201703)
-#define GHC_HAS_STD_STRING_VIEW
-#elif defined(_MSC_VER) && (_MSC_VER >= 1910 && _MSVC_LANG >= 201703)
-#define GHC_HAS_STD_STRING_VIEW
-#endif
-
-// Standard library support for std::experimental::string_view.
-#if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 3700 && _LIBCPP_VERSION < 7000) && (__cplusplus >= 201402)
-#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW
-#elif defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4)) && (__cplusplus >= 201402)
-#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW
-#elif defined(__GLIBCXX__) && defined(_GLIBCXX_USE_DUAL_ABI) && (__cplusplus >= 201402)
-// macro _GLIBCXX_USE_DUAL_ABI is always defined in libstdc++ from gcc-5 and newer
-#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW
-#endif
-
-#if defined(GHC_HAS_STD_STRING_VIEW)
-#include <string_view>
-#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW)
-#include <experimental/string_view>
-#endif
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp):
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// Enforce C++17 API where possible when compiling for C++20, handles the following cases:
-// * fs::path::u8string() returns std::string instead of std::u8string
-// #define GHC_FILESYSTEM_ENFORCE_CPP17_API
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories
-// configure LWG conformance ()
-#define LWG_2682_BEHAVIOUR
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular
-// file with that name, it is superseded by P1164R1, so only activate if really needed
-// #define LWG_2935_BEHAVIOUR
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// LWG #2936 enables new element wise (more expensive) path comparison
-// * if this->root_name().native().compare(p.root_name().native()) != 0 return result
-// * if this->has_root_directory() and !p.has_root_directory() return -1
-// * if !this->has_root_directory() and p.has_root_directory() return -1
-// * else result of element wise comparison of path iteration where first comparison is != 0 or 0
-//   if all comparisons are 0 (on Windows this implementation does case insensitive root_name()
-//   comparison)
-#define LWG_2936_BEHAVIOUR
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2)
-#define LWG_2937_BEHAVIOUR
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// UTF8-Everywhere is the original behaviour of ghc::filesystem. But since v1.5 the windows
-// version defaults to std::wstring storage backend. Still all std::string will be interpreted
-// as UTF-8 encoded. With this define you can enfoce the old behavior on Windows, using
-// std::string as backend and for fs::path::native() and char for fs::path::c_str(). This
-// needs more conversions so it is (an was before v1.5) slower, bot might help keeping source
-// homogeneous in a multi platform project.
-// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found,
-// instead of replacing them with the unicode replacement character (U+FFFD).
-// #define GHC_RAISE_UNICODE_ERRORS
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// Automatic prefix windows path with "\\?\" if they would break the MAX_PATH length.
-// instead of replacing them with the unicode replacement character (U+FFFD).
-#ifndef GHC_WIN_DISABLE_AUTO_PREFIXES
-#define GHC_WIN_AUTO_PREFIX_LONG_PATH
-#endif  // GHC_WIN_DISABLE_AUTO_PREFIXES
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-// ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch)
-#define GHC_FILESYSTEM_VERSION 10506L
-
-#if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND))
-#define GHC_WITH_EXCEPTIONS
-#endif
-#if !defined(GHC_WITH_EXCEPTIONS) && defined(GHC_RAISE_UNICODE_ERRORS)
-#error "Can't raise unicode errors with exception support disabled"
-#endif
-
-namespace ghc {
-namespace filesystem {
-
-#if defined(GHC_HAS_CUSTOM_STRING_VIEW)
-#define GHC_WITH_STRING_VIEW
-#elif defined(GHC_HAS_STD_STRING_VIEW)
-#define GHC_WITH_STRING_VIEW
-using std::basic_string_view;
-#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW)
-#define GHC_WITH_STRING_VIEW
-using std::experimental::basic_string_view;
-#endif
-
-// temporary existing exception type for yet unimplemented parts
-class GHC_FS_API_CLASS not_implemented_exception : public std::logic_error
-{
-public:
-    not_implemented_exception()
-        : std::logic_error("function not implemented yet.")
-    {
-    }
-};
-
-template <typename char_type>
-class path_helper_base
-{
-public:
-    using value_type = char_type;
-#ifdef GHC_OS_WINDOWS
-    static constexpr value_type preferred_separator = '\\';
-#else
-    static constexpr value_type preferred_separator = '/';
-#endif
-};
-
-#if __cplusplus < 201703L
-template <typename char_type>
-constexpr char_type path_helper_base<char_type>::preferred_separator;
-#endif
-
-#ifdef GHC_OS_WINDOWS
-class path;
-namespace detail {
-bool has_executable_extension(const path& p);
-}
-#endif
-
-// [fs.class.path] class path
-class GHC_FS_API_CLASS path
-#if defined(GHC_OS_WINDOWS) && !defined(GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE)
-#define GHC_USE_WCHAR_T
-#define GHC_NATIVEWP(p) p.c_str()
-#define GHC_PLATFORM_LITERAL(str) L##str
-    : private path_helper_base<std::wstring::value_type>
-{
-public:
-    using path_helper_base<std::wstring::value_type>::value_type;
-#else
-#define GHC_NATIVEWP(p) p.wstring().c_str()
-#define GHC_PLATFORM_LITERAL(str) str
-    : private path_helper_base<std::string::value_type>
-{
-public:
-    using path_helper_base<std::string::value_type>::value_type;
-#endif
-    using string_type = std::basic_string<value_type>;
-    using path_helper_base<value_type>::preferred_separator;
-
-    // [fs.enum.path.format] enumeration format
-    /// The path format in which the constructor argument is given.
-    enum format {
-        generic_format,  ///< The generic format, internally used by
-                         ///< ghc::filesystem::path with slashes
-        native_format,   ///< The format native to the current platform this code
-                         ///< is build for
-        auto_format,     ///< Try to auto-detect the format, fallback to native
-    };
-
-    template <class T>
-    struct _is_basic_string : std::false_type
-    {
-    };
-    template <class CharT, class Traits, class Alloc>
-    struct _is_basic_string<std::basic_string<CharT, Traits, Alloc>> : std::true_type
-    {
-    };
-    template <class CharT>
-    struct _is_basic_string<std::basic_string<CharT, std::char_traits<CharT>, std::allocator<CharT>>> : std::true_type
-    {
-    };
-#ifdef GHC_WITH_STRING_VIEW
-    template <class CharT, class Traits>
-    struct _is_basic_string<basic_string_view<CharT, Traits>> : std::true_type
-    {
-    };
-    template <class CharT>
-    struct _is_basic_string<basic_string_view<CharT, std::char_traits<CharT>>> : std::true_type
-    {
-    };
-#endif
-
-    template <typename T1, typename T2 = void>
-    using path_type = typename std::enable_if<!std::is_same<path, T1>::value, path>::type;
-    template <typename T>
-#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
-    using path_from_string =
-        typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value || std::is_same<char8_t const*, typename std::decay<T>::type>::value ||
-                                    std::is_same<char8_t*, typename std::decay<T>::type>::value || std::is_same<char16_t const*, typename std::decay<T>::type>::value || std::is_same<char16_t*, typename std::decay<T>::type>::value ||
-                                    std::is_same<char32_t const*, typename std::decay<T>::type>::value || std::is_same<char32_t*, typename std::decay<T>::type>::value || std::is_same<wchar_t const*, typename std::decay<T>::type>::value ||
-                                    std::is_same<wchar_t*, typename std::decay<T>::type>::value,
-                                path>::type;
-    template <typename T>
-    using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char8_t>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type;
-#else
-    using path_from_string = typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value ||
-                                    std::is_same<char16_t const*, typename std::decay<T>::type>::value || std::is_same<char16_t*, typename std::decay<T>::type>::value || std::is_same<char32_t const*, typename std::decay<T>::type>::value ||
-                                    std::is_same<char32_t*, typename std::decay<T>::type>::value || std::is_same<wchar_t const*, typename std::decay<T>::type>::value || std::is_same<wchar_t*, typename std::decay<T>::type>::value,
-        path>::type;
-    template <typename T>
-    using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type;
-#endif
-    // [fs.path.construct] constructors and destructor
-    path() noexcept;
-    path(const path& p);
-    path(path&& p) noexcept;
-    path(string_type&& source, format fmt = auto_format);
-    template <class Source, typename = path_from_string<Source>>
-    path(const Source& source, format fmt = auto_format);
-    template <class InputIterator>
-    path(InputIterator first, InputIterator last, format fmt = auto_format);
-#ifdef GHC_WITH_EXCEPTIONS
-    template <class Source, typename = path_from_string<Source>>
-    path(const Source& source, const std::locale& loc, format fmt = auto_format);
-    template <class InputIterator>
-    path(InputIterator first, InputIterator last, const std::locale& loc, format fmt = auto_format);
-#endif
-    ~path();
-
-    // [fs.path.assign] assignments
-    path& operator=(const path& p);
-    path& operator=(path&& p) noexcept;
-    path& operator=(string_type&& source);
-    path& assign(string_type&& source);
-    template <class Source>
-    path& operator=(const Source& source);
-    template <class Source>
-    path& assign(const Source& source);
-    template <class InputIterator>
-    path& assign(InputIterator first, InputIterator last);
-
-    // [fs.path.append] appends
-    path& operator/=(const path& p);
-    template <class Source>
-    path& operator/=(const Source& source);
-    template <class Source>
-    path& append(const Source& source);
-    template <class InputIterator>
-    path& append(InputIterator first, InputIterator last);
-
-    // [fs.path.concat] concatenation
-    path& operator+=(const path& x);
-    path& operator+=(const string_type& x);
-#ifdef GHC_WITH_STRING_VIEW
-    path& operator+=(basic_string_view<value_type> x);
-#endif
-    path& operator+=(const value_type* x);
-    path& operator+=(value_type x);
-    template <class Source>
-    path_from_string<Source>& operator+=(const Source& x);
-    template <class EcharT>
-    path_type_EcharT<EcharT>& operator+=(EcharT x);
-    template <class Source>
-    path& concat(const Source& x);
-    template <class InputIterator>
-    path& concat(InputIterator first, InputIterator last);
-
-    // [fs.path.modifiers] modifiers
-    void clear() noexcept;
-    path& make_preferred();
-    path& remove_filename();
-    path& replace_filename(const path& replacement);
-    path& replace_extension(const path& replacement = path());
-    void swap(path& rhs) noexcept;
-
-    // [fs.path.native.obs] native format observers
-    const string_type& native() const noexcept;
-    const value_type* c_str() const noexcept;
-    operator string_type() const;
-    template <class EcharT, class traits = std::char_traits<EcharT>, class Allocator = std::allocator<EcharT>>
-    std::basic_string<EcharT, traits, Allocator> string(const Allocator& a = Allocator()) const;
-    std::string string() const;
-    std::wstring wstring() const;
-#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
-    std::u8string u8string() const;
-#else
-    std::string u8string() const;
-#endif
-    std::u16string u16string() const;
-    std::u32string u32string() const;
-
-    // [fs.path.generic.obs] generic format observers
-    template <class EcharT, class traits = std::char_traits<EcharT>, class Allocator = std::allocator<EcharT>>
-    std::basic_string<EcharT, traits, Allocator> generic_string(const Allocator& a = Allocator()) const;
-    std::string generic_string() const;
-    std::wstring generic_wstring() const;
-#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
-    std::u8string generic_u8string() const;
-#else
-    std::string generic_u8string() const;
-#endif
-    std::u16string generic_u16string() const;
-    std::u32string generic_u32string() const;
-
-    // [fs.path.compare] compare
-    int compare(const path& p) const noexcept;
-    int compare(const string_type& s) const;
-#ifdef GHC_WITH_STRING_VIEW
-    int compare(basic_string_view<value_type> s) const;
-#endif
-    int compare(const value_type* s) const;
-
-    // [fs.path.decompose] decomposition
-    path root_name() const;
-    path root_directory() const;
-    path root_path() const;
-    path relative_path() const;
-    path parent_path() const;
-    path filename() const;
-    path stem() const;
-    path extension() const;
-
-    // [fs.path.query] query
-    bool empty() const noexcept;
-    bool has_root_name() const;
-    bool has_root_directory() const;
-    bool has_root_path() const;
-    bool has_relative_path() const;
-    bool has_parent_path() const;
-    bool has_filename() const;
-    bool has_stem() const;
-    bool has_extension() const;
-    bool is_absolute() const;
-    bool is_relative() const;
-
-    // [fs.path.gen] generation
-    path lexically_normal() const;
-    path lexically_relative(const path& base) const;
-    path lexically_proximate(const path& base) const;
-
-    // [fs.path.itr] iterators
-    class iterator;
-    using const_iterator = iterator;
-    iterator begin() const;
-    iterator end() const;
-
-private:
-    using impl_value_type = value_type;
-    using impl_string_type = std::basic_string<impl_value_type>;
-    friend class directory_iterator;
-    void append_name(const value_type* name);
-    static constexpr impl_value_type generic_separator = '/';
-    template <typename InputIterator>
-    class input_iterator_range
-    {
-    public:
-        typedef InputIterator iterator;
-        typedef InputIterator const_iterator;
-        typedef typename InputIterator::difference_type difference_type;
-
-        input_iterator_range(const InputIterator& first, const InputIterator& last)
-            : _first(first)
-            , _last(last)
-        {
-        }
-
-        InputIterator begin() const { return _first; }
-        InputIterator end() const { return _last; }
-
-    private:
-        InputIterator _first;
-        InputIterator _last;
-    };
-    friend void swap(path& lhs, path& rhs) noexcept;
-    friend size_t hash_value(const path& p) noexcept;
-    friend path canonical(const path& p, std::error_code& ec);
-    string_type::size_type root_name_length() const noexcept;
-    void postprocess_path_with_format(format fmt);
-    void check_long_path();
-    impl_string_type _path;
-#ifdef GHC_OS_WINDOWS
-    void handle_prefixes();
-    friend bool detail::has_executable_extension(const path& p);
-#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH
-    string_type::size_type _prefixLength{0};
-#else  // GHC_WIN_AUTO_PREFIX_LONG_PATH
-    static const string_type::size_type _prefixLength{0};
-#endif  // GHC_WIN_AUTO_PREFIX_LONG_PATH
-#else
-    static const string_type::size_type _prefixLength{0};
-#endif
-};
-
-// [fs.path.nonmember] path non-member functions
-GHC_FS_API void swap(path& lhs, path& rhs) noexcept;
-GHC_FS_API size_t hash_value(const path& p) noexcept;
-#ifdef GHC_HAS_THREEWAY_COMP
-GHC_FS_API std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept;
-#endif
-GHC_FS_API bool operator==(const path& lhs, const path& rhs) noexcept;
-GHC_FS_API bool operator!=(const path& lhs, const path& rhs) noexcept;
-GHC_FS_API bool operator<(const path& lhs, const path& rhs) noexcept;
-GHC_FS_API bool operator<=(const path& lhs, const path& rhs) noexcept;
-GHC_FS_API bool operator>(const path& lhs, const path& rhs) noexcept;
-GHC_FS_API bool operator>=(const path& lhs, const path& rhs) noexcept;
-GHC_FS_API path operator/(const path& lhs, const path& rhs);
-
-// [fs.path.io] path inserter and extractor
-template <class charT, class traits>
-std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const path& p);
-template <class charT, class traits>
-std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& is, path& p);
-
-// [pfs.path.factory] path factory functions
-template <class Source, typename = path::path_from_string<Source>>
-#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
-[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]]
-#endif
-path u8path(const Source& source);
-template <class InputIterator>
-#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
-[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]]
-#endif
-path u8path(InputIterator first, InputIterator last);
-
-// [fs.class.filesystem_error] class filesystem_error
-class GHC_FS_API_CLASS filesystem_error : public std::system_error
-{
-public:
-    filesystem_error(const std::string& what_arg, std::error_code ec);
-    filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec);
-    filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec);
-    const path& path1() const noexcept;
-    const path& path2() const noexcept;
-    const char* what() const noexcept override;
-
-private:
-    std::string _what_arg;
-    std::error_code _ec;
-    path _p1, _p2;
-};
-
-class GHC_FS_API_CLASS path::iterator
-{
-public:
-    using value_type = const path;
-    using difference_type = std::ptrdiff_t;
-    using pointer = const path*;
-    using reference = const path&;
-    using iterator_category = std::bidirectional_iterator_tag;
-
-    iterator();
-    iterator(const path& p, const impl_string_type::const_iterator& pos);
-    iterator& operator++();
-    iterator operator++(int);
-    iterator& operator--();
-    iterator operator--(int);
-    bool operator==(const iterator& other) const;
-    bool operator!=(const iterator& other) const;
-    reference operator*() const;
-    pointer operator->() const;
-
-private:
-    friend class path;
-    impl_string_type::const_iterator increment(const impl_string_type::const_iterator& pos) const;
-    impl_string_type::const_iterator decrement(const impl_string_type::const_iterator& pos) const;
-    void updateCurrent();
-    impl_string_type::const_iterator _first;
-    impl_string_type::const_iterator _last;
-    impl_string_type::const_iterator _prefix;
-    impl_string_type::const_iterator _root;
-    impl_string_type::const_iterator _iter;
-    path _current;
-};
-
-struct space_info
-{
-    uintmax_t capacity;
-    uintmax_t free;
-    uintmax_t available;
-};
-
-// [fs.enum] enumerations
-// [fs.enum.file_type]
-enum class file_type {
-    none,
-    not_found,
-    regular,
-    directory,
-    symlink,
-    block,
-    character,
-    fifo,
-    socket,
-    unknown,
-};
-
-// [fs.enum.perms]
-enum class perms : uint16_t {
-    none = 0,
-
-    owner_read = 0400,
-    owner_write = 0200,
-    owner_exec = 0100,
-    owner_all = 0700,
-
-    group_read = 040,
-    group_write = 020,
-    group_exec = 010,
-    group_all = 070,
-
-    others_read = 04,
-    others_write = 02,
-    others_exec = 01,
-    others_all = 07,
-
-    all = 0777,
-    set_uid = 04000,
-    set_gid = 02000,
-    sticky_bit = 01000,
-
-    mask = 07777,
-    unknown = 0xffff
-};
-
-// [fs.enum.perm.opts]
-enum class perm_options : uint16_t {
-    replace = 3,
-    add = 1,
-    remove = 2,
-    nofollow = 4,
-};
-
-// [fs.enum.copy.opts]
-enum class copy_options : uint16_t {
-    none = 0,
-
-    skip_existing = 1,
-    overwrite_existing = 2,
-    update_existing = 4,
-
-    recursive = 8,
-
-    copy_symlinks = 0x10,
-    skip_symlinks = 0x20,
-
-    directories_only = 0x40,
-    create_symlinks = 0x80,
-#ifndef GHC_OS_WEB
-    create_hard_links = 0x100
-#endif
-};
-
-// [fs.enum.dir.opts]
-enum class directory_options : uint16_t {
-    none = 0,
-    follow_directory_symlink = 1,
-    skip_permission_denied = 2,
-};
-
-// [fs.class.file_status] class file_status
-class GHC_FS_API_CLASS file_status
-{
-public:
-    // [fs.file_status.cons] constructors and destructor
-    file_status() noexcept;
-    explicit file_status(file_type ft, perms prms = perms::unknown) noexcept;
-    file_status(const file_status&) noexcept;
-    file_status(file_status&&) noexcept;
-    ~file_status();
-    // assignments:
-    file_status& operator=(const file_status&) noexcept;
-    file_status& operator=(file_status&&) noexcept;
-    // [fs.file_status.mods] modifiers
-    void type(file_type ft) noexcept;
-    void permissions(perms prms) noexcept;
-    // [fs.file_status.obs] observers
-    file_type type() const noexcept;
-    perms permissions() const noexcept;
-    friend bool operator==(const file_status& lhs, const file_status& rhs) noexcept { return lhs.type() == rhs.type() && lhs.permissions() == rhs.permissions(); }
-private:
-    file_type _type;
-    perms _perms;
-};
-
-using file_time_type = std::chrono::time_point<std::chrono::system_clock>;
-
-// [fs.class.directory_entry] Class directory_entry
-class GHC_FS_API_CLASS directory_entry
-{
-public:
-    // [fs.dir.entry.cons] constructors and destructor
-    directory_entry() noexcept = default;
-    directory_entry(const directory_entry&) = default;
-    directory_entry(directory_entry&&) noexcept = default;
-#ifdef GHC_WITH_EXCEPTIONS
-    explicit directory_entry(const path& p);
-#endif
-    directory_entry(const path& p, std::error_code& ec);
-    ~directory_entry();
-
-    // assignments:
-    directory_entry& operator=(const directory_entry&) = default;
-    directory_entry& operator=(directory_entry&&) noexcept = default;
-
-    // [fs.dir.entry.mods] modifiers
-#ifdef GHC_WITH_EXCEPTIONS
-    void assign(const path& p);
-    void replace_filename(const path& p);
-    void refresh();
-#endif
-    void assign(const path& p, std::error_code& ec);
-    void replace_filename(const path& p, std::error_code& ec);
-    void refresh(std::error_code& ec) noexcept;
-
-    // [fs.dir.entry.obs] observers
-    const filesystem::path& path() const noexcept;
-    operator const filesystem::path&() const noexcept;
-#ifdef GHC_WITH_EXCEPTIONS
-    bool exists() const;
-    bool is_block_file() const;
-    bool is_character_file() const;
-    bool is_directory() const;
-    bool is_fifo() const;
-    bool is_other() const;
-    bool is_regular_file() const;
-    bool is_socket() const;
-    bool is_symlink() const;
-    uintmax_t file_size() const;
-    file_time_type last_write_time() const;
-    file_status status() const;
-    file_status symlink_status() const;
-#endif
-    bool exists(std::error_code& ec) const noexcept;
-    bool is_block_file(std::error_code& ec) const noexcept;
-    bool is_character_file(std::error_code& ec) const noexcept;
-    bool is_directory(std::error_code& ec) const noexcept;
-    bool is_fifo(std::error_code& ec) const noexcept;
-    bool is_other(std::error_code& ec) const noexcept;
-    bool is_regular_file(std::error_code& ec) const noexcept;
-    bool is_socket(std::error_code& ec) const noexcept;
-    bool is_symlink(std::error_code& ec) const noexcept;
-    uintmax_t file_size(std::error_code& ec) const noexcept;
-    file_time_type last_write_time(std::error_code& ec) const noexcept;
-    file_status status(std::error_code& ec) const noexcept;
-    file_status symlink_status(std::error_code& ec) const noexcept;
-
-#ifndef GHC_OS_WEB
-#ifdef GHC_WITH_EXCEPTIONS
-    uintmax_t hard_link_count() const;
-#endif
-    uintmax_t hard_link_count(std::error_code& ec) const noexcept;
-#endif
-
-#ifdef GHC_HAS_THREEWAY_COMP
-    std::strong_ordering operator<=>(const directory_entry& rhs) const noexcept;
-#endif
-    bool operator<(const directory_entry& rhs) const noexcept;
-    bool operator==(const directory_entry& rhs) const noexcept;
-    bool operator!=(const directory_entry& rhs) const noexcept;
-    bool operator<=(const directory_entry& rhs) const noexcept;
-    bool operator>(const directory_entry& rhs) const noexcept;
-    bool operator>=(const directory_entry& rhs) const noexcept;
-
-private:
-    friend class directory_iterator;
-#ifdef GHC_WITH_EXCEPTIONS
-    file_type status_file_type() const;
-#endif
-    file_type status_file_type(std::error_code& ec) const noexcept;
-    filesystem::path _path;
-    file_status _status;
-    file_status _symlink_status;
-    uintmax_t _file_size = static_cast<uintmax_t>(-1);
-#ifndef GHC_OS_WINDOWS
-    uintmax_t _hard_link_count = static_cast<uintmax_t>(-1);
-#endif
-    time_t _last_write_time = 0;
-};
-
-// [fs.class.directory.iterator] Class directory_iterator
-class GHC_FS_API_CLASS directory_iterator
-{
-public:
-    class GHC_FS_API_CLASS proxy
-    {
-    public:
-        const directory_entry& operator*() const& noexcept { return _dir_entry; }
-        directory_entry operator*() && noexcept { return std::move(_dir_entry); }
-
-    private:
-        explicit proxy(const directory_entry& dir_entry)
-            : _dir_entry(dir_entry)
-        {
-        }
-        friend class directory_iterator;
-        friend class recursive_directory_iterator;
-        directory_entry _dir_entry;
-    };
-    using iterator_category = std::input_iterator_tag;
-    using value_type = directory_entry;
-    using difference_type = std::ptrdiff_t;
-    using pointer = const directory_entry*;
-    using reference = const directory_entry&;
-
-    // [fs.dir.itr.members] member functions
-    directory_iterator() noexcept;
-#ifdef GHC_WITH_EXCEPTIONS
-    explicit directory_iterator(const path& p);
-    directory_iterator(const path& p, directory_options options);
-#endif
-    directory_iterator(const path& p, std::error_code& ec) noexcept;
-    directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept;
-    directory_iterator(const directory_iterator& rhs);
-    directory_iterator(directory_iterator&& rhs) noexcept;
-    ~directory_iterator();
-    directory_iterator& operator=(const directory_iterator& rhs);
-    directory_iterator& operator=(directory_iterator&& rhs) noexcept;
-    const directory_entry& operator*() const;
-    const directory_entry* operator->() const;
-#ifdef GHC_WITH_EXCEPTIONS
-    directory_iterator& operator++();
-#endif
-    directory_iterator& increment(std::error_code& ec) noexcept;
-
-    // other members as required by [input.iterators]
-#ifdef GHC_WITH_EXCEPTIONS
-    proxy operator++(int)
-    {
-        proxy p{**this};
-        ++*this;
-        return p;
-    }
-#endif
-    bool operator==(const directory_iterator& rhs) const;
-    bool operator!=(const directory_iterator& rhs) const;
-
-private:
-    friend class recursive_directory_iterator;
-    class impl;
-    std::shared_ptr<impl> _impl;
-};
-
-// [fs.dir.itr.nonmembers] directory_iterator non-member functions
-GHC_FS_API directory_iterator begin(directory_iterator iter) noexcept;
-GHC_FS_API directory_iterator end(const directory_iterator&) noexcept;
-
-// [fs.class.re.dir.itr] class recursive_directory_iterator
-class GHC_FS_API_CLASS recursive_directory_iterator
-{
-public:
-    using iterator_category = std::input_iterator_tag;
-    using value_type = directory_entry;
-    using difference_type = std::ptrdiff_t;
-    using pointer = const directory_entry*;
-    using reference = const directory_entry&;
-
-    // [fs.rec.dir.itr.members] constructors and destructor
-    recursive_directory_iterator() noexcept;
-#ifdef GHC_WITH_EXCEPTIONS
-    explicit recursive_directory_iterator(const path& p);
-    recursive_directory_iterator(const path& p, directory_options options);
-#endif
-    recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept;
-    recursive_directory_iterator(const path& p, std::error_code& ec) noexcept;
-    recursive_directory_iterator(const recursive_directory_iterator& rhs);
-    recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept;
-    ~recursive_directory_iterator();
-
-    // [fs.rec.dir.itr.members] observers
-    directory_options options() const;
-    int depth() const;
-    bool recursion_pending() const;
-
-    const directory_entry& operator*() const;
-    const directory_entry* operator->() const;
-
-    // [fs.rec.dir.itr.members] modifiers recursive_directory_iterator&
-    recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs);
-    recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept;
-#ifdef GHC_WITH_EXCEPTIONS
-    recursive_directory_iterator& operator++();
-#endif
-    recursive_directory_iterator& increment(std::error_code& ec) noexcept;
-
-#ifdef GHC_WITH_EXCEPTIONS
-    void pop();
-#endif
-    void pop(std::error_code& ec);
-    void disable_recursion_pending();
-
-    // other members as required by [input.iterators]
-#ifdef GHC_WITH_EXCEPTIONS
-    directory_iterator::proxy operator++(int)
-    {
-        directory_iterator::proxy proxy{**this};
-        ++*this;
-        return proxy;
-    }
-#endif
-    bool operator==(const recursive_directory_iterator& rhs) const;
-    bool operator!=(const recursive_directory_iterator& rhs) const;
-
-private:
-    struct recursive_directory_iterator_impl
-    {
-        directory_options _options;
-        bool _recursion_pending;
-        std::stack<directory_iterator> _dir_iter_stack;
-        recursive_directory_iterator_impl(directory_options options, bool recursion_pending)
-            : _options(options)
-            , _recursion_pending(recursion_pending)
-        {
-        }
-    };
-    std::shared_ptr<recursive_directory_iterator_impl> _impl;
-};
-
-// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions
-GHC_FS_API recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept;
-GHC_FS_API recursive_directory_iterator end(const recursive_directory_iterator&) noexcept;
-
-// [fs.op.funcs] filesystem operations
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_FS_API path absolute(const path& p);
-GHC_FS_API path canonical(const path& p);
-GHC_FS_API void copy(const path& from, const path& to);
-GHC_FS_API void copy(const path& from, const path& to, copy_options options);
-GHC_FS_API bool copy_file(const path& from, const path& to);
-GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option);
-GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink);
-GHC_FS_API bool create_directories(const path& p);
-GHC_FS_API bool create_directory(const path& p);
-GHC_FS_API bool create_directory(const path& p, const path& attributes);
-GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink);
-GHC_FS_API void create_symlink(const path& to, const path& new_symlink);
-GHC_FS_API path current_path();
-GHC_FS_API void current_path(const path& p);
-GHC_FS_API bool exists(const path& p);
-GHC_FS_API bool equivalent(const path& p1, const path& p2);
-GHC_FS_API uintmax_t file_size(const path& p);
-GHC_FS_API bool is_block_file(const path& p);
-GHC_FS_API bool is_character_file(const path& p);
-GHC_FS_API bool is_directory(const path& p);
-GHC_FS_API bool is_empty(const path& p);
-GHC_FS_API bool is_fifo(const path& p);
-GHC_FS_API bool is_other(const path& p);
-GHC_FS_API bool is_regular_file(const path& p);
-GHC_FS_API bool is_socket(const path& p);
-GHC_FS_API bool is_symlink(const path& p);
-GHC_FS_API file_time_type last_write_time(const path& p);
-GHC_FS_API void last_write_time(const path& p, file_time_type new_time);
-GHC_FS_API void permissions(const path& p, perms prms, perm_options opts = perm_options::replace);
-GHC_FS_API path proximate(const path& p, const path& base = current_path());
-GHC_FS_API path read_symlink(const path& p);
-GHC_FS_API path relative(const path& p, const path& base = current_path());
-GHC_FS_API bool remove(const path& p);
-GHC_FS_API uintmax_t remove_all(const path& p);
-GHC_FS_API void rename(const path& from, const path& to);
-GHC_FS_API void resize_file(const path& p, uintmax_t size);
-GHC_FS_API space_info space(const path& p);
-GHC_FS_API file_status status(const path& p);
-GHC_FS_API file_status symlink_status(const path& p);
-GHC_FS_API path temp_directory_path();
-GHC_FS_API path weakly_canonical(const path& p);
-#endif
-GHC_FS_API path absolute(const path& p, std::error_code& ec);
-GHC_FS_API path canonical(const path& p, std::error_code& ec);
-GHC_FS_API void copy(const path& from, const path& to, std::error_code& ec) noexcept;
-GHC_FS_API void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept;
-GHC_FS_API bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept;
-GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option, std::error_code& ec) noexcept;
-GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept;
-GHC_FS_API bool create_directories(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool create_directory(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept;
-GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept;
-GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept;
-GHC_FS_API path current_path(std::error_code& ec);
-GHC_FS_API void current_path(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool exists(file_status s) noexcept;
-GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept;
-GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool is_block_file(file_status s) noexcept;
-GHC_FS_API bool is_block_file(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool is_character_file(file_status s) noexcept;
-GHC_FS_API bool is_character_file(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool is_directory(file_status s) noexcept;
-GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool is_empty(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool is_fifo(file_status s) noexcept;
-GHC_FS_API bool is_fifo(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool is_other(file_status s) noexcept;
-GHC_FS_API bool is_other(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool is_regular_file(file_status s) noexcept;
-GHC_FS_API bool is_regular_file(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool is_socket(file_status s) noexcept;
-GHC_FS_API bool is_socket(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool is_symlink(file_status s) noexcept;
-GHC_FS_API bool is_symlink(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API file_time_type last_write_time(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept;
-GHC_FS_API void permissions(const path& p, perms prms, std::error_code& ec) noexcept;
-GHC_FS_API void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept;
-GHC_FS_API path proximate(const path& p, std::error_code& ec);
-GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec);
-GHC_FS_API path read_symlink(const path& p, std::error_code& ec);
-GHC_FS_API path relative(const path& p, std::error_code& ec);
-GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec);
-GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API uintmax_t remove_all(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API void rename(const path& from, const path& to, std::error_code& ec) noexcept;
-GHC_FS_API void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept;
-GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API file_status status(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API bool status_known(file_status s) noexcept;
-GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept;
-GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept;
-GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept;
-
-#ifndef GHC_OS_WEB
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link);
-GHC_FS_API uintmax_t hard_link_count(const path& p);
-#endif
-GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept;
-GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept;
-#endif
-
-// Non-C++17 add-on std::fstream wrappers with path
-template <class charT, class traits = std::char_traits<charT>>
-class basic_filebuf : public std::basic_filebuf<charT, traits>
-{
-public:
-    basic_filebuf() {}
-    ~basic_filebuf() override {}
-    basic_filebuf(const basic_filebuf&) = delete;
-    const basic_filebuf& operator=(const basic_filebuf&) = delete;
-    basic_filebuf<charT, traits>* open(const path& p, std::ios_base::openmode mode)
-    {
-#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
-        return std::basic_filebuf<charT, traits>::open(p.wstring().c_str(), mode) ? this : 0;
-#else
-        return std::basic_filebuf<charT, traits>::open(p.string().c_str(), mode) ? this : 0;
-#endif
-    }
-};
-
-template <class charT, class traits = std::char_traits<charT>>
-class basic_ifstream : public std::basic_ifstream<charT, traits>
-{
-public:
-    basic_ifstream() {}
-#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
-    explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in)
-        : std::basic_ifstream<charT, traits>(p.wstring().c_str(), mode)
-    {
-    }
-    void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream<charT, traits>::open(p.wstring().c_str(), mode); }
-#else
-    explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in)
-        : std::basic_ifstream<charT, traits>(p.string().c_str(), mode)
-    {
-    }
-    void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream<charT, traits>::open(p.string().c_str(), mode); }
-#endif
-    basic_ifstream(const basic_ifstream&) = delete;
-    const basic_ifstream& operator=(const basic_ifstream&) = delete;
-    ~basic_ifstream() override {}
-};
-
-template <class charT, class traits = std::char_traits<charT>>
-class basic_ofstream : public std::basic_ofstream<charT, traits>
-{
-public:
-    basic_ofstream() {}
-#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
-    explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out)
-        : std::basic_ofstream<charT, traits>(p.wstring().c_str(), mode)
-    {
-    }
-    void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream<charT, traits>::open(p.wstring().c_str(), mode); }
-#else
-    explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out)
-        : std::basic_ofstream<charT, traits>(p.string().c_str(), mode)
-    {
-    }
-    void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream<charT, traits>::open(p.string().c_str(), mode); }
-#endif
-    basic_ofstream(const basic_ofstream&) = delete;
-    const basic_ofstream& operator=(const basic_ofstream&) = delete;
-    ~basic_ofstream() override {}
-};
-
-template <class charT, class traits = std::char_traits<charT>>
-class basic_fstream : public std::basic_fstream<charT, traits>
-{
-public:
-    basic_fstream() {}
-#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
-    explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
-        : std::basic_fstream<charT, traits>(p.wstring().c_str(), mode)
-    {
-    }
-    void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream<charT, traits>::open(p.wstring().c_str(), mode); }
-#else
-    explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
-        : std::basic_fstream<charT, traits>(p.string().c_str(), mode)
-    {
-    }
-    void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream<charT, traits>::open(p.string().c_str(), mode); }
-#endif
-    basic_fstream(const basic_fstream&) = delete;
-    const basic_fstream& operator=(const basic_fstream&) = delete;
-    ~basic_fstream() override {}
-};
-
-typedef basic_filebuf<char> filebuf;
-typedef basic_filebuf<wchar_t> wfilebuf;
-typedef basic_ifstream<char> ifstream;
-typedef basic_ifstream<wchar_t> wifstream;
-typedef basic_ofstream<char> ofstream;
-typedef basic_ofstream<wchar_t> wofstream;
-typedef basic_fstream<char> fstream;
-typedef basic_fstream<wchar_t> wfstream;
-
-class GHC_FS_API_CLASS u8arguments
-{
-public:
-    u8arguments(int& argc, char**& argv);
-    ~u8arguments()
-    {
-        _refargc = _argc;
-        _refargv = _argv;
-    }
-
-    bool valid() const { return _isvalid; }
-
-private:
-    int _argc;
-    char** _argv;
-    int& _refargc;
-    char**& _refargv;
-    bool _isvalid;
-#ifdef GHC_OS_WINDOWS
-    std::vector<std::string> _args;
-    std::vector<char*> _argp;
-#endif
-};
-
-//-------------------------------------------------------------------------------------------------
-//  Implementation
-//-------------------------------------------------------------------------------------------------
-
-namespace detail {
-enum utf8_states_t { S_STRT = 0, S_RJCT = 8 };
-GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode);
-GHC_FS_API bool is_surrogate(uint32_t c);
-GHC_FS_API bool is_high_surrogate(uint32_t c);
-GHC_FS_API bool is_low_surrogate(uint32_t c);
-GHC_FS_API unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint);
-enum class portable_error {
-    none = 0,
-    exists,
-    not_found,
-    not_supported,
-    not_implemented,
-    invalid_argument,
-    is_a_directory,
-};
-GHC_FS_API std::error_code make_error_code(portable_error err);
-#ifdef GHC_OS_WINDOWS
-GHC_FS_API std::error_code make_system_error(uint32_t err = 0);
-#else
-GHC_FS_API std::error_code make_system_error(int err = 0);
-#endif
-}  // namespace detail
-
-namespace detail {
-
-#ifdef GHC_EXPAND_IMPL
-
-GHC_INLINE std::error_code make_error_code(portable_error err)
-{
-#ifdef GHC_OS_WINDOWS
-    switch (err) {
-        case portable_error::none:
-            return std::error_code();
-        case portable_error::exists:
-            return std::error_code(ERROR_ALREADY_EXISTS, std::system_category());
-        case portable_error::not_found:
-            return std::error_code(ERROR_PATH_NOT_FOUND, std::system_category());
-        case portable_error::not_supported:
-            return std::error_code(ERROR_NOT_SUPPORTED, std::system_category());
-        case portable_error::not_implemented:
-            return std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category());
-        case portable_error::invalid_argument:
-            return std::error_code(ERROR_INVALID_PARAMETER, std::system_category());
-        case portable_error::is_a_directory:
-#ifdef ERROR_DIRECTORY_NOT_SUPPORTED
-            return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category());
-#else
-            return std::error_code(ERROR_NOT_SUPPORTED, std::system_category());
-#endif
-    }
-#else
-    switch (err) {
-        case portable_error::none:
-            return std::error_code();
-        case portable_error::exists:
-            return std::error_code(EEXIST, std::system_category());
-        case portable_error::not_found:
-            return std::error_code(ENOENT, std::system_category());
-        case portable_error::not_supported:
-            return std::error_code(ENOTSUP, std::system_category());
-        case portable_error::not_implemented:
-            return std::error_code(ENOSYS, std::system_category());
-        case portable_error::invalid_argument:
-            return std::error_code(EINVAL, std::system_category());
-        case portable_error::is_a_directory:
-            return std::error_code(EISDIR, std::system_category());
-    }
-#endif
-    return std::error_code();
-}
-
-#ifdef GHC_OS_WINDOWS
-GHC_INLINE std::error_code make_system_error(uint32_t err)
-{
-    return std::error_code(err ? static_cast<int>(err) : static_cast<int>(::GetLastError()), std::system_category());
-}
-#else
-GHC_INLINE std::error_code make_system_error(int err)
-{
-    return std::error_code(err ? err : errno, std::system_category());
-}
-#endif
-
-#endif  // GHC_EXPAND_IMPL
-
-template <typename Enum>
-using EnableBitmask = typename std::enable_if<std::is_same<Enum, perms>::value || std::is_same<Enum, perm_options>::value || std::is_same<Enum, copy_options>::value || std::is_same<Enum, directory_options>::value, Enum>::type;
-}  // namespace detail
-
-template <typename Enum>
-constexpr detail::EnableBitmask<Enum> operator&(Enum X, Enum Y)
-{
-    using underlying = typename std::underlying_type<Enum>::type;
-    return static_cast<Enum>(static_cast<underlying>(X) & static_cast<underlying>(Y));
-}
-
-template <typename Enum>
-constexpr detail::EnableBitmask<Enum> operator|(Enum X, Enum Y)
-{
-    using underlying = typename std::underlying_type<Enum>::type;
-    return static_cast<Enum>(static_cast<underlying>(X) | static_cast<underlying>(Y));
-}
-
-template <typename Enum>
-constexpr detail::EnableBitmask<Enum> operator^(Enum X, Enum Y)
-{
-    using underlying = typename std::underlying_type<Enum>::type;
-    return static_cast<Enum>(static_cast<underlying>(X) ^ static_cast<underlying>(Y));
-}
-
-template <typename Enum>
-constexpr detail::EnableBitmask<Enum> operator~(Enum X)
-{
-    using underlying = typename std::underlying_type<Enum>::type;
-    return static_cast<Enum>(~static_cast<underlying>(X));
-}
-
-template <typename Enum>
-detail::EnableBitmask<Enum>& operator&=(Enum& X, Enum Y)
-{
-    X = X & Y;
-    return X;
-}
-
-template <typename Enum>
-detail::EnableBitmask<Enum>& operator|=(Enum& X, Enum Y)
-{
-    X = X | Y;
-    return X;
-}
-
-template <typename Enum>
-detail::EnableBitmask<Enum>& operator^=(Enum& X, Enum Y)
-{
-    X = X ^ Y;
-    return X;
-}
-
-#ifdef GHC_EXPAND_IMPL
-
-namespace detail {
-
-GHC_INLINE bool in_range(uint32_t c, uint32_t lo, uint32_t hi)
-{
-    return (static_cast<uint32_t>(c - lo) < (hi - lo + 1));
-}
-
-GHC_INLINE bool is_surrogate(uint32_t c)
-{
-    return in_range(c, 0xd800, 0xdfff);
-}
-
-GHC_INLINE bool is_high_surrogate(uint32_t c)
-{
-    return (c & 0xfffffc00) == 0xd800;
-}
-
-GHC_INLINE bool is_low_surrogate(uint32_t c)
-{
-    return (c & 0xfffffc00) == 0xdc00;
-}
-
-GHC_INLINE void appendUTF8(std::string& str, uint32_t unicode)
-{
-    if (unicode <= 0x7f) {
-        str.push_back(static_cast<char>(unicode));
-    }
-    else if (unicode >= 0x80 && unicode <= 0x7ff) {
-        str.push_back(static_cast<char>((unicode >> 6) + 192));
-        str.push_back(static_cast<char>((unicode & 0x3f) + 128));
-    }
-    else if ((unicode >= 0x800 && unicode <= 0xd7ff) || (unicode >= 0xe000 && unicode <= 0xffff)) {
-        str.push_back(static_cast<char>((unicode >> 12) + 224));
-        str.push_back(static_cast<char>(((unicode & 0xfff) >> 6) + 128));
-        str.push_back(static_cast<char>((unicode & 0x3f) + 128));
-    }
-    else if (unicode >= 0x10000 && unicode <= 0x10ffff) {
-        str.push_back(static_cast<char>((unicode >> 18) + 240));
-        str.push_back(static_cast<char>(((unicode & 0x3ffff) >> 12) + 128));
-        str.push_back(static_cast<char>(((unicode & 0xfff) >> 6) + 128));
-        str.push_back(static_cast<char>((unicode & 0x3f) + 128));
-    }
-    else {
-#ifdef GHC_RAISE_UNICODE_ERRORS
-        throw filesystem_error("Illegal code point for unicode character.", str, std::make_error_code(std::errc::illegal_byte_sequence));
-#else
-        appendUTF8(str, 0xfffd);
-#endif
-    }
-}
-
-// Thanks to Bjoern Hoehrmann (https://bjoern.hoehrmann.de/utf-8/decoder/dfa/)
-// and Taylor R Campbell for the ideas to this DFA approach of UTF-8 decoding;
-// Generating debugging and shrinking my own DFA from scratch was a day of fun!
-GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint)
-{
-    static const uint32_t utf8_state_info[] = {
-        // encoded states
-        0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u,
-        0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u,          0u,          0u,          0u,
-    };
-    uint8_t category = fragment < 128 ? 0 : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf;
-    codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) : (0xffu >> category) & fragment);
-    return state == S_RJCT ? static_cast<unsigned>(S_RJCT) : static_cast<unsigned>((utf8_state_info[category + 16] >> (state << 2)) & 0xf);
-}
-
-GHC_INLINE bool validUtf8(const std::string& utf8String)
-{
-    std::string::const_iterator iter = utf8String.begin();
-    unsigned utf8_state = S_STRT;
-    std::uint32_t codepoint = 0;
-    while (iter < utf8String.end()) {
-        if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast<uint8_t>(*iter++), codepoint)) == S_RJCT) {
-            return false;
-        }
-    }
-    if (utf8_state) {
-        return false;
-    }
-    return true;
-}
-
-}  // namespace detail
-
-#endif
-
-namespace detail {
-
-template <class StringType, class Utf8String, typename std::enable_if<path::_is_basic_string<Utf8String>::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 1)>::type* = nullptr>
-inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
-{
-    return StringType(utf8String.begin(), utf8String.end(), alloc);
-}
-
-template <class StringType, class Utf8String, typename std::enable_if<path::_is_basic_string<Utf8String>::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 2)>::type* = nullptr>
-inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
-{
-    StringType result(alloc);
-    result.reserve(utf8String.length());
-    auto iter = utf8String.cbegin();
-    unsigned utf8_state = S_STRT;
-    std::uint32_t codepoint = 0;
-    while (iter < utf8String.cend()) {
-        if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast<uint8_t>(*iter++), codepoint)) == S_STRT) {
-            if (codepoint <= 0xffff) {
-                result += static_cast<typename StringType::value_type>(codepoint);
-            }
-            else {
-                codepoint -= 0x10000;
-                result += static_cast<typename StringType::value_type>((codepoint >> 10) + 0xd800);
-                result += static_cast<typename StringType::value_type>((codepoint & 0x3ff) + 0xdc00);
-            }
-            codepoint = 0;
-        }
-        else if (utf8_state == S_RJCT) {
-#ifdef GHC_RAISE_UNICODE_ERRORS
-            throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence));
-#else
-            result += static_cast<typename StringType::value_type>(0xfffd);
-            utf8_state = S_STRT;
-            codepoint = 0;
-#endif
-        }
-    }
-    if (utf8_state) {
-#ifdef GHC_RAISE_UNICODE_ERRORS
-        throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence));
-#else
-        result += static_cast<typename StringType::value_type>(0xfffd);
-#endif
-    }
-    return result;
-}
-
-template <class StringType, class Utf8String, typename std::enable_if<path::_is_basic_string<Utf8String>::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 4)>::type* = nullptr>
-inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
-{
-    StringType result(alloc);
-    result.reserve(utf8String.length());
-    auto iter = utf8String.cbegin();
-    unsigned utf8_state = S_STRT;
-    std::uint32_t codepoint = 0;
-    while (iter < utf8String.cend()) {
-        if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast<uint8_t>(*iter++), codepoint)) == S_STRT) {
-            result += static_cast<typename StringType::value_type>(codepoint);
-            codepoint = 0;
-        }
-        else if (utf8_state == S_RJCT) {
-#ifdef GHC_RAISE_UNICODE_ERRORS
-            throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence));
-#else
-            result += static_cast<typename StringType::value_type>(0xfffd);
-            utf8_state = S_STRT;
-            codepoint = 0;
-#endif
-        }
-    }
-    if (utf8_state) {
-#ifdef GHC_RAISE_UNICODE_ERRORS
-        throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence));
-#else
-        result += static_cast<typename StringType::value_type>(0xfffd);
-#endif
-    }
-    return result;
-}
-
-template <class StringType, typename charT, std::size_t N>
-inline StringType fromUtf8(const charT (&utf8String)[N])
-{
-#ifdef GHC_WITH_STRING_VIEW
-    return fromUtf8<StringType>(basic_string_view<charT>(utf8String, N - 1));
-#else
-    return fromUtf8<StringType>(std::basic_string<charT>(utf8String, N - 1));
-#endif
-}
-
-template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 1), int>::type size = 1>
-inline std::string toUtf8(const strT& unicodeString)
-{
-    return std::string(unicodeString.begin(), unicodeString.end());
-}
-
-template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 2), int>::type size = 2>
-inline std::string toUtf8(const strT& unicodeString)
-{
-    std::string result;
-    for (auto iter = unicodeString.begin(); iter != unicodeString.end(); ++iter) {
-        char32_t c = *iter;
-        if (is_surrogate(c)) {
-            ++iter;
-            if (iter != unicodeString.end() && is_high_surrogate(c) && is_low_surrogate(*iter)) {
-                appendUTF8(result, (char32_t(c) << 10) + *iter - 0x35fdc00);
-            }
-            else {
-#ifdef GHC_RAISE_UNICODE_ERRORS
-                throw filesystem_error("Illegal code point for unicode character.", result, std::make_error_code(std::errc::illegal_byte_sequence));
-#else
-                appendUTF8(result, 0xfffd);
-                if (iter == unicodeString.end()) {
-                    break;
-                }
-#endif
-            }
-        }
-        else {
-            appendUTF8(result, c);
-        }
-    }
-    return result;
-}
-
-template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 4), int>::type size = 4>
-inline std::string toUtf8(const strT& unicodeString)
-{
-    std::string result;
-    for (auto c : unicodeString) {
-        appendUTF8(result, static_cast<uint32_t>(c));
-    }
-    return result;
-}
-
-template <typename charT>
-inline std::string toUtf8(const charT* unicodeString)
-{
-#ifdef GHC_WITH_STRING_VIEW
-    return toUtf8(basic_string_view<charT, std::char_traits<charT>>(unicodeString));
-#else
-    return toUtf8(std::basic_string<charT, std::char_traits<charT>>(unicodeString));
-#endif
-}
-
-#ifdef GHC_USE_WCHAR_T
-template <class StringType, class WString, typename std::enable_if<path::_is_basic_string<WString>::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 1), bool>::type = false>
-inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
-{
-    auto temp = toUtf8(wString);
-    return StringType(temp.begin(), temp.end(), alloc);
-}
-
-template <class StringType, class WString, typename std::enable_if<path::_is_basic_string<WString>::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 2), bool>::type = false>
-inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
-{
-    return StringType(wString.begin(), wString.end(), alloc);
-}
-
-template <class StringType, class WString, typename std::enable_if<path::_is_basic_string<WString>::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 4), bool>::type = false>
-inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
-{
-    auto temp = toUtf8(wString);
-    return fromUtf8<StringType>(temp, alloc);
-}
-
-template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 1), bool>::type = false>
-inline std::wstring toWChar(const strT& unicodeString)
-{
-    return fromUtf8<std::wstring>(unicodeString);
-}
-
-template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 2), bool>::type = false>
-inline std::wstring toWChar(const strT& unicodeString)
-{
-    return std::wstring(unicodeString.begin(), unicodeString.end());
-}
-
-template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 4), bool>::type = false>
-inline std::wstring toWChar(const strT& unicodeString)
-{
-    auto temp = toUtf8(unicodeString);
-    return fromUtf8<std::wstring>(temp);
-}
-
-template <typename charT>
-inline std::wstring toWChar(const charT* unicodeString)
-{
-#ifdef GHC_WITH_STRING_VIEW
-    return toWChar(basic_string_view<charT, std::char_traits<charT>>(unicodeString));
-#else
-    return toWChar(std::basic_string<charT, std::char_traits<charT>>(unicodeString));
-#endif
-}
-#endif // GHC_USE_WCHAR_T
-
-}  // namespace detail
-
-#ifdef GHC_EXPAND_IMPL
-
-namespace detail {
-
-template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value, bool>::type = true>
-GHC_INLINE bool startsWith(const strT& what, const strT& with)
-{
-    return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin());
-}
-
-template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value, bool>::type = true>
-GHC_INLINE bool endsWith(const strT& what, const strT& with)
-{
-    return with.length() <= what.length() && what.compare(what.length() - with.length(), with.size(), with) == 0;
-}
-
-}  // namespace detail
-
-GHC_INLINE void path::check_long_path()
-{
-#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
-    if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) {
-        postprocess_path_with_format(native_format);
-    }
-#endif
-}
-
-GHC_INLINE void path::postprocess_path_with_format(path::format fmt)
-{
-#ifdef GHC_RAISE_UNICODE_ERRORS
-    if (!detail::validUtf8(_path)) {
-        path t;
-        t._path = _path;
-        throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence));
-    }
-#endif
-    switch (fmt) {
-#ifdef GHC_OS_WINDOWS
-        case path::native_format:
-        case path::auto_format:
-        case path::generic_format:
-            for (auto& c : _path) {
-                if (c == generic_separator) {
-                    c = preferred_separator;
-                }
-            }
-#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH
-            if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) {
-                _path = GHC_PLATFORM_LITERAL("\\\\?\\") + _path;
-            }
-#endif
-            handle_prefixes();
-            break;
-#else
-        case path::auto_format:
-        case path::native_format:
-        case path::generic_format:
-            // nothing to do
-            break;
-#endif
-    }
-    if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator) {
-        impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast<string_type::difference_type>(_prefixLength) + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; });
-        _path.erase(new_end, _path.end());
-    }
-    else {
-        impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast<string_type::difference_type>(_prefixLength), _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; });
-        _path.erase(new_end, _path.end());
-    }
-}
-
-#endif  // GHC_EXPAND_IMPL
-
-template <class Source, typename>
-inline path::path(const Source& source, format fmt)
-#ifdef GHC_USE_WCHAR_T
-    : _path(detail::toWChar(source))
-#else
-    : _path(detail::toUtf8(source))
-#endif
-{
-    postprocess_path_with_format(fmt);
-}
-
-template <class Source, typename>
-inline path u8path(const Source& source)
-{
-    return path(source);
-}
-template <class InputIterator>
-inline path u8path(InputIterator first, InputIterator last)
-{
-    return path(first, last);
-}
-
-template <class InputIterator>
-inline path::path(InputIterator first, InputIterator last, format fmt)
-    : path(std::basic_string<typename std::iterator_traits<InputIterator>::value_type>(first, last), fmt)
-{
-    // delegated
-}
-
-#ifdef GHC_EXPAND_IMPL
-
-namespace detail {
-
-GHC_INLINE bool equals_simple_insensitive(const path::value_type* str1, const path::value_type* str2)
-{
-#ifdef GHC_OS_WINDOWS
-#ifdef __GNUC__
-    while (::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2++)) {
-        if (*str1++ == 0)
-            return true;
-    }
-    return false;
-#else // __GNUC__
-#ifdef GHC_USE_WCHAR_T
-    return 0 == ::_wcsicmp(str1, str2);
-#else // GHC_USE_WCHAR_T
-    return 0 == ::_stricmp(str1, str2);
-#endif // GHC_USE_WCHAR_T
-#endif // __GNUC__
-#else // GHC_OS_WINDOWS
-    return 0 == ::strcasecmp(str1, str2);
-#endif // GHC_OS_WINDOWS
-}
-
-GHC_INLINE int compare_simple_insensitive(const path::value_type* str1, size_t len1, const path::value_type* str2, size_t len2)
-{
-    while (len1 > 0 && len2 > 0 && ::tolower(static_cast<unsigned char>(*str1)) == ::tolower(static_cast<unsigned char>(*str2))) {
-        --len1;
-        --len2;
-        ++str1;
-        ++str2;
-    }
-    if (len1 && len2) {
-        return *str1 < *str2 ? -1 : 1;
-    }
-    if (len1 == 0 && len2 == 0) {
-        return 0;
-    }
-    return len1 == 0 ? -1 : 1;
-}
-
-GHC_INLINE const char* strerror_adapter(char* gnu, char*)
-{
-    return gnu;
-}
-
-GHC_INLINE const char* strerror_adapter(int posix, char* buffer)
-{
-    if (posix) {
-        return "Error in strerror_r!";
-    }
-    return buffer;
-}
-
-template <typename ErrorNumber>
-GHC_INLINE std::string systemErrorText(ErrorNumber code = 0)
-{
-#if defined(GHC_OS_WINDOWS)
-    LPVOID msgBuf;
-    DWORD dw = code ? static_cast<DWORD>(code) : ::GetLastError();
-    FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, NULL);
-    std::string msg = toUtf8(std::wstring((LPWSTR)msgBuf));
-    LocalFree(msgBuf);
-    return msg;
-#else
-    char buffer[512];
-    return strerror_adapter(strerror_r(code ? code : errno, buffer, sizeof(buffer)), buffer);
-#endif
-}
-
-#ifdef GHC_OS_WINDOWS
-using CreateSymbolicLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, DWORD);
-using CreateHardLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
-
-GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool to_directory, std::error_code& ec)
-{
-    std::error_code tec;
-    auto fs = status(target_name, tec);
-    if ((fs.type() == file_type::directory && !to_directory) || (fs.type() == file_type::regular && to_directory)) {
-        ec = detail::make_error_code(detail::portable_error::not_supported);
-        return;
-    }
-#if defined(__GNUC__) && __GNUC__ >= 8
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wcast-function-type"
-#endif
-    static CreateSymbolicLinkW_fp api_call = reinterpret_cast<CreateSymbolicLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW"));
-#if defined(__GNUC__) && __GNUC__ >= 8
-#pragma GCC diagnostic pop
-#endif
-    if (api_call) {
-        if (api_call(detail::fromUtf8<std::wstring>(new_symlink.u8string()).c_str(), detail::fromUtf8<std::wstring>(target_name.u8string()).c_str(), to_directory ? 1 : 0) == 0) {
-            auto result = ::GetLastError();
-            if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(detail::fromUtf8<std::wstring>(new_symlink.u8string()).c_str(), detail::fromUtf8<std::wstring>(target_name.u8string()).c_str(), to_directory ? 3 : 2) != 0) {
-                return;
-            }
-            ec = detail::make_system_error(result);
-        }
-    }
-    else {
-        ec = detail::make_system_error(ERROR_NOT_SUPPORTED);
-    }
-}
-
-GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec)
-{
-#if defined(__GNUC__) && __GNUC__ >= 8
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wcast-function-type"
-#endif
-    static CreateHardLinkW_fp api_call = reinterpret_cast<CreateHardLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW"));
-#if defined(__GNUC__) && __GNUC__ >= 8
-#pragma GCC diagnostic pop
-#endif
-    if (api_call) {
-        if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) {
-            ec = detail::make_system_error();
-        }
-    }
-    else {
-        ec = detail::make_system_error(ERROR_NOT_SUPPORTED);
-    }
-}
-
-GHC_INLINE path getFullPathName(const wchar_t* p, std::error_code& ec)
-{
-    ULONG size = ::GetFullPathNameW(p, 0, 0, 0);
-    if (size) {
-        std::vector<wchar_t> buf(size, 0);
-        ULONG s2 = GetFullPathNameW(p, size, buf.data(), nullptr);
-        if (s2 && s2 < size) {
-            return path(std::wstring(buf.data(), s2));
-        }
-    }
-    ec = detail::make_system_error();
-    return path();
-}
-
-#else
-GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool, std::error_code& ec)
-{
-    if (::symlink(target_name.c_str(), new_symlink.c_str()) != 0) {
-        ec = detail::make_system_error();
-    }
-}
-
-#ifndef GHC_OS_WEB
-GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec)
-{
-    if (::link(target_name.c_str(), new_hardlink.c_str()) != 0) {
-        ec = detail::make_system_error();
-    }
-}
-#endif
-#endif
-
-template <typename T>
-GHC_INLINE file_status file_status_from_st_mode(T mode)
-{
-#ifdef GHC_OS_WINDOWS
-    file_type ft = file_type::unknown;
-    if ((mode & _S_IFDIR) == _S_IFDIR) {
-        ft = file_type::directory;
-    }
-    else if ((mode & _S_IFREG) == _S_IFREG) {
-        ft = file_type::regular;
-    }
-    else if ((mode & _S_IFCHR) == _S_IFCHR) {
-        ft = file_type::character;
-    }
-    perms prms = static_cast<perms>(mode & 0xfff);
-    return file_status(ft, prms);
-#else
-    file_type ft = file_type::unknown;
-    if (S_ISDIR(mode)) {
-        ft = file_type::directory;
-    }
-    else if (S_ISREG(mode)) {
-        ft = file_type::regular;
-    }
-    else if (S_ISCHR(mode)) {
-        ft = file_type::character;
-    }
-    else if (S_ISBLK(mode)) {
-        ft = file_type::block;
-    }
-    else if (S_ISFIFO(mode)) {
-        ft = file_type::fifo;
-    }
-    else if (S_ISLNK(mode)) {
-        ft = file_type::symlink;
-    }
-    else if (S_ISSOCK(mode)) {
-        ft = file_type::socket;
-    }
-    perms prms = static_cast<perms>(mode & 0xfff);
-    return file_status(ft, prms);
-#endif
-}
-
-#ifdef GHC_OS_WINDOWS
-#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE
-typedef struct _REPARSE_DATA_BUFFER
-{
-    ULONG ReparseTag;
-    USHORT ReparseDataLength;
-    USHORT Reserved;
-    union
-    {
-        struct
-        {
-            USHORT SubstituteNameOffset;
-            USHORT SubstituteNameLength;
-            USHORT PrintNameOffset;
-            USHORT PrintNameLength;
-            ULONG Flags;
-            WCHAR PathBuffer[1];
-        } SymbolicLinkReparseBuffer;
-        struct
-        {
-            USHORT SubstituteNameOffset;
-            USHORT SubstituteNameLength;
-            USHORT PrintNameOffset;
-            USHORT PrintNameLength;
-            WCHAR PathBuffer[1];
-        } MountPointReparseBuffer;
-        struct
-        {
-            UCHAR DataBuffer[1];
-        } GenericReparseBuffer;
-    } DUMMYUNIONNAME;
-} REPARSE_DATA_BUFFER;
-#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
-#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024)
-#endif
-#endif
-
-GHC_INLINE std::shared_ptr<REPARSE_DATA_BUFFER> getReparseData(const path& p, std::error_code& ec)
-{
-    std::shared_ptr<void> file(CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
-    if (file.get() == INVALID_HANDLE_VALUE) {
-        ec = detail::make_system_error();
-        return nullptr;
-    }
-
-    std::shared_ptr<REPARSE_DATA_BUFFER> reparseData((REPARSE_DATA_BUFFER*)std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE), std::free);
-    ULONG bufferUsed;
-    if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) {
-        return reparseData;
-    }
-    else {
-        ec = detail::make_system_error();
-    }
-    return nullptr;
-}
-#endif
-
-GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec)
-{
-#ifdef GHC_OS_WINDOWS
-    path result;
-    auto reparseData = detail::getReparseData(p, ec);
-    if (!ec) {
-        if (reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag)) {
-            switch (reparseData->ReparseTag) {
-                case IO_REPARSE_TAG_SYMLINK: {
-                    auto printName = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR));
-                    auto substituteName =
-                        std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR));
-                    if (detail::endsWith(substituteName, printName) && detail::startsWith(substituteName, std::wstring(L"\\??\\"))) {
-                        result = printName;
-                    }
-                    else {
-                        result = substituteName;
-                    }
-                    if (reparseData->SymbolicLinkReparseBuffer.Flags & 0x1 /*SYMLINK_FLAG_RELATIVE*/) {
-                        result = p.parent_path() / result;
-                    }
-                    break;
-                }
-                case IO_REPARSE_TAG_MOUNT_POINT:
-                    result = detail::getFullPathName(GHC_NATIVEWP(p), ec);
-                    //result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR));
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-    return result;
-#else
-    size_t bufferSize = 256;
-    while (true) {
-        std::vector<char> buffer(bufferSize, static_cast<char>(0));
-        auto rc = ::readlink(p.c_str(), buffer.data(), buffer.size());
-        if (rc < 0) {
-            ec = detail::make_system_error();
-            return path();
-        }
-        else if (rc < static_cast<int>(bufferSize)) {
-            return path(std::string(buffer.data(), static_cast<std::string::size_type>(rc)));
-        }
-        bufferSize *= 2;
-    }
-    return path();
-#endif
-}
-
-#ifdef GHC_OS_WINDOWS
-GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft)
-{
-    ULARGE_INTEGER ull;
-    ull.LowPart = ft.dwLowDateTime;
-    ull.HighPart = ft.dwHighDateTime;
-    return static_cast<time_t>(ull.QuadPart / 10000000ULL - 11644473600ULL);
-}
-
-GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft)
-{
-    LONGLONG ll;
-    ll = Int32x32To64(t, 10000000) + 116444736000000000;
-    ft.dwLowDateTime = static_cast<DWORD>(ll);
-    ft.dwHighDateTime = static_cast<DWORD>(ll >> 32);
-}
-
-template <typename INFO>
-GHC_INLINE uintmax_t hard_links_from_INFO(const INFO* info)
-{
-    return static_cast<uintmax_t>(-1);
-}
-
-template <>
-GHC_INLINE uintmax_t hard_links_from_INFO<BY_HANDLE_FILE_INFORMATION>(const BY_HANDLE_FILE_INFORMATION* info)
-{
-    return info->nNumberOfLinks;
-}
-
-template <typename INFO>
-GHC_INLINE DWORD reparse_tag_from_INFO(const INFO*)
-{
-    return 0;
-}
-
-template <>
-GHC_INLINE DWORD reparse_tag_from_INFO(const WIN32_FIND_DATAW* info)
-{
-    return info->dwReserved0;
-}
-
-template <typename INFO>
-GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code& ec, uintmax_t* sz = nullptr, time_t* lwt = nullptr)
-{
-    file_type ft = file_type::unknown;
-    if (sizeof(INFO) == sizeof(WIN32_FIND_DATAW)) {
-        if (detail::reparse_tag_from_INFO(info) == IO_REPARSE_TAG_SYMLINK) {
-            ft = file_type::symlink;
-        }
-    }
-    else {
-        if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
-            auto reparseData = detail::getReparseData(p, ec);
-            if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
-                ft = file_type::symlink;
-            }
-        }
-    }
-    if (ft == file_type::unknown) {
-        if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
-            ft = file_type::directory;
-        }
-        else {
-            ft = file_type::regular;
-        }
-    }
-    perms prms = perms::owner_read | perms::group_read | perms::others_read;
-    if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
-        prms = prms | perms::owner_write | perms::group_write | perms::others_write;
-    }
-    if (has_executable_extension(p)) {
-        prms = prms | perms::owner_exec | perms::group_exec | perms::others_exec;
-    }
-    if (sz) {
-        *sz = static_cast<uintmax_t>(info->nFileSizeHigh) << (sizeof(info->nFileSizeHigh) * 8) | info->nFileSizeLow;
-    }
-    if (lwt) {
-        *lwt = detail::timeFromFILETIME(info->ftLastWriteTime);
-    }
-    return file_status(ft, prms);
-}
-
-#endif
-
-GHC_INLINE bool is_not_found_error(std::error_code& ec)
-{
-#ifdef GHC_OS_WINDOWS
-    return ec.value() == ERROR_FILE_NOT_FOUND || ec.value() == ERROR_PATH_NOT_FOUND || ec.value() == ERROR_INVALID_NAME;
-#else
-    return ec.value() == ENOENT || ec.value() == ENOTDIR;
-#endif
-}
-
-GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr) noexcept
-{
-#ifdef GHC_OS_WINDOWS
-    file_status fs;
-    WIN32_FILE_ATTRIBUTE_DATA attr;
-    if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) {
-        ec = detail::make_system_error();
-    }
-    else {
-        ec.clear();
-        fs = detail::status_from_INFO(p, &attr, ec, sz, lwt);
-        if (nhl) {
-            *nhl = 0;
-        }
-    }
-    if (detail::is_not_found_error(ec)) {
-        return file_status(file_type::not_found);
-    }
-    return ec ? file_status(file_type::none) : fs;
-#else
-    (void)sz;
-    (void)nhl;
-    (void)lwt;
-    struct ::stat fs;
-    auto result = ::lstat(p.c_str(), &fs);
-    if (result == 0) {
-        ec.clear();
-        file_status f_s = detail::file_status_from_st_mode(fs.st_mode);
-        return f_s;
-    }
-    ec = detail::make_system_error();
-    if (detail::is_not_found_error(ec)) {
-        return file_status(file_type::not_found, perms::unknown);
-    }
-    return file_status(file_type::none);
-#endif
-}
-
-GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status* sls = nullptr, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr, int recurse_count = 0) noexcept
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    if (recurse_count > 16) {
-        ec = detail::make_system_error(0x2A9 /*ERROR_STOPPED_ON_SYMLINK*/);
-        return file_status(file_type::unknown);
-    }
-    WIN32_FILE_ATTRIBUTE_DATA attr;
-    if (!::GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) {
-        ec = detail::make_system_error();
-    }
-    else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-        auto reparseData = detail::getReparseData(p, ec);
-        if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
-            path target = resolveSymlink(p, ec);
-            file_status result;
-            if (!ec && !target.empty()) {
-                if (sls) {
-                    *sls = status_from_INFO(p, &attr, ec);
-                }
-                return detail::status_ex(target, ec, nullptr, sz, nhl, lwt, recurse_count + 1);
-            }
-            return file_status(file_type::unknown);
-        }
-    }
-    if (ec) {
-        if (detail::is_not_found_error(ec)) {
-            return file_status(file_type::not_found);
-        }
-        return file_status(file_type::none);
-    }
-    if (nhl) {
-        *nhl = 0;
-    }
-    return detail::status_from_INFO(p, &attr, ec, sz, lwt);
-#else
-    (void)recurse_count;
-    struct ::stat st;
-    auto result = ::lstat(p.c_str(), &st);
-    if (result == 0) {
-        ec.clear();
-        file_status fs = detail::file_status_from_st_mode(st.st_mode);
-        if (sls) {
-            *sls = fs;
-        }
-        if (fs.type() == file_type::symlink) {
-            result = ::stat(p.c_str(), &st);
-            if (result == 0) {
-                fs = detail::file_status_from_st_mode(st.st_mode);
-            }
-            else {
-                ec = detail::make_system_error();
-                if (detail::is_not_found_error(ec)) {
-                    return file_status(file_type::not_found, perms::unknown);
-                }
-                return file_status(file_type::none);
-            }
-        }
-        if (sz) {
-            *sz = static_cast<uintmax_t>(st.st_size);
-        }
-        if (nhl) {
-            *nhl = st.st_nlink;
-        }
-        if (lwt) {
-            *lwt = st.st_mtime;
-        }
-        return fs;
-    }
-    else {
-        ec = detail::make_system_error();
-        if (detail::is_not_found_error(ec)) {
-            return file_status(file_type::not_found, perms::unknown);
-        }
-        return file_status(file_type::none);
-    }
-#endif
-}
-
-}  // namespace detail
-
-GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv)
-    : _argc(argc)
-    , _argv(argv)
-    , _refargc(argc)
-    , _refargv(argv)
-    , _isvalid(false)
-{
-#ifdef GHC_OS_WINDOWS
-    LPWSTR* p;
-    p = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
-    _args.reserve(static_cast<size_t>(argc));
-    _argp.reserve(static_cast<size_t>(argc));
-    for (size_t i = 0; i < static_cast<size_t>(argc); ++i) {
-        _args.push_back(detail::toUtf8(std::wstring(p[i])));
-        _argp.push_back((char*)_args[i].data());
-    }
-    argv = _argp.data();
-    ::LocalFree(p);
-    _isvalid = true;
-#else
-    std::setlocale(LC_ALL, "");
-#if defined(__ANDROID__) && __ANDROID_API__ < 26
-    _isvalid = true;
-#else
-    if (detail::equals_simple_insensitive(::nl_langinfo(CODESET), "UTF-8")) {
-        _isvalid = true;
-    }
-#endif
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// [fs.path.construct] constructors and destructor
-
-GHC_INLINE path::path() noexcept {}
-
-GHC_INLINE path::path(const path& p)
-    : _path(p._path)
-#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
-    , _prefixLength(p._prefixLength)
-#endif
-{
-}
-
-GHC_INLINE path::path(path&& p) noexcept
-    : _path(std::move(p._path))
-#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
-    , _prefixLength(p._prefixLength)
-#endif
-{
-}
-
-GHC_INLINE path::path(string_type&& source, format fmt)
-    : _path(std::move(source))
-{
-    postprocess_path_with_format(fmt);
-}
-
-#endif  // GHC_EXPAND_IMPL
-
-#ifdef GHC_WITH_EXCEPTIONS
-template <class Source, typename>
-inline path::path(const Source& source, const std::locale& loc, format fmt)
-    : path(source, fmt)
-{
-    std::string locName = loc.name();
-    if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) {
-        throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported));
-    }
-}
-
-template <class InputIterator>
-inline path::path(InputIterator first, InputIterator last, const std::locale& loc, format fmt)
-    : path(std::basic_string<typename std::iterator_traits<InputIterator>::value_type>(first, last), fmt)
-{
-    std::string locName = loc.name();
-    if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) {
-        throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported));
-    }
-}
-#endif
-
-#ifdef GHC_EXPAND_IMPL
-
-GHC_INLINE path::~path() {}
-
-//-----------------------------------------------------------------------------
-// [fs.path.assign] assignments
-
-GHC_INLINE path& path::operator=(const path& p)
-{
-    _path = p._path;
-#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
-    _prefixLength = p._prefixLength;
-#endif
-    return *this;
-}
-
-GHC_INLINE path& path::operator=(path&& p) noexcept
-{
-    _path = std::move(p._path);
-#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
-    _prefixLength = p._prefixLength;
-#endif
-    return *this;
-}
-
-GHC_INLINE path& path::operator=(path::string_type&& source)
-{
-    return assign(source);
-}
-
-GHC_INLINE path& path::assign(path::string_type&& source)
-{
-    _path = std::move(source);
-    postprocess_path_with_format(native_format);
-    return *this;
-}
-
-#endif  // GHC_EXPAND_IMPL
-
-template <class Source>
-inline path& path::operator=(const Source& source)
-{
-    return assign(source);
-}
-
-template <class Source>
-inline path& path::assign(const Source& source)
-{
-#ifdef GHC_USE_WCHAR_T
-    _path.assign(detail::toWChar(source));
-#else
-    _path.assign(detail::toUtf8(source));
-#endif
-    postprocess_path_with_format(native_format);
-    return *this;
-}
-
-template <>
-inline path& path::assign<path>(const path& source)
-{
-    _path = source._path;
-#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
-    _prefixLength = source._prefixLength;
-#endif
-    return *this;
-}
-
-template <class InputIterator>
-inline path& path::assign(InputIterator first, InputIterator last)
-{
-    _path.assign(first, last);
-    postprocess_path_with_format(native_format);
-    return *this;
-}
-
-#ifdef GHC_EXPAND_IMPL
-
-//-----------------------------------------------------------------------------
-// [fs.path.append] appends
-
-GHC_INLINE path& path::operator/=(const path& p)
-{
-    if (p.empty()) {
-        // was: if ((!has_root_directory() && is_absolute()) || has_filename())
-        if (!_path.empty() && _path[_path.length() - 1] != preferred_separator && _path[_path.length() - 1] != ':') {
-            _path += preferred_separator;
-        }
-        return *this;
-    }
-    if ((p.is_absolute() && (_path != root_name()._path || p._path != "/")) || (p.has_root_name() && p.root_name() != root_name())) {
-        assign(p);
-        return *this;
-    }
-    if (p.has_root_directory()) {
-        assign(root_name());
-    }
-    else if ((!has_root_directory() && is_absolute()) || has_filename()) {
-        _path += preferred_separator;
-    }
-    auto iter = p.begin();
-    bool first = true;
-    if (p.has_root_name()) {
-        ++iter;
-    }
-    while (iter != p.end()) {
-        if (!first && !(!_path.empty() && _path[_path.length() - 1] == preferred_separator)) {
-            _path += preferred_separator;
-        }
-        first = false;
-        _path += (*iter++).native();
-    }
-    check_long_path();
-    return *this;
-}
-
-GHC_INLINE void path::append_name(const value_type* name)
-{
-    if (_path.empty()) {
-        this->operator/=(path(name));
-    }
-    else {
-        if (_path.back() != path::preferred_separator) {
-            _path.push_back(path::preferred_separator);
-        }
-        _path += name;
-        check_long_path();
-    }
-}
-
-#endif  // GHC_EXPAND_IMPL
-
-template <class Source>
-inline path& path::operator/=(const Source& source)
-{
-    return append(source);
-}
-
-template <class Source>
-inline path& path::append(const Source& source)
-{
-    return this->operator/=(path(source));
-}
-
-template <>
-inline path& path::append<path>(const path& p)
-{
-    return this->operator/=(p);
-}
-
-template <class InputIterator>
-inline path& path::append(InputIterator first, InputIterator last)
-{
-    std::basic_string<typename std::iterator_traits<InputIterator>::value_type> part(first, last);
-    return append(part);
-}
-
-#ifdef GHC_EXPAND_IMPL
-
-//-----------------------------------------------------------------------------
-// [fs.path.concat] concatenation
-
-GHC_INLINE path& path::operator+=(const path& x)
-{
-    return concat(x._path);
-}
-
-GHC_INLINE path& path::operator+=(const string_type& x)
-{
-    return concat(x);
-}
-
-#ifdef GHC_WITH_STRING_VIEW
-GHC_INLINE path& path::operator+=(basic_string_view<value_type> x)
-{
-    return concat(x);
-}
-#endif
-
-GHC_INLINE path& path::operator+=(const value_type* x)
-{
-#ifdef GHC_WITH_STRING_VIEW
-    basic_string_view<value_type> part(x);
-#else
-    string_type part(x);
-#endif
-    return concat(part);
-}
-
-GHC_INLINE path& path::operator+=(value_type x)
-{
-#ifdef GHC_OS_WINDOWS
-    if (x == generic_separator) {
-        x = preferred_separator;
-    }
-#endif
-    if (_path.empty() || _path.back() != preferred_separator) {
-        _path += x;
-    }
-    check_long_path();
-    return *this;
-}
-
-#endif  // GHC_EXPAND_IMPL
-
-template <class Source>
-inline path::path_from_string<Source>& path::operator+=(const Source& x)
-{
-    return concat(x);
-}
-
-template <class EcharT>
-inline path::path_type_EcharT<EcharT>& path::operator+=(EcharT x)
-{
-#ifdef GHC_WITH_STRING_VIEW
-    basic_string_view<EcharT> part(&x, 1);
-#else
-    std::basic_string<EcharT> part(1, x);
-#endif
-    concat(part);
-    return *this;
-}
-
-template <class Source>
-inline path& path::concat(const Source& x)
-{
-    path p(x);
-    _path += p._path;
-    postprocess_path_with_format(native_format);
-    return *this;
-}
-template <class InputIterator>
-inline path& path::concat(InputIterator first, InputIterator last)
-{
-    _path.append(first, last);
-    postprocess_path_with_format(native_format);
-    return *this;
-}
-
-#ifdef GHC_EXPAND_IMPL
-
-//-----------------------------------------------------------------------------
-// [fs.path.modifiers] modifiers
-GHC_INLINE void path::clear() noexcept
-{
-    _path.clear();
-#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
-    _prefixLength = 0;
-#endif
-}
-
-GHC_INLINE path& path::make_preferred()
-{
-    // as this filesystem implementation only uses generic_format
-    // internally, this must be a no-op
-    return *this;
-}
-
-GHC_INLINE path& path::remove_filename()
-{
-    if (has_filename()) {
-        _path.erase(_path.size() - filename()._path.size());
-    }
-    return *this;
-}
-
-GHC_INLINE path& path::replace_filename(const path& replacement)
-{
-    remove_filename();
-    return append(replacement);
-}
-
-GHC_INLINE path& path::replace_extension(const path& replacement)
-{
-    if (has_extension()) {
-        _path.erase(_path.size() - extension()._path.size());
-    }
-    if (!replacement.empty() && replacement._path[0] != '.') {
-        _path += '.';
-    }
-    return concat(replacement);
-}
-
-GHC_INLINE void path::swap(path& rhs) noexcept
-{
-    _path.swap(rhs._path);
-#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
-    std::swap(_prefixLength, rhs._prefixLength);
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// [fs.path.native.obs] native format observers
-GHC_INLINE const path::string_type& path::native() const noexcept
-{
-    return _path;
-}
-
-GHC_INLINE const path::value_type* path::c_str() const noexcept
-{
-    return native().c_str();
-}
-
-GHC_INLINE path::operator path::string_type() const
-{
-    return native();
-}
-
-#endif  // GHC_EXPAND_IMPL
-
-template <class EcharT, class traits, class Allocator>
-inline std::basic_string<EcharT, traits, Allocator> path::string(const Allocator& a) const
-{
-#ifdef GHC_USE_WCHAR_T
-    return detail::fromWChar<std::basic_string<EcharT, traits, Allocator>>(_path, a);
-#else
-    return detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(_path, a);
-#endif
-}
-
-#ifdef GHC_EXPAND_IMPL
-
-GHC_INLINE std::string path::string() const
-{
-#ifdef GHC_USE_WCHAR_T
-    return detail::toUtf8(native());
-#else
-    return native();
-#endif
-}
-
-GHC_INLINE std::wstring path::wstring() const
-{
-#ifdef GHC_USE_WCHAR_T
-    return native();
-#else
-    return detail::fromUtf8<std::wstring>(native());
-#endif
-}
-
-#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
-GHC_INLINE std::u8string path::u8string() const
-{
-#ifdef GHC_USE_WCHAR_T
-    return std::u8string(reinterpret_cast<const char8_t*>(detail::toUtf8(native()).c_str()));
-#else
-    return std::u8string(reinterpret_cast<const char8_t*>(c_str()));
-#endif
-}
-#else
-GHC_INLINE std::string path::u8string() const
-{
-#ifdef GHC_USE_WCHAR_T
-    return detail::toUtf8(native());
-#else
-    return native();
-#endif
-}
-#endif
-
-GHC_INLINE std::u16string path::u16string() const
-{
-    // TODO: optimize
-    return detail::fromUtf8<std::u16string>(string());
-}
-
-GHC_INLINE std::u32string path::u32string() const
-{
-    // TODO: optimize
-    return detail::fromUtf8<std::u32string>(string());
-}
-
-#endif  // GHC_EXPAND_IMPL
-
-//-----------------------------------------------------------------------------
-// [fs.path.generic.obs] generic format observers
-template <class EcharT, class traits, class Allocator>
-inline std::basic_string<EcharT, traits, Allocator> path::generic_string(const Allocator& a) const
-{
-#ifdef GHC_OS_WINDOWS
-#ifdef GHC_USE_WCHAR_T
-    auto result = detail::fromWChar<std::basic_string<EcharT, traits, Allocator>, path::string_type>(_path, a);
-#else
-    auto result = detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(_path, a);
-#endif
-    for (auto& c : result) {
-        if (c == preferred_separator) {
-            c = generic_separator;
-        }
-    }
-    return result;
-#else
-    return detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(_path, a);
-#endif
-}
-
-#ifdef GHC_EXPAND_IMPL
-
-GHC_INLINE std::string path::generic_string() const
-{
-#ifdef GHC_OS_WINDOWS
-    return generic_string<std::string::value_type, std::string::traits_type, std::string::allocator_type>();
-#else
-    return _path;
-#endif
-}
-
-GHC_INLINE std::wstring path::generic_wstring() const
-{
-#ifdef GHC_OS_WINDOWS
-    return generic_string<std::wstring::value_type, std::wstring::traits_type, std::wstring::allocator_type>();
-#else
-    return detail::fromUtf8<std::wstring>(_path);
-#endif
-}  // namespace filesystem
-
-#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
-GHC_INLINE std::u8string path::generic_u8string() const
-{
-#ifdef GHC_OS_WINDOWS
-    return generic_string<std::u8string::value_type, std::u8string::traits_type, std::u8string::allocator_type>();
-#else
-    return std::u8string(reinterpret_cast<const char8_t*>(_path.c_str()));
-#endif
-}
-#else
-GHC_INLINE std::string path::generic_u8string() const
-{
-#ifdef GHC_OS_WINDOWS
-    return generic_string<std::string::value_type, std::string::traits_type, std::string::allocator_type>();
-#else
-    return _path;
-#endif
-}
-#endif
-
-GHC_INLINE std::u16string path::generic_u16string() const
-{
-#ifdef GHC_OS_WINDOWS
-    return generic_string<std::u16string::value_type, std::u16string::traits_type, std::u16string::allocator_type>();
-#else
-    return detail::fromUtf8<std::u16string>(_path);
-#endif
-}
-
-GHC_INLINE std::u32string path::generic_u32string() const
-{
-#ifdef GHC_OS_WINDOWS
-    return generic_string<std::u32string::value_type, std::u32string::traits_type, std::u32string::allocator_type>();
-#else
-    return detail::fromUtf8<std::u32string>(_path);
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// [fs.path.compare] compare
-GHC_INLINE int path::compare(const path& p) const noexcept
-{
-#ifdef LWG_2936_BEHAVIOUR
-    auto rnl1 = root_name_length();
-    auto rnl2 = p.root_name_length();
-#ifdef GHC_OS_WINDOWS
-    auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2);
-#else
-    auto rnc = _path.compare(0, rnl1, p._path, 0, (std::min(rnl1, rnl2)));
-#endif
-    if (rnc) {
-        return rnc;
-    }
-    bool hrd1 = has_root_directory(), hrd2 = p.has_root_directory();
-    if (hrd1 != hrd2) {
-        return hrd1 ? 1 : -1;
-    }
-    if (hrd1) {
-        ++rnl1;
-        ++rnl2;
-    }
-    auto iter1 = _path.begin() + static_cast<int>(rnl1);
-    auto iter2 = p._path.begin() + static_cast<int>(rnl2);
-    while (iter1 != _path.end() && iter2 != p._path.end() && *iter1 == *iter2) {
-        ++iter1;
-        ++iter2;
-    }
-    if (iter1 == _path.end()) {
-        return iter2 == p._path.end() ? 0 : -1;
-    }
-    if (iter2 == p._path.end()) {
-        return 1;
-    }
-    if (*iter1 == preferred_separator) {
-        return -1;
-    }
-    if (*iter2 == preferred_separator) {
-        return 1;
-    }
-    return *iter1 < *iter2 ? -1 : 1;
-#else  // LWG_2936_BEHAVIOUR
-#ifdef GHC_OS_WINDOWS
-    auto rnl1 = root_name_length();
-    auto rnl2 = p.root_name_length();
-    auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2);
-    if (rnc) {
-        return rnc;
-    }
-    return _path.compare(rnl1, std::string::npos, p._path, rnl2, std::string::npos);
-#else
-    return _path.compare(p._path);
-#endif
-#endif
-}
-
-GHC_INLINE int path::compare(const string_type& s) const
-{
-    return compare(path(s));
-}
-
-#ifdef GHC_WITH_STRING_VIEW
-GHC_INLINE int path::compare(basic_string_view<value_type> s) const
-{
-    return compare(path(s));
-}
-#endif
-
-GHC_INLINE int path::compare(const value_type* s) const
-{
-    return compare(path(s));
-}
-
-//-----------------------------------------------------------------------------
-// [fs.path.decompose] decomposition
-#ifdef GHC_OS_WINDOWS
-GHC_INLINE void path::handle_prefixes()
-{
-#if defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
-    _prefixLength = 0;
-    if (_path.length() >= 6 && _path[2] == '?' && std::toupper(static_cast<unsigned char>(_path[4])) >= 'A' && std::toupper(static_cast<unsigned char>(_path[4])) <= 'Z' && _path[5] == ':') {
-        if (detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\"))) || detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\??\\")))) {
-            _prefixLength = 4;
-        }
-    }
-#endif  // GHC_WIN_AUTO_PREFIX_LONG_PATH
-}
-#endif
-
-GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept
-{
-#ifdef GHC_OS_WINDOWS
-    if (_path.length() >= _prefixLength + 2 && std::toupper(static_cast<unsigned char>(_path[_prefixLength])) >= 'A' && std::toupper(static_cast<unsigned char>(_path[_prefixLength])) <= 'Z' && _path[_prefixLength + 1] == ':') {
-        return 2;
-    }
-#endif
-    if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator && std::isprint(_path[_prefixLength + 2])) {
-        impl_string_type::size_type pos = _path.find(preferred_separator, _prefixLength + 3);
-        if (pos == impl_string_type::npos) {
-            return _path.length();
-        }
-        else {
-            return pos;
-        }
-    }
-    return 0;
-}
-
-GHC_INLINE path path::root_name() const
-{
-    return path(_path.substr(_prefixLength, root_name_length()), native_format);
-}
-
-GHC_INLINE path path::root_directory() const
-{
-    if (has_root_directory()) {
-        static const path _root_dir(std::string(1, preferred_separator), native_format);
-        return _root_dir;
-    }
-    return path();
-}
-
-GHC_INLINE path path::root_path() const
-{
-    return path(root_name().string() + root_directory().string(), native_format);
-}
-
-GHC_INLINE path path::relative_path() const
-{
-    auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0);
-    return path(_path.substr((std::min)(rootPathLen, _path.length())), generic_format);
-}
-
-GHC_INLINE path path::parent_path() const
-{
-    auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0);
-    if (rootPathLen < _path.length()) {
-        if (empty()) {
-            return path();
-        }
-        else {
-            auto piter = end();
-            auto iter = piter.decrement(_path.end());
-            if (iter > _path.begin() + static_cast<long>(rootPathLen) && *iter != preferred_separator) {
-                --iter;
-            }
-            return path(_path.begin(), iter, native_format);
-        }
-    }
-    else {
-        return *this;
-    }
-}
-
-GHC_INLINE path path::filename() const
-{
-    return !has_relative_path() ? path() : path(*--end());
-}
-
-GHC_INLINE path path::stem() const
-{
-    impl_string_type fn = filename().native();
-    if (fn != "." && fn != "..") {
-        impl_string_type::size_type pos = fn.rfind('.');
-        if (pos != impl_string_type::npos && pos > 0) {
-            return path{fn.substr(0, pos), native_format};
-        }
-    }
-    return path{fn, native_format};
-}
-
-GHC_INLINE path path::extension() const
-{
-    if (has_relative_path()) {
-        auto iter = end();
-        const auto& fn = *--iter;
-        impl_string_type::size_type pos = fn._path.rfind('.');
-        if (pos != std::string::npos && pos > 0) {
-            return path(fn._path.substr(pos), native_format);
-        }
-    }
-    return path();
-}
-
-#ifdef GHC_OS_WINDOWS
-namespace detail {
-GHC_INLINE bool has_executable_extension(const path& p)
-{
-    if (p.has_relative_path()) {
-        auto iter = p.end();
-        const auto& fn = *--iter;
-        auto pos = fn._path.find_last_of('.');
-        if (pos == std::string::npos || pos == 0 || fn._path.length() - pos != 3) {
-            return false;
-        }
-        const path::value_type* ext = fn._path.c_str() + pos + 1;
-        if (detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("exe")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("cmd")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("bat")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) {
-            return true;
-        }
-    }
-    return false;
-}
-}  // namespace detail
-#endif
-
-//-----------------------------------------------------------------------------
-// [fs.path.query] query
-GHC_INLINE bool path::empty() const noexcept
-{
-    return _path.empty();
-}
-
-GHC_INLINE bool path::has_root_name() const
-{
-    return root_name_length() > 0;
-}
-
-GHC_INLINE bool path::has_root_directory() const
-{
-    auto rootLen = _prefixLength + root_name_length();
-    return (_path.length() > rootLen && _path[rootLen] == preferred_separator);
-}
-
-GHC_INLINE bool path::has_root_path() const
-{
-    return has_root_name() || has_root_directory();
-}
-
-GHC_INLINE bool path::has_relative_path() const
-{
-    auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0);
-    return rootPathLen < _path.length();
-}
-
-GHC_INLINE bool path::has_parent_path() const
-{
-    return !parent_path().empty();
-}
-
-GHC_INLINE bool path::has_filename() const
-{
-    return has_relative_path() && !filename().empty();
-}
-
-GHC_INLINE bool path::has_stem() const
-{
-    return !stem().empty();
-}
-
-GHC_INLINE bool path::has_extension() const
-{
-    return !extension().empty();
-}
-
-GHC_INLINE bool path::is_absolute() const
-{
-#ifdef GHC_OS_WINDOWS
-    return has_root_name() && has_root_directory();
-#else
-    return has_root_directory();
-#endif
-}
-
-GHC_INLINE bool path::is_relative() const
-{
-    return !is_absolute();
-}
-
-//-----------------------------------------------------------------------------
-// [fs.path.gen] generation
-GHC_INLINE path path::lexically_normal() const
-{
-    path dest;
-    bool lastDotDot = false;
-    for (string_type s : *this) {
-        if (s == ".") {
-            dest /= "";
-            continue;
-        }
-        else if (s == ".." && !dest.empty()) {
-            auto root = root_path();
-            if (dest == root) {
-                continue;
-            }
-            else if (*(--dest.end()) != "..") {
-                if (dest._path.back() == preferred_separator) {
-                    dest._path.pop_back();
-                }
-                dest.remove_filename();
-                continue;
-            }
-        }
-        if (!(s.empty() && lastDotDot)) {
-            dest /= s;
-        }
-        lastDotDot = s == "..";
-    }
-    if (dest.empty()) {
-        dest = ".";
-    }
-    return dest;
-}
-
-GHC_INLINE path path::lexically_relative(const path& base) const
-{
-    if (root_name() != base.root_name() || is_absolute() != base.is_absolute() || (!has_root_directory() && base.has_root_directory())) {
-        return path();
-    }
-    const_iterator a = begin(), b = base.begin();
-    while (a != end() && b != base.end() && *a == *b) {
-        ++a;
-        ++b;
-    }
-    if (a == end() && b == base.end()) {
-        return path(".");
-    }
-    int count = 0;
-    for (const auto& element : input_iterator_range<const_iterator>(b, base.end())) {
-        if (element != "." && element != "" && element != "..") {
-            ++count;
-        }
-        else if (element == "..") {
-            --count;
-        }
-    }
-    if (count < 0) {
-        return path();
-    }
-    path result;
-    for (int i = 0; i < count; ++i) {
-        result /= "..";
-    }
-    for (const auto& element : input_iterator_range<const_iterator>(a, end())) {
-        result /= element;
-    }
-    return result;
-}
-
-GHC_INLINE path path::lexically_proximate(const path& base) const
-{
-    path result = lexically_relative(base);
-    return result.empty() ? *this : result;
-}
-
-//-----------------------------------------------------------------------------
-// [fs.path.itr] iterators
-GHC_INLINE path::iterator::iterator() {}
-
-GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const_iterator& pos)
-    : _first(p._path.begin())
-    , _last(p._path.end())
-    , _prefix(_first + static_cast<string_type::difference_type>(p._prefixLength))
-    , _root(p.has_root_directory() ? _first + static_cast<string_type::difference_type>(p._prefixLength + p.root_name_length()) : _last)
-    , _iter(pos)
-{
-    if(pos != _last) {
-        updateCurrent();
-    }
-}
-
-GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const
-{
-    path::impl_string_type::const_iterator i = pos;
-    bool fromStart = i == _first || i == _prefix;
-    if (i != _last) {
-        if (fromStart && i == _first && _prefix > _first) {
-            i = _prefix;
-        }
-        else if (*i++ == preferred_separator) {
-            // we can only sit on a slash if it is a network name or a root
-            if (i != _last && *i == preferred_separator) {
-                if (fromStart && !(i + 1 != _last && *(i + 1) == preferred_separator)) {
-                    // leadind double slashes detected, treat this and the
-                    // following until a slash as one unit
-                    i = std::find(++i, _last, preferred_separator);
-                }
-                else {
-                    // skip redundant slashes
-                    while (i != _last && *i == preferred_separator) {
-                        ++i;
-                    }
-                }
-            }
-        }
-        else {
-            if (fromStart && i != _last && *i == ':') {
-                ++i;
-            }
-            else {
-                i = std::find(i, _last, preferred_separator);
-            }
-        }
-    }
-    return i;
-}
-
-GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(const path::impl_string_type::const_iterator& pos) const
-{
-    path::impl_string_type::const_iterator i = pos;
-    if (i != _first) {
-        --i;
-        // if this is now the root slash or the trailing slash, we are done,
-        // else check for network name
-        if (i != _root && (pos != _last || *i != preferred_separator)) {
-#ifdef GHC_OS_WINDOWS
-            static const impl_string_type seps = GHC_PLATFORM_LITERAL("\\:");
-            i = std::find_first_of(std::reverse_iterator<path::impl_string_type::const_iterator>(i), std::reverse_iterator<path::impl_string_type::const_iterator>(_first), seps.begin(), seps.end()).base();
-            if (i > _first && *i == ':') {
-                i++;
-            }
-#else
-            i = std::find(std::reverse_iterator<path::impl_string_type::const_iterator>(i), std::reverse_iterator<path::impl_string_type::const_iterator>(_first), preferred_separator).base();
-#endif
-            // Now we have to check if this is a network name
-            if (i - _first == 2 && *_first == preferred_separator && *(_first + 1) == preferred_separator) {
-                i -= 2;
-            }
-        }
-    }
-    return i;
-}
-
-GHC_INLINE void path::iterator::updateCurrent()
-{
-    if ((_iter == _last) || (_iter != _first && _iter != _last && (*_iter == preferred_separator && _iter != _root) && (_iter + 1 == _last))) {
-        _current.clear();
-    }
-    else {
-        _current.assign(_iter, increment(_iter));
-    }
-}
-
-GHC_INLINE path::iterator& path::iterator::operator++()
-{
-    _iter = increment(_iter);
-    while (_iter != _last &&                // we didn't reach the end
-           _iter != _root &&                // this is not a root position
-           *_iter == preferred_separator &&  // we are on a separator
-           (_iter + 1) != _last             // the slash is not the last char
-    ) {
-        ++_iter;
-    }
-    updateCurrent();
-    return *this;
-}
-
-GHC_INLINE path::iterator path::iterator::operator++(int)
-{
-    path::iterator i{*this};
-    ++(*this);
-    return i;
-}
-
-GHC_INLINE path::iterator& path::iterator::operator--()
-{
-    _iter = decrement(_iter);
-    updateCurrent();
-    return *this;
-}
-
-GHC_INLINE path::iterator path::iterator::operator--(int)
-{
-    auto i = *this;
-    --(*this);
-    return i;
-}
-
-GHC_INLINE bool path::iterator::operator==(const path::iterator& other) const
-{
-    return _iter == other._iter;
-}
-
-GHC_INLINE bool path::iterator::operator!=(const path::iterator& other) const
-{
-    return _iter != other._iter;
-}
-
-GHC_INLINE path::iterator::reference path::iterator::operator*() const
-{
-    return _current;
-}
-
-GHC_INLINE path::iterator::pointer path::iterator::operator->() const
-{
-    return &_current;
-}
-
-GHC_INLINE path::iterator path::begin() const
-{
-    return iterator(*this, _path.begin());
-}
-
-GHC_INLINE path::iterator path::end() const
-{
-    return iterator(*this, _path.end());
-}
-
-//-----------------------------------------------------------------------------
-// [fs.path.nonmember] path non-member functions
-GHC_INLINE void swap(path& lhs, path& rhs) noexcept
-{
-    swap(lhs._path, rhs._path);
-}
-
-GHC_INLINE size_t hash_value(const path& p) noexcept
-{
-    return std::hash<std::string>()(p.generic_string());
-}
-
-#ifdef GHC_HAS_THREEWAY_COMP
-GHC_INLINE std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept
-{
-    return lhs.compare(rhs) <=> 0;
-}
-#endif
-
-GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept
-{
-    return lhs.compare(rhs) == 0;
-}
-
-GHC_INLINE bool operator!=(const path& lhs, const path& rhs) noexcept
-{
-    return !(lhs == rhs);
-}
-
-GHC_INLINE bool operator<(const path& lhs, const path& rhs) noexcept
-{
-    return lhs.compare(rhs) < 0;
-}
-
-GHC_INLINE bool operator<=(const path& lhs, const path& rhs) noexcept
-{
-    return lhs.compare(rhs) <= 0;
-}
-
-GHC_INLINE bool operator>(const path& lhs, const path& rhs) noexcept
-{
-    return lhs.compare(rhs) > 0;
-}
-
-GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept
-{
-    return lhs.compare(rhs) >= 0;
-}
-
-GHC_INLINE path operator/(const path& lhs, const path& rhs)
-{
-    path result(lhs);
-    result /= rhs;
-    return result;
-}
-
-#endif  // GHC_EXPAND_IMPL
-
-//-----------------------------------------------------------------------------
-// [fs.path.io] path inserter and extractor
-template <class charT, class traits>
-inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const path& p)
-{
-    os << "\"";
-    auto ps = p.string<charT, traits>();
-    for (auto c : ps) {
-        if (c == '"' || c == '\\') {
-            os << '\\';
-        }
-        os << c;
-    }
-    os << "\"";
-    return os;
-}
-
-template <class charT, class traits>
-inline std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& is, path& p)
-{
-    std::basic_string<charT, traits> tmp;
-    charT c;
-    is >> c;
-    if (c == '"') {
-        auto sf = is.flags();
-        is >> std::noskipws;
-        while (is) {
-            auto c2 = is.get();
-            if (is) {
-                if (c2 == '\\') {
-                    c2 = is.get();
-                    if (is) {
-                        tmp += static_cast<charT>(c2);
-                    }
-                }
-                else if (c2 == '"') {
-                    break;
-                }
-                else {
-                    tmp += static_cast<charT>(c2);
-                }
-            }
-        }
-        if ((sf & std::ios_base::skipws) == std::ios_base::skipws) {
-            is >> std::skipws;
-        }
-        p = path(tmp);
-    }
-    else {
-        is >> tmp;
-        p = path(static_cast<charT>(c) + tmp);
-    }
-    return is;
-}
-
-#ifdef GHC_EXPAND_IMPL
-
-//-----------------------------------------------------------------------------
-// [fs.class.filesystem_error] Class filesystem_error
-GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, std::error_code ec)
-    : std::system_error(ec, what_arg)
-    , _what_arg(what_arg)
-    , _ec(ec)
-{
-}
-
-GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec)
-    : std::system_error(ec, what_arg)
-    , _what_arg(what_arg)
-    , _ec(ec)
-    , _p1(p1)
-{
-    if (!_p1.empty()) {
-        _what_arg += ": '" + _p1.string() + "'";
-    }
-}
-
-GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec)
-    : std::system_error(ec, what_arg)
-    , _what_arg(what_arg)
-    , _ec(ec)
-    , _p1(p1)
-    , _p2(p2)
-{
-    if (!_p1.empty()) {
-        _what_arg += ": '" + _p1.string() + "'";
-    }
-    if (!_p2.empty()) {
-        _what_arg += ", '" + _p2.string() + "'";
-    }
-}
-
-GHC_INLINE const path& filesystem_error::path1() const noexcept
-{
-    return _p1;
-}
-
-GHC_INLINE const path& filesystem_error::path2() const noexcept
-{
-    return _p2;
-}
-
-GHC_INLINE const char* filesystem_error::what() const noexcept
-{
-    return _what_arg.c_str();
-}
-
-//-----------------------------------------------------------------------------
-// [fs.op.funcs] filesystem operations
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE path absolute(const path& p)
-{
-    std::error_code ec;
-    path result = absolute(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE path absolute(const path& p, std::error_code& ec)
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    if (p.empty()) {
-        return absolute(current_path(ec), ec) / "";
-    }
-    ULONG size = ::GetFullPathNameW(GHC_NATIVEWP(p), 0, 0, 0);
-    if (size) {
-        std::vector<wchar_t> buf(size, 0);
-        ULONG s2 = GetFullPathNameW(GHC_NATIVEWP(p), size, buf.data(), nullptr);
-        if (s2 && s2 < size) {
-            path result = path(std::wstring(buf.data(), s2));
-            if (p.filename() == ".") {
-                result /= ".";
-            }
-            return result;
-        }
-    }
-    ec = detail::make_system_error();
-    return path();
-#else
-    path base = current_path(ec);
-    if (!ec) {
-        if (p.empty()) {
-            return base / p;
-        }
-        if (p.has_root_name()) {
-            if (p.has_root_directory()) {
-                return p;
-            }
-            else {
-                return p.root_name() / base.root_directory() / base.relative_path() / p.relative_path();
-            }
-        }
-        else {
-            if (p.has_root_directory()) {
-                return base.root_name() / p;
-            }
-            else {
-                return base / p;
-            }
-        }
-    }
-    ec = detail::make_system_error();
-    return path();
-#endif
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE path canonical(const path& p)
-{
-    std::error_code ec;
-    auto result = canonical(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE path canonical(const path& p, std::error_code& ec)
-{
-    if (p.empty()) {
-        ec = detail::make_error_code(detail::portable_error::not_found);
-        return path();
-    }
-    path work = p.is_absolute() ? p : absolute(p, ec);
-    path result;
-
-    auto fs = status(work, ec);
-    if (ec) {
-        return path();
-    }
-    if (fs.type() == file_type::not_found) {
-        ec = detail::make_error_code(detail::portable_error::not_found);
-        return path();
-    }
-    bool redo;
-    do {
-        auto rootPathLen = work._prefixLength + work.root_name_length() + (work.has_root_directory() ? 1 : 0);
-        redo = false;
-        result.clear();
-        for (auto pe : work) {
-            if (pe.empty() || pe == ".") {
-                continue;
-            }
-            else if (pe == "..") {
-                result = result.parent_path();
-                continue;
-            }
-            else if ((result / pe).string().length() <= rootPathLen) {
-                result /= pe;
-                continue;
-            }
-            auto sls = symlink_status(result / pe, ec);
-            if (ec) {
-                return path();
-            }
-            if (is_symlink(sls)) {
-                redo = true;
-                auto target = read_symlink(result / pe, ec);
-                if (ec) {
-                    return path();
-                }
-                if (target.is_absolute()) {
-                    result = target;
-                    continue;
-                }
-                else {
-                    result /= target;
-                    continue;
-                }
-            }
-            else {
-                result /= pe;
-            }
-        }
-        work = result;
-    } while (redo);
-    ec.clear();
-    return result;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void copy(const path& from, const path& to)
-{
-    copy(from, to, copy_options::none);
-}
-
-GHC_INLINE void copy(const path& from, const path& to, copy_options options)
-{
-    std::error_code ec;
-    copy(from, to, options, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec);
-    }
-}
-#endif
-
-GHC_INLINE void copy(const path& from, const path& to, std::error_code& ec) noexcept
-{
-    copy(from, to, copy_options::none, ec);
-}
-
-GHC_INLINE void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept
-{
-    std::error_code tec;
-    file_status fs_from, fs_to;
-    ec.clear();
-    if ((options & (copy_options::skip_symlinks | copy_options::copy_symlinks | copy_options::create_symlinks)) != copy_options::none) {
-        fs_from = symlink_status(from, ec);
-    }
-    else {
-        fs_from = status(from, ec);
-    }
-    if (!exists(fs_from)) {
-        if (!ec) {
-            ec = detail::make_error_code(detail::portable_error::not_found);
-        }
-        return;
-    }
-    if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none) {
-        fs_to = symlink_status(to, tec);
-    }
-    else {
-        fs_to = status(to, tec);
-    }
-    if (is_other(fs_from) || is_other(fs_to) || (is_directory(fs_from) && is_regular_file(fs_to)) || (exists(fs_to) && equivalent(from, to, ec))) {
-        ec = detail::make_error_code(detail::portable_error::invalid_argument);
-    }
-    else if (is_symlink(fs_from)) {
-        if ((options & copy_options::skip_symlinks) == copy_options::none) {
-            if (!exists(fs_to) && (options & copy_options::copy_symlinks) != copy_options::none) {
-                copy_symlink(from, to, ec);
-            }
-            else {
-                ec = detail::make_error_code(detail::portable_error::invalid_argument);
-            }
-        }
-    }
-    else if (is_regular_file(fs_from)) {
-        if ((options & copy_options::directories_only) == copy_options::none) {
-            if ((options & copy_options::create_symlinks) != copy_options::none) {
-                create_symlink(from.is_absolute() ? from : canonical(from, ec), to, ec);
-            }
-#ifndef GHC_OS_WEB
-            else if ((options & copy_options::create_hard_links) != copy_options::none) {
-                create_hard_link(from, to, ec);
-            }
-#endif
-            else if (is_directory(fs_to)) {
-                copy_file(from, to / from.filename(), options, ec);
-            }
-            else {
-                copy_file(from, to, options, ec);
-            }
-        }
-    }
-#ifdef LWG_2682_BEHAVIOUR
-    else if (is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) {
-        ec = detail::make_error_code(detail::portable_error::is_a_directory);
-    }
-#endif
-    else if (is_directory(fs_from) && (options == copy_options::none || (options & copy_options::recursive) != copy_options::none)) {
-        if (!exists(fs_to)) {
-            create_directory(to, from, ec);
-            if (ec) {
-                return;
-            }
-        }
-        for (auto iter = directory_iterator(from, ec); iter != directory_iterator(); iter.increment(ec)) {
-            if (!ec) {
-                copy(iter->path(), to / iter->path().filename(), options | static_cast<copy_options>(0x8000), ec);
-            }
-            if (ec) {
-                return;
-            }
-        }
-    }
-    return;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool copy_file(const path& from, const path& to)
-{
-    return copy_file(from, to, copy_options::none);
-}
-
-GHC_INLINE bool copy_file(const path& from, const path& to, copy_options option)
-{
-    std::error_code ec;
-    auto result = copy_file(from, to, option, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept
-{
-    return copy_file(from, to, copy_options::none, ec);
-}
-
-GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept
-{
-    std::error_code tecf, tect;
-    auto sf = status(from, tecf);
-    auto st = status(to, tect);
-    bool overwrite = false;
-    ec.clear();
-    if (!is_regular_file(sf)) {
-        ec = tecf;
-        return false;
-    }
-    if (exists(st) && (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none)) {
-        ec = tect ? tect : detail::make_error_code(detail::portable_error::exists);
-        return false;
-    }
-    if (exists(st)) {
-        if ((options & copy_options::update_existing) == copy_options::update_existing) {
-            auto from_time = last_write_time(from, ec);
-            if (ec) {
-                ec = detail::make_system_error();
-                return false;
-            }
-            auto to_time = last_write_time(to, ec);
-            if (ec) {
-                ec = detail::make_system_error();
-                return false;
-            }
-            if (from_time <= to_time) {
-                return false;
-            }
-        }
-        overwrite = true;
-    }
-#ifdef GHC_OS_WINDOWS
-    if (!::CopyFileW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), !overwrite)) {
-        ec = detail::make_system_error();
-        return false;
-    }
-    return true;
-#else
-    std::vector<char> buffer(16384, '\0');
-    int in = -1, out = -1;
-    if ((in = ::open(from.c_str(), O_RDONLY)) < 0) {
-        ec = detail::make_system_error();
-        return false;
-    }
-    int mode = O_CREAT | O_WRONLY | O_TRUNC;
-    if (!overwrite) {
-        mode |= O_EXCL;
-    }
-    if ((out = ::open(to.c_str(), mode, static_cast<int>(sf.permissions() & perms::all))) < 0) {
-        ec = detail::make_system_error();
-        ::close(in);
-        return false;
-    }
-    ssize_t br, bw;
-    while ((br = ::read(in, buffer.data(), buffer.size())) > 0) {
-        ssize_t offset = 0;
-        do {
-            if ((bw = ::write(out, buffer.data() + offset, static_cast<size_t>(br))) > 0) {
-                br -= bw;
-                offset += bw;
-            }
-            else if (bw < 0) {
-                ec = detail::make_system_error();
-                ::close(in);
-                ::close(out);
-                return false;
-            }
-        } while (br);
-    }
-    ::close(in);
-    ::close(out);
-    return true;
-#endif
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink)
-{
-    std::error_code ec;
-    copy_symlink(existing_symlink, new_symlink, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), existing_symlink, new_symlink, ec);
-    }
-}
-#endif
-
-GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept
-{
-    ec.clear();
-    auto to = read_symlink(existing_symlink, ec);
-    if (!ec) {
-        if (exists(to, ec) && is_directory(to, ec)) {
-            create_directory_symlink(to, new_symlink, ec);
-        }
-        else {
-            create_symlink(to, new_symlink, ec);
-        }
-    }
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool create_directories(const path& p)
-{
-    std::error_code ec;
-    auto result = create_directories(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept
-{
-    path current;
-    ec.clear();
-    bool didCreate = false;
-    for (path::string_type part : p) {
-        current /= part;
-        if (current != p.root_name() && current != p.root_path()) {
-            std::error_code tec;
-            auto fs = status(current, tec);
-            if (tec && fs.type() != file_type::not_found) {
-                ec = tec;
-                return false;
-            }
-            if (!exists(fs)) {
-                create_directory(current, ec);
-                if (ec) {
-                    std::error_code tmp_ec;
-                    if (is_directory(current, tmp_ec)) {
-                        ec.clear();
-                    }
-                    else {
-                        return false;
-                    }
-                }
-                didCreate = true;
-            }
-#ifndef LWG_2935_BEHAVIOUR
-            else if (!is_directory(fs)) {
-                ec = detail::make_error_code(detail::portable_error::exists);
-                return false;
-            }
-#endif
-        }
-    }
-    return didCreate;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool create_directory(const path& p)
-{
-    std::error_code ec;
-    auto result = create_directory(p, path(), ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE bool create_directory(const path& p, std::error_code& ec) noexcept
-{
-    return create_directory(p, path(), ec);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool create_directory(const path& p, const path& attributes)
-{
-    std::error_code ec;
-    auto result = create_directory(p, attributes, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept
-{
-    std::error_code tec;
-    ec.clear();
-    auto fs = status(p, tec);
-#ifdef LWG_2935_BEHAVIOUR
-    if (status_known(fs) && exists(fs)) {
-        return false;
-    }
-#else
-    if (status_known(fs) && exists(fs) && is_directory(fs)) {
-        return false;
-    }
-#endif
-#ifdef GHC_OS_WINDOWS
-    if (!attributes.empty()) {
-        if (!::CreateDirectoryExW(GHC_NATIVEWP(attributes), GHC_NATIVEWP(p), NULL)) {
-            ec = detail::make_system_error();
-            return false;
-        }
-    }
-    else if (!::CreateDirectoryW(GHC_NATIVEWP(p), NULL)) {
-        ec = detail::make_system_error();
-        return false;
-    }
-#else
-    ::mode_t attribs = static_cast<mode_t>(perms::all);
-    if (!attributes.empty()) {
-        struct ::stat fileStat;
-        if (::stat(attributes.c_str(), &fileStat) != 0) {
-            ec = detail::make_system_error();
-            return false;
-        }
-        attribs = fileStat.st_mode;
-    }
-    if (::mkdir(p.c_str(), attribs) != 0) {
-        ec = detail::make_system_error();
-        return false;
-    }
-#endif
-    return true;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink)
-{
-    std::error_code ec;
-    create_directory_symlink(to, new_symlink, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec);
-    }
-}
-#endif
-
-GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept
-{
-    detail::create_symlink(to, new_symlink, true, ec);
-}
-
-#ifndef GHC_OS_WEB
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link)
-{
-    std::error_code ec;
-    create_hard_link(to, new_hard_link, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), to, new_hard_link, ec);
-    }
-}
-#endif
-
-GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept
-{
-    detail::create_hardlink(to, new_hard_link, ec);
-}
-#endif
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void create_symlink(const path& to, const path& new_symlink)
-{
-    std::error_code ec;
-    create_symlink(to, new_symlink, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec);
-    }
-}
-#endif
-
-GHC_INLINE void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept
-{
-    detail::create_symlink(to, new_symlink, false, ec);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE path current_path()
-{
-    std::error_code ec;
-    auto result = current_path(ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE path current_path(std::error_code& ec)
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    DWORD pathlen = ::GetCurrentDirectoryW(0, 0);
-    std::unique_ptr<wchar_t[]> buffer(new wchar_t[size_t(pathlen) + 1]);
-    if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) {
-        ec = detail::make_system_error();
-        return path();
-    }
-    return path(std::wstring(buffer.get()), path::native_format);
-#else
-    size_t pathlen = static_cast<size_t>(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX)));
-    std::unique_ptr<char[]> buffer(new char[pathlen + 1]);
-    if (::getcwd(buffer.get(), pathlen) == nullptr) {
-        ec = detail::make_system_error();
-        return path();
-    }
-    return path(buffer.get());
-#endif
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void current_path(const path& p)
-{
-    std::error_code ec;
-    current_path(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-}
-#endif
-
-GHC_INLINE void current_path(const path& p, std::error_code& ec) noexcept
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    if (!::SetCurrentDirectoryW(GHC_NATIVEWP(p))) {
-        ec = detail::make_system_error();
-    }
-#else
-    if (::chdir(p.string().c_str()) == -1) {
-        ec = detail::make_system_error();
-    }
-#endif
-}
-
-GHC_INLINE bool exists(file_status s) noexcept
-{
-    return status_known(s) && s.type() != file_type::not_found;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool exists(const path& p)
-{
-    return exists(status(p));
-}
-#endif
-
-GHC_INLINE bool exists(const path& p, std::error_code& ec) noexcept
-{
-    file_status s = status(p, ec);
-    if (status_known(s)) {
-        ec.clear();
-    }
-    return exists(s);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool equivalent(const path& p1, const path& p2)
-{
-    std::error_code ec;
-    bool result = equivalent(p1, p2, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p1, p2, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    std::shared_ptr<void> file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
-    auto e1 = ::GetLastError();
-    std::shared_ptr<void> file2(::CreateFileW(GHC_NATIVEWP(p2), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
-    if (file1.get() == INVALID_HANDLE_VALUE || file2.get() == INVALID_HANDLE_VALUE) {
-#ifdef LWG_2937_BEHAVIOUR
-        ec = detail::make_system_error(e1 ? e1 : ::GetLastError());
-#else
-        if (file1 == file2) {
-            ec = detail::make_system_error(e1 ? e1 : ::GetLastError());
-        }
-#endif
-        return false;
-    }
-    BY_HANDLE_FILE_INFORMATION inf1, inf2;
-    if (!::GetFileInformationByHandle(file1.get(), &inf1)) {
-        ec = detail::make_system_error();
-        return false;
-    }
-    if (!::GetFileInformationByHandle(file2.get(), &inf2)) {
-        ec = detail::make_system_error();
-        return false;
-    }
-    return inf1.ftLastWriteTime.dwLowDateTime == inf2.ftLastWriteTime.dwLowDateTime && inf1.ftLastWriteTime.dwHighDateTime == inf2.ftLastWriteTime.dwHighDateTime && inf1.nFileIndexHigh == inf2.nFileIndexHigh && inf1.nFileIndexLow == inf2.nFileIndexLow &&
-           inf1.nFileSizeHigh == inf2.nFileSizeHigh && inf1.nFileSizeLow == inf2.nFileSizeLow && inf1.dwVolumeSerialNumber == inf2.dwVolumeSerialNumber;
-#else
-    struct ::stat s1, s2;
-    auto rc1 = ::stat(p1.c_str(), &s1);
-    auto e1 = errno;
-    auto rc2 = ::stat(p2.c_str(), &s2);
-    if (rc1 || rc2) {
-#ifdef LWG_2937_BEHAVIOUR
-        ec = detail::make_system_error(e1 ? e1 : errno);
-#else
-        if (rc1 && rc2) {
-            ec = detail::make_system_error(e1 ? e1 : errno);
-        }
-#endif
-        return false;
-    }
-    return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino && s1.st_size == s2.st_size && s1.st_mtime == s2.st_mtime;
-#endif
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE uintmax_t file_size(const path& p)
-{
-    std::error_code ec;
-    auto result = file_size(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE uintmax_t file_size(const path& p, std::error_code& ec) noexcept
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    WIN32_FILE_ATTRIBUTE_DATA attr;
-    if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) {
-        ec = detail::make_system_error();
-        return static_cast<uintmax_t>(-1);
-    }
-    return static_cast<uintmax_t>(attr.nFileSizeHigh) << (sizeof(attr.nFileSizeHigh) * 8) | attr.nFileSizeLow;
-#else
-    struct ::stat fileStat;
-    if (::stat(p.c_str(), &fileStat) == -1) {
-        ec = detail::make_system_error();
-        return static_cast<uintmax_t>(-1);
-    }
-    return static_cast<uintmax_t>(fileStat.st_size);
-#endif
-}
-
-#ifndef GHC_OS_WEB
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE uintmax_t hard_link_count(const path& p)
-{
-    std::error_code ec;
-    auto result = hard_link_count(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    uintmax_t result = static_cast<uintmax_t>(-1);
-    std::shared_ptr<void> file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
-    BY_HANDLE_FILE_INFORMATION inf;
-    if (file.get() == INVALID_HANDLE_VALUE) {
-        ec = detail::make_system_error();
-    }
-    else {
-        if (!::GetFileInformationByHandle(file.get(), &inf)) {
-            ec = detail::make_system_error();
-        }
-        else {
-            result = inf.nNumberOfLinks;
-        }
-    }
-    return result;
-#else
-    uintmax_t result = 0;
-    file_status fs = detail::status_ex(p, ec, nullptr, nullptr, &result, nullptr);
-    if (fs.type() == file_type::not_found) {
-        ec = detail::make_error_code(detail::portable_error::not_found);
-    }
-    return ec ? static_cast<uintmax_t>(-1) : result;
-#endif
-}
-#endif
-
-GHC_INLINE bool is_block_file(file_status s) noexcept
-{
-    return s.type() == file_type::block;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool is_block_file(const path& p)
-{
-    return is_block_file(status(p));
-}
-#endif
-
-GHC_INLINE bool is_block_file(const path& p, std::error_code& ec) noexcept
-{
-    return is_block_file(status(p, ec));
-}
-
-GHC_INLINE bool is_character_file(file_status s) noexcept
-{
-    return s.type() == file_type::character;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool is_character_file(const path& p)
-{
-    return is_character_file(status(p));
-}
-#endif
-
-GHC_INLINE bool is_character_file(const path& p, std::error_code& ec) noexcept
-{
-    return is_character_file(status(p, ec));
-}
-
-GHC_INLINE bool is_directory(file_status s) noexcept
-{
-    return s.type() == file_type::directory;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool is_directory(const path& p)
-{
-    return is_directory(status(p));
-}
-#endif
-
-GHC_INLINE bool is_directory(const path& p, std::error_code& ec) noexcept
-{
-    return is_directory(status(p, ec));
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool is_empty(const path& p)
-{
-    if (is_directory(p)) {
-        return directory_iterator(p) == directory_iterator();
-    }
-    else {
-        return file_size(p) == 0;
-    }
-}
-#endif
-
-GHC_INLINE bool is_empty(const path& p, std::error_code& ec) noexcept
-{
-    auto fs = status(p, ec);
-    if (ec) {
-        return false;
-    }
-    if (is_directory(fs)) {
-        directory_iterator iter(p, ec);
-        if (ec) {
-            return false;
-        }
-        return iter == directory_iterator();
-    }
-    else {
-        auto sz = file_size(p, ec);
-        if (ec) {
-            return false;
-        }
-        return sz == 0;
-    }
-}
-
-GHC_INLINE bool is_fifo(file_status s) noexcept
-{
-    return s.type() == file_type::fifo;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool is_fifo(const path& p)
-{
-    return is_fifo(status(p));
-}
-#endif
-
-GHC_INLINE bool is_fifo(const path& p, std::error_code& ec) noexcept
-{
-    return is_fifo(status(p, ec));
-}
-
-GHC_INLINE bool is_other(file_status s) noexcept
-{
-    return exists(s) && !is_regular_file(s) && !is_directory(s) && !is_symlink(s);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool is_other(const path& p)
-{
-    return is_other(status(p));
-}
-#endif
-
-GHC_INLINE bool is_other(const path& p, std::error_code& ec) noexcept
-{
-    return is_other(status(p, ec));
-}
-
-GHC_INLINE bool is_regular_file(file_status s) noexcept
-{
-    return s.type() == file_type::regular;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool is_regular_file(const path& p)
-{
-    return is_regular_file(status(p));
-}
-#endif
-
-GHC_INLINE bool is_regular_file(const path& p, std::error_code& ec) noexcept
-{
-    return is_regular_file(status(p, ec));
-}
-
-GHC_INLINE bool is_socket(file_status s) noexcept
-{
-    return s.type() == file_type::socket;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool is_socket(const path& p)
-{
-    return is_socket(status(p));
-}
-#endif
-
-GHC_INLINE bool is_socket(const path& p, std::error_code& ec) noexcept
-{
-    return is_socket(status(p, ec));
-}
-
-GHC_INLINE bool is_symlink(file_status s) noexcept
-{
-    return s.type() == file_type::symlink;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool is_symlink(const path& p)
-{
-    return is_symlink(symlink_status(p));
-}
-#endif
-
-GHC_INLINE bool is_symlink(const path& p, std::error_code& ec) noexcept
-{
-    return is_symlink(symlink_status(p, ec));
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE file_time_type last_write_time(const path& p)
-{
-    std::error_code ec;
-    auto result = last_write_time(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE file_time_type last_write_time(const path& p, std::error_code& ec) noexcept
-{
-    time_t result = 0;
-    ec.clear();
-    file_status fs = detail::status_ex(p, ec, nullptr, nullptr, nullptr, &result);
-    return ec ? (file_time_type::min)() : std::chrono::system_clock::from_time_t(result);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void last_write_time(const path& p, file_time_type new_time)
-{
-    std::error_code ec;
-    last_write_time(p, new_time, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-}
-#endif
-
-GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept
-{
-    ec.clear();
-    auto d = new_time.time_since_epoch();
-#ifdef GHC_OS_WINDOWS
-    std::shared_ptr<void> file(::CreateFileW(GHC_NATIVEWP(p), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL), ::CloseHandle);
-    FILETIME ft;
-    auto tt = std::chrono::duration_cast<std::chrono::microseconds>(d).count() * 10 + 116444736000000000;
-    ft.dwLowDateTime = static_cast<DWORD>(tt);
-    ft.dwHighDateTime = static_cast<DWORD>(tt >> 32);
-    if (!::SetFileTime(file.get(), 0, 0, &ft)) {
-        ec = detail::make_system_error();
-    }
-#elif defined(GHC_OS_MACOS)
-#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
-#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300
-    struct ::stat fs;
-    if (::stat(p.c_str(), &fs) == 0) {
-        struct ::timeval tv[2];
-        tv[0].tv_sec = fs.st_atimespec.tv_sec;
-        tv[0].tv_usec = static_cast<int>(fs.st_atimespec.tv_nsec / 1000);
-        tv[1].tv_sec = std::chrono::duration_cast<std::chrono::seconds>(d).count();
-        tv[1].tv_usec = static_cast<int>(std::chrono::duration_cast<std::chrono::microseconds>(d).count() % 1000000);
-        if (::utimes(p.c_str(), tv) == 0) {
-            return;
-        }
-    }
-    ec = detail::make_system_error();
-    return;
-#else
-    struct ::timespec times[2];
-    times[0].tv_sec = 0;
-    times[0].tv_nsec = UTIME_OMIT;
-    times[1].tv_sec = std::chrono::duration_cast<std::chrono::seconds>(d).count();
-    times[1].tv_nsec = 0;  // std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000;
-    if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
-        ec = detail::make_system_error();
-    }
-    return;
-#endif
-#endif
-#else
-#ifndef UTIME_OMIT
-#define UTIME_OMIT ((1l << 30) - 2l)
-#endif
-    struct ::timespec times[2];
-    times[0].tv_sec = 0;
-    times[0].tv_nsec = UTIME_OMIT;
-    times[1].tv_sec = static_cast<decltype(times[1].tv_sec)>(std::chrono::duration_cast<std::chrono::seconds>(d).count());
-    times[1].tv_nsec = static_cast<decltype(times[1].tv_nsec)>(std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000);
-#if defined(__ANDROID_API__) && __ANDROID_API__ < 12
-    if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
-#else
-    if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
-#endif
-        ec = detail::make_system_error();
-    }
-    return;
-#endif
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void permissions(const path& p, perms prms, perm_options opts)
-{
-    std::error_code ec;
-    permissions(p, prms, opts, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-}
-#endif
-
-GHC_INLINE void permissions(const path& p, perms prms, std::error_code& ec) noexcept
-{
-    permissions(p, prms, perm_options::replace, ec);
-}
-
-GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept
-{
-    if (static_cast<int>(opts & (perm_options::replace | perm_options::add | perm_options::remove)) == 0) {
-        ec = detail::make_error_code(detail::portable_error::invalid_argument);
-        return;
-    }
-    auto fs = symlink_status(p, ec);
-    if ((opts & perm_options::replace) != perm_options::replace) {
-        if ((opts & perm_options::add) == perm_options::add) {
-            prms = fs.permissions() | prms;
-        }
-        else {
-            prms = fs.permissions() & ~prms;
-        }
-    }
-#ifdef GHC_OS_WINDOWS
-#ifdef __GNUC__
-    auto oldAttr = GetFileAttributesW(GHC_NATIVEWP(p));
-    if (oldAttr != INVALID_FILE_ATTRIBUTES) {
-        DWORD newAttr = ((prms & perms::owner_write) == perms::owner_write) ? oldAttr & ~(static_cast<DWORD>(FILE_ATTRIBUTE_READONLY)) : oldAttr | FILE_ATTRIBUTE_READONLY;
-        if (oldAttr == newAttr || SetFileAttributesW(GHC_NATIVEWP(p), newAttr)) {
-            return;
-        }
-    }
-    ec = detail::make_system_error();
-#else
-    int mode = 0;
-    if ((prms & perms::owner_read) == perms::owner_read) {
-        mode |= _S_IREAD;
-    }
-    if ((prms & perms::owner_write) == perms::owner_write) {
-        mode |= _S_IWRITE;
-    }
-    if (::_wchmod(p.wstring().c_str(), mode) != 0) {
-        ec = detail::make_system_error();
-    }
-#endif
-#else
-    if ((opts & perm_options::nofollow) != perm_options::nofollow) {
-        if (::chmod(p.c_str(), static_cast<mode_t>(prms)) != 0) {
-            ec = detail::make_system_error();
-        }
-    }
-#endif
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE path proximate(const path& p, std::error_code& ec)
-{
-    auto cp = current_path(ec);
-    if (!ec) {
-        return proximate(p, cp, ec);
-    }
-    return path();
-}
-#endif
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE path proximate(const path& p, const path& base)
-{
-    return weakly_canonical(p).lexically_proximate(weakly_canonical(base));
-}
-#endif
-
-GHC_INLINE path proximate(const path& p, const path& base, std::error_code& ec)
-{
-    return weakly_canonical(p, ec).lexically_proximate(weakly_canonical(base, ec));
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE path read_symlink(const path& p)
-{
-    std::error_code ec;
-    auto result = read_symlink(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE path read_symlink(const path& p, std::error_code& ec)
-{
-    file_status fs = symlink_status(p, ec);
-    if (fs.type() != file_type::symlink) {
-        ec = detail::make_error_code(detail::portable_error::invalid_argument);
-        return path();
-    }
-    auto result = detail::resolveSymlink(p, ec);
-    return ec ? path() : result;
-}
-
-GHC_INLINE path relative(const path& p, std::error_code& ec)
-{
-    return relative(p, current_path(ec), ec);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE path relative(const path& p, const path& base)
-{
-    return weakly_canonical(p).lexically_relative(weakly_canonical(base));
-}
-#endif
-
-GHC_INLINE path relative(const path& p, const path& base, std::error_code& ec)
-{
-    return weakly_canonical(p, ec).lexically_relative(weakly_canonical(base, ec));
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool remove(const path& p)
-{
-    std::error_code ec;
-    auto result = remove(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-#ifdef GHC_USE_WCHAR_T
-    auto cstr = p.c_str();
-#else
-    std::wstring np = detail::fromUtf8<std::wstring>(p.u8string());
-    auto cstr = np.c_str();
-#endif
-    DWORD attr = GetFileAttributesW(cstr);
-    if (attr == INVALID_FILE_ATTRIBUTES) {
-        auto error = ::GetLastError();
-        if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) {
-            return false;
-        }
-        ec = detail::make_system_error(error);
-    }
-    else if(attr & FILE_ATTRIBUTE_READONLY) {
-        auto new_attr = attr & ~static_cast<DWORD>(FILE_ATTRIBUTE_READONLY);
-        if(!SetFileAttributesW(cstr, new_attr)) {
-            auto error = ::GetLastError();
-            ec = detail::make_system_error(error);
-        }
-    }
-    if (!ec) {
-        if (attr & FILE_ATTRIBUTE_DIRECTORY) {
-            if (!RemoveDirectoryW(cstr)) {
-                ec = detail::make_system_error();
-            }
-        }
-        else {
-            if (!DeleteFileW(cstr)) {
-                ec = detail::make_system_error();
-            }
-        }
-    }
-#else
-    if (::remove(p.c_str()) == -1) {
-        auto error = errno;
-        if (error == ENOENT) {
-            return false;
-        }
-        ec = detail::make_system_error();
-    }
-#endif
-    return ec ? false : true;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE uintmax_t remove_all(const path& p)
-{
-    std::error_code ec;
-    auto result = remove_all(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept
-{
-    ec.clear();
-    uintmax_t count = 0;
-    if (p == "/") {
-        ec = detail::make_error_code(detail::portable_error::not_supported);
-        return static_cast<uintmax_t>(-1);
-    }
-    std::error_code tec;
-    auto fs = status(p, tec);
-    if (exists(fs) && is_directory(fs)) {
-        for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) {
-            if (ec && !detail::is_not_found_error(ec)) {
-                break;
-            }
-            bool is_symlink_result = iter->is_symlink(ec);
-            if (ec)
-                return static_cast<uintmax_t>(-1);
-            if (!is_symlink_result && iter->is_directory(ec)) {
-                count += remove_all(iter->path(), ec);
-                if (ec) {
-                    return static_cast<uintmax_t>(-1);
-                }
-            }
-            else {
-                if (!ec) {
-                    remove(iter->path(), ec);
-                }
-                if (ec) {
-                    return static_cast<uintmax_t>(-1);
-                }
-                ++count;
-            }
-        }
-    }
-    if (!ec) {
-        if (remove(p, ec)) {
-            ++count;
-        }
-    }
-    if (ec) {
-        return static_cast<uintmax_t>(-1);
-    }
-    return count;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void rename(const path& from, const path& to)
-{
-    std::error_code ec;
-    rename(from, to, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec);
-    }
-}
-#endif
-
-GHC_INLINE void rename(const path& from, const path& to, std::error_code& ec) noexcept
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    if (from != to) {
-        if (!MoveFileExW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), (DWORD)MOVEFILE_REPLACE_EXISTING)) {
-            ec = detail::make_system_error();
-        }
-    }
-#else
-    if (from != to) {
-        if (::rename(from.c_str(), to.c_str()) != 0) {
-            ec = detail::make_system_error();
-        }
-    }
-#endif
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void resize_file(const path& p, uintmax_t size)
-{
-    std::error_code ec;
-    resize_file(p, size, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-}
-#endif
-
-GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    LARGE_INTEGER lisize;
-    lisize.QuadPart = static_cast<LONGLONG>(size);
-    if (lisize.QuadPart < 0) {
-#ifdef ERROR_FILE_TOO_LARGE
-        ec = detail::make_system_error(ERROR_FILE_TOO_LARGE);
-#else
-        ec = detail::make_system_error(223);
-#endif
-        return;
-    }
-    std::shared_ptr<void> file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL), CloseHandle);
-    if (file.get() == INVALID_HANDLE_VALUE) {
-        ec = detail::make_system_error();
-    }
-    else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) {
-        ec = detail::make_system_error();
-    }
-#else
-    if (::truncate(p.c_str(), static_cast<off_t>(size)) != 0) {
-        ec = detail::make_system_error();
-    }
-#endif
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE space_info space(const path& p)
-{
-    std::error_code ec;
-    auto result = space(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    ULARGE_INTEGER freeBytesAvailableToCaller = {{ 0, 0 }};
-    ULARGE_INTEGER totalNumberOfBytes = {{ 0, 0 }};
-    ULARGE_INTEGER totalNumberOfFreeBytes = {{ 0, 0 }};
-    if (!GetDiskFreeSpaceExW(GHC_NATIVEWP(p), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) {
-        ec = detail::make_system_error();
-        return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)};
-    }
-    return {static_cast<uintmax_t>(totalNumberOfBytes.QuadPart), static_cast<uintmax_t>(totalNumberOfFreeBytes.QuadPart), static_cast<uintmax_t>(freeBytesAvailableToCaller.QuadPart)};
-#else
-    struct ::statvfs sfs;
-    if (::statvfs(p.c_str(), &sfs) != 0) {
-        ec = detail::make_system_error();
-        return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)};
-    }
-    return {static_cast<uintmax_t>(sfs.f_blocks * sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bfree * sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bavail * sfs.f_frsize)};
-#endif
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE file_status status(const path& p)
-{
-    std::error_code ec;
-    auto result = status(p, ec);
-    if (result.type() == file_type::none) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE file_status status(const path& p, std::error_code& ec) noexcept
-{
-    return detail::status_ex(p, ec);
-}
-
-GHC_INLINE bool status_known(file_status s) noexcept
-{
-    return s.type() != file_type::none;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE file_status symlink_status(const path& p)
-{
-    std::error_code ec;
-    auto result = symlink_status(p, ec);
-    if (result.type() == file_type::none) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE file_status symlink_status(const path& p, std::error_code& ec) noexcept
-{
-    return detail::symlink_status_ex(p, ec);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE path temp_directory_path()
-{
-    std::error_code ec;
-    path result = temp_directory_path(ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE path temp_directory_path(std::error_code& ec) noexcept
-{
-    ec.clear();
-#ifdef GHC_OS_WINDOWS
-    wchar_t buffer[512];
-    auto rc = GetTempPathW(511, buffer);
-    if (!rc || rc > 511) {
-        ec = detail::make_system_error();
-        return path();
-    }
-    return path(std::wstring(buffer));
-#else
-    static const char* temp_vars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr};
-    const char* temp_path = nullptr;
-    for (auto temp_name = temp_vars; *temp_name != nullptr; ++temp_name) {
-        temp_path = std::getenv(*temp_name);
-        if (temp_path) {
-            return path(temp_path);
-        }
-    }
-    return path("/tmp");
-#endif
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE path weakly_canonical(const path& p)
-{
-    std::error_code ec;
-    auto result = weakly_canonical(p, ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
-    }
-    return result;
-}
-#endif
-
-GHC_INLINE path weakly_canonical(const path& p, std::error_code& ec) noexcept
-{
-    path result;
-    ec.clear();
-    bool scan = true;
-    for (auto pe : p) {
-        if (scan) {
-            std::error_code tec;
-            if (exists(result / pe, tec)) {
-                result /= pe;
-            }
-            else {
-                if (ec) {
-                    return path();
-                }
-                scan = false;
-                if (!result.empty()) {
-                    result = canonical(result, ec) / pe;
-                    if (ec) {
-                        break;
-                    }
-                }
-                else {
-                    result /= pe;
-                }
-            }
-        }
-        else {
-            result /= pe;
-        }
-    }
-    if (scan) {
-        if (!result.empty()) {
-            result = canonical(result, ec);
-        }
-    }
-    return ec ? path() : result.lexically_normal();
-}
-
-//-----------------------------------------------------------------------------
-// [fs.class.file_status] class file_status
-// [fs.file_status.cons] constructors and destructor
-GHC_INLINE file_status::file_status() noexcept
-    : file_status(file_type::none)
-{
-}
-
-GHC_INLINE file_status::file_status(file_type ft, perms prms) noexcept
-    : _type(ft)
-    , _perms(prms)
-{
-}
-
-GHC_INLINE file_status::file_status(const file_status& other) noexcept
-    : _type(other._type)
-    , _perms(other._perms)
-{
-}
-
-GHC_INLINE file_status::file_status(file_status&& other) noexcept
-    : _type(other._type)
-    , _perms(other._perms)
-{
-}
-
-GHC_INLINE file_status::~file_status() {}
-
-// assignments:
-GHC_INLINE file_status& file_status::operator=(const file_status& rhs) noexcept
-{
-    _type = rhs._type;
-    _perms = rhs._perms;
-    return *this;
-}
-
-GHC_INLINE file_status& file_status::operator=(file_status&& rhs) noexcept
-{
-    _type = rhs._type;
-    _perms = rhs._perms;
-    return *this;
-}
-
-// [fs.file_status.mods] modifiers
-GHC_INLINE void file_status::type(file_type ft) noexcept
-{
-    _type = ft;
-}
-
-GHC_INLINE void file_status::permissions(perms prms) noexcept
-{
-    _perms = prms;
-}
-
-// [fs.file_status.obs] observers
-GHC_INLINE file_type file_status::type() const noexcept
-{
-    return _type;
-}
-
-GHC_INLINE perms file_status::permissions() const noexcept
-{
-    return _perms;
-}
-
-//-----------------------------------------------------------------------------
-// [fs.class.directory_entry] class directory_entry
-// [fs.dir.entry.cons] constructors and destructor
-// directory_entry::directory_entry() noexcept = default;
-// directory_entry::directory_entry(const directory_entry&) = default;
-// directory_entry::directory_entry(directory_entry&&) noexcept = default;
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE directory_entry::directory_entry(const filesystem::path& p)
-    : _path(p)
-    , _file_size(static_cast<uintmax_t>(-1))
-#ifndef GHC_OS_WINDOWS
-    , _hard_link_count(static_cast<uintmax_t>(-1))
-#endif
-    , _last_write_time(0)
-{
-    refresh();
-}
-#endif
-
-GHC_INLINE directory_entry::directory_entry(const filesystem::path& p, std::error_code& ec)
-    : _path(p)
-    , _file_size(static_cast<uintmax_t>(-1))
-#ifndef GHC_OS_WINDOWS
-    , _hard_link_count(static_cast<uintmax_t>(-1))
-#endif
-    , _last_write_time(0)
-{
-    refresh(ec);
-}
-
-GHC_INLINE directory_entry::~directory_entry() {}
-
-// assignments:
-// directory_entry& directory_entry::operator=(const directory_entry&) = default;
-// directory_entry& directory_entry::operator=(directory_entry&&) noexcept = default;
-
-// [fs.dir.entry.mods] directory_entry modifiers
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void directory_entry::assign(const filesystem::path& p)
-{
-    _path = p;
-    refresh();
-}
-#endif
-
-GHC_INLINE void directory_entry::assign(const filesystem::path& p, std::error_code& ec)
-{
-    _path = p;
-    refresh(ec);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p)
-{
-    _path.replace_filename(p);
-    refresh();
-}
-#endif
-
-GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p, std::error_code& ec)
-{
-    _path.replace_filename(p);
-    refresh(ec);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void directory_entry::refresh()
-{
-    std::error_code ec;
-    refresh(ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec);
-    }
-}
-#endif
-
-GHC_INLINE void directory_entry::refresh(std::error_code& ec) noexcept
-{
-#ifdef GHC_OS_WINDOWS
-    _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, nullptr, &_last_write_time);
-#else
-    _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, &_hard_link_count, &_last_write_time);
-#endif
-}
-
-// [fs.dir.entry.obs] directory_entry observers
-GHC_INLINE const filesystem::path& directory_entry::path() const noexcept
-{
-    return _path;
-}
-
-GHC_INLINE directory_entry::operator const filesystem::path&() const noexcept
-{
-    return _path;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE file_type directory_entry::status_file_type() const
-{
-    return _status.type() != file_type::none ? _status.type() : filesystem::status(path()).type();
-}
-#endif
-
-GHC_INLINE file_type directory_entry::status_file_type(std::error_code& ec) const noexcept
-{
-    if(_status.type() != file_type::none) {
-        ec.clear();
-        return _status.type();
-    }
-    return filesystem::status(path(), ec).type();
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool directory_entry::exists() const
-{
-    return status_file_type() != file_type::not_found;
-}
-#endif
-
-GHC_INLINE bool directory_entry::exists(std::error_code& ec) const noexcept
-{
-    return status_file_type(ec) != file_type::not_found;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool directory_entry::is_block_file() const
-{
-    return status_file_type() == file_type::block;
-}
-#endif
-GHC_INLINE bool directory_entry::is_block_file(std::error_code& ec) const noexcept
-{
-    return status_file_type(ec) == file_type::block;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool directory_entry::is_character_file() const
-{
-    return status_file_type() == file_type::character;
-}
-#endif
-
-GHC_INLINE bool directory_entry::is_character_file(std::error_code& ec) const noexcept
-{
-    return status_file_type(ec) == file_type::character;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool directory_entry::is_directory() const
-{
-    return status_file_type() == file_type::directory;
-}
-#endif
-
-GHC_INLINE bool directory_entry::is_directory(std::error_code& ec) const noexcept
-{
-    return status_file_type(ec) == file_type::directory;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool directory_entry::is_fifo() const
-{
-    return status_file_type() == file_type::fifo;
-}
-#endif
-
-GHC_INLINE bool directory_entry::is_fifo(std::error_code& ec) const noexcept
-{
-    return status_file_type(ec) == file_type::fifo;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool directory_entry::is_other() const
-{
-    auto ft = status_file_type();
-    return ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink();
-}
-#endif
-
-GHC_INLINE bool directory_entry::is_other(std::error_code& ec) const noexcept
-{
-    auto ft = status_file_type(ec);
-    bool other = ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(ec);
-    return !ec && other;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool directory_entry::is_regular_file() const
-{
-    return status_file_type() == file_type::regular;
-}
-#endif
-
-GHC_INLINE bool directory_entry::is_regular_file(std::error_code& ec) const noexcept
-{
-    return status_file_type(ec) == file_type::regular;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool directory_entry::is_socket() const
-{
-    return status_file_type() == file_type::socket;
-}
-#endif
-
-GHC_INLINE bool directory_entry::is_socket(std::error_code& ec) const noexcept
-{
-    return status_file_type(ec) == file_type::socket;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE bool directory_entry::is_symlink() const
-{
-    return _symlink_status.type() != file_type::none ? _symlink_status.type() == file_type::symlink : filesystem::is_symlink(symlink_status());
-}
-#endif
-
-GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept
-{
-    if(_symlink_status.type() != file_type::none) {
-        ec.clear();
-        return _symlink_status.type() == file_type::symlink;
-    }
-    return filesystem::is_symlink(symlink_status(ec));
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE uintmax_t directory_entry::file_size() const
-{
-    if (_file_size != static_cast<uintmax_t>(-1)) {
-        return _file_size;
-    }
-    return filesystem::file_size(path());
-}
-#endif
-
-GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexcept
-{
-    if (_file_size != static_cast<uintmax_t>(-1)) {
-        ec.clear();
-        return _file_size;
-    }
-    return filesystem::file_size(path(), ec);
-}
-
-#ifndef GHC_OS_WEB
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE uintmax_t directory_entry::hard_link_count() const
-{
-#ifndef GHC_OS_WINDOWS
-    if (_hard_link_count != static_cast<uintmax_t>(-1)) {
-        return _hard_link_count;
-    }
-#endif
-    return filesystem::hard_link_count(path());
-}
-#endif
-
-GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const noexcept
-{
-#ifndef GHC_OS_WINDOWS
-    if (_hard_link_count != static_cast<uintmax_t>(-1)) {
-        ec.clear();
-        return _hard_link_count;
-    }
-#endif
-    return filesystem::hard_link_count(path(), ec);
-}
-#endif
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE file_time_type directory_entry::last_write_time() const
-{
-    if (_last_write_time != 0) {
-        return std::chrono::system_clock::from_time_t(_last_write_time);
-    }
-    return filesystem::last_write_time(path());
-}
-#endif
-
-GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) const noexcept
-{
-    if (_last_write_time != 0) {
-        ec.clear();
-        return std::chrono::system_clock::from_time_t(_last_write_time);
-    }
-    return filesystem::last_write_time(path(), ec);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE file_status directory_entry::status() const
-{
-    if (_status.type() != file_type::none && _status.permissions() != perms::unknown) {
-        return _status;
-    }
-    return filesystem::status(path());
-}
-#endif
-
-GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexcept
-{
-    if (_status.type() != file_type::none && _status.permissions() != perms::unknown) {
-        ec.clear();
-        return _status;
-    }
-    return filesystem::status(path(), ec);
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE file_status directory_entry::symlink_status() const
-{
-    if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) {
-        return _symlink_status;
-    }
-    return filesystem::symlink_status(path());
-}
-#endif
-
-GHC_INLINE file_status directory_entry::symlink_status(std::error_code& ec) const noexcept
-{
-    if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) {
-        ec.clear();
-        return _symlink_status;
-    }
-    return filesystem::symlink_status(path(), ec);
-}
-
-#ifdef GHC_HAS_THREEWAY_COMP
-GHC_INLINE std::strong_ordering directory_entry::operator<=>(const directory_entry& rhs) const noexcept
-{
-    return _path <=> rhs._path;
-}
-#endif
-
-GHC_INLINE bool directory_entry::operator<(const directory_entry& rhs) const noexcept
-{
-    return _path < rhs._path;
-}
-
-GHC_INLINE bool directory_entry::operator==(const directory_entry& rhs) const noexcept
-{
-    return _path == rhs._path;
-}
-
-GHC_INLINE bool directory_entry::operator!=(const directory_entry& rhs) const noexcept
-{
-    return _path != rhs._path;
-}
-
-GHC_INLINE bool directory_entry::operator<=(const directory_entry& rhs) const noexcept
-{
-    return _path <= rhs._path;
-}
-
-GHC_INLINE bool directory_entry::operator>(const directory_entry& rhs) const noexcept
-{
-    return _path > rhs._path;
-}
-
-GHC_INLINE bool directory_entry::operator>=(const directory_entry& rhs) const noexcept
-{
-    return _path >= rhs._path;
-}
-
-//-----------------------------------------------------------------------------
-// [fs.class.directory_iterator] class directory_iterator
-
-#ifdef GHC_OS_WINDOWS
-class directory_iterator::impl
-{
-public:
-    impl(const path& p, directory_options options)
-        : _base(p)
-        , _options(options)
-        , _dirHandle(INVALID_HANDLE_VALUE)
-    {
-        if (!_base.empty()) {
-            ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW));
-            if ((_dirHandle = FindFirstFileW(GHC_NATIVEWP((_base / "*")), &_findData)) != INVALID_HANDLE_VALUE) {
-                if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") {
-                    increment(_ec);
-                }
-                else {
-                    _dir_entry._path = _base / std::wstring(_findData.cFileName);
-                    copyToDirEntry(_ec);
-                }
-            }
-            else {
-                auto error = ::GetLastError();
-                _base = filesystem::path();
-                if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) {
-                    _ec = detail::make_system_error();
-                }
-            }
-        }
-    }
-    impl(const impl& other) = delete;
-    ~impl()
-    {
-        if (_dirHandle != INVALID_HANDLE_VALUE) {
-            FindClose(_dirHandle);
-            _dirHandle = INVALID_HANDLE_VALUE;
-        }
-    }
-    void increment(std::error_code& ec)
-    {
-        if (_dirHandle != INVALID_HANDLE_VALUE) {
-            do {
-                if (FindNextFileW(_dirHandle, &_findData)) {
-                    _dir_entry._path = _base;
-#ifdef GHC_USE_WCHAR_T
-                    _dir_entry._path.append_name(_findData.cFileName);
-#else
-#ifdef GHC_RAISE_UNICODE_ERRORS
-                    try {
-                        _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str());
-                    }
-                    catch (filesystem_error& fe) {
-                        ec = fe.code();
-                        return;
-                    }
-#else
-                    _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str());
-#endif
-#endif
-                    copyToDirEntry(ec);
-                }
-                else {
-                    auto err = ::GetLastError();
-                    if (err != ERROR_NO_MORE_FILES) {
-                        _ec = ec = detail::make_system_error(err);
-                    }
-                    FindClose(_dirHandle);
-                    _dirHandle = INVALID_HANDLE_VALUE;
-                    _dir_entry._path.clear();
-                    break;
-                }
-            } while (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..");
-        }
-        else {
-            ec = _ec;
-        }
-    }
-    void copyToDirEntry(std::error_code& ec)
-    {
-        if (_findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-            _dir_entry._status = detail::status_ex(_dir_entry._path, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time);
-        }
-        else {
-            _dir_entry._status = detail::status_from_INFO(_dir_entry._path, &_findData, ec, &_dir_entry._file_size, &_dir_entry._last_write_time);
-            _dir_entry._symlink_status = _dir_entry._status;
-        }
-        if (ec) {
-            if (_dir_entry._status.type() != file_type::none && _dir_entry._symlink_status.type() != file_type::none) {
-                ec.clear();
-            }
-            else {
-                _dir_entry._file_size = static_cast<uintmax_t>(-1);
-                _dir_entry._last_write_time = 0;
-            }
-        }
-    }
-    path _base;
-    directory_options _options;
-    WIN32_FIND_DATAW _findData;
-    HANDLE _dirHandle;
-    directory_entry _dir_entry;
-    std::error_code _ec;
-};
-#else
-// POSIX implementation
-class directory_iterator::impl
-{
-public:
-    impl(const path& path, directory_options options)
-        : _base(path)
-        , _options(options)
-        , _dir(nullptr)
-        , _entry(nullptr)
-    {
-        if (!path.empty()) {
-            _dir = ::opendir(path.native().c_str());
-            if (!_dir) {
-                auto error = errno;
-                _base = filesystem::path();
-                if ((error != EACCES && error != EPERM) || (options & directory_options::skip_permission_denied) == directory_options::none) {
-                    _ec = detail::make_system_error();
-                }
-            }
-            else {
-                increment(_ec);
-            }
-        }
-    }
-    impl(const impl& other) = delete;
-    ~impl()
-    {
-        if (_dir) {
-            ::closedir(_dir);
-        }
-    }
-    void increment(std::error_code& ec)
-    {
-        if (_dir) {
-            bool skip;
-            do {
-                skip = false;
-                errno = 0;
-                _entry = ::readdir(_dir);
-                if (_entry) {
-                    _dir_entry._path = _base;
-                    _dir_entry._path.append_name(_entry->d_name);
-                    copyToDirEntry();
-                    if (ec && (ec.value() == EACCES || ec.value() == EPERM) && (_options & directory_options::skip_permission_denied) == directory_options::skip_permission_denied) {
-                        ec.clear();
-                        skip = true;
-                    }
-                }
-                else {
-                    ::closedir(_dir);
-                    _dir = nullptr;
-                    _dir_entry._path.clear();
-                    if (errno) {
-                        ec = detail::make_system_error();
-                    }
-                    break;
-                }
-            } while (skip || std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0);
-        }
-    }
-
-    void copyToDirEntry()
-    {
-#ifdef GHC_NO_DIRENT_D_TYPE
-        _dir_entry._symlink_status = file_status();
-        _dir_entry._status = file_status();
-#else
-        _dir_entry._symlink_status.permissions(perms::unknown);
-        switch(_entry->d_type) {
-            case DT_BLK: _dir_entry._symlink_status.type(file_type::block); break;
-            case DT_CHR: _dir_entry._symlink_status.type(file_type::character); break;
-            case DT_DIR: _dir_entry._symlink_status.type(file_type::directory); break;
-            case DT_FIFO: _dir_entry._symlink_status.type(file_type::fifo); break;
-            case DT_LNK: _dir_entry._symlink_status.type(file_type::symlink); break;
-            case DT_REG: _dir_entry._symlink_status.type(file_type::regular); break;
-            case DT_SOCK: _dir_entry._symlink_status.type(file_type::socket); break;
-            case DT_UNKNOWN: _dir_entry._symlink_status.type(file_type::none); break;
-            default: _dir_entry._symlink_status.type(file_type::unknown); break;
-        }
-        if (_entry->d_type != DT_LNK) {
-            _dir_entry._status = _dir_entry._symlink_status;
-        }
-        else {
-            _dir_entry._status.type(file_type::none);
-            _dir_entry._status.permissions(perms::unknown);
-        }
-#endif
-        _dir_entry._file_size = static_cast<uintmax_t>(-1);
-        _dir_entry._hard_link_count = static_cast<uintmax_t>(-1);
-        _dir_entry._last_write_time = 0;
-    }
-    path _base;
-    directory_options _options;
-    DIR* _dir;
-    struct ::dirent* _entry;
-    directory_entry _dir_entry;
-    std::error_code _ec;
-};
-#endif
-
-// [fs.dir.itr.members] member functions
-GHC_INLINE directory_iterator::directory_iterator() noexcept
-    : _impl(new impl(path(), directory_options::none))
-{
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE directory_iterator::directory_iterator(const path& p)
-    : _impl(new impl(p, directory_options::none))
-{
-    if (_impl->_ec) {
-        throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec);
-    }
-    _impl->_ec.clear();
-}
-
-GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options)
-    : _impl(new impl(p, options))
-{
-    if (_impl->_ec) {
-        throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec);
-    }
-}
-#endif
-
-GHC_INLINE directory_iterator::directory_iterator(const path& p, std::error_code& ec) noexcept
-    : _impl(new impl(p, directory_options::none))
-{
-    if (_impl->_ec) {
-        ec = _impl->_ec;
-    }
-}
-
-GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept
-    : _impl(new impl(p, options))
-{
-    if (_impl->_ec) {
-        ec = _impl->_ec;
-    }
-}
-
-GHC_INLINE directory_iterator::directory_iterator(const directory_iterator& rhs)
-    : _impl(rhs._impl)
-{
-}
-
-GHC_INLINE directory_iterator::directory_iterator(directory_iterator&& rhs) noexcept
-    : _impl(std::move(rhs._impl))
-{
-}
-
-GHC_INLINE directory_iterator::~directory_iterator() {}
-
-GHC_INLINE directory_iterator& directory_iterator::operator=(const directory_iterator& rhs)
-{
-    _impl = rhs._impl;
-    return *this;
-}
-
-GHC_INLINE directory_iterator& directory_iterator::operator=(directory_iterator&& rhs) noexcept
-{
-    _impl = std::move(rhs._impl);
-    return *this;
-}
-
-GHC_INLINE const directory_entry& directory_iterator::operator*() const
-{
-    return _impl->_dir_entry;
-}
-
-GHC_INLINE const directory_entry* directory_iterator::operator->() const
-{
-    return &_impl->_dir_entry;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE directory_iterator& directory_iterator::operator++()
-{
-    std::error_code ec;
-    _impl->increment(ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_entry._path, ec);
-    }
-    return *this;
-}
-#endif
-
-GHC_INLINE directory_iterator& directory_iterator::increment(std::error_code& ec) noexcept
-{
-    _impl->increment(ec);
-    return *this;
-}
-
-GHC_INLINE bool directory_iterator::operator==(const directory_iterator& rhs) const
-{
-    return _impl->_dir_entry._path == rhs._impl->_dir_entry._path;
-}
-
-GHC_INLINE bool directory_iterator::operator!=(const directory_iterator& rhs) const
-{
-    return _impl->_dir_entry._path != rhs._impl->_dir_entry._path;
-}
-
-// [fs.dir.itr.nonmembers] directory_iterator non-member functions
-
-GHC_INLINE directory_iterator begin(directory_iterator iter) noexcept
-{
-    return iter;
-}
-
-GHC_INLINE directory_iterator end(const directory_iterator&) noexcept
-{
-    return directory_iterator();
-}
-
-//-----------------------------------------------------------------------------
-// [fs.class.rec.dir.itr] class recursive_directory_iterator
-
-GHC_INLINE recursive_directory_iterator::recursive_directory_iterator() noexcept
-    : _impl(new recursive_directory_iterator_impl(directory_options::none, true))
-{
-    _impl->_dir_iter_stack.push(directory_iterator());
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p)
-    : _impl(new recursive_directory_iterator_impl(directory_options::none, true))
-{
-    _impl->_dir_iter_stack.push(directory_iterator(p));
-}
-
-GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options)
-    : _impl(new recursive_directory_iterator_impl(options, true))
-{
-    _impl->_dir_iter_stack.push(directory_iterator(p, options));
-}
-#endif
-
-GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept
-    : _impl(new recursive_directory_iterator_impl(options, true))
-{
-    _impl->_dir_iter_stack.push(directory_iterator(p, options, ec));
-}
-
-GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, std::error_code& ec) noexcept
-    : _impl(new recursive_directory_iterator_impl(directory_options::none, true))
-{
-    _impl->_dir_iter_stack.push(directory_iterator(p, ec));
-}
-
-GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const recursive_directory_iterator& rhs)
-    : _impl(rhs._impl)
-{
-}
-
-GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept
-    : _impl(std::move(rhs._impl))
-{
-}
-
-GHC_INLINE recursive_directory_iterator::~recursive_directory_iterator() {}
-
-// [fs.rec.dir.itr.members] observers
-GHC_INLINE directory_options recursive_directory_iterator::options() const
-{
-    return _impl->_options;
-}
-
-GHC_INLINE int recursive_directory_iterator::depth() const
-{
-    return static_cast<int>(_impl->_dir_iter_stack.size() - 1);
-}
-
-GHC_INLINE bool recursive_directory_iterator::recursion_pending() const
-{
-    return _impl->_recursion_pending;
-}
-
-GHC_INLINE const directory_entry& recursive_directory_iterator::operator*() const
-{
-    return *(_impl->_dir_iter_stack.top());
-}
-
-GHC_INLINE const directory_entry* recursive_directory_iterator::operator->() const
-{
-    return &(*(_impl->_dir_iter_stack.top()));
-}
-
-// [fs.rec.dir.itr.members] modifiers recursive_directory_iterator&
-GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(const recursive_directory_iterator& rhs)
-{
-    _impl = rhs._impl;
-    return *this;
-}
-
-GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(recursive_directory_iterator&& rhs) noexcept
-{
-    _impl = std::move(rhs._impl);
-    return *this;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator++()
-{
-    std::error_code ec;
-    increment(ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec);
-    }
-    return *this;
-}
-#endif
-
-GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept
-{
-    bool isSymLink = (*this)->is_symlink(ec);
-    bool isDir = !ec && (*this)->is_directory(ec);
-    if(isSymLink && detail::is_not_found_error(ec)) {
-        ec.clear();
-    }
-    if(!ec) {
-        if (recursion_pending() && isDir && (!isSymLink || (options() & directory_options::follow_directory_symlink) != directory_options::none)) {
-            _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec));
-        }
-        else {
-            _impl->_dir_iter_stack.top().increment(ec);
-        }
-        if (!ec) {
-            while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) {
-                _impl->_dir_iter_stack.pop();
-                _impl->_dir_iter_stack.top().increment(ec);
-            }
-        }
-        else if (!_impl->_dir_iter_stack.empty()) {
-            _impl->_dir_iter_stack.pop();
-        }
-        _impl->_recursion_pending = true;
-    }
-    return *this;
-}
-
-#ifdef GHC_WITH_EXCEPTIONS
-GHC_INLINE void recursive_directory_iterator::pop()
-{
-    std::error_code ec;
-    pop(ec);
-    if (ec) {
-        throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec);
-    }
-}
-#endif
-
-GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec)
-{
-    if (depth() == 0) {
-        *this = recursive_directory_iterator();
-    }
-    else {
-        do {
-            _impl->_dir_iter_stack.pop();
-            _impl->_dir_iter_stack.top().increment(ec);
-        } while (depth() && _impl->_dir_iter_stack.top() == directory_iterator());
-    }
-}
-
-GHC_INLINE void recursive_directory_iterator::disable_recursion_pending()
-{
-    _impl->_recursion_pending = false;
-}
-
-// other members as required by [input.iterators]
-GHC_INLINE bool recursive_directory_iterator::operator==(const recursive_directory_iterator& rhs) const
-{
-    return _impl->_dir_iter_stack.top() == rhs._impl->_dir_iter_stack.top();
-}
-
-GHC_INLINE bool recursive_directory_iterator::operator!=(const recursive_directory_iterator& rhs) const
-{
-    return _impl->_dir_iter_stack.top() != rhs._impl->_dir_iter_stack.top();
-}
-
-// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions
-GHC_INLINE recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept
-{
-    return iter;
-}
-
-GHC_INLINE recursive_directory_iterator end(const recursive_directory_iterator&) noexcept
-{
-    return recursive_directory_iterator();
-}
-
-#endif  // GHC_EXPAND_IMPL
-
-}  // namespace filesystem
-}  // namespace ghc
-
-// cleanup some macros
-#undef GHC_INLINE
-#undef GHC_EXPAND_IMPL
-
-#endif  // GHC_FILESYSTEM_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json.cpp
deleted file mode 100644
index f1cfb14..0000000
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json.cpp
+++ /dev/null
@@ -1,1505 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* 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 "fmt/format.h"
-#include "wpi/raw_ostream.h"
-
-namespace wpi {
-namespace detail {
-
-exception::exception(int id_, std::string_view what_arg)
-    : id(id_), m(std::string{what_arg}) {}
-
-parse_error parse_error::create(int id_, std::size_t byte_, std::string_view what_arg)
-{
-    if (byte_ != 0)
-        return parse_error(id_, byte_, fmt::format("[json.exception.parse_error.{}] parse error at {}: {}", id_, byte_, what_arg));
-    else
-        return parse_error(id_, byte_, fmt::format("[json.exception.parse_error.{}] parse error: {}", id_, what_arg));
-}
-
-invalid_iterator invalid_iterator::create(int id_, std::string_view what_arg)
-{
-    return invalid_iterator(id_, fmt::format("[json.exception.invalid_iterator.{}] {}", id_, what_arg));
-}
-
-invalid_iterator invalid_iterator::create(int id_, std::string_view what_arg, std::string_view type_info)
-{
-    return invalid_iterator(id_, fmt::format("[json.exception.invalid_iterator.{}] {} {}", id_, what_arg, type_info));
-}
-
-type_error type_error::create(int id_, std::string_view what_arg)
-{
-    return type_error(id_, fmt::format("[json.exception.type_error.{}] {}", id_, what_arg));
-}
-
-type_error type_error::create(int id_, std::string_view what_arg, std::string_view type_info)
-{
-    return type_error(id_, fmt::format("[json.exception.type_error.{}] {} {}", id_, what_arg, type_info));
-}
-
-out_of_range out_of_range::create(int id_, std::string_view what_arg)
-{
-    return out_of_range(id_, fmt::format("[json.exception.out_of_range.{}] {}", id_, what_arg));
-}
-
-other_error other_error::create(int id_, std::string_view what_arg)
-{
-    return other_error(id_, fmt::format("[json.exception.other_error.{}] {}", 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, fmt::format("array index {} is out of range", idx)));
-        }
-    }
-    else
-    {
-        JSON_THROW(type_error::create(304, "cannot use at() with", 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, fmt::format("array index {} is out of range", idx)));
-        }
-    }
-    else
-    {
-        JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
-    }
-}
-
-json::reference json::at(std::string_view 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, fmt::format("key '{}' not found", key)));
-        }
-        return it->second;
-    }
-    else
-    {
-        JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
-    }
-}
-
-json::const_reference json::at(std::string_view 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, fmt::format("key '{}' not found", key)));
-        }
-        return it->second;
-    }
-    else
-    {
-        JSON_THROW(type_error::create(304, "cannot use at() with", 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", 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", type_name()));
-}
-
-json::reference json::operator[](std::string_view 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", type_name()));
-}
-
-json::const_reference json::operator[](std::string_view 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", type_name()));
-}
-
-json::size_type json::erase(std::string_view 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", 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, fmt::format("array index {} is out of range", idx)));
-        }
-
-        m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
-    }
-    else
-    {
-        JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
-    }
-}
-
-json::iterator json::find(std::string_view key)
-{
-    auto result = end();
-
-    if (is_object())
-    {
-        result.m_it.object_iterator = m_value.object->find(key);
-    }
-
-    return result;
-}
-
-json::const_iterator json::find(std::string_view 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(std::string_view 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", 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", 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<std::string_view, 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", 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", 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", 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", 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", 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", type_name()));
-    }
-    if (JSON_UNLIKELY(not j.is_object()))
-    {
-        JSON_THROW(type_error::create(312, "cannot use update() with", 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", 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, fmt::format("array index {} is out of range", idx)));
-                        }
-                        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, fmt::format("key '{}' not found", last_path)));
-            }
-        }
-        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, fmt::format("{} must have member '{}'", error_msg, 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, fmt::format("{} must have string member '{}'", error_msg, 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, fmt::format("unsuccessful: {}", val.dump())));
-                }
-
-                break;
-            }
-
-            case patch_operations::invalid:
-            {
-                // op must be "add", "remove", "replace", "move", "copy", or
-                // "test"
-                JSON_THROW(parse_error::create(105, 0, fmt::format("operation value '{}' is invalid", op)));
-            }
-        }
-    }
-
-    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(std::string{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(std::string{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/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_binary_reader.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_binary_reader.cpp
deleted file mode 100644
index 2081359..0000000
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_binary_reader.cpp
+++ /dev/null
@@ -1,1415 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* 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 "fmt/format.h"
-#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, fmt::format("error reading CBOR; last byte: {:#02x}", 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,
-                fmt::format("error reading MessagePack; last byte: {:#02x}", 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,
-                fmt::format("expected a CBOR string; last byte: {:#02x}", 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,
-                fmt::format("expected a MessagePack string; last byte: {:#02x}", 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,
-                fmt::format("expected a UBJSON string; last byte: {:#02x}", 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,
-                fmt::format("expected '#' after UBJSON type information; last byte: {:#02x}", 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,
-                    fmt::format("byte after 'C' must be in range 0x00..0x7F; last byte: {:#02x}", 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,
-                fmt::format("error reading UBJSON; last byte: {:#02x}", 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,
-                fmt::format("excessive array size: {}", 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,
-                fmt::format("excessive object size: {}", 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(std::span<const 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(std::span<const 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(std::span<const uint8_t> arr, const bool strict)
-{
-    raw_mem_istream is(arr);
-    return from_ubjson(is, strict);
-}
-
-}  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_binary_writer.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_binary_writer.cpp
deleted file mode 100644
index e25d478..0000000
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_binary_writer.cpp
+++ /dev/null
@@ -1,1091 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* 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 "fmt/format.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(std::string_view str);
-
-    void write_msgpack_string(std::string_view 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(std::string_view 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(std::string_view 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 << std::span{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, fmt::format("number overflow serializing {}", 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, fmt::format("number overflow serializing {}", 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;
-}
-
-std::span<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();
-}
-
-std::span<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;
-}
-
-std::span<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();
-}
-
-std::span<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;
-}
-
-std::span<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();
-}
-
-std::span<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/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_parser.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_parser.cpp
deleted file mode 100644
index 3c29489..0000000
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_parser.cpp
+++ /dev/null
@@ -1,1968 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* 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 "fmt/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
-    std::string_view 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 << fmt::format("<U+{:04X}>", c);
-        }
-        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(std::string_view(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,
-                        fmt::format("number overflow parsing '{}'", 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(std::string_view s,
-                        const parser_callback_t cb,
-                        const bool allow_exceptions)
-{
-    raw_mem_istream is(std::span<const char>(s.data(), s.size()));
-    return parse(is, cb, allow_exceptions);
-}
-
-json json::parse(std::span<const 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(std::string_view s)
-{
-    raw_mem_istream is(std::span<const char>(s.data(), s.size()));
-    return parser(is).accept(true);
-}
-
-bool json::accept(std::span<const 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/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_pointer.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_pointer.cpp
deleted file mode 100644
index 51548b3..0000000
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_pointer.cpp
+++ /dev/null
@@ -1,540 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* 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 "fmt/format.h"
-#include "wpi/SmallString.h"
-#include "wpi/StringExtras.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(std::string_view s)
-{
-    SmallString<128> str{s};
-    std::size_t processed_chars = 0;
-    const int res = std::stoi(str.c_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, fmt::format("unresolved reference token '{}'", 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, fmt::format("array index '{}' is not a number", reference_token)));
-                }
-                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,
-                        fmt::format("array index '{}' must not begin with '0'", reference_token)));
-                }
-
-                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,
-                            fmt::format("array index '{}' is not a number", reference_token)));
-                    }
-                }
-                break;
-            }
-
-            default:
-                JSON_THROW(detail::out_of_range::create(404,
-                    fmt::format("unresolved reference token '{}'", 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,
-                        fmt::format("array index '-' ({}) is out of range", ptr->m_value.array->size())));
-                }
-
-                // 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,
-                        fmt::format("array index '{}' must not begin with '0'", reference_token)));
-                }
-
-                // 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,
-                        fmt::format("array index '{}' is not a number", reference_token)));
-                }
-                break;
-            }
-
-            default:
-                JSON_THROW(detail::out_of_range::create(404,
-                    fmt::format("unresolved reference token '{}'", 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,
-                        fmt::format("array index '-' ({}) is out of range", ptr->m_value.array->size())));
-                }
-
-                // 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,
-                        fmt::format("array index '{}' must not begin with '0'", reference_token)));
-                }
-
-                // 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,
-                        fmt::format("array index '{}' is not a number", reference_token)));
-                }
-                break;
-            }
-
-            default:
-                JSON_THROW(detail::out_of_range::create(404,
-                    fmt::format("unresolved reference token '{}'", 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,
-                        fmt::format("array index '-' ({}) is out of range", ptr->m_value.array->size())));
-                }
-
-                // 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,
-                        fmt::format("array index '{}' must not begin with '0'", reference_token)));
-                }
-
-                // 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,
-                        fmt::format("array index '{}' is not a number", reference_token)));
-                }
-                break;
-            }
-
-            default:
-                JSON_THROW(detail::out_of_range::create(404,
-                    fmt::format("unresolved reference token '{}'", reference_token)));
-        }
-    }
-
-    return *ptr;
-}
-
-std::vector<std::string> json_pointer::split(std::string_view ref_str)
-{
-    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,
-            fmt::format("JSON pointer must be empty or begin with '/' - was: '{}'", 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 = slice(ref_str, start, slash);
-
-        // check reference tokens are properly escaped
-        for (std::size_t pos = reference_token.find_first_of('~');
-                pos != std::string_view::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(std::string_view 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
-                result[reference_string] = nullptr;
-            }
-            else
-            {
-                // iterate array and use index as reference string
-                for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
-                {
-                    flatten(fmt::format("{}/{}", reference_string, 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
-                result[reference_string] = nullptr;
-            }
-            else
-            {
-                // iterate object and use keys as reference string
-                for (const auto& element : *value.m_value.object)
-                {
-                    flatten(fmt::format("{}/{}", reference_string, escape(std::string{element.first()})), element.second, result);
-                }
-            }
-            break;
-        }
-
-        default:
-        {
-            // add primitive value with its reference string
-            result[reference_string] = 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/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_serializer.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_serializer.cpp
deleted file mode 100644
index 1101f66..0000000
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/cpp/json_serializer.cpp
+++ /dev/null
@@ -1,1531 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* 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 "fmt/format.h"
-#include "wpi/SmallString.h"
-#include "wpi/raw_os_ostream.h"
-
-#include "wpi/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(std::string_view 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 << fmt::format("\\u{:04x}", codepoint);
-                            }
-                            else
-                            {
-                                o << fmt::format("\\u{:04x}", 0xD7C0 + (codepoint >> 10));
-                                o << fmt::format("\\u{:04x}", 0xDC00 + (codepoint & 0x3FF));
-                            }
-                        }
-                        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, fmt::format("invalid UTF-8 byte at index {}: {:#02x}", i, 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, fmt::format("incomplete UTF-8 string; last byte: {:#02x}", 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/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/adl_serializer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/adl_serializer.h
new file mode 100644
index 0000000..ad339bc
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/adl_serializer.h
@@ -0,0 +1,55 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <utility>
+
+#include <wpi/detail/abi_macros.h>
+#include <wpi/detail/conversions/from_json.h>
+#include <wpi/detail/conversions/to_json.h>
+#include <wpi/detail/meta/identity_tag.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+
+/// @sa https://json.nlohmann.me/api/adl_serializer/
+template<typename ValueType, typename>
+struct adl_serializer
+{
+    /// @brief convert a JSON value to any value type
+    /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
+    template<typename BasicJsonType, typename TargetType = ValueType>
+    static auto from_json(BasicJsonType && j, TargetType& val) noexcept(
+        noexcept(::wpi::from_json(std::forward<BasicJsonType>(j), val)))
+    -> decltype(::wpi::from_json(std::forward<BasicJsonType>(j), val), void())
+    {
+        ::wpi::from_json(std::forward<BasicJsonType>(j), val);
+    }
+
+    /// @brief convert a JSON value to any value type
+    /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
+    template<typename BasicJsonType, typename TargetType = ValueType>
+    static auto from_json(BasicJsonType && j) noexcept(
+    noexcept(::wpi::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))
+    -> decltype(::wpi::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))
+    {
+        return ::wpi::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});
+    }
+
+    /// @brief convert any value type to a JSON value
+    /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/
+    template<typename BasicJsonType, typename TargetType = ValueType>
+    static auto to_json(BasicJsonType& j, TargetType && val) noexcept(
+        noexcept(::wpi::to_json(j, std::forward<TargetType>(val))))
+    -> decltype(::wpi::to_json(j, std::forward<TargetType>(val)), void())
+    {
+        ::wpi::to_json(j, std::forward<TargetType>(val));
+    }
+};
+
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/byte_container_with_subtype.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/byte_container_with_subtype.h
new file mode 100644
index 0000000..f97a2a7
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/byte_container_with_subtype.h
@@ -0,0 +1,103 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstdint> // uint8_t, uint64_t
+#include <tuple> // tie
+#include <utility> // move
+
+#include <wpi/detail/abi_macros.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+
+/// @brief an internal type for a backed binary type
+/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/
+template<typename BinaryType>
+class byte_container_with_subtype : public BinaryType
+{
+  public:
+    using container_type = BinaryType;
+    using subtype_type = std::uint64_t;
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype() noexcept(noexcept(container_type()))
+        : container_type()
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))
+        : container_type(b)
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))
+        : container_type(std::move(b))
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))
+        : container_type(b)
+        , m_subtype(subtype_)
+        , m_has_subtype(true)
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))
+        : container_type(std::move(b))
+        , m_subtype(subtype_)
+        , m_has_subtype(true)
+    {}
+
+    bool operator==(const byte_container_with_subtype& rhs) const
+    {
+        return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
+               std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
+    }
+
+    bool operator!=(const byte_container_with_subtype& rhs) const
+    {
+        return !(rhs == *this);
+    }
+
+    /// @brief sets the binary subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/
+    void set_subtype(subtype_type subtype_) noexcept
+    {
+        m_subtype = subtype_;
+        m_has_subtype = true;
+    }
+
+    /// @brief return the binary subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/
+    constexpr subtype_type subtype() const noexcept
+    {
+        return m_has_subtype ? m_subtype : static_cast<subtype_type>(-1);
+    }
+
+    /// @brief return whether the value has a subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/
+    constexpr bool has_subtype() const noexcept
+    {
+        return m_has_subtype;
+    }
+
+    /// @brief clears the binary subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/
+    void clear_subtype() noexcept
+    {
+        m_subtype = 0;
+        m_has_subtype = false;
+    }
+
+  private:
+    subtype_type m_subtype = 0;
+    bool m_has_subtype = false;
+};
+
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/abi_macros.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/abi_macros.h
new file mode 100644
index 0000000..438af89
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/abi_macros.h
@@ -0,0 +1,61 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+// This file contains all macro definitions affecting or depending on the ABI
+
+#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
+    #if defined(WPI_JSON_VERSION_MAJOR) && defined(WPI_JSON_VERSION_MINOR) && defined(WPI_JSON_VERSION_PATCH)
+        #if WPI_JSON_VERSION_MAJOR != 3 || WPI_JSON_VERSION_MINOR != 11 || WPI_JSON_VERSION_PATCH != 2
+            #warning "Already included a different version of the library!"
+        #endif
+    #endif
+#endif
+
+#define WPI_JSON_VERSION_MAJOR 3   // NOLINT(modernize-macro-to-enum)
+#define WPI_JSON_VERSION_MINOR 11  // NOLINT(modernize-macro-to-enum)
+#define WPI_JSON_VERSION_PATCH 2   // NOLINT(modernize-macro-to-enum)
+
+#ifndef JSON_DIAGNOSTICS
+    #define JSON_DIAGNOSTICS 0
+#endif
+
+#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
+#endif
+
+#if JSON_DIAGNOSTICS
+    #define WPI_JSON_ABI_TAG_DIAGNOSTICS _diag
+#else
+    #define WPI_JSON_ABI_TAG_DIAGNOSTICS
+#endif
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    #define WPI_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
+#else
+    #define WPI_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
+#endif
+
+#ifndef WPI_JSON_NAMESPACE
+#define WPI_JSON_NAMESPACE               \
+    wpi::WPI_JSON_NAMESPACE_CONCAT( \
+            WPI_JSON_ABI_TAGS,           \
+            WPI_JSON_NAMESPACE_VERSION)
+#endif
+
+#ifndef WPI_JSON_NAMESPACE_BEGIN
+#define WPI_JSON_NAMESPACE_BEGIN \
+    namespace wpi                \
+    {
+#endif
+
+#ifndef WPI_JSON_NAMESPACE_END
+#define WPI_JSON_NAMESPACE_END \
+    }  // namespace wpi
+#endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/conversions/from_json.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/conversions/from_json.h
new file mode 100644
index 0000000..f232f23
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/conversions/from_json.h
@@ -0,0 +1,497 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <algorithm> // transform
+#include <array> // array
+#include <forward_list> // forward_list
+#include <iterator> // inserter, front_inserter, end
+#include <map> // map
+#include <string> // string
+#include <tuple> // tuple, make_tuple
+#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
+#include <unordered_map> // unordered_map
+#include <utility> // pair, declval
+#include <valarray> // valarray
+
+#include <wpi/detail/exceptions.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/meta/cpp_future.h>
+#include <wpi/detail/meta/identity_tag.h>
+#include <wpi/detail/meta/std_fs.h>
+#include <wpi/detail/meta/type_traits.h>
+#include <wpi/detail/string_concat.h>
+#include <wpi/detail/value_t.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
+    }
+    n = nullptr;
+}
+
+// overloads for basic_json template parameters
+template < typename BasicJsonType, typename ArithmeticType,
+           enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
+                         !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+                         int > = 0 >
+void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
+{
+    switch (static_cast<value_t>(j))
+    {
+        case value_t::number_unsigned:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+            break;
+        }
+        case value_t::number_integer:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+            break;
+        }
+        case value_t::number_float:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+            break;
+        }
+
+        case value_t::null:
+        case value_t::object:
+        case value_t::array:
+        case value_t::string:
+        case value_t::boolean:
+        case value_t::binary:
+        case value_t::discarded:
+        default:
+            JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
+    }
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
+    }
+    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+    }
+    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template <
+    typename BasicJsonType, typename StringType,
+    enable_if_t <
+        std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
+        && is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value
+        && !std::is_same<typename BasicJsonType::string_t, StringType>::value
+        && !is_json_ref<StringType>::value, int > = 0 >
+inline void from_json(const BasicJsonType& j, StringType& s)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+    }
+
+    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+#if !JSON_DISABLE_ENUM_SERIALIZATION
+template<typename BasicJsonType, typename EnumType,
+         enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, EnumType& e)
+{
+    typename std::underlying_type<EnumType>::type val;
+    get_arithmetic_value(j, val);
+    e = static_cast<EnumType>(val);
+}
+#endif  // JSON_DISABLE_ENUM_SERIALIZATION
+
+// forward_list doesn't have an insert method
+template<typename BasicJsonType, typename T, typename Allocator,
+         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    l.clear();
+    std::transform(j.rbegin(), j.rend(),
+                   std::front_inserter(l), [](const BasicJsonType & i)
+    {
+        return i.template get<T>();
+    });
+}
+
+// valarray doesn't have an insert method
+template<typename BasicJsonType, typename T,
+         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    l.resize(j.size());
+    std::transform(j.begin(), j.end(), std::begin(l),
+                   [](const BasicJsonType & elem)
+    {
+        return elem.template get<T>();
+    });
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+auto from_json(const BasicJsonType& j, T (&arr)[N])  // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+-> decltype(j.template get<T>(), void())
+{
+    for (std::size_t i = 0; i < N; ++i)
+    {
+        arr[i] = j.at(i).template get<T>();
+    }
+}
+
+template<typename BasicJsonType>
+inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
+{
+    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
+                          priority_tag<2> /*unused*/)
+-> decltype(j.template get<T>(), void())
+{
+    for (std::size_t i = 0; i < N; ++i)
+    {
+        arr[i] = j.at(i).template get<T>();
+    }
+}
+
+template<typename BasicJsonType, typename ConstructibleArrayType,
+         enable_if_t<
+             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+             int> = 0>
+auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
+-> decltype(
+    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
+    j.template get<typename ConstructibleArrayType::value_type>(),
+    void())
+{
+    using std::end;
+
+    ConstructibleArrayType ret;
+    ret.reserve(j.size());
+    std::transform(j.begin(), j.end(),
+                   std::inserter(ret, end(ret)), [](const BasicJsonType & i)
+    {
+        // get<BasicJsonType>() returns *this, this won't call a from_json
+        // method when value_type is BasicJsonType
+        return i.template get<typename ConstructibleArrayType::value_type>();
+    });
+    arr = std::move(ret);
+}
+
+template<typename BasicJsonType, typename ConstructibleArrayType,
+         enable_if_t<
+             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+             int> = 0>
+inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
+                                 priority_tag<0> /*unused*/)
+{
+    using std::end;
+
+    ConstructibleArrayType ret;
+    std::transform(
+        j.begin(), j.end(), std::inserter(ret, end(ret)),
+        [](const BasicJsonType & i)
+    {
+        // get<BasicJsonType>() returns *this, this won't call a from_json
+        // method when value_type is BasicJsonType
+        return i.template get<typename ConstructibleArrayType::value_type>();
+    });
+    arr = std::move(ret);
+}
+
+template < typename BasicJsonType, typename ConstructibleArrayType,
+           enable_if_t <
+               is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
+               !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
+               !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
+               !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
+               !is_basic_json<ConstructibleArrayType>::value,
+               int > = 0 >
+auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
+-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
+j.template get<typename ConstructibleArrayType::value_type>(),
+void())
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+
+    from_json_array_impl(j, arr, priority_tag<3> {});
+}
+
+template < typename BasicJsonType, typename T, std::size_t... Idx >
+std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
+        identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
+{
+    return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
+}
+
+template < typename BasicJsonType, typename T, std::size_t N >
+auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
+-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+
+    return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
+    }
+
+    bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
+}
+
+template<typename BasicJsonType, typename ConstructibleObjectType,
+         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
+    }
+
+    ConstructibleObjectType ret;
+    const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+    using value_type = typename ConstructibleObjectType::value_type;
+    std::transform(
+        inner_object->begin(), inner_object->end(),
+        std::inserter(ret, ret.begin()),
+        [](typename BasicJsonType::object_t::value_type const & p)
+    {
+        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
+    });
+    obj = std::move(ret);
+}
+
+// overload for arithmetic types, not chosen for basic_json template arguments
+// (BooleanType, etc..); note: Is it really necessary to provide explicit
+// overloads for boolean_t etc. in case of a custom BooleanType which is not
+// an arithmetic type?
+template < typename BasicJsonType, typename ArithmeticType,
+           enable_if_t <
+               std::is_arithmetic<ArithmeticType>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+               int > = 0 >
+inline void from_json(const BasicJsonType& j, ArithmeticType& val)
+{
+    switch (static_cast<value_t>(j))
+    {
+        case value_t::number_unsigned:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+            break;
+        }
+        case value_t::number_integer:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+            break;
+        }
+        case value_t::number_float:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+            break;
+        }
+        case value_t::boolean:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
+            break;
+        }
+
+        case value_t::null:
+        case value_t::object:
+        case value_t::array:
+        case value_t::string:
+        case value_t::binary:
+        case value_t::discarded:
+        default:
+            JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
+    }
+}
+
+template<typename BasicJsonType, typename... Args, std::size_t... Idx>
+std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
+{
+    return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
+}
+
+template < typename BasicJsonType, class A1, class A2 >
+std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
+{
+    return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),
+            std::forward<BasicJsonType>(j).at(1).template get<A2>()};
+}
+
+template<typename BasicJsonType, typename A1, typename A2>
+inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
+{
+    p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
+}
+
+template<typename BasicJsonType, typename... Args>
+std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
+{
+    return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
+}
+
+template<typename BasicJsonType, typename... Args>
+inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
+{
+    t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
+}
+
+template<typename BasicJsonType, typename TupleRelated>
+auto from_json(BasicJsonType&& j, TupleRelated&& t)
+-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+
+    return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
+}
+
+template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
+           typename = enable_if_t < !std::is_constructible <
+                                        typename BasicJsonType::string_t, Key >::value >>
+inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    m.clear();
+    for (const auto& p : j)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
+        {
+            JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
+        }
+        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+    }
+}
+
+template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
+           typename = enable_if_t < !std::is_constructible <
+                                        typename BasicJsonType::string_t, Key >::value >>
+inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    m.clear();
+    for (const auto& p : j)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
+        {
+            JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
+        }
+        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+    }
+}
+
+#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, std_fs::path& p)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+    }
+    p = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+#endif
+
+struct from_json_fn
+{
+    template<typename BasicJsonType, typename T>
+    auto operator()(const BasicJsonType& j, T&& val) const
+    noexcept(noexcept(from_json(j, std::forward<T>(val))))
+    -> decltype(from_json(j, std::forward<T>(val)))
+    {
+        return from_json(j, std::forward<T>(val));
+    }
+};
+
+}  // namespace detail
+
+#ifndef JSON_HAS_CPP_17
+/// namespace to hold default `from_json` function
+/// to see why this is required:
+/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
+{
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
+    detail::static_const<detail::from_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
+}  // namespace
+#endif
+
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/conversions/to_chars.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/conversions/to_chars.h
new file mode 100644
index 0000000..f1845dc
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/conversions/to_chars.h
@@ -0,0 +1,1118 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/>
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <array> // array
+#include <cmath>   // signbit, isfinite
+#include <cstdint> // intN_t, uintN_t
+#include <cstring> // memcpy, memmove
+#include <limits> // numeric_limits
+#include <type_traits> // conditional
+
+#include <wpi/detail/macro_scope.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@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
+
+    std::uint64_t f = 0;
+    int e = 0;
+
+    constexpr diyfp(std::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
+    {
+        JSON_ASSERT(x.e == y.e);
+        JSON_ASSERT(x.f >= y.f);
+
+        return {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 std::uint64_t u_lo = x.f & 0xFFFFFFFFu;
+        const std::uint64_t u_hi = x.f >> 32u;
+        const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;
+        const std::uint64_t v_hi = y.f >> 32u;
+
+        const std::uint64_t p0 = u_lo * v_lo;
+        const std::uint64_t p1 = u_lo * v_hi;
+        const std::uint64_t p2 = u_hi * v_lo;
+        const std::uint64_t p3 = u_hi * v_hi;
+
+        const std::uint64_t p0_hi = p0 >> 32u;
+        const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;
+        const std::uint64_t p1_hi = p1 >> 32u;
+        const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;
+        const std::uint64_t p2_hi = p2 >> 32u;
+
+        std::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 += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up
+
+        const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);
+
+        return {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
+    {
+        JSON_ASSERT(x.f != 0);
+
+        while ((x.f >> 63u) == 0)
+        {
+            x.f <<= 1u;
+            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;
+
+        JSON_ASSERT(delta >= 0);
+        JSON_ASSERT(((x.f << delta) >> delta) == x.f);
+
+        return {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)
+{
+    JSON_ASSERT(std::isfinite(value));
+    JSON_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 std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)
+
+    using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;
+
+    const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));
+    const std::uint64_t E = bits >> (kPrecision - 1);
+    const std::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 && 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
+{
+    std::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 exact 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 kCachedPowersMinDecExp = -300;
+    constexpr int kCachedPowersDecStep = 8;
+
+    static constexpr std::array<cached_power, 79> 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
+    JSON_ASSERT(e >= -1500);
+    JSON_ASSERT(e <=  1500);
+    const int f = kAlpha - e - 1;
+    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);
+
+    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
+    JSON_ASSERT(index >= 0);
+    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());
+
+    const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];
+    JSON_ASSERT(kAlpha <= cached.e + e + 64);
+    JSON_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 std::uint32_t n, std::uint32_t& pow10)
+{
+    // LCOV_EXCL_START
+    if (n >= 1000000000)
+    {
+        pow10 = 1000000000;
+        return 10;
+    }
+    // LCOV_EXCL_STOP
+    if (n >= 100000000)
+    {
+        pow10 = 100000000;
+        return  9;
+    }
+    if (n >= 10000000)
+    {
+        pow10 = 10000000;
+        return  8;
+    }
+    if (n >= 1000000)
+    {
+        pow10 = 1000000;
+        return  7;
+    }
+    if (n >= 100000)
+    {
+        pow10 = 100000;
+        return  6;
+    }
+    if (n >= 10000)
+    {
+        pow10 = 10000;
+        return  5;
+    }
+    if (n >= 1000)
+    {
+        pow10 = 1000;
+        return  4;
+    }
+    if (n >= 100)
+    {
+        pow10 = 100;
+        return  3;
+    }
+    if (n >= 10)
+    {
+        pow10 = 10;
+        return  2;
+    }
+
+    pow10 = 1;
+    return 1;
+}
+
+inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,
+                         std::uint64_t rest, std::uint64_t ten_k)
+{
+    JSON_ASSERT(len >= 1);
+    JSON_ASSERT(dist <= delta);
+    JSON_ASSERT(rest <= delta);
+    JSON_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
+            && delta - rest >= ten_k
+            && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))
+    {
+        JSON_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+].
+
+    JSON_ASSERT(M_plus.e >= kAlpha);
+    JSON_ASSERT(M_plus.e <= kGamma);
+
+    std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
+    std::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(std::uint64_t{1} << -M_plus.e, M_plus.e);
+
+    auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)
+    std::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]
+
+    JSON_ASSERT(p1 > 0);
+
+    std::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 std::uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)
+        const std::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)
+        //
+        JSON_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 std::uint64_t rest = (std::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 std::uint64_t ten_n = std::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
+
+    JSON_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
+        //
+        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
+        p2 *= 10;
+        const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e
+        const std::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
+        //
+        JSON_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 std::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.
+*/
+JSON_HEDLEY_NON_NULL(1)
+inline void grisu2(char* buf, int& len, int& decimal_exponent,
+                   diyfp m_minus, diyfp v, diyfp m_plus)
+{
+    JSON_ASSERT(m_plus.e == m_minus.e);
+    JSON_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>
+JSON_HEDLEY_NON_NULL(1)
+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");
+
+    JSON_ASSERT(std::isfinite(value));
+    JSON_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' (https://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 floats to doubles, 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
+*/
+JSON_HEDLEY_NON_NULL(1)
+JSON_HEDLEY_RETURNS_NON_NULL
+inline char* append_exponent(char* buf, int e)
+{
+    JSON_ASSERT(e > -1000);
+    JSON_ASSERT(e <  1000);
+
+    if (e < 0)
+    {
+        e = -e;
+        *buf++ = '-';
+    }
+    else
+    {
+        *buf++ = '+';
+    }
+
+    auto k = static_cast<std::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
+*/
+JSON_HEDLEY_NON_NULL(1)
+JSON_HEDLEY_RETURNS_NON_NULL
+inline char* format_buffer(char* buf, int len, int decimal_exponent,
+                           int min_exp, int max_exp)
+{
+    JSON_ASSERT(min_exp < 0);
+    JSON_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 && n <= max_exp)
+    {
+        // digits[000]
+        // len <= max_exp + 2
+
+        std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));
+        // Make it look like a floating-point number (#362, #378)
+        buf[n + 0] = '.';
+        buf[n + 1] = '0';
+        return buf + (static_cast<size_t>(n) + 2);
+    }
+
+    if (0 < n && n <= max_exp)
+    {
+        // dig.its
+        // len <= max_digits10 + 1
+
+        JSON_ASSERT(k > n);
+
+        std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));
+        buf[n] = '.';
+        return buf + (static_cast<size_t>(k) + 1U);
+    }
+
+    if (min_exp < n && n <= 0)
+    {
+        // 0.[000]digits
+        // len <= 2 + (-min_exp - 1) + max_digits10
+
+        std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));
+        buf[0] = '0';
+        buf[1] = '.';
+        std::memset(buf + 2, '0', static_cast<size_t>(-n));
+        return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(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 + static_cast<size_t>(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>
+JSON_HEDLEY_NON_NULL(1, 2)
+JSON_HEDLEY_RETURNS_NON_NULL
+char* to_chars(char* first, const char* last, FloatType value)
+{
+    static_cast<void>(last); // maybe unused - fix warning
+    JSON_ASSERT(std::isfinite(value));
+
+    // Use signbit(value) instead of (value < 0) since signbit works for -0.
+    if (std::signbit(value))
+    {
+        value = -value;
+        *first++ = '-';
+    }
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+    if (value == 0) // +-0
+    {
+        *first++ = '0';
+        // Make it look like a floating-point number (#362, #378)
+        *first++ = '.';
+        *first++ = '0';
+        return first;
+    }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+    JSON_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);
+
+    JSON_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;
+
+    JSON_ASSERT(last - first >= kMaxExp + 2);
+    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
+    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
+
+    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
+}
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/conversions/to_json.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/conversions/to_json.h
new file mode 100644
index 0000000..59db3be
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/conversions/to_json.h
@@ -0,0 +1,446 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <algorithm> // copy
+#include <iterator> // begin, end
+#include <string> // string
+#include <tuple> // tuple, get
+#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
+#include <utility> // move, forward, declval, pair
+#include <valarray> // valarray
+#include <vector> // vector
+
+#include <wpi/detail/iterators/iteration_proxy.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/meta/cpp_future.h>
+#include <wpi/detail/meta/std_fs.h>
+#include <wpi/detail/meta/type_traits.h>
+#include <wpi/detail/value_t.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+//////////////////
+// constructors //
+//////////////////
+
+/*
+ * Note all external_constructor<>::construct functions need to call
+ * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an
+ * allocated value (e.g., a string). See bug issue
+ * https://github.com/nlohmann/json/issues/2865 for more information.
+ */
+
+template<value_t> struct external_constructor;
+
+template<>
+struct external_constructor<value_t::boolean>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::boolean;
+        j.m_value = b;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::string>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::string;
+        j.m_value = s;
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::string;
+        j.m_value = std::move(s);
+        j.assert_invariant();
+    }
+
+    template < typename BasicJsonType, typename CompatibleStringType,
+               enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
+                             int > = 0 >
+    static void construct(BasicJsonType& j, const CompatibleStringType& str)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::string;
+        j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::binary>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::binary;
+        j.m_value = typename BasicJsonType::binary_t(b);
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::binary;
+        j.m_value = typename BasicJsonType::binary_t(std::move(b));
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_float>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::number_float;
+        j.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_unsigned>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::number_unsigned;
+        j.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_integer>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::number_integer;
+        j.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::array>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::array;
+        j.m_value = arr;
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::array;
+        j.m_value = std::move(arr);
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template < typename BasicJsonType, typename CompatibleArrayType,
+               enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
+                             int > = 0 >
+    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
+    {
+        using std::begin;
+        using std::end;
+
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::array;
+        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const std::vector<bool>& arr)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::array;
+        j.m_value = value_t::array;
+        j.m_value.array->reserve(arr.size());
+        for (const bool x : arr)
+        {
+            j.m_value.array->push_back(x);
+            j.set_parent(j.m_value.array->back());
+        }
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType, typename T,
+             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+    static void construct(BasicJsonType& j, const std::valarray<T>& arr)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::array;
+        j.m_value = value_t::array;
+        j.m_value.array->resize(arr.size());
+        if (arr.size() > 0)
+        {
+            std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
+        }
+        j.set_parents();
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::object>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::object;
+        j.m_value = obj;
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+    {
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::object;
+        j.m_value = std::move(obj);
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template < typename BasicJsonType, typename CompatibleObjectType,
+               enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
+    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
+    {
+        using std::begin;
+        using std::end;
+
+        j.m_value.destroy(j.m_type);
+        j.m_type = value_t::object;
+        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
+        j.set_parents();
+        j.assert_invariant();
+    }
+};
+
+/////////////
+// to_json //
+/////////////
+
+template<typename BasicJsonType, typename T,
+         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
+inline void to_json(BasicJsonType& j, T b) noexcept
+{
+    external_constructor<value_t::boolean>::construct(j, b);
+}
+
+template < typename BasicJsonType, typename BoolRef,
+           enable_if_t <
+               ((std::is_same<std::vector<bool>::reference, BoolRef>::value
+                 && !std::is_same <std::vector<bool>::reference, typename BasicJsonType::boolean_t&>::value)
+                || (std::is_same<std::vector<bool>::const_reference, BoolRef>::value
+                    && !std::is_same <detail::uncvref_t<std::vector<bool>::const_reference>,
+                                      typename BasicJsonType::boolean_t >::value))
+               && std::is_convertible<const BoolRef&, typename BasicJsonType::boolean_t>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const BoolRef& b) noexcept
+{
+    external_constructor<value_t::boolean>::construct(j, static_cast<typename BasicJsonType::boolean_t>(b));
+}
+
+template<typename BasicJsonType, typename CompatibleString,
+         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const CompatibleString& s)
+{
+    external_constructor<value_t::string>::construct(j, s);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+{
+    external_constructor<value_t::string>::construct(j, std::move(s));
+}
+
+template<typename BasicJsonType, typename FloatType,
+         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, FloatType val) noexcept
+{
+    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
+         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
+{
+    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberIntegerType,
+         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
+{
+    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
+}
+
+#if !JSON_DISABLE_ENUM_SERIALIZATION
+template<typename BasicJsonType, typename EnumType,
+         enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, EnumType e) noexcept
+{
+    using underlying_type = typename std::underlying_type<EnumType>::type;
+    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
+}
+#endif  // JSON_DISABLE_ENUM_SERIALIZATION
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const std::vector<bool>& e)
+{
+    external_constructor<value_t::array>::construct(j, e);
+}
+
+template < typename BasicJsonType, typename CompatibleArrayType,
+           enable_if_t < is_compatible_array_type<BasicJsonType,
+                         CompatibleArrayType>::value&&
+                         !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
+                         !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
+                         !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
+                         !is_basic_json<CompatibleArrayType>::value,
+                         int > = 0 >
+inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
+{
+    external_constructor<value_t::array>::construct(j, arr);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
+{
+    external_constructor<value_t::binary>::construct(j, bin);
+}
+
+template<typename BasicJsonType, typename T,
+         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
+{
+    external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+{
+    external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template < typename BasicJsonType, typename CompatibleObjectType,
+           enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
+{
+    external_constructor<value_t::object>::construct(j, obj);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+{
+    external_constructor<value_t::object>::construct(j, std::move(obj));
+}
+
+template <
+    typename BasicJsonType, typename T, std::size_t N,
+    enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
+                  const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+                  int > = 0 >
+inline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+{
+    external_constructor<value_t::array>::construct(j, arr);
+}
+
+template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
+{
+    j = { p.first, p.second };
+}
+
+// for https://github.com/nlohmann/json/pull/1134
+template<typename BasicJsonType, typename T,
+         enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const T& b)
+{
+    j = { {b.key(), b.value()} };
+}
+
+template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
+inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
+{
+    j = { std::get<Idx>(t)... };
+}
+
+template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
+inline void to_json(BasicJsonType& j, const T& t)
+{
+    to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
+}
+
+#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const std_fs::path& p)
+{
+    j = p.string();
+}
+#endif
+
+struct to_json_fn
+{
+    template<typename BasicJsonType, typename T>
+    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
+    -> decltype(to_json(j, std::forward<T>(val)), void())
+    {
+        return to_json(j, std::forward<T>(val));
+    }
+};
+}  // namespace detail
+
+#ifndef JSON_HAS_CPP_17
+/// namespace to hold default `to_json` function
+/// to see why this is required:
+/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
+{
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)
+    detail::static_const<detail::to_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
+}  // namespace
+#endif
+
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/exceptions.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/exceptions.h
new file mode 100644
index 0000000..278ffaa
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/exceptions.h
@@ -0,0 +1,255 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef> // nullptr_t
+#include <exception> // exception
+#include <stdexcept> // runtime_error
+#include <string> // to_string
+#include <vector> // vector
+
+#include <wpi/detail/value_t.h>
+#include <wpi/detail/string_escape.h>
+#include <wpi/detail/input/position_t.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/meta/cpp_future.h>
+#include <wpi/detail/meta/type_traits.h>
+#include <wpi/detail/string_concat.h>
+
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+////////////////
+// exceptions //
+////////////////
+
+/// @brief general exception of the @ref basic_json class
+/// @sa https://json.nlohmann.me/api/basic_json/exception/
+class exception : public std::exception
+{
+  public:
+    /// returns the explanatory string
+    const char* what() const noexcept override
+    {
+        return m.what();
+    }
+
+    /// the id of the exception
+    const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
+
+  protected:
+    JSON_HEDLEY_NON_NULL(3)
+    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)
+
+    static std::string name(const std::string& ename, int id_)
+    {
+        return concat("[json.exception.", ename, '.', std::to_string(id_), "] ");
+    }
+
+    static std::string diagnostics(std::nullptr_t /*leaf_element*/)
+    {
+        return "";
+    }
+
+    template<typename BasicJsonType>
+    static std::string diagnostics(const BasicJsonType* leaf_element)
+    {
+#if JSON_DIAGNOSTICS
+        std::vector<std::string> tokens;
+        for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)
+        {
+            switch (current->m_parent->type())
+            {
+                case value_t::array:
+                {
+                    for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i)
+                    {
+                        if (&current->m_parent->m_value.array->operator[](i) == current)
+                        {
+                            tokens.emplace_back(std::to_string(i));
+                            break;
+                        }
+                    }
+                    break;
+                }
+
+                case value_t::object:
+                {
+                    for (const auto& element : *current->m_parent->m_value.object)
+                    {
+                        if (&element.second == current)
+                        {
+                            tokens.emplace_back(element.first.c_str());
+                            break;
+                        }
+                    }
+                    break;
+                }
+
+                case value_t::null: // LCOV_EXCL_LINE
+                case value_t::string: // LCOV_EXCL_LINE
+                case value_t::boolean: // LCOV_EXCL_LINE
+                case value_t::number_integer: // LCOV_EXCL_LINE
+                case value_t::number_unsigned: // LCOV_EXCL_LINE
+                case value_t::number_float: // LCOV_EXCL_LINE
+                case value_t::binary: // LCOV_EXCL_LINE
+                case value_t::discarded: // LCOV_EXCL_LINE
+                default:   // LCOV_EXCL_LINE
+                    break; // LCOV_EXCL_LINE
+            }
+        }
+
+        if (tokens.empty())
+        {
+            return "";
+        }
+
+        auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
+                                   [](const std::string & a, const std::string & b)
+        {
+            return concat(a, '/', detail::escape(b));
+        });
+        return concat('(', str, ") ");
+#else
+        static_cast<void>(leaf_element);
+        return "";
+#endif
+    }
+
+  private:
+    /// an exception object as storage for error messages
+    std::runtime_error m;
+};
+
+/// @brief exception indicating a parse error
+/// @sa https://json.nlohmann.me/api/basic_json/parse_error/
+class parse_error : public exception
+{
+  public:
+    /*!
+    @brief create a parse error exception
+    @param[in] id_       the id of the exception
+    @param[in] pos       the position where the error occurred (or with
+                         chars_read_total=0 if the position cannot be
+                         determined)
+    @param[in] what_arg  the explanatory string
+    @return parse_error object
+    */
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("parse_error", id_), "parse error",
+                               position_string(pos), ": ", exception::diagnostics(context), what_arg);
+        return {id_, pos.chars_read_total, w.c_str()};
+    }
+
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("parse_error", id_), "parse error",
+                               (byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""),
+                               ": ", exception::diagnostics(context), what_arg);
+        return {id_, byte_, w.c_str()};
+    }
+
+    /*!
+    @brief byte index of the parse error
+
+    The byte index of the last read character in the input file.
+
+    @note For an input with n bytes, 1 is the index of the first character and
+          n+1 is the index of the terminating null byte or the end of file.
+          This also holds true when reading a byte vector (CBOR or MessagePack).
+    */
+    const std::size_t byte;
+
+  private:
+    parse_error(int id_, std::size_t byte_, const char* what_arg)
+        : exception(id_, what_arg), byte(byte_) {}
+
+    static std::string position_string(const position_t& pos)
+    {
+        return concat(" at line ", std::to_string(pos.lines_read + 1),
+                      ", column ", std::to_string(pos.chars_read_current_line));
+    }
+};
+
+/// @brief exception indicating errors with iterators
+/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/
+class invalid_iterator : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    invalid_iterator(int id_, const char* what_arg)
+        : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating executing a member function with a wrong type
+/// @sa https://json.nlohmann.me/api/basic_json/type_error/
+class type_error : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating access out of the defined range
+/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/
+class out_of_range : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating other library errors
+/// @sa https://json.nlohmann.me/api/basic_json/other_error/
+class other_error : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/hash.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/hash.h
new file mode 100644
index 0000000..156fb24
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/hash.h
@@ -0,0 +1,129 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstdint> // uint8_t
+#include <cstddef> // size_t
+#include <functional> // hash
+
+#include <wpi/detail/abi_macros.h>
+#include <wpi/detail/value_t.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// boost::hash_combine
+inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
+{
+    seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);
+    return seed;
+}
+
+/*!
+@brief hash a JSON value
+
+The hash function tries to rely on std::hash where possible. Furthermore, the
+type of the JSON value is taken into account to have different hash values for
+null, 0, 0U, and false, etc.
+
+@tparam BasicJsonType basic_json specialization
+@param j JSON value to hash
+@return hash value of j
+*/
+template<typename BasicJsonType>
+std::size_t hash(const BasicJsonType& j)
+{
+    using string_t = typename BasicJsonType::string_t;
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+
+    const auto type = static_cast<std::size_t>(j.type());
+    switch (j.type())
+    {
+        case BasicJsonType::value_t::null:
+        case BasicJsonType::value_t::discarded:
+        {
+            return combine(type, 0);
+        }
+
+        case BasicJsonType::value_t::object:
+        {
+            auto seed = combine(type, j.size());
+            for (const auto& element : j.items())
+            {
+                const auto h = std::hash<string_t> {}(element.key());
+                seed = combine(seed, h);
+                seed = combine(seed, hash(element.value()));
+            }
+            return seed;
+        }
+
+        case BasicJsonType::value_t::array:
+        {
+            auto seed = combine(type, j.size());
+            for (const auto& element : j)
+            {
+                seed = combine(seed, hash(element));
+            }
+            return seed;
+        }
+
+        case BasicJsonType::value_t::string:
+        {
+            const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::boolean:
+        {
+            const auto h = std::hash<bool> {}(j.template get<bool>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::number_integer:
+        {
+            const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::number_unsigned:
+        {
+            const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::number_float:
+        {
+            const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::binary:
+        {
+            auto seed = combine(type, j.get_binary().size());
+            const auto h = std::hash<bool> {}(j.get_binary().has_subtype());
+            seed = combine(seed, h);
+            seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));
+            for (const auto byte : j.get_binary())
+            {
+                seed = combine(seed, std::hash<std::uint8_t> {}(byte));
+            }
+            return seed;
+        }
+
+        default:                   // LCOV_EXCL_LINE
+            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+            return 0;              // LCOV_EXCL_LINE
+    }
+}
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/binary_reader.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/binary_reader.h
new file mode 100644
index 0000000..0241fe0
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/binary_reader.h
@@ -0,0 +1,3010 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <algorithm> // generate_n
+#include <array> // array
+#include <cmath> // ldexp
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstdio> // snprintf
+#include <cstring> // memcpy
+#include <iterator> // back_inserter
+#include <limits> // numeric_limits
+#include <string> // char_traits, string
+#include <utility> // make_pair, move
+#include <vector> // vector
+
+#include <wpi/detail/exceptions.h>
+#include <wpi/detail/input/input_adapters.h>
+#include <wpi/detail/input/json_sax.h>
+#include <wpi/detail/input/lexer.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/meta/is_sax.h>
+#include <wpi/detail/meta/type_traits.h>
+#include <wpi/detail/string_concat.h>
+#include <wpi/detail/value_t.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// how to treat CBOR tags
+enum class cbor_tag_handler_t
+{
+    error,   ///< throw a parse_error exception in case of a tag
+    ignore,  ///< ignore tags
+    store    ///< store tags as binary type
+};
+
+/*!
+@brief determine system byte order
+
+@return true if and only if system's byte order is little endian
+
+@note from https://stackoverflow.com/a/1001328/266378
+*/
+static inline bool little_endianness(int num = 1) noexcept
+{
+    return *reinterpret_cast<char*>(&num) == 1;
+}
+
+
+///////////////////
+// binary reader //
+///////////////////
+
+/*!
+@brief deserialization of CBOR, MessagePack, and UBJSON values
+*/
+template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>>
+class binary_reader
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using json_sax_t = SAX;
+    using char_type = typename InputAdapterType::char_type;
+    using char_int_type = typename std::char_traits<char_type>::int_type;
+
+  public:
+    /*!
+    @brief create a binary reader
+
+    @param[in] adapter  input adapter to read from
+    */
+    explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format)
+    {
+        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
+    }
+
+    // make class move-only
+    binary_reader(const binary_reader&) = delete;
+    binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    binary_reader& operator=(const binary_reader&) = delete;
+    binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~binary_reader() = default;
+
+    /*!
+    @param[in] format  the binary format to parse
+    @param[in] sax_    a SAX event processor
+    @param[in] strict  whether to expect the input to be consumed completed
+    @param[in] tag_handler  how to treat CBOR tags
+
+    @return whether parsing was successful
+    */
+    JSON_HEDLEY_NON_NULL(3)
+    bool sax_parse(const input_format_t format,
+                   json_sax_t* sax_,
+                   const bool strict = true,
+                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        sax = sax_;
+        bool result = false;
+
+        switch (format)
+        {
+            case input_format_t::bson:
+                result = parse_bson_internal();
+                break;
+
+            case input_format_t::cbor:
+                result = parse_cbor_internal(true, tag_handler);
+                break;
+
+            case input_format_t::msgpack:
+                result = parse_msgpack_internal();
+                break;
+
+            case input_format_t::ubjson:
+            case input_format_t::bjdata:
+                result = parse_ubjson_internal();
+                break;
+
+            case input_format_t::json: // LCOV_EXCL_LINE
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+
+        // strict mode: next byte must be EOF
+        if (result && strict)
+        {
+            if (input_format == input_format_t::ubjson || input_format == input_format_t::bjdata)
+            {
+                get_ignore_noop();
+            }
+            else
+            {
+                get();
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))
+            {
+                return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read,
+                                        exception_message(input_format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr));
+            }
+        }
+
+        return result;
+    }
+
+  private:
+    //////////
+    // BSON //
+    //////////
+
+    /*!
+    @brief Reads in a BSON-object and passes it to the SAX-parser.
+    @return whether a valid BSON-value was passed to the SAX parser
+    */
+    bool parse_bson_internal()
+    {
+        std::int32_t document_size{};
+        get_number<std::int32_t, true>(input_format_t::bson, document_size);
+
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+        {
+            return false;
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))
+        {
+            return false;
+        }
+
+        return sax->end_object();
+    }
+
+    /*!
+    @brief Parses a C-style string from the BSON input.
+    @param[in,out] result  A reference to the string variable where the read
+                            string is to be stored.
+    @return `true` if the \x00-byte indicating the end of the string was
+             encountered before the EOF; false` indicates an unexpected EOF.
+    */
+    bool get_bson_cstr(string_t& result)
+    {
+        auto out = std::back_inserter(result);
+        while (true)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring")))
+            {
+                return false;
+            }
+            if (current == 0x00)
+            {
+                return true;
+            }
+            *out++ = static_cast<typename string_t::value_type>(current);
+        }
+    }
+
+    /*!
+    @brief Parses a zero-terminated string of length @a len from the BSON
+           input.
+    @param[in] len  The length (including the zero-byte at the end) of the
+                    string to be read.
+    @param[in,out] result  A reference to the string variable where the read
+                            string is to be stored.
+    @tparam NumberType The type of the length @a len
+    @pre len >= 1
+    @return `true` if the string was successfully parsed
+    */
+    template<typename NumberType>
+    bool get_bson_string(const NumberType len, string_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(len < 1))
+        {
+            auto last_token = get_token_string();
+            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                    exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
+        }
+
+        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();
+    }
+
+    /*!
+    @brief Parses a byte array input of length @a len from the BSON input.
+    @param[in] len  The length of the byte array to be read.
+    @param[in,out] result  A reference to the binary variable where the read
+                            array is to be stored.
+    @tparam NumberType The type of the length @a len
+    @pre len >= 0
+    @return `true` if the byte array was successfully parsed
+    */
+    template<typename NumberType>
+    bool get_bson_binary(const NumberType len, binary_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(len < 0))
+        {
+            auto last_token = get_token_string();
+            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                    exception_message(input_format_t::bson, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr));
+        }
+
+        // All BSON binary values have a subtype
+        std::uint8_t subtype{};
+        get_number<std::uint8_t>(input_format_t::bson, subtype);
+        result.set_subtype(subtype);
+
+        return get_binary(input_format_t::bson, len, result);
+    }
+
+    /*!
+    @brief Read a BSON document element of the given @a element_type.
+    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html
+    @param[in] element_type_parse_position The position in the input stream,
+               where the `element_type` was read.
+    @warning Not all BSON element types are supported yet. An unsupported
+             @a element_type will give rise to a parse_error.114:
+             Unsupported BSON record type 0x...
+    @return whether a valid BSON-object/array was passed to the SAX parser
+    */
+    bool parse_bson_element_internal(const char_int_type element_type,
+                                     const std::size_t element_type_parse_position)
+    {
+        switch (element_type)
+        {
+            case 0x01: // double
+            {
+                double number{};
+                return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0x02: // string
+            {
+                std::int32_t len{};
+                string_t value;
+                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);
+            }
+
+            case 0x03: // object
+            {
+                return parse_bson_internal();
+            }
+
+            case 0x04: // array
+            {
+                return parse_bson_array();
+            }
+
+            case 0x05: // binary
+            {
+                std::int32_t len{};
+                binary_t value;
+                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);
+            }
+
+            case 0x08: // boolean
+            {
+                return sax->boolean(get() != 0);
+            }
+
+            case 0x0A: // null
+            {
+                return sax->null();
+            }
+
+            case 0x10: // int32
+            {
+                std::int32_t value{};
+                return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);
+            }
+
+            case 0x12: // int64
+            {
+                std::int64_t value{};
+                return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
+            }
+
+            default: // anything else not supported (yet)
+            {
+                std::array<char, 3> cr{{}};
+                static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                std::string cr_str{cr.data()};
+                return sax->parse_error(element_type_parse_position, cr_str,
+                                        parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief Read a BSON element list (as specified in the BSON-spec)
+
+    The same binary layout is used for objects and arrays, hence it must be
+    indicated with the argument @a is_array which one is expected
+    (true --> array, false --> object).
+
+    @param[in] is_array Determines if the element list being read is to be
+                        treated as an object (@a is_array == false), or as an
+                        array (@a is_array == true).
+    @return whether a valid BSON-object/array was passed to the SAX parser
+    */
+    bool parse_bson_element_list(const bool is_array)
+    {
+        string_t key;
+
+        while (auto element_type = get())
+        {
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list")))
+            {
+                return false;
+            }
+
+            const std::size_t element_type_parse_position = chars_read;
+            if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))
+            {
+                return false;
+            }
+
+            if (!is_array && !sax->key(key))
+            {
+                return false;
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))
+            {
+                return false;
+            }
+
+            // get_bson_cstr only appends
+            key.clear();
+        }
+
+        return true;
+    }
+
+    /*!
+    @brief Reads an array from the BSON input and passes it to the SAX-parser.
+    @return whether a valid BSON-array was passed to the SAX parser
+    */
+    bool parse_bson_array()
+    {
+        std::int32_t document_size{};
+        get_number<std::int32_t, true>(input_format_t::bson, document_size);
+
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+        {
+            return false;
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))
+        {
+            return false;
+        }
+
+        return sax->end_array();
+    }
+
+    //////////
+    // CBOR //
+    //////////
+
+    /*!
+    @param[in] get_char  whether a new character should be retrieved from the
+                         input (true) or whether the last read character should
+                         be considered instead (false)
+    @param[in] tag_handler how CBOR tags should be treated
+
+    @return whether a valid CBOR value was passed to the SAX parser
+    */
+    bool parse_cbor_internal(const bool get_char,
+                             const cbor_tag_handler_t tag_handler)
+    {
+        switch (get_char ? get() : current)
+        {
+            // EOF
+            case std::char_traits<char_type>::eof():
+                return unexpect_eof(input_format_t::cbor, "value");
+
+            // 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 sax->number_unsigned(static_cast<number_unsigned_t>(current));
+
+            case 0x18: // Unsigned integer (one-byte uint8_t follows)
+            {
+                std::uint8_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            case 0x19: // Unsigned integer (two-byte uint16_t follows)
+            {
+                std::uint16_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            case 0x1A: // Unsigned integer (four-byte uint32_t follows)
+            {
+                std::uint32_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
+            {
+                std::uint64_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            // 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 sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));
+
+            case 0x38: // Negative integer (one-byte uint8_t follows)
+            {
+                std::uint8_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+            }
+
+            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
+            {
+                std::uint16_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+            }
+
+            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
+            {
+                std::uint32_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+            }
+
+            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
+            {
+                std::uint64_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)
+                        - static_cast<number_integer_t>(number));
+            }
+
+            // Binary data (0x00..0x17 bytes follow)
+            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: // Binary data (one-byte uint8_t for n follows)
+            case 0x59: // Binary data (two-byte uint16_t for n follow)
+            case 0x5A: // Binary data (four-byte uint32_t for n follow)
+            case 0x5B: // Binary data (eight-byte uint64_t for n follow)
+            case 0x5F: // Binary data (indefinite length)
+            {
+                binary_t b;
+                return get_cbor_binary(b) && sax->binary(b);
+            }
+
+            // 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)
+            {
+                string_t s;
+                return get_cbor_string(s) && sax->string(s);
+            }
+
+            // 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(
+                           conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+
+            case 0x98: // array (one-byte uint8_t for n follows)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0x99: // array (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0x9A: // array (four-byte uint32_t for n follow)
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0x9B: // array (eight-byte uint64_t for n follow)
+            {
+                std::uint64_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0x9F: // array (indefinite length)
+                return get_cbor_array(static_cast<std::size_t>(-1), tag_handler);
+
+            // 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(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+
+            case 0xB8: // map (one-byte uint8_t for n follows)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0xB9: // map (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0xBA: // map (four-byte uint32_t for n follow)
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0xBB: // map (eight-byte uint64_t for n follow)
+            {
+                std::uint64_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0xBF: // map (indefinite length)
+                return get_cbor_object(static_cast<std::size_t>(-1), tag_handler);
+
+            case 0xC6: // tagged item
+            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 0xD8: // tagged item (1 bytes follow)
+            case 0xD9: // tagged item (2 bytes follow)
+            case 0xDA: // tagged item (4 bytes follow)
+            case 0xDB: // tagged item (8 bytes follow)
+            {
+                switch (tag_handler)
+                {
+                    case cbor_tag_handler_t::error:
+                    {
+                        auto last_token = get_token_string();
+                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                                exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
+                    }
+
+                    case cbor_tag_handler_t::ignore:
+                    {
+                        // ignore binary subtype
+                        switch (current)
+                        {
+                            case 0xD8:
+                            {
+                                std::uint8_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            case 0xD9:
+                            {
+                                std::uint16_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            case 0xDA:
+                            {
+                                std::uint32_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            case 0xDB:
+                            {
+                                std::uint64_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            default:
+                                break;
+                        }
+                        return parse_cbor_internal(true, tag_handler);
+                    }
+
+                    case cbor_tag_handler_t::store:
+                    {
+                        binary_t b;
+                        // use binary subtype and store in binary container
+                        switch (current)
+                        {
+                            case 0xD8:
+                            {
+                                std::uint8_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            case 0xD9:
+                            {
+                                std::uint16_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            case 0xDA:
+                            {
+                                std::uint32_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            case 0xDB:
+                            {
+                                std::uint64_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            default:
+                                return parse_cbor_internal(true, tag_handler);
+                        }
+                        get();
+                        return get_cbor_binary(b) && sax->binary(b);
+                    }
+
+                    default:                 // LCOV_EXCL_LINE
+                        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+                        return false;        // LCOV_EXCL_LINE
+                }
+            }
+
+            case 0xF4: // false
+                return sax->boolean(false);
+
+            case 0xF5: // true
+                return sax->boolean(true);
+
+            case 0xF6: // null
+                return sax->null();
+
+            case 0xF9: // Half-Precision Float (two-byte IEEE 754)
+            {
+                const auto byte1_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
+                {
+                    return false;
+                }
+                const auto byte2_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
+                {
+                    return false;
+                }
+
+                const auto byte1 = static_cast<unsigned char>(byte1_raw);
+                const auto byte2 = static_cast<unsigned char>(byte2_raw);
+
+                // 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 auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);
+                const double val = [&half]
+                {
+                    const int exp = (half >> 10u) & 0x1Fu;
+                    const unsigned int mant = half & 0x3FFu;
+                    JSON_ASSERT(0 <= exp&& exp <= 32);
+                    JSON_ASSERT(mant <= 1024);
+                    switch (exp)
+                    {
+                        case 0:
+                            return std::ldexp(mant, -24);
+                        case 31:
+                            return (mant == 0)
+                            ? std::numeric_limits<double>::infinity()
+                            : std::numeric_limits<double>::quiet_NaN();
+                        default:
+                            return std::ldexp(mant + 1024, exp - 25);
+                    }
+                }();
+                return sax->number_float((half & 0x8000u) != 0
+                                         ? static_cast<number_float_t>(-val)
+                                         : static_cast<number_float_t>(val), "");
+            }
+
+            case 0xFA: // Single-Precision Float (four-byte IEEE 754)
+            {
+                float number{};
+                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
+            {
+                double number{};
+                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            default: // anything else (0xFF is handled inside the other types)
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @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.
+
+    @param[out] result  created string
+
+    @return whether string creation completed
+    */
+    bool get_cbor_string(string_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
+        {
+            return false;
+        }
+
+        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(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);
+            }
+
+            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+            {
+                std::uint64_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x7F: // UTF-8 string (indefinite length)
+            {
+                while (get() != 0xFF)
+                {
+                    string_t chunk;
+                    if (!get_cbor_string(chunk))
+                    {
+                        return false;
+                    }
+                    result.append(chunk);
+                }
+                return true;
+            }
+
+            default:
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                        exception_message(input_format_t::cbor, concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief reads a CBOR byte array
+
+    This function first reads starting bytes to determine the expected
+    byte array length and then copies this number of bytes into the byte array.
+    Additionally, CBOR's byte arrays with indefinite lengths are supported.
+
+    @param[out] result  created byte array
+
+    @return whether byte array creation completed
+    */
+    bool get_cbor_binary(binary_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
+        {
+            return false;
+        }
+
+        switch (current)
+        {
+            // Binary data (0x00..0x17 bytes follow)
+            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:
+            {
+                return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);
+            }
+
+            case 0x58: // Binary data (one-byte uint8_t for n follows)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x59: // Binary data (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x5A: // Binary data (four-byte uint32_t for n follow)
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x5B: // Binary data (eight-byte uint64_t for n follow)
+            {
+                std::uint64_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x5F: // Binary data (indefinite length)
+            {
+                while (get() != 0xFF)
+                {
+                    binary_t chunk;
+                    if (!get_cbor_binary(chunk))
+                    {
+                        return false;
+                    }
+                    result.insert(result.end(), chunk.begin(), chunk.end());
+                }
+                return true;
+            }
+
+            default:
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                        exception_message(input_format_t::cbor, concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @param[in] len  the length of the array or static_cast<std::size_t>(-1) for an
+                    array of indefinite size
+    @param[in] tag_handler how CBOR tags should be treated
+    @return whether array creation completed
+    */
+    bool get_cbor_array(const std::size_t len,
+                        const cbor_tag_handler_t tag_handler)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
+        {
+            return false;
+        }
+
+        if (len != static_cast<std::size_t>(-1))
+        {
+            for (std::size_t i = 0; i < len; ++i)
+            {
+                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+                {
+                    return false;
+                }
+            }
+        }
+        else
+        {
+            while (get() != 0xFF)
+            {
+                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))
+                {
+                    return false;
+                }
+            }
+        }
+
+        return sax->end_array();
+    }
+
+    /*!
+    @param[in] len  the length of the object or static_cast<std::size_t>(-1) for an
+                    object of indefinite size
+    @param[in] tag_handler how CBOR tags should be treated
+    @return whether object creation completed
+    */
+    bool get_cbor_object(const std::size_t len,
+                         const cbor_tag_handler_t tag_handler)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
+        {
+            return false;
+        }
+
+        if (len != 0)
+        {
+            string_t key;
+            if (len != static_cast<std::size_t>(-1))
+            {
+                for (std::size_t i = 0; i < len; ++i)
+                {
+                    get();
+                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+
+                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+            else
+            {
+                while (get() != 0xFF)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+
+                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+        }
+
+        return sax->end_object();
+    }
+
+    /////////////
+    // MsgPack //
+    /////////////
+
+    /*!
+    @return whether a valid MessagePack value was passed to the SAX parser
+    */
+    bool parse_msgpack_internal()
+    {
+        switch (get())
+        {
+            // EOF
+            case std::char_traits<char_type>::eof():
+                return unexpect_eof(input_format_t::msgpack, "value");
+
+            // 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 sax->number_unsigned(static_cast<number_unsigned_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(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+
+            // 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(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+
+            // 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:
+            case 0xD9: // str 8
+            case 0xDA: // str 16
+            case 0xDB: // str 32
+            {
+                string_t s;
+                return get_msgpack_string(s) && sax->string(s);
+            }
+
+            case 0xC0: // nil
+                return sax->null();
+
+            case 0xC2: // false
+                return sax->boolean(false);
+
+            case 0xC3: // true
+                return sax->boolean(true);
+
+            case 0xC4: // bin 8
+            case 0xC5: // bin 16
+            case 0xC6: // bin 32
+            case 0xC7: // ext 8
+            case 0xC8: // ext 16
+            case 0xC9: // ext 32
+            case 0xD4: // fixext 1
+            case 0xD5: // fixext 2
+            case 0xD6: // fixext 4
+            case 0xD7: // fixext 8
+            case 0xD8: // fixext 16
+            {
+                binary_t b;
+                return get_msgpack_binary(b) && sax->binary(b);
+            }
+
+            case 0xCA: // float 32
+            {
+                float number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0xCB: // float 64
+            {
+                double number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0xCC: // uint 8
+            {
+                std::uint8_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xCD: // uint 16
+            {
+                std::uint16_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xCE: // uint 32
+            {
+                std::uint32_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xCF: // uint 64
+            {
+                std::uint64_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xD0: // int 8
+            {
+                std::int8_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xD1: // int 16
+            {
+                std::int16_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xD2: // int 32
+            {
+                std::int32_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xD3: // int 64
+            {
+                std::int64_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xDC: // array 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));
+            }
+
+            case 0xDD: // array 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast<std::size_t>(len));
+            }
+
+            case 0xDE: // map 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));
+            }
+
+            case 0xDF: // map 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast<std::size_t>(len));
+            }
+
+            // negative 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 sax->number_integer(static_cast<std::int8_t>(current));
+
+            default: // anything else
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format_t::msgpack, concat("invalid byte: 0x", last_token), "value"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @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.
+
+    @param[out] result  created string
+
+    @return whether string creation completed
+    */
+    bool get_msgpack_string(string_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string")))
+        {
+            return false;
+        }
+
+        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(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);
+            }
+
+            case 0xD9: // str 8
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+            }
+
+            case 0xDA: // str 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+            }
+
+            case 0xDB: // str 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+            }
+
+            default:
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                        exception_message(input_format_t::msgpack, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief reads a MessagePack byte array
+
+    This function first reads starting bytes to determine the expected
+    byte array length and then copies this number of bytes into a byte array.
+
+    @param[out] result  created byte array
+
+    @return whether byte array creation completed
+    */
+    bool get_msgpack_binary(binary_t& result)
+    {
+        // helper function to set the subtype
+        auto assign_and_return_true = [&result](std::int8_t subtype)
+        {
+            result.set_subtype(static_cast<std::uint8_t>(subtype));
+            return true;
+        };
+
+        switch (current)
+        {
+            case 0xC4: // bin 8
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_binary(input_format_t::msgpack, len, result);
+            }
+
+            case 0xC5: // bin 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_binary(input_format_t::msgpack, len, result);
+            }
+
+            case 0xC6: // bin 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_binary(input_format_t::msgpack, len, result);
+            }
+
+            case 0xC7: // ext 8
+            {
+                std::uint8_t len{};
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, len, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xC8: // ext 16
+            {
+                std::uint16_t len{};
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, len, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xC9: // ext 32
+            {
+                std::uint32_t len{};
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, len, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD4: // fixext 1
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 1, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD5: // fixext 2
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 2, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD6: // fixext 4
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 4, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD7: // fixext 8
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 8, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD8: // fixext 16
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 16, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            default:           // LCOV_EXCL_LINE
+                return false;  // LCOV_EXCL_LINE
+        }
+    }
+
+    /*!
+    @param[in] len  the length of the array
+    @return whether array creation completed
+    */
+    bool get_msgpack_array(const std::size_t len)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
+        {
+            return false;
+        }
+
+        for (std::size_t i = 0; i < len; ++i)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
+            {
+                return false;
+            }
+        }
+
+        return sax->end_array();
+    }
+
+    /*!
+    @param[in] len  the length of the object
+    @return whether object creation completed
+    */
+    bool get_msgpack_object(const std::size_t len)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
+        {
+            return false;
+        }
+
+        string_t key;
+        for (std::size_t i = 0; i < len; ++i)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))
+            {
+                return false;
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
+            {
+                return false;
+            }
+            key.clear();
+        }
+
+        return sax->end_object();
+    }
+
+    ////////////
+    // UBJSON //
+    ////////////
+
+    /*!
+    @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 whether a valid UBJSON value was passed to the SAX parser
+    */
+    bool parse_ubjson_internal(const bool get_char = true)
+    {
+        return get_ubjson_value(get_char ? get_ignore_noop() : current);
+    }
+
+    /*!
+    @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[out] result   created string
+    @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 whether string creation completed
+    */
+    bool get_ubjson_string(string_t& result, const bool get_char = true)
+    {
+        if (get_char)
+        {
+            get();  // TODO(niels): may we ignore N here?
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value")))
+        {
+            return false;
+        }
+
+        switch (current)
+        {
+            case 'U':
+            {
+                std::uint8_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'i':
+            {
+                std::int8_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'I':
+            {
+                std::int16_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'l':
+            {
+                std::int32_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'L':
+            {
+                std::int64_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'u':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint16_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'm':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint32_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'M':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint64_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            default:
+                break;
+        }
+        auto last_token = get_token_string();
+        std::string message;
+
+        if (input_format != input_format_t::bjdata)
+        {
+            message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
+        }
+        else
+        {
+            message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
+        }
+        return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr));
+    }
+
+    /*!
+    @param[out] dim  an integer vector storing the ND array dimensions
+    @return whether reading ND array size vector is successful
+    */
+    bool get_ubjson_ndarray_size(std::vector<size_t>& dim)
+    {
+        std::pair<std::size_t, char_int_type> size_and_type;
+        size_t dimlen = 0;
+        bool no_ndarray = true;
+
+        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type, no_ndarray)))
+        {
+            return false;
+        }
+
+        if (size_and_type.first != npos)
+        {
+            if (size_and_type.second != 0)
+            {
+                if (size_and_type.second != 'N')
+                {
+                    for (std::size_t i = 0; i < size_and_type.first; ++i)
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))
+                        {
+                            return false;
+                        }
+                        dim.push_back(dimlen);
+                    }
+                }
+            }
+            else
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray)))
+                    {
+                        return false;
+                    }
+                    dim.push_back(dimlen);
+                }
+            }
+        }
+        else
+        {
+            while (current != ']')
+            {
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current)))
+                {
+                    return false;
+                }
+                dim.push_back(dimlen);
+                get_ignore_noop();
+            }
+        }
+        return true;
+    }
+
+    /*!
+    @param[out] result  determined size
+    @param[in,out] is_ndarray  for input, `true` means already inside an ndarray vector
+                               or ndarray dimension is not allowed; `false` means ndarray
+                               is allowed; for output, `true` means an ndarray is found;
+                               is_ndarray can only return `true` when its initial value
+                               is `false`
+    @param[in] prefix  type marker if already read, otherwise set to 0
+
+    @return whether size determination completed
+    */
+    bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
+    {
+        if (prefix == 0)
+        {
+            prefix = get_ignore_noop();
+        }
+
+        switch (prefix)
+        {
+            case 'U':
+            {
+                std::uint8_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'i':
+            {
+                std::int8_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
+                result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
+                return true;
+            }
+
+            case 'I':
+            {
+                std::int16_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'l':
+            {
+                std::int32_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'L':
+            {
+                std::int64_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
+                if (!value_in_range_of<std::size_t>(number))
+                {
+                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+                                            exception_message(input_format, "integer value overflow", "size"), nullptr));
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'u':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint16_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'm':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint32_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                result = conditional_static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'M':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint64_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                if (!value_in_range_of<std::size_t>(number))
+                {
+                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+                                            exception_message(input_format, "integer value overflow", "size"), nullptr));
+                }
+                result = detail::conditional_static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case '[':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimentional vector is not allowed", "size"), nullptr));
+                }
+                std::vector<size_t> dim;
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))
+                {
+                    return false;
+                }
+                if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector
+                {
+                    result = dim.at(dim.size() - 1);
+                    return true;
+                }
+                if (!dim.empty())  // if ndarray, convert to an object in JData annotated array format
+                {
+                    for (auto i : dim) // test if any dimension in an ndarray is 0, if so, return a 1D empty container
+                    {
+                        if ( i == 0 )
+                        {
+                            result = 0;
+                            return true;
+                        }
+                    }
+
+                    string_t key = "_ArraySize_";
+                    if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
+                    {
+                        return false;
+                    }
+                    result = 1;
+                    for (auto i : dim)
+                    {
+                        result *= i;
+                        if (result == 0 || result == npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be npos as it is used to initialize size in get_ubjson_size_type()
+                        {
+                            return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
+                        }
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))
+                        {
+                            return false;
+                        }
+                    }
+                    is_ndarray = true;
+                    return sax->end_array();
+                }
+                result = 0;
+                return true;
+            }
+
+            default:
+                break;
+        }
+        auto last_token = get_token_string();
+        std::string message;
+
+        if (input_format != input_format_t::bjdata)
+        {
+            message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
+        }
+        else
+        {
+            message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
+        }
+        return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr));
+    }
+
+    /*!
+    @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.
+
+    @param[out] result  pair of the size and the type
+    @param[in] inside_ndarray  whether the parser is parsing an ND array dimensional vector
+
+    @return whether pair creation completed
+    */
+    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result, bool inside_ndarray = false)
+    {
+        result.first = npos; // size
+        result.second = 0; // type
+        bool is_ndarray = false;
+
+        get_ignore_noop();
+
+        if (current == '$')
+        {
+            result.second = get();  // must not ignore 'N', because 'N' maybe the type
+            if (input_format == input_format_t::bjdata
+                    && JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second)))
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format, concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr));
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type")))
+            {
+                return false;
+            }
+
+            get_ignore_noop();
+            if (JSON_HEDLEY_UNLIKELY(current != '#'))
+            {
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value")))
+                {
+                    return false;
+                }
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
+            }
+
+            bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+            if (input_format == input_format_t::bjdata && is_ndarray)
+            {
+                if (inside_ndarray)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+                                            exception_message(input_format, "ndarray can not be recursive", "size"), nullptr));
+                }
+                result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
+            }
+            return is_error;
+        }
+
+        if (current == '#')
+        {
+            bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+            if (input_format == input_format_t::bjdata && is_ndarray)
+            {
+                return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+                                        exception_message(input_format, "ndarray requires both type and size", "size"), nullptr));
+            }
+            return is_error;
+        }
+
+        return true;
+    }
+
+    /*!
+    @param prefix  the previously read or set type prefix
+    @return whether value creation completed
+    */
+    bool get_ubjson_value(const char_int_type prefix)
+    {
+        switch (prefix)
+        {
+            case std::char_traits<char_type>::eof():  // EOF
+                return unexpect_eof(input_format, "value");
+
+            case 'T':  // true
+                return sax->boolean(true);
+            case 'F':  // false
+                return sax->boolean(false);
+
+            case 'Z':  // null
+                return sax->null();
+
+            case 'U':
+            {
+                std::uint8_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'i':
+            {
+                std::int8_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'I':
+            {
+                std::int16_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'l':
+            {
+                std::int32_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'L':
+            {
+                std::int64_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'u':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint16_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'm':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint32_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'M':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint64_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'h':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                const auto byte1_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+                {
+                    return false;
+                }
+                const auto byte2_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+                {
+                    return false;
+                }
+
+                const auto byte1 = static_cast<unsigned char>(byte1_raw);
+                const auto byte2 = static_cast<unsigned char>(byte2_raw);
+
+                // 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 auto half = static_cast<unsigned int>((byte2 << 8u) + byte1);
+                const double val = [&half]
+                {
+                    const int exp = (half >> 10u) & 0x1Fu;
+                    const unsigned int mant = half & 0x3FFu;
+                    JSON_ASSERT(0 <= exp&& exp <= 32);
+                    JSON_ASSERT(mant <= 1024);
+                    switch (exp)
+                    {
+                        case 0:
+                            return std::ldexp(mant, -24);
+                        case 31:
+                            return (mant == 0)
+                            ? std::numeric_limits<double>::infinity()
+                            : std::numeric_limits<double>::quiet_NaN();
+                        default:
+                            return std::ldexp(mant + 1024, exp - 25);
+                    }
+                }();
+                return sax->number_float((half & 0x8000u) != 0
+                                         ? static_cast<number_float_t>(-val)
+                                         : static_cast<number_float_t>(val), "");
+            }
+
+            case 'd':
+            {
+                float number{};
+                return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 'D':
+            {
+                double number{};
+                return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 'H':
+            {
+                return get_ubjson_high_precision_number();
+            }
+
+            case 'C':  // char
+            {
+                get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "char")))
+                {
+                    return false;
+                }
+                if (JSON_HEDLEY_UNLIKELY(current > 127))
+                {
+                    auto last_token = get_token_string();
+                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                            exception_message(input_format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
+                }
+                string_t s(1, static_cast<typename string_t::value_type>(current));
+                return sax->string(s);
+            }
+
+            case 'S':  // string
+            {
+                string_t s;
+                return get_ubjson_string(s) && sax->string(s);
+            }
+
+            case '[':  // array
+                return get_ubjson_array();
+
+            case '{':  // object
+                return get_ubjson_object();
+
+            default: // anything else
+                break;
+        }
+        auto last_token = get_token_string();
+        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format, "invalid byte: 0x" + last_token, "value"), nullptr));
+    }
+
+    /*!
+    @return whether array creation completed
+    */
+    bool get_ubjson_array()
+    {
+        std::pair<std::size_t, char_int_type> size_and_type;
+        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+        {
+            return false;
+        }
+
+        // if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
+        // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
+
+        if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
+        {
+            size_and_type.second &= ~(static_cast<char_int_type>(1) << 8);  // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
+            auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t)
+            {
+                return p.first < t;
+            });
+            string_t key = "_ArrayType_";
+            if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second))
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
+            }
+
+            string_t type = it->second; // sax->string() takes a reference
+            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
+            {
+                return false;
+            }
+
+            if (size_and_type.second == 'C')
+            {
+                size_and_type.second = 'U';
+            }
+
+            key = "_ArrayData_";
+            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
+            {
+                return false;
+            }
+
+            for (std::size_t i = 0; i < size_and_type.first; ++i)
+            {
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+                {
+                    return false;
+                }
+            }
+
+            return (sax->end_array() && sax->end_object());
+        }
+
+        if (size_and_type.first != npos)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))
+            {
+                return false;
+            }
+
+            if (size_and_type.second != 0)
+            {
+                if (size_and_type.second != 'N')
+                {
+                    for (std::size_t i = 0; i < size_and_type.first; ++i)
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+                        {
+                            return false;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+                    {
+                        return false;
+                    }
+                }
+            }
+        }
+        else
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+            {
+                return false;
+            }
+
+            while (current != ']')
+            {
+                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))
+                {
+                    return false;
+                }
+                get_ignore_noop();
+            }
+        }
+
+        return sax->end_array();
+    }
+
+    /*!
+    @return whether object creation completed
+    */
+    bool get_ubjson_object()
+    {
+        std::pair<std::size_t, char_int_type> size_and_type;
+        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+        {
+            return false;
+        }
+
+        // do not accept ND-array size in objects in BJData
+        if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
+        {
+            auto last_token = get_token_string();
+            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                    exception_message(input_format, "BJData object does not support ND-array size in optimized format", "object"), nullptr));
+        }
+
+        string_t key;
+        if (size_and_type.first != npos)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))
+            {
+                return false;
+            }
+
+            if (size_and_type.second != 0)
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+            else
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+        }
+        else
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+            {
+                return false;
+            }
+
+            while (current != '}')
+            {
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))
+                {
+                    return false;
+                }
+                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+                {
+                    return false;
+                }
+                get_ignore_noop();
+                key.clear();
+            }
+        }
+
+        return sax->end_object();
+    }
+
+    // Note, no reader for UBJSON binary types is implemented because they do
+    // not exist
+
+    bool get_ubjson_high_precision_number()
+    {
+        // get size of following number string
+        std::size_t size{};
+        bool no_ndarray = true;
+        auto res = get_ubjson_size_value(size, no_ndarray);
+        if (JSON_HEDLEY_UNLIKELY(!res))
+        {
+            return res;
+        }
+
+        // get number string
+        std::vector<char> number_vector;
+        for (std::size_t i = 0; i < size; ++i)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+            {
+                return false;
+            }
+            number_vector.push_back(static_cast<char>(current));
+        }
+
+        // parse number string
+        using ia_type = decltype(detail::input_adapter(number_vector));
+        auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false);
+        const auto result_number = number_lexer.scan();
+        const auto number_string = number_lexer.get_token_string();
+        const auto result_remainder = number_lexer.scan();
+
+        using token_type = typename detail::lexer_base<BasicJsonType>::token_type;
+
+        if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
+        {
+            return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
+                                    exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
+        }
+
+        switch (result_number)
+        {
+            case token_type::value_integer:
+                return sax->number_integer(number_lexer.get_number_integer());
+            case token_type::value_unsigned:
+                return sax->number_unsigned(number_lexer.get_number_unsigned());
+            case token_type::value_float:
+                return sax->number_float(number_lexer.get_number_float(), std::move(number_string));
+            case token_type::uninitialized:
+            case token_type::literal_true:
+            case token_type::literal_false:
+            case token_type::literal_null:
+            case token_type::value_string:
+            case token_type::begin_array:
+            case token_type::begin_object:
+            case token_type::end_array:
+            case token_type::end_object:
+            case token_type::name_separator:
+            case token_type::value_separator:
+            case token_type::parse_error:
+            case token_type::end_of_input:
+            case token_type::literal_or_value:
+            default:
+                return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
+                                        exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
+        }
+    }
+
+    ///////////////////////
+    // Utility functions //
+    ///////////////////////
+
+    /*!
+    @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_type>::eof()` in that case.
+
+    @return character read from the input
+    */
+    char_int_type get()
+    {
+        ++chars_read;
+        return current = ia.get_character();
+    }
+
+    /*!
+    @return character read from the input after ignoring all 'N' entries
+    */
+    char_int_type get_ignore_noop()
+    {
+        do
+        {
+            get();
+        }
+        while (current == 'N');
+
+        return current;
+    }
+
+    /*
+    @brief read a number from the input
+
+    @tparam NumberType the type of the number
+    @param[in] format   the current format (for diagnostics)
+    @param[out] result  number of type @a NumberType
+
+    @return whether conversion completed
+
+    @note This function needs to respect the system's endianness, because
+          bytes in CBOR, MessagePack, and UBJSON are stored in network order
+          (big endian) and therefore need reordering on little endian systems.
+          On the other hand, BSON and BJData use little endian and should reorder
+          on big endian systems.
+    */
+    template<typename NumberType, bool InputIsLittleEndian = false>
+    bool get_number(const input_format_t format, NumberType& result)
+    {
+        // step 1: read input into array with system's byte order
+        std::array<std::uint8_t, sizeof(NumberType)> vec{};
+        for (std::size_t i = 0; i < sizeof(NumberType); ++i)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
+            {
+                return false;
+            }
+
+            // reverse byte order prior to conversion if necessary
+            if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata))
+            {
+                vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);
+            }
+            else
+            {
+                vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE
+            }
+        }
+
+        // step 2: convert array into number of type T and return
+        std::memcpy(&result, vec.data(), sizeof(NumberType));
+        return true;
+    }
+
+    /*!
+    @brief create a string by reading characters from the input
+
+    @tparam NumberType the type of the number
+    @param[in] format the current format (for diagnostics)
+    @param[in] len number of characters to read
+    @param[out] result string created by reading @a len bytes
+
+    @return whether string creation completed
+
+    @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.
+    */
+    template<typename NumberType>
+    bool get_string(const input_format_t format,
+                    const NumberType len,
+                    string_t& result)
+    {
+        bool success = true;
+        for (NumberType i = 0; i < len; i++)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string")))
+            {
+                success = false;
+                break;
+            }
+            result.push_back(static_cast<typename string_t::value_type>(current));
+        }
+        return success;
+    }
+
+    /*!
+    @brief create a byte array by reading bytes from the input
+
+    @tparam NumberType the type of the number
+    @param[in] format the current format (for diagnostics)
+    @param[in] len number of bytes to read
+    @param[out] result byte array created by reading @a len bytes
+
+    @return whether byte array creation completed
+
+    @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 memory.
+    */
+    template<typename NumberType>
+    bool get_binary(const input_format_t format,
+                    const NumberType len,
+                    binary_t& result)
+    {
+        bool success = true;
+        for (NumberType i = 0; i < len; i++)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary")))
+            {
+                success = false;
+                break;
+            }
+            result.push_back(static_cast<std::uint8_t>(current));
+        }
+        return success;
+    }
+
+    /*!
+    @param[in] format   the current format (for diagnostics)
+    @param[in] context  further context information (for diagnostics)
+    @return whether the last read character is not EOF
+    */
+    JSON_HEDLEY_NON_NULL(3)
+    bool unexpect_eof(const input_format_t format, const char* context) const
+    {
+        if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))
+        {
+            return sax->parse_error(chars_read, "<end of file>",
+                                    parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr));
+        }
+        return true;
+    }
+
+    /*!
+    @return a string representation of the last read byte
+    */
+    std::string get_token_string() const
+    {
+        std::array<char, 3> cr{{}};
+        static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+        return std::string{cr.data()};
+    }
+
+    /*!
+    @param[in] format   the current format
+    @param[in] detail   a detailed error message
+    @param[in] context  further context information
+    @return a message string to use in the parse_error exceptions
+    */
+    std::string exception_message(const input_format_t format,
+                                  const std::string& detail,
+                                  const std::string& context) const
+    {
+        std::string error_msg = "syntax error while parsing ";
+
+        switch (format)
+        {
+            case input_format_t::cbor:
+                error_msg += "CBOR";
+                break;
+
+            case input_format_t::msgpack:
+                error_msg += "MessagePack";
+                break;
+
+            case input_format_t::ubjson:
+                error_msg += "UBJSON";
+                break;
+
+            case input_format_t::bson:
+                error_msg += "BSON";
+                break;
+
+            case input_format_t::bjdata:
+                error_msg += "BJData";
+                break;
+
+            case input_format_t::json: // LCOV_EXCL_LINE
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+
+        return concat(error_msg, ' ', context, ": ", detail);
+    }
+
+  private:
+    static JSON_INLINE_VARIABLE constexpr std::size_t npos = static_cast<std::size_t>(-1);
+
+    /// input adapter
+    InputAdapterType ia;
+
+    /// the current character
+    char_int_type current = std::char_traits<char_type>::eof();
+
+    /// the number of characters read
+    std::size_t chars_read = 0;
+
+    /// whether we can assume little endianness
+    const bool is_little_endian = little_endianness();
+
+    /// input format
+    const input_format_t input_format = input_format_t::json;
+
+    /// the SAX parser
+    json_sax_t* sax = nullptr;
+
+    // excluded markers in bjdata optimized type
+#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \
+    make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')
+
+#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \
+    make_array<bjd_type>(                      \
+    bjd_type{'C', "char"},                     \
+    bjd_type{'D', "double"},                   \
+    bjd_type{'I', "int16"},                    \
+    bjd_type{'L', "int64"},                    \
+    bjd_type{'M', "uint64"},                   \
+    bjd_type{'U', "uint8"},                    \
+    bjd_type{'d', "single"},                   \
+    bjd_type{'i', "int8"},                     \
+    bjd_type{'l', "int32"},                    \
+    bjd_type{'m', "uint32"},                   \
+    bjd_type{'u', "uint16"})
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    // lookup tables
+    // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
+    const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers =
+        JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_;
+
+    using bjd_type = std::pair<char_int_type, string_t>;
+    // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
+    const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map =
+        JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_;
+
+#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_
+#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_
+};
+
+#ifndef JSON_HAS_CPP_17
+    template<typename BasicJsonType, typename InputAdapterType, typename SAX>
+    constexpr std::size_t binary_reader<BasicJsonType, InputAdapterType, SAX>::npos;
+#endif
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/input_adapters.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/input_adapters.h
new file mode 100644
index 0000000..49dd812
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/input_adapters.h
@@ -0,0 +1,494 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <cstring> // strlen
+#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
+#include <memory> // shared_ptr, make_shared, addressof
+#include <numeric> // accumulate
+#include <string> // string, char_traits
+#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
+#include <utility> // pair, declval
+
+#ifndef JSON_NO_IO
+    #include <cstdio>   // FILE *
+    #include <istream>  // istream
+#endif                  // JSON_NO_IO
+
+#include <wpi/detail/iterators/iterator_traits.h>
+#include <wpi/detail/macro_scope.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// the supported input formats
+enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };
+
+////////////////////
+// input adapters //
+////////////////////
+
+#ifndef JSON_NO_IO
+/*!
+Input adapter for stdio file access. This adapter read only 1 byte and do not use any
+ buffer. This adapter is a very low level adapter.
+*/
+class file_input_adapter
+{
+  public:
+    using char_type = char;
+
+    JSON_HEDLEY_NON_NULL(2)
+    explicit file_input_adapter(std::FILE* f) noexcept
+        : m_file(f)
+    {
+        JSON_ASSERT(m_file != nullptr);
+    }
+
+    // make class move-only
+    file_input_adapter(const file_input_adapter&) = delete;
+    file_input_adapter(file_input_adapter&&) noexcept = default;
+    file_input_adapter& operator=(const file_input_adapter&) = delete;
+    file_input_adapter& operator=(file_input_adapter&&) = delete;
+    ~file_input_adapter() = default;
+
+    std::char_traits<char>::int_type get_character() noexcept
+    {
+        return std::fgetc(m_file);
+    }
+
+  private:
+    /// the file pointer to read from
+    std::FILE* m_file;
+};
+
+
+/*!
+Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
+beginning of input. Does not support changing the underlying std::streambuf
+in mid-input. Maintains underlying std::istream and std::streambuf to support
+subsequent use of standard std::istream operations to process any input
+characters following those used in parsing the JSON input.  Clears the
+std::istream flags; any input errors (e.g., EOF) will be detected by the first
+subsequent call for input from the std::istream.
+*/
+class input_stream_adapter
+{
+  public:
+    using char_type = char;
+
+    ~input_stream_adapter()
+    {
+        // clear stream flags; we use underlying streambuf I/O, do not
+        // maintain ifstream flags, except eof
+        if (is != nullptr)
+        {
+            is->clear(is->rdstate() & std::ios::eofbit);
+        }
+    }
+
+    explicit input_stream_adapter(std::istream& i)
+        : is(&i), sb(i.rdbuf())
+    {}
+
+    // delete because of pointer members
+    input_stream_adapter(const input_stream_adapter&) = delete;
+    input_stream_adapter& operator=(input_stream_adapter&) = delete;
+    input_stream_adapter& operator=(input_stream_adapter&&) = delete;
+
+    input_stream_adapter(input_stream_adapter&& rhs) noexcept
+        : is(rhs.is), sb(rhs.sb)
+    {
+        rhs.is = nullptr;
+        rhs.sb = nullptr;
+    }
+
+    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
+    // ensure that std::char_traits<char>::eof() and the character 0xFF do not
+    // end up as the same value, e.g. 0xFFFFFFFF.
+    std::char_traits<char>::int_type get_character()
+    {
+        auto res = sb->sbumpc();
+        // set eof manually, as we don't use the istream interface.
+        if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
+        {
+            is->clear(is->rdstate() | std::ios::eofbit);
+        }
+        return res;
+    }
+
+  private:
+    /// the associated input stream
+    std::istream* is = nullptr;
+    std::streambuf* sb = nullptr;
+};
+#endif  // JSON_NO_IO
+
+// General-purpose iterator-based adapter. It might not be as fast as
+// theoretically possible for some containers, but it is extremely versatile.
+template<typename IteratorType>
+class iterator_input_adapter
+{
+  public:
+    using char_type = typename std::iterator_traits<IteratorType>::value_type;
+
+    iterator_input_adapter(IteratorType first, IteratorType last)
+        : current(std::move(first)), end(std::move(last))
+    {}
+
+    typename std::char_traits<char_type>::int_type get_character()
+    {
+        if (JSON_HEDLEY_LIKELY(current != end))
+        {
+            auto result = std::char_traits<char_type>::to_int_type(*current);
+            std::advance(current, 1);
+            return result;
+        }
+
+        return std::char_traits<char_type>::eof();
+    }
+
+  private:
+    IteratorType current;
+    IteratorType end;
+
+    template<typename BaseInputAdapter, size_t T>
+    friend struct wide_string_input_helper;
+
+    bool empty() const
+    {
+        return current == end;
+    }
+};
+
+
+template<typename BaseInputAdapter, size_t T>
+struct wide_string_input_helper;
+
+template<typename BaseInputAdapter>
+struct wide_string_input_helper<BaseInputAdapter, 4>
+{
+    // UTF-32
+    static void fill_buffer(BaseInputAdapter& input,
+                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
+                            size_t& utf8_bytes_index,
+                            size_t& utf8_bytes_filled)
+    {
+        utf8_bytes_index = 0;
+
+        if (JSON_HEDLEY_UNLIKELY(input.empty()))
+        {
+            utf8_bytes[0] = std::char_traits<char>::eof();
+            utf8_bytes_filled = 1;
+        }
+        else
+        {
+            // get the current character
+            const auto wc = input.get_character();
+
+            // UTF-32 to UTF-8 encoding
+            if (wc < 0x80)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                utf8_bytes_filled = 1;
+            }
+            else if (wc <= 0x7FF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 2;
+            }
+            else if (wc <= 0xFFFF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 3;
+            }
+            else if (wc <= 0x10FFFF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));
+                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+                utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 4;
+            }
+            else
+            {
+                // unknown character
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                utf8_bytes_filled = 1;
+            }
+        }
+    }
+};
+
+template<typename BaseInputAdapter>
+struct wide_string_input_helper<BaseInputAdapter, 2>
+{
+    // UTF-16
+    static void fill_buffer(BaseInputAdapter& input,
+                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
+                            size_t& utf8_bytes_index,
+                            size_t& utf8_bytes_filled)
+    {
+        utf8_bytes_index = 0;
+
+        if (JSON_HEDLEY_UNLIKELY(input.empty()))
+        {
+            utf8_bytes[0] = std::char_traits<char>::eof();
+            utf8_bytes_filled = 1;
+        }
+        else
+        {
+            // get the current character
+            const auto wc = input.get_character();
+
+            // UTF-16 to UTF-8 encoding
+            if (wc < 0x80)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                utf8_bytes_filled = 1;
+            }
+            else if (wc <= 0x7FF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 2;
+            }
+            else if (0xD800 > wc || wc >= 0xE000)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 3;
+            }
+            else
+            {
+                if (JSON_HEDLEY_UNLIKELY(!input.empty()))
+                {
+                    const auto wc2 = static_cast<unsigned int>(input.get_character());
+                    const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
+                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
+                    utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
+                    utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
+                    utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
+                    utf8_bytes_filled = 4;
+                }
+                else
+                {
+                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                    utf8_bytes_filled = 1;
+                }
+            }
+        }
+    }
+};
+
+// Wraps another input apdater to convert wide character types into individual bytes.
+template<typename BaseInputAdapter, typename WideCharType>
+class wide_string_input_adapter
+{
+  public:
+    using char_type = char;
+
+    wide_string_input_adapter(BaseInputAdapter base)
+        : base_adapter(base) {}
+
+    typename std::char_traits<char>::int_type get_character() noexcept
+    {
+        // check if buffer needs to be filled
+        if (utf8_bytes_index == utf8_bytes_filled)
+        {
+            fill_buffer<sizeof(WideCharType)>();
+
+            JSON_ASSERT(utf8_bytes_filled > 0);
+            JSON_ASSERT(utf8_bytes_index == 0);
+        }
+
+        // use buffer
+        JSON_ASSERT(utf8_bytes_filled > 0);
+        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
+        return utf8_bytes[utf8_bytes_index++];
+    }
+
+  private:
+    BaseInputAdapter base_adapter;
+
+    template<size_t T>
+    void fill_buffer()
+    {
+        wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
+    }
+
+    /// a buffer for UTF-8 bytes
+    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
+
+    /// index to the utf8_codes array for the next valid byte
+    std::size_t utf8_bytes_index = 0;
+    /// number of valid bytes in the utf8_codes array
+    std::size_t utf8_bytes_filled = 0;
+};
+
+
+template<typename IteratorType, typename Enable = void>
+struct iterator_input_adapter_factory
+{
+    using iterator_type = IteratorType;
+    using char_type = typename std::iterator_traits<iterator_type>::value_type;
+    using adapter_type = iterator_input_adapter<iterator_type>;
+
+    static adapter_type create(IteratorType first, IteratorType last)
+    {
+        return adapter_type(std::move(first), std::move(last));
+    }
+};
+
+template<typename T>
+struct is_iterator_of_multibyte
+{
+    using value_type = typename std::iterator_traits<T>::value_type;
+    enum
+    {
+        value = sizeof(value_type) > 1
+    };
+};
+
+template<typename IteratorType>
+struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
+{
+    using iterator_type = IteratorType;
+    using char_type = typename std::iterator_traits<iterator_type>::value_type;
+    using base_adapter_type = iterator_input_adapter<iterator_type>;
+    using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
+
+    static adapter_type create(IteratorType first, IteratorType last)
+    {
+        return adapter_type(base_adapter_type(std::move(first), std::move(last)));
+    }
+};
+
+// General purpose iterator-based input
+template<typename IteratorType>
+typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
+{
+    using factory_type = iterator_input_adapter_factory<IteratorType>;
+    return factory_type::create(first, last);
+}
+
+// Convenience shorthand from container to iterator
+// Enables ADL on begin(container) and end(container)
+// Encloses the using declarations in namespace for not to leak them to outside scope
+
+namespace container_input_adapter_factory_impl
+{
+
+using std::begin;
+using std::end;
+
+template<typename ContainerType, typename Enable = void>
+struct container_input_adapter_factory {};
+
+template<typename ContainerType>
+struct container_input_adapter_factory< ContainerType,
+       void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>
+       {
+           using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
+
+           static adapter_type create(const ContainerType& container)
+{
+    return input_adapter(begin(container), end(container));
+}
+       };
+
+}  // namespace container_input_adapter_factory_impl
+
+template<typename ContainerType>
+typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
+{
+    return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
+}
+
+#ifndef JSON_NO_IO
+// Special cases with fast paths
+inline file_input_adapter input_adapter(std::FILE* file)
+{
+    return file_input_adapter(file);
+}
+
+inline input_stream_adapter input_adapter(std::istream& stream)
+{
+    return input_stream_adapter(stream);
+}
+
+inline input_stream_adapter input_adapter(std::istream&& stream)
+{
+    return input_stream_adapter(stream);
+}
+#endif  // JSON_NO_IO
+
+using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
+
+// Null-delimited strings, and the like.
+template < typename CharT,
+           typename std::enable_if <
+               std::is_pointer<CharT>::value&&
+               !std::is_array<CharT>::value&&
+               std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
+               sizeof(typename std::remove_pointer<CharT>::type) == 1,
+               int >::type = 0 >
+contiguous_bytes_input_adapter input_adapter(CharT b)
+{
+    auto length = std::strlen(reinterpret_cast<const char*>(b));
+    const auto* ptr = reinterpret_cast<const char*>(b);
+    return input_adapter(ptr, ptr + length);
+}
+
+template<typename T, std::size_t N>
+auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+{
+    return input_adapter(array, array + N);
+}
+
+// This class only handles inputs of input_buffer_adapter type.
+// It's required so that expressions like {ptr, len} can be implicitly cast
+// to the correct adapter.
+class span_input_adapter
+{
+  public:
+    template < typename CharT,
+               typename std::enable_if <
+                   std::is_pointer<CharT>::value&&
+                   std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
+                   sizeof(typename std::remove_pointer<CharT>::type) == 1,
+                   int >::type = 0 >
+    span_input_adapter(CharT b, std::size_t l)
+        : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}
+
+    template<class IteratorType,
+             typename std::enable_if<
+                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
+                 int>::type = 0>
+    span_input_adapter(IteratorType first, IteratorType last)
+        : ia(input_adapter(first, last)) {}
+
+    contiguous_bytes_input_adapter&& get()
+    {
+        return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
+    }
+
+  private:
+    contiguous_bytes_input_adapter ia;
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/json_sax.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/json_sax.h
new file mode 100644
index 0000000..6e763e7
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/json_sax.h
@@ -0,0 +1,728 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef>
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+#include <wpi/detail/exceptions.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/string_concat.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+
+/*!
+@brief SAX interface
+
+This class describes the SAX interface used by @ref wpi::json::sax_parse.
+Each function is called in different situations while the input is parsed. The
+boolean return value informs the parser whether to continue processing the
+input.
+*/
+template<typename BasicJsonType>
+struct json_sax
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+
+    /*!
+    @brief a null value was read
+    @return whether parsing should proceed
+    */
+    virtual bool null() = 0;
+
+    /*!
+    @brief a boolean value was read
+    @param[in] val  boolean value
+    @return whether parsing should proceed
+    */
+    virtual bool boolean(bool val) = 0;
+
+    /*!
+    @brief an integer number was read
+    @param[in] val  integer value
+    @return whether parsing should proceed
+    */
+    virtual bool number_integer(number_integer_t val) = 0;
+
+    /*!
+    @brief an unsigned integer number was read
+    @param[in] val  unsigned integer value
+    @return whether parsing should proceed
+    */
+    virtual bool number_unsigned(number_unsigned_t val) = 0;
+
+    /*!
+    @brief a floating-point number was read
+    @param[in] val  floating-point value
+    @param[in] s    raw token value
+    @return whether parsing should proceed
+    */
+    virtual bool number_float(number_float_t val, const string_t& s) = 0;
+
+    /*!
+    @brief a string value was read
+    @param[in] val  string value
+    @return whether parsing should proceed
+    @note It is safe to move the passed string value.
+    */
+    virtual bool string(string_t& val) = 0;
+
+    /*!
+    @brief a binary value was read
+    @param[in] val  binary value
+    @return whether parsing should proceed
+    @note It is safe to move the passed binary value.
+    */
+    virtual bool binary(binary_t& val) = 0;
+
+    /*!
+    @brief the beginning of an object was read
+    @param[in] elements  number of object elements or -1 if unknown
+    @return whether parsing should proceed
+    @note binary formats may report the number of elements
+    */
+    virtual bool start_object(std::size_t elements) = 0;
+
+    /*!
+    @brief an object key was read
+    @param[in] val  object key
+    @return whether parsing should proceed
+    @note It is safe to move the passed string.
+    */
+    virtual bool key(string_t& val) = 0;
+
+    /*!
+    @brief the end of an object was read
+    @return whether parsing should proceed
+    */
+    virtual bool end_object() = 0;
+
+    /*!
+    @brief the beginning of an array was read
+    @param[in] elements  number of array elements or -1 if unknown
+    @return whether parsing should proceed
+    @note binary formats may report the number of elements
+    */
+    virtual bool start_array(std::size_t elements) = 0;
+
+    /*!
+    @brief the end of an array was read
+    @return whether parsing should proceed
+    */
+    virtual bool end_array() = 0;
+
+    /*!
+    @brief a parse error occurred
+    @param[in] position    the position in the input where the error occurs
+    @param[in] last_token  the last read token
+    @param[in] ex          an exception object describing the error
+    @return whether parsing should proceed (must return false)
+    */
+    virtual bool parse_error(std::size_t position,
+                             const std::string& last_token,
+                             const detail::exception& ex) = 0;
+
+    json_sax() = default;
+    json_sax(const json_sax&) = default;
+    json_sax(json_sax&&) noexcept = default;
+    json_sax& operator=(const json_sax&) = default;
+    json_sax& operator=(json_sax&&) noexcept = default;
+    virtual ~json_sax() = default;
+};
+
+
+namespace detail
+{
+/*!
+@brief SAX implementation to create a JSON value from SAX events
+
+This class implements the @ref json_sax interface and processes the SAX events
+to create a JSON value which makes it basically a DOM parser. The structure or
+hierarchy of the JSON value is managed by the stack `ref_stack` which contains
+a pointer to the respective array or object for each recursion depth.
+
+After successful parsing, the value that is passed by reference to the
+constructor contains the parsed value.
+
+@tparam BasicJsonType  the JSON type
+*/
+template<typename BasicJsonType>
+class json_sax_dom_parser
+{
+  public:
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+
+    /*!
+    @param[in,out] r  reference to a JSON value that is manipulated while
+                       parsing
+    @param[in] allow_exceptions_  whether parse errors yield exceptions
+    */
+    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
+        : root(r), allow_exceptions(allow_exceptions_)
+    {}
+
+    // make class move-only
+    json_sax_dom_parser(const json_sax_dom_parser&) = delete;
+    json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;
+    json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~json_sax_dom_parser() = default;
+
+    bool null()
+    {
+        handle_value(nullptr);
+        return true;
+    }
+
+    bool boolean(bool val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_integer(number_integer_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_unsigned(number_unsigned_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_float(number_float_t val, const string_t& /*unused*/)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool string(string_t& val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool binary(binary_t& val)
+    {
+        handle_value(std::move(val));
+        return true;
+    }
+
+    bool start_object(std::size_t len)
+    {
+        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
+
+        if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    bool key(string_t& val)
+    {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_object());
+
+        // add null at given key and store the reference for later
+        object_element = &(ref_stack.back()->m_value.object->operator[](val));
+        return true;
+    }
+
+    bool end_object()
+    {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_object());
+
+        ref_stack.back()->set_parents();
+        ref_stack.pop_back();
+        return true;
+    }
+
+    bool start_array(std::size_t len)
+    {
+        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
+
+        if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    bool end_array()
+    {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_array());
+
+        ref_stack.back()->set_parents();
+        ref_stack.pop_back();
+        return true;
+    }
+
+    template<class Exception>
+    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
+                     const Exception& ex)
+    {
+        errored = true;
+        static_cast<void>(ex);
+        if (allow_exceptions)
+        {
+            JSON_THROW(ex);
+        }
+        return false;
+    }
+
+    constexpr bool is_errored() const
+    {
+        return errored;
+    }
+
+  private:
+    /*!
+    @invariant If the ref stack is empty, then the passed value will be the new
+               root.
+    @invariant If the ref stack contains a value, then it is an array or an
+               object to which we can add elements
+    */
+    template<typename Value>
+    JSON_HEDLEY_RETURNS_NON_NULL
+    BasicJsonType* handle_value(Value&& v)
+    {
+        if (ref_stack.empty())
+        {
+            root = BasicJsonType(std::forward<Value>(v));
+            return &root;
+        }
+
+        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
+
+        if (ref_stack.back()->is_array())
+        {
+            ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));
+            return &(ref_stack.back()->m_value.array->back());
+        }
+
+        JSON_ASSERT(ref_stack.back()->is_object());
+        JSON_ASSERT(object_element);
+        *object_element = BasicJsonType(std::forward<Value>(v));
+        return object_element;
+    }
+
+    /// the parsed JSON value
+    BasicJsonType& root;
+    /// stack to model hierarchy of values
+    std::vector<BasicJsonType*> ref_stack {};
+    /// helper to hold the reference for the next object element
+    BasicJsonType* object_element = nullptr;
+    /// whether a syntax error occurred
+    bool errored = false;
+    /// whether to throw exceptions in case of errors
+    const bool allow_exceptions = true;
+};
+
+template<typename BasicJsonType>
+class json_sax_dom_callback_parser
+{
+  public:
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using parser_callback_t = typename BasicJsonType::parser_callback_t;
+    using parse_event_t = typename BasicJsonType::parse_event_t;
+
+    json_sax_dom_callback_parser(BasicJsonType& r,
+                                 const parser_callback_t cb,
+                                 const bool allow_exceptions_ = true)
+        : root(r), callback(cb), allow_exceptions(allow_exceptions_)
+    {
+        keep_stack.push_back(true);
+    }
+
+    // make class move-only
+    json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;
+    json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;
+    json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~json_sax_dom_callback_parser() = default;
+
+    bool null()
+    {
+        handle_value(nullptr);
+        return true;
+    }
+
+    bool boolean(bool val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_integer(number_integer_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_unsigned(number_unsigned_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_float(number_float_t val, const string_t& /*unused*/)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool string(string_t& val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool binary(binary_t& val)
+    {
+        handle_value(std::move(val));
+        return true;
+    }
+
+    bool start_object(std::size_t len)
+    {
+        // check callback for object start
+        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
+        keep_stack.push_back(keep);
+
+        auto val = handle_value(BasicJsonType::value_t::object, true);
+        ref_stack.push_back(val.second);
+
+        // check object limit
+        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    bool key(string_t& val)
+    {
+        BasicJsonType k = BasicJsonType(val);
+
+        // check callback for key
+        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
+        key_keep_stack.push_back(keep);
+
+        // add discarded value at given key and store the reference for later
+        if (keep && ref_stack.back())
+        {
+            object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
+        }
+
+        return true;
+    }
+
+    bool end_object()
+    {
+        if (ref_stack.back())
+        {
+            if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
+            {
+                // discard object
+                *ref_stack.back() = discarded;
+            }
+            else
+            {
+                ref_stack.back()->set_parents();
+            }
+        }
+
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(!keep_stack.empty());
+        ref_stack.pop_back();
+        keep_stack.pop_back();
+
+        if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
+        {
+            // remove discarded value
+            for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
+            {
+                if (it->is_discarded())
+                {
+                    ref_stack.back()->erase(it);
+                    break;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    bool start_array(std::size_t len)
+    {
+        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
+        keep_stack.push_back(keep);
+
+        auto val = handle_value(BasicJsonType::value_t::array, true);
+        ref_stack.push_back(val.second);
+
+        // check array limit
+        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    bool end_array()
+    {
+        bool keep = true;
+
+        if (ref_stack.back())
+        {
+            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
+            if (keep)
+            {
+                ref_stack.back()->set_parents();
+            }
+            else
+            {
+                // discard array
+                *ref_stack.back() = discarded;
+            }
+        }
+
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(!keep_stack.empty());
+        ref_stack.pop_back();
+        keep_stack.pop_back();
+
+        // remove discarded value
+        if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
+        {
+            ref_stack.back()->m_value.array->pop_back();
+        }
+
+        return true;
+    }
+
+    template<class Exception>
+    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
+                     const Exception& ex)
+    {
+        errored = true;
+        static_cast<void>(ex);
+        if (allow_exceptions)
+        {
+            JSON_THROW(ex);
+        }
+        return false;
+    }
+
+    constexpr bool is_errored() const
+    {
+        return errored;
+    }
+
+  private:
+    /*!
+    @param[in] v  value to add to the JSON value we build during parsing
+    @param[in] skip_callback  whether we should skip calling the callback
+               function; this is required after start_array() and
+               start_object() SAX events, because otherwise we would call the
+               callback function with an empty array or object, respectively.
+
+    @invariant If the ref stack is empty, then the passed value will be the new
+               root.
+    @invariant If the ref stack contains a value, then it is an array or an
+               object to which we can add elements
+
+    @return pair of boolean (whether value should be kept) and pointer (to the
+            passed value in the ref_stack hierarchy; nullptr if not kept)
+    */
+    template<typename Value>
+    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
+    {
+        JSON_ASSERT(!keep_stack.empty());
+
+        // do not handle this value if we know it would be added to a discarded
+        // container
+        if (!keep_stack.back())
+        {
+            return {false, nullptr};
+        }
+
+        // create value
+        auto value = BasicJsonType(std::forward<Value>(v));
+
+        // check callback
+        const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
+
+        // do not handle this value if we just learnt it shall be discarded
+        if (!keep)
+        {
+            return {false, nullptr};
+        }
+
+        if (ref_stack.empty())
+        {
+            root = std::move(value);
+            return {true, &root};
+        }
+
+        // skip this value if we already decided to skip the parent
+        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
+        if (!ref_stack.back())
+        {
+            return {false, nullptr};
+        }
+
+        // we now only expect arrays and objects
+        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
+
+        // array
+        if (ref_stack.back()->is_array())
+        {
+            ref_stack.back()->m_value.array->emplace_back(std::move(value));
+            return {true, &(ref_stack.back()->m_value.array->back())};
+        }
+
+        // object
+        JSON_ASSERT(ref_stack.back()->is_object());
+        // check if we should store an element for the current key
+        JSON_ASSERT(!key_keep_stack.empty());
+        const bool store_element = key_keep_stack.back();
+        key_keep_stack.pop_back();
+
+        if (!store_element)
+        {
+            return {false, nullptr};
+        }
+
+        JSON_ASSERT(object_element);
+        *object_element = std::move(value);
+        return {true, object_element};
+    }
+
+    /// the parsed JSON value
+    BasicJsonType& root;
+    /// stack to model hierarchy of values
+    std::vector<BasicJsonType*> ref_stack {};
+    /// stack to manage which values to keep
+    std::vector<bool> keep_stack {};
+    /// stack to manage which object keys to keep
+    std::vector<bool> key_keep_stack {};
+    /// helper to hold the reference for the next object element
+    BasicJsonType* object_element = nullptr;
+    /// whether a syntax error occurred
+    bool errored = false;
+    /// callback function
+    const parser_callback_t callback = nullptr;
+    /// whether to throw exceptions in case of errors
+    const bool allow_exceptions = true;
+    /// a discarded value for the callback
+    BasicJsonType discarded = BasicJsonType::value_t::discarded;
+};
+
+template<typename BasicJsonType>
+class json_sax_acceptor
+{
+  public:
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+
+    bool null()
+    {
+        return true;
+    }
+
+    bool boolean(bool /*unused*/)
+    {
+        return true;
+    }
+
+    bool number_integer(number_integer_t /*unused*/)
+    {
+        return true;
+    }
+
+    bool number_unsigned(number_unsigned_t /*unused*/)
+    {
+        return true;
+    }
+
+    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool string(string_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool binary(binary_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool start_object(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
+    {
+        return true;
+    }
+
+    bool key(string_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool end_object()
+    {
+        return true;
+    }
+
+    bool start_array(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
+    {
+        return true;
+    }
+
+    bool end_array()
+    {
+        return true;
+    }
+
+    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
+    {
+        return false;
+    }
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/lexer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/lexer.h
new file mode 100644
index 0000000..93fd84a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/lexer.h
@@ -0,0 +1,1632 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <array> // array
+#include <clocale> // localeconv
+#include <cstddef> // size_t
+#include <cstdio> // snprintf
+#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
+#include <initializer_list> // initializer_list
+#include <string> // char_traits, string
+#include <utility> // move
+#include <vector> // vector
+
+#include <wpi/detail/input/input_adapters.h>
+#include <wpi/detail/input/position_t.h>
+#include <wpi/detail/macro_scope.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////
+// lexer //
+///////////
+
+template<typename BasicJsonType>
+class lexer_base
+{
+  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)
+    JSON_HEDLEY_RETURNS_NON_NULL
+    JSON_HEDLEY_CONST
+    static const char* 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 token_type::value_unsigned:
+            case token_type::value_integer:
+            case 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";
+            // LCOV_EXCL_START
+            default: // catch non-enum values
+                return "unknown token";
+                // LCOV_EXCL_STOP
+        }
+    }
+};
+/*!
+@brief lexical analysis
+
+This class organizes the lexical analysis during JSON deserialization.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class lexer : public lexer_base<BasicJsonType>
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using char_type = typename InputAdapterType::char_type;
+    using char_int_type = typename std::char_traits<char_type>::int_type;
+
+  public:
+    using token_type = typename lexer_base<BasicJsonType>::token_type;
+
+    explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept
+        : ia(std::move(adapter))
+        , ignore_comments(ignore_comments_)
+        , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
+    {}
+
+    // delete because of pointer members
+    lexer(const lexer&) = delete;
+    lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    lexer& operator=(lexer&) = delete;
+    lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~lexer() = default;
+
+  private:
+    /////////////////////
+    // locales
+    /////////////////////
+
+    /// return the locale-dependent decimal point
+    JSON_HEDLEY_PURE
+    static char get_decimal_point() noexcept
+    {
+        const auto* loc = localeconv();
+        JSON_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()
+    {
+        // this function only makes sense after reading `\u`
+        JSON_ASSERT(current == 'u');
+        int codepoint = 0;
+
+        const auto factors = { 12u, 8u, 4u, 0u };
+        for (const auto factor : factors)
+        {
+            get();
+
+            if (current >= '0' && current <= '9')
+            {
+                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);
+            }
+            else if (current >= 'A' && current <= 'F')
+            {
+                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);
+            }
+            else if (current >= 'a' && current <= 'f')
+            {
+                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);
+            }
+            else
+            {
+                return -1;
+            }
+        }
+
+        JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);
+        return 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<char_int_type> ranges)
+    {
+        JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);
+        add(current);
+
+        for (auto range = ranges.begin(); range != ranges.end(); ++range)
+        {
+            get();
+            if (JSON_HEDLEY_LIKELY(*range <= current && 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 8259. 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()
+    {
+        // reset token_buffer (ignore opening quote)
+        reset();
+
+        // we entered the function by reading an open quote
+        JSON_ASSERT(current == '\"');
+
+        while (true)
+        {
+            // get next character
+            switch (get())
+            {
+                // end of file while parsing string
+                case std::char_traits<char_type>::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_HEDLEY_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 && codepoint1 <= 0xDBFF)
+                            {
+                                // expect next \uxxxx entry
+                                if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u'))
+                                {
+                                    const int codepoint2 = get_codepoint();
+
+                                    if (JSON_HEDLEY_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_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))
+                                    {
+                                        // overwrite codepoint
+                                        codepoint = static_cast<int>(
+                                                        // high surrogate occupies the most significant 22 bits
+                                                        (static_cast<unsigned int>(codepoint1) << 10u)
+                                                        // low surrogate occupies the least significant 15 bits
+                                                        + static_cast<unsigned int>(codepoint2)
+                                                        // there is still the 0xD800, 0xDC00 and 0x10000 noise
+                                                        // in the result, so we have to subtract with:
+                                                        // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
+                                                        - 0x35FDC00u);
+                                    }
+                                    else
+                                    {
+                                        error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+                                        return token_type::parse_error;
+                                    }
+                                }
+                                else
+                                {
+                                    error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+                                    return token_type::parse_error;
+                                }
+                            }
+                            else
+                            {
+                                if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && 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
+                            JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
+
+                            // translate codepoint into bytes
+                            if (codepoint < 0x80)
+                            {
+                                // 1-byte characters: 0xxxxxxx (ASCII)
+                                add(static_cast<char_int_type>(codepoint));
+                            }
+                            else if (codepoint <= 0x7FF)
+                            {
+                                // 2-byte characters: 110xxxxx 10xxxxxx
+                                add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));
+                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+                            }
+                            else if (codepoint <= 0xFFFF)
+                            {
+                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
+                                add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));
+                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+                            }
+                            else
+                            {
+                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+                                add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));
+                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));
+                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+                            }
+
+                            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:
+                {
+                    error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000";
+                    return token_type::parse_error;
+                }
+
+                case 0x01:
+                {
+                    error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001";
+                    return token_type::parse_error;
+                }
+
+                case 0x02:
+                {
+                    error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002";
+                    return token_type::parse_error;
+                }
+
+                case 0x03:
+                {
+                    error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003";
+                    return token_type::parse_error;
+                }
+
+                case 0x04:
+                {
+                    error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004";
+                    return token_type::parse_error;
+                }
+
+                case 0x05:
+                {
+                    error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005";
+                    return token_type::parse_error;
+                }
+
+                case 0x06:
+                {
+                    error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006";
+                    return token_type::parse_error;
+                }
+
+                case 0x07:
+                {
+                    error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007";
+                    return token_type::parse_error;
+                }
+
+                case 0x08:
+                {
+                    error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b";
+                    return token_type::parse_error;
+                }
+
+                case 0x09:
+                {
+                    error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t";
+                    return token_type::parse_error;
+                }
+
+                case 0x0A:
+                {
+                    error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n";
+                    return token_type::parse_error;
+                }
+
+                case 0x0B:
+                {
+                    error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B";
+                    return token_type::parse_error;
+                }
+
+                case 0x0C:
+                {
+                    error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f";
+                    return token_type::parse_error;
+                }
+
+                case 0x0D:
+                {
+                    error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r";
+                    return token_type::parse_error;
+                }
+
+                case 0x0E:
+                {
+                    error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E";
+                    return token_type::parse_error;
+                }
+
+                case 0x0F:
+                {
+                    error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F";
+                    return token_type::parse_error;
+                }
+
+                case 0x10:
+                {
+                    error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010";
+                    return token_type::parse_error;
+                }
+
+                case 0x11:
+                {
+                    error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011";
+                    return token_type::parse_error;
+                }
+
+                case 0x12:
+                {
+                    error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012";
+                    return token_type::parse_error;
+                }
+
+                case 0x13:
+                {
+                    error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013";
+                    return token_type::parse_error;
+                }
+
+                case 0x14:
+                {
+                    error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014";
+                    return token_type::parse_error;
+                }
+
+                case 0x15:
+                {
+                    error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015";
+                    return token_type::parse_error;
+                }
+
+                case 0x16:
+                {
+                    error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016";
+                    return token_type::parse_error;
+                }
+
+                case 0x17:
+                {
+                    error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017";
+                    return token_type::parse_error;
+                }
+
+                case 0x18:
+                {
+                    error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018";
+                    return token_type::parse_error;
+                }
+
+                case 0x19:
+                {
+                    error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019";
+                    return token_type::parse_error;
+                }
+
+                case 0x1A:
+                {
+                    error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A";
+                    return token_type::parse_error;
+                }
+
+                case 0x1B:
+                {
+                    error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B";
+                    return token_type::parse_error;
+                }
+
+                case 0x1C:
+                {
+                    error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C";
+                    return token_type::parse_error;
+                }
+
+                case 0x1D:
+                {
+                    error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D";
+                    return token_type::parse_error;
+                }
+
+                case 0x1E:
+                {
+                    error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E";
+                    return token_type::parse_error;
+                }
+
+                case 0x1F:
+                {
+                    error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F";
+                    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_HEDLEY_UNLIKELY(!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_HEDLEY_UNLIKELY(!(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_HEDLEY_UNLIKELY(!(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_HEDLEY_UNLIKELY(!(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_HEDLEY_UNLIKELY(!(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_HEDLEY_UNLIKELY(!(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_HEDLEY_UNLIKELY(!(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;
+                }
+            }
+        }
+    }
+
+    /*!
+     * @brief scan a comment
+     * @return whether comment could be scanned successfully
+     */
+    bool scan_comment()
+    {
+        switch (get())
+        {
+            // single-line comments skip input until a newline or EOF is read
+            case '/':
+            {
+                while (true)
+                {
+                    switch (get())
+                    {
+                        case '\n':
+                        case '\r':
+                        case std::char_traits<char_type>::eof():
+                        case '\0':
+                            return true;
+
+                        default:
+                            break;
+                    }
+                }
+            }
+
+            // multi-line comments skip input until */ is read
+            case '*':
+            {
+                while (true)
+                {
+                    switch (get())
+                    {
+                        case std::char_traits<char_type>::eof():
+                        case '\0':
+                        {
+                            error_message = "invalid comment; missing closing '*/'";
+                            return false;
+                        }
+
+                        case '*':
+                        {
+                            switch (get())
+                            {
+                                case '/':
+                                    return true;
+
+                                default:
+                                {
+                                    unget();
+                                    continue;
+                                }
+                            }
+                        }
+
+                        default:
+                            continue;
+                    }
+                }
+            }
+
+            // unexpected character after reading '/'
+            default:
+            {
+                error_message = "invalid comment; expecting '/' or '*' after '/'";
+                return false;
+            }
+        }
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    static void strtof(float& f, const char* str, char** endptr) noexcept
+    {
+        f = std::strtof(str, endptr);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    static void strtof(double& f, const char* str, char** endptr) noexcept
+    {
+        f = std::strtod(str, endptr);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    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 8259.
+
+    The function is realized with a deterministic finite state machine derived
+    from the grammar described in RFC 8259. 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 | decimal2 | [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()  // lgtm [cpp/use-of-goto]
+    {
+        // 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;
+            }
+
+            // all other characters are rejected outside scan_number()
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) 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; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+        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.data(), &endptr, 10);
+
+            // we checked the number format before
+            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+            if (errno == 0)
+            {
+                value_unsigned = static_cast<number_unsigned_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.data(), &endptr, 10);
+
+            // we checked the number format before
+            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+            if (errno == 0)
+            {
+                value_integer = static_cast<number_integer_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.data(), &endptr);
+
+        // we checked the number format before
+        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+        return token_type::value_float;
+    }
+
+    /*!
+    @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
+    */
+    JSON_HEDLEY_NON_NULL(2)
+    token_type scan_literal(const char_type* literal_text, const std::size_t length,
+                            token_type return_type)
+    {
+        JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);
+        for (std::size_t i = 1; i < length; ++i)
+        {
+            if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))
+            {
+                error_message = "invalid literal";
+                return token_type::parse_error;
+            }
+        }
+        return 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_type>::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
+    */
+    char_int_type get()
+    {
+        ++position.chars_read_total;
+        ++position.chars_read_current_line;
+
+        if (next_unget)
+        {
+            // just reset the next_unget variable and work with current
+            next_unget = false;
+        }
+        else
+        {
+            current = ia.get_character();
+        }
+
+        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
+        {
+            token_string.push_back(std::char_traits<char_type>::to_char_type(current));
+        }
+
+        if (current == '\n')
+        {
+            ++position.lines_read;
+            position.chars_read_current_line = 0;
+        }
+
+        return current;
+    }
+
+    /*!
+    @brief unget current character (read it again on next get)
+
+    We implement unget by setting variable next_unget to true. The input is not
+    changed - we just simulate ungetting by modifying chars_read_total,
+    chars_read_current_line, and token_string. The next call to get() will
+    behave as if the unget character is read again.
+    */
+    void unget()
+    {
+        next_unget = true;
+
+        --position.chars_read_total;
+
+        // in case we "unget" a newline, we have to also decrement the lines_read
+        if (position.chars_read_current_line == 0)
+        {
+            if (position.lines_read > 0)
+            {
+                --position.lines_read;
+            }
+        }
+        else
+        {
+            --position.chars_read_current_line;
+        }
+
+        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
+        {
+            JSON_ASSERT(!token_string.empty());
+            token_string.pop_back();
+        }
+    }
+
+    /// add a character to token_buffer
+    void add(char_int_type c)
+    {
+        token_buffer.push_back(static_cast<typename string_t::value_type>(c));
+    }
+
+  public:
+    /////////////////////
+    // value getters
+    /////////////////////
+
+    /// return integer value
+    constexpr number_integer_t get_number_integer() const noexcept
+    {
+        return value_integer;
+    }
+
+    /// return unsigned integer value
+    constexpr number_unsigned_t get_number_unsigned() const noexcept
+    {
+        return value_unsigned;
+    }
+
+    /// return floating-point value
+    constexpr number_float_t get_number_float() const noexcept
+    {
+        return value_float;
+    }
+
+    /// return current string value (implicitly resets the token; useful only once)
+    string_t& get_string()
+    {
+        return token_buffer;
+    }
+
+    /////////////////////
+    // diagnostics
+    /////////////////////
+
+    /// return position of last read token
+    constexpr position_t get_position() const noexcept
+    {
+        return position;
+    }
+
+    /// 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
+    {
+        // escape control characters
+        std::string result;
+        for (const auto c : token_string)
+        {
+            if (static_cast<unsigned char>(c) <= '\x1F')
+            {
+                // escape control characters
+                std::array<char, 9> cs{{}};
+                static_cast<void>((std::snprintf)(cs.data(), cs.size(), "<U+%.4X>", static_cast<unsigned char>(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                result += cs.data();
+            }
+            else
+            {
+                // add character as is
+                result.push_back(static_cast<std::string::value_type>(c));
+            }
+        }
+
+        return result;
+    }
+
+    /// return syntax error message
+    JSON_HEDLEY_RETURNS_NON_NULL
+    constexpr const char* get_error_message() const noexcept
+    {
+        return error_message;
+    }
+
+    /////////////////////
+    // actual scanner
+    /////////////////////
+
+    /*!
+    @brief skip the UTF-8 byte order mark
+    @return true iff there is no BOM or the correct BOM has been skipped
+    */
+    bool skip_bom()
+    {
+        if (get() == 0xEF)
+        {
+            // check if we completely parse the BOM
+            return get() == 0xBB && get() == 0xBF;
+        }
+
+        // the first character is not the beginning of the BOM; unget it to
+        // process is later
+        unget();
+        return true;
+    }
+
+    void skip_whitespace()
+    {
+        do
+        {
+            get();
+        }
+        while (current == ' ' || current == '\t' || current == '\n' || current == '\r');
+    }
+
+    token_type scan()
+    {
+        // initially, skip the BOM
+        if (position.chars_read_total == 0 && !skip_bom())
+        {
+            error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given";
+            return token_type::parse_error;
+        }
+
+        // read next character and ignore whitespace
+        skip_whitespace();
+
+        // ignore comments
+        while (ignore_comments && current == '/')
+        {
+            if (!scan_comment())
+            {
+                return token_type::parse_error;
+            }
+
+            // skip following whitespace
+            skip_whitespace();
+        }
+
+        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':
+            {
+                std::array<char_type, 4> true_literal = {{static_cast<char_type>('t'), static_cast<char_type>('r'), static_cast<char_type>('u'), static_cast<char_type>('e')}};
+                return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);
+            }
+            case 'f':
+            {
+                std::array<char_type, 5> false_literal = {{static_cast<char_type>('f'), static_cast<char_type>('a'), static_cast<char_type>('l'), static_cast<char_type>('s'), static_cast<char_type>('e')}};
+                return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);
+            }
+            case 'n':
+            {
+                std::array<char_type, 4> null_literal = {{static_cast<char_type>('n'), static_cast<char_type>('u'), static_cast<char_type>('l'), static_cast<char_type>('l')}};
+                return scan_literal(null_literal.data(), null_literal.size(), 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_type>::eof():
+                return token_type::end_of_input;
+
+            // error
+            default:
+                error_message = "invalid literal";
+                return token_type::parse_error;
+        }
+    }
+
+  private:
+    /// input adapter
+    InputAdapterType ia;
+
+    /// whether comments should be ignored (true) or signaled as errors (false)
+    const bool ignore_comments = false;
+
+    /// the current character
+    char_int_type current = std::char_traits<char_type>::eof();
+
+    /// whether the next get() call should just return current
+    bool next_unget = false;
+
+    /// the start position of the current token
+    position_t position {};
+
+    /// raw input token string (for error messages)
+    std::vector<char_type> token_string {};
+
+    /// buffer for variable-length tokens (numbers, strings)
+    string_t token_buffer {};
+
+    /// a description of occurred lexer errors
+    const char* error_message = "";
+
+    // number values
+    number_integer_t value_integer = 0;
+    number_unsigned_t value_unsigned = 0;
+    number_float_t value_float = 0;
+
+    /// the decimal point
+    const char_int_type decimal_point_char = '.';
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/parser.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/parser.h
new file mode 100644
index 0000000..32011ac
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/parser.h
@@ -0,0 +1,507 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cmath> // isfinite
+#include <cstdint> // uint8_t
+#include <functional> // function
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+#include <wpi/detail/exceptions.h>
+#include <wpi/detail/input/input_adapters.h>
+#include <wpi/detail/input/json_sax.h>
+#include <wpi/detail/input/lexer.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/meta/is_sax.h>
+#include <wpi/detail/string_concat.h>
+#include <wpi/detail/value_t.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+////////////
+// parser //
+////////////
+
+enum class parse_event_t : std::uint8_t
+{
+    /// the parser read `{` and started to process a JSON object
+    object_start,
+    /// the parser read `}` and finished processing a JSON object
+    object_end,
+    /// the parser read `[` and started to process a JSON array
+    array_start,
+    /// the parser read `]` and finished processing a JSON array
+    array_end,
+    /// the parser read a key of a value in an object
+    key,
+    /// the parser finished reading a JSON value
+    value
+};
+
+template<typename BasicJsonType>
+using parser_callback_t =
+    std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
+
+/*!
+@brief syntax analysis
+
+This class implements a recursive descent parser.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class parser
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using lexer_t = lexer<BasicJsonType, InputAdapterType>;
+    using token_type = typename lexer_t::token_type;
+
+  public:
+    /// a parser reading from an input adapter
+    explicit parser(InputAdapterType&& adapter,
+                    const parser_callback_t<BasicJsonType> cb = nullptr,
+                    const bool allow_exceptions_ = true,
+                    const bool skip_comments = false)
+        : callback(cb)
+        , m_lexer(std::move(adapter), skip_comments)
+        , allow_exceptions(allow_exceptions_)
+    {
+        // read first token
+        get_token();
+    }
+
+    /*!
+    @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, BasicJsonType& result)
+    {
+        if (callback)
+        {
+            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
+            sax_parse_internal(&sdp);
+
+            // in strict mode, input must be completely read
+            if (strict && (get_token() != token_type::end_of_input))
+            {
+                sdp.parse_error(m_lexer.get_position(),
+                                m_lexer.get_token_string(),
+                                parse_error::create(101, m_lexer.get_position(),
+                                                    exception_message(token_type::end_of_input, "value"), nullptr));
+            }
+
+            // in case of an error, return discarded value
+            if (sdp.is_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;
+            }
+        }
+        else
+        {
+            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
+            sax_parse_internal(&sdp);
+
+            // in strict mode, input must be completely read
+            if (strict && (get_token() != token_type::end_of_input))
+            {
+                sdp.parse_error(m_lexer.get_position(),
+                                m_lexer.get_token_string(),
+                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
+            }
+
+            // in case of an error, return discarded value
+            if (sdp.is_errored())
+            {
+                result = value_t::discarded;
+                return;
+            }
+        }
+
+        result.assert_invariant();
+    }
+
+    /*!
+    @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)
+    {
+        json_sax_acceptor<BasicJsonType> sax_acceptor;
+        return sax_parse(&sax_acceptor, strict);
+    }
+
+    template<typename SAX>
+    JSON_HEDLEY_NON_NULL(2)
+    bool sax_parse(SAX* sax, const bool strict = true)
+    {
+        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
+        const bool result = sax_parse_internal(sax);
+
+        // strict mode: next byte must be EOF
+        if (result && strict && (get_token() != token_type::end_of_input))
+        {
+            return sax->parse_error(m_lexer.get_position(),
+                                    m_lexer.get_token_string(),
+                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
+        }
+
+        return result;
+    }
+
+  private:
+    template<typename SAX>
+    JSON_HEDLEY_NON_NULL(2)
+    bool sax_parse_internal(SAX* sax)
+    {
+        // stack to remember the hierarchy of structured values we are parsing
+        // true = array; false = object
+        std::vector<bool> states;
+        // value to avoid a goto (see comment where set to true)
+        bool skip_to_state_evaluation = false;
+
+        while (true)
+        {
+            if (!skip_to_state_evaluation)
+            {
+                // invariant: get_token() was called before each iteration
+                switch (last_token)
+                {
+                    case token_type::begin_object:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+                        {
+                            return false;
+                        }
+
+                        // closing } -> we are done
+                        if (get_token() == token_type::end_object)
+                        {
+                            if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
+                            {
+                                return false;
+                            }
+                            break;
+                        }
+
+                        // parse key
+                        if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
+                        {
+                            return sax->parse_error(m_lexer.get_position(),
+                                                    m_lexer.get_token_string(),
+                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
+                        }
+                        if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+                        {
+                            return false;
+                        }
+
+                        // parse separator (:)
+                        if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
+                        {
+                            return sax->parse_error(m_lexer.get_position(),
+                                                    m_lexer.get_token_string(),
+                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
+                        }
+
+                        // remember we are now inside an object
+                        states.push_back(false);
+
+                        // parse values
+                        get_token();
+                        continue;
+                    }
+
+                    case token_type::begin_array:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+                        {
+                            return false;
+                        }
+
+                        // closing ] -> we are done
+                        if (get_token() == token_type::end_array)
+                        {
+                            if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
+                            {
+                                return false;
+                            }
+                            break;
+                        }
+
+                        // remember we are now inside an array
+                        states.push_back(true);
+
+                        // parse values (no need to call get_token)
+                        continue;
+                    }
+
+                    case token_type::value_float:
+                    {
+                        const auto res = m_lexer.get_number_float();
+
+                        if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
+                        {
+                            return sax->parse_error(m_lexer.get_position(),
+                                                    m_lexer.get_token_string(),
+                                                    out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
+                        }
+
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
+                        {
+                            return false;
+                        }
+
+                        break;
+                    }
+
+                    case token_type::literal_false:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::literal_null:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->null()))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::literal_true:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::value_integer:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::value_string:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::value_unsigned:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::parse_error:
+                    {
+                        // using "uninitialized" to avoid "expected" message
+                        return sax->parse_error(m_lexer.get_position(),
+                                                m_lexer.get_token_string(),
+                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
+                    }
+
+                    case token_type::uninitialized:
+                    case token_type::end_array:
+                    case token_type::end_object:
+                    case token_type::name_separator:
+                    case token_type::value_separator:
+                    case token_type::end_of_input:
+                    case token_type::literal_or_value:
+                    default: // the last token was unexpected
+                    {
+                        return sax->parse_error(m_lexer.get_position(),
+                                                m_lexer.get_token_string(),
+                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
+                    }
+                }
+            }
+            else
+            {
+                skip_to_state_evaluation = false;
+            }
+
+            // we reached this line after we successfully parsed a value
+            if (states.empty())
+            {
+                // empty stack: we reached the end of the hierarchy: done
+                return true;
+            }
+
+            if (states.back())  // array
+            {
+                // comma -> next value
+                if (get_token() == token_type::value_separator)
+                {
+                    // parse a new value
+                    get_token();
+                    continue;
+                }
+
+                // closing ]
+                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
+                    {
+                        return false;
+                    }
+
+                    // We are done with this array. Before we can parse a
+                    // new value, we need to evaluate the new state first.
+                    // By setting skip_to_state_evaluation to false, we
+                    // are effectively jumping to the beginning of this if.
+                    JSON_ASSERT(!states.empty());
+                    states.pop_back();
+                    skip_to_state_evaluation = true;
+                    continue;
+                }
+
+                return sax->parse_error(m_lexer.get_position(),
+                                        m_lexer.get_token_string(),
+                                        parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
+            }
+
+            // states.back() is false -> object
+
+            // comma -> next value
+            if (get_token() == token_type::value_separator)
+            {
+                // parse key
+                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
+                {
+                    return sax->parse_error(m_lexer.get_position(),
+                                            m_lexer.get_token_string(),
+                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
+                }
+
+                if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+                {
+                    return false;
+                }
+
+                // parse separator (:)
+                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
+                {
+                    return sax->parse_error(m_lexer.get_position(),
+                                            m_lexer.get_token_string(),
+                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
+                }
+
+                // parse values
+                get_token();
+                continue;
+            }
+
+            // closing }
+            if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
+            {
+                if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
+                {
+                    return false;
+                }
+
+                // We are done with this object. Before we can parse a
+                // new value, we need to evaluate the new state first.
+                // By setting skip_to_state_evaluation to false, we
+                // are effectively jumping to the beginning of this if.
+                JSON_ASSERT(!states.empty());
+                states.pop_back();
+                skip_to_state_evaluation = true;
+                continue;
+            }
+
+            return sax->parse_error(m_lexer.get_position(),
+                                    m_lexer.get_token_string(),
+                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
+        }
+    }
+
+    /// get next token from lexer
+    token_type get_token()
+    {
+        return last_token = m_lexer.scan();
+    }
+
+    std::string exception_message(const token_type expected, const std::string& context)
+    {
+        std::string error_msg = "syntax error ";
+
+        if (!context.empty())
+        {
+            error_msg += concat("while parsing ", context, ' ');
+        }
+
+        error_msg += "- ";
+
+        if (last_token == token_type::parse_error)
+        {
+            error_msg += concat(m_lexer.get_error_message(), "; last read: '",
+                                m_lexer.get_token_string(), '\'');
+        }
+        else
+        {
+            error_msg += concat("unexpected ", lexer_t::token_type_name(last_token));
+        }
+
+        if (expected != token_type::uninitialized)
+        {
+            error_msg += concat("; expected ", lexer_t::token_type_name(expected));
+        }
+
+        return error_msg;
+    }
+
+  private:
+    /// callback function
+    const parser_callback_t<BasicJsonType> callback = nullptr;
+    /// the type of the last read token
+    token_type last_token = token_type::uninitialized;
+    /// the lexer
+    lexer_t m_lexer;
+    /// whether to throw exceptions in case of errors
+    const bool allow_exceptions = true;
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/position_t.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/position_t.h
new file mode 100644
index 0000000..1476f55
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/input/position_t.h
@@ -0,0 +1,37 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef> // size_t
+
+#include <wpi/detail/abi_macros.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// struct to capture the start position of the current token
+struct position_t
+{
+    /// the total number of characters read
+    std::size_t chars_read_total = 0;
+    /// the number of characters read in the current line
+    std::size_t chars_read_current_line = 0;
+    /// the number of lines read
+    std::size_t lines_read = 0;
+
+    /// conversion to size_t to preserve SAX interface
+    constexpr operator size_t() const
+    {
+        return chars_read_total;
+    }
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/internal_iterator.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/internal_iterator.h
new file mode 100644
index 0000000..a97fb70
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/internal_iterator.h
@@ -0,0 +1,35 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <wpi/detail/abi_macros.h>
+#include <wpi/detail/iterators/primitive_iterator.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief an iterator value
+
+@note This structure could easily be a union, but MSVC currently does not allow
+unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
+*/
+template<typename BasicJsonType> struct internal_iterator
+{
+    /// iterator for JSON objects
+    typename BasicJsonType::object_t::iterator object_iterator {};
+    /// iterator for JSON arrays
+    typename BasicJsonType::array_t::iterator array_iterator {};
+    /// generic iterator for all other types
+    primitive_iterator_t primitive_iterator {};
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/iter_impl.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/iter_impl.h
new file mode 100644
index 0000000..904a252
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/iter_impl.h
@@ -0,0 +1,751 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
+#include <type_traits> // conditional, is_const, remove_const
+
+#include <wpi/detail/exceptions.h>
+#include <wpi/detail/iterators/internal_iterator.h>
+#include <wpi/detail/iterators/primitive_iterator.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/meta/cpp_future.h>
+#include <wpi/detail/meta/type_traits.h>
+#include <wpi/detail/value_t.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// forward declare, to be able to friend it later on
+template<typename IteratorType> class iteration_proxy;
+template<typename IteratorType> class iteration_proxy_value;
+
+/*!
+@brief a template for a bidirectional iterator for the @ref basic_json class
+This class implements a both iterators (iterator and const_iterator) for the
+@ref basic_json class.
+@note An iterator is called *initialized* when a pointer to a JSON value has
+      been set (e.g., by a constructor or a copy assignment). If the iterator is
+      default-constructed, it is *uninitialized* and most methods are undefined.
+      **The library uses assertions to detect calls on uninitialized iterators.**
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
+  The iterator that can be moved can be moved in both directions (i.e.
+  incremented and decremented).
+@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
+       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
+*/
+template<typename BasicJsonType>
+class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
+{
+    /// the iterator with BasicJsonType of different const-ness
+    using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
+    /// allow basic_json to access private members
+    friend other_iter_impl;
+    friend BasicJsonType;
+    friend iteration_proxy<iter_impl>;
+    friend iteration_proxy_value<iter_impl>;
+
+    using object_t = typename BasicJsonType::object_t;
+    using array_t = typename BasicJsonType::array_t;
+    // make sure BasicJsonType is basic_json or const basic_json
+    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
+                  "iter_impl only accepts (const) basic_json");
+    // superficial check for the LegacyBidirectionalIterator named requirement
+    static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
+                  &&  std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,
+                  "basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
+
+  public:
+    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
+    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
+    /// A user-defined iterator should provide publicly accessible typedefs named
+    /// iterator_category, value_type, difference_type, pointer, and reference.
+    /// Note that value_type is required to be non-const, even for constant iterators.
+    using iterator_category = std::bidirectional_iterator_tag;
+
+    /// the type of the values when the iterator is dereferenced
+    using value_type = typename BasicJsonType::value_type;
+    /// a type to represent differences between iterators
+    using difference_type = typename BasicJsonType::difference_type;
+    /// defines a pointer to the type iterated over (value_type)
+    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
+          typename BasicJsonType::const_pointer,
+          typename BasicJsonType::pointer>::type;
+    /// defines a reference to the type iterated over (value_type)
+    using reference =
+        typename std::conditional<std::is_const<BasicJsonType>::value,
+        typename BasicJsonType::const_reference,
+        typename BasicJsonType::reference>::type;
+
+    iter_impl() = default;
+    ~iter_impl() = default;
+    iter_impl(iter_impl&&) noexcept = default;
+    iter_impl& operator=(iter_impl&&) noexcept = default;
+
+    /*!
+    @brief constructor for a given JSON instance
+    @param[in] object  pointer to a JSON object for this iterator
+    @pre object != nullptr
+    @post The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    explicit iter_impl(pointer object) noexcept : m_object(object)
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                m_it.object_iterator = typename object_t::iterator();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_it.array_iterator = typename array_t::iterator();
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator = primitive_iterator_t();
+                break;
+            }
+        }
+    }
+
+    /*!
+    @note The conventional copy constructor and copy assignment are implicitly
+          defined. Combined with the following converting constructor and
+          assignment, they support: (1) copy from iterator to iterator, (2)
+          copy from const iterator to const iterator, and (3) conversion from
+          iterator to const iterator. However conversion from const iterator
+          to iterator is not defined.
+    */
+
+    /*!
+    @brief const copy constructor
+    @param[in] other const iterator to copy from
+    @note This copy constructor had to be defined explicitly to circumvent a bug
+          occurring on msvc v19.0 compiler (VS 2015) debug build. For more
+          information refer to: https://github.com/nlohmann/json/issues/1608
+    */
+    iter_impl(const iter_impl<const BasicJsonType>& other) noexcept
+        : m_object(other.m_object), m_it(other.m_it)
+    {}
+
+    /*!
+    @brief converting assignment
+    @param[in] other const iterator to copy from
+    @return const/non-const iterator
+    @note It is not checked whether @a other is initialized.
+    */
+    iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept
+    {
+        if (&other != this)
+        {
+            m_object = other.m_object;
+            m_it = other.m_it;
+        }
+        return *this;
+    }
+
+    /*!
+    @brief converting constructor
+    @param[in] other  non-const iterator to copy from
+    @note It is not checked whether @a other is initialized.
+    */
+    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
+        : m_object(other.m_object), m_it(other.m_it)
+    {}
+
+    /*!
+    @brief converting assignment
+    @param[in] other  non-const iterator to copy from
+    @return const/non-const iterator
+    @note It is not checked whether @a other is initialized.
+    */
+    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)
+    {
+        m_object = other.m_object;
+        m_it = other.m_it;
+        return *this;
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /*!
+    @brief set the iterator to the first value
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    void set_begin() noexcept
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                m_it.object_iterator = m_object->m_value.object->begin();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_it.array_iterator = m_object->m_value.array->begin();
+                break;
+            }
+
+            case value_t::null:
+            {
+                // set to end so begin()==end() is true: null is empty
+                m_it.primitive_iterator.set_end();
+                break;
+            }
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator.set_begin();
+                break;
+            }
+        }
+    }
+
+    /*!
+    @brief set the iterator past the last value
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    void set_end() noexcept
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                m_it.object_iterator = m_object->m_value.object->end();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_it.array_iterator = m_object->m_value.array->end();
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator.set_end();
+                break;
+            }
+        }
+    }
+
+  public:
+    /*!
+    @brief return a reference to the value pointed to by the iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    reference operator*() const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
+                return m_it.object_iterator->second;
+            }
+
+            case value_t::array:
+            {
+                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
+                return *m_it.array_iterator;
+            }
+
+            case value_t::null:
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
+                {
+                    return *m_object;
+                }
+
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+            }
+        }
+    }
+
+    /*!
+    @brief dereference the iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    pointer operator->() const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
+                return &(m_it.object_iterator->second);
+            }
+
+            case value_t::array:
+            {
+                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
+                return &*m_it.array_iterator;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
+                {
+                    return m_object;
+                }
+
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+            }
+        }
+    }
+
+    /*!
+    @brief post-increment (it++)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        ++(*this);
+        return result;
+    }
+
+    /*!
+    @brief pre-increment (++it)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator++()
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                std::advance(m_it.object_iterator, 1);
+                break;
+            }
+
+            case value_t::array:
+            {
+                std::advance(m_it.array_iterator, 1);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                ++m_it.primitive_iterator;
+                break;
+            }
+        }
+
+        return *this;
+    }
+
+    /*!
+    @brief post-decrement (it--)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        --(*this);
+        return result;
+    }
+
+    /*!
+    @brief pre-decrement (--it)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator--()
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+            {
+                std::advance(m_it.object_iterator, -1);
+                break;
+            }
+
+            case value_t::array:
+            {
+                std::advance(m_it.array_iterator, -1);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                --m_it.primitive_iterator;
+                break;
+            }
+        }
+
+        return *this;
+    }
+
+    /*!
+    @brief comparison: equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
+    bool operator==(const IterImpl& other) const
+    {
+        // if objects are not the same, the comparison is undefined
+        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
+        }
+
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+                return (m_it.object_iterator == other.m_it.object_iterator);
+
+            case value_t::array:
+                return (m_it.array_iterator == other.m_it.array_iterator);
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return (m_it.primitive_iterator == other.m_it.primitive_iterator);
+        }
+    }
+
+    /*!
+    @brief comparison: not equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
+    bool operator!=(const IterImpl& other) const
+    {
+        return !operator==(other);
+    }
+
+    /*!
+    @brief comparison: smaller
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator<(const iter_impl& other) const
+    {
+        // if objects are not the same, the comparison is undefined
+        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
+        }
+
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
+
+            case value_t::array:
+                return (m_it.array_iterator < other.m_it.array_iterator);
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return (m_it.primitive_iterator < other.m_it.primitive_iterator);
+        }
+    }
+
+    /*!
+    @brief comparison: less than or equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator<=(const iter_impl& other) const
+    {
+        return !other.operator < (*this);
+    }
+
+    /*!
+    @brief comparison: greater than
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator>(const iter_impl& other) const
+    {
+        return !operator<=(other);
+    }
+
+    /*!
+    @brief comparison: greater than or equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator>=(const iter_impl& other) const
+    {
+        return !operator<(other);
+    }
+
+    /*!
+    @brief add to iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator+=(difference_type i)
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
+
+            case value_t::array:
+            {
+                std::advance(m_it.array_iterator, i);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator += i;
+                break;
+            }
+        }
+
+        return *this;
+    }
+
+    /*!
+    @brief subtract from iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator-=(difference_type i)
+    {
+        return operator+=(-i);
+    }
+
+    /*!
+    @brief add to iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator+(difference_type i) const
+    {
+        auto result = *this;
+        result += i;
+        return result;
+    }
+
+    /*!
+    @brief addition of distance and iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    friend iter_impl operator+(difference_type i, const iter_impl& it)
+    {
+        auto result = it;
+        result += i;
+        return result;
+    }
+
+    /*!
+    @brief subtract from iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator-(difference_type i) const
+    {
+        auto result = *this;
+        result -= i;
+        return result;
+    }
+
+    /*!
+    @brief return difference
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    difference_type operator-(const iter_impl& other) const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
+
+            case value_t::array:
+                return m_it.array_iterator - other.m_it.array_iterator;
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return m_it.primitive_iterator - other.m_it.primitive_iterator;
+        }
+    }
+
+    /*!
+    @brief access to successor
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    reference operator[](difference_type n) const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
+
+            case value_t::array:
+                return *std::next(m_it.array_iterator, n);
+
+            case value_t::null:
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))
+                {
+                    return *m_object;
+                }
+
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+            }
+        }
+    }
+
+    /*!
+    @brief return the key of an object iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    const typename object_t::key_type& key() const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        if (JSON_HEDLEY_LIKELY(m_object->is_object()))
+        {
+            return m_it.object_iterator->first;
+        }
+
+        JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
+    }
+
+    /*!
+    @brief return the value of an iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    reference value() const
+    {
+        return operator*();
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /// associated JSON instance
+    pointer m_object = nullptr;
+    /// the actual iterator of the associated instance
+    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/iteration_proxy.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/iteration_proxy.h
new file mode 100644
index 0000000..7f7492f
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/iteration_proxy.h
@@ -0,0 +1,242 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef> // size_t
+#include <iterator> // input_iterator_tag
+#include <string> // string, to_string
+#include <tuple> // tuple_size, get, tuple_element
+#include <utility> // move
+
+#if JSON_HAS_RANGES
+    #include <ranges> // enable_borrowed_range
+#endif
+
+#include <wpi/detail/abi_macros.h>
+#include <wpi/detail/meta/type_traits.h>
+#include <wpi/detail/value_t.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename string_type>
+void int_to_string( string_type& target, std::size_t value )
+{
+    // For ADL
+    using std::to_string;
+    target = to_string(value);
+}
+template<typename IteratorType> class iteration_proxy_value
+{
+  public:
+    using difference_type = std::ptrdiff_t;
+    using value_type = iteration_proxy_value;
+    using pointer = value_type *;
+    using reference = value_type &;
+    using iterator_category = std::input_iterator_tag;
+    using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
+
+  private:
+    /// the iterator
+    IteratorType anchor{};
+    /// an index for arrays (used to create key names)
+    std::size_t array_index = 0;
+    /// last stringified array index
+    mutable std::size_t array_index_last = 0;
+    /// a string representation of the array index
+    mutable string_type array_index_str = "0";
+    /// an empty string (to return a reference for primitive values)
+    string_type empty_str{};
+
+  public:
+    explicit iteration_proxy_value() = default;
+    explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)
+    noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+             && std::is_nothrow_default_constructible<string_type>::value)
+        : anchor(std::move(it))
+        , array_index(array_index_)
+    {}
+
+    iteration_proxy_value(iteration_proxy_value const&) = default;
+    iteration_proxy_value& operator=(iteration_proxy_value const&) = default;
+    // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions
+    iteration_proxy_value(iteration_proxy_value&&)
+    noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+             && std::is_nothrow_move_constructible<string_type>::value) = default;
+    iteration_proxy_value& operator=(iteration_proxy_value&&)
+    noexcept(std::is_nothrow_move_assignable<IteratorType>::value
+             && std::is_nothrow_move_assignable<string_type>::value) = default;
+    ~iteration_proxy_value() = default;
+
+    /// dereference operator (needed for range-based for)
+    const iteration_proxy_value& operator*() const
+    {
+        return *this;
+    }
+
+    /// increment operator (needed for range-based for)
+    iteration_proxy_value& operator++()
+    {
+        ++anchor;
+        ++array_index;
+
+        return *this;
+    }
+
+    iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        auto tmp = iteration_proxy_value(anchor, array_index);
+        ++anchor;
+        ++array_index;
+        return tmp;
+    }
+
+    /// equality operator (needed for InputIterator)
+    bool operator==(const iteration_proxy_value& o) const
+    {
+        return anchor == o.anchor;
+    }
+
+    /// inequality operator (needed for range-based for)
+    bool operator!=(const iteration_proxy_value& o) const
+    {
+        return anchor != o.anchor;
+    }
+
+    /// return key of the iterator
+    const string_type& key() const
+    {
+        JSON_ASSERT(anchor.m_object != nullptr);
+
+        switch (anchor.m_object->type())
+        {
+            // use integer array index as key
+            case value_t::array:
+            {
+                if (array_index != array_index_last)
+                {
+                    int_to_string( array_index_str, array_index );
+                    array_index_last = array_index;
+                }
+                return array_index_str;
+            }
+
+            // use key from the object
+            case value_t::object:
+                return anchor.key();
+
+            // use an empty key for all primitive types
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return empty_str;
+        }
+    }
+
+    /// return value of the iterator
+    typename IteratorType::reference value() const
+    {
+        return anchor.value();
+    }
+};
+
+/// proxy class for the items() function
+template<typename IteratorType> class iteration_proxy
+{
+  private:
+    /// the container to iterate
+    typename IteratorType::pointer container = nullptr;
+
+  public:
+    explicit iteration_proxy() = default;
+
+    /// construct iteration proxy from a container
+    explicit iteration_proxy(typename IteratorType::reference cont) noexcept
+        : container(&cont) {}
+
+    iteration_proxy(iteration_proxy const&) = default;
+    iteration_proxy& operator=(iteration_proxy const&) = default;
+    iteration_proxy(iteration_proxy&&) noexcept = default;
+    iteration_proxy& operator=(iteration_proxy&&) noexcept = default;
+    ~iteration_proxy() = default;
+
+    /// return iterator begin (needed for range-based for)
+    iteration_proxy_value<IteratorType> begin() const noexcept
+    {
+        return iteration_proxy_value<IteratorType>(container->begin());
+    }
+
+    /// return iterator end (needed for range-based for)
+    iteration_proxy_value<IteratorType> end() const noexcept
+    {
+        return iteration_proxy_value<IteratorType>(container->end());
+    }
+};
+
+// Structured Bindings Support
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
+auto get(const wpi::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
+{
+    return i.key();
+}
+// Structured Bindings Support
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
+auto get(const wpi::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
+{
+    return i.value();
+}
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
+
+// The Addition to the STD Namespace is required to add
+// Structured Bindings Support to the iteration_proxy_value class
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+namespace std
+{
+
+#if defined(__clang__)
+    // Fix: https://github.com/nlohmann/json/issues/1401
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+template<typename IteratorType>
+class tuple_size<::wpi::detail::iteration_proxy_value<IteratorType>>
+            : public std::integral_constant<std::size_t, 2> {};
+
+template<std::size_t N, typename IteratorType>
+class tuple_element<N, ::wpi::detail::iteration_proxy_value<IteratorType >>
+{
+  public:
+    using type = decltype(
+                     get<N>(std::declval <
+                            ::wpi::detail::iteration_proxy_value<IteratorType >> ()));
+};
+#if defined(__clang__)
+    #pragma clang diagnostic pop
+#endif
+
+}  // namespace std
+
+#if JSON_HAS_RANGES
+    template <typename IteratorType>
+    inline constexpr bool ::std::ranges::enable_borrowed_range<::wpi::detail::iteration_proxy<IteratorType>> = true;
+#endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/iterator_traits.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/iterator_traits.h
new file mode 100644
index 0000000..855872a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/iterator_traits.h
@@ -0,0 +1,61 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <iterator> // random_access_iterator_tag
+
+#include <wpi/detail/abi_macros.h>
+#include <wpi/detail/meta/void_t.h>
+#include <wpi/detail/meta/cpp_future.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename It, typename = void>
+struct iterator_types {};
+
+template<typename It>
+struct iterator_types <
+    It,
+    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
+    typename It::reference, typename It::iterator_category >>
+{
+    using difference_type = typename It::difference_type;
+    using value_type = typename It::value_type;
+    using pointer = typename It::pointer;
+    using reference = typename It::reference;
+    using iterator_category = typename It::iterator_category;
+};
+
+// This is required as some compilers implement std::iterator_traits in a way that
+// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
+template<typename T, typename = void>
+struct iterator_traits
+{
+};
+
+template<typename T>
+struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
+            : iterator_types<T>
+{
+};
+
+template<typename T>
+struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
+{
+    using iterator_category = std::random_access_iterator_tag;
+    using value_type = T;
+    using difference_type = ptrdiff_t;
+    using pointer = T*;
+    using reference = T&;
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/json_reverse_iterator.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/json_reverse_iterator.h
new file mode 100644
index 0000000..ea2740f
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/json_reverse_iterator.h
@@ -0,0 +1,130 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef> // ptrdiff_t
+#include <iterator> // reverse_iterator
+#include <utility> // declval
+
+#include <wpi/detail/abi_macros.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+//////////////////////
+// reverse_iterator //
+//////////////////////
+
+/*!
+@brief a template for a reverse iterator class
+
+@tparam Base the base iterator type to reverse. Valid types are @ref
+iterator (to create @ref reverse_iterator) and @ref const_iterator (to
+create @ref const_reverse_iterator).
+
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
+  The iterator that can be moved can be moved in both directions (i.e.
+  incremented and decremented).
+- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
+  It is possible to write to the pointed-to element (only if @a Base is
+  @ref iterator).
+
+@since version 1.0.0
+*/
+template<typename Base>
+class json_reverse_iterator : public std::reverse_iterator<Base>
+{
+  public:
+    using difference_type = std::ptrdiff_t;
+    /// shortcut to the reverse iterator adapter
+    using base_iterator = std::reverse_iterator<Base>;
+    /// the reference type for the pointed-to element
+    using reference = typename Base::reference;
+
+    /// create reverse iterator from iterator
+    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
+        : base_iterator(it) {}
+
+    /// create reverse iterator from base class
+    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
+
+    /// post-increment (it++)
+    json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
+    }
+
+    /// pre-increment (++it)
+    json_reverse_iterator& operator++()
+    {
+        return static_cast<json_reverse_iterator&>(base_iterator::operator++());
+    }
+
+    /// post-decrement (it--)
+    json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
+    }
+
+    /// pre-decrement (--it)
+    json_reverse_iterator& operator--()
+    {
+        return static_cast<json_reverse_iterator&>(base_iterator::operator--());
+    }
+
+    /// add to iterator
+    json_reverse_iterator& operator+=(difference_type i)
+    {
+        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
+    }
+
+    /// add to iterator
+    json_reverse_iterator operator+(difference_type i) const
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
+    }
+
+    /// subtract from iterator
+    json_reverse_iterator operator-(difference_type i) const
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
+    }
+
+    /// return difference
+    difference_type operator-(const json_reverse_iterator& other) const
+    {
+        return base_iterator(*this) - base_iterator(other);
+    }
+
+    /// access to successor
+    reference operator[](difference_type n) const
+    {
+        return *(this->operator+(n));
+    }
+
+    /// return the key of an object iterator
+    auto key() const -> decltype(std::declval<Base>().key())
+    {
+        auto it = --this->base();
+        return it.key();
+    }
+
+    /// return the value of an iterator
+    reference value() const
+    {
+        auto it = --this->base();
+        return it.operator * ();
+    }
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/primitive_iterator.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/primitive_iterator.h
new file mode 100644
index 0000000..914bd5b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/iterators/primitive_iterator.h
@@ -0,0 +1,132 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef> // ptrdiff_t
+#include <limits>  // numeric_limits
+
+#include <wpi/detail/macro_scope.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*
+@brief an iterator for primitive JSON types
+
+This class models an iterator for primitive JSON types (boolean, number,
+string). It's only purpose is to allow the iterator/const_iterator classes
+to "iterate" over primitive values. Internally, the iterator is modeled by
+a `difference_type` variable. Value begin_value (`0`) models the begin,
+end_value (`1`) models past the end.
+*/
+class primitive_iterator_t
+{
+  private:
+    using difference_type = std::ptrdiff_t;
+    static constexpr difference_type begin_value = 0;
+    static constexpr difference_type end_value = begin_value + 1;
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /// iterator as signed integer type
+    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
+
+  public:
+    constexpr difference_type get_value() const noexcept
+    {
+        return m_it;
+    }
+
+    /// set iterator to a defined beginning
+    void set_begin() noexcept
+    {
+        m_it = begin_value;
+    }
+
+    /// set iterator to a defined past the end
+    void set_end() noexcept
+    {
+        m_it = end_value;
+    }
+
+    /// return whether the iterator can be dereferenced
+    constexpr bool is_begin() const noexcept
+    {
+        return m_it == begin_value;
+    }
+
+    /// return whether the iterator is at end
+    constexpr bool is_end() const noexcept
+    {
+        return m_it == end_value;
+    }
+
+    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+    {
+        return lhs.m_it == rhs.m_it;
+    }
+
+    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+    {
+        return lhs.m_it < rhs.m_it;
+    }
+
+    primitive_iterator_t operator+(difference_type n) noexcept
+    {
+        auto result = *this;
+        result += n;
+        return result;
+    }
+
+    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+    {
+        return lhs.m_it - rhs.m_it;
+    }
+
+    primitive_iterator_t& operator++() noexcept
+    {
+        ++m_it;
+        return *this;
+    }
+
+    primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        ++m_it;
+        return result;
+    }
+
+    primitive_iterator_t& operator--() noexcept
+    {
+        --m_it;
+        return *this;
+    }
+
+    primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        --m_it;
+        return result;
+    }
+
+    primitive_iterator_t& operator+=(difference_type n) noexcept
+    {
+        m_it += n;
+        return *this;
+    }
+
+    primitive_iterator_t& operator-=(difference_type n) noexcept
+    {
+        m_it -= n;
+        return *this;
+    }
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/json_pointer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/json_pointer.h
new file mode 100644
index 0000000..b52d914
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/json_pointer.h
@@ -0,0 +1,988 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <algorithm> // all_of
+#include <cctype> // isdigit
+#include <cerrno> // errno, ERANGE
+#include <cstdlib> // strtoull
+#ifndef JSON_NO_IO
+    #include <iosfwd> // ostream
+#endif  // JSON_NO_IO
+#include <limits> // max
+#include <numeric> // accumulate
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+#include <wpi/detail/exceptions.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/string_concat.h>
+#include <wpi/detail/string_escape.h>
+#include <wpi/detail/value_t.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+
+/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
+/// @sa https://json.nlohmann.me/api/json_pointer/
+template<typename RefStringType>
+class json_pointer
+{
+    // allow basic_json to access private members
+    WPI_BASIC_JSON_TPL_DECLARATION
+    friend class basic_json;
+
+    template<typename>
+    friend class json_pointer;
+
+    template<typename T>
+    struct string_t_helper
+    {
+        using type = T;
+    };
+
+    WPI_BASIC_JSON_TPL_DECLARATION
+    struct string_t_helper<WPI_BASIC_JSON_TPL>
+    {
+        using type = StringType;
+    };
+
+  public:
+    // for backwards compatibility accept BasicJsonType
+    using string_t = typename string_t_helper<RefStringType>::type;
+
+    /// @brief create JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/
+    explicit json_pointer(const string_t& s = "")
+        : reference_tokens(split(s))
+    {}
+
+    /// @brief return a string representation of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/to_string/
+    string_t to_string() const
+    {
+        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
+                               string_t{},
+                               [](const string_t& a, const string_t& b)
+        {
+            return detail::concat(a, '/', detail::escape(b));
+        });
+    }
+
+    /// @brief return a string representation of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())
+    operator string_t() const
+    {
+        return to_string();
+    }
+
+#ifndef JSON_NO_IO
+    /// @brief write string representation of the JSON pointer to stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+    friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr)
+    {
+        o << ptr.to_string();
+        return o;
+    }
+#endif
+
+    /// @brief append another JSON pointer at the end of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+    json_pointer& operator/=(const json_pointer& ptr)
+    {
+        reference_tokens.insert(reference_tokens.end(),
+                                ptr.reference_tokens.begin(),
+                                ptr.reference_tokens.end());
+        return *this;
+    }
+
+    /// @brief append an unescaped reference token at the end of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+    json_pointer& operator/=(string_t token)
+    {
+        push_back(std::move(token));
+        return *this;
+    }
+
+    /// @brief append an array index at the end of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+    json_pointer& operator/=(std::size_t array_idx)
+    {
+        return *this /= std::to_string(array_idx);
+    }
+
+    /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+    friend json_pointer operator/(const json_pointer& lhs,
+                                  const json_pointer& rhs)
+    {
+        return json_pointer(lhs) /= rhs;
+    }
+
+    /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+    friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)
+    {
+        return json_pointer(lhs) /= std::move(token);
+    }
+
+    /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+    friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)
+    {
+        return json_pointer(lhs) /= array_idx;
+    }
+
+    /// @brief returns the parent of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/
+    json_pointer parent_pointer() const
+    {
+        if (empty())
+        {
+            return *this;
+        }
+
+        json_pointer res = *this;
+        res.pop_back();
+        return res;
+    }
+
+    /// @brief remove last reference token
+    /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/
+    void pop_back()
+    {
+        if (JSON_HEDLEY_UNLIKELY(empty()))
+        {
+            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+        }
+
+        reference_tokens.pop_back();
+    }
+
+    /// @brief return last reference token
+    /// @sa https://json.nlohmann.me/api/json_pointer/back/
+    const string_t& back() const
+    {
+        if (JSON_HEDLEY_UNLIKELY(empty()))
+        {
+            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+        }
+
+        return reference_tokens.back();
+    }
+
+    /// @brief append an unescaped token at the end of the reference pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
+    void push_back(const string_t& token)
+    {
+        reference_tokens.push_back(token);
+    }
+
+    /// @brief append an unescaped token at the end of the reference pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
+    void push_back(string_t&& token)
+    {
+        reference_tokens.push_back(std::move(token));
+    }
+
+    /// @brief return whether pointer points to the root document
+    /// @sa https://json.nlohmann.me/api/json_pointer/empty/
+    bool empty() const noexcept
+    {
+        return reference_tokens.empty();
+    }
+
+  private:
+    /*!
+    @param[in] s  reference token to be converted into an array index
+
+    @return integer representation of @a s
+
+    @throw parse_error.106  if an array index begins with '0'
+    @throw parse_error.109  if an array index begins not with a digit
+    @throw out_of_range.404 if string @a s could not be converted to an integer
+    @throw out_of_range.410 if an array index exceeds size_type
+    */
+    template<typename BasicJsonType>
+    static typename BasicJsonType::size_type array_index(const string_t& s)
+    {
+        using size_type = typename BasicJsonType::size_type;
+
+        // error condition (cf. RFC 6901, Sect. 4)
+        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
+        {
+            JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
+        }
+
+        // error condition (cf. RFC 6901, Sect. 4)
+        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
+        {
+            JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
+        }
+
+        const char* p = s.c_str();
+        char* p_end = nullptr;
+        errno = 0; // strtoull doesn't reset errno
+        unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
+        if (p == p_end // invalid input or empty string
+                || errno == ERANGE // out of range
+                || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
+        {
+            JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
+        }
+
+        // only triggered on special platforms (like 32bit), see also
+        // https://github.com/nlohmann/json/pull/2203
+        if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))  // NOLINT(runtime/int)
+        {
+            JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr));   // LCOV_EXCL_LINE
+        }
+
+        return static_cast<size_type>(res);
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    json_pointer top() const
+    {
+        if (JSON_HEDLEY_UNLIKELY(empty()))
+        {
+            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+        }
+
+        json_pointer result = *this;
+        result.reference_tokens = {reference_tokens[0]};
+        return result;
+    }
+
+  private:
+    /*!
+    @brief create and return a reference to the pointed to value
+
+    @complexity Linear in the number of reference tokens.
+
+    @throw parse_error.109 if array index is not a number
+    @throw type_error.313 if value cannot be unflattened
+    */
+    template<typename BasicJsonType>
+    BasicJsonType& get_and_create(BasicJsonType& j) const
+    {
+        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->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
+                    result = &result->operator[](array_index<BasicJsonType>(reference_token));
+                    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.
+                */
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
+            }
+        }
+
+        return *result;
+    }
+
+    /*!
+    @brief return a reference to the pointed to value
+
+    @note This version does not throw if a value is not present, but tries to
+          create nested values instead. For instance, calling this function
+          with pointer `"/this/that"` on a null value is equivalent to calling
+          `operator[]("this").operator[]("that")` on that value, effectively
+          changing the null value to an object.
+
+    @param[in] ptr  a JSON value
+
+    @return reference to the JSON value pointed to by the JSON pointer
+
+    @complexity Linear in the length of the JSON pointer.
+
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    BasicJsonType& get_unchecked(BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            // convert null values to arrays or objects before continuing
+            if (ptr->is_null())
+            {
+                // check if reference token is a number
+                const bool nums =
+                    std::all_of(reference_token.begin(), reference_token.end(),
+                                [](const unsigned char x)
+                {
+                    return std::isdigit(x);
+                });
+
+                // change value to array for numbers or "-" or to object otherwise
+                *ptr = (nums || reference_token == "-")
+                       ? detail::value_t::array
+                       : detail::value_t::object;
+            }
+
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // use unchecked object access
+                    ptr = &ptr->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    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
+                        ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
+                    }
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.402  if the array index '-' is used
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    BasicJsonType& get_checked(BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // note: at performs range check
+                    ptr = &ptr->at(reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" always fails the range check
+                        JSON_THROW(detail::out_of_range::create(402, detail::concat(
+                                "array index '-' (", std::to_string(ptr->m_value.array->size()),
+                                ") is out of range"), ptr));
+                    }
+
+                    // note: at performs range check
+                    ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @brief return a const reference to the pointed to value
+
+    @param[in] ptr  a JSON value
+
+    @return const reference to the JSON value pointed to by the JSON
+    pointer
+
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.402  if the array index '-' is used
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // use unchecked object access
+                    ptr = &ptr->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" cannot be used for const access
+                        JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr));
+                    }
+
+                    // use unchecked array access
+                    ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.402  if the array index '-' is used
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    const BasicJsonType& get_checked(const BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // note: at performs range check
+                    ptr = &ptr->at(reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" always fails the range check
+                        JSON_THROW(detail::out_of_range::create(402, detail::concat(
+                                "array index '-' (", std::to_string(ptr->m_value.array->size()),
+                                ") is out of range"), ptr));
+                    }
+
+                    // note: at performs range check
+                    ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    */
+    template<typename BasicJsonType>
+    bool contains(const BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    if (!ptr->contains(reference_token))
+                    {
+                        // we did not find the key in the object
+                        return false;
+                    }
+
+                    ptr = &ptr->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" always fails the range check
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
+                    {
+                        // invalid char
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))
+                        {
+                            // first char should be between '1' and '9'
+                            return false;
+                        }
+                        for (std::size_t i = 1; i < reference_token.size(); i++)
+                        {
+                            if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
+                            {
+                                // other char should be between '0' and '9'
+                                return false;
+                            }
+                        }
+                    }
+
+                    const auto idx = array_index<BasicJsonType>(reference_token);
+                    if (idx >= ptr->size())
+                    {
+                        // index out of range
+                        return false;
+                    }
+
+                    ptr = &ptr->operator[](idx);
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                {
+                    // we do not expect primitive values if there is still a
+                    // reference token to process
+                    return false;
+                }
+            }
+        }
+
+        // no reference token left means we found a primitive value
+        return true;
+    }
+
+    /*!
+    @brief split the string input to reference tokens
+
+    @note This function is only called by the json_pointer constructor.
+          All exceptions below are documented there.
+
+    @throw parse_error.107  if the pointer is not empty or begins with '/'
+    @throw parse_error.108  if character '~' is not followed by '0' or '1'
+    */
+    static std::vector<string_t> split(const string_t& reference_string)
+    {
+        std::vector<string_t> result;
+
+        // special case: empty reference string -> no reference tokens
+        if (reference_string.empty())
+        {
+            return result;
+        }
+
+        // check if nonempty reference string begins with slash
+        if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
+        {
+            JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
+        }
+
+        // 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 = reference_string.find_first_of('/', 1),
+            // set the beginning of the first reference token
+            start = 1;
+            // we can stop if start == 0 (if slash == string_t::npos)
+            start != 0;
+            // set the beginning of the next reference token
+            // (will eventually be 0 if slash == string_t::npos)
+            start = (slash == string_t::npos) ? 0 : slash + 1,
+            // find next slash
+            slash = reference_string.find_first_of('/', start))
+        {
+            // use the text between the beginning of the reference token
+            // (start) and the last slash (slash).
+            auto reference_token = reference_string.substr(start, slash - start);
+
+            // check reference tokens are properly escaped
+            for (std::size_t pos = reference_token.find_first_of('~');
+                    pos != string_t::npos;
+                    pos = reference_token.find_first_of('~', pos + 1))
+            {
+                JSON_ASSERT(reference_token[pos] == '~');
+
+                // ~ must be followed by 0 or 1
+                if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
+                                         (reference_token[pos + 1] != '0' &&
+                                          reference_token[pos + 1] != '1')))
+                {
+                    JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
+                }
+            }
+
+            // finally, store the reference token
+            detail::unescape(reference_token);
+            result.push_back(reference_token);
+        }
+
+        return result;
+    }
+
+  private:
+    /*!
+    @param[in] reference_string  the reference string to the current value
+    @param[in] value             the value to consider
+    @param[in,out] result        the result object to insert values to
+
+    @note Empty objects or arrays are flattened to `null`.
+    */
+    template<typename BasicJsonType>
+    static void flatten(const string_t& reference_string,
+                        const BasicJsonType& value,
+                        BasicJsonType& result)
+    {
+        switch (value.type())
+        {
+            case detail::value_t::array:
+            {
+                if (value.m_value.array->empty())
+                {
+                    // flatten empty array as null
+                    result[reference_string] = nullptr;
+                }
+                else
+                {
+                    // iterate array and use index as reference string
+                    for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
+                    {
+                        flatten(detail::concat(reference_string, '/', std::to_string(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
+                    result[reference_string] = nullptr;
+                }
+                else
+                {
+                    // iterate object and use keys as reference string
+                    for (const auto& element : *value.m_value.object)
+                    {
+                        flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result);
+                    }
+                }
+                break;
+            }
+
+            case detail::value_t::null:
+            case detail::value_t::string:
+            case detail::value_t::boolean:
+            case detail::value_t::number_integer:
+            case detail::value_t::number_unsigned:
+            case detail::value_t::number_float:
+            case detail::value_t::binary:
+            case detail::value_t::discarded:
+            default:
+            {
+                // add primitive value with its reference string
+                result[reference_string] = value;
+                break;
+            }
+        }
+    }
+
+    /*!
+    @param[in] value  flattened JSON
+
+    @return unflattened JSON
+
+    @throw parse_error.109 if array index is not a number
+    @throw type_error.314  if value is not an object
+    @throw type_error.315  if object values are not primitive
+    @throw type_error.313  if value cannot be unflattened
+    */
+    template<typename BasicJsonType>
+    static BasicJsonType
+    unflatten(const BasicJsonType& value)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
+        {
+            JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value));
+        }
+
+        BasicJsonType result;
+
+        // iterate the JSON object values
+        for (const auto& element : *value.m_value.object)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
+            {
+                JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second));
+            }
+
+            // 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;
+    }
+
+    // can't use conversion operator because of ambiguity
+    json_pointer<string_t> convert() const&
+    {
+        json_pointer<string_t> result;
+        result.reference_tokens = reference_tokens;
+        return result;
+    }
+
+    json_pointer<string_t> convert()&&
+    {
+        json_pointer<string_t> result;
+        result.reference_tokens = std::move(reference_tokens);
+        return result;
+    }
+
+  public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+    /// @brief compares two JSON pointers for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeRhs>
+    bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept
+    {
+        return reference_tokens == rhs.reference_tokens;
+    }
+
+    /// @brief compares JSON pointer and string for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer))
+    bool operator==(const string_t& rhs) const
+    {
+        return *this == json_pointer(rhs);
+    }
+
+    /// @brief 3-way compares two JSON pointers
+    template<typename RefStringTypeRhs>
+    std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD*
+    {
+        return  reference_tokens <=> rhs.reference_tokens; // *NOPAD*
+    }
+#else
+    /// @brief compares two JSON pointers for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+
+    /// @brief compares JSON pointer and string for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeLhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                           const StringType& rhs);
+
+    /// @brief compares string and JSON pointer for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeRhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator==(const StringType& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs);
+
+    /// @brief compares two JSON pointers for inequality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+    template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+
+    /// @brief compares JSON pointer and string for inequality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+    template<typename RefStringTypeLhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                           const StringType& rhs);
+
+    /// @brief compares string and JSON pointer for inequality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+    template<typename RefStringTypeRhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator!=(const StringType& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs);
+
+    /// @brief compares two JSON pointer for less-than
+    template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
+                          const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+#endif
+
+  private:
+    /// the reference tokens
+    std::vector<string_t> reference_tokens;
+};
+
+#if !JSON_HAS_THREE_WAY_COMPARISON
+// functions cannot be defined inside class due to ODR violations
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+    return lhs.reference_tokens == rhs.reference_tokens;
+}
+
+template<typename RefStringTypeLhs,
+         typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
+inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                       const StringType& rhs)
+{
+    return lhs == json_pointer<RefStringTypeLhs>(rhs);
+}
+
+template<typename RefStringTypeRhs,
+         typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
+inline bool operator==(const StringType& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs)
+{
+    return json_pointer<RefStringTypeRhs>(lhs) == rhs;
+}
+
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+    return !(lhs == rhs);
+}
+
+template<typename RefStringTypeLhs,
+         typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
+inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                       const StringType& rhs)
+{
+    return !(lhs == rhs);
+}
+
+template<typename RefStringTypeRhs,
+         typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
+inline bool operator!=(const StringType& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs)
+{
+    return !(lhs == rhs);
+}
+
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
+                      const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+    return lhs.reference_tokens < rhs.reference_tokens;
+}
+#endif
+
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/json_ref.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/json_ref.h
new file mode 100644
index 0000000..6bf600b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/json_ref.h
@@ -0,0 +1,78 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <initializer_list>
+#include <utility>
+
+#include <wpi/detail/abi_macros.h>
+#include <wpi/detail/meta/type_traits.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename BasicJsonType>
+class json_ref
+{
+  public:
+    using value_type = BasicJsonType;
+
+    json_ref(value_type&& value)
+        : owned_value(std::move(value))
+    {}
+
+    json_ref(const value_type& value)
+        : value_ref(&value)
+    {}
+
+    json_ref(std::initializer_list<json_ref> init)
+        : owned_value(init)
+    {}
+
+    template <
+        class... Args,
+        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >
+    json_ref(Args && ... args)
+        : owned_value(std::forward<Args>(args)...)
+    {}
+
+    // class should be movable only
+    json_ref(json_ref&&) noexcept = default;
+    json_ref(const json_ref&) = delete;
+    json_ref& operator=(const json_ref&) = delete;
+    json_ref& operator=(json_ref&&) = delete;
+    ~json_ref() = default;
+
+    value_type moved_or_copied() const
+    {
+        if (value_ref == nullptr)
+        {
+            return std::move(owned_value);
+        }
+        return *value_ref;
+    }
+
+    value_type const& operator*() const
+    {
+        return value_ref ? *value_ref : owned_value;
+    }
+
+    value_type const* operator->() const
+    {
+        return &** this;
+    }
+
+  private:
+    mutable value_type owned_value = nullptr;
+    value_type const* value_ref = nullptr;
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/macro_scope.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/macro_scope.h
new file mode 100644
index 0000000..80f18b3
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/macro_scope.h
@@ -0,0 +1,468 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <utility> // declval, pair
+#include <wpi/detail/meta/detected.h>
+#include <wpi/thirdparty/hedley/hedley.h>
+
+// This file contains all internal macro definitions (except those affecting ABI)
+// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
+
+#include <wpi/detail/abi_macros.h>
+
+// exclude unsupported compilers
+#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
+    #if defined(__clang__)
+        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
+            #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
+        #endif
+    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
+        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800
+            #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
+        #endif
+    #endif
+#endif
+
+// C++ language standard detection
+// if the user manually specified the used c++ version this is skipped
+#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
+    #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
+        #define JSON_HAS_CPP_20
+        #define JSON_HAS_CPP_17
+        #define JSON_HAS_CPP_14
+    #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
+        #define JSON_HAS_CPP_17
+        #define JSON_HAS_CPP_14
+    #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
+        #define JSON_HAS_CPP_14
+    #endif
+    // the cpp 11 flag is always specified because it is the minimal required version
+    #define JSON_HAS_CPP_11
+#endif
+
+#ifdef __has_include
+    #if __has_include(<version>)
+        #include <version>
+    #endif
+#endif
+
+#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
+    #ifdef JSON_HAS_CPP_17
+        #if defined(__cpp_lib_filesystem)
+            #define JSON_HAS_FILESYSTEM 1
+        #elif defined(__cpp_lib_experimental_filesystem)
+            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+        #elif !defined(__has_include)
+            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+        #elif __has_include(<filesystem>)
+            #define JSON_HAS_FILESYSTEM 1
+        #elif __has_include(<experimental/filesystem>)
+            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+        #endif
+
+        // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/
+        #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support
+        #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support
+        #if defined(__clang_major__) && __clang_major__ < 7
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support
+        #if defined(_MSC_VER) && _MSC_VER < 1914
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before iOS 13
+        #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before macOS Catalina
+        #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+    #endif
+#endif
+
+#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+    #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0
+#endif
+
+#ifndef JSON_HAS_FILESYSTEM
+    #define JSON_HAS_FILESYSTEM 0
+#endif
+
+#ifndef JSON_HAS_THREE_WAY_COMPARISON
+    #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
+        && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
+        #define JSON_HAS_THREE_WAY_COMPARISON 1
+    #else
+        #define JSON_HAS_THREE_WAY_COMPARISON 0
+    #endif
+#endif
+
+#ifndef JSON_HAS_RANGES
+    // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
+    #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
+        #define JSON_HAS_RANGES 0
+    #elif defined(__cpp_lib_ranges)
+        #define JSON_HAS_RANGES 1
+    #else
+        #define JSON_HAS_RANGES 0
+    #endif
+#endif
+
+#ifdef JSON_HAS_CPP_17
+    #define JSON_INLINE_VARIABLE inline
+#else
+    #define JSON_INLINE_VARIABLE
+#endif
+
+#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
+    #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
+#else
+    #define JSON_NO_UNIQUE_ADDRESS
+#endif
+
+// disable documentation warnings on clang
+#if defined(__clang__)
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wdocumentation"
+    #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
+#endif
+
+// allow disabling exceptions
+#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
+    #define JSON_THROW(exception) throw exception
+    #define JSON_TRY try
+    #define JSON_CATCH(exception) catch(exception)
+    #define JSON_INTERNAL_CATCH(exception) catch(exception)
+#else
+    #include <cstdlib>
+    #define JSON_THROW(exception) std::abort()
+    #define JSON_TRY if(true)
+    #define JSON_CATCH(exception) if(false)
+    #define JSON_INTERNAL_CATCH(exception) if(false)
+#endif
+
+// override exception macros
+#if defined(JSON_THROW_USER)
+    #undef JSON_THROW
+    #define JSON_THROW JSON_THROW_USER
+#endif
+#if defined(JSON_TRY_USER)
+    #undef JSON_TRY
+    #define JSON_TRY JSON_TRY_USER
+#endif
+#if defined(JSON_CATCH_USER)
+    #undef JSON_CATCH
+    #define JSON_CATCH JSON_CATCH_USER
+    #undef JSON_INTERNAL_CATCH
+    #define JSON_INTERNAL_CATCH JSON_CATCH_USER
+#endif
+#if defined(JSON_INTERNAL_CATCH_USER)
+    #undef JSON_INTERNAL_CATCH
+    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
+#endif
+
+// allow overriding assert
+#if !defined(JSON_ASSERT)
+    #include <cassert> // assert
+    #define JSON_ASSERT(x) assert(x)
+#endif
+
+// allow to access some private functions (needed by the test suite)
+#if defined(JSON_TESTS_PRIVATE)
+    #define JSON_PRIVATE_UNLESS_TESTED public
+#else
+    #define JSON_PRIVATE_UNLESS_TESTED private
+#endif
+
+/*!
+@brief macro to briefly define a mapping between an enum and JSON
+@def WPI_JSON_SERIALIZE_ENUM
+@since version 3.4.0
+*/
+#define WPI_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                            \
+    template<typename BasicJsonType>                                                            \
+    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                   \
+    {                                                                                           \
+        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");          \
+        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \
+        auto it = std::find_if(std::begin(m), std::end(m),                                      \
+                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool  \
+        {                                                                                       \
+            return ej_pair.first == e;                                                          \
+        });                                                                                     \
+        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                 \
+    }                                                                                           \
+    template<typename BasicJsonType>                                                            \
+    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                 \
+    {                                                                                           \
+        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");          \
+        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \
+        auto it = std::find_if(std::begin(m), std::end(m),                                      \
+                               [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
+        {                                                                                       \
+            return ej_pair.second == j;                                                         \
+        });                                                                                     \
+        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                  \
+    }
+
+// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
+// may be removed in the future once the class is split.
+
+#define WPI_BASIC_JSON_TPL_DECLARATION                                \
+    template<template<typename, typename, typename...> class ObjectType,   \
+             template<typename, typename...> class ArrayType,              \
+             class StringType, class BooleanType, class NumberIntegerType, \
+             class NumberUnsignedType, class NumberFloatType,              \
+             template<typename> class AllocatorType,                       \
+             template<typename, typename = void> class JSONSerializer,     \
+             class BinaryType>
+
+#define WPI_BASIC_JSON_TPL                                            \
+    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \
+    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \
+    AllocatorType, JSONSerializer, BinaryType>
+
+// Macros to simplify conversion from/to types
+
+#define WPI_JSON_EXPAND( x ) x
+#define WPI_JSON_GET_MACRO(_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, _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, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
+#define WPI_JSON_PASTE(...) WPI_JSON_EXPAND(WPI_JSON_GET_MACRO(__VA_ARGS__, \
+        WPI_JSON_PASTE64, \
+        WPI_JSON_PASTE63, \
+        WPI_JSON_PASTE62, \
+        WPI_JSON_PASTE61, \
+        WPI_JSON_PASTE60, \
+        WPI_JSON_PASTE59, \
+        WPI_JSON_PASTE58, \
+        WPI_JSON_PASTE57, \
+        WPI_JSON_PASTE56, \
+        WPI_JSON_PASTE55, \
+        WPI_JSON_PASTE54, \
+        WPI_JSON_PASTE53, \
+        WPI_JSON_PASTE52, \
+        WPI_JSON_PASTE51, \
+        WPI_JSON_PASTE50, \
+        WPI_JSON_PASTE49, \
+        WPI_JSON_PASTE48, \
+        WPI_JSON_PASTE47, \
+        WPI_JSON_PASTE46, \
+        WPI_JSON_PASTE45, \
+        WPI_JSON_PASTE44, \
+        WPI_JSON_PASTE43, \
+        WPI_JSON_PASTE42, \
+        WPI_JSON_PASTE41, \
+        WPI_JSON_PASTE40, \
+        WPI_JSON_PASTE39, \
+        WPI_JSON_PASTE38, \
+        WPI_JSON_PASTE37, \
+        WPI_JSON_PASTE36, \
+        WPI_JSON_PASTE35, \
+        WPI_JSON_PASTE34, \
+        WPI_JSON_PASTE33, \
+        WPI_JSON_PASTE32, \
+        WPI_JSON_PASTE31, \
+        WPI_JSON_PASTE30, \
+        WPI_JSON_PASTE29, \
+        WPI_JSON_PASTE28, \
+        WPI_JSON_PASTE27, \
+        WPI_JSON_PASTE26, \
+        WPI_JSON_PASTE25, \
+        WPI_JSON_PASTE24, \
+        WPI_JSON_PASTE23, \
+        WPI_JSON_PASTE22, \
+        WPI_JSON_PASTE21, \
+        WPI_JSON_PASTE20, \
+        WPI_JSON_PASTE19, \
+        WPI_JSON_PASTE18, \
+        WPI_JSON_PASTE17, \
+        WPI_JSON_PASTE16, \
+        WPI_JSON_PASTE15, \
+        WPI_JSON_PASTE14, \
+        WPI_JSON_PASTE13, \
+        WPI_JSON_PASTE12, \
+        WPI_JSON_PASTE11, \
+        WPI_JSON_PASTE10, \
+        WPI_JSON_PASTE9, \
+        WPI_JSON_PASTE8, \
+        WPI_JSON_PASTE7, \
+        WPI_JSON_PASTE6, \
+        WPI_JSON_PASTE5, \
+        WPI_JSON_PASTE4, \
+        WPI_JSON_PASTE3, \
+        WPI_JSON_PASTE2, \
+        WPI_JSON_PASTE1)(__VA_ARGS__))
+#define WPI_JSON_PASTE2(func, v1) func(v1)
+#define WPI_JSON_PASTE3(func, v1, v2) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE2(func, v2)
+#define WPI_JSON_PASTE4(func, v1, v2, v3) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE3(func, v2, v3)
+#define WPI_JSON_PASTE5(func, v1, v2, v3, v4) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE4(func, v2, v3, v4)
+#define WPI_JSON_PASTE6(func, v1, v2, v3, v4, v5) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE5(func, v2, v3, v4, v5)
+#define WPI_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE6(func, v2, v3, v4, v5, v6)
+#define WPI_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
+#define WPI_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
+#define WPI_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)
+#define WPI_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
+#define WPI_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
+#define WPI_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)
+#define WPI_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)
+#define WPI_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)
+#define WPI_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)
+#define WPI_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)
+#define WPI_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)
+#define WPI_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)
+#define WPI_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)
+#define WPI_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
+#define WPI_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)
+#define WPI_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)
+#define WPI_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)
+#define WPI_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)
+#define WPI_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)
+#define WPI_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)
+#define WPI_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)
+#define WPI_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)
+#define WPI_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)
+#define WPI_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)
+#define WPI_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)
+#define WPI_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)
+#define WPI_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)
+#define WPI_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)
+#define WPI_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)
+#define WPI_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)
+#define WPI_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)
+#define WPI_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)
+#define WPI_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)
+#define WPI_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)
+#define WPI_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)
+#define WPI_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)
+#define WPI_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)
+#define WPI_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)
+#define WPI_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)
+#define WPI_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)
+#define WPI_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)
+#define WPI_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)
+#define WPI_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)
+#define WPI_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)
+#define WPI_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)
+#define WPI_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)
+#define WPI_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)
+#define WPI_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)
+#define WPI_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)
+#define WPI_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)
+#define WPI_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)
+#define WPI_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)
+#define WPI_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)
+#define WPI_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)
+#define WPI_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)
+#define WPI_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)
+#define WPI_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) WPI_JSON_PASTE2(func, v1) WPI_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)
+
+#define WPI_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
+#define WPI_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
+#define WPI_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);
+
+/*!
+@brief macro
+@def WPI_DEFINE_TYPE_INTRUSIVE
+@since version 3.9.0
+*/
+#define WPI_DEFINE_TYPE_INTRUSIVE(Type, ...)  \
+    friend void to_json(wpi::json& nlohmann_json_j, const Type& nlohmann_json_t) { WPI_JSON_EXPAND(WPI_JSON_PASTE(WPI_JSON_TO, __VA_ARGS__)) } \
+    friend void from_json(const wpi::json& nlohmann_json_j, Type& nlohmann_json_t) { WPI_JSON_EXPAND(WPI_JSON_PASTE(WPI_JSON_FROM, __VA_ARGS__)) }
+
+#define WPI_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...)  \
+    friend void to_json(wpi::json& nlohmann_json_j, const Type& nlohmann_json_t) { WPI_JSON_EXPAND(WPI_JSON_PASTE(WPI_JSON_TO, __VA_ARGS__)) } \
+    friend void from_json(const wpi::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; WPI_JSON_EXPAND(WPI_JSON_PASTE(WPI_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def WPI_DEFINE_TYPE_NON_INTRUSIVE
+@since version 3.9.0
+*/
+#define WPI_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \
+    inline void to_json(wpi::json& nlohmann_json_j, const Type& nlohmann_json_t) { WPI_JSON_EXPAND(WPI_JSON_PASTE(WPI_JSON_TO, __VA_ARGS__)) } \
+    inline void from_json(const wpi::json& nlohmann_json_j, Type& nlohmann_json_t) { WPI_JSON_EXPAND(WPI_JSON_PASTE(WPI_JSON_FROM, __VA_ARGS__)) }
+
+#define WPI_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...)  \
+    inline void to_json(wpi::json& nlohmann_json_j, const Type& nlohmann_json_t) { WPI_JSON_EXPAND(WPI_JSON_PASTE(WPI_JSON_TO, __VA_ARGS__)) } \
+    inline void from_json(const wpi::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; WPI_JSON_EXPAND(WPI_JSON_PASTE(WPI_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+
+// inspired from https://stackoverflow.com/a/26745591
+// allows to call any std function as if (e.g. with begin):
+// using std::begin; begin(x);
+//
+// it allows using the detected idiom to retrieve the return type
+// of such an expression
+#define WPI_CAN_CALL_STD_FUNC_IMPL(std_name)                                 \
+    namespace detail {                                                            \
+    using std::std_name;                                                          \
+    \
+    template<typename... T>                                                       \
+    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \
+    }                                                                             \
+    \
+    namespace detail2 {                                                           \
+    struct std_name##_tag                                                         \
+    {                                                                             \
+    };                                                                            \
+    \
+    template<typename... T>                                                       \
+    std_name##_tag std_name(T&&...);                                              \
+    \
+    template<typename... T>                                                       \
+    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \
+    \
+    template<typename... T>                                                       \
+    struct would_call_std_##std_name                                              \
+    {                                                                             \
+        static constexpr auto const value = ::wpi::detail::                  \
+                                            is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \
+    };                                                                            \
+    } /* namespace detail2 */ \
+    \
+    template<typename... T>                                                       \
+    struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...>   \
+    {                                                                             \
+    }
+
+#ifndef JSON_USE_IMPLICIT_CONVERSIONS
+    #define JSON_USE_IMPLICIT_CONVERSIONS 1
+#endif
+
+#if JSON_USE_IMPLICIT_CONVERSIONS
+    #define JSON_EXPLICIT
+#else
+    #define JSON_EXPLICIT explicit
+#endif
+
+#ifndef JSON_DISABLE_ENUM_SERIALIZATION
+    #define JSON_DISABLE_ENUM_SERIALIZATION 0
+#endif
+
+#ifndef JSON_USE_GLOBAL_UDLS
+    #define JSON_USE_GLOBAL_UDLS 1
+#endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/macro_unscope.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/macro_unscope.h
new file mode 100644
index 0000000..00edbf7
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/macro_unscope.h
@@ -0,0 +1,44 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+// restore clang diagnostic settings
+#if defined(__clang__)
+    #pragma clang diagnostic pop
+#endif
+
+// clean up
+#undef JSON_ASSERT
+#undef JSON_INTERNAL_CATCH
+#undef JSON_THROW
+#undef JSON_PRIVATE_UNLESS_TESTED
+#undef WPI_BASIC_JSON_TPL_DECLARATION
+#undef WPI_BASIC_JSON_TPL
+#undef JSON_EXPLICIT
+#undef WPI_CAN_CALL_STD_FUNC_IMPL
+#undef JSON_INLINE_VARIABLE
+#undef JSON_NO_UNIQUE_ADDRESS
+#undef JSON_DISABLE_ENUM_SERIALIZATION
+#undef JSON_USE_GLOBAL_UDLS
+
+#ifndef JSON_TEST_KEEP_MACROS
+    #undef JSON_CATCH
+    #undef JSON_TRY
+    #undef JSON_HAS_CPP_11
+    #undef JSON_HAS_CPP_14
+    #undef JSON_HAS_CPP_17
+    #undef JSON_HAS_CPP_20
+    #undef JSON_HAS_FILESYSTEM
+    #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+    #undef JSON_HAS_THREE_WAY_COMPARISON
+    #undef JSON_HAS_RANGES
+    #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+#endif
+
+#include <wpi/thirdparty/hedley/hedley_undef.h>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/call_std/begin.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/call_std/begin.h
new file mode 100644
index 0000000..7ef8b07
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/call_std/begin.h
@@ -0,0 +1,17 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <wpi/detail/macro_scope.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+
+WPI_CAN_CALL_STD_FUNC_IMPL(begin);
+
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/call_std/end.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/call_std/end.h
new file mode 100644
index 0000000..d727163
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/call_std/end.h
@@ -0,0 +1,17 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <wpi/detail/macro_scope.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+
+WPI_CAN_CALL_STD_FUNC_IMPL(end);
+
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/cpp_future.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/cpp_future.h
new file mode 100644
index 0000000..d10f0cd
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/cpp_future.h
@@ -0,0 +1,171 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-FileCopyrightText: 2018 The Abseil Authors
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
+#include <utility> // index_sequence, make_index_sequence, index_sequence_for
+
+#include <wpi/detail/macro_scope.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename T>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+#ifdef JSON_HAS_CPP_14
+
+// the following utilities are natively available in C++14
+using std::enable_if_t;
+using std::index_sequence;
+using std::make_index_sequence;
+using std::index_sequence_for;
+
+#else
+
+// alias templates to reduce boilerplate
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h
+// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.
+
+//// START OF CODE FROM GOOGLE ABSEIL
+
+// integer_sequence
+//
+// Class template representing a compile-time integer sequence. An instantiation
+// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
+// type through its template arguments (which is a common need when
+// working with C++11 variadic templates). `absl::integer_sequence` is designed
+// to be a drop-in replacement for C++14's `std::integer_sequence`.
+//
+// Example:
+//
+//   template< class T, T... Ints >
+//   void user_function(integer_sequence<T, Ints...>);
+//
+//   int main()
+//   {
+//     // user_function's `T` will be deduced to `int` and `Ints...`
+//     // will be deduced to `0, 1, 2, 3, 4`.
+//     user_function(make_integer_sequence<int, 5>());
+//   }
+template <typename T, T... Ints>
+struct integer_sequence
+{
+    using value_type = T;
+    static constexpr std::size_t size() noexcept
+    {
+        return sizeof...(Ints);
+    }
+};
+
+// index_sequence
+//
+// A helper template for an `integer_sequence` of `size_t`,
+// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
+// `std::index_sequence`.
+template <size_t... Ints>
+using index_sequence = integer_sequence<size_t, Ints...>;
+
+namespace utility_internal
+{
+
+template <typename Seq, size_t SeqSize, size_t Rem>
+struct Extend;
+
+// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 0>
+{
+    using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;
+};
+
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 1>
+{
+    using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;
+};
+
+// Recursion helper for 'make_integer_sequence<T, N>'.
+// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
+template <typename T, size_t N>
+struct Gen
+{
+    using type =
+        typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;
+};
+
+template <typename T>
+struct Gen<T, 0>
+{
+    using type = integer_sequence<T>;
+};
+
+}  // namespace utility_internal
+
+// Compile-time sequences of integers
+
+// make_integer_sequence
+//
+// This template alias is equivalent to
+// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
+// replacement for C++14's `std::make_integer_sequence`.
+template <typename T, T N>
+using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
+
+// make_index_sequence
+//
+// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
+// and is designed to be a drop-in replacement for C++14's
+// `std::make_index_sequence`.
+template <size_t N>
+using make_index_sequence = make_integer_sequence<size_t, N>;
+
+// index_sequence_for
+//
+// Converts a typename pack into an index sequence of the same length, and
+// is designed to be a drop-in replacement for C++14's
+// `std::index_sequence_for()`
+template <typename... Ts>
+using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+
+//// END OF CODE FROM GOOGLE ABSEIL
+
+#endif
+
+// dispatch utility (taken from ranges-v3)
+template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
+template<> struct priority_tag<0> {};
+
+// taken from ranges-v3
+template<typename T>
+struct static_const
+{
+    static JSON_INLINE_VARIABLE constexpr T value{};
+};
+
+#ifndef JSON_HAS_CPP_17
+    template<typename T>
+    constexpr T static_const<T>::value;
+#endif
+
+template<typename T, typename... Args>
+inline constexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)
+{
+    return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};
+}
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/detected.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/detected.h
new file mode 100644
index 0000000..7a667f3
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/detected.h
@@ -0,0 +1,70 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <type_traits>
+
+#include <wpi/detail/meta/void_t.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// https://en.cppreference.com/w/cpp/experimental/is_detected
+struct nonesuch
+{
+    nonesuch() = delete;
+    ~nonesuch() = delete;
+    nonesuch(nonesuch const&) = delete;
+    nonesuch(nonesuch const&&) = delete;
+    void operator=(nonesuch const&) = delete;
+    void operator=(nonesuch&&) = delete;
+};
+
+template<class Default,
+         class AlwaysVoid,
+         template<class...> class Op,
+         class... Args>
+struct detector
+{
+    using value_t = std::false_type;
+    using type = Default;
+};
+
+template<class Default, template<class...> class Op, class... Args>
+struct detector<Default, void_t<Op<Args...>>, Op, Args...>
+{
+    using value_t = std::true_type;
+    using type = Op<Args...>;
+};
+
+template<template<class...> class Op, class... Args>
+using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
+
+template<template<class...> class Op, class... Args>
+struct is_detected_lazy : is_detected<Op, Args...> { };
+
+template<template<class...> class Op, class... Args>
+using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
+
+template<class Default, template<class...> class Op, class... Args>
+using detected_or = detector<Default, void, Op, Args...>;
+
+template<class Default, template<class...> class Op, class... Args>
+using detected_or_t = typename detected_or<Default, Op, Args...>::type;
+
+template<class Expected, template<class...> class Op, class... Args>
+using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
+
+template<class To, template<class...> class Op, class... Args>
+using is_detected_convertible =
+    std::is_convertible<detected_t<Op, Args...>, To>;
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/identity_tag.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/identity_tag.h
new file mode 100644
index 0000000..a062264
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/identity_tag.h
@@ -0,0 +1,21 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <wpi/detail/abi_macros.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// dispatching helper struct
+template <class T> struct identity_tag {};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/is_sax.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/is_sax.h
new file mode 100644
index 0000000..689bf33
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/is_sax.h
@@ -0,0 +1,159 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstdint> // size_t
+#include <utility> // declval
+#include <string> // string
+
+#include <wpi/detail/abi_macros.h>
+#include <wpi/detail/meta/detected.h>
+#include <wpi/detail/meta/type_traits.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename T>
+using null_function_t = decltype(std::declval<T&>().null());
+
+template<typename T>
+using boolean_function_t =
+    decltype(std::declval<T&>().boolean(std::declval<bool>()));
+
+template<typename T, typename Integer>
+using number_integer_function_t =
+    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));
+
+template<typename T, typename Unsigned>
+using number_unsigned_function_t =
+    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));
+
+template<typename T, typename Float, typename String>
+using number_float_function_t = decltype(std::declval<T&>().number_float(
+                                    std::declval<Float>(), std::declval<const String&>()));
+
+template<typename T, typename String>
+using string_function_t =
+    decltype(std::declval<T&>().string(std::declval<String&>()));
+
+template<typename T, typename Binary>
+using binary_function_t =
+    decltype(std::declval<T&>().binary(std::declval<Binary&>()));
+
+template<typename T>
+using start_object_function_t =
+    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));
+
+template<typename T, typename String>
+using key_function_t =
+    decltype(std::declval<T&>().key(std::declval<String&>()));
+
+template<typename T>
+using end_object_function_t = decltype(std::declval<T&>().end_object());
+
+template<typename T>
+using start_array_function_t =
+    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));
+
+template<typename T>
+using end_array_function_t = decltype(std::declval<T&>().end_array());
+
+template<typename T, typename Exception>
+using parse_error_function_t = decltype(std::declval<T&>().parse_error(
+        std::declval<std::size_t>(), std::declval<const std::string&>(),
+        std::declval<const Exception&>()));
+
+template<typename SAX, typename BasicJsonType>
+struct is_sax
+{
+  private:
+    static_assert(is_basic_json<BasicJsonType>::value,
+                  "BasicJsonType must be of type basic_json<...>");
+
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using exception_t = typename BasicJsonType::exception;
+
+  public:
+    static constexpr bool value =
+        is_detected_exact<bool, null_function_t, SAX>::value &&
+        is_detected_exact<bool, boolean_function_t, SAX>::value &&
+        is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&
+        is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&
+        is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&
+        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&
+        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&
+        is_detected_exact<bool, start_object_function_t, SAX>::value &&
+        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&
+        is_detected_exact<bool, end_object_function_t, SAX>::value &&
+        is_detected_exact<bool, start_array_function_t, SAX>::value &&
+        is_detected_exact<bool, end_array_function_t, SAX>::value &&
+        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
+};
+
+template<typename SAX, typename BasicJsonType>
+struct is_sax_static_asserts
+{
+  private:
+    static_assert(is_basic_json<BasicJsonType>::value,
+                  "BasicJsonType must be of type basic_json<...>");
+
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using exception_t = typename BasicJsonType::exception;
+
+  public:
+    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,
+                  "Missing/invalid function: bool null()");
+    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
+                  "Missing/invalid function: bool boolean(bool)");
+    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
+                  "Missing/invalid function: bool boolean(bool)");
+    static_assert(
+        is_detected_exact<bool, number_integer_function_t, SAX,
+        number_integer_t>::value,
+        "Missing/invalid function: bool number_integer(number_integer_t)");
+    static_assert(
+        is_detected_exact<bool, number_unsigned_function_t, SAX,
+        number_unsigned_t>::value,
+        "Missing/invalid function: bool number_unsigned(number_unsigned_t)");
+    static_assert(is_detected_exact<bool, number_float_function_t, SAX,
+                  number_float_t, string_t>::value,
+                  "Missing/invalid function: bool number_float(number_float_t, const string_t&)");
+    static_assert(
+        is_detected_exact<bool, string_function_t, SAX, string_t>::value,
+        "Missing/invalid function: bool string(string_t&)");
+    static_assert(
+        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,
+        "Missing/invalid function: bool binary(binary_t&)");
+    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,
+                  "Missing/invalid function: bool start_object(std::size_t)");
+    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,
+                  "Missing/invalid function: bool key(string_t&)");
+    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,
+                  "Missing/invalid function: bool end_object()");
+    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,
+                  "Missing/invalid function: bool start_array(std::size_t)");
+    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,
+                  "Missing/invalid function: bool end_array()");
+    static_assert(
+        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,
+        "Missing/invalid function: bool parse_error(std::size_t, const "
+        "std::string&, const exception&)");
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/std_fs.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/std_fs.h
new file mode 100644
index 0000000..c012d27
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/std_fs.h
@@ -0,0 +1,29 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <wpi/detail/macro_scope.h>
+
+#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
+#include <experimental/filesystem>
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+namespace std_fs = std::experimental::filesystem;
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
+#elif JSON_HAS_FILESYSTEM
+#include <filesystem>
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+namespace std_fs = std::filesystem;
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
+#endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/type_traits.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/type_traits.h
new file mode 100644
index 0000000..254f099
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/type_traits.h
@@ -0,0 +1,740 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <limits> // numeric_limits
+#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
+#include <utility> // declval
+#include <tuple> // tuple
+
+#include <wpi/detail/iterators/iterator_traits.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/meta/call_std/begin.h>
+#include <wpi/detail/meta/call_std/end.h>
+#include <wpi/detail/meta/cpp_future.h>
+#include <wpi/detail/meta/detected.h>
+#include <wpi/json_fwd.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+/*!
+@brief detail namespace with internal helper functions
+
+This namespace collects functions that should not be exposed,
+implementations of some @ref basic_json methods, and meta-programming helpers.
+
+@since version 2.1.0
+*/
+namespace detail
+{
+
+/////////////
+// helpers //
+/////////////
+
+// Note to maintainers:
+//
+// Every trait in this file expects a non CV-qualified type.
+// The only exceptions are in the 'aliases for detected' section
+// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
+//
+// In this case, T has to be properly CV-qualified to constraint the function arguments
+// (e.g. to_json(BasicJsonType&, const T&))
+
+template<typename> struct is_basic_json : std::false_type {};
+
+WPI_BASIC_JSON_TPL_DECLARATION
+struct is_basic_json<WPI_BASIC_JSON_TPL> : std::true_type {};
+
+// used by exceptions create() member functions
+// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t
+// false_type otherwise
+template<typename BasicJsonContext>
+struct is_basic_json_context :
+    std::integral_constant < bool,
+    is_basic_json<typename std::remove_cv<typename std::remove_pointer<BasicJsonContext>::type>::type>::value
+    || std::is_same<BasicJsonContext, std::nullptr_t>::value >
+{};
+
+//////////////////////
+// json_ref helpers //
+//////////////////////
+
+template<typename>
+class json_ref;
+
+template<typename>
+struct is_json_ref : std::false_type {};
+
+template<typename T>
+struct is_json_ref<json_ref<T>> : std::true_type {};
+
+//////////////////////////
+// aliases for detected //
+//////////////////////////
+
+template<typename T>
+using mapped_type_t = typename T::mapped_type;
+
+template<typename T>
+using key_type_t = typename T::key_type;
+
+template<typename T>
+using value_type_t = typename T::value_type;
+
+template<typename T>
+using difference_type_t = typename T::difference_type;
+
+template<typename T>
+using pointer_t = typename T::pointer;
+
+template<typename T>
+using reference_t = typename T::reference;
+
+template<typename T>
+using iterator_category_t = typename T::iterator_category;
+
+template<typename T, typename... Args>
+using to_json_function = decltype(T::to_json(std::declval<Args>()...));
+
+template<typename T, typename... Args>
+using from_json_function = decltype(T::from_json(std::declval<Args>()...));
+
+template<typename T, typename U>
+using get_template_function = decltype(std::declval<T>().template get<U>());
+
+// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
+template<typename BasicJsonType, typename T, typename = void>
+struct has_from_json : std::false_type {};
+
+// trait checking if j.get<T> is valid
+// use this trait instead of std::is_constructible or std::is_convertible,
+// both rely on, or make use of implicit conversions, and thus fail when T
+// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
+template <typename BasicJsonType, typename T>
+struct is_getable
+{
+    static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
+};
+
+template<typename BasicJsonType, typename T>
+struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+    using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+    static constexpr bool value =
+        is_detected_exact<void, from_json_function, serializer,
+        const BasicJsonType&, T&>::value;
+};
+
+// This trait checks if JSONSerializer<T>::from_json(json const&) exists
+// this overload is used for non-default-constructible user-defined-types
+template<typename BasicJsonType, typename T, typename = void>
+struct has_non_default_from_json : std::false_type {};
+
+template<typename BasicJsonType, typename T>
+struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+    using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+    static constexpr bool value =
+        is_detected_exact<T, from_json_function, serializer,
+        const BasicJsonType&>::value;
+};
+
+// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
+// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
+template<typename BasicJsonType, typename T, typename = void>
+struct has_to_json : std::false_type {};
+
+template<typename BasicJsonType, typename T>
+struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+    using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+    static constexpr bool value =
+        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,
+        T>::value;
+};
+
+template<typename T>
+using detect_key_compare = typename T::key_compare;
+
+template<typename T>
+struct has_key_compare : std::integral_constant<bool, is_detected<detect_key_compare, T>::value> {};
+
+// obtains the actual object key comparator
+template<typename BasicJsonType>
+struct actual_object_comparator
+{
+    using object_t = typename BasicJsonType::object_t;
+    using object_comparator_t = typename BasicJsonType::default_object_comparator_t;
+    using type = typename std::conditional < has_key_compare<object_t>::value,
+          typename object_t::key_compare, object_comparator_t>::type;
+};
+
+template<typename BasicJsonType>
+using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;
+
+///////////////////
+// is_ functions //
+///////////////////
+
+// https://en.cppreference.com/w/cpp/types/conjunction
+template<class...> struct conjunction : std::true_type { };
+template<class B> struct conjunction<B> : B { };
+template<class B, class... Bn>
+struct conjunction<B, Bn...>
+: std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};
+
+// https://en.cppreference.com/w/cpp/types/negation
+template<class B> struct negation : std::integral_constant < bool, !B::value > { };
+
+// Reimplementation of is_constructible and is_default_constructible, due to them being broken for
+// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
+// This causes compile errors in e.g. clang 3.5 or gcc 4.9.
+template <typename T>
+struct is_default_constructible : std::is_default_constructible<T> {};
+
+template <typename T1, typename T2>
+struct is_default_constructible<std::pair<T1, T2>>
+            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
+
+template <typename T1, typename T2>
+struct is_default_constructible<const std::pair<T1, T2>>
+            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
+
+template <typename... Ts>
+struct is_default_constructible<std::tuple<Ts...>>
+            : conjunction<is_default_constructible<Ts>...> {};
+
+template <typename... Ts>
+struct is_default_constructible<const std::tuple<Ts...>>
+            : conjunction<is_default_constructible<Ts>...> {};
+
+
+template <typename T, typename... Args>
+struct is_constructible : std::is_constructible<T, Args...> {};
+
+template <typename T1, typename T2>
+struct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};
+
+template <typename T1, typename T2>
+struct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};
+
+template <typename... Ts>
+struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};
+
+template <typename... Ts>
+struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};
+
+
+template<typename T, typename = void>
+struct is_iterator_traits : std::false_type {};
+
+template<typename T>
+struct is_iterator_traits<iterator_traits<T>>
+{
+  private:
+    using traits = iterator_traits<T>;
+
+  public:
+    static constexpr auto value =
+        is_detected<value_type_t, traits>::value &&
+        is_detected<difference_type_t, traits>::value &&
+        is_detected<pointer_t, traits>::value &&
+        is_detected<iterator_category_t, traits>::value &&
+        is_detected<reference_t, traits>::value;
+};
+
+template<typename T>
+struct is_range
+{
+  private:
+    using t_ref = typename std::add_lvalue_reference<T>::type;
+
+    using iterator = detected_t<result_of_begin, t_ref>;
+    using sentinel = detected_t<result_of_end, t_ref>;
+
+    // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator
+    // and https://en.cppreference.com/w/cpp/iterator/sentinel_for
+    // but reimplementing these would be too much work, as a lot of other concepts are used underneath
+    static constexpr auto is_iterator_begin =
+        is_iterator_traits<iterator_traits<iterator>>::value;
+
+  public:
+    static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;
+};
+
+template<typename R>
+using iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>;
+
+template<typename T>
+using range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;
+
+// The following implementation of is_complete_type is taken from
+// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/
+// and is written by Xiang Fan who agreed to using it in this library.
+
+template<typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template<typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+         typename = void>
+struct is_compatible_object_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleObjectType>
+struct is_compatible_object_type_impl <
+    BasicJsonType, CompatibleObjectType,
+    enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&
+    is_detected<key_type_t, CompatibleObjectType>::value >>
+{
+    using object_t = typename BasicJsonType::object_t;
+
+    // macOS's is_constructible does not play well with nonesuch...
+    static constexpr bool value =
+        is_constructible<typename object_t::key_type,
+        typename CompatibleObjectType::key_type>::value &&
+        is_constructible<typename object_t::mapped_type,
+        typename CompatibleObjectType::mapped_type>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleObjectType>
+struct is_compatible_object_type
+    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
+
+template<typename BasicJsonType, typename ConstructibleObjectType,
+         typename = void>
+struct is_constructible_object_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename ConstructibleObjectType>
+struct is_constructible_object_type_impl <
+    BasicJsonType, ConstructibleObjectType,
+    enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&
+    is_detected<key_type_t, ConstructibleObjectType>::value >>
+{
+    using object_t = typename BasicJsonType::object_t;
+
+    static constexpr bool value =
+        (is_default_constructible<ConstructibleObjectType>::value &&
+         (std::is_move_assignable<ConstructibleObjectType>::value ||
+          std::is_copy_assignable<ConstructibleObjectType>::value) &&
+         (is_constructible<typename ConstructibleObjectType::key_type,
+          typename object_t::key_type>::value &&
+          std::is_same <
+          typename object_t::mapped_type,
+          typename ConstructibleObjectType::mapped_type >::value)) ||
+        (has_from_json<BasicJsonType,
+         typename ConstructibleObjectType::mapped_type>::value ||
+         has_non_default_from_json <
+         BasicJsonType,
+         typename ConstructibleObjectType::mapped_type >::value);
+};
+
+template<typename BasicJsonType, typename ConstructibleObjectType>
+struct is_constructible_object_type
+    : is_constructible_object_type_impl<BasicJsonType,
+      ConstructibleObjectType> {};
+
+template<typename BasicJsonType, typename CompatibleStringType>
+struct is_compatible_string_type
+{
+    static constexpr auto value =
+        is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
+};
+
+template<typename BasicJsonType, typename ConstructibleStringType>
+struct is_constructible_string_type
+{
+    // launder type through decltype() to fix compilation failure on ICPC
+#ifdef __INTEL_COMPILER
+    using laundered_type = decltype(std::declval<ConstructibleStringType>());
+#else
+    using laundered_type = ConstructibleStringType;
+#endif
+
+    static constexpr auto value =
+        conjunction <
+        is_constructible<laundered_type, typename BasicJsonType::string_t>,
+        is_detected_exact<typename BasicJsonType::string_t::value_type,
+        value_type_t, laundered_type >>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
+struct is_compatible_array_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+struct is_compatible_array_type_impl <
+    BasicJsonType, CompatibleArrayType,
+    enable_if_t <
+    is_detected<iterator_t, CompatibleArrayType>::value&&
+    is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&
+// special case for types like std::filesystem::path whose iterator's value_type are themselves
+// c.f. https://github.com/nlohmann/json/pull/3073
+    !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>
+{
+    static constexpr bool value =
+        is_constructible<BasicJsonType,
+        range_value_t<CompatibleArrayType>>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+struct is_compatible_array_type
+    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
+struct is_constructible_array_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type_impl <
+    BasicJsonType, ConstructibleArrayType,
+    enable_if_t<std::is_same<ConstructibleArrayType,
+    typename BasicJsonType::value_type>::value >>
+            : std::true_type {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type_impl <
+    BasicJsonType, ConstructibleArrayType,
+    enable_if_t < !std::is_same<ConstructibleArrayType,
+    typename BasicJsonType::value_type>::value&&
+    !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
+    is_default_constructible<ConstructibleArrayType>::value&&
+(std::is_move_assignable<ConstructibleArrayType>::value ||
+ std::is_copy_assignable<ConstructibleArrayType>::value)&&
+is_detected<iterator_t, ConstructibleArrayType>::value&&
+is_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&&
+is_detected<range_value_t, ConstructibleArrayType>::value&&
+// special case for types like std::filesystem::path whose iterator's value_type are themselves
+// c.f. https://github.com/nlohmann/json/pull/3073
+!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&&
+        is_complete_type <
+        detected_t<range_value_t, ConstructibleArrayType >>::value >>
+{
+    using value_type = range_value_t<ConstructibleArrayType>;
+
+    static constexpr bool value =
+        std::is_same<value_type,
+        typename BasicJsonType::array_t::value_type>::value ||
+        has_from_json<BasicJsonType,
+        value_type>::value ||
+        has_non_default_from_json <
+        BasicJsonType,
+        value_type >::value;
+};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type
+    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType,
+         typename = void>
+struct is_compatible_integer_type_impl : std::false_type {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type_impl <
+    RealIntegerType, CompatibleNumberIntegerType,
+    enable_if_t < std::is_integral<RealIntegerType>::value&&
+    std::is_integral<CompatibleNumberIntegerType>::value&&
+    !std::is_same<bool, CompatibleNumberIntegerType>::value >>
+{
+    // is there an assert somewhere on overflows?
+    using RealLimits = std::numeric_limits<RealIntegerType>;
+    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
+
+    static constexpr auto value =
+        is_constructible<RealIntegerType,
+        CompatibleNumberIntegerType>::value &&
+        CompatibleLimits::is_integer &&
+        RealLimits::is_signed == CompatibleLimits::is_signed;
+};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type
+    : is_compatible_integer_type_impl<RealIntegerType,
+      CompatibleNumberIntegerType> {};
+
+template<typename BasicJsonType, typename CompatibleType, typename = void>
+struct is_compatible_type_impl: std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type_impl <
+    BasicJsonType, CompatibleType,
+    enable_if_t<is_complete_type<CompatibleType>::value >>
+{
+    static constexpr bool value =
+        has_to_json<BasicJsonType, CompatibleType>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type
+    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};
+
+template<typename T1, typename T2>
+struct is_constructible_tuple : std::false_type {};
+
+template<typename T1, typename... Args>
+struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};
+
+template<typename BasicJsonType, typename T>
+struct is_json_iterator_of : std::false_type {};
+
+template<typename BasicJsonType>
+struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::iterator> : std::true_type {};
+
+template<typename BasicJsonType>
+struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::const_iterator> : std::true_type
+{};
+
+// checks if a given type T is a template specialization of Primary
+template<template <typename...> class Primary, typename T>
+struct is_specialization_of : std::false_type {};
+
+template<template <typename...> class Primary, typename... Args>
+struct is_specialization_of<Primary, Primary<Args...>> : std::true_type {};
+
+template<typename T>
+using is_json_pointer = is_specialization_of<::wpi::json_pointer, uncvref_t<T>>;
+
+// checks if A and B are comparable using Compare functor
+template<typename Compare, typename A, typename B, typename = void>
+struct is_comparable : std::false_type {};
+
+template<typename Compare, typename A, typename B>
+struct is_comparable<Compare, A, B, void_t<
+decltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>())),
+decltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))
+>> : std::true_type {};
+
+template<typename T>
+using detect_is_transparent = typename T::is_transparent;
+
+// type trait to check if KeyType can be used as object key (without a BasicJsonType)
+// see is_usable_as_basic_json_key_type below
+template<typename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
+         bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
+using is_usable_as_key_type = typename std::conditional <
+                              is_comparable<Comparator, ObjectKeyType, KeyTypeCVRef>::value
+                              && !(ExcludeObjectKeyType && std::is_same<KeyType,
+                                   ObjectKeyType>::value)
+                              && (!RequireTransparentComparator
+                                  || is_detected <detect_is_transparent, Comparator>::value)
+                              && !is_json_pointer<KeyType>::value,
+                              std::true_type,
+                              std::false_type >::type;
+
+// type trait to check if KeyType can be used as object key
+// true if:
+//   - KeyType is comparable with BasicJsonType::object_t::key_type
+//   - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type
+//   - the comparator is transparent or RequireTransparentComparator is false
+//   - KeyType is not a JSON iterator or json_pointer
+template<typename BasicJsonType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
+         bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
+using is_usable_as_basic_json_key_type = typename std::conditional <
+        is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
+        typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
+        RequireTransparentComparator, ExcludeObjectKeyType>::value
+        && !is_json_iterator_of<BasicJsonType, KeyType>::value,
+        std::true_type,
+        std::false_type >::type;
+
+template<typename ObjectType, typename KeyType>
+using detect_erase_with_key_type = decltype(std::declval<ObjectType&>().erase(std::declval<KeyType>()));
+
+// type trait to check if object_t has an erase() member functions accepting KeyType
+template<typename BasicJsonType, typename KeyType>
+using has_erase_with_key_type = typename std::conditional <
+                                is_detected <
+                                detect_erase_with_key_type,
+                                typename BasicJsonType::object_t, KeyType >::value,
+                                std::true_type,
+                                std::false_type >::type;
+
+// a naive helper to check if a type is an ordered_map (exploits the fact that
+// ordered_map inherits capacity() from std::vector)
+template <typename T>
+struct is_ordered_map
+{
+    using one = char;
+
+    struct two
+    {
+        char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+    };
+
+    template <typename C> static one test( decltype(&C::capacity) ) ;
+    template <typename C> static two test(...);
+
+    enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+};
+
+// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
+template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
+T conditional_static_cast(U value)
+{
+    return static_cast<T>(value);
+}
+
+template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
+T conditional_static_cast(U value)
+{
+    return value;
+}
+
+template<typename... Types>
+using all_integral = conjunction<std::is_integral<Types>...>;
+
+template<typename... Types>
+using all_signed = conjunction<std::is_signed<Types>...>;
+
+template<typename... Types>
+using all_unsigned = conjunction<std::is_unsigned<Types>...>;
+
+// there's a disjunction trait in another PR; replace when merged
+template<typename... Types>
+using same_sign = std::integral_constant < bool,
+      all_signed<Types...>::value || all_unsigned<Types...>::value >;
+
+template<typename OfType, typename T>
+using never_out_of_range = std::integral_constant < bool,
+      (std::is_signed<OfType>::value && (sizeof(T) < sizeof(OfType)))
+      || (same_sign<OfType, T>::value && sizeof(OfType) == sizeof(T)) >;
+
+template<typename OfType, typename T,
+         bool OfTypeSigned = std::is_signed<OfType>::value,
+         bool TSigned = std::is_signed<T>::value>
+struct value_in_range_of_impl2;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, false>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, false>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, true>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return val >= 0 && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+    }
+};
+
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, true>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) >= static_cast<CommonType>((std::numeric_limits<OfType>::min)())
+               && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+    }
+};
+
+template<typename OfType, typename T,
+         bool NeverOutOfRange = never_out_of_range<OfType, T>::value,
+         typename = detail::enable_if_t<all_integral<OfType, T>::value>>
+struct value_in_range_of_impl1;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, false>
+{
+    static constexpr bool test(T val)
+    {
+        return value_in_range_of_impl2<OfType, T>::test(val);
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, true>
+{
+    static constexpr bool test(T /*val*/)
+    {
+        return true;
+    }
+};
+
+template<typename OfType, typename T>
+inline constexpr bool value_in_range_of(T val)
+{
+    return value_in_range_of_impl1<OfType, T>::test(val);
+}
+
+template<bool Value>
+using bool_constant = std::integral_constant<bool, Value>;
+
+///////////////////////////////////////////////////////////////////////////////
+// is_c_string
+///////////////////////////////////////////////////////////////////////////////
+
+namespace impl
+{
+
+template<typename T>
+inline constexpr bool is_c_string()
+{
+    using TUnExt = typename std::remove_extent<T>::type;
+    using TUnCVExt = typename std::remove_cv<TUnExt>::type;
+    using TUnPtr = typename std::remove_pointer<T>::type;
+    using TUnCVPtr = typename std::remove_cv<TUnPtr>::type;
+    return
+        (std::is_array<T>::value && std::is_same<TUnCVExt, char>::value)
+        || (std::is_pointer<T>::value && std::is_same<TUnCVPtr, char>::value);
+}
+
+}  // namespace impl
+
+// checks whether T is a [cv] char */[cv] char[] C string
+template<typename T>
+struct is_c_string : bool_constant<impl::is_c_string<T>()> {};
+
+template<typename T>
+using is_c_string_uncvref = is_c_string<uncvref_t<T>>;
+
+///////////////////////////////////////////////////////////////////////////////
+// is_transparent
+///////////////////////////////////////////////////////////////////////////////
+
+namespace impl
+{
+
+template<typename T>
+inline constexpr bool is_transparent()
+{
+    return is_detected<detect_is_transparent, T>::value;
+}
+
+}  // namespace impl
+
+// checks whether T has a member named is_transparent
+template<typename T>
+struct is_transparent : bool_constant<impl::is_transparent<T>()> {};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/void_t.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/void_t.h
new file mode 100644
index 0000000..8198f93
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/meta/void_t.h
@@ -0,0 +1,24 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <wpi/detail/abi_macros.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename ...Ts> struct make_void
+{
+    using type = void;
+};
+template<typename ...Ts> using void_t = typename make_void<Ts...>::type;
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/output/binary_writer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/output/binary_writer.h
new file mode 100644
index 0000000..8e80abe
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/output/binary_writer.h
@@ -0,0 +1,1838 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <algorithm> // reverse
+#include <array> // array
+#include <map> // map
+#include <cmath> // isnan, isinf
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstring> // memcpy
+#include <limits> // numeric_limits
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+#include <wpi/detail/input/binary_reader.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/output/output_adapters.h>
+#include <wpi/detail/string_concat.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////
+// binary writer //
+///////////////////
+
+/*!
+@brief serialization to CBOR and MessagePack values
+*/
+template<typename BasicJsonType, typename CharType>
+class binary_writer
+{
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+
+  public:
+    /*!
+    @brief create a binary writer
+
+    @param[in] adapter  output adapter to write to
+    */
+    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))
+    {
+        JSON_ASSERT(oa);
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    @pre       j.type() == value_t::object
+    */
+    void write_bson(const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::object:
+            {
+                write_bson_object(*j.m_value.object);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::array:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j));
+            }
+        }
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    */
+    void write_cbor(const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::null:
+            {
+                oa->write_character(to_char_type(0xF6));
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                oa->write_character(j.m_value.boolean
+                                    ? to_char_type(0xF5)
+                                    : to_char_type(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<std::uint8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x18));
+                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x19));
+                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x1A));
+                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
+                    }
+                    else
+                    {
+                        oa->write_character(to_char_type(0x1B));
+                        write_number(static_cast<std::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<std::uint8_t>(0x20 + positive_number));
+                    }
+                    else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x38));
+                        write_number(static_cast<std::uint8_t>(positive_number));
+                    }
+                    else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x39));
+                        write_number(static_cast<std::uint16_t>(positive_number));
+                    }
+                    else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x3A));
+                        write_number(static_cast<std::uint32_t>(positive_number));
+                    }
+                    else
+                    {
+                        oa->write_character(to_char_type(0x3B));
+                        write_number(static_cast<std::uint64_t>(positive_number));
+                    }
+                }
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                if (j.m_value.number_unsigned <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x18));
+                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x19));
+                    write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x1A));
+                    write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));
+                }
+                else
+                {
+                    oa->write_character(to_char_type(0x1B));
+                    write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));
+                }
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                if (std::isnan(j.m_value.number_float))
+                {
+                    // NaN is 0xf97e00 in CBOR
+                    oa->write_character(to_char_type(0xF9));
+                    oa->write_character(to_char_type(0x7E));
+                    oa->write_character(to_char_type(0x00));
+                }
+                else if (std::isinf(j.m_value.number_float))
+                {
+                    // Infinity is 0xf97c00, -Infinity is 0xf9fc00
+                    oa->write_character(to_char_type(0xf9));
+                    oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));
+                    oa->write_character(to_char_type(0x00));
+                }
+                else
+                {
+                    write_compact_float(j.m_value.number_float, detail::input_format_t::cbor);
+                }
+                break;
+            }
+
+            case value_t::string:
+            {
+                // step 1: write control byte and the string length
+                const auto N = j.m_value.string->size();
+                if (N <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(0x60 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x78));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x79));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x7A));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x7B));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write the string
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+                    j.m_value.string->size());
+                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<std::uint8_t>(0x80 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x98));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x99));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x9A));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x9B));
+                    write_number(static_cast<std::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::binary:
+            {
+                if (j.m_value.binary->has_subtype())
+                {
+                    if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xd8));
+                        write_number(static_cast<std::uint8_t>(j.m_value.binary->subtype()));
+                    }
+                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xd9));
+                        write_number(static_cast<std::uint16_t>(j.m_value.binary->subtype()));
+                    }
+                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xda));
+                        write_number(static_cast<std::uint32_t>(j.m_value.binary->subtype()));
+                    }
+                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xdb));
+                        write_number(static_cast<std::uint64_t>(j.m_value.binary->subtype()));
+                    }
+                }
+
+                // step 1: write control byte and the binary array size
+                const auto N = j.m_value.binary->size();
+                if (N <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(0x40 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x58));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x59));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x5A));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x5B));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write each element
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),
+                    N);
+
+                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<std::uint8_t>(0xA0 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xB8));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xB9));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xBA));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xBB));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write each element
+                for (const auto& el : *j.m_value.object)
+                {
+                    write_cbor(el.first);
+                    write_cbor(el.second);
+                }
+                break;
+            }
+
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    */
+    void write_msgpack(const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::null: // nil
+            {
+                oa->write_character(to_char_type(0xC0));
+                break;
+            }
+
+            case value_t::boolean: // true and false
+            {
+                oa->write_character(j.m_value.boolean
+                                    ? to_char_type(0xC3)
+                                    : to_char_type(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<std::uint8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        // uint 8
+                        oa->write_character(to_char_type(0xCC));
+                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        // uint 16
+                        oa->write_character(to_char_type(0xCD));
+                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        // uint 32
+                        oa->write_character(to_char_type(0xCE));
+                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+                    {
+                        // uint 64
+                        oa->write_character(to_char_type(0xCF));
+                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
+                    }
+                }
+                else
+                {
+                    if (j.m_value.number_integer >= -32)
+                    {
+                        // negative fixnum
+                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
+                             j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+                    {
+                        // int 8
+                        oa->write_character(to_char_type(0xD0));
+                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
+                             j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+                    {
+                        // int 16
+                        oa->write_character(to_char_type(0xD1));
+                        write_number(static_cast<std::int16_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
+                             j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+                    {
+                        // int 32
+                        oa->write_character(to_char_type(0xD2));
+                        write_number(static_cast<std::int32_t>(j.m_value.number_integer));
+                    }
+                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&
+                             j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+                    {
+                        // int 64
+                        oa->write_character(to_char_type(0xD3));
+                        write_number(static_cast<std::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<std::uint8_t>(j.m_value.number_integer));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    // uint 8
+                    oa->write_character(to_char_type(0xCC));
+                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // uint 16
+                    oa->write_character(to_char_type(0xCD));
+                    write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // uint 32
+                    oa->write_character(to_char_type(0xCE));
+                    write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
+                }
+                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    // uint 64
+                    oa->write_character(to_char_type(0xCF));
+                    write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
+                }
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack);
+                break;
+            }
+
+            case value_t::string:
+            {
+                // step 1: write control byte and the string length
+                const auto N = j.m_value.string->size();
+                if (N <= 31)
+                {
+                    // fixstr
+                    write_number(static_cast<std::uint8_t>(0xA0 | N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    // str 8
+                    oa->write_character(to_char_type(0xD9));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // str 16
+                    oa->write_character(to_char_type(0xDA));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // str 32
+                    oa->write_character(to_char_type(0xDB));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 2: write the string
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+                    j.m_value.string->size());
+                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<std::uint8_t>(0x90 | N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // array 16
+                    oa->write_character(to_char_type(0xDC));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // array 32
+                    oa->write_character(to_char_type(0xDD));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 2: write each element
+                for (const auto& el : *j.m_value.array)
+                {
+                    write_msgpack(el);
+                }
+                break;
+            }
+
+            case value_t::binary:
+            {
+                // step 0: determine if the binary type has a set subtype to
+                // determine whether or not to use the ext or fixext types
+                const bool use_ext = j.m_value.binary->has_subtype();
+
+                // step 1: write control byte and the byte string length
+                const auto N = j.m_value.binary->size();
+                if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    std::uint8_t output_type{};
+                    bool fixed = true;
+                    if (use_ext)
+                    {
+                        switch (N)
+                        {
+                            case 1:
+                                output_type = 0xD4; // fixext 1
+                                break;
+                            case 2:
+                                output_type = 0xD5; // fixext 2
+                                break;
+                            case 4:
+                                output_type = 0xD6; // fixext 4
+                                break;
+                            case 8:
+                                output_type = 0xD7; // fixext 8
+                                break;
+                            case 16:
+                                output_type = 0xD8; // fixext 16
+                                break;
+                            default:
+                                output_type = 0xC7; // ext 8
+                                fixed = false;
+                                break;
+                        }
+
+                    }
+                    else
+                    {
+                        output_type = 0xC4; // bin 8
+                        fixed = false;
+                    }
+
+                    oa->write_character(to_char_type(output_type));
+                    if (!fixed)
+                    {
+                        write_number(static_cast<std::uint8_t>(N));
+                    }
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    std::uint8_t output_type = use_ext
+                                               ? 0xC8 // ext 16
+                                               : 0xC5; // bin 16
+
+                    oa->write_character(to_char_type(output_type));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    std::uint8_t output_type = use_ext
+                                               ? 0xC9 // ext 32
+                                               : 0xC6; // bin 32
+
+                    oa->write_character(to_char_type(output_type));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 1.5: if this is an ext type, write the subtype
+                if (use_ext)
+                {
+                    write_number(static_cast<std::int8_t>(j.m_value.binary->subtype()));
+                }
+
+                // step 2: write the byte string
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),
+                    N);
+
+                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<std::uint8_t>(0x80 | (N & 0xF)));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // map 16
+                    oa->write_character(to_char_type(0xDE));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // map 32
+                    oa->write_character(to_char_type(0xDF));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 2: write each element
+                for (const auto& el : *j.m_value.object)
+                {
+                    write_msgpack(el.first);
+                    write_msgpack(el.second);
+                }
+                break;
+            }
+
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+    /*!
+    @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
+    @param[in] use_bjdata  whether write in BJData format, default is false
+    */
+    void write_ubjson(const BasicJsonType& j, const bool use_count,
+                      const bool use_type, const bool add_prefix = true,
+                      const bool use_bjdata = false)
+    {
+        switch (j.type())
+        {
+            case value_t::null:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('Z'));
+                }
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(j.m_value.boolean
+                                        ? to_char_type('T')
+                                        : to_char_type('F'));
+                }
+                break;
+            }
+
+            case value_t::number_integer:
+            {
+                write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix, use_bjdata);
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix, use_bjdata);
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix, use_bjdata);
+                break;
+            }
+
+            case value_t::string:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('S'));
+                }
+                write_number_with_ubjson_prefix(j.m_value.string->size(), true, use_bjdata);
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+                    j.m_value.string->size());
+                break;
+            }
+
+            case value_t::array:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('['));
+                }
+
+                bool prefix_required = true;
+                if (use_type && !j.m_value.array->empty())
+                {
+                    JSON_ASSERT(use_count);
+                    const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
+                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
+                                                         [this, first_prefix, use_bjdata](const BasicJsonType & v)
+                    {
+                        return ubjson_prefix(v, use_bjdata) == first_prefix;
+                    });
+
+                    std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
+
+                    if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
+                    {
+                        prefix_required = false;
+                        oa->write_character(to_char_type('$'));
+                        oa->write_character(first_prefix);
+                    }
+                }
+
+                if (use_count)
+                {
+                    oa->write_character(to_char_type('#'));
+                    write_number_with_ubjson_prefix(j.m_value.array->size(), true, use_bjdata);
+                }
+
+                for (const auto& el : *j.m_value.array)
+                {
+                    write_ubjson(el, use_count, use_type, prefix_required, use_bjdata);
+                }
+
+                if (!use_count)
+                {
+                    oa->write_character(to_char_type(']'));
+                }
+
+                break;
+            }
+
+            case value_t::binary:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('['));
+                }
+
+                if (use_type && !j.m_value.binary->empty())
+                {
+                    JSON_ASSERT(use_count);
+                    oa->write_character(to_char_type('$'));
+                    oa->write_character('U');
+                }
+
+                if (use_count)
+                {
+                    oa->write_character(to_char_type('#'));
+                    write_number_with_ubjson_prefix(j.m_value.binary->size(), true, use_bjdata);
+                }
+
+                if (use_type)
+                {
+                    oa->write_characters(
+                        reinterpret_cast<const CharType*>(j.m_value.binary->data()),
+                        j.m_value.binary->size());
+                }
+                else
+                {
+                    for (size_t i = 0; i < j.m_value.binary->size(); ++i)
+                    {
+                        oa->write_character(to_char_type('U'));
+                        oa->write_character(j.m_value.binary->data()[i]);
+                    }
+                }
+
+                if (!use_count)
+                {
+                    oa->write_character(to_char_type(']'));
+                }
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                if (use_bjdata && j.m_value.object->size() == 3 && j.m_value.object->find("_ArrayType_") != j.m_value.object->end() && j.m_value.object->find("_ArraySize_") != j.m_value.object->end() && j.m_value.object->find("_ArrayData_") != j.m_value.object->end())
+                {
+                    if (!write_bjdata_ndarray(*j.m_value.object, use_count, use_type))  // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata)
+                    {
+                        break;
+                    }
+                }
+
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('{'));
+                }
+
+                bool prefix_required = true;
+                if (use_type && !j.m_value.object->empty())
+                {
+                    JSON_ASSERT(use_count);
+                    const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
+                    const bool same_prefix = std::all_of(j.begin(), j.end(),
+                                                         [this, first_prefix, use_bjdata](const BasicJsonType & v)
+                    {
+                        return ubjson_prefix(v, use_bjdata) == first_prefix;
+                    });
+
+                    std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
+
+                    if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
+                    {
+                        prefix_required = false;
+                        oa->write_character(to_char_type('$'));
+                        oa->write_character(first_prefix);
+                    }
+                }
+
+                if (use_count)
+                {
+                    oa->write_character(to_char_type('#'));
+                    write_number_with_ubjson_prefix(j.m_value.object->size(), true, use_bjdata);
+                }
+
+                for (const auto& el : *j.m_value.object)
+                {
+                    write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
+                    oa->write_characters(
+                        reinterpret_cast<const CharType*>(el.first.c_str()),
+                        el.first.size());
+                    write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata);
+                }
+
+                if (!use_count)
+                {
+                    oa->write_character(to_char_type('}'));
+                }
+
+                break;
+            }
+
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+  private:
+    //////////
+    // BSON //
+    //////////
+
+    /*!
+    @return The size of a BSON document entry header, including the id marker
+            and the entry name size (and its null-terminator).
+    */
+    static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
+    {
+        const auto it = name.find(static_cast<typename string_t::value_type>(0));
+        if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
+        {
+            JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
+            static_cast<void>(j);
+        }
+
+        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
+    }
+
+    /*!
+    @brief Writes the given @a element_type and @a name to the output adapter
+    */
+    void write_bson_entry_header(const string_t& name,
+                                 const std::uint8_t element_type)
+    {
+        oa->write_character(to_char_type(element_type)); // boolean
+        oa->write_characters(
+            reinterpret_cast<const CharType*>(name.c_str()),
+            name.size() + 1u);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and boolean value @a value
+    */
+    void write_bson_boolean(const string_t& name,
+                            const bool value)
+    {
+        write_bson_entry_header(name, 0x08);
+        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and double value @a value
+    */
+    void write_bson_double(const string_t& name,
+                           const double value)
+    {
+        write_bson_entry_header(name, 0x01);
+        write_number<double>(value, true);
+    }
+
+    /*!
+    @return The size of the BSON-encoded string in @a value
+    */
+    static std::size_t calc_bson_string_size(const string_t& value)
+    {
+        return sizeof(std::int32_t) + value.size() + 1ul;
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and string value @a value
+    */
+    void write_bson_string(const string_t& name,
+                           const string_t& value)
+    {
+        write_bson_entry_header(name, 0x02);
+
+        write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true);
+        oa->write_characters(
+            reinterpret_cast<const CharType*>(value.c_str()),
+            value.size() + 1);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and null value
+    */
+    void write_bson_null(const string_t& name)
+    {
+        write_bson_entry_header(name, 0x0A);
+    }
+
+    /*!
+    @return The size of the BSON-encoded integer @a value
+    */
+    static std::size_t calc_bson_integer_size(const std::int64_t value)
+    {
+        return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()
+               ? sizeof(std::int32_t)
+               : sizeof(std::int64_t);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and integer @a value
+    */
+    void write_bson_integer(const string_t& name,
+                            const std::int64_t value)
+    {
+        if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())
+        {
+            write_bson_entry_header(name, 0x10); // int32
+            write_number<std::int32_t>(static_cast<std::int32_t>(value), true);
+        }
+        else
+        {
+            write_bson_entry_header(name, 0x12); // int64
+            write_number<std::int64_t>(static_cast<std::int64_t>(value), true);
+        }
+    }
+
+    /*!
+    @return The size of the BSON-encoded unsigned integer in @a j
+    */
+    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
+    {
+        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+               ? sizeof(std::int32_t)
+               : sizeof(std::int64_t);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and unsigned @a value
+    */
+    void write_bson_unsigned(const string_t& name,
+                             const BasicJsonType& j)
+    {
+        if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+        {
+            write_bson_entry_header(name, 0x10 /* int32 */);
+            write_number<std::int32_t>(static_cast<std::int32_t>(j.m_value.number_unsigned), true);
+        }
+        else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+        {
+            write_bson_entry_header(name, 0x12 /* int64 */);
+            write_number<std::int64_t>(static_cast<std::int64_t>(j.m_value.number_unsigned), true);
+        }
+        else
+        {
+            JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j));
+        }
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and object @a value
+    */
+    void write_bson_object_entry(const string_t& name,
+                                 const typename BasicJsonType::object_t& value)
+    {
+        write_bson_entry_header(name, 0x03); // object
+        write_bson_object(value);
+    }
+
+    /*!
+    @return The size of the BSON-encoded array @a value
+    */
+    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)
+    {
+        std::size_t array_index = 0ul;
+
+        const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast<std::size_t>(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)
+        {
+            return result + calc_bson_element_size(std::to_string(array_index++), el);
+        });
+
+        return sizeof(std::int32_t) + embedded_document_size + 1ul;
+    }
+
+    /*!
+    @return The size of the BSON-encoded binary array @a value
+    */
+    static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)
+    {
+        return sizeof(std::int32_t) + value.size() + 1ul;
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and array @a value
+    */
+    void write_bson_array(const string_t& name,
+                          const typename BasicJsonType::array_t& value)
+    {
+        write_bson_entry_header(name, 0x04); // array
+        write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_array_size(value)), true);
+
+        std::size_t array_index = 0ul;
+
+        for (const auto& el : value)
+        {
+            write_bson_element(std::to_string(array_index++), el);
+        }
+
+        oa->write_character(to_char_type(0x00));
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and binary value @a value
+    */
+    void write_bson_binary(const string_t& name,
+                           const binary_t& value)
+    {
+        write_bson_entry_header(name, 0x05);
+
+        write_number<std::int32_t>(static_cast<std::int32_t>(value.size()), true);
+        write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));
+
+        oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
+    }
+
+    /*!
+    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name
+    @return The calculated size for the BSON document entry for @a j with the given @a name.
+    */
+    static std::size_t calc_bson_element_size(const string_t& name,
+            const BasicJsonType& j)
+    {
+        const auto header_size = calc_bson_entry_header_size(name, j);
+        switch (j.type())
+        {
+            case value_t::object:
+                return header_size + calc_bson_object_size(*j.m_value.object);
+
+            case value_t::array:
+                return header_size + calc_bson_array_size(*j.m_value.array);
+
+            case value_t::binary:
+                return header_size + calc_bson_binary_size(*j.m_value.binary);
+
+            case value_t::boolean:
+                return header_size + 1ul;
+
+            case value_t::number_float:
+                return header_size + 8ul;
+
+            case value_t::number_integer:
+                return header_size + calc_bson_integer_size(j.m_value.number_integer);
+
+            case value_t::number_unsigned:
+                return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);
+
+            case value_t::string:
+                return header_size + calc_bson_string_size(*j.m_value.string);
+
+            case value_t::null:
+                return header_size + 0ul;
+
+            // LCOV_EXCL_START
+            case value_t::discarded:
+            default:
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
+                return 0ul;
+                // LCOV_EXCL_STOP
+        }
+    }
+
+    /*!
+    @brief Serializes the JSON value @a j to BSON and associates it with the
+           key @a name.
+    @param name The name to associate with the JSON entity @a j within the
+                current BSON document
+    */
+    void write_bson_element(const string_t& name,
+                            const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::object:
+                return write_bson_object_entry(name, *j.m_value.object);
+
+            case value_t::array:
+                return write_bson_array(name, *j.m_value.array);
+
+            case value_t::binary:
+                return write_bson_binary(name, *j.m_value.binary);
+
+            case value_t::boolean:
+                return write_bson_boolean(name, j.m_value.boolean);
+
+            case value_t::number_float:
+                return write_bson_double(name, j.m_value.number_float);
+
+            case value_t::number_integer:
+                return write_bson_integer(name, j.m_value.number_integer);
+
+            case value_t::number_unsigned:
+                return write_bson_unsigned(name, j);
+
+            case value_t::string:
+                return write_bson_string(name, *j.m_value.string);
+
+            case value_t::null:
+                return write_bson_null(name);
+
+            // LCOV_EXCL_START
+            case value_t::discarded:
+            default:
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
+                return;
+                // LCOV_EXCL_STOP
+        }
+    }
+
+    /*!
+    @brief Calculates the size of the BSON serialization of the given
+           JSON-object @a j.
+    @param[in] value  JSON value to serialize
+    @pre       value.type() == value_t::object
+    */
+    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
+    {
+        std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0),
+                                    [](size_t result, const typename BasicJsonType::object_t::value_type & el)
+        {
+            return result += calc_bson_element_size(el.first, el.second);
+        });
+
+        return sizeof(std::int32_t) + document_size + 1ul;
+    }
+
+    /*!
+    @param[in] value  JSON value to serialize
+    @pre       value.type() == value_t::object
+    */
+    void write_bson_object(const typename BasicJsonType::object_t& value)
+    {
+        write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_object_size(value)), true);
+
+        for (const auto& el : value)
+        {
+            write_bson_element(el.first, el.second);
+        }
+
+        oa->write_character(to_char_type(0x00));
+    }
+
+    //////////
+    // CBOR //
+    //////////
+
+    static constexpr CharType get_cbor_float_prefix(float /*unused*/)
+    {
+        return to_char_type(0xFA);  // Single-Precision Float
+    }
+
+    static constexpr CharType get_cbor_float_prefix(double /*unused*/)
+    {
+        return to_char_type(0xFB);  // Double-Precision Float
+    }
+
+    /////////////
+    // MsgPack //
+    /////////////
+
+    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
+    {
+        return to_char_type(0xCA);  // float 32
+    }
+
+    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
+    {
+        return to_char_type(0xCB);  // float 64
+    }
+
+    ////////////
+    // UBJSON //
+    ////////////
+
+    // 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,
+                                         const bool use_bjdata)
+    {
+        if (add_prefix)
+        {
+            oa->write_character(get_ubjson_float_prefix(n));
+        }
+        write_number(n, use_bjdata);
+    }
+
+    // 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,
+                                         const bool use_bjdata)
+    {
+        if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('i'));  // int8
+            }
+            write_number(static_cast<std::uint8_t>(n), use_bjdata);
+        }
+        else if (n <= (std::numeric_limits<std::uint8_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('U'));  // uint8
+            }
+            write_number(static_cast<std::uint8_t>(n), use_bjdata);
+        }
+        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('I'));  // int16
+            }
+            write_number(static_cast<std::int16_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint16_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('u'));  // uint16 - bjdata only
+            }
+            write_number(static_cast<std::uint16_t>(n), use_bjdata);
+        }
+        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('l'));  // int32
+            }
+            write_number(static_cast<std::int32_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint32_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('m'));  // uint32 - bjdata only
+            }
+            write_number(static_cast<std::uint32_t>(n), use_bjdata);
+        }
+        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('L'));  // int64
+            }
+            write_number(static_cast<std::int64_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && n <= (std::numeric_limits<uint64_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('M'));  // uint64 - bjdata only
+            }
+            write_number(static_cast<std::uint64_t>(n), use_bjdata);
+        }
+        else
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('H'));  // high-precision number
+            }
+
+            const auto number = BasicJsonType(n).dump();
+            write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
+            for (std::size_t i = 0; i < number.size(); ++i)
+            {
+                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+            }
+        }
+    }
+
+    // UBJSON: write number (signed integer)
+    template < typename NumberType, typename std::enable_if <
+                   std::is_signed<NumberType>::value&&
+                   !std::is_floating_point<NumberType>::value, int >::type = 0 >
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix,
+                                         const bool use_bjdata)
+    {
+        if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('i'));  // int8
+            }
+            write_number(static_cast<std::int8_t>(n), use_bjdata);
+        }
+        else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('U'));  // uint8
+            }
+            write_number(static_cast<std::uint8_t>(n), use_bjdata);
+        }
+        else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('I'));  // int16
+            }
+            write_number(static_cast<std::int16_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::max)())))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('u'));  // uint16 - bjdata only
+            }
+            write_number(static_cast<uint16_t>(n), use_bjdata);
+        }
+        else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('l'));  // int32
+            }
+            write_number(static_cast<std::int32_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::max)())))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('m'));  // uint32 - bjdata only
+            }
+            write_number(static_cast<uint32_t>(n), use_bjdata);
+        }
+        else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('L'));  // int64
+            }
+            write_number(static_cast<std::int64_t>(n), use_bjdata);
+        }
+        // LCOV_EXCL_START
+        else
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('H'));  // high-precision number
+            }
+
+            const auto number = BasicJsonType(n).dump();
+            write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
+            for (std::size_t i = 0; i < number.size(); ++i)
+            {
+                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+            }
+        }
+        // LCOV_EXCL_STOP
+    }
+
+    /*!
+    @brief determine the type prefix of container values
+    */
+    CharType ubjson_prefix(const BasicJsonType& j, const bool use_bjdata) 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<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+                {
+                    return 'i';
+                }
+                if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    return 'U';
+                }
+                if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+                {
+                    return 'I';
+                }
+                if (use_bjdata && ((std::numeric_limits<std::uint16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()))
+                {
+                    return 'u';
+                }
+                if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+                {
+                    return 'l';
+                }
+                if (use_bjdata && ((std::numeric_limits<std::uint32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()))
+                {
+                    return 'm';
+                }
+                if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+                {
+                    return 'L';
+                }
+                // anything else is treated as high-precision number
+                return 'H'; // LCOV_EXCL_LINE
+            }
+
+            case value_t::number_unsigned:
+            {
+                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
+                {
+                    return 'i';
+                }
+                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))
+                {
+                    return 'U';
+                }
+                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
+                {
+                    return 'I';
+                }
+                if (use_bjdata && j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint16_t>::max)()))
+                {
+                    return 'u';
+                }
+                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+                {
+                    return 'l';
+                }
+                if (use_bjdata && j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint32_t>::max)()))
+                {
+                    return 'm';
+                }
+                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+                {
+                    return 'L';
+                }
+                if (use_bjdata && j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    return 'M';
+                }
+                // anything else is treated as high-precision number
+                return 'H'; // LCOV_EXCL_LINE
+            }
+
+            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: // fallthrough
+            case value_t::binary:
+                return '[';
+
+            case value_t::object:
+                return '{';
+
+            case value_t::discarded:
+            default:  // discarded values
+                return 'N';
+        }
+    }
+
+    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
+    {
+        return 'd';  // float 32
+    }
+
+    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)
+    {
+        return 'D';  // float 64
+    }
+
+    /*!
+    @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
+    */
+    bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type)
+    {
+        std::map<string_t, CharType> bjdtype = {{"uint8", 'U'},  {"int8", 'i'},  {"uint16", 'u'}, {"int16", 'I'},
+            {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, {"char", 'C'}
+        };
+
+        string_t key = "_ArrayType_";
+        auto it = bjdtype.find(static_cast<string_t>(value.at(key)));
+        if (it == bjdtype.end())
+        {
+            return true;
+        }
+        CharType dtype = it->second;
+
+        key = "_ArraySize_";
+        std::size_t len = (value.at(key).empty() ? 0 : 1);
+        for (const auto& el : value.at(key))
+        {
+            len *= static_cast<std::size_t>(el.m_value.number_unsigned);
+        }
+
+        key = "_ArrayData_";
+        if (value.at(key).size() != len)
+        {
+            return true;
+        }
+
+        oa->write_character('[');
+        oa->write_character('$');
+        oa->write_character(dtype);
+        oa->write_character('#');
+
+        key = "_ArraySize_";
+        write_ubjson(value.at(key), use_count, use_type, true,  true);
+
+        key = "_ArrayData_";
+        if (dtype == 'U' || dtype == 'C')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint8_t>(el.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'i')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int8_t>(el.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'u')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint16_t>(el.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'I')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int16_t>(el.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'm')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint32_t>(el.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'l')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int32_t>(el.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'M')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint64_t>(el.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'L')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int64_t>(el.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'd')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<float>(el.m_value.number_float), true);
+            }
+        }
+        else if (dtype == 'D')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<double>(el.m_value.number_float), true);
+            }
+        }
+        return false;
+    }
+
+    ///////////////////////
+    // Utility functions //
+    ///////////////////////
+
+    /*
+    @brief write a number to output input
+    @param[in] n number of type @a NumberType
+    @param[in] OutputIsLittleEndian Set to true if output data is
+                                 required to be little endian
+    @tparam NumberType the type of the number
+
+    @note This function needs to respect the system's endianness, because bytes
+          in CBOR, MessagePack, and UBJSON are stored in network order (big
+          endian) and therefore need reordering on little endian systems.
+          On the other hand, BSON and BJData use little endian and should reorder
+          on big endian systems.
+    */
+    template<typename NumberType>
+    void write_number(const NumberType n, const bool OutputIsLittleEndian = false)
+    {
+        // step 1: write number to array of length NumberType
+        std::array<CharType, sizeof(NumberType)> vec{};
+        std::memcpy(vec.data(), &n, sizeof(NumberType));
+
+        // step 2: write array to output (with possible reordering)
+        if (is_little_endian != OutputIsLittleEndian)
+        {
+            // reverse byte order prior to conversion if necessary
+            std::reverse(vec.begin(), vec.end());
+        }
+
+        oa->write_characters(vec.data(), sizeof(NumberType));
+    }
+
+    void write_compact_float(const number_float_t n, detail::input_format_t format)
+    {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+        if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
+                static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
+                static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))
+        {
+            oa->write_character(format == detail::input_format_t::cbor
+                                ? get_cbor_float_prefix(static_cast<float>(n))
+                                : get_msgpack_float_prefix(static_cast<float>(n)));
+            write_number(static_cast<float>(n));
+        }
+        else
+        {
+            oa->write_character(format == detail::input_format_t::cbor
+                                ? get_cbor_float_prefix(n)
+                                : get_msgpack_float_prefix(n));
+            write_number(n);
+        }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+    }
+
+  public:
+    // The following to_char_type functions are implement the conversion
+    // between uint8_t and CharType. In case CharType is not unsigned,
+    // such a conversion is required to allow values greater than 128.
+    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.
+    template < typename C = CharType,
+               enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >
+    static constexpr CharType to_char_type(std::uint8_t x) noexcept
+    {
+        return *reinterpret_cast<char*>(&x);
+    }
+
+    template < typename C = CharType,
+               enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >
+    static CharType to_char_type(std::uint8_t x) noexcept
+    {
+        static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t");
+        static_assert(std::is_trivial<CharType>::value, "CharType must be trivial");
+        CharType result;
+        std::memcpy(&result, &x, sizeof(x));
+        return result;
+    }
+
+    template<typename C = CharType,
+             enable_if_t<std::is_unsigned<C>::value>* = nullptr>
+    static constexpr CharType to_char_type(std::uint8_t x) noexcept
+    {
+        return x;
+    }
+
+    template < typename InputCharType, typename C = CharType,
+               enable_if_t <
+                   std::is_signed<C>::value &&
+                   std::is_signed<char>::value &&
+                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value
+                   > * = nullptr >
+    static constexpr CharType to_char_type(InputCharType x) noexcept
+    {
+        return x;
+    }
+
+  private:
+    /// whether we can assume little endianness
+    const bool is_little_endian = little_endianness();
+
+    /// the output
+    output_adapter_t<CharType> oa = nullptr;
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/output/output_adapters.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/output/output_adapters.h
new file mode 100644
index 0000000..06cbeb1
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/output/output_adapters.h
@@ -0,0 +1,173 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <algorithm> // copy
+#include <cstddef> // size_t
+#include <iterator> // back_inserter
+#include <memory> // shared_ptr, make_shared
+#include <string> // basic_string
+#include <vector> // vector
+
+#ifndef JSON_NO_IO
+    #include <ios>      // streamsize
+    #include <ostream>  // basic_ostream
+#endif  // JSON_NO_IO
+
+#include <wpi/detail/macro_scope.h>
+
+#include <wpi/raw_ostream.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// abstract output adapter interface
+template<typename CharType> struct output_adapter_protocol
+{
+    virtual void write_character(CharType c) = 0;
+    virtual void write_characters(const CharType* s, std::size_t length) = 0;
+    virtual ~output_adapter_protocol() = default;
+
+    output_adapter_protocol() = default;
+    output_adapter_protocol(const output_adapter_protocol&) = default;
+    output_adapter_protocol(output_adapter_protocol&&) noexcept = default;
+    output_adapter_protocol& operator=(const output_adapter_protocol&) = default;
+    output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;
+};
+
+/// a type to simplify interfaces
+template<typename CharType>
+using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
+
+/// output adapter for byte vectors
+template<typename CharType, typename AllocatorType = std::allocator<CharType>>
+class output_vector_adapter : public output_adapter_protocol<CharType>
+{
+  public:
+    explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept
+        : v(vec)
+    {}
+
+    void write_character(CharType c) override
+    {
+        v.push_back(c);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    void write_characters(const CharType* s, std::size_t length) override
+    {
+        v.insert(v.end(), s, s + length);
+    }
+
+  private:
+    std::vector<CharType, AllocatorType>& v;
+};
+
+#ifndef JSON_NO_IO
+/// output adapter for output streams
+template<typename CharType>
+class output_stream_adapter : public output_adapter_protocol<CharType>
+{
+  public:
+    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept
+        : stream(s)
+    {}
+
+    void write_character(CharType c) override
+    {
+        stream.put(c);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    void write_characters(const CharType* s, std::size_t length) override
+    {
+        stream.write(s, static_cast<std::streamsize>(length));
+    }
+
+  private:
+    std::basic_ostream<CharType>& stream;
+};
+#endif  // JSON_NO_IO
+
+/// output adapter for basic_string
+template<typename CharType, typename StringType = std::basic_string<CharType>>
+class output_string_adapter : public output_adapter_protocol<CharType>
+{
+  public:
+    explicit output_string_adapter(StringType& s) noexcept
+        : str(s)
+    {}
+
+    void write_character(CharType c) override
+    {
+        str.push_back(c);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    void write_characters(const CharType* s, std::size_t length) override
+    {
+        str.append(s, length);
+    }
+
+  private:
+    StringType& str;
+};
+
+template<typename CharType>
+class raw_ostream_adapter : public output_adapter_protocol<CharType>
+{
+  public:
+    explicit raw_ostream_adapter(raw_ostream& s) noexcept
+        : os(s) {}
+
+
+    void write_character(CharType c) override {
+        os << c;
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    void write_characters(const CharType* s, std::size_t length) override {
+        os.write(s, length);
+    }
+
+  private:
+    raw_ostream& os;
+};
+
+template<typename CharType, typename StringType = std::basic_string<CharType>>
+class output_adapter
+{
+  public:
+    template<typename AllocatorType = std::allocator<CharType>>
+    output_adapter(std::vector<CharType, AllocatorType>& vec)
+        : oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}
+
+#ifndef JSON_NO_IO
+    output_adapter(std::basic_ostream<CharType>& s)
+        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
+#endif  // JSON_NO_IO
+
+    output_adapter(StringType& s)
+        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
+
+    output_adapter(raw_ostream& os)
+        : oa(std::make_shared<raw_ostream_adapter<CharType>>(os)) {}
+
+    operator output_adapter_t<CharType>()
+    {
+        return oa;
+    }
+
+  private:
+    output_adapter_t<CharType> oa = nullptr;
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/output/serializer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/output/serializer.h
new file mode 100644
index 0000000..4d137a3
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/output/serializer.h
@@ -0,0 +1,997 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2008-2009 Björn Hoehrmann <bjoern@hoehrmann.de>
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <algorithm> // reverse, remove, fill, find, none_of
+#include <array> // array
+#include <clocale> // localeconv, lconv
+#include <cmath> // labs, isfinite, isnan, signbit
+#include <cstddef> // size_t, ptrdiff_t
+#include <cstdint> // uint8_t
+#include <cstdio> // snprintf
+#include <limits> // numeric_limits
+#include <string> // string, char_traits
+#include <iomanip> // setfill, setw
+#include <type_traits> // is_same
+#include <utility> // move
+
+#include <wpi/detail/conversions/to_chars.h>
+#include <wpi/detail/exceptions.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/meta/cpp_future.h>
+#include <wpi/detail/output/binary_writer.h>
+#include <wpi/detail/output/output_adapters.h>
+#include <wpi/detail/string_concat.h>
+#include <wpi/detail/value_t.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////
+// serialization //
+///////////////////
+
+/// how to treat decoding errors
+enum class error_handler_t
+{
+    strict,  ///< throw a type_error exception in case of invalid UTF-8
+    replace, ///< replace invalid UTF-8 sequences with U+FFFD
+    ignore   ///< ignore invalid UTF-8 sequences
+};
+
+template<typename BasicJsonType>
+class serializer
+{
+    using string_t = typename BasicJsonType::string_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using binary_char_t = typename BasicJsonType::binary_t::value_type;
+    static constexpr std::uint8_t UTF8_ACCEPT = 0;
+    static constexpr std::uint8_t UTF8_REJECT = 1;
+
+  public:
+    /*!
+    @param[in] s  output stream to serialize to
+    @param[in] ichar  indentation character to use
+    @param[in] error_handler_  how to react on decoding errors
+    */
+    serializer(output_adapter_t<char> s, const char ichar,
+               error_handler_t error_handler_ = error_handler_t::strict,
+               size_t indent_init_len = 512)
+        : o(std::move(s))
+        , loc(std::localeconv())
+        , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
+        , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
+        , indent_char(ichar)
+        , indent_string(indent_init_len, indent_char)
+        , error_handler(error_handler_)
+    {}
+    
+    serializer(raw_ostream& os, const char ichar,
+               size_t indent_init_len = 512,
+               error_handler_t error_handler_ = error_handler_t::strict)
+        : serializer(output_adapter<char>(os), ichar, error_handler_, indent_init_len)
+    {}
+
+    // delete because of pointer members
+    serializer(const serializer&) = delete;
+    serializer& operator=(const serializer&) = delete;
+    serializer(serializer&&) = delete;
+    serializer& operator=(serializer&&) = delete;
+    ~serializer() = default;
+
+    /*!
+    @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
+    - binary values are serialized as objects containing the subtype and the
+      byte array
+
+    @param[in] val               value to serialize
+    @param[in] pretty_print      whether the output shall be pretty-printed
+    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
+    in the output are escaped with `\uXXXX` sequences, and the result consists
+    of ASCII characters only.
+    @param[in] indent_step       the indent level
+    @param[in] current_indent    the current indent level (only used internally)
+    */
+    void dump(const BasicJsonType& val,
+              const bool pretty_print,
+              const bool ensure_ascii,
+              const unsigned int indent_step,
+              const unsigned int current_indent = 0)
+    {
+        switch (val.m_type)
+        {
+            case value_t::object:
+            {
+                if (val.m_value.object->empty())
+                {
+                    o->write_characters("{}", 2);
+                    return;
+                }
+
+                if (pretty_print)
+                {
+                    o->write_characters("{\n", 2);
+
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    // first n-1 elements
+                    auto i = val.m_value.object->cbegin();
+                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+                    {
+                        o->write_characters(indent_string.c_str(), new_indent);
+                        o->write_character('\"');
+                        dump_escaped(i->first, ensure_ascii);
+                        o->write_characters("\": ", 3);
+                        dump(i->second, true, ensure_ascii, indent_step, new_indent);
+                        o->write_characters(",\n", 2);
+                    }
+
+                    // last element
+                    JSON_ASSERT(i != val.m_value.object->cend());
+                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());
+                    o->write_characters(indent_string.c_str(), new_indent);
+                    o->write_character('\"');
+                    dump_escaped(i->first, ensure_ascii);
+                    o->write_characters("\": ", 3);
+                    dump(i->second, true, ensure_ascii, indent_step, new_indent);
+
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character('}');
+                }
+                else
+                {
+                    o->write_character('{');
+
+                    // first n-1 elements
+                    auto i = val.m_value.object->cbegin();
+                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+                    {
+                        o->write_character('\"');
+                        dump_escaped(i->first, ensure_ascii);
+                        o->write_characters("\":", 2);
+                        dump(i->second, false, ensure_ascii, indent_step, current_indent);
+                        o->write_character(',');
+                    }
+
+                    // last element
+                    JSON_ASSERT(i != val.m_value.object->cend());
+                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());
+                    o->write_character('\"');
+                    dump_escaped(i->first, ensure_ascii);
+                    o->write_characters("\":", 2);
+                    dump(i->second, false, ensure_ascii, indent_step, current_indent);
+
+                    o->write_character('}');
+                }
+
+                return;
+            }
+
+            case value_t::array:
+            {
+                if (val.m_value.array->empty())
+                {
+                    o->write_characters("[]", 2);
+                    return;
+                }
+
+                if (pretty_print)
+                {
+                    o->write_characters("[\n", 2);
+
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    // first n-1 elements
+                    for (auto i = val.m_value.array->cbegin();
+                            i != val.m_value.array->cend() - 1; ++i)
+                    {
+                        o->write_characters(indent_string.c_str(), new_indent);
+                        dump(*i, true, ensure_ascii, indent_step, new_indent);
+                        o->write_characters(",\n", 2);
+                    }
+
+                    // last element
+                    JSON_ASSERT(!val.m_value.array->empty());
+                    o->write_characters(indent_string.c_str(), new_indent);
+                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
+
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character(']');
+                }
+                else
+                {
+                    o->write_character('[');
+
+                    // 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->write_character(',');
+                    }
+
+                    // last element
+                    JSON_ASSERT(!val.m_value.array->empty());
+                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
+
+                    o->write_character(']');
+                }
+
+                return;
+            }
+
+            case value_t::string:
+            {
+                o->write_character('\"');
+                dump_escaped(*val.m_value.string, ensure_ascii);
+                o->write_character('\"');
+                return;
+            }
+
+            case value_t::binary:
+            {
+                if (pretty_print)
+                {
+                    o->write_characters("{\n", 2);
+
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    o->write_characters(indent_string.c_str(), new_indent);
+
+                    o->write_characters("\"bytes\": [", 10);
+
+                    if (!val.m_value.binary->empty())
+                    {
+                        for (auto i = val.m_value.binary->cbegin();
+                                i != val.m_value.binary->cend() - 1; ++i)
+                        {
+                            dump_integer(*i);
+                            o->write_characters(", ", 2);
+                        }
+                        dump_integer(val.m_value.binary->back());
+                    }
+
+                    o->write_characters("],\n", 3);
+                    o->write_characters(indent_string.c_str(), new_indent);
+
+                    o->write_characters("\"subtype\": ", 11);
+                    if (val.m_value.binary->has_subtype())
+                    {
+                        dump_integer(val.m_value.binary->subtype());
+                    }
+                    else
+                    {
+                        o->write_characters("null", 4);
+                    }
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character('}');
+                }
+                else
+                {
+                    o->write_characters("{\"bytes\":[", 10);
+
+                    if (!val.m_value.binary->empty())
+                    {
+                        for (auto i = val.m_value.binary->cbegin();
+                                i != val.m_value.binary->cend() - 1; ++i)
+                        {
+                            dump_integer(*i);
+                            o->write_character(',');
+                        }
+                        dump_integer(val.m_value.binary->back());
+                    }
+
+                    o->write_characters("],\"subtype\":", 12);
+                    if (val.m_value.binary->has_subtype())
+                    {
+                        dump_integer(val.m_value.binary->subtype());
+                        o->write_character('}');
+                    }
+                    else
+                    {
+                        o->write_characters("null}", 5);
+                    }
+                }
+                return;
+            }
+
+            case value_t::boolean:
+            {
+                if (val.m_value.boolean)
+                {
+                    o->write_characters("true", 4);
+                }
+                else
+                {
+                    o->write_characters("false", 5);
+                }
+                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->write_characters("<discarded>", 11);
+                return;
+            }
+
+            case value_t::null:
+            {
+                o->write_characters("null", 4);
+                return;
+            }
+
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+    }
+
+  public:
+    /*!
+    @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(std::string_view s, const bool ensure_ascii)
+    {
+        std::uint32_t codepoint{};
+        std::uint8_t state = UTF8_ACCEPT;
+        std::size_t bytes = 0;  // number of bytes written to string_buffer
+
+        // number of bytes written at the point of the last valid byte
+        std::size_t bytes_after_last_accept = 0;
+        std::size_t undumped_chars = 0;
+
+        for (std::size_t i = 0; i < s.size(); ++i)
+        {
+            const auto byte = static_cast<std::uint8_t>(s[i]);
+
+            switch (decode(state, codepoint, byte))
+            {
+                case UTF8_ACCEPT:  // decode found a new code point
+                {
+                    switch (codepoint)
+                    {
+                        case 0x08: // backspace
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'b';
+                            break;
+                        }
+
+                        case 0x09: // horizontal tab
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 't';
+                            break;
+                        }
+
+                        case 0x0A: // newline
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'n';
+                            break;
+                        }
+
+                        case 0x0C: // formfeed
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'f';
+                            break;
+                        }
+
+                        case 0x0D: // carriage return
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'r';
+                            break;
+                        }
+
+                        case 0x22: // quotation mark
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = '\"';
+                            break;
+                        }
+
+                        case 0x5C: // reverse solidus
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = '\\';
+                            break;
+                        }
+
+                        default:
+                        {
+                            // escape control characters (0x00..0x1F) or, if
+                            // ensure_ascii parameter is used, non-ASCII characters
+                            if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
+                            {
+                                if (codepoint <= 0xFFFF)
+                                {
+                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                                    static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
+                                                                      static_cast<std::uint16_t>(codepoint)));
+                                    bytes += 6;
+                                }
+                                else
+                                {
+                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                                    static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
+                                                                      static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
+                                                                      static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
+                                    bytes += 12;
+                                }
+                            }
+                            else
+                            {
+                                // copy byte to buffer (all previous bytes
+                                // been copied have in default case above)
+                                string_buffer[bytes++] = s[i];
+                            }
+                            break;
+                        }
+                    }
+
+                    // write buffer and reset index; there must be 13 bytes
+                    // left, as this is the maximal number of bytes to be
+                    // written ("\uxxxx\uxxxx\0") for one code point
+                    if (string_buffer.size() - bytes < 13)
+                    {
+                        o->write_characters(string_buffer.data(), bytes);
+                        bytes = 0;
+                    }
+
+                    // remember the byte position of this accept
+                    bytes_after_last_accept = bytes;
+                    undumped_chars = 0;
+                    break;
+                }
+
+                case UTF8_REJECT:  // decode found invalid UTF-8 byte
+                {
+                    switch (error_handler)
+                    {
+                        case error_handler_t::strict:
+                        {
+                            JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
+                        }
+
+                        case error_handler_t::ignore:
+                        case error_handler_t::replace:
+                        {
+                            // in case we saw this character the first time, we
+                            // would like to read it again, because the byte
+                            // may be OK for itself, but just not OK for the
+                            // previous sequence
+                            if (undumped_chars > 0)
+                            {
+                                --i;
+                            }
+
+                            // reset length buffer to the last accepted index;
+                            // thus removing/ignoring the invalid characters
+                            bytes = bytes_after_last_accept;
+
+                            if (error_handler == error_handler_t::replace)
+                            {
+                                // add a replacement character
+                                if (ensure_ascii)
+                                {
+                                    string_buffer[bytes++] = '\\';
+                                    string_buffer[bytes++] = 'u';
+                                    string_buffer[bytes++] = 'f';
+                                    string_buffer[bytes++] = 'f';
+                                    string_buffer[bytes++] = 'f';
+                                    string_buffer[bytes++] = 'd';
+                                }
+                                else
+                                {
+                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
+                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
+                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
+                                }
+
+                                // write buffer and reset index; there must be 13 bytes
+                                // left, as this is the maximal number of bytes to be
+                                // written ("\uxxxx\uxxxx\0") for one code point
+                                if (string_buffer.size() - bytes < 13)
+                                {
+                                    o->write_characters(string_buffer.data(), bytes);
+                                    bytes = 0;
+                                }
+
+                                bytes_after_last_accept = bytes;
+                            }
+
+                            undumped_chars = 0;
+
+                            // continue processing the string
+                            state = UTF8_ACCEPT;
+                            break;
+                        }
+
+                        default:            // LCOV_EXCL_LINE
+                            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+                    }
+                    break;
+                }
+
+                default:  // decode found yet incomplete multi-byte code point
+                {
+                    if (!ensure_ascii)
+                    {
+                        // code point will not be escaped - copy byte to buffer
+                        string_buffer[bytes++] = s[i];
+                    }
+                    ++undumped_chars;
+                    break;
+                }
+            }
+        }
+
+        // we finished processing the string
+        if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
+        {
+            // write buffer
+            if (bytes > 0)
+            {
+                o->write_characters(string_buffer.data(), bytes);
+            }
+        }
+        else
+        {
+            // we finish reading, but do not accept: string was incomplete
+            switch (error_handler)
+            {
+                case error_handler_t::strict:
+                {
+                    JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast<std::uint8_t>(s.back() | 0))), nullptr));
+                }
+
+                case error_handler_t::ignore:
+                {
+                    // write all accepted bytes
+                    o->write_characters(string_buffer.data(), bytes_after_last_accept);
+                    break;
+                }
+
+                case error_handler_t::replace:
+                {
+                    // write all accepted bytes
+                    o->write_characters(string_buffer.data(), bytes_after_last_accept);
+                    // add a replacement character
+                    if (ensure_ascii)
+                    {
+                        o->write_characters("\\ufffd", 6);
+                    }
+                    else
+                    {
+                        o->write_characters("\xEF\xBF\xBD", 3);
+                    }
+                    break;
+                }
+
+                default:            // LCOV_EXCL_LINE
+                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+            }
+        }
+    }
+
+  private:
+    /*!
+    @brief count digits
+
+    Count the number of decimal (base 10) digits for an input unsigned integer.
+
+    @param[in] x  unsigned integer number to count its digits
+    @return    number of decimal digits
+    */
+    inline unsigned int count_digits(number_unsigned_t x) noexcept
+    {
+        unsigned int n_digits = 1;
+        for (;;)
+        {
+            if (x < 10)
+            {
+                return n_digits;
+            }
+            if (x < 100)
+            {
+                return n_digits + 1;
+            }
+            if (x < 1000)
+            {
+                return n_digits + 2;
+            }
+            if (x < 10000)
+            {
+                return n_digits + 3;
+            }
+            x = x / 10000u;
+            n_digits += 4;
+        }
+    }
+
+    /*!
+     * @brief convert a byte to a uppercase hex representation
+     * @param[in] byte byte to represent
+     * @return representation ("00".."FF")
+     */
+    static std::string hex_bytes(std::uint8_t byte)
+    {
+        std::string result = "FF";
+        constexpr const char* nibble_to_hex = "0123456789ABCDEF";
+        result[0] = nibble_to_hex[byte / 16];
+        result[1] = nibble_to_hex[byte % 16];
+        return result;
+    }
+
+    // templates to avoid warnings about useless casts
+    template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
+    bool is_negative_number(NumberType x)
+    {
+        return x < 0;
+    }
+
+    template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 >
+    bool is_negative_number(NumberType /*unused*/)
+    {
+        return false;
+    }
+
+  public:
+    /*!
+    @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 number_integer_t or @a number_unsigned_t
+    */
+    template < typename NumberType, detail::enable_if_t <
+                   std::is_integral<NumberType>::value ||
+                   std::is_same<NumberType, number_unsigned_t>::value ||
+                   std::is_same<NumberType, number_integer_t>::value ||
+                   std::is_same<NumberType, binary_char_t>::value,
+                   int > = 0 >
+    void dump_integer(NumberType x)
+    {
+        static constexpr std::array<std::array<char, 2>, 100> digits_to_99
+        {
+            {
+                {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},
+                {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},
+                {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},
+                {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},
+                {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},
+                {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},
+                {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},
+                {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},
+                {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},
+                {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},
+            }
+        };
+
+        // special case for "0"
+        if (x == 0)
+        {
+            o->write_character('0');
+            return;
+        }
+
+        // use a pointer to fill the buffer
+        auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+
+        number_unsigned_t abs_value;
+
+        unsigned int n_chars{};
+
+        if (is_negative_number(x))
+        {
+            *buffer_ptr = '-';
+            abs_value = remove_sign(static_cast<number_integer_t>(x));
+
+            // account one more byte for the minus sign
+            n_chars = 1 + count_digits(abs_value);
+        }
+        else
+        {
+            abs_value = static_cast<number_unsigned_t>(x);
+            n_chars = count_digits(abs_value);
+        }
+
+        // spare 1 byte for '\0'
+        JSON_ASSERT(n_chars < number_buffer.size() - 1);
+
+        // jump to the end to generate the string from backward,
+        // so we later avoid reversing the result
+        buffer_ptr += n_chars;
+
+        // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
+        // See: https://www.youtube.com/watch?v=o4-CwDo2zpg
+        while (abs_value >= 100)
+        {
+            const auto digits_index = static_cast<unsigned>((abs_value % 100));
+            abs_value /= 100;
+            *(--buffer_ptr) = digits_to_99[digits_index][1];
+            *(--buffer_ptr) = digits_to_99[digits_index][0];
+        }
+
+        if (abs_value >= 10)
+        {
+            const auto digits_index = static_cast<unsigned>(abs_value);
+            *(--buffer_ptr) = digits_to_99[digits_index][1];
+            *(--buffer_ptr) = digits_to_99[digits_index][0];
+        }
+        else
+        {
+            *(--buffer_ptr) = static_cast<char>('0' + abs_value);
+        }
+
+        o->write_characters(number_buffer.data(), n_chars);
+    }
+
+    /*!
+    @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(number_float_t x)
+    {
+        // NaN / inf
+        if (!std::isfinite(x))
+        {
+            o->write_characters("null", 4);
+            return;
+        }
+
+        // If number_float_t is an IEEE-754 single or double precision number,
+        // use the Grisu2 algorithm to produce short numbers which are
+        // guaranteed to round-trip, using strtof and strtod, resp.
+        //
+        // NB: The test below works if <long double> == <double>.
+        static constexpr bool is_ieee_single_or_double
+            = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||
+              (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);
+
+        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
+    }
+
+    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
+    {
+        auto* begin = number_buffer.data();
+        auto* end = ::wpi::detail::to_chars(begin, begin + number_buffer.size(), x);
+
+        o->write_characters(begin, static_cast<size_t>(end - begin));
+    }
+
+    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
+    {
+        // get number of digits for a float -> text -> float round-trip
+        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
+
+        // the actual conversion
+        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
+
+        // negative value indicates an error
+        JSON_ASSERT(len > 0);
+        // check if buffer was large enough
+        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
+
+        // erase thousands separator
+        if (thousands_sep != '\0')
+        {
+            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
+            const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
+            std::fill(end, number_buffer.end(), '\0');
+            JSON_ASSERT((end - number_buffer.begin()) <= len);
+            len = (end - number_buffer.begin());
+        }
+
+        // convert decimal point to '.'
+        if (decimal_point != '\0' && decimal_point != '.')
+        {
+            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
+            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
+            if (dec_pos != number_buffer.end())
+            {
+                *dec_pos = '.';
+            }
+        }
+
+        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
+
+        // determine if we need to append ".0"
+        const bool value_is_int_like =
+            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
+                         [](char c)
+        {
+            return c == '.' || c == 'e';
+        });
+
+        if (value_is_int_like)
+        {
+            o->write_characters(".0", 2);
+        }
+    }
+
+  private:
+    /*!
+    @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 std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
+    {
+        static const std::array<std::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
+            }
+        };
+
+        JSON_ASSERT(byte < utf8d.size());
+        const std::uint8_t type = utf8d[byte];
+
+        codep = (state != UTF8_ACCEPT)
+                ? (byte & 0x3fu) | (codep << 6u)
+                : (0xFFu >> type) & (byte);
+
+        std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
+        JSON_ASSERT(index < 400);
+        state = utf8d[index];
+        return state;
+    }
+
+    /*
+     * Overload to make the compiler happy while it is instantiating
+     * dump_integer for number_unsigned_t.
+     * Must never be called.
+     */
+    number_unsigned_t remove_sign(number_unsigned_t x)
+    {
+        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        return x; // LCOV_EXCL_LINE
+    }
+
+    /*
+     * Helper function for dump_integer
+     *
+     * This function takes a negative signed integer and returns its absolute
+     * value as unsigned integer. The plus/minus shuffling is necessary as we can
+     * not directly remove the sign of an arbitrary signed integer as the
+     * absolute values of INT_MIN and INT_MAX are usually not the same. See
+     * #1708 for details.
+     */
+    inline number_unsigned_t remove_sign(number_integer_t x) noexcept
+    {
+        JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
+        return static_cast<number_unsigned_t>(-(x + 1)) + 1;
+    }
+
+  private:
+    /// the output of the serializer
+    output_adapter_t<char> o = nullptr;
+
+    /// a (hopefully) large enough character buffer
+    std::array<char, 64> number_buffer{{}};
+
+    /// the locale
+    const std::lconv* loc = nullptr;
+    /// the locale's thousand separator character
+    const char thousands_sep = '\0';
+    /// the locale's decimal point character
+    const char decimal_point = '\0';
+
+    /// string buffer
+    std::array<char, 512> string_buffer{{}};
+
+    /// the indentation character
+    const char indent_char;
+    /// the indentation string
+    string_t indent_string;
+
+    /// error_handler how to react on decoding errors
+    const error_handler_t error_handler;
+};
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/string_concat.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/string_concat.h
new file mode 100644
index 0000000..972e664
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/string_concat.h
@@ -0,0 +1,146 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstring> // strlen
+#include <string> // string
+#include <utility> // forward
+
+#include <wpi/detail/meta/cpp_future.h>
+#include <wpi/detail/meta/detected.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+inline std::size_t concat_length()
+{
+    return 0;
+}
+
+template<typename... Args>
+inline std::size_t concat_length(const char* cstr, Args&& ... rest);
+
+template<typename StringType, typename... Args>
+inline std::size_t concat_length(const StringType& str, Args&& ... rest);
+
+template<typename... Args>
+inline std::size_t concat_length(const char /*c*/, Args&& ... rest)
+{
+    return 1 + concat_length(std::forward<Args>(rest)...);
+}
+
+template<typename... Args>
+inline std::size_t concat_length(const char* cstr, Args&& ... rest)
+{
+    // cppcheck-suppress ignoredReturnValue
+    return ::strlen(cstr) + concat_length(std::forward<Args>(rest)...);
+}
+
+template<typename StringType, typename... Args>
+inline std::size_t concat_length(const StringType& str, Args&& ... rest)
+{
+    return str.size() + concat_length(std::forward<Args>(rest)...);
+}
+
+template<typename OutStringType>
+inline void concat_into(OutStringType& /*out*/)
+{}
+
+template<typename StringType, typename Arg>
+using string_can_append = decltype(std::declval<StringType&>().append(std::declval < Arg && > ()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append = is_detected<string_can_append, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_op = decltype(std::declval<StringType&>() += std::declval < Arg && > ());
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_op = is_detected<string_can_append_op, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_iter = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().begin(), std::declval<const Arg&>().end()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_iter = is_detected<string_can_append_iter, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_data = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().data(), std::declval<const Arg&>().size()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_data = is_detected<string_can_append_data, StringType, Arg>;
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && detect_string_can_append_op<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest);
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && detect_string_can_append_iter<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && !detect_string_can_append_iter<OutStringType, Arg>::value
+                         && detect_string_can_append_data<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
+
+template<typename OutStringType, typename Arg, typename... Args,
+         enable_if_t<detect_string_can_append<OutStringType, Arg>::value, int> = 0>
+inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest)
+{
+    out.append(std::forward<Arg>(arg));
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && detect_string_can_append_op<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest)
+{
+    out += std::forward<Arg>(arg);
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && detect_string_can_append_iter<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
+{
+    out.append(arg.begin(), arg.end());
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && !detect_string_can_append_iter<OutStringType, Arg>::value
+                         && detect_string_can_append_data<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
+{
+    out.append(arg.data(), arg.size());
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template<typename OutStringType = std::string, typename... Args>
+inline OutStringType concat(Args && ... args)
+{
+    OutStringType str;
+    str.reserve(concat_length(std::forward<Args>(args)...));
+    concat_into(str, std::forward<Args>(args)...);
+    return str;
+}
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/string_escape.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/string_escape.h
new file mode 100644
index 0000000..2e508e6
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/string_escape.h
@@ -0,0 +1,72 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <wpi/detail/abi_macros.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief replace all occurrences of a substring by another string
+
+@param[in,out] s  the string to manipulate; changed so that all
+               occurrences of @a f are replaced with @a t
+@param[in]     f  the substring to replace with @a t
+@param[in]     t  the string to replace @a f
+
+@pre The search string @a f must not be empty. **This precondition is
+enforced with an assertion.**
+
+@since version 2.0.0
+*/
+template<typename StringType>
+inline void replace_substring(StringType& s, const StringType& f,
+                              const StringType& t)
+{
+    JSON_ASSERT(!f.empty());
+    for (auto pos = s.find(f);                // find first occurrence of f
+            pos != StringType::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
+    {}
+}
+
+/*!
+ * @brief string escaping as described in RFC 6901 (Sect. 4)
+ * @param[in] s string to escape
+ * @return    escaped string
+ *
+ * Note the order of escaping "~" to "~0" and "/" to "~1" is important.
+ */
+template<typename StringType>
+inline StringType escape(StringType s)
+{
+    replace_substring(s, StringType{"~"}, StringType{"~0"});
+    replace_substring(s, StringType{"/"}, StringType{"~1"});
+    return s;
+}
+
+/*!
+ * @brief string unescaping as described in RFC 6901 (Sect. 4)
+ * @param[in] s string to unescape
+ * @return    unescaped string
+ *
+ * Note the order of escaping "~1" to "/" and "~0" to "~" is important.
+ */
+template<typename StringType>
+static void unescape(StringType& s)
+{
+    replace_substring(s, StringType{"~1"}, StringType{"/"});
+    replace_substring(s, StringType{"~0"}, StringType{"~"});
+}
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/value_t.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/value_t.h
new file mode 100644
index 0000000..8366f00
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/detail/value_t.h
@@ -0,0 +1,118 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t
+#include <string> // string
+
+#include <wpi/detail/macro_scope.h>
+#if JSON_HAS_THREE_WAY_COMPARISON
+    #include <compare> // partial_ordering
+#endif
+
+WPI_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////////////
+// JSON type enumeration //
+///////////////////////////
+
+/*!
+@brief the JSON type enumeration
+
+This enumeration collects the different JSON types. It is internally used to
+distinguish the stored values, and the functions @ref basic_json::is_null(),
+@ref basic_json::is_object(), @ref basic_json::is_array(),
+@ref basic_json::is_string(), @ref basic_json::is_boolean(),
+@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
+@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
+@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
+@ref basic_json::is_structured() rely on it.
+
+@note There are three enumeration entries (number_integer, number_unsigned, and
+number_float), because the library distinguishes these three types for numbers:
+@ref basic_json::number_unsigned_t is used for unsigned integers,
+@ref basic_json::number_integer_t is used for signed integers, and
+@ref basic_json::number_float_t is used for floating-point numbers or to
+approximate integers which do not fit in the limits of their respective type.
+
+@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON
+value with the default value for a given type
+
+@since version 1.0.0
+*/
+enum class value_t : std::uint8_t
+{
+    null,             ///< null value
+    object,           ///< object (unordered set of name/value pairs)
+    array,            ///< array (ordered collection of values)
+    string,           ///< string value
+    boolean,          ///< boolean value
+    number_integer,   ///< number value (signed integer)
+    number_unsigned,  ///< number value (unsigned integer)
+    number_float,     ///< number value (floating-point)
+    binary,           ///< binary array (ordered collection of bytes)
+    discarded         ///< discarded by the parser callback function
+};
+
+/*!
+@brief comparison operator for JSON types
+
+Returns an ordering that is similar to Python:
+- order: null < boolean < number < object < array < string < binary
+- furthermore, each type is not smaller than itself
+- discarded values are not comparable
+- binary is represented as a b"" string in python and directly comparable to a
+  string; however, making a binary array directly comparable with a string would
+  be surprising behavior in a JSON file.
+
+@since version 1.0.0
+*/
+#if JSON_HAS_THREE_WAY_COMPARISON
+    inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*
+#else
+    inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+#endif
+{
+    static constexpr std::array<std::uint8_t, 9> order = {{
+            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
+            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
+            6 /* binary */
+        }
+    };
+
+    const auto l_index = static_cast<std::size_t>(lhs);
+    const auto r_index = static_cast<std::size_t>(rhs);
+#if JSON_HAS_THREE_WAY_COMPARISON
+    if (l_index < order.size() && r_index < order.size())
+    {
+        return order[l_index] <=> order[r_index]; // *NOPAD*
+    }
+    return std::partial_ordering::unordered;
+#else
+    return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
+#endif
+}
+
+// GCC selects the built-in operator< over an operator rewritten from
+// a user-defined spaceship operator
+// Clang, MSVC, and ICC select the rewritten candidate
+// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
+#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+{
+    return std::is_lt(lhs <=> rhs); // *NOPAD*
+}
+#endif
+
+}  // namespace detail
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/json.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/json.h
index 1c07deb..47ff86a 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/json.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/json.h
@@ -1,2638 +1,82 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications 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.                                                               */
-/*----------------------------------------------------------------------------*/
-/*
-    __ _____ _____ _____
- __|  |   __|     |   | |  JSON for Modern C++
-|  |  |__   |  |  | | | |  version 3.1.2
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
 
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
+/****************************************************************************\
+ * Note on documentation: The source files contain links to the online      *
+ * documentation of the public API at https://json.nlohmann.me. This URL    *
+ * contains the most recent documentation and should also be applicable to  *
+ * previous versions; documentation for deprecated functions is not         *
+ * removed, but marked deprecated. See "Generate documentation" section in  *
+ * file docs/README.md.                                                     *
+\****************************************************************************/
 
-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:
+#ifndef INCLUDE_WPI_JSON_HPP_
+#define INCLUDE_WPI_JSON_HPP_
 
-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.
-*/
-
-#ifndef WPIUTIL_JSON_H
-#define WPIUTIL_JSON_H
-
-#define NLOHMANN_JSON_VERSION_MAJOR 3
-#define NLOHMANN_JSON_VERSION_MINOR 1
-#define NLOHMANN_JSON_VERSION_PATCH 2
-
-
-#include <algorithm> // all_of, copy, find, for_each, generate_n, min, reverse, remove, fill, none_of, transform
-#include <array> // array
-#include <cassert> // assert
+#include <algorithm> // all_of, find, for_each
 #include <cstddef> // nullptr_t, ptrdiff_t, size_t
-#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
-#include <exception> // exception
-#include <functional> // function, hash, less
+#include <functional> // hash, less
 #include <initializer_list> // initializer_list
-#include <iterator>
-#include <limits> // numeric_limits
-#include <memory> // allocator, shared_ptr, make_shared, addressof
-#include <span>
-#include <stdexcept> // runtime_error
-#include <string> // string, char_traits, stoi, to_string
-#include <string_view>
-#include <tuple> // tuple, get, make_tuple
-#include <type_traits>
-#include <utility>
+#ifndef JSON_NO_IO
+    #include <iosfwd> // istream, ostream
+#endif  // JSON_NO_IO
+#include <iterator> // random_access_iterator_tag
+#include <memory> // unique_ptr
+#include <numeric> // accumulate
+#include <string> // string, stoi, to_string
+#include <utility> // declval, forward, move, pair, swap
 #include <vector> // vector
 
-#include "wpi/StringMap.h"
+#include <wpi/adl_serializer.h>
+#include <wpi/byte_container_with_subtype.h>
+#include <wpi/detail/conversions/from_json.h>
+#include <wpi/detail/conversions/to_json.h>
+#include <wpi/detail/exceptions.h>
+#include <wpi/detail/hash.h>
+#include <wpi/detail/input/binary_reader.h>
+#include <wpi/detail/input/input_adapters.h>
+#include <wpi/detail/input/lexer.h>
+#include <wpi/detail/input/parser.h>
+#include <wpi/detail/iterators/internal_iterator.h>
+#include <wpi/detail/iterators/iter_impl.h>
+#include <wpi/detail/iterators/iteration_proxy.h>
+#include <wpi/detail/iterators/json_reverse_iterator.h>
+#include <wpi/detail/iterators/primitive_iterator.h>
+#include <wpi/detail/json_pointer.h>
+#include <wpi/detail/json_ref.h>
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/string_concat.h>
+#include <wpi/detail/string_escape.h>
+#include <wpi/detail/meta/cpp_future.h>
+#include <wpi/detail/meta/type_traits.h>
+#include <wpi/detail/output/binary_writer.h>
+#include <wpi/detail/output/output_adapters.h>
+#include <wpi/detail/output/serializer.h>
+#include <wpi/detail/value_t.h>
+#include <wpi/json_fwd.h>
+#include <wpi/ordered_map.h>
 
-namespace wpi
-{
-
-class raw_istream;
-class raw_ostream;
-
-class JsonTest;
+#if defined(JSON_HAS_CPP_17)
+    #include <any>
+    #include <string_view>
+#endif
 
 /*!
-@brief default JSONSerializer template argument
-
-This serializer ignores the template arguments and uses ADL
-([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl))
-for serialization.
-*/
-template<typename = void, typename = void>
-struct adl_serializer;
-
-/*!
-@brief JSON Pointer
-
-A JSON pointer defines a string syntax for identifying a specific value
-within a JSON document. It can be used with functions `at` and
-`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
-
-@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
-
-@since version 2.0.0
-*/
-class json_pointer;
-
-/*!
-@brief default JSON class
-
-This type is the default specialization of the @ref json class which
-uses the standard template types.
-
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
 @since version 1.0.0
 */
-class json;
-}
-
-// exclude unsupported compilers
-#if defined(__clang__)
-    #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
-        #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
-    #endif
-#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
-    #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
-        #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
-    #endif
-#endif
-
-// disable float-equal warnings on GCC/clang
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
-    #pragma GCC diagnostic push
-    #pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-
-// disable documentation warnings on clang
-#if defined(__clang__)
-    #pragma GCC diagnostic push
-    #pragma GCC diagnostic ignored "-Wdocumentation"
-#endif
-
-// allow to disable exceptions
-#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
-    #define JSON_THROW(exception) throw exception
-    #define JSON_TRY try
-    #define JSON_CATCH(exception) catch(exception)
-#else
-    #define JSON_THROW(exception) std::abort()
-    #define JSON_TRY if(true)
-    #define JSON_CATCH(exception) if(false)
-#endif
-
-// override exception macros
-#if defined(JSON_THROW_USER)
-    #undef JSON_THROW
-    #define JSON_THROW JSON_THROW_USER
-#endif
-#if defined(JSON_TRY_USER)
-    #undef JSON_TRY
-    #define JSON_TRY JSON_TRY_USER
-#endif
-#if defined(JSON_CATCH_USER)
-    #undef JSON_CATCH
-    #define JSON_CATCH JSON_CATCH_USER
-#endif
-
-// manual branch prediction
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
-    #define JSON_LIKELY(x)      __builtin_expect(!!(x), 1)
-    #define JSON_UNLIKELY(x)    __builtin_expect(!!(x), 0)
-#else
-    #define JSON_LIKELY(x)      x
-    #define JSON_UNLIKELY(x)    x
-#endif
-
-/*!
-@brief Helper to determine whether there's a key_type for T.
-
-This helper is used to tell associative containers apart from other containers
-such as sequence containers. For instance, `std::map` passes the test as it
-contains a `mapped_type`, whereas `std::vector` fails the test.
-
-@sa http://stackoverflow.com/a/7728728/266378
-@since version 1.0.0, overworked in version 2.0.6
-*/
-#define NLOHMANN_JSON_HAS_HELPER(type)                                        \
-    template<typename T> struct has_##type {                                  \
-    private:                                                                  \
-        template<typename U, typename = typename U::type>                     \
-        static int detect(U &&);                                              \
-        static void detect(...);                                              \
-    public:                                                                   \
-        static constexpr bool value =                                         \
-                std::is_integral<decltype(detect(std::declval<T>()))>::value; \
-    }
-
-namespace wpi
-{
-/*!
-@brief detail namespace with internal helper functions
-
-This namespace collects functions that should not be exposed,
-implementations of some @ref json methods, and meta-programming helpers.
-
-@since version 2.1.0
-*/
-namespace detail
-{
-/////////////
-// helpers //
-/////////////
-
-template<typename> struct is_json : std::false_type {};
-
-template<> struct is_json<json> : std::true_type {};
-
-// alias templates to reduce boilerplate
-template<bool B, typename T = void>
-using enable_if_t = typename std::enable_if<B, T>::type;
-
-template<typename T>
-using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
-
-// dispatch utility (taken from ranges-v3)
-template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
-template<> struct priority_tag<0> {};
-
-////////////////////////
-// has_/is_ functions //
-////////////////////////
-
-// source: https://stackoverflow.com/a/37193089/4116453
-
-template <typename T, typename = void>
-struct is_complete_type : std::false_type {};
-
-template <typename T>
-struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
-
-NLOHMANN_JSON_HAS_HELPER(mapped_type);
-NLOHMANN_JSON_HAS_HELPER(key_type);
-NLOHMANN_JSON_HAS_HELPER(value_type);
-NLOHMANN_JSON_HAS_HELPER(iterator);
-
-template<bool B, class RealType, class CompatibleObjectType>
-struct is_compatible_object_type_impl : std::false_type {};
-
-template<class RealType, class CompatibleObjectType>
-struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType>
-{
-    static constexpr auto value =
-        std::is_constructible<std::string_view, typename CompatibleObjectType::key_type>::value and
-        std::is_constructible<typename RealType::mapped_type, typename CompatibleObjectType::mapped_type>::value;
-};
-
-template<class BasicJsonType, class CompatibleObjectType>
-struct is_compatible_object_type
-{
-    static auto constexpr value = is_compatible_object_type_impl <
-                                  std::conjunction<std::negation<std::is_same<void, CompatibleObjectType>>,
-                                  has_mapped_type<CompatibleObjectType>,
-                                  has_key_type<CompatibleObjectType>>::value,
-                                  typename BasicJsonType::object_t, CompatibleObjectType >::value;
-};
-
-template<typename BasicJsonType, typename T>
-struct is_json_nested_type
-{
-    static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
-                                  std::is_same<T, typename BasicJsonType::const_iterator>::value or
-                                  std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
-                                  std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value;
-};
-
-template<class BasicJsonType, class CompatibleArrayType>
-struct is_compatible_array_type
-{
-    static auto constexpr value =
-        std::conjunction<std::negation<std::is_same<void, CompatibleArrayType>>,
-        std::negation<is_compatible_object_type<
-        BasicJsonType, CompatibleArrayType>>,
-        std::negation<std::is_constructible<std::string_view,
-        CompatibleArrayType>>,
-        std::negation<is_json_nested_type<BasicJsonType, CompatibleArrayType>>,
-        has_value_type<CompatibleArrayType>,
-        has_iterator<CompatibleArrayType>>::value;
-};
-
-template<bool, typename, typename>
-struct is_compatible_integer_type_impl : std::false_type {};
-
-template<typename RealIntegerType, typename CompatibleNumberIntegerType>
-struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType>
-{
-    // is there an assert somewhere on overflows?
-    using RealLimits = std::numeric_limits<RealIntegerType>;
-    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
-
-    static constexpr auto value =
-        std::is_constructible<RealIntegerType, CompatibleNumberIntegerType>::value and
-        CompatibleLimits::is_integer and
-        RealLimits::is_signed == CompatibleLimits::is_signed;
-};
-
-template<typename RealIntegerType, typename CompatibleNumberIntegerType>
-struct is_compatible_integer_type
-{
-    static constexpr auto value =
-        is_compatible_integer_type_impl <
-        std::is_integral<CompatibleNumberIntegerType>::value and
-        not std::is_same<bool, CompatibleNumberIntegerType>::value,
-        RealIntegerType, CompatibleNumberIntegerType > ::value;
-};
-
-// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
-template<typename BasicJsonType, typename T>
-struct has_from_json
-{
-  private:
-    // also check the return type of from_json
-    template<typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json(
-                 std::declval<BasicJsonType>(), std::declval<T&>()))>::value>>
-    static int detect(U&&);
-    static void detect(...);
-
-  public:
-    static constexpr bool value = std::is_integral<decltype(
-                                      detect(std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
-};
-
-// This trait checks if JSONSerializer<T>::from_json(json const&) exists
-// this overload is used for non-default-constructible user-defined-types
-template<typename BasicJsonType, typename T>
-struct has_non_default_from_json
-{
-  private:
-    template <
-        typename U,
-        typename = enable_if_t<std::is_same<
-                                   T, decltype(uncvref_t<U>::from_json(std::declval<BasicJsonType>()))>::value >>
-    static int detect(U&&);
-    static void detect(...);
-
-  public:
-    static constexpr bool value = std::is_integral<decltype(detect(
-                                      std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
-};
-
-// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
-template<typename BasicJsonType, typename T>
-struct has_to_json
-{
-  private:
-    template<typename U, typename = decltype(uncvref_t<U>::to_json(
-                 std::declval<BasicJsonType&>(), std::declval<T>()))>
-    static int detect(U&&);
-    static void detect(...);
-
-  public:
-    static constexpr bool value = std::is_integral<decltype(detect(
-                                      std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
-};
-
-template <typename BasicJsonType, typename CompatibleCompleteType>
-struct is_compatible_complete_type
-{
-    static constexpr bool value =
-        not std::is_base_of<std::istream, CompatibleCompleteType>::value and
-        not is_json<CompatibleCompleteType>::value and
-        not is_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and
-        has_to_json<BasicJsonType, CompatibleCompleteType>::value;
-};
-
-template <typename BasicJsonType, typename CompatibleType>
-struct is_compatible_type
-    : std::conjunction<is_complete_type<CompatibleType>,
-      is_compatible_complete_type<BasicJsonType, CompatibleType>>
-{
-};
-
-// taken from ranges-v3
-template<typename T>
-struct static_const
-{
-    static constexpr T value{};
-};
-
-template<typename T>
-constexpr T static_const<T>::value;
-
-////////////////
-// exceptions //
-////////////////
-
-/*!
-@brief general exception of the @ref json class
-
-This class is an extension of `std::exception` objects with a member @a id for
-exception ids. It is used as the base class for all exceptions thrown by the
-@ref json class. This class can hence be used as "wildcard" to catch
-exceptions.
-
-Subclasses:
-- @ref parse_error for exceptions indicating a parse error
-- @ref invalid_iterator for exceptions indicating errors with iterators
-- @ref type_error for exceptions indicating executing a member function with
-                  a wrong type
-- @ref out_of_range for exceptions indicating access out of the defined range
-- @ref other_error for exceptions indicating other library errors
-
-@internal
-@note To have nothrow-copy-constructible exceptions, we internally use
-      `std::runtime_error` which can cope with arbitrary-length error messages.
-      Intermediate strings are built with static functions and then passed to
-      the actual constructor.
-@endinternal
-
-@liveexample{The following code shows how arbitrary library exceptions can be
-caught.,exception}
-
-@since version 3.0.0
-*/
-class exception : public std::exception
-{
-  public:
-    /// returns the explanatory string
-    const char* what() const noexcept override
-    {
-        return m.what();
-    }
-
-    /// the id of the exception
-    const int id;
-
-  protected:
-    exception(int id_, std::string_view what_arg);
-
-  private:
-    /// an exception object as storage for error messages
-    std::runtime_error m;
-};
-
-/*!
-@brief exception indicating a parse error
-
-This exception is thrown by the library when a parse error occurs. Parse errors
-can occur during the deserialization of JSON text, CBOR, MessagePack, as well
-as when using JSON Patch.
-
-Member @a byte holds the byte index of the last read character in the input
-file.
-
-Exceptions have ids 1xx.
-
-name / id                      | example message | description
------------------------------- | --------------- | -------------------------
-json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.
-json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.
-json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.
-json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.
-json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors.
-json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.
-json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.
-json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.
-json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.
-json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.
-json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.
-json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.
-
-@note For an input with n bytes, 1 is the index of the first character and n+1
-      is the index of the terminating null byte or the end of file. This also
-      holds true when reading a byte vector (CBOR or MessagePack).
-
-@liveexample{The following code shows how a `parse_error` exception can be
-caught.,parse_error}
-
-@sa @ref exception for the base class of the library exceptions
-@sa @ref invalid_iterator for exceptions indicating errors with iterators
-@sa @ref type_error for exceptions indicating executing a member function with
-                    a wrong type
-@sa @ref out_of_range for exceptions indicating access out of the defined range
-@sa @ref other_error for exceptions indicating other library errors
-
-@since version 3.0.0
-*/
-class parse_error : public exception
-{
-  public:
-    /*!
-    @brief create a parse error exception
-    @param[in] id_       the id of the exception
-    @param[in] byte_     the byte index where the error occurred (or 0 if the
-                         position cannot be determined)
-    @param[in] what_arg  the explanatory string
-    @return parse_error object
-    */
-    static parse_error create(int id_, std::size_t byte_, std::string_view what_arg);
-
-    /*!
-    @brief byte index of the parse error
-
-    The byte index of the last read character in the input file.
-
-    @note For an input with n bytes, 1 is the index of the first character and
-          n+1 is the index of the terminating null byte or the end of file.
-          This also holds true when reading a byte vector (CBOR or MessagePack).
-    */
-    const std::size_t byte;
-
-  private:
-    parse_error(int id_, std::size_t byte_, std::string_view what_arg)
-        : exception(id_, what_arg), byte(byte_) {}
-};
-
-/*!
-@brief exception indicating errors with iterators
-
-This exception is thrown if iterators passed to a library function do not match
-the expected semantics.
-
-Exceptions have ids 2xx.
-
-name / id                           | example message | description
------------------------------------ | --------------- | -------------------------
-json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
-json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
-json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.
-json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.
-json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.
-json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.
-json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.
-json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
-json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
-json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
-json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.
-json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.
-json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.
-json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().
-
-@liveexample{The following code shows how an `invalid_iterator` exception can be
-caught.,invalid_iterator}
-
-@sa @ref exception for the base class of the library exceptions
-@sa @ref parse_error for exceptions indicating a parse error
-@sa @ref type_error for exceptions indicating executing a member function with
-                    a wrong type
-@sa @ref out_of_range for exceptions indicating access out of the defined range
-@sa @ref other_error for exceptions indicating other library errors
-
-@since version 3.0.0
-*/
-class invalid_iterator : public exception
-{
-  public:
-    static invalid_iterator create(int id_, std::string_view what_arg);
-    static invalid_iterator create(int id_, std::string_view what_arg, std::string_view type_info);
-
-  private:
-    invalid_iterator(int id_, std::string_view what_arg)
-        : exception(id_, what_arg) {}
-};
-
-/*!
-@brief exception indicating executing a member function with a wrong type
-
-This exception is thrown in case of a type error; that is, a library function is
-executed on a JSON value whose type does not match the expected semantics.
-
-Exceptions have ids 3xx.
-
-name / id                     | example message | description
------------------------------ | --------------- | -------------------------
-json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.
-json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.
-json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&.
-json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.
-json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.
-json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.
-json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.
-json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.
-json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.
-json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.
-json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.
-json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.
-json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.
-json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
-json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
-json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
-
-@liveexample{The following code shows how a `type_error` exception can be
-caught.,type_error}
-
-@sa @ref exception for the base class of the library exceptions
-@sa @ref parse_error for exceptions indicating a parse error
-@sa @ref invalid_iterator for exceptions indicating errors with iterators
-@sa @ref out_of_range for exceptions indicating access out of the defined range
-@sa @ref other_error for exceptions indicating other library errors
-
-@since version 3.0.0
-*/
-class type_error : public exception
-{
-  public:
-    static type_error create(int id_, std::string_view what_arg);
-    static type_error create(int id_, std::string_view what_arg, std::string_view type_info);
-
-  private:
-    type_error(int id_, std::string_view what_arg) : exception(id_, what_arg) {}
-};
-
-/*!
-@brief exception indicating access out of the defined range
-
-This exception is thrown in case a library function is called on an input
-parameter that exceeds the expected range, for instance in case of array
-indices or nonexisting object keys.
-
-Exceptions have ids 4xx.
-
-name / id                       | example message | description
-------------------------------- | --------------- | -------------------------
-json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.
-json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.
-json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.
-json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
-json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
-json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
-json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON only supports integers numbers up to 9223372036854775807. |
-json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |
-
-@liveexample{The following code shows how an `out_of_range` exception can be
-caught.,out_of_range}
-
-@sa @ref exception for the base class of the library exceptions
-@sa @ref parse_error for exceptions indicating a parse error
-@sa @ref invalid_iterator for exceptions indicating errors with iterators
-@sa @ref type_error for exceptions indicating executing a member function with
-                    a wrong type
-@sa @ref other_error for exceptions indicating other library errors
-
-@since version 3.0.0
-*/
-class out_of_range : public exception
-{
-  public:
-    static out_of_range create(int id_, std::string_view what_arg);
-
-  private:
-    out_of_range(int id_, std::string_view what_arg) : exception(id_, what_arg) {}
-};
-
-/*!
-@brief exception indicating other library errors
-
-This exception is thrown in case of errors that cannot be classified with the
-other exception types.
-
-Exceptions have ids 5xx.
-
-name / id                      | example message | description
------------------------------- | --------------- | -------------------------
-json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.
-
-@sa @ref exception for the base class of the library exceptions
-@sa @ref parse_error for exceptions indicating a parse error
-@sa @ref invalid_iterator for exceptions indicating errors with iterators
-@sa @ref type_error for exceptions indicating executing a member function with
-                    a wrong type
-@sa @ref out_of_range for exceptions indicating access out of the defined range
-
-@liveexample{The following code shows how an `other_error` exception can be
-caught.,other_error}
-
-@since version 3.0.0
-*/
-class other_error : public exception
-{
-  public:
-    static other_error create(int id_, std::string_view what_arg);
-
-  private:
-    other_error(int id_, std::string_view what_arg) : exception(id_, what_arg) {}
-};
-
-///////////////////////////
-// JSON type enumeration //
-///////////////////////////
-
-/*!
-@brief the JSON type enumeration
-
-This enumeration collects the different JSON types. It is internally used to
-distinguish the stored values, and the functions @ref json::is_null(),
-@ref json::is_object(), @ref json::is_array(),
-@ref json::is_string(), @ref json::is_boolean(),
-@ref json::is_number() (with @ref json::is_number_integer(),
-@ref json::is_number_unsigned(), and @ref json::is_number_float()),
-@ref json::is_discarded(), @ref json::is_primitive(), and
-@ref json::is_structured() rely on it.
-
-@note There are three enumeration entries (number_integer, number_unsigned, and
-number_float), because the library distinguishes these three types for numbers:
-uint64_t is used for unsigned integers,
-int64_t is used for signed integers, and
-double is used for floating-point numbers or to
-approximate integers which do not fit in the limits of their respective type.
-
-@sa @ref json::json(const value_t value_type) -- create a JSON
-value with the default value for a given type
-
-@since version 1.0.0
-*/
-enum class value_t : std::uint8_t
-{
-    null,             ///< null value
-    object,           ///< object (unordered set of name/value pairs)
-    array,            ///< array (ordered collection of values)
-    string,           ///< string value
-    boolean,          ///< boolean value
-    number_integer,   ///< number value (signed integer)
-    number_unsigned,  ///< number value (unsigned integer)
-    number_float,     ///< number value (floating-point)
-    discarded         ///< discarded by the the parser callback function
-};
-
-/*!
-@brief comparison operator for JSON types
-
-Returns an ordering that is similar to Python:
-- order: null < boolean < number < object < array < string
-- furthermore, each type is not smaller than itself
-- discarded values are not comparable
-
-@since version 1.0.0
-*/
-inline bool operator<(const value_t lhs, const value_t rhs) noexcept
-{
-    static constexpr std::array<std::uint8_t, 8> order = {{
-            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
-            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */
-        }
-    };
-
-    const auto l_index = static_cast<std::size_t>(lhs);
-    const auto r_index = static_cast<std::size_t>(rhs);
-    return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];
-}
-
-// overloads for json template parameters
-template<typename BasicJsonType, typename ArithmeticType,
-         enable_if_t<std::is_arithmetic<ArithmeticType>::value and
-                     not std::is_same<ArithmeticType, bool>::value,
-                     int> = 0>
-void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
-{
-    switch (static_cast<value_t>(j))
-    {
-        case value_t::number_unsigned:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const uint64_t*>());
-            break;
-        }
-        case value_t::number_integer:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const int64_t*>());
-            break;
-        }
-        case value_t::number_float:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const double*>());
-            break;
-        }
-
-        default:
-            JSON_THROW(type_error::create(302, "type must be number, but is", j.type_name()));
-    }
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, bool& b)
-{
-    if (JSON_UNLIKELY(not j.is_boolean()))
-    {
-        JSON_THROW(type_error::create(302, "type must be boolean, but is", j.type_name()));
-    }
-    b = *j.template get_ptr<const bool*>();
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, std::string& s)
-{
-    if (JSON_UNLIKELY(not j.is_string()))
-    {
-        JSON_THROW(type_error::create(302, "type must be string, but is", j.type_name()));
-    }
-    s = *j.template get_ptr<const std::string*>();
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, double& val)
-{
-    get_arithmetic_value(j, val);
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, uint64_t& val)
-{
-    get_arithmetic_value(j, val);
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, int64_t& val)
-{
-    get_arithmetic_value(j, val);
-}
-
-template<typename BasicJsonType, typename EnumType,
-         enable_if_t<std::is_enum<EnumType>::value, int> = 0>
-void from_json(const BasicJsonType& j, EnumType& e)
-{
-    typename std::underlying_type<EnumType>::type val;
-    get_arithmetic_value(j, val);
-    e = static_cast<EnumType>(val);
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
-{
-    if (JSON_UNLIKELY(not j.is_array()))
-    {
-        JSON_THROW(type_error::create(302, "type must be array, but is", j.type_name()));
-    }
-    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
-}
-
-template<typename BasicJsonType, typename CompatibleArrayType>
-void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/)
-{
-    using std::end;
-
-    std::transform(j.begin(), j.end(),
-                   std::inserter(arr, end(arr)), [](const BasicJsonType & i)
-    {
-        // get<BasicJsonType>() returns *this, this won't call a from_json
-        // method when value_type is BasicJsonType
-        return i.template get<typename CompatibleArrayType::value_type>();
-    });
-}
-
-template<typename BasicJsonType, typename CompatibleArrayType>
-auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/)
--> decltype(
-    arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
-    void())
-{
-    using std::end;
-
-    arr.reserve(j.size());
-    std::transform(j.begin(), j.end(),
-                   std::inserter(arr, end(arr)), [](const BasicJsonType & i)
-    {
-        // get<BasicJsonType>() returns *this, this won't call a from_json
-        // method when value_type is BasicJsonType
-        return i.template get<typename CompatibleArrayType::value_type>();
-    });
-}
-
-template<typename BasicJsonType, typename T, std::size_t N>
-void from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, priority_tag<2> /*unused*/)
-{
-    for (std::size_t i = 0; i < N; ++i)
-    {
-        arr[i] = j.at(i).template get<T>();
-    }
-}
-
-template <
-    typename BasicJsonType, typename CompatibleArrayType,
-    enable_if_t <
-        is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and
-        not std::is_same<typename BasicJsonType::array_t,
-                         CompatibleArrayType>::value and
-        std::is_constructible <
-            BasicJsonType, typename CompatibleArrayType::value_type >::value,
-        int > = 0 >
-void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
-{
-    if (JSON_UNLIKELY(not j.is_array()))
-    {
-        JSON_THROW(type_error::create(302, "type must be array, but is", j.type_name()));
-    }
-
-    from_json_array_impl(j, arr, priority_tag<2> {});
-}
-
-template<typename BasicJsonType>
-inline
-void from_json(const BasicJsonType& j, typename BasicJsonType::object_t& obj)
-{
-    if (!j.is_object())
-    {
-        JSON_THROW(type_error::create(302, "type must be object, but is", j.type_name()));
-    }
-
-    auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
-    for (const auto& i : *inner_object) {
-        obj.try_emplace(i.first(), i.second);
-    }
-}
-
-template<typename BasicJsonType, typename CompatibleObjectType,
-         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value and
-                     not std::is_same<typename BasicJsonType::object_t, CompatibleObjectType>::value, int> = 0>
-void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
-{
-    if (JSON_UNLIKELY(not j.is_object()))
-    {
-        JSON_THROW(type_error::create(302, "type must be object, but is", j.type_name()));
-    }
-
-    auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
-    using std::begin;
-    using std::end;
-    using value_type = typename CompatibleObjectType::value_type;
-    std::vector<value_type> v;
-    v.reserve(j.size());
-    for (const auto& p : *inner_object)
-    {
-        v.emplace_back(
-            p.first(),
-            p.second
-            .template get<typename CompatibleObjectType::mapped_type>());
-    }
-    // we could avoid the assignment, but this might require a for loop, which
-    // might be less efficient than the container constructor for some
-    // containers (would it?)
-    obj = CompatibleObjectType(std::make_move_iterator(begin(v)),
-                               std::make_move_iterator(end(v)));
-}
-
-// overload for arithmetic types, not chosen for json template arguments
-// (BooleanType, etc..); note: Is it really necessary to provide explicit
-// overloads for bool etc. in case of a custom BooleanType which is not
-// an arithmetic type?
-template<typename BasicJsonType, typename ArithmeticType,
-         enable_if_t <
-             std::is_arithmetic<ArithmeticType>::value and
-             not std::is_same<ArithmeticType, uint64_t>::value and
-             not std::is_same<ArithmeticType, int64_t>::value and
-             not std::is_same<ArithmeticType, double>::value and
-             not std::is_same<ArithmeticType, bool>::value,
-             int> = 0>
-void from_json(const BasicJsonType& j, ArithmeticType& val)
-{
-    switch (static_cast<value_t>(j))
-    {
-        case value_t::number_unsigned:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const uint64_t*>());
-            break;
-        }
-        case value_t::number_integer:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const int64_t*>());
-            break;
-        }
-        case value_t::number_float:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const double*>());
-            break;
-        }
-        case value_t::boolean:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const bool*>());
-            break;
-        }
-
-        default:
-            JSON_THROW(type_error::create(302, "type must be number, but is", j.type_name()));
-    }
-}
-
-template<typename BasicJsonType, typename A1, typename A2>
-void from_json(const BasicJsonType& j, std::pair<A1, A2>& p)
-{
-    p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};
-}
-
-template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
-void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, std::index_sequence<Idx...>)
-{
-    t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);
-}
-
-template<typename BasicJsonType, typename... Args>
-void from_json(const BasicJsonType& j, std::tuple<Args...>& t)
-{
-    from_json_tuple_impl(j, t, std::index_sequence_for<Args...> {});
-}
-
-struct from_json_fn
-{
-  private:
-    template<typename BasicJsonType, typename T>
-    auto call(const BasicJsonType& j, T& val, priority_tag<1> /*unused*/) const
-    noexcept(noexcept(from_json(j, val)))
-    -> decltype(from_json(j, val), void())
-    {
-        return from_json(j, val);
-    }
-
-    template<typename BasicJsonType, typename T>
-    void call(const BasicJsonType& /*unused*/, T& /*unused*/, priority_tag<0> /*unused*/) const noexcept
-    {
-        static_assert(sizeof(BasicJsonType) == 0,
-                      "could not find from_json() method in T's namespace");
-#ifdef _MSC_VER
-        // MSVC does not show a stacktrace for the above assert
-        using decayed = uncvref_t<T>;
-        static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
-                      "forcing MSVC stacktrace to show which T we're talking about.");
-#endif
-    }
-
-  public:
-    template<typename BasicJsonType, typename T>
-    void operator()(const BasicJsonType& j, T& val) const
-    noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1> {})))
-    {
-        return call(j, val, priority_tag<1> {});
-    }
-};
-}
-
-// namespace to hold default `from_json` function
-// to see why this is required:
-// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
-namespace
-{
-constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
-}
-
-namespace detail
-{
-//////////////////
-// constructors //
-//////////////////
-
-template<value_t> struct external_constructor;
-
-template<>
-struct external_constructor<value_t::boolean>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, bool b) noexcept
-    {
-        j.m_type = value_t::boolean;
-        j.m_value = b;
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::string>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, std::string_view s)
-    {
-        j.m_type = value_t::string;
-        j.m_value = s;
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType, typename T,
-             enable_if_t<std::is_same<std::string, T>::value, int> = 0>
-    static void construct(BasicJsonType& j, T&& s)
-    {
-        j.m_type = value_t::string;
-        j.m_value = std::move(s);
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::number_float>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, double val) noexcept
-    {
-        j.m_type = value_t::number_float;
-        j.m_value = val;
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::number_unsigned>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, uint64_t val) noexcept
-    {
-        j.m_type = value_t::number_unsigned;
-        j.m_value = val;
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::number_integer>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, int64_t val) noexcept
-    {
-        j.m_type = value_t::number_integer;
-        j.m_value = val;
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::array>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
-    {
-        j.m_type = value_t::array;
-        j.m_value = arr;
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
-    {
-        j.m_type = value_t::array;
-        j.m_value = std::move(arr);
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType, typename T>
-    static void construct(BasicJsonType& j, std::span<T> arr)
-    {
-        using std::begin;
-        using std::end;
-        j.m_type = value_t::array;
-        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType, typename CompatibleArrayType,
-             enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
-                         int> = 0>
-    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
-    {
-        using std::begin;
-        using std::end;
-        j.m_type = value_t::array;
-        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, const std::vector<bool>& arr)
-    {
-        j.m_type = value_t::array;
-        j.m_value = value_t::array;
-        j.m_value.array->reserve(arr.size());
-        for (const bool x : arr)
-        {
-            j.m_value.array->push_back(x);
-        }
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::object>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
-    {
-        j.m_type = value_t::object;
-        j.m_value = obj;
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
-    {
-        j.m_type = value_t::object;
-        j.m_value = std::move(obj);
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType, typename CompatibleObjectType,
-             enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0>
-    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
-    {
-        j.m_type = value_t::object;
-        j.m_value = value_t::object;
-        for (const auto& x : obj)
-        {
-            j.m_value.object->try_emplace(x.first, x.second);
-        }
-        j.assert_invariant();
-    }
-};
-
-/////////////
-// to_json //
-/////////////
-
-template<typename BasicJsonType, typename T,
-         enable_if_t<std::is_same<T, bool>::value, int> = 0>
-void to_json(BasicJsonType& j, T b) noexcept
-{
-    external_constructor<value_t::boolean>::construct(j, b);
-}
-
-template<typename BasicJsonType, typename CompatibleString,
-         enable_if_t<std::is_constructible<std::string_view, CompatibleString>::value, int> = 0>
-void to_json(BasicJsonType& j, const CompatibleString& s)
-{
-    external_constructor<value_t::string>::construct(j, s);
-}
-
-template<typename BasicJsonType, typename T,
-         enable_if_t<std::is_same<std::string, T>::value, int> = 0>
-void to_json(BasicJsonType& j, T&& s)
-{
-    external_constructor<value_t::string>::construct(j, std::move(s));
-}
-
-template<typename BasicJsonType, typename FloatType,
-         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
-void to_json(BasicJsonType& j, FloatType val) noexcept
-{
-    external_constructor<value_t::number_float>::construct(j, static_cast<double>(val));
-}
-
-template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
-         enable_if_t<is_compatible_integer_type<uint64_t, CompatibleNumberUnsignedType>::value, int> = 0>
-void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
-{
-    external_constructor<value_t::number_unsigned>::construct(j, static_cast<uint64_t>(val));
-}
-
-template<typename BasicJsonType, typename CompatibleNumberIntegerType,
-         enable_if_t<is_compatible_integer_type<int64_t, CompatibleNumberIntegerType>::value, int> = 0>
-void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
-{
-    external_constructor<value_t::number_integer>::construct(j, static_cast<int64_t>(val));
-}
-
-template<typename BasicJsonType, typename EnumType,
-         enable_if_t<std::is_enum<EnumType>::value, int> = 0>
-void to_json(BasicJsonType& j, EnumType e) noexcept
-{
-    using underlying_type = typename std::underlying_type<EnumType>::type;
-    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
-}
-
-template<typename BasicJsonType>
-void to_json(BasicJsonType& j, const std::vector<bool>& e)
-{
-    external_constructor<value_t::array>::construct(j, e);
-}
-
-template<typename BasicJsonType, typename CompatibleArrayType,
-         enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value or
-                     std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value,
-                     int> = 0>
-void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
-{
-    external_constructor<value_t::array>::construct(j, arr);
-}
-
-template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
-{
-    external_constructor<value_t::array>::construct(j, std::move(arr));
-}
-
-template<typename BasicJsonType, typename CompatibleObjectType,
-         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
-void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
-{
-    external_constructor<value_t::object>::construct(j, obj);
-}
-
-template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
-{
-    external_constructor<value_t::object>::construct(j, std::move(obj));
-}
-
-template<typename BasicJsonType, typename T, std::size_t N,
-         enable_if_t<not std::is_constructible<std::string_view, T (&)[N]>::value, int> = 0>
-void to_json(BasicJsonType& j, T (&arr)[N])
-{
-    external_constructor<value_t::array>::construct(j, arr);
-}
-
-template<typename BasicJsonType, typename... Args>
-void to_json(BasicJsonType& j, const std::pair<Args...>& p)
-{
-    j = {p.first, p.second};
-}
-
-template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
-void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, std::index_sequence<Idx...>)
-{
-    j = {std::get<Idx>(t)...};
-}
-
-template<typename BasicJsonType, typename... Args>
-void to_json(BasicJsonType& j, const std::tuple<Args...>& t)
-{
-    to_json_tuple_impl(j, t, std::index_sequence_for<Args...> {});
-}
-
-struct to_json_fn
-{
-  private:
-    template<typename BasicJsonType, typename T>
-    auto call(BasicJsonType& j, T&& val, priority_tag<1> /*unused*/) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
-    -> decltype(to_json(j, std::forward<T>(val)), void())
-    {
-        return to_json(j, std::forward<T>(val));
-    }
-
-    template<typename BasicJsonType, typename T>
-    void call(BasicJsonType& /*unused*/, T&& /*unused*/, priority_tag<0> /*unused*/) const noexcept
-    {
-        static_assert(sizeof(BasicJsonType) == 0,
-                      "could not find to_json() method in T's namespace");
-
-#ifdef _MSC_VER
-        // MSVC does not show a stacktrace for the above assert
-        using decayed = uncvref_t<T>;
-        static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
-                      "forcing MSVC stacktrace to show which T we're talking about.");
-#endif
-    }
-
-  public:
-    template<typename BasicJsonType, typename T>
-    void operator()(BasicJsonType& j, T&& val) const
-    noexcept(noexcept(std::declval<to_json_fn>().call(j, std::forward<T>(val), priority_tag<1> {})))
-    {
-        return call(j, std::forward<T>(val), priority_tag<1> {});
-    }
-};
-}
-
-// namespace to hold default `to_json` function
-namespace
-{
-constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
-}
-
-namespace detail
-{
-/*
-@brief an iterator for primitive JSON types
-
-This class models an iterator for primitive JSON types (boolean, number,
-string). It's only purpose is to allow the iterator/const_iterator classes
-to "iterate" over primitive values. Internally, the iterator is modeled by
-a `difference_type` variable. Value begin_value (`0`) models the begin,
-end_value (`1`) models past the end.
-*/
-class primitive_iterator_t
-{
-  private:
-    using difference_type = std::ptrdiff_t;
-    static constexpr difference_type begin_value = 0;
-    static constexpr difference_type end_value = begin_value + 1;
-
-    /// iterator as signed integer type
-    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
-
-  public:
-    constexpr difference_type get_value() const noexcept
-    {
-        return m_it;
-    }
-
-    /// set iterator to a defined beginning
-    void set_begin() noexcept
-    {
-        m_it = begin_value;
-    }
-
-    /// set iterator to a defined past the end
-    void set_end() noexcept
-    {
-        m_it = end_value;
-    }
-
-    /// return whether the iterator can be dereferenced
-    constexpr bool is_begin() const noexcept
-    {
-        return m_it == begin_value;
-    }
-
-    /// return whether the iterator is at end
-    constexpr bool is_end() const noexcept
-    {
-        return m_it == end_value;
-    }
-
-    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
-    {
-        return lhs.m_it == rhs.m_it;
-    }
-
-    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
-    {
-        return lhs.m_it < rhs.m_it;
-    }
-
-    primitive_iterator_t operator+(difference_type n) noexcept
-    {
-        auto result = *this;
-        result += n;
-        return result;
-    }
-
-    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
-    {
-        return lhs.m_it - rhs.m_it;
-    }
-
-    primitive_iterator_t& operator++() noexcept
-    {
-        ++m_it;
-        return *this;
-    }
-
-    primitive_iterator_t const operator++(int) noexcept
-    {
-        auto result = *this;
-        m_it++;
-        return result;
-    }
-
-    primitive_iterator_t& operator--() noexcept
-    {
-        --m_it;
-        return *this;
-    }
-
-    primitive_iterator_t const operator--(int) noexcept
-    {
-        auto result = *this;
-        m_it--;
-        return result;
-    }
-
-    primitive_iterator_t& operator+=(difference_type n) noexcept
-    {
-        m_it += n;
-        return *this;
-    }
-
-    primitive_iterator_t& operator-=(difference_type n) noexcept
-    {
-        m_it -= n;
-        return *this;
-    }
-};
-
-/*!
-@brief an iterator value
-
-@note This structure could easily be a union, but MSVC currently does not allow
-unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
-*/
-template<typename BasicJsonType> struct internal_iterator
-{
-    /// iterator for JSON objects
-    typename BasicJsonType::object_t::iterator object_iterator {};
-    /// iterator for JSON arrays
-    typename BasicJsonType::array_t::iterator array_iterator {};
-    /// generic iterator for all other types
-    primitive_iterator_t primitive_iterator {};
-};
-
-// forward declare, to be able to friend it later on
-template<typename IteratorType> class iteration_proxy;
-
-/*!
-@brief a template for a bidirectional iterator for the @ref json class
-
-This class implements a both iterators (iterator and const_iterator) for the
-@ref json class.
-
-@note An iterator is called *initialized* when a pointer to a JSON value has
-      been set (e.g., by a constructor or a copy assignment). If the iterator is
-      default-constructed, it is *uninitialized* and most methods are undefined.
-      **The library uses assertions to detect calls on uninitialized iterators.**
-
-@requirement The class satisfies the following concept requirements:
--
-[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator):
-  The iterator that can be moved can be moved in both directions (i.e.
-  incremented and decremented).
-
-@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
-       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
-*/
-template<typename BasicJsonType>
-class iter_impl
-{
-    /// allow json to access private members
-    friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
-    friend BasicJsonType;
-    friend iteration_proxy<iter_impl>;
-    friend class ::wpi::JsonTest;
-
-    using object_t = typename BasicJsonType::object_t;
-    using array_t = typename BasicJsonType::array_t;
-    // make sure BasicJsonType is json or const json
-    static_assert(is_json<typename std::remove_const<BasicJsonType>::type>::value,
-                  "iter_impl only accepts (const) json");
-
-  public:
-
-    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
-    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
-    /// A user-defined iterator should provide publicly accessible typedefs named
-    /// iterator_category, value_type, difference_type, pointer, and reference.
-    /// Note that value_type is required to be non-const, even for constant iterators.
-    using iterator_category = std::bidirectional_iterator_tag;
-
-    /// the type of the values when the iterator is dereferenced
-    using value_type = typename BasicJsonType::value_type;
-    /// a type to represent differences between iterators
-    using difference_type = typename BasicJsonType::difference_type;
-    /// defines a pointer to the type iterated over (value_type)
-    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
-          typename BasicJsonType::const_pointer,
-          typename BasicJsonType::pointer>::type;
-    /// defines a reference to the type iterated over (value_type)
-    using reference =
-        typename std::conditional<std::is_const<BasicJsonType>::value,
-        typename BasicJsonType::const_reference,
-        typename BasicJsonType::reference>::type;
-
-    /// default constructor
-    iter_impl() = default;
-
-    /*!
-    @brief constructor for a given JSON instance
-    @param[in] object  pointer to a JSON object for this iterator
-    @pre object != nullptr
-    @post The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    explicit iter_impl(pointer object) noexcept : m_object(object)
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                m_it.object_iterator = typename object_t::iterator();
-                break;
-            }
-
-            case value_t::array:
-            {
-                m_it.array_iterator = typename array_t::iterator();
-                break;
-            }
-
-            default:
-            {
-                m_it.primitive_iterator = primitive_iterator_t();
-                break;
-            }
-        }
-    }
-
-    /*!
-    @note The conventional copy constructor and copy assignment are implicitly
-          defined. Combined with the following converting constructor and
-          assignment, they support: (1) copy from iterator to iterator, (2)
-          copy from const iterator to const iterator, and (3) conversion from
-          iterator to const iterator. However conversion from const iterator
-          to iterator is not defined.
-    */
-
-    /*!
-    @brief converting constructor
-    @param[in] other  non-const iterator to copy from
-    @note It is not checked whether @a other is initialized.
-    */
-    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
-        : m_object(other.m_object), m_it(other.m_it) {}
-
-    /*!
-    @brief converting assignment
-    @param[in,out] other  non-const iterator to copy from
-    @return const/non-const iterator
-    @note It is not checked whether @a other is initialized.
-    */
-    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
-    {
-        m_object = other.m_object;
-        m_it = other.m_it;
-        return *this;
-    }
-
-  private:
-    /*!
-    @brief set the iterator to the first value
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    void set_begin() noexcept
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                m_it.object_iterator = m_object->m_value.object->begin();
-                break;
-            }
-
-            case value_t::array:
-            {
-                m_it.array_iterator = m_object->m_value.array->begin();
-                break;
-            }
-
-            case value_t::null:
-            {
-                // set to end so begin()==end() is true: null is empty
-                m_it.primitive_iterator.set_end();
-                break;
-            }
-
-            default:
-            {
-                m_it.primitive_iterator.set_begin();
-                break;
-            }
-        }
-    }
-
-    /*!
-    @brief set the iterator past the last value
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    void set_end() noexcept
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                m_it.object_iterator = m_object->m_value.object->end();
-                break;
-            }
-
-            case value_t::array:
-            {
-                m_it.array_iterator = m_object->m_value.array->end();
-                break;
-            }
-
-            default:
-            {
-                m_it.primitive_iterator.set_end();
-                break;
-            }
-        }
-    }
-
-  public:
-    /*!
-    @brief return a reference to the value pointed to by the iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    reference operator*() const
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                assert(m_it.object_iterator != m_object->m_value.object->end());
-                return m_it.object_iterator->second;
-            }
-
-            case value_t::array:
-            {
-                assert(m_it.array_iterator != m_object->m_value.array->end());
-                return *m_it.array_iterator;
-            }
-
-            case value_t::null:
-                JSON_THROW(invalid_iterator::create(214, "cannot get value"));
-
-            default:
-            {
-                if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
-                {
-                    return *m_object;
-                }
-
-                JSON_THROW(invalid_iterator::create(214, "cannot get value"));
-            }
-        }
-    }
-
-    /*!
-    @brief dereference the iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    pointer operator->() const
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                assert(m_it.object_iterator != m_object->m_value.object->end());
-                return &(m_it.object_iterator->second);
-            }
-
-            case value_t::array:
-            {
-                assert(m_it.array_iterator != m_object->m_value.array->end());
-                return &*m_it.array_iterator;
-            }
-
-            default:
-            {
-                if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
-                {
-                    return m_object;
-                }
-
-                JSON_THROW(invalid_iterator::create(214, "cannot get value"));
-            }
-        }
-    }
-
-    /*!
-    @brief post-increment (it++)
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl const operator++(int)
-    {
-        auto result = *this;
-        ++(*this);
-        return result;
-    }
-
-    /*!
-    @brief pre-increment (++it)
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl& operator++()
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                ++m_it.object_iterator;
-                break;
-            }
-
-            case value_t::array:
-            {
-                std::advance(m_it.array_iterator, 1);
-                break;
-            }
-
-            default:
-            {
-                ++m_it.primitive_iterator;
-                break;
-            }
-        }
-
-        return *this;
-    }
-
-    /*!
-    @brief post-decrement (it--)
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl const operator--(int)
-    {
-        auto result = *this;
-        --(*this);
-        return result;
-    }
-
-    /*!
-    @brief pre-decrement (--it)
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl& operator--()
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                --m_it.object_iterator;
-                break;
-            }
-
-            case value_t::array:
-            {
-                std::advance(m_it.array_iterator, -1);
-                break;
-            }
-
-            default:
-            {
-                --m_it.primitive_iterator;
-                break;
-            }
-        }
-
-        return *this;
-    }
-
-    /*!
-    @brief  comparison: equal
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator==(const iter_impl& other) const
-    {
-        // if objects are not the same, the comparison is undefined
-        if (JSON_UNLIKELY(m_object != other.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
-        }
-
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-                return (m_it.object_iterator == other.m_it.object_iterator);
-
-            case value_t::array:
-                return (m_it.array_iterator == other.m_it.array_iterator);
-
-            default:
-                return (m_it.primitive_iterator == other.m_it.primitive_iterator);
-        }
-    }
-
-    /*!
-    @brief  comparison: not equal
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator!=(const iter_impl& other) const
-    {
-        return not operator==(other);
-    }
-
-    /*!
-    @brief  comparison: smaller
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator<(const iter_impl& other) const
-    {
-        // if objects are not the same, the comparison is undefined
-        if (JSON_UNLIKELY(m_object != other.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
-        }
-
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-                JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators"));
-
-            case value_t::array:
-                return (m_it.array_iterator < other.m_it.array_iterator);
-
-            default:
-                return (m_it.primitive_iterator < other.m_it.primitive_iterator);
-        }
-    }
-
-    /*!
-    @brief  comparison: less than or equal
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator<=(const iter_impl& other) const
-    {
-        return not other.operator < (*this);
-    }
-
-    /*!
-    @brief  comparison: greater than
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator>(const iter_impl& other) const
-    {
-        return not operator<=(other);
-    }
-
-    /*!
-    @brief  comparison: greater than or equal
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator>=(const iter_impl& other) const
-    {
-        return not operator<(other);
-    }
-
-    /*!
-    @brief  add to iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl& operator+=(difference_type i)
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-                JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
-
-            case value_t::array:
-            {
-                std::advance(m_it.array_iterator, i);
-                break;
-            }
-
-            default:
-            {
-                m_it.primitive_iterator += i;
-                break;
-            }
-        }
-
-        return *this;
-    }
-
-    /*!
-    @brief  subtract from iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl& operator-=(difference_type i)
-    {
-        return operator+=(-i);
-    }
-
-    /*!
-    @brief  add to iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl operator+(difference_type i) const
-    {
-        auto result = *this;
-        result += i;
-        return result;
-    }
-
-    /*!
-    @brief  addition of distance and iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    friend iter_impl operator+(difference_type i, const iter_impl& it)
-    {
-        auto result = it;
-        result += i;
-        return result;
-    }
-
-    /*!
-    @brief  subtract from iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl operator-(difference_type i) const
-    {
-        auto result = *this;
-        result -= i;
-        return result;
-    }
-
-    /*!
-    @brief  return difference
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    difference_type operator-(const iter_impl& other) const
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-                JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
-
-            case value_t::array:
-                return m_it.array_iterator - other.m_it.array_iterator;
-
-            default:
-                return m_it.primitive_iterator - other.m_it.primitive_iterator;
-        }
-    }
-
-    /*!
-    @brief  access to successor
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    reference operator[](difference_type n) const
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-                JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators"));
-
-            case value_t::array:
-                return *std::next(m_it.array_iterator, n);
-
-            case value_t::null:
-                JSON_THROW(invalid_iterator::create(214, "cannot get value"));
-
-            default:
-            {
-                if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n))
-                {
-                    return *m_object;
-                }
-
-                JSON_THROW(invalid_iterator::create(214, "cannot get value"));
-            }
-        }
-    }
-
-    /*!
-    @brief  return the key of an object iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    std::string_view key() const
-    {
-        assert(m_object != nullptr);
-
-        if (JSON_LIKELY(m_object->is_object()))
-        {
-            return m_it.object_iterator->first();
-        }
-
-        JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators"));
-    }
-
-    /*!
-    @brief  return the value of an iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    reference value() const
-    {
-        return operator*();
-    }
-
-  private:
-    /// associated JSON instance
-    pointer m_object = nullptr;
-    /// the actual iterator of the associated instance
-    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it;
-};
-
-/// proxy class for the items() function
-template<typename IteratorType> class iteration_proxy
-{
-  private:
-    /// helper class for iteration
-    class iteration_proxy_internal
-    {
-      private:
-        /// the iterator
-        IteratorType anchor;
-        /// an index for arrays (used to create key names)
-        std::size_t array_index = 0;
-
-      public:
-        explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {}
-
-        /// dereference operator (needed for range-based for)
-        iteration_proxy_internal& operator*()
-        {
-            return *this;
-        }
-
-        /// increment operator (needed for range-based for)
-        iteration_proxy_internal& operator++()
-        {
-            ++anchor;
-            ++array_index;
-
-            return *this;
-        }
-
-        /// inequality operator (needed for range-based for)
-        bool operator!=(const iteration_proxy_internal& o) const noexcept
-        {
-            return anchor != o.anchor;
-        }
-
-        /// return key of the iterator
-        std::string key() const
-        {
-            assert(anchor.m_object != nullptr);
-
-            switch (anchor.m_object->type())
-            {
-                // use integer array index as key
-                case value_t::array:
-                    return std::to_string(array_index);
-
-                // use key from the object
-                case value_t::object:
-                    return std::string{anchor.key()};
-
-                // use an empty key for all primitive types
-                default:
-                    return "";
-            }
-        }
-
-        /// return value of the iterator
-        typename IteratorType::reference value() const
-        {
-            return anchor.value();
-        }
-    };
-
-    /// the container to iterate
-    typename IteratorType::reference container;
-
-  public:
-    /// construct iteration proxy from a container
-    explicit iteration_proxy(typename IteratorType::reference cont) noexcept
-        : container(cont) {}
-
-    /// return iterator begin (needed for range-based for)
-    iteration_proxy_internal begin() noexcept
-    {
-        return iteration_proxy_internal(container.begin());
-    }
-
-    /// return iterator end (needed for range-based for)
-    iteration_proxy_internal end() noexcept
-    {
-        return iteration_proxy_internal(container.end());
-    }
-};
-
-//////////////////////
-// reverse_iterator //
-//////////////////////
-
-/*!
-@brief a template for a reverse iterator class
-
-@tparam Base the base iterator type to reverse. Valid types are @ref
-iterator (to create @ref reverse_iterator) and @ref const_iterator (to
-create @ref const_reverse_iterator).
-
-@requirement The class satisfies the following concept requirements:
--
-[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator):
-  The iterator that can be moved can be moved in both directions (i.e.
-  incremented and decremented).
-- [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator):
-  It is possible to write to the pointed-to element (only if @a Base is
-  @ref iterator).
-
-@since version 1.0.0
-*/
-template<typename Base>
-class json_reverse_iterator : public std::reverse_iterator<Base>
-{
-  public:
-    using difference_type = std::ptrdiff_t;
-    /// shortcut to the reverse iterator adapter
-    using base_iterator = std::reverse_iterator<Base>;
-    /// the reference type for the pointed-to element
-    using reference = typename Base::reference;
-
-    /// create reverse iterator from iterator
-    json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
-        : base_iterator(it) {}
-
-    /// create reverse iterator from base class
-    json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
-
-    /// post-increment (it++)
-    json_reverse_iterator const operator++(int)
-    {
-        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
-    }
-
-    /// pre-increment (++it)
-    json_reverse_iterator& operator++()
-    {
-        return static_cast<json_reverse_iterator&>(base_iterator::operator++());
-    }
-
-    /// post-decrement (it--)
-    json_reverse_iterator const operator--(int)
-    {
-        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
-    }
-
-    /// pre-decrement (--it)
-    json_reverse_iterator& operator--()
-    {
-        return static_cast<json_reverse_iterator&>(base_iterator::operator--());
-    }
-
-    /// add to iterator
-    json_reverse_iterator& operator+=(difference_type i)
-    {
-        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
-    }
-
-    /// add to iterator
-    json_reverse_iterator operator+(difference_type i) const
-    {
-        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
-    }
-
-    /// subtract from iterator
-    json_reverse_iterator operator-(difference_type i) const
-    {
-        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
-    }
-
-    /// return difference
-    difference_type operator-(const json_reverse_iterator& other) const
-    {
-        return base_iterator(*this) - base_iterator(other);
-    }
-
-    /// access to successor
-    reference operator[](difference_type n) const
-    {
-        return *(this->operator+(n));
-    }
-
-    /// return the key of an object iterator
-    auto key() const -> decltype(std::declval<Base>().key())
-    {
-        auto it = --this->base();
-        return it.key();
-    }
-
-    /// return the value of an iterator
-    reference value() const
-    {
-        auto it = --this->base();
-        return it.operator * ();
-    }
-};
-
-template<typename BasicJsonType>
-class json_ref
-{
-  public:
-    using value_type = BasicJsonType;
-
-    json_ref(value_type&& value)
-        : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)
-    {}
-
-    json_ref(const value_type& value)
-        : value_ref(const_cast<value_type*>(&value)), is_rvalue(false)
-    {}
-
-    json_ref(std::initializer_list<json_ref> init)
-        : owned_value(init), value_ref(&owned_value), is_rvalue(true)
-    {}
-
-    template<class... Args>
-    json_ref(Args&& ... args)
-        : owned_value(std::forward<Args>(args)...), value_ref(&owned_value), is_rvalue(true)
-    {}
-
-    // class should be movable only
-    json_ref(json_ref&&) = default;
-    json_ref(const json_ref&) = delete;
-    json_ref& operator=(const json_ref&) = delete;
-
-    value_type moved_or_copied() const
-    {
-        if (is_rvalue)
-        {
-            return std::move(*value_ref);
-        }
-        return *value_ref;
-    }
-
-    value_type const& operator*() const
-    {
-        return *static_cast<value_type const*>(value_ref);
-    }
-
-    value_type const* operator->() const
-    {
-        return static_cast<value_type const*>(value_ref);
-    }
-
-  private:
-    mutable value_type owned_value = nullptr;
-    value_type* value_ref = nullptr;
-    const bool is_rvalue;
-};
-}  // namespace detail
-
-class json_pointer
-{
-    // allow json to access private members
-    friend class json;
-    friend class JsonTest;
-
-  public:
-    /*!
-    @brief create JSON pointer
-
-    Create a JSON pointer according to the syntax described in
-    [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
-
-    @param[in] s  string representing the JSON pointer; if omitted, the empty
-                  string is assumed which references the whole JSON value
-
-    @throw parse_error.107 if the given JSON pointer @a s is nonempty and does
-                           not begin with a slash (`/`); see example below
-
-    @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is
-    not followed by `0` (representing `~`) or `1` (representing `/`); see
-    example below
-
-    @liveexample{The example shows the construction several valid JSON pointers
-    as well as the exceptional behavior.,json_pointer}
-
-    @since version 2.0.0
-    */
-    explicit json_pointer(std::string_view s = {})
-        : reference_tokens(split(s))
-    {}
-
-    /*!
-    @brief return a string representation of the JSON pointer
-
-    @invariant For each JSON pointer `ptr`, it holds:
-    @code {.cpp}
-    ptr == json_pointer(ptr.to_string());
-    @endcode
-
-    @return a string representation of the JSON pointer
-
-    @liveexample{The example shows the result of `to_string`.,
-    json_pointer__to_string}
-
-    @since version 2.0.0
-    */
-    std::string to_string() const noexcept;
-
-    /// @copydoc to_string()
-    operator std::string() const
-    {
-        return to_string();
-    }
-
-    /*!
-    @param[in] s  reference token to be converted into an array index
-
-    @return integer representation of @a s
-
-    @throw out_of_range.404 if string @a s could not be converted to an integer
-    */
-    static int array_index(std::string_view s);
-
-  private:
-    /*!
-    @brief remove and return last reference pointer
-    @throw out_of_range.405 if JSON pointer has no parent
-    */
-    std::string pop_back()
-    {
-        if (JSON_UNLIKELY(is_root()))
-        {
-            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
-        }
-
-        auto last = reference_tokens.back();
-        reference_tokens.pop_back();
-        return last;
-    }
-
-    /// return whether pointer points to the root document
-    bool is_root() const
-    {
-        return reference_tokens.empty();
-    }
-
-    json_pointer top() const
-    {
-        if (JSON_UNLIKELY(is_root()))
-        {
-            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
-        }
-
-        json_pointer result = *this;
-        result.reference_tokens = {reference_tokens[0]};
-        return result;
-    }
-
-    /*!
-    @brief create and return a reference to the pointed to value
-
-    @complexity Linear in the number of reference tokens.
-
-    @throw parse_error.109 if array index is not a number
-    @throw type_error.313 if value cannot be unflattened
-    */
-    json& get_and_create(json& j) const;
-
-    /*!
-    @brief return a reference to the pointed to value
-
-    @note This version does not throw if a value is not present, but tries to
-          create nested values instead. For instance, calling this function
-          with pointer `"/this/that"` on a null value is equivalent to calling
-          `operator[]("this").operator[]("that")` on that value, effectively
-          changing the null value to an object.
-
-    @param[in] ptr  a JSON value
-
-    @return reference to the JSON value pointed to by the JSON pointer
-
-    @complexity Linear in the length of the JSON pointer.
-
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-    */
-    json& get_unchecked(json* ptr) const;
-
-    /*!
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.402  if the array index '-' is used
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-    */
-    json& get_checked(json* ptr) const;
-
-    /*!
-    @brief return a const reference to the pointed to value
-
-    @param[in] ptr  a JSON value
-
-    @return const reference to the JSON value pointed to by the JSON
-    pointer
-
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.402  if the array index '-' is used
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-    */
-    const json& get_unchecked(const json* ptr) const;
-
-    /*!
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.402  if the array index '-' is used
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-    */
-    const json& get_checked(const json* ptr) const;
-
-    /*!
-    @brief split the string input to reference tokens
-
-    @note This function is only called by the json_pointer constructor.
-          All exceptions below are documented there.
-
-    @throw parse_error.107  if the pointer is not empty or begins with '/'
-    @throw parse_error.108  if character '~' is not followed by '0' or '1'
-    */
-    static std::vector<std::string> split(std::string_view reference_string);
-
-    /*!
-    @brief replace all occurrences of a substring by another string
-
-    @param[in,out] s  the string to manipulate; changed so that all
-                   occurrences of @a f are replaced with @a t
-    @param[in]     f  the substring to replace with @a t
-    @param[in]     t  the string to replace @a f
-
-    @pre The search string @a f must not be empty. **This precondition is
-    enforced with an assertion.**
-
-    @since version 2.0.0
-    */
-    static void replace_substring(std::string& s, const std::string& f,
-                                  const std::string& t);
-
-    /// escape "~"" to "~0" and "/" to "~1"
-    static std::string escape(std::string s);
-
-    /// unescape "~1" to tilde and "~0" to slash (order is important!)
-    static void unescape(std::string& s);
-
-    /*!
-    @param[in] reference_string  the reference string to the current value
-    @param[in] value             the value to consider
-    @param[in,out] result        the result object to insert values to
-
-    @note Empty objects or arrays are flattened to `null`.
-    */
-    static void flatten(std::string_view reference_string,
-                        const json& value,
-                        json& result);
-
-    /*!
-    @param[in] value  flattened JSON
-
-    @return unflattened JSON
-
-    @throw parse_error.109 if array index is not a number
-    @throw type_error.314  if value is not an object
-    @throw type_error.315  if object values are not primitive
-    @throw type_error.313  if value cannot be unflattened
-    */
-    static json
-    unflatten(const json& value);
-
-    friend bool operator==(json_pointer const& lhs,
-                           json_pointer const& rhs) noexcept
-    {
-        return (lhs.reference_tokens == rhs.reference_tokens);
-    }
-
-    friend bool operator!=(json_pointer const& lhs,
-                           json_pointer const& rhs) noexcept
-    {
-        return not (lhs == rhs);
-    }
-
-    /// the reference tokens
-    std::vector<std::string> reference_tokens;
-};
-
-template<typename, typename>
-struct adl_serializer
-{
-    /*!
-    @brief convert a JSON value to any value type
-
-    This function is usually called by the `get()` function of the
-    @ref json class (either explicit or via conversion operators).
-
-    @param[in] j         JSON value to read from
-    @param[in,out] val  value to write to
-    */
-    template<typename BasicJsonType, typename ValueType>
-    static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
-        noexcept(::wpi::from_json(std::forward<BasicJsonType>(j), val)))
-    {
-        ::wpi::from_json(std::forward<BasicJsonType>(j), val);
-    }
-
-    /*!
-    @brief convert any value type to a JSON value
-
-    This function is usually called by the constructors of the @ref json
-    class.
-
-    @param[in,out] j  JSON value to write to
-    @param[in] val     value to read from
-    */
-    template<typename BasicJsonType, typename ValueType>
-    static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
-        noexcept(::wpi::to_json(j, std::forward<ValueType>(val))))
-    {
-        ::wpi::to_json(j, std::forward<ValueType>(val));
-    }
-};
+WPI_JSON_NAMESPACE_BEGIN
 
 /*!
 @brief a class to store JSON values
 
-@requirement The class satisfies the following concept requirements:
-- Basic
- - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible):
-   JSON values can be default constructed. The result will be a JSON null
-   value.
- - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible):
-   A JSON value can be constructed from an rvalue argument.
- - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible):
-   A JSON value can be copy-constructed from an lvalue expression.
- - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable):
-   A JSON value van be assigned from an rvalue argument.
- - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable):
-   A JSON value can be copy-assigned from an lvalue expression.
- - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible):
-   JSON values can be destructed.
-- Layout
- - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType):
-   JSON values have
-   [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout):
-   All non-static data members are private and standard layout types, the
-   class has no virtual functions or (virtual) base classes.
-- Library-wide
- - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable):
-   JSON values can be compared with `==`, see @ref
-   operator==(const_reference,const_reference).
- - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable):
-   JSON values can be compared with `<`, see @ref
-   operator<(const_reference,const_reference).
- - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable):
-   Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of
-   other compatible types, using unqualified function call @ref swap().
- - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer):
-   JSON values can be compared against `std::nullptr_t` objects which are used
-   to model the `null` value.
-- Container
- - [Container](http://en.cppreference.com/w/cpp/concept/Container):
-   JSON values can be used like STL containers and provide iterator access.
- - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer);
-   JSON values can be used like STL containers and provide reverse iterator
-   access.
-
+@internal
 @invariant The member variables @a m_value and @a m_type have the following
 relationship:
 - If `m_type == value_t::object`, then `m_value.object != nullptr`.
@@ -2640,30 +84,59 @@
 - If `m_type == value_t::string`, then `m_value.string != nullptr`.
 The invariants are checked by member function assert_invariant().
 
-@internal
-@note ObjectType trick from http://stackoverflow.com/a/9860911
+@note ObjectType trick from https://stackoverflow.com/a/9860911
 @endinternal
 
-@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange
-Format](http://rfc7159.net/rfc7159)
-
 @since version 1.0.0
 
 @nosubgrouping
 */
-class json
+WPI_BASIC_JSON_TPL_DECLARATION
+class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
 {
   private:
     template<detail::value_t> friend struct detail::external_constructor;
-    friend ::wpi::json_pointer;
+
+    template<typename>
+    friend class ::wpi::json_pointer;
+    // can be restored when json_pointer backwards compatibility is removed
+    // friend ::wpi::json_pointer<StringType>;
+
+    template<typename BasicJsonType, typename InputType>
+    friend class ::wpi::detail::parser;
+    friend ::wpi::detail::serializer<basic_json>;
     template<typename BasicJsonType>
     friend class ::wpi::detail::iter_impl;
-    friend class JsonTest;
+    template<typename BasicJsonType, typename CharType>
+    friend class ::wpi::detail::binary_writer;
+    template<typename BasicJsonType, typename InputType, typename SAX>
+    friend class ::wpi::detail::binary_reader;
+    template<typename BasicJsonType>
+    friend class ::wpi::detail::json_sax_dom_parser;
+    template<typename BasicJsonType>
+    friend class ::wpi::detail::json_sax_dom_callback_parser;
+    friend class ::wpi::detail::exception;
 
     /// workaround type for MSVC
-    using json_t = json;
+    using basic_json_t = WPI_BASIC_JSON_TPL;
 
+  JSON_PRIVATE_UNLESS_TESTED:
     // convenience aliases for types residing in namespace detail;
+    using lexer = ::wpi::detail::lexer_base<basic_json>;
+
+    template<typename InputAdapterType>
+    static ::wpi::detail::parser<basic_json, InputAdapterType> parser(
+        InputAdapterType adapter,
+        detail::parser_callback_t<basic_json>cb = nullptr,
+        const bool allow_exceptions = true,
+        const bool ignore_comments = false
+    )
+    {
+        return ::wpi::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
+                std::move(cb), allow_exceptions, ignore_comments);
+    }
+
+  private:
     using primitive_iterator_t = ::wpi::detail::primitive_iterator_t;
     template<typename BasicJsonType>
     using internal_iterator = ::wpi::detail::internal_iterator<BasicJsonType>;
@@ -2673,21 +146,31 @@
     using iteration_proxy = ::wpi::detail::iteration_proxy<Iterator>;
     template<typename Base> using json_reverse_iterator = ::wpi::detail::json_reverse_iterator<Base>;
 
-    class binary_reader;
-    class binary_writer;
-    class lexer;
-    class parser;
+    template<typename CharType>
+    using output_adapter_t = ::wpi::detail::output_adapter_t<CharType>;
+
+    template<typename InputType>
+    using binary_reader = ::wpi::detail::binary_reader<basic_json, InputType>;
+    template<typename CharType> using binary_writer = ::wpi::detail::binary_writer<basic_json, CharType>;
 
   public:
-    class serializer;
+    using serializer = ::wpi::detail::serializer<basic_json>;
 
     using value_t = detail::value_t;
-    /// @copydoc wpi::json_pointer
-    using json_pointer = ::wpi::json_pointer;
+    /// JSON Pointer, see @ref wpi::json_pointer
+    using json_pointer = ::wpi::json_pointer<StringType>;
     template<typename T, typename SFINAE>
-    using json_serializer = adl_serializer<T, SFINAE>;
-    /// helper type for initializer lists of json values
-    using initializer_list_t = std::initializer_list<detail::json_ref<json>>;
+    using json_serializer = JSONSerializer<T, SFINAE>;
+    /// how to treat decoding errors
+    using error_handler_t = detail::error_handler_t;
+    /// how to treat CBOR tags
+    using cbor_tag_handler_t = detail::cbor_tag_handler_t;
+    /// helper type for initializer lists of basic_json values
+    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
+
+    using input_format_t = detail::input_format_t;
+    /// SAX interface type, see @ref wpi::json_sax
+    using json_sax_t = json_sax<basic_json>;
 
     ////////////////
     // exceptions //
@@ -2697,17 +180,11 @@
     /// Classes to implement user-defined exceptions.
     /// @{
 
-    /// @copydoc detail::exception
     using exception = detail::exception;
-    /// @copydoc detail::parse_error
     using parse_error = detail::parse_error;
-    /// @copydoc detail::invalid_iterator
     using invalid_iterator = detail::invalid_iterator;
-    /// @copydoc detail::type_error
     using type_error = detail::type_error;
-    /// @copydoc detail::out_of_range
     using out_of_range = detail::out_of_range;
-    /// @copydoc detail::other_error
     using other_error = detail::other_error;
 
     /// @}
@@ -2718,12 +195,12 @@
     /////////////////////
 
     /// @name container types
-    /// The canonic container types to use @ref json like any other STL
+    /// The canonic container types to use @ref basic_json like any other STL
     /// container.
     /// @{
 
-    /// the type of elements in a json container
-    using value_type = json;
+    /// the type of elements in a basic_json container
+    using value_type = basic_json;
 
     /// the type of an element reference
     using reference = value_type&;
@@ -2736,60 +213,97 @@
     using size_type = std::size_t;
 
     /// the allocator type
-    using allocator_type = std::allocator<json>;
+    using allocator_type = AllocatorType<basic_json>;
 
     /// the type of an element pointer
-    using pointer = json*;
+    using pointer = typename std::allocator_traits<allocator_type>::pointer;
     /// the type of an element const pointer
-    using const_pointer = const json*;
+    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
 
-    /// an iterator for a json container
-    using iterator = iter_impl<json>;
-    /// a const iterator for a json container
-    using const_iterator = iter_impl<const json>;
-    /// a reverse iterator for a json container
-    using reverse_iterator = json_reverse_iterator<typename json::iterator>;
-    /// a const reverse iterator for a json container
-    using const_reverse_iterator = json_reverse_iterator<typename json::const_iterator>;
+    /// an iterator for a basic_json container
+    using iterator = iter_impl<basic_json>;
+    /// a const iterator for a basic_json container
+    using const_iterator = iter_impl<const basic_json>;
+    /// a reverse iterator for a basic_json container
+    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;
+    /// a const reverse iterator for a basic_json container
+    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;
 
     /// @}
 
 
-    /*!
-    @brief returns the allocator associated with the container
-    */
+    /// @brief returns the allocator associated with the container
+    /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/
     static allocator_type get_allocator()
     {
         return allocator_type();
     }
 
-    /*!
-    @brief returns version information on the library
+    /// @brief returns version information on the library
+    /// @sa https://json.nlohmann.me/api/basic_json/meta/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json meta()
+    {
+        basic_json result;
 
-    This function returns a JSON object with information about the library,
-    including the version number and information on the platform and compiler.
+        result["copyright"] = "(C) 2013-2022 Niels Lohmann";
+        result["name"] = "JSON for Modern C++";
+        result["url"] = "https://github.com/nlohmann/json";
+        result["version"]["string"] =
+            detail::concat(std::to_string(WPI_JSON_VERSION_MAJOR), '.',
+                           std::to_string(WPI_JSON_VERSION_MINOR), '.',
+                           std::to_string(WPI_JSON_VERSION_PATCH));
+        result["version"]["major"] = WPI_JSON_VERSION_MAJOR;
+        result["version"]["minor"] = WPI_JSON_VERSION_MINOR;
+        result["version"]["patch"] = WPI_JSON_VERSION_PATCH;
 
-    @return JSON object holding version information
-    key         | description
-    ----------- | ---------------
-    `compiler`  | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).
-    `copyright` | The copyright line for the library as string.
-    `name`      | The name of the library as string.
-    `platform`  | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.
-    `url`       | The URL of the project as string.
-    `version`   | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).
+#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
 
-    @liveexample{The following code shows an example output of the `meta()`
-    function.,meta}
+#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", detail::concat(
+                    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
 
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
 
-    @complexity Constant.
-
-    @since 2.1.0
-    */
-    static json meta();
+#if defined(_MSVC_LANG)
+        result["compiler"]["c++"] = std::to_string(_MSVC_LANG);
+#elif defined(__cplusplus)
+        result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+        result["compiler"]["c++"] = "unknown";
+#endif
+        return result;
+    }
 
 
     ///////////////////////////
@@ -2798,96 +312,60 @@
 
     /// @name JSON value data types
     /// The data types to store a JSON value. These types are derived from
-    /// the template arguments passed to class @ref json.
+    /// the template arguments passed to class @ref basic_json.
     /// @{
 
-    // Use transparent comparator if possible, combined with perfect forwarding
-    // on find() and count() calls prevents unnecessary string construction.
-    using object_comparator_t = std::less<>;
+    /// @brief default object key comparator type
+    /// The actual object key comparator type (@ref object_comparator_t) may be
+    /// different.
+    /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
+#if defined(JSON_HAS_CPP_14)
+    // use of transparent comparator avoids unnecessary repeated construction of temporaries
+    // in functions involving lookup by key with types other than object_t::key_type (aka. StringType)
+    using default_object_comparator_t = std::less<>;
+#else
+    using default_object_comparator_t = std::less<StringType>;
+#endif
 
-    /*!
-    @brief a type for an object
+    /// @brief a type for an object
+    /// @sa https://json.nlohmann.me/api/basic_json/object_t/
+    using object_t = ObjectType<StringType,
+          basic_json,
+          default_object_comparator_t,
+          AllocatorType<std::pair<const StringType,
+          basic_json>>>;
 
-    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows:
-    > An object is an unordered collection of zero or more name/value pairs,
-    > where a name is a string and a value is a string, number, boolean, null,
-    > object, or array.
+    /// @brief a type for an array
+    /// @sa https://json.nlohmann.me/api/basic_json/array_t/
+    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
 
-    #### Behavior
+    /// @brief a type for a string
+    /// @sa https://json.nlohmann.me/api/basic_json/string_t/
+    using string_t = StringType;
 
-    The choice of @a object_t influences the behavior of the JSON class. With
-    the default type, objects have the following behavior:
+    /// @brief a type for a boolean
+    /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/
+    using boolean_t = BooleanType;
 
-    - When all names are unique, objects will be interoperable in the sense
-      that all software implementations receiving that object will agree on
-      the name-value mappings.
-    - When the names within an object are not unique, it is unspecified which
-      one of the values for a given key will be chosen. For instance,
-      `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or
-      `{"key": 2}`.
-    - Internally, name/value pairs are stored in lexicographical order of the
-      names. Objects will also be serialized (see @ref dump) in this order.
-      For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored
-      and serialized as `{"a": 2, "b": 1}`.
-    - When comparing objects, the order of the name/value pairs is irrelevant.
-      This makes objects interoperable in the sense that they will not be
-      affected by these differences. For instance, `{"b": 1, "a": 2}` and
-      `{"a": 2, "b": 1}` will be treated as equal.
+    /// @brief a type for a number (integer)
+    /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/
+    using number_integer_t = NumberIntegerType;
 
-    #### Limits
+    /// @brief a type for a number (unsigned)
+    /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/
+    using number_unsigned_t = NumberUnsignedType;
 
-    [RFC 7159](http://rfc7159.net/rfc7159) specifies:
-    > An implementation may set limits on the maximum depth of nesting.
+    /// @brief a type for a number (floating-point)
+    /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/
+    using number_float_t = NumberFloatType;
 
-    In this class, the object's limit of nesting is not explicitly constrained.
-    However, a maximum depth of nesting may be introduced by the compiler or
-    runtime environment. A theoretical limit can be queried by calling the
-    @ref max_size function of a JSON object.
+    /// @brief a type for a packed binary type
+    /// @sa https://json.nlohmann.me/api/basic_json/binary_t/
+    using binary_t = wpi::byte_container_with_subtype<BinaryType>;
 
-    #### Storage
-
-    Objects are stored as pointers in a @ref json type. That is, for any
-    access to object values, a pointer of type `object_t*` must be
-    dereferenced.
-
-    @since version 1.0.0
-
-    @note The order name/value pairs are added to the object is *not*
-    preserved by the library. Therefore, iterating an object may return
-    name/value pairs in a different order than they were originally stored. In
-    fact, keys will be traversed in alphabetical order as `std::map` with
-    `std::less` is used by default. Please note this behavior conforms to [RFC
-    7159](http://rfc7159.net/rfc7159), because any order implements the
-    specified "unordered" nature of JSON objects.
-    */
-    using object_t = StringMap<json>;
-
-    /*!
-    @brief a type for an array
-
-    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows:
-    > An array is an ordered sequence of zero or more values.
-
-    #### Limits
-
-    [RFC 7159](http://rfc7159.net/rfc7159) specifies:
-    > An implementation may set limits on the maximum depth of nesting.
-
-    In this class, the array's limit of nesting is not explicitly constrained.
-    However, a maximum depth of nesting may be introduced by the compiler or
-    runtime environment. A theoretical limit can be queried by calling the
-    @ref max_size function of a JSON array.
-
-    #### Storage
-
-    Arrays are stored as pointers in a @ref json type. That is, for any
-    access to array values, a pointer of type `array_t*` must be dereferenced.
-
-    @sa @ref object_t -- type for an object value
-
-    @since version 1.0.0
-    */
-    using array_t = std::vector<json>;
+    /// @brief object key comparator type
+    /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/
+    using object_comparator_t = detail::actual_object_comparator_t<basic_json>;
 
     /// @}
 
@@ -2895,30 +373,31 @@
 
     /// helper for exception-safe object creation
     template<typename T, typename... Args>
+    JSON_HEDLEY_RETURNS_NON_NULL
     static T* create(Args&& ... args)
     {
-        std::allocator<T> alloc;
+        AllocatorType<T> alloc;
+        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;
 
-		using AllocatorTraits = std::allocator_traits<std::allocator<T>>;
-
-		auto deleter = [&](T * object)
-		{
-			AllocatorTraits::deallocate(alloc, object, 1);
-		};
-		std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);
-		AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);
-        assert(object != nullptr);
-        return object.release();
+        auto deleter = [&](T * obj)
+        {
+            AllocatorTraits::deallocate(alloc, obj, 1);
+        };
+        std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter);
+        AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...);
+        JSON_ASSERT(obj != nullptr);
+        return obj.release();
     }
 
     ////////////////////////
     // JSON value storage //
     ////////////////////////
 
+  JSON_PRIVATE_UNLESS_TESTED:
     /*!
     @brief a JSON value
 
-    The actual storage for a JSON value of the @ref json class. This
+    The actual storage for a JSON value of the @ref basic_json class. This
     union combines the different storage types for the JSON value types
     defined in @ref value_t.
 
@@ -2926,11 +405,12 @@
     --------- | --------------- | ------------------------
     object    | object          | pointer to @ref object_t
     array     | array           | pointer to @ref array_t
-    string    | string          | pointer to std::string
-    boolean   | boolean         | bool
-    number    | number_integer  | int64_t
-    number    | number_unsigned | uint64_t
-    number    | number_float    | double
+    string    | string          | pointer to @ref string_t
+    boolean   | boolean         | @ref boolean_t
+    number    | number_integer  | @ref number_integer_t
+    number    | number_unsigned | @ref number_unsigned_t
+    number    | number_float    | @ref number_float_t
+    binary    | binary          | pointer to @ref binary_t
     null      | null            | *no value is stored*
 
     @note Variable-length types (objects, arrays, and strings) are stored as
@@ -2946,74 +426,230 @@
         /// array (stored with pointer to save storage)
         array_t* array;
         /// string (stored with pointer to save storage)
-        std::string* string;
+        string_t* string;
+        /// binary (stored with pointer to save storage)
+        binary_t* binary;
         /// boolean
-        bool boolean;
+        boolean_t boolean;
         /// number (integer)
-        int64_t number_integer;
+        number_integer_t number_integer;
         /// number (unsigned integer)
-        uint64_t number_unsigned;
+        number_unsigned_t number_unsigned;
         /// number (floating-point)
-        double number_float;
+        number_float_t number_float;
 
         /// default constructor (for null values)
         json_value() = default;
         /// constructor for booleans
-        json_value(bool v) noexcept : boolean(v) {}
+        json_value(boolean_t v) noexcept : boolean(v) {}
         /// constructor for numbers (integer)
-        json_value(int64_t v) noexcept : number_integer(v) {}
+        json_value(number_integer_t v) noexcept : number_integer(v) {}
         /// constructor for numbers (unsigned)
-        json_value(uint64_t v) noexcept : number_unsigned(v) {}
+        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}
         /// constructor for numbers (floating-point)
-        json_value(double v) noexcept : number_float(v) {}
+        json_value(number_float_t v) noexcept : number_float(v) {}
         /// constructor for empty values of a given type
-        json_value(value_t t);
-
-        /// constructor for strings
-        json_value(std::string_view value)
+        json_value(value_t t)
         {
-            string = create<std::string>(value);
+            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<string_t>("");
+                    break;
+                }
+
+                case value_t::binary:
+                {
+                    binary = create<binary_t>();
+                    break;
+                }
+
+                case value_t::boolean:
+                {
+                    boolean = static_cast<boolean_t>(false);
+                    break;
+                }
+
+                case value_t::number_integer:
+                {
+                    number_integer = static_cast<number_integer_t>(0);
+                    break;
+                }
+
+                case value_t::number_unsigned:
+                {
+                    number_unsigned = static_cast<number_unsigned_t>(0);
+                    break;
+                }
+
+                case value_t::number_float:
+                {
+                    number_float = static_cast<number_float_t>(0.0);
+                    break;
+                }
+
+                case value_t::null:
+                {
+                    object = nullptr;  // silence warning, see #821
+                    break;
+                }
+
+                case value_t::discarded:
+                default:
+                {
+                    object = nullptr;  // silence warning, see #821
+                    if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
+                    {
+                        JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.11.2", nullptr)); // LCOV_EXCL_LINE
+                    }
+                    break;
+                }
+            }
         }
 
         /// constructor for strings
-        json_value(const std::string& value)
-        {
-            string = create<std::string>(value);
-        }
+        json_value(const string_t& value) : string(create<string_t>(value)) {}
 
         /// constructor for rvalue strings
-        json_value(std::string&& value)
-        {
-            string = create<std::string>(std::move(value));
-        }
+        json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}
 
         /// constructor for objects
-        json_value(const object_t& value)
-        {
-            object = create<object_t>(value);
-        }
+        json_value(const object_t& value) : object(create<object_t>(value)) {}
 
         /// constructor for rvalue objects
-        json_value(object_t&& value)
-        {
-            object = create<object_t>(std::move(value));
-        }
+        json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}
 
         /// constructor for arrays
-        json_value(const array_t& value)
-        {
-            array = create<array_t>(value);
-        }
+        json_value(const array_t& value) : array(create<array_t>(value)) {}
 
         /// constructor for rvalue arrays
-        json_value(array_t&& value)
-        {
-            array = create<array_t>(std::move(value));
-        }
+        json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}
 
-        void destroy(value_t t) noexcept;
+        /// constructor for binary arrays
+        json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}
+
+        /// constructor for rvalue binary arrays
+        json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}
+
+        /// constructor for binary arrays (internal type)
+        json_value(const binary_t& value) : binary(create<binary_t>(value)) {}
+
+        /// constructor for rvalue binary arrays (internal type)
+        json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}
+
+        void destroy(value_t t)
+        {
+            if (t == value_t::array || t == value_t::object)
+            {
+                // flatten the current json_value to a heap-allocated stack
+                std::vector<basic_json> stack;
+
+                // move the top-level items to stack
+                if (t == value_t::array)
+                {
+                    stack.reserve(array->size());
+                    std::move(array->begin(), array->end(), std::back_inserter(stack));
+                }
+                else
+                {
+                    stack.reserve(object->size());
+                    for (auto&& it : *object)
+                    {
+                        stack.push_back(std::move(it.second));
+                    }
+                }
+
+                while (!stack.empty())
+                {
+                    // move the last item to local variable to be processed
+                    basic_json current_item(std::move(stack.back()));
+                    stack.pop_back();
+
+                    // if current_item is array/object, move
+                    // its children to the stack to be processed later
+                    if (current_item.is_array())
+                    {
+                        std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack));
+
+                        current_item.m_value.array->clear();
+                    }
+                    else if (current_item.is_object())
+                    {
+                        for (auto&& it : *current_item.m_value.object)
+                        {
+                            stack.push_back(std::move(it.second));
+                        }
+
+                        current_item.m_value.object->clear();
+                    }
+
+                    // it's now safe that current_item get destructed
+                    // since it doesn't have any children
+                }
+            }
+
+            switch (t)
+            {
+                case value_t::object:
+                {
+                    AllocatorType<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:
+                {
+                    AllocatorType<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:
+                {
+                    AllocatorType<string_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
+                    break;
+                }
+
+                case value_t::binary:
+                {
+                    AllocatorType<binary_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);
+                    break;
+                }
+
+                case value_t::null:
+                case value_t::boolean:
+                case value_t::number_integer:
+                case value_t::number_unsigned:
+                case value_t::number_float:
+                case value_t::discarded:
+                default:
+                {
+                    break;
+                }
+            }
+        }
     };
 
+  private:
     /*!
     @brief checks the class invariants
 
@@ -3022,12 +658,123 @@
     invariant. Furthermore, it has to be called each time the type of a JSON
     value is changed, because the invariant expresses a relationship between
     @a m_type and @a m_value.
+
+    Furthermore, the parent relation is checked for arrays and objects: If
+    @a check_parents true and the value is an array or object, then the
+    container's elements must have the current value as parent.
+
+    @param[in] check_parents  whether the parent relation should be checked.
+               The value is true by default and should only be set to false
+               during destruction of objects when the invariant does not
+               need to hold.
     */
-    void assert_invariant() const noexcept
+    void assert_invariant(bool check_parents = true) const noexcept
     {
-        assert(m_type != value_t::object or m_value.object != nullptr);
-        assert(m_type != value_t::array or m_value.array != nullptr);
-        assert(m_type != value_t::string or m_value.string != nullptr);
+        JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);
+        JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);
+        JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);
+        JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);
+
+#if JSON_DIAGNOSTICS
+        JSON_TRY
+        {
+            // cppcheck-suppress assertWithSideEffect
+            JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)
+            {
+                return j.m_parent == this;
+            }));
+        }
+        JSON_CATCH(...) {} // LCOV_EXCL_LINE
+#endif
+        static_cast<void>(check_parents);
+    }
+
+    void set_parents()
+    {
+#if JSON_DIAGNOSTICS
+        switch (m_type)
+        {
+            case value_t::array:
+            {
+                for (auto& element : *m_value.array)
+                {
+                    element.m_parent = this;
+                }
+                break;
+            }
+
+            case value_t::object:
+            {
+                for (auto& element : *m_value.object)
+                {
+                    element.second.m_parent = this;
+                }
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                break;
+        }
+#endif
+    }
+
+    iterator set_parents(iterator it, typename iterator::difference_type count_set_parents)
+    {
+#if JSON_DIAGNOSTICS
+        for (typename iterator::difference_type i = 0; i < count_set_parents; ++i)
+        {
+            (it + i)->m_parent = this;
+        }
+#else
+        static_cast<void>(count_set_parents);
+#endif
+        return it;
+    }
+
+    reference set_parent(reference j, std::size_t old_capacity = static_cast<std::size_t>(-1))
+    {
+#if JSON_DIAGNOSTICS
+        if (old_capacity != static_cast<std::size_t>(-1))
+        {
+            // see https://github.com/nlohmann/json/issues/2838
+            JSON_ASSERT(type() == value_t::array);
+            if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))
+            {
+                // capacity has changed: update all parents
+                set_parents();
+                return j;
+            }
+        }
+
+        // ordered_json uses a vector internally, so pointers could have
+        // been invalidated; see https://github.com/nlohmann/json/issues/2962
+#ifdef JSON_HEDLEY_MSVC_VERSION
+#pragma warning(push )
+#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr
+#endif
+        if (detail::is_ordered_map<object_t>::value)
+        {
+            set_parents();
+            return j;
+        }
+#ifdef JSON_HEDLEY_MSVC_VERSION
+#pragma warning( pop )
+#endif
+
+        j.m_parent = this;
+#else
+        static_cast<void>(j);
+        static_cast<void>(old_capacity);
+#endif
+        return j;
     }
 
   public:
@@ -3035,481 +782,249 @@
     // JSON parser callback //
     //////////////////////////
 
-    /*!
-    @brief parser event types
+    /// @brief parser event types
+    /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/
+    using parse_event_t = detail::parse_event_t;
 
-    The parser callback distinguishes the following events:
-    - `object_start`: the parser read `{` and started to process a JSON object
-    - `key`: the parser read a key of a value in an object
-    - `object_end`: the parser read `}` and finished processing a JSON object
-    - `array_start`: the parser read `[` and started to process a JSON array
-    - `array_end`: the parser read `]` and finished processing a JSON array
-    - `value`: the parser finished reading a JSON value
-
-    @image html callback_events.png "Example when certain parse events are triggered"
-
-    @sa @ref parser_callback_t for more information and examples
-    */
-    enum class parse_event_t : uint8_t
-    {
-        /// the parser read `{` and started to process a JSON object
-        object_start,
-        /// the parser read `}` and finished processing a JSON object
-        object_end,
-        /// the parser read `[` and started to process a JSON array
-        array_start,
-        /// the parser read `]` and finished processing a JSON array
-        array_end,
-        /// the parser read a key of a value in an object
-        key,
-        /// the parser finished reading a JSON value
-        value
-    };
-
-    /*!
-    @brief per-element parser callback type
-
-    With a parser callback function, the result of parsing a JSON text can be
-    influenced. When passed to @ref parse, it is called on certain events
-    (passed as @ref parse_event_t via parameter @a event) with a set recursion
-    depth @a depth and context JSON value @a parsed. The return value of the
-    callback function is a boolean indicating whether the element that emitted
-    the callback shall be kept or not.
-
-    We distinguish six scenarios (determined by the event type) in which the
-    callback function can be called. The following table describes the values
-    of the parameters @a depth, @a event, and @a parsed.
-
-    parameter @a event | description | parameter @a depth | parameter @a parsed
-    ------------------ | ----------- | ------------------ | -------------------
-    parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded
-    parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key
-    parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object
-    parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded
-    parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array
-    parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value
-
-    @image html callback_events.png "Example when certain parse events are triggered"
-
-    Discarding a value (i.e., returning `false`) has different effects
-    depending on the context in which function was called:
-
-    - Discarded values in structured types are skipped. That is, the parser
-      will behave as if the discarded value was never read.
-    - In case a value outside a structured type is skipped, it is replaced
-      with `null`. This case happens if the top-level element is skipped.
-
-    @param[in] depth  the depth of the recursion during parsing
-
-    @param[in] event  an event of type parse_event_t indicating the context in
-    the callback function has been called
-
-    @param[in,out] parsed  the current intermediate parse result; note that
-    writing to this value has no effect for parse_event_t::key events
-
-    @return Whether the JSON value which called the function during parsing
-    should be kept (`true`) or not (`false`). In the latter case, it is either
-    skipped completely or replaced by an empty discarded object.
-
-    @sa @ref parse for examples
-
-    @since version 1.0.0
-    */
-    using parser_callback_t =
-        std::function<bool(int depth, parse_event_t event, json& parsed)>;
-
+    /// @brief per-element parser callback type
+    /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/
+    using parser_callback_t = detail::parser_callback_t<basic_json>;
 
     //////////////////
     // constructors //
     //////////////////
 
     /// @name constructors and destructors
-    /// Constructors of class @ref json, copy/move constructor, copy
+    /// Constructors of class @ref basic_json, copy/move constructor, copy
     /// assignment, static functions creating objects, and the destructor.
     /// @{
 
-    /*!
-    @brief create an empty value with a given type
-
-    Create an empty JSON value with a given type. The value will be default
-    initialized with an empty value which depends on the type:
-
-    Value type  | initial value
-    ----------- | -------------
-    null        | `null`
-    boolean     | `false`
-    string      | `""`
-    number      | `0`
-    object      | `{}`
-    array       | `[]`
-
-    @param[in] v  the type of the value to create
-
-    @complexity Constant.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The following code shows the constructor for different @ref
-    value_t values,json__value_t}
-
-    @sa @ref clear() -- restores the postcondition of this constructor
-
-    @since version 1.0.0
-    */
-    json(const value_t v)
+    /// @brief create an empty value with a given type
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(const value_t v)
         : m_type(v), m_value(v)
     {
         assert_invariant();
     }
 
-    /*!
-    @brief create a null object
-
-    Create a `null` JSON value. It either takes a null pointer as parameter
-    (explicitly creating `null`) or no parameter (implicitly creating `null`).
-    The passed null pointer itself is not read -- it is only used to choose
-    the right constructor.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this constructor never throws
-    exceptions.
-
-    @liveexample{The following code shows the constructor with and without a
-    null pointer parameter.,json__nullptr_t}
-
-    @since version 1.0.0
-    */
-    json(std::nullptr_t = nullptr) noexcept
-        : json(value_t::null)
+    /// @brief create a null object
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape)
+        : basic_json(value_t::null)
     {
         assert_invariant();
     }
 
-    /*!
-    @brief create a JSON value
-
-    This is a "catch all" constructor for all compatible JSON types; that is,
-    types for which a `to_json()` method exists. The constructor forwards the
-    parameter @a val to that method (to `json_serializer<U>::to_json` method
-    with `U = uncvref_t<CompatibleType>`, to be exact).
-
-    Template type @a CompatibleType includes, but is not limited to, the
-    following types:
-    - **arrays**: @ref array_t and all kinds of compatible containers such as
-      `std::vector`, `std::deque`, `std::list`,
-      `std::array`, `std::set`, `std::unordered_set`,
-      `std::multiset`, and `std::unordered_multiset` with a `value_type` from
-      which a @ref json value can be constructed.
-    - **objects**: @ref object_t and all kinds of compatible associative
-      containers such as `std::map`, `std::unordered_map`, `std::multimap`,
-      and `std::unordered_multimap` with a `key_type` compatible to
-      `std::string` and a `value_type` from which a @ref json value can
-      be constructed.
-    - **strings**: `std::string`, string literals, and all compatible string
-      containers can be used.
-    - **numbers**: int64_t, uint64_t,
-      double, and all convertible number types such as `int`,
-      `size_t`, `int64_t`, `float` or `double` can be used.
-    - **boolean**: `bool` can be used.
-
-    See the examples below.
-
-    @tparam CompatibleType a type such that:
-    - @a CompatibleType is not derived from `std::istream`,
-    - @a CompatibleType is not @ref json (to avoid hijacking copy/move
-         constructors),
-    - @a CompatibleType is not a different @ref json type (i.e. with different template arguments)
-    - @a CompatibleType is not a @ref json nested type (e.g.,
-         @ref json_pointer, @ref iterator, etc ...)
-    - @ref @ref json_serializer<U> has a
-         `to_json(json_t&, CompatibleType&&)` method
-
-    @tparam U = `uncvref_t<CompatibleType>`
-
-    @param[in] val the value to be forwarded to the respective constructor
-
-    @complexity Usually linear in the size of the passed @a val, also
-                depending on the implementation of the called `to_json()`
-                method.
-
-    @exceptionsafety Depends on the called constructor. For types directly
-    supported by the library (i.e., all types for which no `to_json()` function
-    was provided), strong guarantee holds: if an exception is thrown, there are
-    no changes to any JSON value.
-
-    @liveexample{The following code shows the constructor with several
-    compatible types.,json__CompatibleType}
-
-    @since version 2.1.0
-    */
-    template <typename CompatibleType,
-              typename U = detail::uncvref_t<CompatibleType>,
-              detail::enable_if_t<
-                  detail::is_compatible_type<json_t, U>::value, int> = 0>
-    json(CompatibleType && val) noexcept(noexcept(
-                adl_serializer<U, void>::to_json(std::declval<json_t&>(),
+    /// @brief create a JSON value from compatible types
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    template < typename CompatibleType,
+               typename U = detail::uncvref_t<CompatibleType>,
+               detail::enable_if_t <
+                   !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >
+    basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)
+                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
                                            std::forward<CompatibleType>(val))))
     {
-        adl_serializer<U, void>::to_json(*this, std::forward<CompatibleType>(val));
+        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
+        set_parents();
         assert_invariant();
     }
 
-    /*!
-    @brief create a container (array or object) from an initializer list
+    /// @brief create a JSON value from an existing one
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    template < typename BasicJsonType,
+               detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >
+    basic_json(const BasicJsonType& val)
+    {
+        using other_boolean_t = typename BasicJsonType::boolean_t;
+        using other_number_float_t = typename BasicJsonType::number_float_t;
+        using other_number_integer_t = typename BasicJsonType::number_integer_t;
+        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+        using other_string_t = typename BasicJsonType::string_t;
+        using other_object_t = typename BasicJsonType::object_t;
+        using other_array_t = typename BasicJsonType::array_t;
+        using other_binary_t = typename BasicJsonType::binary_t;
 
-    Creates a JSON value of type array or object from the passed initializer
-    list @a init. In case @a type_deduction is `true` (default), the type of
-    the JSON value to be created is deducted from the initializer list @a init
-    according to the following rules:
+        switch (val.type())
+        {
+            case value_t::boolean:
+                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
+                break;
+            case value_t::number_float:
+                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
+                break;
+            case value_t::number_integer:
+                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
+                break;
+            case value_t::number_unsigned:
+                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
+                break;
+            case value_t::string:
+                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
+                break;
+            case value_t::object:
+                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
+                break;
+            case value_t::array:
+                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
+                break;
+            case value_t::binary:
+                JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
+                break;
+            case value_t::null:
+                *this = nullptr;
+                break;
+            case value_t::discarded:
+                m_type = value_t::discarded;
+                break;
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+        JSON_ASSERT(m_type == val.type());
+        set_parents();
+        assert_invariant();
+    }
 
-    1. If the list is empty, an empty JSON object value `{}` is created.
-    2. If the list consists of pairs whose first element is a string, a JSON
-       object value is created where the first elements of the pairs are
-       treated as keys and the second elements are as values.
-    3. In all other cases, an array is created.
-
-    The rules aim to create the best fit between a C++ initializer list and
-    JSON values. The rationale is as follows:
-
-    1. The empty initializer list is written as `{}` which is exactly an empty
-       JSON object.
-    2. C++ has no way of describing mapped types other than to list a list of
-       pairs. As JSON requires that keys must be of type string, rule 2 is the
-       weakest constraint one can pose on initializer lists to interpret them
-       as an object.
-    3. In all other cases, the initializer list could not be interpreted as
-       JSON object type, so interpreting it as JSON array type is safe.
-
-    With the rules described above, the following JSON values cannot be
-    expressed by an initializer list:
-
-    - the empty array (`[]`): use @ref array(initializer_list_t)
-      with an empty initializer list in this case
-    - arrays whose elements satisfy rule 2: use @ref
-      array(initializer_list_t) with the same initializer list
-      in this case
-
-    @note When used without parentheses around an empty initializer list, @ref
-    json() is called instead of this function, yielding the JSON null
-    value.
-
-    @param[in] init  initializer list with JSON values
-
-    @param[in] type_deduction internal parameter; when set to `true`, the type
-    of the JSON value is deducted from the initializer list @a init; when set
-    to `false`, the type provided via @a manual_type is forced. This mode is
-    used by the functions @ref array(initializer_list_t) and
-    @ref object(initializer_list_t).
-
-    @param[in] manual_type internal parameter; when @a type_deduction is set
-    to `false`, the created JSON value will use the provided type (only @ref
-    value_t::array and @ref value_t::object are valid); when @a type_deduction
-    is set to `true`, this parameter has no effect
-
-    @throw type_error.301 if @a type_deduction is `false`, @a manual_type is
-    `value_t::object`, but @a init contains an element which is not a pair
-    whose first element is a string. In this case, the constructor could not
-    create an object. If @a type_deduction would have be `true`, an array
-    would have been created. See @ref object(initializer_list_t)
-    for an example.
-
-    @complexity Linear in the size of the initializer list @a init.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The example below shows how JSON values are created from
-    initializer lists.,json__list_init_t}
-
-    @sa @ref array(initializer_list_t) -- create a JSON array
-    value from an initializer list
-    @sa @ref object(initializer_list_t) -- create a JSON object
-    value from an initializer list
-
-    @since version 1.0.0
-    */
-    json(initializer_list_t init,
+    /// @brief create a container (array or object) from an initializer list
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(initializer_list_t init,
                bool type_deduction = true,
-               value_t manual_type = value_t::array);
-
-    /*!
-    @brief explicitly create an array from an initializer list
-
-    Creates a JSON array value from a given initializer list. That is, given a
-    list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the
-    initializer list is empty, the empty array `[]` is created.
-
-    @note This function is only needed to express two edge cases that cannot
-    be realized with the initializer list constructor (@ref
-    json(initializer_list_t, bool, value_t)). These cases
-    are:
-    1. creating an array whose elements are all pairs whose first element is a
-    string -- in this case, the initializer list constructor would create an
-    object, taking the first elements as keys
-    2. creating an empty array -- passing the empty initializer list to the
-    initializer list constructor yields an empty object
-
-    @param[in] init  initializer list with JSON values to create an array from
-    (optional)
-
-    @return JSON array value
-
-    @complexity Linear in the size of @a init.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The following code shows an example for the `array`
-    function.,array}
-
-    @sa @ref json(initializer_list_t, bool, value_t) --
-    create a JSON value from an initializer list
-    @sa @ref object(initializer_list_t) -- create a JSON object
-    value from an initializer list
-
-    @since version 1.0.0
-    */
-    static json array(initializer_list_t init = {})
+               value_t manual_type = value_t::array)
     {
-        return json(init, false, value_t::array);
+        // 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<basic_json>& element_ref)
+        {
+            return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();
+        });
+
+        // adjust type if type deduction is not wanted
+        if (!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_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
+            {
+                JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr));
+            }
+        }
+
+        if (is_an_object)
+        {
+            // the initializer list is a list of pairs -> create object
+            m_type = value_t::object;
+            m_value = value_t::object;
+
+            for (auto& element_ref : init)
+            {
+                auto element = element_ref.moved_or_copied();
+                m_value.object->emplace(
+                    std::move(*((*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());
+        }
+
+        set_parents();
+        assert_invariant();
     }
 
-    /*!
-    @brief explicitly create an object from an initializer list
-
-    Creates a JSON object value from a given initializer list. The initializer
-    lists elements must be pairs, and their first elements must be strings. If
-    the initializer list is empty, the empty object `{}` is created.
-
-    @note This function is only added for symmetry reasons. In contrast to the
-    related function @ref array(initializer_list_t), there are
-    no cases which can only be expressed by this function. That is, any
-    initializer list @a init can also be passed to the initializer list
-    constructor @ref json(initializer_list_t, bool, value_t).
-
-    @param[in] init  initializer list to create an object from (optional)
-
-    @return JSON object value
-
-    @throw type_error.301 if @a init is not a list of pairs whose first
-    elements are strings. In this case, no object can be created. When such a
-    value is passed to @ref json(initializer_list_t, bool, value_t),
-    an array would have been created from the passed initializer list @a init.
-    See example below.
-
-    @complexity Linear in the size of @a init.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The following code shows an example for the `object`
-    function.,object}
-
-    @sa @ref json(initializer_list_t, bool, value_t) --
-    create a JSON value from an initializer list
-    @sa @ref array(initializer_list_t) -- create a JSON array
-    value from an initializer list
-
-    @since version 1.0.0
-    */
-    static json object(initializer_list_t init = {})
+    /// @brief explicitly create a binary array (without subtype)
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(const typename binary_t::container_type& init)
     {
-        return json(init, false, value_t::object);
+        auto res = basic_json();
+        res.m_type = value_t::binary;
+        res.m_value = init;
+        return res;
     }
 
-    /*!
-    @brief construct an array with count copies of given value
-
-    Constructs a JSON array value by creating @a cnt copies of a passed value.
-    In case @a cnt is `0`, an empty array is created.
-
-    @param[in] cnt  the number of JSON copies of @a val to create
-    @param[in] val  the JSON value to copy
-
-    @post `std::distance(begin(),end()) == cnt` holds.
-
-    @complexity Linear in @a cnt.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The following code shows examples for the @ref
-    json(size_type\, const json&)
-    constructor.,json__size_type_json}
-
-    @since version 1.0.0
-    */
-    json(size_type cnt, const json& val);
-
-    /*!
-    @brief construct a JSON container given an iterator range
-
-    Constructs the JSON value with the contents of the range `[first, last)`.
-    The semantics depends on the different types a JSON value can have:
-    - In case of a null type, invalid_iterator.206 is thrown.
-    - In case of other primitive types (number, boolean, or string), @a first
-      must be `begin()` and @a last must be `end()`. In this case, the value is
-      copied. Otherwise, invalid_iterator.204 is thrown.
-    - In case of structured types (array, object), the constructor behaves as
-      similar versions for `std::vector` or `std::map`; that is, a JSON array
-      or object is constructed from the values in the range.
-
-    @tparam InputIT an input iterator type (@ref iterator or @ref
-    const_iterator)
-
-    @param[in] first begin of the range to copy from (included)
-    @param[in] last end of the range to copy from (excluded)
-
-    @pre Iterators @a first and @a last must be initialized. **This
-         precondition is enforced with an assertion (see warning).** If
-         assertions are switched off, a violation of this precondition yields
-         undefined behavior.
-
-    @pre Range `[first, last)` is valid. Usually, this precondition cannot be
-         checked efficiently. Only certain edge cases are detected; see the
-         description of the exceptions below. A violation of this precondition
-         yields undefined behavior.
-
-    @warning A precondition is enforced with a runtime assertion that will
-             result in calling `std::abort` if this precondition is not met.
-             Assertions can be disabled by defining `NDEBUG` at compile time.
-             See http://en.cppreference.com/w/cpp/error/assert for more
-             information.
-
-    @throw invalid_iterator.201 if iterators @a first and @a last are not
-    compatible (i.e., do not belong to the same JSON value). In this case,
-    the range `[first, last)` is undefined.
-    @throw invalid_iterator.204 if iterators @a first and @a last belong to a
-    primitive type (number, boolean, or string), but @a first does not point
-    to the first element any more. In this case, the range `[first, last)` is
-    undefined. See example code below.
-    @throw invalid_iterator.206 if iterators @a first and @a last belong to a
-    null value. In this case, the range `[first, last)` is undefined.
-
-    @complexity Linear in distance between @a first and @a last.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The example below shows several ways to create JSON values by
-    specifying a subrange with iterators.,json__InputIt_InputIt}
-
-    @since version 1.0.0
-    */
-    template<class InputIT, typename std::enable_if<
-                 std::is_same<InputIT, typename json_t::iterator>::value or
-                 std::is_same<InputIT, typename json_t::const_iterator>::value, int>::type = 0>
-    json(InputIT first, InputIT last)
+    /// @brief explicitly create a binary array (with subtype)
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
     {
-        assert(first.m_object != nullptr);
-        assert(last.m_object != nullptr);
+        auto res = basic_json();
+        res.m_type = value_t::binary;
+        res.m_value = binary_t(init, subtype);
+        return res;
+    }
+
+    /// @brief explicitly create a binary array
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(typename binary_t::container_type&& init)
+    {
+        auto res = basic_json();
+        res.m_type = value_t::binary;
+        res.m_value = std::move(init);
+        return res;
+    }
+
+    /// @brief explicitly create a binary array (with subtype)
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
+    {
+        auto res = basic_json();
+        res.m_type = value_t::binary;
+        res.m_value = binary_t(std::move(init), subtype);
+        return res;
+    }
+
+    /// @brief explicitly create an array from an initializer list
+    /// @sa https://json.nlohmann.me/api/basic_json/array/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json array(initializer_list_t init = {})
+    {
+        return basic_json(init, false, value_t::array);
+    }
+
+    /// @brief explicitly create an object from an initializer list
+    /// @sa https://json.nlohmann.me/api/basic_json/object/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json object(initializer_list_t init = {})
+    {
+        return basic_json(init, false, value_t::object);
+    }
+
+    /// @brief construct an array with count copies of given value
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(size_type cnt, const basic_json& val)
+        : m_type(value_t::array)
+    {
+        m_value.array = create<array_t>(cnt, val);
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief construct a JSON container given an iterator range
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    template < class InputIT, typename std::enable_if <
+                   std::is_same<InputIT, typename basic_json_t::iterator>::value ||
+                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >
+    basic_json(InputIT first, InputIT last)
+    {
+        JSON_ASSERT(first.m_object != nullptr);
+        JSON_ASSERT(last.m_object != nullptr);
 
         // make sure iterator fits the current value
-        if (JSON_UNLIKELY(first.m_object != last.m_object))
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
         {
-            JSON_THROW(invalid_iterator::create(201, "iterators are not compatible"));
+            JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr));
         }
 
         // copy type from first iterator
@@ -3524,14 +1039,19 @@
             case value_t::number_unsigned:
             case value_t::string:
             {
-                if (JSON_UNLIKELY(not first.m_it.primitive_iterator.is_begin()
-                                  or not last.m_it.primitive_iterator.is_end()))
+                if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
+                                         || !last.m_it.primitive_iterator.is_end()))
                 {
-                    JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
+                    JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object));
                 }
                 break;
             }
 
+            case value_t::null:
+            case value_t::object:
+            case value_t::array:
+            case value_t::binary:
+            case value_t::discarded:
             default:
                 break;
         }
@@ -3568,6 +1088,13 @@
                 break;
             }
 
+            case value_t::object:
+            {
+                m_value.object = create<object_t>(first.m_it.object_iterator,
+                                                  last.m_it.object_iterator);
+                break;
+            }
+
             case value_t::array:
             {
                 m_value.array = create<array_t>(first.m_it.array_iterator,
@@ -3575,10 +1102,19 @@
                 break;
             }
 
+            case value_t::binary:
+            {
+                m_value = *first.m_object->m_value.binary;
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
             default:
-                JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from", first.m_object->type_name()));
+                JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
         }
 
+        set_parents();
         assert_invariant();
     }
 
@@ -3587,105 +1123,102 @@
     // other constructors and destructor //
     ///////////////////////////////////////
 
-    /// @private
-    json(const detail::json_ref<json>& ref)
-        : json(ref.moved_or_copied())
-    {}
+    template<typename JsonRef,
+             detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
+                                 std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
+    basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}
 
-    /*!
-    @brief copy constructor
+    /// @brief copy constructor
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(const basic_json& other)
+        : m_type(other.m_type)
+    {
+        // check of passed value is valid
+        other.assert_invariant();
 
-    Creates a copy of a given JSON value.
+        switch (m_type)
+        {
+            case value_t::object:
+            {
+                m_value = *other.m_value.object;
+                break;
+            }
 
-    @param[in] other  the JSON value to copy
+            case value_t::array:
+            {
+                m_value = *other.m_value.array;
+                break;
+            }
 
-    @post `*this == other`
+            case value_t::string:
+            {
+                m_value = *other.m_value.string;
+                break;
+            }
 
-    @complexity Linear in the size of @a other.
+            case value_t::boolean:
+            {
+                m_value = other.m_value.boolean;
+                break;
+            }
 
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
+            case value_t::number_integer:
+            {
+                m_value = other.m_value.number_integer;
+                break;
+            }
 
-    @requirement This function helps `json` satisfying the
-    [Container](http://en.cppreference.com/w/cpp/concept/Container)
-    requirements:
-    - The complexity is linear.
-    - As postcondition, it holds: `other == json(other)`.
+            case value_t::number_unsigned:
+            {
+                m_value = other.m_value.number_unsigned;
+                break;
+            }
 
-    @liveexample{The following code shows an example for the copy
-    constructor.,json__json}
+            case value_t::number_float:
+            {
+                m_value = other.m_value.number_float;
+                break;
+            }
 
-    @since version 1.0.0
-    */
-    json(const json& other);
+            case value_t::binary:
+            {
+                m_value = *other.m_value.binary;
+                break;
+            }
 
-    /*!
-    @brief move constructor
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                break;
+        }
 
-    Move constructor. Constructs a JSON value with the contents of the given
-    value @a other using move semantics. It "steals" the resources from @a
-    other and leaves it as JSON null value.
+        set_parents();
+        assert_invariant();
+    }
 
-    @param[in,out] other  value to move to this object
-
-    @post `*this` has the same value as @a other before the call.
-    @post @a other is a JSON null value.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this constructor never throws
-    exceptions.
-
-    @requirement This function helps `json` satisfying the
-    [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible)
-    requirements.
-
-    @liveexample{The code below shows the move constructor explicitly called
-    via std::move.,json__moveconstructor}
-
-    @since version 1.0.0
-    */
-    json(json&& other) noexcept
+    /// @brief move constructor
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(basic_json&& other) noexcept
         : m_type(std::move(other.m_type)),
           m_value(std::move(other.m_value))
     {
         // check that passed value is valid
-        other.assert_invariant();
+        other.assert_invariant(false);
 
         // invalidate payload
         other.m_type = value_t::null;
         other.m_value = {};
 
+        set_parents();
         assert_invariant();
     }
 
-    /*!
-    @brief copy assignment
-
-    Copy assignment operator. Copies a JSON value via the "copy and swap"
-    strategy: It is expressed in terms of the copy constructor, destructor,
-    and the `swap()` member function.
-
-    @param[in] other  value to copy from
-
-    @complexity Linear.
-
-    @requirement This function helps `json` satisfying the
-    [Container](http://en.cppreference.com/w/cpp/concept/Container)
-    requirements:
-    - The complexity is linear.
-
-    @liveexample{The code below shows and example for the copy assignment. It
-    creates a copy of value `a` which is then swapped with `b`. Finally\, the
-    copy of `a` (which is the null value after the swap) is
-    destroyed.,json__copyassignment}
-
-    @since version 1.0.0
-    */
-    reference& operator=(json other) noexcept (
-        std::is_nothrow_move_constructible<value_t>::value and
-        std::is_nothrow_move_assignable<value_t>::value and
-        std::is_nothrow_move_constructible<json_value>::value and
+    /// @brief copy assignment
+    /// @sa https://json.nlohmann.me/api/basic_json/operator=/
+    basic_json& operator=(basic_json other) noexcept (
+        std::is_nothrow_move_constructible<value_t>::value&&
+        std::is_nothrow_move_assignable<value_t>::value&&
+        std::is_nothrow_move_constructible<json_value>::value&&
         std::is_nothrow_move_assignable<json_value>::value
     )
     {
@@ -3696,28 +1229,16 @@
         swap(m_type, other.m_type);
         swap(m_value, other.m_value);
 
+        set_parents();
         assert_invariant();
         return *this;
     }
 
-    /*!
-    @brief destructor
-
-    Destroys the JSON value and frees all allocated memory.
-
-    @complexity Linear.
-
-    @requirement This function helps `json` satisfying the
-    [Container](http://en.cppreference.com/w/cpp/concept/Container)
-    requirements:
-    - The complexity is linear.
-    - All stored elements are destroyed and all memory is freed.
-
-    @since version 1.0.0
-    */
-    ~json() noexcept
+    /// @brief destructor
+    /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/
+    ~basic_json() noexcept
     {
-        assert_invariant();
+        assert_invariant(false);
         m_value.destroy(m_type);
     }
 
@@ -3732,416 +1253,147 @@
     /// Functions to inspect the type of a JSON value.
     /// @{
 
-    /*!
-    @brief serialization
+    /// @brief serialization
+    /// @sa https://json.nlohmann.me/api/basic_json/dump/
+    string_t dump(const int indent = -1,
+                  const char indent_char = ' ',
+                  const bool ensure_ascii = false,
+                  const error_handler_t error_handler = error_handler_t::strict) const
+    {
+        string_t result;
+        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);
 
-    Serialization function for JSON values. The function tries to mimic
-    Python's `json.dumps()` function, and currently supports its @a indent
-    and @a ensure_ascii parameters.
+        if (indent >= 0)
+        {
+            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
+        }
+        else
+        {
+            s.dump(*this, false, ensure_ascii, 0);
+        }
 
-    @param[in] indent If indent is nonnegative, then array elements and object
-    members will be pretty-printed with that indent level. An indent level of
-    `0` will only insert newlines. `-1` (the default) selects the most compact
-    representation.
-    @param[in] indent_char The character to use for indentation if @a indent is
-    greater than `0`. The default is ` ` (space).
-    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
-    in the output are escaped with `\uXXXX` sequences, and the result consists
-    of ASCII characters only.
+        return result;
+    }
 
-    @return string containing the serialization of the JSON value
+    void dump(raw_ostream& os, const int indent = -1,
+                  const char indent_char = ' ',
+                  const bool ensure_ascii = false,
+                  const error_handler_t error_handler = error_handler_t::strict) const {
+      serializer s(os, indent_char);
 
-    @throw type_error.316 if a string stored inside the JSON value is not
-                          UTF-8 encoded
+      if (indent >= 0)
+      {
+          s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
+      }
+      else
+      {
+          s.dump(*this, false, ensure_ascii, 0);
+      }
 
-    @complexity Linear.
+      os.flush();
+    }
 
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @liveexample{The following example shows the effect of different @a indent\,
-    @a indent_char\, and @a ensure_ascii parameters to the result of the
-    serialization.,dump}
-
-    @see https://docs.python.org/2/library/json.html#json.dump
-
-    @since version 1.0.0; indentation character @a indent_char, option
-           @a ensure_ascii and exceptions added in version 3.0.0
-    */
-    std::string dump(const int indent = -1, const char indent_char = ' ',
-                     const bool ensure_ascii = false) const;
-
-    void dump(raw_ostream& os, int indent = -1, const char indent_char = ' ',
-              const bool ensure_ascii = false) const;
-
-    /*!
-    @brief return the type of the JSON value (explicit)
-
-    Return the type of the JSON value as a value from the @ref value_t
-    enumeration.
-
-    @return the type of the JSON value
-            Value type                | return value
-            ------------------------- | -------------------------
-            null                      | value_t::null
-            boolean                   | value_t::boolean
-            string                    | value_t::string
-            number (integer)          | value_t::number_integer
-            number (unsigned integer) | value_t::number_unsigned
-            number (floating-point)   | value_t::number_float
-            object                    | value_t::object
-            array                     | value_t::array
-            discarded                 | value_t::discarded
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `type()` for all JSON
-    types.,type}
-
-    @sa @ref operator value_t() -- return the type of the JSON value (implicit)
-    @sa @ref type_name() -- return the type as string
-
-    @since version 1.0.0
-    */
-    value_t type() const noexcept
+    /// @brief return the type of the JSON value (explicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/type/
+    constexpr value_t type() const noexcept
     {
         return m_type;
     }
 
-    /*!
-    @brief return whether type is primitive
-
-    This function returns true if and only if the JSON type is primitive
-    (string, number, boolean, or null).
-
-    @return `true` if type is primitive (string, number, boolean, or null),
-    `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_primitive()` for all JSON
-    types.,is_primitive}
-
-    @sa @ref is_structured() -- returns whether JSON value is structured
-    @sa @ref is_null() -- returns whether JSON value is `null`
-    @sa @ref is_string() -- returns whether JSON value is a string
-    @sa @ref is_boolean() -- returns whether JSON value is a boolean
-    @sa @ref is_number() -- returns whether JSON value is a number
-
-    @since version 1.0.0
-    */
-    bool is_primitive() const noexcept
+    /// @brief return whether type is primitive
+    /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/
+    constexpr bool is_primitive() const noexcept
     {
-        return is_null() or is_string() or is_boolean() or is_number();
+        return is_null() || is_string() || is_boolean() || is_number() || is_binary();
     }
 
-    /*!
-    @brief return whether type is structured
-
-    This function returns true if and only if the JSON type is structured
-    (array or object).
-
-    @return `true` if type is structured (array or object), `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_structured()` for all JSON
-    types.,is_structured}
-
-    @sa @ref is_primitive() -- returns whether value is primitive
-    @sa @ref is_array() -- returns whether value is an array
-    @sa @ref is_object() -- returns whether value is an object
-
-    @since version 1.0.0
-    */
-    bool is_structured() const noexcept
+    /// @brief return whether type is structured
+    /// @sa https://json.nlohmann.me/api/basic_json/is_structured/
+    constexpr bool is_structured() const noexcept
     {
-        return is_array() or is_object();
+        return is_array() || is_object();
     }
 
-    /*!
-    @brief return whether value is null
-
-    This function returns true if and only if the JSON value is null.
-
-    @return `true` if type is null, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_null()` for all JSON
-    types.,is_null}
-
-    @since version 1.0.0
-    */
-    bool is_null() const noexcept
+    /// @brief return whether value is null
+    /// @sa https://json.nlohmann.me/api/basic_json/is_null/
+    constexpr bool is_null() const noexcept
     {
-        return (m_type == value_t::null);
+        return m_type == value_t::null;
     }
 
-    /*!
-    @brief return whether value is a boolean
-
-    This function returns true if and only if the JSON value is a boolean.
-
-    @return `true` if type is boolean, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_boolean()` for all JSON
-    types.,is_boolean}
-
-    @since version 1.0.0
-    */
-    bool is_boolean() const noexcept
+    /// @brief return whether value is a boolean
+    /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/
+    constexpr bool is_boolean() const noexcept
     {
-        return (m_type == value_t::boolean);
+        return m_type == value_t::boolean;
     }
 
-    /*!
-    @brief return whether value is a number
-
-    This function returns true if and only if the JSON value is a number. This
-    includes both integer (signed and unsigned) and floating-point values.
-
-    @return `true` if type is number (regardless whether integer, unsigned
-    integer or floating-type), `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_number()` for all JSON
-    types.,is_number}
-
-    @sa @ref is_number_integer() -- check if value is an integer or unsigned
-    integer number
-    @sa @ref is_number_unsigned() -- check if value is an unsigned integer
-    number
-    @sa @ref is_number_float() -- check if value is a floating-point number
-
-    @since version 1.0.0
-    */
-    bool is_number() const noexcept
+    /// @brief return whether value is a number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number/
+    constexpr bool is_number() const noexcept
     {
-        return is_number_integer() or is_number_float();
+        return is_number_integer() || is_number_float();
     }
 
-    /*!
-    @brief return whether value is an integer number
-
-    This function returns true if and only if the JSON value is a signed or
-    unsigned integer number. This excludes floating-point values.
-
-    @return `true` if type is an integer or unsigned integer number, `false`
-    otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_number_integer()` for all
-    JSON types.,is_number_integer}
-
-    @sa @ref is_number() -- check if value is a number
-    @sa @ref is_number_unsigned() -- check if value is an unsigned integer
-    number
-    @sa @ref is_number_float() -- check if value is a floating-point number
-
-    @since version 1.0.0
-    */
-    bool is_number_integer() const noexcept
+    /// @brief return whether value is an integer number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/
+    constexpr bool is_number_integer() const noexcept
     {
-        return (m_type == value_t::number_integer or m_type == value_t::number_unsigned);
+        return m_type == value_t::number_integer || m_type == value_t::number_unsigned;
     }
 
-    /*!
-    @brief return whether value is an unsigned integer number
-
-    This function returns true if and only if the JSON value is an unsigned
-    integer number. This excludes floating-point and signed integer values.
-
-    @return `true` if type is an unsigned integer number, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_number_unsigned()` for all
-    JSON types.,is_number_unsigned}
-
-    @sa @ref is_number() -- check if value is a number
-    @sa @ref is_number_integer() -- check if value is an integer or unsigned
-    integer number
-    @sa @ref is_number_float() -- check if value is a floating-point number
-
-    @since version 2.0.0
-    */
-    bool is_number_unsigned() const noexcept
+    /// @brief return whether value is an unsigned integer number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/
+    constexpr bool is_number_unsigned() const noexcept
     {
-        return (m_type == value_t::number_unsigned);
+        return m_type == value_t::number_unsigned;
     }
 
-    /*!
-    @brief return whether value is a floating-point number
-
-    This function returns true if and only if the JSON value is a
-    floating-point number. This excludes signed and unsigned integer values.
-
-    @return `true` if type is a floating-point number, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_number_float()` for all
-    JSON types.,is_number_float}
-
-    @sa @ref is_number() -- check if value is number
-    @sa @ref is_number_integer() -- check if value is an integer number
-    @sa @ref is_number_unsigned() -- check if value is an unsigned integer
-    number
-
-    @since version 1.0.0
-    */
-    bool is_number_float() const noexcept
+    /// @brief return whether value is a floating-point number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/
+    constexpr bool is_number_float() const noexcept
     {
-        return (m_type == value_t::number_float);
+        return m_type == value_t::number_float;
     }
 
-    /*!
-    @brief return whether value is an object
-
-    This function returns true if and only if the JSON value is an object.
-
-    @return `true` if type is object, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_object()` for all JSON
-    types.,is_object}
-
-    @since version 1.0.0
-    */
-    bool is_object() const noexcept
+    /// @brief return whether value is an object
+    /// @sa https://json.nlohmann.me/api/basic_json/is_object/
+    constexpr bool is_object() const noexcept
     {
-        return (m_type == value_t::object);
+        return m_type == value_t::object;
     }
 
-    /*!
-    @brief return whether value is an array
-
-    This function returns true if and only if the JSON value is an array.
-
-    @return `true` if type is array, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_array()` for all JSON
-    types.,is_array}
-
-    @since version 1.0.0
-    */
-    bool is_array() const noexcept
+    /// @brief return whether value is an array
+    /// @sa https://json.nlohmann.me/api/basic_json/is_array/
+    constexpr bool is_array() const noexcept
     {
-        return (m_type == value_t::array);
+        return m_type == value_t::array;
     }
 
-    /*!
-    @brief return whether value is a string
-
-    This function returns true if and only if the JSON value is a string.
-
-    @return `true` if type is string, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_string()` for all JSON
-    types.,is_string}
-
-    @since version 1.0.0
-    */
-    bool is_string() const noexcept
+    /// @brief return whether value is a string
+    /// @sa https://json.nlohmann.me/api/basic_json/is_string/
+    constexpr bool is_string() const noexcept
     {
-        return (m_type == value_t::string);
+        return m_type == value_t::string;
     }
 
-    /*!
-    @brief return whether value is discarded
-
-    This function returns true if and only if the JSON value was discarded
-    during parsing with a callback function (see @ref parser_callback_t).
-
-    @note This function will always be `false` for JSON values after parsing.
-    That is, discarded values can only occur during parsing, but will be
-    removed when inside a structured value or replaced by null in other cases.
-
-    @return `true` if type is discarded, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_discarded()` for all JSON
-    types.,is_discarded}
-
-    @since version 1.0.0
-    */
-    bool is_discarded() const noexcept
+    /// @brief return whether value is a binary array
+    /// @sa https://json.nlohmann.me/api/basic_json/is_binary/
+    constexpr bool is_binary() const noexcept
     {
-        return (m_type == value_t::discarded);
+        return m_type == value_t::binary;
     }
 
-    /*!
-    @brief return the type of the JSON value (implicit)
+    /// @brief return whether value is discarded
+    /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/
+    constexpr bool is_discarded() const noexcept
+    {
+        return m_type == value_t::discarded;
+    }
 
-    Implicitly return the type of the JSON value as a value from the @ref
-    value_t enumeration.
-
-    @return the type of the JSON value
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies the @ref value_t operator for
-    all JSON types.,operator__value_t}
-
-    @sa @ref type() -- return the type of the JSON value (explicit)
-    @sa @ref type_name() -- return the type as string
-
-    @since version 1.0.0
-    */
-    operator value_t() const noexcept
+    /// @brief return the type of the JSON value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/
+    constexpr operator value_t() const noexcept
     {
         return m_type;
     }
@@ -4154,14 +1406,14 @@
     //////////////////
 
     /// get a boolean (explicit)
-    bool get_impl(bool* /*unused*/) const
+    boolean_t get_impl(boolean_t* /*unused*/) const
     {
-        if (JSON_LIKELY(is_boolean()))
+        if (JSON_HEDLEY_LIKELY(is_boolean()))
         {
             return m_value.boolean;
         }
 
-        JSON_THROW(type_error::create(302, "type must be boolean, but is", type_name()));
+        JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this));
     }
 
     /// get a pointer to the value (object)
@@ -4171,7 +1423,7 @@
     }
 
     /// get a pointer to the value (object)
-    const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
+    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
     {
         return is_object() ? m_value.object : nullptr;
     }
@@ -4183,78 +1435,90 @@
     }
 
     /// get a pointer to the value (array)
-    const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
+    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
     {
         return is_array() ? m_value.array : nullptr;
     }
 
     /// get a pointer to the value (string)
-    std::string* get_impl_ptr(std::string* /*unused*/) noexcept
+    string_t* get_impl_ptr(string_t* /*unused*/) noexcept
     {
         return is_string() ? m_value.string : nullptr;
     }
 
     /// get a pointer to the value (string)
-    const std::string* get_impl_ptr(const std::string* /*unused*/) const noexcept
+    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
     {
         return is_string() ? m_value.string : nullptr;
     }
 
     /// get a pointer to the value (boolean)
-    bool* get_impl_ptr(bool* /*unused*/) noexcept
+    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
     {
         return is_boolean() ? &m_value.boolean : nullptr;
     }
 
     /// get a pointer to the value (boolean)
-    const bool* get_impl_ptr(const bool* /*unused*/) const noexcept
+    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
     {
         return is_boolean() ? &m_value.boolean : nullptr;
     }
 
     /// get a pointer to the value (integer number)
-    int64_t* get_impl_ptr(int64_t* /*unused*/) noexcept
+    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
     {
         return is_number_integer() ? &m_value.number_integer : nullptr;
     }
 
     /// get a pointer to the value (integer number)
-    const int64_t* get_impl_ptr(const int64_t* /*unused*/) const noexcept
+    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
     {
         return is_number_integer() ? &m_value.number_integer : nullptr;
     }
 
     /// get a pointer to the value (unsigned number)
-    uint64_t* get_impl_ptr(uint64_t* /*unused*/) noexcept
+    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
     {
         return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
     }
 
     /// get a pointer to the value (unsigned number)
-    const uint64_t* get_impl_ptr(const uint64_t* /*unused*/) const noexcept
+    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
     {
         return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
     }
 
     /// get a pointer to the value (floating-point number)
-    double* get_impl_ptr(double* /*unused*/) noexcept
+    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
     {
         return is_number_float() ? &m_value.number_float : nullptr;
     }
 
     /// get a pointer to the value (floating-point number)
-    const double* get_impl_ptr(const double* /*unused*/) const noexcept
+    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
     {
         return is_number_float() ? &m_value.number_float : nullptr;
     }
 
+    /// get a pointer to the value (binary)
+    binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept
+    {
+        return is_binary() ? m_value.binary : nullptr;
+    }
+
+    /// get a pointer to the value (binary)
+    constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept
+    {
+        return is_binary() ? m_value.binary : nullptr;
+    }
+
     /*!
     @brief helper function to implement get_ref()
 
     This function helps to implement get_ref() without code duplication for
     const and non-const overloads
 
-    @tparam ThisType will be deduced as `json` or `const json`
+    @tparam ThisType will be deduced as `basic_json` or `const basic_json`
 
     @throw type_error.303 if ReferenceType does not match underlying value
     type of the current JSON
@@ -4263,14 +1527,14 @@
     static ReferenceType get_ref_impl(ThisType& obj)
     {
         // delegate the call to get_ptr<>()
-        auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
+        auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
 
-        if (JSON_LIKELY(ptr != nullptr))
+        if (JSON_HEDLEY_LIKELY(ptr != nullptr))
         {
             return *ptr;
         }
 
-        JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is", obj.type_name()));
+        JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
     }
 
   public:
@@ -4278,52 +1542,51 @@
     /// Direct access to the stored value of a JSON value.
     /// @{
 
-    /*!
-    @brief get special-case overload
-
-    This overloads avoids a lot of template boilerplate, it can be seen as the
-    identity method
-
-    @tparam BasicJsonType == @ref json
-
-    @return a copy of *this
-
-    @complexity Constant.
-
-    @since version 2.1.0
-    */
-    template<typename BasicJsonType, detail::enable_if_t<
-                 std::is_same<typename std::remove_const<BasicJsonType>::type, json_t>::value,
-                 int> = 0>
-    json get() const
+    /// @brief get a pointer value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/
+    template<typename PointerType, typename std::enable_if<
+                 std::is_pointer<PointerType>::value, int>::type = 0>
+    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
     {
-        return *this;
+        // delegate the call to get_impl_ptr<>()
+        return get_impl_ptr(static_cast<PointerType>(nullptr));
     }
 
+    /// @brief get a pointer value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/
+    template < typename PointerType, typename std::enable_if <
+                   std::is_pointer<PointerType>::value&&
+                   std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >
+    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
+    {
+        // delegate the call to get_impl_ptr<>() const
+        return get_impl_ptr(static_cast<PointerType>(nullptr));
+    }
+
+  private:
     /*!
     @brief get a value (explicit)
 
     Explicit type conversion between the JSON value and a compatible value
-    which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible)
-    and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible).
+    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
+    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
     The value is converted by calling the @ref json_serializer<ValueType>
     `from_json()` method.
 
     The function is equivalent to executing
     @code {.cpp}
     ValueType ret;
-    adl_serializer<ValueType, void>::from_json(*this, ret);
+    JSONSerializer<ValueType>::from_json(*this, ret);
     return ret;
     @endcode
 
     This overloads is chosen if:
-    - @a ValueType is not @ref json,
+    - @a ValueType is not @ref basic_json,
     - @ref json_serializer<ValueType> has a `from_json()` method of the form
-      `void from_json(const json&, ValueType&)`, and
+      `void from_json(const basic_json&, ValueType&)`, and
     - @ref json_serializer<ValueType> does not have a `from_json()` method of
-      the form `ValueType from_json(const json&)`
+      the form `ValueType from_json(const basic_json&)`
 
-    @tparam ValueTypeCV the provided value type
     @tparam ValueType the returned value type
 
     @return copy of the JSON value, converted to @a ValueType
@@ -4339,25 +1602,16 @@
 
     @since version 2.1.0
     */
-    template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
-             detail::enable_if_t <
-                 not detail::is_json<ValueType>::value and
-                 detail::has_from_json<json_t, ValueType>::value and
-                 not detail::has_non_default_from_json<json_t, ValueType>::value,
-                 int> = 0>
-    ValueType get() const noexcept(noexcept(
-                                       adl_serializer<ValueType, void>::from_json(std::declval<const json_t&>(), std::declval<ValueType&>())))
+    template < typename ValueType,
+               detail::enable_if_t <
+                   detail::is_default_constructible<ValueType>::value&&
+                   detail::has_from_json<basic_json_t, ValueType>::value,
+                   int > = 0 >
+    ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(
+                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
     {
-        // we cannot static_assert on ValueTypeCV being non-const, because
-        // there is support for get<const json_t>(), which is why we
-        // still need the uncvref
-        static_assert(not std::is_reference<ValueTypeCV>::value,
-                      "get() cannot be used with reference types, you might want to use get_ref()");
-        static_assert(std::is_default_constructible<ValueType>::value,
-                      "types must be DefaultConstructible when used with get()");
-
-        ValueType ret;
-        adl_serializer<ValueType, void>::from_json(*this, ret);
+        auto ret = ValueType();
+        JSONSerializer<ValueType>::from_json(*this, ret);
         return ret;
     }
 
@@ -4365,25 +1619,24 @@
     @brief get a value (explicit); special case
 
     Explicit type conversion between the JSON value and a compatible value
-    which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible)
-    and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible).
+    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
+    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
     The value is converted by calling the @ref json_serializer<ValueType>
     `from_json()` method.
 
     The function is equivalent to executing
     @code {.cpp}
-    return adl_serializer<ValueTypeCV, void>::from_json(*this);
+    return JSONSerializer<ValueType>::from_json(*this);
     @endcode
 
     This overloads is chosen if:
-    - @a ValueType is not @ref json and
+    - @a ValueType is not @ref basic_json and
     - @ref json_serializer<ValueType> has a `from_json()` method of the form
-      `ValueType from_json(const json&)`
+      `ValueType from_json(const basic_json&)`
 
     @note If @ref json_serializer<ValueType> has both overloads of
     `from_json()`, this one is chosen.
 
-    @tparam ValueTypeCV the provided value type
     @tparam ValueType the returned value type
 
     @return copy of the JSON value, converted to @a ValueType
@@ -4392,16 +1645,116 @@
 
     @since version 2.1.0
     */
-    template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
-             detail::enable_if_t<not std::is_same<json_t, ValueType>::value and
-                                 detail::has_non_default_from_json<json_t, ValueType>::value,
-                                 int> = 0>
-    ValueType get() const noexcept(noexcept(
-                                       adl_serializer<ValueTypeCV, void>::from_json(std::declval<const json_t&>())))
+    template < typename ValueType,
+               detail::enable_if_t <
+                   detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+                   int > = 0 >
+    ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(
+                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))
     {
-        static_assert(not std::is_reference<ValueTypeCV>::value,
+        return JSONSerializer<ValueType>::from_json(*this);
+    }
+
+    /*!
+    @brief get special-case overload
+
+    This overloads converts the current @ref basic_json in a different
+    @ref basic_json type
+
+    @tparam BasicJsonType == @ref basic_json
+
+    @return a copy of *this, converted into @a BasicJsonType
+
+    @complexity Depending on the implementation of the called `from_json()`
+                method.
+
+    @since version 3.2.0
+    */
+    template < typename BasicJsonType,
+               detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value,
+                   int > = 0 >
+    BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const
+    {
+        return *this;
+    }
+
+    /*!
+    @brief get special-case overload
+
+    This overloads avoids a lot of template boilerplate, it can be seen as the
+    identity method
+
+    @tparam BasicJsonType == @ref basic_json
+
+    @return a copy of *this
+
+    @complexity Constant.
+
+    @since version 2.1.0
+    */
+    template<typename BasicJsonType,
+             detail::enable_if_t<
+                 std::is_same<BasicJsonType, basic_json_t>::value,
+                 int> = 0>
+    basic_json get_impl(detail::priority_tag<3> /*unused*/) const
+    {
+        return *this;
+    }
+
+    /*!
+    @brief get a pointer value (explicit)
+    @copydoc get()
+    */
+    template<typename PointerType,
+             detail::enable_if_t<
+                 std::is_pointer<PointerType>::value,
+                 int> = 0>
+    constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept
+    -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
+    {
+        // delegate the call to get_ptr
+        return get_ptr<PointerType>();
+    }
+
+  public:
+    /*!
+    @brief get a (pointer) value (explicit)
+
+    Performs explicit type conversion between the JSON value and a compatible value if required.
+
+    - If the requested type is a pointer to the internally stored JSON value that pointer is returned.
+    No copies are made.
+
+    - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible
+    from the current @ref basic_json.
+
+    - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()`
+    method.
+
+    @tparam ValueTypeCV the provided value type
+    @tparam ValueType the returned value type
+
+    @return copy of the JSON value, converted to @tparam ValueType if necessary
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
+
+    @since version 2.1.0
+    */
+    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
+#if defined(JSON_HAS_CPP_14)
+    constexpr
+#endif
+    auto get() const noexcept(
+    noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})))
+    -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))
+    {
+        // we cannot static_assert on ValueTypeCV being non-const, because
+        // there is support for get<const basic_json_t>(), which is why we
+        // still need the uncvref
+        static_assert(!std::is_reference<ValueTypeCV>::value,
                       "get() cannot be used with reference types, you might want to use get_ref()");
-        return adl_serializer<ValueTypeCV, void>::from_json(*this);
+        return get_impl<ValueType>(detail::priority_tag<4> {});
     }
 
     /*!
@@ -4414,7 +1767,8 @@
     changes.
 
     @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
-    object_t, `std::string`, bool, int64_t, uint64_t, or double.
+    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
+    @ref number_unsigned_t, or @ref number_float_t.
 
     @return pointer to the internally stored JSON value if the requested
     pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@@ -4426,132 +1780,59 @@
     `nullptr` is returned if the value and the requested pointer type does not
     match.,get__PointerType}
 
-    @sa @ref get_ptr() for explicit pointer-member access
+    @sa see @ref get_ptr() for explicit pointer-member access
 
     @since version 1.0.0
     */
     template<typename PointerType, typename std::enable_if<
                  std::is_pointer<PointerType>::value, int>::type = 0>
-    PointerType get() noexcept
+    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
     {
         // delegate the call to get_ptr
         return get_ptr<PointerType>();
     }
 
-    /*!
-    @brief get a pointer value (explicit)
-    @copydoc get()
-    */
-    template<typename PointerType, typename std::enable_if<
-                 std::is_pointer<PointerType>::value, int>::type = 0>
-    const PointerType get() const noexcept
+    /// @brief get a value (explicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_to/
+    template < typename ValueType,
+               detail::enable_if_t <
+                   !detail::is_basic_json<ValueType>::value&&
+                   detail::has_from_json<basic_json_t, ValueType>::value,
+                   int > = 0 >
+    ValueType & get_to(ValueType& v) const noexcept(noexcept(
+                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
     {
-        // delegate the call to get_ptr
-        return get_ptr<PointerType>();
+        JSONSerializer<ValueType>::from_json(*this, v);
+        return v;
     }
 
-    /*!
-    @brief get a pointer value (implicit)
-
-    Implicit pointer access to the internally stored JSON value. No copies are
-    made.
-
-    @warning Writing data to the pointee of the result yields an undefined
-    state.
-
-    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
-    object_t, `std::string`, bool, int64_t,
-    uint64_t, or double. Enforced by a static assertion.
-
-    @return pointer to the internally stored JSON value if the requested
-    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how pointers to internal values of a
-    JSON value can be requested. Note that no type conversions are made and a
-    `nullptr` is returned if the value and the requested pointer type does not
-    match.,get_ptr}
-
-    @since version 1.0.0
-    */
-    template<typename PointerType, typename std::enable_if<
-                 std::is_pointer<PointerType>::value, int>::type = 0>
-    PointerType get_ptr() noexcept
+    // specialization to allow calling get_to with a basic_json value
+    // see https://github.com/nlohmann/json/issues/2175
+    template<typename ValueType,
+             detail::enable_if_t <
+                 detail::is_basic_json<ValueType>::value,
+                 int> = 0>
+    ValueType & get_to(ValueType& v) const
     {
-        // get the type of the PointerType (remove pointer and const)
-        using pointee_t = typename std::remove_const<typename
-                          std::remove_pointer<typename
-                          std::remove_const<PointerType>::type>::type>::type;
-        // make sure the type matches the allowed types
-        static_assert(
-            std::is_same<object_t, pointee_t>::value
-            or std::is_same<array_t, pointee_t>::value
-            or std::is_same<std::string, pointee_t>::value
-            or std::is_same<bool, pointee_t>::value
-            or std::is_same<int64_t, pointee_t>::value
-            or std::is_same<uint64_t, pointee_t>::value
-            or std::is_same<double, pointee_t>::value
-            , "incompatible pointer type");
-
-        // delegate the call to get_impl_ptr<>()
-        return get_impl_ptr(static_cast<PointerType>(nullptr));
+        v = *this;
+        return v;
     }
 
-    /*!
-    @brief get a pointer value (implicit)
-    @copydoc get_ptr()
-    */
-    template<typename PointerType, typename std::enable_if<
-                 std::is_pointer<PointerType>::value and
-                 std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
-    const PointerType get_ptr() const noexcept
+    template <
+        typename T, std::size_t N,
+        typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+        detail::enable_if_t <
+            detail::has_from_json<basic_json_t, Array>::value, int > = 0 >
+    Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+    noexcept(noexcept(JSONSerializer<Array>::from_json(
+                          std::declval<const basic_json_t&>(), v)))
     {
-        // get the type of the PointerType (remove pointer and const)
-        using pointee_t = typename std::remove_const<typename
-                          std::remove_pointer<typename
-                          std::remove_const<PointerType>::type>::type>::type;
-        // make sure the type matches the allowed types
-        static_assert(
-            std::is_same<object_t, pointee_t>::value
-            or std::is_same<array_t, pointee_t>::value
-            or std::is_same<std::string, pointee_t>::value
-            or std::is_same<bool, pointee_t>::value
-            or std::is_same<int64_t, pointee_t>::value
-            or std::is_same<uint64_t, pointee_t>::value
-            or std::is_same<double, pointee_t>::value
-            , "incompatible pointer type");
-
-        // delegate the call to get_impl_ptr<>() const
-        return get_impl_ptr(static_cast<PointerType>(nullptr));
+        JSONSerializer<Array>::from_json(*this, v);
+        return v;
     }
 
-    /*!
-    @brief get a reference value (implicit)
-
-    Implicit reference access to the internally stored JSON value. No copies
-    are made.
-
-    @warning Writing data to the referee of the result yields an undefined
-    state.
-
-    @tparam ReferenceType reference type; must be a reference to @ref array_t,
-    @ref object_t, std::string, bool, int64_t, or
-    double. Enforced by static assertion.
-
-    @return reference to the internally stored JSON value if the requested
-    reference type @a ReferenceType fits to the JSON value; throws
-    type_error.303 otherwise
-
-    @throw type_error.303 in case passed type @a ReferenceType is incompatible
-    with the stored JSON value; see example below
-
-    @complexity Constant.
-
-    @liveexample{The example shows several calls to `get_ref()`.,get_ref}
-
-    @since version 1.1.0
-    */
+    /// @brief get a reference value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ref/
     template<typename ReferenceType, typename std::enable_if<
                  std::is_reference<ReferenceType>::value, int>::type = 0>
     ReferenceType get_ref()
@@ -4560,13 +1841,11 @@
         return get_ref_impl<ReferenceType>(*this);
     }
 
-    /*!
-    @brief get a reference value (implicit)
-    @copydoc get_ref()
-    */
-    template<typename ReferenceType, typename std::enable_if<
-                 std::is_reference<ReferenceType>::value and
-                 std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0>
+    /// @brief get a reference value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ref/
+    template < typename ReferenceType, typename std::enable_if <
+                   std::is_reference<ReferenceType>::value&&
+                   std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >
     ReferenceType get_ref() const
     {
         // delegate call to get_ref_impl
@@ -4581,7 +1860,7 @@
 
     @tparam ValueType non-pointer type compatible to the JSON value, for
     instance `int` for JSON integer numbers, `bool` for JSON booleans, or
-    `std::vector` types for JSON arrays. The character type of `std::string`
+    `std::vector` types for JSON arrays. The character type of @ref string_t
     as well as an initializer list of this type is excluded to avoid
     ambiguities as these types implicitly convert to `std::string`.
 
@@ -4603,21 +1882,51 @@
     @since version 1.0.0
     */
     template < typename ValueType, typename std::enable_if <
-                   not std::is_pointer<ValueType>::value and
-                   not std::is_same<ValueType, detail::json_ref<json>>::value and
-                   not std::is_same<ValueType, std::string::value_type>::value and
-                   not detail::is_json<ValueType>::value
-#ifndef _MSC_VER  // fix for issue #167 operator<< ambiguity under VS2015
-                   and not std::is_same<ValueType, std::initializer_list<std::string::value_type>>::value
+                   detail::conjunction <
+                       detail::negation<std::is_pointer<ValueType>>,
+                       detail::negation<std::is_same<ValueType, std::nullptr_t>>,
+                       detail::negation<std::is_same<ValueType, detail::json_ref<basic_json>>>,
+                                        detail::negation<std::is_same<ValueType, typename string_t::value_type>>,
+                                        detail::negation<detail::is_basic_json<ValueType>>,
+                                        detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,
+#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))
+                                                detail::negation<std::is_same<ValueType, std::string_view>>,
 #endif
-                   and not std::is_same<ValueType, typename std::string_view>::value
-                   , int >::type = 0 >
-    operator ValueType() const
+#if defined(JSON_HAS_CPP_17)
+                                                detail::negation<std::is_same<ValueType, std::any>>,
+#endif
+                                                detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType>
+                                                >::value, int >::type = 0 >
+                                        JSON_EXPLICIT operator ValueType() const
     {
         // delegate the call to get<>() const
         return get<ValueType>();
     }
 
+    /// @brief get a binary value
+    /// @sa https://json.nlohmann.me/api/basic_json/get_binary/
+    binary_t& get_binary()
+    {
+        if (!is_binary())
+        {
+            JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
+        }
+
+        return *get_ptr<binary_t*>();
+    }
+
+    /// @brief get a binary value
+    /// @sa https://json.nlohmann.me/api/basic_json/get_binary/
+    const binary_t& get_binary() const
+    {
+        if (!is_binary())
+        {
+            JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
+        }
+
+        return *get_ptr<const binary_t*>();
+    }
+
     /// @}
 
 
@@ -4629,542 +1938,478 @@
     /// Access to the JSON value.
     /// @{
 
-    /*!
-    @brief access specified array element with bounds checking
-
-    Returns a reference to the element at specified location @a idx, with
-    bounds checking.
-
-    @param[in] idx  index of the element to access
-
-    @return reference to the element at index @a idx
-
-    @throw type_error.304 if the JSON value is not an array; in this case,
-    calling `at` with an index makes no sense. See example below.
-    @throw out_of_range.401 if the index @a idx is out of range of the array;
-    that is, `idx >= size()`. See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @since version 1.0.0
-
-    @liveexample{The example below shows how array elements can be read and
-    written using `at()`. It also demonstrates the different exceptions that
-    can be thrown.,at__size_type}
-    */
-    reference at(size_type idx);
-
-    /*!
-    @brief access specified array element with bounds checking
-
-    Returns a const reference to the element at specified location @a idx,
-    with bounds checking.
-
-    @param[in] idx  index of the element to access
-
-    @return const reference to the element at index @a idx
-
-    @throw type_error.304 if the JSON value is not an array; in this case,
-    calling `at` with an index makes no sense. See example below.
-    @throw out_of_range.401 if the index @a idx is out of range of the array;
-    that is, `idx >= size()`. See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @since version 1.0.0
-
-    @liveexample{The example below shows how array elements can be read using
-    `at()`. It also demonstrates the different exceptions that can be thrown.,
-    at__size_type_const}
-    */
-    const_reference at(size_type idx) const;
-
-    /*!
-    @brief access specified object element with bounds checking
-
-    Returns a reference to the element at with specified key @a key, with
-    bounds checking.
-
-    @param[in] key  key of the element to access
-
-    @return reference to the element at key @a key
-
-    @throw type_error.304 if the JSON value is not an object; in this case,
-    calling `at` with a key makes no sense. See example below.
-    @throw out_of_range.403 if the key @a key is is not stored in the object;
-    that is, `find(key) == end()`. See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Logarithmic in the size of the container.
-
-    @sa @ref operator[](const typename object_t::key_type&) for unchecked
-    access by reference
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.0.0
-
-    @liveexample{The example below shows how object elements can be read and
-    written using `at()`. It also demonstrates the different exceptions that
-    can be thrown.,at__object_t_key_type}
-    */
-    reference at(std::string_view key);
-
-    /*!
-    @brief access specified object element with bounds checking
-
-    Returns a const reference to the element at with specified key @a key,
-    with bounds checking.
-
-    @param[in] key  key of the element to access
-
-    @return const reference to the element at key @a key
-
-    @throw type_error.304 if the JSON value is not an object; in this case,
-    calling `at` with a key makes no sense. See example below.
-    @throw out_of_range.403 if the key @a key is is not stored in the object;
-    that is, `find(key) == end()`. See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Logarithmic in the size of the container.
-
-    @sa @ref operator[](const typename object_t::key_type&) for unchecked
-    access by reference
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.0.0
-
-    @liveexample{The example below shows how object elements can be read using
-    `at()`. It also demonstrates the different exceptions that can be thrown.,
-    at__object_t_key_type_const}
-    */
-    const_reference at(std::string_view key) const;
-
-    /*!
-    @brief access specified array element
-
-    Returns a reference to the element at specified location @a idx.
-
-    @note If @a idx is beyond the range of the array (i.e., `idx >= size()`),
-    then the array is silently filled up with `null` values to make `idx` a
-    valid reference to the last stored element.
-
-    @param[in] idx  index of the element to access
-
-    @return reference to the element at index @a idx
-
-    @throw type_error.305 if the JSON value is not an array or null; in that
-    cases, using the [] operator with an index makes no sense.
-
-    @complexity Constant if @a idx is in the range of the array. Otherwise
-    linear in `idx - size()`.
-
-    @liveexample{The example below shows how array elements can be read and
-    written using `[]` operator. Note the addition of `null`
-    values.,operatorarray__size_type}
-
-    @since version 1.0.0
-    */
-    reference operator[](size_type idx);
-
-    /*!
-    @brief access specified array element
-
-    Returns a const reference to the element at specified location @a idx.
-
-    @param[in] idx  index of the element to access
-
-    @return const reference to the element at index @a idx
-
-    @throw type_error.305 if the JSON value is not an array; in that case,
-    using the [] operator with an index makes no sense.
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how array elements can be read using
-    the `[]` operator.,operatorarray__size_type_const}
-
-    @since version 1.0.0
-    */
-    const_reference operator[](size_type idx) const;
-
-    /*!
-    @brief access specified object element
-
-    Returns a reference to the element at with specified key @a key.
-
-    @note If @a key is not found in the object, then it is silently added to
-    the object and filled with a `null` value to make `key` a valid reference.
-    In case the value was `null` before, it is converted to an object.
-
-    @param[in] key  key of the element to access
-
-    @return reference to the element at key @a key
-
-    @throw type_error.305 if the JSON value is not an object or null; in that
-    cases, using the [] operator with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be read and
-    written using the `[]` operator.,operatorarray__key_type}
-
-    @sa @ref at(const typename object_t::key_type&) for access by reference
-    with range checking
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.0.0
-    */
-    reference operator[](std::string_view key);
-
-    /*!
-    @brief read-only access specified object element
-
-    Returns a const reference to the element at with specified key @a key. No
-    bounds checking is performed.
-
-    @warning If the element with key @a key does not exist, the behavior is
-    undefined.
-
-    @param[in] key  key of the element to access
-
-    @return const reference to the element at key @a key
-
-    @pre The element with key @a key must exist. **This precondition is
-         enforced with an assertion.**
-
-    @throw type_error.305 if the JSON value is not an object; in that case,
-    using the [] operator with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be read using
-    the `[]` operator.,operatorarray__key_type_const}
-
-    @sa @ref at(const typename object_t::key_type&) for access by reference
-    with range checking
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.0.0
-    */
-    const_reference operator[](std::string_view key) const;
-
-    /*!
-    @brief access specified object element
-
-    Returns a reference to the element at with specified key @a key.
-
-    @note If @a key is not found in the object, then it is silently added to
-    the object and filled with a `null` value to make `key` a valid reference.
-    In case the value was `null` before, it is converted to an object.
-
-    @param[in] key  key of the element to access
-
-    @return reference to the element at key @a key
-
-    @throw type_error.305 if the JSON value is not an object or null; in that
-    cases, using the [] operator with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be read and
-    written using the `[]` operator.,operatorarray__key_type}
-
-    @sa @ref at(const typename object_t::key_type&) for access by reference
-    with range checking
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.1.0
-    */
-    template<typename T>
-    reference operator[](T* key)
+    /// @brief access specified array element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    reference at(size_type idx)
     {
-        // implicitly convert null to object
+        // at only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            JSON_TRY
+            {
+                return set_parent(m_value.array->at(idx));
+            }
+            JSON_CATCH (std::out_of_range&)
+            {
+                // create better exception explanation
+                JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+            }
+        }
+        else
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+    }
+
+    /// @brief access specified array element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    const_reference at(size_type idx) const
+    {
+        // at only works for arrays
+        if (JSON_HEDLEY_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, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+            }
+        }
+        else
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    reference at(const typename object_t::key_type& key)
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_value.object->find(key);
+        if (it == m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
+        }
+        return set_parent(it->second);
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    reference at(KeyType && key)
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_value.object->find(std::forward<KeyType>(key));
+        if (it == m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
+        }
+        return set_parent(it->second);
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    const_reference at(const typename object_t::key_type& key) const
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_value.object->find(key);
+        if (it == m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
+        }
+        return it->second;
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    const_reference at(KeyType && key) const
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_value.object->find(std::forward<KeyType>(key));
+        if (it == m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
+        }
+        return it->second;
+    }
+
+    /// @brief access specified array element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    reference operator[](size_type idx)
+    {
+        // implicitly convert null value to an empty array
         if (is_null())
         {
-            m_type = value_t::object;
-            m_value = value_t::object;
+            m_type = value_t::array;
+            m_value.array = create<array_t>();
             assert_invariant();
         }
 
-        // at only works for objects
-        if (JSON_LIKELY(is_object()))
+        // operator[] only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
         {
-            return m_value.object->operator[](key);
+            // fill up array with null values if given idx is outside range
+            if (idx >= m_value.array->size())
+            {
+#if JSON_DIAGNOSTICS
+                // remember array size & capacity before resizing
+                const auto old_size = m_value.array->size();
+                const auto old_capacity = m_value.array->capacity();
+#endif
+                m_value.array->resize(idx + 1);
+
+#if JSON_DIAGNOSTICS
+                if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))
+                {
+                    // capacity has changed: update all parents
+                    set_parents();
+                }
+                else
+                {
+                    // set parent for values added above
+                    set_parents(begin() + static_cast<typename iterator::difference_type>(old_size), static_cast<typename iterator::difference_type>(idx + 1 - old_size));
+                }
+#endif
+                assert_invariant();
+            }
+
+            return m_value.array->operator[](idx);
         }
 
-        JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
     }
 
-    /*!
-    @brief read-only access specified object element
+    /// @brief access specified array element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    const_reference operator[](size_type idx) const
+    {
+        // const operator[] only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            return m_value.array->operator[](idx);
+        }
 
-    Returns a const reference to the element at with specified key @a key. No
-    bounds checking is performed.
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
+    }
 
-    @warning If the element with key @a key does not exist, the behavior is
-    undefined.
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    reference operator[](typename object_t::key_type 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();
+        }
 
-    @param[in] key  key of the element to access
+        // operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto result = m_value.object->emplace(std::move(key), nullptr);
+            return set_parent(result.first->second);
+        }
 
-    @return const reference to the element at key @a key
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
 
-    @pre The element with key @a key must exist. **This precondition is
-         enforced with an assertion.**
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    const_reference operator[](const typename object_t::key_type& key) const
+    {
+        // const operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto it = m_value.object->find(key);
+            JSON_ASSERT(it != m_value.object->end());
+            return it->second;
+        }
 
-    @throw type_error.305 if the JSON value is not an object; in that case,
-    using the [] operator with a key makes no sense.
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
 
-    @complexity Logarithmic in the size of the container.
+    // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
+    // (they seemingly cannot be constrained to resolve the ambiguity)
+    template<typename T>
+    reference operator[](T* key)
+    {
+        return operator[](typename object_t::key_type(key));
+    }
 
-    @liveexample{The example below shows how object elements can be read using
-    the `[]` operator.,operatorarray__key_type_const}
-
-    @sa @ref at(const typename object_t::key_type&) for access by reference
-    with range checking
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.1.0
-    */
     template<typename T>
     const_reference operator[](T* key) const
     {
-        // at only works for objects
-        if (JSON_LIKELY(is_object()))
+        return operator[](typename object_t::key_type(key));
+    }
+
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    reference operator[](KeyType && key)
+    {
+        // implicitly convert null value to an empty object
+        if (is_null())
         {
-            assert(m_value.object->find(key) != m_value.object->end());
-            return m_value.object->find(key)->second;
+            m_type = value_t::object;
+            m_value.object = create<object_t>();
+            assert_invariant();
         }
 
-        JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
+        // operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto result = m_value.object->emplace(std::forward<KeyType>(key), nullptr);
+            return set_parent(result.first->second);
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
     }
 
-    /*!
-    @brief access specified object element with default value
-
-    Returns either a copy of an object's element at the specified key @a key
-    or a given default value if no element with key @a key exists.
-
-    The function is basically equivalent to executing
-    @code {.cpp}
-    try {
-        return at(key);
-    } catch(out_of_range) {
-        return default_value;
-    }
-    @endcode
-
-    @note Unlike @ref at(const typename object_t::key_type&), this function
-    does not throw if the given key @a key was not found.
-
-    @note Unlike @ref operator[](const typename object_t::key_type& key), this
-    function does not implicitly add an element to the position defined by @a
-    key. This function is furthermore also applicable to const objects.
-
-    @param[in] key  key of the element to access
-    @param[in] default_value  the value to return if @a key is not found
-
-    @tparam ValueType type compatible to JSON values, for instance `int` for
-    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for
-    JSON arrays. Note the type of the expected value at @a key and the default
-    value @a default_value must be compatible.
-
-    @return copy of the element at key @a key or @a default_value if @a key
-    is not found
-
-    @throw type_error.306 if the JSON value is not an object; in that case,
-    using `value()` with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be queried
-    with a default value.,json__value}
-
-    @sa @ref at(const typename object_t::key_type&) for access by reference
-    with range checking
-    @sa @ref operator[](const typename object_t::key_type&) for unchecked
-    access by reference
-
-    @since version 1.0.0
-    */
-    template<class ValueType, typename std::enable_if<
-                 std::is_convertible<json_t, ValueType>::value, int>::type = 0>
-    ValueType value(std::string_view key, const ValueType& default_value) const
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    const_reference operator[](KeyType && key) const
     {
-        // at only works for objects
-        if (JSON_LIKELY(is_object()))
+        // const operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto it = m_value.object->find(std::forward<KeyType>(key));
+            JSON_ASSERT(it != m_value.object->end());
+            return it->second;
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
+
+  private:
+    template<typename KeyType>
+    using is_comparable_with_object_key = detail::is_comparable <
+        object_comparator_t, const typename object_t::key_type&, KeyType >;
+
+    template<typename ValueType>
+    using value_return_type = std::conditional <
+        detail::is_c_string_uncvref<ValueType>::value,
+        string_t, typename std::decay<ValueType>::type >;
+
+  public:
+    /// @brief access specified object element with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, detail::enable_if_t <
+                   !detail::is_transparent<object_comparator_t>::value
+                   && detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
         {
             // if key is found, return value and given default value otherwise
             const auto it = find(key);
             if (it != end())
             {
-                return *it;
+                return it->template get<ValueType>();
             }
 
             return default_value;
         }
 
-        JSON_THROW(type_error::create(306, "cannot use value() with", type_name()));
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
     }
 
-    /*!
-    @brief overload for a default value of type const char*
-    @copydoc json::value(const typename object_t::key_type&, ValueType) const
-    */
-    std::string value(std::string_view key, const char* default_value) const
+    /// @brief access specified object element with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   !detail::is_transparent<object_comparator_t>::value
+                   && detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const
     {
-        return value(key, std::string(default_value));
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(key);
+            if (it != end())
+            {
+                return it->template get<ReturnType>();
+            }
+
+            return std::forward<ValueType>(default_value);
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
     }
 
-    /*!
-    @brief access specified object element via JSON Pointer with default value
+    /// @brief access specified object element with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class KeyType, detail::enable_if_t <
+                   detail::is_transparent<object_comparator_t>::value
+                   && !detail::is_json_pointer<KeyType>::value
+                   && is_comparable_with_object_key<KeyType>::value
+                   && detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ValueType value(KeyType && key, const ValueType& default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(std::forward<KeyType>(key));
+            if (it != end())
+            {
+                return it->template get<ValueType>();
+            }
 
-    Returns either a copy of an object's element at the specified key @a key
-    or a given default value if no element with key @a key exists.
+            return default_value;
+        }
 
-    The function is basically equivalent to executing
-    @code {.cpp}
-    try {
-        return at(ptr);
-    } catch(out_of_range) {
-        return default_value;
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
     }
-    @endcode
 
-    @note Unlike @ref at(const json_pointer&), this function does not throw
-    if the given key @a key was not found.
+    /// @brief access specified object element via JSON Pointer with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class KeyType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   detail::is_transparent<object_comparator_t>::value
+                   && !detail::is_json_pointer<KeyType>::value
+                   && is_comparable_with_object_key<KeyType>::value
+                   && detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ReturnType value(KeyType && key, ValueType && default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(std::forward<KeyType>(key));
+            if (it != end())
+            {
+                return it->template get<ReturnType>();
+            }
 
-    @param[in] ptr  a JSON pointer to the element to access
-    @param[in] default_value  the value to return if @a ptr found no value
+            return std::forward<ValueType>(default_value);
+        }
 
-    @tparam ValueType type compatible to JSON values, for instance `int` for
-    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for
-    JSON arrays. Note the type of the expected value at @a key and the default
-    value @a default_value must be compatible.
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
 
-    @return copy of the element at key @a key or @a default_value if @a key
-    is not found
-
-    @throw type_error.306 if the JSON value is not an object; in that case,
-    using `value()` with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be queried
-    with a default value.,json__value_ptr}
-
-    @sa @ref operator[](const json_pointer&) for unchecked access by reference
-
-    @since version 2.0.2
-    */
-    template<class ValueType, typename std::enable_if<
-                 std::is_convertible<json_t, ValueType>::value, int>::type = 0>
+    /// @brief access specified object element via JSON Pointer with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, detail::enable_if_t <
+                   detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
     ValueType value(const json_pointer& ptr, const ValueType& default_value) const
     {
-        // at only works for objects
-        if (JSON_LIKELY(is_object()))
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
         {
             // if pointer resolves a value, return it or use default value
             JSON_TRY
             {
-                return ptr.get_checked(this);
+                return ptr.get_checked(this).template get<ValueType>();
             }
-            JSON_CATCH (out_of_range&)
+            JSON_INTERNAL_CATCH (out_of_range&)
             {
                 return default_value;
             }
         }
 
-        JSON_THROW(type_error::create(306, "cannot use value() with", type_name()));
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
     }
 
-    /*!
-    @brief overload for a default value of type const char*
-    @copydoc json::value(const json_pointer&, ValueType) const
-    */
-    std::string value(const json_pointer& ptr, const char* default_value) const
+    /// @brief access specified object element via JSON Pointer with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ReturnType value(const json_pointer& ptr, ValueType && default_value) const
     {
-        return value(ptr, std::string(default_value));
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if pointer resolves a value, return it or use default value
+            JSON_TRY
+            {
+                return ptr.get_checked(this).template get<ReturnType>();
+            }
+            JSON_INTERNAL_CATCH (out_of_range&)
+            {
+                return std::forward<ValueType>(default_value);
+            }
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
     }
 
-    /*!
-    @brief access the first element
+    template < class ValueType, class BasicJsonType, detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value
+                   && detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or wpi::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    ValueType value(const ::wpi::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const
+    {
+        return value(ptr.convert(), default_value);
+    }
 
-    Returns a reference to the first element in the container. For a JSON
-    container `c`, the expression `c.front()` is equivalent to `*c.begin()`.
+    template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value
+                   && detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or wpi::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    ReturnType value(const ::wpi::json_pointer<BasicJsonType>& ptr, ValueType && default_value) const
+    {
+        return value(ptr.convert(), std::forward<ValueType>(default_value));
+    }
 
-    @return In case of a structured type (array or object), a reference to the
-    first element is returned. In case of number, string, or boolean values, a
-    reference to the value is returned.
-
-    @complexity Constant.
-
-    @pre The JSON value must not be `null` (would throw `std::out_of_range`)
-    or an empty array or object (undefined behavior, **guarded by
-    assertions**).
-    @post The JSON value remains unchanged.
-
-    @throw invalid_iterator.214 when called on `null` value
-
-    @liveexample{The following code shows an example for `front()`.,front}
-
-    @sa @ref back() -- access the last element
-
-    @since version 1.0.0
-    */
+    /// @brief access the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/front/
     reference front()
     {
         return *begin();
     }
 
-    /*!
-    @copydoc json::front()
-    */
+    /// @brief access the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/front/
     const_reference front() const
     {
         return *cbegin();
     }
 
-    /*!
-    @brief access the last element
-
-    Returns a reference to the last element in the container. For a JSON
-    container `c`, the expression `c.back()` is equivalent to
-    @code {.cpp}
-    auto tmp = c.end();
-    --tmp;
-    return *tmp;
-    @endcode
-
-    @return In case of a structured type (array or object), a reference to the
-    last element is returned. In case of number, string, or boolean values, a
-    reference to the value is returned.
-
-    @complexity Constant.
-
-    @pre The JSON value must not be `null` (would throw `std::out_of_range`)
-    or an empty array or object (undefined behavior, **guarded by
-    assertions**).
-    @post The JSON value remains unchanged.
-
-    @throw invalid_iterator.214 when called on a `null` value. See example
-    below.
-
-    @liveexample{The following code shows an example for `back()`.,back}
-
-    @sa @ref front() -- access the first element
-
-    @since version 1.0.0
-    */
+    /// @brief access the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/back/
     reference back()
     {
         auto tmp = end();
@@ -5172,9 +2417,8 @@
         return *tmp;
     }
 
-    /*!
-    @copydoc json::back()
-    */
+    /// @brief access the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/back/
     const_reference back() const
     {
         auto tmp = cend();
@@ -5182,161 +2426,17 @@
         return *tmp;
     }
 
-    /*!
-    @brief remove element given an iterator
-
-    Removes the element specified by iterator @a pos. The iterator @a pos must
-    be valid and dereferenceable. Thus the `end()` iterator (which is valid,
-    but is not dereferenceable) cannot be used as a value for @a pos.
-
-    If called on a primitive type other than `null`, the resulting JSON value
-    will be `null`.
-
-    @param[in] pos iterator to the element to remove
-
-    @tparam IteratorType an @ref iterator or @ref const_iterator
-
-    @post Invalidates iterators and references at or after the point of the
-    erase, including the `end()` iterator.
-
-    @throw type_error.307 if called on a `null` value; example: `"cannot use
-    erase() with null"`
-    @throw invalid_iterator.202 if called on an iterator which does not belong
-    to the current JSON value; example: `"iterator does not fit current
-    value"`
-    @throw invalid_iterator.205 if called on a primitive type with invalid
-    iterator (i.e., any iterator which is not `begin()`); example: `"iterator
-    out of range"`
-
-    @complexity The complexity depends on the type:
-    - objects: amortized constant
-    - arrays: linear in distance between @a pos and the end of the container
-    - strings: linear in the length of the string
-    - other types: constant
-
-    @liveexample{The example shows the result of `erase()` for different JSON
-    types.,erase__IteratorType}
-
-    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
-    the given range
-    @sa @ref erase(std::string_view) -- removes the element
-    from an object at the given key
-    @sa @ref erase(const size_type) -- removes the element from an array at
-    the given index
-
-    @since version 1.0.0
-    */
-    template<class IteratorType, typename std::enable_if<
-                 std::is_same<IteratorType, typename json_t::iterator>::value or
-                 std::is_same<IteratorType, typename json_t::const_iterator>::value, int>::type
-             = 0>
-    void erase(IteratorType pos)
+    /// @brief remove element given an iterator
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    template < class IteratorType, detail::enable_if_t <
+                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >
+    IteratorType erase(IteratorType pos)
     {
         // make sure iterator fits the current value
-        if (JSON_UNLIKELY(this != pos.m_object))
+        if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
         {
-            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
-        }
-
-        switch (m_type)
-        {
-            case value_t::boolean:
-            case value_t::number_float:
-            case value_t::number_integer:
-            case value_t::number_unsigned:
-            case value_t::string:
-            {
-                if (JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin()))
-                {
-                    JSON_THROW(invalid_iterator::create(205, "iterator out of range"));
-                }
-
-                if (is_string())
-                {
-                    std::allocator<std::string> alloc;
-					std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
-					std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
-                    m_value.string = nullptr;
-                }
-
-                m_type = value_t::null;
-                assert_invariant();
-                break;
-            }
-
-            case value_t::object:
-            {
-                m_value.object->erase(pos.m_it.object_iterator);
-                break;
-            }
-
-            case value_t::array:
-            {
-                m_value.array->erase(pos.m_it.array_iterator);
-                break;
-            }
-
-            default:
-                JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
-        }
-    }
-
-    /*!
-    @brief remove elements given an iterator range
-
-    Removes the element specified by the range `[first; last)`. The iterator
-    @a first does not need to be dereferenceable if `first == last`: erasing
-    an empty range is a no-op.
-
-    If called on a primitive type other than `null`, the resulting JSON value
-    will be `null`.
-
-    @param[in] first iterator to the beginning of the range to remove
-    @param[in] last iterator past the end of the range to remove
-    @return Iterator following the last removed element. If the iterator @a
-    second refers to the last element, the `end()` iterator is returned.
-
-    @tparam IteratorType an @ref iterator or @ref const_iterator
-
-    @post Invalidates iterators and references at or after the point of the
-    erase, including the `end()` iterator.
-
-    @throw type_error.307 if called on a `null` value; example: `"cannot use
-    erase() with null"`
-    @throw invalid_iterator.203 if called on iterators which does not belong
-    to the current JSON value; example: `"iterators do not fit current value"`
-    @throw invalid_iterator.204 if called on a primitive type with invalid
-    iterators (i.e., if `first != begin()` and `last != end()`); example:
-    `"iterators out of range"`
-
-    @complexity The complexity depends on the type:
-    - objects: `log(size()) + std::distance(first, last)`
-    - arrays: linear in the distance between @a first and @a last, plus linear
-      in the distance between @a last and end of the container
-    - strings: linear in the length of the string
-    - other types: constant
-
-    @liveexample{The example shows the result of `erase()` for different JSON
-    types.,erase__IteratorType_IteratorType}
-
-    @sa @ref erase(IteratorType) -- removes the element at a given position
-    @sa @ref erase(const typename object_t::key_type&) -- removes the element
-    from an object at the given key
-    @sa @ref erase(const size_type) -- removes the element from an array at
-    the given index
-
-    @since version 1.0.0
-    */
-    template<class IteratorType, typename std::enable_if<
-                 std::is_same<IteratorType, typename json_t::iterator>::value or
-                 std::is_same<IteratorType, typename json_t::const_iterator>::value, int>::type
-             = 0>
-    IteratorType erase(IteratorType first, IteratorType last)
-    {
-        // make sure iterator fits the current value
-        if (JSON_UNLIKELY(this != first.m_object or this != last.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value"));
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
         }
 
         IteratorType result = end();
@@ -5348,26 +2448,111 @@
             case value_t::number_integer:
             case value_t::number_unsigned:
             case value_t::string:
+            case value_t::binary:
             {
-                if (JSON_LIKELY(not first.m_it.primitive_iterator.is_begin()
-                                or not last.m_it.primitive_iterator.is_end()))
+                if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
                 {
-                    JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
+                    JSON_THROW(invalid_iterator::create(205, "iterator out of range", this));
                 }
 
                 if (is_string())
                 {
-                    std::allocator<std::string> alloc;
-					std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
-					std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
+                    AllocatorType<string_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
                     m_value.string = nullptr;
                 }
+                else if (is_binary())
+                {
+                    AllocatorType<binary_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);
+                    m_value.binary = nullptr;
+                }
 
                 m_type = value_t::null;
                 assert_invariant();
                 break;
             }
 
+            case value_t::object:
+            {
+                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+
+        return result;
+    }
+
+    /// @brief remove elements given an iterator range
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    template < class IteratorType, detail::enable_if_t <
+                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >
+    IteratorType erase(IteratorType first, IteratorType last)
+    {
+        // make sure iterator fits the current value
+        if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this));
+        }
+
+        IteratorType result = end();
+
+        switch (m_type)
+        {
+            case value_t::boolean:
+            case value_t::number_float:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::string:
+            case value_t::binary:
+            {
+                if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()
+                                       || !last.m_it.primitive_iterator.is_end()))
+                {
+                    JSON_THROW(invalid_iterator::create(204, "iterators out of range", this));
+                }
+
+                if (is_string())
+                {
+                    AllocatorType<string_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
+                    m_value.string = nullptr;
+                }
+                else if (is_binary())
+                {
+                    AllocatorType<binary_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);
+                    m_value.binary = nullptr;
+                }
+
+                m_type = value_t::null;
+                assert_invariant();
+                break;
+            }
+
+            case value_t::object:
+            {
+                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
+                                              last.m_it.object_iterator);
+                break;
+            }
+
             case value_t::array:
             {
                 result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
@@ -5375,69 +2560,87 @@
                 break;
             }
 
+            case value_t::null:
+            case value_t::discarded:
             default:
-                JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
+                JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
         }
 
         return result;
     }
 
-    /*!
-    @brief remove element from a JSON object given a key
+  private:
+    template < typename KeyType, detail::enable_if_t <
+                   detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    size_type erase_internal(KeyType && key)
+    {
+        // this erase only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
 
-    Removes elements from a JSON object with the key value @a key.
+        return m_value.object->erase(std::forward<KeyType>(key));
+    }
 
-    @param[in] key value of the elements to remove
+    template < typename KeyType, detail::enable_if_t <
+                   !detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    size_type erase_internal(KeyType && key)
+    {
+        // this erase only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
 
-    @return Number of elements removed. If @a ObjectType is the default
-    `std::map` type, the return value will always be `0` (@a key was not
-    found) or `1` (@a key was found).
+        const auto it = m_value.object->find(std::forward<KeyType>(key));
+        if (it != m_value.object->end())
+        {
+            m_value.object->erase(it);
+            return 1;
+        }
+        return 0;
+    }
 
-    @post References and iterators to the erased elements are invalidated.
-    Other references and iterators are not affected.
+  public:
 
-    @throw type_error.307 when called on a type other than JSON object;
-    example: `"cannot use erase() with null"`
+    /// @brief remove element from a JSON object given a key
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    size_type erase(const typename object_t::key_type& key)
+    {
+        // the indirection via erase_internal() is added to avoid making this
+        // function a template and thus de-rank it during overload resolution
+        return erase_internal(key);
+    }
 
-    @complexity `log(size()) + count(key)`
+    /// @brief remove element from a JSON object given a key
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    size_type erase(KeyType && key)
+    {
+        return erase_internal(std::forward<KeyType>(key));
+    }
 
-    @liveexample{The example shows the effect of `erase()`.,erase__key_type}
+    /// @brief remove element from a JSON array given an index
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    void erase(const size_type idx)
+    {
+        // this erase only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            if (JSON_HEDLEY_UNLIKELY(idx >= size()))
+            {
+                JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+            }
 
-    @sa @ref erase(IteratorType) -- removes the element at a given position
-    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
-    the given range
-    @sa @ref erase(const size_type) -- removes the element from an array at
-    the given index
-
-    @since version 1.0.0
-    */
-    size_type erase(std::string_view key);
-
-    /*!
-    @brief remove element from a JSON array given an index
-
-    Removes element from a JSON array at the index @a idx.
-
-    @param[in] idx index of the element to remove
-
-    @throw type_error.307 when called on a type other than JSON object;
-    example: `"cannot use erase() with null"`
-    @throw out_of_range.401 when `idx >= size()`; example: `"array index 17
-    is out of range"`
-
-    @complexity Linear in distance between @a idx and the end of the container.
-
-    @liveexample{The example shows the effect of `erase()`.,erase__size_type}
-
-    @sa @ref erase(IteratorType) -- removes the element at a given position
-    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
-    the given range
-    @sa @ref erase(const typename object_t::key_type&) -- removes the element
-    from an object at the given key
-
-    @since version 1.0.0
-    */
-    void erase(const size_type idx);
+            m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
+        }
+        else
+        {
+            JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+    }
 
     /// @}
 
@@ -5449,58 +2652,113 @@
     /// @name lookup
     /// @{
 
-    /*!
-    @brief find an element in a JSON object
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    iterator find(const typename object_t::key_type& key)
+    {
+        auto result = end();
 
-    Finds an element in a JSON object with key equivalent to @a key. If the
-    element is not found or the JSON value is not an object, end() is
-    returned.
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_value.object->find(key);
+        }
 
-    @note This method always returns @ref end() when executed on a JSON type
-          that is not an object.
+        return result;
+    }
 
-    @param[in] key key value of the element to search for.
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    const_iterator find(const typename object_t::key_type& key) const
+    {
+        auto result = cend();
 
-    @return Iterator to an element with key equivalent to @a key. If no such
-    element is found or the JSON value is not an object, past-the-end (see
-    @ref end()) iterator is returned.
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_value.object->find(key);
+        }
 
-    @complexity Logarithmic in the size of the JSON object.
+        return result;
+    }
 
-    @liveexample{The example shows how `find()` is used.,find__key_type}
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    iterator find(KeyType && key)
+    {
+        auto result = end();
 
-    @since version 1.0.0
-    */
-    iterator find(std::string_view key);
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_value.object->find(std::forward<KeyType>(key));
+        }
 
-    /*!
-    @brief find an element in a JSON object
-    @copydoc find(KeyT&&)
-    */
-    const_iterator find(std::string_view key) const;
+        return result;
+    }
 
-    /*!
-    @brief returns the number of occurrences of a key in a JSON object
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    const_iterator find(KeyType && key) const
+    {
+        auto result = cend();
 
-    Returns the number of elements with key @a key. If ObjectType is the
-    default `std::map` type, the return value will always be `0` (@a key was
-    not found) or `1` (@a key was found).
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_value.object->find(std::forward<KeyType>(key));
+        }
 
-    @note This method always returns `0` when executed on a JSON type that is
-          not an object.
+        return result;
+    }
 
-    @param[in] key key value of the element to count
+    /// @brief returns the number of occurrences of a key in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/count/
+    size_type count(const typename object_t::key_type& key) const
+    {
+        // return 0 for all nonobject types
+        return is_object() ? m_value.object->count(key) : 0;
+    }
 
-    @return Number of elements with key @a key. If the JSON value is not an
-    object, the return value will be `0`.
+    /// @brief returns the number of occurrences of a key in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/count/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    size_type count(KeyType && key) const
+    {
+        // return 0 for all nonobject types
+        return is_object() ? m_value.object->count(std::forward<KeyType>(key)) : 0;
+    }
 
-    @complexity Logarithmic in the size of the JSON object.
+    /// @brief check the existence of an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/contains/
+    bool contains(const typename object_t::key_type& key) const
+    {
+        return is_object() && m_value.object->find(key) != m_value.object->end();
+    }
 
-    @liveexample{The example shows how `count()` is used.,count}
+    /// @brief check the existence of an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/contains/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    bool contains(KeyType && key) const
+    {
+        return is_object() && m_value.object->find(std::forward<KeyType>(key)) != m_value.object->end();
+    }
 
-    @since version 1.0.0
-    */
-    size_type count(std::string_view key) const;
+    /// @brief check the existence of an element in a JSON object given a JSON pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/contains/
+    bool contains(const json_pointer& ptr) const
+    {
+        return ptr.contains(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or wpi::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    bool contains(const typename ::wpi::json_pointer<BasicJsonType>& ptr) const
+    {
+        return ptr.contains(this);
+    }
 
     /// @}
 
@@ -5512,30 +2770,8 @@
     /// @name iterators
     /// @{
 
-    /*!
-    @brief returns an iterator to the first element
-
-    Returns an iterator to the first element.
-
-    @image html range-begin-end.svg "Illustration from cppreference.com"
-
-    @return iterator to the first element
-
-    @complexity Constant.
-
-    @requirement This function helps `json` satisfying the
-    [Container](http://en.cppreference.com/w/cpp/concept/Container)
-    requirements:
-    - The complexity is constant.
-
-    @liveexample{The following code shows an example for `begin()`.,begin}
-
-    @sa @ref cbegin() -- returns a const iterator to the beginning
-    @sa @ref end() -- returns an iterator to the end
-    @sa @ref cend() -- returns a const iterator to the end
-
-    @since version 1.0.0
-    */
+    /// @brief returns an iterator to the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/begin/
     iterator begin() noexcept
     {
         iterator result(this);
@@ -5543,39 +2779,15 @@
         return result;
     }
 
-    /*!
-    @copydoc json::cbegin()
-    */
+    /// @brief returns an iterator to the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/begin/
     const_iterator begin() const noexcept
     {
         return cbegin();
     }
 
-    /*!
-    @brief returns a const iterator to the first element
-
-    Returns a const iterator to the first element.
-
-    @image html range-begin-end.svg "Illustration from cppreference.com"
-
-    @return const iterator to the first element
-
-    @complexity Constant.
-
-    @requirement This function helps `json` satisfying the
-    [Container](http://en.cppreference.com/w/cpp/concept/Container)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `const_cast<const json&>(*this).begin()`.
-
-    @liveexample{The following code shows an example for `cbegin()`.,cbegin}
-
-    @sa @ref begin() -- returns an iterator to the beginning
-    @sa @ref end() -- returns an iterator to the end
-    @sa @ref cend() -- returns a const iterator to the end
-
-    @since version 1.0.0
-    */
+    /// @brief returns a const iterator to the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/cbegin/
     const_iterator cbegin() const noexcept
     {
         const_iterator result(this);
@@ -5583,30 +2795,8 @@
         return result;
     }
 
-    /*!
-    @brief returns an iterator to one past the last element
-
-    Returns an iterator to one past the last element.
-
-    @image html range-begin-end.svg "Illustration from cppreference.com"
-
-    @return iterator one past the last element
-
-    @complexity Constant.
-
-    @requirement This function helps `json` satisfying the
-    [Container](http://en.cppreference.com/w/cpp/concept/Container)
-    requirements:
-    - The complexity is constant.
-
-    @liveexample{The following code shows an example for `end()`.,end}
-
-    @sa @ref cend() -- returns a const iterator to the end
-    @sa @ref begin() -- returns an iterator to the beginning
-    @sa @ref cbegin() -- returns a const iterator to the beginning
-
-    @since version 1.0.0
-    */
+    /// @brief returns an iterator to one past the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/end/
     iterator end() noexcept
     {
         iterator result(this);
@@ -5614,39 +2804,15 @@
         return result;
     }
 
-    /*!
-    @copydoc json::cend()
-    */
+    /// @brief returns an iterator to one past the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/end/
     const_iterator end() const noexcept
     {
         return cend();
     }
 
-    /*!
-    @brief returns a const iterator to one past the last element
-
-    Returns a const iterator to one past the last element.
-
-    @image html range-begin-end.svg "Illustration from cppreference.com"
-
-    @return const iterator one past the last element
-
-    @complexity Constant.
-
-    @requirement This function helps `json` satisfying the
-    [Container](http://en.cppreference.com/w/cpp/concept/Container)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `const_cast<const json&>(*this).end()`.
-
-    @liveexample{The following code shows an example for `cend()`.,cend}
-
-    @sa @ref end() -- returns an iterator to the end
-    @sa @ref begin() -- returns an iterator to the beginning
-    @sa @ref cbegin() -- returns a const iterator to the beginning
-
-    @since version 1.0.0
-    */
+    /// @brief returns an iterator to one past the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/cend/
     const_iterator cend() const noexcept
     {
         const_iterator result(this);
@@ -5654,198 +2820,80 @@
         return result;
     }
 
-    /*!
-    @brief returns an iterator to the reverse-beginning
-
-    Returns an iterator to the reverse-beginning; that is, the last element.
-
-    @image html range-rbegin-rend.svg "Illustration from cppreference.com"
-
-    @complexity Constant.
-
-    @requirement This function helps `json` satisfying the
-    [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `reverse_iterator(end())`.
-
-    @liveexample{The following code shows an example for `rbegin()`.,rbegin}
-
-    @sa @ref crbegin() -- returns a const reverse iterator to the beginning
-    @sa @ref rend() -- returns a reverse iterator to the end
-    @sa @ref crend() -- returns a const reverse iterator to the end
-
-    @since version 1.0.0
-    */
+    /// @brief returns an iterator to the reverse-beginning
+    /// @sa https://json.nlohmann.me/api/basic_json/rbegin/
     reverse_iterator rbegin() noexcept
     {
         return reverse_iterator(end());
     }
 
-    /*!
-    @copydoc json::crbegin()
-    */
+    /// @brief returns an iterator to the reverse-beginning
+    /// @sa https://json.nlohmann.me/api/basic_json/rbegin/
     const_reverse_iterator rbegin() const noexcept
     {
         return crbegin();
     }
 
-    /*!
-    @brief returns an iterator to the reverse-end
-
-    Returns an iterator to the reverse-end; that is, one before the first
-    element.
-
-    @image html range-rbegin-rend.svg "Illustration from cppreference.com"
-
-    @complexity Constant.
-
-    @requirement This function helps `json` satisfying the
-    [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `reverse_iterator(begin())`.
-
-    @liveexample{The following code shows an example for `rend()`.,rend}
-
-    @sa @ref crend() -- returns a const reverse iterator to the end
-    @sa @ref rbegin() -- returns a reverse iterator to the beginning
-    @sa @ref crbegin() -- returns a const reverse iterator to the beginning
-
-    @since version 1.0.0
-    */
+    /// @brief returns an iterator to the reverse-end
+    /// @sa https://json.nlohmann.me/api/basic_json/rend/
     reverse_iterator rend() noexcept
     {
         return reverse_iterator(begin());
     }
 
-    /*!
-    @copydoc json::crend()
-    */
+    /// @brief returns an iterator to the reverse-end
+    /// @sa https://json.nlohmann.me/api/basic_json/rend/
     const_reverse_iterator rend() const noexcept
     {
         return crend();
     }
 
-    /*!
-    @brief returns a const reverse iterator to the last element
-
-    Returns a const iterator to the reverse-beginning; that is, the last
-    element.
-
-    @image html range-rbegin-rend.svg "Illustration from cppreference.com"
-
-    @complexity Constant.
-
-    @requirement This function helps `json` satisfying the
-    [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `const_cast<const json&>(*this).rbegin()`.
-
-    @liveexample{The following code shows an example for `crbegin()`.,crbegin}
-
-    @sa @ref rbegin() -- returns a reverse iterator to the beginning
-    @sa @ref rend() -- returns a reverse iterator to the end
-    @sa @ref crend() -- returns a const reverse iterator to the end
-
-    @since version 1.0.0
-    */
+    /// @brief returns a const reverse iterator to the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/crbegin/
     const_reverse_iterator crbegin() const noexcept
     {
         return const_reverse_iterator(cend());
     }
 
-    /*!
-    @brief returns a const reverse iterator to one before the first
-
-    Returns a const reverse iterator to the reverse-end; that is, one before
-    the first element.
-
-    @image html range-rbegin-rend.svg "Illustration from cppreference.com"
-
-    @complexity Constant.
-
-    @requirement This function helps `json` satisfying the
-    [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `const_cast<const json&>(*this).rend()`.
-
-    @liveexample{The following code shows an example for `crend()`.,crend}
-
-    @sa @ref rend() -- returns a reverse iterator to the end
-    @sa @ref rbegin() -- returns a reverse iterator to the beginning
-    @sa @ref crbegin() -- returns a const reverse iterator to the beginning
-
-    @since version 1.0.0
-    */
+    /// @brief returns a const reverse iterator to one before the first
+    /// @sa https://json.nlohmann.me/api/basic_json/crend/
     const_reverse_iterator crend() const noexcept
     {
         return const_reverse_iterator(cbegin());
     }
 
   public:
-    /*!
-    @brief helper to access iterator member functions in range-based for
-
-    This function allows to access @ref iterator::key() and @ref
-    iterator::value() during range-based for loops. In these loops, a
-    reference to the JSON values is returned, so there is no access to the
-    underlying iterator.
-
-    For loop without `items()` function:
-
-    @code{cpp}
-    for (auto it = j_object.begin(); it != j_object.end(); ++it)
+    /// @brief wrapper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
+    /// @deprecated This function is deprecated since 3.1.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use @ref items() instead;
+    ///             that is, replace `json::iterator_wrapper(j)` with `j.items()`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
+    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept
     {
-        std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
+        return ref.items();
     }
-    @endcode
 
-    Range-based for loop without `items()` function:
-
-    @code{cpp}
-    for (auto it : j_object)
+    /// @brief wrapper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
+    /// @deprecated This function is deprecated since 3.1.0 and will be removed in
+    ///         version 4.0.0 of the library. Please use @ref items() instead;
+    ///         that is, replace `json::iterator_wrapper(j)` with `j.items()`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
+    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept
     {
-        // "it" is of type json::reference and has no key() member
-        std::cout << "value: " << it << '\n';
+        return ref.items();
     }
-    @endcode
 
-    Range-based for loop with `items()` function:
-
-    @code{cpp}
-    for (auto it : j_object.items())
-    {
-        std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
-    }
-    @endcode
-
-    @note When iterating over an array, `key()` will return the index of the
-          element as string (see example). For primitive types (e.g., numbers),
-          `key()` returns an empty string.
-
-    @return iteration proxy object wrapping @a ref with an interface to use in
-            range-based for loops
-
-    @liveexample{The following code shows how the function is used.,items}
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @since version 3.x.x.
-    */
+    /// @brief helper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
     iteration_proxy<iterator> items() noexcept
     {
         return iteration_proxy<iterator>(*this);
     }
 
-    /*!
-    @copydoc items()
-    */
+    /// @brief helper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
     iteration_proxy<const_iterator> items() const noexcept
     {
         return iteration_proxy<const_iterator>(*this);
@@ -5861,134 +2909,117 @@
     /// @name capacity
     /// @{
 
-    /*!
-    @brief checks whether the container is empty.
+    /// @brief checks whether the container is empty.
+    /// @sa https://json.nlohmann.me/api/basic_json/empty/
+    bool empty() const noexcept
+    {
+        switch (m_type)
+        {
+            case value_t::null:
+            {
+                // null values are empty
+                return true;
+            }
 
-    Checks if a JSON value has no elements (i.e. whether its @ref size is `0`).
+            case value_t::array:
+            {
+                // delegate call to array_t::empty()
+                return m_value.array->empty();
+            }
 
-    @return The return value depends on the different types and is
-            defined as follows:
-            Value type  | return value
-            ----------- | -------------
-            null        | `true`
-            boolean     | `false`
-            string      | `false`
-            number      | `false`
-            object      | result of function `object_t::empty()`
-            array       | result of function `array_t::empty()`
+            case value_t::object:
+            {
+                // delegate call to object_t::empty()
+                return m_value.object->empty();
+            }
 
-    @liveexample{The following code uses `empty()` to check if a JSON
-    object contains any elements.,empty}
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // all other types are nonempty
+                return false;
+            }
+        }
+    }
 
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
-    the Container concept; that is, their `empty()` functions have constant
-    complexity.
+    /// @brief returns the number of elements
+    /// @sa https://json.nlohmann.me/api/basic_json/size/
+    size_type size() const noexcept
+    {
+        switch (m_type)
+        {
+            case value_t::null:
+            {
+                // null values are empty
+                return 0;
+            }
 
-    @iterators No changes.
+            case value_t::array:
+            {
+                // delegate call to array_t::size()
+                return m_value.array->size();
+            }
 
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
+            case value_t::object:
+            {
+                // delegate call to object_t::size()
+                return m_value.object->size();
+            }
 
-    @note This function does not return whether a string stored as JSON value
-    is empty - it returns whether the JSON container itself is empty which is
-    false in the case of a string.
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // all other types have size 1
+                return 1;
+            }
+        }
+    }
 
-    @requirement This function helps `json` satisfying the
-    [Container](http://en.cppreference.com/w/cpp/concept/Container)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `begin() == end()`.
+    /// @brief returns the maximum possible number of elements
+    /// @sa https://json.nlohmann.me/api/basic_json/max_size/
+    size_type max_size() const noexcept
+    {
+        switch (m_type)
+        {
+            case value_t::array:
+            {
+                // delegate call to array_t::max_size()
+                return m_value.array->max_size();
+            }
 
-    @sa @ref size() -- returns the number of elements
+            case value_t::object:
+            {
+                // delegate call to object_t::max_size()
+                return m_value.object->max_size();
+            }
 
-    @since version 1.0.0
-    */
-    bool empty() const noexcept;
-
-    /*!
-    @brief returns the number of elements
-
-    Returns the number of elements in a JSON value.
-
-    @return The return value depends on the different types and is
-            defined as follows:
-            Value type  | return value
-            ----------- | -------------
-            null        | `0`
-            boolean     | `1`
-            string      | `1`
-            number      | `1`
-            object      | result of function object_t::size()
-            array       | result of function array_t::size()
-
-    @liveexample{The following code calls `size()` on the different value
-    types.,size}
-
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
-    the Container concept; that is, their size() functions have constant
-    complexity.
-
-    @iterators No changes.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @note This function does not return the length of a string stored as JSON
-    value - it returns the number of elements in the JSON value which is 1 in
-    the case of a string.
-
-    @requirement This function helps `json` satisfying the
-    [Container](http://en.cppreference.com/w/cpp/concept/Container)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `std::distance(begin(), end())`.
-
-    @sa @ref empty() -- checks whether the container is empty
-    @sa @ref max_size() -- returns the maximal number of elements
-
-    @since version 1.0.0
-    */
-    size_type size() const noexcept;
-
-    /*!
-    @brief returns the maximum possible number of elements
-
-    Returns the maximum number of elements a JSON value is able to hold due to
-    system or library implementation limitations, i.e. `std::distance(begin(),
-    end())` for the JSON value.
-
-    @return The return value depends on the different types and is
-            defined as follows:
-            Value type  | return value
-            ----------- | -------------
-            null        | `0` (same as `size()`)
-            boolean     | `1` (same as `size()`)
-            string      | `1` (same as `size()`)
-            number      | `1` (same as `size()`)
-            object      | result of function `object_t::max_size()`
-            array       | result of function `array_t::max_size()`
-
-    @liveexample{The following code calls `max_size()` on the different value
-    types. Note the output is implementation specific.,max_size}
-
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
-    the Container concept; that is, their `max_size()` functions have constant
-    complexity.
-
-    @iterators No changes.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @requirement This function helps `json` satisfying the
-    [Container](http://en.cppreference.com/w/cpp/concept/Container)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of returning `b.size()` where `b` is the largest
-      possible JSON value.
-
-    @sa @ref size() -- returns the number of elements
-
-    @since version 1.0.0
-    */
-    size_type max_size() const noexcept;
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // all other types have max_size() == size()
+                return size();
+            }
+        }
+    }
 
     /// @}
 
@@ -6000,119 +3031,140 @@
     /// @name modifiers
     /// @{
 
-    /*!
-    @brief clears the contents
+    /// @brief clears the contents
+    /// @sa https://json.nlohmann.me/api/basic_json/clear/
+    void clear() noexcept
+    {
+        switch (m_type)
+        {
+            case value_t::number_integer:
+            {
+                m_value.number_integer = 0;
+                break;
+            }
 
-    Clears the content of a JSON value and resets it to the default value as
-    if @ref json(value_t) would have been called with the current value
-    type from @ref type():
+            case value_t::number_unsigned:
+            {
+                m_value.number_unsigned = 0;
+                break;
+            }
 
-    Value type  | initial value
-    ----------- | -------------
-    null        | `null`
-    boolean     | `false`
-    string      | `""`
-    number      | `0`
-    object      | `{}`
-    array       | `[]`
+            case value_t::number_float:
+            {
+                m_value.number_float = 0.0;
+                break;
+            }
 
-    @post Has the same effect as calling
-    @code {.cpp}
-    *this = json(type());
-    @endcode
+            case value_t::boolean:
+            {
+                m_value.boolean = false;
+                break;
+            }
 
-    @liveexample{The example below shows the effect of `clear()` to different
-    JSON types.,clear}
+            case value_t::string:
+            {
+                m_value.string->clear();
+                break;
+            }
 
-    @complexity Linear in the size of the JSON value.
+            case value_t::binary:
+            {
+                m_value.binary->clear();
+                break;
+            }
 
-    @iterators All iterators, pointers and references related to this container
-               are invalidated.
+            case value_t::array:
+            {
+                m_value.array->clear();
+                break;
+            }
 
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
+            case value_t::object:
+            {
+                m_value.object->clear();
+                break;
+            }
 
-    @sa @ref json(value_t) -- constructor that creates an object with the
-        same value than calling `clear()`
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
 
-    @since version 1.0.0
-    */
-    void clear() noexcept;
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(basic_json&& val)
+    {
+        // push_back only works for null objects or arrays
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+        {
+            JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+        }
 
-    /*!
-    @brief add an object to an array
+        // transform null object into an array
+        if (is_null())
+        {
+            m_type = value_t::array;
+            m_value = value_t::array;
+            assert_invariant();
+        }
 
-    Appends the given element @a val to the end of the JSON value. If the
-    function is called on a JSON null value, an empty array is created before
-    appending @a val.
+        // add element to array (move semantics)
+        const auto old_capacity = m_value.array->capacity();
+        m_value.array->push_back(std::move(val));
+        set_parent(m_value.array->back(), old_capacity);
+        // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor
+    }
 
-    @param[in] val the value to add to the JSON array
-
-    @throw type_error.308 when called on a type other than JSON array or
-    null; example: `"cannot use push_back() with number"`
-
-    @complexity Amortized constant.
-
-    @liveexample{The example shows how `push_back()` and `+=` can be used to
-    add elements to a JSON array. Note how the `null` value was silently
-    converted to a JSON array.,push_back}
-
-    @since version 1.0.0
-    */
-    void push_back(json&& val);
-
-    /*!
-    @brief add an object to an array
-    @copydoc push_back(json&&)
-    */
-    reference operator+=(json&& val)
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(basic_json&& val)
     {
         push_back(std::move(val));
         return *this;
     }
 
-    /*!
-    @brief add an object to an array
-    @copydoc push_back(json&&)
-    */
-    void push_back(const json& val);
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(const basic_json& val)
+    {
+        // push_back only works for null objects or arrays
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+        {
+            JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+        }
 
-    /*!
-    @brief add an object to an array
-    @copydoc push_back(json&&)
-    */
-    reference operator+=(const json& val)
+        // 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
+        const auto old_capacity = m_value.array->capacity();
+        m_value.array->push_back(val);
+        set_parent(m_value.array->back(), old_capacity);
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(const basic_json& val)
     {
         push_back(val);
         return *this;
     }
 
-    /*!
-    @brief add an object to an object
-
-    Inserts the given element @a val to the JSON object. If the function is
-    called on a JSON null value, an empty object is created before inserting
-    @a val.
-
-    @param[in] val the value to add to the JSON object
-
-    @throw type_error.308 when called on a type other than JSON object or
-    null; example: `"cannot use push_back() with number"`
-
-    @complexity Logarithmic in the size of the container, O(log(`size()`)).
-
-    @liveexample{The example shows how `push_back()` and `+=` can be used to
-    add elements to a JSON object. Note how the `null` value was silently
-    converted to a JSON object.,push_back__object_t__value}
-
-    @since version 1.0.0
-    */
-    template<typename T, typename U>
-    void push_back(const std::pair<T, U>& val)
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(const typename object_t::value_type& val)
     {
         // push_back only works for null objects or objects
-        if (JSON_UNLIKELY(not(is_null() or is_object())))
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
         {
-            JSON_THROW(type_error::create(308, "cannot use push_back() with", type_name()));
+            JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
         }
 
         // transform null object into an object
@@ -6123,86 +3175,52 @@
             assert_invariant();
         }
 
-        // add element to array
-        m_value.object->try_emplace(val.first, std::move(val.second));
+        // add element to object
+        auto res = m_value.object->insert(val);
+        set_parent(res.first->second);
     }
 
-    /*!
-    @brief add an object to an object
-    @copydoc push_back(const typename object_t::value_type&)
-    */
-    template<typename T, typename U>
-    reference operator+=(const std::pair<T, U>& val)
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(const typename object_t::value_type& val)
     {
         push_back(val);
         return *this;
     }
 
-    /*!
-    @brief add an object to an object
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(initializer_list_t init)
+    {
+        if (is_object() && init.size() == 2 && (*init.begin())->is_string())
+        {
+            basic_json&& key = init.begin()->moved_or_copied();
+            push_back(typename object_t::value_type(
+                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));
+        }
+        else
+        {
+            push_back(basic_json(init));
+        }
+    }
 
-    This function allows to use `push_back` with an initializer list. In case
-
-    1. the current value is an object,
-    2. the initializer list @a init contains only two elements, and
-    3. the first element of @a init is a string,
-
-    @a init is converted into an object element and added using
-    @ref push_back(const typename object_t::value_type&). Otherwise, @a init
-    is converted to a JSON value and added using @ref push_back(json&&).
-
-    @param[in] init  an initializer list
-
-    @complexity Linear in the size of the initializer list @a init.
-
-    @note This function is required to resolve an ambiguous overload error,
-          because pairs like `{"key", "value"}` can be both interpreted as
-          `object_t::value_type` or `std::initializer_list<json>`, see
-          https://github.com/nlohmann/json/issues/235 for more information.
-
-    @liveexample{The example shows how initializer lists are treated as
-    objects when possible.,push_back__initializer_list}
-    */
-    void push_back(initializer_list_t init);
-
-    /*!
-    @brief add an object to an object
-    @copydoc push_back(initializer_list_t)
-    */
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
     reference operator+=(initializer_list_t init)
     {
         push_back(init);
         return *this;
     }
 
-    /*!
-    @brief add an object to an array
-
-    Creates a JSON value from the passed parameters @a args to the end of the
-    JSON value. If the function is called on a JSON null value, an empty array
-    is created before appending the value created from @a args.
-
-    @param[in] args arguments to forward to a constructor of @ref json
-    @tparam Args compatible types to create a @ref json object
-
-    @throw type_error.311 when called on a type other than JSON array or
-    null; example: `"cannot use emplace_back() with number"`
-
-    @complexity Amortized constant.
-
-    @liveexample{The example shows how `push_back()` can be used to add
-    elements to a JSON array. Note how the `null` value was silently converted
-    to a JSON array.,emplace_back}
-
-    @since version 2.0.8
-    */
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/
     template<class... Args>
-    void emplace_back(Args&& ... args)
+    reference emplace_back(Args&& ... args)
     {
         // emplace_back only works for null objects or arrays
-        if (JSON_UNLIKELY(not(is_null() or is_array())))
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
         {
-            JSON_THROW(type_error::create(311, "cannot use emplace_back() with", type_name()));
+            JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this));
         }
 
         // transform null object into an array
@@ -6214,43 +3232,20 @@
         }
 
         // add element to array (perfect forwarding)
+        const auto old_capacity = m_value.array->capacity();
         m_value.array->emplace_back(std::forward<Args>(args)...);
+        return set_parent(m_value.array->back(), old_capacity);
     }
 
-    /*!
-    @brief add an object to an object if key does not exist
-
-    Inserts a new element into a JSON object constructed in-place with the
-    given @a args if there is no element with the key in the container. If the
-    function is called on a JSON null value, an empty object is created before
-    appending the value created from @a args.
-
-    @param[in] args arguments to forward to a constructor of @ref json
-    @tparam Args compatible types to create a @ref json object
-
-    @return a pair consisting of an iterator to the inserted element, or the
-            already-existing element if no insertion happened, and a bool
-            denoting whether the insertion took place.
-
-    @throw type_error.311 when called on a type other than JSON object or
-    null; example: `"cannot use emplace() with number"`
-
-    @complexity Logarithmic in the size of the container, O(log(`size()`)).
-
-    @liveexample{The example shows how `emplace()` can be used to add elements
-    to a JSON object. Note how the `null` value was silently converted to a
-    JSON object. Further note how no value is added if there was already one
-    value stored with the same key.,emplace}
-
-    @since version 2.0.8
-    */
+    /// @brief add an object to an object if key does not exist
+    /// @sa https://json.nlohmann.me/api/basic_json/emplace/
     template<class... Args>
-    std::pair<iterator, bool> emplace(std::string_view key, Args&& ... args)
+    std::pair<iterator, bool> emplace(Args&& ... args)
     {
         // emplace only works for null objects or arrays
-        if (JSON_UNLIKELY(not(is_null() or is_object())))
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
         {
-            JSON_THROW(type_error::create(311, "cannot use emplace() with", type_name()));
+            JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this));
         }
 
         // transform null object into an object
@@ -6262,7 +3257,9 @@
         }
 
         // add element to array (perfect forwarding)
-        auto res = m_value.object->try_emplace(key, std::forward<Args>(args)...);
+        auto res = m_value.object->emplace(std::forward<Args>(args)...);
+        set_parent(res.first->second);
+
         // create result iterator and set iterator to the result of emplace
         auto it = begin();
         it.m_it.object_iterator = res.first;
@@ -6271,328 +3268,315 @@
         return {it, res.second};
     }
 
-    /*!
-    @brief inserts element
+    /// Helper for insertion of an iterator
+    /// @note: This uses std::distance to support GCC 4.8,
+    ///        see https://github.com/nlohmann/json/pull/1257
+    template<typename... Args>
+    iterator insert_iterator(const_iterator pos, Args&& ... args)
+    {
+        iterator result(this);
+        JSON_ASSERT(m_value.array != nullptr);
 
-    Inserts element @a val before iterator @a pos.
+        auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);
+        m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);
+        result.m_it.array_iterator = m_value.array->begin() + insert_pos;
 
-    @param[in] pos iterator before which the content will be inserted; may be
-    the end() iterator
-    @param[in] val element to insert
-    @return iterator pointing to the inserted @a val.
+        // This could have been written as:
+        // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
+        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.
 
-    @throw type_error.309 if called on JSON values other than arrays;
-    example: `"cannot use insert() with string"`
-    @throw invalid_iterator.202 if @a pos is not an iterator of *this;
-    example: `"iterator does not fit current value"`
+        set_parents();
+        return result;
+    }
 
-    @complexity Constant plus linear in the distance between @a pos and end of
-    the container.
+    /// @brief inserts element into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, const basic_json& val)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            // check if iterator pos fits to this JSON value
+            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+            {
+                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+            }
 
-    @liveexample{The example shows how `insert()` is used.,insert}
+            // insert to array and return iterator
+            return insert_iterator(pos, val);
+        }
 
-    @since version 1.0.0
-    */
-    iterator insert(const_iterator pos, const json& val);
+        JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+    }
 
-    /*!
-    @brief inserts element
-    @copydoc insert(const_iterator, const json&)
-    */
-    iterator insert(const_iterator pos, json&& val)
+    /// @brief inserts element into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, basic_json&& val)
     {
         return insert(pos, val);
     }
 
-    /*!
-    @brief inserts elements
+    /// @brief inserts copies of element into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            // check if iterator pos fits to this JSON value
+            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+            {
+                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+            }
 
-    Inserts @a cnt copies of @a val before iterator @a pos.
+            // insert to array and return iterator
+            return insert_iterator(pos, cnt, val);
+        }
 
-    @param[in] pos iterator before which the content will be inserted; may be
-    the end() iterator
-    @param[in] cnt number of copies of @a val to insert
-    @param[in] val element to insert
-    @return iterator pointing to the first element inserted, or @a pos if
-    `cnt==0`
+        JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+    }
 
-    @throw type_error.309 if called on JSON values other than arrays; example:
-    `"cannot use insert() with string"`
-    @throw invalid_iterator.202 if @a pos is not an iterator of *this;
-    example: `"iterator does not fit current value"`
+    /// @brief inserts range of elements into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, const_iterator first, const_iterator last)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_UNLIKELY(!is_array()))
+        {
+            JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+        }
 
-    @complexity Linear in @a cnt plus linear in the distance between @a pos
-    and end of the container.
+        // check if iterator pos fits to this JSON value
+        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+        }
 
-    @liveexample{The example shows how `insert()` is used.,insert__count}
+        // check if range iterators belong to the same JSON object
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+        }
 
-    @since version 1.0.0
-    */
-    iterator insert(const_iterator pos, size_type cnt, const json& val);
+        if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
+        {
+            JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this));
+        }
 
-    /*!
-    @brief inserts elements
+        // insert to array and return iterator
+        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);
+    }
 
-    Inserts elements from range `[first, last)` before iterator @a pos.
+    /// @brief inserts elements from initializer list into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, initializer_list_t ilist)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_UNLIKELY(!is_array()))
+        {
+            JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+        }
 
-    @param[in] pos iterator before which the content will be inserted; may be
-    the end() iterator
-    @param[in] first begin of the range of elements to insert
-    @param[in] last end of the range of elements to insert
+        // check if iterator pos fits to this JSON value
+        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+        }
 
-    @throw type_error.309 if called on JSON values other than arrays; example:
-    `"cannot use insert() with string"`
-    @throw invalid_iterator.202 if @a pos is not an iterator of *this;
-    example: `"iterator does not fit current value"`
-    @throw invalid_iterator.210 if @a first and @a last do not belong to the
-    same JSON value; example: `"iterators do not fit"`
-    @throw invalid_iterator.211 if @a first or @a last are iterators into
-    container for which insert is called; example: `"passed iterators may not
-    belong to container"`
+        // insert to array and return iterator
+        return insert_iterator(pos, ilist.begin(), ilist.end());
+    }
 
-    @return iterator pointing to the first element inserted, or @a pos if
-    `first==last`
+    /// @brief inserts range of elements into object
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    void insert(const_iterator first, const_iterator last)
+    {
+        // insert only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+        }
 
-    @complexity Linear in `std::distance(first, last)` plus linear in the
-    distance between @a pos and end of the container.
+        // check if range iterators belong to the same JSON object
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+        }
 
-    @liveexample{The example shows how `insert()` is used.,insert__range}
+        // passed iterators must belong to objects
+        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this));
+        }
 
-    @since version 1.0.0
-    */
-    iterator insert(const_iterator pos, const_iterator first, const_iterator last);
+        m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
+    }
 
-    /*!
-    @brief inserts elements
+    /// @brief updates a JSON object from another object, overwriting existing keys
+    /// @sa https://json.nlohmann.me/api/basic_json/update/
+    void update(const_reference j, bool merge_objects = false)
+    {
+        update(j.begin(), j.end(), merge_objects);
+    }
 
-    Inserts elements from initializer list @a ilist before iterator @a pos.
+    /// @brief updates a JSON object from another object, overwriting existing keys
+    /// @sa https://json.nlohmann.me/api/basic_json/update/
+    void update(const_iterator first, const_iterator last, bool merge_objects = false)
+    {
+        // implicitly convert null value to an empty object
+        if (is_null())
+        {
+            m_type = value_t::object;
+            m_value.object = create<object_t>();
+            assert_invariant();
+        }
 
-    @param[in] pos iterator before which the content will be inserted; may be
-    the end() iterator
-    @param[in] ilist initializer list to insert the values from
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this));
+        }
 
-    @throw type_error.309 if called on JSON values other than arrays; example:
-    `"cannot use insert() with string"`
-    @throw invalid_iterator.202 if @a pos is not an iterator of *this;
-    example: `"iterator does not fit current value"`
+        // check if range iterators belong to the same JSON object
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+        }
 
-    @return iterator pointing to the first element inserted, or @a pos if
-    `ilist` is empty
+        // passed iterators must belong to objects
+        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
+        {
+            JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
+        }
 
-    @complexity Linear in `ilist.size()` plus linear in the distance between
-    @a pos and end of the container.
+        for (auto it = first; it != last; ++it)
+        {
+            if (merge_objects && it.value().is_object())
+            {
+                auto it2 = m_value.object->find(it.key());
+                if (it2 != m_value.object->end())
+                {
+                    it2->second.update(it.value(), true);
+                    continue;
+                }
+            }
+            m_value.object->operator[](it.key()) = it.value();
+#if JSON_DIAGNOSTICS
+            m_value.object->operator[](it.key()).m_parent = this;
+#endif
+        }
+    }
 
-    @liveexample{The example shows how `insert()` is used.,insert__ilist}
-
-    @since version 1.0.0
-    */
-    iterator insert(const_iterator pos, initializer_list_t ilist);
-
-    /*!
-    @brief inserts elements
-
-    Inserts elements from range `[first, last)`.
-
-    @param[in] first begin of the range of elements to insert
-    @param[in] last end of the range of elements to insert
-
-    @throw type_error.309 if called on JSON values other than objects; example:
-    `"cannot use insert() with string"`
-    @throw invalid_iterator.202 if iterator @a first or @a last does does not
-    point to an object; example: `"iterators first and last must point to
-    objects"`
-    @throw invalid_iterator.210 if @a first and @a last do not belong to the
-    same JSON value; example: `"iterators do not fit"`
-
-    @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number
-    of elements to insert.
-
-    @liveexample{The example shows how `insert()` is used.,insert__range_object}
-
-    @since version 3.0.0
-    */
-    void insert(const_iterator first, const_iterator last);
-
-    /*!
-    @brief updates a JSON object from another object, overwriting existing keys
-
-    Inserts all values from JSON object @a j and overwrites existing keys.
-
-    @param[in] j  JSON object to read values from
-
-    @throw type_error.312 if called on JSON values other than objects; example:
-    `"cannot use update() with string"`
-
-    @complexity O(N*log(size() + N)), where N is the number of elements to
-                insert.
-
-    @liveexample{The example shows how `update()` is used.,update}
-
-    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
-
-    @since version 3.0.0
-    */
-    void update(const_reference j);
-
-    /*!
-    @brief updates a JSON object from another object, overwriting existing keys
-
-    Inserts all values from from range `[first, last)` and overwrites existing
-    keys.
-
-    @param[in] first begin of the range of elements to insert
-    @param[in] last end of the range of elements to insert
-
-    @throw type_error.312 if called on JSON values other than objects; example:
-    `"cannot use update() with string"`
-    @throw invalid_iterator.202 if iterator @a first or @a last does does not
-    point to an object; example: `"iterators first and last must point to
-    objects"`
-    @throw invalid_iterator.210 if @a first and @a last do not belong to the
-    same JSON value; example: `"iterators do not fit"`
-
-    @complexity O(N*log(size() + N)), where N is the number of elements to
-                insert.
-
-    @liveexample{The example shows how `update()` is used__range.,update}
-
-    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
-
-    @since version 3.0.0
-    */
-    void update(const_iterator first, const_iterator last);
-
-    /*!
-    @brief exchanges the values
-
-    Exchanges the contents of the JSON value with those of @a other. Does not
-    invoke any move, copy, or swap operations on individual elements. All
-    iterators and references remain valid. The past-the-end iterator is
-    invalidated.
-
-    @param[in,out] other JSON value to exchange the contents with
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how JSON values can be swapped with
-    `swap()`.,swap__reference}
-
-    @since version 1.0.0
-    */
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
     void swap(reference other) noexcept (
-        std::is_nothrow_move_constructible<value_t>::value and
-        std::is_nothrow_move_assignable<value_t>::value and
-        std::is_nothrow_move_constructible<json_value>::value and
+        std::is_nothrow_move_constructible<value_t>::value&&
+        std::is_nothrow_move_assignable<value_t>::value&&
+        std::is_nothrow_move_constructible<json_value>::value&&
         std::is_nothrow_move_assignable<json_value>::value
     )
     {
         std::swap(m_type, other.m_type);
         std::swap(m_value, other.m_value);
+
+        set_parents();
+        other.set_parents();
         assert_invariant();
     }
 
-    /*!
-    @brief exchanges the values
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    friend void swap(reference left, reference right) noexcept (
+        std::is_nothrow_move_constructible<value_t>::value&&
+        std::is_nothrow_move_assignable<value_t>::value&&
+        std::is_nothrow_move_constructible<json_value>::value&&
+        std::is_nothrow_move_assignable<json_value>::value
+    )
+    {
+        left.swap(right);
+    }
 
-    Exchanges the contents of a JSON array with those of @a other. Does not
-    invoke any move, copy, or swap operations on individual elements. All
-    iterators and references remain valid. The past-the-end iterator is
-    invalidated.
-
-    @param[in,out] other array to exchange the contents with
-
-    @throw type_error.310 when JSON value is not an array; example: `"cannot
-    use swap() with string"`
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how arrays can be swapped with
-    `swap()`.,swap__std_vector_json}
-
-    @since version 1.0.0
-    */
-    void swap(array_t& other)
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(array_t& other) // NOLINT(bugprone-exception-escape)
     {
         // swap only works for arrays
-        if (JSON_LIKELY(is_array()))
+        if (JSON_HEDLEY_LIKELY(is_array()))
         {
-            std::swap(*(m_value.array), other);
+            using std::swap;
+            swap(*(m_value.array), other);
         }
         else
         {
-            JSON_THROW(type_error::create(310, "cannot use swap() with", type_name()));
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(array_t&) with ", type_name()), this));
         }
     }
 
-    /*!
-    @brief exchanges the values
-
-    Exchanges the contents of a JSON object with those of @a other. Does not
-    invoke any move, copy, or swap operations on individual elements. All
-    iterators and references remain valid. The past-the-end iterator is
-    invalidated.
-
-    @param[in,out] other object to exchange the contents with
-
-    @throw type_error.310 when JSON value is not an object; example:
-    `"cannot use swap() with string"`
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how objects can be swapped with
-    `swap()`.,swap__object_t}
-
-    @since version 1.0.0
-    */
-    void swap(object_t& other)
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(object_t& other) // NOLINT(bugprone-exception-escape)
     {
         // swap only works for objects
-        if (JSON_LIKELY(is_object()))
+        if (JSON_HEDLEY_LIKELY(is_object()))
         {
-            std::swap(*(m_value.object), other);
+            using std::swap;
+            swap(*(m_value.object), other);
         }
         else
         {
-            JSON_THROW(type_error::create(310, "cannot use swap() with", type_name()));
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(object_t&) with ", type_name()), this));
         }
     }
 
-    /*!
-    @brief exchanges the values
-
-    Exchanges the contents of a JSON string with those of @a other. Does not
-    invoke any move, copy, or swap operations on individual elements. All
-    iterators and references remain valid. The past-the-end iterator is
-    invalidated.
-
-    @param[in,out] other string to exchange the contents with
-
-    @throw type_error.310 when JSON value is not a string; example: `"cannot
-    use swap() with boolean"`
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how strings can be swapped with
-    `swap()`.,swap__std_string}
-
-    @since version 1.0.0
-    */
-    void swap(std::string& other)
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(string_t& other) // NOLINT(bugprone-exception-escape)
     {
         // swap only works for strings
-        if (JSON_LIKELY(is_string()))
+        if (JSON_HEDLEY_LIKELY(is_string()))
         {
-            std::swap(*(m_value.string), other);
+            using std::swap;
+            swap(*(m_value.string), other);
         }
         else
         {
-            JSON_THROW(type_error::create(310, "cannot use swap() with", type_name()));
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(string_t&) with ", type_name()), this));
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(binary_t& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for strings
+        if (JSON_HEDLEY_LIKELY(is_binary()))
+        {
+            using std::swap;
+            swap(*(m_value.binary), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t&) with ", type_name()), this));
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for strings
+        if (JSON_HEDLEY_LIKELY(is_binary()))
+        {
+            using std::swap;
+            swap(*(m_value.binary), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t::container_type&) with ", type_name()), this));
         }
     }
 
     /// @}
 
-  public:
     //////////////////////////////////////////
     // lexicographical comparison operators //
     //////////////////////////////////////////
@@ -6600,301 +3584,391 @@
     /// @name lexicographical comparison operators
     /// @{
 
-    /*!
-    @brief comparison: equal
+    // note parentheses around operands are necessary; see
+    // https://github.com/nlohmann/json/issues/1530
+#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result)                       \
+    const auto lhs_type = lhs.type();                                                                    \
+    const auto rhs_type = rhs.type();                                                                    \
+    \
+    if (lhs_type == rhs_type) /* NOLINT(readability/braces) */                                           \
+    {                                                                                                    \
+        switch (lhs_type)                                                                                \
+        {                                                                                                \
+            case value_t::array:                                                                         \
+                return (*lhs.m_value.array) op (*rhs.m_value.array);                                     \
+                \
+            case value_t::object:                                                                        \
+                return (*lhs.m_value.object) op (*rhs.m_value.object);                                   \
+                \
+            case value_t::null:                                                                          \
+                return (null_result);                                                                    \
+                \
+            case value_t::string:                                                                        \
+                return (*lhs.m_value.string) op (*rhs.m_value.string);                                   \
+                \
+            case value_t::boolean:                                                                       \
+                return (lhs.m_value.boolean) op (rhs.m_value.boolean);                                   \
+                \
+            case value_t::number_integer:                                                                \
+                return (lhs.m_value.number_integer) op (rhs.m_value.number_integer);                     \
+                \
+            case value_t::number_unsigned:                                                               \
+                return (lhs.m_value.number_unsigned) op (rhs.m_value.number_unsigned);                   \
+                \
+            case value_t::number_float:                                                                  \
+                return (lhs.m_value.number_float) op (rhs.m_value.number_float);                         \
+                \
+            case value_t::binary:                                                                        \
+                return (*lhs.m_value.binary) op (*rhs.m_value.binary);                                   \
+                \
+            case value_t::discarded:                                                                     \
+            default:                                                                                     \
+                return (unordered_result);                                                               \
+        }                                                                                                \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)                   \
+    {                                                                                                    \
+        return static_cast<number_float_t>(lhs.m_value.number_integer) op rhs.m_value.number_float;      \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)                   \
+    {                                                                                                    \
+        return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_integer);      \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)                  \
+    {                                                                                                    \
+        return static_cast<number_float_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_float;     \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)                  \
+    {                                                                                                    \
+        return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_unsigned);     \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)                \
+    {                                                                                                    \
+        return static_cast<number_integer_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_integer; \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)                \
+    {                                                                                                    \
+        return lhs.m_value.number_integer op static_cast<number_integer_t>(rhs.m_value.number_unsigned); \
+    }                                                                                                    \
+    else if(compares_unordered(lhs, rhs))\
+    {\
+        return (unordered_result);\
+    }\
+    \
+    return (default_result);
 
-    Compares two JSON values for equality according to the following rules:
-    - Two JSON values are equal if (1) they are from the same type and (2)
-      their stored values are the same according to their respective
-      `operator==`.
-    - Integer and floating-point numbers are automatically converted before
-      comparison. Note than two NaN values are always treated as unequal.
-    - Two JSON null values are equal.
-
-    @note Floating-point inside JSON values numbers are compared with
-    `double::operator==`.
-    To compare floating-point while respecting an epsilon, an alternative
-    [comparison function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39)
-    could be used, for instance
-    @code {.cpp}
-    template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>
-    inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept
+  JSON_PRIVATE_UNLESS_TESTED:
+    // returns true if:
+    // - any operand is NaN and the other operand is of number type
+    // - any operand is discarded
+    // in legacy mode, discarded values are considered ordered if
+    // an operation is computed as an odd number of inverses of others
+    static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept
     {
-        return std::abs(a - b) <= epsilon;
+        if ((lhs.is_number_float() && std::isnan(lhs.m_value.number_float) && rhs.is_number())
+                || (rhs.is_number_float() && std::isnan(rhs.m_value.number_float) && lhs.is_number()))
+        {
+            return true;
+        }
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+        return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;
+#else
+        static_cast<void>(inverse);
+        return lhs.is_discarded() || rhs.is_discarded();
+#endif
     }
-    @endcode
 
-    @note NaN values never compare equal to themselves or to other NaN values.
+  private:
+    bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept
+    {
+        return compares_unordered(*this, rhs, inverse);
+    }
 
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether the values @a lhs and @a rhs are equal
+  public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    bool operator==(const_reference rhs) const noexcept
+    {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+        const_reference lhs = *this;
+        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+    }
 
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator==(ScalarType rhs) const noexcept
+    {
+        return *this == basic_json(rhs);
+    }
 
-    @complexity Linear.
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+    bool operator!=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !operator==(rhs);
+    }
 
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__equal}
+    /// @brief comparison: 3-way
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+    std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
+    {
+        const_reference lhs = *this;
+        // default_result is used if we cannot compare values. In that case,
+        // we compare types.
+        JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
+                                std::partial_ordering::equivalent,
+                                std::partial_ordering::unordered,
+                                lhs_type <=> rhs_type) // *NOPAD*
+    }
 
-    @since version 1.0.0
-    */
-    friend bool operator==(const_reference lhs, const_reference rhs) noexcept;
+    /// @brief comparison: 3-way
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*
+    {
+        return *this <=> basic_json(rhs); // *NOPAD*
+    }
 
-    /*!
-    @brief comparison: equal
-    @copydoc operator==(const_reference, const_reference)
-    */
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    // all operators that are computed as an odd number of inverses of others
+    // need to be overloaded to emulate the legacy comparison behavior
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+    bool operator<=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !(rhs < *this);
+    }
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator<=(ScalarType rhs) const noexcept
+    {
+        return *this <= basic_json(rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+    bool operator>=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !(*this < rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator>=(ScalarType rhs) const noexcept
+    {
+        return *this >= basic_json(rhs);
+    }
+#endif
+#else
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    friend bool operator==(const_reference lhs, const_reference rhs) noexcept
+    {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+    }
+
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept
+    friend bool operator==(const_reference lhs, ScalarType rhs) noexcept
     {
-        return (lhs == json(rhs));
+        return lhs == basic_json(rhs);
     }
 
-    /*!
-    @brief comparison: equal
-    @copydoc operator==(const_reference, const_reference)
-    */
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept
+    friend bool operator==(ScalarType lhs, const_reference rhs) noexcept
     {
-        return (json(lhs) == rhs);
+        return basic_json(lhs) == rhs;
     }
 
-    /*!
-    @brief comparison: not equal
-
-    Compares two JSON values for inequality by calculating `not (lhs == rhs)`.
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether the values @a lhs and @a rhs are not equal
-
-    @complexity Linear.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__notequal}
-
-    @since version 1.0.0
-    */
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
     friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
     {
-        return not (lhs == rhs);
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
+        return !(lhs == rhs);
     }
 
-    /*!
-    @brief comparison: not equal
-    @copydoc operator!=(const_reference, const_reference)
-    */
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept
+    friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept
     {
-        return (lhs != json(rhs));
+        return lhs != basic_json(rhs);
     }
 
-    /*!
-    @brief comparison: not equal
-    @copydoc operator!=(const_reference, const_reference)
-    */
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept
+    friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept
     {
-        return (json(lhs) != rhs);
+        return basic_json(lhs) != rhs;
     }
 
-    /*!
-    @brief comparison: less than
+    /// @brief comparison: less than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+    friend bool operator<(const_reference lhs, const_reference rhs) noexcept
+    {
+        // default_result is used if we cannot compare values. In that case,
+        // we compare types. Note we have to call the operator explicitly,
+        // because MSVC has problems otherwise.
+        JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
+    }
 
-    Compares whether one JSON value @a lhs is less than another JSON value @a
-    rhs according to the following rules:
-    - If @a lhs and @a rhs have the same type, the values are compared using
-      the default `<` operator.
-    - Integer and floating-point numbers are automatically converted before
-      comparison
-    - In case @a lhs and @a rhs have different types, the values are ignored
-      and the order of the types is considered, see
-      @ref operator<(const value_t, const value_t).
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether @a lhs is less than @a rhs
-
-    @complexity Linear.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__less}
-
-    @since version 1.0.0
-    */
-    friend bool operator<(const_reference lhs, const_reference rhs) noexcept;
-
-    /*!
-    @brief comparison: less than
-    @copydoc operator<(const_reference, const_reference)
-    */
+    /// @brief comparison: less than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept
+    friend bool operator<(const_reference lhs, ScalarType rhs) noexcept
     {
-        return (lhs < json(rhs));
+        return lhs < basic_json(rhs);
     }
 
-    /*!
-    @brief comparison: less than
-    @copydoc operator<(const_reference, const_reference)
-    */
+    /// @brief comparison: less than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept
+    friend bool operator<(ScalarType lhs, const_reference rhs) noexcept
     {
-        return (json(lhs) < rhs);
+        return basic_json(lhs) < rhs;
     }
 
-    /*!
-    @brief comparison: less than or equal
-
-    Compares whether one JSON value @a lhs is less than or equal to another
-    JSON value by calculating `not (rhs < lhs)`.
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether @a lhs is less than or equal to @a rhs
-
-    @complexity Linear.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__greater}
-
-    @since version 1.0.0
-    */
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
     friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
     {
-        return not (rhs < lhs);
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
+        return !(rhs < lhs);
     }
 
-    /*!
-    @brief comparison: less than or equal
-    @copydoc operator<=(const_reference, const_reference)
-    */
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept
+    friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept
     {
-        return (lhs <= json(rhs));
+        return lhs <= basic_json(rhs);
     }
 
-    /*!
-    @brief comparison: less than or equal
-    @copydoc operator<=(const_reference, const_reference)
-    */
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept
+    friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
     {
-        return (json(lhs) <= rhs);
+        return basic_json(lhs) <= rhs;
     }
 
-    /*!
-    @brief comparison: greater than
-
-    Compares whether one JSON value @a lhs is greater than another
-    JSON value by calculating `not (lhs <= rhs)`.
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether @a lhs is greater than to @a rhs
-
-    @complexity Linear.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__lessequal}
-
-    @since version 1.0.0
-    */
+    /// @brief comparison: greater than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
     friend bool operator>(const_reference lhs, const_reference rhs) noexcept
     {
-        return not (lhs <= rhs);
+        // double inverse
+        if (compares_unordered(lhs, rhs))
+        {
+            return false;
+        }
+        return !(lhs <= rhs);
     }
 
-    /*!
-    @brief comparison: greater than
-    @copydoc operator>(const_reference, const_reference)
-    */
+    /// @brief comparison: greater than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept
+    friend bool operator>(const_reference lhs, ScalarType rhs) noexcept
     {
-        return (lhs > json(rhs));
+        return lhs > basic_json(rhs);
     }
 
-    /*!
-    @brief comparison: greater than
-    @copydoc operator>(const_reference, const_reference)
-    */
+    /// @brief comparison: greater than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept
+    friend bool operator>(ScalarType lhs, const_reference rhs) noexcept
     {
-        return (json(lhs) > rhs);
+        return basic_json(lhs) > rhs;
     }
 
-    /*!
-    @brief comparison: greater than or equal
-
-    Compares whether one JSON value @a lhs is greater than or equal to another
-    JSON value by calculating `not (lhs < rhs)`.
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether @a lhs is greater than or equal to @a rhs
-
-    @complexity Linear.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__greaterequal}
-
-    @since version 1.0.0
-    */
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
     friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
     {
-        return not (lhs < rhs);
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
+        return !(lhs < rhs);
     }
 
-    /*!
-    @brief comparison: greater than or equal
-    @copydoc operator>=(const_reference, const_reference)
-    */
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept
+    friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept
     {
-        return (lhs >= json(rhs));
+        return lhs >= basic_json(rhs);
     }
 
-    /*!
-    @brief comparison: greater than or equal
-    @copydoc operator>=(const_reference, const_reference)
-    */
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
     template<typename ScalarType, typename std::enable_if<
                  std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept
+    friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
     {
-        return (json(lhs) >= rhs);
+        return basic_json(lhs) >= rhs;
     }
+#endif
+
+#undef JSON_IMPLEMENT_OPERATOR
 
     /// @}
 
@@ -6904,40 +3978,42 @@
 
     /// @name serialization
     /// @{
+#ifndef JSON_NO_IO
+    /// @brief serialize to stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
+    {
+        // read width member and use it as indentation parameter if nonzero
+        const bool pretty_print = o.width() > 0;
+        const auto indentation = pretty_print ? o.width() : 0;
 
-    /*!
-    @brief serialize to stream
+        // reset width to 0 for subsequent calls to this stream
+        o.width(0);
 
-    Serialize the given JSON value @a j to the output stream @a o. The JSON
-    value will be serialized using the @ref dump member function.
+        // do the actual serialization
+        serializer s(detail::output_adapter<char>(o), o.fill());
+        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));
+        return o;
+    }
 
-    - The indentation of the output can be controlled with the member variable
-      `width` of the output stream @a o. For instance, using the manipulator
-      `std::setw(4)` on @a o sets the indentation level to `4` and the
-      serialization result is the same as calling `dump(4)`.
-
-    - The indentation character can be controlled with the member variable
-      `fill` of the output stream @a o. For instance, the manipulator
-      `std::setfill('\\t')` sets indentation to use a tab character rather than
-      the default space character.
-
-    @param[in,out] o  stream to serialize to
-    @param[in] j  JSON value to serialize
-
-    @return the stream @a o
-
-    @throw type_error.316 if a string stored inside the JSON value is not
-                          UTF-8 encoded
-
-    @complexity Linear.
-
-    @liveexample{The example below shows the serialization with different
-    parameters to `width` to adjust the indentation level.,operator_serialize}
-
-    @since version 1.0.0; indentation character added in version 3.0.0
-    */
-    friend raw_ostream& operator<<(raw_ostream& o, const json& j);
-
+    /// @brief serialize to stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+    /// @deprecated This function is deprecated since 3.0.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use
+    ///             operator<<(std::ostream&, const basic_json&) instead; that is,
+    ///             replace calls like `j >> o;` with `o << j;`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))
+    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
+    {
+        return o << j;
+    }
+#endif  // JSON_NO_IO
+    
+    friend raw_ostream& operator<<(raw_ostream& o, const basic_json& j)
+    {
+        j.dump(o, 0);
+        return o;
+    }
     /// @}
 
 
@@ -6948,156 +4024,181 @@
     /// @name deserialization
     /// @{
 
-    /*!
-    @brief deserialize from a compatible input
-
-    This function reads from a compatible input. Examples are:
-    - an array of 1-byte values
-    - strings with character/literal type with size of 1 byte
-    - input streams
-    - container with contiguous storage of 1-byte values. Compatible container
-      types include `std::vector`, `std::string`, `std::array`,
-      and `std::initializer_list`. Furthermore, C-style
-      arrays can be used with `std::begin()`/`std::end()`. User-defined
-      containers can be used as long as they implement random-access iterators
-      and a contiguous storage.
-
-    @pre Each element of the container has a size of 1 byte. Violating this
-    precondition yields undefined behavior. **This precondition is enforced
-    with a static assertion.**
-
-    @pre The container storage is contiguous. Violating this precondition
-    yields undefined behavior. **This precondition is enforced with an
-    assertion.**
-    @pre Each element of the container has a size of 1 byte. Violating this
-    precondition yields undefined behavior. **This precondition is enforced
-    with a static assertion.**
-
-    @warning There is no way to enforce all preconditions at compile-time. If
-             the function is called with a noncompliant container and with
-             assertions switched off, the behavior is undefined and will most
-             likely yield segmentation violation.
-
-    @param[in] i  input to read from
-    @param[in] cb  a parser callback function of type @ref parser_callback_t
-    which is used to control the deserialization by filtering unwanted values
-    (optional)
-
-    @return result of the deserialization
-
-    @throw parse_error.101 if a parse error occurs; example: `""unexpected end
-    of input; expected string literal""`
-    @throw parse_error.102 if to_unicode fails or surrogate error
-    @throw parse_error.103 if to_unicode fails
-
-    @complexity Linear in the length of the input. The parser is a predictive
-    LL(1) parser. The complexity can be higher if the parser callback function
-    @a cb has a super-linear complexity.
-
-    @note A UTF-8 byte order mark is silently ignored.
-
-    @liveexample{The example below demonstrates the `parse()` function reading
-    from an array.,parse__array__parser_callback_t}
-
-    @liveexample{The example below demonstrates the `parse()` function with
-    and without callback function.,parse__string__parser_callback_t}
-
-    @liveexample{The example below demonstrates the `parse()` function with
-    and without callback function.,parse__istream__parser_callback_t}
-
-    @liveexample{The example below demonstrates the `parse()` function reading
-    from a contiguous container.,parse__contiguouscontainer__parser_callback_t}
-
-    @since version 2.0.3 (contiguous containers)
-    */
-    static json parse(std::string_view s,
+    /// @brief deserialize from a compatible input
+    /// @sa https://json.nlohmann.me/api/basic_json/parse/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json parse(InputType&& i,
                             const parser_callback_t cb = nullptr,
-                            const bool allow_exceptions = true);
+                            const bool allow_exceptions = true,
+                            const bool ignore_comments = false)
+    {
+        basic_json result;
+        parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);
+        return result;
+    }
 
-    static json parse(std::span<const uint8_t> arr,
+    /// @brief deserialize from a pair of character iterators
+    /// @sa https://json.nlohmann.me/api/basic_json/parse/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json parse(IteratorType first,
+                            IteratorType last,
                             const parser_callback_t cb = nullptr,
-                            const bool allow_exceptions = true);
+                            const bool allow_exceptions = true,
+                            const bool ignore_comments = false)
+    {
+        basic_json result;
+        parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result);
+        return result;
+    }
 
-    /*!
-    @copydoc json parse(raw_istream&, const parser_callback_t)
-    */
-    static json parse(raw_istream& i,
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
+    static basic_json parse(detail::span_input_adapter&& i,
                             const parser_callback_t cb = nullptr,
-                            const bool allow_exceptions = true);
+                            const bool allow_exceptions = true,
+                            const bool ignore_comments = false)
+    {
+        basic_json result;
+        parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result);
+        return result;
+    }
 
-    static bool accept(std::string_view s);
+    /// @brief check if the input is valid JSON
+    /// @sa https://json.nlohmann.me/api/basic_json/accept/
+    template<typename InputType>
+    static bool accept(InputType&& i,
+                       const bool ignore_comments = false)
+    {
+        return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);
+    }
 
-    static bool accept(std::span<const uint8_t> arr);
+    /// @brief check if the input is valid JSON
+    /// @sa https://json.nlohmann.me/api/basic_json/accept/
+    template<typename IteratorType>
+    static bool accept(IteratorType first, IteratorType last,
+                       const bool ignore_comments = false)
+    {
+        return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);
+    }
 
-    static bool accept(raw_istream& i);
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
+    static bool accept(detail::span_input_adapter&& i,
+                       const bool ignore_comments = false)
+    {
+        return parser(i.get(), nullptr, false, ignore_comments).accept(true);
+    }
 
-    /*!
-    @brief deserialize from stream
+    /// @brief generate SAX events
+    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+    template <typename InputType, typename SAX>
+    JSON_HEDLEY_NON_NULL(2)
+    static bool sax_parse(InputType&& i, SAX* sax,
+                          input_format_t format = input_format_t::json,
+                          const bool strict = true,
+                          const bool ignore_comments = false)
+    {
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        return format == input_format_t::json
+               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+    }
 
-    Deserializes an input stream to a JSON value.
+    /// @brief generate SAX events
+    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+    template<class IteratorType, class SAX>
+    JSON_HEDLEY_NON_NULL(3)
+    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
+                          input_format_t format = input_format_t::json,
+                          const bool strict = true,
+                          const bool ignore_comments = false)
+    {
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        return format == input_format_t::json
+               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+    }
 
-    @param[in,out] i  input stream to read a serialized JSON value from
-    @param[in,out] j  JSON value to write the deserialized input to
+    /// @brief generate SAX events
+    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+    /// @deprecated This function is deprecated since 3.8.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use
+    ///             sax_parse(ptr, ptr + len) instead.
+    template <typename SAX>
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))
+    JSON_HEDLEY_NON_NULL(2)
+    static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,
+                          input_format_t format = input_format_t::json,
+                          const bool strict = true,
+                          const bool ignore_comments = false)
+    {
+        auto ia = i.get();
+        return format == input_format_t::json
+               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+    }
+#ifndef JSON_NO_IO
+    /// @brief deserialize from stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
+    /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use
+    ///             operator>>(std::istream&, basic_json&) instead; that is,
+    ///             replace calls like `j << i;` with `i >> j;`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))
+    friend std::istream& operator<<(basic_json& j, std::istream& i)
+    {
+        return operator>>(i, j);
+    }
 
-    @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
-
-    @complexity Linear in the length of the input. The parser is a predictive
-    LL(1) parser.
-
-    @note A UTF-8 byte order mark is silently ignored.
-
-    @liveexample{The example below shows how a JSON value is constructed by
-    reading a serialization from a stream.,operator_deserialize}
-
-    @sa parse(std::istream&, const parser_callback_t) for a variant with a
-    parser callback function to filter values while parsing
-
-    @since version 1.0.0
-    */
-    friend raw_istream& operator>>(raw_istream& i, json& j);
-
+    /// @brief deserialize from stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
+    friend std::istream& operator>>(std::istream& i, basic_json& j)
+    {
+        parser(detail::input_adapter(i)).parse(false, j);
+        return i;
+    }
+#endif  // JSON_NO_IO
     /// @}
 
     ///////////////////////////
     // convenience functions //
     ///////////////////////////
 
-    /*!
-    @brief return the type as string
-
-    Returns the type name as string to be used in error messages - usually to
-    indicate that a function was called on a wrong JSON type.
-
-    @return a string representation of a the @a m_type member:
-            Value type  | return value
-            ----------- | -------------
-            null        | `"null"`
-            boolean     | `"boolean"`
-            string      | `"string"`
-            number      | `"number"` (for all number types)
-            object      | `"object"`
-            array       | `"array"`
-            discarded   | `"discarded"`
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @complexity Constant.
-
-    @liveexample{The following code exemplifies `type_name()` for all JSON
-    types.,type_name}
-
-    @sa @ref type() -- return the type of the JSON value
-    @sa @ref operator value_t() -- return the type of the JSON value (implicit)
-
-    @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`
-    since 3.0.0
-    */
-    const char* type_name() const noexcept;
+    /// @brief return the type as string
+    /// @sa https://json.nlohmann.me/api/basic_json/type_name/
+    JSON_HEDLEY_RETURNS_NON_NULL
+    const char* 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::binary:
+                return "binary";
+            case value_t::discarded:
+                return "discarded";
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            default:
+                return "number";
+        }
+    }
 
 
-  private:
+  JSON_PRIVATE_UNLESS_TESTED:
     //////////////////////
     // member variables //
     //////////////////////
@@ -7108,6 +4209,11 @@
     /// the value of the current element
     json_value m_value = {};
 
+#if JSON_DIAGNOSTICS
+    /// a pointer to a parent value (for debugging purposes)
+    basic_json* m_parent = nullptr;
+#endif
+
     //////////////////////////////////////////
     // binary serialization/deserialization //
     //////////////////////////////////////////
@@ -7116,514 +4222,380 @@
     /// @{
 
   public:
-    /*!
-    @brief create a CBOR serialization of a given JSON value
+    /// @brief create a CBOR serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+    static std::vector<std::uint8_t> to_cbor(const basic_json& j)
+    {
+        std::vector<std::uint8_t> result;
+        to_cbor(j, result);
+        return result;
+    }
 
-    Serializes a given JSON value @a j to a byte vector using the CBOR (Concise
-    Binary Object Representation) serialization format. CBOR is a binary
-    serialization format which aims to be more compact than JSON itself, yet
-    more efficient to parse.
+    /// @brief create a CBOR serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+    static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+    {
+        binary_writer<std::uint8_t>(o).write_cbor(j);
+    }
 
-    The library uses the following mapping from JSON values types to
-    CBOR types according to the CBOR specification (RFC 7049):
+    /// @brief create a CBOR serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)
+    {
+        binary_writer<char>(o).write_cbor(j);
+    }
 
-    JSON value type | value/range                                | CBOR type                          | first byte
-    --------------- | ------------------------------------------ | ---------------------------------- | ---------------
-    null            | `null`                                     | Null                               | 0xF6
-    boolean         | `true`                                     | True                               | 0xF5
-    boolean         | `false`                                    | False                              | 0xF4
-    number_integer  | -9223372036854775808..-2147483649          | Negative integer (8 bytes follow)  | 0x3B
-    number_integer  | -2147483648..-32769                        | Negative integer (4 bytes follow)  | 0x3A
-    number_integer  | -32768..-129                               | Negative integer (2 bytes follow)  | 0x39
-    number_integer  | -128..-25                                  | Negative integer (1 byte follow)   | 0x38
-    number_integer  | -24..-1                                    | Negative integer                   | 0x20..0x37
-    number_integer  | 0..23                                      | Integer                            | 0x00..0x17
-    number_integer  | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18
-    number_integer  | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19
-    number_integer  | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A
-    number_integer  | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B
-    number_unsigned | 0..23                                      | Integer                            | 0x00..0x17
-    number_unsigned | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18
-    number_unsigned | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19
-    number_unsigned | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A
-    number_unsigned | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B
-    number_float    | *any value*                                | Double-Precision Float             | 0xFB
-    string          | *length*: 0..23                            | UTF-8 string                       | 0x60..0x77
-    string          | *length*: 23..255                          | UTF-8 string (1 byte follow)       | 0x78
-    string          | *length*: 256..65535                       | UTF-8 string (2 bytes follow)      | 0x79
-    string          | *length*: 65536..4294967295                | UTF-8 string (4 bytes follow)      | 0x7A
-    string          | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow)      | 0x7B
-    array           | *size*: 0..23                              | array                              | 0x80..0x97
-    array           | *size*: 23..255                            | array (1 byte follow)              | 0x98
-    array           | *size*: 256..65535                         | array (2 bytes follow)             | 0x99
-    array           | *size*: 65536..4294967295                  | array (4 bytes follow)             | 0x9A
-    array           | *size*: 4294967296..18446744073709551615   | array (8 bytes follow)             | 0x9B
-    object          | *size*: 0..23                              | map                                | 0xA0..0xB7
-    object          | *size*: 23..255                            | map (1 byte follow)                | 0xB8
-    object          | *size*: 256..65535                         | map (2 bytes follow)               | 0xB9
-    object          | *size*: 65536..4294967295                  | map (4 bytes follow)               | 0xBA
-    object          | *size*: 4294967296..18446744073709551615   | map (8 bytes follow)               | 0xBB
+    /// @brief create a MessagePack serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+    static std::vector<std::uint8_t> to_msgpack(const basic_json& j)
+    {
+        std::vector<std::uint8_t> result;
+        to_msgpack(j, result);
+        return result;
+    }
 
-    @note The mapping is **complete** in the sense that any JSON value type
-          can be converted to a CBOR value.
+    /// @brief create a MessagePack serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+    static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+    {
+        binary_writer<std::uint8_t>(o).write_msgpack(j);
+    }
 
-    @note If NaN or Infinity are stored inside a JSON number, they are
-          serialized properly. This behavior differs from the @ref dump()
-          function which serializes NaN or Infinity to `null`.
+    /// @brief create a MessagePack serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)
+    {
+        binary_writer<char>(o).write_msgpack(j);
+    }
 
-    @note The following CBOR types are not used in the conversion:
-          - byte strings (0x40..0x5F)
-          - UTF-8 strings terminated by "break" (0x7F)
-          - arrays terminated by "break" (0x9F)
-          - maps terminated by "break" (0xBF)
-          - date/time (0xC0..0xC1)
-          - bignum (0xC2..0xC3)
-          - decimal fraction (0xC4)
-          - bigfloat (0xC5)
-          - tagged items (0xC6..0xD4, 0xD8..0xDB)
-          - expected conversions (0xD5..0xD7)
-          - simple values (0xE0..0xF3, 0xF8)
-          - undefined (0xF7)
-          - half and single-precision floats (0xF9-0xFA)
-          - break (0xFF)
+    /// @brief create a UBJSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+    static std::vector<std::uint8_t> to_ubjson(const basic_json& j,
+            const bool use_size = false,
+            const bool use_type = false)
+    {
+        std::vector<std::uint8_t> result;
+        to_ubjson(j, result, use_size, use_type);
+        return result;
+    }
 
-    @param[in] j  JSON value to serialize
-    @return MessagePack serialization as byte vector
+    /// @brief create a UBJSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+    static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type);
+    }
 
-    @complexity Linear in the size of the JSON value @a j.
+    /// @brief create a UBJSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<char>(o).write_ubjson(j, use_size, use_type);
+    }
 
-    @liveexample{The example shows the serialization of a JSON value to a byte
-    vector in CBOR format.,to_cbor}
+    /// @brief create a BJData serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+    static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
+            const bool use_size = false,
+            const bool use_type = false)
+    {
+        std::vector<std::uint8_t> result;
+        to_bjdata(j, result, use_size, use_type);
+        return result;
+    }
 
-    @sa http://cbor.io
-    @sa @ref from_cbor(raw_istream&, const bool strict) for the
-        analogous deserialization
-    @sa @ref to_msgpack(const json&) for the related MessagePack format
-    @sa @ref to_ubjson(const json&, const bool, const bool) for the
-             related UBJSON format
+    /// @brief create a BJData serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+    static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true);
+    }
 
-    @since version 2.0.9
-    */
-    static std::vector<uint8_t> to_cbor(const json& j);
-    static std::span<uint8_t> to_cbor(const json& j, std::vector<uint8_t>& buf);
-    static std::span<uint8_t> to_cbor(const json& j, SmallVectorImpl<uint8_t>& buf);
-    static void to_cbor(raw_ostream& os, const json& j);
+    /// @brief create a BJData serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+    static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true);
+    }
 
-    /*!
-    @brief create a MessagePack serialization of a given JSON value
+    /// @brief create a BSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+    static std::vector<std::uint8_t> to_bson(const basic_json& j)
+    {
+        std::vector<std::uint8_t> result;
+        to_bson(j, result);
+        return result;
+    }
 
-    Serializes a given JSON value @a j to a byte vector using the MessagePack
-    serialization format. MessagePack is a binary serialization format which
-    aims to be more compact than JSON itself, yet more efficient to parse.
+    /// @brief create a BSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+    static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+    {
+        binary_writer<std::uint8_t>(o).write_bson(j);
+    }
 
-    The library uses the following mapping from JSON values types to
-    MessagePack types according to the MessagePack specification:
+    /// @brief create a BSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+    static void to_bson(const basic_json& j, detail::output_adapter<char> o)
+    {
+        binary_writer<char>(o).write_bson(j);
+    }
 
-    JSON value type | value/range                       | MessagePack type | first byte
-    --------------- | --------------------------------- | ---------------- | ----------
-    null            | `null`                            | nil              | 0xC0
-    boolean         | `true`                            | true             | 0xC3
-    boolean         | `false`                           | false            | 0xC2
-    number_integer  | -9223372036854775808..-2147483649 | int64            | 0xD3
-    number_integer  | -2147483648..-32769               | int32            | 0xD2
-    number_integer  | -32768..-129                      | int16            | 0xD1
-    number_integer  | -128..-33                         | int8             | 0xD0
-    number_integer  | -32..-1                           | negative fixint  | 0xE0..0xFF
-    number_integer  | 0..127                            | positive fixint  | 0x00..0x7F
-    number_integer  | 128..255                          | uint 8           | 0xCC
-    number_integer  | 256..65535                        | uint 16          | 0xCD
-    number_integer  | 65536..4294967295                 | uint 32          | 0xCE
-    number_integer  | 4294967296..18446744073709551615  | uint 64          | 0xCF
-    number_unsigned | 0..127                            | positive fixint  | 0x00..0x7F
-    number_unsigned | 128..255                          | uint 8           | 0xCC
-    number_unsigned | 256..65535                        | uint 16          | 0xCD
-    number_unsigned | 65536..4294967295                 | uint 32          | 0xCE
-    number_unsigned | 4294967296..18446744073709551615  | uint 64          | 0xCF
-    number_float    | *any value*                       | float 64         | 0xCB
-    string          | *length*: 0..31                   | fixstr           | 0xA0..0xBF
-    string          | *length*: 32..255                 | str 8            | 0xD9
-    string          | *length*: 256..65535              | str 16           | 0xDA
-    string          | *length*: 65536..4294967295       | str 32           | 0xDB
-    array           | *size*: 0..15                     | fixarray         | 0x90..0x9F
-    array           | *size*: 16..65535                 | array 16         | 0xDC
-    array           | *size*: 65536..4294967295         | array 32         | 0xDD
-    object          | *size*: 0..15                     | fix map          | 0x80..0x8F
-    object          | *size*: 16..65535                 | map 16           | 0xDE
-    object          | *size*: 65536..4294967295         | map 32           | 0xDF
+    /// @brief create a JSON value from an input in CBOR format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_cbor(InputType&& i,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @note The mapping is **complete** in the sense that any JSON value type
-          can be converted to a MessagePack value.
+    /// @brief create a JSON value from an input in CBOR format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_cbor(IteratorType first, IteratorType last,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @note The following values can **not** be converted to a MessagePack value:
-          - strings with more than 4294967295 bytes
-          - arrays with more than 4294967295 elements
-          - objects with more than 4294967295 elements
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
+    static basic_json from_cbor(const T* ptr, std::size_t len,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
+    }
 
-    @note The following MessagePack types are not used in the conversion:
-          - bin 8 - bin 32 (0xC4..0xC6)
-          - ext 8 - ext 32 (0xC7..0xC9)
-          - float 32 (0xCA)
-          - fixext 1 - fixext 16 (0xD4..0xD8)
 
-    @note Any MessagePack output created @ref to_msgpack can be successfully
-          parsed by @ref from_msgpack.
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
+    static basic_json from_cbor(detail::span_input_adapter&& i,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = i.get();
+        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @note If NaN or Infinity are stored inside a JSON number, they are
-          serialized properly. This behavior differs from the @ref dump()
-          function which serializes NaN or Infinity to `null`.
+    /// @brief create a JSON value from an input in MessagePack format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_msgpack(InputType&& i,
+                                   const bool strict = true,
+                                   const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @param[in] j  JSON value to serialize
-    @return MessagePack serialization as byte vector
+    /// @brief create a JSON value from an input in MessagePack format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_msgpack(IteratorType first, IteratorType last,
+                                   const bool strict = true,
+                                   const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @complexity Linear in the size of the JSON value @a j.
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
+    static basic_json from_msgpack(const T* ptr, std::size_t len,
+                                   const bool strict = true,
+                                   const bool allow_exceptions = true)
+    {
+        return from_msgpack(ptr, ptr + len, strict, allow_exceptions);
+    }
 
-    @liveexample{The example shows the serialization of a JSON value to a byte
-    vector in MessagePack format.,to_msgpack}
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
+    static basic_json from_msgpack(detail::span_input_adapter&& i,
+                                   const bool strict = true,
+                                   const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = i.get();
+        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @sa http://msgpack.org
-    @sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the
-        analogous deserialization
-    @sa @ref to_cbor(const json& for the related CBOR format
-    @sa @ref to_ubjson(const json&, const bool, const bool) for the
-             related UBJSON format
+    /// @brief create a JSON value from an input in UBJSON format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_ubjson(InputType&& i,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @since version 2.0.9
-    */
-    static std::vector<uint8_t> to_msgpack(const json& j);
-    static std::span<uint8_t> to_msgpack(const json& j, std::vector<uint8_t>& buf);
-    static std::span<uint8_t> to_msgpack(const json& j, SmallVectorImpl<uint8_t>& buf);
-    static void to_msgpack(raw_ostream& os, const json& j);
+    /// @brief create a JSON value from an input in UBJSON format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_ubjson(IteratorType first, IteratorType last,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    /*!
-    @brief create a UBJSON serialization of a given JSON value
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
+    static basic_json from_ubjson(const T* ptr, std::size_t len,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        return from_ubjson(ptr, ptr + len, strict, allow_exceptions);
+    }
 
-    Serializes a given JSON value @a j to a byte vector using the UBJSON
-    (Universal Binary JSON) serialization format. UBJSON aims to be more compact
-    than JSON itself, yet more efficient to parse.
-
-    The library uses the following mapping from JSON values types to
-    UBJSON types according to the UBJSON specification:
-
-    JSON value type | value/range                       | UBJSON type | marker
-    --------------- | --------------------------------- | ----------- | ------
-    null            | `null`                            | null        | `Z`
-    boolean         | `true`                            | true        | `T`
-    boolean         | `false`                           | false       | `F`
-    number_integer  | -9223372036854775808..-2147483649 | int64       | `L`
-    number_integer  | -2147483648..-32769               | int32       | `l`
-    number_integer  | -32768..-129                      | int16       | `I`
-    number_integer  | -128..127                         | int8        | `i`
-    number_integer  | 128..255                          | uint8       | `U`
-    number_integer  | 256..32767                        | int16       | `I`
-    number_integer  | 32768..2147483647                 | int32       | `l`
-    number_integer  | 2147483648..9223372036854775807   | int64       | `L`
-    number_unsigned | 0..127                            | int8        | `i`
-    number_unsigned | 128..255                          | uint8       | `U`
-    number_unsigned | 256..32767                        | int16       | `I`
-    number_unsigned | 32768..2147483647                 | int32       | `l`
-    number_unsigned | 2147483648..9223372036854775807   | int64       | `L`
-    number_float    | *any value*                       | float64     | `D`
-    string          | *with shortest length indicator*  | string      | `S`
-    array           | *see notes on optimized format*   | array       | `[`
-    object          | *see notes on optimized format*   | map         | `{`
-
-    @note The mapping is **complete** in the sense that any JSON value type
-          can be converted to a UBJSON value.
-
-    @note The following values can **not** be converted to a UBJSON value:
-          - strings with more than 9223372036854775807 bytes (theoretical)
-          - unsigned integer numbers above 9223372036854775807
-
-    @note The following markers are not used in the conversion:
-          - `Z`: no-op values are not created.
-          - `C`: single-byte strings are serialized with `S` markers.
-
-    @note Any UBJSON output created @ref to_ubjson can be successfully parsed
-          by @ref from_ubjson.
-
-    @note If NaN or Infinity are stored inside a JSON number, they are
-          serialized properly. This behavior differs from the @ref dump()
-          function which serializes NaN or Infinity to `null`.
-
-    @note The optimized formats for containers are supported: Parameter
-          @a use_size adds size information to the beginning of a container and
-          removes the closing marker. Parameter @a use_type further checks
-          whether all elements of a container have the same type and adds the
-          type marker to the beginning of the container. The @a use_type
-          parameter must only be used together with @a use_size = true. Note
-          that @a use_size = true alone may result in larger representations -
-          the benefit of this parameter is that the receiving side is
-          immediately informed on the number of elements of the container.
-
-    @param[in] j  JSON value to serialize
-    @param[in] use_size  whether to add size annotations to container types
-    @param[in] use_type  whether to add type annotations to container types
-                         (must be combined with @a use_size = true)
-    @return UBJSON serialization as byte vector
-
-    @complexity Linear in the size of the JSON value @a j.
-
-    @liveexample{The example shows the serialization of a JSON value to a byte
-    vector in UBJSON format.,to_ubjson}
-
-    @sa http://ubjson.org
-    @sa @ref from_ubjson(raw_istream&, const bool strict) for the
-        analogous deserialization
-    @sa @ref to_cbor(const json& for the related CBOR format
-    @sa @ref to_msgpack(const json&) for the related MessagePack format
-
-    @since version 3.1.0
-    */
-    static std::vector<uint8_t> to_ubjson(const json& j,
-                                          const bool use_size = false,
-                                          const bool use_type = false);
-    static std::span<uint8_t> to_ubjson(const json& j, std::vector<uint8_t>& buf,
-                                   const bool use_size = false, const bool use_type = false);
-    static std::span<uint8_t> to_ubjson(const json& j, SmallVectorImpl<uint8_t>& buf,
-                                   const bool use_size = false, const bool use_type = false);
-    static void to_ubjson(raw_ostream& os, const json& j,
-                          const bool use_size = false, const bool use_type = false);
-
-    /*!
-    @brief create a JSON value from an input in CBOR format
-
-    Deserializes a given input @a i to a JSON value using the CBOR (Concise
-    Binary Object Representation) serialization format.
-
-    The library maps CBOR types to JSON value types as follows:
-
-    CBOR type              | JSON value type | first byte
-    ---------------------- | --------------- | ----------
-    Integer                | number_unsigned | 0x00..0x17
-    Unsigned integer       | number_unsigned | 0x18
-    Unsigned integer       | number_unsigned | 0x19
-    Unsigned integer       | number_unsigned | 0x1A
-    Unsigned integer       | number_unsigned | 0x1B
-    Negative integer       | number_integer  | 0x20..0x37
-    Negative integer       | number_integer  | 0x38
-    Negative integer       | number_integer  | 0x39
-    Negative integer       | number_integer  | 0x3A
-    Negative integer       | number_integer  | 0x3B
-    Negative integer       | number_integer  | 0x40..0x57
-    UTF-8 string           | string          | 0x60..0x77
-    UTF-8 string           | string          | 0x78
-    UTF-8 string           | string          | 0x79
-    UTF-8 string           | string          | 0x7A
-    UTF-8 string           | string          | 0x7B
-    UTF-8 string           | string          | 0x7F
-    array                  | array           | 0x80..0x97
-    array                  | array           | 0x98
-    array                  | array           | 0x99
-    array                  | array           | 0x9A
-    array                  | array           | 0x9B
-    array                  | array           | 0x9F
-    map                    | object          | 0xA0..0xB7
-    map                    | object          | 0xB8
-    map                    | object          | 0xB9
-    map                    | object          | 0xBA
-    map                    | object          | 0xBB
-    map                    | object          | 0xBF
-    False                  | `false`         | 0xF4
-    True                   | `true`          | 0xF5
-    Nill                   | `null`          | 0xF6
-    Half-Precision Float   | number_float    | 0xF9
-    Single-Precision Float | number_float    | 0xFA
-    Double-Precision Float | number_float    | 0xFB
-
-    @warning The mapping is **incomplete** in the sense that not all CBOR
-             types can be converted to a JSON value. The following CBOR types
-             are not supported and will yield parse errors (parse_error.112):
-             - byte strings (0x40..0x5F)
-             - date/time (0xC0..0xC1)
-             - bignum (0xC2..0xC3)
-             - decimal fraction (0xC4)
-             - bigfloat (0xC5)
-             - tagged items (0xC6..0xD4, 0xD8..0xDB)
-             - expected conversions (0xD5..0xD7)
-             - simple values (0xE0..0xF3, 0xF8)
-             - undefined (0xF7)
-
-    @warning CBOR allows map keys of any type, whereas JSON only allows
-             strings as keys in object values. Therefore, CBOR maps with keys
-             other than UTF-8 strings are rejected (parse_error.113).
-
-    @note Any CBOR output created @ref to_cbor can be successfully parsed by
-          @ref from_cbor.
-
-    @param[in] i  an input in CBOR format convertible to an input adapter
-    @param[in] strict  whether to expect the input to be consumed until EOF
-                       (true by default)
-    @return deserialized JSON value
-
-    @throw parse_error.110 if the given input ends prematurely or the end of
-    file was not reached when @a strict was set to true
-    @throw parse_error.112 if unsupported features from CBOR were
-    used in the given input @a v or if the input is not valid CBOR
-    @throw parse_error.113 if a string was expected as map key, but not found
-
-    @complexity Linear in the size of the input @a i.
-
-    @liveexample{The example shows the deserialization of a byte vector in CBOR
-    format to a JSON value.,from_cbor}
-
-    @sa http://cbor.io
-    @sa @ref to_cbor(const json&) for the analogous serialization
-    @sa @ref from_msgpack(raw_istream&, const bool) for the
-        related MessagePack format
-    @sa @ref from_ubjson(raw_istream&, const bool) for the related
-        UBJSON format
-
-    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to
-           consume input adapters, removed start_index parameter, and added
-           @a strict parameter since 3.0.0
-    */
-    static json from_cbor(raw_istream& is,
-                                const bool strict = true);
-
-    /*!
-    @copydoc from_cbor(raw_istream&, const bool)
-    */
-    static json from_cbor(std::span<const uint8_t> arr, const bool strict = true);
-
-    /*!
-    @brief create a JSON value from an input in MessagePack format
-
-    Deserializes a given input @a i to a JSON value using the MessagePack
-    serialization format.
-
-    The library maps MessagePack types to JSON value types as follows:
-
-    MessagePack type | JSON value type | first byte
-    ---------------- | --------------- | ----------
-    positive fixint  | number_unsigned | 0x00..0x7F
-    fixmap           | object          | 0x80..0x8F
-    fixarray         | array           | 0x90..0x9F
-    fixstr           | string          | 0xA0..0xBF
-    nil              | `null`          | 0xC0
-    false            | `false`         | 0xC2
-    true             | `true`          | 0xC3
-    float 32         | number_float    | 0xCA
-    float 64         | number_float    | 0xCB
-    uint 8           | number_unsigned | 0xCC
-    uint 16          | number_unsigned | 0xCD
-    uint 32          | number_unsigned | 0xCE
-    uint 64          | number_unsigned | 0xCF
-    int 8            | number_integer  | 0xD0
-    int 16           | number_integer  | 0xD1
-    int 32           | number_integer  | 0xD2
-    int 64           | number_integer  | 0xD3
-    str 8            | string          | 0xD9
-    str 16           | string          | 0xDA
-    str 32           | string          | 0xDB
-    array 16         | array           | 0xDC
-    array 32         | array           | 0xDD
-    map 16           | object          | 0xDE
-    map 32           | object          | 0xDF
-    negative fixint  | number_integer  | 0xE0-0xFF
-
-    @warning The mapping is **incomplete** in the sense that not all
-             MessagePack types can be converted to a JSON value. The following
-             MessagePack types are not supported and will yield parse errors:
-              - bin 8 - bin 32 (0xC4..0xC6)
-              - ext 8 - ext 32 (0xC7..0xC9)
-              - fixext 1 - fixext 16 (0xD4..0xD8)
-
-    @note Any MessagePack output created @ref to_msgpack can be successfully
-          parsed by @ref from_msgpack.
-
-    @param[in] i  an input in MessagePack format convertible to an input
-                  adapter
-    @param[in] strict  whether to expect the input to be consumed until EOF
-                       (true by default)
-
-    @throw parse_error.110 if the given input ends prematurely or the end of
-    file was not reached when @a strict was set to true
-    @throw parse_error.112 if unsupported features from MessagePack were
-    used in the given input @a i or if the input is not valid MessagePack
-    @throw parse_error.113 if a string was expected as map key, but not found
-
-    @complexity Linear in the size of the input @a i.
-
-    @liveexample{The example shows the deserialization of a byte vector in
-    MessagePack format to a JSON value.,from_msgpack}
-
-    @sa http://msgpack.org
-    @sa @ref to_msgpack(const json&) for the analogous serialization
-    @sa @ref from_cbor(raw_istream&, const bool) for the related CBOR
-        format
-    @sa @ref from_ubjson(raw_istream&, const bool) for the related
-        UBJSON format
-
-    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to
-           consume input adapters, removed start_index parameter, and added
-           @a strict parameter since 3.0.0
-    */
-    static json from_msgpack(raw_istream& is,
-                                   const bool strict = true);
-
-    /*!
-    @copydoc from_msgpack(raw_istream&, const bool)
-    */
-    static json from_msgpack(std::span<const uint8_t> arr, const bool strict = true);
-
-    /*!
-    @brief create a JSON value from an input in UBJSON format
-
-    Deserializes a given input @a i to a JSON value using the UBJSON (Universal
-    Binary JSON) serialization format.
-
-    The library maps UBJSON types to JSON value types as follows:
-
-    UBJSON type | JSON value type                         | marker
-    ----------- | --------------------------------------- | ------
-    no-op       | *no value, next value is read*          | `N`
-    null        | `null`                                  | `Z`
-    false       | `false`                                 | `F`
-    true        | `true`                                  | `T`
-    float32     | number_float                            | `d`
-    float64     | number_float                            | `D`
-    uint8       | number_unsigned                         | `U`
-    int8        | number_integer                          | `i`
-    int16       | number_integer                          | `I`
-    int32       | number_integer                          | `l`
-    int64       | number_integer                          | `L`
-    string      | string                                  | `S`
-    char        | string                                  | `C`
-    array       | array (optimized values are supported)  | `[`
-    object      | object (optimized values are supported) | `{`
-
-    @note The mapping is **complete** in the sense that any UBJSON value can
-          be converted to a JSON value.
-
-    @param[in] i  an input in UBJSON format convertible to an input adapter
-    @param[in] strict  whether to expect the input to be consumed until EOF
-                       (true by default)
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
+    static basic_json from_ubjson(detail::span_input_adapter&& i,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = i.get();
+        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @throw parse_error.110 if the given input ends prematurely or the end of
-    file was not reached when @a strict was set to true
-    @throw parse_error.112 if a parse error occurs
-    @throw parse_error.113 if a string could not be parsed successfully
 
-    @complexity Linear in the size of the input @a i.
+    /// @brief create a JSON value from an input in BJData format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bjdata(InputType&& i,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @liveexample{The example shows the deserialization of a byte vector in
-    UBJSON format to a JSON value.,from_ubjson}
+    /// @brief create a JSON value from an input in BJData format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bjdata(IteratorType first, IteratorType last,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @sa http://ubjson.org
-    @sa @ref to_ubjson(const json&, const bool, const bool) for the
-             analogous serialization
-    @sa @ref from_cbor(raw_istream&, const bool) for the related CBOR
-        format
-    @sa @ref from_msgpack(raw_istream&, const bool) for the related
-        MessagePack format
+    /// @brief create a JSON value from an input in BSON format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_bson/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bson(InputType&& i,
+                                const bool strict = true,
+                                const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    @since version 3.1.0
-    */
-    static json from_ubjson(raw_istream& is,
-                                  const bool strict = true);
+    /// @brief create a JSON value from an input in BSON format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_bson/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bson(IteratorType first, IteratorType last,
+                                const bool strict = true,
+                                const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
 
-    static json from_ubjson(std::span<const uint8_t> arr, const bool strict = true);
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
+    static basic_json from_bson(const T* ptr, std::size_t len,
+                                const bool strict = true,
+                                const bool allow_exceptions = true)
+    {
+        return from_bson(ptr, ptr + len, strict, allow_exceptions);
+    }
 
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
+    static basic_json from_bson(detail::span_input_adapter&& i,
+                                const bool strict = true,
+                                const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = i.get();
+        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
     /// @}
 
     //////////////////////////
@@ -7633,218 +4605,74 @@
     /// @name JSON Pointer functions
     /// @{
 
-    /*!
-    @brief access specified element via JSON Pointer
-
-    Uses a JSON pointer to retrieve a reference to the respective JSON value.
-    No bound checking is performed. Similar to @ref operator[](const typename
-    object_t::key_type&), `null` values are created in arrays and objects if
-    necessary.
-
-    In particular:
-    - If the JSON pointer points to an object key that does not exist, it
-      is created an filled with a `null` value before a reference to it
-      is returned.
-    - If the JSON pointer points to an array index that does not exist, it
-      is created an filled with a `null` value before a reference to it
-      is returned. All indices between the current maximum and the given
-      index are also filled with `null`.
-    - The special value `-` is treated as a synonym for the index past the
-      end.
-
-    @param[in] ptr  a JSON pointer
-
-    @return reference to the element pointed to by @a ptr
-
-    @complexity Constant.
-
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-
-    @liveexample{The behavior is shown in the example.,operatorjson_pointer}
-
-    @since version 2.0.0
-    */
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
     reference operator[](const json_pointer& ptr)
     {
         return ptr.get_unchecked(this);
     }
 
-    /*!
-    @brief access specified element via JSON Pointer
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or wpi::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    reference operator[](const ::wpi::json_pointer<BasicJsonType>& ptr)
+    {
+        return ptr.get_unchecked(this);
+    }
 
-    Uses a JSON pointer to retrieve a reference to the respective JSON value.
-    No bound checking is performed. The function does not change the JSON
-    value; no `null` values are created. In particular, the the special value
-    `-` yields an exception.
-
-    @param[in] ptr  JSON pointer to the desired element
-
-    @return const reference to the element pointed to by @a ptr
-
-    @complexity Constant.
-
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.402  if the array index '-' is used
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-
-    @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}
-
-    @since version 2.0.0
-    */
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
     const_reference operator[](const json_pointer& ptr) const
     {
         return ptr.get_unchecked(this);
     }
 
-    /*!
-    @brief access specified element via JSON Pointer
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or wpi::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    const_reference operator[](const ::wpi::json_pointer<BasicJsonType>& ptr) const
+    {
+        return ptr.get_unchecked(this);
+    }
 
-    Returns a reference to the element at with specified JSON pointer @a ptr,
-    with bounds checking.
-
-    @param[in] ptr  JSON pointer to the desired element
-
-    @return reference to the element pointed to by @a ptr
-
-    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr
-    begins with '0'. See example below.
-
-    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr
-    is not a number. See example below.
-
-    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr
-    is out of range. See example below.
-
-    @throw out_of_range.402 if the array index '-' is used in the passed JSON
-    pointer @a ptr. As `at` provides checked access (and no elements are
-    implicitly inserted), the index '-' is always invalid. See example below.
-
-    @throw out_of_range.403 if the JSON pointer describes a key of an object
-    which cannot be found. See example below.
-
-    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.
-    See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @since version 2.0.0
-
-    @liveexample{The behavior is shown in the example.,at_json_pointer}
-    */
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
     reference at(const json_pointer& ptr)
     {
         return ptr.get_checked(this);
     }
 
-    /*!
-    @brief access specified element via JSON Pointer
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or wpi::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    reference at(const ::wpi::json_pointer<BasicJsonType>& ptr)
+    {
+        return ptr.get_checked(this);
+    }
 
-    Returns a const reference to the element at with specified JSON pointer @a
-    ptr, with bounds checking.
-
-    @param[in] ptr  JSON pointer to the desired element
-
-    @return reference to the element pointed to by @a ptr
-
-    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr
-    begins with '0'. See example below.
-
-    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr
-    is not a number. See example below.
-
-    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr
-    is out of range. See example below.
-
-    @throw out_of_range.402 if the array index '-' is used in the passed JSON
-    pointer @a ptr. As `at` provides checked access (and no elements are
-    implicitly inserted), the index '-' is always invalid. See example below.
-
-    @throw out_of_range.403 if the JSON pointer describes a key of an object
-    which cannot be found. See example below.
-
-    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.
-    See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @since version 2.0.0
-
-    @liveexample{The behavior is shown in the example.,at_json_pointer_const}
-    */
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
     const_reference at(const json_pointer& ptr) const
     {
         return ptr.get_checked(this);
     }
 
-    /*!
-    @brief return flattened JSON value
-
-    The function creates a JSON object whose keys are JSON pointers (see [RFC
-    6901](https://tools.ietf.org/html/rfc6901)) and whose values are all
-    primitive. The original JSON value can be restored using the @ref
-    unflatten() function.
-
-    @return an object that maps JSON pointers to primitive values
-
-    @note Empty objects and arrays are flattened to `null` and will not be
-          reconstructed correctly by the @ref unflatten() function.
-
-    @complexity Linear in the size the JSON value.
-
-    @liveexample{The following code shows how a JSON object is flattened to an
-    object whose keys consist of JSON pointers.,flatten}
-
-    @sa @ref unflatten() for the reverse function
-
-    @since version 2.0.0
-    */
-    json flatten() const
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or wpi::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    const_reference at(const ::wpi::json_pointer<BasicJsonType>& ptr) const
     {
-        json result(value_t::object);
+        return ptr.get_checked(this);
+    }
+
+    /// @brief return flattened JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/flatten/
+    basic_json flatten() const
+    {
+        basic_json result(value_t::object);
         json_pointer::flatten("", *this, result);
         return result;
     }
 
-    /*!
-    @brief unflatten a previously flattened JSON value
-
-    The function restores the arbitrary nesting of a JSON value that has been
-    flattened before using the @ref flatten() function. The JSON value must
-    meet certain constraints:
-    1. The value must be an object.
-    2. The keys must be JSON pointers (see
-       [RFC 6901](https://tools.ietf.org/html/rfc6901))
-    3. The mapped values must be primitive JSON types.
-
-    @return the original JSON from a flattened version
-
-    @note Empty objects and arrays are flattened by @ref flatten() to `null`
-          values and can not unflattened to their original type. Apart from
-          this example, for a JSON value `j`, the following is always true:
-          `j == j.flatten().unflatten()`.
-
-    @complexity Linear in the size the JSON value.
-
-    @throw type_error.314  if value is not an object
-    @throw type_error.315  if object values are not primitive
-
-    @liveexample{The following code shows how a flattened JSON object is
-    unflattened into the original nested JSON object.,unflatten}
-
-    @sa @ref flatten() for the reverse function
-
-    @since version 2.0.0
-    */
-    json unflatten() const
+    /// @brief unflatten a previously flattened JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/unflatten/
+    basic_json unflatten() const
     {
         return json_pointer::unflatten(*this);
     }
@@ -7858,91 +4686,420 @@
     /// @name JSON Patch functions
     /// @{
 
-    /*!
-    @brief applies a JSON patch
+    /// @brief applies a JSON patch in-place without copying the object
+    /// @sa https://json.nlohmann.me/api/basic_json/patch/
+    void patch_inplace(const basic_json& json_patch)
+    {
+        basic_json& result = *this;
+        // the valid JSON Patch operations
+        enum class patch_operations {add, remove, replace, move, copy, test, invalid};
 
-    [JSON Patch](http://jsonpatch.com) defines a JSON document structure for
-    expressing a sequence of operations to apply to a JSON) document. With
-    this function, a JSON Patch is applied to the current JSON value by
-    executing all operations from the patch.
+        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;
+            }
 
-    @param[in] json_patch  JSON patch document
-    @return patched document
+            return patch_operations::invalid;
+        };
 
-    @note The application of a patch is atomic: Either all operations succeed
-          and the patched document is returned or an exception is thrown. In
-          any case, the original value is not changed: the patch is applied
-          to a copy of the value.
+        // wrapper for "add" operation; add value at ptr
+        const auto operation_add = [&result](json_pointer & ptr, basic_json val)
+        {
+            // adding to the root of the target document means replacing it
+            if (ptr.empty())
+            {
+                result = val;
+                return;
+            }
 
-    @throw parse_error.104 if the JSON patch does not consist of an array of
-    objects
+            // make sure the top element of the pointer exists
+            json_pointer top_pointer = ptr.top();
+            if (top_pointer != ptr)
+            {
+                result.at(top_pointer);
+            }
 
-    @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory
-    attributes are missing); example: `"operation add must have member path"`
+            // get reference to parent of JSON pointer ptr
+            const auto last_path = ptr.back();
+            ptr.pop_back();
+            // parent must exist when performing patch add per RFC6902 specs
+            basic_json& parent = result.at(ptr);
 
-    @throw out_of_range.401 if an array index is out of range.
+            switch (parent.m_type)
+            {
+                case value_t::null:
+                case value_t::object:
+                {
+                    // use operator[] to add value
+                    parent[last_path] = val;
+                    break;
+                }
 
-    @throw out_of_range.403 if a JSON pointer inside the patch could not be
-    resolved successfully in the current JSON value; example: `"key baz not
-    found"`
+                case value_t::array:
+                {
+                    if (last_path == "-")
+                    {
+                        // special case: append to back
+                        parent.push_back(val);
+                    }
+                    else
+                    {
+                        const auto idx = json_pointer::template array_index<basic_json_t>(last_path);
+                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
+                        {
+                            // avoid undefined behavior
+                            JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent));
+                        }
 
-    @throw out_of_range.405 if JSON pointer has no parent ("add", "remove",
-    "move")
+                        // default case: insert add offset
+                        parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
+                    }
+                    break;
+                }
 
-    @throw other_error.501 if "test" operation was unsuccessful
+                // if there exists a parent it cannot be primitive
+                case value_t::string: // LCOV_EXCL_LINE
+                case value_t::boolean: // LCOV_EXCL_LINE
+                case value_t::number_integer: // LCOV_EXCL_LINE
+                case value_t::number_unsigned: // LCOV_EXCL_LINE
+                case value_t::number_float: // LCOV_EXCL_LINE
+                case value_t::binary: // LCOV_EXCL_LINE
+                case value_t::discarded: // LCOV_EXCL_LINE
+                default:            // LCOV_EXCL_LINE
+                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+            }
+        };
 
-    @complexity Linear in the size of the JSON value and the length of the
-    JSON patch. As usually only a fraction of the JSON value is affected by
-    the patch, the complexity can usually be neglected.
+        // wrapper for "remove" operation; remove value at ptr
+        const auto operation_remove = [this, &result](json_pointer & ptr)
+        {
+            // get reference to parent of JSON pointer ptr
+            const auto last_path = ptr.back();
+            ptr.pop_back();
+            basic_json& parent = result.at(ptr);
 
-    @liveexample{The following code shows how a JSON patch is applied to a
-    value.,patch}
+            // remove child
+            if (parent.is_object())
+            {
+                // perform range check
+                auto it = parent.find(last_path);
+                if (JSON_HEDLEY_LIKELY(it != parent.end()))
+                {
+                    parent.erase(it);
+                }
+                else
+                {
+                    JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this));
+                }
+            }
+            else if (parent.is_array())
+            {
+                // note erase performs range check
+                parent.erase(json_pointer::template array_index<basic_json_t>(last_path));
+            }
+        };
 
-    @sa @ref diff -- create a JSON patch by comparing two JSON values
+        // type check: top level value must be an array
+        if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
+        {
+            JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch));
+        }
 
-    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
-    @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)
+        // 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) -> basic_json &
+            {
+                // find value
+                auto it = val.m_value.object->find(member);
 
-    @since version 2.0.0
-    */
-    json patch(const json& json_patch) const;
+                // context-sensitive error message
+                const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\'');
 
-    /*!
-    @brief creates a diff as a JSON patch
+                // check if desired value is present
+                if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))
+                {
+                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+                    JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val));
+                }
 
-    Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can
-    be changed into the value @a target by calling @ref patch function.
+                // check if result is of type string
+                if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
+                {
+                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+                    JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val));
+                }
 
-    @invariant For two JSON values @a source and @a target, the following code
-    yields always `true`:
-    @code {.cpp}
-    source.patch(diff(source, target)) == target;
-    @endcode
+                // no error: return value
+                return it->second;
+            };
 
-    @note Currently, only `remove`, `add`, and `replace` operations are
-          generated.
+            // type check: every element of the array must be an object
+            if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
+            {
+                JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val));
+            }
 
-    @param[in] source  JSON value to compare from
-    @param[in] target  JSON value to compare against
-    @param[in] path    helper value to create JSON pointers
+            // collect mandatory members
+            const auto op = get_value("op", "op", true).template get<std::string>();
+            const auto path = get_value(op, "path", true).template get<std::string>();
+            json_pointer ptr(path);
 
-    @return a JSON patch to convert the @a source to @a target
+            switch (get_op(op))
+            {
+                case patch_operations::add:
+                {
+                    operation_add(ptr, get_value("add", "value", false));
+                    break;
+                }
 
-    @complexity Linear in the lengths of @a source and @a target.
+                case patch_operations::remove:
+                {
+                    operation_remove(ptr);
+                    break;
+                }
 
-    @liveexample{The following code shows how a JSON patch is created as a
-    diff for two JSON values.,diff}
+                case patch_operations::replace:
+                {
+                    // the "path" location must exist - use at()
+                    result.at(ptr) = get_value("replace", "value", false);
+                    break;
+                }
 
-    @sa @ref patch -- apply a JSON patch
-    @sa @ref merge_patch -- apply a JSON Merge Patch
+                case patch_operations::move:
+                {
+                    const auto from_path = get_value("move", "from", true).template get<std::string>();
+                    json_pointer from_ptr(from_path);
 
-    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
+                    // the "from" location must exist - use at()
+                    basic_json v = result.at(from_ptr);
 
-    @since version 2.0.0
-    */
-    static json diff(const json& source, const json& target,
-                           const std::string& path = "");
+                    // 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 auto from_path = get_value("copy", "from", true).template get<std::string>();
+                    const json_pointer from_ptr(from_path);
+
+                    // the "from" location must exist - use at()
+                    basic_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_INTERNAL_CATCH (out_of_range&)
+                    {
+                        // ignore out of range errors: success remains false
+                    }
+
+                    // throw an exception if test fails
+                    if (JSON_HEDLEY_UNLIKELY(!success))
+                    {
+                        JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val));
+                    }
+
+                    break;
+                }
+
+                case patch_operations::invalid:
+                default:
+                {
+                    // op must be "add", "remove", "replace", "move", "copy", or
+                    // "test"
+                    JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val));
+                }
+            }
+        }
+    }
+
+    /// @brief applies a JSON patch to a copy of the current object
+    /// @sa https://json.nlohmann.me/api/basic_json/patch/
+    basic_json patch(const basic_json& json_patch) const
+    {
+        basic_json result = *this;
+        result.patch_inplace(json_patch);
+        return result;
+    }
+
+    /// @brief creates a diff as a JSON patch
+    /// @sa https://json.nlohmann.me/api/basic_json/diff/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json diff(const basic_json& source, const basic_json& target,
+                           const std::string& path = "")
+    {
+        // the patch
+        basic_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}
+            });
+            return result;
+        }
+
+        switch (source.type())
+        {
+            case value_t::array:
+            {
+                // first pass: traverse common elements
+                std::size_t i = 0;
+                while (i < source.size() && i < target.size())
+                {
+                    // recursive call to compare array values at index i
+                    auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i)));
+                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                    ++i;
+                }
+
+                // We 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", detail::concat(path, '/', std::to_string(i))}
+                    }));
+                    ++i;
+                }
+
+                // add other remaining elements
+                while (i < target.size())
+                {
+                    result.push_back(
+                    {
+                        {"op", "add"},
+                        {"path", detail::concat(path, "/-")},
+                        {"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 path_key = detail::concat(path, '/', detail::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 path_key = detail::concat(path, '/', detail::escape(it.key()));
+                        result.push_back(
+                        {
+                            {"op", "add"}, {"path", path_key},
+                            {"value", it.value()}
+                        });
+                    }
+                }
+
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // both primitive type: replace value
+                result.push_back(
+                {
+                    {"op", "replace"}, {"path", path}, {"value", target}
+                });
+                break;
+            }
+        }
+
+        return result;
+    }
     /// @}
 
     ////////////////////////////////
@@ -7952,168 +5109,129 @@
     /// @name JSON Merge Patch functions
     /// @{
 
-    /*!
-    @brief applies a JSON Merge Patch
-
-    The merge patch format is primarily intended for use with the HTTP PATCH
-    method as a means of describing a set of modifications to a target
-    resource's content. This function applies a merge patch to the current
-    JSON value.
-
-    The function implements the following algorithm from Section 2 of
-    [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):
-
-    ```
-    define MergePatch(Target, Patch):
-      if Patch is an Object:
-        if Target is not an Object:
-          Target = {} // Ignore the contents and set it to an empty Object
-        for each Name/Value pair in Patch:
-          if Value is null:
-            if Name exists in Target:
-              remove the Name/Value pair from Target
-          else:
-            Target[Name] = MergePatch(Target[Name], Value)
-        return Target
-      else:
-        return Patch
-    ```
-
-    Thereby, `Target` is the current object; that is, the patch is applied to
-    the current value.
-
-    @param[in] patch  the patch to apply
-
-    @complexity Linear in the lengths of @a patch.
-
-    @liveexample{The following code shows how a JSON Merge Patch is applied to
-    a JSON document.,merge_patch}
-
-    @sa @ref patch -- apply a JSON patch
-    @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)
-
-    @since version 3.0.0
-    */
-    void merge_patch(const json& patch);
+    /// @brief applies a JSON Merge Patch
+    /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/
+    void merge_patch(const basic_json& apply_patch)
+    {
+        if (apply_patch.is_object())
+        {
+            if (!is_object())
+            {
+                *this = object();
+            }
+            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)
+            {
+                if (it.value().is_null())
+                {
+                    erase(it.key());
+                }
+                else
+                {
+                    operator[](it.key()).merge_patch(it.value());
+                }
+            }
+        }
+        else
+        {
+            *this = apply_patch;
+        }
+    }
 
     /// @}
 };
-} // namespace wpi
+
+/// @brief user-defined to_string function for JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/to_string/
+WPI_BASIC_JSON_TPL_DECLARATION
+std::string to_string(const WPI_BASIC_JSON_TPL& j)
+{
+    return j.dump();
+}
+
+inline namespace literals
+{
+inline namespace json_literals
+{
+
+/// @brief user-defined string literal for JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/
+JSON_HEDLEY_NON_NULL(1)
+inline wpi::json operator "" _json(const char* s, std::size_t n)
+{
+    return wpi::json::parse(s, s + n);
+}
+
+/// @brief user-defined string literal for JSON pointer
+/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/
+JSON_HEDLEY_NON_NULL(1)
+inline wpi::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
+{
+    return wpi::json::json_pointer(std::string(s, n));
+}
+
+}  // namespace json_literals
+}  // namespace literals
+WPI_JSON_NAMESPACE_END
 
 ///////////////////////
 // nonmember support //
 ///////////////////////
 
-// specialization of std::swap, and std::hash
-namespace std
+namespace std // NOLINT(cert-dcl58-cpp)
 {
-/*!
-@brief exchanges the values of two JSON objects
 
-@since version 1.0.0
-*/
-template<>
-inline void swap<wpi::json>(wpi::json& j1,
-                 wpi::json& j2) noexcept(
-                     is_nothrow_move_constructible<wpi::json>::value and
-                     is_nothrow_move_assignable<wpi::json>::value
-                 )
+/// @brief hash value for JSON objects
+/// @sa https://json.nlohmann.me/api/basic_json/std_hash/
+WPI_BASIC_JSON_TPL_DECLARATION
+struct hash<wpi::WPI_BASIC_JSON_TPL>
 {
-    j1.swap(j2);
-}
-
-/// hash value for JSON objects
-template<>
-struct hash<wpi::json>
-{
-    /*!
-    @brief return a hash value for a JSON object
-
-    @since version 1.0.0
-    */
-    std::size_t operator()(const wpi::json& j) const
+    std::size_t operator()(const wpi::WPI_BASIC_JSON_TPL& j) const
     {
-        // a naive hashing via the string representation
-        const auto& h = hash<std::string>();
-        return h(j.dump());
+        return wpi::detail::hash(j);
     }
 };
 
-/// specialization for std::less<value_t>
-/// @note: do not remove the space after '<',
-///        see https://github.com/nlohmann/json/pull/679
+// specialization for std::less<value_t>
 template<>
-struct less< ::wpi::detail::value_t>
+struct less< ::wpi::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679
 {
     /*!
     @brief compare two value_t enum values
     @since version 3.0.0
     */
-    bool operator()(wpi::detail::value_t lhs,
-                    wpi::detail::value_t rhs) const noexcept
+    bool operator()(::wpi::detail::value_t lhs,
+                    ::wpi::detail::value_t rhs) const noexcept
     {
-        return wpi::detail::operator<(lhs, rhs);
+#if JSON_HAS_THREE_WAY_COMPARISON
+        return std::is_lt(lhs <=> rhs); // *NOPAD*
+#else
+        return ::wpi::detail::operator<(lhs, rhs);
+#endif
     }
 };
 
-} // namespace std
+// C++20 prohibit function specialization in the std namespace.
+#ifndef JSON_HAS_CPP_20
 
-/*!
-@brief user-defined string literal for JSON values
-
-This operator implements a user-defined string literal for JSON objects. It
-can be used by adding `"_json"` to a string literal and returns a JSON object
-if no parse error occurred.
-
-@param[in] s  a string representation of a JSON object
-@param[in] n  the length of string @a s
-@return a JSON object
-
-@since version 1.0.0
-*/
-inline wpi::json operator "" _json(const char* s, std::size_t n)
+/// @brief exchanges the values of two JSON objects
+/// @sa https://json.nlohmann.me/api/basic_json/std_swap/
+WPI_BASIC_JSON_TPL_DECLARATION
+inline void swap(wpi::WPI_BASIC_JSON_TPL& j1, wpi::WPI_BASIC_JSON_TPL& j2) noexcept(  // NOLINT(readability-inconsistent-declaration-parameter-name)
+    is_nothrow_move_constructible<wpi::WPI_BASIC_JSON_TPL>::value&&                          // NOLINT(misc-redundant-expression)
+    is_nothrow_move_assignable<wpi::WPI_BASIC_JSON_TPL>::value)
 {
-    return wpi::json::parse(std::string_view(s, n));
+    j1.swap(j2);
 }
 
-/*!
-@brief user-defined string literal for JSON pointer
-
-This operator implements a user-defined string literal for JSON Pointers. It
-can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer
-object if no parse error occurred.
-
-@param[in] s  a string representation of a JSON Pointer
-@param[in] n  the length of string @a s
-@return a JSON pointer object
-
-@since version 2.0.0
-*/
-inline wpi::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
-{
-    return wpi::json::json_pointer(std::string_view(s, n));
-}
-
-#ifndef WPI_JSON_IMPLEMENTATION
-
-// restore GCC/clang diagnostic settings
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
-    #pragma GCC diagnostic pop
-#endif
-#if defined(__clang__)
-    #pragma GCC diagnostic pop
 #endif
 
-// clean up
-#undef JSON_CATCH
-#undef JSON_THROW
-#undef JSON_TRY
-#undef JSON_LIKELY
-#undef JSON_UNLIKELY
-#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
-#undef NLOHMANN_BASIC_JSON_TPL
-#undef NLOHMANN_JSON_HAS_HELPER
+}  // namespace std
 
-#endif  // WPI_JSON_IMPLEMENTATION
-
+#if JSON_USE_GLOBAL_UDLS
+    using wpi::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+    using wpi::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
 #endif
+
+#include <wpi/detail/macro_unscope.h>
+
+#endif  // INCLUDE_WPI_JSON_HPP_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/json_fwd.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/json_fwd.h
new file mode 100644
index 0000000..3f549d7
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/json_fwd.h
@@ -0,0 +1,74 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#ifndef INCLUDE_WPI_JSON_FWD_HPP_
+#define INCLUDE_WPI_JSON_FWD_HPP_
+
+#include <cstdint> // int64_t, uint64_t
+#include <map> // map
+#include <memory> // allocator
+#include <string> // string
+#include <vector> // vector
+
+#include <wpi/detail/abi_macros.h>
+
+/*!
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
+@since version 1.0.0
+*/
+WPI_JSON_NAMESPACE_BEGIN
+
+/*!
+@brief default JSONSerializer template argument
+
+This serializer ignores the template arguments and uses ADL
+([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
+for serialization.
+*/
+template<typename T = void, typename SFINAE = void>
+struct adl_serializer;
+
+/// a class to store JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/
+template<template<typename U, typename V, typename... Args> class ObjectType =
+         std::map,
+         template<typename U, typename... Args> class ArrayType = std::vector,
+         class StringType = std::string, class BooleanType = bool,
+         class NumberIntegerType = std::int64_t,
+         class NumberUnsignedType = std::uint64_t,
+         class NumberFloatType = double,
+         template<typename U> class AllocatorType = std::allocator,
+         template<typename T, typename SFINAE = void> class JSONSerializer =
+         adl_serializer,
+         class BinaryType = std::vector<std::uint8_t>>
+class basic_json;
+
+/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
+/// @sa https://json.nlohmann.me/api/json_pointer/
+template<typename RefStringType>
+class json_pointer;
+
+/*!
+@brief default specialization
+@sa https://json.nlohmann.me/api/json/
+*/
+using json = basic_json<>;
+
+/// @brief a minimal map-like container that preserves insertion order
+/// @sa https://json.nlohmann.me/api/ordered_map/
+template<class Key, class T, class IgnoredLess, class Allocator>
+struct ordered_map;
+
+/// @brief specialization that maintains the insertion order of object keys
+/// @sa https://json.nlohmann.me/api/ordered_json/
+using ordered_json = basic_json<wpi::ordered_map>;
+
+WPI_JSON_NAMESPACE_END
+
+#endif  // INCLUDE_WPI_JSON_FWD_HPP_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/json_serializer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/json_serializer.h
deleted file mode 100644
index 3796c95..0000000
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/json_serializer.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* 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 <type_traits>
-
-#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
-    @param[in] indent_init_len  initial length of indentation string buffer
-    */
-    serializer(raw_ostream& s, const char ichar, size_t indent_init_len = 512)
-        : o(s), indent_char(ichar),
-          indent_string(indent_init_len, 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] ensure_ascii    whether the output shall only use ASCII chars
-    @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);
-
-    /*!
-    @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(std::string_view s, const bool ensure_ascii);
-
-    template <typename NumberType,
-              detail::enable_if_t<std::is_same_v<NumberType, uint64_t>, int> = 0>
-    bool is_negative_integer(NumberType x) {
-      return false;
-    }
-
-    template <typename NumberType,
-              detail::enable_if_t<std::is_same_v<NumberType, int64_t>, int> = 0>
-    bool is_negative_integer(NumberType x) {
-      return x < 0;
-    }
-
-    /*!
-    @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 = is_negative_integer(x);  // 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 indentation character
-    const char indent_char;
-    /// the indentation string
-    std::string indent_string;
-};
-
-}  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/ordered_map.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/ordered_map.h
new file mode 100644
index 0000000..14969f1
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/ordered_map.h
@@ -0,0 +1,359 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <functional> // equal_to, less
+#include <initializer_list> // initializer_list
+#include <iterator> // input_iterator_tag, iterator_traits
+#include <memory> // allocator
+#include <stdexcept> // for out_of_range
+#include <type_traits> // enable_if, is_convertible
+#include <utility> // pair
+#include <vector> // vector
+
+#include <wpi/detail/macro_scope.h>
+#include <wpi/detail/meta/type_traits.h>
+
+WPI_JSON_NAMESPACE_BEGIN
+
+/// ordered_map: a minimal map-like container that preserves insertion order
+/// for use within wpi::basic_json<ordered_map>
+template <class Key, class T, class IgnoredLess = std::less<Key>,
+          class Allocator = std::allocator<std::pair<const Key, T>>>
+                  struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>
+{
+    using key_type = Key;
+    using mapped_type = T;
+    using Container = std::vector<std::pair<const Key, T>, Allocator>;
+    using iterator = typename Container::iterator;
+    using const_iterator = typename Container::const_iterator;
+    using size_type = typename Container::size_type;
+    using value_type = typename Container::value_type;
+#ifdef JSON_HAS_CPP_14
+    using key_compare = std::equal_to<>;
+#else
+    using key_compare = std::equal_to<Key>;
+#endif
+
+    // Explicit constructors instead of `using Container::Container`
+    // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
+    ordered_map() noexcept(noexcept(Container())) : Container{} {}
+    explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}
+    template <class It>
+    ordered_map(It first, It last, const Allocator& alloc = Allocator())
+        : Container{first, last, alloc} {}
+    ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
+        : Container{init, alloc} {}
+
+    std::pair<iterator, bool> emplace(const key_type& key, T&& t)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return {it, false};
+            }
+        }
+        Container::emplace_back(key, std::forward<T>(t));
+        return {std::prev(this->end()), true};
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    std::pair<iterator, bool> emplace(KeyType && key, T && t)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return {it, false};
+            }
+        }
+        Container::emplace_back(std::forward<KeyType>(key), std::forward<T>(t));
+        return {std::prev(this->end()), true};
+    }
+
+    T& operator[](const key_type& key)
+    {
+        return emplace(key, T{}).first->second;
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    T & operator[](KeyType && key)
+    {
+        return emplace(std::forward<KeyType>(key), T{}).first->second;
+    }
+
+    const T& operator[](const key_type& key) const
+    {
+        return at(key);
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    const T & operator[](KeyType && key) const
+    {
+        return at(std::forward<KeyType>(key));
+    }
+
+    T& at(const key_type& key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    T & at(KeyType && key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    const T& at(const key_type& key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    const T & at(KeyType && key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    size_type erase(const key_type& key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                // Since we cannot move const Keys, re-construct them in place
+                for (auto next = it; ++next != this->end(); ++it)
+                {
+                    it->~value_type(); // Destroy but keep allocation
+                    new (&*it) value_type{std::move(*next)};
+                }
+                Container::pop_back();
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    size_type erase(KeyType && key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                // Since we cannot move const Keys, re-construct them in place
+                for (auto next = it; ++next != this->end(); ++it)
+                {
+                    it->~value_type(); // Destroy but keep allocation
+                    new (&*it) value_type{std::move(*next)};
+                }
+                Container::pop_back();
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    iterator erase(iterator pos)
+    {
+        return erase(pos, std::next(pos));
+    }
+
+    iterator erase(iterator first, iterator last)
+    {
+        if (first == last)
+        {
+            return first;
+        }
+
+        const auto elements_affected = std::distance(first, last);
+        const auto offset = std::distance(Container::begin(), first);
+
+        // This is the start situation. We need to delete elements_affected
+        // elements (3 in this example: e, f, g), and need to return an
+        // iterator past the last deleted element (h in this example).
+        // Note that offset is the distance from the start of the vector
+        // to first. We will need this later.
+
+        // [ a, b, c, d, e, f, g, h, i, j ]
+        //               ^        ^
+        //             first    last
+
+        // Since we cannot move const Keys, we re-construct them in place.
+        // We start at first and re-construct (viz. copy) the elements from
+        // the back of the vector. Example for first iteration:
+
+        //               ,--------.
+        //               v        |   destroy e and re-construct with h
+        // [ a, b, c, d, e, f, g, h, i, j ]
+        //               ^        ^
+        //               it       it + elements_affected
+
+        for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it)
+        {
+            it->~value_type(); // destroy but keep allocation
+            new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it
+        }
+
+        // [ a, b, c, d, h, i, j, h, i, j ]
+        //               ^        ^
+        //             first    last
+
+        // remove the unneeded elements at the end of the vector
+        Container::resize(this->size() - static_cast<size_type>(elements_affected));
+
+        // [ a, b, c, d, h, i, j ]
+        //               ^        ^
+        //             first    last
+
+        // first is now pointing past the last deleted element, but we cannot
+        // use this iterator, because it may have been invalidated by the
+        // resize call. Instead, we can return begin() + offset.
+        return Container::begin() + offset;
+    }
+
+    size_type count(const key_type& key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    size_type count(KeyType && key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    iterator find(const key_type& key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it;
+            }
+        }
+        return Container::end();
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    iterator find(KeyType && key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it;
+            }
+        }
+        return Container::end();
+    }
+
+    const_iterator find(const key_type& key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it;
+            }
+        }
+        return Container::end();
+    }
+
+    std::pair<iterator, bool> insert( value_type&& value )
+    {
+        return emplace(value.first, std::move(value.second));
+    }
+
+    std::pair<iterator, bool> insert( const value_type& value )
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, value.first))
+            {
+                return {it, false};
+            }
+        }
+        Container::push_back(value);
+        return {--this->end(), true};
+    }
+
+    template<typename InputIt>
+    using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
+            std::input_iterator_tag>::value>::type;
+
+    template<typename InputIt, typename = require_input_iter<InputIt>>
+    void insert(InputIt first, InputIt last)
+    {
+        for (auto it = first; it != last; ++it)
+        {
+            insert(*it);
+        }
+    }
+
+private:
+    JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare();
+};
+
+WPI_JSON_NAMESPACE_END
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/thirdparty/hedley/hedley.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/thirdparty/hedley/hedley.h
new file mode 100644
index 0000000..f1377f1
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/thirdparty/hedley/hedley.h
@@ -0,0 +1,2045 @@
+#pragma once
+
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson <evan@nemerson.com>
+// SPDX-License-Identifier: MIT
+
+/* Hedley - https://nemequ.github.io/hedley
+ * Created by Evan Nemerson <evan@nemerson.com>
+ */
+
+#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15)
+#if defined(JSON_HEDLEY_VERSION)
+    #undef JSON_HEDLEY_VERSION
+#endif
+#define JSON_HEDLEY_VERSION 15
+
+#if defined(JSON_HEDLEY_STRINGIFY_EX)
+    #undef JSON_HEDLEY_STRINGIFY_EX
+#endif
+#define JSON_HEDLEY_STRINGIFY_EX(x) #x
+
+#if defined(JSON_HEDLEY_STRINGIFY)
+    #undef JSON_HEDLEY_STRINGIFY
+#endif
+#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)
+
+#if defined(JSON_HEDLEY_CONCAT_EX)
+    #undef JSON_HEDLEY_CONCAT_EX
+#endif
+#define JSON_HEDLEY_CONCAT_EX(a,b) a##b
+
+#if defined(JSON_HEDLEY_CONCAT)
+    #undef JSON_HEDLEY_CONCAT
+#endif
+#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)
+
+#if defined(JSON_HEDLEY_CONCAT3_EX)
+    #undef JSON_HEDLEY_CONCAT3_EX
+#endif
+#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c
+
+#if defined(JSON_HEDLEY_CONCAT3)
+    #undef JSON_HEDLEY_CONCAT3
+#endif
+#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)
+
+#if defined(JSON_HEDLEY_VERSION_ENCODE)
+    #undef JSON_HEDLEY_VERSION_ENCODE
+#endif
+#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)
+    #undef JSON_HEDLEY_VERSION_DECODE_MAJOR
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)
+    #undef JSON_HEDLEY_VERSION_DECODE_MINOR
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)
+    #undef JSON_HEDLEY_VERSION_DECODE_REVISION
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)
+
+#if defined(JSON_HEDLEY_GNUC_VERSION)
+    #undef JSON_HEDLEY_GNUC_VERSION
+#endif
+#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)
+    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#elif defined(__GNUC__)
+    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)
+    #undef JSON_HEDLEY_GNUC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_GNUC_VERSION)
+    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_MSVC_VERSION)
+    #undef JSON_HEDLEY_MSVC_VERSION
+#endif
+#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL)
+    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)
+#elif defined(_MSC_FULL_VER) && !defined(__ICL)
+    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)
+#elif defined(_MSC_VER) && !defined(__ICL)
+    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)
+    #undef JSON_HEDLEY_MSVC_VERSION_CHECK
+#endif
+#if !defined(JSON_HEDLEY_MSVC_VERSION)
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)
+#elif defined(_MSC_VER) && (_MSC_VER >= 1400)
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))
+#else
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_VERSION)
+    #undef JSON_HEDLEY_INTEL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL)
+    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)
+#elif defined(__INTEL_COMPILER) && !defined(__ICL)
+    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)
+    #undef JSON_HEDLEY_INTEL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_VERSION)
+    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+    #undef JSON_HEDLEY_INTEL_CL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL)
+    #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK)
+    #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_PGI_VERSION)
+    #undef JSON_HEDLEY_PGI_VERSION
+#endif
+#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)
+    #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)
+#endif
+
+#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)
+    #undef JSON_HEDLEY_PGI_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_PGI_VERSION)
+    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_SUNPRO_VERSION)
+    #undef JSON_HEDLEY_SUNPRO_VERSION
+#endif
+#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)
+#elif defined(__SUNPRO_C)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)
+#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)
+#elif defined(__SUNPRO_CC)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)
+#endif
+
+#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)
+    #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_SUNPRO_VERSION)
+    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)
+    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION
+#endif
+#if defined(__EMSCRIPTEN__)
+    #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)
+#endif
+
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)
+    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)
+    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_ARM_VERSION)
+    #undef JSON_HEDLEY_ARM_VERSION
+#endif
+#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)
+    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)
+#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)
+    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)
+#endif
+
+#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)
+    #undef JSON_HEDLEY_ARM_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_ARM_VERSION)
+    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_IBM_VERSION)
+    #undef JSON_HEDLEY_IBM_VERSION
+#endif
+#if defined(__ibmxl__)
+    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)
+#elif defined(__xlC__) && defined(__xlC_ver__)
+    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)
+#elif defined(__xlC__)
+    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)
+#endif
+
+#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)
+    #undef JSON_HEDLEY_IBM_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_IBM_VERSION)
+    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_VERSION)
+    #undef JSON_HEDLEY_TI_VERSION
+#endif
+#if \
+    defined(__TI_COMPILER_VERSION__) && \
+    ( \
+      defined(__TMS470__) || defined(__TI_ARM__) || \
+      defined(__MSP430__) || \
+      defined(__TMS320C2000__) \
+    )
+#if (__TI_COMPILER_VERSION__ >= 16000000)
+    #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+#endif
+
+#if defined(JSON_HEDLEY_TI_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_VERSION)
+    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION)
+    #undef JSON_HEDLEY_TI_CL2000_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)
+    #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION)
+    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL430_VERSION)
+    #undef JSON_HEDLEY_TI_CL430_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)
+    #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL430_VERSION)
+    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)
+    #undef JSON_HEDLEY_TI_ARMCL_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))
+    #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)
+    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION)
+    #undef JSON_HEDLEY_TI_CL6X_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)
+    #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION)
+    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION)
+    #undef JSON_HEDLEY_TI_CL7X_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)
+    #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION)
+    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)
+    #undef JSON_HEDLEY_TI_CLPRU_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)
+    #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)
+    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_CRAY_VERSION)
+    #undef JSON_HEDLEY_CRAY_VERSION
+#endif
+#if defined(_CRAYC)
+    #if defined(_RELEASE_PATCHLEVEL)
+        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)
+    #else
+        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)
+    #endif
+#endif
+
+#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)
+    #undef JSON_HEDLEY_CRAY_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_CRAY_VERSION)
+    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_IAR_VERSION)
+    #undef JSON_HEDLEY_IAR_VERSION
+#endif
+#if defined(__IAR_SYSTEMS_ICC__)
+    #if __VER__ > 1000
+        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))
+    #else
+        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0)
+    #endif
+#endif
+
+#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)
+    #undef JSON_HEDLEY_IAR_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_IAR_VERSION)
+    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TINYC_VERSION)
+    #undef JSON_HEDLEY_TINYC_VERSION
+#endif
+#if defined(__TINYC__)
+    #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)
+#endif
+
+#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)
+    #undef JSON_HEDLEY_TINYC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TINYC_VERSION)
+    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_DMC_VERSION)
+    #undef JSON_HEDLEY_DMC_VERSION
+#endif
+#if defined(__DMC__)
+    #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)
+#endif
+
+#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)
+    #undef JSON_HEDLEY_DMC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_DMC_VERSION)
+    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_COMPCERT_VERSION)
+    #undef JSON_HEDLEY_COMPCERT_VERSION
+#endif
+#if defined(__COMPCERT_VERSION__)
+    #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)
+#endif
+
+#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)
+    #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_COMPCERT_VERSION)
+    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_PELLES_VERSION)
+    #undef JSON_HEDLEY_PELLES_VERSION
+#endif
+#if defined(__POCC__)
+    #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)
+    #undef JSON_HEDLEY_PELLES_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_PELLES_VERSION)
+    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+    #undef JSON_HEDLEY_MCST_LCC_VERSION
+#endif
+#if defined(__LCC__) && defined(__LCC_MINOR__)
+    #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK)
+    #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_VERSION)
+    #undef JSON_HEDLEY_GCC_VERSION
+#endif
+#if \
+    defined(JSON_HEDLEY_GNUC_VERSION) && \
+    !defined(__clang__) && \
+    !defined(JSON_HEDLEY_INTEL_VERSION) && \
+    !defined(JSON_HEDLEY_PGI_VERSION) && \
+    !defined(JSON_HEDLEY_ARM_VERSION) && \
+    !defined(JSON_HEDLEY_CRAY_VERSION) && \
+    !defined(JSON_HEDLEY_TI_VERSION) && \
+    !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL430_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \
+    !defined(__COMPCERT__) && \
+    !defined(JSON_HEDLEY_MCST_LCC_VERSION)
+    #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION
+#endif
+
+#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)
+    #undef JSON_HEDLEY_GCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_GCC_VERSION)
+    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_HAS_ATTRIBUTE
+#endif
+#if \
+  defined(__has_attribute) && \
+  ( \
+    (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \
+  )
+#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)
+#else
+#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
+#endif
+#if defined(__has_attribute)
+    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
+#endif
+#if defined(__has_attribute)
+    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+#else
+    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
+#endif
+#if \
+    defined(__has_cpp_attribute) && \
+    defined(__cplusplus) && \
+    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)
+#else
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)
+    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
+#endif
+#if !defined(__cplusplus) || !defined(__has_cpp_attribute)
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)
+#elif \
+    !defined(JSON_HEDLEY_PGI_VERSION) && \
+    !defined(JSON_HEDLEY_IAR_VERSION) && \
+    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \
+    (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)
+#else
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
+#endif
+#if defined(__has_cpp_attribute) && defined(__cplusplus)
+    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
+#endif
+#if defined(__has_cpp_attribute) && defined(__cplusplus)
+    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_BUILTIN)
+    #undef JSON_HEDLEY_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+    #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)
+#else
+    #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)
+    #undef JSON_HEDLEY_GNUC_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)
+    #undef JSON_HEDLEY_GCC_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
+#else
+    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_FEATURE)
+    #undef JSON_HEDLEY_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+    #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)
+#else
+    #define JSON_HEDLEY_HAS_FEATURE(feature) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)
+    #undef JSON_HEDLEY_GNUC_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)
+    #undef JSON_HEDLEY_GCC_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
+#else
+    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_EXTENSION)
+    #undef JSON_HEDLEY_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+    #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)
+#else
+    #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)
+    #undef JSON_HEDLEY_GNUC_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)
+    #undef JSON_HEDLEY_GCC_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
+#else
+    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)
+#else
+    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_WARNING)
+    #undef JSON_HEDLEY_HAS_WARNING
+#endif
+#if defined(__has_warning)
+    #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)
+#else
+    #define JSON_HEDLEY_HAS_WARNING(warning) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)
+    #undef JSON_HEDLEY_GNUC_HAS_WARNING
+#endif
+#if defined(__has_warning)
+    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_WARNING)
+    #undef JSON_HEDLEY_GCC_HAS_WARNING
+#endif
+#if defined(__has_warning)
+    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
+#else
+    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if \
+    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+    defined(__clang__) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \
+    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \
+    (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))
+    #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+    #define JSON_HEDLEY_PRAGMA(value) __pragma(value)
+#else
+    #define JSON_HEDLEY_PRAGMA(value)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)
+    #undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#endif
+#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)
+    #undef JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+#if defined(__clang__)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))
+    #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))
+#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop")
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH
+    #define JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+
+/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for
+   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
+#endif
+#if defined(__cplusplus)
+#  if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat")
+#    if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions")
+#      if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions")
+#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+    _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+    _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \
+    xpr \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#      else
+#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+    _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+    xpr \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#      endif
+#    else
+#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+    xpr \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#    endif
+#  endif
+#endif
+#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x
+#endif
+
+#if defined(JSON_HEDLEY_CONST_CAST)
+    #undef JSON_HEDLEY_CONST_CAST
+#endif
+#if defined(__cplusplus)
+#  define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))
+#elif \
+  JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+#  define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \
+        JSON_HEDLEY_DIAGNOSTIC_PUSH \
+        JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \
+        ((T) (expr)); \
+        JSON_HEDLEY_DIAGNOSTIC_POP \
+    }))
+#else
+#  define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_REINTERPRET_CAST)
+    #undef JSON_HEDLEY_REINTERPRET_CAST
+#endif
+#if defined(__cplusplus)
+    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))
+#else
+    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_STATIC_CAST)
+    #undef JSON_HEDLEY_STATIC_CAST
+#endif
+#if defined(__cplusplus)
+    #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))
+#else
+    #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_CPP_CAST)
+    #undef JSON_HEDLEY_CPP_CAST
+#endif
+#if defined(__cplusplus)
+#  if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast")
+#    define JSON_HEDLEY_CPP_CAST(T, expr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \
+    ((T) (expr)) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#  elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)
+#    define JSON_HEDLEY_CPP_CAST(T, expr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("diag_suppress=Pe137") \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#  else
+#    define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))
+#  endif
+#else
+#  define JSON_HEDLEY_CPP_CAST(T, expr) (expr)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445")
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292))
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098")
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)")
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunused-function")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+
+#if defined(JSON_HEDLEY_DEPRECATED)
+    #undef JSON_HEDLEY_DEPRECATED
+#endif
+#if defined(JSON_HEDLEY_DEPRECATED_FOR)
+    #undef JSON_HEDLEY_DEPRECATED_FOR
+#endif
+#if \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since))
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement))
+#elif \
+    (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since)))
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement)))
+#elif defined(__cplusplus) && (__cplusplus >= 201402L)
+    #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]])
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]])
+#elif \
+    JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated")
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated")
+#else
+    #define JSON_HEDLEY_DEPRECATED(since)
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)
+#endif
+
+#if defined(JSON_HEDLEY_UNAVAILABLE)
+    #undef JSON_HEDLEY_UNAVAILABLE
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since)))
+#else
+    #define JSON_HEDLEY_UNAVAILABLE(available_since)
+#endif
+
+#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)
+    #undef JSON_HEDLEY_WARN_UNUSED_RESULT
+#endif
+#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)
+    #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))
+#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+#elif defined(_Check_return_) /* SAL */
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_
+#else
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)
+#endif
+
+#if defined(JSON_HEDLEY_SENTINEL)
+    #undef JSON_HEDLEY_SENTINEL
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))
+#else
+    #define JSON_HEDLEY_SENTINEL(position)
+#endif
+
+#if defined(JSON_HEDLEY_NO_RETURN)
+    #undef JSON_HEDLEY_NO_RETURN
+#endif
+#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_NO_RETURN __noreturn
+#elif \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+    #define JSON_HEDLEY_NO_RETURN _Noreturn
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+    #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])
+#elif \
+    JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+    #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return")
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;")
+#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
+    #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)
+    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
+#else
+    #define JSON_HEDLEY_NO_RETURN
+#endif
+
+#if defined(JSON_HEDLEY_NO_ESCAPE)
+    #undef JSON_HEDLEY_NO_ESCAPE
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)
+    #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))
+#else
+    #define JSON_HEDLEY_NO_ESCAPE
+#endif
+
+#if defined(JSON_HEDLEY_UNREACHABLE)
+    #undef JSON_HEDLEY_UNREACHABLE
+#endif
+#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)
+    #undef JSON_HEDLEY_UNREACHABLE_RETURN
+#endif
+#if defined(JSON_HEDLEY_ASSUME)
+    #undef JSON_HEDLEY_ASSUME
+#endif
+#if \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_ASSUME(expr) __assume(expr)
+#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)
+    #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)
+#elif \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)
+    #if defined(__cplusplus)
+        #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)
+    #else
+        #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)
+    #endif
+#endif
+#if \
+    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \
+    JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()
+#elif defined(JSON_HEDLEY_ASSUME)
+    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
+#endif
+#if !defined(JSON_HEDLEY_ASSUME)
+    #if defined(JSON_HEDLEY_UNREACHABLE)
+        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))
+    #else
+        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)
+    #endif
+#endif
+#if defined(JSON_HEDLEY_UNREACHABLE)
+    #if  \
+        JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)
+        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))
+    #else
+        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()
+    #endif
+#else
+    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)
+#endif
+#if !defined(JSON_HEDLEY_UNREACHABLE)
+    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
+#endif
+
+JSON_HEDLEY_DIAGNOSTIC_PUSH
+#if JSON_HEDLEY_HAS_WARNING("-Wpedantic")
+    #pragma clang diagnostic ignored "-Wpedantic"
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus)
+    #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#endif
+#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0)
+    #if defined(__clang__)
+        #pragma clang diagnostic ignored "-Wvariadic-macros"
+    #elif defined(JSON_HEDLEY_GCC_VERSION)
+        #pragma GCC diagnostic ignored "-Wvariadic-macros"
+    #endif
+#endif
+#if defined(JSON_HEDLEY_NON_NULL)
+    #undef JSON_HEDLEY_NON_NULL
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
+    #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))
+#else
+    #define JSON_HEDLEY_NON_NULL(...)
+#endif
+JSON_HEDLEY_DIAGNOSTIC_POP
+
+#if defined(JSON_HEDLEY_PRINTF_FORMAT)
+    #undef JSON_HEDLEY_PRINTF_FORMAT
+#endif
+#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))
+#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))
+#elif \
+    JSON_HEDLEY_HAS_ATTRIBUTE(format) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))
+#else
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)
+#endif
+
+#if defined(JSON_HEDLEY_CONSTEXPR)
+    #undef JSON_HEDLEY_CONSTEXPR
+#endif
+#if defined(__cplusplus)
+    #if __cplusplus >= 201103L
+        #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)
+    #endif
+#endif
+#if !defined(JSON_HEDLEY_CONSTEXPR)
+    #define JSON_HEDLEY_CONSTEXPR
+#endif
+
+#if defined(JSON_HEDLEY_PREDICT)
+    #undef JSON_HEDLEY_PREDICT
+#endif
+#if defined(JSON_HEDLEY_LIKELY)
+    #undef JSON_HEDLEY_LIKELY
+#endif
+#if defined(JSON_HEDLEY_UNLIKELY)
+    #undef JSON_HEDLEY_UNLIKELY
+#endif
+#if defined(JSON_HEDLEY_UNPREDICTABLE)
+    #undef JSON_HEDLEY_UNPREDICTABLE
+#endif
+#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)
+    #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))
+#endif
+#if \
+  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#  define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(  (expr), (value), (probability))
+#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability)   __builtin_expect_with_probability(!!(expr),    1   , (probability))
+#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability)  __builtin_expect_with_probability(!!(expr),    0   , (probability))
+#  define JSON_HEDLEY_LIKELY(expr)                      __builtin_expect                 (!!(expr),    1                  )
+#  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )
+#elif \
+  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+  (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
+  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+  JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
+  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+  JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \
+  JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#  define JSON_HEDLEY_PREDICT(expr, expected, probability) \
+    (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))
+#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \
+    (__extension__ ({ \
+        double hedley_probability_ = (probability); \
+        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \
+    }))
+#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \
+    (__extension__ ({ \
+        double hedley_probability_ = (probability); \
+        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \
+    }))
+#  define JSON_HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)
+#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
+#else
+#  define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))
+#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))
+#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))
+#  define JSON_HEDLEY_LIKELY(expr) (!!(expr))
+#  define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))
+#endif
+#if !defined(JSON_HEDLEY_UNPREDICTABLE)
+    #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)
+#endif
+
+#if defined(JSON_HEDLEY_MALLOC)
+    #undef JSON_HEDLEY_MALLOC
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+    #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory")
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_MALLOC __declspec(restrict)
+#else
+    #define JSON_HEDLEY_MALLOC
+#endif
+
+#if defined(JSON_HEDLEY_PURE)
+    #undef JSON_HEDLEY_PURE
+#endif
+#if \
+  JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+  JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#  define JSON_HEDLEY_PURE __attribute__((__pure__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+#  define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data")
+#elif defined(__cplusplus) && \
+    ( \
+      JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \
+      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \
+    )
+#  define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;")
+#else
+#  define JSON_HEDLEY_PURE
+#endif
+
+#if defined(JSON_HEDLEY_CONST)
+    #undef JSON_HEDLEY_CONST
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(const) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_CONST __attribute__((__const__))
+#elif \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+    #define JSON_HEDLEY_CONST _Pragma("no_side_effect")
+#else
+    #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE
+#endif
+
+#if defined(JSON_HEDLEY_RESTRICT)
+    #undef JSON_HEDLEY_RESTRICT
+#endif
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)
+    #define JSON_HEDLEY_RESTRICT restrict
+#elif \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+    defined(__clang__) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_RESTRICT __restrict
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)
+    #define JSON_HEDLEY_RESTRICT _Restrict
+#else
+    #define JSON_HEDLEY_RESTRICT
+#endif
+
+#if defined(JSON_HEDLEY_INLINE)
+    #undef JSON_HEDLEY_INLINE
+#endif
+#if \
+    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+    (defined(__cplusplus) && (__cplusplus >= 199711L))
+    #define JSON_HEDLEY_INLINE inline
+#elif \
+    defined(JSON_HEDLEY_GCC_VERSION) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)
+    #define JSON_HEDLEY_INLINE __inline__
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_INLINE __inline
+#else
+    #define JSON_HEDLEY_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_ALWAYS_INLINE)
+    #undef JSON_HEDLEY_ALWAYS_INLINE
+#endif
+#if \
+  JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+  JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+#  define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE
+#elif \
+  JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+#  define JSON_HEDLEY_ALWAYS_INLINE __forceinline
+#elif defined(__cplusplus) && \
+    ( \
+      JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+      JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+      JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+      JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \
+    )
+#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced")
+#else
+#  define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_NEVER_INLINE)
+    #undef JSON_HEDLEY_NEVER_INLINE
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+    #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)
+    #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline")
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never")
+#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
+    #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)
+    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
+#else
+    #define JSON_HEDLEY_NEVER_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_PRIVATE)
+    #undef JSON_HEDLEY_PRIVATE
+#endif
+#if defined(JSON_HEDLEY_PUBLIC)
+    #undef JSON_HEDLEY_PUBLIC
+#endif
+#if defined(JSON_HEDLEY_IMPORT)
+    #undef JSON_HEDLEY_IMPORT
+#endif
+#if defined(_WIN32) || defined(__CYGWIN__)
+#  define JSON_HEDLEY_PRIVATE
+#  define JSON_HEDLEY_PUBLIC   __declspec(dllexport)
+#  define JSON_HEDLEY_IMPORT   __declspec(dllimport)
+#else
+#  if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+    ( \
+      defined(__TI_EABI__) && \
+      ( \
+        (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \
+      ) \
+    ) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#    define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden")))
+#    define JSON_HEDLEY_PUBLIC  __attribute__((__visibility__("default")))
+#  else
+#    define JSON_HEDLEY_PRIVATE
+#    define JSON_HEDLEY_PUBLIC
+#  endif
+#  define JSON_HEDLEY_IMPORT    extern
+#endif
+
+#if defined(JSON_HEDLEY_NO_THROW)
+    #undef JSON_HEDLEY_NO_THROW
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
+    #define JSON_HEDLEY_NO_THROW __declspec(nothrow)
+#else
+    #define JSON_HEDLEY_NO_THROW
+#endif
+
+#if defined(JSON_HEDLEY_FALL_THROUGH)
+    #undef JSON_HEDLEY_FALL_THROUGH
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)
+    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)
+    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])
+#elif defined(__fallthrough) /* SAL */
+    #define JSON_HEDLEY_FALL_THROUGH __fallthrough
+#else
+    #define JSON_HEDLEY_FALL_THROUGH
+#endif
+
+#if defined(JSON_HEDLEY_RETURNS_NON_NULL)
+    #undef JSON_HEDLEY_RETURNS_NON_NULL
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))
+#elif defined(_Ret_notnull_) /* SAL */
+    #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_
+#else
+    #define JSON_HEDLEY_RETURNS_NON_NULL
+#endif
+
+#if defined(JSON_HEDLEY_ARRAY_PARAM)
+    #undef JSON_HEDLEY_ARRAY_PARAM
+#endif
+#if \
+    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+    !defined(__STDC_NO_VLA__) && \
+    !defined(__cplusplus) && \
+    !defined(JSON_HEDLEY_PGI_VERSION) && \
+    !defined(JSON_HEDLEY_TINYC_VERSION)
+    #define JSON_HEDLEY_ARRAY_PARAM(name) (name)
+#else
+    #define JSON_HEDLEY_ARRAY_PARAM(name)
+#endif
+
+#if defined(JSON_HEDLEY_IS_CONSTANT)
+    #undef JSON_HEDLEY_IS_CONSTANT
+#endif
+#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)
+    #undef JSON_HEDLEY_REQUIRE_CONSTEXPR
+#endif
+/* JSON_HEDLEY_IS_CONSTEXPR_ is for
+   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */
+#if defined(JSON_HEDLEY_IS_CONSTEXPR_)
+    #undef JSON_HEDLEY_IS_CONSTEXPR_
+#endif
+#if \
+    JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \
+    JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)
+#endif
+#if !defined(__cplusplus)
+#  if \
+       JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \
+       JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+       JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+       JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+       JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+       JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+       JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)
+#if defined(__INTPTR_TYPE__)
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)
+#else
+    #include <stdint.h>
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)
+#endif
+#  elif \
+       ( \
+          defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \
+          !defined(JSON_HEDLEY_SUNPRO_VERSION) && \
+          !defined(JSON_HEDLEY_PGI_VERSION) && \
+          !defined(JSON_HEDLEY_IAR_VERSION)) || \
+       (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
+       JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+       JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \
+       JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
+       JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)
+#if defined(__INTPTR_TYPE__)
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)
+#else
+    #include <stdint.h>
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)
+#endif
+#  elif \
+       defined(JSON_HEDLEY_GCC_VERSION) || \
+       defined(JSON_HEDLEY_INTEL_VERSION) || \
+       defined(JSON_HEDLEY_TINYC_VERSION) || \
+       defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \
+       JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \
+       defined(JSON_HEDLEY_TI_CL2000_VERSION) || \
+       defined(JSON_HEDLEY_TI_CL6X_VERSION) || \
+       defined(JSON_HEDLEY_TI_CL7X_VERSION) || \
+       defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \
+       defined(__clang__)
+#    define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \
+        sizeof(void) != \
+        sizeof(*( \
+                  1 ? \
+                  ((void*) ((expr) * 0L) ) : \
+((struct { char v[sizeof(void) * 2]; } *) 1) \
+                ) \
+              ) \
+                                            )
+#  endif
+#endif
+#if defined(JSON_HEDLEY_IS_CONSTEXPR_)
+    #if !defined(JSON_HEDLEY_IS_CONSTANT)
+        #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)
+    #endif
+    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))
+#else
+    #if !defined(JSON_HEDLEY_IS_CONSTANT)
+        #define JSON_HEDLEY_IS_CONSTANT(expr) (0)
+    #endif
+    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)
+#endif
+
+#if defined(JSON_HEDLEY_BEGIN_C_DECLS)
+    #undef JSON_HEDLEY_BEGIN_C_DECLS
+#endif
+#if defined(JSON_HEDLEY_END_C_DECLS)
+    #undef JSON_HEDLEY_END_C_DECLS
+#endif
+#if defined(JSON_HEDLEY_C_DECL)
+    #undef JSON_HEDLEY_C_DECL
+#endif
+#if defined(__cplusplus)
+    #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" {
+    #define JSON_HEDLEY_END_C_DECLS }
+    #define JSON_HEDLEY_C_DECL extern "C"
+#else
+    #define JSON_HEDLEY_BEGIN_C_DECLS
+    #define JSON_HEDLEY_END_C_DECLS
+    #define JSON_HEDLEY_C_DECL
+#endif
+
+#if defined(JSON_HEDLEY_STATIC_ASSERT)
+    #undef JSON_HEDLEY_STATIC_ASSERT
+#endif
+#if \
+  !defined(__cplusplus) && ( \
+      (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \
+      (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
+      JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \
+      JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+      defined(_Static_assert) \
+    )
+#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)
+#elif \
+  (defined(__cplusplus) && (__cplusplus >= 201103L)) || \
+  JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \
+  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))
+#else
+#  define JSON_HEDLEY_STATIC_ASSERT(expr, message)
+#endif
+
+#if defined(JSON_HEDLEY_NULL)
+    #undef JSON_HEDLEY_NULL
+#endif
+#if defined(__cplusplus)
+    #if __cplusplus >= 201103L
+        #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)
+    #elif defined(NULL)
+        #define JSON_HEDLEY_NULL NULL
+    #else
+        #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)
+    #endif
+#elif defined(NULL)
+    #define JSON_HEDLEY_NULL NULL
+#else
+    #define JSON_HEDLEY_NULL ((void*) 0)
+#endif
+
+#if defined(JSON_HEDLEY_MESSAGE)
+    #undef JSON_HEDLEY_MESSAGE
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+#  define JSON_HEDLEY_MESSAGE(msg) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
+    JSON_HEDLEY_PRAGMA(message msg) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#elif \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)
+#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#else
+#  define JSON_HEDLEY_MESSAGE(msg)
+#endif
+
+#if defined(JSON_HEDLEY_WARNING)
+    #undef JSON_HEDLEY_WARNING
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+#  define JSON_HEDLEY_WARNING(msg) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
+    JSON_HEDLEY_PRAGMA(clang warning msg) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#elif \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \
+  JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)
+#elif \
+  JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#else
+#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)
+#endif
+
+#if defined(JSON_HEDLEY_REQUIRE)
+    #undef JSON_HEDLEY_REQUIRE
+#endif
+#if defined(JSON_HEDLEY_REQUIRE_MSG)
+    #undef JSON_HEDLEY_REQUIRE_MSG
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)
+#  if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat")
+#    define JSON_HEDLEY_REQUIRE(expr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+    __attribute__((diagnose_if(!(expr), #expr, "error"))) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+    __attribute__((diagnose_if(!(expr), msg, "error"))) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#  else
+#    define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error")))
+#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error")))
+#  endif
+#else
+#  define JSON_HEDLEY_REQUIRE(expr)
+#  define JSON_HEDLEY_REQUIRE_MSG(expr,msg)
+#endif
+
+#if defined(JSON_HEDLEY_FLAGS)
+    #undef JSON_HEDLEY_FLAGS
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion"))
+    #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))
+#else
+    #define JSON_HEDLEY_FLAGS
+#endif
+
+#if defined(JSON_HEDLEY_FLAGS_CAST)
+    #undef JSON_HEDLEY_FLAGS_CAST
+#endif
+#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)
+#  define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \
+        JSON_HEDLEY_DIAGNOSTIC_PUSH \
+        _Pragma("warning(disable:188)") \
+        ((T) (expr)); \
+        JSON_HEDLEY_DIAGNOSTIC_POP \
+    }))
+#else
+#  define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)
+#endif
+
+#if defined(JSON_HEDLEY_EMPTY_BASES)
+    #undef JSON_HEDLEY_EMPTY_BASES
+#endif
+#if \
+    (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)
+#else
+    #define JSON_HEDLEY_EMPTY_BASES
+#endif
+
+/* Remaining macros are deprecated. */
+
+#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)
+    #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
+#endif
+#if defined(__clang__)
+    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)
+#else
+    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)
+    #undef JSON_HEDLEY_CLANG_HAS_BUILTIN
+#endif
+#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)
+    #undef JSON_HEDLEY_CLANG_HAS_FEATURE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)
+    #undef JSON_HEDLEY_CLANG_HAS_EXTENSION
+#endif
+#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)
+    #undef JSON_HEDLEY_CLANG_HAS_WARNING
+#endif
+#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)
+
+#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/thirdparty/hedley/hedley_undef.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/thirdparty/hedley/hedley_undef.h
new file mode 100644
index 0000000..d0c58ff
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/json/include/wpi/thirdparty/hedley/hedley_undef.h
@@ -0,0 +1,158 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#undef JSON_HEDLEY_ALWAYS_INLINE
+#undef JSON_HEDLEY_ARM_VERSION
+#undef JSON_HEDLEY_ARM_VERSION_CHECK
+#undef JSON_HEDLEY_ARRAY_PARAM
+#undef JSON_HEDLEY_ASSUME
+#undef JSON_HEDLEY_BEGIN_C_DECLS
+#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_BUILTIN
+#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_EXTENSION
+#undef JSON_HEDLEY_CLANG_HAS_FEATURE
+#undef JSON_HEDLEY_CLANG_HAS_WARNING
+#undef JSON_HEDLEY_COMPCERT_VERSION
+#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
+#undef JSON_HEDLEY_CONCAT
+#undef JSON_HEDLEY_CONCAT3
+#undef JSON_HEDLEY_CONCAT3_EX
+#undef JSON_HEDLEY_CONCAT_EX
+#undef JSON_HEDLEY_CONST
+#undef JSON_HEDLEY_CONSTEXPR
+#undef JSON_HEDLEY_CONST_CAST
+#undef JSON_HEDLEY_CPP_CAST
+#undef JSON_HEDLEY_CRAY_VERSION
+#undef JSON_HEDLEY_CRAY_VERSION_CHECK
+#undef JSON_HEDLEY_C_DECL
+#undef JSON_HEDLEY_DEPRECATED
+#undef JSON_HEDLEY_DEPRECATED_FOR
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#undef JSON_HEDLEY_DIAGNOSTIC_POP
+#undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#undef JSON_HEDLEY_DMC_VERSION
+#undef JSON_HEDLEY_DMC_VERSION_CHECK
+#undef JSON_HEDLEY_EMPTY_BASES
+#undef JSON_HEDLEY_EMSCRIPTEN_VERSION
+#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
+#undef JSON_HEDLEY_END_C_DECLS
+#undef JSON_HEDLEY_FLAGS
+#undef JSON_HEDLEY_FLAGS_CAST
+#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_BUILTIN
+#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_EXTENSION
+#undef JSON_HEDLEY_GCC_HAS_FEATURE
+#undef JSON_HEDLEY_GCC_HAS_WARNING
+#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
+#undef JSON_HEDLEY_GCC_VERSION
+#undef JSON_HEDLEY_GCC_VERSION_CHECK
+#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_BUILTIN
+#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_EXTENSION
+#undef JSON_HEDLEY_GNUC_HAS_FEATURE
+#undef JSON_HEDLEY_GNUC_HAS_WARNING
+#undef JSON_HEDLEY_GNUC_VERSION
+#undef JSON_HEDLEY_GNUC_VERSION_CHECK
+#undef JSON_HEDLEY_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_BUILTIN
+#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
+#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_EXTENSION
+#undef JSON_HEDLEY_HAS_FEATURE
+#undef JSON_HEDLEY_HAS_WARNING
+#undef JSON_HEDLEY_IAR_VERSION
+#undef JSON_HEDLEY_IAR_VERSION_CHECK
+#undef JSON_HEDLEY_IBM_VERSION
+#undef JSON_HEDLEY_IBM_VERSION_CHECK
+#undef JSON_HEDLEY_IMPORT
+#undef JSON_HEDLEY_INLINE
+#undef JSON_HEDLEY_INTEL_CL_VERSION
+#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#undef JSON_HEDLEY_INTEL_VERSION
+#undef JSON_HEDLEY_INTEL_VERSION_CHECK
+#undef JSON_HEDLEY_IS_CONSTANT
+#undef JSON_HEDLEY_IS_CONSTEXPR_
+#undef JSON_HEDLEY_LIKELY
+#undef JSON_HEDLEY_MALLOC
+#undef JSON_HEDLEY_MCST_LCC_VERSION
+#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#undef JSON_HEDLEY_MESSAGE
+#undef JSON_HEDLEY_MSVC_VERSION
+#undef JSON_HEDLEY_MSVC_VERSION_CHECK
+#undef JSON_HEDLEY_NEVER_INLINE
+#undef JSON_HEDLEY_NON_NULL
+#undef JSON_HEDLEY_NO_ESCAPE
+#undef JSON_HEDLEY_NO_RETURN
+#undef JSON_HEDLEY_NO_THROW
+#undef JSON_HEDLEY_NULL
+#undef JSON_HEDLEY_PELLES_VERSION
+#undef JSON_HEDLEY_PELLES_VERSION_CHECK
+#undef JSON_HEDLEY_PGI_VERSION
+#undef JSON_HEDLEY_PGI_VERSION_CHECK
+#undef JSON_HEDLEY_PREDICT
+#undef JSON_HEDLEY_PRINTF_FORMAT
+#undef JSON_HEDLEY_PRIVATE
+#undef JSON_HEDLEY_PUBLIC
+#undef JSON_HEDLEY_PURE
+#undef JSON_HEDLEY_REINTERPRET_CAST
+#undef JSON_HEDLEY_REQUIRE
+#undef JSON_HEDLEY_REQUIRE_CONSTEXPR
+#undef JSON_HEDLEY_REQUIRE_MSG
+#undef JSON_HEDLEY_RESTRICT
+#undef JSON_HEDLEY_RETURNS_NON_NULL
+#undef JSON_HEDLEY_SENTINEL
+#undef JSON_HEDLEY_STATIC_ASSERT
+#undef JSON_HEDLEY_STATIC_CAST
+#undef JSON_HEDLEY_STRINGIFY
+#undef JSON_HEDLEY_STRINGIFY_EX
+#undef JSON_HEDLEY_SUNPRO_VERSION
+#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
+#undef JSON_HEDLEY_TINYC_VERSION
+#undef JSON_HEDLEY_TINYC_VERSION_CHECK
+#undef JSON_HEDLEY_TI_ARMCL_VERSION
+#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL2000_VERSION
+#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL430_VERSION
+#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL6X_VERSION
+#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL7X_VERSION
+#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CLPRU_VERSION
+#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
+#undef JSON_HEDLEY_TI_VERSION
+#undef JSON_HEDLEY_TI_VERSION_CHECK
+#undef JSON_HEDLEY_UNAVAILABLE
+#undef JSON_HEDLEY_UNLIKELY
+#undef JSON_HEDLEY_UNPREDICTABLE
+#undef JSON_HEDLEY_UNREACHABLE
+#undef JSON_HEDLEY_UNREACHABLE_RETURN
+#undef JSON_HEDLEY_VERSION
+#undef JSON_HEDLEY_VERSION_DECODE_MAJOR
+#undef JSON_HEDLEY_VERSION_DECODE_MINOR
+#undef JSON_HEDLEY_VERSION_DECODE_REVISION
+#undef JSON_HEDLEY_VERSION_ENCODE
+#undef JSON_HEDLEY_WARNING
+#undef JSON_HEDLEY_WARN_UNUSED_RESULT
+#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
+#undef JSON_HEDLEY_FALL_THROUGH
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/ConvertUTF.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/ConvertUTF.cpp
index dbf41ab..d9aed15 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/ConvertUTF.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/ConvertUTF.cpp
@@ -6,25 +6,41 @@
  *
  *===------------------------------------------------------------------------=*/
 /*
- * Copyright 2001-2004 Unicode, Inc.
+ * Copyright © 1991-2015 Unicode, Inc. All rights reserved.
+ * Distributed under the Terms of Use in
+ * http://www.unicode.org/copyright.html.
  *
- * Disclaimer
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of the Unicode data files and any associated documentation
+ * (the "Data Files") or Unicode software and any associated documentation
+ * (the "Software") to deal in the Data Files or Software
+ * without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, and/or sell copies of
+ * the Data Files or Software, and to permit persons to whom the Data Files
+ * or Software are furnished to do so, provided that
+ * (a) this copyright and permission notice appear with all copies
+ * of the Data Files or Software,
+ * (b) this copyright and permission notice appear in associated
+ * documentation, and
+ * (c) there is clear notice in each modified Data File or in the Software
+ * as well as in the documentation associated with the Data File(s) or
+ * Software that the data or software has been modified.
  *
- * 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.
+ * THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+ * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+ * DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
  *
- * 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.
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale,
+ * use or other dealings in these Data Files or Software without prior
+ * written authorization of the copyright holder.
  */
 
 /* ---------------------------------------------------------------------
@@ -422,6 +438,16 @@
     return isLegalUTF8(source, length);
 }
 
+/*
+ * Exported function to return the size of the first utf-8 code unit sequence,
+ * Or 0 if the sequence is not valid;
+ */
+unsigned getUTF8SequenceSize(const UTF8 *source, const UTF8 *sourceEnd) {
+  int length = trailingBytesForUTF8[*source] + 1;
+  return (length <= sourceEnd - source && isLegalUTF8(source, length)) ? length
+                                                                       : 0;
+}
+
 /* --------------------------------------------------------------------- */
 
 static unsigned
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/ConvertUTFWrapper.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/ConvertUTFWrapper.cpp
index e95c04f..10267ef 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/ConvertUTFWrapper.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/ConvertUTFWrapper.cpp
@@ -6,11 +6,11 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <span>
 #include "wpi/ConvertUTF.h"
 #include "wpi/SmallVector.h"
 #include "wpi/ErrorHandling.h"
 #include "wpi/SwapByteOrder.h"
+#include <span>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -35,31 +35,31 @@
     const UTF8 *sourceStart = (const UTF8*)Source.data();
     // FIXME: Make the type of the result buffer correct instead of
     // using reinterpret_cast.
-    UTF16 *targetStart = reinterpret_cast<UTF16*>(ResultPtr);
+    UTF16 *targetStart = reinterpret_cast<UTF16 *>(ResultPtr);
     ConversionFlags flags = strictConversion;
-    result = ConvertUTF8toUTF16(
-        &sourceStart, sourceStart + Source.size(),
-        &targetStart, targetStart + Source.size(), flags);
+    result =
+        ConvertUTF8toUTF16(&sourceStart, sourceStart + Source.size(),
+                           &targetStart, targetStart + Source.size(), flags);
     if (result == conversionOK)
-      ResultPtr = reinterpret_cast<char*>(targetStart);
+      ResultPtr = reinterpret_cast<char *>(targetStart);
     else
       ErrorPtr = sourceStart;
   } else if (WideCharWidth == 4) {
-    const UTF8 *sourceStart = (const UTF8*)Source.data();
+    const UTF8 *sourceStart = (const UTF8 *)Source.data();
     // FIXME: Make the type of the result buffer correct instead of
     // using reinterpret_cast.
-    UTF32 *targetStart = reinterpret_cast<UTF32*>(ResultPtr);
+    UTF32 *targetStart = reinterpret_cast<UTF32 *>(ResultPtr);
     ConversionFlags flags = strictConversion;
-    result = ConvertUTF8toUTF32(
-        &sourceStart, sourceStart + Source.size(),
-        &targetStart, targetStart + Source.size(), flags);
+    result =
+        ConvertUTF8toUTF32(&sourceStart, sourceStart + Source.size(),
+                           &targetStart, targetStart + Source.size(), flags);
     if (result == conversionOK)
-      ResultPtr = reinterpret_cast<char*>(targetStart);
+      ResultPtr = reinterpret_cast<char *>(targetStart);
     else
       ErrorPtr = sourceStart;
   }
-  assert((result != targetExhausted)
-         && "ConvertUTF8toUTFXX exhausted target buffer");
+  assert((result != targetExhausted) &&
+         "ConvertUTF8toUTFXX exhausted target buffer");
   return result == conversionOK;
 }
 
@@ -68,20 +68,18 @@
   const UTF32 *SourceEnd = SourceStart + 1;
   UTF8 *TargetStart = reinterpret_cast<UTF8 *>(ResultPtr);
   UTF8 *TargetEnd = TargetStart + 4;
-  ConversionResult CR = ConvertUTF32toUTF8(&SourceStart, SourceEnd,
-                                           &TargetStart, TargetEnd,
-                                           strictConversion);
+  ConversionResult CR = ConvertUTF32toUTF8(
+      &SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion);
   if (CR != conversionOK)
     return false;
 
-  ResultPtr = reinterpret_cast<char*>(TargetStart);
+  ResultPtr = reinterpret_cast<char *>(TargetStart);
   return true;
 }
 
 bool hasUTF16ByteOrderMark(std::span<const char> S) {
-  return (S.size() >= 2 &&
-          ((S[0] == '\xff' && S[1] == '\xfe') ||
-           (S[0] == '\xfe' && S[1] == '\xff')));
+  return (S.size() >= 2 && ((S[0] == '\xff' && S[1] == '\xfe') ||
+                            (S[0] == '\xfe' && S[1] == '\xff')));
 }
 
 bool convertUTF16ToUTF8String(std::span<const char> SrcBytes, SmallVectorImpl<char> &Out) {
@@ -105,7 +103,7 @@
   if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_SWAPPED) {
     ByteSwapped.insert(ByteSwapped.end(), Src, SrcEnd);
     for (UTF16 &I : ByteSwapped)
-      I = wpi::ByteSwap_16(I);
+      I = wpi::byteswap<uint16_t>(I);
     Src = &ByteSwapped[0];
     SrcEnd = &ByteSwapped[ByteSwapped.size() - 1] + 1;
   }
@@ -135,11 +133,69 @@
   return true;
 }
 
-bool convertUTF16ToUTF8String(std::span<const UTF16> Src, SmallVectorImpl<char> &Out)
-{
+bool convertUTF16ToUTF8String(std::span<const UTF16> Src, SmallVectorImpl<char> &Out) {
   return convertUTF16ToUTF8String(
       std::span<const char>(reinterpret_cast<const char *>(Src.data()),
-      Src.size() * sizeof(UTF16)), Out);
+                           Src.size() * sizeof(UTF16)),
+      Out);
+}
+
+bool convertUTF32ToUTF8String(std::span<const char> SrcBytes, std::string &Out) {
+  assert(Out.empty());
+
+  // Error out on an uneven byte count.
+  if (SrcBytes.size() % 4)
+    return false;
+
+  // Avoid OOB by returning early on empty input.
+  if (SrcBytes.empty())
+    return true;
+
+  const UTF32 *Src = reinterpret_cast<const UTF32 *>(&*SrcBytes.begin());
+  const UTF32 *SrcEnd = reinterpret_cast<const UTF32 *>(&*SrcBytes.begin() + SrcBytes.size());
+
+  assert((uintptr_t)Src % sizeof(UTF32) == 0);
+
+  // Byteswap if necessary.
+  std::vector<UTF32> ByteSwapped;
+  if (Src[0] == UNI_UTF32_BYTE_ORDER_MARK_SWAPPED) {
+    ByteSwapped.insert(ByteSwapped.end(), Src, SrcEnd);
+    for (UTF32 &I : ByteSwapped)
+      I = wpi::byteswap<uint32_t>(I);
+    Src = &ByteSwapped[0];
+    SrcEnd = &ByteSwapped[ByteSwapped.size() - 1] + 1;
+  }
+
+  // Skip the BOM for conversion.
+  if (Src[0] == UNI_UTF32_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.
+  Out.resize(SrcBytes.size() * UNI_MAX_UTF8_BYTES_PER_CODE_POINT + 1);
+  UTF8 *Dst = reinterpret_cast<UTF8 *>(&Out[0]);
+  UTF8 *DstEnd = Dst + Out.size();
+
+  ConversionResult CR =
+      ConvertUTF32toUTF8(&Src, SrcEnd, &Dst, DstEnd, strictConversion);
+  assert(CR != targetExhausted);
+
+  if (CR != conversionOK) {
+    Out.clear();
+    return false;
+  }
+
+  Out.resize(reinterpret_cast<char *>(Dst) - &Out[0]);
+  Out.push_back(0);
+  Out.pop_back();
+  return true;
+}
+
+bool convertUTF32ToUTF8String(std::span<const UTF32> Src, std::string &Out) {
+  return convertUTF32ToUTF8String(
+      std::span<const char>(reinterpret_cast<const char *>(Src.data()),
+                           Src.size() * sizeof(UTF32)),
+      Out);
 }
 
 bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/MemoryBuffer.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/MemoryBuffer.cpp
index 5beeb38..72cdf09 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/MemoryBuffer.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/MemoryBuffer.cpp
@@ -237,14 +237,17 @@
 #endif
   // Read into Buffer until we hit EOF.
   do {
-    buffer.resize_for_overwrite(buffer.size() + ChunkSize);
+    size_t prevSize = buffer.size();
+    buffer.resize_for_overwrite(prevSize + ChunkSize);
 #ifdef _WIN32
-    if (!ReadFile(f, buffer.end(), ChunkSize, &readBytes, nullptr)) {
+    if (!ReadFile(f, buffer.begin() + prevSize, ChunkSize, &readBytes,
+                  nullptr)) {
       ec = mapWindowsError(GetLastError());
       return nullptr;
     }
 #else
-    readBytes = sys::RetryAfterSignal(-1, ::read, f, buffer.end(), ChunkSize);
+    readBytes = sys::RetryAfterSignal(-1, ::read, f, buffer.begin() + prevSize,
+                                      ChunkSize);
     if (readBytes == -1) {
       ec = std::error_code(errno, std::generic_category());
       return nullptr;
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/SmallVector.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/SmallVector.cpp
index 04f4e06..803523a 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/SmallVector.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/SmallVector.cpp
@@ -99,14 +99,30 @@
   // In theory 2*capacity can overflow if the capacity is 64 bit, but the
   // original capacity would never be large enough for this to be a problem.
   size_t NewCapacity = 2 * OldCapacity + 1; // Always grow.
-  return (std::min)((std::max)(NewCapacity, MinSize), MaxSize);
+  return std::clamp(NewCapacity, MinSize, MaxSize);
+}
+
+void *SmallVectorBase::replaceAllocation(void *NewElts, size_t TSize,
+                                                 size_t NewCapacity,
+                                                 size_t VSize) {
+  void *NewEltsReplace = wpi::safe_malloc(NewCapacity * TSize);
+  if (VSize)
+    memcpy(NewEltsReplace, NewElts, VSize * TSize);
+  free(NewElts);
+  return NewEltsReplace;
 }
 
 // Note: Moving this function into the header may cause performance regression.
-void *SmallVectorBase::mallocForGrow(size_t MinSize, size_t TSize,
+void *SmallVectorBase::mallocForGrow(void *FirstEl, size_t MinSize,
+                                             size_t TSize,
                                              size_t &NewCapacity) {
   NewCapacity = getNewCapacity(MinSize, TSize, this->capacity());
-  return wpi::safe_malloc(NewCapacity * TSize);
+  // Even if capacity is not 0 now, if the vector was originally created with
+  // capacity 0, it's possible for the malloc to return FirstEl.
+  void *NewElts = wpi::safe_malloc(NewCapacity * TSize);
+  if (NewElts == FirstEl)
+    NewElts = replaceAllocation(NewElts, TSize, NewCapacity);
+  return NewElts;
 }
 
 // Note: Moving this function into the header may cause performance regression.
@@ -115,13 +131,17 @@
   size_t NewCapacity = getNewCapacity(MinSize, TSize, this->capacity());
   void *NewElts;
   if (BeginX == FirstEl) {
-    NewElts = safe_malloc(NewCapacity * TSize);
+    NewElts = wpi::safe_malloc(NewCapacity * TSize);
+    if (NewElts == FirstEl)
+      NewElts = replaceAllocation(NewElts, TSize, NewCapacity);
 
     // 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);
+    NewElts = wpi::safe_realloc(this->BeginX, NewCapacity * TSize);
+    if (NewElts == FirstEl)
+      NewElts = replaceAllocation(NewElts, TSize, NewCapacity, size());
   }
 
   this->BeginX = NewElts;
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/StringMap.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/StringMap.cpp
index 2405f2f..de41aea 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/StringMap.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/StringMap.cpp
@@ -11,14 +11,15 @@
 //===----------------------------------------------------------------------===//
 
 #include "wpi/StringMap.h"
-#include "wpi/DJB.h"
 #include "wpi/MathExtras.h"
+#include "wpi/ReverseIteration.h"
+#include "wpi/xxhash.h"
 
 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) {
+static inline unsigned getMinBucketToReserveForEntries(unsigned NumEntries) {
   // Ensure that "NumEntries * 4 < NumBuckets * 3"
   if (NumEntries == 0)
     return 0;
@@ -27,6 +28,21 @@
   return NextPowerOf2(NumEntries * 4 / 3 + 1);
 }
 
+static inline StringMapEntryBase **createTable(unsigned NewNumBuckets) {
+  auto **Table = static_cast<StringMapEntryBase **>(safe_calloc(
+      NewNumBuckets + 1, sizeof(StringMapEntryBase **) + sizeof(unsigned)));
+
+  // Allocate one extra bucket, set it to look filled so the iterators stop at
+  // end.
+  Table[NewNumBuckets] = (StringMapEntryBase *)2;
+  return Table;
+}
+
+static inline unsigned *getHashTable(StringMapEntryBase **TheTable,
+                                     unsigned NumBuckets) {
+  return reinterpret_cast<unsigned *>(TheTable + NumBuckets + 1);
+}
+
 StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) {
   ItemSize = itemSize;
 
@@ -54,15 +70,10 @@
   NumItems = 0;
   NumTombstones = 0;
 
-  TheTable = static_cast<StringMapEntryBase **>(safe_calloc(
-      NewNumBuckets + 1, sizeof(StringMapEntryBase **) + sizeof(unsigned)));
+  TheTable = createTable(NewNumBuckets);
 
   // 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
@@ -71,14 +82,14 @@
 /// case, the FullHashValue field of the bucket will be set to the hash value
 /// of the string.
 unsigned StringMapImpl::LookupBucketFor(std::string_view Name) {
-  unsigned HTSize = NumBuckets;
-  if (HTSize == 0) { // Hash table unallocated so far?
+  // Hash table unallocated so far?
+  if (NumBuckets == 0)
     init(16);
-    HTSize = NumBuckets;
-  }
-  unsigned FullHashValue = djbHash(Name, 0);
-  unsigned BucketNo = FullHashValue & (HTSize - 1);
-  unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
+  unsigned FullHashValue = xxh3_64bits(Name);
+  if (shouldReverseIterate())
+    FullHashValue = ~FullHashValue;
+  unsigned BucketNo = FullHashValue & (NumBuckets - 1);
+  unsigned *HashTable = getHashTable(TheTable, NumBuckets);
 
   unsigned ProbeAmt = 1;
   int FirstTombstone = -1;
@@ -117,7 +128,7 @@
     }
 
     // Okay, we didn't find the item.  Probe to the next bucket.
-    BucketNo = (BucketNo + ProbeAmt) & (HTSize - 1);
+    BucketNo = (BucketNo + ProbeAmt) & (NumBuckets - 1);
 
     // Use quadratic probing, it has fewer clumping artifacts than linear
     // probing and has good cache behavior in the common case.
@@ -129,12 +140,13 @@
 /// in the map, return the bucket number of the key.  Otherwise return -1.
 /// This does not modify the map.
 int StringMapImpl::FindKey(std::string_view Key) const {
-  unsigned HTSize = NumBuckets;
-  if (HTSize == 0)
+  if (NumBuckets == 0)
     return -1; // Really empty table?
-  unsigned FullHashValue = djbHash(Key, 0);
-  unsigned BucketNo = FullHashValue & (HTSize - 1);
-  unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
+  unsigned FullHashValue = xxh3_64bits(Key);
+  if (shouldReverseIterate())
+    FullHashValue = ~FullHashValue;
+  unsigned BucketNo = FullHashValue & (NumBuckets - 1);
+  unsigned *HashTable = getHashTable(TheTable, NumBuckets);
 
   unsigned ProbeAmt = 1;
   while (true) {
@@ -161,7 +173,7 @@
     }
 
     // Okay, we didn't find the item.  Probe to the next bucket.
-    BucketNo = (BucketNo + ProbeAmt) & (HTSize - 1);
+    BucketNo = (BucketNo + ProbeAmt) & (NumBuckets - 1);
 
     // Use quadratic probing, it has fewer clumping artifacts than linear
     // probing and has good cache behavior in the common case.
@@ -198,8 +210,6 @@
 /// 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.
@@ -213,36 +223,25 @@
   }
 
   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;
+  auto **NewTableArray = createTable(NewSize);
+  unsigned *NewHashArray = getHashTable(NewTableArray, NewSize);
+  unsigned *HashTable = getHashTable(TheTable, NumBuckets);
 
   // 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.
+      // If the bucket is not available, probe for a spot.
       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;
+      if (NewTableArray[NewBucket]) {
+        unsigned ProbeSize = 1;
+        do {
+          NewBucket = (NewBucket + ProbeSize++) & (NewSize - 1);
+        } while (NewTableArray[NewBucket]);
       }
 
-      // 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;
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/Windows/WindowsSupport.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/Windows/WindowsSupport.h
index 01200e2..735ce48 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/Windows/WindowsSupport.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/Windows/WindowsSupport.h
@@ -83,6 +83,9 @@
   return GetWindowsOSVersion() >= wpi::VersionTuple(6, 2, 0, 0);
 }
 
+/// Determines if the program is running on Windows 11 or Windows Server 2022.
+bool RunningWindows11OrGreater();
+
 /// 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
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/raw_ostream.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/raw_ostream.cpp
index a2d586b..05036a8 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/raw_ostream.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/raw_ostream.cpp
@@ -89,8 +89,15 @@
 }
 
 size_t raw_ostream::preferred_buffer_size() const {
+#ifdef _WIN32
+  // On Windows BUFSIZ is only 512 which results in more calls to write. This
+  // overhead can cause significant performance degradation. Therefore use a
+  // better default.
+  return (16 * 1024);
+#else
   // BUFSIZ is intended to be a reasonable default.
   return BUFSIZ;
+#endif
 }
 
 void raw_ostream::SetBuffered() {
@@ -237,10 +244,10 @@
   // 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 4: OutBufCur[3] = Ptr[3]; [[fallthrough]];
+  case 3: OutBufCur[2] = Ptr[2]; [[fallthrough]];
+  case 2: OutBufCur[1] = Ptr[1]; [[fallthrough]];
+  case 1: OutBufCur[0] = Ptr[0]; [[fallthrough]];
   case 0: break;
   default:
     memcpy(OutBufCur, Ptr, Size);
@@ -256,7 +263,6 @@
   write_impl(Ptr, Size);
 }
 
-
 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,
@@ -266,12 +272,11 @@
                                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))
+  if (NumChars < std::size(Chars))
     return OS.write(Chars, NumChars);
 
   while (NumChars) {
-    unsigned NumToWrite = std::min(NumChars,
-                                   (unsigned)array_lengthof(Chars)-1);
+    unsigned NumToWrite = std::min(NumChars, (unsigned)std::size(Chars) - 1);
     OS.write(Chars, NumToWrite);
     NumChars -= NumToWrite;
   }
@@ -512,6 +517,14 @@
           )
         continue;
 
+#ifdef _WIN32
+      // Windows equivalents of SIGPIPE/EPIPE.
+      DWORD WinLastError = GetLastError();
+      if (WinLastError == ERROR_BROKEN_PIPE ||
+          (WinLastError == ERROR_NO_DATA && errno == EINVAL)) {
+        errno = EPIPE;
+      }
+#endif
       // Otherwise it's a non-recoverable error. Note it and quit.
       error_detected(std::error_code(errno, std::generic_category()));
       break;
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/xxhash.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/xxhash.cpp
new file mode 100644
index 0000000..323e835
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/xxhash.cpp
@@ -0,0 +1,407 @@
+/*
+*  xxHash - Fast Hash algorithm
+*  Copyright (C) 2012-2021, Yann Collet
+*
+*  BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+*
+*  Redistribution and use in source and binary forms, with or without
+*  modification, are permitted provided that the following conditions are
+*  met:
+*
+*  * Redistributions of source code must retain the above copyright
+*  notice, this list of conditions and the following disclaimer.
+*  * 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.
+*
+*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+*  "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+*  OWNER OR 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.
+*
+*  You can contact the author at :
+*  - xxHash homepage: http://www.xxhash.com
+*  - xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+// xxhash64 is based on commit d2df04efcbef7d7f6886d345861e5dfda4edacc1. Removed
+// everything but a simple interface for computing xxh64.
+
+// xxh3_64bits is based on commit d5891596637d21366b9b1dcf2c0007a3edb26a9e (July
+// 2023).
+
+#include "wpi/xxhash.h"
+#include "wpi/Compiler.h"
+#include "wpi/Endian.h"
+
+#include <stdlib.h>
+
+using namespace wpi;
+using namespace support;
+
+static uint64_t rotl64(uint64_t X, size_t R) {
+  return (X << R) | (X >> (64 - R));
+}
+
+constexpr uint32_t PRIME32_1 = 0x9E3779B1;
+constexpr uint32_t PRIME32_2 = 0x85EBCA77;
+constexpr uint32_t PRIME32_3 = 0xC2B2AE3D;
+
+static const uint64_t PRIME64_1 = 11400714785074694791ULL;
+static const uint64_t PRIME64_2 = 14029467366897019727ULL;
+static const uint64_t PRIME64_3 = 1609587929392839161ULL;
+static const uint64_t PRIME64_4 = 9650029242287828579ULL;
+static const uint64_t PRIME64_5 = 2870177450012600261ULL;
+
+static uint64_t round(uint64_t Acc, uint64_t Input) {
+  Acc += Input * PRIME64_2;
+  Acc = rotl64(Acc, 31);
+  Acc *= PRIME64_1;
+  return Acc;
+}
+
+static uint64_t mergeRound(uint64_t Acc, uint64_t Val) {
+  Val = round(0, Val);
+  Acc ^= Val;
+  Acc = Acc * PRIME64_1 + PRIME64_4;
+  return Acc;
+}
+
+static uint64_t XXH64_avalanche(uint64_t hash) {
+  hash ^= hash >> 33;
+  hash *= PRIME64_2;
+  hash ^= hash >> 29;
+  hash *= PRIME64_3;
+  hash ^= hash >> 32;
+  return hash;
+}
+
+uint64_t wpi::xxHash64(std::string_view Data) {
+  size_t Len = Data.size();
+  uint64_t Seed = 0;
+  const unsigned char *P = reinterpret_cast<const unsigned char*>(Data.data());
+  const unsigned char *const BEnd = P + Data.size();
+  uint64_t H64;
+
+  if (Len >= 32) {
+    const unsigned char *const Limit = BEnd - 32;
+    uint64_t V1 = Seed + PRIME64_1 + PRIME64_2;
+    uint64_t V2 = Seed + PRIME64_2;
+    uint64_t V3 = Seed + 0;
+    uint64_t V4 = Seed - PRIME64_1;
+
+    do {
+      V1 = round(V1, endian::read64le(P));
+      P += 8;
+      V2 = round(V2, endian::read64le(P));
+      P += 8;
+      V3 = round(V3, endian::read64le(P));
+      P += 8;
+      V4 = round(V4, endian::read64le(P));
+      P += 8;
+    } while (P <= Limit);
+
+    H64 = rotl64(V1, 1) + rotl64(V2, 7) + rotl64(V3, 12) + rotl64(V4, 18);
+    H64 = mergeRound(H64, V1);
+    H64 = mergeRound(H64, V2);
+    H64 = mergeRound(H64, V3);
+    H64 = mergeRound(H64, V4);
+
+  } else {
+    H64 = Seed + PRIME64_5;
+  }
+
+  H64 += (uint64_t)Len;
+
+  while (reinterpret_cast<uintptr_t>(P) + 8 <=
+         reinterpret_cast<uintptr_t>(BEnd)) {
+    uint64_t const K1 = round(0, endian::read64le(P));
+    H64 ^= K1;
+    H64 = rotl64(H64, 27) * PRIME64_1 + PRIME64_4;
+    P += 8;
+  }
+
+  if (reinterpret_cast<uintptr_t>(P) + 4 <= reinterpret_cast<uintptr_t>(BEnd)) {
+    H64 ^= (uint64_t)(endian::read32le(P)) * PRIME64_1;
+    H64 = rotl64(H64, 23) * PRIME64_2 + PRIME64_3;
+    P += 4;
+  }
+
+  while (P < BEnd) {
+    H64 ^= (*P) * PRIME64_5;
+    H64 = rotl64(H64, 11) * PRIME64_1;
+    P++;
+  }
+
+  return XXH64_avalanche(H64);
+}
+
+uint64_t wpi::xxHash64(std::span<const uint8_t> Data) {
+  return xxHash64({(const char *)Data.data(), Data.size()});
+}
+
+constexpr size_t XXH3_SECRETSIZE_MIN = 136;
+constexpr size_t XXH_SECRET_DEFAULT_SIZE = 192;
+
+/* Pseudorandom data taken directly from FARSH */
+// clang-format off
+constexpr uint8_t kSecret[XXH_SECRET_DEFAULT_SIZE] = {
+    0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c,
+    0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f,
+    0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
+    0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c,
+    0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3,
+    0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
+    0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d,
+    0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64,
+    0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
+    0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e,
+    0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce,
+    0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
+};
+// clang-format on
+
+constexpr uint64_t PRIME_MX1 = 0x165667919E3779F9;
+constexpr uint64_t PRIME_MX2 = 0x9FB21C651E98DF25;
+
+// Calculates a 64-bit to 128-bit multiply, then XOR folds it.
+static uint64_t XXH3_mul128_fold64(uint64_t lhs, uint64_t rhs) {
+#if defined(__SIZEOF_INT128__) ||                                              \
+    (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128)
+  __uint128_t product = (__uint128_t)lhs * (__uint128_t)rhs;
+  return uint64_t(product) ^ uint64_t(product >> 64);
+
+#else
+  /* First calculate all of the cross products. */
+  const uint64_t lo_lo = (lhs & 0xFFFFFFFF) * (rhs & 0xFFFFFFFF);
+  const uint64_t hi_lo = (lhs >> 32) * (rhs & 0xFFFFFFFF);
+  const uint64_t lo_hi = (lhs & 0xFFFFFFFF) * (rhs >> 32);
+  const uint64_t hi_hi = (lhs >> 32) * (rhs >> 32);
+
+  /* Now add the products together. These will never overflow. */
+  const uint64_t cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi;
+  const uint64_t upper = (hi_lo >> 32) + (cross >> 32) + hi_hi;
+  const uint64_t lower = (cross << 32) | (lo_lo & 0xFFFFFFFF);
+
+  return upper ^ lower;
+#endif
+}
+
+constexpr size_t XXH_STRIPE_LEN = 64;
+constexpr size_t XXH_SECRET_CONSUME_RATE = 8;
+constexpr size_t XXH_ACC_NB = XXH_STRIPE_LEN / sizeof(uint64_t);
+
+static uint64_t XXH3_avalanche(uint64_t hash) {
+  hash ^= hash >> 37;
+  hash *= PRIME_MX1;
+  hash ^= hash >> 32;
+  return hash;
+}
+
+static uint64_t XXH3_len_1to3_64b(const uint8_t *input, size_t len,
+                                  const uint8_t *secret, uint64_t seed) {
+  const uint8_t c1 = input[0];
+  const uint8_t c2 = input[len >> 1];
+  const uint8_t c3 = input[len - 1];
+  uint32_t combined = ((uint32_t)c1 << 16) | ((uint32_t)c2 << 24) |
+                      ((uint32_t)c3 << 0) | ((uint32_t)len << 8);
+  uint64_t bitflip =
+      (uint64_t)(endian::read32le(secret) ^ endian::read32le(secret + 4)) +
+      seed;
+  return XXH64_avalanche(uint64_t(combined) ^ bitflip);
+}
+
+static uint64_t XXH3_len_4to8_64b(const uint8_t *input, size_t len,
+                                  const uint8_t *secret, uint64_t seed) {
+  seed ^= (uint64_t)byteswap(uint32_t(seed)) << 32;
+  const uint32_t input1 = endian::read32le(input);
+  const uint32_t input2 = endian::read32le(input + len - 4);
+  uint64_t acc =
+      (endian::read64le(secret + 8) ^ endian::read64le(secret + 16)) - seed;
+  const uint64_t input64 = (uint64_t)input2 | ((uint64_t)input1 << 32);
+  acc ^= input64;
+  // XXH3_rrmxmx(acc, len)
+  acc ^= rotl64(acc, 49) ^ rotl64(acc, 24);
+  acc *= PRIME_MX2;
+  acc ^= (acc >> 35) + (uint64_t)len;
+  acc *= PRIME_MX2;
+  return acc ^ (acc >> 28);
+}
+
+static uint64_t XXH3_len_9to16_64b(const uint8_t *input, size_t len,
+                                   const uint8_t *secret, uint64_t const seed) {
+  uint64_t input_lo =
+      (endian::read64le(secret + 24) ^ endian::read64le(secret + 32)) + seed;
+  uint64_t input_hi =
+      (endian::read64le(secret + 40) ^ endian::read64le(secret + 48)) - seed;
+  input_lo ^= endian::read64le(input);
+  input_hi ^= endian::read64le(input + len - 8);
+  uint64_t acc = uint64_t(len) + byteswap(input_lo) + input_hi +
+                 XXH3_mul128_fold64(input_lo, input_hi);
+  return XXH3_avalanche(acc);
+}
+
+LLVM_ATTRIBUTE_ALWAYS_INLINE
+static uint64_t XXH3_len_0to16_64b(const uint8_t *input, size_t len,
+                                   const uint8_t *secret, uint64_t const seed) {
+  if (LLVM_LIKELY(len > 8))
+    return XXH3_len_9to16_64b(input, len, secret, seed);
+  if (LLVM_LIKELY(len >= 4))
+    return XXH3_len_4to8_64b(input, len, secret, seed);
+  if (len != 0)
+    return XXH3_len_1to3_64b(input, len, secret, seed);
+  return XXH64_avalanche(seed ^ endian::read64le(secret + 56) ^
+                         endian::read64le(secret + 64));
+}
+
+static uint64_t XXH3_mix16B(const uint8_t *input, uint8_t const *secret,
+                            uint64_t seed) {
+  uint64_t lhs = seed;
+  uint64_t rhs = 0U - seed;
+  lhs += endian::read64le(secret);
+  rhs += endian::read64le(secret + 8);
+  lhs ^= endian::read64le(input);
+  rhs ^= endian::read64le(input + 8);
+  return XXH3_mul128_fold64(lhs, rhs);
+}
+
+/* For mid range keys, XXH3 uses a Mum-hash variant. */
+LLVM_ATTRIBUTE_ALWAYS_INLINE
+static uint64_t XXH3_len_17to128_64b(const uint8_t *input, size_t len,
+                                     const uint8_t *secret,
+                                     uint64_t const seed) {
+  uint64_t acc = len * PRIME64_1, acc_end;
+  acc += XXH3_mix16B(input + 0, secret + 0, seed);
+  acc_end = XXH3_mix16B(input + len - 16, secret + 16, seed);
+  if (len > 32) {
+    acc += XXH3_mix16B(input + 16, secret + 32, seed);
+    acc_end += XXH3_mix16B(input + len - 32, secret + 48, seed);
+    if (len > 64) {
+      acc += XXH3_mix16B(input + 32, secret + 64, seed);
+      acc_end += XXH3_mix16B(input + len - 48, secret + 80, seed);
+      if (len > 96) {
+        acc += XXH3_mix16B(input + 48, secret + 96, seed);
+        acc_end += XXH3_mix16B(input + len - 64, secret + 112, seed);
+      }
+    }
+  }
+  return XXH3_avalanche(acc + acc_end);
+}
+
+constexpr size_t XXH3_MIDSIZE_MAX = 240;
+
+LLVM_ATTRIBUTE_NOINLINE
+static uint64_t XXH3_len_129to240_64b(const uint8_t *input, size_t len,
+                                      const uint8_t *secret, uint64_t seed) {
+  constexpr size_t XXH3_MIDSIZE_STARTOFFSET = 3;
+  constexpr size_t XXH3_MIDSIZE_LASTOFFSET = 17;
+  uint64_t acc = (uint64_t)len * PRIME64_1;
+  const unsigned nbRounds = len / 16;
+  for (unsigned i = 0; i < 8; ++i)
+    acc += XXH3_mix16B(input + 16 * i, secret + 16 * i, seed);
+  acc = XXH3_avalanche(acc);
+
+  for (unsigned i = 8; i < nbRounds; ++i) {
+    acc += XXH3_mix16B(input + 16 * i,
+                       secret + 16 * (i - 8) + XXH3_MIDSIZE_STARTOFFSET, seed);
+  }
+  /* last bytes */
+  acc +=
+      XXH3_mix16B(input + len - 16,
+                  secret + XXH3_SECRETSIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed);
+  return XXH3_avalanche(acc);
+}
+
+LLVM_ATTRIBUTE_ALWAYS_INLINE
+static void XXH3_accumulate_512_scalar(uint64_t *acc, const uint8_t *input,
+                                       const uint8_t *secret) {
+  for (size_t i = 0; i < XXH_ACC_NB; ++i) {
+    uint64_t data_val = endian::read64le(input + 8 * i);
+    uint64_t data_key = data_val ^ endian::read64le(secret + 8 * i);
+    acc[i ^ 1] += data_val;
+    acc[i] += uint32_t(data_key) * (data_key >> 32);
+  }
+}
+
+LLVM_ATTRIBUTE_ALWAYS_INLINE
+static void XXH3_accumulate_scalar(uint64_t *acc, const uint8_t *input,
+                                   const uint8_t *secret, size_t nbStripes) {
+  for (size_t n = 0; n < nbStripes; ++n)
+    XXH3_accumulate_512_scalar(acc, input + n * XXH_STRIPE_LEN,
+                               secret + n * XXH_SECRET_CONSUME_RATE);
+}
+
+static void XXH3_scrambleAcc(uint64_t *acc, const uint8_t *secret) {
+  for (size_t i = 0; i < XXH_ACC_NB; ++i) {
+    acc[i] ^= acc[i] >> 47;
+    acc[i] ^= endian::read64le(secret + 8 * i);
+    acc[i] *= PRIME32_1;
+  }
+}
+
+static uint64_t XXH3_mix2Accs(const uint64_t *acc, const uint8_t *secret) {
+  return XXH3_mul128_fold64(acc[0] ^ endian::read64le(secret),
+                            acc[1] ^ endian::read64le(secret + 8));
+}
+
+static uint64_t XXH3_mergeAccs(const uint64_t *acc, const uint8_t *key,
+                               uint64_t start) {
+  uint64_t result64 = start;
+  for (size_t i = 0; i < 4; ++i)
+    result64 += XXH3_mix2Accs(acc + 2 * i, key + 16 * i);
+  return XXH3_avalanche(result64);
+}
+
+LLVM_ATTRIBUTE_NOINLINE
+static uint64_t XXH3_hashLong_64b(const uint8_t *input, size_t len,
+                                  const uint8_t *secret, size_t secretSize) {
+  const size_t nbStripesPerBlock =
+      (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE;
+  const size_t block_len = XXH_STRIPE_LEN * nbStripesPerBlock;
+  const size_t nb_blocks = (len - 1) / block_len;
+  alignas(16) uint64_t acc[XXH_ACC_NB] = {
+      PRIME32_3, PRIME64_1, PRIME64_2, PRIME64_3,
+      PRIME64_4, PRIME32_2, PRIME64_5, PRIME32_1,
+  };
+  for (size_t n = 0; n < nb_blocks; ++n) {
+    XXH3_accumulate_scalar(acc, input + n * block_len, secret,
+                           nbStripesPerBlock);
+    XXH3_scrambleAcc(acc, secret + secretSize - XXH_STRIPE_LEN);
+  }
+
+  /* last partial block */
+  const size_t nbStripes = (len - 1 - (block_len * nb_blocks)) / XXH_STRIPE_LEN;
+  assert(nbStripes <= secretSize / XXH_SECRET_CONSUME_RATE);
+  XXH3_accumulate_scalar(acc, input + nb_blocks * block_len, secret, nbStripes);
+
+  /* last stripe */
+  constexpr size_t XXH_SECRET_LASTACC_START = 7;
+  XXH3_accumulate_512_scalar(acc, input + len - XXH_STRIPE_LEN,
+                             secret + secretSize - XXH_STRIPE_LEN -
+                                 XXH_SECRET_LASTACC_START);
+
+  /* converge into final hash */
+  constexpr size_t XXH_SECRET_MERGEACCS_START = 11;
+  return XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START,
+                        (uint64_t)len * PRIME64_1);
+}
+
+uint64_t wpi::xxh3_64bits(std::span<const uint8_t> data) {
+  auto *in = data.data();
+  size_t len = data.size();
+  if (len <= 16)
+    return XXH3_len_0to16_64b(in, len, kSecret, 0);
+  if (len <= 128)
+    return XXH3_len_17to128_64b(in, len, kSecret, 0);
+  if (len <= XXH3_MIDSIZE_MAX)
+    return XXH3_len_129to240_64b(in, len, kSecret, 0);
+  return XXH3_hashLong_64b(in, len, kSecret, sizeof(kSecret));
+}
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ADL.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ADL.h
new file mode 100644
index 0000000..3be8691
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ADL.h
@@ -0,0 +1,103 @@
+//===- llvm/ADT/ADL.h - Argument dependent lookup utilities -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef WPIUTIL_WPI_ADL_H
+#define WPIUTIL_WPI_ADL_H
+
+#include <type_traits>
+#include <iterator>
+#include <utility>
+
+namespace wpi {
+
+// Only used by compiler if both template types are the same.  Useful when
+// using SFINAE to test for the existence of member functions.
+template <typename T, T> struct SameType;
+
+namespace adl_detail {
+
+using std::begin;
+
+template <typename RangeT>
+constexpr auto begin_impl(RangeT &&range)
+    -> decltype(begin(std::forward<RangeT>(range))) {
+  return begin(std::forward<RangeT>(range));
+}
+
+using std::end;
+
+template <typename RangeT>
+constexpr auto end_impl(RangeT &&range)
+    -> decltype(end(std::forward<RangeT>(range))) {
+  return end(std::forward<RangeT>(range));
+}
+
+using std::swap;
+
+template <typename T>
+constexpr void swap_impl(T &&lhs,
+                         T &&rhs) noexcept(noexcept(swap(std::declval<T>(),
+                                                         std::declval<T>()))) {
+  swap(std::forward<T>(lhs), std::forward<T>(rhs));
+}
+
+using std::size;
+
+template <typename RangeT>
+constexpr auto size_impl(RangeT &&range)
+    -> decltype(size(std::forward<RangeT>(range))) {
+  return size(std::forward<RangeT>(range));
+}
+
+} // end namespace adl_detail
+
+/// Returns the begin iterator to \p range using `std::begin` and
+/// function found through Argument-Dependent Lookup (ADL).
+template <typename RangeT>
+constexpr auto adl_begin(RangeT &&range)
+    -> decltype(adl_detail::begin_impl(std::forward<RangeT>(range))) {
+  return adl_detail::begin_impl(std::forward<RangeT>(range));
+}
+
+/// Returns the end iterator to \p range using `std::end` and
+/// functions found through Argument-Dependent Lookup (ADL).
+template <typename RangeT>
+constexpr auto adl_end(RangeT &&range)
+    -> decltype(adl_detail::end_impl(std::forward<RangeT>(range))) {
+  return adl_detail::end_impl(std::forward<RangeT>(range));
+}
+
+/// Swaps \p lhs with \p rhs using `std::swap` and functions found through
+/// Argument-Dependent Lookup (ADL).
+template <typename T>
+constexpr void adl_swap(T &&lhs, T &&rhs) noexcept(
+    noexcept(adl_detail::swap_impl(std::declval<T>(), std::declval<T>()))) {
+  adl_detail::swap_impl(std::forward<T>(lhs), std::forward<T>(rhs));
+}
+
+/// Returns the size of \p range using `std::size` and functions found through
+/// Argument-Dependent Lookup (ADL).
+template <typename RangeT>
+constexpr auto adl_size(RangeT &&range)
+    -> decltype(adl_detail::size_impl(std::forward<RangeT>(range))) {
+  return adl_detail::size_impl(std::forward<RangeT>(range));
+}
+
+namespace detail {
+
+template <typename RangeT>
+using IterOfRange = decltype(adl_begin(std::declval<RangeT &>()));
+
+template <typename RangeT>
+using ValueOfRange =
+    std::remove_reference_t<decltype(*adl_begin(std::declval<RangeT &>()))>;
+
+} // namespace detail
+} // namespace wpi
+
+#endif // WPIUTIL_WPI_ADL_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/AllocatorBase.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/AllocatorBase.h
index 29ba16f..97a2f48 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/AllocatorBase.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/AllocatorBase.h
@@ -19,6 +19,12 @@
 #ifndef WPIUTIL_WPI_ALLOCATORBASE_H
 #define WPIUTIL_WPI_ALLOCATORBASE_H
 
+#ifdef _MSC_VER
+#define LLVM_ALLOCATORHOLDER_EMPTYBASE __declspec(empty_bases)
+#else
+#define LLVM_ALLOCATORHOLDER_EMPTYBASE
+#endif // _MSC_VER
+
 #include "wpi/Compiler.h"
 #include "wpi/MemAlloc.h"
 #include <type_traits>
@@ -72,7 +78,7 @@
 
   /// Deallocate space for a sequence of objects without constructing them.
   template <typename T>
-  std::enable_if_t<!std::is_same<std::remove_cv_t<T>, void>::value, void>
+  std::enable_if_t<!std::is_same_v<std::remove_cv_t<T>, void>, void>
   Deallocate(T *Ptr, size_t Num = 1) {
     Deallocate(static_cast<const void *>(Ptr), Num * sizeof(T), alignof(T));
   }
@@ -99,6 +105,28 @@
   void PrintStats() const {}
 };
 
+namespace detail {
+
+template <typename Alloc> class AllocatorHolder : Alloc {
+public:
+  AllocatorHolder() = default;
+  AllocatorHolder(const Alloc &A) : Alloc(A) {}
+  AllocatorHolder(Alloc &&A) : Alloc(static_cast<Alloc &&>(A)) {}
+  Alloc &getAllocator() { return *this; }
+  const Alloc &getAllocator() const { return *this; }
+};
+
+template <typename Alloc> class AllocatorHolder<Alloc &> {
+  Alloc &A;
+
+public:
+  AllocatorHolder(Alloc &A) : A(A) {}
+  Alloc &getAllocator() { return A; }
+  const Alloc &getAllocator() const { return A; }
+};
+
+} // namespace detail
+
 } // namespace wpi
 
 #endif // WPIUTIL_WPI_ALLOCATORBASE_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Casting.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Casting.h
new file mode 100644
index 0000000..c91f1b7
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Casting.h
@@ -0,0 +1,807 @@
+//===- llvm/Support/Casting.h - Allow flexible, checked, casts --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the isa<X>(), cast<X>(), dyn_cast<X>(),
+// cast_if_present<X>(), and dyn_cast_if_present<X>() templates.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef WPIUTIL_WPI_CASTING_H
+#define WPIUTIL_WPI_CASTING_H
+
+#include "wpi/Compiler.h"
+#include "wpi/type_traits.h"
+#include <cassert>
+#include <memory>
+#include <optional>
+#include <type_traits>
+
+namespace wpi {
+
+//===----------------------------------------------------------------------===//
+// simplify_type
+//===----------------------------------------------------------------------===//
+
+/// Define a template that can be specialized by smart pointers to reflect the
+/// fact that they are automatically dereferenced, and are not involved with the
+/// template selection process...  the default implementation is a noop.
+// TODO: rename this and/or replace it with other cast traits.
+template <typename From> struct simplify_type {
+  using SimpleType = From; // The real type this represents...
+
+  // An accessor to get the real value...
+  static SimpleType &getSimplifiedValue(From &Val) { return Val; }
+};
+
+template <typename From> struct simplify_type<const From> {
+  using NonConstSimpleType = typename simplify_type<From>::SimpleType;
+  using SimpleType = typename add_const_past_pointer<NonConstSimpleType>::type;
+  using RetType =
+      typename add_lvalue_reference_if_not_pointer<SimpleType>::type;
+
+  static RetType getSimplifiedValue(const From &Val) {
+    return simplify_type<From>::getSimplifiedValue(const_cast<From &>(Val));
+  }
+};
+
+// TODO: add this namespace once everyone is switched to using the new
+//       interface.
+// namespace detail {
+
+//===----------------------------------------------------------------------===//
+// isa_impl
+//===----------------------------------------------------------------------===//
+
+// The core of the implementation of isa<X> is here; To and From should be
+// the names of classes.  This template can be specialized to customize the
+// implementation of isa<> without rewriting it from scratch.
+template <typename To, typename From, typename Enabler = void> struct isa_impl {
+  static inline bool doit(const From &Val) { return To::classof(&Val); }
+};
+
+// Always allow upcasts, and perform no dynamic check for them.
+template <typename To, typename From>
+struct isa_impl<To, From, std::enable_if_t<std::is_base_of_v<To, From>>> {
+  static inline bool doit(const From &) { return true; }
+};
+
+template <typename To, typename From> struct isa_impl_cl {
+  static inline bool doit(const From &Val) {
+    return isa_impl<To, From>::doit(Val);
+  }
+};
+
+template <typename To, typename From> struct isa_impl_cl<To, const From> {
+  static inline bool doit(const From &Val) {
+    return isa_impl<To, From>::doit(Val);
+  }
+};
+
+template <typename To, typename From>
+struct isa_impl_cl<To, const std::unique_ptr<From>> {
+  static inline bool doit(const std::unique_ptr<From> &Val) {
+    assert(Val && "isa<> used on a null pointer");
+    return isa_impl_cl<To, From>::doit(*Val);
+  }
+};
+
+template <typename To, typename From> struct isa_impl_cl<To, From *> {
+  static inline bool doit(const From *Val) {
+    assert(Val && "isa<> used on a null pointer");
+    return isa_impl<To, From>::doit(*Val);
+  }
+};
+
+template <typename To, typename From> struct isa_impl_cl<To, From *const> {
+  static inline bool doit(const From *Val) {
+    assert(Val && "isa<> used on a null pointer");
+    return isa_impl<To, From>::doit(*Val);
+  }
+};
+
+template <typename To, typename From> struct isa_impl_cl<To, const From *> {
+  static inline bool doit(const From *Val) {
+    assert(Val && "isa<> used on a null pointer");
+    return isa_impl<To, From>::doit(*Val);
+  }
+};
+
+template <typename To, typename From>
+struct isa_impl_cl<To, const From *const> {
+  static inline bool doit(const From *Val) {
+    assert(Val && "isa<> used on a null pointer");
+    return isa_impl<To, From>::doit(*Val);
+  }
+};
+
+template <typename To, typename From, typename SimpleFrom>
+struct isa_impl_wrap {
+  // When From != SimplifiedType, we can simplify the type some more by using
+  // the simplify_type template.
+  static bool doit(const From &Val) {
+    return isa_impl_wrap<To, SimpleFrom,
+                         typename simplify_type<SimpleFrom>::SimpleType>::
+        doit(simplify_type<const From>::getSimplifiedValue(Val));
+  }
+};
+
+template <typename To, typename FromTy>
+struct isa_impl_wrap<To, FromTy, FromTy> {
+  // When From == SimpleType, we are as simple as we are going to get.
+  static bool doit(const FromTy &Val) {
+    return isa_impl_cl<To, FromTy>::doit(Val);
+  }
+};
+
+//===----------------------------------------------------------------------===//
+// cast_retty + cast_retty_impl
+//===----------------------------------------------------------------------===//
+
+template <class To, class From> struct cast_retty;
+
+// Calculate what type the 'cast' function should return, based on a requested
+// type of To and a source type of From.
+template <class To, class From> struct cast_retty_impl {
+  using ret_type = To &; // Normal case, return Ty&
+};
+template <class To, class From> struct cast_retty_impl<To, const From> {
+  using ret_type = const To &; // Normal case, return Ty&
+};
+
+template <class To, class From> struct cast_retty_impl<To, From *> {
+  using ret_type = To *; // Pointer arg case, return Ty*
+};
+
+template <class To, class From> struct cast_retty_impl<To, const From *> {
+  using ret_type = const To *; // Constant pointer arg case, return const Ty*
+};
+
+template <class To, class From> struct cast_retty_impl<To, const From *const> {
+  using ret_type = const To *; // Constant pointer arg case, return const Ty*
+};
+
+template <class To, class From>
+struct cast_retty_impl<To, std::unique_ptr<From>> {
+private:
+  using PointerType = typename cast_retty_impl<To, From *>::ret_type;
+  using ResultType = std::remove_pointer_t<PointerType>;
+
+public:
+  using ret_type = std::unique_ptr<ResultType>;
+};
+
+template <class To, class From, class SimpleFrom> struct cast_retty_wrap {
+  // When the simplified type and the from type are not the same, use the type
+  // simplifier to reduce the type, then reuse cast_retty_impl to get the
+  // resultant type.
+  using ret_type = typename cast_retty<To, SimpleFrom>::ret_type;
+};
+
+template <class To, class FromTy> struct cast_retty_wrap<To, FromTy, FromTy> {
+  // When the simplified type is equal to the from type, use it directly.
+  using ret_type = typename cast_retty_impl<To, FromTy>::ret_type;
+};
+
+template <class To, class From> struct cast_retty {
+  using ret_type = typename cast_retty_wrap<
+      To, From, typename simplify_type<From>::SimpleType>::ret_type;
+};
+
+//===----------------------------------------------------------------------===//
+// cast_convert_val
+//===----------------------------------------------------------------------===//
+
+// Ensure the non-simple values are converted using the simplify_type template
+// that may be specialized by smart pointers...
+//
+template <class To, class From, class SimpleFrom> struct cast_convert_val {
+  // This is not a simple type, use the template to simplify it...
+  static typename cast_retty<To, From>::ret_type doit(const From &Val) {
+    return cast_convert_val<To, SimpleFrom,
+                            typename simplify_type<SimpleFrom>::SimpleType>::
+        doit(simplify_type<From>::getSimplifiedValue(const_cast<From &>(Val)));
+  }
+};
+
+template <class To, class FromTy> struct cast_convert_val<To, FromTy, FromTy> {
+  // If it's a reference, switch to a pointer to do the cast and then deref it.
+  static typename cast_retty<To, FromTy>::ret_type doit(const FromTy &Val) {
+    return *(std::remove_reference_t<typename cast_retty<To, FromTy>::ret_type>
+                 *)&const_cast<FromTy &>(Val);
+  }
+};
+
+template <class To, class FromTy>
+struct cast_convert_val<To, FromTy *, FromTy *> {
+  // If it's a pointer, we can use c-style casting directly.
+  static typename cast_retty<To, FromTy *>::ret_type doit(const FromTy *Val) {
+    return (typename cast_retty<To, FromTy *>::ret_type) const_cast<FromTy *>(
+        Val);
+  }
+};
+
+//===----------------------------------------------------------------------===//
+// is_simple_type
+//===----------------------------------------------------------------------===//
+
+template <class X> struct is_simple_type {
+  static const bool value =
+      std::is_same_v<X, typename simplify_type<X>::SimpleType>;
+};
+
+// } // namespace detail
+
+//===----------------------------------------------------------------------===//
+// CastIsPossible
+//===----------------------------------------------------------------------===//
+
+/// This struct provides a way to check if a given cast is possible. It provides
+/// a static function called isPossible that is used to check if a cast can be
+/// performed. It should be overridden like this:
+///
+/// template<> struct CastIsPossible<foo, bar> {
+///   static inline bool isPossible(const bar &b) {
+///     return bar.isFoo();
+///   }
+/// };
+template <typename To, typename From, typename Enable = void>
+struct CastIsPossible {
+  static inline bool isPossible(const From &f) {
+    return isa_impl_wrap<
+        To, const From,
+        typename simplify_type<const From>::SimpleType>::doit(f);
+  }
+};
+
+// Needed for optional unwrapping. This could be implemented with isa_impl, but
+// we want to implement things in the new method and move old implementations
+// over. In fact, some of the isa_impl templates should be moved over to
+// CastIsPossible.
+template <typename To, typename From>
+struct CastIsPossible<To, std::optional<From>> {
+  static inline bool isPossible(const std::optional<From> &f) {
+    assert(f && "CastIsPossible::isPossible called on a nullopt!");
+    return isa_impl_wrap<
+        To, const From,
+        typename simplify_type<const From>::SimpleType>::doit(*f);
+  }
+};
+
+/// Upcasting (from derived to base) and casting from a type to itself should
+/// always be possible.
+template <typename To, typename From>
+struct CastIsPossible<To, From, std::enable_if_t<std::is_base_of_v<To, From>>> {
+  static inline bool isPossible(const From &f) { return true; }
+};
+
+//===----------------------------------------------------------------------===//
+// Cast traits
+//===----------------------------------------------------------------------===//
+
+/// All of these cast traits are meant to be implementations for useful casts
+/// that users may want to use that are outside the standard behavior. An
+/// example of how to use a special cast called `CastTrait` is:
+///
+/// template<> struct CastInfo<foo, bar> : public CastTrait<foo, bar> {};
+///
+/// Essentially, if your use case falls directly into one of the use cases
+/// supported by a given cast trait, simply inherit your special CastInfo
+/// directly from one of these to avoid having to reimplement the boilerplate
+/// `isPossible/castFailed/doCast/doCastIfPossible`. A cast trait can also
+/// provide a subset of those functions.
+
+/// This cast trait just provides castFailed for the specified `To` type to make
+/// CastInfo specializations more declarative. In order to use this, the target
+/// result type must be `To` and `To` must be constructible from `nullptr`.
+template <typename To> struct NullableValueCastFailed {
+  static To castFailed() { return To(nullptr); }
+};
+
+/// This cast trait just provides the default implementation of doCastIfPossible
+/// to make CastInfo specializations more declarative. The `Derived` template
+/// parameter *must* be provided for forwarding castFailed and doCast.
+template <typename To, typename From, typename Derived>
+struct DefaultDoCastIfPossible {
+  static To doCastIfPossible(From f) {
+    if (!Derived::isPossible(f))
+      return Derived::castFailed();
+    return Derived::doCast(f);
+  }
+};
+
+namespace detail {
+/// A helper to derive the type to use with `Self` for cast traits, when the
+/// provided CRTP derived type is allowed to be void.
+template <typename OptionalDerived, typename Default>
+using SelfType = std::conditional_t<std::is_same_v<OptionalDerived, void>,
+                                    Default, OptionalDerived>;
+} // namespace detail
+
+/// This cast trait provides casting for the specific case of casting to a
+/// value-typed object from a pointer-typed object. Note that `To` must be
+/// nullable/constructible from a pointer to `From` to use this cast.
+template <typename To, typename From, typename Derived = void>
+struct ValueFromPointerCast
+    : public CastIsPossible<To, From *>,
+      public NullableValueCastFailed<To>,
+      public DefaultDoCastIfPossible<
+          To, From *,
+          detail::SelfType<Derived, ValueFromPointerCast<To, From>>> {
+  static inline To doCast(From *f) { return To(f); }
+};
+
+/// This cast trait provides std::unique_ptr casting. It has the semantics of
+/// moving the contents of the input unique_ptr into the output unique_ptr
+/// during the cast. It's also a good example of how to implement a move-only
+/// cast.
+template <typename To, typename From, typename Derived = void>
+struct UniquePtrCast : public CastIsPossible<To, From *> {
+  using Self = detail::SelfType<Derived, UniquePtrCast<To, From>>;
+  using CastResultType = std::unique_ptr<
+      std::remove_reference_t<typename cast_retty<To, From>::ret_type>>;
+
+  static inline CastResultType doCast(std::unique_ptr<From> &&f) {
+    return CastResultType((typename CastResultType::element_type *)f.release());
+  }
+
+  static inline CastResultType castFailed() { return CastResultType(nullptr); }
+
+  static inline CastResultType doCastIfPossible(std::unique_ptr<From> &&f) {
+    if (!Self::isPossible(f))
+      return castFailed();
+    return doCast(f);
+  }
+};
+
+/// This cast trait provides std::optional<T> casting. This means that if you
+/// have a value type, you can cast it to another value type and have dyn_cast
+/// return an std::optional<T>.
+template <typename To, typename From, typename Derived = void>
+struct OptionalValueCast
+    : public CastIsPossible<To, From>,
+      public DefaultDoCastIfPossible<
+          std::optional<To>, From,
+          detail::SelfType<Derived, OptionalValueCast<To, From>>> {
+  static inline std::optional<To> castFailed() { return std::optional<To>{}; }
+
+  static inline std::optional<To> doCast(const From &f) { return To(f); }
+};
+
+/// Provides a cast trait that strips `const` from types to make it easier to
+/// implement a const-version of a non-const cast. It just removes boilerplate
+/// and reduces the amount of code you as the user need to implement. You can
+/// use it like this:
+///
+/// template<> struct CastInfo<foo, bar> {
+///   ...verbose implementation...
+/// };
+///
+/// template<> struct CastInfo<foo, const bar> : public
+///        ConstStrippingForwardingCast<foo, const bar, CastInfo<foo, bar>> {};
+///
+template <typename To, typename From, typename ForwardTo>
+struct ConstStrippingForwardingCast {
+  // Remove the pointer if it exists, then we can get rid of consts/volatiles.
+  using DecayedFrom = std::remove_cv_t<std::remove_pointer_t<From>>;
+  // Now if it's a pointer, add it back. Otherwise, we want a ref.
+  using NonConstFrom =
+      std::conditional_t<std::is_pointer_v<From>, DecayedFrom *, DecayedFrom &>;
+
+  static inline bool isPossible(const From &f) {
+    return ForwardTo::isPossible(const_cast<NonConstFrom>(f));
+  }
+
+  static inline decltype(auto) castFailed() { return ForwardTo::castFailed(); }
+
+  static inline decltype(auto) doCast(const From &f) {
+    return ForwardTo::doCast(const_cast<NonConstFrom>(f));
+  }
+
+  static inline decltype(auto) doCastIfPossible(const From &f) {
+    return ForwardTo::doCastIfPossible(const_cast<NonConstFrom>(f));
+  }
+};
+
+/// Provides a cast trait that uses a defined pointer to pointer cast as a base
+/// for reference-to-reference casts. Note that it does not provide castFailed
+/// and doCastIfPossible because a pointer-to-pointer cast would likely just
+/// return `nullptr` which could cause nullptr dereference. You can use it like
+/// this:
+///
+///   template <> struct CastInfo<foo, bar *> { ... verbose implementation... };
+///
+///   template <>
+///   struct CastInfo<foo, bar>
+///       : public ForwardToPointerCast<foo, bar, CastInfo<foo, bar *>> {};
+///
+template <typename To, typename From, typename ForwardTo>
+struct ForwardToPointerCast {
+  static inline bool isPossible(const From &f) {
+    return ForwardTo::isPossible(&f);
+  }
+
+  static inline decltype(auto) doCast(const From &f) {
+    return *ForwardTo::doCast(&f);
+  }
+};
+
+//===----------------------------------------------------------------------===//
+// CastInfo
+//===----------------------------------------------------------------------===//
+
+/// This struct provides a method for customizing the way a cast is performed.
+/// It inherits from CastIsPossible, to support the case of declaring many
+/// CastIsPossible specializations without having to specialize the full
+/// CastInfo.
+///
+/// In order to specialize different behaviors, specify different functions in
+/// your CastInfo specialization.
+/// For isa<> customization, provide:
+///
+///   `static bool isPossible(const From &f)`
+///
+/// For cast<> customization, provide:
+///
+///  `static To doCast(const From &f)`
+///
+/// For dyn_cast<> and the *_if_present<> variants' customization, provide:
+///
+///  `static To castFailed()` and `static To doCastIfPossible(const From &f)`
+///
+/// Your specialization might look something like this:
+///
+///  template<> struct CastInfo<foo, bar> : public CastIsPossible<foo, bar> {
+///    static inline foo doCast(const bar &b) {
+///      return foo(const_cast<bar &>(b));
+///    }
+///    static inline foo castFailed() { return foo(); }
+///    static inline foo doCastIfPossible(const bar &b) {
+///      if (!CastInfo<foo, bar>::isPossible(b))
+///        return castFailed();
+///      return doCast(b);
+///    }
+///  };
+
+// The default implementations of CastInfo don't use cast traits for now because
+// we need to specify types all over the place due to the current expected
+// casting behavior and the way cast_retty works. New use cases can and should
+// take advantage of the cast traits whenever possible!
+
+template <typename To, typename From, typename Enable = void>
+struct CastInfo : public CastIsPossible<To, From> {
+  using Self = CastInfo<To, From, Enable>;
+
+  using CastReturnType = typename cast_retty<To, From>::ret_type;
+
+  static inline CastReturnType doCast(const From &f) {
+    return cast_convert_val<
+        To, From,
+        typename simplify_type<From>::SimpleType>::doit(const_cast<From &>(f));
+  }
+
+  // This assumes that you can construct the cast return type from `nullptr`.
+  // This is largely to support legacy use cases - if you don't want this
+  // behavior you should specialize CastInfo for your use case.
+  static inline CastReturnType castFailed() { return CastReturnType(nullptr); }
+
+  static inline CastReturnType doCastIfPossible(const From &f) {
+    if (!Self::isPossible(f))
+      return castFailed();
+    return doCast(f);
+  }
+};
+
+/// This struct provides an overload for CastInfo where From has simplify_type
+/// defined. This simply forwards to the appropriate CastInfo with the
+/// simplified type/value, so you don't have to implement both.
+template <typename To, typename From>
+struct CastInfo<To, From, std::enable_if_t<!is_simple_type<From>::value>> {
+  using Self = CastInfo<To, From>;
+  using SimpleFrom = typename simplify_type<From>::SimpleType;
+  using SimplifiedSelf = CastInfo<To, SimpleFrom>;
+
+  static inline bool isPossible(From &f) {
+    return SimplifiedSelf::isPossible(
+        simplify_type<From>::getSimplifiedValue(f));
+  }
+
+  static inline decltype(auto) doCast(From &f) {
+    return SimplifiedSelf::doCast(simplify_type<From>::getSimplifiedValue(f));
+  }
+
+  static inline decltype(auto) castFailed() {
+    return SimplifiedSelf::castFailed();
+  }
+
+  static inline decltype(auto) doCastIfPossible(From &f) {
+    return SimplifiedSelf::doCastIfPossible(
+        simplify_type<From>::getSimplifiedValue(f));
+  }
+};
+
+//===----------------------------------------------------------------------===//
+// Pre-specialized CastInfo
+//===----------------------------------------------------------------------===//
+
+/// Provide a CastInfo specialized for std::unique_ptr.
+template <typename To, typename From>
+struct CastInfo<To, std::unique_ptr<From>> : public UniquePtrCast<To, From> {};
+
+/// Provide a CastInfo specialized for std::optional<From>. It's assumed that if
+/// the input is std::optional<From> that the output can be std::optional<To>.
+/// If that's not the case, specialize CastInfo for your use case.
+template <typename To, typename From>
+struct CastInfo<To, std::optional<From>> : public OptionalValueCast<To, From> {
+};
+
+/// isa<X> - Return true if the parameter to the template is an instance of one
+/// of the template type arguments.  Used like this:
+///
+///  if (isa<Type>(myVal)) { ... }
+///  if (isa<Type0, Type1, Type2>(myVal)) { ... }
+template <typename To, typename From>
+[[nodiscard]] inline bool isa(const From &Val) {
+  return CastInfo<To, const From>::isPossible(Val);
+}
+
+template <typename First, typename Second, typename... Rest, typename From>
+[[nodiscard]] inline bool isa(const From &Val) {
+  return isa<First>(Val) || isa<Second, Rest...>(Val);
+}
+
+/// cast<X> - Return the argument parameter cast to the specified type.  This
+/// casting operator asserts that the type is correct, so it does not return
+/// null on failure.  It does not allow a null argument (use cast_if_present for
+/// that). It is typically used like this:
+///
+///  cast<Instruction>(myVal)->getParent()
+
+template <typename To, typename From>
+[[nodiscard]] inline decltype(auto) cast(const From &Val) {
+  assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!");
+  return CastInfo<To, const From>::doCast(Val);
+}
+
+template <typename To, typename From>
+[[nodiscard]] inline decltype(auto) cast(From &Val) {
+  assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!");
+  return CastInfo<To, From>::doCast(Val);
+}
+
+template <typename To, typename From>
+[[nodiscard]] inline decltype(auto) cast(From *Val) {
+  assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!");
+  return CastInfo<To, From *>::doCast(Val);
+}
+
+template <typename To, typename From>
+[[nodiscard]] inline decltype(auto) cast(std::unique_ptr<From> &&Val) {
+  assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!");
+  return CastInfo<To, std::unique_ptr<From>>::doCast(std::move(Val));
+}
+
+//===----------------------------------------------------------------------===//
+// ValueIsPresent
+//===----------------------------------------------------------------------===//
+
+template <typename T>
+constexpr bool IsNullable =
+    std::is_pointer_v<T> || std::is_constructible_v<T, std::nullptr_t>;
+
+/// ValueIsPresent provides a way to check if a value is, well, present. For
+/// pointers, this is the equivalent of checking against nullptr, for Optionals
+/// this is the equivalent of checking hasValue(). It also provides a method for
+/// unwrapping a value (think calling .value() on an optional).
+
+// Generic values can't *not* be present.
+template <typename T, typename Enable = void> struct ValueIsPresent {
+  using UnwrappedType = T;
+  static inline bool isPresent(const T &t) { return true; }
+  static inline decltype(auto) unwrapValue(T &t) { return t; }
+};
+
+// Optional provides its own way to check if something is present.
+template <typename T> struct ValueIsPresent<std::optional<T>> {
+  using UnwrappedType = T;
+  static inline bool isPresent(const std::optional<T> &t) {
+    return t.has_value();
+  }
+  static inline decltype(auto) unwrapValue(std::optional<T> &t) { return *t; }
+};
+
+// If something is "nullable" then we just compare it to nullptr to see if it
+// exists.
+template <typename T>
+struct ValueIsPresent<T, std::enable_if_t<IsNullable<T>>> {
+  using UnwrappedType = T;
+  static inline bool isPresent(const T &t) { return t != T(nullptr); }
+  static inline decltype(auto) unwrapValue(T &t) { return t; }
+};
+
+namespace detail {
+// Convenience function we can use to check if a value is present. Because of
+// simplify_type, we have to call it on the simplified type for now.
+template <typename T> inline bool isPresent(const T &t) {
+  return ValueIsPresent<typename simplify_type<T>::SimpleType>::isPresent(
+      simplify_type<T>::getSimplifiedValue(const_cast<T &>(t)));
+}
+
+// Convenience function we can use to unwrap a value.
+template <typename T> inline decltype(auto) unwrapValue(T &t) {
+  return ValueIsPresent<T>::unwrapValue(t);
+}
+} // namespace detail
+
+/// dyn_cast<X> - Return the argument parameter cast to the specified type. This
+/// casting operator returns null if the argument is of the wrong type, so it
+/// can be used to test for a type as well as cast if successful. The value
+/// passed in must be present, if not, use dyn_cast_if_present. This should be
+/// used in the context of an if statement like this:
+///
+///  if (const Instruction *I = dyn_cast<Instruction>(myVal)) { ... }
+
+template <typename To, typename From>
+[[nodiscard]] inline decltype(auto) dyn_cast(const From &Val) {
+  assert(detail::isPresent(Val) && "dyn_cast on a non-existent value");
+  return CastInfo<To, const From>::doCastIfPossible(Val);
+}
+
+template <typename To, typename From>
+[[nodiscard]] inline decltype(auto) dyn_cast(From &Val) {
+  assert(detail::isPresent(Val) && "dyn_cast on a non-existent value");
+  return CastInfo<To, From>::doCastIfPossible(Val);
+}
+
+template <typename To, typename From>
+[[nodiscard]] inline decltype(auto) dyn_cast(From *Val) {
+  assert(detail::isPresent(Val) && "dyn_cast on a non-existent value");
+  return CastInfo<To, From *>::doCastIfPossible(Val);
+}
+
+template <typename To, typename From>
+[[nodiscard]] inline decltype(auto) dyn_cast(std::unique_ptr<From> &&Val) {
+  assert(detail::isPresent(Val) && "dyn_cast on a non-existent value");
+  return CastInfo<To, std::unique_ptr<From>>::doCastIfPossible(
+      std::forward<std::unique_ptr<From> &&>(Val));
+}
+
+/// isa_and_present<X> - Functionally identical to isa, except that a null value
+/// is accepted.
+template <typename... X, class Y>
+[[nodiscard]] inline bool isa_and_present(const Y &Val) {
+  if (!detail::isPresent(Val))
+    return false;
+  return isa<X...>(Val);
+}
+
+template <typename... X, class Y>
+[[nodiscard]] inline bool isa_and_nonnull(const Y &Val) {
+  return isa_and_present<X...>(Val);
+}
+
+/// cast_if_present<X> - Functionally identical to cast, except that a null
+/// value is accepted.
+template <class X, class Y>
+[[nodiscard]] inline auto cast_if_present(const Y &Val) {
+  if (!detail::isPresent(Val))
+    return CastInfo<X, const Y>::castFailed();
+  assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!");
+  return cast<X>(detail::unwrapValue(Val));
+}
+
+template <class X, class Y> [[nodiscard]] inline auto cast_if_present(Y &Val) {
+  if (!detail::isPresent(Val))
+    return CastInfo<X, Y>::castFailed();
+  assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!");
+  return cast<X>(detail::unwrapValue(Val));
+}
+
+template <class X, class Y> [[nodiscard]] inline auto cast_if_present(Y *Val) {
+  if (!detail::isPresent(Val))
+    return CastInfo<X, Y *>::castFailed();
+  assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!");
+  return cast<X>(detail::unwrapValue(Val));
+}
+
+template <class X, class Y>
+[[nodiscard]] inline auto cast_if_present(std::unique_ptr<Y> &&Val) {
+  if (!detail::isPresent(Val))
+    return UniquePtrCast<X, Y>::castFailed();
+  return UniquePtrCast<X, Y>::doCast(std::move(Val));
+}
+
+// Provide a forwarding from cast_or_null to cast_if_present for current
+// users. This is deprecated and will be removed in a future patch, use
+// cast_if_present instead.
+template <class X, class Y> auto cast_or_null(const Y &Val) {
+  return cast_if_present<X>(Val);
+}
+
+template <class X, class Y> auto cast_or_null(Y &Val) {
+  return cast_if_present<X>(Val);
+}
+
+template <class X, class Y> auto cast_or_null(Y *Val) {
+  return cast_if_present<X>(Val);
+}
+
+template <class X, class Y> auto cast_or_null(std::unique_ptr<Y> &&Val) {
+  return cast_if_present<X>(std::move(Val));
+}
+
+/// dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a
+/// null (or none in the case of optionals) value is accepted.
+template <class X, class Y> auto dyn_cast_if_present(const Y &Val) {
+  if (!detail::isPresent(Val))
+    return CastInfo<X, const Y>::castFailed();
+  return CastInfo<X, const Y>::doCastIfPossible(detail::unwrapValue(Val));
+}
+
+template <class X, class Y> auto dyn_cast_if_present(Y &Val) {
+  if (!detail::isPresent(Val))
+    return CastInfo<X, Y>::castFailed();
+  return CastInfo<X, Y>::doCastIfPossible(detail::unwrapValue(Val));
+}
+
+template <class X, class Y> auto dyn_cast_if_present(Y *Val) {
+  if (!detail::isPresent(Val))
+    return CastInfo<X, Y *>::castFailed();
+  return CastInfo<X, Y *>::doCastIfPossible(detail::unwrapValue(Val));
+}
+
+// Forwards to dyn_cast_if_present to avoid breaking current users. This is
+// deprecated and will be removed in a future patch, use
+// cast_if_present instead.
+template <class X, class Y> auto dyn_cast_or_null(const Y &Val) {
+  return dyn_cast_if_present<X>(Val);
+}
+
+template <class X, class Y> auto dyn_cast_or_null(Y &Val) {
+  return dyn_cast_if_present<X>(Val);
+}
+
+template <class X, class Y> auto dyn_cast_or_null(Y *Val) {
+  return dyn_cast_if_present<X>(Val);
+}
+
+/// unique_dyn_cast<X> - Given a unique_ptr<Y>, try to return a unique_ptr<X>,
+/// taking ownership of the input pointer iff isa<X>(Val) is true.  If the
+/// cast is successful, From refers to nullptr on exit and the casted value
+/// is returned.  If the cast is unsuccessful, the function returns nullptr
+/// and From is unchanged.
+template <class X, class Y>
+[[nodiscard]] inline typename CastInfo<X, std::unique_ptr<Y>>::CastResultType
+unique_dyn_cast(std::unique_ptr<Y> &Val) {
+  if (!isa<X>(Val))
+    return nullptr;
+  return cast<X>(std::move(Val));
+}
+
+template <class X, class Y>
+[[nodiscard]] inline auto unique_dyn_cast(std::unique_ptr<Y> &&Val) {
+  return unique_dyn_cast<X, Y>(Val);
+}
+
+// unique_dyn_cast_or_null<X> - Functionally identical to unique_dyn_cast,
+// except that a null value is accepted.
+template <class X, class Y>
+[[nodiscard]] inline typename CastInfo<X, std::unique_ptr<Y>>::CastResultType
+unique_dyn_cast_or_null(std::unique_ptr<Y> &Val) {
+  if (!Val)
+    return nullptr;
+  return unique_dyn_cast<X, Y>(Val);
+}
+
+template <class X, class Y>
+[[nodiscard]] inline auto unique_dyn_cast_or_null(std::unique_ptr<Y> &&Val) {
+  return unique_dyn_cast_or_null<X, Y>(Val);
+}
+
+} // end namespace wpi
+
+#endif // WPIUTIL_WPI_CASTING_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Compiler.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Compiler.h
index c98386e..121ed54 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Compiler.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Compiler.h
@@ -38,6 +38,10 @@
 # define __has_builtin(x) 0
 #endif
 
+#ifndef __has_include
+# define __has_include(x) 0
+#endif
+
 // Only use __has_cpp_attribute in C++ mode. GCC defines __has_cpp_attribute in
 // C mode, but the :: in __has_cpp_attribute(scoped::attribute) is invalid.
 #ifndef LLVM_HAS_CPP_ATTRIBUTE
@@ -101,26 +105,6 @@
 #endif
 #endif
 
-/// Does the compiler support ref-qualifiers for *this?
-///
-/// Sadly, this is separate from just rvalue reference support because GCC
-/// and MSVC implemented this later than everything else. This appears to be
-/// corrected in MSVC 2019 but not MSVC 2017.
-/// FIXME: Remove LLVM_HAS_RVALUE_REFERENCE_THIS macro
-#define LLVM_HAS_RVALUE_REFERENCE_THIS 1
-
-/// Expands to '&' if ref-qualifiers for *this are supported.
-///
-/// This can be used to provide lvalue/rvalue overrides of member functions.
-/// The rvalue override should be guarded by LLVM_HAS_RVALUE_REFERENCE_THIS
-#ifndef LLVM_LVALUE_FUNCTION
-#if LLVM_HAS_RVALUE_REFERENCE_THIS
-#define LLVM_LVALUE_FUNCTION &
-#else
-#define LLVM_LVALUE_FUNCTION
-#endif
-#endif
-
 /// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked
 /// into a shared library, then the class should be private to the library and
 /// not accessible from outside it.  Can also be used to mark variables and
@@ -130,11 +114,24 @@
 /// LLVM_EXTERNAL_VISIBILITY - classes, functions, and variables marked with
 /// this attribute will be made public and visible outside of any shared library
 /// they are linked in to.
-#if __has_attribute(visibility) && !defined(__MINGW32__) &&                    \
-    !defined(__CYGWIN__) && !defined(_WIN32)
-#define LLVM_LIBRARY_VISIBILITY __attribute__ ((visibility("hidden")))
+
+#if LLVM_HAS_CPP_ATTRIBUTE(gnu::visibility)
+#define LLVM_ATTRIBUTE_VISIBILITY_HIDDEN [[gnu::visibility("hidden")]]
+#define LLVM_ATTRIBUTE_VISIBILITY_DEFAULT [[gnu::visibility("default")]]
+#elif __has_attribute(visibility)
+#define LLVM_ATTRIBUTE_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
+#define LLVM_ATTRIBUTE_VISIBILITY_DEFAULT __attribute__((visibility("default")))
+#else
+#define LLVM_ATTRIBUTE_VISIBILITY_HIDDEN
+#define LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
+#endif
+
+
+#if (!(defined(_WIN32) || defined(__CYGWIN__)) ||                              \
+     (defined(__MINGW32__) && defined(__clang__)))
+#define LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_VISIBILITY_HIDDEN
 #if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS)
-#define LLVM_EXTERNAL_VISIBILITY __attribute__((visibility("default")))
+#define LLVM_EXTERNAL_VISIBILITY LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
 #else
 #define LLVM_EXTERNAL_VISIBILITY
 #endif
@@ -159,23 +156,10 @@
 #endif
 #endif
 
-/// LLVM_NODISCARD - Warn if a type or return value is discarded.
-
-// Use the 'nodiscard' attribute in C++17 or newer mode.
-#ifndef LLVM_NODISCARD
-#if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(nodiscard)
-#define LLVM_NODISCARD [[nodiscard]]
-#elif LLVM_HAS_CPP_ATTRIBUTE(clang::warn_unused_result)
-#define LLVM_NODISCARD [[clang::warn_unused_result]]
-// Clang in C++14 mode claims that it has the 'nodiscard' attribute, but also
-// warns in the pedantic mode that 'nodiscard' is a C++17 extension (PR33518).
-// Use the 'nodiscard' attribute in C++14 mode only with GCC.
-// TODO: remove this workaround when PR33518 is resolved.
-#elif defined(__GNUC__) && LLVM_HAS_CPP_ATTRIBUTE(nodiscard)
-#define LLVM_NODISCARD [[nodiscard]]
+#if defined(__clang__)
+#define LLVM_DEPRECATED(MSG, FIX) __attribute__((deprecated(MSG, FIX)))
 #else
-#define LLVM_NODISCARD
-#endif
+#define LLVM_DEPRECATED(MSG, FIX) [[deprecated(MSG)]]
 #endif
 
 // Indicate that a non-static, non-const C++ member function reinitializes
@@ -356,23 +340,18 @@
 #endif
 #endif
 
-// LLVM_ATTRIBUTE_DEPRECATED(decl, "message")
-// This macro will be removed.
-// Use C++14's attribute instead: [[deprecated("message")]]
-#ifndef LLVM_ATTRIBUTE_DEPRECATED
-#define LLVM_ATTRIBUTE_DEPRECATED(decl, message) [[deprecated(message)]] decl
-#endif
-
 /// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands
 /// to an expression which states that it is undefined behavior for the
 /// compiler to reach this point.  Otherwise is not defined.
+///
+/// '#else' is intentionally left out so that other macro logic (e.g.,
+/// LLVM_ASSUME_ALIGNED and wpi_unreachable()) can detect whether
+/// LLVM_BUILTIN_UNREACHABLE has a definition.
 #ifndef LLVM_BUILTIN_UNREACHABLE
 #if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
 # define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable()
 #elif defined(_MSC_VER)
 # define LLVM_BUILTIN_UNREACHABLE __assume(false)
-#else
-# define LLVM_BUILTIN_UNREACHABLE
 #endif
 #endif
 
@@ -454,24 +433,6 @@
 #endif
 #endif
 
-/// \macro LLVM_PTR_SIZE
-/// A constant integer equivalent to the value of sizeof(void*).
-/// Generally used in combination with alignas or when doing computation in the
-/// preprocessor.
-#ifndef LLVM_PTR_SIZE
-#ifdef __SIZEOF_POINTER__
-# define LLVM_PTR_SIZE __SIZEOF_POINTER__
-#elif defined(_WIN64)
-# define LLVM_PTR_SIZE 8
-#elif defined(_WIN32)
-# define LLVM_PTR_SIZE 4
-#elif defined(_MSC_VER)
-# error "could not determine LLVM_PTR_SIZE as a constant int for MSVC"
-#else
-# define LLVM_PTR_SIZE sizeof(void *)
-#endif
-#endif
-
 /// \macro LLVM_MEMORY_SANITIZER_BUILD
 /// Whether LLVM itself is built with MemorySanitizer instrumentation.
 #if __has_feature(memory_sanitizer)
@@ -489,13 +450,34 @@
 /// Whether LLVM itself is built with AddressSanitizer instrumentation.
 #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
 # define LLVM_ADDRESS_SANITIZER_BUILD 1
+#if __has_include(<sanitizer/asan_interface.h>)
 # include <sanitizer/asan_interface.h>
 #else
+// These declarations exist to support ASan with MSVC. If MSVC eventually ships
+// asan_interface.h in their headers, then we can remove this.
+#ifdef __cplusplus
+extern "C" {
+#endif
+void __asan_poison_memory_region(void const volatile *addr, size_t size);
+void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+#endif
+#else
 # define LLVM_ADDRESS_SANITIZER_BUILD 0
 # define __asan_poison_memory_region(p, size)
 # define __asan_unpoison_memory_region(p, size)
 #endif
 
+/// \macro LLVM_HWADDRESS_SANITIZER_BUILD
+/// Whether LLVM itself is built with HWAddressSanitizer instrumentation.
+#if __has_feature(hwaddress_sanitizer)
+#define LLVM_HWADDRESS_SANITIZER_BUILD 1
+#else
+#define LLVM_HWADDRESS_SANITIZER_BUILD 0
+#endif
+
 /// \macro LLVM_THREAD_SANITIZER_BUILD
 /// Whether LLVM itself is built with ThreadSanitizer instrumentation.
 #if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ConvertUTF.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ConvertUTF.h
index 436bc6d..6153d81 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ConvertUTF.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ConvertUTF.h
@@ -6,25 +6,41 @@
  *
  *==------------------------------------------------------------------------==*/
 /*
- * Copyright 2001-2004 Unicode, Inc.
+ * Copyright © 1991-2015 Unicode, Inc. All rights reserved.
+ * Distributed under the Terms of Use in
+ * http://www.unicode.org/copyright.html.
  *
- * Disclaimer
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of the Unicode data files and any associated documentation
+ * (the "Data Files") or Unicode software and any associated documentation
+ * (the "Software") to deal in the Data Files or Software
+ * without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, and/or sell copies of
+ * the Data Files or Software, and to permit persons to whom the Data Files
+ * or Software are furnished to do so, provided that
+ * (a) this copyright and permission notice appear with all copies
+ * of the Data Files or Software,
+ * (b) this copyright and permission notice appear in associated
+ * documentation, and
+ * (c) there is clear notice in each modified Data File or in the Software
+ * as well as in the documentation associated with the Data File(s) or
+ * Software that the data or software has been modified.
  *
- * 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.
+ * THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+ * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+ * DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
  *
- * 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.
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale,
+ * use or other dealings in these Data Files or Software without prior
+ * written authorization of the copyright holder.
  */
 
 /* ---------------------------------------------------------------------
@@ -89,10 +105,9 @@
 #ifndef WPIUTIL_WPI_CONVERTUTF_H
 #define WPIUTIL_WPI_CONVERTUTF_H
 
-#include <span>
-
 #include <cstddef>
 #include <string>
+#include <span>
 #include <string_view>
 #include <system_error>
 
@@ -126,6 +141,9 @@
 #define UNI_UTF16_BYTE_ORDER_MARK_NATIVE  0xFEFF
 #define UNI_UTF16_BYTE_ORDER_MARK_SWAPPED 0xFFFE
 
+#define UNI_UTF32_BYTE_ORDER_MARK_NATIVE 0x0000FEFF
+#define UNI_UTF32_BYTE_ORDER_MARK_SWAPPED 0xFFFE0000
+
 typedef enum {
   conversionOK,           /* conversion successful */
   sourceExhausted,        /* partial character in source, but hit end */
@@ -178,6 +196,8 @@
 
 Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd);
 
+unsigned getUTF8SequenceSize(const UTF8 *source, const UTF8 *sourceEnd);
+
 unsigned getNumBytesForUTF8(UTF8 firstByte);
 
 /*************************************************************************/
@@ -280,6 +300,24 @@
 bool convertUTF16ToUTF8String(std::span<const UTF16> Src, SmallVectorImpl<char> &Out);
 
 /**
+ * Converts a stream of raw bytes assumed to be UTF32 into a UTF8 std::string.
+ *
+ * \param [in] SrcBytes A buffer of what is assumed to be UTF-32 encoded text.
+ * \param [out] Out Converted UTF-8 is stored here on success.
+ * \returns true on success
+ */
+bool convertUTF32ToUTF8String(std::span<const char> SrcBytes, std::string &Out);
+
+/**
+ * Converts a UTF32 string into a UTF8 std::string.
+ *
+ * \param [in] Src A buffer of UTF-32 encoded text.
+ * \param [out] Out Converted UTF-8 is stored here on success.
+ * \returns true on success
+ */
+bool convertUTF32ToUTF8String(std::span<const UTF32> Src, std::string &Out);
+
+/**
  * Converts a UTF-8 string into a UTF-16 string with native endianness.
  *
  * \returns true on success
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DJB.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DJB.h
deleted file mode 100644
index 2615bba..0000000
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DJB.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//===-- llvm/Support/DJB.h ---DJB Hash --------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file contains support for the DJ Bernstein hash function.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_DJB_H
-#define WPIUTIL_WPI_DJB_H
-
-#include <string_view>
-
-namespace wpi {
-
-/// The Bernstein hash function used by the DWARF accelerator tables.
-inline uint32_t djbHash(std::string_view Buffer, uint32_t H = 5381) {
-  for (unsigned char C : Buffer)
-    H = (H << 5) + H + C;
-  return H;
-}
-
-} // namespace wpi
-
-#endif // WPIUTIL_WPI_DJB_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMap.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMap.h
index b1ffd43..2b872de 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMap.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMap.h
@@ -23,6 +23,7 @@
 #include "wpi/ReverseIteration.h"
 #include "wpi/type_traits.h"
 #include <algorithm>
+#include <bit>
 #include <cassert>
 #include <cstddef>
 #include <cstring>
@@ -95,9 +96,7 @@
     return makeConstIterator(getBucketsEnd(), getBucketsEnd(), *this, true);
   }
 
-  LLVM_NODISCARD bool empty() const {
-    return getNumEntries() == 0;
-  }
+  [[nodiscard]] bool empty() const { return getNumEntries() == 0; }
   unsigned size() const { return getNumEntries(); }
 
   /// Grow the densemap so that it can contain at least \p NumEntries items
@@ -126,7 +125,7 @@
       for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P)
         P->getFirst() = EmptyKey;
     } else {
-      unsigned NumEntries = getNumEntries();
+      [[maybe_unused]] unsigned NumEntries = getNumEntries();
       for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) {
         if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey)) {
           if (!KeyInfoT::isEqual(P->getFirst(), TombstoneKey)) {
@@ -137,15 +136,21 @@
         }
       }
       assert(NumEntries == 0 && "Node count imbalance!");
+      (void)NumEntries;
     }
     setNumEntries(0);
     setNumTombstones(0);
   }
 
+  /// Return true if the specified key is in the map, false otherwise.
+  bool contains(const_arg_type_t<KeyT> Val) const {
+    const BucketT *TheBucket;
+    return LookupBucketFor(Val, TheBucket);
+  }
+
   /// Return 1 if the specified key is in the map, 0 otherwise.
   size_type count(const_arg_type_t<KeyT> Val) const {
-    const BucketT *TheBucket;
-    return LookupBucketFor(Val, TheBucket) ? 1 : 0;
+    return contains(Val) ? 1 : 0;
   }
 
   iterator find(const_arg_type_t<KeyT> Val) {
@@ -202,6 +207,14 @@
     return ValueT();
   }
 
+  /// at - Return the entry for the specified key, or abort if no such
+  /// entry exists.
+  const ValueT &at(const_arg_type_t<KeyT> Val) const {
+    auto Iter = this->find(std::move(Val));
+    assert(Iter != this->end() && "DenseMap::at failed due to a missing key");
+    return Iter->second;
+  }
+
   // Inserts key,value pair into the map if the key isn't already in the map.
   // If the key is already in the map, it returns false and doesn't update the
   // value.
@@ -300,6 +313,20 @@
       insert(*I);
   }
 
+  /// Returns the value associated to the key in the map if it exists. If it
+  /// does not exist, emplace a default value for the key and returns a
+  /// reference to the newly created value.
+  ValueT &getOrInsertDefault(KeyT &&Key) {
+    return try_emplace(Key).first->second;
+  }
+
+  /// Returns the value associated to the key in the map if it exists. If it
+  /// does not exist, emplace a default value for the key and returns a
+  /// reference to the newly created value.
+  ValueT &getOrInsertDefault(const KeyT &Key) {
+    return try_emplace(Key).first->second;
+  }
+
   bool erase(const KeyT &Val) {
     BucketT *TheBucket;
     if (!LookupBucketFor(Val, TheBucket))
@@ -906,6 +933,8 @@
 
 public:
   explicit SmallDenseMap(unsigned NumInitBuckets = 0) {
+    if (NumInitBuckets > InlineBuckets)
+      NumInitBuckets = std::bit_ceil(NumInitBuckets);
     init(NumInitBuckets);
   }
 
@@ -1196,8 +1225,7 @@
 
 public:
   using difference_type = ptrdiff_t;
-  using value_type =
-      typename std::conditional<IsConst, const Bucket, Bucket>::type;
+  using value_type = std::conditional_t<IsConst, const Bucket, Bucket>;
   using pointer = value_type *;
   using reference = value_type &;
   using iterator_category = std::forward_iterator_tag;
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMapInfo.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMapInfo.h
index 0ff8fc9..9e939ef 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMapInfo.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMapInfo.h
@@ -18,6 +18,7 @@
 #include <cstddef>
 #include <cstdint>
 #include <tuple>
+#include <type_traits>
 #include <utility>
 
 namespace wpi {
@@ -232,6 +233,14 @@
                                     SecondInfo::getHashValue(PairVal.second));
   }
 
+  // Expose an additional function intended to be used by other
+  // specializations of DenseMapInfo without needing to know how
+  // to combine hash values manually
+  static unsigned getHashValuePiecewise(const T &First, const U &Second) {
+    return detail::combineHashValue(FirstInfo::getHashValue(First),
+                                    SecondInfo::getHashValue(Second));
+  }
+
   static bool isEqual(const Pair &LHS, const Pair &RHS) {
     return FirstInfo::isEqual(LHS.first, RHS.first) &&
            SecondInfo::isEqual(LHS.second, RHS.second);
@@ -252,7 +261,7 @@
 
   template <unsigned I>
   static unsigned getHashValueImpl(const Tuple &values, std::false_type) {
-    using EltType = typename std::tuple_element<I, Tuple>::type;
+    using EltType = std::tuple_element_t<I, Tuple>;
     std::integral_constant<bool, I + 1 == sizeof...(Ts)> atEnd;
     return detail::combineHashValue(
         DenseMapInfo<EltType>::getHashValue(std::get<I>(values)),
@@ -271,7 +280,7 @@
 
   template <unsigned I>
   static bool isEqualImpl(const Tuple &lhs, const Tuple &rhs, std::false_type) {
-    using EltType = typename std::tuple_element<I, Tuple>::type;
+    using EltType = std::tuple_element_t<I, Tuple>;
     std::integral_constant<bool, I + 1 == sizeof...(Ts)> atEnd;
     return DenseMapInfo<EltType>::isEqual(std::get<I>(lhs), std::get<I>(rhs)) &&
            isEqualImpl<I + 1>(lhs, rhs, atEnd);
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMapInfoVariant.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMapInfoVariant.h
new file mode 100644
index 0000000..b2d9739
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/DenseMapInfoVariant.h
@@ -0,0 +1,71 @@
+//===- DenseMapInfoVariant.h - Type traits for DenseMap<variant> *- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines DenseMapInfo traits for DenseMap<std::variant<Ts...>>.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef WPIUTIL_WPI_DENSEMAPINFOVARIANT_H
+#define WPIUTIL_WPI_DENSEMAPINFOVARIANT_H
+
+#include "wpi/DenseMapInfo.h"
+#include <utility>
+#include <variant>
+
+namespace wpi {
+
+// Provide DenseMapInfo for variants whose all alternatives have DenseMapInfo.
+template <typename... Ts> struct DenseMapInfo<std::variant<Ts...>> {
+  using Variant = std::variant<Ts...>;
+  using FirstT = std::variant_alternative_t<0, Variant>;
+
+  static inline Variant getEmptyKey() {
+    return Variant(std::in_place_index<0>, DenseMapInfo<FirstT>::getEmptyKey());
+  }
+
+  static inline Variant getTombstoneKey() {
+    return Variant(std::in_place_index<0>,
+                   DenseMapInfo<FirstT>::getTombstoneKey());
+  }
+
+  static unsigned getHashValue(const Variant &Val) {
+    return std::visit(
+        [&Val](auto &&Alternative) {
+          using T = std::decay_t<decltype(Alternative)>;
+          // Include index in hash to make sure same value as different
+          // alternatives don't collide.
+          return DenseMapInfo<std::pair<size_t, T>>::getHashValuePiecewise(
+              Val.index(), Alternative);
+        },
+        Val);
+  }
+
+  static bool isEqual(const Variant &LHS, const Variant &RHS) {
+    if (LHS.index() != RHS.index())
+      return false;
+    if (LHS.valueless_by_exception())
+      return true;
+    // We want to dispatch to DenseMapInfo<T>::isEqual(LHS.get(I), RHS.get(I))
+    // We know the types are the same, but std::visit(V, LHS, RHS) doesn't.
+    // We erase the type held in LHS to void*, and dispatch over RHS.
+    const void *ErasedLHS =
+        std::visit([](const auto &LHS) -> const void * { return &LHS; }, LHS);
+    return std::visit(
+        [&](const auto &RHS) -> bool {
+          using T = std::remove_cv_t<std::remove_reference_t<decltype(RHS)>>;
+          return DenseMapInfo<T>::isEqual(*static_cast<const T *>(ErasedLHS),
+                                          RHS);
+        },
+        RHS);
+  }
+};
+
+} // end namespace wpi
+
+#endif // WPIUTIL_WPI_DENSEMAPINFOVARIANT_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/EpochTracker.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/EpochTracker.h
index 6417c05..8cb122a 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/EpochTracker.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/EpochTracker.h
@@ -22,6 +22,7 @@
 namespace wpi {
 
 #ifndef NDEBUG //ifndef LLVM_ENABLE_ABI_BREAKING_CHECKS
+#define LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE
 
 /// A base class for data structure classes wishing to make iterators
 /// ("handles") pointing into themselves fail-fast.  When building without
@@ -33,10 +34,10 @@
 /// is still valid.
 ///
 class DebugEpochBase {
-  uint64_t Epoch;
+  uint64_t Epoch = 0;
 
 public:
-  DebugEpochBase() : Epoch(0) {}
+  DebugEpochBase() = default;
 
   /// Calling incrementEpoch invalidates all handles pointing into the
   /// calling instance.
@@ -55,11 +56,11 @@
   /// make an iterator-invalidating modification.
   ///
   class HandleBase {
-    const uint64_t *EpochAddress;
-    uint64_t EpochAtCreation;
+    const uint64_t *EpochAddress = nullptr;
+    uint64_t EpochAtCreation = UINT64_MAX;
 
   public:
-    HandleBase() : EpochAddress(nullptr), EpochAtCreation(UINT64_MAX) {}
+    HandleBase() = default;
 
     explicit HandleBase(const DebugEpochBase *Parent)
         : EpochAddress(&Parent->Epoch), EpochAtCreation(Parent->Epoch) {}
@@ -77,6 +78,11 @@
 };
 
 #else
+#ifdef _MSC_VER
+#define LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE __declspec(empty_bases)
+#else
+#define LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE
+#endif // _MSC_VER
 
 class DebugEpochBase {
 public:
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Errno.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Errno.h
index febfc37..a01ebda 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Errno.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Errno.h
@@ -15,7 +15,6 @@
 
 #include <cerrno>
 #include <string>
-#include <type_traits>
 
 namespace wpi {
 namespace sys {
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ErrorHandling.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ErrorHandling.h
index 7b43671..abc9d7d 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ErrorHandling.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ErrorHandling.h
@@ -44,7 +44,7 @@
   void install_fatal_error_handler(fatal_error_handler_t handler,
                                    void *user_data = nullptr);
 
-  /// Restores default error handling behavior.
+  /// Restores default error handling behaviour.
   void remove_fatal_error_handler();
 
   /// ScopedFatalErrorHandler - This is a simple helper class which just
@@ -123,19 +123,34 @@
 
 /// Marks that the current location is not supposed to be reachable.
 /// In !NDEBUG builds, prints the message and location info to stderr.
-/// In NDEBUG builds, becomes an optimizer hint that the current location
-/// is not supposed to be reachable.  On compilers that don't support
-/// such hints, prints a reduced message instead and aborts the program.
+/// In NDEBUG builds, if the platform does not support a builtin unreachable
+/// then we call an internal LLVM runtime function. Otherwise the behavior is
+/// controlled by the CMake flag
+///   -DLLVM_UNREACHABLE_OPTIMIZE
+/// * When "ON" (default) wpi_unreachable() becomes an optimizer hint
+///   that the current location is not supposed to be reachable: the hint
+///   turns such code path into undefined behavior.  On compilers that don't
+///   support such hints, prints a reduced message instead and aborts the
+///   program.
+/// * When "OFF", a builtin_trap is emitted instead of an
+//    optimizer hint or printing a reduced message.
 ///
-/// Use this instead of assert(0).  It conveys intent more clearly and
-/// allows compilers to omit some unnecessary code.
+/// Use this instead of assert(0). It conveys intent more clearly, suppresses
+/// diagnostics for unreachable code paths, and allows compilers to omit
+/// unnecessary code.
 #ifndef NDEBUG
 #define wpi_unreachable(msg) \
   ::wpi::wpi_unreachable_internal(msg, __FILE__, __LINE__)
-#elif defined(LLVM_BUILTIN_UNREACHABLE)
+#elif !defined(LLVM_BUILTIN_UNREACHABLE)
+#define wpi_unreachable(msg) ::wpi::wpi_unreachable_internal()
+#elif LLVM_UNREACHABLE_OPTIMIZE
 #define wpi_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
 #else
-#define wpi_unreachable(msg) ::wpi::wpi_unreachable_internal()
+#define wpi_unreachable(msg)                                                  \
+  do {                                                                         \
+    LLVM_BUILTIN_TRAP;                                                         \
+    LLVM_BUILTIN_UNREACHABLE;                                                  \
+  } while (false)
 #endif
 
 #endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/FunctionExtras.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/FunctionExtras.h
index 681b87e..8c6d847 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/FunctionExtras.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/FunctionExtras.h
@@ -37,6 +37,7 @@
 #include "wpi/STLForwardCompat.h"
 #include "wpi/MemAlloc.h"
 #include "wpi/type_traits.h"
+#include <cstddef>
 #include <cstring>
 #include <memory>
 #include <type_traits>
@@ -66,13 +67,13 @@
 
 template <typename T>
 using EnableIfTrivial =
-    std::enable_if_t<wpi::is_trivially_move_constructible<T>::value &&
+    std::enable_if_t<std::is_trivially_move_constructible<T>::value &&
                      std::is_trivially_destructible<T>::value>;
 template <typename CallableT, typename ThisT>
 using EnableUnlessSameType =
     std::enable_if_t<!std::is_same<remove_cvref_t<CallableT>, ThisT>::value>;
 template <typename CallableT, typename Ret, typename... Params>
-using EnableIfCallable = std::enable_if_t<wpi::disjunction<
+using EnableIfCallable = std::enable_if_t<std::disjunction<
     std::is_void<Ret>,
     std::is_same<decltype(std::declval<CallableT>()(std::declval<Params>()...)),
                  Ret>,
@@ -106,11 +107,11 @@
   template <typename T> struct AdjustedParamTBase {
     static_assert(!std::is_reference<T>::value,
                   "references should be handled by template specialization");
-    using type = typename std::conditional<
-        wpi::is_trivially_copy_constructible<T>::value &&
-            wpi::is_trivially_move_constructible<T>::value &&
-            IsSizeLessThanThresholdT<T>::value,
-        T, T &>::type;
+    using type =
+        std::conditional_t<std::is_trivially_copy_constructible<T>::value &&
+                               std::is_trivially_move_constructible<T>::value &&
+                               IsSizeLessThanThresholdT<T>::value,
+                           T, T &>;
   };
 
   // This specialization ensures that 'AdjustedParam<V<T>&>' or
@@ -167,9 +168,7 @@
     // provide four pointers worth of storage here.
     // This is mutable as an inlined `const unique_function<void() const>` may
     // still modify its own mutable members.
-    mutable
-        typename std::aligned_storage<InlineStorageSize, alignof(void *)>::type
-            InlineStorage;
+    alignas(void*) mutable std::byte InlineStorage[InlineStorageSize];
   } StorageUnion;
 
   // A compressed pointer to either our dispatching callback or our table of
@@ -180,16 +179,15 @@
   bool isInlineStorage() const { return CallbackAndInlineFlag.getInt(); }
 
   bool isTrivialCallback() const {
-    return CallbackAndInlineFlag.getPointer().template is<TrivialCallback *>();
+    return isa<TrivialCallback *>(CallbackAndInlineFlag.getPointer());
   }
 
   CallPtrT getTrivialCallback() const {
-    return CallbackAndInlineFlag.getPointer().template get<TrivialCallback *>()->CallPtr;
+    return cast<TrivialCallback *>(CallbackAndInlineFlag.getPointer())->CallPtr;
   }
 
   NonTrivialCallbacks *getNonTrivialCallbacks() const {
-    return CallbackAndInlineFlag.getPointer()
-        .template get<NonTrivialCallbacks *>();
+    return cast<NonTrivialCallbacks *>(CallbackAndInlineFlag.getPointer());
   }
 
   CallPtrT getCallPtr() const {
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Hashing.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Hashing.h
index 0d4a4c6..bdb439c 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Hashing.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/Hashing.h
@@ -48,8 +48,10 @@
 #include "wpi/SwapByteOrder.h"
 #include "wpi/type_traits.h"
 #include <algorithm>
+#include <bit>
 #include <cassert>
 #include <cstring>
+#include <optional>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -126,6 +128,8 @@
 template <typename T>
 hash_code hash_value(const std::basic_string<T> &arg);
 
+/// Compute a hash_code for a standard string.
+template <typename T> hash_code hash_value(const std::optional<T> &arg);
 
 /// Override the execution seed with a fixed value.
 ///
@@ -220,29 +224,30 @@
   uint64_t b = fetch64(s + 8);
   uint64_t c = fetch64(s + len - 8) * k2;
   uint64_t d = fetch64(s + len - 16) * k0;
-  return hash_16_bytes(rotate(a - b, 43) + rotate(c ^ seed, 30) + d,
-                       a + rotate(b ^ k3, 20) - c + len + seed);
+  return hash_16_bytes(std::rotr<uint64_t>(a - b, 43) +
+                           std::rotr<uint64_t>(c ^ seed, 30) + d,
+                       a + std::rotr<uint64_t>(b ^ k3, 20) - c + len + seed);
 }
 
 inline uint64_t hash_33to64_bytes(const char *s, size_t len, uint64_t seed) {
   uint64_t z = fetch64(s + 24);
   uint64_t a = fetch64(s) + (len + fetch64(s + len - 16)) * k0;
-  uint64_t b = rotate(a + z, 52);
-  uint64_t c = rotate(a, 37);
+  uint64_t b = std::rotr<uint64_t>(a + z, 52);
+  uint64_t c = std::rotr<uint64_t>(a, 37);
   a += fetch64(s + 8);
-  c += rotate(a, 7);
+  c += std::rotr<uint64_t>(a, 7);
   a += fetch64(s + 16);
   uint64_t vf = a + z;
-  uint64_t vs = b + rotate(a, 31) + c;
+  uint64_t vs = b + std::rotr<uint64_t>(a, 31) + c;
   a = fetch64(s + 16) + fetch64(s + len - 32);
   z = fetch64(s + len - 8);
-  b = rotate(a + z, 52);
-  c = rotate(a, 37);
+  b = std::rotr<uint64_t>(a + z, 52);
+  c = std::rotr<uint64_t>(a, 37);
   a += fetch64(s + len - 24);
-  c += rotate(a, 7);
+  c += std::rotr<uint64_t>(a, 7);
   a += fetch64(s + len - 16);
   uint64_t wf = a + z;
-  uint64_t ws = b + rotate(a, 31) + c;
+  uint64_t ws = b + std::rotr<uint64_t>(a, 31) + c;
   uint64_t r = shift_mix((vf + ws) * k2 + (wf + vs) * k0);
   return shift_mix((seed ^ (r * k0)) + vs) * k2;
 }
@@ -272,9 +277,13 @@
   /// seed and the first 64-byte chunk.
   /// This effectively performs the initial mix.
   static hash_state create(const char *s, uint64_t seed) {
-    hash_state state = {
-      0, seed, hash_16_bytes(seed, k1), rotate(seed ^ k1, 49),
-      seed * k1, shift_mix(seed), 0 };
+    hash_state state = {0,
+                        seed,
+                        hash_16_bytes(seed, k1),
+                        std::rotr<uint64_t>(seed ^ k1, 49),
+                        seed * k1,
+                        shift_mix(seed),
+                        0};
     state.h6 = hash_16_bytes(state.h4, state.h5);
     state.mix(s);
     return state;
@@ -285,10 +294,10 @@
   static void mix_32_bytes(const char *s, uint64_t &a, uint64_t &b) {
     a += fetch64(s);
     uint64_t c = fetch64(s + 24);
-    b = rotate(b + a + c, 21);
+    b = std::rotr<uint64_t>(b + a + c, 21);
     uint64_t d = a;
     a += fetch64(s + 8) + fetch64(s + 16);
-    b += rotate(a, 44) + d;
+    b += std::rotr<uint64_t>(a, 44) + d;
     a += c;
   }
 
@@ -296,11 +305,11 @@
   /// We mix all 64 bytes even when the chunk length is smaller, but we
   /// record the actual length.
   void mix(const char *s) {
-    h0 = rotate(h0 + h1 + h3 + fetch64(s + 8), 37) * k1;
-    h1 = rotate(h1 + h4 + fetch64(s + 48), 42) * k1;
+    h0 = std::rotr<uint64_t>(h0 + h1 + h3 + fetch64(s + 8), 37) * k1;
+    h1 = std::rotr<uint64_t>(h1 + h4 + fetch64(s + 48), 42) * k1;
     h0 ^= h6;
     h1 += h3 + fetch64(s + 40);
-    h2 = rotate(h2 + h5, 33) * k1;
+    h2 = std::rotr<uint64_t>(h2 + h5, 33) * k1;
     h3 = h4 * k1;
     h4 = h0 + h5;
     mix_32_bytes(s, h3, h4);
@@ -655,24 +664,8 @@
   return hash_combine(arg.first, arg.second);
 }
 
-// Implementation details for the hash_value overload for std::tuple<...>(...).
-namespace hashing {
-namespace detail {
-
-template <typename... Ts, std::size_t... Indices>
-hash_code hash_value_tuple_helper(const std::tuple<Ts...> &arg,
-                                  std::index_sequence<Indices...>) {
-  return hash_combine(std::get<Indices>(arg)...);
-}
-
-} // namespace detail
-} // namespace hashing
-
-template <typename... Ts>
-hash_code hash_value(const std::tuple<Ts...> &arg) {
-  // TODO: Use std::apply when LLVM starts using C++17.
-  return ::wpi::hashing::detail::hash_value_tuple_helper(
-      arg, typename std::index_sequence_for<Ts...>());
+template <typename... Ts> hash_code hash_value(const std::tuple<Ts...> &arg) {
+  return std::apply([](const auto &...xs) { return hash_combine(xs...); }, arg);
 }
 
 // Declared and documented above, but defined here so that any of the hashing
@@ -682,6 +675,10 @@
   return hash_combine_range(arg.begin(), arg.end());
 }
 
+template <typename T> hash_code hash_value(const std::optional<T> &arg) {
+  return arg ? hash_combine(true, *arg) : hash_value(false);
+}
+
 template <> struct DenseMapInfo<hash_code, void> {
   static inline hash_code getEmptyKey() { return hash_code(-1); }
   static inline hash_code getTombstoneKey() { return hash_code(-2); }
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MapVector.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MapVector.h
index e4706c7..ce6fe78 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MapVector.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MapVector.h
@@ -10,7 +10,7 @@
 /// This file implements a map that provides insertion order iteration. The
 /// interface is purposefully minimal. The key is assumed to be cheap to copy
 /// and 2 copies are kept, one for indexing in a DenseMap, one for iteration in
-/// a std::vector.
+/// a SmallVector.
 ///
 //===----------------------------------------------------------------------===//
 
@@ -24,22 +24,21 @@
 #include <iterator>
 #include <type_traits>
 #include <utility>
-#include <vector>
 
 namespace wpi {
 
 /// This class implements a map that also provides access to all stored values
-/// in a deterministic order. The values are kept in a std::vector and the
+/// in a deterministic order. The values are kept in a SmallVector<*, 0> and the
 /// mapping is done with DenseMap from Keys to indexes in that vector.
-template<typename KeyT, typename ValueT,
-         typename MapType = DenseMap<KeyT, unsigned>,
-         typename VectorType = std::vector<std::pair<KeyT, ValueT>>>
+template <typename KeyT, typename ValueT,
+          typename MapType = DenseMap<KeyT, unsigned>,
+          typename VectorType = SmallVector<std::pair<KeyT, ValueT>, 0>>
 class MapVector {
   MapType Map;
   VectorType Vector;
 
   static_assert(
-      std::is_integral<typename MapType::mapped_type>::value,
+      std::is_integral_v<typename MapType::mapped_type>,
       "The mapped_type of the specified Map must be an integral type");
 
 public:
@@ -109,7 +108,7 @@
 
   // Returns a copy of the value.  Only allowed if ValueT is copyable.
   ValueT lookup(const KeyT &Key) const {
-    static_assert(std::is_copy_constructible<ValueT>::value,
+    static_assert(std::is_copy_constructible_v<ValueT>,
                   "Cannot call lookup() if ValueT is not copyable.");
     typename MapType::const_iterator Pos = Map.find(Key);
     return Pos == Map.end()? ValueT() : Vector[Pos->second].second;
@@ -140,10 +139,9 @@
     return std::make_pair(begin() + I, false);
   }
 
-  size_type count(const KeyT &Key) const {
-    typename MapType::const_iterator Pos = Map.find(Key);
-    return Pos == Map.end()? 0 : 1;
-  }
+  bool contains(const KeyT &Key) const { return Map.find(Key) != Map.end(); }
+
+  size_type count(const KeyT &Key) const { return contains(Key) ? 1 : 0; }
 
   iterator find(const KeyT &Key) {
     typename MapType::const_iterator Pos = Map.find(Key);
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MathExtras.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MathExtras.h
index ea669ee..ed3b2b1 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MathExtras.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MathExtras.h
@@ -13,204 +13,22 @@
 #ifndef WPIUTIL_WPI_MATHEXTRAS_H
 #define WPIUTIL_WPI_MATHEXTRAS_H
 
+#include "wpi/bit.h"
 #include "wpi/Compiler.h"
+#include <bit>
 #include <cassert>
 #include <climits>
-#include <cmath>
 #include <cstdint>
 #include <cstring>
 #include <limits>
 #include <type_traits>
 
-#ifdef __ANDROID_NDK__
-#include <android/api-level.h>
-#endif
-
-#ifdef _MSC_VER
-// Declare these intrinsics manually rather including intrin.h. It's very
-// expensive, and MathExtras.h is popular.
-// #include <intrin.h>
-extern "C" {
-unsigned char _BitScanForward(unsigned long *_Index, unsigned long _Mask);
-unsigned char _BitScanForward64(unsigned long *_Index, unsigned __int64 _Mask);
-unsigned char _BitScanReverse(unsigned long *_Index, unsigned long _Mask);
-unsigned char _BitScanReverse64(unsigned long *_Index, unsigned __int64 _Mask);
-}
-#endif
-
 namespace wpi {
 
-/// The behavior an operation has on an input of 0.
-enum ZeroBehavior {
-  /// The returned value is undefined.
-  ZB_Undefined,
-  /// The returned value is numeric_limits<T>::max()
-  ZB_Max,
-  /// The returned value is numeric_limits<T>::digits
-  ZB_Width
-};
-
-namespace detail {
-template <typename T, std::size_t SizeOfT> struct TrailingZerosCounter {
-  static unsigned count(T Val, ZeroBehavior) {
-    if (!Val)
-      return std::numeric_limits<T>::digits;
-    if (Val & 0x1)
-      return 0;
-
-    // Bisection method.
-    unsigned ZeroBits = 0;
-    T Shift = std::numeric_limits<T>::digits >> 1;
-    T Mask = (std::numeric_limits<T>::max)() >> Shift;
-    while (Shift) {
-      if ((Val & Mask) == 0) {
-        Val >>= Shift;
-        ZeroBits |= Shift;
-      }
-      Shift >>= 1;
-      Mask >>= Shift;
-    }
-    return ZeroBits;
-  }
-};
-
-#if defined(__GNUC__) || defined(_MSC_VER)
-template <typename T> struct TrailingZerosCounter<T, 4> {
-  static unsigned count(T Val, ZeroBehavior ZB) {
-    if (ZB != ZB_Undefined && Val == 0)
-      return 32;
-
-#if __has_builtin(__builtin_ctz) || defined(__GNUC__)
-    return __builtin_ctz(Val);
-#elif defined(_MSC_VER)
-    unsigned long Index;
-    _BitScanForward(&Index, Val);
-    return Index;
-#endif
-  }
-};
-
-#if !defined(_MSC_VER) || defined(_M_X64)
-template <typename T> struct TrailingZerosCounter<T, 8> {
-  static unsigned count(T Val, ZeroBehavior ZB) {
-    if (ZB != ZB_Undefined && Val == 0)
-      return 64;
-
-#if __has_builtin(__builtin_ctzll) || defined(__GNUC__)
-    return __builtin_ctzll(Val);
-#elif defined(_MSC_VER)
-    unsigned long Index;
-    _BitScanForward64(&Index, Val);
-    return Index;
-#endif
-  }
-};
-#endif
-#endif
-} // namespace detail
-
-/// Count number of 0's from the least significant bit to the most
-///   stopping at the first 1.
-///
-/// Only unsigned integral types are allowed.
-///
-/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
-///   valid arguments.
-template <typename T>
-unsigned countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
-  static_assert(std::numeric_limits<T>::is_integer &&
-                    !std::numeric_limits<T>::is_signed,
-                "Only unsigned integral types are allowed.");
-  return wpi::detail::TrailingZerosCounter<T, sizeof(T)>::count(Val, ZB);
-}
-
-namespace detail {
-template <typename T, std::size_t SizeOfT> struct LeadingZerosCounter {
-  static unsigned count(T Val, ZeroBehavior) {
-    if (!Val)
-      return std::numeric_limits<T>::digits;
-
-    // Bisection method.
-    unsigned ZeroBits = 0;
-    for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) {
-      T Tmp = Val >> Shift;
-      if (Tmp)
-        Val = Tmp;
-      else
-        ZeroBits |= Shift;
-    }
-    return ZeroBits;
-  }
-};
-
-#if defined(__GNUC__) || defined(_MSC_VER)
-template <typename T> struct LeadingZerosCounter<T, 4> {
-  static unsigned count(T Val, ZeroBehavior ZB) {
-    if (ZB != ZB_Undefined && Val == 0)
-      return 32;
-
-#if __has_builtin(__builtin_clz) || defined(__GNUC__)
-    return __builtin_clz(Val);
-#elif defined(_MSC_VER)
-    unsigned long Index;
-    _BitScanReverse(&Index, Val);
-    return Index ^ 31;
-#endif
-  }
-};
-
-#if !defined(_MSC_VER) || defined(_M_X64)
-template <typename T> struct LeadingZerosCounter<T, 8> {
-  static unsigned count(T Val, ZeroBehavior ZB) {
-    if (ZB != ZB_Undefined && Val == 0)
-      return 64;
-
-#if __has_builtin(__builtin_clzll) || defined(__GNUC__)
-    return __builtin_clzll(Val);
-#elif defined(_MSC_VER)
-    unsigned long Index;
-    _BitScanReverse64(&Index, Val);
-    return Index ^ 63;
-#endif
-  }
-};
-#endif
-#endif
-} // namespace detail
-
-/// Count number of 0's from the most significant bit to the least
-///   stopping at the first 1.
-///
-/// Only unsigned integral types are allowed.
-///
-/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
-///   valid arguments.
-template <typename T>
-unsigned countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
-  static_assert(std::numeric_limits<T>::is_integer &&
-                    !std::numeric_limits<T>::is_signed,
-                "Only unsigned integral types are allowed.");
-  return wpi::detail::LeadingZerosCounter<T, sizeof(T)>::count(Val, ZB);
-}
-
-/// Get the index of the first set bit starting from the least
-///   significant bit.
-///
-/// Only unsigned integral types are allowed.
-///
-/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
-///   valid arguments.
-template <typename T> T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) {
-  if (ZB == ZB_Max && Val == 0)
-    return (std::numeric_limits<T>::max)();
-
-  return countTrailingZeros(Val, ZB_Undefined);
-}
-
 /// Create a bitmask with the N right-most bits set to 1, and all other
 /// bits set to 0.  Only unsigned types are allowed.
 template <typename T> T maskTrailingOnes(unsigned N) {
-  static_assert(std::is_unsigned<T>::value, "Invalid type!");
+  static_assert(std::is_unsigned_v<T>, "Invalid type!");
   const unsigned Bits = CHAR_BIT * sizeof(T);
   assert(N <= Bits && "Invalid bit index");
   return N == 0 ? 0 : (T(-1) >> (Bits - N));
@@ -234,23 +52,6 @@
   return maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
 }
 
-/// Get the index of the last set bit starting from the least
-///   significant bit.
-///
-/// Only unsigned integral types are allowed.
-///
-/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
-///   valid arguments.
-template <typename T> T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) {
-  if (ZB == ZB_Max && Val == 0)
-    return (std::numeric_limits<T>::max)();
-
-  // Use ^ instead of - because both gcc and llvm can remove the associated ^
-  // in the __builtin_clz intrinsic on x86.
-  return countLeadingZeros(Val, ZB_Undefined) ^
-         (std::numeric_limits<T>::digits - 1);
-}
-
 /// Macro compressed bit reversal table for 256 bits.
 ///
 /// http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
@@ -265,8 +66,24 @@
 };
 
 /// Reverse the bits in \p Val.
-template <typename T>
-T reverseBits(T Val) {
+template <typename T> T reverseBits(T Val) {
+#if __has_builtin(__builtin_bitreverse8)
+  if constexpr (std::is_same_v<T, uint8_t>)
+    return __builtin_bitreverse8(Val);
+#endif
+#if __has_builtin(__builtin_bitreverse16)
+  if constexpr (std::is_same_v<T, uint16_t>)
+    return __builtin_bitreverse16(Val);
+#endif
+#if __has_builtin(__builtin_bitreverse32)
+  if constexpr (std::is_same_v<T, uint32_t>)
+    return __builtin_bitreverse32(Val);
+#endif
+#if __has_builtin(__builtin_bitreverse64)
+  if constexpr (std::is_same_v<T, uint64_t>)
+    return __builtin_bitreverse64(Val);
+#endif
+
   unsigned char in[sizeof(Val)];
   unsigned char out[sizeof(Val)];
   std::memcpy(in, &Val, sizeof(Val));
@@ -276,34 +93,6 @@
   return Val;
 }
 
-#if __has_builtin(__builtin_bitreverse8)
-template<>
-inline uint8_t reverseBits<uint8_t>(uint8_t Val) {
-  return __builtin_bitreverse8(Val);
-}
-#endif
-
-#if __has_builtin(__builtin_bitreverse16)
-template<>
-inline uint16_t reverseBits<uint16_t>(uint16_t Val) {
-  return __builtin_bitreverse16(Val);
-}
-#endif
-
-#if __has_builtin(__builtin_bitreverse32)
-template<>
-inline uint32_t reverseBits<uint32_t>(uint32_t Val) {
-  return __builtin_bitreverse32(Val);
-}
-#endif
-
-#if __has_builtin(__builtin_bitreverse64)
-template<>
-inline uint64_t reverseBits<uint64_t>(uint64_t Val) {
-  return __builtin_bitreverse64(Val);
-}
-#endif
-
 // NOTE: The following support functions use the _32/_64 extensions instead of
 // type overloading so that signed and unsigned integers can be used without
 // ambiguity.
@@ -325,17 +114,16 @@
 
 /// Checks if an integer fits into the given bit width.
 template <unsigned N> constexpr inline bool isInt(int64_t x) {
-  return N >= 64 || (-(INT64_C(1)<<(N-1)) <= x && x < (INT64_C(1)<<(N-1)));
-}
-// Template specializations to get better code for common cases.
-template <> constexpr inline bool isInt<8>(int64_t x) {
-  return static_cast<int8_t>(x) == x;
-}
-template <> constexpr inline bool isInt<16>(int64_t x) {
-  return static_cast<int16_t>(x) == x;
-}
-template <> constexpr inline bool isInt<32>(int64_t x) {
-  return static_cast<int32_t>(x) == x;
+  if constexpr (N == 8)
+    return static_cast<int8_t>(x) == x;
+  if constexpr (N == 16)
+    return static_cast<int16_t>(x) == x;
+  if constexpr (N == 32)
+    return static_cast<int32_t>(x) == x;
+  if constexpr (N < 64)
+    return -(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1));
+  (void)x; // MSVC v19.25 warns that x is unused.
+  return true;
 }
 
 /// Checks if a signed integer is an N bit number shifted left by S.
@@ -348,34 +136,20 @@
 }
 
 /// Checks if an unsigned integer fits into the given bit width.
-///
-/// This is written as two functions rather than as simply
-///
-///   return N >= 64 || X < (UINT64_C(1) << N);
-///
-/// to keep MSVC from (incorrectly) warning on isUInt<64> that we're shifting
-/// left too many places.
-template <unsigned N>
-constexpr inline std::enable_if_t<(N < 64), bool> isUInt(uint64_t X) {
+template <unsigned N> constexpr inline bool isUInt(uint64_t x) {
   static_assert(N > 0, "isUInt<0> doesn't make sense");
-  return X < (UINT64_C(1) << (N));
-}
-template <unsigned N>
-constexpr inline std::enable_if_t<N >= 64, bool> isUInt(uint64_t) {
+  if constexpr (N == 8)
+    return static_cast<uint8_t>(x) == x;
+  if constexpr (N == 16)
+    return static_cast<uint16_t>(x) == x;
+  if constexpr (N == 32)
+    return static_cast<uint32_t>(x) == x;
+  if constexpr (N < 64)
+    return x < (UINT64_C(1) << (N));
+  (void)x; // MSVC v19.25 warns that x is unused.
   return true;
 }
 
-// Template specializations to get better code for common cases.
-template <> constexpr inline bool isUInt<8>(uint64_t x) {
-  return static_cast<uint8_t>(x) == x;
-}
-template <> constexpr inline bool isUInt<16>(uint64_t x) {
-  return static_cast<uint16_t>(x) == x;
-}
-template <> constexpr inline bool isUInt<32>(uint64_t x) {
-  return static_cast<uint32_t>(x) == x;
-}
-
 /// Checks if a unsigned integer is an N bit number shifted left by S.
 template <unsigned N, unsigned S>
 constexpr inline bool isShiftedUInt(uint64_t x) {
@@ -462,86 +236,39 @@
 /// Return true if the argument is a power of two > 0.
 /// Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.)
 constexpr inline bool isPowerOf2_32(uint32_t Value) {
-  return Value && !(Value & (Value - 1));
+  return std::has_single_bit(Value);
 }
 
 /// Return true if the argument is a power of two > 0 (64 bit edition.)
 constexpr inline bool isPowerOf2_64(uint64_t Value) {
-  return Value && !(Value & (Value - 1));
+  return std::has_single_bit(Value);
 }
 
-/// Count the number of ones from the most significant bit to the first
-/// zero bit.
-///
-/// Ex. countLeadingOnes(0xFF0FFF00) == 8.
-/// Only unsigned integral types are allowed.
-///
-/// \param ZB the behavior on an input of all ones. Only ZB_Width and
-/// ZB_Undefined are valid arguments.
-template <typename T>
-unsigned countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
-  static_assert(std::numeric_limits<T>::is_integer &&
-                    !std::numeric_limits<T>::is_signed,
-                "Only unsigned integral types are allowed.");
-  return countLeadingZeros<T>(~Value, ZB);
+/// Return true if the argument contains a non-empty sequence of ones with the
+/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true.
+/// If true, \p MaskIdx will specify the index of the lowest set bit and \p
+/// MaskLen is updated to specify the length of the mask, else neither are
+/// updated.
+inline bool isShiftedMask_32(uint32_t Value, unsigned &MaskIdx,
+                             unsigned &MaskLen) {
+  if (!isShiftedMask_32(Value))
+    return false;
+  MaskIdx = std::countr_zero(Value);
+  MaskLen = std::popcount(Value);
+  return true;
 }
 
-/// Count the number of ones from the least significant bit to the first
-/// zero bit.
-///
-/// Ex. countTrailingOnes(0x00FF00FF) == 8.
-/// Only unsigned integral types are allowed.
-///
-/// \param ZB the behavior on an input of all ones. Only ZB_Width and
-/// ZB_Undefined are valid arguments.
-template <typename T>
-unsigned countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
-  static_assert(std::numeric_limits<T>::is_integer &&
-                    !std::numeric_limits<T>::is_signed,
-                "Only unsigned integral types are allowed.");
-  return countTrailingZeros<T>(~Value, ZB);
-}
-
-namespace detail {
-template <typename T, std::size_t SizeOfT> struct PopulationCounter {
-  static unsigned count(T Value) {
-    // Generic version, forward to 32 bits.
-    static_assert(SizeOfT <= 4, "Not implemented!");
-#if defined(__GNUC__)
-    return __builtin_popcount(Value);
-#else
-    uint32_t v = Value;
-    v = v - ((v >> 1) & 0x55555555);
-    v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
-    return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
-#endif
-  }
-};
-
-template <typename T> struct PopulationCounter<T, 8> {
-  static unsigned count(T Value) {
-#if defined(__GNUC__)
-    return __builtin_popcountll(Value);
-#else
-    uint64_t v = Value;
-    v = v - ((v >> 1) & 0x5555555555555555ULL);
-    v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
-    v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
-    return unsigned((uint64_t)(v * 0x0101010101010101ULL) >> 56);
-#endif
-  }
-};
-} // namespace detail
-
-/// Count the number of set bits in a value.
-/// Ex. countPopulation(0xF000F000) = 8
-/// Returns 0 if the word is zero.
-template <typename T>
-inline unsigned countPopulation(T Value) {
-  static_assert(std::numeric_limits<T>::is_integer &&
-                    !std::numeric_limits<T>::is_signed,
-                "Only unsigned integral types are allowed.");
-  return detail::PopulationCounter<T, sizeof(T)>::count(Value);
+/// Return true if the argument contains a non-empty sequence of ones with the
+/// remainder zero (64 bit version.) If true, \p MaskIdx will specify the index
+/// of the lowest set bit and \p MaskLen is updated to specify the length of the
+/// mask, else neither are updated.
+inline bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx,
+                             unsigned &MaskLen) {
+  if (!isShiftedMask_64(Value))
+    return false;
+  MaskIdx = std::countr_zero(Value);
+  MaskLen = std::popcount(Value);
+  return true;
 }
 
 /// Compile time Log2.
@@ -554,90 +281,30 @@
 
 template <> constexpr inline size_t CTLog2<1>() { return 0; }
 
-/// Return the log base 2 of the specified value.
-inline double Log2(double Value) {
-#if defined(__ANDROID_API__) && __ANDROID_API__ < 18
-  return __builtin_log(Value) / __builtin_log(2.0);
-#else
-  return std::log2(Value);
-#endif
-}
-
 /// Return the floor log base 2 of the specified value, -1 if the value is zero.
 /// (32 bit edition.)
 /// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
 inline unsigned Log2_32(uint32_t Value) {
-  return static_cast<unsigned>(31 - countLeadingZeros(Value));
+  return static_cast<unsigned>(31 - std::countl_zero(Value));
 }
 
 /// Return the floor log base 2 of the specified value, -1 if the value is zero.
 /// (64 bit edition.)
 inline unsigned Log2_64(uint64_t Value) {
-  return static_cast<unsigned>(63 - countLeadingZeros(Value));
+  return static_cast<unsigned>(63 - std::countl_zero(Value));
 }
 
 /// Return the ceil log base 2 of the specified value, 32 if the value is zero.
 /// (32 bit edition).
 /// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
 inline unsigned Log2_32_Ceil(uint32_t Value) {
-  return static_cast<unsigned>(32 - countLeadingZeros(Value - 1));
+  return static_cast<unsigned>(32 - std::countl_zero(Value - 1));
 }
 
 /// Return the ceil log base 2 of the specified value, 64 if the value is zero.
 /// (64 bit edition.)
 inline unsigned Log2_64_Ceil(uint64_t Value) {
-  return static_cast<unsigned>(64 - countLeadingZeros(Value - 1));
-}
-
-/// Return the greatest common divisor of the values using Euclid's algorithm.
-template <typename T>
-inline T greatestCommonDivisor(T A, T B) {
-  while (B) {
-    T Tmp = B;
-    B = A % B;
-    A = Tmp;
-  }
-  return A;
-}
-
-inline uint64_t GreatestCommonDivisor64(uint64_t A, uint64_t B) {
-  return greatestCommonDivisor<uint64_t>(A, B);
-}
-
-/// This function takes a 64-bit integer and returns the bit equivalent double.
-inline double BitsToDouble(uint64_t Bits) {
-  double D;
-  static_assert(sizeof(uint64_t) == sizeof(double), "Unexpected type sizes");
-  memcpy(&D, &Bits, sizeof(Bits));
-  return D;
-}
-
-/// This function takes a 32-bit integer and returns the bit equivalent float.
-inline float BitsToFloat(uint32_t Bits) {
-  float F;
-  static_assert(sizeof(uint32_t) == sizeof(float), "Unexpected type sizes");
-  memcpy(&F, &Bits, sizeof(Bits));
-  return F;
-}
-
-/// This function takes a double and returns the bit equivalent 64-bit integer.
-/// Note that copying doubles around changes the bits of NaNs on some hosts,
-/// notably x86, so this routine cannot be used if these bits are needed.
-inline uint64_t DoubleToBits(double Double) {
-  uint64_t Bits;
-  static_assert(sizeof(uint64_t) == sizeof(double), "Unexpected type sizes");
-  memcpy(&Bits, &Double, sizeof(Double));
-  return Bits;
-}
-
-/// This function takes a float and returns the bit equivalent 32-bit integer.
-/// Note that copying floats around changes the bits of NaNs on some hosts,
-/// notably x86, so this routine cannot be used if these bits are needed.
-inline uint32_t FloatToBits(float Float) {
-  uint32_t Bits;
-  static_assert(sizeof(uint32_t) == sizeof(float), "Unexpected type sizes");
-  memcpy(&Bits, &Float, sizeof(Float));
-  return Bits;
+  return static_cast<unsigned>(64 - std::countl_zero(Value - 1));
 }
 
 /// A and B are either alignments or offsets. Return the minimum alignment that
@@ -653,7 +320,7 @@
 
 /// Returns the next power of two (in 64-bits) that is strictly greater than A.
 /// Returns zero on overflow.
-inline uint64_t NextPowerOf2(uint64_t A) {
+constexpr inline uint64_t NextPowerOf2(uint64_t A) {
   A |= (A >> 1);
   A |= (A >> 2);
   A |= (A >> 4);
@@ -663,13 +330,6 @@
   return A + 1;
 }
 
-/// Returns the power of two which is less than or equal to the given value.
-/// Essentially, it is a floor operation across the domain of powers of two.
-inline uint64_t PowerOf2Floor(uint64_t A) {
-  if (!A) return 0;
-  return 1ull << (63 - countLeadingZeros(A, ZB_Undefined));
-}
-
 /// Returns the power of two which is greater than or equal to the given value.
 /// Essentially, it is a ceil operation across the domain of powers of two.
 inline uint64_t PowerOf2Ceil(uint64_t A) {
@@ -681,27 +341,43 @@
 /// Returns the next integer (mod 2**64) that is greater than or equal to
 /// \p Value and is a multiple of \p Align. \p Align must be non-zero.
 ///
-/// If non-zero \p Skew is specified, the return value will be a minimal
-/// integer that is greater than or equal to \p Value and equal to
-/// \p Align * N + \p Skew for some integer N. If \p Skew is larger than
-/// \p Align, its value is adjusted to '\p Skew mod \p Align'.
-///
 /// Examples:
 /// \code
 ///   alignTo(5, 8) = 8
 ///   alignTo(17, 8) = 24
 ///   alignTo(~0LL, 8) = 0
 ///   alignTo(321, 255) = 510
+/// \endcode
+inline uint64_t alignTo(uint64_t Value, uint64_t Align) {
+  assert(Align != 0u && "Align can't be 0.");
+  return (Value + Align - 1) / Align * Align;
+}
+
+inline uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) {
+  assert(Align != 0 && (Align & (Align - 1)) == 0 &&
+         "Align must be a power of 2");
+  // Replace unary minus to avoid compilation error on Windows:
+  // "unary minus operator applied to unsigned type, result still unsigned"
+  uint64_t negAlign = (~Align) + 1;
+  return (Value + Align - 1) & negAlign;
+}
+
+/// If non-zero \p Skew is specified, the return value will be a minimal integer
+/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for
+/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p
+/// Skew mod \p A'. \p Align must be non-zero.
 ///
+/// Examples:
+/// \code
 ///   alignTo(5, 8, 7) = 7
 ///   alignTo(17, 8, 1) = 17
 ///   alignTo(~0LL, 8, 3) = 3
 ///   alignTo(321, 255, 42) = 552
 /// \endcode
-inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
+inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew) {
   assert(Align != 0u && "Align can't be 0.");
   Skew %= Align;
-  return (Value + Align - 1 - Skew) / Align * Align + Skew;
+  return alignTo(Value - Skew, Align) + Skew;
 }
 
 /// Returns the next integer (mod 2**64) that is greater than or equal to
@@ -764,7 +440,7 @@
 /// Subtract two unsigned integers, X and Y, of type T and return the absolute
 /// value of the result.
 template <typename T>
-std::enable_if_t<std::is_unsigned<T>::value, T> AbsoluteDifference(T X, T Y) {
+std::enable_if_t<std::is_unsigned_v<T>, T> AbsoluteDifference(T X, T Y) {
   return X > Y ? (X - Y) : (Y - X);
 }
 
@@ -772,7 +448,7 @@
 /// maximum representable value of T on overflow.  ResultOverflowed indicates if
 /// the result is larger than the maximum representable value of type T.
 template <typename T>
-std::enable_if_t<std::is_unsigned<T>::value, T>
+std::enable_if_t<std::is_unsigned_v<T>, T>
 SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
   bool Dummy;
   bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
@@ -785,11 +461,23 @@
     return Z;
 }
 
+/// Add multiple unsigned integers of type T.  Clamp the result to the
+/// maximum representable value of T on overflow.
+template <class T, class... Ts>
+std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y, T Z,
+                                                         Ts... Args) {
+  bool Overflowed = false;
+  T XY = SaturatingAdd(X, Y, &Overflowed);
+  if (Overflowed)
+    return SaturatingAdd((std::numeric_limits<T>::max)(), T(1), Args...);
+  return SaturatingAdd(XY, Z, Args...);
+}
+
 /// Multiply two unsigned integers, X and Y, of type T.  Clamp the result to the
 /// maximum representable value of T on overflow.  ResultOverflowed indicates if
 /// the result is larger than the maximum representable value of type T.
 template <typename T>
-std::enable_if_t<std::is_unsigned<T>::value, T>
+std::enable_if_t<std::is_unsigned_v<T>, T>
 SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
   bool Dummy;
   bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
@@ -835,7 +523,7 @@
 /// overflow. ResultOverflowed indicates if the result is larger than the
 /// maximum representable value of type T.
 template <typename T>
-std::enable_if_t<std::is_unsigned<T>::value, T>
+std::enable_if_t<std::is_unsigned_v<T>, T>
 SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
   bool Dummy;
   bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
@@ -852,9 +540,9 @@
 
 
 /// Add two signed integers, computing the two's complement truncated result,
-/// returning true if overflow occured.
+/// returning true if overflow occurred.
 template <typename T>
-std::enable_if_t<std::is_signed<T>::value, T> AddOverflow(T X, T Y, T &Result) {
+std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y, T &Result) {
 #if __has_builtin(__builtin_add_overflow)
   return __builtin_add_overflow(X, Y, &Result);
 #else
@@ -880,7 +568,7 @@
 /// Subtract two signed integers, computing the two's complement truncated
 /// result, returning true if an overflow ocurred.
 template <typename T>
-std::enable_if_t<std::is_signed<T>::value, T> SubOverflow(T X, T Y, T &Result) {
+std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
 #if __has_builtin(__builtin_sub_overflow)
   return __builtin_sub_overflow(X, Y, &Result);
 #else
@@ -906,7 +594,7 @@
 /// Multiply two signed integers, computing the two's complement truncated
 /// result, returning true if an overflow ocurred.
 template <typename T>
-std::enable_if_t<std::is_signed<T>::value, T> MulOverflow(T X, T Y, T &Result) {
+std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
   // Perform the unsigned multiplication on absolute values.
   using U = std::make_unsigned_t<T>;
   const U UX = X < 0 ? (0 - static_cast<U>(X)) : static_cast<U>(X);
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/PointerIntPair.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/PointerIntPair.h
index 34ff4e1..22205c9 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/PointerIntPair.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/PointerIntPair.h
@@ -19,10 +19,44 @@
 #include "wpi/type_traits.h"
 #include <cassert>
 #include <cstdint>
+#include <cstring>
 #include <limits>
 
 namespace wpi {
 
+namespace detail {
+template <typename Ptr> struct PunnedPointer {
+  static_assert(sizeof(Ptr) == sizeof(intptr_t), "");
+
+  // Asserts that allow us to let the compiler implement the destructor and
+  // copy/move constructors
+  static_assert(std::is_trivially_destructible<Ptr>::value, "");
+  static_assert(std::is_trivially_copy_constructible<Ptr>::value, "");
+  static_assert(std::is_trivially_move_constructible<Ptr>::value, "");
+
+  explicit constexpr PunnedPointer(intptr_t i = 0) { *this = i; }
+
+  constexpr intptr_t asInt() const {
+    intptr_t R = 0;
+    std::memcpy(&R, Data, sizeof(R));
+    return R;
+  }
+
+  constexpr operator intptr_t() const { return asInt(); }
+
+  constexpr PunnedPointer &operator=(intptr_t V) {
+    std::memcpy(Data, &V, sizeof(Data));
+    return *this;
+  }
+
+  Ptr *getPointerAddress() { return reinterpret_cast<Ptr *>(Data); }
+  const Ptr *getPointerAddress() const { return reinterpret_cast<Ptr *>(Data); }
+
+private:
+  alignas(Ptr) unsigned char Data[sizeof(Ptr)];
+};
+} // namespace detail
+
 template <typename T, typename Enable> struct DenseMapInfo;
 template <typename PointerT, unsigned IntBits, typename PtrTraits>
 struct PointerIntPairInfo;
@@ -46,7 +80,7 @@
 class PointerIntPair {
   // Used by MSVC visualizer and generally helpful for debugging/visualizing.
   using InfoTy = Info;
-  intptr_t Value = 0;
+  detail::PunnedPointer<PointerTy> Value;
 
 public:
   constexpr PointerIntPair() = default;
@@ -61,19 +95,19 @@
 
   IntType getInt() const { return (IntType)Info::getInt(Value); }
 
-  void setPointer(PointerTy PtrVal) LLVM_LVALUE_FUNCTION {
+  void setPointer(PointerTy PtrVal) & {
     Value = Info::updatePointer(Value, PtrVal);
   }
 
-  void setInt(IntType IntVal) LLVM_LVALUE_FUNCTION {
+  void setInt(IntType IntVal) & {
     Value = Info::updateInt(Value, static_cast<intptr_t>(IntVal));
   }
 
-  void initWithPointer(PointerTy PtrVal) LLVM_LVALUE_FUNCTION {
+  void initWithPointer(PointerTy PtrVal) & {
     Value = Info::updatePointer(0, PtrVal);
   }
 
-  void setPointerAndInt(PointerTy PtrVal, IntType IntVal) LLVM_LVALUE_FUNCTION {
+  void setPointerAndInt(PointerTy PtrVal, IntType IntVal) & {
     Value = Info::updateInt(Info::updatePointer(0, PtrVal),
                             static_cast<intptr_t>(IntVal));
   }
@@ -86,12 +120,14 @@
     assert(Value == reinterpret_cast<intptr_t>(getPointer()) &&
            "Can only return the address if IntBits is cleared and "
            "PtrTraits doesn't change the pointer");
-    return reinterpret_cast<PointerTy *>(&Value);
+    return Value.getPointerAddress();
   }
 
-  void *getOpaqueValue() const { return reinterpret_cast<void *>(Value); }
+  void *getOpaqueValue() const {
+    return reinterpret_cast<void *>(Value.asInt());
+  }
 
-  void setFromOpaqueValue(void *Val) LLVM_LVALUE_FUNCTION {
+  void setFromOpaqueValue(void *Val) & {
     Value = reinterpret_cast<intptr_t>(Val);
   }
 
@@ -128,7 +164,6 @@
   }
 };
 
-
 template <typename PointerT, unsigned IntBits, typename PtrTraits>
 struct PointerIntPairInfo {
   static_assert(PtrTraits::NumLowBitsAvailable <
@@ -228,6 +263,32 @@
       PtrTraits::NumLowBitsAvailable - IntBits;
 };
 
+// Allow structured bindings on PointerIntPair.
+template <std::size_t I, typename PointerTy, unsigned IntBits, typename IntType,
+          typename PtrTraits, typename Info>
+decltype(auto)
+get(const PointerIntPair<PointerTy, IntBits, IntType, PtrTraits, Info> &Pair) {
+  static_assert(I < 2);
+  if constexpr (I == 0)
+    return Pair.getPointer();
+  else
+    return Pair.getInt();
+}
+
 } // end namespace wpi
 
+namespace std {
+template <typename PointerTy, unsigned IntBits, typename IntType,
+          typename PtrTraits, typename Info>
+struct tuple_size<
+    wpi::PointerIntPair<PointerTy, IntBits, IntType, PtrTraits, Info>>
+    : std::integral_constant<std::size_t, 2> {};
+
+template <std::size_t I, typename PointerTy, unsigned IntBits, typename IntType,
+          typename PtrTraits, typename Info>
+struct tuple_element<
+    I, wpi::PointerIntPair<PointerTy, IntBits, IntType, PtrTraits, Info>>
+    : std::conditional<I == 0, PointerTy, IntType> {};
+} // namespace std
+
 #endif // WPIUTIL_WPI_POINTERINTPAIR_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/PointerUnion.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/PointerUnion.h
index cc12bbc..5053a5f 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/PointerUnion.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/PointerUnion.h
@@ -17,6 +17,7 @@
 
 #include "wpi/DenseMapInfo.h"
 #include "wpi/PointerIntPair.h"
+#include "wpi/Casting.h"
 #include "wpi/PointerLikeTypeTraits.h"
 #include <algorithm>
 #include <cassert>
@@ -132,6 +133,9 @@
   };
 }
 
+// This is a forward declaration of CastInfoPointerUnionImpl
+// Refer to its definition below for further details
+template <typename... PTs> struct CastInfoPointerUnionImpl;
 /// A discriminated union of two or more pointer types, with the discriminator
 /// in the low bit of the pointer.
 ///
@@ -167,6 +171,11 @@
   using First = TypeAtIndex<0, PTs...>;
   using Base = typename PointerUnion::PointerUnionMembers;
 
+  /// This is needed to give the CastInfo implementation below access
+  /// to protected members.
+  /// Refer to its definition for further details.
+  friend struct CastInfoPointerUnionImpl<PTs...>;
+
 public:
   PointerUnion() = default;
 
@@ -179,25 +188,24 @@
 
   explicit operator bool() const { return !isNull(); }
 
+  // FIXME: Replace the uses of is(), get() and dyn_cast() with
+  //        isa<T>, cast<T> and the wpi::dyn_cast<T>
+
   /// Test if the Union currently holds the type matching T.
-  template <typename T> bool is() const {
-    return this->Val.getInt() == FirstIndexOfType<T, PTs...>::value;
-  }
+  template <typename T> inline bool is() const { return isa<T>(*this); }
 
   /// Returns the value of the specified pointer type.
   ///
   /// If the specified pointer type is incorrect, assert.
-  template <typename T> T get() const {
-    assert(is<T>() && "Invalid accessor called");
-    return PointerLikeTypeTraits<T>::getFromVoidPointer(this->Val.getPointer());
+  template <typename T> inline T get() const {
+    assert(isa<T>(*this) && "Invalid accessor called");
+    return cast<T>(*this);
   }
 
   /// Returns the current pointer if it is of the specified pointer type,
   /// otherwise returns null.
-  template <typename T> T dyn_cast() const {
-    if (is<T>())
-      return get<T>();
-    return T();
+  template <typename T> inline T dyn_cast() const {
+    return wpi::dyn_cast_if_present<T>(*this);
   }
 
   /// If the union is set to the first pointer type get an address pointing to
@@ -209,9 +217,9 @@
   /// If the union is set to the first pointer type get an address pointing to
   /// it.
   First *getAddrOfPtr1() {
-    assert(is<First>() && "Val is not the first pointer");
+    assert(isa<First>(*this) && "Val is not the first pointer");
     assert(
-        PointerLikeTypeTraits<First>::getAsVoidPointer(get<First>()) ==
+        PointerLikeTypeTraits<First>::getAsVoidPointer(cast<First>(*this)) ==
             this->Val.getPointer() &&
         "Can't get the address because PointerLikeTypeTraits changes the ptr");
     return const_cast<First *>(
@@ -250,6 +258,52 @@
   return lhs.getOpaqueValue() < rhs.getOpaqueValue();
 }
 
+/// We can't (at least, at this moment with C++14) declare CastInfo
+/// as a friend of PointerUnion like this:
+/// ```
+///   template<typename To>
+///   friend struct CastInfo<To, PointerUnion<PTs...>>;
+/// ```
+/// The compiler complains 'Partial specialization cannot be declared as a
+/// friend'.
+/// So we define this struct to be a bridge between CastInfo and
+/// PointerUnion.
+template <typename... PTs> struct CastInfoPointerUnionImpl {
+  using From = PointerUnion<PTs...>;
+
+  template <typename To> static inline bool isPossible(From &F) {
+    return F.Val.getInt() == FirstIndexOfType<To, PTs...>::value;
+  }
+
+  template <typename To> static To doCast(From &F) {
+    assert(isPossible<To>(F) && "cast to an incompatible type!");
+    return PointerLikeTypeTraits<To>::getFromVoidPointer(F.Val.getPointer());
+  }
+};
+
+// Specialization of CastInfo for PointerUnion
+template <typename To, typename... PTs>
+struct CastInfo<To, PointerUnion<PTs...>>
+    : public DefaultDoCastIfPossible<To, PointerUnion<PTs...>,
+                                     CastInfo<To, PointerUnion<PTs...>>> {
+  using From = PointerUnion<PTs...>;
+  using Impl = CastInfoPointerUnionImpl<PTs...>;
+
+  static inline bool isPossible(From &f) {
+    return Impl::template isPossible<To>(f);
+  }
+
+  static To doCast(From &f) { return Impl::template doCast<To>(f); }
+
+  static inline To castFailed() { return To(); }
+};
+
+template <typename To, typename... PTs>
+struct CastInfo<To, const PointerUnion<PTs...>>
+    : public ConstStrippingForwardingCast<To, const PointerUnion<PTs...>,
+                                          CastInfo<To, PointerUnion<PTs...>>> {
+};
+
 // Teach SmallPtrSet that PointerUnion is "basically a pointer", that has
 // # low bits available = min(PT1bits,PT2bits)-1.
 template <typename ...PTs>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ReverseIteration.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ReverseIteration.h
index f46d38d..5425cd9 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ReverseIteration.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/ReverseIteration.h
@@ -14,5 +14,5 @@
 #endif
 }
 
-}
+} // namespace wpi
 #endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/STLForwardCompat.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/STLForwardCompat.h
index 7f8f068..02cf826 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/STLForwardCompat.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/STLForwardCompat.h
@@ -17,54 +17,12 @@
 #ifndef WPIUTIL_WPI_STLFORWARDCOMPAT_H
 #define WPIUTIL_WPI_STLFORWARDCOMPAT_H
 
+#include <optional>
 #include <type_traits>
 
 namespace wpi {
 
 //===----------------------------------------------------------------------===//
-//     Features from C++17
-//===----------------------------------------------------------------------===//
-
-template <typename T>
-struct negation // NOLINT(readability-identifier-naming)
-    : std::integral_constant<bool, !bool(T::value)> {};
-
-template <typename...>
-struct conjunction // NOLINT(readability-identifier-naming)
-    : std::true_type {};
-template <typename B1> struct conjunction<B1> : B1 {};
-template <typename B1, typename... Bn>
-struct conjunction<B1, Bn...>
-    : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
-
-template <typename...>
-struct disjunction // NOLINT(readability-identifier-naming)
-    : std::false_type {};
-template <typename B1> struct disjunction<B1> : B1 {};
-template <typename B1, typename... Bn>
-struct disjunction<B1, Bn...>
-    : std::conditional<bool(B1::value), B1, disjunction<Bn...>>::type {};
-
-struct in_place_t // NOLINT(readability-identifier-naming)
-{
-  explicit in_place_t() = default;
-};
-/// \warning This must not be odr-used, as it cannot be made \c inline in C++14.
-constexpr in_place_t in_place; // NOLINT(readability-identifier-naming)
-
-template <typename T>
-struct in_place_type_t // NOLINT(readability-identifier-naming)
-{
-  explicit in_place_type_t() = default;
-};
-
-template <std::size_t I>
-struct in_place_index_t // NOLINT(readability-identifier-naming)
-{
-  explicit in_place_index_t() = default;
-};
-
-//===----------------------------------------------------------------------===//
 //     Features from C++20
 //===----------------------------------------------------------------------===//
 
@@ -78,6 +36,30 @@
 using remove_cvref_t // NOLINT(readability-identifier-naming)
     = typename wpi::remove_cvref<T>::type;
 
+//===----------------------------------------------------------------------===//
+//     Features from C++23
+//===----------------------------------------------------------------------===//
+
+// TODO: Remove this in favor of std::optional<T>::transform once we switch to
+// C++23.
+template <typename T, typename Function>
+auto transformOptional(const std::optional<T> &O, const Function &F)
+    -> std::optional<decltype(F(*O))> {
+  if (O)
+    return F(*O);
+  return std::nullopt;
+}
+
+// TODO: Remove this in favor of std::optional<T>::transform once we switch to
+// C++23.
+template <typename T, typename Function>
+auto transformOptional(std::optional<T> &&O, const Function &F)
+    -> std::optional<decltype(F(*std::move(O)))> {
+  if (O)
+    return F(*std::move(O));
+  return std::nullopt;
+}
+
 } // namespace wpi
 
 #endif // WPIUTIL_WPI_STLFORWARDCOMPAT_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallPtrSet.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallPtrSet.h
index 1ada1e0..db2b471 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallPtrSet.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallPtrSet.h
@@ -89,7 +89,7 @@
 
   SmallPtrSetImplBase &operator=(const SmallPtrSetImplBase &) = delete;
 
-  LLVM_NODISCARD bool empty() const { return size() == 0; }
+  [[nodiscard]] bool empty() const { return size() == 0; }
   size_type size() const { return NumNonEmpty - NumTombstones; }
 
   void clear() {
@@ -264,8 +264,9 @@
 
 /// SmallPtrSetIterator - This implements a const_iterator for SmallPtrSet.
 template <typename PtrTy>
-class SmallPtrSetIterator : public SmallPtrSetIteratorImpl,
-                            DebugEpochBase::HandleBase {
+class LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE SmallPtrSetIterator
+    : public SmallPtrSetIteratorImpl,
+      DebugEpochBase::HandleBase {
   using PtrTraits = PointerLikeTypeTraits<PtrTy>;
 
 public:
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallSet.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallSet.h
index 7b1b283..699be24 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallSet.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallSet.h
@@ -140,6 +140,7 @@
   std::set<T, C> Set;
 
   using VIterator = typename SmallVector<T, N>::const_iterator;
+  using SIterator = typename std::set<T, C>::const_iterator;
   using mutable_iterator = typename SmallVector<T, N>::iterator;
 
   // In small mode SmallPtrSet uses linear search for the elements, so it is
@@ -148,14 +149,14 @@
   static_assert(N <= 32, "N should be small");
 
 public:
+  using key_type = T;
   using size_type = size_t;
+  using value_type = T;
   using const_iterator = SmallSetIterator<T, N, C>;
 
   SmallSet() = default;
 
-  LLVM_NODISCARD bool empty() const {
-    return Vector.empty() && Set.empty();
-  }
+  [[nodiscard]] bool empty() const { return Vector.empty() && Set.empty(); }
 
   size_type size() const {
     return isSmall() ? Vector.size() : Set.size();
@@ -172,22 +173,21 @@
   }
 
   /// insert - Insert an element into the set if it isn't already there.
-  /// Returns true if the element is inserted (it was not in the set before).
-  /// The first value of the returned pair is unused and provided for
-  /// partial compatibility with the standard library self-associative container
-  /// concept.
-  // FIXME: Add iterators that abstract over the small and large form, and then
-  // return those here.
-  std::pair<std::nullopt_t, bool> insert(const T &V) {
-    if (!isSmall())
-      return std::make_pair(std::nullopt, Set.insert(V).second);
+  /// Returns a pair. The first value of it is an iterator to the inserted
+  /// element or the existing element in the set. The second value is true
+  /// if the element is inserted (it was not in the set before).
+  std::pair<const_iterator, bool> insert(const T &V) {
+    if (!isSmall()) {
+      auto [I, Inserted] = Set.insert(V);
+      return std::make_pair(const_iterator(I), Inserted);
+    }
 
     VIterator I = vfind(V);
     if (I != Vector.end())    // Don't reinsert if it already exists.
-      return std::make_pair(std::nullopt, false);
+      return std::make_pair(const_iterator(I), false);
     if (Vector.size() < N) {
       Vector.push_back(V);
-      return std::make_pair(std::nullopt, true);
+      return std::make_pair(const_iterator(std::prev(Vector.end())), true);
     }
 
     // Otherwise, grow from vector to set.
@@ -195,8 +195,7 @@
       Set.insert(Vector.back());
       Vector.pop_back();
     }
-    Set.insert(V);
-    return std::make_pair(std::nullopt, true);
+    return std::make_pair(const_iterator(Set.insert(V).first), true);
   }
 
   template <typename IterT>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallVector.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallVector.h
index 134ce67..3b4b890 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallVector.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SmallVector.h
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 ///
-/// /file
+/// \file
 /// This file defines the SmallVector class.
 ///
 //===----------------------------------------------------------------------===//
@@ -35,6 +35,7 @@
 #include <limits>
 #include <memory>
 #include <new>
+#include <span>
 #include <type_traits>
 #include <utility>
 
@@ -42,6 +43,11 @@
 
 template <typename IteratorT> class iterator_range;
 
+template <class Iterator>
+using EnableIfConvertibleToInputIterator = std::enable_if_t<std::is_convertible<
+    typename std::iterator_traits<Iterator>::iterator_category,
+    std::input_iterator_tag>::value>;
+
 /// This is all the stuff common to all SmallVectors.
 ///
 /// The template parameter specifies the type which should be used to hold the
@@ -67,18 +73,32 @@
   /// This is a helper for \a grow() that's out of line to reduce code
   /// duplication.  This function will report a fatal error if it can't grow at
   /// least to \p MinSize.
-  void *mallocForGrow(size_t MinSize, size_t TSize, size_t &NewCapacity);
+  void *mallocForGrow(void *FirstEl, size_t MinSize, size_t TSize,
+                      size_t &NewCapacity);
 
   /// This is an implementation of the grow() method which only works
   /// on POD-like data types and is out of line to reduce code duplication.
   /// This function will report a fatal error if it cannot increase capacity.
   void grow_pod(void *FirstEl, size_t MinSize, size_t TSize);
 
+  /// If vector was first created with capacity 0, getFirstEl() points to the
+  /// memory right after, an area unallocated. If a subsequent allocation,
+  /// that grows the vector, happens to return the same pointer as getFirstEl(),
+  /// get a new allocation, otherwise isSmall() will falsely return that no
+  /// allocation was done (true) and the memory will not be freed in the
+  /// destructor. If a VSize is given (vector size), also copy that many
+  /// elements to the new allocation - used if realloca fails to increase
+  /// space, and happens to allocate precisely at BeginX.
+  /// This is unlikely to be called often, but resolves a memory leak when the
+  /// situation does occur.
+  void *replaceAllocation(void *NewElts, size_t TSize, size_t NewCapacity,
+                          size_t VSize = 0);
+
 public:
   size_t size() const { return Size; }
   size_t capacity() const { return Capacity; }
 
-  LLVM_NODISCARD bool empty() const { return !Size; }
+  [[nodiscard]] bool empty() const { return !Size; }
 
 protected:
   /// Set the array size to \p N, which the current array must have enough
@@ -99,13 +119,14 @@
 };
 
 /// This is the part of SmallVectorTemplateBase which does not depend on whether
-/// the type T is a POD. The extra dummy template argument is used by ArrayRef
+/// the type T is a POD. The extra dummy template argument is used by std::span
 /// to avoid unnecessarily requiring T to be complete.
 template <typename T, typename = void>
 class SmallVectorTemplateCommon
     : public SmallVectorBase {
   using Base = SmallVectorBase;
 
+protected:
   /// Find the address of the first element.  For this pointer math to be valid
   /// with small-size of 0 for T with lots of alignment, it's important that
   /// SmallVectorStorage is properly-aligned even for small-size of 0.
@@ -116,7 +137,6 @@
   }
   // Space after 'FirstEl' is clobbered, do not add any instance vars after it.
 
-protected:
   SmallVectorTemplateCommon(size_t Size) : Base(getFirstEl(), Size) {}
 
   void grow_pod(size_t MinSize, size_t TSize) {
@@ -308,8 +328,8 @@
 /// copy these types with memcpy, there is no way for the type to observe this.
 /// This catches the important case of std::pair<POD, POD>, which is not
 /// trivially assignable.
-template <typename T, bool = (is_trivially_copy_constructible<T>::value) &&
-                             (is_trivially_move_constructible<T>::value) &&
+template <typename T, bool = (std::is_trivially_copy_constructible<T>::value) &&
+                             (std::is_trivially_move_constructible<T>::value) &&
                              std::is_trivially_destructible<T>::value>
 class SmallVectorTemplateBase : public SmallVectorTemplateCommon<T> {
   friend class SmallVectorTemplateCommon<T>;
@@ -331,8 +351,7 @@
   /// constructing elements as needed.
   template<typename It1, typename It2>
   static void uninitialized_move(It1 I, It1 E, It2 Dest) {
-    std::uninitialized_copy(std::make_move_iterator(I),
-                            std::make_move_iterator(E), Dest);
+    std::uninitialized_move(I, E, Dest);
   }
 
   /// Copy the range [I, E) onto the uninitialized memory starting with "Dest",
@@ -349,11 +368,7 @@
 
   /// Create a new allocation big enough for \p MinSize and pass back its size
   /// in \p NewCapacity. This is the first section of \a grow().
-  T *mallocForGrow(size_t MinSize, size_t &NewCapacity) {
-    return static_cast<T *>(
-        SmallVectorBase::mallocForGrow(
-            MinSize, sizeof(T), NewCapacity));
-  }
+  T *mallocForGrow(size_t MinSize, size_t &NewCapacity);
 
   /// Move existing elements over to the new allocation \p NewElts, the middle
   /// section of \a grow().
@@ -427,6 +442,14 @@
   takeAllocationForGrow(NewElts, NewCapacity);
 }
 
+template <typename T, bool TriviallyCopyable>
+T *SmallVectorTemplateBase<T, TriviallyCopyable>::mallocForGrow(
+    size_t MinSize, size_t &NewCapacity) {
+  return static_cast<T *>(
+      SmallVectorBase::mallocForGrow(
+          this->getFirstEl(), MinSize, sizeof(T), NewCapacity));
+}
+
 // Define this out-of-line to dissuade the C++ compiler from inlining it.
 template <typename T, bool TriviallyCopyable>
 void SmallVectorTemplateBase<T, TriviallyCopyable>::moveElementsForGrow(
@@ -465,8 +488,7 @@
 
   /// Either const T& or T, depending on whether it's cheap enough to take
   /// parameters by value.
-  using ValueParamT =
-      typename std::conditional<TakesParamByValue, T, const T &>::type;
+  using ValueParamT = std::conditional_t<TakesParamByValue, T, const T &>;
 
   SmallVectorTemplateBase(size_t Size) : SmallVectorTemplateCommon<T>(Size) {}
 
@@ -494,8 +516,8 @@
   template <typename T1, typename T2>
   static void uninitialized_copy(
       T1 *I, T1 *E, T2 *Dest,
-      std::enable_if_t<std::is_same<typename std::remove_const<T1>::type,
-                                    T2>::value> * = nullptr) {
+      std::enable_if_t<std::is_same<std::remove_const_t<T1>, T2>::value> * =
+          nullptr) {
     // Use memcpy for PODs iterated by pointers (which includes SmallVector
     // iterators): std::uninitialized_copy optimizes to memmove, but we can
     // use memcpy here. Note that I and E are iterators and thus might be
@@ -654,7 +676,7 @@
     truncate(this->size() - NumItems);
   }
 
-  LLVM_NODISCARD T pop_back_val() {
+  [[nodiscard]] T pop_back_val() {
     T Result = ::std::move(this->back());
     this->pop_back();
     return Result;
@@ -663,11 +685,8 @@
   void swap(SmallVectorImpl &RHS);
 
   /// Add the specified range to the end of the SmallVector.
-  template <typename in_iter,
-            typename = std::enable_if_t<std::is_convertible<
-                typename std::iterator_traits<in_iter>::iterator_category,
-                std::input_iterator_tag>::value>>
-  void append(in_iter in_start, in_iter in_end) {
+  template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
+  void append(ItTy in_start, ItTy in_end) {
     this->assertSafeToAddRange(in_start, in_end);
     size_type NumInputs = std::distance(in_start, in_end);
     this->reserve(this->size() + NumInputs);
@@ -707,11 +726,8 @@
   // FIXME: Consider assigning over existing elements, rather than clearing &
   // re-initializing them - for all assign(...) variants.
 
-  template <typename in_iter,
-            typename = std::enable_if_t<std::is_convertible<
-                typename std::iterator_traits<in_iter>::iterator_category,
-                std::input_iterator_tag>::value>>
-  void assign(in_iter in_start, in_iter in_end) {
+  template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
+  void assign(ItTy in_start, ItTy in_end) {
     this->assertSafeToReferenceAfterClear(in_start, in_end);
     clear();
     append(in_start, in_end);
@@ -861,10 +877,7 @@
     return I;
   }
 
-  template <typename ItTy,
-            typename = std::enable_if_t<std::is_convertible<
-                typename std::iterator_traits<ItTy>::iterator_category,
-                std::input_iterator_tag>::value>>
+  template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
   iterator insert(iterator I, ItTy From, ItTy To) {
     // Convert iterator to elt# to avoid invalidating iterator when we reserve()
     size_t InsertElt = I - this->begin();
@@ -952,6 +965,9 @@
     return std::lexicographical_compare(this->begin(), this->end(),
                                         RHS.begin(), RHS.end());
   }
+  bool operator>(const SmallVectorImpl &RHS) const { return RHS < *this; }
+  bool operator<=(const SmallVectorImpl &RHS) const { return !(*this > RHS); }
+  bool operator>=(const SmallVectorImpl &RHS) const { return !(*this < RHS); }
 };
 
 template <typename T>
@@ -1192,15 +1208,17 @@
     this->destroy_range(this->begin(), this->end());
   }
 
-  explicit SmallVector(size_t Size, const T &Value = T())
+  explicit SmallVector(size_t Size)
+    : SmallVectorImpl<T>(N) {
+    this->resize(Size);
+  }
+
+  SmallVector(size_t Size, const T &Value)
     : SmallVectorImpl<T>(N) {
     this->assign(Size, Value);
   }
 
-  template <typename ItTy,
-            typename = std::enable_if_t<std::is_convertible<
-                typename std::iterator_traits<ItTy>::iterator_category,
-                std::input_iterator_tag>::value>>
+  template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
   SmallVector(ItTy S, ItTy E) : SmallVectorImpl<T>(N) {
     this->append(S, E);
   }
@@ -1212,7 +1230,13 @@
   }
 
   SmallVector(std::initializer_list<T> IL) : SmallVectorImpl<T>(N) {
-    this->assign(IL);
+    this->append(IL);
+  }
+
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  explicit SmallVector(std::span<const U> A) : SmallVectorImpl<T>(N) {
+    this->append(A.begin(), A.end());
   }
 
   SmallVector(const SmallVector &RHS) : SmallVectorImpl<T>(N) {
@@ -1271,8 +1295,8 @@
 
 template <typename RangeType>
 using ValueTypeFromRangeType =
-    typename std::remove_const<typename std::remove_reference<
-        decltype(*std::begin(std::declval<RangeType &>()))>::type>::type;
+    std::remove_const_t<std::remove_reference_t<decltype(*std::begin(
+        std::declval<RangeType &>()))>>;
 
 /// Given a range of type R, iterate the entire range and return a
 /// SmallVector with elements of the vector.  This is useful, for example,
@@ -1282,10 +1306,16 @@
   return {std::begin(Range), std::end(Range)};
 }
 template <typename R>
-SmallVector<ValueTypeFromRangeType<R>,
-            CalculateSmallVectorDefaultInlinedElements<
-                ValueTypeFromRangeType<R>>::value>
-to_vector(R &&Range) {
+SmallVector<ValueTypeFromRangeType<R>> to_vector(R &&Range) {
+  return {std::begin(Range), std::end(Range)};
+}
+
+template <typename Out, unsigned Size, typename R>
+SmallVector<Out, Size> to_vector_of(R &&Range) {
+  return {std::begin(Range), std::end(Range)};
+}
+
+template <typename Out, typename R> SmallVector<Out> to_vector_of(R &&Range) {
   return {std::begin(Range), std::end(Range)};
 }
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringExtras.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringExtras.h
index 75c637b..e07576e 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringExtras.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringExtras.h
@@ -17,6 +17,7 @@
 
 #pragma once
 
+#include <iterator>
 #include <limits>
 #include <optional>
 #include <string>
@@ -24,6 +25,8 @@
 #include <type_traits>
 #include <utility>
 
+#include <fmt/format.h>
+
 namespace wpi {
 
 template <typename T>
@@ -721,4 +724,23 @@
 std::pair<std::string_view, std::string_view> UnescapeCString(
     std::string_view str, SmallVectorImpl<char>& buf);
 
+/**
+ * Like std::format_to_n() in that it writes at most n bytes to the output
+ * buffer, but also includes a terminating null byte in n.
+ *
+ * This is essentially a more performant replacement for std::snprintf().
+ *
+ * @param out The output buffer.
+ * @param n The size of the output buffer.
+ * @param fmt The format string.
+ * @param args The format string arguments.
+ */
+template <class OutputIt, class... Args>
+inline void format_to_n_c_str(OutputIt out, std::iter_difference_t<OutputIt> n,
+                              fmt::format_string<Args...> fmt, Args&&... args) {
+  const auto result =
+      fmt::format_to_n(out, n - 1, fmt, std::forward<Args>(args)...);
+  *result.out = '\0';
+}
+
 }  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringMap.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringMap.h
index c7da670..323d22e 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringMap.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringMap.h
@@ -111,8 +111,10 @@
 /// funky memory allocation and hashing things to make it extremely efficient,
 /// storing the string data *after* the value in the map.
 template <typename ValueTy, typename AllocatorTy = MallocAllocator>
-class StringMap : public StringMapImpl {
-  AllocatorTy Allocator;
+class LLVM_ALLOCATORHOLDER_EMPTYBASE StringMap
+    : public StringMapImpl,
+      private detail::AllocatorHolder<AllocatorTy> {
+  using AllocTy = detail::AllocatorHolder<AllocatorTy>;
 
 public:
   using MapEntryTy = StringMapEntry<ValueTy>;
@@ -123,12 +125,11 @@
       : StringMapImpl(InitialSize, static_cast<unsigned>(sizeof(MapEntryTy))) {}
 
   explicit StringMap(AllocatorTy A)
-      : StringMapImpl(static_cast<unsigned>(sizeof(MapEntryTy))), Allocator(A) {
-  }
+      : StringMapImpl(static_cast<unsigned>(sizeof(MapEntryTy))), AllocTy(A) {}
 
   StringMap(unsigned InitialSize, AllocatorTy A)
       : StringMapImpl(InitialSize, static_cast<unsigned>(sizeof(MapEntryTy))),
-        Allocator(A) {}
+        AllocTy(A) {}
 
   StringMap(std::initializer_list<std::pair<std::string_view, ValueTy>> List)
       : StringMapImpl(List.size(), static_cast<unsigned>(sizeof(MapEntryTy))) {
@@ -136,11 +137,11 @@
   }
 
   StringMap(StringMap &&RHS)
-      : StringMapImpl(std::move(RHS)), Allocator(std::move(RHS.Allocator)) {}
+      : StringMapImpl(std::move(RHS)), AllocTy(std::move(RHS.getAllocator())) {}
 
   StringMap(const StringMap &RHS)
       : StringMapImpl(static_cast<unsigned>(sizeof(MapEntryTy))),
-        Allocator(RHS.Allocator) {
+        AllocTy(RHS.getAllocator()) {
     if (RHS.empty())
       return;
 
@@ -159,8 +160,8 @@
         continue;
       }
 
-      TheTable[I] = MapEntryTy::Create(
-          static_cast<MapEntryTy *>(Bucket)->getKey(), Allocator,
+      TheTable[I] = MapEntryTy::create(
+          static_cast<MapEntryTy *>(Bucket)->getKey(), getAllocator(),
           static_cast<MapEntryTy *>(Bucket)->getValue());
       HashTable[I] = RHSHashTable[I];
     }
@@ -175,7 +176,7 @@
 
   StringMap &operator=(StringMap RHS) {
     StringMapImpl::swap(RHS);
-    std::swap(Allocator, RHS.Allocator);
+    std::swap(getAllocator(), RHS.getAllocator());
     return *this;
   }
 
@@ -187,15 +188,14 @@
       for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
         StringMapEntryBase *Bucket = TheTable[I];
         if (Bucket && Bucket != getTombstoneVal()) {
-          static_cast<MapEntryTy *>(Bucket)->Destroy(Allocator);
+          static_cast<MapEntryTy *>(Bucket)->Destroy(getAllocator());
         }
       }
     }
     free(TheTable);
   }
 
-  AllocatorTy &getAllocator() { return Allocator; }
-  const AllocatorTy &getAllocator() const { return Allocator; }
+  using AllocTy::getAllocator;
 
   using key_type = const char *;
   using mapped_type = ValueTy;
@@ -236,18 +236,29 @@
   /// lookup - Return the entry for the specified key, or a default
   /// constructed value if no such entry exists.
   ValueTy lookup(std::string_view Key) const {
-    const_iterator it = find(Key);
-    if (it != end())
-      return it->second;
+    const_iterator Iter = find(Key);
+    if (Iter != end())
+      return Iter->second;
     return ValueTy();
   }
 
+  /// at - Return the entry for the specified key, or abort if no such
+  /// entry exists.
+  const ValueTy &at(std::string_view Val) const {
+    auto Iter = this->find(std::move(Val));
+    assert(Iter != this->end() && "StringMap::at failed due to a missing key");
+    return Iter->second;
+  }
+
   /// Lookup the ValueTy for the \p Key, or create a default constructed value
   /// if the key is not in the map.
   ValueTy &operator[](std::string_view Key) { return try_emplace(Key).first->second; }
 
+  /// contains - Return true if the element is in the map, false otherwise.
+  bool contains(std::string_view Key) const { return find(Key) != end(); }
+
   /// count - Return 1 if the element is in the map, 0 otherwise.
-  size_type count(std::string_view Key) const { return find(Key) == end() ? 0 : 1; }
+  size_type count(std::string_view Key) const { return contains(Key) ? 1 : 0; }
 
   template <typename InputTy>
   size_type count(const StringMapEntry<InputTy> &MapEntry) const {
@@ -331,7 +342,7 @@
   /// if and only if the insertion takes place, and the iterator component of
   /// the pair points to the element with key equivalent to the key of the pair.
   template <typename... ArgsTy>
-  std::pair<iterator, bool> try_emplace(std::string_view Key, ArgsTy &&... Args) {
+  std::pair<iterator, bool> try_emplace(std::string_view Key, ArgsTy &&...Args) {
     unsigned BucketNo = LookupBucketFor(Key);
     StringMapEntryBase *&Bucket = TheTable[BucketNo];
     if (Bucket && Bucket != getTombstoneVal())
@@ -340,7 +351,8 @@
 
     if (Bucket == getTombstoneVal())
       --NumTombstones;
-    Bucket = MapEntryTy::Create(Key, Allocator, std::forward<ArgsTy>(Args)...);
+    Bucket =
+        MapEntryTy::create(Key, getAllocator(), std::forward<ArgsTy>(Args)...);
     ++NumItems;
     assert(NumItems + NumTombstones <= NumBuckets);
 
@@ -358,7 +370,7 @@
     for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
       StringMapEntryBase *&Bucket = TheTable[I];
       if (Bucket && Bucket != getTombstoneVal()) {
-        static_cast<MapEntryTy *>(Bucket)->Destroy(Allocator);
+        static_cast<MapEntryTy *>(Bucket)->Destroy(getAllocator());
       }
       Bucket = nullptr;
     }
@@ -374,7 +386,7 @@
   void erase(iterator I) {
     MapEntryTy &V = *I;
     remove(&V);
-    V.Destroy(Allocator);
+    V.Destroy(getAllocator());
   }
 
   bool erase(std::string_view Key) {
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringMapEntry.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringMapEntry.h
index 2b33aa6..88de972 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringMapEntry.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringMapEntry.h
@@ -77,7 +77,7 @@
   explicit StringMapEntryStorage(size_t keyLength)
       : StringMapEntryBase(keyLength), second() {}
   template <typename... InitTy>
-  StringMapEntryStorage(size_t keyLength, InitTy &&... initVals)
+  StringMapEntryStorage(size_t keyLength, InitTy &&...initVals)
       : StringMapEntryBase(keyLength),
         second(std::forward<InitTy>(initVals)...) {}
   StringMapEntryStorage(StringMapEntryStorage &e) = delete;
@@ -88,9 +88,11 @@
   void setValue(const ValueTy &V) { second = V; }
 };
 
-template <> class StringMapEntryStorage<std::nullopt_t> : public StringMapEntryBase {
+template <>
+class StringMapEntryStorage<std::nullopt_t> : public StringMapEntryBase {
 public:
-  explicit StringMapEntryStorage(size_t keyLength, std::nullopt_t = std::nullopt)
+  explicit StringMapEntryStorage(size_t keyLength,
+                                 std::nullopt_t = std::nullopt)
       : StringMapEntryBase(keyLength) {}
   StringMapEntryStorage(StringMapEntryStorage &entry) = delete;
 
@@ -105,6 +107,8 @@
 public:
   using StringMapEntryStorage<ValueTy>::StringMapEntryStorage;
 
+  using ValueType = ValueTy;
+
   std::string_view getKey() const {
     return std::string_view(getKeyData(), this->getKeyLength());
   }
@@ -123,7 +127,7 @@
   /// Create a StringMapEntry for the specified key construct the value using
   /// \p InitiVals.
   template <typename AllocatorTy, typename... InitTy>
-  static StringMapEntry *Create(std::string_view key, AllocatorTy &allocator,
+  static StringMapEntry *create(std::string_view key, AllocatorTy &allocator,
                                 InitTy &&... initVals) {
     return new (StringMapEntryBase::allocateWithKey(
         sizeof(StringMapEntry), alignof(StringMapEntry), key, allocator))
@@ -148,6 +152,26 @@
   }
 };
 
+// Allow structured bindings on StringMapEntry.
+template <std::size_t Index, typename ValueTy>
+decltype(auto) get(const StringMapEntry<ValueTy> &E) {
+  static_assert(Index < 2);
+  if constexpr (Index == 0)
+    return E.first();
+  else
+    return E.second;
+}
+
 } // end namespace wpi
 
+namespace std {
+template <typename ValueTy>
+struct tuple_size<wpi::StringMapEntry<ValueTy>>
+    : std::integral_constant<std::size_t, 2> {};
+
+template <std::size_t I, typename ValueTy>
+struct tuple_element<I, wpi::StringMapEntry<ValueTy>>
+    : std::conditional<I == 0, std::string_view, ValueTy> {};
+} // namespace std
+
 #endif // WPIUTIL_WPI_STRINGMAPENTRY_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SwapByteOrder.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SwapByteOrder.h
index ab2791a..af7fcf9 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SwapByteOrder.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/SwapByteOrder.h
@@ -14,12 +14,10 @@
 #ifndef WPIUTIL_WPI_SWAPBYTEORDER_H
 #define WPIUTIL_WPI_SWAPBYTEORDER_H
 
+#include "wpi/bit.h"
 #include <cstddef>
 #include <cstdint>
 #include <type_traits>
-#if defined(_MSC_VER) && !defined(_DEBUG)
-#include <stdlib.h>
-#endif
 
 #if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__) ||            \
     defined(__Fuchsia__) || defined(__EMSCRIPTEN__)
@@ -48,48 +46,6 @@
 
 namespace wpi {
 
-/// ByteSwap_16 - This function returns a byte-swapped representation of
-/// the 16-bit argument.
-inline uint16_t ByteSwap_16(uint16_t value) {
-#if defined(_MSC_VER) && !defined(_DEBUG)
-  // The DLL version of the runtime lacks these functions (bug!?), but in a
-  // release build they're replaced with BSWAP instructions anyway.
-  return _byteswap_ushort(value);
-#else
-  uint16_t Hi = value << 8;
-  uint16_t Lo = value >> 8;
-  return Hi | Lo;
-#endif
-}
-
-/// This function returns a byte-swapped representation of the 32-bit argument.
-inline uint32_t ByteSwap_32(uint32_t value) {
-#if defined(__llvm__) || (defined(__GNUC__) && !defined(__ICC))
-  return __builtin_bswap32(value);
-#elif defined(_MSC_VER) && !defined(_DEBUG)
-  return _byteswap_ulong(value);
-#else
-  uint32_t Byte0 = value & 0x000000FF;
-  uint32_t Byte1 = value & 0x0000FF00;
-  uint32_t Byte2 = value & 0x00FF0000;
-  uint32_t Byte3 = value & 0xFF000000;
-  return (Byte0 << 24) | (Byte1 << 8) | (Byte2 >> 8) | (Byte3 >> 24);
-#endif
-}
-
-/// This function returns a byte-swapped representation of the 64-bit argument.
-inline uint64_t ByteSwap_64(uint64_t value) {
-#if defined(__llvm__) || (defined(__GNUC__) && !defined(__ICC))
-  return __builtin_bswap64(value);
-#elif defined(_MSC_VER) && !defined(_DEBUG)
-  return _byteswap_uint64(value);
-#else
-  uint64_t Hi = ByteSwap_32(uint32_t(value));
-  uint32_t Lo = ByteSwap_32(uint32_t(value >> 32));
-  return (Hi << 32) | Lo;
-#endif
-}
-
 namespace sys {
 
 #if defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN
@@ -100,33 +56,21 @@
 
 static const bool IsLittleEndianHost = !IsBigEndianHost;
 
-inline unsigned char  getSwappedBytes(unsigned char C) { return C; }
-inline   signed char  getSwappedBytes(signed char C) { return C; }
-inline          char  getSwappedBytes(char C) { return C; }
+inline unsigned char      getSwappedBytes(unsigned char      C) { return wpi::byteswap(C); }
+inline   signed char      getSwappedBytes( signed  char      C) { return wpi::byteswap(C); }
+inline          char      getSwappedBytes(         char      C) { return wpi::byteswap(C); }
 
-inline unsigned short getSwappedBytes(unsigned short C) { return ByteSwap_16(C); }
-inline   signed short getSwappedBytes(  signed short C) { return ByteSwap_16(C); }
+inline unsigned short     getSwappedBytes(unsigned short     C) { return wpi::byteswap(C); }
+inline   signed short     getSwappedBytes(  signed short     C) { return wpi::byteswap(C); }
 
-inline unsigned int   getSwappedBytes(unsigned int   C) { return ByteSwap_32(C); }
-inline   signed int   getSwappedBytes(  signed int   C) { return ByteSwap_32(C); }
+inline unsigned int       getSwappedBytes(unsigned int       C) { return wpi::byteswap(C); }
+inline   signed int       getSwappedBytes(  signed int       C) { return wpi::byteswap(C); }
 
-inline unsigned long getSwappedBytes(unsigned long C) {
-  // Handle LLP64 and LP64 platforms.
-  return sizeof(long) == sizeof(int) ? ByteSwap_32((uint32_t)C)
-                                     : ByteSwap_64((uint64_t)C);
-}
-inline signed long getSwappedBytes(signed long C) {
-  // Handle LLP64 and LP64 platforms.
-  return sizeof(long) == sizeof(int) ? ByteSwap_32((uint32_t)C)
-                                     : ByteSwap_64((uint64_t)C);
-}
+inline unsigned long      getSwappedBytes(unsigned long      C) { return wpi::byteswap(C); }
+inline   signed long      getSwappedBytes(  signed long      C) { return wpi::byteswap(C); }
 
-inline unsigned long long getSwappedBytes(unsigned long long C) {
-  return ByteSwap_64(C);
-}
-inline signed long long getSwappedBytes(signed long long C) {
-  return ByteSwap_64(C);
-}
+inline unsigned long long getSwappedBytes(unsigned long long C) { return wpi::byteswap(C); }
+inline   signed long long getSwappedBytes(  signed long long C) { return wpi::byteswap(C); }
 
 inline float getSwappedBytes(float C) {
   union {
@@ -134,7 +78,7 @@
     float f;
   } in, out;
   in.f = C;
-  out.i = ByteSwap_32(in.i);
+  out.i = wpi::byteswap(in.i);
   return out.f;
 }
 
@@ -144,14 +88,14 @@
     double d;
   } in, out;
   in.d = C;
-  out.i = ByteSwap_64(in.i);
+  out.i = wpi::byteswap(in.i);
   return out.d;
 }
 
 template <typename T>
-inline std::enable_if_t<std::is_enum<T>::value, T> getSwappedBytes(T C) {
+inline std::enable_if_t<std::is_enum_v<T>, T> getSwappedBytes(T C) {
   return static_cast<T>(
-      getSwappedBytes(static_cast<std::underlying_type_t<T>>(C)));
+      wpi::byteswap(static_cast<std::underlying_type_t<T>>(C)));
 }
 
 template<typename T>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/VersionTuple.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/VersionTuple.h
index c08ff37..02f24dd 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/VersionTuple.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/VersionTuple.h
@@ -16,11 +16,14 @@
 
 #include "wpi/DenseMapInfo.h"
 #include "wpi/Hashing.h"
+#include "wpi/Endian.h"
 #include <optional>
 #include <string>
 #include <tuple>
 
 namespace wpi {
+template <typename HasherT, support::endianness Endianness>
+class HashBuilderImpl;
 class raw_ostream;
 
 /// Represents a version number in the form major[.minor[.subminor[.build]]].
@@ -37,24 +40,25 @@
   unsigned HasBuild : 1;
 
 public:
-  VersionTuple()
+  constexpr VersionTuple()
       : Major(0), Minor(0), HasMinor(false), Subminor(0), HasSubminor(false),
         Build(0), HasBuild(false) {}
 
-  explicit VersionTuple(unsigned Major)
+  explicit constexpr VersionTuple(unsigned Major)
       : Major(Major), Minor(0), HasMinor(false), Subminor(0),
         HasSubminor(false), Build(0), HasBuild(false) {}
 
-  explicit VersionTuple(unsigned Major, unsigned Minor)
+  explicit constexpr VersionTuple(unsigned Major, unsigned Minor)
       : Major(Major), Minor(Minor), HasMinor(true), Subminor(0),
         HasSubminor(false), Build(0), HasBuild(false) {}
 
-  explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor)
+  explicit constexpr VersionTuple(unsigned Major, unsigned Minor,
+                                  unsigned Subminor)
       : Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor),
         HasSubminor(true), Build(0), HasBuild(false) {}
 
-  explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor,
-                        unsigned Build)
+  explicit constexpr VersionTuple(unsigned Major, unsigned Minor,
+                                  unsigned Subminor, unsigned Build)
       : Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor),
         HasSubminor(true), Build(Build), HasBuild(true) {}
 
@@ -95,6 +99,12 @@
     return *this;
   }
 
+  /// Return a version tuple that contains a different major version but
+  /// everything else is the same.
+  VersionTuple withMajorReplaced(unsigned NewMajor) const {
+    return VersionTuple(NewMajor, Minor, Subminor, Build);
+  }
+
   /// Return a version tuple that contains only components that are non-zero.
   VersionTuple normalize() const {
     VersionTuple Result = *this;
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/bit.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/bit.h
new file mode 100644
index 0000000..fce6d85
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/bit.h
@@ -0,0 +1,99 @@
+//===-- llvm/ADT/bit.h - C++20 <bit> ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the C++20 <bit> header.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef WPIUTIL_WPI_BIT_H
+#define WPIUTIL_WPI_BIT_H
+
+#include "wpi/Compiler.h"
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+
+#if !__has_builtin(__builtin_bit_cast)
+#include <cstring>
+#endif
+
+#if defined(_MSC_VER) && !defined(_DEBUG)
+#include <cstdlib>  // for _byteswap_{ushort,ulong,uint64}
+#endif
+
+namespace wpi {
+
+// This implementation of bit_cast is different from the C++20 one in two ways:
+//  - It isn't constexpr because that requires compiler support.
+//  - It requires trivially-constructible To, to avoid UB in the implementation.
+template <
+    typename To, typename From,
+    typename = std::enable_if_t<sizeof(To) == sizeof(From)>,
+    typename = std::enable_if_t<std::is_trivially_constructible<To>::value>,
+    typename = std::enable_if_t<std::is_trivially_copyable<To>::value>,
+    typename = std::enable_if_t<std::is_trivially_copyable<From>::value>>
+[[nodiscard]] inline To bit_cast(const From &from) noexcept {
+#if __has_builtin(__builtin_bit_cast)
+  return __builtin_bit_cast(To, from);
+#else
+  To to;
+  std::memcpy(&to, &from, sizeof(To));
+  return to;
+#endif
+}
+
+/// Reverses the bytes in the given integer value V.
+template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
+[[nodiscard]] constexpr T byteswap(T V) noexcept {
+  if constexpr (sizeof(T) == 1) {
+    return V;
+  } else if constexpr (sizeof(T) == 2) {
+    uint16_t UV = V;
+#if defined(_MSC_VER) && !defined(_DEBUG)
+    // The DLL version of the runtime lacks these functions (bug!?), but in a
+    // release build they're replaced with BSWAP instructions anyway.
+    return _byteswap_ushort(UV);
+#else
+    uint16_t Hi = UV << 8;
+    uint16_t Lo = UV >> 8;
+    return Hi | Lo;
+#endif
+  } else if constexpr (sizeof(T) == 4) {
+    uint32_t UV = V;
+#if __has_builtin(__builtin_bswap32)
+    return __builtin_bswap32(UV);
+#elif defined(_MSC_VER) && !defined(_DEBUG)
+    return _byteswap_ulong(UV);
+#else
+    uint32_t Byte0 = UV & 0x000000FF;
+    uint32_t Byte1 = UV & 0x0000FF00;
+    uint32_t Byte2 = UV & 0x00FF0000;
+    uint32_t Byte3 = UV & 0xFF000000;
+    return (Byte0 << 24) | (Byte1 << 8) | (Byte2 >> 8) | (Byte3 >> 24);
+#endif
+  } else if constexpr (sizeof(T) == 8) {
+    uint64_t UV = V;
+#if __has_builtin(__builtin_bswap64)
+    return __builtin_bswap64(UV);
+#elif defined(_MSC_VER) && !defined(_DEBUG)
+    return _byteswap_uint64(UV);
+#else
+    uint64_t Hi = wpi::byteswap<uint32_t>(UV);
+    uint32_t Lo = wpi::byteswap<uint32_t>(UV >> 32);
+    return (Hi << 32) | Lo;
+#endif
+  } else {
+    static_assert(!sizeof(T *), "Don't know how to handle the given type.");
+    return 0;
+  }
+}
+
+} // namespace wpi
+
+#endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/iterator_range.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/iterator_range.h
index ce0e4ee..afb7343 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/iterator_range.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/iterator_range.h
@@ -18,10 +18,22 @@
 #ifndef WPIUTIL_WPI_ITERATOR_RANGE_H
 #define WPIUTIL_WPI_ITERATOR_RANGE_H
 
+#include "wpi/ADL.h"
+#include <type_traits>
 #include <utility>
 
 namespace wpi {
 
+template <typename From, typename To, typename = void>
+struct explicitly_convertible : std::false_type {};
+
+template <typename From, typename To>
+struct explicitly_convertible<
+    From, To,
+    std::void_t<decltype(static_cast<To>(
+        std::declval<std::add_rvalue_reference_t<From>>()))>> : std::true_type {
+};
+
 /// A range adaptor for a pair of iterators.
 ///
 /// This just wraps two iterators into a range-compatible interface. Nothing
@@ -31,12 +43,19 @@
   IteratorT begin_iterator, end_iterator;
 
 public:
-  //TODO: Add SFINAE to test that the Container's iterators match the range's
-  //      iterators.
+#if __GNUC__ == 7
+  // Be careful no to break gcc-7 on the mlir target.
+  // See https://github.com/llvm/llvm-project/issues/63843
   template <typename Container>
+#else
+  template <typename Container,
+            std::enable_if_t<explicitly_convertible<
+                detail::IterOfRange<Container>, IteratorT>::value> * = nullptr>
+#endif
   iterator_range(Container &&c)
-  //TODO: Consider ADL/non-member begin/end calls.
-      : begin_iterator(c.begin()), end_iterator(c.end()) {}
+      : begin_iterator(adl_begin(std::forward<Container>(c))),
+        end_iterator(adl_end(std::forward<Container>(c))) {
+  }
   iterator_range(IteratorT begin_iterator, IteratorT end_iterator)
       : begin_iterator(std::move(begin_iterator)),
         end_iterator(std::move(end_iterator)) {}
@@ -46,6 +65,9 @@
   bool empty() const { return begin_iterator == end_iterator; }
 };
 
+template <typename Container>
+iterator_range(Container &&) -> iterator_range<detail::IterOfRange<Container>>;
+
 /// Convenience function for iterating over sub-ranges.
 ///
 /// This provides a bit of syntactic sugar to make using sub-ranges
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/raw_ostream.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/raw_ostream.h
index 350262b..06ff206 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/raw_ostream.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/raw_ostream.h
@@ -14,15 +14,14 @@
 #define WPIUTIL_WPI_RAW_OSTREAM_H
 
 #include "wpi/SmallVector.h"
-#include <span>
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
+#include <optional>
+#include <span>
 #include <string>
-#if __cplusplus > 201402L
 #include <string_view>
-#endif
 #include <system_error>
 #include <type_traits>
 #include <vector>
@@ -229,6 +228,20 @@
     return *this;
   }
 
+#if defined(__cpp_char8_t)
+  // When using `char8_t *` integers or pointers are written to the ostream
+  // instead of UTF-8 code as one might expect. This might lead to unexpected
+  // behavior, especially as `u8""` literals are of type `char8_t*` instead of
+  // type `char_t*` from C++20 onwards. Thus we disallow using them with
+  // raw_ostreams.
+  // If you have u8"" literals to stream, you can rewrite them as ordinary
+  // literals with escape sequences
+  // e.g.  replace `u8"\u00a0"` by `"\xc2\xa0"`
+  // or use `reinterpret_cast`:
+  // e.g. replace `u8"\u00a0"` by `reinterpret_cast<const char *>(u8"\u00a0")`
+  raw_ostream &operator<<(const char8_t *Str) = delete;
+#endif
+
   raw_ostream &operator<<(const char *Str) {
     // Inline fast path, particularly for constant strings where a sufficiently
     // smart compiler will simplify strlen.
@@ -343,6 +356,11 @@
     SetBufferAndMode(BufferStart, Size, BufferKind::ExternalBuffer);
   }
 
+  /// Force-set the number of bytes in the raw_ostream buffer.
+  void SetNumBytesInBuffer(size_t Size) {
+    OutBufCur = OutBufStart + Size;
+  }
+
   /// Return an efficient buffer size for the underlying output mechanism.
   virtual size_t preferred_buffer_size() const;
 
@@ -374,8 +392,8 @@
 /// Call the appropriate insertion operator, given an rvalue reference to a
 /// raw_ostream object and return a stream of the same type as the argument.
 template <typename OStream, typename T>
-std::enable_if_t<!std::is_reference<OStream>::value &&
-                     std::is_base_of<raw_ostream, OStream>::value,
+std::enable_if_t<!std::is_reference_v<OStream> &&
+                     std::is_base_of_v<raw_ostream, OStream>,
                  OStream &&>
 operator<<(OStream &&OS, const T &Value) {
   OS << Value;
@@ -734,7 +752,7 @@
   raw_ostream &OS;
   SmallVector<char, 0> Buffer;
 
-  virtual void anchor() override;
+  void anchor() override;
 
 public:
   buffer_ostream(raw_ostream &OS) : raw_svector_ostream(Buffer), OS(OS) {}
@@ -745,7 +763,7 @@
   std::unique_ptr<raw_ostream> OS;
   SmallVector<char, 0> Buffer;
 
-  virtual void anchor() override;
+  void anchor() override;
 
 public:
   buffer_unique_ostream(std::unique_ptr<raw_ostream> OS)
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/type_traits.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/type_traits.h
index 53e18d3..d74fd0f 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/type_traits.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/type_traits.h
@@ -32,11 +32,11 @@
 
 public:
   static const bool value =
-      !std::is_class<UnderlyingT>::value && // Filter conversion operators.
-      !std::is_pointer<UnderlyingT>::value &&
-      !std::is_floating_point<UnderlyingT>::value &&
-      (std::is_enum<UnderlyingT>::value ||
-       std::is_convertible<UnderlyingT, unsigned long long>::value);
+      !std::is_class_v<UnderlyingT> && // Filter conversion operators.
+      !std::is_pointer_v<UnderlyingT> &&
+      !std::is_floating_point_v<UnderlyingT> &&
+      (std::is_enum_v<UnderlyingT> ||
+       std::is_convertible_v<UnderlyingT, unsigned long long>);
 };
 
 /// If T is a pointer, just return it. If it is not, return T&.
@@ -45,7 +45,7 @@
 
 template <typename T>
 struct add_lvalue_reference_if_not_pointer<
-    T, std::enable_if_t<std::is_pointer<T>::value>> {
+    T, std::enable_if_t<std::is_pointer_v<T>>> {
   using type = T;
 };
 
@@ -55,7 +55,7 @@
 struct add_const_past_pointer { using type = const T; };
 
 template <typename T>
-struct add_const_past_pointer<T, std::enable_if_t<std::is_pointer<T>::value>> {
+struct add_const_past_pointer<T, std::enable_if_t<std::is_pointer_v<T>>> {
   using type = const std::remove_pointer_t<T> *;
 };
 
@@ -64,27 +64,11 @@
   using type = const T &;
 };
 template <typename T>
-struct const_pointer_or_const_ref<T,
-                                  std::enable_if_t<std::is_pointer<T>::value>> {
+struct const_pointer_or_const_ref<T, std::enable_if_t<std::is_pointer_v<T>>> {
   using type = typename add_const_past_pointer<T>::type;
 };
 
 namespace detail {
-/// Internal utility to detect trivial copy construction.
-template<typename T> union copy_construction_triviality_helper {
-    T t;
-    copy_construction_triviality_helper() = default;
-    copy_construction_triviality_helper(const copy_construction_triviality_helper&) = default;
-    ~copy_construction_triviality_helper() = default;
-};
-/// Internal utility to detect trivial move construction.
-template<typename T> union move_construction_triviality_helper {
-    T t;
-    move_construction_triviality_helper() = default;
-    move_construction_triviality_helper(move_construction_triviality_helper&&) = default;
-    ~move_construction_triviality_helper() = default;
-};
-
 template<class T>
 union trivial_helper {
     T t;
@@ -92,13 +76,6 @@
 
 } // end namespace detail
 
-template <typename T>
-using is_trivially_move_constructible = std::is_trivially_move_constructible<T>;
-
-template <typename T>
-using is_trivially_copy_constructible = std::is_trivially_copy_constructible<T>;
-
-
 } // end namespace wpi
 
 #endif // WPIUTIL_WPI_TYPE_TRAITS_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/xxhash.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/xxhash.h
new file mode 100644
index 0000000..e0284bc
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/llvm/include/wpi/xxhash.h
@@ -0,0 +1,56 @@
+/*
+   xxHash - Extremely Fast Hash algorithm
+   Header File
+   Copyright (C) 2012-2016, Yann Collet.
+
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * 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.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+   OWNER OR 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.
+
+   You can contact the author at :
+   - xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+/* based on revision d2df04efcbef7d7f6886d345861e5dfda4edacc1 Removed
+ * everything but a simple interface for computing XXh64. */
+
+#ifndef WPIUTIL_WPI_XXHASH_H
+#define WPIUTIL_WPI_XXHASH_H
+
+#include <stdint.h>
+
+#include <span>
+#include <string_view>
+
+namespace wpi {
+uint64_t xxHash64(std::string_view Data);
+uint64_t xxHash64(std::span<const uint8_t> Data);
+
+uint64_t xxh3_64bits(std::span<const uint8_t> data);
+inline uint64_t xxh3_64bits(std::string_view data) {
+  return xxh3_64bits(std::span(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
+}
+}
+
+#endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/aligned_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/aligned_allocator.hpp
index 8118ec4..3083a40 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/aligned_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/aligned_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_ALIGNED_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_ALIGNED_ALLOCATOR_HPP_INCLUDED
@@ -52,7 +51,7 @@
             aligned_allocator& operator=(aligned_allocator&& other) noexcept
             {
                 allocator_type::operator=(detail::move(other));
-                min_alignment_          = other.min_alignment_;
+                min_alignment_ = other.min_alignment_;
                 return *this;
             }
             /// @}
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/allocator_storage.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/allocator_storage.hpp
index 8dab2e0..dc90672 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/allocator_storage.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/allocator_storage.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_ALLOCATOR_STORAGE_HPP_INCLUDED
 #define WPI_MEMORY_ALLOCATOR_STORAGE_HPP_INCLUDED
@@ -126,7 +125,7 @@
                 WPI_REQUIRES(
                     (!std::is_base_of<allocator_storage, typename std::decay<Alloc>::type>::value))>
             allocator_storage(Alloc&& alloc,
-                              WPI_SFINAE(new storage_policy(detail::forward<Alloc>(alloc))))
+                              WPI_SFINAE(new storage_policy(std::declval<Alloc>())))
             : storage_policy(detail::forward<Alloc>(alloc))
             {
             }
@@ -136,8 +135,10 @@
             /// \requires The expression <tt>new storage_policy(other.get_allocator())</tt> must be well-formed,
             /// otherwise this constructor does not participate in overload resolution.
             template <class OtherPolicy>
-            allocator_storage(const allocator_storage<OtherPolicy, Mutex>& other,
-                              WPI_SFINAE(new storage_policy(other.get_allocator())))
+            allocator_storage(
+                const allocator_storage<OtherPolicy, Mutex>& other,
+                WPI_SFINAE(new storage_policy(
+                    std::declval<const allocator_storage<OtherPolicy, Mutex>&>().get_allocator())))
             : storage_policy(other.get_allocator())
             {
             }
@@ -165,7 +166,7 @@
             /// @{
             /// \effects Copies the \c allocator_storage object.
             /// \requires The \c StoragePolicy must be copyable.
-            allocator_storage(const allocator_storage&) = default;
+            allocator_storage(const allocator_storage&)            = default;
             allocator_storage& operator=(const allocator_storage&) = default;
             /// @}
 
@@ -296,9 +297,9 @@
                 return detail::lock_allocator(get_allocator(), static_cast<actual_mutex&>(*this));
             }
 
-            auto lock() const noexcept -> WPI_IMPL_DEFINED(decltype(
-                detail::lock_allocator(std::declval<const storage_policy>().get_allocator(),
-                                       std::declval<actual_mutex&>())))
+            auto lock() const noexcept -> WPI_IMPL_DEFINED(decltype(detail::lock_allocator(
+                std::declval<const storage_policy>().get_allocator(),
+                std::declval<actual_mutex&>())))
             {
                 return detail::lock_allocator(get_allocator(), static_cast<actual_mutex&>(*this));
             }
@@ -552,9 +553,9 @@
         {
             using storage = detail::reference_storage_impl<
                 typename allocator_traits<RawAllocator>::allocator_type,
-                decltype(
-                    detail::reference_type(typename allocator_traits<RawAllocator>::is_stateful{},
-                                           is_shared_allocator<RawAllocator>{}))>;
+                decltype(detail::reference_type(typename allocator_traits<
+                                                    RawAllocator>::is_stateful{},
+                                                is_shared_allocator<RawAllocator>{}))>;
 
         public:
             using allocator_type = typename allocator_traits<RawAllocator>::allocator_type;
@@ -580,7 +581,7 @@
             /// @{
             /// \effects Copies the \c allocator_reference object.
             /// Only copies the pointer to it in the stateful case.
-            reference_storage(const reference_storage&) noexcept = default;
+            reference_storage(const reference_storage&) noexcept            = default;
             reference_storage& operator=(const reference_storage&) noexcept = default;
             /// @}
 
@@ -795,9 +796,9 @@
             : public base_allocator,
               private detail::reference_storage_impl<
                   typename allocator_traits<RawAllocator>::allocator_type,
-                  decltype(
-                      detail::reference_type(typename allocator_traits<RawAllocator>::is_stateful{},
-                                             is_shared_allocator<RawAllocator>{}))>
+                  decltype(detail::reference_type(typename allocator_traits<
+                                                      RawAllocator>::is_stateful{},
+                                                  is_shared_allocator<RawAllocator>{}))>
             {
                 using traits     = allocator_traits<RawAllocator>;
                 using composable = is_composable_allocator<typename traits::allocator_type>;
@@ -805,7 +806,7 @@
                     typename allocator_traits<RawAllocator>::allocator_type,
                     decltype(detail::reference_type(typename allocator_traits<
                                                         RawAllocator>::is_stateful{},
-                                                    is_shared_allocator<RawAllocator>{}))>;
+                                                       is_shared_allocator<RawAllocator>{}))>;
 
             public:
                 // non stateful
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/allocator_traits.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/allocator_traits.hpp
index 47b6ffb..0e23c63 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/allocator_traits.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/allocator_traits.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_ALLOCATOR_TRAITS_HPP_INCLUDED
 #define WPI_MEMORY_ALLOCATOR_TRAITS_HPP_INCLUDED
@@ -375,12 +374,12 @@
 
             template <typename T>
             struct has_invalid_alloc_function
-            : std::is_same<decltype(
-                               traits_detail::allocate_node(traits_detail::full_concept{},
-                                                            std::declval<typename allocator_traits<
-                                                                T>::allocator_type&>(),
-                                                            0, 0)),
-                           traits_detail::error>
+            : std::is_same<
+                  decltype(traits_detail::allocate_node(traits_detail::full_concept{},
+                                                        std::declval<typename allocator_traits<
+                                                            T>::allocator_type&>(),
+                                                        0, 0)),
+                  traits_detail::error>
             {
             };
 
@@ -564,13 +563,12 @@
 
             template <typename T>
             struct has_invalid_try_dealloc_function
-            : std::is_same<
-                  decltype(
-                      traits_detail::try_deallocate_node(traits_detail::full_concept{},
-                                                         std::declval<typename allocator_traits<
-                                                             T>::allocator_type&>(),
-                                                         nullptr, 0, 0)),
-                  traits_detail::error>
+            : std::is_same<decltype(traits_detail::
+                                        try_deallocate_node(traits_detail::full_concept{},
+                                                            std::declval<typename allocator_traits<
+                                                                T>::allocator_type&>(),
+                                                            nullptr, 0, 0)),
+                           traits_detail::error>
             {
             };
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/config.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/config.hpp
index 067ecd2..40681bd 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/config.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/config.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 /// \file
 /// Configuration macros.
@@ -140,7 +139,7 @@
 /// Set to `1` to disable automatic lifetime managment of the per-thread stack,
 /// requires managing it through the \ref wpi::memory::temporary_stack_initializer.
 /// Set to `0` to disable the per-thread stack completely.
-/// \ref get_temporary_stack() will abort the program upon call.
+/// \ref wpi::memory::get_temporary_stack() will abort the program upon call.
 /// \ingroup allocator
 #define WPI_MEMORY_TEMPORARY_STACK_MODE 2
 #endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/container.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/container.hpp
index fd1547f..8f8eee5 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/container.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/container.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_CONTAINER_HPP_INCLUDED
 #define WPI_MEMORY_CONTAINER_HPP_INCLUDED
@@ -276,8 +275,9 @@
         /// @{
 
         /// Contains the node size of a node based STL container with a specific type.
-        /// These classes are auto-generated and only available if the tools are build and without
-        /// cross-compiling.
+        ///
+        /// This trait is auto-generated and may not be available depending on the build configuration,
+        /// especially when doing cross compilation.
         template <typename T>
         struct forward_list_node_size : std::integral_constant<std::size_t, implementation_defined>
         {
@@ -314,25 +314,30 @@
         {
         };
 
-        /// \copydoc forward_list_node_size
+        /// Contains the node size of a node based STL container with a specific type.
+        ///
+        /// This trait is auto-generated and may not be available depending on the build configuration,
+        /// especially when doing cross compilation.
+        ///
+        /// \notes `T` is always the `value_type` of the container, e.g. `std::pair<const Key, Value>`.
         template <typename T>
         struct map_node_size : std::integral_constant<std::size_t, implementation_defined>
         {
         };
 
-        /// \copydoc forward_list_node_size
+        /// \copydoc map_node_size
         template <typename T>
         struct multimap_node_size : std::integral_constant<std::size_t, implementation_defined>
         {
         };
 
-        /// \copydoc forward_list_node_size
+        /// \copydoc map_node_size
         template <typename T>
         struct unordered_map_node_size : std::integral_constant<std::size_t, implementation_defined>
         {
         };
 
-        /// \copydoc forward_list_node_size
+        /// \copydoc map_node_size
         template <typename T>
         struct unordered_multimap_node_size
         : std::integral_constant<std::size_t, implementation_defined>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/debugging.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/debugging.hpp
index b04050f..580696a 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/debugging.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/debugging.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DEBUGGING_HPP_INCLUDED
 #define WPI_MEMORY_DEBUGGING_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/default_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/default_allocator.hpp
index 107a641..4d78abb 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/default_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/default_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DEFAULT_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_DEFAULT_ALLOCATOR_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/deleter.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/deleter.hpp
index 01c967c..dd0298b 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/deleter.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/deleter.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DELETER_HPP_INCLUDED
 #define WPI_MEMORY_DELETER_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/align.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/align.hpp
index 99d6bcc..5a06340 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/align.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/align.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAIL_ALIGN_HPP_INCLUDED
 #define WPI_MEMORY_DETAIL_ALIGN_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/assert.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/assert.hpp
index b8a7372..620d07b 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/assert.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/assert.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAIL_ASSERT_HPP_INCLUDED
 #define WPI_MEMORY_DETAIL_ASSERT_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/container_node_sizes.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/container_node_sizes.hpp
index 37949ca..34d6d65 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/container_node_sizes.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/container_node_sizes.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAIL_CONTAINER_NODE_SIZES_HPP_INCLUDED
 #define WPI_MEMORY_DETAIL_CONTAINER_NODE_SIZES_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/debug_helpers.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/debug_helpers.hpp
index 3ef2eed..1decb72 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/debug_helpers.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/debug_helpers.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DEBUG_HELPERS_HPP_INCLUDED
 #define WPI_MEMORY_DEBUG_HELPERS_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/ebo_storage.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/ebo_storage.hpp
index de53f7f..640d844 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/ebo_storage.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/ebo_storage.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAIL_EBO_STORAGE_HPP_INCLUDED
 #define WPI_MEMORY_DETAIL_EBO_STORAGE_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/free_list.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/free_list.hpp
index 612706d..4c91324 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/free_list.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/free_list.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAILL_FREE_LIST_HPP_INCLUDED
 #define WPI_MEMORY_DETAILL_FREE_LIST_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/free_list_array.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/free_list_array.hpp
index ec57e1e..17cd1fa 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/free_list_array.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/free_list_array.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAIL_FREE_LIST_ARRAY_HPP
 #define WPI_MEMORY_DETAIL_FREE_LIST_ARRAY_HPP
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/ilog2.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/ilog2.hpp
index 5cf125a..eb4e4f3 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/ilog2.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/ilog2.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAIL_ILOG2_HPP_INCLUDED
 #define WPI_MEMORY_DETAIL_ILOG2_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/lowlevel_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/lowlevel_allocator.hpp
index 2ac45a5..beb8c36 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/lowlevel_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/lowlevel_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAIL_LOWLEVEL_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_DETAIL_LOWLEVEL_ALLOCATOR_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/memory_stack.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/memory_stack.hpp
index e49d480..3d38dba 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/memory_stack.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/memory_stack.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAIL_MEMORY_STACK_HPP_INCLUDED
 #define WPI_MEMORY_DETAIL_MEMORY_STACK_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/small_free_list.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/small_free_list.hpp
index 682417d..1ea31fc 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/small_free_list.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/small_free_list.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAIL_SMALL_FREE_LIST_HPP_INCLUDED
 #define WPI_MEMORY_DETAIL_SMALL_FREE_LIST_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/utility.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/utility.hpp
index c746fa2..b33aa16 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/utility.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/detail/utility.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_DETAIL_UTILITY_HPP
 #define WPI_MEMORY_DETAIL_UTILITY_HPP
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/error.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/error.hpp
index 5ff99cf..3d2f2fa 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/error.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/error.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 /// \file
 /// The exception classes.
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/fallback_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/fallback_allocator.hpp
index 3bf530e..456e84e 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/fallback_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/fallback_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_FALLBACK_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_FALLBACK_ALLOCATOR_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/heap_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/heap_allocator.hpp
index 0724937..803a705 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/heap_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/heap_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_HEAP_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_HEAP_ALLOCATOR_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/iteration_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/iteration_allocator.hpp
index d35a927..fafb72c 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/iteration_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/iteration_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_ITERATION_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_ITERATION_ALLOCATOR_HPP_INCLUDED
@@ -77,8 +76,8 @@
             iteration_allocator& operator=(iteration_allocator&& other) noexcept
             {
                 allocator_type::operator=(detail::move(other));
-                block_                  = other.block_;
-                cur_                    = other.cur_;
+                block_ = other.block_;
+                cur_   = other.cur_;
 
                 for (auto i = 0u; i != N; ++i)
                     stacks_[i] = detail::move(other.stacks_[i]);
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/joint_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/joint_allocator.hpp
index db67d46..540bbd1 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/joint_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/joint_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_JOINT_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_JOINT_ALLOCATOR_HPP_INCLUDED
@@ -515,7 +514,7 @@
             {
             }
 
-            joint_allocator(const joint_allocator& other) noexcept = default;
+            joint_allocator(const joint_allocator& other) noexcept            = default;
             joint_allocator& operator=(const joint_allocator& other) noexcept = default;
 
             /// \effects Allocates a node with given properties.
@@ -709,7 +708,7 @@
             }
 
             joint_array& operator=(const joint_array&) = delete;
-            joint_array& operator=(joint_array&&) = delete;
+            joint_array& operator=(joint_array&&)      = delete;
 
             //=== accessors ===//
             /// @{
@@ -810,7 +809,7 @@
                         stack_->unwind(objects_);
                 }
 
-                builder(builder&&) = delete;
+                builder(builder&&)            = delete;
                 builder& operator=(builder&&) = delete;
 
                 template <typename... Args>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/malloc_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/malloc_allocator.hpp
index 3be9820..a5da1f4 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/malloc_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/malloc_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_MALLOC_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_MALLOC_ALLOCATOR_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_arena.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_arena.hpp
index a634993..c4469e8 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_arena.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_arena.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_MEMORY_ARENA_HPP_INCLUDED
 #define WPI_MEMORY_MEMORY_ARENA_HPP_INCLUDED
@@ -548,7 +547,7 @@
                 if (block_size_)
                 {
                     auto         mem = traits::allocate_array(get_allocator(), block_size_, 1,
-                                                      detail::max_alignment);
+                                                              detail::max_alignment);
                     memory_block block(mem, block_size_);
                     block_size_ = 0u;
                     return block;
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool.hpp
index 09c2b66..2823686 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_MEMORY_POOL_HPP_INCLUDED
 #define WPI_MEMORY_MEMORY_POOL_HPP_INCLUDED
@@ -103,8 +102,8 @@
             memory_pool& operator=(memory_pool&& other) noexcept
             {
                 leak_checker::operator=(detail::move(other));
-                arena_                = detail::move(other.arena_);
-                free_list_            = detail::move(other.free_list_);
+                arena_     = detail::move(other.arena_);
+                free_list_ = detail::move(other.free_list_);
                 return *this;
             }
             /// @}
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool_collection.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool_collection.hpp
index 294dd0a..0bd0248 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool_collection.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool_collection.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_MEMORY_POOL_COLLECTION_HPP_INCLUDED
 #define WPI_MEMORY_MEMORY_POOL_COLLECTION_HPP_INCLUDED
@@ -76,8 +75,7 @@
             /// the size of the initial memory block and other constructor arguments for the \concept{concept_blockallocator,BlockAllocator}.
             /// The \c BucketDistribution controls how many free lists are created,
             /// but unlike in \ref memory_pool all free lists are initially empty and the first memory block queued.
-            /// \requires \c max_node_size must be a valid \concept{concept_node,node} size
-            /// and \c block_size must be non-zero.
+            /// \requires \c block_size must be non-zero and \c max_node_size must be a valid \concept{concept_node,node} size and smaller than \c block_size divided by the number of pools.
             template <typename... Args>
             memory_pool_collection(std::size_t max_node_size, std::size_t block_size,
                                    Args&&... args)
@@ -85,6 +83,7 @@
               stack_(allocate_block()),
               pools_(stack_, block_end(), max_node_size)
             {
+                detail::check_allocation_size<bad_node_size>(max_node_size, def_capacity(), info());
             }
 
             /// \effects Destroys the \ref memory_pool_collection by returning all memory blocks,
@@ -107,9 +106,9 @@
             memory_pool_collection& operator=(memory_pool_collection&& other) noexcept
             {
                 leak_checker::operator=(detail::move(other));
-                arena_                = detail::move(other.arena_);
-                stack_                = detail::move(other.stack_);
-                pools_                = detail::move(other.pools_);
+                arena_ = detail::move(other.arena_);
+                stack_ = detail::move(other.stack_);
+                pools_ = detail::move(other.pools_);
                 return *this;
             }
             /// @}
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool_type.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool_type.hpp
index a7c8ff8..7ebe4a5 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool_type.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_pool_type.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_MEMORY_POOL_TYPE_HPP_INCLUDED
 #define WPI_MEMORY_MEMORY_POOL_TYPE_HPP_INCLUDED
@@ -28,7 +27,7 @@
         };
 
         /// Tag type defining a memory pool optimized for arrays.
-        /// It keeps the nodes oredered inside the free list and searches the list for an appropriate memory block.
+        /// It keeps the nodes ordered inside the free list and searches the list for an appropriate memory block.
         /// Array allocations are still pretty slow, if the array gets big enough it can get slower than \c new.
         /// Node allocations are still fast, unless there is deallocation in random order.
         /// \note Use this tag type only if you really need to have a memory pool!
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_resource_adapter.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_resource_adapter.hpp
index e805f9b..8428bb2 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_resource_adapter.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_resource_adapter.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_MEMORY_RESOURCE_ADAPTER_HPP_INCLUDED
 #define WPI_MEMORY_MEMORY_RESOURCE_ADAPTER_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_stack.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_stack.hpp
index 4c9f524..0c9d896 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_stack.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/memory_stack.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_MEMORY_STACK_HPP_INCLUDED
 #define WPI_MEMORY_MEMORY_STACK_HPP_INCLUDED
@@ -203,7 +202,8 @@
                         arena_.deallocate_block();
 
                     detail::debug_check_pointer(
-                        [&] {
+                        [&]
+                        {
                             auto cur = arena_.current_block();
                             return m.end == static_cast<char*>(cur.memory) + cur.size;
                         },
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/namespace_alias.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/namespace_alias.hpp
index bf2b95d..4af20c1 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/namespace_alias.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/namespace_alias.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_NAMESPACE_ALIAS_HPP_INCLUDED
 #define WPI_MEMORY_NAMESPACE_ALIAS_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/new_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/new_allocator.hpp
index 8e24f2f..0beec00 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/new_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/new_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_NEW_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_NEW_ALLOCATOR_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/segregator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/segregator.hpp
index fcd02ac..ae06f83 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/segregator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/segregator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_SEGREGATOR_HPP_INCLUDED
 #define WPI_MEMORY_SEGREGATOR_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/smart_ptr.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/smart_ptr.hpp
index 877efd7..016f31b 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/smart_ptr.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/smart_ptr.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_SMART_PTR_HPP_INCLUDED
 #define WPI_MEMORY_SMART_PTR_HPP_INCLUDED
@@ -192,18 +191,6 @@
                                                detail::forward<RawAllocator>(alloc)),
                                            detail::forward<Args>(args)...);
         }
-
-#if !defined(DOXYGEN)
-#include "detail/container_node_sizes.hpp"
-#else
-        /// Contains the node size needed for a `std::shared_ptr`.
-        /// These classes are auto-generated and only available if the tools are build and without cross-compiling.
-        /// \ingroup adapter
-        template <typename T>
-        struct shared_ptr_node_size : std::integral_constant<std::size_t, implementation_defined>
-        {
-        };
-#endif
     } // namespace memory
 } // namespace wpi
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/static_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/static_allocator.hpp
index efbbf73..33242f9 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/static_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/static_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_STATIC_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_STATIC_ALLOCATOR_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/std_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/std_allocator.hpp
index f727a1c..18262eb 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/std_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/std_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_STD_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_STD_ALLOCATOR_HPP_INCLUDED
@@ -134,7 +133,8 @@
                 // MSVC seems to ignore access rights in decltype SFINAE below
                 // use this to prevent this constructor being chosen instead of move/copy for types inheriting from it
                 WPI_REQUIRES((!std::is_base_of<std_allocator, RawAlloc>::value))>
-            std_allocator(RawAlloc& alloc, WPI_SFINAE(alloc_reference(alloc))) noexcept
+            std_allocator(RawAlloc& alloc,
+                          WPI_SFINAE(alloc_reference(std::declval<RawAlloc&>()))) noexcept
             : alloc_reference(alloc)
             {
             }
@@ -149,7 +149,8 @@
                 // MSVC seems to ignore access rights in decltype SFINAE below
                 // use this to prevent this constructor being chosen instead of move/copy for types inheriting from it
                 WPI_REQUIRES((!std::is_base_of<std_allocator, RawAlloc>::value))>
-            std_allocator(const RawAlloc& alloc, WPI_SFINAE(alloc_reference(alloc))) noexcept
+            std_allocator(const RawAlloc& alloc, WPI_SFINAE(alloc_reference(
+                                                     std::declval<const RawAlloc&>()))) noexcept
             : alloc_reference(alloc)
             {
             }
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/temporary_allocator.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/temporary_allocator.hpp
index 7eba76f..6d5d73f 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/temporary_allocator.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/temporary_allocator.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_TEMPORARY_ALLOCATOR_HPP_INCLUDED
 #define WPI_MEMORY_TEMPORARY_ALLOCATOR_HPP_INCLUDED
@@ -195,7 +194,7 @@
             /// \effects Destroys the per-thread stack if it isn't already destroyed.
             ~temporary_stack_initializer() noexcept;
 
-            temporary_stack_initializer(temporary_stack_initializer&&) = delete;
+            temporary_stack_initializer(temporary_stack_initializer&&)            = delete;
             temporary_stack_initializer& operator=(temporary_stack_initializer&&) = delete;
         };
 
@@ -229,7 +228,7 @@
 
             ~temporary_allocator() noexcept;
 
-            temporary_allocator(temporary_allocator&&) = delete;
+            temporary_allocator(temporary_allocator&&)            = delete;
             temporary_allocator& operator=(temporary_allocator&&) = delete;
 
             /// \effects Allocates memory from the internal \ref memory_stack by forwarding to it.
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/threading.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/threading.hpp
index 0314ea4..82629ec 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/threading.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/threading.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_THREADING_HPP_INCLUDED
 #define WPI_MEMORY_THREADING_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/tracking.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/tracking.hpp
index ccbc343..4742a4c 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/tracking.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/tracking.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_TRACKING_HPP_INCLUDED
 #define WPI_MEMORY_TRACKING_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/virtual_memory.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/virtual_memory.hpp
index 33d82f7..360ca78 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/virtual_memory.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/include/wpi/memory/virtual_memory.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_VIRTUAL_MEMORY_HPP_INCLUDED
 #define WPI_MEMORY_VIRTUAL_MEMORY_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/debugging.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/debugging.cpp
index be1b033..f3ab89c 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/debugging.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/debugging.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/debugging.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/align.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/align.cpp
index e1c4f2a..920e13f 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/align.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/align.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/detail/align.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/assert.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/assert.cpp
index 497f7f8..d1d9028 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/assert.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/assert.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/detail/assert.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/debug_helpers.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/debug_helpers.cpp
index b5afb20..405c85f 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/debug_helpers.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/debug_helpers.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/detail/debug_helpers.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list.cpp
index 2ca7034..5ac3950 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/detail/free_list.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list_array.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list_array.cpp
index f4658da..3a103f5 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list_array.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list_array.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/detail/free_list_array.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list_utils.hpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list_utils.hpp
index a752837..106e129 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list_utils.hpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/free_list_utils.hpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #ifndef WPI_MEMORY_SRC_DETAIL_FREE_LIST_UTILS_HPP_INCLUDED
 #define WPI_MEMORY_SRC_DETAIL_FREE_LIST_UTILS_HPP_INCLUDED
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/small_free_list.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/small_free_list.cpp
index 3508daa..08be312 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/small_free_list.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/detail/small_free_list.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/detail/small_free_list.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/error.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/error.cpp
index 6261819..f5ef53c 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/error.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/error.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/error.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/heap_allocator.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/heap_allocator.cpp
index 0f559bd..32d1236 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/heap_allocator.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/heap_allocator.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/heap_allocator.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/iteration_allocator.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/iteration_allocator.cpp
index c174c13..411eba6 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/iteration_allocator.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/iteration_allocator.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/iteration_allocator.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/malloc_allocator.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/malloc_allocator.cpp
index f54817a..f5783c7 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/malloc_allocator.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/malloc_allocator.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/config.hpp"
 #if WPI_HOSTED_IMPLEMENTATION
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_arena.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_arena.cpp
index d8454be..148ed31 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_arena.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_arena.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/memory_arena.hpp"
 
@@ -42,7 +41,7 @@
     auto address = static_cast<const char*>(ptr);
     for (auto cur = head_; cur; cur = cur->prev)
     {
-        auto mem = static_cast<char*>(static_cast<void*>(cur));
+        auto mem = static_cast<char*>(static_cast<void*>(cur)) + implementation_offset();
         if (address >= mem && address < mem + cur->usable_size)
             return true;
     }
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_pool.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_pool.cpp
index 5a5e5ab..47951be 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_pool.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_pool.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/memory_pool.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_pool_collection.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_pool_collection.cpp
index 6366811..550f740 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_pool_collection.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_pool_collection.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/memory_pool_collection.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_stack.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_stack.cpp
index 8db5728..753cfe5 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_stack.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/memory_stack.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/memory_stack.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/new_allocator.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/new_allocator.cpp
index 2791182..d23edf5 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/new_allocator.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/new_allocator.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/new_allocator.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/static_allocator.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/static_allocator.cpp
index 15a88bf..fbc12cc 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/static_allocator.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/static_allocator.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/static_allocator.hpp"
 
@@ -38,9 +37,9 @@
 
 void static_block_allocator::deallocate_block(memory_block block) noexcept
 {
-    detail::
-        debug_check_pointer([&] { return static_cast<char*>(block.memory) + block.size == cur_; },
-                            info(), block.memory);
+    detail::debug_check_pointer([&]
+                                { return static_cast<char*>(block.memory) + block.size == cur_; },
+                                info(), block.memory);
     cur_ -= block_size_;
 }
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/temporary_allocator.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/temporary_allocator.cpp
index fa85e99..de4d7ed 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/temporary_allocator.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/temporary_allocator.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/temporary_allocator.hpp"
 
@@ -216,9 +215,8 @@
 
 namespace
 {
-    WPI_THREAD_LOCAL alignas(
-        temporary_stack) char temporary_stack_storage[sizeof(temporary_stack)];
-    WPI_THREAD_LOCAL bool is_created = false;
+    thread_local alignas(temporary_stack) char temporary_stack_storage[sizeof(temporary_stack)];
+    thread_local bool is_created = false;
 
     temporary_stack& get() noexcept
     {
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/virtual_memory.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/virtual_memory.cpp
index fd7960f..1f54812 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/virtual_memory.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/memory/src/virtual_memory.cpp
@@ -1,6 +1,5 @@
-// Copyright (C) 2015-2021 Müller <jonathanmueller.dev@gmail.com>
-// This file is subject to the license terms in the LICENSE file
-// found in the top-level directory of this distribution.
+// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
+// SPDX-License-Identifier: Zlib
 
 #include "wpi/memory/virtual_memory.hpp"
 
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/mpack/include/wpi/mpack.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/mpack/include/wpi/mpack.h
index 870c26b..504a7b0 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/mpack/include/wpi/mpack.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/mpack/include/wpi/mpack.h
@@ -24,7 +24,7 @@
  */
 
 /*
- * This is the MPack 1.1 amalgamation package.
+ * This is the MPack 1.1.1 amalgamation package.
  *
  * http://github.com/ludocode/mpack
  */
@@ -38,6 +38,11 @@
 #include "mpack-config.h"
 #endif
 
+/**
+ * @defgroup mpack MPack
+ * MPack is a C implementation of an encoder and decoder for the MessagePack
+ * serialization format.
+ */
 
 /* mpack/mpack-platform.h.h */
 
@@ -62,7 +67,8 @@
 
 
 /**
- * @defgroup config Configuration Options
+ * @defgroup mpack_config Configuration Options
+ * @ingroup mpack
  *
  * Defines the MPack configuration options.
  *
@@ -1880,7 +1886,8 @@
 
 
 /**
- * @defgroup common Tags and Common Elements
+ * @defgroup mpack_common Tags and Common Elements
+ * @ingroup mpack
  *
  * Contains types, constants and functions shared by both the encoding
  * and decoding portions of MPack.
@@ -1892,7 +1899,7 @@
 
 #define MPACK_VERSION_MAJOR 1  /**< The major version number of MPack. */
 #define MPACK_VERSION_MINOR 1  /**< The minor version number of MPack. */
-#define MPACK_VERSION_PATCH 0  /**< The patch version number of MPack. */
+#define MPACK_VERSION_PATCH 1  /**< The patch version number of MPack. */
 
 /** A number containing the version number of MPack for comparison purposes. */
 #define MPACK_VERSION ((MPACK_VERSION_MAJOR * 10000) + \
@@ -3056,7 +3063,8 @@
 #endif
 
 /**
- * @defgroup writer Write API
+ * @defgroup mpack_writer Write API
+ * @ingroup mpack
  *
  * The MPack Write API encodes structured data of a fixed (hardcoded) schema to MessagePack.
  *
@@ -3779,7 +3787,7 @@
 /**
  * Starts building an array.
  *
- * Elements must follow, and mpack_complete_map() must be called when done. The
+ * Elements must follow, and mpack_complete_array() must be called when done. The
  * number of elements is determined automatically.
  *
  * If you know ahead of time the number of elements in the array, it is more
@@ -4361,7 +4369,8 @@
 #define MPACK_READER_SMALL_FRACTION_DENOMINATOR 32
 
 /**
- * @defgroup reader Reader API
+ * @defgroup mpack_reader Reader API
+ * @ingroup mpack
  *
  * The MPack Reader API contains functions for imperatively reading dynamically
  * typed data from a MessagePack stream.
@@ -5303,7 +5312,8 @@
 #endif
 
 /**
- * @defgroup expect Expect API
+ * @defgroup mpack_expect Expect API
+ * @ingroup mpack
  *
  * The MPack Expect API allows you to easily read MessagePack data when you
  * expect it to follow a predefined schema.
@@ -6770,7 +6780,8 @@
 #if MPACK_NODE
 
 /**
- * @defgroup node Node API
+ * @defgroup mpack_node Node API
+ * @ingroup mpack
  *
  * The MPack Node API allows you to parse a chunk of MessagePack into a
  * dynamically typed data structure, providing random access to the parsed
@@ -7719,7 +7730,7 @@
  *
  * This returns zero if the tree is in an error state.
  *
- * If this node is not a str, bin or map, @ref mpack_error_type is raised and zero
+ * If this node is not a str, bin or ext, @ref mpack_error_type is raised and zero
  * is returned.
  */
 uint32_t mpack_node_data_len(mpack_node_t node);
@@ -7759,7 +7770,7 @@
  *
  * The pointer is valid as long as the data backing the tree is valid.
  *
- * If this node is not of a str, bin or map, @ref mpack_error_type is raised, and
+ * If this node is not of a str, bin or ext, @ref mpack_error_type is raised, and
  * @c NULL is returned.
  *
  * @see mpack_node_copy_cstr()
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/mpack/src/mpack.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/mpack/src/mpack.cpp
index af5fbd0..1c70ae2 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/mpack/src/mpack.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/mpack/src/mpack.cpp
@@ -24,7 +24,7 @@
  */
 
 /*
- * This is the MPack 1.1 amalgamation package.
+ * This is the MPack 1.1.1 amalgamation package.
  *
  * http://github.com/ludocode/mpack
  */
@@ -427,7 +427,7 @@
         const char* prefix, size_t prefix_size)
 {
     mpack_assert(mpack_tag_type(&tag) == mpack_type_bin);
-    size_t length = (size_t)mpack_snprintf(buffer, buffer_size, "<binary data of length %u", tag.v.l);
+    size_t length = (size_t)mpack_snprintf(buffer, buffer_size, "<binary data of length %" PRIu32 "", tag.v.l);
     mpack_tag_debug_complete_bin_ext(tag, length, buffer, buffer_size, prefix, prefix_size);
 }
 
@@ -436,7 +436,7 @@
         const char* prefix, size_t prefix_size)
 {
     mpack_assert(mpack_tag_type(&tag) == mpack_type_ext);
-    size_t length = (size_t)mpack_snprintf(buffer, buffer_size, "<ext data of type %i and length %u",
+    size_t length = (size_t)mpack_snprintf(buffer, buffer_size, "<ext data of type %i and length %" PRIu32 "",
             mpack_tag_ext_exttype(&tag), mpack_tag_ext_length(&tag));
     mpack_tag_debug_complete_bin_ext(tag, length, buffer, buffer_size, prefix, prefix_size);
 }
@@ -477,7 +477,7 @@
             return;
 
         case mpack_type_str:
-            mpack_snprintf(buffer, buffer_size, "<string of %u bytes>", tag.v.l);
+            mpack_snprintf(buffer, buffer_size, "<string of %" PRIu32 " bytes>", tag.v.l);
             return;
         case mpack_type_bin:
             mpack_tag_debug_pseudo_json_bin(tag, buffer, buffer_size, prefix, prefix_size);
@@ -489,10 +489,10 @@
         #endif
 
         case mpack_type_array:
-            mpack_snprintf(buffer, buffer_size, "<array of %u elements>", tag.v.n);
+            mpack_snprintf(buffer, buffer_size, "<array of %" PRIu32 " elements>", tag.v.n);
             return;
         case mpack_type_map:
-            mpack_snprintf(buffer, buffer_size, "<map of %u key-value pairs>", tag.v.n);
+            mpack_snprintf(buffer, buffer_size, "<map of %" PRIu32 " key-value pairs>", tag.v.n);
             return;
     }
 
@@ -544,22 +544,22 @@
             #endif
             return;
         case mpack_type_str:
-            mpack_snprintf(buffer, buffer_size, "str of %u bytes", tag.v.l);
+            mpack_snprintf(buffer, buffer_size, "str of %" PRIu32 " bytes", tag.v.l);
             return;
         case mpack_type_bin:
-            mpack_snprintf(buffer, buffer_size, "bin of %u bytes", tag.v.l);
+            mpack_snprintf(buffer, buffer_size, "bin of %" PRIu32 " bytes", tag.v.l);
             return;
         #if MPACK_EXTENSIONS
         case mpack_type_ext:
-            mpack_snprintf(buffer, buffer_size, "ext of type %i, %u bytes",
+            mpack_snprintf(buffer, buffer_size, "ext of type %i, %" PRIu32 " bytes",
                     mpack_tag_ext_exttype(&tag), mpack_tag_ext_length(&tag));
             return;
         #endif
         case mpack_type_array:
-            mpack_snprintf(buffer, buffer_size, "array of %u elements", tag.v.n);
+            mpack_snprintf(buffer, buffer_size, "array of %" PRIu32 " elements", tag.v.n);
             return;
         case mpack_type_map:
-            mpack_snprintf(buffer, buffer_size, "map of %u key-value pairs", tag.v.n);
+            mpack_snprintf(buffer, buffer_size, "map of %" PRIu32 " key-value pairs", tag.v.n);
             return;
     }
 
@@ -1039,7 +1039,7 @@
         if (build->nested_compound_elements == 0) {
             if (build->type != mpack_type_map) {
                 ++build->count;
-                mpack_log("adding element to build %p, now %u elements\n", (void*)build, build->count);
+                mpack_log("adding element to build %p, now %" PRIu32 " elements\n", (void*)build, build->count);
             } else if (build->key_needs_value) {
                 build->key_needs_value = false;
                 ++build->count;
@@ -1494,6 +1494,46 @@
     mpack_track_destroy(&writer->track, writer->error != mpack_ok);
     #endif
 
+    #if MPACK_BUILDER
+    mpack_builder_t* builder = &writer->builder;
+    if (builder->current_build != NULL) {
+        // A builder is open!
+
+        // Flag an error, if there's not already an error. You can only skip
+        // closing any open compound types if a write error occurred. If there
+        // wasn't already an error, it's a bug, which will assert in debug.
+        if (mpack_writer_error(writer) == mpack_ok) {
+            mpack_break("writer cannot be destroyed with an incomplete builder unless "
+                    "an error was flagged!");
+            mpack_writer_flag_error(writer, mpack_error_bug);
+        }
+
+        // Free any remaining builder pages
+        mpack_builder_page_t* page = builder->pages;
+        #if MPACK_BUILDER_INTERNAL_STORAGE
+        mpack_assert(page == (mpack_builder_page_t*)builder->internal);
+        page = page->next;
+        #endif
+        while (page != NULL) {
+            mpack_builder_page_t* next = page->next;
+            MPACK_FREE(page);
+            page = next;
+        }
+
+        // Restore the stashed pointers. The teardown function may need to free
+        // them (e.g. mpack_growable_writer_teardown().)
+        writer->buffer = builder->stash_buffer;
+        writer->position = builder->stash_position;
+        writer->end = builder->stash_end;
+
+        // Note: It's not necessary to clean up the current_build or other
+        // pointers at this point because we're guaranteed to be in an error
+        // state already so a user error callback can't longjmp out. This
+        // destroy function will complete no matter what so it doesn't matter
+        // what junk is left in the writer.
+    }
+    #endif
+
     // flush any outstanding data
     if (mpack_writer_error(writer) == mpack_ok && mpack_writer_buffer_used(writer) != 0 && writer->flush != NULL) {
         writer->flush(writer, writer->buffer, mpack_writer_buffer_used(writer));
@@ -2022,7 +2062,7 @@
     #endif
 
     if (nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
-        mpack_break("timestamp nanoseconds out of bounds: %u", nanoseconds);
+        mpack_break("timestamp nanoseconds out of bounds: %" PRIu32 , nanoseconds);
         mpack_writer_flag_error(writer, mpack_error_bug);
         return;
     }
@@ -2165,7 +2205,7 @@
  */
 
 void mpack_write_str(mpack_writer_t* writer, const char* data, uint32_t count) {
-    mpack_assert(data != NULL, "data for string of length %i is NULL", (int)count);
+    mpack_assert(count == 0 || data != NULL, "data for string of length %i is NULL", (int)count);
 
     #if MPACK_OPTIMIZE_FOR_SIZE
     mpack_writer_track_element(writer);
@@ -2220,7 +2260,7 @@
 }
 
 void mpack_write_bin(mpack_writer_t* writer, const char* data, uint32_t count) {
-    mpack_assert(data != NULL, "data pointer for bin of %i bytes is NULL", (int)count);
+    mpack_assert(count == 0 || data != NULL, "data pointer for bin of %i bytes is NULL", (int)count);
     mpack_start_bin(writer, count);
     mpack_write_bytes(writer, data, count);
     mpack_finish_bin(writer);
@@ -2228,7 +2268,7 @@
 
 #if MPACK_EXTENSIONS
 void mpack_write_ext(mpack_writer_t* writer, int8_t exttype, const char* data, uint32_t count) {
-    mpack_assert(data != NULL, "data pointer for ext of type %i and %i bytes is NULL", exttype, (int)count);
+    mpack_assert(count == 0 || data != NULL, "data pointer for ext of type %i and %i bytes is NULL", exttype, (int)count);
     mpack_start_ext(writer, exttype, count);
     mpack_write_bytes(writer, data, count);
     mpack_finish_ext(writer);
@@ -2236,7 +2276,7 @@
 #endif
 
 void mpack_write_bytes(mpack_writer_t* writer, const char* data, size_t count) {
-    mpack_assert(data != NULL, "data pointer for %i bytes is NULL", (int)count);
+    mpack_assert(count == 0 || data != NULL, "data pointer for %i bytes is NULL", (int)count);
     mpack_writer_track_bytes(writer, count);
     mpack_write_native(writer, data, count);
 }
@@ -2257,7 +2297,7 @@
 }
 
 void mpack_write_utf8(mpack_writer_t* writer, const char* str, uint32_t length) {
-    mpack_assert(str != NULL, "data for string of length %i is NULL", (int)length);
+    mpack_assert(length == 0 || str != NULL, "data for string of length %i is NULL", (int)length);
     if (!mpack_utf8_check(str, length)) {
         mpack_writer_flag_error(writer, mpack_error_invalid);
         return;
@@ -2557,6 +2597,16 @@
 static void mpack_builder_resolve(mpack_writer_t* writer) {
     mpack_builder_t* builder = &writer->builder;
 
+    // We should not have gotten here if we are in an error state. If an error
+    // occurs with an open builder, the writer will free the open builder pages
+    // when destroyed.
+    mpack_assert(mpack_writer_error(writer) == mpack_ok, "can't resolve in error state!");
+
+    // We don't want the user to longjmp out of any I/O errors while we are
+    // walking the page list, so defer error callbacks to after we're done.
+    mpack_writer_error_t error_fn = writer->error_fn;
+    writer->error_fn = NULL;
+
     // The starting page is the internal storage (if we have it), otherwise
     // it's the first page in the array
     mpack_builder_page_t* page =
@@ -2589,13 +2639,13 @@
 
     // Walk the list of builds, writing everything out in the buffer. Note that
     // we don't check for errors anywhere. The lower-level write functions will
-    // all check for errors. We need to walk all pages anyway to free them, so
-    // there's not much point in optimizing an error path at the expense of the
-    // normal path.
+    // all check for errors and do nothing after an error occurs. We need to
+    // walk all pages anyway to free them, so there's not much point in
+    // optimizing an error path at the expense of the normal path.
     while (true) {
 
         // write out the container tag
-        mpack_log("writing out an %s with count %u followed by %zi bytes\n",
+        mpack_log("writing out an %s with count %" PRIu32 " followed by %zi bytes\n",
                 mpack_type_to_string(build->type), build->count, build->bytes);
         switch (build->type) {
             case mpack_type_map:
@@ -2645,7 +2695,7 @@
 
         // now see if we can find another build.
         offset = mpack_builder_align_build(offset);
-        if (offset + sizeof(mpack_build_t) >= mpack_builder_page_size(writer, page)) {
+        if (offset + sizeof(mpack_build_t) > mpack_builder_page_size(writer, page)) {
             mpack_log("not enough room in this page for another build\n");
             mpack_builder_page_t* next_page = page->next;
             mpack_builder_free_page(writer, page);
@@ -2671,13 +2721,18 @@
     }
 
     mpack_log("done resolve.\n");
+
+    // We can now restore the error handler and call it if an error occurred.
+    writer->error_fn = error_fn;
+    if (writer->error_fn && mpack_writer_error(writer) != mpack_ok)
+        writer->error_fn(writer, writer->error);
 }
 
 static void mpack_builder_complete(mpack_writer_t* writer, mpack_type_t type) {
+    mpack_writer_track_pop_builder(writer, type);
     if (mpack_writer_error(writer) != mpack_ok)
         return;
 
-    mpack_writer_track_pop_builder(writer, type);
     mpack_builder_t* builder = &writer->builder;
     mpack_assert(builder->current_build != NULL, "no build in progress!");
     mpack_assert(builder->latest_build != NULL, "missing latest build!");
@@ -3120,7 +3175,7 @@
     // check if we have enough in the buffer already
     size_t left = (size_t)(reader->end - reader->data);
     if (left >= count) {
-        mpack_log("skipping %u bytes still in buffer\n", (uint32_t)count);
+        mpack_log("skipping %" PRIu32 " bytes still in buffer\n", (uint32_t)count);
         reader->data += count;
         return;
     }
@@ -5013,7 +5068,7 @@
             return false;
         }
 
-        mpack_log("read %u more bytes\n", (uint32_t)read);
+        mpack_log("read %" PRIu32 " more bytes\n", (uint32_t)read);
         tree->data_length += read;
         tree->parser.possible_nodes_left += read;
     } while (tree->parser.possible_nodes_left < bytes);
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/any.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/any.h
new file mode 100644
index 0000000..92ea2bb
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/any.h
@@ -0,0 +1,157 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_ANY_H__
+#define GOOGLE_PROTOBUF_ANY_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/message_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+class FieldDescriptor;
+class Message;
+
+namespace internal {
+
+extern const char kAnyFullTypeName[];          // "google.protobuf.Any".
+extern const char kTypeGoogleApisComPrefix[];  // "type.googleapis.com/".
+extern const char kTypeGoogleProdComPrefix[];  // "type.googleprod.com/".
+
+std::string GetTypeUrl(StringPiece message_name,
+                       StringPiece type_url_prefix);
+
+// Helper class used to implement google::protobuf::Any.
+class PROTOBUF_EXPORT AnyMetadata {
+  typedef ArenaStringPtr UrlType;
+  typedef ArenaStringPtr ValueType;
+ public:
+  // AnyMetadata does not take ownership of "type_url" and "value".
+  constexpr AnyMetadata(UrlType* type_url, ValueType* value)
+      : type_url_(type_url), value_(value) {}
+
+  // Packs a message using the default type URL prefix: "type.googleapis.com".
+  // The resulted type URL will be "type.googleapis.com/<message_full_name>".
+  // Returns false if serializing the message failed.
+  template <typename T>
+  bool PackFrom(Arena* arena, const T& message) {
+    return InternalPackFrom(arena, message, kTypeGoogleApisComPrefix,
+                            T::FullMessageName());
+  }
+
+  bool PackFrom(Arena* arena, const Message& message);
+
+  // Packs a message using the given type URL prefix. The type URL will be
+  // constructed by concatenating the message type's full name to the prefix
+  // with an optional "/" separator if the prefix doesn't already end with "/".
+  // For example, both PackFrom(message, "type.googleapis.com") and
+  // PackFrom(message, "type.googleapis.com/") yield the same result type
+  // URL: "type.googleapis.com/<message_full_name>".
+  // Returns false if serializing the message failed.
+  template <typename T>
+  bool PackFrom(Arena* arena, const T& message,
+                StringPiece type_url_prefix) {
+    return InternalPackFrom(arena, message, type_url_prefix,
+                            T::FullMessageName());
+  }
+
+  bool PackFrom(Arena* arena, const Message& message,
+                StringPiece type_url_prefix);
+
+  // Unpacks the payload into the given message. Returns false if the message's
+  // type doesn't match the type specified in the type URL (i.e., the full
+  // name after the last "/" of the type URL doesn't match the message's actual
+  // full name) or parsing the payload has failed.
+  template <typename T>
+  bool UnpackTo(T* message) const {
+    return InternalUnpackTo(T::FullMessageName(), message);
+  }
+
+  bool UnpackTo(Message* message) const;
+
+  // Checks whether the type specified in the type URL matches the given type.
+  // A type is considered matching if its full name matches the full name after
+  // the last "/" in the type URL.
+  template <typename T>
+  bool Is() const {
+    return InternalIs(T::FullMessageName());
+  }
+
+ private:
+  bool InternalPackFrom(Arena* arena, const MessageLite& message,
+                        StringPiece type_url_prefix,
+                        StringPiece type_name);
+  bool InternalUnpackTo(StringPiece type_name,
+                        MessageLite* message) const;
+  bool InternalIs(StringPiece type_name) const;
+
+  UrlType* type_url_;
+  ValueType* value_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(AnyMetadata);
+};
+
+// Get the proto type name from Any::type_url value. For example, passing
+// "type.googleapis.com/rpc.QueryOrigin" will return "rpc.QueryOrigin" in
+// *full_type_name. Returns false if the type_url does not have a "/"
+// in the type url separating the full type name.
+//
+// NOTE: this function is available publicly as a static method on the
+// generated message type: google::protobuf::Any::ParseAnyTypeUrl()
+bool ParseAnyTypeUrl(StringPiece type_url, std::string* full_type_name);
+
+// Get the proto type name and prefix from Any::type_url value. For example,
+// passing "type.googleapis.com/rpc.QueryOrigin" will return
+// "type.googleapis.com/" in *url_prefix and "rpc.QueryOrigin" in
+// *full_type_name. Returns false if the type_url does not have a "/" in the
+// type url separating the full type name.
+bool ParseAnyTypeUrl(StringPiece type_url, std::string* url_prefix,
+                     std::string* full_type_name);
+
+// See if message is of type google.protobuf.Any, if so, return the descriptors
+// for "type_url" and "value" fields.
+bool GetAnyFieldDescriptors(const Message& message,
+                            const FieldDescriptor** type_url_field,
+                            const FieldDescriptor** value_field);
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_ANY_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/any.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/any.pb.h
new file mode 100644
index 0000000..2c06052
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/any.pb.h
@@ -0,0 +1,384 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/any.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fany_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fany_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/unknown_field_set.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fany_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2fany_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2fany_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class Any;
+struct AnyDefaultTypeInternal;
+PROTOBUF_EXPORT extern AnyDefaultTypeInternal _Any_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Any* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Any>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class PROTOBUF_EXPORT Any final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Any) */ {
+ public:
+  inline Any() : Any(nullptr) {}
+  ~Any() override;
+  explicit PROTOBUF_CONSTEXPR Any(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Any(const Any& from);
+  Any(Any&& from) noexcept
+    : Any() {
+    *this = ::std::move(from);
+  }
+
+  inline Any& operator=(const Any& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Any& operator=(Any&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Any& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Any* internal_default_instance() {
+    return reinterpret_cast<const Any*>(
+               &_Any_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  // implements Any -----------------------------------------------
+
+  bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message& message) {
+    GOOGLE_DCHECK_NE(&message, this);
+    return _impl_._any_metadata_.PackFrom(GetArena(), message);
+  }
+  bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message& message,
+                ::PROTOBUF_NAMESPACE_ID::ConstStringParam type_url_prefix) {
+    GOOGLE_DCHECK_NE(&message, this);
+    return _impl_._any_metadata_.PackFrom(GetArena(), message, type_url_prefix);
+  }
+  bool UnpackTo(::PROTOBUF_NAMESPACE_ID::Message* message) const {
+    return _impl_._any_metadata_.UnpackTo(message);
+  }
+  static bool GetAnyFieldDescriptors(
+      const ::PROTOBUF_NAMESPACE_ID::Message& message,
+      const ::PROTOBUF_NAMESPACE_ID::FieldDescriptor** type_url_field,
+      const ::PROTOBUF_NAMESPACE_ID::FieldDescriptor** value_field);
+  template <typename T, class = typename std::enable_if<!std::is_convertible<T, const ::PROTOBUF_NAMESPACE_ID::Message&>::value>::type>
+  bool PackFrom(const T& message) {
+    return _impl_._any_metadata_.PackFrom<T>(GetArena(), message);
+  }
+  template <typename T, class = typename std::enable_if<!std::is_convertible<T, const ::PROTOBUF_NAMESPACE_ID::Message&>::value>::type>
+  bool PackFrom(const T& message,
+                ::PROTOBUF_NAMESPACE_ID::ConstStringParam type_url_prefix) {
+    return _impl_._any_metadata_.PackFrom<T>(GetArena(), message, type_url_prefix);}
+  template <typename T, class = typename std::enable_if<!std::is_convertible<T, const ::PROTOBUF_NAMESPACE_ID::Message&>::value>::type>
+  bool UnpackTo(T* message) const {
+    return _impl_._any_metadata_.UnpackTo<T>(message);
+  }
+  template<typename T> bool Is() const {
+    return _impl_._any_metadata_.Is<T>();
+  }
+  static bool ParseAnyTypeUrl(::PROTOBUF_NAMESPACE_ID::ConstStringParam type_url,
+                              std::string* full_type_name);
+  friend void swap(Any& a, Any& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Any* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Any* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Any* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Any>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Any& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Any& from) {
+    Any::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Any* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Any";
+  }
+  protected:
+  explicit Any(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kTypeUrlFieldNumber = 1,
+    kValueFieldNumber = 2,
+  };
+  // string type_url = 1;
+  void clear_type_url();
+  const std::string& type_url() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_type_url(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_type_url();
+  PROTOBUF_NODISCARD std::string* release_type_url();
+  void set_allocated_type_url(std::string* type_url);
+  private:
+  const std::string& _internal_type_url() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_type_url(const std::string& value);
+  std::string* _internal_mutable_type_url();
+  public:
+
+  // bytes value = 2;
+  void clear_value();
+  const std::string& value() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_value(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_value();
+  PROTOBUF_NODISCARD std::string* release_value();
+  void set_allocated_value(std::string* value);
+  private:
+  const std::string& _internal_value() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_value(const std::string& value);
+  std::string* _internal_mutable_value();
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Any)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr type_url_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata _any_metadata_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fany_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// Any
+
+// string type_url = 1;
+inline void Any::clear_type_url() {
+  _impl_.type_url_.ClearToEmpty();
+}
+inline const std::string& Any::type_url() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Any.type_url)
+  return _internal_type_url();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Any::set_type_url(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.type_url_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Any.type_url)
+}
+inline std::string* Any::mutable_type_url() {
+  std::string* _s = _internal_mutable_type_url();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Any.type_url)
+  return _s;
+}
+inline const std::string& Any::_internal_type_url() const {
+  return _impl_.type_url_.Get();
+}
+inline void Any::_internal_set_type_url(const std::string& value) {
+  
+  _impl_.type_url_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Any::_internal_mutable_type_url() {
+  
+  return _impl_.type_url_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Any::release_type_url() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Any.type_url)
+  return _impl_.type_url_.Release();
+}
+inline void Any::set_allocated_type_url(std::string* type_url) {
+  if (type_url != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.type_url_.SetAllocated(type_url, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.type_url_.IsDefault()) {
+    _impl_.type_url_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Any.type_url)
+}
+
+// bytes value = 2;
+inline void Any::clear_value() {
+  _impl_.value_.ClearToEmpty();
+}
+inline const std::string& Any::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Any.value)
+  return _internal_value();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Any::set_value(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.value_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Any.value)
+}
+inline std::string* Any::mutable_value() {
+  std::string* _s = _internal_mutable_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Any.value)
+  return _s;
+}
+inline const std::string& Any::_internal_value() const {
+  return _impl_.value_.Get();
+}
+inline void Any::_internal_set_value(const std::string& value) {
+  
+  _impl_.value_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Any::_internal_mutable_value() {
+  
+  return _impl_.value_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Any::release_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Any.value)
+  return _impl_.value_.Release();
+}
+inline void Any::set_allocated_value(std::string* value) {
+  if (value != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.value_.SetAllocated(value, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.value_.IsDefault()) {
+    _impl_.value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Any.value)
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fany_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/api.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/api.pb.h
new file mode 100644
index 0000000..2c45489
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/api.pb.h
@@ -0,0 +1,1437 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/api.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fapi_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fapi_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/source_context.pb.h>
+#include <google/protobuf/type.pb.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fapi_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2fapi_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2fapi_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class Api;
+struct ApiDefaultTypeInternal;
+PROTOBUF_EXPORT extern ApiDefaultTypeInternal _Api_default_instance_;
+class Method;
+struct MethodDefaultTypeInternal;
+PROTOBUF_EXPORT extern MethodDefaultTypeInternal _Method_default_instance_;
+class Mixin;
+struct MixinDefaultTypeInternal;
+PROTOBUF_EXPORT extern MixinDefaultTypeInternal _Mixin_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Api* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Api>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Method* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Method>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Mixin* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Mixin>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class PROTOBUF_EXPORT Api final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Api) */ {
+ public:
+  inline Api() : Api(nullptr) {}
+  ~Api() override;
+  explicit PROTOBUF_CONSTEXPR Api(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Api(const Api& from);
+  Api(Api&& from) noexcept
+    : Api() {
+    *this = ::std::move(from);
+  }
+
+  inline Api& operator=(const Api& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Api& operator=(Api&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Api& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Api* internal_default_instance() {
+    return reinterpret_cast<const Api*>(
+               &_Api_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(Api& a, Api& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Api* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Api* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Api* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Api>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Api& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Api& from) {
+    Api::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Api* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Api";
+  }
+  protected:
+  explicit Api(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kMethodsFieldNumber = 2,
+    kOptionsFieldNumber = 3,
+    kMixinsFieldNumber = 6,
+    kNameFieldNumber = 1,
+    kVersionFieldNumber = 4,
+    kSourceContextFieldNumber = 5,
+    kSyntaxFieldNumber = 7,
+  };
+  // repeated .google.protobuf.Method methods = 2;
+  int methods_size() const;
+  private:
+  int _internal_methods_size() const;
+  public:
+  void clear_methods();
+  ::PROTOBUF_NAMESPACE_ID::Method* mutable_methods(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Method >*
+      mutable_methods();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Method& _internal_methods(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Method* _internal_add_methods();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Method& methods(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Method* add_methods();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Method >&
+      methods() const;
+
+  // repeated .google.protobuf.Option options = 3;
+  int options_size() const;
+  private:
+  int _internal_options_size() const;
+  public:
+  void clear_options();
+  ::PROTOBUF_NAMESPACE_ID::Option* mutable_options(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+      mutable_options();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Option& _internal_options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* _internal_add_options();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Option& options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* add_options();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+      options() const;
+
+  // repeated .google.protobuf.Mixin mixins = 6;
+  int mixins_size() const;
+  private:
+  int _internal_mixins_size() const;
+  public:
+  void clear_mixins();
+  ::PROTOBUF_NAMESPACE_ID::Mixin* mutable_mixins(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Mixin >*
+      mutable_mixins();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Mixin& _internal_mixins(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Mixin* _internal_add_mixins();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Mixin& mixins(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Mixin* add_mixins();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Mixin >&
+      mixins() const;
+
+  // string name = 1;
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // string version = 4;
+  void clear_version();
+  const std::string& version() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_version(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_version();
+  PROTOBUF_NODISCARD std::string* release_version();
+  void set_allocated_version(std::string* version);
+  private:
+  const std::string& _internal_version() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_version(const std::string& value);
+  std::string* _internal_mutable_version();
+  public:
+
+  // .google.protobuf.SourceContext source_context = 5;
+  bool has_source_context() const;
+  private:
+  bool _internal_has_source_context() const;
+  public:
+  void clear_source_context();
+  const ::PROTOBUF_NAMESPACE_ID::SourceContext& source_context() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::SourceContext* release_source_context();
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* mutable_source_context();
+  void set_allocated_source_context(::PROTOBUF_NAMESPACE_ID::SourceContext* source_context);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::SourceContext& _internal_source_context() const;
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* _internal_mutable_source_context();
+  public:
+  void unsafe_arena_set_allocated_source_context(
+      ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context);
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* unsafe_arena_release_source_context();
+
+  // .google.protobuf.Syntax syntax = 7;
+  void clear_syntax();
+  ::PROTOBUF_NAMESPACE_ID::Syntax syntax() const;
+  void set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::Syntax _internal_syntax() const;
+  void _internal_set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Api)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Method > methods_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option > options_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Mixin > mixins_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr version_;
+    ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context_;
+    int syntax_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fapi_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT Method final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Method) */ {
+ public:
+  inline Method() : Method(nullptr) {}
+  ~Method() override;
+  explicit PROTOBUF_CONSTEXPR Method(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Method(const Method& from);
+  Method(Method&& from) noexcept
+    : Method() {
+    *this = ::std::move(from);
+  }
+
+  inline Method& operator=(const Method& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Method& operator=(Method&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Method& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Method* internal_default_instance() {
+    return reinterpret_cast<const Method*>(
+               &_Method_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    1;
+
+  friend void swap(Method& a, Method& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Method* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Method* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Method* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Method>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Method& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Method& from) {
+    Method::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Method* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Method";
+  }
+  protected:
+  explicit Method(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kOptionsFieldNumber = 6,
+    kNameFieldNumber = 1,
+    kRequestTypeUrlFieldNumber = 2,
+    kResponseTypeUrlFieldNumber = 4,
+    kRequestStreamingFieldNumber = 3,
+    kResponseStreamingFieldNumber = 5,
+    kSyntaxFieldNumber = 7,
+  };
+  // repeated .google.protobuf.Option options = 6;
+  int options_size() const;
+  private:
+  int _internal_options_size() const;
+  public:
+  void clear_options();
+  ::PROTOBUF_NAMESPACE_ID::Option* mutable_options(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+      mutable_options();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Option& _internal_options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* _internal_add_options();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Option& options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* add_options();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+      options() const;
+
+  // string name = 1;
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // string request_type_url = 2;
+  void clear_request_type_url();
+  const std::string& request_type_url() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_request_type_url(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_request_type_url();
+  PROTOBUF_NODISCARD std::string* release_request_type_url();
+  void set_allocated_request_type_url(std::string* request_type_url);
+  private:
+  const std::string& _internal_request_type_url() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_request_type_url(const std::string& value);
+  std::string* _internal_mutable_request_type_url();
+  public:
+
+  // string response_type_url = 4;
+  void clear_response_type_url();
+  const std::string& response_type_url() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_response_type_url(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_response_type_url();
+  PROTOBUF_NODISCARD std::string* release_response_type_url();
+  void set_allocated_response_type_url(std::string* response_type_url);
+  private:
+  const std::string& _internal_response_type_url() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_response_type_url(const std::string& value);
+  std::string* _internal_mutable_response_type_url();
+  public:
+
+  // bool request_streaming = 3;
+  void clear_request_streaming();
+  bool request_streaming() const;
+  void set_request_streaming(bool value);
+  private:
+  bool _internal_request_streaming() const;
+  void _internal_set_request_streaming(bool value);
+  public:
+
+  // bool response_streaming = 5;
+  void clear_response_streaming();
+  bool response_streaming() const;
+  void set_response_streaming(bool value);
+  private:
+  bool _internal_response_streaming() const;
+  void _internal_set_response_streaming(bool value);
+  public:
+
+  // .google.protobuf.Syntax syntax = 7;
+  void clear_syntax();
+  ::PROTOBUF_NAMESPACE_ID::Syntax syntax() const;
+  void set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::Syntax _internal_syntax() const;
+  void _internal_set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Method)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option > options_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr request_type_url_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr response_type_url_;
+    bool request_streaming_;
+    bool response_streaming_;
+    int syntax_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fapi_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT Mixin final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Mixin) */ {
+ public:
+  inline Mixin() : Mixin(nullptr) {}
+  ~Mixin() override;
+  explicit PROTOBUF_CONSTEXPR Mixin(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Mixin(const Mixin& from);
+  Mixin(Mixin&& from) noexcept
+    : Mixin() {
+    *this = ::std::move(from);
+  }
+
+  inline Mixin& operator=(const Mixin& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Mixin& operator=(Mixin&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Mixin& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Mixin* internal_default_instance() {
+    return reinterpret_cast<const Mixin*>(
+               &_Mixin_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    2;
+
+  friend void swap(Mixin& a, Mixin& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Mixin* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Mixin* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Mixin* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Mixin>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Mixin& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Mixin& from) {
+    Mixin::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Mixin* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Mixin";
+  }
+  protected:
+  explicit Mixin(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNameFieldNumber = 1,
+    kRootFieldNumber = 2,
+  };
+  // string name = 1;
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // string root = 2;
+  void clear_root();
+  const std::string& root() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_root(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_root();
+  PROTOBUF_NODISCARD std::string* release_root();
+  void set_allocated_root(std::string* root);
+  private:
+  const std::string& _internal_root() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_root(const std::string& value);
+  std::string* _internal_mutable_root();
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Mixin)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr root_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fapi_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// Api
+
+// string name = 1;
+inline void Api::clear_name() {
+  _impl_.name_.ClearToEmpty();
+}
+inline const std::string& Api::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Api.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Api::set_name(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Api.name)
+}
+inline std::string* Api::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Api.name)
+  return _s;
+}
+inline const std::string& Api::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void Api::_internal_set_name(const std::string& value) {
+  
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Api::_internal_mutable_name() {
+  
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Api::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Api.name)
+  return _impl_.name_.Release();
+}
+inline void Api::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Api.name)
+}
+
+// repeated .google.protobuf.Method methods = 2;
+inline int Api::_internal_methods_size() const {
+  return _impl_.methods_.size();
+}
+inline int Api::methods_size() const {
+  return _internal_methods_size();
+}
+inline void Api::clear_methods() {
+  _impl_.methods_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Method* Api::mutable_methods(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Api.methods)
+  return _impl_.methods_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Method >*
+Api::mutable_methods() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.Api.methods)
+  return &_impl_.methods_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Method& Api::_internal_methods(int index) const {
+  return _impl_.methods_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Method& Api::methods(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Api.methods)
+  return _internal_methods(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Method* Api::_internal_add_methods() {
+  return _impl_.methods_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Method* Api::add_methods() {
+  ::PROTOBUF_NAMESPACE_ID::Method* _add = _internal_add_methods();
+  // @@protoc_insertion_point(field_add:google.protobuf.Api.methods)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Method >&
+Api::methods() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.Api.methods)
+  return _impl_.methods_;
+}
+
+// repeated .google.protobuf.Option options = 3;
+inline int Api::_internal_options_size() const {
+  return _impl_.options_.size();
+}
+inline int Api::options_size() const {
+  return _internal_options_size();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Api::mutable_options(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Api.options)
+  return _impl_.options_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+Api::mutable_options() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.Api.options)
+  return &_impl_.options_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& Api::_internal_options(int index) const {
+  return _impl_.options_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& Api::options(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Api.options)
+  return _internal_options(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Api::_internal_add_options() {
+  return _impl_.options_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Api::add_options() {
+  ::PROTOBUF_NAMESPACE_ID::Option* _add = _internal_add_options();
+  // @@protoc_insertion_point(field_add:google.protobuf.Api.options)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+Api::options() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.Api.options)
+  return _impl_.options_;
+}
+
+// string version = 4;
+inline void Api::clear_version() {
+  _impl_.version_.ClearToEmpty();
+}
+inline const std::string& Api::version() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Api.version)
+  return _internal_version();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Api::set_version(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.version_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Api.version)
+}
+inline std::string* Api::mutable_version() {
+  std::string* _s = _internal_mutable_version();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Api.version)
+  return _s;
+}
+inline const std::string& Api::_internal_version() const {
+  return _impl_.version_.Get();
+}
+inline void Api::_internal_set_version(const std::string& value) {
+  
+  _impl_.version_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Api::_internal_mutable_version() {
+  
+  return _impl_.version_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Api::release_version() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Api.version)
+  return _impl_.version_.Release();
+}
+inline void Api::set_allocated_version(std::string* version) {
+  if (version != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.version_.SetAllocated(version, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.version_.IsDefault()) {
+    _impl_.version_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Api.version)
+}
+
+// .google.protobuf.SourceContext source_context = 5;
+inline bool Api::_internal_has_source_context() const {
+  return this != internal_default_instance() && _impl_.source_context_ != nullptr;
+}
+inline bool Api::has_source_context() const {
+  return _internal_has_source_context();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::SourceContext& Api::_internal_source_context() const {
+  const ::PROTOBUF_NAMESPACE_ID::SourceContext* p = _impl_.source_context_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::SourceContext&>(
+      ::PROTOBUF_NAMESPACE_ID::_SourceContext_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::SourceContext& Api::source_context() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Api.source_context)
+  return _internal_source_context();
+}
+inline void Api::unsafe_arena_set_allocated_source_context(
+    ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.source_context_);
+  }
+  _impl_.source_context_ = source_context;
+  if (source_context) {
+    
+  } else {
+    
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.Api.source_context)
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Api::release_source_context() {
+  
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* temp = _impl_.source_context_;
+  _impl_.source_context_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Api::unsafe_arena_release_source_context() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Api.source_context)
+  
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* temp = _impl_.source_context_;
+  _impl_.source_context_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Api::_internal_mutable_source_context() {
+  
+  if (_impl_.source_context_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::SourceContext>(GetArenaForAllocation());
+    _impl_.source_context_ = p;
+  }
+  return _impl_.source_context_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Api::mutable_source_context() {
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* _msg = _internal_mutable_source_context();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Api.source_context)
+  return _msg;
+}
+inline void Api::set_allocated_source_context(::PROTOBUF_NAMESPACE_ID::SourceContext* source_context) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.source_context_);
+  }
+  if (source_context) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(
+                reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(source_context));
+    if (message_arena != submessage_arena) {
+      source_context = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, source_context, submessage_arena);
+    }
+    
+  } else {
+    
+  }
+  _impl_.source_context_ = source_context;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Api.source_context)
+}
+
+// repeated .google.protobuf.Mixin mixins = 6;
+inline int Api::_internal_mixins_size() const {
+  return _impl_.mixins_.size();
+}
+inline int Api::mixins_size() const {
+  return _internal_mixins_size();
+}
+inline void Api::clear_mixins() {
+  _impl_.mixins_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Mixin* Api::mutable_mixins(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Api.mixins)
+  return _impl_.mixins_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Mixin >*
+Api::mutable_mixins() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.Api.mixins)
+  return &_impl_.mixins_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Mixin& Api::_internal_mixins(int index) const {
+  return _impl_.mixins_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Mixin& Api::mixins(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Api.mixins)
+  return _internal_mixins(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Mixin* Api::_internal_add_mixins() {
+  return _impl_.mixins_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Mixin* Api::add_mixins() {
+  ::PROTOBUF_NAMESPACE_ID::Mixin* _add = _internal_add_mixins();
+  // @@protoc_insertion_point(field_add:google.protobuf.Api.mixins)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Mixin >&
+Api::mixins() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.Api.mixins)
+  return _impl_.mixins_;
+}
+
+// .google.protobuf.Syntax syntax = 7;
+inline void Api::clear_syntax() {
+  _impl_.syntax_ = 0;
+}
+inline ::PROTOBUF_NAMESPACE_ID::Syntax Api::_internal_syntax() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::Syntax >(_impl_.syntax_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Syntax Api::syntax() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Api.syntax)
+  return _internal_syntax();
+}
+inline void Api::_internal_set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value) {
+  
+  _impl_.syntax_ = value;
+}
+inline void Api::set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value) {
+  _internal_set_syntax(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Api.syntax)
+}
+
+// -------------------------------------------------------------------
+
+// Method
+
+// string name = 1;
+inline void Method::clear_name() {
+  _impl_.name_.ClearToEmpty();
+}
+inline const std::string& Method::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Method.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Method::set_name(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Method.name)
+}
+inline std::string* Method::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Method.name)
+  return _s;
+}
+inline const std::string& Method::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void Method::_internal_set_name(const std::string& value) {
+  
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Method::_internal_mutable_name() {
+  
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Method::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Method.name)
+  return _impl_.name_.Release();
+}
+inline void Method::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Method.name)
+}
+
+// string request_type_url = 2;
+inline void Method::clear_request_type_url() {
+  _impl_.request_type_url_.ClearToEmpty();
+}
+inline const std::string& Method::request_type_url() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Method.request_type_url)
+  return _internal_request_type_url();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Method::set_request_type_url(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.request_type_url_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Method.request_type_url)
+}
+inline std::string* Method::mutable_request_type_url() {
+  std::string* _s = _internal_mutable_request_type_url();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Method.request_type_url)
+  return _s;
+}
+inline const std::string& Method::_internal_request_type_url() const {
+  return _impl_.request_type_url_.Get();
+}
+inline void Method::_internal_set_request_type_url(const std::string& value) {
+  
+  _impl_.request_type_url_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Method::_internal_mutable_request_type_url() {
+  
+  return _impl_.request_type_url_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Method::release_request_type_url() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Method.request_type_url)
+  return _impl_.request_type_url_.Release();
+}
+inline void Method::set_allocated_request_type_url(std::string* request_type_url) {
+  if (request_type_url != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.request_type_url_.SetAllocated(request_type_url, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.request_type_url_.IsDefault()) {
+    _impl_.request_type_url_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Method.request_type_url)
+}
+
+// bool request_streaming = 3;
+inline void Method::clear_request_streaming() {
+  _impl_.request_streaming_ = false;
+}
+inline bool Method::_internal_request_streaming() const {
+  return _impl_.request_streaming_;
+}
+inline bool Method::request_streaming() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Method.request_streaming)
+  return _internal_request_streaming();
+}
+inline void Method::_internal_set_request_streaming(bool value) {
+  
+  _impl_.request_streaming_ = value;
+}
+inline void Method::set_request_streaming(bool value) {
+  _internal_set_request_streaming(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Method.request_streaming)
+}
+
+// string response_type_url = 4;
+inline void Method::clear_response_type_url() {
+  _impl_.response_type_url_.ClearToEmpty();
+}
+inline const std::string& Method::response_type_url() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Method.response_type_url)
+  return _internal_response_type_url();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Method::set_response_type_url(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.response_type_url_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Method.response_type_url)
+}
+inline std::string* Method::mutable_response_type_url() {
+  std::string* _s = _internal_mutable_response_type_url();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Method.response_type_url)
+  return _s;
+}
+inline const std::string& Method::_internal_response_type_url() const {
+  return _impl_.response_type_url_.Get();
+}
+inline void Method::_internal_set_response_type_url(const std::string& value) {
+  
+  _impl_.response_type_url_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Method::_internal_mutable_response_type_url() {
+  
+  return _impl_.response_type_url_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Method::release_response_type_url() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Method.response_type_url)
+  return _impl_.response_type_url_.Release();
+}
+inline void Method::set_allocated_response_type_url(std::string* response_type_url) {
+  if (response_type_url != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.response_type_url_.SetAllocated(response_type_url, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.response_type_url_.IsDefault()) {
+    _impl_.response_type_url_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Method.response_type_url)
+}
+
+// bool response_streaming = 5;
+inline void Method::clear_response_streaming() {
+  _impl_.response_streaming_ = false;
+}
+inline bool Method::_internal_response_streaming() const {
+  return _impl_.response_streaming_;
+}
+inline bool Method::response_streaming() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Method.response_streaming)
+  return _internal_response_streaming();
+}
+inline void Method::_internal_set_response_streaming(bool value) {
+  
+  _impl_.response_streaming_ = value;
+}
+inline void Method::set_response_streaming(bool value) {
+  _internal_set_response_streaming(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Method.response_streaming)
+}
+
+// repeated .google.protobuf.Option options = 6;
+inline int Method::_internal_options_size() const {
+  return _impl_.options_.size();
+}
+inline int Method::options_size() const {
+  return _internal_options_size();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Method::mutable_options(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Method.options)
+  return _impl_.options_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+Method::mutable_options() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.Method.options)
+  return &_impl_.options_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& Method::_internal_options(int index) const {
+  return _impl_.options_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& Method::options(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Method.options)
+  return _internal_options(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Method::_internal_add_options() {
+  return _impl_.options_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Method::add_options() {
+  ::PROTOBUF_NAMESPACE_ID::Option* _add = _internal_add_options();
+  // @@protoc_insertion_point(field_add:google.protobuf.Method.options)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+Method::options() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.Method.options)
+  return _impl_.options_;
+}
+
+// .google.protobuf.Syntax syntax = 7;
+inline void Method::clear_syntax() {
+  _impl_.syntax_ = 0;
+}
+inline ::PROTOBUF_NAMESPACE_ID::Syntax Method::_internal_syntax() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::Syntax >(_impl_.syntax_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Syntax Method::syntax() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Method.syntax)
+  return _internal_syntax();
+}
+inline void Method::_internal_set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value) {
+  
+  _impl_.syntax_ = value;
+}
+inline void Method::set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value) {
+  _internal_set_syntax(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Method.syntax)
+}
+
+// -------------------------------------------------------------------
+
+// Mixin
+
+// string name = 1;
+inline void Mixin::clear_name() {
+  _impl_.name_.ClearToEmpty();
+}
+inline const std::string& Mixin::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Mixin.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Mixin::set_name(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Mixin.name)
+}
+inline std::string* Mixin::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Mixin.name)
+  return _s;
+}
+inline const std::string& Mixin::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void Mixin::_internal_set_name(const std::string& value) {
+  
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Mixin::_internal_mutable_name() {
+  
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Mixin::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Mixin.name)
+  return _impl_.name_.Release();
+}
+inline void Mixin::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Mixin.name)
+}
+
+// string root = 2;
+inline void Mixin::clear_root() {
+  _impl_.root_.ClearToEmpty();
+}
+inline const std::string& Mixin::root() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Mixin.root)
+  return _internal_root();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Mixin::set_root(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.root_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Mixin.root)
+}
+inline std::string* Mixin::mutable_root() {
+  std::string* _s = _internal_mutable_root();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Mixin.root)
+  return _s;
+}
+inline const std::string& Mixin::_internal_root() const {
+  return _impl_.root_.Get();
+}
+inline void Mixin::_internal_set_root(const std::string& value) {
+  
+  _impl_.root_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Mixin::_internal_mutable_root() {
+  
+  return _impl_.root_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Mixin::release_root() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Mixin.root)
+  return _impl_.root_.Release();
+}
+inline void Mixin::set_allocated_root(std::string* root) {
+  if (root != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.root_.SetAllocated(root, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.root_.IsDefault()) {
+    _impl_.root_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Mixin.root)
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fapi_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arena.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arena.h
new file mode 100644
index 0000000..3b5f16c
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arena.h
@@ -0,0 +1,851 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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 file defines an Arena allocator for better allocation performance.
+
+#ifndef GOOGLE_PROTOBUF_ARENA_H__
+#define GOOGLE_PROTOBUF_ARENA_H__
+
+
+#include <limits>
+#include <type_traits>
+#include <utility>
+#if defined(_MSC_VER) && !defined(_LIBCPP_STD_VER) && !_HAS_EXCEPTIONS
+// Work around bugs in MSVC <typeinfo> header when _HAS_EXCEPTIONS=0.
+#include <exception>
+#include <typeinfo>
+namespace std {
+using type_info = ::type_info;
+}
+#else
+#include <typeinfo>
+#endif
+
+#include <type_traits>
+#include <google/protobuf/arena_impl.h>
+#include <google/protobuf/port.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+
+struct ArenaOptions;  // defined below
+class Arena;    // defined below
+class Message;  // defined in message.h
+class MessageLite;
+template <typename Key, typename T>
+class Map;
+
+namespace arena_metrics {
+
+void EnableArenaMetrics(ArenaOptions* options);
+
+}  // namespace arena_metrics
+
+namespace TestUtil {
+class ReflectionTester;  // defined in test_util.h
+}  // namespace TestUtil
+
+namespace internal {
+
+struct ArenaTestPeer;        // defined in arena_test_util.h
+class InternalMetadata;      // defined in metadata_lite.h
+class LazyField;             // defined in lazy_field.h
+class EpsCopyInputStream;    // defined in parse_context.h
+class RepeatedPtrFieldBase;  // defined in repeated_ptr_field.h
+
+template <typename Type>
+class GenericTypeHandler;  // defined in repeated_field.h
+
+inline PROTOBUF_ALWAYS_INLINE
+void* AlignTo(void* ptr, size_t align) {
+  return reinterpret_cast<void*>(
+      (reinterpret_cast<uintptr_t>(ptr) + align - 1) & (~align + 1));
+}
+
+// Templated cleanup methods.
+template <typename T>
+void arena_destruct_object(void* object) {
+  reinterpret_cast<T*>(object)->~T();
+}
+
+template <bool destructor_skippable, typename T>
+struct ObjectDestructor {
+  constexpr static void (*destructor)(void*) = &arena_destruct_object<T>;
+};
+
+template <typename T>
+struct ObjectDestructor<true, T> {
+  constexpr static void (*destructor)(void*) = nullptr;
+};
+
+template <typename T>
+void arena_delete_object(void* object) {
+  delete reinterpret_cast<T*>(object);
+}
+}  // namespace internal
+
+// ArenaOptions provides optional additional parameters to arena construction
+// that control its block-allocation behavior.
+struct ArenaOptions {
+  // This defines the size of the first block requested from the system malloc.
+  // Subsequent block sizes will increase in a geometric series up to a maximum.
+  size_t start_block_size;
+
+  // This defines the maximum block size requested from system malloc (unless an
+  // individual arena allocation request occurs with a size larger than this
+  // maximum). Requested block sizes increase up to this value, then remain
+  // here.
+  size_t max_block_size;
+
+  // An initial block of memory for the arena to use, or NULL for none. If
+  // provided, the block must live at least as long as the arena itself. The
+  // creator of the Arena retains ownership of the block after the Arena is
+  // destroyed.
+  char* initial_block;
+
+  // The size of the initial block, if provided.
+  size_t initial_block_size;
+
+  // A function pointer to an alloc method that returns memory blocks of size
+  // requested. By default, it contains a ptr to the malloc function.
+  //
+  // NOTE: block_alloc and dealloc functions are expected to behave like
+  // malloc and free, including Asan poisoning.
+  void* (*block_alloc)(size_t);
+  // A function pointer to a dealloc method that takes ownership of the blocks
+  // from the arena. By default, it contains a ptr to a wrapper function that
+  // calls free.
+  void (*block_dealloc)(void*, size_t);
+
+  ArenaOptions()
+      : start_block_size(internal::AllocationPolicy::kDefaultStartBlockSize),
+        max_block_size(internal::AllocationPolicy::kDefaultMaxBlockSize),
+        initial_block(NULL),
+        initial_block_size(0),
+        block_alloc(nullptr),
+        block_dealloc(nullptr),
+        make_metrics_collector(nullptr) {}
+
+ private:
+  // If make_metrics_collector is not nullptr, it will be called at Arena init
+  // time. It may return a pointer to a collector instance that will be notified
+  // of interesting events related to the arena.
+  internal::ArenaMetricsCollector* (*make_metrics_collector)();
+
+  internal::ArenaMetricsCollector* MetricsCollector() const {
+    return make_metrics_collector ? (*make_metrics_collector)() : nullptr;
+  }
+
+  internal::AllocationPolicy AllocationPolicy() const {
+    internal::AllocationPolicy res;
+    res.start_block_size = start_block_size;
+    res.max_block_size = max_block_size;
+    res.block_alloc = block_alloc;
+    res.block_dealloc = block_dealloc;
+    res.metrics_collector = MetricsCollector();
+    return res;
+  }
+
+  friend void arena_metrics::EnableArenaMetrics(ArenaOptions*);
+
+  friend class Arena;
+  friend class ArenaOptionsTestFriend;
+};
+
+// Support for non-RTTI environments. (The metrics hooks API uses type
+// information.)
+#if PROTOBUF_RTTI
+#define RTTI_TYPE_ID(type) (&typeid(type))
+#else
+#define RTTI_TYPE_ID(type) (NULL)
+#endif
+
+// Arena allocator. Arena allocation replaces ordinary (heap-based) allocation
+// with new/delete, and improves performance by aggregating allocations into
+// larger blocks and freeing allocations all at once. Protocol messages are
+// allocated on an arena by using Arena::CreateMessage<T>(Arena*), below, and
+// are automatically freed when the arena is destroyed.
+//
+// This is a thread-safe implementation: multiple threads may allocate from the
+// arena concurrently. Destruction is not thread-safe and the destructing
+// thread must synchronize with users of the arena first.
+//
+// An arena provides two allocation interfaces: CreateMessage<T>, which works
+// for arena-enabled proto2 message types as well as other types that satisfy
+// the appropriate protocol (described below), and Create<T>, which works for
+// any arbitrary type T. CreateMessage<T> is better when the type T supports it,
+// because this interface (i) passes the arena pointer to the created object so
+// that its sub-objects and internal allocations can use the arena too, and (ii)
+// elides the object's destructor call when possible. Create<T> does not place
+// any special requirements on the type T, and will invoke the object's
+// destructor when the arena is destroyed.
+//
+// The arena message allocation protocol, required by
+// CreateMessage<T>(Arena* arena, Args&&... args), is as follows:
+//
+// - The type T must have (at least) two constructors: a constructor callable
+//   with `args` (without `arena`), called when a T is allocated on the heap;
+//   and a constructor callable with `Arena* arena, Args&&... args`, called when
+//   a T is allocated on an arena. If the second constructor is called with a
+//   NULL arena pointer, it must be equivalent to invoking the first
+//   (`args`-only) constructor.
+//
+// - The type T must have a particular type trait: a nested type
+//   |InternalArenaConstructable_|. This is usually a typedef to |void|. If no
+//   such type trait exists, then the instantiation CreateMessage<T> will fail
+//   to compile.
+//
+// - The type T *may* have the type trait |DestructorSkippable_|. If this type
+//   trait is present in the type, then its destructor will not be called if and
+//   only if it was passed a non-NULL arena pointer. If this type trait is not
+//   present on the type, then its destructor is always called when the
+//   containing arena is destroyed.
+//
+// This protocol is implemented by all arena-enabled proto2 message classes as
+// well as protobuf container types like RepeatedPtrField and Map. The protocol
+// is internal to protobuf and is not guaranteed to be stable. Non-proto types
+// should not rely on this protocol.
+class PROTOBUF_EXPORT PROTOBUF_ALIGNAS(8) Arena final {
+ public:
+  // Default constructor with sensible default options, tuned for average
+  // use-cases.
+  inline Arena() : impl_() {}
+
+  // Construct an arena with default options, except for the supplied
+  // initial block. It is more efficient to use this constructor
+  // instead of passing ArenaOptions if the only configuration needed
+  // by the caller is supplying an initial block.
+  inline Arena(char* initial_block, size_t initial_block_size)
+      : impl_(initial_block, initial_block_size) {}
+
+  // Arena constructor taking custom options. See ArenaOptions above for
+  // descriptions of the options available.
+  explicit Arena(const ArenaOptions& options)
+      : impl_(options.initial_block, options.initial_block_size,
+              options.AllocationPolicy()) {}
+
+  // Block overhead.  Use this as a guide for how much to over-allocate the
+  // initial block if you want an allocation of size N to fit inside it.
+  //
+  // WARNING: if you allocate multiple objects, it is difficult to guarantee
+  // that a series of allocations will fit in the initial block, especially if
+  // Arena changes its alignment guarantees in the future!
+  static const size_t kBlockOverhead =
+      internal::ThreadSafeArena::kBlockHeaderSize +
+      internal::ThreadSafeArena::kSerialArenaSize;
+
+  inline ~Arena() {}
+
+  // TODO(protobuf-team): Fix callers to use constructor and delete this method.
+  void Init(const ArenaOptions&) {}
+
+  // API to create proto2 message objects on the arena. If the arena passed in
+  // is NULL, then a heap allocated object is returned. Type T must be a message
+  // defined in a .proto file with cc_enable_arenas set to true, otherwise a
+  // compilation error will occur.
+  //
+  // RepeatedField and RepeatedPtrField may also be instantiated directly on an
+  // arena with this method.
+  //
+  // This function also accepts any type T that satisfies the arena message
+  // allocation protocol, documented above.
+  template <typename T, typename... Args>
+  PROTOBUF_ALWAYS_INLINE static T* CreateMessage(Arena* arena, Args&&... args) {
+    static_assert(
+        InternalHelper<T>::is_arena_constructable::value,
+        "CreateMessage can only construct types that are ArenaConstructable");
+    // We must delegate to CreateMaybeMessage() and NOT CreateMessageInternal()
+    // because protobuf generated classes specialize CreateMaybeMessage() and we
+    // need to use that specialization for code size reasons.
+    return Arena::CreateMaybeMessage<T>(arena, static_cast<Args&&>(args)...);
+  }
+
+  // API to create any objects on the arena. Note that only the object will
+  // be created on the arena; the underlying ptrs (in case of a proto2 message)
+  // will be still heap allocated. Proto messages should usually be allocated
+  // with CreateMessage<T>() instead.
+  //
+  // Note that even if T satisfies the arena message construction protocol
+  // (InternalArenaConstructable_ trait and optional DestructorSkippable_
+  // trait), as described above, this function does not follow the protocol;
+  // instead, it treats T as a black-box type, just as if it did not have these
+  // traits. Specifically, T's constructor arguments will always be only those
+  // passed to Create<T>() -- no additional arena pointer is implicitly added.
+  // Furthermore, the destructor will always be called at arena destruction time
+  // (unless the destructor is trivial). Hence, from T's point of view, it is as
+  // if the object were allocated on the heap (except that the underlying memory
+  // is obtained from the arena).
+  template <typename T, typename... Args>
+  PROTOBUF_NDEBUG_INLINE static T* Create(Arena* arena, Args&&... args) {
+    return CreateInternal<T>(arena, std::is_convertible<T*, MessageLite*>(),
+                             static_cast<Args&&>(args)...);
+  }
+
+  // Allocates memory with the specific size and alignment.
+  void* AllocateAligned(size_t size, size_t align = 8) {
+    if (align <= 8) {
+      return AllocateAlignedNoHook(internal::AlignUpTo8(size));
+    } else {
+      // We are wasting space by over allocating align - 8 bytes. Compared
+      // to a dedicated function that takes current alignment in consideration.
+      // Such a scheme would only waste (align - 8)/2 bytes on average, but
+      // requires a dedicated function in the outline arena allocation
+      // functions. Possibly re-evaluate tradeoffs later.
+      return internal::AlignTo(AllocateAlignedNoHook(size + align - 8), align);
+    }
+  }
+
+  // Create an array of object type T on the arena *without* invoking the
+  // constructor of T. If `arena` is null, then the return value should be freed
+  // with `delete[] x;` (or `::operator delete[](x);`).
+  // To ensure safe uses, this function checks at compile time
+  // (when compiled as C++11) that T is trivially default-constructible and
+  // trivially destructible.
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE static T* CreateArray(Arena* arena,
+                                               size_t num_elements) {
+    static_assert(std::is_trivial<T>::value,
+                  "CreateArray requires a trivially constructible type");
+    static_assert(std::is_trivially_destructible<T>::value,
+                  "CreateArray requires a trivially destructible type");
+    GOOGLE_CHECK_LE(num_elements, std::numeric_limits<size_t>::max() / sizeof(T))
+        << "Requested size is too large to fit into size_t.";
+    if (arena == NULL) {
+      return static_cast<T*>(::operator new[](num_elements * sizeof(T)));
+    } else {
+      return arena->CreateInternalRawArray<T>(num_elements);
+    }
+  }
+
+  // The following are routines are for monitoring. They will approximate the
+  // total sum allocated and used memory, but the exact value is an
+  // implementation deal. For instance allocated space depends on growth
+  // policies. Do not use these in unit tests.
+  // Returns the total space allocated by the arena, which is the sum of the
+  // sizes of the underlying blocks.
+  uint64_t SpaceAllocated() const { return impl_.SpaceAllocated(); }
+  // Returns the total space used by the arena. Similar to SpaceAllocated but
+  // does not include free space and block overhead. The total space returned
+  // may not include space used by other threads executing concurrently with
+  // the call to this method.
+  uint64_t SpaceUsed() const { return impl_.SpaceUsed(); }
+
+  // Frees all storage allocated by this arena after calling destructors
+  // registered with OwnDestructor() and freeing objects registered with Own().
+  // Any objects allocated on this arena are unusable after this call. It also
+  // returns the total space used by the arena which is the sums of the sizes
+  // of the allocated blocks. This method is not thread-safe.
+  uint64_t Reset() { return impl_.Reset(); }
+
+  // Adds |object| to a list of heap-allocated objects to be freed with |delete|
+  // when the arena is destroyed or reset.
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE void Own(T* object) {
+    OwnInternal(object, std::is_convertible<T*, MessageLite*>());
+  }
+
+  // Adds |object| to a list of objects whose destructors will be manually
+  // called when the arena is destroyed or reset. This differs from Own() in
+  // that it does not free the underlying memory with |delete|; hence, it is
+  // normally only used for objects that are placement-newed into
+  // arena-allocated memory.
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE void OwnDestructor(T* object) {
+    if (object != NULL) {
+      impl_.AddCleanup(object, &internal::arena_destruct_object<T>);
+    }
+  }
+
+  // Adds a custom member function on an object to the list of destructors that
+  // will be manually called when the arena is destroyed or reset. This differs
+  // from OwnDestructor() in that any member function may be specified, not only
+  // the class destructor.
+  PROTOBUF_ALWAYS_INLINE void OwnCustomDestructor(void* object,
+                                                  void (*destruct)(void*)) {
+    impl_.AddCleanup(object, destruct);
+  }
+
+  // Retrieves the arena associated with |value| if |value| is an arena-capable
+  // message, or NULL otherwise. If possible, the call resolves at compile time.
+  // Note that we can often devirtualize calls to `value->GetArena()` so usually
+  // calling this method is unnecessary.
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE static Arena* GetArena(const T* value) {
+    return GetArenaInternal(value);
+  }
+
+  template <typename T>
+  class InternalHelper {
+   private:
+    // Provides access to protected GetOwningArena to generated messages.
+    static Arena* GetOwningArena(const T* p) { return p->GetOwningArena(); }
+
+    static void InternalSwap(T* a, T* b) { a->InternalSwap(b); }
+
+    static Arena* GetArenaForAllocationInternal(
+        const T* p, std::true_type /*is_derived_from<MessageLite>*/) {
+      return p->GetArenaForAllocation();
+    }
+
+    static Arena* GetArenaForAllocationInternal(
+        const T* p, std::false_type /*is_derived_from<MessageLite>*/) {
+      return GetArenaForAllocationForNonMessage(
+          p, typename is_arena_constructable::type());
+    }
+
+    static Arena* GetArenaForAllocationForNonMessage(
+        const T* p, std::true_type /*is_arena_constructible*/) {
+      return p->GetArena();
+    }
+
+    static Arena* GetArenaForAllocationForNonMessage(
+        const T* p, std::false_type /*is_arena_constructible*/) {
+      return GetArenaForAllocationForNonMessageNonArenaConstructible(
+          p, typename has_get_arena::type());
+    }
+
+    static Arena* GetArenaForAllocationForNonMessageNonArenaConstructible(
+        const T* p, std::true_type /*has_get_arena*/) {
+      return p->GetArena();
+    }
+
+    static Arena* GetArenaForAllocationForNonMessageNonArenaConstructible(
+        const T* /* p */, std::false_type /*has_get_arena*/) {
+      return nullptr;
+    }
+
+    template <typename U>
+    static char DestructorSkippable(const typename U::DestructorSkippable_*);
+    template <typename U>
+    static double DestructorSkippable(...);
+
+    typedef std::integral_constant<
+        bool, sizeof(DestructorSkippable<T>(static_cast<const T*>(0))) ==
+                      sizeof(char) ||
+                  std::is_trivially_destructible<T>::value>
+        is_destructor_skippable;
+
+    template <typename U>
+    static char ArenaConstructable(
+        const typename U::InternalArenaConstructable_*);
+    template <typename U>
+    static double ArenaConstructable(...);
+
+    typedef std::integral_constant<bool, sizeof(ArenaConstructable<T>(
+                                             static_cast<const T*>(0))) ==
+                                             sizeof(char)>
+        is_arena_constructable;
+
+    template <typename U,
+              typename std::enable_if<
+                  std::is_same<Arena*, decltype(std::declval<const U>()
+                                                    .GetArena())>::value,
+                  int>::type = 0>
+    static char HasGetArena(decltype(&U::GetArena));
+    template <typename U>
+    static double HasGetArena(...);
+
+    typedef std::integral_constant<bool, sizeof(HasGetArena<T>(nullptr)) ==
+                                             sizeof(char)>
+        has_get_arena;
+
+    template <typename... Args>
+    static T* Construct(void* ptr, Args&&... args) {
+      return new (ptr) T(static_cast<Args&&>(args)...);
+    }
+
+    static inline PROTOBUF_ALWAYS_INLINE T* New() {
+      return new T(nullptr);
+    }
+
+    static Arena* GetArena(const T* p) { return p->GetArena(); }
+
+    friend class Arena;
+    friend class TestUtil::ReflectionTester;
+  };
+
+  // Provides access to protected GetOwningArena to generated messages.  For
+  // internal use only.
+  template <typename T>
+  static Arena* InternalGetOwningArena(const T* p) {
+    return InternalHelper<T>::GetOwningArena(p);
+  }
+
+  // Provides access to protected GetArenaForAllocation to generated messages.
+  // For internal use only.
+  template <typename T>
+  static Arena* InternalGetArenaForAllocation(const T* p) {
+    return InternalHelper<T>::GetArenaForAllocationInternal(
+        p, std::is_convertible<T*, MessageLite*>());
+  }
+
+  // Creates message-owned arena.  For internal use only.
+  static Arena* InternalCreateMessageOwnedArena() {
+    return new Arena(internal::MessageOwned{});
+  }
+
+  // Checks whether this arena is message-owned.  For internal use only.
+  bool InternalIsMessageOwnedArena() { return IsMessageOwned(); }
+
+  // Helper typetraits that indicates support for arenas in a type T at compile
+  // time. This is public only to allow construction of higher-level templated
+  // utilities.
+  //
+  // is_arena_constructable<T>::value is true if the message type T has arena
+  // support enabled, and false otherwise.
+  //
+  // is_destructor_skippable<T>::value is true if the message type T has told
+  // the arena that it is safe to skip the destructor, and false otherwise.
+  //
+  // This is inside Arena because only Arena has the friend relationships
+  // necessary to see the underlying generated code traits.
+  template <typename T>
+  struct is_arena_constructable : InternalHelper<T>::is_arena_constructable {};
+  template <typename T>
+  struct is_destructor_skippable : InternalHelper<T>::is_destructor_skippable {
+  };
+
+ private:
+  internal::ThreadSafeArena impl_;
+
+  template <typename T>
+  struct has_get_arena : InternalHelper<T>::has_get_arena {};
+
+  // Constructor solely used by message-owned arena.
+  inline Arena(internal::MessageOwned) : impl_(internal::MessageOwned{}) {}
+
+  // Checks whether this arena is message-owned.
+  PROTOBUF_ALWAYS_INLINE bool IsMessageOwned() const {
+    return impl_.IsMessageOwned();
+  }
+
+  void ReturnArrayMemory(void* p, size_t size) {
+    impl_.ReturnArrayMemory(p, size);
+  }
+
+  template <typename T, typename... Args>
+  PROTOBUF_NDEBUG_INLINE static T* CreateMessageInternal(Arena* arena,
+                                                         Args&&... args) {
+    static_assert(
+        InternalHelper<T>::is_arena_constructable::value,
+        "CreateMessage can only construct types that are ArenaConstructable");
+    if (arena == NULL) {
+      return new T(nullptr, static_cast<Args&&>(args)...);
+    } else {
+      return arena->DoCreateMessage<T>(static_cast<Args&&>(args)...);
+    }
+  }
+
+  // This specialization for no arguments is necessary, because its behavior is
+  // slightly different.  When the arena pointer is nullptr, it calls T()
+  // instead of T(nullptr).
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE static T* CreateMessageInternal(Arena* arena) {
+    static_assert(
+        InternalHelper<T>::is_arena_constructable::value,
+        "CreateMessage can only construct types that are ArenaConstructable");
+    if (arena == NULL) {
+      // Generated arena constructor T(Arena*) is protected. Call via
+      // InternalHelper.
+      return InternalHelper<T>::New();
+    } else {
+      return arena->DoCreateMessage<T>();
+    }
+  }
+
+  // Allocate and also optionally call collector with the allocated type info
+  // when allocation recording is enabled.
+  PROTOBUF_NDEBUG_INLINE void* AllocateInternal(size_t size, size_t align,
+                                                void (*destructor)(void*),
+                                                const std::type_info* type) {
+    // Monitor allocation if needed.
+    if (destructor == nullptr) {
+      return AllocateAlignedWithHook(size, align, type);
+    } else {
+      if (align <= 8) {
+        auto res = AllocateAlignedWithCleanup(internal::AlignUpTo8(size), type);
+        res.second->elem = res.first;
+        res.second->cleanup = destructor;
+        return res.first;
+      } else {
+        auto res = AllocateAlignedWithCleanup(size + align - 8, type);
+        auto ptr = internal::AlignTo(res.first, align);
+        res.second->elem = ptr;
+        res.second->cleanup = destructor;
+        return ptr;
+      }
+    }
+  }
+
+  // CreateMessage<T> requires that T supports arenas, but this private method
+  // works whether or not T supports arenas. These are not exposed to user code
+  // as it can cause confusing API usages, and end up having double free in
+  // user code. These are used only internally from LazyField and Repeated
+  // fields, since they are designed to work in all mode combinations.
+  template <typename Msg, typename... Args>
+  PROTOBUF_ALWAYS_INLINE static Msg* DoCreateMaybeMessage(Arena* arena,
+                                                          std::true_type,
+                                                          Args&&... args) {
+    return CreateMessageInternal<Msg>(arena, std::forward<Args>(args)...);
+  }
+
+  template <typename T, typename... Args>
+  PROTOBUF_ALWAYS_INLINE static T* DoCreateMaybeMessage(Arena* arena,
+                                                        std::false_type,
+                                                        Args&&... args) {
+    return Create<T>(arena, std::forward<Args>(args)...);
+  }
+
+  template <typename T, typename... Args>
+  PROTOBUF_ALWAYS_INLINE static T* CreateMaybeMessage(Arena* arena,
+                                                      Args&&... args) {
+    return DoCreateMaybeMessage<T>(arena, is_arena_constructable<T>(),
+                                   std::forward<Args>(args)...);
+  }
+
+  // Just allocate the required size for the given type assuming the
+  // type has a trivial constructor.
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE T* CreateInternalRawArray(size_t num_elements) {
+    GOOGLE_CHECK_LE(num_elements, std::numeric_limits<size_t>::max() / sizeof(T))
+        << "Requested size is too large to fit into size_t.";
+    // We count on compiler to realize that if sizeof(T) is a multiple of
+    // 8 AlignUpTo can be elided.
+    const size_t n = sizeof(T) * num_elements;
+    return static_cast<T*>(
+        AllocateAlignedWithHookForArray(n, alignof(T), RTTI_TYPE_ID(T)));
+  }
+
+  template <typename T, typename... Args>
+  PROTOBUF_NDEBUG_INLINE T* DoCreateMessage(Args&&... args) {
+    return InternalHelper<T>::Construct(
+        AllocateInternal(sizeof(T), alignof(T),
+                         internal::ObjectDestructor<
+                             InternalHelper<T>::is_destructor_skippable::value,
+                             T>::destructor,
+                         RTTI_TYPE_ID(T)),
+        this, std::forward<Args>(args)...);
+  }
+
+  // CreateInArenaStorage is used to implement map field. Without it,
+  // Map need to call generated message's protected arena constructor,
+  // which needs to declare Map as friend of generated message.
+  template <typename T, typename... Args>
+  static void CreateInArenaStorage(T* ptr, Arena* arena, Args&&... args) {
+    CreateInArenaStorageInternal(ptr, arena,
+                                 typename is_arena_constructable<T>::type(),
+                                 std::forward<Args>(args)...);
+    if (arena != nullptr) {
+      RegisterDestructorInternal(
+          ptr, arena,
+          typename InternalHelper<T>::is_destructor_skippable::type());
+    }
+  }
+
+  template <typename T, typename... Args>
+  static void CreateInArenaStorageInternal(T* ptr, Arena* arena,
+                                           std::true_type, Args&&... args) {
+    InternalHelper<T>::Construct(ptr, arena, std::forward<Args>(args)...);
+  }
+  template <typename T, typename... Args>
+  static void CreateInArenaStorageInternal(T* ptr, Arena* /* arena */,
+                                           std::false_type, Args&&... args) {
+    new (ptr) T(std::forward<Args>(args)...);
+  }
+
+  template <typename T>
+  static void RegisterDestructorInternal(T* /* ptr */, Arena* /* arena */,
+                                         std::true_type) {}
+  template <typename T>
+  static void RegisterDestructorInternal(T* ptr, Arena* arena,
+                                         std::false_type) {
+    arena->OwnDestructor(ptr);
+  }
+
+  // These implement Create(). The second parameter has type 'true_type' if T is
+  // a subtype of Message and 'false_type' otherwise.
+  template <typename T, typename... Args>
+  PROTOBUF_ALWAYS_INLINE static T* CreateInternal(Arena* arena, std::true_type,
+                                                  Args&&... args) {
+    if (arena == nullptr) {
+      return new T(std::forward<Args>(args)...);
+    } else {
+      auto destructor =
+          internal::ObjectDestructor<std::is_trivially_destructible<T>::value,
+                                     T>::destructor;
+      T* result =
+          new (arena->AllocateInternal(sizeof(T), alignof(T), destructor,
+                                       RTTI_TYPE_ID(T)))
+          T(std::forward<Args>(args)...);
+      return result;
+    }
+  }
+  template <typename T, typename... Args>
+  PROTOBUF_ALWAYS_INLINE static T* CreateInternal(Arena* arena, std::false_type,
+                                                  Args&&... args) {
+    if (arena == nullptr) {
+      return new T(std::forward<Args>(args)...);
+    } else {
+      auto destructor =
+          internal::ObjectDestructor<std::is_trivially_destructible<T>::value,
+                                     T>::destructor;
+      return new (arena->AllocateInternal(sizeof(T), alignof(T), destructor,
+                                          RTTI_TYPE_ID(T)))
+          T(std::forward<Args>(args)...);
+    }
+  }
+
+  // These implement Own(), which registers an object for deletion (destructor
+  // call and operator delete()). The second parameter has type 'true_type' if T
+  // is a subtype of Message and 'false_type' otherwise. Collapsing
+  // all template instantiations to one for generic Message reduces code size,
+  // using the virtual destructor instead.
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE void OwnInternal(T* object, std::true_type) {
+    if (object != NULL) {
+      impl_.AddCleanup(object, &internal::arena_delete_object<MessageLite>);
+    }
+  }
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE void OwnInternal(T* object, std::false_type) {
+    if (object != NULL) {
+      impl_.AddCleanup(object, &internal::arena_delete_object<T>);
+    }
+  }
+
+  // Implementation for GetArena(). Only message objects with
+  // InternalArenaConstructable_ tags can be associated with an arena, and such
+  // objects must implement a GetArena() method.
+  template <typename T, typename std::enable_if<
+                            is_arena_constructable<T>::value, int>::type = 0>
+  PROTOBUF_ALWAYS_INLINE static Arena* GetArenaInternal(const T* value) {
+    return InternalHelper<T>::GetArena(value);
+  }
+  template <typename T,
+            typename std::enable_if<!is_arena_constructable<T>::value &&
+                                        has_get_arena<T>::value,
+                                    int>::type = 0>
+  PROTOBUF_ALWAYS_INLINE static Arena* GetArenaInternal(const T* value) {
+    return value->GetArena();
+  }
+  template <typename T,
+            typename std::enable_if<!is_arena_constructable<T>::value &&
+                                        !has_get_arena<T>::value,
+                                    int>::type = 0>
+  PROTOBUF_ALWAYS_INLINE static Arena* GetArenaInternal(const T* value) {
+    (void)value;
+    return nullptr;
+  }
+
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE static Arena* GetOwningArena(const T* value) {
+    return GetOwningArenaInternal(
+        value, std::is_convertible<T*, MessageLite*>());
+  }
+
+  // Implementation for GetOwningArena(). All and only message objects have
+  // GetOwningArena() method.
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE static Arena* GetOwningArenaInternal(
+      const T* value, std::true_type) {
+    return InternalHelper<T>::GetOwningArena(value);
+  }
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE static Arena* GetOwningArenaInternal(
+      const T* /* value */, std::false_type) {
+    return nullptr;
+  }
+
+  void* AllocateAlignedWithHookForArray(size_t n, size_t align,
+                                        const std::type_info* type) {
+    if (align <= 8) {
+      return AllocateAlignedWithHookForArray(internal::AlignUpTo8(n), type);
+    } else {
+      // We are wasting space by over allocating align - 8 bytes. Compared
+      // to a dedicated function that takes current alignment in consideration.
+      // Such a scheme would only waste (align - 8)/2 bytes on average, but
+      // requires a dedicated function in the outline arena allocation
+      // functions. Possibly re-evaluate tradeoffs later.
+      return internal::AlignTo(
+          AllocateAlignedWithHookForArray(n + align - 8, type), align);
+    }
+  }
+
+  void* AllocateAlignedWithHook(size_t n, size_t align,
+                                const std::type_info* type) {
+    if (align <= 8) {
+      return AllocateAlignedWithHook(internal::AlignUpTo8(n), type);
+    } else {
+      // We are wasting space by over allocating align - 8 bytes. Compared
+      // to a dedicated function that takes current alignment in consideration.
+      // Such a scheme would only waste (align - 8)/2 bytes on average, but
+      // requires a dedicated function in the outline arena allocation
+      // functions. Possibly re-evaluate tradeoffs later.
+      return internal::AlignTo(AllocateAlignedWithHook(n + align - 8, type),
+                               align);
+    }
+  }
+
+  void* AllocateAlignedNoHook(size_t n);
+  void* AllocateAlignedWithHook(size_t n, const std::type_info* type);
+  void* AllocateAlignedWithHookForArray(size_t n, const std::type_info* type);
+  std::pair<void*, internal::SerialArena::CleanupNode*>
+  AllocateAlignedWithCleanup(size_t n, const std::type_info* type);
+
+  template <typename Type>
+  friend class internal::GenericTypeHandler;
+  friend class internal::InternalMetadata;  // For user_arena().
+  friend class internal::LazyField;        // For CreateMaybeMessage.
+  friend class internal::EpsCopyInputStream;  // For parser performance
+  friend class MessageLite;
+  template <typename Key, typename T>
+  friend class Map;
+  template <typename>
+  friend class RepeatedField;                   // For ReturnArrayMemory
+  friend class internal::RepeatedPtrFieldBase;  // For ReturnArrayMemory
+  friend struct internal::ArenaTestPeer;
+};
+
+// Defined above for supporting environments without RTTI.
+#undef RTTI_TYPE_ID
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_ARENA_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arena_impl.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arena_impl.h
new file mode 100644
index 0000000..8af70c4
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arena_impl.h
@@ -0,0 +1,686 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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 file defines an Arena allocator for better allocation performance.
+
+#ifndef GOOGLE_PROTOBUF_ARENA_IMPL_H__
+#define GOOGLE_PROTOBUF_ARENA_IMPL_H__
+
+#include <atomic>
+#include <limits>
+#include <typeinfo>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/port.h>
+
+#ifdef ADDRESS_SANITIZER
+#include <sanitizer/asan_interface.h>
+#endif  // ADDRESS_SANITIZER
+
+#include <google/protobuf/arenaz_sampler.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// To prevent sharing cache lines between threads
+#ifdef __cpp_aligned_new
+enum { kCacheAlignment = 64 };
+#else
+enum { kCacheAlignment = alignof(max_align_t) };  // do the best we can
+#endif
+
+inline constexpr size_t AlignUpTo8(size_t n) {
+  // Align n to next multiple of 8 (from Hacker's Delight, Chapter 3.)
+  return (n + 7) & static_cast<size_t>(-8);
+}
+
+using LifecycleIdAtomic = uint64_t;
+
+// MetricsCollector collects stats for a particular arena.
+class PROTOBUF_EXPORT ArenaMetricsCollector {
+ public:
+  ArenaMetricsCollector(bool record_allocs) : record_allocs_(record_allocs) {}
+
+  // Invoked when the arena is about to be destroyed. This method will
+  // typically finalize any metric collection and delete the collector.
+  // space_allocated is the space used by the arena.
+  virtual void OnDestroy(uint64_t space_allocated) = 0;
+
+  // OnReset() is called when the associated arena is reset.
+  // space_allocated is the space used by the arena just before the reset.
+  virtual void OnReset(uint64_t space_allocated) = 0;
+
+  // OnAlloc is called when an allocation happens.
+  // type_info is promised to be static - its lifetime extends to
+  // match program's lifetime (It is given by typeid operator).
+  // Note: typeid(void) will be passed as allocated_type every time we
+  // intentionally want to avoid monitoring an allocation. (i.e. internal
+  // allocations for managing the arena)
+  virtual void OnAlloc(const std::type_info* allocated_type,
+                       uint64_t alloc_size) = 0;
+
+  // Does OnAlloc() need to be called?  If false, metric collection overhead
+  // will be reduced since we will not do extra work per allocation.
+  bool RecordAllocs() { return record_allocs_; }
+
+ protected:
+  // This class is destructed by the call to OnDestroy().
+  ~ArenaMetricsCollector() = default;
+  const bool record_allocs_;
+};
+
+struct AllocationPolicy {
+  static constexpr size_t kDefaultStartBlockSize = 256;
+  static constexpr size_t kDefaultMaxBlockSize = 8192;
+
+  size_t start_block_size = kDefaultStartBlockSize;
+  size_t max_block_size = kDefaultMaxBlockSize;
+  void* (*block_alloc)(size_t) = nullptr;
+  void (*block_dealloc)(void*, size_t) = nullptr;
+  ArenaMetricsCollector* metrics_collector = nullptr;
+
+  bool IsDefault() const {
+    return start_block_size == kDefaultMaxBlockSize &&
+           max_block_size == kDefaultMaxBlockSize && block_alloc == nullptr &&
+           block_dealloc == nullptr && metrics_collector == nullptr;
+  }
+};
+
+// Tagged pointer to an AllocationPolicy.
+class TaggedAllocationPolicyPtr {
+ public:
+  constexpr TaggedAllocationPolicyPtr() : policy_(0) {}
+
+  explicit TaggedAllocationPolicyPtr(AllocationPolicy* policy)
+      : policy_(reinterpret_cast<uintptr_t>(policy)) {}
+
+  void set_policy(AllocationPolicy* policy) {
+    auto bits = policy_ & kTagsMask;
+    policy_ = reinterpret_cast<uintptr_t>(policy) | bits;
+  }
+
+  AllocationPolicy* get() {
+    return reinterpret_cast<AllocationPolicy*>(policy_ & kPtrMask);
+  }
+  const AllocationPolicy* get() const {
+    return reinterpret_cast<const AllocationPolicy*>(policy_ & kPtrMask);
+  }
+
+  AllocationPolicy& operator*() { return *get(); }
+  const AllocationPolicy& operator*() const { return *get(); }
+
+  AllocationPolicy* operator->() { return get(); }
+  const AllocationPolicy* operator->() const { return get(); }
+
+  bool is_user_owned_initial_block() const {
+    return static_cast<bool>(get_mask<kUserOwnedInitialBlock>());
+  }
+  void set_is_user_owned_initial_block(bool v) {
+    set_mask<kUserOwnedInitialBlock>(v);
+  }
+
+  bool should_record_allocs() const {
+    return static_cast<bool>(get_mask<kRecordAllocs>());
+  }
+  void set_should_record_allocs(bool v) { set_mask<kRecordAllocs>(v); }
+
+  uintptr_t get_raw() const { return policy_; }
+
+  inline void RecordAlloc(const std::type_info* allocated_type,
+                          size_t n) const {
+    get()->metrics_collector->OnAlloc(allocated_type, n);
+  }
+
+ private:
+  enum : uintptr_t {
+    kUserOwnedInitialBlock = 1,
+    kRecordAllocs = 2,
+  };
+
+  static constexpr uintptr_t kTagsMask = 7;
+  static constexpr uintptr_t kPtrMask = ~kTagsMask;
+
+  template <uintptr_t kMask>
+  uintptr_t get_mask() const {
+    return policy_ & kMask;
+  }
+  template <uintptr_t kMask>
+  void set_mask(bool v) {
+    if (v) {
+      policy_ |= kMask;
+    } else {
+      policy_ &= ~kMask;
+    }
+  }
+  uintptr_t policy_;
+};
+
+enum class AllocationClient { kDefault, kArray };
+
+// A simple arena allocator. Calls to allocate functions must be properly
+// serialized by the caller, hence this class cannot be used as a general
+// purpose allocator in a multi-threaded program. It serves as a building block
+// for ThreadSafeArena, which provides a thread-safe arena allocator.
+//
+// This class manages
+// 1) Arena bump allocation + owning memory blocks.
+// 2) Maintaining a cleanup list.
+// It delagetes the actual memory allocation back to ThreadSafeArena, which
+// contains the information on block growth policy and backing memory allocation
+// used.
+class PROTOBUF_EXPORT SerialArena {
+ public:
+  struct Memory {
+    void* ptr;
+    size_t size;
+  };
+
+  // Node contains the ptr of the object to be cleaned up and the associated
+  // cleanup function ptr.
+  struct CleanupNode {
+    void* elem;              // Pointer to the object to be cleaned up.
+    void (*cleanup)(void*);  // Function pointer to the destructor or deleter.
+  };
+
+  void CleanupList();
+  uint64_t SpaceAllocated() const {
+    return space_allocated_.load(std::memory_order_relaxed);
+  }
+  uint64_t SpaceUsed() const;
+
+  bool HasSpace(size_t n) const {
+    return n <= static_cast<size_t>(limit_ - ptr_);
+  }
+
+  // See comments on `cached_blocks_` member for details.
+  PROTOBUF_ALWAYS_INLINE void* TryAllocateFromCachedBlock(size_t size) {
+    if (PROTOBUF_PREDICT_FALSE(size < 16)) return nullptr;
+    // We round up to the next larger block in case the memory doesn't match
+    // the pattern we are looking for.
+    const size_t index = Bits::Log2FloorNonZero64(size - 1) - 3;
+
+    if (index >= cached_block_length_) return nullptr;
+    auto& cached_head = cached_blocks_[index];
+    if (cached_head == nullptr) return nullptr;
+
+    void* ret = cached_head;
+#ifdef ADDRESS_SANITIZER
+    ASAN_UNPOISON_MEMORY_REGION(ret, size);
+#endif  // ADDRESS_SANITIZER
+    cached_head = cached_head->next;
+    return ret;
+  }
+
+  // In kArray mode we look through cached blocks.
+  // We do not do this by default because most non-array allocations will not
+  // have the right size and will fail to find an appropriate cached block.
+  //
+  // TODO(sbenza): Evaluate if we should use cached blocks for message types of
+  // the right size. We can statically know if the allocation size can benefit
+  // from it.
+  template <AllocationClient alloc_client = AllocationClient::kDefault>
+  void* AllocateAligned(size_t n, const AllocationPolicy* policy) {
+    GOOGLE_DCHECK_EQ(internal::AlignUpTo8(n), n);  // Must be already aligned.
+    GOOGLE_DCHECK_GE(limit_, ptr_);
+
+    if (alloc_client == AllocationClient::kArray) {
+      if (void* res = TryAllocateFromCachedBlock(n)) {
+        return res;
+      }
+    }
+
+    if (PROTOBUF_PREDICT_FALSE(!HasSpace(n))) {
+      return AllocateAlignedFallback(n, policy);
+    }
+    return AllocateFromExisting(n);
+  }
+
+ private:
+  void* AllocateFromExisting(size_t n) {
+    void* ret = ptr_;
+    ptr_ += n;
+#ifdef ADDRESS_SANITIZER
+    ASAN_UNPOISON_MEMORY_REGION(ret, n);
+#endif  // ADDRESS_SANITIZER
+    return ret;
+  }
+
+  // See comments on `cached_blocks_` member for details.
+  void ReturnArrayMemory(void* p, size_t size) {
+    // We only need to check for 32-bit platforms.
+    // In 64-bit platforms the minimum allocation size from Repeated*Field will
+    // be 16 guaranteed.
+    if (sizeof(void*) < 8) {
+      if (PROTOBUF_PREDICT_FALSE(size < 16)) return;
+    } else {
+      GOOGLE_DCHECK(size >= 16);
+    }
+
+    // We round down to the next smaller block in case the memory doesn't match
+    // the pattern we are looking for. eg, someone might have called Reserve()
+    // on the repeated field.
+    const size_t index = Bits::Log2FloorNonZero64(size) - 4;
+
+    if (PROTOBUF_PREDICT_FALSE(index >= cached_block_length_)) {
+      // We can't put this object on the freelist so make this object the
+      // freelist. It is guaranteed it is larger than the one we have, and
+      // large enough to hold another allocation of `size`.
+      CachedBlock** new_list = static_cast<CachedBlock**>(p);
+      size_t new_size = size / sizeof(CachedBlock*);
+
+      std::copy(cached_blocks_, cached_blocks_ + cached_block_length_,
+                new_list);
+      std::fill(new_list + cached_block_length_, new_list + new_size, nullptr);
+      cached_blocks_ = new_list;
+      // Make the size fit in uint8_t. This is the power of two, so we don't
+      // need anything larger.
+      cached_block_length_ =
+          static_cast<uint8_t>(std::min(size_t{64}, new_size));
+
+      return;
+    }
+
+    auto& cached_head = cached_blocks_[index];
+    auto* new_node = static_cast<CachedBlock*>(p);
+    new_node->next = cached_head;
+    cached_head = new_node;
+#ifdef ADDRESS_SANITIZER
+    ASAN_POISON_MEMORY_REGION(p, size);
+#endif  // ADDRESS_SANITIZER
+  }
+
+ public:
+  // Allocate space if the current region provides enough space.
+  bool MaybeAllocateAligned(size_t n, void** out) {
+    GOOGLE_DCHECK_EQ(internal::AlignUpTo8(n), n);  // Must be already aligned.
+    GOOGLE_DCHECK_GE(limit_, ptr_);
+    if (PROTOBUF_PREDICT_FALSE(!HasSpace(n))) return false;
+    *out = AllocateFromExisting(n);
+    return true;
+  }
+
+  std::pair<void*, CleanupNode*> AllocateAlignedWithCleanup(
+      size_t n, const AllocationPolicy* policy) {
+    GOOGLE_DCHECK_EQ(internal::AlignUpTo8(n), n);  // Must be already aligned.
+    if (PROTOBUF_PREDICT_FALSE(!HasSpace(n + kCleanupSize))) {
+      return AllocateAlignedWithCleanupFallback(n, policy);
+    }
+    return AllocateFromExistingWithCleanupFallback(n);
+  }
+
+ private:
+  std::pair<void*, CleanupNode*> AllocateFromExistingWithCleanupFallback(
+      size_t n) {
+    void* ret = ptr_;
+    ptr_ += n;
+    limit_ -= kCleanupSize;
+#ifdef ADDRESS_SANITIZER
+    ASAN_UNPOISON_MEMORY_REGION(ret, n);
+    ASAN_UNPOISON_MEMORY_REGION(limit_, kCleanupSize);
+#endif  // ADDRESS_SANITIZER
+    return CreatePair(ret, reinterpret_cast<CleanupNode*>(limit_));
+  }
+
+ public:
+  void AddCleanup(void* elem, void (*cleanup)(void*),
+                  const AllocationPolicy* policy) {
+    auto res = AllocateAlignedWithCleanup(0, policy);
+    res.second->elem = elem;
+    res.second->cleanup = cleanup;
+  }
+
+  void* owner() const { return owner_; }
+  SerialArena* next() const { return next_; }
+  void set_next(SerialArena* next) { next_ = next; }
+
+ private:
+  friend class ThreadSafeArena;
+  friend class ArenaBenchmark;
+
+  // Creates a new SerialArena inside mem using the remaining memory as for
+  // future allocations.
+  static SerialArena* New(SerialArena::Memory mem, void* owner,
+                          ThreadSafeArenaStats* stats);
+  // Free SerialArena returning the memory passed in to New
+  template <typename Deallocator>
+  Memory Free(Deallocator deallocator);
+
+  // Blocks are variable length malloc-ed objects.  The following structure
+  // describes the common header for all blocks.
+  struct Block {
+    Block(Block* next, size_t size) : next(next), size(size), start(nullptr) {}
+
+    char* Pointer(size_t n) {
+      GOOGLE_DCHECK(n <= size);
+      return reinterpret_cast<char*>(this) + n;
+    }
+
+    Block* const next;
+    const size_t size;
+    CleanupNode* start;
+    // data follows
+  };
+
+  void* owner_;            // &ThreadCache of this thread;
+  Block* head_;            // Head of linked list of blocks.
+  SerialArena* next_;      // Next SerialArena in this linked list.
+  size_t space_used_ = 0;  // Necessary for metrics.
+  std::atomic<size_t> space_allocated_;
+
+  // Next pointer to allocate from.  Always 8-byte aligned.  Points inside
+  // head_ (and head_->pos will always be non-canonical).  We keep these
+  // here to reduce indirection.
+  char* ptr_;
+  // Limiting address up to which memory can be allocated from the head block.
+  char* limit_;
+  // For holding sampling information.  The pointer is owned by the
+  // ThreadSafeArena that holds this serial arena.
+  ThreadSafeArenaStats* arena_stats_;
+
+  // Repeated*Field and Arena play together to reduce memory consumption by
+  // reusing blocks. Currently, natural growth of the repeated field types makes
+  // them allocate blocks of size `8 + 2^N, N>=3`.
+  // When the repeated field grows returns the previous block and we put it in
+  // this free list.
+  // `cached_blocks_[i]` points to the free list for blocks of size `8+2^(i+3)`.
+  // The array of freelists is grown when needed in `ReturnArrayMemory()`.
+  struct CachedBlock {
+    // Simple linked list.
+    CachedBlock* next;
+  };
+  uint8_t cached_block_length_ = 0;
+  CachedBlock** cached_blocks_ = nullptr;
+
+  // Constructor is private as only New() should be used.
+  inline SerialArena(Block* b, void* owner, ThreadSafeArenaStats* stats);
+  void* AllocateAlignedFallback(size_t n, const AllocationPolicy* policy);
+  std::pair<void*, CleanupNode*> AllocateAlignedWithCleanupFallback(
+      size_t n, const AllocationPolicy* policy);
+  void AllocateNewBlock(size_t n, const AllocationPolicy* policy);
+
+  std::pair<void*, CleanupNode*> CreatePair(void* ptr, CleanupNode* node) {
+    return {ptr, node};
+  }
+
+ public:
+  static constexpr size_t kBlockHeaderSize = AlignUpTo8(sizeof(Block));
+  static constexpr size_t kCleanupSize = AlignUpTo8(sizeof(CleanupNode));
+};
+
+// Tag type used to invoke the constructor of message-owned arena.
+// Only message-owned arenas use this constructor for creation.
+// Such constructors are internal implementation details of the library.
+struct MessageOwned {
+  explicit MessageOwned() = default;
+};
+
+// This class provides the core Arena memory allocation library. Different
+// implementations only need to implement the public interface below.
+// Arena is not a template type as that would only be useful if all protos
+// in turn would be templates, which will/cannot happen. However separating
+// the memory allocation part from the cruft of the API users expect we can
+// use #ifdef the select the best implementation based on hardware / OS.
+class PROTOBUF_EXPORT ThreadSafeArena {
+ public:
+  ThreadSafeArena() { Init(); }
+
+  // Constructor solely used by message-owned arena.
+  ThreadSafeArena(internal::MessageOwned) : tag_and_id_(kMessageOwnedArena) {
+    Init();
+  }
+
+  ThreadSafeArena(char* mem, size_t size) { InitializeFrom(mem, size); }
+
+  explicit ThreadSafeArena(void* mem, size_t size,
+                           const AllocationPolicy& policy) {
+    InitializeWithPolicy(mem, size, policy);
+  }
+
+  // Destructor deletes all owned heap allocated objects, and destructs objects
+  // that have non-trivial destructors, except for proto2 message objects whose
+  // destructors can be skipped. Also, frees all blocks except the initial block
+  // if it was passed in.
+  ~ThreadSafeArena();
+
+  uint64_t Reset();
+
+  uint64_t SpaceAllocated() const;
+  uint64_t SpaceUsed() const;
+
+  template <AllocationClient alloc_client = AllocationClient::kDefault>
+  void* AllocateAligned(size_t n, const std::type_info* type) {
+    SerialArena* arena = nullptr;
+    if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
+                              GetSerialArenaFast(&arena))) {
+      return arena->AllocateAligned<alloc_client>(n, AllocPolicy());
+    } else {
+      return AllocateAlignedFallback(n, type);
+    }
+  }
+
+  void ReturnArrayMemory(void* p, size_t size) {
+    SerialArena* arena = nullptr;
+    if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
+      arena->ReturnArrayMemory(p, size);
+    }
+  }
+
+  // This function allocates n bytes if the common happy case is true and
+  // returns true. Otherwise does nothing and returns false. This strange
+  // semantics is necessary to allow callers to program functions that only
+  // have fallback function calls in tail position. This substantially improves
+  // code for the happy path.
+  PROTOBUF_NDEBUG_INLINE bool MaybeAllocateAligned(size_t n, void** out) {
+    SerialArena* arena;
+    if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
+                              GetSerialArenaFromThreadCache(&arena))) {
+      return arena->MaybeAllocateAligned(n, out);
+    }
+    return false;
+  }
+
+  std::pair<void*, SerialArena::CleanupNode*> AllocateAlignedWithCleanup(
+      size_t n, const std::type_info* type);
+
+  // Add object pointer and cleanup function pointer to the list.
+  void AddCleanup(void* elem, void (*cleanup)(void*));
+
+  // Checks whether this arena is message-owned.
+  PROTOBUF_ALWAYS_INLINE bool IsMessageOwned() const {
+    return tag_and_id_ & kMessageOwnedArena;
+  }
+
+ private:
+  // Unique for each arena. Changes on Reset().
+  uint64_t tag_and_id_ = 0;
+  // The LSB of tag_and_id_ indicates if the arena is message-owned.
+  enum : uint64_t { kMessageOwnedArena = 1 };
+
+  TaggedAllocationPolicyPtr alloc_policy_;  // Tagged pointer to AllocPolicy.
+
+  static_assert(std::is_trivially_destructible<SerialArena>{},
+                "SerialArena needs to be trivially destructible.");
+  // Pointer to a linked list of SerialArena.
+  std::atomic<SerialArena*> threads_;
+  std::atomic<SerialArena*> hint_;  // Fast thread-local block access
+
+  const AllocationPolicy* AllocPolicy() const { return alloc_policy_.get(); }
+  void InitializeFrom(void* mem, size_t size);
+  void InitializeWithPolicy(void* mem, size_t size, AllocationPolicy policy);
+  void* AllocateAlignedFallback(size_t n, const std::type_info* type);
+  std::pair<void*, SerialArena::CleanupNode*>
+  AllocateAlignedWithCleanupFallback(size_t n, const std::type_info* type);
+
+  void Init();
+  void SetInitialBlock(void* mem, size_t size);
+
+  // Delete or Destruct all objects owned by the arena.
+  void CleanupList();
+
+  inline uint64_t LifeCycleId() const {
+    return tag_and_id_ & ~kMessageOwnedArena;
+  }
+
+  inline void CacheSerialArena(SerialArena* serial) {
+    thread_cache().last_serial_arena = serial;
+    thread_cache().last_lifecycle_id_seen = tag_and_id_;
+    // TODO(haberman): evaluate whether we would gain efficiency by getting rid
+    // of hint_.  It's the only write we do to ThreadSafeArena in the allocation
+    // path, which will dirty the cache line.
+
+    hint_.store(serial, std::memory_order_release);
+  }
+
+  PROTOBUF_NDEBUG_INLINE bool GetSerialArenaFast(SerialArena** arena) {
+    if (GetSerialArenaFromThreadCache(arena)) return true;
+
+    // Check whether we own the last accessed SerialArena on this arena.  This
+    // fast path optimizes the case where a single thread uses multiple arenas.
+    ThreadCache* tc = &thread_cache();
+    SerialArena* serial = hint_.load(std::memory_order_acquire);
+    if (PROTOBUF_PREDICT_TRUE(serial != nullptr && serial->owner() == tc)) {
+      *arena = serial;
+      return true;
+    }
+    return false;
+  }
+
+  PROTOBUF_NDEBUG_INLINE bool GetSerialArenaFromThreadCache(
+      SerialArena** arena) {
+    // If this thread already owns a block in this arena then try to use that.
+    // This fast path optimizes the case where multiple threads allocate from
+    // the same arena.
+    ThreadCache* tc = &thread_cache();
+    if (PROTOBUF_PREDICT_TRUE(tc->last_lifecycle_id_seen == tag_and_id_)) {
+      *arena = tc->last_serial_arena;
+      return true;
+    }
+    return false;
+  }
+  SerialArena* GetSerialArenaFallback(void* me);
+
+  template <typename Functor>
+  void PerSerialArena(Functor fn) {
+    // By omitting an Acquire barrier we ensure that any user code that doesn't
+    // properly synchronize Reset() or the destructor will throw a TSAN warning.
+    SerialArena* serial = threads_.load(std::memory_order_relaxed);
+
+    for (; serial; serial = serial->next()) fn(serial);
+  }
+
+  // Releases all memory except the first block which it returns. The first
+  // block might be owned by the user and thus need some extra checks before
+  // deleting.
+  SerialArena::Memory Free(size_t* space_allocated);
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4324)
+#endif
+  struct alignas(kCacheAlignment) ThreadCache {
+#if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
+    // If we are using the ThreadLocalStorage class to store the ThreadCache,
+    // then the ThreadCache's default constructor has to be responsible for
+    // initializing it.
+    ThreadCache()
+        : next_lifecycle_id(0),
+          last_lifecycle_id_seen(-1),
+          last_serial_arena(nullptr) {}
+#endif
+
+    // Number of per-thread lifecycle IDs to reserve. Must be power of two.
+    // To reduce contention on a global atomic, each thread reserves a batch of
+    // IDs.  The following number is calculated based on a stress test with
+    // ~6500 threads all frequently allocating a new arena.
+    static constexpr size_t kPerThreadIds = 256;
+    // Next lifecycle ID available to this thread. We need to reserve a new
+    // batch, if `next_lifecycle_id & (kPerThreadIds - 1) == 0`.
+    uint64_t next_lifecycle_id;
+    // The ThreadCache is considered valid as long as this matches the
+    // lifecycle_id of the arena being used.
+    uint64_t last_lifecycle_id_seen;
+    SerialArena* last_serial_arena;
+  };
+
+  // Lifecycle_id can be highly contended variable in a situation of lots of
+  // arena creation. Make sure that other global variables are not sharing the
+  // cacheline.
+#ifdef _MSC_VER
+#pragma warning(disable : 4324)
+#endif
+  struct alignas(kCacheAlignment) CacheAlignedLifecycleIdGenerator {
+    std::atomic<LifecycleIdAtomic> id;
+  };
+  static CacheAlignedLifecycleIdGenerator lifecycle_id_generator_;
+#if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
+  // iOS does not support __thread keyword so we use a custom thread local
+  // storage class we implemented.
+  static ThreadCache& thread_cache();
+#elif defined(PROTOBUF_USE_DLLS)
+  // Thread local variables cannot be exposed through DLL interface but we can
+  // wrap them in static functions.
+  static ThreadCache& thread_cache();
+#else
+  static PROTOBUF_THREAD_LOCAL ThreadCache thread_cache_;
+  static ThreadCache& thread_cache() { return thread_cache_; }
+#endif
+
+  ThreadSafeArenaStatsHandle arena_stats_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ThreadSafeArena);
+  // All protos have pointers back to the arena hence Arena must have
+  // pointer stability.
+  ThreadSafeArena(ThreadSafeArena&&) = delete;
+  ThreadSafeArena& operator=(ThreadSafeArena&&) = delete;
+
+ public:
+  // kBlockHeaderSize is sizeof(Block), aligned up to the nearest multiple of 8
+  // to protect the invariant that pos is always at a multiple of 8.
+  static constexpr size_t kBlockHeaderSize = SerialArena::kBlockHeaderSize;
+  static constexpr size_t kSerialArenaSize =
+      (sizeof(SerialArena) + 7) & static_cast<size_t>(-8);
+  static_assert(kBlockHeaderSize % 8 == 0,
+                "kBlockHeaderSize must be a multiple of 8.");
+  static_assert(kSerialArenaSize % 8 == 0,
+                "kSerialArenaSize must be a multiple of 8.");
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_ARENA_IMPL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arenastring.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arenastring.h
new file mode 100644
index 0000000..6bc8395
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arenastring.h
@@ -0,0 +1,480 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_ARENASTRING_H__
+#define GOOGLE_PROTOBUF_ARENASTRING_H__
+
+#include <algorithm>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/explicitly_constructed.h>
+
+// must be last:
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+
+namespace google {
+namespace protobuf {
+namespace internal {
+class EpsCopyInputStream;
+
+class SwapFieldHelper;
+
+// Declared in message_lite.h
+PROTOBUF_EXPORT extern ExplicitlyConstructedArenaString
+    fixed_address_empty_string;
+
+// Lazy string instance to support string fields with non-empty default.
+// These are initialized on the first call to .get().
+class PROTOBUF_EXPORT LazyString {
+ public:
+  // We explicitly make LazyString an aggregate so that MSVC can do constant
+  // initialization on it without marking it `constexpr`.
+  // We do not want to use `constexpr` because it makes it harder to have extern
+  // storage for it and causes library bloat.
+  struct InitValue {
+    const char* ptr;
+    size_t size;
+  };
+  // We keep a union of the initialization value and the std::string to save on
+  // space. We don't need the string array after Init() is done.
+  union {
+    mutable InitValue init_value_;
+    alignas(std::string) mutable char string_buf_[sizeof(std::string)];
+  };
+  mutable std::atomic<const std::string*> inited_;
+
+  const std::string& get() const {
+    // This check generates less code than a call-once invocation.
+    auto* res = inited_.load(std::memory_order_acquire);
+    if (PROTOBUF_PREDICT_FALSE(res == nullptr)) return Init();
+    return *res;
+  }
+
+ private:
+  // Initialize the string in `string_buf_`, update `inited_` and return it.
+  // We return it here to avoid having to read it again in the inlined code.
+  const std::string& Init() const;
+};
+
+class TaggedStringPtr {
+ public:
+  // Bit flags qualifying string properties. We can use 2 bits as
+  // ptr_ is guaranteed and enforced to be aligned on 4 byte boundaries.
+  enum Flags {
+    kArenaBit = 0x1,      // ptr is arena allocated
+    kMutableBit = 0x2,    // ptr contents are fully mutable
+    kMask = 0x3           // Bit mask
+  };
+
+  // Composed logical types
+  enum Type {
+    // Default strings are immutable and never owned.
+    kDefault = 0,
+
+    // Allocated strings are mutable and (as the name implies) owned.
+    // A heap allocated string must be deleted.
+    kAllocated = kMutableBit,
+
+    // Mutable arena strings are strings where the string instance is owned
+    // by the arena, but the string contents itself are owned by the string
+    // instance. Mutable arena string instances need to be destroyed which is
+    // typically done through a cleanup action added to the arena owning it.
+    kMutableArena = kArenaBit | kMutableBit,
+
+    // Fixed size arena strings are strings where both the string instance and
+    // the string contents are fully owned by the arena. Fixed size arena
+    // strings are a platform and c++ library specific customization. Fixed
+    // size arena strings are immutable, with the exception of custom internal
+    // updates to the content that fit inside the existing capacity.
+    // Fixed size arena strings must never be deleted or destroyed.
+    kFixedSizeArena = kArenaBit,
+  };
+
+  TaggedStringPtr() = default;
+  explicit constexpr TaggedStringPtr(ExplicitlyConstructedArenaString* ptr)
+      : ptr_(ptr) {}
+
+  // Sets the value to `p`, tagging the value as being a 'default' value.
+  // See documentation for kDefault for more info.
+  inline const std::string* SetDefault(const std::string* p) {
+    return TagAs(kDefault, const_cast<std::string*>(p));
+  }
+
+  // Sets the value to `p`, tagging the value as a heap allocated value.
+  // Allocated strings are mutable and (as the name implies) owned.
+  // `p` must not be null
+  inline std::string* SetAllocated(std::string* p) {
+    return TagAs(kAllocated, p);
+  }
+
+  // Sets the value to `p`, tagging the value as a fixed size arena string.
+  // See documentation for kFixedSizeArena for more info.
+  // `p` must not be null
+  inline std::string* SetFixedSizeArena(std::string* p) {
+    return TagAs(kFixedSizeArena, p);
+  }
+
+  // Sets the value to `p`, tagging the value as a mutable arena string.
+  // See documentation for kMutableArena for more info.
+  // `p` must not be null
+  inline std::string* SetMutableArena(std::string* p) {
+    return TagAs(kMutableArena, p);
+  }
+
+  // Returns true if the contents of the current string are fully mutable.
+  inline bool IsMutable() const { return as_int() & kMutableBit; }
+
+  // Returns true if the current string is an immutable default value.
+  inline bool IsDefault() const { return (as_int() & kMask) == kDefault; }
+
+  // If the current string is a heap-allocated mutable value, returns a pointer
+  // to it.  Returns nullptr otherwise.
+  inline std::string *GetIfAllocated() const {
+    auto allocated = as_int() ^ kAllocated;
+    if (allocated & kMask) return nullptr;
+
+    auto ptr = reinterpret_cast<std::string*>(allocated);
+    PROTOBUF_ASSUME(ptr != nullptr);
+    return ptr;
+  }
+
+  // Returns true if the current string is an arena allocated value.
+  // This means it's either a mutable or fixed size arena string.
+  inline bool IsArena() const { return as_int() & kArenaBit; }
+
+  // Returns true if the current string is a fixed size arena allocated value.
+  inline bool IsFixedSizeArena() const {
+    return (as_int() & kMask) == kFixedSizeArena;
+  }
+
+  // Returns the contained string pointer.
+  inline std::string* Get() const {
+    return reinterpret_cast<std::string*>(as_int() & ~kMask);
+  }
+
+  // Returns true if the contained pointer is null, indicating some error.
+  // The Null value is only used during parsing for temporary values.
+  // A persisted ArenaStringPtr value is never null.
+  inline bool IsNull() { return ptr_ == nullptr; }
+
+ private:
+  static inline void assert_aligned(const void* p) {
+    GOOGLE_DCHECK_EQ(reinterpret_cast<uintptr_t>(p) & kMask, 0UL);
+  }
+
+  inline std::string* TagAs(Type type, std::string* p) {
+    GOOGLE_DCHECK(p != nullptr);
+    assert_aligned(p);
+    ptr_ = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(p) | type);
+    return p;
+  }
+
+  uintptr_t as_int() const { return reinterpret_cast<uintptr_t>(ptr_); }
+  void* ptr_;
+};
+
+static_assert(std::is_trivial<TaggedStringPtr>::value,
+              "TaggedStringPtr must be trivial");
+
+// This class encapsulates a pointer to a std::string with or without arena
+// owned contents, tagged by the bottom bits of the string pointer. It is a
+// high-level wrapper that almost directly corresponds to the interface required
+// by string fields in generated code. It replaces the old std::string* pointer
+// in such cases.
+//
+// The string pointer is tagged to be either a default, externally owned value,
+// a mutable heap allocated value, or an arena allocated value. The object uses
+// a single global instance of an empty string that is used as the initial
+// default value. Fields that have empty default values directly use this global
+// default. Fields that have non empty default values are supported through
+// lazily initialized default values managed by the LazyString class.
+//
+// Generated code and reflection code both ensure that ptr_ is never null.
+// Because ArenaStringPtr is used in oneof unions, its constructor is a NOP and
+// the field is always manually initialized via method calls.
+//
+// See TaggedStringPtr for more information about the types of string values
+// being held, and the mutable and ownership invariants for each type.
+struct PROTOBUF_EXPORT ArenaStringPtr {
+  ArenaStringPtr() = default;
+  constexpr ArenaStringPtr(ExplicitlyConstructedArenaString* default_value,
+                           ConstantInitialized)
+      : tagged_ptr_(default_value) {}
+
+  // Called from generated code / reflection runtime only. Resets value to point
+  // to a default string pointer, with the semantics that this ArenaStringPtr
+  // does not own the pointed-to memory. Disregards initial value of ptr_ (so
+  // this is the *ONLY* safe method to call after construction or when
+  // reinitializing after becoming the active field in a oneof union).
+  inline void InitDefault();
+
+  // Similar to `InitDefault` except that it allows the default value to be
+  // initialized to an externally owned string. This method is called from
+  // parsing code. `str` must not be null and outlive this instance.
+  inline void InitExternal(const std::string* str);
+
+  // Called from generated code / reflection runtime only. Resets the value of
+  // this instances to the heap allocated value in `str`. `str` must not be
+  // null. Invokes `arena->Own(str)` to transfer ownership into the arena if
+  // `arena` is not null, else, `str` will be owned by ArenaStringPtr. This
+  // function should only be used to initialize a ArenaStringPtr or on an
+  // instance known to not carry any heap allocated value.
+  inline void InitAllocated(std::string* str, Arena* arena);
+
+  void Set(ConstStringParam value, Arena* arena);
+  void Set(std::string&& value, Arena* arena);
+  void Set(const char* s, Arena* arena);
+  void Set(const char* s, size_t n, Arena* arena);
+
+  void SetBytes(ConstStringParam value, Arena* arena);
+  void SetBytes(std::string&& value, Arena* arena);
+  void SetBytes(const char* s, Arena* arena);
+  void SetBytes(const void* p, size_t n, Arena* arena);
+
+  template <typename RefWrappedType>
+  void Set(std::reference_wrapper<RefWrappedType> const_string_ref,
+           ::google::protobuf::Arena* arena) {
+    Set(const_string_ref.get(), arena);
+  }
+
+  // Returns a mutable std::string reference.
+  // The version accepting a `LazyString` value is used in the generated code to
+  // initialize mutable copies for fields with a non-empty default where the
+  // default value is lazily initialized.
+  std::string* Mutable(Arena* arena);
+  std::string* Mutable(const LazyString& default_value, Arena* arena);
+
+  // Gets a mutable pointer with unspecified contents.
+  // This function is identical to Mutable(), except it is optimized for the
+  // case where the caller is not interested in the current contents. For
+  // example, if the current field is not mutable, it will re-initialize the
+  // value with an empty string rather than a (non-empty) default value.
+  // Likewise, if the current value is a fixed size arena string with contents,
+  // it will be initialized into an empty mutable arena string.
+  std::string* MutableNoCopy(Arena* arena);
+
+  // Basic accessors.
+  PROTOBUF_NDEBUG_INLINE const std::string& Get() const {
+    // Unconditionally mask away the tag.
+    return *tagged_ptr_.Get();
+  }
+
+  // Returns a pointer to the stored contents for this instance.
+  // This method is for internal debugging and tracking purposes only.
+  PROTOBUF_NDEBUG_INLINE const std::string* UnsafeGetPointer() const
+      PROTOBUF_RETURNS_NONNULL {
+    return tagged_ptr_.Get();
+  }
+
+  // Release returns a std::string* instance that is heap-allocated and is not
+  // Own()'d by any arena. If the field is not set, this returns nullptr. The
+  // caller retains ownership. Clears this field back to the default state.
+  // Used to implement release_<field>() methods on generated classes.
+  PROTOBUF_NODISCARD std::string* Release();
+
+  // Takes a std::string that is heap-allocated, and takes ownership. The
+  // std::string's destructor is registered with the arena. Used to implement
+  // set_allocated_<field> in generated classes.
+  void SetAllocated(std::string* value, Arena* arena);
+
+  // Frees storage (if not on an arena).
+  void Destroy();
+
+  // Clears content, but keeps allocated std::string, to avoid the overhead of
+  // heap operations. After this returns, the content (as seen by the user) will
+  // always be the empty std::string. Assumes that |default_value| is an empty
+  // std::string.
+  void ClearToEmpty();
+
+  // Clears content, assuming that the current value is not the empty
+  // string default.
+  void ClearNonDefaultToEmpty();
+
+  // Clears content, but keeps allocated std::string if arena != nullptr, to
+  // avoid the overhead of heap operations. After this returns, the content
+  // (as seen by the user) will always be equal to |default_value|.
+  void ClearToDefault(const LazyString& default_value, ::google::protobuf::Arena* arena);
+
+  // Swaps internal pointers. Arena-safety semantics: this is guarded by the
+  // logic in Swap()/UnsafeArenaSwap() at the message level, so this method is
+  // 'unsafe' if called directly.
+  inline PROTOBUF_NDEBUG_INLINE static void InternalSwap(ArenaStringPtr* rhs,
+                                                         Arena* rhs_arena,
+                                                         ArenaStringPtr* lhs,
+                                                         Arena* lhs_arena);
+
+  // Internal setter used only at parse time to directly set a donated string
+  // value.
+  void UnsafeSetTaggedPointer(TaggedStringPtr value) { tagged_ptr_ = value; }
+  // Generated code only! An optimization, in certain cases the generated
+  // code is certain we can obtain a std::string with no default checks and
+  // tag tests.
+  std::string* UnsafeMutablePointer() PROTOBUF_RETURNS_NONNULL;
+
+  // Returns true if this instances holds an immutable default value.
+  inline bool IsDefault() const { return tagged_ptr_.IsDefault(); }
+
+ private:
+  template <typename... Args>
+  inline std::string* NewString(Arena* arena, Args&&... args) {
+    if (arena == nullptr) {
+      auto* s = new std::string(std::forward<Args>(args)...);
+      return tagged_ptr_.SetAllocated(s);
+    } else {
+      auto* s = Arena::Create<std::string>(arena, std::forward<Args>(args)...);
+      return tagged_ptr_.SetMutableArena(s);
+    }
+  }
+
+  TaggedStringPtr tagged_ptr_;
+
+  bool IsFixedSizeArena() const { return false; }
+
+  // Swaps tagged pointer without debug hardening. This is to allow python
+  // protobuf to maintain pointer stability even in DEBUG builds.
+  inline PROTOBUF_NDEBUG_INLINE static void UnsafeShallowSwap(
+      ArenaStringPtr* rhs, ArenaStringPtr* lhs) {
+    std::swap(lhs->tagged_ptr_, rhs->tagged_ptr_);
+  }
+
+  friend class ::google::protobuf::internal::SwapFieldHelper;
+  friend class TcParser;
+
+  // Slow paths.
+
+  // MutableSlow requires that !IsString() || IsDefault
+  // Variadic to support 0 args for empty default and 1 arg for LazyString.
+  template <typename... Lazy>
+  std::string* MutableSlow(::google::protobuf::Arena* arena, const Lazy&... lazy_default);
+
+  friend class EpsCopyInputStream;
+};
+
+inline void ArenaStringPtr::InitDefault() {
+  tagged_ptr_ = TaggedStringPtr(&fixed_address_empty_string);
+}
+
+inline void ArenaStringPtr::InitExternal(const std::string* str) {
+  tagged_ptr_.SetDefault(str);
+}
+
+inline void ArenaStringPtr::InitAllocated(std::string* str, Arena* arena) {
+  if (arena != nullptr) {
+    tagged_ptr_.SetMutableArena(str);
+    arena->Own(str);
+  } else {
+    tagged_ptr_.SetAllocated(str);
+  }
+}
+
+inline void ArenaStringPtr::Set(const char* s, Arena* arena) {
+  Set(ConstStringParam{s}, arena);
+}
+
+inline void ArenaStringPtr::Set(const char* s, size_t n, Arena* arena) {
+  Set(ConstStringParam{s, n}, arena);
+}
+
+inline void ArenaStringPtr::SetBytes(ConstStringParam value, Arena* arena) {
+  Set(value, arena);
+}
+
+inline void ArenaStringPtr::SetBytes(std::string&& value, Arena* arena) {
+  Set(std::move(value), arena);
+}
+
+inline void ArenaStringPtr::SetBytes(const char* s, Arena* arena) {
+  Set(s, arena);
+}
+
+inline void ArenaStringPtr::SetBytes(const void* p, size_t n, Arena* arena) {
+  Set(ConstStringParam{static_cast<const char*>(p), n}, arena);
+}
+
+// Make sure rhs_arena allocated rhs, and lhs_arena allocated lhs.
+inline PROTOBUF_NDEBUG_INLINE void ArenaStringPtr::InternalSwap(  //
+    ArenaStringPtr* rhs, Arena* rhs_arena,                        //
+    ArenaStringPtr* lhs, Arena* lhs_arena) {
+  // Silence unused variable warnings in release buildls.
+  (void)rhs_arena;
+  (void)lhs_arena;
+  std::swap(lhs->tagged_ptr_, rhs->tagged_ptr_);
+#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+  auto force_realloc = [](ArenaStringPtr* p, Arena* arena) {
+    if (p->IsDefault()) return;
+    std::string* old_value = p->tagged_ptr_.Get();
+    std::string* new_value =
+        p->IsFixedSizeArena()
+            ? Arena::Create<std::string>(arena, *old_value)
+            : Arena::Create<std::string>(arena, std::move(*old_value));
+    if (arena == nullptr) {
+      delete old_value;
+      p->tagged_ptr_.SetAllocated(new_value);
+    } else {
+      p->tagged_ptr_.SetMutableArena(new_value);
+    }
+  };
+  // Because, at this point, tagged_ptr_ has been swapped, arena should also be
+  // swapped.
+  force_realloc(lhs, rhs_arena);
+  force_realloc(rhs, lhs_arena);
+#endif  // PROTOBUF_FORCE_COPY_IN_SWAP
+}
+
+inline void ArenaStringPtr::ClearNonDefaultToEmpty() {
+  // Unconditionally mask away the tag.
+  tagged_ptr_.Get()->clear();
+}
+
+inline std::string* ArenaStringPtr::UnsafeMutablePointer() {
+  GOOGLE_DCHECK(tagged_ptr_.IsMutable());
+  GOOGLE_DCHECK(tagged_ptr_.Get() != nullptr);
+  return tagged_ptr_.Get();
+}
+
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_ARENASTRING_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arenaz_sampler.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arenaz_sampler.h
new file mode 100644
index 0000000..b04b0cc
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/arenaz_sampler.h
@@ -0,0 +1,207 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_SRC_GOOGLE_PROTOBUF_ARENAZ_SAMPLER_H__
+#define GOOGLE_PROTOBUF_SRC_GOOGLE_PROTOBUF_ARENAZ_SAMPLER_H__
+
+#include <atomic>
+#include <cstddef>
+#include <cstdint>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+#if defined(PROTOBUF_ARENAZ_SAMPLE)
+struct ThreadSafeArenaStats;
+void RecordResetSlow(ThreadSafeArenaStats* info);
+void RecordAllocateSlow(ThreadSafeArenaStats* info, size_t requested,
+                        size_t allocated, size_t wasted);
+// Stores information about a sampled thread safe arena.  All mutations to this
+// *must* be made through `Record*` functions below.  All reads from this *must*
+// only occur in the callback to `ThreadSafeArenazSampler::Iterate`.
+struct ThreadSafeArenaStats
+    : public absl::profiling_internal::Sample<ThreadSafeArenaStats> {
+  // Constructs the object but does not fill in any fields.
+  ThreadSafeArenaStats();
+  ~ThreadSafeArenaStats();
+
+  // Puts the object into a clean state, fills in the logically `const` members,
+  // blocking for any readers that are currently sampling the object.
+  void PrepareForSampling() ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu);
+
+  // These fields are mutated by the various Record* APIs and need to be
+  // thread-safe.
+  std::atomic<int> num_allocations;
+  std::atomic<int> num_resets;
+  std::atomic<size_t> bytes_requested;
+  std::atomic<size_t> bytes_allocated;
+  std::atomic<size_t> bytes_wasted;
+  // Records the largest size an arena ever had.  Maintained across resets.
+  std::atomic<size_t> max_bytes_allocated;
+  // Bit i when set to 1 indicates that a thread with tid % 63 = i accessed the
+  // underlying arena.  The field is maintained across resets.
+  std::atomic<uint64_t> thread_ids;
+
+  // All of the fields below are set by `PrepareForSampling`, they must not
+  // be mutated in `Record*` functions.  They are logically `const` in that
+  // sense. These are guarded by init_mu, but that is not externalized to
+  // clients, who can only read them during
+  // `ThreadSafeArenazSampler::Iterate` which will hold the lock.
+  static constexpr int kMaxStackDepth = 64;
+  int32_t depth;
+  void* stack[kMaxStackDepth];
+  static void RecordAllocateStats(ThreadSafeArenaStats* info, size_t requested,
+                                  size_t allocated, size_t wasted) {
+    if (PROTOBUF_PREDICT_TRUE(info == nullptr)) return;
+    RecordAllocateSlow(info, requested, allocated, wasted);
+  }
+};
+
+ThreadSafeArenaStats* SampleSlow(int64_t* next_sample);
+void UnsampleSlow(ThreadSafeArenaStats* info);
+
+class ThreadSafeArenaStatsHandle {
+ public:
+  explicit ThreadSafeArenaStatsHandle() = default;
+  explicit ThreadSafeArenaStatsHandle(ThreadSafeArenaStats* info)
+      : info_(info) {}
+
+  ~ThreadSafeArenaStatsHandle() {
+    if (PROTOBUF_PREDICT_TRUE(info_ == nullptr)) return;
+    UnsampleSlow(info_);
+  }
+
+  ThreadSafeArenaStatsHandle(ThreadSafeArenaStatsHandle&& other) noexcept
+      : info_(absl::exchange(other.info_, nullptr)) {}
+
+  ThreadSafeArenaStatsHandle& operator=(
+      ThreadSafeArenaStatsHandle&& other) noexcept {
+    if (PROTOBUF_PREDICT_FALSE(info_ != nullptr)) {
+      UnsampleSlow(info_);
+    }
+    info_ = absl::exchange(other.info_, nullptr);
+    return *this;
+  }
+
+  void RecordReset() {
+    if (PROTOBUF_PREDICT_TRUE(info_ == nullptr)) return;
+    RecordResetSlow(info_);
+  }
+
+  ThreadSafeArenaStats* MutableStats() { return info_; }
+
+  friend void swap(ThreadSafeArenaStatsHandle& lhs,
+                   ThreadSafeArenaStatsHandle& rhs) {
+    std::swap(lhs.info_, rhs.info_);
+  }
+
+  friend class ThreadSafeArenaStatsHandlePeer;
+
+ private:
+  ThreadSafeArenaStats* info_ = nullptr;
+};
+
+using ThreadSafeArenazSampler =
+    ::absl::profiling_internal::SampleRecorder<ThreadSafeArenaStats>;
+
+extern PROTOBUF_THREAD_LOCAL int64_t global_next_sample;
+
+// Returns an RAII sampling handle that manages registration and unregistation
+// with the global sampler.
+inline ThreadSafeArenaStatsHandle Sample() {
+  if (PROTOBUF_PREDICT_TRUE(--global_next_sample > 0)) {
+    return ThreadSafeArenaStatsHandle(nullptr);
+  }
+  return ThreadSafeArenaStatsHandle(SampleSlow(&global_next_sample));
+}
+
+#else
+struct ThreadSafeArenaStats {
+  static void RecordAllocateStats(ThreadSafeArenaStats*, size_t /*requested*/,
+                                  size_t /*allocated*/, size_t /*wasted*/) {}
+};
+
+ThreadSafeArenaStats* SampleSlow(int64_t* next_sample);
+void UnsampleSlow(ThreadSafeArenaStats* info);
+
+class ThreadSafeArenaStatsHandle {
+ public:
+  explicit ThreadSafeArenaStatsHandle() = default;
+  explicit ThreadSafeArenaStatsHandle(ThreadSafeArenaStats*) {}
+
+  void RecordReset() {}
+
+  ThreadSafeArenaStats* MutableStats() { return nullptr; }
+
+  friend void swap(ThreadSafeArenaStatsHandle&, ThreadSafeArenaStatsHandle&) {}
+
+ private:
+  friend class ThreadSafeArenaStatsHandlePeer;
+};
+
+class ThreadSafeArenazSampler {
+ public:
+  void Unregister(ThreadSafeArenaStats*) {}
+  void SetMaxSamples(int32_t) {}
+};
+
+// Returns an RAII sampling handle that manages registration and unregistation
+// with the global sampler.
+inline ThreadSafeArenaStatsHandle Sample() {
+  return ThreadSafeArenaStatsHandle(nullptr);
+}
+#endif  // defined(PROTOBUF_ARENAZ_SAMPLE)
+
+// Returns a global Sampler.
+ThreadSafeArenazSampler& GlobalThreadSafeArenazSampler();
+
+// Enables or disables sampling for thread safe arenas.
+void SetThreadSafeArenazEnabled(bool enabled);
+
+// Sets the rate at which thread safe arena will be sampled.
+void SetThreadSafeArenazSampleParameter(int32_t rate);
+
+// Sets a soft max for the number of samples that will be kept.
+void SetThreadSafeArenazMaxSamples(int32_t max);
+
+// Sets the current value for when arenas should be next sampled.
+void SetThreadSafeArenazGlobalNextSample(int64_t next_sample);
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_SRC_PROTOBUF_ARENAZ_SAMPLER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/compiler/importer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/compiler/importer.h
new file mode 100644
index 0000000..2fb88b9
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/compiler/importer.h
@@ -0,0 +1,338 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This file is the public interface to the .proto file parser.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__
+#define GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <google/protobuf/compiler/parser.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor_database.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+namespace io {
+class ZeroCopyInputStream;
+}
+
+namespace compiler {
+
+// Defined in this file.
+class Importer;
+class MultiFileErrorCollector;
+class SourceTree;
+class DiskSourceTree;
+
+// TODO(kenton):  Move all SourceTree stuff to a separate file?
+
+// An implementation of DescriptorDatabase which loads files from a SourceTree
+// and parses them.
+//
+// Note:  This class is not thread-safe since it maintains a table of source
+//   code locations for error reporting.  However, when a DescriptorPool wraps
+//   a DescriptorDatabase, it uses mutex locking to make sure only one method
+//   of the database is called at a time, even if the DescriptorPool is used
+//   from multiple threads.  Therefore, there is only a problem if you create
+//   multiple DescriptorPools wrapping the same SourceTreeDescriptorDatabase
+//   and use them from multiple threads.
+//
+// Note:  This class does not implement FindFileContainingSymbol() or
+//   FindFileContainingExtension(); these will always return false.
+class PROTOBUF_EXPORT SourceTreeDescriptorDatabase : public DescriptorDatabase {
+ public:
+  SourceTreeDescriptorDatabase(SourceTree* source_tree);
+
+  // If non-NULL, fallback_database will be checked if a file doesn't exist in
+  // the specified source_tree.
+  SourceTreeDescriptorDatabase(SourceTree* source_tree,
+                               DescriptorDatabase* fallback_database);
+  ~SourceTreeDescriptorDatabase() override;
+
+  // Instructs the SourceTreeDescriptorDatabase to report any parse errors
+  // to the given MultiFileErrorCollector.  This should be called before
+  // parsing.  error_collector must remain valid until either this method
+  // is called again or the SourceTreeDescriptorDatabase is destroyed.
+  void RecordErrorsTo(MultiFileErrorCollector* error_collector) {
+    error_collector_ = error_collector;
+  }
+
+  // Gets a DescriptorPool::ErrorCollector which records errors to the
+  // MultiFileErrorCollector specified with RecordErrorsTo().  This collector
+  // has the ability to determine exact line and column numbers of errors
+  // from the information given to it by the DescriptorPool.
+  DescriptorPool::ErrorCollector* GetValidationErrorCollector() {
+    using_validation_error_collector_ = true;
+    return &validation_error_collector_;
+  }
+
+  // implements DescriptorDatabase -----------------------------------
+  bool FindFileByName(const std::string& filename,
+                      FileDescriptorProto* output) override;
+  bool FindFileContainingSymbol(const std::string& symbol_name,
+                                FileDescriptorProto* output) override;
+  bool FindFileContainingExtension(const std::string& containing_type,
+                                   int field_number,
+                                   FileDescriptorProto* output) override;
+
+ private:
+  class SingleFileErrorCollector;
+
+  SourceTree* source_tree_;
+  DescriptorDatabase* fallback_database_;
+  MultiFileErrorCollector* error_collector_;
+
+  class PROTOBUF_EXPORT ValidationErrorCollector
+      : public DescriptorPool::ErrorCollector {
+   public:
+    ValidationErrorCollector(SourceTreeDescriptorDatabase* owner);
+    ~ValidationErrorCollector() override;
+
+    // implements ErrorCollector ---------------------------------------
+    void AddError(const std::string& filename, const std::string& element_name,
+                  const Message* descriptor, ErrorLocation location,
+                  const std::string& message) override;
+
+    void AddWarning(const std::string& filename,
+                    const std::string& element_name, const Message* descriptor,
+                    ErrorLocation location,
+                    const std::string& message) override;
+
+   private:
+    SourceTreeDescriptorDatabase* owner_;
+  };
+  friend class ValidationErrorCollector;
+
+  bool using_validation_error_collector_;
+  SourceLocationTable source_locations_;
+  ValidationErrorCollector validation_error_collector_;
+};
+
+// Simple interface for parsing .proto files.  This wraps the process
+// of opening the file, parsing it with a Parser, recursively parsing all its
+// imports, and then cross-linking the results to produce a FileDescriptor.
+//
+// This is really just a thin wrapper around SourceTreeDescriptorDatabase.
+// You may find that SourceTreeDescriptorDatabase is more flexible.
+//
+// TODO(kenton):  I feel like this class is not well-named.
+class PROTOBUF_EXPORT Importer {
+ public:
+  Importer(SourceTree* source_tree, MultiFileErrorCollector* error_collector);
+  ~Importer();
+
+  // Import the given file and build a FileDescriptor representing it.  If
+  // the file is already in the DescriptorPool, the existing FileDescriptor
+  // will be returned.  The FileDescriptor is property of the DescriptorPool,
+  // and will remain valid until it is destroyed.  If any errors occur, they
+  // will be reported using the error collector and Import() will return NULL.
+  //
+  // A particular Importer object will only report errors for a particular
+  // file once.  All future attempts to import the same file will return NULL
+  // without reporting any errors.  The idea is that you might want to import
+  // a lot of files without seeing the same errors over and over again.  If
+  // you want to see errors for the same files repeatedly, you can use a
+  // separate Importer object to import each one (but use the same
+  // DescriptorPool so that they can be cross-linked).
+  const FileDescriptor* Import(const std::string& filename);
+
+  // The DescriptorPool in which all imported FileDescriptors and their
+  // contents are stored.
+  inline const DescriptorPool* pool() const { return &pool_; }
+
+  void AddUnusedImportTrackFile(const std::string& file_name,
+                                bool is_error = false);
+  void ClearUnusedImportTrackFiles();
+
+
+ private:
+  SourceTreeDescriptorDatabase database_;
+  DescriptorPool pool_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Importer);
+};
+
+// If the importer encounters problems while trying to import the proto files,
+// it reports them to a MultiFileErrorCollector.
+class PROTOBUF_EXPORT MultiFileErrorCollector {
+ public:
+  inline MultiFileErrorCollector() {}
+  virtual ~MultiFileErrorCollector();
+
+  // Line and column numbers are zero-based.  A line number of -1 indicates
+  // an error with the entire file (e.g. "not found").
+  virtual void AddError(const std::string& filename, int line, int column,
+                        const std::string& message) = 0;
+
+  virtual void AddWarning(const std::string& /* filename */, int /* line */,
+                          int /* column */, const std::string& /* message */) {}
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultiFileErrorCollector);
+};
+
+// Abstract interface which represents a directory tree containing proto files.
+// Used by the default implementation of Importer to resolve import statements
+// Most users will probably want to use the DiskSourceTree implementation,
+// below.
+class PROTOBUF_EXPORT SourceTree {
+ public:
+  inline SourceTree() {}
+  virtual ~SourceTree();
+
+  // Open the given file and return a stream that reads it, or NULL if not
+  // found.  The caller takes ownership of the returned object.  The filename
+  // must be a path relative to the root of the source tree and must not
+  // contain "." or ".." components.
+  virtual io::ZeroCopyInputStream* Open(const std::string& filename) = 0;
+
+  // If Open() returns NULL, calling this method immediately will return an
+  // description of the error.
+  // Subclasses should implement this method and return a meaningful value for
+  // better error reporting.
+  // TODO(xiaofeng): change this to a pure virtual function.
+  virtual std::string GetLastErrorMessage();
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SourceTree);
+};
+
+// An implementation of SourceTree which loads files from locations on disk.
+// Multiple mappings can be set up to map locations in the DiskSourceTree to
+// locations in the physical filesystem.
+class PROTOBUF_EXPORT DiskSourceTree : public SourceTree {
+ public:
+  DiskSourceTree();
+  ~DiskSourceTree() override;
+
+  // Map a path on disk to a location in the SourceTree.  The path may be
+  // either a file or a directory.  If it is a directory, the entire tree
+  // under it will be mapped to the given virtual location.  To map a directory
+  // to the root of the source tree, pass an empty string for virtual_path.
+  //
+  // If multiple mapped paths apply when opening a file, they will be searched
+  // in order.  For example, if you do:
+  //   MapPath("bar", "foo/bar");
+  //   MapPath("", "baz");
+  // and then you do:
+  //   Open("bar/qux");
+  // the DiskSourceTree will first try to open foo/bar/qux, then baz/bar/qux,
+  // returning the first one that opens successfully.
+  //
+  // disk_path may be an absolute path or relative to the current directory,
+  // just like a path you'd pass to open().
+  void MapPath(const std::string& virtual_path, const std::string& disk_path);
+
+  // Return type for DiskFileToVirtualFile().
+  enum DiskFileToVirtualFileResult {
+    SUCCESS,
+    SHADOWED,
+    CANNOT_OPEN,
+    NO_MAPPING
+  };
+
+  // Given a path to a file on disk, find a virtual path mapping to that
+  // file.  The first mapping created with MapPath() whose disk_path contains
+  // the filename is used.  However, that virtual path may not actually be
+  // usable to open the given file.  Possible return values are:
+  // * SUCCESS: The mapping was found.  *virtual_file is filled in so that
+  //   calling Open(*virtual_file) will open the file named by disk_file.
+  // * SHADOWED: A mapping was found, but using Open() to open this virtual
+  //   path will end up returning some different file.  This is because some
+  //   other mapping with a higher precedence also matches this virtual path
+  //   and maps it to a different file that exists on disk.  *virtual_file
+  //   is filled in as it would be in the SUCCESS case.  *shadowing_disk_file
+  //   is filled in with the disk path of the file which would be opened if
+  //   you were to call Open(*virtual_file).
+  // * CANNOT_OPEN: The mapping was found and was not shadowed, but the
+  //   file specified cannot be opened.  When this value is returned,
+  //   errno will indicate the reason the file cannot be opened.  *virtual_file
+  //   will be set to the virtual path as in the SUCCESS case, even though
+  //   it is not useful.
+  // * NO_MAPPING: Indicates that no mapping was found which contains this
+  //   file.
+  DiskFileToVirtualFileResult DiskFileToVirtualFile(
+      const std::string& disk_file, std::string* virtual_file,
+      std::string* shadowing_disk_file);
+
+  // Given a virtual path, find the path to the file on disk.
+  // Return true and update disk_file with the on-disk path if the file exists.
+  // Return false and leave disk_file untouched if the file doesn't exist.
+  bool VirtualFileToDiskFile(const std::string& virtual_file,
+                             std::string* disk_file);
+
+  // implements SourceTree -------------------------------------------
+  io::ZeroCopyInputStream* Open(const std::string& filename) override;
+
+  std::string GetLastErrorMessage() override;
+
+ private:
+  struct Mapping {
+    std::string virtual_path;
+    std::string disk_path;
+
+    inline Mapping(const std::string& virtual_path_param,
+                   const std::string& disk_path_param)
+        : virtual_path(virtual_path_param), disk_path(disk_path_param) {}
+  };
+  std::vector<Mapping> mappings_;
+  std::string last_error_message_;
+
+  // Like Open(), but returns the on-disk path in disk_file if disk_file is
+  // non-NULL and the file could be successfully opened.
+  io::ZeroCopyInputStream* OpenVirtualFile(const std::string& virtual_file,
+                                           std::string* disk_file);
+
+  // Like Open() but given the actual on-disk path.
+  io::ZeroCopyInputStream* OpenDiskFile(const std::string& filename);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DiskSourceTree);
+};
+
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/compiler/parser.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/compiler/parser.h
new file mode 100644
index 0000000..d4eb763
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/compiler/parser.h
@@ -0,0 +1,602 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Implements parsing of .proto files to FileDescriptorProtos.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_PARSER_H__
+#define GOOGLE_PROTOBUF_COMPILER_PARSER_H__
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <utility>
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/io/tokenizer.h>
+#include <google/protobuf/repeated_field.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+class Message;
+
+namespace compiler {
+
+// Defined in this file.
+class Parser;
+class SourceLocationTable;
+
+// Implements parsing of protocol definitions (such as .proto files).
+//
+// Note that most users will be more interested in the Importer class.
+// Parser is a lower-level class which simply converts a single .proto file
+// to a FileDescriptorProto.  It does not resolve import directives or perform
+// many other kinds of validation needed to construct a complete
+// FileDescriptor.
+class PROTOBUF_EXPORT Parser {
+ public:
+  Parser();
+  ~Parser();
+
+  // Parse the entire input and construct a FileDescriptorProto representing
+  // it.  Returns true if no errors occurred, false otherwise.
+  bool Parse(io::Tokenizer* input, FileDescriptorProto* file);
+
+  // Optional features:
+
+  // DEPRECATED:  New code should use the SourceCodeInfo embedded in the
+  //   FileDescriptorProto.
+  //
+  // Requests that locations of certain definitions be recorded to the given
+  // SourceLocationTable while parsing.  This can be used to look up exact line
+  // and column numbers for errors reported by DescriptorPool during validation.
+  // Set to NULL (the default) to discard source location information.
+  void RecordSourceLocationsTo(SourceLocationTable* location_table) {
+    source_location_table_ = location_table;
+  }
+
+  // Requests that errors be recorded to the given ErrorCollector while
+  // parsing.  Set to NULL (the default) to discard error messages.
+  void RecordErrorsTo(io::ErrorCollector* error_collector) {
+    error_collector_ = error_collector;
+  }
+
+  // Returns the identifier used in the "syntax = " declaration, if one was
+  // seen during the last call to Parse(), or the empty string otherwise.
+  const std::string& GetSyntaxIdentifier() { return syntax_identifier_; }
+
+  // If set true, input files will be required to begin with a syntax
+  // identifier.  Otherwise, files may omit this.  If a syntax identifier
+  // is provided, it must be 'syntax = "proto2";' and must appear at the
+  // top of this file regardless of whether or not it was required.
+  void SetRequireSyntaxIdentifier(bool value) {
+    require_syntax_identifier_ = value;
+  }
+
+  // Call SetStopAfterSyntaxIdentifier(true) to tell the parser to stop
+  // parsing as soon as it has seen the syntax identifier, or lack thereof.
+  // This is useful for quickly identifying the syntax of the file without
+  // parsing the whole thing.  If this is enabled, no error will be recorded
+  // if the syntax identifier is something other than "proto2" (since
+  // presumably the caller intends to deal with that), but other kinds of
+  // errors (e.g. parse errors) will still be reported.  When this is enabled,
+  // you may pass a NULL FileDescriptorProto to Parse().
+  void SetStopAfterSyntaxIdentifier(bool value) {
+    stop_after_syntax_identifier_ = value;
+  }
+
+ private:
+  class LocationRecorder;
+  struct MapField;
+
+  // =================================================================
+  // Error recovery helpers
+
+  // Consume the rest of the current statement.  This consumes tokens
+  // until it sees one of:
+  //   ';'  Consumes the token and returns.
+  //   '{'  Consumes the brace then calls SkipRestOfBlock().
+  //   '}'  Returns without consuming.
+  //   EOF  Returns (can't consume).
+  // The Parser often calls SkipStatement() after encountering a syntax
+  // error.  This allows it to go on parsing the following lines, allowing
+  // it to report more than just one error in the file.
+  void SkipStatement();
+
+  // Consume the rest of the current block, including nested blocks,
+  // ending after the closing '}' is encountered and consumed, or at EOF.
+  void SkipRestOfBlock();
+
+  // -----------------------------------------------------------------
+  // Single-token consuming helpers
+  //
+  // These make parsing code more readable.
+
+  // True if the current token is TYPE_END.
+  inline bool AtEnd();
+
+  // True if the next token matches the given text.
+  inline bool LookingAt(const char* text);
+  // True if the next token is of the given type.
+  inline bool LookingAtType(io::Tokenizer::TokenType token_type);
+
+  // If the next token exactly matches the text given, consume it and return
+  // true.  Otherwise, return false without logging an error.
+  bool TryConsume(const char* text);
+
+  // These attempt to read some kind of token from the input.  If successful,
+  // they return true.  Otherwise they return false and add the given error
+  // to the error list.
+
+  // Consume a token with the exact text given.
+  bool Consume(const char* text, const char* error);
+  // Same as above, but automatically generates the error "Expected \"text\".",
+  // where "text" is the expected token text.
+  bool Consume(const char* text);
+  // Consume a token of type IDENTIFIER and store its text in "output".
+  bool ConsumeIdentifier(std::string* output, const char* error);
+  // Consume an integer and store its value in "output".
+  bool ConsumeInteger(int* output, const char* error);
+  // Consume a signed integer and store its value in "output".
+  bool ConsumeSignedInteger(int* output, const char* error);
+  // Consume a 64-bit integer and store its value in "output".  If the value
+  // is greater than max_value, an error will be reported.
+  bool ConsumeInteger64(uint64_t max_value, uint64_t* output,
+                        const char* error);
+  // Consume a number and store its value in "output".  This will accept
+  // tokens of either INTEGER or FLOAT type.
+  bool ConsumeNumber(double* output, const char* error);
+  // Consume a string literal and store its (unescaped) value in "output".
+  bool ConsumeString(std::string* output, const char* error);
+
+  // Consume a token representing the end of the statement.  Comments between
+  // this token and the next will be harvested for documentation.  The given
+  // LocationRecorder should refer to the declaration that was just parsed;
+  // it will be populated with these comments.
+  //
+  // TODO(kenton):  The LocationRecorder is const because historically locations
+  //   have been passed around by const reference, for no particularly good
+  //   reason.  We should probably go through and change them all to mutable
+  //   pointer to make this more intuitive.
+  bool TryConsumeEndOfDeclaration(const char* text,
+                                  const LocationRecorder* location);
+  bool TryConsumeEndOfDeclarationFinishScope(const char* text,
+                                             const LocationRecorder* location);
+
+  bool ConsumeEndOfDeclaration(const char* text,
+                               const LocationRecorder* location);
+
+  // -----------------------------------------------------------------
+  // Error logging helpers
+
+  // Invokes error_collector_->AddError(), if error_collector_ is not NULL.
+  void AddError(int line, int column, const std::string& error);
+
+  // Invokes error_collector_->AddError() with the line and column number
+  // of the current token.
+  void AddError(const std::string& error);
+
+  // Invokes error_collector_->AddWarning() with the line and column number
+  // of the current token.
+  void AddWarning(const std::string& warning);
+
+  // Records a location in the SourceCodeInfo.location table (see
+  // descriptor.proto).  We use RAII to ensure that the start and end locations
+  // are recorded -- the constructor records the start location and the
+  // destructor records the end location.  Since the parser is
+  // recursive-descent, this works out beautifully.
+  class PROTOBUF_EXPORT LocationRecorder {
+   public:
+    // Construct the file's "root" location.
+    LocationRecorder(Parser* parser);
+
+    // Construct a location that represents a declaration nested within the
+    // given parent.  E.g. a field's location is nested within the location
+    // for a message type.  The parent's path will be copied, so you should
+    // call AddPath() only to add the path components leading from the parent
+    // to the child (as opposed to leading from the root to the child).
+    LocationRecorder(const LocationRecorder& parent);
+
+    // Convenience constructors that call AddPath() one or two times.
+    LocationRecorder(const LocationRecorder& parent, int path1);
+    LocationRecorder(const LocationRecorder& parent, int path1, int path2);
+
+    // Creates a recorder that generates locations into given source code info.
+    LocationRecorder(const LocationRecorder& parent, int path1,
+                     SourceCodeInfo* source_code_info);
+
+    ~LocationRecorder();
+
+    // Add a path component.  See SourceCodeInfo.Location.path in
+    // descriptor.proto.
+    void AddPath(int path_component);
+
+    // By default the location is considered to start at the current token at
+    // the time the LocationRecorder is created.  StartAt() sets the start
+    // location to the given token instead.
+    void StartAt(const io::Tokenizer::Token& token);
+
+    // Start at the same location as some other LocationRecorder.
+    void StartAt(const LocationRecorder& other);
+
+    // By default the location is considered to end at the previous token at
+    // the time the LocationRecorder is destroyed.  EndAt() sets the end
+    // location to the given token instead.
+    void EndAt(const io::Tokenizer::Token& token);
+
+    // Records the start point of this location to the SourceLocationTable that
+    // was passed to RecordSourceLocationsTo(), if any.  SourceLocationTable
+    // is an older way of keeping track of source locations which is still
+    // used in some places.
+    void RecordLegacyLocation(
+        const Message* descriptor,
+        DescriptorPool::ErrorCollector::ErrorLocation location);
+    void RecordLegacyImportLocation(const Message* descriptor,
+                                    const std::string& name);
+
+    // Returns the number of path components in the recorder's current location.
+    int CurrentPathSize() const;
+
+    // Attaches leading and trailing comments to the location.  The two strings
+    // will be swapped into place, so after this is called *leading and
+    // *trailing will be empty.
+    //
+    // TODO(kenton):  See comment on TryConsumeEndOfDeclaration(), above, for
+    //   why this is const.
+    void AttachComments(std::string* leading, std::string* trailing,
+                        std::vector<std::string>* detached_comments) const;
+
+   private:
+    Parser* parser_;
+    SourceCodeInfo* source_code_info_;
+    SourceCodeInfo::Location* location_;
+
+    void Init(const LocationRecorder& parent, SourceCodeInfo* source_code_info);
+  };
+
+  // =================================================================
+  // Parsers for various language constructs
+
+  // Parses the "syntax = \"proto2\";" line at the top of the file.  Returns
+  // false if it failed to parse or if the syntax identifier was not
+  // recognized.
+  bool ParseSyntaxIdentifier(const LocationRecorder& parent);
+
+  // These methods parse various individual bits of code.  They return
+  // false if they completely fail to parse the construct.  In this case,
+  // it is probably necessary to skip the rest of the statement to recover.
+  // However, if these methods return true, it does NOT mean that there
+  // were no errors; only that there were no *syntax* errors.  For instance,
+  // if a service method is defined using proper syntax but uses a primitive
+  // type as its input or output, ParseMethodField() still returns true
+  // and only reports the error by calling AddError().  In practice, this
+  // makes logic much simpler for the caller.
+
+  // Parse a top-level message, enum, service, etc.
+  bool ParseTopLevelStatement(FileDescriptorProto* file,
+                              const LocationRecorder& root_location);
+
+  // Parse various language high-level language construrcts.
+  bool ParseMessageDefinition(DescriptorProto* message,
+                              const LocationRecorder& message_location,
+                              const FileDescriptorProto* containing_file);
+  bool ParseEnumDefinition(EnumDescriptorProto* enum_type,
+                           const LocationRecorder& enum_location,
+                           const FileDescriptorProto* containing_file);
+  bool ParseServiceDefinition(ServiceDescriptorProto* service,
+                              const LocationRecorder& service_location,
+                              const FileDescriptorProto* containing_file);
+  bool ParsePackage(FileDescriptorProto* file,
+                    const LocationRecorder& root_location,
+                    const FileDescriptorProto* containing_file);
+  bool ParseImport(RepeatedPtrField<std::string>* dependency,
+                   RepeatedField<int32_t>* public_dependency,
+                   RepeatedField<int32_t>* weak_dependency,
+                   const LocationRecorder& root_location,
+                   const FileDescriptorProto* containing_file);
+
+  // These methods parse the contents of a message, enum, or service type and
+  // add them to the given object.  They consume the entire block including
+  // the beginning and ending brace.
+  bool ParseMessageBlock(DescriptorProto* message,
+                         const LocationRecorder& message_location,
+                         const FileDescriptorProto* containing_file);
+  bool ParseEnumBlock(EnumDescriptorProto* enum_type,
+                      const LocationRecorder& enum_location,
+                      const FileDescriptorProto* containing_file);
+  bool ParseServiceBlock(ServiceDescriptorProto* service,
+                         const LocationRecorder& service_location,
+                         const FileDescriptorProto* containing_file);
+
+  // Parse one statement within a message, enum, or service block, including
+  // final semicolon.
+  bool ParseMessageStatement(DescriptorProto* message,
+                             const LocationRecorder& message_location,
+                             const FileDescriptorProto* containing_file);
+  bool ParseEnumStatement(EnumDescriptorProto* message,
+                          const LocationRecorder& enum_location,
+                          const FileDescriptorProto* containing_file);
+  bool ParseServiceStatement(ServiceDescriptorProto* message,
+                             const LocationRecorder& service_location,
+                             const FileDescriptorProto* containing_file);
+
+  // Parse a field of a message.  If the field is a group, its type will be
+  // added to "messages".
+  //
+  // parent_location and location_field_number_for_nested_type are needed when
+  // parsing groups -- we need to generate a nested message type within the
+  // parent and record its location accordingly.  Since the parent could be
+  // either a FileDescriptorProto or a DescriptorProto, we must pass in the
+  // correct field number to use.
+  bool ParseMessageField(FieldDescriptorProto* field,
+                         RepeatedPtrField<DescriptorProto>* messages,
+                         const LocationRecorder& parent_location,
+                         int location_field_number_for_nested_type,
+                         const LocationRecorder& field_location,
+                         const FileDescriptorProto* containing_file);
+
+  // Like ParseMessageField() but expects the label has already been filled in
+  // by the caller.
+  bool ParseMessageFieldNoLabel(FieldDescriptorProto* field,
+                                RepeatedPtrField<DescriptorProto>* messages,
+                                const LocationRecorder& parent_location,
+                                int location_field_number_for_nested_type,
+                                const LocationRecorder& field_location,
+                                const FileDescriptorProto* containing_file);
+
+  bool ParseMapType(MapField* map_field, FieldDescriptorProto* field,
+                    LocationRecorder& type_name_location);
+
+  // Parse an "extensions" declaration.
+  bool ParseExtensions(DescriptorProto* message,
+                       const LocationRecorder& extensions_location,
+                       const FileDescriptorProto* containing_file);
+
+  // Parse a "reserved" declaration.
+  bool ParseReserved(DescriptorProto* message,
+                     const LocationRecorder& message_location);
+  bool ParseReservedNames(DescriptorProto* message,
+                          const LocationRecorder& parent_location);
+  bool ParseReservedNumbers(DescriptorProto* message,
+                            const LocationRecorder& parent_location);
+  bool ParseReserved(EnumDescriptorProto* message,
+                     const LocationRecorder& message_location);
+  bool ParseReservedNames(EnumDescriptorProto* message,
+                          const LocationRecorder& parent_location);
+  bool ParseReservedNumbers(EnumDescriptorProto* message,
+                            const LocationRecorder& parent_location);
+
+  // Parse an "extend" declaration.  (See also comments for
+  // ParseMessageField().)
+  bool ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
+                   RepeatedPtrField<DescriptorProto>* messages,
+                   const LocationRecorder& parent_location,
+                   int location_field_number_for_nested_type,
+                   const LocationRecorder& extend_location,
+                   const FileDescriptorProto* containing_file);
+
+  // Parse a "oneof" declaration.  The caller is responsible for setting
+  // oneof_decl->label() since it will have had to parse the label before it
+  // knew it was parsing a oneof.
+  bool ParseOneof(OneofDescriptorProto* oneof_decl,
+                  DescriptorProto* containing_type, int oneof_index,
+                  const LocationRecorder& oneof_location,
+                  const LocationRecorder& containing_type_location,
+                  const FileDescriptorProto* containing_file);
+
+  // Parse a single enum value within an enum block.
+  bool ParseEnumConstant(EnumValueDescriptorProto* enum_value,
+                         const LocationRecorder& enum_value_location,
+                         const FileDescriptorProto* containing_file);
+
+  // Parse enum constant options, i.e. the list in square brackets at the end
+  // of the enum constant value definition.
+  bool ParseEnumConstantOptions(EnumValueDescriptorProto* value,
+                                const LocationRecorder& enum_value_location,
+                                const FileDescriptorProto* containing_file);
+
+  // Parse a single method within a service definition.
+  bool ParseServiceMethod(MethodDescriptorProto* method,
+                          const LocationRecorder& method_location,
+                          const FileDescriptorProto* containing_file);
+
+  // Parse options of a single method or stream.
+  bool ParseMethodOptions(const LocationRecorder& parent_location,
+                          const FileDescriptorProto* containing_file,
+                          const int optionsFieldNumber,
+                          Message* mutable_options);
+
+  // Parse "required", "optional", or "repeated" and fill in "label"
+  // with the value. Returns true if such a label is consumed.
+  bool ParseLabel(FieldDescriptorProto::Label* label,
+                  const LocationRecorder& field_location);
+
+  // Parse a type name and fill in "type" (if it is a primitive) or
+  // "type_name" (if it is not) with the type parsed.
+  bool ParseType(FieldDescriptorProto::Type* type, std::string* type_name);
+  // Parse a user-defined type and fill in "type_name" with the name.
+  // If a primitive type is named, it is treated as an error.
+  bool ParseUserDefinedType(std::string* type_name);
+
+  // Parses field options, i.e. the stuff in square brackets at the end
+  // of a field definition.  Also parses default value.
+  bool ParseFieldOptions(FieldDescriptorProto* field,
+                         const LocationRecorder& field_location,
+                         const FileDescriptorProto* containing_file);
+
+  // Parse the "default" option.  This needs special handling because its
+  // type is the field's type.
+  bool ParseDefaultAssignment(FieldDescriptorProto* field,
+                              const LocationRecorder& field_location,
+                              const FileDescriptorProto* containing_file);
+
+  bool ParseJsonName(FieldDescriptorProto* field,
+                     const LocationRecorder& field_location,
+                     const FileDescriptorProto* containing_file);
+
+  enum OptionStyle {
+    OPTION_ASSIGNMENT,  // just "name = value"
+    OPTION_STATEMENT    // "option name = value;"
+  };
+
+  // Parse a single option name/value pair, e.g. "ctype = CORD".  The name
+  // identifies a field of the given Message, and the value of that field
+  // is set to the parsed value.
+  bool ParseOption(Message* options, const LocationRecorder& options_location,
+                   const FileDescriptorProto* containing_file,
+                   OptionStyle style);
+
+  // Parses a single part of a multipart option name. A multipart name consists
+  // of names separated by dots. Each name is either an identifier or a series
+  // of identifiers separated by dots and enclosed in parentheses. E.g.,
+  // "foo.(bar.baz).moo".
+  bool ParseOptionNamePart(UninterpretedOption* uninterpreted_option,
+                           const LocationRecorder& part_location,
+                           const FileDescriptorProto* containing_file);
+
+  // Parses a string surrounded by balanced braces.  Strips off the outer
+  // braces and stores the enclosed string in *value.
+  // E.g.,
+  //     { foo }                     *value gets 'foo'
+  //     { foo { bar: box } }        *value gets 'foo { bar: box }'
+  //     {}                          *value gets ''
+  //
+  // REQUIRES: LookingAt("{")
+  // When finished successfully, we are looking at the first token past
+  // the ending brace.
+  bool ParseUninterpretedBlock(std::string* value);
+
+  struct MapField {
+    // Whether the field is a map field.
+    bool is_map_field;
+    // The types of the key and value if they are primitive types.
+    FieldDescriptorProto::Type key_type;
+    FieldDescriptorProto::Type value_type;
+    // Or the type names string if the types are customized types.
+    std::string key_type_name;
+    std::string value_type_name;
+
+    MapField() : is_map_field(false) {}
+  };
+  // Desugar the map syntax to generate a nested map entry message.
+  void GenerateMapEntry(const MapField& map_field, FieldDescriptorProto* field,
+                        RepeatedPtrField<DescriptorProto>* messages);
+
+  // Whether fields without label default to optional fields.
+  bool DefaultToOptionalFields() const {
+    return syntax_identifier_ == "proto3";
+  }
+
+  bool ValidateEnum(const EnumDescriptorProto* proto);
+
+  // =================================================================
+
+  io::Tokenizer* input_;
+  io::ErrorCollector* error_collector_;
+  SourceCodeInfo* source_code_info_;
+  SourceLocationTable* source_location_table_;  // legacy
+  bool had_errors_;
+  bool require_syntax_identifier_;
+  bool stop_after_syntax_identifier_;
+  std::string syntax_identifier_;
+
+  // Leading doc comments for the next declaration.  These are not complete
+  // yet; use ConsumeEndOfDeclaration() to get the complete comments.
+  std::string upcoming_doc_comments_;
+
+  // Detached comments are not connected to any syntax entities. Elements in
+  // this vector are paragraphs of comments separated by empty lines. The
+  // detached comments will be put into the leading_detached_comments field for
+  // the next element (See SourceCodeInfo.Location in descriptor.proto), when
+  // ConsumeEndOfDeclaration() is called.
+  std::vector<std::string> upcoming_detached_comments_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Parser);
+};
+
+// A table mapping (descriptor, ErrorLocation) pairs -- as reported by
+// DescriptorPool when validating descriptors -- to line and column numbers
+// within the original source code.
+//
+// This is semi-obsolete:  FileDescriptorProto.source_code_info now contains
+// far more complete information about source locations.  However, as of this
+// writing you still need to use SourceLocationTable when integrating with
+// DescriptorPool.
+class PROTOBUF_EXPORT SourceLocationTable {
+ public:
+  SourceLocationTable();
+  ~SourceLocationTable();
+
+  // Finds the precise location of the given error and fills in *line and
+  // *column with the line and column numbers.  If not found, sets *line to
+  // -1 and *column to 0 (since line = -1 is used to mean "error has no exact
+  // location" in the ErrorCollector interface).  Returns true if found, false
+  // otherwise.
+  bool Find(const Message* descriptor,
+            DescriptorPool::ErrorCollector::ErrorLocation location, int* line,
+            int* column) const;
+  bool FindImport(const Message* descriptor, const std::string& name, int* line,
+                  int* column) const;
+
+  // Adds a location to the table.
+  void Add(const Message* descriptor,
+           DescriptorPool::ErrorCollector::ErrorLocation location, int line,
+           int column);
+  void AddImport(const Message* descriptor, const std::string& name, int line,
+                 int column);
+
+  // Clears the contents of the table.
+  void Clear();
+
+ private:
+  typedef std::map<
+      std::pair<const Message*, DescriptorPool::ErrorCollector::ErrorLocation>,
+      std::pair<int, int> >
+      LocationMap;
+  LocationMap location_map_;
+  std::map<std::pair<const Message*, std::string>, std::pair<int, int> >
+      import_location_map_;
+};
+
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_COMPILER_PARSER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/descriptor.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/descriptor.h
new file mode 100644
index 0000000..bee3e32
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/descriptor.h
@@ -0,0 +1,2448 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This file contains classes which describe a type of protocol message.
+// You can use a message's descriptor to learn at runtime what fields
+// it contains and what the types of those fields are.  The Message
+// interface also allows you to dynamically access and modify individual
+// fields by passing the FieldDescriptor of the field you are interested
+// in.
+//
+// Most users will not care about descriptors, because they will write
+// code specific to certain protocol types and will simply use the classes
+// generated by the protocol compiler directly.  Advanced users who want
+// to operate on arbitrary types (not known at compile time) may want to
+// read descriptors in order to learn about the contents of a message.
+// A very small number of users will want to construct their own
+// Descriptors, either because they are implementing Message manually or
+// because they are writing something like the protocol compiler.
+//
+// For an example of how you might use descriptors, see the code example
+// at the top of message.h.
+
+#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_H__
+#define GOOGLE_PROTOBUF_DESCRIPTOR_H__
+
+
+#include <atomic>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/mutex.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/port.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+// TYPE_BOOL is defined in the MacOS's ConditionalMacros.h.
+#ifdef TYPE_BOOL
+#undef TYPE_BOOL
+#endif  // TYPE_BOOL
+
+#ifdef SWIG
+#define PROTOBUF_EXPORT
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+
+namespace google {
+namespace protobuf {
+
+// Defined in this file.
+class Descriptor;
+class FieldDescriptor;
+class OneofDescriptor;
+class EnumDescriptor;
+class EnumValueDescriptor;
+class ServiceDescriptor;
+class MethodDescriptor;
+class FileDescriptor;
+class DescriptorDatabase;
+class DescriptorPool;
+
+// Defined in descriptor.proto
+class DescriptorProto;
+class DescriptorProto_ExtensionRange;
+class FieldDescriptorProto;
+class OneofDescriptorProto;
+class EnumDescriptorProto;
+class EnumValueDescriptorProto;
+class ServiceDescriptorProto;
+class MethodDescriptorProto;
+class FileDescriptorProto;
+class MessageOptions;
+class FieldOptions;
+class OneofOptions;
+class EnumOptions;
+class EnumValueOptions;
+class ExtensionRangeOptions;
+class ServiceOptions;
+class MethodOptions;
+class FileOptions;
+class UninterpretedOption;
+class SourceCodeInfo;
+
+// Defined in message.h
+class Message;
+class Reflection;
+
+// Defined in descriptor.cc
+class DescriptorBuilder;
+class FileDescriptorTables;
+class Symbol;
+
+// Defined in unknown_field_set.h.
+class UnknownField;
+
+// Defined in command_line_interface.cc
+namespace compiler {
+class CommandLineInterface;
+namespace cpp {
+// Defined in helpers.h
+class Formatter;
+}  // namespace cpp
+}  // namespace compiler
+
+namespace descriptor_unittest {
+class DescriptorTest;
+}  // namespace descriptor_unittest
+
+// Defined in printer.h
+namespace io {
+class Printer;
+}  // namespace io
+
+// NB, all indices are zero-based.
+struct SourceLocation {
+  int start_line;
+  int end_line;
+  int start_column;
+  int end_column;
+
+  // Doc comments found at the source location.
+  // See the comments in SourceCodeInfo.Location (descriptor.proto) for details.
+  std::string leading_comments;
+  std::string trailing_comments;
+  std::vector<std::string> leading_detached_comments;
+};
+
+// Options when generating machine-parsable output from a descriptor with
+// DebugString().
+struct DebugStringOptions {
+  // include original user comments as recorded in SourceLocation entries. N.B.
+  // that this must be |false| by default: several other pieces of code (for
+  // example, the C++ code generation for fields in the proto compiler) rely on
+  // DebugString() output being unobstructed by user comments.
+  bool include_comments;
+  // If true, elide the braced body in the debug string.
+  bool elide_group_body;
+  bool elide_oneof_body;
+
+  DebugStringOptions()
+      : include_comments(false),
+        elide_group_body(false),
+        elide_oneof_body(false) {
+  }
+};
+
+// A class to handle the simplest cases of a lazily linked descriptor
+// for a message type that isn't built at the time of cross linking,
+// which is needed when a pool has lazily_build_dependencies_ set.
+// Must be instantiated as mutable in a descriptor.
+namespace internal {
+
+// The classes in this file represent a significant memory footprint for the
+// library. We make sure we are not accidentally making them larger by
+// hardcoding the struct size for a specific platform. Use as:
+//
+//   PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(type, expected_size_in_x84-64);
+//
+
+#if !defined(PROTOBUF_INTERNAL_CHECK_CLASS_SIZE)
+#define PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(t, expected)
+#endif
+
+class FlatAllocator;
+
+class PROTOBUF_EXPORT LazyDescriptor {
+ public:
+  // Init function to be called at init time of a descriptor containing
+  // a LazyDescriptor.
+  void Init() {
+    descriptor_ = nullptr;
+    once_ = nullptr;
+  }
+
+  // Sets the value of the descriptor if it is known during the descriptor
+  // building process. Not thread safe, should only be called during the
+  // descriptor build process. Should not be called after SetLazy has been
+  // called.
+  void Set(const Descriptor* descriptor);
+
+  // Sets the information needed to lazily cross link the descriptor at a later
+  // time, SetLazy is not thread safe, should be called only once at descriptor
+  // build time if the symbol wasn't found and building of the file containing
+  // that type is delayed because lazily_build_dependencies_ is set on the pool.
+  // Should not be called after Set() has been called.
+  void SetLazy(StringPiece name, const FileDescriptor* file);
+
+  // Returns the current value of the descriptor, thread-safe. If SetLazy(...)
+  // has been called, will do a one-time cross link of the type specified,
+  // building the descriptor file that contains the type if necessary.
+  inline const Descriptor* Get(const ServiceDescriptor* service) {
+    Once(service);
+    return descriptor_;
+  }
+
+ private:
+  void Once(const ServiceDescriptor* service);
+
+  const Descriptor* descriptor_;
+  // The once_ flag is followed by a NUL terminated string for the type name.
+  internal::once_flag* once_;
+};
+
+class PROTOBUF_EXPORT SymbolBase {
+ private:
+  friend class google::protobuf::Symbol;
+  uint8_t symbol_type_;
+};
+
+// Some types have more than one SymbolBase because they have multiple
+// identities in the table. We can't have duplicate direct bases, so we use this
+// intermediate base to do so.
+// See BuildEnumValue for details.
+template <int N>
+class PROTOBUF_EXPORT SymbolBaseN : public SymbolBase {};
+
+}  // namespace internal
+
+// Describes a type of protocol message, or a particular group within a
+// message.  To obtain the Descriptor for a given message object, call
+// Message::GetDescriptor().  Generated message classes also have a
+// static method called descriptor() which returns the type's descriptor.
+// Use DescriptorPool to construct your own descriptors.
+class PROTOBUF_EXPORT Descriptor : private internal::SymbolBase {
+ public:
+  typedef DescriptorProto Proto;
+
+  // The name of the message type, not including its scope.
+  const std::string& name() const;
+
+  // The fully-qualified name of the message type, scope delimited by
+  // periods.  For example, message type "Foo" which is declared in package
+  // "bar" has full name "bar.Foo".  If a type "Baz" is nested within
+  // Foo, Baz's full_name is "bar.Foo.Baz".  To get only the part that
+  // comes after the last '.', use name().
+  const std::string& full_name() const;
+
+  // Index of this descriptor within the file or containing type's message
+  // type array.
+  int index() const;
+
+  // The .proto file in which this message type was defined.  Never nullptr.
+  const FileDescriptor* file() const;
+
+  // If this Descriptor describes a nested type, this returns the type
+  // in which it is nested.  Otherwise, returns nullptr.
+  const Descriptor* containing_type() const;
+
+  // Get options for this message type.  These are specified in the .proto file
+  // by placing lines like "option foo = 1234;" in the message definition.
+  // Allowed options are defined by MessageOptions in descriptor.proto, and any
+  // available extensions of that message.
+  const MessageOptions& options() const;
+
+  // Write the contents of this Descriptor into the given DescriptorProto.
+  // The target DescriptorProto must be clear before calling this; if it
+  // isn't, the result may be garbage.
+  void CopyTo(DescriptorProto* proto) const;
+
+  // Write the contents of this descriptor in a human-readable form. Output
+  // will be suitable for re-parsing.
+  std::string DebugString() const;
+
+  // Similar to DebugString(), but additionally takes options (e.g.,
+  // include original user comments in output).
+  std::string DebugStringWithOptions(const DebugStringOptions& options) const;
+
+  // Returns true if this is a placeholder for an unknown type. This will
+  // only be the case if this descriptor comes from a DescriptorPool
+  // with AllowUnknownDependencies() set.
+  bool is_placeholder() const;
+
+  enum WellKnownType {
+    WELLKNOWNTYPE_UNSPECIFIED,  // Not a well-known type.
+
+    // Wrapper types.
+    WELLKNOWNTYPE_DOUBLEVALUE,  // google.protobuf.DoubleValue
+    WELLKNOWNTYPE_FLOATVALUE,   // google.protobuf.FloatValue
+    WELLKNOWNTYPE_INT64VALUE,   // google.protobuf.Int64Value
+    WELLKNOWNTYPE_UINT64VALUE,  // google.protobuf.UInt64Value
+    WELLKNOWNTYPE_INT32VALUE,   // google.protobuf.Int32Value
+    WELLKNOWNTYPE_UINT32VALUE,  // google.protobuf.UInt32Value
+    WELLKNOWNTYPE_STRINGVALUE,  // google.protobuf.StringValue
+    WELLKNOWNTYPE_BYTESVALUE,   // google.protobuf.BytesValue
+    WELLKNOWNTYPE_BOOLVALUE,    // google.protobuf.BoolValue
+
+    // Other well known types.
+    WELLKNOWNTYPE_ANY,        // google.protobuf.Any
+    WELLKNOWNTYPE_FIELDMASK,  // google.protobuf.FieldMask
+    WELLKNOWNTYPE_DURATION,   // google.protobuf.Duration
+    WELLKNOWNTYPE_TIMESTAMP,  // google.protobuf.Timestamp
+    WELLKNOWNTYPE_VALUE,      // google.protobuf.Value
+    WELLKNOWNTYPE_LISTVALUE,  // google.protobuf.ListValue
+    WELLKNOWNTYPE_STRUCT,     // google.protobuf.Struct
+
+    // New well-known types may be added in the future.
+    // Please make sure any switch() statements have a 'default' case.
+    __WELLKNOWNTYPE__DO_NOT_USE__ADD_DEFAULT_INSTEAD__,
+  };
+
+  WellKnownType well_known_type() const;
+
+  // Field stuff -----------------------------------------------------
+
+  // The number of fields in this message type.
+  int field_count() const;
+  // Gets a field by index, where 0 <= index < field_count().
+  // These are returned in the order they were defined in the .proto file.
+  const FieldDescriptor* field(int index) const;
+
+  // Looks up a field by declared tag number.  Returns nullptr if no such field
+  // exists.
+  const FieldDescriptor* FindFieldByNumber(int number) const;
+  // Looks up a field by name.  Returns nullptr if no such field exists.
+  const FieldDescriptor* FindFieldByName(ConstStringParam name) const;
+
+  // Looks up a field by lowercased name (as returned by lowercase_name()).
+  // This lookup may be ambiguous if multiple field names differ only by case,
+  // in which case the field returned is chosen arbitrarily from the matches.
+  const FieldDescriptor* FindFieldByLowercaseName(
+      ConstStringParam lowercase_name) const;
+
+  // Looks up a field by camel-case name (as returned by camelcase_name()).
+  // This lookup may be ambiguous if multiple field names differ in a way that
+  // leads them to have identical camel-case names, in which case the field
+  // returned is chosen arbitrarily from the matches.
+  const FieldDescriptor* FindFieldByCamelcaseName(
+      ConstStringParam camelcase_name) const;
+
+  // The number of oneofs in this message type.
+  int oneof_decl_count() const;
+  // The number of oneofs in this message type, excluding synthetic oneofs.
+  // Real oneofs always come first, so iterating up to real_oneof_decl_cout()
+  // will yield all real oneofs.
+  int real_oneof_decl_count() const;
+  // Get a oneof by index, where 0 <= index < oneof_decl_count().
+  // These are returned in the order they were defined in the .proto file.
+  const OneofDescriptor* oneof_decl(int index) const;
+
+  // Looks up a oneof by name.  Returns nullptr if no such oneof exists.
+  const OneofDescriptor* FindOneofByName(ConstStringParam name) const;
+
+  // Nested type stuff -----------------------------------------------
+
+  // The number of nested types in this message type.
+  int nested_type_count() const;
+  // Gets a nested type by index, where 0 <= index < nested_type_count().
+  // These are returned in the order they were defined in the .proto file.
+  const Descriptor* nested_type(int index) const;
+
+  // Looks up a nested type by name.  Returns nullptr if no such nested type
+  // exists.
+  const Descriptor* FindNestedTypeByName(ConstStringParam name) const;
+
+  // Enum stuff ------------------------------------------------------
+
+  // The number of enum types in this message type.
+  int enum_type_count() const;
+  // Gets an enum type by index, where 0 <= index < enum_type_count().
+  // These are returned in the order they were defined in the .proto file.
+  const EnumDescriptor* enum_type(int index) const;
+
+  // Looks up an enum type by name.  Returns nullptr if no such enum type
+  // exists.
+  const EnumDescriptor* FindEnumTypeByName(ConstStringParam name) const;
+
+  // Looks up an enum value by name, among all enum types in this message.
+  // Returns nullptr if no such value exists.
+  const EnumValueDescriptor* FindEnumValueByName(ConstStringParam name) const;
+
+  // Extensions ------------------------------------------------------
+
+  // A range of field numbers which are designated for third-party
+  // extensions.
+  struct ExtensionRange {
+    typedef DescriptorProto_ExtensionRange Proto;
+
+    typedef ExtensionRangeOptions OptionsType;
+
+    // See Descriptor::CopyTo().
+    void CopyTo(DescriptorProto_ExtensionRange* proto) const;
+
+    int start;  // inclusive
+    int end;    // exclusive
+
+    const ExtensionRangeOptions* options_;
+  };
+
+  // The number of extension ranges in this message type.
+  int extension_range_count() const;
+  // Gets an extension range by index, where 0 <= index <
+  // extension_range_count(). These are returned in the order they were defined
+  // in the .proto file.
+  const ExtensionRange* extension_range(int index) const;
+
+  // Returns true if the number is in one of the extension ranges.
+  bool IsExtensionNumber(int number) const;
+
+  // Returns nullptr if no extension range contains the given number.
+  const ExtensionRange* FindExtensionRangeContainingNumber(int number) const;
+
+  // The number of extensions defined nested within this message type's scope.
+  // See doc:
+  // https://developers.google.com/protocol-buffers/docs/proto#nested-extensions
+  //
+  // Note that the extensions may be extending *other* messages.
+  //
+  // For example:
+  // message M1 {
+  //   extensions 1 to max;
+  // }
+  //
+  // message M2 {
+  //   extend M1 {
+  //     optional int32 foo = 1;
+  //   }
+  // }
+  //
+  // In this case,
+  // DescriptorPool::generated_pool()
+  //     ->FindMessageTypeByName("M2")
+  //     ->extension(0)
+  // will return "foo", even though "foo" is an extension of M1.
+  // To find all known extensions of a given message, instead use
+  // DescriptorPool::FindAllExtensions.
+  int extension_count() const;
+  // Get an extension by index, where 0 <= index < extension_count().
+  // These are returned in the order they were defined in the .proto file.
+  const FieldDescriptor* extension(int index) const;
+
+  // Looks up a named extension (which extends some *other* message type)
+  // defined within this message type's scope.
+  const FieldDescriptor* FindExtensionByName(ConstStringParam name) const;
+
+  // Similar to FindFieldByLowercaseName(), but finds extensions defined within
+  // this message type's scope.
+  const FieldDescriptor* FindExtensionByLowercaseName(
+      ConstStringParam name) const;
+
+  // Similar to FindFieldByCamelcaseName(), but finds extensions defined within
+  // this message type's scope.
+  const FieldDescriptor* FindExtensionByCamelcaseName(
+      ConstStringParam name) const;
+
+  // Reserved fields -------------------------------------------------
+
+  // A range of reserved field numbers.
+  struct ReservedRange {
+    int start;  // inclusive
+    int end;    // exclusive
+  };
+
+  // The number of reserved ranges in this message type.
+  int reserved_range_count() const;
+  // Gets an reserved range by index, where 0 <= index <
+  // reserved_range_count(). These are returned in the order they were defined
+  // in the .proto file.
+  const ReservedRange* reserved_range(int index) const;
+
+  // Returns true if the number is in one of the reserved ranges.
+  bool IsReservedNumber(int number) const;
+
+  // Returns nullptr if no reserved range contains the given number.
+  const ReservedRange* FindReservedRangeContainingNumber(int number) const;
+
+  // The number of reserved field names in this message type.
+  int reserved_name_count() const;
+
+  // Gets a reserved name by index, where 0 <= index < reserved_name_count().
+  const std::string& reserved_name(int index) const;
+
+  // Returns true if the field name is reserved.
+  bool IsReservedName(ConstStringParam name) const;
+
+  // Source Location ---------------------------------------------------
+
+  // Updates |*out_location| to the source location of the complete
+  // extent of this message declaration.  Returns false and leaves
+  // |*out_location| unchanged iff location information was not available.
+  bool GetSourceLocation(SourceLocation* out_location) const;
+
+  // Maps --------------------------------------------------------------
+
+  // Returns the FieldDescriptor for the "key" field. If this isn't a map entry
+  // field, returns nullptr.
+  const FieldDescriptor* map_key() const;
+
+  // Returns the FieldDescriptor for the "value" field. If this isn't a map
+  // entry field, returns nullptr.
+  const FieldDescriptor* map_value() const;
+
+ private:
+  friend class Symbol;
+  typedef MessageOptions OptionsType;
+
+  // Allows tests to test CopyTo(proto, true).
+  friend class descriptor_unittest::DescriptorTest;
+
+  // Allows access to GetLocationPath for annotations.
+  friend class io::Printer;
+  friend class compiler::cpp::Formatter;
+
+  // Fill the json_name field of FieldDescriptorProto.
+  void CopyJsonNameTo(DescriptorProto* proto) const;
+
+  // Internal version of DebugString; controls the level of indenting for
+  // correct depth. Takes |options| to control debug-string options, and
+  // |include_opening_clause| to indicate whether the "message ... " part of the
+  // clause has already been generated (this varies depending on context).
+  void DebugString(int depth, std::string* contents,
+                   const DebugStringOptions& options,
+                   bool include_opening_clause) const;
+
+  // Walks up the descriptor tree to generate the source location path
+  // to this descriptor from the file root.
+  void GetLocationPath(std::vector<int>* output) const;
+
+  // True if this is a placeholder for an unknown type.
+  bool is_placeholder_ : 1;
+  // True if this is a placeholder and the type name wasn't fully-qualified.
+  bool is_unqualified_placeholder_ : 1;
+  // Well known type.  Stored like this to conserve space.
+  uint8_t well_known_type_ : 5;
+
+  // This points to the last field _number_ that is part of the sequence
+  // starting at 1, where
+  //     `desc->field(i)->number() == i + 1`
+  // A value of `0` means no field matches. That is, there are no fields or the
+  // first field is not field `1`.
+  // Uses 16-bit to avoid extra padding. Unlikely to have more than 2^16
+  // sequentially numbered fields in a message.
+  uint16_t sequential_field_limit_;
+
+  int field_count_;
+
+  // all_names_ = [name, full_name]
+  const std::string* all_names_;
+  const FileDescriptor* file_;
+  const Descriptor* containing_type_;
+  const MessageOptions* options_;
+
+  // These arrays are separated from their sizes to minimize padding on 64-bit.
+  FieldDescriptor* fields_;
+  OneofDescriptor* oneof_decls_;
+  Descriptor* nested_types_;
+  EnumDescriptor* enum_types_;
+  ExtensionRange* extension_ranges_;
+  FieldDescriptor* extensions_;
+  ReservedRange* reserved_ranges_;
+  const std::string** reserved_names_;
+
+  int oneof_decl_count_;
+  int real_oneof_decl_count_;
+  int nested_type_count_;
+  int enum_type_count_;
+  int extension_range_count_;
+  int extension_count_;
+  int reserved_range_count_;
+  int reserved_name_count_;
+
+  // IMPORTANT:  If you add a new field, make sure to search for all instances
+  // of Allocate<Descriptor>() and AllocateArray<Descriptor>() in descriptor.cc
+  // and update them to initialize the field.
+
+  // Must be constructed using DescriptorPool.
+  Descriptor() {}
+  friend class DescriptorBuilder;
+  friend class DescriptorPool;
+  friend class EnumDescriptor;
+  friend class FieldDescriptor;
+  friend class FileDescriptorTables;
+  friend class OneofDescriptor;
+  friend class MethodDescriptor;
+  friend class FileDescriptor;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Descriptor);
+};
+
+PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(Descriptor, 136);
+
+// Describes a single field of a message.  To get the descriptor for a given
+// field, first get the Descriptor for the message in which it is defined,
+// then call Descriptor::FindFieldByName().  To get a FieldDescriptor for
+// an extension, do one of the following:
+// - Get the Descriptor or FileDescriptor for its containing scope, then
+//   call Descriptor::FindExtensionByName() or
+//   FileDescriptor::FindExtensionByName().
+// - Given a DescriptorPool, call DescriptorPool::FindExtensionByNumber() or
+//   DescriptorPool::FindExtensionByPrintableName().
+// Use DescriptorPool to construct your own descriptors.
+class PROTOBUF_EXPORT FieldDescriptor : private internal::SymbolBase {
+ public:
+  typedef FieldDescriptorProto Proto;
+
+  // Identifies a field type.  0 is reserved for errors.  The order is weird
+  // for historical reasons.  Types 12 and up are new in proto2.
+  enum Type {
+    TYPE_DOUBLE = 1,    // double, exactly eight bytes on the wire.
+    TYPE_FLOAT = 2,     // float, exactly four bytes on the wire.
+    TYPE_INT64 = 3,     // int64, varint on the wire.  Negative numbers
+                        // take 10 bytes.  Use TYPE_SINT64 if negative
+                        // values are likely.
+    TYPE_UINT64 = 4,    // uint64, varint on the wire.
+    TYPE_INT32 = 5,     // int32, varint on the wire.  Negative numbers
+                        // take 10 bytes.  Use TYPE_SINT32 if negative
+                        // values are likely.
+    TYPE_FIXED64 = 6,   // uint64, exactly eight bytes on the wire.
+    TYPE_FIXED32 = 7,   // uint32, exactly four bytes on the wire.
+    TYPE_BOOL = 8,      // bool, varint on the wire.
+    TYPE_STRING = 9,    // UTF-8 text.
+    TYPE_GROUP = 10,    // Tag-delimited message.  Deprecated.
+    TYPE_MESSAGE = 11,  // Length-delimited message.
+
+    TYPE_BYTES = 12,     // Arbitrary byte array.
+    TYPE_UINT32 = 13,    // uint32, varint on the wire
+    TYPE_ENUM = 14,      // Enum, varint on the wire
+    TYPE_SFIXED32 = 15,  // int32, exactly four bytes on the wire
+    TYPE_SFIXED64 = 16,  // int64, exactly eight bytes on the wire
+    TYPE_SINT32 = 17,    // int32, ZigZag-encoded varint on the wire
+    TYPE_SINT64 = 18,    // int64, ZigZag-encoded varint on the wire
+
+    MAX_TYPE = 18,  // Constant useful for defining lookup tables
+                    // indexed by Type.
+  };
+
+  // Specifies the C++ data type used to represent the field.  There is a
+  // fixed mapping from Type to CppType where each Type maps to exactly one
+  // CppType.  0 is reserved for errors.
+  enum CppType {
+    CPPTYPE_INT32 = 1,     // TYPE_INT32, TYPE_SINT32, TYPE_SFIXED32
+    CPPTYPE_INT64 = 2,     // TYPE_INT64, TYPE_SINT64, TYPE_SFIXED64
+    CPPTYPE_UINT32 = 3,    // TYPE_UINT32, TYPE_FIXED32
+    CPPTYPE_UINT64 = 4,    // TYPE_UINT64, TYPE_FIXED64
+    CPPTYPE_DOUBLE = 5,    // TYPE_DOUBLE
+    CPPTYPE_FLOAT = 6,     // TYPE_FLOAT
+    CPPTYPE_BOOL = 7,      // TYPE_BOOL
+    CPPTYPE_ENUM = 8,      // TYPE_ENUM
+    CPPTYPE_STRING = 9,    // TYPE_STRING, TYPE_BYTES
+    CPPTYPE_MESSAGE = 10,  // TYPE_MESSAGE, TYPE_GROUP
+
+    MAX_CPPTYPE = 10,  // Constant useful for defining lookup tables
+                       // indexed by CppType.
+  };
+
+  // Identifies whether the field is optional, required, or repeated.  0 is
+  // reserved for errors.
+  enum Label {
+    LABEL_OPTIONAL = 1,  // optional
+    LABEL_REQUIRED = 2,  // required
+    LABEL_REPEATED = 3,  // repeated
+
+    MAX_LABEL = 3,  // Constant useful for defining lookup tables
+                    // indexed by Label.
+  };
+
+  // Valid field numbers are positive integers up to kMaxNumber.
+  static const int kMaxNumber = (1 << 29) - 1;
+
+  // First field number reserved for the protocol buffer library implementation.
+  // Users may not declare fields that use reserved numbers.
+  static const int kFirstReservedNumber = 19000;
+  // Last field number reserved for the protocol buffer library implementation.
+  // Users may not declare fields that use reserved numbers.
+  static const int kLastReservedNumber = 19999;
+
+  const std::string& name() const;  // Name of this field within the message.
+  const std::string& full_name() const;  // Fully-qualified name of the field.
+  const std::string& json_name() const;  // JSON name of this field.
+  const FileDescriptor* file() const;  // File in which this field was defined.
+  bool is_extension() const;           // Is this an extension field?
+  int number() const;                  // Declared tag number.
+
+  // Same as name() except converted to lower-case.  This (and especially the
+  // FindFieldByLowercaseName() method) can be useful when parsing formats
+  // which prefer to use lowercase naming style.  (Although, technically
+  // field names should be lowercased anyway according to the protobuf style
+  // guide, so this only makes a difference when dealing with old .proto files
+  // which do not follow the guide.)
+  const std::string& lowercase_name() const;
+
+  // Same as name() except converted to camel-case.  In this conversion, any
+  // time an underscore appears in the name, it is removed and the next
+  // letter is capitalized.  Furthermore, the first letter of the name is
+  // lower-cased.  Examples:
+  //   FooBar -> fooBar
+  //   foo_bar -> fooBar
+  //   fooBar -> fooBar
+  // This (and especially the FindFieldByCamelcaseName() method) can be useful
+  // when parsing formats which prefer to use camel-case naming style.
+  const std::string& camelcase_name() const;
+
+  Type type() const;                  // Declared type of this field.
+  const char* type_name() const;      // Name of the declared type.
+  CppType cpp_type() const;           // C++ type of this field.
+  const char* cpp_type_name() const;  // Name of the C++ type.
+  Label label() const;                // optional/required/repeated
+
+  bool is_required() const;  // shorthand for label() == LABEL_REQUIRED
+  bool is_optional() const;  // shorthand for label() == LABEL_OPTIONAL
+  bool is_repeated() const;  // shorthand for label() == LABEL_REPEATED
+  bool is_packable() const;  // shorthand for is_repeated() &&
+                             //               IsTypePackable(type())
+  bool is_packed() const;    // shorthand for is_packable() &&
+                             //               options().packed()
+  bool is_map() const;       // shorthand for type() == TYPE_MESSAGE &&
+                             // message_type()->options().map_entry()
+
+  // Returns true if this field was syntactically written with "optional" in the
+  // .proto file. Excludes singular proto3 fields that do not have a label.
+  bool has_optional_keyword() const;
+
+  // Returns true if this field tracks presence, ie. does the field
+  // distinguish between "unset" and "present with default value."
+  // This includes required, optional, and oneof fields. It excludes maps,
+  // repeated fields, and singular proto3 fields without "optional".
+  //
+  // For fields where has_presence() == true, the return value of
+  // Reflection::HasField() is semantically meaningful.
+  bool has_presence() const;
+
+  // Index of this field within the message's field array, or the file or
+  // extension scope's extensions array.
+  int index() const;
+
+  // Does this field have an explicitly-declared default value?
+  bool has_default_value() const;
+
+  // Whether the user has specified the json_name field option in the .proto
+  // file.
+  bool has_json_name() const;
+
+  // Get the field default value if cpp_type() == CPPTYPE_INT32.  If no
+  // explicit default was defined, the default is 0.
+  int32_t default_value_int32_t() const;
+  int32_t default_value_int32() const { return default_value_int32_t(); }
+  // Get the field default value if cpp_type() == CPPTYPE_INT64.  If no
+  // explicit default was defined, the default is 0.
+  int64_t default_value_int64_t() const;
+  int64_t default_value_int64() const { return default_value_int64_t(); }
+  // Get the field default value if cpp_type() == CPPTYPE_UINT32.  If no
+  // explicit default was defined, the default is 0.
+  uint32_t default_value_uint32_t() const;
+  uint32_t default_value_uint32() const { return default_value_uint32_t(); }
+  // Get the field default value if cpp_type() == CPPTYPE_UINT64.  If no
+  // explicit default was defined, the default is 0.
+  uint64_t default_value_uint64_t() const;
+  uint64_t default_value_uint64() const { return default_value_uint64_t(); }
+  // Get the field default value if cpp_type() == CPPTYPE_FLOAT.  If no
+  // explicit default was defined, the default is 0.0.
+  float default_value_float() const;
+  // Get the field default value if cpp_type() == CPPTYPE_DOUBLE.  If no
+  // explicit default was defined, the default is 0.0.
+  double default_value_double() const;
+  // Get the field default value if cpp_type() == CPPTYPE_BOOL.  If no
+  // explicit default was defined, the default is false.
+  bool default_value_bool() const;
+  // Get the field default value if cpp_type() == CPPTYPE_ENUM.  If no
+  // explicit default was defined, the default is the first value defined
+  // in the enum type (all enum types are required to have at least one value).
+  // This never returns nullptr.
+  const EnumValueDescriptor* default_value_enum() const;
+  // Get the field default value if cpp_type() == CPPTYPE_STRING.  If no
+  // explicit default was defined, the default is the empty string.
+  const std::string& default_value_string() const;
+
+  // The Descriptor for the message of which this is a field.  For extensions,
+  // this is the extended type.  Never nullptr.
+  const Descriptor* containing_type() const;
+
+  // If the field is a member of a oneof, this is the one, otherwise this is
+  // nullptr.
+  const OneofDescriptor* containing_oneof() const;
+
+  // If the field is a member of a non-synthetic oneof, returns the descriptor
+  // for the oneof, otherwise returns nullptr.
+  const OneofDescriptor* real_containing_oneof() const;
+
+  // If the field is a member of a oneof, returns the index in that oneof.
+  int index_in_oneof() const;
+
+  // An extension may be declared within the scope of another message.  If this
+  // field is an extension (is_extension() is true), then extension_scope()
+  // returns that message, or nullptr if the extension was declared at global
+  // scope.  If this is not an extension, extension_scope() is undefined (may
+  // assert-fail).
+  const Descriptor* extension_scope() const;
+
+  // If type is TYPE_MESSAGE or TYPE_GROUP, returns a descriptor for the
+  // message or the group type.  Otherwise, returns null.
+  const Descriptor* message_type() const;
+  // If type is TYPE_ENUM, returns a descriptor for the enum.  Otherwise,
+  // returns null.
+  const EnumDescriptor* enum_type() const;
+
+  // Get the FieldOptions for this field.  This includes things listed in
+  // square brackets after the field definition.  E.g., the field:
+  //   optional string text = 1 [ctype=CORD];
+  // has the "ctype" option set.  Allowed options are defined by FieldOptions in
+  // descriptor.proto, and any available extensions of that message.
+  const FieldOptions& options() const;
+
+  // See Descriptor::CopyTo().
+  void CopyTo(FieldDescriptorProto* proto) const;
+
+  // See Descriptor::DebugString().
+  std::string DebugString() const;
+
+  // See Descriptor::DebugStringWithOptions().
+  std::string DebugStringWithOptions(const DebugStringOptions& options) const;
+
+  // Helper method to get the CppType for a particular Type.
+  static CppType TypeToCppType(Type type);
+
+  // Helper method to get the name of a Type.
+  static const char* TypeName(Type type);
+
+  // Helper method to get the name of a CppType.
+  static const char* CppTypeName(CppType cpp_type);
+
+  // Return true iff [packed = true] is valid for fields of this type.
+  static inline bool IsTypePackable(Type field_type);
+
+  // Returns full_name() except if the field is a MessageSet extension,
+  // in which case it returns the full_name() of the containing message type
+  // for backwards compatibility with proto1.
+  //
+  // A MessageSet extension is defined as an optional message extension
+  // whose containing type has the message_set_wire_format option set.
+  // This should be true of extensions of google.protobuf.bridge.MessageSet;
+  // by convention, such extensions are named "message_set_extension".
+  //
+  // The opposite operation (looking up an extension's FieldDescriptor given
+  // its printable name) can be accomplished with
+  //     message->file()->pool()->FindExtensionByPrintableName(message, name)
+  // where the extension extends "message".
+  const std::string& PrintableNameForExtension() const;
+
+  // Source Location ---------------------------------------------------
+
+  // Updates |*out_location| to the source location of the complete
+  // extent of this field declaration.  Returns false and leaves
+  // |*out_location| unchanged iff location information was not available.
+  bool GetSourceLocation(SourceLocation* out_location) const;
+
+ private:
+  friend class Symbol;
+  typedef FieldOptions OptionsType;
+
+  // Allows access to GetLocationPath for annotations.
+  friend class io::Printer;
+  friend class compiler::cpp::Formatter;
+  friend class Reflection;
+
+  // Fill the json_name field of FieldDescriptorProto.
+  void CopyJsonNameTo(FieldDescriptorProto* proto) const;
+
+  // See Descriptor::DebugString().
+  void DebugString(int depth, std::string* contents,
+                   const DebugStringOptions& options) const;
+
+  // formats the default value appropriately and returns it as a string.
+  // Must have a default value to call this. If quote_string_type is true, then
+  // types of CPPTYPE_STRING will be surrounded by quotes and CEscaped.
+  std::string DefaultValueAsString(bool quote_string_type) const;
+
+  // Helper function that returns the field type name for DebugString.
+  std::string FieldTypeNameDebugString() const;
+
+  // Walks up the descriptor tree to generate the source location path
+  // to this descriptor from the file root.
+  void GetLocationPath(std::vector<int>* output) const;
+
+  // Returns true if this is a map message type.
+  bool is_map_message_type() const;
+
+  bool has_default_value_ : 1;
+  bool proto3_optional_ : 1;
+  // Whether the user has specified the json_name field option in the .proto
+  // file.
+  bool has_json_name_ : 1;
+  bool is_extension_ : 1;
+  bool is_oneof_ : 1;
+
+  // Actually a `Label` but stored as uint8_t to save space.
+  uint8_t label_ : 2;
+
+  // Actually a `Type`, but stored as uint8_t to save space.
+  mutable uint8_t type_;
+
+  // Logically:
+  //   all_names_ = [name, full_name, lower, camel, json]
+  // However:
+  //   duplicates will be omitted, so lower/camel/json might be in the same
+  //   position.
+  // We store the true offset for each name here, and the bit width must be
+  // large enough to account for the worst case where all names are present.
+  uint8_t lowercase_name_index_ : 2;
+  uint8_t camelcase_name_index_ : 2;
+  uint8_t json_name_index_ : 3;
+  // Sadly, `number_` located here to reduce padding. Unrelated to all_names_
+  // and its indices above.
+  int number_;
+  const std::string* all_names_;
+  const FileDescriptor* file_;
+
+  // The once_flag is followed by a NUL terminated string for the type name and
+  // enum default value (or empty string if no default enum).
+  internal::once_flag* type_once_;
+  static void TypeOnceInit(const FieldDescriptor* to_init);
+  void InternalTypeOnceInit() const;
+  const Descriptor* containing_type_;
+  union {
+    const OneofDescriptor* containing_oneof;
+    const Descriptor* extension_scope;
+  } scope_;
+  union {
+    mutable const Descriptor* message_type;
+    mutable const EnumDescriptor* enum_type;
+  } type_descriptor_;
+  const FieldOptions* options_;
+  // IMPORTANT:  If you add a new field, make sure to search for all instances
+  // of Allocate<FieldDescriptor>() and AllocateArray<FieldDescriptor>() in
+  // descriptor.cc and update them to initialize the field.
+
+  union {
+    int32_t default_value_int32_t_;
+    int64_t default_value_int64_t_;
+    uint32_t default_value_uint32_t_;
+    uint64_t default_value_uint64_t_;
+    float default_value_float_;
+    double default_value_double_;
+    bool default_value_bool_;
+
+    mutable const EnumValueDescriptor* default_value_enum_;
+    const std::string* default_value_string_;
+    mutable std::atomic<const Message*> default_generated_instance_;
+  };
+
+  static const CppType kTypeToCppTypeMap[MAX_TYPE + 1];
+
+  static const char* const kTypeToName[MAX_TYPE + 1];
+
+  static const char* const kCppTypeToName[MAX_CPPTYPE + 1];
+
+  static const char* const kLabelToName[MAX_LABEL + 1];
+
+  // Must be constructed using DescriptorPool.
+  FieldDescriptor() {}
+  friend class DescriptorBuilder;
+  friend class FileDescriptor;
+  friend class Descriptor;
+  friend class OneofDescriptor;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldDescriptor);
+};
+
+PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(FieldDescriptor, 72);
+
+// Describes a oneof defined in a message type.
+class PROTOBUF_EXPORT OneofDescriptor : private internal::SymbolBase {
+ public:
+  typedef OneofDescriptorProto Proto;
+
+  const std::string& name() const;       // Name of this oneof.
+  const std::string& full_name() const;  // Fully-qualified name of the oneof.
+
+  // Index of this oneof within the message's oneof array.
+  int index() const;
+
+  // Returns whether this oneof was inserted by the compiler to wrap a proto3
+  // optional field. If this returns true, code generators should *not* emit it.
+  bool is_synthetic() const;
+
+  // The .proto file in which this oneof was defined.  Never nullptr.
+  const FileDescriptor* file() const;
+  // The Descriptor for the message containing this oneof.
+  const Descriptor* containing_type() const;
+
+  // The number of (non-extension) fields which are members of this oneof.
+  int field_count() const;
+  // Get a member of this oneof, in the order in which they were declared in the
+  // .proto file.  Does not include extensions.
+  const FieldDescriptor* field(int index) const;
+
+  const OneofOptions& options() const;
+
+  // See Descriptor::CopyTo().
+  void CopyTo(OneofDescriptorProto* proto) const;
+
+  // See Descriptor::DebugString().
+  std::string DebugString() const;
+
+  // See Descriptor::DebugStringWithOptions().
+  std::string DebugStringWithOptions(const DebugStringOptions& options) const;
+
+  // Source Location ---------------------------------------------------
+
+  // Updates |*out_location| to the source location of the complete
+  // extent of this oneof declaration.  Returns false and leaves
+  // |*out_location| unchanged iff location information was not available.
+  bool GetSourceLocation(SourceLocation* out_location) const;
+
+ private:
+  friend class Symbol;
+  typedef OneofOptions OptionsType;
+
+  // Allows access to GetLocationPath for annotations.
+  friend class io::Printer;
+  friend class compiler::cpp::Formatter;
+
+  // See Descriptor::DebugString().
+  void DebugString(int depth, std::string* contents,
+                   const DebugStringOptions& options) const;
+
+  // Walks up the descriptor tree to generate the source location path
+  // to this descriptor from the file root.
+  void GetLocationPath(std::vector<int>* output) const;
+
+  int field_count_;
+
+  // all_names_ = [name, full_name]
+  const std::string* all_names_;
+  const Descriptor* containing_type_;
+  const OneofOptions* options_;
+  const FieldDescriptor* fields_;
+
+  // IMPORTANT:  If you add a new field, make sure to search for all instances
+  // of Allocate<OneofDescriptor>() and AllocateArray<OneofDescriptor>()
+  // in descriptor.cc and update them to initialize the field.
+
+  // Must be constructed using DescriptorPool.
+  OneofDescriptor() {}
+  friend class DescriptorBuilder;
+  friend class Descriptor;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OneofDescriptor);
+};
+
+PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(OneofDescriptor, 40);
+
+// Describes an enum type defined in a .proto file.  To get the EnumDescriptor
+// for a generated enum type, call TypeName_descriptor().  Use DescriptorPool
+// to construct your own descriptors.
+class PROTOBUF_EXPORT EnumDescriptor : private internal::SymbolBase {
+ public:
+  typedef EnumDescriptorProto Proto;
+
+  // The name of this enum type in the containing scope.
+  const std::string& name() const;
+
+  // The fully-qualified name of the enum type, scope delimited by periods.
+  const std::string& full_name() const;
+
+  // Index of this enum within the file or containing message's enum array.
+  int index() const;
+
+  // The .proto file in which this enum type was defined.  Never nullptr.
+  const FileDescriptor* file() const;
+
+  // The number of values for this EnumDescriptor.  Guaranteed to be greater
+  // than zero.
+  int value_count() const;
+  // Gets a value by index, where 0 <= index < value_count().
+  // These are returned in the order they were defined in the .proto file.
+  const EnumValueDescriptor* value(int index) const;
+
+  // Looks up a value by name.  Returns nullptr if no such value exists.
+  const EnumValueDescriptor* FindValueByName(ConstStringParam name) const;
+  // Looks up a value by number.  Returns nullptr if no such value exists.  If
+  // multiple values have this number, the first one defined is returned.
+  const EnumValueDescriptor* FindValueByNumber(int number) const;
+
+  // If this enum type is nested in a message type, this is that message type.
+  // Otherwise, nullptr.
+  const Descriptor* containing_type() const;
+
+  // Get options for this enum type.  These are specified in the .proto file by
+  // placing lines like "option foo = 1234;" in the enum definition.  Allowed
+  // options are defined by EnumOptions in descriptor.proto, and any available
+  // extensions of that message.
+  const EnumOptions& options() const;
+
+  // See Descriptor::CopyTo().
+  void CopyTo(EnumDescriptorProto* proto) const;
+
+  // See Descriptor::DebugString().
+  std::string DebugString() const;
+
+  // See Descriptor::DebugStringWithOptions().
+  std::string DebugStringWithOptions(const DebugStringOptions& options) const;
+
+  // Returns true if this is a placeholder for an unknown enum. This will
+  // only be the case if this descriptor comes from a DescriptorPool
+  // with AllowUnknownDependencies() set.
+  bool is_placeholder() const;
+
+  // Reserved fields -------------------------------------------------
+
+  // A range of reserved field numbers.
+  struct ReservedRange {
+    int start;  // inclusive
+    int end;    // inclusive
+  };
+
+  // The number of reserved ranges in this message type.
+  int reserved_range_count() const;
+  // Gets an reserved range by index, where 0 <= index <
+  // reserved_range_count(). These are returned in the order they were defined
+  // in the .proto file.
+  const EnumDescriptor::ReservedRange* reserved_range(int index) const;
+
+  // Returns true if the number is in one of the reserved ranges.
+  bool IsReservedNumber(int number) const;
+
+  // Returns nullptr if no reserved range contains the given number.
+  const EnumDescriptor::ReservedRange* FindReservedRangeContainingNumber(
+      int number) const;
+
+  // The number of reserved field names in this message type.
+  int reserved_name_count() const;
+
+  // Gets a reserved name by index, where 0 <= index < reserved_name_count().
+  const std::string& reserved_name(int index) const;
+
+  // Returns true if the field name is reserved.
+  bool IsReservedName(ConstStringParam name) const;
+
+  // Source Location ---------------------------------------------------
+
+  // Updates |*out_location| to the source location of the complete
+  // extent of this enum declaration.  Returns false and leaves
+  // |*out_location| unchanged iff location information was not available.
+  bool GetSourceLocation(SourceLocation* out_location) const;
+
+ private:
+  friend class Symbol;
+  typedef EnumOptions OptionsType;
+
+  // Allows access to GetLocationPath for annotations.
+  friend class io::Printer;
+  friend class compiler::cpp::Formatter;
+
+  // Allow access to FindValueByNumberCreatingIfUnknown.
+  friend class descriptor_unittest::DescriptorTest;
+
+  // Looks up a value by number.  If the value does not exist, dynamically
+  // creates a new EnumValueDescriptor for that value, assuming that it was
+  // unknown. If a new descriptor is created, this is done in a thread-safe way,
+  // and future calls will return the same value descriptor pointer.
+  //
+  // This is private but is used by Reflection (which is friended below) to
+  // return a valid EnumValueDescriptor from GetEnum() when this feature is
+  // enabled.
+  const EnumValueDescriptor* FindValueByNumberCreatingIfUnknown(
+      int number) const;
+
+  // See Descriptor::DebugString().
+  void DebugString(int depth, std::string* contents,
+                   const DebugStringOptions& options) const;
+
+  // Walks up the descriptor tree to generate the source location path
+  // to this descriptor from the file root.
+  void GetLocationPath(std::vector<int>* output) const;
+
+  // True if this is a placeholder for an unknown type.
+  bool is_placeholder_ : 1;
+  // True if this is a placeholder and the type name wasn't fully-qualified.
+  bool is_unqualified_placeholder_ : 1;
+
+  // This points to the last value _index_ that is part of the sequence starting
+  // with the first label, where
+  //   `enum->value(i)->number() == enum->value(0)->number() + i`
+  // We measure relative to the first label to adapt to enum labels starting at
+  // 0 or 1.
+  // Uses 16-bit to avoid extra padding. Unlikely to have more than 2^15
+  // sequentially numbered labels in an enum.
+  int16_t sequential_value_limit_;
+
+  int value_count_;
+
+  // all_names_ = [name, full_name]
+  const std::string* all_names_;
+  const FileDescriptor* file_;
+  const Descriptor* containing_type_;
+  const EnumOptions* options_;
+  EnumValueDescriptor* values_;
+
+  int reserved_range_count_;
+  int reserved_name_count_;
+  EnumDescriptor::ReservedRange* reserved_ranges_;
+  const std::string** reserved_names_;
+
+  // IMPORTANT:  If you add a new field, make sure to search for all instances
+  // of Allocate<EnumDescriptor>() and AllocateArray<EnumDescriptor>() in
+  // descriptor.cc and update them to initialize the field.
+
+  // Must be constructed using DescriptorPool.
+  EnumDescriptor() {}
+  friend class DescriptorBuilder;
+  friend class Descriptor;
+  friend class FieldDescriptor;
+  friend class FileDescriptorTables;
+  friend class EnumValueDescriptor;
+  friend class FileDescriptor;
+  friend class DescriptorPool;
+  friend class Reflection;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumDescriptor);
+};
+
+PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(EnumDescriptor, 72);
+
+// Describes an individual enum constant of a particular type.  To get the
+// EnumValueDescriptor for a given enum value, first get the EnumDescriptor
+// for its type, then use EnumDescriptor::FindValueByName() or
+// EnumDescriptor::FindValueByNumber().  Use DescriptorPool to construct
+// your own descriptors.
+class PROTOBUF_EXPORT EnumValueDescriptor : private internal::SymbolBaseN<0>,
+                                            private internal::SymbolBaseN<1> {
+ public:
+  typedef EnumValueDescriptorProto Proto;
+
+  const std::string& name() const;  // Name of this enum constant.
+  int index() const;                // Index within the enums's Descriptor.
+  int number() const;               // Numeric value of this enum constant.
+
+  // The full_name of an enum value is a sibling symbol of the enum type.
+  // e.g. the full name of FieldDescriptorProto::TYPE_INT32 is actually
+  // "google.protobuf.FieldDescriptorProto.TYPE_INT32", NOT
+  // "google.protobuf.FieldDescriptorProto.Type.TYPE_INT32".  This is to conform
+  // with C++ scoping rules for enums.
+  const std::string& full_name() const;
+
+  // The .proto file in which this value was defined.  Never nullptr.
+  const FileDescriptor* file() const;
+  // The type of this value.  Never nullptr.
+  const EnumDescriptor* type() const;
+
+  // Get options for this enum value.  These are specified in the .proto file by
+  // adding text like "[foo = 1234]" after an enum value definition.  Allowed
+  // options are defined by EnumValueOptions in descriptor.proto, and any
+  // available extensions of that message.
+  const EnumValueOptions& options() const;
+
+  // See Descriptor::CopyTo().
+  void CopyTo(EnumValueDescriptorProto* proto) const;
+
+  // See Descriptor::DebugString().
+  std::string DebugString() const;
+
+  // See Descriptor::DebugStringWithOptions().
+  std::string DebugStringWithOptions(const DebugStringOptions& options) const;
+
+  // Source Location ---------------------------------------------------
+
+  // Updates |*out_location| to the source location of the complete
+  // extent of this enum value declaration.  Returns false and leaves
+  // |*out_location| unchanged iff location information was not available.
+  bool GetSourceLocation(SourceLocation* out_location) const;
+
+ private:
+  friend class Symbol;
+  typedef EnumValueOptions OptionsType;
+
+  // Allows access to GetLocationPath for annotations.
+  friend class io::Printer;
+  friend class compiler::cpp::Formatter;
+
+  // See Descriptor::DebugString().
+  void DebugString(int depth, std::string* contents,
+                   const DebugStringOptions& options) const;
+
+  // Walks up the descriptor tree to generate the source location path
+  // to this descriptor from the file root.
+  void GetLocationPath(std::vector<int>* output) const;
+
+  int number_;
+  // all_names_ = [name, full_name]
+  const std::string* all_names_;
+  const EnumDescriptor* type_;
+  const EnumValueOptions* options_;
+  // IMPORTANT:  If you add a new field, make sure to search for all instances
+  // of Allocate<EnumValueDescriptor>() and AllocateArray<EnumValueDescriptor>()
+  // in descriptor.cc and update them to initialize the field.
+
+  // Must be constructed using DescriptorPool.
+  EnumValueDescriptor() {}
+  friend class DescriptorBuilder;
+  friend class EnumDescriptor;
+  friend class DescriptorPool;
+  friend class FileDescriptorTables;
+  friend class Reflection;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumValueDescriptor);
+};
+
+PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(EnumValueDescriptor, 32);
+
+// Describes an RPC service. Use DescriptorPool to construct your own
+// descriptors.
+class PROTOBUF_EXPORT ServiceDescriptor : private internal::SymbolBase {
+ public:
+  typedef ServiceDescriptorProto Proto;
+
+  // The name of the service, not including its containing scope.
+  const std::string& name() const;
+  // The fully-qualified name of the service, scope delimited by periods.
+  const std::string& full_name() const;
+  // Index of this service within the file's services array.
+  int index() const;
+
+  // The .proto file in which this service was defined.  Never nullptr.
+  const FileDescriptor* file() const;
+
+  // Get options for this service type.  These are specified in the .proto file
+  // by placing lines like "option foo = 1234;" in the service definition.
+  // Allowed options are defined by ServiceOptions in descriptor.proto, and any
+  // available extensions of that message.
+  const ServiceOptions& options() const;
+
+  // The number of methods this service defines.
+  int method_count() const;
+  // Gets a MethodDescriptor by index, where 0 <= index < method_count().
+  // These are returned in the order they were defined in the .proto file.
+  const MethodDescriptor* method(int index) const;
+
+  // Look up a MethodDescriptor by name.
+  const MethodDescriptor* FindMethodByName(ConstStringParam name) const;
+
+  // See Descriptor::CopyTo().
+  void CopyTo(ServiceDescriptorProto* proto) const;
+
+  // See Descriptor::DebugString().
+  std::string DebugString() const;
+
+  // See Descriptor::DebugStringWithOptions().
+  std::string DebugStringWithOptions(const DebugStringOptions& options) const;
+
+  // Source Location ---------------------------------------------------
+
+  // Updates |*out_location| to the source location of the complete
+  // extent of this service declaration.  Returns false and leaves
+  // |*out_location| unchanged iff location information was not available.
+  bool GetSourceLocation(SourceLocation* out_location) const;
+
+ private:
+  friend class Symbol;
+  typedef ServiceOptions OptionsType;
+
+  // Allows access to GetLocationPath for annotations.
+  friend class io::Printer;
+  friend class compiler::cpp::Formatter;
+
+  // See Descriptor::DebugString().
+  void DebugString(std::string* contents,
+                   const DebugStringOptions& options) const;
+
+  // Walks up the descriptor tree to generate the source location path
+  // to this descriptor from the file root.
+  void GetLocationPath(std::vector<int>* output) const;
+
+  // all_names_ = [name, full_name]
+  const std::string* all_names_;
+  const FileDescriptor* file_;
+  const ServiceOptions* options_;
+  MethodDescriptor* methods_;
+  int method_count_;
+  // IMPORTANT:  If you add a new field, make sure to search for all instances
+  // of Allocate<ServiceDescriptor>() and AllocateArray<ServiceDescriptor>() in
+  // descriptor.cc and update them to initialize the field.
+
+  // Must be constructed using DescriptorPool.
+  ServiceDescriptor() {}
+  friend class DescriptorBuilder;
+  friend class FileDescriptor;
+  friend class MethodDescriptor;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceDescriptor);
+};
+
+PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(ServiceDescriptor, 48);
+
+// Describes an individual service method.  To obtain a MethodDescriptor given
+// a service, first get its ServiceDescriptor, then call
+// ServiceDescriptor::FindMethodByName().  Use DescriptorPool to construct your
+// own descriptors.
+class PROTOBUF_EXPORT MethodDescriptor : private internal::SymbolBase {
+ public:
+  typedef MethodDescriptorProto Proto;
+
+  // Name of this method, not including containing scope.
+  const std::string& name() const;
+  // The fully-qualified name of the method, scope delimited by periods.
+  const std::string& full_name() const;
+  // Index within the service's Descriptor.
+  int index() const;
+
+  // The .proto file in which this method was defined.  Never nullptr.
+  const FileDescriptor* file() const;
+  // Gets the service to which this method belongs.  Never nullptr.
+  const ServiceDescriptor* service() const;
+
+  // Gets the type of protocol message which this method accepts as input.
+  const Descriptor* input_type() const;
+  // Gets the type of protocol message which this message produces as output.
+  const Descriptor* output_type() const;
+
+  // Gets whether the client streams multiple requests.
+  bool client_streaming() const;
+  // Gets whether the server streams multiple responses.
+  bool server_streaming() const;
+
+  // Get options for this method.  These are specified in the .proto file by
+  // placing lines like "option foo = 1234;" in curly-braces after a method
+  // declaration.  Allowed options are defined by MethodOptions in
+  // descriptor.proto, and any available extensions of that message.
+  const MethodOptions& options() const;
+
+  // See Descriptor::CopyTo().
+  void CopyTo(MethodDescriptorProto* proto) const;
+
+  // See Descriptor::DebugString().
+  std::string DebugString() const;
+
+  // See Descriptor::DebugStringWithOptions().
+  std::string DebugStringWithOptions(const DebugStringOptions& options) const;
+
+  // Source Location ---------------------------------------------------
+
+  // Updates |*out_location| to the source location of the complete
+  // extent of this method declaration.  Returns false and leaves
+  // |*out_location| unchanged iff location information was not available.
+  bool GetSourceLocation(SourceLocation* out_location) const;
+
+ private:
+  friend class Symbol;
+  typedef MethodOptions OptionsType;
+
+  // Allows access to GetLocationPath for annotations.
+  friend class io::Printer;
+  friend class compiler::cpp::Formatter;
+
+  // See Descriptor::DebugString().
+  void DebugString(int depth, std::string* contents,
+                   const DebugStringOptions& options) const;
+
+  // Walks up the descriptor tree to generate the source location path
+  // to this descriptor from the file root.
+  void GetLocationPath(std::vector<int>* output) const;
+
+  bool client_streaming_;
+  bool server_streaming_;
+  // all_names_ = [name, full_name]
+  const std::string* all_names_;
+  const ServiceDescriptor* service_;
+  mutable internal::LazyDescriptor input_type_;
+  mutable internal::LazyDescriptor output_type_;
+  const MethodOptions* options_;
+  // IMPORTANT:  If you add a new field, make sure to search for all instances
+  // of Allocate<MethodDescriptor>() and AllocateArray<MethodDescriptor>() in
+  // descriptor.cc and update them to initialize the field.
+
+  // Must be constructed using DescriptorPool.
+  MethodDescriptor() {}
+  friend class DescriptorBuilder;
+  friend class ServiceDescriptor;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MethodDescriptor);
+};
+
+PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(MethodDescriptor, 64);
+
+// Describes a whole .proto file.  To get the FileDescriptor for a compiled-in
+// file, get the descriptor for something defined in that file and call
+// descriptor->file().  Use DescriptorPool to construct your own descriptors.
+class PROTOBUF_EXPORT FileDescriptor : private internal::SymbolBase {
+ public:
+  typedef FileDescriptorProto Proto;
+
+  // The filename, relative to the source tree.
+  // e.g. "foo/bar/baz.proto"
+  const std::string& name() const;
+
+  // The package, e.g. "google.protobuf.compiler".
+  const std::string& package() const;
+
+  // The DescriptorPool in which this FileDescriptor and all its contents were
+  // allocated.  Never nullptr.
+  const DescriptorPool* pool() const;
+
+  // The number of files imported by this one.
+  int dependency_count() const;
+  // Gets an imported file by index, where 0 <= index < dependency_count().
+  // These are returned in the order they were defined in the .proto file.
+  const FileDescriptor* dependency(int index) const;
+
+  // The number of files public imported by this one.
+  // The public dependency list is a subset of the dependency list.
+  int public_dependency_count() const;
+  // Gets a public imported file by index, where 0 <= index <
+  // public_dependency_count().
+  // These are returned in the order they were defined in the .proto file.
+  const FileDescriptor* public_dependency(int index) const;
+
+  // The number of files that are imported for weak fields.
+  // The weak dependency list is a subset of the dependency list.
+  int weak_dependency_count() const;
+  // Gets a weak imported file by index, where 0 <= index <
+  // weak_dependency_count().
+  // These are returned in the order they were defined in the .proto file.
+  const FileDescriptor* weak_dependency(int index) const;
+
+  // Number of top-level message types defined in this file.  (This does not
+  // include nested types.)
+  int message_type_count() const;
+  // Gets a top-level message type, where 0 <= index < message_type_count().
+  // These are returned in the order they were defined in the .proto file.
+  const Descriptor* message_type(int index) const;
+
+  // Number of top-level enum types defined in this file.  (This does not
+  // include nested types.)
+  int enum_type_count() const;
+  // Gets a top-level enum type, where 0 <= index < enum_type_count().
+  // These are returned in the order they were defined in the .proto file.
+  const EnumDescriptor* enum_type(int index) const;
+
+  // Number of services defined in this file.
+  int service_count() const;
+  // Gets a service, where 0 <= index < service_count().
+  // These are returned in the order they were defined in the .proto file.
+  const ServiceDescriptor* service(int index) const;
+
+  // Number of extensions defined at file scope.  (This does not include
+  // extensions nested within message types.)
+  int extension_count() const;
+  // Gets an extension's descriptor, where 0 <= index < extension_count().
+  // These are returned in the order they were defined in the .proto file.
+  const FieldDescriptor* extension(int index) const;
+
+  // Get options for this file.  These are specified in the .proto file by
+  // placing lines like "option foo = 1234;" at the top level, outside of any
+  // other definitions.  Allowed options are defined by FileOptions in
+  // descriptor.proto, and any available extensions of that message.
+  const FileOptions& options() const;
+
+  // Syntax of this file.
+  enum Syntax {
+    SYNTAX_UNKNOWN = 0,
+    SYNTAX_PROTO2 = 2,
+    SYNTAX_PROTO3 = 3,
+  };
+  Syntax syntax() const;
+  static const char* SyntaxName(Syntax syntax);
+
+  // Find a top-level message type by name (not full_name).  Returns nullptr if
+  // not found.
+  const Descriptor* FindMessageTypeByName(ConstStringParam name) const;
+  // Find a top-level enum type by name.  Returns nullptr if not found.
+  const EnumDescriptor* FindEnumTypeByName(ConstStringParam name) const;
+  // Find an enum value defined in any top-level enum by name.  Returns nullptr
+  // if not found.
+  const EnumValueDescriptor* FindEnumValueByName(ConstStringParam name) const;
+  // Find a service definition by name.  Returns nullptr if not found.
+  const ServiceDescriptor* FindServiceByName(ConstStringParam name) const;
+  // Find a top-level extension definition by name.  Returns nullptr if not
+  // found.
+  const FieldDescriptor* FindExtensionByName(ConstStringParam name) const;
+  // Similar to FindExtensionByName(), but searches by lowercased-name.  See
+  // Descriptor::FindFieldByLowercaseName().
+  const FieldDescriptor* FindExtensionByLowercaseName(
+      ConstStringParam name) const;
+  // Similar to FindExtensionByName(), but searches by camelcased-name.  See
+  // Descriptor::FindFieldByCamelcaseName().
+  const FieldDescriptor* FindExtensionByCamelcaseName(
+      ConstStringParam name) const;
+
+  // See Descriptor::CopyTo().
+  // Notes:
+  // - This method does NOT copy source code information since it is relatively
+  //   large and rarely needed.  See CopySourceCodeInfoTo() below.
+  void CopyTo(FileDescriptorProto* proto) const;
+  // Write the source code information of this FileDescriptor into the given
+  // FileDescriptorProto.  See CopyTo() above.
+  void CopySourceCodeInfoTo(FileDescriptorProto* proto) const;
+  // Fill the json_name field of FieldDescriptorProto for all fields. Can only
+  // be called after CopyTo().
+  void CopyJsonNameTo(FileDescriptorProto* proto) const;
+
+  // See Descriptor::DebugString().
+  std::string DebugString() const;
+
+  // See Descriptor::DebugStringWithOptions().
+  std::string DebugStringWithOptions(const DebugStringOptions& options) const;
+
+  // Returns true if this is a placeholder for an unknown file. This will
+  // only be the case if this descriptor comes from a DescriptorPool
+  // with AllowUnknownDependencies() set.
+  bool is_placeholder() const;
+
+  // Updates |*out_location| to the source location of the complete extent of
+  // this file declaration (namely, the empty path).
+  bool GetSourceLocation(SourceLocation* out_location) const;
+
+  // Updates |*out_location| to the source location of the complete
+  // extent of the declaration or declaration-part denoted by |path|.
+  // Returns false and leaves |*out_location| unchanged iff location
+  // information was not available.  (See SourceCodeInfo for
+  // description of path encoding.)
+  bool GetSourceLocation(const std::vector<int>& path,
+                         SourceLocation* out_location) const;
+
+ private:
+  friend class Symbol;
+  typedef FileOptions OptionsType;
+
+  bool is_placeholder_;
+  // Indicates the FileDescriptor is completed building. Used to verify
+  // that type accessor functions that can possibly build a dependent file
+  // aren't called during the process of building the file.
+  bool finished_building_;
+  // Actually a `Syntax` but stored as uint8_t to save space.
+  uint8_t syntax_;
+  // This one is here to fill the padding.
+  int extension_count_;
+
+  const std::string* name_;
+  const std::string* package_;
+  const DescriptorPool* pool_;
+
+  // dependencies_once_ contain a once_flag followed by N NUL terminated
+  // strings. Dependencies that do not need to be loaded will be empty. ie just
+  // {'\0'}
+  internal::once_flag* dependencies_once_;
+  static void DependenciesOnceInit(const FileDescriptor* to_init);
+  void InternalDependenciesOnceInit() const;
+
+  // These are arranged to minimize padding on 64-bit.
+  int dependency_count_;
+  int public_dependency_count_;
+  int weak_dependency_count_;
+  int message_type_count_;
+  int enum_type_count_;
+  int service_count_;
+
+  mutable const FileDescriptor** dependencies_;
+  int* public_dependencies_;
+  int* weak_dependencies_;
+  Descriptor* message_types_;
+  EnumDescriptor* enum_types_;
+  ServiceDescriptor* services_;
+  FieldDescriptor* extensions_;
+  const FileOptions* options_;
+
+  const FileDescriptorTables* tables_;
+  const SourceCodeInfo* source_code_info_;
+
+  // IMPORTANT:  If you add a new field, make sure to search for all instances
+  // of Allocate<FileDescriptor>() and AllocateArray<FileDescriptor>() in
+  // descriptor.cc and update them to initialize the field.
+
+  FileDescriptor() {}
+  friend class DescriptorBuilder;
+  friend class DescriptorPool;
+  friend class Descriptor;
+  friend class FieldDescriptor;
+  friend class internal::LazyDescriptor;
+  friend class OneofDescriptor;
+  friend class EnumDescriptor;
+  friend class EnumValueDescriptor;
+  friend class MethodDescriptor;
+  friend class ServiceDescriptor;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileDescriptor);
+};
+
+PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(FileDescriptor, 144);
+
+// ===================================================================
+
+// Used to construct descriptors.
+//
+// Normally you won't want to build your own descriptors.  Message classes
+// constructed by the protocol compiler will provide them for you.  However,
+// if you are implementing Message on your own, or if you are writing a
+// program which can operate on totally arbitrary types and needs to load
+// them from some sort of database, you might need to.
+//
+// Since Descriptors are composed of a whole lot of cross-linked bits of
+// data that would be a pain to put together manually, the
+// DescriptorPool class is provided to make the process easier.  It can
+// take a FileDescriptorProto (defined in descriptor.proto), validate it,
+// and convert it to a set of nicely cross-linked Descriptors.
+//
+// DescriptorPool also helps with memory management.  Descriptors are
+// composed of many objects containing static data and pointers to each
+// other.  In all likelihood, when it comes time to delete this data,
+// you'll want to delete it all at once.  In fact, it is not uncommon to
+// have a whole pool of descriptors all cross-linked with each other which
+// you wish to delete all at once.  This class represents such a pool, and
+// handles the memory management for you.
+//
+// You can also search for descriptors within a DescriptorPool by name, and
+// extensions by number.
+class PROTOBUF_EXPORT DescriptorPool {
+ public:
+  // Create a normal, empty DescriptorPool.
+  DescriptorPool();
+
+  // Constructs a DescriptorPool that, when it can't find something among the
+  // descriptors already in the pool, looks for it in the given
+  // DescriptorDatabase.
+  // Notes:
+  // - If a DescriptorPool is constructed this way, its BuildFile*() methods
+  //   must not be called (they will assert-fail).  The only way to populate
+  //   the pool with descriptors is to call the Find*By*() methods.
+  // - The Find*By*() methods may block the calling thread if the
+  //   DescriptorDatabase blocks.  This in turn means that parsing messages
+  //   may block if they need to look up extensions.
+  // - The Find*By*() methods will use mutexes for thread-safety, thus making
+  //   them slower even when they don't have to fall back to the database.
+  //   In fact, even the Find*By*() methods of descriptor objects owned by
+  //   this pool will be slower, since they will have to obtain locks too.
+  // - An ErrorCollector may optionally be given to collect validation errors
+  //   in files loaded from the database.  If not given, errors will be printed
+  //   to GOOGLE_LOG(ERROR).  Remember that files are built on-demand, so this
+  //   ErrorCollector may be called from any thread that calls one of the
+  //   Find*By*() methods.
+  // - The DescriptorDatabase must not be mutated during the lifetime of
+  //   the DescriptorPool. Even if the client takes care to avoid data races,
+  //   changes to the content of the DescriptorDatabase may not be reflected
+  //   in subsequent lookups in the DescriptorPool.
+  class ErrorCollector;
+  explicit DescriptorPool(DescriptorDatabase* fallback_database,
+                          ErrorCollector* error_collector = nullptr);
+
+  ~DescriptorPool();
+
+  // Get a pointer to the generated pool.  Generated protocol message classes
+  // which are compiled into the binary will allocate their descriptors in
+  // this pool.  Do not add your own descriptors to this pool.
+  static const DescriptorPool* generated_pool();
+
+
+  // Find a FileDescriptor in the pool by file name.  Returns nullptr if not
+  // found.
+  const FileDescriptor* FindFileByName(ConstStringParam name) const;
+
+  // Find the FileDescriptor in the pool which defines the given symbol.
+  // If any of the Find*ByName() methods below would succeed, then this is
+  // equivalent to calling that method and calling the result's file() method.
+  // Otherwise this returns nullptr.
+  const FileDescriptor* FindFileContainingSymbol(
+      ConstStringParam symbol_name) const;
+
+  // Looking up descriptors ------------------------------------------
+  // These find descriptors by fully-qualified name.  These will find both
+  // top-level descriptors and nested descriptors.  They return nullptr if not
+  // found.
+
+  const Descriptor* FindMessageTypeByName(ConstStringParam name) const;
+  const FieldDescriptor* FindFieldByName(ConstStringParam name) const;
+  const FieldDescriptor* FindExtensionByName(ConstStringParam name) const;
+  const OneofDescriptor* FindOneofByName(ConstStringParam name) const;
+  const EnumDescriptor* FindEnumTypeByName(ConstStringParam name) const;
+  const EnumValueDescriptor* FindEnumValueByName(ConstStringParam name) const;
+  const ServiceDescriptor* FindServiceByName(ConstStringParam name) const;
+  const MethodDescriptor* FindMethodByName(ConstStringParam name) const;
+
+  // Finds an extension of the given type by number.  The extendee must be
+  // a member of this DescriptorPool or one of its underlays.
+  const FieldDescriptor* FindExtensionByNumber(const Descriptor* extendee,
+                                               int number) const;
+
+  // Finds an extension of the given type by its printable name.
+  // See comments above PrintableNameForExtension() for the definition of
+  // "printable name".  The extendee must be a member of this DescriptorPool
+  // or one of its underlays.  Returns nullptr if there is no known message
+  // extension with the given printable name.
+  const FieldDescriptor* FindExtensionByPrintableName(
+      const Descriptor* extendee, ConstStringParam printable_name) const;
+
+  // Finds extensions of extendee. The extensions will be appended to
+  // out in an undefined order. Only extensions defined directly in
+  // this DescriptorPool or one of its underlays are guaranteed to be
+  // found: extensions defined in the fallback database might not be found
+  // depending on the database implementation.
+  void FindAllExtensions(const Descriptor* extendee,
+                         std::vector<const FieldDescriptor*>* out) const;
+
+  // Building descriptors --------------------------------------------
+
+  // When converting a FileDescriptorProto to a FileDescriptor, various
+  // errors might be detected in the input.  The caller may handle these
+  // programmatically by implementing an ErrorCollector.
+  class PROTOBUF_EXPORT ErrorCollector {
+   public:
+    inline ErrorCollector() {}
+    virtual ~ErrorCollector();
+
+    // These constants specify what exact part of the construct is broken.
+    // This is useful e.g. for mapping the error back to an exact location
+    // in a .proto file.
+    enum ErrorLocation {
+      NAME,           // the symbol name, or the package name for files
+      NUMBER,         // field or extension range number
+      TYPE,           // field type
+      EXTENDEE,       // field extendee
+      DEFAULT_VALUE,  // field default value
+      INPUT_TYPE,     // method input type
+      OUTPUT_TYPE,    // method output type
+      OPTION_NAME,    // name in assignment
+      OPTION_VALUE,   // value in option assignment
+      IMPORT,         // import error
+      OTHER           // some other problem
+    };
+
+    // Reports an error in the FileDescriptorProto. Use this function if the
+    // problem occurred should interrupt building the FileDescriptorProto.
+    virtual void AddError(
+        const std::string& filename,  // File name in which the error occurred.
+        const std::string& element_name,  // Full name of the erroneous element.
+        const Message* descriptor,  // Descriptor of the erroneous element.
+        ErrorLocation location,     // One of the location constants, above.
+        const std::string& message  // Human-readable error message.
+        ) = 0;
+
+    // Reports a warning in the FileDescriptorProto. Use this function if the
+    // problem occurred should NOT interrupt building the FileDescriptorProto.
+    virtual void AddWarning(
+        const std::string& /*filename*/,      // File name in which the error
+                                              // occurred.
+        const std::string& /*element_name*/,  // Full name of the erroneous
+                                              // element.
+        const Message* /*descriptor*/,  // Descriptor of the erroneous element.
+        ErrorLocation /*location*/,     // One of the location constants, above.
+        const std::string& /*message*/  // Human-readable error message.
+    ) {}
+
+   private:
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorCollector);
+  };
+
+  // Convert the FileDescriptorProto to real descriptors and place them in
+  // this DescriptorPool.  All dependencies of the file must already be in
+  // the pool.  Returns the resulting FileDescriptor, or nullptr if there were
+  // problems with the input (e.g. the message was invalid, or dependencies
+  // were missing).  Details about the errors are written to GOOGLE_LOG(ERROR).
+  const FileDescriptor* BuildFile(const FileDescriptorProto& proto);
+
+  // Same as BuildFile() except errors are sent to the given ErrorCollector.
+  const FileDescriptor* BuildFileCollectingErrors(
+      const FileDescriptorProto& proto, ErrorCollector* error_collector);
+
+  // By default, it is an error if a FileDescriptorProto contains references
+  // to types or other files that are not found in the DescriptorPool (or its
+  // backing DescriptorDatabase, if any).  If you call
+  // AllowUnknownDependencies(), however, then unknown types and files
+  // will be replaced by placeholder descriptors (which can be identified by
+  // the is_placeholder() method).  This can allow you to
+  // perform some useful operations with a .proto file even if you do not
+  // have access to other .proto files on which it depends.  However, some
+  // heuristics must be used to fill in the gaps in information, and these
+  // can lead to descriptors which are inaccurate.  For example, the
+  // DescriptorPool may be forced to guess whether an unknown type is a message
+  // or an enum, as well as what package it resides in.  Furthermore,
+  // placeholder types will not be discoverable via FindMessageTypeByName()
+  // and similar methods, which could confuse some descriptor-based algorithms.
+  // Generally, the results of this option should be handled with extreme care.
+  void AllowUnknownDependencies() { allow_unknown_ = true; }
+
+  // By default, weak imports are allowed to be missing, in which case we will
+  // use a placeholder for the dependency and convert the field to be an Empty
+  // message field. If you call EnforceWeakDependencies(true), however, the
+  // DescriptorPool will report a import not found error.
+  void EnforceWeakDependencies(bool enforce) { enforce_weak_ = enforce; }
+
+  // Internal stuff --------------------------------------------------
+  // These methods MUST NOT be called from outside the proto2 library.
+  // These methods may contain hidden pitfalls and may be removed in a
+  // future library version.
+
+  // Create a DescriptorPool which is overlaid on top of some other pool.
+  // If you search for a descriptor in the overlay and it is not found, the
+  // underlay will be searched as a backup.  If the underlay has its own
+  // underlay, that will be searched next, and so on.  This also means that
+  // files built in the overlay will be cross-linked with the underlay's
+  // descriptors if necessary.  The underlay remains property of the caller;
+  // it must remain valid for the lifetime of the newly-constructed pool.
+  //
+  // Example:  Say you want to parse a .proto file at runtime in order to use
+  // its type with a DynamicMessage.  Say this .proto file has dependencies,
+  // but you know that all the dependencies will be things that are already
+  // compiled into the binary.  For ease of use, you'd like to load the types
+  // right out of generated_pool() rather than have to parse redundant copies
+  // of all these .protos and runtime.  But, you don't want to add the parsed
+  // types directly into generated_pool(): this is not allowed, and would be
+  // bad design anyway.  So, instead, you could use generated_pool() as an
+  // underlay for a new DescriptorPool in which you add only the new file.
+  //
+  // WARNING:  Use of underlays can lead to many subtle gotchas.  Instead,
+  //   try to formulate what you want to do in terms of DescriptorDatabases.
+  explicit DescriptorPool(const DescriptorPool* underlay);
+
+  // Called by generated classes at init time to add their descriptors to
+  // generated_pool.  Do NOT call this in your own code!  filename must be a
+  // permanent string (e.g. a string literal).
+  static void InternalAddGeneratedFile(const void* encoded_file_descriptor,
+                                       int size);
+
+  // Disallow [enforce_utf8 = false] in .proto files.
+  void DisallowEnforceUtf8() { disallow_enforce_utf8_ = true; }
+
+
+  // For internal use only:  Gets a non-const pointer to the generated pool.
+  // This is called at static-initialization time only, so thread-safety is
+  // not a concern.  If both an underlay and a fallback database are present,
+  // the underlay takes precedence.
+  static DescriptorPool* internal_generated_pool();
+
+  // For internal use only:  Gets a non-const pointer to the generated
+  // descriptor database.
+  // Only used for testing.
+  static DescriptorDatabase* internal_generated_database();
+
+  // For internal use only:  Changes the behavior of BuildFile() such that it
+  // allows the file to make reference to message types declared in other files
+  // which it did not officially declare as dependencies.
+  void InternalDontEnforceDependencies();
+
+  // For internal use only: Enables lazy building of dependencies of a file.
+  // Delay the building of dependencies of a file descriptor until absolutely
+  // necessary, like when message_type() is called on a field that is defined
+  // in that dependency's file. This will cause functional issues if a proto
+  // or one of its dependencies has errors. Should only be enabled for the
+  // generated_pool_ (because no descriptor build errors are guaranteed by
+  // the compilation generation process), testing, or if a lack of descriptor
+  // build errors can be guaranteed for a pool.
+  void InternalSetLazilyBuildDependencies() {
+    lazily_build_dependencies_ = true;
+    // This needs to be set when lazily building dependencies, as it breaks
+    // dependency checking.
+    InternalDontEnforceDependencies();
+  }
+
+  // For internal use only.
+  void internal_set_underlay(const DescriptorPool* underlay) {
+    underlay_ = underlay;
+  }
+
+  // For internal (unit test) use only:  Returns true if a FileDescriptor has
+  // been constructed for the given file, false otherwise.  Useful for testing
+  // lazy descriptor initialization behavior.
+  bool InternalIsFileLoaded(ConstStringParam filename) const;
+
+  // Add a file to unused_import_track_files_. DescriptorBuilder will log
+  // warnings or errors for those files if there is any unused import.
+  void AddUnusedImportTrackFile(ConstStringParam file_name,
+                                bool is_error = false);
+  void ClearUnusedImportTrackFiles();
+
+ private:
+  friend class Descriptor;
+  friend class internal::LazyDescriptor;
+  friend class FieldDescriptor;
+  friend class EnumDescriptor;
+  friend class ServiceDescriptor;
+  friend class MethodDescriptor;
+  friend class FileDescriptor;
+  friend class DescriptorBuilder;
+  friend class FileDescriptorTables;
+
+  // Return true if the given name is a sub-symbol of any non-package
+  // descriptor that already exists in the descriptor pool.  (The full
+  // definition of such types is already known.)
+  bool IsSubSymbolOfBuiltType(StringPiece name) const;
+
+  // Tries to find something in the fallback database and link in the
+  // corresponding proto file.  Returns true if successful, in which case
+  // the caller should search for the thing again.  These are declared
+  // const because they are called by (semantically) const methods.
+  bool TryFindFileInFallbackDatabase(StringPiece name) const;
+  bool TryFindSymbolInFallbackDatabase(StringPiece name) const;
+  bool TryFindExtensionInFallbackDatabase(const Descriptor* containing_type,
+                                          int field_number) const;
+
+  // This internal find extension method only check with its table and underlay
+  // descriptor_pool's table. It does not check with fallback DB and no
+  // additional proto file will be build in this method.
+  const FieldDescriptor* InternalFindExtensionByNumberNoLock(
+      const Descriptor* extendee, int number) const;
+
+  // Like BuildFile() but called internally when the file has been loaded from
+  // fallback_database_.  Declared const because it is called by (semantically)
+  // const methods.
+  const FileDescriptor* BuildFileFromDatabase(
+      const FileDescriptorProto& proto) const;
+
+  // Helper for when lazily_build_dependencies_ is set, can look up a symbol
+  // after the file's descriptor is built, and can build the file where that
+  // symbol is defined if necessary. Will create a placeholder if the type
+  // doesn't exist in the fallback database, or the file doesn't build
+  // successfully.
+  Symbol CrossLinkOnDemandHelper(StringPiece name,
+                                 bool expecting_enum) const;
+
+  // Create a placeholder FileDescriptor of the specified name
+  FileDescriptor* NewPlaceholderFile(StringPiece name) const;
+  FileDescriptor* NewPlaceholderFileWithMutexHeld(
+      StringPiece name, internal::FlatAllocator& alloc) const;
+
+  enum PlaceholderType {
+    PLACEHOLDER_MESSAGE,
+    PLACEHOLDER_ENUM,
+    PLACEHOLDER_EXTENDABLE_MESSAGE
+  };
+  // Create a placeholder Descriptor of the specified name
+  Symbol NewPlaceholder(StringPiece name,
+                        PlaceholderType placeholder_type) const;
+  Symbol NewPlaceholderWithMutexHeld(StringPiece name,
+                                     PlaceholderType placeholder_type) const;
+
+  // If fallback_database_ is nullptr, this is nullptr.  Otherwise, this is a
+  // mutex which must be locked while accessing tables_.
+  internal::WrappedMutex* mutex_;
+
+  // See constructor.
+  DescriptorDatabase* fallback_database_;
+  ErrorCollector* default_error_collector_;
+  const DescriptorPool* underlay_;
+
+  // This class contains a lot of hash maps with complicated types that
+  // we'd like to keep out of the header.
+  class Tables;
+  std::unique_ptr<Tables> tables_;
+
+  bool enforce_dependencies_;
+  bool lazily_build_dependencies_;
+  bool allow_unknown_;
+  bool enforce_weak_;
+  bool disallow_enforce_utf8_;
+
+  // Set of files to track for unused imports. The bool value when true means
+  // unused imports are treated as errors (and as warnings when false).
+  std::map<std::string, bool> unused_import_track_files_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorPool);
+};
+
+
+// inline methods ====================================================
+
+// These macros makes this repetitive code more readable.
+#define PROTOBUF_DEFINE_ACCESSOR(CLASS, FIELD, TYPE) \
+  inline TYPE CLASS::FIELD() const { return FIELD##_; }
+
+// Strings fields are stored as pointers but returned as const references.
+#define PROTOBUF_DEFINE_STRING_ACCESSOR(CLASS, FIELD) \
+  inline const std::string& CLASS::FIELD() const { return *FIELD##_; }
+
+// Name and full name are stored in a single array to save space.
+#define PROTOBUF_DEFINE_NAME_ACCESSOR(CLASS)                              \
+  inline const std::string& CLASS::name() const { return all_names_[0]; } \
+  inline const std::string& CLASS::full_name() const { return all_names_[1]; }
+
+// Arrays take an index parameter, obviously.
+#define PROTOBUF_DEFINE_ARRAY_ACCESSOR(CLASS, FIELD, TYPE) \
+  inline TYPE CLASS::FIELD(int index) const { return FIELD##s_ + index; }
+
+#define PROTOBUF_DEFINE_OPTIONS_ACCESSOR(CLASS, TYPE) \
+  inline const TYPE& CLASS::options() const { return *options_; }
+
+PROTOBUF_DEFINE_NAME_ACCESSOR(Descriptor)
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, file, const FileDescriptor*)
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, containing_type, const Descriptor*)
+
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, field_count, int)
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, oneof_decl_count, int)
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, real_oneof_decl_count, int)
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, nested_type_count, int)
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, enum_type_count, int)
+
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, field, const FieldDescriptor*)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, oneof_decl, const OneofDescriptor*)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, nested_type, const Descriptor*)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, enum_type, const EnumDescriptor*)
+
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, extension_range_count, int)
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, extension_count, int)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, extension_range,
+                               const Descriptor::ExtensionRange*)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, extension, const FieldDescriptor*)
+
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, reserved_range_count, int)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, reserved_range,
+                               const Descriptor::ReservedRange*)
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, reserved_name_count, int)
+
+PROTOBUF_DEFINE_OPTIONS_ACCESSOR(Descriptor, MessageOptions)
+PROTOBUF_DEFINE_ACCESSOR(Descriptor, is_placeholder, bool)
+
+PROTOBUF_DEFINE_NAME_ACCESSOR(FieldDescriptor)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, file, const FileDescriptor*)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, number, int)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, is_extension, bool)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, containing_type, const Descriptor*)
+PROTOBUF_DEFINE_OPTIONS_ACCESSOR(FieldDescriptor, FieldOptions)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, has_default_value, bool)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, has_json_name, bool)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_int32_t, int32_t)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_int64_t, int64_t)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_uint32_t, uint32_t)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_uint64_t, uint64_t)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_float, float)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_double, double)
+PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_bool, bool)
+PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, default_value_string)
+
+PROTOBUF_DEFINE_NAME_ACCESSOR(OneofDescriptor)
+PROTOBUF_DEFINE_ACCESSOR(OneofDescriptor, containing_type, const Descriptor*)
+PROTOBUF_DEFINE_ACCESSOR(OneofDescriptor, field_count, int)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(OneofDescriptor, field, const FieldDescriptor*)
+PROTOBUF_DEFINE_OPTIONS_ACCESSOR(OneofDescriptor, OneofOptions)
+
+PROTOBUF_DEFINE_NAME_ACCESSOR(EnumDescriptor)
+PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, file, const FileDescriptor*)
+PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, containing_type, const Descriptor*)
+PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, value_count, int)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(EnumDescriptor, value,
+                               const EnumValueDescriptor*)
+PROTOBUF_DEFINE_OPTIONS_ACCESSOR(EnumDescriptor, EnumOptions)
+PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, is_placeholder, bool)
+PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, reserved_range_count, int)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(EnumDescriptor, reserved_range,
+                               const EnumDescriptor::ReservedRange*)
+PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, reserved_name_count, int)
+
+PROTOBUF_DEFINE_NAME_ACCESSOR(EnumValueDescriptor)
+PROTOBUF_DEFINE_ACCESSOR(EnumValueDescriptor, number, int)
+PROTOBUF_DEFINE_ACCESSOR(EnumValueDescriptor, type, const EnumDescriptor*)
+PROTOBUF_DEFINE_OPTIONS_ACCESSOR(EnumValueDescriptor, EnumValueOptions)
+
+PROTOBUF_DEFINE_NAME_ACCESSOR(ServiceDescriptor)
+PROTOBUF_DEFINE_ACCESSOR(ServiceDescriptor, file, const FileDescriptor*)
+PROTOBUF_DEFINE_ACCESSOR(ServiceDescriptor, method_count, int)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(ServiceDescriptor, method,
+                               const MethodDescriptor*)
+PROTOBUF_DEFINE_OPTIONS_ACCESSOR(ServiceDescriptor, ServiceOptions)
+
+PROTOBUF_DEFINE_NAME_ACCESSOR(MethodDescriptor)
+PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, service, const ServiceDescriptor*)
+PROTOBUF_DEFINE_OPTIONS_ACCESSOR(MethodDescriptor, MethodOptions)
+PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, client_streaming, bool)
+PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, server_streaming, bool)
+
+PROTOBUF_DEFINE_STRING_ACCESSOR(FileDescriptor, name)
+PROTOBUF_DEFINE_STRING_ACCESSOR(FileDescriptor, package)
+PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, pool, const DescriptorPool*)
+PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, dependency_count, int)
+PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, public_dependency_count, int)
+PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, weak_dependency_count, int)
+PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, message_type_count, int)
+PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, enum_type_count, int)
+PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, service_count, int)
+PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, extension_count, int)
+PROTOBUF_DEFINE_OPTIONS_ACCESSOR(FileDescriptor, FileOptions)
+PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, is_placeholder, bool)
+
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, message_type, const Descriptor*)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, enum_type, const EnumDescriptor*)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, service,
+                               const ServiceDescriptor*)
+PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, extension,
+                               const FieldDescriptor*)
+
+#undef PROTOBUF_DEFINE_ACCESSOR
+#undef PROTOBUF_DEFINE_STRING_ACCESSOR
+#undef PROTOBUF_DEFINE_ARRAY_ACCESSOR
+
+// A few accessors differ from the macros...
+
+inline Descriptor::WellKnownType Descriptor::well_known_type() const {
+  return static_cast<Descriptor::WellKnownType>(well_known_type_);
+}
+
+inline bool Descriptor::IsExtensionNumber(int number) const {
+  return FindExtensionRangeContainingNumber(number) != nullptr;
+}
+
+inline bool Descriptor::IsReservedNumber(int number) const {
+  return FindReservedRangeContainingNumber(number) != nullptr;
+}
+
+inline bool Descriptor::IsReservedName(ConstStringParam name) const {
+  for (int i = 0; i < reserved_name_count(); i++) {
+    if (name == static_cast<ConstStringParam>(reserved_name(i))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Can't use PROTOBUF_DEFINE_ARRAY_ACCESSOR because reserved_names_ is actually
+// an array of pointers rather than the usual array of objects.
+inline const std::string& Descriptor::reserved_name(int index) const {
+  return *reserved_names_[index];
+}
+
+inline bool EnumDescriptor::IsReservedNumber(int number) const {
+  return FindReservedRangeContainingNumber(number) != nullptr;
+}
+
+inline bool EnumDescriptor::IsReservedName(ConstStringParam name) const {
+  for (int i = 0; i < reserved_name_count(); i++) {
+    if (name == static_cast<ConstStringParam>(reserved_name(i))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Can't use PROTOBUF_DEFINE_ARRAY_ACCESSOR because reserved_names_ is actually
+// an array of pointers rather than the usual array of objects.
+inline const std::string& EnumDescriptor::reserved_name(int index) const {
+  return *reserved_names_[index];
+}
+
+inline const std::string& FieldDescriptor::lowercase_name() const {
+  return all_names_[lowercase_name_index_];
+}
+
+inline const std::string& FieldDescriptor::camelcase_name() const {
+  return all_names_[camelcase_name_index_];
+}
+
+inline const std::string& FieldDescriptor::json_name() const {
+  return all_names_[json_name_index_];
+}
+
+inline const OneofDescriptor* FieldDescriptor::containing_oneof() const {
+  return is_oneof_ ? scope_.containing_oneof : nullptr;
+}
+
+inline int FieldDescriptor::index_in_oneof() const {
+  GOOGLE_DCHECK(is_oneof_);
+  return static_cast<int>(this - scope_.containing_oneof->field(0));
+}
+
+inline const Descriptor* FieldDescriptor::extension_scope() const {
+  GOOGLE_CHECK(is_extension_);
+  return scope_.extension_scope;
+}
+
+inline FieldDescriptor::Label FieldDescriptor::label() const {
+  return static_cast<Label>(label_);
+}
+
+inline FieldDescriptor::Type FieldDescriptor::type() const {
+  if (type_once_) {
+    internal::call_once(*type_once_, &FieldDescriptor::TypeOnceInit, this);
+  }
+  return static_cast<Type>(type_);
+}
+
+inline bool FieldDescriptor::is_required() const {
+  return label() == LABEL_REQUIRED;
+}
+
+inline bool FieldDescriptor::is_optional() const {
+  return label() == LABEL_OPTIONAL;
+}
+
+inline bool FieldDescriptor::is_repeated() const {
+  return label() == LABEL_REPEATED;
+}
+
+inline bool FieldDescriptor::is_packable() const {
+  return is_repeated() && IsTypePackable(type());
+}
+
+inline bool FieldDescriptor::is_map() const {
+  return type() == TYPE_MESSAGE && is_map_message_type();
+}
+
+inline bool FieldDescriptor::has_optional_keyword() const {
+  return proto3_optional_ ||
+         (file()->syntax() == FileDescriptor::SYNTAX_PROTO2 && is_optional() &&
+          !containing_oneof());
+}
+
+inline const OneofDescriptor* FieldDescriptor::real_containing_oneof() const {
+  auto* oneof = containing_oneof();
+  return oneof && !oneof->is_synthetic() ? oneof : nullptr;
+}
+
+inline bool FieldDescriptor::has_presence() const {
+  if (is_repeated()) return false;
+  return cpp_type() == CPPTYPE_MESSAGE || containing_oneof() ||
+         file()->syntax() == FileDescriptor::SYNTAX_PROTO2;
+}
+
+// To save space, index() is computed by looking at the descriptor's position
+// in the parent's array of children.
+inline int FieldDescriptor::index() const {
+  if (!is_extension_) {
+    return static_cast<int>(this - containing_type()->fields_);
+  } else if (extension_scope() != nullptr) {
+    return static_cast<int>(this - extension_scope()->extensions_);
+  } else {
+    return static_cast<int>(this - file_->extensions_);
+  }
+}
+
+inline int Descriptor::index() const {
+  if (containing_type_ == nullptr) {
+    return static_cast<int>(this - file_->message_types_);
+  } else {
+    return static_cast<int>(this - containing_type_->nested_types_);
+  }
+}
+
+inline const FileDescriptor* OneofDescriptor::file() const {
+  return containing_type()->file();
+}
+
+inline int OneofDescriptor::index() const {
+  return static_cast<int>(this - containing_type_->oneof_decls_);
+}
+
+inline bool OneofDescriptor::is_synthetic() const {
+  return field_count() == 1 && field(0)->proto3_optional_;
+}
+
+inline int EnumDescriptor::index() const {
+  if (containing_type_ == nullptr) {
+    return static_cast<int>(this - file_->enum_types_);
+  } else {
+    return static_cast<int>(this - containing_type_->enum_types_);
+  }
+}
+
+inline const FileDescriptor* EnumValueDescriptor::file() const {
+  return type()->file();
+}
+
+inline int EnumValueDescriptor::index() const {
+  return static_cast<int>(this - type_->values_);
+}
+
+inline int ServiceDescriptor::index() const {
+  return static_cast<int>(this - file_->services_);
+}
+
+inline const FileDescriptor* MethodDescriptor::file() const {
+  return service()->file();
+}
+
+inline int MethodDescriptor::index() const {
+  return static_cast<int>(this - service_->methods_);
+}
+
+inline const char* FieldDescriptor::type_name() const {
+  return kTypeToName[type()];
+}
+
+inline FieldDescriptor::CppType FieldDescriptor::cpp_type() const {
+  return kTypeToCppTypeMap[type()];
+}
+
+inline const char* FieldDescriptor::cpp_type_name() const {
+  return kCppTypeToName[kTypeToCppTypeMap[type()]];
+}
+
+inline FieldDescriptor::CppType FieldDescriptor::TypeToCppType(Type type) {
+  return kTypeToCppTypeMap[type];
+}
+
+inline const char* FieldDescriptor::TypeName(Type type) {
+  return kTypeToName[type];
+}
+
+inline const char* FieldDescriptor::CppTypeName(CppType cpp_type) {
+  return kCppTypeToName[cpp_type];
+}
+
+inline bool FieldDescriptor::IsTypePackable(Type field_type) {
+  return (field_type != FieldDescriptor::TYPE_STRING &&
+          field_type != FieldDescriptor::TYPE_GROUP &&
+          field_type != FieldDescriptor::TYPE_MESSAGE &&
+          field_type != FieldDescriptor::TYPE_BYTES);
+}
+
+inline const FileDescriptor* FileDescriptor::public_dependency(
+    int index) const {
+  return dependency(public_dependencies_[index]);
+}
+
+inline const FileDescriptor* FileDescriptor::weak_dependency(int index) const {
+  return dependency(weak_dependencies_[index]);
+}
+
+inline FileDescriptor::Syntax FileDescriptor::syntax() const {
+  return static_cast<Syntax>(syntax_);
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#undef PROTOBUF_INTERNAL_CHECK_CLASS_SIZE
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_DESCRIPTOR_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/descriptor.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/descriptor.pb.h
new file mode 100644
index 0000000..9ae046a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/descriptor.pb.h
@@ -0,0 +1,14821 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/descriptor.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fdescriptor_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fdescriptor_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/generated_enum_reflection.h>
+#include <google/protobuf/unknown_field_set.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fdescriptor_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2fdescriptor_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2fdescriptor_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class DescriptorProto;
+struct DescriptorProtoDefaultTypeInternal;
+PROTOBUF_EXPORT extern DescriptorProtoDefaultTypeInternal _DescriptorProto_default_instance_;
+class DescriptorProto_ExtensionRange;
+struct DescriptorProto_ExtensionRangeDefaultTypeInternal;
+PROTOBUF_EXPORT extern DescriptorProto_ExtensionRangeDefaultTypeInternal _DescriptorProto_ExtensionRange_default_instance_;
+class DescriptorProto_ReservedRange;
+struct DescriptorProto_ReservedRangeDefaultTypeInternal;
+PROTOBUF_EXPORT extern DescriptorProto_ReservedRangeDefaultTypeInternal _DescriptorProto_ReservedRange_default_instance_;
+class EnumDescriptorProto;
+struct EnumDescriptorProtoDefaultTypeInternal;
+PROTOBUF_EXPORT extern EnumDescriptorProtoDefaultTypeInternal _EnumDescriptorProto_default_instance_;
+class EnumDescriptorProto_EnumReservedRange;
+struct EnumDescriptorProto_EnumReservedRangeDefaultTypeInternal;
+PROTOBUF_EXPORT extern EnumDescriptorProto_EnumReservedRangeDefaultTypeInternal _EnumDescriptorProto_EnumReservedRange_default_instance_;
+class EnumOptions;
+struct EnumOptionsDefaultTypeInternal;
+PROTOBUF_EXPORT extern EnumOptionsDefaultTypeInternal _EnumOptions_default_instance_;
+class EnumValueDescriptorProto;
+struct EnumValueDescriptorProtoDefaultTypeInternal;
+PROTOBUF_EXPORT extern EnumValueDescriptorProtoDefaultTypeInternal _EnumValueDescriptorProto_default_instance_;
+class EnumValueOptions;
+struct EnumValueOptionsDefaultTypeInternal;
+PROTOBUF_EXPORT extern EnumValueOptionsDefaultTypeInternal _EnumValueOptions_default_instance_;
+class ExtensionRangeOptions;
+struct ExtensionRangeOptionsDefaultTypeInternal;
+PROTOBUF_EXPORT extern ExtensionRangeOptionsDefaultTypeInternal _ExtensionRangeOptions_default_instance_;
+class FieldDescriptorProto;
+struct FieldDescriptorProtoDefaultTypeInternal;
+PROTOBUF_EXPORT extern FieldDescriptorProtoDefaultTypeInternal _FieldDescriptorProto_default_instance_;
+class FieldOptions;
+struct FieldOptionsDefaultTypeInternal;
+PROTOBUF_EXPORT extern FieldOptionsDefaultTypeInternal _FieldOptions_default_instance_;
+class FileDescriptorProto;
+struct FileDescriptorProtoDefaultTypeInternal;
+PROTOBUF_EXPORT extern FileDescriptorProtoDefaultTypeInternal _FileDescriptorProto_default_instance_;
+class FileDescriptorSet;
+struct FileDescriptorSetDefaultTypeInternal;
+PROTOBUF_EXPORT extern FileDescriptorSetDefaultTypeInternal _FileDescriptorSet_default_instance_;
+class FileOptions;
+struct FileOptionsDefaultTypeInternal;
+PROTOBUF_EXPORT extern FileOptionsDefaultTypeInternal _FileOptions_default_instance_;
+class GeneratedCodeInfo;
+struct GeneratedCodeInfoDefaultTypeInternal;
+PROTOBUF_EXPORT extern GeneratedCodeInfoDefaultTypeInternal _GeneratedCodeInfo_default_instance_;
+class GeneratedCodeInfo_Annotation;
+struct GeneratedCodeInfo_AnnotationDefaultTypeInternal;
+PROTOBUF_EXPORT extern GeneratedCodeInfo_AnnotationDefaultTypeInternal _GeneratedCodeInfo_Annotation_default_instance_;
+class MessageOptions;
+struct MessageOptionsDefaultTypeInternal;
+PROTOBUF_EXPORT extern MessageOptionsDefaultTypeInternal _MessageOptions_default_instance_;
+class MethodDescriptorProto;
+struct MethodDescriptorProtoDefaultTypeInternal;
+PROTOBUF_EXPORT extern MethodDescriptorProtoDefaultTypeInternal _MethodDescriptorProto_default_instance_;
+class MethodOptions;
+struct MethodOptionsDefaultTypeInternal;
+PROTOBUF_EXPORT extern MethodOptionsDefaultTypeInternal _MethodOptions_default_instance_;
+class OneofDescriptorProto;
+struct OneofDescriptorProtoDefaultTypeInternal;
+PROTOBUF_EXPORT extern OneofDescriptorProtoDefaultTypeInternal _OneofDescriptorProto_default_instance_;
+class OneofOptions;
+struct OneofOptionsDefaultTypeInternal;
+PROTOBUF_EXPORT extern OneofOptionsDefaultTypeInternal _OneofOptions_default_instance_;
+class ServiceDescriptorProto;
+struct ServiceDescriptorProtoDefaultTypeInternal;
+PROTOBUF_EXPORT extern ServiceDescriptorProtoDefaultTypeInternal _ServiceDescriptorProto_default_instance_;
+class ServiceOptions;
+struct ServiceOptionsDefaultTypeInternal;
+PROTOBUF_EXPORT extern ServiceOptionsDefaultTypeInternal _ServiceOptions_default_instance_;
+class SourceCodeInfo;
+struct SourceCodeInfoDefaultTypeInternal;
+PROTOBUF_EXPORT extern SourceCodeInfoDefaultTypeInternal _SourceCodeInfo_default_instance_;
+class SourceCodeInfo_Location;
+struct SourceCodeInfo_LocationDefaultTypeInternal;
+PROTOBUF_EXPORT extern SourceCodeInfo_LocationDefaultTypeInternal _SourceCodeInfo_Location_default_instance_;
+class UninterpretedOption;
+struct UninterpretedOptionDefaultTypeInternal;
+PROTOBUF_EXPORT extern UninterpretedOptionDefaultTypeInternal _UninterpretedOption_default_instance_;
+class UninterpretedOption_NamePart;
+struct UninterpretedOption_NamePartDefaultTypeInternal;
+PROTOBUF_EXPORT extern UninterpretedOption_NamePartDefaultTypeInternal _UninterpretedOption_NamePart_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::DescriptorProto* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::DescriptorProto>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::EnumOptions* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::EnumOptions>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::EnumValueOptions>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::FieldOptions* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::FieldOptions>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::FileDescriptorProto>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::FileDescriptorSet* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::FileDescriptorSet>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::FileOptions* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::FileOptions>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::MessageOptions* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::MessageOptions>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::MethodOptions* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::MethodOptions>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::OneofOptions* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::OneofOptions>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::ServiceOptions* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::ServiceOptions>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::SourceCodeInfo>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::UninterpretedOption>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+enum FieldDescriptorProto_Type : int {
+  FieldDescriptorProto_Type_TYPE_DOUBLE = 1,
+  FieldDescriptorProto_Type_TYPE_FLOAT = 2,
+  FieldDescriptorProto_Type_TYPE_INT64 = 3,
+  FieldDescriptorProto_Type_TYPE_UINT64 = 4,
+  FieldDescriptorProto_Type_TYPE_INT32 = 5,
+  FieldDescriptorProto_Type_TYPE_FIXED64 = 6,
+  FieldDescriptorProto_Type_TYPE_FIXED32 = 7,
+  FieldDescriptorProto_Type_TYPE_BOOL = 8,
+  FieldDescriptorProto_Type_TYPE_STRING = 9,
+  FieldDescriptorProto_Type_TYPE_GROUP = 10,
+  FieldDescriptorProto_Type_TYPE_MESSAGE = 11,
+  FieldDescriptorProto_Type_TYPE_BYTES = 12,
+  FieldDescriptorProto_Type_TYPE_UINT32 = 13,
+  FieldDescriptorProto_Type_TYPE_ENUM = 14,
+  FieldDescriptorProto_Type_TYPE_SFIXED32 = 15,
+  FieldDescriptorProto_Type_TYPE_SFIXED64 = 16,
+  FieldDescriptorProto_Type_TYPE_SINT32 = 17,
+  FieldDescriptorProto_Type_TYPE_SINT64 = 18
+};
+PROTOBUF_EXPORT bool FieldDescriptorProto_Type_IsValid(int value);
+constexpr FieldDescriptorProto_Type FieldDescriptorProto_Type_Type_MIN = FieldDescriptorProto_Type_TYPE_DOUBLE;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto_Type_Type_MAX = FieldDescriptorProto_Type_TYPE_SINT64;
+constexpr int FieldDescriptorProto_Type_Type_ARRAYSIZE = FieldDescriptorProto_Type_Type_MAX + 1;
+
+PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* FieldDescriptorProto_Type_descriptor();
+template<typename T>
+inline const std::string& FieldDescriptorProto_Type_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, FieldDescriptorProto_Type>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function FieldDescriptorProto_Type_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    FieldDescriptorProto_Type_descriptor(), enum_t_value);
+}
+inline bool FieldDescriptorProto_Type_Parse(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FieldDescriptorProto_Type* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<FieldDescriptorProto_Type>(
+    FieldDescriptorProto_Type_descriptor(), name, value);
+}
+enum FieldDescriptorProto_Label : int {
+  FieldDescriptorProto_Label_LABEL_OPTIONAL = 1,
+  FieldDescriptorProto_Label_LABEL_REQUIRED = 2,
+  FieldDescriptorProto_Label_LABEL_REPEATED = 3
+};
+PROTOBUF_EXPORT bool FieldDescriptorProto_Label_IsValid(int value);
+constexpr FieldDescriptorProto_Label FieldDescriptorProto_Label_Label_MIN = FieldDescriptorProto_Label_LABEL_OPTIONAL;
+constexpr FieldDescriptorProto_Label FieldDescriptorProto_Label_Label_MAX = FieldDescriptorProto_Label_LABEL_REPEATED;
+constexpr int FieldDescriptorProto_Label_Label_ARRAYSIZE = FieldDescriptorProto_Label_Label_MAX + 1;
+
+PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* FieldDescriptorProto_Label_descriptor();
+template<typename T>
+inline const std::string& FieldDescriptorProto_Label_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, FieldDescriptorProto_Label>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function FieldDescriptorProto_Label_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    FieldDescriptorProto_Label_descriptor(), enum_t_value);
+}
+inline bool FieldDescriptorProto_Label_Parse(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FieldDescriptorProto_Label* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<FieldDescriptorProto_Label>(
+    FieldDescriptorProto_Label_descriptor(), name, value);
+}
+enum FileOptions_OptimizeMode : int {
+  FileOptions_OptimizeMode_SPEED = 1,
+  FileOptions_OptimizeMode_CODE_SIZE = 2,
+  FileOptions_OptimizeMode_LITE_RUNTIME = 3
+};
+PROTOBUF_EXPORT bool FileOptions_OptimizeMode_IsValid(int value);
+constexpr FileOptions_OptimizeMode FileOptions_OptimizeMode_OptimizeMode_MIN = FileOptions_OptimizeMode_SPEED;
+constexpr FileOptions_OptimizeMode FileOptions_OptimizeMode_OptimizeMode_MAX = FileOptions_OptimizeMode_LITE_RUNTIME;
+constexpr int FileOptions_OptimizeMode_OptimizeMode_ARRAYSIZE = FileOptions_OptimizeMode_OptimizeMode_MAX + 1;
+
+PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* FileOptions_OptimizeMode_descriptor();
+template<typename T>
+inline const std::string& FileOptions_OptimizeMode_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, FileOptions_OptimizeMode>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function FileOptions_OptimizeMode_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    FileOptions_OptimizeMode_descriptor(), enum_t_value);
+}
+inline bool FileOptions_OptimizeMode_Parse(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FileOptions_OptimizeMode* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<FileOptions_OptimizeMode>(
+    FileOptions_OptimizeMode_descriptor(), name, value);
+}
+enum FieldOptions_CType : int {
+  FieldOptions_CType_STRING = 0,
+  FieldOptions_CType_CORD = 1,
+  FieldOptions_CType_STRING_PIECE = 2
+};
+PROTOBUF_EXPORT bool FieldOptions_CType_IsValid(int value);
+constexpr FieldOptions_CType FieldOptions_CType_CType_MIN = FieldOptions_CType_STRING;
+constexpr FieldOptions_CType FieldOptions_CType_CType_MAX = FieldOptions_CType_STRING_PIECE;
+constexpr int FieldOptions_CType_CType_ARRAYSIZE = FieldOptions_CType_CType_MAX + 1;
+
+PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* FieldOptions_CType_descriptor();
+template<typename T>
+inline const std::string& FieldOptions_CType_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, FieldOptions_CType>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function FieldOptions_CType_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    FieldOptions_CType_descriptor(), enum_t_value);
+}
+inline bool FieldOptions_CType_Parse(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FieldOptions_CType* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<FieldOptions_CType>(
+    FieldOptions_CType_descriptor(), name, value);
+}
+enum FieldOptions_JSType : int {
+  FieldOptions_JSType_JS_NORMAL = 0,
+  FieldOptions_JSType_JS_STRING = 1,
+  FieldOptions_JSType_JS_NUMBER = 2
+};
+PROTOBUF_EXPORT bool FieldOptions_JSType_IsValid(int value);
+constexpr FieldOptions_JSType FieldOptions_JSType_JSType_MIN = FieldOptions_JSType_JS_NORMAL;
+constexpr FieldOptions_JSType FieldOptions_JSType_JSType_MAX = FieldOptions_JSType_JS_NUMBER;
+constexpr int FieldOptions_JSType_JSType_ARRAYSIZE = FieldOptions_JSType_JSType_MAX + 1;
+
+PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* FieldOptions_JSType_descriptor();
+template<typename T>
+inline const std::string& FieldOptions_JSType_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, FieldOptions_JSType>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function FieldOptions_JSType_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    FieldOptions_JSType_descriptor(), enum_t_value);
+}
+inline bool FieldOptions_JSType_Parse(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FieldOptions_JSType* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<FieldOptions_JSType>(
+    FieldOptions_JSType_descriptor(), name, value);
+}
+enum MethodOptions_IdempotencyLevel : int {
+  MethodOptions_IdempotencyLevel_IDEMPOTENCY_UNKNOWN = 0,
+  MethodOptions_IdempotencyLevel_NO_SIDE_EFFECTS = 1,
+  MethodOptions_IdempotencyLevel_IDEMPOTENT = 2
+};
+PROTOBUF_EXPORT bool MethodOptions_IdempotencyLevel_IsValid(int value);
+constexpr MethodOptions_IdempotencyLevel MethodOptions_IdempotencyLevel_IdempotencyLevel_MIN = MethodOptions_IdempotencyLevel_IDEMPOTENCY_UNKNOWN;
+constexpr MethodOptions_IdempotencyLevel MethodOptions_IdempotencyLevel_IdempotencyLevel_MAX = MethodOptions_IdempotencyLevel_IDEMPOTENT;
+constexpr int MethodOptions_IdempotencyLevel_IdempotencyLevel_ARRAYSIZE = MethodOptions_IdempotencyLevel_IdempotencyLevel_MAX + 1;
+
+PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MethodOptions_IdempotencyLevel_descriptor();
+template<typename T>
+inline const std::string& MethodOptions_IdempotencyLevel_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, MethodOptions_IdempotencyLevel>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function MethodOptions_IdempotencyLevel_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    MethodOptions_IdempotencyLevel_descriptor(), enum_t_value);
+}
+inline bool MethodOptions_IdempotencyLevel_Parse(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, MethodOptions_IdempotencyLevel* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<MethodOptions_IdempotencyLevel>(
+    MethodOptions_IdempotencyLevel_descriptor(), name, value);
+}
+// ===================================================================
+
+class PROTOBUF_EXPORT FileDescriptorSet final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.FileDescriptorSet) */ {
+ public:
+  inline FileDescriptorSet() : FileDescriptorSet(nullptr) {}
+  ~FileDescriptorSet() override;
+  explicit PROTOBUF_CONSTEXPR FileDescriptorSet(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  FileDescriptorSet(const FileDescriptorSet& from);
+  FileDescriptorSet(FileDescriptorSet&& from) noexcept
+    : FileDescriptorSet() {
+    *this = ::std::move(from);
+  }
+
+  inline FileDescriptorSet& operator=(const FileDescriptorSet& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline FileDescriptorSet& operator=(FileDescriptorSet&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const FileDescriptorSet& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const FileDescriptorSet* internal_default_instance() {
+    return reinterpret_cast<const FileDescriptorSet*>(
+               &_FileDescriptorSet_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(FileDescriptorSet& a, FileDescriptorSet& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(FileDescriptorSet* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(FileDescriptorSet* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  FileDescriptorSet* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<FileDescriptorSet>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const FileDescriptorSet& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const FileDescriptorSet& from) {
+    FileDescriptorSet::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(FileDescriptorSet* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.FileDescriptorSet";
+  }
+  protected:
+  explicit FileDescriptorSet(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kFileFieldNumber = 1,
+  };
+  // repeated .google.protobuf.FileDescriptorProto file = 1;
+  int file_size() const;
+  private:
+  int _internal_file_size() const;
+  public:
+  void clear_file();
+  ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto* mutable_file(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto >*
+      mutable_file();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto& _internal_file(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto* _internal_add_file();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto& file(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto* add_file();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto >&
+      file() const;
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.FileDescriptorSet)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto > file_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT FileDescriptorProto final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.FileDescriptorProto) */ {
+ public:
+  inline FileDescriptorProto() : FileDescriptorProto(nullptr) {}
+  ~FileDescriptorProto() override;
+  explicit PROTOBUF_CONSTEXPR FileDescriptorProto(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  FileDescriptorProto(const FileDescriptorProto& from);
+  FileDescriptorProto(FileDescriptorProto&& from) noexcept
+    : FileDescriptorProto() {
+    *this = ::std::move(from);
+  }
+
+  inline FileDescriptorProto& operator=(const FileDescriptorProto& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline FileDescriptorProto& operator=(FileDescriptorProto&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const FileDescriptorProto& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const FileDescriptorProto* internal_default_instance() {
+    return reinterpret_cast<const FileDescriptorProto*>(
+               &_FileDescriptorProto_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    1;
+
+  friend void swap(FileDescriptorProto& a, FileDescriptorProto& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(FileDescriptorProto* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(FileDescriptorProto* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  FileDescriptorProto* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<FileDescriptorProto>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const FileDescriptorProto& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const FileDescriptorProto& from) {
+    FileDescriptorProto::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(FileDescriptorProto* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.FileDescriptorProto";
+  }
+  protected:
+  explicit FileDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kDependencyFieldNumber = 3,
+    kMessageTypeFieldNumber = 4,
+    kEnumTypeFieldNumber = 5,
+    kServiceFieldNumber = 6,
+    kExtensionFieldNumber = 7,
+    kPublicDependencyFieldNumber = 10,
+    kWeakDependencyFieldNumber = 11,
+    kNameFieldNumber = 1,
+    kPackageFieldNumber = 2,
+    kSyntaxFieldNumber = 12,
+    kOptionsFieldNumber = 8,
+    kSourceCodeInfoFieldNumber = 9,
+  };
+  // repeated string dependency = 3;
+  int dependency_size() const;
+  private:
+  int _internal_dependency_size() const;
+  public:
+  void clear_dependency();
+  const std::string& dependency(int index) const;
+  std::string* mutable_dependency(int index);
+  void set_dependency(int index, const std::string& value);
+  void set_dependency(int index, std::string&& value);
+  void set_dependency(int index, const char* value);
+  void set_dependency(int index, const char* value, size_t size);
+  std::string* add_dependency();
+  void add_dependency(const std::string& value);
+  void add_dependency(std::string&& value);
+  void add_dependency(const char* value);
+  void add_dependency(const char* value, size_t size);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& dependency() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_dependency();
+  private:
+  const std::string& _internal_dependency(int index) const;
+  std::string* _internal_add_dependency();
+  public:
+
+  // repeated .google.protobuf.DescriptorProto message_type = 4;
+  int message_type_size() const;
+  private:
+  int _internal_message_type_size() const;
+  public:
+  void clear_message_type();
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto* mutable_message_type(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto >*
+      mutable_message_type();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::DescriptorProto& _internal_message_type(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto* _internal_add_message_type();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::DescriptorProto& message_type(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto* add_message_type();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto >&
+      message_type() const;
+
+  // repeated .google.protobuf.EnumDescriptorProto enum_type = 5;
+  int enum_type_size() const;
+  private:
+  int _internal_enum_type_size() const;
+  public:
+  void clear_enum_type();
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* mutable_enum_type(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto >*
+      mutable_enum_type();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto& _internal_enum_type(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* _internal_add_enum_type();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto& enum_type(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* add_enum_type();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto >&
+      enum_type() const;
+
+  // repeated .google.protobuf.ServiceDescriptorProto service = 6;
+  int service_size() const;
+  private:
+  int _internal_service_size() const;
+  public:
+  void clear_service();
+  ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto* mutable_service(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto >*
+      mutable_service();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto& _internal_service(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto* _internal_add_service();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto& service(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto* add_service();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto >&
+      service() const;
+
+  // repeated .google.protobuf.FieldDescriptorProto extension = 7;
+  int extension_size() const;
+  private:
+  int _internal_extension_size() const;
+  public:
+  void clear_extension();
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* mutable_extension(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >*
+      mutable_extension();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& _internal_extension(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* _internal_add_extension();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& extension(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* add_extension();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >&
+      extension() const;
+
+  // repeated int32 public_dependency = 10;
+  int public_dependency_size() const;
+  private:
+  int _internal_public_dependency_size() const;
+  public:
+  void clear_public_dependency();
+  private:
+  int32_t _internal_public_dependency(int index) const;
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+      _internal_public_dependency() const;
+  void _internal_add_public_dependency(int32_t value);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+      _internal_mutable_public_dependency();
+  public:
+  int32_t public_dependency(int index) const;
+  void set_public_dependency(int index, int32_t value);
+  void add_public_dependency(int32_t value);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+      public_dependency() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+      mutable_public_dependency();
+
+  // repeated int32 weak_dependency = 11;
+  int weak_dependency_size() const;
+  private:
+  int _internal_weak_dependency_size() const;
+  public:
+  void clear_weak_dependency();
+  private:
+  int32_t _internal_weak_dependency(int index) const;
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+      _internal_weak_dependency() const;
+  void _internal_add_weak_dependency(int32_t value);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+      _internal_mutable_weak_dependency();
+  public:
+  int32_t weak_dependency(int index) const;
+  void set_weak_dependency(int index, int32_t value);
+  void add_weak_dependency(int32_t value);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+      weak_dependency() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+      mutable_weak_dependency();
+
+  // optional string name = 1;
+  bool has_name() const;
+  private:
+  bool _internal_has_name() const;
+  public:
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // optional string package = 2;
+  bool has_package() const;
+  private:
+  bool _internal_has_package() const;
+  public:
+  void clear_package();
+  const std::string& package() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_package(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_package();
+  PROTOBUF_NODISCARD std::string* release_package();
+  void set_allocated_package(std::string* package);
+  private:
+  const std::string& _internal_package() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_package(const std::string& value);
+  std::string* _internal_mutable_package();
+  public:
+
+  // optional string syntax = 12;
+  bool has_syntax() const;
+  private:
+  bool _internal_has_syntax() const;
+  public:
+  void clear_syntax();
+  const std::string& syntax() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_syntax(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_syntax();
+  PROTOBUF_NODISCARD std::string* release_syntax();
+  void set_allocated_syntax(std::string* syntax);
+  private:
+  const std::string& _internal_syntax() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_syntax(const std::string& value);
+  std::string* _internal_mutable_syntax();
+  public:
+
+  // optional .google.protobuf.FileOptions options = 8;
+  bool has_options() const;
+  private:
+  bool _internal_has_options() const;
+  public:
+  void clear_options();
+  const ::PROTOBUF_NAMESPACE_ID::FileOptions& options() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::FileOptions* release_options();
+  ::PROTOBUF_NAMESPACE_ID::FileOptions* mutable_options();
+  void set_allocated_options(::PROTOBUF_NAMESPACE_ID::FileOptions* options);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::FileOptions& _internal_options() const;
+  ::PROTOBUF_NAMESPACE_ID::FileOptions* _internal_mutable_options();
+  public:
+  void unsafe_arena_set_allocated_options(
+      ::PROTOBUF_NAMESPACE_ID::FileOptions* options);
+  ::PROTOBUF_NAMESPACE_ID::FileOptions* unsafe_arena_release_options();
+
+  // optional .google.protobuf.SourceCodeInfo source_code_info = 9;
+  bool has_source_code_info() const;
+  private:
+  bool _internal_has_source_code_info() const;
+  public:
+  void clear_source_code_info();
+  const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo& source_code_info() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* release_source_code_info();
+  ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* mutable_source_code_info();
+  void set_allocated_source_code_info(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* source_code_info);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo& _internal_source_code_info() const;
+  ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* _internal_mutable_source_code_info();
+  public:
+  void unsafe_arena_set_allocated_source_code_info(
+      ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* source_code_info);
+  ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* unsafe_arena_release_source_code_info();
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.FileDescriptorProto)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> dependency_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto > message_type_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto > enum_type_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto > service_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto > extension_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > public_dependency_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > weak_dependency_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr package_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr syntax_;
+    ::PROTOBUF_NAMESPACE_ID::FileOptions* options_;
+    ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* source_code_info_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT DescriptorProto_ExtensionRange final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.DescriptorProto.ExtensionRange) */ {
+ public:
+  inline DescriptorProto_ExtensionRange() : DescriptorProto_ExtensionRange(nullptr) {}
+  ~DescriptorProto_ExtensionRange() override;
+  explicit PROTOBUF_CONSTEXPR DescriptorProto_ExtensionRange(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  DescriptorProto_ExtensionRange(const DescriptorProto_ExtensionRange& from);
+  DescriptorProto_ExtensionRange(DescriptorProto_ExtensionRange&& from) noexcept
+    : DescriptorProto_ExtensionRange() {
+    *this = ::std::move(from);
+  }
+
+  inline DescriptorProto_ExtensionRange& operator=(const DescriptorProto_ExtensionRange& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline DescriptorProto_ExtensionRange& operator=(DescriptorProto_ExtensionRange&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const DescriptorProto_ExtensionRange& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const DescriptorProto_ExtensionRange* internal_default_instance() {
+    return reinterpret_cast<const DescriptorProto_ExtensionRange*>(
+               &_DescriptorProto_ExtensionRange_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    2;
+
+  friend void swap(DescriptorProto_ExtensionRange& a, DescriptorProto_ExtensionRange& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(DescriptorProto_ExtensionRange* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(DescriptorProto_ExtensionRange* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  DescriptorProto_ExtensionRange* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<DescriptorProto_ExtensionRange>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const DescriptorProto_ExtensionRange& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const DescriptorProto_ExtensionRange& from) {
+    DescriptorProto_ExtensionRange::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(DescriptorProto_ExtensionRange* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.DescriptorProto.ExtensionRange";
+  }
+  protected:
+  explicit DescriptorProto_ExtensionRange(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kOptionsFieldNumber = 3,
+    kStartFieldNumber = 1,
+    kEndFieldNumber = 2,
+  };
+  // optional .google.protobuf.ExtensionRangeOptions options = 3;
+  bool has_options() const;
+  private:
+  bool _internal_has_options() const;
+  public:
+  void clear_options();
+  const ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions& options() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* release_options();
+  ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* mutable_options();
+  void set_allocated_options(::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* options);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions& _internal_options() const;
+  ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* _internal_mutable_options();
+  public:
+  void unsafe_arena_set_allocated_options(
+      ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* options);
+  ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* unsafe_arena_release_options();
+
+  // optional int32 start = 1;
+  bool has_start() const;
+  private:
+  bool _internal_has_start() const;
+  public:
+  void clear_start();
+  int32_t start() const;
+  void set_start(int32_t value);
+  private:
+  int32_t _internal_start() const;
+  void _internal_set_start(int32_t value);
+  public:
+
+  // optional int32 end = 2;
+  bool has_end() const;
+  private:
+  bool _internal_has_end() const;
+  public:
+  void clear_end();
+  int32_t end() const;
+  void set_end(int32_t value);
+  private:
+  int32_t _internal_end() const;
+  void _internal_set_end(int32_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto.ExtensionRange)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* options_;
+    int32_t start_;
+    int32_t end_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT DescriptorProto_ReservedRange final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.DescriptorProto.ReservedRange) */ {
+ public:
+  inline DescriptorProto_ReservedRange() : DescriptorProto_ReservedRange(nullptr) {}
+  ~DescriptorProto_ReservedRange() override;
+  explicit PROTOBUF_CONSTEXPR DescriptorProto_ReservedRange(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  DescriptorProto_ReservedRange(const DescriptorProto_ReservedRange& from);
+  DescriptorProto_ReservedRange(DescriptorProto_ReservedRange&& from) noexcept
+    : DescriptorProto_ReservedRange() {
+    *this = ::std::move(from);
+  }
+
+  inline DescriptorProto_ReservedRange& operator=(const DescriptorProto_ReservedRange& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline DescriptorProto_ReservedRange& operator=(DescriptorProto_ReservedRange&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const DescriptorProto_ReservedRange& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const DescriptorProto_ReservedRange* internal_default_instance() {
+    return reinterpret_cast<const DescriptorProto_ReservedRange*>(
+               &_DescriptorProto_ReservedRange_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    3;
+
+  friend void swap(DescriptorProto_ReservedRange& a, DescriptorProto_ReservedRange& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(DescriptorProto_ReservedRange* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(DescriptorProto_ReservedRange* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  DescriptorProto_ReservedRange* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<DescriptorProto_ReservedRange>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const DescriptorProto_ReservedRange& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const DescriptorProto_ReservedRange& from) {
+    DescriptorProto_ReservedRange::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(DescriptorProto_ReservedRange* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.DescriptorProto.ReservedRange";
+  }
+  protected:
+  explicit DescriptorProto_ReservedRange(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kStartFieldNumber = 1,
+    kEndFieldNumber = 2,
+  };
+  // optional int32 start = 1;
+  bool has_start() const;
+  private:
+  bool _internal_has_start() const;
+  public:
+  void clear_start();
+  int32_t start() const;
+  void set_start(int32_t value);
+  private:
+  int32_t _internal_start() const;
+  void _internal_set_start(int32_t value);
+  public:
+
+  // optional int32 end = 2;
+  bool has_end() const;
+  private:
+  bool _internal_has_end() const;
+  public:
+  void clear_end();
+  int32_t end() const;
+  void set_end(int32_t value);
+  private:
+  int32_t _internal_end() const;
+  void _internal_set_end(int32_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto.ReservedRange)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    int32_t start_;
+    int32_t end_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT DescriptorProto final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.DescriptorProto) */ {
+ public:
+  inline DescriptorProto() : DescriptorProto(nullptr) {}
+  ~DescriptorProto() override;
+  explicit PROTOBUF_CONSTEXPR DescriptorProto(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  DescriptorProto(const DescriptorProto& from);
+  DescriptorProto(DescriptorProto&& from) noexcept
+    : DescriptorProto() {
+    *this = ::std::move(from);
+  }
+
+  inline DescriptorProto& operator=(const DescriptorProto& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline DescriptorProto& operator=(DescriptorProto&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const DescriptorProto& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const DescriptorProto* internal_default_instance() {
+    return reinterpret_cast<const DescriptorProto*>(
+               &_DescriptorProto_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    4;
+
+  friend void swap(DescriptorProto& a, DescriptorProto& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(DescriptorProto* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(DescriptorProto* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  DescriptorProto* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<DescriptorProto>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const DescriptorProto& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const DescriptorProto& from) {
+    DescriptorProto::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(DescriptorProto* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.DescriptorProto";
+  }
+  protected:
+  explicit DescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef DescriptorProto_ExtensionRange ExtensionRange;
+  typedef DescriptorProto_ReservedRange ReservedRange;
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kFieldFieldNumber = 2,
+    kNestedTypeFieldNumber = 3,
+    kEnumTypeFieldNumber = 4,
+    kExtensionRangeFieldNumber = 5,
+    kExtensionFieldNumber = 6,
+    kOneofDeclFieldNumber = 8,
+    kReservedRangeFieldNumber = 9,
+    kReservedNameFieldNumber = 10,
+    kNameFieldNumber = 1,
+    kOptionsFieldNumber = 7,
+  };
+  // repeated .google.protobuf.FieldDescriptorProto field = 2;
+  int field_size() const;
+  private:
+  int _internal_field_size() const;
+  public:
+  void clear_field();
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* mutable_field(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >*
+      mutable_field();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& _internal_field(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* _internal_add_field();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& field(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* add_field();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >&
+      field() const;
+
+  // repeated .google.protobuf.DescriptorProto nested_type = 3;
+  int nested_type_size() const;
+  private:
+  int _internal_nested_type_size() const;
+  public:
+  void clear_nested_type();
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto* mutable_nested_type(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto >*
+      mutable_nested_type();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::DescriptorProto& _internal_nested_type(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto* _internal_add_nested_type();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::DescriptorProto& nested_type(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto* add_nested_type();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto >&
+      nested_type() const;
+
+  // repeated .google.protobuf.EnumDescriptorProto enum_type = 4;
+  int enum_type_size() const;
+  private:
+  int _internal_enum_type_size() const;
+  public:
+  void clear_enum_type();
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* mutable_enum_type(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto >*
+      mutable_enum_type();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto& _internal_enum_type(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* _internal_add_enum_type();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto& enum_type(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* add_enum_type();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto >&
+      enum_type() const;
+
+  // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5;
+  int extension_range_size() const;
+  private:
+  int _internal_extension_range_size() const;
+  public:
+  void clear_extension_range();
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange* mutable_extension_range(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange >*
+      mutable_extension_range();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange& _internal_extension_range(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange* _internal_add_extension_range();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange& extension_range(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange* add_extension_range();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange >&
+      extension_range() const;
+
+  // repeated .google.protobuf.FieldDescriptorProto extension = 6;
+  int extension_size() const;
+  private:
+  int _internal_extension_size() const;
+  public:
+  void clear_extension();
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* mutable_extension(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >*
+      mutable_extension();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& _internal_extension(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* _internal_add_extension();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& extension(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* add_extension();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >&
+      extension() const;
+
+  // repeated .google.protobuf.OneofDescriptorProto oneof_decl = 8;
+  int oneof_decl_size() const;
+  private:
+  int _internal_oneof_decl_size() const;
+  public:
+  void clear_oneof_decl();
+  ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto* mutable_oneof_decl(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto >*
+      mutable_oneof_decl();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto& _internal_oneof_decl(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto* _internal_add_oneof_decl();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto& oneof_decl(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto* add_oneof_decl();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto >&
+      oneof_decl() const;
+
+  // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9;
+  int reserved_range_size() const;
+  private:
+  int _internal_reserved_range_size() const;
+  public:
+  void clear_reserved_range();
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange* mutable_reserved_range(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange >*
+      mutable_reserved_range();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange& _internal_reserved_range(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange* _internal_add_reserved_range();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange& reserved_range(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange* add_reserved_range();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange >&
+      reserved_range() const;
+
+  // repeated string reserved_name = 10;
+  int reserved_name_size() const;
+  private:
+  int _internal_reserved_name_size() const;
+  public:
+  void clear_reserved_name();
+  const std::string& reserved_name(int index) const;
+  std::string* mutable_reserved_name(int index);
+  void set_reserved_name(int index, const std::string& value);
+  void set_reserved_name(int index, std::string&& value);
+  void set_reserved_name(int index, const char* value);
+  void set_reserved_name(int index, const char* value, size_t size);
+  std::string* add_reserved_name();
+  void add_reserved_name(const std::string& value);
+  void add_reserved_name(std::string&& value);
+  void add_reserved_name(const char* value);
+  void add_reserved_name(const char* value, size_t size);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& reserved_name() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_reserved_name();
+  private:
+  const std::string& _internal_reserved_name(int index) const;
+  std::string* _internal_add_reserved_name();
+  public:
+
+  // optional string name = 1;
+  bool has_name() const;
+  private:
+  bool _internal_has_name() const;
+  public:
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // optional .google.protobuf.MessageOptions options = 7;
+  bool has_options() const;
+  private:
+  bool _internal_has_options() const;
+  public:
+  void clear_options();
+  const ::PROTOBUF_NAMESPACE_ID::MessageOptions& options() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::MessageOptions* release_options();
+  ::PROTOBUF_NAMESPACE_ID::MessageOptions* mutable_options();
+  void set_allocated_options(::PROTOBUF_NAMESPACE_ID::MessageOptions* options);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::MessageOptions& _internal_options() const;
+  ::PROTOBUF_NAMESPACE_ID::MessageOptions* _internal_mutable_options();
+  public:
+  void unsafe_arena_set_allocated_options(
+      ::PROTOBUF_NAMESPACE_ID::MessageOptions* options);
+  ::PROTOBUF_NAMESPACE_ID::MessageOptions* unsafe_arena_release_options();
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto > field_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto > nested_type_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto > enum_type_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange > extension_range_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto > extension_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto > oneof_decl_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange > reserved_range_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> reserved_name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::MessageOptions* options_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT ExtensionRangeOptions final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.ExtensionRangeOptions) */ {
+ public:
+  inline ExtensionRangeOptions() : ExtensionRangeOptions(nullptr) {}
+  ~ExtensionRangeOptions() override;
+  explicit PROTOBUF_CONSTEXPR ExtensionRangeOptions(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  ExtensionRangeOptions(const ExtensionRangeOptions& from);
+  ExtensionRangeOptions(ExtensionRangeOptions&& from) noexcept
+    : ExtensionRangeOptions() {
+    *this = ::std::move(from);
+  }
+
+  inline ExtensionRangeOptions& operator=(const ExtensionRangeOptions& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline ExtensionRangeOptions& operator=(ExtensionRangeOptions&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const ExtensionRangeOptions& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const ExtensionRangeOptions* internal_default_instance() {
+    return reinterpret_cast<const ExtensionRangeOptions*>(
+               &_ExtensionRangeOptions_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    5;
+
+  friend void swap(ExtensionRangeOptions& a, ExtensionRangeOptions& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(ExtensionRangeOptions* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(ExtensionRangeOptions* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  ExtensionRangeOptions* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<ExtensionRangeOptions>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const ExtensionRangeOptions& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const ExtensionRangeOptions& from) {
+    ExtensionRangeOptions::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(ExtensionRangeOptions* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.ExtensionRangeOptions";
+  }
+  protected:
+  explicit ExtensionRangeOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kUninterpretedOptionFieldNumber = 999,
+  };
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  int uninterpreted_option_size() const;
+  private:
+  int _internal_uninterpreted_option_size() const;
+  public:
+  void clear_uninterpreted_option();
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* mutable_uninterpreted_option(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+      mutable_uninterpreted_option();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& _internal_uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _internal_add_uninterpreted_option();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* add_uninterpreted_option();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+      uninterpreted_option() const;
+
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline bool HasExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.Has(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void ClearExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    _impl_._extensions_.ClearExtension(id.number());
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline int ExtensionSize(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.ExtensionSize(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_,
+                                  id.default_value());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Mutable(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), _field_type, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::SetAllocated(id.number(), _field_type, value,
+                                    &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void UnsafeArenaSetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::UnsafeArenaSetAllocated(id.number(), _field_type,
+                                               value, &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  PROTOBUF_NODISCARD inline
+      typename _proto_TypeTraits::Singular::MutableType
+      ReleaseExtension(
+          const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+              ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Release(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType
+  UnsafeArenaReleaseExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::UnsafeArenaRelease(id.number(), _field_type,
+                                                 &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_, index);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) {
+
+    return _proto_TypeTraits::Mutable(id.number(), index, &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index, typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), index, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    typename _proto_TypeTraits::Repeated::MutableType to_add =
+        _proto_TypeTraits::Add(id.number(), _field_type, &_impl_._extensions_);
+
+    return to_add;
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Add(id.number(), _field_type, _is_packed, value,
+                           &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline const typename _proto_TypeTraits::Repeated::RepeatedFieldType&
+  GetRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::GetRepeated(id.number(), _impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::RepeatedFieldType*
+  MutableRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ExtensionRangeOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::MutableRepeated(id.number(), _field_type,
+                                              _is_packed, &_impl_._extensions_);
+  }
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.ExtensionRangeOptions)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
+
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption > uninterpreted_option_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT FieldDescriptorProto final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.FieldDescriptorProto) */ {
+ public:
+  inline FieldDescriptorProto() : FieldDescriptorProto(nullptr) {}
+  ~FieldDescriptorProto() override;
+  explicit PROTOBUF_CONSTEXPR FieldDescriptorProto(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  FieldDescriptorProto(const FieldDescriptorProto& from);
+  FieldDescriptorProto(FieldDescriptorProto&& from) noexcept
+    : FieldDescriptorProto() {
+    *this = ::std::move(from);
+  }
+
+  inline FieldDescriptorProto& operator=(const FieldDescriptorProto& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline FieldDescriptorProto& operator=(FieldDescriptorProto&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const FieldDescriptorProto& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const FieldDescriptorProto* internal_default_instance() {
+    return reinterpret_cast<const FieldDescriptorProto*>(
+               &_FieldDescriptorProto_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    6;
+
+  friend void swap(FieldDescriptorProto& a, FieldDescriptorProto& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(FieldDescriptorProto* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(FieldDescriptorProto* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  FieldDescriptorProto* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<FieldDescriptorProto>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const FieldDescriptorProto& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const FieldDescriptorProto& from) {
+    FieldDescriptorProto::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(FieldDescriptorProto* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.FieldDescriptorProto";
+  }
+  protected:
+  explicit FieldDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef FieldDescriptorProto_Type Type;
+  static constexpr Type TYPE_DOUBLE =
+    FieldDescriptorProto_Type_TYPE_DOUBLE;
+  static constexpr Type TYPE_FLOAT =
+    FieldDescriptorProto_Type_TYPE_FLOAT;
+  static constexpr Type TYPE_INT64 =
+    FieldDescriptorProto_Type_TYPE_INT64;
+  static constexpr Type TYPE_UINT64 =
+    FieldDescriptorProto_Type_TYPE_UINT64;
+  static constexpr Type TYPE_INT32 =
+    FieldDescriptorProto_Type_TYPE_INT32;
+  static constexpr Type TYPE_FIXED64 =
+    FieldDescriptorProto_Type_TYPE_FIXED64;
+  static constexpr Type TYPE_FIXED32 =
+    FieldDescriptorProto_Type_TYPE_FIXED32;
+  static constexpr Type TYPE_BOOL =
+    FieldDescriptorProto_Type_TYPE_BOOL;
+  static constexpr Type TYPE_STRING =
+    FieldDescriptorProto_Type_TYPE_STRING;
+  static constexpr Type TYPE_GROUP =
+    FieldDescriptorProto_Type_TYPE_GROUP;
+  static constexpr Type TYPE_MESSAGE =
+    FieldDescriptorProto_Type_TYPE_MESSAGE;
+  static constexpr Type TYPE_BYTES =
+    FieldDescriptorProto_Type_TYPE_BYTES;
+  static constexpr Type TYPE_UINT32 =
+    FieldDescriptorProto_Type_TYPE_UINT32;
+  static constexpr Type TYPE_ENUM =
+    FieldDescriptorProto_Type_TYPE_ENUM;
+  static constexpr Type TYPE_SFIXED32 =
+    FieldDescriptorProto_Type_TYPE_SFIXED32;
+  static constexpr Type TYPE_SFIXED64 =
+    FieldDescriptorProto_Type_TYPE_SFIXED64;
+  static constexpr Type TYPE_SINT32 =
+    FieldDescriptorProto_Type_TYPE_SINT32;
+  static constexpr Type TYPE_SINT64 =
+    FieldDescriptorProto_Type_TYPE_SINT64;
+  static inline bool Type_IsValid(int value) {
+    return FieldDescriptorProto_Type_IsValid(value);
+  }
+  static constexpr Type Type_MIN =
+    FieldDescriptorProto_Type_Type_MIN;
+  static constexpr Type Type_MAX =
+    FieldDescriptorProto_Type_Type_MAX;
+  static constexpr int Type_ARRAYSIZE =
+    FieldDescriptorProto_Type_Type_ARRAYSIZE;
+  static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
+  Type_descriptor() {
+    return FieldDescriptorProto_Type_descriptor();
+  }
+  template<typename T>
+  static inline const std::string& Type_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, Type>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function Type_Name.");
+    return FieldDescriptorProto_Type_Name(enum_t_value);
+  }
+  static inline bool Type_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+      Type* value) {
+    return FieldDescriptorProto_Type_Parse(name, value);
+  }
+
+  typedef FieldDescriptorProto_Label Label;
+  static constexpr Label LABEL_OPTIONAL =
+    FieldDescriptorProto_Label_LABEL_OPTIONAL;
+  static constexpr Label LABEL_REQUIRED =
+    FieldDescriptorProto_Label_LABEL_REQUIRED;
+  static constexpr Label LABEL_REPEATED =
+    FieldDescriptorProto_Label_LABEL_REPEATED;
+  static inline bool Label_IsValid(int value) {
+    return FieldDescriptorProto_Label_IsValid(value);
+  }
+  static constexpr Label Label_MIN =
+    FieldDescriptorProto_Label_Label_MIN;
+  static constexpr Label Label_MAX =
+    FieldDescriptorProto_Label_Label_MAX;
+  static constexpr int Label_ARRAYSIZE =
+    FieldDescriptorProto_Label_Label_ARRAYSIZE;
+  static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
+  Label_descriptor() {
+    return FieldDescriptorProto_Label_descriptor();
+  }
+  template<typename T>
+  static inline const std::string& Label_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, Label>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function Label_Name.");
+    return FieldDescriptorProto_Label_Name(enum_t_value);
+  }
+  static inline bool Label_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+      Label* value) {
+    return FieldDescriptorProto_Label_Parse(name, value);
+  }
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNameFieldNumber = 1,
+    kExtendeeFieldNumber = 2,
+    kTypeNameFieldNumber = 6,
+    kDefaultValueFieldNumber = 7,
+    kJsonNameFieldNumber = 10,
+    kOptionsFieldNumber = 8,
+    kNumberFieldNumber = 3,
+    kOneofIndexFieldNumber = 9,
+    kProto3OptionalFieldNumber = 17,
+    kLabelFieldNumber = 4,
+    kTypeFieldNumber = 5,
+  };
+  // optional string name = 1;
+  bool has_name() const;
+  private:
+  bool _internal_has_name() const;
+  public:
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // optional string extendee = 2;
+  bool has_extendee() const;
+  private:
+  bool _internal_has_extendee() const;
+  public:
+  void clear_extendee();
+  const std::string& extendee() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_extendee(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_extendee();
+  PROTOBUF_NODISCARD std::string* release_extendee();
+  void set_allocated_extendee(std::string* extendee);
+  private:
+  const std::string& _internal_extendee() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_extendee(const std::string& value);
+  std::string* _internal_mutable_extendee();
+  public:
+
+  // optional string type_name = 6;
+  bool has_type_name() const;
+  private:
+  bool _internal_has_type_name() const;
+  public:
+  void clear_type_name();
+  const std::string& type_name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_type_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_type_name();
+  PROTOBUF_NODISCARD std::string* release_type_name();
+  void set_allocated_type_name(std::string* type_name);
+  private:
+  const std::string& _internal_type_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_type_name(const std::string& value);
+  std::string* _internal_mutable_type_name();
+  public:
+
+  // optional string default_value = 7;
+  bool has_default_value() const;
+  private:
+  bool _internal_has_default_value() const;
+  public:
+  void clear_default_value();
+  const std::string& default_value() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_default_value(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_default_value();
+  PROTOBUF_NODISCARD std::string* release_default_value();
+  void set_allocated_default_value(std::string* default_value);
+  private:
+  const std::string& _internal_default_value() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_default_value(const std::string& value);
+  std::string* _internal_mutable_default_value();
+  public:
+
+  // optional string json_name = 10;
+  bool has_json_name() const;
+  private:
+  bool _internal_has_json_name() const;
+  public:
+  void clear_json_name();
+  const std::string& json_name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_json_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_json_name();
+  PROTOBUF_NODISCARD std::string* release_json_name();
+  void set_allocated_json_name(std::string* json_name);
+  private:
+  const std::string& _internal_json_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_json_name(const std::string& value);
+  std::string* _internal_mutable_json_name();
+  public:
+
+  // optional .google.protobuf.FieldOptions options = 8;
+  bool has_options() const;
+  private:
+  bool _internal_has_options() const;
+  public:
+  void clear_options();
+  const ::PROTOBUF_NAMESPACE_ID::FieldOptions& options() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::FieldOptions* release_options();
+  ::PROTOBUF_NAMESPACE_ID::FieldOptions* mutable_options();
+  void set_allocated_options(::PROTOBUF_NAMESPACE_ID::FieldOptions* options);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::FieldOptions& _internal_options() const;
+  ::PROTOBUF_NAMESPACE_ID::FieldOptions* _internal_mutable_options();
+  public:
+  void unsafe_arena_set_allocated_options(
+      ::PROTOBUF_NAMESPACE_ID::FieldOptions* options);
+  ::PROTOBUF_NAMESPACE_ID::FieldOptions* unsafe_arena_release_options();
+
+  // optional int32 number = 3;
+  bool has_number() const;
+  private:
+  bool _internal_has_number() const;
+  public:
+  void clear_number();
+  int32_t number() const;
+  void set_number(int32_t value);
+  private:
+  int32_t _internal_number() const;
+  void _internal_set_number(int32_t value);
+  public:
+
+  // optional int32 oneof_index = 9;
+  bool has_oneof_index() const;
+  private:
+  bool _internal_has_oneof_index() const;
+  public:
+  void clear_oneof_index();
+  int32_t oneof_index() const;
+  void set_oneof_index(int32_t value);
+  private:
+  int32_t _internal_oneof_index() const;
+  void _internal_set_oneof_index(int32_t value);
+  public:
+
+  // optional bool proto3_optional = 17;
+  bool has_proto3_optional() const;
+  private:
+  bool _internal_has_proto3_optional() const;
+  public:
+  void clear_proto3_optional();
+  bool proto3_optional() const;
+  void set_proto3_optional(bool value);
+  private:
+  bool _internal_proto3_optional() const;
+  void _internal_set_proto3_optional(bool value);
+  public:
+
+  // optional .google.protobuf.FieldDescriptorProto.Label label = 4;
+  bool has_label() const;
+  private:
+  bool _internal_has_label() const;
+  public:
+  void clear_label();
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label label() const;
+  void set_label(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label _internal_label() const;
+  void _internal_set_label(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label value);
+  public:
+
+  // optional .google.protobuf.FieldDescriptorProto.Type type = 5;
+  bool has_type() const;
+  private:
+  bool _internal_has_type() const;
+  public:
+  void clear_type();
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type type() const;
+  void set_type(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type _internal_type() const;
+  void _internal_set_type(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.FieldDescriptorProto)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr extendee_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr type_name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr default_value_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr json_name_;
+    ::PROTOBUF_NAMESPACE_ID::FieldOptions* options_;
+    int32_t number_;
+    int32_t oneof_index_;
+    bool proto3_optional_;
+    int label_;
+    int type_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT OneofDescriptorProto final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.OneofDescriptorProto) */ {
+ public:
+  inline OneofDescriptorProto() : OneofDescriptorProto(nullptr) {}
+  ~OneofDescriptorProto() override;
+  explicit PROTOBUF_CONSTEXPR OneofDescriptorProto(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  OneofDescriptorProto(const OneofDescriptorProto& from);
+  OneofDescriptorProto(OneofDescriptorProto&& from) noexcept
+    : OneofDescriptorProto() {
+    *this = ::std::move(from);
+  }
+
+  inline OneofDescriptorProto& operator=(const OneofDescriptorProto& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline OneofDescriptorProto& operator=(OneofDescriptorProto&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const OneofDescriptorProto& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const OneofDescriptorProto* internal_default_instance() {
+    return reinterpret_cast<const OneofDescriptorProto*>(
+               &_OneofDescriptorProto_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    7;
+
+  friend void swap(OneofDescriptorProto& a, OneofDescriptorProto& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(OneofDescriptorProto* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(OneofDescriptorProto* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  OneofDescriptorProto* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<OneofDescriptorProto>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const OneofDescriptorProto& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const OneofDescriptorProto& from) {
+    OneofDescriptorProto::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(OneofDescriptorProto* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.OneofDescriptorProto";
+  }
+  protected:
+  explicit OneofDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNameFieldNumber = 1,
+    kOptionsFieldNumber = 2,
+  };
+  // optional string name = 1;
+  bool has_name() const;
+  private:
+  bool _internal_has_name() const;
+  public:
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // optional .google.protobuf.OneofOptions options = 2;
+  bool has_options() const;
+  private:
+  bool _internal_has_options() const;
+  public:
+  void clear_options();
+  const ::PROTOBUF_NAMESPACE_ID::OneofOptions& options() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::OneofOptions* release_options();
+  ::PROTOBUF_NAMESPACE_ID::OneofOptions* mutable_options();
+  void set_allocated_options(::PROTOBUF_NAMESPACE_ID::OneofOptions* options);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::OneofOptions& _internal_options() const;
+  ::PROTOBUF_NAMESPACE_ID::OneofOptions* _internal_mutable_options();
+  public:
+  void unsafe_arena_set_allocated_options(
+      ::PROTOBUF_NAMESPACE_ID::OneofOptions* options);
+  ::PROTOBUF_NAMESPACE_ID::OneofOptions* unsafe_arena_release_options();
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.OneofDescriptorProto)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::OneofOptions* options_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT EnumDescriptorProto_EnumReservedRange final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.EnumDescriptorProto.EnumReservedRange) */ {
+ public:
+  inline EnumDescriptorProto_EnumReservedRange() : EnumDescriptorProto_EnumReservedRange(nullptr) {}
+  ~EnumDescriptorProto_EnumReservedRange() override;
+  explicit PROTOBUF_CONSTEXPR EnumDescriptorProto_EnumReservedRange(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  EnumDescriptorProto_EnumReservedRange(const EnumDescriptorProto_EnumReservedRange& from);
+  EnumDescriptorProto_EnumReservedRange(EnumDescriptorProto_EnumReservedRange&& from) noexcept
+    : EnumDescriptorProto_EnumReservedRange() {
+    *this = ::std::move(from);
+  }
+
+  inline EnumDescriptorProto_EnumReservedRange& operator=(const EnumDescriptorProto_EnumReservedRange& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline EnumDescriptorProto_EnumReservedRange& operator=(EnumDescriptorProto_EnumReservedRange&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const EnumDescriptorProto_EnumReservedRange& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const EnumDescriptorProto_EnumReservedRange* internal_default_instance() {
+    return reinterpret_cast<const EnumDescriptorProto_EnumReservedRange*>(
+               &_EnumDescriptorProto_EnumReservedRange_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    8;
+
+  friend void swap(EnumDescriptorProto_EnumReservedRange& a, EnumDescriptorProto_EnumReservedRange& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(EnumDescriptorProto_EnumReservedRange* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(EnumDescriptorProto_EnumReservedRange* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  EnumDescriptorProto_EnumReservedRange* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<EnumDescriptorProto_EnumReservedRange>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const EnumDescriptorProto_EnumReservedRange& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const EnumDescriptorProto_EnumReservedRange& from) {
+    EnumDescriptorProto_EnumReservedRange::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(EnumDescriptorProto_EnumReservedRange* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.EnumDescriptorProto.EnumReservedRange";
+  }
+  protected:
+  explicit EnumDescriptorProto_EnumReservedRange(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kStartFieldNumber = 1,
+    kEndFieldNumber = 2,
+  };
+  // optional int32 start = 1;
+  bool has_start() const;
+  private:
+  bool _internal_has_start() const;
+  public:
+  void clear_start();
+  int32_t start() const;
+  void set_start(int32_t value);
+  private:
+  int32_t _internal_start() const;
+  void _internal_set_start(int32_t value);
+  public:
+
+  // optional int32 end = 2;
+  bool has_end() const;
+  private:
+  bool _internal_has_end() const;
+  public:
+  void clear_end();
+  int32_t end() const;
+  void set_end(int32_t value);
+  private:
+  int32_t _internal_end() const;
+  void _internal_set_end(int32_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.EnumDescriptorProto.EnumReservedRange)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    int32_t start_;
+    int32_t end_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT EnumDescriptorProto final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.EnumDescriptorProto) */ {
+ public:
+  inline EnumDescriptorProto() : EnumDescriptorProto(nullptr) {}
+  ~EnumDescriptorProto() override;
+  explicit PROTOBUF_CONSTEXPR EnumDescriptorProto(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  EnumDescriptorProto(const EnumDescriptorProto& from);
+  EnumDescriptorProto(EnumDescriptorProto&& from) noexcept
+    : EnumDescriptorProto() {
+    *this = ::std::move(from);
+  }
+
+  inline EnumDescriptorProto& operator=(const EnumDescriptorProto& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline EnumDescriptorProto& operator=(EnumDescriptorProto&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const EnumDescriptorProto& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const EnumDescriptorProto* internal_default_instance() {
+    return reinterpret_cast<const EnumDescriptorProto*>(
+               &_EnumDescriptorProto_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    9;
+
+  friend void swap(EnumDescriptorProto& a, EnumDescriptorProto& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(EnumDescriptorProto* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(EnumDescriptorProto* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  EnumDescriptorProto* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<EnumDescriptorProto>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const EnumDescriptorProto& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const EnumDescriptorProto& from) {
+    EnumDescriptorProto::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(EnumDescriptorProto* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.EnumDescriptorProto";
+  }
+  protected:
+  explicit EnumDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef EnumDescriptorProto_EnumReservedRange EnumReservedRange;
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValueFieldNumber = 2,
+    kReservedRangeFieldNumber = 4,
+    kReservedNameFieldNumber = 5,
+    kNameFieldNumber = 1,
+    kOptionsFieldNumber = 3,
+  };
+  // repeated .google.protobuf.EnumValueDescriptorProto value = 2;
+  int value_size() const;
+  private:
+  int _internal_value_size() const;
+  public:
+  void clear_value();
+  ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto* mutable_value(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto >*
+      mutable_value();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto& _internal_value(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto* _internal_add_value();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto& value(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto* add_value();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto >&
+      value() const;
+
+  // repeated .google.protobuf.EnumDescriptorProto.EnumReservedRange reserved_range = 4;
+  int reserved_range_size() const;
+  private:
+  int _internal_reserved_range_size() const;
+  public:
+  void clear_reserved_range();
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange* mutable_reserved_range(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange >*
+      mutable_reserved_range();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange& _internal_reserved_range(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange* _internal_add_reserved_range();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange& reserved_range(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange* add_reserved_range();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange >&
+      reserved_range() const;
+
+  // repeated string reserved_name = 5;
+  int reserved_name_size() const;
+  private:
+  int _internal_reserved_name_size() const;
+  public:
+  void clear_reserved_name();
+  const std::string& reserved_name(int index) const;
+  std::string* mutable_reserved_name(int index);
+  void set_reserved_name(int index, const std::string& value);
+  void set_reserved_name(int index, std::string&& value);
+  void set_reserved_name(int index, const char* value);
+  void set_reserved_name(int index, const char* value, size_t size);
+  std::string* add_reserved_name();
+  void add_reserved_name(const std::string& value);
+  void add_reserved_name(std::string&& value);
+  void add_reserved_name(const char* value);
+  void add_reserved_name(const char* value, size_t size);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& reserved_name() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_reserved_name();
+  private:
+  const std::string& _internal_reserved_name(int index) const;
+  std::string* _internal_add_reserved_name();
+  public:
+
+  // optional string name = 1;
+  bool has_name() const;
+  private:
+  bool _internal_has_name() const;
+  public:
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // optional .google.protobuf.EnumOptions options = 3;
+  bool has_options() const;
+  private:
+  bool _internal_has_options() const;
+  public:
+  void clear_options();
+  const ::PROTOBUF_NAMESPACE_ID::EnumOptions& options() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::EnumOptions* release_options();
+  ::PROTOBUF_NAMESPACE_ID::EnumOptions* mutable_options();
+  void set_allocated_options(::PROTOBUF_NAMESPACE_ID::EnumOptions* options);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::EnumOptions& _internal_options() const;
+  ::PROTOBUF_NAMESPACE_ID::EnumOptions* _internal_mutable_options();
+  public:
+  void unsafe_arena_set_allocated_options(
+      ::PROTOBUF_NAMESPACE_ID::EnumOptions* options);
+  ::PROTOBUF_NAMESPACE_ID::EnumOptions* unsafe_arena_release_options();
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.EnumDescriptorProto)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto > value_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange > reserved_range_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> reserved_name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::EnumOptions* options_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT EnumValueDescriptorProto final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.EnumValueDescriptorProto) */ {
+ public:
+  inline EnumValueDescriptorProto() : EnumValueDescriptorProto(nullptr) {}
+  ~EnumValueDescriptorProto() override;
+  explicit PROTOBUF_CONSTEXPR EnumValueDescriptorProto(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  EnumValueDescriptorProto(const EnumValueDescriptorProto& from);
+  EnumValueDescriptorProto(EnumValueDescriptorProto&& from) noexcept
+    : EnumValueDescriptorProto() {
+    *this = ::std::move(from);
+  }
+
+  inline EnumValueDescriptorProto& operator=(const EnumValueDescriptorProto& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline EnumValueDescriptorProto& operator=(EnumValueDescriptorProto&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const EnumValueDescriptorProto& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const EnumValueDescriptorProto* internal_default_instance() {
+    return reinterpret_cast<const EnumValueDescriptorProto*>(
+               &_EnumValueDescriptorProto_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    10;
+
+  friend void swap(EnumValueDescriptorProto& a, EnumValueDescriptorProto& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(EnumValueDescriptorProto* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(EnumValueDescriptorProto* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  EnumValueDescriptorProto* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<EnumValueDescriptorProto>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const EnumValueDescriptorProto& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const EnumValueDescriptorProto& from) {
+    EnumValueDescriptorProto::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(EnumValueDescriptorProto* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.EnumValueDescriptorProto";
+  }
+  protected:
+  explicit EnumValueDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNameFieldNumber = 1,
+    kOptionsFieldNumber = 3,
+    kNumberFieldNumber = 2,
+  };
+  // optional string name = 1;
+  bool has_name() const;
+  private:
+  bool _internal_has_name() const;
+  public:
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // optional .google.protobuf.EnumValueOptions options = 3;
+  bool has_options() const;
+  private:
+  bool _internal_has_options() const;
+  public:
+  void clear_options();
+  const ::PROTOBUF_NAMESPACE_ID::EnumValueOptions& options() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* release_options();
+  ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* mutable_options();
+  void set_allocated_options(::PROTOBUF_NAMESPACE_ID::EnumValueOptions* options);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::EnumValueOptions& _internal_options() const;
+  ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* _internal_mutable_options();
+  public:
+  void unsafe_arena_set_allocated_options(
+      ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* options);
+  ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* unsafe_arena_release_options();
+
+  // optional int32 number = 2;
+  bool has_number() const;
+  private:
+  bool _internal_has_number() const;
+  public:
+  void clear_number();
+  int32_t number() const;
+  void set_number(int32_t value);
+  private:
+  int32_t _internal_number() const;
+  void _internal_set_number(int32_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.EnumValueDescriptorProto)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* options_;
+    int32_t number_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT ServiceDescriptorProto final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.ServiceDescriptorProto) */ {
+ public:
+  inline ServiceDescriptorProto() : ServiceDescriptorProto(nullptr) {}
+  ~ServiceDescriptorProto() override;
+  explicit PROTOBUF_CONSTEXPR ServiceDescriptorProto(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  ServiceDescriptorProto(const ServiceDescriptorProto& from);
+  ServiceDescriptorProto(ServiceDescriptorProto&& from) noexcept
+    : ServiceDescriptorProto() {
+    *this = ::std::move(from);
+  }
+
+  inline ServiceDescriptorProto& operator=(const ServiceDescriptorProto& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline ServiceDescriptorProto& operator=(ServiceDescriptorProto&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const ServiceDescriptorProto& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const ServiceDescriptorProto* internal_default_instance() {
+    return reinterpret_cast<const ServiceDescriptorProto*>(
+               &_ServiceDescriptorProto_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    11;
+
+  friend void swap(ServiceDescriptorProto& a, ServiceDescriptorProto& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(ServiceDescriptorProto* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(ServiceDescriptorProto* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  ServiceDescriptorProto* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<ServiceDescriptorProto>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const ServiceDescriptorProto& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const ServiceDescriptorProto& from) {
+    ServiceDescriptorProto::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(ServiceDescriptorProto* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.ServiceDescriptorProto";
+  }
+  protected:
+  explicit ServiceDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kMethodFieldNumber = 2,
+    kNameFieldNumber = 1,
+    kOptionsFieldNumber = 3,
+  };
+  // repeated .google.protobuf.MethodDescriptorProto method = 2;
+  int method_size() const;
+  private:
+  int _internal_method_size() const;
+  public:
+  void clear_method();
+  ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto* mutable_method(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto >*
+      mutable_method();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto& _internal_method(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto* _internal_add_method();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto& method(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto* add_method();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto >&
+      method() const;
+
+  // optional string name = 1;
+  bool has_name() const;
+  private:
+  bool _internal_has_name() const;
+  public:
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // optional .google.protobuf.ServiceOptions options = 3;
+  bool has_options() const;
+  private:
+  bool _internal_has_options() const;
+  public:
+  void clear_options();
+  const ::PROTOBUF_NAMESPACE_ID::ServiceOptions& options() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::ServiceOptions* release_options();
+  ::PROTOBUF_NAMESPACE_ID::ServiceOptions* mutable_options();
+  void set_allocated_options(::PROTOBUF_NAMESPACE_ID::ServiceOptions* options);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::ServiceOptions& _internal_options() const;
+  ::PROTOBUF_NAMESPACE_ID::ServiceOptions* _internal_mutable_options();
+  public:
+  void unsafe_arena_set_allocated_options(
+      ::PROTOBUF_NAMESPACE_ID::ServiceOptions* options);
+  ::PROTOBUF_NAMESPACE_ID::ServiceOptions* unsafe_arena_release_options();
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.ServiceDescriptorProto)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto > method_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::ServiceOptions* options_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT MethodDescriptorProto final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.MethodDescriptorProto) */ {
+ public:
+  inline MethodDescriptorProto() : MethodDescriptorProto(nullptr) {}
+  ~MethodDescriptorProto() override;
+  explicit PROTOBUF_CONSTEXPR MethodDescriptorProto(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  MethodDescriptorProto(const MethodDescriptorProto& from);
+  MethodDescriptorProto(MethodDescriptorProto&& from) noexcept
+    : MethodDescriptorProto() {
+    *this = ::std::move(from);
+  }
+
+  inline MethodDescriptorProto& operator=(const MethodDescriptorProto& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline MethodDescriptorProto& operator=(MethodDescriptorProto&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const MethodDescriptorProto& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const MethodDescriptorProto* internal_default_instance() {
+    return reinterpret_cast<const MethodDescriptorProto*>(
+               &_MethodDescriptorProto_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    12;
+
+  friend void swap(MethodDescriptorProto& a, MethodDescriptorProto& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(MethodDescriptorProto* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(MethodDescriptorProto* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  MethodDescriptorProto* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<MethodDescriptorProto>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const MethodDescriptorProto& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const MethodDescriptorProto& from) {
+    MethodDescriptorProto::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(MethodDescriptorProto* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.MethodDescriptorProto";
+  }
+  protected:
+  explicit MethodDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNameFieldNumber = 1,
+    kInputTypeFieldNumber = 2,
+    kOutputTypeFieldNumber = 3,
+    kOptionsFieldNumber = 4,
+    kClientStreamingFieldNumber = 5,
+    kServerStreamingFieldNumber = 6,
+  };
+  // optional string name = 1;
+  bool has_name() const;
+  private:
+  bool _internal_has_name() const;
+  public:
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // optional string input_type = 2;
+  bool has_input_type() const;
+  private:
+  bool _internal_has_input_type() const;
+  public:
+  void clear_input_type();
+  const std::string& input_type() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_input_type(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_input_type();
+  PROTOBUF_NODISCARD std::string* release_input_type();
+  void set_allocated_input_type(std::string* input_type);
+  private:
+  const std::string& _internal_input_type() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_input_type(const std::string& value);
+  std::string* _internal_mutable_input_type();
+  public:
+
+  // optional string output_type = 3;
+  bool has_output_type() const;
+  private:
+  bool _internal_has_output_type() const;
+  public:
+  void clear_output_type();
+  const std::string& output_type() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_output_type(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_output_type();
+  PROTOBUF_NODISCARD std::string* release_output_type();
+  void set_allocated_output_type(std::string* output_type);
+  private:
+  const std::string& _internal_output_type() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_output_type(const std::string& value);
+  std::string* _internal_mutable_output_type();
+  public:
+
+  // optional .google.protobuf.MethodOptions options = 4;
+  bool has_options() const;
+  private:
+  bool _internal_has_options() const;
+  public:
+  void clear_options();
+  const ::PROTOBUF_NAMESPACE_ID::MethodOptions& options() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::MethodOptions* release_options();
+  ::PROTOBUF_NAMESPACE_ID::MethodOptions* mutable_options();
+  void set_allocated_options(::PROTOBUF_NAMESPACE_ID::MethodOptions* options);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::MethodOptions& _internal_options() const;
+  ::PROTOBUF_NAMESPACE_ID::MethodOptions* _internal_mutable_options();
+  public:
+  void unsafe_arena_set_allocated_options(
+      ::PROTOBUF_NAMESPACE_ID::MethodOptions* options);
+  ::PROTOBUF_NAMESPACE_ID::MethodOptions* unsafe_arena_release_options();
+
+  // optional bool client_streaming = 5 [default = false];
+  bool has_client_streaming() const;
+  private:
+  bool _internal_has_client_streaming() const;
+  public:
+  void clear_client_streaming();
+  bool client_streaming() const;
+  void set_client_streaming(bool value);
+  private:
+  bool _internal_client_streaming() const;
+  void _internal_set_client_streaming(bool value);
+  public:
+
+  // optional bool server_streaming = 6 [default = false];
+  bool has_server_streaming() const;
+  private:
+  bool _internal_has_server_streaming() const;
+  public:
+  void clear_server_streaming();
+  bool server_streaming() const;
+  void set_server_streaming(bool value);
+  private:
+  bool _internal_server_streaming() const;
+  void _internal_set_server_streaming(bool value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.MethodDescriptorProto)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr input_type_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr output_type_;
+    ::PROTOBUF_NAMESPACE_ID::MethodOptions* options_;
+    bool client_streaming_;
+    bool server_streaming_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT FileOptions final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.FileOptions) */ {
+ public:
+  inline FileOptions() : FileOptions(nullptr) {}
+  ~FileOptions() override;
+  explicit PROTOBUF_CONSTEXPR FileOptions(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  FileOptions(const FileOptions& from);
+  FileOptions(FileOptions&& from) noexcept
+    : FileOptions() {
+    *this = ::std::move(from);
+  }
+
+  inline FileOptions& operator=(const FileOptions& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline FileOptions& operator=(FileOptions&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const FileOptions& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const FileOptions* internal_default_instance() {
+    return reinterpret_cast<const FileOptions*>(
+               &_FileOptions_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    13;
+
+  friend void swap(FileOptions& a, FileOptions& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(FileOptions* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(FileOptions* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  FileOptions* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<FileOptions>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const FileOptions& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const FileOptions& from) {
+    FileOptions::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(FileOptions* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.FileOptions";
+  }
+  protected:
+  explicit FileOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef FileOptions_OptimizeMode OptimizeMode;
+  static constexpr OptimizeMode SPEED =
+    FileOptions_OptimizeMode_SPEED;
+  static constexpr OptimizeMode CODE_SIZE =
+    FileOptions_OptimizeMode_CODE_SIZE;
+  static constexpr OptimizeMode LITE_RUNTIME =
+    FileOptions_OptimizeMode_LITE_RUNTIME;
+  static inline bool OptimizeMode_IsValid(int value) {
+    return FileOptions_OptimizeMode_IsValid(value);
+  }
+  static constexpr OptimizeMode OptimizeMode_MIN =
+    FileOptions_OptimizeMode_OptimizeMode_MIN;
+  static constexpr OptimizeMode OptimizeMode_MAX =
+    FileOptions_OptimizeMode_OptimizeMode_MAX;
+  static constexpr int OptimizeMode_ARRAYSIZE =
+    FileOptions_OptimizeMode_OptimizeMode_ARRAYSIZE;
+  static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
+  OptimizeMode_descriptor() {
+    return FileOptions_OptimizeMode_descriptor();
+  }
+  template<typename T>
+  static inline const std::string& OptimizeMode_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, OptimizeMode>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function OptimizeMode_Name.");
+    return FileOptions_OptimizeMode_Name(enum_t_value);
+  }
+  static inline bool OptimizeMode_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+      OptimizeMode* value) {
+    return FileOptions_OptimizeMode_Parse(name, value);
+  }
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kUninterpretedOptionFieldNumber = 999,
+    kJavaPackageFieldNumber = 1,
+    kJavaOuterClassnameFieldNumber = 8,
+    kGoPackageFieldNumber = 11,
+    kObjcClassPrefixFieldNumber = 36,
+    kCsharpNamespaceFieldNumber = 37,
+    kSwiftPrefixFieldNumber = 39,
+    kPhpClassPrefixFieldNumber = 40,
+    kPhpNamespaceFieldNumber = 41,
+    kPhpMetadataNamespaceFieldNumber = 44,
+    kRubyPackageFieldNumber = 45,
+    kJavaMultipleFilesFieldNumber = 10,
+    kJavaGenerateEqualsAndHashFieldNumber = 20,
+    kJavaStringCheckUtf8FieldNumber = 27,
+    kCcGenericServicesFieldNumber = 16,
+    kJavaGenericServicesFieldNumber = 17,
+    kPyGenericServicesFieldNumber = 18,
+    kPhpGenericServicesFieldNumber = 42,
+    kDeprecatedFieldNumber = 23,
+    kOptimizeForFieldNumber = 9,
+    kCcEnableArenasFieldNumber = 31,
+  };
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  int uninterpreted_option_size() const;
+  private:
+  int _internal_uninterpreted_option_size() const;
+  public:
+  void clear_uninterpreted_option();
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* mutable_uninterpreted_option(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+      mutable_uninterpreted_option();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& _internal_uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _internal_add_uninterpreted_option();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* add_uninterpreted_option();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+      uninterpreted_option() const;
+
+  // optional string java_package = 1;
+  bool has_java_package() const;
+  private:
+  bool _internal_has_java_package() const;
+  public:
+  void clear_java_package();
+  const std::string& java_package() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_java_package(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_java_package();
+  PROTOBUF_NODISCARD std::string* release_java_package();
+  void set_allocated_java_package(std::string* java_package);
+  private:
+  const std::string& _internal_java_package() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_java_package(const std::string& value);
+  std::string* _internal_mutable_java_package();
+  public:
+
+  // optional string java_outer_classname = 8;
+  bool has_java_outer_classname() const;
+  private:
+  bool _internal_has_java_outer_classname() const;
+  public:
+  void clear_java_outer_classname();
+  const std::string& java_outer_classname() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_java_outer_classname(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_java_outer_classname();
+  PROTOBUF_NODISCARD std::string* release_java_outer_classname();
+  void set_allocated_java_outer_classname(std::string* java_outer_classname);
+  private:
+  const std::string& _internal_java_outer_classname() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_java_outer_classname(const std::string& value);
+  std::string* _internal_mutable_java_outer_classname();
+  public:
+
+  // optional string go_package = 11;
+  bool has_go_package() const;
+  private:
+  bool _internal_has_go_package() const;
+  public:
+  void clear_go_package();
+  const std::string& go_package() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_go_package(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_go_package();
+  PROTOBUF_NODISCARD std::string* release_go_package();
+  void set_allocated_go_package(std::string* go_package);
+  private:
+  const std::string& _internal_go_package() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_go_package(const std::string& value);
+  std::string* _internal_mutable_go_package();
+  public:
+
+  // optional string objc_class_prefix = 36;
+  bool has_objc_class_prefix() const;
+  private:
+  bool _internal_has_objc_class_prefix() const;
+  public:
+  void clear_objc_class_prefix();
+  const std::string& objc_class_prefix() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_objc_class_prefix(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_objc_class_prefix();
+  PROTOBUF_NODISCARD std::string* release_objc_class_prefix();
+  void set_allocated_objc_class_prefix(std::string* objc_class_prefix);
+  private:
+  const std::string& _internal_objc_class_prefix() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_objc_class_prefix(const std::string& value);
+  std::string* _internal_mutable_objc_class_prefix();
+  public:
+
+  // optional string csharp_namespace = 37;
+  bool has_csharp_namespace() const;
+  private:
+  bool _internal_has_csharp_namespace() const;
+  public:
+  void clear_csharp_namespace();
+  const std::string& csharp_namespace() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_csharp_namespace(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_csharp_namespace();
+  PROTOBUF_NODISCARD std::string* release_csharp_namespace();
+  void set_allocated_csharp_namespace(std::string* csharp_namespace);
+  private:
+  const std::string& _internal_csharp_namespace() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_csharp_namespace(const std::string& value);
+  std::string* _internal_mutable_csharp_namespace();
+  public:
+
+  // optional string swift_prefix = 39;
+  bool has_swift_prefix() const;
+  private:
+  bool _internal_has_swift_prefix() const;
+  public:
+  void clear_swift_prefix();
+  const std::string& swift_prefix() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_swift_prefix(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_swift_prefix();
+  PROTOBUF_NODISCARD std::string* release_swift_prefix();
+  void set_allocated_swift_prefix(std::string* swift_prefix);
+  private:
+  const std::string& _internal_swift_prefix() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_swift_prefix(const std::string& value);
+  std::string* _internal_mutable_swift_prefix();
+  public:
+
+  // optional string php_class_prefix = 40;
+  bool has_php_class_prefix() const;
+  private:
+  bool _internal_has_php_class_prefix() const;
+  public:
+  void clear_php_class_prefix();
+  const std::string& php_class_prefix() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_php_class_prefix(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_php_class_prefix();
+  PROTOBUF_NODISCARD std::string* release_php_class_prefix();
+  void set_allocated_php_class_prefix(std::string* php_class_prefix);
+  private:
+  const std::string& _internal_php_class_prefix() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_php_class_prefix(const std::string& value);
+  std::string* _internal_mutable_php_class_prefix();
+  public:
+
+  // optional string php_namespace = 41;
+  bool has_php_namespace() const;
+  private:
+  bool _internal_has_php_namespace() const;
+  public:
+  void clear_php_namespace();
+  const std::string& php_namespace() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_php_namespace(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_php_namespace();
+  PROTOBUF_NODISCARD std::string* release_php_namespace();
+  void set_allocated_php_namespace(std::string* php_namespace);
+  private:
+  const std::string& _internal_php_namespace() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_php_namespace(const std::string& value);
+  std::string* _internal_mutable_php_namespace();
+  public:
+
+  // optional string php_metadata_namespace = 44;
+  bool has_php_metadata_namespace() const;
+  private:
+  bool _internal_has_php_metadata_namespace() const;
+  public:
+  void clear_php_metadata_namespace();
+  const std::string& php_metadata_namespace() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_php_metadata_namespace(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_php_metadata_namespace();
+  PROTOBUF_NODISCARD std::string* release_php_metadata_namespace();
+  void set_allocated_php_metadata_namespace(std::string* php_metadata_namespace);
+  private:
+  const std::string& _internal_php_metadata_namespace() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_php_metadata_namespace(const std::string& value);
+  std::string* _internal_mutable_php_metadata_namespace();
+  public:
+
+  // optional string ruby_package = 45;
+  bool has_ruby_package() const;
+  private:
+  bool _internal_has_ruby_package() const;
+  public:
+  void clear_ruby_package();
+  const std::string& ruby_package() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_ruby_package(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_ruby_package();
+  PROTOBUF_NODISCARD std::string* release_ruby_package();
+  void set_allocated_ruby_package(std::string* ruby_package);
+  private:
+  const std::string& _internal_ruby_package() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_ruby_package(const std::string& value);
+  std::string* _internal_mutable_ruby_package();
+  public:
+
+  // optional bool java_multiple_files = 10 [default = false];
+  bool has_java_multiple_files() const;
+  private:
+  bool _internal_has_java_multiple_files() const;
+  public:
+  void clear_java_multiple_files();
+  bool java_multiple_files() const;
+  void set_java_multiple_files(bool value);
+  private:
+  bool _internal_java_multiple_files() const;
+  void _internal_set_java_multiple_files(bool value);
+  public:
+
+  // optional bool java_generate_equals_and_hash = 20 [deprecated = true];
+  PROTOBUF_DEPRECATED bool has_java_generate_equals_and_hash() const;
+  private:
+  bool _internal_has_java_generate_equals_and_hash() const;
+  public:
+  PROTOBUF_DEPRECATED void clear_java_generate_equals_and_hash();
+  PROTOBUF_DEPRECATED bool java_generate_equals_and_hash() const;
+  PROTOBUF_DEPRECATED void set_java_generate_equals_and_hash(bool value);
+  private:
+  bool _internal_java_generate_equals_and_hash() const;
+  void _internal_set_java_generate_equals_and_hash(bool value);
+  public:
+
+  // optional bool java_string_check_utf8 = 27 [default = false];
+  bool has_java_string_check_utf8() const;
+  private:
+  bool _internal_has_java_string_check_utf8() const;
+  public:
+  void clear_java_string_check_utf8();
+  bool java_string_check_utf8() const;
+  void set_java_string_check_utf8(bool value);
+  private:
+  bool _internal_java_string_check_utf8() const;
+  void _internal_set_java_string_check_utf8(bool value);
+  public:
+
+  // optional bool cc_generic_services = 16 [default = false];
+  bool has_cc_generic_services() const;
+  private:
+  bool _internal_has_cc_generic_services() const;
+  public:
+  void clear_cc_generic_services();
+  bool cc_generic_services() const;
+  void set_cc_generic_services(bool value);
+  private:
+  bool _internal_cc_generic_services() const;
+  void _internal_set_cc_generic_services(bool value);
+  public:
+
+  // optional bool java_generic_services = 17 [default = false];
+  bool has_java_generic_services() const;
+  private:
+  bool _internal_has_java_generic_services() const;
+  public:
+  void clear_java_generic_services();
+  bool java_generic_services() const;
+  void set_java_generic_services(bool value);
+  private:
+  bool _internal_java_generic_services() const;
+  void _internal_set_java_generic_services(bool value);
+  public:
+
+  // optional bool py_generic_services = 18 [default = false];
+  bool has_py_generic_services() const;
+  private:
+  bool _internal_has_py_generic_services() const;
+  public:
+  void clear_py_generic_services();
+  bool py_generic_services() const;
+  void set_py_generic_services(bool value);
+  private:
+  bool _internal_py_generic_services() const;
+  void _internal_set_py_generic_services(bool value);
+  public:
+
+  // optional bool php_generic_services = 42 [default = false];
+  bool has_php_generic_services() const;
+  private:
+  bool _internal_has_php_generic_services() const;
+  public:
+  void clear_php_generic_services();
+  bool php_generic_services() const;
+  void set_php_generic_services(bool value);
+  private:
+  bool _internal_php_generic_services() const;
+  void _internal_set_php_generic_services(bool value);
+  public:
+
+  // optional bool deprecated = 23 [default = false];
+  bool has_deprecated() const;
+  private:
+  bool _internal_has_deprecated() const;
+  public:
+  void clear_deprecated();
+  bool deprecated() const;
+  void set_deprecated(bool value);
+  private:
+  bool _internal_deprecated() const;
+  void _internal_set_deprecated(bool value);
+  public:
+
+  // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED];
+  bool has_optimize_for() const;
+  private:
+  bool _internal_has_optimize_for() const;
+  public:
+  void clear_optimize_for();
+  ::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode optimize_for() const;
+  void set_optimize_for(::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode _internal_optimize_for() const;
+  void _internal_set_optimize_for(::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode value);
+  public:
+
+  // optional bool cc_enable_arenas = 31 [default = true];
+  bool has_cc_enable_arenas() const;
+  private:
+  bool _internal_has_cc_enable_arenas() const;
+  public:
+  void clear_cc_enable_arenas();
+  bool cc_enable_arenas() const;
+  void set_cc_enable_arenas(bool value);
+  private:
+  bool _internal_cc_enable_arenas() const;
+  void _internal_set_cc_enable_arenas(bool value);
+  public:
+
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline bool HasExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.Has(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void ClearExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    _impl_._extensions_.ClearExtension(id.number());
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline int ExtensionSize(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.ExtensionSize(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_,
+                                  id.default_value());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Mutable(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), _field_type, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::SetAllocated(id.number(), _field_type, value,
+                                    &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void UnsafeArenaSetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::UnsafeArenaSetAllocated(id.number(), _field_type,
+                                               value, &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  PROTOBUF_NODISCARD inline
+      typename _proto_TypeTraits::Singular::MutableType
+      ReleaseExtension(
+          const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+              FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Release(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType
+  UnsafeArenaReleaseExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::UnsafeArenaRelease(id.number(), _field_type,
+                                                 &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_, index);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) {
+
+    return _proto_TypeTraits::Mutable(id.number(), index, &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index, typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), index, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    typename _proto_TypeTraits::Repeated::MutableType to_add =
+        _proto_TypeTraits::Add(id.number(), _field_type, &_impl_._extensions_);
+
+    return to_add;
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Add(id.number(), _field_type, _is_packed, value,
+                           &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline const typename _proto_TypeTraits::Repeated::RepeatedFieldType&
+  GetRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::GetRepeated(id.number(), _impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::RepeatedFieldType*
+  MutableRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FileOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::MutableRepeated(id.number(), _field_type,
+                                              _is_packed, &_impl_._extensions_);
+  }
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.FileOptions)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
+
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption > uninterpreted_option_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr java_package_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr java_outer_classname_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr go_package_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr objc_class_prefix_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr csharp_namespace_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr swift_prefix_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr php_class_prefix_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr php_namespace_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr php_metadata_namespace_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr ruby_package_;
+    bool java_multiple_files_;
+    bool java_generate_equals_and_hash_;
+    bool java_string_check_utf8_;
+    bool cc_generic_services_;
+    bool java_generic_services_;
+    bool py_generic_services_;
+    bool php_generic_services_;
+    bool deprecated_;
+    int optimize_for_;
+    bool cc_enable_arenas_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT MessageOptions final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.MessageOptions) */ {
+ public:
+  inline MessageOptions() : MessageOptions(nullptr) {}
+  ~MessageOptions() override;
+  explicit PROTOBUF_CONSTEXPR MessageOptions(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  MessageOptions(const MessageOptions& from);
+  MessageOptions(MessageOptions&& from) noexcept
+    : MessageOptions() {
+    *this = ::std::move(from);
+  }
+
+  inline MessageOptions& operator=(const MessageOptions& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline MessageOptions& operator=(MessageOptions&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const MessageOptions& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const MessageOptions* internal_default_instance() {
+    return reinterpret_cast<const MessageOptions*>(
+               &_MessageOptions_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    14;
+
+  friend void swap(MessageOptions& a, MessageOptions& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(MessageOptions* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(MessageOptions* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  MessageOptions* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<MessageOptions>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const MessageOptions& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const MessageOptions& from) {
+    MessageOptions::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(MessageOptions* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.MessageOptions";
+  }
+  protected:
+  explicit MessageOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kUninterpretedOptionFieldNumber = 999,
+    kMessageSetWireFormatFieldNumber = 1,
+    kNoStandardDescriptorAccessorFieldNumber = 2,
+    kDeprecatedFieldNumber = 3,
+    kMapEntryFieldNumber = 7,
+  };
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  int uninterpreted_option_size() const;
+  private:
+  int _internal_uninterpreted_option_size() const;
+  public:
+  void clear_uninterpreted_option();
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* mutable_uninterpreted_option(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+      mutable_uninterpreted_option();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& _internal_uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _internal_add_uninterpreted_option();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* add_uninterpreted_option();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+      uninterpreted_option() const;
+
+  // optional bool message_set_wire_format = 1 [default = false];
+  bool has_message_set_wire_format() const;
+  private:
+  bool _internal_has_message_set_wire_format() const;
+  public:
+  void clear_message_set_wire_format();
+  bool message_set_wire_format() const;
+  void set_message_set_wire_format(bool value);
+  private:
+  bool _internal_message_set_wire_format() const;
+  void _internal_set_message_set_wire_format(bool value);
+  public:
+
+  // optional bool no_standard_descriptor_accessor = 2 [default = false];
+  bool has_no_standard_descriptor_accessor() const;
+  private:
+  bool _internal_has_no_standard_descriptor_accessor() const;
+  public:
+  void clear_no_standard_descriptor_accessor();
+  bool no_standard_descriptor_accessor() const;
+  void set_no_standard_descriptor_accessor(bool value);
+  private:
+  bool _internal_no_standard_descriptor_accessor() const;
+  void _internal_set_no_standard_descriptor_accessor(bool value);
+  public:
+
+  // optional bool deprecated = 3 [default = false];
+  bool has_deprecated() const;
+  private:
+  bool _internal_has_deprecated() const;
+  public:
+  void clear_deprecated();
+  bool deprecated() const;
+  void set_deprecated(bool value);
+  private:
+  bool _internal_deprecated() const;
+  void _internal_set_deprecated(bool value);
+  public:
+
+  // optional bool map_entry = 7;
+  bool has_map_entry() const;
+  private:
+  bool _internal_has_map_entry() const;
+  public:
+  void clear_map_entry();
+  bool map_entry() const;
+  void set_map_entry(bool value);
+  private:
+  bool _internal_map_entry() const;
+  void _internal_set_map_entry(bool value);
+  public:
+
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline bool HasExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.Has(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void ClearExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    _impl_._extensions_.ClearExtension(id.number());
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline int ExtensionSize(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.ExtensionSize(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_,
+                                  id.default_value());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Mutable(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), _field_type, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::SetAllocated(id.number(), _field_type, value,
+                                    &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void UnsafeArenaSetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::UnsafeArenaSetAllocated(id.number(), _field_type,
+                                               value, &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  PROTOBUF_NODISCARD inline
+      typename _proto_TypeTraits::Singular::MutableType
+      ReleaseExtension(
+          const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+              MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Release(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType
+  UnsafeArenaReleaseExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::UnsafeArenaRelease(id.number(), _field_type,
+                                                 &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_, index);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) {
+
+    return _proto_TypeTraits::Mutable(id.number(), index, &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index, typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), index, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    typename _proto_TypeTraits::Repeated::MutableType to_add =
+        _proto_TypeTraits::Add(id.number(), _field_type, &_impl_._extensions_);
+
+    return to_add;
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Add(id.number(), _field_type, _is_packed, value,
+                           &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline const typename _proto_TypeTraits::Repeated::RepeatedFieldType&
+  GetRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::GetRepeated(id.number(), _impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::RepeatedFieldType*
+  MutableRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MessageOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::MutableRepeated(id.number(), _field_type,
+                                              _is_packed, &_impl_._extensions_);
+  }
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.MessageOptions)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
+
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption > uninterpreted_option_;
+    bool message_set_wire_format_;
+    bool no_standard_descriptor_accessor_;
+    bool deprecated_;
+    bool map_entry_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT FieldOptions final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.FieldOptions) */ {
+ public:
+  inline FieldOptions() : FieldOptions(nullptr) {}
+  ~FieldOptions() override;
+  explicit PROTOBUF_CONSTEXPR FieldOptions(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  FieldOptions(const FieldOptions& from);
+  FieldOptions(FieldOptions&& from) noexcept
+    : FieldOptions() {
+    *this = ::std::move(from);
+  }
+
+  inline FieldOptions& operator=(const FieldOptions& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline FieldOptions& operator=(FieldOptions&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const FieldOptions& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const FieldOptions* internal_default_instance() {
+    return reinterpret_cast<const FieldOptions*>(
+               &_FieldOptions_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    15;
+
+  friend void swap(FieldOptions& a, FieldOptions& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(FieldOptions* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(FieldOptions* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  FieldOptions* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<FieldOptions>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const FieldOptions& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const FieldOptions& from) {
+    FieldOptions::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(FieldOptions* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.FieldOptions";
+  }
+  protected:
+  explicit FieldOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef FieldOptions_CType CType;
+  static constexpr CType STRING =
+    FieldOptions_CType_STRING;
+  static constexpr CType CORD =
+    FieldOptions_CType_CORD;
+  static constexpr CType STRING_PIECE =
+    FieldOptions_CType_STRING_PIECE;
+  static inline bool CType_IsValid(int value) {
+    return FieldOptions_CType_IsValid(value);
+  }
+  static constexpr CType CType_MIN =
+    FieldOptions_CType_CType_MIN;
+  static constexpr CType CType_MAX =
+    FieldOptions_CType_CType_MAX;
+  static constexpr int CType_ARRAYSIZE =
+    FieldOptions_CType_CType_ARRAYSIZE;
+  static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
+  CType_descriptor() {
+    return FieldOptions_CType_descriptor();
+  }
+  template<typename T>
+  static inline const std::string& CType_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, CType>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function CType_Name.");
+    return FieldOptions_CType_Name(enum_t_value);
+  }
+  static inline bool CType_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+      CType* value) {
+    return FieldOptions_CType_Parse(name, value);
+  }
+
+  typedef FieldOptions_JSType JSType;
+  static constexpr JSType JS_NORMAL =
+    FieldOptions_JSType_JS_NORMAL;
+  static constexpr JSType JS_STRING =
+    FieldOptions_JSType_JS_STRING;
+  static constexpr JSType JS_NUMBER =
+    FieldOptions_JSType_JS_NUMBER;
+  static inline bool JSType_IsValid(int value) {
+    return FieldOptions_JSType_IsValid(value);
+  }
+  static constexpr JSType JSType_MIN =
+    FieldOptions_JSType_JSType_MIN;
+  static constexpr JSType JSType_MAX =
+    FieldOptions_JSType_JSType_MAX;
+  static constexpr int JSType_ARRAYSIZE =
+    FieldOptions_JSType_JSType_ARRAYSIZE;
+  static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
+  JSType_descriptor() {
+    return FieldOptions_JSType_descriptor();
+  }
+  template<typename T>
+  static inline const std::string& JSType_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, JSType>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function JSType_Name.");
+    return FieldOptions_JSType_Name(enum_t_value);
+  }
+  static inline bool JSType_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+      JSType* value) {
+    return FieldOptions_JSType_Parse(name, value);
+  }
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kUninterpretedOptionFieldNumber = 999,
+    kCtypeFieldNumber = 1,
+    kJstypeFieldNumber = 6,
+    kPackedFieldNumber = 2,
+    kLazyFieldNumber = 5,
+    kUnverifiedLazyFieldNumber = 15,
+    kDeprecatedFieldNumber = 3,
+    kWeakFieldNumber = 10,
+  };
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  int uninterpreted_option_size() const;
+  private:
+  int _internal_uninterpreted_option_size() const;
+  public:
+  void clear_uninterpreted_option();
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* mutable_uninterpreted_option(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+      mutable_uninterpreted_option();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& _internal_uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _internal_add_uninterpreted_option();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* add_uninterpreted_option();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+      uninterpreted_option() const;
+
+  // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING];
+  bool has_ctype() const;
+  private:
+  bool _internal_has_ctype() const;
+  public:
+  void clear_ctype();
+  ::PROTOBUF_NAMESPACE_ID::FieldOptions_CType ctype() const;
+  void set_ctype(::PROTOBUF_NAMESPACE_ID::FieldOptions_CType value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::FieldOptions_CType _internal_ctype() const;
+  void _internal_set_ctype(::PROTOBUF_NAMESPACE_ID::FieldOptions_CType value);
+  public:
+
+  // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];
+  bool has_jstype() const;
+  private:
+  bool _internal_has_jstype() const;
+  public:
+  void clear_jstype();
+  ::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType jstype() const;
+  void set_jstype(::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType _internal_jstype() const;
+  void _internal_set_jstype(::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType value);
+  public:
+
+  // optional bool packed = 2;
+  bool has_packed() const;
+  private:
+  bool _internal_has_packed() const;
+  public:
+  void clear_packed();
+  bool packed() const;
+  void set_packed(bool value);
+  private:
+  bool _internal_packed() const;
+  void _internal_set_packed(bool value);
+  public:
+
+  // optional bool lazy = 5 [default = false];
+  bool has_lazy() const;
+  private:
+  bool _internal_has_lazy() const;
+  public:
+  void clear_lazy();
+  bool lazy() const;
+  void set_lazy(bool value);
+  private:
+  bool _internal_lazy() const;
+  void _internal_set_lazy(bool value);
+  public:
+
+  // optional bool unverified_lazy = 15 [default = false];
+  bool has_unverified_lazy() const;
+  private:
+  bool _internal_has_unverified_lazy() const;
+  public:
+  void clear_unverified_lazy();
+  bool unverified_lazy() const;
+  void set_unverified_lazy(bool value);
+  private:
+  bool _internal_unverified_lazy() const;
+  void _internal_set_unverified_lazy(bool value);
+  public:
+
+  // optional bool deprecated = 3 [default = false];
+  bool has_deprecated() const;
+  private:
+  bool _internal_has_deprecated() const;
+  public:
+  void clear_deprecated();
+  bool deprecated() const;
+  void set_deprecated(bool value);
+  private:
+  bool _internal_deprecated() const;
+  void _internal_set_deprecated(bool value);
+  public:
+
+  // optional bool weak = 10 [default = false];
+  bool has_weak() const;
+  private:
+  bool _internal_has_weak() const;
+  public:
+  void clear_weak();
+  bool weak() const;
+  void set_weak(bool value);
+  private:
+  bool _internal_weak() const;
+  void _internal_set_weak(bool value);
+  public:
+
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline bool HasExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.Has(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void ClearExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    _impl_._extensions_.ClearExtension(id.number());
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline int ExtensionSize(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.ExtensionSize(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_,
+                                  id.default_value());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Mutable(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), _field_type, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::SetAllocated(id.number(), _field_type, value,
+                                    &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void UnsafeArenaSetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::UnsafeArenaSetAllocated(id.number(), _field_type,
+                                               value, &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  PROTOBUF_NODISCARD inline
+      typename _proto_TypeTraits::Singular::MutableType
+      ReleaseExtension(
+          const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+              FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Release(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType
+  UnsafeArenaReleaseExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::UnsafeArenaRelease(id.number(), _field_type,
+                                                 &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_, index);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) {
+
+    return _proto_TypeTraits::Mutable(id.number(), index, &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index, typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), index, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    typename _proto_TypeTraits::Repeated::MutableType to_add =
+        _proto_TypeTraits::Add(id.number(), _field_type, &_impl_._extensions_);
+
+    return to_add;
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Add(id.number(), _field_type, _is_packed, value,
+                           &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline const typename _proto_TypeTraits::Repeated::RepeatedFieldType&
+  GetRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::GetRepeated(id.number(), _impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::RepeatedFieldType*
+  MutableRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          FieldOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::MutableRepeated(id.number(), _field_type,
+                                              _is_packed, &_impl_._extensions_);
+  }
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.FieldOptions)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
+
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption > uninterpreted_option_;
+    int ctype_;
+    int jstype_;
+    bool packed_;
+    bool lazy_;
+    bool unverified_lazy_;
+    bool deprecated_;
+    bool weak_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT OneofOptions final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.OneofOptions) */ {
+ public:
+  inline OneofOptions() : OneofOptions(nullptr) {}
+  ~OneofOptions() override;
+  explicit PROTOBUF_CONSTEXPR OneofOptions(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  OneofOptions(const OneofOptions& from);
+  OneofOptions(OneofOptions&& from) noexcept
+    : OneofOptions() {
+    *this = ::std::move(from);
+  }
+
+  inline OneofOptions& operator=(const OneofOptions& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline OneofOptions& operator=(OneofOptions&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const OneofOptions& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const OneofOptions* internal_default_instance() {
+    return reinterpret_cast<const OneofOptions*>(
+               &_OneofOptions_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    16;
+
+  friend void swap(OneofOptions& a, OneofOptions& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(OneofOptions* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(OneofOptions* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  OneofOptions* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<OneofOptions>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const OneofOptions& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const OneofOptions& from) {
+    OneofOptions::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(OneofOptions* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.OneofOptions";
+  }
+  protected:
+  explicit OneofOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kUninterpretedOptionFieldNumber = 999,
+  };
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  int uninterpreted_option_size() const;
+  private:
+  int _internal_uninterpreted_option_size() const;
+  public:
+  void clear_uninterpreted_option();
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* mutable_uninterpreted_option(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+      mutable_uninterpreted_option();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& _internal_uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _internal_add_uninterpreted_option();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* add_uninterpreted_option();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+      uninterpreted_option() const;
+
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline bool HasExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.Has(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void ClearExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    _impl_._extensions_.ClearExtension(id.number());
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline int ExtensionSize(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.ExtensionSize(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_,
+                                  id.default_value());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Mutable(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), _field_type, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::SetAllocated(id.number(), _field_type, value,
+                                    &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void UnsafeArenaSetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::UnsafeArenaSetAllocated(id.number(), _field_type,
+                                               value, &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  PROTOBUF_NODISCARD inline
+      typename _proto_TypeTraits::Singular::MutableType
+      ReleaseExtension(
+          const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+              OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Release(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType
+  UnsafeArenaReleaseExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::UnsafeArenaRelease(id.number(), _field_type,
+                                                 &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_, index);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) {
+
+    return _proto_TypeTraits::Mutable(id.number(), index, &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index, typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), index, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    typename _proto_TypeTraits::Repeated::MutableType to_add =
+        _proto_TypeTraits::Add(id.number(), _field_type, &_impl_._extensions_);
+
+    return to_add;
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Add(id.number(), _field_type, _is_packed, value,
+                           &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline const typename _proto_TypeTraits::Repeated::RepeatedFieldType&
+  GetRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::GetRepeated(id.number(), _impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::RepeatedFieldType*
+  MutableRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          OneofOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::MutableRepeated(id.number(), _field_type,
+                                              _is_packed, &_impl_._extensions_);
+  }
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.OneofOptions)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
+
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption > uninterpreted_option_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT EnumOptions final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.EnumOptions) */ {
+ public:
+  inline EnumOptions() : EnumOptions(nullptr) {}
+  ~EnumOptions() override;
+  explicit PROTOBUF_CONSTEXPR EnumOptions(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  EnumOptions(const EnumOptions& from);
+  EnumOptions(EnumOptions&& from) noexcept
+    : EnumOptions() {
+    *this = ::std::move(from);
+  }
+
+  inline EnumOptions& operator=(const EnumOptions& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline EnumOptions& operator=(EnumOptions&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const EnumOptions& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const EnumOptions* internal_default_instance() {
+    return reinterpret_cast<const EnumOptions*>(
+               &_EnumOptions_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    17;
+
+  friend void swap(EnumOptions& a, EnumOptions& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(EnumOptions* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(EnumOptions* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  EnumOptions* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<EnumOptions>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const EnumOptions& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const EnumOptions& from) {
+    EnumOptions::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(EnumOptions* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.EnumOptions";
+  }
+  protected:
+  explicit EnumOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kUninterpretedOptionFieldNumber = 999,
+    kAllowAliasFieldNumber = 2,
+    kDeprecatedFieldNumber = 3,
+  };
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  int uninterpreted_option_size() const;
+  private:
+  int _internal_uninterpreted_option_size() const;
+  public:
+  void clear_uninterpreted_option();
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* mutable_uninterpreted_option(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+      mutable_uninterpreted_option();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& _internal_uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _internal_add_uninterpreted_option();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* add_uninterpreted_option();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+      uninterpreted_option() const;
+
+  // optional bool allow_alias = 2;
+  bool has_allow_alias() const;
+  private:
+  bool _internal_has_allow_alias() const;
+  public:
+  void clear_allow_alias();
+  bool allow_alias() const;
+  void set_allow_alias(bool value);
+  private:
+  bool _internal_allow_alias() const;
+  void _internal_set_allow_alias(bool value);
+  public:
+
+  // optional bool deprecated = 3 [default = false];
+  bool has_deprecated() const;
+  private:
+  bool _internal_has_deprecated() const;
+  public:
+  void clear_deprecated();
+  bool deprecated() const;
+  void set_deprecated(bool value);
+  private:
+  bool _internal_deprecated() const;
+  void _internal_set_deprecated(bool value);
+  public:
+
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline bool HasExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.Has(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void ClearExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    _impl_._extensions_.ClearExtension(id.number());
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline int ExtensionSize(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.ExtensionSize(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_,
+                                  id.default_value());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Mutable(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), _field_type, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::SetAllocated(id.number(), _field_type, value,
+                                    &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void UnsafeArenaSetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::UnsafeArenaSetAllocated(id.number(), _field_type,
+                                               value, &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  PROTOBUF_NODISCARD inline
+      typename _proto_TypeTraits::Singular::MutableType
+      ReleaseExtension(
+          const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+              EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Release(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType
+  UnsafeArenaReleaseExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::UnsafeArenaRelease(id.number(), _field_type,
+                                                 &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_, index);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) {
+
+    return _proto_TypeTraits::Mutable(id.number(), index, &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index, typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), index, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    typename _proto_TypeTraits::Repeated::MutableType to_add =
+        _proto_TypeTraits::Add(id.number(), _field_type, &_impl_._extensions_);
+
+    return to_add;
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Add(id.number(), _field_type, _is_packed, value,
+                           &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline const typename _proto_TypeTraits::Repeated::RepeatedFieldType&
+  GetRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::GetRepeated(id.number(), _impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::RepeatedFieldType*
+  MutableRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::MutableRepeated(id.number(), _field_type,
+                                              _is_packed, &_impl_._extensions_);
+  }
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.EnumOptions)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
+
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption > uninterpreted_option_;
+    bool allow_alias_;
+    bool deprecated_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT EnumValueOptions final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.EnumValueOptions) */ {
+ public:
+  inline EnumValueOptions() : EnumValueOptions(nullptr) {}
+  ~EnumValueOptions() override;
+  explicit PROTOBUF_CONSTEXPR EnumValueOptions(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  EnumValueOptions(const EnumValueOptions& from);
+  EnumValueOptions(EnumValueOptions&& from) noexcept
+    : EnumValueOptions() {
+    *this = ::std::move(from);
+  }
+
+  inline EnumValueOptions& operator=(const EnumValueOptions& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline EnumValueOptions& operator=(EnumValueOptions&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const EnumValueOptions& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const EnumValueOptions* internal_default_instance() {
+    return reinterpret_cast<const EnumValueOptions*>(
+               &_EnumValueOptions_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    18;
+
+  friend void swap(EnumValueOptions& a, EnumValueOptions& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(EnumValueOptions* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(EnumValueOptions* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  EnumValueOptions* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<EnumValueOptions>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const EnumValueOptions& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const EnumValueOptions& from) {
+    EnumValueOptions::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(EnumValueOptions* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.EnumValueOptions";
+  }
+  protected:
+  explicit EnumValueOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kUninterpretedOptionFieldNumber = 999,
+    kDeprecatedFieldNumber = 1,
+  };
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  int uninterpreted_option_size() const;
+  private:
+  int _internal_uninterpreted_option_size() const;
+  public:
+  void clear_uninterpreted_option();
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* mutable_uninterpreted_option(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+      mutable_uninterpreted_option();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& _internal_uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _internal_add_uninterpreted_option();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* add_uninterpreted_option();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+      uninterpreted_option() const;
+
+  // optional bool deprecated = 1 [default = false];
+  bool has_deprecated() const;
+  private:
+  bool _internal_has_deprecated() const;
+  public:
+  void clear_deprecated();
+  bool deprecated() const;
+  void set_deprecated(bool value);
+  private:
+  bool _internal_deprecated() const;
+  void _internal_set_deprecated(bool value);
+  public:
+
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline bool HasExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.Has(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void ClearExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    _impl_._extensions_.ClearExtension(id.number());
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline int ExtensionSize(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.ExtensionSize(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_,
+                                  id.default_value());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Mutable(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), _field_type, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::SetAllocated(id.number(), _field_type, value,
+                                    &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void UnsafeArenaSetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::UnsafeArenaSetAllocated(id.number(), _field_type,
+                                               value, &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  PROTOBUF_NODISCARD inline
+      typename _proto_TypeTraits::Singular::MutableType
+      ReleaseExtension(
+          const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+              EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Release(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType
+  UnsafeArenaReleaseExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::UnsafeArenaRelease(id.number(), _field_type,
+                                                 &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_, index);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) {
+
+    return _proto_TypeTraits::Mutable(id.number(), index, &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index, typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), index, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    typename _proto_TypeTraits::Repeated::MutableType to_add =
+        _proto_TypeTraits::Add(id.number(), _field_type, &_impl_._extensions_);
+
+    return to_add;
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Add(id.number(), _field_type, _is_packed, value,
+                           &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline const typename _proto_TypeTraits::Repeated::RepeatedFieldType&
+  GetRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::GetRepeated(id.number(), _impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::RepeatedFieldType*
+  MutableRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          EnumValueOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::MutableRepeated(id.number(), _field_type,
+                                              _is_packed, &_impl_._extensions_);
+  }
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.EnumValueOptions)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
+
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption > uninterpreted_option_;
+    bool deprecated_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT ServiceOptions final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.ServiceOptions) */ {
+ public:
+  inline ServiceOptions() : ServiceOptions(nullptr) {}
+  ~ServiceOptions() override;
+  explicit PROTOBUF_CONSTEXPR ServiceOptions(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  ServiceOptions(const ServiceOptions& from);
+  ServiceOptions(ServiceOptions&& from) noexcept
+    : ServiceOptions() {
+    *this = ::std::move(from);
+  }
+
+  inline ServiceOptions& operator=(const ServiceOptions& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline ServiceOptions& operator=(ServiceOptions&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const ServiceOptions& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const ServiceOptions* internal_default_instance() {
+    return reinterpret_cast<const ServiceOptions*>(
+               &_ServiceOptions_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    19;
+
+  friend void swap(ServiceOptions& a, ServiceOptions& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(ServiceOptions* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(ServiceOptions* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  ServiceOptions* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<ServiceOptions>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const ServiceOptions& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const ServiceOptions& from) {
+    ServiceOptions::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(ServiceOptions* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.ServiceOptions";
+  }
+  protected:
+  explicit ServiceOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kUninterpretedOptionFieldNumber = 999,
+    kDeprecatedFieldNumber = 33,
+  };
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  int uninterpreted_option_size() const;
+  private:
+  int _internal_uninterpreted_option_size() const;
+  public:
+  void clear_uninterpreted_option();
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* mutable_uninterpreted_option(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+      mutable_uninterpreted_option();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& _internal_uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _internal_add_uninterpreted_option();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* add_uninterpreted_option();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+      uninterpreted_option() const;
+
+  // optional bool deprecated = 33 [default = false];
+  bool has_deprecated() const;
+  private:
+  bool _internal_has_deprecated() const;
+  public:
+  void clear_deprecated();
+  bool deprecated() const;
+  void set_deprecated(bool value);
+  private:
+  bool _internal_deprecated() const;
+  void _internal_set_deprecated(bool value);
+  public:
+
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline bool HasExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.Has(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void ClearExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    _impl_._extensions_.ClearExtension(id.number());
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline int ExtensionSize(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.ExtensionSize(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_,
+                                  id.default_value());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Mutable(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), _field_type, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::SetAllocated(id.number(), _field_type, value,
+                                    &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void UnsafeArenaSetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::UnsafeArenaSetAllocated(id.number(), _field_type,
+                                               value, &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  PROTOBUF_NODISCARD inline
+      typename _proto_TypeTraits::Singular::MutableType
+      ReleaseExtension(
+          const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+              ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Release(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType
+  UnsafeArenaReleaseExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::UnsafeArenaRelease(id.number(), _field_type,
+                                                 &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_, index);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) {
+
+    return _proto_TypeTraits::Mutable(id.number(), index, &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index, typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), index, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    typename _proto_TypeTraits::Repeated::MutableType to_add =
+        _proto_TypeTraits::Add(id.number(), _field_type, &_impl_._extensions_);
+
+    return to_add;
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Add(id.number(), _field_type, _is_packed, value,
+                           &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline const typename _proto_TypeTraits::Repeated::RepeatedFieldType&
+  GetRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::GetRepeated(id.number(), _impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::RepeatedFieldType*
+  MutableRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          ServiceOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::MutableRepeated(id.number(), _field_type,
+                                              _is_packed, &_impl_._extensions_);
+  }
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.ServiceOptions)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
+
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption > uninterpreted_option_;
+    bool deprecated_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT MethodOptions final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.MethodOptions) */ {
+ public:
+  inline MethodOptions() : MethodOptions(nullptr) {}
+  ~MethodOptions() override;
+  explicit PROTOBUF_CONSTEXPR MethodOptions(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  MethodOptions(const MethodOptions& from);
+  MethodOptions(MethodOptions&& from) noexcept
+    : MethodOptions() {
+    *this = ::std::move(from);
+  }
+
+  inline MethodOptions& operator=(const MethodOptions& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline MethodOptions& operator=(MethodOptions&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const MethodOptions& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const MethodOptions* internal_default_instance() {
+    return reinterpret_cast<const MethodOptions*>(
+               &_MethodOptions_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    20;
+
+  friend void swap(MethodOptions& a, MethodOptions& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(MethodOptions* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(MethodOptions* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  MethodOptions* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<MethodOptions>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const MethodOptions& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const MethodOptions& from) {
+    MethodOptions::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(MethodOptions* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.MethodOptions";
+  }
+  protected:
+  explicit MethodOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef MethodOptions_IdempotencyLevel IdempotencyLevel;
+  static constexpr IdempotencyLevel IDEMPOTENCY_UNKNOWN =
+    MethodOptions_IdempotencyLevel_IDEMPOTENCY_UNKNOWN;
+  static constexpr IdempotencyLevel NO_SIDE_EFFECTS =
+    MethodOptions_IdempotencyLevel_NO_SIDE_EFFECTS;
+  static constexpr IdempotencyLevel IDEMPOTENT =
+    MethodOptions_IdempotencyLevel_IDEMPOTENT;
+  static inline bool IdempotencyLevel_IsValid(int value) {
+    return MethodOptions_IdempotencyLevel_IsValid(value);
+  }
+  static constexpr IdempotencyLevel IdempotencyLevel_MIN =
+    MethodOptions_IdempotencyLevel_IdempotencyLevel_MIN;
+  static constexpr IdempotencyLevel IdempotencyLevel_MAX =
+    MethodOptions_IdempotencyLevel_IdempotencyLevel_MAX;
+  static constexpr int IdempotencyLevel_ARRAYSIZE =
+    MethodOptions_IdempotencyLevel_IdempotencyLevel_ARRAYSIZE;
+  static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
+  IdempotencyLevel_descriptor() {
+    return MethodOptions_IdempotencyLevel_descriptor();
+  }
+  template<typename T>
+  static inline const std::string& IdempotencyLevel_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, IdempotencyLevel>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function IdempotencyLevel_Name.");
+    return MethodOptions_IdempotencyLevel_Name(enum_t_value);
+  }
+  static inline bool IdempotencyLevel_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+      IdempotencyLevel* value) {
+    return MethodOptions_IdempotencyLevel_Parse(name, value);
+  }
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kUninterpretedOptionFieldNumber = 999,
+    kDeprecatedFieldNumber = 33,
+    kIdempotencyLevelFieldNumber = 34,
+  };
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  int uninterpreted_option_size() const;
+  private:
+  int _internal_uninterpreted_option_size() const;
+  public:
+  void clear_uninterpreted_option();
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* mutable_uninterpreted_option(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+      mutable_uninterpreted_option();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& _internal_uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _internal_add_uninterpreted_option();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& uninterpreted_option(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* add_uninterpreted_option();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+      uninterpreted_option() const;
+
+  // optional bool deprecated = 33 [default = false];
+  bool has_deprecated() const;
+  private:
+  bool _internal_has_deprecated() const;
+  public:
+  void clear_deprecated();
+  bool deprecated() const;
+  void set_deprecated(bool value);
+  private:
+  bool _internal_deprecated() const;
+  void _internal_set_deprecated(bool value);
+  public:
+
+  // optional .google.protobuf.MethodOptions.IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN];
+  bool has_idempotency_level() const;
+  private:
+  bool _internal_has_idempotency_level() const;
+  public:
+  void clear_idempotency_level();
+  ::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel idempotency_level() const;
+  void set_idempotency_level(::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel _internal_idempotency_level() const;
+  void _internal_set_idempotency_level(::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel value);
+  public:
+
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline bool HasExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.Has(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void ClearExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    _impl_._extensions_.ClearExtension(id.number());
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline int ExtensionSize(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _impl_._extensions_.ExtensionSize(id.number());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_,
+                                  id.default_value());
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Mutable(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), _field_type, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::SetAllocated(id.number(), _field_type, value,
+                                    &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void UnsafeArenaSetAllocatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Singular::MutableType value) {
+    _proto_TypeTraits::UnsafeArenaSetAllocated(id.number(), _field_type,
+                                               value, &_impl_._extensions_);
+
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  PROTOBUF_NODISCARD inline
+      typename _proto_TypeTraits::Singular::MutableType
+      ReleaseExtension(
+          const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+              MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::Release(id.number(), _field_type,
+                                      &_impl_._extensions_);
+  }
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Singular::MutableType
+  UnsafeArenaReleaseExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::UnsafeArenaRelease(id.number(), _field_type,
+                                                 &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::ConstType GetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) const {
+
+    return _proto_TypeTraits::Get(id.number(), _impl_._extensions_, index);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType MutableExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index) {
+
+    return _proto_TypeTraits::Mutable(id.number(), index, &_impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void SetExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      int index, typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Set(id.number(), index, value, &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::MutableType AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+    typename _proto_TypeTraits::Repeated::MutableType to_add =
+        _proto_TypeTraits::Add(id.number(), _field_type, &_impl_._extensions_);
+
+    return to_add;
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline void AddExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id,
+      typename _proto_TypeTraits::Repeated::ConstType value) {
+    _proto_TypeTraits::Add(id.number(), _field_type, _is_packed, value,
+                           &_impl_._extensions_);
+
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline const typename _proto_TypeTraits::Repeated::RepeatedFieldType&
+  GetRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id) const {
+
+    return _proto_TypeTraits::GetRepeated(id.number(), _impl_._extensions_);
+  }
+
+  template <typename _proto_TypeTraits,
+            ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
+            bool _is_packed>
+  inline typename _proto_TypeTraits::Repeated::RepeatedFieldType*
+  MutableRepeatedExtension(
+      const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
+          MethodOptions, _proto_TypeTraits, _field_type, _is_packed>& id) {
+
+    return _proto_TypeTraits::MutableRepeated(id.number(), _field_type,
+                                              _is_packed, &_impl_._extensions_);
+  }
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.MethodOptions)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
+
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption > uninterpreted_option_;
+    bool deprecated_;
+    int idempotency_level_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT UninterpretedOption_NamePart final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.UninterpretedOption.NamePart) */ {
+ public:
+  inline UninterpretedOption_NamePart() : UninterpretedOption_NamePart(nullptr) {}
+  ~UninterpretedOption_NamePart() override;
+  explicit PROTOBUF_CONSTEXPR UninterpretedOption_NamePart(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  UninterpretedOption_NamePart(const UninterpretedOption_NamePart& from);
+  UninterpretedOption_NamePart(UninterpretedOption_NamePart&& from) noexcept
+    : UninterpretedOption_NamePart() {
+    *this = ::std::move(from);
+  }
+
+  inline UninterpretedOption_NamePart& operator=(const UninterpretedOption_NamePart& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline UninterpretedOption_NamePart& operator=(UninterpretedOption_NamePart&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const UninterpretedOption_NamePart& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const UninterpretedOption_NamePart* internal_default_instance() {
+    return reinterpret_cast<const UninterpretedOption_NamePart*>(
+               &_UninterpretedOption_NamePart_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    21;
+
+  friend void swap(UninterpretedOption_NamePart& a, UninterpretedOption_NamePart& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(UninterpretedOption_NamePart* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(UninterpretedOption_NamePart* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  UninterpretedOption_NamePart* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<UninterpretedOption_NamePart>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const UninterpretedOption_NamePart& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const UninterpretedOption_NamePart& from) {
+    UninterpretedOption_NamePart::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(UninterpretedOption_NamePart* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.UninterpretedOption.NamePart";
+  }
+  protected:
+  explicit UninterpretedOption_NamePart(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNamePartFieldNumber = 1,
+    kIsExtensionFieldNumber = 2,
+  };
+  // required string name_part = 1;
+  bool has_name_part() const;
+  private:
+  bool _internal_has_name_part() const;
+  public:
+  void clear_name_part();
+  const std::string& name_part() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name_part(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name_part();
+  PROTOBUF_NODISCARD std::string* release_name_part();
+  void set_allocated_name_part(std::string* name_part);
+  private:
+  const std::string& _internal_name_part() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name_part(const std::string& value);
+  std::string* _internal_mutable_name_part();
+  public:
+
+  // required bool is_extension = 2;
+  bool has_is_extension() const;
+  private:
+  bool _internal_has_is_extension() const;
+  public:
+  void clear_is_extension();
+  bool is_extension() const;
+  void set_is_extension(bool value);
+  private:
+  bool _internal_is_extension() const;
+  void _internal_set_is_extension(bool value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.UninterpretedOption.NamePart)
+ private:
+  class _Internal;
+
+  // helper for ByteSizeLong()
+  size_t RequiredFieldsByteSizeFallback() const;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_part_;
+    bool is_extension_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT UninterpretedOption final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.UninterpretedOption) */ {
+ public:
+  inline UninterpretedOption() : UninterpretedOption(nullptr) {}
+  ~UninterpretedOption() override;
+  explicit PROTOBUF_CONSTEXPR UninterpretedOption(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  UninterpretedOption(const UninterpretedOption& from);
+  UninterpretedOption(UninterpretedOption&& from) noexcept
+    : UninterpretedOption() {
+    *this = ::std::move(from);
+  }
+
+  inline UninterpretedOption& operator=(const UninterpretedOption& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline UninterpretedOption& operator=(UninterpretedOption&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const UninterpretedOption& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const UninterpretedOption* internal_default_instance() {
+    return reinterpret_cast<const UninterpretedOption*>(
+               &_UninterpretedOption_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    22;
+
+  friend void swap(UninterpretedOption& a, UninterpretedOption& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(UninterpretedOption* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(UninterpretedOption* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  UninterpretedOption* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<UninterpretedOption>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const UninterpretedOption& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const UninterpretedOption& from) {
+    UninterpretedOption::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(UninterpretedOption* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.UninterpretedOption";
+  }
+  protected:
+  explicit UninterpretedOption(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef UninterpretedOption_NamePart NamePart;
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNameFieldNumber = 2,
+    kIdentifierValueFieldNumber = 3,
+    kStringValueFieldNumber = 7,
+    kAggregateValueFieldNumber = 8,
+    kPositiveIntValueFieldNumber = 4,
+    kNegativeIntValueFieldNumber = 5,
+    kDoubleValueFieldNumber = 6,
+  };
+  // repeated .google.protobuf.UninterpretedOption.NamePart name = 2;
+  int name_size() const;
+  private:
+  int _internal_name_size() const;
+  public:
+  void clear_name();
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart* mutable_name(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart >*
+      mutable_name();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart& _internal_name(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart* _internal_add_name();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart& name(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart* add_name();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart >&
+      name() const;
+
+  // optional string identifier_value = 3;
+  bool has_identifier_value() const;
+  private:
+  bool _internal_has_identifier_value() const;
+  public:
+  void clear_identifier_value();
+  const std::string& identifier_value() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_identifier_value(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_identifier_value();
+  PROTOBUF_NODISCARD std::string* release_identifier_value();
+  void set_allocated_identifier_value(std::string* identifier_value);
+  private:
+  const std::string& _internal_identifier_value() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_identifier_value(const std::string& value);
+  std::string* _internal_mutable_identifier_value();
+  public:
+
+  // optional bytes string_value = 7;
+  bool has_string_value() const;
+  private:
+  bool _internal_has_string_value() const;
+  public:
+  void clear_string_value();
+  const std::string& string_value() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_string_value(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_string_value();
+  PROTOBUF_NODISCARD std::string* release_string_value();
+  void set_allocated_string_value(std::string* string_value);
+  private:
+  const std::string& _internal_string_value() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_string_value(const std::string& value);
+  std::string* _internal_mutable_string_value();
+  public:
+
+  // optional string aggregate_value = 8;
+  bool has_aggregate_value() const;
+  private:
+  bool _internal_has_aggregate_value() const;
+  public:
+  void clear_aggregate_value();
+  const std::string& aggregate_value() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_aggregate_value(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_aggregate_value();
+  PROTOBUF_NODISCARD std::string* release_aggregate_value();
+  void set_allocated_aggregate_value(std::string* aggregate_value);
+  private:
+  const std::string& _internal_aggregate_value() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_aggregate_value(const std::string& value);
+  std::string* _internal_mutable_aggregate_value();
+  public:
+
+  // optional uint64 positive_int_value = 4;
+  bool has_positive_int_value() const;
+  private:
+  bool _internal_has_positive_int_value() const;
+  public:
+  void clear_positive_int_value();
+  uint64_t positive_int_value() const;
+  void set_positive_int_value(uint64_t value);
+  private:
+  uint64_t _internal_positive_int_value() const;
+  void _internal_set_positive_int_value(uint64_t value);
+  public:
+
+  // optional int64 negative_int_value = 5;
+  bool has_negative_int_value() const;
+  private:
+  bool _internal_has_negative_int_value() const;
+  public:
+  void clear_negative_int_value();
+  int64_t negative_int_value() const;
+  void set_negative_int_value(int64_t value);
+  private:
+  int64_t _internal_negative_int_value() const;
+  void _internal_set_negative_int_value(int64_t value);
+  public:
+
+  // optional double double_value = 6;
+  bool has_double_value() const;
+  private:
+  bool _internal_has_double_value() const;
+  public:
+  void clear_double_value();
+  double double_value() const;
+  void set_double_value(double value);
+  private:
+  double _internal_double_value() const;
+  void _internal_set_double_value(double value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.UninterpretedOption)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart > name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr identifier_value_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr string_value_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr aggregate_value_;
+    uint64_t positive_int_value_;
+    int64_t negative_int_value_;
+    double double_value_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT SourceCodeInfo_Location final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.SourceCodeInfo.Location) */ {
+ public:
+  inline SourceCodeInfo_Location() : SourceCodeInfo_Location(nullptr) {}
+  ~SourceCodeInfo_Location() override;
+  explicit PROTOBUF_CONSTEXPR SourceCodeInfo_Location(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  SourceCodeInfo_Location(const SourceCodeInfo_Location& from);
+  SourceCodeInfo_Location(SourceCodeInfo_Location&& from) noexcept
+    : SourceCodeInfo_Location() {
+    *this = ::std::move(from);
+  }
+
+  inline SourceCodeInfo_Location& operator=(const SourceCodeInfo_Location& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline SourceCodeInfo_Location& operator=(SourceCodeInfo_Location&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const SourceCodeInfo_Location& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const SourceCodeInfo_Location* internal_default_instance() {
+    return reinterpret_cast<const SourceCodeInfo_Location*>(
+               &_SourceCodeInfo_Location_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    23;
+
+  friend void swap(SourceCodeInfo_Location& a, SourceCodeInfo_Location& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(SourceCodeInfo_Location* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(SourceCodeInfo_Location* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  SourceCodeInfo_Location* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<SourceCodeInfo_Location>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const SourceCodeInfo_Location& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const SourceCodeInfo_Location& from) {
+    SourceCodeInfo_Location::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(SourceCodeInfo_Location* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.SourceCodeInfo.Location";
+  }
+  protected:
+  explicit SourceCodeInfo_Location(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kPathFieldNumber = 1,
+    kSpanFieldNumber = 2,
+    kLeadingDetachedCommentsFieldNumber = 6,
+    kLeadingCommentsFieldNumber = 3,
+    kTrailingCommentsFieldNumber = 4,
+  };
+  // repeated int32 path = 1 [packed = true];
+  int path_size() const;
+  private:
+  int _internal_path_size() const;
+  public:
+  void clear_path();
+  private:
+  int32_t _internal_path(int index) const;
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+      _internal_path() const;
+  void _internal_add_path(int32_t value);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+      _internal_mutable_path();
+  public:
+  int32_t path(int index) const;
+  void set_path(int index, int32_t value);
+  void add_path(int32_t value);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+      path() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+      mutable_path();
+
+  // repeated int32 span = 2 [packed = true];
+  int span_size() const;
+  private:
+  int _internal_span_size() const;
+  public:
+  void clear_span();
+  private:
+  int32_t _internal_span(int index) const;
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+      _internal_span() const;
+  void _internal_add_span(int32_t value);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+      _internal_mutable_span();
+  public:
+  int32_t span(int index) const;
+  void set_span(int index, int32_t value);
+  void add_span(int32_t value);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+      span() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+      mutable_span();
+
+  // repeated string leading_detached_comments = 6;
+  int leading_detached_comments_size() const;
+  private:
+  int _internal_leading_detached_comments_size() const;
+  public:
+  void clear_leading_detached_comments();
+  const std::string& leading_detached_comments(int index) const;
+  std::string* mutable_leading_detached_comments(int index);
+  void set_leading_detached_comments(int index, const std::string& value);
+  void set_leading_detached_comments(int index, std::string&& value);
+  void set_leading_detached_comments(int index, const char* value);
+  void set_leading_detached_comments(int index, const char* value, size_t size);
+  std::string* add_leading_detached_comments();
+  void add_leading_detached_comments(const std::string& value);
+  void add_leading_detached_comments(std::string&& value);
+  void add_leading_detached_comments(const char* value);
+  void add_leading_detached_comments(const char* value, size_t size);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& leading_detached_comments() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_leading_detached_comments();
+  private:
+  const std::string& _internal_leading_detached_comments(int index) const;
+  std::string* _internal_add_leading_detached_comments();
+  public:
+
+  // optional string leading_comments = 3;
+  bool has_leading_comments() const;
+  private:
+  bool _internal_has_leading_comments() const;
+  public:
+  void clear_leading_comments();
+  const std::string& leading_comments() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_leading_comments(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_leading_comments();
+  PROTOBUF_NODISCARD std::string* release_leading_comments();
+  void set_allocated_leading_comments(std::string* leading_comments);
+  private:
+  const std::string& _internal_leading_comments() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_leading_comments(const std::string& value);
+  std::string* _internal_mutable_leading_comments();
+  public:
+
+  // optional string trailing_comments = 4;
+  bool has_trailing_comments() const;
+  private:
+  bool _internal_has_trailing_comments() const;
+  public:
+  void clear_trailing_comments();
+  const std::string& trailing_comments() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_trailing_comments(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_trailing_comments();
+  PROTOBUF_NODISCARD std::string* release_trailing_comments();
+  void set_allocated_trailing_comments(std::string* trailing_comments);
+  private:
+  const std::string& _internal_trailing_comments() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_trailing_comments(const std::string& value);
+  std::string* _internal_mutable_trailing_comments();
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.SourceCodeInfo.Location)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > path_;
+    mutable std::atomic<int> _path_cached_byte_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > span_;
+    mutable std::atomic<int> _span_cached_byte_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> leading_detached_comments_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr leading_comments_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr trailing_comments_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT SourceCodeInfo final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.SourceCodeInfo) */ {
+ public:
+  inline SourceCodeInfo() : SourceCodeInfo(nullptr) {}
+  ~SourceCodeInfo() override;
+  explicit PROTOBUF_CONSTEXPR SourceCodeInfo(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  SourceCodeInfo(const SourceCodeInfo& from);
+  SourceCodeInfo(SourceCodeInfo&& from) noexcept
+    : SourceCodeInfo() {
+    *this = ::std::move(from);
+  }
+
+  inline SourceCodeInfo& operator=(const SourceCodeInfo& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline SourceCodeInfo& operator=(SourceCodeInfo&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const SourceCodeInfo& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const SourceCodeInfo* internal_default_instance() {
+    return reinterpret_cast<const SourceCodeInfo*>(
+               &_SourceCodeInfo_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    24;
+
+  friend void swap(SourceCodeInfo& a, SourceCodeInfo& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(SourceCodeInfo* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(SourceCodeInfo* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  SourceCodeInfo* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<SourceCodeInfo>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const SourceCodeInfo& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const SourceCodeInfo& from) {
+    SourceCodeInfo::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(SourceCodeInfo* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.SourceCodeInfo";
+  }
+  protected:
+  explicit SourceCodeInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef SourceCodeInfo_Location Location;
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kLocationFieldNumber = 1,
+  };
+  // repeated .google.protobuf.SourceCodeInfo.Location location = 1;
+  int location_size() const;
+  private:
+  int _internal_location_size() const;
+  public:
+  void clear_location();
+  ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location* mutable_location(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location >*
+      mutable_location();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location& _internal_location(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location* _internal_add_location();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location& location(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location* add_location();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location >&
+      location() const;
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.SourceCodeInfo)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location > location_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT GeneratedCodeInfo_Annotation final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.GeneratedCodeInfo.Annotation) */ {
+ public:
+  inline GeneratedCodeInfo_Annotation() : GeneratedCodeInfo_Annotation(nullptr) {}
+  ~GeneratedCodeInfo_Annotation() override;
+  explicit PROTOBUF_CONSTEXPR GeneratedCodeInfo_Annotation(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  GeneratedCodeInfo_Annotation(const GeneratedCodeInfo_Annotation& from);
+  GeneratedCodeInfo_Annotation(GeneratedCodeInfo_Annotation&& from) noexcept
+    : GeneratedCodeInfo_Annotation() {
+    *this = ::std::move(from);
+  }
+
+  inline GeneratedCodeInfo_Annotation& operator=(const GeneratedCodeInfo_Annotation& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline GeneratedCodeInfo_Annotation& operator=(GeneratedCodeInfo_Annotation&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const GeneratedCodeInfo_Annotation& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const GeneratedCodeInfo_Annotation* internal_default_instance() {
+    return reinterpret_cast<const GeneratedCodeInfo_Annotation*>(
+               &_GeneratedCodeInfo_Annotation_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    25;
+
+  friend void swap(GeneratedCodeInfo_Annotation& a, GeneratedCodeInfo_Annotation& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(GeneratedCodeInfo_Annotation* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(GeneratedCodeInfo_Annotation* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  GeneratedCodeInfo_Annotation* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<GeneratedCodeInfo_Annotation>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const GeneratedCodeInfo_Annotation& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const GeneratedCodeInfo_Annotation& from) {
+    GeneratedCodeInfo_Annotation::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(GeneratedCodeInfo_Annotation* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.GeneratedCodeInfo.Annotation";
+  }
+  protected:
+  explicit GeneratedCodeInfo_Annotation(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kPathFieldNumber = 1,
+    kSourceFileFieldNumber = 2,
+    kBeginFieldNumber = 3,
+    kEndFieldNumber = 4,
+  };
+  // repeated int32 path = 1 [packed = true];
+  int path_size() const;
+  private:
+  int _internal_path_size() const;
+  public:
+  void clear_path();
+  private:
+  int32_t _internal_path(int index) const;
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+      _internal_path() const;
+  void _internal_add_path(int32_t value);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+      _internal_mutable_path();
+  public:
+  int32_t path(int index) const;
+  void set_path(int index, int32_t value);
+  void add_path(int32_t value);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+      path() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+      mutable_path();
+
+  // optional string source_file = 2;
+  bool has_source_file() const;
+  private:
+  bool _internal_has_source_file() const;
+  public:
+  void clear_source_file();
+  const std::string& source_file() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_source_file(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_source_file();
+  PROTOBUF_NODISCARD std::string* release_source_file();
+  void set_allocated_source_file(std::string* source_file);
+  private:
+  const std::string& _internal_source_file() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_source_file(const std::string& value);
+  std::string* _internal_mutable_source_file();
+  public:
+
+  // optional int32 begin = 3;
+  bool has_begin() const;
+  private:
+  bool _internal_has_begin() const;
+  public:
+  void clear_begin();
+  int32_t begin() const;
+  void set_begin(int32_t value);
+  private:
+  int32_t _internal_begin() const;
+  void _internal_set_begin(int32_t value);
+  public:
+
+  // optional int32 end = 4;
+  bool has_end() const;
+  private:
+  bool _internal_has_end() const;
+  public:
+  void clear_end();
+  int32_t end() const;
+  void set_end(int32_t value);
+  private:
+  int32_t _internal_end() const;
+  void _internal_set_end(int32_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.GeneratedCodeInfo.Annotation)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > path_;
+    mutable std::atomic<int> _path_cached_byte_size_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr source_file_;
+    int32_t begin_;
+    int32_t end_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT GeneratedCodeInfo final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.GeneratedCodeInfo) */ {
+ public:
+  inline GeneratedCodeInfo() : GeneratedCodeInfo(nullptr) {}
+  ~GeneratedCodeInfo() override;
+  explicit PROTOBUF_CONSTEXPR GeneratedCodeInfo(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  GeneratedCodeInfo(const GeneratedCodeInfo& from);
+  GeneratedCodeInfo(GeneratedCodeInfo&& from) noexcept
+    : GeneratedCodeInfo() {
+    *this = ::std::move(from);
+  }
+
+  inline GeneratedCodeInfo& operator=(const GeneratedCodeInfo& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline GeneratedCodeInfo& operator=(GeneratedCodeInfo&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const GeneratedCodeInfo& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const GeneratedCodeInfo* internal_default_instance() {
+    return reinterpret_cast<const GeneratedCodeInfo*>(
+               &_GeneratedCodeInfo_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    26;
+
+  friend void swap(GeneratedCodeInfo& a, GeneratedCodeInfo& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(GeneratedCodeInfo* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(GeneratedCodeInfo* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  GeneratedCodeInfo* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<GeneratedCodeInfo>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const GeneratedCodeInfo& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const GeneratedCodeInfo& from) {
+    GeneratedCodeInfo::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(GeneratedCodeInfo* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.GeneratedCodeInfo";
+  }
+  protected:
+  explicit GeneratedCodeInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef GeneratedCodeInfo_Annotation Annotation;
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kAnnotationFieldNumber = 1,
+  };
+  // repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+  int annotation_size() const;
+  private:
+  int _internal_annotation_size() const;
+  public:
+  void clear_annotation();
+  ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation* mutable_annotation(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation >*
+      mutable_annotation();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation& _internal_annotation(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation* _internal_add_annotation();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation& annotation(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation* add_annotation();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation >&
+      annotation() const;
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.GeneratedCodeInfo)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation > annotation_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fdescriptor_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// FileDescriptorSet
+
+// repeated .google.protobuf.FileDescriptorProto file = 1;
+inline int FileDescriptorSet::_internal_file_size() const {
+  return _impl_.file_.size();
+}
+inline int FileDescriptorSet::file_size() const {
+  return _internal_file_size();
+}
+inline void FileDescriptorSet::clear_file() {
+  _impl_.file_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto* FileDescriptorSet::mutable_file(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorSet.file)
+  return _impl_.file_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto >*
+FileDescriptorSet::mutable_file() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorSet.file)
+  return &_impl_.file_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto& FileDescriptorSet::_internal_file(int index) const {
+  return _impl_.file_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto& FileDescriptorSet::file(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorSet.file)
+  return _internal_file(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto* FileDescriptorSet::_internal_add_file() {
+  return _impl_.file_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto* FileDescriptorSet::add_file() {
+  ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto* _add = _internal_add_file();
+  // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorSet.file)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto >&
+FileDescriptorSet::file() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorSet.file)
+  return _impl_.file_;
+}
+
+// -------------------------------------------------------------------
+
+// FileDescriptorProto
+
+// optional string name = 1;
+inline bool FileDescriptorProto::_internal_has_name() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool FileDescriptorProto::has_name() const {
+  return _internal_has_name();
+}
+inline void FileDescriptorProto::clear_name() {
+  _impl_.name_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& FileDescriptorProto::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileDescriptorProto::set_name(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.name)
+}
+inline std::string* FileDescriptorProto::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.name)
+  return _s;
+}
+inline const std::string& FileDescriptorProto::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void FileDescriptorProto::_internal_set_name(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileDescriptorProto::_internal_mutable_name() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileDescriptorProto::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.name)
+  if (!_internal_has_name()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.name_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileDescriptorProto::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileDescriptorProto.name)
+}
+
+// optional string package = 2;
+inline bool FileDescriptorProto::_internal_has_package() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool FileDescriptorProto::has_package() const {
+  return _internal_has_package();
+}
+inline void FileDescriptorProto::clear_package() {
+  _impl_.package_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& FileDescriptorProto::package() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.package)
+  return _internal_package();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileDescriptorProto::set_package(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.package_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.package)
+}
+inline std::string* FileDescriptorProto::mutable_package() {
+  std::string* _s = _internal_mutable_package();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.package)
+  return _s;
+}
+inline const std::string& FileDescriptorProto::_internal_package() const {
+  return _impl_.package_.Get();
+}
+inline void FileDescriptorProto::_internal_set_package(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.package_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileDescriptorProto::_internal_mutable_package() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  return _impl_.package_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileDescriptorProto::release_package() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.package)
+  if (!_internal_has_package()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  auto* p = _impl_.package_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.package_.IsDefault()) {
+    _impl_.package_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileDescriptorProto::set_allocated_package(std::string* package) {
+  if (package != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.package_.SetAllocated(package, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.package_.IsDefault()) {
+    _impl_.package_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileDescriptorProto.package)
+}
+
+// repeated string dependency = 3;
+inline int FileDescriptorProto::_internal_dependency_size() const {
+  return _impl_.dependency_.size();
+}
+inline int FileDescriptorProto::dependency_size() const {
+  return _internal_dependency_size();
+}
+inline void FileDescriptorProto::clear_dependency() {
+  _impl_.dependency_.Clear();
+}
+inline std::string* FileDescriptorProto::add_dependency() {
+  std::string* _s = _internal_add_dependency();
+  // @@protoc_insertion_point(field_add_mutable:google.protobuf.FileDescriptorProto.dependency)
+  return _s;
+}
+inline const std::string& FileDescriptorProto::_internal_dependency(int index) const {
+  return _impl_.dependency_.Get(index);
+}
+inline const std::string& FileDescriptorProto::dependency(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.dependency)
+  return _internal_dependency(index);
+}
+inline std::string* FileDescriptorProto::mutable_dependency(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.dependency)
+  return _impl_.dependency_.Mutable(index);
+}
+inline void FileDescriptorProto::set_dependency(int index, const std::string& value) {
+  _impl_.dependency_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.dependency)
+}
+inline void FileDescriptorProto::set_dependency(int index, std::string&& value) {
+  _impl_.dependency_.Mutable(index)->assign(std::move(value));
+  // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.dependency)
+}
+inline void FileDescriptorProto::set_dependency(int index, const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.dependency_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set_char:google.protobuf.FileDescriptorProto.dependency)
+}
+inline void FileDescriptorProto::set_dependency(int index, const char* value, size_t size) {
+  _impl_.dependency_.Mutable(index)->assign(
+    reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_set_pointer:google.protobuf.FileDescriptorProto.dependency)
+}
+inline std::string* FileDescriptorProto::_internal_add_dependency() {
+  return _impl_.dependency_.Add();
+}
+inline void FileDescriptorProto::add_dependency(const std::string& value) {
+  _impl_.dependency_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.dependency)
+}
+inline void FileDescriptorProto::add_dependency(std::string&& value) {
+  _impl_.dependency_.Add(std::move(value));
+  // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.dependency)
+}
+inline void FileDescriptorProto::add_dependency(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.dependency_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add_char:google.protobuf.FileDescriptorProto.dependency)
+}
+inline void FileDescriptorProto::add_dependency(const char* value, size_t size) {
+  _impl_.dependency_.Add()->assign(reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_add_pointer:google.protobuf.FileDescriptorProto.dependency)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&
+FileDescriptorProto::dependency() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.dependency)
+  return _impl_.dependency_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
+FileDescriptorProto::mutable_dependency() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.dependency)
+  return &_impl_.dependency_;
+}
+
+// repeated int32 public_dependency = 10;
+inline int FileDescriptorProto::_internal_public_dependency_size() const {
+  return _impl_.public_dependency_.size();
+}
+inline int FileDescriptorProto::public_dependency_size() const {
+  return _internal_public_dependency_size();
+}
+inline void FileDescriptorProto::clear_public_dependency() {
+  _impl_.public_dependency_.Clear();
+}
+inline int32_t FileDescriptorProto::_internal_public_dependency(int index) const {
+  return _impl_.public_dependency_.Get(index);
+}
+inline int32_t FileDescriptorProto::public_dependency(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.public_dependency)
+  return _internal_public_dependency(index);
+}
+inline void FileDescriptorProto::set_public_dependency(int index, int32_t value) {
+  _impl_.public_dependency_.Set(index, value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.public_dependency)
+}
+inline void FileDescriptorProto::_internal_add_public_dependency(int32_t value) {
+  _impl_.public_dependency_.Add(value);
+}
+inline void FileDescriptorProto::add_public_dependency(int32_t value) {
+  _internal_add_public_dependency(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.public_dependency)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+FileDescriptorProto::_internal_public_dependency() const {
+  return _impl_.public_dependency_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+FileDescriptorProto::public_dependency() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.public_dependency)
+  return _internal_public_dependency();
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+FileDescriptorProto::_internal_mutable_public_dependency() {
+  return &_impl_.public_dependency_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+FileDescriptorProto::mutable_public_dependency() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.public_dependency)
+  return _internal_mutable_public_dependency();
+}
+
+// repeated int32 weak_dependency = 11;
+inline int FileDescriptorProto::_internal_weak_dependency_size() const {
+  return _impl_.weak_dependency_.size();
+}
+inline int FileDescriptorProto::weak_dependency_size() const {
+  return _internal_weak_dependency_size();
+}
+inline void FileDescriptorProto::clear_weak_dependency() {
+  _impl_.weak_dependency_.Clear();
+}
+inline int32_t FileDescriptorProto::_internal_weak_dependency(int index) const {
+  return _impl_.weak_dependency_.Get(index);
+}
+inline int32_t FileDescriptorProto::weak_dependency(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.weak_dependency)
+  return _internal_weak_dependency(index);
+}
+inline void FileDescriptorProto::set_weak_dependency(int index, int32_t value) {
+  _impl_.weak_dependency_.Set(index, value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.weak_dependency)
+}
+inline void FileDescriptorProto::_internal_add_weak_dependency(int32_t value) {
+  _impl_.weak_dependency_.Add(value);
+}
+inline void FileDescriptorProto::add_weak_dependency(int32_t value) {
+  _internal_add_weak_dependency(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.weak_dependency)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+FileDescriptorProto::_internal_weak_dependency() const {
+  return _impl_.weak_dependency_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+FileDescriptorProto::weak_dependency() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.weak_dependency)
+  return _internal_weak_dependency();
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+FileDescriptorProto::_internal_mutable_weak_dependency() {
+  return &_impl_.weak_dependency_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+FileDescriptorProto::mutable_weak_dependency() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.weak_dependency)
+  return _internal_mutable_weak_dependency();
+}
+
+// repeated .google.protobuf.DescriptorProto message_type = 4;
+inline int FileDescriptorProto::_internal_message_type_size() const {
+  return _impl_.message_type_.size();
+}
+inline int FileDescriptorProto::message_type_size() const {
+  return _internal_message_type_size();
+}
+inline void FileDescriptorProto::clear_message_type() {
+  _impl_.message_type_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto* FileDescriptorProto::mutable_message_type(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.message_type)
+  return _impl_.message_type_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto >*
+FileDescriptorProto::mutable_message_type() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.message_type)
+  return &_impl_.message_type_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::DescriptorProto& FileDescriptorProto::_internal_message_type(int index) const {
+  return _impl_.message_type_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::DescriptorProto& FileDescriptorProto::message_type(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.message_type)
+  return _internal_message_type(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto* FileDescriptorProto::_internal_add_message_type() {
+  return _impl_.message_type_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto* FileDescriptorProto::add_message_type() {
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto* _add = _internal_add_message_type();
+  // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.message_type)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto >&
+FileDescriptorProto::message_type() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.message_type)
+  return _impl_.message_type_;
+}
+
+// repeated .google.protobuf.EnumDescriptorProto enum_type = 5;
+inline int FileDescriptorProto::_internal_enum_type_size() const {
+  return _impl_.enum_type_.size();
+}
+inline int FileDescriptorProto::enum_type_size() const {
+  return _internal_enum_type_size();
+}
+inline void FileDescriptorProto::clear_enum_type() {
+  _impl_.enum_type_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* FileDescriptorProto::mutable_enum_type(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.enum_type)
+  return _impl_.enum_type_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto >*
+FileDescriptorProto::mutable_enum_type() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.enum_type)
+  return &_impl_.enum_type_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto& FileDescriptorProto::_internal_enum_type(int index) const {
+  return _impl_.enum_type_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto& FileDescriptorProto::enum_type(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.enum_type)
+  return _internal_enum_type(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* FileDescriptorProto::_internal_add_enum_type() {
+  return _impl_.enum_type_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* FileDescriptorProto::add_enum_type() {
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* _add = _internal_add_enum_type();
+  // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.enum_type)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto >&
+FileDescriptorProto::enum_type() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.enum_type)
+  return _impl_.enum_type_;
+}
+
+// repeated .google.protobuf.ServiceDescriptorProto service = 6;
+inline int FileDescriptorProto::_internal_service_size() const {
+  return _impl_.service_.size();
+}
+inline int FileDescriptorProto::service_size() const {
+  return _internal_service_size();
+}
+inline void FileDescriptorProto::clear_service() {
+  _impl_.service_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto* FileDescriptorProto::mutable_service(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.service)
+  return _impl_.service_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto >*
+FileDescriptorProto::mutable_service() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.service)
+  return &_impl_.service_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto& FileDescriptorProto::_internal_service(int index) const {
+  return _impl_.service_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto& FileDescriptorProto::service(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.service)
+  return _internal_service(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto* FileDescriptorProto::_internal_add_service() {
+  return _impl_.service_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto* FileDescriptorProto::add_service() {
+  ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto* _add = _internal_add_service();
+  // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.service)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto >&
+FileDescriptorProto::service() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.service)
+  return _impl_.service_;
+}
+
+// repeated .google.protobuf.FieldDescriptorProto extension = 7;
+inline int FileDescriptorProto::_internal_extension_size() const {
+  return _impl_.extension_.size();
+}
+inline int FileDescriptorProto::extension_size() const {
+  return _internal_extension_size();
+}
+inline void FileDescriptorProto::clear_extension() {
+  _impl_.extension_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* FileDescriptorProto::mutable_extension(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.extension)
+  return _impl_.extension_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >*
+FileDescriptorProto::mutable_extension() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.extension)
+  return &_impl_.extension_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& FileDescriptorProto::_internal_extension(int index) const {
+  return _impl_.extension_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& FileDescriptorProto::extension(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.extension)
+  return _internal_extension(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* FileDescriptorProto::_internal_add_extension() {
+  return _impl_.extension_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* FileDescriptorProto::add_extension() {
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* _add = _internal_add_extension();
+  // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.extension)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >&
+FileDescriptorProto::extension() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.extension)
+  return _impl_.extension_;
+}
+
+// optional .google.protobuf.FileOptions options = 8;
+inline bool FileDescriptorProto::_internal_has_options() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.options_ != nullptr);
+  return value;
+}
+inline bool FileDescriptorProto::has_options() const {
+  return _internal_has_options();
+}
+inline void FileDescriptorProto::clear_options() {
+  if (_impl_.options_ != nullptr) _impl_.options_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FileOptions& FileDescriptorProto::_internal_options() const {
+  const ::PROTOBUF_NAMESPACE_ID::FileOptions* p = _impl_.options_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::FileOptions&>(
+      ::PROTOBUF_NAMESPACE_ID::_FileOptions_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FileOptions& FileDescriptorProto::options() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.options)
+  return _internal_options();
+}
+inline void FileDescriptorProto::unsafe_arena_set_allocated_options(
+    ::PROTOBUF_NAMESPACE_ID::FileOptions* options) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.options_);
+  }
+  _impl_.options_ = options;
+  if (options) {
+    _impl_._has_bits_[0] |= 0x00000008u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000008u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.FileDescriptorProto.options)
+}
+inline ::PROTOBUF_NAMESPACE_ID::FileOptions* FileDescriptorProto::release_options() {
+  _impl_._has_bits_[0] &= ~0x00000008u;
+  ::PROTOBUF_NAMESPACE_ID::FileOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FileOptions* FileDescriptorProto::unsafe_arena_release_options() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.options)
+  _impl_._has_bits_[0] &= ~0x00000008u;
+  ::PROTOBUF_NAMESPACE_ID::FileOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FileOptions* FileDescriptorProto::_internal_mutable_options() {
+  _impl_._has_bits_[0] |= 0x00000008u;
+  if (_impl_.options_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::FileOptions>(GetArenaForAllocation());
+    _impl_.options_ = p;
+  }
+  return _impl_.options_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FileOptions* FileDescriptorProto::mutable_options() {
+  ::PROTOBUF_NAMESPACE_ID::FileOptions* _msg = _internal_mutable_options();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.options)
+  return _msg;
+}
+inline void FileDescriptorProto::set_allocated_options(::PROTOBUF_NAMESPACE_ID::FileOptions* options) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete _impl_.options_;
+  }
+  if (options) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(options);
+    if (message_arena != submessage_arena) {
+      options = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, options, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000008u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000008u;
+  }
+  _impl_.options_ = options;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileDescriptorProto.options)
+}
+
+// optional .google.protobuf.SourceCodeInfo source_code_info = 9;
+inline bool FileDescriptorProto::_internal_has_source_code_info() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.source_code_info_ != nullptr);
+  return value;
+}
+inline bool FileDescriptorProto::has_source_code_info() const {
+  return _internal_has_source_code_info();
+}
+inline void FileDescriptorProto::clear_source_code_info() {
+  if (_impl_.source_code_info_ != nullptr) _impl_.source_code_info_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo& FileDescriptorProto::_internal_source_code_info() const {
+  const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* p = _impl_.source_code_info_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo&>(
+      ::PROTOBUF_NAMESPACE_ID::_SourceCodeInfo_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo& FileDescriptorProto::source_code_info() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.source_code_info)
+  return _internal_source_code_info();
+}
+inline void FileDescriptorProto::unsafe_arena_set_allocated_source_code_info(
+    ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* source_code_info) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.source_code_info_);
+  }
+  _impl_.source_code_info_ = source_code_info;
+  if (source_code_info) {
+    _impl_._has_bits_[0] |= 0x00000010u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000010u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.FileDescriptorProto.source_code_info)
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* FileDescriptorProto::release_source_code_info() {
+  _impl_._has_bits_[0] &= ~0x00000010u;
+  ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* temp = _impl_.source_code_info_;
+  _impl_.source_code_info_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* FileDescriptorProto::unsafe_arena_release_source_code_info() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.source_code_info)
+  _impl_._has_bits_[0] &= ~0x00000010u;
+  ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* temp = _impl_.source_code_info_;
+  _impl_.source_code_info_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* FileDescriptorProto::_internal_mutable_source_code_info() {
+  _impl_._has_bits_[0] |= 0x00000010u;
+  if (_impl_.source_code_info_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::SourceCodeInfo>(GetArenaForAllocation());
+    _impl_.source_code_info_ = p;
+  }
+  return _impl_.source_code_info_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* FileDescriptorProto::mutable_source_code_info() {
+  ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* _msg = _internal_mutable_source_code_info();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.source_code_info)
+  return _msg;
+}
+inline void FileDescriptorProto::set_allocated_source_code_info(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo* source_code_info) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete _impl_.source_code_info_;
+  }
+  if (source_code_info) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(source_code_info);
+    if (message_arena != submessage_arena) {
+      source_code_info = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, source_code_info, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000010u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000010u;
+  }
+  _impl_.source_code_info_ = source_code_info;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileDescriptorProto.source_code_info)
+}
+
+// optional string syntax = 12;
+inline bool FileDescriptorProto::_internal_has_syntax() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool FileDescriptorProto::has_syntax() const {
+  return _internal_has_syntax();
+}
+inline void FileDescriptorProto::clear_syntax() {
+  _impl_.syntax_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline const std::string& FileDescriptorProto::syntax() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.syntax)
+  return _internal_syntax();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileDescriptorProto::set_syntax(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.syntax_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.syntax)
+}
+inline std::string* FileDescriptorProto::mutable_syntax() {
+  std::string* _s = _internal_mutable_syntax();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.syntax)
+  return _s;
+}
+inline const std::string& FileDescriptorProto::_internal_syntax() const {
+  return _impl_.syntax_.Get();
+}
+inline void FileDescriptorProto::_internal_set_syntax(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  _impl_.syntax_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileDescriptorProto::_internal_mutable_syntax() {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  return _impl_.syntax_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileDescriptorProto::release_syntax() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.syntax)
+  if (!_internal_has_syntax()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000004u;
+  auto* p = _impl_.syntax_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.syntax_.IsDefault()) {
+    _impl_.syntax_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileDescriptorProto::set_allocated_syntax(std::string* syntax) {
+  if (syntax != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000004u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000004u;
+  }
+  _impl_.syntax_.SetAllocated(syntax, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.syntax_.IsDefault()) {
+    _impl_.syntax_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileDescriptorProto.syntax)
+}
+
+// -------------------------------------------------------------------
+
+// DescriptorProto_ExtensionRange
+
+// optional int32 start = 1;
+inline bool DescriptorProto_ExtensionRange::_internal_has_start() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool DescriptorProto_ExtensionRange::has_start() const {
+  return _internal_has_start();
+}
+inline void DescriptorProto_ExtensionRange::clear_start() {
+  _impl_.start_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline int32_t DescriptorProto_ExtensionRange::_internal_start() const {
+  return _impl_.start_;
+}
+inline int32_t DescriptorProto_ExtensionRange::start() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ExtensionRange.start)
+  return _internal_start();
+}
+inline void DescriptorProto_ExtensionRange::_internal_set_start(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.start_ = value;
+}
+inline void DescriptorProto_ExtensionRange::set_start(int32_t value) {
+  _internal_set_start(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ExtensionRange.start)
+}
+
+// optional int32 end = 2;
+inline bool DescriptorProto_ExtensionRange::_internal_has_end() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool DescriptorProto_ExtensionRange::has_end() const {
+  return _internal_has_end();
+}
+inline void DescriptorProto_ExtensionRange::clear_end() {
+  _impl_.end_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline int32_t DescriptorProto_ExtensionRange::_internal_end() const {
+  return _impl_.end_;
+}
+inline int32_t DescriptorProto_ExtensionRange::end() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ExtensionRange.end)
+  return _internal_end();
+}
+inline void DescriptorProto_ExtensionRange::_internal_set_end(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  _impl_.end_ = value;
+}
+inline void DescriptorProto_ExtensionRange::set_end(int32_t value) {
+  _internal_set_end(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ExtensionRange.end)
+}
+
+// optional .google.protobuf.ExtensionRangeOptions options = 3;
+inline bool DescriptorProto_ExtensionRange::_internal_has_options() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.options_ != nullptr);
+  return value;
+}
+inline bool DescriptorProto_ExtensionRange::has_options() const {
+  return _internal_has_options();
+}
+inline void DescriptorProto_ExtensionRange::clear_options() {
+  if (_impl_.options_ != nullptr) _impl_.options_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions& DescriptorProto_ExtensionRange::_internal_options() const {
+  const ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* p = _impl_.options_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions&>(
+      ::PROTOBUF_NAMESPACE_ID::_ExtensionRangeOptions_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions& DescriptorProto_ExtensionRange::options() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ExtensionRange.options)
+  return _internal_options();
+}
+inline void DescriptorProto_ExtensionRange::unsafe_arena_set_allocated_options(
+    ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* options) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.options_);
+  }
+  _impl_.options_ = options;
+  if (options) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.DescriptorProto.ExtensionRange.options)
+}
+inline ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* DescriptorProto_ExtensionRange::release_options() {
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* DescriptorProto_ExtensionRange::unsafe_arena_release_options() {
+  // @@protoc_insertion_point(field_release:google.protobuf.DescriptorProto.ExtensionRange.options)
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* DescriptorProto_ExtensionRange::_internal_mutable_options() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  if (_impl_.options_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions>(GetArenaForAllocation());
+    _impl_.options_ = p;
+  }
+  return _impl_.options_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* DescriptorProto_ExtensionRange::mutable_options() {
+  ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* _msg = _internal_mutable_options();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.ExtensionRange.options)
+  return _msg;
+}
+inline void DescriptorProto_ExtensionRange::set_allocated_options(::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions* options) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete _impl_.options_;
+  }
+  if (options) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(options);
+    if (message_arena != submessage_arena) {
+      options = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, options, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.options_ = options;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.DescriptorProto.ExtensionRange.options)
+}
+
+// -------------------------------------------------------------------
+
+// DescriptorProto_ReservedRange
+
+// optional int32 start = 1;
+inline bool DescriptorProto_ReservedRange::_internal_has_start() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool DescriptorProto_ReservedRange::has_start() const {
+  return _internal_has_start();
+}
+inline void DescriptorProto_ReservedRange::clear_start() {
+  _impl_.start_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline int32_t DescriptorProto_ReservedRange::_internal_start() const {
+  return _impl_.start_;
+}
+inline int32_t DescriptorProto_ReservedRange::start() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ReservedRange.start)
+  return _internal_start();
+}
+inline void DescriptorProto_ReservedRange::_internal_set_start(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.start_ = value;
+}
+inline void DescriptorProto_ReservedRange::set_start(int32_t value) {
+  _internal_set_start(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ReservedRange.start)
+}
+
+// optional int32 end = 2;
+inline bool DescriptorProto_ReservedRange::_internal_has_end() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool DescriptorProto_ReservedRange::has_end() const {
+  return _internal_has_end();
+}
+inline void DescriptorProto_ReservedRange::clear_end() {
+  _impl_.end_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline int32_t DescriptorProto_ReservedRange::_internal_end() const {
+  return _impl_.end_;
+}
+inline int32_t DescriptorProto_ReservedRange::end() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ReservedRange.end)
+  return _internal_end();
+}
+inline void DescriptorProto_ReservedRange::_internal_set_end(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.end_ = value;
+}
+inline void DescriptorProto_ReservedRange::set_end(int32_t value) {
+  _internal_set_end(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ReservedRange.end)
+}
+
+// -------------------------------------------------------------------
+
+// DescriptorProto
+
+// optional string name = 1;
+inline bool DescriptorProto::_internal_has_name() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool DescriptorProto::has_name() const {
+  return _internal_has_name();
+}
+inline void DescriptorProto::clear_name() {
+  _impl_.name_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& DescriptorProto::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void DescriptorProto::set_name(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.name)
+}
+inline std::string* DescriptorProto::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.name)
+  return _s;
+}
+inline const std::string& DescriptorProto::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void DescriptorProto::_internal_set_name(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* DescriptorProto::_internal_mutable_name() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* DescriptorProto::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.DescriptorProto.name)
+  if (!_internal_has_name()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.name_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void DescriptorProto::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.DescriptorProto.name)
+}
+
+// repeated .google.protobuf.FieldDescriptorProto field = 2;
+inline int DescriptorProto::_internal_field_size() const {
+  return _impl_.field_.size();
+}
+inline int DescriptorProto::field_size() const {
+  return _internal_field_size();
+}
+inline void DescriptorProto::clear_field() {
+  _impl_.field_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* DescriptorProto::mutable_field(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.field)
+  return _impl_.field_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >*
+DescriptorProto::mutable_field() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.field)
+  return &_impl_.field_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& DescriptorProto::_internal_field(int index) const {
+  return _impl_.field_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& DescriptorProto::field(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.field)
+  return _internal_field(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* DescriptorProto::_internal_add_field() {
+  return _impl_.field_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* DescriptorProto::add_field() {
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* _add = _internal_add_field();
+  // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.field)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >&
+DescriptorProto::field() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.field)
+  return _impl_.field_;
+}
+
+// repeated .google.protobuf.FieldDescriptorProto extension = 6;
+inline int DescriptorProto::_internal_extension_size() const {
+  return _impl_.extension_.size();
+}
+inline int DescriptorProto::extension_size() const {
+  return _internal_extension_size();
+}
+inline void DescriptorProto::clear_extension() {
+  _impl_.extension_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* DescriptorProto::mutable_extension(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.extension)
+  return _impl_.extension_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >*
+DescriptorProto::mutable_extension() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.extension)
+  return &_impl_.extension_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& DescriptorProto::_internal_extension(int index) const {
+  return _impl_.extension_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto& DescriptorProto::extension(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.extension)
+  return _internal_extension(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* DescriptorProto::_internal_add_extension() {
+  return _impl_.extension_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* DescriptorProto::add_extension() {
+  ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto* _add = _internal_add_extension();
+  // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.extension)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >&
+DescriptorProto::extension() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.extension)
+  return _impl_.extension_;
+}
+
+// repeated .google.protobuf.DescriptorProto nested_type = 3;
+inline int DescriptorProto::_internal_nested_type_size() const {
+  return _impl_.nested_type_.size();
+}
+inline int DescriptorProto::nested_type_size() const {
+  return _internal_nested_type_size();
+}
+inline void DescriptorProto::clear_nested_type() {
+  _impl_.nested_type_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto* DescriptorProto::mutable_nested_type(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.nested_type)
+  return _impl_.nested_type_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto >*
+DescriptorProto::mutable_nested_type() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.nested_type)
+  return &_impl_.nested_type_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::DescriptorProto& DescriptorProto::_internal_nested_type(int index) const {
+  return _impl_.nested_type_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::DescriptorProto& DescriptorProto::nested_type(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.nested_type)
+  return _internal_nested_type(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto* DescriptorProto::_internal_add_nested_type() {
+  return _impl_.nested_type_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto* DescriptorProto::add_nested_type() {
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto* _add = _internal_add_nested_type();
+  // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.nested_type)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto >&
+DescriptorProto::nested_type() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.nested_type)
+  return _impl_.nested_type_;
+}
+
+// repeated .google.protobuf.EnumDescriptorProto enum_type = 4;
+inline int DescriptorProto::_internal_enum_type_size() const {
+  return _impl_.enum_type_.size();
+}
+inline int DescriptorProto::enum_type_size() const {
+  return _internal_enum_type_size();
+}
+inline void DescriptorProto::clear_enum_type() {
+  _impl_.enum_type_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* DescriptorProto::mutable_enum_type(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.enum_type)
+  return _impl_.enum_type_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto >*
+DescriptorProto::mutable_enum_type() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.enum_type)
+  return &_impl_.enum_type_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto& DescriptorProto::_internal_enum_type(int index) const {
+  return _impl_.enum_type_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto& DescriptorProto::enum_type(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.enum_type)
+  return _internal_enum_type(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* DescriptorProto::_internal_add_enum_type() {
+  return _impl_.enum_type_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* DescriptorProto::add_enum_type() {
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto* _add = _internal_add_enum_type();
+  // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.enum_type)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto >&
+DescriptorProto::enum_type() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.enum_type)
+  return _impl_.enum_type_;
+}
+
+// repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5;
+inline int DescriptorProto::_internal_extension_range_size() const {
+  return _impl_.extension_range_.size();
+}
+inline int DescriptorProto::extension_range_size() const {
+  return _internal_extension_range_size();
+}
+inline void DescriptorProto::clear_extension_range() {
+  _impl_.extension_range_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange* DescriptorProto::mutable_extension_range(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.extension_range)
+  return _impl_.extension_range_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange >*
+DescriptorProto::mutable_extension_range() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.extension_range)
+  return &_impl_.extension_range_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange& DescriptorProto::_internal_extension_range(int index) const {
+  return _impl_.extension_range_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange& DescriptorProto::extension_range(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.extension_range)
+  return _internal_extension_range(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange* DescriptorProto::_internal_add_extension_range() {
+  return _impl_.extension_range_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange* DescriptorProto::add_extension_range() {
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange* _add = _internal_add_extension_range();
+  // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.extension_range)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange >&
+DescriptorProto::extension_range() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.extension_range)
+  return _impl_.extension_range_;
+}
+
+// repeated .google.protobuf.OneofDescriptorProto oneof_decl = 8;
+inline int DescriptorProto::_internal_oneof_decl_size() const {
+  return _impl_.oneof_decl_.size();
+}
+inline int DescriptorProto::oneof_decl_size() const {
+  return _internal_oneof_decl_size();
+}
+inline void DescriptorProto::clear_oneof_decl() {
+  _impl_.oneof_decl_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto* DescriptorProto::mutable_oneof_decl(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.oneof_decl)
+  return _impl_.oneof_decl_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto >*
+DescriptorProto::mutable_oneof_decl() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.oneof_decl)
+  return &_impl_.oneof_decl_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto& DescriptorProto::_internal_oneof_decl(int index) const {
+  return _impl_.oneof_decl_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto& DescriptorProto::oneof_decl(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.oneof_decl)
+  return _internal_oneof_decl(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto* DescriptorProto::_internal_add_oneof_decl() {
+  return _impl_.oneof_decl_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto* DescriptorProto::add_oneof_decl() {
+  ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto* _add = _internal_add_oneof_decl();
+  // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.oneof_decl)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto >&
+DescriptorProto::oneof_decl() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.oneof_decl)
+  return _impl_.oneof_decl_;
+}
+
+// optional .google.protobuf.MessageOptions options = 7;
+inline bool DescriptorProto::_internal_has_options() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.options_ != nullptr);
+  return value;
+}
+inline bool DescriptorProto::has_options() const {
+  return _internal_has_options();
+}
+inline void DescriptorProto::clear_options() {
+  if (_impl_.options_ != nullptr) _impl_.options_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::MessageOptions& DescriptorProto::_internal_options() const {
+  const ::PROTOBUF_NAMESPACE_ID::MessageOptions* p = _impl_.options_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::MessageOptions&>(
+      ::PROTOBUF_NAMESPACE_ID::_MessageOptions_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::MessageOptions& DescriptorProto::options() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.options)
+  return _internal_options();
+}
+inline void DescriptorProto::unsafe_arena_set_allocated_options(
+    ::PROTOBUF_NAMESPACE_ID::MessageOptions* options) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.options_);
+  }
+  _impl_.options_ = options;
+  if (options) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.DescriptorProto.options)
+}
+inline ::PROTOBUF_NAMESPACE_ID::MessageOptions* DescriptorProto::release_options() {
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  ::PROTOBUF_NAMESPACE_ID::MessageOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::MessageOptions* DescriptorProto::unsafe_arena_release_options() {
+  // @@protoc_insertion_point(field_release:google.protobuf.DescriptorProto.options)
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  ::PROTOBUF_NAMESPACE_ID::MessageOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::MessageOptions* DescriptorProto::_internal_mutable_options() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  if (_impl_.options_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::MessageOptions>(GetArenaForAllocation());
+    _impl_.options_ = p;
+  }
+  return _impl_.options_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::MessageOptions* DescriptorProto::mutable_options() {
+  ::PROTOBUF_NAMESPACE_ID::MessageOptions* _msg = _internal_mutable_options();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.options)
+  return _msg;
+}
+inline void DescriptorProto::set_allocated_options(::PROTOBUF_NAMESPACE_ID::MessageOptions* options) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete _impl_.options_;
+  }
+  if (options) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(options);
+    if (message_arena != submessage_arena) {
+      options = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, options, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.options_ = options;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.DescriptorProto.options)
+}
+
+// repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9;
+inline int DescriptorProto::_internal_reserved_range_size() const {
+  return _impl_.reserved_range_.size();
+}
+inline int DescriptorProto::reserved_range_size() const {
+  return _internal_reserved_range_size();
+}
+inline void DescriptorProto::clear_reserved_range() {
+  _impl_.reserved_range_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange* DescriptorProto::mutable_reserved_range(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_range)
+  return _impl_.reserved_range_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange >*
+DescriptorProto::mutable_reserved_range() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_range)
+  return &_impl_.reserved_range_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange& DescriptorProto::_internal_reserved_range(int index) const {
+  return _impl_.reserved_range_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange& DescriptorProto::reserved_range(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_range)
+  return _internal_reserved_range(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange* DescriptorProto::_internal_add_reserved_range() {
+  return _impl_.reserved_range_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange* DescriptorProto::add_reserved_range() {
+  ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange* _add = _internal_add_reserved_range();
+  // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_range)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange >&
+DescriptorProto::reserved_range() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_range)
+  return _impl_.reserved_range_;
+}
+
+// repeated string reserved_name = 10;
+inline int DescriptorProto::_internal_reserved_name_size() const {
+  return _impl_.reserved_name_.size();
+}
+inline int DescriptorProto::reserved_name_size() const {
+  return _internal_reserved_name_size();
+}
+inline void DescriptorProto::clear_reserved_name() {
+  _impl_.reserved_name_.Clear();
+}
+inline std::string* DescriptorProto::add_reserved_name() {
+  std::string* _s = _internal_add_reserved_name();
+  // @@protoc_insertion_point(field_add_mutable:google.protobuf.DescriptorProto.reserved_name)
+  return _s;
+}
+inline const std::string& DescriptorProto::_internal_reserved_name(int index) const {
+  return _impl_.reserved_name_.Get(index);
+}
+inline const std::string& DescriptorProto::reserved_name(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_name)
+  return _internal_reserved_name(index);
+}
+inline std::string* DescriptorProto::mutable_reserved_name(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_name)
+  return _impl_.reserved_name_.Mutable(index);
+}
+inline void DescriptorProto::set_reserved_name(int index, const std::string& value) {
+  _impl_.reserved_name_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.reserved_name)
+}
+inline void DescriptorProto::set_reserved_name(int index, std::string&& value) {
+  _impl_.reserved_name_.Mutable(index)->assign(std::move(value));
+  // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.reserved_name)
+}
+inline void DescriptorProto::set_reserved_name(int index, const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.reserved_name_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set_char:google.protobuf.DescriptorProto.reserved_name)
+}
+inline void DescriptorProto::set_reserved_name(int index, const char* value, size_t size) {
+  _impl_.reserved_name_.Mutable(index)->assign(
+    reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_set_pointer:google.protobuf.DescriptorProto.reserved_name)
+}
+inline std::string* DescriptorProto::_internal_add_reserved_name() {
+  return _impl_.reserved_name_.Add();
+}
+inline void DescriptorProto::add_reserved_name(const std::string& value) {
+  _impl_.reserved_name_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_name)
+}
+inline void DescriptorProto::add_reserved_name(std::string&& value) {
+  _impl_.reserved_name_.Add(std::move(value));
+  // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_name)
+}
+inline void DescriptorProto::add_reserved_name(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.reserved_name_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add_char:google.protobuf.DescriptorProto.reserved_name)
+}
+inline void DescriptorProto::add_reserved_name(const char* value, size_t size) {
+  _impl_.reserved_name_.Add()->assign(reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_add_pointer:google.protobuf.DescriptorProto.reserved_name)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&
+DescriptorProto::reserved_name() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_name)
+  return _impl_.reserved_name_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
+DescriptorProto::mutable_reserved_name() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_name)
+  return &_impl_.reserved_name_;
+}
+
+// -------------------------------------------------------------------
+
+// ExtensionRangeOptions
+
+// repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+inline int ExtensionRangeOptions::_internal_uninterpreted_option_size() const {
+  return _impl_.uninterpreted_option_.size();
+}
+inline int ExtensionRangeOptions::uninterpreted_option_size() const {
+  return _internal_uninterpreted_option_size();
+}
+inline void ExtensionRangeOptions::clear_uninterpreted_option() {
+  _impl_.uninterpreted_option_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* ExtensionRangeOptions::mutable_uninterpreted_option(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.ExtensionRangeOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+ExtensionRangeOptions::mutable_uninterpreted_option() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.ExtensionRangeOptions.uninterpreted_option)
+  return &_impl_.uninterpreted_option_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& ExtensionRangeOptions::_internal_uninterpreted_option(int index) const {
+  return _impl_.uninterpreted_option_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& ExtensionRangeOptions::uninterpreted_option(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.ExtensionRangeOptions.uninterpreted_option)
+  return _internal_uninterpreted_option(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* ExtensionRangeOptions::_internal_add_uninterpreted_option() {
+  return _impl_.uninterpreted_option_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* ExtensionRangeOptions::add_uninterpreted_option() {
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _add = _internal_add_uninterpreted_option();
+  // @@protoc_insertion_point(field_add:google.protobuf.ExtensionRangeOptions.uninterpreted_option)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+ExtensionRangeOptions::uninterpreted_option() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.ExtensionRangeOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_;
+}
+
+// -------------------------------------------------------------------
+
+// FieldDescriptorProto
+
+// optional string name = 1;
+inline bool FieldDescriptorProto::_internal_has_name() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool FieldDescriptorProto::has_name() const {
+  return _internal_has_name();
+}
+inline void FieldDescriptorProto::clear_name() {
+  _impl_.name_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& FieldDescriptorProto::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FieldDescriptorProto::set_name(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.name)
+}
+inline std::string* FieldDescriptorProto::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.name)
+  return _s;
+}
+inline const std::string& FieldDescriptorProto::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void FieldDescriptorProto::_internal_set_name(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FieldDescriptorProto::_internal_mutable_name() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FieldDescriptorProto::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.name)
+  if (!_internal_has_name()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.name_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FieldDescriptorProto::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FieldDescriptorProto.name)
+}
+
+// optional int32 number = 3;
+inline bool FieldDescriptorProto::_internal_has_number() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000040u) != 0;
+  return value;
+}
+inline bool FieldDescriptorProto::has_number() const {
+  return _internal_has_number();
+}
+inline void FieldDescriptorProto::clear_number() {
+  _impl_.number_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000040u;
+}
+inline int32_t FieldDescriptorProto::_internal_number() const {
+  return _impl_.number_;
+}
+inline int32_t FieldDescriptorProto::number() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.number)
+  return _internal_number();
+}
+inline void FieldDescriptorProto::_internal_set_number(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000040u;
+  _impl_.number_ = value;
+}
+inline void FieldDescriptorProto::set_number(int32_t value) {
+  _internal_set_number(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.number)
+}
+
+// optional .google.protobuf.FieldDescriptorProto.Label label = 4;
+inline bool FieldDescriptorProto::_internal_has_label() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000200u) != 0;
+  return value;
+}
+inline bool FieldDescriptorProto::has_label() const {
+  return _internal_has_label();
+}
+inline void FieldDescriptorProto::clear_label() {
+  _impl_.label_ = 1;
+  _impl_._has_bits_[0] &= ~0x00000200u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label FieldDescriptorProto::_internal_label() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label >(_impl_.label_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label FieldDescriptorProto::label() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.label)
+  return _internal_label();
+}
+inline void FieldDescriptorProto::_internal_set_label(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label value) {
+  assert(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label_IsValid(value));
+  _impl_._has_bits_[0] |= 0x00000200u;
+  _impl_.label_ = value;
+}
+inline void FieldDescriptorProto::set_label(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label value) {
+  _internal_set_label(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.label)
+}
+
+// optional .google.protobuf.FieldDescriptorProto.Type type = 5;
+inline bool FieldDescriptorProto::_internal_has_type() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000400u) != 0;
+  return value;
+}
+inline bool FieldDescriptorProto::has_type() const {
+  return _internal_has_type();
+}
+inline void FieldDescriptorProto::clear_type() {
+  _impl_.type_ = 1;
+  _impl_._has_bits_[0] &= ~0x00000400u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type FieldDescriptorProto::_internal_type() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type >(_impl_.type_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type FieldDescriptorProto::type() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.type)
+  return _internal_type();
+}
+inline void FieldDescriptorProto::_internal_set_type(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type value) {
+  assert(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type_IsValid(value));
+  _impl_._has_bits_[0] |= 0x00000400u;
+  _impl_.type_ = value;
+}
+inline void FieldDescriptorProto::set_type(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type value) {
+  _internal_set_type(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.type)
+}
+
+// optional string type_name = 6;
+inline bool FieldDescriptorProto::_internal_has_type_name() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool FieldDescriptorProto::has_type_name() const {
+  return _internal_has_type_name();
+}
+inline void FieldDescriptorProto::clear_type_name() {
+  _impl_.type_name_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline const std::string& FieldDescriptorProto::type_name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.type_name)
+  return _internal_type_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FieldDescriptorProto::set_type_name(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.type_name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.type_name)
+}
+inline std::string* FieldDescriptorProto::mutable_type_name() {
+  std::string* _s = _internal_mutable_type_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.type_name)
+  return _s;
+}
+inline const std::string& FieldDescriptorProto::_internal_type_name() const {
+  return _impl_.type_name_.Get();
+}
+inline void FieldDescriptorProto::_internal_set_type_name(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  _impl_.type_name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FieldDescriptorProto::_internal_mutable_type_name() {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  return _impl_.type_name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FieldDescriptorProto::release_type_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.type_name)
+  if (!_internal_has_type_name()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000004u;
+  auto* p = _impl_.type_name_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.type_name_.IsDefault()) {
+    _impl_.type_name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FieldDescriptorProto::set_allocated_type_name(std::string* type_name) {
+  if (type_name != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000004u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000004u;
+  }
+  _impl_.type_name_.SetAllocated(type_name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.type_name_.IsDefault()) {
+    _impl_.type_name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FieldDescriptorProto.type_name)
+}
+
+// optional string extendee = 2;
+inline bool FieldDescriptorProto::_internal_has_extendee() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool FieldDescriptorProto::has_extendee() const {
+  return _internal_has_extendee();
+}
+inline void FieldDescriptorProto::clear_extendee() {
+  _impl_.extendee_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& FieldDescriptorProto::extendee() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.extendee)
+  return _internal_extendee();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FieldDescriptorProto::set_extendee(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.extendee_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.extendee)
+}
+inline std::string* FieldDescriptorProto::mutable_extendee() {
+  std::string* _s = _internal_mutable_extendee();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.extendee)
+  return _s;
+}
+inline const std::string& FieldDescriptorProto::_internal_extendee() const {
+  return _impl_.extendee_.Get();
+}
+inline void FieldDescriptorProto::_internal_set_extendee(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.extendee_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FieldDescriptorProto::_internal_mutable_extendee() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  return _impl_.extendee_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FieldDescriptorProto::release_extendee() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.extendee)
+  if (!_internal_has_extendee()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  auto* p = _impl_.extendee_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.extendee_.IsDefault()) {
+    _impl_.extendee_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FieldDescriptorProto::set_allocated_extendee(std::string* extendee) {
+  if (extendee != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.extendee_.SetAllocated(extendee, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.extendee_.IsDefault()) {
+    _impl_.extendee_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FieldDescriptorProto.extendee)
+}
+
+// optional string default_value = 7;
+inline bool FieldDescriptorProto::_internal_has_default_value() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+  return value;
+}
+inline bool FieldDescriptorProto::has_default_value() const {
+  return _internal_has_default_value();
+}
+inline void FieldDescriptorProto::clear_default_value() {
+  _impl_.default_value_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline const std::string& FieldDescriptorProto::default_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.default_value)
+  return _internal_default_value();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FieldDescriptorProto::set_default_value(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000008u;
+ _impl_.default_value_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.default_value)
+}
+inline std::string* FieldDescriptorProto::mutable_default_value() {
+  std::string* _s = _internal_mutable_default_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.default_value)
+  return _s;
+}
+inline const std::string& FieldDescriptorProto::_internal_default_value() const {
+  return _impl_.default_value_.Get();
+}
+inline void FieldDescriptorProto::_internal_set_default_value(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000008u;
+  _impl_.default_value_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FieldDescriptorProto::_internal_mutable_default_value() {
+  _impl_._has_bits_[0] |= 0x00000008u;
+  return _impl_.default_value_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FieldDescriptorProto::release_default_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.default_value)
+  if (!_internal_has_default_value()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000008u;
+  auto* p = _impl_.default_value_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.default_value_.IsDefault()) {
+    _impl_.default_value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FieldDescriptorProto::set_allocated_default_value(std::string* default_value) {
+  if (default_value != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000008u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000008u;
+  }
+  _impl_.default_value_.SetAllocated(default_value, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.default_value_.IsDefault()) {
+    _impl_.default_value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FieldDescriptorProto.default_value)
+}
+
+// optional int32 oneof_index = 9;
+inline bool FieldDescriptorProto::_internal_has_oneof_index() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000080u) != 0;
+  return value;
+}
+inline bool FieldDescriptorProto::has_oneof_index() const {
+  return _internal_has_oneof_index();
+}
+inline void FieldDescriptorProto::clear_oneof_index() {
+  _impl_.oneof_index_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000080u;
+}
+inline int32_t FieldDescriptorProto::_internal_oneof_index() const {
+  return _impl_.oneof_index_;
+}
+inline int32_t FieldDescriptorProto::oneof_index() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.oneof_index)
+  return _internal_oneof_index();
+}
+inline void FieldDescriptorProto::_internal_set_oneof_index(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000080u;
+  _impl_.oneof_index_ = value;
+}
+inline void FieldDescriptorProto::set_oneof_index(int32_t value) {
+  _internal_set_oneof_index(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.oneof_index)
+}
+
+// optional string json_name = 10;
+inline bool FieldDescriptorProto::_internal_has_json_name() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+  return value;
+}
+inline bool FieldDescriptorProto::has_json_name() const {
+  return _internal_has_json_name();
+}
+inline void FieldDescriptorProto::clear_json_name() {
+  _impl_.json_name_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline const std::string& FieldDescriptorProto::json_name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.json_name)
+  return _internal_json_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FieldDescriptorProto::set_json_name(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000010u;
+ _impl_.json_name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.json_name)
+}
+inline std::string* FieldDescriptorProto::mutable_json_name() {
+  std::string* _s = _internal_mutable_json_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.json_name)
+  return _s;
+}
+inline const std::string& FieldDescriptorProto::_internal_json_name() const {
+  return _impl_.json_name_.Get();
+}
+inline void FieldDescriptorProto::_internal_set_json_name(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000010u;
+  _impl_.json_name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FieldDescriptorProto::_internal_mutable_json_name() {
+  _impl_._has_bits_[0] |= 0x00000010u;
+  return _impl_.json_name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FieldDescriptorProto::release_json_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.json_name)
+  if (!_internal_has_json_name()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000010u;
+  auto* p = _impl_.json_name_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.json_name_.IsDefault()) {
+    _impl_.json_name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FieldDescriptorProto::set_allocated_json_name(std::string* json_name) {
+  if (json_name != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000010u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000010u;
+  }
+  _impl_.json_name_.SetAllocated(json_name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.json_name_.IsDefault()) {
+    _impl_.json_name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FieldDescriptorProto.json_name)
+}
+
+// optional .google.protobuf.FieldOptions options = 8;
+inline bool FieldDescriptorProto::_internal_has_options() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000020u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.options_ != nullptr);
+  return value;
+}
+inline bool FieldDescriptorProto::has_options() const {
+  return _internal_has_options();
+}
+inline void FieldDescriptorProto::clear_options() {
+  if (_impl_.options_ != nullptr) _impl_.options_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000020u;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FieldOptions& FieldDescriptorProto::_internal_options() const {
+  const ::PROTOBUF_NAMESPACE_ID::FieldOptions* p = _impl_.options_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::FieldOptions&>(
+      ::PROTOBUF_NAMESPACE_ID::_FieldOptions_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::FieldOptions& FieldDescriptorProto::options() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.options)
+  return _internal_options();
+}
+inline void FieldDescriptorProto::unsafe_arena_set_allocated_options(
+    ::PROTOBUF_NAMESPACE_ID::FieldOptions* options) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.options_);
+  }
+  _impl_.options_ = options;
+  if (options) {
+    _impl_._has_bits_[0] |= 0x00000020u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000020u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.FieldDescriptorProto.options)
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldOptions* FieldDescriptorProto::release_options() {
+  _impl_._has_bits_[0] &= ~0x00000020u;
+  ::PROTOBUF_NAMESPACE_ID::FieldOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldOptions* FieldDescriptorProto::unsafe_arena_release_options() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.options)
+  _impl_._has_bits_[0] &= ~0x00000020u;
+  ::PROTOBUF_NAMESPACE_ID::FieldOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldOptions* FieldDescriptorProto::_internal_mutable_options() {
+  _impl_._has_bits_[0] |= 0x00000020u;
+  if (_impl_.options_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::FieldOptions>(GetArenaForAllocation());
+    _impl_.options_ = p;
+  }
+  return _impl_.options_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldOptions* FieldDescriptorProto::mutable_options() {
+  ::PROTOBUF_NAMESPACE_ID::FieldOptions* _msg = _internal_mutable_options();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.options)
+  return _msg;
+}
+inline void FieldDescriptorProto::set_allocated_options(::PROTOBUF_NAMESPACE_ID::FieldOptions* options) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete _impl_.options_;
+  }
+  if (options) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(options);
+    if (message_arena != submessage_arena) {
+      options = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, options, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000020u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000020u;
+  }
+  _impl_.options_ = options;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FieldDescriptorProto.options)
+}
+
+// optional bool proto3_optional = 17;
+inline bool FieldDescriptorProto::_internal_has_proto3_optional() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000100u) != 0;
+  return value;
+}
+inline bool FieldDescriptorProto::has_proto3_optional() const {
+  return _internal_has_proto3_optional();
+}
+inline void FieldDescriptorProto::clear_proto3_optional() {
+  _impl_.proto3_optional_ = false;
+  _impl_._has_bits_[0] &= ~0x00000100u;
+}
+inline bool FieldDescriptorProto::_internal_proto3_optional() const {
+  return _impl_.proto3_optional_;
+}
+inline bool FieldDescriptorProto::proto3_optional() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.proto3_optional)
+  return _internal_proto3_optional();
+}
+inline void FieldDescriptorProto::_internal_set_proto3_optional(bool value) {
+  _impl_._has_bits_[0] |= 0x00000100u;
+  _impl_.proto3_optional_ = value;
+}
+inline void FieldDescriptorProto::set_proto3_optional(bool value) {
+  _internal_set_proto3_optional(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.proto3_optional)
+}
+
+// -------------------------------------------------------------------
+
+// OneofDescriptorProto
+
+// optional string name = 1;
+inline bool OneofDescriptorProto::_internal_has_name() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool OneofDescriptorProto::has_name() const {
+  return _internal_has_name();
+}
+inline void OneofDescriptorProto::clear_name() {
+  _impl_.name_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& OneofDescriptorProto::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.OneofDescriptorProto.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void OneofDescriptorProto::set_name(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.OneofDescriptorProto.name)
+}
+inline std::string* OneofDescriptorProto::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.OneofDescriptorProto.name)
+  return _s;
+}
+inline const std::string& OneofDescriptorProto::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void OneofDescriptorProto::_internal_set_name(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* OneofDescriptorProto::_internal_mutable_name() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* OneofDescriptorProto::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.OneofDescriptorProto.name)
+  if (!_internal_has_name()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.name_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void OneofDescriptorProto::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.OneofDescriptorProto.name)
+}
+
+// optional .google.protobuf.OneofOptions options = 2;
+inline bool OneofDescriptorProto::_internal_has_options() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.options_ != nullptr);
+  return value;
+}
+inline bool OneofDescriptorProto::has_options() const {
+  return _internal_has_options();
+}
+inline void OneofDescriptorProto::clear_options() {
+  if (_impl_.options_ != nullptr) _impl_.options_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::OneofOptions& OneofDescriptorProto::_internal_options() const {
+  const ::PROTOBUF_NAMESPACE_ID::OneofOptions* p = _impl_.options_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::OneofOptions&>(
+      ::PROTOBUF_NAMESPACE_ID::_OneofOptions_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::OneofOptions& OneofDescriptorProto::options() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.OneofDescriptorProto.options)
+  return _internal_options();
+}
+inline void OneofDescriptorProto::unsafe_arena_set_allocated_options(
+    ::PROTOBUF_NAMESPACE_ID::OneofOptions* options) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.options_);
+  }
+  _impl_.options_ = options;
+  if (options) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.OneofDescriptorProto.options)
+}
+inline ::PROTOBUF_NAMESPACE_ID::OneofOptions* OneofDescriptorProto::release_options() {
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  ::PROTOBUF_NAMESPACE_ID::OneofOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::OneofOptions* OneofDescriptorProto::unsafe_arena_release_options() {
+  // @@protoc_insertion_point(field_release:google.protobuf.OneofDescriptorProto.options)
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  ::PROTOBUF_NAMESPACE_ID::OneofOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::OneofOptions* OneofDescriptorProto::_internal_mutable_options() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  if (_impl_.options_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::OneofOptions>(GetArenaForAllocation());
+    _impl_.options_ = p;
+  }
+  return _impl_.options_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::OneofOptions* OneofDescriptorProto::mutable_options() {
+  ::PROTOBUF_NAMESPACE_ID::OneofOptions* _msg = _internal_mutable_options();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.OneofDescriptorProto.options)
+  return _msg;
+}
+inline void OneofDescriptorProto::set_allocated_options(::PROTOBUF_NAMESPACE_ID::OneofOptions* options) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete _impl_.options_;
+  }
+  if (options) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(options);
+    if (message_arena != submessage_arena) {
+      options = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, options, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.options_ = options;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.OneofDescriptorProto.options)
+}
+
+// -------------------------------------------------------------------
+
+// EnumDescriptorProto_EnumReservedRange
+
+// optional int32 start = 1;
+inline bool EnumDescriptorProto_EnumReservedRange::_internal_has_start() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool EnumDescriptorProto_EnumReservedRange::has_start() const {
+  return _internal_has_start();
+}
+inline void EnumDescriptorProto_EnumReservedRange::clear_start() {
+  _impl_.start_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline int32_t EnumDescriptorProto_EnumReservedRange::_internal_start() const {
+  return _impl_.start_;
+}
+inline int32_t EnumDescriptorProto_EnumReservedRange::start() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumDescriptorProto.EnumReservedRange.start)
+  return _internal_start();
+}
+inline void EnumDescriptorProto_EnumReservedRange::_internal_set_start(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.start_ = value;
+}
+inline void EnumDescriptorProto_EnumReservedRange::set_start(int32_t value) {
+  _internal_set_start(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumDescriptorProto.EnumReservedRange.start)
+}
+
+// optional int32 end = 2;
+inline bool EnumDescriptorProto_EnumReservedRange::_internal_has_end() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool EnumDescriptorProto_EnumReservedRange::has_end() const {
+  return _internal_has_end();
+}
+inline void EnumDescriptorProto_EnumReservedRange::clear_end() {
+  _impl_.end_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline int32_t EnumDescriptorProto_EnumReservedRange::_internal_end() const {
+  return _impl_.end_;
+}
+inline int32_t EnumDescriptorProto_EnumReservedRange::end() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumDescriptorProto.EnumReservedRange.end)
+  return _internal_end();
+}
+inline void EnumDescriptorProto_EnumReservedRange::_internal_set_end(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.end_ = value;
+}
+inline void EnumDescriptorProto_EnumReservedRange::set_end(int32_t value) {
+  _internal_set_end(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumDescriptorProto.EnumReservedRange.end)
+}
+
+// -------------------------------------------------------------------
+
+// EnumDescriptorProto
+
+// optional string name = 1;
+inline bool EnumDescriptorProto::_internal_has_name() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool EnumDescriptorProto::has_name() const {
+  return _internal_has_name();
+}
+inline void EnumDescriptorProto::clear_name() {
+  _impl_.name_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& EnumDescriptorProto::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumDescriptorProto.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void EnumDescriptorProto::set_name(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumDescriptorProto.name)
+}
+inline std::string* EnumDescriptorProto::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumDescriptorProto.name)
+  return _s;
+}
+inline const std::string& EnumDescriptorProto::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void EnumDescriptorProto::_internal_set_name(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* EnumDescriptorProto::_internal_mutable_name() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* EnumDescriptorProto::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.EnumDescriptorProto.name)
+  if (!_internal_has_name()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.name_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void EnumDescriptorProto::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.EnumDescriptorProto.name)
+}
+
+// repeated .google.protobuf.EnumValueDescriptorProto value = 2;
+inline int EnumDescriptorProto::_internal_value_size() const {
+  return _impl_.value_.size();
+}
+inline int EnumDescriptorProto::value_size() const {
+  return _internal_value_size();
+}
+inline void EnumDescriptorProto::clear_value() {
+  _impl_.value_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto* EnumDescriptorProto::mutable_value(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumDescriptorProto.value)
+  return _impl_.value_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto >*
+EnumDescriptorProto::mutable_value() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumDescriptorProto.value)
+  return &_impl_.value_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto& EnumDescriptorProto::_internal_value(int index) const {
+  return _impl_.value_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto& EnumDescriptorProto::value(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumDescriptorProto.value)
+  return _internal_value(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto* EnumDescriptorProto::_internal_add_value() {
+  return _impl_.value_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto* EnumDescriptorProto::add_value() {
+  ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto* _add = _internal_add_value();
+  // @@protoc_insertion_point(field_add:google.protobuf.EnumDescriptorProto.value)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto >&
+EnumDescriptorProto::value() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.EnumDescriptorProto.value)
+  return _impl_.value_;
+}
+
+// optional .google.protobuf.EnumOptions options = 3;
+inline bool EnumDescriptorProto::_internal_has_options() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.options_ != nullptr);
+  return value;
+}
+inline bool EnumDescriptorProto::has_options() const {
+  return _internal_has_options();
+}
+inline void EnumDescriptorProto::clear_options() {
+  if (_impl_.options_ != nullptr) _impl_.options_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumOptions& EnumDescriptorProto::_internal_options() const {
+  const ::PROTOBUF_NAMESPACE_ID::EnumOptions* p = _impl_.options_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::EnumOptions&>(
+      ::PROTOBUF_NAMESPACE_ID::_EnumOptions_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumOptions& EnumDescriptorProto::options() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumDescriptorProto.options)
+  return _internal_options();
+}
+inline void EnumDescriptorProto::unsafe_arena_set_allocated_options(
+    ::PROTOBUF_NAMESPACE_ID::EnumOptions* options) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.options_);
+  }
+  _impl_.options_ = options;
+  if (options) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.EnumDescriptorProto.options)
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumOptions* EnumDescriptorProto::release_options() {
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  ::PROTOBUF_NAMESPACE_ID::EnumOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumOptions* EnumDescriptorProto::unsafe_arena_release_options() {
+  // @@protoc_insertion_point(field_release:google.protobuf.EnumDescriptorProto.options)
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  ::PROTOBUF_NAMESPACE_ID::EnumOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumOptions* EnumDescriptorProto::_internal_mutable_options() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  if (_impl_.options_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::EnumOptions>(GetArenaForAllocation());
+    _impl_.options_ = p;
+  }
+  return _impl_.options_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumOptions* EnumDescriptorProto::mutable_options() {
+  ::PROTOBUF_NAMESPACE_ID::EnumOptions* _msg = _internal_mutable_options();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumDescriptorProto.options)
+  return _msg;
+}
+inline void EnumDescriptorProto::set_allocated_options(::PROTOBUF_NAMESPACE_ID::EnumOptions* options) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete _impl_.options_;
+  }
+  if (options) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(options);
+    if (message_arena != submessage_arena) {
+      options = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, options, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.options_ = options;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.EnumDescriptorProto.options)
+}
+
+// repeated .google.protobuf.EnumDescriptorProto.EnumReservedRange reserved_range = 4;
+inline int EnumDescriptorProto::_internal_reserved_range_size() const {
+  return _impl_.reserved_range_.size();
+}
+inline int EnumDescriptorProto::reserved_range_size() const {
+  return _internal_reserved_range_size();
+}
+inline void EnumDescriptorProto::clear_reserved_range() {
+  _impl_.reserved_range_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange* EnumDescriptorProto::mutable_reserved_range(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumDescriptorProto.reserved_range)
+  return _impl_.reserved_range_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange >*
+EnumDescriptorProto::mutable_reserved_range() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumDescriptorProto.reserved_range)
+  return &_impl_.reserved_range_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange& EnumDescriptorProto::_internal_reserved_range(int index) const {
+  return _impl_.reserved_range_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange& EnumDescriptorProto::reserved_range(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumDescriptorProto.reserved_range)
+  return _internal_reserved_range(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange* EnumDescriptorProto::_internal_add_reserved_range() {
+  return _impl_.reserved_range_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange* EnumDescriptorProto::add_reserved_range() {
+  ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange* _add = _internal_add_reserved_range();
+  // @@protoc_insertion_point(field_add:google.protobuf.EnumDescriptorProto.reserved_range)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange >&
+EnumDescriptorProto::reserved_range() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.EnumDescriptorProto.reserved_range)
+  return _impl_.reserved_range_;
+}
+
+// repeated string reserved_name = 5;
+inline int EnumDescriptorProto::_internal_reserved_name_size() const {
+  return _impl_.reserved_name_.size();
+}
+inline int EnumDescriptorProto::reserved_name_size() const {
+  return _internal_reserved_name_size();
+}
+inline void EnumDescriptorProto::clear_reserved_name() {
+  _impl_.reserved_name_.Clear();
+}
+inline std::string* EnumDescriptorProto::add_reserved_name() {
+  std::string* _s = _internal_add_reserved_name();
+  // @@protoc_insertion_point(field_add_mutable:google.protobuf.EnumDescriptorProto.reserved_name)
+  return _s;
+}
+inline const std::string& EnumDescriptorProto::_internal_reserved_name(int index) const {
+  return _impl_.reserved_name_.Get(index);
+}
+inline const std::string& EnumDescriptorProto::reserved_name(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumDescriptorProto.reserved_name)
+  return _internal_reserved_name(index);
+}
+inline std::string* EnumDescriptorProto::mutable_reserved_name(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumDescriptorProto.reserved_name)
+  return _impl_.reserved_name_.Mutable(index);
+}
+inline void EnumDescriptorProto::set_reserved_name(int index, const std::string& value) {
+  _impl_.reserved_name_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumDescriptorProto.reserved_name)
+}
+inline void EnumDescriptorProto::set_reserved_name(int index, std::string&& value) {
+  _impl_.reserved_name_.Mutable(index)->assign(std::move(value));
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumDescriptorProto.reserved_name)
+}
+inline void EnumDescriptorProto::set_reserved_name(int index, const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.reserved_name_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set_char:google.protobuf.EnumDescriptorProto.reserved_name)
+}
+inline void EnumDescriptorProto::set_reserved_name(int index, const char* value, size_t size) {
+  _impl_.reserved_name_.Mutable(index)->assign(
+    reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_set_pointer:google.protobuf.EnumDescriptorProto.reserved_name)
+}
+inline std::string* EnumDescriptorProto::_internal_add_reserved_name() {
+  return _impl_.reserved_name_.Add();
+}
+inline void EnumDescriptorProto::add_reserved_name(const std::string& value) {
+  _impl_.reserved_name_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.EnumDescriptorProto.reserved_name)
+}
+inline void EnumDescriptorProto::add_reserved_name(std::string&& value) {
+  _impl_.reserved_name_.Add(std::move(value));
+  // @@protoc_insertion_point(field_add:google.protobuf.EnumDescriptorProto.reserved_name)
+}
+inline void EnumDescriptorProto::add_reserved_name(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.reserved_name_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add_char:google.protobuf.EnumDescriptorProto.reserved_name)
+}
+inline void EnumDescriptorProto::add_reserved_name(const char* value, size_t size) {
+  _impl_.reserved_name_.Add()->assign(reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_add_pointer:google.protobuf.EnumDescriptorProto.reserved_name)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&
+EnumDescriptorProto::reserved_name() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.EnumDescriptorProto.reserved_name)
+  return _impl_.reserved_name_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
+EnumDescriptorProto::mutable_reserved_name() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumDescriptorProto.reserved_name)
+  return &_impl_.reserved_name_;
+}
+
+// -------------------------------------------------------------------
+
+// EnumValueDescriptorProto
+
+// optional string name = 1;
+inline bool EnumValueDescriptorProto::_internal_has_name() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool EnumValueDescriptorProto::has_name() const {
+  return _internal_has_name();
+}
+inline void EnumValueDescriptorProto::clear_name() {
+  _impl_.name_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& EnumValueDescriptorProto::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumValueDescriptorProto.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void EnumValueDescriptorProto::set_name(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumValueDescriptorProto.name)
+}
+inline std::string* EnumValueDescriptorProto::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumValueDescriptorProto.name)
+  return _s;
+}
+inline const std::string& EnumValueDescriptorProto::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void EnumValueDescriptorProto::_internal_set_name(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* EnumValueDescriptorProto::_internal_mutable_name() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* EnumValueDescriptorProto::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.EnumValueDescriptorProto.name)
+  if (!_internal_has_name()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.name_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void EnumValueDescriptorProto::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.EnumValueDescriptorProto.name)
+}
+
+// optional int32 number = 2;
+inline bool EnumValueDescriptorProto::_internal_has_number() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool EnumValueDescriptorProto::has_number() const {
+  return _internal_has_number();
+}
+inline void EnumValueDescriptorProto::clear_number() {
+  _impl_.number_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline int32_t EnumValueDescriptorProto::_internal_number() const {
+  return _impl_.number_;
+}
+inline int32_t EnumValueDescriptorProto::number() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumValueDescriptorProto.number)
+  return _internal_number();
+}
+inline void EnumValueDescriptorProto::_internal_set_number(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  _impl_.number_ = value;
+}
+inline void EnumValueDescriptorProto::set_number(int32_t value) {
+  _internal_set_number(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumValueDescriptorProto.number)
+}
+
+// optional .google.protobuf.EnumValueOptions options = 3;
+inline bool EnumValueDescriptorProto::_internal_has_options() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.options_ != nullptr);
+  return value;
+}
+inline bool EnumValueDescriptorProto::has_options() const {
+  return _internal_has_options();
+}
+inline void EnumValueDescriptorProto::clear_options() {
+  if (_impl_.options_ != nullptr) _impl_.options_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumValueOptions& EnumValueDescriptorProto::_internal_options() const {
+  const ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* p = _impl_.options_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::EnumValueOptions&>(
+      ::PROTOBUF_NAMESPACE_ID::_EnumValueOptions_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumValueOptions& EnumValueDescriptorProto::options() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumValueDescriptorProto.options)
+  return _internal_options();
+}
+inline void EnumValueDescriptorProto::unsafe_arena_set_allocated_options(
+    ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* options) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.options_);
+  }
+  _impl_.options_ = options;
+  if (options) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.EnumValueDescriptorProto.options)
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* EnumValueDescriptorProto::release_options() {
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* EnumValueDescriptorProto::unsafe_arena_release_options() {
+  // @@protoc_insertion_point(field_release:google.protobuf.EnumValueDescriptorProto.options)
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* EnumValueDescriptorProto::_internal_mutable_options() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  if (_impl_.options_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::EnumValueOptions>(GetArenaForAllocation());
+    _impl_.options_ = p;
+  }
+  return _impl_.options_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* EnumValueDescriptorProto::mutable_options() {
+  ::PROTOBUF_NAMESPACE_ID::EnumValueOptions* _msg = _internal_mutable_options();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumValueDescriptorProto.options)
+  return _msg;
+}
+inline void EnumValueDescriptorProto::set_allocated_options(::PROTOBUF_NAMESPACE_ID::EnumValueOptions* options) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete _impl_.options_;
+  }
+  if (options) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(options);
+    if (message_arena != submessage_arena) {
+      options = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, options, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.options_ = options;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.EnumValueDescriptorProto.options)
+}
+
+// -------------------------------------------------------------------
+
+// ServiceDescriptorProto
+
+// optional string name = 1;
+inline bool ServiceDescriptorProto::_internal_has_name() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool ServiceDescriptorProto::has_name() const {
+  return _internal_has_name();
+}
+inline void ServiceDescriptorProto::clear_name() {
+  _impl_.name_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& ServiceDescriptorProto::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.ServiceDescriptorProto.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ServiceDescriptorProto::set_name(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.ServiceDescriptorProto.name)
+}
+inline std::string* ServiceDescriptorProto::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.ServiceDescriptorProto.name)
+  return _s;
+}
+inline const std::string& ServiceDescriptorProto::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void ServiceDescriptorProto::_internal_set_name(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ServiceDescriptorProto::_internal_mutable_name() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ServiceDescriptorProto::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.ServiceDescriptorProto.name)
+  if (!_internal_has_name()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.name_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void ServiceDescriptorProto::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.ServiceDescriptorProto.name)
+}
+
+// repeated .google.protobuf.MethodDescriptorProto method = 2;
+inline int ServiceDescriptorProto::_internal_method_size() const {
+  return _impl_.method_.size();
+}
+inline int ServiceDescriptorProto::method_size() const {
+  return _internal_method_size();
+}
+inline void ServiceDescriptorProto::clear_method() {
+  _impl_.method_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto* ServiceDescriptorProto::mutable_method(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.ServiceDescriptorProto.method)
+  return _impl_.method_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto >*
+ServiceDescriptorProto::mutable_method() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.ServiceDescriptorProto.method)
+  return &_impl_.method_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto& ServiceDescriptorProto::_internal_method(int index) const {
+  return _impl_.method_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto& ServiceDescriptorProto::method(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.ServiceDescriptorProto.method)
+  return _internal_method(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto* ServiceDescriptorProto::_internal_add_method() {
+  return _impl_.method_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto* ServiceDescriptorProto::add_method() {
+  ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto* _add = _internal_add_method();
+  // @@protoc_insertion_point(field_add:google.protobuf.ServiceDescriptorProto.method)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto >&
+ServiceDescriptorProto::method() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.ServiceDescriptorProto.method)
+  return _impl_.method_;
+}
+
+// optional .google.protobuf.ServiceOptions options = 3;
+inline bool ServiceDescriptorProto::_internal_has_options() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.options_ != nullptr);
+  return value;
+}
+inline bool ServiceDescriptorProto::has_options() const {
+  return _internal_has_options();
+}
+inline void ServiceDescriptorProto::clear_options() {
+  if (_impl_.options_ != nullptr) _impl_.options_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::ServiceOptions& ServiceDescriptorProto::_internal_options() const {
+  const ::PROTOBUF_NAMESPACE_ID::ServiceOptions* p = _impl_.options_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::ServiceOptions&>(
+      ::PROTOBUF_NAMESPACE_ID::_ServiceOptions_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::ServiceOptions& ServiceDescriptorProto::options() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.ServiceDescriptorProto.options)
+  return _internal_options();
+}
+inline void ServiceDescriptorProto::unsafe_arena_set_allocated_options(
+    ::PROTOBUF_NAMESPACE_ID::ServiceOptions* options) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.options_);
+  }
+  _impl_.options_ = options;
+  if (options) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.ServiceDescriptorProto.options)
+}
+inline ::PROTOBUF_NAMESPACE_ID::ServiceOptions* ServiceDescriptorProto::release_options() {
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  ::PROTOBUF_NAMESPACE_ID::ServiceOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::ServiceOptions* ServiceDescriptorProto::unsafe_arena_release_options() {
+  // @@protoc_insertion_point(field_release:google.protobuf.ServiceDescriptorProto.options)
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  ::PROTOBUF_NAMESPACE_ID::ServiceOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::ServiceOptions* ServiceDescriptorProto::_internal_mutable_options() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  if (_impl_.options_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::ServiceOptions>(GetArenaForAllocation());
+    _impl_.options_ = p;
+  }
+  return _impl_.options_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::ServiceOptions* ServiceDescriptorProto::mutable_options() {
+  ::PROTOBUF_NAMESPACE_ID::ServiceOptions* _msg = _internal_mutable_options();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.ServiceDescriptorProto.options)
+  return _msg;
+}
+inline void ServiceDescriptorProto::set_allocated_options(::PROTOBUF_NAMESPACE_ID::ServiceOptions* options) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete _impl_.options_;
+  }
+  if (options) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(options);
+    if (message_arena != submessage_arena) {
+      options = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, options, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.options_ = options;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.ServiceDescriptorProto.options)
+}
+
+// -------------------------------------------------------------------
+
+// MethodDescriptorProto
+
+// optional string name = 1;
+inline bool MethodDescriptorProto::_internal_has_name() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool MethodDescriptorProto::has_name() const {
+  return _internal_has_name();
+}
+inline void MethodDescriptorProto::clear_name() {
+  _impl_.name_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& MethodDescriptorProto::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MethodDescriptorProto.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void MethodDescriptorProto::set_name(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.MethodDescriptorProto.name)
+}
+inline std::string* MethodDescriptorProto::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.MethodDescriptorProto.name)
+  return _s;
+}
+inline const std::string& MethodDescriptorProto::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void MethodDescriptorProto::_internal_set_name(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* MethodDescriptorProto::_internal_mutable_name() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* MethodDescriptorProto::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.name)
+  if (!_internal_has_name()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.name_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void MethodDescriptorProto::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.MethodDescriptorProto.name)
+}
+
+// optional string input_type = 2;
+inline bool MethodDescriptorProto::_internal_has_input_type() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool MethodDescriptorProto::has_input_type() const {
+  return _internal_has_input_type();
+}
+inline void MethodDescriptorProto::clear_input_type() {
+  _impl_.input_type_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& MethodDescriptorProto::input_type() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MethodDescriptorProto.input_type)
+  return _internal_input_type();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void MethodDescriptorProto::set_input_type(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.input_type_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.MethodDescriptorProto.input_type)
+}
+inline std::string* MethodDescriptorProto::mutable_input_type() {
+  std::string* _s = _internal_mutable_input_type();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.MethodDescriptorProto.input_type)
+  return _s;
+}
+inline const std::string& MethodDescriptorProto::_internal_input_type() const {
+  return _impl_.input_type_.Get();
+}
+inline void MethodDescriptorProto::_internal_set_input_type(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.input_type_.Set(value, GetArenaForAllocation());
+}
+inline std::string* MethodDescriptorProto::_internal_mutable_input_type() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  return _impl_.input_type_.Mutable(GetArenaForAllocation());
+}
+inline std::string* MethodDescriptorProto::release_input_type() {
+  // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.input_type)
+  if (!_internal_has_input_type()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  auto* p = _impl_.input_type_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.input_type_.IsDefault()) {
+    _impl_.input_type_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void MethodDescriptorProto::set_allocated_input_type(std::string* input_type) {
+  if (input_type != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.input_type_.SetAllocated(input_type, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.input_type_.IsDefault()) {
+    _impl_.input_type_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.MethodDescriptorProto.input_type)
+}
+
+// optional string output_type = 3;
+inline bool MethodDescriptorProto::_internal_has_output_type() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool MethodDescriptorProto::has_output_type() const {
+  return _internal_has_output_type();
+}
+inline void MethodDescriptorProto::clear_output_type() {
+  _impl_.output_type_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline const std::string& MethodDescriptorProto::output_type() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MethodDescriptorProto.output_type)
+  return _internal_output_type();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void MethodDescriptorProto::set_output_type(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.output_type_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.MethodDescriptorProto.output_type)
+}
+inline std::string* MethodDescriptorProto::mutable_output_type() {
+  std::string* _s = _internal_mutable_output_type();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.MethodDescriptorProto.output_type)
+  return _s;
+}
+inline const std::string& MethodDescriptorProto::_internal_output_type() const {
+  return _impl_.output_type_.Get();
+}
+inline void MethodDescriptorProto::_internal_set_output_type(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  _impl_.output_type_.Set(value, GetArenaForAllocation());
+}
+inline std::string* MethodDescriptorProto::_internal_mutable_output_type() {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  return _impl_.output_type_.Mutable(GetArenaForAllocation());
+}
+inline std::string* MethodDescriptorProto::release_output_type() {
+  // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.output_type)
+  if (!_internal_has_output_type()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000004u;
+  auto* p = _impl_.output_type_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.output_type_.IsDefault()) {
+    _impl_.output_type_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void MethodDescriptorProto::set_allocated_output_type(std::string* output_type) {
+  if (output_type != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000004u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000004u;
+  }
+  _impl_.output_type_.SetAllocated(output_type, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.output_type_.IsDefault()) {
+    _impl_.output_type_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.MethodDescriptorProto.output_type)
+}
+
+// optional .google.protobuf.MethodOptions options = 4;
+inline bool MethodDescriptorProto::_internal_has_options() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.options_ != nullptr);
+  return value;
+}
+inline bool MethodDescriptorProto::has_options() const {
+  return _internal_has_options();
+}
+inline void MethodDescriptorProto::clear_options() {
+  if (_impl_.options_ != nullptr) _impl_.options_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::MethodOptions& MethodDescriptorProto::_internal_options() const {
+  const ::PROTOBUF_NAMESPACE_ID::MethodOptions* p = _impl_.options_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::MethodOptions&>(
+      ::PROTOBUF_NAMESPACE_ID::_MethodOptions_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::MethodOptions& MethodDescriptorProto::options() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MethodDescriptorProto.options)
+  return _internal_options();
+}
+inline void MethodDescriptorProto::unsafe_arena_set_allocated_options(
+    ::PROTOBUF_NAMESPACE_ID::MethodOptions* options) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.options_);
+  }
+  _impl_.options_ = options;
+  if (options) {
+    _impl_._has_bits_[0] |= 0x00000008u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000008u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.MethodDescriptorProto.options)
+}
+inline ::PROTOBUF_NAMESPACE_ID::MethodOptions* MethodDescriptorProto::release_options() {
+  _impl_._has_bits_[0] &= ~0x00000008u;
+  ::PROTOBUF_NAMESPACE_ID::MethodOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::MethodOptions* MethodDescriptorProto::unsafe_arena_release_options() {
+  // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.options)
+  _impl_._has_bits_[0] &= ~0x00000008u;
+  ::PROTOBUF_NAMESPACE_ID::MethodOptions* temp = _impl_.options_;
+  _impl_.options_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::MethodOptions* MethodDescriptorProto::_internal_mutable_options() {
+  _impl_._has_bits_[0] |= 0x00000008u;
+  if (_impl_.options_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::MethodOptions>(GetArenaForAllocation());
+    _impl_.options_ = p;
+  }
+  return _impl_.options_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::MethodOptions* MethodDescriptorProto::mutable_options() {
+  ::PROTOBUF_NAMESPACE_ID::MethodOptions* _msg = _internal_mutable_options();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.MethodDescriptorProto.options)
+  return _msg;
+}
+inline void MethodDescriptorProto::set_allocated_options(::PROTOBUF_NAMESPACE_ID::MethodOptions* options) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete _impl_.options_;
+  }
+  if (options) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(options);
+    if (message_arena != submessage_arena) {
+      options = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, options, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000008u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000008u;
+  }
+  _impl_.options_ = options;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.MethodDescriptorProto.options)
+}
+
+// optional bool client_streaming = 5 [default = false];
+inline bool MethodDescriptorProto::_internal_has_client_streaming() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+  return value;
+}
+inline bool MethodDescriptorProto::has_client_streaming() const {
+  return _internal_has_client_streaming();
+}
+inline void MethodDescriptorProto::clear_client_streaming() {
+  _impl_.client_streaming_ = false;
+  _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline bool MethodDescriptorProto::_internal_client_streaming() const {
+  return _impl_.client_streaming_;
+}
+inline bool MethodDescriptorProto::client_streaming() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MethodDescriptorProto.client_streaming)
+  return _internal_client_streaming();
+}
+inline void MethodDescriptorProto::_internal_set_client_streaming(bool value) {
+  _impl_._has_bits_[0] |= 0x00000010u;
+  _impl_.client_streaming_ = value;
+}
+inline void MethodDescriptorProto::set_client_streaming(bool value) {
+  _internal_set_client_streaming(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.MethodDescriptorProto.client_streaming)
+}
+
+// optional bool server_streaming = 6 [default = false];
+inline bool MethodDescriptorProto::_internal_has_server_streaming() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000020u) != 0;
+  return value;
+}
+inline bool MethodDescriptorProto::has_server_streaming() const {
+  return _internal_has_server_streaming();
+}
+inline void MethodDescriptorProto::clear_server_streaming() {
+  _impl_.server_streaming_ = false;
+  _impl_._has_bits_[0] &= ~0x00000020u;
+}
+inline bool MethodDescriptorProto::_internal_server_streaming() const {
+  return _impl_.server_streaming_;
+}
+inline bool MethodDescriptorProto::server_streaming() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MethodDescriptorProto.server_streaming)
+  return _internal_server_streaming();
+}
+inline void MethodDescriptorProto::_internal_set_server_streaming(bool value) {
+  _impl_._has_bits_[0] |= 0x00000020u;
+  _impl_.server_streaming_ = value;
+}
+inline void MethodDescriptorProto::set_server_streaming(bool value) {
+  _internal_set_server_streaming(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.MethodDescriptorProto.server_streaming)
+}
+
+// -------------------------------------------------------------------
+
+// FileOptions
+
+// optional string java_package = 1;
+inline bool FileOptions::_internal_has_java_package() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool FileOptions::has_java_package() const {
+  return _internal_has_java_package();
+}
+inline void FileOptions::clear_java_package() {
+  _impl_.java_package_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& FileOptions::java_package() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.java_package)
+  return _internal_java_package();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileOptions::set_java_package(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.java_package_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.java_package)
+}
+inline std::string* FileOptions::mutable_java_package() {
+  std::string* _s = _internal_mutable_java_package();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.java_package)
+  return _s;
+}
+inline const std::string& FileOptions::_internal_java_package() const {
+  return _impl_.java_package_.Get();
+}
+inline void FileOptions::_internal_set_java_package(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.java_package_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileOptions::_internal_mutable_java_package() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.java_package_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileOptions::release_java_package() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.java_package)
+  if (!_internal_has_java_package()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.java_package_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.java_package_.IsDefault()) {
+    _impl_.java_package_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileOptions::set_allocated_java_package(std::string* java_package) {
+  if (java_package != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.java_package_.SetAllocated(java_package, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.java_package_.IsDefault()) {
+    _impl_.java_package_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.java_package)
+}
+
+// optional string java_outer_classname = 8;
+inline bool FileOptions::_internal_has_java_outer_classname() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool FileOptions::has_java_outer_classname() const {
+  return _internal_has_java_outer_classname();
+}
+inline void FileOptions::clear_java_outer_classname() {
+  _impl_.java_outer_classname_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& FileOptions::java_outer_classname() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.java_outer_classname)
+  return _internal_java_outer_classname();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileOptions::set_java_outer_classname(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.java_outer_classname_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.java_outer_classname)
+}
+inline std::string* FileOptions::mutable_java_outer_classname() {
+  std::string* _s = _internal_mutable_java_outer_classname();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.java_outer_classname)
+  return _s;
+}
+inline const std::string& FileOptions::_internal_java_outer_classname() const {
+  return _impl_.java_outer_classname_.Get();
+}
+inline void FileOptions::_internal_set_java_outer_classname(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.java_outer_classname_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileOptions::_internal_mutable_java_outer_classname() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  return _impl_.java_outer_classname_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileOptions::release_java_outer_classname() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.java_outer_classname)
+  if (!_internal_has_java_outer_classname()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  auto* p = _impl_.java_outer_classname_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.java_outer_classname_.IsDefault()) {
+    _impl_.java_outer_classname_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileOptions::set_allocated_java_outer_classname(std::string* java_outer_classname) {
+  if (java_outer_classname != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.java_outer_classname_.SetAllocated(java_outer_classname, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.java_outer_classname_.IsDefault()) {
+    _impl_.java_outer_classname_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.java_outer_classname)
+}
+
+// optional bool java_multiple_files = 10 [default = false];
+inline bool FileOptions::_internal_has_java_multiple_files() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000400u) != 0;
+  return value;
+}
+inline bool FileOptions::has_java_multiple_files() const {
+  return _internal_has_java_multiple_files();
+}
+inline void FileOptions::clear_java_multiple_files() {
+  _impl_.java_multiple_files_ = false;
+  _impl_._has_bits_[0] &= ~0x00000400u;
+}
+inline bool FileOptions::_internal_java_multiple_files() const {
+  return _impl_.java_multiple_files_;
+}
+inline bool FileOptions::java_multiple_files() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.java_multiple_files)
+  return _internal_java_multiple_files();
+}
+inline void FileOptions::_internal_set_java_multiple_files(bool value) {
+  _impl_._has_bits_[0] |= 0x00000400u;
+  _impl_.java_multiple_files_ = value;
+}
+inline void FileOptions::set_java_multiple_files(bool value) {
+  _internal_set_java_multiple_files(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.java_multiple_files)
+}
+
+// optional bool java_generate_equals_and_hash = 20 [deprecated = true];
+inline bool FileOptions::_internal_has_java_generate_equals_and_hash() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000800u) != 0;
+  return value;
+}
+inline bool FileOptions::has_java_generate_equals_and_hash() const {
+  return _internal_has_java_generate_equals_and_hash();
+}
+inline void FileOptions::clear_java_generate_equals_and_hash() {
+  _impl_.java_generate_equals_and_hash_ = false;
+  _impl_._has_bits_[0] &= ~0x00000800u;
+}
+inline bool FileOptions::_internal_java_generate_equals_and_hash() const {
+  return _impl_.java_generate_equals_and_hash_;
+}
+inline bool FileOptions::java_generate_equals_and_hash() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.java_generate_equals_and_hash)
+  return _internal_java_generate_equals_and_hash();
+}
+inline void FileOptions::_internal_set_java_generate_equals_and_hash(bool value) {
+  _impl_._has_bits_[0] |= 0x00000800u;
+  _impl_.java_generate_equals_and_hash_ = value;
+}
+inline void FileOptions::set_java_generate_equals_and_hash(bool value) {
+  _internal_set_java_generate_equals_and_hash(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.java_generate_equals_and_hash)
+}
+
+// optional bool java_string_check_utf8 = 27 [default = false];
+inline bool FileOptions::_internal_has_java_string_check_utf8() const {
+  bool value = (_impl_._has_bits_[0] & 0x00001000u) != 0;
+  return value;
+}
+inline bool FileOptions::has_java_string_check_utf8() const {
+  return _internal_has_java_string_check_utf8();
+}
+inline void FileOptions::clear_java_string_check_utf8() {
+  _impl_.java_string_check_utf8_ = false;
+  _impl_._has_bits_[0] &= ~0x00001000u;
+}
+inline bool FileOptions::_internal_java_string_check_utf8() const {
+  return _impl_.java_string_check_utf8_;
+}
+inline bool FileOptions::java_string_check_utf8() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.java_string_check_utf8)
+  return _internal_java_string_check_utf8();
+}
+inline void FileOptions::_internal_set_java_string_check_utf8(bool value) {
+  _impl_._has_bits_[0] |= 0x00001000u;
+  _impl_.java_string_check_utf8_ = value;
+}
+inline void FileOptions::set_java_string_check_utf8(bool value) {
+  _internal_set_java_string_check_utf8(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.java_string_check_utf8)
+}
+
+// optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED];
+inline bool FileOptions::_internal_has_optimize_for() const {
+  bool value = (_impl_._has_bits_[0] & 0x00040000u) != 0;
+  return value;
+}
+inline bool FileOptions::has_optimize_for() const {
+  return _internal_has_optimize_for();
+}
+inline void FileOptions::clear_optimize_for() {
+  _impl_.optimize_for_ = 1;
+  _impl_._has_bits_[0] &= ~0x00040000u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode FileOptions::_internal_optimize_for() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode >(_impl_.optimize_for_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode FileOptions::optimize_for() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.optimize_for)
+  return _internal_optimize_for();
+}
+inline void FileOptions::_internal_set_optimize_for(::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode value) {
+  assert(::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode_IsValid(value));
+  _impl_._has_bits_[0] |= 0x00040000u;
+  _impl_.optimize_for_ = value;
+}
+inline void FileOptions::set_optimize_for(::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode value) {
+  _internal_set_optimize_for(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.optimize_for)
+}
+
+// optional string go_package = 11;
+inline bool FileOptions::_internal_has_go_package() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool FileOptions::has_go_package() const {
+  return _internal_has_go_package();
+}
+inline void FileOptions::clear_go_package() {
+  _impl_.go_package_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline const std::string& FileOptions::go_package() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.go_package)
+  return _internal_go_package();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileOptions::set_go_package(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.go_package_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.go_package)
+}
+inline std::string* FileOptions::mutable_go_package() {
+  std::string* _s = _internal_mutable_go_package();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.go_package)
+  return _s;
+}
+inline const std::string& FileOptions::_internal_go_package() const {
+  return _impl_.go_package_.Get();
+}
+inline void FileOptions::_internal_set_go_package(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  _impl_.go_package_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileOptions::_internal_mutable_go_package() {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  return _impl_.go_package_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileOptions::release_go_package() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.go_package)
+  if (!_internal_has_go_package()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000004u;
+  auto* p = _impl_.go_package_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.go_package_.IsDefault()) {
+    _impl_.go_package_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileOptions::set_allocated_go_package(std::string* go_package) {
+  if (go_package != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000004u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000004u;
+  }
+  _impl_.go_package_.SetAllocated(go_package, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.go_package_.IsDefault()) {
+    _impl_.go_package_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.go_package)
+}
+
+// optional bool cc_generic_services = 16 [default = false];
+inline bool FileOptions::_internal_has_cc_generic_services() const {
+  bool value = (_impl_._has_bits_[0] & 0x00002000u) != 0;
+  return value;
+}
+inline bool FileOptions::has_cc_generic_services() const {
+  return _internal_has_cc_generic_services();
+}
+inline void FileOptions::clear_cc_generic_services() {
+  _impl_.cc_generic_services_ = false;
+  _impl_._has_bits_[0] &= ~0x00002000u;
+}
+inline bool FileOptions::_internal_cc_generic_services() const {
+  return _impl_.cc_generic_services_;
+}
+inline bool FileOptions::cc_generic_services() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.cc_generic_services)
+  return _internal_cc_generic_services();
+}
+inline void FileOptions::_internal_set_cc_generic_services(bool value) {
+  _impl_._has_bits_[0] |= 0x00002000u;
+  _impl_.cc_generic_services_ = value;
+}
+inline void FileOptions::set_cc_generic_services(bool value) {
+  _internal_set_cc_generic_services(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.cc_generic_services)
+}
+
+// optional bool java_generic_services = 17 [default = false];
+inline bool FileOptions::_internal_has_java_generic_services() const {
+  bool value = (_impl_._has_bits_[0] & 0x00004000u) != 0;
+  return value;
+}
+inline bool FileOptions::has_java_generic_services() const {
+  return _internal_has_java_generic_services();
+}
+inline void FileOptions::clear_java_generic_services() {
+  _impl_.java_generic_services_ = false;
+  _impl_._has_bits_[0] &= ~0x00004000u;
+}
+inline bool FileOptions::_internal_java_generic_services() const {
+  return _impl_.java_generic_services_;
+}
+inline bool FileOptions::java_generic_services() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.java_generic_services)
+  return _internal_java_generic_services();
+}
+inline void FileOptions::_internal_set_java_generic_services(bool value) {
+  _impl_._has_bits_[0] |= 0x00004000u;
+  _impl_.java_generic_services_ = value;
+}
+inline void FileOptions::set_java_generic_services(bool value) {
+  _internal_set_java_generic_services(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.java_generic_services)
+}
+
+// optional bool py_generic_services = 18 [default = false];
+inline bool FileOptions::_internal_has_py_generic_services() const {
+  bool value = (_impl_._has_bits_[0] & 0x00008000u) != 0;
+  return value;
+}
+inline bool FileOptions::has_py_generic_services() const {
+  return _internal_has_py_generic_services();
+}
+inline void FileOptions::clear_py_generic_services() {
+  _impl_.py_generic_services_ = false;
+  _impl_._has_bits_[0] &= ~0x00008000u;
+}
+inline bool FileOptions::_internal_py_generic_services() const {
+  return _impl_.py_generic_services_;
+}
+inline bool FileOptions::py_generic_services() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.py_generic_services)
+  return _internal_py_generic_services();
+}
+inline void FileOptions::_internal_set_py_generic_services(bool value) {
+  _impl_._has_bits_[0] |= 0x00008000u;
+  _impl_.py_generic_services_ = value;
+}
+inline void FileOptions::set_py_generic_services(bool value) {
+  _internal_set_py_generic_services(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.py_generic_services)
+}
+
+// optional bool php_generic_services = 42 [default = false];
+inline bool FileOptions::_internal_has_php_generic_services() const {
+  bool value = (_impl_._has_bits_[0] & 0x00010000u) != 0;
+  return value;
+}
+inline bool FileOptions::has_php_generic_services() const {
+  return _internal_has_php_generic_services();
+}
+inline void FileOptions::clear_php_generic_services() {
+  _impl_.php_generic_services_ = false;
+  _impl_._has_bits_[0] &= ~0x00010000u;
+}
+inline bool FileOptions::_internal_php_generic_services() const {
+  return _impl_.php_generic_services_;
+}
+inline bool FileOptions::php_generic_services() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.php_generic_services)
+  return _internal_php_generic_services();
+}
+inline void FileOptions::_internal_set_php_generic_services(bool value) {
+  _impl_._has_bits_[0] |= 0x00010000u;
+  _impl_.php_generic_services_ = value;
+}
+inline void FileOptions::set_php_generic_services(bool value) {
+  _internal_set_php_generic_services(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.php_generic_services)
+}
+
+// optional bool deprecated = 23 [default = false];
+inline bool FileOptions::_internal_has_deprecated() const {
+  bool value = (_impl_._has_bits_[0] & 0x00020000u) != 0;
+  return value;
+}
+inline bool FileOptions::has_deprecated() const {
+  return _internal_has_deprecated();
+}
+inline void FileOptions::clear_deprecated() {
+  _impl_.deprecated_ = false;
+  _impl_._has_bits_[0] &= ~0x00020000u;
+}
+inline bool FileOptions::_internal_deprecated() const {
+  return _impl_.deprecated_;
+}
+inline bool FileOptions::deprecated() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.deprecated)
+  return _internal_deprecated();
+}
+inline void FileOptions::_internal_set_deprecated(bool value) {
+  _impl_._has_bits_[0] |= 0x00020000u;
+  _impl_.deprecated_ = value;
+}
+inline void FileOptions::set_deprecated(bool value) {
+  _internal_set_deprecated(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.deprecated)
+}
+
+// optional bool cc_enable_arenas = 31 [default = true];
+inline bool FileOptions::_internal_has_cc_enable_arenas() const {
+  bool value = (_impl_._has_bits_[0] & 0x00080000u) != 0;
+  return value;
+}
+inline bool FileOptions::has_cc_enable_arenas() const {
+  return _internal_has_cc_enable_arenas();
+}
+inline void FileOptions::clear_cc_enable_arenas() {
+  _impl_.cc_enable_arenas_ = true;
+  _impl_._has_bits_[0] &= ~0x00080000u;
+}
+inline bool FileOptions::_internal_cc_enable_arenas() const {
+  return _impl_.cc_enable_arenas_;
+}
+inline bool FileOptions::cc_enable_arenas() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.cc_enable_arenas)
+  return _internal_cc_enable_arenas();
+}
+inline void FileOptions::_internal_set_cc_enable_arenas(bool value) {
+  _impl_._has_bits_[0] |= 0x00080000u;
+  _impl_.cc_enable_arenas_ = value;
+}
+inline void FileOptions::set_cc_enable_arenas(bool value) {
+  _internal_set_cc_enable_arenas(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.cc_enable_arenas)
+}
+
+// optional string objc_class_prefix = 36;
+inline bool FileOptions::_internal_has_objc_class_prefix() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+  return value;
+}
+inline bool FileOptions::has_objc_class_prefix() const {
+  return _internal_has_objc_class_prefix();
+}
+inline void FileOptions::clear_objc_class_prefix() {
+  _impl_.objc_class_prefix_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline const std::string& FileOptions::objc_class_prefix() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.objc_class_prefix)
+  return _internal_objc_class_prefix();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileOptions::set_objc_class_prefix(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000008u;
+ _impl_.objc_class_prefix_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.objc_class_prefix)
+}
+inline std::string* FileOptions::mutable_objc_class_prefix() {
+  std::string* _s = _internal_mutable_objc_class_prefix();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.objc_class_prefix)
+  return _s;
+}
+inline const std::string& FileOptions::_internal_objc_class_prefix() const {
+  return _impl_.objc_class_prefix_.Get();
+}
+inline void FileOptions::_internal_set_objc_class_prefix(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000008u;
+  _impl_.objc_class_prefix_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileOptions::_internal_mutable_objc_class_prefix() {
+  _impl_._has_bits_[0] |= 0x00000008u;
+  return _impl_.objc_class_prefix_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileOptions::release_objc_class_prefix() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.objc_class_prefix)
+  if (!_internal_has_objc_class_prefix()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000008u;
+  auto* p = _impl_.objc_class_prefix_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.objc_class_prefix_.IsDefault()) {
+    _impl_.objc_class_prefix_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileOptions::set_allocated_objc_class_prefix(std::string* objc_class_prefix) {
+  if (objc_class_prefix != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000008u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000008u;
+  }
+  _impl_.objc_class_prefix_.SetAllocated(objc_class_prefix, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.objc_class_prefix_.IsDefault()) {
+    _impl_.objc_class_prefix_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.objc_class_prefix)
+}
+
+// optional string csharp_namespace = 37;
+inline bool FileOptions::_internal_has_csharp_namespace() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+  return value;
+}
+inline bool FileOptions::has_csharp_namespace() const {
+  return _internal_has_csharp_namespace();
+}
+inline void FileOptions::clear_csharp_namespace() {
+  _impl_.csharp_namespace_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline const std::string& FileOptions::csharp_namespace() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.csharp_namespace)
+  return _internal_csharp_namespace();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileOptions::set_csharp_namespace(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000010u;
+ _impl_.csharp_namespace_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.csharp_namespace)
+}
+inline std::string* FileOptions::mutable_csharp_namespace() {
+  std::string* _s = _internal_mutable_csharp_namespace();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.csharp_namespace)
+  return _s;
+}
+inline const std::string& FileOptions::_internal_csharp_namespace() const {
+  return _impl_.csharp_namespace_.Get();
+}
+inline void FileOptions::_internal_set_csharp_namespace(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000010u;
+  _impl_.csharp_namespace_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileOptions::_internal_mutable_csharp_namespace() {
+  _impl_._has_bits_[0] |= 0x00000010u;
+  return _impl_.csharp_namespace_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileOptions::release_csharp_namespace() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.csharp_namespace)
+  if (!_internal_has_csharp_namespace()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000010u;
+  auto* p = _impl_.csharp_namespace_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.csharp_namespace_.IsDefault()) {
+    _impl_.csharp_namespace_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileOptions::set_allocated_csharp_namespace(std::string* csharp_namespace) {
+  if (csharp_namespace != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000010u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000010u;
+  }
+  _impl_.csharp_namespace_.SetAllocated(csharp_namespace, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.csharp_namespace_.IsDefault()) {
+    _impl_.csharp_namespace_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.csharp_namespace)
+}
+
+// optional string swift_prefix = 39;
+inline bool FileOptions::_internal_has_swift_prefix() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000020u) != 0;
+  return value;
+}
+inline bool FileOptions::has_swift_prefix() const {
+  return _internal_has_swift_prefix();
+}
+inline void FileOptions::clear_swift_prefix() {
+  _impl_.swift_prefix_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000020u;
+}
+inline const std::string& FileOptions::swift_prefix() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.swift_prefix)
+  return _internal_swift_prefix();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileOptions::set_swift_prefix(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000020u;
+ _impl_.swift_prefix_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.swift_prefix)
+}
+inline std::string* FileOptions::mutable_swift_prefix() {
+  std::string* _s = _internal_mutable_swift_prefix();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.swift_prefix)
+  return _s;
+}
+inline const std::string& FileOptions::_internal_swift_prefix() const {
+  return _impl_.swift_prefix_.Get();
+}
+inline void FileOptions::_internal_set_swift_prefix(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000020u;
+  _impl_.swift_prefix_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileOptions::_internal_mutable_swift_prefix() {
+  _impl_._has_bits_[0] |= 0x00000020u;
+  return _impl_.swift_prefix_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileOptions::release_swift_prefix() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.swift_prefix)
+  if (!_internal_has_swift_prefix()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000020u;
+  auto* p = _impl_.swift_prefix_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.swift_prefix_.IsDefault()) {
+    _impl_.swift_prefix_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileOptions::set_allocated_swift_prefix(std::string* swift_prefix) {
+  if (swift_prefix != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000020u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000020u;
+  }
+  _impl_.swift_prefix_.SetAllocated(swift_prefix, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.swift_prefix_.IsDefault()) {
+    _impl_.swift_prefix_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.swift_prefix)
+}
+
+// optional string php_class_prefix = 40;
+inline bool FileOptions::_internal_has_php_class_prefix() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000040u) != 0;
+  return value;
+}
+inline bool FileOptions::has_php_class_prefix() const {
+  return _internal_has_php_class_prefix();
+}
+inline void FileOptions::clear_php_class_prefix() {
+  _impl_.php_class_prefix_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000040u;
+}
+inline const std::string& FileOptions::php_class_prefix() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.php_class_prefix)
+  return _internal_php_class_prefix();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileOptions::set_php_class_prefix(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000040u;
+ _impl_.php_class_prefix_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.php_class_prefix)
+}
+inline std::string* FileOptions::mutable_php_class_prefix() {
+  std::string* _s = _internal_mutable_php_class_prefix();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.php_class_prefix)
+  return _s;
+}
+inline const std::string& FileOptions::_internal_php_class_prefix() const {
+  return _impl_.php_class_prefix_.Get();
+}
+inline void FileOptions::_internal_set_php_class_prefix(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000040u;
+  _impl_.php_class_prefix_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileOptions::_internal_mutable_php_class_prefix() {
+  _impl_._has_bits_[0] |= 0x00000040u;
+  return _impl_.php_class_prefix_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileOptions::release_php_class_prefix() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.php_class_prefix)
+  if (!_internal_has_php_class_prefix()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000040u;
+  auto* p = _impl_.php_class_prefix_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.php_class_prefix_.IsDefault()) {
+    _impl_.php_class_prefix_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileOptions::set_allocated_php_class_prefix(std::string* php_class_prefix) {
+  if (php_class_prefix != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000040u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000040u;
+  }
+  _impl_.php_class_prefix_.SetAllocated(php_class_prefix, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.php_class_prefix_.IsDefault()) {
+    _impl_.php_class_prefix_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.php_class_prefix)
+}
+
+// optional string php_namespace = 41;
+inline bool FileOptions::_internal_has_php_namespace() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000080u) != 0;
+  return value;
+}
+inline bool FileOptions::has_php_namespace() const {
+  return _internal_has_php_namespace();
+}
+inline void FileOptions::clear_php_namespace() {
+  _impl_.php_namespace_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000080u;
+}
+inline const std::string& FileOptions::php_namespace() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.php_namespace)
+  return _internal_php_namespace();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileOptions::set_php_namespace(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000080u;
+ _impl_.php_namespace_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.php_namespace)
+}
+inline std::string* FileOptions::mutable_php_namespace() {
+  std::string* _s = _internal_mutable_php_namespace();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.php_namespace)
+  return _s;
+}
+inline const std::string& FileOptions::_internal_php_namespace() const {
+  return _impl_.php_namespace_.Get();
+}
+inline void FileOptions::_internal_set_php_namespace(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000080u;
+  _impl_.php_namespace_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileOptions::_internal_mutable_php_namespace() {
+  _impl_._has_bits_[0] |= 0x00000080u;
+  return _impl_.php_namespace_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileOptions::release_php_namespace() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.php_namespace)
+  if (!_internal_has_php_namespace()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000080u;
+  auto* p = _impl_.php_namespace_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.php_namespace_.IsDefault()) {
+    _impl_.php_namespace_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileOptions::set_allocated_php_namespace(std::string* php_namespace) {
+  if (php_namespace != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000080u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000080u;
+  }
+  _impl_.php_namespace_.SetAllocated(php_namespace, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.php_namespace_.IsDefault()) {
+    _impl_.php_namespace_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.php_namespace)
+}
+
+// optional string php_metadata_namespace = 44;
+inline bool FileOptions::_internal_has_php_metadata_namespace() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000100u) != 0;
+  return value;
+}
+inline bool FileOptions::has_php_metadata_namespace() const {
+  return _internal_has_php_metadata_namespace();
+}
+inline void FileOptions::clear_php_metadata_namespace() {
+  _impl_.php_metadata_namespace_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000100u;
+}
+inline const std::string& FileOptions::php_metadata_namespace() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.php_metadata_namespace)
+  return _internal_php_metadata_namespace();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileOptions::set_php_metadata_namespace(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000100u;
+ _impl_.php_metadata_namespace_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.php_metadata_namespace)
+}
+inline std::string* FileOptions::mutable_php_metadata_namespace() {
+  std::string* _s = _internal_mutable_php_metadata_namespace();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.php_metadata_namespace)
+  return _s;
+}
+inline const std::string& FileOptions::_internal_php_metadata_namespace() const {
+  return _impl_.php_metadata_namespace_.Get();
+}
+inline void FileOptions::_internal_set_php_metadata_namespace(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000100u;
+  _impl_.php_metadata_namespace_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileOptions::_internal_mutable_php_metadata_namespace() {
+  _impl_._has_bits_[0] |= 0x00000100u;
+  return _impl_.php_metadata_namespace_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileOptions::release_php_metadata_namespace() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.php_metadata_namespace)
+  if (!_internal_has_php_metadata_namespace()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000100u;
+  auto* p = _impl_.php_metadata_namespace_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.php_metadata_namespace_.IsDefault()) {
+    _impl_.php_metadata_namespace_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileOptions::set_allocated_php_metadata_namespace(std::string* php_metadata_namespace) {
+  if (php_metadata_namespace != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000100u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000100u;
+  }
+  _impl_.php_metadata_namespace_.SetAllocated(php_metadata_namespace, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.php_metadata_namespace_.IsDefault()) {
+    _impl_.php_metadata_namespace_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.php_metadata_namespace)
+}
+
+// optional string ruby_package = 45;
+inline bool FileOptions::_internal_has_ruby_package() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000200u) != 0;
+  return value;
+}
+inline bool FileOptions::has_ruby_package() const {
+  return _internal_has_ruby_package();
+}
+inline void FileOptions::clear_ruby_package() {
+  _impl_.ruby_package_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000200u;
+}
+inline const std::string& FileOptions::ruby_package() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.ruby_package)
+  return _internal_ruby_package();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FileOptions::set_ruby_package(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000200u;
+ _impl_.ruby_package_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.ruby_package)
+}
+inline std::string* FileOptions::mutable_ruby_package() {
+  std::string* _s = _internal_mutable_ruby_package();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.ruby_package)
+  return _s;
+}
+inline const std::string& FileOptions::_internal_ruby_package() const {
+  return _impl_.ruby_package_.Get();
+}
+inline void FileOptions::_internal_set_ruby_package(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000200u;
+  _impl_.ruby_package_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FileOptions::_internal_mutable_ruby_package() {
+  _impl_._has_bits_[0] |= 0x00000200u;
+  return _impl_.ruby_package_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FileOptions::release_ruby_package() {
+  // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.ruby_package)
+  if (!_internal_has_ruby_package()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000200u;
+  auto* p = _impl_.ruby_package_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.ruby_package_.IsDefault()) {
+    _impl_.ruby_package_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void FileOptions::set_allocated_ruby_package(std::string* ruby_package) {
+  if (ruby_package != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000200u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000200u;
+  }
+  _impl_.ruby_package_.SetAllocated(ruby_package, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.ruby_package_.IsDefault()) {
+    _impl_.ruby_package_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.ruby_package)
+}
+
+// repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+inline int FileOptions::_internal_uninterpreted_option_size() const {
+  return _impl_.uninterpreted_option_.size();
+}
+inline int FileOptions::uninterpreted_option_size() const {
+  return _internal_uninterpreted_option_size();
+}
+inline void FileOptions::clear_uninterpreted_option() {
+  _impl_.uninterpreted_option_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* FileOptions::mutable_uninterpreted_option(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+FileOptions::mutable_uninterpreted_option() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileOptions.uninterpreted_option)
+  return &_impl_.uninterpreted_option_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& FileOptions::_internal_uninterpreted_option(int index) const {
+  return _impl_.uninterpreted_option_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& FileOptions::uninterpreted_option(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.uninterpreted_option)
+  return _internal_uninterpreted_option(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* FileOptions::_internal_add_uninterpreted_option() {
+  return _impl_.uninterpreted_option_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* FileOptions::add_uninterpreted_option() {
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _add = _internal_add_uninterpreted_option();
+  // @@protoc_insertion_point(field_add:google.protobuf.FileOptions.uninterpreted_option)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+FileOptions::uninterpreted_option() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FileOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_;
+}
+
+// -------------------------------------------------------------------
+
+// MessageOptions
+
+// optional bool message_set_wire_format = 1 [default = false];
+inline bool MessageOptions::_internal_has_message_set_wire_format() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool MessageOptions::has_message_set_wire_format() const {
+  return _internal_has_message_set_wire_format();
+}
+inline void MessageOptions::clear_message_set_wire_format() {
+  _impl_.message_set_wire_format_ = false;
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline bool MessageOptions::_internal_message_set_wire_format() const {
+  return _impl_.message_set_wire_format_;
+}
+inline bool MessageOptions::message_set_wire_format() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MessageOptions.message_set_wire_format)
+  return _internal_message_set_wire_format();
+}
+inline void MessageOptions::_internal_set_message_set_wire_format(bool value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.message_set_wire_format_ = value;
+}
+inline void MessageOptions::set_message_set_wire_format(bool value) {
+  _internal_set_message_set_wire_format(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.MessageOptions.message_set_wire_format)
+}
+
+// optional bool no_standard_descriptor_accessor = 2 [default = false];
+inline bool MessageOptions::_internal_has_no_standard_descriptor_accessor() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool MessageOptions::has_no_standard_descriptor_accessor() const {
+  return _internal_has_no_standard_descriptor_accessor();
+}
+inline void MessageOptions::clear_no_standard_descriptor_accessor() {
+  _impl_.no_standard_descriptor_accessor_ = false;
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline bool MessageOptions::_internal_no_standard_descriptor_accessor() const {
+  return _impl_.no_standard_descriptor_accessor_;
+}
+inline bool MessageOptions::no_standard_descriptor_accessor() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MessageOptions.no_standard_descriptor_accessor)
+  return _internal_no_standard_descriptor_accessor();
+}
+inline void MessageOptions::_internal_set_no_standard_descriptor_accessor(bool value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.no_standard_descriptor_accessor_ = value;
+}
+inline void MessageOptions::set_no_standard_descriptor_accessor(bool value) {
+  _internal_set_no_standard_descriptor_accessor(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.MessageOptions.no_standard_descriptor_accessor)
+}
+
+// optional bool deprecated = 3 [default = false];
+inline bool MessageOptions::_internal_has_deprecated() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool MessageOptions::has_deprecated() const {
+  return _internal_has_deprecated();
+}
+inline void MessageOptions::clear_deprecated() {
+  _impl_.deprecated_ = false;
+  _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline bool MessageOptions::_internal_deprecated() const {
+  return _impl_.deprecated_;
+}
+inline bool MessageOptions::deprecated() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MessageOptions.deprecated)
+  return _internal_deprecated();
+}
+inline void MessageOptions::_internal_set_deprecated(bool value) {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  _impl_.deprecated_ = value;
+}
+inline void MessageOptions::set_deprecated(bool value) {
+  _internal_set_deprecated(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.MessageOptions.deprecated)
+}
+
+// optional bool map_entry = 7;
+inline bool MessageOptions::_internal_has_map_entry() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+  return value;
+}
+inline bool MessageOptions::has_map_entry() const {
+  return _internal_has_map_entry();
+}
+inline void MessageOptions::clear_map_entry() {
+  _impl_.map_entry_ = false;
+  _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline bool MessageOptions::_internal_map_entry() const {
+  return _impl_.map_entry_;
+}
+inline bool MessageOptions::map_entry() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MessageOptions.map_entry)
+  return _internal_map_entry();
+}
+inline void MessageOptions::_internal_set_map_entry(bool value) {
+  _impl_._has_bits_[0] |= 0x00000008u;
+  _impl_.map_entry_ = value;
+}
+inline void MessageOptions::set_map_entry(bool value) {
+  _internal_set_map_entry(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.MessageOptions.map_entry)
+}
+
+// repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+inline int MessageOptions::_internal_uninterpreted_option_size() const {
+  return _impl_.uninterpreted_option_.size();
+}
+inline int MessageOptions::uninterpreted_option_size() const {
+  return _internal_uninterpreted_option_size();
+}
+inline void MessageOptions::clear_uninterpreted_option() {
+  _impl_.uninterpreted_option_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* MessageOptions::mutable_uninterpreted_option(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.MessageOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+MessageOptions::mutable_uninterpreted_option() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.MessageOptions.uninterpreted_option)
+  return &_impl_.uninterpreted_option_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& MessageOptions::_internal_uninterpreted_option(int index) const {
+  return _impl_.uninterpreted_option_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& MessageOptions::uninterpreted_option(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MessageOptions.uninterpreted_option)
+  return _internal_uninterpreted_option(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* MessageOptions::_internal_add_uninterpreted_option() {
+  return _impl_.uninterpreted_option_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* MessageOptions::add_uninterpreted_option() {
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _add = _internal_add_uninterpreted_option();
+  // @@protoc_insertion_point(field_add:google.protobuf.MessageOptions.uninterpreted_option)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+MessageOptions::uninterpreted_option() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.MessageOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_;
+}
+
+// -------------------------------------------------------------------
+
+// FieldOptions
+
+// optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING];
+inline bool FieldOptions::_internal_has_ctype() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool FieldOptions::has_ctype() const {
+  return _internal_has_ctype();
+}
+inline void FieldOptions::clear_ctype() {
+  _impl_.ctype_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldOptions_CType FieldOptions::_internal_ctype() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::FieldOptions_CType >(_impl_.ctype_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldOptions_CType FieldOptions::ctype() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.ctype)
+  return _internal_ctype();
+}
+inline void FieldOptions::_internal_set_ctype(::PROTOBUF_NAMESPACE_ID::FieldOptions_CType value) {
+  assert(::PROTOBUF_NAMESPACE_ID::FieldOptions_CType_IsValid(value));
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.ctype_ = value;
+}
+inline void FieldOptions::set_ctype(::PROTOBUF_NAMESPACE_ID::FieldOptions_CType value) {
+  _internal_set_ctype(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.ctype)
+}
+
+// optional bool packed = 2;
+inline bool FieldOptions::_internal_has_packed() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool FieldOptions::has_packed() const {
+  return _internal_has_packed();
+}
+inline void FieldOptions::clear_packed() {
+  _impl_.packed_ = false;
+  _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline bool FieldOptions::_internal_packed() const {
+  return _impl_.packed_;
+}
+inline bool FieldOptions::packed() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.packed)
+  return _internal_packed();
+}
+inline void FieldOptions::_internal_set_packed(bool value) {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  _impl_.packed_ = value;
+}
+inline void FieldOptions::set_packed(bool value) {
+  _internal_set_packed(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.packed)
+}
+
+// optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];
+inline bool FieldOptions::_internal_has_jstype() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool FieldOptions::has_jstype() const {
+  return _internal_has_jstype();
+}
+inline void FieldOptions::clear_jstype() {
+  _impl_.jstype_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType FieldOptions::_internal_jstype() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType >(_impl_.jstype_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType FieldOptions::jstype() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.jstype)
+  return _internal_jstype();
+}
+inline void FieldOptions::_internal_set_jstype(::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType value) {
+  assert(::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType_IsValid(value));
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.jstype_ = value;
+}
+inline void FieldOptions::set_jstype(::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType value) {
+  _internal_set_jstype(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.jstype)
+}
+
+// optional bool lazy = 5 [default = false];
+inline bool FieldOptions::_internal_has_lazy() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+  return value;
+}
+inline bool FieldOptions::has_lazy() const {
+  return _internal_has_lazy();
+}
+inline void FieldOptions::clear_lazy() {
+  _impl_.lazy_ = false;
+  _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline bool FieldOptions::_internal_lazy() const {
+  return _impl_.lazy_;
+}
+inline bool FieldOptions::lazy() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.lazy)
+  return _internal_lazy();
+}
+inline void FieldOptions::_internal_set_lazy(bool value) {
+  _impl_._has_bits_[0] |= 0x00000008u;
+  _impl_.lazy_ = value;
+}
+inline void FieldOptions::set_lazy(bool value) {
+  _internal_set_lazy(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.lazy)
+}
+
+// optional bool unverified_lazy = 15 [default = false];
+inline bool FieldOptions::_internal_has_unverified_lazy() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+  return value;
+}
+inline bool FieldOptions::has_unverified_lazy() const {
+  return _internal_has_unverified_lazy();
+}
+inline void FieldOptions::clear_unverified_lazy() {
+  _impl_.unverified_lazy_ = false;
+  _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline bool FieldOptions::_internal_unverified_lazy() const {
+  return _impl_.unverified_lazy_;
+}
+inline bool FieldOptions::unverified_lazy() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.unverified_lazy)
+  return _internal_unverified_lazy();
+}
+inline void FieldOptions::_internal_set_unverified_lazy(bool value) {
+  _impl_._has_bits_[0] |= 0x00000010u;
+  _impl_.unverified_lazy_ = value;
+}
+inline void FieldOptions::set_unverified_lazy(bool value) {
+  _internal_set_unverified_lazy(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.unverified_lazy)
+}
+
+// optional bool deprecated = 3 [default = false];
+inline bool FieldOptions::_internal_has_deprecated() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000020u) != 0;
+  return value;
+}
+inline bool FieldOptions::has_deprecated() const {
+  return _internal_has_deprecated();
+}
+inline void FieldOptions::clear_deprecated() {
+  _impl_.deprecated_ = false;
+  _impl_._has_bits_[0] &= ~0x00000020u;
+}
+inline bool FieldOptions::_internal_deprecated() const {
+  return _impl_.deprecated_;
+}
+inline bool FieldOptions::deprecated() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.deprecated)
+  return _internal_deprecated();
+}
+inline void FieldOptions::_internal_set_deprecated(bool value) {
+  _impl_._has_bits_[0] |= 0x00000020u;
+  _impl_.deprecated_ = value;
+}
+inline void FieldOptions::set_deprecated(bool value) {
+  _internal_set_deprecated(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.deprecated)
+}
+
+// optional bool weak = 10 [default = false];
+inline bool FieldOptions::_internal_has_weak() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000040u) != 0;
+  return value;
+}
+inline bool FieldOptions::has_weak() const {
+  return _internal_has_weak();
+}
+inline void FieldOptions::clear_weak() {
+  _impl_.weak_ = false;
+  _impl_._has_bits_[0] &= ~0x00000040u;
+}
+inline bool FieldOptions::_internal_weak() const {
+  return _impl_.weak_;
+}
+inline bool FieldOptions::weak() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.weak)
+  return _internal_weak();
+}
+inline void FieldOptions::_internal_set_weak(bool value) {
+  _impl_._has_bits_[0] |= 0x00000040u;
+  _impl_.weak_ = value;
+}
+inline void FieldOptions::set_weak(bool value) {
+  _internal_set_weak(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.weak)
+}
+
+// repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+inline int FieldOptions::_internal_uninterpreted_option_size() const {
+  return _impl_.uninterpreted_option_.size();
+}
+inline int FieldOptions::uninterpreted_option_size() const {
+  return _internal_uninterpreted_option_size();
+}
+inline void FieldOptions::clear_uninterpreted_option() {
+  _impl_.uninterpreted_option_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* FieldOptions::mutable_uninterpreted_option(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FieldOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+FieldOptions::mutable_uninterpreted_option() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FieldOptions.uninterpreted_option)
+  return &_impl_.uninterpreted_option_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& FieldOptions::_internal_uninterpreted_option(int index) const {
+  return _impl_.uninterpreted_option_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& FieldOptions::uninterpreted_option(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.uninterpreted_option)
+  return _internal_uninterpreted_option(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* FieldOptions::_internal_add_uninterpreted_option() {
+  return _impl_.uninterpreted_option_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* FieldOptions::add_uninterpreted_option() {
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _add = _internal_add_uninterpreted_option();
+  // @@protoc_insertion_point(field_add:google.protobuf.FieldOptions.uninterpreted_option)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+FieldOptions::uninterpreted_option() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FieldOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_;
+}
+
+// -------------------------------------------------------------------
+
+// OneofOptions
+
+// repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+inline int OneofOptions::_internal_uninterpreted_option_size() const {
+  return _impl_.uninterpreted_option_.size();
+}
+inline int OneofOptions::uninterpreted_option_size() const {
+  return _internal_uninterpreted_option_size();
+}
+inline void OneofOptions::clear_uninterpreted_option() {
+  _impl_.uninterpreted_option_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* OneofOptions::mutable_uninterpreted_option(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.OneofOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+OneofOptions::mutable_uninterpreted_option() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.OneofOptions.uninterpreted_option)
+  return &_impl_.uninterpreted_option_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& OneofOptions::_internal_uninterpreted_option(int index) const {
+  return _impl_.uninterpreted_option_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& OneofOptions::uninterpreted_option(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.OneofOptions.uninterpreted_option)
+  return _internal_uninterpreted_option(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* OneofOptions::_internal_add_uninterpreted_option() {
+  return _impl_.uninterpreted_option_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* OneofOptions::add_uninterpreted_option() {
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _add = _internal_add_uninterpreted_option();
+  // @@protoc_insertion_point(field_add:google.protobuf.OneofOptions.uninterpreted_option)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+OneofOptions::uninterpreted_option() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.OneofOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_;
+}
+
+// -------------------------------------------------------------------
+
+// EnumOptions
+
+// optional bool allow_alias = 2;
+inline bool EnumOptions::_internal_has_allow_alias() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool EnumOptions::has_allow_alias() const {
+  return _internal_has_allow_alias();
+}
+inline void EnumOptions::clear_allow_alias() {
+  _impl_.allow_alias_ = false;
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline bool EnumOptions::_internal_allow_alias() const {
+  return _impl_.allow_alias_;
+}
+inline bool EnumOptions::allow_alias() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumOptions.allow_alias)
+  return _internal_allow_alias();
+}
+inline void EnumOptions::_internal_set_allow_alias(bool value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.allow_alias_ = value;
+}
+inline void EnumOptions::set_allow_alias(bool value) {
+  _internal_set_allow_alias(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumOptions.allow_alias)
+}
+
+// optional bool deprecated = 3 [default = false];
+inline bool EnumOptions::_internal_has_deprecated() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool EnumOptions::has_deprecated() const {
+  return _internal_has_deprecated();
+}
+inline void EnumOptions::clear_deprecated() {
+  _impl_.deprecated_ = false;
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline bool EnumOptions::_internal_deprecated() const {
+  return _impl_.deprecated_;
+}
+inline bool EnumOptions::deprecated() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumOptions.deprecated)
+  return _internal_deprecated();
+}
+inline void EnumOptions::_internal_set_deprecated(bool value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.deprecated_ = value;
+}
+inline void EnumOptions::set_deprecated(bool value) {
+  _internal_set_deprecated(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumOptions.deprecated)
+}
+
+// repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+inline int EnumOptions::_internal_uninterpreted_option_size() const {
+  return _impl_.uninterpreted_option_.size();
+}
+inline int EnumOptions::uninterpreted_option_size() const {
+  return _internal_uninterpreted_option_size();
+}
+inline void EnumOptions::clear_uninterpreted_option() {
+  _impl_.uninterpreted_option_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* EnumOptions::mutable_uninterpreted_option(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+EnumOptions::mutable_uninterpreted_option() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumOptions.uninterpreted_option)
+  return &_impl_.uninterpreted_option_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& EnumOptions::_internal_uninterpreted_option(int index) const {
+  return _impl_.uninterpreted_option_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& EnumOptions::uninterpreted_option(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumOptions.uninterpreted_option)
+  return _internal_uninterpreted_option(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* EnumOptions::_internal_add_uninterpreted_option() {
+  return _impl_.uninterpreted_option_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* EnumOptions::add_uninterpreted_option() {
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _add = _internal_add_uninterpreted_option();
+  // @@protoc_insertion_point(field_add:google.protobuf.EnumOptions.uninterpreted_option)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+EnumOptions::uninterpreted_option() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.EnumOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_;
+}
+
+// -------------------------------------------------------------------
+
+// EnumValueOptions
+
+// optional bool deprecated = 1 [default = false];
+inline bool EnumValueOptions::_internal_has_deprecated() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool EnumValueOptions::has_deprecated() const {
+  return _internal_has_deprecated();
+}
+inline void EnumValueOptions::clear_deprecated() {
+  _impl_.deprecated_ = false;
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline bool EnumValueOptions::_internal_deprecated() const {
+  return _impl_.deprecated_;
+}
+inline bool EnumValueOptions::deprecated() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumValueOptions.deprecated)
+  return _internal_deprecated();
+}
+inline void EnumValueOptions::_internal_set_deprecated(bool value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.deprecated_ = value;
+}
+inline void EnumValueOptions::set_deprecated(bool value) {
+  _internal_set_deprecated(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumValueOptions.deprecated)
+}
+
+// repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+inline int EnumValueOptions::_internal_uninterpreted_option_size() const {
+  return _impl_.uninterpreted_option_.size();
+}
+inline int EnumValueOptions::uninterpreted_option_size() const {
+  return _internal_uninterpreted_option_size();
+}
+inline void EnumValueOptions::clear_uninterpreted_option() {
+  _impl_.uninterpreted_option_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* EnumValueOptions::mutable_uninterpreted_option(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumValueOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+EnumValueOptions::mutable_uninterpreted_option() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumValueOptions.uninterpreted_option)
+  return &_impl_.uninterpreted_option_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& EnumValueOptions::_internal_uninterpreted_option(int index) const {
+  return _impl_.uninterpreted_option_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& EnumValueOptions::uninterpreted_option(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumValueOptions.uninterpreted_option)
+  return _internal_uninterpreted_option(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* EnumValueOptions::_internal_add_uninterpreted_option() {
+  return _impl_.uninterpreted_option_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* EnumValueOptions::add_uninterpreted_option() {
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _add = _internal_add_uninterpreted_option();
+  // @@protoc_insertion_point(field_add:google.protobuf.EnumValueOptions.uninterpreted_option)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+EnumValueOptions::uninterpreted_option() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.EnumValueOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_;
+}
+
+// -------------------------------------------------------------------
+
+// ServiceOptions
+
+// optional bool deprecated = 33 [default = false];
+inline bool ServiceOptions::_internal_has_deprecated() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool ServiceOptions::has_deprecated() const {
+  return _internal_has_deprecated();
+}
+inline void ServiceOptions::clear_deprecated() {
+  _impl_.deprecated_ = false;
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline bool ServiceOptions::_internal_deprecated() const {
+  return _impl_.deprecated_;
+}
+inline bool ServiceOptions::deprecated() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.ServiceOptions.deprecated)
+  return _internal_deprecated();
+}
+inline void ServiceOptions::_internal_set_deprecated(bool value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.deprecated_ = value;
+}
+inline void ServiceOptions::set_deprecated(bool value) {
+  _internal_set_deprecated(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.ServiceOptions.deprecated)
+}
+
+// repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+inline int ServiceOptions::_internal_uninterpreted_option_size() const {
+  return _impl_.uninterpreted_option_.size();
+}
+inline int ServiceOptions::uninterpreted_option_size() const {
+  return _internal_uninterpreted_option_size();
+}
+inline void ServiceOptions::clear_uninterpreted_option() {
+  _impl_.uninterpreted_option_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* ServiceOptions::mutable_uninterpreted_option(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.ServiceOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+ServiceOptions::mutable_uninterpreted_option() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.ServiceOptions.uninterpreted_option)
+  return &_impl_.uninterpreted_option_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& ServiceOptions::_internal_uninterpreted_option(int index) const {
+  return _impl_.uninterpreted_option_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& ServiceOptions::uninterpreted_option(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.ServiceOptions.uninterpreted_option)
+  return _internal_uninterpreted_option(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* ServiceOptions::_internal_add_uninterpreted_option() {
+  return _impl_.uninterpreted_option_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* ServiceOptions::add_uninterpreted_option() {
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _add = _internal_add_uninterpreted_option();
+  // @@protoc_insertion_point(field_add:google.protobuf.ServiceOptions.uninterpreted_option)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+ServiceOptions::uninterpreted_option() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.ServiceOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_;
+}
+
+// -------------------------------------------------------------------
+
+// MethodOptions
+
+// optional bool deprecated = 33 [default = false];
+inline bool MethodOptions::_internal_has_deprecated() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool MethodOptions::has_deprecated() const {
+  return _internal_has_deprecated();
+}
+inline void MethodOptions::clear_deprecated() {
+  _impl_.deprecated_ = false;
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline bool MethodOptions::_internal_deprecated() const {
+  return _impl_.deprecated_;
+}
+inline bool MethodOptions::deprecated() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MethodOptions.deprecated)
+  return _internal_deprecated();
+}
+inline void MethodOptions::_internal_set_deprecated(bool value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.deprecated_ = value;
+}
+inline void MethodOptions::set_deprecated(bool value) {
+  _internal_set_deprecated(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.MethodOptions.deprecated)
+}
+
+// optional .google.protobuf.MethodOptions.IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN];
+inline bool MethodOptions::_internal_has_idempotency_level() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool MethodOptions::has_idempotency_level() const {
+  return _internal_has_idempotency_level();
+}
+inline void MethodOptions::clear_idempotency_level() {
+  _impl_.idempotency_level_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel MethodOptions::_internal_idempotency_level() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel >(_impl_.idempotency_level_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel MethodOptions::idempotency_level() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MethodOptions.idempotency_level)
+  return _internal_idempotency_level();
+}
+inline void MethodOptions::_internal_set_idempotency_level(::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel value) {
+  assert(::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel_IsValid(value));
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.idempotency_level_ = value;
+}
+inline void MethodOptions::set_idempotency_level(::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel value) {
+  _internal_set_idempotency_level(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.MethodOptions.idempotency_level)
+}
+
+// repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+inline int MethodOptions::_internal_uninterpreted_option_size() const {
+  return _impl_.uninterpreted_option_.size();
+}
+inline int MethodOptions::uninterpreted_option_size() const {
+  return _internal_uninterpreted_option_size();
+}
+inline void MethodOptions::clear_uninterpreted_option() {
+  _impl_.uninterpreted_option_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* MethodOptions::mutable_uninterpreted_option(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.MethodOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >*
+MethodOptions::mutable_uninterpreted_option() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.MethodOptions.uninterpreted_option)
+  return &_impl_.uninterpreted_option_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& MethodOptions::_internal_uninterpreted_option(int index) const {
+  return _impl_.uninterpreted_option_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption& MethodOptions::uninterpreted_option(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.MethodOptions.uninterpreted_option)
+  return _internal_uninterpreted_option(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* MethodOptions::_internal_add_uninterpreted_option() {
+  return _impl_.uninterpreted_option_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* MethodOptions::add_uninterpreted_option() {
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption* _add = _internal_add_uninterpreted_option();
+  // @@protoc_insertion_point(field_add:google.protobuf.MethodOptions.uninterpreted_option)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >&
+MethodOptions::uninterpreted_option() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.MethodOptions.uninterpreted_option)
+  return _impl_.uninterpreted_option_;
+}
+
+// -------------------------------------------------------------------
+
+// UninterpretedOption_NamePart
+
+// required string name_part = 1;
+inline bool UninterpretedOption_NamePart::_internal_has_name_part() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool UninterpretedOption_NamePart::has_name_part() const {
+  return _internal_has_name_part();
+}
+inline void UninterpretedOption_NamePart::clear_name_part() {
+  _impl_.name_part_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& UninterpretedOption_NamePart::name_part() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UninterpretedOption.NamePart.name_part)
+  return _internal_name_part();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void UninterpretedOption_NamePart::set_name_part(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.name_part_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.NamePart.name_part)
+}
+inline std::string* UninterpretedOption_NamePart::mutable_name_part() {
+  std::string* _s = _internal_mutable_name_part();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.UninterpretedOption.NamePart.name_part)
+  return _s;
+}
+inline const std::string& UninterpretedOption_NamePart::_internal_name_part() const {
+  return _impl_.name_part_.Get();
+}
+inline void UninterpretedOption_NamePart::_internal_set_name_part(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.name_part_.Set(value, GetArenaForAllocation());
+}
+inline std::string* UninterpretedOption_NamePart::_internal_mutable_name_part() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.name_part_.Mutable(GetArenaForAllocation());
+}
+inline std::string* UninterpretedOption_NamePart::release_name_part() {
+  // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.NamePart.name_part)
+  if (!_internal_has_name_part()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.name_part_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_part_.IsDefault()) {
+    _impl_.name_part_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void UninterpretedOption_NamePart::set_allocated_name_part(std::string* name_part) {
+  if (name_part != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.name_part_.SetAllocated(name_part, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_part_.IsDefault()) {
+    _impl_.name_part_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.UninterpretedOption.NamePart.name_part)
+}
+
+// required bool is_extension = 2;
+inline bool UninterpretedOption_NamePart::_internal_has_is_extension() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool UninterpretedOption_NamePart::has_is_extension() const {
+  return _internal_has_is_extension();
+}
+inline void UninterpretedOption_NamePart::clear_is_extension() {
+  _impl_.is_extension_ = false;
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline bool UninterpretedOption_NamePart::_internal_is_extension() const {
+  return _impl_.is_extension_;
+}
+inline bool UninterpretedOption_NamePart::is_extension() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UninterpretedOption.NamePart.is_extension)
+  return _internal_is_extension();
+}
+inline void UninterpretedOption_NamePart::_internal_set_is_extension(bool value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.is_extension_ = value;
+}
+inline void UninterpretedOption_NamePart::set_is_extension(bool value) {
+  _internal_set_is_extension(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.NamePart.is_extension)
+}
+
+// -------------------------------------------------------------------
+
+// UninterpretedOption
+
+// repeated .google.protobuf.UninterpretedOption.NamePart name = 2;
+inline int UninterpretedOption::_internal_name_size() const {
+  return _impl_.name_.size();
+}
+inline int UninterpretedOption::name_size() const {
+  return _internal_name_size();
+}
+inline void UninterpretedOption::clear_name() {
+  _impl_.name_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart* UninterpretedOption::mutable_name(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.UninterpretedOption.name)
+  return _impl_.name_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart >*
+UninterpretedOption::mutable_name() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.UninterpretedOption.name)
+  return &_impl_.name_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart& UninterpretedOption::_internal_name(int index) const {
+  return _impl_.name_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart& UninterpretedOption::name(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UninterpretedOption.name)
+  return _internal_name(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart* UninterpretedOption::_internal_add_name() {
+  return _impl_.name_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart* UninterpretedOption::add_name() {
+  ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart* _add = _internal_add_name();
+  // @@protoc_insertion_point(field_add:google.protobuf.UninterpretedOption.name)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart >&
+UninterpretedOption::name() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.UninterpretedOption.name)
+  return _impl_.name_;
+}
+
+// optional string identifier_value = 3;
+inline bool UninterpretedOption::_internal_has_identifier_value() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool UninterpretedOption::has_identifier_value() const {
+  return _internal_has_identifier_value();
+}
+inline void UninterpretedOption::clear_identifier_value() {
+  _impl_.identifier_value_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& UninterpretedOption::identifier_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UninterpretedOption.identifier_value)
+  return _internal_identifier_value();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void UninterpretedOption::set_identifier_value(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.identifier_value_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.identifier_value)
+}
+inline std::string* UninterpretedOption::mutable_identifier_value() {
+  std::string* _s = _internal_mutable_identifier_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.UninterpretedOption.identifier_value)
+  return _s;
+}
+inline const std::string& UninterpretedOption::_internal_identifier_value() const {
+  return _impl_.identifier_value_.Get();
+}
+inline void UninterpretedOption::_internal_set_identifier_value(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.identifier_value_.Set(value, GetArenaForAllocation());
+}
+inline std::string* UninterpretedOption::_internal_mutable_identifier_value() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.identifier_value_.Mutable(GetArenaForAllocation());
+}
+inline std::string* UninterpretedOption::release_identifier_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.identifier_value)
+  if (!_internal_has_identifier_value()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.identifier_value_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.identifier_value_.IsDefault()) {
+    _impl_.identifier_value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void UninterpretedOption::set_allocated_identifier_value(std::string* identifier_value) {
+  if (identifier_value != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.identifier_value_.SetAllocated(identifier_value, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.identifier_value_.IsDefault()) {
+    _impl_.identifier_value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.UninterpretedOption.identifier_value)
+}
+
+// optional uint64 positive_int_value = 4;
+inline bool UninterpretedOption::_internal_has_positive_int_value() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+  return value;
+}
+inline bool UninterpretedOption::has_positive_int_value() const {
+  return _internal_has_positive_int_value();
+}
+inline void UninterpretedOption::clear_positive_int_value() {
+  _impl_.positive_int_value_ = uint64_t{0u};
+  _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline uint64_t UninterpretedOption::_internal_positive_int_value() const {
+  return _impl_.positive_int_value_;
+}
+inline uint64_t UninterpretedOption::positive_int_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UninterpretedOption.positive_int_value)
+  return _internal_positive_int_value();
+}
+inline void UninterpretedOption::_internal_set_positive_int_value(uint64_t value) {
+  _impl_._has_bits_[0] |= 0x00000008u;
+  _impl_.positive_int_value_ = value;
+}
+inline void UninterpretedOption::set_positive_int_value(uint64_t value) {
+  _internal_set_positive_int_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.positive_int_value)
+}
+
+// optional int64 negative_int_value = 5;
+inline bool UninterpretedOption::_internal_has_negative_int_value() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+  return value;
+}
+inline bool UninterpretedOption::has_negative_int_value() const {
+  return _internal_has_negative_int_value();
+}
+inline void UninterpretedOption::clear_negative_int_value() {
+  _impl_.negative_int_value_ = int64_t{0};
+  _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline int64_t UninterpretedOption::_internal_negative_int_value() const {
+  return _impl_.negative_int_value_;
+}
+inline int64_t UninterpretedOption::negative_int_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UninterpretedOption.negative_int_value)
+  return _internal_negative_int_value();
+}
+inline void UninterpretedOption::_internal_set_negative_int_value(int64_t value) {
+  _impl_._has_bits_[0] |= 0x00000010u;
+  _impl_.negative_int_value_ = value;
+}
+inline void UninterpretedOption::set_negative_int_value(int64_t value) {
+  _internal_set_negative_int_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.negative_int_value)
+}
+
+// optional double double_value = 6;
+inline bool UninterpretedOption::_internal_has_double_value() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000020u) != 0;
+  return value;
+}
+inline bool UninterpretedOption::has_double_value() const {
+  return _internal_has_double_value();
+}
+inline void UninterpretedOption::clear_double_value() {
+  _impl_.double_value_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000020u;
+}
+inline double UninterpretedOption::_internal_double_value() const {
+  return _impl_.double_value_;
+}
+inline double UninterpretedOption::double_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UninterpretedOption.double_value)
+  return _internal_double_value();
+}
+inline void UninterpretedOption::_internal_set_double_value(double value) {
+  _impl_._has_bits_[0] |= 0x00000020u;
+  _impl_.double_value_ = value;
+}
+inline void UninterpretedOption::set_double_value(double value) {
+  _internal_set_double_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.double_value)
+}
+
+// optional bytes string_value = 7;
+inline bool UninterpretedOption::_internal_has_string_value() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool UninterpretedOption::has_string_value() const {
+  return _internal_has_string_value();
+}
+inline void UninterpretedOption::clear_string_value() {
+  _impl_.string_value_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& UninterpretedOption::string_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UninterpretedOption.string_value)
+  return _internal_string_value();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void UninterpretedOption::set_string_value(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.string_value_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.string_value)
+}
+inline std::string* UninterpretedOption::mutable_string_value() {
+  std::string* _s = _internal_mutable_string_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.UninterpretedOption.string_value)
+  return _s;
+}
+inline const std::string& UninterpretedOption::_internal_string_value() const {
+  return _impl_.string_value_.Get();
+}
+inline void UninterpretedOption::_internal_set_string_value(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.string_value_.Set(value, GetArenaForAllocation());
+}
+inline std::string* UninterpretedOption::_internal_mutable_string_value() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  return _impl_.string_value_.Mutable(GetArenaForAllocation());
+}
+inline std::string* UninterpretedOption::release_string_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.string_value)
+  if (!_internal_has_string_value()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  auto* p = _impl_.string_value_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.string_value_.IsDefault()) {
+    _impl_.string_value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void UninterpretedOption::set_allocated_string_value(std::string* string_value) {
+  if (string_value != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.string_value_.SetAllocated(string_value, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.string_value_.IsDefault()) {
+    _impl_.string_value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.UninterpretedOption.string_value)
+}
+
+// optional string aggregate_value = 8;
+inline bool UninterpretedOption::_internal_has_aggregate_value() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool UninterpretedOption::has_aggregate_value() const {
+  return _internal_has_aggregate_value();
+}
+inline void UninterpretedOption::clear_aggregate_value() {
+  _impl_.aggregate_value_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline const std::string& UninterpretedOption::aggregate_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UninterpretedOption.aggregate_value)
+  return _internal_aggregate_value();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void UninterpretedOption::set_aggregate_value(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.aggregate_value_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.aggregate_value)
+}
+inline std::string* UninterpretedOption::mutable_aggregate_value() {
+  std::string* _s = _internal_mutable_aggregate_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.UninterpretedOption.aggregate_value)
+  return _s;
+}
+inline const std::string& UninterpretedOption::_internal_aggregate_value() const {
+  return _impl_.aggregate_value_.Get();
+}
+inline void UninterpretedOption::_internal_set_aggregate_value(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  _impl_.aggregate_value_.Set(value, GetArenaForAllocation());
+}
+inline std::string* UninterpretedOption::_internal_mutable_aggregate_value() {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  return _impl_.aggregate_value_.Mutable(GetArenaForAllocation());
+}
+inline std::string* UninterpretedOption::release_aggregate_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.aggregate_value)
+  if (!_internal_has_aggregate_value()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000004u;
+  auto* p = _impl_.aggregate_value_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.aggregate_value_.IsDefault()) {
+    _impl_.aggregate_value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void UninterpretedOption::set_allocated_aggregate_value(std::string* aggregate_value) {
+  if (aggregate_value != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000004u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000004u;
+  }
+  _impl_.aggregate_value_.SetAllocated(aggregate_value, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.aggregate_value_.IsDefault()) {
+    _impl_.aggregate_value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.UninterpretedOption.aggregate_value)
+}
+
+// -------------------------------------------------------------------
+
+// SourceCodeInfo_Location
+
+// repeated int32 path = 1 [packed = true];
+inline int SourceCodeInfo_Location::_internal_path_size() const {
+  return _impl_.path_.size();
+}
+inline int SourceCodeInfo_Location::path_size() const {
+  return _internal_path_size();
+}
+inline void SourceCodeInfo_Location::clear_path() {
+  _impl_.path_.Clear();
+}
+inline int32_t SourceCodeInfo_Location::_internal_path(int index) const {
+  return _impl_.path_.Get(index);
+}
+inline int32_t SourceCodeInfo_Location::path(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.SourceCodeInfo.Location.path)
+  return _internal_path(index);
+}
+inline void SourceCodeInfo_Location::set_path(int index, int32_t value) {
+  _impl_.path_.Set(index, value);
+  // @@protoc_insertion_point(field_set:google.protobuf.SourceCodeInfo.Location.path)
+}
+inline void SourceCodeInfo_Location::_internal_add_path(int32_t value) {
+  _impl_.path_.Add(value);
+}
+inline void SourceCodeInfo_Location::add_path(int32_t value) {
+  _internal_add_path(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.SourceCodeInfo.Location.path)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+SourceCodeInfo_Location::_internal_path() const {
+  return _impl_.path_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+SourceCodeInfo_Location::path() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.SourceCodeInfo.Location.path)
+  return _internal_path();
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+SourceCodeInfo_Location::_internal_mutable_path() {
+  return &_impl_.path_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+SourceCodeInfo_Location::mutable_path() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.SourceCodeInfo.Location.path)
+  return _internal_mutable_path();
+}
+
+// repeated int32 span = 2 [packed = true];
+inline int SourceCodeInfo_Location::_internal_span_size() const {
+  return _impl_.span_.size();
+}
+inline int SourceCodeInfo_Location::span_size() const {
+  return _internal_span_size();
+}
+inline void SourceCodeInfo_Location::clear_span() {
+  _impl_.span_.Clear();
+}
+inline int32_t SourceCodeInfo_Location::_internal_span(int index) const {
+  return _impl_.span_.Get(index);
+}
+inline int32_t SourceCodeInfo_Location::span(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.SourceCodeInfo.Location.span)
+  return _internal_span(index);
+}
+inline void SourceCodeInfo_Location::set_span(int index, int32_t value) {
+  _impl_.span_.Set(index, value);
+  // @@protoc_insertion_point(field_set:google.protobuf.SourceCodeInfo.Location.span)
+}
+inline void SourceCodeInfo_Location::_internal_add_span(int32_t value) {
+  _impl_.span_.Add(value);
+}
+inline void SourceCodeInfo_Location::add_span(int32_t value) {
+  _internal_add_span(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.SourceCodeInfo.Location.span)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+SourceCodeInfo_Location::_internal_span() const {
+  return _impl_.span_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+SourceCodeInfo_Location::span() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.SourceCodeInfo.Location.span)
+  return _internal_span();
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+SourceCodeInfo_Location::_internal_mutable_span() {
+  return &_impl_.span_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+SourceCodeInfo_Location::mutable_span() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.SourceCodeInfo.Location.span)
+  return _internal_mutable_span();
+}
+
+// optional string leading_comments = 3;
+inline bool SourceCodeInfo_Location::_internal_has_leading_comments() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool SourceCodeInfo_Location::has_leading_comments() const {
+  return _internal_has_leading_comments();
+}
+inline void SourceCodeInfo_Location::clear_leading_comments() {
+  _impl_.leading_comments_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& SourceCodeInfo_Location::leading_comments() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.SourceCodeInfo.Location.leading_comments)
+  return _internal_leading_comments();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void SourceCodeInfo_Location::set_leading_comments(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.leading_comments_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.SourceCodeInfo.Location.leading_comments)
+}
+inline std::string* SourceCodeInfo_Location::mutable_leading_comments() {
+  std::string* _s = _internal_mutable_leading_comments();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.SourceCodeInfo.Location.leading_comments)
+  return _s;
+}
+inline const std::string& SourceCodeInfo_Location::_internal_leading_comments() const {
+  return _impl_.leading_comments_.Get();
+}
+inline void SourceCodeInfo_Location::_internal_set_leading_comments(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.leading_comments_.Set(value, GetArenaForAllocation());
+}
+inline std::string* SourceCodeInfo_Location::_internal_mutable_leading_comments() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.leading_comments_.Mutable(GetArenaForAllocation());
+}
+inline std::string* SourceCodeInfo_Location::release_leading_comments() {
+  // @@protoc_insertion_point(field_release:google.protobuf.SourceCodeInfo.Location.leading_comments)
+  if (!_internal_has_leading_comments()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.leading_comments_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.leading_comments_.IsDefault()) {
+    _impl_.leading_comments_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void SourceCodeInfo_Location::set_allocated_leading_comments(std::string* leading_comments) {
+  if (leading_comments != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.leading_comments_.SetAllocated(leading_comments, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.leading_comments_.IsDefault()) {
+    _impl_.leading_comments_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.SourceCodeInfo.Location.leading_comments)
+}
+
+// optional string trailing_comments = 4;
+inline bool SourceCodeInfo_Location::_internal_has_trailing_comments() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool SourceCodeInfo_Location::has_trailing_comments() const {
+  return _internal_has_trailing_comments();
+}
+inline void SourceCodeInfo_Location::clear_trailing_comments() {
+  _impl_.trailing_comments_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& SourceCodeInfo_Location::trailing_comments() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.SourceCodeInfo.Location.trailing_comments)
+  return _internal_trailing_comments();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void SourceCodeInfo_Location::set_trailing_comments(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.trailing_comments_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.SourceCodeInfo.Location.trailing_comments)
+}
+inline std::string* SourceCodeInfo_Location::mutable_trailing_comments() {
+  std::string* _s = _internal_mutable_trailing_comments();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.SourceCodeInfo.Location.trailing_comments)
+  return _s;
+}
+inline const std::string& SourceCodeInfo_Location::_internal_trailing_comments() const {
+  return _impl_.trailing_comments_.Get();
+}
+inline void SourceCodeInfo_Location::_internal_set_trailing_comments(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.trailing_comments_.Set(value, GetArenaForAllocation());
+}
+inline std::string* SourceCodeInfo_Location::_internal_mutable_trailing_comments() {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  return _impl_.trailing_comments_.Mutable(GetArenaForAllocation());
+}
+inline std::string* SourceCodeInfo_Location::release_trailing_comments() {
+  // @@protoc_insertion_point(field_release:google.protobuf.SourceCodeInfo.Location.trailing_comments)
+  if (!_internal_has_trailing_comments()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000002u;
+  auto* p = _impl_.trailing_comments_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.trailing_comments_.IsDefault()) {
+    _impl_.trailing_comments_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void SourceCodeInfo_Location::set_allocated_trailing_comments(std::string* trailing_comments) {
+  if (trailing_comments != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000002u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000002u;
+  }
+  _impl_.trailing_comments_.SetAllocated(trailing_comments, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.trailing_comments_.IsDefault()) {
+    _impl_.trailing_comments_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.SourceCodeInfo.Location.trailing_comments)
+}
+
+// repeated string leading_detached_comments = 6;
+inline int SourceCodeInfo_Location::_internal_leading_detached_comments_size() const {
+  return _impl_.leading_detached_comments_.size();
+}
+inline int SourceCodeInfo_Location::leading_detached_comments_size() const {
+  return _internal_leading_detached_comments_size();
+}
+inline void SourceCodeInfo_Location::clear_leading_detached_comments() {
+  _impl_.leading_detached_comments_.Clear();
+}
+inline std::string* SourceCodeInfo_Location::add_leading_detached_comments() {
+  std::string* _s = _internal_add_leading_detached_comments();
+  // @@protoc_insertion_point(field_add_mutable:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+  return _s;
+}
+inline const std::string& SourceCodeInfo_Location::_internal_leading_detached_comments(int index) const {
+  return _impl_.leading_detached_comments_.Get(index);
+}
+inline const std::string& SourceCodeInfo_Location::leading_detached_comments(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+  return _internal_leading_detached_comments(index);
+}
+inline std::string* SourceCodeInfo_Location::mutable_leading_detached_comments(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+  return _impl_.leading_detached_comments_.Mutable(index);
+}
+inline void SourceCodeInfo_Location::set_leading_detached_comments(int index, const std::string& value) {
+  _impl_.leading_detached_comments_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+}
+inline void SourceCodeInfo_Location::set_leading_detached_comments(int index, std::string&& value) {
+  _impl_.leading_detached_comments_.Mutable(index)->assign(std::move(value));
+  // @@protoc_insertion_point(field_set:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+}
+inline void SourceCodeInfo_Location::set_leading_detached_comments(int index, const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.leading_detached_comments_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set_char:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+}
+inline void SourceCodeInfo_Location::set_leading_detached_comments(int index, const char* value, size_t size) {
+  _impl_.leading_detached_comments_.Mutable(index)->assign(
+    reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_set_pointer:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+}
+inline std::string* SourceCodeInfo_Location::_internal_add_leading_detached_comments() {
+  return _impl_.leading_detached_comments_.Add();
+}
+inline void SourceCodeInfo_Location::add_leading_detached_comments(const std::string& value) {
+  _impl_.leading_detached_comments_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+}
+inline void SourceCodeInfo_Location::add_leading_detached_comments(std::string&& value) {
+  _impl_.leading_detached_comments_.Add(std::move(value));
+  // @@protoc_insertion_point(field_add:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+}
+inline void SourceCodeInfo_Location::add_leading_detached_comments(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.leading_detached_comments_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add_char:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+}
+inline void SourceCodeInfo_Location::add_leading_detached_comments(const char* value, size_t size) {
+  _impl_.leading_detached_comments_.Add()->assign(reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_add_pointer:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&
+SourceCodeInfo_Location::leading_detached_comments() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+  return _impl_.leading_detached_comments_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
+SourceCodeInfo_Location::mutable_leading_detached_comments() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.SourceCodeInfo.Location.leading_detached_comments)
+  return &_impl_.leading_detached_comments_;
+}
+
+// -------------------------------------------------------------------
+
+// SourceCodeInfo
+
+// repeated .google.protobuf.SourceCodeInfo.Location location = 1;
+inline int SourceCodeInfo::_internal_location_size() const {
+  return _impl_.location_.size();
+}
+inline int SourceCodeInfo::location_size() const {
+  return _internal_location_size();
+}
+inline void SourceCodeInfo::clear_location() {
+  _impl_.location_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location* SourceCodeInfo::mutable_location(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.SourceCodeInfo.location)
+  return _impl_.location_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location >*
+SourceCodeInfo::mutable_location() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.SourceCodeInfo.location)
+  return &_impl_.location_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location& SourceCodeInfo::_internal_location(int index) const {
+  return _impl_.location_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location& SourceCodeInfo::location(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.SourceCodeInfo.location)
+  return _internal_location(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location* SourceCodeInfo::_internal_add_location() {
+  return _impl_.location_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location* SourceCodeInfo::add_location() {
+  ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location* _add = _internal_add_location();
+  // @@protoc_insertion_point(field_add:google.protobuf.SourceCodeInfo.location)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location >&
+SourceCodeInfo::location() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.SourceCodeInfo.location)
+  return _impl_.location_;
+}
+
+// -------------------------------------------------------------------
+
+// GeneratedCodeInfo_Annotation
+
+// repeated int32 path = 1 [packed = true];
+inline int GeneratedCodeInfo_Annotation::_internal_path_size() const {
+  return _impl_.path_.size();
+}
+inline int GeneratedCodeInfo_Annotation::path_size() const {
+  return _internal_path_size();
+}
+inline void GeneratedCodeInfo_Annotation::clear_path() {
+  _impl_.path_.Clear();
+}
+inline int32_t GeneratedCodeInfo_Annotation::_internal_path(int index) const {
+  return _impl_.path_.Get(index);
+}
+inline int32_t GeneratedCodeInfo_Annotation::path(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.path)
+  return _internal_path(index);
+}
+inline void GeneratedCodeInfo_Annotation::set_path(int index, int32_t value) {
+  _impl_.path_.Set(index, value);
+  // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.path)
+}
+inline void GeneratedCodeInfo_Annotation::_internal_add_path(int32_t value) {
+  _impl_.path_.Add(value);
+}
+inline void GeneratedCodeInfo_Annotation::add_path(int32_t value) {
+  _internal_add_path(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.GeneratedCodeInfo.Annotation.path)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+GeneratedCodeInfo_Annotation::_internal_path() const {
+  return _impl_.path_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+GeneratedCodeInfo_Annotation::path() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.GeneratedCodeInfo.Annotation.path)
+  return _internal_path();
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+GeneratedCodeInfo_Annotation::_internal_mutable_path() {
+  return &_impl_.path_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+GeneratedCodeInfo_Annotation::mutable_path() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.GeneratedCodeInfo.Annotation.path)
+  return _internal_mutable_path();
+}
+
+// optional string source_file = 2;
+inline bool GeneratedCodeInfo_Annotation::_internal_has_source_file() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  return value;
+}
+inline bool GeneratedCodeInfo_Annotation::has_source_file() const {
+  return _internal_has_source_file();
+}
+inline void GeneratedCodeInfo_Annotation::clear_source_file() {
+  _impl_.source_file_.ClearToEmpty();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& GeneratedCodeInfo_Annotation::source_file() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+  return _internal_source_file();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void GeneratedCodeInfo_Annotation::set_source_file(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.source_file_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+}
+inline std::string* GeneratedCodeInfo_Annotation::mutable_source_file() {
+  std::string* _s = _internal_mutable_source_file();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+  return _s;
+}
+inline const std::string& GeneratedCodeInfo_Annotation::_internal_source_file() const {
+  return _impl_.source_file_.Get();
+}
+inline void GeneratedCodeInfo_Annotation::_internal_set_source_file(const std::string& value) {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  _impl_.source_file_.Set(value, GetArenaForAllocation());
+}
+inline std::string* GeneratedCodeInfo_Annotation::_internal_mutable_source_file() {
+  _impl_._has_bits_[0] |= 0x00000001u;
+  return _impl_.source_file_.Mutable(GetArenaForAllocation());
+}
+inline std::string* GeneratedCodeInfo_Annotation::release_source_file() {
+  // @@protoc_insertion_point(field_release:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+  if (!_internal_has_source_file()) {
+    return nullptr;
+  }
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  auto* p = _impl_.source_file_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.source_file_.IsDefault()) {
+    _impl_.source_file_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  return p;
+}
+inline void GeneratedCodeInfo_Annotation::set_allocated_source_file(std::string* source_file) {
+  if (source_file != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  _impl_.source_file_.SetAllocated(source_file, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.source_file_.IsDefault()) {
+    _impl_.source_file_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+}
+
+// optional int32 begin = 3;
+inline bool GeneratedCodeInfo_Annotation::_internal_has_begin() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+  return value;
+}
+inline bool GeneratedCodeInfo_Annotation::has_begin() const {
+  return _internal_has_begin();
+}
+inline void GeneratedCodeInfo_Annotation::clear_begin() {
+  _impl_.begin_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline int32_t GeneratedCodeInfo_Annotation::_internal_begin() const {
+  return _impl_.begin_;
+}
+inline int32_t GeneratedCodeInfo_Annotation::begin() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.begin)
+  return _internal_begin();
+}
+inline void GeneratedCodeInfo_Annotation::_internal_set_begin(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000002u;
+  _impl_.begin_ = value;
+}
+inline void GeneratedCodeInfo_Annotation::set_begin(int32_t value) {
+  _internal_set_begin(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.begin)
+}
+
+// optional int32 end = 4;
+inline bool GeneratedCodeInfo_Annotation::_internal_has_end() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+  return value;
+}
+inline bool GeneratedCodeInfo_Annotation::has_end() const {
+  return _internal_has_end();
+}
+inline void GeneratedCodeInfo_Annotation::clear_end() {
+  _impl_.end_ = 0;
+  _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline int32_t GeneratedCodeInfo_Annotation::_internal_end() const {
+  return _impl_.end_;
+}
+inline int32_t GeneratedCodeInfo_Annotation::end() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.end)
+  return _internal_end();
+}
+inline void GeneratedCodeInfo_Annotation::_internal_set_end(int32_t value) {
+  _impl_._has_bits_[0] |= 0x00000004u;
+  _impl_.end_ = value;
+}
+inline void GeneratedCodeInfo_Annotation::set_end(int32_t value) {
+  _internal_set_end(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.end)
+}
+
+// -------------------------------------------------------------------
+
+// GeneratedCodeInfo
+
+// repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+inline int GeneratedCodeInfo::_internal_annotation_size() const {
+  return _impl_.annotation_.size();
+}
+inline int GeneratedCodeInfo::annotation_size() const {
+  return _internal_annotation_size();
+}
+inline void GeneratedCodeInfo::clear_annotation() {
+  _impl_.annotation_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation* GeneratedCodeInfo::mutable_annotation(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.GeneratedCodeInfo.annotation)
+  return _impl_.annotation_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation >*
+GeneratedCodeInfo::mutable_annotation() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.GeneratedCodeInfo.annotation)
+  return &_impl_.annotation_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation& GeneratedCodeInfo::_internal_annotation(int index) const {
+  return _impl_.annotation_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation& GeneratedCodeInfo::annotation(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.annotation)
+  return _internal_annotation(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation* GeneratedCodeInfo::_internal_add_annotation() {
+  return _impl_.annotation_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation* GeneratedCodeInfo::add_annotation() {
+  ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation* _add = _internal_add_annotation();
+  // @@protoc_insertion_point(field_add:google.protobuf.GeneratedCodeInfo.annotation)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation >&
+GeneratedCodeInfo::annotation() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.GeneratedCodeInfo.annotation)
+  return _impl_.annotation_;
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+PROTOBUF_NAMESPACE_OPEN
+
+template <> struct is_proto_enum< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type>() {
+  return ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type_descriptor();
+}
+template <> struct is_proto_enum< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label>() {
+  return ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label_descriptor();
+}
+template <> struct is_proto_enum< ::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode>() {
+  return ::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode_descriptor();
+}
+template <> struct is_proto_enum< ::PROTOBUF_NAMESPACE_ID::FieldOptions_CType> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::PROTOBUF_NAMESPACE_ID::FieldOptions_CType>() {
+  return ::PROTOBUF_NAMESPACE_ID::FieldOptions_CType_descriptor();
+}
+template <> struct is_proto_enum< ::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType>() {
+  return ::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType_descriptor();
+}
+template <> struct is_proto_enum< ::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel>() {
+  return ::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel_descriptor();
+}
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fdescriptor_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/descriptor_database.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/descriptor_database.h
new file mode 100644
index 0000000..f4f06bb
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/descriptor_database.h
@@ -0,0 +1,398 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Interface for manipulating databases of descriptors.
+
+#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__
+#define GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__
+
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/descriptor.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+
+// Defined in this file.
+class DescriptorDatabase;
+class SimpleDescriptorDatabase;
+class EncodedDescriptorDatabase;
+class DescriptorPoolDatabase;
+class MergedDescriptorDatabase;
+
+// Abstract interface for a database of descriptors.
+//
+// This is useful if you want to create a DescriptorPool which loads
+// descriptors on-demand from some sort of large database.  If the database
+// is large, it may be inefficient to enumerate every .proto file inside it
+// calling DescriptorPool::BuildFile() for each one.  Instead, a DescriptorPool
+// can be created which wraps a DescriptorDatabase and only builds particular
+// descriptors when they are needed.
+class PROTOBUF_EXPORT DescriptorDatabase {
+ public:
+  inline DescriptorDatabase() {}
+  virtual ~DescriptorDatabase();
+
+  // Find a file by file name.  Fills in in *output and returns true if found.
+  // Otherwise, returns false, leaving the contents of *output undefined.
+  virtual bool FindFileByName(const std::string& filename,
+                              FileDescriptorProto* output) = 0;
+
+  // Find the file that declares the given fully-qualified symbol name.
+  // If found, fills in *output and returns true, otherwise returns false
+  // and leaves *output undefined.
+  virtual bool FindFileContainingSymbol(const std::string& symbol_name,
+                                        FileDescriptorProto* output) = 0;
+
+  // Find the file which defines an extension extending the given message type
+  // with the given field number.  If found, fills in *output and returns true,
+  // otherwise returns false and leaves *output undefined.  containing_type
+  // must be a fully-qualified type name.
+  virtual bool FindFileContainingExtension(const std::string& containing_type,
+                                           int field_number,
+                                           FileDescriptorProto* output) = 0;
+
+  // Finds the tag numbers used by all known extensions of
+  // extendee_type, and appends them to output in an undefined
+  // order. This method is best-effort: it's not guaranteed that the
+  // database will find all extensions, and it's not guaranteed that
+  // FindFileContainingExtension will return true on all of the found
+  // numbers. Returns true if the search was successful, otherwise
+  // returns false and leaves output unchanged.
+  //
+  // This method has a default implementation that always returns
+  // false.
+  virtual bool FindAllExtensionNumbers(const std::string& /* extendee_type */,
+                                       std::vector<int>* /* output */) {
+    return false;
+  }
+
+
+  // Finds the file names and appends them to the output in an
+  // undefined order. This method is best-effort: it's not guaranteed that the
+  // database will find all files. Returns true if the database supports
+  // searching all file names, otherwise returns false and leaves output
+  // unchanged.
+  //
+  // This method has a default implementation that always returns
+  // false.
+  virtual bool FindAllFileNames(std::vector<std::string>* /*output*/) {
+    return false;
+  }
+
+  // Finds the package names and appends them to the output in an
+  // undefined order. This method is best-effort: it's not guaranteed that the
+  // database will find all packages. Returns true if the database supports
+  // searching all package names, otherwise returns false and leaves output
+  // unchanged.
+  bool FindAllPackageNames(std::vector<std::string>* output);
+
+  // Finds the message names and appends them to the output in an
+  // undefined order. This method is best-effort: it's not guaranteed that the
+  // database will find all messages. Returns true if the database supports
+  // searching all message names, otherwise returns false and leaves output
+  // unchanged.
+  bool FindAllMessageNames(std::vector<std::string>* output);
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorDatabase);
+};
+
+// A DescriptorDatabase into which you can insert files manually.
+//
+// FindFileContainingSymbol() is fully-implemented.  When you add a file, its
+// symbols will be indexed for this purpose.  Note that the implementation
+// may return false positives, but only if it isn't possible for the symbol
+// to be defined in any other file.  In particular, if a file defines a symbol
+// "Foo", then searching for "Foo.[anything]" will match that file.  This way,
+// the database does not need to aggressively index all children of a symbol.
+//
+// FindFileContainingExtension() is mostly-implemented.  It works if and only
+// if the original FieldDescriptorProto defining the extension has a
+// fully-qualified type name in its "extendee" field (i.e. starts with a '.').
+// If the extendee is a relative name, SimpleDescriptorDatabase will not
+// attempt to resolve the type, so it will not know what type the extension is
+// extending.  Therefore, calling FindFileContainingExtension() with the
+// extension's containing type will never actually find that extension.  Note
+// that this is an unlikely problem, as all FileDescriptorProtos created by the
+// protocol compiler (as well as ones created by calling
+// FileDescriptor::CopyTo()) will always use fully-qualified names for all
+// types.  You only need to worry if you are constructing FileDescriptorProtos
+// yourself, or are calling compiler::Parser directly.
+class PROTOBUF_EXPORT SimpleDescriptorDatabase : public DescriptorDatabase {
+ public:
+  SimpleDescriptorDatabase();
+  ~SimpleDescriptorDatabase() override;
+
+  // Adds the FileDescriptorProto to the database, making a copy.  The object
+  // can be deleted after Add() returns.  Returns false if the file conflicted
+  // with a file already in the database, in which case an error will have
+  // been written to GOOGLE_LOG(ERROR).
+  bool Add(const FileDescriptorProto& file);
+
+  // Adds the FileDescriptorProto to the database and takes ownership of it.
+  bool AddAndOwn(const FileDescriptorProto* file);
+
+  // implements DescriptorDatabase -----------------------------------
+  bool FindFileByName(const std::string& filename,
+                      FileDescriptorProto* output) override;
+  bool FindFileContainingSymbol(const std::string& symbol_name,
+                                FileDescriptorProto* output) override;
+  bool FindFileContainingExtension(const std::string& containing_type,
+                                   int field_number,
+                                   FileDescriptorProto* output) override;
+  bool FindAllExtensionNumbers(const std::string& extendee_type,
+                               std::vector<int>* output) override;
+
+  bool FindAllFileNames(std::vector<std::string>* output) override;
+
+ private:
+  // An index mapping file names, symbol names, and extension numbers to
+  // some sort of values.
+  template <typename Value>
+  class DescriptorIndex {
+   public:
+    // Helpers to recursively add particular descriptors and all their contents
+    // to the index.
+    bool AddFile(const FileDescriptorProto& file, Value value);
+    bool AddSymbol(const std::string& name, Value value);
+    bool AddNestedExtensions(const std::string& filename,
+                             const DescriptorProto& message_type, Value value);
+    bool AddExtension(const std::string& filename,
+                      const FieldDescriptorProto& field, Value value);
+
+    Value FindFile(const std::string& filename);
+    Value FindSymbol(const std::string& name);
+    Value FindExtension(const std::string& containing_type, int field_number);
+    bool FindAllExtensionNumbers(const std::string& containing_type,
+                                 std::vector<int>* output);
+    void FindAllFileNames(std::vector<std::string>* output);
+
+   private:
+    std::map<std::string, Value> by_name_;
+    std::map<std::string, Value> by_symbol_;
+    std::map<std::pair<std::string, int>, Value> by_extension_;
+
+    // Invariant:  The by_symbol_ map does not contain any symbols which are
+    // prefixes of other symbols in the map.  For example, "foo.bar" is a
+    // prefix of "foo.bar.baz" (but is not a prefix of "foo.barbaz").
+    //
+    // This invariant is important because it means that given a symbol name,
+    // we can find a key in the map which is a prefix of the symbol in O(lg n)
+    // time, and we know that there is at most one such key.
+    //
+    // The prefix lookup algorithm works like so:
+    // 1) Find the last key in the map which is less than or equal to the
+    //    search key.
+    // 2) If the found key is a prefix of the search key, then return it.
+    //    Otherwise, there is no match.
+    //
+    // I am sure this algorithm has been described elsewhere, but since I
+    // wasn't able to find it quickly I will instead prove that it works
+    // myself.  The key to the algorithm is that if a match exists, step (1)
+    // will find it.  Proof:
+    // 1) Define the "search key" to be the key we are looking for, the "found
+    //    key" to be the key found in step (1), and the "match key" to be the
+    //    key which actually matches the search key (i.e. the key we're trying
+    //    to find).
+    // 2) The found key must be less than or equal to the search key by
+    //    definition.
+    // 3) The match key must also be less than or equal to the search key
+    //    (because it is a prefix).
+    // 4) The match key cannot be greater than the found key, because if it
+    //    were, then step (1) of the algorithm would have returned the match
+    //    key instead (since it finds the *greatest* key which is less than or
+    //    equal to the search key).
+    // 5) Therefore, the found key must be between the match key and the search
+    //    key, inclusive.
+    // 6) Since the search key must be a sub-symbol of the match key, if it is
+    //    not equal to the match key, then search_key[match_key.size()] must
+    //    be '.'.
+    // 7) Since '.' sorts before any other character that is valid in a symbol
+    //    name, then if the found key is not equal to the match key, then
+    //    found_key[match_key.size()] must also be '.', because any other value
+    //    would make it sort after the search key.
+    // 8) Therefore, if the found key is not equal to the match key, then the
+    //    found key must be a sub-symbol of the match key.  However, this would
+    //    contradict our map invariant which says that no symbol in the map is
+    //    a sub-symbol of any other.
+    // 9) Therefore, the found key must match the match key.
+    //
+    // The above proof assumes the match key exists.  In the case that the
+    // match key does not exist, then step (1) will return some other symbol.
+    // That symbol cannot be a super-symbol of the search key since if it were,
+    // then it would be a match, and we're assuming the match key doesn't exist.
+    // Therefore, step 2 will correctly return no match.
+  };
+
+  DescriptorIndex<const FileDescriptorProto*> index_;
+  std::vector<std::unique_ptr<const FileDescriptorProto>> files_to_delete_;
+
+  // If file is non-nullptr, copy it into *output and return true, otherwise
+  // return false.
+  bool MaybeCopy(const FileDescriptorProto* file, FileDescriptorProto* output);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SimpleDescriptorDatabase);
+};
+
+// Very similar to SimpleDescriptorDatabase, but stores all the descriptors
+// as raw bytes and generally tries to use as little memory as possible.
+//
+// The same caveats regarding FindFileContainingExtension() apply as with
+// SimpleDescriptorDatabase.
+class PROTOBUF_EXPORT EncodedDescriptorDatabase : public DescriptorDatabase {
+ public:
+  EncodedDescriptorDatabase();
+  ~EncodedDescriptorDatabase() override;
+
+  // Adds the FileDescriptorProto to the database.  The descriptor is provided
+  // in encoded form.  The database does not make a copy of the bytes, nor
+  // does it take ownership; it's up to the caller to make sure the bytes
+  // remain valid for the life of the database.  Returns false and logs an error
+  // if the bytes are not a valid FileDescriptorProto or if the file conflicted
+  // with a file already in the database.
+  bool Add(const void* encoded_file_descriptor, int size);
+
+  // Like Add(), but makes a copy of the data, so that the caller does not
+  // need to keep it around.
+  bool AddCopy(const void* encoded_file_descriptor, int size);
+
+  // Like FindFileContainingSymbol but returns only the name of the file.
+  bool FindNameOfFileContainingSymbol(const std::string& symbol_name,
+                                      std::string* output);
+
+  // implements DescriptorDatabase -----------------------------------
+  bool FindFileByName(const std::string& filename,
+                      FileDescriptorProto* output) override;
+  bool FindFileContainingSymbol(const std::string& symbol_name,
+                                FileDescriptorProto* output) override;
+  bool FindFileContainingExtension(const std::string& containing_type,
+                                   int field_number,
+                                   FileDescriptorProto* output) override;
+  bool FindAllExtensionNumbers(const std::string& extendee_type,
+                               std::vector<int>* output) override;
+  bool FindAllFileNames(std::vector<std::string>* output) override;
+
+ private:
+  class DescriptorIndex;
+  // Keep DescriptorIndex by pointer to hide the implementation to keep a
+  // cleaner header.
+  std::unique_ptr<DescriptorIndex> index_;
+  std::vector<void*> files_to_delete_;
+
+  // If encoded_file.first is non-nullptr, parse the data into *output and
+  // return true, otherwise return false.
+  bool MaybeParse(std::pair<const void*, int> encoded_file,
+                  FileDescriptorProto* output);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EncodedDescriptorDatabase);
+};
+
+// A DescriptorDatabase that fetches files from a given pool.
+class PROTOBUF_EXPORT DescriptorPoolDatabase : public DescriptorDatabase {
+ public:
+  explicit DescriptorPoolDatabase(const DescriptorPool& pool);
+  ~DescriptorPoolDatabase() override;
+
+  // implements DescriptorDatabase -----------------------------------
+  bool FindFileByName(const std::string& filename,
+                      FileDescriptorProto* output) override;
+  bool FindFileContainingSymbol(const std::string& symbol_name,
+                                FileDescriptorProto* output) override;
+  bool FindFileContainingExtension(const std::string& containing_type,
+                                   int field_number,
+                                   FileDescriptorProto* output) override;
+  bool FindAllExtensionNumbers(const std::string& extendee_type,
+                               std::vector<int>* output) override;
+
+ private:
+  const DescriptorPool& pool_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorPoolDatabase);
+};
+
+// A DescriptorDatabase that wraps two or more others.  It first searches the
+// first database and, if that fails, tries the second, and so on.
+class PROTOBUF_EXPORT MergedDescriptorDatabase : public DescriptorDatabase {
+ public:
+  // Merge just two databases.  The sources remain property of the caller.
+  MergedDescriptorDatabase(DescriptorDatabase* source1,
+                           DescriptorDatabase* source2);
+  // Merge more than two databases.  The sources remain property of the caller.
+  // The vector may be deleted after the constructor returns but the
+  // DescriptorDatabases need to stick around.
+  explicit MergedDescriptorDatabase(
+      const std::vector<DescriptorDatabase*>& sources);
+  ~MergedDescriptorDatabase() override;
+
+  // implements DescriptorDatabase -----------------------------------
+  bool FindFileByName(const std::string& filename,
+                      FileDescriptorProto* output) override;
+  bool FindFileContainingSymbol(const std::string& symbol_name,
+                                FileDescriptorProto* output) override;
+  bool FindFileContainingExtension(const std::string& containing_type,
+                                   int field_number,
+                                   FileDescriptorProto* output) override;
+  // Merges the results of calling all databases. Returns true iff any
+  // of the databases returned true.
+  bool FindAllExtensionNumbers(const std::string& extendee_type,
+                               std::vector<int>* output) override;
+
+
+  // This function is best-effort. Returns true if at least one underlying
+  // DescriptorDatabase returns true.
+  bool FindAllFileNames(std::vector<std::string>* output) override;
+
+ private:
+  std::vector<DescriptorDatabase*> sources_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MergedDescriptorDatabase);
+};
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/duration.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/duration.pb.h
new file mode 100644
index 0000000..a5272c8
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/duration.pb.h
@@ -0,0 +1,278 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/duration.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fduration_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fduration_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/unknown_field_set.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fduration_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2fduration_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2fduration_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class Duration;
+struct DurationDefaultTypeInternal;
+PROTOBUF_EXPORT extern DurationDefaultTypeInternal _Duration_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Duration* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Duration>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class PROTOBUF_EXPORT Duration final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Duration) */ {
+ public:
+  inline Duration() : Duration(nullptr) {}
+  ~Duration() override;
+  explicit PROTOBUF_CONSTEXPR Duration(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Duration(const Duration& from);
+  Duration(Duration&& from) noexcept
+    : Duration() {
+    *this = ::std::move(from);
+  }
+
+  inline Duration& operator=(const Duration& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Duration& operator=(Duration&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Duration& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Duration* internal_default_instance() {
+    return reinterpret_cast<const Duration*>(
+               &_Duration_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(Duration& a, Duration& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Duration* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Duration* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Duration* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Duration>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Duration& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Duration& from) {
+    Duration::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Duration* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Duration";
+  }
+  protected:
+  explicit Duration(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kSecondsFieldNumber = 1,
+    kNanosFieldNumber = 2,
+  };
+  // int64 seconds = 1;
+  void clear_seconds();
+  int64_t seconds() const;
+  void set_seconds(int64_t value);
+  private:
+  int64_t _internal_seconds() const;
+  void _internal_set_seconds(int64_t value);
+  public:
+
+  // int32 nanos = 2;
+  void clear_nanos();
+  int32_t nanos() const;
+  void set_nanos(int32_t value);
+  private:
+  int32_t _internal_nanos() const;
+  void _internal_set_nanos(int32_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Duration)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    int64_t seconds_;
+    int32_t nanos_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fduration_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// Duration
+
+// int64 seconds = 1;
+inline void Duration::clear_seconds() {
+  _impl_.seconds_ = int64_t{0};
+}
+inline int64_t Duration::_internal_seconds() const {
+  return _impl_.seconds_;
+}
+inline int64_t Duration::seconds() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Duration.seconds)
+  return _internal_seconds();
+}
+inline void Duration::_internal_set_seconds(int64_t value) {
+  
+  _impl_.seconds_ = value;
+}
+inline void Duration::set_seconds(int64_t value) {
+  _internal_set_seconds(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Duration.seconds)
+}
+
+// int32 nanos = 2;
+inline void Duration::clear_nanos() {
+  _impl_.nanos_ = 0;
+}
+inline int32_t Duration::_internal_nanos() const {
+  return _impl_.nanos_;
+}
+inline int32_t Duration::nanos() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Duration.nanos)
+  return _internal_nanos();
+}
+inline void Duration::_internal_set_nanos(int32_t value) {
+  
+  _impl_.nanos_ = value;
+}
+inline void Duration::set_nanos(int32_t value) {
+  _internal_set_nanos(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Duration.nanos)
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fduration_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/dynamic_message.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/dynamic_message.h
new file mode 100644
index 0000000..6fa6425
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/dynamic_message.h
@@ -0,0 +1,227 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Defines an implementation of Message which can emulate types which are not
+// known at compile-time.
+
+#ifndef GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__
+#define GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__
+
+
+#include <algorithm>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/mutex.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/reflection.h>
+#include <google/protobuf/repeated_field.h>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+// Defined in other files.
+class Descriptor;      // descriptor.h
+class DescriptorPool;  // descriptor.h
+
+// Constructs implementations of Message which can emulate types which are not
+// known at compile-time.
+//
+// Sometimes you want to be able to manipulate protocol types that you don't
+// know about at compile time.  It would be nice to be able to construct
+// a Message object which implements the message type given by any arbitrary
+// Descriptor.  DynamicMessage provides this.
+//
+// As it turns out, a DynamicMessage needs to construct extra
+// information about its type in order to operate.  Most of this information
+// can be shared between all DynamicMessages of the same type.  But, caching
+// this information in some sort of global map would be a bad idea, since
+// the cached information for a particular descriptor could outlive the
+// descriptor itself.  To avoid this problem, DynamicMessageFactory
+// encapsulates this "cache".  All DynamicMessages of the same type created
+// from the same factory will share the same support data.  Any Descriptors
+// used with a particular factory must outlive the factory.
+class PROTOBUF_EXPORT DynamicMessageFactory : public MessageFactory {
+ public:
+  // Construct a DynamicMessageFactory that will search for extensions in
+  // the DescriptorPool in which the extendee is defined.
+  DynamicMessageFactory();
+
+  // Construct a DynamicMessageFactory that will search for extensions in
+  // the given DescriptorPool.
+  //
+  // DEPRECATED:  Use CodedInputStream::SetExtensionRegistry() to tell the
+  //   parser to look for extensions in an alternate pool.  However, note that
+  //   this is almost never what you want to do.  Almost all users should use
+  //   the zero-arg constructor.
+  DynamicMessageFactory(const DescriptorPool* pool);
+
+  ~DynamicMessageFactory() override;
+
+  // Call this to tell the DynamicMessageFactory that if it is given a
+  // Descriptor d for which:
+  //   d->file()->pool() == DescriptorPool::generated_pool(),
+  // then it should delegate to MessageFactory::generated_factory() instead
+  // of constructing a dynamic implementation of the message.  In theory there
+  // is no down side to doing this, so it may become the default in the future.
+  void SetDelegateToGeneratedFactory(bool enable) {
+    delegate_to_generated_factory_ = enable;
+  }
+
+  // implements MessageFactory ---------------------------------------
+
+  // Given a Descriptor, constructs the default (prototype) Message of that
+  // type.  You can then call that message's New() method to construct a
+  // mutable message of that type.
+  //
+  // Calling this method twice with the same Descriptor returns the same
+  // object.  The returned object remains property of the factory and will
+  // be destroyed when the factory is destroyed.  Also, any objects created
+  // by calling the prototype's New() method share some data with the
+  // prototype, so these must be destroyed before the DynamicMessageFactory
+  // is destroyed.
+  //
+  // The given descriptor must outlive the returned message, and hence must
+  // outlive the DynamicMessageFactory.
+  //
+  // The method is thread-safe.
+  const Message* GetPrototype(const Descriptor* type) override;
+
+ private:
+  const DescriptorPool* pool_;
+  bool delegate_to_generated_factory_;
+
+  struct TypeInfo;
+  std::unordered_map<const Descriptor*, const TypeInfo*> prototypes_;
+  mutable internal::WrappedMutex prototypes_mutex_;
+
+  friend class DynamicMessage;
+  const Message* GetPrototypeNoLock(const Descriptor* type);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DynamicMessageFactory);
+};
+
+// Helper for computing a sorted list of map entries via reflection.
+class PROTOBUF_EXPORT DynamicMapSorter {
+ public:
+  static std::vector<const Message*> Sort(const Message& message, int map_size,
+                                          const Reflection* reflection,
+                                          const FieldDescriptor* field) {
+    std::vector<const Message*> result;
+    result.reserve(map_size);
+    RepeatedFieldRef<Message> map_field =
+        reflection->GetRepeatedFieldRef<Message>(message, field);
+    for (auto it = map_field.begin(); it != map_field.end(); ++it) {
+      result.push_back(&*it);
+    }
+    MapEntryMessageComparator comparator(field->message_type());
+    std::stable_sort(result.begin(), result.end(), comparator);
+    // Complain if the keys aren't in ascending order.
+#ifndef NDEBUG
+    for (size_t j = 1; j < static_cast<size_t>(map_size); j++) {
+      if (!comparator(result[j - 1], result[j])) {
+        GOOGLE_LOG(ERROR) << (comparator(result[j], result[j - 1])
+                           ? "internal error in map key sorting"
+                           : "map keys are not unique");
+      }
+    }
+#endif
+    return result;
+  }
+
+ private:
+  class PROTOBUF_EXPORT MapEntryMessageComparator {
+   public:
+    explicit MapEntryMessageComparator(const Descriptor* descriptor)
+        : field_(descriptor->field(0)) {}
+
+    bool operator()(const Message* a, const Message* b) {
+      const Reflection* reflection = a->GetReflection();
+      switch (field_->cpp_type()) {
+        case FieldDescriptor::CPPTYPE_BOOL: {
+          bool first = reflection->GetBool(*a, field_);
+          bool second = reflection->GetBool(*b, field_);
+          return first < second;
+        }
+        case FieldDescriptor::CPPTYPE_INT32: {
+          int32_t first = reflection->GetInt32(*a, field_);
+          int32_t second = reflection->GetInt32(*b, field_);
+          return first < second;
+        }
+        case FieldDescriptor::CPPTYPE_INT64: {
+          int64_t first = reflection->GetInt64(*a, field_);
+          int64_t second = reflection->GetInt64(*b, field_);
+          return first < second;
+        }
+        case FieldDescriptor::CPPTYPE_UINT32: {
+          uint32_t first = reflection->GetUInt32(*a, field_);
+          uint32_t second = reflection->GetUInt32(*b, field_);
+          return first < second;
+        }
+        case FieldDescriptor::CPPTYPE_UINT64: {
+          uint64_t first = reflection->GetUInt64(*a, field_);
+          uint64_t second = reflection->GetUInt64(*b, field_);
+          return first < second;
+        }
+        case FieldDescriptor::CPPTYPE_STRING: {
+          std::string first = reflection->GetString(*a, field_);
+          std::string second = reflection->GetString(*b, field_);
+          return first < second;
+        }
+        default:
+          GOOGLE_LOG(DFATAL) << "Invalid key for map field.";
+          return true;
+      }
+    }
+
+   private:
+    const FieldDescriptor* field_;
+  };
+};
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/empty.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/empty.pb.h
new file mode 100644
index 0000000..bcb6478
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/empty.pb.h
@@ -0,0 +1,198 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/empty.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fempty_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fempty_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_bases.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/unknown_field_set.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fempty_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2fempty_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2fempty_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class Empty;
+struct EmptyDefaultTypeInternal;
+PROTOBUF_EXPORT extern EmptyDefaultTypeInternal _Empty_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Empty* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Empty>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class PROTOBUF_EXPORT Empty final :
+    public ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase /* @@protoc_insertion_point(class_definition:google.protobuf.Empty) */ {
+ public:
+  inline Empty() : Empty(nullptr) {}
+  explicit PROTOBUF_CONSTEXPR Empty(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Empty(const Empty& from);
+  Empty(Empty&& from) noexcept
+    : Empty() {
+    *this = ::std::move(from);
+  }
+
+  inline Empty& operator=(const Empty& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Empty& operator=(Empty&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Empty& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Empty* internal_default_instance() {
+    return reinterpret_cast<const Empty*>(
+               &_Empty_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(Empty& a, Empty& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Empty* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Empty* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Empty* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Empty>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyFrom;
+  inline void CopyFrom(const Empty& from) {
+    ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyImpl(*this, from);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeFrom;
+  void MergeFrom(const Empty& from) {
+    ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeImpl(*this, from);
+  }
+  public:
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Empty";
+  }
+  protected:
+  explicit Empty(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Empty)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+  };
+  friend struct ::TableStruct_google_2fprotobuf_2fempty_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// Empty
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fempty_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/endian.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/endian.h
new file mode 100644
index 0000000..e0ee6cd
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/endian.h
@@ -0,0 +1,198 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_ENDIAN_H__
+#define GOOGLE_PROTOBUF_ENDIAN_H__
+
+#if defined(_MSC_VER)
+#include <stdlib.h>
+#endif
+
+#include <cstdint>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+inline uint64_t BSwap64(uint64_t host_int) {
+#if defined(PROTOBUF_BUILTIN_BSWAP64)
+  return PROTOBUF_BUILTIN_BSWAP64(host_int);
+#elif defined(_MSC_VER)
+  return _byteswap_uint64(host_int);
+#else
+  return (((host_int & uint64_t{0xFF}) << 56) |
+          ((host_int & uint64_t{0xFF00}) << 40) |
+          ((host_int & uint64_t{0xFF0000}) << 24) |
+          ((host_int & uint64_t{0xFF000000}) << 8) |
+          ((host_int & uint64_t{0xFF00000000}) >> 8) |
+          ((host_int & uint64_t{0xFF0000000000}) >> 24) |
+          ((host_int & uint64_t{0xFF000000000000}) >> 40) |
+          ((host_int & uint64_t{0xFF00000000000000}) >> 56));
+#endif
+}
+
+inline uint32_t BSwap32(uint32_t host_int) {
+#if defined(PROTOBUF_BUILTIN_BSWAP32)
+  return PROTOBUF_BUILTIN_BSWAP32(host_int);
+#elif defined(_MSC_VER)
+  return _byteswap_ulong(host_int);
+#else
+  return (((host_int & uint32_t{0xFF}) << 24) |
+          ((host_int & uint32_t{0xFF00}) << 8) |
+          ((host_int & uint32_t{0xFF0000}) >> 8) |
+          ((host_int & uint32_t{0xFF000000}) >> 24));
+#endif
+}
+
+inline uint16_t BSwap16(uint16_t host_int) {
+#if defined(PROTOBUF_BUILTIN_BSWAP16)
+  return PROTOBUF_BUILTIN_BSWAP16(host_int);
+#elif defined(_MSC_VER)
+  return _byteswap_ushort(host_int);
+#else
+  return (((host_int & uint16_t{0xFF}) << 8) |
+          ((host_int & uint16_t{0xFF00}) >> 8));
+#endif
+}
+
+namespace little_endian {
+
+inline uint16_t FromHost(uint16_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return BSwap16(value);
+#else
+  return value;
+#endif
+}
+
+inline uint32_t FromHost(uint32_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return BSwap32(value);
+#else
+  return value;
+#endif
+}
+
+inline uint64_t FromHost(uint64_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return BSwap64(value);
+#else
+  return value;
+#endif
+}
+
+inline uint16_t ToHost(uint16_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return BSwap16(value);
+#else
+  return value;
+#endif
+}
+
+inline uint32_t ToHost(uint32_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return BSwap32(value);
+#else
+  return value;
+#endif
+}
+
+inline uint64_t ToHost(uint64_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return BSwap64(value);
+#else
+  return value;
+#endif
+}
+
+}  // namespace little_endian
+
+namespace big_endian {
+
+inline uint16_t FromHost(uint16_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return value;
+#else
+  return BSwap16(value);
+#endif
+}
+
+inline uint32_t FromHost(uint32_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return value;
+#else
+  return BSwap32(value);
+#endif
+}
+
+inline uint64_t FromHost(uint64_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return value;
+#else
+  return BSwap64(value);
+#endif
+}
+
+inline uint16_t ToHost(uint16_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return value;
+#else
+  return BSwap16(value);
+#endif
+}
+
+inline uint32_t ToHost(uint32_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return value;
+#else
+  return BSwap32(value);
+#endif
+}
+
+inline uint64_t ToHost(uint64_t value) {
+#if defined(PROTOBUF_BIG_ENDIAN)
+  return value;
+#else
+  return BSwap64(value);
+#endif
+}
+
+}  // namespace big_endian
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_ENDIAN_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/explicitly_constructed.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/explicitly_constructed.h
new file mode 100644
index 0000000..174c59a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/explicitly_constructed.h
@@ -0,0 +1,97 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_EXPLICITLY_CONSTRUCTED_H__
+#define GOOGLE_PROTOBUF_EXPLICITLY_CONSTRUCTED_H__
+
+#include <stdint.h>
+
+#include <utility>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Wraps a variable whose constructor and destructor are explicitly
+// called. It is particularly useful for a global variable, without its
+// constructor and destructor run on start and end of the program lifetime.
+// This circumvents the initial construction order fiasco, while keeping
+// the address of the empty string a compile time constant.
+//
+// Pay special attention to the initialization state of the object.
+// 1. The object is "uninitialized" to begin with.
+// 2. Call Construct() or DefaultConstruct() only if the object is
+//    uninitialized. After the call, the object becomes "initialized".
+// 3. Call get() and get_mutable() only if the object is initialized.
+// 4. Call Destruct() only if the object is initialized.
+//    After the call, the object becomes uninitialized.
+template <typename T, size_t min_align = 1>
+class ExplicitlyConstructed {
+ public:
+  void DefaultConstruct() { new (&union_) T(); }
+
+  template <typename... Args>
+  void Construct(Args&&... args) {
+    new (&union_) T(std::forward<Args>(args)...);
+  }
+
+  void Destruct() { get_mutable()->~T(); }
+
+  constexpr const T& get() const { return reinterpret_cast<const T&>(union_); }
+  T* get_mutable() { return reinterpret_cast<T*>(&union_); }
+
+ private:
+  union AlignedUnion {
+    alignas(min_align > alignof(T) ? min_align
+                                   : alignof(T)) char space[sizeof(T)];
+    int64_t align_to_int64;
+    void* align_to_ptr;
+  } union_;
+};
+
+// ArenaStringPtr compatible explicitly constructed string type.
+// This empty string type is aligned with a minimum alignment of 8 bytes
+// which is the minimum requirement of ArenaStringPtr
+using ExplicitlyConstructedArenaString = ExplicitlyConstructed<std::string, 8>;
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_EXPLICITLY_CONSTRUCTED_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/extension_set.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/extension_set.h
new file mode 100644
index 0000000..b534368
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/extension_set.h
@@ -0,0 +1,1561 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This header is logically internal, but is made public because it is used
+// from protocol-compiler-generated code, which may reside in other components.
+
+#ifndef GOOGLE_PROTOBUF_EXTENSION_SET_H__
+#define GOOGLE_PROTOBUF_EXTENSION_SET_H__
+
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>  // Must be last
+// clang-format on
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+class Arena;
+class Descriptor;       // descriptor.h
+class FieldDescriptor;  // descriptor.h
+class DescriptorPool;   // descriptor.h
+class MessageLite;      // message_lite.h
+class Message;          // message.h
+class MessageFactory;   // message.h
+class Reflection;       // message.h
+class UnknownFieldSet;  // unknown_field_set.h
+namespace internal {
+class FieldSkipper;  // wire_format_lite.h
+enum class LazyVerifyOption;
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+class InternalMetadata;
+
+// Used to store values of type WireFormatLite::FieldType without having to
+// #include wire_format_lite.h.  Also, ensures that we use only one byte to
+// store these values, which is important to keep the layout of
+// ExtensionSet::Extension small.
+typedef uint8_t FieldType;
+
+// A function which, given an integer value, returns true if the number
+// matches one of the defined values for the corresponding enum type.  This
+// is used with RegisterEnumExtension, below.
+typedef bool EnumValidityFunc(int number);
+
+// Version of the above which takes an argument.  This is needed to deal with
+// extensions that are not compiled in.
+typedef bool EnumValidityFuncWithArg(const void* arg, int number);
+
+// Information about a registered extension.
+struct ExtensionInfo {
+  constexpr ExtensionInfo() : enum_validity_check() {}
+  constexpr ExtensionInfo(const MessageLite* extendee, int param_number,
+                          FieldType type_param, bool isrepeated, bool ispacked,
+                          LazyEagerVerifyFnType verify_func)
+      : message(extendee),
+        number(param_number),
+        type(type_param),
+        is_repeated(isrepeated),
+        is_packed(ispacked),
+        enum_validity_check(),
+        lazy_eager_verify_func(verify_func) {}
+
+  const MessageLite* message = nullptr;
+  int number = 0;
+
+  FieldType type = 0;
+  bool is_repeated = false;
+  bool is_packed = false;
+
+  struct EnumValidityCheck {
+    EnumValidityFuncWithArg* func;
+    const void* arg;
+  };
+
+  struct MessageInfo {
+    const MessageLite* prototype;
+  };
+
+  union {
+    EnumValidityCheck enum_validity_check;
+    MessageInfo message_info;
+  };
+
+  // The descriptor for this extension, if one exists and is known.  May be
+  // nullptr.  Must not be nullptr if the descriptor for the extension does not
+  // live in the same pool as the descriptor for the containing type.
+  const FieldDescriptor* descriptor = nullptr;
+
+  // If this field is potentially lazy this function can be used as a cheap
+  // verification of the raw bytes.
+  // If nullptr then no verification is performed.
+  LazyEagerVerifyFnType lazy_eager_verify_func = nullptr;
+};
+
+// An ExtensionFinder is an object which looks up extension definitions.  It
+// must implement this method:
+//
+// bool Find(int number, ExtensionInfo* output);
+
+// GeneratedExtensionFinder is an ExtensionFinder which finds extensions
+// defined in .proto files which have been compiled into the binary.
+class PROTOBUF_EXPORT GeneratedExtensionFinder {
+ public:
+  explicit GeneratedExtensionFinder(const MessageLite* extendee)
+      : extendee_(extendee) {}
+
+  // Returns true and fills in *output if found, otherwise returns false.
+  bool Find(int number, ExtensionInfo* output);
+
+ private:
+  const MessageLite* extendee_;
+};
+
+// Note:  extension_set_heavy.cc defines DescriptorPoolExtensionFinder for
+// finding extensions from a DescriptorPool.
+
+// This is an internal helper class intended for use within the protocol buffer
+// library and generated classes.  Clients should not use it directly.  Instead,
+// use the generated accessors such as GetExtension() of the class being
+// extended.
+//
+// This class manages extensions for a protocol message object.  The
+// message's HasExtension(), GetExtension(), MutableExtension(), and
+// ClearExtension() methods are just thin wrappers around the embedded
+// ExtensionSet.  When parsing, if a tag number is encountered which is
+// inside one of the message type's extension ranges, the tag is passed
+// off to the ExtensionSet for parsing.  Etc.
+class PROTOBUF_EXPORT ExtensionSet {
+ public:
+  constexpr ExtensionSet();
+  explicit ExtensionSet(Arena* arena);
+  ExtensionSet(ArenaInitialized, Arena* arena) : ExtensionSet(arena) {}
+  ~ExtensionSet();
+
+  // These are called at startup by protocol-compiler-generated code to
+  // register known extensions.  The registrations are used by ParseField()
+  // to look up extensions for parsed field numbers.  Note that dynamic parsing
+  // does not use ParseField(); only protocol-compiler-generated parsing
+  // methods do.
+  static void RegisterExtension(const MessageLite* extendee, int number,
+                                FieldType type, bool is_repeated,
+                                bool is_packed,
+                                LazyEagerVerifyFnType verify_func);
+  static void RegisterEnumExtension(const MessageLite* extendee, int number,
+                                    FieldType type, bool is_repeated,
+                                    bool is_packed, EnumValidityFunc* is_valid);
+  static void RegisterMessageExtension(const MessageLite* extendee, int number,
+                                       FieldType type, bool is_repeated,
+                                       bool is_packed,
+                                       const MessageLite* prototype,
+                                       LazyEagerVerifyFnType verify_func);
+
+  // =================================================================
+
+  // Add all fields which are currently present to the given vector.  This
+  // is useful to implement Reflection::ListFields().
+  void AppendToList(const Descriptor* extendee, const DescriptorPool* pool,
+                    std::vector<const FieldDescriptor*>* output) const;
+
+  // =================================================================
+  // Accessors
+  //
+  // Generated message classes include type-safe templated wrappers around
+  // these methods.  Generally you should use those rather than call these
+  // directly, unless you are doing low-level memory management.
+  //
+  // When calling any of these accessors, the extension number requested
+  // MUST exist in the DescriptorPool provided to the constructor.  Otherwise,
+  // the method will fail an assert.  Normally, though, you would not call
+  // these directly; you would either call the generated accessors of your
+  // message class (e.g. GetExtension()) or you would call the accessors
+  // of the reflection interface.  In both cases, it is impossible to
+  // trigger this assert failure:  the generated accessors only accept
+  // linked-in extension types as parameters, while the Reflection interface
+  // requires you to provide the FieldDescriptor describing the extension.
+  //
+  // When calling any of these accessors, a protocol-compiler-generated
+  // implementation of the extension corresponding to the number MUST
+  // be linked in, and the FieldDescriptor used to refer to it MUST be
+  // the one generated by that linked-in code.  Otherwise, the method will
+  // die on an assert failure.  The message objects returned by the message
+  // accessors are guaranteed to be of the correct linked-in type.
+  //
+  // These methods pretty much match Reflection except that:
+  // - They're not virtual.
+  // - They identify fields by number rather than FieldDescriptors.
+  // - They identify enum values using integers rather than descriptors.
+  // - Strings provide Mutable() in addition to Set() accessors.
+
+  bool Has(int number) const;
+  int ExtensionSize(int number) const;  // Size of a repeated extension.
+  int NumExtensions() const;            // The number of extensions
+  FieldType ExtensionType(int number) const;
+  void ClearExtension(int number);
+
+  // singular fields -------------------------------------------------
+
+  int32_t GetInt32(int number, int32_t default_value) const;
+  int64_t GetInt64(int number, int64_t default_value) const;
+  uint32_t GetUInt32(int number, uint32_t default_value) const;
+  uint64_t GetUInt64(int number, uint64_t default_value) const;
+  float GetFloat(int number, float default_value) const;
+  double GetDouble(int number, double default_value) const;
+  bool GetBool(int number, bool default_value) const;
+  int GetEnum(int number, int default_value) const;
+  const std::string& GetString(int number,
+                               const std::string& default_value) const;
+  const MessageLite& GetMessage(int number,
+                                const MessageLite& default_value) const;
+  const MessageLite& GetMessage(int number, const Descriptor* message_type,
+                                MessageFactory* factory) const;
+
+  // |descriptor| may be nullptr so long as it is known that the descriptor for
+  // the extension lives in the same pool as the descriptor for the containing
+  // type.
+#define desc const FieldDescriptor* descriptor  // avoid line wrapping
+  void SetInt32(int number, FieldType type, int32_t value, desc);
+  void SetInt64(int number, FieldType type, int64_t value, desc);
+  void SetUInt32(int number, FieldType type, uint32_t value, desc);
+  void SetUInt64(int number, FieldType type, uint64_t value, desc);
+  void SetFloat(int number, FieldType type, float value, desc);
+  void SetDouble(int number, FieldType type, double value, desc);
+  void SetBool(int number, FieldType type, bool value, desc);
+  void SetEnum(int number, FieldType type, int value, desc);
+  void SetString(int number, FieldType type, std::string value, desc);
+  std::string* MutableString(int number, FieldType type, desc);
+  MessageLite* MutableMessage(int number, FieldType type,
+                              const MessageLite& prototype, desc);
+  MessageLite* MutableMessage(const FieldDescriptor* descriptor,
+                              MessageFactory* factory);
+  // Adds the given message to the ExtensionSet, taking ownership of the
+  // message object. Existing message with the same number will be deleted.
+  // If "message" is nullptr, this is equivalent to "ClearExtension(number)".
+  void SetAllocatedMessage(int number, FieldType type,
+                           const FieldDescriptor* descriptor,
+                           MessageLite* message);
+  void UnsafeArenaSetAllocatedMessage(int number, FieldType type,
+                                      const FieldDescriptor* descriptor,
+                                      MessageLite* message);
+  PROTOBUF_NODISCARD MessageLite* ReleaseMessage(int number,
+                                                 const MessageLite& prototype);
+  MessageLite* UnsafeArenaReleaseMessage(int number,
+                                         const MessageLite& prototype);
+
+  PROTOBUF_NODISCARD MessageLite* ReleaseMessage(
+      const FieldDescriptor* descriptor, MessageFactory* factory);
+  MessageLite* UnsafeArenaReleaseMessage(const FieldDescriptor* descriptor,
+                                         MessageFactory* factory);
+#undef desc
+  Arena* GetArena() const { return arena_; }
+
+  // repeated fields -------------------------------------------------
+
+  // Fetches a RepeatedField extension by number; returns |default_value|
+  // if no such extension exists. User should not touch this directly; it is
+  // used by the GetRepeatedExtension() method.
+  const void* GetRawRepeatedField(int number, const void* default_value) const;
+  // Fetches a mutable version of a RepeatedField extension by number,
+  // instantiating one if none exists. Similar to above, user should not use
+  // this directly; it underlies MutableRepeatedExtension().
+  void* MutableRawRepeatedField(int number, FieldType field_type, bool packed,
+                                const FieldDescriptor* desc);
+
+  // This is an overload of MutableRawRepeatedField to maintain compatibility
+  // with old code using a previous API. This version of
+  // MutableRawRepeatedField() will GOOGLE_CHECK-fail on a missing extension.
+  // (E.g.: borg/clients/internal/proto1/proto2_reflection.cc.)
+  void* MutableRawRepeatedField(int number);
+
+  int32_t GetRepeatedInt32(int number, int index) const;
+  int64_t GetRepeatedInt64(int number, int index) const;
+  uint32_t GetRepeatedUInt32(int number, int index) const;
+  uint64_t GetRepeatedUInt64(int number, int index) const;
+  float GetRepeatedFloat(int number, int index) const;
+  double GetRepeatedDouble(int number, int index) const;
+  bool GetRepeatedBool(int number, int index) const;
+  int GetRepeatedEnum(int number, int index) const;
+  const std::string& GetRepeatedString(int number, int index) const;
+  const MessageLite& GetRepeatedMessage(int number, int index) const;
+
+  void SetRepeatedInt32(int number, int index, int32_t value);
+  void SetRepeatedInt64(int number, int index, int64_t value);
+  void SetRepeatedUInt32(int number, int index, uint32_t value);
+  void SetRepeatedUInt64(int number, int index, uint64_t value);
+  void SetRepeatedFloat(int number, int index, float value);
+  void SetRepeatedDouble(int number, int index, double value);
+  void SetRepeatedBool(int number, int index, bool value);
+  void SetRepeatedEnum(int number, int index, int value);
+  void SetRepeatedString(int number, int index, std::string value);
+  std::string* MutableRepeatedString(int number, int index);
+  MessageLite* MutableRepeatedMessage(int number, int index);
+
+#define desc const FieldDescriptor* descriptor  // avoid line wrapping
+  void AddInt32(int number, FieldType type, bool packed, int32_t value, desc);
+  void AddInt64(int number, FieldType type, bool packed, int64_t value, desc);
+  void AddUInt32(int number, FieldType type, bool packed, uint32_t value, desc);
+  void AddUInt64(int number, FieldType type, bool packed, uint64_t value, desc);
+  void AddFloat(int number, FieldType type, bool packed, float value, desc);
+  void AddDouble(int number, FieldType type, bool packed, double value, desc);
+  void AddBool(int number, FieldType type, bool packed, bool value, desc);
+  void AddEnum(int number, FieldType type, bool packed, int value, desc);
+  void AddString(int number, FieldType type, std::string value, desc);
+  std::string* AddString(int number, FieldType type, desc);
+  MessageLite* AddMessage(int number, FieldType type,
+                          const MessageLite& prototype, desc);
+  MessageLite* AddMessage(const FieldDescriptor* descriptor,
+                          MessageFactory* factory);
+  void AddAllocatedMessage(const FieldDescriptor* descriptor,
+                           MessageLite* new_entry);
+  void UnsafeArenaAddAllocatedMessage(const FieldDescriptor* descriptor,
+                                      MessageLite* new_entry);
+#undef desc
+
+  void RemoveLast(int number);
+  PROTOBUF_NODISCARD MessageLite* ReleaseLast(int number);
+  MessageLite* UnsafeArenaReleaseLast(int number);
+  void SwapElements(int number, int index1, int index2);
+
+  // =================================================================
+  // convenience methods for implementing methods of Message
+  //
+  // These could all be implemented in terms of the other methods of this
+  // class, but providing them here helps keep the generated code size down.
+
+  void Clear();
+  void MergeFrom(const MessageLite* extendee, const ExtensionSet& other);
+  void Swap(const MessageLite* extendee, ExtensionSet* other);
+  void InternalSwap(ExtensionSet* other);
+  void SwapExtension(const MessageLite* extendee, ExtensionSet* other,
+                     int number);
+  void UnsafeShallowSwapExtension(ExtensionSet* other, int number);
+  bool IsInitialized() const;
+
+  // Lite parser
+  const char* ParseField(uint64_t tag, const char* ptr,
+                         const MessageLite* extendee,
+                         internal::InternalMetadata* metadata,
+                         internal::ParseContext* ctx);
+  // Full parser
+  const char* ParseField(uint64_t tag, const char* ptr, const Message* extendee,
+                         internal::InternalMetadata* metadata,
+                         internal::ParseContext* ctx);
+  template <typename Msg>
+  const char* ParseMessageSet(const char* ptr, const Msg* extendee,
+                              InternalMetadata* metadata,
+                              internal::ParseContext* ctx) {
+    struct MessageSetItem {
+      const char* _InternalParse(const char* ptr, ParseContext* ctx) {
+        return me->ParseMessageSetItem(ptr, extendee, metadata, ctx);
+      }
+      ExtensionSet* me;
+      const Msg* extendee;
+      InternalMetadata* metadata;
+    } item{this, extendee, metadata};
+    while (!ctx->Done(&ptr)) {
+      uint32_t tag;
+      ptr = ReadTag(ptr, &tag);
+      GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+      if (tag == WireFormatLite::kMessageSetItemStartTag) {
+        ptr = ctx->ParseGroup(&item, ptr, tag);
+        GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+      } else {
+        if (tag == 0 || (tag & 7) == 4) {
+          ctx->SetLastTag(tag);
+          return ptr;
+        }
+        ptr = ParseField(tag, ptr, extendee, metadata, ctx);
+        GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+      }
+    }
+    return ptr;
+  }
+
+  // Write all extension fields with field numbers in the range
+  //   [start_field_number, end_field_number)
+  // to the output stream, using the cached sizes computed when ByteSize() was
+  // last called.  Note that the range bounds are inclusive-exclusive.
+  void SerializeWithCachedSizes(const MessageLite* extendee,
+                                int start_field_number, int end_field_number,
+                                io::CodedOutputStream* output) const {
+    output->SetCur(_InternalSerialize(extendee, start_field_number,
+                                      end_field_number, output->Cur(),
+                                      output->EpsCopy()));
+  }
+
+  // Same as SerializeWithCachedSizes, but without any bounds checking.
+  // The caller must ensure that target has sufficient capacity for the
+  // serialized extensions.
+  //
+  // Returns a pointer past the last written byte.
+
+  uint8_t* _InternalSerialize(const MessageLite* extendee,
+                              int start_field_number, int end_field_number,
+                              uint8_t* target,
+                              io::EpsCopyOutputStream* stream) const {
+    if (flat_size_ == 0) {
+      assert(!is_large());
+      return target;
+    }
+    return _InternalSerializeImpl(extendee, start_field_number,
+                                  end_field_number, target, stream);
+  }
+
+  // Like above but serializes in MessageSet format.
+  void SerializeMessageSetWithCachedSizes(const MessageLite* extendee,
+                                          io::CodedOutputStream* output) const {
+    output->SetCur(InternalSerializeMessageSetWithCachedSizesToArray(
+        extendee, output->Cur(), output->EpsCopy()));
+  }
+  uint8_t* InternalSerializeMessageSetWithCachedSizesToArray(
+      const MessageLite* extendee, uint8_t* target,
+      io::EpsCopyOutputStream* stream) const;
+
+  // For backward-compatibility, versions of two of the above methods that
+  // serialize deterministically iff SetDefaultSerializationDeterministic()
+  // has been called.
+  uint8_t* SerializeWithCachedSizesToArray(int start_field_number,
+                                           int end_field_number,
+                                           uint8_t* target) const;
+  uint8_t* SerializeMessageSetWithCachedSizesToArray(
+      const MessageLite* extendee, uint8_t* target) const;
+
+  // Returns the total serialized size of all the extensions.
+  size_t ByteSize() const;
+
+  // Like ByteSize() but uses MessageSet format.
+  size_t MessageSetByteSize() const;
+
+  // Returns (an estimate of) the total number of bytes used for storing the
+  // extensions in memory, excluding sizeof(*this).  If the ExtensionSet is
+  // for a lite message (and thus possibly contains lite messages), the results
+  // are undefined (might work, might crash, might corrupt data, might not even
+  // be linked in).  It's up to the protocol compiler to avoid calling this on
+  // such ExtensionSets (easy enough since lite messages don't implement
+  // SpaceUsed()).
+  size_t SpaceUsedExcludingSelfLong() const;
+
+  // This method just calls SpaceUsedExcludingSelfLong() but it can not be
+  // inlined because the definition of SpaceUsedExcludingSelfLong() is not
+  // included in lite runtime and when an inline method refers to it MSVC
+  // will complain about unresolved symbols when building the lite runtime
+  // as .dll.
+  int SpaceUsedExcludingSelf() const;
+
+ private:
+  template <typename Type>
+  friend class PrimitiveTypeTraits;
+
+  template <typename Type>
+  friend class RepeatedPrimitiveTypeTraits;
+
+  template <typename Type, bool IsValid(int)>
+  friend class EnumTypeTraits;
+
+  template <typename Type, bool IsValid(int)>
+  friend class RepeatedEnumTypeTraits;
+
+  friend class google::protobuf::Reflection;
+
+  const int32_t& GetRefInt32(int number, const int32_t& default_value) const;
+  const int64_t& GetRefInt64(int number, const int64_t& default_value) const;
+  const uint32_t& GetRefUInt32(int number, const uint32_t& default_value) const;
+  const uint64_t& GetRefUInt64(int number, const uint64_t& default_value) const;
+  const float& GetRefFloat(int number, const float& default_value) const;
+  const double& GetRefDouble(int number, const double& default_value) const;
+  const bool& GetRefBool(int number, const bool& default_value) const;
+  const int& GetRefEnum(int number, const int& default_value) const;
+  const int32_t& GetRefRepeatedInt32(int number, int index) const;
+  const int64_t& GetRefRepeatedInt64(int number, int index) const;
+  const uint32_t& GetRefRepeatedUInt32(int number, int index) const;
+  const uint64_t& GetRefRepeatedUInt64(int number, int index) const;
+  const float& GetRefRepeatedFloat(int number, int index) const;
+  const double& GetRefRepeatedDouble(int number, int index) const;
+  const bool& GetRefRepeatedBool(int number, int index) const;
+  const int& GetRefRepeatedEnum(int number, int index) const;
+
+  // Implementation of _InternalSerialize for non-empty map_.
+  uint8_t* _InternalSerializeImpl(const MessageLite* extendee,
+                                  int start_field_number, int end_field_number,
+                                  uint8_t* target,
+                                  io::EpsCopyOutputStream* stream) const;
+  // Interface of a lazily parsed singular message extension.
+  class PROTOBUF_EXPORT LazyMessageExtension {
+   public:
+    LazyMessageExtension() {}
+    virtual ~LazyMessageExtension() {}
+
+    virtual LazyMessageExtension* New(Arena* arena) const = 0;
+    virtual const MessageLite& GetMessage(const MessageLite& prototype,
+                                          Arena* arena) const = 0;
+    virtual MessageLite* MutableMessage(const MessageLite& prototype,
+                                        Arena* arena) = 0;
+    virtual void SetAllocatedMessage(MessageLite* message, Arena* arena) = 0;
+    virtual void UnsafeArenaSetAllocatedMessage(MessageLite* message,
+                                                Arena* arena) = 0;
+    PROTOBUF_NODISCARD virtual MessageLite* ReleaseMessage(
+        const MessageLite& prototype, Arena* arena) = 0;
+    virtual MessageLite* UnsafeArenaReleaseMessage(const MessageLite& prototype,
+                                                   Arena* arena) = 0;
+
+    virtual bool IsInitialized() const = 0;
+
+    PROTOBUF_DEPRECATED_MSG("Please use ByteSizeLong() instead")
+    virtual int ByteSize() const { return internal::ToIntSize(ByteSizeLong()); }
+    virtual size_t ByteSizeLong() const = 0;
+    virtual size_t SpaceUsedLong() const = 0;
+
+    virtual void MergeFrom(const MessageLite* prototype,
+                           const LazyMessageExtension& other, Arena* arena) = 0;
+    virtual void MergeFromMessage(const MessageLite& msg, Arena* arena) = 0;
+    virtual void Clear() = 0;
+
+    virtual const char* _InternalParse(const Message& prototype, Arena* arena,
+                                       LazyVerifyOption option, const char* ptr,
+                                       ParseContext* ctx) = 0;
+    virtual uint8_t* WriteMessageToArray(
+        const MessageLite* prototype, int number, uint8_t* target,
+        io::EpsCopyOutputStream* stream) const = 0;
+
+   private:
+    virtual void UnusedKeyMethod();  // Dummy key method to avoid weak vtable.
+
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LazyMessageExtension);
+  };
+  // Give access to function defined below to see LazyMessageExtension.
+  friend LazyMessageExtension* MaybeCreateLazyExtension(Arena* arena);
+  struct Extension {
+    // The order of these fields packs Extension into 24 bytes when using 8
+    // byte alignment. Consider this when adding or removing fields here.
+    union {
+      int32_t int32_t_value;
+      int64_t int64_t_value;
+      uint32_t uint32_t_value;
+      uint64_t uint64_t_value;
+      float float_value;
+      double double_value;
+      bool bool_value;
+      int enum_value;
+      std::string* string_value;
+      MessageLite* message_value;
+      LazyMessageExtension* lazymessage_value;
+
+      RepeatedField<int32_t>* repeated_int32_t_value;
+      RepeatedField<int64_t>* repeated_int64_t_value;
+      RepeatedField<uint32_t>* repeated_uint32_t_value;
+      RepeatedField<uint64_t>* repeated_uint64_t_value;
+      RepeatedField<float>* repeated_float_value;
+      RepeatedField<double>* repeated_double_value;
+      RepeatedField<bool>* repeated_bool_value;
+      RepeatedField<int>* repeated_enum_value;
+      RepeatedPtrField<std::string>* repeated_string_value;
+      RepeatedPtrField<MessageLite>* repeated_message_value;
+    };
+
+    FieldType type;
+    bool is_repeated;
+
+    // For singular types, indicates if the extension is "cleared".  This
+    // happens when an extension is set and then later cleared by the caller.
+    // We want to keep the Extension object around for reuse, so instead of
+    // removing it from the map, we just set is_cleared = true.  This has no
+    // meaning for repeated types; for those, the size of the RepeatedField
+    // simply becomes zero when cleared.
+    bool is_cleared : 4;
+
+    // For singular message types, indicates whether lazy parsing is enabled
+    // for this extension. This field is only valid when type == TYPE_MESSAGE
+    // and !is_repeated because we only support lazy parsing for singular
+    // message types currently. If is_lazy = true, the extension is stored in
+    // lazymessage_value. Otherwise, the extension will be message_value.
+    bool is_lazy : 4;
+
+    // For repeated types, this indicates if the [packed=true] option is set.
+    bool is_packed;
+
+    // For packed fields, the size of the packed data is recorded here when
+    // ByteSize() is called then used during serialization.
+    // TODO(kenton):  Use atomic<int> when C++ supports it.
+    mutable int cached_size;
+
+    // The descriptor for this extension, if one exists and is known.  May be
+    // nullptr.  Must not be nullptr if the descriptor for the extension does
+    // not live in the same pool as the descriptor for the containing type.
+    const FieldDescriptor* descriptor;
+
+    // Some helper methods for operations on a single Extension.
+    uint8_t* InternalSerializeFieldWithCachedSizesToArray(
+        const MessageLite* extendee, const ExtensionSet* extension_set,
+        int number, uint8_t* target, io::EpsCopyOutputStream* stream) const;
+    uint8_t* InternalSerializeMessageSetItemWithCachedSizesToArray(
+        const MessageLite* extendee, const ExtensionSet* extension_set,
+        int number, uint8_t* target, io::EpsCopyOutputStream* stream) const;
+    size_t ByteSize(int number) const;
+    size_t MessageSetItemByteSize(int number) const;
+    void Clear();
+    int GetSize() const;
+    void Free();
+    size_t SpaceUsedExcludingSelfLong() const;
+    bool IsInitialized() const;
+  };
+
+  // The Extension struct is small enough to be passed by value, so we use it
+  // directly as the value type in mappings rather than use pointers.  We use
+  // sorted maps rather than hash-maps because we expect most ExtensionSets will
+  // only contain a small number of extension.  Also, we want AppendToList and
+  // deterministic serialization to order fields by field number.
+
+  struct KeyValue {
+    int first;
+    Extension second;
+
+    struct FirstComparator {
+      bool operator()(const KeyValue& lhs, const KeyValue& rhs) const {
+        return lhs.first < rhs.first;
+      }
+      bool operator()(const KeyValue& lhs, int key) const {
+        return lhs.first < key;
+      }
+      bool operator()(int key, const KeyValue& rhs) const {
+        return key < rhs.first;
+      }
+    };
+  };
+
+  typedef std::map<int, Extension> LargeMap;
+
+  // Wrapper API that switches between flat-map and LargeMap.
+
+  // Finds a key (if present) in the ExtensionSet.
+  const Extension* FindOrNull(int key) const;
+  Extension* FindOrNull(int key);
+
+  // Helper-functions that only inspect the LargeMap.
+  const Extension* FindOrNullInLargeMap(int key) const;
+  Extension* FindOrNullInLargeMap(int key);
+
+  // Inserts a new (key, Extension) into the ExtensionSet (and returns true), or
+  // finds the already-existing Extension for that key (returns false).
+  // The Extension* will point to the new-or-found Extension.
+  std::pair<Extension*, bool> Insert(int key);
+
+  // Grows the flat_capacity_.
+  // If flat_capacity_ > kMaximumFlatCapacity, converts to LargeMap.
+  void GrowCapacity(size_t minimum_new_capacity);
+  static constexpr uint16_t kMaximumFlatCapacity = 256;
+  bool is_large() const { return static_cast<int16_t>(flat_size_) < 0; }
+
+  // Removes a key from the ExtensionSet.
+  void Erase(int key);
+
+  size_t Size() const {
+    return PROTOBUF_PREDICT_FALSE(is_large()) ? map_.large->size() : flat_size_;
+  }
+
+  // Similar to std::for_each.
+  // Each Iterator is decomposed into ->first and ->second fields, so
+  // that the KeyValueFunctor can be agnostic vis-a-vis KeyValue-vs-std::pair.
+  template <typename Iterator, typename KeyValueFunctor>
+  static KeyValueFunctor ForEach(Iterator begin, Iterator end,
+                                 KeyValueFunctor func) {
+    for (Iterator it = begin; it != end; ++it) func(it->first, it->second);
+    return func;
+  }
+
+  // Applies a functor to the <int, Extension&> pairs in sorted order.
+  template <typename KeyValueFunctor>
+  KeyValueFunctor ForEach(KeyValueFunctor func) {
+    if (PROTOBUF_PREDICT_FALSE(is_large())) {
+      return ForEach(map_.large->begin(), map_.large->end(), std::move(func));
+    }
+    return ForEach(flat_begin(), flat_end(), std::move(func));
+  }
+
+  // Applies a functor to the <int, const Extension&> pairs in sorted order.
+  template <typename KeyValueFunctor>
+  KeyValueFunctor ForEach(KeyValueFunctor func) const {
+    if (PROTOBUF_PREDICT_FALSE(is_large())) {
+      return ForEach(map_.large->begin(), map_.large->end(), std::move(func));
+    }
+    return ForEach(flat_begin(), flat_end(), std::move(func));
+  }
+
+  // Merges existing Extension from other_extension
+  void InternalExtensionMergeFrom(const MessageLite* extendee, int number,
+                                  const Extension& other_extension,
+                                  Arena* other_arena);
+
+  inline static bool is_packable(WireFormatLite::WireType type) {
+    switch (type) {
+      case WireFormatLite::WIRETYPE_VARINT:
+      case WireFormatLite::WIRETYPE_FIXED64:
+      case WireFormatLite::WIRETYPE_FIXED32:
+        return true;
+      case WireFormatLite::WIRETYPE_LENGTH_DELIMITED:
+      case WireFormatLite::WIRETYPE_START_GROUP:
+      case WireFormatLite::WIRETYPE_END_GROUP:
+        return false;
+
+        // Do not add a default statement. Let the compiler complain when
+        // someone
+        // adds a new wire type.
+    }
+    PROTOBUF_ASSUME(false);  // switch handles all possible enum values
+    return false;
+  }
+
+  // Returns true and fills field_number and extension if extension is found.
+  // Note to support packed repeated field compatibility, it also fills whether
+  // the tag on wire is packed, which can be different from
+  // extension->is_packed (whether packed=true is specified).
+  template <typename ExtensionFinder>
+  bool FindExtensionInfoFromTag(uint32_t tag, ExtensionFinder* extension_finder,
+                                int* field_number, ExtensionInfo* extension,
+                                bool* was_packed_on_wire) {
+    *field_number = WireFormatLite::GetTagFieldNumber(tag);
+    WireFormatLite::WireType wire_type = WireFormatLite::GetTagWireType(tag);
+    return FindExtensionInfoFromFieldNumber(wire_type, *field_number,
+                                            extension_finder, extension,
+                                            was_packed_on_wire);
+  }
+
+  // Returns true and fills extension if extension is found.
+  // Note to support packed repeated field compatibility, it also fills whether
+  // the tag on wire is packed, which can be different from
+  // extension->is_packed (whether packed=true is specified).
+  template <typename ExtensionFinder>
+  bool FindExtensionInfoFromFieldNumber(int wire_type, int field_number,
+                                        ExtensionFinder* extension_finder,
+                                        ExtensionInfo* extension,
+                                        bool* was_packed_on_wire) const {
+    if (!extension_finder->Find(field_number, extension)) {
+      return false;
+    }
+
+    GOOGLE_DCHECK(extension->type > 0 &&
+           extension->type <= WireFormatLite::MAX_FIELD_TYPE);
+    auto real_type = static_cast<WireFormatLite::FieldType>(extension->type);
+
+    WireFormatLite::WireType expected_wire_type =
+        WireFormatLite::WireTypeForFieldType(real_type);
+
+    // Check if this is a packed field.
+    *was_packed_on_wire = false;
+    if (extension->is_repeated &&
+        wire_type == WireFormatLite::WIRETYPE_LENGTH_DELIMITED &&
+        is_packable(expected_wire_type)) {
+      *was_packed_on_wire = true;
+      return true;
+    }
+    // Otherwise the wire type must match.
+    return expected_wire_type == wire_type;
+  }
+
+  // Find the prototype for a LazyMessage from the extension registry. Returns
+  // null if the extension is not found.
+  const MessageLite* GetPrototypeForLazyMessage(const MessageLite* extendee,
+                                                int number) const;
+
+  // Returns true if extension is present and lazy.
+  bool HasLazy(int number) const;
+
+  // Gets the extension with the given number, creating it if it does not
+  // already exist.  Returns true if the extension did not already exist.
+  bool MaybeNewExtension(int number, const FieldDescriptor* descriptor,
+                         Extension** result);
+
+  // Gets the repeated extension for the given descriptor, creating it if
+  // it does not exist.
+  Extension* MaybeNewRepeatedExtension(const FieldDescriptor* descriptor);
+
+  bool FindExtension(int wire_type, uint32_t field, const MessageLite* extendee,
+                     const internal::ParseContext* /*ctx*/,
+                     ExtensionInfo* extension, bool* was_packed_on_wire) {
+    GeneratedExtensionFinder finder(extendee);
+    return FindExtensionInfoFromFieldNumber(wire_type, field, &finder,
+                                            extension, was_packed_on_wire);
+  }
+  inline bool FindExtension(int wire_type, uint32_t field,
+                            const Message* extendee,
+                            const internal::ParseContext* ctx,
+                            ExtensionInfo* extension, bool* was_packed_on_wire);
+  // Used for MessageSet only
+  const char* ParseFieldMaybeLazily(uint64_t tag, const char* ptr,
+                                    const MessageLite* extendee,
+                                    internal::InternalMetadata* metadata,
+                                    internal::ParseContext* ctx) {
+    // Lite MessageSet doesn't implement lazy.
+    return ParseField(tag, ptr, extendee, metadata, ctx);
+  }
+  const char* ParseFieldMaybeLazily(uint64_t tag, const char* ptr,
+                                    const Message* extendee,
+                                    internal::InternalMetadata* metadata,
+                                    internal::ParseContext* ctx);
+  const char* ParseMessageSetItem(const char* ptr, const MessageLite* extendee,
+                                  internal::InternalMetadata* metadata,
+                                  internal::ParseContext* ctx);
+  const char* ParseMessageSetItem(const char* ptr, const Message* extendee,
+                                  internal::InternalMetadata* metadata,
+                                  internal::ParseContext* ctx);
+
+  // Implemented in extension_set_inl.h to keep code out of the header file.
+  template <typename T>
+  const char* ParseFieldWithExtensionInfo(int number, bool was_packed_on_wire,
+                                          const ExtensionInfo& info,
+                                          internal::InternalMetadata* metadata,
+                                          const char* ptr,
+                                          internal::ParseContext* ctx);
+  template <typename Msg, typename T>
+  const char* ParseMessageSetItemTmpl(const char* ptr, const Msg* extendee,
+                                      internal::InternalMetadata* metadata,
+                                      internal::ParseContext* ctx);
+
+  // Hack:  RepeatedPtrFieldBase declares ExtensionSet as a friend.  This
+  //   friendship should automatically extend to ExtensionSet::Extension, but
+  //   unfortunately some older compilers (e.g. GCC 3.4.4) do not implement this
+  //   correctly.  So, we must provide helpers for calling methods of that
+  //   class.
+
+  // Defined in extension_set_heavy.cc.
+  static inline size_t RepeatedMessage_SpaceUsedExcludingSelfLong(
+      RepeatedPtrFieldBase* field);
+
+  KeyValue* flat_begin() {
+    assert(!is_large());
+    return map_.flat;
+  }
+  const KeyValue* flat_begin() const {
+    assert(!is_large());
+    return map_.flat;
+  }
+  KeyValue* flat_end() {
+    assert(!is_large());
+    return map_.flat + flat_size_;
+  }
+  const KeyValue* flat_end() const {
+    assert(!is_large());
+    return map_.flat + flat_size_;
+  }
+
+  Arena* arena_;
+
+  // Manual memory-management:
+  // map_.flat is an allocated array of flat_capacity_ elements.
+  // [map_.flat, map_.flat + flat_size_) is the currently-in-use prefix.
+  uint16_t flat_capacity_;
+  uint16_t flat_size_;  // negative int16_t(flat_size_) indicates is_large()
+  union AllocatedData {
+    KeyValue* flat;
+
+    // If flat_capacity_ > kMaximumFlatCapacity, switch to LargeMap,
+    // which guarantees O(n lg n) CPU but larger constant factors.
+    LargeMap* large;
+  } map_;
+
+  static void DeleteFlatMap(const KeyValue* flat, uint16_t flat_capacity);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionSet);
+};
+
+constexpr ExtensionSet::ExtensionSet()
+    : arena_(nullptr), flat_capacity_(0), flat_size_(0), map_{nullptr} {}
+
+// These are just for convenience...
+inline void ExtensionSet::SetString(int number, FieldType type,
+                                    std::string value,
+                                    const FieldDescriptor* descriptor) {
+  MutableString(number, type, descriptor)->assign(std::move(value));
+}
+inline void ExtensionSet::SetRepeatedString(int number, int index,
+                                            std::string value) {
+  MutableRepeatedString(number, index)->assign(std::move(value));
+}
+inline void ExtensionSet::AddString(int number, FieldType type,
+                                    std::string value,
+                                    const FieldDescriptor* descriptor) {
+  AddString(number, type, descriptor)->assign(std::move(value));
+}
+// ===================================================================
+// Glue for generated extension accessors
+
+// -------------------------------------------------------------------
+// Template magic
+
+// First we have a set of classes representing "type traits" for different
+// field types.  A type traits class knows how to implement basic accessors
+// for extensions of a particular type given an ExtensionSet.  The signature
+// for a type traits class looks like this:
+//
+//   class TypeTraits {
+//    public:
+//     typedef ? ConstType;
+//     typedef ? MutableType;
+//     // TypeTraits for singular fields and repeated fields will define the
+//     // symbol "Singular" or "Repeated" respectively. These two symbols will
+//     // be used in extension accessors to distinguish between singular
+//     // extensions and repeated extensions. If the TypeTraits for the passed
+//     // in extension doesn't have the expected symbol defined, it means the
+//     // user is passing a repeated extension to a singular accessor, or the
+//     // opposite. In that case the C++ compiler will generate an error
+//     // message "no matching member function" to inform the user.
+//     typedef ? Singular
+//     typedef ? Repeated
+//
+//     static inline ConstType Get(int number, const ExtensionSet& set);
+//     static inline void Set(int number, ConstType value, ExtensionSet* set);
+//     static inline MutableType Mutable(int number, ExtensionSet* set);
+//
+//     // Variants for repeated fields.
+//     static inline ConstType Get(int number, const ExtensionSet& set,
+//                                 int index);
+//     static inline void Set(int number, int index,
+//                            ConstType value, ExtensionSet* set);
+//     static inline MutableType Mutable(int number, int index,
+//                                       ExtensionSet* set);
+//     static inline void Add(int number, ConstType value, ExtensionSet* set);
+//     static inline MutableType Add(int number, ExtensionSet* set);
+//     This is used by the ExtensionIdentifier constructor to register
+//     the extension at dynamic initialization.
+//     template <typename ExtendeeT>
+//     static void Register(int number, FieldType type, bool is_packed);
+//   };
+//
+// Not all of these methods make sense for all field types.  For example, the
+// "Mutable" methods only make sense for strings and messages, and the
+// repeated methods only make sense for repeated types.  So, each type
+// traits class implements only the set of methods from this signature that it
+// actually supports.  This will cause a compiler error if the user tries to
+// access an extension using a method that doesn't make sense for its type.
+// For example, if "foo" is an extension of type "optional int32", then if you
+// try to write code like:
+//   my_message.MutableExtension(foo)
+// you will get a compile error because PrimitiveTypeTraits<int32_t> does not
+// have a "Mutable()" method.
+
+// -------------------------------------------------------------------
+// PrimitiveTypeTraits
+
+// Since the ExtensionSet has different methods for each primitive type,
+// we must explicitly define the methods of the type traits class for each
+// known type.
+template <typename Type>
+class PrimitiveTypeTraits {
+ public:
+  typedef Type ConstType;
+  typedef Type MutableType;
+  typedef PrimitiveTypeTraits<Type> Singular;
+
+  static inline ConstType Get(int number, const ExtensionSet& set,
+                              ConstType default_value);
+
+  static inline const ConstType* GetPtr(int number, const ExtensionSet& set,
+                                        const ConstType& default_value);
+  static inline void Set(int number, FieldType field_type, ConstType value,
+                         ExtensionSet* set);
+  template <typename ExtendeeT>
+  static void Register(int number, FieldType type, bool is_packed,
+                       LazyEagerVerifyFnType verify_func) {
+    ExtensionSet::RegisterExtension(&ExtendeeT::default_instance(), number,
+                                    type, false, is_packed, verify_func);
+  }
+};
+
+template <typename Type>
+class RepeatedPrimitiveTypeTraits {
+ public:
+  typedef Type ConstType;
+  typedef Type MutableType;
+  typedef RepeatedPrimitiveTypeTraits<Type> Repeated;
+
+  typedef RepeatedField<Type> RepeatedFieldType;
+
+  static inline Type Get(int number, const ExtensionSet& set, int index);
+  static inline const Type* GetPtr(int number, const ExtensionSet& set,
+                                   int index);
+  static inline const RepeatedField<ConstType>* GetRepeatedPtr(
+      int number, const ExtensionSet& set);
+  static inline void Set(int number, int index, Type value, ExtensionSet* set);
+  static inline void Add(int number, FieldType field_type, bool is_packed,
+                         Type value, ExtensionSet* set);
+
+  static inline const RepeatedField<ConstType>& GetRepeated(
+      int number, const ExtensionSet& set);
+  static inline RepeatedField<Type>* MutableRepeated(int number,
+                                                     FieldType field_type,
+                                                     bool is_packed,
+                                                     ExtensionSet* set);
+
+  static const RepeatedFieldType* GetDefaultRepeatedField();
+  template <typename ExtendeeT>
+  static void Register(int number, FieldType type, bool is_packed,
+                       LazyEagerVerifyFnType verify_func) {
+    ExtensionSet::RegisterExtension(&ExtendeeT::default_instance(), number,
+                                    type, true, is_packed, verify_func);
+  }
+};
+
+class PROTOBUF_EXPORT RepeatedPrimitiveDefaults {
+ private:
+  template <typename Type>
+  friend class RepeatedPrimitiveTypeTraits;
+  static const RepeatedPrimitiveDefaults* default_instance();
+  RepeatedField<int32_t> default_repeated_field_int32_t_;
+  RepeatedField<int64_t> default_repeated_field_int64_t_;
+  RepeatedField<uint32_t> default_repeated_field_uint32_t_;
+  RepeatedField<uint64_t> default_repeated_field_uint64_t_;
+  RepeatedField<double> default_repeated_field_double_;
+  RepeatedField<float> default_repeated_field_float_;
+  RepeatedField<bool> default_repeated_field_bool_;
+};
+
+#define PROTOBUF_DEFINE_PRIMITIVE_TYPE(TYPE, METHOD)                           \
+  template <>                                                                  \
+  inline TYPE PrimitiveTypeTraits<TYPE>::Get(                                  \
+      int number, const ExtensionSet& set, TYPE default_value) {               \
+    return set.Get##METHOD(number, default_value);                             \
+  }                                                                            \
+  template <>                                                                  \
+  inline const TYPE* PrimitiveTypeTraits<TYPE>::GetPtr(                        \
+      int number, const ExtensionSet& set, const TYPE& default_value) {        \
+    return &set.GetRef##METHOD(number, default_value);                         \
+  }                                                                            \
+  template <>                                                                  \
+  inline void PrimitiveTypeTraits<TYPE>::Set(int number, FieldType field_type, \
+                                             TYPE value, ExtensionSet* set) {  \
+    set->Set##METHOD(number, field_type, value, nullptr);                      \
+  }                                                                            \
+                                                                               \
+  template <>                                                                  \
+  inline TYPE RepeatedPrimitiveTypeTraits<TYPE>::Get(                          \
+      int number, const ExtensionSet& set, int index) {                        \
+    return set.GetRepeated##METHOD(number, index);                             \
+  }                                                                            \
+  template <>                                                                  \
+  inline const TYPE* RepeatedPrimitiveTypeTraits<TYPE>::GetPtr(                \
+      int number, const ExtensionSet& set, int index) {                        \
+    return &set.GetRefRepeated##METHOD(number, index);                         \
+  }                                                                            \
+  template <>                                                                  \
+  inline void RepeatedPrimitiveTypeTraits<TYPE>::Set(                          \
+      int number, int index, TYPE value, ExtensionSet* set) {                  \
+    set->SetRepeated##METHOD(number, index, value);                            \
+  }                                                                            \
+  template <>                                                                  \
+  inline void RepeatedPrimitiveTypeTraits<TYPE>::Add(                          \
+      int number, FieldType field_type, bool is_packed, TYPE value,            \
+      ExtensionSet* set) {                                                     \
+    set->Add##METHOD(number, field_type, is_packed, value, nullptr);           \
+  }                                                                            \
+  template <>                                                                  \
+  inline const RepeatedField<TYPE>*                                            \
+  RepeatedPrimitiveTypeTraits<TYPE>::GetDefaultRepeatedField() {               \
+    return &RepeatedPrimitiveDefaults::default_instance()                      \
+                ->default_repeated_field_##TYPE##_;                            \
+  }                                                                            \
+  template <>                                                                  \
+  inline const RepeatedField<TYPE>&                                            \
+  RepeatedPrimitiveTypeTraits<TYPE>::GetRepeated(int number,                   \
+                                                 const ExtensionSet& set) {    \
+    return *reinterpret_cast<const RepeatedField<TYPE>*>(                      \
+        set.GetRawRepeatedField(number, GetDefaultRepeatedField()));           \
+  }                                                                            \
+  template <>                                                                  \
+  inline const RepeatedField<TYPE>*                                            \
+  RepeatedPrimitiveTypeTraits<TYPE>::GetRepeatedPtr(int number,                \
+                                                    const ExtensionSet& set) { \
+    return &GetRepeated(number, set);                                          \
+  }                                                                            \
+  template <>                                                                  \
+  inline RepeatedField<TYPE>*                                                  \
+  RepeatedPrimitiveTypeTraits<TYPE>::MutableRepeated(                          \
+      int number, FieldType field_type, bool is_packed, ExtensionSet* set) {   \
+    return reinterpret_cast<RepeatedField<TYPE>*>(                             \
+        set->MutableRawRepeatedField(number, field_type, is_packed, nullptr)); \
+  }
+
+PROTOBUF_DEFINE_PRIMITIVE_TYPE(int32_t, Int32)
+PROTOBUF_DEFINE_PRIMITIVE_TYPE(int64_t, Int64)
+PROTOBUF_DEFINE_PRIMITIVE_TYPE(uint32_t, UInt32)
+PROTOBUF_DEFINE_PRIMITIVE_TYPE(uint64_t, UInt64)
+PROTOBUF_DEFINE_PRIMITIVE_TYPE(float, Float)
+PROTOBUF_DEFINE_PRIMITIVE_TYPE(double, Double)
+PROTOBUF_DEFINE_PRIMITIVE_TYPE(bool, Bool)
+
+#undef PROTOBUF_DEFINE_PRIMITIVE_TYPE
+
+// -------------------------------------------------------------------
+// StringTypeTraits
+
+// Strings support both Set() and Mutable().
+class PROTOBUF_EXPORT StringTypeTraits {
+ public:
+  typedef const std::string& ConstType;
+  typedef std::string* MutableType;
+  typedef StringTypeTraits Singular;
+
+  static inline const std::string& Get(int number, const ExtensionSet& set,
+                                       ConstType default_value) {
+    return set.GetString(number, default_value);
+  }
+  static inline const std::string* GetPtr(int number, const ExtensionSet& set,
+                                          ConstType default_value) {
+    return &Get(number, set, default_value);
+  }
+  static inline void Set(int number, FieldType field_type,
+                         const std::string& value, ExtensionSet* set) {
+    set->SetString(number, field_type, value, nullptr);
+  }
+  static inline std::string* Mutable(int number, FieldType field_type,
+                                     ExtensionSet* set) {
+    return set->MutableString(number, field_type, nullptr);
+  }
+  template <typename ExtendeeT>
+  static void Register(int number, FieldType type, bool is_packed,
+                       LazyEagerVerifyFnType verify_func) {
+    ExtensionSet::RegisterExtension(&ExtendeeT::default_instance(), number,
+                                    type, false, is_packed, verify_func);
+  }
+};
+
+class PROTOBUF_EXPORT RepeatedStringTypeTraits {
+ public:
+  typedef const std::string& ConstType;
+  typedef std::string* MutableType;
+  typedef RepeatedStringTypeTraits Repeated;
+
+  typedef RepeatedPtrField<std::string> RepeatedFieldType;
+
+  static inline const std::string& Get(int number, const ExtensionSet& set,
+                                       int index) {
+    return set.GetRepeatedString(number, index);
+  }
+  static inline const std::string* GetPtr(int number, const ExtensionSet& set,
+                                          int index) {
+    return &Get(number, set, index);
+  }
+  static inline const RepeatedPtrField<std::string>* GetRepeatedPtr(
+      int number, const ExtensionSet& set) {
+    return &GetRepeated(number, set);
+  }
+  static inline void Set(int number, int index, const std::string& value,
+                         ExtensionSet* set) {
+    set->SetRepeatedString(number, index, value);
+  }
+  static inline std::string* Mutable(int number, int index, ExtensionSet* set) {
+    return set->MutableRepeatedString(number, index);
+  }
+  static inline void Add(int number, FieldType field_type, bool /*is_packed*/,
+                         const std::string& value, ExtensionSet* set) {
+    set->AddString(number, field_type, value, nullptr);
+  }
+  static inline std::string* Add(int number, FieldType field_type,
+                                 ExtensionSet* set) {
+    return set->AddString(number, field_type, nullptr);
+  }
+  static inline const RepeatedPtrField<std::string>& GetRepeated(
+      int number, const ExtensionSet& set) {
+    return *reinterpret_cast<const RepeatedPtrField<std::string>*>(
+        set.GetRawRepeatedField(number, GetDefaultRepeatedField()));
+  }
+
+  static inline RepeatedPtrField<std::string>* MutableRepeated(
+      int number, FieldType field_type, bool is_packed, ExtensionSet* set) {
+    return reinterpret_cast<RepeatedPtrField<std::string>*>(
+        set->MutableRawRepeatedField(number, field_type, is_packed, nullptr));
+  }
+
+  static const RepeatedFieldType* GetDefaultRepeatedField();
+
+  template <typename ExtendeeT>
+  static void Register(int number, FieldType type, bool is_packed,
+                       LazyEagerVerifyFnType fn) {
+    ExtensionSet::RegisterExtension(&ExtendeeT::default_instance(), number,
+                                    type, true, is_packed, fn);
+  }
+
+ private:
+  static void InitializeDefaultRepeatedFields();
+  static void DestroyDefaultRepeatedFields();
+};
+
+// -------------------------------------------------------------------
+// EnumTypeTraits
+
+// ExtensionSet represents enums using integers internally, so we have to
+// static_cast around.
+template <typename Type, bool IsValid(int)>
+class EnumTypeTraits {
+ public:
+  typedef Type ConstType;
+  typedef Type MutableType;
+  typedef EnumTypeTraits<Type, IsValid> Singular;
+
+  static inline ConstType Get(int number, const ExtensionSet& set,
+                              ConstType default_value) {
+    return static_cast<Type>(set.GetEnum(number, default_value));
+  }
+  static inline const ConstType* GetPtr(int number, const ExtensionSet& set,
+                                        const ConstType& default_value) {
+    return reinterpret_cast<const Type*>(
+        &set.GetRefEnum(number, default_value));
+  }
+  static inline void Set(int number, FieldType field_type, ConstType value,
+                         ExtensionSet* set) {
+    GOOGLE_DCHECK(IsValid(value));
+    set->SetEnum(number, field_type, value, nullptr);
+  }
+  template <typename ExtendeeT>
+  static void Register(int number, FieldType type, bool is_packed,
+                       LazyEagerVerifyFnType fn) {
+    ExtensionSet::RegisterEnumExtension(&ExtendeeT::default_instance(), number,
+                                        type, false, is_packed, IsValid);
+  }
+};
+
+template <typename Type, bool IsValid(int)>
+class RepeatedEnumTypeTraits {
+ public:
+  typedef Type ConstType;
+  typedef Type MutableType;
+  typedef RepeatedEnumTypeTraits<Type, IsValid> Repeated;
+
+  typedef RepeatedField<Type> RepeatedFieldType;
+
+  static inline ConstType Get(int number, const ExtensionSet& set, int index) {
+    return static_cast<Type>(set.GetRepeatedEnum(number, index));
+  }
+  static inline const ConstType* GetPtr(int number, const ExtensionSet& set,
+                                        int index) {
+    return reinterpret_cast<const Type*>(
+        &set.GetRefRepeatedEnum(number, index));
+  }
+  static inline void Set(int number, int index, ConstType value,
+                         ExtensionSet* set) {
+    GOOGLE_DCHECK(IsValid(value));
+    set->SetRepeatedEnum(number, index, value);
+  }
+  static inline void Add(int number, FieldType field_type, bool is_packed,
+                         ConstType value, ExtensionSet* set) {
+    GOOGLE_DCHECK(IsValid(value));
+    set->AddEnum(number, field_type, is_packed, value, nullptr);
+  }
+  static inline const RepeatedField<Type>& GetRepeated(
+      int number, const ExtensionSet& set) {
+    // Hack: the `Extension` struct stores a RepeatedField<int> for enums.
+    // RepeatedField<int> cannot implicitly convert to RepeatedField<EnumType>
+    // so we need to do some casting magic. See message.h for similar
+    // contortions for non-extension fields.
+    return *reinterpret_cast<const RepeatedField<Type>*>(
+        set.GetRawRepeatedField(number, GetDefaultRepeatedField()));
+  }
+  static inline const RepeatedField<Type>* GetRepeatedPtr(
+      int number, const ExtensionSet& set) {
+    return &GetRepeated(number, set);
+  }
+  static inline RepeatedField<Type>* MutableRepeated(int number,
+                                                     FieldType field_type,
+                                                     bool is_packed,
+                                                     ExtensionSet* set) {
+    return reinterpret_cast<RepeatedField<Type>*>(
+        set->MutableRawRepeatedField(number, field_type, is_packed, nullptr));
+  }
+
+  static const RepeatedFieldType* GetDefaultRepeatedField() {
+    // Hack: as noted above, repeated enum fields are internally stored as a
+    // RepeatedField<int>. We need to be able to instantiate global static
+    // objects to return as default (empty) repeated fields on non-existent
+    // extensions. We would not be able to know a-priori all of the enum types
+    // (values of |Type|) to instantiate all of these, so we just re-use
+    // int32_t's default repeated field object.
+    return reinterpret_cast<const RepeatedField<Type>*>(
+        RepeatedPrimitiveTypeTraits<int32_t>::GetDefaultRepeatedField());
+  }
+  template <typename ExtendeeT>
+  static void Register(int number, FieldType type, bool is_packed,
+                       LazyEagerVerifyFnType fn) {
+    ExtensionSet::RegisterEnumExtension(&ExtendeeT::default_instance(), number,
+                                        type, true, is_packed, IsValid);
+  }
+};
+
+// -------------------------------------------------------------------
+// MessageTypeTraits
+
+// ExtensionSet guarantees that when manipulating extensions with message
+// types, the implementation used will be the compiled-in class representing
+// that type.  So, we can static_cast down to the exact type we expect.
+template <typename Type>
+class MessageTypeTraits {
+ public:
+  typedef const Type& ConstType;
+  typedef Type* MutableType;
+  typedef MessageTypeTraits<Type> Singular;
+
+  static inline ConstType Get(int number, const ExtensionSet& set,
+                              ConstType default_value) {
+    return static_cast<const Type&>(set.GetMessage(number, default_value));
+  }
+  static inline std::nullptr_t GetPtr(int /* number */,
+                                      const ExtensionSet& /* set */,
+                                      ConstType /* default_value */) {
+    // Cannot be implemented because of forward declared messages?
+    return nullptr;
+  }
+  static inline MutableType Mutable(int number, FieldType field_type,
+                                    ExtensionSet* set) {
+    return static_cast<Type*>(set->MutableMessage(
+        number, field_type, Type::default_instance(), nullptr));
+  }
+  static inline void SetAllocated(int number, FieldType field_type,
+                                  MutableType message, ExtensionSet* set) {
+    set->SetAllocatedMessage(number, field_type, nullptr, message);
+  }
+  static inline void UnsafeArenaSetAllocated(int number, FieldType field_type,
+                                             MutableType message,
+                                             ExtensionSet* set) {
+    set->UnsafeArenaSetAllocatedMessage(number, field_type, nullptr, message);
+  }
+  PROTOBUF_NODISCARD static inline MutableType Release(
+      int number, FieldType /* field_type */, ExtensionSet* set) {
+    return static_cast<Type*>(
+        set->ReleaseMessage(number, Type::default_instance()));
+  }
+  static inline MutableType UnsafeArenaRelease(int number,
+                                               FieldType /* field_type */,
+                                               ExtensionSet* set) {
+    return static_cast<Type*>(
+        set->UnsafeArenaReleaseMessage(number, Type::default_instance()));
+  }
+  template <typename ExtendeeT>
+  static void Register(int number, FieldType type, bool is_packed,
+                       LazyEagerVerifyFnType fn) {
+    ExtensionSet::RegisterMessageExtension(&ExtendeeT::default_instance(),
+                                           number, type, false, is_packed,
+                                           &Type::default_instance(), fn);
+  }
+};
+
+// Used by WireFormatVerify to extract the verify function from the registry.
+LazyEagerVerifyFnType FindExtensionLazyEagerVerifyFn(
+    const MessageLite* extendee, int number);
+
+// forward declaration.
+class RepeatedMessageGenericTypeTraits;
+
+template <typename Type>
+class RepeatedMessageTypeTraits {
+ public:
+  typedef const Type& ConstType;
+  typedef Type* MutableType;
+  typedef RepeatedMessageTypeTraits<Type> Repeated;
+
+  typedef RepeatedPtrField<Type> RepeatedFieldType;
+
+  static inline ConstType Get(int number, const ExtensionSet& set, int index) {
+    return static_cast<const Type&>(set.GetRepeatedMessage(number, index));
+  }
+  static inline std::nullptr_t GetPtr(int /* number */,
+                                      const ExtensionSet& /* set */,
+                                      int /* index */) {
+    // Cannot be implemented because of forward declared messages?
+    return nullptr;
+  }
+  static inline std::nullptr_t GetRepeatedPtr(int /* number */,
+                                              const ExtensionSet& /* set */) {
+    // Cannot be implemented because of forward declared messages?
+    return nullptr;
+  }
+  static inline MutableType Mutable(int number, int index, ExtensionSet* set) {
+    return static_cast<Type*>(set->MutableRepeatedMessage(number, index));
+  }
+  static inline MutableType Add(int number, FieldType field_type,
+                                ExtensionSet* set) {
+    return static_cast<Type*>(
+        set->AddMessage(number, field_type, Type::default_instance(), nullptr));
+  }
+  static inline const RepeatedPtrField<Type>& GetRepeated(
+      int number, const ExtensionSet& set) {
+    // See notes above in RepeatedEnumTypeTraits::GetRepeated(): same
+    // casting hack applies here, because a RepeatedPtrField<MessageLite>
+    // cannot naturally become a RepeatedPtrType<Type> even though Type is
+    // presumably a message. google::protobuf::Message goes through similar contortions
+    // with a reinterpret_cast<>.
+    return *reinterpret_cast<const RepeatedPtrField<Type>*>(
+        set.GetRawRepeatedField(number, GetDefaultRepeatedField()));
+  }
+  static inline RepeatedPtrField<Type>* MutableRepeated(int number,
+                                                        FieldType field_type,
+                                                        bool is_packed,
+                                                        ExtensionSet* set) {
+    return reinterpret_cast<RepeatedPtrField<Type>*>(
+        set->MutableRawRepeatedField(number, field_type, is_packed, nullptr));
+  }
+
+  static const RepeatedFieldType* GetDefaultRepeatedField();
+  template <typename ExtendeeT>
+  static void Register(int number, FieldType type, bool is_packed,
+                       LazyEagerVerifyFnType fn) {
+    ExtensionSet::RegisterMessageExtension(&ExtendeeT::default_instance(),
+                                           number, type, true, is_packed,
+                                           &Type::default_instance(), fn);
+  }
+};
+
+template <typename Type>
+inline const typename RepeatedMessageTypeTraits<Type>::RepeatedFieldType*
+RepeatedMessageTypeTraits<Type>::GetDefaultRepeatedField() {
+  static auto instance = OnShutdownDelete(new RepeatedFieldType);
+  return instance;
+}
+
+// -------------------------------------------------------------------
+// ExtensionIdentifier
+
+// This is the type of actual extension objects.  E.g. if you have:
+//   extend Foo {
+//     optional int32 bar = 1234;
+//   }
+// then "bar" will be defined in C++ as:
+//   ExtensionIdentifier<Foo, PrimitiveTypeTraits<int32_t>, 5, false> bar(1234);
+//
+// Note that we could, in theory, supply the field number as a template
+// parameter, and thus make an instance of ExtensionIdentifier have no
+// actual contents.  However, if we did that, then using an extension
+// identifier would not necessarily cause the compiler to output any sort
+// of reference to any symbol defined in the extension's .pb.o file.  Some
+// linkers will actually drop object files that are not explicitly referenced,
+// but that would be bad because it would cause this extension to not be
+// registered at static initialization, and therefore using it would crash.
+
+template <typename ExtendeeType, typename TypeTraitsType, FieldType field_type,
+          bool is_packed>
+class ExtensionIdentifier {
+ public:
+  typedef TypeTraitsType TypeTraits;
+  typedef ExtendeeType Extendee;
+
+  ExtensionIdentifier(int number, typename TypeTraits::ConstType default_value,
+                      LazyEagerVerifyFnType verify_func = nullptr)
+      : number_(number), default_value_(default_value) {
+    Register(number, verify_func);
+  }
+  inline int number() const { return number_; }
+  typename TypeTraits::ConstType default_value() const {
+    return default_value_;
+  }
+
+  static void Register(int number, LazyEagerVerifyFnType verify_func) {
+    TypeTraits::template Register<ExtendeeType>(number, field_type, is_packed,
+                                                verify_func);
+  }
+
+  typename TypeTraits::ConstType const& default_value_ref() const {
+    return default_value_;
+  }
+
+ private:
+  const int number_;
+  typename TypeTraits::ConstType default_value_;
+};
+
+// -------------------------------------------------------------------
+// Generated accessors
+
+
+// Used to retrieve a lazy extension, may return nullptr in some environments.
+extern PROTOBUF_ATTRIBUTE_WEAK ExtensionSet::LazyMessageExtension*
+MaybeCreateLazyExtension(Arena* arena);
+
+}  // namespace internal
+
+// Call this function to ensure that this extensions's reflection is linked into
+// the binary:
+//
+//   google::protobuf::LinkExtensionReflection(Foo::my_extension);
+//
+// This will ensure that the following lookup will succeed:
+//
+//   DescriptorPool::generated_pool()->FindExtensionByName("Foo.my_extension");
+//
+// This is often relevant for parsing extensions in text mode.
+//
+// As a side-effect, it will also guarantee that anything else from the same
+// .proto file will also be available for lookup in the generated pool.
+//
+// This function does not actually register the extension, so it does not need
+// to be called before the lookup.  However it does need to occur in a function
+// that cannot be stripped from the binary (ie. it must be reachable from main).
+//
+// Best practice is to call this function as close as possible to where the
+// reflection is actually needed.  This function is very cheap to call, so you
+// should not need to worry about its runtime overhead except in tight loops (on
+// x86-64 it compiles into two "mov" instructions).
+template <typename ExtendeeType, typename TypeTraitsType,
+          internal::FieldType field_type, bool is_packed>
+void LinkExtensionReflection(
+    const google::protobuf::internal::ExtensionIdentifier<
+        ExtendeeType, TypeTraitsType, field_type, is_packed>& extension) {
+  internal::StrongReference(extension);
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_EXTENSION_SET_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/extension_set_inl.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/extension_set_inl.h
new file mode 100644
index 0000000..50c04cd
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/extension_set_inl.h
@@ -0,0 +1,285 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_EXTENSION_SET_INL_H__
+#define GOOGLE_PROTOBUF_EXTENSION_SET_INL_H__
+
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/parse_context.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+template <typename T>
+const char* ExtensionSet::ParseFieldWithExtensionInfo(
+    int number, bool was_packed_on_wire, const ExtensionInfo& extension,
+    InternalMetadata* metadata, const char* ptr, internal::ParseContext* ctx) {
+  if (was_packed_on_wire) {
+    switch (extension.type) {
+#define HANDLE_TYPE(UPPERCASE, CPP_CAMELCASE)                                \
+  case WireFormatLite::TYPE_##UPPERCASE:                                     \
+    return internal::Packed##CPP_CAMELCASE##Parser(                          \
+        MutableRawRepeatedField(number, extension.type, extension.is_packed, \
+                                extension.descriptor),                       \
+        ptr, ctx);
+      HANDLE_TYPE(INT32, Int32);
+      HANDLE_TYPE(INT64, Int64);
+      HANDLE_TYPE(UINT32, UInt32);
+      HANDLE_TYPE(UINT64, UInt64);
+      HANDLE_TYPE(SINT32, SInt32);
+      HANDLE_TYPE(SINT64, SInt64);
+      HANDLE_TYPE(FIXED32, Fixed32);
+      HANDLE_TYPE(FIXED64, Fixed64);
+      HANDLE_TYPE(SFIXED32, SFixed32);
+      HANDLE_TYPE(SFIXED64, SFixed64);
+      HANDLE_TYPE(FLOAT, Float);
+      HANDLE_TYPE(DOUBLE, Double);
+      HANDLE_TYPE(BOOL, Bool);
+#undef HANDLE_TYPE
+
+      case WireFormatLite::TYPE_ENUM:
+        return internal::PackedEnumParserArg<T>(
+            MutableRawRepeatedField(number, extension.type, extension.is_packed,
+                                    extension.descriptor),
+            ptr, ctx, extension.enum_validity_check.func,
+            extension.enum_validity_check.arg, metadata, number);
+      case WireFormatLite::TYPE_STRING:
+      case WireFormatLite::TYPE_BYTES:
+      case WireFormatLite::TYPE_GROUP:
+      case WireFormatLite::TYPE_MESSAGE:
+        GOOGLE_LOG(FATAL) << "Non-primitive types can't be packed.";
+        break;
+    }
+  } else {
+    switch (extension.type) {
+#define HANDLE_VARINT_TYPE(UPPERCASE, CPP_CAMELCASE)                        \
+  case WireFormatLite::TYPE_##UPPERCASE: {                                  \
+    uint64_t value;                                                         \
+    ptr = VarintParse(ptr, &value);                                         \
+    GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);                                    \
+    if (extension.is_repeated) {                                            \
+      Add##CPP_CAMELCASE(number, WireFormatLite::TYPE_##UPPERCASE,          \
+                         extension.is_packed, value, extension.descriptor); \
+    } else {                                                                \
+      Set##CPP_CAMELCASE(number, WireFormatLite::TYPE_##UPPERCASE, value,   \
+                         extension.descriptor);                             \
+    }                                                                       \
+  } break
+
+      HANDLE_VARINT_TYPE(INT32, Int32);
+      HANDLE_VARINT_TYPE(INT64, Int64);
+      HANDLE_VARINT_TYPE(UINT32, UInt32);
+      HANDLE_VARINT_TYPE(UINT64, UInt64);
+      HANDLE_VARINT_TYPE(BOOL, Bool);
+#undef HANDLE_VARINT_TYPE
+#define HANDLE_SVARINT_TYPE(UPPERCASE, CPP_CAMELCASE, SIZE)                 \
+  case WireFormatLite::TYPE_##UPPERCASE: {                                  \
+    uint64_t val;                                                           \
+    ptr = VarintParse(ptr, &val);                                           \
+    GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);                                    \
+    auto value = WireFormatLite::ZigZagDecode##SIZE(val);                   \
+    if (extension.is_repeated) {                                            \
+      Add##CPP_CAMELCASE(number, WireFormatLite::TYPE_##UPPERCASE,          \
+                         extension.is_packed, value, extension.descriptor); \
+    } else {                                                                \
+      Set##CPP_CAMELCASE(number, WireFormatLite::TYPE_##UPPERCASE, value,   \
+                         extension.descriptor);                             \
+    }                                                                       \
+  } break
+
+      HANDLE_SVARINT_TYPE(SINT32, Int32, 32);
+      HANDLE_SVARINT_TYPE(SINT64, Int64, 64);
+#undef HANDLE_SVARINT_TYPE
+#define HANDLE_FIXED_TYPE(UPPERCASE, CPP_CAMELCASE, CPPTYPE)                \
+  case WireFormatLite::TYPE_##UPPERCASE: {                                  \
+    auto value = UnalignedLoad<CPPTYPE>(ptr);                               \
+    ptr += sizeof(CPPTYPE);                                                 \
+    if (extension.is_repeated) {                                            \
+      Add##CPP_CAMELCASE(number, WireFormatLite::TYPE_##UPPERCASE,          \
+                         extension.is_packed, value, extension.descriptor); \
+    } else {                                                                \
+      Set##CPP_CAMELCASE(number, WireFormatLite::TYPE_##UPPERCASE, value,   \
+                         extension.descriptor);                             \
+    }                                                                       \
+  } break
+
+      HANDLE_FIXED_TYPE(FIXED32, UInt32, uint32_t);
+      HANDLE_FIXED_TYPE(FIXED64, UInt64, uint64_t);
+      HANDLE_FIXED_TYPE(SFIXED32, Int32, int32_t);
+      HANDLE_FIXED_TYPE(SFIXED64, Int64, int64_t);
+      HANDLE_FIXED_TYPE(FLOAT, Float, float);
+      HANDLE_FIXED_TYPE(DOUBLE, Double, double);
+#undef HANDLE_FIXED_TYPE
+
+      case WireFormatLite::TYPE_ENUM: {
+        uint64_t val;
+        ptr = VarintParse(ptr, &val);
+        GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+        int value = val;
+
+        if (!extension.enum_validity_check.func(
+                extension.enum_validity_check.arg, value)) {
+          WriteVarint(number, val, metadata->mutable_unknown_fields<T>());
+        } else if (extension.is_repeated) {
+          AddEnum(number, WireFormatLite::TYPE_ENUM, extension.is_packed, value,
+                  extension.descriptor);
+        } else {
+          SetEnum(number, WireFormatLite::TYPE_ENUM, value,
+                  extension.descriptor);
+        }
+        break;
+      }
+
+      case WireFormatLite::TYPE_BYTES:
+      case WireFormatLite::TYPE_STRING: {
+        std::string* value =
+            extension.is_repeated
+                ? AddString(number, WireFormatLite::TYPE_STRING,
+                            extension.descriptor)
+                : MutableString(number, WireFormatLite::TYPE_STRING,
+                                extension.descriptor);
+        int size = ReadSize(&ptr);
+        GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+        return ctx->ReadString(ptr, size, value);
+      }
+
+      case WireFormatLite::TYPE_GROUP: {
+        MessageLite* value =
+            extension.is_repeated
+                ? AddMessage(number, WireFormatLite::TYPE_GROUP,
+                             *extension.message_info.prototype,
+                             extension.descriptor)
+                : MutableMessage(number, WireFormatLite::TYPE_GROUP,
+                                 *extension.message_info.prototype,
+                                 extension.descriptor);
+        uint32_t tag = (number << 3) + WireFormatLite::WIRETYPE_START_GROUP;
+        return ctx->ParseGroup(value, ptr, tag);
+      }
+
+      case WireFormatLite::TYPE_MESSAGE: {
+        MessageLite* value =
+            extension.is_repeated
+                ? AddMessage(number, WireFormatLite::TYPE_MESSAGE,
+                             *extension.message_info.prototype,
+                             extension.descriptor)
+                : MutableMessage(number, WireFormatLite::TYPE_MESSAGE,
+                                 *extension.message_info.prototype,
+                                 extension.descriptor);
+        return ctx->ParseMessage(value, ptr);
+      }
+    }
+  }
+  return ptr;
+}
+
+template <typename Msg, typename T>
+const char* ExtensionSet::ParseMessageSetItemTmpl(
+    const char* ptr, const Msg* extendee, internal::InternalMetadata* metadata,
+    internal::ParseContext* ctx) {
+  std::string payload;
+  uint32_t type_id = 0;
+  enum class State { kNoTag, kHasType, kHasPayload, kDone };
+  State state = State::kNoTag;
+
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag = static_cast<uint8_t>(*ptr++);
+    if (tag == WireFormatLite::kMessageSetTypeIdTag) {
+      uint64_t tmp;
+      ptr = ParseBigVarint(ptr, &tmp);
+      GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+      if (state == State::kNoTag) {
+        type_id = tmp;
+        state = State::kHasType;
+      } else if (state == State::kHasPayload) {
+        type_id = tmp;
+        ExtensionInfo extension;
+        bool was_packed_on_wire;
+        if (!FindExtension(2, type_id, extendee, ctx, &extension,
+                           &was_packed_on_wire)) {
+          WriteLengthDelimited(type_id, payload,
+                               metadata->mutable_unknown_fields<T>());
+        } else {
+          MessageLite* value =
+              extension.is_repeated
+                  ? AddMessage(type_id, WireFormatLite::TYPE_MESSAGE,
+                               *extension.message_info.prototype,
+                               extension.descriptor)
+                  : MutableMessage(type_id, WireFormatLite::TYPE_MESSAGE,
+                                   *extension.message_info.prototype,
+                                   extension.descriptor);
+
+          const char* p;
+          // We can't use regular parse from string as we have to track
+          // proper recursion depth and descriptor pools.
+          ParseContext tmp_ctx(ctx->depth(), false, &p, payload);
+          tmp_ctx.data().pool = ctx->data().pool;
+          tmp_ctx.data().factory = ctx->data().factory;
+          GOOGLE_PROTOBUF_PARSER_ASSERT(value->_InternalParse(p, &tmp_ctx) &&
+                                         tmp_ctx.EndedAtLimit());
+        }
+        state = State::kDone;
+      }
+    } else if (tag == WireFormatLite::kMessageSetMessageTag) {
+      if (state == State::kHasType) {
+        ptr = ParseFieldMaybeLazily(static_cast<uint64_t>(type_id) * 8 + 2, ptr,
+                                    extendee, metadata, ctx);
+        GOOGLE_PROTOBUF_PARSER_ASSERT(ptr != nullptr);
+        state = State::kDone;
+      } else {
+        std::string tmp;
+        int32_t size = ReadSize(&ptr);
+        GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+        ptr = ctx->ReadString(ptr, size, &tmp);
+        GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+        if (state == State::kNoTag) {
+          payload = std::move(tmp);
+          state = State::kHasPayload;
+        }
+      }
+    } else {
+      ptr = ReadTag(ptr - 1, &tag);
+      if (tag == 0 || (tag & 7) == 4) {
+        ctx->SetLastTag(tag);
+        return ptr;
+      }
+      ptr = ParseField(tag, ptr, extendee, metadata, ctx);
+      GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+    }
+  }
+  return ptr;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_EXTENSION_SET_INL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/field_access_listener.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/field_access_listener.h
new file mode 100644
index 0000000..47422e6
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/field_access_listener.h
@@ -0,0 +1,172 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_FIELD_ACCESS_LISTENER_H__
+#define GOOGLE_PROTOBUF_FIELD_ACCESS_LISTENER_H__
+
+#include <cstddef>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/message_lite.h>
+
+
+namespace google {
+namespace protobuf {
+
+// A default/no-op implementation of message hooks.
+//
+// See go/statically-dispatched-message-hooks for details.
+template <typename Proto>
+struct NoOpAccessListener {
+  // Number of fields are provided at compile time for the trackers to be able
+  // to have stack allocated bitmaps for the fields. This is useful for
+  // performance critical trackers. This is also to avoid cyclic dependencies
+  // if the number of fields is needed.
+  static constexpr int kFields = Proto::_kInternalFieldNumber;
+  // Default constructor is called during the static global initialization of
+  // the program.
+  // We provide a pointer to extract the name of the proto not to get cyclic
+  // dependencies on GetDescriptor() and OnGetMetadata() calls. If you want
+  // to differentiate the protos during the runtime before the start of the
+  // program, use this functor to get its name. We either way need it for
+  // LITE_RUNTIME protos as they don't have descriptors at all.
+  explicit NoOpAccessListener(StringPiece (*name_extractor)()) {}
+  // called repeatedly during serialization/deserialization/ByteSize of
+  // Reflection as:
+  //   AccessListener<MessageT>::OnSerialize(this);
+  static void OnSerialize(const MessageLite* msg) {}
+  static void OnDeserialize(const MessageLite* msg) {}
+  static void OnByteSize(const MessageLite* msg) {}
+  static void OnMergeFrom(const MessageLite* to, const MessageLite* from) {}
+
+  // NOTE: This function can be called pre-main. Make sure it does not make
+  // the state of the listener invalid.
+  static void OnGetMetadata() {}
+
+  // called from accessors as:
+  //   AccessListener<MessageT>::On$operation(this, &field_storage_);
+  // If you need to override this with type, in your hook implementation
+  // introduce
+  // template <int kFieldNum, typename T>
+  // static void On$operation(const MessageLite* msg,
+  //                          const T* field) {}
+  // And overloads for std::nullptr_t for incomplete types such as Messages,
+  // Maps. Extract them using reflection if you need. Consequently, second
+  // argument can be null pointer.
+  // For an example, see proto_hooks/testing/memory_test_field_listener.h
+  // And argument template deduction will deduce the type itself without
+  // changing the generated code.
+
+  // add_<field>(f)
+  template <int kFieldNum>
+  static void OnAdd(const MessageLite* msg, const void* field) {}
+
+  // add_<field>()
+  template <int kFieldNum>
+  static void OnAddMutable(const MessageLite* msg, const void* field) {}
+
+  // <field>() and <repeated_field>(i)
+  template <int kFieldNum>
+  static void OnGet(const MessageLite* msg, const void* field) {}
+
+  // clear_<field>()
+  template <int kFieldNum>
+  static void OnClear(const MessageLite* msg, const void* field) {}
+
+  // has_<field>()
+  template <int kFieldNum>
+  static void OnHas(const MessageLite* msg, const void* field) {}
+
+  // <repeated_field>()
+  template <int kFieldNum>
+  static void OnList(const MessageLite* msg, const void* field) {}
+
+  // mutable_<field>()
+  template <int kFieldNum>
+  static void OnMutable(const MessageLite* msg, const void* field) {}
+
+  // mutable_<repeated_field>()
+  template <int kFieldNum>
+  static void OnMutableList(const MessageLite* msg, const void* field) {}
+
+  // release_<field>()
+  template <int kFieldNum>
+  static void OnRelease(const MessageLite* msg, const void* field) {}
+
+  // set_<field>() and set_<repeated_field>(i)
+  template <int kFieldNum>
+  static void OnSet(const MessageLite* msg, const void* field) {}
+
+  // <repeated_field>_size()
+  template <int kFieldNum>
+  static void OnSize(const MessageLite* msg, const void* field) {}
+
+  static void OnHasExtension(const MessageLite* msg, int extension_tag,
+                             const void* field) {}
+  // TODO(b/190614678): Support clear in the proto compiler.
+  static void OnClearExtension(const MessageLite* msg, int extension_tag,
+                               const void* field) {}
+  static void OnExtensionSize(const MessageLite* msg, int extension_tag,
+                              const void* field) {}
+  static void OnGetExtension(const MessageLite* msg, int extension_tag,
+                             const void* field) {}
+  static void OnMutableExtension(const MessageLite* msg, int extension_tag,
+                                 const void* field) {}
+  static void OnSetExtension(const MessageLite* msg, int extension_tag,
+                             const void* field) {}
+  static void OnReleaseExtension(const MessageLite* msg, int extension_tag,
+                                 const void* field) {}
+  static void OnAddExtension(const MessageLite* msg, int extension_tag,
+                             const void* field) {}
+  static void OnAddMutableExtension(const MessageLite* msg, int extension_tag,
+                                    const void* field) {}
+  static void OnListExtension(const MessageLite* msg, int extension_tag,
+                              const void* field) {}
+  static void OnMutableListExtension(const MessageLite* msg, int extension_tag,
+                                     const void* field) {}
+};
+
+}  // namespace protobuf
+}  // namespace google
+
+#ifndef REPLACE_PROTO_LISTENER_IMPL
+namespace google {
+namespace protobuf {
+template <class T>
+using AccessListener = NoOpAccessListener<T>;
+}  // namespace protobuf
+}  // namespace google
+#else
+// You can put your implementations of hooks/listeners here.
+// All hooks are subject to approval by protobuf-team@.
+
+#endif  // !REPLACE_PROTO_LISTENER_IMPL
+
+#endif  // GOOGLE_PROTOBUF_FIELD_ACCESS_LISTENER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/field_mask.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/field_mask.pb.h
new file mode 100644
index 0000000..8f90e4c
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/field_mask.pb.h
@@ -0,0 +1,317 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/field_mask.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2ffield_5fmask_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2ffield_5fmask_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/unknown_field_set.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2ffield_5fmask_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2ffield_5fmask_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2ffield_5fmask_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class FieldMask;
+struct FieldMaskDefaultTypeInternal;
+PROTOBUF_EXPORT extern FieldMaskDefaultTypeInternal _FieldMask_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::FieldMask* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::FieldMask>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class PROTOBUF_EXPORT FieldMask final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.FieldMask) */ {
+ public:
+  inline FieldMask() : FieldMask(nullptr) {}
+  ~FieldMask() override;
+  explicit PROTOBUF_CONSTEXPR FieldMask(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  FieldMask(const FieldMask& from);
+  FieldMask(FieldMask&& from) noexcept
+    : FieldMask() {
+    *this = ::std::move(from);
+  }
+
+  inline FieldMask& operator=(const FieldMask& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline FieldMask& operator=(FieldMask&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const FieldMask& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const FieldMask* internal_default_instance() {
+    return reinterpret_cast<const FieldMask*>(
+               &_FieldMask_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(FieldMask& a, FieldMask& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(FieldMask* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(FieldMask* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  FieldMask* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<FieldMask>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const FieldMask& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const FieldMask& from) {
+    FieldMask::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(FieldMask* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.FieldMask";
+  }
+  protected:
+  explicit FieldMask(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kPathsFieldNumber = 1,
+  };
+  // repeated string paths = 1;
+  int paths_size() const;
+  private:
+  int _internal_paths_size() const;
+  public:
+  void clear_paths();
+  const std::string& paths(int index) const;
+  std::string* mutable_paths(int index);
+  void set_paths(int index, const std::string& value);
+  void set_paths(int index, std::string&& value);
+  void set_paths(int index, const char* value);
+  void set_paths(int index, const char* value, size_t size);
+  std::string* add_paths();
+  void add_paths(const std::string& value);
+  void add_paths(std::string&& value);
+  void add_paths(const char* value);
+  void add_paths(const char* value, size_t size);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& paths() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_paths();
+  private:
+  const std::string& _internal_paths(int index) const;
+  std::string* _internal_add_paths();
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.FieldMask)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> paths_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2ffield_5fmask_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// FieldMask
+
+// repeated string paths = 1;
+inline int FieldMask::_internal_paths_size() const {
+  return _impl_.paths_.size();
+}
+inline int FieldMask::paths_size() const {
+  return _internal_paths_size();
+}
+inline void FieldMask::clear_paths() {
+  _impl_.paths_.Clear();
+}
+inline std::string* FieldMask::add_paths() {
+  std::string* _s = _internal_add_paths();
+  // @@protoc_insertion_point(field_add_mutable:google.protobuf.FieldMask.paths)
+  return _s;
+}
+inline const std::string& FieldMask::_internal_paths(int index) const {
+  return _impl_.paths_.Get(index);
+}
+inline const std::string& FieldMask::paths(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FieldMask.paths)
+  return _internal_paths(index);
+}
+inline std::string* FieldMask::mutable_paths(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.FieldMask.paths)
+  return _impl_.paths_.Mutable(index);
+}
+inline void FieldMask::set_paths(int index, const std::string& value) {
+  _impl_.paths_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldMask.paths)
+}
+inline void FieldMask::set_paths(int index, std::string&& value) {
+  _impl_.paths_.Mutable(index)->assign(std::move(value));
+  // @@protoc_insertion_point(field_set:google.protobuf.FieldMask.paths)
+}
+inline void FieldMask::set_paths(int index, const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.paths_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set_char:google.protobuf.FieldMask.paths)
+}
+inline void FieldMask::set_paths(int index, const char* value, size_t size) {
+  _impl_.paths_.Mutable(index)->assign(
+    reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_set_pointer:google.protobuf.FieldMask.paths)
+}
+inline std::string* FieldMask::_internal_add_paths() {
+  return _impl_.paths_.Add();
+}
+inline void FieldMask::add_paths(const std::string& value) {
+  _impl_.paths_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.FieldMask.paths)
+}
+inline void FieldMask::add_paths(std::string&& value) {
+  _impl_.paths_.Add(std::move(value));
+  // @@protoc_insertion_point(field_add:google.protobuf.FieldMask.paths)
+}
+inline void FieldMask::add_paths(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.paths_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add_char:google.protobuf.FieldMask.paths)
+}
+inline void FieldMask::add_paths(const char* value, size_t size) {
+  _impl_.paths_.Add()->assign(reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_add_pointer:google.protobuf.FieldMask.paths)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&
+FieldMask::paths() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.FieldMask.paths)
+  return _impl_.paths_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
+FieldMask::mutable_paths() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.FieldMask.paths)
+  return &_impl_.paths_;
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2ffield_5fmask_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_enum_reflection.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_enum_reflection.h
new file mode 100644
index 0000000..b96a9c6
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_enum_reflection.h
@@ -0,0 +1,100 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: jasonh@google.com (Jason Hsueh)
+//
+// This header is logically internal, but is made public because it is used
+// from protocol-compiler-generated code, which may reside in other components.
+// It provides reflection support for generated enums, and is included in
+// generated .pb.h files and should have minimal dependencies. The methods are
+// implemented in generated_message_reflection.cc.
+
+#ifndef GOOGLE_PROTOBUF_GENERATED_ENUM_REFLECTION_H__
+#define GOOGLE_PROTOBUF_GENERATED_ENUM_REFLECTION_H__
+
+
+#include <string>
+
+#include <google/protobuf/port.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/generated_enum_util.h>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+class EnumDescriptor;
+}  // namespace protobuf
+}  // namespace google
+
+namespace google {
+namespace protobuf {
+
+// Returns the EnumDescriptor for enum type E, which must be a
+// proto-declared enum type.  Code generated by the protocol compiler
+// will include specializations of this template for each enum type declared.
+template <typename E>
+const EnumDescriptor* GetEnumDescriptor();
+
+namespace internal {
+
+// Helper for EnumType_Parse functions: try to parse the string 'name' as
+// an enum name of the given type, returning true and filling in value on
+// success, or returning false and leaving value unchanged on failure.
+PROTOBUF_EXPORT bool ParseNamedEnum(const EnumDescriptor* descriptor,
+                                    ConstStringParam name, int* value);
+
+template <typename EnumType>
+bool ParseNamedEnum(const EnumDescriptor* descriptor, ConstStringParam name,
+                    EnumType* value) {
+  int tmp;
+  if (!ParseNamedEnum(descriptor, name, &tmp)) return false;
+  *value = static_cast<EnumType>(tmp);
+  return true;
+}
+
+// Just a wrapper around printing the name of a value. The main point of this
+// function is not to be inlined, so that you can do this without including
+// descriptor.h.
+PROTOBUF_EXPORT const std::string& NameOfEnum(const EnumDescriptor* descriptor,
+                                              int value);
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_GENERATED_ENUM_REFLECTION_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_enum_util.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_enum_util.h
new file mode 100644
index 0000000..5d10ac0
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_enum_util.h
@@ -0,0 +1,85 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_GENERATED_ENUM_UTIL_H__
+#define GOOGLE_PROTOBUF_GENERATED_ENUM_UTIL_H__
+
+
+#include <type_traits>
+
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/message_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+
+// This type trait can be used to cause templates to only match proto2 enum
+// types.
+template <typename T>
+struct is_proto_enum : ::std::false_type {};
+
+namespace internal {
+
+// The table entry format for storing enum name-to-value mapping used with lite
+// protos. This struct and the following related functions should only be used
+// by protobuf generated code.
+struct EnumEntry {
+  StringPiece name;
+  int value;
+};
+
+// Looks up a numeric enum value given the string name.
+PROTOBUF_EXPORT bool LookUpEnumValue(const EnumEntry* enums, size_t size,
+                                     StringPiece name, int* value);
+
+// Looks up an enum name given the numeric value.
+PROTOBUF_EXPORT int LookUpEnumName(const EnumEntry* enums,
+                                   const int* sorted_indices, size_t size,
+                                   int value);
+
+// Initializes the list of enum names in std::string form.
+PROTOBUF_EXPORT bool InitializeEnumStrings(
+    const EnumEntry* enums, const int* sorted_indices, size_t size,
+    internal::ExplicitlyConstructed<std::string>* enum_strings);
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_GENERATED_ENUM_UTIL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_bases.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_bases.h
new file mode 100644
index 0000000..b295218
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_bases.h
@@ -0,0 +1,87 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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 file contains helpers for generated code.
+//
+//  Nothing in this file should be directly referenced by users of protobufs.
+
+#ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_BASES_H__
+#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_BASES_H__
+
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/parse_context.h>
+
+// Must come last:
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// To save code size, protos without any fields are derived from ZeroFieldsBase
+// rather than Message.
+class PROTOBUF_EXPORT ZeroFieldsBase : public Message {
+ public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final { return true; }
+  size_t ByteSizeLong() const final;
+  int GetCachedSize() const final { return _cached_size_.Get(); }
+  const char* _InternalParse(const char* ptr,
+                             internal::ParseContext* ctx) final;
+  ::uint8_t* _InternalSerialize(::uint8_t* target,
+                                io::EpsCopyOutputStream* stream) const final;
+
+ protected:
+  constexpr ZeroFieldsBase() {}
+  explicit ZeroFieldsBase(Arena* arena, bool is_message_owned)
+      : Message(arena, is_message_owned) {}
+  ZeroFieldsBase(const ZeroFieldsBase&) = delete;
+  ZeroFieldsBase& operator=(const ZeroFieldsBase&) = delete;
+  ~ZeroFieldsBase() override;
+
+  void SetCachedSize(int size) const final { _cached_size_.Set(size); }
+
+  static void MergeImpl(Message& to, const Message& from);
+  static void CopyImpl(Message& to, const Message& from);
+  void InternalSwap(ZeroFieldsBase* other);
+
+  mutable internal::CachedSize _cached_size_;
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_GENERATED_MESSAGE_BASES_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_reflection.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_reflection.h
new file mode 100644
index 0000000..334b2cc
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_reflection.h
@@ -0,0 +1,354 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This header is logically internal, but is made public because it is used
+// from protocol-compiler-generated code, which may reside in other components.
+
+#ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__
+#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_enum_reflection.h>
+#include <google/protobuf/unknown_field_set.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+class MapKey;
+class MapValueRef;
+class MessageLayoutInspector;
+class Message;
+struct Metadata;
+}  // namespace protobuf
+}  // namespace google
+
+namespace google {
+namespace protobuf {
+namespace internal {
+class DefaultEmptyOneof;
+// Defined in other files.
+class ExtensionSet;  // extension_set.h
+class WeakFieldMap;  // weak_field_map.h
+
+// This struct describes the internal layout of the message, hence this is
+// used to act on the message reflectively.
+//   default_instance:  The default instance of the message.  This is only
+//                  used to obtain pointers to default instances of embedded
+//                  messages, which GetMessage() will return if the particular
+//                  sub-message has not been initialized yet.  (Thus, all
+//                  embedded message fields *must* have non-null pointers
+//                  in the default instance.)
+//   offsets:       An array of ints giving the byte offsets.
+//                  For each oneof or weak field, the offset is relative to the
+//                  default_instance. These can be computed at compile time
+//                  using the
+//                  PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET()
+//                  macro. For each none oneof field, the offset is related to
+//                  the start of the message object.  These can be computed at
+//                  compile time using the
+//                  PROTO2_GENERATED_MESSAGE_FIELD_OFFSET() macro.
+//                  Besides offsets for all fields, this array also contains
+//                  offsets for oneof unions. The offset of the i-th oneof union
+//                  is offsets[descriptor->field_count() + i].
+//   has_bit_indices:  Mapping from field indexes to their index in the has
+//                  bit array.
+//   has_bits_offset:  Offset in the message of an array of uint32s of size
+//                  descriptor->field_count()/32, rounded up.  This is a
+//                  bitfield where each bit indicates whether or not the
+//                  corresponding field of the message has been initialized.
+//                  The bit for field index i is obtained by the expression:
+//                    has_bits[i / 32] & (1 << (i % 32))
+//   unknown_fields_offset:  Offset in the message of the UnknownFieldSet for
+//                  the message.
+//   extensions_offset:  Offset in the message of the ExtensionSet for the
+//                  message, or -1 if the message type has no extension
+//                  ranges.
+//   oneof_case_offset:  Offset in the message of an array of uint32s of
+//                  size descriptor->oneof_decl_count().  Each uint32_t
+//                  indicates what field is set for each oneof.
+//   object_size:   The size of a message object of this type, as measured
+//                  by sizeof().
+//   arena_offset:  If a message doesn't have a unknown_field_set that stores
+//                  the arena, it must have a direct pointer to the arena.
+//   weak_field_map_offset: If the message proto has weak fields, this is the
+//                  offset of _weak_field_map_ in the generated proto. Otherwise
+//                  -1.
+struct ReflectionSchema {
+ public:
+  // Size of a google::protobuf::Message object of this type.
+  uint32_t GetObjectSize() const { return static_cast<uint32_t>(object_size_); }
+
+  bool InRealOneof(const FieldDescriptor* field) const {
+    return field->containing_oneof() &&
+           !field->containing_oneof()->is_synthetic();
+  }
+
+  // Offset of a non-oneof field.  Getting a field offset is slightly more
+  // efficient when we know statically that it is not a oneof field.
+  uint32_t GetFieldOffsetNonOneof(const FieldDescriptor* field) const {
+    GOOGLE_DCHECK(!InRealOneof(field));
+    return OffsetValue(offsets_[field->index()], field->type());
+  }
+
+  // Offset of any field.
+  uint32_t GetFieldOffset(const FieldDescriptor* field) const {
+    if (InRealOneof(field)) {
+      size_t offset =
+          static_cast<size_t>(field->containing_type()->field_count()) +
+          field->containing_oneof()->index();
+      return OffsetValue(offsets_[offset], field->type());
+    } else {
+      return GetFieldOffsetNonOneof(field);
+    }
+  }
+
+  bool IsFieldInlined(const FieldDescriptor* field) const {
+    return Inlined(offsets_[field->index()], field->type());
+  }
+
+  uint32_t GetOneofCaseOffset(const OneofDescriptor* oneof_descriptor) const {
+    return static_cast<uint32_t>(oneof_case_offset_) +
+           static_cast<uint32_t>(
+               static_cast<size_t>(oneof_descriptor->index()) *
+               sizeof(uint32_t));
+  }
+
+  bool HasHasbits() const { return has_bits_offset_ != -1; }
+
+  // Bit index within the bit array of hasbits.  Bit order is low-to-high.
+  uint32_t HasBitIndex(const FieldDescriptor* field) const {
+    if (has_bits_offset_ == -1) return static_cast<uint32_t>(-1);
+    GOOGLE_DCHECK(HasHasbits());
+    return has_bit_indices_[field->index()];
+  }
+
+  // Byte offset of the hasbits array.
+  uint32_t HasBitsOffset() const {
+    GOOGLE_DCHECK(HasHasbits());
+    return static_cast<uint32_t>(has_bits_offset_);
+  }
+
+  bool HasInlinedString() const { return inlined_string_donated_offset_ != -1; }
+
+  // Bit index within the bit array of _inlined_string_donated_.  Bit order is
+  // low-to-high.
+  uint32_t InlinedStringIndex(const FieldDescriptor* field) const {
+    GOOGLE_DCHECK(HasInlinedString());
+    return inlined_string_indices_[field->index()];
+  }
+
+  // Byte offset of the _inlined_string_donated_ array.
+  uint32_t InlinedStringDonatedOffset() const {
+    GOOGLE_DCHECK(HasInlinedString());
+    return static_cast<uint32_t>(inlined_string_donated_offset_);
+  }
+
+  // The offset of the InternalMetadataWithArena member.
+  // For Lite this will actually be an InternalMetadataWithArenaLite.
+  // The schema doesn't contain enough information to distinguish between
+  // these two cases.
+  uint32_t GetMetadataOffset() const {
+    return static_cast<uint32_t>(metadata_offset_);
+  }
+
+  // Whether this message has an ExtensionSet.
+  bool HasExtensionSet() const { return extensions_offset_ != -1; }
+
+  // The offset of the ExtensionSet in this message.
+  uint32_t GetExtensionSetOffset() const {
+    GOOGLE_DCHECK(HasExtensionSet());
+    return static_cast<uint32_t>(extensions_offset_);
+  }
+
+  // The off set of WeakFieldMap when the message contains weak fields.
+  // The default is 0 for now.
+  int GetWeakFieldMapOffset() const { return weak_field_map_offset_; }
+
+  bool IsDefaultInstance(const Message& message) const {
+    return &message == default_instance_;
+  }
+
+  // Returns a pointer to the default value for this field.  The size and type
+  // of the underlying data depends on the field's type.
+  const void* GetFieldDefault(const FieldDescriptor* field) const {
+    return reinterpret_cast<const uint8_t*>(default_instance_) +
+           OffsetValue(offsets_[field->index()], field->type());
+  }
+
+  // Returns true if the field is implicitly backed by LazyField.
+  bool IsEagerlyVerifiedLazyField(const FieldDescriptor* field) const {
+    GOOGLE_DCHECK_EQ(field->type(), FieldDescriptor::TYPE_MESSAGE);
+    (void)field;
+    return false;
+  }
+
+  bool IsFieldStripped(const FieldDescriptor* field) const {
+    (void)field;
+    return false;
+  }
+
+  bool IsMessageStripped(const Descriptor* descriptor) const {
+    (void)descriptor;
+    return false;
+  }
+
+
+  bool HasWeakFields() const { return weak_field_map_offset_ > 0; }
+
+  // These members are intended to be private, but we cannot actually make them
+  // private because this prevents us from using aggregate initialization of
+  // them, ie.
+  //
+  //   ReflectionSchema schema = {a, b, c, d, e, ...};
+  // private:
+  const Message* default_instance_;
+  const uint32_t* offsets_;
+  const uint32_t* has_bit_indices_;
+  int has_bits_offset_;
+  int metadata_offset_;
+  int extensions_offset_;
+  int oneof_case_offset_;
+  int object_size_;
+  int weak_field_map_offset_;
+  const uint32_t* inlined_string_indices_;
+  int inlined_string_donated_offset_;
+
+  // We tag offset values to provide additional data about fields (such as
+  // "unused" or "lazy" or "inlined").
+  static uint32_t OffsetValue(uint32_t v, FieldDescriptor::Type type) {
+    if (type == FieldDescriptor::TYPE_MESSAGE ||
+        type == FieldDescriptor::TYPE_STRING ||
+        type == FieldDescriptor::TYPE_BYTES) {
+      return v & 0xFFFFFFFEu;
+    }
+    return v;
+  }
+
+  static bool Inlined(uint32_t v, FieldDescriptor::Type type) {
+    if (type == FieldDescriptor::TYPE_STRING ||
+        type == FieldDescriptor::TYPE_BYTES) {
+      return (v & 1u) != 0u;
+    } else {
+      // Non string/byte fields are not inlined.
+      return false;
+    }
+  }
+};
+
+// Structs that the code generator emits directly to describe a message.
+// These should never used directly except to build a ReflectionSchema
+// object.
+//
+// EXPERIMENTAL: these are changing rapidly, and may completely disappear
+// or merge with ReflectionSchema.
+struct MigrationSchema {
+  int32_t offsets_index;
+  int32_t has_bit_indices_index;
+  int32_t inlined_string_indices_index;
+  int object_size;
+};
+
+// This struct tries to reduce unnecessary padding.
+// The num_xxx might not be close to their respective pointer, but this saves
+// padding.
+struct PROTOBUF_EXPORT DescriptorTable {
+  mutable bool is_initialized;
+  bool is_eager;
+  int size;  // of serialized descriptor
+  const char* descriptor;
+  const char* filename;
+  once_flag* once;
+  const DescriptorTable* const* deps;
+  int num_deps;
+  int num_messages;
+  const MigrationSchema* schemas;
+  const Message* const* default_instances;
+  const uint32_t* offsets;
+  // update the following descriptor arrays.
+  Metadata* file_level_metadata;
+  const EnumDescriptor** file_level_enum_descriptors;
+  const ServiceDescriptor** file_level_service_descriptors;
+};
+
+enum {
+  // Tag used on offsets for fields that don't have a real offset.
+  // For example, weak message fields go into the WeakFieldMap and not in an
+  // actual field.
+  kInvalidFieldOffsetTag = 0x40000000u,
+};
+
+// AssignDescriptors() pulls the compiled FileDescriptor from the DescriptorPool
+// and uses it to populate all of the global variables which store pointers to
+// the descriptor objects.  It also constructs the reflection objects.  It is
+// called the first time anyone calls descriptor() or GetReflection() on one of
+// the types defined in the file.  AssignDescriptors() is thread-safe.
+void PROTOBUF_EXPORT AssignDescriptors(const DescriptorTable* table,
+                                       bool eager = false);
+
+// Overload used to implement GetMetadataStatic in the generated code.
+// See comments in compiler/cpp/internal/file.cc as to why.
+// It takes a `Metadata` and returns it to allow for tail calls and reduce
+// binary size.
+Metadata PROTOBUF_EXPORT AssignDescriptors(const DescriptorTable* (*table)(),
+                                           internal::once_flag* once,
+                                           const Metadata& metadata);
+
+// These cannot be in lite so we put them in the reflection.
+PROTOBUF_EXPORT void UnknownFieldSetSerializer(const uint8_t* base,
+                                               uint32_t offset, uint32_t tag,
+                                               uint32_t has_offset,
+                                               io::CodedOutputStream* output);
+
+struct PROTOBUF_EXPORT AddDescriptorsRunner {
+  explicit AddDescriptorsRunner(const DescriptorTable* table);
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_tctable_decl.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_tctable_decl.h
new file mode 100644
index 0000000..b1bb1de
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_tctable_decl.h
@@ -0,0 +1,312 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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 file contains declarations needed in generated headers for messages
+// that use tail-call table parsing. Everything in this file is for internal
+// use only.
+
+#ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_DECL_H__
+#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_DECL_H__
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/parse_context.h>
+
+// Must come last:
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Additional information about this field:
+struct TcFieldData {
+  constexpr TcFieldData() : data(0) {}
+
+  // Fast table entry constructor:
+  constexpr TcFieldData(uint16_t coded_tag, uint8_t hasbit_idx, uint8_t aux_idx,
+                        uint16_t offset)
+      : data(uint64_t{offset} << 48 |      //
+             uint64_t{aux_idx} << 24 |     //
+             uint64_t{hasbit_idx} << 16 |  //
+             uint64_t{coded_tag}) {}
+
+  // Fields used in fast table parsing:
+  //
+  //     Bit:
+  //     +-----------+-------------------+
+  //     |63    ..     32|31     ..     0|
+  //     +---------------+---------------+
+  //     :   .   :   .   :   . 16|=======| [16] coded_tag()
+  //     :   .   :   .   : 24|===|   .   : [ 8] hasbit_idx()
+  //     :   .   :   . 32|===|   :   .   : [ 8] aux_idx()
+  //     :   . 48:---.---:   .   :   .   : [16] (unused)
+  //     |=======|   .   :   .   :   .   : [16] offset()
+  //     +-----------+-------------------+
+  //     |63    ..     32|31     ..     0|
+  //     +---------------+---------------+
+
+  template <typename TagType = uint16_t>
+  TagType coded_tag() const {
+    return static_cast<TagType>(data);
+  }
+  uint8_t hasbit_idx() const { return static_cast<uint8_t>(data >> 16); }
+  uint8_t aux_idx() const { return static_cast<uint8_t>(data >> 24); }
+  uint16_t offset() const { return static_cast<uint16_t>(data >> 48); }
+
+  // Fields used in mini table parsing:
+  //
+  //     Bit:
+  //     +-----------+-------------------+
+  //     |63    ..     32|31     ..     0|
+  //     +---------------+---------------+
+  //     :   .   :   .   |===============| [32] tag() (decoded)
+  //     |===============|   .   :   .   : [32] entry_offset()
+  //     +-----------+-------------------+
+  //     |63    ..     32|31     ..     0|
+  //     +---------------+---------------+
+
+  uint32_t tag() const { return static_cast<uint32_t>(data); }
+  uint32_t entry_offset() const { return static_cast<uint32_t>(data >> 32); }
+
+  uint64_t data;
+};
+
+struct TcParseTableBase;
+
+// TailCallParseFunc is the function pointer type used in the tailcall table.
+typedef const char* (*TailCallParseFunc)(PROTOBUF_TC_PARAM_DECL);
+
+namespace field_layout {
+struct Offset {
+  uint32_t off;
+};
+}  // namespace field_layout
+
+#if defined(_MSC_VER) && !defined(_WIN64)
+#pragma warning(push)
+// TcParseTableBase is intentionally overaligned on 32 bit targets.
+#pragma warning(disable : 4324)
+#endif
+
+// Base class for message-level table with info for the tail-call parser.
+struct alignas(uint64_t) TcParseTableBase {
+  // Common attributes for message layout:
+  uint16_t has_bits_offset;
+  uint16_t extension_offset;
+  uint32_t extension_range_low;
+  uint32_t extension_range_high;
+  uint32_t max_field_number;
+  uint8_t fast_idx_mask;
+  uint16_t lookup_table_offset;
+  uint32_t skipmap32;
+  uint32_t field_entries_offset;
+  uint16_t num_field_entries;
+
+  uint16_t num_aux_entries;
+  uint32_t aux_offset;
+
+  const MessageLite* default_instance;
+
+  // Handler for fields which are not handled by table dispatch.
+  TailCallParseFunc fallback;
+
+  // This constructor exactly follows the field layout, so it's technically
+  // not necessary.  However, it makes it much much easier to add or re-arrange
+  // fields, because it can be overloaded with an additional constructor,
+  // temporarily allowing both old and new protocol buffer headers to be
+  // compiled.
+  constexpr TcParseTableBase(
+      uint16_t has_bits_offset, uint16_t extension_offset,
+      uint32_t extension_range_low, uint32_t extension_range_high,
+      uint32_t max_field_number, uint8_t fast_idx_mask,
+      uint16_t lookup_table_offset, uint32_t skipmap32,
+      uint32_t field_entries_offset, uint16_t num_field_entries,
+      uint16_t num_aux_entries, uint32_t aux_offset,
+      const MessageLite* default_instance, TailCallParseFunc fallback)
+      : has_bits_offset(has_bits_offset),
+        extension_offset(extension_offset),
+        extension_range_low(extension_range_low),
+        extension_range_high(extension_range_high),
+        max_field_number(max_field_number),
+        fast_idx_mask(fast_idx_mask),
+        lookup_table_offset(lookup_table_offset),
+        skipmap32(skipmap32),
+        field_entries_offset(field_entries_offset),
+        num_field_entries(num_field_entries),
+        num_aux_entries(num_aux_entries),
+        aux_offset(aux_offset),
+        default_instance(default_instance),
+        fallback(fallback) {}
+
+  // Table entry for fast-path tailcall dispatch handling.
+  struct FastFieldEntry {
+    // Target function for dispatch:
+    TailCallParseFunc target;
+    // Field data used during parse:
+    TcFieldData bits;
+  };
+  // There is always at least one table entry.
+  const FastFieldEntry* fast_entry(size_t idx) const {
+    return reinterpret_cast<const FastFieldEntry*>(this + 1) + idx;
+  }
+
+  // Returns a begin iterator (pointer) to the start of the field lookup table.
+  const uint16_t* field_lookup_begin() const {
+    return reinterpret_cast<const uint16_t*>(reinterpret_cast<uintptr_t>(this) +
+                                             lookup_table_offset);
+  }
+
+  // Field entry for all fields.
+  struct FieldEntry {
+    uint32_t offset;     // offset in the message object
+    int32_t has_idx;     // has-bit index
+    uint16_t aux_idx;    // index for `field_aux`.
+    uint16_t type_card;  // `FieldType` and `Cardinality` (see _impl.h)
+  };
+
+  // Returns a begin iterator (pointer) to the start of the field entries array.
+  const FieldEntry* field_entries_begin() const {
+    return reinterpret_cast<const FieldEntry*>(
+        reinterpret_cast<uintptr_t>(this) + field_entries_offset);
+  }
+
+  // Auxiliary entries for field types that need extra information.
+  union FieldAux {
+    constexpr FieldAux() : message_default(nullptr) {}
+    constexpr FieldAux(bool (*enum_validator)(int))
+        : enum_validator(enum_validator) {}
+    constexpr FieldAux(field_layout::Offset off) : offset(off.off) {}
+    constexpr FieldAux(int16_t range_start, uint16_t range_length)
+        : enum_range{range_start, range_length} {}
+    constexpr FieldAux(const MessageLite* msg) : message_default(msg) {}
+    bool (*enum_validator)(int);
+    struct {
+      int16_t start;    // minimum enum number (if it fits)
+      uint16_t length;  // length of range (i.e., max = start + length - 1)
+    } enum_range;
+    uint32_t offset;
+    const MessageLite* message_default;
+  };
+  const FieldAux* field_aux(uint32_t idx) const {
+    return reinterpret_cast<const FieldAux*>(reinterpret_cast<uintptr_t>(this) +
+                                             aux_offset) +
+           idx;
+  }
+  const FieldAux* field_aux(const FieldEntry* entry) const {
+    return field_aux(entry->aux_idx);
+  }
+
+  // Field name data
+  const char* name_data() const {
+    return reinterpret_cast<const char*>(reinterpret_cast<uintptr_t>(this) +
+                                         aux_offset +
+                                         num_aux_entries * sizeof(FieldAux));
+  }
+};
+
+#if defined(_MSC_VER) && !defined(_WIN64)
+#pragma warning(pop)
+#endif
+
+static_assert(sizeof(TcParseTableBase::FastFieldEntry) <= 16,
+              "Fast field entry is too big.");
+static_assert(sizeof(TcParseTableBase::FieldEntry) <= 16,
+              "Field entry is too big.");
+
+template <size_t kFastTableSizeLog2, size_t kNumFieldEntries = 0,
+          size_t kNumFieldAux = 0, size_t kNameTableSize = 0,
+          size_t kFieldLookupSize = 2>
+struct TcParseTable {
+  TcParseTableBase header;
+
+  // Entries for each field.
+  //
+  // Fields are indexed by the lowest bits of their field number. The field
+  // number is masked to fit inside the table. Note that the parsing logic
+  // generally calls `TailCallParseTableBase::fast_entry()` instead of accessing
+  // this field directly.
+  std::array<TcParseTableBase::FastFieldEntry, (1 << kFastTableSizeLog2)>
+      fast_entries;
+
+  // Just big enough to find all the field entries.
+  std::array<uint16_t, kFieldLookupSize> field_lookup_table;
+  // Entries for all fields:
+  std::array<TcParseTableBase::FieldEntry, kNumFieldEntries> field_entries;
+  std::array<TcParseTableBase::FieldAux, kNumFieldAux> aux_entries;
+  std::array<char, kNameTableSize> field_names;
+};
+
+// Partial specialization: if there are no aux entries, there will be no array.
+// In C++, arrays cannot have length 0, but (C++11) std::array<T, 0> is valid.
+// However, different implementations have different sizeof(std::array<T, 0>).
+// Skipping the member makes offset computations portable.
+template <size_t kFastTableSizeLog2, size_t kNumFieldEntries,
+          size_t kNameTableSize, size_t kFieldLookupSize>
+struct TcParseTable<kFastTableSizeLog2, kNumFieldEntries, 0, kNameTableSize,
+                    kFieldLookupSize> {
+  TcParseTableBase header;
+  std::array<TcParseTableBase::FastFieldEntry, (1 << kFastTableSizeLog2)>
+      fast_entries;
+  std::array<uint16_t, kFieldLookupSize> field_lookup_table;
+  std::array<TcParseTableBase::FieldEntry, kNumFieldEntries> field_entries;
+  std::array<char, kNameTableSize> field_names;
+};
+
+// Partial specialization: if there are no fields at all, then we can save space
+// by skipping the field numbers and entries.
+template <size_t kNameTableSize, size_t kFieldLookupSize>
+struct TcParseTable<0, 0, 0, kNameTableSize, kFieldLookupSize> {
+  TcParseTableBase header;
+  // N.B.: the fast entries are sized by log2, so 2**0 fields = 1 entry.
+  // The fast parsing loop will always use this entry, so it must be present.
+  std::array<TcParseTableBase::FastFieldEntry, 1> fast_entries;
+  std::array<uint16_t, kFieldLookupSize> field_lookup_table;
+  std::array<char, kNameTableSize> field_names;
+};
+
+static_assert(std::is_standard_layout<TcParseTable<1>>::value,
+              "TcParseTable must be standard layout.");
+
+static_assert(offsetof(TcParseTable<1>, fast_entries) ==
+                  sizeof(TcParseTableBase),
+              "Table entries must be laid out after TcParseTableBase.");
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_DECL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_tctable_impl.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_tctable_impl.h
new file mode 100644
index 0000000..a8f8bdc
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_tctable_impl.h
@@ -0,0 +1,565 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_IMPL_H__
+#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_IMPL_H__
+
+#include <cstdint>
+#include <type_traits>
+
+#include <google/protobuf/port.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/generated_message_tctable_decl.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// Must come last:
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+class Message;
+class UnknownFieldSet;
+
+namespace internal {
+
+// Field layout enums.
+//
+// Structural information about fields is packed into a 16-bit value. The enum
+// types below represent bitwise fields, along with their respective widths,
+// shifts, and masks.
+//
+//     Bit:
+//     +-----------------------+-----------------------+
+//     |15        ..          8|7         ..          0|
+//     +-----------------------+-----------------------+
+//     :  .  :  .  :  .  :  .  :  .  :  .  : 3|========| [3] FieldType
+//     :     :     :     :     :     : 5|=====|  :     : [2] FieldCardinality
+//     :  .  :  .  :  .  :  . 8|========|  :  .  :  .  : [3] FieldRep
+//     :     :     :   10|=====|     :     :     :     : [2] TransformValidation
+//     :  .  :  .12|=====|  .  :  .  :  .  :  .  :  .  : [2] FormatDiscriminator
+//     +-----------------------+-----------------------+
+//     |15        ..          8|7         ..          0|
+//     +-----------------------+-----------------------+
+//
+namespace field_layout {
+// clang-format off
+
+// Field kind (3 bits):
+// These values broadly represent a wire type and an in-memory storage class.
+enum FieldKind : uint16_t {
+  kFkShift = 0,
+  kFkBits = 3,
+  kFkMask = ((1 << kFkBits) - 1) << kFkShift,
+
+  kFkNone = 0,
+  kFkVarint,        // WT=0     rep=8,32,64 bits
+  kFkPackedVarint,  // WT=2     rep=8,32,64 bits
+  kFkFixed,         // WT=1,5   rep=32,64 bits
+  kFkPackedFixed,   // WT=2     rep=32,64 bits
+  kFkString,        // WT=2     rep=various
+  kFkMessage,       // WT=2,3,4 rep=MessageLite*
+  // Maps are a special case of Message, but use different parsing logic.
+  kFkMap,           // WT=2     rep=Map(Lite)<various, various>
+};
+
+static_assert(kFkMap < (1 << kFkBits), "too many types");
+
+// Cardinality (2 bits):
+// These values determine how many values a field can have and its presence.
+// Packed fields are represented in FieldType.
+enum Cardinality : uint16_t {
+  kFcShift    = kFkShift + kFkBits,
+  kFcBits     = 2,
+  kFcMask     = ((1 << kFcBits) - 1) << kFcShift,
+
+  kFcSingular = 0,
+  kFcOptional = 1 << kFcShift,
+  kFcRepeated = 2 << kFcShift,
+  kFcOneof    = 3 << kFcShift,
+};
+
+// Field representation (3 bits):
+// These values are the specific refinements of storage classes in FieldType.
+enum FieldRep : uint16_t {
+  kRepShift    = kFcShift + kFcBits,
+  kRepBits     = 3,
+  kRepMask     = ((1 << kRepBits) - 1) << kRepShift,
+
+  // Numeric types (used for optional and repeated fields):
+  kRep8Bits    = 0,
+  kRep32Bits   = 2 << kRepShift,
+  kRep64Bits   = 3 << kRepShift,
+  // String types:
+  kRepAString  = 0,               // ArenaStringPtr
+  kRepIString  = 1 << kRepShift,  // InlinedString
+  kRepCord     = 2 << kRepShift,  // absl::Cord
+  kRepSPiece   = 3 << kRepShift,  // StringPieceField
+  kRepSString  = 4 << kRepShift,  // std::string*
+  // Message types (WT=2 unless otherwise noted):
+  kRepMessage  = 0,               // MessageLite*
+  kRepGroup    = 1 << kRepShift,  // MessageLite* (WT=3,4)
+  kRepLazy     = 2 << kRepShift,  // LazyField*
+  kRepIWeak    = 3 << kRepShift,  // ImplicitWeak
+};
+
+// Transform/validation (2 bits):
+// These values determine transforms or validation to/from wire format.
+enum TransformValidation : uint16_t {
+  kTvShift     = kRepShift + kRepBits,
+  kTvBits      = 2,
+  kTvMask      = ((1 << kTvBits) - 1) << kTvShift,
+
+  // Varint fields:
+  kTvZigZag    = 1 << kTvShift,
+  kTvEnum      = 2 << kTvShift,  // validate using generated _IsValid()
+  kTvRange     = 3 << kTvShift,  // validate using FieldAux::enum_range
+  // String fields:
+  kTvUtf8Debug = 1 << kTvShift,  // proto2
+  kTvUtf8      = 2 << kTvShift,  // proto3
+};
+
+static_assert((kTvEnum & kTvRange) != 0,
+              "enum validation types must share a bit");
+static_assert((kTvEnum & kTvRange & kTvZigZag) == 0,
+              "zigzag encoding is not enum validation");
+
+// Format discriminators (2 bits):
+enum FormatDiscriminator : uint16_t {
+  kFmtShift      = kTvShift + kTvBits,
+  kFmtBits       = 2,
+  kFmtMask       = ((1 << kFmtBits) - 1) << kFmtShift,
+
+  // Numeric:
+  kFmtUnsigned   = 1 << kFmtShift,  // fixed, varint
+  kFmtSigned     = 2 << kFmtShift,  // fixed, varint
+  kFmtFloating   = 3 << kFmtShift,  // fixed
+  kFmtEnum       = 3 << kFmtShift,  // varint
+  // Strings:
+  kFmtUtf8       = 1 << kFmtShift,  // string (proto3, enforce_utf8=true)
+  kFmtUtf8Escape = 2 << kFmtShift,  // string (proto2, enforce_utf8=false)
+  // Bytes:
+  kFmtArray      = 1 << kFmtShift,  // bytes
+  // Messages:
+  kFmtShow       = 1 << kFmtShift,  // message, map
+};
+
+// Update this assertion (and comments above) when adding or removing bits:
+static_assert(kFmtShift + kFmtBits == 12, "number of bits changed");
+
+// This assertion should not change unless the storage width changes:
+static_assert(kFmtShift + kFmtBits <= 16, "too many bits");
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#if __GNUC__ >= 12 || (__GNUC__ == 11 && __GNUC_MINOR >= 1)
+#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion"
+#endif
+#endif
+// Convenience aliases (16 bits, with format):
+enum FieldType : uint16_t {
+  // Numeric types:
+  kBool            = kFkVarint | kRep8Bits,
+
+  kFixed32         = kFkFixed  | kRep32Bits | kFmtUnsigned,
+  kUInt32          = kFkVarint | kRep32Bits | kFmtUnsigned,
+  kSFixed32        = kFkFixed  | kRep32Bits | kFmtSigned,
+  kInt32           = kFkVarint | kRep32Bits | kFmtSigned,
+  kSInt32          = kFkVarint | kRep32Bits | kFmtSigned | kTvZigZag,
+  kFloat           = kFkFixed  | kRep32Bits | kFmtFloating,
+  kEnum            = kFkVarint | kRep32Bits | kFmtEnum   | kTvEnum,
+  kEnumRange       = kFkVarint | kRep32Bits | kFmtEnum   | kTvRange,
+  kOpenEnum        = kFkVarint | kRep32Bits | kFmtEnum,
+
+  kFixed64         = kFkFixed  | kRep64Bits | kFmtUnsigned,
+  kUInt64          = kFkVarint | kRep64Bits | kFmtUnsigned,
+  kSFixed64        = kFkFixed  | kRep64Bits | kFmtSigned,
+  kInt64           = kFkVarint | kRep64Bits | kFmtSigned,
+  kSInt64          = kFkVarint | kRep64Bits | kFmtSigned | kTvZigZag,
+  kDouble          = kFkFixed  | kRep64Bits | kFmtFloating,
+
+  kPackedBool      = kFkPackedVarint | kRep8Bits,
+
+  kPackedFixed32   = kFkPackedFixed  | kRep32Bits | kFmtUnsigned,
+  kPackedUInt32    = kFkPackedVarint | kRep32Bits | kFmtUnsigned,
+  kPackedSFixed32  = kFkPackedFixed  | kRep32Bits | kFmtSigned,
+  kPackedInt32     = kFkPackedVarint | kRep32Bits | kFmtSigned,
+  kPackedSInt32    = kFkPackedVarint | kRep32Bits | kFmtSigned | kTvZigZag,
+  kPackedFloat     = kFkPackedFixed  | kRep32Bits | kFmtFloating,
+  kPackedEnum      = kFkPackedVarint | kRep32Bits | kFmtEnum   | kTvEnum,
+  kPackedEnumRange = kFkPackedVarint | kRep32Bits | kFmtEnum   | kTvRange,
+  kPackedOpenEnum  = kFkPackedVarint | kRep32Bits | kFmtEnum,
+
+  kPackedFixed64   = kFkPackedFixed  | kRep64Bits | kFmtUnsigned,
+  kPackedUInt64    = kFkPackedVarint | kRep64Bits | kFmtUnsigned,
+  kPackedSFixed64  = kFkPackedFixed  | kRep64Bits | kFmtSigned,
+  kPackedInt64     = kFkPackedVarint | kRep64Bits | kFmtSigned,
+  kPackedSInt64    = kFkPackedVarint | kRep64Bits | kFmtSigned | kTvZigZag,
+  kPackedDouble    = kFkPackedFixed  | kRep64Bits | kFmtFloating,
+
+  // String types:
+  kBytes           = kFkString | kFmtArray,
+  kRawString       = kFkString | kFmtUtf8  | kTvUtf8Debug,
+  kUtf8String      = kFkString | kFmtUtf8  | kTvUtf8,
+
+  // Message types:
+  kMessage         = kFkMessage,
+
+  // Map types:
+  kMap             = kFkMap,
+};
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+// clang-format on
+}  // namespace field_layout
+
+// PROTOBUF_TC_PARAM_DECL are the parameters for tailcall functions, it is
+// defined in port_def.inc.
+//
+// Note that this is performance sensitive: changing the parameters will change
+// the registers used by the ABI calling convention, which subsequently affects
+// register selection logic inside the function.
+
+// PROTOBUF_TC_PARAM_PASS passes values to match PROTOBUF_TC_PARAM_DECL.
+#define PROTOBUF_TC_PARAM_PASS msg, ptr, ctx, table, hasbits, data
+
+#ifndef NDEBUG
+template <size_t align>
+#ifndef _MSC_VER
+[[noreturn]]
+#endif
+void AlignFail(uintptr_t address) {
+  GOOGLE_LOG(FATAL) << "Unaligned (" << align << ") access at " << address;
+#ifdef __GNUC__
+  __builtin_unreachable();
+#endif
+}
+
+extern template void AlignFail<4>(uintptr_t);
+extern template void AlignFail<8>(uintptr_t);
+#endif
+
+// TcParser implements most of the parsing logic for tailcall tables.
+class PROTOBUF_EXPORT TcParser final {
+ public:
+  static const char* GenericFallback(PROTOBUF_TC_PARAM_DECL);
+  static const char* GenericFallbackLite(PROTOBUF_TC_PARAM_DECL);
+
+  static const char* ParseLoop(MessageLite* msg, const char* ptr,
+                               ParseContext* ctx,
+                               const TcParseTableBase* table);
+
+  // Functions referenced by generated fast tables (numeric types):
+  //   F: fixed      V: varint     Z: zigzag
+  //   8/32/64: storage type width (bits)
+  //   S: singular   R: repeated   P: packed
+  //   1/2: tag length (bytes)
+
+  // Fixed:
+  static const char* FastF32S1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF32S2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF32R1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF32R2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF32P1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF32P2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF64S1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF64S2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF64R1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF64R2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF64P1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastF64P2(PROTOBUF_TC_PARAM_DECL);
+
+  // Varint:
+  static const char* FastV8S1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV8S2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV8R1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV8R2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV8P1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV8P2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV32S1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV32S2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV32R1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV32R2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV32P1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV32P2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV64S1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV64S2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV64R1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV64R2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV64P1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastV64P2(PROTOBUF_TC_PARAM_DECL);
+
+  // Varint (with zigzag):
+  static const char* FastZ32S1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ32S2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ32R1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ32R2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ32P1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ32P2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ64S1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ64S2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ64R1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ64R2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ64P1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastZ64P2(PROTOBUF_TC_PARAM_DECL);
+
+  // Functions referenced by generated fast tables (closed enum):
+  //   E: closed enum (N.B.: open enums use V32, above)
+  //   r: enum range  v: enum validator (_IsValid function)
+  //   S: singular   R: repeated
+  //   1/2: tag length (bytes)
+  static const char* FastErS1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastErS2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastErR1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastErR2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastEvS1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastEvS2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastEvR1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastEvR2(PROTOBUF_TC_PARAM_DECL);
+
+  // Functions referenced by generated fast tables (string types):
+  //   B: bytes      S: string     U: UTF-8 string
+  //   (empty): ArenaStringPtr     i: InlinedString
+  //   S: singular   R: repeated
+  //   1/2: tag length (bytes)
+  static const char* FastBS1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastBS2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastBR1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastBR2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastSS1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastSS2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastSR1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastSR2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastUS1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastUS2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastUR1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastUR2(PROTOBUF_TC_PARAM_DECL);
+
+  static const char* FastBiS1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastBiS2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastSiS1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastSiS2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastUiS1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastUiS2(PROTOBUF_TC_PARAM_DECL);
+
+  // Functions referenced by generated fast tables (message types):
+  //   M: message    G: group
+  //   S: singular   R: repeated
+  //   1/2: tag length (bytes)
+  static const char* FastMS1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastMS2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastMR1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastMR2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastGS1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastGS2(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastGR1(PROTOBUF_TC_PARAM_DECL);
+  static const char* FastGR2(PROTOBUF_TC_PARAM_DECL);
+
+  template <typename T>
+  static inline T& RefAt(void* x, size_t offset) {
+    T* target = reinterpret_cast<T*>(static_cast<char*>(x) + offset);
+#ifndef NDEBUG
+    if (PROTOBUF_PREDICT_FALSE(
+            reinterpret_cast<uintptr_t>(target) % alignof(T) != 0)) {
+      AlignFail<alignof(T)>(reinterpret_cast<uintptr_t>(target));
+    }
+#endif
+    return *target;
+  }
+
+  template <typename T>
+  static inline const T& RefAt(const void* x, size_t offset) {
+    const T* target =
+        reinterpret_cast<const T*>(static_cast<const char*>(x) + offset);
+#ifndef NDEBUG
+    if (PROTOBUF_PREDICT_FALSE(
+            reinterpret_cast<uintptr_t>(target) % alignof(T) != 0)) {
+      AlignFail<alignof(T)>(reinterpret_cast<uintptr_t>(target));
+    }
+#endif
+    return *target;
+  }
+
+  template <typename T>
+  static inline T ReadAt(const void* x, size_t offset) {
+    T out;
+    memcpy(&out, static_cast<const char*>(x) + offset, sizeof(T));
+    return out;
+  }
+
+  // Mini parsing:
+  //
+  // This function parses a field from incoming data based on metadata stored in
+  // the message definition. If the field is not defined in the message, it is
+  // stored in either the ExtensionSet (if applicable) or the UnknownFieldSet.
+  //
+  // NOTE: Currently, this function only calls the table-level fallback
+  // function, so it should only be called as the fallback from fast table
+  // parsing.
+  static const char* MiniParse(PROTOBUF_TC_PARAM_DECL);
+
+ private:
+  friend class GeneratedTcTableLiteTest;
+
+  template <typename TagType, bool group_coding>
+  static inline const char* SingularParseMessageAuxImpl(PROTOBUF_TC_PARAM_DECL);
+  template <typename TagType, bool group_coding>
+  static inline const char* RepeatedParseMessageAuxImpl(PROTOBUF_TC_PARAM_DECL);
+
+  static inline PROTOBUF_ALWAYS_INLINE void SyncHasbits(
+      MessageLite* msg, uint64_t hasbits, const TcParseTableBase* table) {
+    const uint32_t has_bits_offset = table->has_bits_offset;
+    if (has_bits_offset) {
+      // Only the first 32 has-bits are updated. Nothing above those is stored,
+      // but e.g. messages without has-bits update the upper bits.
+      RefAt<uint32_t>(msg, has_bits_offset) = static_cast<uint32_t>(hasbits);
+    }
+  }
+
+  static const char* TagDispatch(PROTOBUF_TC_PARAM_DECL);
+  static const char* ToTagDispatch(PROTOBUF_TC_PARAM_DECL);
+  static const char* ToParseLoop(PROTOBUF_TC_PARAM_DECL);
+  static const char* Error(PROTOBUF_TC_PARAM_DECL);
+
+  static const char* FastUnknownEnumFallback(PROTOBUF_TC_PARAM_DECL);
+
+  class ScopedArenaSwap;
+
+  template <class MessageBaseT, class UnknownFieldsT>
+  static const char* GenericFallbackImpl(PROTOBUF_TC_PARAM_DECL) {
+#define CHK_(x) \
+  if (PROTOBUF_PREDICT_FALSE(!(x))) return nullptr /* NOLINT */
+
+    SyncHasbits(msg, hasbits, table);
+    CHK_(ptr);
+    uint32_t tag = data.tag();
+    if ((tag & 7) == WireFormatLite::WIRETYPE_END_GROUP || tag == 0) {
+      ctx->SetLastTag(tag);
+      return ptr;
+    }
+    uint32_t num = tag >> 3;
+    if (table->extension_range_low <= num &&
+        num <= table->extension_range_high) {
+      return RefAt<ExtensionSet>(msg, table->extension_offset)
+          .ParseField(tag, ptr,
+                      static_cast<const MessageBaseT*>(table->default_instance),
+                      &msg->_internal_metadata_, ctx);
+    }
+    return UnknownFieldParse(
+        tag, msg->_internal_metadata_.mutable_unknown_fields<UnknownFieldsT>(),
+        ptr, ctx);
+#undef CHK_
+  }
+
+  // Note: `inline` is needed on template function declarations below to avoid
+  // -Wattributes diagnostic in GCC.
+
+  // Implementations for fast fixed field parsing functions:
+  template <typename LayoutType, typename TagType>
+  static inline const char* SingularFixed(PROTOBUF_TC_PARAM_DECL);
+  template <typename LayoutType, typename TagType>
+  static inline const char* RepeatedFixed(PROTOBUF_TC_PARAM_DECL);
+  template <typename LayoutType, typename TagType>
+  static inline const char* PackedFixed(PROTOBUF_TC_PARAM_DECL);
+
+  // Implementations for fast varint field parsing functions:
+  template <typename FieldType, typename TagType, bool zigzag = false>
+  static inline const char* SingularVarint(PROTOBUF_TC_PARAM_DECL);
+  template <typename FieldType, typename TagType, bool zigzag = false>
+  static inline const char* RepeatedVarint(PROTOBUF_TC_PARAM_DECL);
+  template <typename FieldType, typename TagType, bool zigzag = false>
+  static inline const char* PackedVarint(PROTOBUF_TC_PARAM_DECL);
+
+  // Helper for ints > 127:
+  template <typename FieldType, typename TagType, bool zigzag = false>
+  static const char* SingularVarBigint(PROTOBUF_TC_PARAM_DECL);
+
+  // Implementations for fast enum field parsing functions:
+  template <typename TagType, uint16_t xform_val>
+  static inline const char* SingularEnum(PROTOBUF_TC_PARAM_DECL);
+  template <typename TagType, uint16_t xform_val>
+  static inline const char* RepeatedEnum(PROTOBUF_TC_PARAM_DECL);
+
+  // Implementations for fast string field parsing functions:
+  enum Utf8Type { kNoUtf8 = 0, kUtf8 = 1, kUtf8ValidateOnly = 2 };
+  template <typename TagType, Utf8Type utf8>
+  static inline const char* SingularString(PROTOBUF_TC_PARAM_DECL);
+  template <typename TagType, Utf8Type utf8>
+  static inline const char* RepeatedString(PROTOBUF_TC_PARAM_DECL);
+
+  // Mini field lookup:
+  static const TcParseTableBase::FieldEntry* FindFieldEntry(
+      const TcParseTableBase* table, uint32_t field_num);
+  static StringPiece MessageName(const TcParseTableBase* table);
+  static StringPiece FieldName(const TcParseTableBase* table,
+                                     const TcParseTableBase::FieldEntry*);
+  static bool ChangeOneof(const TcParseTableBase* table,
+                          const TcParseTableBase::FieldEntry& entry,
+                          uint32_t field_num, ParseContext* ctx,
+                          MessageLite* msg);
+
+  // UTF-8 validation:
+  static void ReportFastUtf8Error(uint32_t decoded_tag,
+                                  const TcParseTableBase* table);
+  static bool MpVerifyUtf8(StringPiece wire_bytes,
+                           const TcParseTableBase* table,
+                           const TcParseTableBase::FieldEntry& entry,
+                           uint16_t xform_val);
+
+  // For FindFieldEntry tests:
+  friend class FindFieldEntryTest;
+  static constexpr const uint32_t kMtSmallScanSize = 4;
+
+  // Mini parsing:
+  static const char* MpVarint(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpRepeatedVarint(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpPackedVarint(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpFixed(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpRepeatedFixed(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpPackedFixed(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpString(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpRepeatedString(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpMessage(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpRepeatedMessage(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpMap(PROTOBUF_TC_PARAM_DECL);
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_IMPL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_util.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_util.h
new file mode 100644
index 0000000..71d15cd
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/generated_message_util.h
@@ -0,0 +1,214 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This file contains miscellaneous helper code used by generated code --
+// including lite types -- but which should not be used directly by users.
+
+#ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_UTIL_H__
+#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_UTIL_H__
+
+#include <assert.h>
+
+#include <atomic>
+#include <climits>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/once.h>  // Add direct dep on port for pb.cc
+#include <google/protobuf/port.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/any.h>
+#include <google/protobuf/has_bits.h>
+#include <google/protobuf/implicit_weak_message.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/stubs/casts.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+
+class Arena;
+class Message;
+
+namespace io {
+class CodedInputStream;
+}
+
+namespace internal {
+
+template <typename To, typename From>
+inline To DownCast(From* f) {
+  return PROTOBUF_NAMESPACE_ID::internal::down_cast<To>(f);
+}
+template <typename To, typename From>
+inline To DownCast(From& f) {
+  return PROTOBUF_NAMESPACE_ID::internal::down_cast<To>(f);
+}
+
+
+// This fastpath inlines a single branch instead of having to make the
+// InitProtobufDefaults function call.
+// It also generates less inlined code than a function-scope static initializer.
+PROTOBUF_EXPORT extern std::atomic<bool> init_protobuf_defaults_state;
+PROTOBUF_EXPORT void InitProtobufDefaultsSlow();
+PROTOBUF_EXPORT inline void InitProtobufDefaults() {
+  if (PROTOBUF_PREDICT_FALSE(
+          !init_protobuf_defaults_state.load(std::memory_order_acquire))) {
+    InitProtobufDefaultsSlow();
+  }
+}
+
+// This used by proto1
+PROTOBUF_EXPORT inline const std::string& GetEmptyString() {
+  InitProtobufDefaults();
+  return GetEmptyStringAlreadyInited();
+}
+
+
+// True if IsInitialized() is true for all elements of t.  Type is expected
+// to be a RepeatedPtrField<some message type>.  It's useful to have this
+// helper here to keep the protobuf compiler from ever having to emit loops in
+// IsInitialized() methods.  We want the C++ compiler to inline this or not
+// as it sees fit.
+template <typename Msg>
+bool AllAreInitialized(const RepeatedPtrField<Msg>& t) {
+  for (int i = t.size(); --i >= 0;) {
+    if (!t.Get(i).IsInitialized()) return false;
+  }
+  return true;
+}
+
+// "Weak" variant of AllAreInitialized, used to implement implicit weak fields.
+// This version operates on MessageLite to avoid introducing a dependency on the
+// concrete message type.
+template <class T>
+bool AllAreInitializedWeak(const RepeatedPtrField<T>& t) {
+  for (int i = t.size(); --i >= 0;) {
+    if (!reinterpret_cast<const RepeatedPtrFieldBase&>(t)
+             .Get<ImplicitWeakTypeHandler<T> >(i)
+             .IsInitialized()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+inline bool IsPresent(const void* base, uint32_t hasbit) {
+  const uint32_t* has_bits_array = static_cast<const uint32_t*>(base);
+  return (has_bits_array[hasbit / 32] & (1u << (hasbit & 31))) != 0;
+}
+
+inline bool IsOneofPresent(const void* base, uint32_t offset, uint32_t tag) {
+  const uint32_t* oneof = reinterpret_cast<const uint32_t*>(
+      static_cast<const uint8_t*>(base) + offset);
+  return *oneof == tag >> 3;
+}
+
+typedef void (*SpecialSerializer)(const uint8_t* base, uint32_t offset,
+                                  uint32_t tag, uint32_t has_offset,
+                                  io::CodedOutputStream* output);
+
+PROTOBUF_EXPORT void ExtensionSerializer(const MessageLite* extendee,
+                                         const uint8_t* ptr, uint32_t offset,
+                                         uint32_t tag, uint32_t has_offset,
+                                         io::CodedOutputStream* output);
+PROTOBUF_EXPORT void UnknownFieldSerializerLite(const uint8_t* base,
+                                                uint32_t offset, uint32_t tag,
+                                                uint32_t has_offset,
+                                                io::CodedOutputStream* output);
+
+PROTOBUF_EXPORT MessageLite* DuplicateIfNonNullInternal(MessageLite* message);
+PROTOBUF_EXPORT MessageLite* GetOwnedMessageInternal(Arena* message_arena,
+                                                     MessageLite* submessage,
+                                                     Arena* submessage_arena);
+PROTOBUF_EXPORT void GenericSwap(MessageLite* m1, MessageLite* m2);
+// We specialize GenericSwap for non-lite messages to benefit from reflection.
+PROTOBUF_EXPORT void GenericSwap(Message* m1, Message* m2);
+
+template <typename T>
+T* DuplicateIfNonNull(T* message) {
+  // The casts must be reinterpret_cast<> because T might be a forward-declared
+  // type that the compiler doesn't know is related to MessageLite.
+  return reinterpret_cast<T*>(
+      DuplicateIfNonNullInternal(reinterpret_cast<MessageLite*>(message)));
+}
+
+template <typename T>
+T* GetOwnedMessage(Arena* message_arena, T* submessage,
+                   Arena* submessage_arena) {
+  // The casts must be reinterpret_cast<> because T might be a forward-declared
+  // type that the compiler doesn't know is related to MessageLite.
+  return reinterpret_cast<T*>(GetOwnedMessageInternal(
+      message_arena, reinterpret_cast<MessageLite*>(submessage),
+      submessage_arena));
+}
+
+// Hide atomic from the public header and allow easy change to regular int
+// on platforms where the atomic might have a perf impact.
+class PROTOBUF_EXPORT CachedSize {
+ public:
+  int Get() const { return size_.load(std::memory_order_relaxed); }
+  void Set(int size) { size_.store(size, std::memory_order_relaxed); }
+
+ private:
+  std::atomic<int> size_{0};
+};
+
+PROTOBUF_EXPORT void DestroyMessage(const void* message);
+PROTOBUF_EXPORT void DestroyString(const void* s);
+// Destroy (not delete) the message
+inline void OnShutdownDestroyMessage(const void* ptr) {
+  OnShutdownRun(DestroyMessage, ptr);
+}
+// Destroy the string (call std::string destructor)
+inline void OnShutdownDestroyString(const std::string* ptr) {
+  OnShutdownRun(DestroyString, ptr);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_GENERATED_MESSAGE_UTIL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/has_bits.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/has_bits.h
new file mode 100644
index 0000000..f8a4587
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/has_bits.h
@@ -0,0 +1,117 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_HAS_BITS_H__
+#define GOOGLE_PROTOBUF_HAS_BITS_H__
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/port.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+template <size_t doublewords>
+class HasBits {
+ public:
+  PROTOBUF_NDEBUG_INLINE constexpr HasBits() : has_bits_{} {}
+
+  PROTOBUF_NDEBUG_INLINE void Clear() {
+    memset(has_bits_, 0, sizeof(has_bits_));
+  }
+
+  PROTOBUF_NDEBUG_INLINE uint32_t& operator[](int index) {
+    return has_bits_[index];
+  }
+
+  PROTOBUF_NDEBUG_INLINE const uint32_t& operator[](int index) const {
+    return has_bits_[index];
+  }
+
+  bool operator==(const HasBits<doublewords>& rhs) const {
+    return memcmp(has_bits_, rhs.has_bits_, sizeof(has_bits_)) == 0;
+  }
+
+  bool operator!=(const HasBits<doublewords>& rhs) const {
+    return !(*this == rhs);
+  }
+
+  void Or(const HasBits<doublewords>& rhs) {
+    for (size_t i = 0; i < doublewords; i++) has_bits_[i] |= rhs[i];
+  }
+
+  bool empty() const;
+
+ private:
+  uint32_t has_bits_[doublewords];
+};
+
+template <>
+inline bool HasBits<1>::empty() const {
+  return !has_bits_[0];
+}
+
+template <>
+inline bool HasBits<2>::empty() const {
+  return !(has_bits_[0] | has_bits_[1]);
+}
+
+template <>
+inline bool HasBits<3>::empty() const {
+  return !(has_bits_[0] | has_bits_[1] | has_bits_[2]);
+}
+
+template <>
+inline bool HasBits<4>::empty() const {
+  return !(has_bits_[0] | has_bits_[1] | has_bits_[2] | has_bits_[3]);
+}
+
+template <size_t doublewords>
+inline bool HasBits<doublewords>::empty() const {
+  for (size_t i = 0; i < doublewords; ++i) {
+    if (has_bits_[i]) return false;
+  }
+  return true;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_HAS_BITS_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/implicit_weak_message.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/implicit_weak_message.h
new file mode 100644
index 0000000..c358c14
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/implicit_weak_message.h
@@ -0,0 +1,213 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_IMPLICIT_WEAK_MESSAGE_H__
+#define GOOGLE_PROTOBUF_IMPLICIT_WEAK_MESSAGE_H__
+
+#include <string>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+// This file is logically internal-only and should only be used by protobuf
+// generated code.
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// An implementation of MessageLite that treats all data as unknown. This type
+// acts as a placeholder for an implicit weak field in the case where the true
+// message type does not get linked into the binary.
+class PROTOBUF_EXPORT ImplicitWeakMessage : public MessageLite {
+ public:
+  ImplicitWeakMessage() : data_(new std::string) {}
+  explicit constexpr ImplicitWeakMessage(ConstantInitialized)
+      : data_(nullptr) {}
+  explicit ImplicitWeakMessage(Arena* arena)
+      : MessageLite(arena), data_(new std::string) {}
+
+  ~ImplicitWeakMessage() override {
+    // data_ will be null in the default instance, but we can safely call delete
+    // here because the default instance will never be destroyed.
+    delete data_;
+  }
+
+  static const ImplicitWeakMessage* default_instance();
+
+  std::string GetTypeName() const override { return ""; }
+
+  MessageLite* New(Arena* arena) const override {
+    return Arena::CreateMessage<ImplicitWeakMessage>(arena);
+  }
+
+  void Clear() override { data_->clear(); }
+
+  bool IsInitialized() const override { return true; }
+
+  void CheckTypeAndMergeFrom(const MessageLite& other) override {
+    const std::string* other_data =
+        static_cast<const ImplicitWeakMessage&>(other).data_;
+    if (other_data != nullptr) {
+      data_->append(*other_data);
+    }
+  }
+
+  const char* _InternalParse(const char* ptr, ParseContext* ctx) final;
+
+  size_t ByteSizeLong() const override {
+    return data_ == nullptr ? 0 : data_->size();
+  }
+
+  uint8_t* _InternalSerialize(uint8_t* target,
+                              io::EpsCopyOutputStream* stream) const final {
+    if (data_ == nullptr) {
+      return target;
+    }
+    return stream->WriteRaw(data_->data(), data_->size(),
+                            target);
+  }
+
+  int GetCachedSize() const override {
+    return data_ == nullptr ? 0 : static_cast<int>(data_->size());
+  }
+
+  typedef void InternalArenaConstructable_;
+
+ private:
+  // This std::string is allocated on the heap, but we use a raw pointer so that
+  // the default instance can be constant-initialized. In the const methods, we
+  // have to handle the possibility of data_ being null.
+  std::string* data_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImplicitWeakMessage);
+};
+
+struct ImplicitWeakMessageDefaultType;
+extern ImplicitWeakMessageDefaultType implicit_weak_message_default_instance;
+
+// A type handler for use with implicit weak repeated message fields.
+template <typename ImplicitWeakType>
+class ImplicitWeakTypeHandler {
+ public:
+  typedef MessageLite Type;
+  static constexpr bool Moveable = false;
+
+  static inline MessageLite* NewFromPrototype(const MessageLite* prototype,
+                                              Arena* arena = nullptr) {
+    return prototype->New(arena);
+  }
+
+  static inline void Delete(MessageLite* value, Arena* arena) {
+    if (arena == nullptr) {
+      delete value;
+    }
+  }
+  static inline Arena* GetArena(MessageLite* value) {
+    return value->GetArena();
+  }
+  static inline void Clear(MessageLite* value) { value->Clear(); }
+  static void Merge(const MessageLite& from, MessageLite* to) {
+    to->CheckTypeAndMergeFrom(from);
+  }
+};
+
+}  // namespace internal
+
+template <typename T>
+struct WeakRepeatedPtrField {
+  using TypeHandler = internal::ImplicitWeakTypeHandler<T>;
+  constexpr WeakRepeatedPtrField() : weak() {}
+  explicit WeakRepeatedPtrField(Arena* arena) : weak(arena) {}
+  ~WeakRepeatedPtrField() { weak.template Destroy<TypeHandler>(); }
+
+  typedef internal::RepeatedPtrIterator<MessageLite> iterator;
+  typedef internal::RepeatedPtrIterator<const MessageLite> const_iterator;
+  typedef internal::RepeatedPtrOverPtrsIterator<MessageLite*, void*>
+      pointer_iterator;
+  typedef internal::RepeatedPtrOverPtrsIterator<const MessageLite* const,
+                                                const void* const>
+      const_pointer_iterator;
+
+  iterator begin() { return iterator(base().raw_data()); }
+  const_iterator begin() const { return iterator(base().raw_data()); }
+  const_iterator cbegin() const { return begin(); }
+  iterator end() { return begin() + base().size(); }
+  const_iterator end() const { return begin() + base().size(); }
+  const_iterator cend() const { return end(); }
+  pointer_iterator pointer_begin() {
+    return pointer_iterator(base().raw_mutable_data());
+  }
+  const_pointer_iterator pointer_begin() const {
+    return const_pointer_iterator(base().raw_mutable_data());
+  }
+  pointer_iterator pointer_end() {
+    return pointer_iterator(base().raw_mutable_data() + base().size());
+  }
+  const_pointer_iterator pointer_end() const {
+    return const_pointer_iterator(base().raw_mutable_data() + base().size());
+  }
+
+  MessageLite* AddWeak(const MessageLite* prototype) {
+    return base().AddWeak(prototype);
+  }
+  T* Add() { return weak.Add(); }
+  void Clear() { base().template Clear<TypeHandler>(); }
+  void MergeFrom(const WeakRepeatedPtrField& other) {
+    base().template MergeFrom<TypeHandler>(other.base());
+  }
+  void InternalSwap(WeakRepeatedPtrField* other) {
+    base().InternalSwap(&other->base());
+  }
+
+  const internal::RepeatedPtrFieldBase& base() const { return weak; }
+  internal::RepeatedPtrFieldBase& base() { return weak; }
+  // Union disables running the destructor. Which would create a strong link.
+  // Instead we explicitly destroy the underlying base through the virtual
+  // destructor.
+  union {
+    RepeatedPtrField<T> weak;
+  };
+};
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_IMPLICIT_WEAK_MESSAGE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/inlined_string_field.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/inlined_string_field.h
new file mode 100644
index 0000000..79e37d4
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/inlined_string_field.h
@@ -0,0 +1,532 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_INLINED_STRING_FIELD_H__
+#define GOOGLE_PROTOBUF_INLINED_STRING_FIELD_H__
+
+#include <string>
+#include <utility>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/message_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+
+class Arena;
+
+namespace internal {
+
+// InlinedStringField wraps a std::string instance and exposes an API similar to
+// ArenaStringPtr's wrapping of a std::string* instance.
+//
+// default_value parameters are taken for consistency with ArenaStringPtr, but
+// are not used for most methods. With inlining, these should be removed from
+// the generated binary.
+//
+// InlinedStringField has a donating mechanism that allows string buffer
+// allocated on arena. A string is donated means both the string container and
+// the data buffer are on arena. The donating mechanism here is similar to the
+// one in ArenaStringPtr with some differences:
+//
+// When an InlinedStringField is constructed, the donating state is true. This
+// is because the string container is directly stored in the message on the
+// arena:
+//
+//   Construction: donated=true
+//   Arena:
+//   +-----------------------+
+//   |Message foo:           |
+//   | +-------------------+ |
+//   | |InlinedStringField:| |
+//   | | +-----+           | |
+//   | | | | | |           | |
+//   | | +-----+           | |
+//   | +-------------------+ |
+//   +-----------------------+
+//
+// When lvalue Set is called, the donating state is still true. String data will
+// be allocated on the arena:
+//
+//   Lvalue Set: donated=true
+//   Arena:
+//   +-----------------------+
+//   |Message foo:           |
+//   | +-------------------+ |
+//   | |InlinedStringField:| |
+//   | | +-----+           | |
+//   | | | | | |           | |
+//   | | +|----+           | |
+//   | +--|----------------+ |
+//   |    V                  |
+//   |  +----------------+   |
+//   |  |'f','o','o',... |   |
+//   |  +----------------+   |
+//   +-----------------------+
+//
+// Some operations will undonate a donated string, including: Mutable,
+// SetAllocated, Rvalue Set, and Swap with a non-donated string.
+//
+// For more details of the donating states transitions, go/pd-inlined-string.
+class PROTOBUF_EXPORT InlinedStringField {
+ public:
+  InlinedStringField() { Init(); }
+  inline void Init() { new (get_mutable()) std::string(); }
+  // Add the dummy parameter just to make InlinedStringField(nullptr)
+  // unambiguous.
+  constexpr InlinedStringField(
+      const ExplicitlyConstructed<std::string>* /*default_value*/,
+      bool /*dummy*/)
+      : value_{} {}
+  explicit InlinedStringField(const std::string& default_value);
+  explicit InlinedStringField(Arena* arena);
+  ~InlinedStringField() { Destruct(); }
+
+  // Lvalue Set. To save space, we pack the donating states of multiple
+  // InlinedStringFields into an uint32_t `donating_states`. The `mask`
+  // indicates the position of the bit for this InlinedStringField. `donated` is
+  // whether this field is donated.
+  //
+  // The caller should guarantee that:
+  //
+  //   `donated == ((donating_states & ~mask) != 0)`
+  //
+  // This method never changes the `donating_states`.
+  void Set(ConstStringParam value, Arena* arena, bool donated,
+           uint32_t* donating_states, uint32_t mask, MessageLite* msg);
+
+  // Rvalue Set. If this field is donated, this method will undonate this field
+  // by mutating the `donating_states` according to `mask`.
+  void Set(std::string&& value, Arena* arena, bool donated,
+           uint32_t* donating_states, uint32_t mask, MessageLite* msg);
+
+  void Set(const char* str, ::google::protobuf::Arena* arena, bool donated,
+           uint32_t* donating_states, uint32_t mask, MessageLite* msg);
+
+  void Set(const char* str, size_t size, ::google::protobuf::Arena* arena, bool donated,
+           uint32_t* donating_states, uint32_t mask, MessageLite* msg);
+
+  template <typename RefWrappedType>
+  void Set(std::reference_wrapper<RefWrappedType> const_string_ref,
+           ::google::protobuf::Arena* arena, bool donated, uint32_t* donating_states,
+           uint32_t mask, MessageLite* msg);
+
+  void SetBytes(ConstStringParam value, Arena* arena, bool donated,
+                uint32_t* donating_states, uint32_t mask, MessageLite* msg);
+
+  void SetBytes(std::string&& value, Arena* arena, bool donated,
+                uint32_t* donating_states, uint32_t mask, MessageLite* msg);
+
+  void SetBytes(const char* str, ::google::protobuf::Arena* arena, bool donated,
+                uint32_t* donating_states, uint32_t mask, MessageLite* msg);
+
+  void SetBytes(const void* p, size_t size, ::google::protobuf::Arena* arena,
+                bool donated, uint32_t* donating_states, uint32_t mask,
+                MessageLite* msg);
+
+  template <typename RefWrappedType>
+  void SetBytes(std::reference_wrapper<RefWrappedType> const_string_ref,
+                ::google::protobuf::Arena* arena, bool donated, uint32_t* donating_states,
+                uint32_t mask, MessageLite* msg);
+
+  PROTOBUF_NDEBUG_INLINE void SetNoArena(StringPiece value);
+  PROTOBUF_NDEBUG_INLINE void SetNoArena(std::string&& value);
+
+  // Basic accessors.
+  PROTOBUF_NDEBUG_INLINE const std::string& Get() const { return GetNoArena(); }
+  PROTOBUF_NDEBUG_INLINE const std::string& GetNoArena() const;
+
+  // Mutable returns a std::string* instance that is heap-allocated. If this
+  // field is donated, this method undonates this field by mutating the
+  // `donating_states` according to `mask`, and copies the content of the
+  // original string to the returning string.
+  std::string* Mutable(Arena* arena, bool donated, uint32_t* donating_states,
+                       uint32_t mask, MessageLite* msg);
+  std::string* Mutable(const LazyString& default_value, Arena* arena,
+                       bool donated, uint32_t* donating_states, uint32_t mask,
+                       MessageLite* msg);
+
+  // Mutable(nullptr_t) is an overload to explicitly support Mutable(nullptr)
+  // calls used by the internal parser logic. This provides API equivalence with
+  // ArenaStringPtr, while still protecting against calls with arena pointers.
+  std::string* Mutable(std::nullptr_t);
+  std::string* MutableNoCopy(std::nullptr_t);
+
+  // Takes a std::string that is heap-allocated, and takes ownership. The
+  // std::string's destructor is registered with the arena. Used to implement
+  // set_allocated_<field> in generated classes.
+  //
+  // If this field is donated, this method undonates this field by mutating the
+  // `donating_states` according to `mask`.
+  void SetAllocated(const std::string* default_value, std::string* value,
+                    Arena* arena, bool donated, uint32_t* donating_states,
+                    uint32_t mask, MessageLite* msg);
+
+  void SetAllocatedNoArena(const std::string* default_value,
+                           std::string* value);
+
+  // Release returns a std::string* instance that is heap-allocated and is not
+  // Own()'d by any arena. If the field is not set, this returns nullptr. The
+  // caller retains ownership. Clears this field back to nullptr state. Used to
+  // implement release_<field>() methods on generated classes.
+  PROTOBUF_NODISCARD std::string* Release(Arena* arena, bool donated);
+  PROTOBUF_NODISCARD std::string* Release();
+
+  // --------------------------------------------------------
+  // Below functions will be removed in subsequent code change
+  // --------------------------------------------------------
+#ifdef DEPRECATED_METHODS_TO_BE_DELETED
+  PROTOBUF_NODISCARD std::string* Release(const std::string*, Arena* arena,
+                                          bool donated) {
+    return Release(arena, donated);
+  }
+
+  PROTOBUF_NODISCARD std::string* ReleaseNonDefault(const std::string*,
+                                                    Arena* arena) {
+    return Release();
+  }
+
+  std::string* ReleaseNonDefaultNoArena(const std::string* default_value) {
+    return Release();
+  }
+
+  void Set(const std::string*, ConstStringParam value, Arena* arena,
+           bool donated, uint32_t* donating_states, uint32_t mask,
+           MessageLite* msg) {
+    Set(value, arena, donated, donating_states, mask, msg);
+  }
+
+  void Set(const std::string*, std::string&& value, Arena* arena, bool donated,
+           uint32_t* donating_states, uint32_t mask, MessageLite* msg) {
+    Set(std::move(value), arena, donated, donating_states, mask, msg);
+  }
+
+
+  template <typename FirstParam>
+  void Set(FirstParam, const char* str, ::google::protobuf::Arena* arena, bool donated,
+           uint32_t* donating_states, uint32_t mask, MessageLite* msg) {
+    Set(str, arena, donated, donating_states, mask, msg);
+  }
+
+  template <typename FirstParam>
+  void Set(FirstParam p1, const char* str, size_t size, ::google::protobuf::Arena* arena,
+           bool donated, uint32_t* donating_states, uint32_t mask,
+           MessageLite* msg) {
+    Set(str, size, arena, donated, donating_states, mask, msg);
+  }
+
+  template <typename FirstParam, typename RefWrappedType>
+  void Set(FirstParam p1,
+           std::reference_wrapper<RefWrappedType> const_string_ref,
+           ::google::protobuf::Arena* arena, bool donated, uint32_t* donating_states,
+           uint32_t mask, MessageLite* msg) {
+    Set(const_string_ref, arena, donated, donating_states, mask, msg);
+  }
+
+  void SetBytes(const std::string*, ConstStringParam value, Arena* arena,
+                bool donated, uint32_t* donating_states, uint32_t mask,
+                MessageLite* msg) {
+    Set(value, arena, donated, donating_states, mask, msg);
+  }
+
+
+  void SetBytes(const std::string*, std::string&& value, Arena* arena,
+                bool donated, uint32_t* donating_states, uint32_t mask,
+                MessageLite* msg) {
+    Set(std::move(value), arena, donated, donating_states, mask, msg);
+  }
+
+  template <typename FirstParam>
+  void SetBytes(FirstParam p1, const char* str, ::google::protobuf::Arena* arena,
+                bool donated, uint32_t* donating_states, uint32_t mask,
+                MessageLite* msg) {
+    SetBytes(str, arena, donated, donating_states, mask, msg);
+  }
+
+  template <typename FirstParam>
+  void SetBytes(FirstParam p1, const void* p, size_t size,
+                ::google::protobuf::Arena* arena, bool donated, uint32_t* donating_states,
+                uint32_t mask, MessageLite* msg) {
+    SetBytes(p, size, arena, donated, donating_states, mask, msg);
+  }
+
+  template <typename FirstParam, typename RefWrappedType>
+  void SetBytes(FirstParam p1,
+                std::reference_wrapper<RefWrappedType> const_string_ref,
+                ::google::protobuf::Arena* arena, bool donated, uint32_t* donating_states,
+                uint32_t mask, MessageLite* msg) {
+    SetBytes(const_string_ref.get(), arena, donated, donating_states, mask,
+             msg);
+  }
+
+  void SetNoArena(const std::string*, StringPiece value) {
+    SetNoArena(value);
+  }
+  void SetNoArena(const std::string*, std::string&& value) {
+    SetNoArena(std::move(value));
+  }
+
+  std::string* Mutable(ArenaStringPtr::EmptyDefault, Arena* arena, bool donated,
+                       uint32_t* donating_states, uint32_t mask,
+                       MessageLite* msg) {
+    return Mutable(arena, donated, donating_states, mask, msg);
+  }
+
+  PROTOBUF_NDEBUG_INLINE std::string* MutableNoArenaNoDefault(
+      const std::string* /*default_value*/) {
+    return MutableNoCopy(nullptr);
+  }
+
+#endif  // DEPRECATED_METHODS_TO_BE_DELETED
+
+  // Arena-safety semantics: this is guarded by the logic in
+  // Swap()/UnsafeArenaSwap() at the message level, so this method is
+  // 'unsafe' if called directly.
+  inline PROTOBUF_NDEBUG_INLINE static void InternalSwap(
+      InlinedStringField* lhs, Arena* lhs_arena, bool lhs_arena_dtor_registered,
+      MessageLite* lhs_msg,  //
+      InlinedStringField* rhs, Arena* rhs_arena, bool rhs_arena_dtor_registered,
+      MessageLite* rhs_msg);
+
+  // Frees storage (if not on an arena).
+  PROTOBUF_NDEBUG_INLINE void Destroy(const std::string* default_value,
+                                      Arena* arena) {
+    if (arena == nullptr) {
+      DestroyNoArena(default_value);
+    }
+  }
+  PROTOBUF_NDEBUG_INLINE void DestroyNoArena(const std::string* default_value);
+
+  // Clears content, but keeps allocated std::string, to avoid the overhead of
+  // heap operations. After this returns, the content (as seen by the user) will
+  // always be the empty std::string.
+  PROTOBUF_NDEBUG_INLINE void ClearToEmpty() { ClearNonDefaultToEmpty(); }
+  PROTOBUF_NDEBUG_INLINE void ClearNonDefaultToEmpty() {
+    get_mutable()->clear();
+  }
+
+  // Clears content, but keeps allocated std::string if arena != nullptr, to
+  // avoid the overhead of heap operations. After this returns, the content (as
+  // seen by the user) will always be equal to |default_value|.
+  void ClearToDefault(const LazyString& default_value, Arena* arena,
+                      bool donated);
+
+  // Generated code / reflection only! Returns a mutable pointer to the string.
+  PROTOBUF_NDEBUG_INLINE std::string* UnsafeMutablePointer();
+
+  // InlinedStringField doesn't have things like the `default_value` pointer in
+  // ArenaStringPtr.
+  static constexpr bool IsDefault() { return false; }
+  static constexpr bool IsDefault(const std::string*) { return false; }
+
+ private:
+  void Destruct() { get_mutable()->~basic_string(); }
+
+  PROTOBUF_NDEBUG_INLINE std::string* get_mutable();
+  PROTOBUF_NDEBUG_INLINE const std::string* get_const() const;
+
+  alignas(std::string) char value_[sizeof(std::string)];
+
+  std::string* MutableSlow(::google::protobuf::Arena* arena, bool donated,
+                           uint32_t* donating_states, uint32_t mask,
+                           MessageLite* msg);
+
+
+  // When constructed in an Arena, we want our destructor to be skipped.
+  friend class ::google::protobuf::Arena;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+};
+
+inline std::string* InlinedStringField::get_mutable() {
+  return reinterpret_cast<std::string*>(&value_);
+}
+
+inline const std::string* InlinedStringField::get_const() const {
+  return reinterpret_cast<const std::string*>(&value_);
+}
+
+inline InlinedStringField::InlinedStringField(
+    const std::string& default_value) {
+  new (get_mutable()) std::string(default_value);
+}
+
+
+inline InlinedStringField::InlinedStringField(Arena* /*arena*/) { Init(); }
+
+inline const std::string& InlinedStringField::GetNoArena() const {
+  return *get_const();
+}
+
+inline void InlinedStringField::SetAllocatedNoArena(
+    const std::string* /*default_value*/, std::string* value) {
+  if (value == nullptr) {
+    // Currently, inlined string field can't have non empty default.
+    get_mutable()->clear();
+  } else {
+    get_mutable()->assign(std::move(*value));
+    delete value;
+  }
+}
+
+inline void InlinedStringField::DestroyNoArena(const std::string*) {
+  // This is invoked from the generated message's ArenaDtor, which is used to
+  // clean up objects not allocated on the Arena.
+  this->~InlinedStringField();
+}
+
+inline void InlinedStringField::SetNoArena(StringPiece value) {
+  get_mutable()->assign(value.data(), value.length());
+}
+
+inline void InlinedStringField::SetNoArena(std::string&& value) {
+  get_mutable()->assign(std::move(value));
+}
+
+// Caller should make sure rhs_arena allocated rhs, and lhs_arena allocated lhs.
+inline PROTOBUF_NDEBUG_INLINE void InlinedStringField::InternalSwap(
+    InlinedStringField* lhs, Arena* lhs_arena, bool lhs_arena_dtor_registered,
+    MessageLite* lhs_msg,  //
+    InlinedStringField* rhs, Arena* rhs_arena, bool rhs_arena_dtor_registered,
+    MessageLite* rhs_msg) {
+#if GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
+  lhs->get_mutable()->swap(*rhs->get_mutable());
+  if (!lhs_arena_dtor_registered && rhs_arena_dtor_registered) {
+    lhs_msg->OnDemandRegisterArenaDtor(lhs_arena);
+  } else if (lhs_arena_dtor_registered && !rhs_arena_dtor_registered) {
+    rhs_msg->OnDemandRegisterArenaDtor(rhs_arena);
+  }
+#else
+  (void)lhs_arena;
+  (void)rhs_arena;
+  (void)lhs_arena_dtor_registered;
+  (void)rhs_arena_dtor_registered;
+  (void)lhs_msg;
+  (void)rhs_msg;
+  lhs->get_mutable()->swap(*rhs->get_mutable());
+#endif
+}
+
+inline void InlinedStringField::Set(ConstStringParam value, Arena* arena,
+                                    bool donated, uint32_t* /*donating_states*/,
+                                    uint32_t /*mask*/, MessageLite* /*msg*/) {
+  (void)arena;
+  (void)donated;
+  SetNoArena(value);
+}
+
+inline void InlinedStringField::Set(const char* str, ::google::protobuf::Arena* arena,
+                                    bool donated, uint32_t* donating_states,
+                                    uint32_t mask, MessageLite* msg) {
+  Set(ConstStringParam(str), arena, donated, donating_states, mask, msg);
+}
+
+inline void InlinedStringField::Set(const char* str, size_t size,
+                                    ::google::protobuf::Arena* arena, bool donated,
+                                    uint32_t* donating_states, uint32_t mask,
+                                    MessageLite* msg) {
+  Set(ConstStringParam{str, size}, arena, donated, donating_states, mask, msg);
+}
+
+inline void InlinedStringField::SetBytes(ConstStringParam value, Arena* arena,
+                                         bool donated,
+                                         uint32_t* donating_states,
+                                         uint32_t mask, MessageLite* msg) {
+  Set(value, arena, donated, donating_states, mask, msg);
+}
+
+inline void InlinedStringField::SetBytes(std::string&& value, Arena* arena,
+                                         bool donated,
+                                         uint32_t* donating_states,
+                                         uint32_t mask, MessageLite* msg) {
+  Set(std::move(value), arena, donated, donating_states, mask, msg);
+}
+
+inline void InlinedStringField::SetBytes(const char* str,
+                                         ::google::protobuf::Arena* arena, bool donated,
+                                         uint32_t* donating_states,
+                                         uint32_t mask, MessageLite* msg) {
+  Set(str, arena, donated, donating_states, mask, msg);
+}
+
+inline void InlinedStringField::SetBytes(const void* p, size_t size,
+                                         ::google::protobuf::Arena* arena, bool donated,
+                                         uint32_t* donating_states,
+                                         uint32_t mask, MessageLite* msg) {
+  Set(static_cast<const char*>(p), size, arena, donated, donating_states, mask,
+      msg);
+}
+
+template <typename RefWrappedType>
+inline void InlinedStringField::Set(
+    std::reference_wrapper<RefWrappedType> const_string_ref,
+    ::google::protobuf::Arena* arena, bool donated, uint32_t* donating_states,
+    uint32_t mask, MessageLite* msg) {
+  Set(const_string_ref.get(), arena, donated, donating_states, mask, msg);
+}
+
+template <typename RefWrappedType>
+inline void InlinedStringField::SetBytes(
+    std::reference_wrapper<RefWrappedType> const_string_ref,
+    ::google::protobuf::Arena* arena, bool donated, uint32_t* donating_states,
+    uint32_t mask, MessageLite* msg) {
+  Set(const_string_ref.get(), arena, donated, donating_states, mask, msg);
+}
+
+inline std::string* InlinedStringField::UnsafeMutablePointer() {
+  return get_mutable();
+}
+
+inline std::string* InlinedStringField::Mutable(std::nullptr_t) {
+  return get_mutable();
+}
+
+inline std::string* InlinedStringField::MutableNoCopy(std::nullptr_t) {
+  return get_mutable();
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_INLINED_STRING_FIELD_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/coded_stream.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/coded_stream.h
new file mode 100644
index 0000000..6c0dd4a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/coded_stream.h
@@ -0,0 +1,1799 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This file contains the CodedInputStream and CodedOutputStream classes,
+// which wrap a ZeroCopyInputStream or ZeroCopyOutputStream, respectively,
+// and allow you to read or write individual pieces of data in various
+// formats.  In particular, these implement the varint encoding for
+// integers, a simple variable-length encoding in which smaller numbers
+// take fewer bytes.
+//
+// Typically these classes will only be used internally by the protocol
+// buffer library in order to encode and decode protocol buffers.  Clients
+// of the library only need to know about this class if they wish to write
+// custom message parsing or serialization procedures.
+//
+// CodedOutputStream example:
+//   // Write some data to "myfile".  First we write a 4-byte "magic number"
+//   // to identify the file type, then write a length-delimited string.  The
+//   // string is composed of a varint giving the length followed by the raw
+//   // bytes.
+//   int fd = open("myfile", O_CREAT | O_WRONLY);
+//   ZeroCopyOutputStream* raw_output = new FileOutputStream(fd);
+//   CodedOutputStream* coded_output = new CodedOutputStream(raw_output);
+//
+//   int magic_number = 1234;
+//   char text[] = "Hello world!";
+//   coded_output->WriteLittleEndian32(magic_number);
+//   coded_output->WriteVarint32(strlen(text));
+//   coded_output->WriteRaw(text, strlen(text));
+//
+//   delete coded_output;
+//   delete raw_output;
+//   close(fd);
+//
+// CodedInputStream example:
+//   // Read a file created by the above code.
+//   int fd = open("myfile", O_RDONLY);
+//   ZeroCopyInputStream* raw_input = new FileInputStream(fd);
+//   CodedInputStream* coded_input = new CodedInputStream(raw_input);
+//
+//   coded_input->ReadLittleEndian32(&magic_number);
+//   if (magic_number != 1234) {
+//     cerr << "File not in expected format." << endl;
+//     return;
+//   }
+//
+//   uint32_t size;
+//   coded_input->ReadVarint32(&size);
+//
+//   char* text = new char[size + 1];
+//   coded_input->ReadRaw(buffer, size);
+//   text[size] = '\0';
+//
+//   delete coded_input;
+//   delete raw_input;
+//   close(fd);
+//
+//   cout << "Text is: " << text << endl;
+//   delete [] text;
+//
+// For those who are interested, varint encoding is defined as follows:
+//
+// The encoding operates on unsigned integers of up to 64 bits in length.
+// Each byte of the encoded value has the format:
+// * bits 0-6: Seven bits of the number being encoded.
+// * bit 7: Zero if this is the last byte in the encoding (in which
+//   case all remaining bits of the number are zero) or 1 if
+//   more bytes follow.
+// The first byte contains the least-significant 7 bits of the number, the
+// second byte (if present) contains the next-least-significant 7 bits,
+// and so on.  So, the binary number 1011000101011 would be encoded in two
+// bytes as "10101011 00101100".
+//
+// In theory, varint could be used to encode integers of any length.
+// However, for practicality we set a limit at 64 bits.  The maximum encoded
+// length of a number is thus 10 bytes.
+
+#ifndef GOOGLE_PROTOBUF_IO_CODED_STREAM_H__
+#define GOOGLE_PROTOBUF_IO_CODED_STREAM_H__
+
+
+#include <assert.h>
+
+#include <atomic>
+#include <climits>
+#include <cstddef>
+#include <cstring>
+#include <limits>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#if defined(_MSC_VER) && _MSC_VER >= 1300 && !defined(__INTEL_COMPILER)
+// If MSVC has "/RTCc" set, it will complain about truncating casts at
+// runtime.  This file contains some intentional truncating casts.
+#pragma runtime_checks("c", off)
+#endif
+
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/stubs/port.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+class DescriptorPool;
+class MessageFactory;
+class ZeroCopyCodedInputStream;
+
+namespace internal {
+void MapTestForceDeterministic();
+class EpsCopyByteStream;
+}  // namespace internal
+
+namespace io {
+
+// Defined in this file.
+class CodedInputStream;
+class CodedOutputStream;
+
+// Defined in other files.
+class ZeroCopyInputStream;   // zero_copy_stream.h
+class ZeroCopyOutputStream;  // zero_copy_stream.h
+
+// Class which reads and decodes binary data which is composed of varint-
+// encoded integers and fixed-width pieces.  Wraps a ZeroCopyInputStream.
+// Most users will not need to deal with CodedInputStream.
+//
+// Most methods of CodedInputStream that return a bool return false if an
+// underlying I/O error occurs or if the data is malformed.  Once such a
+// failure occurs, the CodedInputStream is broken and is no longer useful.
+// After a failure, callers also should assume writes to "out" args may have
+// occurred, though nothing useful can be determined from those writes.
+class PROTOBUF_EXPORT CodedInputStream {
+ public:
+  // Create a CodedInputStream that reads from the given ZeroCopyInputStream.
+  explicit CodedInputStream(ZeroCopyInputStream* input);
+
+  // Create a CodedInputStream that reads from the given flat array.  This is
+  // faster than using an ArrayInputStream.  PushLimit(size) is implied by
+  // this constructor.
+  explicit CodedInputStream(const uint8_t* buffer, int size);
+
+  // Destroy the CodedInputStream and position the underlying
+  // ZeroCopyInputStream at the first unread byte.  If an error occurred while
+  // reading (causing a method to return false), then the exact position of
+  // the input stream may be anywhere between the last value that was read
+  // successfully and the stream's byte limit.
+  ~CodedInputStream();
+
+  // Return true if this CodedInputStream reads from a flat array instead of
+  // a ZeroCopyInputStream.
+  inline bool IsFlat() const;
+
+  // Skips a number of bytes.  Returns false if an underlying read error
+  // occurs.
+  inline bool Skip(int count);
+
+  // Sets *data to point directly at the unread part of the CodedInputStream's
+  // underlying buffer, and *size to the size of that buffer, but does not
+  // advance the stream's current position.  This will always either produce
+  // a non-empty buffer or return false.  If the caller consumes any of
+  // this data, it should then call Skip() to skip over the consumed bytes.
+  // This may be useful for implementing external fast parsing routines for
+  // types of data not covered by the CodedInputStream interface.
+  bool GetDirectBufferPointer(const void** data, int* size);
+
+  // Like GetDirectBufferPointer, but this method is inlined, and does not
+  // attempt to Refresh() if the buffer is currently empty.
+  PROTOBUF_ALWAYS_INLINE
+  void GetDirectBufferPointerInline(const void** data, int* size);
+
+  // Read raw bytes, copying them into the given buffer.
+  bool ReadRaw(void* buffer, int size);
+
+  // Like ReadRaw, but reads into a string.
+  bool ReadString(std::string* buffer, int size);
+
+
+  // Read a 32-bit little-endian integer.
+  bool ReadLittleEndian32(uint32_t* value);
+  // Read a 64-bit little-endian integer.
+  bool ReadLittleEndian64(uint64_t* value);
+
+  // These methods read from an externally provided buffer. The caller is
+  // responsible for ensuring that the buffer has sufficient space.
+  // Read a 32-bit little-endian integer.
+  static const uint8_t* ReadLittleEndian32FromArray(const uint8_t* buffer,
+                                                    uint32_t* value);
+  // Read a 64-bit little-endian integer.
+  static const uint8_t* ReadLittleEndian64FromArray(const uint8_t* buffer,
+                                                    uint64_t* value);
+
+  // Read an unsigned integer with Varint encoding, truncating to 32 bits.
+  // Reading a 32-bit value is equivalent to reading a 64-bit one and casting
+  // it to uint32_t, but may be more efficient.
+  bool ReadVarint32(uint32_t* value);
+  // Read an unsigned integer with Varint encoding.
+  bool ReadVarint64(uint64_t* value);
+
+  // Reads a varint off the wire into an "int". This should be used for reading
+  // sizes off the wire (sizes of strings, submessages, bytes fields, etc).
+  //
+  // The value from the wire is interpreted as unsigned.  If its value exceeds
+  // the representable value of an integer on this platform, instead of
+  // truncating we return false. Truncating (as performed by ReadVarint32()
+  // above) is an acceptable approach for fields representing an integer, but
+  // when we are parsing a size from the wire, truncating the value would result
+  // in us misparsing the payload.
+  bool ReadVarintSizeAsInt(int* value);
+
+  // Read a tag.  This calls ReadVarint32() and returns the result, or returns
+  // zero (which is not a valid tag) if ReadVarint32() fails.  Also, ReadTag
+  // (but not ReadTagNoLastTag) updates the last tag value, which can be checked
+  // with LastTagWas().
+  //
+  // Always inline because this is only called in one place per parse loop
+  // but it is called for every iteration of said loop, so it should be fast.
+  // GCC doesn't want to inline this by default.
+  PROTOBUF_ALWAYS_INLINE uint32_t ReadTag() {
+    return last_tag_ = ReadTagNoLastTag();
+  }
+
+  PROTOBUF_ALWAYS_INLINE uint32_t ReadTagNoLastTag();
+
+  // This usually a faster alternative to ReadTag() when cutoff is a manifest
+  // constant.  It does particularly well for cutoff >= 127.  The first part
+  // of the return value is the tag that was read, though it can also be 0 in
+  // the cases where ReadTag() would return 0.  If the second part is true
+  // then the tag is known to be in [0, cutoff].  If not, the tag either is
+  // above cutoff or is 0.  (There's intentional wiggle room when tag is 0,
+  // because that can arise in several ways, and for best performance we want
+  // to avoid an extra "is tag == 0?" check here.)
+  PROTOBUF_ALWAYS_INLINE
+  std::pair<uint32_t, bool> ReadTagWithCutoff(uint32_t cutoff) {
+    std::pair<uint32_t, bool> result = ReadTagWithCutoffNoLastTag(cutoff);
+    last_tag_ = result.first;
+    return result;
+  }
+
+  PROTOBUF_ALWAYS_INLINE
+  std::pair<uint32_t, bool> ReadTagWithCutoffNoLastTag(uint32_t cutoff);
+
+  // Usually returns true if calling ReadVarint32() now would produce the given
+  // value.  Will always return false if ReadVarint32() would not return the
+  // given value.  If ExpectTag() returns true, it also advances past
+  // the varint.  For best performance, use a compile-time constant as the
+  // parameter.
+  // Always inline because this collapses to a small number of instructions
+  // when given a constant parameter, but GCC doesn't want to inline by default.
+  PROTOBUF_ALWAYS_INLINE bool ExpectTag(uint32_t expected);
+
+  // Like above, except this reads from the specified buffer. The caller is
+  // responsible for ensuring that the buffer is large enough to read a varint
+  // of the expected size. For best performance, use a compile-time constant as
+  // the expected tag parameter.
+  //
+  // Returns a pointer beyond the expected tag if it was found, or NULL if it
+  // was not.
+  PROTOBUF_ALWAYS_INLINE
+  static const uint8_t* ExpectTagFromArray(const uint8_t* buffer,
+                                           uint32_t expected);
+
+  // Usually returns true if no more bytes can be read.  Always returns false
+  // if more bytes can be read.  If ExpectAtEnd() returns true, a subsequent
+  // call to LastTagWas() will act as if ReadTag() had been called and returned
+  // zero, and ConsumedEntireMessage() will return true.
+  bool ExpectAtEnd();
+
+  // If the last call to ReadTag() or ReadTagWithCutoff() returned the given
+  // value, returns true.  Otherwise, returns false.
+  // ReadTagNoLastTag/ReadTagWithCutoffNoLastTag do not preserve the last
+  // returned value.
+  //
+  // This is needed because parsers for some types of embedded messages
+  // (with field type TYPE_GROUP) don't actually know that they've reached the
+  // end of a message until they see an ENDGROUP tag, which was actually part
+  // of the enclosing message.  The enclosing message would like to check that
+  // tag to make sure it had the right number, so it calls LastTagWas() on
+  // return from the embedded parser to check.
+  bool LastTagWas(uint32_t expected);
+  void SetLastTag(uint32_t tag) { last_tag_ = tag; }
+
+  // When parsing message (but NOT a group), this method must be called
+  // immediately after MergeFromCodedStream() returns (if it returns true)
+  // to further verify that the message ended in a legitimate way.  For
+  // example, this verifies that parsing did not end on an end-group tag.
+  // It also checks for some cases where, due to optimizations,
+  // MergeFromCodedStream() can incorrectly return true.
+  bool ConsumedEntireMessage();
+  void SetConsumed() { legitimate_message_end_ = true; }
+
+  // Limits ----------------------------------------------------------
+  // Limits are used when parsing length-delimited embedded messages.
+  // After the message's length is read, PushLimit() is used to prevent
+  // the CodedInputStream from reading beyond that length.  Once the
+  // embedded message has been parsed, PopLimit() is called to undo the
+  // limit.
+
+  // Opaque type used with PushLimit() and PopLimit().  Do not modify
+  // values of this type yourself.  The only reason that this isn't a
+  // struct with private internals is for efficiency.
+  typedef int Limit;
+
+  // Places a limit on the number of bytes that the stream may read,
+  // starting from the current position.  Once the stream hits this limit,
+  // it will act like the end of the input has been reached until PopLimit()
+  // is called.
+  //
+  // As the names imply, the stream conceptually has a stack of limits.  The
+  // shortest limit on the stack is always enforced, even if it is not the
+  // top limit.
+  //
+  // The value returned by PushLimit() is opaque to the caller, and must
+  // be passed unchanged to the corresponding call to PopLimit().
+  Limit PushLimit(int byte_limit);
+
+  // Pops the last limit pushed by PushLimit().  The input must be the value
+  // returned by that call to PushLimit().
+  void PopLimit(Limit limit);
+
+  // Returns the number of bytes left until the nearest limit on the
+  // stack is hit, or -1 if no limits are in place.
+  int BytesUntilLimit() const;
+
+  // Returns current position relative to the beginning of the input stream.
+  int CurrentPosition() const;
+
+  // Total Bytes Limit -----------------------------------------------
+  // To prevent malicious users from sending excessively large messages
+  // and causing memory exhaustion, CodedInputStream imposes a hard limit on
+  // the total number of bytes it will read.
+
+  // Sets the maximum number of bytes that this CodedInputStream will read
+  // before refusing to continue.  To prevent servers from allocating enormous
+  // amounts of memory to hold parsed messages, the maximum message length
+  // should be limited to the shortest length that will not harm usability.
+  // The default limit is INT_MAX (~2GB) and apps should set shorter limits
+  // if possible. An error will always be printed to stderr if the limit is
+  // reached.
+  //
+  // Note: setting a limit less than the current read position is interpreted
+  // as a limit on the current position.
+  //
+  // This is unrelated to PushLimit()/PopLimit().
+  void SetTotalBytesLimit(int total_bytes_limit);
+
+  // The Total Bytes Limit minus the Current Position, or -1 if the total bytes
+  // limit is INT_MAX.
+  int BytesUntilTotalBytesLimit() const;
+
+  // Recursion Limit -------------------------------------------------
+  // To prevent corrupt or malicious messages from causing stack overflows,
+  // we must keep track of the depth of recursion when parsing embedded
+  // messages and groups.  CodedInputStream keeps track of this because it
+  // is the only object that is passed down the stack during parsing.
+
+  // Sets the maximum recursion depth.  The default is 100.
+  void SetRecursionLimit(int limit);
+  int RecursionBudget() { return recursion_budget_; }
+
+  static int GetDefaultRecursionLimit() { return default_recursion_limit_; }
+
+  // Increments the current recursion depth.  Returns true if the depth is
+  // under the limit, false if it has gone over.
+  bool IncrementRecursionDepth();
+
+  // Decrements the recursion depth if possible.
+  void DecrementRecursionDepth();
+
+  // Decrements the recursion depth blindly.  This is faster than
+  // DecrementRecursionDepth().  It should be used only if all previous
+  // increments to recursion depth were successful.
+  void UnsafeDecrementRecursionDepth();
+
+  // Shorthand for make_pair(PushLimit(byte_limit), --recursion_budget_).
+  // Using this can reduce code size and complexity in some cases.  The caller
+  // is expected to check that the second part of the result is non-negative (to
+  // bail out if the depth of recursion is too high) and, if all is well, to
+  // later pass the first part of the result to PopLimit() or similar.
+  std::pair<CodedInputStream::Limit, int> IncrementRecursionDepthAndPushLimit(
+      int byte_limit);
+
+  // Shorthand for PushLimit(ReadVarint32(&length) ? length : 0).
+  Limit ReadLengthAndPushLimit();
+
+  // Helper that is equivalent to: {
+  //  bool result = ConsumedEntireMessage();
+  //  PopLimit(limit);
+  //  UnsafeDecrementRecursionDepth();
+  //  return result; }
+  // Using this can reduce code size and complexity in some cases.
+  // Do not use unless the current recursion depth is greater than zero.
+  bool DecrementRecursionDepthAndPopLimit(Limit limit);
+
+  // Helper that is equivalent to: {
+  //  bool result = ConsumedEntireMessage();
+  //  PopLimit(limit);
+  //  return result; }
+  // Using this can reduce code size and complexity in some cases.
+  bool CheckEntireMessageConsumedAndPopLimit(Limit limit);
+
+  // Extension Registry ----------------------------------------------
+  // ADVANCED USAGE:  99.9% of people can ignore this section.
+  //
+  // By default, when parsing extensions, the parser looks for extension
+  // definitions in the pool which owns the outer message's Descriptor.
+  // However, you may call SetExtensionRegistry() to provide an alternative
+  // pool instead.  This makes it possible, for example, to parse a message
+  // using a generated class, but represent some extensions using
+  // DynamicMessage.
+
+  // Set the pool used to look up extensions.  Most users do not need to call
+  // this as the correct pool will be chosen automatically.
+  //
+  // WARNING:  It is very easy to misuse this.  Carefully read the requirements
+  //   below.  Do not use this unless you are sure you need it.  Almost no one
+  //   does.
+  //
+  // Let's say you are parsing a message into message object m, and you want
+  // to take advantage of SetExtensionRegistry().  You must follow these
+  // requirements:
+  //
+  // The given DescriptorPool must contain m->GetDescriptor().  It is not
+  // sufficient for it to simply contain a descriptor that has the same name
+  // and content -- it must be the *exact object*.  In other words:
+  //   assert(pool->FindMessageTypeByName(m->GetDescriptor()->full_name()) ==
+  //          m->GetDescriptor());
+  // There are two ways to satisfy this requirement:
+  // 1) Use m->GetDescriptor()->pool() as the pool.  This is generally useless
+  //    because this is the pool that would be used anyway if you didn't call
+  //    SetExtensionRegistry() at all.
+  // 2) Use a DescriptorPool which has m->GetDescriptor()->pool() as an
+  //    "underlay".  Read the documentation for DescriptorPool for more
+  //    information about underlays.
+  //
+  // You must also provide a MessageFactory.  This factory will be used to
+  // construct Message objects representing extensions.  The factory's
+  // GetPrototype() MUST return non-NULL for any Descriptor which can be found
+  // through the provided pool.
+  //
+  // If the provided factory might return instances of protocol-compiler-
+  // generated (i.e. compiled-in) types, or if the outer message object m is
+  // a generated type, then the given factory MUST have this property:  If
+  // GetPrototype() is given a Descriptor which resides in
+  // DescriptorPool::generated_pool(), the factory MUST return the same
+  // prototype which MessageFactory::generated_factory() would return.  That
+  // is, given a descriptor for a generated type, the factory must return an
+  // instance of the generated class (NOT DynamicMessage).  However, when
+  // given a descriptor for a type that is NOT in generated_pool, the factory
+  // is free to return any implementation.
+  //
+  // The reason for this requirement is that generated sub-objects may be
+  // accessed via the standard (non-reflection) extension accessor methods,
+  // and these methods will down-cast the object to the generated class type.
+  // If the object is not actually of that type, the results would be undefined.
+  // On the other hand, if an extension is not compiled in, then there is no
+  // way the code could end up accessing it via the standard accessors -- the
+  // only way to access the extension is via reflection.  When using reflection,
+  // DynamicMessage and generated messages are indistinguishable, so it's fine
+  // if these objects are represented using DynamicMessage.
+  //
+  // Using DynamicMessageFactory on which you have called
+  // SetDelegateToGeneratedFactory(true) should be sufficient to satisfy the
+  // above requirement.
+  //
+  // If either pool or factory is NULL, both must be NULL.
+  //
+  // Note that this feature is ignored when parsing "lite" messages as they do
+  // not have descriptors.
+  void SetExtensionRegistry(const DescriptorPool* pool,
+                            MessageFactory* factory);
+
+  // Get the DescriptorPool set via SetExtensionRegistry(), or NULL if no pool
+  // has been provided.
+  const DescriptorPool* GetExtensionPool();
+
+  // Get the MessageFactory set via SetExtensionRegistry(), or NULL if no
+  // factory has been provided.
+  MessageFactory* GetExtensionFactory();
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodedInputStream);
+
+  const uint8_t* buffer_;
+  const uint8_t* buffer_end_;  // pointer to the end of the buffer.
+  ZeroCopyInputStream* input_;
+  int total_bytes_read_;  // total bytes read from input_, including
+                          // the current buffer
+
+  // If total_bytes_read_ surpasses INT_MAX, we record the extra bytes here
+  // so that we can BackUp() on destruction.
+  int overflow_bytes_;
+
+  // LastTagWas() stuff.
+  uint32_t last_tag_;  // result of last ReadTag() or ReadTagWithCutoff().
+
+  // This is set true by ReadTag{Fallback/Slow}() if it is called when exactly
+  // at EOF, or by ExpectAtEnd() when it returns true.  This happens when we
+  // reach the end of a message and attempt to read another tag.
+  bool legitimate_message_end_;
+
+  // See EnableAliasing().
+  bool aliasing_enabled_;
+
+  // Limits
+  Limit current_limit_;  // if position = -1, no limit is applied
+
+  // For simplicity, if the current buffer crosses a limit (either a normal
+  // limit created by PushLimit() or the total bytes limit), buffer_size_
+  // only tracks the number of bytes before that limit.  This field
+  // contains the number of bytes after it.  Note that this implies that if
+  // buffer_size_ == 0 and buffer_size_after_limit_ > 0, we know we've
+  // hit a limit.  However, if both are zero, it doesn't necessarily mean
+  // we aren't at a limit -- the buffer may have ended exactly at the limit.
+  int buffer_size_after_limit_;
+
+  // Maximum number of bytes to read, period.  This is unrelated to
+  // current_limit_.  Set using SetTotalBytesLimit().
+  int total_bytes_limit_;
+
+  // Current recursion budget, controlled by IncrementRecursionDepth() and
+  // similar.  Starts at recursion_limit_ and goes down: if this reaches
+  // -1 we are over budget.
+  int recursion_budget_;
+  // Recursion depth limit, set by SetRecursionLimit().
+  int recursion_limit_;
+
+  // See SetExtensionRegistry().
+  const DescriptorPool* extension_pool_;
+  MessageFactory* extension_factory_;
+
+  // Private member functions.
+
+  // Fallback when Skip() goes past the end of the current buffer.
+  bool SkipFallback(int count, int original_buffer_size);
+
+  // Advance the buffer by a given number of bytes.
+  void Advance(int amount);
+
+  // Back up input_ to the current buffer position.
+  void BackUpInputToCurrentPosition();
+
+  // Recomputes the value of buffer_size_after_limit_.  Must be called after
+  // current_limit_ or total_bytes_limit_ changes.
+  void RecomputeBufferLimits();
+
+  // Writes an error message saying that we hit total_bytes_limit_.
+  void PrintTotalBytesLimitError();
+
+  // Called when the buffer runs out to request more data.  Implies an
+  // Advance(BufferSize()).
+  bool Refresh();
+
+  // When parsing varints, we optimize for the common case of small values, and
+  // then optimize for the case when the varint fits within the current buffer
+  // piece. The Fallback method is used when we can't use the one-byte
+  // optimization. The Slow method is yet another fallback when the buffer is
+  // not large enough. Making the slow path out-of-line speeds up the common
+  // case by 10-15%. The slow path is fairly uncommon: it only triggers when a
+  // message crosses multiple buffers.  Note: ReadVarint32Fallback() and
+  // ReadVarint64Fallback() are called frequently and generally not inlined, so
+  // they have been optimized to avoid "out" parameters.  The former returns -1
+  // if it fails and the uint32_t it read otherwise.  The latter has a bool
+  // indicating success or failure as part of its return type.
+  int64_t ReadVarint32Fallback(uint32_t first_byte_or_zero);
+  int ReadVarintSizeAsIntFallback();
+  std::pair<uint64_t, bool> ReadVarint64Fallback();
+  bool ReadVarint32Slow(uint32_t* value);
+  bool ReadVarint64Slow(uint64_t* value);
+  int ReadVarintSizeAsIntSlow();
+  bool ReadLittleEndian32Fallback(uint32_t* value);
+  bool ReadLittleEndian64Fallback(uint64_t* value);
+
+  // Fallback/slow methods for reading tags. These do not update last_tag_,
+  // but will set legitimate_message_end_ if we are at the end of the input
+  // stream.
+  uint32_t ReadTagFallback(uint32_t first_byte_or_zero);
+  uint32_t ReadTagSlow();
+  bool ReadStringFallback(std::string* buffer, int size);
+
+  // Return the size of the buffer.
+  int BufferSize() const;
+
+  static const int kDefaultTotalBytesLimit = INT_MAX;
+
+  static int default_recursion_limit_;  // 100 by default.
+
+  friend class google::protobuf::ZeroCopyCodedInputStream;
+  friend class google::protobuf::internal::EpsCopyByteStream;
+};
+
+// EpsCopyOutputStream wraps a ZeroCopyOutputStream and exposes a new stream,
+// which has the property you can write kSlopBytes (16 bytes) from the current
+// position without bounds checks. The cursor into the stream is managed by
+// the user of the class and is an explicit parameter in the methods. Careful
+// use of this class, ie. keep ptr a local variable, eliminates the need to
+// for the compiler to sync the ptr value between register and memory.
+class PROTOBUF_EXPORT EpsCopyOutputStream {
+ public:
+  enum { kSlopBytes = 16 };
+
+  // Initialize from a stream.
+  EpsCopyOutputStream(ZeroCopyOutputStream* stream, bool deterministic,
+                      uint8_t** pp)
+      : end_(buffer_),
+        stream_(stream),
+        is_serialization_deterministic_(deterministic) {
+    *pp = buffer_;
+  }
+
+  // Only for array serialization. No overflow protection, end_ will be the
+  // pointed to the end of the array. When using this the total size is already
+  // known, so no need to maintain the slop region.
+  EpsCopyOutputStream(void* data, int size, bool deterministic)
+      : end_(static_cast<uint8_t*>(data) + size),
+        buffer_end_(nullptr),
+        stream_(nullptr),
+        is_serialization_deterministic_(deterministic) {}
+
+  // Initialize from stream but with the first buffer already given (eager).
+  EpsCopyOutputStream(void* data, int size, ZeroCopyOutputStream* stream,
+                      bool deterministic, uint8_t** pp)
+      : stream_(stream), is_serialization_deterministic_(deterministic) {
+    *pp = SetInitialBuffer(data, size);
+  }
+
+  // Flush everything that's written into the underlying ZeroCopyOutputStream
+  // and trims the underlying stream to the location of ptr.
+  uint8_t* Trim(uint8_t* ptr);
+
+  // After this it's guaranteed you can safely write kSlopBytes to ptr. This
+  // will never fail! The underlying stream can produce an error. Use HadError
+  // to check for errors.
+  PROTOBUF_NODISCARD uint8_t* EnsureSpace(uint8_t* ptr) {
+    if (PROTOBUF_PREDICT_FALSE(ptr >= end_)) {
+      return EnsureSpaceFallback(ptr);
+    }
+    return ptr;
+  }
+
+  uint8_t* WriteRaw(const void* data, size_t size, uint8_t* ptr) {
+    if (PROTOBUF_PREDICT_FALSE(end_ - ptr < static_cast<int>(size))) {
+      return WriteRawFallback(data, size, ptr);
+    }
+    std::memcpy(ptr, data, size);
+    return ptr + size;
+  }
+  // Writes the buffer specified by data, size to the stream. Possibly by
+  // aliasing the buffer (ie. not copying the data). The caller is responsible
+  // to make sure the buffer is alive for the duration of the
+  // ZeroCopyOutputStream.
+#ifndef NDEBUG
+  PROTOBUF_NOINLINE
+#endif
+  uint8_t* WriteRawMaybeAliased(const void* data, int size, uint8_t* ptr) {
+    if (aliasing_enabled_) {
+      return WriteAliasedRaw(data, size, ptr);
+    } else {
+      return WriteRaw(data, size, ptr);
+    }
+  }
+
+
+#ifndef NDEBUG
+  PROTOBUF_NOINLINE
+#endif
+  uint8_t* WriteStringMaybeAliased(uint32_t num, const std::string& s,
+                                   uint8_t* ptr) {
+    std::ptrdiff_t size = s.size();
+    if (PROTOBUF_PREDICT_FALSE(
+            size >= 128 || end_ - ptr + 16 - TagSize(num << 3) - 1 < size)) {
+      return WriteStringMaybeAliasedOutline(num, s, ptr);
+    }
+    ptr = UnsafeVarint((num << 3) | 2, ptr);
+    *ptr++ = static_cast<uint8_t>(size);
+    std::memcpy(ptr, s.data(), size);
+    return ptr + size;
+  }
+  uint8_t* WriteBytesMaybeAliased(uint32_t num, const std::string& s,
+                                  uint8_t* ptr) {
+    return WriteStringMaybeAliased(num, s, ptr);
+  }
+
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteString(uint32_t num, const T& s,
+                                              uint8_t* ptr) {
+    std::ptrdiff_t size = s.size();
+    if (PROTOBUF_PREDICT_FALSE(
+            size >= 128 || end_ - ptr + 16 - TagSize(num << 3) - 1 < size)) {
+      return WriteStringOutline(num, s, ptr);
+    }
+    ptr = UnsafeVarint((num << 3) | 2, ptr);
+    *ptr++ = static_cast<uint8_t>(size);
+    std::memcpy(ptr, s.data(), size);
+    return ptr + size;
+  }
+  template <typename T>
+#ifndef NDEBUG
+  PROTOBUF_NOINLINE
+#endif
+  uint8_t* WriteBytes(uint32_t num, const T& s, uint8_t* ptr) {
+    return WriteString(num, s, ptr);
+  }
+
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteInt32Packed(int num, const T& r,
+                                                   int size, uint8_t* ptr) {
+    return WriteVarintPacked(num, r, size, ptr, Encode64);
+  }
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteUInt32Packed(int num, const T& r,
+                                                    int size, uint8_t* ptr) {
+    return WriteVarintPacked(num, r, size, ptr, Encode32);
+  }
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteSInt32Packed(int num, const T& r,
+                                                    int size, uint8_t* ptr) {
+    return WriteVarintPacked(num, r, size, ptr, ZigZagEncode32);
+  }
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteInt64Packed(int num, const T& r,
+                                                   int size, uint8_t* ptr) {
+    return WriteVarintPacked(num, r, size, ptr, Encode64);
+  }
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteUInt64Packed(int num, const T& r,
+                                                    int size, uint8_t* ptr) {
+    return WriteVarintPacked(num, r, size, ptr, Encode64);
+  }
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteSInt64Packed(int num, const T& r,
+                                                    int size, uint8_t* ptr) {
+    return WriteVarintPacked(num, r, size, ptr, ZigZagEncode64);
+  }
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteEnumPacked(int num, const T& r, int size,
+                                                  uint8_t* ptr) {
+    return WriteVarintPacked(num, r, size, ptr, Encode64);
+  }
+
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteFixedPacked(int num, const T& r,
+                                                   uint8_t* ptr) {
+    ptr = EnsureSpace(ptr);
+    constexpr auto element_size = sizeof(typename T::value_type);
+    auto size = r.size() * element_size;
+    ptr = WriteLengthDelim(num, size, ptr);
+    return WriteRawLittleEndian<element_size>(r.data(), static_cast<int>(size),
+                                              ptr);
+  }
+
+  // Returns true if there was an underlying I/O error since this object was
+  // created.
+  bool HadError() const { return had_error_; }
+
+  // Instructs the EpsCopyOutputStream to allow the underlying
+  // ZeroCopyOutputStream to hold pointers to the original structure instead of
+  // copying, if it supports it (i.e. output->AllowsAliasing() is true).  If the
+  // underlying stream does not support aliasing, then enabling it has no
+  // affect.  For now, this only affects the behavior of
+  // WriteRawMaybeAliased().
+  //
+  // NOTE: It is caller's responsibility to ensure that the chunk of memory
+  // remains live until all of the data has been consumed from the stream.
+  void EnableAliasing(bool enabled);
+
+  // See documentation on CodedOutputStream::SetSerializationDeterministic.
+  void SetSerializationDeterministic(bool value) {
+    is_serialization_deterministic_ = value;
+  }
+
+  // See documentation on CodedOutputStream::IsSerializationDeterministic.
+  bool IsSerializationDeterministic() const {
+    return is_serialization_deterministic_;
+  }
+
+  // The number of bytes written to the stream at position ptr, relative to the
+  // stream's overall position.
+  int64_t ByteCount(uint8_t* ptr) const;
+
+
+ private:
+  uint8_t* end_;
+  uint8_t* buffer_end_ = buffer_;
+  uint8_t buffer_[2 * kSlopBytes];
+  ZeroCopyOutputStream* stream_;
+  bool had_error_ = false;
+  bool aliasing_enabled_ = false;  // See EnableAliasing().
+  bool is_serialization_deterministic_;
+  bool skip_check_consistency = false;
+
+  uint8_t* EnsureSpaceFallback(uint8_t* ptr);
+  inline uint8_t* Next();
+  int Flush(uint8_t* ptr);
+  std::ptrdiff_t GetSize(uint8_t* ptr) const {
+    GOOGLE_DCHECK(ptr <= end_ + kSlopBytes);  // NOLINT
+    return end_ + kSlopBytes - ptr;
+  }
+
+  uint8_t* Error() {
+    had_error_ = true;
+    // We use the patch buffer to always guarantee space to write to.
+    end_ = buffer_ + kSlopBytes;
+    return buffer_;
+  }
+
+  static constexpr int TagSize(uint32_t tag) {
+    return (tag < (1 << 7))    ? 1
+           : (tag < (1 << 14)) ? 2
+           : (tag < (1 << 21)) ? 3
+           : (tag < (1 << 28)) ? 4
+                               : 5;
+  }
+
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteTag(uint32_t num, uint32_t wt,
+                                           uint8_t* ptr) {
+    GOOGLE_DCHECK(ptr < end_);  // NOLINT
+    return UnsafeVarint((num << 3) | wt, ptr);
+  }
+
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteLengthDelim(int num, uint32_t size,
+                                                   uint8_t* ptr) {
+    ptr = WriteTag(num, 2, ptr);
+    return UnsafeWriteSize(size, ptr);
+  }
+
+  uint8_t* WriteRawFallback(const void* data, int size, uint8_t* ptr);
+
+  uint8_t* WriteAliasedRaw(const void* data, int size, uint8_t* ptr);
+
+  uint8_t* WriteStringMaybeAliasedOutline(uint32_t num, const std::string& s,
+                                          uint8_t* ptr);
+  uint8_t* WriteStringOutline(uint32_t num, const std::string& s, uint8_t* ptr);
+
+  template <typename T, typename E>
+  PROTOBUF_ALWAYS_INLINE uint8_t* WriteVarintPacked(int num, const T& r,
+                                                    int size, uint8_t* ptr,
+                                                    const E& encode) {
+    ptr = EnsureSpace(ptr);
+    ptr = WriteLengthDelim(num, size, ptr);
+    auto it = r.data();
+    auto end = it + r.size();
+    do {
+      ptr = EnsureSpace(ptr);
+      ptr = UnsafeVarint(encode(*it++), ptr);
+    } while (it < end);
+    return ptr;
+  }
+
+  static uint32_t Encode32(uint32_t v) { return v; }
+  static uint64_t Encode64(uint64_t v) { return v; }
+  static uint32_t ZigZagEncode32(int32_t v) {
+    return (static_cast<uint32_t>(v) << 1) ^ static_cast<uint32_t>(v >> 31);
+  }
+  static uint64_t ZigZagEncode64(int64_t v) {
+    return (static_cast<uint64_t>(v) << 1) ^ static_cast<uint64_t>(v >> 63);
+  }
+
+  template <typename T>
+  PROTOBUF_ALWAYS_INLINE static uint8_t* UnsafeVarint(T value, uint8_t* ptr) {
+    static_assert(std::is_unsigned<T>::value,
+                  "Varint serialization must be unsigned");
+    ptr[0] = static_cast<uint8_t>(value);
+    if (value < 0x80) {
+      return ptr + 1;
+    }
+    // Turn on continuation bit in the byte we just wrote.
+    ptr[0] |= static_cast<uint8_t>(0x80);
+    value >>= 7;
+    ptr[1] = static_cast<uint8_t>(value);
+    if (value < 0x80) {
+      return ptr + 2;
+    }
+    ptr += 2;
+    do {
+      // Turn on continuation bit in the byte we just wrote.
+      ptr[-1] |= static_cast<uint8_t>(0x80);
+      value >>= 7;
+      *ptr = static_cast<uint8_t>(value);
+      ++ptr;
+    } while (value >= 0x80);
+    return ptr;
+  }
+
+  PROTOBUF_ALWAYS_INLINE static uint8_t* UnsafeWriteSize(uint32_t value,
+                                                         uint8_t* ptr) {
+    while (PROTOBUF_PREDICT_FALSE(value >= 0x80)) {
+      *ptr = static_cast<uint8_t>(value | 0x80);
+      value >>= 7;
+      ++ptr;
+    }
+    *ptr++ = static_cast<uint8_t>(value);
+    return ptr;
+  }
+
+  template <int S>
+  uint8_t* WriteRawLittleEndian(const void* data, int size, uint8_t* ptr);
+#if !defined(PROTOBUF_LITTLE_ENDIAN) || \
+    defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+  uint8_t* WriteRawLittleEndian32(const void* data, int size, uint8_t* ptr);
+  uint8_t* WriteRawLittleEndian64(const void* data, int size, uint8_t* ptr);
+#endif
+
+  // These methods are for CodedOutputStream. Ideally they should be private
+  // but to match current behavior of CodedOutputStream as close as possible
+  // we allow it some functionality.
+ public:
+  uint8_t* SetInitialBuffer(void* data, int size) {
+    auto ptr = static_cast<uint8_t*>(data);
+    if (size > kSlopBytes) {
+      end_ = ptr + size - kSlopBytes;
+      buffer_end_ = nullptr;
+      return ptr;
+    } else {
+      end_ = buffer_ + size;
+      buffer_end_ = ptr;
+      return buffer_;
+    }
+  }
+
+ private:
+  // Needed by CodedOutputStream HadError. HadError needs to flush the patch
+  // buffers to ensure there is no error as of yet.
+  uint8_t* FlushAndResetBuffer(uint8_t*);
+
+  // The following functions mimic the old CodedOutputStream behavior as close
+  // as possible. They flush the current state to the stream, behave as
+  // the old CodedOutputStream and then return to normal operation.
+  bool Skip(int count, uint8_t** pp);
+  bool GetDirectBufferPointer(void** data, int* size, uint8_t** pp);
+  uint8_t* GetDirectBufferForNBytesAndAdvance(int size, uint8_t** pp);
+
+  friend class CodedOutputStream;
+};
+
+template <>
+inline uint8_t* EpsCopyOutputStream::WriteRawLittleEndian<1>(const void* data,
+                                                             int size,
+                                                             uint8_t* ptr) {
+  return WriteRaw(data, size, ptr);
+}
+template <>
+inline uint8_t* EpsCopyOutputStream::WriteRawLittleEndian<4>(const void* data,
+                                                             int size,
+                                                             uint8_t* ptr) {
+#if defined(PROTOBUF_LITTLE_ENDIAN) && \
+    !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+  return WriteRaw(data, size, ptr);
+#else
+  return WriteRawLittleEndian32(data, size, ptr);
+#endif
+}
+template <>
+inline uint8_t* EpsCopyOutputStream::WriteRawLittleEndian<8>(const void* data,
+                                                             int size,
+                                                             uint8_t* ptr) {
+#if defined(PROTOBUF_LITTLE_ENDIAN) && \
+    !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+  return WriteRaw(data, size, ptr);
+#else
+  return WriteRawLittleEndian64(data, size, ptr);
+#endif
+}
+
+// Class which encodes and writes binary data which is composed of varint-
+// encoded integers and fixed-width pieces.  Wraps a ZeroCopyOutputStream.
+// Most users will not need to deal with CodedOutputStream.
+//
+// Most methods of CodedOutputStream which return a bool return false if an
+// underlying I/O error occurs.  Once such a failure occurs, the
+// CodedOutputStream is broken and is no longer useful. The Write* methods do
+// not return the stream status, but will invalidate the stream if an error
+// occurs. The client can probe HadError() to determine the status.
+//
+// Note that every method of CodedOutputStream which writes some data has
+// a corresponding static "ToArray" version. These versions write directly
+// to the provided buffer, returning a pointer past the last written byte.
+// They require that the buffer has sufficient capacity for the encoded data.
+// This allows an optimization where we check if an output stream has enough
+// space for an entire message before we start writing and, if there is, we
+// call only the ToArray methods to avoid doing bound checks for each
+// individual value.
+// i.e., in the example above:
+//
+//   CodedOutputStream* coded_output = new CodedOutputStream(raw_output);
+//   int magic_number = 1234;
+//   char text[] = "Hello world!";
+//
+//   int coded_size = sizeof(magic_number) +
+//                    CodedOutputStream::VarintSize32(strlen(text)) +
+//                    strlen(text);
+//
+//   uint8_t* buffer =
+//       coded_output->GetDirectBufferForNBytesAndAdvance(coded_size);
+//   if (buffer != nullptr) {
+//     // The output stream has enough space in the buffer: write directly to
+//     // the array.
+//     buffer = CodedOutputStream::WriteLittleEndian32ToArray(magic_number,
+//                                                            buffer);
+//     buffer = CodedOutputStream::WriteVarint32ToArray(strlen(text), buffer);
+//     buffer = CodedOutputStream::WriteRawToArray(text, strlen(text), buffer);
+//   } else {
+//     // Make bound-checked writes, which will ask the underlying stream for
+//     // more space as needed.
+//     coded_output->WriteLittleEndian32(magic_number);
+//     coded_output->WriteVarint32(strlen(text));
+//     coded_output->WriteRaw(text, strlen(text));
+//   }
+//
+//   delete coded_output;
+class PROTOBUF_EXPORT CodedOutputStream {
+ public:
+  // Creates a CodedOutputStream that writes to the given `stream`.
+  // The provided stream must publicly derive from `ZeroCopyOutputStream`.
+  template <class Stream, class = typename std::enable_if<std::is_base_of<
+                              ZeroCopyOutputStream, Stream>::value>::type>
+  explicit CodedOutputStream(Stream* stream);
+
+  // Creates a CodedOutputStream that writes to the given `stream`, and does
+  // an 'eager initialization' of the internal state if `eager_init` is true.
+  // The provided stream must publicly derive from `ZeroCopyOutputStream`.
+  template <class Stream, class = typename std::enable_if<std::is_base_of<
+                              ZeroCopyOutputStream, Stream>::value>::type>
+  CodedOutputStream(Stream* stream, bool eager_init);
+
+  // Destroy the CodedOutputStream and position the underlying
+  // ZeroCopyOutputStream immediately after the last byte written.
+  ~CodedOutputStream();
+
+  // Returns true if there was an underlying I/O error since this object was
+  // created. On should call Trim before this function in order to catch all
+  // errors.
+  bool HadError() {
+    cur_ = impl_.FlushAndResetBuffer(cur_);
+    GOOGLE_DCHECK(cur_);
+    return impl_.HadError();
+  }
+
+  // Trims any unused space in the underlying buffer so that its size matches
+  // the number of bytes written by this stream. The underlying buffer will
+  // automatically be trimmed when this stream is destroyed; this call is only
+  // necessary if the underlying buffer is accessed *before* the stream is
+  // destroyed.
+  void Trim() { cur_ = impl_.Trim(cur_); }
+
+  // Skips a number of bytes, leaving the bytes unmodified in the underlying
+  // buffer.  Returns false if an underlying write error occurs.  This is
+  // mainly useful with GetDirectBufferPointer().
+  // Note of caution, the skipped bytes may contain uninitialized data. The
+  // caller must make sure that the skipped bytes are properly initialized,
+  // otherwise you might leak bytes from your heap.
+  bool Skip(int count) { return impl_.Skip(count, &cur_); }
+
+  // Sets *data to point directly at the unwritten part of the
+  // CodedOutputStream's underlying buffer, and *size to the size of that
+  // buffer, but does not advance the stream's current position.  This will
+  // always either produce a non-empty buffer or return false.  If the caller
+  // writes any data to this buffer, it should then call Skip() to skip over
+  // the consumed bytes.  This may be useful for implementing external fast
+  // serialization routines for types of data not covered by the
+  // CodedOutputStream interface.
+  bool GetDirectBufferPointer(void** data, int* size) {
+    return impl_.GetDirectBufferPointer(data, size, &cur_);
+  }
+
+  // If there are at least "size" bytes available in the current buffer,
+  // returns a pointer directly into the buffer and advances over these bytes.
+  // The caller may then write directly into this buffer (e.g. using the
+  // *ToArray static methods) rather than go through CodedOutputStream.  If
+  // there are not enough bytes available, returns NULL.  The return pointer is
+  // invalidated as soon as any other non-const method of CodedOutputStream
+  // is called.
+  inline uint8_t* GetDirectBufferForNBytesAndAdvance(int size) {
+    return impl_.GetDirectBufferForNBytesAndAdvance(size, &cur_);
+  }
+
+  // Write raw bytes, copying them from the given buffer.
+  void WriteRaw(const void* buffer, int size) {
+    cur_ = impl_.WriteRaw(buffer, size, cur_);
+  }
+  // Like WriteRaw()  but will try to write aliased data if aliasing is
+  // turned on.
+  void WriteRawMaybeAliased(const void* data, int size);
+  // Like WriteRaw()  but writing directly to the target array.
+  // This is _not_ inlined, as the compiler often optimizes memcpy into inline
+  // copy loops. Since this gets called by every field with string or bytes
+  // type, inlining may lead to a significant amount of code bloat, with only a
+  // minor performance gain.
+  static uint8_t* WriteRawToArray(const void* buffer, int size,
+                                  uint8_t* target);
+
+  // Equivalent to WriteRaw(str.data(), str.size()).
+  void WriteString(const std::string& str);
+  // Like WriteString()  but writing directly to the target array.
+  static uint8_t* WriteStringToArray(const std::string& str, uint8_t* target);
+  // Write the varint-encoded size of str followed by str.
+  static uint8_t* WriteStringWithSizeToArray(const std::string& str,
+                                             uint8_t* target);
+
+
+  // Write a 32-bit little-endian integer.
+  void WriteLittleEndian32(uint32_t value) {
+    cur_ = impl_.EnsureSpace(cur_);
+    SetCur(WriteLittleEndian32ToArray(value, Cur()));
+  }
+  // Like WriteLittleEndian32()  but writing directly to the target array.
+  static uint8_t* WriteLittleEndian32ToArray(uint32_t value, uint8_t* target);
+  // Write a 64-bit little-endian integer.
+  void WriteLittleEndian64(uint64_t value) {
+    cur_ = impl_.EnsureSpace(cur_);
+    SetCur(WriteLittleEndian64ToArray(value, Cur()));
+  }
+  // Like WriteLittleEndian64()  but writing directly to the target array.
+  static uint8_t* WriteLittleEndian64ToArray(uint64_t value, uint8_t* target);
+
+  // Write an unsigned integer with Varint encoding.  Writing a 32-bit value
+  // is equivalent to casting it to uint64_t and writing it as a 64-bit value,
+  // but may be more efficient.
+  void WriteVarint32(uint32_t value);
+  // Like WriteVarint32()  but writing directly to the target array.
+  static uint8_t* WriteVarint32ToArray(uint32_t value, uint8_t* target);
+  // Like WriteVarint32()  but writing directly to the target array, and with
+  // the less common-case paths being out of line rather than inlined.
+  static uint8_t* WriteVarint32ToArrayOutOfLine(uint32_t value,
+                                                uint8_t* target);
+  // Write an unsigned integer with Varint encoding.
+  void WriteVarint64(uint64_t value);
+  // Like WriteVarint64()  but writing directly to the target array.
+  static uint8_t* WriteVarint64ToArray(uint64_t value, uint8_t* target);
+
+  // Equivalent to WriteVarint32() except when the value is negative,
+  // in which case it must be sign-extended to a full 10 bytes.
+  void WriteVarint32SignExtended(int32_t value);
+  // Like WriteVarint32SignExtended()  but writing directly to the target array.
+  static uint8_t* WriteVarint32SignExtendedToArray(int32_t value,
+                                                   uint8_t* target);
+
+  // This is identical to WriteVarint32(), but optimized for writing tags.
+  // In particular, if the input is a compile-time constant, this method
+  // compiles down to a couple instructions.
+  // Always inline because otherwise the aforementioned optimization can't work,
+  // but GCC by default doesn't want to inline this.
+  void WriteTag(uint32_t value);
+  // Like WriteTag()  but writing directly to the target array.
+  PROTOBUF_ALWAYS_INLINE
+  static uint8_t* WriteTagToArray(uint32_t value, uint8_t* target);
+
+  // Returns the number of bytes needed to encode the given value as a varint.
+  static size_t VarintSize32(uint32_t value);
+  // Returns the number of bytes needed to encode the given value as a varint.
+  static size_t VarintSize64(uint64_t value);
+
+  // If negative, 10 bytes.  Otherwise, same as VarintSize32().
+  static size_t VarintSize32SignExtended(int32_t value);
+
+  // Same as above, plus one.  The additional one comes at no compute cost.
+  static size_t VarintSize32PlusOne(uint32_t value);
+  static size_t VarintSize64PlusOne(uint64_t value);
+  static size_t VarintSize32SignExtendedPlusOne(int32_t value);
+
+  // Compile-time equivalent of VarintSize32().
+  template <uint32_t Value>
+  struct StaticVarintSize32 {
+    static const size_t value = (Value < (1 << 7))    ? 1
+                                : (Value < (1 << 14)) ? 2
+                                : (Value < (1 << 21)) ? 3
+                                : (Value < (1 << 28)) ? 4
+                                                      : 5;
+  };
+
+  // Returns the total number of bytes written since this object was created.
+  int ByteCount() const {
+    return static_cast<int>(impl_.ByteCount(cur_) - start_count_);
+  }
+
+  // Instructs the CodedOutputStream to allow the underlying
+  // ZeroCopyOutputStream to hold pointers to the original structure instead of
+  // copying, if it supports it (i.e. output->AllowsAliasing() is true).  If the
+  // underlying stream does not support aliasing, then enabling it has no
+  // affect.  For now, this only affects the behavior of
+  // WriteRawMaybeAliased().
+  //
+  // NOTE: It is caller's responsibility to ensure that the chunk of memory
+  // remains live until all of the data has been consumed from the stream.
+  void EnableAliasing(bool enabled) { impl_.EnableAliasing(enabled); }
+
+  // Indicate to the serializer whether the user wants deterministic
+  // serialization. The default when this is not called comes from the global
+  // default, controlled by SetDefaultSerializationDeterministic.
+  //
+  // What deterministic serialization means is entirely up to the driver of the
+  // serialization process (i.e. the caller of methods like WriteVarint32). In
+  // the case of serializing a proto buffer message using one of the methods of
+  // MessageLite, this means that for a given binary equal messages will always
+  // be serialized to the same bytes. This implies:
+  //
+  //   * Repeated serialization of a message will return the same bytes.
+  //
+  //   * Different processes running the same binary (including on different
+  //     machines) will serialize equal messages to the same bytes.
+  //
+  // Note that this is *not* canonical across languages. It is also unstable
+  // across different builds with intervening message definition changes, due to
+  // unknown fields. Users who need canonical serialization (e.g. persistent
+  // storage in a canonical form, fingerprinting) should define their own
+  // canonicalization specification and implement the serializer using
+  // reflection APIs rather than relying on this API.
+  void SetSerializationDeterministic(bool value) {
+    impl_.SetSerializationDeterministic(value);
+  }
+
+  // Return whether the user wants deterministic serialization. See above.
+  bool IsSerializationDeterministic() const {
+    return impl_.IsSerializationDeterministic();
+  }
+
+  static bool IsDefaultSerializationDeterministic() {
+    return default_serialization_deterministic_.load(
+               std::memory_order_relaxed) != 0;
+  }
+
+  template <typename Func>
+  void Serialize(const Func& func);
+
+  uint8_t* Cur() const { return cur_; }
+  void SetCur(uint8_t* ptr) { cur_ = ptr; }
+  EpsCopyOutputStream* EpsCopy() { return &impl_; }
+
+ private:
+  template <class Stream>
+  void InitEagerly(Stream* stream);
+
+  EpsCopyOutputStream impl_;
+  uint8_t* cur_;
+  int64_t start_count_;
+  static std::atomic<bool> default_serialization_deterministic_;
+
+  // See above.  Other projects may use "friend" to allow them to call this.
+  // After SetDefaultSerializationDeterministic() completes, all protocol
+  // buffer serializations will be deterministic by default.  Thread safe.
+  // However, the meaning of "after" is subtle here: to be safe, each thread
+  // that wants deterministic serialization by default needs to call
+  // SetDefaultSerializationDeterministic() or ensure on its own that another
+  // thread has done so.
+  friend void internal::MapTestForceDeterministic();
+  static void SetDefaultSerializationDeterministic() {
+    default_serialization_deterministic_.store(true, std::memory_order_relaxed);
+  }
+  // REQUIRES: value >= 0x80, and that (value & 7f) has been written to *target.
+  static uint8_t* WriteVarint32ToArrayOutOfLineHelper(uint32_t value,
+                                                      uint8_t* target);
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodedOutputStream);
+};
+
+// inline methods ====================================================
+// The vast majority of varints are only one byte.  These inline
+// methods optimize for that case.
+
+inline bool CodedInputStream::ReadVarint32(uint32_t* value) {
+  uint32_t v = 0;
+  if (PROTOBUF_PREDICT_TRUE(buffer_ < buffer_end_)) {
+    v = *buffer_;
+    if (v < 0x80) {
+      *value = v;
+      Advance(1);
+      return true;
+    }
+  }
+  int64_t result = ReadVarint32Fallback(v);
+  *value = static_cast<uint32_t>(result);
+  return result >= 0;
+}
+
+inline bool CodedInputStream::ReadVarint64(uint64_t* value) {
+  if (PROTOBUF_PREDICT_TRUE(buffer_ < buffer_end_) && *buffer_ < 0x80) {
+    *value = *buffer_;
+    Advance(1);
+    return true;
+  }
+  std::pair<uint64_t, bool> p = ReadVarint64Fallback();
+  *value = p.first;
+  return p.second;
+}
+
+inline bool CodedInputStream::ReadVarintSizeAsInt(int* value) {
+  if (PROTOBUF_PREDICT_TRUE(buffer_ < buffer_end_)) {
+    int v = *buffer_;
+    if (v < 0x80) {
+      *value = v;
+      Advance(1);
+      return true;
+    }
+  }
+  *value = ReadVarintSizeAsIntFallback();
+  return *value >= 0;
+}
+
+// static
+inline const uint8_t* CodedInputStream::ReadLittleEndian32FromArray(
+    const uint8_t* buffer, uint32_t* value) {
+#if defined(PROTOBUF_LITTLE_ENDIAN) && \
+    !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+  memcpy(value, buffer, sizeof(*value));
+  return buffer + sizeof(*value);
+#else
+  *value = (static_cast<uint32_t>(buffer[0])) |
+           (static_cast<uint32_t>(buffer[1]) << 8) |
+           (static_cast<uint32_t>(buffer[2]) << 16) |
+           (static_cast<uint32_t>(buffer[3]) << 24);
+  return buffer + sizeof(*value);
+#endif
+}
+// static
+inline const uint8_t* CodedInputStream::ReadLittleEndian64FromArray(
+    const uint8_t* buffer, uint64_t* value) {
+#if defined(PROTOBUF_LITTLE_ENDIAN) && \
+    !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+  memcpy(value, buffer, sizeof(*value));
+  return buffer + sizeof(*value);
+#else
+  uint32_t part0 = (static_cast<uint32_t>(buffer[0])) |
+                   (static_cast<uint32_t>(buffer[1]) << 8) |
+                   (static_cast<uint32_t>(buffer[2]) << 16) |
+                   (static_cast<uint32_t>(buffer[3]) << 24);
+  uint32_t part1 = (static_cast<uint32_t>(buffer[4])) |
+                   (static_cast<uint32_t>(buffer[5]) << 8) |
+                   (static_cast<uint32_t>(buffer[6]) << 16) |
+                   (static_cast<uint32_t>(buffer[7]) << 24);
+  *value = static_cast<uint64_t>(part0) | (static_cast<uint64_t>(part1) << 32);
+  return buffer + sizeof(*value);
+#endif
+}
+
+inline bool CodedInputStream::ReadLittleEndian32(uint32_t* value) {
+#if defined(PROTOBUF_LITTLE_ENDIAN) && \
+    !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+  if (PROTOBUF_PREDICT_TRUE(BufferSize() >= static_cast<int>(sizeof(*value)))) {
+    buffer_ = ReadLittleEndian32FromArray(buffer_, value);
+    return true;
+  } else {
+    return ReadLittleEndian32Fallback(value);
+  }
+#else
+  return ReadLittleEndian32Fallback(value);
+#endif
+}
+
+inline bool CodedInputStream::ReadLittleEndian64(uint64_t* value) {
+#if defined(PROTOBUF_LITTLE_ENDIAN) && \
+    !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+  if (PROTOBUF_PREDICT_TRUE(BufferSize() >= static_cast<int>(sizeof(*value)))) {
+    buffer_ = ReadLittleEndian64FromArray(buffer_, value);
+    return true;
+  } else {
+    return ReadLittleEndian64Fallback(value);
+  }
+#else
+  return ReadLittleEndian64Fallback(value);
+#endif
+}
+
+inline uint32_t CodedInputStream::ReadTagNoLastTag() {
+  uint32_t v = 0;
+  if (PROTOBUF_PREDICT_TRUE(buffer_ < buffer_end_)) {
+    v = *buffer_;
+    if (v < 0x80) {
+      Advance(1);
+      return v;
+    }
+  }
+  v = ReadTagFallback(v);
+  return v;
+}
+
+inline std::pair<uint32_t, bool> CodedInputStream::ReadTagWithCutoffNoLastTag(
+    uint32_t cutoff) {
+  // In performance-sensitive code we can expect cutoff to be a compile-time
+  // constant, and things like "cutoff >= kMax1ByteVarint" to be evaluated at
+  // compile time.
+  uint32_t first_byte_or_zero = 0;
+  if (PROTOBUF_PREDICT_TRUE(buffer_ < buffer_end_)) {
+    // Hot case: buffer_ non_empty, buffer_[0] in [1, 128).
+    // TODO(gpike): Is it worth rearranging this? E.g., if the number of fields
+    // is large enough then is it better to check for the two-byte case first?
+    first_byte_or_zero = buffer_[0];
+    if (static_cast<int8_t>(buffer_[0]) > 0) {
+      const uint32_t kMax1ByteVarint = 0x7f;
+      uint32_t tag = buffer_[0];
+      Advance(1);
+      return std::make_pair(tag, cutoff >= kMax1ByteVarint || tag <= cutoff);
+    }
+    // Other hot case: cutoff >= 0x80, buffer_ has at least two bytes available,
+    // and tag is two bytes.  The latter is tested by bitwise-and-not of the
+    // first byte and the second byte.
+    if (cutoff >= 0x80 && PROTOBUF_PREDICT_TRUE(buffer_ + 1 < buffer_end_) &&
+        PROTOBUF_PREDICT_TRUE((buffer_[0] & ~buffer_[1]) >= 0x80)) {
+      const uint32_t kMax2ByteVarint = (0x7f << 7) + 0x7f;
+      uint32_t tag = (1u << 7) * buffer_[1] + (buffer_[0] - 0x80);
+      Advance(2);
+      // It might make sense to test for tag == 0 now, but it is so rare that
+      // that we don't bother.  A varint-encoded 0 should be one byte unless
+      // the encoder lost its mind.  The second part of the return value of
+      // this function is allowed to be either true or false if the tag is 0,
+      // so we don't have to check for tag == 0.  We may need to check whether
+      // it exceeds cutoff.
+      bool at_or_below_cutoff = cutoff >= kMax2ByteVarint || tag <= cutoff;
+      return std::make_pair(tag, at_or_below_cutoff);
+    }
+  }
+  // Slow path
+  const uint32_t tag = ReadTagFallback(first_byte_or_zero);
+  return std::make_pair(tag, static_cast<uint32_t>(tag - 1) < cutoff);
+}
+
+inline bool CodedInputStream::LastTagWas(uint32_t expected) {
+  return last_tag_ == expected;
+}
+
+inline bool CodedInputStream::ConsumedEntireMessage() {
+  return legitimate_message_end_;
+}
+
+inline bool CodedInputStream::ExpectTag(uint32_t expected) {
+  if (expected < (1 << 7)) {
+    if (PROTOBUF_PREDICT_TRUE(buffer_ < buffer_end_) &&
+        buffer_[0] == expected) {
+      Advance(1);
+      return true;
+    } else {
+      return false;
+    }
+  } else if (expected < (1 << 14)) {
+    if (PROTOBUF_PREDICT_TRUE(BufferSize() >= 2) &&
+        buffer_[0] == static_cast<uint8_t>(expected | 0x80) &&
+        buffer_[1] == static_cast<uint8_t>(expected >> 7)) {
+      Advance(2);
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    // Don't bother optimizing for larger values.
+    return false;
+  }
+}
+
+inline const uint8_t* CodedInputStream::ExpectTagFromArray(
+    const uint8_t* buffer, uint32_t expected) {
+  if (expected < (1 << 7)) {
+    if (buffer[0] == expected) {
+      return buffer + 1;
+    }
+  } else if (expected < (1 << 14)) {
+    if (buffer[0] == static_cast<uint8_t>(expected | 0x80) &&
+        buffer[1] == static_cast<uint8_t>(expected >> 7)) {
+      return buffer + 2;
+    }
+  }
+  return nullptr;
+}
+
+inline void CodedInputStream::GetDirectBufferPointerInline(const void** data,
+                                                           int* size) {
+  *data = buffer_;
+  *size = static_cast<int>(buffer_end_ - buffer_);
+}
+
+inline bool CodedInputStream::ExpectAtEnd() {
+  // If we are at a limit we know no more bytes can be read.  Otherwise, it's
+  // hard to say without calling Refresh(), and we'd rather not do that.
+
+  if (buffer_ == buffer_end_ && ((buffer_size_after_limit_ != 0) ||
+                                 (total_bytes_read_ == current_limit_))) {
+    last_tag_ = 0;                   // Pretend we called ReadTag()...
+    legitimate_message_end_ = true;  // ... and it hit EOF.
+    return true;
+  } else {
+    return false;
+  }
+}
+
+inline int CodedInputStream::CurrentPosition() const {
+  return total_bytes_read_ - (BufferSize() + buffer_size_after_limit_);
+}
+
+inline void CodedInputStream::Advance(int amount) { buffer_ += amount; }
+
+inline void CodedInputStream::SetRecursionLimit(int limit) {
+  recursion_budget_ += limit - recursion_limit_;
+  recursion_limit_ = limit;
+}
+
+inline bool CodedInputStream::IncrementRecursionDepth() {
+  --recursion_budget_;
+  return recursion_budget_ >= 0;
+}
+
+inline void CodedInputStream::DecrementRecursionDepth() {
+  if (recursion_budget_ < recursion_limit_) ++recursion_budget_;
+}
+
+inline void CodedInputStream::UnsafeDecrementRecursionDepth() {
+  assert(recursion_budget_ < recursion_limit_);
+  ++recursion_budget_;
+}
+
+inline void CodedInputStream::SetExtensionRegistry(const DescriptorPool* pool,
+                                                   MessageFactory* factory) {
+  extension_pool_ = pool;
+  extension_factory_ = factory;
+}
+
+inline const DescriptorPool* CodedInputStream::GetExtensionPool() {
+  return extension_pool_;
+}
+
+inline MessageFactory* CodedInputStream::GetExtensionFactory() {
+  return extension_factory_;
+}
+
+inline int CodedInputStream::BufferSize() const {
+  return static_cast<int>(buffer_end_ - buffer_);
+}
+
+inline CodedInputStream::CodedInputStream(ZeroCopyInputStream* input)
+    : buffer_(nullptr),
+      buffer_end_(nullptr),
+      input_(input),
+      total_bytes_read_(0),
+      overflow_bytes_(0),
+      last_tag_(0),
+      legitimate_message_end_(false),
+      aliasing_enabled_(false),
+      current_limit_(std::numeric_limits<int32_t>::max()),
+      buffer_size_after_limit_(0),
+      total_bytes_limit_(kDefaultTotalBytesLimit),
+      recursion_budget_(default_recursion_limit_),
+      recursion_limit_(default_recursion_limit_),
+      extension_pool_(nullptr),
+      extension_factory_(nullptr) {
+  // Eagerly Refresh() so buffer space is immediately available.
+  Refresh();
+}
+
+inline CodedInputStream::CodedInputStream(const uint8_t* buffer, int size)
+    : buffer_(buffer),
+      buffer_end_(buffer + size),
+      input_(nullptr),
+      total_bytes_read_(size),
+      overflow_bytes_(0),
+      last_tag_(0),
+      legitimate_message_end_(false),
+      aliasing_enabled_(false),
+      current_limit_(size),
+      buffer_size_after_limit_(0),
+      total_bytes_limit_(kDefaultTotalBytesLimit),
+      recursion_budget_(default_recursion_limit_),
+      recursion_limit_(default_recursion_limit_),
+      extension_pool_(nullptr),
+      extension_factory_(nullptr) {
+  // Note that setting current_limit_ == size is important to prevent some
+  // code paths from trying to access input_ and segfaulting.
+}
+
+inline bool CodedInputStream::IsFlat() const { return input_ == nullptr; }
+
+inline bool CodedInputStream::Skip(int count) {
+  if (count < 0) return false;  // security: count is often user-supplied
+
+  const int original_buffer_size = BufferSize();
+
+  if (count <= original_buffer_size) {
+    // Just skipping within the current buffer.  Easy.
+    Advance(count);
+    return true;
+  }
+
+  return SkipFallback(count, original_buffer_size);
+}
+
+template <class Stream, class>
+inline CodedOutputStream::CodedOutputStream(Stream* stream)
+    : impl_(stream, IsDefaultSerializationDeterministic(), &cur_),
+      start_count_(stream->ByteCount()) {
+  InitEagerly(stream);
+}
+
+template <class Stream, class>
+inline CodedOutputStream::CodedOutputStream(Stream* stream, bool eager_init)
+    : impl_(stream, IsDefaultSerializationDeterministic(), &cur_),
+      start_count_(stream->ByteCount()) {
+  if (eager_init) {
+    InitEagerly(stream);
+  }
+}
+
+template <class Stream>
+inline void CodedOutputStream::InitEagerly(Stream* stream) {
+  void* data;
+  int size;
+  if (PROTOBUF_PREDICT_TRUE(stream->Next(&data, &size) && size > 0)) {
+    cur_ = impl_.SetInitialBuffer(data, size);
+  }
+}
+
+inline uint8_t* CodedOutputStream::WriteVarint32ToArray(uint32_t value,
+                                                        uint8_t* target) {
+  return EpsCopyOutputStream::UnsafeVarint(value, target);
+}
+
+inline uint8_t* CodedOutputStream::WriteVarint32ToArrayOutOfLine(
+    uint32_t value, uint8_t* target) {
+  target[0] = static_cast<uint8_t>(value);
+  if (value < 0x80) {
+    return target + 1;
+  } else {
+    return WriteVarint32ToArrayOutOfLineHelper(value, target);
+  }
+}
+
+inline uint8_t* CodedOutputStream::WriteVarint64ToArray(uint64_t value,
+                                                        uint8_t* target) {
+  return EpsCopyOutputStream::UnsafeVarint(value, target);
+}
+
+inline void CodedOutputStream::WriteVarint32SignExtended(int32_t value) {
+  WriteVarint64(static_cast<uint64_t>(value));
+}
+
+inline uint8_t* CodedOutputStream::WriteVarint32SignExtendedToArray(
+    int32_t value, uint8_t* target) {
+  return WriteVarint64ToArray(static_cast<uint64_t>(value), target);
+}
+
+inline uint8_t* CodedOutputStream::WriteLittleEndian32ToArray(uint32_t value,
+                                                              uint8_t* target) {
+#if defined(PROTOBUF_LITTLE_ENDIAN) && \
+    !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+  memcpy(target, &value, sizeof(value));
+#else
+  target[0] = static_cast<uint8_t>(value);
+  target[1] = static_cast<uint8_t>(value >> 8);
+  target[2] = static_cast<uint8_t>(value >> 16);
+  target[3] = static_cast<uint8_t>(value >> 24);
+#endif
+  return target + sizeof(value);
+}
+
+inline uint8_t* CodedOutputStream::WriteLittleEndian64ToArray(uint64_t value,
+                                                              uint8_t* target) {
+#if defined(PROTOBUF_LITTLE_ENDIAN) && \
+    !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+  memcpy(target, &value, sizeof(value));
+#else
+  uint32_t part0 = static_cast<uint32_t>(value);
+  uint32_t part1 = static_cast<uint32_t>(value >> 32);
+
+  target[0] = static_cast<uint8_t>(part0);
+  target[1] = static_cast<uint8_t>(part0 >> 8);
+  target[2] = static_cast<uint8_t>(part0 >> 16);
+  target[3] = static_cast<uint8_t>(part0 >> 24);
+  target[4] = static_cast<uint8_t>(part1);
+  target[5] = static_cast<uint8_t>(part1 >> 8);
+  target[6] = static_cast<uint8_t>(part1 >> 16);
+  target[7] = static_cast<uint8_t>(part1 >> 24);
+#endif
+  return target + sizeof(value);
+}
+
+inline void CodedOutputStream::WriteVarint32(uint32_t value) {
+  cur_ = impl_.EnsureSpace(cur_);
+  SetCur(WriteVarint32ToArray(value, Cur()));
+}
+
+inline void CodedOutputStream::WriteVarint64(uint64_t value) {
+  cur_ = impl_.EnsureSpace(cur_);
+  SetCur(WriteVarint64ToArray(value, Cur()));
+}
+
+inline void CodedOutputStream::WriteTag(uint32_t value) {
+  WriteVarint32(value);
+}
+
+inline uint8_t* CodedOutputStream::WriteTagToArray(uint32_t value,
+                                                   uint8_t* target) {
+  return WriteVarint32ToArray(value, target);
+}
+
+inline size_t CodedOutputStream::VarintSize32(uint32_t value) {
+  // This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1
+  // Use an explicit multiplication to implement the divide of
+  // a number in the 1..31 range.
+  // Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is
+  // undefined.
+  uint32_t log2value = Bits::Log2FloorNonZero(value | 0x1);
+  return static_cast<size_t>((log2value * 9 + 73) / 64);
+}
+
+inline size_t CodedOutputStream::VarintSize32PlusOne(uint32_t value) {
+  // Same as above, but one more.
+  uint32_t log2value = Bits::Log2FloorNonZero(value | 0x1);
+  return static_cast<size_t>((log2value * 9 + 73 + 64) / 64);
+}
+
+inline size_t CodedOutputStream::VarintSize64(uint64_t value) {
+  // This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1
+  // Use an explicit multiplication to implement the divide of
+  // a number in the 1..63 range.
+  // Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is
+  // undefined.
+  uint32_t log2value = Bits::Log2FloorNonZero64(value | 0x1);
+  return static_cast<size_t>((log2value * 9 + 73) / 64);
+}
+
+inline size_t CodedOutputStream::VarintSize64PlusOne(uint64_t value) {
+  // Same as above, but one more.
+  uint32_t log2value = Bits::Log2FloorNonZero64(value | 0x1);
+  return static_cast<size_t>((log2value * 9 + 73 + 64) / 64);
+}
+
+inline size_t CodedOutputStream::VarintSize32SignExtended(int32_t value) {
+  return VarintSize64(static_cast<uint64_t>(int64_t{value}));
+}
+
+inline size_t CodedOutputStream::VarintSize32SignExtendedPlusOne(
+    int32_t value) {
+  return VarintSize64PlusOne(static_cast<uint64_t>(int64_t{value}));
+}
+
+inline void CodedOutputStream::WriteString(const std::string& str) {
+  WriteRaw(str.data(), static_cast<int>(str.size()));
+}
+
+inline void CodedOutputStream::WriteRawMaybeAliased(const void* data,
+                                                    int size) {
+  cur_ = impl_.WriteRawMaybeAliased(data, size, cur_);
+}
+
+inline uint8_t* CodedOutputStream::WriteRawToArray(const void* data, int size,
+                                                   uint8_t* target) {
+  memcpy(target, data, size);
+  return target + size;
+}
+
+inline uint8_t* CodedOutputStream::WriteStringToArray(const std::string& str,
+                                                      uint8_t* target) {
+  return WriteRawToArray(str.data(), static_cast<int>(str.size()), target);
+}
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#if defined(_MSC_VER) && _MSC_VER >= 1300 && !defined(__INTEL_COMPILER)
+#pragma runtime_checks("c", restore)
+#endif  // _MSC_VER && !defined(__INTEL_COMPILER)
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_IO_CODED_STREAM_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/gzip_stream.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/gzip_stream.h
new file mode 100644
index 0000000..4cf71b6
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/gzip_stream.h
@@ -0,0 +1,205 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: brianolson@google.com (Brian Olson)
+//
+// This file contains the definition for classes GzipInputStream and
+// GzipOutputStream.
+//
+// GzipInputStream decompresses data from an underlying
+// ZeroCopyInputStream and provides the decompressed data as a
+// ZeroCopyInputStream.
+//
+// GzipOutputStream is an ZeroCopyOutputStream that compresses data to
+// an underlying ZeroCopyOutputStream.
+
+#ifndef GOOGLE_PROTOBUF_IO_GZIP_STREAM_H__
+#define GOOGLE_PROTOBUF_IO_GZIP_STREAM_H__
+
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/port.h>
+#include "zlib.h"
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+// A ZeroCopyInputStream that reads compressed data through zlib
+class PROTOBUF_EXPORT GzipInputStream PROTOBUF_FUTURE_FINAL
+    : public ZeroCopyInputStream {
+ public:
+  // Format key for constructor
+  enum Format {
+    // zlib will autodetect gzip header or deflate stream
+    AUTO = 0,
+
+    // GZIP streams have some extra header data for file attributes.
+    GZIP = 1,
+
+    // Simpler zlib stream format.
+    ZLIB = 2,
+  };
+
+  // buffer_size and format may be -1 for default of 64kB and GZIP format
+  explicit GzipInputStream(ZeroCopyInputStream* sub_stream,
+                           Format format = AUTO, int buffer_size = -1);
+  virtual ~GzipInputStream();
+
+  // Return last error message or NULL if no error.
+  inline const char* ZlibErrorMessage() const { return zcontext_.msg; }
+  inline int ZlibErrorCode() const { return zerror_; }
+
+  // implements ZeroCopyInputStream ----------------------------------
+  bool Next(const void** data, int* size) override;
+  void BackUp(int count) override;
+  bool Skip(int count) override;
+  int64_t ByteCount() const override;
+
+ private:
+  Format format_;
+
+  ZeroCopyInputStream* sub_stream_;
+
+  z_stream zcontext_;
+  int zerror_;
+
+  void* output_buffer_;
+  void* output_position_;
+  size_t output_buffer_length_;
+  int64_t byte_count_;
+
+  int Inflate(int flush);
+  void DoNextOutput(const void** data, int* size);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GzipInputStream);
+};
+
+class PROTOBUF_EXPORT GzipOutputStream PROTOBUF_FUTURE_FINAL
+    : public ZeroCopyOutputStream {
+ public:
+  // Format key for constructor
+  enum Format {
+    // GZIP streams have some extra header data for file attributes.
+    GZIP = 1,
+
+    // Simpler zlib stream format.
+    ZLIB = 2,
+  };
+
+  struct PROTOBUF_EXPORT Options {
+    // Defaults to GZIP.
+    Format format;
+
+    // What size buffer to use internally.  Defaults to 64kB.
+    int buffer_size;
+
+    // A number between 0 and 9, where 0 is no compression and 9 is best
+    // compression.  Defaults to Z_DEFAULT_COMPRESSION (see zlib.h).
+    int compression_level;
+
+    // Defaults to Z_DEFAULT_STRATEGY.  Can also be set to Z_FILTERED,
+    // Z_HUFFMAN_ONLY, or Z_RLE.  See the documentation for deflateInit2 in
+    // zlib.h for definitions of these constants.
+    int compression_strategy;
+
+    Options();  // Initializes with default values.
+  };
+
+  // Create a GzipOutputStream with default options.
+  explicit GzipOutputStream(ZeroCopyOutputStream* sub_stream);
+
+  // Create a GzipOutputStream with the given options.
+  GzipOutputStream(ZeroCopyOutputStream* sub_stream, const Options& options);
+
+  virtual ~GzipOutputStream();
+
+  // Return last error message or NULL if no error.
+  inline const char* ZlibErrorMessage() const { return zcontext_.msg; }
+  inline int ZlibErrorCode() const { return zerror_; }
+
+  // Flushes data written so far to zipped data in the underlying stream.
+  // It is the caller's responsibility to flush the underlying stream if
+  // necessary.
+  // Compression may be less efficient stopping and starting around flushes.
+  // Returns true if no error.
+  //
+  // Please ensure that block size is > 6. Here is an excerpt from the zlib
+  // doc that explains why:
+  //
+  // In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out
+  // is greater than six to avoid repeated flush markers due to
+  // avail_out == 0 on return.
+  bool Flush();
+
+  // Writes out all data and closes the gzip stream.
+  // It is the caller's responsibility to close the underlying stream if
+  // necessary.
+  // Returns true if no error.
+  bool Close();
+
+  // implements ZeroCopyOutputStream ---------------------------------
+  bool Next(void** data, int* size) override;
+  void BackUp(int count) override;
+  int64_t ByteCount() const override;
+
+ private:
+  ZeroCopyOutputStream* sub_stream_;
+  // Result from calling Next() on sub_stream_
+  void* sub_data_;
+  int sub_data_size_;
+
+  z_stream zcontext_;
+  int zerror_;
+  void* input_buffer_;
+  size_t input_buffer_length_;
+
+  // Shared constructor code.
+  void Init(ZeroCopyOutputStream* sub_stream, const Options& options);
+
+  // Do some compression.
+  // Takes zlib flush mode.
+  // Returns zlib error code.
+  int Deflate(int flush);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GzipOutputStream);
+};
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_IO_GZIP_STREAM_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/io_win32.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/io_win32.h
new file mode 100644
index 0000000..a72b4ea
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/io_win32.h
@@ -0,0 +1,141 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: laszlocsomor@google.com (Laszlo Csomor)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+// This file contains the declarations for Windows implementations of
+// commonly used POSIX functions such as open(2) and access(2), as well
+// as macro definitions for flags of these functions.
+//
+// By including this file you'll redefine open/access/etc. to
+// ::google::protobuf::io::win32::{open/access/etc.}.
+// Make sure you don't include a header that attempts to redeclare or
+// redefine these functions, that'll lead to confusing compilation
+// errors. It's best to #include this file as the last one to ensure that.
+//
+// This file is only used on Windows, it's empty on other platforms.
+
+#ifndef GOOGLE_PROTOBUF_IO_IO_WIN32_H__
+#define GOOGLE_PROTOBUF_IO_IO_WIN32_H__
+
+#if defined(_WIN32)
+
+#include <functional>
+#include <string>
+
+#include <google/protobuf/port.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+// Compilers on Windows other than MSVC (e.g. Cygwin, MinGW32) define the
+// following functions already, except for mkdir.
+namespace google {
+namespace protobuf {
+namespace io {
+namespace win32 {
+
+PROTOBUF_EXPORT FILE* fopen(const char* path, const char* mode);
+PROTOBUF_EXPORT int access(const char* path, int mode);
+PROTOBUF_EXPORT int chdir(const char* path);
+PROTOBUF_EXPORT int close(int fd);
+PROTOBUF_EXPORT int dup(int fd);
+PROTOBUF_EXPORT int dup2(int fd1, int fd2);
+PROTOBUF_EXPORT int mkdir(const char* path, int _mode);
+PROTOBUF_EXPORT int open(const char* path, int flags, int mode = 0);
+PROTOBUF_EXPORT int read(int fd, void* buffer, size_t size);
+PROTOBUF_EXPORT int setmode(int fd, int mode);
+PROTOBUF_EXPORT int stat(const char* path, struct _stat* buffer);
+PROTOBUF_EXPORT int write(int fd, const void* buffer, size_t size);
+PROTOBUF_EXPORT std::wstring testonly_utf8_to_winpath(const char* path);
+
+enum class ExpandWildcardsResult {
+  kSuccess = 0,
+  kErrorNoMatchingFile = 1,
+  kErrorInputPathConversion = 2,
+  kErrorOutputPathConversion = 3,
+};
+
+// Expand wildcards in a path pattern, feed the result to a consumer function.
+//
+// `path` must be a valid, Windows-style path. It may be absolute, or relative
+// to the current working directory, and it may contain wildcards ("*" and "?")
+// in the last path segment. This function passes all matching file names to
+// `consume`. The resulting paths may not be absolute nor normalized.
+//
+// The function returns a value from `ExpandWildcardsResult`.
+PROTOBUF_EXPORT ExpandWildcardsResult ExpandWildcards(
+    const std::string& path, std::function<void(const std::string&)> consume);
+
+namespace strings {
+
+// Convert from UTF-16 to Active-Code-Page-encoded or to UTF-8-encoded text.
+PROTOBUF_EXPORT bool wcs_to_mbs(const wchar_t* s, std::string* out,
+                                bool outUtf8);
+
+// Convert from Active-Code-Page-encoded or UTF-8-encoded text to UTF-16.
+PROTOBUF_EXPORT bool mbs_to_wcs(const char* s, std::wstring* out, bool inUtf8);
+
+// Convert from UTF-8-encoded text to UTF-16.
+PROTOBUF_EXPORT bool utf8_to_wcs(const char* input, std::wstring* out);
+
+// Convert from UTF-16-encoded text to UTF-8.
+PROTOBUF_EXPORT bool wcs_to_utf8(const wchar_t* input, std::string* out);
+
+}  // namespace strings
+
+}  // namespace win32
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#ifndef W_OK
+#define W_OK 02  // not defined by MSVC for whatever reason
+#endif
+
+#ifndef F_OK
+#define F_OK 00  // not defined by MSVC for whatever reason
+#endif
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // defined(_WIN32)
+
+#endif  // GOOGLE_PROTOBUF_IO_IO_WIN32_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/printer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/printer.h
new file mode 100644
index 0000000..92a4321
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/printer.h
@@ -0,0 +1,387 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Utility class for writing text to a ZeroCopyOutputStream.
+
+#ifndef GOOGLE_PROTOBUF_IO_PRINTER_H__
+#define GOOGLE_PROTOBUF_IO_PRINTER_H__
+
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+class ZeroCopyOutputStream;  // zero_copy_stream.h
+
+// Records annotations about a Printer's output.
+class PROTOBUF_EXPORT AnnotationCollector {
+ public:
+  // Annotation is a offset range and a payload pair.
+  typedef std::pair<std::pair<size_t, size_t>, std::string> Annotation;
+
+  // Records that the bytes in file_path beginning with begin_offset and ending
+  // before end_offset are associated with the SourceCodeInfo-style path.
+  virtual void AddAnnotation(size_t begin_offset, size_t end_offset,
+                             const std::string& file_path,
+                             const std::vector<int>& path) = 0;
+
+  // TODO(gerbens) I don't see why we need virtuals here. Just a vector of
+  // range, payload pairs stored in a context should suffice.
+  virtual void AddAnnotationNew(Annotation& /* a */) {}
+
+  virtual ~AnnotationCollector() {}
+};
+
+// Records annotations about a Printer's output to the given protocol buffer,
+// assuming that the buffer has an ::Annotation message exposing path,
+// source_file, begin and end fields.
+template <typename AnnotationProto>
+class AnnotationProtoCollector : public AnnotationCollector {
+ public:
+  // annotation_proto is the protocol buffer to which new Annotations should be
+  // added. It is not owned by the AnnotationProtoCollector.
+  explicit AnnotationProtoCollector(AnnotationProto* annotation_proto)
+      : annotation_proto_(annotation_proto) {}
+
+  // Override for AnnotationCollector::AddAnnotation.
+  void AddAnnotation(size_t begin_offset, size_t end_offset,
+                     const std::string& file_path,
+                     const std::vector<int>& path) override {
+    typename AnnotationProto::Annotation* annotation =
+        annotation_proto_->add_annotation();
+    for (int i = 0; i < path.size(); ++i) {
+      annotation->add_path(path[i]);
+    }
+    annotation->set_source_file(file_path);
+    annotation->set_begin(begin_offset);
+    annotation->set_end(end_offset);
+  }
+  // Override for AnnotationCollector::AddAnnotation.
+  void AddAnnotationNew(Annotation& a) override {
+    auto* annotation = annotation_proto_->add_annotation();
+    annotation->ParseFromString(a.second);
+    annotation->set_begin(a.first.first);
+    annotation->set_end(a.first.second);
+  }
+
+ private:
+  // The protocol buffer to which new annotations should be added.
+  AnnotationProto* const annotation_proto_;
+};
+
+// This simple utility class assists in code generation.  It basically
+// allows the caller to define a set of variables and then output some
+// text with variable substitutions.  Example usage:
+//
+//   Printer printer(output, '$');
+//   map<string, string> vars;
+//   vars["name"] = "Bob";
+//   printer.Print(vars, "My name is $name$.");
+//
+// The above writes "My name is Bob." to the output stream.
+//
+// Printer aggressively enforces correct usage, crashing (with assert failures)
+// in the case of undefined variables in debug builds. This helps greatly in
+// debugging code which uses it.
+//
+// If a Printer is constructed with an AnnotationCollector, it will provide it
+// with annotations that connect the Printer's output to paths that can identify
+// various descriptors.  In the above example, if person_ is a descriptor that
+// identifies Bob, we can associate the output string "My name is Bob." with
+// a source path pointing to that descriptor with:
+//
+//   printer.Annotate("name", person_);
+//
+// The AnnotationCollector will be sent an annotation linking the output range
+// covering "Bob" to the logical path provided by person_.  Tools may use
+// this association to (for example) link "Bob" in the output back to the
+// source file that defined the person_ descriptor identifying Bob.
+//
+// Annotate can only examine variables substituted during the last call to
+// Print.  It is invalid to refer to a variable that was used multiple times
+// in a single Print call.
+//
+// In full generality, one may specify a range of output text using a beginning
+// substitution variable and an ending variable.  The resulting annotation will
+// span from the first character of the substituted value for the beginning
+// variable to the last character of the substituted value for the ending
+// variable.  For example, the Annotate call above is equivalent to this one:
+//
+//   printer.Annotate("name", "name", person_);
+//
+// This is useful if multiple variables combine to form a single span of output
+// that should be annotated with the same source path.  For example:
+//
+//   Printer printer(output, '$');
+//   map<string, string> vars;
+//   vars["first"] = "Alice";
+//   vars["last"] = "Smith";
+//   printer.Print(vars, "My name is $first$ $last$.");
+//   printer.Annotate("first", "last", person_);
+//
+// This code would associate the span covering "Alice Smith" in the output with
+// the person_ descriptor.
+//
+// Note that the beginning variable must come before (or overlap with, in the
+// case of zero-sized substitution values) the ending variable.
+//
+// It is also sometimes useful to use variables with zero-sized values as
+// markers.  This avoids issues with multiple references to the same variable
+// and also allows annotation ranges to span literal text from the Print
+// templates:
+//
+//   Printer printer(output, '$');
+//   map<string, string> vars;
+//   vars["foo"] = "bar";
+//   vars["function"] = "call";
+//   vars["mark"] = "";
+//   printer.Print(vars, "$function$($foo$,$foo$)$mark$");
+//   printer.Annotate("function", "mark", call_);
+//
+// This code associates the span covering "call(bar,bar)" in the output with the
+// call_ descriptor.
+
+class PROTOBUF_EXPORT Printer {
+ public:
+  // Create a printer that writes text to the given output stream.  Use the
+  // given character as the delimiter for variables.
+  Printer(ZeroCopyOutputStream* output, char variable_delimiter);
+
+  // Create a printer that writes text to the given output stream.  Use the
+  // given character as the delimiter for variables.  If annotation_collector
+  // is not null, Printer will provide it with annotations about code written
+  // to the stream.  annotation_collector is not owned by Printer.
+  Printer(ZeroCopyOutputStream* output, char variable_delimiter,
+          AnnotationCollector* annotation_collector);
+
+  ~Printer();
+
+  // Link a substitution variable emitted by the last call to Print to the
+  // object described by descriptor.
+  template <typename SomeDescriptor>
+  void Annotate(const char* varname, const SomeDescriptor* descriptor) {
+    Annotate(varname, varname, descriptor);
+  }
+
+  // Link the output range defined by the substitution variables as emitted by
+  // the last call to Print to the object described by descriptor. The range
+  // begins at begin_varname's value and ends after the last character of the
+  // value substituted for end_varname.
+  template <typename SomeDescriptor>
+  void Annotate(const char* begin_varname, const char* end_varname,
+                const SomeDescriptor* descriptor) {
+    if (annotation_collector_ == NULL) {
+      // Annotations aren't turned on for this Printer, so don't pay the cost
+      // of building the location path.
+      return;
+    }
+    std::vector<int> path;
+    descriptor->GetLocationPath(&path);
+    Annotate(begin_varname, end_varname, descriptor->file()->name(), path);
+  }
+
+  // Link a substitution variable emitted by the last call to Print to the file
+  // with path file_name.
+  void Annotate(const char* varname, const std::string& file_name) {
+    Annotate(varname, varname, file_name);
+  }
+
+  // Link the output range defined by the substitution variables as emitted by
+  // the last call to Print to the file with path file_name. The range begins
+  // at begin_varname's value and ends after the last character of the value
+  // substituted for end_varname.
+  void Annotate(const char* begin_varname, const char* end_varname,
+                const std::string& file_name) {
+    if (annotation_collector_ == NULL) {
+      // Annotations aren't turned on for this Printer.
+      return;
+    }
+    std::vector<int> empty_path;
+    Annotate(begin_varname, end_varname, file_name, empty_path);
+  }
+
+  // Print some text after applying variable substitutions.  If a particular
+  // variable in the text is not defined, this will crash.  Variables to be
+  // substituted are identified by their names surrounded by delimiter
+  // characters (as given to the constructor).  The variable bindings are
+  // defined by the given map.
+  void Print(const std::map<std::string, std::string>& variables,
+             const char* text);
+
+  // Like the first Print(), except the substitutions are given as parameters.
+  template <typename... Args>
+  void Print(const char* text, const Args&... args) {
+    std::map<std::string, std::string> vars;
+    PrintInternal(&vars, text, args...);
+  }
+
+  // Indent text by two spaces.  After calling Indent(), two spaces will be
+  // inserted at the beginning of each line of text.  Indent() may be called
+  // multiple times to produce deeper indents.
+  void Indent();
+
+  // Reduces the current indent level by two spaces, or crashes if the indent
+  // level is zero.
+  void Outdent();
+
+  // Write a string to the output buffer.
+  // This method does not look for newlines to add indentation.
+  void PrintRaw(const std::string& data);
+
+  // Write a zero-delimited string to output buffer.
+  // This method does not look for newlines to add indentation.
+  void PrintRaw(const char* data);
+
+  // Write some bytes to the output buffer.
+  // This method does not look for newlines to add indentation.
+  void WriteRaw(const char* data, int size);
+
+  // FormatInternal is a helper function not meant to use directly, use
+  // compiler::cpp::Formatter instead. This function is meant to support
+  // formatting text using named variables (eq. "$foo$) from a lookup map (vars)
+  // and variables directly supplied by arguments (eq "$1$" meaning first
+  // argument which is the zero index element of args).
+  void FormatInternal(const std::vector<std::string>& args,
+                      const std::map<std::string, std::string>& vars,
+                      const char* format);
+
+  // True if any write to the underlying stream failed.  (We don't just
+  // crash in this case because this is an I/O failure, not a programming
+  // error.)
+  bool failed() const { return failed_; }
+
+ private:
+  // Link the output range defined by the substitution variables as emitted by
+  // the last call to Print to the object found at the SourceCodeInfo-style path
+  // in a file with path file_path. The range begins at the start of
+  // begin_varname's value and ends after the last character of the value
+  // substituted for end_varname. Note that begin_varname and end_varname
+  // may refer to the same variable.
+  void Annotate(const char* begin_varname, const char* end_varname,
+                const std::string& file_path, const std::vector<int>& path);
+
+  // Base case
+  void PrintInternal(std::map<std::string, std::string>* vars,
+                     const char* text) {
+    Print(*vars, text);
+  }
+
+  template <typename... Args>
+  void PrintInternal(std::map<std::string, std::string>* vars, const char* text,
+                     const char* key, const std::string& value,
+                     const Args&... args) {
+    (*vars)[key] = value;
+    PrintInternal(vars, text, args...);
+  }
+
+  // Copy size worth of bytes from data to buffer_.
+  void CopyToBuffer(const char* data, int size);
+
+  void push_back(char c) {
+    if (failed_) return;
+    if (buffer_size_ == 0) {
+      if (!Next()) return;
+    }
+    *buffer_++ = c;
+    buffer_size_--;
+    offset_++;
+  }
+
+  bool Next();
+
+  inline void IndentIfAtStart();
+  const char* WriteVariable(
+      const std::vector<std::string>& args,
+      const std::map<std::string, std::string>& vars, const char* format,
+      int* arg_index,
+      std::vector<AnnotationCollector::Annotation>* annotations);
+
+  const char variable_delimiter_;
+
+  ZeroCopyOutputStream* const output_;
+  char* buffer_;
+  int buffer_size_;
+  // The current position, in bytes, in the output stream.  This is equivalent
+  // to the total number of bytes that have been written so far.  This value is
+  // used to calculate annotation ranges in the substitutions_ map below.
+  size_t offset_;
+
+  std::string indent_;
+  bool at_start_of_line_;
+  bool failed_;
+
+  // A map from variable name to [start, end) offsets in the output buffer.
+  // These refer to the offsets used for a variable after the last call to
+  // Print.  If a variable was used more than once, the entry used in
+  // this map is set to a negative-length span.  For singly-used variables, the
+  // start offset is the beginning of the substitution; the end offset is the
+  // last byte of the substitution plus one (such that (end - start) is the
+  // length of the substituted string).
+  std::map<std::string, std::pair<size_t, size_t> > substitutions_;
+
+  // Keeps track of the keys in substitutions_ that need to be updated when
+  // indents are inserted. These are keys that refer to the beginning of the
+  // current line.
+  std::vector<std::string> line_start_variables_;
+
+  // Returns true and sets range to the substitution range in the output for
+  // varname if varname was used once in the last call to Print. If varname
+  // was not used, or if it was used multiple times, returns false (and
+  // fails a debug assertion).
+  bool GetSubstitutionRange(const char* varname,
+                            std::pair<size_t, size_t>* range);
+
+  // If non-null, annotation_collector_ is used to store annotations about
+  // generated code.
+  AnnotationCollector* const annotation_collector_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Printer);
+};
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_IO_PRINTER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/strtod.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/strtod.h
new file mode 100644
index 0000000..38f544a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/strtod.h
@@ -0,0 +1,55 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// A locale-independent version of strtod(), used to parse floating
+// point default values in .proto files, where the decimal separator
+// is always a dot.
+
+#ifndef GOOGLE_PROTOBUF_IO_STRTOD_H__
+#define GOOGLE_PROTOBUF_IO_STRTOD_H__
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+// A locale-independent version of the standard strtod(), which always
+// uses a dot as the decimal separator.
+double NoLocaleStrtod(const char* str, char** endptr);
+
+// Casts a double value to a float value. If the value is outside of the
+// representable range of float, it will be converted to positive or negative
+// infinity.
+float SafeDoubleToFloat(double value);
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_IO_STRTOD_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/tokenizer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/tokenizer.h
new file mode 100644
index 0000000..4abab7e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/tokenizer.h
@@ -0,0 +1,442 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Class for parsing tokenized text from a ZeroCopyInputStream.
+
+#ifndef GOOGLE_PROTOBUF_IO_TOKENIZER_H__
+#define GOOGLE_PROTOBUF_IO_TOKENIZER_H__
+
+
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+class ZeroCopyInputStream;  // zero_copy_stream.h
+
+// Defined in this file.
+class ErrorCollector;
+class Tokenizer;
+
+// By "column number", the proto compiler refers to a count of the number
+// of bytes before a given byte, except that a tab character advances to
+// the next multiple of 8 bytes.  Note in particular that column numbers
+// are zero-based, while many user interfaces use one-based column numbers.
+typedef int ColumnNumber;
+
+// Abstract interface for an object which collects the errors that occur
+// during parsing.  A typical implementation might simply print the errors
+// to stdout.
+class PROTOBUF_EXPORT ErrorCollector {
+ public:
+  inline ErrorCollector() {}
+  virtual ~ErrorCollector();
+
+  // Indicates that there was an error in the input at the given line and
+  // column numbers.  The numbers are zero-based, so you may want to add
+  // 1 to each before printing them.
+  virtual void AddError(int line, ColumnNumber column,
+                        const std::string& message) = 0;
+
+  // Indicates that there was a warning in the input at the given line and
+  // column numbers.  The numbers are zero-based, so you may want to add
+  // 1 to each before printing them.
+  virtual void AddWarning(int /* line */, ColumnNumber /* column */,
+                          const std::string& /* message */) {}
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorCollector);
+};
+
+// This class converts a stream of raw text into a stream of tokens for
+// the protocol definition parser to parse.  The tokens recognized are
+// similar to those that make up the C language; see the TokenType enum for
+// precise descriptions.  Whitespace and comments are skipped.  By default,
+// C- and C++-style comments are recognized, but other styles can be used by
+// calling set_comment_style().
+class PROTOBUF_EXPORT Tokenizer {
+ public:
+  // Construct a Tokenizer that reads and tokenizes text from the given
+  // input stream and writes errors to the given error_collector.
+  // The caller keeps ownership of input and error_collector.
+  Tokenizer(ZeroCopyInputStream* input, ErrorCollector* error_collector);
+  ~Tokenizer();
+
+  enum TokenType {
+    TYPE_START,  // Next() has not yet been called.
+    TYPE_END,    // End of input reached.  "text" is empty.
+
+    TYPE_IDENTIFIER,  // A sequence of letters, digits, and underscores, not
+                      // starting with a digit.  It is an error for a number
+                      // to be followed by an identifier with no space in
+                      // between.
+    TYPE_INTEGER,     // A sequence of digits representing an integer.  Normally
+                      // the digits are decimal, but a prefix of "0x" indicates
+                      // a hex number and a leading zero indicates octal, just
+                      // like with C numeric literals.  A leading negative sign
+                      // is NOT included in the token; it's up to the parser to
+                      // interpret the unary minus operator on its own.
+    TYPE_FLOAT,       // A floating point literal, with a fractional part and/or
+                      // an exponent.  Always in decimal.  Again, never
+                      // negative.
+    TYPE_STRING,      // A quoted sequence of escaped characters.  Either single
+                      // or double quotes can be used, but they must match.
+                      // A string literal cannot cross a line break.
+    TYPE_SYMBOL,      // Any other printable character, like '!' or '+'.
+                      // Symbols are always a single character, so "!+$%" is
+                      // four tokens.
+    TYPE_WHITESPACE,  // A sequence of whitespace.  This token type is only
+                      // produced if report_whitespace() is true.  It is not
+                      // reported for whitespace within comments or strings.
+    TYPE_NEWLINE,     // A newline (\n).  This token type is only
+                      // produced if report_whitespace() is true and
+                      // report_newlines() is true.  It is not reported for
+                      // newlines in comments or strings.
+  };
+
+  // Structure representing a token read from the token stream.
+  struct Token {
+    TokenType type;
+    std::string text;  // The exact text of the token as it appeared in
+                       // the input.  e.g. tokens of TYPE_STRING will still
+                       // be escaped and in quotes.
+
+    // "line" and "column" specify the position of the first character of
+    // the token within the input stream.  They are zero-based.
+    int line;
+    ColumnNumber column;
+    ColumnNumber end_column;
+  };
+
+  // Get the current token.  This is updated when Next() is called.  Before
+  // the first call to Next(), current() has type TYPE_START and no contents.
+  const Token& current();
+
+  // Return the previous token -- i.e. what current() returned before the
+  // previous call to Next().
+  const Token& previous();
+
+  // Advance to the next token.  Returns false if the end of the input is
+  // reached.
+  bool Next();
+
+  // Like Next(), but also collects comments which appear between the previous
+  // and next tokens.
+  //
+  // Comments which appear to be attached to the previous token are stored
+  // in *prev_tailing_comments.  Comments which appear to be attached to the
+  // next token are stored in *next_leading_comments.  Comments appearing in
+  // between which do not appear to be attached to either will be added to
+  // detached_comments.  Any of these parameters can be NULL to simply discard
+  // the comments.
+  //
+  // A series of line comments appearing on consecutive lines, with no other
+  // tokens appearing on those lines, will be treated as a single comment.
+  //
+  // Only the comment content is returned; comment markers (e.g. //) are
+  // stripped out.  For block comments, leading whitespace and an asterisk will
+  // be stripped from the beginning of each line other than the first.  Newlines
+  // are included in the output.
+  //
+  // Examples:
+  //
+  //   optional int32 foo = 1;  // Comment attached to foo.
+  //   // Comment attached to bar.
+  //   optional int32 bar = 2;
+  //
+  //   optional string baz = 3;
+  //   // Comment attached to baz.
+  //   // Another line attached to baz.
+  //
+  //   // Comment attached to qux.
+  //   //
+  //   // Another line attached to qux.
+  //   optional double qux = 4;
+  //
+  //   // Detached comment.  This is not attached to qux or corge
+  //   // because there are blank lines separating it from both.
+  //
+  //   optional string corge = 5;
+  //   /* Block comment attached
+  //    * to corge.  Leading asterisks
+  //    * will be removed. */
+  //   /* Block comment attached to
+  //    * grault. */
+  //   optional int32 grault = 6;
+  bool NextWithComments(std::string* prev_trailing_comments,
+                        std::vector<std::string>* detached_comments,
+                        std::string* next_leading_comments);
+
+  // Parse helpers ---------------------------------------------------
+
+  // Parses a TYPE_FLOAT token.  This never fails, so long as the text actually
+  // comes from a TYPE_FLOAT token parsed by Tokenizer.  If it doesn't, the
+  // result is undefined (possibly an assert failure).
+  static double ParseFloat(const std::string& text);
+
+  // Parses a TYPE_STRING token.  This never fails, so long as the text actually
+  // comes from a TYPE_STRING token parsed by Tokenizer.  If it doesn't, the
+  // result is undefined (possibly an assert failure).
+  static void ParseString(const std::string& text, std::string* output);
+
+  // Identical to ParseString, but appends to output.
+  static void ParseStringAppend(const std::string& text, std::string* output);
+
+  // Parses a TYPE_INTEGER token.  Returns false if the result would be
+  // greater than max_value.  Otherwise, returns true and sets *output to the
+  // result.  If the text is not from a Token of type TYPE_INTEGER originally
+  // parsed by a Tokenizer, the result is undefined (possibly an assert
+  // failure).
+  static bool ParseInteger(const std::string& text, uint64_t max_value,
+                           uint64_t* output);
+
+  // Options ---------------------------------------------------------
+
+  // Set true to allow floats to be suffixed with the letter 'f'.  Tokens
+  // which would otherwise be integers but which have the 'f' suffix will be
+  // forced to be interpreted as floats.  For all other purposes, the 'f' is
+  // ignored.
+  void set_allow_f_after_float(bool value) { allow_f_after_float_ = value; }
+
+  // Valid values for set_comment_style().
+  enum CommentStyle {
+    // Line comments begin with "//", block comments are delimited by "/*" and
+    // "*/".
+    CPP_COMMENT_STYLE,
+    // Line comments begin with "#".  No way to write block comments.
+    SH_COMMENT_STYLE
+  };
+
+  // Sets the comment style.
+  void set_comment_style(CommentStyle style) { comment_style_ = style; }
+
+  // Whether to require whitespace between a number and a field name.
+  // Default is true. Do not use this; for Google-internal cleanup only.
+  void set_require_space_after_number(bool require) {
+    require_space_after_number_ = require;
+  }
+
+  // Whether to allow string literals to span multiple lines. Default is false.
+  // Do not use this; for Google-internal cleanup only.
+  void set_allow_multiline_strings(bool allow) {
+    allow_multiline_strings_ = allow;
+  }
+
+  // If true, whitespace tokens are reported by Next().
+  // Note: `set_report_whitespace(false)` implies `set_report_newlines(false)`.
+  bool report_whitespace() const;
+  void set_report_whitespace(bool report);
+
+  // If true, newline tokens are reported by Next().
+  // Note: `set_report_newlines(true)` implies `set_report_whitespace(true)`.
+  bool report_newlines() const;
+  void set_report_newlines(bool report);
+
+  // External helper: validate an identifier.
+  static bool IsIdentifier(const std::string& text);
+
+  // -----------------------------------------------------------------
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Tokenizer);
+
+  Token current_;   // Returned by current().
+  Token previous_;  // Returned by previous().
+
+  ZeroCopyInputStream* input_;
+  ErrorCollector* error_collector_;
+
+  char current_char_;   // == buffer_[buffer_pos_], updated by NextChar().
+  const char* buffer_;  // Current buffer returned from input_.
+  int buffer_size_;     // Size of buffer_.
+  int buffer_pos_;      // Current position within the buffer.
+  bool read_error_;     // Did we previously encounter a read error?
+
+  // Line and column number of current_char_ within the whole input stream.
+  int line_;
+  ColumnNumber column_;
+
+  // String to which text should be appended as we advance through it.
+  // Call RecordTo(&str) to start recording and StopRecording() to stop.
+  // E.g. StartToken() calls RecordTo(&current_.text).  record_start_ is the
+  // position within the current buffer where recording started.
+  std::string* record_target_;
+  int record_start_;
+
+  // Options.
+  bool allow_f_after_float_;
+  CommentStyle comment_style_;
+  bool require_space_after_number_;
+  bool allow_multiline_strings_;
+  bool report_whitespace_ = false;
+  bool report_newlines_ = false;
+
+  // Since we count columns we need to interpret tabs somehow.  We'll take
+  // the standard 8-character definition for lack of any way to do better.
+  // This must match the documentation of ColumnNumber.
+  static const int kTabWidth = 8;
+
+  // -----------------------------------------------------------------
+  // Helper methods.
+
+  // Consume this character and advance to the next one.
+  void NextChar();
+
+  // Read a new buffer from the input.
+  void Refresh();
+
+  inline void RecordTo(std::string* target);
+  inline void StopRecording();
+
+  // Called when the current character is the first character of a new
+  // token (not including whitespace or comments).
+  inline void StartToken();
+  // Called when the current character is the first character after the
+  // end of the last token.  After this returns, current_.text will
+  // contain all text consumed since StartToken() was called.
+  inline void EndToken();
+
+  // Convenience method to add an error at the current line and column.
+  void AddError(const std::string& message) {
+    error_collector_->AddError(line_, column_, message);
+  }
+
+  // -----------------------------------------------------------------
+  // The following four methods are used to consume tokens of specific
+  // types.  They are actually used to consume all characters *after*
+  // the first, since the calling function consumes the first character
+  // in order to decide what kind of token is being read.
+
+  // Read and consume a string, ending when the given delimiter is
+  // consumed.
+  void ConsumeString(char delimiter);
+
+  // Read and consume a number, returning TYPE_FLOAT or TYPE_INTEGER
+  // depending on what was read.  This needs to know if the first
+  // character was a zero in order to correctly recognize hex and octal
+  // numbers.
+  // It also needs to know if the first character was a . to parse floating
+  // point correctly.
+  TokenType ConsumeNumber(bool started_with_zero, bool started_with_dot);
+
+  // Consume the rest of a line.
+  void ConsumeLineComment(std::string* content);
+  // Consume until "*/".
+  void ConsumeBlockComment(std::string* content);
+
+  enum NextCommentStatus {
+    // Started a line comment.
+    LINE_COMMENT,
+
+    // Started a block comment.
+    BLOCK_COMMENT,
+
+    // Consumed a slash, then realized it wasn't a comment.  current_ has
+    // been filled in with a slash token.  The caller should return it.
+    SLASH_NOT_COMMENT,
+
+    // We do not appear to be starting a comment here.
+    NO_COMMENT
+  };
+
+  // If we're at the start of a new comment, consume it and return what kind
+  // of comment it is.
+  NextCommentStatus TryConsumeCommentStart();
+
+  // If we're looking at a TYPE_WHITESPACE token and `report_whitespace_` is
+  // true, consume it and return true.
+  bool TryConsumeWhitespace();
+
+  // If we're looking at a TYPE_NEWLINE token and `report_newlines_` is true,
+  // consume it and return true.
+  bool TryConsumeNewline();
+
+  // -----------------------------------------------------------------
+  // These helper methods make the parsing code more readable.  The
+  // "character classes" referred to are defined at the top of the .cc file.
+  // Basically it is a C++ class with one method:
+  //   static bool InClass(char c);
+  // The method returns true if c is a member of this "class", like "Letter"
+  // or "Digit".
+
+  // Returns true if the current character is of the given character
+  // class, but does not consume anything.
+  template <typename CharacterClass>
+  inline bool LookingAt();
+
+  // If the current character is in the given class, consume it and return
+  // true.  Otherwise return false.
+  // e.g. TryConsumeOne<Letter>()
+  template <typename CharacterClass>
+  inline bool TryConsumeOne();
+
+  // Like above, but try to consume the specific character indicated.
+  inline bool TryConsume(char c);
+
+  // Consume zero or more of the given character class.
+  template <typename CharacterClass>
+  inline void ConsumeZeroOrMore();
+
+  // Consume one or more of the given character class or log the given
+  // error message.
+  // e.g. ConsumeOneOrMore<Digit>("Expected digits.");
+  template <typename CharacterClass>
+  inline void ConsumeOneOrMore(const char* error);
+};
+
+// inline methods ====================================================
+inline const Tokenizer::Token& Tokenizer::current() { return current_; }
+
+inline const Tokenizer::Token& Tokenizer::previous() { return previous_; }
+
+inline void Tokenizer::ParseString(const std::string& text,
+                                   std::string* output) {
+  output->clear();
+  ParseStringAppend(text, output);
+}
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_IO_TOKENIZER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/zero_copy_stream.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/zero_copy_stream.h
new file mode 100644
index 0000000..2041cbf
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/zero_copy_stream.h
@@ -0,0 +1,260 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This file contains the ZeroCopyInputStream and ZeroCopyOutputStream
+// interfaces, which represent abstract I/O streams to and from which
+// protocol buffers can be read and written.  For a few simple
+// implementations of these interfaces, see zero_copy_stream_impl.h.
+//
+// These interfaces are different from classic I/O streams in that they
+// try to minimize the amount of data copying that needs to be done.
+// To accomplish this, responsibility for allocating buffers is moved to
+// the stream object, rather than being the responsibility of the caller.
+// So, the stream can return a buffer which actually points directly into
+// the final data structure where the bytes are to be stored, and the caller
+// can interact directly with that buffer, eliminating an intermediate copy
+// operation.
+//
+// As an example, consider the common case in which you are reading bytes
+// from an array that is already in memory (or perhaps an mmap()ed file).
+// With classic I/O streams, you would do something like:
+//   char buffer[BUFFER_SIZE];
+//   input->Read(buffer, BUFFER_SIZE);
+//   DoSomething(buffer, BUFFER_SIZE);
+// Then, the stream basically just calls memcpy() to copy the data from
+// the array into your buffer.  With a ZeroCopyInputStream, you would do
+// this instead:
+//   const void* buffer;
+//   int size;
+//   input->Next(&buffer, &size);
+//   DoSomething(buffer, size);
+// Here, no copy is performed.  The input stream returns a pointer directly
+// into the backing array, and the caller ends up reading directly from it.
+//
+// If you want to be able to read the old-fashion way, you can create
+// a CodedInputStream or CodedOutputStream wrapping these objects and use
+// their ReadRaw()/WriteRaw() methods.  These will, of course, add a copy
+// step, but Coded*Stream will handle buffering so at least it will be
+// reasonably efficient.
+//
+// ZeroCopyInputStream example:
+//   // Read in a file and print its contents to stdout.
+//   int fd = open("myfile", O_RDONLY);
+//   ZeroCopyInputStream* input = new FileInputStream(fd);
+//
+//   const void* buffer;
+//   int size;
+//   while (input->Next(&buffer, &size)) {
+//     cout.write(buffer, size);
+//   }
+//
+//   delete input;
+//   close(fd);
+//
+// ZeroCopyOutputStream example:
+//   // Copy the contents of "infile" to "outfile", using plain read() for
+//   // "infile" but a ZeroCopyOutputStream for "outfile".
+//   int infd = open("infile", O_RDONLY);
+//   int outfd = open("outfile", O_WRONLY);
+//   ZeroCopyOutputStream* output = new FileOutputStream(outfd);
+//
+//   void* buffer;
+//   int size;
+//   while (output->Next(&buffer, &size)) {
+//     int bytes = read(infd, buffer, size);
+//     if (bytes < size) {
+//       // Reached EOF.
+//       output->BackUp(size - bytes);
+//       break;
+//     }
+//   }
+//
+//   delete output;
+//   close(infd);
+//   close(outfd);
+
+#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__
+#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__
+
+
+#include <google/protobuf/stubs/common.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+// Defined in this file.
+class ZeroCopyInputStream;
+class ZeroCopyOutputStream;
+
+// Abstract interface similar to an input stream but designed to minimize
+// copying.
+class PROTOBUF_EXPORT ZeroCopyInputStream {
+ public:
+  ZeroCopyInputStream() {}
+  virtual ~ZeroCopyInputStream() {}
+
+  // Obtains a chunk of data from the stream.
+  //
+  // Preconditions:
+  // * "size" and "data" are not NULL.
+  //
+  // Postconditions:
+  // * If the returned value is false, there is no more data to return or
+  //   an error occurred.  All errors are permanent.
+  // * Otherwise, "size" points to the actual number of bytes read and "data"
+  //   points to a pointer to a buffer containing these bytes.
+  // * Ownership of this buffer remains with the stream, and the buffer
+  //   remains valid only until some other method of the stream is called
+  //   or the stream is destroyed.
+  // * It is legal for the returned buffer to have zero size, as long
+  //   as repeatedly calling Next() eventually yields a buffer with non-zero
+  //   size.
+  virtual bool Next(const void** data, int* size) = 0;
+
+  // Backs up a number of bytes, so that the next call to Next() returns
+  // data again that was already returned by the last call to Next().  This
+  // is useful when writing procedures that are only supposed to read up
+  // to a certain point in the input, then return.  If Next() returns a
+  // buffer that goes beyond what you wanted to read, you can use BackUp()
+  // to return to the point where you intended to finish.
+  //
+  // This method can be called with `count = 0` to finalize (flush) any
+  // previously returned buffer. For example, a file output stream can
+  // flush buffers returned from a previous call to Next() upon such
+  // BackUp(0) invocations. ZeroCopyOutputStream callers should always
+  // invoke BackUp() after a final Next() call, even if there is no
+  // excess buffer data to be backed up to indicate a flush point.
+  //
+  // Preconditions:
+  // * The last method called must have been Next().
+  // * count must be less than or equal to the size of the last buffer
+  //   returned by Next().
+  //
+  // Postconditions:
+  // * The last "count" bytes of the last buffer returned by Next() will be
+  //   pushed back into the stream.  Subsequent calls to Next() will return
+  //   the same data again before producing new data.
+  virtual void BackUp(int count) = 0;
+
+  // Skips a number of bytes.  Returns false if the end of the stream is
+  // reached or some input error occurred.  In the end-of-stream case, the
+  // stream is advanced to the end of the stream (so ByteCount() will return
+  // the total size of the stream).
+  virtual bool Skip(int count) = 0;
+
+  // Returns the total number of bytes read since this object was created.
+  virtual int64_t ByteCount() const = 0;
+
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyInputStream);
+};
+
+// Abstract interface similar to an output stream but designed to minimize
+// copying.
+class PROTOBUF_EXPORT ZeroCopyOutputStream {
+ public:
+  ZeroCopyOutputStream() {}
+  virtual ~ZeroCopyOutputStream() {}
+
+  // Obtains a buffer into which data can be written.  Any data written
+  // into this buffer will eventually (maybe instantly, maybe later on)
+  // be written to the output.
+  //
+  // Preconditions:
+  // * "size" and "data" are not NULL.
+  //
+  // Postconditions:
+  // * If the returned value is false, an error occurred.  All errors are
+  //   permanent.
+  // * Otherwise, "size" points to the actual number of bytes in the buffer
+  //   and "data" points to the buffer.
+  // * Ownership of this buffer remains with the stream, and the buffer
+  //   remains valid only until some other method of the stream is called
+  //   or the stream is destroyed.
+  // * Any data which the caller stores in this buffer will eventually be
+  //   written to the output (unless BackUp() is called).
+  // * It is legal for the returned buffer to have zero size, as long
+  //   as repeatedly calling Next() eventually yields a buffer with non-zero
+  //   size.
+  virtual bool Next(void** data, int* size) = 0;
+
+  // Backs up a number of bytes, so that the end of the last buffer returned
+  // by Next() is not actually written.  This is needed when you finish
+  // writing all the data you want to write, but the last buffer was bigger
+  // than you needed.  You don't want to write a bunch of garbage after the
+  // end of your data, so you use BackUp() to back up.
+  //
+  // Preconditions:
+  // * The last method called must have been Next().
+  // * count must be less than or equal to the size of the last buffer
+  //   returned by Next().
+  // * The caller must not have written anything to the last "count" bytes
+  //   of that buffer.
+  //
+  // Postconditions:
+  // * The last "count" bytes of the last buffer returned by Next() will be
+  //   ignored.
+  virtual void BackUp(int count) = 0;
+
+  // Returns the total number of bytes written since this object was created.
+  virtual int64_t ByteCount() const = 0;
+
+  // Write a given chunk of data to the output.  Some output streams may
+  // implement this in a way that avoids copying. Check AllowsAliasing() before
+  // calling WriteAliasedRaw(). It will GOOGLE_CHECK fail if WriteAliasedRaw() is
+  // called on a stream that does not allow aliasing.
+  //
+  // NOTE: It is caller's responsibility to ensure that the chunk of memory
+  // remains live until all of the data has been consumed from the stream.
+  virtual bool WriteAliasedRaw(const void* data, int size);
+  virtual bool AllowsAliasing() const { return false; }
+
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyOutputStream);
+};
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/zero_copy_stream_impl.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/zero_copy_stream_impl.h
new file mode 100644
index 0000000..a385992
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/zero_copy_stream_impl.h
@@ -0,0 +1,336 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This file contains common implementations of the interfaces defined in
+// zero_copy_stream.h which are only included in the full (non-lite)
+// protobuf library.  These implementations include Unix file descriptors
+// and C++ iostreams.  See also:  zero_copy_stream_impl_lite.h
+
+#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__
+#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__
+
+
+#include <iosfwd>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+// ===================================================================
+
+// A ZeroCopyInputStream which reads from a file descriptor.
+//
+// FileInputStream is preferred over using an ifstream with IstreamInputStream.
+// The latter will introduce an extra layer of buffering, harming performance.
+// Also, it's conceivable that FileInputStream could someday be enhanced
+// to use zero-copy file descriptors on OSs which support them.
+class PROTOBUF_EXPORT FileInputStream PROTOBUF_FUTURE_FINAL
+    : public ZeroCopyInputStream {
+ public:
+  // Creates a stream that reads from the given Unix file descriptor.
+  // If a block_size is given, it specifies the number of bytes that
+  // should be read and returned with each call to Next().  Otherwise,
+  // a reasonable default is used.
+  explicit FileInputStream(int file_descriptor, int block_size = -1);
+
+  // Flushes any buffers and closes the underlying file.  Returns false if
+  // an error occurs during the process; use GetErrno() to examine the error.
+  // Even if an error occurs, the file descriptor is closed when this returns.
+  bool Close();
+
+  // By default, the file descriptor is not closed when the stream is
+  // destroyed.  Call SetCloseOnDelete(true) to change that.  WARNING:
+  // This leaves no way for the caller to detect if close() fails.  If
+  // detecting close() errors is important to you, you should arrange
+  // to close the descriptor yourself.
+  void SetCloseOnDelete(bool value) { copying_input_.SetCloseOnDelete(value); }
+
+  // If an I/O error has occurred on this file descriptor, this is the
+  // errno from that error.  Otherwise, this is zero.  Once an error
+  // occurs, the stream is broken and all subsequent operations will
+  // fail.
+  int GetErrno() const { return copying_input_.GetErrno(); }
+
+  // implements ZeroCopyInputStream ----------------------------------
+  bool Next(const void** data, int* size) override;
+  void BackUp(int count) override;
+  bool Skip(int count) override;
+  int64_t ByteCount() const override;
+
+ private:
+  class PROTOBUF_EXPORT CopyingFileInputStream PROTOBUF_FUTURE_FINAL
+      : public CopyingInputStream {
+   public:
+    CopyingFileInputStream(int file_descriptor);
+    ~CopyingFileInputStream() override;
+
+    bool Close();
+    void SetCloseOnDelete(bool value) { close_on_delete_ = value; }
+    int GetErrno() const { return errno_; }
+
+    // implements CopyingInputStream ---------------------------------
+    int Read(void* buffer, int size) override;
+    int Skip(int count) override;
+
+   private:
+    // The file descriptor.
+    const int file_;
+    bool close_on_delete_;
+    bool is_closed_;
+
+    // The errno of the I/O error, if one has occurred.  Otherwise, zero.
+    int errno_;
+
+    // Did we try to seek once and fail?  If so, we assume this file descriptor
+    // doesn't support seeking and won't try again.
+    bool previous_seek_failed_;
+
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingFileInputStream);
+  };
+
+  CopyingFileInputStream copying_input_;
+  CopyingInputStreamAdaptor impl_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileInputStream);
+};
+
+// ===================================================================
+
+// A ZeroCopyOutputStream which writes to a file descriptor.
+//
+// FileOutputStream is preferred over using an ofstream with
+// OstreamOutputStream.  The latter will introduce an extra layer of buffering,
+// harming performance.  Also, it's conceivable that FileOutputStream could
+// someday be enhanced to use zero-copy file descriptors on OSs which
+// support them.
+class PROTOBUF_EXPORT FileOutputStream PROTOBUF_FUTURE_FINAL
+    : public CopyingOutputStreamAdaptor {
+ public:
+  // Creates a stream that writes to the given Unix file descriptor.
+  // If a block_size is given, it specifies the size of the buffers
+  // that should be returned by Next().  Otherwise, a reasonable default
+  // is used.
+  explicit FileOutputStream(int file_descriptor, int block_size = -1);
+
+  ~FileOutputStream() override;
+
+  // Flushes any buffers and closes the underlying file.  Returns false if
+  // an error occurs during the process; use GetErrno() to examine the error.
+  // Even if an error occurs, the file descriptor is closed when this returns.
+  bool Close();
+
+  // By default, the file descriptor is not closed when the stream is
+  // destroyed.  Call SetCloseOnDelete(true) to change that.  WARNING:
+  // This leaves no way for the caller to detect if close() fails.  If
+  // detecting close() errors is important to you, you should arrange
+  // to close the descriptor yourself.
+  void SetCloseOnDelete(bool value) { copying_output_.SetCloseOnDelete(value); }
+
+  // If an I/O error has occurred on this file descriptor, this is the
+  // errno from that error.  Otherwise, this is zero.  Once an error
+  // occurs, the stream is broken and all subsequent operations will
+  // fail.
+  int GetErrno() const { return copying_output_.GetErrno(); }
+
+ private:
+  class PROTOBUF_EXPORT CopyingFileOutputStream PROTOBUF_FUTURE_FINAL
+      : public CopyingOutputStream {
+   public:
+    CopyingFileOutputStream(int file_descriptor);
+    ~CopyingFileOutputStream() override;
+
+    bool Close();
+    void SetCloseOnDelete(bool value) { close_on_delete_ = value; }
+    int GetErrno() const { return errno_; }
+
+    // implements CopyingOutputStream --------------------------------
+    bool Write(const void* buffer, int size) override;
+
+   private:
+    // The file descriptor.
+    const int file_;
+    bool close_on_delete_;
+    bool is_closed_;
+
+    // The errno of the I/O error, if one has occurred.  Otherwise, zero.
+    int errno_;
+
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingFileOutputStream);
+  };
+
+  CopyingFileOutputStream copying_output_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileOutputStream);
+};
+
+// ===================================================================
+
+// A ZeroCopyInputStream which reads from a C++ istream.
+//
+// Note that for reading files (or anything represented by a file descriptor),
+// FileInputStream is more efficient.
+class PROTOBUF_EXPORT IstreamInputStream PROTOBUF_FUTURE_FINAL
+    : public ZeroCopyInputStream {
+ public:
+  // Creates a stream that reads from the given C++ istream.
+  // If a block_size is given, it specifies the number of bytes that
+  // should be read and returned with each call to Next().  Otherwise,
+  // a reasonable default is used.
+  explicit IstreamInputStream(std::istream* stream, int block_size = -1);
+
+  // implements ZeroCopyInputStream ----------------------------------
+  bool Next(const void** data, int* size) override;
+  void BackUp(int count) override;
+  bool Skip(int count) override;
+  int64_t ByteCount() const override;
+
+ private:
+  class PROTOBUF_EXPORT CopyingIstreamInputStream PROTOBUF_FUTURE_FINAL
+      : public CopyingInputStream {
+   public:
+    CopyingIstreamInputStream(std::istream* input);
+    ~CopyingIstreamInputStream() override;
+
+    // implements CopyingInputStream ---------------------------------
+    int Read(void* buffer, int size) override;
+    // (We use the default implementation of Skip().)
+
+   private:
+    // The stream.
+    std::istream* input_;
+
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingIstreamInputStream);
+  };
+
+  CopyingIstreamInputStream copying_input_;
+  CopyingInputStreamAdaptor impl_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(IstreamInputStream);
+};
+
+// ===================================================================
+
+// A ZeroCopyOutputStream which writes to a C++ ostream.
+//
+// Note that for writing files (or anything represented by a file descriptor),
+// FileOutputStream is more efficient.
+class PROTOBUF_EXPORT OstreamOutputStream PROTOBUF_FUTURE_FINAL
+    : public ZeroCopyOutputStream {
+ public:
+  // Creates a stream that writes to the given C++ ostream.
+  // If a block_size is given, it specifies the size of the buffers
+  // that should be returned by Next().  Otherwise, a reasonable default
+  // is used.
+  explicit OstreamOutputStream(std::ostream* stream, int block_size = -1);
+  ~OstreamOutputStream() override;
+
+  // implements ZeroCopyOutputStream ---------------------------------
+  bool Next(void** data, int* size) override;
+  void BackUp(int count) override;
+  int64_t ByteCount() const override;
+
+ private:
+  class PROTOBUF_EXPORT CopyingOstreamOutputStream PROTOBUF_FUTURE_FINAL
+      : public CopyingOutputStream {
+   public:
+    CopyingOstreamOutputStream(std::ostream* output);
+    ~CopyingOstreamOutputStream() override;
+
+    // implements CopyingOutputStream --------------------------------
+    bool Write(const void* buffer, int size) override;
+
+   private:
+    // The stream.
+    std::ostream* output_;
+
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingOstreamOutputStream);
+  };
+
+  CopyingOstreamOutputStream copying_output_;
+  CopyingOutputStreamAdaptor impl_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OstreamOutputStream);
+};
+
+// ===================================================================
+
+// A ZeroCopyInputStream which reads from several other streams in sequence.
+// ConcatenatingInputStream is unable to distinguish between end-of-stream
+// and read errors in the underlying streams, so it assumes any errors mean
+// end-of-stream.  So, if the underlying streams fail for any other reason,
+// ConcatenatingInputStream may do odd things.  It is suggested that you do
+// not use ConcatenatingInputStream on streams that might produce read errors
+// other than end-of-stream.
+class PROTOBUF_EXPORT ConcatenatingInputStream PROTOBUF_FUTURE_FINAL
+    : public ZeroCopyInputStream {
+ public:
+  // All streams passed in as well as the array itself must remain valid
+  // until the ConcatenatingInputStream is destroyed.
+  ConcatenatingInputStream(ZeroCopyInputStream* const streams[], int count);
+  ~ConcatenatingInputStream() override = default;
+
+  // implements ZeroCopyInputStream ----------------------------------
+  bool Next(const void** data, int* size) override;
+  void BackUp(int count) override;
+  bool Skip(int count) override;
+  int64_t ByteCount() const override;
+
+
+ private:
+  // As streams are retired, streams_ is incremented and count_ is
+  // decremented.
+  ZeroCopyInputStream* const* streams_;
+  int stream_count_;
+  int64_t bytes_retired_;  // Bytes read from previous streams.
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ConcatenatingInputStream);
+};
+
+// ===================================================================
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/zero_copy_stream_impl_lite.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/zero_copy_stream_impl_lite.h
new file mode 100644
index 0000000..cbda328
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/zero_copy_stream_impl_lite.h
@@ -0,0 +1,413 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This file contains common implementations of the interfaces defined in
+// zero_copy_stream.h which are included in the "lite" protobuf library.
+// These implementations cover I/O on raw arrays and strings, as well as
+// adaptors which make it easy to implement streams based on traditional
+// streams.  Of course, many users will probably want to write their own
+// implementations of these interfaces specific to the particular I/O
+// abstractions they prefer to use, but these should cover the most common
+// cases.
+
+#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_LITE_H__
+#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_LITE_H__
+
+
+#include <iosfwd>
+#include <memory>
+#include <string>
+
+#include <google/protobuf/stubs/callback.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+// ===================================================================
+
+// A ZeroCopyInputStream backed by an in-memory array of bytes.
+class PROTOBUF_EXPORT ArrayInputStream PROTOBUF_FUTURE_FINAL
+    : public ZeroCopyInputStream {
+ public:
+  // Create an InputStream that returns the bytes pointed to by "data".
+  // "data" remains the property of the caller but must remain valid until
+  // the stream is destroyed.  If a block_size is given, calls to Next()
+  // will return data blocks no larger than the given size.  Otherwise, the
+  // first call to Next() returns the entire array.  block_size is mainly
+  // useful for testing; in production you would probably never want to set
+  // it.
+  ArrayInputStream(const void* data, int size, int block_size = -1);
+  ~ArrayInputStream() override = default;
+
+  // implements ZeroCopyInputStream ----------------------------------
+  bool Next(const void** data, int* size) override;
+  void BackUp(int count) override;
+  bool Skip(int count) override;
+  int64_t ByteCount() const override;
+
+
+ private:
+  const uint8_t* const data_;  // The byte array.
+  const int size_;           // Total size of the array.
+  const int block_size_;     // How many bytes to return at a time.
+
+  int position_;
+  int last_returned_size_;  // How many bytes we returned last time Next()
+                            // was called (used for error checking only).
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayInputStream);
+};
+
+// ===================================================================
+
+// A ZeroCopyOutputStream backed by an in-memory array of bytes.
+class PROTOBUF_EXPORT ArrayOutputStream PROTOBUF_FUTURE_FINAL
+    : public ZeroCopyOutputStream {
+ public:
+  // Create an OutputStream that writes to the bytes pointed to by "data".
+  // "data" remains the property of the caller but must remain valid until
+  // the stream is destroyed.  If a block_size is given, calls to Next()
+  // will return data blocks no larger than the given size.  Otherwise, the
+  // first call to Next() returns the entire array.  block_size is mainly
+  // useful for testing; in production you would probably never want to set
+  // it.
+  ArrayOutputStream(void* data, int size, int block_size = -1);
+  ~ArrayOutputStream() override = default;
+
+  // implements ZeroCopyOutputStream ---------------------------------
+  bool Next(void** data, int* size) override;
+  void BackUp(int count) override;
+  int64_t ByteCount() const override;
+
+ private:
+  uint8_t* const data_;     // The byte array.
+  const int size_;        // Total size of the array.
+  const int block_size_;  // How many bytes to return at a time.
+
+  int position_;
+  int last_returned_size_;  // How many bytes we returned last time Next()
+                            // was called (used for error checking only).
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayOutputStream);
+};
+
+// ===================================================================
+
+// A ZeroCopyOutputStream which appends bytes to a string.
+class PROTOBUF_EXPORT StringOutputStream PROTOBUF_FUTURE_FINAL
+    : public ZeroCopyOutputStream {
+ public:
+  // Create a StringOutputStream which appends bytes to the given string.
+  // The string remains property of the caller, but it is mutated in arbitrary
+  // ways and MUST NOT be accessed in any way until you're done with the
+  // stream. Either be sure there's no further usage, or (safest) destroy the
+  // stream before using the contents.
+  //
+  // Hint:  If you call target->reserve(n) before creating the stream,
+  //   the first call to Next() will return at least n bytes of buffer
+  //   space.
+  explicit StringOutputStream(std::string* target);
+  ~StringOutputStream() override = default;
+
+  // implements ZeroCopyOutputStream ---------------------------------
+  bool Next(void** data, int* size) override;
+  void BackUp(int count) override;
+  int64_t ByteCount() const override;
+
+ private:
+  static constexpr size_t kMinimumSize = 16;
+
+  std::string* target_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOutputStream);
+};
+
+// Note:  There is no StringInputStream.  Instead, just create an
+// ArrayInputStream as follows:
+//   ArrayInputStream input(str.data(), str.size());
+
+// ===================================================================
+
+// A generic traditional input stream interface.
+//
+// Lots of traditional input streams (e.g. file descriptors, C stdio
+// streams, and C++ iostreams) expose an interface where every read
+// involves copying bytes into a buffer.  If you want to take such an
+// interface and make a ZeroCopyInputStream based on it, simply implement
+// CopyingInputStream and then use CopyingInputStreamAdaptor.
+//
+// CopyingInputStream implementations should avoid buffering if possible.
+// CopyingInputStreamAdaptor does its own buffering and will read data
+// in large blocks.
+class PROTOBUF_EXPORT CopyingInputStream {
+ public:
+  virtual ~CopyingInputStream() {}
+
+  // Reads up to "size" bytes into the given buffer.  Returns the number of
+  // bytes read.  Read() waits until at least one byte is available, or
+  // returns zero if no bytes will ever become available (EOF), or -1 if a
+  // permanent read error occurred.
+  virtual int Read(void* buffer, int size) = 0;
+
+  // Skips the next "count" bytes of input.  Returns the number of bytes
+  // actually skipped.  This will always be exactly equal to "count" unless
+  // EOF was reached or a permanent read error occurred.
+  //
+  // The default implementation just repeatedly calls Read() into a scratch
+  // buffer.
+  virtual int Skip(int count);
+};
+
+// A ZeroCopyInputStream which reads from a CopyingInputStream.  This is
+// useful for implementing ZeroCopyInputStreams that read from traditional
+// streams.  Note that this class is not really zero-copy.
+//
+// If you want to read from file descriptors or C++ istreams, this is
+// already implemented for you:  use FileInputStream or IstreamInputStream
+// respectively.
+class PROTOBUF_EXPORT CopyingInputStreamAdaptor : public ZeroCopyInputStream {
+ public:
+  // Creates a stream that reads from the given CopyingInputStream.
+  // If a block_size is given, it specifies the number of bytes that
+  // should be read and returned with each call to Next().  Otherwise,
+  // a reasonable default is used.  The caller retains ownership of
+  // copying_stream unless SetOwnsCopyingStream(true) is called.
+  explicit CopyingInputStreamAdaptor(CopyingInputStream* copying_stream,
+                                     int block_size = -1);
+  ~CopyingInputStreamAdaptor() override;
+
+  // Call SetOwnsCopyingStream(true) to tell the CopyingInputStreamAdaptor to
+  // delete the underlying CopyingInputStream when it is destroyed.
+  void SetOwnsCopyingStream(bool value) { owns_copying_stream_ = value; }
+
+  // implements ZeroCopyInputStream ----------------------------------
+  bool Next(const void** data, int* size) override;
+  void BackUp(int count) override;
+  bool Skip(int count) override;
+  int64_t ByteCount() const override;
+
+ private:
+  // Insures that buffer_ is not NULL.
+  void AllocateBufferIfNeeded();
+  // Frees the buffer and resets buffer_used_.
+  void FreeBuffer();
+
+  // The underlying copying stream.
+  CopyingInputStream* copying_stream_;
+  bool owns_copying_stream_;
+
+  // True if we have seen a permanent error from the underlying stream.
+  bool failed_;
+
+  // The current position of copying_stream_, relative to the point where
+  // we started reading.
+  int64_t position_;
+
+  // Data is read into this buffer.  It may be NULL if no buffer is currently
+  // in use.  Otherwise, it points to an array of size buffer_size_.
+  std::unique_ptr<uint8_t[]> buffer_;
+  const int buffer_size_;
+
+  // Number of valid bytes currently in the buffer (i.e. the size last
+  // returned by Next()).  0 <= buffer_used_ <= buffer_size_.
+  int buffer_used_;
+
+  // Number of bytes in the buffer which were backed up over by a call to
+  // BackUp().  These need to be returned again.
+  // 0 <= backup_bytes_ <= buffer_used_
+  int backup_bytes_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingInputStreamAdaptor);
+};
+
+// ===================================================================
+
+// A generic traditional output stream interface.
+//
+// Lots of traditional output streams (e.g. file descriptors, C stdio
+// streams, and C++ iostreams) expose an interface where every write
+// involves copying bytes from a buffer.  If you want to take such an
+// interface and make a ZeroCopyOutputStream based on it, simply implement
+// CopyingOutputStream and then use CopyingOutputStreamAdaptor.
+//
+// CopyingOutputStream implementations should avoid buffering if possible.
+// CopyingOutputStreamAdaptor does its own buffering and will write data
+// in large blocks.
+class PROTOBUF_EXPORT CopyingOutputStream {
+ public:
+  virtual ~CopyingOutputStream() {}
+
+  // Writes "size" bytes from the given buffer to the output.  Returns true
+  // if successful, false on a write error.
+  virtual bool Write(const void* buffer, int size) = 0;
+};
+
+// A ZeroCopyOutputStream which writes to a CopyingOutputStream.  This is
+// useful for implementing ZeroCopyOutputStreams that write to traditional
+// streams.  Note that this class is not really zero-copy.
+//
+// If you want to write to file descriptors or C++ ostreams, this is
+// already implemented for you:  use FileOutputStream or OstreamOutputStream
+// respectively.
+class PROTOBUF_EXPORT CopyingOutputStreamAdaptor : public ZeroCopyOutputStream {
+ public:
+  // Creates a stream that writes to the given Unix file descriptor.
+  // If a block_size is given, it specifies the size of the buffers
+  // that should be returned by Next().  Otherwise, a reasonable default
+  // is used.
+  explicit CopyingOutputStreamAdaptor(CopyingOutputStream* copying_stream,
+                                      int block_size = -1);
+  ~CopyingOutputStreamAdaptor() override;
+
+  // Writes all pending data to the underlying stream.  Returns false if a
+  // write error occurred on the underlying stream.  (The underlying
+  // stream itself is not necessarily flushed.)
+  bool Flush();
+
+  // Call SetOwnsCopyingStream(true) to tell the CopyingOutputStreamAdaptor to
+  // delete the underlying CopyingOutputStream when it is destroyed.
+  void SetOwnsCopyingStream(bool value) { owns_copying_stream_ = value; }
+
+  // implements ZeroCopyOutputStream ---------------------------------
+  bool Next(void** data, int* size) override;
+  void BackUp(int count) override;
+  int64_t ByteCount() const override;
+  bool WriteAliasedRaw(const void* data, int size) override;
+  bool AllowsAliasing() const override { return true; }
+
+ private:
+  // Write the current buffer, if it is present.
+  bool WriteBuffer();
+  // Insures that buffer_ is not NULL.
+  void AllocateBufferIfNeeded();
+  // Frees the buffer.
+  void FreeBuffer();
+
+  // The underlying copying stream.
+  CopyingOutputStream* copying_stream_;
+  bool owns_copying_stream_;
+
+  // True if we have seen a permanent error from the underlying stream.
+  bool failed_;
+
+  // The current position of copying_stream_, relative to the point where
+  // we started writing.
+  int64_t position_;
+
+  // Data is written from this buffer.  It may be NULL if no buffer is
+  // currently in use.  Otherwise, it points to an array of size buffer_size_.
+  std::unique_ptr<uint8_t[]> buffer_;
+  const int buffer_size_;
+
+  // Number of valid bytes currently in the buffer (i.e. the size last
+  // returned by Next()).  When BackUp() is called, we just reduce this.
+  // 0 <= buffer_used_ <= buffer_size_.
+  int buffer_used_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingOutputStreamAdaptor);
+};
+
+// ===================================================================
+
+// A ZeroCopyInputStream which wraps some other stream and limits it to
+// a particular byte count.
+class PROTOBUF_EXPORT LimitingInputStream PROTOBUF_FUTURE_FINAL
+    : public ZeroCopyInputStream {
+ public:
+  LimitingInputStream(ZeroCopyInputStream* input, int64_t limit);
+  ~LimitingInputStream() override;
+
+  // implements ZeroCopyInputStream ----------------------------------
+  bool Next(const void** data, int* size) override;
+  void BackUp(int count) override;
+  bool Skip(int count) override;
+  int64_t ByteCount() const override;
+
+
+ private:
+  ZeroCopyInputStream* input_;
+  int64_t limit_;  // Decreases as we go, becomes negative if we overshoot.
+  int64_t prior_bytes_read_;  // Bytes read on underlying stream at construction
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LimitingInputStream);
+};
+
+
+// ===================================================================
+
+// mutable_string_data() and as_string_data() are workarounds to improve
+// the performance of writing new data to an existing string.  Unfortunately
+// the methods provided by the string class are suboptimal, and using memcpy()
+// is mildly annoying because it requires its pointer args to be non-NULL even
+// if we ask it to copy 0 bytes.  Furthermore, string_as_array() has the
+// property that it always returns NULL if its arg is the empty string, exactly
+// what we want to avoid if we're using it in conjunction with memcpy()!
+// With C++11, the desired memcpy() boils down to memcpy(..., &(*s)[0], size),
+// where s is a string*.  Without C++11, &(*s)[0] is not guaranteed to be safe,
+// so we use string_as_array(), and live with the extra logic that tests whether
+// *s is empty.
+
+// Return a pointer to mutable characters underlying the given string.  The
+// return value is valid until the next time the string is resized.  We
+// trust the caller to treat the return value as an array of length s->size().
+inline char* mutable_string_data(std::string* s) {
+  // This should be simpler & faster than string_as_array() because the latter
+  // is guaranteed to return NULL when *s is empty, so it has to check for that.
+  return &(*s)[0];
+}
+
+// as_string_data(s) is equivalent to
+//  ({ char* p = mutable_string_data(s); make_pair(p, p != NULL); })
+// Sometimes it's faster: in some scenarios p cannot be NULL, and then the
+// code can avoid that check.
+inline std::pair<char*, bool> as_string_data(std::string* s) {
+  char* p = mutable_string_data(s);
+  return std::make_pair(p, true);
+}
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_LITE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map.h
new file mode 100644
index 0000000..008c192
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map.h
@@ -0,0 +1,1448 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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 file defines the map container and its helpers to support protobuf maps.
+//
+// The Map and MapIterator types are provided by this header file.
+// Please avoid using other types defined here, unless they are public
+// types within Map or MapIterator, such as Map::value_type.
+
+#ifndef GOOGLE_PROTOBUF_MAP_H__
+#define GOOGLE_PROTOBUF_MAP_H__
+
+
+#include <functional>
+#include <initializer_list>
+#include <iterator>
+#include <limits>  // To support Visual Studio 2008
+#include <map>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#if defined(__cpp_lib_string_view)
+#include <string_view>
+#endif  // defined(__cpp_lib_string_view)
+
+#if !defined(GOOGLE_PROTOBUF_NO_RDTSC) && defined(__APPLE__)
+#include <mach/mach_time.h>
+#endif
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/generated_enum_util.h>
+#include <google/protobuf/map_type_handler.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/stubs/hash.h>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+template <typename Key, typename T>
+class Map;
+
+class MapIterator;
+
+template <typename Enum>
+struct is_proto_enum;
+
+namespace internal {
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType key_wire_type,
+          WireFormatLite::FieldType value_wire_type>
+class MapFieldLite;
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType key_wire_type,
+          WireFormatLite::FieldType value_wire_type>
+class MapField;
+
+template <typename Key, typename T>
+class TypeDefinedMapFieldBase;
+
+class DynamicMapField;
+
+class GeneratedMessageReflection;
+
+// re-implement std::allocator to use arena allocator for memory allocation.
+// Used for Map implementation. Users should not use this class
+// directly.
+template <typename U>
+class MapAllocator {
+ public:
+  using value_type = U;
+  using pointer = value_type*;
+  using const_pointer = const value_type*;
+  using reference = value_type&;
+  using const_reference = const value_type&;
+  using size_type = size_t;
+  using difference_type = ptrdiff_t;
+
+  constexpr MapAllocator() : arena_(nullptr) {}
+  explicit constexpr MapAllocator(Arena* arena) : arena_(arena) {}
+  template <typename X>
+  MapAllocator(const MapAllocator<X>& allocator)  // NOLINT(runtime/explicit)
+      : arena_(allocator.arena()) {}
+
+  // MapAllocator does not support alignments beyond 8. Technically we should
+  // support up to std::max_align_t, but this fails with ubsan and tcmalloc
+  // debug allocation logic which assume 8 as default alignment.
+  static_assert(alignof(value_type) <= 8, "");
+
+  pointer allocate(size_type n, const void* /* hint */ = nullptr) {
+    // If arena is not given, malloc needs to be called which doesn't
+    // construct element object.
+    if (arena_ == nullptr) {
+      return static_cast<pointer>(::operator new(n * sizeof(value_type)));
+    } else {
+      return reinterpret_cast<pointer>(
+          Arena::CreateArray<uint8_t>(arena_, n * sizeof(value_type)));
+    }
+  }
+
+  void deallocate(pointer p, size_type n) {
+    if (arena_ == nullptr) {
+      internal::SizedDelete(p, n * sizeof(value_type));
+    }
+  }
+
+#if !defined(GOOGLE_PROTOBUF_OS_APPLE) && !defined(GOOGLE_PROTOBUF_OS_NACL) && \
+    !defined(GOOGLE_PROTOBUF_OS_EMSCRIPTEN)
+  template <class NodeType, class... Args>
+  void construct(NodeType* p, Args&&... args) {
+    // Clang 3.6 doesn't compile static casting to void* directly. (Issue
+    // #1266) According C++ standard 5.2.9/1: "The static_cast operator shall
+    // not cast away constness". So first the maybe const pointer is casted to
+    // const void* and after the const void* is const casted.
+    new (const_cast<void*>(static_cast<const void*>(p)))
+        NodeType(std::forward<Args>(args)...);
+  }
+
+  template <class NodeType>
+  void destroy(NodeType* p) {
+    p->~NodeType();
+  }
+#else
+  void construct(pointer p, const_reference t) { new (p) value_type(t); }
+
+  void destroy(pointer p) { p->~value_type(); }
+#endif
+
+  template <typename X>
+  struct rebind {
+    using other = MapAllocator<X>;
+  };
+
+  template <typename X>
+  bool operator==(const MapAllocator<X>& other) const {
+    return arena_ == other.arena_;
+  }
+
+  template <typename X>
+  bool operator!=(const MapAllocator<X>& other) const {
+    return arena_ != other.arena_;
+  }
+
+  // To support Visual Studio 2008
+  size_type max_size() const {
+    // parentheses around (std::...:max) prevents macro warning of max()
+    return (std::numeric_limits<size_type>::max)();
+  }
+
+  // To support gcc-4.4, which does not properly
+  // support templated friend classes
+  Arena* arena() const { return arena_; }
+
+ private:
+  using DestructorSkippable_ = void;
+  Arena* arena_;
+};
+
+template <typename T>
+using KeyForTree =
+    typename std::conditional<std::is_scalar<T>::value, T,
+                              std::reference_wrapper<const T>>::type;
+
+// Default case: Not transparent.
+// We use std::hash<key_type>/std::less<key_type> and all the lookup functions
+// only accept `key_type`.
+template <typename key_type>
+struct TransparentSupport {
+  using hash = std::hash<key_type>;
+  using less = std::less<key_type>;
+
+  static bool Equals(const key_type& a, const key_type& b) { return a == b; }
+
+  template <typename K>
+  using key_arg = key_type;
+};
+
+#if defined(__cpp_lib_string_view)
+// If std::string_view is available, we add transparent support for std::string
+// keys. We use std::hash<std::string_view> as it supports the input types we
+// care about. The lookup functions accept arbitrary `K`. This will include any
+// key type that is convertible to std::string_view.
+template <>
+struct TransparentSupport<std::string> {
+  static std::string_view ImplicitConvert(std::string_view str) { return str; }
+  // If the element is not convertible to std::string_view, try to convert to
+  // std::string first.
+  // The template makes this overload lose resolution when both have the same
+  // rank otherwise.
+  template <typename = void>
+  static std::string_view ImplicitConvert(const std::string& str) {
+    return str;
+  }
+
+  struct hash : private std::hash<std::string_view> {
+    using is_transparent = void;
+
+    template <typename T>
+    size_t operator()(const T& str) const {
+      return base()(ImplicitConvert(str));
+    }
+
+   private:
+    const std::hash<std::string_view>& base() const { return *this; }
+  };
+  struct less {
+    using is_transparent = void;
+
+    template <typename T, typename U>
+    bool operator()(const T& t, const U& u) const {
+      return ImplicitConvert(t) < ImplicitConvert(u);
+    }
+  };
+
+  template <typename T, typename U>
+  static bool Equals(const T& t, const U& u) {
+    return ImplicitConvert(t) == ImplicitConvert(u);
+  }
+
+  template <typename K>
+  using key_arg = K;
+};
+#endif  // defined(__cpp_lib_string_view)
+
+template <typename Key>
+using TreeForMap =
+    std::map<KeyForTree<Key>, void*, typename TransparentSupport<Key>::less,
+             MapAllocator<std::pair<const KeyForTree<Key>, void*>>>;
+
+inline bool TableEntryIsEmpty(void* const* table, size_t b) {
+  return table[b] == nullptr;
+}
+inline bool TableEntryIsNonEmptyList(void* const* table, size_t b) {
+  return table[b] != nullptr && table[b] != table[b ^ 1];
+}
+inline bool TableEntryIsTree(void* const* table, size_t b) {
+  return !TableEntryIsEmpty(table, b) && !TableEntryIsNonEmptyList(table, b);
+}
+inline bool TableEntryIsList(void* const* table, size_t b) {
+  return !TableEntryIsTree(table, b);
+}
+
+// This captures all numeric types.
+inline size_t MapValueSpaceUsedExcludingSelfLong(bool) { return 0; }
+inline size_t MapValueSpaceUsedExcludingSelfLong(const std::string& str) {
+  return StringSpaceUsedExcludingSelfLong(str);
+}
+template <typename T,
+          typename = decltype(std::declval<const T&>().SpaceUsedLong())>
+size_t MapValueSpaceUsedExcludingSelfLong(const T& message) {
+  return message.SpaceUsedLong() - sizeof(T);
+}
+
+constexpr size_t kGlobalEmptyTableSize = 1;
+PROTOBUF_EXPORT extern void* const kGlobalEmptyTable[kGlobalEmptyTableSize];
+
+// Space used for the table, trees, and nodes.
+// Does not include the indirect space used. Eg the data of a std::string.
+template <typename Key>
+PROTOBUF_NOINLINE size_t SpaceUsedInTable(void** table, size_t num_buckets,
+                                          size_t num_elements,
+                                          size_t sizeof_node) {
+  size_t size = 0;
+  // The size of the table.
+  size += sizeof(void*) * num_buckets;
+  // All the nodes.
+  size += sizeof_node * num_elements;
+  // For each tree, count the overhead of the those nodes.
+  // Two buckets at a time because we only care about trees.
+  for (size_t b = 0; b < num_buckets; b += 2) {
+    if (internal::TableEntryIsTree(table, b)) {
+      using Tree = TreeForMap<Key>;
+      Tree* tree = static_cast<Tree*>(table[b]);
+      // Estimated cost of the red-black tree nodes, 3 pointers plus a
+      // bool (plus alignment, so 4 pointers).
+      size += tree->size() *
+              (sizeof(typename Tree::value_type) + sizeof(void*) * 4);
+    }
+  }
+  return size;
+}
+
+template <typename Map,
+          typename = typename std::enable_if<
+              !std::is_scalar<typename Map::key_type>::value ||
+              !std::is_scalar<typename Map::mapped_type>::value>::type>
+size_t SpaceUsedInValues(const Map* map) {
+  size_t size = 0;
+  for (const auto& v : *map) {
+    size += internal::MapValueSpaceUsedExcludingSelfLong(v.first) +
+            internal::MapValueSpaceUsedExcludingSelfLong(v.second);
+  }
+  return size;
+}
+
+inline size_t SpaceUsedInValues(const void*) { return 0; }
+
+}  // namespace internal
+
+// This is the class for Map's internal value_type. Instead of using
+// std::pair as value_type, we use this class which provides us more control of
+// its process of construction and destruction.
+template <typename Key, typename T>
+struct PROTOBUF_ATTRIBUTE_STANDALONE_DEBUG MapPair {
+  using first_type = const Key;
+  using second_type = T;
+
+  MapPair(const Key& other_first, const T& other_second)
+      : first(other_first), second(other_second) {}
+  explicit MapPair(const Key& other_first) : first(other_first), second() {}
+  explicit MapPair(Key&& other_first)
+      : first(std::move(other_first)), second() {}
+  MapPair(const MapPair& other) : first(other.first), second(other.second) {}
+
+  ~MapPair() {}
+
+  // Implicitly convertible to std::pair of compatible types.
+  template <typename T1, typename T2>
+  operator std::pair<T1, T2>() const {  // NOLINT(runtime/explicit)
+    return std::pair<T1, T2>(first, second);
+  }
+
+  const Key first;
+  T second;
+
+ private:
+  friend class Arena;
+  friend class Map<Key, T>;
+};
+
+// Map is an associative container type used to store protobuf map
+// fields.  Each Map instance may or may not use a different hash function, a
+// different iteration order, and so on.  E.g., please don't examine
+// implementation details to decide if the following would work:
+//  Map<int, int> m0, m1;
+//  m0[0] = m1[0] = m0[1] = m1[1] = 0;
+//  assert(m0.begin()->first == m1.begin()->first);  // Bug!
+//
+// Map's interface is similar to std::unordered_map, except that Map is not
+// designed to play well with exceptions.
+template <typename Key, typename T>
+class Map {
+ public:
+  using key_type = Key;
+  using mapped_type = T;
+  using value_type = MapPair<Key, T>;
+
+  using pointer = value_type*;
+  using const_pointer = const value_type*;
+  using reference = value_type&;
+  using const_reference = const value_type&;
+
+  using size_type = size_t;
+  using hasher = typename internal::TransparentSupport<Key>::hash;
+
+  constexpr Map() : elements_(nullptr) {}
+  explicit Map(Arena* arena) : elements_(arena) {}
+
+  Map(const Map& other) : Map() { insert(other.begin(), other.end()); }
+
+  Map(Map&& other) noexcept : Map() {
+    if (other.arena() != nullptr) {
+      *this = other;
+    } else {
+      swap(other);
+    }
+  }
+
+  Map& operator=(Map&& other) noexcept {
+    if (this != &other) {
+      if (arena() != other.arena()) {
+        *this = other;
+      } else {
+        swap(other);
+      }
+    }
+    return *this;
+  }
+
+  template <class InputIt>
+  Map(const InputIt& first, const InputIt& last) : Map() {
+    insert(first, last);
+  }
+
+  ~Map() {}
+
+ private:
+  using Allocator = internal::MapAllocator<void*>;
+
+  // InnerMap is a generic hash-based map.  It doesn't contain any
+  // protocol-buffer-specific logic.  It is a chaining hash map with the
+  // additional feature that some buckets can be converted to use an ordered
+  // container.  This ensures O(lg n) bounds on find, insert, and erase, while
+  // avoiding the overheads of ordered containers most of the time.
+  //
+  // The implementation doesn't need the full generality of unordered_map,
+  // and it doesn't have it.  More bells and whistles can be added as needed.
+  // Some implementation details:
+  // 1. The hash function has type hasher and the equality function
+  //    equal_to<Key>.  We inherit from hasher to save space
+  //    (empty-base-class optimization).
+  // 2. The number of buckets is a power of two.
+  // 3. Buckets are converted to trees in pairs: if we convert bucket b then
+  //    buckets b and b^1 will share a tree.  Invariant: buckets b and b^1 have
+  //    the same non-null value iff they are sharing a tree.  (An alternative
+  //    implementation strategy would be to have a tag bit per bucket.)
+  // 4. As is typical for hash_map and such, the Keys and Values are always
+  //    stored in linked list nodes.  Pointers to elements are never invalidated
+  //    until the element is deleted.
+  // 5. The trees' payload type is pointer to linked-list node.  Tree-converting
+  //    a bucket doesn't copy Key-Value pairs.
+  // 6. Once we've tree-converted a bucket, it is never converted back. However,
+  //    the items a tree contains may wind up assigned to trees or lists upon a
+  //    rehash.
+  // 7. The code requires no C++ features from C++14 or later.
+  // 8. Mutations to a map do not invalidate the map's iterators, pointers to
+  //    elements, or references to elements.
+  // 9. Except for erase(iterator), any non-const method can reorder iterators.
+  // 10. InnerMap uses KeyForTree<Key> when using the Tree representation, which
+  //    is either `Key`, if Key is a scalar, or `reference_wrapper<const Key>`
+  //    otherwise. This avoids unnecessary copies of string keys, for example.
+  class InnerMap : private hasher {
+   public:
+    explicit constexpr InnerMap(Arena* arena)
+        : hasher(),
+          num_elements_(0),
+          num_buckets_(internal::kGlobalEmptyTableSize),
+          seed_(0),
+          index_of_first_non_null_(internal::kGlobalEmptyTableSize),
+          table_(const_cast<void**>(internal::kGlobalEmptyTable)),
+          alloc_(arena) {}
+
+    ~InnerMap() {
+      if (alloc_.arena() == nullptr &&
+          num_buckets_ != internal::kGlobalEmptyTableSize) {
+        clear();
+        Dealloc<void*>(table_, num_buckets_);
+      }
+    }
+
+   private:
+    enum { kMinTableSize = 8 };
+
+    // Linked-list nodes, as one would expect for a chaining hash table.
+    struct Node {
+      value_type kv;
+      Node* next;
+    };
+
+    // Trees. The payload type is a copy of Key, so that we can query the tree
+    // with Keys that are not in any particular data structure.
+    // The value is a void* pointing to Node. We use void* instead of Node* to
+    // avoid code bloat. That way there is only one instantiation of the tree
+    // class per key type.
+    using Tree = internal::TreeForMap<Key>;
+    using TreeIterator = typename Tree::iterator;
+
+    static Node* NodeFromTreeIterator(TreeIterator it) {
+      return static_cast<Node*>(it->second);
+    }
+
+    // iterator and const_iterator are instantiations of iterator_base.
+    template <typename KeyValueType>
+    class iterator_base {
+     public:
+      using reference = KeyValueType&;
+      using pointer = KeyValueType*;
+
+      // Invariants:
+      // node_ is always correct. This is handy because the most common
+      // operations are operator* and operator-> and they only use node_.
+      // When node_ is set to a non-null value, all the other non-const fields
+      // are updated to be correct also, but those fields can become stale
+      // if the underlying map is modified.  When those fields are needed they
+      // are rechecked, and updated if necessary.
+      iterator_base() : node_(nullptr), m_(nullptr), bucket_index_(0) {}
+
+      explicit iterator_base(const InnerMap* m) : m_(m) {
+        SearchFrom(m->index_of_first_non_null_);
+      }
+
+      // Any iterator_base can convert to any other.  This is overkill, and we
+      // rely on the enclosing class to use it wisely.  The standard "iterator
+      // can convert to const_iterator" is OK but the reverse direction is not.
+      template <typename U>
+      explicit iterator_base(const iterator_base<U>& it)
+          : node_(it.node_), m_(it.m_), bucket_index_(it.bucket_index_) {}
+
+      iterator_base(Node* n, const InnerMap* m, size_type index)
+          : node_(n), m_(m), bucket_index_(index) {}
+
+      iterator_base(TreeIterator tree_it, const InnerMap* m, size_type index)
+          : node_(NodeFromTreeIterator(tree_it)), m_(m), bucket_index_(index) {
+        // Invariant: iterators that use buckets with trees have an even
+        // bucket_index_.
+        GOOGLE_DCHECK_EQ(bucket_index_ % 2, 0u);
+      }
+
+      // Advance through buckets, looking for the first that isn't empty.
+      // If nothing non-empty is found then leave node_ == nullptr.
+      void SearchFrom(size_type start_bucket) {
+        GOOGLE_DCHECK(m_->index_of_first_non_null_ == m_->num_buckets_ ||
+               m_->table_[m_->index_of_first_non_null_] != nullptr);
+        node_ = nullptr;
+        for (bucket_index_ = start_bucket; bucket_index_ < m_->num_buckets_;
+             bucket_index_++) {
+          if (m_->TableEntryIsNonEmptyList(bucket_index_)) {
+            node_ = static_cast<Node*>(m_->table_[bucket_index_]);
+            break;
+          } else if (m_->TableEntryIsTree(bucket_index_)) {
+            Tree* tree = static_cast<Tree*>(m_->table_[bucket_index_]);
+            GOOGLE_DCHECK(!tree->empty());
+            node_ = NodeFromTreeIterator(tree->begin());
+            break;
+          }
+        }
+      }
+
+      reference operator*() const { return node_->kv; }
+      pointer operator->() const { return &(operator*()); }
+
+      friend bool operator==(const iterator_base& a, const iterator_base& b) {
+        return a.node_ == b.node_;
+      }
+      friend bool operator!=(const iterator_base& a, const iterator_base& b) {
+        return a.node_ != b.node_;
+      }
+
+      iterator_base& operator++() {
+        if (node_->next == nullptr) {
+          TreeIterator tree_it;
+          const bool is_list = revalidate_if_necessary(&tree_it);
+          if (is_list) {
+            SearchFrom(bucket_index_ + 1);
+          } else {
+            GOOGLE_DCHECK_EQ(bucket_index_ & 1, 0u);
+            Tree* tree = static_cast<Tree*>(m_->table_[bucket_index_]);
+            if (++tree_it == tree->end()) {
+              SearchFrom(bucket_index_ + 2);
+            } else {
+              node_ = NodeFromTreeIterator(tree_it);
+            }
+          }
+        } else {
+          node_ = node_->next;
+        }
+        return *this;
+      }
+
+      iterator_base operator++(int /* unused */) {
+        iterator_base tmp = *this;
+        ++*this;
+        return tmp;
+      }
+
+      // Assumes node_ and m_ are correct and non-null, but other fields may be
+      // stale.  Fix them as needed.  Then return true iff node_ points to a
+      // Node in a list.  If false is returned then *it is modified to be
+      // a valid iterator for node_.
+      bool revalidate_if_necessary(TreeIterator* it) {
+        GOOGLE_DCHECK(node_ != nullptr && m_ != nullptr);
+        // Force bucket_index_ to be in range.
+        bucket_index_ &= (m_->num_buckets_ - 1);
+        // Common case: the bucket we think is relevant points to node_.
+        if (m_->table_[bucket_index_] == static_cast<void*>(node_)) return true;
+        // Less common: the bucket is a linked list with node_ somewhere in it,
+        // but not at the head.
+        if (m_->TableEntryIsNonEmptyList(bucket_index_)) {
+          Node* l = static_cast<Node*>(m_->table_[bucket_index_]);
+          while ((l = l->next) != nullptr) {
+            if (l == node_) {
+              return true;
+            }
+          }
+        }
+        // Well, bucket_index_ still might be correct, but probably
+        // not.  Revalidate just to be sure.  This case is rare enough that we
+        // don't worry about potential optimizations, such as having a custom
+        // find-like method that compares Node* instead of the key.
+        iterator_base i(m_->find(node_->kv.first, it));
+        bucket_index_ = i.bucket_index_;
+        return m_->TableEntryIsList(bucket_index_);
+      }
+
+      Node* node_;
+      const InnerMap* m_;
+      size_type bucket_index_;
+    };
+
+   public:
+    using iterator = iterator_base<value_type>;
+    using const_iterator = iterator_base<const value_type>;
+
+    Arena* arena() const { return alloc_.arena(); }
+
+    void Swap(InnerMap* other) {
+      std::swap(num_elements_, other->num_elements_);
+      std::swap(num_buckets_, other->num_buckets_);
+      std::swap(seed_, other->seed_);
+      std::swap(index_of_first_non_null_, other->index_of_first_non_null_);
+      std::swap(table_, other->table_);
+      std::swap(alloc_, other->alloc_);
+    }
+
+    iterator begin() { return iterator(this); }
+    iterator end() { return iterator(); }
+    const_iterator begin() const { return const_iterator(this); }
+    const_iterator end() const { return const_iterator(); }
+
+    void clear() {
+      for (size_type b = 0; b < num_buckets_; b++) {
+        if (TableEntryIsNonEmptyList(b)) {
+          Node* node = static_cast<Node*>(table_[b]);
+          table_[b] = nullptr;
+          do {
+            Node* next = node->next;
+            DestroyNode(node);
+            node = next;
+          } while (node != nullptr);
+        } else if (TableEntryIsTree(b)) {
+          Tree* tree = static_cast<Tree*>(table_[b]);
+          GOOGLE_DCHECK(table_[b] == table_[b + 1] && (b & 1) == 0);
+          table_[b] = table_[b + 1] = nullptr;
+          typename Tree::iterator tree_it = tree->begin();
+          do {
+            Node* node = NodeFromTreeIterator(tree_it);
+            typename Tree::iterator next = tree_it;
+            ++next;
+            tree->erase(tree_it);
+            DestroyNode(node);
+            tree_it = next;
+          } while (tree_it != tree->end());
+          DestroyTree(tree);
+          b++;
+        }
+      }
+      num_elements_ = 0;
+      index_of_first_non_null_ = num_buckets_;
+    }
+
+    const hasher& hash_function() const { return *this; }
+
+    static size_type max_size() {
+      return static_cast<size_type>(1) << (sizeof(void**) >= 8 ? 60 : 28);
+    }
+    size_type size() const { return num_elements_; }
+    bool empty() const { return size() == 0; }
+
+    template <typename K>
+    iterator find(const K& k) {
+      return iterator(FindHelper(k).first);
+    }
+
+    template <typename K>
+    const_iterator find(const K& k) const {
+      return FindHelper(k).first;
+    }
+
+    // Inserts a new element into the container if there is no element with the
+    // key in the container.
+    // The new element is:
+    //  (1) Constructed in-place with the given args, if mapped_type is not
+    //      arena constructible.
+    //  (2) Constructed in-place with the arena and then assigned with a
+    //      mapped_type temporary constructed with the given args, otherwise.
+    template <typename K, typename... Args>
+    std::pair<iterator, bool> try_emplace(K&& k, Args&&... args) {
+      return ArenaAwareTryEmplace(Arena::is_arena_constructable<mapped_type>(),
+                                  std::forward<K>(k),
+                                  std::forward<Args>(args)...);
+    }
+
+    // Inserts the key into the map, if not present. In that case, the value
+    // will be value initialized.
+    template <typename K>
+    std::pair<iterator, bool> insert(K&& k) {
+      return try_emplace(std::forward<K>(k));
+    }
+
+    template <typename K>
+    value_type& operator[](K&& k) {
+      return *try_emplace(std::forward<K>(k)).first;
+    }
+
+    void erase(iterator it) {
+      GOOGLE_DCHECK_EQ(it.m_, this);
+      typename Tree::iterator tree_it;
+      const bool is_list = it.revalidate_if_necessary(&tree_it);
+      size_type b = it.bucket_index_;
+      Node* const item = it.node_;
+      if (is_list) {
+        GOOGLE_DCHECK(TableEntryIsNonEmptyList(b));
+        Node* head = static_cast<Node*>(table_[b]);
+        head = EraseFromLinkedList(item, head);
+        table_[b] = static_cast<void*>(head);
+      } else {
+        GOOGLE_DCHECK(TableEntryIsTree(b));
+        Tree* tree = static_cast<Tree*>(table_[b]);
+        tree->erase(tree_it);
+        if (tree->empty()) {
+          // Force b to be the minimum of b and b ^ 1.  This is important
+          // only because we want index_of_first_non_null_ to be correct.
+          b &= ~static_cast<size_type>(1);
+          DestroyTree(tree);
+          table_[b] = table_[b + 1] = nullptr;
+        }
+      }
+      DestroyNode(item);
+      --num_elements_;
+      if (PROTOBUF_PREDICT_FALSE(b == index_of_first_non_null_)) {
+        while (index_of_first_non_null_ < num_buckets_ &&
+               table_[index_of_first_non_null_] == nullptr) {
+          ++index_of_first_non_null_;
+        }
+      }
+    }
+
+    size_t SpaceUsedInternal() const {
+      return internal::SpaceUsedInTable<Key>(table_, num_buckets_,
+                                             num_elements_, sizeof(Node));
+    }
+
+   private:
+    template <typename K, typename... Args>
+    std::pair<iterator, bool> TryEmplaceInternal(K&& k, Args&&... args) {
+      std::pair<const_iterator, size_type> p = FindHelper(k);
+      // Case 1: key was already present.
+      if (p.first.node_ != nullptr)
+        return std::make_pair(iterator(p.first), false);
+      // Case 2: insert.
+      if (ResizeIfLoadIsOutOfRange(num_elements_ + 1)) {
+        p = FindHelper(k);
+      }
+      const size_type b = p.second;  // bucket number
+      // If K is not key_type, make the conversion to key_type explicit.
+      using TypeToInit = typename std::conditional<
+          std::is_same<typename std::decay<K>::type, key_type>::value, K&&,
+          key_type>::type;
+      Node* node = Alloc<Node>(1);
+      // Even when arena is nullptr, CreateInArenaStorage is still used to
+      // ensure the arena of submessage will be consistent. Otherwise,
+      // submessage may have its own arena when message-owned arena is enabled.
+      // Note: This only works if `Key` is not arena constructible.
+      Arena::CreateInArenaStorage(const_cast<Key*>(&node->kv.first),
+                                  alloc_.arena(),
+                                  static_cast<TypeToInit>(std::forward<K>(k)));
+      // Note: if `T` is arena constructible, `Args` needs to be empty.
+      Arena::CreateInArenaStorage(&node->kv.second, alloc_.arena(),
+                                  std::forward<Args>(args)...);
+
+      iterator result = InsertUnique(b, node);
+      ++num_elements_;
+      return std::make_pair(result, true);
+    }
+
+    // A helper function to perform an assignment of `mapped_type`.
+    // If the first argument is true, then it is a regular assignment.
+    // Otherwise, we first create a temporary and then perform an assignment.
+    template <typename V>
+    static void AssignMapped(std::true_type, mapped_type& mapped, V&& v) {
+      mapped = std::forward<V>(v);
+    }
+    template <typename... Args>
+    static void AssignMapped(std::false_type, mapped_type& mapped,
+                             Args&&... args) {
+      mapped = mapped_type(std::forward<Args>(args)...);
+    }
+
+    // Case 1: `mapped_type` is arena constructible. A temporary object is
+    // created and then (if `Args` are not empty) assigned to a mapped value
+    // that was created with the arena.
+    template <typename K>
+    std::pair<iterator, bool> ArenaAwareTryEmplace(std::true_type, K&& k) {
+      // case 1.1: "default" constructed (e.g. from arena only).
+      return TryEmplaceInternal(std::forward<K>(k));
+    }
+    template <typename K, typename... Args>
+    std::pair<iterator, bool> ArenaAwareTryEmplace(std::true_type, K&& k,
+                                                   Args&&... args) {
+      // case 1.2: "default" constructed + copy/move assignment
+      auto p = TryEmplaceInternal(std::forward<K>(k));
+      if (p.second) {
+        AssignMapped(std::is_same<void(typename std::decay<Args>::type...),
+                                  void(mapped_type)>(),
+                     p.first->second, std::forward<Args>(args)...);
+      }
+      return p;
+    }
+    // Case 2: `mapped_type` is not arena constructible. Using in-place
+    // construction.
+    template <typename... Args>
+    std::pair<iterator, bool> ArenaAwareTryEmplace(std::false_type,
+                                                   Args&&... args) {
+      return TryEmplaceInternal(std::forward<Args>(args)...);
+    }
+
+    const_iterator find(const Key& k, TreeIterator* it) const {
+      return FindHelper(k, it).first;
+    }
+    template <typename K>
+    std::pair<const_iterator, size_type> FindHelper(const K& k) const {
+      return FindHelper(k, nullptr);
+    }
+    template <typename K>
+    std::pair<const_iterator, size_type> FindHelper(const K& k,
+                                                    TreeIterator* it) const {
+      size_type b = BucketNumber(k);
+      if (TableEntryIsNonEmptyList(b)) {
+        Node* node = static_cast<Node*>(table_[b]);
+        do {
+          if (internal::TransparentSupport<Key>::Equals(node->kv.first, k)) {
+            return std::make_pair(const_iterator(node, this, b), b);
+          } else {
+            node = node->next;
+          }
+        } while (node != nullptr);
+      } else if (TableEntryIsTree(b)) {
+        GOOGLE_DCHECK_EQ(table_[b], table_[b ^ 1]);
+        b &= ~static_cast<size_t>(1);
+        Tree* tree = static_cast<Tree*>(table_[b]);
+        auto tree_it = tree->find(k);
+        if (tree_it != tree->end()) {
+          if (it != nullptr) *it = tree_it;
+          return std::make_pair(const_iterator(tree_it, this, b), b);
+        }
+      }
+      return std::make_pair(end(), b);
+    }
+
+    // Insert the given Node in bucket b.  If that would make bucket b too big,
+    // and bucket b is not a tree, create a tree for buckets b and b^1 to share.
+    // Requires count(*KeyPtrFromNodePtr(node)) == 0 and that b is the correct
+    // bucket.  num_elements_ is not modified.
+    iterator InsertUnique(size_type b, Node* node) {
+      GOOGLE_DCHECK(index_of_first_non_null_ == num_buckets_ ||
+             table_[index_of_first_non_null_] != nullptr);
+      // In practice, the code that led to this point may have already
+      // determined whether we are inserting into an empty list, a short list,
+      // or whatever.  But it's probably cheap enough to recompute that here;
+      // it's likely that we're inserting into an empty or short list.
+      iterator result;
+      GOOGLE_DCHECK(find(node->kv.first) == end());
+      if (TableEntryIsEmpty(b)) {
+        result = InsertUniqueInList(b, node);
+      } else if (TableEntryIsNonEmptyList(b)) {
+        if (PROTOBUF_PREDICT_FALSE(TableEntryIsTooLong(b))) {
+          TreeConvert(b);
+          result = InsertUniqueInTree(b, node);
+          GOOGLE_DCHECK_EQ(result.bucket_index_, b & ~static_cast<size_type>(1));
+        } else {
+          // Insert into a pre-existing list.  This case cannot modify
+          // index_of_first_non_null_, so we skip the code to update it.
+          return InsertUniqueInList(b, node);
+        }
+      } else {
+        // Insert into a pre-existing tree.  This case cannot modify
+        // index_of_first_non_null_, so we skip the code to update it.
+        return InsertUniqueInTree(b, node);
+      }
+      // parentheses around (std::min) prevents macro expansion of min(...)
+      index_of_first_non_null_ =
+          (std::min)(index_of_first_non_null_, result.bucket_index_);
+      return result;
+    }
+
+    // Returns whether we should insert after the head of the list. For
+    // non-optimized builds, we randomly decide whether to insert right at the
+    // head of the list or just after the head. This helps add a little bit of
+    // non-determinism to the map ordering.
+    bool ShouldInsertAfterHead(void* node) {
+#ifdef NDEBUG
+      (void)node;
+      return false;
+#else
+      // Doing modulo with a prime mixes the bits more.
+      return (reinterpret_cast<uintptr_t>(node) ^ seed_) % 13 > 6;
+#endif
+    }
+
+    // Helper for InsertUnique.  Handles the case where bucket b is a
+    // not-too-long linked list.
+    iterator InsertUniqueInList(size_type b, Node* node) {
+      if (table_[b] != nullptr && ShouldInsertAfterHead(node)) {
+        Node* first = static_cast<Node*>(table_[b]);
+        node->next = first->next;
+        first->next = node;
+        return iterator(node, this, b);
+      }
+
+      node->next = static_cast<Node*>(table_[b]);
+      table_[b] = static_cast<void*>(node);
+      return iterator(node, this, b);
+    }
+
+    // Helper for InsertUnique.  Handles the case where bucket b points to a
+    // Tree.
+    iterator InsertUniqueInTree(size_type b, Node* node) {
+      GOOGLE_DCHECK_EQ(table_[b], table_[b ^ 1]);
+      // Maintain the invariant that node->next is null for all Nodes in Trees.
+      node->next = nullptr;
+      return iterator(
+          static_cast<Tree*>(table_[b])->insert({node->kv.first, node}).first,
+          this, b & ~static_cast<size_t>(1));
+    }
+
+    // Returns whether it did resize.  Currently this is only used when
+    // num_elements_ increases, though it could be used in other situations.
+    // It checks for load too low as well as load too high: because any number
+    // of erases can occur between inserts, the load could be as low as 0 here.
+    // Resizing to a lower size is not always helpful, but failing to do so can
+    // destroy the expected big-O bounds for some operations. By having the
+    // policy that sometimes we resize down as well as up, clients can easily
+    // keep O(size()) = O(number of buckets) if they want that.
+    bool ResizeIfLoadIsOutOfRange(size_type new_size) {
+      const size_type kMaxMapLoadTimes16 = 12;  // controls RAM vs CPU tradeoff
+      const size_type hi_cutoff = num_buckets_ * kMaxMapLoadTimes16 / 16;
+      const size_type lo_cutoff = hi_cutoff / 4;
+      // We don't care how many elements are in trees.  If a lot are,
+      // we may resize even though there are many empty buckets.  In
+      // practice, this seems fine.
+      if (PROTOBUF_PREDICT_FALSE(new_size >= hi_cutoff)) {
+        if (num_buckets_ <= max_size() / 2) {
+          Resize(num_buckets_ * 2);
+          return true;
+        }
+      } else if (PROTOBUF_PREDICT_FALSE(new_size <= lo_cutoff &&
+                                        num_buckets_ > kMinTableSize)) {
+        size_type lg2_of_size_reduction_factor = 1;
+        // It's possible we want to shrink a lot here... size() could even be 0.
+        // So, estimate how much to shrink by making sure we don't shrink so
+        // much that we would need to grow the table after a few inserts.
+        const size_type hypothetical_size = new_size * 5 / 4 + 1;
+        while ((hypothetical_size << lg2_of_size_reduction_factor) <
+               hi_cutoff) {
+          ++lg2_of_size_reduction_factor;
+        }
+        size_type new_num_buckets = std::max<size_type>(
+            kMinTableSize, num_buckets_ >> lg2_of_size_reduction_factor);
+        if (new_num_buckets != num_buckets_) {
+          Resize(new_num_buckets);
+          return true;
+        }
+      }
+      return false;
+    }
+
+    // Resize to the given number of buckets.
+    void Resize(size_t new_num_buckets) {
+      if (num_buckets_ == internal::kGlobalEmptyTableSize) {
+        // This is the global empty array.
+        // Just overwrite with a new one. No need to transfer or free anything.
+        num_buckets_ = index_of_first_non_null_ = kMinTableSize;
+        table_ = CreateEmptyTable(num_buckets_);
+        seed_ = Seed();
+        return;
+      }
+
+      GOOGLE_DCHECK_GE(new_num_buckets, kMinTableSize);
+      void** const old_table = table_;
+      const size_type old_table_size = num_buckets_;
+      num_buckets_ = new_num_buckets;
+      table_ = CreateEmptyTable(num_buckets_);
+      const size_type start = index_of_first_non_null_;
+      index_of_first_non_null_ = num_buckets_;
+      for (size_type i = start; i < old_table_size; i++) {
+        if (internal::TableEntryIsNonEmptyList(old_table, i)) {
+          TransferList(old_table, i);
+        } else if (internal::TableEntryIsTree(old_table, i)) {
+          TransferTree(old_table, i++);
+        }
+      }
+      Dealloc<void*>(old_table, old_table_size);
+    }
+
+    void TransferList(void* const* table, size_type index) {
+      Node* node = static_cast<Node*>(table[index]);
+      do {
+        Node* next = node->next;
+        InsertUnique(BucketNumber(node->kv.first), node);
+        node = next;
+      } while (node != nullptr);
+    }
+
+    void TransferTree(void* const* table, size_type index) {
+      Tree* tree = static_cast<Tree*>(table[index]);
+      typename Tree::iterator tree_it = tree->begin();
+      do {
+        InsertUnique(BucketNumber(std::cref(tree_it->first).get()),
+                     NodeFromTreeIterator(tree_it));
+      } while (++tree_it != tree->end());
+      DestroyTree(tree);
+    }
+
+    Node* EraseFromLinkedList(Node* item, Node* head) {
+      if (head == item) {
+        return head->next;
+      } else {
+        head->next = EraseFromLinkedList(item, head->next);
+        return head;
+      }
+    }
+
+    bool TableEntryIsEmpty(size_type b) const {
+      return internal::TableEntryIsEmpty(table_, b);
+    }
+    bool TableEntryIsNonEmptyList(size_type b) const {
+      return internal::TableEntryIsNonEmptyList(table_, b);
+    }
+    bool TableEntryIsTree(size_type b) const {
+      return internal::TableEntryIsTree(table_, b);
+    }
+    bool TableEntryIsList(size_type b) const {
+      return internal::TableEntryIsList(table_, b);
+    }
+
+    void TreeConvert(size_type b) {
+      GOOGLE_DCHECK(!TableEntryIsTree(b) && !TableEntryIsTree(b ^ 1));
+      Tree* tree =
+          Arena::Create<Tree>(alloc_.arena(), typename Tree::key_compare(),
+                              typename Tree::allocator_type(alloc_));
+      size_type count = CopyListToTree(b, tree) + CopyListToTree(b ^ 1, tree);
+      GOOGLE_DCHECK_EQ(count, tree->size());
+      table_[b] = table_[b ^ 1] = static_cast<void*>(tree);
+    }
+
+    // Copy a linked list in the given bucket to a tree.
+    // Returns the number of things it copied.
+    size_type CopyListToTree(size_type b, Tree* tree) {
+      size_type count = 0;
+      Node* node = static_cast<Node*>(table_[b]);
+      while (node != nullptr) {
+        tree->insert({node->kv.first, node});
+        ++count;
+        Node* next = node->next;
+        node->next = nullptr;
+        node = next;
+      }
+      return count;
+    }
+
+    // Return whether table_[b] is a linked list that seems awfully long.
+    // Requires table_[b] to point to a non-empty linked list.
+    bool TableEntryIsTooLong(size_type b) {
+      const size_type kMaxLength = 8;
+      size_type count = 0;
+      Node* node = static_cast<Node*>(table_[b]);
+      do {
+        ++count;
+        node = node->next;
+      } while (node != nullptr);
+      // Invariant: no linked list ever is more than kMaxLength in length.
+      GOOGLE_DCHECK_LE(count, kMaxLength);
+      return count >= kMaxLength;
+    }
+
+    template <typename K>
+    size_type BucketNumber(const K& k) const {
+      // We xor the hash value against the random seed so that we effectively
+      // have a random hash function.
+      uint64_t h = hash_function()(k) ^ seed_;
+
+      // We use the multiplication method to determine the bucket number from
+      // the hash value. The constant kPhi (suggested by Knuth) is roughly
+      // (sqrt(5) - 1) / 2 * 2^64.
+      constexpr uint64_t kPhi = uint64_t{0x9e3779b97f4a7c15};
+      return ((kPhi * h) >> 32) & (num_buckets_ - 1);
+    }
+
+    // Return a power of two no less than max(kMinTableSize, n).
+    // Assumes either n < kMinTableSize or n is a power of two.
+    size_type TableSize(size_type n) {
+      return n < static_cast<size_type>(kMinTableSize)
+                 ? static_cast<size_type>(kMinTableSize)
+                 : n;
+    }
+
+    // Use alloc_ to allocate an array of n objects of type U.
+    template <typename U>
+    U* Alloc(size_type n) {
+      using alloc_type = typename Allocator::template rebind<U>::other;
+      return alloc_type(alloc_).allocate(n);
+    }
+
+    // Use alloc_ to deallocate an array of n objects of type U.
+    template <typename U>
+    void Dealloc(U* t, size_type n) {
+      using alloc_type = typename Allocator::template rebind<U>::other;
+      alloc_type(alloc_).deallocate(t, n);
+    }
+
+    void DestroyNode(Node* node) {
+      if (alloc_.arena() == nullptr) {
+        delete node;
+      }
+    }
+
+    void DestroyTree(Tree* tree) {
+      if (alloc_.arena() == nullptr) {
+        delete tree;
+      }
+    }
+
+    void** CreateEmptyTable(size_type n) {
+      GOOGLE_DCHECK(n >= kMinTableSize);
+      GOOGLE_DCHECK_EQ(n & (n - 1), 0u);
+      void** result = Alloc<void*>(n);
+      memset(result, 0, n * sizeof(result[0]));
+      return result;
+    }
+
+    // Return a randomish value.
+    size_type Seed() const {
+      // We get a little bit of randomness from the address of the map. The
+      // lower bits are not very random, due to alignment, so we discard them
+      // and shift the higher bits into their place.
+      size_type s = reinterpret_cast<uintptr_t>(this) >> 4;
+#if !defined(GOOGLE_PROTOBUF_NO_RDTSC)
+#if defined(__APPLE__)
+      // Use a commpage-based fast time function on Apple environments (MacOS,
+      // iOS, tvOS, watchOS, etc).
+      s += mach_absolute_time();
+#elif defined(__x86_64__) && defined(__GNUC__)
+      uint32_t hi, lo;
+      asm volatile("rdtsc" : "=a"(lo), "=d"(hi));
+      s += ((static_cast<uint64_t>(hi) << 32) | lo);
+#elif defined(__aarch64__) && defined(__GNUC__)
+      // There is no rdtsc on ARMv8. CNTVCT_EL0 is the virtual counter of the
+      // system timer. It runs at a different frequency than the CPU's, but is
+      // the best source of time-based entropy we get.
+      uint64_t virtual_timer_value;
+      asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
+      s += virtual_timer_value;
+#endif
+#endif  // !defined(GOOGLE_PROTOBUF_NO_RDTSC)
+      return s;
+    }
+
+    friend class Arena;
+    using InternalArenaConstructable_ = void;
+    using DestructorSkippable_ = void;
+
+    size_type num_elements_;
+    size_type num_buckets_;
+    size_type seed_;
+    size_type index_of_first_non_null_;
+    void** table_;  // an array with num_buckets_ entries
+    Allocator alloc_;
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(InnerMap);
+  };  // end of class InnerMap
+
+  template <typename LookupKey>
+  using key_arg = typename internal::TransparentSupport<
+      key_type>::template key_arg<LookupKey>;
+
+ public:
+  // Iterators
+  class const_iterator {
+    using InnerIt = typename InnerMap::const_iterator;
+
+   public:
+    using iterator_category = std::forward_iterator_tag;
+    using value_type = typename Map::value_type;
+    using difference_type = ptrdiff_t;
+    using pointer = const value_type*;
+    using reference = const value_type&;
+
+    const_iterator() {}
+    explicit const_iterator(const InnerIt& it) : it_(it) {}
+
+    const_reference operator*() const { return *it_; }
+    const_pointer operator->() const { return &(operator*()); }
+
+    const_iterator& operator++() {
+      ++it_;
+      return *this;
+    }
+    const_iterator operator++(int) { return const_iterator(it_++); }
+
+    friend bool operator==(const const_iterator& a, const const_iterator& b) {
+      return a.it_ == b.it_;
+    }
+    friend bool operator!=(const const_iterator& a, const const_iterator& b) {
+      return !(a == b);
+    }
+
+   private:
+    InnerIt it_;
+  };
+
+  class iterator {
+    using InnerIt = typename InnerMap::iterator;
+
+   public:
+    using iterator_category = std::forward_iterator_tag;
+    using value_type = typename Map::value_type;
+    using difference_type = ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    iterator() {}
+    explicit iterator(const InnerIt& it) : it_(it) {}
+
+    reference operator*() const { return *it_; }
+    pointer operator->() const { return &(operator*()); }
+
+    iterator& operator++() {
+      ++it_;
+      return *this;
+    }
+    iterator operator++(int) { return iterator(it_++); }
+
+    // Allow implicit conversion to const_iterator.
+    operator const_iterator() const {  // NOLINT(runtime/explicit)
+      return const_iterator(typename InnerMap::const_iterator(it_));
+    }
+
+    friend bool operator==(const iterator& a, const iterator& b) {
+      return a.it_ == b.it_;
+    }
+    friend bool operator!=(const iterator& a, const iterator& b) {
+      return !(a == b);
+    }
+
+   private:
+    friend class Map;
+
+    InnerIt it_;
+  };
+
+  iterator begin() { return iterator(elements_.begin()); }
+  iterator end() { return iterator(elements_.end()); }
+  const_iterator begin() const { return const_iterator(elements_.begin()); }
+  const_iterator end() const { return const_iterator(elements_.end()); }
+  const_iterator cbegin() const { return begin(); }
+  const_iterator cend() const { return end(); }
+
+  // Capacity
+  size_type size() const { return elements_.size(); }
+  bool empty() const { return size() == 0; }
+
+  // Element access
+  template <typename K = key_type>
+  T& operator[](const key_arg<K>& key) {
+    return elements_[key].second;
+  }
+  template <
+      typename K = key_type,
+      // Disable for integral types to reduce code bloat.
+      typename = typename std::enable_if<!std::is_integral<K>::value>::type>
+  T& operator[](key_arg<K>&& key) {
+    return elements_[std::forward<K>(key)].second;
+  }
+
+  template <typename K = key_type>
+  const T& at(const key_arg<K>& key) const {
+    const_iterator it = find(key);
+    GOOGLE_CHECK(it != end()) << "key not found: " << static_cast<Key>(key);
+    return it->second;
+  }
+
+  template <typename K = key_type>
+  T& at(const key_arg<K>& key) {
+    iterator it = find(key);
+    GOOGLE_CHECK(it != end()) << "key not found: " << static_cast<Key>(key);
+    return it->second;
+  }
+
+  // Lookup
+  template <typename K = key_type>
+  size_type count(const key_arg<K>& key) const {
+    return find(key) == end() ? 0 : 1;
+  }
+
+  template <typename K = key_type>
+  const_iterator find(const key_arg<K>& key) const {
+    return const_iterator(elements_.find(key));
+  }
+  template <typename K = key_type>
+  iterator find(const key_arg<K>& key) {
+    return iterator(elements_.find(key));
+  }
+
+  template <typename K = key_type>
+  bool contains(const key_arg<K>& key) const {
+    return find(key) != end();
+  }
+
+  template <typename K = key_type>
+  std::pair<const_iterator, const_iterator> equal_range(
+      const key_arg<K>& key) const {
+    const_iterator it = find(key);
+    if (it == end()) {
+      return std::pair<const_iterator, const_iterator>(it, it);
+    } else {
+      const_iterator begin = it++;
+      return std::pair<const_iterator, const_iterator>(begin, it);
+    }
+  }
+
+  template <typename K = key_type>
+  std::pair<iterator, iterator> equal_range(const key_arg<K>& key) {
+    iterator it = find(key);
+    if (it == end()) {
+      return std::pair<iterator, iterator>(it, it);
+    } else {
+      iterator begin = it++;
+      return std::pair<iterator, iterator>(begin, it);
+    }
+  }
+
+  // insert
+  template <typename K, typename... Args>
+  std::pair<iterator, bool> try_emplace(K&& k, Args&&... args) {
+    auto p =
+        elements_.try_emplace(std::forward<K>(k), std::forward<Args>(args)...);
+    return std::pair<iterator, bool>(iterator(p.first), p.second);
+  }
+  std::pair<iterator, bool> insert(const value_type& value) {
+    return try_emplace(value.first, value.second);
+  }
+  std::pair<iterator, bool> insert(value_type&& value) {
+    return try_emplace(value.first, std::move(value.second));
+  }
+  template <typename... Args>
+  std::pair<iterator, bool> emplace(Args&&... args) {
+    return insert(value_type(std::forward<Args>(args)...));
+  }
+  template <class InputIt>
+  void insert(InputIt first, InputIt last) {
+    for (; first != last; ++first) {
+      try_emplace(first->first, first->second);
+    }
+  }
+  void insert(std::initializer_list<value_type> values) {
+    insert(values.begin(), values.end());
+  }
+
+  // Erase and clear
+  template <typename K = key_type>
+  size_type erase(const key_arg<K>& key) {
+    iterator it = find(key);
+    if (it == end()) {
+      return 0;
+    } else {
+      erase(it);
+      return 1;
+    }
+  }
+  iterator erase(iterator pos) {
+    iterator i = pos++;
+    elements_.erase(i.it_);
+    return pos;
+  }
+  void erase(iterator first, iterator last) {
+    while (first != last) {
+      first = erase(first);
+    }
+  }
+  void clear() { elements_.clear(); }
+
+  // Assign
+  Map& operator=(const Map& other) {
+    if (this != &other) {
+      clear();
+      insert(other.begin(), other.end());
+    }
+    return *this;
+  }
+
+  void swap(Map& other) {
+    if (arena() == other.arena()) {
+      InternalSwap(other);
+    } else {
+      // TODO(zuguang): optimize this. The temporary copy can be allocated
+      // in the same arena as the other message, and the "other = copy" can
+      // be replaced with the fast-path swap above.
+      Map copy = *this;
+      *this = other;
+      other = copy;
+    }
+  }
+
+  void InternalSwap(Map& other) { elements_.Swap(&other.elements_); }
+
+  // Access to hasher.  Currently this returns a copy, but it may
+  // be modified to return a const reference in the future.
+  hasher hash_function() const { return elements_.hash_function(); }
+
+  size_t SpaceUsedExcludingSelfLong() const {
+    if (empty()) return 0;
+    return elements_.SpaceUsedInternal() + internal::SpaceUsedInValues(this);
+  }
+
+ private:
+  Arena* arena() const { return elements_.arena(); }
+  InnerMap elements_;
+
+  friend class Arena;
+  using InternalArenaConstructable_ = void;
+  using DestructorSkippable_ = void;
+  template <typename Derived, typename K, typename V,
+            internal::WireFormatLite::FieldType key_wire_type,
+            internal::WireFormatLite::FieldType value_wire_type>
+  friend class internal::MapFieldLite;
+};
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_MAP_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_entry.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_entry.h
new file mode 100644
index 0000000..536dec9
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_entry.h
@@ -0,0 +1,134 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_MAP_ENTRY_H__
+#define GOOGLE_PROTOBUF_MAP_ENTRY_H__
+
+#include <google/protobuf/port.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/map_entry_lite.h>
+#include <google/protobuf/map_type_handler.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+class Arena;
+namespace internal {
+template <typename Derived, typename Key, typename Value,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+class MapField;
+}
+}  // namespace protobuf
+}  // namespace google
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// MapEntry is the returned google::protobuf::Message when calling AddMessage of
+// google::protobuf::Reflection. In order to let it work with generated message
+// reflection, its in-memory type is the same as generated message with the same
+// fields. However, in order to decide the in-memory type of key/value, we need
+// to know both their cpp type in generated api and proto type. In
+// implementation, all in-memory types have related wire format functions to
+// support except ArenaStringPtr. Therefore, we need to define another type with
+// supporting wire format functions. Since this type is only used as return type
+// of MapEntry accessors, it's named MapEntry accessor type.
+//
+// cpp type:               the type visible to users in public API.
+// proto type:             WireFormatLite::FieldType of the field.
+// in-memory type:         type of the data member used to stored this field.
+// MapEntry accessor type: type used in MapEntry getters/mutators to access the
+//                         field.
+//
+// cpp type | proto type  | in-memory type | MapEntry accessor type
+// int32_t    TYPE_INT32    int32_t          int32_t
+// int32_t    TYPE_FIXED32  int32_t          int32_t
+// string     TYPE_STRING   ArenaStringPtr   string
+// FooEnum    TYPE_ENUM     int              int
+// FooMessage TYPE_MESSAGE  FooMessage*      FooMessage
+//
+// The in-memory types of primitive types can be inferred from its proto type,
+// while we need to explicitly specify the cpp type if proto type is
+// TYPE_MESSAGE to infer the in-memory type.
+template <typename Derived, typename Key, typename Value,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+class MapEntry : public MapEntryImpl<Derived, Message, Key, Value,
+                                     kKeyFieldType, kValueFieldType> {
+ public:
+  constexpr MapEntry() {}
+  explicit MapEntry(Arena* arena)
+      : MapEntryImpl<Derived, Message, Key, Value, kKeyFieldType,
+                     kValueFieldType>(arena) {}
+  ~MapEntry() override {
+    Message::_internal_metadata_.template Delete<UnknownFieldSet>();
+  }
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+
+  typedef typename MapEntryImpl<Derived, Message, Key, Value, kKeyFieldType,
+                                kValueFieldType>::KeyTypeHandler KeyTypeHandler;
+  typedef
+      typename MapEntryImpl<Derived, Message, Key, Value, kKeyFieldType,
+                            kValueFieldType>::ValueTypeHandler ValueTypeHandler;
+  size_t SpaceUsedLong() const override {
+    size_t size = sizeof(Derived);
+    size += KeyTypeHandler::SpaceUsedInMapEntryLong(this->key_);
+    size += ValueTypeHandler::SpaceUsedInMapEntryLong(this->value_);
+    return size;
+  }
+
+ private:
+  friend class ::PROTOBUF_NAMESPACE_ID::Arena;
+  template <typename C, typename K, typename V,
+            WireFormatLite::FieldType k_wire_type, WireFormatLite::FieldType>
+  friend class internal::MapField;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapEntry);
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_MAP_ENTRY_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_entry_lite.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_entry_lite.h
new file mode 100644
index 0000000..6b08cd9
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_entry_lite.h
@@ -0,0 +1,563 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_MAP_ENTRY_LITE_H__
+#define GOOGLE_PROTOBUF_MAP_ENTRY_LITE_H__
+
+#include <assert.h>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/map.h>
+#include <google/protobuf/map_type_handler.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+namespace internal {
+template <typename Derived, typename Key, typename Value,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+class MapEntry;
+template <typename Derived, typename Key, typename Value,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+class MapFieldLite;
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// MoveHelper::Move is used to set *dest.  It copies *src, or moves it (in
+// the C++11 sense), or swaps it. *src is left in a sane state for
+// subsequent destruction, but shouldn't be used for anything.
+template <bool is_enum, bool is_message, bool is_stringlike, typename T>
+struct MoveHelper {  // primitives
+  static void Move(T* src, T* dest) { *dest = *src; }
+};
+
+template <bool is_message, bool is_stringlike, typename T>
+struct MoveHelper<true, is_message, is_stringlike, T> {  // enums
+  static void Move(T* src, T* dest) { *dest = *src; }
+  // T is an enum here, so allow conversions to and from int.
+  static void Move(T* src, int* dest) { *dest = static_cast<int>(*src); }
+  static void Move(int* src, T* dest) { *dest = static_cast<T>(*src); }
+};
+
+template <bool is_stringlike, typename T>
+struct MoveHelper<false, true, is_stringlike, T> {  // messages
+  static void Move(T* src, T* dest) { dest->Swap(src); }
+};
+
+template <typename T>
+struct MoveHelper<false, false, true, T> {  // strings and similar
+  static void Move(T* src, T* dest) {
+    *dest = std::move(*src);
+  }
+};
+
+// MapEntryImpl is used to implement parsing and serialization of map entries.
+// It uses Curious Recursive Template Pattern (CRTP) to provide the type of
+// the eventual code to the template code.
+template <typename Derived, typename Base, typename Key, typename Value,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+class MapEntryImpl : public Base {
+ public:
+  typedef MapEntryFuncs<Key, Value, kKeyFieldType, kValueFieldType> Funcs;
+
+ protected:
+  // Provide utilities to parse/serialize key/value.  Provide utilities to
+  // manipulate internal stored type.
+  typedef MapTypeHandler<kKeyFieldType, Key> KeyTypeHandler;
+  typedef MapTypeHandler<kValueFieldType, Value> ValueTypeHandler;
+
+  // Define internal memory layout. Strings and messages are stored as
+  // pointers, while other types are stored as values.
+  typedef typename KeyTypeHandler::TypeOnMemory KeyOnMemory;
+  typedef typename ValueTypeHandler::TypeOnMemory ValueOnMemory;
+
+  // Enum type cannot be used for MapTypeHandler::Read. Define a type
+  // which will replace Enum with int.
+  typedef typename KeyTypeHandler::MapEntryAccessorType KeyMapEntryAccessorType;
+  typedef
+      typename ValueTypeHandler::MapEntryAccessorType ValueMapEntryAccessorType;
+
+  // Constants for field number.
+  static const int kKeyFieldNumber = 1;
+  static const int kValueFieldNumber = 2;
+
+  // Constants for field tag.
+  static const uint8_t kKeyTag =
+      GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(kKeyFieldNumber, KeyTypeHandler::kWireType);
+  static const uint8_t kValueTag = GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(
+      kValueFieldNumber, ValueTypeHandler::kWireType);
+  static const size_t kTagSize = 1;
+
+ public:
+  // Work-around for a compiler bug (see repeated_field.h).
+  typedef void MapEntryHasMergeTypeTrait;
+  typedef Derived EntryType;
+  typedef Key EntryKeyType;
+  typedef Value EntryValueType;
+  static const WireFormatLite::FieldType kEntryKeyFieldType = kKeyFieldType;
+  static const WireFormatLite::FieldType kEntryValueFieldType = kValueFieldType;
+
+  constexpr MapEntryImpl()
+      : key_(KeyTypeHandler::Constinit()),
+        value_(ValueTypeHandler::Constinit()),
+        _has_bits_{} {}
+
+  explicit MapEntryImpl(Arena* arena)
+      : Base(arena),
+        key_(KeyTypeHandler::Constinit()),
+        value_(ValueTypeHandler::Constinit()),
+        _has_bits_{} {}
+
+  ~MapEntryImpl() override {
+    if (Base::GetArenaForAllocation() != nullptr) return;
+    KeyTypeHandler::DeleteNoArena(key_);
+    ValueTypeHandler::DeleteNoArena(value_);
+  }
+
+  // accessors ======================================================
+
+  virtual inline const KeyMapEntryAccessorType& key() const {
+    return KeyTypeHandler::GetExternalReference(key_);
+  }
+  virtual inline const ValueMapEntryAccessorType& value() const {
+    return ValueTypeHandler::DefaultIfNotInitialized(value_);
+  }
+  inline KeyMapEntryAccessorType* mutable_key() {
+    set_has_key();
+    return KeyTypeHandler::EnsureMutable(&key_, Base::GetArenaForAllocation());
+  }
+  inline ValueMapEntryAccessorType* mutable_value() {
+    set_has_value();
+    return ValueTypeHandler::EnsureMutable(&value_,
+                                           Base::GetArenaForAllocation());
+  }
+
+  // implements MessageLite =========================================
+
+  // MapEntryImpl is for implementation only and this function isn't called
+  // anywhere. Just provide a fake implementation here for MessageLite.
+  std::string GetTypeName() const override { return ""; }
+
+  void CheckTypeAndMergeFrom(const MessageLite& other) override {
+    MergeFromInternal(*::google::protobuf::internal::DownCast<const Derived*>(&other));
+  }
+
+  const char* _InternalParse(const char* ptr, ParseContext* ctx) final {
+    while (!ctx->Done(&ptr)) {
+      uint32_t tag;
+      ptr = ReadTag(ptr, &tag);
+      GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+      if (tag == kKeyTag) {
+        set_has_key();
+        KeyMapEntryAccessorType* key = mutable_key();
+        ptr = KeyTypeHandler::Read(ptr, ctx, key);
+        if (!Derived::ValidateKey(key)) return nullptr;
+      } else if (tag == kValueTag) {
+        set_has_value();
+        ValueMapEntryAccessorType* value = mutable_value();
+        ptr = ValueTypeHandler::Read(ptr, ctx, value);
+        if (!Derived::ValidateValue(value)) return nullptr;
+      } else {
+        if (tag == 0 || WireFormatLite::GetTagWireType(tag) ==
+                            WireFormatLite::WIRETYPE_END_GROUP) {
+          ctx->SetLastTag(tag);
+          return ptr;
+        }
+        ptr = UnknownFieldParse(tag, static_cast<std::string*>(nullptr), ptr,
+                                ctx);
+      }
+      GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+    }
+    return ptr;
+  }
+
+  size_t ByteSizeLong() const override {
+    size_t size = 0;
+    size += kTagSize + static_cast<size_t>(KeyTypeHandler::ByteSize(key()));
+    size += kTagSize + static_cast<size_t>(ValueTypeHandler::ByteSize(value()));
+    return size;
+  }
+
+  ::uint8_t* _InternalSerialize(
+      ::uint8_t* ptr, io::EpsCopyOutputStream* stream) const override {
+    ptr = KeyTypeHandler::Write(kKeyFieldNumber, key(), ptr, stream);
+    return ValueTypeHandler::Write(kValueFieldNumber, value(), ptr, stream);
+  }
+
+  // Don't override SerializeWithCachedSizesToArray.  Use MessageLite's.
+
+  int GetCachedSize() const override {
+    int size = 0;
+    size += has_key() ? static_cast<int>(kTagSize) +
+                            KeyTypeHandler::GetCachedSize(key())
+                      : 0;
+    size += has_value() ? static_cast<int>(kTagSize) +
+                              ValueTypeHandler::GetCachedSize(value())
+                        : 0;
+    return size;
+  }
+
+  bool IsInitialized() const override {
+    return ValueTypeHandler::IsInitialized(value_);
+  }
+
+  Base* New(Arena* arena) const override {
+    Derived* entry = Arena::CreateMessage<Derived>(arena);
+    return entry;
+  }
+
+ protected:
+  // We can't declare this function directly here as it would hide the other
+  // overload (const Message&).
+  void MergeFromInternal(const MapEntryImpl& from) {
+    if (from._has_bits_[0]) {
+      if (from.has_key()) {
+        KeyTypeHandler::EnsureMutable(&key_, Base::GetArenaForAllocation());
+        KeyTypeHandler::Merge(from.key(), &key_, Base::GetArenaForAllocation());
+        set_has_key();
+      }
+      if (from.has_value()) {
+        ValueTypeHandler::EnsureMutable(&value_, Base::GetArenaForAllocation());
+        ValueTypeHandler::Merge(from.value(), &value_,
+                                Base::GetArenaForAllocation());
+        set_has_value();
+      }
+    }
+  }
+
+ public:
+  void Clear() override {
+    KeyTypeHandler::Clear(&key_, Base::GetArenaForAllocation());
+    ValueTypeHandler::Clear(&value_, Base::GetArenaForAllocation());
+    clear_has_key();
+    clear_has_value();
+  }
+
+  // Parsing using MergePartialFromCodedStream, above, is not as
+  // efficient as it could be.  This helper class provides a speedier way.
+  template <typename MapField, typename Map>
+  class Parser {
+   public:
+    explicit Parser(MapField* mf) : mf_(mf), map_(mf->MutableMap()) {}
+    ~Parser() {
+      if (entry_ != nullptr && entry_->GetArenaForAllocation() == nullptr)
+        delete entry_;
+    }
+
+    const char* _InternalParse(const char* ptr, ParseContext* ctx) {
+      if (PROTOBUF_PREDICT_TRUE(!ctx->Done(&ptr) && *ptr == kKeyTag)) {
+        ptr = KeyTypeHandler::Read(ptr + 1, ctx, &key_);
+        if (PROTOBUF_PREDICT_FALSE(!ptr || !Derived::ValidateKey(&key_))) {
+          return nullptr;
+        }
+        if (PROTOBUF_PREDICT_TRUE(!ctx->Done(&ptr) && *ptr == kValueTag)) {
+          typename Map::size_type map_size = map_->size();
+          value_ptr_ = &(*map_)[key_];
+          if (PROTOBUF_PREDICT_TRUE(map_size != map_->size())) {
+            using T =
+                typename MapIf<ValueTypeHandler::kIsEnum, int*, Value*>::type;
+            ptr = ValueTypeHandler::Read(ptr + 1, ctx,
+                                         reinterpret_cast<T>(value_ptr_));
+            if (PROTOBUF_PREDICT_FALSE(!ptr ||
+                                       !Derived::ValidateValue(value_ptr_))) {
+              map_->erase(key_);  // Failure! Undo insertion.
+              return nullptr;
+            }
+            if (PROTOBUF_PREDICT_TRUE(ctx->Done(&ptr))) return ptr;
+            if (!ptr) return nullptr;
+            NewEntry();
+            ValueMover::Move(value_ptr_, entry_->mutable_value());
+            map_->erase(key_);
+            goto move_key;
+          }
+        } else {
+          if (!ptr) return nullptr;
+        }
+        NewEntry();
+      move_key:
+        KeyMover::Move(&key_, entry_->mutable_key());
+      } else {
+        if (!ptr) return nullptr;
+        NewEntry();
+      }
+      ptr = entry_->_InternalParse(ptr, ctx);
+      if (ptr) UseKeyAndValueFromEntry();
+      return ptr;
+    }
+
+    template <typename UnknownType>
+    const char* ParseWithEnumValidation(const char* ptr, ParseContext* ctx,
+                                        bool (*is_valid)(int),
+                                        uint32_t field_num,
+                                        InternalMetadata* metadata) {
+      auto entry = NewEntry();
+      ptr = entry->_InternalParse(ptr, ctx);
+      if (!ptr) return nullptr;
+      if (is_valid(entry->value())) {
+        UseKeyAndValueFromEntry();
+      } else {
+        WriteLengthDelimited(field_num, entry->SerializeAsString(),
+                             metadata->mutable_unknown_fields<UnknownType>());
+      }
+      return ptr;
+    }
+
+    MapEntryImpl* NewEntry() { return entry_ = mf_->NewEntry(); }
+
+    const Key& key() const { return key_; }
+    const Value& value() const { return *value_ptr_; }
+
+    const Key& entry_key() const { return entry_->key(); }
+    const Value& entry_value() const { return entry_->value(); }
+
+   private:
+    void UseKeyAndValueFromEntry() {
+      // Update key_ in case we need it later (because key() is called).
+      // This is potentially inefficient, especially if the key is
+      // expensive to copy (e.g., a long string), but this is a cold
+      // path, so it's not a big deal.
+      key_ = entry_->key();
+      value_ptr_ = &(*map_)[key_];
+      ValueMover::Move(entry_->mutable_value(), value_ptr_);
+    }
+
+    // After reading a key and value successfully, and inserting that data
+    // into map_, we are not at the end of the input.  This is unusual, but
+    // allowed by the spec.
+    bool ReadBeyondKeyValuePair(io::CodedInputStream* input) PROTOBUF_COLD {
+      NewEntry();
+      ValueMover::Move(value_ptr_, entry_->mutable_value());
+      map_->erase(key_);
+      KeyMover::Move(&key_, entry_->mutable_key());
+      const bool result = entry_->MergePartialFromCodedStream(input);
+      if (result) UseKeyAndValueFromEntry();
+      return result;
+    }
+
+    typedef MoveHelper<KeyTypeHandler::kIsEnum, KeyTypeHandler::kIsMessage,
+                       KeyTypeHandler::kWireType ==
+                           WireFormatLite::WIRETYPE_LENGTH_DELIMITED,
+                       Key>
+        KeyMover;
+    typedef MoveHelper<ValueTypeHandler::kIsEnum, ValueTypeHandler::kIsMessage,
+                       ValueTypeHandler::kWireType ==
+                           WireFormatLite::WIRETYPE_LENGTH_DELIMITED,
+                       Value>
+        ValueMover;
+
+    MapField* const mf_;
+    Map* const map_;
+    Key key_;
+    Value* value_ptr_;
+    MapEntryImpl* entry_ = nullptr;
+  };
+
+ protected:
+  void set_has_key() { _has_bits_[0] |= 0x00000001u; }
+  bool has_key() const { return (_has_bits_[0] & 0x00000001u) != 0; }
+  void clear_has_key() { _has_bits_[0] &= ~0x00000001u; }
+  void set_has_value() { _has_bits_[0] |= 0x00000002u; }
+  bool has_value() const { return (_has_bits_[0] & 0x00000002u) != 0; }
+  void clear_has_value() { _has_bits_[0] &= ~0x00000002u; }
+
+ public:
+  inline Arena* GetArena() const { return Base::GetArena(); }
+
+ protected:  // Needed for constructing tables
+  KeyOnMemory key_;
+  ValueOnMemory value_;
+  uint32_t _has_bits_[1];
+
+ private:
+  friend class ::PROTOBUF_NAMESPACE_ID::Arena;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  template <typename C, typename K, typename V, WireFormatLite::FieldType,
+            WireFormatLite::FieldType>
+  friend class internal::MapEntry;
+  template <typename C, typename K, typename V, WireFormatLite::FieldType,
+            WireFormatLite::FieldType>
+  friend class internal::MapFieldLite;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapEntryImpl);
+};
+
+template <typename T, typename Key, typename Value,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+class MapEntryLite : public MapEntryImpl<T, MessageLite, Key, Value,
+                                         kKeyFieldType, kValueFieldType> {
+ public:
+  typedef MapEntryImpl<T, MessageLite, Key, Value, kKeyFieldType,
+                       kValueFieldType>
+      SuperType;
+  constexpr MapEntryLite() {}
+  explicit MapEntryLite(Arena* arena) : SuperType(arena) {}
+  ~MapEntryLite() override {
+    MessageLite::_internal_metadata_.template Delete<std::string>();
+  }
+  void MergeFrom(const MapEntryLite& other) { MergeFromInternal(other); }
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapEntryLite);
+};
+
+// Helpers for deterministic serialization =============================
+
+// Iterator base for MapSorterFlat and MapSorterPtr.
+template <typename storage_type>
+struct MapSorterIt {
+  storage_type* ptr;
+  MapSorterIt(storage_type* ptr) : ptr(ptr) {}
+  bool operator==(const MapSorterIt& other) const { return ptr == other.ptr; }
+  bool operator!=(const MapSorterIt& other) const { return !(*this == other); }
+  MapSorterIt& operator++() { ++ptr; return *this; }
+  MapSorterIt operator++(int) { auto other = *this; ++ptr; return other; }
+  MapSorterIt operator+(int v) { return MapSorterIt{ptr + v}; }
+};
+
+// MapSorterFlat stores keys inline with pointers to map entries, so that
+// keys can be compared without indirection. This type is used for maps with
+// keys that are not strings.
+template <typename MapT>
+class MapSorterFlat {
+ public:
+  using value_type = typename MapT::value_type;
+  using storage_type = std::pair<typename MapT::key_type, const value_type*>;
+
+  // This const_iterator dereferenes to the map entry stored in the sorting
+  // array pairs. This is the same interface as the Map::const_iterator type,
+  // and allows generated code to use the same loop body with either form:
+  //   for (const auto& entry : map) { ... }
+  //   for (const auto& entry : MapSorterFlat(map)) { ... }
+  struct const_iterator : public MapSorterIt<storage_type> {
+    using pointer = const typename MapT::value_type*;
+    using reference = const typename MapT::value_type&;
+    using MapSorterIt<storage_type>::MapSorterIt;
+
+    pointer operator->() const { return this->ptr->second; }
+    reference operator*() const { return *this->operator->(); }
+  };
+
+  explicit MapSorterFlat(const MapT& m)
+      : size_(m.size()), items_(size_ ? new storage_type[size_] : nullptr) {
+    if (!size_) return;
+    storage_type* it = &items_[0];
+    for (const auto& entry : m) {
+      *it++ = {entry.first, &entry};
+    }
+    std::sort(&items_[0], &items_[size_],
+              [](const storage_type& a, const storage_type& b) {
+                return a.first < b.first;
+              });
+  }
+  size_t size() const { return size_; }
+  const_iterator begin() const { return {items_.get()}; }
+  const_iterator end() const { return {items_.get() + size_}; }
+
+ private:
+  size_t size_;
+  std::unique_ptr<storage_type[]> items_;
+};
+
+// MapSorterPtr stores and sorts pointers to map entries. This type is used for
+// maps with keys that are strings.
+template <typename MapT>
+class MapSorterPtr {
+ public:
+  using value_type = typename MapT::value_type;
+  using storage_type = const typename MapT::value_type*;
+
+  // This const_iterator dereferenes the map entry pointer stored in the sorting
+  // array. This is the same interface as the Map::const_iterator type, and
+  // allows generated code to use the same loop body with either form:
+  //   for (const auto& entry : map) { ... }
+  //   for (const auto& entry : MapSorterPtr(map)) { ... }
+  struct const_iterator : public MapSorterIt<storage_type> {
+    using pointer = const typename MapT::value_type*;
+    using reference = const typename MapT::value_type&;
+    using MapSorterIt<storage_type>::MapSorterIt;
+
+    pointer operator->() const { return *this->ptr; }
+    reference operator*() const { return *this->operator->(); }
+  };
+
+  explicit MapSorterPtr(const MapT& m)
+      : size_(m.size()), items_(size_ ? new storage_type[size_] : nullptr) {
+    if (!size_) return;
+    storage_type* it = &items_[0];
+    for (const auto& entry : m) {
+      *it++ = &entry;
+    }
+    std::sort(&items_[0], &items_[size_],
+              [](const storage_type& a, const storage_type& b) {
+                return a->first < b->first;
+              });
+  }
+  size_t size() const { return size_; }
+  const_iterator begin() const { return {items_.get()}; }
+  const_iterator end() const { return {items_.get() + size_}; }
+
+ private:
+  size_t size_;
+  std::unique_ptr<storage_type[]> items_;
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_MAP_ENTRY_LITE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_field.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_field.h
new file mode 100644
index 0000000..287d58f
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_field.h
@@ -0,0 +1,946 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_MAP_FIELD_H__
+#define GOOGLE_PROTOBUF_MAP_FIELD_H__
+
+#include <atomic>
+#include <functional>
+
+#include <google/protobuf/arena.h>
+#include <google/protobuf/stubs/mutex.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/map_entry.h>
+#include <google/protobuf/map_field_lite.h>
+#include <google/protobuf/map_type_handler.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/unknown_field_set.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+class DynamicMessage;
+class MapIterator;
+
+// Microsoft compiler complains about non-virtual destructor,
+// even when the destructor is private.
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4265)
+#endif  // _MSC_VER
+
+#define TYPE_CHECK(EXPECTEDTYPE, METHOD)                                   \
+  if (type() != EXPECTEDTYPE) {                                            \
+    GOOGLE_LOG(FATAL) << "Protocol Buffer map usage error:\n"                     \
+               << METHOD << " type does not match\n"                       \
+               << "  Expected : "                                          \
+               << FieldDescriptor::CppTypeName(EXPECTEDTYPE) << "\n"       \
+               << "  Actual   : " << FieldDescriptor::CppTypeName(type()); \
+  }
+
+// MapKey is an union type for representing any possible
+// map key.
+class PROTOBUF_EXPORT MapKey {
+ public:
+  MapKey() : type_() {}
+  MapKey(const MapKey& other) : type_() { CopyFrom(other); }
+
+  MapKey& operator=(const MapKey& other) {
+    CopyFrom(other);
+    return *this;
+  }
+
+  ~MapKey() {
+    if (type_ == FieldDescriptor::CPPTYPE_STRING) {
+      val_.string_value_.Destruct();
+    }
+  }
+
+  FieldDescriptor::CppType type() const {
+    if (type_ == FieldDescriptor::CppType()) {
+      GOOGLE_LOG(FATAL) << "Protocol Buffer map usage error:\n"
+                 << "MapKey::type MapKey is not initialized. "
+                 << "Call set methods to initialize MapKey.";
+    }
+    return type_;
+  }
+
+  void SetInt64Value(int64_t value) {
+    SetType(FieldDescriptor::CPPTYPE_INT64);
+    val_.int64_value_ = value;
+  }
+  void SetUInt64Value(uint64_t value) {
+    SetType(FieldDescriptor::CPPTYPE_UINT64);
+    val_.uint64_value_ = value;
+  }
+  void SetInt32Value(int32_t value) {
+    SetType(FieldDescriptor::CPPTYPE_INT32);
+    val_.int32_value_ = value;
+  }
+  void SetUInt32Value(uint32_t value) {
+    SetType(FieldDescriptor::CPPTYPE_UINT32);
+    val_.uint32_value_ = value;
+  }
+  void SetBoolValue(bool value) {
+    SetType(FieldDescriptor::CPPTYPE_BOOL);
+    val_.bool_value_ = value;
+  }
+  void SetStringValue(std::string val) {
+    SetType(FieldDescriptor::CPPTYPE_STRING);
+    *val_.string_value_.get_mutable() = std::move(val);
+  }
+
+  int64_t GetInt64Value() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_INT64, "MapKey::GetInt64Value");
+    return val_.int64_value_;
+  }
+  uint64_t GetUInt64Value() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT64, "MapKey::GetUInt64Value");
+    return val_.uint64_value_;
+  }
+  int32_t GetInt32Value() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_INT32, "MapKey::GetInt32Value");
+    return val_.int32_value_;
+  }
+  uint32_t GetUInt32Value() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT32, "MapKey::GetUInt32Value");
+    return val_.uint32_value_;
+  }
+  bool GetBoolValue() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_BOOL, "MapKey::GetBoolValue");
+    return val_.bool_value_;
+  }
+  const std::string& GetStringValue() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_STRING, "MapKey::GetStringValue");
+    return val_.string_value_.get();
+  }
+
+  bool operator<(const MapKey& other) const {
+    if (type_ != other.type_) {
+      // We could define a total order that handles this case, but
+      // there currently no need.  So, for now, fail.
+      GOOGLE_LOG(FATAL) << "Unsupported: type mismatch";
+    }
+    switch (type()) {
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+      case FieldDescriptor::CPPTYPE_FLOAT:
+      case FieldDescriptor::CPPTYPE_ENUM:
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        GOOGLE_LOG(FATAL) << "Unsupported";
+        return false;
+      case FieldDescriptor::CPPTYPE_STRING:
+        return val_.string_value_.get() < other.val_.string_value_.get();
+      case FieldDescriptor::CPPTYPE_INT64:
+        return val_.int64_value_ < other.val_.int64_value_;
+      case FieldDescriptor::CPPTYPE_INT32:
+        return val_.int32_value_ < other.val_.int32_value_;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        return val_.uint64_value_ < other.val_.uint64_value_;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        return val_.uint32_value_ < other.val_.uint32_value_;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        return val_.bool_value_ < other.val_.bool_value_;
+    }
+    return false;
+  }
+
+  bool operator==(const MapKey& other) const {
+    if (type_ != other.type_) {
+      // To be consistent with operator<, we don't allow this either.
+      GOOGLE_LOG(FATAL) << "Unsupported: type mismatch";
+    }
+    switch (type()) {
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+      case FieldDescriptor::CPPTYPE_FLOAT:
+      case FieldDescriptor::CPPTYPE_ENUM:
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        GOOGLE_LOG(FATAL) << "Unsupported";
+        break;
+      case FieldDescriptor::CPPTYPE_STRING:
+        return val_.string_value_.get() == other.val_.string_value_.get();
+      case FieldDescriptor::CPPTYPE_INT64:
+        return val_.int64_value_ == other.val_.int64_value_;
+      case FieldDescriptor::CPPTYPE_INT32:
+        return val_.int32_value_ == other.val_.int32_value_;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        return val_.uint64_value_ == other.val_.uint64_value_;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        return val_.uint32_value_ == other.val_.uint32_value_;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        return val_.bool_value_ == other.val_.bool_value_;
+    }
+    GOOGLE_LOG(FATAL) << "Can't get here.";
+    return false;
+  }
+
+  void CopyFrom(const MapKey& other) {
+    SetType(other.type());
+    switch (type_) {
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+      case FieldDescriptor::CPPTYPE_FLOAT:
+      case FieldDescriptor::CPPTYPE_ENUM:
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        GOOGLE_LOG(FATAL) << "Unsupported";
+        break;
+      case FieldDescriptor::CPPTYPE_STRING:
+        *val_.string_value_.get_mutable() = other.val_.string_value_.get();
+        break;
+      case FieldDescriptor::CPPTYPE_INT64:
+        val_.int64_value_ = other.val_.int64_value_;
+        break;
+      case FieldDescriptor::CPPTYPE_INT32:
+        val_.int32_value_ = other.val_.int32_value_;
+        break;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        val_.uint64_value_ = other.val_.uint64_value_;
+        break;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        val_.uint32_value_ = other.val_.uint32_value_;
+        break;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        val_.bool_value_ = other.val_.bool_value_;
+        break;
+    }
+  }
+
+ private:
+  template <typename K, typename V>
+  friend class internal::TypeDefinedMapFieldBase;
+  friend class ::PROTOBUF_NAMESPACE_ID::MapIterator;
+  friend class internal::DynamicMapField;
+
+  union KeyValue {
+    KeyValue() {}
+    internal::ExplicitlyConstructed<std::string> string_value_;
+    int64_t int64_value_;
+    int32_t int32_value_;
+    uint64_t uint64_value_;
+    uint32_t uint32_value_;
+    bool bool_value_;
+  } val_;
+
+  void SetType(FieldDescriptor::CppType type) {
+    if (type_ == type) return;
+    if (type_ == FieldDescriptor::CPPTYPE_STRING) {
+      val_.string_value_.Destruct();
+    }
+    type_ = type;
+    if (type_ == FieldDescriptor::CPPTYPE_STRING) {
+      val_.string_value_.DefaultConstruct();
+    }
+  }
+
+  // type_ is 0 or a valid FieldDescriptor::CppType.
+  // Use "CppType()" to indicate zero.
+  FieldDescriptor::CppType type_;
+};
+
+}  // namespace protobuf
+}  // namespace google
+namespace std {
+template <>
+struct hash<::PROTOBUF_NAMESPACE_ID::MapKey> {
+  size_t operator()(const ::PROTOBUF_NAMESPACE_ID::MapKey& map_key) const {
+    switch (map_key.type()) {
+      case ::PROTOBUF_NAMESPACE_ID::FieldDescriptor::CPPTYPE_DOUBLE:
+      case ::PROTOBUF_NAMESPACE_ID::FieldDescriptor::CPPTYPE_FLOAT:
+      case ::PROTOBUF_NAMESPACE_ID::FieldDescriptor::CPPTYPE_ENUM:
+      case ::PROTOBUF_NAMESPACE_ID::FieldDescriptor::CPPTYPE_MESSAGE:
+        GOOGLE_LOG(FATAL) << "Unsupported";
+        break;
+      case ::PROTOBUF_NAMESPACE_ID::FieldDescriptor::CPPTYPE_STRING:
+        return hash<std::string>()(map_key.GetStringValue());
+      case ::PROTOBUF_NAMESPACE_ID::FieldDescriptor::CPPTYPE_INT64: {
+        auto value = map_key.GetInt64Value();
+        return hash<decltype(value)>()(value);
+      }
+      case ::PROTOBUF_NAMESPACE_ID::FieldDescriptor::CPPTYPE_INT32: {
+        auto value = map_key.GetInt32Value();
+        return hash<decltype(value)>()(map_key.GetInt32Value());
+      }
+      case ::PROTOBUF_NAMESPACE_ID::FieldDescriptor::CPPTYPE_UINT64: {
+        auto value = map_key.GetUInt64Value();
+        return hash<decltype(value)>()(map_key.GetUInt64Value());
+      }
+      case ::PROTOBUF_NAMESPACE_ID::FieldDescriptor::CPPTYPE_UINT32: {
+        auto value = map_key.GetUInt32Value();
+        return hash<decltype(value)>()(map_key.GetUInt32Value());
+      }
+      case ::PROTOBUF_NAMESPACE_ID::FieldDescriptor::CPPTYPE_BOOL: {
+        return hash<bool>()(map_key.GetBoolValue());
+      }
+    }
+    GOOGLE_LOG(FATAL) << "Can't get here.";
+    return 0;
+  }
+  bool operator()(const ::PROTOBUF_NAMESPACE_ID::MapKey& map_key1,
+                  const ::PROTOBUF_NAMESPACE_ID::MapKey& map_key2) const {
+    return map_key1 < map_key2;
+  }
+};
+}  // namespace std
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+class ContendedMapCleanTest;
+class GeneratedMessageReflection;
+class MapFieldAccessor;
+
+// This class provides access to map field using reflection, which is the same
+// as those provided for RepeatedPtrField<Message>. It is used for internal
+// reflection implementation only. Users should never use this directly.
+class PROTOBUF_EXPORT MapFieldBase {
+ public:
+  MapFieldBase()
+      : arena_(nullptr), repeated_field_(nullptr), state_(STATE_MODIFIED_MAP) {}
+
+  // This constructor is for constant initialized global instances.
+  // It uses a linker initialized mutex, so it is not compatible with regular
+  // runtime instances.
+  // Except in MSVC, where we can't have a constinit mutex.
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr MapFieldBase(ConstantInitialized)
+      : arena_(nullptr),
+        repeated_field_(nullptr),
+        mutex_(GOOGLE_PROTOBUF_LINKER_INITIALIZED),
+        state_(STATE_MODIFIED_MAP) {}
+  explicit MapFieldBase(Arena* arena)
+      : arena_(arena), repeated_field_(nullptr), state_(STATE_MODIFIED_MAP) {}
+
+ protected:
+  ~MapFieldBase() {  // "protected" stops users from deleting a `MapFieldBase *`
+    GOOGLE_DCHECK(repeated_field_ == nullptr);
+  }
+  void Destruct();
+
+ public:
+  // Returns reference to internal repeated field. Data written using
+  // Map's api prior to calling this function is guarantted to be
+  // included in repeated field.
+  const RepeatedPtrFieldBase& GetRepeatedField() const;
+
+  // Like above. Returns mutable pointer to the internal repeated field.
+  RepeatedPtrFieldBase* MutableRepeatedField();
+
+  // Pure virtual map APIs for Map Reflection.
+  virtual bool ContainsMapKey(const MapKey& map_key) const = 0;
+  virtual bool InsertOrLookupMapValue(const MapKey& map_key,
+                                      MapValueRef* val) = 0;
+  virtual bool LookupMapValue(const MapKey& map_key,
+                              MapValueConstRef* val) const = 0;
+  bool LookupMapValue(const MapKey&, MapValueRef*) const = delete;
+
+  // Returns whether changes to the map are reflected in the repeated field.
+  bool IsRepeatedFieldValid() const;
+  // Insures operations after won't get executed before calling this.
+  bool IsMapValid() const;
+  virtual bool DeleteMapValue(const MapKey& map_key) = 0;
+  virtual bool EqualIterator(const MapIterator& a,
+                             const MapIterator& b) const = 0;
+  virtual void MapBegin(MapIterator* map_iter) const = 0;
+  virtual void MapEnd(MapIterator* map_iter) const = 0;
+  virtual void MergeFrom(const MapFieldBase& other) = 0;
+  virtual void Swap(MapFieldBase* other);
+  virtual void UnsafeShallowSwap(MapFieldBase* other);
+  // Sync Map with repeated field and returns the size of map.
+  virtual int size() const = 0;
+  virtual void Clear() = 0;
+
+  // Returns the number of bytes used by the repeated field, excluding
+  // sizeof(*this)
+  size_t SpaceUsedExcludingSelfLong() const;
+
+  int SpaceUsedExcludingSelf() const {
+    return internal::ToIntSize(SpaceUsedExcludingSelfLong());
+  }
+
+ protected:
+  // Gets the size of space used by map field.
+  virtual size_t SpaceUsedExcludingSelfNoLock() const;
+
+  // Synchronizes the content in Map to RepeatedPtrField if there is any change
+  // to Map after last synchronization.
+  void SyncRepeatedFieldWithMap() const;
+  virtual void SyncRepeatedFieldWithMapNoLock() const;
+
+  // Synchronizes the content in RepeatedPtrField to Map if there is any change
+  // to RepeatedPtrField after last synchronization.
+  void SyncMapWithRepeatedField() const;
+  virtual void SyncMapWithRepeatedFieldNoLock() const {}
+
+  // Tells MapFieldBase that there is new change to Map.
+  void SetMapDirty();
+
+  // Tells MapFieldBase that there is new change to RepeatedPtrField.
+  void SetRepeatedDirty();
+
+  // Provides derived class the access to repeated field.
+  void* MutableRepeatedPtrField() const;
+
+  void InternalSwap(MapFieldBase* other);
+
+  // Support thread sanitizer (tsan) by making const / mutable races
+  // more apparent.  If one thread calls MutableAccess() while another
+  // thread calls either ConstAccess() or MutableAccess(), on the same
+  // MapFieldBase-derived object, and there is no synchronization going
+  // on between them, tsan will alert.
+#if defined(__SANITIZE_THREAD__) || defined(THREAD_SANITIZER)
+  void ConstAccess() const { GOOGLE_CHECK_EQ(seq1_, seq2_); }
+  void MutableAccess() {
+    if (seq1_ & 1) {
+      seq2_ = ++seq1_;
+    } else {
+      seq1_ = ++seq2_;
+    }
+  }
+  unsigned int seq1_ = 0, seq2_ = 0;
+#else
+  void ConstAccess() const {}
+  void MutableAccess() {}
+#endif
+  enum State {
+    STATE_MODIFIED_MAP = 0,       // map has newly added data that has not been
+                                  // synchronized to repeated field
+    STATE_MODIFIED_REPEATED = 1,  // repeated field has newly added data that
+                                  // has not been synchronized to map
+    CLEAN = 2,                    // data in map and repeated field are same
+  };
+
+  Arena* arena_;
+  mutable RepeatedPtrField<Message>* repeated_field_;
+
+  mutable internal::WrappedMutex
+      mutex_;  // The thread to synchronize map and repeated field
+               // needs to get lock first;
+  mutable std::atomic<State> state_;
+
+ private:
+  friend class ContendedMapCleanTest;
+  friend class GeneratedMessageReflection;
+  friend class MapFieldAccessor;
+  friend class ::PROTOBUF_NAMESPACE_ID::Reflection;
+  friend class ::PROTOBUF_NAMESPACE_ID::DynamicMessage;
+
+  // Virtual helper methods for MapIterator. MapIterator doesn't have the
+  // type helper for key and value. Call these help methods to deal with
+  // different types. Real helper methods are implemented in
+  // TypeDefinedMapFieldBase.
+  friend class ::PROTOBUF_NAMESPACE_ID::MapIterator;
+  // Allocate map<...>::iterator for MapIterator.
+  virtual void InitializeIterator(MapIterator* map_iter) const = 0;
+
+  // DeleteIterator() is called by the destructor of MapIterator only.
+  // It deletes map<...>::iterator for MapIterator.
+  virtual void DeleteIterator(MapIterator* map_iter) const = 0;
+
+  // Copy the map<...>::iterator from other_iterator to
+  // this_iterator.
+  virtual void CopyIterator(MapIterator* this_iterator,
+                            const MapIterator& other_iterator) const = 0;
+
+  // IncreaseIterator() is called by operator++() of MapIterator only.
+  // It implements the ++ operator of MapIterator.
+  virtual void IncreaseIterator(MapIterator* map_iter) const = 0;
+
+  // Swaps state_ with another MapFieldBase
+  void SwapState(MapFieldBase* other);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapFieldBase);
+};
+
+// This class provides common Map Reflection implementations for generated
+// message and dynamic message.
+template <typename Key, typename T>
+class TypeDefinedMapFieldBase : public MapFieldBase {
+ public:
+  TypeDefinedMapFieldBase() {}
+
+  // This constructor is for constant initialized global instances.
+  // It uses a linker initialized mutex, so it is not compatible with regular
+  // runtime instances.
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr TypeDefinedMapFieldBase(ConstantInitialized tag)
+      : MapFieldBase(tag) {}
+  explicit TypeDefinedMapFieldBase(Arena* arena) : MapFieldBase(arena) {}
+  TypeDefinedMapFieldBase(ArenaInitialized, Arena* arena)
+      : TypeDefinedMapFieldBase(arena) {}
+
+ protected:
+  ~TypeDefinedMapFieldBase() {}
+  using MapFieldBase::Destruct;
+
+ public:
+  void MapBegin(MapIterator* map_iter) const override;
+  void MapEnd(MapIterator* map_iter) const override;
+  bool EqualIterator(const MapIterator& a, const MapIterator& b) const override;
+
+  virtual const Map<Key, T>& GetMap() const = 0;
+  virtual Map<Key, T>* MutableMap() = 0;
+
+ protected:
+  typename Map<Key, T>::const_iterator& InternalGetIterator(
+      const MapIterator* map_iter) const;
+
+ private:
+  void InitializeIterator(MapIterator* map_iter) const override;
+  void DeleteIterator(MapIterator* map_iter) const override;
+  void CopyIterator(MapIterator* this_iteratorm,
+                    const MapIterator& that_iterator) const override;
+  void IncreaseIterator(MapIterator* map_iter) const override;
+
+  virtual void SetMapIteratorValue(MapIterator* map_iter) const = 0;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeDefinedMapFieldBase);
+};
+
+// This class provides access to map field using generated api. It is used for
+// internal generated message implementation only. Users should never use this
+// directly.
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+class MapField : public TypeDefinedMapFieldBase<Key, T> {
+  // Provide utilities to parse/serialize key/value.  Provide utilities to
+  // manipulate internal stored type.
+  typedef MapTypeHandler<kKeyFieldType, Key> KeyTypeHandler;
+  typedef MapTypeHandler<kValueFieldType, T> ValueTypeHandler;
+
+  // Define message type for internal repeated field.
+  typedef Derived EntryType;
+
+  // Define abbreviation for parent MapFieldLite
+  typedef MapFieldLite<Derived, Key, T, kKeyFieldType, kValueFieldType>
+      MapFieldLiteType;
+
+  // Enum needs to be handled differently from other types because it has
+  // different exposed type in Map's api and repeated field's api. For
+  // details see the comment in the implementation of
+  // SyncMapWithRepeatedFieldNoLock.
+  static constexpr bool kIsValueEnum = ValueTypeHandler::kIsEnum;
+  typedef typename MapIf<kIsValueEnum, T, const T&>::type CastValueType;
+
+ public:
+  typedef Map<Key, T> MapType;
+
+  MapField() : impl_() {}
+  virtual ~MapField() {}  // Destruct() must already have been called!
+  void Destruct() {
+    impl_.Destruct();
+    TypeDefinedMapFieldBase<Key, T>::Destruct();
+  }
+
+  // This constructor is for constant initialized global instances.
+  // It uses a linker initialized mutex, so it is not compatible with regular
+  // runtime instances.
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr MapField(ConstantInitialized tag)
+      : TypeDefinedMapFieldBase<Key, T>(tag), impl_() {}
+  explicit MapField(Arena* arena)
+      : TypeDefinedMapFieldBase<Key, T>(arena), impl_(arena) {}
+  MapField(ArenaInitialized, Arena* arena) : MapField(arena) {}
+
+  // Implement MapFieldBase
+  bool ContainsMapKey(const MapKey& map_key) const override;
+  bool InsertOrLookupMapValue(const MapKey& map_key, MapValueRef* val) override;
+  bool LookupMapValue(const MapKey& map_key,
+                      MapValueConstRef* val) const override;
+  bool LookupMapValue(const MapKey&, MapValueRef*) const = delete;
+  bool DeleteMapValue(const MapKey& map_key) override;
+
+  const Map<Key, T>& GetMap() const override {
+    MapFieldBase::SyncMapWithRepeatedField();
+    return impl_.GetMap();
+  }
+
+  Map<Key, T>* MutableMap() override {
+    MapFieldBase::SyncMapWithRepeatedField();
+    Map<Key, T>* result = impl_.MutableMap();
+    MapFieldBase::SetMapDirty();
+    return result;
+  }
+
+  int size() const override;
+  void Clear() override;
+  void MergeFrom(const MapFieldBase& other) override;
+  void Swap(MapFieldBase* other) override;
+  void UnsafeShallowSwap(MapFieldBase* other) override;
+  void InternalSwap(MapField* other);
+
+  // Used in the implementation of parsing. Caller should take the ownership iff
+  // arena_ is nullptr.
+  EntryType* NewEntry() const { return impl_.NewEntry(); }
+
+  const char* _InternalParse(const char* ptr, ParseContext* ctx) {
+    return impl_._InternalParse(ptr, ctx);
+  }
+  template <typename UnknownType>
+  const char* ParseWithEnumValidation(const char* ptr, ParseContext* ctx,
+                                      bool (*is_valid)(int), uint32_t field_num,
+                                      InternalMetadata* metadata) {
+    return impl_.template ParseWithEnumValidation<UnknownType>(
+        ptr, ctx, is_valid, field_num, metadata);
+  }
+
+ private:
+  MapFieldLiteType impl_;
+
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+
+  // Implements MapFieldBase
+  void SyncRepeatedFieldWithMapNoLock() const override;
+  void SyncMapWithRepeatedFieldNoLock() const override;
+  size_t SpaceUsedExcludingSelfNoLock() const override;
+
+  void SetMapIteratorValue(MapIterator* map_iter) const override;
+
+  friend class ::PROTOBUF_NAMESPACE_ID::Arena;
+  friend class MapFieldStateTest;  // For testing, it needs raw access to impl_
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapField);
+};
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType key_wire_type,
+          WireFormatLite::FieldType value_wire_type>
+bool AllAreInitialized(
+    const MapField<Derived, Key, T, key_wire_type, value_wire_type>& field) {
+  const auto& t = field.GetMap();
+  for (typename Map<Key, T>::const_iterator it = t.begin(); it != t.end();
+       ++it) {
+    if (!it->second.IsInitialized()) return false;
+  }
+  return true;
+}
+
+template <typename T, typename Key, typename Value,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+struct MapEntryToMapField<
+    MapEntry<T, Key, Value, kKeyFieldType, kValueFieldType>> {
+  typedef MapField<T, Key, Value, kKeyFieldType, kValueFieldType> MapFieldType;
+};
+
+class PROTOBUF_EXPORT DynamicMapField
+    : public TypeDefinedMapFieldBase<MapKey, MapValueRef> {
+ public:
+  explicit DynamicMapField(const Message* default_entry);
+  DynamicMapField(const Message* default_entry, Arena* arena);
+  virtual ~DynamicMapField();
+
+  // Implement MapFieldBase
+  bool ContainsMapKey(const MapKey& map_key) const override;
+  bool InsertOrLookupMapValue(const MapKey& map_key, MapValueRef* val) override;
+  bool LookupMapValue(const MapKey& map_key,
+                      MapValueConstRef* val) const override;
+  bool LookupMapValue(const MapKey&, MapValueRef*) const = delete;
+  bool DeleteMapValue(const MapKey& map_key) override;
+  void MergeFrom(const MapFieldBase& other) override;
+  void Swap(MapFieldBase* other) override;
+  void UnsafeShallowSwap(MapFieldBase* other) override { Swap(other); }
+
+  const Map<MapKey, MapValueRef>& GetMap() const override;
+  Map<MapKey, MapValueRef>* MutableMap() override;
+
+  int size() const override;
+  void Clear() override;
+
+ private:
+  Map<MapKey, MapValueRef> map_;
+  const Message* default_entry_;
+
+  void AllocateMapValue(MapValueRef* map_val);
+
+  // Implements MapFieldBase
+  void SyncRepeatedFieldWithMapNoLock() const override;
+  void SyncMapWithRepeatedFieldNoLock() const override;
+  size_t SpaceUsedExcludingSelfNoLock() const override;
+  void SetMapIteratorValue(MapIterator* map_iter) const override;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DynamicMapField);
+};
+
+}  // namespace internal
+
+// MapValueConstRef points to a map value. Users can NOT modify
+// the map value.
+class PROTOBUF_EXPORT MapValueConstRef {
+ public:
+  MapValueConstRef() : data_(nullptr), type_() {}
+
+  int64_t GetInt64Value() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_INT64,
+               "MapValueConstRef::GetInt64Value");
+    return *reinterpret_cast<int64_t*>(data_);
+  }
+  uint64_t GetUInt64Value() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT64,
+               "MapValueConstRef::GetUInt64Value");
+    return *reinterpret_cast<uint64_t*>(data_);
+  }
+  int32_t GetInt32Value() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_INT32,
+               "MapValueConstRef::GetInt32Value");
+    return *reinterpret_cast<int32_t*>(data_);
+  }
+  uint32_t GetUInt32Value() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT32,
+               "MapValueConstRef::GetUInt32Value");
+    return *reinterpret_cast<uint32_t*>(data_);
+  }
+  bool GetBoolValue() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_BOOL, "MapValueConstRef::GetBoolValue");
+    return *reinterpret_cast<bool*>(data_);
+  }
+  int GetEnumValue() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_ENUM, "MapValueConstRef::GetEnumValue");
+    return *reinterpret_cast<int*>(data_);
+  }
+  const std::string& GetStringValue() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_STRING,
+               "MapValueConstRef::GetStringValue");
+    return *reinterpret_cast<std::string*>(data_);
+  }
+  float GetFloatValue() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_FLOAT,
+               "MapValueConstRef::GetFloatValue");
+    return *reinterpret_cast<float*>(data_);
+  }
+  double GetDoubleValue() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_DOUBLE,
+               "MapValueConstRef::GetDoubleValue");
+    return *reinterpret_cast<double*>(data_);
+  }
+
+  const Message& GetMessageValue() const {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_MESSAGE,
+               "MapValueConstRef::GetMessageValue");
+    return *reinterpret_cast<Message*>(data_);
+  }
+
+ protected:
+  // data_ point to a map value. MapValueConstRef does not
+  // own this value.
+  void* data_;
+  // type_ is 0 or a valid FieldDescriptor::CppType.
+  // Use "CppType()" to indicate zero.
+  FieldDescriptor::CppType type_;
+
+  FieldDescriptor::CppType type() const {
+    if (type_ == FieldDescriptor::CppType() || data_ == nullptr) {
+      GOOGLE_LOG(FATAL)
+          << "Protocol Buffer map usage error:\n"
+          << "MapValueConstRef::type MapValueConstRef is not initialized.";
+    }
+    return type_;
+  }
+
+ private:
+  template <typename Derived, typename K, typename V,
+            internal::WireFormatLite::FieldType key_wire_type,
+            internal::WireFormatLite::FieldType value_wire_type>
+  friend class internal::MapField;
+  template <typename K, typename V>
+  friend class internal::TypeDefinedMapFieldBase;
+  friend class ::PROTOBUF_NAMESPACE_ID::MapIterator;
+  friend class Reflection;
+  friend class internal::DynamicMapField;
+
+  void SetType(FieldDescriptor::CppType type) { type_ = type; }
+  void SetValue(const void* val) { data_ = const_cast<void*>(val); }
+  void CopyFrom(const MapValueConstRef& other) {
+    type_ = other.type_;
+    data_ = other.data_;
+  }
+};
+
+// MapValueRef points to a map value. Users are able to modify
+// the map value.
+class PROTOBUF_EXPORT MapValueRef final : public MapValueConstRef {
+ public:
+  MapValueRef() {}
+
+  void SetInt64Value(int64_t value) {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_INT64, "MapValueRef::SetInt64Value");
+    *reinterpret_cast<int64_t*>(data_) = value;
+  }
+  void SetUInt64Value(uint64_t value) {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT64, "MapValueRef::SetUInt64Value");
+    *reinterpret_cast<uint64_t*>(data_) = value;
+  }
+  void SetInt32Value(int32_t value) {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_INT32, "MapValueRef::SetInt32Value");
+    *reinterpret_cast<int32_t*>(data_) = value;
+  }
+  void SetUInt32Value(uint32_t value) {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT32, "MapValueRef::SetUInt32Value");
+    *reinterpret_cast<uint32_t*>(data_) = value;
+  }
+  void SetBoolValue(bool value) {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_BOOL, "MapValueRef::SetBoolValue");
+    *reinterpret_cast<bool*>(data_) = value;
+  }
+  // TODO(jieluo) - Checks that enum is member.
+  void SetEnumValue(int value) {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_ENUM, "MapValueRef::SetEnumValue");
+    *reinterpret_cast<int*>(data_) = value;
+  }
+  void SetStringValue(const std::string& value) {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_STRING, "MapValueRef::SetStringValue");
+    *reinterpret_cast<std::string*>(data_) = value;
+  }
+  void SetFloatValue(float value) {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_FLOAT, "MapValueRef::SetFloatValue");
+    *reinterpret_cast<float*>(data_) = value;
+  }
+  void SetDoubleValue(double value) {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_DOUBLE, "MapValueRef::SetDoubleValue");
+    *reinterpret_cast<double*>(data_) = value;
+  }
+
+  Message* MutableMessageValue() {
+    TYPE_CHECK(FieldDescriptor::CPPTYPE_MESSAGE,
+               "MapValueRef::MutableMessageValue");
+    return reinterpret_cast<Message*>(data_);
+  }
+
+ private:
+  friend class internal::DynamicMapField;
+
+  // Only used in DynamicMapField
+  void DeleteData() {
+    switch (type_) {
+#define HANDLE_TYPE(CPPTYPE, TYPE)           \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE: { \
+    delete reinterpret_cast<TYPE*>(data_);   \
+    break;                                   \
+  }
+      HANDLE_TYPE(INT32, int32_t);
+      HANDLE_TYPE(INT64, int64_t);
+      HANDLE_TYPE(UINT32, uint32_t);
+      HANDLE_TYPE(UINT64, uint64_t);
+      HANDLE_TYPE(DOUBLE, double);
+      HANDLE_TYPE(FLOAT, float);
+      HANDLE_TYPE(BOOL, bool);
+      HANDLE_TYPE(STRING, std::string);
+      HANDLE_TYPE(ENUM, int32_t);
+      HANDLE_TYPE(MESSAGE, Message);
+#undef HANDLE_TYPE
+    }
+  }
+};
+
+#undef TYPE_CHECK
+
+class PROTOBUF_EXPORT MapIterator {
+ public:
+  MapIterator(Message* message, const FieldDescriptor* field) {
+    const Reflection* reflection = message->GetReflection();
+    map_ = reflection->MutableMapData(message, field);
+    key_.SetType(field->message_type()->map_key()->cpp_type());
+    value_.SetType(field->message_type()->map_value()->cpp_type());
+    map_->InitializeIterator(this);
+  }
+  MapIterator(const MapIterator& other) {
+    map_ = other.map_;
+    map_->InitializeIterator(this);
+    map_->CopyIterator(this, other);
+  }
+  ~MapIterator() { map_->DeleteIterator(this); }
+  MapIterator& operator=(const MapIterator& other) {
+    map_ = other.map_;
+    map_->CopyIterator(this, other);
+    return *this;
+  }
+  friend bool operator==(const MapIterator& a, const MapIterator& b) {
+    return a.map_->EqualIterator(a, b);
+  }
+  friend bool operator!=(const MapIterator& a, const MapIterator& b) {
+    return !a.map_->EqualIterator(a, b);
+  }
+  MapIterator& operator++() {
+    map_->IncreaseIterator(this);
+    return *this;
+  }
+  MapIterator operator++(int) {
+    // iter_ is copied from Map<...>::iterator, no need to
+    // copy from its self again. Use the same implementation
+    // with operator++()
+    map_->IncreaseIterator(this);
+    return *this;
+  }
+  const MapKey& GetKey() { return key_; }
+  const MapValueRef& GetValueRef() { return value_; }
+  MapValueRef* MutableValueRef() {
+    map_->SetMapDirty();
+    return &value_;
+  }
+
+ private:
+  template <typename Key, typename T>
+  friend class internal::TypeDefinedMapFieldBase;
+  friend class internal::DynamicMapField;
+  template <typename Derived, typename Key, typename T,
+            internal::WireFormatLite::FieldType kKeyFieldType,
+            internal::WireFormatLite::FieldType kValueFieldType>
+  friend class internal::MapField;
+
+  // reinterpret_cast from heap-allocated Map<...>::iterator*. MapIterator owns
+  // the iterator. It is allocated by MapField<...>::InitializeIterator() called
+  // in constructor and deleted by MapField<...>::DeleteIterator() called in
+  // destructor.
+  void* iter_;
+  // Point to a MapField to call helper methods implemented in MapField.
+  // MapIterator does not own this object.
+  internal::MapFieldBase* map_;
+  MapKey key_;
+  MapValueRef value_;
+};
+
+}  // namespace protobuf
+}  // namespace google
+
+#ifdef _MSC_VER
+#pragma warning(pop)  // restore warning C4265
+#endif                // _MSC_VER
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_MAP_FIELD_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_field_inl.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_field_inl.h
new file mode 100644
index 0000000..7c4c232
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_field_inl.h
@@ -0,0 +1,375 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_MAP_FIELD_INL_H__
+#define GOOGLE_PROTOBUF_MAP_FIELD_INL_H__
+
+#include <memory>
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/map.h>
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/map_type_handler.h>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+namespace internal {
+// UnwrapMapKey template
+template <typename T>
+T UnwrapMapKey(const MapKey& map_key);
+template <>
+inline int32_t UnwrapMapKey<int32_t>(const MapKey& map_key) {
+  return map_key.GetInt32Value();
+}
+template <>
+inline uint32_t UnwrapMapKey<uint32_t>(const MapKey& map_key) {
+  return map_key.GetUInt32Value();
+}
+template <>
+inline int64_t UnwrapMapKey<int64_t>(const MapKey& map_key) {
+  return map_key.GetInt64Value();
+}
+template <>
+inline uint64_t UnwrapMapKey<uint64_t>(const MapKey& map_key) {
+  return map_key.GetUInt64Value();
+}
+template <>
+inline bool UnwrapMapKey<bool>(const MapKey& map_key) {
+  return map_key.GetBoolValue();
+}
+template <>
+inline std::string UnwrapMapKey<std::string>(const MapKey& map_key) {
+  return map_key.GetStringValue();
+}
+
+// SetMapKey template
+template <typename T>
+inline void SetMapKey(MapKey* map_key, const T& value);
+template <>
+inline void SetMapKey<int32_t>(MapKey* map_key, const int32_t& value) {
+  map_key->SetInt32Value(value);
+}
+template <>
+inline void SetMapKey<uint32_t>(MapKey* map_key, const uint32_t& value) {
+  map_key->SetUInt32Value(value);
+}
+template <>
+inline void SetMapKey<int64_t>(MapKey* map_key, const int64_t& value) {
+  map_key->SetInt64Value(value);
+}
+template <>
+inline void SetMapKey<uint64_t>(MapKey* map_key, const uint64_t& value) {
+  map_key->SetUInt64Value(value);
+}
+template <>
+inline void SetMapKey<bool>(MapKey* map_key, const bool& value) {
+  map_key->SetBoolValue(value);
+}
+template <>
+inline void SetMapKey<std::string>(MapKey* map_key, const std::string& value) {
+  map_key->SetStringValue(value);
+}
+
+// ------------------------TypeDefinedMapFieldBase---------------
+template <typename Key, typename T>
+typename Map<Key, T>::const_iterator&
+TypeDefinedMapFieldBase<Key, T>::InternalGetIterator(
+    const MapIterator* map_iter) const {
+  return *reinterpret_cast<typename Map<Key, T>::const_iterator*>(
+      map_iter->iter_);
+}
+
+template <typename Key, typename T>
+void TypeDefinedMapFieldBase<Key, T>::MapBegin(MapIterator* map_iter) const {
+  InternalGetIterator(map_iter) = GetMap().begin();
+  SetMapIteratorValue(map_iter);
+}
+
+template <typename Key, typename T>
+void TypeDefinedMapFieldBase<Key, T>::MapEnd(MapIterator* map_iter) const {
+  InternalGetIterator(map_iter) = GetMap().end();
+}
+
+template <typename Key, typename T>
+bool TypeDefinedMapFieldBase<Key, T>::EqualIterator(
+    const MapIterator& a, const MapIterator& b) const {
+  return InternalGetIterator(&a) == InternalGetIterator(&b);
+}
+
+template <typename Key, typename T>
+void TypeDefinedMapFieldBase<Key, T>::IncreaseIterator(
+    MapIterator* map_iter) const {
+  ++InternalGetIterator(map_iter);
+  SetMapIteratorValue(map_iter);
+}
+
+template <typename Key, typename T>
+void TypeDefinedMapFieldBase<Key, T>::InitializeIterator(
+    MapIterator* map_iter) const {
+  map_iter->iter_ = new typename Map<Key, T>::const_iterator;
+  GOOGLE_CHECK(map_iter->iter_ != nullptr);
+}
+
+template <typename Key, typename T>
+void TypeDefinedMapFieldBase<Key, T>::DeleteIterator(
+    MapIterator* map_iter) const {
+  delete reinterpret_cast<typename Map<Key, T>::const_iterator*>(
+      map_iter->iter_);
+}
+
+template <typename Key, typename T>
+void TypeDefinedMapFieldBase<Key, T>::CopyIterator(
+    MapIterator* this_iter, const MapIterator& that_iter) const {
+  InternalGetIterator(this_iter) = InternalGetIterator(&that_iter);
+  this_iter->key_.SetType(that_iter.key_.type());
+  // MapValueRef::type() fails when containing data is null. However, if
+  // this_iter points to MapEnd, data can be null.
+  this_iter->value_.SetType(
+      static_cast<FieldDescriptor::CppType>(that_iter.value_.type_));
+  SetMapIteratorValue(this_iter);
+}
+
+// ----------------------------------------------------------------------
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+int MapField<Derived, Key, T, kKeyFieldType, kValueFieldType>::size() const {
+  MapFieldBase::SyncMapWithRepeatedField();
+  return static_cast<int>(impl_.GetMap().size());
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+void MapField<Derived, Key, T, kKeyFieldType, kValueFieldType>::Clear() {
+  if (this->MapFieldBase::repeated_field_ != nullptr) {
+    RepeatedPtrField<EntryType>* repeated_field =
+        reinterpret_cast<RepeatedPtrField<EntryType>*>(
+            this->MapFieldBase::repeated_field_);
+    repeated_field->Clear();
+  }
+
+  impl_.MutableMap()->clear();
+  // Data in map and repeated field are both empty, but we can't set status
+  // CLEAN. Because clear is a generated API, we cannot invalidate previous
+  // reference to map.
+  MapFieldBase::SetMapDirty();
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+void MapField<Derived, Key, T, kKeyFieldType,
+              kValueFieldType>::SetMapIteratorValue(MapIterator* map_iter)
+    const {
+  const Map<Key, T>& map = impl_.GetMap();
+  typename Map<Key, T>::const_iterator iter =
+      TypeDefinedMapFieldBase<Key, T>::InternalGetIterator(map_iter);
+  if (iter == map.end()) return;
+  SetMapKey(&map_iter->key_, iter->first);
+  map_iter->value_.SetValue(&iter->second);
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+bool MapField<Derived, Key, T, kKeyFieldType, kValueFieldType>::ContainsMapKey(
+    const MapKey& map_key) const {
+  const Map<Key, T>& map = impl_.GetMap();
+  const Key& key = UnwrapMapKey<Key>(map_key);
+  typename Map<Key, T>::const_iterator iter = map.find(key);
+  return iter != map.end();
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+bool MapField<Derived, Key, T, kKeyFieldType,
+              kValueFieldType>::InsertOrLookupMapValue(const MapKey& map_key,
+                                                       MapValueRef* val) {
+  // Always use mutable map because users may change the map value by
+  // MapValueRef.
+  Map<Key, T>* map = MutableMap();
+  const Key& key = UnwrapMapKey<Key>(map_key);
+  typename Map<Key, T>::iterator iter = map->find(key);
+  if (map->end() == iter) {
+    val->SetValue(&((*map)[key]));
+    return true;
+  }
+  // Key is already in the map. Make sure (*map)[key] is not called.
+  // [] may reorder the map and iterators.
+  val->SetValue(&(iter->second));
+  return false;
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+bool MapField<Derived, Key, T, kKeyFieldType, kValueFieldType>::LookupMapValue(
+    const MapKey& map_key, MapValueConstRef* val) const {
+  const Map<Key, T>& map = GetMap();
+  const Key& key = UnwrapMapKey<Key>(map_key);
+  typename Map<Key, T>::const_iterator iter = map.find(key);
+  if (map.end() == iter) {
+    return false;
+  }
+  // Key is already in the map. Make sure (*map)[key] is not called.
+  // [] may reorder the map and iterators.
+  val->SetValue(&(iter->second));
+  return true;
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+bool MapField<Derived, Key, T, kKeyFieldType, kValueFieldType>::DeleteMapValue(
+    const MapKey& map_key) {
+  const Key& key = UnwrapMapKey<Key>(map_key);
+  return MutableMap()->erase(key);
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+void MapField<Derived, Key, T, kKeyFieldType, kValueFieldType>::MergeFrom(
+    const MapFieldBase& other) {
+  MapFieldBase::SyncMapWithRepeatedField();
+  const MapField& other_field = static_cast<const MapField&>(other);
+  other_field.SyncMapWithRepeatedField();
+  impl_.MergeFrom(other_field.impl_);
+  MapFieldBase::SetMapDirty();
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+void MapField<Derived, Key, T, kKeyFieldType, kValueFieldType>::Swap(
+    MapFieldBase* other) {
+  MapFieldBase::Swap(other);
+  MapField* other_field = down_cast<MapField*>(other);
+  impl_.Swap(&other_field->impl_);
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+void MapField<Derived, Key, T, kKeyFieldType,
+              kValueFieldType>::UnsafeShallowSwap(MapFieldBase* other) {
+  InternalSwap(down_cast<MapField*>(other));
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+void MapField<Derived, Key, T, kKeyFieldType, kValueFieldType>::InternalSwap(
+    MapField* other) {
+  MapFieldBase::InternalSwap(other);
+  impl_.InternalSwap(&other->impl_);
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+void MapField<Derived, Key, T, kKeyFieldType,
+              kValueFieldType>::SyncRepeatedFieldWithMapNoLock() const {
+  if (this->MapFieldBase::repeated_field_ == nullptr) {
+    this->MapFieldBase::repeated_field_ =
+        Arena::CreateMessage<RepeatedPtrField<Message> >(
+            this->MapFieldBase::arena_);
+  }
+  const Map<Key, T>& map = impl_.GetMap();
+  RepeatedPtrField<EntryType>* repeated_field =
+      reinterpret_cast<RepeatedPtrField<EntryType>*>(
+          this->MapFieldBase::repeated_field_);
+
+  repeated_field->Clear();
+
+  // The only way we can get at this point is through reflection and the
+  // only way we can get the reflection object is by having called GetReflection
+  // on the encompassing field. So that type must have existed and hence we
+  // know that this MapEntry default_type has also already been constructed.
+  // So it's safe to just call internal_default_instance().
+  const Message* default_entry = Derived::internal_default_instance();
+  for (typename Map<Key, T>::const_iterator it = map.begin(); it != map.end();
+       ++it) {
+    EntryType* new_entry =
+        down_cast<EntryType*>(default_entry->New(this->MapFieldBase::arena_));
+    repeated_field->AddAllocated(new_entry);
+    (*new_entry->mutable_key()) = it->first;
+    (*new_entry->mutable_value()) = it->second;
+  }
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+void MapField<Derived, Key, T, kKeyFieldType,
+              kValueFieldType>::SyncMapWithRepeatedFieldNoLock() const {
+  Map<Key, T>* map = const_cast<MapField*>(this)->impl_.MutableMap();
+  RepeatedPtrField<EntryType>* repeated_field =
+      reinterpret_cast<RepeatedPtrField<EntryType>*>(
+          this->MapFieldBase::repeated_field_);
+  GOOGLE_CHECK(this->MapFieldBase::repeated_field_ != nullptr);
+  map->clear();
+  for (typename RepeatedPtrField<EntryType>::iterator it =
+           repeated_field->begin();
+       it != repeated_field->end(); ++it) {
+    // Cast is needed because Map's api and internal storage is different when
+    // value is enum. For enum, we cannot cast an int to enum. Thus, we have to
+    // copy value. For other types, they have same exposed api type and internal
+    // stored type. We should not introduce value copy for them. We achieve this
+    // by casting to value for enum while casting to reference for other types.
+    (*map)[it->key()] = static_cast<CastValueType>(it->value());
+  }
+}
+
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+size_t MapField<Derived, Key, T, kKeyFieldType,
+                kValueFieldType>::SpaceUsedExcludingSelfNoLock() const {
+  size_t size = 0;
+  if (this->MapFieldBase::repeated_field_ != nullptr) {
+    size += this->MapFieldBase::repeated_field_->SpaceUsedExcludingSelfLong();
+  }
+  size += impl_.GetMap().SpaceUsedExcludingSelfLong();
+
+  return size;
+}
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_MAP_FIELD_INL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_field_lite.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_field_lite.h
new file mode 100644
index 0000000..53bf7a0
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_field_lite.h
@@ -0,0 +1,209 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_MAP_FIELD_LITE_H__
+#define GOOGLE_PROTOBUF_MAP_FIELD_LITE_H__
+
+#include <type_traits>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/map.h>
+#include <google/protobuf/map_entry_lite.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+#ifndef NDEBUG
+void MapFieldLiteNotDestructed(void* map_field_lite);
+#endif
+
+// This class provides access to map field using generated api. It is used for
+// internal generated message implementation only. Users should never use this
+// directly.
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType key_wire_type,
+          WireFormatLite::FieldType value_wire_type>
+class MapFieldLite {
+  // Define message type for internal repeated field.
+  typedef Derived EntryType;
+
+ public:
+  typedef Map<Key, T> MapType;
+
+  constexpr MapFieldLite() : map_() {}
+  explicit MapFieldLite(Arena* arena) : map_(arena) {}
+  MapFieldLite(ArenaInitialized, Arena* arena) : MapFieldLite(arena) {}
+
+#ifdef NDEBUG
+  void Destruct() { map_.~Map(); }
+  ~MapFieldLite() {}
+#else
+  void Destruct() {
+    // We want to destruct the map in such a way that we can verify
+    // that we've done that, but also be sure that we've deallocated
+    // everything (as opposed to leaving an allocation behind with no
+    // data in it, as would happen if a vector was resize'd to zero.
+    // Map::Swap with an empty map accomplishes that.
+    decltype(map_) swapped_map(map_.arena());
+    map_.InternalSwap(swapped_map);
+  }
+  ~MapFieldLite() {
+    if (map_.arena() == nullptr && !map_.empty()) {
+      MapFieldLiteNotDestructed(this);
+    }
+  }
+#endif
+  // Accessors
+  const Map<Key, T>& GetMap() const { return map_; }
+  Map<Key, T>* MutableMap() { return &map_; }
+
+  // Convenient methods for generated message implementation.
+  int size() const { return static_cast<int>(map_.size()); }
+  void Clear() { return map_.clear(); }
+  void MergeFrom(const MapFieldLite& other) {
+    for (typename Map<Key, T>::const_iterator it = other.map_.begin();
+         it != other.map_.end(); ++it) {
+      map_[it->first] = it->second;
+    }
+  }
+  void Swap(MapFieldLite* other) { map_.swap(other->map_); }
+  void InternalSwap(MapFieldLite* other) { map_.InternalSwap(other->map_); }
+
+  // Used in the implementation of parsing. Caller should take the ownership iff
+  // arena_ is nullptr.
+  EntryType* NewEntry() const {
+    return Arena::CreateMessage<EntryType>(map_.arena());
+  }
+
+  const char* _InternalParse(const char* ptr, ParseContext* ctx) {
+    typename Derived::template Parser<MapFieldLite, Map<Key, T>> parser(this);
+    return parser._InternalParse(ptr, ctx);
+  }
+
+  template <typename UnknownType>
+  const char* ParseWithEnumValidation(const char* ptr, ParseContext* ctx,
+                                      bool (*is_valid)(int), uint32_t field_num,
+                                      InternalMetadata* metadata) {
+    typename Derived::template Parser<MapFieldLite, Map<Key, T>> parser(this);
+    return parser.template ParseWithEnumValidation<UnknownType>(
+        ptr, ctx, is_valid, field_num, metadata);
+  }
+
+ private:
+  typedef void DestructorSkippable_;
+
+  // map_ is inside an anonymous union so we can explicitly control its
+  // destruction
+  union {
+    Map<Key, T> map_;
+  };
+
+  friend class ::PROTOBUF_NAMESPACE_ID::Arena;
+};
+
+template <typename UnknownType, typename T>
+struct EnumParseWrapper {
+  const char* _InternalParse(const char* ptr, ParseContext* ctx) {
+    return map_field->template ParseWithEnumValidation<UnknownType>(
+        ptr, ctx, is_valid, field_num, metadata);
+  }
+  T* map_field;
+  bool (*is_valid)(int);
+  uint32_t field_num;
+  InternalMetadata* metadata;
+};
+
+// Helper function because the typenames of maps are horrendous to print. This
+// leverages compiler type deduction, to keep all type data out of the
+// generated code
+template <typename UnknownType, typename T>
+EnumParseWrapper<UnknownType, T> InitEnumParseWrapper(
+    T* map_field, bool (*is_valid)(int), uint32_t field_num,
+    InternalMetadata* metadata) {
+  return EnumParseWrapper<UnknownType, T>{map_field, is_valid, field_num,
+                                          metadata};
+}
+
+// True if IsInitialized() is true for value field in all elements of t. T is
+// expected to be message.  It's useful to have this helper here to keep the
+// protobuf compiler from ever having to emit loops in IsInitialized() methods.
+// We want the C++ compiler to inline this or not as it sees fit.
+template <typename Derived, typename Key, typename T,
+          WireFormatLite::FieldType key_wire_type,
+          WireFormatLite::FieldType value_wire_type>
+bool AllAreInitialized(const MapFieldLite<Derived, Key, T, key_wire_type,
+                                          value_wire_type>& field) {
+  const auto& t = field.GetMap();
+  for (typename Map<Key, T>::const_iterator it = t.begin(); it != t.end();
+       ++it) {
+    if (!it->second.IsInitialized()) return false;
+  }
+  return true;
+}
+
+template <typename MEntry>
+struct MapEntryToMapField : MapEntryToMapField<typename MEntry::SuperType> {};
+
+template <typename T, typename Key, typename Value,
+          WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+struct MapEntryToMapField<
+    MapEntryLite<T, Key, Value, kKeyFieldType, kValueFieldType>> {
+  typedef MapFieldLite<
+      MapEntryLite<T, Key, Value, kKeyFieldType, kValueFieldType>, Key, Value,
+      kKeyFieldType, kValueFieldType>
+      MapFieldType;
+};
+
+#ifndef NDEBUG
+inline PROTOBUF_NOINLINE void MapFieldLiteNotDestructed(void* map_field_lite) {
+  bool proper_destruct = false;
+  GOOGLE_CHECK(proper_destruct) << map_field_lite;
+}
+#endif
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_MAP_FIELD_LITE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_type_handler.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_type_handler.h
new file mode 100644
index 0000000..c210c63
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/map_type_handler.h
@@ -0,0 +1,736 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_MAP_TYPE_HANDLER_H__
+#define GOOGLE_PROTOBUF_MAP_TYPE_HANDLER_H__
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/wire_format_lite.h>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Used for compile time type selection. MapIf::type will be TrueType if Flag is
+// true and FalseType otherwise.
+template <bool Flag, typename TrueType, typename FalseType>
+struct MapIf;
+
+template <typename TrueType, typename FalseType>
+struct MapIf<true, TrueType, FalseType> {
+  typedef TrueType type;
+};
+
+template <typename TrueType, typename FalseType>
+struct MapIf<false, TrueType, FalseType> {
+  typedef FalseType type;
+};
+
+template <typename Type, bool is_arena_constructable>
+class MapArenaMessageCreator {
+ public:
+  // Use arena to create message if Type is arena constructable. Otherwise,
+  // create the message on heap.
+  static inline Type* CreateMessage(Arena* arena);
+};
+template <typename Type>
+class MapArenaMessageCreator<Type, true> {
+ public:
+  static inline Type* CreateMessage(Arena* arena) {
+    return Arena::CreateMessage<Type>(arena);
+  }
+};
+template <typename Type>
+class MapArenaMessageCreator<Type, false> {
+ public:
+  static inline Type* CreateMessage(Arena* arena) {
+    return Arena::Create<Type>(arena);
+  }
+};
+
+// Define constants for given wire field type
+template <WireFormatLite::FieldType field_type, typename Type>
+class MapWireFieldTypeTraits {};
+
+#define TYPE_TRAITS(FieldType, CType, WireFormatType, IsMessage, IsEnum)   \
+  template <typename Type>                                                 \
+  class MapWireFieldTypeTraits<WireFormatLite::TYPE_##FieldType, Type> {   \
+   public:                                                                 \
+    static const bool kIsMessage = IsMessage;                              \
+    static const bool kIsEnum = IsEnum;                                    \
+    typedef typename MapIf<kIsMessage, Type*, CType>::type TypeOnMemory;   \
+    typedef typename MapIf<kIsEnum, int, Type>::type MapEntryAccessorType; \
+    static const WireFormatLite::WireType kWireType =                      \
+        WireFormatLite::WIRETYPE_##WireFormatType;                         \
+  };
+
+TYPE_TRAITS(MESSAGE, Type, LENGTH_DELIMITED, true, false)
+TYPE_TRAITS(STRING, ArenaStringPtr, LENGTH_DELIMITED, false, false)
+TYPE_TRAITS(BYTES, ArenaStringPtr, LENGTH_DELIMITED, false, false)
+TYPE_TRAITS(INT64, int64_t, VARINT, false, false)
+TYPE_TRAITS(UINT64, uint64_t, VARINT, false, false)
+TYPE_TRAITS(INT32, int32_t, VARINT, false, false)
+TYPE_TRAITS(UINT32, uint32_t, VARINT, false, false)
+TYPE_TRAITS(SINT64, int64_t, VARINT, false, false)
+TYPE_TRAITS(SINT32, int32_t, VARINT, false, false)
+TYPE_TRAITS(ENUM, int, VARINT, false, true)
+TYPE_TRAITS(DOUBLE, double, FIXED64, false, false)
+TYPE_TRAITS(FLOAT, float, FIXED32, false, false)
+TYPE_TRAITS(FIXED64, uint64_t, FIXED64, false, false)
+TYPE_TRAITS(FIXED32, uint32_t, FIXED32, false, false)
+TYPE_TRAITS(SFIXED64, int64_t, FIXED64, false, false)
+TYPE_TRAITS(SFIXED32, int32_t, FIXED32, false, false)
+TYPE_TRAITS(BOOL, bool, VARINT, false, false)
+
+#undef TYPE_TRAITS
+
+template <WireFormatLite::FieldType field_type, typename Type>
+class MapTypeHandler {};
+
+template <typename Type>
+class MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type> {
+ public:
+  // Enum type cannot be used for MapTypeHandler::Read. Define a type which will
+  // replace Enum with int.
+  typedef typename MapWireFieldTypeTraits<WireFormatLite::TYPE_MESSAGE,
+                                          Type>::MapEntryAccessorType
+      MapEntryAccessorType;
+  // Internal stored type in MapEntryLite for given wire field type.
+  typedef typename MapWireFieldTypeTraits<WireFormatLite::TYPE_MESSAGE,
+                                          Type>::TypeOnMemory TypeOnMemory;
+  // Corresponding wire type for field type.
+  static constexpr WireFormatLite::WireType kWireType =
+      MapWireFieldTypeTraits<WireFormatLite::TYPE_MESSAGE, Type>::kWireType;
+  // Whether wire type is for message.
+  static constexpr bool kIsMessage =
+      MapWireFieldTypeTraits<WireFormatLite::TYPE_MESSAGE, Type>::kIsMessage;
+  // Whether wire type is for enum.
+  static constexpr bool kIsEnum =
+      MapWireFieldTypeTraits<WireFormatLite::TYPE_MESSAGE, Type>::kIsEnum;
+
+  // Functions used in parsing and serialization. ===================
+  static inline size_t ByteSize(const MapEntryAccessorType& value);
+  static inline int GetCachedSize(const MapEntryAccessorType& value);
+  static inline bool Read(io::CodedInputStream* input,
+                          MapEntryAccessorType* value);
+  static inline const char* Read(const char* ptr, ParseContext* ctx,
+                                 MapEntryAccessorType* value);
+
+  static inline uint8_t* Write(int field, const MapEntryAccessorType& value,
+                               uint8_t* ptr, io::EpsCopyOutputStream* stream);
+
+  // Functions to manipulate data on memory. ========================
+  static inline const Type& GetExternalReference(const Type* value);
+  static inline void DeleteNoArena(const Type* x);
+  static inline void Merge(const Type& from, Type** to, Arena* arena);
+  static inline void Clear(Type** value, Arena* arena);
+  static constexpr TypeOnMemory Constinit();
+
+  static inline Type* EnsureMutable(Type** value, Arena* arena);
+  // SpaceUsedInMapEntry: Return bytes used by value in MapEntry, excluding
+  // those already calculate in sizeof(MapField).
+  static inline size_t SpaceUsedInMapEntryLong(const Type* value);
+  // Return default instance if value is not initialized when calling const
+  // reference accessor.
+  static inline const Type& DefaultIfNotInitialized(const Type* value);
+  // Check if all required fields have values set.
+  static inline bool IsInitialized(Type* value);
+};
+
+#define MAP_HANDLER(FieldType)                                                 \
+  template <typename Type>                                                     \
+  class MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type> {               \
+   public:                                                                     \
+    typedef typename MapWireFieldTypeTraits<WireFormatLite::TYPE_##FieldType,  \
+                                            Type>::MapEntryAccessorType        \
+        MapEntryAccessorType;                                                  \
+    typedef typename MapWireFieldTypeTraits<WireFormatLite::TYPE_##FieldType,  \
+                                            Type>::TypeOnMemory TypeOnMemory;  \
+    static const WireFormatLite::WireType kWireType =                          \
+        MapWireFieldTypeTraits<WireFormatLite::TYPE_##FieldType,               \
+                               Type>::kWireType;                               \
+    static const bool kIsMessage =                                             \
+        MapWireFieldTypeTraits<WireFormatLite::TYPE_##FieldType,               \
+                               Type>::kIsMessage;                              \
+    static const bool kIsEnum =                                                \
+        MapWireFieldTypeTraits<WireFormatLite::TYPE_##FieldType,               \
+                               Type>::kIsEnum;                                 \
+    static inline int ByteSize(const MapEntryAccessorType& value);             \
+    static inline int GetCachedSize(const MapEntryAccessorType& value);        \
+    static inline bool Read(io::CodedInputStream* input,                       \
+                            MapEntryAccessorType* value);                      \
+    static inline const char* Read(const char* begin, ParseContext* ctx,       \
+                                   MapEntryAccessorType* value);               \
+    static inline uint8_t* Write(int field, const MapEntryAccessorType& value, \
+                                 uint8_t* ptr,                                 \
+                                 io::EpsCopyOutputStream* stream);             \
+    static inline const MapEntryAccessorType& GetExternalReference(            \
+        const TypeOnMemory& value);                                            \
+    static inline void DeleteNoArena(const TypeOnMemory& x);                   \
+    static inline void Merge(const MapEntryAccessorType& from,                 \
+                             TypeOnMemory* to, Arena* arena);                  \
+    static inline void Clear(TypeOnMemory* value, Arena* arena);               \
+    static inline size_t SpaceUsedInMapEntryLong(const TypeOnMemory& value);   \
+    static inline const MapEntryAccessorType& DefaultIfNotInitialized(         \
+        const TypeOnMemory& value);                                            \
+    static inline bool IsInitialized(const TypeOnMemory& value);               \
+    static void DeleteNoArena(TypeOnMemory& value);                            \
+    static constexpr TypeOnMemory Constinit();                                 \
+    static inline MapEntryAccessorType* EnsureMutable(TypeOnMemory* value,     \
+                                                      Arena* arena);           \
+  };
+MAP_HANDLER(STRING)
+MAP_HANDLER(BYTES)
+MAP_HANDLER(INT64)
+MAP_HANDLER(UINT64)
+MAP_HANDLER(INT32)
+MAP_HANDLER(UINT32)
+MAP_HANDLER(SINT64)
+MAP_HANDLER(SINT32)
+MAP_HANDLER(ENUM)
+MAP_HANDLER(DOUBLE)
+MAP_HANDLER(FLOAT)
+MAP_HANDLER(FIXED64)
+MAP_HANDLER(FIXED32)
+MAP_HANDLER(SFIXED64)
+MAP_HANDLER(SFIXED32)
+MAP_HANDLER(BOOL)
+#undef MAP_HANDLER
+
+template <typename Type>
+inline size_t MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::ByteSize(
+    const MapEntryAccessorType& value) {
+  return WireFormatLite::MessageSizeNoVirtual(value);
+}
+
+#define GOOGLE_PROTOBUF_BYTE_SIZE(FieldType, DeclaredType)                     \
+  template <typename Type>                                                     \
+  inline int MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::ByteSize( \
+      const MapEntryAccessorType& value) {                                     \
+    return static_cast<int>(WireFormatLite::DeclaredType##Size(value));        \
+  }
+
+GOOGLE_PROTOBUF_BYTE_SIZE(STRING, String)
+GOOGLE_PROTOBUF_BYTE_SIZE(BYTES, Bytes)
+GOOGLE_PROTOBUF_BYTE_SIZE(INT64, Int64)
+GOOGLE_PROTOBUF_BYTE_SIZE(UINT64, UInt64)
+GOOGLE_PROTOBUF_BYTE_SIZE(INT32, Int32)
+GOOGLE_PROTOBUF_BYTE_SIZE(UINT32, UInt32)
+GOOGLE_PROTOBUF_BYTE_SIZE(SINT64, SInt64)
+GOOGLE_PROTOBUF_BYTE_SIZE(SINT32, SInt32)
+GOOGLE_PROTOBUF_BYTE_SIZE(ENUM, Enum)
+
+#undef GOOGLE_PROTOBUF_BYTE_SIZE
+
+#define FIXED_BYTE_SIZE(FieldType, DeclaredType)                               \
+  template <typename Type>                                                     \
+  inline int MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::ByteSize( \
+      const MapEntryAccessorType& /* value */) {                               \
+    return WireFormatLite::k##DeclaredType##Size;                              \
+  }
+
+FIXED_BYTE_SIZE(DOUBLE, Double)
+FIXED_BYTE_SIZE(FLOAT, Float)
+FIXED_BYTE_SIZE(FIXED64, Fixed64)
+FIXED_BYTE_SIZE(FIXED32, Fixed32)
+FIXED_BYTE_SIZE(SFIXED64, SFixed64)
+FIXED_BYTE_SIZE(SFIXED32, SFixed32)
+FIXED_BYTE_SIZE(BOOL, Bool)
+
+#undef FIXED_BYTE_SIZE
+
+template <typename Type>
+inline int MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::GetCachedSize(
+    const MapEntryAccessorType& value) {
+  return static_cast<int>(WireFormatLite::LengthDelimitedSize(
+      static_cast<size_t>(value.GetCachedSize())));
+}
+
+#define GET_CACHED_SIZE(FieldType, DeclaredType)                         \
+  template <typename Type>                                               \
+  inline int                                                             \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::GetCachedSize( \
+      const MapEntryAccessorType& value) {                               \
+    return static_cast<int>(WireFormatLite::DeclaredType##Size(value));  \
+  }
+
+GET_CACHED_SIZE(STRING, String)
+GET_CACHED_SIZE(BYTES, Bytes)
+GET_CACHED_SIZE(INT64, Int64)
+GET_CACHED_SIZE(UINT64, UInt64)
+GET_CACHED_SIZE(INT32, Int32)
+GET_CACHED_SIZE(UINT32, UInt32)
+GET_CACHED_SIZE(SINT64, SInt64)
+GET_CACHED_SIZE(SINT32, SInt32)
+GET_CACHED_SIZE(ENUM, Enum)
+
+#undef GET_CACHED_SIZE
+
+#define GET_FIXED_CACHED_SIZE(FieldType, DeclaredType)                   \
+  template <typename Type>                                               \
+  inline int                                                             \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::GetCachedSize( \
+      const MapEntryAccessorType& /* value */) {                         \
+    return WireFormatLite::k##DeclaredType##Size;                        \
+  }
+
+GET_FIXED_CACHED_SIZE(DOUBLE, Double)
+GET_FIXED_CACHED_SIZE(FLOAT, Float)
+GET_FIXED_CACHED_SIZE(FIXED64, Fixed64)
+GET_FIXED_CACHED_SIZE(FIXED32, Fixed32)
+GET_FIXED_CACHED_SIZE(SFIXED64, SFixed64)
+GET_FIXED_CACHED_SIZE(SFIXED32, SFixed32)
+GET_FIXED_CACHED_SIZE(BOOL, Bool)
+
+#undef GET_FIXED_CACHED_SIZE
+
+template <typename Type>
+inline uint8_t* MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::Write(
+    int field, const MapEntryAccessorType& value, uint8_t* ptr,
+    io::EpsCopyOutputStream* stream) {
+  ptr = stream->EnsureSpace(ptr);
+  return WireFormatLite::InternalWriteMessage(
+      field, value, value.GetCachedSize(), ptr, stream);
+}
+
+#define WRITE_METHOD(FieldType, DeclaredType)                     \
+  template <typename Type>                                        \
+  inline uint8_t*                                                 \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::Write(  \
+      int field, const MapEntryAccessorType& value, uint8_t* ptr, \
+      io::EpsCopyOutputStream* stream) {                          \
+    ptr = stream->EnsureSpace(ptr);                               \
+    return stream->Write##DeclaredType(field, value, ptr);        \
+  }
+
+WRITE_METHOD(STRING, String)
+WRITE_METHOD(BYTES, Bytes)
+
+#undef WRITE_METHOD
+#define WRITE_METHOD(FieldType, DeclaredType)                               \
+  template <typename Type>                                                  \
+  inline uint8_t*                                                           \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::Write(            \
+      int field, const MapEntryAccessorType& value, uint8_t* ptr,           \
+      io::EpsCopyOutputStream* stream) {                                    \
+    ptr = stream->EnsureSpace(ptr);                                         \
+    return WireFormatLite::Write##DeclaredType##ToArray(field, value, ptr); \
+  }
+
+WRITE_METHOD(INT64, Int64)
+WRITE_METHOD(UINT64, UInt64)
+WRITE_METHOD(INT32, Int32)
+WRITE_METHOD(UINT32, UInt32)
+WRITE_METHOD(SINT64, SInt64)
+WRITE_METHOD(SINT32, SInt32)
+WRITE_METHOD(ENUM, Enum)
+WRITE_METHOD(DOUBLE, Double)
+WRITE_METHOD(FLOAT, Float)
+WRITE_METHOD(FIXED64, Fixed64)
+WRITE_METHOD(FIXED32, Fixed32)
+WRITE_METHOD(SFIXED64, SFixed64)
+WRITE_METHOD(SFIXED32, SFixed32)
+WRITE_METHOD(BOOL, Bool)
+
+#undef WRITE_METHOD
+
+template <typename Type>
+inline bool MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::Read(
+    io::CodedInputStream* input, MapEntryAccessorType* value) {
+  return WireFormatLite::ReadMessageNoVirtual(input, value);
+}
+
+template <typename Type>
+inline bool MapTypeHandler<WireFormatLite::TYPE_STRING, Type>::Read(
+    io::CodedInputStream* input, MapEntryAccessorType* value) {
+  return WireFormatLite::ReadString(input, value);
+}
+
+template <typename Type>
+inline bool MapTypeHandler<WireFormatLite::TYPE_BYTES, Type>::Read(
+    io::CodedInputStream* input, MapEntryAccessorType* value) {
+  return WireFormatLite::ReadBytes(input, value);
+}
+
+template <typename Type>
+const char* MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::Read(
+    const char* ptr, ParseContext* ctx, MapEntryAccessorType* value) {
+  return ctx->ParseMessage(value, ptr);
+}
+
+template <typename Type>
+const char* MapTypeHandler<WireFormatLite::TYPE_STRING, Type>::Read(
+    const char* ptr, ParseContext* ctx, MapEntryAccessorType* value) {
+  int size = ReadSize(&ptr);
+  GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+  return ctx->ReadString(ptr, size, value);
+}
+
+template <typename Type>
+const char* MapTypeHandler<WireFormatLite::TYPE_BYTES, Type>::Read(
+    const char* ptr, ParseContext* ctx, MapEntryAccessorType* value) {
+  int size = ReadSize(&ptr);
+  GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+  return ctx->ReadString(ptr, size, value);
+}
+
+inline const char* ReadINT64(const char* ptr, int64_t* value) {
+  return VarintParse(ptr, reinterpret_cast<uint64_t*>(value));
+}
+inline const char* ReadUINT64(const char* ptr, uint64_t* value) {
+  return VarintParse(ptr, value);
+}
+inline const char* ReadINT32(const char* ptr, int32_t* value) {
+  return VarintParse(ptr, reinterpret_cast<uint32_t*>(value));
+}
+inline const char* ReadUINT32(const char* ptr, uint32_t* value) {
+  return VarintParse(ptr, value);
+}
+inline const char* ReadSINT64(const char* ptr, int64_t* value) {
+  *value = ReadVarintZigZag64(&ptr);
+  return ptr;
+}
+inline const char* ReadSINT32(const char* ptr, int32_t* value) {
+  *value = ReadVarintZigZag32(&ptr);
+  return ptr;
+}
+template <typename E>
+inline const char* ReadENUM(const char* ptr, E* value) {
+  *value = static_cast<E>(ReadVarint32(&ptr));
+  return ptr;
+}
+inline const char* ReadBOOL(const char* ptr, bool* value) {
+  *value = static_cast<bool>(ReadVarint32(&ptr));
+  return ptr;
+}
+
+template <typename F>
+inline const char* ReadUnaligned(const char* ptr, F* value) {
+  *value = UnalignedLoad<F>(ptr);
+  return ptr + sizeof(F);
+}
+inline const char* ReadFLOAT(const char* ptr, float* value) {
+  return ReadUnaligned(ptr, value);
+}
+inline const char* ReadDOUBLE(const char* ptr, double* value) {
+  return ReadUnaligned(ptr, value);
+}
+inline const char* ReadFIXED64(const char* ptr, uint64_t* value) {
+  return ReadUnaligned(ptr, value);
+}
+inline const char* ReadFIXED32(const char* ptr, uint32_t* value) {
+  return ReadUnaligned(ptr, value);
+}
+inline const char* ReadSFIXED64(const char* ptr, int64_t* value) {
+  return ReadUnaligned(ptr, value);
+}
+inline const char* ReadSFIXED32(const char* ptr, int32_t* value) {
+  return ReadUnaligned(ptr, value);
+}
+
+#define READ_METHOD(FieldType)                                              \
+  template <typename Type>                                                  \
+  inline bool MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::Read( \
+      io::CodedInputStream* input, MapEntryAccessorType* value) {           \
+    return WireFormatLite::ReadPrimitive<TypeOnMemory,                      \
+                                         WireFormatLite::TYPE_##FieldType>( \
+        input, value);                                                      \
+  }                                                                         \
+  template <typename Type>                                                  \
+  const char* MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::Read( \
+      const char* begin, ParseContext* ctx, MapEntryAccessorType* value) {  \
+    (void)ctx;                                                              \
+    return Read##FieldType(begin, value);                                   \
+  }
+
+READ_METHOD(INT64)
+READ_METHOD(UINT64)
+READ_METHOD(INT32)
+READ_METHOD(UINT32)
+READ_METHOD(SINT64)
+READ_METHOD(SINT32)
+READ_METHOD(ENUM)
+READ_METHOD(DOUBLE)
+READ_METHOD(FLOAT)
+READ_METHOD(FIXED64)
+READ_METHOD(FIXED32)
+READ_METHOD(SFIXED64)
+READ_METHOD(SFIXED32)
+READ_METHOD(BOOL)
+
+#undef READ_METHOD
+
+// Definition for message handler
+
+template <typename Type>
+inline const Type&
+MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::GetExternalReference(
+    const Type* value) {
+  return *value;
+}
+
+template <typename Type>
+inline size_t MapTypeHandler<WireFormatLite::TYPE_MESSAGE,
+                             Type>::SpaceUsedInMapEntryLong(const Type* value) {
+  return value->SpaceUsedLong();
+}
+
+template <typename Type>
+inline void MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::Clear(
+    Type** value, Arena* /* arena */) {
+  if (*value != nullptr) (*value)->Clear();
+}
+template <typename Type>
+inline void MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::Merge(
+    const Type& from, Type** to, Arena* /* arena */) {
+  (*to)->MergeFrom(from);
+}
+
+template <typename Type>
+void MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::DeleteNoArena(
+    const Type* ptr) {
+  delete ptr;
+}
+
+template <typename Type>
+constexpr auto MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::Constinit()
+    -> TypeOnMemory {
+  return nullptr;
+}
+
+template <typename Type>
+inline Type* MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::EnsureMutable(
+    Type** value, Arena* arena) {
+  if (*value == nullptr) {
+    *value = MapArenaMessageCreator<
+        Type,
+        Arena::is_arena_constructable<Type>::type::value>::CreateMessage(arena);
+  }
+  return *value;
+}
+
+template <typename Type>
+inline const Type&
+MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::DefaultIfNotInitialized(
+    const Type* value) {
+  return value != nullptr ? *value : *Type::internal_default_instance();
+}
+
+template <typename Type>
+inline bool MapTypeHandler<WireFormatLite::TYPE_MESSAGE, Type>::IsInitialized(
+    Type* value) {
+  return value ? value->IsInitialized() : false;
+}
+
+// Definition for string/bytes handler
+
+#define STRING_OR_BYTES_HANDLER_FUNCTIONS(FieldType)                          \
+  template <typename Type>                                                    \
+  inline const typename MapTypeHandler<WireFormatLite::TYPE_##FieldType,      \
+                                       Type>::MapEntryAccessorType&           \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType,                            \
+                 Type>::GetExternalReference(const TypeOnMemory& value) {     \
+    return value.Get();                                                       \
+  }                                                                           \
+  template <typename Type>                                                    \
+  inline size_t                                                               \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType,                            \
+                 Type>::SpaceUsedInMapEntryLong(const TypeOnMemory& value) {  \
+    return sizeof(value);                                                     \
+  }                                                                           \
+  template <typename Type>                                                    \
+  inline void MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::Clear(  \
+      TypeOnMemory* value, Arena* /* arena */) {                              \
+    value->ClearToEmpty();                                                    \
+  }                                                                           \
+  template <typename Type>                                                    \
+  inline void MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::Merge(  \
+      const MapEntryAccessorType& from, TypeOnMemory* to, Arena* arena) {     \
+    to->Set(from, arena);                                                     \
+  }                                                                           \
+  template <typename Type>                                                    \
+  void MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::DeleteNoArena( \
+      TypeOnMemory& value) {                                                  \
+    value.Destroy();                                                          \
+  }                                                                           \
+  template <typename Type>                                                    \
+  constexpr auto                                                              \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::Constinit()         \
+      ->TypeOnMemory {                                                        \
+    return TypeOnMemory(&internal::fixed_address_empty_string,                \
+                        ConstantInitialized{});                               \
+  }                                                                           \
+  template <typename Type>                                                    \
+  inline typename MapTypeHandler<WireFormatLite::TYPE_##FieldType,            \
+                                 Type>::MapEntryAccessorType*                 \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::EnsureMutable(      \
+      TypeOnMemory* value, Arena* arena) {                                    \
+    return value->Mutable(arena);                                             \
+  }                                                                           \
+  template <typename Type>                                                    \
+  inline const typename MapTypeHandler<WireFormatLite::TYPE_##FieldType,      \
+                                       Type>::MapEntryAccessorType&           \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType,                            \
+                 Type>::DefaultIfNotInitialized(const TypeOnMemory& value) {  \
+    return value.Get();                                                       \
+  }                                                                           \
+  template <typename Type>                                                    \
+  inline bool                                                                 \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::IsInitialized(      \
+      const TypeOnMemory& /* value */) {                                      \
+    return true;                                                              \
+  }
+STRING_OR_BYTES_HANDLER_FUNCTIONS(STRING)
+STRING_OR_BYTES_HANDLER_FUNCTIONS(BYTES)
+#undef STRING_OR_BYTES_HANDLER_FUNCTIONS
+
+#define PRIMITIVE_HANDLER_FUNCTIONS(FieldType)                               \
+  template <typename Type>                                                   \
+  inline const typename MapTypeHandler<WireFormatLite::TYPE_##FieldType,     \
+                                       Type>::MapEntryAccessorType&          \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType,                           \
+                 Type>::GetExternalReference(const TypeOnMemory& value) {    \
+    return value;                                                            \
+  }                                                                          \
+  template <typename Type>                                                   \
+  inline size_t MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::     \
+      SpaceUsedInMapEntryLong(const TypeOnMemory& /* value */) {             \
+    return 0;                                                                \
+  }                                                                          \
+  template <typename Type>                                                   \
+  inline void MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::Clear( \
+      TypeOnMemory* value, Arena* /* arena */) {                             \
+    *value = 0;                                                              \
+  }                                                                          \
+  template <typename Type>                                                   \
+  inline void MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::Merge( \
+      const MapEntryAccessorType& from, TypeOnMemory* to,                    \
+      Arena* /* arena */) {                                                  \
+    *to = from;                                                              \
+  }                                                                          \
+  template <typename Type>                                                   \
+  inline void MapTypeHandler<WireFormatLite::TYPE_##FieldType,               \
+                             Type>::DeleteNoArena(TypeOnMemory& /* x */) {}  \
+  template <typename Type>                                                   \
+  constexpr auto                                                             \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::Constinit()        \
+      ->TypeOnMemory {                                                       \
+    return 0;                                                                \
+  }                                                                          \
+  template <typename Type>                                                   \
+  inline typename MapTypeHandler<WireFormatLite::TYPE_##FieldType,           \
+                                 Type>::MapEntryAccessorType*                \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::EnsureMutable(     \
+      TypeOnMemory* value, Arena* /* arena */) {                             \
+    return value;                                                            \
+  }                                                                          \
+  template <typename Type>                                                   \
+  inline const typename MapTypeHandler<WireFormatLite::TYPE_##FieldType,     \
+                                       Type>::MapEntryAccessorType&          \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType,                           \
+                 Type>::DefaultIfNotInitialized(const TypeOnMemory& value) { \
+    return value;                                                            \
+  }                                                                          \
+  template <typename Type>                                                   \
+  inline bool                                                                \
+  MapTypeHandler<WireFormatLite::TYPE_##FieldType, Type>::IsInitialized(     \
+      const TypeOnMemory& /* value */) {                                     \
+    return true;                                                             \
+  }
+PRIMITIVE_HANDLER_FUNCTIONS(INT64)
+PRIMITIVE_HANDLER_FUNCTIONS(UINT64)
+PRIMITIVE_HANDLER_FUNCTIONS(INT32)
+PRIMITIVE_HANDLER_FUNCTIONS(UINT32)
+PRIMITIVE_HANDLER_FUNCTIONS(SINT64)
+PRIMITIVE_HANDLER_FUNCTIONS(SINT32)
+PRIMITIVE_HANDLER_FUNCTIONS(ENUM)
+PRIMITIVE_HANDLER_FUNCTIONS(DOUBLE)
+PRIMITIVE_HANDLER_FUNCTIONS(FLOAT)
+PRIMITIVE_HANDLER_FUNCTIONS(FIXED64)
+PRIMITIVE_HANDLER_FUNCTIONS(FIXED32)
+PRIMITIVE_HANDLER_FUNCTIONS(SFIXED64)
+PRIMITIVE_HANDLER_FUNCTIONS(SFIXED32)
+PRIMITIVE_HANDLER_FUNCTIONS(BOOL)
+#undef PRIMITIVE_HANDLER_FUNCTIONS
+
+// Functions for operating on a map entry using type handlers.
+//
+// Does not contain any representation (this class is not intended to be
+// instantiated).
+template <typename Key, typename Value, WireFormatLite::FieldType kKeyFieldType,
+          WireFormatLite::FieldType kValueFieldType>
+struct MapEntryFuncs {
+  typedef MapTypeHandler<kKeyFieldType, Key> KeyTypeHandler;
+  typedef MapTypeHandler<kValueFieldType, Value> ValueTypeHandler;
+  enum : int {
+    kKeyFieldNumber = 1,
+    kValueFieldNumber = 2
+  };
+
+  static uint8_t* InternalSerialize(int field_number, const Key& key,
+                                    const Value& value, uint8_t* ptr,
+                                    io::EpsCopyOutputStream* stream) {
+    ptr = stream->EnsureSpace(ptr);
+    ptr = WireFormatLite::WriteTagToArray(
+        field_number, WireFormatLite::WIRETYPE_LENGTH_DELIMITED, ptr);
+    ptr = io::CodedOutputStream::WriteVarint32ToArray(GetCachedSize(key, value),
+                                                      ptr);
+
+    ptr = KeyTypeHandler::Write(kKeyFieldNumber, key, ptr, stream);
+    return ValueTypeHandler::Write(kValueFieldNumber, value, ptr, stream);
+  }
+
+  static size_t ByteSizeLong(const Key& key, const Value& value) {
+    // Tags for key and value will both be one byte (field numbers 1 and 2).
+    size_t inner_length =
+        2 + KeyTypeHandler::ByteSize(key) + ValueTypeHandler::ByteSize(value);
+    return inner_length + io::CodedOutputStream::VarintSize32(
+                              static_cast<uint32_t>(inner_length));
+  }
+
+  static int GetCachedSize(const Key& key, const Value& value) {
+    // Tags for key and value will both be one byte (field numbers 1 and 2).
+    return 2 + KeyTypeHandler::GetCachedSize(key) +
+           ValueTypeHandler::GetCachedSize(value);
+  }
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_MAP_TYPE_HANDLER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/message.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/message.h
new file mode 100644
index 0000000..39ec154
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/message.h
@@ -0,0 +1,1497 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Defines Message, the abstract interface implemented by non-lite
+// protocol message objects.  Although it's possible to implement this
+// interface manually, most users will use the protocol compiler to
+// generate implementations.
+//
+// Example usage:
+//
+// Say you have a message defined as:
+//
+//   message Foo {
+//     optional string text = 1;
+//     repeated int32 numbers = 2;
+//   }
+//
+// Then, if you used the protocol compiler to generate a class from the above
+// definition, you could use it like so:
+//
+//   std::string data;  // Will store a serialized version of the message.
+//
+//   {
+//     // Create a message and serialize it.
+//     Foo foo;
+//     foo.set_text("Hello World!");
+//     foo.add_numbers(1);
+//     foo.add_numbers(5);
+//     foo.add_numbers(42);
+//
+//     foo.SerializeToString(&data);
+//   }
+//
+//   {
+//     // Parse the serialized message and check that it contains the
+//     // correct data.
+//     Foo foo;
+//     foo.ParseFromString(data);
+//
+//     assert(foo.text() == "Hello World!");
+//     assert(foo.numbers_size() == 3);
+//     assert(foo.numbers(0) == 1);
+//     assert(foo.numbers(1) == 5);
+//     assert(foo.numbers(2) == 42);
+//   }
+//
+//   {
+//     // Same as the last block, but do it dynamically via the Message
+//     // reflection interface.
+//     Message* foo = new Foo;
+//     const Descriptor* descriptor = foo->GetDescriptor();
+//
+//     // Get the descriptors for the fields we're interested in and verify
+//     // their types.
+//     const FieldDescriptor* text_field = descriptor->FindFieldByName("text");
+//     assert(text_field != nullptr);
+//     assert(text_field->type() == FieldDescriptor::TYPE_STRING);
+//     assert(text_field->label() == FieldDescriptor::LABEL_OPTIONAL);
+//     const FieldDescriptor* numbers_field = descriptor->
+//                                            FindFieldByName("numbers");
+//     assert(numbers_field != nullptr);
+//     assert(numbers_field->type() == FieldDescriptor::TYPE_INT32);
+//     assert(numbers_field->label() == FieldDescriptor::LABEL_REPEATED);
+//
+//     // Parse the message.
+//     foo->ParseFromString(data);
+//
+//     // Use the reflection interface to examine the contents.
+//     const Reflection* reflection = foo->GetReflection();
+//     assert(reflection->GetString(*foo, text_field) == "Hello World!");
+//     assert(reflection->FieldSize(*foo, numbers_field) == 3);
+//     assert(reflection->GetRepeatedInt32(*foo, numbers_field, 0) == 1);
+//     assert(reflection->GetRepeatedInt32(*foo, numbers_field, 1) == 5);
+//     assert(reflection->GetRepeatedInt32(*foo, numbers_field, 2) == 42);
+//
+//     delete foo;
+//   }
+
+#ifndef GOOGLE_PROTOBUF_MESSAGE_H__
+#define GOOGLE_PROTOBUF_MESSAGE_H__
+
+
+#include <iosfwd>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/map.h>  // TODO(b/211442718): cleanup
+#include <google/protobuf/message_lite.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+
+// Defined in this file.
+class Message;
+class Reflection;
+class MessageFactory;
+
+// Defined in other files.
+class AssignDescriptorsHelper;
+class DynamicMessageFactory;
+class GeneratedMessageReflectionTestHelper;
+class MapKey;
+class MapValueConstRef;
+class MapValueRef;
+class MapIterator;
+class MapReflectionTester;
+
+namespace internal {
+struct DescriptorTable;
+class MapFieldBase;
+class SwapFieldHelper;
+class CachedSize;
+}  // namespace internal
+class UnknownFieldSet;  // unknown_field_set.h
+namespace io {
+class ZeroCopyInputStream;   // zero_copy_stream.h
+class ZeroCopyOutputStream;  // zero_copy_stream.h
+class CodedInputStream;      // coded_stream.h
+class CodedOutputStream;     // coded_stream.h
+}  // namespace io
+namespace python {
+class MapReflectionFriend;  // scalar_map_container.h
+class MessageReflectionFriend;
+}  // namespace python
+namespace expr {
+class CelMapReflectionFriend;  // field_backed_map_impl.cc
+}
+
+namespace internal {
+class MapFieldPrinterHelper;  // text_format.cc
+}
+namespace util {
+class MessageDifferencer;
+}
+
+
+namespace internal {
+class ReflectionAccessor;      // message.cc
+class ReflectionOps;           // reflection_ops.h
+class MapKeySorter;            // wire_format.cc
+class WireFormat;              // wire_format.h
+class MapFieldReflectionTest;  // map_test.cc
+}  // namespace internal
+
+template <typename T>
+class RepeatedField;  // repeated_field.h
+
+template <typename T>
+class RepeatedPtrField;  // repeated_field.h
+
+// A container to hold message metadata.
+struct Metadata {
+  const Descriptor* descriptor;
+  const Reflection* reflection;
+};
+
+namespace internal {
+template <class To>
+inline To* GetPointerAtOffset(Message* message, uint32_t offset) {
+  return reinterpret_cast<To*>(reinterpret_cast<char*>(message) + offset);
+}
+
+template <class To>
+const To* GetConstPointerAtOffset(const Message* message, uint32_t offset) {
+  return reinterpret_cast<const To*>(reinterpret_cast<const char*>(message) +
+                                     offset);
+}
+
+template <class To>
+const To& GetConstRefAtOffset(const Message& message, uint32_t offset) {
+  return *GetConstPointerAtOffset<To>(&message, offset);
+}
+
+bool CreateUnknownEnumValues(const FieldDescriptor* field);
+
+// Returns true if "message" is a descendant of "root".
+PROTOBUF_EXPORT bool IsDescendant(Message& root, const Message& message);
+}  // namespace internal
+
+// Abstract interface for protocol messages.
+//
+// See also MessageLite, which contains most every-day operations.  Message
+// adds descriptors and reflection on top of that.
+//
+// The methods of this class that are virtual but not pure-virtual have
+// default implementations based on reflection.  Message classes which are
+// optimized for speed will want to override these with faster implementations,
+// but classes optimized for code size may be happy with keeping them.  See
+// the optimize_for option in descriptor.proto.
+//
+// Users must not derive from this class. Only the protocol compiler and
+// the internal library are allowed to create subclasses.
+class PROTOBUF_EXPORT Message : public MessageLite {
+ public:
+  constexpr Message() {}
+
+  // Basic Operations ------------------------------------------------
+
+  // Construct a new instance of the same type.  Ownership is passed to the
+  // caller.  (This is also defined in MessageLite, but is defined again here
+  // for return-type covariance.)
+  Message* New() const { return New(nullptr); }
+
+  // Construct a new instance on the arena. Ownership is passed to the caller
+  // if arena is a nullptr.
+  Message* New(Arena* arena) const override = 0;
+
+  // Make this message into a copy of the given message.  The given message
+  // must have the same descriptor, but need not necessarily be the same class.
+  // By default this is just implemented as "Clear(); MergeFrom(from);".
+  void CopyFrom(const Message& from);
+
+  // Merge the fields from the given message into this message.  Singular
+  // fields will be overwritten, if specified in from, except for embedded
+  // messages which will be merged.  Repeated fields will be concatenated.
+  // The given message must be of the same type as this message (i.e. the
+  // exact same class).
+  virtual void MergeFrom(const Message& from);
+
+  // Verifies that IsInitialized() returns true.  GOOGLE_CHECK-fails otherwise, with
+  // a nice error message.
+  void CheckInitialized() const;
+
+  // Slowly build a list of all required fields that are not set.
+  // This is much, much slower than IsInitialized() as it is implemented
+  // purely via reflection.  Generally, you should not call this unless you
+  // have already determined that an error exists by calling IsInitialized().
+  void FindInitializationErrors(std::vector<std::string>* errors) const;
+
+  // Like FindInitializationErrors, but joins all the strings, delimited by
+  // commas, and returns them.
+  std::string InitializationErrorString() const override;
+
+  // Clears all unknown fields from this message and all embedded messages.
+  // Normally, if unknown tag numbers are encountered when parsing a message,
+  // the tag and value are stored in the message's UnknownFieldSet and
+  // then written back out when the message is serialized.  This allows servers
+  // which simply route messages to other servers to pass through messages
+  // that have new field definitions which they don't yet know about.  However,
+  // this behavior can have security implications.  To avoid it, call this
+  // method after parsing.
+  //
+  // See Reflection::GetUnknownFields() for more on unknown fields.
+  void DiscardUnknownFields();
+
+  // Computes (an estimate of) the total number of bytes currently used for
+  // storing the message in memory.  The default implementation calls the
+  // Reflection object's SpaceUsed() method.
+  //
+  // SpaceUsed() is noticeably slower than ByteSize(), as it is implemented
+  // using reflection (rather than the generated code implementation for
+  // ByteSize()). Like ByteSize(), its CPU time is linear in the number of
+  // fields defined for the proto.
+  virtual size_t SpaceUsedLong() const;
+
+  PROTOBUF_DEPRECATED_MSG("Please use SpaceUsedLong() instead")
+  int SpaceUsed() const { return internal::ToIntSize(SpaceUsedLong()); }
+
+  // Debugging & Testing----------------------------------------------
+
+  // Generates a human-readable form of this message for debugging purposes.
+  // Note that the format and content of a debug string is not guaranteed, may
+  // change without notice, and should not be depended on. Code that does
+  // anything except display a string to assist in debugging should use
+  // TextFormat instead.
+  std::string DebugString() const;
+  // Like DebugString(), but with less whitespace.
+  std::string ShortDebugString() const;
+  // Like DebugString(), but do not escape UTF-8 byte sequences.
+  std::string Utf8DebugString() const;
+  // Convenience function useful in GDB.  Prints DebugString() to stdout.
+  void PrintDebugString() const;
+
+  // Reflection-based methods ----------------------------------------
+  // These methods are pure-virtual in MessageLite, but Message provides
+  // reflection-based default implementations.
+
+  std::string GetTypeName() const override;
+  void Clear() override;
+
+  // Returns whether all required fields have been set. Note that required
+  // fields no longer exist starting in proto3.
+  bool IsInitialized() const override;
+
+  void CheckTypeAndMergeFrom(const MessageLite& other) override;
+  // Reflective parser
+  const char* _InternalParse(const char* ptr,
+                             internal::ParseContext* ctx) override;
+  size_t ByteSizeLong() const override;
+  uint8_t* _InternalSerialize(uint8_t* target,
+                              io::EpsCopyOutputStream* stream) const override;
+
+ private:
+  // This is called only by the default implementation of ByteSize(), to
+  // update the cached size.  If you override ByteSize(), you do not need
+  // to override this.  If you do not override ByteSize(), you MUST override
+  // this; the default implementation will crash.
+  //
+  // The method is private because subclasses should never call it; only
+  // override it.  Yes, C++ lets you do that.  Crazy, huh?
+  virtual void SetCachedSize(int size) const;
+
+ public:
+  // Introspection ---------------------------------------------------
+
+
+  // Get a non-owning pointer to a Descriptor for this message's type.  This
+  // describes what fields the message contains, the types of those fields, etc.
+  // This object remains property of the Message.
+  const Descriptor* GetDescriptor() const { return GetMetadata().descriptor; }
+
+  // Get a non-owning pointer to the Reflection interface for this Message,
+  // which can be used to read and modify the fields of the Message dynamically
+  // (in other words, without knowing the message type at compile time).  This
+  // object remains property of the Message.
+  const Reflection* GetReflection() const { return GetMetadata().reflection; }
+
+ protected:
+  // Get a struct containing the metadata for the Message, which is used in turn
+  // to implement GetDescriptor() and GetReflection() above.
+  virtual Metadata GetMetadata() const = 0;
+
+  struct ClassData {
+    // Note: The order of arguments (to, then from) is chosen so that the ABI
+    // of this function is the same as the CopyFrom method.  That is, the
+    // hidden "this" parameter comes first.
+    void (*copy_to_from)(Message& to, const Message& from_msg);
+    void (*merge_to_from)(Message& to, const Message& from_msg);
+  };
+  // GetClassData() returns a pointer to a ClassData struct which
+  // exists in global memory and is unique to each subclass.  This uniqueness
+  // property is used in order to quickly determine whether two messages are
+  // of the same type.
+  // TODO(jorg): change to pure virtual
+  virtual const ClassData* GetClassData() const { return nullptr; }
+
+  // CopyWithSourceCheck calls Clear() and then MergeFrom(), and in debug
+  // builds, checks that calling Clear() on the destination message doesn't
+  // alter the source.  It assumes the messages are known to be of the same
+  // type, and thus uses GetClassData().
+  static void CopyWithSourceCheck(Message& to, const Message& from);
+
+  // Fail if "from" is a descendant of "to" as such copy is not allowed.
+  static void FailIfCopyFromDescendant(Message& to, const Message& from);
+
+  inline explicit Message(Arena* arena, bool is_message_owned = false)
+      : MessageLite(arena, is_message_owned) {}
+  size_t ComputeUnknownFieldsSize(size_t total_size,
+                                  internal::CachedSize* cached_size) const;
+  size_t MaybeComputeUnknownFieldsSize(size_t total_size,
+                                       internal::CachedSize* cached_size) const;
+
+
+ protected:
+  static uint64_t GetInvariantPerBuild(uint64_t salt);
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Message);
+};
+
+namespace internal {
+// Forward-declare interfaces used to implement RepeatedFieldRef.
+// These are protobuf internals that users shouldn't care about.
+class RepeatedFieldAccessor;
+}  // namespace internal
+
+// Forward-declare RepeatedFieldRef templates. The second type parameter is
+// used for SFINAE tricks. Users should ignore it.
+template <typename T, typename Enable = void>
+class RepeatedFieldRef;
+
+template <typename T, typename Enable = void>
+class MutableRepeatedFieldRef;
+
+// This interface contains methods that can be used to dynamically access
+// and modify the fields of a protocol message.  Their semantics are
+// similar to the accessors the protocol compiler generates.
+//
+// To get the Reflection for a given Message, call Message::GetReflection().
+//
+// This interface is separate from Message only for efficiency reasons;
+// the vast majority of implementations of Message will share the same
+// implementation of Reflection (GeneratedMessageReflection,
+// defined in generated_message.h), and all Messages of a particular class
+// should share the same Reflection object (though you should not rely on
+// the latter fact).
+//
+// There are several ways that these methods can be used incorrectly.  For
+// example, any of the following conditions will lead to undefined
+// results (probably assertion failures):
+// - The FieldDescriptor is not a field of this message type.
+// - The method called is not appropriate for the field's type.  For
+//   each field type in FieldDescriptor::TYPE_*, there is only one
+//   Get*() method, one Set*() method, and one Add*() method that is
+//   valid for that type.  It should be obvious which (except maybe
+//   for TYPE_BYTES, which are represented using strings in C++).
+// - A Get*() or Set*() method for singular fields is called on a repeated
+//   field.
+// - GetRepeated*(), SetRepeated*(), or Add*() is called on a non-repeated
+//   field.
+// - The Message object passed to any method is not of the right type for
+//   this Reflection object (i.e. message.GetReflection() != reflection).
+//
+// You might wonder why there is not any abstract representation for a field
+// of arbitrary type.  E.g., why isn't there just a "GetField()" method that
+// returns "const Field&", where "Field" is some class with accessors like
+// "GetInt32Value()".  The problem is that someone would have to deal with
+// allocating these Field objects.  For generated message classes, having to
+// allocate space for an additional object to wrap every field would at least
+// double the message's memory footprint, probably worse.  Allocating the
+// objects on-demand, on the other hand, would be expensive and prone to
+// memory leaks.  So, instead we ended up with this flat interface.
+class PROTOBUF_EXPORT Reflection final {
+ public:
+  // Get the UnknownFieldSet for the message.  This contains fields which
+  // were seen when the Message was parsed but were not recognized according
+  // to the Message's definition.
+  const UnknownFieldSet& GetUnknownFields(const Message& message) const;
+  // Get a mutable pointer to the UnknownFieldSet for the message.  This
+  // contains fields which were seen when the Message was parsed but were not
+  // recognized according to the Message's definition.
+  UnknownFieldSet* MutableUnknownFields(Message* message) const;
+
+  // Estimate the amount of memory used by the message object.
+  size_t SpaceUsedLong(const Message& message) const;
+
+  PROTOBUF_DEPRECATED_MSG("Please use SpaceUsedLong() instead")
+  int SpaceUsed(const Message& message) const {
+    return internal::ToIntSize(SpaceUsedLong(message));
+  }
+
+  // Check if the given non-repeated field is set.
+  bool HasField(const Message& message, const FieldDescriptor* field) const;
+
+  // Get the number of elements of a repeated field.
+  int FieldSize(const Message& message, const FieldDescriptor* field) const;
+
+  // Clear the value of a field, so that HasField() returns false or
+  // FieldSize() returns zero.
+  void ClearField(Message* message, const FieldDescriptor* field) const;
+
+  // Check if the oneof is set. Returns true if any field in oneof
+  // is set, false otherwise.
+  bool HasOneof(const Message& message,
+                const OneofDescriptor* oneof_descriptor) const;
+
+  void ClearOneof(Message* message,
+                  const OneofDescriptor* oneof_descriptor) const;
+
+  // Returns the field descriptor if the oneof is set. nullptr otherwise.
+  const FieldDescriptor* GetOneofFieldDescriptor(
+      const Message& message, const OneofDescriptor* oneof_descriptor) const;
+
+  // Removes the last element of a repeated field.
+  // We don't provide a way to remove any element other than the last
+  // because it invites inefficient use, such as O(n^2) filtering loops
+  // that should have been O(n).  If you want to remove an element other
+  // than the last, the best way to do it is to re-arrange the elements
+  // (using Swap()) so that the one you want removed is at the end, then
+  // call RemoveLast().
+  void RemoveLast(Message* message, const FieldDescriptor* field) const;
+  // Removes the last element of a repeated message field, and returns the
+  // pointer to the caller.  Caller takes ownership of the returned pointer.
+  PROTOBUF_NODISCARD Message* ReleaseLast(Message* message,
+                                          const FieldDescriptor* field) const;
+
+  // Similar to ReleaseLast() without internal safety and ownershp checks. This
+  // method should only be used when the objects are on the same arena or paired
+  // with a call to `UnsafeArenaAddAllocatedMessage`.
+  Message* UnsafeArenaReleaseLast(Message* message,
+                                  const FieldDescriptor* field) const;
+
+  // Swap the complete contents of two messages.
+  void Swap(Message* message1, Message* message2) const;
+
+  // Swap fields listed in fields vector of two messages.
+  void SwapFields(Message* message1, Message* message2,
+                  const std::vector<const FieldDescriptor*>& fields) const;
+
+  // Swap two elements of a repeated field.
+  void SwapElements(Message* message, const FieldDescriptor* field, int index1,
+                    int index2) const;
+
+  // Swap without internal safety and ownership checks. This method should only
+  // be used when the objects are on the same arena.
+  void UnsafeArenaSwap(Message* lhs, Message* rhs) const;
+
+  // SwapFields without internal safety and ownership checks. This method should
+  // only be used when the objects are on the same arena.
+  void UnsafeArenaSwapFields(
+      Message* lhs, Message* rhs,
+      const std::vector<const FieldDescriptor*>& fields) const;
+
+  // List all fields of the message which are currently set, except for unknown
+  // fields, but including extension known to the parser (i.e. compiled in).
+  // Singular fields will only be listed if HasField(field) would return true
+  // and repeated fields will only be listed if FieldSize(field) would return
+  // non-zero.  Fields (both normal fields and extension fields) will be listed
+  // ordered by field number.
+  // Use Reflection::GetUnknownFields() or message.unknown_fields() to also get
+  // access to fields/extensions unknown to the parser.
+  void ListFields(const Message& message,
+                  std::vector<const FieldDescriptor*>* output) const;
+
+  // Singular field getters ------------------------------------------
+  // These get the value of a non-repeated field.  They return the default
+  // value for fields that aren't set.
+
+  int32_t GetInt32(const Message& message, const FieldDescriptor* field) const;
+  int64_t GetInt64(const Message& message, const FieldDescriptor* field) const;
+  uint32_t GetUInt32(const Message& message,
+                     const FieldDescriptor* field) const;
+  uint64_t GetUInt64(const Message& message,
+                     const FieldDescriptor* field) const;
+  float GetFloat(const Message& message, const FieldDescriptor* field) const;
+  double GetDouble(const Message& message, const FieldDescriptor* field) const;
+  bool GetBool(const Message& message, const FieldDescriptor* field) const;
+  std::string GetString(const Message& message,
+                        const FieldDescriptor* field) const;
+  const EnumValueDescriptor* GetEnum(const Message& message,
+                                     const FieldDescriptor* field) const;
+
+  // GetEnumValue() returns an enum field's value as an integer rather than
+  // an EnumValueDescriptor*. If the integer value does not correspond to a
+  // known value descriptor, a new value descriptor is created. (Such a value
+  // will only be present when the new unknown-enum-value semantics are enabled
+  // for a message.)
+  int GetEnumValue(const Message& message, const FieldDescriptor* field) const;
+
+  // See MutableMessage() for the meaning of the "factory" parameter.
+  const Message& GetMessage(const Message& message,
+                            const FieldDescriptor* field,
+                            MessageFactory* factory = nullptr) const;
+
+  // Get a string value without copying, if possible.
+  //
+  // GetString() necessarily returns a copy of the string.  This can be
+  // inefficient when the std::string is already stored in a std::string object
+  // in the underlying message.  GetStringReference() will return a reference to
+  // the underlying std::string in this case.  Otherwise, it will copy the
+  // string into *scratch and return that.
+  //
+  // Note:  It is perfectly reasonable and useful to write code like:
+  //     str = reflection->GetStringReference(message, field, &str);
+  //   This line would ensure that only one copy of the string is made
+  //   regardless of the field's underlying representation.  When initializing
+  //   a newly-constructed string, though, it's just as fast and more
+  //   readable to use code like:
+  //     std::string str = reflection->GetString(message, field);
+  const std::string& GetStringReference(const Message& message,
+                                        const FieldDescriptor* field,
+                                        std::string* scratch) const;
+
+
+  // Singular field mutators -----------------------------------------
+  // These mutate the value of a non-repeated field.
+
+  void SetInt32(Message* message, const FieldDescriptor* field,
+                int32_t value) const;
+  void SetInt64(Message* message, const FieldDescriptor* field,
+                int64_t value) const;
+  void SetUInt32(Message* message, const FieldDescriptor* field,
+                 uint32_t value) const;
+  void SetUInt64(Message* message, const FieldDescriptor* field,
+                 uint64_t value) const;
+  void SetFloat(Message* message, const FieldDescriptor* field,
+                float value) const;
+  void SetDouble(Message* message, const FieldDescriptor* field,
+                 double value) const;
+  void SetBool(Message* message, const FieldDescriptor* field,
+               bool value) const;
+  void SetString(Message* message, const FieldDescriptor* field,
+                 std::string value) const;
+  void SetEnum(Message* message, const FieldDescriptor* field,
+               const EnumValueDescriptor* value) const;
+  // Set an enum field's value with an integer rather than EnumValueDescriptor.
+  // For proto3 this is just setting the enum field to the value specified, for
+  // proto2 it's more complicated. If value is a known enum value the field is
+  // set as usual. If the value is unknown then it is added to the unknown field
+  // set. Note this matches the behavior of parsing unknown enum values.
+  // If multiple calls with unknown values happen than they are all added to the
+  // unknown field set in order of the calls.
+  void SetEnumValue(Message* message, const FieldDescriptor* field,
+                    int value) const;
+
+  // Get a mutable pointer to a field with a message type.  If a MessageFactory
+  // is provided, it will be used to construct instances of the sub-message;
+  // otherwise, the default factory is used.  If the field is an extension that
+  // does not live in the same pool as the containing message's descriptor (e.g.
+  // it lives in an overlay pool), then a MessageFactory must be provided.
+  // If you have no idea what that meant, then you probably don't need to worry
+  // about it (don't provide a MessageFactory).  WARNING:  If the
+  // FieldDescriptor is for a compiled-in extension, then
+  // factory->GetPrototype(field->message_type()) MUST return an instance of
+  // the compiled-in class for this type, NOT DynamicMessage.
+  Message* MutableMessage(Message* message, const FieldDescriptor* field,
+                          MessageFactory* factory = nullptr) const;
+
+  // Replaces the message specified by 'field' with the already-allocated object
+  // sub_message, passing ownership to the message.  If the field contained a
+  // message, that message is deleted.  If sub_message is nullptr, the field is
+  // cleared.
+  void SetAllocatedMessage(Message* message, Message* sub_message,
+                           const FieldDescriptor* field) const;
+
+  // Similar to `SetAllocatedMessage`, but omits all internal safety and
+  // ownership checks.  This method should only be used when the objects are on
+  // the same arena or paired with a call to `UnsafeArenaReleaseMessage`.
+  void UnsafeArenaSetAllocatedMessage(Message* message, Message* sub_message,
+                                      const FieldDescriptor* field) const;
+
+  // Releases the message specified by 'field' and returns the pointer,
+  // ReleaseMessage() will return the message the message object if it exists.
+  // Otherwise, it may or may not return nullptr.  In any case, if the return
+  // value is non-null, the caller takes ownership of the pointer.
+  // If the field existed (HasField() is true), then the returned pointer will
+  // be the same as the pointer returned by MutableMessage().
+  // This function has the same effect as ClearField().
+  PROTOBUF_NODISCARD Message* ReleaseMessage(
+      Message* message, const FieldDescriptor* field,
+      MessageFactory* factory = nullptr) const;
+
+  // Similar to `ReleaseMessage`, but omits all internal safety and ownership
+  // checks.  This method should only be used when the objects are on the same
+  // arena or paired with a call to `UnsafeArenaSetAllocatedMessage`.
+  Message* UnsafeArenaReleaseMessage(Message* message,
+                                     const FieldDescriptor* field,
+                                     MessageFactory* factory = nullptr) const;
+
+
+  // Repeated field getters ------------------------------------------
+  // These get the value of one element of a repeated field.
+
+  int32_t GetRepeatedInt32(const Message& message, const FieldDescriptor* field,
+                           int index) const;
+  int64_t GetRepeatedInt64(const Message& message, const FieldDescriptor* field,
+                           int index) const;
+  uint32_t GetRepeatedUInt32(const Message& message,
+                             const FieldDescriptor* field, int index) const;
+  uint64_t GetRepeatedUInt64(const Message& message,
+                             const FieldDescriptor* field, int index) const;
+  float GetRepeatedFloat(const Message& message, const FieldDescriptor* field,
+                         int index) const;
+  double GetRepeatedDouble(const Message& message, const FieldDescriptor* field,
+                           int index) const;
+  bool GetRepeatedBool(const Message& message, const FieldDescriptor* field,
+                       int index) const;
+  std::string GetRepeatedString(const Message& message,
+                                const FieldDescriptor* field, int index) const;
+  const EnumValueDescriptor* GetRepeatedEnum(const Message& message,
+                                             const FieldDescriptor* field,
+                                             int index) const;
+  // GetRepeatedEnumValue() returns an enum field's value as an integer rather
+  // than an EnumValueDescriptor*. If the integer value does not correspond to a
+  // known value descriptor, a new value descriptor is created. (Such a value
+  // will only be present when the new unknown-enum-value semantics are enabled
+  // for a message.)
+  int GetRepeatedEnumValue(const Message& message, const FieldDescriptor* field,
+                           int index) const;
+  const Message& GetRepeatedMessage(const Message& message,
+                                    const FieldDescriptor* field,
+                                    int index) const;
+
+  // See GetStringReference(), above.
+  const std::string& GetRepeatedStringReference(const Message& message,
+                                                const FieldDescriptor* field,
+                                                int index,
+                                                std::string* scratch) const;
+
+
+  // Repeated field mutators -----------------------------------------
+  // These mutate the value of one element of a repeated field.
+
+  void SetRepeatedInt32(Message* message, const FieldDescriptor* field,
+                        int index, int32_t value) const;
+  void SetRepeatedInt64(Message* message, const FieldDescriptor* field,
+                        int index, int64_t value) const;
+  void SetRepeatedUInt32(Message* message, const FieldDescriptor* field,
+                         int index, uint32_t value) const;
+  void SetRepeatedUInt64(Message* message, const FieldDescriptor* field,
+                         int index, uint64_t value) const;
+  void SetRepeatedFloat(Message* message, const FieldDescriptor* field,
+                        int index, float value) const;
+  void SetRepeatedDouble(Message* message, const FieldDescriptor* field,
+                         int index, double value) const;
+  void SetRepeatedBool(Message* message, const FieldDescriptor* field,
+                       int index, bool value) const;
+  void SetRepeatedString(Message* message, const FieldDescriptor* field,
+                         int index, std::string value) const;
+  void SetRepeatedEnum(Message* message, const FieldDescriptor* field,
+                       int index, const EnumValueDescriptor* value) const;
+  // Set an enum field's value with an integer rather than EnumValueDescriptor.
+  // For proto3 this is just setting the enum field to the value specified, for
+  // proto2 it's more complicated. If value is a known enum value the field is
+  // set as usual. If the value is unknown then it is added to the unknown field
+  // set. Note this matches the behavior of parsing unknown enum values.
+  // If multiple calls with unknown values happen than they are all added to the
+  // unknown field set in order of the calls.
+  void SetRepeatedEnumValue(Message* message, const FieldDescriptor* field,
+                            int index, int value) const;
+  // Get a mutable pointer to an element of a repeated field with a message
+  // type.
+  Message* MutableRepeatedMessage(Message* message,
+                                  const FieldDescriptor* field,
+                                  int index) const;
+
+
+  // Repeated field adders -------------------------------------------
+  // These add an element to a repeated field.
+
+  void AddInt32(Message* message, const FieldDescriptor* field,
+                int32_t value) const;
+  void AddInt64(Message* message, const FieldDescriptor* field,
+                int64_t value) const;
+  void AddUInt32(Message* message, const FieldDescriptor* field,
+                 uint32_t value) const;
+  void AddUInt64(Message* message, const FieldDescriptor* field,
+                 uint64_t value) const;
+  void AddFloat(Message* message, const FieldDescriptor* field,
+                float value) const;
+  void AddDouble(Message* message, const FieldDescriptor* field,
+                 double value) const;
+  void AddBool(Message* message, const FieldDescriptor* field,
+               bool value) const;
+  void AddString(Message* message, const FieldDescriptor* field,
+                 std::string value) const;
+  void AddEnum(Message* message, const FieldDescriptor* field,
+               const EnumValueDescriptor* value) const;
+  // Add an integer value to a repeated enum field rather than
+  // EnumValueDescriptor. For proto3 this is just setting the enum field to the
+  // value specified, for proto2 it's more complicated. If value is a known enum
+  // value the field is set as usual. If the value is unknown then it is added
+  // to the unknown field set. Note this matches the behavior of parsing unknown
+  // enum values. If multiple calls with unknown values happen than they are all
+  // added to the unknown field set in order of the calls.
+  void AddEnumValue(Message* message, const FieldDescriptor* field,
+                    int value) const;
+  // See MutableMessage() for comments on the "factory" parameter.
+  Message* AddMessage(Message* message, const FieldDescriptor* field,
+                      MessageFactory* factory = nullptr) const;
+
+  // Appends an already-allocated object 'new_entry' to the repeated field
+  // specified by 'field' passing ownership to the message.
+  void AddAllocatedMessage(Message* message, const FieldDescriptor* field,
+                           Message* new_entry) const;
+
+  // Similar to AddAllocatedMessage() without internal safety and ownership
+  // checks. This method should only be used when the objects are on the same
+  // arena or paired with a call to `UnsafeArenaReleaseLast`.
+  void UnsafeArenaAddAllocatedMessage(Message* message,
+                                      const FieldDescriptor* field,
+                                      Message* new_entry) const;
+
+
+  // Get a RepeatedFieldRef object that can be used to read the underlying
+  // repeated field. The type parameter T must be set according to the
+  // field's cpp type. The following table shows the mapping from cpp type
+  // to acceptable T.
+  //
+  //   field->cpp_type()      T
+  //   CPPTYPE_INT32        int32_t
+  //   CPPTYPE_UINT32       uint32_t
+  //   CPPTYPE_INT64        int64_t
+  //   CPPTYPE_UINT64       uint64_t
+  //   CPPTYPE_DOUBLE       double
+  //   CPPTYPE_FLOAT        float
+  //   CPPTYPE_BOOL         bool
+  //   CPPTYPE_ENUM         generated enum type or int32_t
+  //   CPPTYPE_STRING       std::string
+  //   CPPTYPE_MESSAGE      generated message type or google::protobuf::Message
+  //
+  // A RepeatedFieldRef object can be copied and the resulted object will point
+  // to the same repeated field in the same message. The object can be used as
+  // long as the message is not destroyed.
+  //
+  // Note that to use this method users need to include the header file
+  // "reflection.h" (which defines the RepeatedFieldRef class templates).
+  template <typename T>
+  RepeatedFieldRef<T> GetRepeatedFieldRef(const Message& message,
+                                          const FieldDescriptor* field) const;
+
+  // Like GetRepeatedFieldRef() but return an object that can also be used
+  // manipulate the underlying repeated field.
+  template <typename T>
+  MutableRepeatedFieldRef<T> GetMutableRepeatedFieldRef(
+      Message* message, const FieldDescriptor* field) const;
+
+  // DEPRECATED. Please use Get(Mutable)RepeatedFieldRef() for repeated field
+  // access. The following repeated field accessors will be removed in the
+  // future.
+  //
+  // Repeated field accessors  -------------------------------------------------
+  // The methods above, e.g. GetRepeatedInt32(msg, fd, index), provide singular
+  // access to the data in a RepeatedField.  The methods below provide aggregate
+  // access by exposing the RepeatedField object itself with the Message.
+  // Applying these templates to inappropriate types will lead to an undefined
+  // reference at link time (e.g. GetRepeatedField<***double>), or possibly a
+  // template matching error at compile time (e.g. GetRepeatedPtrField<File>).
+  //
+  // Usage example: my_doubs = refl->GetRepeatedField<double>(msg, fd);
+
+  // DEPRECATED. Please use GetRepeatedFieldRef().
+  //
+  // for T = Cord and all protobuf scalar types except enums.
+  template <typename T>
+  PROTOBUF_DEPRECATED_MSG("Please use GetRepeatedFieldRef() instead")
+  const RepeatedField<T>& GetRepeatedField(const Message& msg,
+                                           const FieldDescriptor* d) const {
+    return GetRepeatedFieldInternal<T>(msg, d);
+  }
+
+  // DEPRECATED. Please use GetMutableRepeatedFieldRef().
+  //
+  // for T = Cord and all protobuf scalar types except enums.
+  template <typename T>
+  PROTOBUF_DEPRECATED_MSG("Please use GetMutableRepeatedFieldRef() instead")
+  RepeatedField<T>* MutableRepeatedField(Message* msg,
+                                         const FieldDescriptor* d) const {
+    return MutableRepeatedFieldInternal<T>(msg, d);
+  }
+
+  // DEPRECATED. Please use GetRepeatedFieldRef().
+  //
+  // for T = std::string, google::protobuf::internal::StringPieceField
+  //         google::protobuf::Message & descendants.
+  template <typename T>
+  PROTOBUF_DEPRECATED_MSG("Please use GetRepeatedFieldRef() instead")
+  const RepeatedPtrField<T>& GetRepeatedPtrField(
+      const Message& msg, const FieldDescriptor* d) const {
+    return GetRepeatedPtrFieldInternal<T>(msg, d);
+  }
+
+  // DEPRECATED. Please use GetMutableRepeatedFieldRef().
+  //
+  // for T = std::string, google::protobuf::internal::StringPieceField
+  //         google::protobuf::Message & descendants.
+  template <typename T>
+  PROTOBUF_DEPRECATED_MSG("Please use GetMutableRepeatedFieldRef() instead")
+  RepeatedPtrField<T>* MutableRepeatedPtrField(Message* msg,
+                                               const FieldDescriptor* d) const {
+    return MutableRepeatedPtrFieldInternal<T>(msg, d);
+  }
+
+  // Extensions ----------------------------------------------------------------
+
+  // Try to find an extension of this message type by fully-qualified field
+  // name.  Returns nullptr if no extension is known for this name or number.
+  const FieldDescriptor* FindKnownExtensionByName(
+      const std::string& name) const;
+
+  // Try to find an extension of this message type by field number.
+  // Returns nullptr if no extension is known for this name or number.
+  const FieldDescriptor* FindKnownExtensionByNumber(int number) const;
+
+  // Feature Flags -------------------------------------------------------------
+
+  // Does this message support storing arbitrary integer values in enum fields?
+  // If |true|, GetEnumValue/SetEnumValue and associated repeated-field versions
+  // take arbitrary integer values, and the legacy GetEnum() getter will
+  // dynamically create an EnumValueDescriptor for any integer value without
+  // one. If |false|, setting an unknown enum value via the integer-based
+  // setters results in undefined behavior (in practice, GOOGLE_DCHECK-fails).
+  //
+  // Generic code that uses reflection to handle messages with enum fields
+  // should check this flag before using the integer-based setter, and either
+  // downgrade to a compatible value or use the UnknownFieldSet if not. For
+  // example:
+  //
+  //   int new_value = GetValueFromApplicationLogic();
+  //   if (reflection->SupportsUnknownEnumValues()) {
+  //     reflection->SetEnumValue(message, field, new_value);
+  //   } else {
+  //     if (field_descriptor->enum_type()->
+  //             FindValueByNumber(new_value) != nullptr) {
+  //       reflection->SetEnumValue(message, field, new_value);
+  //     } else if (emit_unknown_enum_values) {
+  //       reflection->MutableUnknownFields(message)->AddVarint(
+  //           field->number(), new_value);
+  //     } else {
+  //       // convert value to a compatible/default value.
+  //       new_value = CompatibleDowngrade(new_value);
+  //       reflection->SetEnumValue(message, field, new_value);
+  //     }
+  //   }
+  bool SupportsUnknownEnumValues() const;
+
+  // Returns the MessageFactory associated with this message.  This can be
+  // useful for determining if a message is a generated message or not, for
+  // example:
+  //   if (message->GetReflection()->GetMessageFactory() ==
+  //       google::protobuf::MessageFactory::generated_factory()) {
+  //     // This is a generated message.
+  //   }
+  // It can also be used to create more messages of this type, though
+  // Message::New() is an easier way to accomplish this.
+  MessageFactory* GetMessageFactory() const;
+
+ private:
+  template <typename T>
+  const RepeatedField<T>& GetRepeatedFieldInternal(
+      const Message& message, const FieldDescriptor* field) const;
+  template <typename T>
+  RepeatedField<T>* MutableRepeatedFieldInternal(
+      Message* message, const FieldDescriptor* field) const;
+  template <typename T>
+  const RepeatedPtrField<T>& GetRepeatedPtrFieldInternal(
+      const Message& message, const FieldDescriptor* field) const;
+  template <typename T>
+  RepeatedPtrField<T>* MutableRepeatedPtrFieldInternal(
+      Message* message, const FieldDescriptor* field) const;
+  // Obtain a pointer to a Repeated Field Structure and do some type checking:
+  //   on field->cpp_type(),
+  //   on field->field_option().ctype() (if ctype >= 0)
+  //   of field->message_type() (if message_type != nullptr).
+  // We use 2 routine rather than 4 (const vs mutable) x (scalar vs pointer).
+  void* MutableRawRepeatedField(Message* message, const FieldDescriptor* field,
+                                FieldDescriptor::CppType, int ctype,
+                                const Descriptor* message_type) const;
+
+  const void* GetRawRepeatedField(const Message& message,
+                                  const FieldDescriptor* field,
+                                  FieldDescriptor::CppType cpptype, int ctype,
+                                  const Descriptor* message_type) const;
+
+  // The following methods are used to implement (Mutable)RepeatedFieldRef.
+  // A Ref object will store a raw pointer to the repeated field data (obtained
+  // from RepeatedFieldData()) and a pointer to a Accessor (obtained from
+  // RepeatedFieldAccessor) which will be used to access the raw data.
+
+  // Returns a raw pointer to the repeated field
+  //
+  // "cpp_type" and "message_type" are deduced from the type parameter T passed
+  // to Get(Mutable)RepeatedFieldRef. If T is a generated message type,
+  // "message_type" should be set to its descriptor. Otherwise "message_type"
+  // should be set to nullptr. Implementations of this method should check
+  // whether "cpp_type"/"message_type" is consistent with the actual type of the
+  // field. We use 1 routine rather than 2 (const vs mutable) because it is
+  // protected and it doesn't change the message.
+  void* RepeatedFieldData(Message* message, const FieldDescriptor* field,
+                          FieldDescriptor::CppType cpp_type,
+                          const Descriptor* message_type) const;
+
+  // The returned pointer should point to a singleton instance which implements
+  // the RepeatedFieldAccessor interface.
+  const internal::RepeatedFieldAccessor* RepeatedFieldAccessor(
+      const FieldDescriptor* field) const;
+
+  // Lists all fields of the message which are currently set, except for unknown
+  // fields and stripped fields. See ListFields for details.
+  void ListFieldsOmitStripped(
+      const Message& message,
+      std::vector<const FieldDescriptor*>* output) const;
+
+  bool IsMessageStripped(const Descriptor* descriptor) const {
+    return schema_.IsMessageStripped(descriptor);
+  }
+
+  friend class TextFormat;
+
+  void ListFieldsMayFailOnStripped(
+      const Message& message, bool should_fail,
+      std::vector<const FieldDescriptor*>* output) const;
+
+  // Returns true if the message field is backed by a LazyField.
+  //
+  // A message field may be backed by a LazyField without the user annotation
+  // ([lazy = true]). While the user-annotated LazyField is lazily verified on
+  // first touch (i.e. failure on access rather than parsing if the LazyField is
+  // not initialized), the inferred LazyField is eagerly verified to avoid lazy
+  // parsing error at the cost of lower efficiency. When reflecting a message
+  // field, use this API instead of checking field->options().lazy().
+  bool IsLazyField(const FieldDescriptor* field) const {
+    return IsLazilyVerifiedLazyField(field) ||
+           IsEagerlyVerifiedLazyField(field);
+  }
+
+  // Returns true if the field is lazy extension. It is meant to allow python
+  // reparse lazy field until b/157559327 is fixed.
+  bool IsLazyExtension(const Message& message,
+                       const FieldDescriptor* field) const;
+
+  bool IsLazilyVerifiedLazyField(const FieldDescriptor* field) const;
+  bool IsEagerlyVerifiedLazyField(const FieldDescriptor* field) const;
+
+  friend class FastReflectionMessageMutator;
+  friend bool internal::IsDescendant(Message& root, const Message& message);
+
+  const Descriptor* const descriptor_;
+  const internal::ReflectionSchema schema_;
+  const DescriptorPool* const descriptor_pool_;
+  MessageFactory* const message_factory_;
+
+  // Last non weak field index. This is an optimization when most weak fields
+  // are at the end of the containing message. If a message proto doesn't
+  // contain weak fields, then this field equals descriptor_->field_count().
+  int last_non_weak_field_index_;
+
+  template <typename T, typename Enable>
+  friend class RepeatedFieldRef;
+  template <typename T, typename Enable>
+  friend class MutableRepeatedFieldRef;
+  friend class ::PROTOBUF_NAMESPACE_ID::MessageLayoutInspector;
+  friend class ::PROTOBUF_NAMESPACE_ID::AssignDescriptorsHelper;
+  friend class DynamicMessageFactory;
+  friend class GeneratedMessageReflectionTestHelper;
+  friend class python::MapReflectionFriend;
+  friend class python::MessageReflectionFriend;
+  friend class util::MessageDifferencer;
+#define GOOGLE_PROTOBUF_HAS_CEL_MAP_REFLECTION_FRIEND
+  friend class expr::CelMapReflectionFriend;
+  friend class internal::MapFieldReflectionTest;
+  friend class internal::MapKeySorter;
+  friend class internal::WireFormat;
+  friend class internal::ReflectionOps;
+  friend class internal::SwapFieldHelper;
+  // Needed for implementing text format for map.
+  friend class internal::MapFieldPrinterHelper;
+
+  Reflection(const Descriptor* descriptor,
+             const internal::ReflectionSchema& schema,
+             const DescriptorPool* pool, MessageFactory* factory);
+
+  // Special version for specialized implementations of string.  We can't
+  // call MutableRawRepeatedField directly here because we don't have access to
+  // FieldOptions::* which are defined in descriptor.pb.h.  Including that
+  // file here is not possible because it would cause a circular include cycle.
+  // We use 1 routine rather than 2 (const vs mutable) because it is private
+  // and mutable a repeated string field doesn't change the message.
+  void* MutableRawRepeatedString(Message* message, const FieldDescriptor* field,
+                                 bool is_string) const;
+
+  friend class MapReflectionTester;
+  // Returns true if key is in map. Returns false if key is not in map field.
+  bool ContainsMapKey(const Message& message, const FieldDescriptor* field,
+                      const MapKey& key) const;
+
+  // If key is in map field: Saves the value pointer to val and returns
+  // false. If key in not in map field: Insert the key into map, saves
+  // value pointer to val and returns true. Users are able to modify the
+  // map value by MapValueRef.
+  bool InsertOrLookupMapValue(Message* message, const FieldDescriptor* field,
+                              const MapKey& key, MapValueRef* val) const;
+
+  // If key is in map field: Saves the value pointer to val and returns true.
+  // Returns false if key is not in map field. Users are NOT able to modify
+  // the value by MapValueConstRef.
+  bool LookupMapValue(const Message& message, const FieldDescriptor* field,
+                      const MapKey& key, MapValueConstRef* val) const;
+  bool LookupMapValue(const Message&, const FieldDescriptor*, const MapKey&,
+                      MapValueRef*) const = delete;
+
+  // Delete and returns true if key is in the map field. Returns false
+  // otherwise.
+  bool DeleteMapValue(Message* message, const FieldDescriptor* field,
+                      const MapKey& key) const;
+
+  // Returns a MapIterator referring to the first element in the map field.
+  // If the map field is empty, this function returns the same as
+  // reflection::MapEnd. Mutation to the field may invalidate the iterator.
+  MapIterator MapBegin(Message* message, const FieldDescriptor* field) const;
+
+  // Returns a MapIterator referring to the theoretical element that would
+  // follow the last element in the map field. It does not point to any
+  // real element. Mutation to the field may invalidate the iterator.
+  MapIterator MapEnd(Message* message, const FieldDescriptor* field) const;
+
+  // Get the number of <key, value> pair of a map field. The result may be
+  // different from FieldSize which can have duplicate keys.
+  int MapSize(const Message& message, const FieldDescriptor* field) const;
+
+  // Help method for MapIterator.
+  friend class MapIterator;
+  friend class WireFormatForMapFieldTest;
+  internal::MapFieldBase* MutableMapData(Message* message,
+                                         const FieldDescriptor* field) const;
+
+  const internal::MapFieldBase* GetMapData(const Message& message,
+                                           const FieldDescriptor* field) const;
+
+  template <class T>
+  const T& GetRawNonOneof(const Message& message,
+                          const FieldDescriptor* field) const;
+  template <class T>
+  T* MutableRawNonOneof(Message* message, const FieldDescriptor* field) const;
+
+  template <typename Type>
+  const Type& GetRaw(const Message& message,
+                     const FieldDescriptor* field) const;
+  template <typename Type>
+  inline Type* MutableRaw(Message* message, const FieldDescriptor* field) const;
+  template <typename Type>
+  const Type& DefaultRaw(const FieldDescriptor* field) const;
+
+  const Message* GetDefaultMessageInstance(const FieldDescriptor* field) const;
+
+  inline const uint32_t* GetHasBits(const Message& message) const;
+  inline uint32_t* MutableHasBits(Message* message) const;
+  inline uint32_t GetOneofCase(const Message& message,
+                               const OneofDescriptor* oneof_descriptor) const;
+  inline uint32_t* MutableOneofCase(
+      Message* message, const OneofDescriptor* oneof_descriptor) const;
+  inline bool HasExtensionSet(const Message& /* message */) const {
+    return schema_.HasExtensionSet();
+  }
+  const internal::ExtensionSet& GetExtensionSet(const Message& message) const;
+  internal::ExtensionSet* MutableExtensionSet(Message* message) const;
+
+  const internal::InternalMetadata& GetInternalMetadata(
+      const Message& message) const;
+
+  internal::InternalMetadata* MutableInternalMetadata(Message* message) const;
+
+  inline bool IsInlined(const FieldDescriptor* field) const;
+
+  inline bool HasBit(const Message& message,
+                     const FieldDescriptor* field) const;
+  inline void SetBit(Message* message, const FieldDescriptor* field) const;
+  inline void ClearBit(Message* message, const FieldDescriptor* field) const;
+  inline void SwapBit(Message* message1, Message* message2,
+                      const FieldDescriptor* field) const;
+
+  inline const uint32_t* GetInlinedStringDonatedArray(
+      const Message& message) const;
+  inline uint32_t* MutableInlinedStringDonatedArray(Message* message) const;
+  inline bool IsInlinedStringDonated(const Message& message,
+                                     const FieldDescriptor* field) const;
+  inline void SwapInlinedStringDonated(Message* lhs, Message* rhs,
+                                       const FieldDescriptor* field) const;
+
+  // Shallow-swap fields listed in fields vector of two messages. It is the
+  // caller's responsibility to make sure shallow swap is safe.
+  void UnsafeShallowSwapFields(
+      Message* message1, Message* message2,
+      const std::vector<const FieldDescriptor*>& fields) const;
+
+  // This function only swaps the field. Should swap corresponding has_bit
+  // before or after using this function.
+  void SwapField(Message* message1, Message* message2,
+                 const FieldDescriptor* field) const;
+
+  // Unsafe but shallow version of SwapField.
+  void UnsafeShallowSwapField(Message* message1, Message* message2,
+                              const FieldDescriptor* field) const;
+
+  template <bool unsafe_shallow_swap>
+  void SwapFieldsImpl(Message* message1, Message* message2,
+                      const std::vector<const FieldDescriptor*>& fields) const;
+
+  template <bool unsafe_shallow_swap>
+  void SwapOneofField(Message* lhs, Message* rhs,
+                      const OneofDescriptor* oneof_descriptor) const;
+
+  inline bool HasOneofField(const Message& message,
+                            const FieldDescriptor* field) const;
+  inline void SetOneofCase(Message* message,
+                           const FieldDescriptor* field) const;
+  inline void ClearOneofField(Message* message,
+                              const FieldDescriptor* field) const;
+
+  template <typename Type>
+  inline const Type& GetField(const Message& message,
+                              const FieldDescriptor* field) const;
+  template <typename Type>
+  inline void SetField(Message* message, const FieldDescriptor* field,
+                       const Type& value) const;
+  template <typename Type>
+  inline Type* MutableField(Message* message,
+                            const FieldDescriptor* field) const;
+  template <typename Type>
+  inline const Type& GetRepeatedField(const Message& message,
+                                      const FieldDescriptor* field,
+                                      int index) const;
+  template <typename Type>
+  inline const Type& GetRepeatedPtrField(const Message& message,
+                                         const FieldDescriptor* field,
+                                         int index) const;
+  template <typename Type>
+  inline void SetRepeatedField(Message* message, const FieldDescriptor* field,
+                               int index, Type value) const;
+  template <typename Type>
+  inline Type* MutableRepeatedField(Message* message,
+                                    const FieldDescriptor* field,
+                                    int index) const;
+  template <typename Type>
+  inline void AddField(Message* message, const FieldDescriptor* field,
+                       const Type& value) const;
+  template <typename Type>
+  inline Type* AddField(Message* message, const FieldDescriptor* field) const;
+
+  int GetExtensionNumberOrDie(const Descriptor* type) const;
+
+  // Internal versions of EnumValue API perform no checking. Called after checks
+  // by public methods.
+  void SetEnumValueInternal(Message* message, const FieldDescriptor* field,
+                            int value) const;
+  void SetRepeatedEnumValueInternal(Message* message,
+                                    const FieldDescriptor* field, int index,
+                                    int value) const;
+  void AddEnumValueInternal(Message* message, const FieldDescriptor* field,
+                            int value) const;
+
+  friend inline  // inline so nobody can call this function.
+      void
+      RegisterAllTypesInternal(const Metadata* file_level_metadata, int size);
+  friend inline const char* ParseLenDelim(int field_number,
+                                          const FieldDescriptor* field,
+                                          Message* msg,
+                                          const Reflection* reflection,
+                                          const char* ptr,
+                                          internal::ParseContext* ctx);
+  friend inline const char* ParsePackedField(const FieldDescriptor* field,
+                                             Message* msg,
+                                             const Reflection* reflection,
+                                             const char* ptr,
+                                             internal::ParseContext* ctx);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Reflection);
+};
+
+// Abstract interface for a factory for message objects.
+class PROTOBUF_EXPORT MessageFactory {
+ public:
+  inline MessageFactory() {}
+  virtual ~MessageFactory();
+
+  // Given a Descriptor, gets or constructs the default (prototype) Message
+  // of that type.  You can then call that message's New() method to construct
+  // a mutable message of that type.
+  //
+  // Calling this method twice with the same Descriptor returns the same
+  // object.  The returned object remains property of the factory.  Also, any
+  // objects created by calling the prototype's New() method share some data
+  // with the prototype, so these must be destroyed before the MessageFactory
+  // is destroyed.
+  //
+  // The given descriptor must outlive the returned message, and hence must
+  // outlive the MessageFactory.
+  //
+  // Some implementations do not support all types.  GetPrototype() will
+  // return nullptr if the descriptor passed in is not supported.
+  //
+  // This method may or may not be thread-safe depending on the implementation.
+  // Each implementation should document its own degree thread-safety.
+  virtual const Message* GetPrototype(const Descriptor* type) = 0;
+
+  // Gets a MessageFactory which supports all generated, compiled-in messages.
+  // In other words, for any compiled-in type FooMessage, the following is true:
+  //   MessageFactory::generated_factory()->GetPrototype(
+  //     FooMessage::descriptor()) == FooMessage::default_instance()
+  // This factory supports all types which are found in
+  // DescriptorPool::generated_pool().  If given a descriptor from any other
+  // pool, GetPrototype() will return nullptr.  (You can also check if a
+  // descriptor is for a generated message by checking if
+  // descriptor->file()->pool() == DescriptorPool::generated_pool().)
+  //
+  // This factory is 100% thread-safe; calling GetPrototype() does not modify
+  // any shared data.
+  //
+  // This factory is a singleton.  The caller must not delete the object.
+  static MessageFactory* generated_factory();
+
+  // For internal use only:  Registers a .proto file at static initialization
+  // time, to be placed in generated_factory.  The first time GetPrototype()
+  // is called with a descriptor from this file, |register_messages| will be
+  // called, with the file name as the parameter.  It must call
+  // InternalRegisterGeneratedMessage() (below) to register each message type
+  // in the file.  This strange mechanism is necessary because descriptors are
+  // built lazily, so we can't register types by their descriptor until we
+  // know that the descriptor exists.  |filename| must be a permanent string.
+  static void InternalRegisterGeneratedFile(
+      const google::protobuf::internal::DescriptorTable* table);
+
+  // For internal use only:  Registers a message type.  Called only by the
+  // functions which are registered with InternalRegisterGeneratedFile(),
+  // above.
+  static void InternalRegisterGeneratedMessage(const Descriptor* descriptor,
+                                               const Message* prototype);
+
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFactory);
+};
+
+#define DECLARE_GET_REPEATED_FIELD(TYPE)                           \
+  template <>                                                      \
+  PROTOBUF_EXPORT const RepeatedField<TYPE>&                       \
+  Reflection::GetRepeatedFieldInternal<TYPE>(                      \
+      const Message& message, const FieldDescriptor* field) const; \
+                                                                   \
+  template <>                                                      \
+  PROTOBUF_EXPORT RepeatedField<TYPE>*                             \
+  Reflection::MutableRepeatedFieldInternal<TYPE>(                  \
+      Message * message, const FieldDescriptor* field) const;
+
+DECLARE_GET_REPEATED_FIELD(int32_t)
+DECLARE_GET_REPEATED_FIELD(int64_t)
+DECLARE_GET_REPEATED_FIELD(uint32_t)
+DECLARE_GET_REPEATED_FIELD(uint64_t)
+DECLARE_GET_REPEATED_FIELD(float)
+DECLARE_GET_REPEATED_FIELD(double)
+DECLARE_GET_REPEATED_FIELD(bool)
+
+#undef DECLARE_GET_REPEATED_FIELD
+
+// Tries to downcast this message to a generated message type.  Returns nullptr
+// if this class is not an instance of T.  This works even if RTTI is disabled.
+//
+// This also has the effect of creating a strong reference to T that will
+// prevent the linker from stripping it out at link time.  This can be important
+// if you are using a DynamicMessageFactory that delegates to the generated
+// factory.
+template <typename T>
+const T* DynamicCastToGenerated(const Message* from) {
+  // Compile-time assert that T is a generated type that has a
+  // default_instance() accessor, but avoid actually calling it.
+  const T& (*get_default_instance)() = &T::default_instance;
+  (void)get_default_instance;
+
+  // Compile-time assert that T is a subclass of google::protobuf::Message.
+  const Message* unused = static_cast<T*>(nullptr);
+  (void)unused;
+
+#if PROTOBUF_RTTI
+  return dynamic_cast<const T*>(from);
+#else
+  bool ok = from != nullptr &&
+            T::default_instance().GetReflection() == from->GetReflection();
+  return ok ? down_cast<const T*>(from) : nullptr;
+#endif
+}
+
+template <typename T>
+T* DynamicCastToGenerated(Message* from) {
+  const Message* message_const = from;
+  return const_cast<T*>(DynamicCastToGenerated<T>(message_const));
+}
+
+// Call this function to ensure that this message's reflection is linked into
+// the binary:
+//
+//   google::protobuf::LinkMessageReflection<pkg::FooMessage>();
+//
+// This will ensure that the following lookup will succeed:
+//
+//   DescriptorPool::generated_pool()->FindMessageTypeByName("pkg.FooMessage");
+//
+// As a side-effect, it will also guarantee that anything else from the same
+// .proto file will also be available for lookup in the generated pool.
+//
+// This function does not actually register the message, so it does not need
+// to be called before the lookup.  However it does need to occur in a function
+// that cannot be stripped from the binary (ie. it must be reachable from main).
+//
+// Best practice is to call this function as close as possible to where the
+// reflection is actually needed.  This function is very cheap to call, so you
+// should not need to worry about its runtime overhead except in the tightest
+// of loops (on x86-64 it compiles into two "mov" instructions).
+template <typename T>
+void LinkMessageReflection() {
+  internal::StrongReference(T::default_instance);
+}
+
+// =============================================================================
+// Implementation details for {Get,Mutable}RawRepeatedPtrField.  We provide
+// specializations for <std::string>, <StringPieceField> and <Message> and
+// handle everything else with the default template which will match any type
+// having a method with signature "static const google::protobuf::Descriptor*
+// descriptor()". Such a type presumably is a descendant of google::protobuf::Message.
+
+template <>
+inline const RepeatedPtrField<std::string>&
+Reflection::GetRepeatedPtrFieldInternal<std::string>(
+    const Message& message, const FieldDescriptor* field) const {
+  return *static_cast<RepeatedPtrField<std::string>*>(
+      MutableRawRepeatedString(const_cast<Message*>(&message), field, true));
+}
+
+template <>
+inline RepeatedPtrField<std::string>*
+Reflection::MutableRepeatedPtrFieldInternal<std::string>(
+    Message* message, const FieldDescriptor* field) const {
+  return static_cast<RepeatedPtrField<std::string>*>(
+      MutableRawRepeatedString(message, field, true));
+}
+
+
+// -----
+
+template <>
+inline const RepeatedPtrField<Message>& Reflection::GetRepeatedPtrFieldInternal(
+    const Message& message, const FieldDescriptor* field) const {
+  return *static_cast<const RepeatedPtrField<Message>*>(GetRawRepeatedField(
+      message, field, FieldDescriptor::CPPTYPE_MESSAGE, -1, nullptr));
+}
+
+template <>
+inline RepeatedPtrField<Message>* Reflection::MutableRepeatedPtrFieldInternal(
+    Message* message, const FieldDescriptor* field) const {
+  return static_cast<RepeatedPtrField<Message>*>(MutableRawRepeatedField(
+      message, field, FieldDescriptor::CPPTYPE_MESSAGE, -1, nullptr));
+}
+
+template <typename PB>
+inline const RepeatedPtrField<PB>& Reflection::GetRepeatedPtrFieldInternal(
+    const Message& message, const FieldDescriptor* field) const {
+  return *static_cast<const RepeatedPtrField<PB>*>(
+      GetRawRepeatedField(message, field, FieldDescriptor::CPPTYPE_MESSAGE, -1,
+                          PB::default_instance().GetDescriptor()));
+}
+
+template <typename PB>
+inline RepeatedPtrField<PB>* Reflection::MutableRepeatedPtrFieldInternal(
+    Message* message, const FieldDescriptor* field) const {
+  return static_cast<RepeatedPtrField<PB>*>(
+      MutableRawRepeatedField(message, field, FieldDescriptor::CPPTYPE_MESSAGE,
+                              -1, PB::default_instance().GetDescriptor()));
+}
+
+template <typename Type>
+const Type& Reflection::DefaultRaw(const FieldDescriptor* field) const {
+  return *reinterpret_cast<const Type*>(schema_.GetFieldDefault(field));
+}
+
+uint32_t Reflection::GetOneofCase(
+    const Message& message, const OneofDescriptor* oneof_descriptor) const {
+  GOOGLE_DCHECK(!oneof_descriptor->is_synthetic());
+  return internal::GetConstRefAtOffset<uint32_t>(
+      message, schema_.GetOneofCaseOffset(oneof_descriptor));
+}
+
+bool Reflection::HasOneofField(const Message& message,
+                               const FieldDescriptor* field) const {
+  return (GetOneofCase(message, field->containing_oneof()) ==
+          static_cast<uint32_t>(field->number()));
+}
+
+template <typename Type>
+const Type& Reflection::GetRaw(const Message& message,
+                               const FieldDescriptor* field) const {
+  GOOGLE_DCHECK(!schema_.InRealOneof(field) || HasOneofField(message, field))
+      << "Field = " << field->full_name();
+  return internal::GetConstRefAtOffset<Type>(message,
+                                             schema_.GetFieldOffset(field));
+}
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_MESSAGE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/message_lite.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/message_lite.h
new file mode 100644
index 0000000..950ae1a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/message_lite.h
@@ -0,0 +1,591 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Authors: wink@google.com (Wink Saville),
+//          kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Defines MessageLite, the abstract interface implemented by all (lite
+// and non-lite) protocol message objects.
+
+#ifndef GOOGLE_PROTOBUF_MESSAGE_LITE_H__
+#define GOOGLE_PROTOBUF_MESSAGE_LITE_H__
+
+
+#include <climits>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/explicitly_constructed.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/stubs/hash.h>  // TODO(b/211442718): cleanup
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+
+template <typename T>
+class RepeatedPtrField;
+
+class FastReflectionMessageMutator;
+class FastReflectionStringSetter;
+class Reflection;
+
+namespace io {
+
+class CodedInputStream;
+class CodedOutputStream;
+class ZeroCopyInputStream;
+class ZeroCopyOutputStream;
+
+}  // namespace io
+namespace internal {
+
+class SwapFieldHelper;
+
+// See parse_context.h for explanation
+class ParseContext;
+
+class ExtensionSet;
+class LazyField;
+class RepeatedPtrFieldBase;
+class TcParser;
+class WireFormatLite;
+class WeakFieldMap;
+
+template <typename Type>
+class GenericTypeHandler;  // defined in repeated_field.h
+
+// We compute sizes as size_t but cache them as int.  This function converts a
+// computed size to a cached size.  Since we don't proceed with serialization
+// if the total size was > INT_MAX, it is not important what this function
+// returns for inputs > INT_MAX.  However this case should not error or
+// GOOGLE_CHECK-fail, because the full size_t resolution is still returned from
+// ByteSizeLong() and checked against INT_MAX; we can catch the overflow
+// there.
+inline int ToCachedSize(size_t size) { return static_cast<int>(size); }
+
+// We mainly calculate sizes in terms of size_t, but some functions that
+// compute sizes return "int".  These int sizes are expected to always be
+// positive. This function is more efficient than casting an int to size_t
+// directly on 64-bit platforms because it avoids making the compiler emit a
+// sign extending instruction, which we don't want and don't want to pay for.
+inline size_t FromIntSize(int size) {
+  // Convert to unsigned before widening so sign extension is not necessary.
+  return static_cast<unsigned int>(size);
+}
+
+// For cases where a legacy function returns an integer size.  We GOOGLE_DCHECK()
+// that the conversion will fit within an integer; if this is false then we
+// are losing information.
+inline int ToIntSize(size_t size) {
+  GOOGLE_DCHECK_LE(size, static_cast<size_t>(INT_MAX));
+  return static_cast<int>(size);
+}
+
+// Default empty string object. Don't use this directly. Instead, call
+// GetEmptyString() to get the reference. This empty string is aligned with a
+// minimum alignment of 8 bytes to match the requirement of ArenaStringPtr.
+PROTOBUF_EXPORT extern ExplicitlyConstructedArenaString
+    fixed_address_empty_string;
+
+
+PROTOBUF_EXPORT constexpr const std::string& GetEmptyStringAlreadyInited() {
+  return fixed_address_empty_string.get();
+}
+
+PROTOBUF_EXPORT size_t StringSpaceUsedExcludingSelfLong(const std::string& str);
+
+}  // namespace internal
+
+// Interface to light weight protocol messages.
+//
+// This interface is implemented by all protocol message objects.  Non-lite
+// messages additionally implement the Message interface, which is a
+// subclass of MessageLite.  Use MessageLite instead when you only need
+// the subset of features which it supports -- namely, nothing that uses
+// descriptors or reflection.  You can instruct the protocol compiler
+// to generate classes which implement only MessageLite, not the full
+// Message interface, by adding the following line to the .proto file:
+//
+//   option optimize_for = LITE_RUNTIME;
+//
+// This is particularly useful on resource-constrained systems where
+// the full protocol buffers runtime library is too big.
+//
+// Note that on non-constrained systems (e.g. servers) when you need
+// to link in lots of protocol definitions, a better way to reduce
+// total code footprint is to use optimize_for = CODE_SIZE.  This
+// will make the generated code smaller while still supporting all the
+// same features (at the expense of speed).  optimize_for = LITE_RUNTIME
+// is best when you only have a small number of message types linked
+// into your binary, in which case the size of the protocol buffers
+// runtime itself is the biggest problem.
+//
+// Users must not derive from this class. Only the protocol compiler and
+// the internal library are allowed to create subclasses.
+class PROTOBUF_EXPORT MessageLite {
+ public:
+  constexpr MessageLite() {}
+  virtual ~MessageLite() = default;
+
+  // Basic Operations ------------------------------------------------
+
+  // Get the name of this message type, e.g. "foo.bar.BazProto".
+  virtual std::string GetTypeName() const = 0;
+
+  // Construct a new instance of the same type.  Ownership is passed to the
+  // caller.
+  MessageLite* New() const { return New(nullptr); }
+
+  // Construct a new instance on the arena. Ownership is passed to the caller
+  // if arena is a nullptr.
+  virtual MessageLite* New(Arena* arena) const = 0;
+
+  // Returns user-owned arena; nullptr if it's message owned.
+  Arena* GetArena() const { return _internal_metadata_.user_arena(); }
+
+  // Clear all fields of the message and set them to their default values.
+  // Clear() assumes that any memory allocated to hold parts of the message
+  // will likely be needed again, so the memory used may not be freed.
+  // To ensure that all memory used by a Message is freed, you must delete it.
+  virtual void Clear() = 0;
+
+  // Quickly check if all required fields have values set.
+  virtual bool IsInitialized() const = 0;
+
+  // This is not implemented for Lite messages -- it just returns "(cannot
+  // determine missing fields for lite message)".  However, it is implemented
+  // for full messages.  See message.h.
+  virtual std::string InitializationErrorString() const;
+
+  // If |other| is the exact same class as this, calls MergeFrom(). Otherwise,
+  // results are undefined (probably crash).
+  virtual void CheckTypeAndMergeFrom(const MessageLite& other) = 0;
+
+  // These methods return a human-readable summary of the message. Note that
+  // since the MessageLite interface does not support reflection, there is very
+  // little information that these methods can provide. They are shadowed by
+  // methods of the same name on the Message interface which provide much more
+  // information. The methods here are intended primarily to facilitate code
+  // reuse for logic that needs to interoperate with both full and lite protos.
+  //
+  // The format of the returned string is subject to change, so please do not
+  // assume it will remain stable over time.
+  std::string DebugString() const;
+  std::string ShortDebugString() const { return DebugString(); }
+  // MessageLite::DebugString is already Utf8 Safe. This is to add compatibility
+  // with Message.
+  std::string Utf8DebugString() const { return DebugString(); }
+
+  // Parsing ---------------------------------------------------------
+  // Methods for parsing in protocol buffer format.  Most of these are
+  // just simple wrappers around MergeFromCodedStream().  Clear() will be
+  // called before merging the input.
+
+  // Fill the message with a protocol buffer parsed from the given input
+  // stream. Returns false on a read error or if the input is in the wrong
+  // format.  A successful return does not indicate the entire input is
+  // consumed, ensure you call ConsumedEntireMessage() to check that if
+  // applicable.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParseFromCodedStream(
+      io::CodedInputStream* input);
+  // Like ParseFromCodedStream(), but accepts messages that are missing
+  // required fields.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParsePartialFromCodedStream(
+      io::CodedInputStream* input);
+  // Read a protocol buffer from the given zero-copy input stream.  If
+  // successful, the entire input will be consumed.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParseFromZeroCopyStream(
+      io::ZeroCopyInputStream* input);
+  // Like ParseFromZeroCopyStream(), but accepts messages that are missing
+  // required fields.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParsePartialFromZeroCopyStream(
+      io::ZeroCopyInputStream* input);
+  // Parse a protocol buffer from a file descriptor.  If successful, the entire
+  // input will be consumed.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParseFromFileDescriptor(
+      int file_descriptor);
+  // Like ParseFromFileDescriptor(), but accepts messages that are missing
+  // required fields.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParsePartialFromFileDescriptor(
+      int file_descriptor);
+  // Parse a protocol buffer from a C++ istream.  If successful, the entire
+  // input will be consumed.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParseFromIstream(std::istream* input);
+  // Like ParseFromIstream(), but accepts messages that are missing
+  // required fields.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParsePartialFromIstream(
+      std::istream* input);
+  // Read a protocol buffer from the given zero-copy input stream, expecting
+  // the message to be exactly "size" bytes long.  If successful, exactly
+  // this many bytes will have been consumed from the input.
+  bool MergePartialFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input,
+                                             int size);
+  // Like ParseFromBoundedZeroCopyStream(), but accepts messages that are
+  // missing required fields.
+  bool MergeFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input, int size);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParseFromBoundedZeroCopyStream(
+      io::ZeroCopyInputStream* input, int size);
+  // Like ParseFromBoundedZeroCopyStream(), but accepts messages that are
+  // missing required fields.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParsePartialFromBoundedZeroCopyStream(
+      io::ZeroCopyInputStream* input, int size);
+  // Parses a protocol buffer contained in a string. Returns true on success.
+  // This function takes a string in the (non-human-readable) binary wire
+  // format, matching the encoding output by MessageLite::SerializeToString().
+  // If you'd like to convert a human-readable string into a protocol buffer
+  // object, see google::protobuf::TextFormat::ParseFromString().
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParseFromString(ConstStringParam data);
+  // Like ParseFromString(), but accepts messages that are missing
+  // required fields.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParsePartialFromString(
+      ConstStringParam data);
+  // Parse a protocol buffer contained in an array of bytes.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParseFromArray(const void* data,
+                                                       int size);
+  // Like ParseFromArray(), but accepts messages that are missing
+  // required fields.
+  PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParsePartialFromArray(const void* data,
+                                                              int size);
+
+
+  // Reads a protocol buffer from the stream and merges it into this
+  // Message.  Singular fields read from the what is
+  // already in the Message and repeated fields are appended to those
+  // already present.
+  //
+  // It is the responsibility of the caller to call input->LastTagWas()
+  // (for groups) or input->ConsumedEntireMessage() (for non-groups) after
+  // this returns to verify that the message's end was delimited correctly.
+  //
+  // ParseFromCodedStream() is implemented as Clear() followed by
+  // MergeFromCodedStream().
+  bool MergeFromCodedStream(io::CodedInputStream* input);
+
+  // Like MergeFromCodedStream(), but succeeds even if required fields are
+  // missing in the input.
+  //
+  // MergeFromCodedStream() is just implemented as MergePartialFromCodedStream()
+  // followed by IsInitialized().
+  bool MergePartialFromCodedStream(io::CodedInputStream* input);
+
+  // Merge a protocol buffer contained in a string.
+  bool MergeFromString(ConstStringParam data);
+
+
+  // Serialization ---------------------------------------------------
+  // Methods for serializing in protocol buffer format.  Most of these
+  // are just simple wrappers around ByteSize() and SerializeWithCachedSizes().
+
+  // Write a protocol buffer of this message to the given output.  Returns
+  // false on a write error.  If the message is missing required fields,
+  // this may GOOGLE_CHECK-fail.
+  bool SerializeToCodedStream(io::CodedOutputStream* output) const;
+  // Like SerializeToCodedStream(), but allows missing required fields.
+  bool SerializePartialToCodedStream(io::CodedOutputStream* output) const;
+  // Write the message to the given zero-copy output stream.  All required
+  // fields must be set.
+  bool SerializeToZeroCopyStream(io::ZeroCopyOutputStream* output) const;
+  // Like SerializeToZeroCopyStream(), but allows missing required fields.
+  bool SerializePartialToZeroCopyStream(io::ZeroCopyOutputStream* output) const;
+  // Serialize the message and store it in the given string.  All required
+  // fields must be set.
+  bool SerializeToString(std::string* output) const;
+  // Like SerializeToString(), but allows missing required fields.
+  bool SerializePartialToString(std::string* output) const;
+  // Serialize the message and store it in the given byte array.  All required
+  // fields must be set.
+  bool SerializeToArray(void* data, int size) const;
+  // Like SerializeToArray(), but allows missing required fields.
+  bool SerializePartialToArray(void* data, int size) const;
+
+  // Make a string encoding the message. Is equivalent to calling
+  // SerializeToString() on a string and using that.  Returns the empty
+  // string if SerializeToString() would have returned an error.
+  // Note: If you intend to generate many such strings, you may
+  // reduce heap fragmentation by instead re-using the same string
+  // object with calls to SerializeToString().
+  std::string SerializeAsString() const;
+  // Like SerializeAsString(), but allows missing required fields.
+  std::string SerializePartialAsString() const;
+
+  // Serialize the message and write it to the given file descriptor.  All
+  // required fields must be set.
+  bool SerializeToFileDescriptor(int file_descriptor) const;
+  // Like SerializeToFileDescriptor(), but allows missing required fields.
+  bool SerializePartialToFileDescriptor(int file_descriptor) const;
+  // Serialize the message and write it to the given C++ ostream.  All
+  // required fields must be set.
+  bool SerializeToOstream(std::ostream* output) const;
+  // Like SerializeToOstream(), but allows missing required fields.
+  bool SerializePartialToOstream(std::ostream* output) const;
+
+  // Like SerializeToString(), but appends to the data to the string's
+  // existing contents.  All required fields must be set.
+  bool AppendToString(std::string* output) const;
+  // Like AppendToString(), but allows missing required fields.
+  bool AppendPartialToString(std::string* output) const;
+
+
+  // Computes the serialized size of the message.  This recursively calls
+  // ByteSizeLong() on all embedded messages.
+  //
+  // ByteSizeLong() is generally linear in the number of fields defined for the
+  // proto.
+  virtual size_t ByteSizeLong() const = 0;
+
+  // Legacy ByteSize() API.
+  PROTOBUF_DEPRECATED_MSG("Please use ByteSizeLong() instead")
+  int ByteSize() const { return internal::ToIntSize(ByteSizeLong()); }
+
+  // Serializes the message without recomputing the size.  The message must not
+  // have changed since the last call to ByteSize(), and the value returned by
+  // ByteSize must be non-negative.  Otherwise the results are undefined.
+  void SerializeWithCachedSizes(io::CodedOutputStream* output) const {
+    output->SetCur(_InternalSerialize(output->Cur(), output->EpsCopy()));
+  }
+
+  // Functions below here are not part of the public interface.  It isn't
+  // enforced, but they should be treated as private, and will be private
+  // at some future time.  Unfortunately the implementation of the "friend"
+  // keyword in GCC is broken at the moment, but we expect it will be fixed.
+
+  // Like SerializeWithCachedSizes, but writes directly to *target, returning
+  // a pointer to the byte immediately after the last byte written.  "target"
+  // must point at a byte array of at least ByteSize() bytes.  Whether to use
+  // deterministic serialization, e.g., maps in sorted order, is determined by
+  // CodedOutputStream::IsDefaultSerializationDeterministic().
+  uint8_t* SerializeWithCachedSizesToArray(uint8_t* target) const;
+
+  // Returns the result of the last call to ByteSize().  An embedded message's
+  // size is needed both to serialize it (because embedded messages are
+  // length-delimited) and to compute the outer message's size.  Caching
+  // the size avoids computing it multiple times.
+  //
+  // ByteSize() does not automatically use the cached size when available
+  // because this would require invalidating it every time the message was
+  // modified, which would be too hard and expensive.  (E.g. if a deeply-nested
+  // sub-message is changed, all of its parents' cached sizes would need to be
+  // invalidated, which is too much work for an otherwise inlined setter
+  // method.)
+  virtual int GetCachedSize() const = 0;
+
+  virtual const char* _InternalParse(const char* /*ptr*/,
+                                     internal::ParseContext* /*ctx*/) {
+    return nullptr;
+  }
+
+  virtual void OnDemandRegisterArenaDtor(Arena* /*arena*/) {}
+
+ protected:
+  template <typename T>
+  static T* CreateMaybeMessage(Arena* arena) {
+    return Arena::CreateMaybeMessage<T>(arena);
+  }
+
+  inline explicit MessageLite(Arena* arena, bool is_message_owned = false)
+      : _internal_metadata_(arena, is_message_owned) {}
+
+  // Returns the arena, if any, that directly owns this message and its internal
+  // memory (Arena::Own is different in that the arena doesn't directly own the
+  // internal memory). This method is used in proto's implementation for
+  // swapping, moving and setting allocated, for deciding whether the ownership
+  // of this message or its internal memory could be changed.
+  Arena* GetOwningArena() const { return _internal_metadata_.owning_arena(); }
+
+  // Returns the arena, used for allocating internal objects(e.g., child
+  // messages, etc), or owning incoming objects (e.g., set allocated).
+  Arena* GetArenaForAllocation() const { return _internal_metadata_.arena(); }
+
+  // Returns true if this message is enabled for message-owned arena (MOA)
+  // trials. No lite messages are eligible for MOA.
+  static bool InMoaTrial() { return false; }
+
+  internal::InternalMetadata _internal_metadata_;
+
+ public:
+  enum ParseFlags {
+    kMerge = 0,
+    kParse = 1,
+    kMergePartial = 2,
+    kParsePartial = 3,
+    kMergeWithAliasing = 4,
+    kParseWithAliasing = 5,
+    kMergePartialWithAliasing = 6,
+    kParsePartialWithAliasing = 7
+  };
+
+  template <ParseFlags flags, typename T>
+  bool ParseFrom(const T& input);
+
+  // Fast path when conditions match (ie. non-deterministic)
+  //  uint8_t* _InternalSerialize(uint8_t* ptr) const;
+  virtual uint8_t* _InternalSerialize(
+      uint8_t* ptr, io::EpsCopyOutputStream* stream) const = 0;
+
+  // Identical to IsInitialized() except that it logs an error message.
+  bool IsInitializedWithErrors() const {
+    if (IsInitialized()) return true;
+    LogInitializationErrorMessage();
+    return false;
+  }
+
+ private:
+  friend class FastReflectionMessageMutator;
+  friend class FastReflectionStringSetter;
+  friend class Message;
+  friend class Reflection;
+  friend class internal::ExtensionSet;
+  friend class internal::LazyField;
+  friend class internal::SwapFieldHelper;
+  friend class internal::TcParser;
+  friend class internal::WeakFieldMap;
+  friend class internal::WireFormatLite;
+
+  template <typename Type>
+  friend class Arena::InternalHelper;
+  template <typename Type>
+  friend class internal::GenericTypeHandler;
+
+  void LogInitializationErrorMessage() const;
+
+  bool MergeFromImpl(io::CodedInputStream* input, ParseFlags parse_flags);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageLite);
+};
+
+namespace internal {
+
+template <bool alias>
+bool MergeFromImpl(StringPiece input, MessageLite* msg,
+                   MessageLite::ParseFlags parse_flags);
+extern template bool MergeFromImpl<false>(StringPiece input,
+                                          MessageLite* msg,
+                                          MessageLite::ParseFlags parse_flags);
+extern template bool MergeFromImpl<true>(StringPiece input,
+                                         MessageLite* msg,
+                                         MessageLite::ParseFlags parse_flags);
+
+template <bool alias>
+bool MergeFromImpl(io::ZeroCopyInputStream* input, MessageLite* msg,
+                   MessageLite::ParseFlags parse_flags);
+extern template bool MergeFromImpl<false>(io::ZeroCopyInputStream* input,
+                                          MessageLite* msg,
+                                          MessageLite::ParseFlags parse_flags);
+extern template bool MergeFromImpl<true>(io::ZeroCopyInputStream* input,
+                                         MessageLite* msg,
+                                         MessageLite::ParseFlags parse_flags);
+
+struct BoundedZCIS {
+  io::ZeroCopyInputStream* zcis;
+  int limit;
+};
+
+template <bool alias>
+bool MergeFromImpl(BoundedZCIS input, MessageLite* msg,
+                   MessageLite::ParseFlags parse_flags);
+extern template bool MergeFromImpl<false>(BoundedZCIS input, MessageLite* msg,
+                                          MessageLite::ParseFlags parse_flags);
+extern template bool MergeFromImpl<true>(BoundedZCIS input, MessageLite* msg,
+                                         MessageLite::ParseFlags parse_flags);
+
+template <typename T>
+struct SourceWrapper;
+
+template <bool alias, typename T>
+bool MergeFromImpl(const SourceWrapper<T>& input, MessageLite* msg,
+                   MessageLite::ParseFlags parse_flags) {
+  return input.template MergeInto<alias>(msg, parse_flags);
+}
+
+}  // namespace internal
+
+template <MessageLite::ParseFlags flags, typename T>
+bool MessageLite::ParseFrom(const T& input) {
+  if (flags & kParse) Clear();
+  constexpr bool alias = (flags & kMergeWithAliasing) != 0;
+  return internal::MergeFromImpl<alias>(input, this, flags);
+}
+
+// ===================================================================
+// Shutdown support.
+
+
+// Shut down the entire protocol buffers library, deleting all static-duration
+// objects allocated by the library or by generated .pb.cc files.
+//
+// There are two reasons you might want to call this:
+// * You use a draconian definition of "memory leak" in which you expect
+//   every single malloc() to have a corresponding free(), even for objects
+//   which live until program exit.
+// * You are writing a dynamically-loaded library which needs to clean up
+//   after itself when the library is unloaded.
+//
+// It is safe to call this multiple times.  However, it is not safe to use
+// any other part of the protocol buffers library after
+// ShutdownProtobufLibrary() has been called. Furthermore this call is not
+// thread safe, user needs to synchronize multiple calls.
+PROTOBUF_EXPORT void ShutdownProtobufLibrary();
+
+namespace internal {
+
+// Register a function to be called when ShutdownProtocolBuffers() is called.
+PROTOBUF_EXPORT void OnShutdown(void (*func)());
+// Run an arbitrary function on an arg
+PROTOBUF_EXPORT void OnShutdownRun(void (*f)(const void*), const void* arg);
+
+template <typename T>
+T* OnShutdownDelete(T* p) {
+  OnShutdownRun([](const void* pp) { delete static_cast<const T*>(pp); }, p);
+  return p;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_MESSAGE_LITE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/metadata.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/metadata.h
new file mode 100644
index 0000000..4e89648
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/metadata.h
@@ -0,0 +1,36 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_METADATA_H__
+#define GOOGLE_PROTOBUF_METADATA_H__
+
+// TODO(b/151117630): Remove this file and all instances where it gets imported.
+
+#endif  // GOOGLE_PROTOBUF_METADATA_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/metadata_lite.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/metadata_lite.h
new file mode 100644
index 0000000..0c31517
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/metadata_lite.h
@@ -0,0 +1,316 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_METADATA_LITE_H__
+#define GOOGLE_PROTOBUF_METADATA_LITE_H__
+
+#include <string>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/port.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// This is the representation for messages that support arena allocation. It
+// uses a tagged pointer to either store the owning Arena pointer, if there are
+// no unknown fields, or a pointer to a block of memory with both the owning
+// Arena pointer and the UnknownFieldSet, if there are unknown fields. Besides,
+// it also uses the tag to distinguish whether the owning Arena pointer is also
+// used by sub-structure allocation. This optimization allows for
+// "zero-overhead" storage of the Arena pointer, relative to the above baseline
+// implementation.
+//
+// The tagged pointer uses the least two significant bits to disambiguate cases.
+// It uses bit 0 == 0 to indicate an arena pointer and bit 0 == 1 to indicate a
+// UFS+Arena-container pointer. Besides it uses bit 1 == 0 to indicate arena
+// allocation and bit 1 == 1 to indicate heap allocation.
+class PROTOBUF_EXPORT InternalMetadata {
+ public:
+  constexpr InternalMetadata() : ptr_(0) {}
+  explicit InternalMetadata(Arena* arena, bool is_message_owned = false) {
+    SetArena(arena, is_message_owned);
+  }
+
+  void SetArena(Arena* arena, bool is_message_owned) {
+    ptr_ = is_message_owned
+               ? reinterpret_cast<intptr_t>(arena) | kMessageOwnedArenaTagMask
+               : reinterpret_cast<intptr_t>(arena);
+    GOOGLE_DCHECK(!is_message_owned || arena != nullptr);
+  }
+
+  // To keep the ABI identical between debug and non-debug builds,
+  // the destructor is always defined here even though it may delegate
+  // to a non-inline private method.
+  // (see https://github.com/protocolbuffers/protobuf/issues/9947)
+  ~InternalMetadata() {
+#if defined(NDEBUG) || defined(_MSC_VER)
+    if (HasMessageOwnedArenaTag()) {
+      delete reinterpret_cast<Arena*>(ptr_ - kMessageOwnedArenaTagMask);
+    }
+#else
+    CheckedDestruct();
+#endif
+  }
+
+  template <typename T>
+  void Delete() {
+    // Note that Delete<> should be called not more than once.
+    if (have_unknown_fields()) {
+      DeleteOutOfLineHelper<T>();
+    }
+  }
+
+  // DeleteReturnArena will delete the unknown fields only if they weren't
+  // allocated on an arena.  Then it updates the flags so that if you call
+  // have_unknown_fields(), it will return false.  Finally, it returns the
+  // current value of arena().  It is designed to be used as part of a
+  // Message class's destructor call, so that when control eventually gets
+  // to ~InternalMetadata(), we don't need to check for have_unknown_fields()
+  // again.
+  template <typename T>
+  Arena* DeleteReturnArena() {
+    if (have_unknown_fields()) {
+      return DeleteOutOfLineHelper<T>();
+    } else {
+      return PtrValue<Arena>();
+    }
+  }
+
+  PROTOBUF_NDEBUG_INLINE Arena* owning_arena() const {
+    return HasMessageOwnedArenaTag() ? nullptr : arena();
+  }
+
+  PROTOBUF_NDEBUG_INLINE Arena* user_arena() const {
+    Arena* a = arena();
+    return a && !a->IsMessageOwned() ? a : nullptr;
+  }
+
+  PROTOBUF_NDEBUG_INLINE Arena* arena() const {
+    if (PROTOBUF_PREDICT_FALSE(have_unknown_fields())) {
+      return PtrValue<ContainerBase>()->arena;
+    } else {
+      return PtrValue<Arena>();
+    }
+  }
+
+  PROTOBUF_NDEBUG_INLINE bool have_unknown_fields() const {
+    return HasUnknownFieldsTag();
+  }
+
+  PROTOBUF_NDEBUG_INLINE void* raw_arena_ptr() const {
+    return reinterpret_cast<void*>(ptr_);
+  }
+
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE const T& unknown_fields(
+      const T& (*default_instance)()) const {
+    if (PROTOBUF_PREDICT_FALSE(have_unknown_fields())) {
+      return PtrValue<Container<T>>()->unknown_fields;
+    } else {
+      return default_instance();
+    }
+  }
+
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE T* mutable_unknown_fields() {
+    if (PROTOBUF_PREDICT_TRUE(have_unknown_fields())) {
+      return &PtrValue<Container<T>>()->unknown_fields;
+    } else {
+      return mutable_unknown_fields_slow<T>();
+    }
+  }
+
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE void Swap(InternalMetadata* other) {
+    // Semantics here are that we swap only the unknown fields, not the arena
+    // pointer. We cannot simply swap ptr_ with other->ptr_ because we need to
+    // maintain our own arena ptr. Also, our ptr_ and other's ptr_ may be in
+    // different states (direct arena pointer vs. container with UFS) so we
+    // cannot simply swap ptr_ and then restore the arena pointers. We reuse
+    // UFS's swap implementation instead.
+    if (have_unknown_fields() || other->have_unknown_fields()) {
+      DoSwap<T>(other->mutable_unknown_fields<T>());
+    }
+  }
+
+  PROTOBUF_NDEBUG_INLINE void InternalSwap(InternalMetadata* other) {
+    std::swap(ptr_, other->ptr_);
+  }
+
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE void MergeFrom(const InternalMetadata& other) {
+    if (other.have_unknown_fields()) {
+      DoMergeFrom<T>(other.unknown_fields<T>(nullptr));
+    }
+  }
+
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE void Clear() {
+    if (have_unknown_fields()) {
+      DoClear<T>();
+    }
+  }
+
+ private:
+  intptr_t ptr_;
+
+  // Tagged pointer implementation.
+  static constexpr intptr_t kUnknownFieldsTagMask = 1;
+  static constexpr intptr_t kMessageOwnedArenaTagMask = 2;
+  static constexpr intptr_t kPtrTagMask =
+      kUnknownFieldsTagMask | kMessageOwnedArenaTagMask;
+  static constexpr intptr_t kPtrValueMask = ~kPtrTagMask;
+
+  // Accessors for pointer tag and pointer value.
+  PROTOBUF_ALWAYS_INLINE bool HasUnknownFieldsTag() const {
+    return ptr_ & kUnknownFieldsTagMask;
+  }
+  PROTOBUF_ALWAYS_INLINE bool HasMessageOwnedArenaTag() const {
+    return ptr_ & kMessageOwnedArenaTagMask;
+  }
+
+  template <typename U>
+  U* PtrValue() const {
+    return reinterpret_cast<U*>(ptr_ & kPtrValueMask);
+  }
+
+  // If ptr_'s tag is kTagContainer, it points to an instance of this struct.
+  struct ContainerBase {
+    Arena* arena;
+  };
+
+  template <typename T>
+  struct Container : public ContainerBase {
+    T unknown_fields;
+  };
+
+  template <typename T>
+  PROTOBUF_NOINLINE Arena* DeleteOutOfLineHelper() {
+    if (auto* a = arena()) {
+      // Subtle: we want to preserve the message-owned arena flag, while at the
+      // same time replacing the pointer to Container<T> with a pointer to the
+      // arena.
+      intptr_t message_owned_arena_tag = ptr_ & kMessageOwnedArenaTagMask;
+      ptr_ = reinterpret_cast<intptr_t>(a) | message_owned_arena_tag;
+      return a;
+    } else {
+      delete PtrValue<Container<T>>();
+      ptr_ = 0;
+      return nullptr;
+    }
+  }
+
+  template <typename T>
+  PROTOBUF_NOINLINE T* mutable_unknown_fields_slow() {
+    Arena* my_arena = arena();
+    Container<T>* container = Arena::Create<Container<T>>(my_arena);
+    intptr_t message_owned_arena_tag = ptr_ & kMessageOwnedArenaTagMask;
+    // Two-step assignment works around a bug in clang's static analyzer:
+    // https://bugs.llvm.org/show_bug.cgi?id=34198.
+    ptr_ = reinterpret_cast<intptr_t>(container);
+    ptr_ |= kUnknownFieldsTagMask | message_owned_arena_tag;
+    container->arena = my_arena;
+    return &(container->unknown_fields);
+  }
+
+  // Templated functions.
+
+  template <typename T>
+  PROTOBUF_NOINLINE void DoClear() {
+    mutable_unknown_fields<T>()->Clear();
+  }
+
+  template <typename T>
+  PROTOBUF_NOINLINE void DoMergeFrom(const T& other) {
+    mutable_unknown_fields<T>()->MergeFrom(other);
+  }
+
+  template <typename T>
+  PROTOBUF_NOINLINE void DoSwap(T* other) {
+    mutable_unknown_fields<T>()->Swap(other);
+  }
+
+  // Private helper with debug checks for ~InternalMetadata()
+  void CheckedDestruct();
+};
+
+// String Template specializations.
+
+template <>
+PROTOBUF_EXPORT void InternalMetadata::DoClear<std::string>();
+template <>
+PROTOBUF_EXPORT void InternalMetadata::DoMergeFrom<std::string>(
+    const std::string& other);
+template <>
+PROTOBUF_EXPORT void InternalMetadata::DoSwap<std::string>(std::string* other);
+
+// This helper RAII class is needed to efficiently parse unknown fields. We
+// should only call mutable_unknown_fields if there are actual unknown fields.
+// The obvious thing to just use a stack string and swap it at the end of
+// the parse won't work, because the destructor of StringOutputStream needs to
+// be called before we can modify the string (it check-fails). Using
+// LiteUnknownFieldSetter setter(&_internal_metadata_);
+// StringOutputStream stream(setter.buffer());
+// guarantees that the string is only swapped after stream is destroyed.
+class PROTOBUF_EXPORT LiteUnknownFieldSetter {
+ public:
+  explicit LiteUnknownFieldSetter(InternalMetadata* metadata)
+      : metadata_(metadata) {
+    if (metadata->have_unknown_fields()) {
+      buffer_.swap(*metadata->mutable_unknown_fields<std::string>());
+    }
+  }
+  ~LiteUnknownFieldSetter() {
+    if (!buffer_.empty())
+      metadata_->mutable_unknown_fields<std::string>()->swap(buffer_);
+  }
+  std::string* buffer() { return &buffer_; }
+
+ private:
+  InternalMetadata* metadata_;
+  std::string buffer_;
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_METADATA_LITE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/parse_context.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/parse_context.h
new file mode 100644
index 0000000..97daae0
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/parse_context.h
@@ -0,0 +1,1033 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_PARSE_CONTEXT_H__
+#define GOOGLE_PROTOBUF_PARSE_CONTEXT_H__
+
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <type_traits>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/endian.h>
+#include <google/protobuf/implicit_weak_message.h>
+#include <google/protobuf/inlined_string_field.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+
+namespace google {
+namespace protobuf {
+
+class UnknownFieldSet;
+class DescriptorPool;
+class MessageFactory;
+
+namespace internal {
+
+// Template code below needs to know about the existence of these functions.
+PROTOBUF_EXPORT void WriteVarint(uint32_t num, uint64_t val, std::string* s);
+PROTOBUF_EXPORT void WriteLengthDelimited(uint32_t num, StringPiece val,
+                                          std::string* s);
+// Inline because it is just forwarding to s->WriteVarint
+inline void WriteVarint(uint32_t num, uint64_t val, UnknownFieldSet* s);
+inline void WriteLengthDelimited(uint32_t num, StringPiece val,
+                                 UnknownFieldSet* s);
+
+
+// The basic abstraction the parser is designed for is a slight modification
+// of the ZeroCopyInputStream (ZCIS) abstraction. A ZCIS presents a serialized
+// stream as a series of buffers that concatenate to the full stream.
+// Pictorially a ZCIS presents a stream in chunks like so
+// [---------------------------------------------------------------]
+// [---------------------] chunk 1
+//                      [----------------------------] chunk 2
+//                                          chunk 3 [--------------]
+//
+// Where the '-' represent the bytes which are vertically lined up with the
+// bytes of the stream. The proto parser requires its input to be presented
+// similarly with the extra
+// property that each chunk has kSlopBytes past its end that overlaps with the
+// first kSlopBytes of the next chunk, or if there is no next chunk at least its
+// still valid to read those bytes. Again, pictorially, we now have
+//
+// [---------------------------------------------------------------]
+// [-------------------....] chunk 1
+//                    [------------------------....] chunk 2
+//                                    chunk 3 [------------------..**]
+//                                                      chunk 4 [--****]
+// Here '-' mean the bytes of the stream or chunk and '.' means bytes past the
+// chunk that match up with the start of the next chunk. Above each chunk has
+// 4 '.' after the chunk. In the case these 'overflow' bytes represents bytes
+// past the stream, indicated by '*' above, their values are unspecified. It is
+// still legal to read them (ie. should not segfault). Reading past the
+// end should be detected by the user and indicated as an error.
+//
+// The reason for this, admittedly, unconventional invariant is to ruthlessly
+// optimize the protobuf parser. Having an overlap helps in two important ways.
+// Firstly it alleviates having to performing bounds checks if a piece of code
+// is guaranteed to not read more than kSlopBytes. Secondly, and more
+// importantly, the protobuf wireformat is such that reading a key/value pair is
+// always less than 16 bytes. This removes the need to change to next buffer in
+// the middle of reading primitive values. Hence there is no need to store and
+// load the current position.
+
+class PROTOBUF_EXPORT EpsCopyInputStream {
+ public:
+  enum { kSlopBytes = 16, kMaxCordBytesToCopy = 512 };
+
+  explicit EpsCopyInputStream(bool enable_aliasing)
+      : aliasing_(enable_aliasing ? kOnPatch : kNoAliasing) {}
+
+  void BackUp(const char* ptr) {
+    GOOGLE_DCHECK(ptr <= buffer_end_ + kSlopBytes);
+    int count;
+    if (next_chunk_ == buffer_) {
+      count = static_cast<int>(buffer_end_ + kSlopBytes - ptr);
+    } else {
+      count = size_ + static_cast<int>(buffer_end_ - ptr);
+    }
+    if (count > 0) StreamBackUp(count);
+  }
+
+  // If return value is negative it's an error
+  PROTOBUF_NODISCARD int PushLimit(const char* ptr, int limit) {
+    GOOGLE_DCHECK(limit >= 0 && limit <= INT_MAX - kSlopBytes);
+    // This add is safe due to the invariant above, because
+    // ptr - buffer_end_ <= kSlopBytes.
+    limit += static_cast<int>(ptr - buffer_end_);
+    limit_end_ = buffer_end_ + (std::min)(0, limit);
+    auto old_limit = limit_;
+    limit_ = limit;
+    return old_limit - limit;
+  }
+
+  PROTOBUF_NODISCARD bool PopLimit(int delta) {
+    if (PROTOBUF_PREDICT_FALSE(!EndedAtLimit())) return false;
+    limit_ = limit_ + delta;
+    // TODO(gerbens) We could remove this line and hoist the code to
+    // DoneFallback. Study the perf/bin-size effects.
+    limit_end_ = buffer_end_ + (std::min)(0, limit_);
+    return true;
+  }
+
+  PROTOBUF_NODISCARD const char* Skip(const char* ptr, int size) {
+    if (size <= buffer_end_ + kSlopBytes - ptr) {
+      return ptr + size;
+    }
+    return SkipFallback(ptr, size);
+  }
+  PROTOBUF_NODISCARD const char* ReadString(const char* ptr, int size,
+                                            std::string* s) {
+    if (size <= buffer_end_ + kSlopBytes - ptr) {
+      s->assign(ptr, size);
+      return ptr + size;
+    }
+    return ReadStringFallback(ptr, size, s);
+  }
+  PROTOBUF_NODISCARD const char* AppendString(const char* ptr, int size,
+                                              std::string* s) {
+    if (size <= buffer_end_ + kSlopBytes - ptr) {
+      s->append(ptr, size);
+      return ptr + size;
+    }
+    return AppendStringFallback(ptr, size, s);
+  }
+  // Implemented in arenastring.cc
+  PROTOBUF_NODISCARD const char* ReadArenaString(const char* ptr,
+                                                 ArenaStringPtr* s,
+                                                 Arena* arena);
+
+  template <typename Tag, typename T>
+  PROTOBUF_NODISCARD const char* ReadRepeatedFixed(const char* ptr,
+                                                   Tag expected_tag,
+                                                   RepeatedField<T>* out);
+
+  template <typename T>
+  PROTOBUF_NODISCARD const char* ReadPackedFixed(const char* ptr, int size,
+                                                 RepeatedField<T>* out);
+  template <typename Add>
+  PROTOBUF_NODISCARD const char* ReadPackedVarint(const char* ptr, Add add);
+
+  uint32_t LastTag() const { return last_tag_minus_1_ + 1; }
+  bool ConsumeEndGroup(uint32_t start_tag) {
+    bool res = last_tag_minus_1_ == start_tag;
+    last_tag_minus_1_ = 0;
+    return res;
+  }
+  bool EndedAtLimit() const { return last_tag_minus_1_ == 0; }
+  bool EndedAtEndOfStream() const { return last_tag_minus_1_ == 1; }
+  void SetLastTag(uint32_t tag) { last_tag_minus_1_ = tag - 1; }
+  void SetEndOfStream() { last_tag_minus_1_ = 1; }
+  bool IsExceedingLimit(const char* ptr) {
+    return ptr > limit_end_ &&
+           (next_chunk_ == nullptr || ptr - buffer_end_ > limit_);
+  }
+  bool AliasingEnabled() const { return aliasing_ != kNoAliasing; }
+  int BytesUntilLimit(const char* ptr) const {
+    return limit_ + static_cast<int>(buffer_end_ - ptr);
+  }
+  // Returns true if more data is available, if false is returned one has to
+  // call Done for further checks.
+  bool DataAvailable(const char* ptr) { return ptr < limit_end_; }
+
+ protected:
+  // Returns true is limit (either an explicit limit or end of stream) is
+  // reached. It aligns *ptr across buffer seams.
+  // If limit is exceeded it returns true and ptr is set to null.
+  bool DoneWithCheck(const char** ptr, int d) {
+    GOOGLE_DCHECK(*ptr);
+    if (PROTOBUF_PREDICT_TRUE(*ptr < limit_end_)) return false;
+    int overrun = static_cast<int>(*ptr - buffer_end_);
+    GOOGLE_DCHECK_LE(overrun, kSlopBytes);  // Guaranteed by parse loop.
+    if (overrun ==
+        limit_) {  //  No need to flip buffers if we ended on a limit.
+      // If we actually overrun the buffer and next_chunk_ is null. It means
+      // the stream ended and we passed the stream end.
+      if (overrun > 0 && next_chunk_ == nullptr) *ptr = nullptr;
+      return true;
+    }
+    auto res = DoneFallback(overrun, d);
+    *ptr = res.first;
+    return res.second;
+  }
+
+  const char* InitFrom(StringPiece flat) {
+    overall_limit_ = 0;
+    if (flat.size() > kSlopBytes) {
+      limit_ = kSlopBytes;
+      limit_end_ = buffer_end_ = flat.data() + flat.size() - kSlopBytes;
+      next_chunk_ = buffer_;
+      if (aliasing_ == kOnPatch) aliasing_ = kNoDelta;
+      return flat.data();
+    } else {
+      std::memcpy(buffer_, flat.data(), flat.size());
+      limit_ = 0;
+      limit_end_ = buffer_end_ = buffer_ + flat.size();
+      next_chunk_ = nullptr;
+      if (aliasing_ == kOnPatch) {
+        aliasing_ = reinterpret_cast<std::uintptr_t>(flat.data()) -
+                    reinterpret_cast<std::uintptr_t>(buffer_);
+      }
+      return buffer_;
+    }
+  }
+
+  const char* InitFrom(io::ZeroCopyInputStream* zcis);
+
+  const char* InitFrom(io::ZeroCopyInputStream* zcis, int limit) {
+    if (limit == -1) return InitFrom(zcis);
+    overall_limit_ = limit;
+    auto res = InitFrom(zcis);
+    limit_ = limit - static_cast<int>(buffer_end_ - res);
+    limit_end_ = buffer_end_ + (std::min)(0, limit_);
+    return res;
+  }
+
+ private:
+  const char* limit_end_;  // buffer_end_ + min(limit_, 0)
+  const char* buffer_end_;
+  const char* next_chunk_;
+  int size_;
+  int limit_;  // relative to buffer_end_;
+  io::ZeroCopyInputStream* zcis_ = nullptr;
+  char buffer_[2 * kSlopBytes] = {};
+  enum { kNoAliasing = 0, kOnPatch = 1, kNoDelta = 2 };
+  std::uintptr_t aliasing_ = kNoAliasing;
+  // This variable is used to communicate how the parse ended, in order to
+  // completely verify the parsed data. A wire-format parse can end because of
+  // one of the following conditions:
+  // 1) A parse can end on a pushed limit.
+  // 2) A parse can end on End Of Stream (EOS).
+  // 3) A parse can end on 0 tag (only valid for toplevel message).
+  // 4) A parse can end on an end-group tag.
+  // This variable should always be set to 0, which indicates case 1. If the
+  // parse terminated due to EOS (case 2), it's set to 1. In case the parse
+  // ended due to a terminating tag (case 3 and 4) it's set to (tag - 1).
+  // This var doesn't really belong in EpsCopyInputStream and should be part of
+  // the ParseContext, but case 2 is most easily and optimally implemented in
+  // DoneFallback.
+  uint32_t last_tag_minus_1_ = 0;
+  int overall_limit_ = INT_MAX;  // Overall limit independent of pushed limits.
+  // Pretty random large number that seems like a safe allocation on most
+  // systems. TODO(gerbens) do we need to set this as build flag?
+  enum { kSafeStringSize = 50000000 };
+
+  // Advances to next buffer chunk returns a pointer to the same logical place
+  // in the stream as set by overrun. Overrun indicates the position in the slop
+  // region the parse was left (0 <= overrun <= kSlopBytes). Returns true if at
+  // limit, at which point the returned pointer maybe null if there was an
+  // error. The invariant of this function is that it's guaranteed that
+  // kSlopBytes bytes can be accessed from the returned ptr. This function might
+  // advance more buffers than one in the underlying ZeroCopyInputStream.
+  std::pair<const char*, bool> DoneFallback(int overrun, int depth);
+  // Advances to the next buffer, at most one call to Next() on the underlying
+  // ZeroCopyInputStream is made. This function DOES NOT match the returned
+  // pointer to where in the slop region the parse ends, hence no overrun
+  // parameter. This is useful for string operations where you always copy
+  // to the end of the buffer (including the slop region).
+  const char* Next();
+  // overrun is the location in the slop region the stream currently is
+  // (0 <= overrun <= kSlopBytes). To prevent flipping to the next buffer of
+  // the ZeroCopyInputStream in the case the parse will end in the last
+  // kSlopBytes of the current buffer. depth is the current depth of nested
+  // groups (or negative if the use case does not need careful tracking).
+  inline const char* NextBuffer(int overrun, int depth);
+  const char* SkipFallback(const char* ptr, int size);
+  const char* AppendStringFallback(const char* ptr, int size, std::string* str);
+  const char* ReadStringFallback(const char* ptr, int size, std::string* str);
+  bool StreamNext(const void** data) {
+    bool res = zcis_->Next(data, &size_);
+    if (res) overall_limit_ -= size_;
+    return res;
+  }
+  void StreamBackUp(int count) {
+    zcis_->BackUp(count);
+    overall_limit_ += count;
+  }
+
+  template <typename A>
+  const char* AppendSize(const char* ptr, int size, const A& append) {
+    int chunk_size = buffer_end_ + kSlopBytes - ptr;
+    do {
+      GOOGLE_DCHECK(size > chunk_size);
+      if (next_chunk_ == nullptr) return nullptr;
+      append(ptr, chunk_size);
+      ptr += chunk_size;
+      size -= chunk_size;
+      // TODO(gerbens) Next calls NextBuffer which generates buffers with
+      // overlap and thus incurs cost of copying the slop regions. This is not
+      // necessary for reading strings. We should just call Next buffers.
+      if (limit_ <= kSlopBytes) return nullptr;
+      ptr = Next();
+      if (ptr == nullptr) return nullptr;  // passed the limit
+      ptr += kSlopBytes;
+      chunk_size = buffer_end_ + kSlopBytes - ptr;
+    } while (size > chunk_size);
+    append(ptr, size);
+    return ptr + size;
+  }
+
+  // AppendUntilEnd appends data until a limit (either a PushLimit or end of
+  // stream. Normal payloads are from length delimited fields which have an
+  // explicit size. Reading until limit only comes when the string takes
+  // the place of a protobuf, ie RawMessage/StringRawMessage, lazy fields and
+  // implicit weak messages. We keep these methods private and friend them.
+  template <typename A>
+  const char* AppendUntilEnd(const char* ptr, const A& append) {
+    if (ptr - buffer_end_ > limit_) return nullptr;
+    while (limit_ > kSlopBytes) {
+      size_t chunk_size = buffer_end_ + kSlopBytes - ptr;
+      append(ptr, chunk_size);
+      ptr = Next();
+      if (ptr == nullptr) return limit_end_;
+      ptr += kSlopBytes;
+    }
+    auto end = buffer_end_ + limit_;
+    GOOGLE_DCHECK(end >= ptr);
+    append(ptr, end - ptr);
+    return end;
+  }
+
+  PROTOBUF_NODISCARD const char* AppendString(const char* ptr,
+                                              std::string* str) {
+    return AppendUntilEnd(
+        ptr, [str](const char* p, ptrdiff_t s) { str->append(p, s); });
+  }
+  friend class ImplicitWeakMessage;
+};
+
+using LazyEagerVerifyFnType = const char* (*)(const char* ptr,
+                                              ParseContext* ctx);
+using LazyEagerVerifyFnRef = std::remove_pointer<LazyEagerVerifyFnType>::type&;
+
+// ParseContext holds all data that is global to the entire parse. Most
+// importantly it contains the input stream, but also recursion depth and also
+// stores the end group tag, in case a parser ended on a endgroup, to verify
+// matching start/end group tags.
+class PROTOBUF_EXPORT ParseContext : public EpsCopyInputStream {
+ public:
+  struct Data {
+    const DescriptorPool* pool = nullptr;
+    MessageFactory* factory = nullptr;
+    Arena* arena = nullptr;
+  };
+
+  template <typename... T>
+  ParseContext(int depth, bool aliasing, const char** start, T&&... args)
+      : EpsCopyInputStream(aliasing), depth_(depth) {
+    *start = InitFrom(std::forward<T>(args)...);
+  }
+
+  void TrackCorrectEnding() { group_depth_ = 0; }
+
+  bool Done(const char** ptr) { return DoneWithCheck(ptr, group_depth_); }
+
+  int depth() const { return depth_; }
+
+  Data& data() { return data_; }
+  const Data& data() const { return data_; }
+
+  const char* ParseMessage(MessageLite* msg, const char* ptr);
+
+  // Spawns a child parsing context that inherits key properties. New context
+  // inherits the following:
+  // --depth_, data_, check_required_fields_, lazy_parse_mode_
+  // The spawned context always disables aliasing (different input).
+  template <typename... T>
+  ParseContext Spawn(const char** start, T&&... args) {
+    ParseContext spawned(depth_, false, start, std::forward<T>(args)...);
+    // Transfer key context states.
+    spawned.data_ = data_;
+    return spawned;
+  }
+
+  // This overload supports those few cases where ParseMessage is called
+  // on a class that is not actually a proto message.
+  // TODO(jorg): Eliminate this use case.
+  template <typename T,
+            typename std::enable_if<!std::is_base_of<MessageLite, T>::value,
+                                    bool>::type = true>
+  PROTOBUF_NODISCARD const char* ParseMessage(T* msg, const char* ptr);
+
+  template <typename T>
+  PROTOBUF_NODISCARD PROTOBUF_NDEBUG_INLINE const char* ParseGroup(
+      T* msg, const char* ptr, uint32_t tag) {
+    if (--depth_ < 0) return nullptr;
+    group_depth_++;
+    ptr = msg->_InternalParse(ptr, this);
+    group_depth_--;
+    depth_++;
+    if (PROTOBUF_PREDICT_FALSE(!ConsumeEndGroup(tag))) return nullptr;
+    return ptr;
+  }
+
+ private:
+  // Out-of-line routine to save space in ParseContext::ParseMessage<T>
+  //   int old;
+  //   ptr = ReadSizeAndPushLimitAndDepth(ptr, &old)
+  // is equivalent to:
+  //   int size = ReadSize(&ptr);
+  //   if (!ptr) return nullptr;
+  //   int old = PushLimit(ptr, size);
+  //   if (--depth_ < 0) return nullptr;
+  PROTOBUF_NODISCARD const char* ReadSizeAndPushLimitAndDepth(const char* ptr,
+                                                              int* old_limit);
+
+  // The context keeps an internal stack to keep track of the recursive
+  // part of the parse state.
+  // Current depth of the active parser, depth counts down.
+  // This is used to limit recursion depth (to prevent overflow on malicious
+  // data), but is also used to index in stack_ to store the current state.
+  int depth_;
+  // Unfortunately necessary for the fringe case of ending on 0 or end-group tag
+  // in the last kSlopBytes of a ZeroCopyInputStream chunk.
+  int group_depth_ = INT_MIN;
+  Data data_;
+};
+
+template <uint32_t tag>
+bool ExpectTag(const char* ptr) {
+  if (tag < 128) {
+    return *ptr == static_cast<char>(tag);
+  } else {
+    static_assert(tag < 128 * 128, "We only expect tags for 1 or 2 bytes");
+    char buf[2] = {static_cast<char>(tag | 0x80), static_cast<char>(tag >> 7)};
+    return std::memcmp(ptr, buf, 2) == 0;
+  }
+}
+
+template <int>
+struct EndianHelper;
+
+template <>
+struct EndianHelper<1> {
+  static uint8_t Load(const void* p) { return *static_cast<const uint8_t*>(p); }
+};
+
+template <>
+struct EndianHelper<2> {
+  static uint16_t Load(const void* p) {
+    uint16_t tmp;
+    std::memcpy(&tmp, p, 2);
+    return little_endian::ToHost(tmp);
+  }
+};
+
+template <>
+struct EndianHelper<4> {
+  static uint32_t Load(const void* p) {
+    uint32_t tmp;
+    std::memcpy(&tmp, p, 4);
+    return little_endian::ToHost(tmp);
+  }
+};
+
+template <>
+struct EndianHelper<8> {
+  static uint64_t Load(const void* p) {
+    uint64_t tmp;
+    std::memcpy(&tmp, p, 8);
+    return little_endian::ToHost(tmp);
+  }
+};
+
+template <typename T>
+T UnalignedLoad(const char* p) {
+  auto tmp = EndianHelper<sizeof(T)>::Load(p);
+  T res;
+  memcpy(&res, &tmp, sizeof(T));
+  return res;
+}
+
+PROTOBUF_EXPORT
+std::pair<const char*, uint32_t> VarintParseSlow32(const char* p, uint32_t res);
+PROTOBUF_EXPORT
+std::pair<const char*, uint64_t> VarintParseSlow64(const char* p, uint32_t res);
+
+inline const char* VarintParseSlow(const char* p, uint32_t res, uint32_t* out) {
+  auto tmp = VarintParseSlow32(p, res);
+  *out = tmp.second;
+  return tmp.first;
+}
+
+inline const char* VarintParseSlow(const char* p, uint32_t res, uint64_t* out) {
+  auto tmp = VarintParseSlow64(p, res);
+  *out = tmp.second;
+  return tmp.first;
+}
+
+template <typename T>
+PROTOBUF_NODISCARD const char* VarintParse(const char* p, T* out) {
+  auto ptr = reinterpret_cast<const uint8_t*>(p);
+  uint32_t res = ptr[0];
+  if (!(res & 0x80)) {
+    *out = res;
+    return p + 1;
+  }
+  uint32_t byte = ptr[1];
+  res += (byte - 1) << 7;
+  if (!(byte & 0x80)) {
+    *out = res;
+    return p + 2;
+  }
+  return VarintParseSlow(p, res, out);
+}
+
+// Used for tags, could read up to 5 bytes which must be available.
+// Caller must ensure its safe to call.
+
+PROTOBUF_EXPORT
+std::pair<const char*, uint32_t> ReadTagFallback(const char* p, uint32_t res);
+
+// Same as ParseVarint but only accept 5 bytes at most.
+inline const char* ReadTag(const char* p, uint32_t* out,
+                           uint32_t /*max_tag*/ = 0) {
+  uint32_t res = static_cast<uint8_t>(p[0]);
+  if (res < 128) {
+    *out = res;
+    return p + 1;
+  }
+  uint32_t second = static_cast<uint8_t>(p[1]);
+  res += (second - 1) << 7;
+  if (second < 128) {
+    *out = res;
+    return p + 2;
+  }
+  auto tmp = ReadTagFallback(p, res);
+  *out = tmp.second;
+  return tmp.first;
+}
+
+// As above, but optimized to consume very few registers while still being fast,
+// ReadTagInlined is useful for callers that don't mind the extra code but would
+// like to avoid an extern function call causing spills into the stack.
+//
+// Two support routines for ReadTagInlined come first...
+template <class T>
+PROTOBUF_NODISCARD PROTOBUF_ALWAYS_INLINE constexpr T RotateLeft(
+    T x, int s) noexcept {
+  return static_cast<T>(x << (s & (std::numeric_limits<T>::digits - 1))) |
+         static_cast<T>(x >> ((-s) & (std::numeric_limits<T>::digits - 1)));
+}
+
+PROTOBUF_NODISCARD inline PROTOBUF_ALWAYS_INLINE uint64_t
+RotRight7AndReplaceLowByte(uint64_t res, const char& byte) {
+#if defined(__x86_64__) && defined(__GNUC__)
+  // This will only use one register for `res`.
+  // `byte` comes as a reference to allow the compiler to generate code like:
+  //
+  //   rorq    $7, %rcx
+  //   movb    1(%rax), %cl
+  //
+  // which avoids loading the incoming bytes into a separate register first.
+  asm("ror $7,%0\n\t"
+      "movb %1,%b0"
+      : "+r"(res)
+      : "m"(byte));
+#else
+  res = RotateLeft(res, -7);
+  res = res & ~0xFF;
+  res |= 0xFF & byte;
+#endif
+  return res;
+};
+
+inline PROTOBUF_ALWAYS_INLINE
+const char* ReadTagInlined(const char* ptr, uint32_t* out) {
+  uint64_t res = 0xFF & ptr[0];
+  if (PROTOBUF_PREDICT_FALSE(res >= 128)) {
+    res = RotRight7AndReplaceLowByte(res, ptr[1]);
+    if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
+      res = RotRight7AndReplaceLowByte(res, ptr[2]);
+      if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
+        res = RotRight7AndReplaceLowByte(res, ptr[3]);
+        if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
+          // Note: this wouldn't work if res were 32-bit,
+          // because then replacing the low byte would overwrite
+          // the bottom 4 bits of the result.
+          res = RotRight7AndReplaceLowByte(res, ptr[4]);
+          if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
+            // The proto format does not permit longer than 5-byte encodings for
+            // tags.
+            *out = 0;
+            return nullptr;
+          }
+          *out = static_cast<uint32_t>(RotateLeft(res, 28));
+#if defined(__GNUC__)
+          // Note: this asm statement prevents the compiler from
+          // trying to share the "return ptr + constant" among all
+          // branches.
+          asm("" : "+r"(ptr));
+#endif
+          return ptr + 5;
+        }
+        *out = static_cast<uint32_t>(RotateLeft(res, 21));
+        return ptr + 4;
+      }
+      *out = static_cast<uint32_t>(RotateLeft(res, 14));
+      return ptr + 3;
+    }
+    *out = static_cast<uint32_t>(RotateLeft(res, 7));
+    return ptr + 2;
+  }
+  *out = static_cast<uint32_t>(res);
+  return ptr + 1;
+}
+
+// Decode 2 consecutive bytes of a varint and returns the value, shifted left
+// by 1. It simultaneous updates *ptr to *ptr + 1 or *ptr + 2 depending if the
+// first byte's continuation bit is set.
+// If bit 15 of return value is set (equivalent to the continuation bits of both
+// bytes being set) the varint continues, otherwise the parse is done. On x86
+// movsx eax, dil
+// and edi, eax
+// add eax, edi
+// adc [rsi], 1
+inline uint32_t DecodeTwoBytes(const char** ptr) {
+  uint32_t value = UnalignedLoad<uint16_t>(*ptr);
+  // Sign extend the low byte continuation bit
+  uint32_t x = static_cast<int8_t>(value);
+  value &= x;  // Mask out the high byte iff no continuation
+  // This add is an amazing operation, it cancels the low byte continuation bit
+  // from y transferring it to the carry. Simultaneously it also shifts the 7
+  // LSB left by one tightly against high byte varint bits. Hence value now
+  // contains the unpacked value shifted left by 1.
+  value += x;
+  // Use the carry to update the ptr appropriately.
+  *ptr += value < x ? 2 : 1;
+  return value;
+}
+
+// More efficient varint parsing for big varints
+inline const char* ParseBigVarint(const char* p, uint64_t* out) {
+  auto pnew = p;
+  auto tmp = DecodeTwoBytes(&pnew);
+  uint64_t res = tmp >> 1;
+  if (PROTOBUF_PREDICT_TRUE(static_cast<std::int16_t>(tmp) >= 0)) {
+    *out = res;
+    return pnew;
+  }
+  for (std::uint32_t i = 1; i < 5; i++) {
+    pnew = p + 2 * i;
+    tmp = DecodeTwoBytes(&pnew);
+    res += (static_cast<std::uint64_t>(tmp) - 2) << (14 * i - 1);
+    if (PROTOBUF_PREDICT_TRUE(static_cast<std::int16_t>(tmp) >= 0)) {
+      *out = res;
+      return pnew;
+    }
+  }
+  return nullptr;
+}
+
+PROTOBUF_EXPORT
+std::pair<const char*, int32_t> ReadSizeFallback(const char* p, uint32_t first);
+// Used for tags, could read up to 5 bytes which must be available. Additionally
+// it makes sure the unsigned value fits a int32_t, otherwise returns nullptr.
+// Caller must ensure its safe to call.
+inline uint32_t ReadSize(const char** pp) {
+  auto p = *pp;
+  uint32_t res = static_cast<uint8_t>(p[0]);
+  if (res < 128) {
+    *pp = p + 1;
+    return res;
+  }
+  auto x = ReadSizeFallback(p, res);
+  *pp = x.first;
+  return x.second;
+}
+
+// Some convenience functions to simplify the generated parse loop code.
+// Returning the value and updating the buffer pointer allows for nicer
+// function composition. We rely on the compiler to inline this.
+// Also in debug compiles having local scoped variables tend to generated
+// stack frames that scale as O(num fields).
+inline uint64_t ReadVarint64(const char** p) {
+  uint64_t tmp;
+  *p = VarintParse(*p, &tmp);
+  return tmp;
+}
+
+inline uint32_t ReadVarint32(const char** p) {
+  uint32_t tmp;
+  *p = VarintParse(*p, &tmp);
+  return tmp;
+}
+
+inline int64_t ReadVarintZigZag64(const char** p) {
+  uint64_t tmp;
+  *p = VarintParse(*p, &tmp);
+  return WireFormatLite::ZigZagDecode64(tmp);
+}
+
+inline int32_t ReadVarintZigZag32(const char** p) {
+  uint64_t tmp;
+  *p = VarintParse(*p, &tmp);
+  return WireFormatLite::ZigZagDecode32(static_cast<uint32_t>(tmp));
+}
+
+template <typename T, typename std::enable_if<
+                          !std::is_base_of<MessageLite, T>::value, bool>::type>
+PROTOBUF_NODISCARD const char* ParseContext::ParseMessage(T* msg,
+                                                          const char* ptr) {
+  int old;
+  ptr = ReadSizeAndPushLimitAndDepth(ptr, &old);
+  ptr = ptr ? msg->_InternalParse(ptr, this) : nullptr;
+  depth_++;
+  if (!PopLimit(old)) return nullptr;
+  return ptr;
+}
+
+template <typename Tag, typename T>
+const char* EpsCopyInputStream::ReadRepeatedFixed(const char* ptr,
+                                                  Tag expected_tag,
+                                                  RepeatedField<T>* out) {
+  do {
+    out->Add(UnalignedLoad<T>(ptr));
+    ptr += sizeof(T);
+    if (PROTOBUF_PREDICT_FALSE(ptr >= limit_end_)) return ptr;
+  } while (UnalignedLoad<Tag>(ptr) == expected_tag && (ptr += sizeof(Tag)));
+  return ptr;
+}
+
+// Add any of the following lines to debug which parse function is failing.
+
+#define GOOGLE_PROTOBUF_ASSERT_RETURN(predicate, ret) \
+  if (!(predicate)) {                                  \
+    /*  ::raise(SIGINT);  */                           \
+    /*  GOOGLE_LOG(ERROR) << "Parse failure";  */             \
+    return ret;                                        \
+  }
+
+#define GOOGLE_PROTOBUF_PARSER_ASSERT(predicate) \
+  GOOGLE_PROTOBUF_ASSERT_RETURN(predicate, nullptr)
+
+template <typename T>
+const char* EpsCopyInputStream::ReadPackedFixed(const char* ptr, int size,
+                                                RepeatedField<T>* out) {
+  GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+  int nbytes = buffer_end_ + kSlopBytes - ptr;
+  while (size > nbytes) {
+    int num = nbytes / sizeof(T);
+    int old_entries = out->size();
+    out->Reserve(old_entries + num);
+    int block_size = num * sizeof(T);
+    auto dst = out->AddNAlreadyReserved(num);
+#ifdef PROTOBUF_LITTLE_ENDIAN
+    std::memcpy(dst, ptr, block_size);
+#else
+    for (int i = 0; i < num; i++)
+      dst[i] = UnalignedLoad<T>(ptr + i * sizeof(T));
+#endif
+    size -= block_size;
+    if (limit_ <= kSlopBytes) return nullptr;
+    ptr = Next();
+    if (ptr == nullptr) return nullptr;
+    ptr += kSlopBytes - (nbytes - block_size);
+    nbytes = buffer_end_ + kSlopBytes - ptr;
+  }
+  int num = size / sizeof(T);
+  int old_entries = out->size();
+  out->Reserve(old_entries + num);
+  int block_size = num * sizeof(T);
+  auto dst = out->AddNAlreadyReserved(num);
+#ifdef PROTOBUF_LITTLE_ENDIAN
+  std::memcpy(dst, ptr, block_size);
+#else
+  for (int i = 0; i < num; i++) dst[i] = UnalignedLoad<T>(ptr + i * sizeof(T));
+#endif
+  ptr += block_size;
+  if (size != block_size) return nullptr;
+  return ptr;
+}
+
+template <typename Add>
+const char* ReadPackedVarintArray(const char* ptr, const char* end, Add add) {
+  while (ptr < end) {
+    uint64_t varint;
+    ptr = VarintParse(ptr, &varint);
+    if (ptr == nullptr) return nullptr;
+    add(varint);
+  }
+  return ptr;
+}
+
+template <typename Add>
+const char* EpsCopyInputStream::ReadPackedVarint(const char* ptr, Add add) {
+  int size = ReadSize(&ptr);
+  GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+  int chunk_size = buffer_end_ - ptr;
+  while (size > chunk_size) {
+    ptr = ReadPackedVarintArray(ptr, buffer_end_, add);
+    if (ptr == nullptr) return nullptr;
+    int overrun = ptr - buffer_end_;
+    GOOGLE_DCHECK(overrun >= 0 && overrun <= kSlopBytes);
+    if (size - chunk_size <= kSlopBytes) {
+      // The current buffer contains all the information needed, we don't need
+      // to flip buffers. However we must parse from a buffer with enough space
+      // so we are not prone to a buffer overflow.
+      char buf[kSlopBytes + 10] = {};
+      std::memcpy(buf, buffer_end_, kSlopBytes);
+      GOOGLE_CHECK_LE(size - chunk_size, kSlopBytes);
+      auto end = buf + (size - chunk_size);
+      auto res = ReadPackedVarintArray(buf + overrun, end, add);
+      if (res == nullptr || res != end) return nullptr;
+      return buffer_end_ + (res - buf);
+    }
+    size -= overrun + chunk_size;
+    GOOGLE_DCHECK_GT(size, 0);
+    // We must flip buffers
+    if (limit_ <= kSlopBytes) return nullptr;
+    ptr = Next();
+    if (ptr == nullptr) return nullptr;
+    ptr += overrun;
+    chunk_size = buffer_end_ - ptr;
+  }
+  auto end = ptr + size;
+  ptr = ReadPackedVarintArray(ptr, end, add);
+  return end == ptr ? ptr : nullptr;
+}
+
+// Helper for verification of utf8
+PROTOBUF_EXPORT
+bool VerifyUTF8(StringPiece s, const char* field_name);
+
+inline bool VerifyUTF8(const std::string* s, const char* field_name) {
+  return VerifyUTF8(*s, field_name);
+}
+
+// All the string parsers with or without UTF checking and for all CTypes.
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* InlineGreedyStringParser(
+    std::string* s, const char* ptr, ParseContext* ctx);
+
+
+template <typename T>
+PROTOBUF_NODISCARD const char* FieldParser(uint64_t tag, T& field_parser,
+                                           const char* ptr, ParseContext* ctx) {
+  uint32_t number = tag >> 3;
+  GOOGLE_PROTOBUF_PARSER_ASSERT(number != 0);
+  using WireType = internal::WireFormatLite::WireType;
+  switch (tag & 7) {
+    case WireType::WIRETYPE_VARINT: {
+      uint64_t value;
+      ptr = VarintParse(ptr, &value);
+      GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+      field_parser.AddVarint(number, value);
+      break;
+    }
+    case WireType::WIRETYPE_FIXED64: {
+      uint64_t value = UnalignedLoad<uint64_t>(ptr);
+      ptr += 8;
+      field_parser.AddFixed64(number, value);
+      break;
+    }
+    case WireType::WIRETYPE_LENGTH_DELIMITED: {
+      ptr = field_parser.ParseLengthDelimited(number, ptr, ctx);
+      GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+      break;
+    }
+    case WireType::WIRETYPE_START_GROUP: {
+      ptr = field_parser.ParseGroup(number, ptr, ctx);
+      GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+      break;
+    }
+    case WireType::WIRETYPE_END_GROUP: {
+      GOOGLE_LOG(FATAL) << "Can't happen";
+      break;
+    }
+    case WireType::WIRETYPE_FIXED32: {
+      uint32_t value = UnalignedLoad<uint32_t>(ptr);
+      ptr += 4;
+      field_parser.AddFixed32(number, value);
+      break;
+    }
+    default:
+      return nullptr;
+  }
+  return ptr;
+}
+
+template <typename T>
+PROTOBUF_NODISCARD const char* WireFormatParser(T& field_parser,
+                                                const char* ptr,
+                                                ParseContext* ctx) {
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ReadTag(ptr, &tag);
+    GOOGLE_PROTOBUF_PARSER_ASSERT(ptr != nullptr);
+    if (tag == 0 || (tag & 7) == 4) {
+      ctx->SetLastTag(tag);
+      return ptr;
+    }
+    ptr = FieldParser(tag, field_parser, ptr, ctx);
+    GOOGLE_PROTOBUF_PARSER_ASSERT(ptr != nullptr);
+  }
+  return ptr;
+}
+
+// The packed parsers parse repeated numeric primitives directly into  the
+// corresponding field
+
+// These are packed varints
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedInt32Parser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedUInt32Parser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedInt64Parser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedUInt64Parser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedSInt32Parser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedSInt64Parser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedEnumParser(
+    void* object, const char* ptr, ParseContext* ctx);
+
+template <typename T>
+PROTOBUF_NODISCARD const char* PackedEnumParser(void* object, const char* ptr,
+                                                ParseContext* ctx,
+                                                bool (*is_valid)(int),
+                                                InternalMetadata* metadata,
+                                                int field_num) {
+  return ctx->ReadPackedVarint(
+      ptr, [object, is_valid, metadata, field_num](uint64_t val) {
+        if (is_valid(val)) {
+          static_cast<RepeatedField<int>*>(object)->Add(val);
+        } else {
+          WriteVarint(field_num, val, metadata->mutable_unknown_fields<T>());
+        }
+      });
+}
+
+template <typename T>
+PROTOBUF_NODISCARD const char* PackedEnumParserArg(
+    void* object, const char* ptr, ParseContext* ctx,
+    bool (*is_valid)(const void*, int), const void* data,
+    InternalMetadata* metadata, int field_num) {
+  return ctx->ReadPackedVarint(
+      ptr, [object, is_valid, data, metadata, field_num](uint64_t val) {
+        if (is_valid(data, val)) {
+          static_cast<RepeatedField<int>*>(object)->Add(val);
+        } else {
+          WriteVarint(field_num, val, metadata->mutable_unknown_fields<T>());
+        }
+      });
+}
+
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedBoolParser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedFixed32Parser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedSFixed32Parser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedFixed64Parser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedSFixed64Parser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedFloatParser(
+    void* object, const char* ptr, ParseContext* ctx);
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* PackedDoubleParser(
+    void* object, const char* ptr, ParseContext* ctx);
+
+// This is the only recursive parser.
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* UnknownGroupLiteParse(
+    std::string* unknown, const char* ptr, ParseContext* ctx);
+// This is a helper to for the UnknownGroupLiteParse but is actually also
+// useful in the generated code. It uses overload on std::string* vs
+// UnknownFieldSet* to make the generated code isomorphic between full and lite.
+PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* UnknownFieldParse(
+    uint32_t tag, std::string* unknown, const char* ptr, ParseContext* ctx);
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_PARSE_CONTEXT_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/port.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/port.h
new file mode 100644
index 0000000..a5c060b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/port.h
@@ -0,0 +1,80 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// A common header that is included across all protobuf headers.  We do our best
+// to avoid #defining any macros here; instead we generally put macros in
+// port_def.inc and port_undef.inc so they are not visible from outside of
+// protobuf.
+
+#ifndef GOOGLE_PROTOBUF_PORT_H__
+#define GOOGLE_PROTOBUF_PORT_H__
+
+#include <cstddef>
+#include <new>
+
+
+namespace google {
+namespace protobuf {
+namespace internal {
+inline void SizedDelete(void* p, size_t size) {
+#if defined(__cpp_sized_deallocation)
+  ::operator delete(p, size);
+#else
+  ::operator delete(p);
+#endif
+}
+inline void SizedArrayDelete(void* p, size_t size) {
+#if defined(__cpp_sized_deallocation)
+  ::operator delete[](p, size);
+#else
+  ::operator delete[](p);
+#endif
+}
+
+// Tag type used to invoke the constinit constructor overload of classes
+// such as ArenaStringPtr and MapFieldBase. Such constructors are internal
+// implementation details of the library.
+struct ConstantInitialized {
+  explicit ConstantInitialized() = default;
+};
+
+// Tag type used to invoke the arena constructor overload of classes such
+// as ExtensionSet and MapFieldLite in aggregate initialization. These
+// classes typically don't have move/copy constructors, which rules out
+// explicit initialization in pre-C++17.
+struct ArenaInitialized {
+  explicit ArenaInitialized() = default;
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_PORT_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/port_def.inc b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/port_def.inc
new file mode 100644
index 0000000..6c6aa18
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/port_def.inc
@@ -0,0 +1,928 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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 file defines common macros that are used in protobuf.
+//
+// To hide these definitions from the outside world (and to prevent collisions
+// if more than one version of protobuf is #included in the same project) you
+// must follow this pattern when #including port_def.inc in a header file:
+//
+// #include "other_header.h"
+// #include "message.h"
+// // etc.
+//
+// #include "port_def.inc"  // MUST be last header included
+//
+// // Definitions for this header.
+//
+// #include "port_undef.inc"
+//
+// This is a textual header with no include guard, because we want to
+// detect/prohibit anytime it is #included twice without a corresponding
+// #undef.
+
+// The definitions in this file are intended to be portable across Clang,
+// GCC, and MSVC. Function-like macros are usable without an #ifdef guard.
+// Syntax macros (for example, attributes) are always defined, although
+// they may be empty.
+//
+// Some definitions rely on the NDEBUG macro and/or (in MSVC) _DEBUG:
+// - https://en.cppreference.com/w/c/error/assert
+// - https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros#microsoft-specific-predefined-macros
+//
+// References for predefined macros:
+// - Standard: https://en.cppreference.com/w/cpp/preprocessor/replace
+// - Clang: https://clang.llvm.org/docs/LanguageExtensions.html
+//          (see also GCC predefined macros)
+// - GCC: https://gcc.gnu.org/onlinedocs/cpp/Predefined-Macros.html
+// - MSVC: https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
+// - Interactive (Clang/GCC only): https://www.compiler-explorer.com/z/hc6jKd3sj
+//
+// References for attributes (and extension attributes):
+// - Standard: https://en.cppreference.com/w/cpp/language/attributes
+// - Clang: https://clang.llvm.org/docs/AttributeReference.html
+// - GCC: https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
+//        (see Clang attribute docs as well)
+//
+// References for standard C++ language conformance (and minimum versions):
+// - Clang: https://clang.llvm.org/cxx_status.html
+// - GCC: https://gcc.gnu.org/projects/cxx-status.html
+// - MSVC: https://docs.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance
+//
+// Historical release notes (which can help to determine minimum versions):
+// - Clang: https://releases.llvm.org/
+// - GCC: https://gcc.gnu.org/releases.html
+// - MSVC: https://docs.microsoft.com/en-us/visualstudio/releases/2019/release-notes-history
+//         https://docs.microsoft.com/en-us/visualstudio/releasenotes/vs2017-relnotes-history
+
+// Portable fallbacks for C++20 feature test macros:
+// https://en.cppreference.com/w/cpp/feature_test
+#ifndef __has_cpp_attribute
+#define __has_cpp_attribute(x) 0
+#define PROTOBUF_has_cpp_attribute_DEFINED_
+#endif
+
+// Portable fallback for Clang's __has_feature macro:
+// https://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension
+#ifndef __has_feature
+#define __has_feature(x) 0
+#define PROTOBUF_has_feature_DEFINED_
+#endif
+
+// Portable fallback for Clang's __has_warning macro:
+#ifndef __has_warning
+#define __has_warning(x) 0
+#define PROTOBUF_has_warning_DEFINED_
+#endif
+
+// Portable fallbacks for the __has_attribute macro (GCC and Clang):
+// https://clang.llvm.org/docs/LanguageExtensions.html#has-attribute
+// https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005fattribute.html
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#define PROTOBUF_has_attribute_DEFINED_
+#endif
+
+// Portable fallback for __has_builtin (GCC and Clang):
+// https://clang.llvm.org/docs/LanguageExtensions.html#has-builtin
+// https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005fbuiltin.html
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#define PROTOBUF_has_builtin_DEFINED_
+#endif
+
+// Portable PROTOBUF_BUILTIN_BSWAPxx definitions
+// Code must check for availability, e.g.: `defined(PROTOBUF_BUILTIN_BSWAP32)`
+#ifdef PROTOBUF_BUILTIN_BSWAP16
+#error PROTOBUF_BUILTIN_BSWAP16 was previously defined
+#endif
+#ifdef PROTOBUF_BUILTIN_BSWAP32
+#error PROTOBUF_BUILTIN_BSWAP32 was previously defined
+#endif
+#ifdef PROTOBUF_BUILTIN_BSWAP64
+#error PROTOBUF_BUILTIN_BSWAP64 was previously defined
+#endif
+#if defined(__GNUC__) || __has_builtin(__builtin_bswap16)
+#define PROTOBUF_BUILTIN_BSWAP16(x) __builtin_bswap16(x)
+#endif
+#if defined(__GNUC__) || __has_builtin(__builtin_bswap32)
+#define PROTOBUF_BUILTIN_BSWAP32(x) __builtin_bswap32(x)
+#endif
+#if defined(__GNUC__) || __has_builtin(__builtin_bswap64)
+#define PROTOBUF_BUILTIN_BSWAP64(x) __builtin_bswap64(x)
+#endif
+
+// Portable check for GCC minimum version:
+// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
+#if defined(__GNUC__) && defined(__GNUC_MINOR__) \
+    && defined(__GNUC_PATCHLEVEL__)
+#  define PROTOBUF_GNUC_MIN(x, y) \
+  (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
+#else
+#  define PROTOBUF_GNUC_MIN(x, y) 0
+#endif
+
+// Portable check for MSVC minimum version:
+// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
+#if defined(_MSC_VER)
+#define PROTOBUF_MSC_VER_MIN(x) (_MSC_VER >= x)
+#else
+#define PROTOBUF_MSC_VER_MIN(x) 0
+#endif
+
+// Portable check for minimum C++ language version:
+// https://en.cppreference.com/w/cpp/preprocessor/replace
+// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
+#if !defined(_MSVC_LANG)
+#define PROTOBUF_CPLUSPLUS_MIN(x) (__cplusplus >= x)
+#else
+#define PROTOBUF_CPLUSPLUS_MIN(x) (_MSVC_LANG >= x)
+#endif
+
+// Future versions of protobuf will include breaking changes to some APIs.
+// This macro can be set to enable these API changes ahead of time, so that
+// user code can be updated before upgrading versions of protobuf.
+// PROTOBUF_FUTURE_FINAL is used on classes that are historically not marked as
+// final, but that may be marked final in future (breaking) releases.
+// #define PROTOBUF_FUTURE_BREAKING_CHANGES 1
+// #define PROTOBUF_FUTURE_FINAL final
+#define PROTOBUF_FUTURE_FINAL
+
+#ifdef PROTOBUF_VERSION
+#error PROTOBUF_VERSION was previously defined
+#endif
+#define PROTOBUF_VERSION 3021012
+
+#ifdef PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC
+#error PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC was previously defined
+#endif
+#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC 3021000
+
+#ifdef PROTOBUF_MIN_PROTOC_VERSION
+#error PROTOBUF_MIN_PROTOC_VERSION was previously defined
+#endif
+#define PROTOBUF_MIN_PROTOC_VERSION 3021000
+
+#ifdef PROTOBUF_VERSION_SUFFIX
+#error PROTOBUF_VERSION_SUFFIX was previously defined
+#endif
+#define PROTOBUF_VERSION_SUFFIX ""
+
+#if defined(PROTOBUF_NAMESPACE) || defined(PROTOBUF_NAMESPACE_ID)
+#error PROTOBUF_NAMESPACE or PROTOBUF_NAMESPACE_ID was previously defined
+#endif
+#define PROTOBUF_NAMESPACE "google::protobuf"
+#define PROTOBUF_NAMESPACE_ID google::protobuf
+#define PROTOBUF_NAMESPACE_OPEN \
+  namespace google {            \
+  namespace protobuf {
+#define PROTOBUF_NAMESPACE_CLOSE \
+  } /* namespace protobuf */     \
+  } /* namespace google */
+
+#ifdef PROTOBUF_ALWAYS_INLINE
+#error PROTOBUF_ALWAYS_INLINE was previously defined
+#endif
+// For functions we want to force inline.
+#if defined(PROTOBUF_NO_INLINE)
+# define PROTOBUF_ALWAYS_INLINE
+#elif PROTOBUF_GNUC_MIN(3, 1)
+# define PROTOBUF_ALWAYS_INLINE __attribute__((always_inline))
+#elif defined(_MSC_VER)
+# define PROTOBUF_ALWAYS_INLINE __forceinline
+#else
+# define PROTOBUF_ALWAYS_INLINE
+#endif
+
+#ifdef PROTOBUF_NDEBUG_INLINE
+#error PROTOBUF_NDEBUG_INLINE was previously defined
+#endif
+// Avoid excessive inlining in non-optimized builds. Without other optimizations
+// the inlining is not going to provide benefits anyway and the huge resulting
+// functions, especially in the proto-generated serialization functions, produce
+// stack frames so large that many tests run into stack overflows (b/32192897).
+#if defined(NDEBUG) || (defined(_MSC_VER) && !defined(_DEBUG))
+# define PROTOBUF_NDEBUG_INLINE PROTOBUF_ALWAYS_INLINE
+#else
+# define PROTOBUF_NDEBUG_INLINE
+#endif
+
+// Note that PROTOBUF_NOINLINE is an attribute applied to functions, to prevent
+// them from being inlined by the compiler. This is different from
+// PROTOBUF_NO_INLINE, which is a user-supplied macro that disables forced
+// inlining by PROTOBUF_(ALWAYS|NDEBUG)_INLINE.
+#ifdef PROTOBUF_NOINLINE
+#error PROTOBUF_NOINLINE was previously defined
+#endif
+#if PROTOBUF_GNUC_MIN(3, 1)
+# define PROTOBUF_NOINLINE __attribute__((noinline))
+#elif defined(_MSC_VER)
+// Seems to have been around since at least Visual Studio 2005
+# define PROTOBUF_NOINLINE __declspec(noinline)
+#endif
+
+#ifdef PROTOBUF_MUSTTAIL
+#error PROTOBUF_MUSTTAIL was previously defined
+#endif
+#ifdef PROTOBUF_TAILCALL
+#error PROTOBUF_TAILCALL was previously defined
+#endif
+#if __has_cpp_attribute(clang::musttail) && !defined(__arm__) && \
+    !defined(_ARCH_PPC) && !defined(__wasm__) &&                 \
+    !(defined(_MSC_VER) && defined(_M_IX86)) &&                  \
+    !(defined(__NDK_MAJOR__) && __NDK_MAJOR <= 24)
+#  ifndef PROTO2_OPENSOURCE
+// Compilation fails on ARM32: b/195943306
+// Compilation fails on powerpc64le: b/187985113
+// Compilation fails on X86 Windows:
+// https://github.com/llvm/llvm-project/issues/53271
+#  endif
+#define PROTOBUF_MUSTTAIL [[clang::musttail]]
+#define PROTOBUF_TAILCALL true
+#else
+#define PROTOBUF_MUSTTAIL
+#define PROTOBUF_TAILCALL false
+#endif
+
+#ifdef PROTOBUF_EXCLUSIVE_LOCKS_REQUIRED
+#error PROTOBUF_EXCLUSIVE_LOCKS_REQUIRED was previously defined
+#endif
+#if __has_attribute(exclusive_locks_required)
+#define PROTOBUF_EXCLUSIVE_LOCKS_REQUIRED(...) \
+  __attribute__((exclusive_locks_required(__VA_ARGS__)))
+#else
+#define PROTOBUF_EXCLUSIVE_LOCKS_REQUIRED(...)
+#endif
+
+#ifdef PROTOBUF_NO_THREAD_SAFETY_ANALYSIS
+#error PROTOBUF_NO_THREAD_SAFETY_ANALYSIS was previously defined
+#endif
+#if __has_attribute(no_thread_safety_analysis)
+#define PROTOBUF_NO_THREAD_SAFETY_ANALYSIS \
+  __attribute__((no_thread_safety_analysis))
+#else
+#define PROTOBUF_NO_THREAD_SAFETY_ANALYSIS
+#endif
+
+#ifdef PROTOBUF_GUARDED_BY
+#error PROTOBUF_GUARDED_BY was previously defined
+#endif
+#if __has_attribute(guarded_by)
+#define PROTOBUF_GUARDED_BY(x) __attribute__((guarded_by(x)))
+#else
+#define PROTOBUF_GUARDED_BY(x)
+#endif
+
+#ifdef PROTOBUF_LOCKS_EXCLUDED
+#error PROTOBUF_LOCKS_EXCLUDED was previously defined
+#endif
+#if __has_attribute(locks_excluded)
+#define PROTOBUF_LOCKS_EXCLUDED(...) \
+  __attribute__((locks_excluded(__VA_ARGS__)))
+#else
+#define PROTOBUF_LOCKS_EXCLUDED(...)
+#endif
+
+#ifdef PROTOBUF_COLD
+#error PROTOBUF_COLD was previously defined
+#endif
+#if __has_attribute(cold) || PROTOBUF_GNUC_MIN(4, 3)
+# define PROTOBUF_COLD __attribute__((cold))
+#else
+# define PROTOBUF_COLD
+#endif
+
+#ifdef PROTOBUF_SECTION_VARIABLE
+#error PROTOBUF_SECTION_VARIABLE was previously defined
+#endif
+#if (__has_attribute(section) || defined(__GNUC__)) && defined(__ELF__)
+// Place a variable in the given ELF section.
+# define PROTOBUF_SECTION_VARIABLE(x) __attribute__((section(#x)))
+#else
+# define PROTOBUF_SECTION_VARIABLE(x)
+#endif
+
+#if defined(PROTOBUF_DEPRECATED)
+#error PROTOBUF_DEPRECATED was previously defined
+#endif
+#if defined(PROTOBUF_DEPRECATED_MSG)
+#error PROTOBUF_DEPRECATED_MSG was previously defined
+#endif
+#if __has_attribute(deprecated) || PROTOBUF_GNUC_MIN(3, 0)
+# define PROTOBUF_DEPRECATED __attribute__((deprecated))
+# define PROTOBUF_DEPRECATED_MSG(msg) __attribute__((deprecated(msg)))
+#elif defined(_MSC_VER)
+# define PROTOBUF_DEPRECATED __declspec(deprecated)
+# define PROTOBUF_DEPRECATED_MSG(msg) __declspec(deprecated(msg))
+#else
+# define PROTOBUF_DEPRECATED
+# define PROTOBUF_DEPRECATED_MSG(msg)
+#endif
+
+#if defined(PROTOBUF_DEPRECATED_ENUM)
+#error PROTOBUF_DEPRECATED_ENUM was previously defined
+#endif
+#if defined(__clang__) || PROTOBUF_GNUC_MIN(6, 0)
+// https://gcc.gnu.org/gcc-6/changes.html
+# define PROTOBUF_DEPRECATED_ENUM __attribute__((deprecated))
+#else
+# define PROTOBUF_DEPRECATED_ENUM
+#endif
+
+#ifdef PROTOBUF_FUNC_ALIGN
+#error PROTOBUF_FUNC_ALIGN was previously defined
+#endif
+#if __has_attribute(aligned) || PROTOBUF_GNUC_MIN(4, 3)
+#define PROTOBUF_FUNC_ALIGN(bytes) __attribute__((aligned(bytes)))
+#else
+#define PROTOBUF_FUNC_ALIGN(bytes)
+#endif
+
+#ifdef PROTOBUF_RETURNS_NONNULL
+#error PROTOBUF_RETURNS_NONNULL was previously defined
+#endif
+#if __has_attribute(returns_nonnull) || PROTOBUF_GNUC_MIN(4, 9)
+#define PROTOBUF_RETURNS_NONNULL __attribute__((returns_nonnull))
+#else
+#define PROTOBUF_RETURNS_NONNULL
+#endif
+
+#ifdef PROTOBUF_ATTRIBUTE_REINITIALIZES
+#error PROTOBUF_ATTRIBUTE_REINITIALIZES was previously defined
+#endif
+#if __has_cpp_attribute(clang::reinitializes)
+#define PROTOBUF_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]]
+#else
+#define PROTOBUF_ATTRIBUTE_REINITIALIZES
+#endif
+
+// The minimum library version which works with the current version of the
+// headers.
+#define GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION 3021000
+
+#ifdef PROTOBUF_RTTI
+#error PROTOBUF_RTTI was previously defined
+#endif
+#if defined(GOOGLE_PROTOBUF_NO_RTTI) && GOOGLE_PROTOBUF_NO_RTTI
+// A user-provided definition GOOGLE_PROTOBUF_NO_RTTI=1 disables RTTI.
+#define PROTOBUF_RTTI 0
+#elif defined(__cpp_rtti)
+// https://en.cppreference.com/w/cpp/feature_test
+#define PROTOBUF_RTTI 1
+#elif __has_feature(cxx_rtti)
+// https://clang.llvm.org/docs/LanguageExtensions.html#c-rtti
+#define PROTOBUF_RTTI 1
+#elif defined(__GXX_RTTI)
+// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
+#define PROTOBUF_RTTI 1
+#elif defined(_CPPRTTI)
+// https://docs.microsoft.com/en-us/cpp/build/reference/gr-enable-run-time-type-information
+#define PROTOBUF_RTTI 1
+#else
+#define PROTOBUF_RTTI 0
+#endif
+
+// Returns the offset of the given field within the given aggregate type.
+// This is equivalent to the ANSI C offsetof() macro.  However, according
+// to the C++ standard, offsetof() only works on POD types, and GCC
+// enforces this requirement with a warning.  In practice, this rule is
+// unnecessarily strict; there is probably no compiler or platform on
+// which the offsets of the direct fields of a class are non-constant.
+// Fields inherited from superclasses *can* have non-constant offsets,
+// but that's not what this macro will be used for.
+#ifdef PROTOBUF_FIELD_OFFSET
+#error PROTOBUF_FIELD_OFFSET was previously defined
+#endif
+#if defined(__clang__)
+// For Clang we use __builtin_offsetof() and suppress the warning,
+// to avoid Control Flow Integrity and UBSan vptr sanitizers from
+// crashing while trying to validate the invalid reinterpret_casts.
+#define PROTOBUF_FIELD_OFFSET(TYPE, FIELD)                   \
+  _Pragma("clang diagnostic push")                           \
+  _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \
+  __builtin_offsetof(TYPE, FIELD)                            \
+  _Pragma("clang diagnostic pop")
+#elif PROTOBUF_GNUC_MIN(4, 8)
+#define PROTOBUF_FIELD_OFFSET(TYPE, FIELD) __builtin_offsetof(TYPE, FIELD)
+#else  // defined(__clang__)
+// Note that we calculate relative to the pointer value 16 here since if we
+// just use zero, GCC complains about dereferencing a NULL pointer.  We
+// choose 16 rather than some other number just in case the compiler would
+// be confused by an unaligned pointer.
+#define PROTOBUF_FIELD_OFFSET(TYPE, FIELD)                                \
+  static_cast< ::uint32_t>(reinterpret_cast<const char*>(                   \
+                             &reinterpret_cast<const TYPE*>(16)->FIELD) - \
+                         reinterpret_cast<const char*>(16))
+#endif
+
+#ifdef PROTOBUF_EXPORT
+#error PROTOBUF_EXPORT was previously defined
+#endif
+
+#if defined(PROTOBUF_USE_DLLS) && defined(_MSC_VER)
+# if defined(LIBPROTOBUF_EXPORTS)
+#  define PROTOBUF_EXPORT __declspec(dllexport)
+#  define PROTOBUF_EXPORT_TEMPLATE_DECLARE
+#  define PROTOBUF_EXPORT_TEMPLATE_DEFINE __declspec(dllexport)
+# else
+#  define PROTOBUF_EXPORT __declspec(dllimport)
+#  define PROTOBUF_EXPORT_TEMPLATE_DECLARE
+#  define PROTOBUF_EXPORT_TEMPLATE_DEFINE __declspec(dllimport)
+# endif  // defined(LIBPROTOBUF_EXPORTS)
+#elif defined(PROTOBUF_USE_DLLS) && defined(LIBPROTOBUF_EXPORTS)
+# define PROTOBUF_EXPORT __attribute__((visibility("default")))
+# define PROTOBUF_EXPORT_TEMPLATE_DECLARE __attribute__((visibility("default")))
+# define PROTOBUF_EXPORT_TEMPLATE_DEFINE
+#else
+# define PROTOBUF_EXPORT
+# define PROTOBUF_EXPORT_TEMPLATE_DECLARE
+# define PROTOBUF_EXPORT_TEMPLATE_DEFINE
+#endif
+
+#ifdef PROTOC_EXPORT
+#error PROTOC_EXPORT was previously defined
+#endif
+
+#if defined(PROTOBUF_USE_DLLS) && defined(_MSC_VER)
+# if defined(LIBPROTOC_EXPORTS)
+#  define PROTOC_EXPORT __declspec(dllexport)
+# else
+#  define PROTOC_EXPORT __declspec(dllimport)
+# endif  // defined(LIBPROTOC_EXPORTS)
+#elif defined(PROTOBUF_USE_DLLS) && defined(LIBPROTOC_EXPORTS)
+# define PROTOC_EXPORT __attribute__((visibility("default")))
+#else
+# define PROTOC_EXPORT
+#endif
+
+#if defined(PROTOBUF_PREDICT_TRUE) || defined(PROTOBUF_PREDICT_FALSE)
+#error PROTOBUF_PREDICT_(TRUE|FALSE) was previously defined
+#endif
+#if PROTOBUF_GNUC_MIN(3, 0)
+# define PROTOBUF_PREDICT_TRUE(x) (__builtin_expect(false || (x), true))
+# define PROTOBUF_PREDICT_FALSE(x) (__builtin_expect(false || (x), false))
+#else
+# define PROTOBUF_PREDICT_TRUE(x) (x)
+# define PROTOBUF_PREDICT_FALSE(x) (x)
+#endif
+
+#ifdef PROTOBUF_NODISCARD
+#error PROTOBUF_NODISCARD was previously defined
+#endif
+#if __has_cpp_attribute(nodiscard) && PROTOBUF_CPLUSPLUS_MIN(201703L)
+#define PROTOBUF_NODISCARD [[nodiscard]]
+#elif __has_attribute(warn_unused_result) || PROTOBUF_GNUC_MIN(4, 8)
+#define PROTOBUF_NODISCARD __attribute__((warn_unused_result))
+#else
+#define PROTOBUF_NODISCARD
+#endif
+
+// Enable all stable experiments if this flag is set.  This allows us to group
+// all of these experiments under a single build flag, which can be enabled in
+// the protobuf.stable-experiments TAP project.
+#ifdef PROTOBUF_ENABLE_STABLE_EXPERIMENTS
+#define PROTOBUF_FORCE_MESSAGE_OWNED_ARENA
+#endif  // !PROTOBUF_ENABLE_STABLE_EXPERIMENTS
+
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+#error PROTOBUF_FORCE_COPY_IN_RELEASE was previously defined
+#endif
+
+#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+#error PROTOBUF_FORCE_COPY_IN_SWAP was previously defined
+#endif
+
+#ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+#error PROTOBUF_FORCE_COPY_IN_MOVE was previously defined
+#endif
+
+#ifdef PROTOBUF_FORCE_RESET_IN_CLEAR
+#error PROTOBUF_FORCE_RESET_IN_CLEAR was previously defined
+#endif
+
+// Force copy the default string to a string field so that non-optimized builds
+// have harder-to-rely-on address stability.
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+#error PROTOBUF_FORCE_COPY_DEFAULT_STRING was previously defined
+#endif
+
+#ifdef PROTOBUF_FALLTHROUGH_INTENDED
+#error PROTOBUF_FALLTHROUGH_INTENDED was previously defined
+#endif
+#if __has_cpp_attribute(fallthrough)
+#define PROTOBUF_FALLTHROUGH_INTENDED [[fallthrough]]
+#elif __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
+#define PROTOBUF_FALLTHROUGH_INTENDED [[clang::fallthrough]]
+#elif PROTOBUF_GNUC_MIN(7, 0)
+#define PROTOBUF_FALLTHROUGH_INTENDED [[gnu::fallthrough]]
+#else
+#define PROTOBUF_FALLTHROUGH_INTENDED
+#endif
+
+// PROTOBUF_ASSUME(pred) tells the compiler that it can assume pred is true. To
+// be safe, we also validate the assumption with a GOOGLE_DCHECK in unoptimized
+// builds. The macro does not do anything useful if the compiler does not
+// support __builtin_assume.
+#ifdef PROTOBUF_ASSUME
+#error PROTOBUF_ASSUME was previously defined
+#endif
+#if __has_builtin(__builtin_assume)
+#define PROTOBUF_ASSUME(pred) \
+  GOOGLE_DCHECK(pred);               \
+  __builtin_assume(pred)
+#else
+#define PROTOBUF_ASSUME(pred) GOOGLE_DCHECK(pred)
+#endif
+
+// Specify memory alignment for structs, classes, etc.
+// Use like:
+//   class PROTOBUF_ALIGNAS(16) MyClass { ... }
+//   PROTOBUF_ALIGNAS(16) int array[4];
+//
+// In most places you can use the C++11 keyword "alignas", which is preferred.
+//
+// But compilers have trouble mixing __attribute__((...)) syntax with
+// alignas(...) syntax.
+//
+// Doesn't work in clang or gcc:
+//   struct alignas(16) __attribute__((packed)) S { char c; };
+// Works in clang but not gcc:
+//   struct __attribute__((packed)) alignas(16) S2 { char c; };
+// Works in clang and gcc:
+//   struct alignas(16) S3 { char c; } __attribute__((packed));
+//
+// There are also some attributes that must be specified *before* a class
+// definition: visibility (used for exporting functions/classes) is one of
+// these attributes. This means that it is not possible to use alignas() with a
+// class that is marked as exported.
+#ifdef PROTOBUF_ALIGNAS
+#error PROTOBUF_ALIGNAS was previously defined
+#endif
+#if defined(_MSC_VER)
+#define PROTOBUF_ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
+#elif PROTOBUF_GNUC_MIN(3, 0)
+#define PROTOBUF_ALIGNAS(byte_alignment) \
+  __attribute__((aligned(byte_alignment)))
+#else
+#define PROTOBUF_ALIGNAS(byte_alignment) alignas(byte_alignment)
+#endif
+
+#ifdef PROTOBUF_FINAL
+#error PROTOBUF_FINAL was previously defined
+#endif
+#define PROTOBUF_FINAL final
+
+#ifdef PROTOBUF_THREAD_LOCAL
+#error PROTOBUF_THREAD_LOCAL was previously defined
+#endif
+#if defined(_MSC_VER)
+#define PROTOBUF_THREAD_LOCAL __declspec(thread)
+#else
+#define PROTOBUF_THREAD_LOCAL __thread
+#endif
+
+// TODO(b/228173843): cleanup PROTOBUF_LITTLE_ENDIAN in various 3p forks.
+#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+     __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#define PROTOBUF_LITTLE_ENDIAN 1
+#ifdef PROTOBUF_BIG_ENDIAN
+#error Conflicting PROTOBUF_BIG_ENDIAN was previously defined
+#endif
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
+    __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define PROTOBUF_BIG_ENDIAN 1
+#elif defined(_WIN32) || defined(__x86_64__) || defined(__aarch64__)
+#define PROTOBUF_LITTLE_ENDIAN 1
+#else
+#error "endian detection failed for current compiler"
+#endif
+
+// For enabling message owned arena, one major blocker is semantic change from
+// moving to copying when there is ownership transfer (e.g., move ctor, swap,
+// set allocated, release). This change not only causes performance regression
+// but also breaks users code (e.g., dangling reference). For top-level
+// messages, since it owns the arena, we can mitigate the issue by transferring
+// ownership of arena. However, we cannot do that for nested messages. In order
+// to tell how many usages of nested messages affected by message owned arena,
+// we need to simulate the arena ownership.
+// This experiment is purely for the purpose of gathering data. All code guarded
+// by this flag is supposed to be removed after this experiment.
+#define PROTOBUF_MESSAGE_OWNED_ARENA_EXPERIMENT
+#ifdef PROTOBUF_CONSTINIT
+#error PROTOBUF_CONSTINIT was previously defined
+#endif
+#if defined(__cpp_constinit) && !defined(_MSC_VER)
+#define PROTOBUF_CONSTINIT constinit
+#define PROTOBUF_CONSTEXPR constexpr
+// Some older Clang versions incorrectly raise an error about
+// constant-initializing weak default instance pointers. Versions 12.0 and
+// higher seem to work, except that XCode 12.5.1 shows the error even though it
+// uses Clang 12.0.5.
+// Clang-cl on Windows raises error also.
+#elif !defined(_MSC_VER) && __has_cpp_attribute(clang::require_constant_initialization) && \
+    ((defined(__APPLE__) && __clang_major__ >= 13) ||                \
+     (!defined(__APPLE__) && __clang_major__ >= 12))
+#define PROTOBUF_CONSTINIT [[clang::require_constant_initialization]]
+#define PROTOBUF_CONSTEXPR constexpr
+#elif PROTOBUF_GNUC_MIN(12, 2)
+#define PROTOBUF_CONSTINIT __constinit
+#define PROTOBUF_CONSTEXPR constexpr
+// MSVC 17 currently seems to raise an error about constant-initialized pointers.
+#elif defined(_MSC_VER) && _MSC_VER >= 1930
+#define PROTOBUF_CONSTINIT
+#define PROTOBUF_CONSTEXPR constexpr
+#else
+#define PROTOBUF_CONSTINIT
+#define PROTOBUF_CONSTEXPR
+#endif
+
+// Some globals with an empty non-trivial destructor are annotated with
+// no_destroy for performance reasons. It reduces the cost of these globals in
+// non-opt mode and under sanitizers.
+#ifdef PROTOBUF_ATTRIBUTE_NO_DESTROY
+#error PROTOBUF_ATTRIBUTE_NO_DESTROY was previously defined
+#endif
+#if __has_cpp_attribute(clang::no_destroy)
+#define PROTOBUF_ATTRIBUTE_NO_DESTROY [[clang::no_destroy]]
+#else
+#define PROTOBUF_ATTRIBUTE_NO_DESTROY
+#endif
+
+// Force clang to always emit complete debug info for a type.
+// Clang uses constructor homing to determine when to emit debug info for a
+// type. If the constructor of a type is never used, which can happen in some
+// cases where member variables are constructed in place for optimization
+// purposes (see b/208803175 for an example), the type will have incomplete
+// debug info unless this attribute is used.
+#ifdef PROTOBUF_ATTRIBUTE_STANDALONE_DEBUG
+#error PROTOBUF_ATTRIBUTE_STANDALONE_DEBUG was previously defined
+#endif
+#if __has_cpp_attribute(clang::standalone_debug)
+#define PROTOBUF_ATTRIBUTE_STANDALONE_DEBUG [[clang::standalone_debug]]
+#else
+#define PROTOBUF_ATTRIBUTE_STANDALONE_DEBUG
+#endif
+
+// Protobuf extensions and reflection require registration of the protos linked
+// in the binary. Not until everything is registered does the runtime have a
+// complete view on all protos. When code is using reflection or extensions
+// in between registration calls this can lead to surprising behavior. By
+// having the registration run first we mitigate this scenario.
+// Highest priority is 101. We use 102 for registration, to allow code that
+// really wants to higher priority to still beat us. Some initialization happens
+// at higher priority, though, since it is needed before registration.
+#ifdef PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
+#error PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 was previously defined
+#endif
+#ifdef PROTOBUF_ATTRIBUTE_INIT_PRIORITY2
+#error PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 was previously defined
+#endif
+#if PROTOBUF_GNUC_MIN(3, 0) && (!defined(__APPLE__) || defined(__clang__)) && \
+    !((defined(sun) || defined(__sun)) &&                                     \
+      (defined(__SVR4) || defined(__svr4__)))
+#define PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 __attribute__((init_priority((101))))
+#define PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 __attribute__((init_priority((102))))
+#else
+#define PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
+#define PROTOBUF_ATTRIBUTE_INIT_PRIORITY2
+#endif
+
+#ifdef PROTOBUF_PRAGMA_INIT_SEG
+#error PROTOBUF_PRAGMA_INIT_SEG was previously defined
+#endif
+#ifdef _MSC_VER
+#define PROTOBUF_PRAGMA_INIT_SEG __pragma(init_seg(lib))
+#else
+#define PROTOBUF_PRAGMA_INIT_SEG
+#endif
+
+#ifdef PROTOBUF_ATTRIBUTE_WEAK
+#error PROTOBUF_ATTRIBUTE_WEAK was previously defined
+#endif
+#if __has_attribute(weak) && \
+    !defined(__APPLE__) && \
+    (!defined(_WIN32) || __clang_major__ < 9) && \
+    !defined(__MINGW32__)
+#define PROTOBUF_ATTRIBUTE_WEAK __attribute__((weak))
+#define PROTOBUF_HAVE_ATTRIBUTE_WEAK 1
+#else
+#define PROTOBUF_ATTRIBUTE_WEAK
+#define PROTOBUF_HAVE_ATTRIBUTE_WEAK 0
+#endif
+
+// Macros to detect sanitizers.
+#ifdef PROTOBUF_ASAN
+#error PROTOBUF_ASAN was previously defined
+#endif
+#ifdef PROTOBUF_MSAN
+#error PROTOBUF_MSAN was previously defined
+#endif
+#ifdef PROTOBUF_TSAN
+#error PROTOBUF_TSAN was previously defined
+#endif
+#if defined(__clang__)
+#  if __has_feature(address_sanitizer)
+#    define PROTOBUF_ASAN 1
+#  endif
+#  if __has_feature(thread_sanitizer)
+#    define PROTOBUF_TSAN 1
+#  endif
+#  if __has_feature(memory_sanitizer)
+#    define PROTOBUF_MSAN 1
+#  endif
+#elif PROTOBUF_GNUC_MIN(3, 0)
+// Double-guard is needed for -Wundef:
+#  ifdef __SANITIZE_ADDRESS__
+#  if    __SANITIZE_ADDRESS__
+#    define PROTOBUF_ASAN 1
+#  endif
+#  endif
+#  ifdef __SANITIZE_THREAD__
+#  if    __SANITIZE_THREAD__
+#    define PROTOBUF_TSAN 1
+#  endif
+#  endif
+#endif
+
+// Tail call table-driven parsing can be enabled by defining
+// PROTOBUF_EXPERIMENTAL_USE_TAIL_CALL_TABLE_PARSER at compilation time. Note
+// that this macro is for small-scale testing only, and is not supported.
+#ifdef PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED
+#error PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED was previously declared
+#endif
+#if defined(PROTOBUF_EXPERIMENTAL_USE_TAIL_CALL_TABLE_PARSER)
+#define PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED 1
+#endif
+
+#define PROTOBUF_TC_PARAM_DECL                                 \
+  ::google::protobuf::MessageLite *msg, const char *ptr,                 \
+      ::google::protobuf::internal::ParseContext *ctx,                   \
+      const ::google::protobuf::internal::TcParseTableBase *table,       \
+      uint64_t hasbits, ::google::protobuf::internal::TcFieldData data
+
+#ifdef PROTOBUF_UNUSED
+#error PROTOBUF_UNUSED was previously defined
+#endif
+#if __has_cpp_attribute(maybe_unused) || \
+    (PROTOBUF_MSC_VER_MIN(1911) && PROTOBUF_CPLUSPLUS_MIN(201703L))
+#define PROTOBUF_UNUSED [[maybe_unused]]
+#elif __has_attribute(unused) || PROTOBUF_GNUC_MIN(3, 0)
+#define PROTOBUF_UNUSED __attribute__((__unused__))
+#else
+#define PROTOBUF_UNUSED
+#endif
+
+// ThreadSafeArenaz is turned off completely in opensource builds.
+
+// Windows declares several inconvenient macro names.  We #undef them and then
+// restore them in port_undef.inc.
+#ifdef _MSC_VER
+#pragma push_macro("CREATE_NEW")
+#undef CREATE_NEW
+#pragma push_macro("DELETE")
+#undef DELETE
+#pragma push_macro("DOUBLE_CLICK")
+#undef DOUBLE_CLICK
+#pragma push_macro("ERROR")
+#undef ERROR
+#pragma push_macro("ERROR_BUSY")
+#undef ERROR_BUSY
+#pragma push_macro("ERROR_INSTALL_FAILED")
+#undef ERROR_INSTALL_FAILED
+#pragma push_macro("ERROR_NOT_FOUND")
+#undef ERROR_NOT_FOUND
+#pragma push_macro("GetClassName")
+#undef GetClassName
+#pragma push_macro("GetMessage")
+#undef GetMessage
+#pragma push_macro("GetObject")
+#undef GetObject
+#pragma push_macro("IGNORE")
+#undef IGNORE
+#pragma push_macro("IN")
+#undef IN
+#pragma push_macro("INPUT_KEYBOARD")
+#undef INPUT_KEYBOARD
+#pragma push_macro("NO_ERROR")
+#undef NO_ERROR
+#pragma push_macro("OUT")
+#undef OUT
+#pragma push_macro("OPTIONAL")
+#undef OPTIONAL
+#pragma push_macro("min")
+#undef min
+#pragma push_macro("max")
+#undef max
+#pragma push_macro("NEAR")
+#undef NEAR
+#pragma push_macro("NO_DATA")
+#undef NO_DATA
+#pragma push_macro("REASON_UNKNOWN")
+#undef REASON_UNKNOWN
+#pragma push_macro("SERVICE_DISABLED")
+#undef SERVICE_DISABLED
+#pragma push_macro("SEVERITY_ERROR")
+#undef SEVERITY_ERROR
+#pragma push_macro("STATUS_PENDING")
+#undef STATUS_PENDING
+#pragma push_macro("STRICT")
+#undef STRICT
+#pragma push_macro("timezone")
+#undef timezone
+#endif  // _MSC_VER
+
+#ifdef __APPLE__
+// Inconvenient macro names from usr/include/math.h in some macOS SDKs.
+#pragma push_macro("DOMAIN")
+#undef DOMAIN
+// Inconvenient macro names from /usr/include/mach/boolean.h in some macOS SDKs.
+#pragma push_macro("TRUE")
+#undef TRUE
+#pragma push_macro("FALSE")
+#undef FALSE
+// Inconvenient macro names from usr/include/sys/syslimits.h in some macOS SDKs.
+#pragma push_macro("UID_MAX")
+#undef UID_MAX
+#endif  // __APPLE__
+
+#if defined(__clang__) || PROTOBUF_GNUC_MIN(3, 0) || defined(_MSC_VER)
+// Don't let Objective-C Macros interfere with proto identifiers with the same
+// name.
+#pragma push_macro("DEBUG")
+#undef DEBUG
+#endif // defined(__clang__) || PROTOBUF_GNUC_MIN(3, 0) || defined(_MSC_VER)
+
+#if PROTOBUF_GNUC_MIN(3, 0)
+// GCC does not allow disabling diagnostics within an expression:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60875, so we disable this one
+// globally even though it's only used for PROTOBUF_FIELD_OFFSET.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winvalid-offsetof"
+#endif
+
+// Silence some MSVC warnings in all our code.
+#ifdef _MSC_VER
+#pragma warning(push)
+// For non-trivial unions
+#pragma warning(disable : 4582)
+#pragma warning(disable : 4583)
+// For init_seg(lib)
+#pragma warning(disable : 4073)
+// To silence the fact that we will pop this push from another file
+#pragma warning(disable : 5031)
+// Conditional expression is constant
+#pragma warning(disable: 4127)
+// decimal digit terminates octal escape sequence
+#pragma warning(disable: 4125)
+#endif
+
+// We don't want code outside port_def doing complex testing, so
+// remove our portable condition test macros to nudge folks away from
+// using it themselves.
+#ifdef PROTOBUF_has_cpp_attribute_DEFINED_
+#  undef __has_cpp_attribute
+#  undef PROTOBUF_has_cpp_attribute_DEFINED_
+#endif
+#ifdef PROTOBUF_has_feature_DEFINED_
+#  undef __has_feature
+#  undef PROTOBUF_has_feature_DEFINED_
+#endif
+#ifdef PROTOBUF_has_warning_DEFINED_
+#  undef __has_warning
+#  undef PROTOBUF_has_warning_DEFINED_
+#endif
+#ifdef PROTOBUF_has_attribute_DEFINED_
+#  undef __has_attribute
+#  undef PROTOBUF_has_attribute_DEFINED_
+#endif
+#ifdef PROTOBUF_has_builtin_DEFINED_
+#  undef __has_builtin
+#  undef PROTOBUF_has_builtin_DEFINED_
+#endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/port_undef.inc b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/port_undef.inc
new file mode 100644
index 0000000..e880fa5
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/port_undef.inc
@@ -0,0 +1,160 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// #undefs all macros defined in port_def.inc.  See comments in port_def.inc
+// for more info.
+
+#ifndef PROTOBUF_NAMESPACE
+#error "port_undef.inc must be included after port_def.inc"
+#endif
+
+#undef PROTOBUF_BUILTIN_BSWAP16
+#undef PROTOBUF_BUILTIN_BSWAP32
+#undef PROTOBUF_BUILTIN_BSWAP64
+#undef PROTOBUF_GNUC_MIN
+#undef PROTOBUF_MSC_VER_MIN
+#undef PROTOBUF_CPLUSPLUS_MIN
+#undef PROTOBUF_NAMESPACE
+#undef PROTOBUF_NAMESPACE_ID
+#undef PROTOBUF_ALWAYS_INLINE
+#undef PROTOBUF_NDEBUG_INLINE
+#undef PROTOBUF_MUSTTAIL
+#undef PROTOBUF_TAILCALL
+#undef PROTOBUF_COLD
+#undef PROTOBUF_NOINLINE
+#undef PROTOBUF_SECTION_VARIABLE
+#undef PROTOBUF_DEPRECATED
+#undef PROTOBUF_DEPRECATED_ENUM
+#undef PROTOBUF_DEPRECATED_MSG
+#undef PROTOBUF_FUNC_ALIGN
+#undef PROTOBUF_RETURNS_NONNULL
+#undef PROTOBUF_ATTRIBUTE_REINITIALIZES
+#undef PROTOBUF_RTTI
+#undef PROTOBUF_VERSION
+#undef PROTOBUF_VERSION_SUFFIX
+#undef PROTOBUF_FIELD_OFFSET
+#undef PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC
+#undef PROTOBUF_MIN_PROTOC_VERSION
+#undef PROTOBUF_PREDICT_TRUE
+#undef PROTOBUF_PREDICT_FALSE
+#undef PROTOBUF_FALLTHROUGH_INTENDED
+#undef PROTOBUF_EXPORT
+#undef PROTOC_EXPORT
+#undef PROTOBUF_NODISCARD
+#undef PROTOBUF_FORCE_COPY_IN_RELEASE
+#undef PROTOBUF_FORCE_COPY_IN_SWAP
+#undef PROTOBUF_FORCE_COPY_IN_MOVE
+#undef PROTOBUF_FORCE_RESET_IN_CLEAR
+#undef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+#undef PROTOBUF_NAMESPACE_OPEN
+#undef PROTOBUF_NAMESPACE_CLOSE
+#undef PROTOBUF_UNUSED
+#undef PROTOBUF_ASSUME
+#undef PROTOBUF_EXPORT_TEMPLATE_DECLARE
+#undef PROTOBUF_EXPORT_TEMPLATE_DEFINE
+#undef PROTOBUF_ALIGNAS
+#undef PROTOBUF_FINAL
+#undef PROTOBUF_FUTURE_FINAL
+#undef PROTOBUF_THREAD_LOCAL
+#undef PROTOBUF_LITTLE_ENDIAN
+#undef PROTOBUF_BIG_ENDIAN
+#undef PROTOBUF_MESSAGE_OWNED_ARENA_EXPERIMENT
+#undef PROTOBUF_CONSTINIT
+#undef PROTOBUF_CONSTEXPR
+#undef PROTOBUF_ATTRIBUTE_WEAK
+#undef PROTOBUF_HAVE_ATTRIBUTE_WEAK
+#undef PROTOBUF_ATTRIBUTE_NO_DESTROY
+#undef PROTOBUF_ATTRIBUTE_STANDALONE_DEBUG
+#undef PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
+#undef PROTOBUF_ATTRIBUTE_INIT_PRIORITY2
+#undef PROTOBUF_PRAGMA_INIT_SEG
+#undef PROTOBUF_ASAN
+#undef PROTOBUF_MSAN
+#undef PROTOBUF_TSAN
+#undef PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED
+#undef PROTOBUF_TC_PARAM_DECL
+#undef PROTOBUF_EXCLUSIVE_LOCKS_REQUIRED
+#undef PROTOBUF_LOCKS_EXCLUDED
+#undef PROTOBUF_NO_THREAD_SAFETY_ANALYSIS
+#undef PROTOBUF_GUARDED_BY
+
+#ifdef PROTOBUF_FUTURE_BREAKING_CHANGES
+#undef PROTOBUF_FUTURE_BREAKING_CHANGES
+#endif
+
+// Restore macro that may have been #undef'd in port_def.inc.
+#ifdef _MSC_VER
+#pragma pop_macro("CREATE_NEW")
+#pragma pop_macro("DELETE")
+#pragma pop_macro("DOUBLE_CLICK")
+#pragma pop_macro("ERROR")
+#pragma pop_macro("ERROR_BUSY")
+#pragma pop_macro("ERROR_INSTALL_FAILED")
+#pragma pop_macro("ERROR_NOT_FOUND")
+#pragma pop_macro("GetClassName")
+#pragma pop_macro("GetMessage")
+#pragma pop_macro("GetObject")
+#pragma pop_macro("IGNORE")
+#pragma pop_macro("IN")
+#pragma pop_macro("INPUT_KEYBOARD")
+#pragma pop_macro("OUT")
+#pragma pop_macro("OPTIONAL")
+#pragma pop_macro("min")
+#pragma pop_macro("max")
+#pragma pop_macro("NEAR")
+#pragma pop_macro("NO_DATA")
+#pragma pop_macro("NO_ERROR")
+#pragma pop_macro("REASON_UNKNOWN")
+#pragma pop_macro("SERVICE_DISABLED")
+#pragma pop_macro("SEVERITY_ERROR")
+#pragma pop_macro("STRICT")
+#pragma pop_macro("STATUS_PENDING")
+#pragma pop_macro("timezone")
+#endif
+
+#ifdef __APPLE__
+#pragma pop_macro("DOMAIN")
+#pragma pop_macro("TRUE")
+#pragma pop_macro("FALSE")
+#pragma pop_macro("UID_MAX")
+#endif  // __APPLE__
+
+#if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER)
+#pragma pop_macro("DEBUG")
+#endif // defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER)
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
+// Pop the warning(push) from port_def.inc
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/reflection.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/reflection.h
new file mode 100644
index 0000000..7b75a43
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/reflection.h
@@ -0,0 +1,570 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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 header defines the RepeatedFieldRef class template used to access
+// repeated fields with protobuf reflection API.
+#ifndef GOOGLE_PROTOBUF_REFLECTION_H__
+#define GOOGLE_PROTOBUF_REFLECTION_H__
+
+
+#include <memory>
+
+#include <google/protobuf/message.h>
+#include <google/protobuf/generated_enum_util.h>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+template <typename T, typename Enable = void>
+struct RefTypeTraits;
+}  // namespace internal
+
+template <typename T>
+RepeatedFieldRef<T> Reflection::GetRepeatedFieldRef(
+    const Message& message, const FieldDescriptor* field) const {
+  return RepeatedFieldRef<T>(message, field);
+}
+
+template <typename T>
+MutableRepeatedFieldRef<T> Reflection::GetMutableRepeatedFieldRef(
+    Message* message, const FieldDescriptor* field) const {
+  return MutableRepeatedFieldRef<T>(message, field);
+}
+
+// RepeatedFieldRef definition for non-message types.
+template <typename T>
+class RepeatedFieldRef<
+    T, typename std::enable_if<!std::is_base_of<Message, T>::value>::type> {
+  typedef typename internal::RefTypeTraits<T>::iterator IteratorType;
+  typedef typename internal::RefTypeTraits<T>::AccessorType AccessorType;
+
+ public:
+  bool empty() const { return accessor_->IsEmpty(data_); }
+  int size() const { return accessor_->Size(data_); }
+  T Get(int index) const { return accessor_->template Get<T>(data_, index); }
+
+  typedef IteratorType iterator;
+  typedef IteratorType const_iterator;
+  typedef T value_type;
+  typedef T& reference;
+  typedef const T& const_reference;
+  typedef int size_type;
+  typedef ptrdiff_t difference_type;
+
+  iterator begin() const { return iterator(data_, accessor_, true); }
+  iterator end() const { return iterator(data_, accessor_, false); }
+
+ private:
+  friend class Reflection;
+  RepeatedFieldRef(const Message& message, const FieldDescriptor* field) {
+    const Reflection* reflection = message.GetReflection();
+    data_ = reflection->RepeatedFieldData(const_cast<Message*>(&message), field,
+                                          internal::RefTypeTraits<T>::cpp_type,
+                                          nullptr);
+    accessor_ = reflection->RepeatedFieldAccessor(field);
+  }
+
+  const void* data_;
+  const AccessorType* accessor_;
+};
+
+// MutableRepeatedFieldRef definition for non-message types.
+template <typename T>
+class MutableRepeatedFieldRef<
+    T, typename std::enable_if<!std::is_base_of<Message, T>::value>::type> {
+  typedef typename internal::RefTypeTraits<T>::AccessorType AccessorType;
+
+ public:
+  bool empty() const { return accessor_->IsEmpty(data_); }
+  int size() const { return accessor_->Size(data_); }
+  T Get(int index) const { return accessor_->template Get<T>(data_, index); }
+
+  void Set(int index, const T& value) const {
+    accessor_->template Set<T>(data_, index, value);
+  }
+  void Add(const T& value) const { accessor_->template Add<T>(data_, value); }
+  void RemoveLast() const { accessor_->RemoveLast(data_); }
+  void SwapElements(int index1, int index2) const {
+    accessor_->SwapElements(data_, index1, index2);
+  }
+  void Clear() const { accessor_->Clear(data_); }
+
+  void Swap(const MutableRepeatedFieldRef& other) const {
+    accessor_->Swap(data_, other.accessor_, other.data_);
+  }
+
+  template <typename Container>
+  void MergeFrom(const Container& container) const {
+    typedef typename Container::const_iterator Iterator;
+    for (Iterator it = container.begin(); it != container.end(); ++it) {
+      Add(*it);
+    }
+  }
+  template <typename Container>
+  void CopyFrom(const Container& container) const {
+    Clear();
+    MergeFrom(container);
+  }
+
+ private:
+  friend class Reflection;
+  MutableRepeatedFieldRef(Message* message, const FieldDescriptor* field) {
+    const Reflection* reflection = message->GetReflection();
+    data_ = reflection->RepeatedFieldData(
+        message, field, internal::RefTypeTraits<T>::cpp_type, nullptr);
+    accessor_ = reflection->RepeatedFieldAccessor(field);
+  }
+
+  void* data_;
+  const AccessorType* accessor_;
+};
+
+// RepeatedFieldRef definition for message types.
+template <typename T>
+class RepeatedFieldRef<
+    T, typename std::enable_if<std::is_base_of<Message, T>::value>::type> {
+  typedef typename internal::RefTypeTraits<T>::iterator IteratorType;
+  typedef typename internal::RefTypeTraits<T>::AccessorType AccessorType;
+
+ public:
+  bool empty() const { return accessor_->IsEmpty(data_); }
+  int size() const { return accessor_->Size(data_); }
+  // This method returns a reference to the underlying message object if it
+  // exists. If a message object doesn't exist (e.g., data stored in serialized
+  // form), scratch_space will be filled with the data and a reference to it
+  // will be returned.
+  //
+  // Example:
+  //   RepeatedFieldRef<Message> h = ...
+  //   unique_ptr<Message> scratch_space(h.NewMessage());
+  //   const Message& item = h.Get(index, scratch_space.get());
+  const T& Get(int index, T* scratch_space) const {
+    return *static_cast<const T*>(accessor_->Get(data_, index, scratch_space));
+  }
+  // Create a new message of the same type as the messages stored in this
+  // repeated field. Caller takes ownership of the returned object.
+  T* NewMessage() const { return static_cast<T*>(default_instance_->New()); }
+
+  typedef IteratorType iterator;
+  typedef IteratorType const_iterator;
+  typedef T value_type;
+  typedef T& reference;
+  typedef const T& const_reference;
+  typedef int size_type;
+  typedef ptrdiff_t difference_type;
+
+  iterator begin() const {
+    return iterator(data_, accessor_, true, NewMessage());
+  }
+  iterator end() const {
+    // The end iterator must not be dereferenced, no need for scratch space.
+    return iterator(data_, accessor_, false, nullptr);
+  }
+
+ private:
+  friend class Reflection;
+  RepeatedFieldRef(const Message& message, const FieldDescriptor* field) {
+    const Reflection* reflection = message.GetReflection();
+    data_ = reflection->RepeatedFieldData(
+        const_cast<Message*>(&message), field,
+        internal::RefTypeTraits<T>::cpp_type,
+        internal::RefTypeTraits<T>::GetMessageFieldDescriptor());
+    accessor_ = reflection->RepeatedFieldAccessor(field);
+    default_instance_ =
+        reflection->GetMessageFactory()->GetPrototype(field->message_type());
+  }
+
+  const void* data_;
+  const AccessorType* accessor_;
+  const Message* default_instance_;
+};
+
+// MutableRepeatedFieldRef definition for message types.
+template <typename T>
+class MutableRepeatedFieldRef<
+    T, typename std::enable_if<std::is_base_of<Message, T>::value>::type> {
+  typedef typename internal::RefTypeTraits<T>::AccessorType AccessorType;
+
+ public:
+  bool empty() const { return accessor_->IsEmpty(data_); }
+  int size() const { return accessor_->Size(data_); }
+  // See comments for RepeatedFieldRef<Message>::Get()
+  const T& Get(int index, T* scratch_space) const {
+    return *static_cast<const T*>(accessor_->Get(data_, index, scratch_space));
+  }
+  // Create a new message of the same type as the messages stored in this
+  // repeated field. Caller takes ownership of the returned object.
+  T* NewMessage() const { return static_cast<T*>(default_instance_->New()); }
+
+  void Set(int index, const T& value) const {
+    accessor_->Set(data_, index, &value);
+  }
+  void Add(const T& value) const { accessor_->Add(data_, &value); }
+  void RemoveLast() const { accessor_->RemoveLast(data_); }
+  void SwapElements(int index1, int index2) const {
+    accessor_->SwapElements(data_, index1, index2);
+  }
+  void Clear() const { accessor_->Clear(data_); }
+
+  void Swap(const MutableRepeatedFieldRef& other) const {
+    accessor_->Swap(data_, other.accessor_, other.data_);
+  }
+
+  template <typename Container>
+  void MergeFrom(const Container& container) const {
+    typedef typename Container::const_iterator Iterator;
+    for (Iterator it = container.begin(); it != container.end(); ++it) {
+      Add(*it);
+    }
+  }
+  template <typename Container>
+  void CopyFrom(const Container& container) const {
+    Clear();
+    MergeFrom(container);
+  }
+
+ private:
+  friend class Reflection;
+  MutableRepeatedFieldRef(Message* message, const FieldDescriptor* field) {
+    const Reflection* reflection = message->GetReflection();
+    data_ = reflection->RepeatedFieldData(
+        message, field, internal::RefTypeTraits<T>::cpp_type,
+        internal::RefTypeTraits<T>::GetMessageFieldDescriptor());
+    accessor_ = reflection->RepeatedFieldAccessor(field);
+    default_instance_ =
+        reflection->GetMessageFactory()->GetPrototype(field->message_type());
+  }
+
+  void* data_;
+  const AccessorType* accessor_;
+  const Message* default_instance_;
+};
+
+namespace internal {
+// Interfaces used to implement reflection RepeatedFieldRef API.
+// Reflection::GetRepeatedAccessor() should return a pointer to an singleton
+// object that implements the below interface.
+//
+// This interface passes/returns values using void pointers. The actual type
+// of the value depends on the field's cpp_type. Following is a mapping from
+// cpp_type to the type that should be used in this interface:
+//
+//   field->cpp_type()      T                Actual type of void*
+//   CPPTYPE_INT32        int32_t                 int32_t
+//   CPPTYPE_UINT32       uint32_t                uint32_t
+//   CPPTYPE_INT64        int64_t                 int64_t
+//   CPPTYPE_UINT64       uint64_t                uint64_t
+//   CPPTYPE_DOUBLE       double                  double
+//   CPPTYPE_FLOAT        float                   float
+//   CPPTYPE_BOOL         bool                    bool
+//   CPPTYPE_ENUM         generated enum type     int32_t
+//   CPPTYPE_STRING       string                  std::string
+//   CPPTYPE_MESSAGE      generated message type  google::protobuf::Message
+//                        or google::protobuf::Message
+//
+// Note that for enums we use int32_t in the interface.
+//
+// You can map from T to the actual type using RefTypeTraits:
+//   typedef RefTypeTraits<T>::AccessorValueType ActualType;
+class PROTOBUF_EXPORT RepeatedFieldAccessor {
+ public:
+  // Typedefs for clarity.
+  typedef void Field;
+  typedef void Value;
+  typedef void Iterator;
+
+  virtual bool IsEmpty(const Field* data) const = 0;
+  virtual int Size(const Field* data) const = 0;
+  // Depends on the underlying representation of the repeated field, this
+  // method can return a pointer to the underlying object if such an object
+  // exists, or fill the data into scratch_space and return scratch_space.
+  // Callers of this method must ensure scratch_space is a valid pointer
+  // to a mutable object of the correct type.
+  virtual const Value* Get(const Field* data, int index,
+                           Value* scratch_space) const = 0;
+
+  virtual void Clear(Field* data) const = 0;
+  virtual void Set(Field* data, int index, const Value* value) const = 0;
+  virtual void Add(Field* data, const Value* value) const = 0;
+  virtual void RemoveLast(Field* data) const = 0;
+  virtual void SwapElements(Field* data, int index1, int index2) const = 0;
+  virtual void Swap(Field* data, const RepeatedFieldAccessor* other_mutator,
+                    Field* other_data) const = 0;
+
+  // Create an iterator that points at the beginning of the repeated field.
+  virtual Iterator* BeginIterator(const Field* data) const = 0;
+  // Create an iterator that points at the end of the repeated field.
+  virtual Iterator* EndIterator(const Field* data) const = 0;
+  // Make a copy of an iterator and return the new copy.
+  virtual Iterator* CopyIterator(const Field* data,
+                                 const Iterator* iterator) const = 0;
+  // Move an iterator to point to the next element.
+  virtual Iterator* AdvanceIterator(const Field* data,
+                                    Iterator* iterator) const = 0;
+  // Compare whether two iterators point to the same element.
+  virtual bool EqualsIterator(const Field* data, const Iterator* a,
+                              const Iterator* b) const = 0;
+  // Delete an iterator created by BeginIterator(), EndIterator() and
+  // CopyIterator().
+  virtual void DeleteIterator(const Field* data, Iterator* iterator) const = 0;
+  // Like Get() but for iterators.
+  virtual const Value* GetIteratorValue(const Field* data,
+                                        const Iterator* iterator,
+                                        Value* scratch_space) const = 0;
+
+  // Templated methods that make using this interface easier for non-message
+  // types.
+  template <typename T>
+  T Get(const Field* data, int index) const {
+    typedef typename RefTypeTraits<T>::AccessorValueType ActualType;
+    ActualType scratch_space;
+    return static_cast<T>(*reinterpret_cast<const ActualType*>(
+        Get(data, index, static_cast<Value*>(&scratch_space))));
+  }
+
+  template <typename T, typename ValueType>
+  void Set(Field* data, int index, const ValueType& value) const {
+    typedef typename RefTypeTraits<T>::AccessorValueType ActualType;
+    // In this RepeatedFieldAccessor interface we pass/return data using
+    // raw pointers. Type of the data these raw pointers point to should
+    // be ActualType. Here we have a ValueType object and want a ActualType
+    // pointer. We can't cast a ValueType pointer to an ActualType pointer
+    // directly because their type might be different (for enums ValueType
+    // may be a generated enum type while ActualType is int32_t). To be safe
+    // we make a copy to get a temporary ActualType object and use it.
+    ActualType tmp = static_cast<ActualType>(value);
+    Set(data, index, static_cast<const Value*>(&tmp));
+  }
+
+  template <typename T, typename ValueType>
+  void Add(Field* data, const ValueType& value) const {
+    typedef typename RefTypeTraits<T>::AccessorValueType ActualType;
+    // In this RepeatedFieldAccessor interface we pass/return data using
+    // raw pointers. Type of the data these raw pointers point to should
+    // be ActualType. Here we have a ValueType object and want a ActualType
+    // pointer. We can't cast a ValueType pointer to an ActualType pointer
+    // directly because their type might be different (for enums ValueType
+    // may be a generated enum type while ActualType is int32_t). To be safe
+    // we make a copy to get a temporary ActualType object and use it.
+    ActualType tmp = static_cast<ActualType>(value);
+    Add(data, static_cast<const Value*>(&tmp));
+  }
+
+ protected:
+  // We want the destructor to be completely trivial as to allow it to be
+  // a function local static. Hence we make it non-virtual and protected,
+  // this class only live as part of a global singleton and should not be
+  // deleted.
+  ~RepeatedFieldAccessor() = default;
+};
+
+// Implement (Mutable)RepeatedFieldRef::iterator
+template <typename T>
+class RepeatedFieldRefIterator {
+  typedef typename RefTypeTraits<T>::AccessorValueType AccessorValueType;
+  typedef typename RefTypeTraits<T>::IteratorValueType IteratorValueType;
+  typedef typename RefTypeTraits<T>::IteratorPointerType IteratorPointerType;
+
+ public:
+  using iterator_category = std::forward_iterator_tag;
+  using value_type = T;
+  using pointer = T*;
+  using reference = T&;
+  using difference_type = std::ptrdiff_t;
+
+  // Constructor for non-message fields.
+  RepeatedFieldRefIterator(const void* data,
+                           const RepeatedFieldAccessor* accessor, bool begin)
+      : data_(data),
+        accessor_(accessor),
+        iterator_(begin ? accessor->BeginIterator(data)
+                        : accessor->EndIterator(data)),
+        // The end iterator must not be dereferenced, no need for scratch space.
+        scratch_space_(begin ? new AccessorValueType : nullptr) {}
+  // Constructor for message fields.
+  RepeatedFieldRefIterator(const void* data,
+                           const RepeatedFieldAccessor* accessor, bool begin,
+                           AccessorValueType* scratch_space)
+      : data_(data),
+        accessor_(accessor),
+        iterator_(begin ? accessor->BeginIterator(data)
+                        : accessor->EndIterator(data)),
+        scratch_space_(scratch_space) {}
+  ~RepeatedFieldRefIterator() { accessor_->DeleteIterator(data_, iterator_); }
+  RepeatedFieldRefIterator operator++(int) {
+    RepeatedFieldRefIterator tmp(*this);
+    iterator_ = accessor_->AdvanceIterator(data_, iterator_);
+    return tmp;
+  }
+  RepeatedFieldRefIterator& operator++() {
+    iterator_ = accessor_->AdvanceIterator(data_, iterator_);
+    return *this;
+  }
+  IteratorValueType operator*() const {
+    return static_cast<IteratorValueType>(
+        *static_cast<const AccessorValueType*>(accessor_->GetIteratorValue(
+            data_, iterator_, scratch_space_.get())));
+  }
+  IteratorPointerType operator->() const {
+    return static_cast<IteratorPointerType>(
+        accessor_->GetIteratorValue(data_, iterator_, scratch_space_.get()));
+  }
+  bool operator!=(const RepeatedFieldRefIterator& other) const {
+    assert(data_ == other.data_);
+    assert(accessor_ == other.accessor_);
+    return !accessor_->EqualsIterator(data_, iterator_, other.iterator_);
+  }
+  bool operator==(const RepeatedFieldRefIterator& other) const {
+    return !this->operator!=(other);
+  }
+
+  RepeatedFieldRefIterator(const RepeatedFieldRefIterator& other)
+      : data_(other.data_),
+        accessor_(other.accessor_),
+        iterator_(accessor_->CopyIterator(data_, other.iterator_)) {}
+  RepeatedFieldRefIterator& operator=(const RepeatedFieldRefIterator& other) {
+    if (this != &other) {
+      accessor_->DeleteIterator(data_, iterator_);
+      data_ = other.data_;
+      accessor_ = other.accessor_;
+      iterator_ = accessor_->CopyIterator(data_, other.iterator_);
+    }
+    return *this;
+  }
+
+ protected:
+  const void* data_;
+  const RepeatedFieldAccessor* accessor_;
+  void* iterator_;
+  std::unique_ptr<AccessorValueType> scratch_space_;
+};
+
+// TypeTraits that maps the type parameter T of RepeatedFieldRef or
+// MutableRepeatedFieldRef to corresponding iterator type,
+// RepeatedFieldAccessor type, etc.
+template <typename T>
+struct PrimitiveTraits {
+  static constexpr bool is_primitive = false;
+};
+#define DEFINE_PRIMITIVE(TYPE, type)                 \
+  template <>                                        \
+  struct PrimitiveTraits<type> {                     \
+    static const bool is_primitive = true;           \
+    static const FieldDescriptor::CppType cpp_type = \
+        FieldDescriptor::CPPTYPE_##TYPE;             \
+  };
+DEFINE_PRIMITIVE(INT32, int32_t)
+DEFINE_PRIMITIVE(UINT32, uint32_t)
+DEFINE_PRIMITIVE(INT64, int64_t)
+DEFINE_PRIMITIVE(UINT64, uint64_t)
+DEFINE_PRIMITIVE(FLOAT, float)
+DEFINE_PRIMITIVE(DOUBLE, double)
+DEFINE_PRIMITIVE(BOOL, bool)
+#undef DEFINE_PRIMITIVE
+
+template <typename T>
+struct RefTypeTraits<
+    T, typename std::enable_if<PrimitiveTraits<T>::is_primitive>::type> {
+  typedef RepeatedFieldRefIterator<T> iterator;
+  typedef RepeatedFieldAccessor AccessorType;
+  typedef T AccessorValueType;
+  typedef T IteratorValueType;
+  typedef T* IteratorPointerType;
+  static constexpr FieldDescriptor::CppType cpp_type =
+      PrimitiveTraits<T>::cpp_type;
+  static const Descriptor* GetMessageFieldDescriptor() { return nullptr; }
+};
+
+template <typename T>
+struct RefTypeTraits<
+    T, typename std::enable_if<is_proto_enum<T>::value>::type> {
+  typedef RepeatedFieldRefIterator<T> iterator;
+  typedef RepeatedFieldAccessor AccessorType;
+  // We use int32_t for repeated enums in RepeatedFieldAccessor.
+  typedef int32_t AccessorValueType;
+  typedef T IteratorValueType;
+  typedef int32_t* IteratorPointerType;
+  static constexpr FieldDescriptor::CppType cpp_type =
+      FieldDescriptor::CPPTYPE_ENUM;
+  static const Descriptor* GetMessageFieldDescriptor() { return nullptr; }
+};
+
+template <typename T>
+struct RefTypeTraits<
+    T, typename std::enable_if<std::is_same<std::string, T>::value>::type> {
+  typedef RepeatedFieldRefIterator<T> iterator;
+  typedef RepeatedFieldAccessor AccessorType;
+  typedef std::string AccessorValueType;
+  typedef const std::string IteratorValueType;
+  typedef const std::string* IteratorPointerType;
+  static constexpr FieldDescriptor::CppType cpp_type =
+      FieldDescriptor::CPPTYPE_STRING;
+  static const Descriptor* GetMessageFieldDescriptor() { return nullptr; }
+};
+
+template <typename T>
+struct MessageDescriptorGetter {
+  static const Descriptor* get() {
+    return T::default_instance().GetDescriptor();
+  }
+};
+template <>
+struct MessageDescriptorGetter<Message> {
+  static const Descriptor* get() { return nullptr; }
+};
+
+template <typename T>
+struct RefTypeTraits<
+    T, typename std::enable_if<std::is_base_of<Message, T>::value>::type> {
+  typedef RepeatedFieldRefIterator<T> iterator;
+  typedef RepeatedFieldAccessor AccessorType;
+  typedef Message AccessorValueType;
+  typedef const T& IteratorValueType;
+  typedef const T* IteratorPointerType;
+  static constexpr FieldDescriptor::CppType cpp_type =
+      FieldDescriptor::CPPTYPE_MESSAGE;
+  static const Descriptor* GetMessageFieldDescriptor() {
+    return MessageDescriptorGetter<T>::get();
+  }
+};
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_REFLECTION_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/reflection_internal.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/reflection_internal.h
new file mode 100644
index 0000000..f749c3e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/reflection_internal.h
@@ -0,0 +1,364 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_REFLECTION_INTERNAL_H__
+#define GOOGLE_PROTOBUF_REFLECTION_INTERNAL_H__
+
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/reflection.h>
+#include <google/protobuf/repeated_field.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+// A base class for RepeatedFieldAccessor implementations that can support
+// random-access efficiently. All iterator methods delegates the work to
+// corresponding random-access methods.
+class RandomAccessRepeatedFieldAccessor : public RepeatedFieldAccessor {
+ public:
+  Iterator* BeginIterator(const Field* /*data*/) const override {
+    return PositionToIterator(0);
+  }
+  Iterator* EndIterator(const Field* data) const override {
+    return PositionToIterator(this->Size(data));
+  }
+  Iterator* CopyIterator(const Field* /*data*/,
+                         const Iterator* iterator) const override {
+    return const_cast<Iterator*>(iterator);
+  }
+  Iterator* AdvanceIterator(const Field* /*data*/,
+                            Iterator* iterator) const override {
+    return PositionToIterator(IteratorToPosition(iterator) + 1);
+  }
+  bool EqualsIterator(const Field* /*data*/, const Iterator* a,
+                      const Iterator* b) const override {
+    return a == b;
+  }
+  void DeleteIterator(const Field* /*data*/,
+                      Iterator* /*iterator*/) const override {}
+  const Value* GetIteratorValue(const Field* data, const Iterator* iterator,
+                                Value* scratch_space) const override {
+    return Get(data, static_cast<int>(IteratorToPosition(iterator)),
+               scratch_space);
+  }
+
+ protected:
+  ~RandomAccessRepeatedFieldAccessor() = default;
+
+ private:
+  static intptr_t IteratorToPosition(const Iterator* iterator) {
+    return reinterpret_cast<intptr_t>(iterator);
+  }
+  static Iterator* PositionToIterator(intptr_t position) {
+    return reinterpret_cast<Iterator*>(position);
+  }
+};
+
+// Base class for RepeatedFieldAccessor implementations that manipulates
+// RepeatedField<T>.
+template <typename T>
+class RepeatedFieldWrapper : public RandomAccessRepeatedFieldAccessor {
+ public:
+  RepeatedFieldWrapper() {}
+  bool IsEmpty(const Field* data) const override {
+    return GetRepeatedField(data)->empty();
+  }
+  int Size(const Field* data) const override {
+    return GetRepeatedField(data)->size();
+  }
+  const Value* Get(const Field* data, int index,
+                   Value* scratch_space) const override {
+    return ConvertFromT(GetRepeatedField(data)->Get(index), scratch_space);
+  }
+  void Clear(Field* data) const override {
+    MutableRepeatedField(data)->Clear();
+  }
+  void Set(Field* data, int index, const Value* value) const override {
+    MutableRepeatedField(data)->Set(index, ConvertToT(value));
+  }
+  void Add(Field* data, const Value* value) const override {
+    MutableRepeatedField(data)->Add(ConvertToT(value));
+  }
+  void RemoveLast(Field* data) const override {
+    MutableRepeatedField(data)->RemoveLast();
+  }
+  void SwapElements(Field* data, int index1, int index2) const override {
+    MutableRepeatedField(data)->SwapElements(index1, index2);
+  }
+
+ protected:
+  ~RepeatedFieldWrapper() = default;
+  typedef RepeatedField<T> RepeatedFieldType;
+  static const RepeatedFieldType* GetRepeatedField(const Field* data) {
+    return reinterpret_cast<const RepeatedFieldType*>(data);
+  }
+  static RepeatedFieldType* MutableRepeatedField(Field* data) {
+    return reinterpret_cast<RepeatedFieldType*>(data);
+  }
+
+  // Convert an object received by this accessor to an object to be stored in
+  // the underlying RepeatedField.
+  virtual T ConvertToT(const Value* value) const = 0;
+
+  // Convert an object stored in RepeatedPtrField to an object that will be
+  // returned by this accessor. If the two objects have the same type (true for
+  // string fields with ctype=STRING), a pointer to the source object can be
+  // returned directly. Otherwise, data should be copied from value to
+  // scratch_space and scratch_space should be returned.
+  virtual const Value* ConvertFromT(const T& value,
+                                    Value* scratch_space) const = 0;
+};
+
+// Base class for RepeatedFieldAccessor implementations that manipulates
+// RepeatedPtrField<T>.
+template <typename T>
+class RepeatedPtrFieldWrapper : public RandomAccessRepeatedFieldAccessor {
+ public:
+  bool IsEmpty(const Field* data) const override {
+    return GetRepeatedField(data)->empty();
+  }
+  int Size(const Field* data) const override {
+    return GetRepeatedField(data)->size();
+  }
+  const Value* Get(const Field* data, int index,
+                   Value* scratch_space) const override {
+    return ConvertFromT(GetRepeatedField(data)->Get(index), scratch_space);
+  }
+  void Clear(Field* data) const override {
+    MutableRepeatedField(data)->Clear();
+  }
+  void Set(Field* data, int index, const Value* value) const override {
+    ConvertToT(value, MutableRepeatedField(data)->Mutable(index));
+  }
+  void Add(Field* data, const Value* value) const override {
+    T* allocated = New(value);
+    ConvertToT(value, allocated);
+    MutableRepeatedField(data)->AddAllocated(allocated);
+  }
+  void RemoveLast(Field* data) const override {
+    MutableRepeatedField(data)->RemoveLast();
+  }
+  void SwapElements(Field* data, int index1, int index2) const override {
+    MutableRepeatedField(data)->SwapElements(index1, index2);
+  }
+
+ protected:
+  ~RepeatedPtrFieldWrapper() = default;
+  typedef RepeatedPtrField<T> RepeatedFieldType;
+  static const RepeatedFieldType* GetRepeatedField(const Field* data) {
+    return reinterpret_cast<const RepeatedFieldType*>(data);
+  }
+  static RepeatedFieldType* MutableRepeatedField(Field* data) {
+    return reinterpret_cast<RepeatedFieldType*>(data);
+  }
+
+  // Create a new T instance. For repeated message fields, T can be specified
+  // as google::protobuf::Message so we can't use "new T()" directly. In that case, value
+  // should be a message of the same type (it's ensured by the caller) and a
+  // new message object will be created using it.
+  virtual T* New(const Value* value) const = 0;
+
+  // Convert an object received by this accessor to an object that will be
+  // stored in the underlying RepeatedPtrField.
+  virtual void ConvertToT(const Value* value, T* result) const = 0;
+
+  // Convert an object stored in RepeatedPtrField to an object that will be
+  // returned by this accessor. If the two objects have the same type (true for
+  // string fields with ctype=STRING), a pointer to the source object can be
+  // returned directly. Otherwise, data should be copied from value to
+  // scratch_space and scratch_space should be returned.
+  virtual const Value* ConvertFromT(const T& value,
+                                    Value* scratch_space) const = 0;
+};
+
+// An implementation of RandomAccessRepeatedFieldAccessor that manipulates
+// MapFieldBase.
+class MapFieldAccessor final : public RandomAccessRepeatedFieldAccessor {
+ public:
+  MapFieldAccessor() {}
+  virtual ~MapFieldAccessor() {}
+  bool IsEmpty(const Field* data) const override {
+    return GetRepeatedField(data)->empty();
+  }
+  int Size(const Field* data) const override {
+    return GetRepeatedField(data)->size();
+  }
+  const Value* Get(const Field* data, int index,
+                   Value* scratch_space) const override {
+    return ConvertFromEntry(GetRepeatedField(data)->Get(index), scratch_space);
+  }
+  void Clear(Field* data) const override {
+    MutableRepeatedField(data)->Clear();
+  }
+  void Set(Field* data, int index, const Value* value) const override {
+    ConvertToEntry(value, MutableRepeatedField(data)->Mutable(index));
+  }
+  void Add(Field* data, const Value* value) const override {
+    Message* allocated = New(value);
+    ConvertToEntry(value, allocated);
+    MutableRepeatedField(data)->AddAllocated(allocated);
+  }
+  void RemoveLast(Field* data) const override {
+    MutableRepeatedField(data)->RemoveLast();
+  }
+  void SwapElements(Field* data, int index1, int index2) const override {
+    MutableRepeatedField(data)->SwapElements(index1, index2);
+  }
+  void Swap(Field* data, const internal::RepeatedFieldAccessor* other_mutator,
+            Field* other_data) const override {
+    GOOGLE_CHECK(this == other_mutator);
+    MutableRepeatedField(data)->Swap(MutableRepeatedField(other_data));
+  }
+
+ protected:
+  typedef RepeatedPtrField<Message> RepeatedFieldType;
+  static const RepeatedFieldType* GetRepeatedField(const Field* data) {
+    return reinterpret_cast<const RepeatedFieldType*>(
+        (&reinterpret_cast<const MapFieldBase*>(data)->GetRepeatedField()));
+  }
+  static RepeatedFieldType* MutableRepeatedField(Field* data) {
+    return reinterpret_cast<RepeatedFieldType*>(
+        reinterpret_cast<MapFieldBase*>(data)->MutableRepeatedField());
+  }
+  virtual Message* New(const Value* value) const {
+    return static_cast<const Message*>(value)->New();
+  }
+  // Convert an object received by this accessor to an MapEntry message to be
+  // stored in the underlying MapFieldBase.
+  virtual void ConvertToEntry(const Value* value, Message* result) const {
+    result->CopyFrom(*static_cast<const Message*>(value));
+  }
+  // Convert a MapEntry message stored in the underlying MapFieldBase to an
+  // object that will be returned by this accessor.
+  virtual const Value* ConvertFromEntry(const Message& value,
+                                        Value* /*scratch_space*/) const {
+    return static_cast<const Value*>(&value);
+  }
+};
+
+// Default implementations of RepeatedFieldAccessor for primitive types.
+template <typename T>
+class RepeatedFieldPrimitiveAccessor final : public RepeatedFieldWrapper<T> {
+  typedef void Field;
+  typedef void Value;
+  using RepeatedFieldWrapper<T>::MutableRepeatedField;
+
+ public:
+  RepeatedFieldPrimitiveAccessor() {}
+  void Swap(Field* data, const internal::RepeatedFieldAccessor* other_mutator,
+            Field* other_data) const override {
+    // Currently RepeatedFieldPrimitiveAccessor is the only implementation of
+    // RepeatedFieldAccessor for primitive types. As we are using singletons
+    // for these accessors, here "other_mutator" must be "this".
+    GOOGLE_CHECK(this == other_mutator);
+    MutableRepeatedField(data)->Swap(MutableRepeatedField(other_data));
+  }
+
+ protected:
+  T ConvertToT(const Value* value) const override {
+    return *static_cast<const T*>(value);
+  }
+  const Value* ConvertFromT(const T& value,
+                            Value* /*scratch_space*/) const override {
+    return static_cast<const Value*>(&value);
+  }
+};
+
+// Default implementation of RepeatedFieldAccessor for string fields with
+// ctype=STRING.
+class RepeatedPtrFieldStringAccessor final
+    : public RepeatedPtrFieldWrapper<std::string> {
+  typedef void Field;
+  typedef void Value;
+  using RepeatedFieldAccessor::Add;
+
+ public:
+  RepeatedPtrFieldStringAccessor() {}
+  void Swap(Field* data, const internal::RepeatedFieldAccessor* other_mutator,
+            Field* other_data) const override {
+    if (this == other_mutator) {
+      MutableRepeatedField(data)->Swap(MutableRepeatedField(other_data));
+    } else {
+      RepeatedPtrField<std::string> tmp;
+      tmp.Swap(MutableRepeatedField(data));
+      int other_size = other_mutator->Size(other_data);
+      for (int i = 0; i < other_size; ++i) {
+        Add<std::string>(data, other_mutator->Get<std::string>(other_data, i));
+      }
+      int size = Size(data);
+      other_mutator->Clear(other_data);
+      for (int i = 0; i < size; ++i) {
+        other_mutator->Add<std::string>(other_data, tmp.Get(i));
+      }
+    }
+  }
+
+ protected:
+  std::string* New(const Value*) const override { return new std::string(); }
+  void ConvertToT(const Value* value, std::string* result) const override {
+    *result = *static_cast<const std::string*>(value);
+  }
+  const Value* ConvertFromT(const std::string& value,
+                            Value* /*scratch_space*/) const override {
+    return static_cast<const Value*>(&value);
+  }
+};
+
+
+class RepeatedPtrFieldMessageAccessor final
+    : public RepeatedPtrFieldWrapper<Message> {
+  typedef void Field;
+  typedef void Value;
+
+ public:
+  RepeatedPtrFieldMessageAccessor() {}
+  void Swap(Field* data, const internal::RepeatedFieldAccessor* other_mutator,
+            Field* other_data) const override {
+    GOOGLE_CHECK(this == other_mutator);
+    MutableRepeatedField(data)->Swap(MutableRepeatedField(other_data));
+  }
+
+ protected:
+  Message* New(const Value* value) const override {
+    return static_cast<const Message*>(value)->New();
+  }
+  void ConvertToT(const Value* value, Message* result) const override {
+    result->CopyFrom(*static_cast<const Message*>(value));
+  }
+  const Value* ConvertFromT(const Message& value,
+                            Value* /*scratch_space*/) const override {
+    return static_cast<const Value*>(&value);
+  }
+};
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_REFLECTION_INTERNAL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/reflection_ops.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/reflection_ops.h
new file mode 100644
index 0000000..0a45702
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/reflection_ops.h
@@ -0,0 +1,92 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This header is logically internal, but is made public because it is used
+// from protocol-compiler-generated code, which may reside in other components.
+
+#ifndef GOOGLE_PROTOBUF_REFLECTION_OPS_H__
+#define GOOGLE_PROTOBUF_REFLECTION_OPS_H__
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/message.h>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Basic operations that can be performed using reflection.
+// These can be used as a cheap way to implement the corresponding
+// methods of the Message interface, though they are likely to be
+// slower than implementations tailored for the specific message type.
+//
+// This class should stay limited to operations needed to implement
+// the Message interface.
+//
+// This class is really a namespace that contains only static methods.
+class PROTOBUF_EXPORT ReflectionOps {
+ public:
+  static void Copy(const Message& from, Message* to);
+  static void Merge(const Message& from, Message* to);
+  static void Clear(Message* message);
+  static bool IsInitialized(const Message& message);
+  static bool IsInitialized(const Message& message, bool check_fields,
+                            bool check_descendants);
+  static void DiscardUnknownFields(Message* message);
+
+  // Finds all unset required fields in the message and adds their full
+  // paths (e.g. "foo.bar[5].baz") to *names.  "prefix" will be attached to
+  // the front of each name.
+  static void FindInitializationErrors(const Message& message,
+                                       const std::string& prefix,
+                                       std::vector<std::string>* errors);
+
+ private:
+  // All methods are static.  No need to construct.
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ReflectionOps);
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_REFLECTION_OPS_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/repeated_field.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/repeated_field.h
new file mode 100644
index 0000000..3fb734e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/repeated_field.h
@@ -0,0 +1,1219 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// RepeatedField and RepeatedPtrField are used by generated protocol message
+// classes to manipulate repeated fields.  These classes are very similar to
+// STL's vector, but include a number of optimizations found to be useful
+// specifically in the case of Protocol Buffers.  RepeatedPtrField is
+// particularly different from STL vector as it manages ownership of the
+// pointers that it contains.
+//
+// This header covers RepeatedField.
+
+#ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_H__
+#define GOOGLE_PROTOBUF_REPEATED_FIELD_H__
+
+
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_ptr_field.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+
+class Message;
+
+namespace internal {
+
+template <typename T, int kRepHeaderSize>
+constexpr int RepeatedFieldLowerClampLimit() {
+  // The header is padded to be at least `sizeof(T)` when it would be smaller
+  // otherwise.
+  static_assert(sizeof(T) <= kRepHeaderSize, "");
+  // We want to pad the minimum size to be a power of two bytes, including the
+  // header.
+  // The first allocation is kRepHeaderSize bytes worth of elements for a total
+  // of 2*kRepHeaderSize bytes.
+  // For an 8-byte header, we allocate 8 bool, 2 ints, or 1 int64.
+  return kRepHeaderSize / sizeof(T);
+}
+
+// kRepeatedFieldUpperClampLimit is the lowest signed integer value that
+// overflows when multiplied by 2 (which is undefined behavior). Sizes above
+// this will clamp to the maximum int value instead of following exponential
+// growth when growing a repeated field.
+constexpr int kRepeatedFieldUpperClampLimit =
+    (std::numeric_limits<int>::max() / 2) + 1;
+
+template <typename Iter>
+inline int CalculateReserve(Iter begin, Iter end, std::forward_iterator_tag) {
+  return static_cast<int>(std::distance(begin, end));
+}
+
+template <typename Iter>
+inline int CalculateReserve(Iter /*begin*/, Iter /*end*/,
+                            std::input_iterator_tag /*unused*/) {
+  return -1;
+}
+
+template <typename Iter>
+inline int CalculateReserve(Iter begin, Iter end) {
+  typedef typename std::iterator_traits<Iter>::iterator_category Category;
+  return CalculateReserve(begin, end, Category());
+}
+
+// Swaps two blocks of memory of size sizeof(T).
+template <typename T>
+inline void SwapBlock(char* p, char* q) {
+  T tmp;
+  memcpy(&tmp, p, sizeof(T));
+  memcpy(p, q, sizeof(T));
+  memcpy(q, &tmp, sizeof(T));
+}
+
+// Swaps two blocks of memory of size kSize:
+//  template <int kSize> void memswap(char* p, char* q);
+template <int kSize>
+inline typename std::enable_if<(kSize == 0), void>::type memswap(char*, char*) {
+}
+
+#define PROTO_MEMSWAP_DEF_SIZE(reg_type, max_size)                           \
+  template <int kSize>                                                       \
+  typename std::enable_if<(kSize >= sizeof(reg_type) && kSize < (max_size)), \
+                          void>::type                                        \
+  memswap(char* p, char* q) {                                                \
+    SwapBlock<reg_type>(p, q);                                               \
+    memswap<kSize - sizeof(reg_type)>(p + sizeof(reg_type),                  \
+                                      q + sizeof(reg_type));                 \
+  }
+
+PROTO_MEMSWAP_DEF_SIZE(uint8_t, 2)
+PROTO_MEMSWAP_DEF_SIZE(uint16_t, 4)
+PROTO_MEMSWAP_DEF_SIZE(uint32_t, 8)
+
+#ifdef __SIZEOF_INT128__
+PROTO_MEMSWAP_DEF_SIZE(uint64_t, 16)
+PROTO_MEMSWAP_DEF_SIZE(__uint128_t, (1u << 31))
+#else
+PROTO_MEMSWAP_DEF_SIZE(uint64_t, (1u << 31))
+#endif
+
+#undef PROTO_MEMSWAP_DEF_SIZE
+
+template <typename Element>
+class RepeatedIterator;
+
+}  // namespace internal
+
+// RepeatedField is used to represent repeated fields of a primitive type (in
+// other words, everything except strings and nested Messages).  Most users will
+// not ever use a RepeatedField directly; they will use the get-by-index,
+// set-by-index, and add accessors that are generated for all repeated fields.
+template <typename Element>
+class RepeatedField final {
+  static_assert(
+      alignof(Arena) >= alignof(Element),
+      "We only support types that have an alignment smaller than Arena");
+
+ public:
+  constexpr RepeatedField();
+  explicit RepeatedField(Arena* arena);
+
+  RepeatedField(const RepeatedField& other);
+
+  template <typename Iter,
+            typename = typename std::enable_if<std::is_constructible<
+                Element, decltype(*std::declval<Iter>())>::value>::type>
+  RepeatedField(Iter begin, Iter end);
+
+  ~RepeatedField();
+
+  RepeatedField& operator=(const RepeatedField& other);
+
+  RepeatedField(RepeatedField&& other) noexcept;
+  RepeatedField& operator=(RepeatedField&& other) noexcept;
+
+  bool empty() const;
+  int size() const;
+
+  const Element& Get(int index) const;
+  Element* Mutable(int index);
+
+  const Element& operator[](int index) const { return Get(index); }
+  Element& operator[](int index) { return *Mutable(index); }
+
+  const Element& at(int index) const;
+  Element& at(int index);
+
+  void Set(int index, const Element& value);
+  void Add(const Element& value);
+  // Appends a new element and returns a pointer to it.
+  // The new element is uninitialized if |Element| is a POD type.
+  Element* Add();
+  // Appends elements in the range [begin, end) after reserving
+  // the appropriate number of elements.
+  template <typename Iter>
+  void Add(Iter begin, Iter end);
+
+  // Removes the last element in the array.
+  void RemoveLast();
+
+  // Extracts elements with indices in "[start .. start+num-1]".
+  // Copies them into "elements[0 .. num-1]" if "elements" is not nullptr.
+  // Caution: also moves elements with indices [start+num ..].
+  // Calling this routine inside a loop can cause quadratic behavior.
+  void ExtractSubrange(int start, int num, Element* elements);
+
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear();
+  void MergeFrom(const RepeatedField& other);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void CopyFrom(const RepeatedField& other);
+
+  // Replaces the contents with RepeatedField(begin, end).
+  template <typename Iter>
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Assign(Iter begin, Iter end);
+
+  // Reserves space to expand the field to at least the given size.  If the
+  // array is grown, it will always be at least doubled in size.
+  void Reserve(int new_size);
+
+  // Resizes the RepeatedField to a new, smaller size.  This is O(1).
+  void Truncate(int new_size);
+
+  void AddAlreadyReserved(const Element& value);
+  // Appends a new element and return a pointer to it.
+  // The new element is uninitialized if |Element| is a POD type.
+  // Should be called only if Capacity() > Size().
+  Element* AddAlreadyReserved();
+  Element* AddNAlreadyReserved(int elements);
+  int Capacity() const;
+
+  // Like STL resize.  Uses value to fill appended elements.
+  // Like Truncate() if new_size <= size(), otherwise this is
+  // O(new_size - size()).
+  void Resize(int new_size, const Element& value);
+
+  // Gets the underlying array.  This pointer is possibly invalidated by
+  // any add or remove operation.
+  Element* mutable_data();
+  const Element* data() const;
+
+  // Swaps entire contents with "other". If they are separate arenas then,
+  // copies data between each other.
+  void Swap(RepeatedField* other);
+
+  // Swaps entire contents with "other". Should be called only if the caller can
+  // guarantee that both repeated fields are on the same arena or are on the
+  // heap. Swapping between different arenas is disallowed and caught by a
+  // GOOGLE_DCHECK (see API docs for details).
+  void UnsafeArenaSwap(RepeatedField* other);
+
+  // Swaps two elements.
+  void SwapElements(int index1, int index2);
+
+  // STL-like iterator support
+  typedef internal::RepeatedIterator<Element> iterator;
+  typedef internal::RepeatedIterator<const Element> const_iterator;
+  typedef Element value_type;
+  typedef value_type& reference;
+  typedef const value_type& const_reference;
+  typedef value_type* pointer;
+  typedef const value_type* const_pointer;
+  typedef int size_type;
+  typedef ptrdiff_t difference_type;
+
+  iterator begin();
+  const_iterator begin() const;
+  const_iterator cbegin() const;
+  iterator end();
+  const_iterator end() const;
+  const_iterator cend() const;
+
+  // Reverse iterator support
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+  typedef std::reverse_iterator<iterator> reverse_iterator;
+  reverse_iterator rbegin() { return reverse_iterator(end()); }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(end());
+  }
+  reverse_iterator rend() { return reverse_iterator(begin()); }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(begin());
+  }
+
+  // Returns the number of bytes used by the repeated field, excluding
+  // sizeof(*this)
+  size_t SpaceUsedExcludingSelfLong() const;
+
+  int SpaceUsedExcludingSelf() const {
+    return internal::ToIntSize(SpaceUsedExcludingSelfLong());
+  }
+
+  // Removes the element referenced by position.
+  //
+  // Returns an iterator to the element immediately following the removed
+  // element.
+  //
+  // Invalidates all iterators at or after the removed element, including end().
+  iterator erase(const_iterator position);
+
+  // Removes the elements in the range [first, last).
+  //
+  // Returns an iterator to the element immediately following the removed range.
+  //
+  // Invalidates all iterators at or after the removed range, including end().
+  iterator erase(const_iterator first, const_iterator last);
+
+  // Gets the Arena on which this RepeatedField stores its elements.
+  inline Arena* GetArena() const {
+    return GetOwningArena();
+  }
+
+  // For internal use only.
+  //
+  // This is public due to it being called by generated code.
+  inline void InternalSwap(RepeatedField* other);
+
+ private:
+  template <typename T> friend class Arena::InternalHelper;
+
+  // Gets the Arena on which this RepeatedField stores its elements.
+  inline Arena* GetOwningArena() const {
+    return (total_size_ == 0) ? static_cast<Arena*>(arena_or_elements_)
+                              : rep()->arena;
+  }
+
+  static constexpr int kInitialSize = 0;
+  // A note on the representation here (see also comment below for
+  // RepeatedPtrFieldBase's struct Rep):
+  //
+  // We maintain the same sizeof(RepeatedField) as before we added arena support
+  // so that we do not degrade performance by bloating memory usage. Directly
+  // adding an arena_ element to RepeatedField is quite costly. By using
+  // indirection in this way, we keep the same size when the RepeatedField is
+  // empty (common case), and add only an 8-byte header to the elements array
+  // when non-empty. We make sure to place the size fields directly in the
+  // RepeatedField class to avoid costly cache misses due to the indirection.
+  int current_size_;
+  int total_size_;
+  // Pad the Rep after arena allow for power-of-two byte sizes when
+  // sizeof(Element) > sizeof(Arena*). eg for 16-byte objects.
+  static PROTOBUF_CONSTEXPR const size_t kRepHeaderSize =
+      sizeof(Arena*) < sizeof(Element) ? sizeof(Element) : sizeof(Arena*);
+  struct Rep {
+    Arena* arena;
+    Element* elements() {
+      return reinterpret_cast<Element*>(reinterpret_cast<char*>(this) +
+                                        kRepHeaderSize);
+    }
+  };
+
+  // If total_size_ == 0 this points to an Arena otherwise it points to the
+  // elements member of a Rep struct. Using this invariant allows the storage of
+  // the arena pointer without an extra allocation in the constructor.
+  void* arena_or_elements_;
+
+  // Returns a pointer to elements array.
+  // pre-condition: the array must have been allocated.
+  Element* elements() const {
+    GOOGLE_DCHECK_GT(total_size_, 0);
+    // Because of above pre-condition this cast is safe.
+    return unsafe_elements();
+  }
+
+  // Returns a pointer to elements array if it exists; otherwise either null or
+  // an invalid pointer is returned. This only happens for empty repeated
+  // fields, where you can't dereference this pointer anyway (it's empty).
+  Element* unsafe_elements() const {
+    return static_cast<Element*>(arena_or_elements_);
+  }
+
+  // Returns a pointer to the Rep struct.
+  // pre-condition: the Rep must have been allocated, ie elements() is safe.
+  Rep* rep() const {
+    return reinterpret_cast<Rep*>(reinterpret_cast<char*>(elements()) -
+                                  kRepHeaderSize);
+  }
+
+  friend class Arena;
+  typedef void InternalArenaConstructable_;
+
+  // Moves the contents of |from| into |to|, possibly clobbering |from| in the
+  // process.  For primitive types this is just a memcpy(), but it could be
+  // specialized for non-primitive types to, say, swap each element instead.
+  void MoveArray(Element* to, Element* from, int size);
+
+  // Copies the elements of |from| into |to|.
+  void CopyArray(Element* to, const Element* from, int size);
+
+  // Internal helper to delete all elements and deallocate the storage.
+  void InternalDeallocate(Rep* rep, int size, bool in_destructor) {
+    if (rep != nullptr) {
+      Element* e = &rep->elements()[0];
+      if (!std::is_trivial<Element>::value) {
+        Element* limit = &rep->elements()[size];
+        for (; e < limit; e++) {
+          e->~Element();
+        }
+      }
+      const size_t bytes = size * sizeof(*e) + kRepHeaderSize;
+      if (rep->arena == nullptr) {
+        internal::SizedDelete(rep, bytes);
+      } else if (!in_destructor) {
+        // If we are in the destructor, we might be being destroyed as part of
+        // the arena teardown. We can't try and return blocks to the arena then.
+        rep->arena->ReturnArrayMemory(rep, bytes);
+      }
+    }
+  }
+
+  // This class is a performance wrapper around RepeatedField::Add(const T&)
+  // function. In general unless a RepeatedField is a local stack variable LLVM
+  // has a hard time optimizing Add. The machine code tends to be
+  // loop:
+  // mov %size, dword ptr [%repeated_field]       // load
+  // cmp %size, dword ptr [%repeated_field + 4]
+  // jae fallback
+  // mov %buffer, qword ptr [%repeated_field + 8]
+  // mov dword [%buffer + %size * 4], %value
+  // inc %size                                    // increment
+  // mov dword ptr [%repeated_field], %size       // store
+  // jmp loop
+  //
+  // This puts a load/store in each iteration of the important loop variable
+  // size. It's a pretty bad compile that happens even in simple cases, but
+  // largely the presence of the fallback path disturbs the compilers mem-to-reg
+  // analysis.
+  //
+  // This class takes ownership of a repeated field for the duration of its
+  // lifetime. The repeated field should not be accessed during this time, ie.
+  // only access through this class is allowed. This class should always be a
+  // function local stack variable. Intended use
+  //
+  // void AddSequence(const int* begin, const int* end, RepeatedField<int>* out)
+  // {
+  //   RepeatedFieldAdder<int> adder(out);  // Take ownership of out
+  //   for (auto it = begin; it != end; ++it) {
+  //     adder.Add(*it);
+  //   }
+  // }
+  //
+  // Typically, due to the fact that adder is a local stack variable, the
+  // compiler will be successful in mem-to-reg transformation and the machine
+  // code will be loop: cmp %size, %capacity jae fallback mov dword ptr [%buffer
+  // + %size * 4], %val inc %size jmp loop
+  //
+  // The first version executes at 7 cycles per iteration while the second
+  // version executes at only 1 or 2 cycles.
+  template <int = 0, bool = std::is_trivial<Element>::value>
+  class FastAdderImpl {
+   public:
+    explicit FastAdderImpl(RepeatedField* rf) : repeated_field_(rf) {
+      index_ = repeated_field_->current_size_;
+      capacity_ = repeated_field_->total_size_;
+      buffer_ = repeated_field_->unsafe_elements();
+    }
+    ~FastAdderImpl() { repeated_field_->current_size_ = index_; }
+
+    void Add(Element val) {
+      if (index_ == capacity_) {
+        repeated_field_->current_size_ = index_;
+        repeated_field_->Reserve(index_ + 1);
+        capacity_ = repeated_field_->total_size_;
+        buffer_ = repeated_field_->unsafe_elements();
+      }
+      buffer_[index_++] = val;
+    }
+
+   private:
+    RepeatedField* repeated_field_;
+    int index_;
+    int capacity_;
+    Element* buffer_;
+
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FastAdderImpl);
+  };
+
+  // FastAdder is a wrapper for adding fields. The specialization above handles
+  // POD types more efficiently than RepeatedField.
+  template <int I>
+  class FastAdderImpl<I, false> {
+   public:
+    explicit FastAdderImpl(RepeatedField* rf) : repeated_field_(rf) {}
+    void Add(const Element& val) { repeated_field_->Add(val); }
+
+   private:
+    RepeatedField* repeated_field_;
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FastAdderImpl);
+  };
+
+  using FastAdder = FastAdderImpl<>;
+
+  friend class TestRepeatedFieldHelper;
+  friend class ::google::protobuf::internal::ParseContext;
+};
+
+namespace internal {
+
+// This is a helper template to copy an array of elements efficiently when they
+// have a trivial copy constructor, and correctly otherwise. This really
+// shouldn't be necessary, but our compiler doesn't optimize std::copy very
+// effectively.
+template <typename Element,
+          bool HasTrivialCopy = std::is_trivial<Element>::value>
+struct ElementCopier {
+  void operator()(Element* to, const Element* from, int array_size);
+};
+
+}  // namespace internal
+
+// implementation ====================================================
+
+template <typename Element>
+constexpr RepeatedField<Element>::RepeatedField()
+    : current_size_(0), total_size_(0), arena_or_elements_(nullptr) {}
+
+template <typename Element>
+inline RepeatedField<Element>::RepeatedField(Arena* arena)
+    : current_size_(0), total_size_(0), arena_or_elements_(arena) {}
+
+template <typename Element>
+inline RepeatedField<Element>::RepeatedField(const RepeatedField& other)
+    : current_size_(0), total_size_(0), arena_or_elements_(nullptr) {
+  if (other.current_size_ != 0) {
+    Reserve(other.size());
+    AddNAlreadyReserved(other.size());
+    CopyArray(Mutable(0), &other.Get(0), other.size());
+  }
+}
+
+template <typename Element>
+template <typename Iter, typename>
+RepeatedField<Element>::RepeatedField(Iter begin, Iter end)
+    : current_size_(0), total_size_(0), arena_or_elements_(nullptr) {
+  Add(begin, end);
+}
+
+template <typename Element>
+RepeatedField<Element>::~RepeatedField() {
+#ifndef NDEBUG
+  // Try to trigger segfault / asan failure in non-opt builds if arena_
+  // lifetime has ended before the destructor.
+  auto arena = GetOwningArena();
+  if (arena) (void)arena->SpaceAllocated();
+#endif
+  if (total_size_ > 0) {
+    InternalDeallocate(rep(), total_size_, true);
+  }
+}
+
+template <typename Element>
+inline RepeatedField<Element>& RepeatedField<Element>::operator=(
+    const RepeatedField& other) {
+  if (this != &other) CopyFrom(other);
+  return *this;
+}
+
+template <typename Element>
+inline RepeatedField<Element>::RepeatedField(RepeatedField&& other) noexcept
+    : RepeatedField() {
+#ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+  CopyFrom(other);
+#else   // PROTOBUF_FORCE_COPY_IN_MOVE
+  // We don't just call Swap(&other) here because it would perform 3 copies if
+  // other is on an arena. This field can't be on an arena because arena
+  // construction always uses the Arena* accepting constructor.
+  if (other.GetOwningArena()) {
+    CopyFrom(other);
+  } else {
+    InternalSwap(&other);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+}
+
+template <typename Element>
+inline RepeatedField<Element>& RepeatedField<Element>::operator=(
+    RepeatedField&& other) noexcept {
+  // We don't just call Swap(&other) here because it would perform 3 copies if
+  // the two fields are on different arenas.
+  if (this != &other) {
+    if (GetOwningArena() != other.GetOwningArena()
+#ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        || GetOwningArena() == nullptr
+#endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      CopyFrom(other);
+    } else {
+      InternalSwap(&other);
+    }
+  }
+  return *this;
+}
+
+template <typename Element>
+inline bool RepeatedField<Element>::empty() const {
+  return current_size_ == 0;
+}
+
+template <typename Element>
+inline int RepeatedField<Element>::size() const {
+  return current_size_;
+}
+
+template <typename Element>
+inline int RepeatedField<Element>::Capacity() const {
+  return total_size_;
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::AddAlreadyReserved(const Element& value) {
+  GOOGLE_DCHECK_LT(current_size_, total_size_);
+  elements()[current_size_++] = value;
+}
+
+template <typename Element>
+inline Element* RepeatedField<Element>::AddAlreadyReserved() {
+  GOOGLE_DCHECK_LT(current_size_, total_size_);
+  return &elements()[current_size_++];
+}
+
+template <typename Element>
+inline Element* RepeatedField<Element>::AddNAlreadyReserved(int elements) {
+  GOOGLE_DCHECK_GE(total_size_ - current_size_, elements)
+      << total_size_ << ", " << current_size_;
+  // Warning: sometimes people call this when elements == 0 and
+  // total_size_ == 0. In this case the return pointer points to a zero size
+  // array (n == 0). Hence we can just use unsafe_elements(), because the user
+  // cannot dereference the pointer anyway.
+  Element* ret = unsafe_elements() + current_size_;
+  current_size_ += elements;
+  return ret;
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::Resize(int new_size, const Element& value) {
+  GOOGLE_DCHECK_GE(new_size, 0);
+  if (new_size > current_size_) {
+    Reserve(new_size);
+    std::fill(&elements()[current_size_], &elements()[new_size], value);
+  }
+  current_size_ = new_size;
+}
+
+template <typename Element>
+inline const Element& RepeatedField<Element>::Get(int index) const {
+  GOOGLE_DCHECK_GE(index, 0);
+  GOOGLE_DCHECK_LT(index, current_size_);
+  return elements()[index];
+}
+
+template <typename Element>
+inline const Element& RepeatedField<Element>::at(int index) const {
+  GOOGLE_CHECK_GE(index, 0);
+  GOOGLE_CHECK_LT(index, current_size_);
+  return elements()[index];
+}
+
+template <typename Element>
+inline Element& RepeatedField<Element>::at(int index) {
+  GOOGLE_CHECK_GE(index, 0);
+  GOOGLE_CHECK_LT(index, current_size_);
+  return elements()[index];
+}
+
+template <typename Element>
+inline Element* RepeatedField<Element>::Mutable(int index) {
+  GOOGLE_DCHECK_GE(index, 0);
+  GOOGLE_DCHECK_LT(index, current_size_);
+  return &elements()[index];
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::Set(int index, const Element& value) {
+  GOOGLE_DCHECK_GE(index, 0);
+  GOOGLE_DCHECK_LT(index, current_size_);
+  elements()[index] = value;
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::Add(const Element& value) {
+  uint32_t size = current_size_;
+  if (static_cast<int>(size) == total_size_) {
+    // value could reference an element of the array. Reserving new space will
+    // invalidate the reference. So we must make a copy first.
+    auto tmp = value;
+    Reserve(total_size_ + 1);
+    elements()[size] = std::move(tmp);
+  } else {
+    elements()[size] = value;
+  }
+  current_size_ = size + 1;
+}
+
+template <typename Element>
+inline Element* RepeatedField<Element>::Add() {
+  uint32_t size = current_size_;
+  if (static_cast<int>(size) == total_size_) Reserve(total_size_ + 1);
+  auto ptr = &elements()[size];
+  current_size_ = size + 1;
+  return ptr;
+}
+
+template <typename Element>
+template <typename Iter>
+inline void RepeatedField<Element>::Add(Iter begin, Iter end) {
+  int reserve = internal::CalculateReserve(begin, end);
+  if (reserve != -1) {
+    if (reserve == 0) {
+      return;
+    }
+
+    Reserve(reserve + size());
+    // TODO(ckennelly):  The compiler loses track of the buffer freshly
+    // allocated by Reserve() by the time we call elements, so it cannot
+    // guarantee that elements does not alias [begin(), end()).
+    //
+    // If restrict is available, annotating the pointer obtained from elements()
+    // causes this to lower to memcpy instead of memmove.
+    std::copy(begin, end, elements() + size());
+    current_size_ = reserve + size();
+  } else {
+    FastAdder fast_adder(this);
+    for (; begin != end; ++begin) fast_adder.Add(*begin);
+  }
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::RemoveLast() {
+  GOOGLE_DCHECK_GT(current_size_, 0);
+  current_size_--;
+}
+
+template <typename Element>
+void RepeatedField<Element>::ExtractSubrange(int start, int num,
+                                             Element* elements) {
+  GOOGLE_DCHECK_GE(start, 0);
+  GOOGLE_DCHECK_GE(num, 0);
+  GOOGLE_DCHECK_LE(start + num, this->current_size_);
+
+  // Save the values of the removed elements if requested.
+  if (elements != nullptr) {
+    for (int i = 0; i < num; ++i) elements[i] = this->Get(i + start);
+  }
+
+  // Slide remaining elements down to fill the gap.
+  if (num > 0) {
+    for (int i = start + num; i < this->current_size_; ++i)
+      this->Set(i - num, this->Get(i));
+    this->Truncate(this->current_size_ - num);
+  }
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::Clear() {
+  current_size_ = 0;
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::MergeFrom(const RepeatedField& other) {
+  GOOGLE_DCHECK_NE(&other, this);
+  if (other.current_size_ != 0) {
+    int existing_size = size();
+    Reserve(existing_size + other.size());
+    AddNAlreadyReserved(other.size());
+    CopyArray(Mutable(existing_size), &other.Get(0), other.size());
+  }
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::CopyFrom(const RepeatedField& other) {
+  if (&other == this) return;
+  Clear();
+  MergeFrom(other);
+}
+
+template <typename Element>
+template <typename Iter>
+inline void RepeatedField<Element>::Assign(Iter begin, Iter end) {
+  Clear();
+  Add(begin, end);
+}
+
+template <typename Element>
+inline typename RepeatedField<Element>::iterator RepeatedField<Element>::erase(
+    const_iterator position) {
+  return erase(position, position + 1);
+}
+
+template <typename Element>
+inline typename RepeatedField<Element>::iterator RepeatedField<Element>::erase(
+    const_iterator first, const_iterator last) {
+  size_type first_offset = first - cbegin();
+  if (first != last) {
+    Truncate(std::copy(last, cend(), begin() + first_offset) - cbegin());
+  }
+  return begin() + first_offset;
+}
+
+template <typename Element>
+inline Element* RepeatedField<Element>::mutable_data() {
+  return unsafe_elements();
+}
+
+template <typename Element>
+inline const Element* RepeatedField<Element>::data() const {
+  return unsafe_elements();
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::InternalSwap(RepeatedField* other) {
+  GOOGLE_DCHECK(this != other);
+
+  // Swap all fields at once.
+  static_assert(std::is_standard_layout<RepeatedField<Element>>::value,
+                "offsetof() requires standard layout before c++17");
+  internal::memswap<offsetof(RepeatedField, arena_or_elements_) +
+                    sizeof(this->arena_or_elements_) -
+                    offsetof(RepeatedField, current_size_)>(
+      reinterpret_cast<char*>(this) + offsetof(RepeatedField, current_size_),
+      reinterpret_cast<char*>(other) + offsetof(RepeatedField, current_size_));
+}
+
+template <typename Element>
+void RepeatedField<Element>::Swap(RepeatedField* other) {
+  if (this == other) return;
+#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+  if (GetOwningArena() != nullptr &&
+      GetOwningArena() == other->GetOwningArena()) {
+#else   // PROTOBUF_FORCE_COPY_IN_SWAP
+  if (GetOwningArena() == other->GetOwningArena()) {
+#endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+    InternalSwap(other);
+  } else {
+    RepeatedField<Element> temp(other->GetOwningArena());
+    temp.MergeFrom(*this);
+    CopyFrom(*other);
+    other->UnsafeArenaSwap(&temp);
+  }
+}
+
+template <typename Element>
+void RepeatedField<Element>::UnsafeArenaSwap(RepeatedField* other) {
+  if (this == other) return;
+  GOOGLE_DCHECK_EQ(GetOwningArena(), other->GetOwningArena());
+  InternalSwap(other);
+}
+
+template <typename Element>
+void RepeatedField<Element>::SwapElements(int index1, int index2) {
+  using std::swap;  // enable ADL with fallback
+  swap(elements()[index1], elements()[index2]);
+}
+
+template <typename Element>
+inline typename RepeatedField<Element>::iterator
+RepeatedField<Element>::begin() {
+  return iterator(unsafe_elements());
+}
+template <typename Element>
+inline typename RepeatedField<Element>::const_iterator
+RepeatedField<Element>::begin() const {
+  return const_iterator(unsafe_elements());
+}
+template <typename Element>
+inline typename RepeatedField<Element>::const_iterator
+RepeatedField<Element>::cbegin() const {
+  return const_iterator(unsafe_elements());
+}
+template <typename Element>
+inline typename RepeatedField<Element>::iterator RepeatedField<Element>::end() {
+  return iterator(unsafe_elements() + current_size_);
+}
+template <typename Element>
+inline typename RepeatedField<Element>::const_iterator
+RepeatedField<Element>::end() const {
+  return const_iterator(unsafe_elements() + current_size_);
+}
+template <typename Element>
+inline typename RepeatedField<Element>::const_iterator
+RepeatedField<Element>::cend() const {
+  return const_iterator(unsafe_elements() + current_size_);
+}
+
+template <typename Element>
+inline size_t RepeatedField<Element>::SpaceUsedExcludingSelfLong() const {
+  return total_size_ > 0 ? (total_size_ * sizeof(Element) + kRepHeaderSize) : 0;
+}
+
+namespace internal {
+// Returns the new size for a reserved field based on its 'total_size' and the
+// requested 'new_size'. The result is clamped to the closed interval:
+//   [internal::kMinRepeatedFieldAllocationSize,
+//    std::numeric_limits<int>::max()]
+// Requires:
+//     new_size > total_size &&
+//     (total_size == 0 ||
+//      total_size >= kRepeatedFieldLowerClampLimit)
+template <typename T, int kRepHeaderSize>
+inline int CalculateReserveSize(int total_size, int new_size) {
+  constexpr int lower_limit = RepeatedFieldLowerClampLimit<T, kRepHeaderSize>();
+  if (new_size < lower_limit) {
+    // Clamp to smallest allowed size.
+    return lower_limit;
+  }
+  constexpr int kMaxSizeBeforeClamp =
+      (std::numeric_limits<int>::max() - kRepHeaderSize) / 2;
+  if (PROTOBUF_PREDICT_FALSE(total_size > kMaxSizeBeforeClamp)) {
+    return std::numeric_limits<int>::max();
+  }
+  // We want to double the number of bytes, not the number of elements, to try
+  // to stay within power-of-two allocations.
+  // The allocation has kRepHeaderSize + sizeof(T) * capacity.
+  int doubled_size = 2 * total_size + kRepHeaderSize / sizeof(T);
+  return std::max(doubled_size, new_size);
+}
+}  // namespace internal
+
+// Avoid inlining of Reserve(): new, copy, and delete[] lead to a significant
+// amount of code bloat.
+template <typename Element>
+void RepeatedField<Element>::Reserve(int new_size) {
+  if (total_size_ >= new_size) return;
+  Rep* old_rep = total_size_ > 0 ? rep() : nullptr;
+  Rep* new_rep;
+  Arena* arena = GetOwningArena();
+
+  new_size = internal::CalculateReserveSize<Element, kRepHeaderSize>(
+      total_size_, new_size);
+
+  GOOGLE_DCHECK_LE(
+      static_cast<size_t>(new_size),
+      (std::numeric_limits<size_t>::max() - kRepHeaderSize) / sizeof(Element))
+      << "Requested size is too large to fit into size_t.";
+  size_t bytes =
+      kRepHeaderSize + sizeof(Element) * static_cast<size_t>(new_size);
+  if (arena == nullptr) {
+    new_rep = static_cast<Rep*>(::operator new(bytes));
+  } else {
+    new_rep = reinterpret_cast<Rep*>(Arena::CreateArray<char>(arena, bytes));
+  }
+  new_rep->arena = arena;
+  int old_total_size = total_size_;
+  // Already known: new_size >= internal::kMinRepeatedFieldAllocationSize
+  // Maintain invariant:
+  //     total_size_ == 0 ||
+  //     total_size_ >= internal::kMinRepeatedFieldAllocationSize
+  total_size_ = new_size;
+  arena_or_elements_ = new_rep->elements();
+  // Invoke placement-new on newly allocated elements. We shouldn't have to do
+  // this, since Element is supposed to be POD, but a previous version of this
+  // code allocated storage with "new Element[size]" and some code uses
+  // RepeatedField with non-POD types, relying on constructor invocation. If
+  // Element has a trivial constructor (e.g., int32_t), gcc (tested with -O2)
+  // completely removes this loop because the loop body is empty, so this has no
+  // effect unless its side-effects are required for correctness.
+  // Note that we do this before MoveArray() below because Element's copy
+  // assignment implementation will want an initialized instance first.
+  Element* e = &elements()[0];
+  Element* limit = e + total_size_;
+  for (; e < limit; e++) {
+    new (e) Element;
+  }
+  if (current_size_ > 0) {
+    MoveArray(&elements()[0], old_rep->elements(), current_size_);
+  }
+
+  // Likewise, we need to invoke destructors on the old array.
+  InternalDeallocate(old_rep, old_total_size, false);
+
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::Truncate(int new_size) {
+  GOOGLE_DCHECK_LE(new_size, current_size_);
+  if (current_size_ > 0) {
+    current_size_ = new_size;
+  }
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::MoveArray(Element* to, Element* from,
+                                              int array_size) {
+  CopyArray(to, from, array_size);
+}
+
+template <typename Element>
+inline void RepeatedField<Element>::CopyArray(Element* to, const Element* from,
+                                              int array_size) {
+  internal::ElementCopier<Element>()(to, from, array_size);
+}
+
+namespace internal {
+
+template <typename Element, bool HasTrivialCopy>
+void ElementCopier<Element, HasTrivialCopy>::operator()(Element* to,
+                                                        const Element* from,
+                                                        int array_size) {
+  std::copy(from, from + array_size, to);
+}
+
+template <typename Element>
+struct ElementCopier<Element, true> {
+  void operator()(Element* to, const Element* from, int array_size) {
+    memcpy(to, from, static_cast<size_t>(array_size) * sizeof(Element));
+  }
+};
+
+}  // namespace internal
+
+
+// -------------------------------------------------------------------
+
+// Iterators and helper functions that follow the spirit of the STL
+// std::back_insert_iterator and std::back_inserter but are tailor-made
+// for RepeatedField and RepeatedPtrField. Typical usage would be:
+//
+//   std::copy(some_sequence.begin(), some_sequence.end(),
+//             RepeatedFieldBackInserter(proto.mutable_sequence()));
+//
+// Ported by johannes from util/gtl/proto-array-iterators.h
+
+namespace internal {
+
+// STL-like iterator implementation for RepeatedField.  You should not
+// refer to this class directly; use RepeatedField<T>::iterator instead.
+//
+// Note: All of the iterator operators *must* be inlined to avoid performance
+// regressions.  This is caused by the extern template declarations below (which
+// are required because of the RepeatedField extern template declarations).  If
+// any of these functions aren't explicitly inlined (e.g. defined in the class),
+// the compiler isn't allowed to inline them.
+template <typename Element>
+class RepeatedIterator {
+ public:
+  using iterator_category = std::random_access_iterator_tag;
+  // Note: remove_const is necessary for std::partial_sum, which uses value_type
+  // to determine the summation variable type.
+  using value_type = typename std::remove_const<Element>::type;
+  using difference_type = std::ptrdiff_t;
+  using pointer = Element*;
+  using reference = Element&;
+
+  constexpr RepeatedIterator() noexcept : it_(nullptr) {}
+
+  // Allows "upcasting" from RepeatedIterator<T**> to
+  // RepeatedIterator<const T*const*>.
+  template <typename OtherElement,
+            typename std::enable_if<std::is_convertible<
+                OtherElement*, pointer>::value>::type* = nullptr>
+  constexpr RepeatedIterator(
+      const RepeatedIterator<OtherElement>& other) noexcept
+      : it_(other.it_) {}
+
+  // dereferenceable
+  constexpr reference operator*() const noexcept { return *it_; }
+  constexpr pointer operator->() const noexcept { return it_; }
+
+ private:
+  // Helper alias to hide the internal type.
+  using iterator = RepeatedIterator<Element>;
+
+ public:
+  // {inc,dec}rementable
+  iterator& operator++() noexcept {
+    ++it_;
+    return *this;
+  }
+  iterator operator++(int) noexcept { return iterator(it_++); }
+  iterator& operator--() noexcept {
+    --it_;
+    return *this;
+  }
+  iterator operator--(int) noexcept { return iterator(it_--); }
+
+  // equality_comparable
+  friend constexpr bool operator==(const iterator& x,
+                                   const iterator& y) noexcept {
+    return x.it_ == y.it_;
+  }
+  friend constexpr bool operator!=(const iterator& x,
+                                   const iterator& y) noexcept {
+    return x.it_ != y.it_;
+  }
+
+  // less_than_comparable
+  friend constexpr bool operator<(const iterator& x,
+                                  const iterator& y) noexcept {
+    return x.it_ < y.it_;
+  }
+  friend constexpr bool operator<=(const iterator& x,
+                                   const iterator& y) noexcept {
+    return x.it_ <= y.it_;
+  }
+  friend constexpr bool operator>(const iterator& x,
+                                  const iterator& y) noexcept {
+    return x.it_ > y.it_;
+  }
+  friend constexpr bool operator>=(const iterator& x,
+                                   const iterator& y) noexcept {
+    return x.it_ >= y.it_;
+  }
+
+  // addable, subtractable
+  iterator& operator+=(difference_type d) noexcept {
+    it_ += d;
+    return *this;
+  }
+  constexpr iterator operator+(difference_type d) const noexcept {
+    return iterator(it_ + d);
+  }
+  friend constexpr iterator operator+(const difference_type d,
+                                      iterator it) noexcept {
+    return it + d;
+  }
+
+  iterator& operator-=(difference_type d) noexcept {
+    it_ -= d;
+    return *this;
+  }
+  iterator constexpr operator-(difference_type d) const noexcept {
+    return iterator(it_ - d);
+  }
+
+  // indexable
+  constexpr reference operator[](difference_type d) const noexcept {
+    return it_[d];
+  }
+
+  // random access iterator
+  friend constexpr difference_type operator-(iterator it1,
+                                             iterator it2) noexcept {
+    return it1.it_ - it2.it_;
+  }
+
+ private:
+  template <typename OtherElement>
+  friend class RepeatedIterator;
+
+  // Allow construction from RepeatedField.
+  friend class RepeatedField<value_type>;
+  explicit RepeatedIterator(Element* it) noexcept : it_(it) {}
+
+  // The internal iterator.
+  Element* it_;
+};
+
+// A back inserter for RepeatedField objects.
+template <typename T>
+class RepeatedFieldBackInsertIterator {
+ public:
+  using iterator_category = std::output_iterator_tag;
+  using value_type = T;
+  using pointer = void;
+  using reference = void;
+  using difference_type = std::ptrdiff_t;
+
+  explicit RepeatedFieldBackInsertIterator(
+      RepeatedField<T>* const mutable_field)
+      : field_(mutable_field) {}
+  RepeatedFieldBackInsertIterator<T>& operator=(const T& value) {
+    field_->Add(value);
+    return *this;
+  }
+  RepeatedFieldBackInsertIterator<T>& operator*() { return *this; }
+  RepeatedFieldBackInsertIterator<T>& operator++() { return *this; }
+  RepeatedFieldBackInsertIterator<T>& operator++(int /* unused */) {
+    return *this;
+  }
+
+ private:
+  RepeatedField<T>* field_;
+};
+
+}  // namespace internal
+
+// Provides a back insert iterator for RepeatedField instances,
+// similar to std::back_inserter().
+template <typename T>
+internal::RepeatedFieldBackInsertIterator<T> RepeatedFieldBackInserter(
+    RepeatedField<T>* const mutable_field) {
+  return internal::RepeatedFieldBackInsertIterator<T>(mutable_field);
+}
+
+// Extern declarations of common instantiations to reduce library bloat.
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE RepeatedField<bool>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE RepeatedField<int32_t>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE RepeatedField<uint32_t>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE RepeatedField<int64_t>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE RepeatedField<uint64_t>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE RepeatedField<float>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE RepeatedField<double>;
+
+namespace internal {
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE RepeatedIterator<bool>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE
+    RepeatedIterator<int32_t>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE
+    RepeatedIterator<uint32_t>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE
+    RepeatedIterator<int64_t>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE
+    RepeatedIterator<uint64_t>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE RepeatedIterator<float>;
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE RepeatedIterator<double>;
+}  // namespace internal
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_REPEATED_FIELD_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/repeated_ptr_field.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/repeated_ptr_field.h
new file mode 100644
index 0000000..401230b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/repeated_ptr_field.h
@@ -0,0 +1,1970 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// RepeatedField and RepeatedPtrField are used by generated protocol message
+// classes to manipulate repeated fields.  These classes are very similar to
+// STL's vector, but include a number of optimizations found to be useful
+// specifically in the case of Protocol Buffers.  RepeatedPtrField is
+// particularly different from STL vector as it manages ownership of the
+// pointers that it contains.
+//
+// This header covers RepeatedPtrField.
+
+// IWYU pragma: private, include "net/proto2/public/repeated_field.h"
+
+#ifndef GOOGLE_PROTOBUF_REPEATED_PTR_FIELD_H__
+#define GOOGLE_PROTOBUF_REPEATED_PTR_FIELD_H__
+
+#include <utility>
+
+#ifdef _MSC_VER
+// This is required for min/max on VS2013 only.
+#include <algorithm>
+#endif
+
+#include <iterator>
+#include <limits>
+#include <string>
+#include <type_traits>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/message_lite.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+
+class Message;
+class Reflection;
+
+template <typename T>
+struct WeakRepeatedPtrField;
+
+namespace internal {
+
+class MergePartialFromCodedStreamHelper;
+class SwapFieldHelper;
+
+
+}  // namespace internal
+
+namespace internal {
+template <typename It>
+class RepeatedPtrIterator;
+template <typename It, typename VoidPtr>
+class RepeatedPtrOverPtrsIterator;
+}  // namespace internal
+
+namespace internal {
+
+// type-traits helper for RepeatedPtrFieldBase: we only want to invoke
+// arena-related "copy if on different arena" behavior if the necessary methods
+// exist on the contained type. In particular, we rely on MergeFrom() existing
+// as a general proxy for the fact that a copy will work, and we also provide a
+// specific override for std::string*.
+template <typename T>
+struct TypeImplementsMergeBehaviorProbeForMergeFrom {
+  typedef char HasMerge;
+  typedef long HasNoMerge;
+
+  // We accept either of:
+  // - void MergeFrom(const T& other)
+  // - bool MergeFrom(const T& other)
+  //
+  // We mangle these names a bit to avoid compatibility issues in 'unclean'
+  // include environments that may have, e.g., "#define test ..." (yes, this
+  // exists).
+  template <typename U, typename RetType, RetType (U::*)(const U& arg)>
+  struct CheckType;
+  template <typename U>
+  static HasMerge Check(CheckType<U, void, &U::MergeFrom>*);
+  template <typename U>
+  static HasMerge Check(CheckType<U, bool, &U::MergeFrom>*);
+  template <typename U>
+  static HasNoMerge Check(...);
+
+  // Resolves to either std::true_type or std::false_type.
+  typedef std::integral_constant<bool,
+                                 (sizeof(Check<T>(0)) == sizeof(HasMerge))>
+      type;
+};
+
+template <typename T, typename = void>
+struct TypeImplementsMergeBehavior
+    : TypeImplementsMergeBehaviorProbeForMergeFrom<T> {};
+
+
+template <>
+struct TypeImplementsMergeBehavior<std::string> {
+  typedef std::true_type type;
+};
+
+template <typename T>
+struct IsMovable
+    : std::integral_constant<bool, std::is_move_constructible<T>::value &&
+                                       std::is_move_assignable<T>::value> {};
+
+// This is the common base class for RepeatedPtrFields.  It deals only in void*
+// pointers.  Users should not use this interface directly.
+//
+// The methods of this interface correspond to the methods of RepeatedPtrField,
+// but may have a template argument called TypeHandler.  Its signature is:
+//   class TypeHandler {
+//    public:
+//     typedef MyType Type;
+//     static Type* New();
+//     static Type* NewFromPrototype(const Type* prototype,
+//                                       Arena* arena);
+//     static void Delete(Type*);
+//     static void Clear(Type*);
+//     static void Merge(const Type& from, Type* to);
+//
+//     // Only needs to be implemented if SpaceUsedExcludingSelf() is called.
+//     static int SpaceUsedLong(const Type&);
+//   };
+class PROTOBUF_EXPORT RepeatedPtrFieldBase {
+ protected:
+  constexpr RepeatedPtrFieldBase()
+      : arena_(nullptr), current_size_(0), total_size_(0), rep_(nullptr) {}
+  explicit RepeatedPtrFieldBase(Arena* arena)
+      : arena_(arena), current_size_(0), total_size_(0), rep_(nullptr) {}
+
+  RepeatedPtrFieldBase(const RepeatedPtrFieldBase&) = delete;
+  RepeatedPtrFieldBase& operator=(const RepeatedPtrFieldBase&) = delete;
+
+  ~RepeatedPtrFieldBase() {
+#ifndef NDEBUG
+    // Try to trigger segfault / asan failure in non-opt builds. If arena_
+    // lifetime has ended before the destructor.
+    if (arena_) (void)arena_->SpaceAllocated();
+#endif
+  }
+
+  bool empty() const { return current_size_ == 0; }
+  int size() const { return current_size_; }
+  int Capacity() const { return total_size_; }
+
+  template <typename TypeHandler>
+  const typename TypeHandler::Type& at(int index) const {
+    GOOGLE_CHECK_GE(index, 0);
+    GOOGLE_CHECK_LT(index, current_size_);
+    return *cast<TypeHandler>(rep_->elements[index]);
+  }
+
+  template <typename TypeHandler>
+  typename TypeHandler::Type& at(int index) {
+    GOOGLE_CHECK_GE(index, 0);
+    GOOGLE_CHECK_LT(index, current_size_);
+    return *cast<TypeHandler>(rep_->elements[index]);
+  }
+
+  template <typename TypeHandler>
+  typename TypeHandler::Type* Mutable(int index) {
+    GOOGLE_DCHECK_GE(index, 0);
+    GOOGLE_DCHECK_LT(index, current_size_);
+    return cast<TypeHandler>(rep_->elements[index]);
+  }
+
+  template <typename TypeHandler>
+  typename TypeHandler::Type* Add(
+      const typename TypeHandler::Type* prototype = nullptr) {
+    if (rep_ != nullptr && current_size_ < rep_->allocated_size) {
+      return cast<TypeHandler>(rep_->elements[current_size_++]);
+    }
+    typename TypeHandler::Type* result =
+        TypeHandler::NewFromPrototype(prototype, arena_);
+    return reinterpret_cast<typename TypeHandler::Type*>(
+        AddOutOfLineHelper(result));
+  }
+
+  template <
+      typename TypeHandler,
+      typename std::enable_if<TypeHandler::Movable::value>::type* = nullptr>
+  inline void Add(typename TypeHandler::Type&& value) {
+    if (rep_ != nullptr && current_size_ < rep_->allocated_size) {
+      *cast<TypeHandler>(rep_->elements[current_size_++]) = std::move(value);
+      return;
+    }
+    if (!rep_ || rep_->allocated_size == total_size_) {
+      Reserve(total_size_ + 1);
+    }
+    ++rep_->allocated_size;
+    typename TypeHandler::Type* result =
+        TypeHandler::New(arena_, std::move(value));
+    rep_->elements[current_size_++] = result;
+  }
+
+  template <typename TypeHandler>
+  void Delete(int index) {
+    GOOGLE_DCHECK_GE(index, 0);
+    GOOGLE_DCHECK_LT(index, current_size_);
+    TypeHandler::Delete(cast<TypeHandler>(rep_->elements[index]), arena_);
+  }
+
+  // Must be called from destructor.
+  template <typename TypeHandler>
+  void Destroy() {
+    if (rep_ != nullptr && arena_ == nullptr) {
+      int n = rep_->allocated_size;
+      void* const* elements = rep_->elements;
+      for (int i = 0; i < n; i++) {
+        TypeHandler::Delete(cast<TypeHandler>(elements[i]), nullptr);
+      }
+      const size_t size = total_size_ * sizeof(elements[0]) + kRepHeaderSize;
+      internal::SizedDelete(rep_, size);
+    }
+    rep_ = nullptr;
+  }
+
+  bool NeedsDestroy() const { return rep_ != nullptr && arena_ == nullptr; }
+  void DestroyProtos();  // implemented in the cc file
+
+ public:
+  // The next few methods are public so that they can be called from generated
+  // code when implicit weak fields are used, but they should never be called by
+  // application code.
+
+  template <typename TypeHandler>
+  const typename TypeHandler::Type& Get(int index) const {
+    GOOGLE_DCHECK_GE(index, 0);
+    GOOGLE_DCHECK_LT(index, current_size_);
+    return *cast<TypeHandler>(rep_->elements[index]);
+  }
+
+  // Creates and adds an element using the given prototype, without introducing
+  // a link-time dependency on the concrete message type. This method is used to
+  // implement implicit weak fields. The prototype may be nullptr, in which case
+  // an ImplicitWeakMessage will be used as a placeholder.
+  MessageLite* AddWeak(const MessageLite* prototype);
+
+  template <typename TypeHandler>
+  void Clear() {
+    const int n = current_size_;
+    GOOGLE_DCHECK_GE(n, 0);
+    if (n > 0) {
+      void* const* elements = rep_->elements;
+      int i = 0;
+      do {
+        TypeHandler::Clear(cast<TypeHandler>(elements[i++]));
+      } while (i < n);
+      current_size_ = 0;
+    }
+  }
+
+  template <typename TypeHandler>
+  void MergeFrom(const RepeatedPtrFieldBase& other) {
+    // To avoid unnecessary code duplication and reduce binary size, we use a
+    // layered approach to implementing MergeFrom(). The toplevel method is
+    // templated, so we get a small thunk per concrete message type in the
+    // binary. This calls a shared implementation with most of the logic,
+    // passing a function pointer to another type-specific piece of code that
+    // calls the object-allocate and merge handlers.
+    GOOGLE_DCHECK_NE(&other, this);
+    if (other.current_size_ == 0) return;
+    MergeFromInternal(other,
+                      &RepeatedPtrFieldBase::MergeFromInnerLoop<TypeHandler>);
+  }
+
+  inline void InternalSwap(RepeatedPtrFieldBase* rhs) {
+    GOOGLE_DCHECK(this != rhs);
+
+    // Swap all fields at once.
+    auto temp = std::make_tuple(rhs->arena_, rhs->current_size_,
+                                rhs->total_size_, rhs->rep_);
+    std::tie(rhs->arena_, rhs->current_size_, rhs->total_size_, rhs->rep_) =
+        std::make_tuple(arena_, current_size_, total_size_, rep_);
+    std::tie(arena_, current_size_, total_size_, rep_) = temp;
+  }
+
+ protected:
+  template <typename TypeHandler>
+  void RemoveLast() {
+    GOOGLE_DCHECK_GT(current_size_, 0);
+    TypeHandler::Clear(cast<TypeHandler>(rep_->elements[--current_size_]));
+  }
+
+  template <typename TypeHandler>
+  void CopyFrom(const RepeatedPtrFieldBase& other) {
+    if (&other == this) return;
+    RepeatedPtrFieldBase::Clear<TypeHandler>();
+    RepeatedPtrFieldBase::MergeFrom<TypeHandler>(other);
+  }
+
+  void CloseGap(int start, int num);  // implemented in the cc file
+
+  void Reserve(int new_size);  // implemented in the cc file
+
+  template <typename TypeHandler>
+  static inline typename TypeHandler::Type* copy(
+      typename TypeHandler::Type* value) {
+    auto* new_value = TypeHandler::NewFromPrototype(value, nullptr);
+    TypeHandler::Merge(*value, new_value);
+    return new_value;
+  }
+
+  // Used for constructing iterators.
+  void* const* raw_data() const { return rep_ ? rep_->elements : nullptr; }
+  void** raw_mutable_data() const {
+    return rep_ ? const_cast<void**>(rep_->elements) : nullptr;
+  }
+
+  template <typename TypeHandler>
+  typename TypeHandler::Type** mutable_data() {
+    // TODO(kenton):  Breaks C++ aliasing rules.  We should probably remove this
+    //   method entirely.
+    return reinterpret_cast<typename TypeHandler::Type**>(raw_mutable_data());
+  }
+
+  template <typename TypeHandler>
+  const typename TypeHandler::Type* const* data() const {
+    // TODO(kenton):  Breaks C++ aliasing rules.  We should probably remove this
+    //   method entirely.
+    return reinterpret_cast<const typename TypeHandler::Type* const*>(
+        raw_data());
+  }
+
+  template <typename TypeHandler>
+  PROTOBUF_NDEBUG_INLINE void Swap(RepeatedPtrFieldBase* other) {
+#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena())
+#else   // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena())
+#endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+    {
+      InternalSwap(other);
+    } else {
+      SwapFallback<TypeHandler>(other);
+    }
+  }
+
+  void SwapElements(int index1, int index2) {
+    using std::swap;  // enable ADL with fallback
+    swap(rep_->elements[index1], rep_->elements[index2]);
+  }
+
+  template <typename TypeHandler>
+  size_t SpaceUsedExcludingSelfLong() const {
+    size_t allocated_bytes = static_cast<size_t>(total_size_) * sizeof(void*);
+    if (rep_ != nullptr) {
+      for (int i = 0; i < rep_->allocated_size; ++i) {
+        allocated_bytes +=
+            TypeHandler::SpaceUsedLong(*cast<TypeHandler>(rep_->elements[i]));
+      }
+      allocated_bytes += kRepHeaderSize;
+    }
+    return allocated_bytes;
+  }
+
+  // Advanced memory management --------------------------------------
+
+  // Like Add(), but if there are no cleared objects to use, returns nullptr.
+  template <typename TypeHandler>
+  typename TypeHandler::Type* AddFromCleared() {
+    if (rep_ != nullptr && current_size_ < rep_->allocated_size) {
+      return cast<TypeHandler>(rep_->elements[current_size_++]);
+    } else {
+      return nullptr;
+    }
+  }
+
+  template <typename TypeHandler>
+  void AddAllocated(typename TypeHandler::Type* value) {
+    typename TypeImplementsMergeBehavior<typename TypeHandler::Type>::type t;
+    AddAllocatedInternal<TypeHandler>(value, t);
+  }
+
+  template <typename TypeHandler>
+  void UnsafeArenaAddAllocated(typename TypeHandler::Type* value) {
+    // Make room for the new pointer.
+    if (!rep_ || current_size_ == total_size_) {
+      // The array is completely full with no cleared objects, so grow it.
+      Reserve(total_size_ + 1);
+      ++rep_->allocated_size;
+    } else if (rep_->allocated_size == total_size_) {
+      // There is no more space in the pointer array because it contains some
+      // cleared objects awaiting reuse.  We don't want to grow the array in
+      // this case because otherwise a loop calling AddAllocated() followed by
+      // Clear() would leak memory.
+      TypeHandler::Delete(cast<TypeHandler>(rep_->elements[current_size_]),
+                          arena_);
+    } else if (current_size_ < rep_->allocated_size) {
+      // We have some cleared objects.  We don't care about their order, so we
+      // can just move the first one to the end to make space.
+      rep_->elements[rep_->allocated_size] = rep_->elements[current_size_];
+      ++rep_->allocated_size;
+    } else {
+      // There are no cleared objects.
+      ++rep_->allocated_size;
+    }
+
+    rep_->elements[current_size_++] = value;
+  }
+
+  template <typename TypeHandler>
+  PROTOBUF_NODISCARD typename TypeHandler::Type* ReleaseLast() {
+    typename TypeImplementsMergeBehavior<typename TypeHandler::Type>::type t;
+    return ReleaseLastInternal<TypeHandler>(t);
+  }
+
+  // Releases and returns the last element, but does not do out-of-arena copy.
+  // Instead, just returns the raw pointer to the contained element in the
+  // arena.
+  template <typename TypeHandler>
+  typename TypeHandler::Type* UnsafeArenaReleaseLast() {
+    GOOGLE_DCHECK_GT(current_size_, 0);
+    typename TypeHandler::Type* result =
+        cast<TypeHandler>(rep_->elements[--current_size_]);
+    --rep_->allocated_size;
+    if (current_size_ < rep_->allocated_size) {
+      // There are cleared elements on the end; replace the removed element
+      // with the last allocated element.
+      rep_->elements[current_size_] = rep_->elements[rep_->allocated_size];
+    }
+    return result;
+  }
+
+  int ClearedCount() const {
+    return rep_ ? (rep_->allocated_size - current_size_) : 0;
+  }
+
+  template <typename TypeHandler>
+  void AddCleared(typename TypeHandler::Type* value) {
+    GOOGLE_DCHECK(GetOwningArena() == nullptr) << "AddCleared() can only be used on a "
+                                           "RepeatedPtrField not on an arena.";
+    GOOGLE_DCHECK(TypeHandler::GetOwningArena(value) == nullptr)
+        << "AddCleared() can only accept values not on an arena.";
+    if (!rep_ || rep_->allocated_size == total_size_) {
+      Reserve(total_size_ + 1);
+    }
+    rep_->elements[rep_->allocated_size++] = value;
+  }
+
+  template <typename TypeHandler>
+  PROTOBUF_NODISCARD typename TypeHandler::Type* ReleaseCleared() {
+    GOOGLE_DCHECK(GetOwningArena() == nullptr)
+        << "ReleaseCleared() can only be used on a RepeatedPtrField not on "
+        << "an arena.";
+    GOOGLE_DCHECK(GetOwningArena() == nullptr);
+    GOOGLE_DCHECK(rep_ != nullptr);
+    GOOGLE_DCHECK_GT(rep_->allocated_size, current_size_);
+    return cast<TypeHandler>(rep_->elements[--rep_->allocated_size]);
+  }
+
+  template <typename TypeHandler>
+  void AddAllocatedInternal(typename TypeHandler::Type* value, std::true_type) {
+    // AddAllocated version that implements arena-safe copying behavior.
+    Arena* element_arena =
+        reinterpret_cast<Arena*>(TypeHandler::GetOwningArena(value));
+    Arena* arena = GetOwningArena();
+    if (arena == element_arena && rep_ && rep_->allocated_size < total_size_) {
+      // Fast path: underlying arena representation (tagged pointer) is equal to
+      // our arena pointer, and we can add to array without resizing it (at
+      // least one slot that is not allocated).
+      void** elems = rep_->elements;
+      if (current_size_ < rep_->allocated_size) {
+        // Make space at [current] by moving first allocated element to end of
+        // allocated list.
+        elems[rep_->allocated_size] = elems[current_size_];
+      }
+      elems[current_size_] = value;
+      current_size_ = current_size_ + 1;
+      rep_->allocated_size = rep_->allocated_size + 1;
+    } else {
+      AddAllocatedSlowWithCopy<TypeHandler>(value, element_arena, arena);
+    }
+  }
+
+  template <typename TypeHandler>
+  void AddAllocatedInternal(
+      // AddAllocated version that does not implement arena-safe copying
+      // behavior.
+      typename TypeHandler::Type* value, std::false_type) {
+    if (rep_ && rep_->allocated_size < total_size_) {
+      // Fast path: underlying arena representation (tagged pointer) is equal to
+      // our arena pointer, and we can add to array without resizing it (at
+      // least one slot that is not allocated).
+      void** elems = rep_->elements;
+      if (current_size_ < rep_->allocated_size) {
+        // Make space at [current] by moving first allocated element to end of
+        // allocated list.
+        elems[rep_->allocated_size] = elems[current_size_];
+      }
+      elems[current_size_] = value;
+      current_size_ = current_size_ + 1;
+      ++rep_->allocated_size;
+    } else {
+      UnsafeArenaAddAllocated<TypeHandler>(value);
+    }
+  }
+
+  // Slowpath handles all cases, copying if necessary.
+  template <typename TypeHandler>
+  PROTOBUF_NOINLINE void AddAllocatedSlowWithCopy(
+      // Pass value_arena and my_arena to avoid duplicate virtual call (value)
+      // or load (mine).
+      typename TypeHandler::Type* value, Arena* value_arena, Arena* my_arena) {
+    // Ensure that either the value is in the same arena, or if not, we do the
+    // appropriate thing: Own() it (if it's on heap and we're in an arena) or
+    // copy it to our arena/heap (otherwise).
+    if (my_arena != nullptr && value_arena == nullptr) {
+      my_arena->Own(value);
+    } else if (my_arena != value_arena) {
+      typename TypeHandler::Type* new_value =
+          TypeHandler::NewFromPrototype(value, my_arena);
+      TypeHandler::Merge(*value, new_value);
+      TypeHandler::Delete(value, value_arena);
+      value = new_value;
+    }
+
+    UnsafeArenaAddAllocated<TypeHandler>(value);
+  }
+
+  template <typename TypeHandler>
+  typename TypeHandler::Type* ReleaseLastInternal(std::true_type) {
+    // ReleaseLast() for types that implement merge/copy behavior.
+    // First, release an element.
+    typename TypeHandler::Type* result = UnsafeArenaReleaseLast<TypeHandler>();
+    // Now perform a copy if we're on an arena.
+    Arena* arena = GetOwningArena();
+
+    typename TypeHandler::Type* new_result;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+    new_result = copy<TypeHandler>(result);
+    if (arena == nullptr) delete result;
+#else   // PROTOBUF_FORCE_COPY_IN_RELEASE
+    new_result = (arena == nullptr) ? result : copy<TypeHandler>(result);
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+    return new_result;
+  }
+
+  template <typename TypeHandler>
+  typename TypeHandler::Type* ReleaseLastInternal(std::false_type) {
+    // ReleaseLast() for types that *do not* implement merge/copy behavior --
+    // this is the same as UnsafeArenaReleaseLast(). Note that we GOOGLE_DCHECK-fail if
+    // we're on an arena, since the user really should implement the copy
+    // operation in this case.
+    GOOGLE_DCHECK(GetOwningArena() == nullptr)
+        << "ReleaseLast() called on a RepeatedPtrField that is on an arena, "
+        << "with a type that does not implement MergeFrom. This is unsafe; "
+        << "please implement MergeFrom for your type.";
+    return UnsafeArenaReleaseLast<TypeHandler>();
+  }
+
+  template <typename TypeHandler>
+  PROTOBUF_NOINLINE void SwapFallback(RepeatedPtrFieldBase* other) {
+#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    GOOGLE_DCHECK(GetOwningArena() == nullptr ||
+           other->GetOwningArena() != GetOwningArena());
+#else   // PROTOBUF_FORCE_COPY_IN_SWAP
+    GOOGLE_DCHECK(other->GetOwningArena() != GetOwningArena());
+#endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+
+    // Copy semantics in this case. We try to improve efficiency by placing the
+    // temporary on |other|'s arena so that messages are copied twice rather
+    // than three times.
+    RepeatedPtrFieldBase temp(other->GetOwningArena());
+    temp.MergeFrom<TypeHandler>(*this);
+    this->Clear<TypeHandler>();
+    this->MergeFrom<TypeHandler>(*other);
+    other->InternalSwap(&temp);
+    temp.Destroy<TypeHandler>();  // Frees rep_ if `other` had no arena.
+  }
+
+  inline Arena* GetArena() const { return arena_; }
+
+ protected:
+  inline Arena* GetOwningArena() const { return arena_; }
+
+ private:
+  template <typename T> friend class Arena::InternalHelper;
+
+  static constexpr int kInitialSize = 0;
+  // A few notes on internal representation:
+  //
+  // We use an indirected approach, with struct Rep, to keep
+  // sizeof(RepeatedPtrFieldBase) equivalent to what it was before arena support
+  // was added; namely, 3 8-byte machine words on x86-64. An instance of Rep is
+  // allocated only when the repeated field is non-empty, and it is a
+  // dynamically-sized struct (the header is directly followed by elements[]).
+  // We place arena_ and current_size_ directly in the object to avoid cache
+  // misses due to the indirection, because these fields are checked frequently.
+  // Placing all fields directly in the RepeatedPtrFieldBase instance would cost
+  // significant performance for memory-sensitive workloads.
+  Arena* arena_;
+  int current_size_;
+  int total_size_;
+  struct Rep {
+    int allocated_size;
+    // Here we declare a huge array as a way of approximating C's "flexible
+    // array member" feature without relying on undefined behavior.
+    void* elements[(std::numeric_limits<int>::max() - 2 * sizeof(int)) /
+                   sizeof(void*)];
+  };
+  static constexpr size_t kRepHeaderSize = offsetof(Rep, elements);
+  Rep* rep_;
+
+  template <typename TypeHandler>
+  static inline typename TypeHandler::Type* cast(void* element) {
+    return reinterpret_cast<typename TypeHandler::Type*>(element);
+  }
+  template <typename TypeHandler>
+  static inline const typename TypeHandler::Type* cast(const void* element) {
+    return reinterpret_cast<const typename TypeHandler::Type*>(element);
+  }
+
+  // Non-templated inner function to avoid code duplication. Takes a function
+  // pointer to the type-specific (templated) inner allocate/merge loop.
+  void MergeFromInternal(const RepeatedPtrFieldBase& other,
+                         void (RepeatedPtrFieldBase::*inner_loop)(void**,
+                                                                  void**, int,
+                                                                  int)) {
+    // Note: wrapper has already guaranteed that other.rep_ != nullptr here.
+    int other_size = other.current_size_;
+    void** other_elements = other.rep_->elements;
+    void** new_elements = InternalExtend(other_size);
+    int allocated_elems = rep_->allocated_size - current_size_;
+    (this->*inner_loop)(new_elements, other_elements, other_size,
+                        allocated_elems);
+    current_size_ += other_size;
+    if (rep_->allocated_size < current_size_) {
+      rep_->allocated_size = current_size_;
+    }
+  }
+
+  // Merges other_elems to our_elems.
+  template <typename TypeHandler>
+  PROTOBUF_NOINLINE void MergeFromInnerLoop(void** our_elems,
+                                            void** other_elems, int length,
+                                            int already_allocated) {
+    if (already_allocated < length) {
+      Arena* arena = GetOwningArena();
+      typename TypeHandler::Type* elem_prototype =
+          reinterpret_cast<typename TypeHandler::Type*>(other_elems[0]);
+      for (int i = already_allocated; i < length; i++) {
+        // Allocate a new empty element that we'll merge into below
+        typename TypeHandler::Type* new_elem =
+            TypeHandler::NewFromPrototype(elem_prototype, arena);
+        our_elems[i] = new_elem;
+      }
+    }
+    // Main loop that does the actual merging
+    for (int i = 0; i < length; i++) {
+      // Already allocated: use existing element.
+      typename TypeHandler::Type* other_elem =
+          reinterpret_cast<typename TypeHandler::Type*>(other_elems[i]);
+      typename TypeHandler::Type* new_elem =
+          reinterpret_cast<typename TypeHandler::Type*>(our_elems[i]);
+      TypeHandler::Merge(*other_elem, new_elem);
+    }
+  }
+
+  // Internal helper: extends array space if necessary to contain
+  // |extend_amount| more elements, and returns a pointer to the element
+  // immediately following the old list of elements.  This interface factors out
+  // common behavior from Reserve() and MergeFrom() to reduce code size.
+  // |extend_amount| must be > 0.
+  void** InternalExtend(int extend_amount);
+
+  // Internal helper for Add: adds "obj" as the next element in the
+  // array, including potentially resizing the array with Reserve if
+  // needed
+  void* AddOutOfLineHelper(void* obj);
+
+  // The reflection implementation needs to call protected methods directly,
+  // reinterpreting pointers as being to Message instead of a specific Message
+  // subclass.
+  friend class ::PROTOBUF_NAMESPACE_ID::Reflection;
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::SwapFieldHelper;
+
+  // ExtensionSet stores repeated message extensions as
+  // RepeatedPtrField<MessageLite>, but non-lite ExtensionSets need to implement
+  // SpaceUsedLong(), and thus need to call SpaceUsedExcludingSelfLong()
+  // reinterpreting MessageLite as Message.  ExtensionSet also needs to make use
+  // of AddFromCleared(), which is not part of the public interface.
+  friend class ExtensionSet;
+
+  // The MapFieldBase implementation needs to call protected methods directly,
+  // reinterpreting pointers as being to Message instead of a specific Message
+  // subclass.
+  friend class MapFieldBase;
+  friend class MapFieldBaseStub;
+
+  // The table-driven MergePartialFromCodedStream implementation needs to
+  // operate on RepeatedPtrField<MessageLite>.
+  friend class MergePartialFromCodedStreamHelper;
+  friend class AccessorHelper;
+  template <typename T>
+  friend struct google::protobuf::WeakRepeatedPtrField;
+  friend class internal::TcParser;  // TODO(jorg): Remove this friend.
+};
+
+template <typename GenericType>
+class GenericTypeHandler {
+ public:
+  typedef GenericType Type;
+  using Movable = IsMovable<GenericType>;
+
+  static inline GenericType* New(Arena* arena) {
+    return Arena::CreateMaybeMessage<Type>(arena);
+  }
+  static inline GenericType* New(Arena* arena, GenericType&& value) {
+    return Arena::Create<GenericType>(arena, std::move(value));
+  }
+  static inline GenericType* NewFromPrototype(const GenericType* /*prototype*/,
+                                              Arena* arena = nullptr) {
+    return New(arena);
+  }
+  static inline void Delete(GenericType* value, Arena* arena) {
+    if (arena == nullptr) {
+      delete value;
+    }
+  }
+  static inline Arena* GetOwningArena(GenericType* value) {
+    return Arena::GetOwningArena<Type>(value);
+  }
+
+  static inline void Clear(GenericType* value) { value->Clear(); }
+  static void Merge(const GenericType& from, GenericType* to);
+  static inline size_t SpaceUsedLong(const GenericType& value) {
+    return value.SpaceUsedLong();
+  }
+};
+
+// NewFromPrototypeHelper() is not defined inline here, as we will need to do a
+// virtual function dispatch anyways to go from Message* to call New/Merge. (The
+// additional helper is needed as a workaround for MSVC.)
+MessageLite* NewFromPrototypeHelper(const MessageLite* prototype, Arena* arena);
+
+template <>
+inline MessageLite* GenericTypeHandler<MessageLite>::NewFromPrototype(
+    const MessageLite* prototype, Arena* arena) {
+  return NewFromPrototypeHelper(prototype, arena);
+}
+template <>
+inline Arena* GenericTypeHandler<MessageLite>::GetOwningArena(
+    MessageLite* value) {
+  return value->GetOwningArena();
+}
+
+template <typename GenericType>
+PROTOBUF_NOINLINE inline void GenericTypeHandler<GenericType>::Merge(
+    const GenericType& from, GenericType* to) {
+  to->MergeFrom(from);
+}
+template <>
+void GenericTypeHandler<MessageLite>::Merge(const MessageLite& from,
+                                            MessageLite* to);
+
+template <>
+inline void GenericTypeHandler<std::string>::Clear(std::string* value) {
+  value->clear();
+}
+template <>
+void GenericTypeHandler<std::string>::Merge(const std::string& from,
+                                            std::string* to);
+
+// Message specialization bodies defined in message.cc. This split is necessary
+// to allow proto2-lite (which includes this header) to be independent of
+// Message.
+template <>
+PROTOBUF_EXPORT Message* GenericTypeHandler<Message>::NewFromPrototype(
+    const Message* prototype, Arena* arena);
+template <>
+PROTOBUF_EXPORT Arena* GenericTypeHandler<Message>::GetOwningArena(
+    Message* value);
+
+class StringTypeHandler {
+ public:
+  typedef std::string Type;
+  using Movable = IsMovable<Type>;
+
+  static inline std::string* New(Arena* arena) {
+    return Arena::Create<std::string>(arena);
+  }
+  static inline std::string* New(Arena* arena, std::string&& value) {
+    return Arena::Create<std::string>(arena, std::move(value));
+  }
+  static inline std::string* NewFromPrototype(const std::string*,
+                                              Arena* arena) {
+    return New(arena);
+  }
+  static inline Arena* GetOwningArena(std::string*) { return nullptr; }
+  static inline void Delete(std::string* value, Arena* arena) {
+    if (arena == nullptr) {
+      delete value;
+    }
+  }
+  static inline void Clear(std::string* value) { value->clear(); }
+  static inline void Merge(const std::string& from, std::string* to) {
+    *to = from;
+  }
+  static size_t SpaceUsedLong(const std::string& value) {
+    return sizeof(value) + StringSpaceUsedExcludingSelfLong(value);
+  }
+};
+
+}  // namespace internal
+
+// RepeatedPtrField is like RepeatedField, but used for repeated strings or
+// Messages.
+template <typename Element>
+class RepeatedPtrField final : private internal::RepeatedPtrFieldBase {
+
+ public:
+  constexpr RepeatedPtrField();
+  explicit RepeatedPtrField(Arena* arena);
+
+  RepeatedPtrField(const RepeatedPtrField& other);
+
+  template <typename Iter,
+            typename = typename std::enable_if<std::is_constructible<
+                Element, decltype(*std::declval<Iter>())>::value>::type>
+  RepeatedPtrField(Iter begin, Iter end);
+
+  ~RepeatedPtrField();
+
+  RepeatedPtrField& operator=(const RepeatedPtrField& other);
+
+  RepeatedPtrField(RepeatedPtrField&& other) noexcept;
+  RepeatedPtrField& operator=(RepeatedPtrField&& other) noexcept;
+
+  bool empty() const;
+  int size() const;
+
+  const Element& Get(int index) const;
+  Element* Mutable(int index);
+
+  // Unlike std::vector, adding an element to a RepeatedPtrField doesn't always
+  // make a new element; it might re-use an element left over from when the
+  // field was Clear()'d or reize()'d smaller.  For this reason, Add() is the
+  // fastest API for adding a new element.
+  Element* Add();
+
+  // `Add(std::move(value));` is equivalent to `*Add() = std::move(value);`
+  // It will either move-construct to the end of this field, or swap value
+  // with the new-or-recycled element at the end of this field.  Note that
+  // this operation is very slow if this RepeatedPtrField is not on the
+  // same Arena, if any, as `value`.
+  void Add(Element&& value);
+
+  // Copying to the end of this RepeatedPtrField is slowest of all; it can't
+  // reliably copy-construct to the last element of this RepeatedPtrField, for
+  // example (unlike std::vector).
+  // We currently block this API.  The right way to add to the end is to call
+  // Add() and modify the element it points to.
+  // If you must add an existing value, call `*Add() = value;`
+  void Add(const Element& value) = delete;
+
+  // Append elements in the range [begin, end) after reserving
+  // the appropriate number of elements.
+  template <typename Iter>
+  void Add(Iter begin, Iter end);
+
+  const Element& operator[](int index) const { return Get(index); }
+  Element& operator[](int index) { return *Mutable(index); }
+
+  const Element& at(int index) const;
+  Element& at(int index);
+
+  // Removes the last element in the array.
+  // Ownership of the element is retained by the array.
+  void RemoveLast();
+
+  // Deletes elements with indices in the range [start .. start+num-1].
+  // Caution: moves all elements with indices [start+num .. ].
+  // Calling this routine inside a loop can cause quadratic behavior.
+  void DeleteSubrange(int start, int num);
+
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear();
+  void MergeFrom(const RepeatedPtrField& other);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void CopyFrom(const RepeatedPtrField& other);
+
+  // Replaces the contents with RepeatedPtrField(begin, end).
+  template <typename Iter>
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Assign(Iter begin, Iter end);
+
+  // Reserves space to expand the field to at least the given size.  This only
+  // resizes the pointer array; it doesn't allocate any objects.  If the
+  // array is grown, it will always be at least doubled in size.
+  void Reserve(int new_size);
+
+  int Capacity() const;
+
+  // Gets the underlying array.  This pointer is possibly invalidated by
+  // any add or remove operation.
+  //
+  // This API is deprecated. Instead of directly working with element array,
+  // use APIs in repeated_field_util.h; e.g. sorting, etc.
+  PROTOBUF_DEPRECATED_MSG("Use APIs in repeated_field_util.h")
+  Element** mutable_data();
+  const Element* const* data() const;
+
+  // Swaps entire contents with "other". If they are on separate arenas, then
+  // copies data.
+  void Swap(RepeatedPtrField* other);
+
+  // Swaps entire contents with "other". Caller should guarantee that either
+  // both fields are on the same arena or both are on the heap. Swapping between
+  // different arenas with this function is disallowed and is caught via
+  // GOOGLE_DCHECK.
+  void UnsafeArenaSwap(RepeatedPtrField* other);
+
+  // Swaps two elements.
+  void SwapElements(int index1, int index2);
+
+  // STL-like iterator support
+  typedef internal::RepeatedPtrIterator<Element> iterator;
+  typedef internal::RepeatedPtrIterator<const Element> const_iterator;
+  typedef Element value_type;
+  typedef value_type& reference;
+  typedef const value_type& const_reference;
+  typedef value_type* pointer;
+  typedef const value_type* const_pointer;
+  typedef int size_type;
+  typedef ptrdiff_t difference_type;
+
+  iterator begin();
+  const_iterator begin() const;
+  const_iterator cbegin() const;
+  iterator end();
+  const_iterator end() const;
+  const_iterator cend() const;
+
+  // Reverse iterator support
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+  typedef std::reverse_iterator<iterator> reverse_iterator;
+  reverse_iterator rbegin() { return reverse_iterator(end()); }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(end());
+  }
+  reverse_iterator rend() { return reverse_iterator(begin()); }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(begin());
+  }
+
+  // Custom STL-like iterator that iterates over and returns the underlying
+  // pointers to Element rather than Element itself.
+  typedef internal::RepeatedPtrOverPtrsIterator<Element*, void*>
+      pointer_iterator;
+  typedef internal::RepeatedPtrOverPtrsIterator<const Element* const,
+                                                const void* const>
+      const_pointer_iterator;
+  pointer_iterator pointer_begin();
+  const_pointer_iterator pointer_begin() const;
+  pointer_iterator pointer_end();
+  const_pointer_iterator pointer_end() const;
+
+  // Returns (an estimate of) the number of bytes used by the repeated field,
+  // excluding sizeof(*this).
+  size_t SpaceUsedExcludingSelfLong() const;
+
+  int SpaceUsedExcludingSelf() const {
+    return internal::ToIntSize(SpaceUsedExcludingSelfLong());
+  }
+
+  // Advanced memory management --------------------------------------
+  // When hardcore memory management becomes necessary -- as it sometimes
+  // does here at Google -- the following methods may be useful.
+
+  // Adds an already-allocated object, passing ownership to the
+  // RepeatedPtrField.
+  //
+  // Note that some special behavior occurs with respect to arenas:
+  //
+  //   (i) if this field holds submessages, the new submessage will be copied if
+  //   the original is in an arena and this RepeatedPtrField is either in a
+  //   different arena, or on the heap.
+  //   (ii) if this field holds strings, the passed-in string *must* be
+  //   heap-allocated, not arena-allocated. There is no way to dynamically check
+  //   this at runtime, so User Beware.
+  void AddAllocated(Element* value);
+
+  // Removes and returns the last element, passing ownership to the caller.
+  // Requires:  size() > 0
+  //
+  // If this RepeatedPtrField is on an arena, an object copy is required to pass
+  // ownership back to the user (for compatible semantics). Use
+  // UnsafeArenaReleaseLast() if this behavior is undesired.
+  PROTOBUF_NODISCARD Element* ReleaseLast();
+
+  // Adds an already-allocated object, skipping arena-ownership checks. The user
+  // must guarantee that the given object is in the same arena as this
+  // RepeatedPtrField.
+  // It is also useful in legacy code that uses temporary ownership to avoid
+  // copies. Example:
+  //   RepeatedPtrField<T> temp_field;
+  //   temp_field.UnsafeArenaAddAllocated(new T);
+  //   ... // Do something with temp_field
+  //   temp_field.UnsafeArenaExtractSubrange(0, temp_field.size(), nullptr);
+  // If you put temp_field on the arena this fails, because the ownership
+  // transfers to the arena at the "AddAllocated" call and is not released
+  // anymore, causing a double delete. UnsafeArenaAddAllocated prevents this.
+  void UnsafeArenaAddAllocated(Element* value);
+
+  // Removes and returns the last element.  Unlike ReleaseLast, the returned
+  // pointer is always to the original object.  This may be in an arena, in
+  // which case it would have the arena's lifetime.
+  // Requires: current_size_ > 0
+  Element* UnsafeArenaReleaseLast();
+
+  // Extracts elements with indices in the range "[start .. start+num-1]".
+  // The caller assumes ownership of the extracted elements and is responsible
+  // for deleting them when they are no longer needed.
+  // If "elements" is non-nullptr, then pointers to the extracted elements
+  // are stored in "elements[0 .. num-1]" for the convenience of the caller.
+  // If "elements" is nullptr, then the caller must use some other mechanism
+  // to perform any further operations (like deletion) on these elements.
+  // Caution: implementation also moves elements with indices [start+num ..].
+  // Calling this routine inside a loop can cause quadratic behavior.
+  //
+  // Memory copying behavior is identical to ReleaseLast(), described above: if
+  // this RepeatedPtrField is on an arena, an object copy is performed for each
+  // returned element, so that all returned element pointers are to
+  // heap-allocated copies. If this copy is not desired, the user should call
+  // UnsafeArenaExtractSubrange().
+  void ExtractSubrange(int start, int num, Element** elements);
+
+  // Identical to ExtractSubrange() described above, except that no object
+  // copies are ever performed. Instead, the raw object pointers are returned.
+  // Thus, if on an arena, the returned objects must not be freed, because they
+  // will not be heap-allocated objects.
+  void UnsafeArenaExtractSubrange(int start, int num, Element** elements);
+
+  // When elements are removed by calls to RemoveLast() or Clear(), they
+  // are not actually freed.  Instead, they are cleared and kept so that
+  // they can be reused later.  This can save lots of CPU time when
+  // repeatedly reusing a protocol message for similar purposes.
+  //
+  // Hardcore programs may choose to manipulate these cleared objects
+  // to better optimize memory management using the following routines.
+
+  // Gets the number of cleared objects that are currently being kept
+  // around for reuse.
+  int ClearedCount() const;
+#ifndef PROTOBUF_FUTURE_BREAKING_CHANGES
+  // Adds an element to the pool of cleared objects, passing ownership to
+  // the RepeatedPtrField.  The element must be cleared prior to calling
+  // this method.
+  //
+  // This method cannot be called when either the repeated field or |value| is
+  // on an arena; both cases will trigger a GOOGLE_DCHECK-failure.
+  void AddCleared(Element* value);
+  // Removes and returns a single element from the cleared pool, passing
+  // ownership to the caller.  The element is guaranteed to be cleared.
+  // Requires:  ClearedCount() > 0
+  //
+  // This method cannot be called when the repeated field is on an arena; doing
+  // so will trigger a GOOGLE_DCHECK-failure.
+  PROTOBUF_NODISCARD Element* ReleaseCleared();
+#endif  // !PROTOBUF_FUTURE_BREAKING_CHANGES
+
+  // Removes the element referenced by position.
+  //
+  // Returns an iterator to the element immediately following the removed
+  // element.
+  //
+  // Invalidates all iterators at or after the removed element, including end().
+  iterator erase(const_iterator position);
+
+  // Removes the elements in the range [first, last).
+  //
+  // Returns an iterator to the element immediately following the removed range.
+  //
+  // Invalidates all iterators at or after the removed range, including end().
+  iterator erase(const_iterator first, const_iterator last);
+
+  // Gets the arena on which this RepeatedPtrField stores its elements.
+  inline Arena* GetArena() const;
+
+  // For internal use only.
+  //
+  // This is public due to it being called by generated code.
+  void InternalSwap(RepeatedPtrField* other) {
+    internal::RepeatedPtrFieldBase::InternalSwap(other);
+  }
+
+ private:
+  // Note:  RepeatedPtrField SHOULD NOT be subclassed by users.
+  class TypeHandler;
+
+  // Internal version of GetArena().
+  inline Arena* GetOwningArena() const;
+
+  // Implementations for ExtractSubrange(). The copying behavior must be
+  // included only if the type supports the necessary operations (e.g.,
+  // MergeFrom()), so we must resolve this at compile time. ExtractSubrange()
+  // uses SFINAE to choose one of the below implementations.
+  void ExtractSubrangeInternal(int start, int num, Element** elements,
+                               std::true_type);
+  void ExtractSubrangeInternal(int start, int num, Element** elements,
+                               std::false_type);
+
+  friend class Arena;
+
+  template <typename T>
+  friend struct WeakRepeatedPtrField;
+
+  typedef void InternalArenaConstructable_;
+
+};
+
+// -------------------------------------------------------------------
+
+template <typename Element>
+class RepeatedPtrField<Element>::TypeHandler
+    : public internal::GenericTypeHandler<Element> {};
+
+template <>
+class RepeatedPtrField<std::string>::TypeHandler
+    : public internal::StringTypeHandler {};
+
+template <typename Element>
+constexpr RepeatedPtrField<Element>::RepeatedPtrField()
+    : RepeatedPtrFieldBase() {}
+
+template <typename Element>
+inline RepeatedPtrField<Element>::RepeatedPtrField(Arena* arena)
+    : RepeatedPtrFieldBase(arena) {}
+
+template <typename Element>
+inline RepeatedPtrField<Element>::RepeatedPtrField(
+    const RepeatedPtrField& other)
+    : RepeatedPtrFieldBase() {
+  MergeFrom(other);
+}
+
+template <typename Element>
+template <typename Iter, typename>
+inline RepeatedPtrField<Element>::RepeatedPtrField(Iter begin, Iter end) {
+  Add(begin, end);
+}
+
+template <typename Element>
+RepeatedPtrField<Element>::~RepeatedPtrField() {
+#ifdef __cpp_if_constexpr
+  if constexpr (std::is_base_of<MessageLite, Element>::value) {
+#else
+  if (std::is_base_of<MessageLite, Element>::value) {
+#endif
+    if (NeedsDestroy()) DestroyProtos();
+  } else {
+    Destroy<TypeHandler>();
+  }
+}
+
+template <typename Element>
+inline RepeatedPtrField<Element>& RepeatedPtrField<Element>::operator=(
+    const RepeatedPtrField& other) {
+  if (this != &other) CopyFrom(other);
+  return *this;
+}
+
+template <typename Element>
+inline RepeatedPtrField<Element>::RepeatedPtrField(
+    RepeatedPtrField&& other) noexcept
+    : RepeatedPtrField() {
+#ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+  CopyFrom(other);
+#else   // PROTOBUF_FORCE_COPY_IN_MOVE
+  // We don't just call Swap(&other) here because it would perform 3 copies if
+  // other is on an arena. This field can't be on an arena because arena
+  // construction always uses the Arena* accepting constructor.
+  if (other.GetOwningArena()) {
+    CopyFrom(other);
+  } else {
+    InternalSwap(&other);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+}
+
+template <typename Element>
+inline RepeatedPtrField<Element>& RepeatedPtrField<Element>::operator=(
+    RepeatedPtrField&& other) noexcept {
+  // We don't just call Swap(&other) here because it would perform 3 copies if
+  // the two fields are on different arenas.
+  if (this != &other) {
+    if (GetOwningArena() != other.GetOwningArena()
+#ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        || GetOwningArena() == nullptr
+#endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      CopyFrom(other);
+    } else {
+      InternalSwap(&other);
+    }
+  }
+  return *this;
+}
+
+template <typename Element>
+inline bool RepeatedPtrField<Element>::empty() const {
+  return RepeatedPtrFieldBase::empty();
+}
+
+template <typename Element>
+inline int RepeatedPtrField<Element>::size() const {
+  return RepeatedPtrFieldBase::size();
+}
+
+template <typename Element>
+inline const Element& RepeatedPtrField<Element>::Get(int index) const {
+  return RepeatedPtrFieldBase::Get<TypeHandler>(index);
+}
+
+template <typename Element>
+inline const Element& RepeatedPtrField<Element>::at(int index) const {
+  return RepeatedPtrFieldBase::at<TypeHandler>(index);
+}
+
+template <typename Element>
+inline Element& RepeatedPtrField<Element>::at(int index) {
+  return RepeatedPtrFieldBase::at<TypeHandler>(index);
+}
+
+
+template <typename Element>
+inline Element* RepeatedPtrField<Element>::Mutable(int index) {
+  return RepeatedPtrFieldBase::Mutable<TypeHandler>(index);
+}
+
+template <typename Element>
+inline Element* RepeatedPtrField<Element>::Add() {
+  return RepeatedPtrFieldBase::Add<TypeHandler>();
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::Add(Element&& value) {
+  RepeatedPtrFieldBase::Add<TypeHandler>(std::move(value));
+}
+
+template <typename Element>
+template <typename Iter>
+inline void RepeatedPtrField<Element>::Add(Iter begin, Iter end) {
+  if (std::is_base_of<
+          std::forward_iterator_tag,
+          typename std::iterator_traits<Iter>::iterator_category>::value) {
+    int reserve = std::distance(begin, end);
+    Reserve(size() + reserve);
+  }
+  for (; begin != end; ++begin) {
+    *Add() = *begin;
+  }
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::RemoveLast() {
+  RepeatedPtrFieldBase::RemoveLast<TypeHandler>();
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::DeleteSubrange(int start, int num) {
+  GOOGLE_DCHECK_GE(start, 0);
+  GOOGLE_DCHECK_GE(num, 0);
+  GOOGLE_DCHECK_LE(start + num, size());
+  for (int i = 0; i < num; ++i) {
+    RepeatedPtrFieldBase::Delete<TypeHandler>(start + i);
+  }
+  UnsafeArenaExtractSubrange(start, num, nullptr);
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::ExtractSubrange(int start, int num,
+                                                       Element** elements) {
+  typename internal::TypeImplementsMergeBehavior<
+      typename TypeHandler::Type>::type t;
+  ExtractSubrangeInternal(start, num, elements, t);
+}
+
+// ExtractSubrange() implementation for types that implement merge/copy
+// behavior.
+template <typename Element>
+inline void RepeatedPtrField<Element>::ExtractSubrangeInternal(
+    int start, int num, Element** elements, std::true_type) {
+  GOOGLE_DCHECK_GE(start, 0);
+  GOOGLE_DCHECK_GE(num, 0);
+  GOOGLE_DCHECK_LE(start + num, size());
+
+  if (num == 0) return;
+
+  GOOGLE_DCHECK_NE(elements, nullptr)
+      << "Releasing elements without transferring ownership is an unsafe "
+         "operation.  Use UnsafeArenaExtractSubrange.";
+  if (elements == nullptr) {
+    CloseGap(start, num);
+    return;
+  }
+
+  Arena* arena = GetOwningArena();
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  // Always copy.
+  for (int i = 0; i < num; ++i) {
+    elements[i] = copy<TypeHandler>(
+        RepeatedPtrFieldBase::Mutable<TypeHandler>(i + start));
+  }
+  if (arena == nullptr) {
+    for (int i = 0; i < num; ++i) {
+      delete RepeatedPtrFieldBase::Mutable<TypeHandler>(i + start);
+    }
+  }
+#else   // PROTOBUF_FORCE_COPY_IN_RELEASE
+  // If we're on an arena, we perform a copy for each element so that the
+  // returned elements are heap-allocated. Otherwise, just forward it.
+  if (arena != nullptr) {
+    for (int i = 0; i < num; ++i) {
+      elements[i] = copy<TypeHandler>(
+          RepeatedPtrFieldBase::Mutable<TypeHandler>(i + start));
+    }
+  } else {
+    for (int i = 0; i < num; ++i) {
+      elements[i] = RepeatedPtrFieldBase::Mutable<TypeHandler>(i + start);
+    }
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  CloseGap(start, num);
+}
+
+// ExtractSubrange() implementation for types that do not implement merge/copy
+// behavior.
+template <typename Element>
+inline void RepeatedPtrField<Element>::ExtractSubrangeInternal(
+    int start, int num, Element** elements, std::false_type) {
+  // This case is identical to UnsafeArenaExtractSubrange(). However, since
+  // ExtractSubrange() must return heap-allocated objects by contract, and we
+  // cannot fulfill this contract if we are an on arena, we must GOOGLE_DCHECK() that
+  // we are not on an arena.
+  GOOGLE_DCHECK(GetOwningArena() == nullptr)
+      << "ExtractSubrange() when arena is non-nullptr is only supported when "
+      << "the Element type supplies a MergeFrom() operation to make copies.";
+  UnsafeArenaExtractSubrange(start, num, elements);
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::UnsafeArenaExtractSubrange(
+    int start, int num, Element** elements) {
+  GOOGLE_DCHECK_GE(start, 0);
+  GOOGLE_DCHECK_GE(num, 0);
+  GOOGLE_DCHECK_LE(start + num, size());
+
+  if (num > 0) {
+    // Save the values of the removed elements if requested.
+    if (elements != nullptr) {
+      for (int i = 0; i < num; ++i) {
+        elements[i] = RepeatedPtrFieldBase::Mutable<TypeHandler>(i + start);
+      }
+    }
+    CloseGap(start, num);
+  }
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::Clear() {
+  RepeatedPtrFieldBase::Clear<TypeHandler>();
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::MergeFrom(
+    const RepeatedPtrField& other) {
+  RepeatedPtrFieldBase::MergeFrom<TypeHandler>(other);
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::CopyFrom(const RepeatedPtrField& other) {
+  RepeatedPtrFieldBase::CopyFrom<TypeHandler>(other);
+}
+
+template <typename Element>
+template <typename Iter>
+inline void RepeatedPtrField<Element>::Assign(Iter begin, Iter end) {
+  Clear();
+  Add(begin, end);
+}
+
+template <typename Element>
+inline typename RepeatedPtrField<Element>::iterator
+RepeatedPtrField<Element>::erase(const_iterator position) {
+  return erase(position, position + 1);
+}
+
+template <typename Element>
+inline typename RepeatedPtrField<Element>::iterator
+RepeatedPtrField<Element>::erase(const_iterator first, const_iterator last) {
+  size_type pos_offset = std::distance(cbegin(), first);
+  size_type last_offset = std::distance(cbegin(), last);
+  DeleteSubrange(pos_offset, last_offset - pos_offset);
+  return begin() + pos_offset;
+}
+
+template <typename Element>
+inline Element** RepeatedPtrField<Element>::mutable_data() {
+  return RepeatedPtrFieldBase::mutable_data<TypeHandler>();
+}
+
+template <typename Element>
+inline const Element* const* RepeatedPtrField<Element>::data() const {
+  return RepeatedPtrFieldBase::data<TypeHandler>();
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::Swap(RepeatedPtrField* other) {
+  if (this == other) return;
+  RepeatedPtrFieldBase::Swap<TypeHandler>(other);
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::UnsafeArenaSwap(
+    RepeatedPtrField* other) {
+  if (this == other) return;
+  GOOGLE_DCHECK_EQ(GetOwningArena(), other->GetOwningArena());
+  RepeatedPtrFieldBase::InternalSwap(other);
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::SwapElements(int index1, int index2) {
+  RepeatedPtrFieldBase::SwapElements(index1, index2);
+}
+
+template <typename Element>
+inline Arena* RepeatedPtrField<Element>::GetArena() const {
+  return RepeatedPtrFieldBase::GetArena();
+}
+
+template <typename Element>
+inline Arena* RepeatedPtrField<Element>::GetOwningArena() const {
+  return RepeatedPtrFieldBase::GetOwningArena();
+}
+
+template <typename Element>
+inline size_t RepeatedPtrField<Element>::SpaceUsedExcludingSelfLong() const {
+  return RepeatedPtrFieldBase::SpaceUsedExcludingSelfLong<TypeHandler>();
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::AddAllocated(Element* value) {
+  RepeatedPtrFieldBase::AddAllocated<TypeHandler>(value);
+}
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::UnsafeArenaAddAllocated(Element* value) {
+  RepeatedPtrFieldBase::UnsafeArenaAddAllocated<TypeHandler>(value);
+}
+
+template <typename Element>
+inline Element* RepeatedPtrField<Element>::ReleaseLast() {
+  return RepeatedPtrFieldBase::ReleaseLast<TypeHandler>();
+}
+
+template <typename Element>
+inline Element* RepeatedPtrField<Element>::UnsafeArenaReleaseLast() {
+  return RepeatedPtrFieldBase::UnsafeArenaReleaseLast<TypeHandler>();
+}
+
+template <typename Element>
+inline int RepeatedPtrField<Element>::ClearedCount() const {
+  return RepeatedPtrFieldBase::ClearedCount();
+}
+
+#ifndef PROTOBUF_FUTURE_BREAKING_CHANGES
+template <typename Element>
+inline void RepeatedPtrField<Element>::AddCleared(Element* value) {
+  return RepeatedPtrFieldBase::AddCleared<TypeHandler>(value);
+}
+
+template <typename Element>
+inline Element* RepeatedPtrField<Element>::ReleaseCleared() {
+  return RepeatedPtrFieldBase::ReleaseCleared<TypeHandler>();
+}
+#endif  // !PROTOBUF_FUTURE_BREAKING_CHANGES
+
+template <typename Element>
+inline void RepeatedPtrField<Element>::Reserve(int new_size) {
+  return RepeatedPtrFieldBase::Reserve(new_size);
+}
+
+template <typename Element>
+inline int RepeatedPtrField<Element>::Capacity() const {
+  return RepeatedPtrFieldBase::Capacity();
+}
+
+// -------------------------------------------------------------------
+
+namespace internal {
+
+// STL-like iterator implementation for RepeatedPtrField.  You should not
+// refer to this class directly; use RepeatedPtrField<T>::iterator instead.
+//
+// The iterator for RepeatedPtrField<T>, RepeatedPtrIterator<T>, is
+// very similar to iterator_ptr<T**> in util/gtl/iterator_adaptors.h,
+// but adds random-access operators and is modified to wrap a void** base
+// iterator (since RepeatedPtrField stores its array as a void* array and
+// casting void** to T** would violate C++ aliasing rules).
+//
+// This code based on net/proto/proto-array-internal.h by Jeffrey Yasskin
+// (jyasskin@google.com).
+template <typename Element>
+class RepeatedPtrIterator {
+ public:
+  using iterator = RepeatedPtrIterator<Element>;
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = typename std::remove_const<Element>::type;
+  using difference_type = std::ptrdiff_t;
+  using pointer = Element*;
+  using reference = Element&;
+
+  RepeatedPtrIterator() : it_(nullptr) {}
+  explicit RepeatedPtrIterator(void* const* it) : it_(it) {}
+
+  // Allows "upcasting" from RepeatedPtrIterator<T**> to
+  // RepeatedPtrIterator<const T*const*>.
+  template <typename OtherElement,
+            typename std::enable_if<std::is_convertible<
+                OtherElement*, pointer>::value>::type* = nullptr>
+  RepeatedPtrIterator(const RepeatedPtrIterator<OtherElement>& other)
+      : it_(other.it_) {}
+
+  // dereferenceable
+  reference operator*() const { return *reinterpret_cast<Element*>(*it_); }
+  pointer operator->() const { return &(operator*()); }
+
+  // {inc,dec}rementable
+  iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  iterator operator++(int) { return iterator(it_++); }
+  iterator& operator--() {
+    --it_;
+    return *this;
+  }
+  iterator operator--(int) { return iterator(it_--); }
+
+  // equality_comparable
+  friend bool operator==(const iterator& x, const iterator& y) {
+    return x.it_ == y.it_;
+  }
+  friend bool operator!=(const iterator& x, const iterator& y) {
+    return x.it_ != y.it_;
+  }
+
+  // less_than_comparable
+  friend bool operator<(const iterator& x, const iterator& y) {
+    return x.it_ < y.it_;
+  }
+  friend bool operator<=(const iterator& x, const iterator& y) {
+    return x.it_ <= y.it_;
+  }
+  friend bool operator>(const iterator& x, const iterator& y) {
+    return x.it_ > y.it_;
+  }
+  friend bool operator>=(const iterator& x, const iterator& y) {
+    return x.it_ >= y.it_;
+  }
+
+  // addable, subtractable
+  iterator& operator+=(difference_type d) {
+    it_ += d;
+    return *this;
+  }
+  friend iterator operator+(iterator it, const difference_type d) {
+    it += d;
+    return it;
+  }
+  friend iterator operator+(const difference_type d, iterator it) {
+    it += d;
+    return it;
+  }
+  iterator& operator-=(difference_type d) {
+    it_ -= d;
+    return *this;
+  }
+  friend iterator operator-(iterator it, difference_type d) {
+    it -= d;
+    return it;
+  }
+
+  // indexable
+  reference operator[](difference_type d) const { return *(*this + d); }
+
+  // random access iterator
+  friend difference_type operator-(iterator it1, iterator it2) {
+    return it1.it_ - it2.it_;
+  }
+
+ private:
+  template <typename OtherElement>
+  friend class RepeatedPtrIterator;
+
+  // The internal iterator.
+  void* const* it_;
+};
+
+// Provides an iterator that operates on pointers to the underlying objects
+// rather than the objects themselves as RepeatedPtrIterator does.
+// Consider using this when working with stl algorithms that change
+// the array.
+// The VoidPtr template parameter holds the type-agnostic pointer value
+// referenced by the iterator.  It should either be "void *" for a mutable
+// iterator, or "const void* const" for a constant iterator.
+template <typename Element, typename VoidPtr>
+class RepeatedPtrOverPtrsIterator {
+ public:
+  using iterator = RepeatedPtrOverPtrsIterator<Element, VoidPtr>;
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = typename std::remove_const<Element>::type;
+  using difference_type = std::ptrdiff_t;
+  using pointer = Element*;
+  using reference = Element&;
+
+  RepeatedPtrOverPtrsIterator() : it_(nullptr) {}
+  explicit RepeatedPtrOverPtrsIterator(VoidPtr* it) : it_(it) {}
+
+  // Allows "upcasting" from RepeatedPtrOverPtrsIterator<T**> to
+  // RepeatedPtrOverPtrsIterator<const T*const*>.
+  template <
+      typename OtherElement, typename OtherVoidPtr,
+      typename std::enable_if<
+          std::is_convertible<OtherElement*, pointer>::value &&
+          std::is_convertible<OtherVoidPtr*, VoidPtr>::value>::type* = nullptr>
+  RepeatedPtrOverPtrsIterator(
+      const RepeatedPtrOverPtrsIterator<OtherElement, OtherVoidPtr>& other)
+      : it_(other.it_) {}
+
+  // dereferenceable
+  reference operator*() const { return *reinterpret_cast<Element*>(it_); }
+  pointer operator->() const { return &(operator*()); }
+
+  // {inc,dec}rementable
+  iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  iterator operator++(int) { return iterator(it_++); }
+  iterator& operator--() {
+    --it_;
+    return *this;
+  }
+  iterator operator--(int) { return iterator(it_--); }
+
+  // equality_comparable
+  friend bool operator==(const iterator& x, const iterator& y) {
+    return x.it_ == y.it_;
+  }
+  friend bool operator!=(const iterator& x, const iterator& y) {
+    return x.it_ != y.it_;
+  }
+
+  // less_than_comparable
+  friend bool operator<(const iterator& x, const iterator& y) {
+    return x.it_ < y.it_;
+  }
+  friend bool operator<=(const iterator& x, const iterator& y) {
+    return x.it_ <= y.it_;
+  }
+  friend bool operator>(const iterator& x, const iterator& y) {
+    return x.it_ > y.it_;
+  }
+  friend bool operator>=(const iterator& x, const iterator& y) {
+    return x.it_ >= y.it_;
+  }
+
+  // addable, subtractable
+  iterator& operator+=(difference_type d) {
+    it_ += d;
+    return *this;
+  }
+  friend iterator operator+(iterator it, difference_type d) {
+    it += d;
+    return it;
+  }
+  friend iterator operator+(difference_type d, iterator it) {
+    it += d;
+    return it;
+  }
+  iterator& operator-=(difference_type d) {
+    it_ -= d;
+    return *this;
+  }
+  friend iterator operator-(iterator it, difference_type d) {
+    it -= d;
+    return it;
+  }
+
+  // indexable
+  reference operator[](difference_type d) const { return *(*this + d); }
+
+  // random access iterator
+  friend difference_type operator-(iterator it1, iterator it2) {
+    return it1.it_ - it2.it_;
+  }
+
+ private:
+  template <typename OtherElement, typename OtherVoidPtr>
+  friend class RepeatedPtrOverPtrsIterator;
+
+  // The internal iterator.
+  VoidPtr* it_;
+};
+
+}  // namespace internal
+
+template <typename Element>
+inline typename RepeatedPtrField<Element>::iterator
+RepeatedPtrField<Element>::begin() {
+  return iterator(raw_data());
+}
+template <typename Element>
+inline typename RepeatedPtrField<Element>::const_iterator
+RepeatedPtrField<Element>::begin() const {
+  return iterator(raw_data());
+}
+template <typename Element>
+inline typename RepeatedPtrField<Element>::const_iterator
+RepeatedPtrField<Element>::cbegin() const {
+  return begin();
+}
+template <typename Element>
+inline typename RepeatedPtrField<Element>::iterator
+RepeatedPtrField<Element>::end() {
+  return iterator(raw_data() + size());
+}
+template <typename Element>
+inline typename RepeatedPtrField<Element>::const_iterator
+RepeatedPtrField<Element>::end() const {
+  return iterator(raw_data() + size());
+}
+template <typename Element>
+inline typename RepeatedPtrField<Element>::const_iterator
+RepeatedPtrField<Element>::cend() const {
+  return end();
+}
+
+template <typename Element>
+inline typename RepeatedPtrField<Element>::pointer_iterator
+RepeatedPtrField<Element>::pointer_begin() {
+  return pointer_iterator(raw_mutable_data());
+}
+template <typename Element>
+inline typename RepeatedPtrField<Element>::const_pointer_iterator
+RepeatedPtrField<Element>::pointer_begin() const {
+  return const_pointer_iterator(const_cast<const void* const*>(raw_data()));
+}
+template <typename Element>
+inline typename RepeatedPtrField<Element>::pointer_iterator
+RepeatedPtrField<Element>::pointer_end() {
+  return pointer_iterator(raw_mutable_data() + size());
+}
+template <typename Element>
+inline typename RepeatedPtrField<Element>::const_pointer_iterator
+RepeatedPtrField<Element>::pointer_end() const {
+  return const_pointer_iterator(
+      const_cast<const void* const*>(raw_data() + size()));
+}
+
+// Iterators and helper functions that follow the spirit of the STL
+// std::back_insert_iterator and std::back_inserter but are tailor-made
+// for RepeatedField and RepeatedPtrField. Typical usage would be:
+//
+//   std::copy(some_sequence.begin(), some_sequence.end(),
+//             RepeatedFieldBackInserter(proto.mutable_sequence()));
+//
+// Ported by johannes from util/gtl/proto-array-iterators.h
+
+namespace internal {
+
+// A back inserter for RepeatedPtrField objects.
+template <typename T>
+class RepeatedPtrFieldBackInsertIterator {
+ public:
+  using iterator_category = std::output_iterator_tag;
+  using value_type = T;
+  using pointer = void;
+  using reference = void;
+  using difference_type = std::ptrdiff_t;
+
+  RepeatedPtrFieldBackInsertIterator(RepeatedPtrField<T>* const mutable_field)
+      : field_(mutable_field) {}
+  RepeatedPtrFieldBackInsertIterator<T>& operator=(const T& value) {
+    *field_->Add() = value;
+    return *this;
+  }
+  RepeatedPtrFieldBackInsertIterator<T>& operator=(
+      const T* const ptr_to_value) {
+    *field_->Add() = *ptr_to_value;
+    return *this;
+  }
+  RepeatedPtrFieldBackInsertIterator<T>& operator=(T&& value) {
+    *field_->Add() = std::move(value);
+    return *this;
+  }
+  RepeatedPtrFieldBackInsertIterator<T>& operator*() { return *this; }
+  RepeatedPtrFieldBackInsertIterator<T>& operator++() { return *this; }
+  RepeatedPtrFieldBackInsertIterator<T>& operator++(int /* unused */) {
+    return *this;
+  }
+
+ private:
+  RepeatedPtrField<T>* field_;
+};
+
+// A back inserter for RepeatedPtrFields that inserts by transferring ownership
+// of a pointer.
+template <typename T>
+class AllocatedRepeatedPtrFieldBackInsertIterator {
+ public:
+  using iterator_category = std::output_iterator_tag;
+  using value_type = T;
+  using pointer = void;
+  using reference = void;
+  using difference_type = std::ptrdiff_t;
+
+  explicit AllocatedRepeatedPtrFieldBackInsertIterator(
+      RepeatedPtrField<T>* const mutable_field)
+      : field_(mutable_field) {}
+  AllocatedRepeatedPtrFieldBackInsertIterator<T>& operator=(
+      T* const ptr_to_value) {
+    field_->AddAllocated(ptr_to_value);
+    return *this;
+  }
+  AllocatedRepeatedPtrFieldBackInsertIterator<T>& operator*() { return *this; }
+  AllocatedRepeatedPtrFieldBackInsertIterator<T>& operator++() { return *this; }
+  AllocatedRepeatedPtrFieldBackInsertIterator<T>& operator++(int /* unused */) {
+    return *this;
+  }
+
+ private:
+  RepeatedPtrField<T>* field_;
+};
+
+// Almost identical to AllocatedRepeatedPtrFieldBackInsertIterator. This one
+// uses the UnsafeArenaAddAllocated instead.
+template <typename T>
+class UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator {
+ public:
+  using iterator_category = std::output_iterator_tag;
+  using value_type = T;
+  using pointer = void;
+  using reference = void;
+  using difference_type = std::ptrdiff_t;
+
+  explicit UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator(
+      RepeatedPtrField<T>* const mutable_field)
+      : field_(mutable_field) {}
+  UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator<T>& operator=(
+      T const* const ptr_to_value) {
+    field_->UnsafeArenaAddAllocated(const_cast<T*>(ptr_to_value));
+    return *this;
+  }
+  UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator<T>& operator*() {
+    return *this;
+  }
+  UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator<T>& operator++() {
+    return *this;
+  }
+  UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator<T>& operator++(
+      int /* unused */) {
+    return *this;
+  }
+
+ private:
+  RepeatedPtrField<T>* field_;
+};
+
+}  // namespace internal
+
+// Provides a back insert iterator for RepeatedPtrField instances,
+// similar to std::back_inserter().
+template <typename T>
+internal::RepeatedPtrFieldBackInsertIterator<T> RepeatedPtrFieldBackInserter(
+    RepeatedPtrField<T>* const mutable_field) {
+  return internal::RepeatedPtrFieldBackInsertIterator<T>(mutable_field);
+}
+
+// Special back insert iterator for RepeatedPtrField instances, just in
+// case someone wants to write generic template code that can access both
+// RepeatedFields and RepeatedPtrFields using a common name.
+template <typename T>
+internal::RepeatedPtrFieldBackInsertIterator<T> RepeatedFieldBackInserter(
+    RepeatedPtrField<T>* const mutable_field) {
+  return internal::RepeatedPtrFieldBackInsertIterator<T>(mutable_field);
+}
+
+// Provides a back insert iterator for RepeatedPtrField instances
+// similar to std::back_inserter() which transfers the ownership while
+// copying elements.
+template <typename T>
+internal::AllocatedRepeatedPtrFieldBackInsertIterator<T>
+AllocatedRepeatedPtrFieldBackInserter(
+    RepeatedPtrField<T>* const mutable_field) {
+  return internal::AllocatedRepeatedPtrFieldBackInsertIterator<T>(
+      mutable_field);
+}
+
+// Similar to AllocatedRepeatedPtrFieldBackInserter, using
+// UnsafeArenaAddAllocated instead of AddAllocated.
+// This is slightly faster if that matters. It is also useful in legacy code
+// that uses temporary ownership to avoid copies. Example:
+//   RepeatedPtrField<T> temp_field;
+//   temp_field.UnsafeArenaAddAllocated(new T);
+//   ... // Do something with temp_field
+//   temp_field.UnsafeArenaExtractSubrange(0, temp_field.size(), nullptr);
+// Putting temp_field on the arena fails because the ownership transfers to the
+// arena at the "AddAllocated" call and is not released anymore causing a
+// double delete. This function uses UnsafeArenaAddAllocated to prevent this.
+template <typename T>
+internal::UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator<T>
+UnsafeArenaAllocatedRepeatedPtrFieldBackInserter(
+    RepeatedPtrField<T>* const mutable_field) {
+  return internal::UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator<T>(
+      mutable_field);
+}
+
+extern template class PROTOBUF_EXPORT_TEMPLATE_DECLARE
+    RepeatedPtrField<std::string>;
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_REPEATED_PTR_FIELD_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/service.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/service.h
new file mode 100644
index 0000000..d288eb5
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/service.h
@@ -0,0 +1,295 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// DEPRECATED:  This module declares the abstract interfaces underlying proto2
+// RPC services.  These are intended to be independent of any particular RPC
+// implementation, so that proto2 services can be used on top of a variety
+// of implementations.  Starting with version 2.3.0, RPC implementations should
+// not try to build on these, but should instead provide code generator plugins
+// which generate code specific to the particular RPC implementation.  This way
+// the generated code can be more appropriate for the implementation in use
+// and can avoid unnecessary layers of indirection.
+//
+//
+// When you use the protocol compiler to compile a service definition, it
+// generates two classes:  An abstract interface for the service (with
+// methods matching the service definition) and a "stub" implementation.
+// A stub is just a type-safe wrapper around an RpcChannel which emulates a
+// local implementation of the service.
+//
+// For example, the service definition:
+//   service MyService {
+//     rpc Foo(MyRequest) returns(MyResponse);
+//   }
+// will generate abstract interface "MyService" and class "MyService::Stub".
+// You could implement a MyService as follows:
+//   class MyServiceImpl : public MyService {
+//    public:
+//     MyServiceImpl() {}
+//     ~MyServiceImpl() {}
+//
+//     // implements MyService ---------------------------------------
+//
+//     void Foo(google::protobuf::RpcController* controller,
+//              const MyRequest* request,
+//              MyResponse* response,
+//              Closure* done) {
+//       // ... read request and fill in response ...
+//       done->Run();
+//     }
+//   };
+// You would then register an instance of MyServiceImpl with your RPC server
+// implementation.  (How to do that depends on the implementation.)
+//
+// To call a remote MyServiceImpl, first you need an RpcChannel connected to it.
+// How to construct a channel depends, again, on your RPC implementation.
+// Here we use a hypothetical "MyRpcChannel" as an example:
+//   MyRpcChannel channel("rpc:hostname:1234/myservice");
+//   MyRpcController controller;
+//   MyServiceImpl::Stub stub(&channel);
+//   FooRequest request;
+//   FooResponse response;
+//
+//   // ... fill in request ...
+//
+//   stub.Foo(&controller, request, &response, NewCallback(HandleResponse));
+//
+// On Thread-Safety:
+//
+// Different RPC implementations may make different guarantees about what
+// threads they may run callbacks on, and what threads the application is
+// allowed to use to call the RPC system.  Portable software should be ready
+// for callbacks to be called on any thread, but should not try to call the
+// RPC system from any thread except for the ones on which it received the
+// callbacks.  Realistically, though, simple software will probably want to
+// use a single-threaded RPC system while high-end software will want to
+// use multiple threads.  RPC implementations should provide multiple
+// choices.
+
+#ifndef GOOGLE_PROTOBUF_SERVICE_H__
+#define GOOGLE_PROTOBUF_SERVICE_H__
+
+
+#include <string>
+#include <google/protobuf/stubs/callback.h>
+#include <google/protobuf/stubs/common.h>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+// Defined in this file.
+class Service;
+class RpcController;
+class RpcChannel;
+
+// Defined in other files.
+class Descriptor;         // descriptor.h
+class ServiceDescriptor;  // descriptor.h
+class MethodDescriptor;   // descriptor.h
+class Message;            // message.h
+
+// Abstract base interface for protocol-buffer-based RPC services.  Services
+// themselves are abstract interfaces (implemented either by servers or as
+// stubs), but they subclass this base interface.  The methods of this
+// interface can be used to call the methods of the Service without knowing
+// its exact type at compile time (analogous to Reflection).
+class PROTOBUF_EXPORT Service {
+ public:
+  inline Service() {}
+  virtual ~Service();
+
+  // When constructing a stub, you may pass STUB_OWNS_CHANNEL as the second
+  // parameter to the constructor to tell it to delete its RpcChannel when
+  // destroyed.
+  enum ChannelOwnership { STUB_OWNS_CHANNEL, STUB_DOESNT_OWN_CHANNEL };
+
+  // Get the ServiceDescriptor describing this service and its methods.
+  virtual const ServiceDescriptor* GetDescriptor() = 0;
+
+  // Call a method of the service specified by MethodDescriptor.  This is
+  // normally implemented as a simple switch() that calls the standard
+  // definitions of the service's methods.
+  //
+  // Preconditions:
+  // * method->service() == GetDescriptor()
+  // * request and response are of the exact same classes as the objects
+  //   returned by GetRequestPrototype(method) and
+  //   GetResponsePrototype(method).
+  // * After the call has started, the request must not be modified and the
+  //   response must not be accessed at all until "done" is called.
+  // * "controller" is of the correct type for the RPC implementation being
+  //   used by this Service.  For stubs, the "correct type" depends on the
+  //   RpcChannel which the stub is using.  Server-side Service
+  //   implementations are expected to accept whatever type of RpcController
+  //   the server-side RPC implementation uses.
+  //
+  // Postconditions:
+  // * "done" will be called when the method is complete.  This may be
+  //   before CallMethod() returns or it may be at some point in the future.
+  // * If the RPC succeeded, "response" contains the response returned by
+  //   the server.
+  // * If the RPC failed, "response"'s contents are undefined.  The
+  //   RpcController can be queried to determine if an error occurred and
+  //   possibly to get more information about the error.
+  virtual void CallMethod(const MethodDescriptor* method,
+                          RpcController* controller, const Message* request,
+                          Message* response, Closure* done) = 0;
+
+  // CallMethod() requires that the request and response passed in are of a
+  // particular subclass of Message.  GetRequestPrototype() and
+  // GetResponsePrototype() get the default instances of these required types.
+  // You can then call Message::New() on these instances to construct mutable
+  // objects which you can then pass to CallMethod().
+  //
+  // Example:
+  //   const MethodDescriptor* method =
+  //     service->GetDescriptor()->FindMethodByName("Foo");
+  //   Message* request  = stub->GetRequestPrototype (method)->New();
+  //   Message* response = stub->GetResponsePrototype(method)->New();
+  //   request->ParseFromString(input);
+  //   service->CallMethod(method, *request, response, callback);
+  virtual const Message& GetRequestPrototype(
+      const MethodDescriptor* method) const = 0;
+  virtual const Message& GetResponsePrototype(
+      const MethodDescriptor* method) const = 0;
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Service);
+};
+
+// An RpcController mediates a single method call.  The primary purpose of
+// the controller is to provide a way to manipulate settings specific to the
+// RPC implementation and to find out about RPC-level errors.
+//
+// The methods provided by the RpcController interface are intended to be a
+// "least common denominator" set of features which we expect all
+// implementations to support.  Specific implementations may provide more
+// advanced features (e.g. deadline propagation).
+class PROTOBUF_EXPORT RpcController {
+ public:
+  inline RpcController() {}
+  virtual ~RpcController();
+
+  // Client-side methods ---------------------------------------------
+  // These calls may be made from the client side only.  Their results
+  // are undefined on the server side (may crash).
+
+  // Resets the RpcController to its initial state so that it may be reused in
+  // a new call.  Must not be called while an RPC is in progress.
+  virtual void Reset() = 0;
+
+  // After a call has finished, returns true if the call failed.  The possible
+  // reasons for failure depend on the RPC implementation.  Failed() must not
+  // be called before a call has finished.  If Failed() returns true, the
+  // contents of the response message are undefined.
+  virtual bool Failed() const = 0;
+
+  // If Failed() is true, returns a human-readable description of the error.
+  virtual std::string ErrorText() const = 0;
+
+  // Advises the RPC system that the caller desires that the RPC call be
+  // canceled.  The RPC system may cancel it immediately, may wait awhile and
+  // then cancel it, or may not even cancel the call at all.  If the call is
+  // canceled, the "done" callback will still be called and the RpcController
+  // will indicate that the call failed at that time.
+  virtual void StartCancel() = 0;
+
+  // Server-side methods ---------------------------------------------
+  // These calls may be made from the server side only.  Their results
+  // are undefined on the client side (may crash).
+
+  // Causes Failed() to return true on the client side.  "reason" will be
+  // incorporated into the message returned by ErrorText().  If you find
+  // you need to return machine-readable information about failures, you
+  // should incorporate it into your response protocol buffer and should
+  // NOT call SetFailed().
+  virtual void SetFailed(const std::string& reason) = 0;
+
+  // If true, indicates that the client canceled the RPC, so the server may
+  // as well give up on replying to it.  The server should still call the
+  // final "done" callback.
+  virtual bool IsCanceled() const = 0;
+
+  // Asks that the given callback be called when the RPC is canceled.  The
+  // callback will always be called exactly once.  If the RPC completes without
+  // being canceled, the callback will be called after completion.  If the RPC
+  // has already been canceled when NotifyOnCancel() is called, the callback
+  // will be called immediately.
+  //
+  // NotifyOnCancel() must be called no more than once per request.
+  virtual void NotifyOnCancel(Closure* callback) = 0;
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcController);
+};
+
+// Abstract interface for an RPC channel.  An RpcChannel represents a
+// communication line to a Service which can be used to call that Service's
+// methods.  The Service may be running on another machine.  Normally, you
+// should not call an RpcChannel directly, but instead construct a stub Service
+// wrapping it.  Example:
+//   RpcChannel* channel = new MyRpcChannel("remotehost.example.com:1234");
+//   MyService* service = new MyService::Stub(channel);
+//   service->MyMethod(request, &response, callback);
+class PROTOBUF_EXPORT RpcChannel {
+ public:
+  inline RpcChannel() {}
+  virtual ~RpcChannel();
+
+  // Call the given method of the remote service.  The signature of this
+  // procedure looks the same as Service::CallMethod(), but the requirements
+  // are less strict in one important way:  the request and response objects
+  // need not be of any specific class as long as their descriptors are
+  // method->input_type() and method->output_type().
+  virtual void CallMethod(const MethodDescriptor* method,
+                          RpcController* controller, const Message* request,
+                          Message* response, Closure* done) = 0;
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcChannel);
+};
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_SERVICE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/source_context.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/source_context.pb.h
new file mode 100644
index 0000000..63e7d11
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/source_context.pb.h
@@ -0,0 +1,282 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/source_context.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fsource_5fcontext_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fsource_5fcontext_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/unknown_field_set.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fsource_5fcontext_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2fsource_5fcontext_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class SourceContext;
+struct SourceContextDefaultTypeInternal;
+PROTOBUF_EXPORT extern SourceContextDefaultTypeInternal _SourceContext_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::SourceContext* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::SourceContext>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class PROTOBUF_EXPORT SourceContext final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.SourceContext) */ {
+ public:
+  inline SourceContext() : SourceContext(nullptr) {}
+  ~SourceContext() override;
+  explicit PROTOBUF_CONSTEXPR SourceContext(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  SourceContext(const SourceContext& from);
+  SourceContext(SourceContext&& from) noexcept
+    : SourceContext() {
+    *this = ::std::move(from);
+  }
+
+  inline SourceContext& operator=(const SourceContext& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline SourceContext& operator=(SourceContext&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const SourceContext& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const SourceContext* internal_default_instance() {
+    return reinterpret_cast<const SourceContext*>(
+               &_SourceContext_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(SourceContext& a, SourceContext& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(SourceContext* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(SourceContext* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  SourceContext* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<SourceContext>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const SourceContext& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const SourceContext& from) {
+    SourceContext::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(SourceContext* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.SourceContext";
+  }
+  protected:
+  explicit SourceContext(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kFileNameFieldNumber = 1,
+  };
+  // string file_name = 1;
+  void clear_file_name();
+  const std::string& file_name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_file_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_file_name();
+  PROTOBUF_NODISCARD std::string* release_file_name();
+  void set_allocated_file_name(std::string* file_name);
+  private:
+  const std::string& _internal_file_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_file_name(const std::string& value);
+  std::string* _internal_mutable_file_name();
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.SourceContext)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr file_name_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fsource_5fcontext_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// SourceContext
+
+// string file_name = 1;
+inline void SourceContext::clear_file_name() {
+  _impl_.file_name_.ClearToEmpty();
+}
+inline const std::string& SourceContext::file_name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.SourceContext.file_name)
+  return _internal_file_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void SourceContext::set_file_name(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.file_name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.SourceContext.file_name)
+}
+inline std::string* SourceContext::mutable_file_name() {
+  std::string* _s = _internal_mutable_file_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.SourceContext.file_name)
+  return _s;
+}
+inline const std::string& SourceContext::_internal_file_name() const {
+  return _impl_.file_name_.Get();
+}
+inline void SourceContext::_internal_set_file_name(const std::string& value) {
+  
+  _impl_.file_name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* SourceContext::_internal_mutable_file_name() {
+  
+  return _impl_.file_name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* SourceContext::release_file_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.SourceContext.file_name)
+  return _impl_.file_name_.Release();
+}
+inline void SourceContext::set_allocated_file_name(std::string* file_name) {
+  if (file_name != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.file_name_.SetAllocated(file_name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.file_name_.IsDefault()) {
+    _impl_.file_name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.SourceContext.file_name)
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fsource_5fcontext_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/struct.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/struct.pb.h
new file mode 100644
index 0000000..002aa85
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/struct.pb.h
@@ -0,0 +1,1177 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/struct.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fstruct_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fstruct_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/map.h>  // IWYU pragma: export
+#include <google/protobuf/map_entry.h>
+#include <google/protobuf/map_field_inl.h>
+#include <google/protobuf/generated_enum_reflection.h>
+#include <google/protobuf/unknown_field_set.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fstruct_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2fstruct_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2fstruct_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class ListValue;
+struct ListValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern ListValueDefaultTypeInternal _ListValue_default_instance_;
+class Struct;
+struct StructDefaultTypeInternal;
+PROTOBUF_EXPORT extern StructDefaultTypeInternal _Struct_default_instance_;
+class Struct_FieldsEntry_DoNotUse;
+struct Struct_FieldsEntry_DoNotUseDefaultTypeInternal;
+PROTOBUF_EXPORT extern Struct_FieldsEntry_DoNotUseDefaultTypeInternal _Struct_FieldsEntry_DoNotUse_default_instance_;
+class Value;
+struct ValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern ValueDefaultTypeInternal _Value_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::ListValue* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::ListValue>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Struct* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Struct>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Struct_FieldsEntry_DoNotUse* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Struct_FieldsEntry_DoNotUse>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Value* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Value>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+enum NullValue : int {
+  NULL_VALUE = 0,
+  NullValue_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),
+  NullValue_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
+};
+PROTOBUF_EXPORT bool NullValue_IsValid(int value);
+constexpr NullValue NullValue_MIN = NULL_VALUE;
+constexpr NullValue NullValue_MAX = NULL_VALUE;
+constexpr int NullValue_ARRAYSIZE = NullValue_MAX + 1;
+
+PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* NullValue_descriptor();
+template<typename T>
+inline const std::string& NullValue_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, NullValue>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function NullValue_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    NullValue_descriptor(), enum_t_value);
+}
+inline bool NullValue_Parse(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, NullValue* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<NullValue>(
+    NullValue_descriptor(), name, value);
+}
+// ===================================================================
+
+class Struct_FieldsEntry_DoNotUse : public ::PROTOBUF_NAMESPACE_ID::internal::MapEntry<Struct_FieldsEntry_DoNotUse, 
+    std::string, ::PROTOBUF_NAMESPACE_ID::Value,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_MESSAGE> {
+public:
+  typedef ::PROTOBUF_NAMESPACE_ID::internal::MapEntry<Struct_FieldsEntry_DoNotUse, 
+    std::string, ::PROTOBUF_NAMESPACE_ID::Value,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_MESSAGE> SuperType;
+  Struct_FieldsEntry_DoNotUse();
+  explicit PROTOBUF_CONSTEXPR Struct_FieldsEntry_DoNotUse(
+      ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+  explicit Struct_FieldsEntry_DoNotUse(::PROTOBUF_NAMESPACE_ID::Arena* arena);
+  void MergeFrom(const Struct_FieldsEntry_DoNotUse& other);
+  static const Struct_FieldsEntry_DoNotUse* internal_default_instance() { return reinterpret_cast<const Struct_FieldsEntry_DoNotUse*>(&_Struct_FieldsEntry_DoNotUse_default_instance_); }
+  static bool ValidateKey(std::string* s) {
+    return ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(s->data(), static_cast<int>(s->size()), ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, "google.protobuf.Struct.FieldsEntry.key");
+ }
+  static bool ValidateValue(void*) { return true; }
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+  friend struct ::TableStruct_google_2fprotobuf_2fstruct_2eproto;
+};
+
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT Struct final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Struct) */ {
+ public:
+  inline Struct() : Struct(nullptr) {}
+  ~Struct() override;
+  explicit PROTOBUF_CONSTEXPR Struct(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Struct(const Struct& from);
+  Struct(Struct&& from) noexcept
+    : Struct() {
+    *this = ::std::move(from);
+  }
+
+  inline Struct& operator=(const Struct& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Struct& operator=(Struct&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Struct& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Struct* internal_default_instance() {
+    return reinterpret_cast<const Struct*>(
+               &_Struct_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    1;
+
+  friend void swap(Struct& a, Struct& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Struct* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Struct* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Struct* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Struct>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Struct& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Struct& from) {
+    Struct::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Struct* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Struct";
+  }
+  protected:
+  explicit Struct(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  private:
+  static void ArenaDtor(void* object);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kFieldsFieldNumber = 1,
+  };
+  // map<string, .google.protobuf.Value> fields = 1;
+  int fields_size() const;
+  private:
+  int _internal_fields_size() const;
+  public:
+  void clear_fields();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::PROTOBUF_NAMESPACE_ID::Value >&
+      _internal_fields() const;
+  ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::PROTOBUF_NAMESPACE_ID::Value >*
+      _internal_mutable_fields();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::PROTOBUF_NAMESPACE_ID::Value >&
+      fields() const;
+  ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::PROTOBUF_NAMESPACE_ID::Value >*
+      mutable_fields();
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Struct)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::MapField<
+        Struct_FieldsEntry_DoNotUse,
+        std::string, ::PROTOBUF_NAMESPACE_ID::Value,
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_STRING,
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_MESSAGE> fields_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fstruct_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT Value final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Value) */ {
+ public:
+  inline Value() : Value(nullptr) {}
+  ~Value() override;
+  explicit PROTOBUF_CONSTEXPR Value(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Value(const Value& from);
+  Value(Value&& from) noexcept
+    : Value() {
+    *this = ::std::move(from);
+  }
+
+  inline Value& operator=(const Value& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Value& operator=(Value&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Value& default_instance() {
+    return *internal_default_instance();
+  }
+  enum KindCase {
+    kNullValue = 1,
+    kNumberValue = 2,
+    kStringValue = 3,
+    kBoolValue = 4,
+    kStructValue = 5,
+    kListValue = 6,
+    KIND_NOT_SET = 0,
+  };
+
+  static inline const Value* internal_default_instance() {
+    return reinterpret_cast<const Value*>(
+               &_Value_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    2;
+
+  friend void swap(Value& a, Value& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Value* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Value* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Value* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Value>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Value& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Value& from) {
+    Value::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Value* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Value";
+  }
+  protected:
+  explicit Value(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNullValueFieldNumber = 1,
+    kNumberValueFieldNumber = 2,
+    kStringValueFieldNumber = 3,
+    kBoolValueFieldNumber = 4,
+    kStructValueFieldNumber = 5,
+    kListValueFieldNumber = 6,
+  };
+  // .google.protobuf.NullValue null_value = 1;
+  bool has_null_value() const;
+  private:
+  bool _internal_has_null_value() const;
+  public:
+  void clear_null_value();
+  ::PROTOBUF_NAMESPACE_ID::NullValue null_value() const;
+  void set_null_value(::PROTOBUF_NAMESPACE_ID::NullValue value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::NullValue _internal_null_value() const;
+  void _internal_set_null_value(::PROTOBUF_NAMESPACE_ID::NullValue value);
+  public:
+
+  // double number_value = 2;
+  bool has_number_value() const;
+  private:
+  bool _internal_has_number_value() const;
+  public:
+  void clear_number_value();
+  double number_value() const;
+  void set_number_value(double value);
+  private:
+  double _internal_number_value() const;
+  void _internal_set_number_value(double value);
+  public:
+
+  // string string_value = 3;
+  bool has_string_value() const;
+  private:
+  bool _internal_has_string_value() const;
+  public:
+  void clear_string_value();
+  const std::string& string_value() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_string_value(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_string_value();
+  PROTOBUF_NODISCARD std::string* release_string_value();
+  void set_allocated_string_value(std::string* string_value);
+  private:
+  const std::string& _internal_string_value() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_string_value(const std::string& value);
+  std::string* _internal_mutable_string_value();
+  public:
+
+  // bool bool_value = 4;
+  bool has_bool_value() const;
+  private:
+  bool _internal_has_bool_value() const;
+  public:
+  void clear_bool_value();
+  bool bool_value() const;
+  void set_bool_value(bool value);
+  private:
+  bool _internal_bool_value() const;
+  void _internal_set_bool_value(bool value);
+  public:
+
+  // .google.protobuf.Struct struct_value = 5;
+  bool has_struct_value() const;
+  private:
+  bool _internal_has_struct_value() const;
+  public:
+  void clear_struct_value();
+  const ::PROTOBUF_NAMESPACE_ID::Struct& struct_value() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::Struct* release_struct_value();
+  ::PROTOBUF_NAMESPACE_ID::Struct* mutable_struct_value();
+  void set_allocated_struct_value(::PROTOBUF_NAMESPACE_ID::Struct* struct_value);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Struct& _internal_struct_value() const;
+  ::PROTOBUF_NAMESPACE_ID::Struct* _internal_mutable_struct_value();
+  public:
+  void unsafe_arena_set_allocated_struct_value(
+      ::PROTOBUF_NAMESPACE_ID::Struct* struct_value);
+  ::PROTOBUF_NAMESPACE_ID::Struct* unsafe_arena_release_struct_value();
+
+  // .google.protobuf.ListValue list_value = 6;
+  bool has_list_value() const;
+  private:
+  bool _internal_has_list_value() const;
+  public:
+  void clear_list_value();
+  const ::PROTOBUF_NAMESPACE_ID::ListValue& list_value() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::ListValue* release_list_value();
+  ::PROTOBUF_NAMESPACE_ID::ListValue* mutable_list_value();
+  void set_allocated_list_value(::PROTOBUF_NAMESPACE_ID::ListValue* list_value);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::ListValue& _internal_list_value() const;
+  ::PROTOBUF_NAMESPACE_ID::ListValue* _internal_mutable_list_value();
+  public:
+  void unsafe_arena_set_allocated_list_value(
+      ::PROTOBUF_NAMESPACE_ID::ListValue* list_value);
+  ::PROTOBUF_NAMESPACE_ID::ListValue* unsafe_arena_release_list_value();
+
+  void clear_kind();
+  KindCase kind_case() const;
+  // @@protoc_insertion_point(class_scope:google.protobuf.Value)
+ private:
+  class _Internal;
+  void set_has_null_value();
+  void set_has_number_value();
+  void set_has_string_value();
+  void set_has_bool_value();
+  void set_has_struct_value();
+  void set_has_list_value();
+
+  inline bool has_kind() const;
+  inline void clear_has_kind();
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    union KindUnion {
+      constexpr KindUnion() : _constinit_{} {}
+        ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized _constinit_;
+      int null_value_;
+      double number_value_;
+      ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr string_value_;
+      bool bool_value_;
+      ::PROTOBUF_NAMESPACE_ID::Struct* struct_value_;
+      ::PROTOBUF_NAMESPACE_ID::ListValue* list_value_;
+    } kind_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+    uint32_t _oneof_case_[1];
+
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fstruct_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT ListValue final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.ListValue) */ {
+ public:
+  inline ListValue() : ListValue(nullptr) {}
+  ~ListValue() override;
+  explicit PROTOBUF_CONSTEXPR ListValue(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  ListValue(const ListValue& from);
+  ListValue(ListValue&& from) noexcept
+    : ListValue() {
+    *this = ::std::move(from);
+  }
+
+  inline ListValue& operator=(const ListValue& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline ListValue& operator=(ListValue&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const ListValue& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const ListValue* internal_default_instance() {
+    return reinterpret_cast<const ListValue*>(
+               &_ListValue_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    3;
+
+  friend void swap(ListValue& a, ListValue& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(ListValue* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(ListValue* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  ListValue* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<ListValue>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const ListValue& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const ListValue& from) {
+    ListValue::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(ListValue* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.ListValue";
+  }
+  protected:
+  explicit ListValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValuesFieldNumber = 1,
+  };
+  // repeated .google.protobuf.Value values = 1;
+  int values_size() const;
+  private:
+  int _internal_values_size() const;
+  public:
+  void clear_values();
+  ::PROTOBUF_NAMESPACE_ID::Value* mutable_values(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Value >*
+      mutable_values();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Value& _internal_values(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Value* _internal_add_values();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Value& values(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Value* add_values();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Value >&
+      values() const;
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.ListValue)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Value > values_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fstruct_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// Struct
+
+// map<string, .google.protobuf.Value> fields = 1;
+inline int Struct::_internal_fields_size() const {
+  return _impl_.fields_.size();
+}
+inline int Struct::fields_size() const {
+  return _internal_fields_size();
+}
+inline void Struct::clear_fields() {
+  _impl_.fields_.Clear();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::PROTOBUF_NAMESPACE_ID::Value >&
+Struct::_internal_fields() const {
+  return _impl_.fields_.GetMap();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::PROTOBUF_NAMESPACE_ID::Value >&
+Struct::fields() const {
+  // @@protoc_insertion_point(field_map:google.protobuf.Struct.fields)
+  return _internal_fields();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::PROTOBUF_NAMESPACE_ID::Value >*
+Struct::_internal_mutable_fields() {
+  return _impl_.fields_.MutableMap();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Map< std::string, ::PROTOBUF_NAMESPACE_ID::Value >*
+Struct::mutable_fields() {
+  // @@protoc_insertion_point(field_mutable_map:google.protobuf.Struct.fields)
+  return _internal_mutable_fields();
+}
+
+// -------------------------------------------------------------------
+
+// Value
+
+// .google.protobuf.NullValue null_value = 1;
+inline bool Value::_internal_has_null_value() const {
+  return kind_case() == kNullValue;
+}
+inline bool Value::has_null_value() const {
+  return _internal_has_null_value();
+}
+inline void Value::set_has_null_value() {
+  _impl_._oneof_case_[0] = kNullValue;
+}
+inline void Value::clear_null_value() {
+  if (_internal_has_null_value()) {
+    _impl_.kind_.null_value_ = 0;
+    clear_has_kind();
+  }
+}
+inline ::PROTOBUF_NAMESPACE_ID::NullValue Value::_internal_null_value() const {
+  if (_internal_has_null_value()) {
+    return static_cast< ::PROTOBUF_NAMESPACE_ID::NullValue >(_impl_.kind_.null_value_);
+  }
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::NullValue >(0);
+}
+inline ::PROTOBUF_NAMESPACE_ID::NullValue Value::null_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Value.null_value)
+  return _internal_null_value();
+}
+inline void Value::_internal_set_null_value(::PROTOBUF_NAMESPACE_ID::NullValue value) {
+  if (!_internal_has_null_value()) {
+    clear_kind();
+    set_has_null_value();
+  }
+  _impl_.kind_.null_value_ = value;
+}
+inline void Value::set_null_value(::PROTOBUF_NAMESPACE_ID::NullValue value) {
+  _internal_set_null_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Value.null_value)
+}
+
+// double number_value = 2;
+inline bool Value::_internal_has_number_value() const {
+  return kind_case() == kNumberValue;
+}
+inline bool Value::has_number_value() const {
+  return _internal_has_number_value();
+}
+inline void Value::set_has_number_value() {
+  _impl_._oneof_case_[0] = kNumberValue;
+}
+inline void Value::clear_number_value() {
+  if (_internal_has_number_value()) {
+    _impl_.kind_.number_value_ = 0;
+    clear_has_kind();
+  }
+}
+inline double Value::_internal_number_value() const {
+  if (_internal_has_number_value()) {
+    return _impl_.kind_.number_value_;
+  }
+  return 0;
+}
+inline void Value::_internal_set_number_value(double value) {
+  if (!_internal_has_number_value()) {
+    clear_kind();
+    set_has_number_value();
+  }
+  _impl_.kind_.number_value_ = value;
+}
+inline double Value::number_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Value.number_value)
+  return _internal_number_value();
+}
+inline void Value::set_number_value(double value) {
+  _internal_set_number_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Value.number_value)
+}
+
+// string string_value = 3;
+inline bool Value::_internal_has_string_value() const {
+  return kind_case() == kStringValue;
+}
+inline bool Value::has_string_value() const {
+  return _internal_has_string_value();
+}
+inline void Value::set_has_string_value() {
+  _impl_._oneof_case_[0] = kStringValue;
+}
+inline void Value::clear_string_value() {
+  if (_internal_has_string_value()) {
+    _impl_.kind_.string_value_.Destroy();
+    clear_has_kind();
+  }
+}
+inline const std::string& Value::string_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Value.string_value)
+  return _internal_string_value();
+}
+template <typename ArgT0, typename... ArgT>
+inline void Value::set_string_value(ArgT0&& arg0, ArgT... args) {
+  if (!_internal_has_string_value()) {
+    clear_kind();
+    set_has_string_value();
+    _impl_.kind_.string_value_.InitDefault();
+  }
+  _impl_.kind_.string_value_.Set( static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Value.string_value)
+}
+inline std::string* Value::mutable_string_value() {
+  std::string* _s = _internal_mutable_string_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Value.string_value)
+  return _s;
+}
+inline const std::string& Value::_internal_string_value() const {
+  if (_internal_has_string_value()) {
+    return _impl_.kind_.string_value_.Get();
+  }
+  return ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited();
+}
+inline void Value::_internal_set_string_value(const std::string& value) {
+  if (!_internal_has_string_value()) {
+    clear_kind();
+    set_has_string_value();
+    _impl_.kind_.string_value_.InitDefault();
+  }
+  _impl_.kind_.string_value_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Value::_internal_mutable_string_value() {
+  if (!_internal_has_string_value()) {
+    clear_kind();
+    set_has_string_value();
+    _impl_.kind_.string_value_.InitDefault();
+  }
+  return _impl_.kind_.string_value_.Mutable(      GetArenaForAllocation());
+}
+inline std::string* Value::release_string_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Value.string_value)
+  if (_internal_has_string_value()) {
+    clear_has_kind();
+    return _impl_.kind_.string_value_.Release();
+  } else {
+    return nullptr;
+  }
+}
+inline void Value::set_allocated_string_value(std::string* string_value) {
+  if (has_kind()) {
+    clear_kind();
+  }
+  if (string_value != nullptr) {
+    set_has_string_value();
+    _impl_.kind_.string_value_.InitAllocated(string_value, GetArenaForAllocation());
+  }
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Value.string_value)
+}
+
+// bool bool_value = 4;
+inline bool Value::_internal_has_bool_value() const {
+  return kind_case() == kBoolValue;
+}
+inline bool Value::has_bool_value() const {
+  return _internal_has_bool_value();
+}
+inline void Value::set_has_bool_value() {
+  _impl_._oneof_case_[0] = kBoolValue;
+}
+inline void Value::clear_bool_value() {
+  if (_internal_has_bool_value()) {
+    _impl_.kind_.bool_value_ = false;
+    clear_has_kind();
+  }
+}
+inline bool Value::_internal_bool_value() const {
+  if (_internal_has_bool_value()) {
+    return _impl_.kind_.bool_value_;
+  }
+  return false;
+}
+inline void Value::_internal_set_bool_value(bool value) {
+  if (!_internal_has_bool_value()) {
+    clear_kind();
+    set_has_bool_value();
+  }
+  _impl_.kind_.bool_value_ = value;
+}
+inline bool Value::bool_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Value.bool_value)
+  return _internal_bool_value();
+}
+inline void Value::set_bool_value(bool value) {
+  _internal_set_bool_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Value.bool_value)
+}
+
+// .google.protobuf.Struct struct_value = 5;
+inline bool Value::_internal_has_struct_value() const {
+  return kind_case() == kStructValue;
+}
+inline bool Value::has_struct_value() const {
+  return _internal_has_struct_value();
+}
+inline void Value::set_has_struct_value() {
+  _impl_._oneof_case_[0] = kStructValue;
+}
+inline void Value::clear_struct_value() {
+  if (_internal_has_struct_value()) {
+    if (GetArenaForAllocation() == nullptr) {
+      delete _impl_.kind_.struct_value_;
+    }
+    clear_has_kind();
+  }
+}
+inline ::PROTOBUF_NAMESPACE_ID::Struct* Value::release_struct_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Value.struct_value)
+  if (_internal_has_struct_value()) {
+    clear_has_kind();
+    ::PROTOBUF_NAMESPACE_ID::Struct* temp = _impl_.kind_.struct_value_;
+    if (GetArenaForAllocation() != nullptr) {
+      temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+    }
+    _impl_.kind_.struct_value_ = nullptr;
+    return temp;
+  } else {
+    return nullptr;
+  }
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Struct& Value::_internal_struct_value() const {
+  return _internal_has_struct_value()
+      ? *_impl_.kind_.struct_value_
+      : reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::Struct&>(::PROTOBUF_NAMESPACE_ID::_Struct_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Struct& Value::struct_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Value.struct_value)
+  return _internal_struct_value();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Struct* Value::unsafe_arena_release_struct_value() {
+  // @@protoc_insertion_point(field_unsafe_arena_release:google.protobuf.Value.struct_value)
+  if (_internal_has_struct_value()) {
+    clear_has_kind();
+    ::PROTOBUF_NAMESPACE_ID::Struct* temp = _impl_.kind_.struct_value_;
+    _impl_.kind_.struct_value_ = nullptr;
+    return temp;
+  } else {
+    return nullptr;
+  }
+}
+inline void Value::unsafe_arena_set_allocated_struct_value(::PROTOBUF_NAMESPACE_ID::Struct* struct_value) {
+  clear_kind();
+  if (struct_value) {
+    set_has_struct_value();
+    _impl_.kind_.struct_value_ = struct_value;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.Value.struct_value)
+}
+inline ::PROTOBUF_NAMESPACE_ID::Struct* Value::_internal_mutable_struct_value() {
+  if (!_internal_has_struct_value()) {
+    clear_kind();
+    set_has_struct_value();
+    _impl_.kind_.struct_value_ = CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Struct >(GetArenaForAllocation());
+  }
+  return _impl_.kind_.struct_value_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::Struct* Value::mutable_struct_value() {
+  ::PROTOBUF_NAMESPACE_ID::Struct* _msg = _internal_mutable_struct_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Value.struct_value)
+  return _msg;
+}
+
+// .google.protobuf.ListValue list_value = 6;
+inline bool Value::_internal_has_list_value() const {
+  return kind_case() == kListValue;
+}
+inline bool Value::has_list_value() const {
+  return _internal_has_list_value();
+}
+inline void Value::set_has_list_value() {
+  _impl_._oneof_case_[0] = kListValue;
+}
+inline void Value::clear_list_value() {
+  if (_internal_has_list_value()) {
+    if (GetArenaForAllocation() == nullptr) {
+      delete _impl_.kind_.list_value_;
+    }
+    clear_has_kind();
+  }
+}
+inline ::PROTOBUF_NAMESPACE_ID::ListValue* Value::release_list_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Value.list_value)
+  if (_internal_has_list_value()) {
+    clear_has_kind();
+    ::PROTOBUF_NAMESPACE_ID::ListValue* temp = _impl_.kind_.list_value_;
+    if (GetArenaForAllocation() != nullptr) {
+      temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+    }
+    _impl_.kind_.list_value_ = nullptr;
+    return temp;
+  } else {
+    return nullptr;
+  }
+}
+inline const ::PROTOBUF_NAMESPACE_ID::ListValue& Value::_internal_list_value() const {
+  return _internal_has_list_value()
+      ? *_impl_.kind_.list_value_
+      : reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::ListValue&>(::PROTOBUF_NAMESPACE_ID::_ListValue_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::ListValue& Value::list_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Value.list_value)
+  return _internal_list_value();
+}
+inline ::PROTOBUF_NAMESPACE_ID::ListValue* Value::unsafe_arena_release_list_value() {
+  // @@protoc_insertion_point(field_unsafe_arena_release:google.protobuf.Value.list_value)
+  if (_internal_has_list_value()) {
+    clear_has_kind();
+    ::PROTOBUF_NAMESPACE_ID::ListValue* temp = _impl_.kind_.list_value_;
+    _impl_.kind_.list_value_ = nullptr;
+    return temp;
+  } else {
+    return nullptr;
+  }
+}
+inline void Value::unsafe_arena_set_allocated_list_value(::PROTOBUF_NAMESPACE_ID::ListValue* list_value) {
+  clear_kind();
+  if (list_value) {
+    set_has_list_value();
+    _impl_.kind_.list_value_ = list_value;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.Value.list_value)
+}
+inline ::PROTOBUF_NAMESPACE_ID::ListValue* Value::_internal_mutable_list_value() {
+  if (!_internal_has_list_value()) {
+    clear_kind();
+    set_has_list_value();
+    _impl_.kind_.list_value_ = CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::ListValue >(GetArenaForAllocation());
+  }
+  return _impl_.kind_.list_value_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::ListValue* Value::mutable_list_value() {
+  ::PROTOBUF_NAMESPACE_ID::ListValue* _msg = _internal_mutable_list_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Value.list_value)
+  return _msg;
+}
+
+inline bool Value::has_kind() const {
+  return kind_case() != KIND_NOT_SET;
+}
+inline void Value::clear_has_kind() {
+  _impl_._oneof_case_[0] = KIND_NOT_SET;
+}
+inline Value::KindCase Value::kind_case() const {
+  return Value::KindCase(_impl_._oneof_case_[0]);
+}
+// -------------------------------------------------------------------
+
+// ListValue
+
+// repeated .google.protobuf.Value values = 1;
+inline int ListValue::_internal_values_size() const {
+  return _impl_.values_.size();
+}
+inline int ListValue::values_size() const {
+  return _internal_values_size();
+}
+inline void ListValue::clear_values() {
+  _impl_.values_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Value* ListValue::mutable_values(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.ListValue.values)
+  return _impl_.values_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Value >*
+ListValue::mutable_values() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.ListValue.values)
+  return &_impl_.values_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Value& ListValue::_internal_values(int index) const {
+  return _impl_.values_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Value& ListValue::values(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.ListValue.values)
+  return _internal_values(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Value* ListValue::_internal_add_values() {
+  return _impl_.values_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Value* ListValue::add_values() {
+  ::PROTOBUF_NAMESPACE_ID::Value* _add = _internal_add_values();
+  // @@protoc_insertion_point(field_add:google.protobuf.ListValue.values)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Value >&
+ListValue::values() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.ListValue.values)
+  return _impl_.values_;
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+PROTOBUF_NAMESPACE_OPEN
+
+template <> struct is_proto_enum< ::PROTOBUF_NAMESPACE_ID::NullValue> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::PROTOBUF_NAMESPACE_ID::NullValue>() {
+  return ::PROTOBUF_NAMESPACE_ID::NullValue_descriptor();
+}
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fstruct_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/bytestream.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/bytestream.h
new file mode 100644
index 0000000..c7a48de
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/bytestream.h
@@ -0,0 +1,351 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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 file declares the ByteSink and ByteSource abstract interfaces. These
+// interfaces represent objects that consume (ByteSink) or produce (ByteSource)
+// a sequence of bytes. Using these abstract interfaces in your APIs can help
+// make your code work with a variety of input and output types.
+//
+// This file also declares the following commonly used implementations of these
+// interfaces.
+//
+//   ByteSink:
+//      UncheckedArrayByteSink  Writes to an array, without bounds checking
+//      CheckedArrayByteSink    Writes to an array, with bounds checking
+//      GrowingArrayByteSink    Allocates and writes to a growable buffer
+//      StringByteSink          Writes to an STL string
+//      NullByteSink            Consumes a never-ending stream of bytes
+//
+//   ByteSource:
+//      ArrayByteSource         Reads from an array or string/StringPiece
+//      LimitedByteSource       Limits the number of bytes read from an
+
+#ifndef GOOGLE_PROTOBUF_STUBS_BYTESTREAM_H_
+#define GOOGLE_PROTOBUF_STUBS_BYTESTREAM_H_
+
+#include <stddef.h>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+
+#include <google/protobuf/port_def.inc>
+
+class CordByteSink;
+
+namespace google {
+namespace protobuf {
+namespace strings {
+
+// An abstract interface for an object that consumes a sequence of bytes. This
+// interface offers a way to append data as well as a Flush() function.
+//
+// Example:
+//
+//   string my_data;
+//   ...
+//   ByteSink* sink = ...
+//   sink->Append(my_data.data(), my_data.size());
+//   sink->Flush();
+//
+class PROTOBUF_EXPORT ByteSink {
+ public:
+  ByteSink() {}
+  virtual ~ByteSink() {}
+
+  // Appends the "n" bytes starting at "bytes".
+  virtual void Append(const char* bytes, size_t n) = 0;
+
+  // Flushes internal buffers. The default implementation does nothing. ByteSink
+  // subclasses may use internal buffers that require calling Flush() at the end
+  // of the stream.
+  virtual void Flush();
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ByteSink);
+};
+
+// An abstract interface for an object that produces a fixed-size sequence of
+// bytes.
+//
+// Example:
+//
+//   ByteSource* source = ...
+//   while (source->Available() > 0) {
+//     StringPiece data = source->Peek();
+//     ... do something with "data" ...
+//     source->Skip(data.length());
+//   }
+//
+class PROTOBUF_EXPORT ByteSource {
+ public:
+  ByteSource() {}
+  virtual ~ByteSource() {}
+
+  // Returns the number of bytes left to read from the source. Available()
+  // should decrease by N each time Skip(N) is called. Available() may not
+  // increase. Available() returning 0 indicates that the ByteSource is
+  // exhausted.
+  //
+  // Note: Size() may have been a more appropriate name as it's more
+  //       indicative of the fixed-size nature of a ByteSource.
+  virtual size_t Available() const = 0;
+
+  // Returns a StringPiece of the next contiguous region of the source. Does not
+  // reposition the source. The returned region is empty iff Available() == 0.
+  //
+  // The returned region is valid until the next call to Skip() or until this
+  // object is destroyed, whichever occurs first.
+  //
+  // The length of the returned StringPiece will be <= Available().
+  virtual StringPiece Peek() = 0;
+
+  // Skips the next n bytes. Invalidates any StringPiece returned by a previous
+  // call to Peek().
+  //
+  // REQUIRES: Available() >= n
+  virtual void Skip(size_t n) = 0;
+
+  // Writes the next n bytes in this ByteSource to the given ByteSink, and
+  // advances this ByteSource past the copied bytes. The default implementation
+  // of this method just copies the bytes normally, but subclasses might
+  // override CopyTo to optimize certain cases.
+  //
+  // REQUIRES: Available() >= n
+  virtual void CopyTo(ByteSink* sink, size_t n);
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ByteSource);
+};
+
+//
+// Some commonly used implementations of ByteSink
+//
+
+// Implementation of ByteSink that writes to an unsized byte array. No
+// bounds-checking is performed--it is the caller's responsibility to ensure
+// that the destination array is large enough.
+//
+// Example:
+//
+//   char buf[10];
+//   UncheckedArrayByteSink sink(buf);
+//   sink.Append("hi", 2);    // OK
+//   sink.Append(data, 100);  // WOOPS! Overflows buf[10].
+//
+class PROTOBUF_EXPORT UncheckedArrayByteSink : public ByteSink {
+ public:
+  explicit UncheckedArrayByteSink(char* dest) : dest_(dest) {}
+  virtual void Append(const char* data, size_t n) override;
+
+  // Returns the current output pointer so that a caller can see how many bytes
+  // were produced.
+  //
+  // Note: this method is not part of the ByteSink interface.
+  char* CurrentDestination() const { return dest_; }
+
+ private:
+  char* dest_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UncheckedArrayByteSink);
+};
+
+// Implementation of ByteSink that writes to a sized byte array. This sink will
+// not write more than "capacity" bytes to outbuf. Once "capacity" bytes are
+// appended, subsequent bytes will be ignored and Overflowed() will return true.
+// Overflowed() does not cause a runtime error (i.e., it does not CHECK fail).
+//
+// Example:
+//
+//   char buf[10];
+//   CheckedArrayByteSink sink(buf, 10);
+//   sink.Append("hi", 2);    // OK
+//   sink.Append(data, 100);  // Will only write 8 more bytes
+//
+class PROTOBUF_EXPORT CheckedArrayByteSink : public ByteSink {
+ public:
+  CheckedArrayByteSink(char* outbuf, size_t capacity);
+  virtual void Append(const char* bytes, size_t n) override;
+
+  // Returns the number of bytes actually written to the sink.
+  size_t NumberOfBytesWritten() const { return size_; }
+
+  // Returns true if any bytes were discarded, i.e., if there was an
+  // attempt to write more than 'capacity' bytes.
+  bool Overflowed() const { return overflowed_; }
+
+ private:
+  char* outbuf_;
+  const size_t capacity_;
+  size_t size_;
+  bool overflowed_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CheckedArrayByteSink);
+};
+
+// Implementation of ByteSink that allocates an internal buffer (a char array)
+// and expands it as needed to accommodate appended data (similar to a string),
+// and allows the caller to take ownership of the internal buffer via the
+// GetBuffer() method. The buffer returned from GetBuffer() must be deleted by
+// the caller with delete[]. GetBuffer() also sets the internal buffer to be
+// empty, and subsequent appends to the sink will create a new buffer. The
+// destructor will free the internal buffer if GetBuffer() was not called.
+//
+// Example:
+//
+//   GrowingArrayByteSink sink(10);
+//   sink.Append("hi", 2);
+//   sink.Append(data, n);
+//   const char* buf = sink.GetBuffer();  // Ownership transferred
+//   delete[] buf;
+//
+class PROTOBUF_EXPORT GrowingArrayByteSink : public strings::ByteSink {
+ public:
+  explicit GrowingArrayByteSink(size_t estimated_size);
+  virtual ~GrowingArrayByteSink();
+  virtual void Append(const char* bytes, size_t n) override;
+
+  // Returns the allocated buffer, and sets nbytes to its size. The caller takes
+  // ownership of the buffer and must delete it with delete[].
+  char* GetBuffer(size_t* nbytes);
+
+ private:
+  void Expand(size_t amount);
+  void ShrinkToFit();
+
+  size_t capacity_;
+  char* buf_;
+  size_t size_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GrowingArrayByteSink);
+};
+
+// Implementation of ByteSink that appends to the given string.
+// Existing contents of "dest" are not modified; new data is appended.
+//
+// Example:
+//
+//   string dest = "Hello ";
+//   StringByteSink sink(&dest);
+//   sink.Append("World", 5);
+//   assert(dest == "Hello World");
+//
+class PROTOBUF_EXPORT StringByteSink : public ByteSink {
+ public:
+  explicit StringByteSink(std::string* dest) : dest_(dest) {}
+  virtual void Append(const char* data, size_t n) override;
+
+ private:
+  std::string* dest_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringByteSink);
+};
+
+// Implementation of ByteSink that discards all data.
+//
+// Example:
+//
+//   NullByteSink sink;
+//   sink.Append(data, data.size());  // All data ignored.
+//
+class PROTOBUF_EXPORT NullByteSink : public ByteSink {
+ public:
+  NullByteSink() {}
+  void Append(const char* /*data*/, size_t /*n*/) override {}
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(NullByteSink);
+};
+
+//
+// Some commonly used implementations of ByteSource
+//
+
+// Implementation of ByteSource that reads from a StringPiece.
+//
+// Example:
+//
+//   string data = "Hello";
+//   ArrayByteSource source(data);
+//   assert(source.Available() == 5);
+//   assert(source.Peek() == "Hello");
+//
+class PROTOBUF_EXPORT ArrayByteSource : public ByteSource {
+ public:
+  explicit ArrayByteSource(StringPiece s) : input_(s) {}
+
+  virtual size_t Available() const override;
+  virtual StringPiece Peek() override;
+  virtual void Skip(size_t n) override;
+
+ private:
+  StringPiece   input_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayByteSource);
+};
+
+// Implementation of ByteSource that wraps another ByteSource, limiting the
+// number of bytes returned.
+//
+// The caller maintains ownership of the underlying source, and may not use the
+// underlying source while using the LimitByteSource object.  The underlying
+// source's pointer is advanced by n bytes every time this LimitByteSource
+// object is advanced by n.
+//
+// Example:
+//
+//   string data = "Hello World";
+//   ArrayByteSource abs(data);
+//   assert(abs.Available() == data.size());
+//
+//   LimitByteSource limit(abs, 5);
+//   assert(limit.Available() == 5);
+//   assert(limit.Peek() == "Hello");
+//
+class PROTOBUF_EXPORT LimitByteSource : public ByteSource {
+ public:
+  // Returns at most "limit" bytes from "source".
+  LimitByteSource(ByteSource* source, size_t limit);
+
+  virtual size_t Available() const override;
+  virtual StringPiece Peek() override;
+  virtual void Skip(size_t n) override;
+
+  // We override CopyTo so that we can forward to the underlying source, in
+  // case it has an efficient implementation of CopyTo.
+  virtual void CopyTo(ByteSink* sink, size_t n) override;
+
+ private:
+  ByteSource* source_;
+  size_t limit_;
+};
+
+}  // namespace strings
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_BYTESTREAM_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/callback.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/callback.h
new file mode 100644
index 0000000..43d546d
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/callback.h
@@ -0,0 +1,583 @@
+#ifndef GOOGLE_PROTOBUF_STUBS_CALLBACK_H_
+#define GOOGLE_PROTOBUF_STUBS_CALLBACK_H_
+
+#include <type_traits>
+
+#include <google/protobuf/stubs/macros.h>
+
+#include <google/protobuf/port_def.inc>
+
+// ===================================================================
+// emulates google3/base/callback.h
+
+namespace google {
+namespace protobuf {
+
+// Abstract interface for a callback.  When calling an RPC, you must provide
+// a Closure to call when the procedure completes.  See the Service interface
+// in service.h.
+//
+// To automatically construct a Closure which calls a particular function or
+// method with a particular set of parameters, use the NewCallback() function.
+// Example:
+//   void FooDone(const FooResponse* response) {
+//     ...
+//   }
+//
+//   void CallFoo() {
+//     ...
+//     // When done, call FooDone() and pass it a pointer to the response.
+//     Closure* callback = NewCallback(&FooDone, response);
+//     // Make the call.
+//     service->Foo(controller, request, response, callback);
+//   }
+//
+// Example that calls a method:
+//   class Handler {
+//    public:
+//     ...
+//
+//     void FooDone(const FooResponse* response) {
+//       ...
+//     }
+//
+//     void CallFoo() {
+//       ...
+//       // When done, call FooDone() and pass it a pointer to the response.
+//       Closure* callback = NewCallback(this, &Handler::FooDone, response);
+//       // Make the call.
+//       service->Foo(controller, request, response, callback);
+//     }
+//   };
+//
+// Currently NewCallback() supports binding zero, one, or two arguments.
+//
+// Callbacks created with NewCallback() automatically delete themselves when
+// executed.  They should be used when a callback is to be called exactly
+// once (usually the case with RPC callbacks).  If a callback may be called
+// a different number of times (including zero), create it with
+// NewPermanentCallback() instead.  You are then responsible for deleting the
+// callback (using the "delete" keyword as normal).
+//
+// Note that NewCallback() is a bit touchy regarding argument types.  Generally,
+// the values you provide for the parameter bindings must exactly match the
+// types accepted by the callback function.  For example:
+//   void Foo(std::string s);
+//   NewCallback(&Foo, "foo");          // WON'T WORK:  const char* != string
+//   NewCallback(&Foo, std::string("foo"));  // WORKS
+// Also note that the arguments cannot be references:
+//   void Foo(const std::string& s);
+//   std::string my_str;
+//   NewCallback(&Foo, my_str);  // WON'T WORK:  Can't use references.
+// However, correctly-typed pointers will work just fine.
+class PROTOBUF_EXPORT Closure {
+ public:
+  Closure() {}
+  virtual ~Closure();
+
+  virtual void Run() = 0;
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Closure);
+};
+
+template<typename R>
+class ResultCallback {
+ public:
+  ResultCallback() {}
+  virtual ~ResultCallback() {}
+
+  virtual R Run() = 0;
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ResultCallback);
+};
+
+template <typename R, typename A1>
+class PROTOBUF_EXPORT ResultCallback1 {
+ public:
+  ResultCallback1() {}
+  virtual ~ResultCallback1() {}
+
+  virtual R Run(A1) = 0;
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ResultCallback1);
+};
+
+template <typename R, typename A1, typename A2>
+class PROTOBUF_EXPORT ResultCallback2 {
+ public:
+  ResultCallback2() {}
+  virtual ~ResultCallback2() {}
+
+  virtual R Run(A1,A2) = 0;
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ResultCallback2);
+};
+
+namespace internal {
+
+class PROTOBUF_EXPORT FunctionClosure0 : public Closure {
+ public:
+  typedef void (*FunctionType)();
+
+  FunctionClosure0(FunctionType function, bool self_deleting)
+    : function_(function), self_deleting_(self_deleting) {}
+  ~FunctionClosure0();
+
+  void Run() override {
+    bool needs_delete = self_deleting_;  // read in case callback deletes
+    function_();
+    if (needs_delete) delete this;
+  }
+
+ private:
+  FunctionType function_;
+  bool self_deleting_;
+};
+
+template <typename Class>
+class MethodClosure0 : public Closure {
+ public:
+  typedef void (Class::*MethodType)();
+
+  MethodClosure0(Class* object, MethodType method, bool self_deleting)
+    : object_(object), method_(method), self_deleting_(self_deleting) {}
+  ~MethodClosure0() {}
+
+  void Run() override {
+    bool needs_delete = self_deleting_;  // read in case callback deletes
+    (object_->*method_)();
+    if (needs_delete) delete this;
+  }
+
+ private:
+  Class* object_;
+  MethodType method_;
+  bool self_deleting_;
+};
+
+template <typename Arg1>
+class FunctionClosure1 : public Closure {
+ public:
+  typedef void (*FunctionType)(Arg1 arg1);
+
+  FunctionClosure1(FunctionType function, bool self_deleting,
+                   Arg1 arg1)
+    : function_(function), self_deleting_(self_deleting),
+      arg1_(arg1) {}
+  ~FunctionClosure1() {}
+
+  void Run() override {
+    bool needs_delete = self_deleting_;  // read in case callback deletes
+    function_(arg1_);
+    if (needs_delete) delete this;
+  }
+
+ private:
+  FunctionType function_;
+  bool self_deleting_;
+  Arg1 arg1_;
+};
+
+template <typename Class, typename Arg1>
+class MethodClosure1 : public Closure {
+ public:
+  typedef void (Class::*MethodType)(Arg1 arg1);
+
+  MethodClosure1(Class* object, MethodType method, bool self_deleting,
+                 Arg1 arg1)
+    : object_(object), method_(method), self_deleting_(self_deleting),
+      arg1_(arg1) {}
+  ~MethodClosure1() {}
+
+  void Run() override {
+    bool needs_delete = self_deleting_;  // read in case callback deletes
+    (object_->*method_)(arg1_);
+    if (needs_delete) delete this;
+  }
+
+ private:
+  Class* object_;
+  MethodType method_;
+  bool self_deleting_;
+  Arg1 arg1_;
+};
+
+template <typename Arg1, typename Arg2>
+class FunctionClosure2 : public Closure {
+ public:
+  typedef void (*FunctionType)(Arg1 arg1, Arg2 arg2);
+
+  FunctionClosure2(FunctionType function, bool self_deleting,
+                   Arg1 arg1, Arg2 arg2)
+    : function_(function), self_deleting_(self_deleting),
+      arg1_(arg1), arg2_(arg2) {}
+  ~FunctionClosure2() {}
+
+  void Run() override {
+    bool needs_delete = self_deleting_;  // read in case callback deletes
+    function_(arg1_, arg2_);
+    if (needs_delete) delete this;
+  }
+
+ private:
+  FunctionType function_;
+  bool self_deleting_;
+  Arg1 arg1_;
+  Arg2 arg2_;
+};
+
+template <typename Class, typename Arg1, typename Arg2>
+class MethodClosure2 : public Closure {
+ public:
+  typedef void (Class::*MethodType)(Arg1 arg1, Arg2 arg2);
+
+  MethodClosure2(Class* object, MethodType method, bool self_deleting,
+                 Arg1 arg1, Arg2 arg2)
+    : object_(object), method_(method), self_deleting_(self_deleting),
+      arg1_(arg1), arg2_(arg2) {}
+  ~MethodClosure2() {}
+
+  void Run() override {
+    bool needs_delete = self_deleting_;  // read in case callback deletes
+    (object_->*method_)(arg1_, arg2_);
+    if (needs_delete) delete this;
+  }
+
+ private:
+  Class* object_;
+  MethodType method_;
+  bool self_deleting_;
+  Arg1 arg1_;
+  Arg2 arg2_;
+};
+
+template<typename R>
+class FunctionResultCallback_0_0 : public ResultCallback<R> {
+ public:
+  typedef R (*FunctionType)();
+
+  FunctionResultCallback_0_0(FunctionType function, bool self_deleting)
+      : function_(function), self_deleting_(self_deleting) {}
+  ~FunctionResultCallback_0_0() {}
+
+  R Run() override {
+    bool needs_delete = self_deleting_;  // read in case callback deletes
+    R result = function_();
+    if (needs_delete) delete this;
+    return result;
+  }
+
+ private:
+  FunctionType function_;
+  bool self_deleting_;
+};
+
+template<typename R, typename P1>
+class FunctionResultCallback_1_0 : public ResultCallback<R> {
+ public:
+  typedef R (*FunctionType)(P1);
+
+  FunctionResultCallback_1_0(FunctionType function, bool self_deleting,
+                             P1 p1)
+      : function_(function), self_deleting_(self_deleting), p1_(p1) {}
+  ~FunctionResultCallback_1_0() {}
+
+  R Run() override {
+    bool needs_delete = self_deleting_;  // read in case callback deletes
+    R result = function_(p1_);
+    if (needs_delete) delete this;
+    return result;
+  }
+
+ private:
+  FunctionType function_;
+  bool self_deleting_;
+  P1 p1_;
+};
+
+template<typename R, typename Arg1>
+class FunctionResultCallback_0_1 : public ResultCallback1<R, Arg1> {
+ public:
+  typedef R (*FunctionType)(Arg1 arg1);
+
+  FunctionResultCallback_0_1(FunctionType function, bool self_deleting)
+      : function_(function), self_deleting_(self_deleting) {}
+  ~FunctionResultCallback_0_1() {}
+
+  R Run(Arg1 a1) override {
+    bool needs_delete = self_deleting_;  // read in case callback deletes
+    R result = function_(a1);
+    if (needs_delete) delete this;
+    return result;
+  }
+
+ private:
+  FunctionType function_;
+  bool self_deleting_;
+};
+
+template<typename R, typename P1, typename A1>
+class FunctionResultCallback_1_1 : public ResultCallback1<R, A1> {
+ public:
+  typedef R (*FunctionType)(P1, A1);
+
+  FunctionResultCallback_1_1(FunctionType function, bool self_deleting,
+                             P1 p1)
+      : function_(function), self_deleting_(self_deleting), p1_(p1) {}
+  ~FunctionResultCallback_1_1() {}
+
+  R Run(A1 a1) override {
+    bool needs_delete = self_deleting_;  // read in case callback deletes
+    R result = function_(p1_, a1);
+    if (needs_delete) delete this;
+    return result;
+  }
+
+ private:
+  FunctionType function_;
+  bool self_deleting_;
+  P1 p1_;
+};
+
+template <typename T>
+struct InternalConstRef {
+  typedef typename std::remove_reference<T>::type base_type;
+  typedef const base_type& type;
+};
+
+template<typename R, typename T>
+class MethodResultCallback_0_0 : public ResultCallback<R> {
+ public:
+  typedef R (T::*MethodType)();
+  MethodResultCallback_0_0(T* object, MethodType method, bool self_deleting)
+      : object_(object),
+        method_(method),
+        self_deleting_(self_deleting) {}
+  ~MethodResultCallback_0_0() {}
+
+  R Run() {
+    bool needs_delete = self_deleting_;
+    R result = (object_->*method_)();
+    if (needs_delete) delete this;
+    return result;
+  }
+
+ private:
+  T* object_;
+  MethodType method_;
+  bool self_deleting_;
+};
+
+template <typename R, typename T, typename P1, typename P2, typename P3,
+          typename P4, typename P5, typename P6, typename A1, typename A2>
+class MethodResultCallback_6_2 : public ResultCallback2<R, A1, A2> {
+ public:
+  typedef R (T::*MethodType)(P1, P2, P3, P4, P5, P6, A1, A2);
+  MethodResultCallback_6_2(T* object, MethodType method, bool self_deleting,
+                           P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)
+      : object_(object),
+        method_(method),
+        self_deleting_(self_deleting),
+        p1_(p1),
+        p2_(p2),
+        p3_(p3),
+        p4_(p4),
+        p5_(p5),
+        p6_(p6) {}
+  ~MethodResultCallback_6_2() {}
+
+  R Run(A1 a1, A2 a2) override {
+    bool needs_delete = self_deleting_;
+    R result = (object_->*method_)(p1_, p2_, p3_, p4_, p5_, p6_, a1, a2);
+    if (needs_delete) delete this;
+    return result;
+  }
+
+ private:
+  T* object_;
+  MethodType method_;
+  bool self_deleting_;
+  typename std::remove_reference<P1>::type p1_;
+  typename std::remove_reference<P2>::type p2_;
+  typename std::remove_reference<P3>::type p3_;
+  typename std::remove_reference<P4>::type p4_;
+  typename std::remove_reference<P5>::type p5_;
+  typename std::remove_reference<P6>::type p6_;
+};
+
+}  // namespace internal
+
+// See Closure.
+inline Closure* NewCallback(void (*function)()) {
+  return new internal::FunctionClosure0(function, true);
+}
+
+// See Closure.
+inline Closure* NewPermanentCallback(void (*function)()) {
+  return new internal::FunctionClosure0(function, false);
+}
+
+// See Closure.
+template <typename Class>
+inline Closure* NewCallback(Class* object, void (Class::*method)()) {
+  return new internal::MethodClosure0<Class>(object, method, true);
+}
+
+// See Closure.
+template <typename Class>
+inline Closure* NewPermanentCallback(Class* object, void (Class::*method)()) {
+  return new internal::MethodClosure0<Class>(object, method, false);
+}
+
+// See Closure.
+template <typename Arg1>
+inline Closure* NewCallback(void (*function)(Arg1),
+                            Arg1 arg1) {
+  return new internal::FunctionClosure1<Arg1>(function, true, arg1);
+}
+
+// See Closure.
+template <typename Arg1>
+inline Closure* NewPermanentCallback(void (*function)(Arg1),
+                                     Arg1 arg1) {
+  return new internal::FunctionClosure1<Arg1>(function, false, arg1);
+}
+
+// See Closure.
+template <typename Class, typename Arg1>
+inline Closure* NewCallback(Class* object, void (Class::*method)(Arg1),
+                            Arg1 arg1) {
+  return new internal::MethodClosure1<Class, Arg1>(object, method, true, arg1);
+}
+
+// See Closure.
+template <typename Class, typename Arg1>
+inline Closure* NewPermanentCallback(Class* object, void (Class::*method)(Arg1),
+                                     Arg1 arg1) {
+  return new internal::MethodClosure1<Class, Arg1>(object, method, false, arg1);
+}
+
+// See Closure.
+template <typename Arg1, typename Arg2>
+inline Closure* NewCallback(void (*function)(Arg1, Arg2),
+                            Arg1 arg1, Arg2 arg2) {
+  return new internal::FunctionClosure2<Arg1, Arg2>(
+    function, true, arg1, arg2);
+}
+
+// See Closure.
+template <typename Arg1, typename Arg2>
+inline Closure* NewPermanentCallback(void (*function)(Arg1, Arg2),
+                                     Arg1 arg1, Arg2 arg2) {
+  return new internal::FunctionClosure2<Arg1, Arg2>(
+    function, false, arg1, arg2);
+}
+
+// See Closure.
+template <typename Class, typename Arg1, typename Arg2>
+inline Closure* NewCallback(Class* object, void (Class::*method)(Arg1, Arg2),
+                            Arg1 arg1, Arg2 arg2) {
+  return new internal::MethodClosure2<Class, Arg1, Arg2>(
+    object, method, true, arg1, arg2);
+}
+
+// See Closure.
+template <typename Class, typename Arg1, typename Arg2>
+inline Closure* NewPermanentCallback(
+    Class* object, void (Class::*method)(Arg1, Arg2),
+    Arg1 arg1, Arg2 arg2) {
+  return new internal::MethodClosure2<Class, Arg1, Arg2>(
+    object, method, false, arg1, arg2);
+}
+
+// See ResultCallback
+template<typename R>
+inline ResultCallback<R>* NewCallback(R (*function)()) {
+  return new internal::FunctionResultCallback_0_0<R>(function, true);
+}
+
+// See ResultCallback
+template<typename R>
+inline ResultCallback<R>* NewPermanentCallback(R (*function)()) {
+  return new internal::FunctionResultCallback_0_0<R>(function, false);
+}
+
+// See ResultCallback
+template<typename R, typename P1>
+inline ResultCallback<R>* NewCallback(R (*function)(P1), P1 p1) {
+  return new internal::FunctionResultCallback_1_0<R, P1>(
+      function, true, p1);
+}
+
+// See ResultCallback
+template<typename R, typename P1>
+inline ResultCallback<R>* NewPermanentCallback(
+    R (*function)(P1), P1 p1) {
+  return new internal::FunctionResultCallback_1_0<R, P1>(
+      function, false, p1);
+}
+
+// See ResultCallback1
+template<typename R, typename A1>
+inline ResultCallback1<R, A1>* NewCallback(R (*function)(A1)) {
+  return new internal::FunctionResultCallback_0_1<R, A1>(function, true);
+}
+
+// See ResultCallback1
+template<typename R, typename A1>
+inline ResultCallback1<R, A1>* NewPermanentCallback(R (*function)(A1)) {
+  return new internal::FunctionResultCallback_0_1<R, A1>(function, false);
+}
+
+// See ResultCallback1
+template<typename R, typename P1, typename A1>
+inline ResultCallback1<R, A1>* NewCallback(R (*function)(P1, A1), P1 p1) {
+  return new internal::FunctionResultCallback_1_1<R, P1, A1>(
+      function, true, p1);
+}
+
+// See ResultCallback1
+template<typename R, typename P1, typename A1>
+inline ResultCallback1<R, A1>* NewPermanentCallback(
+    R (*function)(P1, A1), P1 p1) {
+  return new internal::FunctionResultCallback_1_1<R, P1, A1>(
+      function, false, p1);
+}
+
+// See MethodResultCallback_0_0
+template <typename R, typename T1, typename T2>
+inline ResultCallback<R>* NewPermanentCallback(
+    T1* object, R (T2::*function)()) {
+  return new internal::MethodResultCallback_0_0<R, T1>(object, function, false);
+}
+
+// See MethodResultCallback_6_2
+template <typename R, typename T, typename P1, typename P2, typename P3,
+          typename P4, typename P5, typename P6, typename A1, typename A2>
+inline ResultCallback2<R, A1, A2>* NewPermanentCallback(
+    T* object, R (T::*function)(P1, P2, P3, P4, P5, P6, A1, A2),
+    typename internal::InternalConstRef<P1>::type p1,
+    typename internal::InternalConstRef<P2>::type p2,
+    typename internal::InternalConstRef<P3>::type p3,
+    typename internal::InternalConstRef<P4>::type p4,
+    typename internal::InternalConstRef<P5>::type p5,
+    typename internal::InternalConstRef<P6>::type p6) {
+  return new internal::MethodResultCallback_6_2<R, T, P1, P2, P3, P4, P5, P6,
+                                                A1, A2>(object, function, false,
+                                                        p1, p2, p3, p4, p5, p6);
+}
+
+// A function which does nothing.  Useful for creating no-op callbacks, e.g.:
+//   Closure* nothing = NewCallback(&DoNothing);
+void PROTOBUF_EXPORT DoNothing();
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_CALLBACK_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/casts.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/casts.h
new file mode 100644
index 0000000..ad29dac
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/casts.h
@@ -0,0 +1,138 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2014 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_CASTS_H__
+#define GOOGLE_PROTOBUF_CASTS_H__
+
+#include <google/protobuf/stubs/common.h>
+
+#include <google/protobuf/port_def.inc>
+#include <type_traits>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Use implicit_cast as a safe version of static_cast or const_cast
+// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
+// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
+// a const pointer to Foo).
+// When you use implicit_cast, the compiler checks that the cast is safe.
+// Such explicit implicit_casts are necessary in surprisingly many
+// situations where C++ demands an exact type match instead of an
+// argument type convertible to a target type.
+//
+// The From type can be inferred, so the preferred syntax for using
+// implicit_cast is the same as for static_cast etc.:
+//
+//   implicit_cast<ToType>(expr)
+//
+// implicit_cast would have been part of the C++ standard library,
+// but the proposal was submitted too late.  It will probably make
+// its way into the language in the future.
+template<typename To, typename From>
+inline To implicit_cast(From const &f) {
+  return f;
+}
+
+// When you upcast (that is, cast a pointer from type Foo to type
+// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts
+// always succeed.  When you downcast (that is, cast a pointer from
+// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
+// how do you know the pointer is really of type SubclassOfFoo?  It
+// could be a bare Foo, or of type DifferentSubclassOfFoo.  Thus,
+// when you downcast, you should use this macro.  In debug mode, we
+// use dynamic_cast<> to double-check the downcast is legal (we die
+// if it's not).  In normal mode, we do the efficient static_cast<>
+// instead.  Thus, it's important to test in debug mode to make sure
+// the cast is legal!
+//    This is the only place in the code we should use dynamic_cast<>.
+// In particular, you SHOULDN'T be using dynamic_cast<> in order to
+// do RTTI (eg code like this:
+//    if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
+//    if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
+// You should design the code some other way not to need this.
+
+template<typename To, typename From>     // use like this: down_cast<T*>(foo);
+inline To down_cast(From* f) {                   // so we only accept pointers
+  // Ensures that To is a sub-type of From *.  This test is here only
+  // for compile-time type checking, and has no overhead in an
+  // optimized build at run-time, as it will be optimized away
+  // completely.
+  if (false) {
+    implicit_cast<From*, To>(0);
+  }
+
+#if !defined(NDEBUG) && PROTOBUF_RTTI
+  assert(f == nullptr || dynamic_cast<To>(f) != nullptr);  // RTTI: debug mode only!
+#endif
+  return static_cast<To>(f);
+}
+
+template<typename To, typename From>    // use like this: down_cast<T&>(foo);
+inline To down_cast(From& f) {
+  typedef typename std::remove_reference<To>::type* ToAsPointer;
+  // Ensures that To is a sub-type of From *.  This test is here only
+  // for compile-time type checking, and has no overhead in an
+  // optimized build at run-time, as it will be optimized away
+  // completely.
+  if (false) {
+    implicit_cast<From*, ToAsPointer>(0);
+  }
+
+#if !defined(NDEBUG) && PROTOBUF_RTTI
+  // RTTI: debug mode only!
+  assert(dynamic_cast<ToAsPointer>(&f) != nullptr);
+#endif
+  return *static_cast<ToAsPointer>(&f);
+}
+
+template<typename To, typename From>
+inline To bit_cast(const From& from) {
+  static_assert(sizeof(From) == sizeof(To), "bit_cast_with_different_sizes");
+  To dest;
+  memcpy(&dest, &from, sizeof(dest));
+  return dest;
+}
+
+}  // namespace internal
+
+// We made these internal so that they would show up as such in the docs,
+// but we don't want to stick "internal::" in front of them everywhere.
+using internal::implicit_cast;
+using internal::down_cast;
+using internal::bit_cast;
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_CASTS_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/common.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/common.h
new file mode 100644
index 0000000..0b9fa81
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/common.h
@@ -0,0 +1,197 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda) and others
+//
+// Contains basic types and utilities used by the rest of the library.
+
+#ifndef GOOGLE_PROTOBUF_COMMON_H__
+#define GOOGLE_PROTOBUF_COMMON_H__
+
+#include <algorithm>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/macros.h>
+#include <google/protobuf/stubs/platform_macros.h>
+#include <google/protobuf/stubs/port.h>
+#include <google/protobuf/stubs/stringpiece.h>
+
+#ifndef PROTOBUF_USE_EXCEPTIONS
+#if defined(_MSC_VER) && defined(_CPPUNWIND)
+  #define PROTOBUF_USE_EXCEPTIONS 1
+#elif defined(__EXCEPTIONS)
+  #define PROTOBUF_USE_EXCEPTIONS 1
+#else
+  #define PROTOBUF_USE_EXCEPTIONS 0
+#endif
+#endif
+
+#if PROTOBUF_USE_EXCEPTIONS
+#include <exception>
+#endif
+#if defined(__APPLE__)
+#include <TargetConditionals.h>  // for TARGET_OS_IPHONE
+#endif
+
+#if defined(__ANDROID__) || defined(GOOGLE_PROTOBUF_OS_ANDROID) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || defined(GOOGLE_PROTOBUF_OS_IPHONE)
+#include <pthread.h>
+#endif
+
+#include <google/protobuf/port_def.inc>
+
+namespace std {}
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Some of these constants are macros rather than const ints so that they can
+// be used in #if directives.
+
+// The current version, represented as a single integer to make comparison
+// easier:  major * 10^6 + minor * 10^3 + micro
+#define GOOGLE_PROTOBUF_VERSION 3021012
+
+// A suffix string for alpha, beta or rc releases. Empty for stable releases.
+#define GOOGLE_PROTOBUF_VERSION_SUFFIX ""
+
+// The minimum header version which works with the current version of
+// the library.  This constant should only be used by protoc's C++ code
+// generator.
+static const int kMinHeaderVersionForLibrary = 3021000;
+
+// The minimum protoc version which works with the current version of the
+// headers.
+#define GOOGLE_PROTOBUF_MIN_PROTOC_VERSION 3021000
+
+// The minimum header version which works with the current version of
+// protoc.  This constant should only be used in VerifyVersion().
+static const int kMinHeaderVersionForProtoc = 3021000;
+
+// Verifies that the headers and libraries are compatible.  Use the macro
+// below to call this.
+void PROTOBUF_EXPORT VerifyVersion(int headerVersion, int minLibraryVersion,
+                                   const char* filename);
+
+// Converts a numeric version number to a string.
+std::string PROTOBUF_EXPORT VersionString(int version);
+
+}  // namespace internal
+
+// Place this macro in your main() function (or somewhere before you attempt
+// to use the protobuf library) to verify that the version you link against
+// matches the headers you compiled against.  If a version mismatch is
+// detected, the process will abort.
+#define GOOGLE_PROTOBUF_VERIFY_VERSION                                    \
+  ::google::protobuf::internal::VerifyVersion(                            \
+    GOOGLE_PROTOBUF_VERSION, GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION,         \
+    __FILE__)
+
+
+// ===================================================================
+// from google3/util/utf8/public/unilib.h
+
+namespace internal {
+
+// Checks if the buffer contains structurally-valid UTF-8.  Implemented in
+// structurally_valid.cc.
+PROTOBUF_EXPORT bool IsStructurallyValidUTF8(const char* buf, int len);
+
+inline bool IsStructurallyValidUTF8(StringPiece str) {
+  return IsStructurallyValidUTF8(str.data(), static_cast<int>(str.length()));
+}
+
+// Returns initial number of bytes of structurally valid UTF-8.
+PROTOBUF_EXPORT int UTF8SpnStructurallyValid(StringPiece str);
+
+// Coerce UTF-8 byte string in src_str to be
+// a structurally-valid equal-length string by selectively
+// overwriting illegal bytes with replace_char (typically ' ' or '?').
+// replace_char must be legal printable 7-bit Ascii 0x20..0x7e.
+// src_str is read-only.
+//
+// Returns pointer to output buffer, src_str.data() if no changes were made,
+//  or idst if some bytes were changed. idst is allocated by the caller
+//  and must be at least as big as src_str
+//
+// Optimized for: all structurally valid and no byte copying is done.
+//
+PROTOBUF_EXPORT char* UTF8CoerceToStructurallyValid(StringPiece str, char* dst,
+                                                    char replace_char);
+
+}  // namespace internal
+
+// This lives in message_lite.h now, but we leave this here for any users that
+// #include common.h and not message_lite.h.
+PROTOBUF_EXPORT void ShutdownProtobufLibrary();
+
+namespace internal {
+
+// Strongly references the given variable such that the linker will be forced
+// to pull in this variable's translation unit.
+template <typename T>
+void StrongReference(const T& var) {
+  auto volatile unused = &var;
+  (void)&unused;  // Use address to avoid an extra load of "unused".
+}
+
+}  // namespace internal
+
+#if PROTOBUF_USE_EXCEPTIONS
+class FatalException : public std::exception {
+ public:
+  FatalException(const char* filename, int line, const std::string& message)
+      : filename_(filename), line_(line), message_(message) {}
+  virtual ~FatalException() throw();
+
+  const char* what() const throw() override;
+
+  const char* filename() const { return filename_; }
+  int line() const { return line_; }
+  const std::string& message() const { return message_; }
+
+ private:
+  const char* filename_;
+  const int line_;
+  const std::string message_;
+};
+#endif
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_COMMON_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/hash.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/hash.h
new file mode 100644
index 0000000..a7ec068
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/hash.h
@@ -0,0 +1,114 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+
+#ifndef GOOGLE_PROTOBUF_STUBS_HASH_H__
+#define GOOGLE_PROTOBUF_STUBS_HASH_H__
+
+#include <cstring>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+# define GOOGLE_PROTOBUF_HASH_NAMESPACE_DECLARATION_START \
+  namespace google {                                      \
+  namespace protobuf {
+# define GOOGLE_PROTOBUF_HASH_NAMESPACE_DECLARATION_END }}
+
+namespace google {
+namespace protobuf {
+
+template <typename Key>
+struct hash : public std::hash<Key> {};
+
+template <typename Key>
+struct hash<const Key*> {
+  inline size_t operator()(const Key* key) const {
+    return reinterpret_cast<size_t>(key);
+  }
+};
+
+// Unlike the old SGI version, the TR1 "hash" does not special-case char*.  So,
+// we go ahead and provide our own implementation.
+template <>
+struct hash<const char*> {
+  inline size_t operator()(const char* str) const {
+    size_t result = 0;
+    for (; *str != '\0'; str++) {
+      result = 5 * result + static_cast<size_t>(*str);
+    }
+    return result;
+  }
+};
+
+template<>
+struct hash<bool> {
+  size_t operator()(bool x) const {
+    return static_cast<size_t>(x);
+  }
+};
+
+template <>
+struct hash<std::string> {
+  inline size_t operator()(const std::string& key) const {
+    return hash<const char*>()(key.c_str());
+  }
+
+  static const size_t bucket_size = 4;
+  static const size_t min_buckets = 8;
+  inline bool operator()(const std::string& a, const std::string& b) const {
+    return a < b;
+  }
+};
+
+template <typename First, typename Second>
+struct hash<std::pair<First, Second> > {
+  inline size_t operator()(const std::pair<First, Second>& key) const {
+    size_t first_hash = hash<First>()(key.first);
+    size_t second_hash = hash<Second>()(key.second);
+
+    // FIXME(kenton):  What is the best way to compute this hash?  I have
+    // no idea!  This seems a bit better than an XOR.
+    return first_hash * ((1 << 16) - 1) + second_hash;
+  }
+
+  static const size_t bucket_size = 4;
+  static const size_t min_buckets = 8;
+  inline bool operator()(const std::pair<First, Second>& a,
+                           const std::pair<First, Second>& b) const {
+    return a < b;
+  }
+};
+
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_STUBS_HASH_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/int128.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/int128.h
new file mode 100644
index 0000000..92d7bdf
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/int128.h
@@ -0,0 +1,387 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+#ifndef GOOGLE_PROTOBUF_STUBS_INT128_H_
+#define GOOGLE_PROTOBUF_STUBS_INT128_H_
+
+#include <google/protobuf/stubs/common.h>
+
+#include <iosfwd>
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+struct uint128_pod;
+
+// TODO(xiaofeng): Define GOOGLE_PROTOBUF_HAS_CONSTEXPR when constexpr is
+// available.
+#ifdef GOOGLE_PROTOBUF_HAS_CONSTEXPR
+# define UINT128_CONSTEXPR constexpr
+#else
+# define UINT128_CONSTEXPR
+#endif
+
+// An unsigned 128-bit integer type. Thread-compatible.
+class PROTOBUF_EXPORT uint128 {
+ public:
+  UINT128_CONSTEXPR uint128();  // Sets to 0, but don't trust on this behavior.
+  UINT128_CONSTEXPR uint128(uint64_t top, uint64_t bottom);
+#ifndef SWIG
+  UINT128_CONSTEXPR uint128(int bottom);
+  UINT128_CONSTEXPR uint128(uint32_t bottom);   // Top 96 bits = 0
+#endif
+  UINT128_CONSTEXPR uint128(uint64_t bottom);   // hi_ = 0
+  UINT128_CONSTEXPR uint128(const uint128_pod &val);
+
+  // Trivial copy constructor, assignment operator and destructor.
+
+  void Initialize(uint64_t top, uint64_t bottom);
+
+  // Arithmetic operators.
+  uint128& operator+=(const uint128& b);
+  uint128& operator-=(const uint128& b);
+  uint128& operator*=(const uint128& b);
+  // Long division/modulo for uint128.
+  uint128& operator/=(const uint128& b);
+  uint128& operator%=(const uint128& b);
+  uint128 operator++(int);
+  uint128 operator--(int);
+  uint128& operator<<=(int);
+  uint128& operator>>=(int);
+  uint128& operator&=(const uint128& b);
+  uint128& operator|=(const uint128& b);
+  uint128& operator^=(const uint128& b);
+  uint128& operator++();
+  uint128& operator--();
+
+  friend uint64_t Uint128Low64(const uint128& v);
+  friend uint64_t Uint128High64(const uint128& v);
+
+  // We add "std::" to avoid including all of port.h.
+  PROTOBUF_EXPORT friend std::ostream& operator<<(std::ostream& o,
+                                                  const uint128& b);
+
+ private:
+  static void DivModImpl(uint128 dividend, uint128 divisor,
+                         uint128* quotient_ret, uint128* remainder_ret);
+
+  // Little-endian memory order optimizations can benefit from
+  // having lo_ first, hi_ last.
+  // See util/endian/endian.h and Load128/Store128 for storing a uint128.
+  uint64_t lo_;
+  uint64_t hi_;
+
+  // Not implemented, just declared for catching automatic type conversions.
+  uint128(uint8_t);
+  uint128(uint16_t);
+  uint128(float v);
+  uint128(double v);
+};
+
+// This is a POD form of uint128 which can be used for static variables which
+// need to be operated on as uint128.
+struct uint128_pod {
+  // Note: The ordering of fields is different than 'class uint128' but the
+  // same as its 2-arg constructor.  This enables more obvious initialization
+  // of static instances, which is the primary reason for this struct in the
+  // first place.  This does not seem to defeat any optimizations wrt
+  // operations involving this struct.
+  uint64_t hi;
+  uint64_t lo;
+};
+
+PROTOBUF_EXPORT extern const uint128_pod kuint128max;
+
+// allow uint128 to be logged
+PROTOBUF_EXPORT extern std::ostream& operator<<(std::ostream& o,
+                                                const uint128& b);
+
+// Methods to access low and high pieces of 128-bit value.
+// Defined externally from uint128 to facilitate conversion
+// to native 128-bit types when compilers support them.
+inline uint64_t Uint128Low64(const uint128& v) { return v.lo_; }
+inline uint64_t Uint128High64(const uint128& v) { return v.hi_; }
+
+// TODO: perhaps it would be nice to have int128, a signed 128-bit type?
+
+// --------------------------------------------------------------------------
+//                      Implementation details follow
+// --------------------------------------------------------------------------
+inline bool operator==(const uint128& lhs, const uint128& rhs) {
+  return (Uint128Low64(lhs) == Uint128Low64(rhs) &&
+          Uint128High64(lhs) == Uint128High64(rhs));
+}
+inline bool operator!=(const uint128& lhs, const uint128& rhs) {
+  return !(lhs == rhs);
+}
+
+inline UINT128_CONSTEXPR uint128::uint128() : lo_(0), hi_(0) {}
+inline UINT128_CONSTEXPR uint128::uint128(uint64_t top, uint64_t bottom)
+    : lo_(bottom), hi_(top) {}
+inline UINT128_CONSTEXPR uint128::uint128(const uint128_pod& v)
+    : lo_(v.lo), hi_(v.hi) {}
+inline UINT128_CONSTEXPR uint128::uint128(uint64_t bottom)
+    : lo_(bottom), hi_(0) {}
+#ifndef SWIG
+inline UINT128_CONSTEXPR uint128::uint128(uint32_t bottom)
+    : lo_(bottom), hi_(0) {}
+inline UINT128_CONSTEXPR uint128::uint128(int bottom)
+    : lo_(bottom), hi_(static_cast<int64_t>((bottom < 0) ? -1 : 0)) {}
+#endif
+
+#undef UINT128_CONSTEXPR
+
+inline void uint128::Initialize(uint64_t top, uint64_t bottom) {
+  hi_ = top;
+  lo_ = bottom;
+}
+
+// Comparison operators.
+
+#define CMP128(op)                                                \
+inline bool operator op(const uint128& lhs, const uint128& rhs) { \
+  return (Uint128High64(lhs) == Uint128High64(rhs)) ?             \
+      (Uint128Low64(lhs) op Uint128Low64(rhs)) :                  \
+      (Uint128High64(lhs) op Uint128High64(rhs));                 \
+}
+
+CMP128(<)
+CMP128(>)
+CMP128(>=)
+CMP128(<=)
+
+#undef CMP128
+
+// Unary operators
+
+inline uint128 operator-(const uint128& val) {
+  const uint64_t hi_flip = ~Uint128High64(val);
+  const uint64_t lo_flip = ~Uint128Low64(val);
+  const uint64_t lo_add = lo_flip + 1;
+  if (lo_add < lo_flip) {
+    return uint128(hi_flip + 1, lo_add);
+  }
+  return uint128(hi_flip, lo_add);
+}
+
+inline bool operator!(const uint128& val) {
+  return !Uint128High64(val) && !Uint128Low64(val);
+}
+
+// Logical operators.
+
+inline uint128 operator~(const uint128& val) {
+  return uint128(~Uint128High64(val), ~Uint128Low64(val));
+}
+
+#define LOGIC128(op)                                                 \
+inline uint128 operator op(const uint128& lhs, const uint128& rhs) { \
+  return uint128(Uint128High64(lhs) op Uint128High64(rhs),           \
+                 Uint128Low64(lhs) op Uint128Low64(rhs));            \
+}
+
+LOGIC128(|)
+LOGIC128(&)
+LOGIC128(^)
+
+#undef LOGIC128
+
+#define LOGICASSIGN128(op)                                   \
+inline uint128& uint128::operator op(const uint128& other) { \
+  hi_ op other.hi_;                                          \
+  lo_ op other.lo_;                                          \
+  return *this;                                              \
+}
+
+LOGICASSIGN128(|=)
+LOGICASSIGN128(&=)
+LOGICASSIGN128(^=)
+
+#undef LOGICASSIGN128
+
+// Shift operators.
+
+inline uint128 operator<<(const uint128& val, int amount) {
+  // uint64 shifts of >= 64 are undefined, so we will need some special-casing.
+  if (amount < 64) {
+    if (amount == 0) {
+      return val;
+    }
+    uint64_t new_hi = (Uint128High64(val) << amount) |
+                      (Uint128Low64(val) >> (64 - amount));
+    uint64_t new_lo = Uint128Low64(val) << amount;
+    return uint128(new_hi, new_lo);
+  } else if (amount < 128) {
+    return uint128(Uint128Low64(val) << (amount - 64), 0);
+  } else {
+    return uint128(0, 0);
+  }
+}
+
+inline uint128 operator>>(const uint128& val, int amount) {
+  // uint64 shifts of >= 64 are undefined, so we will need some special-casing.
+  if (amount < 64) {
+    if (amount == 0) {
+      return val;
+    }
+    uint64_t new_hi = Uint128High64(val) >> amount;
+    uint64_t new_lo = (Uint128Low64(val) >> amount) |
+                      (Uint128High64(val) << (64 - amount));
+    return uint128(new_hi, new_lo);
+  } else if (amount < 128) {
+    return uint128(0, Uint128High64(val) >> (amount - 64));
+  } else {
+    return uint128(0, 0);
+  }
+}
+
+inline uint128& uint128::operator<<=(int amount) {
+  // uint64 shifts of >= 64 are undefined, so we will need some special-casing.
+  if (amount < 64) {
+    if (amount != 0) {
+      hi_ = (hi_ << amount) | (lo_ >> (64 - amount));
+      lo_ = lo_ << amount;
+    }
+  } else if (amount < 128) {
+    hi_ = lo_ << (amount - 64);
+    lo_ = 0;
+  } else {
+    hi_ = 0;
+    lo_ = 0;
+  }
+  return *this;
+}
+
+inline uint128& uint128::operator>>=(int amount) {
+  // uint64 shifts of >= 64 are undefined, so we will need some special-casing.
+  if (amount < 64) {
+    if (amount != 0) {
+      lo_ = (lo_ >> amount) | (hi_ << (64 - amount));
+      hi_ = hi_ >> amount;
+    }
+  } else if (amount < 128) {
+    lo_ = hi_ >> (amount - 64);
+    hi_ = 0;
+  } else {
+    lo_ = 0;
+    hi_ = 0;
+  }
+  return *this;
+}
+
+inline uint128 operator+(const uint128& lhs, const uint128& rhs) {
+  return uint128(lhs) += rhs;
+}
+
+inline uint128 operator-(const uint128& lhs, const uint128& rhs) {
+  return uint128(lhs) -= rhs;
+}
+
+inline uint128 operator*(const uint128& lhs, const uint128& rhs) {
+  return uint128(lhs) *= rhs;
+}
+
+inline uint128 operator/(const uint128& lhs, const uint128& rhs) {
+  return uint128(lhs) /= rhs;
+}
+
+inline uint128 operator%(const uint128& lhs, const uint128& rhs) {
+  return uint128(lhs) %= rhs;
+}
+
+inline uint128& uint128::operator+=(const uint128& b) {
+  hi_ += b.hi_;
+  uint64_t lolo = lo_ + b.lo_;
+  if (lolo < lo_)
+    ++hi_;
+  lo_ = lolo;
+  return *this;
+}
+
+inline uint128& uint128::operator-=(const uint128& b) {
+  hi_ -= b.hi_;
+  if (b.lo_ > lo_)
+    --hi_;
+  lo_ -= b.lo_;
+  return *this;
+}
+
+inline uint128& uint128::operator*=(const uint128& b) {
+  uint64_t a96 = hi_ >> 32;
+  uint64_t a64 = hi_ & 0xffffffffu;
+  uint64_t a32 = lo_ >> 32;
+  uint64_t a00 = lo_ & 0xffffffffu;
+  uint64_t b96 = b.hi_ >> 32;
+  uint64_t b64 = b.hi_ & 0xffffffffu;
+  uint64_t b32 = b.lo_ >> 32;
+  uint64_t b00 = b.lo_ & 0xffffffffu;
+  // multiply [a96 .. a00] x [b96 .. b00]
+  // terms higher than c96 disappear off the high side
+  // terms c96 and c64 are safe to ignore carry bit
+  uint64_t c96 = a96 * b00 + a64 * b32 + a32 * b64 + a00 * b96;
+  uint64_t c64 = a64 * b00 + a32 * b32 + a00 * b64;
+  this->hi_ = (c96 << 32) + c64;
+  this->lo_ = 0;
+  // add terms after this one at a time to capture carry
+  *this += uint128(a32 * b00) << 32;
+  *this += uint128(a00 * b32) << 32;
+  *this += a00 * b00;
+  return *this;
+}
+
+inline uint128 uint128::operator++(int) {
+  uint128 tmp(*this);
+  *this += 1;
+  return tmp;
+}
+
+inline uint128 uint128::operator--(int) {
+  uint128 tmp(*this);
+  *this -= 1;
+  return tmp;
+}
+
+inline uint128& uint128::operator++() {
+  *this += 1;
+  return *this;
+}
+
+inline uint128& uint128::operator--() {
+  *this -= 1;
+  return *this;
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_INT128_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/logging.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/logging.h
new file mode 100644
index 0000000..8ecc2fa
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/logging.h
@@ -0,0 +1,239 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_STUBS_LOGGING_H_
+#define GOOGLE_PROTOBUF_STUBS_LOGGING_H_
+
+#include <google/protobuf/stubs/macros.h>
+#include <google/protobuf/stubs/port.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/stringpiece.h>
+
+#include <google/protobuf/port_def.inc>
+
+// ===================================================================
+// emulates google3/base/logging.h
+
+namespace google {
+namespace protobuf {
+
+enum LogLevel {
+  LOGLEVEL_INFO,     // Informational.  This is never actually used by
+                     // libprotobuf.
+  LOGLEVEL_WARNING,  // Warns about issues that, although not technically a
+                     // problem now, could cause problems in the future.  For
+                     // example, a // warning will be printed when parsing a
+                     // message that is near the message size limit.
+  LOGLEVEL_ERROR,    // An error occurred which should never happen during
+                     // normal use.
+  LOGLEVEL_FATAL,    // An error occurred from which the library cannot
+                     // recover.  This usually indicates a programming error
+                     // in the code which calls the library, especially when
+                     // compiled in debug mode.
+
+#ifdef NDEBUG
+  LOGLEVEL_DFATAL = LOGLEVEL_ERROR
+#else
+  LOGLEVEL_DFATAL = LOGLEVEL_FATAL
+#endif
+};
+
+class uint128;
+namespace internal {
+
+class LogFinisher;
+
+class PROTOBUF_EXPORT LogMessage {
+ public:
+  LogMessage(LogLevel level, const char* filename, int line);
+  ~LogMessage();
+
+  LogMessage& operator<<(const std::string& value);
+  LogMessage& operator<<(const char* value);
+  LogMessage& operator<<(char value);
+  LogMessage& operator<<(int value);
+  LogMessage& operator<<(uint value);
+  LogMessage& operator<<(long value);
+  LogMessage& operator<<(unsigned long value);
+  LogMessage& operator<<(long long value);
+  LogMessage& operator<<(unsigned long long value);
+  LogMessage& operator<<(double value);
+  LogMessage& operator<<(void* value);
+  LogMessage& operator<<(const StringPiece& value);
+  LogMessage& operator<<(const util::Status& status);
+  LogMessage& operator<<(const uint128& value);
+
+ private:
+  friend class LogFinisher;
+  void Finish();
+
+  LogLevel level_;
+  const char* filename_;
+  int line_;
+  std::string message_;
+};
+
+// Used to make the entire "LOG(BLAH) << etc." expression have a void return
+// type and print a newline after each message.
+class PROTOBUF_EXPORT LogFinisher {
+ public:
+  void operator=(LogMessage& other);
+};
+
+template<typename T>
+bool IsOk(T status) { return status.ok(); }
+template<>
+inline bool IsOk(bool status) { return status; }
+
+}  // namespace internal
+
+// Undef everything in case we're being mixed with some other Google library
+// which already defined them itself.  Presumably all Google libraries will
+// support the same syntax for these so it should not be a big deal if they
+// end up using our definitions instead.
+#undef GOOGLE_LOG
+#undef GOOGLE_LOG_IF
+
+#undef GOOGLE_CHECK
+#undef GOOGLE_CHECK_OK
+#undef GOOGLE_CHECK_EQ
+#undef GOOGLE_CHECK_NE
+#undef GOOGLE_CHECK_LT
+#undef GOOGLE_CHECK_LE
+#undef GOOGLE_CHECK_GT
+#undef GOOGLE_CHECK_GE
+#undef GOOGLE_CHECK_NOTNULL
+
+#undef GOOGLE_DLOG
+#undef GOOGLE_DCHECK
+#undef GOOGLE_DCHECK_OK
+#undef GOOGLE_DCHECK_EQ
+#undef GOOGLE_DCHECK_NE
+#undef GOOGLE_DCHECK_LT
+#undef GOOGLE_DCHECK_LE
+#undef GOOGLE_DCHECK_GT
+#undef GOOGLE_DCHECK_GE
+
+#define GOOGLE_LOG(LEVEL)                          \
+  ::google::protobuf::internal::LogFinisher() = \
+      ::google::protobuf::internal::LogMessage( \
+          ::google::protobuf::LOGLEVEL_##LEVEL, __FILE__, __LINE__)
+#define GOOGLE_LOG_IF(LEVEL, CONDITION) \
+  !(CONDITION) ? (void)0 : GOOGLE_LOG(LEVEL)
+
+#define GOOGLE_CHECK(EXPRESSION) \
+  GOOGLE_LOG_IF(FATAL, !(EXPRESSION)) << "CHECK failed: " #EXPRESSION ": "
+#define GOOGLE_CHECK_OK(A) GOOGLE_CHECK(::google::protobuf::internal::IsOk(A))
+#define GOOGLE_CHECK_EQ(A, B) GOOGLE_CHECK((A) == (B))
+#define GOOGLE_CHECK_NE(A, B) GOOGLE_CHECK((A) != (B))
+#define GOOGLE_CHECK_LT(A, B) GOOGLE_CHECK((A) <  (B))
+#define GOOGLE_CHECK_LE(A, B) GOOGLE_CHECK((A) <= (B))
+#define GOOGLE_CHECK_GT(A, B) GOOGLE_CHECK((A) >  (B))
+#define GOOGLE_CHECK_GE(A, B) GOOGLE_CHECK((A) >= (B))
+
+namespace internal {
+template<typename T>
+T* CheckNotNull(const char* /* file */, int /* line */,
+                const char* name, T* val) {
+  if (val == nullptr) {
+    GOOGLE_LOG(FATAL) << name;
+  }
+  return val;
+}
+}  // namespace internal
+#define GOOGLE_CHECK_NOTNULL(A)               \
+  ::google::protobuf::internal::CheckNotNull( \
+      __FILE__, __LINE__, "'" #A "' must not be nullptr", (A))
+
+#ifdef NDEBUG
+
+#define GOOGLE_DLOG(LEVEL) GOOGLE_LOG_IF(LEVEL, false)
+
+#define GOOGLE_DCHECK(EXPRESSION) while(false) GOOGLE_CHECK(EXPRESSION)
+#define GOOGLE_DCHECK_OK(E) GOOGLE_DCHECK(::google::protobuf::internal::IsOk(E))
+#define GOOGLE_DCHECK_EQ(A, B) GOOGLE_DCHECK((A) == (B))
+#define GOOGLE_DCHECK_NE(A, B) GOOGLE_DCHECK((A) != (B))
+#define GOOGLE_DCHECK_LT(A, B) GOOGLE_DCHECK((A) <  (B))
+#define GOOGLE_DCHECK_LE(A, B) GOOGLE_DCHECK((A) <= (B))
+#define GOOGLE_DCHECK_GT(A, B) GOOGLE_DCHECK((A) >  (B))
+#define GOOGLE_DCHECK_GE(A, B) GOOGLE_DCHECK((A) >= (B))
+
+#else  // NDEBUG
+
+#define GOOGLE_DLOG GOOGLE_LOG
+
+#define GOOGLE_DCHECK    GOOGLE_CHECK
+#define GOOGLE_DCHECK_OK GOOGLE_CHECK_OK
+#define GOOGLE_DCHECK_EQ GOOGLE_CHECK_EQ
+#define GOOGLE_DCHECK_NE GOOGLE_CHECK_NE
+#define GOOGLE_DCHECK_LT GOOGLE_CHECK_LT
+#define GOOGLE_DCHECK_LE GOOGLE_CHECK_LE
+#define GOOGLE_DCHECK_GT GOOGLE_CHECK_GT
+#define GOOGLE_DCHECK_GE GOOGLE_CHECK_GE
+
+#endif  // !NDEBUG
+
+typedef void LogHandler(LogLevel level, const char* filename, int line,
+                        const std::string& message);
+
+// The protobuf library sometimes writes warning and error messages to
+// stderr.  These messages are primarily useful for developers, but may
+// also help end users figure out a problem.  If you would prefer that
+// these messages be sent somewhere other than stderr, call SetLogHandler()
+// to set your own handler.  This returns the old handler.  Set the handler
+// to nullptr to ignore log messages (but see also LogSilencer, below).
+//
+// Obviously, SetLogHandler is not thread-safe.  You should only call it
+// at initialization time, and probably not from library code.  If you
+// simply want to suppress log messages temporarily (e.g. because you
+// have some code that tends to trigger them frequently and you know
+// the warnings are not important to you), use the LogSilencer class
+// below.
+PROTOBUF_EXPORT LogHandler* SetLogHandler(LogHandler* new_func);
+
+// Create a LogSilencer if you want to temporarily suppress all log
+// messages.  As long as any LogSilencer objects exist, non-fatal
+// log messages will be discarded (the current LogHandler will *not*
+// be called).  Constructing a LogSilencer is thread-safe.  You may
+// accidentally suppress log messages occurring in another thread, but
+// since messages are generally for debugging purposes only, this isn't
+// a big deal.  If you want to intercept log messages, use SetLogHandler().
+class PROTOBUF_EXPORT LogSilencer {
+ public:
+  LogSilencer();
+  ~LogSilencer();
+};
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_LOGGING_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/macros.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/macros.h
new file mode 100644
index 0000000..ae9a8b9
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/macros.h
@@ -0,0 +1,93 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_MACROS_H__
+#define GOOGLE_PROTOBUF_MACROS_H__
+
+namespace google {
+namespace protobuf {
+
+#undef GOOGLE_DISALLOW_EVIL_CONSTRUCTORS
+#define GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
+  TypeName(const TypeName&) = delete;               \
+  void operator=(const TypeName&) = delete
+
+#undef GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS
+#define GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+  TypeName() = delete;                                  \
+  TypeName(const TypeName&) = delete;                   \
+  void operator=(const TypeName&) = delete
+
+// ===================================================================
+// from google3/base/basictypes.h
+
+// The GOOGLE_ARRAYSIZE(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example.
+//
+// GOOGLE_ARRAYSIZE catches a few type errors.  If you see a compiler error
+//
+//   "warning: division by zero in ..."
+//
+// when using GOOGLE_ARRAYSIZE, you are (wrongfully) giving it a pointer.
+// You should only use GOOGLE_ARRAYSIZE on statically allocated arrays.
+//
+// The following comments are on the implementation details, and can
+// be ignored by the users.
+//
+// ARRAYSIZE(arr) works by inspecting sizeof(arr) (the # of bytes in
+// the array) and sizeof(*(arr)) (the # of bytes in one array
+// element).  If the former is divisible by the latter, perhaps arr is
+// indeed an array, in which case the division result is the # of
+// elements in the array.  Otherwise, arr cannot possibly be an array,
+// and we generate a compiler error to prevent the code from
+// compiling.
+//
+// Since the size of bool is implementation-defined, we need to cast
+// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
+// result has type size_t.
+//
+// This macro is not perfect as it wrongfully accepts certain
+// pointers, namely where the pointer size is divisible by the pointee
+// size.  Since all our code has to go through a 32-bit compiler,
+// where a pointer is 4 bytes, this means all pointers to a type whose
+// size is 3 or greater than 4 will be (righteously) rejected.
+//
+// Kudos to Jorg Brown for this simple and elegant implementation.
+
+#undef GOOGLE_ARRAYSIZE
+#define GOOGLE_ARRAYSIZE(a) \
+  ((sizeof(a) / sizeof(*(a))) / \
+   static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_MACROS_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/map_util.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/map_util.h
new file mode 100644
index 0000000..24e098a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/map_util.h
@@ -0,0 +1,769 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2014 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// from google3/util/gtl/map_util.h
+// Author: Anton Carver
+
+#ifndef GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__
+#define GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__
+
+#include <stddef.h>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+// Local implementation of RemoveConst to avoid including base/type_traits.h.
+template <class T> struct RemoveConst { typedef T type; };
+template <class T> struct RemoveConst<const T> : RemoveConst<T> {};
+}  // namespace internal
+
+//
+// Find*()
+//
+
+// Returns a const reference to the value associated with the given key if it
+// exists. Crashes otherwise.
+//
+// This is intended as a replacement for operator[] as an rvalue (for reading)
+// when the key is guaranteed to exist.
+//
+// operator[] for lookup is discouraged for several reasons:
+//  * It has a side-effect of inserting missing keys
+//  * It is not thread-safe (even when it is not inserting, it can still
+//      choose to resize the underlying storage)
+//  * It invalidates iterators (when it chooses to resize)
+//  * It default constructs a value object even if it doesn't need to
+//
+// This version assumes the key is printable, and includes it in the fatal log
+// message.
+template <class Collection>
+const typename Collection::value_type::second_type&
+FindOrDie(const Collection& collection,
+          const typename Collection::value_type::first_type& key) {
+  typename Collection::const_iterator it = collection.find(key);
+  GOOGLE_CHECK(it != collection.end()) << "Map key not found: " << key;
+  return it->second;
+}
+
+// Same as above, but returns a non-const reference.
+template <class Collection>
+typename Collection::value_type::second_type&
+FindOrDie(Collection& collection,  // NOLINT
+          const typename Collection::value_type::first_type& key) {
+  typename Collection::iterator it = collection.find(key);
+  GOOGLE_CHECK(it != collection.end()) << "Map key not found: " << key;
+  return it->second;
+}
+
+// Same as FindOrDie above, but doesn't log the key on failure.
+template <class Collection>
+const typename Collection::value_type::second_type&
+FindOrDieNoPrint(const Collection& collection,
+                 const typename Collection::value_type::first_type& key) {
+  typename Collection::const_iterator it = collection.find(key);
+  GOOGLE_CHECK(it != collection.end()) << "Map key not found";
+  return it->second;
+}
+
+// Same as above, but returns a non-const reference.
+template <class Collection>
+typename Collection::value_type::second_type&
+FindOrDieNoPrint(Collection& collection,  // NOLINT
+                 const typename Collection::value_type::first_type& key) {
+  typename Collection::iterator it = collection.find(key);
+  GOOGLE_CHECK(it != collection.end()) << "Map key not found";
+  return it->second;
+}
+
+// Returns a const reference to the value associated with the given key if it
+// exists, otherwise returns a const reference to the provided default value.
+//
+// WARNING: If a temporary object is passed as the default "value,"
+// this function will return a reference to that temporary object,
+// which will be destroyed at the end of the statement. A common
+// example: if you have a map with string values, and you pass a char*
+// as the default "value," either use the returned value immediately
+// or store it in a string (not string&).
+// Details: http://go/findwithdefault
+template <class Collection>
+const typename Collection::value_type::second_type&
+FindWithDefault(const Collection& collection,
+                const typename Collection::value_type::first_type& key,
+                const typename Collection::value_type::second_type& value) {
+  typename Collection::const_iterator it = collection.find(key);
+  if (it == collection.end()) {
+    return value;
+  }
+  return it->second;
+}
+
+// Returns a pointer to the const value associated with the given key if it
+// exists, or nullptr otherwise.
+template <class Collection>
+const typename Collection::value_type::second_type*
+FindOrNull(const Collection& collection,
+           const typename Collection::value_type::first_type& key) {
+  typename Collection::const_iterator it = collection.find(key);
+  if (it == collection.end()) {
+    return 0;
+  }
+  return &it->second;
+}
+
+// Same as above but returns a pointer to the non-const value.
+template <class Collection>
+typename Collection::value_type::second_type*
+FindOrNull(Collection& collection,  // NOLINT
+           const typename Collection::value_type::first_type& key) {
+  typename Collection::iterator it = collection.find(key);
+  if (it == collection.end()) {
+    return 0;
+  }
+  return &it->second;
+}
+
+// Returns the pointer value associated with the given key. If none is found,
+// nullptr is returned. The function is designed to be used with a map of keys to
+// pointers.
+//
+// This function does not distinguish between a missing key and a key mapped
+// to nullptr.
+template <class Collection>
+typename Collection::value_type::second_type
+FindPtrOrNull(const Collection& collection,
+              const typename Collection::value_type::first_type& key) {
+  typename Collection::const_iterator it = collection.find(key);
+  if (it == collection.end()) {
+    return typename Collection::value_type::second_type();
+  }
+  return it->second;
+}
+
+// Same as above, except takes non-const reference to collection.
+//
+// This function is needed for containers that propagate constness to the
+// pointee, such as boost::ptr_map.
+template <class Collection>
+typename Collection::value_type::second_type
+FindPtrOrNull(Collection& collection,  // NOLINT
+              const typename Collection::value_type::first_type& key) {
+  typename Collection::iterator it = collection.find(key);
+  if (it == collection.end()) {
+    return typename Collection::value_type::second_type();
+  }
+  return it->second;
+}
+
+// Finds the pointer value associated with the given key in a map whose values
+// are linked_ptrs. Returns nullptr if key is not found.
+template <class Collection>
+typename Collection::value_type::second_type::element_type*
+FindLinkedPtrOrNull(const Collection& collection,
+                    const typename Collection::value_type::first_type& key) {
+  typename Collection::const_iterator it = collection.find(key);
+  if (it == collection.end()) {
+    return 0;
+  }
+  // Since linked_ptr::get() is a const member returning a non const,
+  // we do not need a version of this function taking a non const collection.
+  return it->second.get();
+}
+
+// Same as above, but dies if the key is not found.
+template <class Collection>
+typename Collection::value_type::second_type::element_type&
+FindLinkedPtrOrDie(const Collection& collection,
+                   const typename Collection::value_type::first_type& key) {
+  typename Collection::const_iterator it = collection.find(key);
+  GOOGLE_CHECK(it != collection.end()) <<  "key not found: " << key;
+  // Since linked_ptr::operator*() is a const member returning a non const,
+  // we do not need a version of this function taking a non const collection.
+  return *it->second;
+}
+
+// Finds the value associated with the given key and copies it to *value (if not
+// nullptr). Returns false if the key was not found, true otherwise.
+template <class Collection, class Key, class Value>
+bool FindCopy(const Collection& collection,
+              const Key& key,
+              Value* const value) {
+  typename Collection::const_iterator it = collection.find(key);
+  if (it == collection.end()) {
+    return false;
+  }
+  if (value) {
+    *value = it->second;
+  }
+  return true;
+}
+
+//
+// Contains*()
+//
+
+// Returns true if and only if the given collection contains the given key.
+template <class Collection, class Key>
+bool ContainsKey(const Collection& collection, const Key& key) {
+  return collection.find(key) != collection.end();
+}
+
+// Returns true if and only if the given collection contains the given key-value
+// pair.
+template <class Collection, class Key, class Value>
+bool ContainsKeyValuePair(const Collection& collection,
+                          const Key& key,
+                          const Value& value) {
+  typedef typename Collection::const_iterator const_iterator;
+  std::pair<const_iterator, const_iterator> range = collection.equal_range(key);
+  for (const_iterator it = range.first; it != range.second; ++it) {
+    if (it->second == value) {
+      return true;
+    }
+  }
+  return false;
+}
+
+//
+// Insert*()
+//
+
+// Inserts the given key-value pair into the collection. Returns true if and
+// only if the key from the given pair didn't previously exist. Otherwise, the
+// value in the map is replaced with the value from the given pair.
+template <class Collection>
+bool InsertOrUpdate(Collection* const collection,
+                    const typename Collection::value_type& vt) {
+  std::pair<typename Collection::iterator, bool> ret = collection->insert(vt);
+  if (!ret.second) {
+    // update
+    ret.first->second = vt.second;
+    return false;
+  }
+  return true;
+}
+
+// Same as above, except that the key and value are passed separately.
+template <class Collection>
+bool InsertOrUpdate(Collection* const collection,
+                    const typename Collection::value_type::first_type& key,
+                    const typename Collection::value_type::second_type& value) {
+  return InsertOrUpdate(
+      collection, typename Collection::value_type(key, value));
+}
+
+// Inserts/updates all the key-value pairs from the range defined by the
+// iterators "first" and "last" into the given collection.
+template <class Collection, class InputIterator>
+void InsertOrUpdateMany(Collection* const collection,
+                        InputIterator first, InputIterator last) {
+  for (; first != last; ++first) {
+    InsertOrUpdate(collection, *first);
+  }
+}
+
+// Change the value associated with a particular key in a map or hash_map
+// of the form map<Key, Value*> which owns the objects pointed to by the
+// value pointers.  If there was an existing value for the key, it is deleted.
+// True indicates an insert took place, false indicates an update + delete.
+template <class Collection>
+bool InsertAndDeleteExisting(
+    Collection* const collection,
+    const typename Collection::value_type::first_type& key,
+    const typename Collection::value_type::second_type& value) {
+  std::pair<typename Collection::iterator, bool> ret =
+      collection->insert(typename Collection::value_type(key, value));
+  if (!ret.second) {
+    delete ret.first->second;
+    ret.first->second = value;
+    return false;
+  }
+  return true;
+}
+
+// Inserts the given key and value into the given collection if and only if the
+// given key did NOT already exist in the collection. If the key previously
+// existed in the collection, the value is not changed. Returns true if the
+// key-value pair was inserted; returns false if the key was already present.
+template <class Collection>
+bool InsertIfNotPresent(Collection* const collection,
+                        const typename Collection::value_type& vt) {
+  return collection->insert(vt).second;
+}
+
+// Same as above except the key and value are passed separately.
+template <class Collection>
+bool InsertIfNotPresent(
+    Collection* const collection,
+    const typename Collection::value_type::first_type& key,
+    const typename Collection::value_type::second_type& value) {
+  return InsertIfNotPresent(
+      collection, typename Collection::value_type(key, value));
+}
+
+// Same as above except dies if the key already exists in the collection.
+template <class Collection>
+void InsertOrDie(Collection* const collection,
+                 const typename Collection::value_type& value) {
+  GOOGLE_CHECK(InsertIfNotPresent(collection, value))
+      << "duplicate value: " << value;
+}
+
+// Same as above except doesn't log the value on error.
+template <class Collection>
+void InsertOrDieNoPrint(Collection* const collection,
+                        const typename Collection::value_type& value) {
+  GOOGLE_CHECK(InsertIfNotPresent(collection, value)) << "duplicate value.";
+}
+
+// Inserts the key-value pair into the collection. Dies if key was already
+// present.
+template <class Collection>
+void InsertOrDie(Collection* const collection,
+                 const typename Collection::value_type::first_type& key,
+                 const typename Collection::value_type::second_type& data) {
+  GOOGLE_CHECK(InsertIfNotPresent(collection, key, data))
+      << "duplicate key: " << key;
+}
+
+// Same as above except doesn't log the key on error.
+template <class Collection>
+void InsertOrDieNoPrint(
+    Collection* const collection,
+    const typename Collection::value_type::first_type& key,
+    const typename Collection::value_type::second_type& data) {
+  GOOGLE_CHECK(InsertIfNotPresent(collection, key, data)) << "duplicate key.";
+}
+
+// Inserts a new key and default-initialized value. Dies if the key was already
+// present. Returns a reference to the value. Example usage:
+//
+// map<int, SomeProto> m;
+// SomeProto& proto = InsertKeyOrDie(&m, 3);
+// proto.set_field("foo");
+template <class Collection>
+typename Collection::value_type::second_type& InsertKeyOrDie(
+    Collection* const collection,
+    const typename Collection::value_type::first_type& key) {
+  typedef typename Collection::value_type value_type;
+  std::pair<typename Collection::iterator, bool> res =
+      collection->insert(value_type(key, typename value_type::second_type()));
+  GOOGLE_CHECK(res.second) << "duplicate key: " << key;
+  return res.first->second;
+}
+
+//
+// Lookup*()
+//
+
+// Looks up a given key and value pair in a collection and inserts the key-value
+// pair if it's not already present. Returns a reference to the value associated
+// with the key.
+template <class Collection>
+typename Collection::value_type::second_type&
+LookupOrInsert(Collection* const collection,
+               const typename Collection::value_type& vt) {
+  return collection->insert(vt).first->second;
+}
+
+// Same as above except the key-value are passed separately.
+template <class Collection>
+typename Collection::value_type::second_type&
+LookupOrInsert(Collection* const collection,
+               const typename Collection::value_type::first_type& key,
+               const typename Collection::value_type::second_type& value) {
+  return LookupOrInsert(
+      collection, typename Collection::value_type(key, value));
+}
+
+// Counts the number of equivalent elements in the given "sequence", and stores
+// the results in "count_map" with element as the key and count as the value.
+//
+// Example:
+//   vector<string> v = {"a", "b", "c", "a", "b"};
+//   map<string, int> m;
+//   AddTokenCounts(v, 1, &m);
+//   assert(m["a"] == 2);
+//   assert(m["b"] == 2);
+//   assert(m["c"] == 1);
+template <typename Sequence, typename Collection>
+void AddTokenCounts(
+    const Sequence& sequence,
+    const typename Collection::value_type::second_type& increment,
+    Collection* const count_map) {
+  for (typename Sequence::const_iterator it = sequence.begin();
+       it != sequence.end(); ++it) {
+    typename Collection::value_type::second_type& value =
+        LookupOrInsert(count_map, *it,
+                       typename Collection::value_type::second_type());
+    value += increment;
+  }
+}
+
+// Returns a reference to the value associated with key. If not found, a value
+// is default constructed on the heap and added to the map.
+//
+// This function is useful for containers of the form map<Key, Value*>, where
+// inserting a new key, value pair involves constructing a new heap-allocated
+// Value, and storing a pointer to that in the collection.
+template <class Collection>
+typename Collection::value_type::second_type&
+LookupOrInsertNew(Collection* const collection,
+                  const typename Collection::value_type::first_type& key) {
+  typedef typename std::iterator_traits<
+    typename Collection::value_type::second_type>::value_type Element;
+  std::pair<typename Collection::iterator, bool> ret =
+      collection->insert(typename Collection::value_type(
+          key,
+          static_cast<typename Collection::value_type::second_type>(nullptr)));
+  if (ret.second) {
+    ret.first->second = new Element();
+  }
+  return ret.first->second;
+}
+
+// Same as above but constructs the value using the single-argument constructor
+// and the given "arg".
+template <class Collection, class Arg>
+typename Collection::value_type::second_type&
+LookupOrInsertNew(Collection* const collection,
+                  const typename Collection::value_type::first_type& key,
+                  const Arg& arg) {
+  typedef typename std::iterator_traits<
+    typename Collection::value_type::second_type>::value_type Element;
+  std::pair<typename Collection::iterator, bool> ret =
+      collection->insert(typename Collection::value_type(
+          key,
+          static_cast<typename Collection::value_type::second_type>(nullptr)));
+  if (ret.second) {
+    ret.first->second = new Element(arg);
+  }
+  return ret.first->second;
+}
+
+// Lookup of linked/shared pointers is used in two scenarios:
+//
+// Use LookupOrInsertNewLinkedPtr if the container owns the elements.
+// In this case it is fine working with the raw pointer as long as it is
+// guaranteed that no other thread can delete/update an accessed element.
+// A mutex will need to lock the container operation as well as the use
+// of the returned elements. Finding an element may be performed using
+// FindLinkedPtr*().
+//
+// Use LookupOrInsertNewSharedPtr if the container does not own the elements
+// for their whole lifetime. This is typically the case when a reader allows
+// parallel updates to the container. In this case a Mutex only needs to lock
+// container operations, but all element operations must be performed on the
+// shared pointer. Finding an element must be performed using FindPtr*() and
+// cannot be done with FindLinkedPtr*() even though it compiles.
+
+// Lookup a key in a map or hash_map whose values are linked_ptrs.  If it is
+// missing, set collection[key].reset(new Value::element_type) and return that.
+// Value::element_type must be default constructable.
+template <class Collection>
+typename Collection::value_type::second_type::element_type*
+LookupOrInsertNewLinkedPtr(
+    Collection* const collection,
+    const typename Collection::value_type::first_type& key) {
+  typedef typename Collection::value_type::second_type Value;
+  std::pair<typename Collection::iterator, bool> ret =
+      collection->insert(typename Collection::value_type(key, Value()));
+  if (ret.second) {
+    ret.first->second.reset(new typename Value::element_type);
+  }
+  return ret.first->second.get();
+}
+
+// A variant of LookupOrInsertNewLinkedPtr where the value is constructed using
+// a single-parameter constructor.  Note: the constructor argument is computed
+// even if it will not be used, so only values cheap to compute should be passed
+// here.  On the other hand it does not matter how expensive the construction of
+// the actual stored value is, as that only occurs if necessary.
+template <class Collection, class Arg>
+typename Collection::value_type::second_type::element_type*
+LookupOrInsertNewLinkedPtr(
+    Collection* const collection,
+    const typename Collection::value_type::first_type& key,
+    const Arg& arg) {
+  typedef typename Collection::value_type::second_type Value;
+  std::pair<typename Collection::iterator, bool> ret =
+      collection->insert(typename Collection::value_type(key, Value()));
+  if (ret.second) {
+    ret.first->second.reset(new typename Value::element_type(arg));
+  }
+  return ret.first->second.get();
+}
+
+// Lookup a key in a map or hash_map whose values are shared_ptrs.  If it is
+// missing, set collection[key].reset(new Value::element_type). Unlike
+// LookupOrInsertNewLinkedPtr, this function returns the shared_ptr instead of
+// the raw pointer. Value::element_type must be default constructable.
+template <class Collection>
+typename Collection::value_type::second_type&
+LookupOrInsertNewSharedPtr(
+    Collection* const collection,
+    const typename Collection::value_type::first_type& key) {
+  typedef typename Collection::value_type::second_type SharedPtr;
+  typedef typename Collection::value_type::second_type::element_type Element;
+  std::pair<typename Collection::iterator, bool> ret =
+      collection->insert(typename Collection::value_type(key, SharedPtr()));
+  if (ret.second) {
+    ret.first->second.reset(new Element());
+  }
+  return ret.first->second;
+}
+
+// A variant of LookupOrInsertNewSharedPtr where the value is constructed using
+// a single-parameter constructor.  Note: the constructor argument is computed
+// even if it will not be used, so only values cheap to compute should be passed
+// here.  On the other hand it does not matter how expensive the construction of
+// the actual stored value is, as that only occurs if necessary.
+template <class Collection, class Arg>
+typename Collection::value_type::second_type&
+LookupOrInsertNewSharedPtr(
+    Collection* const collection,
+    const typename Collection::value_type::first_type& key,
+    const Arg& arg) {
+  typedef typename Collection::value_type::second_type SharedPtr;
+  typedef typename Collection::value_type::second_type::element_type Element;
+  std::pair<typename Collection::iterator, bool> ret =
+      collection->insert(typename Collection::value_type(key, SharedPtr()));
+  if (ret.second) {
+    ret.first->second.reset(new Element(arg));
+  }
+  return ret.first->second;
+}
+
+//
+// Misc Utility Functions
+//
+
+// Updates the value associated with the given key. If the key was not already
+// present, then the key-value pair are inserted and "previous" is unchanged. If
+// the key was already present, the value is updated and "*previous" will
+// contain a copy of the old value.
+//
+// InsertOrReturnExisting has complementary behavior that returns the
+// address of an already existing value, rather than updating it.
+template <class Collection>
+bool UpdateReturnCopy(Collection* const collection,
+                      const typename Collection::value_type::first_type& key,
+                      const typename Collection::value_type::second_type& value,
+                      typename Collection::value_type::second_type* previous) {
+  std::pair<typename Collection::iterator, bool> ret =
+      collection->insert(typename Collection::value_type(key, value));
+  if (!ret.second) {
+    // update
+    if (previous) {
+      *previous = ret.first->second;
+    }
+    ret.first->second = value;
+    return true;
+  }
+  return false;
+}
+
+// Same as above except that the key and value are passed as a pair.
+template <class Collection>
+bool UpdateReturnCopy(Collection* const collection,
+                      const typename Collection::value_type& vt,
+                      typename Collection::value_type::second_type* previous) {
+  std::pair<typename Collection::iterator, bool> ret = collection->insert(vt);
+  if (!ret.second) {
+    // update
+    if (previous) {
+      *previous = ret.first->second;
+    }
+    ret.first->second = vt.second;
+    return true;
+  }
+  return false;
+}
+
+// Tries to insert the given key-value pair into the collection. Returns nullptr if
+// the insert succeeds. Otherwise, returns a pointer to the existing value.
+//
+// This complements UpdateReturnCopy in that it allows to update only after
+// verifying the old value and still insert quickly without having to look up
+// twice. Unlike UpdateReturnCopy this also does not come with the issue of an
+// undefined previous* in case new data was inserted.
+template <class Collection>
+typename Collection::value_type::second_type* InsertOrReturnExisting(
+    Collection* const collection, const typename Collection::value_type& vt) {
+  std::pair<typename Collection::iterator, bool> ret = collection->insert(vt);
+  if (ret.second) {
+    return nullptr;  // Inserted, no existing previous value.
+  } else {
+    return &ret.first->second;  // Return address of already existing value.
+  }
+}
+
+// Same as above, except for explicit key and data.
+template <class Collection>
+typename Collection::value_type::second_type* InsertOrReturnExisting(
+    Collection* const collection,
+    const typename Collection::value_type::first_type& key,
+    const typename Collection::value_type::second_type& data) {
+  return InsertOrReturnExisting(collection,
+                                typename Collection::value_type(key, data));
+}
+
+// Erases the collection item identified by the given key, and returns the value
+// associated with that key. It is assumed that the value (i.e., the
+// mapped_type) is a pointer. Returns nullptr if the key was not found in the
+// collection.
+//
+// Examples:
+//   map<string, MyType*> my_map;
+//
+// One line cleanup:
+//     delete EraseKeyReturnValuePtr(&my_map, "abc");
+//
+// Use returned value:
+//     std::unique_ptr<MyType> value_ptr(
+//         EraseKeyReturnValuePtr(&my_map, "abc"));
+//     if (value_ptr.get())
+//       value_ptr->DoSomething();
+//
+template <class Collection>
+typename Collection::value_type::second_type EraseKeyReturnValuePtr(
+    Collection* const collection,
+    const typename Collection::value_type::first_type& key) {
+  typename Collection::iterator it = collection->find(key);
+  if (it == collection->end()) {
+    return nullptr;
+  }
+  typename Collection::value_type::second_type v = it->second;
+  collection->erase(it);
+  return v;
+}
+
+// Inserts all the keys from map_container into key_container, which must
+// support insert(MapContainer::key_type).
+//
+// Note: any initial contents of the key_container are not cleared.
+template <class MapContainer, class KeyContainer>
+void InsertKeysFromMap(const MapContainer& map_container,
+                       KeyContainer* key_container) {
+  GOOGLE_CHECK(key_container != nullptr);
+  for (typename MapContainer::const_iterator it = map_container.begin();
+       it != map_container.end(); ++it) {
+    key_container->insert(it->first);
+  }
+}
+
+// Appends all the keys from map_container into key_container, which must
+// support push_back(MapContainer::key_type).
+//
+// Note: any initial contents of the key_container are not cleared.
+template <class MapContainer, class KeyContainer>
+void AppendKeysFromMap(const MapContainer& map_container,
+                       KeyContainer* key_container) {
+  GOOGLE_CHECK(key_container != nullptr);
+  for (typename MapContainer::const_iterator it = map_container.begin();
+       it != map_container.end(); ++it) {
+    key_container->push_back(it->first);
+  }
+}
+
+// A more specialized overload of AppendKeysFromMap to optimize reallocations
+// for the common case in which we're appending keys to a vector and hence can
+// (and sometimes should) call reserve() first.
+//
+// (It would be possible to play SFINAE games to call reserve() for any
+// container that supports it, but this seems to get us 99% of what we need
+// without the complexity of a SFINAE-based solution.)
+template <class MapContainer, class KeyType>
+void AppendKeysFromMap(const MapContainer& map_container,
+                       std::vector<KeyType>* key_container) {
+  GOOGLE_CHECK(key_container != nullptr);
+  // We now have the opportunity to call reserve(). Calling reserve() every
+  // time is a bad idea for some use cases: libstdc++'s implementation of
+  // vector<>::reserve() resizes the vector's backing store to exactly the
+  // given size (unless it's already at least that big). Because of this,
+  // the use case that involves appending a lot of small maps (total size
+  // N) one by one to a vector would be O(N^2). But never calling reserve()
+  // loses the opportunity to improve the use case of adding from a large
+  // map to an empty vector (this improves performance by up to 33%). A
+  // number of heuristics are possible; see the discussion in
+  // cl/34081696. Here we use the simplest one.
+  if (key_container->empty()) {
+    key_container->reserve(map_container.size());
+  }
+  for (typename MapContainer::const_iterator it = map_container.begin();
+       it != map_container.end(); ++it) {
+    key_container->push_back(it->first);
+  }
+}
+
+// Inserts all the values from map_container into value_container, which must
+// support push_back(MapContainer::mapped_type).
+//
+// Note: any initial contents of the value_container are not cleared.
+template <class MapContainer, class ValueContainer>
+void AppendValuesFromMap(const MapContainer& map_container,
+                         ValueContainer* value_container) {
+  GOOGLE_CHECK(value_container != nullptr);
+  for (typename MapContainer::const_iterator it = map_container.begin();
+       it != map_container.end(); ++it) {
+    value_container->push_back(it->second);
+  }
+}
+
+// A more specialized overload of AppendValuesFromMap to optimize reallocations
+// for the common case in which we're appending values to a vector and hence
+// can (and sometimes should) call reserve() first.
+//
+// (It would be possible to play SFINAE games to call reserve() for any
+// container that supports it, but this seems to get us 99% of what we need
+// without the complexity of a SFINAE-based solution.)
+template <class MapContainer, class ValueType>
+void AppendValuesFromMap(const MapContainer& map_container,
+                         std::vector<ValueType>* value_container) {
+  GOOGLE_CHECK(value_container != nullptr);
+  // See AppendKeysFromMap for why this is done.
+  if (value_container->empty()) {
+    value_container->reserve(map_container.size());
+  }
+  for (typename MapContainer::const_iterator it = map_container.begin();
+       it != map_container.end(); ++it) {
+    value_container->push_back(it->second);
+  }
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/mathutil.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/mathutil.h
new file mode 100644
index 0000000..1d16bce
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/mathutil.h
@@ -0,0 +1,162 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+#ifndef GOOGLE_PROTOBUF_STUBS_MATHUTIL_H_
+#define GOOGLE_PROTOBUF_STUBS_MATHUTIL_H_
+
+#include <cmath>
+#include <float.h>
+#include <limits>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Like std::make_unsigned_t except floating point types map to themselves.
+template <typename T>
+using MakeUnsignedT =
+    typename std::conditional<std::is_integral<T>::value, std::make_unsigned<T>,
+                              std::common_type<T>>::type::type;
+
+// Like std::isnan() except a template function that is defined for all numeric
+// types.
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
+bool IsNan(T /*val*/) {
+  return false;
+}
+
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value,
+                                              int>::type = 0>
+bool IsNan(T val) {
+  return std::isnan(val);
+}
+
+template<typename T>
+bool AlmostEquals(T a, T b) {
+  return a == b;
+}
+template<>
+inline bool AlmostEquals(float a, float b) {
+  return fabs(a - b) < 32 * FLT_EPSILON;
+}
+
+template<>
+inline bool AlmostEquals(double a, double b) {
+  return fabs(a - b) < 32 * DBL_EPSILON;
+}
+
+}  // namespace internal
+
+class MathUtil {
+ public:
+  template <typename T>
+  static T Sign(T value) {
+    if (value == T(0) || internal::IsNan(value)) {
+      return value;
+    }
+    return value > T(0) ? 1 : -1;
+  }
+
+  template <typename T>
+  static bool AlmostEquals(T a, T b) {
+    return internal::AlmostEquals(a, b);
+  }
+
+  // Largest of two values.
+  // Works correctly for special floating point values.
+  // Note: 0.0 and -0.0 are not differentiated by Max (Max(0.0, -0.0) is -0.0),
+  // which should be OK because, although they (can) have different
+  // bit representation, they are observably the same when examined
+  // with arithmetic and (in)equality operators.
+  template <typename T>
+  static T Max(const T x, const T y) {
+    return internal::IsNan(x) || x > y ? x : y;
+  }
+
+  // Absolute value of x
+  // Works correctly for unsigned types and
+  // for special floating point values.
+  // Note: 0.0 and -0.0 are not differentiated by Abs (Abs(0.0) is -0.0),
+  // which should be OK: see the comment for Max above.
+  template<typename T>
+  static T Abs(const T x) {
+    return x > T(0) ? x : -x;
+  }
+
+  // Absolute value of the difference between two numbers.
+  // Works correctly for signed types and special floating point values.
+  template <typename T>
+  static typename internal::MakeUnsignedT<T> AbsDiff(const T x, const T y) {
+    // Carries out arithmetic as unsigned to avoid overflow.
+    typedef typename internal::MakeUnsignedT<T> R;
+    return x > y ? R(x) - R(y) : R(y) - R(x);
+  }
+
+  // If two (usually floating point) numbers are within a certain
+  // fraction of their magnitude or within a certain absolute margin of error.
+  // This is the same as the following but faster:
+  //   WithinFraction(x, y, fraction)  ||  WithinMargin(x, y, margin)
+  // E.g. WithinFraction(0.0, 1e-10, 1e-5) is false but
+  //      WithinFractionOrMargin(0.0, 1e-10, 1e-5, 1e-5) is true.
+  template<typename T>
+  static bool WithinFractionOrMargin(const T x, const T y,
+                                     const T fraction, const T margin);
+};
+
+template<typename T>
+bool MathUtil::WithinFractionOrMargin(const T x, const T y,
+                                      const T fraction, const T margin) {
+  // Not just "0 <= fraction" to fool the compiler for unsigned types.
+  GOOGLE_DCHECK((T(0) < fraction || T(0) == fraction) &&
+         fraction < T(1) &&
+         margin >= T(0));
+
+  // Template specialization will convert the if() condition to a constant,
+  // which will cause the compiler to generate code for either the "if" part
+  // or the "then" part.  In this way we avoid a compiler warning
+  // about a potential integer overflow in crosstool v12 (gcc 4.3.1).
+  if (std::numeric_limits<T>::is_integer) {
+    return x == y;
+  } else {
+    if (!std::isfinite(x) || !std::isfinite(y)) {
+      return false;
+    }
+    T relative_margin = static_cast<T>(fraction * Max(Abs(x), Abs(y)));
+    return AbsDiff(x, y) <= Max(margin, relative_margin);
+  }
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_STUBS_MATHUTIL_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/mutex.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/mutex.h
new file mode 100644
index 0000000..c459991
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/mutex.h
@@ -0,0 +1,218 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_STUBS_MUTEX_H_
+#define GOOGLE_PROTOBUF_STUBS_MUTEX_H_
+
+#include <mutex>
+
+#ifdef GOOGLE_PROTOBUF_SUPPORT_WINDOWS_XP
+
+#include <windows.h>
+
+// GetMessage conflicts with GeneratedMessageReflection::GetMessage().
+#ifdef GetMessage
+#undef GetMessage
+#endif
+
+#endif
+
+#include <google/protobuf/stubs/macros.h>
+
+// Define thread-safety annotations for use below, if we are building with
+// Clang.
+#if defined(__clang__) && !defined(SWIG)
+#define GOOGLE_PROTOBUF_ACQUIRE(...) \
+  __attribute__((acquire_capability(__VA_ARGS__)))
+#define GOOGLE_PROTOBUF_RELEASE(...) \
+  __attribute__((release_capability(__VA_ARGS__)))
+#define GOOGLE_PROTOBUF_SCOPED_CAPABILITY __attribute__((scoped_lockable))
+#define GOOGLE_PROTOBUF_CAPABILITY(x) __attribute__((capability(x)))
+#else
+#define GOOGLE_PROTOBUF_ACQUIRE(...)
+#define GOOGLE_PROTOBUF_RELEASE(...)
+#define GOOGLE_PROTOBUF_SCOPED_CAPABILITY
+#define GOOGLE_PROTOBUF_CAPABILITY(x)
+#endif
+
+#include <google/protobuf/port_def.inc>
+
+// ===================================================================
+// emulates google3/base/mutex.h
+namespace google {
+namespace protobuf {
+namespace internal {
+
+#define GOOGLE_PROTOBUF_LINKER_INITIALIZED
+
+#ifdef GOOGLE_PROTOBUF_SUPPORT_WINDOWS_XP
+
+// This class is a lightweight replacement for std::mutex on Windows platforms.
+// std::mutex does not work on Windows XP SP2 with the latest VC++ libraries,
+// because it utilizes the Concurrency Runtime that is only supported on Windows
+// XP SP3 and above.
+class PROTOBUF_EXPORT CriticalSectionLock {
+ public:
+  CriticalSectionLock() { InitializeCriticalSection(&critical_section_); }
+  ~CriticalSectionLock() { DeleteCriticalSection(&critical_section_); }
+  void lock() { EnterCriticalSection(&critical_section_); }
+  void unlock() { LeaveCriticalSection(&critical_section_); }
+
+ private:
+  CRITICAL_SECTION critical_section_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CriticalSectionLock);
+};
+
+#endif
+
+// In MSVC std::mutex does not have a constexpr constructor.
+// This wrapper makes the constructor constexpr.
+template <typename T>
+class CallOnceInitializedMutex {
+ public:
+  constexpr CallOnceInitializedMutex() : flag_{}, buf_{} {}
+  ~CallOnceInitializedMutex() { get().~T(); }
+
+  void lock() { get().lock(); }
+  void unlock() { get().unlock(); }
+
+ private:
+  T& get() {
+    std::call_once(flag_, [&] { ::new (static_cast<void*>(&buf_)) T(); });
+    return reinterpret_cast<T&>(buf_);
+  }
+
+  std::once_flag flag_;
+  alignas(T) char buf_[sizeof(T)];
+};
+
+// Mutex is a natural type to wrap. As both google and other organization have
+// specialized mutexes. gRPC also provides an injection mechanism for custom
+// mutexes.
+class GOOGLE_PROTOBUF_CAPABILITY("mutex") PROTOBUF_EXPORT WrappedMutex {
+ public:
+#if defined(__QNX__)
+  constexpr WrappedMutex() = default;
+#else
+  constexpr WrappedMutex() {}
+#endif
+  void Lock() GOOGLE_PROTOBUF_ACQUIRE() { mu_.lock(); }
+  void Unlock() GOOGLE_PROTOBUF_RELEASE() { mu_.unlock(); }
+  // Crash if this Mutex is not held exclusively by this thread.
+  // May fail to crash when it should; will never crash when it should not.
+  void AssertHeld() const {}
+
+ private:
+#if defined(GOOGLE_PROTOBUF_SUPPORT_WINDOWS_XP)
+  CallOnceInitializedMutex<CriticalSectionLock> mu_{};
+#elif defined(_WIN32)
+  CallOnceInitializedMutex<std::mutex> mu_{};
+#else
+  std::mutex mu_{};
+#endif
+};
+
+using Mutex = WrappedMutex;
+
+// MutexLock(mu) acquires mu when constructed and releases it when destroyed.
+class GOOGLE_PROTOBUF_SCOPED_CAPABILITY PROTOBUF_EXPORT MutexLock {
+ public:
+  explicit MutexLock(Mutex* mu) GOOGLE_PROTOBUF_ACQUIRE(mu) : mu_(mu) {
+    this->mu_->Lock();
+  }
+  ~MutexLock() GOOGLE_PROTOBUF_RELEASE() { this->mu_->Unlock(); }
+
+ private:
+  Mutex *const mu_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MutexLock);
+};
+
+// TODO(kenton):  Implement these?  Hard to implement portably.
+typedef MutexLock ReaderMutexLock;
+typedef MutexLock WriterMutexLock;
+
+// MutexLockMaybe is like MutexLock, but is a no-op when mu is nullptr.
+class PROTOBUF_EXPORT MutexLockMaybe {
+ public:
+  explicit MutexLockMaybe(Mutex *mu) :
+    mu_(mu) { if (this->mu_ != nullptr) { this->mu_->Lock(); } }
+  ~MutexLockMaybe() { if (this->mu_ != nullptr) { this->mu_->Unlock(); } }
+ private:
+  Mutex *const mu_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MutexLockMaybe);
+};
+
+#if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
+template<typename T>
+class ThreadLocalStorage {
+ public:
+  ThreadLocalStorage() {
+    pthread_key_create(&key_, &ThreadLocalStorage::Delete);
+  }
+  ~ThreadLocalStorage() {
+    pthread_key_delete(key_);
+  }
+  T* Get() {
+    T* result = static_cast<T*>(pthread_getspecific(key_));
+    if (result == nullptr) {
+      result = new T();
+      pthread_setspecific(key_, result);
+    }
+    return result;
+  }
+ private:
+  static void Delete(void* value) {
+    delete static_cast<T*>(value);
+  }
+  pthread_key_t key_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ThreadLocalStorage);
+};
+#endif
+
+}  // namespace internal
+
+// We made these internal so that they would show up as such in the docs,
+// but we don't want to stick "internal::" in front of them everywhere.
+using internal::Mutex;
+using internal::MutexLock;
+using internal::ReaderMutexLock;
+using internal::WriterMutexLock;
+using internal::MutexLockMaybe;
+
+}  // namespace protobuf
+}  // namespace google
+
+#undef GOOGLE_PROTOBUF_ACQUIRE
+#undef GOOGLE_PROTOBUF_RELEASE
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_MUTEX_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/once.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/once.h
new file mode 100644
index 0000000..070d36d
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/once.h
@@ -0,0 +1,55 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_STUBS_ONCE_H__
+#define GOOGLE_PROTOBUF_STUBS_ONCE_H__
+
+#include <mutex>
+#include <utility>
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+using once_flag = std::once_flag;
+template <typename... Args>
+void call_once(Args&&... args ) {
+  std::call_once(std::forward<Args>(args)...);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_ONCE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/platform_macros.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/platform_macros.h
new file mode 100644
index 0000000..d10faf9
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/platform_macros.h
@@ -0,0 +1,138 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2012 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_PLATFORM_MACROS_H_
+#define GOOGLE_PROTOBUF_PLATFORM_MACROS_H_
+
+#define GOOGLE_PROTOBUF_PLATFORM_ERROR \
+#error "Host platform was not detected as supported by protobuf"
+
+// Processor architecture detection.  For more info on what's defined, see:
+//   http://msdn.microsoft.com/en-us/library/b0084kay.aspx
+//   http://www.agner.org/optimize/calling_conventions.pdf
+//   or with gcc, run: "echo | gcc -E -dM -"
+#if defined(_M_X64) || defined(__x86_64__)
+#define GOOGLE_PROTOBUF_ARCH_X64 1
+#define GOOGLE_PROTOBUF_ARCH_64_BIT 1
+#elif defined(_M_IX86) || defined(__i386__)
+#define GOOGLE_PROTOBUF_ARCH_IA32 1
+#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
+#elif defined(__QNX__)
+#define GOOGLE_PROTOBUF_ARCH_ARM_QNX 1
+#if defined(__aarch64__)
+#define GOOGLE_PROTOBUF_ARCH_64_BIT 1
+#else
+#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
+#endif
+#elif defined(_M_ARM) || defined(__ARMEL__)
+#define GOOGLE_PROTOBUF_ARCH_ARM 1
+#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
+#elif defined(_M_ARM64)
+#define GOOGLE_PROTOBUF_ARCH_ARM 1
+#define GOOGLE_PROTOBUF_ARCH_64_BIT 1
+#elif defined(__aarch64__)
+#define GOOGLE_PROTOBUF_ARCH_AARCH64 1
+#define GOOGLE_PROTOBUF_ARCH_64_BIT 1
+#elif defined(__mips__)
+#if defined(__LP64__)
+#define GOOGLE_PROTOBUF_ARCH_MIPS64 1
+#define GOOGLE_PROTOBUF_ARCH_64_BIT 1
+#else
+#define GOOGLE_PROTOBUF_ARCH_MIPS 1
+#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
+#endif
+#elif defined(__pnacl__)
+#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
+#elif defined(sparc)
+#define GOOGLE_PROTOBUF_ARCH_SPARC 1
+#if defined(__sparc_v9__) || defined(__sparcv9) || defined(__arch64__)
+#define GOOGLE_PROTOBUF_ARCH_64_BIT 1
+#else
+#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
+#endif
+#elif defined(_POWER) || defined(__powerpc64__) || defined(__PPC64__)
+#define GOOGLE_PROTOBUF_ARCH_POWER 1
+#define GOOGLE_PROTOBUF_ARCH_64_BIT 1
+#elif defined(__PPC__)
+#define GOOGLE_PROTOBUF_ARCH_PPC 1
+#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
+#elif defined(__GNUC__)
+# if (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4))
+// We fallback to the generic Clang/GCC >= 4.7 implementation in atomicops.h
+# elif defined(__clang__)
+#  if !__has_extension(c_atomic)
+GOOGLE_PROTOBUF_PLATFORM_ERROR
+#  endif
+// We fallback to the generic Clang/GCC >= 4.7 implementation in atomicops.h
+# endif
+# if __LP64__
+#  define GOOGLE_PROTOBUF_ARCH_64_BIT 1
+# else
+#  define GOOGLE_PROTOBUF_ARCH_32_BIT 1
+# endif
+#else
+GOOGLE_PROTOBUF_PLATFORM_ERROR
+#endif
+
+#if defined(__APPLE__)
+#define GOOGLE_PROTOBUF_OS_APPLE
+#include <Availability.h>
+#include <TargetConditionals.h>
+#if TARGET_OS_IPHONE
+#define GOOGLE_PROTOBUF_OS_IPHONE
+#endif
+#elif defined(__EMSCRIPTEN__)
+#define GOOGLE_PROTOBUF_OS_EMSCRIPTEN
+#elif defined(__native_client__)
+#define GOOGLE_PROTOBUF_OS_NACL
+#elif defined(sun)
+#define GOOGLE_PROTOBUF_OS_SOLARIS
+#elif defined(_AIX)
+#define GOOGLE_PROTOBUF_OS_AIX
+#elif defined(__ANDROID__)
+#define GOOGLE_PROTOBUF_OS_ANDROID
+#endif
+
+#undef GOOGLE_PROTOBUF_PLATFORM_ERROR
+
+#if defined(GOOGLE_PROTOBUF_OS_ANDROID) || defined(GOOGLE_PROTOBUF_OS_IPHONE)
+// Android ndk does not support the __thread keyword very well yet. Here
+// we use pthread_key_create()/pthread_getspecific()/... methods for
+// TLS support on android.
+// iOS also does not support the __thread keyword.
+#define GOOGLE_PROTOBUF_NO_THREADLOCAL
+#endif
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070
+// __thread keyword requires at least 10.7
+#define GOOGLE_PROTOBUF_NO_THREADLOCAL
+#endif
+
+#endif  // GOOGLE_PROTOBUF_PLATFORM_MACROS_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/port.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/port.h
new file mode 100644
index 0000000..b074cb1
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/port.h
@@ -0,0 +1,413 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_STUBS_PORT_H_
+#define GOOGLE_PROTOBUF_STUBS_PORT_H_
+
+#include <assert.h>
+#include <cstdint>
+#include <stdlib.h>
+#include <cstddef>
+#include <string>
+#include <string.h>
+
+#include <google/protobuf/stubs/platform_macros.h>
+
+#include <google/protobuf/port_def.inc>
+
+#undef PROTOBUF_LITTLE_ENDIAN
+#ifdef _WIN32
+  // Assuming windows is always little-endian.
+  // TODO(xiaofeng): The PROTOBUF_LITTLE_ENDIAN is not only used for
+  // optimization but also for correctness. We should define an
+  // different macro to test the big-endian code path in coded_stream.
+  #if !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+    #define PROTOBUF_LITTLE_ENDIAN 1
+  #endif
+#if defined(_MSC_VER) && _MSC_VER >= 1300 && !defined(__INTEL_COMPILER)
+// If MSVC has "/RTCc" set, it will complain about truncating casts at
+// runtime.  This file contains some intentional truncating casts.
+#pragma runtime_checks("c", off)
+#endif
+#else
+#ifdef __APPLE__
+#include <machine/endian.h>  // __BYTE_ORDER
+#elif defined(__FreeBSD__)
+#include <sys/endian.h>  // __BYTE_ORDER
+#elif (defined(sun) || defined(__sun)) && (defined(__SVR4) || defined(__svr4__))
+#include <sys/isa_defs.h>  // __BYTE_ORDER
+#elif defined(_AIX) || defined(__TOS_AIX__)
+#include <sys/machine.h>  // BYTE_ORDER
+#else
+#if !defined(__QNX__)
+#include <endian.h>  // __BYTE_ORDER
+#endif
+#endif
+#if ((defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) ||   \
+     (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \
+     (defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN)) &&      \
+    !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+#define PROTOBUF_LITTLE_ENDIAN 1
+#endif
+#endif
+
+// These #includes are for the byte swap functions declared later on.
+#ifdef _MSC_VER
+#include <stdlib.h>  // NOLINT(build/include)
+#include <intrin.h>
+#elif defined(__APPLE__)
+#include <libkern/OSByteOrder.h>
+#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__)
+#include <byteswap.h>  // IWYU pragma: export
+#endif
+
+// Legacy: some users reference these (internal-only) macros even though we
+// don't need them any more.
+#if defined(_MSC_VER) && defined(PROTOBUF_USE_DLLS)
+  #ifdef LIBPROTOBUF_EXPORTS
+    #define LIBPROTOBUF_EXPORT __declspec(dllexport)
+  #else
+    #define LIBPROTOBUF_EXPORT __declspec(dllimport)
+  #endif
+  #ifdef LIBPROTOC_EXPORTS
+    #define LIBPROTOC_EXPORT   __declspec(dllexport)
+  #else
+    #define LIBPROTOC_EXPORT   __declspec(dllimport)
+  #endif
+#else
+  #define LIBPROTOBUF_EXPORT
+  #define LIBPROTOC_EXPORT
+#endif
+
+#define PROTOBUF_RUNTIME_DEPRECATED(message) PROTOBUF_DEPRECATED_MSG(message)
+#define GOOGLE_PROTOBUF_RUNTIME_DEPRECATED(message) \
+  PROTOBUF_DEPRECATED_MSG(message)
+
+// ===================================================================
+// from google3/base/port.h
+
+#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \
+     (defined(_MSC_VER) && _MSC_VER >= 1900))
+// Define this to 1 if the code is compiled in C++11 mode; leave it
+// undefined otherwise.  Do NOT define it to 0 -- that causes
+// '#ifdef LANG_CXX11' to behave differently from '#if LANG_CXX11'.
+#define LANG_CXX11 1
+#else
+#error "Protobuf requires at least C++11."
+#endif
+
+namespace google {
+namespace protobuf {
+
+using ConstStringParam = const std::string &;
+
+typedef unsigned int uint;
+
+typedef int8_t int8;
+typedef int16_t int16;
+typedef int32_t int32;
+typedef int64_t int64;
+
+typedef uint8_t uint8;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+
+static const int32 kint32max = 0x7FFFFFFF;
+static const int32 kint32min = -kint32max - 1;
+static const int64 kint64max = int64_t{0x7FFFFFFFFFFFFFFF};
+static const int64 kint64min = -kint64max - 1;
+static const uint32 kuint32max = 0xFFFFFFFFu;
+static const uint64 kuint64max = uint64_t{0xFFFFFFFFFFFFFFFFu};
+
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||\
+    defined(MEMORY_SANITIZER)
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+uint16_t __sanitizer_unaligned_load16(const void *p);
+uint32_t __sanitizer_unaligned_load32(const void *p);
+uint64_t __sanitizer_unaligned_load64(const void *p);
+void __sanitizer_unaligned_store16(void *p, uint16_t v);
+void __sanitizer_unaligned_store32(void *p, uint32_t v);
+void __sanitizer_unaligned_store64(void *p, uint64_t v);
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+inline uint16_t GOOGLE_UNALIGNED_LOAD16(const void *p) {
+  return __sanitizer_unaligned_load16(p);
+}
+
+inline uint32_t GOOGLE_UNALIGNED_LOAD32(const void *p) {
+  return __sanitizer_unaligned_load32(p);
+}
+
+inline uint64_t GOOGLE_UNALIGNED_LOAD64(const void *p) {
+  return __sanitizer_unaligned_load64(p);
+}
+
+inline void GOOGLE_UNALIGNED_STORE16(void *p, uint16_t v) {
+  __sanitizer_unaligned_store16(p, v);
+}
+
+inline void GOOGLE_UNALIGNED_STORE32(void *p, uint32_t v) {
+  __sanitizer_unaligned_store32(p, v);
+}
+
+inline void GOOGLE_UNALIGNED_STORE64(void *p, uint64_t v) {
+  __sanitizer_unaligned_store64(p, v);
+}
+
+#elif defined(GOOGLE_PROTOBUF_USE_UNALIGNED) && GOOGLE_PROTOBUF_USE_UNALIGNED
+
+#define GOOGLE_UNALIGNED_LOAD16(_p) (*reinterpret_cast<const uint16_t *>(_p))
+#define GOOGLE_UNALIGNED_LOAD32(_p) (*reinterpret_cast<const uint32_t *>(_p))
+#define GOOGLE_UNALIGNED_LOAD64(_p) (*reinterpret_cast<const uint64_t *>(_p))
+
+#define GOOGLE_UNALIGNED_STORE16(_p, _val) (*reinterpret_cast<uint16_t *>(_p) = (_val))
+#define GOOGLE_UNALIGNED_STORE32(_p, _val) (*reinterpret_cast<uint32_t *>(_p) = (_val))
+#define GOOGLE_UNALIGNED_STORE64(_p, _val) (*reinterpret_cast<uint64_t *>(_p) = (_val))
+
+#else
+inline uint16_t GOOGLE_UNALIGNED_LOAD16(const void *p) {
+  uint16_t t;
+  memcpy(&t, p, sizeof t);
+  return t;
+}
+
+inline uint32_t GOOGLE_UNALIGNED_LOAD32(const void *p) {
+  uint32_t t;
+  memcpy(&t, p, sizeof t);
+  return t;
+}
+
+inline uint64_t GOOGLE_UNALIGNED_LOAD64(const void *p) {
+  uint64_t t;
+  memcpy(&t, p, sizeof t);
+  return t;
+}
+
+inline void GOOGLE_UNALIGNED_STORE16(void *p, uint16_t v) {
+  memcpy(p, &v, sizeof v);
+}
+
+inline void GOOGLE_UNALIGNED_STORE32(void *p, uint32_t v) {
+  memcpy(p, &v, sizeof v);
+}
+
+inline void GOOGLE_UNALIGNED_STORE64(void *p, uint64_t v) {
+  memcpy(p, &v, sizeof v);
+}
+#endif
+
+#if defined(GOOGLE_PROTOBUF_OS_NACL) \
+    || (defined(__ANDROID__) && defined(__clang__) \
+        && (__clang_major__ == 3 && __clang_minor__ == 8) \
+        && (__clang_patchlevel__ < 275480))
+# define GOOGLE_PROTOBUF_USE_PORTABLE_LOG2
+#endif
+
+// The following guarantees declaration of the byte swap functions.
+#ifdef _MSC_VER
+#define bswap_16(x) _byteswap_ushort(x)
+#define bswap_32(x) _byteswap_ulong(x)
+#define bswap_64(x) _byteswap_uint64(x)
+
+#elif defined(__APPLE__)
+// Mac OS X / Darwin features
+#define bswap_16(x) OSSwapInt16(x)
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+
+#elif !defined(__linux__) && !defined(__ANDROID__) && !defined(__CYGWIN__)
+
+#ifndef bswap_16
+static inline uint16_t bswap_16(uint16_t x) {
+  return static_cast<uint16_t>(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8));
+}
+#define bswap_16(x) bswap_16(x)
+#endif
+
+#ifndef bswap_32
+static inline uint32_t bswap_32(uint32_t x) {
+  return (((x & 0xFF) << 24) |
+          ((x & 0xFF00) << 8) |
+          ((x & 0xFF0000) >> 8) |
+          ((x & 0xFF000000) >> 24));
+}
+#define bswap_32(x) bswap_32(x)
+#endif
+
+#ifndef bswap_64
+static inline uint64_t bswap_64(uint64_t x) {
+  return (((x & uint64_t{0xFFu}) << 56) | ((x & uint64_t{0xFF00u}) << 40) |
+          ((x & uint64_t{0xFF0000u}) << 24) |
+          ((x & uint64_t{0xFF000000u}) << 8) |
+          ((x & uint64_t{0xFF00000000u}) >> 8) |
+          ((x & uint64_t{0xFF0000000000u}) >> 24) |
+          ((x & uint64_t{0xFF000000000000u}) >> 40) |
+          ((x & uint64_t{0xFF00000000000000u}) >> 56));
+}
+#define bswap_64(x) bswap_64(x)
+#endif
+
+#endif
+
+// ===================================================================
+// from google3/util/bits/bits.h
+
+class Bits {
+ public:
+  static uint32_t Log2FloorNonZero(uint32_t n) {
+#if defined(__GNUC__)
+  return 31 ^ static_cast<uint32_t>(__builtin_clz(n));
+#elif defined(_MSC_VER)
+  unsigned long where;
+  _BitScanReverse(&where, n);
+  return where;
+#else
+  return Log2FloorNonZero_Portable(n);
+#endif
+  }
+
+  static uint32_t Log2FloorNonZero64(uint64_t n) {
+    // Older versions of clang run into an instruction-selection failure when
+    // it encounters __builtin_clzll:
+    // https://bugs.chromium.org/p/nativeclient/issues/detail?id=4395
+    // This includes arm-nacl-clang and clang in older Android NDK versions.
+    // To work around this, when we build with those we use the portable
+    // implementation instead.
+#if defined(__GNUC__) && !defined(GOOGLE_PROTOBUF_USE_PORTABLE_LOG2)
+  return 63 ^ static_cast<uint32_t>(__builtin_clzll(n));
+#elif defined(_MSC_VER) && defined(_M_X64)
+  unsigned long where;
+  _BitScanReverse64(&where, n);
+  return where;
+#else
+  return Log2FloorNonZero64_Portable(n);
+#endif
+  }
+ private:
+  static int Log2FloorNonZero_Portable(uint32_t n) {
+    if (n == 0)
+      return -1;
+    int log = 0;
+    uint32_t value = n;
+    for (int i = 4; i >= 0; --i) {
+      int shift = (1 << i);
+      uint32_t x = value >> shift;
+      if (x != 0) {
+        value = x;
+        log += shift;
+      }
+    }
+    assert(value == 1);
+    return log;
+  }
+
+  static int Log2FloorNonZero64_Portable(uint64_t n) {
+    const uint32_t topbits = static_cast<uint32_t>(n >> 32);
+    if (topbits == 0) {
+      // Top bits are zero, so scan in bottom bits
+      return static_cast<int>(Log2FloorNonZero(static_cast<uint32_t>(n)));
+    } else {
+      return 32 + static_cast<int>(Log2FloorNonZero(topbits));
+    }
+  }
+};
+
+// ===================================================================
+// from google3/util/endian/endian.h
+PROTOBUF_EXPORT uint32_t ghtonl(uint32_t x);
+
+class BigEndian {
+ public:
+#ifdef PROTOBUF_LITTLE_ENDIAN
+
+  static uint16_t FromHost16(uint16_t x) { return bswap_16(x); }
+  static uint16_t ToHost16(uint16_t x) { return bswap_16(x); }
+
+  static uint32_t FromHost32(uint32_t x) { return bswap_32(x); }
+  static uint32_t ToHost32(uint32_t x) { return bswap_32(x); }
+
+  static uint64_t FromHost64(uint64_t x) { return bswap_64(x); }
+  static uint64_t ToHost64(uint64_t x) { return bswap_64(x); }
+
+  static bool IsLittleEndian() { return true; }
+
+#else
+
+  static uint16_t FromHost16(uint16_t x) { return x; }
+  static uint16_t ToHost16(uint16_t x) { return x; }
+
+  static uint32_t FromHost32(uint32_t x) { return x; }
+  static uint32_t ToHost32(uint32_t x) { return x; }
+
+  static uint64_t FromHost64(uint64_t x) { return x; }
+  static uint64_t ToHost64(uint64_t x) { return x; }
+
+  static bool IsLittleEndian() { return false; }
+
+#endif /* ENDIAN */
+
+  // Functions to do unaligned loads and stores in big-endian order.
+  static uint16_t Load16(const void *p) {
+    return ToHost16(GOOGLE_UNALIGNED_LOAD16(p));
+  }
+
+  static void Store16(void *p, uint16_t v) {
+    GOOGLE_UNALIGNED_STORE16(p, FromHost16(v));
+  }
+
+  static uint32_t Load32(const void *p) {
+    return ToHost32(GOOGLE_UNALIGNED_LOAD32(p));
+  }
+
+  static void Store32(void *p, uint32_t v) {
+    GOOGLE_UNALIGNED_STORE32(p, FromHost32(v));
+  }
+
+  static uint64_t Load64(const void *p) {
+    return ToHost64(GOOGLE_UNALIGNED_LOAD64(p));
+  }
+
+  static void Store64(void *p, uint64_t v) {
+    GOOGLE_UNALIGNED_STORE64(p, FromHost64(v));
+  }
+};
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_PORT_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/status.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/status.h
new file mode 100644
index 0000000..c858cf6
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/status.h
@@ -0,0 +1,196 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STATUS_H_
+#define GOOGLE_PROTOBUF_STUBS_STATUS_H_
+
+#include <string>
+
+#include <google/protobuf/stubs/stringpiece.h>
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace status_internal {
+
+// These values must match error codes defined in google/rpc/code.proto.
+enum class StatusCode : int {
+  kOk = 0,
+  kCancelled = 1,
+  kUnknown = 2,
+  kInvalidArgument = 3,
+  kDeadlineExceeded = 4,
+  kNotFound = 5,
+  kAlreadyExists = 6,
+  kPermissionDenied = 7,
+  kUnauthenticated = 16,
+  kResourceExhausted = 8,
+  kFailedPrecondition = 9,
+  kAborted = 10,
+  kOutOfRange = 11,
+  kUnimplemented = 12,
+  kInternal = 13,
+  kUnavailable = 14,
+  kDataLoss = 15,
+};
+
+class PROTOBUF_EXPORT Status {
+ public:
+  // Creates a "successful" status.
+  Status();
+
+  // Create a status in the canonical error space with the specified
+  // code, and error message.  If "code == 0", error_message is
+  // ignored and a Status object identical to Status::kOk is
+  // constructed.
+  Status(StatusCode error_code, StringPiece error_message);
+  Status(const Status&);
+  Status& operator=(const Status& x);
+  ~Status() {}
+
+  // Accessor
+  bool ok() const { return error_code_ == StatusCode::kOk; }
+  StatusCode code() const { return error_code_; }
+  StringPiece message() const {
+    return error_message_;
+  }
+
+  bool operator==(const Status& x) const;
+  bool operator!=(const Status& x) const {
+    return !operator==(x);
+  }
+
+  // Return a combination of the error code name and message.
+  std::string ToString() const;
+
+ private:
+  StatusCode error_code_;
+  std::string error_message_;
+};
+
+// Returns an OK status, equivalent to a default constructed instance. Prefer
+// usage of `OkStatus()` when constructing such an OK status.
+PROTOBUF_EXPORT Status OkStatus();
+
+// Prints a human-readable representation of 'x' to 'os'.
+PROTOBUF_EXPORT std::ostream& operator<<(std::ostream& os, const Status& x);
+
+// These convenience functions return `true` if a given status matches the
+// `StatusCode` error code of its associated function.
+PROTOBUF_EXPORT bool IsAborted(const Status& status);
+PROTOBUF_EXPORT bool IsAlreadyExists(const Status& status);
+PROTOBUF_EXPORT bool IsCancelled(const Status& status);
+PROTOBUF_EXPORT bool IsDataLoss(const Status& status);
+PROTOBUF_EXPORT bool IsDeadlineExceeded(const Status& status);
+PROTOBUF_EXPORT bool IsFailedPrecondition(const Status& status);
+PROTOBUF_EXPORT bool IsInternal(const Status& status);
+PROTOBUF_EXPORT bool IsInvalidArgument(const Status& status);
+PROTOBUF_EXPORT bool IsNotFound(const Status& status);
+PROTOBUF_EXPORT bool IsOutOfRange(const Status& status);
+PROTOBUF_EXPORT bool IsPermissionDenied(const Status& status);
+PROTOBUF_EXPORT bool IsResourceExhausted(const Status& status);
+PROTOBUF_EXPORT bool IsUnauthenticated(const Status& status);
+PROTOBUF_EXPORT bool IsUnavailable(const Status& status);
+PROTOBUF_EXPORT bool IsUnimplemented(const Status& status);
+PROTOBUF_EXPORT bool IsUnknown(const Status& status);
+
+// These convenience functions create an `Status` object with an error code as
+// indicated by the associated function name, using the error message passed in
+// `message`.
+//
+// These functions are intentionally named `*Error` rather than `*Status` to
+// match the names from Abseil:
+// https://github.com/abseil/abseil-cpp/blob/2e9532cc6c701a8323d0cffb468999ab804095ab/absl/status/status.h#L716
+PROTOBUF_EXPORT Status AbortedError(StringPiece message);
+PROTOBUF_EXPORT Status AlreadyExistsError(StringPiece message);
+PROTOBUF_EXPORT Status CancelledError(StringPiece message);
+PROTOBUF_EXPORT Status DataLossError(StringPiece message);
+PROTOBUF_EXPORT Status DeadlineExceededError(StringPiece message);
+PROTOBUF_EXPORT Status FailedPreconditionError(StringPiece message);
+PROTOBUF_EXPORT Status InternalError(StringPiece message);
+PROTOBUF_EXPORT Status InvalidArgumentError(StringPiece message);
+PROTOBUF_EXPORT Status NotFoundError(StringPiece message);
+PROTOBUF_EXPORT Status OutOfRangeError(StringPiece message);
+PROTOBUF_EXPORT Status PermissionDeniedError(StringPiece message);
+PROTOBUF_EXPORT Status ResourceExhaustedError(StringPiece message);
+PROTOBUF_EXPORT Status UnauthenticatedError(StringPiece message);
+PROTOBUF_EXPORT Status UnavailableError(StringPiece message);
+PROTOBUF_EXPORT Status UnimplementedError(StringPiece message);
+PROTOBUF_EXPORT Status UnknownError(StringPiece message);
+
+}  // namespace status_internal
+
+using ::google::protobuf::util::status_internal::Status;
+using ::google::protobuf::util::status_internal::StatusCode;
+
+using ::google::protobuf::util::status_internal::IsAborted;
+using ::google::protobuf::util::status_internal::IsAlreadyExists;
+using ::google::protobuf::util::status_internal::IsCancelled;
+using ::google::protobuf::util::status_internal::IsDataLoss;
+using ::google::protobuf::util::status_internal::IsDeadlineExceeded;
+using ::google::protobuf::util::status_internal::IsFailedPrecondition;
+using ::google::protobuf::util::status_internal::IsInternal;
+using ::google::protobuf::util::status_internal::IsInvalidArgument;
+using ::google::protobuf::util::status_internal::IsNotFound;
+using ::google::protobuf::util::status_internal::IsOutOfRange;
+using ::google::protobuf::util::status_internal::IsPermissionDenied;
+using ::google::protobuf::util::status_internal::IsResourceExhausted;
+using ::google::protobuf::util::status_internal::IsUnauthenticated;
+using ::google::protobuf::util::status_internal::IsUnavailable;
+using ::google::protobuf::util::status_internal::IsUnimplemented;
+using ::google::protobuf::util::status_internal::IsUnknown;
+
+using ::google::protobuf::util::status_internal::AbortedError;
+using ::google::protobuf::util::status_internal::AlreadyExistsError;
+using ::google::protobuf::util::status_internal::CancelledError;
+using ::google::protobuf::util::status_internal::DataLossError;
+using ::google::protobuf::util::status_internal::DeadlineExceededError;
+using ::google::protobuf::util::status_internal::FailedPreconditionError;
+using ::google::protobuf::util::status_internal::InternalError;
+using ::google::protobuf::util::status_internal::InvalidArgumentError;
+using ::google::protobuf::util::status_internal::NotFoundError;
+using ::google::protobuf::util::status_internal::OkStatus;
+using ::google::protobuf::util::status_internal::OutOfRangeError;
+using ::google::protobuf::util::status_internal::PermissionDeniedError;
+using ::google::protobuf::util::status_internal::ResourceExhaustedError;
+using ::google::protobuf::util::status_internal::UnauthenticatedError;
+using ::google::protobuf::util::status_internal::UnavailableError;
+using ::google::protobuf::util::status_internal::UnimplementedError;
+using ::google::protobuf::util::status_internal::UnknownError;
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_STATUS_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/status_macros.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/status_macros.h
new file mode 100644
index 0000000..407ff4c
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/status_macros.h
@@ -0,0 +1,89 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// From: util/task/contrib/status_macros/status_macros.h
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STATUS_MACROS_H_
+#define GOOGLE_PROTOBUF_STUBS_STATUS_MACROS_H_
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/statusor.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+// Run a command that returns a util::Status.  If the called code returns an
+// error status, return that status up out of this method too.
+//
+// Example:
+//   RETURN_IF_ERROR(DoThings(4));
+#define RETURN_IF_ERROR(expr)                                                \
+  do {                                                                       \
+    /* Using _status below to avoid capture problems if expr is "status". */ \
+    const PROTOBUF_NAMESPACE_ID::util::Status _status = (expr);              \
+    if (PROTOBUF_PREDICT_FALSE(!_status.ok())) return _status;               \
+  } while (0)
+
+// Internal helper for concatenating macro values.
+#define STATUS_MACROS_CONCAT_NAME_INNER(x, y) x##y
+#define STATUS_MACROS_CONCAT_NAME(x, y) STATUS_MACROS_CONCAT_NAME_INNER(x, y)
+
+template<typename T>
+Status DoAssignOrReturn(T& lhs, StatusOr<T> result) {
+  if (result.ok()) {
+    lhs = result.value();
+  }
+  return result.status();
+}
+
+#define ASSIGN_OR_RETURN_IMPL(status, lhs, rexpr) \
+  Status status = DoAssignOrReturn(lhs, (rexpr)); \
+  if (PROTOBUF_PREDICT_FALSE(!status.ok())) return status;
+
+// Executes an expression that returns a util::StatusOr, extracting its value
+// into the variable defined by lhs (or returning on error).
+//
+// Example: Assigning to an existing value
+//   ValueType value;
+//   ASSIGN_OR_RETURN(value, MaybeGetValue(arg));
+//
+// WARNING: ASSIGN_OR_RETURN expands into multiple statements; it cannot be used
+//  in a single statement (e.g. as the body of an if statement without {})!
+#define ASSIGN_OR_RETURN(lhs, rexpr) \
+  ASSIGN_OR_RETURN_IMPL( \
+      STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr);
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_STUBS_STATUS_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/statusor.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/statusor.h
new file mode 100644
index 0000000..20e603e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/statusor.h
@@ -0,0 +1,253 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// StatusOr<T> is the union of a Status object and a T
+// object. StatusOr models the concept of an object that is either a
+// usable value, or an error Status explaining why such a value is
+// not present. To this end, StatusOr<T> does not allow its Status
+// value to be OkStatus(). Further, StatusOr<T*> does not allow the
+// contained pointer to be nullptr.
+//
+// The primary use-case for StatusOr<T> is as the return value of a
+// function which may fail.
+//
+// Example client usage for a StatusOr<T>, where T is not a pointer:
+//
+//  StatusOr<float> result = DoBigCalculationThatCouldFail();
+//  if (result.ok()) {
+//    float answer = result.value();
+//    printf("Big calculation yielded: %f", answer);
+//  } else {
+//    LOG(ERROR) << result.status();
+//  }
+//
+// Example client usage for a StatusOr<T*>:
+//
+//  StatusOr<Foo*> result = FooFactory::MakeNewFoo(arg);
+//  if (result.ok()) {
+//    std::unique_ptr<Foo> foo(result.value());
+//    foo->DoSomethingCool();
+//  } else {
+//    LOG(ERROR) << result.status();
+//  }
+//
+// Example factory implementation returning StatusOr<T*>:
+//
+//  StatusOr<Foo*> FooFactory::MakeNewFoo(int arg) {
+//    if (arg <= 0) {
+//      return InvalidArgumentError("Arg must be positive");
+//    } else {
+//      return new Foo(arg);
+//    }
+//  }
+//
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
+#define GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
+
+#include <new>
+#include <string>
+#include <utility>
+
+#include <google/protobuf/stubs/status.h>
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace statusor_internal {
+
+template<typename T>
+class StatusOr {
+  template<typename U> friend class StatusOr;
+
+ public:
+  using value_type = T;
+
+  // Construct a new StatusOr with Status::UNKNOWN status.
+  // Construct a new StatusOr with UnknownError() status.
+  explicit StatusOr();
+
+  // Construct a new StatusOr with the given non-ok status. After calling
+  // this constructor, calls to value() will CHECK-fail.
+  //
+  // NOTE: Not explicit - we want to use StatusOr<T> as a return
+  // value, so it is convenient and sensible to be able to do 'return
+  // Status()' when the return type is StatusOr<T>.
+  //
+  // REQUIRES: status != OkStatus(). This requirement is DCHECKed.
+  // In optimized builds, passing OkStatus() here will have the effect
+  // of passing PosixErrorSpace::EINVAL as a fallback.
+  StatusOr(const Status& status);  // NOLINT
+
+  // Construct a new StatusOr with the given value. If T is a plain pointer,
+  // value must not be nullptr. After calling this constructor, calls to
+  // value() will succeed, and calls to status() will return OK.
+  //
+  // NOTE: Not explicit - we want to use StatusOr<T> as a return type
+  // so it is convenient and sensible to be able to do 'return T()'
+  // when when the return type is StatusOr<T>.
+  //
+  // REQUIRES: if T is a plain pointer, value != nullptr. This requirement is
+  // DCHECKed. In optimized builds, passing a null pointer here will have
+  // the effect of passing PosixErrorSpace::EINVAL as a fallback.
+  StatusOr(const T& value);  // NOLINT
+
+  // Copy constructor.
+  StatusOr(const StatusOr& other);
+
+  // Conversion copy constructor, T must be copy constructible from U
+  template<typename U>
+  StatusOr(const StatusOr<U>& other);
+
+  // Assignment operator.
+  StatusOr& operator=(const StatusOr& other);
+
+  // Conversion assignment operator, T must be assignable from U
+  template<typename U>
+  StatusOr& operator=(const StatusOr<U>& other);
+
+  // Returns a reference to our status. If this contains a T, then
+  // returns OkStatus().
+  const Status& status() const;
+
+  // Returns this->status().ok()
+  bool ok() const;
+
+  // Returns a reference to our current value, or CHECK-fails if !this->ok().
+  const T& value () const;
+
+ private:
+  Status status_;
+  T value_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation details for StatusOr<T>
+
+class PROTOBUF_EXPORT StatusOrHelper {
+ public:
+  // Move type-agnostic error handling to the .cc.
+  static void Crash(const util::Status& status);
+
+  // Customized behavior for StatusOr<T> vs. StatusOr<T*>
+  template<typename T>
+  struct Specialize;
+};
+
+template<typename T>
+struct StatusOrHelper::Specialize {
+  // For non-pointer T, a reference can never be nullptr.
+  static inline bool IsValueNull(const T& /*t*/) { return false; }
+};
+
+template<typename T>
+struct StatusOrHelper::Specialize<T*> {
+  static inline bool IsValueNull(const T* t) { return t == nullptr; }
+};
+
+template <typename T>
+inline StatusOr<T>::StatusOr() : status_(util::UnknownError("")) {}
+
+template<typename T>
+inline StatusOr<T>::StatusOr(const Status& status) {
+  if (status.ok()) {
+    status_ = util::InternalError("OkStatus() is not a valid argument.");
+  } else {
+    status_ = status;
+  }
+}
+
+template<typename T>
+inline StatusOr<T>::StatusOr(const T& value) {
+  if (StatusOrHelper::Specialize<T>::IsValueNull(value)) {
+    status_ = util::InternalError("nullptr is not a valid argument.");
+  } else {
+    status_ = util::OkStatus();
+    value_ = value;
+  }
+}
+
+template<typename T>
+inline StatusOr<T>::StatusOr(const StatusOr<T>& other)
+    : status_(other.status_), value_(other.value_) {
+}
+
+template<typename T>
+inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<T>& other) {
+  status_ = other.status_;
+  value_ = other.value_;
+  return *this;
+}
+
+template<typename T>
+template<typename U>
+inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
+    : status_(other.status_), value_(other.status_.ok() ? other.value_ : T()) {
+}
+
+template<typename T>
+template<typename U>
+inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
+  status_ = other.status_;
+  if (status_.ok()) value_ = other.value_;
+  return *this;
+}
+
+template<typename T>
+inline const Status& StatusOr<T>::status() const {
+  return status_;
+}
+
+template<typename T>
+inline bool StatusOr<T>::ok() const {
+  return status().ok();
+}
+
+template<typename T>
+inline const T& StatusOr<T>::value() const {
+  if (!status_.ok()) {
+    StatusOrHelper::Crash(status_);
+  }
+  return value_;
+}
+
+}  // namespace statusor_internal
+
+using ::google::protobuf::util::statusor_internal::StatusOr;
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/stl_util.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/stl_util.h
new file mode 100644
index 0000000..e6260d0
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/stl_util.h
@@ -0,0 +1,90 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// from google3/util/gtl/stl_util.h
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STL_UTIL_H__
+#define GOOGLE_PROTOBUF_STUBS_STL_UTIL_H__
+
+#include <google/protobuf/stubs/common.h>
+
+#include <algorithm>
+
+// Must be last.
+#include <google/protobuf/port_def.inc>  // NOLINT
+
+namespace google {
+namespace protobuf {
+
+// Inside Google, this function implements a horrible, disgusting hack in which
+// we reach into the string's private implementation and resize it without
+// initializing the new bytes.  In some cases doing this can significantly
+// improve performance.  However, since it's totally non-portable it has no
+// place in open source code.  Feel free to fill this function in with your
+// own disgusting hack if you want the perf boost.
+inline void STLStringResizeUninitialized(std::string* s, size_t new_size) {
+  s->resize(new_size);
+}
+
+// As above, but we make sure to follow amortized growth in which we always
+// increase the capacity by at least a constant factor >1.
+inline void STLStringResizeUninitializedAmortized(std::string* s,
+                                                  size_t new_size) {
+  const size_t cap = s->capacity();
+  if (new_size > cap) {
+    // Make sure to always grow by at least a factor of 2x.
+    s->reserve(std::max<size_t>(new_size, 2 * cap));
+  }
+  STLStringResizeUninitialized(s, new_size);
+}
+
+// Return a mutable char* pointing to a string's internal buffer,
+// which may not be null-terminated. Writing through this pointer will
+// modify the string.
+//
+// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the
+// next call to a string method that invalidates iterators.
+//
+// As of 2006-04, there is no standard-blessed way of getting a
+// mutable reference to a string's internal buffer. However, issue 530
+// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530)
+// proposes this as the method. According to Matt Austern, this should
+// already work on all current implementations.
+inline char* string_as_array(std::string* str) {
+  // DO NOT USE const_cast<char*>(str->data())! See the unittest for why.
+  return str->empty() ? nullptr : &*str->begin();
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>  // NOLINT
+
+#endif  // GOOGLE_PROTOBUF_STUBS_STL_UTIL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/stringpiece.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/stringpiece.h
new file mode 100644
index 0000000..c63e25b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/stringpiece.h
@@ -0,0 +1,402 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// A StringPiece points to part or all of a string, Cord, double-quoted string
+// literal, or other string-like object.  A StringPiece does *not* own the
+// string to which it points.  A StringPiece is not null-terminated.
+//
+// You can use StringPiece as a function or method parameter.  A StringPiece
+// parameter can receive a double-quoted string literal argument, a "const
+// char*" argument, a string argument, or a StringPiece argument with no data
+// copying.  Systematic use of StringPiece for arguments reduces data
+// copies and strlen() calls.
+//
+// Prefer passing StringPieces by value:
+//   void MyFunction(StringPiece arg);
+// If circumstances require, you may also pass by const reference:
+//   void MyFunction(const StringPiece& arg);  // not preferred
+// Both of these have the same lifetime semantics.  Passing by value
+// generates slightly smaller code.  For more discussion, see the thread
+// go/stringpiecebyvalue on c-users.
+//
+// StringPiece is also suitable for local variables if you know that
+// the lifetime of the underlying object is longer than the lifetime
+// of your StringPiece variable.
+//
+// Beware of binding a StringPiece to a temporary:
+//   StringPiece sp = obj.MethodReturningString();  // BAD: lifetime problem
+//
+// This code is okay:
+//   string str = obj.MethodReturningString();  // str owns its contents
+//   StringPiece sp(str);  // GOOD, because str outlives sp
+//
+// StringPiece is sometimes a poor choice for a return value and usually a poor
+// choice for a data member.  If you do use a StringPiece this way, it is your
+// responsibility to ensure that the object pointed to by the StringPiece
+// outlives the StringPiece.
+//
+// A StringPiece may represent just part of a string; thus the name "Piece".
+// For example, when splitting a string, vector<StringPiece> is a natural data
+// type for the output.  For another example, a Cord is a non-contiguous,
+// potentially very long string-like object.  The Cord class has an interface
+// that iteratively provides StringPiece objects that point to the
+// successive pieces of a Cord object.
+//
+// A StringPiece is not null-terminated.  If you write code that scans a
+// StringPiece, you must check its length before reading any characters.
+// Common idioms that work on null-terminated strings do not work on
+// StringPiece objects.
+//
+// There are several ways to create a null StringPiece:
+//   StringPiece()
+//   StringPiece(nullptr)
+//   StringPiece(nullptr, 0)
+// For all of the above, sp.data() == nullptr, sp.length() == 0,
+// and sp.empty() == true.  Also, if you create a StringPiece with
+// a non-null pointer then sp.data() != nullptr.  Once created,
+// sp.data() will stay either nullptr or not-nullptr, except if you call
+// sp.clear() or sp.set().
+//
+// Thus, you can use StringPiece(nullptr) to signal an out-of-band value
+// that is different from other StringPiece values.  This is similar
+// to the way that const char* p1 = nullptr; is different from
+// const char* p2 = "";.
+//
+// There are many ways to create an empty StringPiece:
+//   StringPiece()
+//   StringPiece(nullptr)
+//   StringPiece(nullptr, 0)
+//   StringPiece("")
+//   StringPiece("", 0)
+//   StringPiece("abcdef", 0)
+//   StringPiece("abcdef"+6, 0)
+// For all of the above, sp.length() will be 0 and sp.empty() will be true.
+// For some empty StringPiece values, sp.data() will be nullptr.
+// For some empty StringPiece values, sp.data() will not be nullptr.
+//
+// Be careful not to confuse: null StringPiece and empty StringPiece.
+// The set of empty StringPieces properly includes the set of null StringPieces.
+// That is, every null StringPiece is an empty StringPiece,
+// but some non-null StringPieces are empty Stringpieces too.
+//
+// All empty StringPiece values compare equal to each other.
+// Even a null StringPieces compares equal to a non-null empty StringPiece:
+//  StringPiece() == StringPiece("", 0)
+//  StringPiece(nullptr) == StringPiece("abc", 0)
+//  StringPiece(nullptr, 0) == StringPiece("abcdef"+6, 0)
+//
+// Look carefully at this example:
+//   StringPiece("") == nullptr
+// True or false?  TRUE, because StringPiece::operator== converts
+// the right-hand side from nullptr to StringPiece(nullptr),
+// and then compares two zero-length spans of characters.
+// However, we are working to make this example produce a compile error.
+//
+// Suppose you want to write:
+//   bool TestWhat?(StringPiece sp) { return sp == nullptr; }  // BAD
+// Do not do that.  Write one of these instead:
+//   bool TestNull(StringPiece sp) { return sp.data() == nullptr; }
+//   bool TestEmpty(StringPiece sp) { return sp.empty(); }
+// The intent of TestWhat? is unclear.  Did you mean TestNull or TestEmpty?
+// Right now, TestWhat? behaves likes TestEmpty.
+// We are working to make TestWhat? produce a compile error.
+// TestNull is good to test for an out-of-band signal.
+// TestEmpty is good to test for an empty StringPiece.
+//
+// Caveats (again):
+// (1) The lifetime of the pointed-to string (or piece of a string)
+//     must be longer than the lifetime of the StringPiece.
+// (2) There may or may not be a '\0' character after the end of
+//     StringPiece data.
+// (3) A null StringPiece is empty.
+//     An empty StringPiece may or may not be a null StringPiece.
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STRINGPIECE_H_
+#define GOOGLE_PROTOBUF_STUBS_STRINGPIECE_H_
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+#include <iosfwd>
+#include <limits>
+#include <string>
+
+#if defined(__cpp_lib_string_view)
+#include <string_view>
+#endif
+
+#include <google/protobuf/stubs/hash.h>
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace stringpiece_internal {
+
+class PROTOBUF_EXPORT StringPiece {
+ public:
+  using traits_type = std::char_traits<char>;
+  using value_type = char;
+  using pointer = char*;
+  using const_pointer = const char*;
+  using reference = char&;
+  using const_reference = const char&;
+  using const_iterator = const char*;
+  using iterator = const_iterator;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+  using reverse_iterator = const_reverse_iterator;
+  using size_type = size_t;
+  using difference_type = std::ptrdiff_t;
+
+ private:
+  const char* ptr_;
+  size_type length_;
+
+  static constexpr size_type kMaxSize =
+      (std::numeric_limits<difference_type>::max)();
+
+  static size_type CheckSize(size_type size) {
+#if !defined(NDEBUG) || defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0
+    if (PROTOBUF_PREDICT_FALSE(size > kMaxSize)) {
+      // Some people grep for this message in logs
+      // so take care if you ever change it.
+      LogFatalSizeTooBig(size, "string length exceeds max size");
+    }
+#endif
+    return size;
+  }
+
+  // Out-of-line error path.
+  static void LogFatalSizeTooBig(size_type size, const char* details);
+
+ public:
+  // We provide non-explicit singleton constructors so users can pass
+  // in a "const char*" or a "string" wherever a "StringPiece" is
+  // expected.
+  //
+  // Style guide exception granted:
+  // http://goto/style-guide-exception-20978288
+  StringPiece() : ptr_(nullptr), length_(0) {}
+
+  StringPiece(const char* str)  // NOLINT(runtime/explicit)
+      : ptr_(str), length_(0) {
+    if (str != nullptr) {
+      length_ = CheckSize(strlen(str));
+    }
+  }
+
+  template <class Allocator>
+  StringPiece(  // NOLINT(runtime/explicit)
+      const std::basic_string<char, std::char_traits<char>, Allocator>& str)
+      : ptr_(str.data()), length_(0) {
+    length_ = CheckSize(str.size());
+  }
+
+#if defined(__cpp_lib_string_view)
+  StringPiece(  // NOLINT(runtime/explicit)
+      std::string_view str)
+      : ptr_(str.data()), length_(0) {
+    length_ = CheckSize(str.size());
+  }
+#endif
+
+  StringPiece(const char* offset, size_type len)
+      : ptr_(offset), length_(CheckSize(len)) {}
+
+  // data() may return a pointer to a buffer with embedded NULs, and the
+  // returned buffer may or may not be null terminated.  Therefore it is
+  // typically a mistake to pass data() to a routine that expects a NUL
+  // terminated string.
+  const_pointer data() const { return ptr_; }
+  size_type size() const { return length_; }
+  size_type length() const { return length_; }
+  bool empty() const { return length_ == 0; }
+
+  char operator[](size_type i) const {
+    assert(i < length_);
+    return ptr_[i];
+  }
+
+  void remove_prefix(size_type n) {
+    assert(length_ >= n);
+    ptr_ += n;
+    length_ -= n;
+  }
+
+  void remove_suffix(size_type n) {
+    assert(length_ >= n);
+    length_ -= n;
+  }
+
+  // returns {-1, 0, 1}
+  int compare(StringPiece x) const {
+    size_type min_size = length_ < x.length_ ? length_ : x.length_;
+    int r = memcmp(ptr_, x.ptr_, static_cast<size_t>(min_size));
+    if (r < 0) return -1;
+    if (r > 0) return 1;
+    if (length_ < x.length_) return -1;
+    if (length_ > x.length_) return 1;
+    return 0;
+  }
+
+  std::string as_string() const { return ToString(); }
+  // We also define ToString() here, since many other string-like
+  // interfaces name the routine that converts to a C++ string
+  // "ToString", and it's confusing to have the method that does that
+  // for a StringPiece be called "as_string()".  We also leave the
+  // "as_string()" method defined here for existing code.
+  std::string ToString() const {
+    if (ptr_ == nullptr) return "";
+    return std::string(data(), static_cast<size_type>(size()));
+  }
+
+  explicit operator std::string() const { return ToString(); }
+
+  void CopyToString(std::string* target) const;
+  void AppendToString(std::string* target) const;
+
+  bool starts_with(StringPiece x) const {
+    return (length_ >= x.length_) &&
+           (memcmp(ptr_, x.ptr_, static_cast<size_t>(x.length_)) == 0);
+  }
+
+  bool ends_with(StringPiece x) const {
+    return ((length_ >= x.length_) &&
+            (memcmp(ptr_ + (length_-x.length_), x.ptr_,
+                 static_cast<size_t>(x.length_)) == 0));
+  }
+
+  // Checks whether StringPiece starts with x and if so advances the beginning
+  // of it to past the match.  It's basically a shortcut for starts_with
+  // followed by remove_prefix.
+  bool Consume(StringPiece x);
+  // Like above but for the end of the string.
+  bool ConsumeFromEnd(StringPiece x);
+
+  // standard STL container boilerplate
+  static const size_type npos;
+  const_iterator begin() const { return ptr_; }
+  const_iterator end() const { return ptr_ + length_; }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(ptr_ + length_);
+  }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(ptr_);
+  }
+  size_type max_size() const { return length_; }
+  size_type capacity() const { return length_; }
+
+  // cpplint.py emits a false positive [build/include_what_you_use]
+  size_type copy(char* buf, size_type n, size_type pos = 0) const;  // NOLINT
+
+  bool contains(StringPiece s) const;
+
+  size_type find(StringPiece s, size_type pos = 0) const;
+  size_type find(char c, size_type pos = 0) const;
+  size_type rfind(StringPiece s, size_type pos = npos) const;
+  size_type rfind(char c, size_type pos = npos) const;
+
+  size_type find_first_of(StringPiece s, size_type pos = 0) const;
+  size_type find_first_of(char c, size_type pos = 0) const {
+    return find(c, pos);
+  }
+  size_type find_first_not_of(StringPiece s, size_type pos = 0) const;
+  size_type find_first_not_of(char c, size_type pos = 0) const;
+  size_type find_last_of(StringPiece s, size_type pos = npos) const;
+  size_type find_last_of(char c, size_type pos = npos) const {
+    return rfind(c, pos);
+  }
+  size_type find_last_not_of(StringPiece s, size_type pos = npos) const;
+  size_type find_last_not_of(char c, size_type pos = npos) const;
+
+  StringPiece substr(size_type pos, size_type n = npos) const;
+};
+
+// This large function is defined inline so that in a fairly common case where
+// one of the arguments is a literal, the compiler can elide a lot of the
+// following comparisons.
+inline bool operator==(StringPiece x, StringPiece y) {
+  StringPiece::size_type len = x.size();
+  if (len != y.size()) {
+    return false;
+  }
+
+  return x.data() == y.data() || len <= 0 ||
+      memcmp(x.data(), y.data(), static_cast<size_t>(len)) == 0;
+}
+
+inline bool operator!=(StringPiece x, StringPiece y) {
+  return !(x == y);
+}
+
+inline bool operator<(StringPiece x, StringPiece y) {
+  const StringPiece::size_type min_size =
+      x.size() < y.size() ? x.size() : y.size();
+  const int r = memcmp(x.data(), y.data(), static_cast<size_t>(min_size));
+  return (r < 0) || (r == 0 && x.size() < y.size());
+}
+
+inline bool operator>(StringPiece x, StringPiece y) {
+  return y < x;
+}
+
+inline bool operator<=(StringPiece x, StringPiece y) {
+  return !(x > y);
+}
+
+inline bool operator>=(StringPiece x, StringPiece y) {
+  return !(x < y);
+}
+
+// allow StringPiece to be logged
+extern std::ostream& operator<<(std::ostream& o, StringPiece piece);
+
+}  // namespace stringpiece_internal
+
+using ::google::protobuf::stringpiece_internal::StringPiece;
+
+}  // namespace protobuf
+}  // namespace google
+
+GOOGLE_PROTOBUF_HASH_NAMESPACE_DECLARATION_START
+template<> struct hash<StringPiece> {
+  size_t operator()(const StringPiece& s) const {
+    size_t result = 0;
+    for (const char *str = s.data(), *end = str + s.size(); str < end; str++) {
+      result = 5 * result + static_cast<size_t>(*str);
+    }
+    return result;
+  }
+};
+GOOGLE_PROTOBUF_HASH_NAMESPACE_DECLARATION_END
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // STRINGS_STRINGPIECE_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/stringprintf.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/stringprintf.h
new file mode 100644
index 0000000..e3858be
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/stringprintf.h
@@ -0,0 +1,85 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2012 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// from google3/base/stringprintf.h
+//
+// Printf variants that place their output in a C++ string.
+//
+// Usage:
+//      string result = StringPrintf("%d %s\n", 10, "hello");
+//      SStringPrintf(&result, "%d %s\n", 10, "hello");
+//      StringAppendF(&result, "%d %s\n", 20, "there");
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STRINGPRINTF_H
+#define GOOGLE_PROTOBUF_STUBS_STRINGPRINTF_H
+
+#include <stdarg.h>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+// Return a C++ string
+PROTOBUF_EXPORT extern std::string StringPrintf(const char* format, ...);
+
+// Store result into a supplied string and return it
+PROTOBUF_EXPORT extern const std::string& SStringPrintf(std::string* dst,
+                                                        const char* format,
+                                                        ...);
+
+// Append result to a supplied string
+PROTOBUF_EXPORT extern void StringAppendF(std::string* dst, const char* format,
+                                          ...);
+
+// Lower-level routine that takes a va_list and appends to a specified
+// string.  All other routines are just convenience wrappers around it.
+PROTOBUF_EXPORT extern void StringAppendV(std::string* dst, const char* format,
+                                          va_list ap);
+
+// The max arguments supported by StringPrintfVector
+PROTOBUF_EXPORT extern const int kStringPrintfVectorMaxArgs;
+
+// You can use this version when all your arguments are strings, but
+// you don't know how many arguments you'll have at compile time.
+// StringPrintfVector will LOG(FATAL) if v.size() > kStringPrintfVectorMaxArgs
+PROTOBUF_EXPORT extern std::string StringPrintfVector(
+    const char* format, const std::vector<std::string>& v);
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_STRINGPRINTF_H
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/strutil.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/strutil.h
new file mode 100644
index 0000000..9658abf
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/strutil.h
@@ -0,0 +1,950 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// from google3/strings/strutil.h
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STRUTIL_H__
+#define GOOGLE_PROTOBUF_STUBS_STRUTIL_H__
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <stdlib.h>
+
+#include <cstring>
+#include <google/protobuf/port_def.inc>
+#include <vector>
+
+namespace google {
+namespace protobuf {
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#define strtoll  _strtoi64
+#define strtoull _strtoui64
+#elif defined(__DECCXX) && defined(__osf__)
+// HP C++ on Tru64 does not have strtoll, but strtol is already 64-bit.
+#define strtoll strtol
+#define strtoull strtoul
+#endif
+
+// ----------------------------------------------------------------------
+// ascii_isalnum()
+//    Check if an ASCII character is alphanumeric.  We can't use ctype's
+//    isalnum() because it is affected by locale.  This function is applied
+//    to identifiers in the protocol buffer language, not to natural-language
+//    strings, so locale should not be taken into account.
+// ascii_isdigit()
+//    Like above, but only accepts digits.
+// ascii_isspace()
+//    Check if the character is a space character.
+// ----------------------------------------------------------------------
+
+inline bool ascii_isalnum(char c) {
+  return ('a' <= c && c <= 'z') ||
+         ('A' <= c && c <= 'Z') ||
+         ('0' <= c && c <= '9');
+}
+
+inline bool ascii_isdigit(char c) {
+  return ('0' <= c && c <= '9');
+}
+
+inline bool ascii_isspace(char c) {
+  return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' ||
+      c == '\r';
+}
+
+inline bool ascii_isupper(char c) {
+  return c >= 'A' && c <= 'Z';
+}
+
+inline bool ascii_islower(char c) {
+  return c >= 'a' && c <= 'z';
+}
+
+inline char ascii_toupper(char c) {
+  return ascii_islower(c) ? c - ('a' - 'A') : c;
+}
+
+inline char ascii_tolower(char c) {
+  return ascii_isupper(c) ? c + ('a' - 'A') : c;
+}
+
+inline int hex_digit_to_int(char c) {
+  /* Assume ASCII. */
+  int x = static_cast<unsigned char>(c);
+  if (x > '9') {
+    x += 9;
+  }
+  return x & 0xf;
+}
+
+// ----------------------------------------------------------------------
+// HasPrefixString()
+//    Check if a string begins with a given prefix.
+// StripPrefixString()
+//    Given a string and a putative prefix, returns the string minus the
+//    prefix string if the prefix matches, otherwise the original
+//    string.
+// ----------------------------------------------------------------------
+inline bool HasPrefixString(StringPiece str, StringPiece prefix) {
+  return str.size() >= prefix.size() &&
+         memcmp(str.data(), prefix.data(), prefix.size()) == 0;
+}
+
+inline std::string StripPrefixString(const std::string& str,
+                                     const std::string& prefix) {
+  if (HasPrefixString(str, prefix)) {
+    return str.substr(prefix.size());
+  } else {
+    return str;
+  }
+}
+
+// ----------------------------------------------------------------------
+// HasSuffixString()
+//    Return true if str ends in suffix.
+// StripSuffixString()
+//    Given a string and a putative suffix, returns the string minus the
+//    suffix string if the suffix matches, otherwise the original
+//    string.
+// ----------------------------------------------------------------------
+inline bool HasSuffixString(StringPiece str, StringPiece suffix) {
+  return str.size() >= suffix.size() &&
+         memcmp(str.data() + str.size() - suffix.size(), suffix.data(),
+                suffix.size()) == 0;
+}
+
+inline std::string StripSuffixString(const std::string& str,
+                                     const std::string& suffix) {
+  if (HasSuffixString(str, suffix)) {
+    return str.substr(0, str.size() - suffix.size());
+  } else {
+    return str;
+  }
+}
+
+// ----------------------------------------------------------------------
+// ReplaceCharacters
+//    Replaces any occurrence of the character 'remove' (or the characters
+//    in 'remove') with the character 'replacewith'.
+//    Good for keeping html characters or protocol characters (\t) out
+//    of places where they might cause a problem.
+// StripWhitespace
+//    Removes whitespaces from both ends of the given string.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT void ReplaceCharacters(std::string* s, const char* remove,
+                                       char replacewith);
+
+PROTOBUF_EXPORT void StripWhitespace(std::string* s);
+
+// ----------------------------------------------------------------------
+// LowerString()
+// UpperString()
+// ToUpper()
+//    Convert the characters in "s" to lowercase or uppercase.  ASCII-only:
+//    these functions intentionally ignore locale because they are applied to
+//    identifiers used in the Protocol Buffer language, not to natural-language
+//    strings.
+// ----------------------------------------------------------------------
+
+inline void LowerString(std::string* s) {
+  std::string::iterator end = s->end();
+  for (std::string::iterator i = s->begin(); i != end; ++i) {
+    // tolower() changes based on locale.  We don't want this!
+    if ('A' <= *i && *i <= 'Z') *i += 'a' - 'A';
+  }
+}
+
+inline void UpperString(std::string* s) {
+  std::string::iterator end = s->end();
+  for (std::string::iterator i = s->begin(); i != end; ++i) {
+    // toupper() changes based on locale.  We don't want this!
+    if ('a' <= *i && *i <= 'z') *i += 'A' - 'a';
+  }
+}
+
+inline void ToUpper(std::string* s) { UpperString(s); }
+
+inline std::string ToUpper(const std::string& s) {
+  std::string out = s;
+  UpperString(&out);
+  return out;
+}
+
+// ----------------------------------------------------------------------
+// StringReplace()
+//    Give me a string and two patterns "old" and "new", and I replace
+//    the first instance of "old" in the string with "new", if it
+//    exists.  RETURN a new string, regardless of whether the replacement
+//    happened or not.
+// ----------------------------------------------------------------------
+
+PROTOBUF_EXPORT std::string StringReplace(const std::string& s,
+                                          const std::string& oldsub,
+                                          const std::string& newsub,
+                                          bool replace_all);
+
+// ----------------------------------------------------------------------
+// SplitStringUsing()
+//    Split a string using a character delimiter. Append the components
+//    to 'result'.  If there are consecutive delimiters, this function skips
+//    over all of them.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT void SplitStringUsing(StringPiece full, const char* delim,
+                                      std::vector<std::string>* res);
+
+// Split a string using one or more byte delimiters, presented
+// as a nul-terminated c string. Append the components to 'result'.
+// If there are consecutive delimiters, this function will return
+// corresponding empty strings.  If you want to drop the empty
+// strings, try SplitStringUsing().
+//
+// If "full" is the empty string, yields an empty string as the only value.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT void SplitStringAllowEmpty(StringPiece full, const char* delim,
+                                           std::vector<std::string>* result);
+
+// ----------------------------------------------------------------------
+// Split()
+//    Split a string using a character delimiter.
+// ----------------------------------------------------------------------
+inline std::vector<std::string> Split(StringPiece full, const char* delim,
+                                      bool skip_empty = true) {
+  std::vector<std::string> result;
+  if (skip_empty) {
+    SplitStringUsing(full, delim, &result);
+  } else {
+    SplitStringAllowEmpty(full, delim, &result);
+  }
+  return result;
+}
+
+// ----------------------------------------------------------------------
+// JoinStrings()
+//    These methods concatenate a vector of strings into a C++ string, using
+//    the C-string "delim" as a separator between components. There are two
+//    flavors of the function, one flavor returns the concatenated string,
+//    another takes a pointer to the target string. In the latter case the
+//    target string is cleared and overwritten.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT void JoinStrings(const std::vector<std::string>& components,
+                                 const char* delim, std::string* result);
+
+inline std::string JoinStrings(const std::vector<std::string>& components,
+                               const char* delim) {
+  std::string result;
+  JoinStrings(components, delim, &result);
+  return result;
+}
+
+// ----------------------------------------------------------------------
+// UnescapeCEscapeSequences()
+//    Copies "source" to "dest", rewriting C-style escape sequences
+//    -- '\n', '\r', '\\', '\ooo', etc -- to their ASCII
+//    equivalents.  "dest" must be sufficiently large to hold all
+//    the characters in the rewritten string (i.e. at least as large
+//    as strlen(source) + 1 should be safe, since the replacements
+//    are always shorter than the original escaped sequences).  It's
+//    safe for source and dest to be the same.  RETURNS the length
+//    of dest.
+//
+//    It allows hex sequences \xhh, or generally \xhhhhh with an
+//    arbitrary number of hex digits, but all of them together must
+//    specify a value of a single byte (e.g. \x0045 is equivalent
+//    to \x45, and \x1234 is erroneous).
+//
+//    It also allows escape sequences of the form \uhhhh (exactly four
+//    hex digits, upper or lower case) or \Uhhhhhhhh (exactly eight
+//    hex digits, upper or lower case) to specify a Unicode code
+//    point. The dest array will contain the UTF8-encoded version of
+//    that code-point (e.g., if source contains \u2019, then dest will
+//    contain the three bytes 0xE2, 0x80, and 0x99).
+//
+//    Errors: In the first form of the call, errors are reported with
+//    LOG(ERROR). The same is true for the second form of the call if
+//    the pointer to the string std::vector is nullptr; otherwise, error
+//    messages are stored in the std::vector. In either case, the effect on
+//    the dest array is not defined, but rest of the source will be
+//    processed.
+//    ----------------------------------------------------------------------
+
+PROTOBUF_EXPORT int UnescapeCEscapeSequences(const char* source, char* dest);
+PROTOBUF_EXPORT int UnescapeCEscapeSequences(const char* source, char* dest,
+                                             std::vector<std::string>* errors);
+
+// ----------------------------------------------------------------------
+// UnescapeCEscapeString()
+//    This does the same thing as UnescapeCEscapeSequences, but creates
+//    a new string. The caller does not need to worry about allocating
+//    a dest buffer. This should be used for non performance critical
+//    tasks such as printing debug messages. It is safe for src and dest
+//    to be the same.
+//
+//    The second call stores its errors in a supplied string vector.
+//    If the string vector pointer is nullptr, it reports the errors with LOG().
+//
+//    In the first and second calls, the length of dest is returned. In the
+//    the third call, the new string is returned.
+// ----------------------------------------------------------------------
+
+PROTOBUF_EXPORT int UnescapeCEscapeString(const std::string& src,
+                                          std::string* dest);
+PROTOBUF_EXPORT int UnescapeCEscapeString(const std::string& src,
+                                          std::string* dest,
+                                          std::vector<std::string>* errors);
+PROTOBUF_EXPORT std::string UnescapeCEscapeString(const std::string& src);
+
+// ----------------------------------------------------------------------
+// CEscape()
+//    Escapes 'src' using C-style escape sequences and returns the resulting
+//    string.
+//
+//    Escaped chars: \n, \r, \t, ", ', \, and !isprint().
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT std::string CEscape(const std::string& src);
+
+// ----------------------------------------------------------------------
+// CEscapeAndAppend()
+//    Escapes 'src' using C-style escape sequences, and appends the escaped
+//    string to 'dest'.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT void CEscapeAndAppend(StringPiece src, std::string* dest);
+
+namespace strings {
+// Like CEscape() but does not escape bytes with the upper bit set.
+PROTOBUF_EXPORT std::string Utf8SafeCEscape(const std::string& src);
+
+// Like CEscape() but uses hex (\x) escapes instead of octals.
+PROTOBUF_EXPORT std::string CHexEscape(const std::string& src);
+}  // namespace strings
+
+// ----------------------------------------------------------------------
+// strto32()
+// strtou32()
+// strto64()
+// strtou64()
+//    Architecture-neutral plug compatible replacements for strtol() and
+//    strtoul().  Long's have different lengths on ILP-32 and LP-64
+//    platforms, so using these is safer, from the point of view of
+//    overflow behavior, than using the standard libc functions.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT int32_t strto32_adaptor(const char* nptr, char** endptr,
+                                        int base);
+PROTOBUF_EXPORT uint32_t strtou32_adaptor(const char* nptr, char** endptr,
+                                          int base);
+
+inline int32_t strto32(const char *nptr, char **endptr, int base) {
+  if (sizeof(int32_t) == sizeof(long))
+    return strtol(nptr, endptr, base);
+  else
+    return strto32_adaptor(nptr, endptr, base);
+}
+
+inline uint32_t strtou32(const char *nptr, char **endptr, int base) {
+  if (sizeof(uint32_t) == sizeof(unsigned long))
+    return strtoul(nptr, endptr, base);
+  else
+    return strtou32_adaptor(nptr, endptr, base);
+}
+
+// For now, long long is 64-bit on all the platforms we care about, so these
+// functions can simply pass the call to strto[u]ll.
+inline int64_t strto64(const char *nptr, char **endptr, int base) {
+  static_assert(sizeof(int64_t) == sizeof(long long),
+                "sizeof int64_t is not sizeof long long");
+  return strtoll(nptr, endptr, base);
+}
+
+inline uint64_t strtou64(const char *nptr, char **endptr, int base) {
+  static_assert(sizeof(uint64_t) == sizeof(unsigned long long),
+                "sizeof uint64_t is not sizeof unsigned long long");
+  return strtoull(nptr, endptr, base);
+}
+
+// ----------------------------------------------------------------------
+// safe_strtob()
+// safe_strto32()
+// safe_strtou32()
+// safe_strto64()
+// safe_strtou64()
+// safe_strtof()
+// safe_strtod()
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT bool safe_strtob(StringPiece str, bool* value);
+
+PROTOBUF_EXPORT bool safe_strto32(const std::string& str, int32_t* value);
+PROTOBUF_EXPORT bool safe_strtou32(const std::string& str, uint32_t* value);
+inline bool safe_strto32(const char* str, int32_t* value) {
+  return safe_strto32(std::string(str), value);
+}
+inline bool safe_strto32(StringPiece str, int32_t* value) {
+  return safe_strto32(str.ToString(), value);
+}
+inline bool safe_strtou32(const char* str, uint32_t* value) {
+  return safe_strtou32(std::string(str), value);
+}
+inline bool safe_strtou32(StringPiece str, uint32_t* value) {
+  return safe_strtou32(str.ToString(), value);
+}
+
+PROTOBUF_EXPORT bool safe_strto64(const std::string& str, int64_t* value);
+PROTOBUF_EXPORT bool safe_strtou64(const std::string& str, uint64_t* value);
+inline bool safe_strto64(const char* str, int64_t* value) {
+  return safe_strto64(std::string(str), value);
+}
+inline bool safe_strto64(StringPiece str, int64_t* value) {
+  return safe_strto64(str.ToString(), value);
+}
+inline bool safe_strtou64(const char* str, uint64_t* value) {
+  return safe_strtou64(std::string(str), value);
+}
+inline bool safe_strtou64(StringPiece str, uint64_t* value) {
+  return safe_strtou64(str.ToString(), value);
+}
+
+PROTOBUF_EXPORT bool safe_strtof(const char* str, float* value);
+PROTOBUF_EXPORT bool safe_strtod(const char* str, double* value);
+inline bool safe_strtof(const std::string& str, float* value) {
+  return safe_strtof(str.c_str(), value);
+}
+inline bool safe_strtod(const std::string& str, double* value) {
+  return safe_strtod(str.c_str(), value);
+}
+inline bool safe_strtof(StringPiece str, float* value) {
+  return safe_strtof(str.ToString(), value);
+}
+inline bool safe_strtod(StringPiece str, double* value) {
+  return safe_strtod(str.ToString(), value);
+}
+
+// ----------------------------------------------------------------------
+// FastIntToBuffer()
+// FastHexToBuffer()
+// FastHex64ToBuffer()
+// FastHex32ToBuffer()
+// FastTimeToBuffer()
+//    These are intended for speed.  FastIntToBuffer() assumes the
+//    integer is non-negative.  FastHexToBuffer() puts output in
+//    hex rather than decimal.  FastTimeToBuffer() puts the output
+//    into RFC822 format.
+//
+//    FastHex64ToBuffer() puts a 64-bit unsigned value in hex-format,
+//    padded to exactly 16 bytes (plus one byte for '\0')
+//
+//    FastHex32ToBuffer() puts a 32-bit unsigned value in hex-format,
+//    padded to exactly 8 bytes (plus one byte for '\0')
+//
+//       All functions take the output buffer as an arg.
+//    They all return a pointer to the beginning of the output,
+//    which may not be the beginning of the input buffer.
+// ----------------------------------------------------------------------
+
+// Suggested buffer size for FastToBuffer functions.  Also works with
+// DoubleToBuffer() and FloatToBuffer().
+static const int kFastToBufferSize = 32;
+
+PROTOBUF_EXPORT char* FastInt32ToBuffer(int32_t i, char* buffer);
+PROTOBUF_EXPORT char* FastInt64ToBuffer(int64_t i, char* buffer);
+char* FastUInt32ToBuffer(uint32_t i, char* buffer);  // inline below
+char* FastUInt64ToBuffer(uint64_t i, char* buffer);  // inline below
+PROTOBUF_EXPORT char* FastHexToBuffer(int i, char* buffer);
+PROTOBUF_EXPORT char* FastHex64ToBuffer(uint64_t i, char* buffer);
+PROTOBUF_EXPORT char* FastHex32ToBuffer(uint32_t i, char* buffer);
+
+// at least 22 bytes long
+inline char* FastIntToBuffer(int i, char* buffer) {
+  return (sizeof(i) == 4 ?
+          FastInt32ToBuffer(i, buffer) : FastInt64ToBuffer(i, buffer));
+}
+inline char* FastUIntToBuffer(unsigned int i, char* buffer) {
+  return (sizeof(i) == 4 ?
+          FastUInt32ToBuffer(i, buffer) : FastUInt64ToBuffer(i, buffer));
+}
+inline char* FastLongToBuffer(long i, char* buffer) {
+  return (sizeof(i) == 4 ?
+          FastInt32ToBuffer(i, buffer) : FastInt64ToBuffer(i, buffer));
+}
+inline char* FastULongToBuffer(unsigned long i, char* buffer) {
+  return (sizeof(i) == 4 ?
+          FastUInt32ToBuffer(i, buffer) : FastUInt64ToBuffer(i, buffer));
+}
+
+// ----------------------------------------------------------------------
+// FastInt32ToBufferLeft()
+// FastUInt32ToBufferLeft()
+// FastInt64ToBufferLeft()
+// FastUInt64ToBufferLeft()
+//
+// Like the Fast*ToBuffer() functions above, these are intended for speed.
+// Unlike the Fast*ToBuffer() functions, however, these functions write
+// their output to the beginning of the buffer (hence the name, as the
+// output is left-aligned).  The caller is responsible for ensuring that
+// the buffer has enough space to hold the output.
+//
+// Returns a pointer to the end of the string (i.e. the null character
+// terminating the string).
+// ----------------------------------------------------------------------
+
+PROTOBUF_EXPORT char* FastInt32ToBufferLeft(int32_t i, char* buffer);
+PROTOBUF_EXPORT char* FastUInt32ToBufferLeft(uint32_t i, char* buffer);
+PROTOBUF_EXPORT char* FastInt64ToBufferLeft(int64_t i, char* buffer);
+PROTOBUF_EXPORT char* FastUInt64ToBufferLeft(uint64_t i, char* buffer);
+
+// Just define these in terms of the above.
+inline char* FastUInt32ToBuffer(uint32_t i, char* buffer) {
+  FastUInt32ToBufferLeft(i, buffer);
+  return buffer;
+}
+inline char* FastUInt64ToBuffer(uint64_t i, char* buffer) {
+  FastUInt64ToBufferLeft(i, buffer);
+  return buffer;
+}
+
+inline std::string SimpleBtoa(bool value) { return value ? "true" : "false"; }
+
+// ----------------------------------------------------------------------
+// SimpleItoa()
+//    Description: converts an integer to a string.
+//
+//    Return value: string
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT std::string SimpleItoa(int i);
+PROTOBUF_EXPORT std::string SimpleItoa(unsigned int i);
+PROTOBUF_EXPORT std::string SimpleItoa(long i);
+PROTOBUF_EXPORT std::string SimpleItoa(unsigned long i);
+PROTOBUF_EXPORT std::string SimpleItoa(long long i);
+PROTOBUF_EXPORT std::string SimpleItoa(unsigned long long i);
+
+// ----------------------------------------------------------------------
+// SimpleDtoa()
+// SimpleFtoa()
+// DoubleToBuffer()
+// FloatToBuffer()
+//    Description: converts a double or float to a string which, if
+//    passed to NoLocaleStrtod(), will produce the exact same original double
+//    (except in case of NaN; all NaNs are considered the same value).
+//    We try to keep the string short but it's not guaranteed to be as
+//    short as possible.
+//
+//    DoubleToBuffer() and FloatToBuffer() write the text to the given
+//    buffer and return it.  The buffer must be at least
+//    kDoubleToBufferSize bytes for doubles and kFloatToBufferSize
+//    bytes for floats.  kFastToBufferSize is also guaranteed to be large
+//    enough to hold either.
+//
+//    Return value: string
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT std::string SimpleDtoa(double value);
+PROTOBUF_EXPORT std::string SimpleFtoa(float value);
+
+PROTOBUF_EXPORT char* DoubleToBuffer(double i, char* buffer);
+PROTOBUF_EXPORT char* FloatToBuffer(float i, char* buffer);
+
+// In practice, doubles should never need more than 24 bytes and floats
+// should never need more than 14 (including null terminators), but we
+// overestimate to be safe.
+static const int kDoubleToBufferSize = 32;
+static const int kFloatToBufferSize = 24;
+
+namespace strings {
+
+enum PadSpec {
+  NO_PAD = 1,
+  ZERO_PAD_2,
+  ZERO_PAD_3,
+  ZERO_PAD_4,
+  ZERO_PAD_5,
+  ZERO_PAD_6,
+  ZERO_PAD_7,
+  ZERO_PAD_8,
+  ZERO_PAD_9,
+  ZERO_PAD_10,
+  ZERO_PAD_11,
+  ZERO_PAD_12,
+  ZERO_PAD_13,
+  ZERO_PAD_14,
+  ZERO_PAD_15,
+  ZERO_PAD_16,
+};
+
+struct Hex {
+  uint64_t value;
+  enum PadSpec spec;
+  template <class Int>
+  explicit Hex(Int v, PadSpec s = NO_PAD)
+      : spec(s) {
+    // Prevent sign-extension by casting integers to
+    // their unsigned counterparts.
+#ifdef LANG_CXX11
+    static_assert(
+        sizeof(v) == 1 || sizeof(v) == 2 || sizeof(v) == 4 || sizeof(v) == 8,
+        "Unknown integer type");
+#endif
+    value = sizeof(v) == 1 ? static_cast<uint8_t>(v)
+          : sizeof(v) == 2 ? static_cast<uint16_t>(v)
+          : sizeof(v) == 4 ? static_cast<uint32_t>(v)
+          : static_cast<uint64_t>(v);
+  }
+};
+
+struct PROTOBUF_EXPORT AlphaNum {
+  const char *piece_data_;  // move these to string_ref eventually
+  size_t piece_size_;       // move these to string_ref eventually
+
+  char digits[kFastToBufferSize];
+
+  // No bool ctor -- bools convert to an integral type.
+  // A bool ctor would also convert incoming pointers (bletch).
+
+  AlphaNum(int i32)
+      : piece_data_(digits),
+        piece_size_(FastInt32ToBufferLeft(i32, digits) - &digits[0]) {}
+  AlphaNum(unsigned int u32)
+      : piece_data_(digits),
+        piece_size_(FastUInt32ToBufferLeft(u32, digits) - &digits[0]) {}
+  AlphaNum(long long i64)
+      : piece_data_(digits),
+        piece_size_(FastInt64ToBufferLeft(i64, digits) - &digits[0]) {}
+  AlphaNum(unsigned long long u64)
+      : piece_data_(digits),
+        piece_size_(FastUInt64ToBufferLeft(u64, digits) - &digits[0]) {}
+
+  // Note: on some architectures, "long" is only 32 bits, not 64, but the
+  // performance hit of using FastInt64ToBufferLeft to handle 32-bit values
+  // is quite minor.
+  AlphaNum(long i64)
+      : piece_data_(digits),
+        piece_size_(FastInt64ToBufferLeft(i64, digits) - &digits[0]) {}
+  AlphaNum(unsigned long u64)
+      : piece_data_(digits),
+        piece_size_(FastUInt64ToBufferLeft(u64, digits) - &digits[0]) {}
+
+  AlphaNum(float f)
+    : piece_data_(digits), piece_size_(strlen(FloatToBuffer(f, digits))) {}
+  AlphaNum(double f)
+    : piece_data_(digits), piece_size_(strlen(DoubleToBuffer(f, digits))) {}
+
+  AlphaNum(Hex hex);
+
+  AlphaNum(const char* c_str)
+      : piece_data_(c_str), piece_size_(strlen(c_str)) {}
+  // TODO: Add a string_ref constructor, eventually
+  // AlphaNum(const StringPiece &pc) : piece(pc) {}
+
+  AlphaNum(const std::string& str)
+      : piece_data_(str.data()), piece_size_(str.size()) {}
+
+  AlphaNum(StringPiece str)
+      : piece_data_(str.data()), piece_size_(str.size()) {}
+
+  size_t size() const { return piece_size_; }
+  const char *data() const { return piece_data_; }
+
+ private:
+  // Use ":" not ':'
+  AlphaNum(char c);  // NOLINT(runtime/explicit)
+
+  // Disallow copy and assign.
+  AlphaNum(const AlphaNum&);
+  void operator=(const AlphaNum&);
+};
+
+}  // namespace strings
+
+using strings::AlphaNum;
+
+// ----------------------------------------------------------------------
+// StrCat()
+//    This merges the given strings or numbers, with no delimiter.  This
+//    is designed to be the fastest possible way to construct a string out
+//    of a mix of raw C strings, strings, bool values,
+//    and numeric values.
+//
+//    Don't use this for user-visible strings.  The localization process
+//    works poorly on strings built up out of fragments.
+//
+//    For clarity and performance, don't use StrCat when appending to a
+//    string.  In particular, avoid using any of these (anti-)patterns:
+//      str.append(StrCat(...)
+//      str += StrCat(...)
+//      str = StrCat(str, ...)
+//    where the last is the worse, with the potential to change a loop
+//    from a linear time operation with O(1) dynamic allocations into a
+//    quadratic time operation with O(n) dynamic allocations.  StrAppend
+//    is a better choice than any of the above, subject to the restriction
+//    of StrAppend(&str, a, b, c, ...) that none of the a, b, c, ... may
+//    be a reference into str.
+// ----------------------------------------------------------------------
+
+PROTOBUF_EXPORT std::string StrCat(const AlphaNum& a, const AlphaNum& b);
+PROTOBUF_EXPORT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
+                                   const AlphaNum& c);
+PROTOBUF_EXPORT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
+                                   const AlphaNum& c, const AlphaNum& d);
+PROTOBUF_EXPORT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
+                                   const AlphaNum& c, const AlphaNum& d,
+                                   const AlphaNum& e);
+PROTOBUF_EXPORT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
+                                   const AlphaNum& c, const AlphaNum& d,
+                                   const AlphaNum& e, const AlphaNum& f);
+PROTOBUF_EXPORT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
+                                   const AlphaNum& c, const AlphaNum& d,
+                                   const AlphaNum& e, const AlphaNum& f,
+                                   const AlphaNum& g);
+PROTOBUF_EXPORT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
+                                   const AlphaNum& c, const AlphaNum& d,
+                                   const AlphaNum& e, const AlphaNum& f,
+                                   const AlphaNum& g, const AlphaNum& h);
+PROTOBUF_EXPORT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
+                                   const AlphaNum& c, const AlphaNum& d,
+                                   const AlphaNum& e, const AlphaNum& f,
+                                   const AlphaNum& g, const AlphaNum& h,
+                                   const AlphaNum& i);
+
+inline std::string StrCat(const AlphaNum& a) {
+  return std::string(a.data(), a.size());
+}
+
+// ----------------------------------------------------------------------
+// StrAppend()
+//    Same as above, but adds the output to the given string.
+//    WARNING: For speed, StrAppend does not try to check each of its input
+//    arguments to be sure that they are not a subset of the string being
+//    appended to.  That is, while this will work:
+//
+//    string s = "foo";
+//    s += s;
+//
+//    This will not (necessarily) work:
+//
+//    string s = "foo";
+//    StrAppend(&s, s);
+//
+//    Note: while StrCat supports appending up to 9 arguments, StrAppend
+//    is currently limited to 4.  That's rarely an issue except when
+//    automatically transforming StrCat to StrAppend, and can easily be
+//    worked around as consecutive calls to StrAppend are quite efficient.
+// ----------------------------------------------------------------------
+
+PROTOBUF_EXPORT void StrAppend(std::string* dest, const AlphaNum& a);
+PROTOBUF_EXPORT void StrAppend(std::string* dest, const AlphaNum& a,
+                               const AlphaNum& b);
+PROTOBUF_EXPORT void StrAppend(std::string* dest, const AlphaNum& a,
+                               const AlphaNum& b, const AlphaNum& c);
+PROTOBUF_EXPORT void StrAppend(std::string* dest, const AlphaNum& a,
+                               const AlphaNum& b, const AlphaNum& c,
+                               const AlphaNum& d);
+
+// ----------------------------------------------------------------------
+// Join()
+//    These methods concatenate a range of components into a C++ string, using
+//    the C-string "delim" as a separator between components.
+// ----------------------------------------------------------------------
+template <typename Iterator>
+void Join(Iterator start, Iterator end, const char* delim,
+          std::string* result) {
+  for (Iterator it = start; it != end; ++it) {
+    if (it != start) {
+      result->append(delim);
+    }
+    StrAppend(result, *it);
+  }
+}
+
+template <typename Range>
+std::string Join(const Range& components, const char* delim) {
+  std::string result;
+  Join(components.begin(), components.end(), delim, &result);
+  return result;
+}
+
+// ----------------------------------------------------------------------
+// ToHex()
+//    Return a lower-case hex string representation of the given integer.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT std::string ToHex(uint64_t num);
+
+// ----------------------------------------------------------------------
+// GlobalReplaceSubstring()
+//    Replaces all instances of a substring in a string.  Does nothing
+//    if 'substring' is empty.  Returns the number of replacements.
+//
+//    NOTE: The string pieces must not overlap s.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT int GlobalReplaceSubstring(const std::string& substring,
+                                           const std::string& replacement,
+                                           std::string* s);
+
+// ----------------------------------------------------------------------
+// Base64Unescape()
+//    Converts "src" which is encoded in Base64 to its binary equivalent and
+//    writes it to "dest". If src contains invalid characters, dest is cleared
+//    and the function returns false. Returns true on success.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT bool Base64Unescape(StringPiece src, std::string* dest);
+
+// ----------------------------------------------------------------------
+// WebSafeBase64Unescape()
+//    This is a variation of Base64Unescape which uses '-' instead of '+', and
+//    '_' instead of '/'. src is not null terminated, instead specify len. I
+//    recommend that slen<szdest, but we honor szdest anyway.
+//    RETURNS the length of dest, or -1 if src contains invalid chars.
+
+//    The variation that stores into a string clears the string first, and
+//    returns false (with dest empty) if src contains invalid chars; for
+//    this version src and dest must be different strings.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT int WebSafeBase64Unescape(const char* src, int slen, char* dest,
+                                          int szdest);
+PROTOBUF_EXPORT bool WebSafeBase64Unescape(StringPiece src, std::string* dest);
+
+// Return the length to use for the output buffer given to the base64 escape
+// routines. Make sure to use the same value for do_padding in both.
+// This function may return incorrect results if given input_len values that
+// are extremely high, which should happen rarely.
+PROTOBUF_EXPORT int CalculateBase64EscapedLen(int input_len, bool do_padding);
+// Use this version when calling Base64Escape without a do_padding arg.
+PROTOBUF_EXPORT int CalculateBase64EscapedLen(int input_len);
+
+// ----------------------------------------------------------------------
+// Base64Escape()
+// WebSafeBase64Escape()
+//    Encode "src" to "dest" using base64 encoding.
+//    src is not null terminated, instead specify len.
+//    'dest' should have at least CalculateBase64EscapedLen() length.
+//    RETURNS the length of dest.
+//    The WebSafe variation use '-' instead of '+' and '_' instead of '/'
+//    so that we can place the out in the URL or cookies without having
+//    to escape them.  It also has an extra parameter "do_padding",
+//    which when set to false will prevent padding with "=".
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT int Base64Escape(const unsigned char* src, int slen, char* dest,
+                                 int szdest);
+PROTOBUF_EXPORT int WebSafeBase64Escape(const unsigned char* src, int slen,
+                                        char* dest, int szdest,
+                                        bool do_padding);
+// Encode src into dest with padding.
+PROTOBUF_EXPORT void Base64Escape(StringPiece src, std::string* dest);
+// Encode src into dest web-safely without padding.
+PROTOBUF_EXPORT void WebSafeBase64Escape(StringPiece src, std::string* dest);
+// Encode src into dest web-safely with padding.
+PROTOBUF_EXPORT void WebSafeBase64EscapeWithPadding(StringPiece src,
+                                                    std::string* dest);
+
+PROTOBUF_EXPORT void Base64Escape(const unsigned char* src, int szsrc,
+                                  std::string* dest, bool do_padding);
+PROTOBUF_EXPORT void WebSafeBase64Escape(const unsigned char* src, int szsrc,
+                                         std::string* dest, bool do_padding);
+
+inline bool IsValidCodePoint(uint32_t code_point) {
+  return code_point < 0xD800 ||
+         (code_point >= 0xE000 && code_point <= 0x10FFFF);
+}
+
+static const int UTFmax = 4;
+// ----------------------------------------------------------------------
+// EncodeAsUTF8Char()
+//  Helper to append a Unicode code point to a string as UTF8, without bringing
+//  in any external dependencies. The output buffer must be as least 4 bytes
+//  large.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT int EncodeAsUTF8Char(uint32_t code_point, char* output);
+
+// ----------------------------------------------------------------------
+// UTF8FirstLetterNumBytes()
+//   Length of the first UTF-8 character.
+// ----------------------------------------------------------------------
+PROTOBUF_EXPORT int UTF8FirstLetterNumBytes(const char* src, int len);
+
+// From google3/third_party/absl/strings/escaping.h
+
+// ----------------------------------------------------------------------
+// CleanStringLineEndings()
+//   Clean up a multi-line string to conform to Unix line endings.
+//   Reads from src and appends to dst, so usually dst should be empty.
+//
+//   If there is no line ending at the end of a non-empty string, it can
+//   be added automatically.
+//
+//   Four different types of input are correctly handled:
+//
+//     - Unix/Linux files: line ending is LF: pass through unchanged
+//
+//     - DOS/Windows files: line ending is CRLF: convert to LF
+//
+//     - Legacy Mac files: line ending is CR: convert to LF
+//
+//     - Garbled files: random line endings: convert gracefully
+//                      lonely CR, lonely LF, CRLF: convert to LF
+//
+//   @param src The multi-line string to convert
+//   @param dst The converted string is appended to this string
+//   @param auto_end_last_line Automatically terminate the last line
+//
+//   Limitations:
+//
+//     This does not do the right thing for CRCRLF files created by
+//     broken programs that do another Unix->DOS conversion on files
+//     that are already in CRLF format.  For this, a two-pass approach
+//     brute-force would be needed that
+//
+//       (1) determines the presence of LF (first one is ok)
+//       (2) if yes, removes any CR, else convert every CR to LF
+PROTOBUF_EXPORT void CleanStringLineEndings(const std::string& src,
+                                            std::string* dst,
+                                            bool auto_end_last_line);
+
+// Same as above, but transforms the argument in place.
+PROTOBUF_EXPORT void CleanStringLineEndings(std::string* str,
+                                            bool auto_end_last_line);
+
+namespace strings {
+inline bool EndsWith(StringPiece text, StringPiece suffix) {
+  return suffix.empty() ||
+      (text.size() >= suffix.size() &&
+       memcmp(text.data() + (text.size() - suffix.size()), suffix.data(),
+              suffix.size()) == 0);
+}
+}  // namespace strings
+
+namespace internal {
+
+// A locale-independent version of the standard strtod(), which always
+// uses a dot as the decimal separator.
+double NoLocaleStrtod(const char* str, char** endptr);
+
+}  // namespace internal
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_STRUTIL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/substitute.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/substitute.h
new file mode 100644
index 0000000..0f851de
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/substitute.h
@@ -0,0 +1,178 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+// from google3/strings/substitute.h
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/strutil.h>
+
+#include <string>
+
+#ifndef GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_
+#define GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace strings {
+
+// ----------------------------------------------------------------------
+// strings::Substitute()
+// strings::SubstituteAndAppend()
+//   Kind of like StringPrintf, but different.
+//
+//   Example:
+//     string GetMessage(string first_name, string last_name, int age) {
+//       return strings::Substitute("My name is $0 $1 and I am $2 years old.",
+//                                  first_name, last_name, age);
+//     }
+//
+//   Differences from StringPrintf:
+//   * The format string does not identify the types of arguments.
+//     Instead, the magic of C++ deals with this for us.  See below
+//     for a list of accepted types.
+//   * Substitutions in the format string are identified by a '$'
+//     followed by a digit.  So, you can use arguments out-of-order and
+//     use the same argument multiple times.
+//   * It's much faster than StringPrintf.
+//
+//   Supported types:
+//   * Strings (const char*, const string&)
+//     * Note that this means you do not have to add .c_str() to all of
+//       your strings.  In fact, you shouldn't; it will be slower.
+//   * int32, int64, uint32, uint64:  Formatted using SimpleItoa().
+//   * float, double:  Formatted using SimpleFtoa() and SimpleDtoa().
+//   * bool:  Printed as "true" or "false".
+//
+//   SubstituteAndAppend() is like Substitute() but appends the result to
+//   *output.  Example:
+//
+//     string str;
+//     strings::SubstituteAndAppend(&str,
+//                                  "My name is $0 $1 and I am $2 years old.",
+//                                  first_name, last_name, age);
+//
+//   Substitute() is significantly faster than StringPrintf().  For very
+//   large strings, it may be orders of magnitude faster.
+// ----------------------------------------------------------------------
+
+namespace internal {  // Implementation details.
+
+class SubstituteArg {
+ public:
+  inline SubstituteArg(const char* value)
+    : text_(value), size_(strlen(text_)) {}
+  inline SubstituteArg(const std::string& value)
+      : text_(value.data()), size_(value.size()) {}
+  inline SubstituteArg(const StringPiece value)
+      : text_(value.data()), size_(value.size()) {}
+
+  // Indicates that no argument was given.
+  inline explicit SubstituteArg()
+    : text_(nullptr), size_(-1) {}
+
+  // Primitives
+  // We don't overload for signed and unsigned char because if people are
+  // explicitly declaring their chars as signed or unsigned then they are
+  // probably actually using them as 8-bit integers and would probably
+  // prefer an integer representation.  But, we don't really know.  So, we
+  // make the caller decide what to do.
+  inline SubstituteArg(char value)
+    : text_(scratch_), size_(1) { scratch_[0] = value; }
+  inline SubstituteArg(short value)
+    : text_(FastInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {}
+  inline SubstituteArg(unsigned short value)
+    : text_(FastUInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {}
+  inline SubstituteArg(int value)
+    : text_(FastInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {}
+  inline SubstituteArg(unsigned int value)
+    : text_(FastUInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {}
+  inline SubstituteArg(long value)
+    : text_(FastLongToBuffer(value, scratch_)), size_(strlen(text_)) {}
+  inline SubstituteArg(unsigned long value)
+    : text_(FastULongToBuffer(value, scratch_)), size_(strlen(text_)) {}
+  inline SubstituteArg(long long value)
+    : text_(FastInt64ToBuffer(value, scratch_)), size_(strlen(text_)) {}
+  inline SubstituteArg(unsigned long long value)
+    : text_(FastUInt64ToBuffer(value, scratch_)), size_(strlen(text_)) {}
+  inline SubstituteArg(float value)
+    : text_(FloatToBuffer(value, scratch_)), size_(strlen(text_)) {}
+  inline SubstituteArg(double value)
+    : text_(DoubleToBuffer(value, scratch_)), size_(strlen(text_)) {}
+  inline SubstituteArg(bool value)
+    : text_(value ? "true" : "false"), size_(strlen(text_)) {}
+
+  inline const char* data() const { return text_; }
+  inline int size() const { return size_; }
+
+ private:
+  const char* text_;
+  int size_;
+  char scratch_[kFastToBufferSize];
+};
+
+}  // namespace internal
+
+PROTOBUF_EXPORT std::string Substitute(
+    const std::string& format,
+    const internal::SubstituteArg& arg0 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg1 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg2 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg3 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg4 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg5 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg6 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg7 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg8 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg9 = internal::SubstituteArg());
+
+PROTOBUF_EXPORT void SubstituteAndAppend(
+    std::string* output, const char* format,
+    const internal::SubstituteArg& arg0 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg1 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg2 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg3 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg4 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg5 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg6 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg7 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg8 = internal::SubstituteArg(),
+    const internal::SubstituteArg& arg9 = internal::SubstituteArg());
+
+}  // namespace strings
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif // GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/template_util.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/template_util.h
new file mode 100644
index 0000000..feef904
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/template_util.h
@@ -0,0 +1,138 @@
+// Copyright 2005 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// ----
+// Author: lar@google.com (Laramie Leavitt)
+//
+// Template metaprogramming utility functions.
+//
+// This code is compiled directly on many platforms, including client
+// platforms like Windows, Mac, and embedded systems.  Before making
+// any changes here, make sure that you're not breaking any platforms.
+//
+//
+// The names chosen here reflect those used in tr1 and the boost::mpl
+// library, there are similar operations used in the Loki library as
+// well.  I prefer the boost names for 2 reasons:
+// 1.  I think that portions of the Boost libraries are more likely to
+// be included in the c++ standard.
+// 2.  It is not impossible that some of the boost libraries will be
+// included in our own build in the future.
+// Both of these outcomes means that we may be able to directly replace
+// some of these with boost equivalents.
+//
+#ifndef GOOGLE_PROTOBUF_TEMPLATE_UTIL_H_
+#define GOOGLE_PROTOBUF_TEMPLATE_UTIL_H_
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Types small_ and big_ are guaranteed such that sizeof(small_) <
+// sizeof(big_)
+typedef char small_;
+
+struct big_ {
+  char dummy[2];
+};
+
+// Identity metafunction.
+template <class T>
+struct identity_ {
+  typedef T type;
+};
+
+// integral_constant, defined in tr1, is a wrapper for an integer
+// value. We don't really need this generality; we could get away
+// with hardcoding the integer type to bool. We use the fully
+// general integer_constant for compatibility with tr1.
+
+template<class T, T v>
+struct integral_constant {
+  static const T value = v;
+  typedef T value_type;
+  typedef integral_constant<T, v> type;
+};
+
+template <class T, T v> const T integral_constant<T, v>::value;
+
+
+// Abbreviations: true_type and false_type are structs that represent boolean
+// true and false values. Also define the boost::mpl versions of those names,
+// true_ and false_.
+typedef integral_constant<bool, true>  true_type;
+typedef integral_constant<bool, false> false_type;
+typedef true_type  true_;
+typedef false_type false_;
+
+// if_ is a templatized conditional statement.
+// if_<cond, A, B> is a compile time evaluation of cond.
+// if_<>::type contains A if cond is true, B otherwise.
+template<bool cond, typename A, typename B>
+struct if_{
+  typedef A type;
+};
+
+template<typename A, typename B>
+struct if_<false, A, B> {
+  typedef B type;
+};
+
+
+// type_equals_ is a template type comparator, similar to Loki IsSameType.
+// type_equals_<A, B>::value is true iff "A" is the same type as "B".
+//
+// New code should prefer base::is_same, defined in base/type_traits.h.
+// It is functionally identical, but is_same is the standard spelling.
+template<typename A, typename B>
+struct type_equals_ : public false_ {
+};
+
+template<typename A>
+struct type_equals_<A, A> : public true_ {
+};
+
+// and_ is a template && operator.
+// and_<A, B>::value evaluates "A::value && B::value".
+template<typename A, typename B>
+struct and_ : public integral_constant<bool, (A::value && B::value)> {
+};
+
+// or_ is a template || operator.
+// or_<A, B>::value evaluates "A::value || B::value".
+template<typename A, typename B>
+struct or_ : public integral_constant<bool, (A::value || B::value)> {
+};
+
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_TEMPLATE_UTIL_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/time.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/time.h
new file mode 100644
index 0000000..8b6e562
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/stubs/time.h
@@ -0,0 +1,82 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+#ifndef GOOGLE_PROTOBUF_STUBS_TIME_H_
+#define GOOGLE_PROTOBUF_STUBS_TIME_H_
+
+#include <cstdint>
+
+#include <google/protobuf/stubs/common.h>
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+struct DateTime {
+  int year;
+  int month;
+  int day;
+  int hour;
+  int minute;
+  int second;
+};
+
+// Converts a timestamp (seconds elapsed since 1970-01-01T00:00:00, could be
+// negative to represent time before 1970-01-01) to DateTime. Returns false
+// if the timestamp is not in the range between 0001-01-01T00:00:00 and
+// 9999-12-31T23:59:59.
+bool PROTOBUF_EXPORT SecondsToDateTime(int64_t seconds, DateTime* time);
+// Converts DateTime to a timestamp (seconds since 1970-01-01T00:00:00).
+// Returns false if the DateTime is not valid or is not in the valid range.
+bool PROTOBUF_EXPORT DateTimeToSeconds(const DateTime& time, int64_t* seconds);
+
+void PROTOBUF_EXPORT GetCurrentTime(int64_t* seconds, int32_t* nanos);
+
+// Formats a time string in RFC3339 format.
+//
+// For example, "2015-05-20T13:29:35.120Z". For nanos, 0, 3, 6 or 9 fractional
+// digits will be used depending on how many are required to represent the exact
+// value.
+//
+// Note that "nanos" must in the range of [0, 999999999].
+std::string PROTOBUF_EXPORT FormatTime(int64_t seconds, int32_t nanos);
+// Parses a time string. This method accepts RFC3339 date/time string with UTC
+// offset. For example, "2015-05-20T13:29:35.120-08:00".
+bool PROTOBUF_EXPORT ParseTime(const std::string& value, int64_t* seconds,
+                               int32_t* nanos);
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_STUBS_TIME_H_
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/text_format.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/text_format.h
new file mode 100644
index 0000000..e10bef7
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/text_format.h
@@ -0,0 +1,693 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: jschorr@google.com (Joseph Schorr)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Utilities for printing and parsing protocol messages in a human-readable,
+// text-based format.
+
+#ifndef GOOGLE_PROTOBUF_TEXT_FORMAT_H__
+#define GOOGLE_PROTOBUF_TEXT_FORMAT_H__
+
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/message_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+
+namespace internal {
+PROTOBUF_EXPORT extern const char kDebugStringSilentMarker[1];
+PROTOBUF_EXPORT extern const char kDebugStringSilentMarkerForDetection[3];
+}  // namespace internal
+
+namespace io {
+class ErrorCollector;  // tokenizer.h
+}
+
+// This class implements protocol buffer text format, colloquially known as text
+// proto.  Printing and parsing protocol messages in text format is useful for
+// debugging and human editing of messages.
+//
+// This class is really a namespace that contains only static methods.
+class PROTOBUF_EXPORT TextFormat {
+ public:
+  // Outputs a textual representation of the given message to the given
+  // output stream. Returns false if printing fails.
+  static bool Print(const Message& message, io::ZeroCopyOutputStream* output);
+
+  // Print the fields in an UnknownFieldSet.  They are printed by tag number
+  // only.  Embedded messages are heuristically identified by attempting to
+  // parse them. Returns false if printing fails.
+  static bool PrintUnknownFields(const UnknownFieldSet& unknown_fields,
+                                 io::ZeroCopyOutputStream* output);
+
+  // Like Print(), but outputs directly to a string.
+  // Note: output will be cleared prior to printing, and will be left empty
+  // even if printing fails. Returns false if printing fails.
+  static bool PrintToString(const Message& message, std::string* output);
+
+  // Like PrintUnknownFields(), but outputs directly to a string. Returns
+  // false if printing fails.
+  static bool PrintUnknownFieldsToString(const UnknownFieldSet& unknown_fields,
+                                         std::string* output);
+
+  // Outputs a textual representation of the value of the field supplied on
+  // the message supplied. For non-repeated fields, an index of -1 must
+  // be supplied. Note that this method will print the default value for a
+  // field if it is not set.
+  static void PrintFieldValueToString(const Message& message,
+                                      const FieldDescriptor* field, int index,
+                                      std::string* output);
+
+  class PROTOBUF_EXPORT BaseTextGenerator {
+   public:
+    virtual ~BaseTextGenerator();
+
+    virtual void Indent() {}
+    virtual void Outdent() {}
+    // Returns the current indentation size in characters.
+    virtual size_t GetCurrentIndentationSize() const { return 0; }
+
+    // Print text to the output stream.
+    virtual void Print(const char* text, size_t size) = 0;
+
+    void PrintString(const std::string& str) { Print(str.data(), str.size()); }
+
+    template <size_t n>
+    void PrintLiteral(const char (&text)[n]) {
+      Print(text, n - 1);  // n includes the terminating zero character.
+    }
+  };
+
+  // The default printer that converts scalar values from fields into their
+  // string representation.
+  // You can derive from this FastFieldValuePrinter if you want to have fields
+  // to be printed in a different way and register it at the Printer.
+  class PROTOBUF_EXPORT FastFieldValuePrinter {
+   public:
+    FastFieldValuePrinter();
+    virtual ~FastFieldValuePrinter();
+    virtual void PrintBool(bool val, BaseTextGenerator* generator) const;
+    virtual void PrintInt32(int32_t val, BaseTextGenerator* generator) const;
+    virtual void PrintUInt32(uint32_t val, BaseTextGenerator* generator) const;
+    virtual void PrintInt64(int64_t val, BaseTextGenerator* generator) const;
+    virtual void PrintUInt64(uint64_t val, BaseTextGenerator* generator) const;
+    virtual void PrintFloat(float val, BaseTextGenerator* generator) const;
+    virtual void PrintDouble(double val, BaseTextGenerator* generator) const;
+    virtual void PrintString(const std::string& val,
+                             BaseTextGenerator* generator) const;
+    virtual void PrintBytes(const std::string& val,
+                            BaseTextGenerator* generator) const;
+    virtual void PrintEnum(int32_t val, const std::string& name,
+                           BaseTextGenerator* generator) const;
+    virtual void PrintFieldName(const Message& message, int field_index,
+                                int field_count, const Reflection* reflection,
+                                const FieldDescriptor* field,
+                                BaseTextGenerator* generator) const;
+    virtual void PrintFieldName(const Message& message,
+                                const Reflection* reflection,
+                                const FieldDescriptor* field,
+                                BaseTextGenerator* generator) const;
+    virtual void PrintMessageStart(const Message& message, int field_index,
+                                   int field_count, bool single_line_mode,
+                                   BaseTextGenerator* generator) const;
+    // Allows to override the logic on how to print the content of a message.
+    // Return false to use the default printing logic. Note that it is legal for
+    // this function to print something and then return false to use the default
+    // content printing (although at that point it would behave similarly to
+    // PrintMessageStart).
+    virtual bool PrintMessageContent(const Message& message, int field_index,
+                                     int field_count, bool single_line_mode,
+                                     BaseTextGenerator* generator) const;
+    virtual void PrintMessageEnd(const Message& message, int field_index,
+                                 int field_count, bool single_line_mode,
+                                 BaseTextGenerator* generator) const;
+
+   private:
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FastFieldValuePrinter);
+  };
+
+  // Deprecated: please use FastFieldValuePrinter instead.
+  class PROTOBUF_EXPORT FieldValuePrinter {
+   public:
+    FieldValuePrinter();
+    virtual ~FieldValuePrinter();
+    virtual std::string PrintBool(bool val) const;
+    virtual std::string PrintInt32(int32_t val) const;
+    virtual std::string PrintUInt32(uint32_t val) const;
+    virtual std::string PrintInt64(int64_t val) const;
+    virtual std::string PrintUInt64(uint64_t val) const;
+    virtual std::string PrintFloat(float val) const;
+    virtual std::string PrintDouble(double val) const;
+    virtual std::string PrintString(const std::string& val) const;
+    virtual std::string PrintBytes(const std::string& val) const;
+    virtual std::string PrintEnum(int32_t val, const std::string& name) const;
+    virtual std::string PrintFieldName(const Message& message,
+                                       const Reflection* reflection,
+                                       const FieldDescriptor* field) const;
+    virtual std::string PrintMessageStart(const Message& message,
+                                          int field_index, int field_count,
+                                          bool single_line_mode) const;
+    virtual std::string PrintMessageEnd(const Message& message, int field_index,
+                                        int field_count,
+                                        bool single_line_mode) const;
+
+   private:
+    FastFieldValuePrinter delegate_;
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldValuePrinter);
+  };
+
+  class PROTOBUF_EXPORT MessagePrinter {
+   public:
+    MessagePrinter() {}
+    virtual ~MessagePrinter() {}
+    virtual void Print(const Message& message, bool single_line_mode,
+                       BaseTextGenerator* generator) const = 0;
+
+   private:
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessagePrinter);
+  };
+
+  // Interface that Printers or Parsers can use to find extensions, or types
+  // referenced in Any messages.
+  class PROTOBUF_EXPORT Finder {
+   public:
+    virtual ~Finder();
+
+    // Try to find an extension of *message by fully-qualified field
+    // name.  Returns nullptr if no extension is known for this name or number.
+    // The base implementation uses the extensions already known by the message.
+    virtual const FieldDescriptor* FindExtension(Message* message,
+                                                 const std::string& name) const;
+
+    // Similar to FindExtension, but uses a Descriptor and the extension number
+    // instead of using a Message and the name when doing the look up.
+    virtual const FieldDescriptor* FindExtensionByNumber(
+        const Descriptor* descriptor, int number) const;
+
+    // Find the message type for an Any proto.
+    // Returns nullptr if no message is known for this name.
+    // The base implementation only accepts prefixes of type.googleprod.com/ or
+    // type.googleapis.com/, and searches the DescriptorPool of the parent
+    // message.
+    virtual const Descriptor* FindAnyType(const Message& message,
+                                          const std::string& prefix,
+                                          const std::string& name) const;
+
+    // Find the message factory for the given extension field. This can be used
+    // to generalize the Parser to add extension fields to a message in the same
+    // way as the "input" message for the Parser.
+    virtual MessageFactory* FindExtensionFactory(
+        const FieldDescriptor* field) const;
+  };
+
+  // Class for those users which require more fine-grained control over how
+  // a protobuffer message is printed out.
+  class PROTOBUF_EXPORT Printer {
+   public:
+    Printer();
+
+    // Like TextFormat::Print
+    bool Print(const Message& message, io::ZeroCopyOutputStream* output) const;
+    // Like TextFormat::PrintUnknownFields
+    bool PrintUnknownFields(const UnknownFieldSet& unknown_fields,
+                            io::ZeroCopyOutputStream* output) const;
+    // Like TextFormat::PrintToString
+    bool PrintToString(const Message& message, std::string* output) const;
+    // Like TextFormat::PrintUnknownFieldsToString
+    bool PrintUnknownFieldsToString(const UnknownFieldSet& unknown_fields,
+                                    std::string* output) const;
+    // Like TextFormat::PrintFieldValueToString
+    void PrintFieldValueToString(const Message& message,
+                                 const FieldDescriptor* field, int index,
+                                 std::string* output) const;
+
+    // Adjust the initial indent level of all output.  Each indent level is
+    // equal to two spaces.
+    void SetInitialIndentLevel(int indent_level) {
+      initial_indent_level_ = indent_level;
+    }
+
+    // If printing in single line mode, then the entire message will be output
+    // on a single line with no line breaks.
+    void SetSingleLineMode(bool single_line_mode) {
+      single_line_mode_ = single_line_mode;
+    }
+
+    bool IsInSingleLineMode() const { return single_line_mode_; }
+
+    // If use_field_number is true, uses field number instead of field name.
+    void SetUseFieldNumber(bool use_field_number) {
+      use_field_number_ = use_field_number;
+    }
+
+    // Set true to print repeated primitives in a format like:
+    //   field_name: [1, 2, 3, 4]
+    // instead of printing each value on its own line.  Short format applies
+    // only to primitive values -- i.e. everything except strings and
+    // sub-messages/groups.
+    void SetUseShortRepeatedPrimitives(bool use_short_repeated_primitives) {
+      use_short_repeated_primitives_ = use_short_repeated_primitives;
+    }
+
+    // Set true to output UTF-8 instead of ASCII.  The only difference
+    // is that bytes >= 0x80 in string fields will not be escaped,
+    // because they are assumed to be part of UTF-8 multi-byte
+    // sequences. This will change the default FastFieldValuePrinter.
+    void SetUseUtf8StringEscaping(bool as_utf8);
+
+    // Set the default FastFieldValuePrinter that is used for all fields that
+    // don't have a field-specific printer registered.
+    // Takes ownership of the printer.
+    void SetDefaultFieldValuePrinter(const FastFieldValuePrinter* printer);
+
+    PROTOBUF_DEPRECATED_MSG("Please use FastFieldValuePrinter")
+    void SetDefaultFieldValuePrinter(const FieldValuePrinter* printer);
+
+    // Sets whether we want to hide unknown fields or not.
+    // Usually unknown fields are printed in a generic way that includes the
+    // tag number of the field instead of field name. However, sometimes it
+    // is useful to be able to print the message without unknown fields (e.g.
+    // for the python protobuf version to maintain consistency between its pure
+    // python and c++ implementations).
+    void SetHideUnknownFields(bool hide) { hide_unknown_fields_ = hide; }
+
+    // If print_message_fields_in_index_order is true, fields of a proto message
+    // will be printed using the order defined in source code instead of the
+    // field number, extensions will be printed at the end of the message
+    // and their relative order is determined by the extension number.
+    // By default, use the field number order.
+    void SetPrintMessageFieldsInIndexOrder(
+        bool print_message_fields_in_index_order) {
+      print_message_fields_in_index_order_ =
+          print_message_fields_in_index_order;
+    }
+
+    // If expand==true, expand google.protobuf.Any payloads. The output
+    // will be of form
+    //    [type_url] { <value_printed_in_text> }
+    //
+    // If expand==false, print Any using the default printer. The output will
+    // look like
+    //    type_url: "<type_url>"  value: "serialized_content"
+    void SetExpandAny(bool expand) { expand_any_ = expand; }
+
+    // Set how parser finds message for Any payloads.
+    void SetFinder(const Finder* finder) { finder_ = finder; }
+
+    // If non-zero, we truncate all string fields that are  longer than
+    // this threshold.  This is useful when the proto message has very long
+    // strings, e.g., dump of encoded image file.
+    //
+    // NOTE(hfgong):  Setting a non-zero value breaks round-trip safe
+    // property of TextFormat::Printer.  That is, from the printed message, we
+    // cannot fully recover the original string field any more.
+    void SetTruncateStringFieldLongerThan(
+        const int64_t truncate_string_field_longer_than) {
+      truncate_string_field_longer_than_ = truncate_string_field_longer_than;
+    }
+
+    // Register a custom field-specific FastFieldValuePrinter for fields
+    // with a particular FieldDescriptor.
+    // Returns "true" if the registration succeeded, or "false", if there is
+    // already a printer for that FieldDescriptor.
+    // Takes ownership of the printer on successful registration.
+    bool RegisterFieldValuePrinter(const FieldDescriptor* field,
+                                   const FastFieldValuePrinter* printer);
+
+    PROTOBUF_DEPRECATED_MSG("Please use FastFieldValuePrinter")
+    bool RegisterFieldValuePrinter(const FieldDescriptor* field,
+                                   const FieldValuePrinter* printer);
+
+    // Register a custom message-specific MessagePrinter for messages with a
+    // particular Descriptor.
+    // Returns "true" if the registration succeeded, or "false" if there is
+    // already a printer for that Descriptor.
+    bool RegisterMessagePrinter(const Descriptor* descriptor,
+                                const MessagePrinter* printer);
+
+   private:
+    friend std::string Message::DebugString() const;
+    friend std::string Message::ShortDebugString() const;
+    friend std::string Message::Utf8DebugString() const;
+
+    // Sets whether *DebugString should insert a silent marker.
+    void SetInsertSilentMarker(bool v) { insert_silent_marker_ = v; }
+
+    // Forward declaration of an internal class used to print the text
+    // output to the OutputStream (see text_format.cc for implementation).
+    class TextGenerator;
+
+    // Forward declaration of an internal class used to print field values for
+    // DebugString APIs (see text_format.cc for implementation).
+    class DebugStringFieldValuePrinter;
+
+    // Forward declaration of an internal class used to print UTF-8 escaped
+    // strings (see text_format.cc for implementation).
+    class FastFieldValuePrinterUtf8Escaping;
+
+    static const char* const kDoNotParse;
+
+    // Internal Print method, used for writing to the OutputStream via
+    // the TextGenerator class.
+    void Print(const Message& message, TextGenerator* generator) const;
+
+    // Print a single field.
+    void PrintField(const Message& message, const Reflection* reflection,
+                    const FieldDescriptor* field,
+                    TextGenerator* generator) const;
+
+    // Print a repeated primitive field in short form.
+    void PrintShortRepeatedField(const Message& message,
+                                 const Reflection* reflection,
+                                 const FieldDescriptor* field,
+                                 TextGenerator* generator) const;
+
+    // Print the name of a field -- i.e. everything that comes before the
+    // ':' for a single name/value pair.
+    void PrintFieldName(const Message& message, int field_index,
+                        int field_count, const Reflection* reflection,
+                        const FieldDescriptor* field,
+                        TextGenerator* generator) const;
+
+    // Outputs a textual representation of the value of the field supplied on
+    // the message supplied or the default value if not set.
+    void PrintFieldValue(const Message& message, const Reflection* reflection,
+                         const FieldDescriptor* field, int index,
+                         TextGenerator* generator) const;
+
+    // Print the fields in an UnknownFieldSet.  They are printed by tag number
+    // only.  Embedded messages are heuristically identified by attempting to
+    // parse them (subject to the recursion budget).
+    void PrintUnknownFields(const UnknownFieldSet& unknown_fields,
+                            TextGenerator* generator,
+                            int recursion_budget) const;
+
+    bool PrintAny(const Message& message, TextGenerator* generator) const;
+
+    const FastFieldValuePrinter* GetFieldPrinter(
+        const FieldDescriptor* field) const {
+      auto it = custom_printers_.find(field);
+      return it == custom_printers_.end() ? default_field_value_printer_.get()
+                                          : it->second.get();
+    }
+
+    int initial_indent_level_;
+    bool single_line_mode_;
+    bool use_field_number_;
+    bool use_short_repeated_primitives_;
+    bool insert_silent_marker_;
+    bool hide_unknown_fields_;
+    bool print_message_fields_in_index_order_;
+    bool expand_any_;
+    int64_t truncate_string_field_longer_than_;
+
+    std::unique_ptr<const FastFieldValuePrinter> default_field_value_printer_;
+    typedef std::map<const FieldDescriptor*,
+                     std::unique_ptr<const FastFieldValuePrinter>>
+        CustomPrinterMap;
+    CustomPrinterMap custom_printers_;
+
+    typedef std::map<const Descriptor*, std::unique_ptr<const MessagePrinter>>
+        CustomMessagePrinterMap;
+    CustomMessagePrinterMap custom_message_printers_;
+
+    const Finder* finder_;
+  };
+
+  // Parses a text-format protocol message from the given input stream to
+  // the given message object. This function parses the human-readable
+  // serialization format written by Print(). Returns true on success. The
+  // message is cleared first, even if the function fails -- See Merge() to
+  // avoid this behavior.
+  //
+  // Example input: "user {\n id: 123 extra { gender: MALE language: 'en' }\n}"
+  //
+  // One common use for this function is parsing handwritten strings in test
+  // code.
+  //
+  // If you would like to read a protocol buffer serialized in the
+  // (non-human-readable) binary wire format, see
+  // google::protobuf::MessageLite::ParseFromString().
+  static bool Parse(io::ZeroCopyInputStream* input, Message* output);
+  // Like Parse(), but reads directly from a string.
+  static bool ParseFromString(ConstStringParam input, Message* output);
+
+  // Like Parse(), but the data is merged into the given message, as if
+  // using Message::MergeFrom().
+  static bool Merge(io::ZeroCopyInputStream* input, Message* output);
+  // Like Merge(), but reads directly from a string.
+  static bool MergeFromString(ConstStringParam input, Message* output);
+
+  // Parse the given text as a single field value and store it into the
+  // given field of the given message. If the field is a repeated field,
+  // the new value will be added to the end
+  static bool ParseFieldValueFromString(const std::string& input,
+                                        const FieldDescriptor* field,
+                                        Message* message);
+
+  // A location in the parsed text.
+  struct ParseLocation {
+    int line;
+    int column;
+
+    ParseLocation() : line(-1), column(-1) {}
+    ParseLocation(int line_param, int column_param)
+        : line(line_param), column(column_param) {}
+  };
+
+  // A range of locations in the parsed text, including `start` and excluding
+  // `end`.
+  struct ParseLocationRange {
+    ParseLocation start;
+    ParseLocation end;
+    ParseLocationRange() : start(), end() {}
+    ParseLocationRange(ParseLocation start_param, ParseLocation end_param)
+        : start(start_param), end(end_param) {}
+  };
+
+  // Data structure which is populated with the locations of each field
+  // value parsed from the text.
+  class PROTOBUF_EXPORT ParseInfoTree {
+   public:
+    ParseInfoTree() = default;
+    ParseInfoTree(const ParseInfoTree&) = delete;
+    ParseInfoTree& operator=(const ParseInfoTree&) = delete;
+
+    // Returns the parse location range for index-th value of the field in
+    // the parsed text. If none exists, returns a location with start and end
+    // line -1. Index should be -1 for not-repeated fields.
+    ParseLocationRange GetLocationRange(const FieldDescriptor* field,
+                                        int index) const;
+
+    // Returns the starting parse location for index-th value of the field in
+    // the parsed text. If none exists, returns a location with line = -1. Index
+    // should be -1 for not-repeated fields.
+    ParseLocation GetLocation(const FieldDescriptor* field, int index) const {
+      return GetLocationRange(field, index).start;
+    }
+
+    // Returns the parse info tree for the given field, which must be a message
+    // type. The nested information tree is owned by the root tree and will be
+    // deleted when it is deleted.
+    ParseInfoTree* GetTreeForNested(const FieldDescriptor* field,
+                                    int index) const;
+
+   private:
+    // Allow the text format parser to record information into the tree.
+    friend class TextFormat;
+
+    // Records the starting and ending locations of a single value for a field.
+    void RecordLocation(const FieldDescriptor* field, ParseLocationRange range);
+
+    // Create and records a nested tree for a nested message field.
+    ParseInfoTree* CreateNested(const FieldDescriptor* field);
+
+    // Defines the map from the index-th field descriptor to its parse location.
+    typedef std::map<const FieldDescriptor*, std::vector<ParseLocationRange>>
+        LocationMap;
+
+    // Defines the map from the index-th field descriptor to the nested parse
+    // info tree.
+    typedef std::map<const FieldDescriptor*,
+                     std::vector<std::unique_ptr<ParseInfoTree>>>
+        NestedMap;
+
+    LocationMap locations_;
+    NestedMap nested_;
+  };
+
+  // For more control over parsing, use this class.
+  class PROTOBUF_EXPORT Parser {
+   public:
+    Parser();
+    ~Parser();
+
+    // Like TextFormat::Parse().
+    bool Parse(io::ZeroCopyInputStream* input, Message* output);
+    // Like TextFormat::ParseFromString().
+    bool ParseFromString(ConstStringParam input, Message* output);
+    // Like TextFormat::Merge().
+    bool Merge(io::ZeroCopyInputStream* input, Message* output);
+    // Like TextFormat::MergeFromString().
+    bool MergeFromString(ConstStringParam input, Message* output);
+
+    // Set where to report parse errors.  If nullptr (the default), errors will
+    // be printed to stderr.
+    void RecordErrorsTo(io::ErrorCollector* error_collector) {
+      error_collector_ = error_collector;
+    }
+
+    // Set how parser finds extensions.  If nullptr (the default), the
+    // parser will use the standard Reflection object associated with
+    // the message being parsed.
+    void SetFinder(const Finder* finder) { finder_ = finder; }
+
+    // Sets where location information about the parse will be written. If
+    // nullptr
+    // (the default), then no location will be written.
+    void WriteLocationsTo(ParseInfoTree* tree) { parse_info_tree_ = tree; }
+
+    // Normally parsing fails if, after parsing, output->IsInitialized()
+    // returns false.  Call AllowPartialMessage(true) to skip this check.
+    void AllowPartialMessage(bool allow) { allow_partial_ = allow; }
+
+    // Allow field names to be matched case-insensitively.
+    // This is not advisable if there are fields that only differ in case, or
+    // if you want to enforce writing in the canonical form.
+    // This is 'false' by default.
+    void AllowCaseInsensitiveField(bool allow) {
+      allow_case_insensitive_field_ = allow;
+    }
+
+    // Like TextFormat::ParseFieldValueFromString
+    bool ParseFieldValueFromString(const std::string& input,
+                                   const FieldDescriptor* field,
+                                   Message* output);
+
+    // When an unknown extension is met, parsing will fail if this option is
+    // set to false (the default). If true, unknown extensions will be ignored
+    // and a warning message will be generated.
+    // Beware! Setting this option true may hide some errors (e.g. spelling
+    // error on extension name).  This allows data loss; unlike binary format,
+    // text format cannot preserve unknown extensions.  Avoid using this option
+    // if possible.
+    void AllowUnknownExtension(bool allow) { allow_unknown_extension_ = allow; }
+
+    // When an unknown field is met, parsing will fail if this option is set
+    // to false (the default). If true, unknown fields will be ignored and
+    // a warning message will be generated.
+    // Beware! Setting this option true may hide some errors (e.g. spelling
+    // error on field name). This allows data loss; unlike binary format, text
+    // format cannot preserve unknown fields.  Avoid using this option
+    // if possible.
+    void AllowUnknownField(bool allow) { allow_unknown_field_ = allow; }
+
+
+    void AllowFieldNumber(bool allow) { allow_field_number_ = allow; }
+
+    // Sets maximum recursion depth which parser can use. This is effectively
+    // the maximum allowed nesting of proto messages.
+    void SetRecursionLimit(int limit) { recursion_limit_ = limit; }
+
+   private:
+    // Forward declaration of an internal class used to parse text
+    // representations (see text_format.cc for implementation).
+    class ParserImpl;
+
+    // Like TextFormat::Merge().  The provided implementation is used
+    // to do the parsing.
+    bool MergeUsingImpl(io::ZeroCopyInputStream* input, Message* output,
+                        ParserImpl* parser_impl);
+
+    io::ErrorCollector* error_collector_;
+    const Finder* finder_;
+    ParseInfoTree* parse_info_tree_;
+    bool allow_partial_;
+    bool allow_case_insensitive_field_;
+    bool allow_unknown_field_;
+    bool allow_unknown_extension_;
+    bool allow_unknown_enum_;
+    bool allow_field_number_;
+    bool allow_relaxed_whitespace_;
+    bool allow_singular_overwrites_;
+    int recursion_limit_;
+  };
+
+
+ private:
+  // Hack: ParseInfoTree declares TextFormat as a friend which should extend
+  // the friendship to TextFormat::Parser::ParserImpl, but unfortunately some
+  // old compilers (e.g. GCC 3.4.6) don't implement this correctly. We provide
+  // helpers for ParserImpl to call methods of ParseInfoTree.
+  static inline void RecordLocation(ParseInfoTree* info_tree,
+                                    const FieldDescriptor* field,
+                                    ParseLocationRange location);
+  static inline ParseInfoTree* CreateNested(ParseInfoTree* info_tree,
+                                            const FieldDescriptor* field);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TextFormat);
+};
+
+inline void TextFormat::RecordLocation(ParseInfoTree* info_tree,
+                                       const FieldDescriptor* field,
+                                       ParseLocationRange location) {
+  info_tree->RecordLocation(field, location);
+}
+
+inline TextFormat::ParseInfoTree* TextFormat::CreateNested(
+    ParseInfoTree* info_tree, const FieldDescriptor* field) {
+  return info_tree->CreateNested(field);
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_TEXT_FORMAT_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/timestamp.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/timestamp.pb.h
new file mode 100644
index 0000000..540194b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/timestamp.pb.h
@@ -0,0 +1,278 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/timestamp.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2ftimestamp_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2ftimestamp_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/unknown_field_set.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2ftimestamp_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2ftimestamp_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2ftimestamp_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class Timestamp;
+struct TimestampDefaultTypeInternal;
+PROTOBUF_EXPORT extern TimestampDefaultTypeInternal _Timestamp_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Timestamp* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Timestamp>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class PROTOBUF_EXPORT Timestamp final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Timestamp) */ {
+ public:
+  inline Timestamp() : Timestamp(nullptr) {}
+  ~Timestamp() override;
+  explicit PROTOBUF_CONSTEXPR Timestamp(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Timestamp(const Timestamp& from);
+  Timestamp(Timestamp&& from) noexcept
+    : Timestamp() {
+    *this = ::std::move(from);
+  }
+
+  inline Timestamp& operator=(const Timestamp& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Timestamp& operator=(Timestamp&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Timestamp& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Timestamp* internal_default_instance() {
+    return reinterpret_cast<const Timestamp*>(
+               &_Timestamp_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(Timestamp& a, Timestamp& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Timestamp* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Timestamp* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Timestamp* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Timestamp>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Timestamp& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Timestamp& from) {
+    Timestamp::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Timestamp* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Timestamp";
+  }
+  protected:
+  explicit Timestamp(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kSecondsFieldNumber = 1,
+    kNanosFieldNumber = 2,
+  };
+  // int64 seconds = 1;
+  void clear_seconds();
+  int64_t seconds() const;
+  void set_seconds(int64_t value);
+  private:
+  int64_t _internal_seconds() const;
+  void _internal_set_seconds(int64_t value);
+  public:
+
+  // int32 nanos = 2;
+  void clear_nanos();
+  int32_t nanos() const;
+  void set_nanos(int32_t value);
+  private:
+  int32_t _internal_nanos() const;
+  void _internal_set_nanos(int32_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Timestamp)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    int64_t seconds_;
+    int32_t nanos_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2ftimestamp_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// Timestamp
+
+// int64 seconds = 1;
+inline void Timestamp::clear_seconds() {
+  _impl_.seconds_ = int64_t{0};
+}
+inline int64_t Timestamp::_internal_seconds() const {
+  return _impl_.seconds_;
+}
+inline int64_t Timestamp::seconds() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Timestamp.seconds)
+  return _internal_seconds();
+}
+inline void Timestamp::_internal_set_seconds(int64_t value) {
+  
+  _impl_.seconds_ = value;
+}
+inline void Timestamp::set_seconds(int64_t value) {
+  _internal_set_seconds(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Timestamp.seconds)
+}
+
+// int32 nanos = 2;
+inline void Timestamp::clear_nanos() {
+  _impl_.nanos_ = 0;
+}
+inline int32_t Timestamp::_internal_nanos() const {
+  return _impl_.nanos_;
+}
+inline int32_t Timestamp::nanos() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Timestamp.nanos)
+  return _internal_nanos();
+}
+inline void Timestamp::_internal_set_nanos(int32_t value) {
+  
+  _impl_.nanos_ = value;
+}
+inline void Timestamp::set_nanos(int32_t value) {
+  _internal_set_nanos(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Timestamp.nanos)
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2ftimestamp_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/type.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/type.pb.h
new file mode 100644
index 0000000..cb8105a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/type.pb.h
@@ -0,0 +1,2571 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/type.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2ftype_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2ftype_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/generated_enum_reflection.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/any.pb.h>
+#include <google/protobuf/source_context.pb.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2ftype_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2ftype_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2ftype_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class Enum;
+struct EnumDefaultTypeInternal;
+PROTOBUF_EXPORT extern EnumDefaultTypeInternal _Enum_default_instance_;
+class EnumValue;
+struct EnumValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern EnumValueDefaultTypeInternal _EnumValue_default_instance_;
+class Field;
+struct FieldDefaultTypeInternal;
+PROTOBUF_EXPORT extern FieldDefaultTypeInternal _Field_default_instance_;
+class Option;
+struct OptionDefaultTypeInternal;
+PROTOBUF_EXPORT extern OptionDefaultTypeInternal _Option_default_instance_;
+class Type;
+struct TypeDefaultTypeInternal;
+PROTOBUF_EXPORT extern TypeDefaultTypeInternal _Type_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Enum* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Enum>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::EnumValue* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::EnumValue>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Field* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Field>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Option* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Option>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Type* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Type>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+enum Field_Kind : int {
+  Field_Kind_TYPE_UNKNOWN = 0,
+  Field_Kind_TYPE_DOUBLE = 1,
+  Field_Kind_TYPE_FLOAT = 2,
+  Field_Kind_TYPE_INT64 = 3,
+  Field_Kind_TYPE_UINT64 = 4,
+  Field_Kind_TYPE_INT32 = 5,
+  Field_Kind_TYPE_FIXED64 = 6,
+  Field_Kind_TYPE_FIXED32 = 7,
+  Field_Kind_TYPE_BOOL = 8,
+  Field_Kind_TYPE_STRING = 9,
+  Field_Kind_TYPE_GROUP = 10,
+  Field_Kind_TYPE_MESSAGE = 11,
+  Field_Kind_TYPE_BYTES = 12,
+  Field_Kind_TYPE_UINT32 = 13,
+  Field_Kind_TYPE_ENUM = 14,
+  Field_Kind_TYPE_SFIXED32 = 15,
+  Field_Kind_TYPE_SFIXED64 = 16,
+  Field_Kind_TYPE_SINT32 = 17,
+  Field_Kind_TYPE_SINT64 = 18,
+  Field_Kind_Field_Kind_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),
+  Field_Kind_Field_Kind_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
+};
+PROTOBUF_EXPORT bool Field_Kind_IsValid(int value);
+constexpr Field_Kind Field_Kind_Kind_MIN = Field_Kind_TYPE_UNKNOWN;
+constexpr Field_Kind Field_Kind_Kind_MAX = Field_Kind_TYPE_SINT64;
+constexpr int Field_Kind_Kind_ARRAYSIZE = Field_Kind_Kind_MAX + 1;
+
+PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Field_Kind_descriptor();
+template<typename T>
+inline const std::string& Field_Kind_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, Field_Kind>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function Field_Kind_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    Field_Kind_descriptor(), enum_t_value);
+}
+inline bool Field_Kind_Parse(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, Field_Kind* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<Field_Kind>(
+    Field_Kind_descriptor(), name, value);
+}
+enum Field_Cardinality : int {
+  Field_Cardinality_CARDINALITY_UNKNOWN = 0,
+  Field_Cardinality_CARDINALITY_OPTIONAL = 1,
+  Field_Cardinality_CARDINALITY_REQUIRED = 2,
+  Field_Cardinality_CARDINALITY_REPEATED = 3,
+  Field_Cardinality_Field_Cardinality_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),
+  Field_Cardinality_Field_Cardinality_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
+};
+PROTOBUF_EXPORT bool Field_Cardinality_IsValid(int value);
+constexpr Field_Cardinality Field_Cardinality_Cardinality_MIN = Field_Cardinality_CARDINALITY_UNKNOWN;
+constexpr Field_Cardinality Field_Cardinality_Cardinality_MAX = Field_Cardinality_CARDINALITY_REPEATED;
+constexpr int Field_Cardinality_Cardinality_ARRAYSIZE = Field_Cardinality_Cardinality_MAX + 1;
+
+PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Field_Cardinality_descriptor();
+template<typename T>
+inline const std::string& Field_Cardinality_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, Field_Cardinality>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function Field_Cardinality_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    Field_Cardinality_descriptor(), enum_t_value);
+}
+inline bool Field_Cardinality_Parse(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, Field_Cardinality* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<Field_Cardinality>(
+    Field_Cardinality_descriptor(), name, value);
+}
+enum Syntax : int {
+  SYNTAX_PROTO2 = 0,
+  SYNTAX_PROTO3 = 1,
+  Syntax_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),
+  Syntax_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
+};
+PROTOBUF_EXPORT bool Syntax_IsValid(int value);
+constexpr Syntax Syntax_MIN = SYNTAX_PROTO2;
+constexpr Syntax Syntax_MAX = SYNTAX_PROTO3;
+constexpr int Syntax_ARRAYSIZE = Syntax_MAX + 1;
+
+PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Syntax_descriptor();
+template<typename T>
+inline const std::string& Syntax_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, Syntax>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function Syntax_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    Syntax_descriptor(), enum_t_value);
+}
+inline bool Syntax_Parse(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, Syntax* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<Syntax>(
+    Syntax_descriptor(), name, value);
+}
+// ===================================================================
+
+class PROTOBUF_EXPORT Type final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Type) */ {
+ public:
+  inline Type() : Type(nullptr) {}
+  ~Type() override;
+  explicit PROTOBUF_CONSTEXPR Type(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Type(const Type& from);
+  Type(Type&& from) noexcept
+    : Type() {
+    *this = ::std::move(from);
+  }
+
+  inline Type& operator=(const Type& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Type& operator=(Type&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Type& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Type* internal_default_instance() {
+    return reinterpret_cast<const Type*>(
+               &_Type_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(Type& a, Type& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Type* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Type* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Type* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Type>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Type& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Type& from) {
+    Type::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Type* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Type";
+  }
+  protected:
+  explicit Type(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kFieldsFieldNumber = 2,
+    kOneofsFieldNumber = 3,
+    kOptionsFieldNumber = 4,
+    kNameFieldNumber = 1,
+    kSourceContextFieldNumber = 5,
+    kSyntaxFieldNumber = 6,
+  };
+  // repeated .google.protobuf.Field fields = 2;
+  int fields_size() const;
+  private:
+  int _internal_fields_size() const;
+  public:
+  void clear_fields();
+  ::PROTOBUF_NAMESPACE_ID::Field* mutable_fields(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Field >*
+      mutable_fields();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Field& _internal_fields(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Field* _internal_add_fields();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Field& fields(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Field* add_fields();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Field >&
+      fields() const;
+
+  // repeated string oneofs = 3;
+  int oneofs_size() const;
+  private:
+  int _internal_oneofs_size() const;
+  public:
+  void clear_oneofs();
+  const std::string& oneofs(int index) const;
+  std::string* mutable_oneofs(int index);
+  void set_oneofs(int index, const std::string& value);
+  void set_oneofs(int index, std::string&& value);
+  void set_oneofs(int index, const char* value);
+  void set_oneofs(int index, const char* value, size_t size);
+  std::string* add_oneofs();
+  void add_oneofs(const std::string& value);
+  void add_oneofs(std::string&& value);
+  void add_oneofs(const char* value);
+  void add_oneofs(const char* value, size_t size);
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& oneofs() const;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_oneofs();
+  private:
+  const std::string& _internal_oneofs(int index) const;
+  std::string* _internal_add_oneofs();
+  public:
+
+  // repeated .google.protobuf.Option options = 4;
+  int options_size() const;
+  private:
+  int _internal_options_size() const;
+  public:
+  void clear_options();
+  ::PROTOBUF_NAMESPACE_ID::Option* mutable_options(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+      mutable_options();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Option& _internal_options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* _internal_add_options();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Option& options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* add_options();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+      options() const;
+
+  // string name = 1;
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // .google.protobuf.SourceContext source_context = 5;
+  bool has_source_context() const;
+  private:
+  bool _internal_has_source_context() const;
+  public:
+  void clear_source_context();
+  const ::PROTOBUF_NAMESPACE_ID::SourceContext& source_context() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::SourceContext* release_source_context();
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* mutable_source_context();
+  void set_allocated_source_context(::PROTOBUF_NAMESPACE_ID::SourceContext* source_context);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::SourceContext& _internal_source_context() const;
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* _internal_mutable_source_context();
+  public:
+  void unsafe_arena_set_allocated_source_context(
+      ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context);
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* unsafe_arena_release_source_context();
+
+  // .google.protobuf.Syntax syntax = 6;
+  void clear_syntax();
+  ::PROTOBUF_NAMESPACE_ID::Syntax syntax() const;
+  void set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::Syntax _internal_syntax() const;
+  void _internal_set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Type)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Field > fields_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> oneofs_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option > options_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context_;
+    int syntax_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2ftype_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT Field final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Field) */ {
+ public:
+  inline Field() : Field(nullptr) {}
+  ~Field() override;
+  explicit PROTOBUF_CONSTEXPR Field(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Field(const Field& from);
+  Field(Field&& from) noexcept
+    : Field() {
+    *this = ::std::move(from);
+  }
+
+  inline Field& operator=(const Field& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Field& operator=(Field&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Field& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Field* internal_default_instance() {
+    return reinterpret_cast<const Field*>(
+               &_Field_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    1;
+
+  friend void swap(Field& a, Field& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Field* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Field* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Field* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Field>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Field& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Field& from) {
+    Field::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Field* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Field";
+  }
+  protected:
+  explicit Field(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef Field_Kind Kind;
+  static constexpr Kind TYPE_UNKNOWN =
+    Field_Kind_TYPE_UNKNOWN;
+  static constexpr Kind TYPE_DOUBLE =
+    Field_Kind_TYPE_DOUBLE;
+  static constexpr Kind TYPE_FLOAT =
+    Field_Kind_TYPE_FLOAT;
+  static constexpr Kind TYPE_INT64 =
+    Field_Kind_TYPE_INT64;
+  static constexpr Kind TYPE_UINT64 =
+    Field_Kind_TYPE_UINT64;
+  static constexpr Kind TYPE_INT32 =
+    Field_Kind_TYPE_INT32;
+  static constexpr Kind TYPE_FIXED64 =
+    Field_Kind_TYPE_FIXED64;
+  static constexpr Kind TYPE_FIXED32 =
+    Field_Kind_TYPE_FIXED32;
+  static constexpr Kind TYPE_BOOL =
+    Field_Kind_TYPE_BOOL;
+  static constexpr Kind TYPE_STRING =
+    Field_Kind_TYPE_STRING;
+  static constexpr Kind TYPE_GROUP =
+    Field_Kind_TYPE_GROUP;
+  static constexpr Kind TYPE_MESSAGE =
+    Field_Kind_TYPE_MESSAGE;
+  static constexpr Kind TYPE_BYTES =
+    Field_Kind_TYPE_BYTES;
+  static constexpr Kind TYPE_UINT32 =
+    Field_Kind_TYPE_UINT32;
+  static constexpr Kind TYPE_ENUM =
+    Field_Kind_TYPE_ENUM;
+  static constexpr Kind TYPE_SFIXED32 =
+    Field_Kind_TYPE_SFIXED32;
+  static constexpr Kind TYPE_SFIXED64 =
+    Field_Kind_TYPE_SFIXED64;
+  static constexpr Kind TYPE_SINT32 =
+    Field_Kind_TYPE_SINT32;
+  static constexpr Kind TYPE_SINT64 =
+    Field_Kind_TYPE_SINT64;
+  static inline bool Kind_IsValid(int value) {
+    return Field_Kind_IsValid(value);
+  }
+  static constexpr Kind Kind_MIN =
+    Field_Kind_Kind_MIN;
+  static constexpr Kind Kind_MAX =
+    Field_Kind_Kind_MAX;
+  static constexpr int Kind_ARRAYSIZE =
+    Field_Kind_Kind_ARRAYSIZE;
+  static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
+  Kind_descriptor() {
+    return Field_Kind_descriptor();
+  }
+  template<typename T>
+  static inline const std::string& Kind_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, Kind>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function Kind_Name.");
+    return Field_Kind_Name(enum_t_value);
+  }
+  static inline bool Kind_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+      Kind* value) {
+    return Field_Kind_Parse(name, value);
+  }
+
+  typedef Field_Cardinality Cardinality;
+  static constexpr Cardinality CARDINALITY_UNKNOWN =
+    Field_Cardinality_CARDINALITY_UNKNOWN;
+  static constexpr Cardinality CARDINALITY_OPTIONAL =
+    Field_Cardinality_CARDINALITY_OPTIONAL;
+  static constexpr Cardinality CARDINALITY_REQUIRED =
+    Field_Cardinality_CARDINALITY_REQUIRED;
+  static constexpr Cardinality CARDINALITY_REPEATED =
+    Field_Cardinality_CARDINALITY_REPEATED;
+  static inline bool Cardinality_IsValid(int value) {
+    return Field_Cardinality_IsValid(value);
+  }
+  static constexpr Cardinality Cardinality_MIN =
+    Field_Cardinality_Cardinality_MIN;
+  static constexpr Cardinality Cardinality_MAX =
+    Field_Cardinality_Cardinality_MAX;
+  static constexpr int Cardinality_ARRAYSIZE =
+    Field_Cardinality_Cardinality_ARRAYSIZE;
+  static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
+  Cardinality_descriptor() {
+    return Field_Cardinality_descriptor();
+  }
+  template<typename T>
+  static inline const std::string& Cardinality_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, Cardinality>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function Cardinality_Name.");
+    return Field_Cardinality_Name(enum_t_value);
+  }
+  static inline bool Cardinality_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+      Cardinality* value) {
+    return Field_Cardinality_Parse(name, value);
+  }
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kOptionsFieldNumber = 9,
+    kNameFieldNumber = 4,
+    kTypeUrlFieldNumber = 6,
+    kJsonNameFieldNumber = 10,
+    kDefaultValueFieldNumber = 11,
+    kKindFieldNumber = 1,
+    kCardinalityFieldNumber = 2,
+    kNumberFieldNumber = 3,
+    kOneofIndexFieldNumber = 7,
+    kPackedFieldNumber = 8,
+  };
+  // repeated .google.protobuf.Option options = 9;
+  int options_size() const;
+  private:
+  int _internal_options_size() const;
+  public:
+  void clear_options();
+  ::PROTOBUF_NAMESPACE_ID::Option* mutable_options(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+      mutable_options();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Option& _internal_options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* _internal_add_options();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Option& options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* add_options();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+      options() const;
+
+  // string name = 4;
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // string type_url = 6;
+  void clear_type_url();
+  const std::string& type_url() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_type_url(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_type_url();
+  PROTOBUF_NODISCARD std::string* release_type_url();
+  void set_allocated_type_url(std::string* type_url);
+  private:
+  const std::string& _internal_type_url() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_type_url(const std::string& value);
+  std::string* _internal_mutable_type_url();
+  public:
+
+  // string json_name = 10;
+  void clear_json_name();
+  const std::string& json_name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_json_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_json_name();
+  PROTOBUF_NODISCARD std::string* release_json_name();
+  void set_allocated_json_name(std::string* json_name);
+  private:
+  const std::string& _internal_json_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_json_name(const std::string& value);
+  std::string* _internal_mutable_json_name();
+  public:
+
+  // string default_value = 11;
+  void clear_default_value();
+  const std::string& default_value() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_default_value(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_default_value();
+  PROTOBUF_NODISCARD std::string* release_default_value();
+  void set_allocated_default_value(std::string* default_value);
+  private:
+  const std::string& _internal_default_value() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_default_value(const std::string& value);
+  std::string* _internal_mutable_default_value();
+  public:
+
+  // .google.protobuf.Field.Kind kind = 1;
+  void clear_kind();
+  ::PROTOBUF_NAMESPACE_ID::Field_Kind kind() const;
+  void set_kind(::PROTOBUF_NAMESPACE_ID::Field_Kind value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::Field_Kind _internal_kind() const;
+  void _internal_set_kind(::PROTOBUF_NAMESPACE_ID::Field_Kind value);
+  public:
+
+  // .google.protobuf.Field.Cardinality cardinality = 2;
+  void clear_cardinality();
+  ::PROTOBUF_NAMESPACE_ID::Field_Cardinality cardinality() const;
+  void set_cardinality(::PROTOBUF_NAMESPACE_ID::Field_Cardinality value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::Field_Cardinality _internal_cardinality() const;
+  void _internal_set_cardinality(::PROTOBUF_NAMESPACE_ID::Field_Cardinality value);
+  public:
+
+  // int32 number = 3;
+  void clear_number();
+  int32_t number() const;
+  void set_number(int32_t value);
+  private:
+  int32_t _internal_number() const;
+  void _internal_set_number(int32_t value);
+  public:
+
+  // int32 oneof_index = 7;
+  void clear_oneof_index();
+  int32_t oneof_index() const;
+  void set_oneof_index(int32_t value);
+  private:
+  int32_t _internal_oneof_index() const;
+  void _internal_set_oneof_index(int32_t value);
+  public:
+
+  // bool packed = 8;
+  void clear_packed();
+  bool packed() const;
+  void set_packed(bool value);
+  private:
+  bool _internal_packed() const;
+  void _internal_set_packed(bool value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Field)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option > options_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr type_url_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr json_name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr default_value_;
+    int kind_;
+    int cardinality_;
+    int32_t number_;
+    int32_t oneof_index_;
+    bool packed_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2ftype_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT Enum final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Enum) */ {
+ public:
+  inline Enum() : Enum(nullptr) {}
+  ~Enum() override;
+  explicit PROTOBUF_CONSTEXPR Enum(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Enum(const Enum& from);
+  Enum(Enum&& from) noexcept
+    : Enum() {
+    *this = ::std::move(from);
+  }
+
+  inline Enum& operator=(const Enum& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Enum& operator=(Enum&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Enum& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Enum* internal_default_instance() {
+    return reinterpret_cast<const Enum*>(
+               &_Enum_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    2;
+
+  friend void swap(Enum& a, Enum& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Enum* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Enum* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Enum* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Enum>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Enum& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Enum& from) {
+    Enum::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Enum* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Enum";
+  }
+  protected:
+  explicit Enum(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kEnumvalueFieldNumber = 2,
+    kOptionsFieldNumber = 3,
+    kNameFieldNumber = 1,
+    kSourceContextFieldNumber = 4,
+    kSyntaxFieldNumber = 5,
+  };
+  // repeated .google.protobuf.EnumValue enumvalue = 2;
+  int enumvalue_size() const;
+  private:
+  int _internal_enumvalue_size() const;
+  public:
+  void clear_enumvalue();
+  ::PROTOBUF_NAMESPACE_ID::EnumValue* mutable_enumvalue(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValue >*
+      mutable_enumvalue();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::EnumValue& _internal_enumvalue(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::EnumValue* _internal_add_enumvalue();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::EnumValue& enumvalue(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::EnumValue* add_enumvalue();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValue >&
+      enumvalue() const;
+
+  // repeated .google.protobuf.Option options = 3;
+  int options_size() const;
+  private:
+  int _internal_options_size() const;
+  public:
+  void clear_options();
+  ::PROTOBUF_NAMESPACE_ID::Option* mutable_options(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+      mutable_options();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Option& _internal_options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* _internal_add_options();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Option& options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* add_options();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+      options() const;
+
+  // string name = 1;
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // .google.protobuf.SourceContext source_context = 4;
+  bool has_source_context() const;
+  private:
+  bool _internal_has_source_context() const;
+  public:
+  void clear_source_context();
+  const ::PROTOBUF_NAMESPACE_ID::SourceContext& source_context() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::SourceContext* release_source_context();
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* mutable_source_context();
+  void set_allocated_source_context(::PROTOBUF_NAMESPACE_ID::SourceContext* source_context);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::SourceContext& _internal_source_context() const;
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* _internal_mutable_source_context();
+  public:
+  void unsafe_arena_set_allocated_source_context(
+      ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context);
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* unsafe_arena_release_source_context();
+
+  // .google.protobuf.Syntax syntax = 5;
+  void clear_syntax();
+  ::PROTOBUF_NAMESPACE_ID::Syntax syntax() const;
+  void set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::Syntax _internal_syntax() const;
+  void _internal_set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Enum)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValue > enumvalue_;
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option > options_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context_;
+    int syntax_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2ftype_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT EnumValue final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.EnumValue) */ {
+ public:
+  inline EnumValue() : EnumValue(nullptr) {}
+  ~EnumValue() override;
+  explicit PROTOBUF_CONSTEXPR EnumValue(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  EnumValue(const EnumValue& from);
+  EnumValue(EnumValue&& from) noexcept
+    : EnumValue() {
+    *this = ::std::move(from);
+  }
+
+  inline EnumValue& operator=(const EnumValue& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline EnumValue& operator=(EnumValue&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const EnumValue& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const EnumValue* internal_default_instance() {
+    return reinterpret_cast<const EnumValue*>(
+               &_EnumValue_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    3;
+
+  friend void swap(EnumValue& a, EnumValue& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(EnumValue* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(EnumValue* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  EnumValue* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<EnumValue>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const EnumValue& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const EnumValue& from) {
+    EnumValue::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(EnumValue* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.EnumValue";
+  }
+  protected:
+  explicit EnumValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kOptionsFieldNumber = 3,
+    kNameFieldNumber = 1,
+    kNumberFieldNumber = 2,
+  };
+  // repeated .google.protobuf.Option options = 3;
+  int options_size() const;
+  private:
+  int _internal_options_size() const;
+  public:
+  void clear_options();
+  ::PROTOBUF_NAMESPACE_ID::Option* mutable_options(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+      mutable_options();
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Option& _internal_options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* _internal_add_options();
+  public:
+  const ::PROTOBUF_NAMESPACE_ID::Option& options(int index) const;
+  ::PROTOBUF_NAMESPACE_ID::Option* add_options();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+      options() const;
+
+  // string name = 1;
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // int32 number = 2;
+  void clear_number();
+  int32_t number() const;
+  void set_number(int32_t value);
+  private:
+  int32_t _internal_number() const;
+  void _internal_set_number(int32_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.EnumValue)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option > options_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    int32_t number_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2ftype_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT Option final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Option) */ {
+ public:
+  inline Option() : Option(nullptr) {}
+  ~Option() override;
+  explicit PROTOBUF_CONSTEXPR Option(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Option(const Option& from);
+  Option(Option&& from) noexcept
+    : Option() {
+    *this = ::std::move(from);
+  }
+
+  inline Option& operator=(const Option& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Option& operator=(Option&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Option& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Option* internal_default_instance() {
+    return reinterpret_cast<const Option*>(
+               &_Option_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    4;
+
+  friend void swap(Option& a, Option& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Option* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Option* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Option* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Option>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Option& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Option& from) {
+    Option::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Option* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Option";
+  }
+  protected:
+  explicit Option(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNameFieldNumber = 1,
+    kValueFieldNumber = 2,
+  };
+  // string name = 1;
+  void clear_name();
+  const std::string& name() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_name(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_name();
+  PROTOBUF_NODISCARD std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // .google.protobuf.Any value = 2;
+  bool has_value() const;
+  private:
+  bool _internal_has_value() const;
+  public:
+  void clear_value();
+  const ::PROTOBUF_NAMESPACE_ID::Any& value() const;
+  PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::Any* release_value();
+  ::PROTOBUF_NAMESPACE_ID::Any* mutable_value();
+  void set_allocated_value(::PROTOBUF_NAMESPACE_ID::Any* value);
+  private:
+  const ::PROTOBUF_NAMESPACE_ID::Any& _internal_value() const;
+  ::PROTOBUF_NAMESPACE_ID::Any* _internal_mutable_value();
+  public:
+  void unsafe_arena_set_allocated_value(
+      ::PROTOBUF_NAMESPACE_ID::Any* value);
+  ::PROTOBUF_NAMESPACE_ID::Any* unsafe_arena_release_value();
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Option)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::Any* value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2ftype_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// Type
+
+// string name = 1;
+inline void Type::clear_name() {
+  _impl_.name_.ClearToEmpty();
+}
+inline const std::string& Type::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Type.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Type::set_name(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Type.name)
+}
+inline std::string* Type::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Type.name)
+  return _s;
+}
+inline const std::string& Type::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void Type::_internal_set_name(const std::string& value) {
+  
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Type::_internal_mutable_name() {
+  
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Type::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Type.name)
+  return _impl_.name_.Release();
+}
+inline void Type::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Type.name)
+}
+
+// repeated .google.protobuf.Field fields = 2;
+inline int Type::_internal_fields_size() const {
+  return _impl_.fields_.size();
+}
+inline int Type::fields_size() const {
+  return _internal_fields_size();
+}
+inline void Type::clear_fields() {
+  _impl_.fields_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Field* Type::mutable_fields(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Type.fields)
+  return _impl_.fields_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Field >*
+Type::mutable_fields() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.Type.fields)
+  return &_impl_.fields_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Field& Type::_internal_fields(int index) const {
+  return _impl_.fields_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Field& Type::fields(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Type.fields)
+  return _internal_fields(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Field* Type::_internal_add_fields() {
+  return _impl_.fields_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Field* Type::add_fields() {
+  ::PROTOBUF_NAMESPACE_ID::Field* _add = _internal_add_fields();
+  // @@protoc_insertion_point(field_add:google.protobuf.Type.fields)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Field >&
+Type::fields() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.Type.fields)
+  return _impl_.fields_;
+}
+
+// repeated string oneofs = 3;
+inline int Type::_internal_oneofs_size() const {
+  return _impl_.oneofs_.size();
+}
+inline int Type::oneofs_size() const {
+  return _internal_oneofs_size();
+}
+inline void Type::clear_oneofs() {
+  _impl_.oneofs_.Clear();
+}
+inline std::string* Type::add_oneofs() {
+  std::string* _s = _internal_add_oneofs();
+  // @@protoc_insertion_point(field_add_mutable:google.protobuf.Type.oneofs)
+  return _s;
+}
+inline const std::string& Type::_internal_oneofs(int index) const {
+  return _impl_.oneofs_.Get(index);
+}
+inline const std::string& Type::oneofs(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Type.oneofs)
+  return _internal_oneofs(index);
+}
+inline std::string* Type::mutable_oneofs(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Type.oneofs)
+  return _impl_.oneofs_.Mutable(index);
+}
+inline void Type::set_oneofs(int index, const std::string& value) {
+  _impl_.oneofs_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Type.oneofs)
+}
+inline void Type::set_oneofs(int index, std::string&& value) {
+  _impl_.oneofs_.Mutable(index)->assign(std::move(value));
+  // @@protoc_insertion_point(field_set:google.protobuf.Type.oneofs)
+}
+inline void Type::set_oneofs(int index, const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.oneofs_.Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set_char:google.protobuf.Type.oneofs)
+}
+inline void Type::set_oneofs(int index, const char* value, size_t size) {
+  _impl_.oneofs_.Mutable(index)->assign(
+    reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_set_pointer:google.protobuf.Type.oneofs)
+}
+inline std::string* Type::_internal_add_oneofs() {
+  return _impl_.oneofs_.Add();
+}
+inline void Type::add_oneofs(const std::string& value) {
+  _impl_.oneofs_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add:google.protobuf.Type.oneofs)
+}
+inline void Type::add_oneofs(std::string&& value) {
+  _impl_.oneofs_.Add(std::move(value));
+  // @@protoc_insertion_point(field_add:google.protobuf.Type.oneofs)
+}
+inline void Type::add_oneofs(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  _impl_.oneofs_.Add()->assign(value);
+  // @@protoc_insertion_point(field_add_char:google.protobuf.Type.oneofs)
+}
+inline void Type::add_oneofs(const char* value, size_t size) {
+  _impl_.oneofs_.Add()->assign(reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_add_pointer:google.protobuf.Type.oneofs)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&
+Type::oneofs() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.Type.oneofs)
+  return _impl_.oneofs_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
+Type::mutable_oneofs() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.Type.oneofs)
+  return &_impl_.oneofs_;
+}
+
+// repeated .google.protobuf.Option options = 4;
+inline int Type::_internal_options_size() const {
+  return _impl_.options_.size();
+}
+inline int Type::options_size() const {
+  return _internal_options_size();
+}
+inline void Type::clear_options() {
+  _impl_.options_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Type::mutable_options(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Type.options)
+  return _impl_.options_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+Type::mutable_options() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.Type.options)
+  return &_impl_.options_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& Type::_internal_options(int index) const {
+  return _impl_.options_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& Type::options(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Type.options)
+  return _internal_options(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Type::_internal_add_options() {
+  return _impl_.options_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Type::add_options() {
+  ::PROTOBUF_NAMESPACE_ID::Option* _add = _internal_add_options();
+  // @@protoc_insertion_point(field_add:google.protobuf.Type.options)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+Type::options() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.Type.options)
+  return _impl_.options_;
+}
+
+// .google.protobuf.SourceContext source_context = 5;
+inline bool Type::_internal_has_source_context() const {
+  return this != internal_default_instance() && _impl_.source_context_ != nullptr;
+}
+inline bool Type::has_source_context() const {
+  return _internal_has_source_context();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::SourceContext& Type::_internal_source_context() const {
+  const ::PROTOBUF_NAMESPACE_ID::SourceContext* p = _impl_.source_context_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::SourceContext&>(
+      ::PROTOBUF_NAMESPACE_ID::_SourceContext_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::SourceContext& Type::source_context() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Type.source_context)
+  return _internal_source_context();
+}
+inline void Type::unsafe_arena_set_allocated_source_context(
+    ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.source_context_);
+  }
+  _impl_.source_context_ = source_context;
+  if (source_context) {
+    
+  } else {
+    
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.Type.source_context)
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Type::release_source_context() {
+  
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* temp = _impl_.source_context_;
+  _impl_.source_context_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Type::unsafe_arena_release_source_context() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Type.source_context)
+  
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* temp = _impl_.source_context_;
+  _impl_.source_context_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Type::_internal_mutable_source_context() {
+  
+  if (_impl_.source_context_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::SourceContext>(GetArenaForAllocation());
+    _impl_.source_context_ = p;
+  }
+  return _impl_.source_context_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Type::mutable_source_context() {
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* _msg = _internal_mutable_source_context();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Type.source_context)
+  return _msg;
+}
+inline void Type::set_allocated_source_context(::PROTOBUF_NAMESPACE_ID::SourceContext* source_context) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.source_context_);
+  }
+  if (source_context) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(
+                reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(source_context));
+    if (message_arena != submessage_arena) {
+      source_context = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, source_context, submessage_arena);
+    }
+    
+  } else {
+    
+  }
+  _impl_.source_context_ = source_context;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Type.source_context)
+}
+
+// .google.protobuf.Syntax syntax = 6;
+inline void Type::clear_syntax() {
+  _impl_.syntax_ = 0;
+}
+inline ::PROTOBUF_NAMESPACE_ID::Syntax Type::_internal_syntax() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::Syntax >(_impl_.syntax_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Syntax Type::syntax() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Type.syntax)
+  return _internal_syntax();
+}
+inline void Type::_internal_set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value) {
+  
+  _impl_.syntax_ = value;
+}
+inline void Type::set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value) {
+  _internal_set_syntax(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Type.syntax)
+}
+
+// -------------------------------------------------------------------
+
+// Field
+
+// .google.protobuf.Field.Kind kind = 1;
+inline void Field::clear_kind() {
+  _impl_.kind_ = 0;
+}
+inline ::PROTOBUF_NAMESPACE_ID::Field_Kind Field::_internal_kind() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::Field_Kind >(_impl_.kind_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Field_Kind Field::kind() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Field.kind)
+  return _internal_kind();
+}
+inline void Field::_internal_set_kind(::PROTOBUF_NAMESPACE_ID::Field_Kind value) {
+  
+  _impl_.kind_ = value;
+}
+inline void Field::set_kind(::PROTOBUF_NAMESPACE_ID::Field_Kind value) {
+  _internal_set_kind(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Field.kind)
+}
+
+// .google.protobuf.Field.Cardinality cardinality = 2;
+inline void Field::clear_cardinality() {
+  _impl_.cardinality_ = 0;
+}
+inline ::PROTOBUF_NAMESPACE_ID::Field_Cardinality Field::_internal_cardinality() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::Field_Cardinality >(_impl_.cardinality_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Field_Cardinality Field::cardinality() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Field.cardinality)
+  return _internal_cardinality();
+}
+inline void Field::_internal_set_cardinality(::PROTOBUF_NAMESPACE_ID::Field_Cardinality value) {
+  
+  _impl_.cardinality_ = value;
+}
+inline void Field::set_cardinality(::PROTOBUF_NAMESPACE_ID::Field_Cardinality value) {
+  _internal_set_cardinality(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Field.cardinality)
+}
+
+// int32 number = 3;
+inline void Field::clear_number() {
+  _impl_.number_ = 0;
+}
+inline int32_t Field::_internal_number() const {
+  return _impl_.number_;
+}
+inline int32_t Field::number() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Field.number)
+  return _internal_number();
+}
+inline void Field::_internal_set_number(int32_t value) {
+  
+  _impl_.number_ = value;
+}
+inline void Field::set_number(int32_t value) {
+  _internal_set_number(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Field.number)
+}
+
+// string name = 4;
+inline void Field::clear_name() {
+  _impl_.name_.ClearToEmpty();
+}
+inline const std::string& Field::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Field.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Field::set_name(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Field.name)
+}
+inline std::string* Field::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Field.name)
+  return _s;
+}
+inline const std::string& Field::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void Field::_internal_set_name(const std::string& value) {
+  
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Field::_internal_mutable_name() {
+  
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Field::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Field.name)
+  return _impl_.name_.Release();
+}
+inline void Field::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.name)
+}
+
+// string type_url = 6;
+inline void Field::clear_type_url() {
+  _impl_.type_url_.ClearToEmpty();
+}
+inline const std::string& Field::type_url() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Field.type_url)
+  return _internal_type_url();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Field::set_type_url(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.type_url_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Field.type_url)
+}
+inline std::string* Field::mutable_type_url() {
+  std::string* _s = _internal_mutable_type_url();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Field.type_url)
+  return _s;
+}
+inline const std::string& Field::_internal_type_url() const {
+  return _impl_.type_url_.Get();
+}
+inline void Field::_internal_set_type_url(const std::string& value) {
+  
+  _impl_.type_url_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Field::_internal_mutable_type_url() {
+  
+  return _impl_.type_url_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Field::release_type_url() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Field.type_url)
+  return _impl_.type_url_.Release();
+}
+inline void Field::set_allocated_type_url(std::string* type_url) {
+  if (type_url != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.type_url_.SetAllocated(type_url, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.type_url_.IsDefault()) {
+    _impl_.type_url_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.type_url)
+}
+
+// int32 oneof_index = 7;
+inline void Field::clear_oneof_index() {
+  _impl_.oneof_index_ = 0;
+}
+inline int32_t Field::_internal_oneof_index() const {
+  return _impl_.oneof_index_;
+}
+inline int32_t Field::oneof_index() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Field.oneof_index)
+  return _internal_oneof_index();
+}
+inline void Field::_internal_set_oneof_index(int32_t value) {
+  
+  _impl_.oneof_index_ = value;
+}
+inline void Field::set_oneof_index(int32_t value) {
+  _internal_set_oneof_index(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Field.oneof_index)
+}
+
+// bool packed = 8;
+inline void Field::clear_packed() {
+  _impl_.packed_ = false;
+}
+inline bool Field::_internal_packed() const {
+  return _impl_.packed_;
+}
+inline bool Field::packed() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Field.packed)
+  return _internal_packed();
+}
+inline void Field::_internal_set_packed(bool value) {
+  
+  _impl_.packed_ = value;
+}
+inline void Field::set_packed(bool value) {
+  _internal_set_packed(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Field.packed)
+}
+
+// repeated .google.protobuf.Option options = 9;
+inline int Field::_internal_options_size() const {
+  return _impl_.options_.size();
+}
+inline int Field::options_size() const {
+  return _internal_options_size();
+}
+inline void Field::clear_options() {
+  _impl_.options_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Field::mutable_options(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Field.options)
+  return _impl_.options_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+Field::mutable_options() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.Field.options)
+  return &_impl_.options_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& Field::_internal_options(int index) const {
+  return _impl_.options_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& Field::options(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Field.options)
+  return _internal_options(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Field::_internal_add_options() {
+  return _impl_.options_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Field::add_options() {
+  ::PROTOBUF_NAMESPACE_ID::Option* _add = _internal_add_options();
+  // @@protoc_insertion_point(field_add:google.protobuf.Field.options)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+Field::options() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.Field.options)
+  return _impl_.options_;
+}
+
+// string json_name = 10;
+inline void Field::clear_json_name() {
+  _impl_.json_name_.ClearToEmpty();
+}
+inline const std::string& Field::json_name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Field.json_name)
+  return _internal_json_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Field::set_json_name(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.json_name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Field.json_name)
+}
+inline std::string* Field::mutable_json_name() {
+  std::string* _s = _internal_mutable_json_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Field.json_name)
+  return _s;
+}
+inline const std::string& Field::_internal_json_name() const {
+  return _impl_.json_name_.Get();
+}
+inline void Field::_internal_set_json_name(const std::string& value) {
+  
+  _impl_.json_name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Field::_internal_mutable_json_name() {
+  
+  return _impl_.json_name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Field::release_json_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Field.json_name)
+  return _impl_.json_name_.Release();
+}
+inline void Field::set_allocated_json_name(std::string* json_name) {
+  if (json_name != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.json_name_.SetAllocated(json_name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.json_name_.IsDefault()) {
+    _impl_.json_name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.json_name)
+}
+
+// string default_value = 11;
+inline void Field::clear_default_value() {
+  _impl_.default_value_.ClearToEmpty();
+}
+inline const std::string& Field::default_value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Field.default_value)
+  return _internal_default_value();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Field::set_default_value(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.default_value_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Field.default_value)
+}
+inline std::string* Field::mutable_default_value() {
+  std::string* _s = _internal_mutable_default_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Field.default_value)
+  return _s;
+}
+inline const std::string& Field::_internal_default_value() const {
+  return _impl_.default_value_.Get();
+}
+inline void Field::_internal_set_default_value(const std::string& value) {
+  
+  _impl_.default_value_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Field::_internal_mutable_default_value() {
+  
+  return _impl_.default_value_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Field::release_default_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Field.default_value)
+  return _impl_.default_value_.Release();
+}
+inline void Field::set_allocated_default_value(std::string* default_value) {
+  if (default_value != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.default_value_.SetAllocated(default_value, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.default_value_.IsDefault()) {
+    _impl_.default_value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.default_value)
+}
+
+// -------------------------------------------------------------------
+
+// Enum
+
+// string name = 1;
+inline void Enum::clear_name() {
+  _impl_.name_.ClearToEmpty();
+}
+inline const std::string& Enum::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Enum.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Enum::set_name(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Enum.name)
+}
+inline std::string* Enum::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Enum.name)
+  return _s;
+}
+inline const std::string& Enum::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void Enum::_internal_set_name(const std::string& value) {
+  
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Enum::_internal_mutable_name() {
+  
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Enum::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Enum.name)
+  return _impl_.name_.Release();
+}
+inline void Enum::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Enum.name)
+}
+
+// repeated .google.protobuf.EnumValue enumvalue = 2;
+inline int Enum::_internal_enumvalue_size() const {
+  return _impl_.enumvalue_.size();
+}
+inline int Enum::enumvalue_size() const {
+  return _internal_enumvalue_size();
+}
+inline void Enum::clear_enumvalue() {
+  _impl_.enumvalue_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumValue* Enum::mutable_enumvalue(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Enum.enumvalue)
+  return _impl_.enumvalue_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValue >*
+Enum::mutable_enumvalue() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.Enum.enumvalue)
+  return &_impl_.enumvalue_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumValue& Enum::_internal_enumvalue(int index) const {
+  return _impl_.enumvalue_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::EnumValue& Enum::enumvalue(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Enum.enumvalue)
+  return _internal_enumvalue(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumValue* Enum::_internal_add_enumvalue() {
+  return _impl_.enumvalue_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::EnumValue* Enum::add_enumvalue() {
+  ::PROTOBUF_NAMESPACE_ID::EnumValue* _add = _internal_add_enumvalue();
+  // @@protoc_insertion_point(field_add:google.protobuf.Enum.enumvalue)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValue >&
+Enum::enumvalue() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.Enum.enumvalue)
+  return _impl_.enumvalue_;
+}
+
+// repeated .google.protobuf.Option options = 3;
+inline int Enum::_internal_options_size() const {
+  return _impl_.options_.size();
+}
+inline int Enum::options_size() const {
+  return _internal_options_size();
+}
+inline void Enum::clear_options() {
+  _impl_.options_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Enum::mutable_options(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Enum.options)
+  return _impl_.options_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+Enum::mutable_options() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.Enum.options)
+  return &_impl_.options_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& Enum::_internal_options(int index) const {
+  return _impl_.options_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& Enum::options(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Enum.options)
+  return _internal_options(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Enum::_internal_add_options() {
+  return _impl_.options_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* Enum::add_options() {
+  ::PROTOBUF_NAMESPACE_ID::Option* _add = _internal_add_options();
+  // @@protoc_insertion_point(field_add:google.protobuf.Enum.options)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+Enum::options() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.Enum.options)
+  return _impl_.options_;
+}
+
+// .google.protobuf.SourceContext source_context = 4;
+inline bool Enum::_internal_has_source_context() const {
+  return this != internal_default_instance() && _impl_.source_context_ != nullptr;
+}
+inline bool Enum::has_source_context() const {
+  return _internal_has_source_context();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::SourceContext& Enum::_internal_source_context() const {
+  const ::PROTOBUF_NAMESPACE_ID::SourceContext* p = _impl_.source_context_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::SourceContext&>(
+      ::PROTOBUF_NAMESPACE_ID::_SourceContext_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::SourceContext& Enum::source_context() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Enum.source_context)
+  return _internal_source_context();
+}
+inline void Enum::unsafe_arena_set_allocated_source_context(
+    ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.source_context_);
+  }
+  _impl_.source_context_ = source_context;
+  if (source_context) {
+    
+  } else {
+    
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.Enum.source_context)
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Enum::release_source_context() {
+  
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* temp = _impl_.source_context_;
+  _impl_.source_context_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Enum::unsafe_arena_release_source_context() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Enum.source_context)
+  
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* temp = _impl_.source_context_;
+  _impl_.source_context_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Enum::_internal_mutable_source_context() {
+  
+  if (_impl_.source_context_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::SourceContext>(GetArenaForAllocation());
+    _impl_.source_context_ = p;
+  }
+  return _impl_.source_context_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::SourceContext* Enum::mutable_source_context() {
+  ::PROTOBUF_NAMESPACE_ID::SourceContext* _msg = _internal_mutable_source_context();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Enum.source_context)
+  return _msg;
+}
+inline void Enum::set_allocated_source_context(::PROTOBUF_NAMESPACE_ID::SourceContext* source_context) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.source_context_);
+  }
+  if (source_context) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(
+                reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(source_context));
+    if (message_arena != submessage_arena) {
+      source_context = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, source_context, submessage_arena);
+    }
+    
+  } else {
+    
+  }
+  _impl_.source_context_ = source_context;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Enum.source_context)
+}
+
+// .google.protobuf.Syntax syntax = 5;
+inline void Enum::clear_syntax() {
+  _impl_.syntax_ = 0;
+}
+inline ::PROTOBUF_NAMESPACE_ID::Syntax Enum::_internal_syntax() const {
+  return static_cast< ::PROTOBUF_NAMESPACE_ID::Syntax >(_impl_.syntax_);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Syntax Enum::syntax() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Enum.syntax)
+  return _internal_syntax();
+}
+inline void Enum::_internal_set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value) {
+  
+  _impl_.syntax_ = value;
+}
+inline void Enum::set_syntax(::PROTOBUF_NAMESPACE_ID::Syntax value) {
+  _internal_set_syntax(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Enum.syntax)
+}
+
+// -------------------------------------------------------------------
+
+// EnumValue
+
+// string name = 1;
+inline void EnumValue::clear_name() {
+  _impl_.name_.ClearToEmpty();
+}
+inline const std::string& EnumValue::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumValue.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void EnumValue::set_name(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumValue.name)
+}
+inline std::string* EnumValue::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumValue.name)
+  return _s;
+}
+inline const std::string& EnumValue::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void EnumValue::_internal_set_name(const std::string& value) {
+  
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* EnumValue::_internal_mutable_name() {
+  
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* EnumValue::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.EnumValue.name)
+  return _impl_.name_.Release();
+}
+inline void EnumValue::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.EnumValue.name)
+}
+
+// int32 number = 2;
+inline void EnumValue::clear_number() {
+  _impl_.number_ = 0;
+}
+inline int32_t EnumValue::_internal_number() const {
+  return _impl_.number_;
+}
+inline int32_t EnumValue::number() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumValue.number)
+  return _internal_number();
+}
+inline void EnumValue::_internal_set_number(int32_t value) {
+  
+  _impl_.number_ = value;
+}
+inline void EnumValue::set_number(int32_t value) {
+  _internal_set_number(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.EnumValue.number)
+}
+
+// repeated .google.protobuf.Option options = 3;
+inline int EnumValue::_internal_options_size() const {
+  return _impl_.options_.size();
+}
+inline int EnumValue::options_size() const {
+  return _internal_options_size();
+}
+inline void EnumValue::clear_options() {
+  _impl_.options_.Clear();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* EnumValue::mutable_options(int index) {
+  // @@protoc_insertion_point(field_mutable:google.protobuf.EnumValue.options)
+  return _impl_.options_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >*
+EnumValue::mutable_options() {
+  // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumValue.options)
+  return &_impl_.options_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& EnumValue::_internal_options(int index) const {
+  return _impl_.options_.Get(index);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Option& EnumValue::options(int index) const {
+  // @@protoc_insertion_point(field_get:google.protobuf.EnumValue.options)
+  return _internal_options(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* EnumValue::_internal_add_options() {
+  return _impl_.options_.Add();
+}
+inline ::PROTOBUF_NAMESPACE_ID::Option* EnumValue::add_options() {
+  ::PROTOBUF_NAMESPACE_ID::Option* _add = _internal_add_options();
+  // @@protoc_insertion_point(field_add:google.protobuf.EnumValue.options)
+  return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option >&
+EnumValue::options() const {
+  // @@protoc_insertion_point(field_list:google.protobuf.EnumValue.options)
+  return _impl_.options_;
+}
+
+// -------------------------------------------------------------------
+
+// Option
+
+// string name = 1;
+inline void Option::clear_name() {
+  _impl_.name_.ClearToEmpty();
+}
+inline const std::string& Option::name() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Option.name)
+  return _internal_name();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Option::set_name(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.name_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Option.name)
+}
+inline std::string* Option::mutable_name() {
+  std::string* _s = _internal_mutable_name();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Option.name)
+  return _s;
+}
+inline const std::string& Option::_internal_name() const {
+  return _impl_.name_.Get();
+}
+inline void Option::_internal_set_name(const std::string& value) {
+  
+  _impl_.name_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Option::_internal_mutable_name() {
+  
+  return _impl_.name_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Option::release_name() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Option.name)
+  return _impl_.name_.Release();
+}
+inline void Option::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.name_.SetAllocated(name, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.name_.IsDefault()) {
+    _impl_.name_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Option.name)
+}
+
+// .google.protobuf.Any value = 2;
+inline bool Option::_internal_has_value() const {
+  return this != internal_default_instance() && _impl_.value_ != nullptr;
+}
+inline bool Option::has_value() const {
+  return _internal_has_value();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Any& Option::_internal_value() const {
+  const ::PROTOBUF_NAMESPACE_ID::Any* p = _impl_.value_;
+  return p != nullptr ? *p : reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Any&>(
+      ::PROTOBUF_NAMESPACE_ID::_Any_default_instance_);
+}
+inline const ::PROTOBUF_NAMESPACE_ID::Any& Option::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Option.value)
+  return _internal_value();
+}
+inline void Option::unsafe_arena_set_allocated_value(
+    ::PROTOBUF_NAMESPACE_ID::Any* value) {
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.value_);
+  }
+  _impl_.value_ = value;
+  if (value) {
+    
+  } else {
+    
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.Option.value)
+}
+inline ::PROTOBUF_NAMESPACE_ID::Any* Option::release_value() {
+  
+  ::PROTOBUF_NAMESPACE_ID::Any* temp = _impl_.value_;
+  _impl_.value_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old =  reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+  temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  if (GetArenaForAllocation() == nullptr) { delete old; }
+#else  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::Any* Option::unsafe_arena_release_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Option.value)
+  
+  ::PROTOBUF_NAMESPACE_ID::Any* temp = _impl_.value_;
+  _impl_.value_ = nullptr;
+  return temp;
+}
+inline ::PROTOBUF_NAMESPACE_ID::Any* Option::_internal_mutable_value() {
+  
+  if (_impl_.value_ == nullptr) {
+    auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Any>(GetArenaForAllocation());
+    _impl_.value_ = p;
+  }
+  return _impl_.value_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::Any* Option::mutable_value() {
+  ::PROTOBUF_NAMESPACE_ID::Any* _msg = _internal_mutable_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Option.value)
+  return _msg;
+}
+inline void Option::set_allocated_value(::PROTOBUF_NAMESPACE_ID::Any* value) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  if (message_arena == nullptr) {
+    delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.value_);
+  }
+  if (value) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+        ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(
+                reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(value));
+    if (message_arena != submessage_arena) {
+      value = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, value, submessage_arena);
+    }
+    
+  } else {
+    
+  }
+  _impl_.value_ = value;
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Option.value)
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+PROTOBUF_NAMESPACE_OPEN
+
+template <> struct is_proto_enum< ::PROTOBUF_NAMESPACE_ID::Field_Kind> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::PROTOBUF_NAMESPACE_ID::Field_Kind>() {
+  return ::PROTOBUF_NAMESPACE_ID::Field_Kind_descriptor();
+}
+template <> struct is_proto_enum< ::PROTOBUF_NAMESPACE_ID::Field_Cardinality> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::PROTOBUF_NAMESPACE_ID::Field_Cardinality>() {
+  return ::PROTOBUF_NAMESPACE_ID::Field_Cardinality_descriptor();
+}
+template <> struct is_proto_enum< ::PROTOBUF_NAMESPACE_ID::Syntax> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::PROTOBUF_NAMESPACE_ID::Syntax>() {
+  return ::PROTOBUF_NAMESPACE_ID::Syntax_descriptor();
+}
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2ftype_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/unknown_field_set.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/unknown_field_set.h
new file mode 100644
index 0000000..9aa2cbb
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/unknown_field_set.h
@@ -0,0 +1,407 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Contains classes used to keep track of unrecognized fields seen while
+// parsing a protocol message.
+
+#ifndef GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__
+#define GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__
+
+
+#include <assert.h>
+
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/parse_context.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+namespace google {
+namespace protobuf {
+namespace internal {
+class InternalMetadata;           // metadata_lite.h
+class WireFormat;                 // wire_format.h
+class MessageSetFieldSkipperUsingCord;
+// extension_set_heavy.cc
+}  // namespace internal
+
+class Message;       // message.h
+class UnknownField;  // below
+
+// An UnknownFieldSet contains fields that were encountered while parsing a
+// message but were not defined by its type.  Keeping track of these can be
+// useful, especially in that they may be written if the message is serialized
+// again without being cleared in between.  This means that software which
+// simply receives messages and forwards them to other servers does not need
+// to be updated every time a new field is added to the message definition.
+//
+// To get the UnknownFieldSet attached to any message, call
+// Reflection::GetUnknownFields().
+//
+// This class is necessarily tied to the protocol buffer wire format, unlike
+// the Reflection interface which is independent of any serialization scheme.
+class PROTOBUF_EXPORT UnknownFieldSet {
+ public:
+  UnknownFieldSet();
+  ~UnknownFieldSet();
+
+  // Remove all fields.
+  inline void Clear();
+
+  // Remove all fields and deallocate internal data objects
+  void ClearAndFreeMemory();
+
+  // Is this set empty?
+  inline bool empty() const;
+
+  // Merge the contents of some other UnknownFieldSet with this one.
+  void MergeFrom(const UnknownFieldSet& other);
+
+  // Similar to above, but this function will destroy the contents of other.
+  void MergeFromAndDestroy(UnknownFieldSet* other);
+
+  // Merge the contents an UnknownFieldSet with the UnknownFieldSet in
+  // *metadata, if there is one.  If *metadata doesn't have an UnknownFieldSet
+  // then add one to it and make it be a copy of the first arg.
+  static void MergeToInternalMetadata(const UnknownFieldSet& other,
+                                      internal::InternalMetadata* metadata);
+
+  // Swaps the contents of some other UnknownFieldSet with this one.
+  inline void Swap(UnknownFieldSet* x);
+
+  // Computes (an estimate of) the total number of bytes currently used for
+  // storing the unknown fields in memory. Does NOT include
+  // sizeof(*this) in the calculation.
+  size_t SpaceUsedExcludingSelfLong() const;
+
+  int SpaceUsedExcludingSelf() const {
+    return internal::ToIntSize(SpaceUsedExcludingSelfLong());
+  }
+
+  // Version of SpaceUsed() including sizeof(*this).
+  size_t SpaceUsedLong() const;
+
+  int SpaceUsed() const { return internal::ToIntSize(SpaceUsedLong()); }
+
+  // Returns the number of fields present in the UnknownFieldSet.
+  inline int field_count() const;
+  // Get a field in the set, where 0 <= index < field_count().  The fields
+  // appear in the order in which they were added.
+  inline const UnknownField& field(int index) const;
+  // Get a mutable pointer to a field in the set, where
+  // 0 <= index < field_count().  The fields appear in the order in which
+  // they were added.
+  inline UnknownField* mutable_field(int index);
+
+  // Adding fields ---------------------------------------------------
+
+  void AddVarint(int number, uint64_t value);
+  void AddFixed32(int number, uint32_t value);
+  void AddFixed64(int number, uint64_t value);
+  void AddLengthDelimited(int number, const std::string& value);
+  std::string* AddLengthDelimited(int number);
+  UnknownFieldSet* AddGroup(int number);
+
+  // Adds an unknown field from another set.
+  void AddField(const UnknownField& field);
+
+  // Delete fields with indices in the range [start .. start+num-1].
+  // Caution: implementation moves all fields with indices [start+num .. ].
+  void DeleteSubrange(int start, int num);
+
+  // Delete all fields with a specific field number. The order of left fields
+  // is preserved.
+  // Caution: implementation moves all fields after the first deleted field.
+  void DeleteByNumber(int number);
+
+  // Parsing helpers -------------------------------------------------
+  // These work exactly like the similarly-named methods of Message.
+
+  bool MergeFromCodedStream(io::CodedInputStream* input);
+  bool ParseFromCodedStream(io::CodedInputStream* input);
+  bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input);
+  bool ParseFromArray(const void* data, int size);
+  inline bool ParseFromString(const std::string& data) {
+    return ParseFromArray(data.data(), static_cast<int>(data.size()));
+  }
+
+  // Merges this message's unknown field data (if any).  This works whether
+  // the message is a lite or full proto (for legacy reasons, lite and full
+  // return different types for MessageType::unknown_fields()).
+  template <typename MessageType>
+  bool MergeFromMessage(const MessageType& message);
+
+  // Serialization.
+  bool SerializeToString(std::string* output) const;
+  bool SerializeToCodedStream(io::CodedOutputStream* output) const;
+  static const UnknownFieldSet& default_instance();
+
+ private:
+  // For InternalMergeFrom
+  friend class UnknownField;
+  // Merges from other UnknownFieldSet. This method assumes, that this object
+  // is newly created and has no fields.
+  void InternalMergeFrom(const UnknownFieldSet& other);
+  void ClearFallback();
+
+  template <typename MessageType,
+            typename std::enable_if<
+                std::is_base_of<Message, MessageType>::value, int>::type = 0>
+  bool InternalMergeFromMessage(const MessageType& message) {
+    MergeFrom(message.GetReflection()->GetUnknownFields(message));
+    return true;
+  }
+
+  template <typename MessageType,
+            typename std::enable_if<
+                std::is_base_of<MessageLite, MessageType>::value &&
+                    !std::is_base_of<Message, MessageType>::value,
+                int>::type = 0>
+  bool InternalMergeFromMessage(const MessageType& message) {
+    const auto& unknown_fields = message.unknown_fields();
+    io::ArrayInputStream array_stream(unknown_fields.data(),
+                                      unknown_fields.size());
+    io::CodedInputStream coded_stream(&array_stream);
+    return MergeFromCodedStream(&coded_stream);
+  }
+
+  std::vector<UnknownField> fields_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UnknownFieldSet);
+};
+
+namespace internal {
+
+inline void WriteVarint(uint32_t num, uint64_t val, UnknownFieldSet* unknown) {
+  unknown->AddVarint(num, val);
+}
+inline void WriteLengthDelimited(uint32_t num, StringPiece val,
+                                 UnknownFieldSet* unknown) {
+  unknown->AddLengthDelimited(num)->assign(val.data(), val.size());
+}
+
+PROTOBUF_EXPORT
+const char* UnknownGroupParse(UnknownFieldSet* unknown, const char* ptr,
+                              ParseContext* ctx);
+PROTOBUF_EXPORT
+const char* UnknownFieldParse(uint64_t tag, UnknownFieldSet* unknown,
+                              const char* ptr, ParseContext* ctx);
+
+}  // namespace internal
+
+// Represents one field in an UnknownFieldSet.
+class PROTOBUF_EXPORT UnknownField {
+ public:
+  enum Type {
+    TYPE_VARINT,
+    TYPE_FIXED32,
+    TYPE_FIXED64,
+    TYPE_LENGTH_DELIMITED,
+    TYPE_GROUP
+  };
+
+  // The field's field number, as seen on the wire.
+  inline int number() const;
+
+  // The field type.
+  inline Type type() const;
+
+  // Accessors -------------------------------------------------------
+  // Each method works only for UnknownFields of the corresponding type.
+
+  inline uint64_t varint() const;
+  inline uint32_t fixed32() const;
+  inline uint64_t fixed64() const;
+  inline const std::string& length_delimited() const;
+  inline const UnknownFieldSet& group() const;
+
+  inline void set_varint(uint64_t value);
+  inline void set_fixed32(uint32_t value);
+  inline void set_fixed64(uint64_t value);
+  inline void set_length_delimited(const std::string& value);
+  inline std::string* mutable_length_delimited();
+  inline UnknownFieldSet* mutable_group();
+
+  inline size_t GetLengthDelimitedSize() const;
+  uint8_t* InternalSerializeLengthDelimitedNoTag(
+      uint8_t* target, io::EpsCopyOutputStream* stream) const;
+
+
+  // If this UnknownField contains a pointer, delete it.
+  void Delete();
+
+  // Make a deep copy of any pointers in this UnknownField.
+  void DeepCopy(const UnknownField& other);
+
+  // Set the wire type of this UnknownField. Should only be used when this
+  // UnknownField is being created.
+  inline void SetType(Type type);
+
+  union LengthDelimited {
+    std::string* string_value;
+  };
+
+  uint32_t number_;
+  uint32_t type_;
+  union {
+    uint64_t varint_;
+    uint32_t fixed32_;
+    uint64_t fixed64_;
+    mutable union LengthDelimited length_delimited_;
+    UnknownFieldSet* group_;
+  } data_;
+};
+
+// ===================================================================
+// inline implementations
+
+inline UnknownFieldSet::UnknownFieldSet() {}
+
+inline UnknownFieldSet::~UnknownFieldSet() { Clear(); }
+
+inline void UnknownFieldSet::ClearAndFreeMemory() { Clear(); }
+
+inline void UnknownFieldSet::Clear() {
+  if (!fields_.empty()) {
+    ClearFallback();
+  }
+}
+
+inline bool UnknownFieldSet::empty() const { return fields_.empty(); }
+
+inline void UnknownFieldSet::Swap(UnknownFieldSet* x) {
+  fields_.swap(x->fields_);
+}
+
+inline int UnknownFieldSet::field_count() const {
+  return static_cast<int>(fields_.size());
+}
+inline const UnknownField& UnknownFieldSet::field(int index) const {
+  return (fields_)[static_cast<size_t>(index)];
+}
+inline UnknownField* UnknownFieldSet::mutable_field(int index) {
+  return &(fields_)[static_cast<size_t>(index)];
+}
+
+inline void UnknownFieldSet::AddLengthDelimited(int number,
+                                                const std::string& value) {
+  AddLengthDelimited(number)->assign(value);
+}
+
+
+
+
+inline int UnknownField::number() const { return static_cast<int>(number_); }
+inline UnknownField::Type UnknownField::type() const {
+  return static_cast<Type>(type_);
+}
+
+inline uint64_t UnknownField::varint() const {
+  assert(type() == TYPE_VARINT);
+  return data_.varint_;
+}
+inline uint32_t UnknownField::fixed32() const {
+  assert(type() == TYPE_FIXED32);
+  return data_.fixed32_;
+}
+inline uint64_t UnknownField::fixed64() const {
+  assert(type() == TYPE_FIXED64);
+  return data_.fixed64_;
+}
+inline const std::string& UnknownField::length_delimited() const {
+  assert(type() == TYPE_LENGTH_DELIMITED);
+  return *data_.length_delimited_.string_value;
+}
+inline const UnknownFieldSet& UnknownField::group() const {
+  assert(type() == TYPE_GROUP);
+  return *data_.group_;
+}
+
+inline void UnknownField::set_varint(uint64_t value) {
+  assert(type() == TYPE_VARINT);
+  data_.varint_ = value;
+}
+inline void UnknownField::set_fixed32(uint32_t value) {
+  assert(type() == TYPE_FIXED32);
+  data_.fixed32_ = value;
+}
+inline void UnknownField::set_fixed64(uint64_t value) {
+  assert(type() == TYPE_FIXED64);
+  data_.fixed64_ = value;
+}
+inline void UnknownField::set_length_delimited(const std::string& value) {
+  assert(type() == TYPE_LENGTH_DELIMITED);
+  data_.length_delimited_.string_value->assign(value);
+}
+inline std::string* UnknownField::mutable_length_delimited() {
+  assert(type() == TYPE_LENGTH_DELIMITED);
+  return data_.length_delimited_.string_value;
+}
+inline UnknownFieldSet* UnknownField::mutable_group() {
+  assert(type() == TYPE_GROUP);
+  return data_.group_;
+}
+template <typename MessageType>
+bool UnknownFieldSet::MergeFromMessage(const MessageType& message) {
+  // SFINAE will route to the right version.
+  return InternalMergeFromMessage(message);
+}
+
+
+inline size_t UnknownField::GetLengthDelimitedSize() const {
+  GOOGLE_DCHECK_EQ(TYPE_LENGTH_DELIMITED, type());
+  return data_.length_delimited_.string_value->size();
+}
+
+inline void UnknownField::SetType(Type type) {
+  type_ = type;
+}
+
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/delimited_message_util.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/delimited_message_util.h
new file mode 100644
index 0000000..78625cf
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/delimited_message_util.h
@@ -0,0 +1,109 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Adapted from the patch of kenton@google.com (Kenton Varda)
+// See https://github.com/protocolbuffers/protobuf/pull/710 for details.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_DELIMITED_MESSAGE_UTIL_H__
+#define GOOGLE_PROTOBUF_UTIL_DELIMITED_MESSAGE_UTIL_H__
+
+
+#include <ostream>
+
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+// Write a single size-delimited message from the given stream. Delimited
+// format allows a single file or stream to contain multiple messages,
+// whereas normally writing multiple non-delimited messages to the same
+// stream would cause them to be merged. A delimited message is a varint
+// encoding the message size followed by a message of exactly that size.
+//
+// Note that if you want to *read* a delimited message from a file descriptor
+// or istream, you will need to construct an io::FileInputStream or
+// io::OstreamInputStream (implementations of io::ZeroCopyStream) and use the
+// utility function ParseDelimitedFromZeroCopyStream(). You must then
+// continue to use the same ZeroCopyInputStream to read all further data from
+// the stream until EOF. This is because these ZeroCopyInputStream
+// implementations are buffered: they read a big chunk of data at a time,
+// then parse it. As a result, they may read past the end of the delimited
+// message. There is no way for them to push the extra data back into the
+// underlying source, so instead you must keep using the same stream object.
+bool PROTOBUF_EXPORT SerializeDelimitedToFileDescriptor(
+    const MessageLite& message, int file_descriptor);
+
+bool PROTOBUF_EXPORT SerializeDelimitedToOstream(const MessageLite& message,
+                                                 std::ostream* output);
+
+// Read a single size-delimited message from the given stream. Delimited
+// format allows a single file or stream to contain multiple messages,
+// whereas normally parsing consumes the entire input. A delimited message
+// is a varint encoding the message size followed by a message of exactly
+// that size.
+//
+// If |clean_eof| is not NULL, then it will be set to indicate whether the
+// stream ended cleanly. That is, if the stream ends without this method
+// having read any data at all from it, then *clean_eof will be set true,
+// otherwise it will be set false. Note that these methods return false
+// on EOF, but they also return false on other errors, so |clean_eof| is
+// needed to distinguish a clean end from errors.
+bool PROTOBUF_EXPORT ParseDelimitedFromZeroCopyStream(
+    MessageLite* message, io::ZeroCopyInputStream* input, bool* clean_eof);
+
+bool PROTOBUF_EXPORT ParseDelimitedFromCodedStream(MessageLite* message,
+                                                   io::CodedInputStream* input,
+                                                   bool* clean_eof);
+
+// Write a single size-delimited message from the given stream. Delimited
+// format allows a single file or stream to contain multiple messages,
+// whereas normally writing multiple non-delimited messages to the same
+// stream would cause them to be merged. A delimited message is a varint
+// encoding the message size followed by a message of exactly that size.
+bool PROTOBUF_EXPORT SerializeDelimitedToZeroCopyStream(
+    const MessageLite& message, io::ZeroCopyOutputStream* output);
+
+bool PROTOBUF_EXPORT SerializeDelimitedToCodedStream(
+    const MessageLite& message, io::CodedOutputStream* output);
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_DELIMITED_MESSAGE_UTIL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/field_comparator.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/field_comparator.h
new file mode 100644
index 0000000..5706da3
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/field_comparator.h
@@ -0,0 +1,288 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Defines classes for field comparison.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__
+#define GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__
+
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+class Message;
+class EnumValueDescriptor;
+class FieldDescriptor;
+
+namespace util {
+
+class FieldContext;
+class MessageDifferencer;
+
+// Base class specifying the interface for comparing protocol buffer fields.
+// Regular users should consider using or subclassing DefaultFieldComparator
+// rather than this interface.
+// Currently, this does not support comparing unknown fields.
+class PROTOBUF_EXPORT FieldComparator {
+ public:
+  FieldComparator();
+  virtual ~FieldComparator();
+
+  enum ComparisonResult {
+    SAME,       // Compared fields are equal. In case of comparing submessages,
+                // user should not recursively compare their contents.
+    DIFFERENT,  // Compared fields are different. In case of comparing
+                // submessages, user should not recursively compare their
+                // contents.
+    RECURSE,    // Compared submessages need to be compared recursively.
+                // FieldComparator does not specify the semantics of recursive
+                // comparison. This value should not be returned for simple
+                // values.
+  };
+
+  // Compares the values of a field in two protocol buffer messages.
+  // Returns SAME or DIFFERENT for simple values, and SAME, DIFFERENT or RECURSE
+  // for submessages. Returning RECURSE for fields not being submessages is
+  // illegal.
+  // In case the given FieldDescriptor points to a repeated field, the indices
+  // need to be valid. Otherwise they should be ignored.
+  //
+  // FieldContext contains information about the specific instances of the
+  // fields being compared, versus FieldDescriptor which only contains general
+  // type information about the fields.
+  virtual ComparisonResult Compare(const Message& message_1,
+                                   const Message& message_2,
+                                   const FieldDescriptor* field, int index_1,
+                                   int index_2,
+                                   const util::FieldContext* field_context) = 0;
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldComparator);
+};
+
+// Basic implementation of FieldComparator.  Supports three modes of floating
+// point value comparison: exact, approximate using MathUtil::AlmostEqual
+// method, and arbitrarily precise using MathUtil::WithinFractionOrMargin.
+class PROTOBUF_EXPORT SimpleFieldComparator : public FieldComparator {
+ public:
+  enum FloatComparison {
+    EXACT,        // Floats and doubles are compared exactly.
+    APPROXIMATE,  // Floats and doubles are compared using the
+                  // MathUtil::AlmostEqual method or
+                  // MathUtil::WithinFractionOrMargin method.
+    // TODO(ksroka): Introduce third value to differentiate uses of AlmostEqual
+    //               and WithinFractionOrMargin.
+  };
+
+  // Creates new comparator with float comparison set to EXACT.
+  SimpleFieldComparator();
+
+  ~SimpleFieldComparator() override;
+
+  void set_float_comparison(FloatComparison float_comparison) {
+    float_comparison_ = float_comparison;
+  }
+
+  FloatComparison float_comparison() const { return float_comparison_; }
+
+  // Set whether the FieldComparator shall treat floats or doubles that are both
+  // NaN as equal (treat_nan_as_equal = true) or as different
+  // (treat_nan_as_equal = false). Default is treating NaNs always as different.
+  void set_treat_nan_as_equal(bool treat_nan_as_equal) {
+    treat_nan_as_equal_ = treat_nan_as_equal;
+  }
+
+  bool treat_nan_as_equal() const { return treat_nan_as_equal_; }
+
+  // Sets the fraction and margin for the float comparison of a given field.
+  // Uses MathUtil::WithinFractionOrMargin to compare the values.
+  //
+  // REQUIRES: field->cpp_type == FieldDescriptor::CPPTYPE_DOUBLE or
+  //           field->cpp_type == FieldDescriptor::CPPTYPE_FLOAT
+  // REQUIRES: float_comparison_ == APPROXIMATE
+  void SetFractionAndMargin(const FieldDescriptor* field, double fraction,
+                            double margin);
+
+  // Sets the fraction and margin for the float comparison of all float and
+  // double fields, unless a field has been given a specific setting via
+  // SetFractionAndMargin() above.
+  // Uses MathUtil::WithinFractionOrMargin to compare the values.
+  //
+  // REQUIRES: float_comparison_ == APPROXIMATE
+  void SetDefaultFractionAndMargin(double fraction, double margin);
+
+ protected:
+  // Returns the comparison result for the given field in two messages.
+  //
+  // This function is called directly by DefaultFieldComparator::Compare.
+  // Subclasses can call this function to compare fields they do not need to
+  // handle specially.
+  ComparisonResult SimpleCompare(const Message& message_1,
+                                 const Message& message_2,
+                                 const FieldDescriptor* field, int index_1,
+                                 int index_2,
+                                 const util::FieldContext* field_context);
+
+  // Compare using the provided message_differencer. For example, a subclass can
+  // use this method to compare some field in a certain way using the same
+  // message_differencer instance and the field context.
+  bool CompareWithDifferencer(MessageDifferencer* differencer,
+                              const Message& message1, const Message& message2,
+                              const util::FieldContext* field_context);
+
+  // Returns FieldComparator::SAME if boolean_result is true and
+  // FieldComparator::DIFFERENT otherwise.
+  ComparisonResult ResultFromBoolean(bool boolean_result) const;
+
+ private:
+  // Defines the tolerance for floating point comparison (fraction and margin).
+  struct Tolerance {
+    double fraction;
+    double margin;
+    Tolerance() : fraction(0.0), margin(0.0) {}
+    Tolerance(double f, double m) : fraction(f), margin(m) {}
+  };
+
+  // Defines the map to store the tolerances for floating point comparison.
+  typedef std::map<const FieldDescriptor*, Tolerance> ToleranceMap;
+
+  friend class MessageDifferencer;
+  // The following methods get executed when CompareFields is called for the
+  // basic types (instead of submessages). They return true on success. One
+  // can use ResultFromBoolean() to convert that boolean to a ComparisonResult
+  // value.
+  bool CompareBool(const FieldDescriptor& /* unused */, bool value_1,
+                   bool value_2) {
+    return value_1 == value_2;
+  }
+
+  // Uses CompareDoubleOrFloat, a helper function used by both CompareDouble and
+  // CompareFloat.
+  bool CompareDouble(const FieldDescriptor& field, double value_1,
+                     double value_2);
+
+  bool CompareEnum(const FieldDescriptor& field,
+                   const EnumValueDescriptor* value_1,
+                   const EnumValueDescriptor* value_2);
+
+  // Uses CompareDoubleOrFloat, a helper function used by both CompareDouble and
+  // CompareFloat.
+  bool CompareFloat(const FieldDescriptor& field, float value_1, float value_2);
+
+  bool CompareInt32(const FieldDescriptor& /* unused */, int32_t value_1,
+                    int32_t value_2) {
+    return value_1 == value_2;
+  }
+
+  bool CompareInt64(const FieldDescriptor& /* unused */, int64_t value_1,
+                    int64_t value_2) {
+    return value_1 == value_2;
+  }
+
+  bool CompareString(const FieldDescriptor& /* unused */,
+                     const std::string& value_1, const std::string& value_2) {
+    return value_1 == value_2;
+  }
+
+  bool CompareUInt32(const FieldDescriptor& /* unused */, uint32_t value_1,
+                     uint32_t value_2) {
+    return value_1 == value_2;
+  }
+
+  bool CompareUInt64(const FieldDescriptor& /* unused */, uint64_t value_1,
+                     uint64_t value_2) {
+    return value_1 == value_2;
+  }
+
+  // This function is used by CompareDouble and CompareFloat to avoid code
+  // duplication. There are no checks done against types of the values passed,
+  // but it's likely to fail if passed non-numeric arguments.
+  template <typename T>
+  bool CompareDoubleOrFloat(const FieldDescriptor& field, T value_1, T value_2);
+
+  FloatComparison float_comparison_;
+
+  // If true, floats and doubles that are both NaN are considered to be
+  // equal. Otherwise, two floats or doubles that are NaN are considered to be
+  // different.
+  bool treat_nan_as_equal_;
+
+  // True iff default_tolerance_ has been explicitly set.
+  //
+  // If false, then the default tolerance for floats and doubles is that which
+  // is used by MathUtil::AlmostEquals().
+  bool has_default_tolerance_;
+
+  // Default float/double tolerance. Only meaningful if
+  // has_default_tolerance_ == true.
+  Tolerance default_tolerance_;
+
+  // Field-specific float/double tolerances, which override any default for
+  // those particular fields.
+  ToleranceMap map_tolerance_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SimpleFieldComparator);
+};
+
+// Default field comparison: use the basic implementation of FieldComparator.
+#ifdef PROTOBUF_FUTURE_BREAKING_CHANGES
+class PROTOBUF_EXPORT DefaultFieldComparator final
+    : public SimpleFieldComparator
+#else   // PROTOBUF_FUTURE_BREAKING_CHANGES
+class PROTOBUF_EXPORT DefaultFieldComparator : public SimpleFieldComparator
+#endif  // PROTOBUF_FUTURE_BREAKING_CHANGES
+{
+ public:
+  ComparisonResult Compare(const Message& message_1, const Message& message_2,
+                           const FieldDescriptor* field, int index_1,
+                           int index_2,
+                           const util::FieldContext* field_context) override {
+    return SimpleCompare(message_1, message_2, field, index_1, index_2,
+                         field_context);
+  }
+};
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/field_mask_util.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/field_mask_util.h
new file mode 100644
index 0000000..d51a332
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/field_mask_util.h
@@ -0,0 +1,263 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Defines utilities for the FieldMask well known type.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_FIELD_MASK_UTIL_H__
+#define GOOGLE_PROTOBUF_UTIL_FIELD_MASK_UTIL_H__
+
+#include <cstdint>
+#include <string>
+
+#include <google/protobuf/field_mask.pb.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/descriptor.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+class PROTOBUF_EXPORT FieldMaskUtil {
+  typedef google::protobuf::FieldMask FieldMask;
+
+ public:
+  // Converts FieldMask to/from string, formatted by separating each path
+  // with a comma (e.g., "foo_bar,baz.quz").
+  static std::string ToString(const FieldMask& mask);
+  static void FromString(StringPiece str, FieldMask* out);
+
+  // Populates the FieldMask with the paths corresponding to the fields with the
+  // given numbers, after checking that all field numbers are valid.
+  template <typename T>
+  static void FromFieldNumbers(const std::vector<int64_t>& field_numbers,
+                               FieldMask* out) {
+    for (const auto field_number : field_numbers) {
+      const FieldDescriptor* field_desc =
+          T::descriptor()->FindFieldByNumber(field_number);
+      GOOGLE_CHECK(field_desc != nullptr)
+          << "Invalid field number for " << T::descriptor()->full_name() << ": "
+          << field_number;
+      AddPathToFieldMask<T>(field_desc->lowercase_name(), out);
+    }
+  }
+
+  // Converts FieldMask to/from string, formatted according to proto3 JSON
+  // spec for FieldMask (e.g., "fooBar,baz.quz"). If the field name is not
+  // style conforming (i.e., not snake_case when converted to string, or not
+  // camelCase when converted from string), the conversion will fail.
+  static bool ToJsonString(const FieldMask& mask, std::string* out);
+  static bool FromJsonString(StringPiece str, FieldMask* out);
+
+  // Get the descriptors of the fields which the given path from the message
+  // descriptor traverses, if field_descriptors is not null.
+  // Return false if the path is not valid, and the content of field_descriptors
+  // is unspecified.
+  static bool GetFieldDescriptors(
+      const Descriptor* descriptor, StringPiece path,
+      std::vector<const FieldDescriptor*>* field_descriptors);
+
+  // Checks whether the given path is valid for type T.
+  template <typename T>
+  static bool IsValidPath(StringPiece path) {
+    return GetFieldDescriptors(T::descriptor(), path, nullptr);
+  }
+
+  // Checks whether the given FieldMask is valid for type T.
+  template <typename T>
+  static bool IsValidFieldMask(const FieldMask& mask) {
+    for (int i = 0; i < mask.paths_size(); ++i) {
+      if (!GetFieldDescriptors(T::descriptor(), mask.paths(i), nullptr)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // Adds a path to FieldMask after checking whether the given path is valid.
+  // This method check-fails if the path is not a valid path for type T.
+  template <typename T>
+  static void AddPathToFieldMask(StringPiece path, FieldMask* mask) {
+    GOOGLE_CHECK(IsValidPath<T>(path)) << path;
+    mask->add_paths(std::string(path));
+  }
+
+  // Creates a FieldMask with all fields of type T. This FieldMask only
+  // contains fields of T but not any sub-message fields.
+  template <typename T>
+  static FieldMask GetFieldMaskForAllFields() {
+    FieldMask out;
+    GetFieldMaskForAllFields(T::descriptor(), &out);
+    return out;
+  }
+  template <typename T>
+  PROTOBUF_DEPRECATED_MSG("Use *out = GetFieldMaskForAllFields() instead")
+  static void GetFieldMaskForAllFields(FieldMask* out) {
+    GetFieldMaskForAllFields(T::descriptor(), out);
+  }
+  // This flavor takes the protobuf type descriptor as an argument.
+  // Useful when the type is not known at compile time.
+  static void GetFieldMaskForAllFields(const Descriptor* descriptor,
+                                       FieldMask* out);
+
+  // Converts a FieldMask to the canonical form. It will:
+  //   1. Remove paths that are covered by another path. For example,
+  //      "foo.bar" is covered by "foo" and will be removed if "foo"
+  //      is also in the FieldMask.
+  //   2. Sort all paths in alphabetical order.
+  static void ToCanonicalForm(const FieldMask& mask, FieldMask* out);
+
+  // Creates an union of two FieldMasks.
+  static void Union(const FieldMask& mask1, const FieldMask& mask2,
+                    FieldMask* out);
+
+  // Creates an intersection of two FieldMasks.
+  static void Intersect(const FieldMask& mask1, const FieldMask& mask2,
+                        FieldMask* out);
+
+  // Subtracts mask2 from mask1 base of type T.
+  template <typename T>
+  static void Subtract(const FieldMask& mask1, const FieldMask& mask2,
+                       FieldMask* out) {
+    Subtract(T::descriptor(), mask1, mask2, out);
+  }
+  // This flavor takes the protobuf type descriptor as an argument.
+  // Useful when the type is not known at compile time.
+  static void Subtract(const Descriptor* descriptor, const FieldMask& mask1,
+                       const FieldMask& mask2, FieldMask* out);
+
+  // Returns true if path is covered by the given FieldMask. Note that path
+  // "foo.bar" covers all paths like "foo.bar.baz", "foo.bar.quz.x", etc.
+  // Also note that parent paths are not covered by explicit child path, i.e.
+  // "foo.bar" does NOT cover "foo", even if "bar" is the only child.
+  static bool IsPathInFieldMask(StringPiece path, const FieldMask& mask);
+
+  class MergeOptions;
+  // Merges fields specified in a FieldMask into another message.
+  static void MergeMessageTo(const Message& source, const FieldMask& mask,
+                             const MergeOptions& options, Message* destination);
+
+  class TrimOptions;
+  // Removes from 'message' any field that is not represented in the given
+  // FieldMask. If the FieldMask is empty, does nothing.
+  // Returns true if the message is modified.
+  static bool TrimMessage(const FieldMask& mask, Message* message);
+
+  // Removes from 'message' any field that is not represented in the given
+  // FieldMask with customized TrimOptions.
+  // If the FieldMask is empty, does nothing.
+  // Returns true if the message is modified.
+  static bool TrimMessage(const FieldMask& mask, Message* message,
+                          const TrimOptions& options);
+
+ private:
+  friend class SnakeCaseCamelCaseTest;
+  // Converts a field name from snake_case to camelCase:
+  //   1. Every character after "_" will be converted to uppercase.
+  //   2. All "_"s are removed.
+  // The conversion will fail if:
+  //   1. The field name contains uppercase letters.
+  //   2. Any character after a "_" is not a lowercase letter.
+  // If the conversion succeeds, it's guaranteed that the resulted
+  // camelCase name will yield the original snake_case name when
+  // converted using CamelCaseToSnakeCase().
+  //
+  // Note that the input can contain characters not allowed in C identifiers.
+  // For example, "foo_bar,baz_quz" will be converted to "fooBar,bazQuz"
+  // successfully.
+  static bool SnakeCaseToCamelCase(StringPiece input,
+                                   std::string* output);
+  // Converts a field name from camelCase to snake_case:
+  //   1. Every uppercase letter is converted to lowercase with an additional
+  //      preceding "_".
+  // The conversion will fail if:
+  //   1. The field name contains "_"s.
+  // If the conversion succeeds, it's guaranteed that the resulted
+  // snake_case name will yield the original camelCase name when
+  // converted using SnakeCaseToCamelCase().
+  //
+  // Note that the input can contain characters not allowed in C identifiers.
+  // For example, "fooBar,bazQuz" will be converted to "foo_bar,baz_quz"
+  // successfully.
+  static bool CamelCaseToSnakeCase(StringPiece input,
+                                   std::string* output);
+};
+
+class PROTOBUF_EXPORT FieldMaskUtil::MergeOptions {
+ public:
+  MergeOptions()
+      : replace_message_fields_(false), replace_repeated_fields_(false) {}
+  // When merging message fields, the default behavior is to merge the
+  // content of two message fields together. If you instead want to use
+  // the field from the source message to replace the corresponding field
+  // in the destination message, set this flag to true. When this flag is set,
+  // specified submessage fields that are missing in source will be cleared in
+  // destination.
+  void set_replace_message_fields(bool value) {
+    replace_message_fields_ = value;
+  }
+  bool replace_message_fields() const { return replace_message_fields_; }
+  // The default merging behavior will append entries from the source
+  // repeated field to the destination repeated field. If you only want
+  // to keep the entries from the source repeated field, set this flag
+  // to true.
+  void set_replace_repeated_fields(bool value) {
+    replace_repeated_fields_ = value;
+  }
+  bool replace_repeated_fields() const { return replace_repeated_fields_; }
+
+ private:
+  bool replace_message_fields_;
+  bool replace_repeated_fields_;
+};
+
+class PROTOBUF_EXPORT FieldMaskUtil::TrimOptions {
+ public:
+  TrimOptions() : keep_required_fields_(false) {}
+  // When trimming message fields, the default behavior is to trim required
+  // fields of the present message if they are not specified in the field mask.
+  // If you instead want to keep required fields of the present message even
+  // when they are not specified in the field mask, set this flag to true.
+  void set_keep_required_fields(bool value) { keep_required_fields_ = value; }
+  bool keep_required_fields() const { return keep_required_fields_; }
+
+ private:
+  bool keep_required_fields_;
+};
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_FIELD_MASK_UTIL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/constants.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/constants.h
new file mode 100644
index 0000000..8bded86
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/constants.h
@@ -0,0 +1,101 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_CONSTANTS_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_CONSTANTS_H__
+
+#include <cstdint>
+
+#include <google/protobuf/stubs/common.h>
+
+// This file contains constants used by //net/proto2/util/converter.
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+// Prefix for type URLs.
+const char kTypeServiceBaseUrl[] = "type.googleapis.com";
+
+// Format string for RFC3339 timestamp formatting.
+const char kRfc3339TimeFormat[] = "%E4Y-%m-%dT%H:%M:%S";
+
+// Same as above, but the year value is not zero-padded i.e. this accepts
+// timestamps like "1-01-0001T23:59:59Z" instead of "0001-01-0001T23:59:59Z".
+const char kRfc3339TimeFormatNoPadding[] = "%Y-%m-%dT%H:%M:%S";
+
+// Minimum seconds allowed in a google.protobuf.Timestamp value.
+const int64_t kTimestampMinSeconds = -62135596800LL;
+
+// Maximum seconds allowed in a google.protobuf.Timestamp value.
+const int64_t kTimestampMaxSeconds = 253402300799LL;
+
+// Minimum seconds allowed in a google.protobuf.Duration value.
+const int64_t kDurationMinSeconds = -315576000000LL;
+
+// Maximum seconds allowed in a google.protobuf.Duration value.
+const int64_t kDurationMaxSeconds = 315576000000LL;
+
+// Nano seconds in a second.
+const int32_t kNanosPerSecond = 1000000000;
+
+// Type url representing NULL values in google.protobuf.Struct type.
+const char kStructNullValueTypeUrl[] =
+    "type.googleapis.com/google.protobuf.NullValue";
+
+// Type string for google.protobuf.Struct
+const char kStructType[] = "google.protobuf.Struct";
+
+// Type string for struct.proto's google.protobuf.Value value type.
+const char kStructValueType[] = "google.protobuf.Value";
+
+// Type string for struct.proto's google.protobuf.ListValue value type.
+const char kStructListValueType[] = "google.protobuf.ListValue";
+
+// Type string for google.protobuf.Timestamp
+const char kTimestampType[] = "google.protobuf.Timestamp";
+
+// Type string for google.protobuf.Duration
+const char kDurationType[] = "google.protobuf.Duration";
+
+// Type URL for struct value type google.protobuf.Value
+const char kStructValueTypeUrl[] = "type.googleapis.com/google.protobuf.Value";
+
+// Type string for google.protobuf.Any
+const char kAnyType[] = "google.protobuf.Any";
+
+// The protobuf option name of jspb.message_id;
+const char kOptionJspbMessageId[] = "jspb.message_id";
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_CONSTANTS_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/datapiece.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/datapiece.h
new file mode 100644
index 0000000..6d08349
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/datapiece.h
@@ -0,0 +1,219 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_DATAPIECE_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_DATAPIECE_H__
+
+#include <cstdint>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/stubs/statusor.h>
+#include <google/protobuf/stubs/strutil.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+class ProtoWriter;
+
+// Container for a single piece of data together with its data type.
+//
+// For primitive types (int32, int64, uint32, uint64, double, float, bool),
+// the data is stored by value.
+//
+// For string, a StringPiece is stored. For Cord, a pointer to Cord is stored.
+// Just like StringPiece, the DataPiece class does not own the storage for
+// the actual string or Cord, so it is the user's responsibility to guarantee
+// that the underlying storage is still valid when the DataPiece is accessed.
+class PROTOBUF_EXPORT DataPiece {
+ public:
+  // Identifies data type of the value.
+  // These are the types supported by DataPiece.
+  enum Type {
+    TYPE_INT32 = 1,
+    TYPE_INT64 = 2,
+    TYPE_UINT32 = 3,
+    TYPE_UINT64 = 4,
+    TYPE_DOUBLE = 5,
+    TYPE_FLOAT = 6,
+    TYPE_BOOL = 7,
+    TYPE_ENUM = 8,
+    TYPE_STRING = 9,
+    TYPE_BYTES = 10,
+    TYPE_NULL = 11,  // explicit NULL type
+  };
+
+  // Constructors and Destructor
+  explicit DataPiece(const int32_t value)
+      : type_(TYPE_INT32), i32_(value), use_strict_base64_decoding_(false) {}
+  explicit DataPiece(const int64_t value)
+      : type_(TYPE_INT64), i64_(value), use_strict_base64_decoding_(false) {}
+  explicit DataPiece(const uint32_t value)
+      : type_(TYPE_UINT32), u32_(value), use_strict_base64_decoding_(false) {}
+  explicit DataPiece(const uint64_t value)
+      : type_(TYPE_UINT64), u64_(value), use_strict_base64_decoding_(false) {}
+  explicit DataPiece(const double value)
+      : type_(TYPE_DOUBLE),
+        double_(value),
+        use_strict_base64_decoding_(false) {}
+  explicit DataPiece(const float value)
+      : type_(TYPE_FLOAT), float_(value), use_strict_base64_decoding_(false) {}
+  explicit DataPiece(const bool value)
+      : type_(TYPE_BOOL), bool_(value), use_strict_base64_decoding_(false) {}
+  DataPiece(StringPiece value, bool use_strict_base64_decoding)
+      : type_(TYPE_STRING),
+        str_(value),
+        use_strict_base64_decoding_(use_strict_base64_decoding) {}
+  // Constructor for bytes. The second parameter is not used.
+  DataPiece(StringPiece value, bool /*dummy*/, bool use_strict_base64_decoding)
+      : type_(TYPE_BYTES),
+        str_(value),
+        use_strict_base64_decoding_(use_strict_base64_decoding) {}
+
+  DataPiece(const DataPiece& r) : type_(r.type_) { InternalCopy(r); }
+
+  DataPiece& operator=(const DataPiece& x) {
+    InternalCopy(x);
+    return *this;
+  }
+
+  static DataPiece NullData() { return DataPiece(TYPE_NULL, 0); }
+
+  virtual ~DataPiece() {
+  }
+
+  // Accessors
+  Type type() const { return type_; }
+
+  bool use_strict_base64_decoding() { return use_strict_base64_decoding_; }
+
+  StringPiece str() const {
+    GOOGLE_LOG_IF(DFATAL, type_ != TYPE_STRING) << "Not a string type.";
+    return str_;
+  }
+
+
+  // Parses, casts or converts the value stored in the DataPiece into an int32.
+  util::StatusOr<int32_t> ToInt32() const;
+
+  // Parses, casts or converts the value stored in the DataPiece into a uint32.
+  util::StatusOr<uint32_t> ToUint32() const;
+
+  // Parses, casts or converts the value stored in the DataPiece into an int64.
+  util::StatusOr<int64_t> ToInt64() const;
+
+  // Parses, casts or converts the value stored in the DataPiece into a uint64.
+  util::StatusOr<uint64_t> ToUint64() const;
+
+  // Parses, casts or converts the value stored in the DataPiece into a double.
+  util::StatusOr<double> ToDouble() const;
+
+  // Parses, casts or converts the value stored in the DataPiece into a float.
+  util::StatusOr<float> ToFloat() const;
+
+  // Parses, casts or converts the value stored in the DataPiece into a bool.
+  util::StatusOr<bool> ToBool() const;
+
+  // Parses, casts or converts the value stored in the DataPiece into a string.
+  util::StatusOr<std::string> ToString() const;
+
+  // Tries to convert the value contained in this datapiece to string. If the
+  // conversion fails, it returns the default_string.
+  std::string ValueAsStringOrDefault(StringPiece default_string) const;
+
+  util::StatusOr<std::string> ToBytes() const;
+
+ private:
+  friend class ProtoWriter;
+
+  // Disallow implicit constructor.
+  DataPiece();
+
+  // Helper to create NULL or ENUM types.
+  DataPiece(Type type, int32_t val)
+      : type_(type), i32_(val), use_strict_base64_decoding_(false) {}
+
+  // Same as the ToEnum() method above but with additional flag to ignore
+  // unknown enum values.
+  util::StatusOr<int> ToEnum(const google::protobuf::Enum* enum_type,
+                             bool use_lower_camel_for_enums,
+                             bool case_insensitive_enum_parsing,
+                             bool ignore_unknown_enum_values,
+                             bool* is_unknown_enum_value) const;
+
+  // For numeric conversion between
+  //     int32, int64, uint32, uint64, double, float and bool
+  template <typename To>
+  util::StatusOr<To> GenericConvert() const;
+
+  // For conversion from string to
+  //     int32, int64, uint32, uint64, double, float and bool
+  template <typename To>
+  util::StatusOr<To> StringToNumber(bool (*func)(StringPiece, To*)) const;
+
+  // Decodes a base64 string. Returns true on success.
+  bool DecodeBase64(StringPiece src, std::string* dest) const;
+
+  // Helper function to initialize this DataPiece with 'other'.
+  void InternalCopy(const DataPiece& other);
+
+  // Data type for this piece of data.
+  Type type_;
+
+  // Stored piece of data.
+  union {
+    int32_t i32_;
+    int64_t i64_;
+    uint32_t u32_;
+    uint64_t u64_;
+    double double_;
+    float float_;
+    bool bool_;
+    StringPiece str_;
+  };
+
+  // Uses a stricter version of base64 decoding for byte fields.
+  bool use_strict_base64_decoding_;
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_DATAPIECE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/default_value_objectwriter.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/default_value_objectwriter.h
new file mode 100644
index 0000000..1a151ab
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/default_value_objectwriter.h
@@ -0,0 +1,332 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_DEFAULT_VALUE_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_DEFAULT_VALUE_OBJECTWRITER_H__
+
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <stack>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/datapiece.h>
+#include <google/protobuf/util/internal/object_writer.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/util/type_resolver.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// An ObjectWriter that renders non-repeated primitive fields of proto messages
+// with their default values. DefaultValueObjectWriter holds objects, lists and
+// fields it receives in a tree structure and writes them out to another
+// ObjectWriter when EndObject() is called on the root object. It also writes
+// out all non-repeated primitive fields that haven't been explicitly rendered
+// with their default values (0 for numbers, "" for strings, etc).
+class PROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
+ public:
+  // A Callback function to check whether a field needs to be scrubbed.
+  //
+  // Returns true if the field should not be present in the output. Returns
+  // false otherwise.
+  //
+  // The 'path' parameter is a vector of path to the field from root. For
+  // example: if a nested field "a.b.c" (b is the parent message field of c and
+  // a is the parent message field of b), then the vector should contain { "a",
+  // "b", "c" }.
+  //
+  // The Field* should point to the google::protobuf::Field of "c".
+  typedef std::function<bool(
+      const std::vector<std::string>& /*path of the field*/,
+      const google::protobuf::Field* /*field*/)>
+      FieldScrubCallBack;
+
+  DefaultValueObjectWriter(TypeResolver* type_resolver,
+                           const google::protobuf::Type& type,
+                           ObjectWriter* ow);
+
+  ~DefaultValueObjectWriter() override;
+
+  // ObjectWriter methods.
+  DefaultValueObjectWriter* StartObject(StringPiece name) override;
+
+  DefaultValueObjectWriter* EndObject() override;
+
+  DefaultValueObjectWriter* StartList(StringPiece name) override;
+
+  DefaultValueObjectWriter* EndList() override;
+
+  DefaultValueObjectWriter* RenderBool(StringPiece name,
+                                       bool value) override;
+
+  DefaultValueObjectWriter* RenderInt32(StringPiece name,
+                                        int32_t value) override;
+
+  DefaultValueObjectWriter* RenderUint32(StringPiece name,
+                                         uint32_t value) override;
+
+  DefaultValueObjectWriter* RenderInt64(StringPiece name,
+                                        int64_t value) override;
+
+  DefaultValueObjectWriter* RenderUint64(StringPiece name,
+                                         uint64_t value) override;
+
+  DefaultValueObjectWriter* RenderDouble(StringPiece name,
+                                         double value) override;
+
+  DefaultValueObjectWriter* RenderFloat(StringPiece name,
+                                        float value) override;
+
+  DefaultValueObjectWriter* RenderString(StringPiece name,
+                                         StringPiece value) override;
+  DefaultValueObjectWriter* RenderBytes(StringPiece name,
+                                        StringPiece value) override;
+
+  DefaultValueObjectWriter* RenderNull(StringPiece name) override;
+
+  // Register the callback for scrubbing of fields.
+  void RegisterFieldScrubCallBack(FieldScrubCallBack field_scrub_callback);
+
+  // If set to true, empty lists are suppressed from output when default values
+  // are written.
+  void set_suppress_empty_list(bool value) { suppress_empty_list_ = value; }
+
+  // If set to true, original proto field names are used
+  void set_preserve_proto_field_names(bool value) {
+    preserve_proto_field_names_ = value;
+  }
+
+  // If set to true, enums are rendered as ints from output when default values
+  // are written.
+  void set_print_enums_as_ints(bool value) { use_ints_for_enums_ = value; }
+
+ protected:
+  enum NodeKind {
+    PRIMITIVE = 0,
+    OBJECT = 1,
+    LIST = 2,
+    MAP = 3,
+  };
+
+  // "Node" represents a node in the tree that holds the input of
+  // DefaultValueObjectWriter.
+  class PROTOBUF_EXPORT Node {
+   public:
+    Node(const std::string& name, const google::protobuf::Type* type,
+         NodeKind kind, const DataPiece& data, bool is_placeholder,
+         const std::vector<std::string>& path, bool suppress_empty_list,
+         bool preserve_proto_field_names, bool use_ints_for_enums,
+         FieldScrubCallBack field_scrub_callback);
+    virtual ~Node() {
+      for (size_t i = 0; i < children_.size(); ++i) {
+        delete children_[i];
+      }
+    }
+
+    // Adds a child to this node. Takes ownership of this child.
+    void AddChild(Node* child) { children_.push_back(child); }
+
+    // Finds the child given its name.
+    Node* FindChild(StringPiece name);
+
+    // Populates children of this Node based on its type. If there are already
+    // children created, they will be merged to the result. Caller should pass
+    // in TypeInfo for looking up types of the children.
+    virtual void PopulateChildren(const TypeInfo* typeinfo);
+
+    // If this node is a leaf (has data), writes the current node to the
+    // ObjectWriter; if not, then recursively writes the children to the
+    // ObjectWriter.
+    virtual void WriteTo(ObjectWriter* ow);
+
+    // Accessors
+    const std::string& name() const { return name_; }
+
+    const std::vector<std::string>& path() const { return path_; }
+
+    const google::protobuf::Type* type() const { return type_; }
+
+    void set_type(const google::protobuf::Type* type) { type_ = type; }
+
+    NodeKind kind() const { return kind_; }
+
+    int number_of_children() const { return children_.size(); }
+
+    void set_data(const DataPiece& data) { data_ = data; }
+
+    bool is_any() const { return is_any_; }
+
+    void set_is_any(bool is_any) { is_any_ = is_any; }
+
+    void set_is_placeholder(bool is_placeholder) {
+      is_placeholder_ = is_placeholder;
+    }
+
+   protected:
+    // Returns the Value Type of a map given the Type of the map entry and a
+    // TypeInfo instance.
+    const google::protobuf::Type* GetMapValueType(
+        const google::protobuf::Type& found_type, const TypeInfo* typeinfo);
+
+    // Calls WriteTo() on every child in children_.
+    void WriteChildren(ObjectWriter* ow);
+
+    // The name of this node.
+    std::string name_;
+    // google::protobuf::Type of this node. Owned by TypeInfo.
+    const google::protobuf::Type* type_;
+    // The kind of this node.
+    NodeKind kind_;
+    // Whether this is a node for "Any".
+    bool is_any_;
+    // The data of this node when it is a leaf node.
+    DataPiece data_;
+    // Children of this node.
+    std::vector<Node*> children_;
+    // Whether this node is a placeholder for an object or list automatically
+    // generated when creating the parent node. Should be set to false after
+    // the parent node's StartObject()/StartList() method is called with this
+    // node's name.
+    bool is_placeholder_;
+
+    // Path of the field of this node
+    std::vector<std::string> path_;
+
+    // Whether to suppress empty list output.
+    bool suppress_empty_list_;
+
+    // Whether to preserve original proto field names
+    bool preserve_proto_field_names_;
+
+    // Whether to always print enums as ints
+    bool use_ints_for_enums_;
+
+    // Function for determining whether a field needs to be scrubbed or not.
+    FieldScrubCallBack field_scrub_callback_;
+
+   private:
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Node);
+  };
+
+  // Creates a new Node and returns it. Caller owns memory of returned object.
+  virtual Node* CreateNewNode(const std::string& name,
+                              const google::protobuf::Type* type, NodeKind kind,
+                              const DataPiece& data, bool is_placeholder,
+                              const std::vector<std::string>& path,
+                              bool suppress_empty_list,
+                              bool preserve_proto_field_names,
+                              bool use_ints_for_enums,
+                              FieldScrubCallBack field_scrub_callback);
+
+  // Creates a DataPiece containing the default value of the type of the field.
+  static DataPiece CreateDefaultDataPieceForField(
+      const google::protobuf::Field& field, const TypeInfo* typeinfo) {
+    return CreateDefaultDataPieceForField(field, typeinfo, false);
+  }
+
+  // Same as the above but with a flag to use ints instead of enum names.
+  static DataPiece CreateDefaultDataPieceForField(
+      const google::protobuf::Field& field, const TypeInfo* typeinfo,
+      bool use_ints_for_enums);
+
+ protected:
+  // Returns a pointer to current Node in tree.
+  Node* current() { return current_; }
+
+ private:
+  // Populates children of "node" if it is an "any" Node and its real type has
+  // been given.
+  void MaybePopulateChildrenOfAny(Node* node);
+
+  // Writes the root_ node to ow_ and resets the root_ and current_ pointer to
+  // nullptr.
+  void WriteRoot();
+
+  // Adds or replaces the data_ of a primitive child node.
+  void RenderDataPiece(StringPiece name, const DataPiece& data);
+
+  // Returns the default enum value as a DataPiece, or the first enum value if
+  // there is no default. For proto3, where we cannot specify an explicit
+  // default, a zero value will always be returned.
+  static DataPiece FindEnumDefault(const google::protobuf::Field& field,
+                                   const TypeInfo* typeinfo,
+                                   bool use_ints_for_enums);
+
+  // Type information for all the types used in the descriptor. Used to find
+  // google::protobuf::Type of nested messages/enums.
+  const TypeInfo* typeinfo_;
+  // Whether the TypeInfo object is owned by this class.
+  bool own_typeinfo_;
+  // google::protobuf::Type of the root message type.
+  const google::protobuf::Type& type_;
+  // Holds copies of strings passed to RenderString.
+  std::vector<std::unique_ptr<std::string>> string_values_;
+
+  // The current Node. Owned by its parents.
+  Node* current_;
+  // The root Node.
+  std::unique_ptr<Node> root_;
+  // The stack to hold the path of Nodes from current_ to root_;
+  std::stack<Node*> stack_;
+
+  // Whether to suppress output of empty lists.
+  bool suppress_empty_list_;
+
+  // Whether to preserve original proto field names
+  bool preserve_proto_field_names_;
+
+  // Whether to always print enums as ints
+  bool use_ints_for_enums_;
+
+  // Function for determining whether a field needs to be scrubbed or not.
+  FieldScrubCallBack field_scrub_callback_;
+
+  ObjectWriter* ow_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DefaultValueObjectWriter);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_DEFAULT_VALUE_OBJECTWRITER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/error_listener.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/error_listener.h
new file mode 100644
index 0000000..8c9c501
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/error_listener.h
@@ -0,0 +1,109 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_ERROR_LISTENER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_ERROR_LISTENER_H__
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/callback.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/location_tracker.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// Interface for error listener.
+class PROTOBUF_EXPORT ErrorListener {
+ public:
+  virtual ~ErrorListener() {}
+
+  // Reports an invalid name at the given location.
+  virtual void InvalidName(const LocationTrackerInterface& loc,
+                           StringPiece invalid_name,
+                           StringPiece message) = 0;
+
+  // Reports an invalid value for a field.
+  virtual void InvalidValue(const LocationTrackerInterface& loc,
+                            StringPiece type_name,
+                            StringPiece value) = 0;
+
+  // Reports a missing required field.
+  virtual void MissingField(const LocationTrackerInterface& loc,
+                            StringPiece missing_name) = 0;
+
+ protected:
+  ErrorListener() {}
+
+ private:
+  // Do not add any data members to this class.
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorListener);
+};
+
+// An error listener that ignores all errors.
+class PROTOBUF_EXPORT NoopErrorListener : public ErrorListener {
+ public:
+  NoopErrorListener() {}
+  ~NoopErrorListener() override {}
+
+  void InvalidName(const LocationTrackerInterface& /*loc*/,
+                   StringPiece /* invalid_name */,
+                   StringPiece /* message */) override {}
+
+  void InvalidValue(const LocationTrackerInterface& /*loc*/,
+                    StringPiece /* type_name */,
+                    StringPiece /* value */) override {}
+
+  void MissingField(const LocationTrackerInterface& /* loc */,
+                    StringPiece /* missing_name */) override {}
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(NoopErrorListener);
+};
+
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_ERROR_LISTENER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/field_mask_utility.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/field_mask_utility.h
new file mode 100644
index 0000000..1882333
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/field_mask_utility.h
@@ -0,0 +1,74 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// FieldMask related utility methods.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_FIELD_MASK_UTILITY_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_FIELD_MASK_UTILITY_H__
+
+#include <functional>
+#include <stack>
+
+#include <google/protobuf/stubs/callback.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/status.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+typedef std::function<std::string(StringPiece)> ConverterCallback;
+typedef std::function<util::Status(StringPiece)> PathSinkCallback;
+
+// Applies a 'converter' to each segment of a FieldMask path and returns the
+// result. Quoted strings in the 'path' are copied to the output as-is without
+// converting their content. Escaping is supported within quoted strings.
+// For example, "ab\"_c" will be returned as "ab\"_c" without any changes.
+std::string ConvertFieldMaskPath(const StringPiece path,
+                                 ConverterCallback converter);
+
+// Decodes a compact list of FieldMasks. For example, "a.b,a.c.d,a.c.e" will be
+// decoded into a list of field paths - "a.b", "a.c.d", "a.c.e". And the results
+// will be sent to 'path_sink', i.e. 'path_sink' will be called once per
+// resulting path.
+// Note that we also support Apiary style FieldMask form. The above example in
+// the Apiary style will look like "a.b,a.c(d,e)".
+util::Status DecodeCompactFieldMaskPaths(StringPiece paths,
+                                         PathSinkCallback path_sink);
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_FIELD_MASK_UTILITY_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/json_escaping.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/json_escaping.h
new file mode 100644
index 0000000..7d54f22
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/json_escaping.h
@@ -0,0 +1,98 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_JSON_ESCAPING_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_JSON_ESCAPING_H__
+
+#include <cstdint>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/bytestream.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class JsonEscaping {
+ public:
+  // The minimum value of a unicode high-surrogate code unit in the utf-16
+  // encoding. A high-surrogate is also known as a leading-surrogate.
+  // See http://www.unicode.org/glossary/#high_surrogate_code_unit
+  static constexpr uint16_t kMinHighSurrogate = 0xd800;
+
+  // The maximum value of a unicide high-surrogate code unit in the utf-16
+  // encoding. A high-surrogate is also known as a leading-surrogate.
+  // See http://www.unicode.org/glossary/#high_surrogate_code_unit
+  static constexpr uint16_t kMaxHighSurrogate = 0xdbff;
+
+  // The minimum value of a unicode low-surrogate code unit in the utf-16
+  // encoding. A low-surrogate is also known as a trailing-surrogate.
+  // See http://www.unicode.org/glossary/#low_surrogate_code_unit
+  static constexpr uint16_t kMinLowSurrogate = 0xdc00;
+
+  // The maximum value of a unicode low-surrogate code unit in the utf-16
+  // encoding. A low-surrogate is also known as a trailing surrogate.
+  // See http://www.unicode.org/glossary/#low_surrogate_code_unit
+  static constexpr uint16_t kMaxLowSurrogate = 0xdfff;
+
+  // The minimum value of a unicode supplementary code point.
+  // See http://www.unicode.org/glossary/#supplementary_code_point
+  static constexpr uint32_t kMinSupplementaryCodePoint = 0x010000;
+
+  // The minimum value of a unicode code point.
+  // See http://www.unicode.org/glossary/#code_point
+  static constexpr uint32_t kMinCodePoint = 0x000000;
+
+  // The maximum value of a unicode code point.
+  // See http://www.unicode.org/glossary/#code_point
+  static constexpr uint32_t kMaxCodePoint = 0x10ffff;
+
+  JsonEscaping() {}
+  virtual ~JsonEscaping() {}
+
+  // Escape the given ByteSource to the given ByteSink.
+  static void Escape(strings::ByteSource* input, strings::ByteSink* output);
+
+  // Escape the given ByteSource to the given ByteSink.
+  // This is optimized for the case where the string is all printable 7-bit
+  // ASCII and does not contain a few other characters (such as quotes).
+  static void Escape(StringPiece input, strings::ByteSink* output);
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(JsonEscaping);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_JSON_ESCAPING_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/json_objectwriter.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/json_objectwriter.h
new file mode 100644
index 0000000..92348da
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/json_objectwriter.h
@@ -0,0 +1,278 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_JSON_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_JSON_OBJECTWRITER_H__
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/stubs/bytestream.h>
+#include <google/protobuf/util/internal/structured_objectwriter.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+
+// An ObjectWriter implementation that outputs JSON. This ObjectWriter
+// supports writing a compact form or a pretty printed form.
+//
+// Sample usage:
+//   string output;
+//   StringOutputStream* str_stream = new StringOutputStream(&output);
+//   CodedOutputStream* out_stream = new CodedOutputStream(str_stream);
+//   JsonObjectWriter* ow = new JsonObjectWriter("  ", out_stream);
+//   ow->StartObject("")
+//       ->RenderString("name", "value")
+//       ->RenderString("emptystring", string())
+//       ->StartObject("nested")
+//         ->RenderInt64("light", 299792458);
+//         ->RenderDouble("pi", 3.141592653589793);
+//       ->EndObject()
+//       ->StartList("empty")
+//       ->EndList()
+//     ->EndObject();
+//
+// And then the output string would become:
+// {
+//   "name": "value",
+//   "emptystring": "",
+//   "nested": {
+//     "light": "299792458",
+//     "pi": 3.141592653589793
+//   },
+//   "empty": []
+// }
+//
+// JsonObjectWriter does not validate if calls actually result in valid JSON.
+// For example, passing an empty name when one would be required won't result
+// in an error, just an invalid output.
+//
+// Note that all int64 and uint64 are rendered as strings instead of numbers.
+// This is because JavaScript parses numbers as 64-bit float thus int64 and
+// uint64 would lose precision if rendered as numbers.
+//
+// JsonObjectWriter is thread-unsafe.
+class PROTOBUF_EXPORT JsonObjectWriter : public StructuredObjectWriter {
+ public:
+  JsonObjectWriter(StringPiece indent_string, io::CodedOutputStream* out)
+      : element_(new Element(/*parent=*/nullptr, /*is_json_object=*/false)),
+        stream_(out),
+        sink_(out),
+        indent_string_(indent_string),
+        indent_char_('\0'),
+        indent_count_(0),
+        use_websafe_base64_for_bytes_(false) {
+    // See if we have a trivial sequence of indent characters.
+    if (!indent_string.empty()) {
+      indent_char_ = indent_string[0];
+      indent_count_ = indent_string.length();
+      for (size_t i = 1; i < indent_string.length(); i++) {
+        if (indent_char_ != indent_string_[i]) {
+          indent_char_ = '\0';
+          indent_count_ = 0;
+          break;
+        }
+      }
+    }
+  }
+  ~JsonObjectWriter() override;
+
+  // ObjectWriter methods.
+  JsonObjectWriter* StartObject(StringPiece name) override;
+  JsonObjectWriter* EndObject() override;
+  JsonObjectWriter* StartList(StringPiece name) override;
+  JsonObjectWriter* EndList() override;
+  JsonObjectWriter* RenderBool(StringPiece name, bool value) override;
+  JsonObjectWriter* RenderInt32(StringPiece name, int32_t value) override;
+  JsonObjectWriter* RenderUint32(StringPiece name,
+                                 uint32_t value) override;
+  JsonObjectWriter* RenderInt64(StringPiece name, int64_t value) override;
+  JsonObjectWriter* RenderUint64(StringPiece name,
+                                 uint64_t value) override;
+  JsonObjectWriter* RenderDouble(StringPiece name, double value) override;
+  JsonObjectWriter* RenderFloat(StringPiece name, float value) override;
+  JsonObjectWriter* RenderString(StringPiece name,
+                                 StringPiece value) override;
+  JsonObjectWriter* RenderBytes(StringPiece name, StringPiece value) override;
+  JsonObjectWriter* RenderNull(StringPiece name) override;
+  virtual JsonObjectWriter* RenderNullAsEmpty(StringPiece name);
+
+  void set_use_websafe_base64_for_bytes(bool value) {
+    use_websafe_base64_for_bytes_ = value;
+  }
+
+ protected:
+  class PROTOBUF_EXPORT Element : public BaseElement {
+   public:
+    Element(Element* parent, bool is_json_object)
+        : BaseElement(parent),
+          is_first_(true),
+          is_json_object_(is_json_object) {}
+
+    // Called before each field of the Element is to be processed.
+    // Returns true if this is the first call (processing the first field).
+    bool is_first() {
+      if (is_first_) {
+        is_first_ = false;
+        return true;
+      }
+      return false;
+    }
+
+    // Whether we are currently rendering inside a JSON object (i.e., between
+    // StartObject() and EndObject()).
+    bool is_json_object() const { return is_json_object_; }
+
+   private:
+    bool is_first_;
+    bool is_json_object_;
+
+    GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(Element);
+  };
+
+  Element* element() override { return element_.get(); }
+
+ private:
+  class PROTOBUF_EXPORT ByteSinkWrapper : public strings::ByteSink {
+   public:
+    explicit ByteSinkWrapper(io::CodedOutputStream* stream) : stream_(stream) {}
+    ~ByteSinkWrapper() override {}
+
+    // ByteSink methods.
+    void Append(const char* bytes, size_t n) override {
+      stream_->WriteRaw(bytes, n);
+    }
+
+   private:
+    io::CodedOutputStream* stream_;
+
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ByteSinkWrapper);
+  };
+
+  // Renders a simple value as a string. By default all non-string Render
+  // methods convert their argument to a string and call this method. This
+  // method can then be used to render the simple value without escaping it.
+  JsonObjectWriter* RenderSimple(StringPiece name,
+                                 StringPiece value) {
+    WritePrefix(name);
+    WriteRawString(value);
+    return this;
+  }
+
+  // Pushes a new JSON array element to the stack.
+  void PushArray() {
+    element_.reset(new Element(element_.release(), /*is_json_object=*/false));
+  }
+
+  // Pushes a new JSON object element to the stack.
+  void PushObject() {
+    element_.reset(new Element(element_.release(), /*is_json_object=*/true));
+  }
+
+  // Pops an element off of the stack and deletes the popped element.
+  void Pop() {
+    bool needs_newline = !element_->is_first();
+    element_.reset(element_->pop<Element>());
+    if (needs_newline) NewLine();
+  }
+
+  // If pretty printing is enabled, this will write a newline to the output,
+  // followed by optional indentation. Otherwise this method is a noop.
+  void NewLine() {
+    if (!indent_string_.empty()) {
+      size_t len = sizeof('\n') + (indent_string_.size() * element()->level());
+
+      // Take the slow-path if we don't have sufficient characters remaining in
+      // our buffer or we have a non-trivial indent string which would prevent
+      // us from using memset.
+      uint8_t* out = nullptr;
+      if (indent_count_ > 0) {
+        out = stream_->GetDirectBufferForNBytesAndAdvance(len);
+      }
+
+      if (out != nullptr) {
+        out[0] = '\n';
+        memset(&out[1], indent_char_, len - 1);
+      } else {
+        // Slow path, no contiguous output buffer available.
+        WriteChar('\n');
+        for (int i = 0; i < element()->level(); i++) {
+          stream_->WriteRaw(indent_string_.c_str(), indent_string_.length());
+        }
+      }
+    }
+  }
+
+  // Writes a prefix. This will write out any pretty printing and
+  // commas that are required, followed by the name and a ':' if
+  // the name is not null.
+  void WritePrefix(StringPiece name);
+
+  // Writes an individual character to the output.
+  void WriteChar(const char c) { stream_->WriteRaw(&c, sizeof(c)); }
+
+  // Writes a string to the output.
+  void WriteRawString(StringPiece s) {
+    stream_->WriteRaw(s.data(), s.length());
+  }
+
+  std::unique_ptr<Element> element_;
+  io::CodedOutputStream* stream_;
+  ByteSinkWrapper sink_;
+  const std::string indent_string_;
+
+  // For the common case of indent being a single character repeated.
+  char indent_char_;
+  int indent_count_;
+
+  // Whether to use regular or websafe base64 encoding for byte fields. Defaults
+  // to regular base64 encoding.
+  bool use_websafe_base64_for_bytes_;
+
+  GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(JsonObjectWriter);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_JSON_OBJECTWRITER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/json_stream_parser.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/json_stream_parser.h
new file mode 100644
index 0000000..09f17ad
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/json_stream_parser.h
@@ -0,0 +1,350 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_JSON_STREAM_PARSER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_JSON_STREAM_PARSER_H__
+
+#include <cstdint>
+#include <stack>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/status.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+
+class ObjectWriter;
+
+// A JSON parser that can parse a stream of JSON chunks rather than needing the
+// entire JSON string up front. It is a modified version of the parser in
+// //net/proto/json/json-parser.h that has been changed in the following ways:
+// - Changed from recursion to an explicit stack to allow resumption
+// - Added support for int64 and uint64 numbers
+// - Removed support for octal and decimal escapes
+// - Removed support for numeric keys
+// - Removed support for functions (javascript)
+// - Removed some lax-comma support (but kept trailing comma support)
+// - Writes directly to an ObjectWriter rather than using subclassing
+//
+// Here is an example usage:
+// JsonStreamParser parser(ow_.get());
+// util::Status result = parser.Parse(chunk1);
+// result.Update(parser.Parse(chunk2));
+// result.Update(parser.FinishParse());
+// GOOGLE_DCHECK(result.ok()) << "Failed to parse JSON";
+//
+// This parser is thread-compatible as long as only one thread is calling a
+// Parse() method at a time.
+class PROTOBUF_EXPORT JsonStreamParser {
+ public:
+  // Creates a JsonStreamParser that will write to the given ObjectWriter.
+  explicit JsonStreamParser(ObjectWriter* ow);
+  virtual ~JsonStreamParser();
+
+  // Parses a UTF-8 encoded JSON string from a StringPiece. If the returned
+  // status is non-ok, the status might contain a payload ParseErrorType with
+  // type_url kParseErrorTypeUrl and a payload containing string snippet of the
+  // error with type_url kParseErrorSnippetUrl.
+  util::Status Parse(StringPiece json);
+
+
+  // Finish parsing the JSON string. If the returned status is non-ok, the
+  // status might contain a payload ParseErrorType with type_url
+  // kParseErrorTypeUrl and a payload containing string snippet of the error
+  // with type_url kParseErrorSnippetUrl.
+  util::Status FinishParse();
+
+
+  // Sets the max recursion depth of JSON message to be deserialized. JSON
+  // messages over this depth will fail to be deserialized.
+  // Default value is 100.
+  void set_max_recursion_depth(int max_depth) {
+    max_recursion_depth_ = max_depth;
+  }
+
+  // Denotes the cause of error.
+  enum ParseErrorType {
+    UNKNOWN_PARSE_ERROR,
+    OCTAL_OR_HEX_ARE_NOT_VALID_JSON_VALUES,
+    EXPECTED_COLON,
+    EXPECTED_COMMA_OR_BRACKET,
+    EXPECTED_VALUE,
+    EXPECTED_COMMA_OR_BRACES,
+    EXPECTED_OBJECT_KEY_OR_BRACES,
+    EXPECTED_VALUE_OR_BRACKET,
+    INVALID_KEY_OR_VARIABLE_NAME,
+    NON_UTF_8,
+    PARSING_TERMINATED_BEFORE_END_OF_INPUT,
+    UNEXPECTED_TOKEN,
+    EXPECTED_CLOSING_QUOTE,
+    ILLEGAL_HEX_STRING,
+    INVALID_ESCAPE_SEQUENCE,
+    MISSING_LOW_SURROGATE,
+    INVALID_LOW_SURROGATE,
+    INVALID_UNICODE,
+    UNABLE_TO_PARSE_NUMBER,
+    NUMBER_EXCEEDS_RANGE_DOUBLE
+  };
+
+ private:
+  friend class JsonStreamParserTest;
+  // Return the current recursion depth.
+  int recursion_depth() { return recursion_depth_; }
+
+  enum TokenType {
+    BEGIN_STRING,     // " or '
+    BEGIN_NUMBER,     // - or digit
+    BEGIN_TRUE,       // true
+    BEGIN_FALSE,      // false
+    BEGIN_NULL,       // null
+    BEGIN_OBJECT,     // {
+    END_OBJECT,       // }
+    BEGIN_ARRAY,      // [
+    END_ARRAY,        // ]
+    ENTRY_SEPARATOR,  // :
+    VALUE_SEPARATOR,  // ,
+    BEGIN_KEY,        // letter, _, $ or digit.  Must begin with non-digit
+    UNKNOWN           // Unknown token or we ran out of the stream.
+  };
+
+  enum ParseType {
+    VALUE,        // Expects a {, [, true, false, null, string or number
+    OBJ_MID,      // Expects a ',' or }
+    ENTRY,        // Expects a key or }
+    ENTRY_MID,    // Expects a :
+    ARRAY_VALUE,  // Expects a value or ]
+    ARRAY_MID     // Expects a ',' or ]
+  };
+
+  // Holds the result of parsing a number
+  struct NumberResult {
+    enum Type { DOUBLE, INT, UINT };
+    Type type;
+    union {
+      double double_val;
+      int64_t int_val;
+      uint64_t uint_val;
+    };
+  };
+
+  // Parses a single chunk of JSON, returning an error if the JSON was invalid.
+  util::Status ParseChunk(StringPiece chunk);
+
+  // Runs the parser based on stack_ and p_, until the stack is empty or p_ runs
+  // out of data. If we unexpectedly run out of p_ we push the latest back onto
+  // the stack and return.
+  util::Status RunParser();
+
+  // Parses a value from p_ and writes it to ow_.
+  // A value may be an object, array, true, false, null, string or number.
+  util::Status ParseValue(TokenType type);
+
+  // Parses a string and writes it out to the ow_.
+  util::Status ParseString();
+
+  // Parses a string, storing the result in parsed_.
+  util::Status ParseStringHelper();
+
+  // This function parses unicode escape sequences in strings. It returns an
+  // error when there's a parsing error, either the size is not the expected
+  // size or a character is not a hex digit.  When it returns str will contain
+  // what has been successfully parsed so far.
+  util::Status ParseUnicodeEscape();
+
+  // Expects p_ to point to a JSON number, writes the number to the writer using
+  // the appropriate Render method based on the type of number.
+  util::Status ParseNumber();
+
+  // Parse a number into a NumberResult, reporting an error if no number could
+  // be parsed. This method will try to parse into a uint64, int64, or double
+  // based on whether the number was positive or negative or had a decimal
+  // component.
+  util::Status ParseNumberHelper(NumberResult* result);
+
+  // Parse a number as double into a NumberResult.
+  util::Status ParseDoubleHelper(const std::string& number,
+                                 NumberResult* result);
+
+  // Handles a { during parsing of a value.
+  util::Status HandleBeginObject();
+
+  // Parses from the ENTRY state.
+  util::Status ParseEntry(TokenType type);
+
+  // Parses from the ENTRY_MID state.
+  util::Status ParseEntryMid(TokenType type);
+
+  // Parses from the OBJ_MID state.
+  util::Status ParseObjectMid(TokenType type);
+
+  // Handles a [ during parsing of a value.
+  util::Status HandleBeginArray();
+
+  // Parses from the ARRAY_VALUE state.
+  util::Status ParseArrayValue(TokenType type);
+
+  // Parses from the ARRAY_MID state.
+  util::Status ParseArrayMid(TokenType type);
+
+  // Expects p_ to point to an unquoted literal
+  util::Status ParseTrue();
+  util::Status ParseFalse();
+  util::Status ParseNull();
+  util::Status ParseEmptyNull();
+
+  // Whether an empty-null is allowed in the current state.
+  bool IsEmptyNullAllowed(TokenType type);
+
+  // Whether the whole input is all whitespaces.
+  bool IsInputAllWhiteSpaces(TokenType type);
+
+  // Report a failure as a util::Status.
+  util::Status ReportFailure(StringPiece message,
+                             ParseErrorType parse_code);
+
+  // Report a failure due to an UNKNOWN token type. We check if we hit the
+  // end of the stream and if we're finishing or not to detect what type of
+  // status to return in this case.
+  util::Status ReportUnknown(StringPiece message,
+                             ParseErrorType parse_code);
+
+  // Helper function to check recursion depth and increment it. It will return
+  // OkStatus() if the current depth is allowed. Otherwise an error is returned.
+  // key is used for error reporting.
+  util::Status IncrementRecursionDepth(StringPiece key) const;
+
+  // Advance p_ past all whitespace or until the end of the string.
+  void SkipWhitespace();
+
+  // Advance p_ one UTF-8 character
+  void Advance();
+
+  // Expects p_ to point to the beginning of a key.
+  util::Status ParseKey();
+
+  // Return the type of the next token at p_.
+  TokenType GetNextTokenType();
+
+  // The object writer to write parse events to.
+  ObjectWriter* ow_;
+
+  // The stack of parsing we still need to do. When the stack runs empty we will
+  // have parsed a single value from the root (e.g. an object or list).
+  std::stack<ParseType> stack_;
+
+  // Contains any leftover text from a previous chunk that we weren't able to
+  // fully parse, for example the start of a key or number.
+  std::string leftover_;
+
+  // The current chunk of JSON being parsed. Primarily used for providing
+  // context during error reporting.
+  StringPiece json_;
+
+  // A pointer within the current JSON being parsed, used to track location.
+  StringPiece p_;
+
+  // Stores the last key read, as we separate parsing of keys and values.
+  StringPiece key_;
+
+  // Storage for key_ if we need to keep ownership, for example between chunks
+  // or if the key was unescaped from a JSON string.
+  std::string key_storage_;
+
+  // True during the FinishParse() call, so we know that any errors are fatal.
+  // For example an unterminated string will normally result in cancelling and
+  // trying during the next chunk, but during FinishParse() it is an error.
+  bool finishing_;
+
+  // Whether non whitespace tokens have been seen during parsing.
+  // It is used to handle the case of a pure whitespace stream input.
+  bool seen_non_whitespace_;
+
+  // The JsonStreamParser requires a root element by default and it will raise
+  // error if the root element is missing. If `allow_no_root_element_` is true,
+  // the JsonStreamParser can also handle this case.
+  bool allow_no_root_element_;
+
+  // String we parsed during a call to ParseStringHelper().
+  StringPiece parsed_;
+
+  // Storage for the string we parsed. This may be empty if the string was able
+  // to be parsed directly from the input.
+  std::string parsed_storage_;
+
+  // The character that opened the string, either ' or ".
+  // A value of 0 indicates that string parsing is not in process.
+  char string_open_;
+
+  // Storage for the chunk that are being parsed in ParseChunk().
+  std::string chunk_storage_;
+
+  // Whether to allow non UTF-8 encoded input and replace invalid code points.
+  bool coerce_to_utf8_;
+
+  // Replacement character for invalid UTF-8 code points.
+  std::string utf8_replacement_character_;
+
+  // Whether allows empty string represented null array value or object entry
+  // value.
+  bool allow_empty_null_;
+
+  // Whether unquoted object keys can contain embedded non-alphanumeric
+  // characters when this is unambiguous for parsing.
+  bool allow_permissive_key_naming_;
+
+  // Whether allows out-of-range floating point numbers or reject them.
+  bool loose_float_number_conversion_;
+
+  // Tracks current recursion depth.
+  mutable int recursion_depth_;
+
+  // Maximum allowed recursion depth.
+  int max_recursion_depth_;
+
+  GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(JsonStreamParser);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_JSON_STREAM_PARSER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/location_tracker.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/location_tracker.h
new file mode 100644
index 0000000..68fefcc
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/location_tracker.h
@@ -0,0 +1,70 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_LOCATION_TRACKER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_LOCATION_TRACKER_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// LocationTrackerInterface is an interface for classes that track
+// the location information for the purpose of error reporting.
+class PROTOBUF_EXPORT LocationTrackerInterface {
+ public:
+  virtual ~LocationTrackerInterface() {}
+
+  // Returns the object location as human readable string.
+  virtual std::string ToString() const = 0;
+
+ protected:
+  LocationTrackerInterface() {}
+
+ private:
+  // Please do not add any data members to this class.
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LocationTrackerInterface);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_LOCATION_TRACKER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/object_location_tracker.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/object_location_tracker.h
new file mode 100644
index 0000000..47821e6
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/object_location_tracker.h
@@ -0,0 +1,64 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_OBJECT_LOCATION_TRACKER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_OBJECT_LOCATION_TRACKER_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/util/internal/location_tracker.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// An empty concrete implementation of LocationTrackerInterface.
+class ObjectLocationTracker : public LocationTrackerInterface {
+ public:
+  // Creates an empty location tracker.
+  ObjectLocationTracker() {}
+
+  ~ObjectLocationTracker() override {}
+
+  // Returns empty because nothing is tracked.
+  std::string ToString() const override { return ""; }
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectLocationTracker);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_OBJECT_LOCATION_TRACKER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/object_source.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/object_source.h
new file mode 100644
index 0000000..fc7672e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/object_source.h
@@ -0,0 +1,85 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_OBJECT_SOURCE_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_OBJECT_SOURCE_H__
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/status.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class ObjectWriter;
+
+// An ObjectSource is anything that can write to an ObjectWriter.
+// Implementation of this interface typically provide constructors or
+// factory methods to create an instance based on some source data, for
+// example, a character stream, or protobuf.
+//
+// Derived classes could be thread-unsafe.
+class PROTOBUF_EXPORT ObjectSource {
+ public:
+  virtual ~ObjectSource() {}
+
+  // Writes to the ObjectWriter
+  virtual util::Status WriteTo(ObjectWriter* ow) const {
+    return NamedWriteTo("", ow);
+  }
+
+  // Writes to the ObjectWriter with a custom name for the message.
+  // This is useful when you chain ObjectSource together by embedding one
+  // within another.
+  virtual util::Status NamedWriteTo(StringPiece name,
+                                    ObjectWriter* ow) const = 0;
+
+ protected:
+  ObjectSource() {}
+
+ private:
+  // Do not add any data members to this class.
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectSource);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_OBJECT_SOURCE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/object_writer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/object_writer.h
new file mode 100644
index 0000000..bc4095b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/object_writer.h
@@ -0,0 +1,151 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_OBJECT_WRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_OBJECT_WRITER_H__
+
+#include <cstdint>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/strutil.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+
+class DataPiece;
+
+// An ObjectWriter is an interface for writing a stream of events
+// representing objects and collections. Implementation of this
+// interface can be used to write an object stream to an in-memory
+// structure, protobufs, JSON, XML, or any other output format
+// desired. The ObjectSource interface is typically used as the
+// source of an object stream.
+//
+// See JsonObjectWriter for a sample implementation of ObjectWriter
+// and its use.
+//
+// Derived classes could be thread-unsafe.
+//
+// TODO(xinb): seems like a prime candidate to apply the RAII paradigm
+// and get rid the need to call EndXXX().
+class PROTOBUF_EXPORT ObjectWriter {
+ public:
+  virtual ~ObjectWriter() {}
+
+  // Starts an object. If the name is empty, the object will not be named.
+  virtual ObjectWriter* StartObject(StringPiece name) = 0;
+
+  // Ends an object.
+  virtual ObjectWriter* EndObject() = 0;
+
+  // Starts a list. If the name is empty, the list will not be named.
+  virtual ObjectWriter* StartList(StringPiece name) = 0;
+
+  // Ends a list.
+  virtual ObjectWriter* EndList() = 0;
+
+  // Renders a boolean value.
+  virtual ObjectWriter* RenderBool(StringPiece name, bool value) = 0;
+
+  // Renders an 32-bit integer value.
+  virtual ObjectWriter* RenderInt32(StringPiece name, int32_t value) = 0;
+
+  // Renders an 32-bit unsigned integer value.
+  virtual ObjectWriter* RenderUint32(StringPiece name,
+                                     uint32_t value) = 0;
+
+  // Renders a 64-bit integer value.
+  virtual ObjectWriter* RenderInt64(StringPiece name, int64_t value) = 0;
+
+  // Renders an 64-bit unsigned integer value.
+  virtual ObjectWriter* RenderUint64(StringPiece name,
+                                     uint64_t value) = 0;
+
+
+  // Renders a double value.
+  virtual ObjectWriter* RenderDouble(StringPiece name, double value) = 0;
+  // Renders a float value.
+  virtual ObjectWriter* RenderFloat(StringPiece name, float value) = 0;
+
+  // Renders a StringPiece value. This is for rendering strings.
+  virtual ObjectWriter* RenderString(StringPiece name,
+                                     StringPiece value) = 0;
+
+  // Renders a bytes value.
+  virtual ObjectWriter* RenderBytes(StringPiece name, StringPiece value) = 0;
+
+  // Renders a Null value.
+  virtual ObjectWriter* RenderNull(StringPiece name) = 0;
+
+
+  // Renders a DataPiece object to a ObjectWriter.
+  static void RenderDataPieceTo(const DataPiece& data, StringPiece name,
+                                ObjectWriter* ow);
+
+
+  // Indicates whether this ObjectWriter has completed writing the root message,
+  // usually this means writing of one complete object. Subclasses must override
+  // this behavior appropriately.
+  virtual bool done() { return false; }
+
+  void set_use_strict_base64_decoding(bool value) {
+    use_strict_base64_decoding_ = value;
+  }
+
+  bool use_strict_base64_decoding() const {
+    return use_strict_base64_decoding_;
+  }
+
+ protected:
+  ObjectWriter() : use_strict_base64_decoding_(true) {}
+
+ private:
+  // If set to true, we use the stricter version of base64 decoding for byte
+  // fields by making sure decoded version encodes back to the original string.
+  bool use_strict_base64_decoding_;
+
+  // Do not add any data members to this class.
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectWriter);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_OBJECT_WRITER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/proto_writer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/proto_writer.h
new file mode 100644
index 0000000..a7cf6ac
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/proto_writer.h
@@ -0,0 +1,389 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_PROTO_WRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_PROTO_WRITER_H__
+
+#include <cstdint>
+#include <deque>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/stubs/bytestream.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/util/internal/datapiece.h>
+#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/structured_objectwriter.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/hash.h>
+#include <google/protobuf/stubs/status.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+
+class ObjectLocationTracker;
+
+// An ObjectWriter that can write protobuf bytes directly from writer events.
+// This class does not support special types like Struct or Map. However, since
+// this class supports raw protobuf, it can be used to provide support for
+// special types by inheriting from it or by wrapping it.
+//
+// It also supports streaming.
+class PROTOBUF_EXPORT ProtoWriter : public StructuredObjectWriter {
+ public:
+// Constructor. Does not take ownership of any parameter passed in.
+  ProtoWriter(TypeResolver* type_resolver, const google::protobuf::Type& type,
+              strings::ByteSink* output, ErrorListener* listener);
+  ~ProtoWriter() override;
+
+  // ObjectWriter methods.
+  ProtoWriter* StartObject(StringPiece name) override;
+  ProtoWriter* EndObject() override;
+  ProtoWriter* StartList(StringPiece name) override;
+  ProtoWriter* EndList() override;
+  ProtoWriter* RenderBool(StringPiece name, bool value) override {
+    return RenderDataPiece(name, DataPiece(value));
+  }
+  ProtoWriter* RenderInt32(StringPiece name, int32_t value) override {
+    return RenderDataPiece(name, DataPiece(value));
+  }
+  ProtoWriter* RenderUint32(StringPiece name, uint32_t value) override {
+    return RenderDataPiece(name, DataPiece(value));
+  }
+  ProtoWriter* RenderInt64(StringPiece name, int64_t value) override {
+    return RenderDataPiece(name, DataPiece(value));
+  }
+  ProtoWriter* RenderUint64(StringPiece name, uint64_t value) override {
+    return RenderDataPiece(name, DataPiece(value));
+  }
+  ProtoWriter* RenderDouble(StringPiece name, double value) override {
+    return RenderDataPiece(name, DataPiece(value));
+  }
+  ProtoWriter* RenderFloat(StringPiece name, float value) override {
+    return RenderDataPiece(name, DataPiece(value));
+  }
+  ProtoWriter* RenderString(StringPiece name,
+                            StringPiece value) override {
+    return RenderDataPiece(name,
+                           DataPiece(value, use_strict_base64_decoding()));
+  }
+
+  ProtoWriter* RenderBytes(StringPiece name, StringPiece value) override {
+    return RenderDataPiece(
+        name, DataPiece(value, false, use_strict_base64_decoding()));
+  }
+
+  ProtoWriter* RenderNull(StringPiece name) override {
+    return RenderDataPiece(name, DataPiece::NullData());
+  }
+
+
+  // Renders a DataPiece 'value' into a field whose wire type is determined
+  // from the given field 'name'.
+  virtual ProtoWriter* RenderDataPiece(StringPiece name,
+                                       const DataPiece& data);
+
+
+  // Returns the location tracker to use for tracking locations for errors.
+  const LocationTrackerInterface& location() {
+    return element_ != nullptr ? *element_ : *tracker_;
+  }
+
+  // When true, we finished writing to output a complete message.
+  bool done() override { return done_; }
+
+  // Returns the proto stream object.
+  io::CodedOutputStream* stream() { return stream_.get(); }
+
+  // Getters and mutators of invalid_depth_.
+  void IncrementInvalidDepth() { ++invalid_depth_; }
+  void DecrementInvalidDepth() { --invalid_depth_; }
+  int invalid_depth() { return invalid_depth_; }
+
+  ErrorListener* listener() { return listener_; }
+
+  const TypeInfo* typeinfo() { return typeinfo_; }
+
+  void set_ignore_unknown_fields(bool ignore_unknown_fields) {
+    ignore_unknown_fields_ = ignore_unknown_fields;
+  }
+
+  bool ignore_unknown_fields() { return ignore_unknown_fields_; }
+
+  void set_ignore_unknown_enum_values(bool ignore_unknown_enum_values) {
+    ignore_unknown_enum_values_ = ignore_unknown_enum_values;
+  }
+
+  void set_use_lower_camel_for_enums(bool use_lower_camel_for_enums) {
+    use_lower_camel_for_enums_ = use_lower_camel_for_enums;
+  }
+
+  void set_case_insensitive_enum_parsing(bool case_insensitive_enum_parsing) {
+    case_insensitive_enum_parsing_ = case_insensitive_enum_parsing;
+  }
+
+  void set_use_json_name_in_missing_fields(
+      bool use_json_name_in_missing_fields) {
+    use_json_name_in_missing_fields_ = use_json_name_in_missing_fields;
+  }
+
+ protected:
+  class PROTOBUF_EXPORT ProtoElement : public BaseElement,
+                                       public LocationTrackerInterface {
+   public:
+    // Constructor for the root element. No parent nor field.
+    ProtoElement(const TypeInfo* typeinfo, const google::protobuf::Type& type,
+                 ProtoWriter* enclosing);
+
+    // Constructor for a field of an element.
+    ProtoElement(ProtoElement* parent, const google::protobuf::Field* field,
+                 const google::protobuf::Type& type, bool is_list);
+
+    ~ProtoElement() override {}
+
+    // Called just before the destructor for clean up:
+    //   - reports any missing required fields
+    //   - computes the space needed by the size field, and augment the
+    //     length of all parent messages by this additional space.
+    //   - releases and returns the parent pointer.
+    ProtoElement* pop();
+
+    // Accessors
+    // parent_field() may be nullptr if we are at root.
+    const google::protobuf::Field* parent_field() const {
+      return parent_field_;
+    }
+    const google::protobuf::Type& type() const { return type_; }
+
+    // Registers field for accounting required fields.
+    void RegisterField(const google::protobuf::Field* field);
+
+    // To report location on error messages.
+    std::string ToString() const override;
+
+    ProtoElement* parent() const override {
+      return static_cast<ProtoElement*>(BaseElement::parent());
+    }
+
+    // Returns true if the index is already taken by a preceding oneof input.
+    bool IsOneofIndexTaken(int32_t index);
+
+    // Marks the oneof 'index' as taken. Future inputs to this oneof will
+    // generate an error.
+    void TakeOneofIndex(int32_t index);
+
+    bool proto3() { return proto3_; }
+
+   private:
+    // Used for access to variables of the enclosing instance of
+    // ProtoWriter.
+    ProtoWriter* ow_;
+
+    // Describes the element as a field in the parent message.
+    // parent_field_ is nullptr if and only if this element is the root element.
+    const google::protobuf::Field* parent_field_;
+
+    // TypeInfo to lookup types.
+    const TypeInfo* typeinfo_;
+
+    // Whether the type_ is proto3 or not.
+    bool proto3_;
+
+    // Additional variables if this element is a message:
+    // (Root element is always a message).
+    // type_             : the type of this element.
+    // required_fields_  : set of required fields.
+    // size_index_       : index into ProtoWriter::size_insert_
+    //                     for later insertion of serialized message length.
+    const google::protobuf::Type& type_;
+    std::unordered_set<const google::protobuf::Field*> required_fields_;
+    const int size_index_;
+
+    // Tracks position in repeated fields, needed for LocationTrackerInterface.
+    int array_index_;
+
+    // Set of oneof indices already seen for the type_. Used to validate
+    // incoming messages so no more than one oneof is set.
+    std::vector<bool> oneof_indices_;
+
+    GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement);
+  };
+
+  // Container for inserting 'size' information at the 'pos' position.
+  struct SizeInfo {
+    const int pos;
+    int size;
+  };
+
+  ProtoWriter(const TypeInfo* typeinfo, const google::protobuf::Type& type,
+              strings::ByteSink* output, ErrorListener* listener);
+
+  ProtoElement* element() override { return element_.get(); }
+
+  // Helper methods for calling ErrorListener. See error_listener.h.
+  void InvalidName(StringPiece unknown_name, StringPiece message);
+  void InvalidValue(StringPiece type_name, StringPiece value);
+  void MissingField(StringPiece missing_name);
+
+  // Common code for BeginObject() and BeginList() that does invalid_depth_
+  // bookkeeping associated with name lookup.
+  const google::protobuf::Field* BeginNamed(StringPiece name,
+                                            bool is_list);
+
+  // Lookup the field in the current element. Looks in the base descriptor
+  // and in any extension. This will report an error if the field cannot be
+  // found when ignore_unknown_names_ is false or if multiple matching
+  // extensions are found.
+  const google::protobuf::Field* Lookup(StringPiece name);
+
+  // Lookup the field type in the type descriptor. Returns nullptr if the type
+  // is not known.
+  const google::protobuf::Type* LookupType(
+      const google::protobuf::Field* field);
+
+  // Write serialized output to the final output ByteSink, inserting all
+  // the size information for nested messages that are missing from the
+  // intermediate Cord buffer.
+  void WriteRootMessage();
+
+  // Helper method to write proto tags based on the given field.
+  void WriteTag(const google::protobuf::Field& field);
+
+
+  // Returns true if the field for type_ can be set as a oneof. If field is not
+  // a oneof type, this function does nothing and returns true.
+  // If another field for this oneof is already set, this function returns
+  // false. It also calls the appropriate error callback.
+  // unnormalized_name is used for error string.
+  bool ValidOneof(const google::protobuf::Field& field,
+                  StringPiece unnormalized_name);
+
+  // Returns true if the field is repeated.
+  bool IsRepeated(const google::protobuf::Field& field);
+
+  // Starts an object given the field and the enclosing type.
+  ProtoWriter* StartObjectField(const google::protobuf::Field& field,
+                                const google::protobuf::Type& type);
+
+  // Starts a list given the field and the enclosing type.
+  ProtoWriter* StartListField(const google::protobuf::Field& field,
+                              const google::protobuf::Type& type);
+
+  // Renders a primitive field given the field and the enclosing type.
+  ProtoWriter* RenderPrimitiveField(const google::protobuf::Field& field,
+                                    const google::protobuf::Type& type,
+                                    const DataPiece& data);
+
+ private:
+  // Writes an ENUM field, including tag, to the stream.
+  static util::Status WriteEnum(int field_number, const DataPiece& data,
+                                const google::protobuf::Enum* enum_type,
+                                io::CodedOutputStream* stream,
+                                bool use_lower_camel_for_enums,
+                                bool case_insensitive_enum_parsing,
+                                bool ignore_unknown_values);
+
+  // Variables for describing the structure of the input tree:
+  // master_type_: descriptor for the whole protobuf message.
+  // typeinfo_ : the TypeInfo object to lookup types.
+  const google::protobuf::Type& master_type_;
+  const TypeInfo* typeinfo_;
+  // Whether we own the typeinfo_ object.
+  bool own_typeinfo_;
+
+  // Indicates whether we finished writing root message completely.
+  bool done_;
+
+  // If true, don't report unknown field names to the listener.
+  bool ignore_unknown_fields_;
+
+  // If true, don't report unknown enum values to the listener.
+  bool ignore_unknown_enum_values_;
+
+  // If true, check if enum name in camel case or without underscore matches the
+  // field name.
+  bool use_lower_camel_for_enums_;
+
+  // If true, check if enum name in UPPER_CASE matches the field name.
+  bool case_insensitive_enum_parsing_;
+
+  // If true, use the json name in missing fields errors.
+  bool use_json_name_in_missing_fields_;
+
+  // Variable for internal state processing:
+  // element_    : the current element.
+  // size_insert_: sizes of nested messages.
+  //               pos  - position to insert the size field.
+  //               size - size value to be inserted.
+  std::unique_ptr<ProtoElement> element_;
+  std::deque<SizeInfo> size_insert_;
+
+  // Variables for output generation:
+  // output_  : pointer to an external ByteSink for final user-visible output.
+  // buffer_  : buffer holding partial message before being ready for output_.
+  // adapter_ : internal adapter between CodedOutputStream and buffer_.
+  // stream_  : wrapper for writing tags and other encodings in wire format.
+  strings::ByteSink* output_;
+  std::string buffer_;
+  io::StringOutputStream adapter_;
+  std::unique_ptr<io::CodedOutputStream> stream_;
+
+  // Variables for error tracking and reporting:
+  // listener_     : a place to report any errors found.
+  // invalid_depth_: number of enclosing invalid nested messages.
+  // tracker_      : the root location tracker interface.
+  ErrorListener* listener_;
+  int invalid_depth_;
+  std::unique_ptr<LocationTrackerInterface> tracker_;
+
+  GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoWriter);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_PROTO_WRITER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/protostream_objectsource.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/protostream_objectsource.h
new file mode 100644
index 0000000..8ed2ca9
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/protostream_objectsource.h
@@ -0,0 +1,329 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_PROTOSTREAM_OBJECTSOURCE_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_PROTOSTREAM_OBJECTSOURCE_H__
+
+#include <cstdint>
+#include <functional>
+#include <string>
+#include <unordered_map>
+
+#include <google/protobuf/stubs/status.h>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/stubs/statusor.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/object_source.h>
+#include <google/protobuf/util/internal/object_writer.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/hash.h>
+#include <google/protobuf/stubs/status.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class TypeInfo;
+
+// An ObjectSource that can parse a stream of bytes as a protocol buffer.
+// Its WriteTo() method can be given an ObjectWriter.
+// This implementation uses a google.protobuf.Type for tag and name lookup.
+// The field names are converted into lower camel-case when writing to the
+// ObjectWriter.
+//
+// Sample usage: (suppose input is: string proto)
+//   ArrayInputStream arr_stream(proto.data(), proto.size());
+//   CodedInputStream in_stream(&arr_stream);
+//   ProtoStreamObjectSource os(&in_stream, /*ServiceTypeInfo*/ typeinfo,
+//                              <your message google::protobuf::Type>);
+//
+//   Status status = os.WriteTo(<some ObjectWriter>);
+class PROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource {
+ public:
+
+  struct RenderOptions {
+    RenderOptions() = default;
+    RenderOptions(const RenderOptions&) = default;
+
+    // Sets whether or not to use lowerCamelCase casing for enum values. If set
+    // to false, enum values are output without any case conversions.
+    //
+    // For example, if we have an enum:
+    // enum Type {
+    //   ACTION_AND_ADVENTURE = 1;
+    // }
+    // Type type = 20;
+    //
+    // And this option is set to true. Then the rendered "type" field will have
+    // the string "actionAndAdventure".
+    // {
+    //   ...
+    //   "type": "actionAndAdventure",
+    //   ...
+    // }
+    //
+    // If set to false, the rendered "type" field will have the string
+    // "ACTION_AND_ADVENTURE".
+    // {
+    //   ...
+    //   "type": "ACTION_AND_ADVENTURE",
+    //   ...
+    // }
+    bool use_lower_camel_for_enums = false;
+
+    // Sets whether to always output enums as ints, by default this is off, and
+    // enums are rendered as strings.
+    bool use_ints_for_enums = false;
+
+    // Whether to preserve proto field names
+    bool preserve_proto_field_names = false;
+
+  };
+
+  ProtoStreamObjectSource(io::CodedInputStream* stream,
+                          TypeResolver* type_resolver,
+                          const google::protobuf::Type& type)
+      : ProtoStreamObjectSource(stream, type_resolver, type, RenderOptions()) {}
+  ProtoStreamObjectSource(io::CodedInputStream* stream,
+                          TypeResolver* type_resolver,
+                          const google::protobuf::Type& type,
+                          const RenderOptions& render_options);
+
+  ~ProtoStreamObjectSource() override;
+
+  util::Status NamedWriteTo(StringPiece name,
+                            ObjectWriter* ow) const override;
+
+  // Sets the max recursion depth of proto message to be deserialized. Proto
+  // messages over this depth will fail to be deserialized.
+  // Default value is 64.
+  void set_max_recursion_depth(int max_depth) {
+    max_recursion_depth_ = max_depth;
+  }
+
+ protected:
+  // Writes a proto2 Message to the ObjectWriter. When the given end_tag is
+  // found this method will complete, allowing it to be used for parsing both
+  // nested messages (end with 0) and nested groups (end with group end tag).
+  // The include_start_and_end parameter allows this method to be called when
+  // already inside of an object, and skip calling StartObject and EndObject.
+  virtual util::Status WriteMessage(const google::protobuf::Type& type,
+                                    StringPiece name,
+                                    const uint32_t end_tag,
+                                    bool include_start_and_end,
+                                    ObjectWriter* ow) const;
+
+  // Renders a repeating field (packed or unpacked).  Returns the next tag after
+  // reading all sequential repeating elements. The caller should use this tag
+  // before reading more tags from the stream.
+  virtual util::StatusOr<uint32_t> RenderList(
+      const google::protobuf::Field* field, StringPiece name,
+      uint32_t list_tag, ObjectWriter* ow) const;
+
+  // Looks up a field and verify its consistency with wire type in tag.
+  const google::protobuf::Field* FindAndVerifyField(
+      const google::protobuf::Type& type, uint32_t tag) const;
+
+  // Renders a field value to the ObjectWriter.
+  virtual util::Status RenderField(const google::protobuf::Field* field,
+                                   StringPiece field_name,
+                                   ObjectWriter* ow) const;
+
+  // Reads field value according to Field spec in 'field' and returns the read
+  // value as string. This only works for primitive datatypes (no message
+  // types).
+  const std::string ReadFieldValueAsString(
+      const google::protobuf::Field& field) const;
+
+
+  // Returns the input stream.
+  io::CodedInputStream* stream() const { return stream_; }
+
+ private:
+  ProtoStreamObjectSource(io::CodedInputStream* stream,
+                          const TypeInfo* typeinfo,
+                          const google::protobuf::Type& type,
+                          const RenderOptions& render_options);
+  // Function that renders a well known type with a modified behavior.
+  typedef util::Status (*TypeRenderer)(const ProtoStreamObjectSource*,
+                                       const google::protobuf::Type&,
+                                       StringPiece, ObjectWriter*);
+
+  // TODO(skarvaje): Mark these methods as non-const as they modify internal
+  // state (stream_).
+  //
+  // Renders a NWP map.
+  // Returns the next tag after reading all map entries. The caller should use
+  // this tag before reading more tags from the stream.
+  util::StatusOr<uint32_t> RenderMap(const google::protobuf::Field* field,
+                                     StringPiece name, uint32_t list_tag,
+                                     ObjectWriter* ow) const;
+
+  // Renders a packed repeating field. A packed field is stored as:
+  // {tag length item1 item2 item3} instead of the less efficient
+  // {tag item1 tag item2 tag item3}.
+  util::Status RenderPacked(const google::protobuf::Field* field,
+                            ObjectWriter* ow) const;
+
+  // Renders a google.protobuf.Timestamp value to ObjectWriter
+  static util::Status RenderTimestamp(const ProtoStreamObjectSource* os,
+                                      const google::protobuf::Type& type,
+                                      StringPiece name, ObjectWriter* ow);
+
+  // Renders a google.protobuf.Duration value to ObjectWriter
+  static util::Status RenderDuration(const ProtoStreamObjectSource* os,
+                                     const google::protobuf::Type& type,
+                                     StringPiece name, ObjectWriter* ow);
+
+  // Following RenderTYPE functions render well known types in
+  // google/protobuf/wrappers.proto corresponding to TYPE.
+  static util::Status RenderDouble(const ProtoStreamObjectSource* os,
+                                   const google::protobuf::Type& type,
+                                   StringPiece name, ObjectWriter* ow);
+  static util::Status RenderFloat(const ProtoStreamObjectSource* os,
+                                  const google::protobuf::Type& type,
+                                  StringPiece name, ObjectWriter* ow);
+  static util::Status RenderInt64(const ProtoStreamObjectSource* os,
+                                  const google::protobuf::Type& type,
+                                  StringPiece name, ObjectWriter* ow);
+  static util::Status RenderUInt64(const ProtoStreamObjectSource* os,
+                                   const google::protobuf::Type& type,
+                                   StringPiece name, ObjectWriter* ow);
+  static util::Status RenderInt32(const ProtoStreamObjectSource* os,
+                                  const google::protobuf::Type& type,
+                                  StringPiece name, ObjectWriter* ow);
+  static util::Status RenderUInt32(const ProtoStreamObjectSource* os,
+                                   const google::protobuf::Type& type,
+                                   StringPiece name, ObjectWriter* ow);
+  static util::Status RenderBool(const ProtoStreamObjectSource* os,
+                                 const google::protobuf::Type& type,
+                                 StringPiece name, ObjectWriter* ow);
+  static util::Status RenderString(const ProtoStreamObjectSource* os,
+                                   const google::protobuf::Type& type,
+                                   StringPiece name, ObjectWriter* ow);
+  static util::Status RenderBytes(const ProtoStreamObjectSource* os,
+                                  const google::protobuf::Type& type,
+                                  StringPiece name, ObjectWriter* ow);
+
+  // Renders a google.protobuf.Struct to ObjectWriter.
+  static util::Status RenderStruct(const ProtoStreamObjectSource* os,
+                                   const google::protobuf::Type& type,
+                                   StringPiece name, ObjectWriter* ow);
+
+  // Helper to render google.protobuf.Struct's Value fields to ObjectWriter.
+  static util::Status RenderStructValue(const ProtoStreamObjectSource* os,
+                                        const google::protobuf::Type& type,
+                                        StringPiece name,
+                                        ObjectWriter* ow);
+
+  // Helper to render google.protobuf.Struct's ListValue fields to ObjectWriter.
+  static util::Status RenderStructListValue(const ProtoStreamObjectSource* os,
+                                            const google::protobuf::Type& type,
+                                            StringPiece name,
+                                            ObjectWriter* ow);
+
+  // Render the "Any" type.
+  static util::Status RenderAny(const ProtoStreamObjectSource* os,
+                                const google::protobuf::Type& type,
+                                StringPiece name, ObjectWriter* ow);
+
+  // Render the "FieldMask" type.
+  static util::Status RenderFieldMask(const ProtoStreamObjectSource* os,
+                                      const google::protobuf::Type& type,
+                                      StringPiece name, ObjectWriter* ow);
+
+  static std::unordered_map<std::string, TypeRenderer>* renderers_;
+  static void InitRendererMap();
+  static void DeleteRendererMap();
+  static TypeRenderer* FindTypeRenderer(const std::string& type_url);
+
+  // Same as above but renders all non-message field types. Callers don't call
+  // this function directly. They just use RenderField.
+  util::Status RenderNonMessageField(const google::protobuf::Field* field,
+                                     StringPiece field_name,
+                                     ObjectWriter* ow) const;
+
+
+  // Utility function to detect proto maps. The 'field' MUST be repeated.
+  bool IsMap(const google::protobuf::Field& field) const;
+
+  // Utility to read int64 and int32 values from a message type in stream_.
+  // Used for reading google.protobuf.Timestamp and Duration messages.
+  std::pair<int64_t, int32_t> ReadSecondsAndNanos(
+      const google::protobuf::Type& type) const;
+
+  // Helper function to check recursion depth and increment it. It will return
+  // OkStatus() if the current depth is allowed. Otherwise an error is returned.
+  // type_name and field_name are used for error reporting.
+  util::Status IncrementRecursionDepth(StringPiece type_name,
+                                       StringPiece field_name) const;
+
+  // Input stream to read from. Ownership rests with the caller.
+  mutable io::CodedInputStream* stream_;
+
+  // Type information for all the types used in the descriptor. Used to find
+  // google::protobuf::Type of nested messages/enums.
+  const TypeInfo* typeinfo_;
+
+  // Whether this class owns the typeinfo_ object. If true the typeinfo_ object
+  // should be deleted in the destructor.
+  bool own_typeinfo_;
+
+  // google::protobuf::Type of the message source.
+  const google::protobuf::Type& type_;
+
+
+  const RenderOptions render_options_;
+
+  // Tracks current recursion depth.
+  mutable int recursion_depth_;
+
+  // Maximum allowed recursion depth.
+  int max_recursion_depth_;
+
+  GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectSource);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_PROTOSTREAM_OBJECTSOURCE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/protostream_objectwriter.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/protostream_objectwriter.h
new file mode 100644
index 0000000..ce2517f
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/protostream_objectwriter.h
@@ -0,0 +1,453 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_PROTOSTREAM_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_PROTOSTREAM_OBJECTWRITER_H__
+
+#include <deque>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/stubs/bytestream.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/util/internal/datapiece.h>
+#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/proto_writer.h>
+#include <google/protobuf/util/internal/structured_objectwriter.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/hash.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class ObjectLocationTracker;
+
+// An ObjectWriter that can write protobuf bytes directly from writer events.
+// This class supports all special types like Struct and Map. It uses
+// the ProtoWriter class to write raw proto bytes.
+//
+// It also supports streaming.
+class PROTOBUF_EXPORT ProtoStreamObjectWriter : public ProtoWriter {
+ public:
+  // Options that control ProtoStreamObjectWriter class's behavior.
+  struct Options {
+    // Treats numeric inputs in google.protobuf.Struct as strings. Normally,
+    // numeric values are returned in double field "number_value" of
+    // google.protobuf.Struct. However, this can cause precision loss for
+    // int64/uint64/double inputs. This option is provided for cases that want
+    // to preserve number precision.
+    //
+    // TODO(skarvaje): Rename to struct_numbers_as_strings as it covers double
+    // as well.
+    bool struct_integers_as_strings;
+
+    // Not treat unknown fields as an error. If there is an unknown fields,
+    // just ignore it and continue to process the rest. Note that this doesn't
+    // apply to unknown enum values.
+    bool ignore_unknown_fields;
+
+    // Ignore unknown enum values.
+    bool ignore_unknown_enum_values;
+
+    // If true, check if enum name in camel case or without underscore matches
+    // the field name.
+    bool use_lower_camel_for_enums;
+
+    // If true, check if enum name in UPPER_CASE matches the field name.
+    bool case_insensitive_enum_parsing;
+
+    // If true, skips rendering the map entry if map value is null unless the
+    // value type is google.protobuf.NullType.
+    bool ignore_null_value_map_entry;
+
+    // If true, accepts repeated key/value pair for a map proto field.
+    bool use_legacy_json_map_format;
+
+    // If true, disable implicitly creating message list.
+    bool disable_implicit_message_list;
+
+    // If true, suppress the error of implicitly creating message list when it
+    // is disabled.
+    bool suppress_implicit_message_list_error;
+
+    // If true, disable implicitly creating scalar list.
+    bool disable_implicit_scalar_list;
+
+    // If true, suppress the error of implicitly creating scalar list when it
+    // is disabled.
+    bool suppress_implicit_scalar_list_error;
+
+    // If true, suppress the error of rendering scalar field if the source is an
+    // object.
+    bool suppress_object_to_scalar_error;
+
+    // If true, use the json name in missing fields errors.
+    bool use_json_name_in_missing_fields;
+
+    Options()
+        : struct_integers_as_strings(false),
+          ignore_unknown_fields(false),
+          ignore_unknown_enum_values(false),
+          use_lower_camel_for_enums(false),
+          case_insensitive_enum_parsing(false),
+          ignore_null_value_map_entry(false),
+          use_legacy_json_map_format(false),
+          disable_implicit_message_list(false),
+          suppress_implicit_message_list_error(false),
+          disable_implicit_scalar_list(false),
+          suppress_implicit_scalar_list_error(false),
+          suppress_object_to_scalar_error(false),
+          use_json_name_in_missing_fields(false) {}
+
+    // Default instance of Options with all options set to defaults.
+    static const Options& Defaults() {
+      static Options defaults;
+      return defaults;
+    }
+  };
+
+  // Constructor. Does not take ownership of any parameter passed in.
+  ProtoStreamObjectWriter(TypeResolver* type_resolver,
+                          const google::protobuf::Type& type,
+                          strings::ByteSink* output, ErrorListener* listener,
+                          const ProtoStreamObjectWriter::Options& options =
+                              ProtoStreamObjectWriter::Options::Defaults());
+  ~ProtoStreamObjectWriter() override;
+
+  // ObjectWriter methods.
+  ProtoStreamObjectWriter* StartObject(StringPiece name) override;
+  ProtoStreamObjectWriter* EndObject() override;
+  ProtoStreamObjectWriter* StartList(StringPiece name) override;
+  ProtoStreamObjectWriter* EndList() override;
+
+  // Renders a DataPiece 'value' into a field whose wire type is determined
+  // from the given field 'name'.
+  ProtoStreamObjectWriter* RenderDataPiece(StringPiece name,
+                                           const DataPiece& data) override;
+
+ protected:
+  // Function that renders a well known type with modified behavior.
+  typedef util::Status (*TypeRenderer)(ProtoStreamObjectWriter*,
+                                       const DataPiece&);
+
+  // Handles writing Anys out using nested object writers and the like.
+  class PROTOBUF_EXPORT AnyWriter {
+   public:
+    explicit AnyWriter(ProtoStreamObjectWriter* parent);
+    ~AnyWriter();
+
+    // Passes a StartObject call through to the Any writer.
+    void StartObject(StringPiece name);
+
+    // Passes an EndObject call through to the Any. Returns true if the any
+    // handled the EndObject call, false if the Any is now all done and is no
+    // longer needed.
+    bool EndObject();
+
+    // Passes a StartList call through to the Any writer.
+    void StartList(StringPiece name);
+
+    // Passes an EndList call through to the Any writer.
+    void EndList();
+
+    // Renders a data piece on the any.
+    void RenderDataPiece(StringPiece name, const DataPiece& value);
+
+   private:
+    // Before the "@type" field is encountered, we store all incoming data
+    // into this Event struct and replay them after we get the "@type" field.
+    class PROTOBUF_EXPORT Event {
+     public:
+      enum Type {
+        START_OBJECT = 0,
+        END_OBJECT = 1,
+        START_LIST = 2,
+        END_LIST = 3,
+        RENDER_DATA_PIECE = 4,
+      };
+
+      // Constructor for END_OBJECT and END_LIST events.
+      explicit Event(Type type) : type_(type), value_(DataPiece::NullData()) {}
+
+      // Constructor for START_OBJECT and START_LIST events.
+      explicit Event(Type type, StringPiece name)
+          : type_(type), name_(name), value_(DataPiece::NullData()) {}
+
+      // Constructor for RENDER_DATA_PIECE events.
+      explicit Event(StringPiece name, const DataPiece& value)
+          : type_(RENDER_DATA_PIECE), name_(name), value_(value) {
+        DeepCopy();
+      }
+
+      Event(const Event& other)
+          : type_(other.type_), name_(other.name_), value_(other.value_) {
+        DeepCopy();
+      }
+
+      Event& operator=(const Event& other) {
+        type_ = other.type_;
+        name_ = other.name_;
+        value_ = other.value_;
+        DeepCopy();
+        return *this;
+      }
+
+      void Replay(AnyWriter* writer) const;
+
+     private:
+      void DeepCopy();
+
+      Type type_;
+      std::string name_;
+      DataPiece value_;
+      std::string value_storage_;
+    };
+
+    // Handles starting up the any once we have a type.
+    void StartAny(const DataPiece& value);
+
+    // Writes the Any out to the parent writer in its serialized form.
+    void WriteAny();
+
+    // The parent of this writer, needed for various bits such as type info and
+    // the listeners.
+    ProtoStreamObjectWriter* parent_;
+
+    // The nested object writer, used to write events.
+    std::unique_ptr<ProtoStreamObjectWriter> ow_;
+
+    // The type_url_ that this Any represents.
+    std::string type_url_;
+
+    // Whether this any is invalid. This allows us to only report an invalid
+    // Any message a single time rather than every time we get a nested field.
+    bool invalid_;
+
+    // The output data and wrapping ByteSink.
+    std::string data_;
+    strings::StringByteSink output_;
+
+    // The depth within the Any, so we can track when we're done.
+    int depth_;
+
+    // True if the type is a well-known type. Well-known types in Any
+    // has a special formatting:
+    // {
+    //   "@type": "type.googleapis.com/google.protobuf.XXX",
+    //   "value": <JSON representation of the type>,
+    // }
+    bool is_well_known_type_;
+    TypeRenderer* well_known_type_render_;
+
+    // Store data before the "@type" field.
+    std::vector<Event> uninterpreted_events_;
+  };
+
+  // Represents an item in a stack of items used to keep state between
+  // ObjectWrier events.
+  class PROTOBUF_EXPORT Item : public BaseElement {
+   public:
+    // Indicates the type of item.
+    enum ItemType {
+      MESSAGE,  // Simple message
+      MAP,      // Proto3 map type
+      ANY,      // Proto3 Any type
+    };
+
+    // Constructor for the root item.
+    Item(ProtoStreamObjectWriter* enclosing, ItemType item_type,
+         bool is_placeholder, bool is_list);
+
+    // Constructor for a field of a message.
+    Item(Item* parent, ItemType item_type, bool is_placeholder, bool is_list);
+
+    ~Item() override {}
+
+    // These functions return true if the element type is corresponding to the
+    // type in function name.
+    bool IsMap() { return item_type_ == MAP; }
+    bool IsAny() { return item_type_ == ANY; }
+
+    AnyWriter* any() const { return any_.get(); }
+
+    Item* parent() const override {
+      return static_cast<Item*>(BaseElement::parent());
+    }
+
+    // Inserts map key into hash set if and only if the key did NOT already
+    // exist in hash set.
+    // The hash set (map_keys_) is ONLY used to keep track of map keys.
+    // Return true if insert successfully; returns false if the map key was
+    // already present.
+    bool InsertMapKeyIfNotPresent(StringPiece map_key);
+
+    bool is_placeholder() const { return is_placeholder_; }
+    bool is_list() const { return is_list_; }
+
+   private:
+    // Used for access to variables of the enclosing instance of
+    // ProtoStreamObjectWriter.
+    ProtoStreamObjectWriter* ow_;
+
+    // A writer for Any objects, handles all Any-related nonsense.
+    std::unique_ptr<AnyWriter> any_;
+
+    // The type of this element, see enum for permissible types.
+    ItemType item_type_;
+
+    // Set of map keys already seen for the type_. Used to validate incoming
+    // messages so no map key appears more than once.
+    std::unique_ptr<std::unordered_set<std::string> > map_keys_;
+
+    // Conveys whether this Item is a placeholder or not. Placeholder items are
+    // pushed to stack to account for special types.
+    bool is_placeholder_;
+
+    // Conveys whether this Item is a list or not. This is used to send
+    // StartList or EndList calls to underlying ObjectWriter.
+    bool is_list_;
+
+    GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(Item);
+  };
+
+  ProtoStreamObjectWriter(const TypeInfo* typeinfo,
+                          const google::protobuf::Type& type,
+                          strings::ByteSink* output, ErrorListener* listener);
+
+  ProtoStreamObjectWriter(const TypeInfo* typeinfo,
+                          const google::protobuf::Type& type,
+                          strings::ByteSink* output, ErrorListener* listener,
+                          const ProtoStreamObjectWriter::Options& options);
+
+  // Returns true if the field is a map.
+  inline bool IsMap(const google::protobuf::Field& field);
+
+  // Returns true if the field is an any.
+  inline bool IsAny(const google::protobuf::Field& field);
+
+  // Returns true if the field is google.protobuf.Struct.
+  inline bool IsStruct(const google::protobuf::Field& field);
+
+  // Returns true if the field is google.protobuf.Value.
+  inline bool IsStructValue(const google::protobuf::Field& field);
+
+  // Returns true if the field is google.protobuf.ListValue.
+  inline bool IsStructListValue(const google::protobuf::Field& field);
+
+  // Renders google.protobuf.Value in struct.proto. It picks the right oneof
+  // type based on value's type.
+  static util::Status RenderStructValue(ProtoStreamObjectWriter* ow,
+                                        const DataPiece& data);
+
+  // Renders google.protobuf.Timestamp value.
+  static util::Status RenderTimestamp(ProtoStreamObjectWriter* ow,
+                                      const DataPiece& data);
+
+  // Renders google.protobuf.FieldMask value.
+  static util::Status RenderFieldMask(ProtoStreamObjectWriter* ow,
+                                      const DataPiece& data);
+
+  // Renders google.protobuf.Duration value.
+  static util::Status RenderDuration(ProtoStreamObjectWriter* ow,
+                                     const DataPiece& data);
+
+  // Renders wrapper message types for primitive types in
+  // google/protobuf/wrappers.proto.
+  static util::Status RenderWrapperType(ProtoStreamObjectWriter* ow,
+                                        const DataPiece& data);
+
+  static void InitRendererMap();
+  static void DeleteRendererMap();
+  static TypeRenderer* FindTypeRenderer(const std::string& type_url);
+
+  // Returns true if the map key for type_ is not duplicated key.
+  // If map key is duplicated key, this function returns false.
+  // Note that caller should make sure that the current proto element (current_)
+  // is of element type MAP or STRUCT_MAP.
+  // It also calls the appropriate error callback and unnormalzied_name is used
+  // for error string.
+  bool ValidMapKey(StringPiece unnormalized_name);
+
+  // Pushes an item on to the stack. Also calls either StartObject or StartList
+  // on the underlying ObjectWriter depending on whether is_list is false or
+  // not.
+  // is_placeholder conveys whether the item is a placeholder item or not.
+  // Placeholder items are pushed when adding auxiliary types' StartObject or
+  // StartList calls.
+  void Push(StringPiece name, Item::ItemType item_type,
+            bool is_placeholder, bool is_list);
+
+
+  // Pops items from the stack. All placeholder items are popped until a
+  // non-placeholder item is found.
+  void Pop();
+
+  // Pops one element from the stack. Calls EndObject() or EndList() on the
+  // underlying ObjectWriter depending on the value of is_list_.
+  void PopOneElement();
+
+ private:
+  // Helper functions to create the map and find functions responsible for
+  // rendering well known types, keyed by type URL.
+  static std::unordered_map<std::string, TypeRenderer>* renderers_;
+
+  // Variables for describing the structure of the input tree:
+  // master_type_: descriptor for the whole protobuf message.
+  const google::protobuf::Type& master_type_;
+
+  // The current element, variable for internal state processing.
+  std::unique_ptr<Item> current_;
+
+  // Reference to the options that control this class's behavior.
+  const ProtoStreamObjectWriter::Options options_;
+
+  GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectWriter);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_PROTOSTREAM_OBJECTWRITER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/structured_objectwriter.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/structured_objectwriter.h
new file mode 100644
index 0000000..f6f7c89
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/structured_objectwriter.h
@@ -0,0 +1,121 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_STRUCTURED_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_STRUCTURED_OBJECTWRITER_H__
+
+#include <memory>
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/util/internal/object_writer.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// An StructuredObjectWriter is an ObjectWriter for writing
+// tree-structured data in a stream of events representing objects
+// and collections. Implementation of this interface can be used to
+// write an object stream to an in-memory structure, protobufs,
+// JSON, XML, or any other output format desired. The ObjectSource
+// interface is typically used as the source of an object stream.
+//
+// See JsonObjectWriter for a sample implementation of
+// StructuredObjectWriter and its use.
+//
+// Derived classes could be thread-unsafe.
+class PROTOBUF_EXPORT StructuredObjectWriter : public ObjectWriter {
+ public:
+  ~StructuredObjectWriter() override {}
+
+ protected:
+  // A base element class for subclasses to extend, makes tracking state easier.
+  //
+  // StructuredObjectWriter behaves as a visitor. BaseElement represents a node
+  // in the input tree. Implementation of StructuredObjectWriter should also
+  // extend BaseElement to keep track of the location in the input tree.
+  class PROTOBUF_EXPORT BaseElement {
+   public:
+    // Takes ownership of the parent Element.
+    explicit BaseElement(BaseElement* parent)
+        : parent_(parent),
+          level_(parent == nullptr ? 0 : parent->level() + 1) {}
+    virtual ~BaseElement() {}
+
+    // Releases ownership of the parent and returns a pointer to it.
+    template <typename ElementType>
+    ElementType* pop() {
+      return down_cast<ElementType*>(parent_.release());
+    }
+
+    // Returns true if this element is the root.
+    bool is_root() const { return parent_ == nullptr; }
+
+    // Returns the number of hops from this element to the root element.
+    int level() const { return level_; }
+
+   protected:
+    // Returns pointer to parent element without releasing ownership.
+    virtual BaseElement* parent() const { return parent_.get(); }
+
+   private:
+    // Pointer to the parent Element.
+    std::unique_ptr<BaseElement> parent_;
+
+    // Number of hops to the root Element.
+    // The root Element has nullptr parent_ and a level_ of 0.
+    const int level_;
+
+    GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(BaseElement);
+  };
+
+  StructuredObjectWriter() {}
+
+  // Returns the current element. Used for indentation and name overrides.
+  virtual BaseElement* element() = 0;
+
+ private:
+  // Do not add any data members to this class.
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StructuredObjectWriter);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_STRUCTURED_OBJECTWRITER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/type_info.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/type_info.h
new file mode 100644
index 0000000..257df5b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/type_info.h
@@ -0,0 +1,97 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_TYPE_INFO_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_TYPE_INFO_H__
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/stubs/statusor.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/status.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+// Internal helper class for type resolving. Note that this class is not
+// thread-safe and should only be accessed in one thread.
+class PROTOBUF_EXPORT TypeInfo {
+ public:
+  TypeInfo() {}
+  virtual ~TypeInfo() {}
+
+  // Resolves a type url into a Type. If the type url is invalid, returns
+  // INVALID_ARGUMENT error status. If the type url is valid but the
+  // corresponding type cannot be found, returns a NOT_FOUND error status.
+  //
+  // This TypeInfo class retains the ownership of the returned pointer.
+  virtual util::StatusOr<const google::protobuf::Type*> ResolveTypeUrl(
+      StringPiece type_url) const = 0;
+
+  // Resolves a type url into a Type. Like ResolveTypeUrl() but returns
+  // NULL if the type url is invalid or the type cannot be found.
+  //
+  // This TypeInfo class retains the ownership of the returned pointer.
+  virtual const google::protobuf::Type* GetTypeByTypeUrl(
+      StringPiece type_url) const = 0;
+
+  // Resolves a type url for an enum. Returns NULL if the type url is
+  // invalid or the type cannot be found.
+  //
+  // This TypeInfo class retains the ownership of the returned pointer.
+  virtual const google::protobuf::Enum* GetEnumByTypeUrl(
+      StringPiece type_url) const = 0;
+
+  // Looks up a field in the specified type given a CamelCase name.
+  virtual const google::protobuf::Field* FindField(
+      const google::protobuf::Type* type,
+      StringPiece camel_case_name) const = 0;
+
+  // Creates a TypeInfo object that looks up type information from a
+  // TypeResolver. Caller takes ownership of the returned pointer.
+  static TypeInfo* NewTypeInfo(TypeResolver* type_resolver);
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeInfo);
+};
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_TYPE_INFO_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/utility.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/utility.h
new file mode 100644
index 0000000..79d6779
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/internal/utility.h
@@ -0,0 +1,204 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_UTILITY_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_UTILITY_H__
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/any.pb.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/statusor.h>
+#include <google/protobuf/stubs/status.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// Size of "type.googleapis.com"
+static const int64_t kTypeUrlSize = 19;
+
+// Finds the tech option identified by option_name. Parses the boolean value and
+// returns it.
+// When the option with the given name is not found, default_value is returned.
+PROTOBUF_EXPORT bool GetBoolOptionOrDefault(
+    const RepeatedPtrField<google::protobuf::Option>& options,
+    StringPiece option_name, bool default_value);
+
+// Returns int64 option value. If the option isn't found, returns the
+// default_value.
+PROTOBUF_EXPORT int64_t GetInt64OptionOrDefault(
+    const RepeatedPtrField<google::protobuf::Option>& options,
+    StringPiece option_name, int64_t default_value);
+
+// Returns double option value. If the option isn't found, returns the
+// default_value.
+PROTOBUF_EXPORT double GetDoubleOptionOrDefault(
+    const RepeatedPtrField<google::protobuf::Option>& options,
+    StringPiece option_name, double default_value);
+
+// Returns string option value. If the option isn't found, returns the
+// default_value.
+PROTOBUF_EXPORT std::string GetStringOptionOrDefault(
+    const RepeatedPtrField<google::protobuf::Option>& options,
+    StringPiece option_name, StringPiece default_value);
+
+// Returns a boolean value contained in Any type.
+// TODO(skarvaje): Make these utilities dealing with Any types more generic,
+// add more error checking and move to a more public/shareable location so
+// others can use.
+PROTOBUF_EXPORT bool GetBoolFromAny(const google::protobuf::Any& any);
+
+// Returns int64 value contained in Any type.
+PROTOBUF_EXPORT int64_t GetInt64FromAny(const google::protobuf::Any& any);
+
+// Returns double value contained in Any type.
+PROTOBUF_EXPORT double GetDoubleFromAny(const google::protobuf::Any& any);
+
+// Returns string value contained in Any type.
+PROTOBUF_EXPORT std::string GetStringFromAny(const google::protobuf::Any& any);
+
+// Returns the type string without the url prefix. e.g.: If the passed type is
+// 'type.googleapis.com/tech.type.Bool', the returned value is 'tech.type.Bool'.
+PROTOBUF_EXPORT const StringPiece GetTypeWithoutUrl(
+    StringPiece type_url);
+
+// Returns the simple_type with the base type url (kTypeServiceBaseUrl)
+// prefixed.
+//
+// E.g:
+// GetFullTypeWithUrl("google.protobuf.Timestamp") returns the string
+// "type.googleapis.com/google.protobuf.Timestamp".
+PROTOBUF_EXPORT const std::string GetFullTypeWithUrl(
+    StringPiece simple_type);
+
+// Finds and returns option identified by name and option_name within the
+// provided map. Returns nullptr if none found.
+const google::protobuf::Option* FindOptionOrNull(
+    const RepeatedPtrField<google::protobuf::Option>& options,
+    StringPiece option_name);
+
+// Finds and returns the field identified by field_name in the passed tech Type
+// object. Returns nullptr if none found.
+const google::protobuf::Field* FindFieldInTypeOrNull(
+    const google::protobuf::Type* type, StringPiece field_name);
+
+// Similar to FindFieldInTypeOrNull, but this looks up fields with given
+// json_name.
+const google::protobuf::Field* FindJsonFieldInTypeOrNull(
+    const google::protobuf::Type* type, StringPiece json_name);
+
+// Similar to FindFieldInTypeOrNull, but this looks up fields by number.
+const google::protobuf::Field* FindFieldInTypeByNumberOrNull(
+    const google::protobuf::Type* type, int32_t number);
+
+// Finds and returns the EnumValue identified by enum_name in the passed tech
+// Enum object. Returns nullptr if none found.
+const google::protobuf::EnumValue* FindEnumValueByNameOrNull(
+    const google::protobuf::Enum* enum_type, StringPiece enum_name);
+
+// Finds and returns the EnumValue identified by value in the passed tech
+// Enum object. Returns nullptr if none found.
+const google::protobuf::EnumValue* FindEnumValueByNumberOrNull(
+    const google::protobuf::Enum* enum_type, int32_t value);
+
+// Finds and returns the EnumValue identified by enum_name without underscore in
+// the passed tech Enum object. Returns nullptr if none found.
+// For Ex. if enum_name is ACTIONANDADVENTURE it can get accepted if
+// EnumValue's name is action_and_adventure or ACTION_AND_ADVENTURE.
+const google::protobuf::EnumValue* FindEnumValueByNameWithoutUnderscoreOrNull(
+    const google::protobuf::Enum* enum_type, StringPiece enum_name);
+
+// Converts input to camel-case and returns it.
+PROTOBUF_EXPORT std::string ToCamelCase(const StringPiece input);
+
+// Converts enum name string to camel-case and returns it.
+std::string EnumValueNameToLowerCamelCase(const StringPiece input);
+
+// Converts input to snake_case and returns it.
+PROTOBUF_EXPORT std::string ToSnakeCase(StringPiece input);
+
+// Returns true if type_name represents a well-known type.
+PROTOBUF_EXPORT bool IsWellKnownType(const std::string& type_name);
+
+// Returns true if 'bool_string' represents a valid boolean value. Only "true",
+// "false", "0" and "1" are allowed.
+PROTOBUF_EXPORT bool IsValidBoolString(StringPiece bool_string);
+
+// Returns true if "field" is a protobuf map field based on its type.
+PROTOBUF_EXPORT bool IsMap(const google::protobuf::Field& field,
+                           const google::protobuf::Type& type);
+
+// Returns true if the given type has special MessageSet wire format.
+bool IsMessageSetWireFormat(const google::protobuf::Type& type);
+
+// Infinity/NaN-aware conversion to string.
+PROTOBUF_EXPORT std::string DoubleAsString(double value);
+PROTOBUF_EXPORT std::string FloatAsString(float value);
+
+// Convert from int32, int64, uint32, uint64, double or float to string.
+template <typename T>
+std::string ValueAsString(T value) {
+  return StrCat(value);
+}
+
+template <>
+inline std::string ValueAsString(float value) {
+  return FloatAsString(value);
+}
+
+template <>
+inline std::string ValueAsString(double value) {
+  return DoubleAsString(value);
+}
+
+// Converts a string to float. Unlike safe_strtof, conversion will fail if the
+// value fits into double but not float (e.g., DBL_MAX).
+PROTOBUF_EXPORT bool SafeStrToFloat(StringPiece str, float* value);
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_UTILITY_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/json_util.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/json_util.h
new file mode 100644
index 0000000..0f1c4d8b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/json_util.h
@@ -0,0 +1,204 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Utility functions to convert between protobuf binary format and proto3 JSON
+// format.
+#ifndef GOOGLE_PROTOBUF_UTIL_JSON_UTIL_H__
+#define GOOGLE_PROTOBUF_UTIL_JSON_UTIL_H__
+
+#include <google/protobuf/stubs/bytestream.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/util/type_resolver.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace io {
+class ZeroCopyInputStream;
+class ZeroCopyOutputStream;
+}  // namespace io
+namespace util {
+
+struct JsonParseOptions {
+  // Whether to ignore unknown JSON fields during parsing
+  bool ignore_unknown_fields;
+
+  // If true, when a lowercase enum value fails to parse, try convert it to
+  // UPPER_CASE and see if it matches a valid enum.
+  // WARNING: This option exists only to preserve legacy behavior. Avoid using
+  // this option. If your enum needs to support different casing, consider using
+  // allow_alias instead.
+  bool case_insensitive_enum_parsing;
+
+  JsonParseOptions()
+      : ignore_unknown_fields(false), case_insensitive_enum_parsing(false) {}
+};
+
+struct JsonPrintOptions {
+  // Whether to add spaces, line breaks and indentation to make the JSON output
+  // easy to read.
+  bool add_whitespace;
+  // Whether to always print primitive fields. By default proto3 primitive
+  // fields with default values will be omitted in JSON output. For example, an
+  // int32 field set to 0 will be omitted. Set this flag to true will override
+  // the default behavior and print primitive fields regardless of their values.
+  bool always_print_primitive_fields;
+  // Whether to always print enums as ints. By default they are rendered as
+  // strings.
+  bool always_print_enums_as_ints;
+  // Whether to preserve proto field names
+  bool preserve_proto_field_names;
+
+  JsonPrintOptions()
+      : add_whitespace(false),
+        always_print_primitive_fields(false),
+        always_print_enums_as_ints(false),
+        preserve_proto_field_names(false) {}
+};
+
+// DEPRECATED. Use JsonPrintOptions instead.
+typedef JsonPrintOptions JsonOptions;
+
+// Converts from protobuf message to JSON and appends it to |output|. This is a
+// simple wrapper of BinaryToJsonString(). It will use the DescriptorPool of the
+// passed-in message to resolve Any types.
+PROTOBUF_EXPORT util::Status MessageToJsonString(const Message& message,
+                                                 std::string* output,
+                                                 const JsonOptions& options);
+
+inline util::Status MessageToJsonString(const Message& message,
+                                        std::string* output) {
+  return MessageToJsonString(message, output, JsonOptions());
+}
+
+// Converts from JSON to protobuf message. This is a simple wrapper of
+// JsonStringToBinary(). It will use the DescriptorPool of the passed-in
+// message to resolve Any types.
+PROTOBUF_EXPORT util::Status JsonStringToMessage(
+    StringPiece input, Message* message, const JsonParseOptions& options);
+
+inline util::Status JsonStringToMessage(StringPiece input,
+                                        Message* message) {
+  return JsonStringToMessage(input, message, JsonParseOptions());
+}
+
+// Converts protobuf binary data to JSON.
+// The conversion will fail if:
+//   1. TypeResolver fails to resolve a type.
+//   2. input is not valid protobuf wire format, or conflicts with the type
+//      information returned by TypeResolver.
+// Note that unknown fields will be discarded silently.
+PROTOBUF_EXPORT util::Status BinaryToJsonStream(
+    TypeResolver* resolver, const std::string& type_url,
+    io::ZeroCopyInputStream* binary_input,
+    io::ZeroCopyOutputStream* json_output, const JsonPrintOptions& options);
+
+inline util::Status BinaryToJsonStream(TypeResolver* resolver,
+                                       const std::string& type_url,
+                                       io::ZeroCopyInputStream* binary_input,
+                                       io::ZeroCopyOutputStream* json_output) {
+  return BinaryToJsonStream(resolver, type_url, binary_input, json_output,
+                            JsonPrintOptions());
+}
+
+PROTOBUF_EXPORT util::Status BinaryToJsonString(
+    TypeResolver* resolver, const std::string& type_url,
+    const std::string& binary_input, std::string* json_output,
+    const JsonPrintOptions& options);
+
+inline util::Status BinaryToJsonString(TypeResolver* resolver,
+                                       const std::string& type_url,
+                                       const std::string& binary_input,
+                                       std::string* json_output) {
+  return BinaryToJsonString(resolver, type_url, binary_input, json_output,
+                            JsonPrintOptions());
+}
+
+// Converts JSON data to protobuf binary format.
+// The conversion will fail if:
+//   1. TypeResolver fails to resolve a type.
+//   2. input is not valid JSON format, or conflicts with the type
+//      information returned by TypeResolver.
+PROTOBUF_EXPORT util::Status JsonToBinaryStream(
+    TypeResolver* resolver, const std::string& type_url,
+    io::ZeroCopyInputStream* json_input,
+    io::ZeroCopyOutputStream* binary_output, const JsonParseOptions& options);
+
+inline util::Status JsonToBinaryStream(
+    TypeResolver* resolver, const std::string& type_url,
+    io::ZeroCopyInputStream* json_input,
+    io::ZeroCopyOutputStream* binary_output) {
+  return JsonToBinaryStream(resolver, type_url, json_input, binary_output,
+                            JsonParseOptions());
+}
+
+PROTOBUF_EXPORT util::Status JsonToBinaryString(
+    TypeResolver* resolver, const std::string& type_url,
+    StringPiece json_input, std::string* binary_output,
+    const JsonParseOptions& options);
+
+inline util::Status JsonToBinaryString(TypeResolver* resolver,
+                                       const std::string& type_url,
+                                       StringPiece json_input,
+                                       std::string* binary_output) {
+  return JsonToBinaryString(resolver, type_url, json_input, binary_output,
+                            JsonParseOptions());
+}
+
+namespace internal {
+// Internal helper class. Put in the header so we can write unit-tests for it.
+class PROTOBUF_EXPORT ZeroCopyStreamByteSink : public strings::ByteSink {
+ public:
+  explicit ZeroCopyStreamByteSink(io::ZeroCopyOutputStream* stream)
+      : stream_(stream), buffer_(nullptr), buffer_size_(0) {}
+  ~ZeroCopyStreamByteSink() override;
+
+  void Append(const char* bytes, size_t len) override;
+
+ private:
+  io::ZeroCopyOutputStream* stream_;
+  void* buffer_;
+  int buffer_size_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyStreamByteSink);
+};
+}  // namespace internal
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_JSON_UTIL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/message_differencer.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/message_differencer.h
new file mode 100644
index 0000000..f63cd54
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/message_differencer.h
@@ -0,0 +1,980 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: jschorr@google.com (Joseph Schorr)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This file defines static methods and classes for comparing Protocol
+// Messages.
+//
+// Aug. 2008: Added Unknown Fields Comparison for messages.
+// Aug. 2009: Added different options to compare repeated fields.
+// Apr. 2010: Moved field comparison to FieldComparator
+// Sep. 2020: Added option to output map keys in path
+
+#ifndef GOOGLE_PROTOBUF_UTIL_MESSAGE_DIFFERENCER_H__
+#define GOOGLE_PROTOBUF_UTIL_MESSAGE_DIFFERENCER_H__
+
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/descriptor.h>  // FieldDescriptor
+#include <google/protobuf/message.h>     // Message
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/util/field_comparator.h>
+
+// Always include as last one, otherwise it can break compilation
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+class DynamicMessageFactory;
+class FieldDescriptor;
+
+namespace io {
+class ZeroCopyOutputStream;
+class Printer;
+}  // namespace io
+
+namespace util {
+
+class DefaultFieldComparator;
+class FieldContext;  // declared below MessageDifferencer
+
+// Defines a collection of field descriptors.
+// In case of internal google codebase we are using absl::FixedArray instead
+// of vector. It significantly speeds up proto comparison (by ~30%) by
+// reducing the number of malloc/free operations
+typedef std::vector<const FieldDescriptor*> FieldDescriptorArray;
+
+// A basic differencer that can be used to determine
+// the differences between two specified Protocol Messages. If any differences
+// are found, the Compare method will return false, and any differencer reporter
+// specified via ReportDifferencesTo will have its reporting methods called (see
+// below for implementation of the report). Based off of the original
+// ProtocolDifferencer implementation in //net/proto/protocol-differencer.h
+// (Thanks Todd!).
+//
+// MessageDifferencer REQUIRES that compared messages be the same type, defined
+// as messages that share the same descriptor.  If not, the behavior of this
+// class is undefined.
+//
+// People disagree on what MessageDifferencer should do when asked to compare
+// messages with different descriptors.  Some people think it should always
+// return false.  Others expect it to try to look for similar fields and
+// compare them anyway -- especially if the descriptors happen to be identical.
+// If we chose either of these behaviors, some set of people would find it
+// surprising, and could end up writing code expecting the other behavior
+// without realizing their error.  Therefore, we forbid that usage.
+//
+// This class is implemented based on the proto2 reflection. The performance
+// should be good enough for normal usages. However, for places where the
+// performance is extremely sensitive, there are several alternatives:
+// - Comparing serialized string
+// Downside: false negatives (there are messages that are the same but their
+// serialized strings are different).
+// - Equals code generator by compiler plugin (net/proto2/contrib/equals_plugin)
+// Downside: more generated code; maintenance overhead for the additional rule
+// (must be in sync with the original proto_library).
+//
+// Note on handling of google.protobuf.Any: MessageDifferencer automatically
+// unpacks Any::value into a Message and compares its individual fields.
+// Messages encoded in a repeated Any cannot be compared using TreatAsMap.
+//
+// Note on thread-safety: MessageDifferencer is *not* thread-safe. You need to
+// guard it with a lock to use the same MessageDifferencer instance from
+// multiple threads. Note that it's fine to call static comparison methods
+// (like MessageDifferencer::Equals) concurrently, but it's not recommended for
+// performance critical code as it leads to extra allocations.
+class PROTOBUF_EXPORT MessageDifferencer {
+ public:
+  // Determines whether the supplied messages are equal. Equality is defined as
+  // all fields within the two messages being set to the same value. Primitive
+  // fields and strings are compared by value while embedded messages/groups
+  // are compared as if via a recursive call. Use Compare() with IgnoreField()
+  // if some fields should be ignored in the comparison. Use Compare() with
+  // TreatAsSet() if there are repeated fields where ordering does not matter.
+  //
+  // This method REQUIRES that the two messages have the same
+  // Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
+  static bool Equals(const Message& message1, const Message& message2);
+
+  // Determines whether the supplied messages are equivalent. Equivalency is
+  // defined as all fields within the two messages having the same value. This
+  // differs from the Equals method above in that fields with default values
+  // are considered set to said value automatically. For details on how default
+  // values are defined for each field type, see:
+  // https://developers.google.com/protocol-buffers/docs/proto?csw=1#optional.
+  // Also, Equivalent() ignores unknown fields. Use IgnoreField() and Compare()
+  // if some fields should be ignored in the comparison.
+  //
+  // This method REQUIRES that the two messages have the same
+  // Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
+  static bool Equivalent(const Message& message1, const Message& message2);
+
+  // Determines whether the supplied messages are approximately equal.
+  // Approximate equality is defined as all fields within the two messages
+  // being approximately equal.  Primitive (non-float) fields and strings are
+  // compared by value, floats are compared using MathUtil::AlmostEquals() and
+  // embedded messages/groups are compared as if via a recursive call. Use
+  // IgnoreField() and Compare() if some fields should be ignored in the
+  // comparison.
+  //
+  // This method REQUIRES that the two messages have the same
+  // Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
+  static bool ApproximatelyEquals(const Message& message1,
+                                  const Message& message2);
+
+  // Determines whether the supplied messages are approximately equivalent.
+  // Approximate equivalency is defined as all fields within the two messages
+  // being approximately equivalent. As in
+  // MessageDifferencer::ApproximatelyEquals, primitive (non-float) fields and
+  // strings are compared by value, floats are compared using
+  // MathUtil::AlmostEquals() and embedded messages/groups are compared as if
+  // via a recursive call. However, fields with default values are considered
+  // set to said value, as per MessageDiffencer::Equivalent. Use IgnoreField()
+  // and Compare() if some fields should be ignored in the comparison.
+  //
+  // This method REQUIRES that the two messages have the same
+  // Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
+  static bool ApproximatelyEquivalent(const Message& message1,
+                                      const Message& message2);
+
+  // Identifies an individual field in a message instance.  Used for field_path,
+  // below.
+  struct SpecificField {
+    // For known fields, "field" is filled in and "unknown_field_number" is -1.
+    // For unknown fields, "field" is NULL, "unknown_field_number" is the field
+    // number, and "unknown_field_type" is its type.
+    const FieldDescriptor* field = nullptr;
+    int unknown_field_number = -1;
+    UnknownField::Type unknown_field_type = UnknownField::Type::TYPE_VARINT;
+
+    // If this a repeated field, "index" is the index within it.  For unknown
+    // fields, this is the index of the field among all unknown fields of the
+    // same field number and type.
+    int index = -1;
+
+    // If "field" is a repeated field which is being treated as a map or
+    // a set (see TreatAsMap() and TreatAsSet(), below), new_index indicates
+    // the index the position to which the element has moved.  If the element
+    // has not moved, "new_index" will have the same value as "index".
+    int new_index = -1;
+
+    // If "field" is a map field, point to the map entry.
+    const Message* map_entry1 = nullptr;
+    const Message* map_entry2 = nullptr;
+
+    // For unknown fields, these are the pointers to the UnknownFieldSet
+    // containing the unknown fields. In certain cases (e.g. proto1's
+    // MessageSet, or nested groups of unknown fields), these may differ from
+    // the messages' internal UnknownFieldSets.
+    const UnknownFieldSet* unknown_field_set1 = nullptr;
+    const UnknownFieldSet* unknown_field_set2 = nullptr;
+
+    // For unknown fields, these are the index of the field within the
+    // UnknownFieldSets. One or the other will be -1 when
+    // reporting an addition or deletion.
+    int unknown_field_index1 = -1;
+    int unknown_field_index2 = -1;
+  };
+
+  // Abstract base class from which all MessageDifferencer
+  // reporters derive. The five Report* methods below will be called when
+  // a field has been added, deleted, modified, moved, or matched. The third
+  // argument is a vector of FieldDescriptor pointers which describes the chain
+  // of fields that was taken to find the current field. For example, for a
+  // field found in an embedded message, the vector will contain two
+  // FieldDescriptors. The first will be the field of the embedded message
+  // itself and the second will be the actual field in the embedded message
+  // that was added/deleted/modified.
+  // Fields will be reported in PostTraversalOrder.
+  // For example, given following proto, if both baz and mooo are changed.
+  // foo {
+  //   bar {
+  //     baz: 1
+  //     mooo: 2
+  //   }
+  // }
+  // ReportModified will be invoked with following order:
+  // 1. foo.bar.baz or foo.bar.mooo
+  // 2. foo.bar.mooo or foo.bar.baz
+  // 2. foo.bar
+  // 3. foo
+  class PROTOBUF_EXPORT Reporter {
+   public:
+    Reporter();
+    virtual ~Reporter();
+
+    // Reports that a field has been added into Message2.
+    virtual void ReportAdded(const Message& message1, const Message& message2,
+                             const std::vector<SpecificField>& field_path) = 0;
+
+    // Reports that a field has been deleted from Message1.
+    virtual void ReportDeleted(
+        const Message& message1, const Message& message2,
+        const std::vector<SpecificField>& field_path) = 0;
+
+    // Reports that the value of a field has been modified.
+    virtual void ReportModified(
+        const Message& message1, const Message& message2,
+        const std::vector<SpecificField>& field_path) = 0;
+
+    // Reports that a repeated field has been moved to another location.  This
+    // only applies when using TreatAsSet or TreatAsMap()  -- see below. Also
+    // note that for any given field, ReportModified and ReportMoved are
+    // mutually exclusive. If a field has been both moved and modified, then
+    // only ReportModified will be called.
+    virtual void ReportMoved(
+        const Message& /* message1 */, const Message& /* message2 */,
+        const std::vector<SpecificField>& /* field_path */) {}
+
+    // Reports that two fields match. Useful for doing side-by-side diffs.
+    // This function is mutually exclusive with ReportModified and ReportMoved.
+    // Note that you must call set_report_matches(true) before calling Compare
+    // to make use of this function.
+    virtual void ReportMatched(
+        const Message& /* message1 */, const Message& /* message2 */,
+        const std::vector<SpecificField>& /* field_path */) {}
+
+    // Reports that two fields would have been compared, but the
+    // comparison has been skipped because the field was marked as
+    // 'ignored' using IgnoreField().  This function is mutually
+    // exclusive with all the other Report() functions.
+    //
+    // The contract of ReportIgnored is slightly different than the
+    // other Report() functions, in that |field_path.back().index| is
+    // always equal to -1, even if the last field is repeated. This is
+    // because while the other Report() functions indicate where in a
+    // repeated field the action (Addition, Deletion, etc...)
+    // happened, when a repeated field is 'ignored', the differencer
+    // simply calls ReportIgnored on the repeated field as a whole and
+    // moves on without looking at its individual elements.
+    //
+    // Furthermore, ReportIgnored() does not indicate whether the
+    // fields were in fact equal or not, as Compare() does not inspect
+    // these fields at all. It is up to the Reporter to decide whether
+    // the fields are equal or not (perhaps with a second call to
+    // Compare()), if it cares.
+    virtual void ReportIgnored(
+        const Message& /* message1 */, const Message& /* message2 */,
+        const std::vector<SpecificField>& /* field_path */) {}
+
+    // Report that an unknown field is ignored. (see comment above).
+    // Note this is a different function since the last SpecificField in field
+    // path has a null field.  This could break existing Reporter.
+    virtual void ReportUnknownFieldIgnored(
+        const Message& /* message1 */, const Message& /* message2 */,
+        const std::vector<SpecificField>& /* field_path */) {}
+
+   private:
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Reporter);
+  };
+
+  // MapKeyComparator is used to determine if two elements have the same key
+  // when comparing elements of a repeated field as a map.
+  class PROTOBUF_EXPORT MapKeyComparator {
+   public:
+    MapKeyComparator();
+    virtual ~MapKeyComparator();
+
+    virtual bool IsMatch(
+        const Message& /* message1 */, const Message& /* message2 */,
+        const std::vector<SpecificField>& /* parent_fields */) const {
+      GOOGLE_CHECK(false) << "IsMatch() is not implemented.";
+      return false;
+    }
+
+   private:
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapKeyComparator);
+  };
+
+  // Abstract base class from which all IgnoreCriteria derive.
+  // By adding IgnoreCriteria more complex ignore logic can be implemented.
+  // IgnoreCriteria are registered with AddIgnoreCriteria. For each compared
+  // field IsIgnored is called on each added IgnoreCriteria until one returns
+  // true or all return false.
+  // IsIgnored is called for fields where at least one side has a value.
+  class PROTOBUF_EXPORT IgnoreCriteria {
+   public:
+    IgnoreCriteria();
+    virtual ~IgnoreCriteria();
+
+    // Returns true if the field should be ignored.
+    virtual bool IsIgnored(
+        const Message& /* message1 */, const Message& /* message2 */,
+        const FieldDescriptor* /* field */,
+        const std::vector<SpecificField>& /* parent_fields */) = 0;
+
+    // Returns true if the unknown field should be ignored.
+    // Note: This will be called for unknown fields as well in which case
+    //       field.field will be null.
+    virtual bool IsUnknownFieldIgnored(
+        const Message& /* message1 */, const Message& /* message2 */,
+        const SpecificField& /* field */,
+        const std::vector<SpecificField>& /* parent_fields */) {
+      return false;
+    }
+  };
+
+  // To add a Reporter, construct default here, then use ReportDifferencesTo or
+  // ReportDifferencesToString.
+  explicit MessageDifferencer();
+
+  ~MessageDifferencer();
+
+  enum MessageFieldComparison {
+    EQUAL,       // Fields must be present in both messages
+                 // for the messages to be considered the same.
+    EQUIVALENT,  // Fields with default values are considered set
+                 // for comparison purposes even if not explicitly
+                 // set in the messages themselves.  Unknown fields
+                 // are ignored.
+  };
+
+  enum Scope {
+    FULL,    // All fields of both messages are considered in the comparison.
+    PARTIAL  // Only fields present in the first message are considered; fields
+             // set only in the second message will be skipped during
+             // comparison.
+  };
+
+  // DEPRECATED. Use FieldComparator::FloatComparison instead.
+  enum FloatComparison {
+    EXACT,       // Floats and doubles are compared exactly.
+    APPROXIMATE  // Floats and doubles are compared using the
+                 // MathUtil::AlmostEquals method.
+  };
+
+  enum RepeatedFieldComparison {
+    AS_LIST,  // Repeated fields are compared in order.  Differing values at
+              // the same index are reported using ReportModified().  If the
+              // repeated fields have different numbers of elements, the
+              // unpaired elements are reported using ReportAdded() or
+              // ReportDeleted().
+    AS_SET,   // Treat all the repeated fields as sets.
+              // See TreatAsSet(), as below.
+    AS_SMART_LIST,  // Similar to AS_SET, but preserve the order and find the
+                    // longest matching sequence from the first matching
+                    // element. To use an optimal solution, call
+                    // SetMatchIndicesForSmartListCallback() to pass it in.
+    AS_SMART_SET,   // Similar to AS_SET, but match elements with fewest diffs.
+  };
+
+  // The elements of the given repeated field will be treated as a set for
+  // diffing purposes, so different orderings of the same elements will be
+  // considered equal.  Elements which are present on both sides of the
+  // comparison but which have changed position will be reported with
+  // ReportMoved().  Elements which only exist on one side or the other are
+  // reported with ReportAdded() and ReportDeleted() regardless of their
+  // positions.  ReportModified() is never used for this repeated field.  If
+  // the only differences between the compared messages is that some fields
+  // have been moved, then the comparison returns true.
+  //
+  // Note that despite the name of this method, this is really
+  // comparison as multisets: if one side of the comparison has a duplicate
+  // in the repeated field but the other side doesn't, this will count as
+  // a mismatch.
+  //
+  // If the scope of comparison is set to PARTIAL, then in addition to what's
+  // above, extra values added to repeated fields of the second message will
+  // not cause the comparison to fail.
+  //
+  // Note that set comparison is currently O(k * n^2) (where n is the total
+  // number of elements, and k is the average size of each element). In theory
+  // it could be made O(n * k) with a more complex hashing implementation. Feel
+  // free to contribute one if the current implementation is too slow for you.
+  // If partial matching is also enabled, the time complexity will be O(k * n^2
+  // + n^3) in which n^3 is the time complexity of the maximum matching
+  // algorithm.
+  //
+  // REQUIRES: field->is_repeated() and field not registered with TreatAsMap*
+  void TreatAsSet(const FieldDescriptor* field);
+  void TreatAsSmartSet(const FieldDescriptor* field);
+
+  // The elements of the given repeated field will be treated as a list for
+  // diffing purposes, so different orderings of the same elements will NOT be
+  // considered equal.
+  //
+  // REQUIRES: field->is_repeated() and field not registered with TreatAsMap*
+  void TreatAsList(const FieldDescriptor* field);
+  // Note that the complexity is similar to treating as SET.
+  void TreatAsSmartList(const FieldDescriptor* field);
+
+  // The elements of the given repeated field will be treated as a map for
+  // diffing purposes, with |key| being the map key.  Thus, elements with the
+  // same key will be compared even if they do not appear at the same index.
+  // Differences are reported similarly to TreatAsSet(), except that
+  // ReportModified() is used to report elements with the same key but
+  // different values.  Note that if an element is both moved and modified,
+  // only ReportModified() will be called.  As with TreatAsSet, if the only
+  // differences between the compared messages is that some fields have been
+  // moved, then the comparison returns true. See TreatAsSet for notes on
+  // performance.
+  //
+  // REQUIRES:  field->is_repeated()
+  // REQUIRES:  field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
+  // REQUIRES:  key->containing_type() == field->message_type()
+  void TreatAsMap(const FieldDescriptor* field, const FieldDescriptor* key);
+  // Same as TreatAsMap except that this method will use multiple fields as
+  // the key in comparison. All specified fields in 'key_fields' should be
+  // present in the compared elements. Two elements will be treated as having
+  // the same key iff they have the same value for every specified field. There
+  // are two steps in the comparison process. The first one is key matching.
+  // Every element from one message will be compared to every element from
+  // the other message. Only fields in 'key_fields' are compared in this step
+  // to decide if two elements have the same key. The second step is value
+  // comparison. Those pairs of elements with the same key (with equal value
+  // for every field in 'key_fields') will be compared in this step.
+  // Time complexity of the first step is O(s * m * n ^ 2) where s is the
+  // average size of the fields specified in 'key_fields', m is the number of
+  // fields in 'key_fields' and n is the number of elements. If partial
+  // matching is enabled, an extra O(n^3) will be incured by the maximum
+  // matching algorithm. The second step is O(k * n) where k is the average
+  // size of each element.
+  void TreatAsMapWithMultipleFieldsAsKey(
+      const FieldDescriptor* field,
+      const std::vector<const FieldDescriptor*>& key_fields);
+  // Same as TreatAsMapWithMultipleFieldsAsKey, except that each of the field
+  // do not necessarily need to be a direct subfield. Each element in
+  // key_field_paths indicate a path from the message being compared, listing
+  // successive subfield to reach the key field.
+  //
+  // REQUIRES:
+  //   for key_field_path in key_field_paths:
+  //     key_field_path[0]->containing_type() == field->message_type()
+  //     for i in [0, key_field_path.size() - 1):
+  //       key_field_path[i+1]->containing_type() ==
+  //           key_field_path[i]->message_type()
+  //       key_field_path[i]->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
+  //       !key_field_path[i]->is_repeated()
+  void TreatAsMapWithMultipleFieldPathsAsKey(
+      const FieldDescriptor* field,
+      const std::vector<std::vector<const FieldDescriptor*> >& key_field_paths);
+
+  // Uses a custom MapKeyComparator to determine if two elements have the same
+  // key when comparing a repeated field as a map.
+  // The caller is responsible to delete the key_comparator.
+  // This method varies from TreatAsMapWithMultipleFieldsAsKey only in the
+  // first key matching step. Rather than comparing some specified fields, it
+  // will invoke the IsMatch method of the given 'key_comparator' to decide if
+  // two elements have the same key.
+  void TreatAsMapUsingKeyComparator(const FieldDescriptor* field,
+                                    const MapKeyComparator* key_comparator);
+
+  // Initiates and returns a new instance of MultipleFieldsMapKeyComparator.
+  MapKeyComparator* CreateMultipleFieldsMapKeyComparator(
+      const std::vector<std::vector<const FieldDescriptor*> >& key_field_paths);
+
+  // Add a custom ignore criteria that is evaluated in addition to the
+  // ignored fields added with IgnoreField.
+  // Takes ownership of ignore_criteria.
+  void AddIgnoreCriteria(IgnoreCriteria* ignore_criteria);
+
+  // Indicates that any field with the given descriptor should be
+  // ignored for the purposes of comparing two messages. This applies
+  // to fields nested in the message structure as well as top level
+  // ones. When the MessageDifferencer encounters an ignored field,
+  // ReportIgnored is called on the reporter, if one is specified.
+  //
+  // The only place where the field's 'ignored' status is not applied is when
+  // it is being used as a key in a field passed to TreatAsMap or is one of
+  // the fields passed to TreatAsMapWithMultipleFieldsAsKey.
+  // In this case it is compared in key matching but after that it's ignored
+  // in value comparison.
+  void IgnoreField(const FieldDescriptor* field);
+
+  // Sets the field comparator used to determine differences between protocol
+  // buffer fields. By default it's set to a DefaultFieldComparator instance.
+  // MessageDifferencer doesn't take ownership over the passed object.
+  // Note that this method must be called before Compare for the comparator to
+  // be used.
+  void set_field_comparator(FieldComparator* comparator);
+#ifdef PROTOBUF_FUTURE_BREAKING_CHANGES
+  void set_field_comparator(DefaultFieldComparator* comparator);
+#endif  // PROTOBUF_FUTURE_BREAKING_CHANGES
+
+  // DEPRECATED. Pass a DefaultFieldComparator instance instead.
+  // Sets the fraction and margin for the float comparison of a given field.
+  // Uses MathUtil::WithinFractionOrMargin to compare the values.
+  // NOTE: this method does nothing if differencer's field comparator has been
+  //       set to a custom object.
+  //
+  // REQUIRES: field->cpp_type == FieldDescriptor::CPPTYPE_DOUBLE or
+  //           field->cpp_type == FieldDescriptor::CPPTYPE_FLOAT
+  // REQUIRES: float_comparison_ == APPROXIMATE
+  void SetFractionAndMargin(const FieldDescriptor* field, double fraction,
+                            double margin);
+
+  // Sets the type of comparison (as defined in the MessageFieldComparison
+  // enumeration above) that is used by this differencer when determining how
+  // to compare fields in messages.
+  void set_message_field_comparison(MessageFieldComparison comparison);
+
+  // Returns the current message field comparison used in this differencer.
+  MessageFieldComparison message_field_comparison() const;
+
+  // Tells the differencer whether or not to report matches. This method must
+  // be called before Compare. The default for a new differencer is false.
+  void set_report_matches(bool report_matches) {
+    report_matches_ = report_matches;
+  }
+
+  // Tells the differencer whether or not to report moves (in a set or map
+  // repeated field). This method must be called before Compare. The default for
+  // a new differencer is true.
+  void set_report_moves(bool report_moves) { report_moves_ = report_moves; }
+
+  // Tells the differencer whether or not to report ignored values. This method
+  // must be called before Compare. The default for a new differencer is true.
+  void set_report_ignores(bool report_ignores) {
+    report_ignores_ = report_ignores;
+  }
+
+  // Sets the scope of the comparison (as defined in the Scope enumeration
+  // above) that is used by this differencer when determining which fields to
+  // compare between the messages.
+  void set_scope(Scope scope);
+
+  // Returns the current scope used by this differencer.
+  Scope scope() const;
+
+  // DEPRECATED. Pass a DefaultFieldComparator instance instead.
+  // Sets the type of comparison (as defined in the FloatComparison enumeration
+  // above) that is used by this differencer when comparing float (and double)
+  // fields in messages.
+  // NOTE: this method does nothing if differencer's field comparator has been
+  //       set to a custom object.
+  void set_float_comparison(FloatComparison comparison);
+
+  // Sets the type of comparison for repeated field (as defined in the
+  // RepeatedFieldComparison enumeration above) that is used by this
+  // differencer when compare repeated fields in messages.
+  void set_repeated_field_comparison(RepeatedFieldComparison comparison);
+
+  // Returns the current repeated field comparison used by this differencer.
+  RepeatedFieldComparison repeated_field_comparison() const;
+
+  // Compares the two specified messages, returning true if they are the same,
+  // false otherwise. If this method returns false, any changes between the
+  // two messages will be reported if a Reporter was specified via
+  // ReportDifferencesTo (see also ReportDifferencesToString).
+  //
+  // This method REQUIRES that the two messages have the same
+  // Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
+  bool Compare(const Message& message1, const Message& message2);
+
+  // Same as above, except comparing only the list of fields specified by the
+  // two vectors of FieldDescriptors.
+  bool CompareWithFields(
+      const Message& message1, const Message& message2,
+      const std::vector<const FieldDescriptor*>& message1_fields,
+      const std::vector<const FieldDescriptor*>& message2_fields);
+
+  // Automatically creates a reporter that will output the differences
+  // found (if any) to the specified output string pointer. Note that this
+  // method must be called before Compare.
+  void ReportDifferencesToString(std::string* output);
+
+  // Tells the MessageDifferencer to report differences via the specified
+  // reporter. Note that this method must be called before Compare for
+  // the reporter to be used. It is the responsibility of the caller to delete
+  // this object.
+  // If the provided pointer equals NULL, the MessageDifferencer stops reporting
+  // differences to any previously set reporters or output strings.
+  void ReportDifferencesTo(Reporter* reporter);
+
+ private:
+  // Class for processing Any deserialization.  This logic is used by both the
+  // MessageDifferencer and StreamReporter classes.
+  class UnpackAnyField {
+   private:
+    std::unique_ptr<DynamicMessageFactory> dynamic_message_factory_;
+
+   public:
+    UnpackAnyField() = default;
+    ~UnpackAnyField() = default;
+    // If "any" is of type google.protobuf.Any, extract its payload using
+    // DynamicMessageFactory and store in "data".
+    bool UnpackAny(const Message& any, std::unique_ptr<Message>* data);
+  };
+
+ public:
+  // An implementation of the MessageDifferencer Reporter that outputs
+  // any differences found in human-readable form to the supplied
+  // ZeroCopyOutputStream or Printer. If a printer is used, the delimiter
+  // *must* be '$'.
+  //
+  // WARNING: this reporter does not necessarily flush its output until it is
+  // destroyed. As a result, it is not safe to assume the output is valid or
+  // complete until after you destroy the reporter. For example, if you use a
+  // StreamReporter to write to a StringOutputStream, the target string may
+  // contain uninitialized data until the reporter is destroyed.
+  class PROTOBUF_EXPORT StreamReporter : public Reporter {
+   public:
+    explicit StreamReporter(io::ZeroCopyOutputStream* output);
+    explicit StreamReporter(io::Printer* printer);  // delimiter '$'
+    ~StreamReporter() override;
+
+    // When set to true, the stream reporter will also output aggregates nodes
+    // (i.e. messages and groups) whose subfields have been modified. When
+    // false, will only report the individual subfields. Defaults to false.
+    void set_report_modified_aggregates(bool report) {
+      report_modified_aggregates_ = report;
+    }
+
+    // The following are implementations of the methods described above.
+
+    void ReportAdded(const Message& message1, const Message& message2,
+                     const std::vector<SpecificField>& field_path) override;
+
+    void ReportDeleted(const Message& message1, const Message& message2,
+                       const std::vector<SpecificField>& field_path) override;
+
+    void ReportModified(const Message& message1, const Message& message2,
+                        const std::vector<SpecificField>& field_path) override;
+
+    void ReportMoved(const Message& message1, const Message& message2,
+                     const std::vector<SpecificField>& field_path) override;
+
+    void ReportMatched(const Message& message1, const Message& message2,
+                       const std::vector<SpecificField>& field_path) override;
+
+    void ReportIgnored(const Message& message1, const Message& message2,
+                       const std::vector<SpecificField>& field_path) override;
+
+    void ReportUnknownFieldIgnored(
+        const Message& message1, const Message& message2,
+        const std::vector<SpecificField>& field_path) override;
+
+    // Messages that are being compared must be provided to StreamReporter prior
+    // to processing
+    void SetMessages(const Message& message1, const Message& message2);
+
+   protected:
+    // Prints the specified path of fields to the buffer.
+    virtual void PrintPath(const std::vector<SpecificField>& field_path,
+                           bool left_side);
+
+    // Prints the value of fields to the buffer.  left_side is true if the
+    // given message is from the left side of the comparison, false if it
+    // was the right.  This is relevant only to decide whether to follow
+    // unknown_field_index1 or unknown_field_index2 when an unknown field
+    // is encountered in field_path.
+    virtual void PrintValue(const Message& message,
+                            const std::vector<SpecificField>& field_path,
+                            bool left_side);
+
+    // Prints the specified path of unknown fields to the buffer.
+    virtual void PrintUnknownFieldValue(const UnknownField* unknown_field);
+
+    // Just print a string
+    void Print(const std::string& str);
+
+   private:
+    // helper function for PrintPath that contains logic for printing maps
+    void PrintMapKey(bool left_side, const SpecificField& specific_field);
+
+    io::Printer* printer_;
+    bool delete_printer_;
+    bool report_modified_aggregates_;
+    const Message* message1_;
+    const Message* message2_;
+    MessageDifferencer::UnpackAnyField unpack_any_field_;
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StreamReporter);
+  };
+
+ private:
+  friend class SimpleFieldComparator;
+
+  // A MapKeyComparator to be used in TreatAsMapUsingKeyComparator.
+  // Implementation of this class needs to do field value comparison which
+  // relies on some private methods of MessageDifferencer. That's why this
+  // class is declared as a nested class of MessageDifferencer.
+  class MultipleFieldsMapKeyComparator;
+
+  // A MapKeyComparator for use with map_entries.
+  class PROTOBUF_EXPORT MapEntryKeyComparator : public MapKeyComparator {
+   public:
+    explicit MapEntryKeyComparator(MessageDifferencer* message_differencer);
+    bool IsMatch(
+        const Message& message1, const Message& message2,
+        const std::vector<SpecificField>& parent_fields) const override;
+
+   private:
+    MessageDifferencer* message_differencer_;
+  };
+
+  // Returns true if field1's number() is less than field2's.
+  static bool FieldBefore(const FieldDescriptor* field1,
+                          const FieldDescriptor* field2);
+
+  // Retrieve all the set fields, including extensions.
+  FieldDescriptorArray RetrieveFields(const Message& message,
+                                      bool base_message);
+
+  // Combine the two lists of fields into the combined_fields output vector.
+  // All fields present in both lists will always be included in the combined
+  // list.  Fields only present in one of the lists will only appear in the
+  // combined list if the corresponding fields_scope option is set to FULL.
+  FieldDescriptorArray CombineFields(const FieldDescriptorArray& fields1,
+                                     Scope fields1_scope,
+                                     const FieldDescriptorArray& fields2,
+                                     Scope fields2_scope);
+
+  // Internal version of the Compare method which performs the actual
+  // comparison. The parent_fields vector is a vector containing field
+  // descriptors of all fields accessed to get to this comparison operation
+  // (i.e. if the current message is an embedded message, the parent_fields
+  // vector will contain the field that has this embedded message).
+  bool Compare(const Message& message1, const Message& message2,
+               std::vector<SpecificField>* parent_fields);
+
+  // Compares all the unknown fields in two messages.
+  bool CompareUnknownFields(const Message& message1, const Message& message2,
+                            const UnknownFieldSet&, const UnknownFieldSet&,
+                            std::vector<SpecificField>* parent_fields);
+
+  // Compares the specified messages for the requested field lists. The field
+  // lists are modified depending on comparison settings, and then passed to
+  // CompareWithFieldsInternal.
+  bool CompareRequestedFieldsUsingSettings(
+      const Message& message1, const Message& message2,
+      const FieldDescriptorArray& message1_fields,
+      const FieldDescriptorArray& message2_fields,
+      std::vector<SpecificField>* parent_fields);
+
+  // Compares the specified messages with the specified field lists.
+  bool CompareWithFieldsInternal(const Message& message1,
+                                 const Message& message2,
+                                 const FieldDescriptorArray& message1_fields,
+                                 const FieldDescriptorArray& message2_fields,
+                                 std::vector<SpecificField>* parent_fields);
+
+  // Compares the repeated fields, and report the error.
+  bool CompareRepeatedField(const Message& message1, const Message& message2,
+                            const FieldDescriptor* field,
+                            std::vector<SpecificField>* parent_fields);
+
+  // Compares map fields, and report the error.
+  bool CompareMapField(const Message& message1, const Message& message2,
+                       const FieldDescriptor* field,
+                       std::vector<SpecificField>* parent_fields);
+
+  // Helper for CompareRepeatedField and CompareMapField: compares and reports
+  // differences element-wise. This is the implementation for non-map fields,
+  // and can also compare map fields by using the underlying representation.
+  bool CompareRepeatedRep(const Message& message1, const Message& message2,
+                          const FieldDescriptor* field,
+                          std::vector<SpecificField>* parent_fields);
+
+  // Helper for CompareMapField: compare the map fields using map reflection
+  // instead of sync to repeated.
+  bool CompareMapFieldByMapReflection(const Message& message1,
+                                      const Message& message2,
+                                      const FieldDescriptor* field,
+                                      std::vector<SpecificField>* parent_fields,
+                                      DefaultFieldComparator* comparator);
+
+  // Shorthand for CompareFieldValueUsingParentFields with NULL parent_fields.
+  bool CompareFieldValue(const Message& message1, const Message& message2,
+                         const FieldDescriptor* field, int index1, int index2);
+
+  // Compares the specified field on the two messages, returning
+  // true if they are the same, false otherwise. For repeated fields,
+  // this method only compares the value in the specified index. This method
+  // uses Compare functions to recurse into submessages.
+  // The parent_fields vector is used in calls to a Reporter instance calls.
+  // It can be NULL, in which case the MessageDifferencer will create new
+  // list of parent messages if it needs to recursively compare the given field.
+  // To avoid confusing users you should not set it to NULL unless you modified
+  // Reporter to handle the change of parent_fields correctly.
+  bool CompareFieldValueUsingParentFields(
+      const Message& message1, const Message& message2,
+      const FieldDescriptor* field, int index1, int index2,
+      std::vector<SpecificField>* parent_fields);
+
+  // Compares the specified field on the two messages, returning comparison
+  // result, as returned by appropriate FieldComparator.
+  FieldComparator::ComparisonResult GetFieldComparisonResult(
+      const Message& message1, const Message& message2,
+      const FieldDescriptor* field, int index1, int index2,
+      const FieldContext* field_context);
+
+  // Check if the two elements in the repeated field are match to each other.
+  // if the key_comprator is NULL, this function returns true when the two
+  // elements are equal.
+  bool IsMatch(const FieldDescriptor* repeated_field,
+               const MapKeyComparator* key_comparator, const Message* message1,
+               const Message* message2,
+               const std::vector<SpecificField>& parent_fields,
+               Reporter* reporter, int index1, int index2);
+
+  // Returns true when this repeated field has been configured to be treated
+  // as a Set / SmartSet / SmartList.
+  bool IsTreatedAsSet(const FieldDescriptor* field);
+  bool IsTreatedAsSmartSet(const FieldDescriptor* field);
+
+  bool IsTreatedAsSmartList(const FieldDescriptor* field);
+  // When treating as SMART_LIST, it uses MatchIndicesPostProcessorForSmartList
+  // by default to find the longest matching sequence from the first matching
+  // element. The callback takes two vectors showing the matching indices from
+  // the other vector, where -1 means an unmatch.
+  void SetMatchIndicesForSmartListCallback(
+      std::function<void(std::vector<int>*, std::vector<int>*)> callback);
+
+  // Returns true when this repeated field is to be compared as a subset, ie.
+  // has been configured to be treated as a set or map and scope is set to
+  // PARTIAL.
+  bool IsTreatedAsSubset(const FieldDescriptor* field);
+
+  // Returns true if this field is to be ignored when this
+  // MessageDifferencer compares messages.
+  bool IsIgnored(const Message& message1, const Message& message2,
+                 const FieldDescriptor* field,
+                 const std::vector<SpecificField>& parent_fields);
+
+  // Returns true if this unknown field is to be ignored when this
+  // MessageDifferencer compares messages.
+  bool IsUnknownFieldIgnored(const Message& message1, const Message& message2,
+                             const SpecificField& field,
+                             const std::vector<SpecificField>& parent_fields);
+
+  // Returns MapKeyComparator* when this field has been configured to be treated
+  // as a map or its is_map() return true.  If not, returns NULL.
+  const MapKeyComparator* GetMapKeyComparator(
+      const FieldDescriptor* field) const;
+
+  // Attempts to match indices of a repeated field, so that the contained values
+  // match. Clears output vectors and sets their values to indices of paired
+  // messages, ie. if message1[0] matches message2[1], then match_list1[0] == 1
+  // and match_list2[1] == 0. The unmatched indices are indicated by -1.
+  // Assumes the repeated field is not treated as a simple list.
+  // This method returns false if the match failed. However, it doesn't mean
+  // that the comparison succeeds when this method returns true (you need to
+  // double-check in this case).
+  bool MatchRepeatedFieldIndices(
+      const Message& message1, const Message& message2,
+      const FieldDescriptor* repeated_field,
+      const MapKeyComparator* key_comparator,
+      const std::vector<SpecificField>& parent_fields,
+      std::vector<int>* match_list1, std::vector<int>* match_list2);
+
+  // Checks if index is equal to new_index in all the specific fields.
+  static bool CheckPathChanged(const std::vector<SpecificField>& parent_fields);
+
+  // CHECKs that the given repeated field can be compared according to
+  // new_comparison.
+  void CheckRepeatedFieldComparisons(
+      const FieldDescriptor* field,
+      const RepeatedFieldComparison& new_comparison);
+
+  // Defines a map between field descriptors and their MapKeyComparators.
+  // Used for repeated fields when they are configured as TreatAsMap.
+  typedef std::map<const FieldDescriptor*, const MapKeyComparator*>
+      FieldKeyComparatorMap;
+
+  // Defines a set to store field descriptors.  Used for repeated fields when
+  // they are configured as TreatAsSet.
+  typedef std::set<const FieldDescriptor*> FieldSet;
+  typedef std::map<const FieldDescriptor*, RepeatedFieldComparison> FieldMap;
+
+  Reporter* reporter_;
+  DefaultFieldComparator default_field_comparator_;
+  MessageFieldComparison message_field_comparison_;
+  Scope scope_;
+  RepeatedFieldComparison repeated_field_comparison_;
+
+  FieldMap repeated_field_comparisons_;
+  // Keeps track of MapKeyComparators that are created within
+  // MessageDifferencer. These MapKeyComparators should be deleted
+  // before MessageDifferencer is destroyed.
+  // When TreatAsMap or TreatAsMapWithMultipleFieldsAsKey is called, we don't
+  // store the supplied FieldDescriptors directly. Instead, a new
+  // MapKeyComparator is created for comparison purpose.
+  std::vector<MapKeyComparator*> owned_key_comparators_;
+  FieldKeyComparatorMap map_field_key_comparator_;
+  MapEntryKeyComparator map_entry_key_comparator_;
+  std::vector<IgnoreCriteria*> ignore_criteria_;
+  // Reused multiple times in RetrieveFields to avoid extra allocations
+  std::vector<const FieldDescriptor*> tmp_message_fields_;
+
+  FieldSet ignored_fields_;
+
+  union {
+    DefaultFieldComparator* default_impl;
+    FieldComparator* base;
+  } field_comparator_ = {&default_field_comparator_};
+  enum { kFCDefault, kFCBase } field_comparator_kind_ = kFCDefault;
+
+  bool report_matches_;
+  bool report_moves_;
+  bool report_ignores_;
+
+  std::string* output_string_;
+
+  // Callback to post-process the matched indices to support SMART_LIST.
+  std::function<void(std::vector<int>*, std::vector<int>*)>
+      match_indices_for_smart_list_callback_;
+
+  MessageDifferencer::UnpackAnyField unpack_any_field_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageDifferencer);
+};
+
+// This class provides extra information to the FieldComparator::Compare
+// function.
+class PROTOBUF_EXPORT FieldContext {
+ public:
+  explicit FieldContext(
+      std::vector<MessageDifferencer::SpecificField>* parent_fields)
+      : parent_fields_(parent_fields) {}
+
+  std::vector<MessageDifferencer::SpecificField>* parent_fields() const {
+    return parent_fields_;
+  }
+
+ private:
+  std::vector<MessageDifferencer::SpecificField>* parent_fields_;
+};
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_MESSAGE_DIFFERENCER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/time_util.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/time_util.h
new file mode 100644
index 0000000..709527e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/time_util.h
@@ -0,0 +1,314 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Defines utilities for the Timestamp and Duration well known types.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_TIME_UTIL_H__
+#define GOOGLE_PROTOBUF_UTIL_TIME_UTIL_H__
+
+#include <cstdint>
+#include <ctime>
+#include <ostream>
+#include <string>
+#ifdef _MSC_VER
+#ifdef _XBOX_ONE
+struct timeval {
+  int64_t tv_sec;  /* seconds */
+  int64_t tv_usec; /* and microseconds */
+};
+#else
+#include <winsock2.h>
+#endif  // _XBOX_ONE
+#else
+#include <sys/time.h>
+#endif
+
+#include <google/protobuf/duration.pb.h>
+#include <google/protobuf/timestamp.pb.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+// Utility functions for Timestamp and Duration.
+class PROTOBUF_EXPORT TimeUtil {
+  typedef google::protobuf::Timestamp Timestamp;
+  typedef google::protobuf::Duration Duration;
+
+ public:
+  // The min/max Timestamp/Duration values we support.
+  //
+  // For "0001-01-01T00:00:00Z".
+  static const int64_t kTimestampMinSeconds = -62135596800LL;
+  // For "9999-12-31T23:59:59.999999999Z".
+  static const int64_t kTimestampMaxSeconds = 253402300799LL;
+  static const int64_t kDurationMinSeconds = -315576000000LL;
+  static const int64_t kDurationMaxSeconds = 315576000000LL;
+
+  // Converts Timestamp to/from RFC 3339 date string format.
+  // Generated output will always be Z-normalized and uses 3, 6 or 9
+  // fractional digits as required to represent the exact time. When
+  // parsing, any fractional digits (or none) and any offset are
+  // accepted as long as they fit into nano-seconds precision.
+  // Note that Timestamp can only represent time from
+  // 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. Converting
+  // a Timestamp outside of this range is undefined behavior.
+  // See https://www.ietf.org/rfc/rfc3339.txt
+  //
+  // Example of generated format:
+  //   "1972-01-01T10:00:20.021Z"
+  //
+  // Example of accepted format:
+  //   "1972-01-01T10:00:20.021-05:00"
+  static std::string ToString(const Timestamp& timestamp);
+  static bool FromString(const std::string& value, Timestamp* timestamp);
+
+  // Converts Duration to/from string format. The string format will contains
+  // 3, 6, or 9 fractional digits depending on the precision required to
+  // represent the exact Duration value. For example:
+  //   "1s", "1.010s", "1.000000100s", "-3.100s"
+  // The range that can be represented by Duration is from -315,576,000,000
+  // to +315,576,000,000 inclusive (in seconds).
+  static std::string ToString(const Duration& duration);
+  static bool FromString(const std::string& value, Duration* timestamp);
+
+#ifdef GetCurrentTime
+#undef GetCurrentTime  // Visual Studio has macro GetCurrentTime
+#endif
+  // Gets the current UTC time.
+  static Timestamp GetCurrentTime();
+  // Returns the Time representing "1970-01-01 00:00:00".
+  static Timestamp GetEpoch();
+
+  // Converts between Duration and integer types. The behavior is undefined if
+  // the input value is not in the valid range of Duration.
+  static Duration NanosecondsToDuration(int64_t nanos);
+  static Duration MicrosecondsToDuration(int64_t micros);
+  static Duration MillisecondsToDuration(int64_t millis);
+  static Duration SecondsToDuration(int64_t seconds);
+  static Duration MinutesToDuration(int64_t minutes);
+  static Duration HoursToDuration(int64_t hours);
+  // Result will be truncated towards zero. For example, "-1.5s" will be
+  // truncated to "-1s", and "1.5s" to "1s" when converting to seconds.
+  // It's undefined behavior if the input duration is not valid or the result
+  // exceeds the range of int64. A duration is not valid if it's not in the
+  // valid range of Duration, or have an invalid nanos value (i.e., larger
+  // than 999999999, less than -999999999, or have a different sign from the
+  // seconds part).
+  static int64_t DurationToNanoseconds(const Duration& duration);
+  static int64_t DurationToMicroseconds(const Duration& duration);
+  static int64_t DurationToMilliseconds(const Duration& duration);
+  static int64_t DurationToSeconds(const Duration& duration);
+  static int64_t DurationToMinutes(const Duration& duration);
+  static int64_t DurationToHours(const Duration& duration);
+  // Creates Timestamp from integer types. The integer value indicates the
+  // time elapsed from Epoch time. The behavior is undefined if the input
+  // value is not in the valid range of Timestamp.
+  static Timestamp NanosecondsToTimestamp(int64_t nanos);
+  static Timestamp MicrosecondsToTimestamp(int64_t micros);
+  static Timestamp MillisecondsToTimestamp(int64_t millis);
+  static Timestamp SecondsToTimestamp(int64_t seconds);
+  // Result will be truncated down to the nearest integer value. For example,
+  // with "1969-12-31T23:59:59.9Z", TimestampToMilliseconds() returns -100
+  // and TimestampToSeconds() returns -1. It's undefined behavior if the input
+  // Timestamp is not valid (i.e., its seconds part or nanos part does not fall
+  // in the valid range) or the return value doesn't fit into int64.
+  static int64_t TimestampToNanoseconds(const Timestamp& timestamp);
+  static int64_t TimestampToMicroseconds(const Timestamp& timestamp);
+  static int64_t TimestampToMilliseconds(const Timestamp& timestamp);
+  static int64_t TimestampToSeconds(const Timestamp& timestamp);
+
+  // Conversion to/from other time/date types. Note that these types may
+  // have a different precision and time range from Timestamp/Duration.
+  // When converting to a lower precision type, the value will be truncated
+  // to the nearest value that can be represented. If the value is
+  // out of the range of the result type, the return value is undefined.
+  //
+  // Conversion to/from time_t
+  static Timestamp TimeTToTimestamp(time_t value);
+  static time_t TimestampToTimeT(const Timestamp& value);
+
+  // Conversion to/from timeval
+  static Timestamp TimevalToTimestamp(const timeval& value);
+  static timeval TimestampToTimeval(const Timestamp& value);
+  static Duration TimevalToDuration(const timeval& value);
+  static timeval DurationToTimeval(const Duration& value);
+};
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+namespace google {
+namespace protobuf {
+// Overloaded operators for Duration.
+//
+// Assignment operators.
+PROTOBUF_EXPORT Duration& operator+=(Duration& d1,
+                                     const Duration& d2);  // NOLINT
+PROTOBUF_EXPORT Duration& operator-=(Duration& d1,
+                                     const Duration& d2);     // NOLINT
+PROTOBUF_EXPORT Duration& operator*=(Duration& d, int64_t r);  // NOLINT
+PROTOBUF_EXPORT Duration& operator*=(Duration& d, double r);  // NOLINT
+PROTOBUF_EXPORT Duration& operator/=(Duration& d, int64_t r);  // NOLINT
+PROTOBUF_EXPORT Duration& operator/=(Duration& d, double r);  // NOLINT
+// Overload for other integer types.
+template <typename T>
+Duration& operator*=(Duration& d, T r) {  // NOLINT
+  int64_t x = r;
+  return d *= x;
+}
+template <typename T>
+Duration& operator/=(Duration& d, T r) {  // NOLINT
+  int64_t x = r;
+  return d /= x;
+}
+PROTOBUF_EXPORT Duration& operator%=(Duration& d1,
+                                     const Duration& d2);  // NOLINT
+// Relational operators.
+inline bool operator<(const Duration& d1, const Duration& d2) {
+  if (d1.seconds() == d2.seconds()) {
+    return d1.nanos() < d2.nanos();
+  }
+  return d1.seconds() < d2.seconds();
+}
+inline bool operator>(const Duration& d1, const Duration& d2) {
+  return d2 < d1;
+}
+inline bool operator>=(const Duration& d1, const Duration& d2) {
+  return !(d1 < d2);
+}
+inline bool operator<=(const Duration& d1, const Duration& d2) {
+  return !(d2 < d1);
+}
+inline bool operator==(const Duration& d1, const Duration& d2) {
+  return d1.seconds() == d2.seconds() && d1.nanos() == d2.nanos();
+}
+inline bool operator!=(const Duration& d1, const Duration& d2) {
+  return !(d1 == d2);
+}
+// Additive operators
+inline Duration operator-(const Duration& d) {
+  Duration result;
+  result.set_seconds(-d.seconds());
+  result.set_nanos(-d.nanos());
+  return result;
+}
+inline Duration operator+(const Duration& d1, const Duration& d2) {
+  Duration result = d1;
+  return result += d2;
+}
+inline Duration operator-(const Duration& d1, const Duration& d2) {
+  Duration result = d1;
+  return result -= d2;
+}
+// Multiplicative operators
+template <typename T>
+inline Duration operator*(Duration d, T r) {
+  return d *= r;
+}
+template <typename T>
+inline Duration operator*(T r, Duration d) {
+  return d *= r;
+}
+template <typename T>
+inline Duration operator/(Duration d, T r) {
+  return d /= r;
+}
+PROTOBUF_EXPORT int64_t operator/(const Duration& d1, const Duration& d2);
+
+inline Duration operator%(const Duration& d1, const Duration& d2) {
+  Duration result = d1;
+  return result %= d2;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const Duration& d) {
+  out << ::PROTOBUF_NAMESPACE_ID::util::TimeUtil::ToString(d);
+  return out;
+}
+
+// Overloaded operators for Timestamp
+//
+// Assignment operators.
+PROTOBUF_EXPORT Timestamp& operator+=(Timestamp& t,
+                                      const Duration& d);  // NOLINT
+PROTOBUF_EXPORT Timestamp& operator-=(Timestamp& t,
+                                      const Duration& d);  // NOLINT
+// Relational operators.
+inline bool operator<(const Timestamp& t1, const Timestamp& t2) {
+  if (t1.seconds() == t2.seconds()) {
+    return t1.nanos() < t2.nanos();
+  }
+  return t1.seconds() < t2.seconds();
+}
+inline bool operator>(const Timestamp& t1, const Timestamp& t2) {
+  return t2 < t1;
+}
+inline bool operator>=(const Timestamp& t1, const Timestamp& t2) {
+  return !(t1 < t2);
+}
+inline bool operator<=(const Timestamp& t1, const Timestamp& t2) {
+  return !(t2 < t1);
+}
+inline bool operator==(const Timestamp& t1, const Timestamp& t2) {
+  return t1.seconds() == t2.seconds() && t1.nanos() == t2.nanos();
+}
+inline bool operator!=(const Timestamp& t1, const Timestamp& t2) {
+  return !(t1 == t2);
+}
+// Additive operators.
+inline Timestamp operator+(const Timestamp& t, const Duration& d) {
+  Timestamp result = t;
+  return result += d;
+}
+inline Timestamp operator+(const Duration& d, const Timestamp& t) {
+  Timestamp result = t;
+  return result += d;
+}
+inline Timestamp operator-(const Timestamp& t, const Duration& d) {
+  Timestamp result = t;
+  return result -= d;
+}
+PROTOBUF_EXPORT Duration operator-(const Timestamp& t1, const Timestamp& t2);
+
+inline std::ostream& operator<<(std::ostream& out, const Timestamp& t) {
+  out << ::PROTOBUF_NAMESPACE_ID::util::TimeUtil::ToString(t);
+  return out;
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_TIME_UTIL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/type_resolver.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/type_resolver.h
new file mode 100644
index 0000000..b2e7b43
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/type_resolver.h
@@ -0,0 +1,77 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Defines a TypeResolver for the Any message.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_H__
+#define GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/status.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+class DescriptorPool;
+namespace util {
+
+// Abstract interface for a type resolver.
+//
+// Implementations of this interface must be thread-safe.
+class PROTOBUF_EXPORT TypeResolver {
+ public:
+  TypeResolver() {}
+  virtual ~TypeResolver() {}
+
+  // Resolves a type url for a message type.
+  virtual util::Status ResolveMessageType(
+      const std::string& type_url, google::protobuf::Type* message_type) = 0;
+
+  // Resolves a type url for an enum type.
+  virtual util::Status ResolveEnumType(const std::string& type_url,
+                                       google::protobuf::Enum* enum_type) = 0;
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeResolver);
+};
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/type_resolver_util.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/type_resolver_util.h
new file mode 100644
index 0000000..7f6a4b9
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/util/type_resolver_util.h
@@ -0,0 +1,58 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Defines utilities for the TypeResolver.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_UTIL_H__
+#define GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_UTIL_H__
+
+#include <string>
+
+namespace google {
+namespace protobuf {
+class DescriptorPool;
+namespace util {
+class TypeResolver;
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+// Creates a TypeResolver that serves type information in the given descriptor
+// pool. Caller takes ownership of the returned TypeResolver.
+PROTOBUF_EXPORT TypeResolver* NewTypeResolverForDescriptorPool(
+    const std::string& url_prefix, const DescriptorPool* pool);
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_UTIL_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/wire_format.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/wire_format.h
new file mode 100644
index 0000000..1acbf9e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/wire_format.h
@@ -0,0 +1,414 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//         atenasio@google.com (Chris Atenasio) (ZigZag transform)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This header is logically internal, but is made public because it is used
+// from protocol-compiler-generated code, which may reside in other components.
+
+#ifndef GOOGLE_PROTOBUF_WIRE_FORMAT_H__
+#define GOOGLE_PROTOBUF_WIRE_FORMAT_H__
+
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/wire_format_lite.h>
+
+#ifdef SWIG
+#error "You cannot SWIG proto headers"
+#endif
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+class MapKey;           // map_field.h
+class UnknownFieldSet;  // unknown_field_set.h
+}  // namespace protobuf
+}  // namespace google
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// This class is for internal use by the protocol buffer library and by
+// protocol-compiler-generated message classes.  It must not be called
+// directly by clients.
+//
+// This class contains code for implementing the binary protocol buffer
+// wire format via reflection.  The WireFormatLite class implements the
+// non-reflection based routines.
+//
+// This class is really a namespace that contains only static methods
+class PROTOBUF_EXPORT WireFormat {
+ public:
+  // Given a field return its WireType
+  static inline WireFormatLite::WireType WireTypeForField(
+      const FieldDescriptor* field);
+
+  // Given a FieldDescriptor::Type return its WireType
+  static inline WireFormatLite::WireType WireTypeForFieldType(
+      FieldDescriptor::Type type);
+
+  // Compute the byte size of a tag.  For groups, this includes both the start
+  // and end tags.
+  static inline size_t TagSize(int field_number, FieldDescriptor::Type type);
+
+  // These procedures can be used to implement the methods of Message which
+  // handle parsing and serialization of the protocol buffer wire format
+  // using only the Reflection interface.  When you ask the protocol
+  // compiler to optimize for code size rather than speed, it will implement
+  // those methods in terms of these procedures.  Of course, these are much
+  // slower than the specialized implementations which the protocol compiler
+  // generates when told to optimize for speed.
+
+  // Read a message in protocol buffer wire format.
+  //
+  // This procedure reads either to the end of the input stream or through
+  // a WIRETYPE_END_GROUP tag ending the message, whichever comes first.
+  // It returns false if the input is invalid.
+  //
+  // Required fields are NOT checked by this method.  You must call
+  // IsInitialized() on the resulting message yourself.
+  static bool ParseAndMergePartial(io::CodedInputStream* input,
+                                   Message* message);
+
+  // This is meant for internal protobuf use (WireFormat is an internal class).
+  // This is the reflective implementation of the _InternalParse functionality.
+  static const char* _InternalParse(Message* msg, const char* ptr,
+                                    internal::ParseContext* ctx);
+
+  // Serialize a message in protocol buffer wire format.
+  //
+  // Any embedded messages within the message must have their correct sizes
+  // cached.  However, the top-level message need not; its size is passed as
+  // a parameter to this procedure.
+  //
+  // These return false iff the underlying stream returns a write error.
+  static void SerializeWithCachedSizes(const Message& message, int size,
+                                       io::CodedOutputStream* output) {
+    int expected_endpoint = output->ByteCount() + size;
+    output->SetCur(
+        _InternalSerialize(message, output->Cur(), output->EpsCopy()));
+    GOOGLE_CHECK_EQ(output->ByteCount(), expected_endpoint)
+        << ": Protocol message serialized to a size different from what was "
+           "originally expected.  Perhaps it was modified by another thread "
+           "during serialization?";
+  }
+  static uint8_t* _InternalSerialize(const Message& message, uint8_t* target,
+                                     io::EpsCopyOutputStream* stream);
+
+  // Implements Message::ByteSize() via reflection.  WARNING:  The result
+  // of this method is *not* cached anywhere.  However, all embedded messages
+  // will have their ByteSize() methods called, so their sizes will be cached.
+  // Therefore, calling this method is sufficient to allow you to call
+  // WireFormat::SerializeWithCachedSizes() on the same object.
+  static size_t ByteSize(const Message& message);
+
+  // -----------------------------------------------------------------
+  // Helpers for dealing with unknown fields
+
+  // Skips a field value of the given WireType.  The input should start
+  // positioned immediately after the tag.  If unknown_fields is non-nullptr,
+  // the contents of the field will be added to it.
+  static bool SkipField(io::CodedInputStream* input, uint32_t tag,
+                        UnknownFieldSet* unknown_fields);
+
+  // Reads and ignores a message from the input.  If unknown_fields is
+  // non-nullptr, the contents will be added to it.
+  static bool SkipMessage(io::CodedInputStream* input,
+                          UnknownFieldSet* unknown_fields);
+
+  // Read a packed enum field. If the is_valid function is not nullptr, values
+  // for which is_valid(value) returns false are appended to
+  // unknown_fields_stream.
+  static bool ReadPackedEnumPreserveUnknowns(io::CodedInputStream* input,
+                                             uint32_t field_number,
+                                             bool (*is_valid)(int),
+                                             UnknownFieldSet* unknown_fields,
+                                             RepeatedField<int>* values);
+
+  // Write the contents of an UnknownFieldSet to the output.
+  static void SerializeUnknownFields(const UnknownFieldSet& unknown_fields,
+                                     io::CodedOutputStream* output) {
+    output->SetCur(InternalSerializeUnknownFieldsToArray(
+        unknown_fields, output->Cur(), output->EpsCopy()));
+  }
+  // Same as above, except writing directly to the provided buffer.
+  // Requires that the buffer have sufficient capacity for
+  // ComputeUnknownFieldsSize(unknown_fields).
+  //
+  // Returns a pointer past the last written byte.
+  static uint8_t* SerializeUnknownFieldsToArray(
+      const UnknownFieldSet& unknown_fields, uint8_t* target) {
+    io::EpsCopyOutputStream stream(
+        target, static_cast<int>(ComputeUnknownFieldsSize(unknown_fields)),
+        io::CodedOutputStream::IsDefaultSerializationDeterministic());
+    return InternalSerializeUnknownFieldsToArray(unknown_fields, target,
+                                                 &stream);
+  }
+  static uint8_t* InternalSerializeUnknownFieldsToArray(
+      const UnknownFieldSet& unknown_fields, uint8_t* target,
+      io::EpsCopyOutputStream* stream);
+
+  // Same thing except for messages that have the message_set_wire_format
+  // option.
+  static void SerializeUnknownMessageSetItems(
+      const UnknownFieldSet& unknown_fields, io::CodedOutputStream* output) {
+    output->SetCur(InternalSerializeUnknownMessageSetItemsToArray(
+        unknown_fields, output->Cur(), output->EpsCopy()));
+  }
+  // Same as above, except writing directly to the provided buffer.
+  // Requires that the buffer have sufficient capacity for
+  // ComputeUnknownMessageSetItemsSize(unknown_fields).
+  //
+  // Returns a pointer past the last written byte.
+  static uint8_t* SerializeUnknownMessageSetItemsToArray(
+      const UnknownFieldSet& unknown_fields, uint8_t* target);
+  static uint8_t* InternalSerializeUnknownMessageSetItemsToArray(
+      const UnknownFieldSet& unknown_fields, uint8_t* target,
+      io::EpsCopyOutputStream* stream);
+
+  // Compute the size of the UnknownFieldSet on the wire.
+  static size_t ComputeUnknownFieldsSize(const UnknownFieldSet& unknown_fields);
+
+  // Same thing except for messages that have the message_set_wire_format
+  // option.
+  static size_t ComputeUnknownMessageSetItemsSize(
+      const UnknownFieldSet& unknown_fields);
+
+  // Helper functions for encoding and decoding tags.  (Inlined below and in
+  // _inl.h)
+  //
+  // This is different from MakeTag(field->number(), field->type()) in the
+  // case of packed repeated fields.
+  static uint32_t MakeTag(const FieldDescriptor* field);
+
+  // Parse a single field.  The input should start out positioned immediately
+  // after the tag.
+  static bool ParseAndMergeField(
+      uint32_t tag,
+      const FieldDescriptor* field,  // May be nullptr for unknown
+      Message* message, io::CodedInputStream* input);
+
+  // Serialize a single field.
+  static void SerializeFieldWithCachedSizes(
+      const FieldDescriptor* field,  // Cannot be nullptr
+      const Message& message, io::CodedOutputStream* output) {
+    output->SetCur(InternalSerializeField(field, message, output->Cur(),
+                                          output->EpsCopy()));
+  }
+  static uint8_t* InternalSerializeField(
+      const FieldDescriptor* field,  // Cannot be nullptr
+      const Message& message, uint8_t* target, io::EpsCopyOutputStream* stream);
+
+  // Compute size of a single field.  If the field is a message type, this
+  // will call ByteSize() for the embedded message, insuring that it caches
+  // its size.
+  static size_t FieldByteSize(const FieldDescriptor* field,  // Can't be nullptr
+                              const Message& message);
+
+  // Parse/serialize a MessageSet::Item group.  Used with messages that use
+  // option message_set_wire_format = true.
+  static bool ParseAndMergeMessageSetItem(io::CodedInputStream* input,
+                                          Message* message);
+  static void SerializeMessageSetItemWithCachedSizes(
+      const FieldDescriptor* field, const Message& message,
+      io::CodedOutputStream* output) {
+    output->SetCur(InternalSerializeMessageSetItem(
+        field, message, output->Cur(), output->EpsCopy()));
+  }
+  static uint8_t* InternalSerializeMessageSetItem(
+      const FieldDescriptor* field, const Message& message, uint8_t* target,
+      io::EpsCopyOutputStream* stream);
+  static size_t MessageSetItemByteSize(const FieldDescriptor* field,
+                                       const Message& message);
+
+  // Computes the byte size of a field, excluding tags. For packed fields, it
+  // only includes the size of the raw data, and not the size of the total
+  // length, but for other length-delimited types, the size of the length is
+  // included.
+  static size_t FieldDataOnlyByteSize(
+      const FieldDescriptor* field,  // Cannot be nullptr
+      const Message& message);
+
+  enum Operation {
+    PARSE = 0,
+    SERIALIZE = 1,
+  };
+
+  // Verifies that a string field is valid UTF8, logging an error if not.
+  // This function will not be called by newly generated protobuf code
+  // but remains present to support existing code.
+  static void VerifyUTF8String(const char* data, int size, Operation op);
+  // The NamedField variant takes a field name in order to produce an
+  // informative error message if verification fails.
+  static void VerifyUTF8StringNamedField(const char* data, int size,
+                                         Operation op, const char* field_name);
+
+ private:
+  struct MessageSetParser;
+  // Skip a MessageSet field.
+  static bool SkipMessageSetField(io::CodedInputStream* input,
+                                  uint32_t field_number,
+                                  UnknownFieldSet* unknown_fields);
+
+  // Parse a MessageSet field.
+  static bool ParseAndMergeMessageSetField(uint32_t field_number,
+                                           const FieldDescriptor* field,
+                                           Message* message,
+                                           io::CodedInputStream* input);
+  // Parses the value from the wire that belongs to tag.
+  static const char* _InternalParseAndMergeField(Message* msg, const char* ptr,
+                                                 internal::ParseContext* ctx,
+                                                 uint64_t tag,
+                                                 const Reflection* reflection,
+                                                 const FieldDescriptor* field);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(WireFormat);
+};
+
+// Subclass of FieldSkipper which saves skipped fields to an UnknownFieldSet.
+class PROTOBUF_EXPORT UnknownFieldSetFieldSkipper : public FieldSkipper {
+ public:
+  UnknownFieldSetFieldSkipper(UnknownFieldSet* unknown_fields)
+      : unknown_fields_(unknown_fields) {}
+  ~UnknownFieldSetFieldSkipper() override {}
+
+  // implements FieldSkipper -----------------------------------------
+  bool SkipField(io::CodedInputStream* input, uint32_t tag) override;
+  bool SkipMessage(io::CodedInputStream* input) override;
+  void SkipUnknownEnum(int field_number, int value) override;
+
+ protected:
+  UnknownFieldSet* unknown_fields_;
+};
+
+// inline methods ====================================================
+
+inline WireFormatLite::WireType WireFormat::WireTypeForField(
+    const FieldDescriptor* field) {
+  if (field->is_packed()) {
+    return WireFormatLite::WIRETYPE_LENGTH_DELIMITED;
+  } else {
+    return WireTypeForFieldType(field->type());
+  }
+}
+
+inline WireFormatLite::WireType WireFormat::WireTypeForFieldType(
+    FieldDescriptor::Type type) {
+  // Some compilers don't like enum -> enum casts, so we implicit_cast to
+  // int first.
+  return WireFormatLite::WireTypeForFieldType(
+      static_cast<WireFormatLite::FieldType>(implicit_cast<int>(type)));
+}
+
+inline uint32_t WireFormat::MakeTag(const FieldDescriptor* field) {
+  return WireFormatLite::MakeTag(field->number(), WireTypeForField(field));
+}
+
+inline size_t WireFormat::TagSize(int field_number,
+                                  FieldDescriptor::Type type) {
+  // Some compilers don't like enum -> enum casts, so we implicit_cast to
+  // int first.
+  return WireFormatLite::TagSize(
+      field_number,
+      static_cast<WireFormatLite::FieldType>(implicit_cast<int>(type)));
+}
+
+inline void WireFormat::VerifyUTF8String(const char* data, int size,
+                                         WireFormat::Operation op) {
+#ifdef GOOGLE_PROTOBUF_UTF8_VALIDATION_ENABLED
+  WireFormatLite::VerifyUtf8String(
+      data, size, static_cast<WireFormatLite::Operation>(op), nullptr);
+#else
+  // Avoid the compiler warning about unused variables.
+  (void)data;
+  (void)size;
+  (void)op;
+#endif
+}
+
+inline void WireFormat::VerifyUTF8StringNamedField(const char* data, int size,
+                                                   WireFormat::Operation op,
+                                                   const char* field_name) {
+#ifdef GOOGLE_PROTOBUF_UTF8_VALIDATION_ENABLED
+  WireFormatLite::VerifyUtf8String(
+      data, size, static_cast<WireFormatLite::Operation>(op), field_name);
+#else
+  // Avoid the compiler warning about unused variables.
+  (void)data;
+  (void)size;
+  (void)op;
+  (void)field_name;
+#endif
+}
+
+
+inline uint8_t* InternalSerializeUnknownMessageSetItemsToArray(
+    const UnknownFieldSet& unknown_fields, uint8_t* target,
+    io::EpsCopyOutputStream* stream) {
+  return WireFormat::InternalSerializeUnknownMessageSetItemsToArray(
+      unknown_fields, target, stream);
+}
+
+inline size_t ComputeUnknownMessageSetItemsSize(
+    const UnknownFieldSet& unknown_fields) {
+  return WireFormat::ComputeUnknownMessageSetItemsSize(unknown_fields);
+}
+
+// Compute the size of the UnknownFieldSet on the wire.
+PROTOBUF_EXPORT
+size_t ComputeUnknownFieldsSize(const InternalMetadata& metadata, size_t size,
+                                CachedSize* cached_size);
+
+size_t MapKeyDataOnlyByteSize(const FieldDescriptor* field,
+                              const MapKey& value);
+
+uint8_t* SerializeMapKeyWithCachedSizes(const FieldDescriptor* field,
+                                        const MapKey& value, uint8_t* target,
+                                        io::EpsCopyOutputStream* stream);
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_WIRE_FORMAT_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/wire_format_lite.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/wire_format_lite.h
new file mode 100644
index 0000000..80d3961
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/wire_format_lite.h
@@ -0,0 +1,1908 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//         atenasio@google.com (Chris Atenasio) (ZigZag transform)
+//         wink@google.com (Wink Saville) (refactored from wire_format.h)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This header is logically internal, but is made public because it is used
+// from protocol-compiler-generated code, which may reside in other components.
+
+#ifndef GOOGLE_PROTOBUF_WIRE_FORMAT_LITE_H__
+#define GOOGLE_PROTOBUF_WIRE_FORMAT_LITE_H__
+
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h>
+
+// Do UTF-8 validation on string type in Debug build only
+#ifndef NDEBUG
+#define GOOGLE_PROTOBUF_UTF8_VALIDATION_ENABLED
+#endif
+
+// Avoid conflict with iOS where <ConditionalMacros.h> #defines TYPE_BOOL.
+//
+// If some one needs the macro TYPE_BOOL in a file that includes this header,
+// it's possible to bring it back using push/pop_macro as follows.
+//
+// #pragma push_macro("TYPE_BOOL")
+// #include this header and/or all headers that need the macro to be undefined.
+// #pragma pop_macro("TYPE_BOOL")
+#undef TYPE_BOOL
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// This class is for internal use by the protocol buffer library and by
+// protocol-compiler-generated message classes.  It must not be called
+// directly by clients.
+//
+// This class contains helpers for implementing the binary protocol buffer
+// wire format without the need for reflection. Use WireFormat when using
+// reflection.
+//
+// This class is really a namespace that contains only static methods.
+class PROTOBUF_EXPORT WireFormatLite {
+ public:
+  // -----------------------------------------------------------------
+  // Helper constants and functions related to the format.  These are
+  // mostly meant for internal and generated code to use.
+
+  // The wire format is composed of a sequence of tag/value pairs, each
+  // of which contains the value of one field (or one element of a repeated
+  // field).  Each tag is encoded as a varint.  The lower bits of the tag
+  // identify its wire type, which specifies the format of the data to follow.
+  // The rest of the bits contain the field number.  Each type of field (as
+  // declared by FieldDescriptor::Type, in descriptor.h) maps to one of
+  // these wire types.  Immediately following each tag is the field's value,
+  // encoded in the format specified by the wire type.  Because the tag
+  // identifies the encoding of this data, it is possible to skip
+  // unrecognized fields for forwards compatibility.
+
+  enum WireType {
+    WIRETYPE_VARINT = 0,
+    WIRETYPE_FIXED64 = 1,
+    WIRETYPE_LENGTH_DELIMITED = 2,
+    WIRETYPE_START_GROUP = 3,
+    WIRETYPE_END_GROUP = 4,
+    WIRETYPE_FIXED32 = 5,
+  };
+
+  // Lite alternative to FieldDescriptor::Type.  Must be kept in sync.
+  enum FieldType {
+    TYPE_DOUBLE = 1,
+    TYPE_FLOAT = 2,
+    TYPE_INT64 = 3,
+    TYPE_UINT64 = 4,
+    TYPE_INT32 = 5,
+    TYPE_FIXED64 = 6,
+    TYPE_FIXED32 = 7,
+    TYPE_BOOL = 8,
+    TYPE_STRING = 9,
+    TYPE_GROUP = 10,
+    TYPE_MESSAGE = 11,
+    TYPE_BYTES = 12,
+    TYPE_UINT32 = 13,
+    TYPE_ENUM = 14,
+    TYPE_SFIXED32 = 15,
+    TYPE_SFIXED64 = 16,
+    TYPE_SINT32 = 17,
+    TYPE_SINT64 = 18,
+    MAX_FIELD_TYPE = 18,
+  };
+
+  // Lite alternative to FieldDescriptor::CppType.  Must be kept in sync.
+  enum CppType {
+    CPPTYPE_INT32 = 1,
+    CPPTYPE_INT64 = 2,
+    CPPTYPE_UINT32 = 3,
+    CPPTYPE_UINT64 = 4,
+    CPPTYPE_DOUBLE = 5,
+    CPPTYPE_FLOAT = 6,
+    CPPTYPE_BOOL = 7,
+    CPPTYPE_ENUM = 8,
+    CPPTYPE_STRING = 9,
+    CPPTYPE_MESSAGE = 10,
+    MAX_CPPTYPE = 10,
+  };
+
+  // Helper method to get the CppType for a particular Type.
+  static CppType FieldTypeToCppType(FieldType type);
+
+  // Given a FieldDescriptor::Type return its WireType
+  static inline WireFormatLite::WireType WireTypeForFieldType(
+      WireFormatLite::FieldType type) {
+    return kWireTypeForFieldType[type];
+  }
+
+  // Number of bits in a tag which identify the wire type.
+  static constexpr int kTagTypeBits = 3;
+  // Mask for those bits.
+  static constexpr uint32_t kTagTypeMask = (1 << kTagTypeBits) - 1;
+
+  // Helper functions for encoding and decoding tags.  (Inlined below and in
+  // _inl.h)
+  //
+  // This is different from MakeTag(field->number(), field->type()) in the
+  // case of packed repeated fields.
+  constexpr static uint32_t MakeTag(int field_number, WireType type);
+  static WireType GetTagWireType(uint32_t tag);
+  static int GetTagFieldNumber(uint32_t tag);
+
+  // Compute the byte size of a tag.  For groups, this includes both the start
+  // and end tags.
+  static inline size_t TagSize(int field_number,
+                               WireFormatLite::FieldType type);
+
+  // Skips a field value with the given tag.  The input should start
+  // positioned immediately after the tag.  Skipped values are simply
+  // discarded, not recorded anywhere.  See WireFormat::SkipField() for a
+  // version that records to an UnknownFieldSet.
+  static bool SkipField(io::CodedInputStream* input, uint32_t tag);
+
+  // Skips a field value with the given tag.  The input should start
+  // positioned immediately after the tag. Skipped values are recorded to a
+  // CodedOutputStream.
+  static bool SkipField(io::CodedInputStream* input, uint32_t tag,
+                        io::CodedOutputStream* output);
+
+  // Reads and ignores a message from the input.  Skipped values are simply
+  // discarded, not recorded anywhere.  See WireFormat::SkipMessage() for a
+  // version that records to an UnknownFieldSet.
+  static bool SkipMessage(io::CodedInputStream* input);
+
+  // Reads and ignores a message from the input.  Skipped values are recorded
+  // to a CodedOutputStream.
+  static bool SkipMessage(io::CodedInputStream* input,
+                          io::CodedOutputStream* output);
+
+  // This macro does the same thing as WireFormatLite::MakeTag(), but the
+  // result is usable as a compile-time constant, which makes it usable
+  // as a switch case or a template input.  WireFormatLite::MakeTag() is more
+  // type-safe, though, so prefer it if possible.
+#define GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(FIELD_NUMBER, TYPE) \
+  static_cast<uint32_t>((static_cast<uint32_t>(FIELD_NUMBER) << 3) | (TYPE))
+
+  // These are the tags for the old MessageSet format, which was defined as:
+  //   message MessageSet {
+  //     repeated group Item = 1 {
+  //       required int32 type_id = 2;
+  //       required string message = 3;
+  //     }
+  //   }
+  static constexpr int kMessageSetItemNumber = 1;
+  static constexpr int kMessageSetTypeIdNumber = 2;
+  static constexpr int kMessageSetMessageNumber = 3;
+  static const int kMessageSetItemStartTag = GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(
+      kMessageSetItemNumber, WireFormatLite::WIRETYPE_START_GROUP);
+  static const int kMessageSetItemEndTag = GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(
+      kMessageSetItemNumber, WireFormatLite::WIRETYPE_END_GROUP);
+  static const int kMessageSetTypeIdTag = GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(
+      kMessageSetTypeIdNumber, WireFormatLite::WIRETYPE_VARINT);
+  static const int kMessageSetMessageTag = GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(
+      kMessageSetMessageNumber, WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
+
+  // Byte size of all tags of a MessageSet::Item combined.
+  static const size_t kMessageSetItemTagsSize;
+
+  // Helper functions for converting between floats/doubles and IEEE-754
+  // uint32s/uint64s so that they can be written.  (Assumes your platform
+  // uses IEEE-754 floats.)
+  static uint32_t EncodeFloat(float value);
+  static float DecodeFloat(uint32_t value);
+  static uint64_t EncodeDouble(double value);
+  static double DecodeDouble(uint64_t value);
+
+  // Helper functions for mapping signed integers to unsigned integers in
+  // such a way that numbers with small magnitudes will encode to smaller
+  // varints.  If you simply static_cast a negative number to an unsigned
+  // number and varint-encode it, it will always take 10 bytes, defeating
+  // the purpose of varint.  So, for the "sint32" and "sint64" field types,
+  // we ZigZag-encode the values.
+  static uint32_t ZigZagEncode32(int32_t n);
+  static int32_t ZigZagDecode32(uint32_t n);
+  static uint64_t ZigZagEncode64(int64_t n);
+  static int64_t ZigZagDecode64(uint64_t n);
+
+  // =================================================================
+  // Methods for reading/writing individual field.
+
+  // Read fields, not including tags.  The assumption is that you already
+  // read the tag to determine what field to read.
+
+  // For primitive fields, we just use a templatized routine parameterized by
+  // the represented type and the FieldType. These are specialized with the
+  // appropriate definition for each declared type.
+  template <typename CType, enum FieldType DeclaredType>
+  PROTOBUF_NDEBUG_INLINE static bool ReadPrimitive(io::CodedInputStream* input,
+                                                   CType* value);
+
+  // Reads repeated primitive values, with optimizations for repeats.
+  // tag_size and tag should both be compile-time constants provided by the
+  // protocol compiler.
+  template <typename CType, enum FieldType DeclaredType>
+  PROTOBUF_NDEBUG_INLINE static bool ReadRepeatedPrimitive(
+      int tag_size, uint32_t tag, io::CodedInputStream* input,
+      RepeatedField<CType>* value);
+
+  // Identical to ReadRepeatedPrimitive, except will not inline the
+  // implementation.
+  template <typename CType, enum FieldType DeclaredType>
+  static bool ReadRepeatedPrimitiveNoInline(int tag_size, uint32_t tag,
+                                            io::CodedInputStream* input,
+                                            RepeatedField<CType>* value);
+
+  // Reads a primitive value directly from the provided buffer. It returns a
+  // pointer past the segment of data that was read.
+  //
+  // This is only implemented for the types with fixed wire size, e.g.
+  // float, double, and the (s)fixed* types.
+  template <typename CType, enum FieldType DeclaredType>
+  PROTOBUF_NDEBUG_INLINE static const uint8_t* ReadPrimitiveFromArray(
+      const uint8_t* buffer, CType* value);
+
+  // Reads a primitive packed field.
+  //
+  // This is only implemented for packable types.
+  template <typename CType, enum FieldType DeclaredType>
+  PROTOBUF_NDEBUG_INLINE static bool ReadPackedPrimitive(
+      io::CodedInputStream* input, RepeatedField<CType>* value);
+
+  // Identical to ReadPackedPrimitive, except will not inline the
+  // implementation.
+  template <typename CType, enum FieldType DeclaredType>
+  static bool ReadPackedPrimitiveNoInline(io::CodedInputStream* input,
+                                          RepeatedField<CType>* value);
+
+  // Read a packed enum field. If the is_valid function is not nullptr, values
+  // for which is_valid(value) returns false are silently dropped.
+  static bool ReadPackedEnumNoInline(io::CodedInputStream* input,
+                                     bool (*is_valid)(int),
+                                     RepeatedField<int>* values);
+
+  // Read a packed enum field. If the is_valid function is not nullptr, values
+  // for which is_valid(value) returns false are appended to
+  // unknown_fields_stream.
+  static bool ReadPackedEnumPreserveUnknowns(
+      io::CodedInputStream* input, int field_number, bool (*is_valid)(int),
+      io::CodedOutputStream* unknown_fields_stream, RepeatedField<int>* values);
+
+  // Read a string.  ReadString(..., std::string* value) requires an
+  // existing std::string.
+  static inline bool ReadString(io::CodedInputStream* input,
+                                std::string* value);
+  // ReadString(..., std::string** p) is internal-only, and should only be
+  // called from generated code. It starts by setting *p to "new std::string" if
+  // *p == &GetEmptyStringAlreadyInited().  It then invokes
+  // ReadString(io::CodedInputStream* input, *p).  This is useful for reducing
+  // code size.
+  static inline bool ReadString(io::CodedInputStream* input, std::string** p);
+  // Analogous to ReadString().
+  static bool ReadBytes(io::CodedInputStream* input, std::string* value);
+  static bool ReadBytes(io::CodedInputStream* input, std::string** p);
+
+  enum Operation {
+    PARSE = 0,
+    SERIALIZE = 1,
+  };
+
+  // Returns true if the data is valid UTF-8.
+  static bool VerifyUtf8String(const char* data, int size, Operation op,
+                               const char* field_name);
+
+  template <typename MessageType>
+  static inline bool ReadGroup(int field_number, io::CodedInputStream* input,
+                               MessageType* value);
+
+  template <typename MessageType>
+  static inline bool ReadMessage(io::CodedInputStream* input,
+                                 MessageType* value);
+
+  template <typename MessageType>
+  static inline bool ReadMessageNoVirtual(io::CodedInputStream* input,
+                                          MessageType* value) {
+    return ReadMessage(input, value);
+  }
+
+  // Write a tag.  The Write*() functions typically include the tag, so
+  // normally there's no need to call this unless using the Write*NoTag()
+  // variants.
+  PROTOBUF_NDEBUG_INLINE static void WriteTag(int field_number, WireType type,
+                                              io::CodedOutputStream* output);
+
+  // Write fields, without tags.
+  PROTOBUF_NDEBUG_INLINE static void WriteInt32NoTag(
+      int32_t value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteInt64NoTag(
+      int64_t value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteUInt32NoTag(
+      uint32_t value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteUInt64NoTag(
+      uint64_t value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteSInt32NoTag(
+      int32_t value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteSInt64NoTag(
+      int64_t value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteFixed32NoTag(
+      uint32_t value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteFixed64NoTag(
+      uint64_t value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteSFixed32NoTag(
+      int32_t value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteSFixed64NoTag(
+      int64_t value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteFloatNoTag(
+      float value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteDoubleNoTag(
+      double value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteBoolNoTag(
+      bool value, io::CodedOutputStream* output);
+  PROTOBUF_NDEBUG_INLINE static void WriteEnumNoTag(
+      int value, io::CodedOutputStream* output);
+
+  // Write array of primitive fields, without tags
+  static void WriteFloatArray(const float* a, int n,
+                              io::CodedOutputStream* output);
+  static void WriteDoubleArray(const double* a, int n,
+                               io::CodedOutputStream* output);
+  static void WriteFixed32Array(const uint32_t* a, int n,
+                                io::CodedOutputStream* output);
+  static void WriteFixed64Array(const uint64_t* a, int n,
+                                io::CodedOutputStream* output);
+  static void WriteSFixed32Array(const int32_t* a, int n,
+                                 io::CodedOutputStream* output);
+  static void WriteSFixed64Array(const int64_t* a, int n,
+                                 io::CodedOutputStream* output);
+  static void WriteBoolArray(const bool* a, int n,
+                             io::CodedOutputStream* output);
+
+  // Write fields, including tags.
+  static void WriteInt32(int field_number, int32_t value,
+                         io::CodedOutputStream* output);
+  static void WriteInt64(int field_number, int64_t value,
+                         io::CodedOutputStream* output);
+  static void WriteUInt32(int field_number, uint32_t value,
+                          io::CodedOutputStream* output);
+  static void WriteUInt64(int field_number, uint64_t value,
+                          io::CodedOutputStream* output);
+  static void WriteSInt32(int field_number, int32_t value,
+                          io::CodedOutputStream* output);
+  static void WriteSInt64(int field_number, int64_t value,
+                          io::CodedOutputStream* output);
+  static void WriteFixed32(int field_number, uint32_t value,
+                           io::CodedOutputStream* output);
+  static void WriteFixed64(int field_number, uint64_t value,
+                           io::CodedOutputStream* output);
+  static void WriteSFixed32(int field_number, int32_t value,
+                            io::CodedOutputStream* output);
+  static void WriteSFixed64(int field_number, int64_t value,
+                            io::CodedOutputStream* output);
+  static void WriteFloat(int field_number, float value,
+                         io::CodedOutputStream* output);
+  static void WriteDouble(int field_number, double value,
+                          io::CodedOutputStream* output);
+  static void WriteBool(int field_number, bool value,
+                        io::CodedOutputStream* output);
+  static void WriteEnum(int field_number, int value,
+                        io::CodedOutputStream* output);
+
+  static void WriteString(int field_number, const std::string& value,
+                          io::CodedOutputStream* output);
+  static void WriteBytes(int field_number, const std::string& value,
+                         io::CodedOutputStream* output);
+  static void WriteStringMaybeAliased(int field_number,
+                                      const std::string& value,
+                                      io::CodedOutputStream* output);
+  static void WriteBytesMaybeAliased(int field_number, const std::string& value,
+                                     io::CodedOutputStream* output);
+
+  static void WriteGroup(int field_number, const MessageLite& value,
+                         io::CodedOutputStream* output);
+  static void WriteMessage(int field_number, const MessageLite& value,
+                           io::CodedOutputStream* output);
+  // Like above, but these will check if the output stream has enough
+  // space to write directly to a flat array.
+  static void WriteGroupMaybeToArray(int field_number, const MessageLite& value,
+                                     io::CodedOutputStream* output);
+  static void WriteMessageMaybeToArray(int field_number,
+                                       const MessageLite& value,
+                                       io::CodedOutputStream* output);
+
+  // Like above, but de-virtualize the call to SerializeWithCachedSizes().  The
+  // pointer must point at an instance of MessageType, *not* a subclass (or
+  // the subclass must not override SerializeWithCachedSizes()).
+  template <typename MessageType>
+  static inline void WriteGroupNoVirtual(int field_number,
+                                         const MessageType& value,
+                                         io::CodedOutputStream* output);
+  template <typename MessageType>
+  static inline void WriteMessageNoVirtual(int field_number,
+                                           const MessageType& value,
+                                           io::CodedOutputStream* output);
+
+  // Like above, but use only *ToArray methods of CodedOutputStream.
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteTagToArray(int field_number,
+                                                         WireType type,
+                                                         uint8_t* target);
+
+  // Write fields, without tags.
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteInt32NoTagToArray(
+      int32_t value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteInt64NoTagToArray(
+      int64_t value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteUInt32NoTagToArray(
+      uint32_t value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteUInt64NoTagToArray(
+      uint64_t value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSInt32NoTagToArray(
+      int32_t value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSInt64NoTagToArray(
+      int64_t value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFixed32NoTagToArray(
+      uint32_t value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFixed64NoTagToArray(
+      uint64_t value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSFixed32NoTagToArray(
+      int32_t value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSFixed64NoTagToArray(
+      int64_t value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFloatNoTagToArray(
+      float value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteDoubleNoTagToArray(
+      double value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteBoolNoTagToArray(bool value,
+                                                               uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteEnumNoTagToArray(int value,
+                                                               uint8_t* target);
+
+  // Write fields, without tags.  These require that value.size() > 0.
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WritePrimitiveNoTagToArray(
+      const RepeatedField<T>& value, uint8_t* (*Writer)(T, uint8_t*),
+      uint8_t* target);
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFixedNoTagToArray(
+      const RepeatedField<T>& value, uint8_t* (*Writer)(T, uint8_t*),
+      uint8_t* target);
+
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteInt32NoTagToArray(
+      const RepeatedField<int32_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteInt64NoTagToArray(
+      const RepeatedField<int64_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteUInt32NoTagToArray(
+      const RepeatedField<uint32_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteUInt64NoTagToArray(
+      const RepeatedField<uint64_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSInt32NoTagToArray(
+      const RepeatedField<int32_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSInt64NoTagToArray(
+      const RepeatedField<int64_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFixed32NoTagToArray(
+      const RepeatedField<uint32_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFixed64NoTagToArray(
+      const RepeatedField<uint64_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSFixed32NoTagToArray(
+      const RepeatedField<int32_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSFixed64NoTagToArray(
+      const RepeatedField<int64_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFloatNoTagToArray(
+      const RepeatedField<float>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteDoubleNoTagToArray(
+      const RepeatedField<double>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteBoolNoTagToArray(
+      const RepeatedField<bool>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteEnumNoTagToArray(
+      const RepeatedField<int>& value, uint8_t* output);
+
+  // Write fields, including tags.
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteInt32ToArray(int field_number,
+                                                           int32_t value,
+                                                           uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteInt64ToArray(int field_number,
+                                                           int64_t value,
+                                                           uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteUInt32ToArray(int field_number,
+                                                            uint32_t value,
+                                                            uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteUInt64ToArray(int field_number,
+                                                            uint64_t value,
+                                                            uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSInt32ToArray(int field_number,
+                                                            int32_t value,
+                                                            uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSInt64ToArray(int field_number,
+                                                            int64_t value,
+                                                            uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFixed32ToArray(int field_number,
+                                                             uint32_t value,
+                                                             uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFixed64ToArray(int field_number,
+                                                             uint64_t value,
+                                                             uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSFixed32ToArray(int field_number,
+                                                              int32_t value,
+                                                              uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSFixed64ToArray(int field_number,
+                                                              int64_t value,
+                                                              uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFloatToArray(int field_number,
+                                                           float value,
+                                                           uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteDoubleToArray(int field_number,
+                                                            double value,
+                                                            uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteBoolToArray(int field_number,
+                                                          bool value,
+                                                          uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteEnumToArray(int field_number,
+                                                          int value,
+                                                          uint8_t* target);
+
+  template <typename T>
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WritePrimitiveToArray(
+      int field_number, const RepeatedField<T>& value,
+      uint8_t* (*Writer)(int, T, uint8_t*), uint8_t* target);
+
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteInt32ToArray(
+      int field_number, const RepeatedField<int32_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteInt64ToArray(
+      int field_number, const RepeatedField<int64_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteUInt32ToArray(
+      int field_number, const RepeatedField<uint32_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteUInt64ToArray(
+      int field_number, const RepeatedField<uint64_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSInt32ToArray(
+      int field_number, const RepeatedField<int32_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSInt64ToArray(
+      int field_number, const RepeatedField<int64_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFixed32ToArray(
+      int field_number, const RepeatedField<uint32_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFixed64ToArray(
+      int field_number, const RepeatedField<uint64_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSFixed32ToArray(
+      int field_number, const RepeatedField<int32_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteSFixed64ToArray(
+      int field_number, const RepeatedField<int64_t>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteFloatToArray(
+      int field_number, const RepeatedField<float>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteDoubleToArray(
+      int field_number, const RepeatedField<double>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteBoolToArray(
+      int field_number, const RepeatedField<bool>& value, uint8_t* output);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteEnumToArray(
+      int field_number, const RepeatedField<int>& value, uint8_t* output);
+
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteStringToArray(
+      int field_number, const std::string& value, uint8_t* target);
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteBytesToArray(
+      int field_number, const std::string& value, uint8_t* target);
+
+  // Whether to serialize deterministically (e.g., map keys are
+  // sorted) is a property of a CodedOutputStream, and in the process
+  // of serialization, the "ToArray" variants may be invoked.  But they don't
+  // have a CodedOutputStream available, so they get an additional parameter
+  // telling them whether to serialize deterministically.
+  static uint8_t* InternalWriteGroup(int field_number, const MessageLite& value,
+                                     uint8_t* target,
+                                     io::EpsCopyOutputStream* stream);
+  static uint8_t* InternalWriteMessage(int field_number,
+                                       const MessageLite& value,
+                                       int cached_size, uint8_t* target,
+                                       io::EpsCopyOutputStream* stream);
+
+  // Like above, but de-virtualize the call to SerializeWithCachedSizes().  The
+  // pointer must point at an instance of MessageType, *not* a subclass (or
+  // the subclass must not override SerializeWithCachedSizes()).
+  template <typename MessageType>
+  PROTOBUF_NDEBUG_INLINE static uint8_t* InternalWriteGroupNoVirtualToArray(
+      int field_number, const MessageType& value, uint8_t* target);
+  template <typename MessageType>
+  PROTOBUF_NDEBUG_INLINE static uint8_t* InternalWriteMessageNoVirtualToArray(
+      int field_number, const MessageType& value, uint8_t* target);
+
+  // For backward-compatibility, the last four methods also have versions
+  // that are non-deterministic always.
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteGroupToArray(
+      int field_number, const MessageLite& value, uint8_t* target) {
+    io::EpsCopyOutputStream stream(
+        target,
+        value.GetCachedSize() +
+            static_cast<int>(2 * io::CodedOutputStream::VarintSize32(
+                                     static_cast<uint32_t>(field_number) << 3)),
+        io::CodedOutputStream::IsDefaultSerializationDeterministic());
+    return InternalWriteGroup(field_number, value, target, &stream);
+  }
+  PROTOBUF_NDEBUG_INLINE static uint8_t* WriteMessageToArray(
+      int field_number, const MessageLite& value, uint8_t* target) {
+    int size = value.GetCachedSize();
+    io::EpsCopyOutputStream stream(
+        target,
+        size + static_cast<int>(io::CodedOutputStream::VarintSize32(
+                                    static_cast<uint32_t>(field_number) << 3) +
+                                io::CodedOutputStream::VarintSize32(size)),
+        io::CodedOutputStream::IsDefaultSerializationDeterministic());
+    return InternalWriteMessage(field_number, value, value.GetCachedSize(),
+                                target, &stream);
+  }
+
+  // Compute the byte size of a field.  The XxSize() functions do NOT include
+  // the tag, so you must also call TagSize().  (This is because, for repeated
+  // fields, you should only call TagSize() once and multiply it by the element
+  // count, but you may have to call XxSize() for each individual element.)
+  static inline size_t Int32Size(int32_t value);
+  static inline size_t Int64Size(int64_t value);
+  static inline size_t UInt32Size(uint32_t value);
+  static inline size_t UInt64Size(uint64_t value);
+  static inline size_t SInt32Size(int32_t value);
+  static inline size_t SInt64Size(int64_t value);
+  static inline size_t EnumSize(int value);
+  static inline size_t Int32SizePlusOne(int32_t value);
+  static inline size_t Int64SizePlusOne(int64_t value);
+  static inline size_t UInt32SizePlusOne(uint32_t value);
+  static inline size_t UInt64SizePlusOne(uint64_t value);
+  static inline size_t SInt32SizePlusOne(int32_t value);
+  static inline size_t SInt64SizePlusOne(int64_t value);
+  static inline size_t EnumSizePlusOne(int value);
+
+  static size_t Int32Size(const RepeatedField<int32_t>& value);
+  static size_t Int64Size(const RepeatedField<int64_t>& value);
+  static size_t UInt32Size(const RepeatedField<uint32_t>& value);
+  static size_t UInt64Size(const RepeatedField<uint64_t>& value);
+  static size_t SInt32Size(const RepeatedField<int32_t>& value);
+  static size_t SInt64Size(const RepeatedField<int64_t>& value);
+  static size_t EnumSize(const RepeatedField<int>& value);
+
+  // These types always have the same size.
+  static constexpr size_t kFixed32Size = 4;
+  static constexpr size_t kFixed64Size = 8;
+  static constexpr size_t kSFixed32Size = 4;
+  static constexpr size_t kSFixed64Size = 8;
+  static constexpr size_t kFloatSize = 4;
+  static constexpr size_t kDoubleSize = 8;
+  static constexpr size_t kBoolSize = 1;
+
+  static inline size_t StringSize(const std::string& value);
+  static inline size_t BytesSize(const std::string& value);
+
+  template <typename MessageType>
+  static inline size_t GroupSize(const MessageType& value);
+  template <typename MessageType>
+  static inline size_t MessageSize(const MessageType& value);
+
+  // Like above, but de-virtualize the call to ByteSize().  The
+  // pointer must point at an instance of MessageType, *not* a subclass (or
+  // the subclass must not override ByteSize()).
+  template <typename MessageType>
+  static inline size_t GroupSizeNoVirtual(const MessageType& value);
+  template <typename MessageType>
+  static inline size_t MessageSizeNoVirtual(const MessageType& value);
+
+  // Given the length of data, calculate the byte size of the data on the
+  // wire if we encode the data as a length delimited field.
+  static inline size_t LengthDelimitedSize(size_t length);
+
+ private:
+  // A helper method for the repeated primitive reader. This method has
+  // optimizations for primitive types that have fixed size on the wire, and
+  // can be read using potentially faster paths.
+  template <typename CType, enum FieldType DeclaredType>
+  PROTOBUF_NDEBUG_INLINE static bool ReadRepeatedFixedSizePrimitive(
+      int tag_size, uint32_t tag, io::CodedInputStream* input,
+      RepeatedField<CType>* value);
+
+  // Like ReadRepeatedFixedSizePrimitive but for packed primitive fields.
+  template <typename CType, enum FieldType DeclaredType>
+  PROTOBUF_NDEBUG_INLINE static bool ReadPackedFixedSizePrimitive(
+      io::CodedInputStream* input, RepeatedField<CType>* value);
+
+  static const CppType kFieldTypeToCppTypeMap[];
+  static const WireFormatLite::WireType kWireTypeForFieldType[];
+  static void WriteSubMessageMaybeToArray(int size, const MessageLite& value,
+                                          io::CodedOutputStream* output);
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(WireFormatLite);
+};
+
+// A class which deals with unknown values.  The default implementation just
+// discards them.  WireFormat defines a subclass which writes to an
+// UnknownFieldSet.  This class is used by ExtensionSet::ParseField(), since
+// ExtensionSet is part of the lite library but UnknownFieldSet is not.
+class PROTOBUF_EXPORT FieldSkipper {
+ public:
+  FieldSkipper() {}
+  virtual ~FieldSkipper() {}
+
+  // Skip a field whose tag has already been consumed.
+  virtual bool SkipField(io::CodedInputStream* input, uint32_t tag);
+
+  // Skip an entire message or group, up to an end-group tag (which is consumed)
+  // or end-of-stream.
+  virtual bool SkipMessage(io::CodedInputStream* input);
+
+  // Deal with an already-parsed unrecognized enum value.  The default
+  // implementation does nothing, but the UnknownFieldSet-based implementation
+  // saves it as an unknown varint.
+  virtual void SkipUnknownEnum(int field_number, int value);
+};
+
+// Subclass of FieldSkipper which saves skipped fields to a CodedOutputStream.
+
+class PROTOBUF_EXPORT CodedOutputStreamFieldSkipper : public FieldSkipper {
+ public:
+  explicit CodedOutputStreamFieldSkipper(io::CodedOutputStream* unknown_fields)
+      : unknown_fields_(unknown_fields) {}
+  ~CodedOutputStreamFieldSkipper() override {}
+
+  // implements FieldSkipper -----------------------------------------
+  bool SkipField(io::CodedInputStream* input, uint32_t tag) override;
+  bool SkipMessage(io::CodedInputStream* input) override;
+  void SkipUnknownEnum(int field_number, int value) override;
+
+ protected:
+  io::CodedOutputStream* unknown_fields_;
+};
+
+// inline methods ====================================================
+
+inline WireFormatLite::CppType WireFormatLite::FieldTypeToCppType(
+    FieldType type) {
+  return kFieldTypeToCppTypeMap[type];
+}
+
+constexpr inline uint32_t WireFormatLite::MakeTag(int field_number,
+                                                  WireType type) {
+  return GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(field_number, type);
+}
+
+inline WireFormatLite::WireType WireFormatLite::GetTagWireType(uint32_t tag) {
+  return static_cast<WireType>(tag & kTagTypeMask);
+}
+
+inline int WireFormatLite::GetTagFieldNumber(uint32_t tag) {
+  return static_cast<int>(tag >> kTagTypeBits);
+}
+
+inline size_t WireFormatLite::TagSize(int field_number,
+                                      WireFormatLite::FieldType type) {
+  size_t result = io::CodedOutputStream::VarintSize32(
+      static_cast<uint32_t>(field_number << kTagTypeBits));
+  if (type == TYPE_GROUP) {
+    // Groups have both a start and an end tag.
+    return result * 2;
+  } else {
+    return result;
+  }
+}
+
+inline uint32_t WireFormatLite::EncodeFloat(float value) {
+  return bit_cast<uint32_t>(value);
+}
+
+inline float WireFormatLite::DecodeFloat(uint32_t value) {
+  return bit_cast<float>(value);
+}
+
+inline uint64_t WireFormatLite::EncodeDouble(double value) {
+  return bit_cast<uint64_t>(value);
+}
+
+inline double WireFormatLite::DecodeDouble(uint64_t value) {
+  return bit_cast<double>(value);
+}
+
+// ZigZag Transform:  Encodes signed integers so that they can be
+// effectively used with varint encoding.
+//
+// varint operates on unsigned integers, encoding smaller numbers into
+// fewer bytes.  If you try to use it on a signed integer, it will treat
+// this number as a very large unsigned integer, which means that even
+// small signed numbers like -1 will take the maximum number of bytes
+// (10) to encode.  ZigZagEncode() maps signed integers to unsigned
+// in such a way that those with a small absolute value will have smaller
+// encoded values, making them appropriate for encoding using varint.
+//
+//       int32_t ->     uint32_t
+// -------------------------
+//           0 ->          0
+//          -1 ->          1
+//           1 ->          2
+//          -2 ->          3
+//         ... ->        ...
+//  2147483647 -> 4294967294
+// -2147483648 -> 4294967295
+//
+//        >> encode >>
+//        << decode <<
+
+inline uint32_t WireFormatLite::ZigZagEncode32(int32_t n) {
+  // Note:  the right-shift must be arithmetic
+  // Note:  left shift must be unsigned because of overflow
+  return (static_cast<uint32_t>(n) << 1) ^ static_cast<uint32_t>(n >> 31);
+}
+
+inline int32_t WireFormatLite::ZigZagDecode32(uint32_t n) {
+  // Note:  Using unsigned types prevent undefined behavior
+  return static_cast<int32_t>((n >> 1) ^ (~(n & 1) + 1));
+}
+
+inline uint64_t WireFormatLite::ZigZagEncode64(int64_t n) {
+  // Note:  the right-shift must be arithmetic
+  // Note:  left shift must be unsigned because of overflow
+  return (static_cast<uint64_t>(n) << 1) ^ static_cast<uint64_t>(n >> 63);
+}
+
+inline int64_t WireFormatLite::ZigZagDecode64(uint64_t n) {
+  // Note:  Using unsigned types prevent undefined behavior
+  return static_cast<int64_t>((n >> 1) ^ (~(n & 1) + 1));
+}
+
+// String is for UTF-8 text only, but, even so, ReadString() can simply
+// call ReadBytes().
+
+inline bool WireFormatLite::ReadString(io::CodedInputStream* input,
+                                       std::string* value) {
+  return ReadBytes(input, value);
+}
+
+inline bool WireFormatLite::ReadString(io::CodedInputStream* input,
+                                       std::string** p) {
+  return ReadBytes(input, p);
+}
+
+inline uint8_t* InternalSerializeUnknownMessageSetItemsToArray(
+    const std::string& unknown_fields, uint8_t* target,
+    io::EpsCopyOutputStream* stream) {
+  return stream->WriteRaw(unknown_fields.data(),
+                          static_cast<int>(unknown_fields.size()), target);
+}
+
+inline size_t ComputeUnknownMessageSetItemsSize(
+    const std::string& unknown_fields) {
+  return unknown_fields.size();
+}
+
+// Implementation details of ReadPrimitive.
+
+template <>
+inline bool WireFormatLite::ReadPrimitive<int32_t, WireFormatLite::TYPE_INT32>(
+    io::CodedInputStream* input, int32_t* value) {
+  uint32_t temp;
+  if (!input->ReadVarint32(&temp)) return false;
+  *value = static_cast<int32_t>(temp);
+  return true;
+}
+template <>
+inline bool WireFormatLite::ReadPrimitive<int64_t, WireFormatLite::TYPE_INT64>(
+    io::CodedInputStream* input, int64_t* value) {
+  uint64_t temp;
+  if (!input->ReadVarint64(&temp)) return false;
+  *value = static_cast<int64_t>(temp);
+  return true;
+}
+template <>
+inline bool
+WireFormatLite::ReadPrimitive<uint32_t, WireFormatLite::TYPE_UINT32>(
+    io::CodedInputStream* input, uint32_t* value) {
+  return input->ReadVarint32(value);
+}
+template <>
+inline bool
+WireFormatLite::ReadPrimitive<uint64_t, WireFormatLite::TYPE_UINT64>(
+    io::CodedInputStream* input, uint64_t* value) {
+  return input->ReadVarint64(value);
+}
+template <>
+inline bool WireFormatLite::ReadPrimitive<int32_t, WireFormatLite::TYPE_SINT32>(
+    io::CodedInputStream* input, int32_t* value) {
+  uint32_t temp;
+  if (!input->ReadVarint32(&temp)) return false;
+  *value = ZigZagDecode32(temp);
+  return true;
+}
+template <>
+inline bool WireFormatLite::ReadPrimitive<int64_t, WireFormatLite::TYPE_SINT64>(
+    io::CodedInputStream* input, int64_t* value) {
+  uint64_t temp;
+  if (!input->ReadVarint64(&temp)) return false;
+  *value = ZigZagDecode64(temp);
+  return true;
+}
+template <>
+inline bool
+WireFormatLite::ReadPrimitive<uint32_t, WireFormatLite::TYPE_FIXED32>(
+    io::CodedInputStream* input, uint32_t* value) {
+  return input->ReadLittleEndian32(value);
+}
+template <>
+inline bool
+WireFormatLite::ReadPrimitive<uint64_t, WireFormatLite::TYPE_FIXED64>(
+    io::CodedInputStream* input, uint64_t* value) {
+  return input->ReadLittleEndian64(value);
+}
+template <>
+inline bool
+WireFormatLite::ReadPrimitive<int32_t, WireFormatLite::TYPE_SFIXED32>(
+    io::CodedInputStream* input, int32_t* value) {
+  uint32_t temp;
+  if (!input->ReadLittleEndian32(&temp)) return false;
+  *value = static_cast<int32_t>(temp);
+  return true;
+}
+template <>
+inline bool
+WireFormatLite::ReadPrimitive<int64_t, WireFormatLite::TYPE_SFIXED64>(
+    io::CodedInputStream* input, int64_t* value) {
+  uint64_t temp;
+  if (!input->ReadLittleEndian64(&temp)) return false;
+  *value = static_cast<int64_t>(temp);
+  return true;
+}
+template <>
+inline bool WireFormatLite::ReadPrimitive<float, WireFormatLite::TYPE_FLOAT>(
+    io::CodedInputStream* input, float* value) {
+  uint32_t temp;
+  if (!input->ReadLittleEndian32(&temp)) return false;
+  *value = DecodeFloat(temp);
+  return true;
+}
+template <>
+inline bool WireFormatLite::ReadPrimitive<double, WireFormatLite::TYPE_DOUBLE>(
+    io::CodedInputStream* input, double* value) {
+  uint64_t temp;
+  if (!input->ReadLittleEndian64(&temp)) return false;
+  *value = DecodeDouble(temp);
+  return true;
+}
+template <>
+inline bool WireFormatLite::ReadPrimitive<bool, WireFormatLite::TYPE_BOOL>(
+    io::CodedInputStream* input, bool* value) {
+  uint64_t temp;
+  if (!input->ReadVarint64(&temp)) return false;
+  *value = temp != 0;
+  return true;
+}
+template <>
+inline bool WireFormatLite::ReadPrimitive<int, WireFormatLite::TYPE_ENUM>(
+    io::CodedInputStream* input, int* value) {
+  uint32_t temp;
+  if (!input->ReadVarint32(&temp)) return false;
+  *value = static_cast<int>(temp);
+  return true;
+}
+
+template <>
+inline const uint8_t*
+WireFormatLite::ReadPrimitiveFromArray<uint32_t, WireFormatLite::TYPE_FIXED32>(
+    const uint8_t* buffer, uint32_t* value) {
+  return io::CodedInputStream::ReadLittleEndian32FromArray(buffer, value);
+}
+template <>
+inline const uint8_t*
+WireFormatLite::ReadPrimitiveFromArray<uint64_t, WireFormatLite::TYPE_FIXED64>(
+    const uint8_t* buffer, uint64_t* value) {
+  return io::CodedInputStream::ReadLittleEndian64FromArray(buffer, value);
+}
+template <>
+inline const uint8_t*
+WireFormatLite::ReadPrimitiveFromArray<int32_t, WireFormatLite::TYPE_SFIXED32>(
+    const uint8_t* buffer, int32_t* value) {
+  uint32_t temp;
+  buffer = io::CodedInputStream::ReadLittleEndian32FromArray(buffer, &temp);
+  *value = static_cast<int32_t>(temp);
+  return buffer;
+}
+template <>
+inline const uint8_t*
+WireFormatLite::ReadPrimitiveFromArray<int64_t, WireFormatLite::TYPE_SFIXED64>(
+    const uint8_t* buffer, int64_t* value) {
+  uint64_t temp;
+  buffer = io::CodedInputStream::ReadLittleEndian64FromArray(buffer, &temp);
+  *value = static_cast<int64_t>(temp);
+  return buffer;
+}
+template <>
+inline const uint8_t*
+WireFormatLite::ReadPrimitiveFromArray<float, WireFormatLite::TYPE_FLOAT>(
+    const uint8_t* buffer, float* value) {
+  uint32_t temp;
+  buffer = io::CodedInputStream::ReadLittleEndian32FromArray(buffer, &temp);
+  *value = DecodeFloat(temp);
+  return buffer;
+}
+template <>
+inline const uint8_t*
+WireFormatLite::ReadPrimitiveFromArray<double, WireFormatLite::TYPE_DOUBLE>(
+    const uint8_t* buffer, double* value) {
+  uint64_t temp;
+  buffer = io::CodedInputStream::ReadLittleEndian64FromArray(buffer, &temp);
+  *value = DecodeDouble(temp);
+  return buffer;
+}
+
+template <typename CType, enum WireFormatLite::FieldType DeclaredType>
+inline bool WireFormatLite::ReadRepeatedPrimitive(
+    int,  // tag_size, unused.
+    uint32_t tag, io::CodedInputStream* input, RepeatedField<CType>* values) {
+  CType value;
+  if (!ReadPrimitive<CType, DeclaredType>(input, &value)) return false;
+  values->Add(value);
+  int elements_already_reserved = values->Capacity() - values->size();
+  while (elements_already_reserved > 0 && input->ExpectTag(tag)) {
+    if (!ReadPrimitive<CType, DeclaredType>(input, &value)) return false;
+    values->AddAlreadyReserved(value);
+    elements_already_reserved--;
+  }
+  return true;
+}
+
+template <typename CType, enum WireFormatLite::FieldType DeclaredType>
+inline bool WireFormatLite::ReadRepeatedFixedSizePrimitive(
+    int tag_size, uint32_t tag, io::CodedInputStream* input,
+    RepeatedField<CType>* values) {
+  GOOGLE_DCHECK_EQ(UInt32Size(tag), static_cast<size_t>(tag_size));
+  CType value;
+  if (!ReadPrimitive<CType, DeclaredType>(input, &value)) return false;
+  values->Add(value);
+
+  // For fixed size values, repeated values can be read more quickly by
+  // reading directly from a raw array.
+  //
+  // We can get a tight loop by only reading as many elements as can be
+  // added to the RepeatedField without having to do any resizing. Additionally,
+  // we only try to read as many elements as are available from the current
+  // buffer space. Doing so avoids having to perform boundary checks when
+  // reading the value: the maximum number of elements that can be read is
+  // known outside of the loop.
+  const void* void_pointer;
+  int size;
+  input->GetDirectBufferPointerInline(&void_pointer, &size);
+  if (size > 0) {
+    const uint8_t* buffer = reinterpret_cast<const uint8_t*>(void_pointer);
+    // The number of bytes each type occupies on the wire.
+    const int per_value_size = tag_size + static_cast<int>(sizeof(value));
+
+    // parentheses around (std::min) prevents macro expansion of min(...)
+    int elements_available =
+        (std::min)(values->Capacity() - values->size(), size / per_value_size);
+    int num_read = 0;
+    while (num_read < elements_available &&
+           (buffer = io::CodedInputStream::ExpectTagFromArray(buffer, tag)) !=
+               nullptr) {
+      buffer = ReadPrimitiveFromArray<CType, DeclaredType>(buffer, &value);
+      values->AddAlreadyReserved(value);
+      ++num_read;
+    }
+    const int read_bytes = num_read * per_value_size;
+    if (read_bytes > 0) {
+      input->Skip(read_bytes);
+    }
+  }
+  return true;
+}
+
+// Specializations of ReadRepeatedPrimitive for the fixed size types, which use
+// the optimized code path.
+#define READ_REPEATED_FIXED_SIZE_PRIMITIVE(CPPTYPE, DECLARED_TYPE)        \
+  template <>                                                             \
+  inline bool WireFormatLite::ReadRepeatedPrimitive<                      \
+      CPPTYPE, WireFormatLite::DECLARED_TYPE>(                            \
+      int tag_size, uint32_t tag, io::CodedInputStream* input,            \
+      RepeatedField<CPPTYPE>* values) {                                   \
+    return ReadRepeatedFixedSizePrimitive<CPPTYPE,                        \
+                                          WireFormatLite::DECLARED_TYPE>( \
+        tag_size, tag, input, values);                                    \
+  }
+
+READ_REPEATED_FIXED_SIZE_PRIMITIVE(uint32_t, TYPE_FIXED32)
+READ_REPEATED_FIXED_SIZE_PRIMITIVE(uint64_t, TYPE_FIXED64)
+READ_REPEATED_FIXED_SIZE_PRIMITIVE(int32_t, TYPE_SFIXED32)
+READ_REPEATED_FIXED_SIZE_PRIMITIVE(int64_t, TYPE_SFIXED64)
+READ_REPEATED_FIXED_SIZE_PRIMITIVE(float, TYPE_FLOAT)
+READ_REPEATED_FIXED_SIZE_PRIMITIVE(double, TYPE_DOUBLE)
+
+#undef READ_REPEATED_FIXED_SIZE_PRIMITIVE
+
+template <typename CType, enum WireFormatLite::FieldType DeclaredType>
+bool WireFormatLite::ReadRepeatedPrimitiveNoInline(
+    int tag_size, uint32_t tag, io::CodedInputStream* input,
+    RepeatedField<CType>* value) {
+  return ReadRepeatedPrimitive<CType, DeclaredType>(tag_size, tag, input,
+                                                    value);
+}
+
+template <typename CType, enum WireFormatLite::FieldType DeclaredType>
+inline bool WireFormatLite::ReadPackedPrimitive(io::CodedInputStream* input,
+                                                RepeatedField<CType>* values) {
+  int length;
+  if (!input->ReadVarintSizeAsInt(&length)) return false;
+  io::CodedInputStream::Limit limit = input->PushLimit(length);
+  while (input->BytesUntilLimit() > 0) {
+    CType value;
+    if (!ReadPrimitive<CType, DeclaredType>(input, &value)) return false;
+    values->Add(value);
+  }
+  input->PopLimit(limit);
+  return true;
+}
+
+template <typename CType, enum WireFormatLite::FieldType DeclaredType>
+inline bool WireFormatLite::ReadPackedFixedSizePrimitive(
+    io::CodedInputStream* input, RepeatedField<CType>* values) {
+  int length;
+  if (!input->ReadVarintSizeAsInt(&length)) return false;
+  const int old_entries = values->size();
+  const int new_entries = length / static_cast<int>(sizeof(CType));
+  const int new_bytes = new_entries * static_cast<int>(sizeof(CType));
+  if (new_bytes != length) return false;
+  // We would *like* to pre-allocate the buffer to write into (for
+  // speed), but *must* avoid performing a very large allocation due
+  // to a malicious user-supplied "length" above.  So we have a fast
+  // path that pre-allocates when the "length" is less than a bound.
+  // We determine the bound by calling BytesUntilTotalBytesLimit() and
+  // BytesUntilLimit().  These return -1 to mean "no limit set".
+  // There are four cases:
+  // TotalBytesLimit  Limit
+  // -1               -1     Use slow path.
+  // -1               >= 0   Use fast path if length <= Limit.
+  // >= 0             -1     Use slow path.
+  // >= 0             >= 0   Use fast path if length <= min(both limits).
+  int64_t bytes_limit = input->BytesUntilTotalBytesLimit();
+  if (bytes_limit == -1) {
+    bytes_limit = input->BytesUntilLimit();
+  } else {
+    // parentheses around (std::min) prevents macro expansion of min(...)
+    bytes_limit =
+        (std::min)(bytes_limit, static_cast<int64_t>(input->BytesUntilLimit()));
+  }
+  if (bytes_limit >= new_bytes) {
+    // Fast-path that pre-allocates *values to the final size.
+#if defined(PROTOBUF_LITTLE_ENDIAN)
+    values->Resize(old_entries + new_entries, 0);
+    // values->mutable_data() may change after Resize(), so do this after:
+    void* dest = reinterpret_cast<void*>(values->mutable_data() + old_entries);
+    if (!input->ReadRaw(dest, new_bytes)) {
+      values->Truncate(old_entries);
+      return false;
+    }
+#else
+    values->Reserve(old_entries + new_entries);
+    CType value;
+    for (int i = 0; i < new_entries; ++i) {
+      if (!ReadPrimitive<CType, DeclaredType>(input, &value)) return false;
+      values->AddAlreadyReserved(value);
+    }
+#endif
+  } else {
+    // This is the slow-path case where "length" may be too large to
+    // safely allocate.  We read as much as we can into *values
+    // without pre-allocating "length" bytes.
+    CType value;
+    for (int i = 0; i < new_entries; ++i) {
+      if (!ReadPrimitive<CType, DeclaredType>(input, &value)) return false;
+      values->Add(value);
+    }
+  }
+  return true;
+}
+
+// Specializations of ReadPackedPrimitive for the fixed size types, which use
+// an optimized code path.
+#define READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(CPPTYPE, DECLARED_TYPE)      \
+  template <>                                                                  \
+  inline bool                                                                  \
+  WireFormatLite::ReadPackedPrimitive<CPPTYPE, WireFormatLite::DECLARED_TYPE>( \
+      io::CodedInputStream * input, RepeatedField<CPPTYPE> * values) {         \
+    return ReadPackedFixedSizePrimitive<CPPTYPE,                               \
+                                        WireFormatLite::DECLARED_TYPE>(        \
+        input, values);                                                        \
+  }
+
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(uint32_t, TYPE_FIXED32)
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(uint64_t, TYPE_FIXED64)
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(int32_t, TYPE_SFIXED32)
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(int64_t, TYPE_SFIXED64)
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(float, TYPE_FLOAT)
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(double, TYPE_DOUBLE)
+
+#undef READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE
+
+template <typename CType, enum WireFormatLite::FieldType DeclaredType>
+bool WireFormatLite::ReadPackedPrimitiveNoInline(io::CodedInputStream* input,
+                                                 RepeatedField<CType>* values) {
+  return ReadPackedPrimitive<CType, DeclaredType>(input, values);
+}
+
+
+template <typename MessageType>
+inline bool WireFormatLite::ReadGroup(int field_number,
+                                      io::CodedInputStream* input,
+                                      MessageType* value) {
+  if (!input->IncrementRecursionDepth()) return false;
+  if (!value->MergePartialFromCodedStream(input)) return false;
+  input->UnsafeDecrementRecursionDepth();
+  // Make sure the last thing read was an end tag for this group.
+  if (!input->LastTagWas(MakeTag(field_number, WIRETYPE_END_GROUP))) {
+    return false;
+  }
+  return true;
+}
+template <typename MessageType>
+inline bool WireFormatLite::ReadMessage(io::CodedInputStream* input,
+                                        MessageType* value) {
+  int length;
+  if (!input->ReadVarintSizeAsInt(&length)) return false;
+  std::pair<io::CodedInputStream::Limit, int> p =
+      input->IncrementRecursionDepthAndPushLimit(length);
+  if (p.second < 0 || !value->MergePartialFromCodedStream(input)) return false;
+  // Make sure that parsing stopped when the limit was hit, not at an endgroup
+  // tag.
+  return input->DecrementRecursionDepthAndPopLimit(p.first);
+}
+
+// ===================================================================
+
+inline void WireFormatLite::WriteTag(int field_number, WireType type,
+                                     io::CodedOutputStream* output) {
+  output->WriteTag(MakeTag(field_number, type));
+}
+
+inline void WireFormatLite::WriteInt32NoTag(int32_t value,
+                                            io::CodedOutputStream* output) {
+  output->WriteVarint32SignExtended(value);
+}
+inline void WireFormatLite::WriteInt64NoTag(int64_t value,
+                                            io::CodedOutputStream* output) {
+  output->WriteVarint64(static_cast<uint64_t>(value));
+}
+inline void WireFormatLite::WriteUInt32NoTag(uint32_t value,
+                                             io::CodedOutputStream* output) {
+  output->WriteVarint32(value);
+}
+inline void WireFormatLite::WriteUInt64NoTag(uint64_t value,
+                                             io::CodedOutputStream* output) {
+  output->WriteVarint64(value);
+}
+inline void WireFormatLite::WriteSInt32NoTag(int32_t value,
+                                             io::CodedOutputStream* output) {
+  output->WriteVarint32(ZigZagEncode32(value));
+}
+inline void WireFormatLite::WriteSInt64NoTag(int64_t value,
+                                             io::CodedOutputStream* output) {
+  output->WriteVarint64(ZigZagEncode64(value));
+}
+inline void WireFormatLite::WriteFixed32NoTag(uint32_t value,
+                                              io::CodedOutputStream* output) {
+  output->WriteLittleEndian32(value);
+}
+inline void WireFormatLite::WriteFixed64NoTag(uint64_t value,
+                                              io::CodedOutputStream* output) {
+  output->WriteLittleEndian64(value);
+}
+inline void WireFormatLite::WriteSFixed32NoTag(int32_t value,
+                                               io::CodedOutputStream* output) {
+  output->WriteLittleEndian32(static_cast<uint32_t>(value));
+}
+inline void WireFormatLite::WriteSFixed64NoTag(int64_t value,
+                                               io::CodedOutputStream* output) {
+  output->WriteLittleEndian64(static_cast<uint64_t>(value));
+}
+inline void WireFormatLite::WriteFloatNoTag(float value,
+                                            io::CodedOutputStream* output) {
+  output->WriteLittleEndian32(EncodeFloat(value));
+}
+inline void WireFormatLite::WriteDoubleNoTag(double value,
+                                             io::CodedOutputStream* output) {
+  output->WriteLittleEndian64(EncodeDouble(value));
+}
+inline void WireFormatLite::WriteBoolNoTag(bool value,
+                                           io::CodedOutputStream* output) {
+  output->WriteVarint32(value ? 1 : 0);
+}
+inline void WireFormatLite::WriteEnumNoTag(int value,
+                                           io::CodedOutputStream* output) {
+  output->WriteVarint32SignExtended(value);
+}
+
+// See comment on ReadGroupNoVirtual to understand the need for this template
+// parameter name.
+template <typename MessageType_WorkAroundCppLookupDefect>
+inline void WireFormatLite::WriteGroupNoVirtual(
+    int field_number, const MessageType_WorkAroundCppLookupDefect& value,
+    io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_START_GROUP, output);
+  value.MessageType_WorkAroundCppLookupDefect::SerializeWithCachedSizes(output);
+  WriteTag(field_number, WIRETYPE_END_GROUP, output);
+}
+template <typename MessageType_WorkAroundCppLookupDefect>
+inline void WireFormatLite::WriteMessageNoVirtual(
+    int field_number, const MessageType_WorkAroundCppLookupDefect& value,
+    io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output);
+  output->WriteVarint32(
+      value.MessageType_WorkAroundCppLookupDefect::GetCachedSize());
+  value.MessageType_WorkAroundCppLookupDefect::SerializeWithCachedSizes(output);
+}
+
+// ===================================================================
+
+inline uint8_t* WireFormatLite::WriteTagToArray(int field_number, WireType type,
+                                                uint8_t* target) {
+  return io::CodedOutputStream::WriteTagToArray(MakeTag(field_number, type),
+                                                target);
+}
+
+inline uint8_t* WireFormatLite::WriteInt32NoTagToArray(int32_t value,
+                                                       uint8_t* target) {
+  return io::CodedOutputStream::WriteVarint32SignExtendedToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteInt64NoTagToArray(int64_t value,
+                                                       uint8_t* target) {
+  return io::CodedOutputStream::WriteVarint64ToArray(
+      static_cast<uint64_t>(value), target);
+}
+inline uint8_t* WireFormatLite::WriteUInt32NoTagToArray(uint32_t value,
+                                                        uint8_t* target) {
+  return io::CodedOutputStream::WriteVarint32ToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteUInt64NoTagToArray(uint64_t value,
+                                                        uint8_t* target) {
+  return io::CodedOutputStream::WriteVarint64ToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteSInt32NoTagToArray(int32_t value,
+                                                        uint8_t* target) {
+  return io::CodedOutputStream::WriteVarint32ToArray(ZigZagEncode32(value),
+                                                     target);
+}
+inline uint8_t* WireFormatLite::WriteSInt64NoTagToArray(int64_t value,
+                                                        uint8_t* target) {
+  return io::CodedOutputStream::WriteVarint64ToArray(ZigZagEncode64(value),
+                                                     target);
+}
+inline uint8_t* WireFormatLite::WriteFixed32NoTagToArray(uint32_t value,
+                                                         uint8_t* target) {
+  return io::CodedOutputStream::WriteLittleEndian32ToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteFixed64NoTagToArray(uint64_t value,
+                                                         uint8_t* target) {
+  return io::CodedOutputStream::WriteLittleEndian64ToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteSFixed32NoTagToArray(int32_t value,
+                                                          uint8_t* target) {
+  return io::CodedOutputStream::WriteLittleEndian32ToArray(
+      static_cast<uint32_t>(value), target);
+}
+inline uint8_t* WireFormatLite::WriteSFixed64NoTagToArray(int64_t value,
+                                                          uint8_t* target) {
+  return io::CodedOutputStream::WriteLittleEndian64ToArray(
+      static_cast<uint64_t>(value), target);
+}
+inline uint8_t* WireFormatLite::WriteFloatNoTagToArray(float value,
+                                                       uint8_t* target) {
+  return io::CodedOutputStream::WriteLittleEndian32ToArray(EncodeFloat(value),
+                                                           target);
+}
+inline uint8_t* WireFormatLite::WriteDoubleNoTagToArray(double value,
+                                                        uint8_t* target) {
+  return io::CodedOutputStream::WriteLittleEndian64ToArray(EncodeDouble(value),
+                                                           target);
+}
+inline uint8_t* WireFormatLite::WriteBoolNoTagToArray(bool value,
+                                                      uint8_t* target) {
+  return io::CodedOutputStream::WriteVarint32ToArray(value ? 1 : 0, target);
+}
+inline uint8_t* WireFormatLite::WriteEnumNoTagToArray(int value,
+                                                      uint8_t* target) {
+  return io::CodedOutputStream::WriteVarint32SignExtendedToArray(value, target);
+}
+
+template <typename T>
+inline uint8_t* WireFormatLite::WritePrimitiveNoTagToArray(
+    const RepeatedField<T>& value, uint8_t* (*Writer)(T, uint8_t*),
+    uint8_t* target) {
+  const int n = value.size();
+  GOOGLE_DCHECK_GT(n, 0);
+
+  const T* ii = value.data();
+  int i = 0;
+  do {
+    target = Writer(ii[i], target);
+  } while (++i < n);
+
+  return target;
+}
+
+template <typename T>
+inline uint8_t* WireFormatLite::WriteFixedNoTagToArray(
+    const RepeatedField<T>& value, uint8_t* (*Writer)(T, uint8_t*),
+    uint8_t* target) {
+#if defined(PROTOBUF_LITTLE_ENDIAN)
+  (void)Writer;
+
+  const int n = value.size();
+  GOOGLE_DCHECK_GT(n, 0);
+
+  const T* ii = value.data();
+  const int bytes = n * static_cast<int>(sizeof(ii[0]));
+  memcpy(target, ii, static_cast<size_t>(bytes));
+  return target + bytes;
+#else
+  return WritePrimitiveNoTagToArray(value, Writer, target);
+#endif
+}
+
+inline uint8_t* WireFormatLite::WriteInt32NoTagToArray(
+    const RepeatedField<int32_t>& value, uint8_t* target) {
+  return WritePrimitiveNoTagToArray(value, WriteInt32NoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteInt64NoTagToArray(
+    const RepeatedField<int64_t>& value, uint8_t* target) {
+  return WritePrimitiveNoTagToArray(value, WriteInt64NoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteUInt32NoTagToArray(
+    const RepeatedField<uint32_t>& value, uint8_t* target) {
+  return WritePrimitiveNoTagToArray(value, WriteUInt32NoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteUInt64NoTagToArray(
+    const RepeatedField<uint64_t>& value, uint8_t* target) {
+  return WritePrimitiveNoTagToArray(value, WriteUInt64NoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteSInt32NoTagToArray(
+    const RepeatedField<int32_t>& value, uint8_t* target) {
+  return WritePrimitiveNoTagToArray(value, WriteSInt32NoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteSInt64NoTagToArray(
+    const RepeatedField<int64_t>& value, uint8_t* target) {
+  return WritePrimitiveNoTagToArray(value, WriteSInt64NoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteFixed32NoTagToArray(
+    const RepeatedField<uint32_t>& value, uint8_t* target) {
+  return WriteFixedNoTagToArray(value, WriteFixed32NoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteFixed64NoTagToArray(
+    const RepeatedField<uint64_t>& value, uint8_t* target) {
+  return WriteFixedNoTagToArray(value, WriteFixed64NoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteSFixed32NoTagToArray(
+    const RepeatedField<int32_t>& value, uint8_t* target) {
+  return WriteFixedNoTagToArray(value, WriteSFixed32NoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteSFixed64NoTagToArray(
+    const RepeatedField<int64_t>& value, uint8_t* target) {
+  return WriteFixedNoTagToArray(value, WriteSFixed64NoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteFloatNoTagToArray(
+    const RepeatedField<float>& value, uint8_t* target) {
+  return WriteFixedNoTagToArray(value, WriteFloatNoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteDoubleNoTagToArray(
+    const RepeatedField<double>& value, uint8_t* target) {
+  return WriteFixedNoTagToArray(value, WriteDoubleNoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteBoolNoTagToArray(
+    const RepeatedField<bool>& value, uint8_t* target) {
+  return WritePrimitiveNoTagToArray(value, WriteBoolNoTagToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteEnumNoTagToArray(
+    const RepeatedField<int>& value, uint8_t* target) {
+  return WritePrimitiveNoTagToArray(value, WriteEnumNoTagToArray, target);
+}
+
+inline uint8_t* WireFormatLite::WriteInt32ToArray(int field_number,
+                                                  int32_t value,
+                                                  uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_VARINT, target);
+  return WriteInt32NoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteInt64ToArray(int field_number,
+                                                  int64_t value,
+                                                  uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_VARINT, target);
+  return WriteInt64NoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteUInt32ToArray(int field_number,
+                                                   uint32_t value,
+                                                   uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_VARINT, target);
+  return WriteUInt32NoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteUInt64ToArray(int field_number,
+                                                   uint64_t value,
+                                                   uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_VARINT, target);
+  return WriteUInt64NoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteSInt32ToArray(int field_number,
+                                                   int32_t value,
+                                                   uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_VARINT, target);
+  return WriteSInt32NoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteSInt64ToArray(int field_number,
+                                                   int64_t value,
+                                                   uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_VARINT, target);
+  return WriteSInt64NoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteFixed32ToArray(int field_number,
+                                                    uint32_t value,
+                                                    uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_FIXED32, target);
+  return WriteFixed32NoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteFixed64ToArray(int field_number,
+                                                    uint64_t value,
+                                                    uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_FIXED64, target);
+  return WriteFixed64NoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteSFixed32ToArray(int field_number,
+                                                     int32_t value,
+                                                     uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_FIXED32, target);
+  return WriteSFixed32NoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteSFixed64ToArray(int field_number,
+                                                     int64_t value,
+                                                     uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_FIXED64, target);
+  return WriteSFixed64NoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteFloatToArray(int field_number, float value,
+                                                  uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_FIXED32, target);
+  return WriteFloatNoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteDoubleToArray(int field_number,
+                                                   double value,
+                                                   uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_FIXED64, target);
+  return WriteDoubleNoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteBoolToArray(int field_number, bool value,
+                                                 uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_VARINT, target);
+  return WriteBoolNoTagToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteEnumToArray(int field_number, int value,
+                                                 uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_VARINT, target);
+  return WriteEnumNoTagToArray(value, target);
+}
+
+template <typename T>
+inline uint8_t* WireFormatLite::WritePrimitiveToArray(
+    int field_number, const RepeatedField<T>& value,
+    uint8_t* (*Writer)(int, T, uint8_t*), uint8_t* target) {
+  const int n = value.size();
+  if (n == 0) {
+    return target;
+  }
+
+  const T* ii = value.data();
+  int i = 0;
+  do {
+    target = Writer(field_number, ii[i], target);
+  } while (++i < n);
+
+  return target;
+}
+
+inline uint8_t* WireFormatLite::WriteInt32ToArray(
+    int field_number, const RepeatedField<int32_t>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteInt32ToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteInt64ToArray(
+    int field_number, const RepeatedField<int64_t>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteInt64ToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteUInt32ToArray(
+    int field_number, const RepeatedField<uint32_t>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteUInt32ToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteUInt64ToArray(
+    int field_number, const RepeatedField<uint64_t>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteUInt64ToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteSInt32ToArray(
+    int field_number, const RepeatedField<int32_t>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteSInt32ToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteSInt64ToArray(
+    int field_number, const RepeatedField<int64_t>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteSInt64ToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteFixed32ToArray(
+    int field_number, const RepeatedField<uint32_t>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteFixed32ToArray,
+                               target);
+}
+inline uint8_t* WireFormatLite::WriteFixed64ToArray(
+    int field_number, const RepeatedField<uint64_t>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteFixed64ToArray,
+                               target);
+}
+inline uint8_t* WireFormatLite::WriteSFixed32ToArray(
+    int field_number, const RepeatedField<int32_t>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteSFixed32ToArray,
+                               target);
+}
+inline uint8_t* WireFormatLite::WriteSFixed64ToArray(
+    int field_number, const RepeatedField<int64_t>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteSFixed64ToArray,
+                               target);
+}
+inline uint8_t* WireFormatLite::WriteFloatToArray(
+    int field_number, const RepeatedField<float>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteFloatToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteDoubleToArray(
+    int field_number, const RepeatedField<double>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteDoubleToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteBoolToArray(
+    int field_number, const RepeatedField<bool>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteBoolToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteEnumToArray(
+    int field_number, const RepeatedField<int>& value, uint8_t* target) {
+  return WritePrimitiveToArray(field_number, value, WriteEnumToArray, target);
+}
+inline uint8_t* WireFormatLite::WriteStringToArray(int field_number,
+                                                   const std::string& value,
+                                                   uint8_t* target) {
+  // String is for UTF-8 text only
+  // WARNING:  In wire_format.cc, both strings and bytes are handled by
+  //   WriteString() to avoid code duplication.  If the implementations become
+  //   different, you will need to update that usage.
+  target = WriteTagToArray(field_number, WIRETYPE_LENGTH_DELIMITED, target);
+  return io::CodedOutputStream::WriteStringWithSizeToArray(value, target);
+}
+inline uint8_t* WireFormatLite::WriteBytesToArray(int field_number,
+                                                  const std::string& value,
+                                                  uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_LENGTH_DELIMITED, target);
+  return io::CodedOutputStream::WriteStringWithSizeToArray(value, target);
+}
+
+
+// See comment on ReadGroupNoVirtual to understand the need for this template
+// parameter name.
+template <typename MessageType_WorkAroundCppLookupDefect>
+inline uint8_t* WireFormatLite::InternalWriteGroupNoVirtualToArray(
+    int field_number, const MessageType_WorkAroundCppLookupDefect& value,
+    uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_START_GROUP, target);
+  target = value.MessageType_WorkAroundCppLookupDefect::
+               SerializeWithCachedSizesToArray(target);
+  return WriteTagToArray(field_number, WIRETYPE_END_GROUP, target);
+}
+template <typename MessageType_WorkAroundCppLookupDefect>
+inline uint8_t* WireFormatLite::InternalWriteMessageNoVirtualToArray(
+    int field_number, const MessageType_WorkAroundCppLookupDefect& value,
+    uint8_t* target) {
+  target = WriteTagToArray(field_number, WIRETYPE_LENGTH_DELIMITED, target);
+  target = io::CodedOutputStream::WriteVarint32ToArray(
+      static_cast<uint32_t>(
+          value.MessageType_WorkAroundCppLookupDefect::GetCachedSize()),
+      target);
+  return value
+      .MessageType_WorkAroundCppLookupDefect::SerializeWithCachedSizesToArray(
+          target);
+}
+
+// ===================================================================
+
+inline size_t WireFormatLite::Int32Size(int32_t value) {
+  return io::CodedOutputStream::VarintSize32SignExtended(value);
+}
+inline size_t WireFormatLite::Int64Size(int64_t value) {
+  return io::CodedOutputStream::VarintSize64(static_cast<uint64_t>(value));
+}
+inline size_t WireFormatLite::UInt32Size(uint32_t value) {
+  return io::CodedOutputStream::VarintSize32(value);
+}
+inline size_t WireFormatLite::UInt64Size(uint64_t value) {
+  return io::CodedOutputStream::VarintSize64(value);
+}
+inline size_t WireFormatLite::SInt32Size(int32_t value) {
+  return io::CodedOutputStream::VarintSize32(ZigZagEncode32(value));
+}
+inline size_t WireFormatLite::SInt64Size(int64_t value) {
+  return io::CodedOutputStream::VarintSize64(ZigZagEncode64(value));
+}
+inline size_t WireFormatLite::EnumSize(int value) {
+  return io::CodedOutputStream::VarintSize32SignExtended(value);
+}
+inline size_t WireFormatLite::Int32SizePlusOne(int32_t value) {
+  return io::CodedOutputStream::VarintSize32SignExtendedPlusOne(value);
+}
+inline size_t WireFormatLite::Int64SizePlusOne(int64_t value) {
+  return io::CodedOutputStream::VarintSize64PlusOne(
+      static_cast<uint64_t>(value));
+}
+inline size_t WireFormatLite::UInt32SizePlusOne(uint32_t value) {
+  return io::CodedOutputStream::VarintSize32PlusOne(value);
+}
+inline size_t WireFormatLite::UInt64SizePlusOne(uint64_t value) {
+  return io::CodedOutputStream::VarintSize64PlusOne(value);
+}
+inline size_t WireFormatLite::SInt32SizePlusOne(int32_t value) {
+  return io::CodedOutputStream::VarintSize32PlusOne(ZigZagEncode32(value));
+}
+inline size_t WireFormatLite::SInt64SizePlusOne(int64_t value) {
+  return io::CodedOutputStream::VarintSize64PlusOne(ZigZagEncode64(value));
+}
+inline size_t WireFormatLite::EnumSizePlusOne(int value) {
+  return io::CodedOutputStream::VarintSize32SignExtendedPlusOne(value);
+}
+
+inline size_t WireFormatLite::StringSize(const std::string& value) {
+  return LengthDelimitedSize(value.size());
+}
+inline size_t WireFormatLite::BytesSize(const std::string& value) {
+  return LengthDelimitedSize(value.size());
+}
+
+
+template <typename MessageType>
+inline size_t WireFormatLite::GroupSize(const MessageType& value) {
+  return value.ByteSizeLong();
+}
+template <typename MessageType>
+inline size_t WireFormatLite::MessageSize(const MessageType& value) {
+  return LengthDelimitedSize(value.ByteSizeLong());
+}
+
+// See comment on ReadGroupNoVirtual to understand the need for this template
+// parameter name.
+template <typename MessageType_WorkAroundCppLookupDefect>
+inline size_t WireFormatLite::GroupSizeNoVirtual(
+    const MessageType_WorkAroundCppLookupDefect& value) {
+  return value.MessageType_WorkAroundCppLookupDefect::ByteSizeLong();
+}
+template <typename MessageType_WorkAroundCppLookupDefect>
+inline size_t WireFormatLite::MessageSizeNoVirtual(
+    const MessageType_WorkAroundCppLookupDefect& value) {
+  return LengthDelimitedSize(
+      value.MessageType_WorkAroundCppLookupDefect::ByteSizeLong());
+}
+
+inline size_t WireFormatLite::LengthDelimitedSize(size_t length) {
+  // The static_cast here prevents an error in certain compiler configurations
+  // but is not technically correct--if length is too large to fit in a uint32_t
+  // then it will be silently truncated. We will need to fix this if we ever
+  // decide to start supporting serialized messages greater than 2 GiB in size.
+  return length +
+         io::CodedOutputStream::VarintSize32(static_cast<uint32_t>(length));
+}
+
+template <typename MS>
+bool ParseMessageSetItemImpl(io::CodedInputStream* input, MS ms) {
+  // This method parses a group which should contain two fields:
+  //   required int32 type_id = 2;
+  //   required data message = 3;
+
+  uint32_t last_type_id = 0;
+
+  // If we see message data before the type_id, we'll append it to this so
+  // we can parse it later.
+  std::string message_data;
+
+  enum class State { kNoTag, kHasType, kHasPayload, kDone };
+  State state = State::kNoTag;
+
+  while (true) {
+    const uint32_t tag = input->ReadTagNoLastTag();
+    if (tag == 0) return false;
+
+    switch (tag) {
+      case WireFormatLite::kMessageSetTypeIdTag: {
+        uint32_t type_id;
+        if (!input->ReadVarint32(&type_id)) return false;
+        if (state == State::kNoTag) {
+          last_type_id = type_id;
+          state = State::kHasType;
+        } else if (state == State::kHasPayload) {
+          // We saw some message data before the type_id.  Have to parse it
+          // now.
+          io::CodedInputStream sub_input(
+              reinterpret_cast<const uint8_t*>(message_data.data()),
+              static_cast<int>(message_data.size()));
+          sub_input.SetRecursionLimit(input->RecursionBudget());
+          if (!ms.ParseField(type_id, &sub_input)) {
+            return false;
+          }
+          message_data.clear();
+          state = State::kDone;
+        }
+
+        break;
+      }
+
+      case WireFormatLite::kMessageSetMessageTag: {
+        if (state == State::kHasType) {
+          // Already saw type_id, so we can parse this directly.
+          if (!ms.ParseField(last_type_id, input)) {
+            return false;
+          }
+          state = State::kDone;
+        } else if (state == State::kNoTag) {
+          // We haven't seen a type_id yet.  Append this data to message_data.
+          uint32_t length;
+          if (!input->ReadVarint32(&length)) return false;
+          if (static_cast<int32_t>(length) < 0) return false;
+          uint32_t size = static_cast<uint32_t>(
+              length + io::CodedOutputStream::VarintSize32(length));
+          message_data.resize(size);
+          auto ptr = reinterpret_cast<uint8_t*>(&message_data[0]);
+          ptr = io::CodedOutputStream::WriteVarint32ToArray(length, ptr);
+          if (!input->ReadRaw(ptr, length)) return false;
+          state = State::kHasPayload;
+        } else {
+          if (!ms.SkipField(tag, input)) return false;
+        }
+
+        break;
+      }
+
+      case WireFormatLite::kMessageSetItemEndTag: {
+        return true;
+      }
+
+      default: {
+        if (!ms.SkipField(tag, input)) return false;
+      }
+    }
+  }
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
+
+#endif  // GOOGLE_PROTOBUF_WIRE_FORMAT_LITE_H__
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/wrappers.pb.h b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/wrappers.pb.h
new file mode 100644
index 0000000..0f4b835
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/wrappers.pb.h
@@ -0,0 +1,1741 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/wrappers.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fwrappers_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fwrappers_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/unknown_field_set.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fwrappers_2eproto PROTOBUF_EXPORT
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2fwrappers_2eproto {
+  static const uint32_t offsets[];
+};
+PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2fwrappers_2eproto;
+PROTOBUF_NAMESPACE_OPEN
+class BoolValue;
+struct BoolValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern BoolValueDefaultTypeInternal _BoolValue_default_instance_;
+class BytesValue;
+struct BytesValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern BytesValueDefaultTypeInternal _BytesValue_default_instance_;
+class DoubleValue;
+struct DoubleValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern DoubleValueDefaultTypeInternal _DoubleValue_default_instance_;
+class FloatValue;
+struct FloatValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern FloatValueDefaultTypeInternal _FloatValue_default_instance_;
+class Int32Value;
+struct Int32ValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern Int32ValueDefaultTypeInternal _Int32Value_default_instance_;
+class Int64Value;
+struct Int64ValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern Int64ValueDefaultTypeInternal _Int64Value_default_instance_;
+class StringValue;
+struct StringValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern StringValueDefaultTypeInternal _StringValue_default_instance_;
+class UInt32Value;
+struct UInt32ValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern UInt32ValueDefaultTypeInternal _UInt32Value_default_instance_;
+class UInt64Value;
+struct UInt64ValueDefaultTypeInternal;
+PROTOBUF_EXPORT extern UInt64ValueDefaultTypeInternal _UInt64Value_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::BoolValue* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::BoolValue>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::BytesValue* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::BytesValue>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::DoubleValue* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::DoubleValue>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::FloatValue* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::FloatValue>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Int32Value* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Int32Value>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Int64Value* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Int64Value>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::StringValue* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::StringValue>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::UInt32Value* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::UInt32Value>(Arena*);
+template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::UInt64Value* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::UInt64Value>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class PROTOBUF_EXPORT DoubleValue final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.DoubleValue) */ {
+ public:
+  inline DoubleValue() : DoubleValue(nullptr) {}
+  ~DoubleValue() override;
+  explicit PROTOBUF_CONSTEXPR DoubleValue(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  DoubleValue(const DoubleValue& from);
+  DoubleValue(DoubleValue&& from) noexcept
+    : DoubleValue() {
+    *this = ::std::move(from);
+  }
+
+  inline DoubleValue& operator=(const DoubleValue& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline DoubleValue& operator=(DoubleValue&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const DoubleValue& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const DoubleValue* internal_default_instance() {
+    return reinterpret_cast<const DoubleValue*>(
+               &_DoubleValue_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(DoubleValue& a, DoubleValue& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(DoubleValue* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(DoubleValue* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  DoubleValue* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<DoubleValue>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const DoubleValue& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const DoubleValue& from) {
+    DoubleValue::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(DoubleValue* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.DoubleValue";
+  }
+  protected:
+  explicit DoubleValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValueFieldNumber = 1,
+  };
+  // double value = 1;
+  void clear_value();
+  double value() const;
+  void set_value(double value);
+  private:
+  double _internal_value() const;
+  void _internal_set_value(double value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.DoubleValue)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    double value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fwrappers_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT FloatValue final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.FloatValue) */ {
+ public:
+  inline FloatValue() : FloatValue(nullptr) {}
+  ~FloatValue() override;
+  explicit PROTOBUF_CONSTEXPR FloatValue(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  FloatValue(const FloatValue& from);
+  FloatValue(FloatValue&& from) noexcept
+    : FloatValue() {
+    *this = ::std::move(from);
+  }
+
+  inline FloatValue& operator=(const FloatValue& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline FloatValue& operator=(FloatValue&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const FloatValue& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const FloatValue* internal_default_instance() {
+    return reinterpret_cast<const FloatValue*>(
+               &_FloatValue_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    1;
+
+  friend void swap(FloatValue& a, FloatValue& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(FloatValue* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(FloatValue* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  FloatValue* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<FloatValue>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const FloatValue& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const FloatValue& from) {
+    FloatValue::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(FloatValue* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.FloatValue";
+  }
+  protected:
+  explicit FloatValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValueFieldNumber = 1,
+  };
+  // float value = 1;
+  void clear_value();
+  float value() const;
+  void set_value(float value);
+  private:
+  float _internal_value() const;
+  void _internal_set_value(float value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.FloatValue)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    float value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fwrappers_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT Int64Value final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Int64Value) */ {
+ public:
+  inline Int64Value() : Int64Value(nullptr) {}
+  ~Int64Value() override;
+  explicit PROTOBUF_CONSTEXPR Int64Value(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Int64Value(const Int64Value& from);
+  Int64Value(Int64Value&& from) noexcept
+    : Int64Value() {
+    *this = ::std::move(from);
+  }
+
+  inline Int64Value& operator=(const Int64Value& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Int64Value& operator=(Int64Value&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Int64Value& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Int64Value* internal_default_instance() {
+    return reinterpret_cast<const Int64Value*>(
+               &_Int64Value_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    2;
+
+  friend void swap(Int64Value& a, Int64Value& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Int64Value* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Int64Value* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Int64Value* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Int64Value>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Int64Value& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Int64Value& from) {
+    Int64Value::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Int64Value* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Int64Value";
+  }
+  protected:
+  explicit Int64Value(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValueFieldNumber = 1,
+  };
+  // int64 value = 1;
+  void clear_value();
+  int64_t value() const;
+  void set_value(int64_t value);
+  private:
+  int64_t _internal_value() const;
+  void _internal_set_value(int64_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Int64Value)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    int64_t value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fwrappers_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT UInt64Value final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.UInt64Value) */ {
+ public:
+  inline UInt64Value() : UInt64Value(nullptr) {}
+  ~UInt64Value() override;
+  explicit PROTOBUF_CONSTEXPR UInt64Value(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  UInt64Value(const UInt64Value& from);
+  UInt64Value(UInt64Value&& from) noexcept
+    : UInt64Value() {
+    *this = ::std::move(from);
+  }
+
+  inline UInt64Value& operator=(const UInt64Value& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline UInt64Value& operator=(UInt64Value&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const UInt64Value& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const UInt64Value* internal_default_instance() {
+    return reinterpret_cast<const UInt64Value*>(
+               &_UInt64Value_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    3;
+
+  friend void swap(UInt64Value& a, UInt64Value& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(UInt64Value* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(UInt64Value* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  UInt64Value* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<UInt64Value>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const UInt64Value& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const UInt64Value& from) {
+    UInt64Value::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(UInt64Value* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.UInt64Value";
+  }
+  protected:
+  explicit UInt64Value(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValueFieldNumber = 1,
+  };
+  // uint64 value = 1;
+  void clear_value();
+  uint64_t value() const;
+  void set_value(uint64_t value);
+  private:
+  uint64_t _internal_value() const;
+  void _internal_set_value(uint64_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.UInt64Value)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    uint64_t value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fwrappers_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT Int32Value final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Int32Value) */ {
+ public:
+  inline Int32Value() : Int32Value(nullptr) {}
+  ~Int32Value() override;
+  explicit PROTOBUF_CONSTEXPR Int32Value(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  Int32Value(const Int32Value& from);
+  Int32Value(Int32Value&& from) noexcept
+    : Int32Value() {
+    *this = ::std::move(from);
+  }
+
+  inline Int32Value& operator=(const Int32Value& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Int32Value& operator=(Int32Value&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const Int32Value& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const Int32Value* internal_default_instance() {
+    return reinterpret_cast<const Int32Value*>(
+               &_Int32Value_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    4;
+
+  friend void swap(Int32Value& a, Int32Value& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Int32Value* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(Int32Value* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  Int32Value* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<Int32Value>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const Int32Value& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const Int32Value& from) {
+    Int32Value::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Int32Value* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.Int32Value";
+  }
+  protected:
+  explicit Int32Value(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValueFieldNumber = 1,
+  };
+  // int32 value = 1;
+  void clear_value();
+  int32_t value() const;
+  void set_value(int32_t value);
+  private:
+  int32_t _internal_value() const;
+  void _internal_set_value(int32_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.Int32Value)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    int32_t value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fwrappers_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT UInt32Value final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.UInt32Value) */ {
+ public:
+  inline UInt32Value() : UInt32Value(nullptr) {}
+  ~UInt32Value() override;
+  explicit PROTOBUF_CONSTEXPR UInt32Value(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  UInt32Value(const UInt32Value& from);
+  UInt32Value(UInt32Value&& from) noexcept
+    : UInt32Value() {
+    *this = ::std::move(from);
+  }
+
+  inline UInt32Value& operator=(const UInt32Value& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline UInt32Value& operator=(UInt32Value&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const UInt32Value& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const UInt32Value* internal_default_instance() {
+    return reinterpret_cast<const UInt32Value*>(
+               &_UInt32Value_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    5;
+
+  friend void swap(UInt32Value& a, UInt32Value& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(UInt32Value* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(UInt32Value* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  UInt32Value* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<UInt32Value>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const UInt32Value& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const UInt32Value& from) {
+    UInt32Value::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(UInt32Value* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.UInt32Value";
+  }
+  protected:
+  explicit UInt32Value(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValueFieldNumber = 1,
+  };
+  // uint32 value = 1;
+  void clear_value();
+  uint32_t value() const;
+  void set_value(uint32_t value);
+  private:
+  uint32_t _internal_value() const;
+  void _internal_set_value(uint32_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.UInt32Value)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    uint32_t value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fwrappers_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT BoolValue final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.BoolValue) */ {
+ public:
+  inline BoolValue() : BoolValue(nullptr) {}
+  ~BoolValue() override;
+  explicit PROTOBUF_CONSTEXPR BoolValue(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  BoolValue(const BoolValue& from);
+  BoolValue(BoolValue&& from) noexcept
+    : BoolValue() {
+    *this = ::std::move(from);
+  }
+
+  inline BoolValue& operator=(const BoolValue& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline BoolValue& operator=(BoolValue&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const BoolValue& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const BoolValue* internal_default_instance() {
+    return reinterpret_cast<const BoolValue*>(
+               &_BoolValue_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    6;
+
+  friend void swap(BoolValue& a, BoolValue& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(BoolValue* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(BoolValue* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  BoolValue* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<BoolValue>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const BoolValue& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const BoolValue& from) {
+    BoolValue::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(BoolValue* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.BoolValue";
+  }
+  protected:
+  explicit BoolValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValueFieldNumber = 1,
+  };
+  // bool value = 1;
+  void clear_value();
+  bool value() const;
+  void set_value(bool value);
+  private:
+  bool _internal_value() const;
+  void _internal_set_value(bool value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.BoolValue)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    bool value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fwrappers_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT StringValue final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.StringValue) */ {
+ public:
+  inline StringValue() : StringValue(nullptr) {}
+  ~StringValue() override;
+  explicit PROTOBUF_CONSTEXPR StringValue(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  StringValue(const StringValue& from);
+  StringValue(StringValue&& from) noexcept
+    : StringValue() {
+    *this = ::std::move(from);
+  }
+
+  inline StringValue& operator=(const StringValue& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline StringValue& operator=(StringValue&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const StringValue& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const StringValue* internal_default_instance() {
+    return reinterpret_cast<const StringValue*>(
+               &_StringValue_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    7;
+
+  friend void swap(StringValue& a, StringValue& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(StringValue* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(StringValue* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  StringValue* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<StringValue>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const StringValue& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const StringValue& from) {
+    StringValue::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(StringValue* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.StringValue";
+  }
+  protected:
+  explicit StringValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValueFieldNumber = 1,
+  };
+  // string value = 1;
+  void clear_value();
+  const std::string& value() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_value(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_value();
+  PROTOBUF_NODISCARD std::string* release_value();
+  void set_allocated_value(std::string* value);
+  private:
+  const std::string& _internal_value() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_value(const std::string& value);
+  std::string* _internal_mutable_value();
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.StringValue)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fwrappers_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PROTOBUF_EXPORT BytesValue final :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.BytesValue) */ {
+ public:
+  inline BytesValue() : BytesValue(nullptr) {}
+  ~BytesValue() override;
+  explicit PROTOBUF_CONSTEXPR BytesValue(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  BytesValue(const BytesValue& from);
+  BytesValue(BytesValue&& from) noexcept
+    : BytesValue() {
+    *this = ::std::move(from);
+  }
+
+  inline BytesValue& operator=(const BytesValue& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline BytesValue& operator=(BytesValue&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const BytesValue& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const BytesValue* internal_default_instance() {
+    return reinterpret_cast<const BytesValue*>(
+               &_BytesValue_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    8;
+
+  friend void swap(BytesValue& a, BytesValue& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(BytesValue* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(BytesValue* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  BytesValue* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<BytesValue>(arena);
+  }
+  using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
+  void CopyFrom(const BytesValue& from);
+  using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
+  void MergeFrom( const BytesValue& from) {
+    BytesValue::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  uint8_t* _InternalSerialize(
+      uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(BytesValue* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "google.protobuf.BytesValue";
+  }
+  protected:
+  explicit BytesValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  static const ClassData _class_data_;
+  const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kValueFieldNumber = 1,
+  };
+  // bytes value = 1;
+  void clear_value();
+  const std::string& value() const;
+  template <typename ArgT0 = const std::string&, typename... ArgT>
+  void set_value(ArgT0&& arg0, ArgT... args);
+  std::string* mutable_value();
+  PROTOBUF_NODISCARD std::string* release_value();
+  void set_allocated_value(std::string* value);
+  private:
+  const std::string& _internal_value() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_value(const std::string& value);
+  std::string* _internal_mutable_value();
+  public:
+
+  // @@protoc_insertion_point(class_scope:google.protobuf.BytesValue)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr value_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_google_2fprotobuf_2fwrappers_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// DoubleValue
+
+// double value = 1;
+inline void DoubleValue::clear_value() {
+  _impl_.value_ = 0;
+}
+inline double DoubleValue::_internal_value() const {
+  return _impl_.value_;
+}
+inline double DoubleValue::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.DoubleValue.value)
+  return _internal_value();
+}
+inline void DoubleValue::_internal_set_value(double value) {
+  
+  _impl_.value_ = value;
+}
+inline void DoubleValue::set_value(double value) {
+  _internal_set_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.DoubleValue.value)
+}
+
+// -------------------------------------------------------------------
+
+// FloatValue
+
+// float value = 1;
+inline void FloatValue::clear_value() {
+  _impl_.value_ = 0;
+}
+inline float FloatValue::_internal_value() const {
+  return _impl_.value_;
+}
+inline float FloatValue::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.FloatValue.value)
+  return _internal_value();
+}
+inline void FloatValue::_internal_set_value(float value) {
+  
+  _impl_.value_ = value;
+}
+inline void FloatValue::set_value(float value) {
+  _internal_set_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.FloatValue.value)
+}
+
+// -------------------------------------------------------------------
+
+// Int64Value
+
+// int64 value = 1;
+inline void Int64Value::clear_value() {
+  _impl_.value_ = int64_t{0};
+}
+inline int64_t Int64Value::_internal_value() const {
+  return _impl_.value_;
+}
+inline int64_t Int64Value::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Int64Value.value)
+  return _internal_value();
+}
+inline void Int64Value::_internal_set_value(int64_t value) {
+  
+  _impl_.value_ = value;
+}
+inline void Int64Value::set_value(int64_t value) {
+  _internal_set_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Int64Value.value)
+}
+
+// -------------------------------------------------------------------
+
+// UInt64Value
+
+// uint64 value = 1;
+inline void UInt64Value::clear_value() {
+  _impl_.value_ = uint64_t{0u};
+}
+inline uint64_t UInt64Value::_internal_value() const {
+  return _impl_.value_;
+}
+inline uint64_t UInt64Value::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UInt64Value.value)
+  return _internal_value();
+}
+inline void UInt64Value::_internal_set_value(uint64_t value) {
+  
+  _impl_.value_ = value;
+}
+inline void UInt64Value::set_value(uint64_t value) {
+  _internal_set_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.UInt64Value.value)
+}
+
+// -------------------------------------------------------------------
+
+// Int32Value
+
+// int32 value = 1;
+inline void Int32Value::clear_value() {
+  _impl_.value_ = 0;
+}
+inline int32_t Int32Value::_internal_value() const {
+  return _impl_.value_;
+}
+inline int32_t Int32Value::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Int32Value.value)
+  return _internal_value();
+}
+inline void Int32Value::_internal_set_value(int32_t value) {
+  
+  _impl_.value_ = value;
+}
+inline void Int32Value::set_value(int32_t value) {
+  _internal_set_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.Int32Value.value)
+}
+
+// -------------------------------------------------------------------
+
+// UInt32Value
+
+// uint32 value = 1;
+inline void UInt32Value::clear_value() {
+  _impl_.value_ = 0u;
+}
+inline uint32_t UInt32Value::_internal_value() const {
+  return _impl_.value_;
+}
+inline uint32_t UInt32Value::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.UInt32Value.value)
+  return _internal_value();
+}
+inline void UInt32Value::_internal_set_value(uint32_t value) {
+  
+  _impl_.value_ = value;
+}
+inline void UInt32Value::set_value(uint32_t value) {
+  _internal_set_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.UInt32Value.value)
+}
+
+// -------------------------------------------------------------------
+
+// BoolValue
+
+// bool value = 1;
+inline void BoolValue::clear_value() {
+  _impl_.value_ = false;
+}
+inline bool BoolValue::_internal_value() const {
+  return _impl_.value_;
+}
+inline bool BoolValue::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.BoolValue.value)
+  return _internal_value();
+}
+inline void BoolValue::_internal_set_value(bool value) {
+  
+  _impl_.value_ = value;
+}
+inline void BoolValue::set_value(bool value) {
+  _internal_set_value(value);
+  // @@protoc_insertion_point(field_set:google.protobuf.BoolValue.value)
+}
+
+// -------------------------------------------------------------------
+
+// StringValue
+
+// string value = 1;
+inline void StringValue::clear_value() {
+  _impl_.value_.ClearToEmpty();
+}
+inline const std::string& StringValue::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.StringValue.value)
+  return _internal_value();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void StringValue::set_value(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.value_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.StringValue.value)
+}
+inline std::string* StringValue::mutable_value() {
+  std::string* _s = _internal_mutable_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.StringValue.value)
+  return _s;
+}
+inline const std::string& StringValue::_internal_value() const {
+  return _impl_.value_.Get();
+}
+inline void StringValue::_internal_set_value(const std::string& value) {
+  
+  _impl_.value_.Set(value, GetArenaForAllocation());
+}
+inline std::string* StringValue::_internal_mutable_value() {
+  
+  return _impl_.value_.Mutable(GetArenaForAllocation());
+}
+inline std::string* StringValue::release_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.StringValue.value)
+  return _impl_.value_.Release();
+}
+inline void StringValue::set_allocated_value(std::string* value) {
+  if (value != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.value_.SetAllocated(value, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.value_.IsDefault()) {
+    _impl_.value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value)
+}
+
+// -------------------------------------------------------------------
+
+// BytesValue
+
+// bytes value = 1;
+inline void BytesValue::clear_value() {
+  _impl_.value_.ClearToEmpty();
+}
+inline const std::string& BytesValue::value() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.BytesValue.value)
+  return _internal_value();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void BytesValue::set_value(ArgT0&& arg0, ArgT... args) {
+ 
+ _impl_.value_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.BytesValue.value)
+}
+inline std::string* BytesValue::mutable_value() {
+  std::string* _s = _internal_mutable_value();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.BytesValue.value)
+  return _s;
+}
+inline const std::string& BytesValue::_internal_value() const {
+  return _impl_.value_.Get();
+}
+inline void BytesValue::_internal_set_value(const std::string& value) {
+  
+  _impl_.value_.Set(value, GetArenaForAllocation());
+}
+inline std::string* BytesValue::_internal_mutable_value() {
+  
+  return _impl_.value_.Mutable(GetArenaForAllocation());
+}
+inline std::string* BytesValue::release_value() {
+  // @@protoc_insertion_point(field_release:google.protobuf.BytesValue.value)
+  return _impl_.value_.Release();
+}
+inline void BytesValue::set_allocated_value(std::string* value) {
+  if (value != nullptr) {
+    
+  } else {
+    
+  }
+  _impl_.value_.SetAllocated(value, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (_impl_.value_.IsDefault()) {
+    _impl_.value_.Set("", GetArenaForAllocation());
+  }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value)
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fwrappers_2eproto
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/any.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/any.cpp
new file mode 100644
index 0000000..346fa19
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/any.cpp
@@ -0,0 +1,82 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/any.h>
+
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/message.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+bool AnyMetadata::PackFrom(Arena* arena, const Message& message) {
+  return PackFrom(arena, message, kTypeGoogleApisComPrefix);
+}
+
+bool AnyMetadata::PackFrom(Arena* arena, const Message& message,
+                           StringPiece type_url_prefix) {
+  type_url_->Set(
+      GetTypeUrl(message.GetDescriptor()->full_name(), type_url_prefix), arena);
+  return message.SerializeToString(value_->Mutable(arena));
+}
+
+bool AnyMetadata::UnpackTo(Message* message) const {
+  if (!InternalIs(message->GetDescriptor()->full_name())) {
+    return false;
+  }
+  return message->ParseFromString(value_->Get());
+}
+
+bool GetAnyFieldDescriptors(const Message& message,
+                            const FieldDescriptor** type_url_field,
+                            const FieldDescriptor** value_field) {
+  const Descriptor* descriptor = message.GetDescriptor();
+  if (descriptor->full_name() != kAnyFullTypeName) {
+    return false;
+  }
+  *type_url_field = descriptor->FindFieldByNumber(1);
+  *value_field = descriptor->FindFieldByNumber(2);
+  return (*type_url_field != nullptr &&
+          (*type_url_field)->type() == FieldDescriptor::TYPE_STRING &&
+          *value_field != nullptr &&
+          (*value_field)->type() == FieldDescriptor::TYPE_BYTES);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/any.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/any.pb.cpp
new file mode 100644
index 0000000..c02f9eb
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/any.pb.cpp
@@ -0,0 +1,368 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/any.proto
+
+#include <google/protobuf/any.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+#if defined(__llvm__)
+  #pragma clang diagnostic push
+  #pragma clang diagnostic ignored "-Wuninitialized"
+#endif  // __llvm__
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR Any::Any(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.type_url_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.value_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_._any_metadata_)*/{&_impl_.type_url_, &_impl_.value_}} {}
+struct AnyDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR AnyDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~AnyDefaultTypeInternal() {}
+  union {
+    Any _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 AnyDefaultTypeInternal _Any_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2fany_2eproto[1];
+static constexpr ::_pb::EnumDescriptor const** file_level_enum_descriptors_google_2fprotobuf_2fany_2eproto = nullptr;
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2fany_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2fany_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Any, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Any, _impl_.type_url_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Any, _impl_.value_),
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Any)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_Any_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2fany_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n\031google/protobuf/any.proto\022\017google.prot"
+  "obuf\"&\n\003Any\022\020\n\010type_url\030\001 \001(\t\022\r\n\005value\030\002"
+  " \001(\014Bv\n\023com.google.protobufB\010AnyProtoP\001Z"
+  ",google.golang.org/protobuf/types/known/"
+  "anypb\242\002\003GPB\252\002\036Google.Protobuf.WellKnownT"
+  "ypesb\006proto3"
+  ;
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2fany_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fany_2eproto = {
+    false, false, 212, descriptor_table_protodef_google_2fprotobuf_2fany_2eproto,
+    "google/protobuf/any.proto",
+    &descriptor_table_google_2fprotobuf_2fany_2eproto_once, nullptr, 0, 1,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2fany_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2fany_2eproto, file_level_enum_descriptors_google_2fprotobuf_2fany_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2fany_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2fany_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2fany_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2fany_2eproto(&descriptor_table_google_2fprotobuf_2fany_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+bool Any::GetAnyFieldDescriptors(
+    const ::PROTOBUF_NAMESPACE_ID::Message& message,
+    const ::PROTOBUF_NAMESPACE_ID::FieldDescriptor** type_url_field,
+    const ::PROTOBUF_NAMESPACE_ID::FieldDescriptor** value_field) {
+  return ::_pbi::GetAnyFieldDescriptors(
+      message, type_url_field, value_field);
+}
+bool Any::ParseAnyTypeUrl(
+    ::PROTOBUF_NAMESPACE_ID::ConstStringParam type_url,
+    std::string* full_type_name) {
+  return ::_pbi::ParseAnyTypeUrl(type_url, full_type_name);
+}
+
+class Any::_Internal {
+ public:
+};
+
+Any::Any(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Any)
+}
+Any::Any(const Any& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Any* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.type_url_){}
+    , decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , /*decltype(_impl_._any_metadata_)*/{&_impl_.type_url_, &_impl_.value_}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.type_url_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.type_url_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_type_url().empty()) {
+    _this->_impl_.type_url_.Set(from._internal_type_url(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_value().empty()) {
+    _this->_impl_.value_.Set(from._internal_value(), 
+      _this->GetArenaForAllocation());
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Any)
+}
+
+inline void Any::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.type_url_){}
+    , decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , /*decltype(_impl_._any_metadata_)*/{&_impl_.type_url_, &_impl_.value_}
+  };
+  _impl_.type_url_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.type_url_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+Any::~Any() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Any)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Any::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.type_url_.Destroy();
+  _impl_.value_.Destroy();
+  _impl_._any_metadata_.~AnyMetadata();
+}
+
+void Any::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Any::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Any)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.type_url_.ClearToEmpty();
+  _impl_.value_.ClearToEmpty();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Any::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // string type_url = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_type_url();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Any.type_url"));
+        } else
+          goto handle_unusual;
+        continue;
+      // bytes value = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          auto str = _internal_mutable_value();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Any::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Any)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string type_url = 1;
+  if (!this->_internal_type_url().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_type_url().data(), static_cast<int>(this->_internal_type_url().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Any.type_url");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_type_url(), target);
+  }
+
+  // bytes value = 2;
+  if (!this->_internal_value().empty()) {
+    target = stream->WriteBytesMaybeAliased(
+        2, this->_internal_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Any)
+  return target;
+}
+
+size_t Any::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Any)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // string type_url = 1;
+  if (!this->_internal_type_url().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_type_url());
+  }
+
+  // bytes value = 2;
+  if (!this->_internal_value().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+        this->_internal_value());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Any::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Any::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Any::GetClassData() const { return &_class_data_; }
+
+
+void Any::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Any*>(&to_msg);
+  auto& from = static_cast<const Any&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Any)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (!from._internal_type_url().empty()) {
+    _this->_internal_set_type_url(from._internal_type_url());
+  }
+  if (!from._internal_value().empty()) {
+    _this->_internal_set_value(from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Any::CopyFrom(const Any& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Any)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Any::IsInitialized() const {
+  return true;
+}
+
+void Any::InternalSwap(Any* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.type_url_, lhs_arena,
+      &other->_impl_.type_url_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.value_, lhs_arena,
+      &other->_impl_.value_, rhs_arena
+  );
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Any::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fany_2eproto_getter, &descriptor_table_google_2fprotobuf_2fany_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fany_2eproto[0]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Any*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Any >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Any >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#if defined(__llvm__)
+  #pragma clang diagnostic pop
+#endif  // __llvm__
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/any_lite.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/any_lite.cpp
new file mode 100644
index 0000000..f283a31
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/any_lite.cpp
@@ -0,0 +1,96 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/any.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+std::string GetTypeUrl(StringPiece message_name,
+                       StringPiece type_url_prefix) {
+  if (!type_url_prefix.empty() &&
+      type_url_prefix[type_url_prefix.size() - 1] == '/') {
+    return StrCat(type_url_prefix, message_name);
+  } else {
+    return StrCat(type_url_prefix, "/", message_name);
+  }
+}
+
+const char kAnyFullTypeName[] = "google.protobuf.Any";
+const char kTypeGoogleApisComPrefix[] = "type.googleapis.com/";
+const char kTypeGoogleProdComPrefix[] = "type.googleprod.com/";
+
+bool AnyMetadata::InternalPackFrom(Arena* arena, const MessageLite& message,
+                                   StringPiece type_url_prefix,
+                                   StringPiece type_name) {
+  type_url_->Set(GetTypeUrl(type_name, type_url_prefix), arena);
+  return message.SerializeToString(value_->Mutable(arena));
+}
+
+bool AnyMetadata::InternalUnpackTo(StringPiece type_name,
+                                   MessageLite* message) const {
+  if (!InternalIs(type_name)) {
+    return false;
+  }
+  return message->ParseFromString(value_->Get());
+}
+
+bool AnyMetadata::InternalIs(StringPiece type_name) const {
+  StringPiece type_url = type_url_->Get();
+  return type_url.size() >= type_name.size() + 1 &&
+         type_url[type_url.size() - type_name.size() - 1] == '/' &&
+         HasSuffixString(type_url, type_name);
+}
+
+bool ParseAnyTypeUrl(StringPiece type_url, std::string* url_prefix,
+                     std::string* full_type_name) {
+  size_t pos = type_url.find_last_of('/');
+  if (pos == std::string::npos || pos + 1 == type_url.size()) {
+    return false;
+  }
+  if (url_prefix) {
+    *url_prefix = std::string(type_url.substr(0, pos + 1));
+  }
+  *full_type_name = std::string(type_url.substr(pos + 1));
+  return true;
+}
+
+bool ParseAnyTypeUrl(StringPiece type_url, std::string* full_type_name) {
+  return ParseAnyTypeUrl(type_url, nullptr, full_type_name);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/api.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/api.pb.cpp
new file mode 100644
index 0000000..24b6049
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/api.pb.cpp
@@ -0,0 +1,1309 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/api.proto
+
+#include <google/protobuf/api.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR Api::Api(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.methods_)*/{}
+  , /*decltype(_impl_.options_)*/{}
+  , /*decltype(_impl_.mixins_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.version_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.source_context_)*/nullptr
+  , /*decltype(_impl_.syntax_)*/0
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct ApiDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR ApiDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~ApiDefaultTypeInternal() {}
+  union {
+    Api _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ApiDefaultTypeInternal _Api_default_instance_;
+PROTOBUF_CONSTEXPR Method::Method(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.options_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.request_type_url_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.response_type_url_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.request_streaming_)*/false
+  , /*decltype(_impl_.response_streaming_)*/false
+  , /*decltype(_impl_.syntax_)*/0
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct MethodDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR MethodDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~MethodDefaultTypeInternal() {}
+  union {
+    Method _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 MethodDefaultTypeInternal _Method_default_instance_;
+PROTOBUF_CONSTEXPR Mixin::Mixin(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.root_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct MixinDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR MixinDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~MixinDefaultTypeInternal() {}
+  union {
+    Mixin _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 MixinDefaultTypeInternal _Mixin_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2fapi_2eproto[3];
+static constexpr ::_pb::EnumDescriptor const** file_level_enum_descriptors_google_2fprotobuf_2fapi_2eproto = nullptr;
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2fapi_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2fapi_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Api, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Api, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Api, _impl_.methods_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Api, _impl_.options_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Api, _impl_.version_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Api, _impl_.source_context_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Api, _impl_.mixins_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Api, _impl_.syntax_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Method, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Method, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Method, _impl_.request_type_url_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Method, _impl_.request_streaming_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Method, _impl_.response_type_url_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Method, _impl_.response_streaming_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Method, _impl_.options_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Method, _impl_.syntax_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Mixin, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Mixin, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Mixin, _impl_.root_),
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Api)},
+  { 13, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Method)},
+  { 26, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Mixin)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_Api_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_Method_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_Mixin_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2fapi_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n\031google/protobuf/api.proto\022\017google.prot"
+  "obuf\032$google/protobuf/source_context.pro"
+  "to\032\032google/protobuf/type.proto\"\201\002\n\003Api\022\014"
+  "\n\004name\030\001 \001(\t\022(\n\007methods\030\002 \003(\0132\027.google.p"
+  "rotobuf.Method\022(\n\007options\030\003 \003(\0132\027.google"
+  ".protobuf.Option\022\017\n\007version\030\004 \001(\t\0226\n\016sou"
+  "rce_context\030\005 \001(\0132\036.google.protobuf.Sour"
+  "ceContext\022&\n\006mixins\030\006 \003(\0132\026.google.proto"
+  "buf.Mixin\022\'\n\006syntax\030\007 \001(\0162\027.google.proto"
+  "buf.Syntax\"\325\001\n\006Method\022\014\n\004name\030\001 \001(\t\022\030\n\020r"
+  "equest_type_url\030\002 \001(\t\022\031\n\021request_streami"
+  "ng\030\003 \001(\010\022\031\n\021response_type_url\030\004 \001(\t\022\032\n\022r"
+  "esponse_streaming\030\005 \001(\010\022(\n\007options\030\006 \003(\013"
+  "2\027.google.protobuf.Option\022\'\n\006syntax\030\007 \001("
+  "\0162\027.google.protobuf.Syntax\"#\n\005Mixin\022\014\n\004n"
+  "ame\030\001 \001(\t\022\014\n\004root\030\002 \001(\tBv\n\023com.google.pr"
+  "otobufB\010ApiProtoP\001Z,google.golang.org/pr"
+  "otobuf/types/known/apipb\242\002\003GPB\252\002\036Google."
+  "Protobuf.WellKnownTypesb\006proto3"
+  ;
+static const ::_pbi::DescriptorTable* const descriptor_table_google_2fprotobuf_2fapi_2eproto_deps[2] = {
+  &::descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto,
+  &::descriptor_table_google_2fprotobuf_2ftype_2eproto,
+};
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2fapi_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fapi_2eproto = {
+    false, false, 751, descriptor_table_protodef_google_2fprotobuf_2fapi_2eproto,
+    "google/protobuf/api.proto",
+    &descriptor_table_google_2fprotobuf_2fapi_2eproto_once, descriptor_table_google_2fprotobuf_2fapi_2eproto_deps, 2, 3,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2fapi_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2fapi_2eproto, file_level_enum_descriptors_google_2fprotobuf_2fapi_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2fapi_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2fapi_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2fapi_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2fapi_2eproto(&descriptor_table_google_2fprotobuf_2fapi_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class Api::_Internal {
+ public:
+  static const ::PROTOBUF_NAMESPACE_ID::SourceContext& source_context(const Api* msg);
+};
+
+const ::PROTOBUF_NAMESPACE_ID::SourceContext&
+Api::_Internal::source_context(const Api* msg) {
+  return *msg->_impl_.source_context_;
+}
+void Api::clear_options() {
+  _impl_.options_.Clear();
+}
+void Api::clear_source_context() {
+  if (GetArenaForAllocation() == nullptr && _impl_.source_context_ != nullptr) {
+    delete _impl_.source_context_;
+  }
+  _impl_.source_context_ = nullptr;
+}
+Api::Api(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Api)
+}
+Api::Api(const Api& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Api* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.methods_){from._impl_.methods_}
+    , decltype(_impl_.options_){from._impl_.options_}
+    , decltype(_impl_.mixins_){from._impl_.mixins_}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.version_){}
+    , decltype(_impl_.source_context_){nullptr}
+    , decltype(_impl_.syntax_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_name().empty()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.version_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.version_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_version().empty()) {
+    _this->_impl_.version_.Set(from._internal_version(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_source_context()) {
+    _this->_impl_.source_context_ = new ::PROTOBUF_NAMESPACE_ID::SourceContext(*from._impl_.source_context_);
+  }
+  _this->_impl_.syntax_ = from._impl_.syntax_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Api)
+}
+
+inline void Api::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.methods_){arena}
+    , decltype(_impl_.options_){arena}
+    , decltype(_impl_.mixins_){arena}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.version_){}
+    , decltype(_impl_.source_context_){nullptr}
+    , decltype(_impl_.syntax_){0}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.version_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.version_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+Api::~Api() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Api)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Api::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.methods_.~RepeatedPtrField();
+  _impl_.options_.~RepeatedPtrField();
+  _impl_.mixins_.~RepeatedPtrField();
+  _impl_.name_.Destroy();
+  _impl_.version_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.source_context_;
+}
+
+void Api::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Api::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Api)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.methods_.Clear();
+  _impl_.options_.Clear();
+  _impl_.mixins_.Clear();
+  _impl_.name_.ClearToEmpty();
+  _impl_.version_.ClearToEmpty();
+  if (GetArenaForAllocation() == nullptr && _impl_.source_context_ != nullptr) {
+    delete _impl_.source_context_;
+  }
+  _impl_.source_context_ = nullptr;
+  _impl_.syntax_ = 0;
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Api::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Api.name"));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.Method methods = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_methods(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.Option options = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_options(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<26>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // string version = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          auto str = _internal_mutable_version();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Api.version"));
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.SourceContext source_context = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+          ptr = ctx->ParseMessage(_internal_mutable_source_context(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.Mixin mixins = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 50)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_mixins(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<50>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.Syntax syntax = 7;
+      case 7:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 56)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          _internal_set_syntax(static_cast<::PROTOBUF_NAMESPACE_ID::Syntax>(val));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Api::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Api)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Api.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // repeated .google.protobuf.Method methods = 2;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_methods_size()); i < n; i++) {
+    const auto& repfield = this->_internal_methods(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(2, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.Option options = 3;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_options_size()); i < n; i++) {
+    const auto& repfield = this->_internal_options(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(3, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // string version = 4;
+  if (!this->_internal_version().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_version().data(), static_cast<int>(this->_internal_version().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Api.version");
+    target = stream->WriteStringMaybeAliased(
+        4, this->_internal_version(), target);
+  }
+
+  // .google.protobuf.SourceContext source_context = 5;
+  if (this->_internal_has_source_context()) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(5, _Internal::source_context(this),
+        _Internal::source_context(this).GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.Mixin mixins = 6;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_mixins_size()); i < n; i++) {
+    const auto& repfield = this->_internal_mixins(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(6, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // .google.protobuf.Syntax syntax = 7;
+  if (this->_internal_syntax() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      7, this->_internal_syntax(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Api)
+  return target;
+}
+
+size_t Api::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Api)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.Method methods = 2;
+  total_size += 1UL * this->_internal_methods_size();
+  for (const auto& msg : this->_impl_.methods_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.Option options = 3;
+  total_size += 1UL * this->_internal_options_size();
+  for (const auto& msg : this->_impl_.options_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.Mixin mixins = 6;
+  total_size += 1UL * this->_internal_mixins_size();
+  for (const auto& msg : this->_impl_.mixins_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name());
+  }
+
+  // string version = 4;
+  if (!this->_internal_version().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_version());
+  }
+
+  // .google.protobuf.SourceContext source_context = 5;
+  if (this->_internal_has_source_context()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+        *_impl_.source_context_);
+  }
+
+  // .google.protobuf.Syntax syntax = 7;
+  if (this->_internal_syntax() != 0) {
+    total_size += 1 +
+      ::_pbi::WireFormatLite::EnumSize(this->_internal_syntax());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Api::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Api::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Api::GetClassData() const { return &_class_data_; }
+
+
+void Api::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Api*>(&to_msg);
+  auto& from = static_cast<const Api&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Api)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.methods_.MergeFrom(from._impl_.methods_);
+  _this->_impl_.options_.MergeFrom(from._impl_.options_);
+  _this->_impl_.mixins_.MergeFrom(from._impl_.mixins_);
+  if (!from._internal_name().empty()) {
+    _this->_internal_set_name(from._internal_name());
+  }
+  if (!from._internal_version().empty()) {
+    _this->_internal_set_version(from._internal_version());
+  }
+  if (from._internal_has_source_context()) {
+    _this->_internal_mutable_source_context()->::PROTOBUF_NAMESPACE_ID::SourceContext::MergeFrom(
+        from._internal_source_context());
+  }
+  if (from._internal_syntax() != 0) {
+    _this->_internal_set_syntax(from._internal_syntax());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Api::CopyFrom(const Api& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Api)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Api::IsInitialized() const {
+  return true;
+}
+
+void Api::InternalSwap(Api* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.methods_.InternalSwap(&other->_impl_.methods_);
+  _impl_.options_.InternalSwap(&other->_impl_.options_);
+  _impl_.mixins_.InternalSwap(&other->_impl_.mixins_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.version_, lhs_arena,
+      &other->_impl_.version_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(Api, _impl_.syntax_)
+      + sizeof(Api::_impl_.syntax_)
+      - PROTOBUF_FIELD_OFFSET(Api, _impl_.source_context_)>(
+          reinterpret_cast<char*>(&_impl_.source_context_),
+          reinterpret_cast<char*>(&other->_impl_.source_context_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Api::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fapi_2eproto_getter, &descriptor_table_google_2fprotobuf_2fapi_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fapi_2eproto[0]);
+}
+
+// ===================================================================
+
+class Method::_Internal {
+ public:
+};
+
+void Method::clear_options() {
+  _impl_.options_.Clear();
+}
+Method::Method(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Method)
+}
+Method::Method(const Method& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Method* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.options_){from._impl_.options_}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.request_type_url_){}
+    , decltype(_impl_.response_type_url_){}
+    , decltype(_impl_.request_streaming_){}
+    , decltype(_impl_.response_streaming_){}
+    , decltype(_impl_.syntax_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_name().empty()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.request_type_url_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.request_type_url_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_request_type_url().empty()) {
+    _this->_impl_.request_type_url_.Set(from._internal_request_type_url(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.response_type_url_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.response_type_url_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_response_type_url().empty()) {
+    _this->_impl_.response_type_url_.Set(from._internal_response_type_url(), 
+      _this->GetArenaForAllocation());
+  }
+  ::memcpy(&_impl_.request_streaming_, &from._impl_.request_streaming_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.syntax_) -
+    reinterpret_cast<char*>(&_impl_.request_streaming_)) + sizeof(_impl_.syntax_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Method)
+}
+
+inline void Method::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.options_){arena}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.request_type_url_){}
+    , decltype(_impl_.response_type_url_){}
+    , decltype(_impl_.request_streaming_){false}
+    , decltype(_impl_.response_streaming_){false}
+    , decltype(_impl_.syntax_){0}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.request_type_url_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.request_type_url_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.response_type_url_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.response_type_url_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+Method::~Method() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Method)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Method::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.options_.~RepeatedPtrField();
+  _impl_.name_.Destroy();
+  _impl_.request_type_url_.Destroy();
+  _impl_.response_type_url_.Destroy();
+}
+
+void Method::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Method::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Method)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.options_.Clear();
+  _impl_.name_.ClearToEmpty();
+  _impl_.request_type_url_.ClearToEmpty();
+  _impl_.response_type_url_.ClearToEmpty();
+  ::memset(&_impl_.request_streaming_, 0, static_cast<size_t>(
+      reinterpret_cast<char*>(&_impl_.syntax_) -
+      reinterpret_cast<char*>(&_impl_.request_streaming_)) + sizeof(_impl_.syntax_));
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Method::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Method.name"));
+        } else
+          goto handle_unusual;
+        continue;
+      // string request_type_url = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          auto str = _internal_mutable_request_type_url();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Method.request_type_url"));
+        } else
+          goto handle_unusual;
+        continue;
+      // bool request_streaming = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) {
+          _impl_.request_streaming_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // string response_type_url = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          auto str = _internal_mutable_response_type_url();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Method.response_type_url"));
+        } else
+          goto handle_unusual;
+        continue;
+      // bool response_streaming = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 40)) {
+          _impl_.response_streaming_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.Option options = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 50)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_options(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<50>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.Syntax syntax = 7;
+      case 7:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 56)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          _internal_set_syntax(static_cast<::PROTOBUF_NAMESPACE_ID::Syntax>(val));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Method::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Method)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Method.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // string request_type_url = 2;
+  if (!this->_internal_request_type_url().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_request_type_url().data(), static_cast<int>(this->_internal_request_type_url().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Method.request_type_url");
+    target = stream->WriteStringMaybeAliased(
+        2, this->_internal_request_type_url(), target);
+  }
+
+  // bool request_streaming = 3;
+  if (this->_internal_request_streaming() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(3, this->_internal_request_streaming(), target);
+  }
+
+  // string response_type_url = 4;
+  if (!this->_internal_response_type_url().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_response_type_url().data(), static_cast<int>(this->_internal_response_type_url().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Method.response_type_url");
+    target = stream->WriteStringMaybeAliased(
+        4, this->_internal_response_type_url(), target);
+  }
+
+  // bool response_streaming = 5;
+  if (this->_internal_response_streaming() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(5, this->_internal_response_streaming(), target);
+  }
+
+  // repeated .google.protobuf.Option options = 6;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_options_size()); i < n; i++) {
+    const auto& repfield = this->_internal_options(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(6, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // .google.protobuf.Syntax syntax = 7;
+  if (this->_internal_syntax() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      7, this->_internal_syntax(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Method)
+  return target;
+}
+
+size_t Method::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Method)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.Option options = 6;
+  total_size += 1UL * this->_internal_options_size();
+  for (const auto& msg : this->_impl_.options_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name());
+  }
+
+  // string request_type_url = 2;
+  if (!this->_internal_request_type_url().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_request_type_url());
+  }
+
+  // string response_type_url = 4;
+  if (!this->_internal_response_type_url().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_response_type_url());
+  }
+
+  // bool request_streaming = 3;
+  if (this->_internal_request_streaming() != 0) {
+    total_size += 1 + 1;
+  }
+
+  // bool response_streaming = 5;
+  if (this->_internal_response_streaming() != 0) {
+    total_size += 1 + 1;
+  }
+
+  // .google.protobuf.Syntax syntax = 7;
+  if (this->_internal_syntax() != 0) {
+    total_size += 1 +
+      ::_pbi::WireFormatLite::EnumSize(this->_internal_syntax());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Method::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Method::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Method::GetClassData() const { return &_class_data_; }
+
+
+void Method::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Method*>(&to_msg);
+  auto& from = static_cast<const Method&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Method)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.options_.MergeFrom(from._impl_.options_);
+  if (!from._internal_name().empty()) {
+    _this->_internal_set_name(from._internal_name());
+  }
+  if (!from._internal_request_type_url().empty()) {
+    _this->_internal_set_request_type_url(from._internal_request_type_url());
+  }
+  if (!from._internal_response_type_url().empty()) {
+    _this->_internal_set_response_type_url(from._internal_response_type_url());
+  }
+  if (from._internal_request_streaming() != 0) {
+    _this->_internal_set_request_streaming(from._internal_request_streaming());
+  }
+  if (from._internal_response_streaming() != 0) {
+    _this->_internal_set_response_streaming(from._internal_response_streaming());
+  }
+  if (from._internal_syntax() != 0) {
+    _this->_internal_set_syntax(from._internal_syntax());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Method::CopyFrom(const Method& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Method)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Method::IsInitialized() const {
+  return true;
+}
+
+void Method::InternalSwap(Method* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.options_.InternalSwap(&other->_impl_.options_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.request_type_url_, lhs_arena,
+      &other->_impl_.request_type_url_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.response_type_url_, lhs_arena,
+      &other->_impl_.response_type_url_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(Method, _impl_.syntax_)
+      + sizeof(Method::_impl_.syntax_)
+      - PROTOBUF_FIELD_OFFSET(Method, _impl_.request_streaming_)>(
+          reinterpret_cast<char*>(&_impl_.request_streaming_),
+          reinterpret_cast<char*>(&other->_impl_.request_streaming_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Method::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fapi_2eproto_getter, &descriptor_table_google_2fprotobuf_2fapi_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fapi_2eproto[1]);
+}
+
+// ===================================================================
+
+class Mixin::_Internal {
+ public:
+};
+
+Mixin::Mixin(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Mixin)
+}
+Mixin::Mixin(const Mixin& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Mixin* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.name_){}
+    , decltype(_impl_.root_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_name().empty()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.root_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.root_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_root().empty()) {
+    _this->_impl_.root_.Set(from._internal_root(), 
+      _this->GetArenaForAllocation());
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Mixin)
+}
+
+inline void Mixin::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.name_){}
+    , decltype(_impl_.root_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.root_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.root_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+Mixin::~Mixin() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Mixin)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Mixin::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.name_.Destroy();
+  _impl_.root_.Destroy();
+}
+
+void Mixin::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Mixin::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Mixin)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.name_.ClearToEmpty();
+  _impl_.root_.ClearToEmpty();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Mixin::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Mixin.name"));
+        } else
+          goto handle_unusual;
+        continue;
+      // string root = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          auto str = _internal_mutable_root();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Mixin.root"));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Mixin::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Mixin)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Mixin.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // string root = 2;
+  if (!this->_internal_root().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_root().data(), static_cast<int>(this->_internal_root().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Mixin.root");
+    target = stream->WriteStringMaybeAliased(
+        2, this->_internal_root(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Mixin)
+  return target;
+}
+
+size_t Mixin::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Mixin)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name());
+  }
+
+  // string root = 2;
+  if (!this->_internal_root().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_root());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Mixin::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Mixin::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Mixin::GetClassData() const { return &_class_data_; }
+
+
+void Mixin::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Mixin*>(&to_msg);
+  auto& from = static_cast<const Mixin&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Mixin)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (!from._internal_name().empty()) {
+    _this->_internal_set_name(from._internal_name());
+  }
+  if (!from._internal_root().empty()) {
+    _this->_internal_set_root(from._internal_root());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Mixin::CopyFrom(const Mixin& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Mixin)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Mixin::IsInitialized() const {
+  return true;
+}
+
+void Mixin::InternalSwap(Mixin* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.root_, lhs_arena,
+      &other->_impl_.root_, rhs_arena
+  );
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Mixin::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fapi_2eproto_getter, &descriptor_table_google_2fprotobuf_2fapi_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fapi_2eproto[2]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Api*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Api >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Api >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Method*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Method >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Method >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Mixin*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Mixin >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Mixin >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/arena.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/arena.cpp
new file mode 100644
index 0000000..1944042
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/arena.cpp
@@ -0,0 +1,537 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/arena.h>
+
+#include <algorithm>
+#include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <typeinfo>
+
+#include <google/protobuf/arena_impl.h>
+#include <google/protobuf/arenaz_sampler.h>
+#include <google/protobuf/port.h>
+
+#include <google/protobuf/stubs/mutex.h>
+#ifdef ADDRESS_SANITIZER
+#include <sanitizer/asan_interface.h>
+#endif  // ADDRESS_SANITIZER
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+static SerialArena::Memory AllocateMemory(const AllocationPolicy* policy_ptr,
+                                          size_t last_size, size_t min_bytes) {
+  AllocationPolicy policy;  // default policy
+  if (policy_ptr) policy = *policy_ptr;
+  size_t size;
+  if (last_size != 0) {
+    // Double the current block size, up to a limit.
+    auto max_size = policy.max_block_size;
+    size = std::min(2 * last_size, max_size);
+  } else {
+    size = policy.start_block_size;
+  }
+  // Verify that min_bytes + kBlockHeaderSize won't overflow.
+  GOOGLE_CHECK_LE(min_bytes,
+           std::numeric_limits<size_t>::max() - SerialArena::kBlockHeaderSize);
+  size = std::max(size, SerialArena::kBlockHeaderSize + min_bytes);
+
+  void* mem;
+  if (policy.block_alloc == nullptr) {
+    mem = ::operator new(size);
+  } else {
+    mem = policy.block_alloc(size);
+  }
+  return {mem, size};
+}
+
+class GetDeallocator {
+ public:
+  GetDeallocator(const AllocationPolicy* policy, size_t* space_allocated)
+      : dealloc_(policy ? policy->block_dealloc : nullptr),
+        space_allocated_(space_allocated) {}
+
+  void operator()(SerialArena::Memory mem) const {
+#ifdef ADDRESS_SANITIZER
+    // This memory was provided by the underlying allocator as unpoisoned,
+    // so return it in an unpoisoned state.
+    ASAN_UNPOISON_MEMORY_REGION(mem.ptr, mem.size);
+#endif  // ADDRESS_SANITIZER
+    if (dealloc_) {
+      dealloc_(mem.ptr, mem.size);
+    } else {
+      internal::SizedDelete(mem.ptr, mem.size);
+    }
+    *space_allocated_ += mem.size;
+  }
+
+ private:
+  void (*dealloc_)(void*, size_t);
+  size_t* space_allocated_;
+};
+
+SerialArena::SerialArena(Block* b, void* owner, ThreadSafeArenaStats* stats)
+    : space_allocated_(b->size) {
+  owner_ = owner;
+  head_ = b;
+  ptr_ = b->Pointer(kBlockHeaderSize + ThreadSafeArena::kSerialArenaSize);
+  limit_ = b->Pointer(b->size & static_cast<size_t>(-8));
+  arena_stats_ = stats;
+}
+
+SerialArena* SerialArena::New(Memory mem, void* owner,
+                              ThreadSafeArenaStats* stats) {
+  GOOGLE_DCHECK_LE(kBlockHeaderSize + ThreadSafeArena::kSerialArenaSize, mem.size);
+  ThreadSafeArenaStats::RecordAllocateStats(
+      stats, /*requested=*/mem.size, /*allocated=*/mem.size, /*wasted=*/0);
+  auto b = new (mem.ptr) Block{nullptr, mem.size};
+  return new (b->Pointer(kBlockHeaderSize)) SerialArena(b, owner, stats);
+}
+
+template <typename Deallocator>
+SerialArena::Memory SerialArena::Free(Deallocator deallocator) {
+  Block* b = head_;
+  Memory mem = {b, b->size};
+  while (b->next) {
+    b = b->next;  // We must first advance before deleting this block
+    deallocator(mem);
+    mem = {b, b->size};
+  }
+  return mem;
+}
+
+PROTOBUF_NOINLINE
+std::pair<void*, SerialArena::CleanupNode*>
+SerialArena::AllocateAlignedWithCleanupFallback(
+    size_t n, const AllocationPolicy* policy) {
+  AllocateNewBlock(n + kCleanupSize, policy);
+  return AllocateFromExistingWithCleanupFallback(n);
+}
+
+PROTOBUF_NOINLINE
+void* SerialArena::AllocateAlignedFallback(size_t n,
+                                           const AllocationPolicy* policy) {
+  AllocateNewBlock(n, policy);
+  return AllocateFromExisting(n);
+}
+
+void SerialArena::AllocateNewBlock(size_t n, const AllocationPolicy* policy) {
+  // Sync limit to block
+  head_->start = reinterpret_cast<CleanupNode*>(limit_);
+
+  // Record how much used in this block.
+  size_t used = ptr_ - head_->Pointer(kBlockHeaderSize);
+  size_t wasted = head_->size - used;
+  space_used_ += used;
+
+  // TODO(sbenza): Evaluate if pushing unused space into the cached blocks is a
+  // win. In preliminary testing showed increased memory savings as expected,
+  // but with a CPU regression. The regression might have been an artifact of
+  // the microbenchmark.
+
+  auto mem = AllocateMemory(policy, head_->size, n);
+  // We don't want to emit an expensive RMW instruction that requires
+  // exclusive access to a cacheline. Hence we write it in terms of a
+  // regular add.
+  auto relaxed = std::memory_order_relaxed;
+  space_allocated_.store(space_allocated_.load(relaxed) + mem.size, relaxed);
+  ThreadSafeArenaStats::RecordAllocateStats(arena_stats_, /*requested=*/n,
+                                            /*allocated=*/mem.size, wasted);
+  head_ = new (mem.ptr) Block{head_, mem.size};
+  ptr_ = head_->Pointer(kBlockHeaderSize);
+  limit_ = head_->Pointer(head_->size);
+
+#ifdef ADDRESS_SANITIZER
+  ASAN_POISON_MEMORY_REGION(ptr_, limit_ - ptr_);
+#endif  // ADDRESS_SANITIZER
+}
+
+uint64_t SerialArena::SpaceUsed() const {
+  uint64_t space_used = ptr_ - head_->Pointer(kBlockHeaderSize);
+  space_used += space_used_;
+  // Remove the overhead of the SerialArena itself.
+  space_used -= ThreadSafeArena::kSerialArenaSize;
+  return space_used;
+}
+
+void SerialArena::CleanupList() {
+  Block* b = head_;
+  b->start = reinterpret_cast<CleanupNode*>(limit_);
+  do {
+    auto* limit = reinterpret_cast<CleanupNode*>(
+        b->Pointer(b->size & static_cast<size_t>(-8)));
+    auto it = b->start;
+    auto num = limit - it;
+    if (num > 0) {
+      for (; it < limit; it++) {
+        it->cleanup(it->elem);
+      }
+    }
+    b = b->next;
+  } while (b);
+}
+
+
+ThreadSafeArena::CacheAlignedLifecycleIdGenerator
+    ThreadSafeArena::lifecycle_id_generator_;
+#if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
+ThreadSafeArena::ThreadCache& ThreadSafeArena::thread_cache() {
+  static internal::ThreadLocalStorage<ThreadCache>* thread_cache_ =
+      new internal::ThreadLocalStorage<ThreadCache>();
+  return *thread_cache_->Get();
+}
+#elif defined(PROTOBUF_USE_DLLS)
+ThreadSafeArena::ThreadCache& ThreadSafeArena::thread_cache() {
+  static PROTOBUF_THREAD_LOCAL ThreadCache thread_cache_ = {
+      0, static_cast<LifecycleIdAtomic>(-1), nullptr};
+  return thread_cache_;
+}
+#else
+PROTOBUF_THREAD_LOCAL ThreadSafeArena::ThreadCache
+    ThreadSafeArena::thread_cache_ = {0, static_cast<LifecycleIdAtomic>(-1),
+                                      nullptr};
+#endif
+
+void ThreadSafeArena::InitializeFrom(void* mem, size_t size) {
+  GOOGLE_DCHECK_EQ(reinterpret_cast<uintptr_t>(mem) & 7, 0u);
+  GOOGLE_DCHECK(!AllocPolicy());  // Reset should call InitializeWithPolicy instead.
+  Init();
+
+  // Ignore initial block if it is too small.
+  if (mem != nullptr && size >= kBlockHeaderSize + kSerialArenaSize) {
+    alloc_policy_.set_is_user_owned_initial_block(true);
+    SetInitialBlock(mem, size);
+  }
+}
+
+void ThreadSafeArena::InitializeWithPolicy(void* mem, size_t size,
+                                           AllocationPolicy policy) {
+#ifndef NDEBUG
+  const uint64_t old_alloc_policy = alloc_policy_.get_raw();
+  // If there was a policy (e.g., in Reset()), make sure flags were preserved.
+#define GOOGLE_DCHECK_POLICY_FLAGS_() \
+  if (old_alloc_policy > 3)    \
+    GOOGLE_CHECK_EQ(old_alloc_policy & 3, alloc_policy_.get_raw() & 3)
+#else
+#define GOOGLE_DCHECK_POLICY_FLAGS_()
+#endif  // NDEBUG
+
+  if (policy.IsDefault()) {
+    // Legacy code doesn't use the API above, but provides the initial block
+    // through ArenaOptions. I suspect most do not touch the allocation
+    // policy parameters.
+    InitializeFrom(mem, size);
+    GOOGLE_DCHECK_POLICY_FLAGS_();
+    return;
+  }
+  GOOGLE_DCHECK_EQ(reinterpret_cast<uintptr_t>(mem) & 7, 0u);
+  Init();
+
+  // Ignore initial block if it is too small. We include an optional
+  // AllocationPolicy in this check, so that this can be allocated on the
+  // first block.
+  constexpr size_t kAPSize = internal::AlignUpTo8(sizeof(AllocationPolicy));
+  constexpr size_t kMinimumSize = kBlockHeaderSize + kSerialArenaSize + kAPSize;
+
+  // The value for alloc_policy_ stores whether or not allocations should be
+  // recorded.
+  alloc_policy_.set_should_record_allocs(
+      policy.metrics_collector != nullptr &&
+      policy.metrics_collector->RecordAllocs());
+  // Make sure we have an initial block to store the AllocationPolicy.
+  if (mem != nullptr && size >= kMinimumSize) {
+    alloc_policy_.set_is_user_owned_initial_block(true);
+  } else {
+    auto tmp = AllocateMemory(&policy, 0, kMinimumSize);
+    mem = tmp.ptr;
+    size = tmp.size;
+  }
+  SetInitialBlock(mem, size);
+
+  auto sa = threads_.load(std::memory_order_relaxed);
+  // We ensured enough space so this cannot fail.
+  void* p;
+  if (!sa || !sa->MaybeAllocateAligned(kAPSize, &p)) {
+    GOOGLE_LOG(FATAL) << "MaybeAllocateAligned cannot fail here.";
+    return;
+  }
+  new (p) AllocationPolicy{policy};
+  // Low bits store flags, so they mustn't be overwritten.
+  GOOGLE_DCHECK_EQ(0, reinterpret_cast<uintptr_t>(p) & 3);
+  alloc_policy_.set_policy(reinterpret_cast<AllocationPolicy*>(p));
+  GOOGLE_DCHECK_POLICY_FLAGS_();
+
+#undef GOOGLE_DCHECK_POLICY_FLAGS_
+}
+
+void ThreadSafeArena::Init() {
+#ifndef NDEBUG
+  const bool was_message_owned = IsMessageOwned();
+#endif  // NDEBUG
+  ThreadCache& tc = thread_cache();
+  auto id = tc.next_lifecycle_id;
+  // We increment lifecycle_id's by multiples of two so we can use bit 0 as
+  // a tag.
+  constexpr uint64_t kDelta = 2;
+  constexpr uint64_t kInc = ThreadCache::kPerThreadIds * kDelta;
+  if (PROTOBUF_PREDICT_FALSE((id & (kInc - 1)) == 0)) {
+    constexpr auto relaxed = std::memory_order_relaxed;
+    // On platforms that don't support uint64_t atomics we can certainly not
+    // afford to increment by large intervals and expect uniqueness due to
+    // wrapping, hence we only add by 1.
+    id = lifecycle_id_generator_.id.fetch_add(1, relaxed) * kInc;
+  }
+  tc.next_lifecycle_id = id + kDelta;
+  // Message ownership is stored in tag_and_id_, and is set in the constructor.
+  // This flag bit must be preserved, even across calls to Reset().
+  tag_and_id_ = id | (tag_and_id_ & kMessageOwnedArena);
+  hint_.store(nullptr, std::memory_order_relaxed);
+  threads_.store(nullptr, std::memory_order_relaxed);
+#ifndef NDEBUG
+  GOOGLE_CHECK_EQ(was_message_owned, IsMessageOwned());
+#endif  // NDEBUG
+  arena_stats_ = Sample();
+}
+
+void ThreadSafeArena::SetInitialBlock(void* mem, size_t size) {
+  SerialArena* serial = SerialArena::New({mem, size}, &thread_cache(),
+                                         arena_stats_.MutableStats());
+  serial->set_next(NULL);
+  threads_.store(serial, std::memory_order_relaxed);
+  CacheSerialArena(serial);
+}
+
+ThreadSafeArena::~ThreadSafeArena() {
+  // Have to do this in a first pass, because some of the destructors might
+  // refer to memory in other blocks.
+  CleanupList();
+
+  size_t space_allocated = 0;
+  auto mem = Free(&space_allocated);
+
+  // Policy is about to get deleted.
+  auto* p = alloc_policy_.get();
+  ArenaMetricsCollector* collector = p ? p->metrics_collector : nullptr;
+
+  if (alloc_policy_.is_user_owned_initial_block()) {
+#ifdef ADDRESS_SANITIZER
+    // Unpoison the initial block, now that it's going back to the user.
+    ASAN_UNPOISON_MEMORY_REGION(mem.ptr, mem.size);
+#endif  // ADDRESS_SANITIZER
+    space_allocated += mem.size;
+  } else {
+    GetDeallocator(alloc_policy_.get(), &space_allocated)(mem);
+  }
+
+  if (collector) collector->OnDestroy(space_allocated);
+}
+
+SerialArena::Memory ThreadSafeArena::Free(size_t* space_allocated) {
+  SerialArena::Memory mem = {nullptr, 0};
+  auto deallocator = GetDeallocator(alloc_policy_.get(), space_allocated);
+  PerSerialArena([deallocator, &mem](SerialArena* a) {
+    if (mem.ptr) deallocator(mem);
+    mem = a->Free(deallocator);
+  });
+  return mem;
+}
+
+uint64_t ThreadSafeArena::Reset() {
+  // Have to do this in a first pass, because some of the destructors might
+  // refer to memory in other blocks.
+  CleanupList();
+
+  // Discard all blocks except the special block (if present).
+  size_t space_allocated = 0;
+  auto mem = Free(&space_allocated);
+  arena_stats_.RecordReset();
+
+  AllocationPolicy* policy = alloc_policy_.get();
+  if (policy) {
+    auto saved_policy = *policy;
+    if (alloc_policy_.is_user_owned_initial_block()) {
+      space_allocated += mem.size;
+    } else {
+      GetDeallocator(alloc_policy_.get(), &space_allocated)(mem);
+      mem.ptr = nullptr;
+      mem.size = 0;
+    }
+    ArenaMetricsCollector* collector = saved_policy.metrics_collector;
+    if (collector) collector->OnReset(space_allocated);
+    InitializeWithPolicy(mem.ptr, mem.size, saved_policy);
+  } else {
+    GOOGLE_DCHECK(!alloc_policy_.should_record_allocs());
+    // Nullptr policy
+    if (alloc_policy_.is_user_owned_initial_block()) {
+      space_allocated += mem.size;
+      InitializeFrom(mem.ptr, mem.size);
+    } else {
+      GetDeallocator(alloc_policy_.get(), &space_allocated)(mem);
+      Init();
+    }
+  }
+
+  return space_allocated;
+}
+
+std::pair<void*, SerialArena::CleanupNode*>
+ThreadSafeArena::AllocateAlignedWithCleanup(size_t n,
+                                            const std::type_info* type) {
+  SerialArena* arena = nullptr;
+  if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
+                            GetSerialArenaFast(&arena))) {
+    return arena->AllocateAlignedWithCleanup(n, alloc_policy_.get());
+  } else {
+    return AllocateAlignedWithCleanupFallback(n, type);
+  }
+}
+
+void ThreadSafeArena::AddCleanup(void* elem, void (*cleanup)(void*)) {
+  SerialArena* arena = nullptr;
+  if (PROTOBUF_PREDICT_FALSE(!GetSerialArenaFast(&arena))) {
+    arena = GetSerialArenaFallback(&thread_cache());
+  }
+  arena->AddCleanup(elem, cleanup, AllocPolicy());
+}
+
+PROTOBUF_NOINLINE
+void* ThreadSafeArena::AllocateAlignedFallback(size_t n,
+                                               const std::type_info* type) {
+  if (alloc_policy_.should_record_allocs()) {
+    alloc_policy_.RecordAlloc(type, n);
+    SerialArena* arena = nullptr;
+    if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
+      return arena->AllocateAligned(n, alloc_policy_.get());
+    }
+  }
+  return GetSerialArenaFallback(&thread_cache())
+      ->AllocateAligned(n, alloc_policy_.get());
+}
+
+PROTOBUF_NOINLINE
+std::pair<void*, SerialArena::CleanupNode*>
+ThreadSafeArena::AllocateAlignedWithCleanupFallback(
+    size_t n, const std::type_info* type) {
+  if (alloc_policy_.should_record_allocs()) {
+    alloc_policy_.RecordAlloc(type, n);
+    SerialArena* arena;
+    if (GetSerialArenaFast(&arena)) {
+      return arena->AllocateAlignedWithCleanup(n, alloc_policy_.get());
+    }
+  }
+  return GetSerialArenaFallback(&thread_cache())
+      ->AllocateAlignedWithCleanup(n, alloc_policy_.get());
+}
+
+uint64_t ThreadSafeArena::SpaceAllocated() const {
+  SerialArena* serial = threads_.load(std::memory_order_acquire);
+  uint64_t res = 0;
+  for (; serial; serial = serial->next()) {
+    res += serial->SpaceAllocated();
+  }
+  return res;
+}
+
+uint64_t ThreadSafeArena::SpaceUsed() const {
+  SerialArena* serial = threads_.load(std::memory_order_acquire);
+  uint64_t space_used = 0;
+  for (; serial; serial = serial->next()) {
+    space_used += serial->SpaceUsed();
+  }
+  return space_used - (alloc_policy_.get() ? sizeof(AllocationPolicy) : 0);
+}
+
+void ThreadSafeArena::CleanupList() {
+  PerSerialArena([](SerialArena* a) { a->CleanupList(); });
+}
+
+PROTOBUF_NOINLINE
+SerialArena* ThreadSafeArena::GetSerialArenaFallback(void* me) {
+  // Look for this SerialArena in our linked list.
+  SerialArena* serial = threads_.load(std::memory_order_acquire);
+  for (; serial; serial = serial->next()) {
+    if (serial->owner() == me) {
+      break;
+    }
+  }
+
+  if (!serial) {
+    // This thread doesn't have any SerialArena, which also means it doesn't
+    // have any blocks yet.  So we'll allocate its first block now.
+    serial = SerialArena::New(
+        AllocateMemory(alloc_policy_.get(), 0, kSerialArenaSize), me,
+        arena_stats_.MutableStats());
+
+    SerialArena* head = threads_.load(std::memory_order_relaxed);
+    do {
+      serial->set_next(head);
+    } while (!threads_.compare_exchange_weak(
+        head, serial, std::memory_order_release, std::memory_order_relaxed));
+  }
+
+  CacheSerialArena(serial);
+  return serial;
+}
+
+}  // namespace internal
+
+PROTOBUF_FUNC_ALIGN(32)
+void* Arena::AllocateAlignedNoHook(size_t n) {
+  return impl_.AllocateAligned(n, nullptr);
+}
+
+PROTOBUF_FUNC_ALIGN(32)
+void* Arena::AllocateAlignedWithHook(size_t n, const std::type_info* type) {
+  return impl_.AllocateAligned(n, type);
+}
+
+PROTOBUF_FUNC_ALIGN(32)
+void* Arena::AllocateAlignedWithHookForArray(size_t n,
+                                             const std::type_info* type) {
+  return impl_.AllocateAligned<internal::AllocationClient::kArray>(n, type);
+}
+
+PROTOBUF_FUNC_ALIGN(32)
+std::pair<void*, internal::SerialArena::CleanupNode*>
+Arena::AllocateAlignedWithCleanup(size_t n, const std::type_info* type) {
+  return impl_.AllocateAlignedWithCleanup(n, type);
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/arenastring.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/arenastring.cpp
new file mode 100644
index 0000000..af0c9df
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/arenastring.cpp
@@ -0,0 +1,267 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/arenastring.h>
+
+#include <cstddef>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/stubs/mutex.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+namespace  {
+
+// TaggedStringPtr::Flags uses the lower 2 bits as tags.
+// Enforce that allocated data aligns to at least 4 bytes, and that
+// the alignment of the global const string value does as well.
+// The alignment guaranteed by `new std::string` depends on both:
+// - new align = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / max_align_t
+// - alignof(std::string)
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+constexpr size_t kNewAlign = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
+constexpr size_t kNewAlign = alignof(::max_align_t);
+#else
+constexpr size_t kNewAlign = alignof(std::max_align_t);
+#endif
+constexpr size_t kStringAlign = alignof(std::string);
+
+static_assert((kStringAlign > kNewAlign ? kStringAlign : kNewAlign) >= 4, "");
+static_assert(alignof(ExplicitlyConstructedArenaString) >= 4, "");
+
+}  // namespace
+
+const std::string& LazyString::Init() const {
+  static WrappedMutex mu{GOOGLE_PROTOBUF_LINKER_INITIALIZED};
+  mu.Lock();
+  const std::string* res = inited_.load(std::memory_order_acquire);
+  if (res == nullptr) {
+    auto init_value = init_value_;
+    res = ::new (static_cast<void*>(string_buf_))
+        std::string(init_value.ptr, init_value.size);
+    inited_.store(res, std::memory_order_release);
+  }
+  mu.Unlock();
+  return *res;
+}
+
+namespace {
+
+
+#if defined(NDEBUG) || !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
+
+class ScopedCheckPtrInvariants {
+ public:
+  explicit ScopedCheckPtrInvariants(const TaggedStringPtr*) {}
+};
+
+#endif  // NDEBUG || !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
+
+// Creates a heap allocated std::string value.
+inline TaggedStringPtr CreateString(ConstStringParam value) {
+  TaggedStringPtr res;
+  res.SetAllocated(new std::string(value.data(), value.length()));
+  return res;
+}
+
+#if !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
+
+// Creates an arena allocated std::string value.
+TaggedStringPtr CreateArenaString(Arena& arena, ConstStringParam s) {
+  TaggedStringPtr res;
+  res.SetMutableArena(Arena::Create<std::string>(&arena, s.data(), s.length()));
+  return res;
+}
+
+#endif  // !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
+
+}  // namespace
+
+void ArenaStringPtr::Set(ConstStringParam value, Arena* arena) {
+  ScopedCheckPtrInvariants check(&tagged_ptr_);
+  if (IsDefault()) {
+    // If we're not on an arena, skip straight to a true string to avoid
+    // possible copy cost later.
+    tagged_ptr_ = arena != nullptr ? CreateArenaString(*arena, value)
+                                   : CreateString(value);
+  } else {
+    UnsafeMutablePointer()->assign(value.data(), value.length());
+  }
+}
+
+void ArenaStringPtr::Set(std::string&& value, Arena* arena) {
+  ScopedCheckPtrInvariants check(&tagged_ptr_);
+  if (IsDefault()) {
+    NewString(arena, std::move(value));
+  } else if (IsFixedSizeArena()) {
+    std::string* current = tagged_ptr_.Get();
+    auto* s = new (current) std::string(std::move(value));
+    arena->OwnDestructor(s);
+    tagged_ptr_.SetMutableArena(s);
+  } else /* !IsFixedSizeArena() */ {
+    *UnsafeMutablePointer() = std::move(value);
+  }
+}
+
+std::string* ArenaStringPtr::Mutable(Arena* arena) {
+  ScopedCheckPtrInvariants check(&tagged_ptr_);
+  if (tagged_ptr_.IsMutable()) {
+    return tagged_ptr_.Get();
+  } else {
+    return MutableSlow(arena);
+  }
+}
+
+std::string* ArenaStringPtr::Mutable(const LazyString& default_value,
+                                     Arena* arena) {
+  ScopedCheckPtrInvariants check(&tagged_ptr_);
+  if (tagged_ptr_.IsMutable()) {
+    return tagged_ptr_.Get();
+  } else {
+    return MutableSlow(arena, default_value);
+  }
+}
+
+std::string* ArenaStringPtr::MutableNoCopy(Arena* arena) {
+  ScopedCheckPtrInvariants check(&tagged_ptr_);
+  if (tagged_ptr_.IsMutable()) {
+    return tagged_ptr_.Get();
+  } else {
+    GOOGLE_DCHECK(IsDefault());
+    // Allocate empty. The contents are not relevant.
+    return NewString(arena);
+  }
+}
+
+template <typename... Lazy>
+std::string* ArenaStringPtr::MutableSlow(::google::protobuf::Arena* arena,
+                                         const Lazy&... lazy_default) {
+  GOOGLE_DCHECK(IsDefault());
+
+  // For empty defaults, this ends up calling the default constructor which is
+  // more efficient than a copy construction from
+  // GetEmptyStringAlreadyInited().
+  return NewString(arena, lazy_default.get()...);
+}
+
+std::string* ArenaStringPtr::Release() {
+  ScopedCheckPtrInvariants check(&tagged_ptr_);
+  if (IsDefault()) return nullptr;
+
+  std::string* released = tagged_ptr_.Get();
+  if (tagged_ptr_.IsArena()) {
+    released = tagged_ptr_.IsMutable() ? new std::string(std::move(*released))
+                                       : new std::string(*released);
+  }
+  InitDefault();
+  return released;
+}
+
+void ArenaStringPtr::SetAllocated(std::string* value, Arena* arena) {
+  ScopedCheckPtrInvariants check(&tagged_ptr_);
+  // Release what we have first.
+  Destroy();
+
+  if (value == nullptr) {
+    InitDefault();
+  } else {
+#ifndef NDEBUG
+    // On debug builds, copy the string so the address differs.  delete will
+    // fail if value was a stack-allocated temporary/etc., which would have
+    // failed when arena ran its cleanup list.
+    std::string* new_value = new std::string(std::move(*value));
+    delete value;
+    value = new_value;
+#endif  // !NDEBUG
+    InitAllocated(value, arena);
+  }
+}
+
+void ArenaStringPtr::Destroy() {
+  delete tagged_ptr_.GetIfAllocated();
+}
+
+void ArenaStringPtr::ClearToEmpty() {
+  ScopedCheckPtrInvariants check(&tagged_ptr_);
+  if (IsDefault()) {
+    // Already set to default -- do nothing.
+  } else {
+    // Unconditionally mask away the tag.
+    //
+    // UpdateArenaString uses assign when capacity is larger than the new
+    // value, which is trivially true in the donated string case.
+    // const_cast<std::string*>(PtrValue<std::string>())->clear();
+    tagged_ptr_.Get()->clear();
+  }
+}
+
+void ArenaStringPtr::ClearToDefault(const LazyString& default_value,
+                                    ::google::protobuf::Arena* arena) {
+  ScopedCheckPtrInvariants check(&tagged_ptr_);
+  (void)arena;
+  if (IsDefault()) {
+    // Already set to default -- do nothing.
+  } else {
+    UnsafeMutablePointer()->assign(default_value.get());
+  }
+}
+
+const char* EpsCopyInputStream::ReadArenaString(const char* ptr,
+                                                ArenaStringPtr* s,
+                                                Arena* arena) {
+  ScopedCheckPtrInvariants check(&s->tagged_ptr_);
+  GOOGLE_DCHECK(arena != nullptr);
+
+  int size = ReadSize(&ptr);
+  if (!ptr) return nullptr;
+
+  auto* str = s->NewString(arena);
+  ptr = ReadString(ptr, size, str);
+  GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+  return ptr;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/arenaz_sampler.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/arenaz_sampler.cpp
new file mode 100644
index 0000000..0eac693
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/arenaz_sampler.cpp
@@ -0,0 +1,177 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/arenaz_sampler.h>
+
+#include <atomic>
+#include <cstdint>
+#include <limits>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+ThreadSafeArenazSampler& GlobalThreadSafeArenazSampler() {
+  static auto* sampler = new ThreadSafeArenazSampler();
+  return *sampler;
+}
+
+void UnsampleSlow(ThreadSafeArenaStats* info) {
+  GlobalThreadSafeArenazSampler().Unregister(info);
+}
+
+#if defined(PROTOBUF_ARENAZ_SAMPLE)
+namespace {
+
+PROTOBUF_CONSTINIT std::atomic<bool> g_arenaz_enabled{true};
+PROTOBUF_CONSTINIT std::atomic<int32_t> g_arenaz_sample_parameter{1 << 10};
+PROTOBUF_THREAD_LOCAL absl::profiling_internal::ExponentialBiased
+    g_exponential_biased_generator;
+
+}  // namespace
+
+PROTOBUF_THREAD_LOCAL int64_t global_next_sample = 1LL << 10;
+
+ThreadSafeArenaStats::ThreadSafeArenaStats() { PrepareForSampling(); }
+ThreadSafeArenaStats::~ThreadSafeArenaStats() = default;
+
+void ThreadSafeArenaStats::PrepareForSampling() {
+  num_allocations.store(0, std::memory_order_relaxed);
+  num_resets.store(0, std::memory_order_relaxed);
+  bytes_requested.store(0, std::memory_order_relaxed);
+  bytes_allocated.store(0, std::memory_order_relaxed);
+  bytes_wasted.store(0, std::memory_order_relaxed);
+  max_bytes_allocated.store(0, std::memory_order_relaxed);
+  thread_ids.store(0, std::memory_order_relaxed);
+  // The inliner makes hardcoded skip_count difficult (especially when combined
+  // with LTO).  We use the ability to exclude stacks by regex when encoding
+  // instead.
+  depth = absl::GetStackTrace(stack, kMaxStackDepth, /* skip_count= */ 0);
+}
+
+void RecordResetSlow(ThreadSafeArenaStats* info) {
+  const size_t max_bytes =
+      info->max_bytes_allocated.load(std::memory_order_relaxed);
+  const size_t allocated_bytes =
+      info->bytes_allocated.load(std::memory_order_relaxed);
+  if (max_bytes < allocated_bytes) {
+    info->max_bytes_allocated.store(allocated_bytes);
+  }
+  info->bytes_requested.store(0, std::memory_order_relaxed);
+  info->bytes_allocated.store(0, std::memory_order_relaxed);
+  info->bytes_wasted.fetch_add(0, std::memory_order_relaxed);
+  info->num_allocations.fetch_add(0, std::memory_order_relaxed);
+  info->num_resets.fetch_add(1, std::memory_order_relaxed);
+}
+
+void RecordAllocateSlow(ThreadSafeArenaStats* info, size_t requested,
+                        size_t allocated, size_t wasted) {
+  info->bytes_requested.fetch_add(requested, std::memory_order_relaxed);
+  info->bytes_allocated.fetch_add(allocated, std::memory_order_relaxed);
+  info->bytes_wasted.fetch_add(wasted, std::memory_order_relaxed);
+  info->num_allocations.fetch_add(1, std::memory_order_relaxed);
+  const uint64_t tid = (1ULL << (GetCachedTID() % 63));
+  const uint64_t thread_ids = info->thread_ids.load(std::memory_order_relaxed);
+  if (!(thread_ids & tid)) {
+    info->thread_ids.store(thread_ids | tid, std::memory_order_relaxed);
+  }
+}
+
+ThreadSafeArenaStats* SampleSlow(int64_t* next_sample) {
+  bool first = *next_sample < 0;
+  *next_sample = g_exponential_biased_generator.GetStride(
+      g_arenaz_sample_parameter.load(std::memory_order_relaxed));
+  // Small values of interval are equivalent to just sampling next time.
+  ABSL_ASSERT(*next_sample >= 1);
+
+  // g_arenaz_enabled can be dynamically flipped, we need to set a threshold low
+  // enough that we will start sampling in a reasonable time, so we just use the
+  // default sampling rate.
+  if (!g_arenaz_enabled.load(std::memory_order_relaxed)) return nullptr;
+  // We will only be negative on our first count, so we should just retry in
+  // that case.
+  if (first) {
+    if (PROTOBUF_PREDICT_TRUE(--*next_sample > 0)) return nullptr;
+    return SampleSlow(next_sample);
+  }
+
+  return GlobalThreadSafeArenazSampler().Register();
+}
+
+void SetThreadSafeArenazEnabled(bool enabled) {
+  g_arenaz_enabled.store(enabled, std::memory_order_release);
+}
+
+void SetThreadSafeArenazSampleParameter(int32_t rate) {
+  if (rate > 0) {
+    g_arenaz_sample_parameter.store(rate, std::memory_order_release);
+  } else {
+    ABSL_RAW_LOG(ERROR, "Invalid thread safe arenaz sample rate: %lld",
+                 static_cast<long long>(rate));  // NOLINT(runtime/int)
+  }
+}
+
+void SetThreadSafeArenazMaxSamples(int32_t max) {
+  if (max > 0) {
+    GlobalThreadSafeArenazSampler().SetMaxSamples(max);
+  } else {
+    ABSL_RAW_LOG(ERROR, "Invalid thread safe arenaz max samples: %lld",
+                 static_cast<long long>(max));  // NOLINT(runtime/int)
+  }
+}
+
+void SetThreadSafeArenazGlobalNextSample(int64_t next_sample) {
+  if (next_sample >= 0) {
+    global_next_sample = next_sample;
+  } else {
+    ABSL_RAW_LOG(ERROR, "Invalid thread safe arenaz next sample: %lld",
+                 static_cast<long long>(next_sample));  // NOLINT(runtime/int)
+  }
+}
+
+#else
+ThreadSafeArenaStats* SampleSlow(int64_t* next_sample) {
+  *next_sample = std::numeric_limits<int64_t>::max();
+  return nullptr;
+}
+
+void SetThreadSafeArenazEnabled(bool enabled) {}
+void SetThreadSafeArenazSampleParameter(int32_t rate) {}
+void SetThreadSafeArenazMaxSamples(int32_t max) {}
+void SetThreadSafeArenazGlobalNextSample(int64_t next_sample) {}
+#endif  // defined(PROTOBUF_ARENAZ_SAMPLE)
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/compiler/importer.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/compiler/importer.cpp
new file mode 100644
index 0000000..678e87e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/compiler/importer.cpp
@@ -0,0 +1,524 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#ifdef _MSC_VER
+#include <direct.h>
+#else
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <google/protobuf/compiler/importer.h>
+#include <google/protobuf/compiler/parser.h>
+#include <google/protobuf/io/tokenizer.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/io/io_win32.h>
+
+#ifdef _WIN32
+#include <ctype.h>
+#endif
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+
+#ifdef _WIN32
+// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
+// them like we do below.
+using google::protobuf::io::win32::access;
+using google::protobuf::io::win32::open;
+#endif
+
+// Returns true if the text looks like a Windows-style absolute path, starting
+// with a drive letter.  Example:  "C:\foo".  TODO(kenton):  Share this with
+// copy in command_line_interface.cc?
+static bool IsWindowsAbsolutePath(const std::string& text) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+  return text.size() >= 3 && text[1] == ':' && isalpha(text[0]) &&
+         (text[2] == '/' || text[2] == '\\') && text.find_last_of(':') == 1;
+#else
+  return false;
+#endif
+}
+
+MultiFileErrorCollector::~MultiFileErrorCollector() {}
+
+// This class serves two purposes:
+// - It implements the ErrorCollector interface (used by Tokenizer and Parser)
+//   in terms of MultiFileErrorCollector, using a particular filename.
+// - It lets us check if any errors have occurred.
+class SourceTreeDescriptorDatabase::SingleFileErrorCollector
+    : public io::ErrorCollector {
+ public:
+  SingleFileErrorCollector(const std::string& filename,
+                           MultiFileErrorCollector* multi_file_error_collector)
+      : filename_(filename),
+        multi_file_error_collector_(multi_file_error_collector),
+        had_errors_(false) {}
+  ~SingleFileErrorCollector() override {}
+
+  bool had_errors() { return had_errors_; }
+
+  // implements ErrorCollector ---------------------------------------
+  void AddError(int line, int column, const std::string& message) override {
+    if (multi_file_error_collector_ != nullptr) {
+      multi_file_error_collector_->AddError(filename_, line, column, message);
+    }
+    had_errors_ = true;
+  }
+
+ private:
+  std::string filename_;
+  MultiFileErrorCollector* multi_file_error_collector_;
+  bool had_errors_;
+};
+
+// ===================================================================
+
+SourceTreeDescriptorDatabase::SourceTreeDescriptorDatabase(
+    SourceTree* source_tree)
+    : source_tree_(source_tree),
+      fallback_database_(nullptr),
+      error_collector_(nullptr),
+      using_validation_error_collector_(false),
+      validation_error_collector_(this) {}
+
+SourceTreeDescriptorDatabase::SourceTreeDescriptorDatabase(
+    SourceTree* source_tree, DescriptorDatabase* fallback_database)
+    : source_tree_(source_tree),
+      fallback_database_(fallback_database),
+      error_collector_(nullptr),
+      using_validation_error_collector_(false),
+      validation_error_collector_(this) {}
+
+SourceTreeDescriptorDatabase::~SourceTreeDescriptorDatabase() {}
+
+bool SourceTreeDescriptorDatabase::FindFileByName(const std::string& filename,
+                                                  FileDescriptorProto* output) {
+  std::unique_ptr<io::ZeroCopyInputStream> input(source_tree_->Open(filename));
+  if (input == nullptr) {
+    if (fallback_database_ != nullptr &&
+        fallback_database_->FindFileByName(filename, output)) {
+      return true;
+    }
+    if (error_collector_ != nullptr) {
+      error_collector_->AddError(filename, -1, 0,
+                                 source_tree_->GetLastErrorMessage());
+    }
+    return false;
+  }
+
+  // Set up the tokenizer and parser.
+  SingleFileErrorCollector file_error_collector(filename, error_collector_);
+  io::Tokenizer tokenizer(input.get(), &file_error_collector);
+
+  Parser parser;
+  if (error_collector_ != nullptr) {
+    parser.RecordErrorsTo(&file_error_collector);
+  }
+  if (using_validation_error_collector_) {
+    parser.RecordSourceLocationsTo(&source_locations_);
+  }
+
+  // Parse it.
+  output->set_name(filename);
+  return parser.Parse(&tokenizer, output) && !file_error_collector.had_errors();
+}
+
+bool SourceTreeDescriptorDatabase::FindFileContainingSymbol(
+    const std::string& symbol_name, FileDescriptorProto* output) {
+  return false;
+}
+
+bool SourceTreeDescriptorDatabase::FindFileContainingExtension(
+    const std::string& containing_type, int field_number,
+    FileDescriptorProto* output) {
+  return false;
+}
+
+// -------------------------------------------------------------------
+
+SourceTreeDescriptorDatabase::ValidationErrorCollector::
+    ValidationErrorCollector(SourceTreeDescriptorDatabase* owner)
+    : owner_(owner) {}
+
+SourceTreeDescriptorDatabase::ValidationErrorCollector::
+    ~ValidationErrorCollector() {}
+
+void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddError(
+    const std::string& filename, const std::string& element_name,
+    const Message* descriptor, ErrorLocation location,
+    const std::string& message) {
+  if (owner_->error_collector_ == nullptr) return;
+
+  int line, column;
+  if (location == DescriptorPool::ErrorCollector::IMPORT) {
+    owner_->source_locations_.FindImport(descriptor, element_name, &line,
+                                         &column);
+  } else {
+    owner_->source_locations_.Find(descriptor, location, &line, &column);
+  }
+  owner_->error_collector_->AddError(filename, line, column, message);
+}
+
+void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddWarning(
+    const std::string& filename, const std::string& element_name,
+    const Message* descriptor, ErrorLocation location,
+    const std::string& message) {
+  if (owner_->error_collector_ == nullptr) return;
+
+  int line, column;
+  if (location == DescriptorPool::ErrorCollector::IMPORT) {
+    owner_->source_locations_.FindImport(descriptor, element_name, &line,
+                                         &column);
+  } else {
+    owner_->source_locations_.Find(descriptor, location, &line, &column);
+  }
+  owner_->error_collector_->AddWarning(filename, line, column, message);
+}
+
+// ===================================================================
+
+Importer::Importer(SourceTree* source_tree,
+                   MultiFileErrorCollector* error_collector)
+    : database_(source_tree),
+      pool_(&database_, database_.GetValidationErrorCollector()) {
+  pool_.EnforceWeakDependencies(true);
+  database_.RecordErrorsTo(error_collector);
+}
+
+Importer::~Importer() {}
+
+const FileDescriptor* Importer::Import(const std::string& filename) {
+  return pool_.FindFileByName(filename);
+}
+
+void Importer::AddUnusedImportTrackFile(const std::string& file_name,
+                                        bool is_error) {
+  pool_.AddUnusedImportTrackFile(file_name, is_error);
+}
+
+void Importer::ClearUnusedImportTrackFiles() {
+  pool_.ClearUnusedImportTrackFiles();
+}
+
+
+// ===================================================================
+
+SourceTree::~SourceTree() {}
+
+std::string SourceTree::GetLastErrorMessage() { return "File not found."; }
+
+DiskSourceTree::DiskSourceTree() {}
+
+DiskSourceTree::~DiskSourceTree() {}
+
+static inline char LastChar(const std::string& str) {
+  return str[str.size() - 1];
+}
+
+// Given a path, returns an equivalent path with these changes:
+// - On Windows, any backslashes are replaced with forward slashes.
+// - Any instances of the directory "." are removed.
+// - Any consecutive '/'s are collapsed into a single slash.
+// Note that the resulting string may be empty.
+//
+// TODO(kenton):  It would be nice to handle "..", e.g. so that we can figure
+//   out that "foo/bar.proto" is inside "baz/../foo".  However, if baz is a
+//   symlink or doesn't exist, then things get complicated, and we can't
+//   actually determine this without investigating the filesystem, probably
+//   in non-portable ways.  So, we punt.
+//
+// TODO(kenton):  It would be nice to use realpath() here except that it
+//   resolves symbolic links.  This could cause problems if people place
+//   symbolic links in their source tree.  For example, if you executed:
+//     protoc --proto_path=foo foo/bar/baz.proto
+//   then if foo/bar is a symbolic link, foo/bar/baz.proto will canonicalize
+//   to a path which does not appear to be under foo, and thus the compiler
+//   will complain that baz.proto is not inside the --proto_path.
+static std::string CanonicalizePath(std::string path) {
+#ifdef _WIN32
+  // The Win32 API accepts forward slashes as a path delimiter even though
+  // backslashes are standard.  Let's avoid confusion and use only forward
+  // slashes.
+  if (HasPrefixString(path, "\\\\")) {
+    // Avoid converting two leading backslashes.
+    path = "\\\\" + StringReplace(path.substr(2), "\\", "/", true);
+  } else {
+    path = StringReplace(path, "\\", "/", true);
+  }
+#endif
+
+  std::vector<std::string> canonical_parts;
+  std::vector<std::string> parts = Split(
+      path, "/", true);  // Note:  Removes empty parts.
+  for (const std::string& part : parts) {
+    if (part == ".") {
+      // Ignore.
+    } else {
+      canonical_parts.push_back(part);
+    }
+  }
+  std::string result = Join(canonical_parts, "/");
+  if (!path.empty() && path[0] == '/') {
+    // Restore leading slash.
+    result = '/' + result;
+  }
+  if (!path.empty() && LastChar(path) == '/' && !result.empty() &&
+      LastChar(result) != '/') {
+    // Restore trailing slash.
+    result += '/';
+  }
+  return result;
+}
+
+static inline bool ContainsParentReference(const std::string& path) {
+  return path == ".." || HasPrefixString(path, "../") ||
+         HasSuffixString(path, "/..") || path.find("/../") != std::string::npos;
+}
+
+// Maps a file from an old location to a new one.  Typically, old_prefix is
+// a virtual path and new_prefix is its corresponding disk path.  Returns
+// false if the filename did not start with old_prefix, otherwise replaces
+// old_prefix with new_prefix and stores the result in *result.  Examples:
+//   string result;
+//   assert(ApplyMapping("foo/bar", "", "baz", &result));
+//   assert(result == "baz/foo/bar");
+//
+//   assert(ApplyMapping("foo/bar", "foo", "baz", &result));
+//   assert(result == "baz/bar");
+//
+//   assert(ApplyMapping("foo", "foo", "bar", &result));
+//   assert(result == "bar");
+//
+//   assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
+//   assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
+//   assert(!ApplyMapping("foobar", "foo", "baz", &result));
+static bool ApplyMapping(const std::string& filename,
+                         const std::string& old_prefix,
+                         const std::string& new_prefix, std::string* result) {
+  if (old_prefix.empty()) {
+    // old_prefix matches any relative path.
+    if (ContainsParentReference(filename)) {
+      // We do not allow the file name to use "..".
+      return false;
+    }
+    if (HasPrefixString(filename, "/") || IsWindowsAbsolutePath(filename)) {
+      // This is an absolute path, so it isn't matched by the empty string.
+      return false;
+    }
+    result->assign(new_prefix);
+    if (!result->empty()) result->push_back('/');
+    result->append(filename);
+    return true;
+  } else if (HasPrefixString(filename, old_prefix)) {
+    // old_prefix is a prefix of the filename.  Is it the whole filename?
+    if (filename.size() == old_prefix.size()) {
+      // Yep, it's an exact match.
+      *result = new_prefix;
+      return true;
+    } else {
+      // Not an exact match.  Is the next character a '/'?  Otherwise,
+      // this isn't actually a match at all.  E.g. the prefix "foo/bar"
+      // does not match the filename "foo/barbaz".
+      int after_prefix_start = -1;
+      if (filename[old_prefix.size()] == '/') {
+        after_prefix_start = old_prefix.size() + 1;
+      } else if (filename[old_prefix.size() - 1] == '/') {
+        // old_prefix is never empty, and canonicalized paths never have
+        // consecutive '/' characters.
+        after_prefix_start = old_prefix.size();
+      }
+      if (after_prefix_start != -1) {
+        // Yep.  So the prefixes are directories and the filename is a file
+        // inside them.
+        std::string after_prefix = filename.substr(after_prefix_start);
+        if (ContainsParentReference(after_prefix)) {
+          // We do not allow the file name to use "..".
+          return false;
+        }
+        result->assign(new_prefix);
+        if (!result->empty()) result->push_back('/');
+        result->append(after_prefix);
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+void DiskSourceTree::MapPath(const std::string& virtual_path,
+                             const std::string& disk_path) {
+  mappings_.push_back(Mapping(virtual_path, CanonicalizePath(disk_path)));
+}
+
+DiskSourceTree::DiskFileToVirtualFileResult
+DiskSourceTree::DiskFileToVirtualFile(const std::string& disk_file,
+                                      std::string* virtual_file,
+                                      std::string* shadowing_disk_file) {
+  int mapping_index = -1;
+  std::string canonical_disk_file = CanonicalizePath(disk_file);
+
+  for (size_t i = 0; i < mappings_.size(); i++) {
+    // Apply the mapping in reverse.
+    if (ApplyMapping(canonical_disk_file, mappings_[i].disk_path,
+                     mappings_[i].virtual_path, virtual_file)) {
+      // Success.
+      mapping_index = i;
+      break;
+    }
+  }
+
+  if (mapping_index == -1) {
+    return NO_MAPPING;
+  }
+
+  // Iterate through all mappings with higher precedence and verify that none
+  // of them map this file to some other existing file.
+  for (int i = 0; i < mapping_index; i++) {
+    if (ApplyMapping(*virtual_file, mappings_[i].virtual_path,
+                     mappings_[i].disk_path, shadowing_disk_file)) {
+      if (access(shadowing_disk_file->c_str(), F_OK) >= 0) {
+        // File exists.
+        return SHADOWED;
+      }
+    }
+  }
+  shadowing_disk_file->clear();
+
+  // Verify that we can open the file.  Note that this also has the side-effect
+  // of verifying that we are not canonicalizing away any non-existent
+  // directories.
+  std::unique_ptr<io::ZeroCopyInputStream> stream(OpenDiskFile(disk_file));
+  if (stream == nullptr) {
+    return CANNOT_OPEN;
+  }
+
+  return SUCCESS;
+}
+
+bool DiskSourceTree::VirtualFileToDiskFile(const std::string& virtual_file,
+                                           std::string* disk_file) {
+  std::unique_ptr<io::ZeroCopyInputStream> stream(
+      OpenVirtualFile(virtual_file, disk_file));
+  return stream != nullptr;
+}
+
+io::ZeroCopyInputStream* DiskSourceTree::Open(const std::string& filename) {
+  return OpenVirtualFile(filename, nullptr);
+}
+
+std::string DiskSourceTree::GetLastErrorMessage() {
+  return last_error_message_;
+}
+
+io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
+    const std::string& virtual_file, std::string* disk_file) {
+  if (virtual_file != CanonicalizePath(virtual_file) ||
+      ContainsParentReference(virtual_file)) {
+    // We do not allow importing of paths containing things like ".." or
+    // consecutive slashes since the compiler expects files to be uniquely
+    // identified by file name.
+    last_error_message_ =
+        "Backslashes, consecutive slashes, \".\", or \"..\" "
+        "are not allowed in the virtual path";
+    return nullptr;
+  }
+
+  for (const auto& mapping : mappings_) {
+    std::string temp_disk_file;
+    if (ApplyMapping(virtual_file, mapping.virtual_path, mapping.disk_path,
+                     &temp_disk_file)) {
+      io::ZeroCopyInputStream* stream = OpenDiskFile(temp_disk_file);
+      if (stream != nullptr) {
+        if (disk_file != nullptr) {
+          *disk_file = temp_disk_file;
+        }
+        return stream;
+      }
+
+      if (errno == EACCES) {
+        // The file exists but is not readable.
+        last_error_message_ =
+            "Read access is denied for file: " + temp_disk_file;
+        return nullptr;
+      }
+    }
+  }
+  last_error_message_ = "File not found.";
+  return nullptr;
+}
+
+io::ZeroCopyInputStream* DiskSourceTree::OpenDiskFile(
+    const std::string& filename) {
+  struct stat sb;
+  int ret = 0;
+  do {
+    ret = stat(filename.c_str(), &sb);
+  } while (ret != 0 && errno == EINTR);
+#if defined(_WIN32)
+  if (ret == 0 && sb.st_mode & S_IFDIR) {
+    last_error_message_ = "Input file is a directory.";
+    return nullptr;
+  }
+#else
+  if (ret == 0 && S_ISDIR(sb.st_mode)) {
+    last_error_message_ = "Input file is a directory.";
+    return nullptr;
+  }
+#endif
+  int file_descriptor;
+  do {
+    file_descriptor = open(filename.c_str(), O_RDONLY);
+  } while (file_descriptor < 0 && errno == EINTR);
+  if (file_descriptor >= 0) {
+    io::FileInputStream* result = new io::FileInputStream(file_descriptor);
+    result->SetCloseOnDelete(true);
+    return result;
+  } else {
+    return nullptr;
+  }
+}
+
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/compiler/parser.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/compiler/parser.cpp
new file mode 100644
index 0000000..e36a4a7
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/compiler/parser.cpp
@@ -0,0 +1,2446 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Recursive descent FTW.
+
+#include <google/protobuf/compiler/parser.h>
+
+#include <float.h>
+
+#include <cstdint>
+#include <limits>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/io/tokenizer.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/hash.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+
+using internal::WireFormat;
+
+namespace {
+
+typedef std::unordered_map<std::string, FieldDescriptorProto::Type> TypeNameMap;
+
+const TypeNameMap& GetTypeNameTable() {
+  static auto* table = new auto([]() {
+    TypeNameMap result;
+
+    result["double"] = FieldDescriptorProto::TYPE_DOUBLE;
+    result["float"] = FieldDescriptorProto::TYPE_FLOAT;
+    result["uint64"] = FieldDescriptorProto::TYPE_UINT64;
+    result["fixed64"] = FieldDescriptorProto::TYPE_FIXED64;
+    result["fixed32"] = FieldDescriptorProto::TYPE_FIXED32;
+    result["bool"] = FieldDescriptorProto::TYPE_BOOL;
+    result["string"] = FieldDescriptorProto::TYPE_STRING;
+    result["group"] = FieldDescriptorProto::TYPE_GROUP;
+
+    result["bytes"] = FieldDescriptorProto::TYPE_BYTES;
+    result["uint32"] = FieldDescriptorProto::TYPE_UINT32;
+    result["sfixed32"] = FieldDescriptorProto::TYPE_SFIXED32;
+    result["sfixed64"] = FieldDescriptorProto::TYPE_SFIXED64;
+    result["int32"] = FieldDescriptorProto::TYPE_INT32;
+    result["int64"] = FieldDescriptorProto::TYPE_INT64;
+    result["sint32"] = FieldDescriptorProto::TYPE_SINT32;
+    result["sint64"] = FieldDescriptorProto::TYPE_SINT64;
+
+    return result;
+  }());
+  return *table;
+}
+
+// Camel-case the field name and append "Entry" for generated map entry name.
+// e.g. map<KeyType, ValueType> foo_map => FooMapEntry
+std::string MapEntryName(const std::string& field_name) {
+  std::string result;
+  static const char kSuffix[] = "Entry";
+  result.reserve(field_name.size() + sizeof(kSuffix));
+  bool cap_next = true;
+  for (const char field_name_char : field_name) {
+    if (field_name_char == '_') {
+      cap_next = true;
+    } else if (cap_next) {
+      // Note: Do not use ctype.h due to locales.
+      if ('a' <= field_name_char && field_name_char <= 'z') {
+        result.push_back(field_name_char - 'a' + 'A');
+      } else {
+        result.push_back(field_name_char);
+      }
+      cap_next = false;
+    } else {
+      result.push_back(field_name_char);
+    }
+  }
+  result.append(kSuffix);
+  return result;
+}
+
+bool IsUppercase(char c) { return c >= 'A' && c <= 'Z'; }
+
+bool IsLowercase(char c) { return c >= 'a' && c <= 'z'; }
+
+bool IsNumber(char c) { return c >= '0' && c <= '9'; }
+
+bool IsUpperCamelCase(const std::string& name) {
+  if (name.empty()) {
+    return true;
+  }
+  // Name must start with an upper case character.
+  if (!IsUppercase(name[0])) {
+    return false;
+  }
+  // Must not contains underscore.
+  for (const char c : name) {
+    if (c == '_') {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool IsUpperUnderscore(const std::string& name) {
+  for (const char c : name) {
+    if (!IsUppercase(c) && c != '_' && !IsNumber(c)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool IsLowerUnderscore(const std::string& name) {
+  for (const char c : name) {
+    if (!IsLowercase(c) && c != '_' && !IsNumber(c)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool IsNumberFollowUnderscore(const std::string& name) {
+  for (size_t i = 1; i < name.length(); i++) {
+    const char c = name[i];
+    if (IsNumber(c) && name[i - 1] == '_') {
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // anonymous namespace
+
+// Makes code slightly more readable.  The meaning of "DO(foo)" is
+// "Execute foo and fail if it fails.", where failure is indicated by
+// returning false.
+#define DO(STATEMENT) \
+  if (STATEMENT) {    \
+  } else              \
+    return false
+
+// ===================================================================
+
+Parser::Parser()
+    : input_(nullptr),
+      error_collector_(nullptr),
+      source_location_table_(nullptr),
+      had_errors_(false),
+      require_syntax_identifier_(false),
+      stop_after_syntax_identifier_(false) {
+}
+
+Parser::~Parser() {}
+
+// ===================================================================
+
+inline bool Parser::LookingAt(const char* text) {
+  return input_->current().text == text;
+}
+
+inline bool Parser::LookingAtType(io::Tokenizer::TokenType token_type) {
+  return input_->current().type == token_type;
+}
+
+inline bool Parser::AtEnd() { return LookingAtType(io::Tokenizer::TYPE_END); }
+
+bool Parser::TryConsume(const char* text) {
+  if (LookingAt(text)) {
+    input_->Next();
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool Parser::Consume(const char* text, const char* error) {
+  if (TryConsume(text)) {
+    return true;
+  } else {
+    AddError(error);
+    return false;
+  }
+}
+
+bool Parser::Consume(const char* text) {
+  std::string error = "Expected \"" + std::string(text) + "\".";
+  return Consume(text, error.c_str());
+}
+
+bool Parser::ConsumeIdentifier(std::string* output, const char* error) {
+  if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
+    *output = input_->current().text;
+    input_->Next();
+    return true;
+  } else {
+    AddError(error);
+    return false;
+  }
+}
+
+bool Parser::ConsumeInteger(int* output, const char* error) {
+  if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
+    uint64_t value = 0;
+    if (!io::Tokenizer::ParseInteger(input_->current().text,
+                                     std::numeric_limits<int32_t>::max(),
+                                     &value)) {
+      AddError("Integer out of range.");
+      // We still return true because we did, in fact, parse an integer.
+    }
+    *output = value;
+    input_->Next();
+    return true;
+  } else {
+    AddError(error);
+    return false;
+  }
+}
+
+bool Parser::ConsumeSignedInteger(int* output, const char* error) {
+  bool is_negative = false;
+  uint64_t max_value = std::numeric_limits<int32_t>::max();
+  if (TryConsume("-")) {
+    is_negative = true;
+    max_value += 1;
+  }
+  uint64_t value = 0;
+  DO(ConsumeInteger64(max_value, &value, error));
+  if (is_negative) value *= -1;
+  *output = value;
+  return true;
+}
+
+bool Parser::ConsumeInteger64(uint64_t max_value, uint64_t* output,
+                              const char* error) {
+  if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
+    if (!io::Tokenizer::ParseInteger(input_->current().text, max_value,
+                                     output)) {
+      AddError("Integer out of range.");
+      // We still return true because we did, in fact, parse an integer.
+      *output = 0;
+    }
+    input_->Next();
+    return true;
+  } else {
+    AddError(error);
+    return false;
+  }
+}
+
+bool Parser::ConsumeNumber(double* output, const char* error) {
+  if (LookingAtType(io::Tokenizer::TYPE_FLOAT)) {
+    *output = io::Tokenizer::ParseFloat(input_->current().text);
+    input_->Next();
+    return true;
+  } else if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
+    // Also accept integers.
+    uint64_t value = 0;
+    if (!io::Tokenizer::ParseInteger(input_->current().text,
+                                     std::numeric_limits<uint64_t>::max(),
+                                     &value)) {
+      AddError("Integer out of range.");
+      // We still return true because we did, in fact, parse a number.
+    }
+    *output = value;
+    input_->Next();
+    return true;
+  } else if (LookingAt("inf")) {
+    *output = std::numeric_limits<double>::infinity();
+    input_->Next();
+    return true;
+  } else if (LookingAt("nan")) {
+    *output = std::numeric_limits<double>::quiet_NaN();
+    input_->Next();
+    return true;
+  } else {
+    AddError(error);
+    return false;
+  }
+}
+
+bool Parser::ConsumeString(std::string* output, const char* error) {
+  if (LookingAtType(io::Tokenizer::TYPE_STRING)) {
+    io::Tokenizer::ParseString(input_->current().text, output);
+    input_->Next();
+    // Allow C++ like concatenation of adjacent string tokens.
+    while (LookingAtType(io::Tokenizer::TYPE_STRING)) {
+      io::Tokenizer::ParseStringAppend(input_->current().text, output);
+      input_->Next();
+    }
+    return true;
+  } else {
+    AddError(error);
+    return false;
+  }
+}
+
+bool Parser::TryConsumeEndOfDeclaration(const char* text,
+                                        const LocationRecorder* location) {
+  if (LookingAt(text)) {
+    std::string leading, trailing;
+    std::vector<std::string> detached;
+    input_->NextWithComments(&trailing, &detached, &leading);
+
+    // Save the leading comments for next time, and recall the leading comments
+    // from last time.
+    leading.swap(upcoming_doc_comments_);
+
+    if (location != nullptr) {
+      upcoming_detached_comments_.swap(detached);
+      location->AttachComments(&leading, &trailing, &detached);
+    } else if (strcmp(text, "}") == 0) {
+      // If the current location is null and we are finishing the current scope,
+      // drop pending upcoming detached comments.
+      upcoming_detached_comments_.swap(detached);
+    } else {
+      // Otherwise, append the new detached comments to the existing upcoming
+      // detached comments.
+      upcoming_detached_comments_.insert(upcoming_detached_comments_.end(),
+                                         detached.begin(), detached.end());
+    }
+
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool Parser::ConsumeEndOfDeclaration(const char* text,
+                                     const LocationRecorder* location) {
+  if (TryConsumeEndOfDeclaration(text, location)) {
+    return true;
+  } else {
+    AddError("Expected \"" + std::string(text) + "\".");
+    return false;
+  }
+}
+
+// -------------------------------------------------------------------
+
+void Parser::AddError(int line, int column, const std::string& error) {
+  if (error_collector_ != nullptr) {
+    error_collector_->AddError(line, column, error);
+  }
+  had_errors_ = true;
+}
+
+void Parser::AddError(const std::string& error) {
+  AddError(input_->current().line, input_->current().column, error);
+}
+
+void Parser::AddWarning(const std::string& warning) {
+  if (error_collector_ != nullptr) {
+    error_collector_->AddWarning(input_->current().line,
+                                 input_->current().column, warning);
+  }
+}
+
+// -------------------------------------------------------------------
+
+Parser::LocationRecorder::LocationRecorder(Parser* parser)
+    : parser_(parser),
+      source_code_info_(parser->source_code_info_),
+      location_(parser_->source_code_info_->add_location()) {
+  location_->add_span(parser_->input_->current().line);
+  location_->add_span(parser_->input_->current().column);
+}
+
+Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent) {
+  Init(parent, parent.source_code_info_);
+}
+
+Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
+                                           int path1,
+                                           SourceCodeInfo* source_code_info) {
+  Init(parent, source_code_info);
+  AddPath(path1);
+}
+
+Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
+                                           int path1) {
+  Init(parent, parent.source_code_info_);
+  AddPath(path1);
+}
+
+Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
+                                           int path1, int path2) {
+  Init(parent, parent.source_code_info_);
+  AddPath(path1);
+  AddPath(path2);
+}
+
+void Parser::LocationRecorder::Init(const LocationRecorder& parent,
+                                    SourceCodeInfo* source_code_info) {
+  parser_ = parent.parser_;
+  source_code_info_ = source_code_info;
+
+  location_ = source_code_info_->add_location();
+  location_->mutable_path()->CopyFrom(parent.location_->path());
+
+  location_->add_span(parser_->input_->current().line);
+  location_->add_span(parser_->input_->current().column);
+}
+
+Parser::LocationRecorder::~LocationRecorder() {
+  if (location_->span_size() <= 2) {
+    EndAt(parser_->input_->previous());
+  }
+}
+
+void Parser::LocationRecorder::AddPath(int path_component) {
+  location_->add_path(path_component);
+}
+
+void Parser::LocationRecorder::StartAt(const io::Tokenizer::Token& token) {
+  location_->set_span(0, token.line);
+  location_->set_span(1, token.column);
+}
+
+void Parser::LocationRecorder::StartAt(const LocationRecorder& other) {
+  location_->set_span(0, other.location_->span(0));
+  location_->set_span(1, other.location_->span(1));
+}
+
+void Parser::LocationRecorder::EndAt(const io::Tokenizer::Token& token) {
+  if (token.line != location_->span(0)) {
+    location_->add_span(token.line);
+  }
+  location_->add_span(token.end_column);
+}
+
+void Parser::LocationRecorder::RecordLegacyLocation(
+    const Message* descriptor,
+    DescriptorPool::ErrorCollector::ErrorLocation location) {
+  if (parser_->source_location_table_ != nullptr) {
+    parser_->source_location_table_->Add(
+        descriptor, location, location_->span(0), location_->span(1));
+  }
+}
+
+void Parser::LocationRecorder::RecordLegacyImportLocation(
+    const Message* descriptor, const std::string& name) {
+  if (parser_->source_location_table_ != nullptr) {
+    parser_->source_location_table_->AddImport(
+        descriptor, name, location_->span(0), location_->span(1));
+  }
+}
+
+int Parser::LocationRecorder::CurrentPathSize() const {
+  return location_->path_size();
+}
+
+void Parser::LocationRecorder::AttachComments(
+    std::string* leading, std::string* trailing,
+    std::vector<std::string>* detached_comments) const {
+  GOOGLE_CHECK(!location_->has_leading_comments());
+  GOOGLE_CHECK(!location_->has_trailing_comments());
+
+  if (!leading->empty()) {
+    location_->mutable_leading_comments()->swap(*leading);
+  }
+  if (!trailing->empty()) {
+    location_->mutable_trailing_comments()->swap(*trailing);
+  }
+  for (size_t i = 0; i < detached_comments->size(); ++i) {
+    location_->add_leading_detached_comments()->swap((*detached_comments)[i]);
+  }
+  detached_comments->clear();
+}
+
+// -------------------------------------------------------------------
+
+void Parser::SkipStatement() {
+  while (true) {
+    if (AtEnd()) {
+      return;
+    } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) {
+      if (TryConsumeEndOfDeclaration(";", nullptr)) {
+        return;
+      } else if (TryConsume("{")) {
+        SkipRestOfBlock();
+        return;
+      } else if (LookingAt("}")) {
+        return;
+      }
+    }
+    input_->Next();
+  }
+}
+
+void Parser::SkipRestOfBlock() {
+  while (true) {
+    if (AtEnd()) {
+      return;
+    } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) {
+      if (TryConsumeEndOfDeclaration("}", nullptr)) {
+        return;
+      } else if (TryConsume("{")) {
+        SkipRestOfBlock();
+      }
+    }
+    input_->Next();
+  }
+}
+
+// ===================================================================
+
+bool Parser::ValidateEnum(const EnumDescriptorProto* proto) {
+  bool has_allow_alias = false;
+  bool allow_alias = false;
+
+  for (int i = 0; i < proto->options().uninterpreted_option_size(); i++) {
+    const UninterpretedOption option = proto->options().uninterpreted_option(i);
+    if (option.name_size() > 1) {
+      continue;
+    }
+    if (!option.name(0).is_extension() &&
+        option.name(0).name_part() == "allow_alias") {
+      has_allow_alias = true;
+      if (option.identifier_value() == "true") {
+        allow_alias = true;
+      }
+      break;
+    }
+  }
+
+  if (has_allow_alias && !allow_alias) {
+    std::string error =
+        "\"" + proto->name() +
+        "\" declares 'option allow_alias = false;' which has no effect. "
+        "Please remove the declaration.";
+    // This needlessly clutters declarations with nops.
+    AddError(error);
+    return false;
+  }
+
+  std::set<int> used_values;
+  bool has_duplicates = false;
+  for (int i = 0; i < proto->value_size(); ++i) {
+    const EnumValueDescriptorProto& enum_value = proto->value(i);
+    if (used_values.find(enum_value.number()) != used_values.end()) {
+      has_duplicates = true;
+      break;
+    } else {
+      used_values.insert(enum_value.number());
+    }
+  }
+  if (allow_alias && !has_duplicates) {
+    std::string error =
+        "\"" + proto->name() +
+        "\" declares support for enum aliases but no enum values share field "
+        "numbers. Please remove the unnecessary 'option allow_alias = true;' "
+        "declaration.";
+    // Generate an error if an enum declares support for duplicate enum values
+    // and does not use it protect future authors.
+    AddError(error);
+    return false;
+  }
+
+  // Enforce that enum constants must be UPPER_CASE except in case of
+  // enum_alias.
+  if (!allow_alias) {
+    for (const auto& enum_value : proto->value()) {
+      if (!IsUpperUnderscore(enum_value.name())) {
+        AddWarning(
+            "Enum constant should be in UPPER_CASE. Found: " +
+            enum_value.name() +
+            ". See https://developers.google.com/protocol-buffers/docs/style");
+      }
+    }
+  }
+
+  return true;
+}
+
+bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) {
+  input_ = input;
+  had_errors_ = false;
+  syntax_identifier_.clear();
+
+  // Note that |file| could be NULL at this point if
+  // stop_after_syntax_identifier_ is true.  So, we conservatively allocate
+  // SourceCodeInfo on the stack, then swap it into the FileDescriptorProto
+  // later on.
+  SourceCodeInfo source_code_info;
+  source_code_info_ = &source_code_info;
+
+  if (LookingAtType(io::Tokenizer::TYPE_START)) {
+    // Advance to first token.
+    input_->NextWithComments(nullptr, &upcoming_detached_comments_,
+                             &upcoming_doc_comments_);
+  }
+
+  {
+    LocationRecorder root_location(this);
+    root_location.RecordLegacyLocation(file,
+                                       DescriptorPool::ErrorCollector::OTHER);
+
+    if (require_syntax_identifier_ || LookingAt("syntax")) {
+      if (!ParseSyntaxIdentifier(root_location)) {
+        // Don't attempt to parse the file if we didn't recognize the syntax
+        // identifier.
+        return false;
+      }
+      // Store the syntax into the file.
+      if (file != nullptr) file->set_syntax(syntax_identifier_);
+    } else if (!stop_after_syntax_identifier_) {
+      GOOGLE_LOG(WARNING) << "No syntax specified for the proto file: " << file->name()
+                   << ". Please use 'syntax = \"proto2\";' "
+                   << "or 'syntax = \"proto3\";' to specify a syntax "
+                   << "version. (Defaulted to proto2 syntax.)";
+      syntax_identifier_ = "proto2";
+    }
+
+    if (stop_after_syntax_identifier_) return !had_errors_;
+
+    // Repeatedly parse statements until we reach the end of the file.
+    while (!AtEnd()) {
+      if (!ParseTopLevelStatement(file, root_location)) {
+        // This statement failed to parse.  Skip it, but keep looping to parse
+        // other statements.
+        SkipStatement();
+
+        if (LookingAt("}")) {
+          AddError("Unmatched \"}\".");
+          input_->NextWithComments(nullptr, &upcoming_detached_comments_,
+                                   &upcoming_doc_comments_);
+        }
+      }
+    }
+  }
+
+  input_ = nullptr;
+  source_code_info_ = nullptr;
+  assert(file != nullptr);
+  source_code_info.Swap(file->mutable_source_code_info());
+  return !had_errors_;
+}
+
+bool Parser::ParseSyntaxIdentifier(const LocationRecorder& parent) {
+  LocationRecorder syntax_location(parent,
+                                   FileDescriptorProto::kSyntaxFieldNumber);
+  DO(Consume(
+      "syntax",
+      "File must begin with a syntax statement, e.g. 'syntax = \"proto2\";'."));
+  DO(Consume("="));
+  io::Tokenizer::Token syntax_token = input_->current();
+  std::string syntax;
+  DO(ConsumeString(&syntax, "Expected syntax identifier."));
+  DO(ConsumeEndOfDeclaration(";", &syntax_location));
+
+  syntax_identifier_ = syntax;
+
+  if (syntax != "proto2" && syntax != "proto3" &&
+      !stop_after_syntax_identifier_) {
+    AddError(syntax_token.line, syntax_token.column,
+             "Unrecognized syntax identifier \"" + syntax +
+                 "\".  This parser "
+                 "only recognizes \"proto2\" and \"proto3\".");
+    return false;
+  }
+
+  return true;
+}
+
+bool Parser::ParseTopLevelStatement(FileDescriptorProto* file,
+                                    const LocationRecorder& root_location) {
+  if (TryConsumeEndOfDeclaration(";", nullptr)) {
+    // empty statement; ignore
+    return true;
+  } else if (LookingAt("message")) {
+    LocationRecorder location(root_location,
+                              FileDescriptorProto::kMessageTypeFieldNumber,
+                              file->message_type_size());
+    return ParseMessageDefinition(file->add_message_type(), location, file);
+  } else if (LookingAt("enum")) {
+    LocationRecorder location(root_location,
+                              FileDescriptorProto::kEnumTypeFieldNumber,
+                              file->enum_type_size());
+    return ParseEnumDefinition(file->add_enum_type(), location, file);
+  } else if (LookingAt("service")) {
+    LocationRecorder location(root_location,
+                              FileDescriptorProto::kServiceFieldNumber,
+                              file->service_size());
+    return ParseServiceDefinition(file->add_service(), location, file);
+  } else if (LookingAt("extend")) {
+    LocationRecorder location(root_location,
+                              FileDescriptorProto::kExtensionFieldNumber);
+    return ParseExtend(
+        file->mutable_extension(), file->mutable_message_type(), root_location,
+        FileDescriptorProto::kMessageTypeFieldNumber, location, file);
+  } else if (LookingAt("import")) {
+    return ParseImport(file->mutable_dependency(),
+                       file->mutable_public_dependency(),
+                       file->mutable_weak_dependency(), root_location, file);
+  } else if (LookingAt("package")) {
+    return ParsePackage(file, root_location, file);
+  } else if (LookingAt("option")) {
+    LocationRecorder location(root_location,
+                              FileDescriptorProto::kOptionsFieldNumber);
+    return ParseOption(file->mutable_options(), location, file,
+                       OPTION_STATEMENT);
+  } else {
+    AddError("Expected top-level statement (e.g. \"message\").");
+    return false;
+  }
+}
+
+// -------------------------------------------------------------------
+// Messages
+
+bool Parser::ParseMessageDefinition(
+    DescriptorProto* message, const LocationRecorder& message_location,
+    const FileDescriptorProto* containing_file) {
+  DO(Consume("message"));
+  {
+    LocationRecorder location(message_location,
+                              DescriptorProto::kNameFieldNumber);
+    location.RecordLegacyLocation(message,
+                                  DescriptorPool::ErrorCollector::NAME);
+    DO(ConsumeIdentifier(message->mutable_name(), "Expected message name."));
+    if (!IsUpperCamelCase(message->name())) {
+      AddWarning(
+          "Message name should be in UpperCamelCase. Found: " +
+          message->name() +
+          ". See https://developers.google.com/protocol-buffers/docs/style");
+    }
+  }
+  DO(ParseMessageBlock(message, message_location, containing_file));
+
+  if (syntax_identifier_ == "proto3") {
+    // Add synthetic one-field oneofs for optional fields, except messages which
+    // already have presence in proto3.
+    //
+    // We have to make sure the oneof names don't conflict with any other
+    // field or oneof.
+    std::unordered_set<std::string> names;
+    for (const auto& field : message->field()) {
+      names.insert(field.name());
+    }
+    for (const auto& oneof : message->oneof_decl()) {
+      names.insert(oneof.name());
+    }
+
+    for (auto& field : *message->mutable_field()) {
+      if (field.proto3_optional()) {
+        std::string oneof_name = field.name();
+
+        // Prepend 'XXXXX_' until we are no longer conflicting.
+        // Avoid prepending a double-underscore because such names are
+        // reserved in C++.
+        if (oneof_name.empty() || oneof_name[0] != '_') {
+          oneof_name = '_' + oneof_name;
+        }
+        while (names.count(oneof_name) > 0) {
+          oneof_name = 'X' + oneof_name;
+        }
+
+        names.insert(oneof_name);
+        field.set_oneof_index(message->oneof_decl_size());
+        OneofDescriptorProto* oneof = message->add_oneof_decl();
+        oneof->set_name(oneof_name);
+      }
+    }
+  }
+
+  return true;
+}
+
+namespace {
+
+const int kMaxRangeSentinel = -1;
+
+bool IsMessageSetWireFormatMessage(const DescriptorProto& message) {
+  const MessageOptions& options = message.options();
+  for (int i = 0; i < options.uninterpreted_option_size(); ++i) {
+    const UninterpretedOption& uninterpreted = options.uninterpreted_option(i);
+    if (uninterpreted.name_size() == 1 &&
+        uninterpreted.name(0).name_part() == "message_set_wire_format" &&
+        uninterpreted.identifier_value() == "true") {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Modifies any extension ranges that specified 'max' as the end of the
+// extension range, and sets them to the type-specific maximum. The actual max
+// tag number can only be determined after all options have been parsed.
+void AdjustExtensionRangesWithMaxEndNumber(DescriptorProto* message) {
+  const bool is_message_set = IsMessageSetWireFormatMessage(*message);
+  const int max_extension_number = is_message_set
+                                       ? std::numeric_limits<int32_t>::max()
+                                       : FieldDescriptor::kMaxNumber + 1;
+  for (int i = 0; i < message->extension_range_size(); ++i) {
+    if (message->extension_range(i).end() == kMaxRangeSentinel) {
+      message->mutable_extension_range(i)->set_end(max_extension_number);
+    }
+  }
+}
+
+// Modifies any reserved ranges that specified 'max' as the end of the
+// reserved range, and sets them to the type-specific maximum. The actual max
+// tag number can only be determined after all options have been parsed.
+void AdjustReservedRangesWithMaxEndNumber(DescriptorProto* message) {
+  const bool is_message_set = IsMessageSetWireFormatMessage(*message);
+  const int max_field_number = is_message_set
+                                   ? std::numeric_limits<int32_t>::max()
+                                   : FieldDescriptor::kMaxNumber + 1;
+  for (int i = 0; i < message->reserved_range_size(); ++i) {
+    if (message->reserved_range(i).end() == kMaxRangeSentinel) {
+      message->mutable_reserved_range(i)->set_end(max_field_number);
+    }
+  }
+}
+
+}  // namespace
+
+bool Parser::ParseMessageBlock(DescriptorProto* message,
+                               const LocationRecorder& message_location,
+                               const FileDescriptorProto* containing_file) {
+  DO(ConsumeEndOfDeclaration("{", &message_location));
+
+  while (!TryConsumeEndOfDeclaration("}", nullptr)) {
+    if (AtEnd()) {
+      AddError("Reached end of input in message definition (missing '}').");
+      return false;
+    }
+
+    if (!ParseMessageStatement(message, message_location, containing_file)) {
+      // This statement failed to parse.  Skip it, but keep looping to parse
+      // other statements.
+      SkipStatement();
+    }
+  }
+
+  if (message->extension_range_size() > 0) {
+    AdjustExtensionRangesWithMaxEndNumber(message);
+  }
+  if (message->reserved_range_size() > 0) {
+    AdjustReservedRangesWithMaxEndNumber(message);
+  }
+  return true;
+}
+
+bool Parser::ParseMessageStatement(DescriptorProto* message,
+                                   const LocationRecorder& message_location,
+                                   const FileDescriptorProto* containing_file) {
+  if (TryConsumeEndOfDeclaration(";", nullptr)) {
+    // empty statement; ignore
+    return true;
+  } else if (LookingAt("message")) {
+    LocationRecorder location(message_location,
+                              DescriptorProto::kNestedTypeFieldNumber,
+                              message->nested_type_size());
+    return ParseMessageDefinition(message->add_nested_type(), location,
+                                  containing_file);
+  } else if (LookingAt("enum")) {
+    LocationRecorder location(message_location,
+                              DescriptorProto::kEnumTypeFieldNumber,
+                              message->enum_type_size());
+    return ParseEnumDefinition(message->add_enum_type(), location,
+                               containing_file);
+  } else if (LookingAt("extensions")) {
+    LocationRecorder location(message_location,
+                              DescriptorProto::kExtensionRangeFieldNumber);
+    return ParseExtensions(message, location, containing_file);
+  } else if (LookingAt("reserved")) {
+    return ParseReserved(message, message_location);
+  } else if (LookingAt("extend")) {
+    LocationRecorder location(message_location,
+                              DescriptorProto::kExtensionFieldNumber);
+    return ParseExtend(message->mutable_extension(),
+                       message->mutable_nested_type(), message_location,
+                       DescriptorProto::kNestedTypeFieldNumber, location,
+                       containing_file);
+  } else if (LookingAt("option")) {
+    LocationRecorder location(message_location,
+                              DescriptorProto::kOptionsFieldNumber);
+    return ParseOption(message->mutable_options(), location, containing_file,
+                       OPTION_STATEMENT);
+  } else if (LookingAt("oneof")) {
+    int oneof_index = message->oneof_decl_size();
+    LocationRecorder oneof_location(
+        message_location, DescriptorProto::kOneofDeclFieldNumber, oneof_index);
+
+    return ParseOneof(message->add_oneof_decl(), message, oneof_index,
+                      oneof_location, message_location, containing_file);
+  } else {
+    LocationRecorder location(message_location,
+                              DescriptorProto::kFieldFieldNumber,
+                              message->field_size());
+    return ParseMessageField(
+        message->add_field(), message->mutable_nested_type(), message_location,
+        DescriptorProto::kNestedTypeFieldNumber, location, containing_file);
+  }
+}
+
+bool Parser::ParseMessageField(FieldDescriptorProto* field,
+                               RepeatedPtrField<DescriptorProto>* messages,
+                               const LocationRecorder& parent_location,
+                               int location_field_number_for_nested_type,
+                               const LocationRecorder& field_location,
+                               const FileDescriptorProto* containing_file) {
+  {
+    FieldDescriptorProto::Label label;
+    if (ParseLabel(&label, field_location)) {
+      field->set_label(label);
+      if (label == FieldDescriptorProto::LABEL_OPTIONAL &&
+          syntax_identifier_ == "proto3") {
+        field->set_proto3_optional(true);
+      }
+    }
+  }
+
+  return ParseMessageFieldNoLabel(field, messages, parent_location,
+                                  location_field_number_for_nested_type,
+                                  field_location, containing_file);
+}
+
+bool Parser::ParseMessageFieldNoLabel(
+    FieldDescriptorProto* field, RepeatedPtrField<DescriptorProto>* messages,
+    const LocationRecorder& parent_location,
+    int location_field_number_for_nested_type,
+    const LocationRecorder& field_location,
+    const FileDescriptorProto* containing_file) {
+  MapField map_field;
+  // Parse type.
+  {
+    LocationRecorder location(field_location);  // add path later
+    location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::TYPE);
+
+    bool type_parsed = false;
+    FieldDescriptorProto::Type type = FieldDescriptorProto::TYPE_INT32;
+    std::string type_name;
+
+    // Special case map field. We only treat the field as a map field if the
+    // field type name starts with the word "map" with a following "<".
+    if (TryConsume("map")) {
+      if (LookingAt("<")) {
+        map_field.is_map_field = true;
+        DO(ParseMapType(&map_field, field, location));
+      } else {
+        // False positive
+        type_parsed = true;
+        type_name = "map";
+      }
+    }
+    if (!map_field.is_map_field) {
+      // Handle the case where no explicit label is given for a non-map field.
+      if (!field->has_label() && DefaultToOptionalFields()) {
+        field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+      }
+      if (!field->has_label()) {
+        AddError("Expected \"required\", \"optional\", or \"repeated\".");
+        // We can actually reasonably recover here by just assuming the user
+        // forgot the label altogether.
+        field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+      }
+
+      // Handle the case where the actual type is a message or enum named
+      // "map", which we already consumed in the code above.
+      if (!type_parsed) {
+        DO(ParseType(&type, &type_name));
+      }
+      if (type_name.empty()) {
+        location.AddPath(FieldDescriptorProto::kTypeFieldNumber);
+        field->set_type(type);
+      } else {
+        location.AddPath(FieldDescriptorProto::kTypeNameFieldNumber);
+        field->set_type_name(type_name);
+      }
+    }
+  }
+
+  // Parse name and '='.
+  io::Tokenizer::Token name_token = input_->current();
+  {
+    LocationRecorder location(field_location,
+                              FieldDescriptorProto::kNameFieldNumber);
+    location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::NAME);
+    DO(ConsumeIdentifier(field->mutable_name(), "Expected field name."));
+
+    if (!IsLowerUnderscore(field->name())) {
+      AddWarning(
+          "Field name should be lowercase. Found: " + field->name() +
+          ". See: https://developers.google.com/protocol-buffers/docs/style");
+    }
+    if (IsNumberFollowUnderscore(field->name())) {
+      AddWarning(
+          "Number should not come right after an underscore. Found: " +
+          field->name() +
+          ". See: https://developers.google.com/protocol-buffers/docs/style");
+    }
+  }
+  DO(Consume("=", "Missing field number."));
+
+  // Parse field number.
+  {
+    LocationRecorder location(field_location,
+                              FieldDescriptorProto::kNumberFieldNumber);
+    location.RecordLegacyLocation(field,
+                                  DescriptorPool::ErrorCollector::NUMBER);
+    int number;
+    DO(ConsumeInteger(&number, "Expected field number."));
+    field->set_number(number);
+  }
+
+  // Parse options.
+  DO(ParseFieldOptions(field, field_location, containing_file));
+
+  // Deal with groups.
+  if (field->has_type() && field->type() == FieldDescriptorProto::TYPE_GROUP) {
+    // Awkward:  Since a group declares both a message type and a field, we
+    //   have to create overlapping locations.
+    LocationRecorder group_location(parent_location);
+    group_location.StartAt(field_location);
+    group_location.AddPath(location_field_number_for_nested_type);
+    group_location.AddPath(messages->size());
+
+    DescriptorProto* group = messages->Add();
+    group->set_name(field->name());
+
+    // Record name location to match the field name's location.
+    {
+      LocationRecorder location(group_location,
+                                DescriptorProto::kNameFieldNumber);
+      location.StartAt(name_token);
+      location.EndAt(name_token);
+      location.RecordLegacyLocation(group,
+                                    DescriptorPool::ErrorCollector::NAME);
+    }
+
+    // The field's type_name also comes from the name.  Confusing!
+    {
+      LocationRecorder location(field_location,
+                                FieldDescriptorProto::kTypeNameFieldNumber);
+      location.StartAt(name_token);
+      location.EndAt(name_token);
+    }
+
+    // As a hack for backwards-compatibility, we force the group name to start
+    // with a capital letter and lower-case the field name.  New code should
+    // not use groups; it should use nested messages.
+    if (group->name()[0] < 'A' || 'Z' < group->name()[0]) {
+      AddError(name_token.line, name_token.column,
+               "Group names must start with a capital letter.");
+    }
+    LowerString(field->mutable_name());
+
+    field->set_type_name(group->name());
+    if (LookingAt("{")) {
+      DO(ParseMessageBlock(group, group_location, containing_file));
+    } else {
+      AddError("Missing group body.");
+      return false;
+    }
+  } else {
+    DO(ConsumeEndOfDeclaration(";", &field_location));
+  }
+
+  // Create a map entry type if this is a map field.
+  if (map_field.is_map_field) {
+    GenerateMapEntry(map_field, field, messages);
+  }
+
+  return true;
+}
+
+bool Parser::ParseMapType(MapField* map_field, FieldDescriptorProto* field,
+                          LocationRecorder& type_name_location) {
+  if (field->has_oneof_index()) {
+    AddError("Map fields are not allowed in oneofs.");
+    return false;
+  }
+  if (field->has_label()) {
+    AddError(
+        "Field labels (required/optional/repeated) are not allowed on "
+        "map fields.");
+    return false;
+  }
+  if (field->has_extendee()) {
+    AddError("Map fields are not allowed to be extensions.");
+    return false;
+  }
+  field->set_label(FieldDescriptorProto::LABEL_REPEATED);
+  DO(Consume("<"));
+  DO(ParseType(&map_field->key_type, &map_field->key_type_name));
+  DO(Consume(","));
+  DO(ParseType(&map_field->value_type, &map_field->value_type_name));
+  DO(Consume(">"));
+  // Defer setting of the type name of the map field until the
+  // field name is parsed. Add the source location though.
+  type_name_location.AddPath(FieldDescriptorProto::kTypeNameFieldNumber);
+  return true;
+}
+
+void Parser::GenerateMapEntry(const MapField& map_field,
+                              FieldDescriptorProto* field,
+                              RepeatedPtrField<DescriptorProto>* messages) {
+  DescriptorProto* entry = messages->Add();
+  std::string entry_name = MapEntryName(field->name());
+  field->set_type_name(entry_name);
+  entry->set_name(entry_name);
+  entry->mutable_options()->set_map_entry(true);
+  FieldDescriptorProto* key_field = entry->add_field();
+  key_field->set_name("key");
+  key_field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+  key_field->set_number(1);
+  if (map_field.key_type_name.empty()) {
+    key_field->set_type(map_field.key_type);
+  } else {
+    key_field->set_type_name(map_field.key_type_name);
+  }
+  FieldDescriptorProto* value_field = entry->add_field();
+  value_field->set_name("value");
+  value_field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+  value_field->set_number(2);
+  if (map_field.value_type_name.empty()) {
+    value_field->set_type(map_field.value_type);
+  } else {
+    value_field->set_type_name(map_field.value_type_name);
+  }
+  // Propagate the "enforce_utf8" option to key and value fields if they
+  // are strings. This helps simplify the implementation of code generators
+  // and also reflection-based parsing code.
+  //
+  // The following definition:
+  //   message Foo {
+  //     map<string, string> value = 1 [enforce_utf8 = false];
+  //   }
+  // will be interpreted as:
+  //   message Foo {
+  //     message ValueEntry {
+  //       option map_entry = true;
+  //       string key = 1 [enforce_utf8 = false];
+  //       string value = 2 [enforce_utf8 = false];
+  //     }
+  //     repeated ValueEntry value = 1 [enforce_utf8 = false];
+  //  }
+  //
+  // TODO(xiaofeng): Remove this when the "enforce_utf8" option is removed
+  // from protocol compiler.
+  for (int i = 0; i < field->options().uninterpreted_option_size(); ++i) {
+    const UninterpretedOption& option =
+        field->options().uninterpreted_option(i);
+    if (option.name_size() == 1 &&
+        option.name(0).name_part() == "enforce_utf8" &&
+        !option.name(0).is_extension()) {
+      if (key_field->type() == FieldDescriptorProto::TYPE_STRING) {
+        key_field->mutable_options()->add_uninterpreted_option()->CopyFrom(
+            option);
+      }
+      if (value_field->type() == FieldDescriptorProto::TYPE_STRING) {
+        value_field->mutable_options()->add_uninterpreted_option()->CopyFrom(
+            option);
+      }
+    }
+  }
+}
+
+bool Parser::ParseFieldOptions(FieldDescriptorProto* field,
+                               const LocationRecorder& field_location,
+                               const FileDescriptorProto* containing_file) {
+  if (!LookingAt("[")) return true;
+
+  LocationRecorder location(field_location,
+                            FieldDescriptorProto::kOptionsFieldNumber);
+
+  DO(Consume("["));
+
+  // Parse field options.
+  do {
+    if (LookingAt("default")) {
+      // We intentionally pass field_location rather than location here, since
+      // the default value is not actually an option.
+      DO(ParseDefaultAssignment(field, field_location, containing_file));
+    } else if (LookingAt("json_name")) {
+      // Like default value, this "json_name" is not an actual option.
+      DO(ParseJsonName(field, field_location, containing_file));
+    } else {
+      DO(ParseOption(field->mutable_options(), location, containing_file,
+                     OPTION_ASSIGNMENT));
+    }
+  } while (TryConsume(","));
+
+  DO(Consume("]"));
+  return true;
+}
+
+bool Parser::ParseDefaultAssignment(
+    FieldDescriptorProto* field, const LocationRecorder& field_location,
+    const FileDescriptorProto* containing_file) {
+  if (field->has_default_value()) {
+    AddError("Already set option \"default\".");
+    field->clear_default_value();
+  }
+
+  DO(Consume("default"));
+  DO(Consume("="));
+
+  LocationRecorder location(field_location,
+                            FieldDescriptorProto::kDefaultValueFieldNumber);
+  location.RecordLegacyLocation(field,
+                                DescriptorPool::ErrorCollector::DEFAULT_VALUE);
+  std::string* default_value = field->mutable_default_value();
+
+  if (!field->has_type()) {
+    // The field has a type name, but we don't know if it is a message or an
+    // enum yet. (If it were a primitive type, |field| would have a type set
+    // already.) In this case, simply take the current string as the default
+    // value; we will catch the error later if it is not a valid enum value.
+    // (N.B. that we do not check whether the current token is an identifier:
+    // doing so throws strange errors when the user mistypes a primitive
+    // typename and we assume it's an enum. E.g.: "optional int foo = 1 [default
+    // = 42]". In such a case the fundamental error is really that "int" is not
+    // a type, not that "42" is not an identifier. See b/12533582.)
+    *default_value = input_->current().text;
+    input_->Next();
+    return true;
+  }
+
+  switch (field->type()) {
+    case FieldDescriptorProto::TYPE_INT32:
+    case FieldDescriptorProto::TYPE_INT64:
+    case FieldDescriptorProto::TYPE_SINT32:
+    case FieldDescriptorProto::TYPE_SINT64:
+    case FieldDescriptorProto::TYPE_SFIXED32:
+    case FieldDescriptorProto::TYPE_SFIXED64: {
+      uint64_t max_value = std::numeric_limits<int64_t>::max();
+      if (field->type() == FieldDescriptorProto::TYPE_INT32 ||
+          field->type() == FieldDescriptorProto::TYPE_SINT32 ||
+          field->type() == FieldDescriptorProto::TYPE_SFIXED32) {
+        max_value = std::numeric_limits<int32_t>::max();
+      }
+
+      // These types can be negative.
+      if (TryConsume("-")) {
+        default_value->append("-");
+        // Two's complement always has one more negative value than positive.
+        ++max_value;
+      }
+      // Parse the integer to verify that it is not out-of-range.
+      uint64_t value;
+      DO(ConsumeInteger64(max_value, &value,
+                          "Expected integer for field default value."));
+      // And stringify it again.
+      default_value->append(StrCat(value));
+      break;
+    }
+
+    case FieldDescriptorProto::TYPE_UINT32:
+    case FieldDescriptorProto::TYPE_UINT64:
+    case FieldDescriptorProto::TYPE_FIXED32:
+    case FieldDescriptorProto::TYPE_FIXED64: {
+      uint64_t max_value = std::numeric_limits<uint64_t>::max();
+      if (field->type() == FieldDescriptorProto::TYPE_UINT32 ||
+          field->type() == FieldDescriptorProto::TYPE_FIXED32) {
+        max_value = std::numeric_limits<uint32_t>::max();
+      }
+
+      // Numeric, not negative.
+      if (TryConsume("-")) {
+        AddError("Unsigned field can't have negative default value.");
+      }
+      // Parse the integer to verify that it is not out-of-range.
+      uint64_t value;
+      DO(ConsumeInteger64(max_value, &value,
+                          "Expected integer for field default value."));
+      // And stringify it again.
+      default_value->append(StrCat(value));
+      break;
+    }
+
+    case FieldDescriptorProto::TYPE_FLOAT:
+    case FieldDescriptorProto::TYPE_DOUBLE:
+      // These types can be negative.
+      if (TryConsume("-")) {
+        default_value->append("-");
+      }
+      // Parse the integer because we have to convert hex integers to decimal
+      // floats.
+      double value;
+      DO(ConsumeNumber(&value, "Expected number."));
+      // And stringify it again.
+      default_value->append(SimpleDtoa(value));
+      break;
+
+    case FieldDescriptorProto::TYPE_BOOL:
+      if (TryConsume("true")) {
+        default_value->assign("true");
+      } else if (TryConsume("false")) {
+        default_value->assign("false");
+      } else {
+        AddError("Expected \"true\" or \"false\".");
+        return false;
+      }
+      break;
+
+    case FieldDescriptorProto::TYPE_STRING:
+      // Note: When file option java_string_check_utf8 is true, if a
+      // non-string representation (eg byte[]) is later supported, it must
+      // be checked for UTF-8-ness.
+      DO(ConsumeString(default_value,
+                       "Expected string for field default "
+                       "value."));
+      break;
+
+    case FieldDescriptorProto::TYPE_BYTES:
+      DO(ConsumeString(default_value, "Expected string."));
+      *default_value = CEscape(*default_value);
+      break;
+
+    case FieldDescriptorProto::TYPE_ENUM:
+      DO(ConsumeIdentifier(default_value,
+                           "Expected enum identifier for field "
+                           "default value."));
+      break;
+
+    case FieldDescriptorProto::TYPE_MESSAGE:
+    case FieldDescriptorProto::TYPE_GROUP:
+      AddError("Messages can't have default values.");
+      return false;
+  }
+
+  return true;
+}
+
+bool Parser::ParseJsonName(FieldDescriptorProto* field,
+                           const LocationRecorder& field_location,
+                           const FileDescriptorProto* containing_file) {
+  if (field->has_json_name()) {
+    AddError("Already set option \"json_name\".");
+    field->clear_json_name();
+  }
+
+  LocationRecorder location(field_location,
+                            FieldDescriptorProto::kJsonNameFieldNumber);
+  location.RecordLegacyLocation(field,
+                                DescriptorPool::ErrorCollector::OPTION_NAME);
+
+  DO(Consume("json_name"));
+  DO(Consume("="));
+
+  LocationRecorder value_location(location);
+  value_location.RecordLegacyLocation(
+      field, DescriptorPool::ErrorCollector::OPTION_VALUE);
+
+  DO(ConsumeString(field->mutable_json_name(),
+                   "Expected string for JSON name."));
+  return true;
+}
+
+bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option,
+                                 const LocationRecorder& part_location,
+                                 const FileDescriptorProto* containing_file) {
+  UninterpretedOption::NamePart* name = uninterpreted_option->add_name();
+  std::string identifier;  // We parse identifiers into this string.
+  if (LookingAt("(")) {    // This is an extension.
+    DO(Consume("("));
+
+    {
+      LocationRecorder location(
+          part_location, UninterpretedOption::NamePart::kNamePartFieldNumber);
+      // An extension name consists of dot-separated identifiers, and may begin
+      // with a dot.
+      if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
+        DO(ConsumeIdentifier(&identifier, "Expected identifier."));
+        name->mutable_name_part()->append(identifier);
+      }
+      while (LookingAt(".")) {
+        DO(Consume("."));
+        name->mutable_name_part()->append(".");
+        DO(ConsumeIdentifier(&identifier, "Expected identifier."));
+        name->mutable_name_part()->append(identifier);
+      }
+    }
+
+    DO(Consume(")"));
+    name->set_is_extension(true);
+  } else {  // This is a regular field.
+    LocationRecorder location(
+        part_location, UninterpretedOption::NamePart::kNamePartFieldNumber);
+    DO(ConsumeIdentifier(&identifier, "Expected identifier."));
+    name->mutable_name_part()->append(identifier);
+    name->set_is_extension(false);
+  }
+  return true;
+}
+
+bool Parser::ParseUninterpretedBlock(std::string* value) {
+  // Note that enclosing braces are not added to *value.
+  // We do NOT use ConsumeEndOfStatement for this brace because it's delimiting
+  // an expression, not a block of statements.
+  DO(Consume("{"));
+  int brace_depth = 1;
+  while (!AtEnd()) {
+    if (LookingAt("{")) {
+      brace_depth++;
+    } else if (LookingAt("}")) {
+      brace_depth--;
+      if (brace_depth == 0) {
+        input_->Next();
+        return true;
+      }
+    }
+    // TODO(sanjay): Interpret line/column numbers to preserve formatting
+    if (!value->empty()) value->push_back(' ');
+    value->append(input_->current().text);
+    input_->Next();
+  }
+  AddError("Unexpected end of stream while parsing aggregate value.");
+  return false;
+}
+
+// We don't interpret the option here. Instead we store it in an
+// UninterpretedOption, to be interpreted later.
+bool Parser::ParseOption(Message* options,
+                         const LocationRecorder& options_location,
+                         const FileDescriptorProto* containing_file,
+                         OptionStyle style) {
+  // Create an entry in the uninterpreted_option field.
+  const FieldDescriptor* uninterpreted_option_field =
+      options->GetDescriptor()->FindFieldByName("uninterpreted_option");
+  GOOGLE_CHECK(uninterpreted_option_field != nullptr)
+      << "No field named \"uninterpreted_option\" in the Options proto.";
+
+  const Reflection* reflection = options->GetReflection();
+
+  LocationRecorder location(
+      options_location, uninterpreted_option_field->number(),
+      reflection->FieldSize(*options, uninterpreted_option_field));
+
+  if (style == OPTION_STATEMENT) {
+    DO(Consume("option"));
+  }
+
+  UninterpretedOption* uninterpreted_option =
+      down_cast<UninterpretedOption*>(options->GetReflection()->AddMessage(
+          options, uninterpreted_option_field));
+
+  // Parse dot-separated name.
+  {
+    LocationRecorder name_location(location,
+                                   UninterpretedOption::kNameFieldNumber);
+    name_location.RecordLegacyLocation(
+        uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_NAME);
+
+    {
+      LocationRecorder part_location(name_location,
+                                     uninterpreted_option->name_size());
+      DO(ParseOptionNamePart(uninterpreted_option, part_location,
+                             containing_file));
+    }
+
+    while (LookingAt(".")) {
+      DO(Consume("."));
+      LocationRecorder part_location(name_location,
+                                     uninterpreted_option->name_size());
+      DO(ParseOptionNamePart(uninterpreted_option, part_location,
+                             containing_file));
+    }
+  }
+
+  DO(Consume("="));
+
+  {
+    LocationRecorder value_location(location);
+    value_location.RecordLegacyLocation(
+        uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_VALUE);
+
+    // All values are a single token, except for negative numbers, which consist
+    // of a single '-' symbol, followed by a positive number.
+    bool is_negative = TryConsume("-");
+
+    switch (input_->current().type) {
+      case io::Tokenizer::TYPE_START:
+        GOOGLE_LOG(FATAL) << "Trying to read value before any tokens have been read.";
+        return false;
+
+      case io::Tokenizer::TYPE_END:
+        AddError("Unexpected end of stream while parsing option value.");
+        return false;
+
+      case io::Tokenizer::TYPE_WHITESPACE:
+      case io::Tokenizer::TYPE_NEWLINE:
+        GOOGLE_CHECK(!input_->report_whitespace() && !input_->report_newlines())
+            << "Whitespace tokens were not requested.";
+        GOOGLE_LOG(FATAL) << "Tokenizer reported whitespace.";
+        return false;
+
+      case io::Tokenizer::TYPE_IDENTIFIER: {
+        value_location.AddPath(
+            UninterpretedOption::kIdentifierValueFieldNumber);
+        if (is_negative) {
+          AddError("Invalid '-' symbol before identifier.");
+          return false;
+        }
+        std::string value;
+        DO(ConsumeIdentifier(&value, "Expected identifier."));
+        uninterpreted_option->set_identifier_value(value);
+        break;
+      }
+
+      case io::Tokenizer::TYPE_INTEGER: {
+        uint64_t value;
+        uint64_t max_value =
+            is_negative
+                ? static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1
+                : std::numeric_limits<uint64_t>::max();
+        DO(ConsumeInteger64(max_value, &value, "Expected integer."));
+        if (is_negative) {
+          value_location.AddPath(
+              UninterpretedOption::kNegativeIntValueFieldNumber);
+          uninterpreted_option->set_negative_int_value(
+              static_cast<int64_t>(0 - value));
+        } else {
+          value_location.AddPath(
+              UninterpretedOption::kPositiveIntValueFieldNumber);
+          uninterpreted_option->set_positive_int_value(value);
+        }
+        break;
+      }
+
+      case io::Tokenizer::TYPE_FLOAT: {
+        value_location.AddPath(UninterpretedOption::kDoubleValueFieldNumber);
+        double value;
+        DO(ConsumeNumber(&value, "Expected number."));
+        uninterpreted_option->set_double_value(is_negative ? -value : value);
+        break;
+      }
+
+      case io::Tokenizer::TYPE_STRING: {
+        value_location.AddPath(UninterpretedOption::kStringValueFieldNumber);
+        if (is_negative) {
+          AddError("Invalid '-' symbol before string.");
+          return false;
+        }
+        std::string value;
+        DO(ConsumeString(&value, "Expected string."));
+        uninterpreted_option->set_string_value(value);
+        break;
+      }
+
+      case io::Tokenizer::TYPE_SYMBOL:
+        if (LookingAt("{")) {
+          value_location.AddPath(
+              UninterpretedOption::kAggregateValueFieldNumber);
+          DO(ParseUninterpretedBlock(
+              uninterpreted_option->mutable_aggregate_value()));
+        } else {
+          AddError("Expected option value.");
+          return false;
+        }
+        break;
+    }
+  }
+
+  if (style == OPTION_STATEMENT) {
+    DO(ConsumeEndOfDeclaration(";", &location));
+  }
+
+  return true;
+}
+
+bool Parser::ParseExtensions(DescriptorProto* message,
+                             const LocationRecorder& extensions_location,
+                             const FileDescriptorProto* containing_file) {
+  // Parse the declaration.
+  DO(Consume("extensions"));
+
+  int old_range_size = message->extension_range_size();
+
+  do {
+    // Note that kExtensionRangeFieldNumber was already pushed by the parent.
+    LocationRecorder location(extensions_location,
+                              message->extension_range_size());
+
+    DescriptorProto::ExtensionRange* range = message->add_extension_range();
+    location.RecordLegacyLocation(range,
+                                  DescriptorPool::ErrorCollector::NUMBER);
+
+    int start, end;
+    io::Tokenizer::Token start_token;
+
+    {
+      LocationRecorder start_location(
+          location, DescriptorProto::ExtensionRange::kStartFieldNumber);
+      start_token = input_->current();
+      DO(ConsumeInteger(&start, "Expected field number range."));
+    }
+
+    if (TryConsume("to")) {
+      LocationRecorder end_location(
+          location, DescriptorProto::ExtensionRange::kEndFieldNumber);
+      if (TryConsume("max")) {
+        // Set to the sentinel value - 1 since we increment the value below.
+        // The actual value of the end of the range should be set with
+        // AdjustExtensionRangesWithMaxEndNumber.
+        end = kMaxRangeSentinel - 1;
+      } else {
+        DO(ConsumeInteger(&end, "Expected integer."));
+      }
+    } else {
+      LocationRecorder end_location(
+          location, DescriptorProto::ExtensionRange::kEndFieldNumber);
+      end_location.StartAt(start_token);
+      end_location.EndAt(start_token);
+      end = start;
+    }
+
+    // Users like to specify inclusive ranges, but in code we like the end
+    // number to be exclusive.
+    ++end;
+
+    range->set_start(start);
+    range->set_end(end);
+  } while (TryConsume(","));
+
+  if (LookingAt("[")) {
+    int range_number_index = extensions_location.CurrentPathSize();
+    SourceCodeInfo info;
+
+    // Parse extension range options in the first range.
+    ExtensionRangeOptions* options =
+        message->mutable_extension_range(old_range_size)->mutable_options();
+
+    {
+      LocationRecorder index_location(
+          extensions_location, 0 /* we fill this in w/ actual index below */,
+          &info);
+      LocationRecorder location(
+          index_location, DescriptorProto::ExtensionRange::kOptionsFieldNumber);
+      DO(Consume("["));
+
+      do {
+        DO(ParseOption(options, location, containing_file, OPTION_ASSIGNMENT));
+      } while (TryConsume(","));
+
+      DO(Consume("]"));
+    }
+
+    // Then copy the extension range options to all of the other ranges we've
+    // parsed.
+    for (int i = old_range_size + 1; i < message->extension_range_size(); i++) {
+      message->mutable_extension_range(i)->mutable_options()->CopyFrom(
+          *options);
+    }
+    // and copy source locations to the other ranges, too
+    for (int i = old_range_size; i < message->extension_range_size(); i++) {
+      for (int j = 0; j < info.location_size(); j++) {
+        if (info.location(j).path_size() == range_number_index + 1) {
+          // this location's path is up to the extension range index, but
+          // doesn't include options; so it's redundant with location above
+          continue;
+        }
+        SourceCodeInfo_Location* dest = source_code_info_->add_location();
+        *dest = info.location(j);
+        dest->set_path(range_number_index, i);
+      }
+    }
+  }
+
+  DO(ConsumeEndOfDeclaration(";", &extensions_location));
+  return true;
+}
+
+// This is similar to extension range parsing, except that it accepts field
+// name literals.
+bool Parser::ParseReserved(DescriptorProto* message,
+                           const LocationRecorder& message_location) {
+  io::Tokenizer::Token start_token = input_->current();
+  // Parse the declaration.
+  DO(Consume("reserved"));
+  if (LookingAtType(io::Tokenizer::TYPE_STRING)) {
+    LocationRecorder location(message_location,
+                              DescriptorProto::kReservedNameFieldNumber);
+    location.StartAt(start_token);
+    return ParseReservedNames(message, location);
+  } else {
+    LocationRecorder location(message_location,
+                              DescriptorProto::kReservedRangeFieldNumber);
+    location.StartAt(start_token);
+    return ParseReservedNumbers(message, location);
+  }
+}
+
+bool Parser::ParseReservedNames(DescriptorProto* message,
+                                const LocationRecorder& parent_location) {
+  do {
+    LocationRecorder location(parent_location, message->reserved_name_size());
+    DO(ConsumeString(message->add_reserved_name(), "Expected field name."));
+  } while (TryConsume(","));
+  DO(ConsumeEndOfDeclaration(";", &parent_location));
+  return true;
+}
+
+bool Parser::ParseReservedNumbers(DescriptorProto* message,
+                                  const LocationRecorder& parent_location) {
+  bool first = true;
+  do {
+    LocationRecorder location(parent_location, message->reserved_range_size());
+
+    DescriptorProto::ReservedRange* range = message->add_reserved_range();
+    int start, end;
+    io::Tokenizer::Token start_token;
+    {
+      LocationRecorder start_location(
+          location, DescriptorProto::ReservedRange::kStartFieldNumber);
+      start_token = input_->current();
+      DO(ConsumeInteger(&start, (first ? "Expected field name or number range."
+                                       : "Expected field number range.")));
+    }
+
+    if (TryConsume("to")) {
+      LocationRecorder end_location(
+          location, DescriptorProto::ReservedRange::kEndFieldNumber);
+      if (TryConsume("max")) {
+        // Set to the sentinel value - 1 since we increment the value below.
+        // The actual value of the end of the range should be set with
+        // AdjustExtensionRangesWithMaxEndNumber.
+        end = kMaxRangeSentinel - 1;
+      } else {
+        DO(ConsumeInteger(&end, "Expected integer."));
+      }
+    } else {
+      LocationRecorder end_location(
+          location, DescriptorProto::ReservedRange::kEndFieldNumber);
+      end_location.StartAt(start_token);
+      end_location.EndAt(start_token);
+      end = start;
+    }
+
+    // Users like to specify inclusive ranges, but in code we like the end
+    // number to be exclusive.
+    ++end;
+
+    range->set_start(start);
+    range->set_end(end);
+    first = false;
+  } while (TryConsume(","));
+
+  DO(ConsumeEndOfDeclaration(";", &parent_location));
+  return true;
+}
+
+bool Parser::ParseReserved(EnumDescriptorProto* message,
+                           const LocationRecorder& message_location) {
+  io::Tokenizer::Token start_token = input_->current();
+  // Parse the declaration.
+  DO(Consume("reserved"));
+  if (LookingAtType(io::Tokenizer::TYPE_STRING)) {
+    LocationRecorder location(message_location,
+                              EnumDescriptorProto::kReservedNameFieldNumber);
+    location.StartAt(start_token);
+    return ParseReservedNames(message, location);
+  } else {
+    LocationRecorder location(message_location,
+                              EnumDescriptorProto::kReservedRangeFieldNumber);
+    location.StartAt(start_token);
+    return ParseReservedNumbers(message, location);
+  }
+}
+
+bool Parser::ParseReservedNames(EnumDescriptorProto* message,
+                                const LocationRecorder& parent_location) {
+  do {
+    LocationRecorder location(parent_location, message->reserved_name_size());
+    DO(ConsumeString(message->add_reserved_name(), "Expected enum value."));
+  } while (TryConsume(","));
+  DO(ConsumeEndOfDeclaration(";", &parent_location));
+  return true;
+}
+
+bool Parser::ParseReservedNumbers(EnumDescriptorProto* message,
+                                  const LocationRecorder& parent_location) {
+  bool first = true;
+  do {
+    LocationRecorder location(parent_location, message->reserved_range_size());
+
+    EnumDescriptorProto::EnumReservedRange* range =
+        message->add_reserved_range();
+    int start, end;
+    io::Tokenizer::Token start_token;
+    {
+      LocationRecorder start_location(
+          location, EnumDescriptorProto::EnumReservedRange::kStartFieldNumber);
+      start_token = input_->current();
+      DO(ConsumeSignedInteger(&start,
+                              (first ? "Expected enum value or number range."
+                                     : "Expected enum number range.")));
+    }
+
+    if (TryConsume("to")) {
+      LocationRecorder end_location(
+          location, EnumDescriptorProto::EnumReservedRange::kEndFieldNumber);
+      if (TryConsume("max")) {
+        // This is in the enum descriptor path, which doesn't have the message
+        // set duality to fix up, so it doesn't integrate with the sentinel.
+        end = INT_MAX;
+      } else {
+        DO(ConsumeSignedInteger(&end, "Expected integer."));
+      }
+    } else {
+      LocationRecorder end_location(
+          location, EnumDescriptorProto::EnumReservedRange::kEndFieldNumber);
+      end_location.StartAt(start_token);
+      end_location.EndAt(start_token);
+      end = start;
+    }
+
+    range->set_start(start);
+    range->set_end(end);
+    first = false;
+  } while (TryConsume(","));
+
+  DO(ConsumeEndOfDeclaration(";", &parent_location));
+  return true;
+}
+
+bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
+                         RepeatedPtrField<DescriptorProto>* messages,
+                         const LocationRecorder& parent_location,
+                         int location_field_number_for_nested_type,
+                         const LocationRecorder& extend_location,
+                         const FileDescriptorProto* containing_file) {
+  DO(Consume("extend"));
+
+  // Parse the extendee type.
+  io::Tokenizer::Token extendee_start = input_->current();
+  std::string extendee;
+  DO(ParseUserDefinedType(&extendee));
+  io::Tokenizer::Token extendee_end = input_->previous();
+
+  // Parse the block.
+  DO(ConsumeEndOfDeclaration("{", &extend_location));
+
+  bool is_first = true;
+
+  do {
+    if (AtEnd()) {
+      AddError("Reached end of input in extend definition (missing '}').");
+      return false;
+    }
+
+    // Note that kExtensionFieldNumber was already pushed by the parent.
+    LocationRecorder location(extend_location, extensions->size());
+
+    FieldDescriptorProto* field = extensions->Add();
+
+    {
+      LocationRecorder extendee_location(
+          location, FieldDescriptorProto::kExtendeeFieldNumber);
+      extendee_location.StartAt(extendee_start);
+      extendee_location.EndAt(extendee_end);
+
+      if (is_first) {
+        extendee_location.RecordLegacyLocation(
+            field, DescriptorPool::ErrorCollector::EXTENDEE);
+        is_first = false;
+      }
+    }
+
+    field->set_extendee(extendee);
+
+    if (!ParseMessageField(field, messages, parent_location,
+                           location_field_number_for_nested_type, location,
+                           containing_file)) {
+      // This statement failed to parse.  Skip it, but keep looping to parse
+      // other statements.
+      SkipStatement();
+    }
+  } while (!TryConsumeEndOfDeclaration("}", nullptr));
+
+  return true;
+}
+
+bool Parser::ParseOneof(OneofDescriptorProto* oneof_decl,
+                        DescriptorProto* containing_type, int oneof_index,
+                        const LocationRecorder& oneof_location,
+                        const LocationRecorder& containing_type_location,
+                        const FileDescriptorProto* containing_file) {
+  DO(Consume("oneof"));
+
+  {
+    LocationRecorder name_location(oneof_location,
+                                   OneofDescriptorProto::kNameFieldNumber);
+    DO(ConsumeIdentifier(oneof_decl->mutable_name(), "Expected oneof name."));
+  }
+
+  DO(ConsumeEndOfDeclaration("{", &oneof_location));
+
+  do {
+    if (AtEnd()) {
+      AddError("Reached end of input in oneof definition (missing '}').");
+      return false;
+    }
+
+    if (LookingAt("option")) {
+      LocationRecorder option_location(
+          oneof_location, OneofDescriptorProto::kOptionsFieldNumber);
+      if (!ParseOption(oneof_decl->mutable_options(), option_location,
+                       containing_file, OPTION_STATEMENT)) {
+        return false;
+      }
+      continue;
+    }
+
+    // Print a nice error if the user accidentally tries to place a label
+    // on an individual member of a oneof.
+    if (LookingAt("required") || LookingAt("optional") ||
+        LookingAt("repeated")) {
+      AddError(
+          "Fields in oneofs must not have labels (required / optional "
+          "/ repeated).");
+      // We can continue parsing here because we understand what the user
+      // meant.  The error report will still make parsing fail overall.
+      input_->Next();
+    }
+
+    LocationRecorder field_location(containing_type_location,
+                                    DescriptorProto::kFieldFieldNumber,
+                                    containing_type->field_size());
+
+    FieldDescriptorProto* field = containing_type->add_field();
+    field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+    field->set_oneof_index(oneof_index);
+
+    if (!ParseMessageFieldNoLabel(field, containing_type->mutable_nested_type(),
+                                  containing_type_location,
+                                  DescriptorProto::kNestedTypeFieldNumber,
+                                  field_location, containing_file)) {
+      // This statement failed to parse.  Skip it, but keep looping to parse
+      // other statements.
+      SkipStatement();
+    }
+  } while (!TryConsumeEndOfDeclaration("}", nullptr));
+
+  return true;
+}
+
+// -------------------------------------------------------------------
+// Enums
+
+bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type,
+                                 const LocationRecorder& enum_location,
+                                 const FileDescriptorProto* containing_file) {
+  DO(Consume("enum"));
+
+  {
+    LocationRecorder location(enum_location,
+                              EnumDescriptorProto::kNameFieldNumber);
+    location.RecordLegacyLocation(enum_type,
+                                  DescriptorPool::ErrorCollector::NAME);
+    DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name."));
+  }
+
+  DO(ParseEnumBlock(enum_type, enum_location, containing_file));
+
+  DO(ValidateEnum(enum_type));
+
+  return true;
+}
+
+bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type,
+                            const LocationRecorder& enum_location,
+                            const FileDescriptorProto* containing_file) {
+  DO(ConsumeEndOfDeclaration("{", &enum_location));
+
+  while (!TryConsumeEndOfDeclaration("}", nullptr)) {
+    if (AtEnd()) {
+      AddError("Reached end of input in enum definition (missing '}').");
+      return false;
+    }
+
+    if (!ParseEnumStatement(enum_type, enum_location, containing_file)) {
+      // This statement failed to parse.  Skip it, but keep looping to parse
+      // other statements.
+      SkipStatement();
+    }
+  }
+
+  return true;
+}
+
+bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type,
+                                const LocationRecorder& enum_location,
+                                const FileDescriptorProto* containing_file) {
+  if (TryConsumeEndOfDeclaration(";", nullptr)) {
+    // empty statement; ignore
+    return true;
+  } else if (LookingAt("option")) {
+    LocationRecorder location(enum_location,
+                              EnumDescriptorProto::kOptionsFieldNumber);
+    return ParseOption(enum_type->mutable_options(), location, containing_file,
+                       OPTION_STATEMENT);
+  } else if (LookingAt("reserved")) {
+    return ParseReserved(enum_type, enum_location);
+  } else {
+    LocationRecorder location(enum_location,
+                              EnumDescriptorProto::kValueFieldNumber,
+                              enum_type->value_size());
+    return ParseEnumConstant(enum_type->add_value(), location, containing_file);
+  }
+}
+
+bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value,
+                               const LocationRecorder& enum_value_location,
+                               const FileDescriptorProto* containing_file) {
+  // Parse name.
+  {
+    LocationRecorder location(enum_value_location,
+                              EnumValueDescriptorProto::kNameFieldNumber);
+    location.RecordLegacyLocation(enum_value,
+                                  DescriptorPool::ErrorCollector::NAME);
+    DO(ConsumeIdentifier(enum_value->mutable_name(),
+                         "Expected enum constant name."));
+  }
+
+  DO(Consume("=", "Missing numeric value for enum constant."));
+
+  // Parse value.
+  {
+    LocationRecorder location(enum_value_location,
+                              EnumValueDescriptorProto::kNumberFieldNumber);
+    location.RecordLegacyLocation(enum_value,
+                                  DescriptorPool::ErrorCollector::NUMBER);
+
+    int number;
+    DO(ConsumeSignedInteger(&number, "Expected integer."));
+    enum_value->set_number(number);
+  }
+
+  DO(ParseEnumConstantOptions(enum_value, enum_value_location,
+                              containing_file));
+
+  DO(ConsumeEndOfDeclaration(";", &enum_value_location));
+
+  return true;
+}
+
+bool Parser::ParseEnumConstantOptions(
+    EnumValueDescriptorProto* value,
+    const LocationRecorder& enum_value_location,
+    const FileDescriptorProto* containing_file) {
+  if (!LookingAt("[")) return true;
+
+  LocationRecorder location(enum_value_location,
+                            EnumValueDescriptorProto::kOptionsFieldNumber);
+
+  DO(Consume("["));
+
+  do {
+    DO(ParseOption(value->mutable_options(), location, containing_file,
+                   OPTION_ASSIGNMENT));
+  } while (TryConsume(","));
+
+  DO(Consume("]"));
+  return true;
+}
+
+// -------------------------------------------------------------------
+// Services
+
+bool Parser::ParseServiceDefinition(
+    ServiceDescriptorProto* service, const LocationRecorder& service_location,
+    const FileDescriptorProto* containing_file) {
+  DO(Consume("service"));
+
+  {
+    LocationRecorder location(service_location,
+                              ServiceDescriptorProto::kNameFieldNumber);
+    location.RecordLegacyLocation(service,
+                                  DescriptorPool::ErrorCollector::NAME);
+    DO(ConsumeIdentifier(service->mutable_name(), "Expected service name."));
+  }
+
+  DO(ParseServiceBlock(service, service_location, containing_file));
+  return true;
+}
+
+bool Parser::ParseServiceBlock(ServiceDescriptorProto* service,
+                               const LocationRecorder& service_location,
+                               const FileDescriptorProto* containing_file) {
+  DO(ConsumeEndOfDeclaration("{", &service_location));
+
+  while (!TryConsumeEndOfDeclaration("}", nullptr)) {
+    if (AtEnd()) {
+      AddError("Reached end of input in service definition (missing '}').");
+      return false;
+    }
+
+    if (!ParseServiceStatement(service, service_location, containing_file)) {
+      // This statement failed to parse.  Skip it, but keep looping to parse
+      // other statements.
+      SkipStatement();
+    }
+  }
+
+  return true;
+}
+
+bool Parser::ParseServiceStatement(ServiceDescriptorProto* service,
+                                   const LocationRecorder& service_location,
+                                   const FileDescriptorProto* containing_file) {
+  if (TryConsumeEndOfDeclaration(";", nullptr)) {
+    // empty statement; ignore
+    return true;
+  } else if (LookingAt("option")) {
+    LocationRecorder location(service_location,
+                              ServiceDescriptorProto::kOptionsFieldNumber);
+    return ParseOption(service->mutable_options(), location, containing_file,
+                       OPTION_STATEMENT);
+  } else {
+    LocationRecorder location(service_location,
+                              ServiceDescriptorProto::kMethodFieldNumber,
+                              service->method_size());
+    return ParseServiceMethod(service->add_method(), location, containing_file);
+  }
+}
+
+bool Parser::ParseServiceMethod(MethodDescriptorProto* method,
+                                const LocationRecorder& method_location,
+                                const FileDescriptorProto* containing_file) {
+  DO(Consume("rpc"));
+
+  {
+    LocationRecorder location(method_location,
+                              MethodDescriptorProto::kNameFieldNumber);
+    location.RecordLegacyLocation(method, DescriptorPool::ErrorCollector::NAME);
+    DO(ConsumeIdentifier(method->mutable_name(), "Expected method name."));
+  }
+
+  // Parse input type.
+  DO(Consume("("));
+  {
+    if (LookingAt("stream")) {
+      LocationRecorder location(
+          method_location, MethodDescriptorProto::kClientStreamingFieldNumber);
+      location.RecordLegacyLocation(method,
+                                    DescriptorPool::ErrorCollector::OTHER);
+      method->set_client_streaming(true);
+      DO(Consume("stream"));
+    }
+    LocationRecorder location(method_location,
+                              MethodDescriptorProto::kInputTypeFieldNumber);
+    location.RecordLegacyLocation(method,
+                                  DescriptorPool::ErrorCollector::INPUT_TYPE);
+    DO(ParseUserDefinedType(method->mutable_input_type()));
+  }
+  DO(Consume(")"));
+
+  // Parse output type.
+  DO(Consume("returns"));
+  DO(Consume("("));
+  {
+    if (LookingAt("stream")) {
+      LocationRecorder location(
+          method_location, MethodDescriptorProto::kServerStreamingFieldNumber);
+      location.RecordLegacyLocation(method,
+                                    DescriptorPool::ErrorCollector::OTHER);
+      DO(Consume("stream"));
+      method->set_server_streaming(true);
+    }
+    LocationRecorder location(method_location,
+                              MethodDescriptorProto::kOutputTypeFieldNumber);
+    location.RecordLegacyLocation(method,
+                                  DescriptorPool::ErrorCollector::OUTPUT_TYPE);
+    DO(ParseUserDefinedType(method->mutable_output_type()));
+  }
+  DO(Consume(")"));
+
+  if (LookingAt("{")) {
+    // Options!
+    DO(ParseMethodOptions(method_location, containing_file,
+                          MethodDescriptorProto::kOptionsFieldNumber,
+                          method->mutable_options()));
+  } else {
+    DO(ConsumeEndOfDeclaration(";", &method_location));
+  }
+
+  return true;
+}
+
+bool Parser::ParseMethodOptions(const LocationRecorder& parent_location,
+                                const FileDescriptorProto* containing_file,
+                                const int optionsFieldNumber,
+                                Message* mutable_options) {
+  // Options!
+  ConsumeEndOfDeclaration("{", &parent_location);
+  while (!TryConsumeEndOfDeclaration("}", nullptr)) {
+    if (AtEnd()) {
+      AddError("Reached end of input in method options (missing '}').");
+      return false;
+    }
+
+    if (TryConsumeEndOfDeclaration(";", nullptr)) {
+      // empty statement; ignore
+    } else {
+      LocationRecorder location(parent_location, optionsFieldNumber);
+      if (!ParseOption(mutable_options, location, containing_file,
+                       OPTION_STATEMENT)) {
+        // This statement failed to parse.  Skip it, but keep looping to
+        // parse other statements.
+        SkipStatement();
+      }
+    }
+  }
+
+  return true;
+}
+
+// -------------------------------------------------------------------
+
+bool Parser::ParseLabel(FieldDescriptorProto::Label* label,
+                        const LocationRecorder& field_location) {
+  if (!LookingAt("optional") && !LookingAt("repeated") &&
+      !LookingAt("required")) {
+    return false;
+  }
+  LocationRecorder location(field_location,
+                            FieldDescriptorProto::kLabelFieldNumber);
+  if (TryConsume("optional")) {
+    *label = FieldDescriptorProto::LABEL_OPTIONAL;
+  } else if (TryConsume("repeated")) {
+    *label = FieldDescriptorProto::LABEL_REPEATED;
+  } else {
+    Consume("required");
+    *label = FieldDescriptorProto::LABEL_REQUIRED;
+  }
+  return true;
+}
+
+bool Parser::ParseType(FieldDescriptorProto::Type* type,
+                       std::string* type_name) {
+  const auto& type_names_table = GetTypeNameTable();
+  auto iter = type_names_table.find(input_->current().text);
+  if (iter != type_names_table.end()) {
+    *type = iter->second;
+    input_->Next();
+  } else {
+    DO(ParseUserDefinedType(type_name));
+  }
+  return true;
+}
+
+bool Parser::ParseUserDefinedType(std::string* type_name) {
+  type_name->clear();
+
+  const auto& type_names_table = GetTypeNameTable();
+  auto iter = type_names_table.find(input_->current().text);
+  if (iter != type_names_table.end()) {
+    // Note:  The only place enum types are allowed is for field types, but
+    //   if we are parsing a field type then we would not get here because
+    //   primitives are allowed there as well.  So this error message doesn't
+    //   need to account for enums.
+    AddError("Expected message type.");
+
+    // Pretend to accept this type so that we can go on parsing.
+    *type_name = input_->current().text;
+    input_->Next();
+    return true;
+  }
+
+  // A leading "." means the name is fully-qualified.
+  if (TryConsume(".")) type_name->append(".");
+
+  // Consume the first part of the name.
+  std::string identifier;
+  DO(ConsumeIdentifier(&identifier, "Expected type name."));
+  type_name->append(identifier);
+
+  // Consume more parts.
+  while (TryConsume(".")) {
+    type_name->append(".");
+    DO(ConsumeIdentifier(&identifier, "Expected identifier."));
+    type_name->append(identifier);
+  }
+
+  return true;
+}
+
+// ===================================================================
+
+bool Parser::ParsePackage(FileDescriptorProto* file,
+                          const LocationRecorder& root_location,
+                          const FileDescriptorProto* containing_file) {
+  if (file->has_package()) {
+    AddError("Multiple package definitions.");
+    // Don't append the new package to the old one.  Just replace it.  Not
+    // that it really matters since this is an error anyway.
+    file->clear_package();
+  }
+
+  LocationRecorder location(root_location,
+                            FileDescriptorProto::kPackageFieldNumber);
+  location.RecordLegacyLocation(file, DescriptorPool::ErrorCollector::NAME);
+
+  DO(Consume("package"));
+
+  while (true) {
+    std::string identifier;
+    DO(ConsumeIdentifier(&identifier, "Expected identifier."));
+    file->mutable_package()->append(identifier);
+    if (!TryConsume(".")) break;
+    file->mutable_package()->append(".");
+  }
+
+  DO(ConsumeEndOfDeclaration(";", &location));
+
+  return true;
+}
+
+bool Parser::ParseImport(RepeatedPtrField<std::string>* dependency,
+                         RepeatedField<int32_t>* public_dependency,
+                         RepeatedField<int32_t>* weak_dependency,
+                         const LocationRecorder& root_location,
+                         const FileDescriptorProto* containing_file) {
+  LocationRecorder location(root_location,
+                            FileDescriptorProto::kDependencyFieldNumber,
+                            dependency->size());
+
+  DO(Consume("import"));
+
+  if (LookingAt("public")) {
+    LocationRecorder public_location(
+        root_location, FileDescriptorProto::kPublicDependencyFieldNumber,
+        public_dependency->size());
+    DO(Consume("public"));
+    *public_dependency->Add() = dependency->size();
+  } else if (LookingAt("weak")) {
+    LocationRecorder weak_location(
+        root_location, FileDescriptorProto::kWeakDependencyFieldNumber,
+        weak_dependency->size());
+    weak_location.RecordLegacyImportLocation(containing_file, "weak");
+    DO(Consume("weak"));
+    *weak_dependency->Add() = dependency->size();
+  }
+
+  std::string import_file;
+  DO(ConsumeString(&import_file,
+                   "Expected a string naming the file to import."));
+  *dependency->Add() = import_file;
+  location.RecordLegacyImportLocation(containing_file, import_file);
+
+  DO(ConsumeEndOfDeclaration(";", &location));
+
+  return true;
+}
+
+// ===================================================================
+
+SourceLocationTable::SourceLocationTable() {}
+SourceLocationTable::~SourceLocationTable() {}
+
+bool SourceLocationTable::Find(
+    const Message* descriptor,
+    DescriptorPool::ErrorCollector::ErrorLocation location, int* line,
+    int* column) const {
+  const std::pair<int, int>* result =
+      FindOrNull(location_map_, std::make_pair(descriptor, location));
+  if (result == nullptr) {
+    *line = -1;
+    *column = 0;
+    return false;
+  } else {
+    *line = result->first;
+    *column = result->second;
+    return true;
+  }
+}
+
+bool SourceLocationTable::FindImport(const Message* descriptor,
+                                     const std::string& name, int* line,
+                                     int* column) const {
+  const std::pair<int, int>* result =
+      FindOrNull(import_location_map_, std::make_pair(descriptor, name));
+  if (result == nullptr) {
+    *line = -1;
+    *column = 0;
+    return false;
+  } else {
+    *line = result->first;
+    *column = result->second;
+    return true;
+  }
+}
+
+void SourceLocationTable::Add(
+    const Message* descriptor,
+    DescriptorPool::ErrorCollector::ErrorLocation location, int line,
+    int column) {
+  location_map_[std::make_pair(descriptor, location)] =
+      std::make_pair(line, column);
+}
+
+void SourceLocationTable::AddImport(const Message* descriptor,
+                                    const std::string& name, int line,
+                                    int column) {
+  import_location_map_[std::make_pair(descriptor, name)] =
+      std::make_pair(line, column);
+}
+
+void SourceLocationTable::Clear() { location_map_.clear(); }
+
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/descriptor.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/descriptor.cpp
new file mode 100644
index 0000000..5f3427d
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/descriptor.cpp
@@ -0,0 +1,8340 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/descriptor.h>
+
+#include <algorithm>
+#include <array>
+#include <functional>
+#include <limits>
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/any.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/tokenizer.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/stubs/substitute.h>
+#include <google/protobuf/descriptor_database.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/io/strtod.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/stl_util.h>
+#include <google/protobuf/stubs/hash.h>
+
+#undef PACKAGE  // autoheader #defines this.  :(
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+namespace {
+const int kPackageLimit = 100;
+
+// Note:  I distrust ctype.h due to locales.
+char ToUpper(char ch) {
+  return (ch >= 'a' && ch <= 'z') ? (ch - 'a' + 'A') : ch;
+}
+
+char ToLower(char ch) {
+  return (ch >= 'A' && ch <= 'Z') ? (ch - 'A' + 'a') : ch;
+}
+
+std::string ToCamelCase(const std::string& input, bool lower_first) {
+  bool capitalize_next = !lower_first;
+  std::string result;
+  result.reserve(input.size());
+
+  for (char character : input) {
+    if (character == '_') {
+      capitalize_next = true;
+    } else if (capitalize_next) {
+      result.push_back(ToUpper(character));
+      capitalize_next = false;
+    } else {
+      result.push_back(character);
+    }
+  }
+
+  // Lower-case the first letter.
+  if (lower_first && !result.empty()) {
+    result[0] = ToLower(result[0]);
+  }
+
+  return result;
+}
+
+std::string ToJsonName(const std::string& input) {
+  bool capitalize_next = false;
+  std::string result;
+  result.reserve(input.size());
+
+  for (char character : input) {
+    if (character == '_') {
+      capitalize_next = true;
+    } else if (capitalize_next) {
+      result.push_back(ToUpper(character));
+      capitalize_next = false;
+    } else {
+      result.push_back(character);
+    }
+  }
+
+  return result;
+}
+
+// Backport of fold expressions for the comma operator to C++11.
+// Usage:  Fold({expr...});
+// Guaranteed to evaluate left-to-right
+struct ExpressionEater {
+  template <typename T>
+  ExpressionEater(T&&) {}  // NOLINT
+};
+void Fold(std::initializer_list<ExpressionEater>) {}
+
+template <int R>
+constexpr size_t RoundUpTo(size_t n) {
+  static_assert((R & (R - 1)) == 0, "Must be power of two");
+  return (n + (R - 1)) & ~(R - 1);
+}
+
+constexpr size_t Max(size_t a, size_t b) { return a > b ? a : b; }
+template <typename T, typename... Ts>
+constexpr size_t Max(T a, Ts... b) {
+  return Max(a, Max(b...));
+}
+
+template <typename T>
+constexpr size_t EffectiveAlignof() {
+  // `char` is special in that it gets aligned to 8. It is where we drop the
+  // trivial structs.
+  return std::is_same<T, char>::value ? 8 : alignof(T);
+}
+
+template <int align, typename U, typename... T>
+using AppendIfAlign =
+    typename std::conditional<EffectiveAlignof<U>() == align, void (*)(T..., U),
+                              void (*)(T...)>::type;
+
+// Metafunction to sort types in descending order of alignment.
+// Useful for the flat allocator to ensure proper alignment of all elements
+// without having to add padding.
+// Instead of implementing a proper sort metafunction we just do a
+// filter+merge, which is much simpler to write as a metafunction.
+// We have a fixed set of alignments we can filter on.
+// For simplicity we use a function pointer as a type list.
+template <typename In, typename T16, typename T8, typename T4, typename T2,
+          typename T1>
+struct TypeListSortImpl;
+
+template <typename... T16, typename... T8, typename... T4, typename... T2,
+          typename... T1>
+struct TypeListSortImpl<void (*)(), void (*)(T16...), void (*)(T8...),
+                        void (*)(T4...), void (*)(T2...), void (*)(T1...)> {
+  using type = void (*)(T16..., T8..., T4..., T2..., T1...);
+};
+
+template <typename First, typename... Rest, typename... T16, typename... T8,
+          typename... T4, typename... T2, typename... T1>
+struct TypeListSortImpl<void (*)(First, Rest...), void (*)(T16...),
+                        void (*)(T8...), void (*)(T4...), void (*)(T2...),
+                        void (*)(T1...)> {
+  using type = typename TypeListSortImpl<
+      void (*)(Rest...), AppendIfAlign<16, First, T16...>,
+      AppendIfAlign<8, First, T8...>, AppendIfAlign<4, First, T4...>,
+      AppendIfAlign<2, First, T2...>, AppendIfAlign<1, First, T1...>>::type;
+};
+
+template <typename... T>
+using SortByAlignment =
+    typename TypeListSortImpl<void (*)(T...), void (*)(), void (*)(),
+                              void (*)(), void (*)(), void (*)()>::type;
+
+template <template <typename...> class C, typename... T>
+auto ApplyTypeList(void (*)(T...)) -> C<T...>;
+
+template <typename T>
+constexpr int FindTypeIndex() {
+  return -1;
+}
+
+template <typename T, typename T1, typename... Ts>
+constexpr int FindTypeIndex() {
+  return std::is_same<T, T1>::value ? 0 : FindTypeIndex<T, Ts...>() + 1;
+}
+
+// A type to value map, where the possible keys as specified in `Keys...`.
+// The values for key `K` is `ValueT<K>`
+template <template <typename> class ValueT, typename... Keys>
+class TypeMap {
+ public:
+  template <typename K>
+  ValueT<K>& Get() {
+    return static_cast<Base<K>&>(payload_).value;
+  }
+
+  template <typename K>
+  const ValueT<K>& Get() const {
+    return static_cast<const Base<K>&>(payload_).value;
+  }
+
+ private:
+  template <typename K>
+  struct Base {
+    ValueT<K> value{};
+  };
+  struct Payload : Base<Keys>... {};
+  Payload payload_;
+};
+
+template <typename T>
+using IntT = int;
+template <typename T>
+using PointerT = T*;
+
+// Manages an allocation of sequential arrays of type `T...`.
+// It is more space efficient than storing N (ptr, size) pairs, by storing only
+// the pointer to the head and the boundaries between the arrays.
+template <typename... T>
+class FlatAllocation {
+ public:
+  static constexpr size_t kMaxAlign = Max(alignof(T)...);
+
+  FlatAllocation(const TypeMap<IntT, T...>& ends) : ends_(ends) {
+    // The arrays start just after FlatAllocation, so adjust the ends.
+    Fold({(ends_.template Get<T>() +=
+           RoundUpTo<kMaxAlign>(sizeof(FlatAllocation)))...});
+    Fold({Init<T>()...});
+  }
+
+  void Destroy() {
+    Fold({Destroy<T>()...});
+    internal::SizedDelete(this, total_bytes());
+  }
+
+  template <int I>
+  using type = typename std::tuple_element<I, std::tuple<T...>>::type;
+
+  // Gets a tuple of the head pointers for the arrays
+  TypeMap<PointerT, T...> Pointers() const {
+    TypeMap<PointerT, T...> out;
+    Fold({(out.template Get<T>() = Begin<T>())...});
+    return out;
+  }
+
+
+ private:
+  // Total number of bytes used by all arrays.
+  int total_bytes() const {
+    // Get the last end.
+    return ends_.template Get<typename std::tuple_element<
+        sizeof...(T) - 1, std::tuple<T...>>::type>();
+  }
+
+
+  template <typename U>
+  int BeginOffset() const {
+    constexpr int type_index = FindTypeIndex<U, T...>();
+    // Avoid a negative value here to keep it compiling when type_index == 0
+    constexpr int prev_type_index = type_index == 0 ? 0 : type_index - 1;
+    using PrevType =
+        typename std::tuple_element<prev_type_index, std::tuple<T...>>::type;
+    // Ensure the types are properly aligned.
+    static_assert(EffectiveAlignof<PrevType>() >= EffectiveAlignof<U>(), "");
+    return type_index == 0 ? RoundUpTo<kMaxAlign>(sizeof(FlatAllocation))
+                           : ends_.template Get<PrevType>();
+  }
+
+  template <typename U>
+  int EndOffset() const {
+    return ends_.template Get<U>();
+  }
+
+  // Avoid the reinterpret_cast if the array is empty.
+  // Clang's Control Flow Integrity does not like the cast pointing to memory
+  // that is not yet initialized to be of that type.
+  // (from -fsanitize=cfi-unrelated-cast)
+  template <typename U>
+  U* Begin() const {
+    int begin = BeginOffset<U>(), end = EndOffset<U>();
+    if (begin == end) return nullptr;
+    return reinterpret_cast<U*>(data() + begin);
+  }
+
+  template <typename U>
+  U* End() const {
+    int begin = BeginOffset<U>(), end = EndOffset<U>();
+    if (begin == end) return nullptr;
+    return reinterpret_cast<U*>(data() + end);
+  }
+
+  template <typename U>
+  bool Init() {
+    // Skip for the `char` block. No need to zero initialize it.
+    if (std::is_same<U, char>::value) return true;
+    for (char *p = data() + BeginOffset<U>(), *end = data() + EndOffset<U>();
+         p != end; p += sizeof(U)) {
+      ::new (p) U{};
+    }
+    return true;
+  }
+
+  template <typename U>
+  bool Destroy() {
+    if (std::is_trivially_destructible<U>::value) return true;
+    for (U* it = Begin<U>(), *end = End<U>(); it != end; ++it) {
+      it->~U();
+    }
+    return true;
+  }
+
+  char* data() const {
+    return const_cast<char*>(reinterpret_cast<const char*>(this));
+  }
+
+  TypeMap<IntT, T...> ends_;
+};
+
+template <typename... T>
+TypeMap<IntT, T...> CalculateEnds(const TypeMap<IntT, T...>& sizes) {
+  int total = 0;
+  TypeMap<IntT, T...> out;
+  Fold({(out.template Get<T>() = total +=
+         sizeof(T) * sizes.template Get<T>())...});
+  return out;
+}
+
+// The implementation for FlatAllocator below.
+// This separate class template makes it easier to have methods that fold on
+// `T...`.
+template <typename... T>
+class FlatAllocatorImpl {
+ public:
+  using Allocation = FlatAllocation<T...>;
+
+  template <typename U>
+  void PlanArray(int array_size) {
+    // We can't call PlanArray after FinalizePlanning has been called.
+    GOOGLE_CHECK(!has_allocated());
+    if (std::is_trivially_destructible<U>::value) {
+      // Trivial types are aligned to 8 bytes.
+      static_assert(alignof(U) <= 8, "");
+      total_.template Get<char>() += RoundUpTo<8>(array_size * sizeof(U));
+    } else {
+      // Since we can't use `if constexpr`, just make the expression compile
+      // when this path is not taken.
+      using TypeToUse =
+          typename std::conditional<std::is_trivially_destructible<U>::value,
+                                    char, U>::type;
+      total_.template Get<TypeToUse>() += array_size;
+    }
+  }
+
+  template <typename U>
+  U* AllocateArray(int array_size) {
+    constexpr bool trivial = std::is_trivially_destructible<U>::value;
+    using TypeToUse = typename std::conditional<trivial, char, U>::type;
+
+    // We can only allocate after FinalizePlanning has been called.
+    GOOGLE_CHECK(has_allocated());
+
+    TypeToUse*& data = pointers_.template Get<TypeToUse>();
+    int& used = used_.template Get<TypeToUse>();
+    U* res = reinterpret_cast<U*>(data + used);
+    used += trivial ? RoundUpTo<8>(array_size * sizeof(U)) : array_size;
+    GOOGLE_CHECK_LE(used, total_.template Get<TypeToUse>());
+    return res;
+  }
+
+  template <typename... In>
+  const std::string* AllocateStrings(In&&... in) {
+    std::string* strings = AllocateArray<std::string>(sizeof...(in));
+    std::string* res = strings;
+    Fold({(*strings++ = std::string(std::forward<In>(in)))...});
+    return res;
+  }
+
+  // Allocate all 5 names of the field:
+  // name, full name, lowercase, camelcase and json.
+  // It will dedup the strings when possible.
+  // The resulting array contains `name` at index 0, `full_name` at index 1
+  // and the other 3 indices are specified in the result.
+  void PlanFieldNames(const std::string& name,
+                      const std::string* opt_json_name) {
+    GOOGLE_CHECK(!has_allocated());
+
+    // Fast path for snake_case names, which follow the style guide.
+    if (opt_json_name == nullptr) {
+      switch (GetFieldNameCase(name)) {
+        case FieldNameCase::kAllLower:
+          // Case 1: they are all the same.
+          return PlanArray<std::string>(2);
+        case FieldNameCase::kSnakeCase:
+          // Case 2: name==lower, camel==json
+          return PlanArray<std::string>(3);
+        default:
+          break;
+      }
+    }
+
+    std::string lowercase_name = name;
+    LowerString(&lowercase_name);
+
+    std::string camelcase_name = ToCamelCase(name, /* lower_first = */ true);
+    std::string json_name =
+        opt_json_name != nullptr ? *opt_json_name : ToJsonName(name);
+
+    StringPiece all_names[] = {name, lowercase_name, camelcase_name,
+                                     json_name};
+    std::sort(all_names, all_names + 4);
+    int unique =
+        static_cast<int>(std::unique(all_names, all_names + 4) - all_names);
+
+    PlanArray<std::string>(unique + 1);
+  }
+
+  struct FieldNamesResult {
+    const std::string* array;
+    int lowercase_index;
+    int camelcase_index;
+    int json_index;
+  };
+  FieldNamesResult AllocateFieldNames(const std::string& name,
+                                      const std::string& scope,
+                                      const std::string* opt_json_name) {
+    GOOGLE_CHECK(has_allocated());
+
+    std::string full_name =
+        scope.empty() ? name : StrCat(scope, ".", name);
+
+    // Fast path for snake_case names, which follow the style guide.
+    if (opt_json_name == nullptr) {
+      switch (GetFieldNameCase(name)) {
+        case FieldNameCase::kAllLower:
+          // Case 1: they are all the same.
+          return {AllocateStrings(name, std::move(full_name)), 0, 0, 0};
+        case FieldNameCase::kSnakeCase:
+          // Case 2: name==lower, camel==json
+          return {AllocateStrings(name, std::move(full_name),
+                                  ToCamelCase(name, /* lower_first = */ true)),
+                  0, 2, 2};
+        default:
+          break;
+      }
+    }
+
+    std::vector<std::string> names;
+    names.push_back(name);
+    names.push_back(std::move(full_name));
+
+    const auto push_name = [&](std::string new_name) {
+      for (size_t i = 0; i < names.size(); ++i) {
+        // Do not compare the full_name. It is unlikely to match, except in
+        // custom json_name. We are not taking this into account in
+        // PlanFieldNames so better to not try it.
+        if (i == 1) continue;
+        if (names[i] == new_name) return i;
+      }
+      names.push_back(std::move(new_name));
+      return names.size() - 1;
+    };
+
+    FieldNamesResult result{nullptr, 0, 0, 0};
+
+    std::string lowercase_name = name;
+    LowerString(&lowercase_name);
+    result.lowercase_index = push_name(std::move(lowercase_name));
+    result.camelcase_index =
+        push_name(ToCamelCase(name, /* lower_first = */ true));
+    result.json_index =
+        push_name(opt_json_name != nullptr ? *opt_json_name : ToJsonName(name));
+
+    std::string* all_names = AllocateArray<std::string>(names.size());
+    result.array = all_names;
+    std::move(names.begin(), names.end(), all_names);
+
+    return result;
+  }
+
+  template <typename Alloc>
+  void FinalizePlanning(Alloc& alloc) {
+    GOOGLE_CHECK(!has_allocated());
+
+    pointers_ = alloc->CreateFlatAlloc(total_)->Pointers();
+
+    GOOGLE_CHECK(has_allocated());
+  }
+
+  void ExpectConsumed() const {
+    // We verify that we consumed all the memory requested if there was no
+    // error in processing.
+    Fold({ExpectConsumed<T>()...});
+  }
+
+ private:
+  bool has_allocated() const {
+    return pointers_.template Get<char>() != nullptr;
+  }
+
+  static bool IsLower(char c) { return 'a' <= c && c <= 'z'; }
+  static bool IsDigit(char c) { return '0' <= c && c <= '9'; }
+  static bool IsLowerOrDigit(char c) { return IsLower(c) || IsDigit(c); }
+
+  enum class FieldNameCase { kAllLower, kSnakeCase, kOther };
+  FieldNameCase GetFieldNameCase(const std::string& name) {
+    if (!IsLower(name[0])) return FieldNameCase::kOther;
+    FieldNameCase best = FieldNameCase::kAllLower;
+    for (char c : name) {
+      if (IsLowerOrDigit(c)) {
+        // nothing to do
+      } else if (c == '_') {
+        best = FieldNameCase::kSnakeCase;
+      } else {
+        return FieldNameCase::kOther;
+      }
+    }
+    return best;
+  }
+
+  template <typename U>
+  bool ExpectConsumed() const {
+    GOOGLE_CHECK_EQ(total_.template Get<U>(), used_.template Get<U>());
+    return true;
+  }
+
+  TypeMap<PointerT, T...> pointers_;
+  TypeMap<IntT, T...> total_;
+  TypeMap<IntT, T...> used_;
+};
+
+}  // namespace
+
+class Symbol {
+ public:
+  enum Type {
+    NULL_SYMBOL,
+    MESSAGE,
+    FIELD,
+    ONEOF,
+    ENUM,
+    ENUM_VALUE,
+    ENUM_VALUE_OTHER_PARENT,
+    SERVICE,
+    METHOD,
+    FULL_PACKAGE,
+    SUB_PACKAGE,
+    QUERY_KEY
+  };
+
+  Symbol() {
+    static constexpr internal::SymbolBase null_symbol{};
+    static_assert(null_symbol.symbol_type_ == NULL_SYMBOL, "");
+    // Initialize with a sentinel to make sure `ptr_` is never null.
+    ptr_ = &null_symbol;
+  }
+
+  // Every object we store derives from internal::SymbolBase, where we store the
+  // symbol type enum.
+  // Storing in the object can be done without using more space in most cases,
+  // while storing it in the Symbol type would require 8 bytes.
+#define DEFINE_MEMBERS(TYPE, TYPE_CONSTANT, FIELD)                             \
+  explicit Symbol(TYPE* value) : ptr_(value) {                                 \
+    value->symbol_type_ = TYPE_CONSTANT;                                       \
+  }                                                                            \
+  const TYPE* FIELD() const {                                                  \
+    return type() == TYPE_CONSTANT ? static_cast<const TYPE*>(ptr_) : nullptr; \
+  }
+
+  DEFINE_MEMBERS(Descriptor, MESSAGE, descriptor)
+  DEFINE_MEMBERS(FieldDescriptor, FIELD, field_descriptor)
+  DEFINE_MEMBERS(OneofDescriptor, ONEOF, oneof_descriptor)
+  DEFINE_MEMBERS(EnumDescriptor, ENUM, enum_descriptor)
+  DEFINE_MEMBERS(ServiceDescriptor, SERVICE, service_descriptor)
+  DEFINE_MEMBERS(MethodDescriptor, METHOD, method_descriptor)
+  DEFINE_MEMBERS(FileDescriptor, FULL_PACKAGE, file_descriptor)
+
+  // We use a special node for subpackage FileDescriptor.
+  // It is potentially added to the table with multiple different names, so we
+  // need a separate place to put the name.
+  struct Subpackage : internal::SymbolBase {
+    int name_size;
+    const FileDescriptor* file;
+  };
+  DEFINE_MEMBERS(Subpackage, SUB_PACKAGE, sub_package_file_descriptor)
+
+  // Enum values have two different parents.
+  // We use two different identitied for the same object to determine the two
+  // different insertions in the map.
+  static Symbol EnumValue(EnumValueDescriptor* value, int n) {
+    Symbol s;
+    internal::SymbolBase* ptr;
+    if (n == 0) {
+      ptr = static_cast<internal::SymbolBaseN<0>*>(value);
+      ptr->symbol_type_ = ENUM_VALUE;
+    } else {
+      ptr = static_cast<internal::SymbolBaseN<1>*>(value);
+      ptr->symbol_type_ = ENUM_VALUE_OTHER_PARENT;
+    }
+    s.ptr_ = ptr;
+    return s;
+  }
+
+  const EnumValueDescriptor* enum_value_descriptor() const {
+    return type() == ENUM_VALUE
+               ? static_cast<const EnumValueDescriptor*>(
+                     static_cast<const internal::SymbolBaseN<0>*>(ptr_))
+           : type() == ENUM_VALUE_OTHER_PARENT
+               ? static_cast<const EnumValueDescriptor*>(
+                     static_cast<const internal::SymbolBaseN<1>*>(ptr_))
+               : nullptr;
+  }
+
+  // Not a real symbol.
+  // Only used for heterogeneous lookups and never actually inserted in the
+  // tables.
+  // TODO(b/215557658): If we templetize QueryKey on the expected object type we
+  // can skip the switches for the eq function altogether.
+  struct QueryKey : internal::SymbolBase {
+    StringPiece name;
+    const void* parent;
+    int field_number;
+
+    // Adaptor functions to look like a Symbol to the comparators.
+    StringPiece full_name() const { return name; }
+    std::pair<const void*, int> parent_number_key() const {
+      return {parent, field_number};
+    }
+    std::pair<const void*, StringPiece> parent_name_key() const {
+      return {parent, name};
+    }
+  };
+  // This constructor is implicit to allow for non-transparent lookups when
+  // necessary.
+  // For transparent lookup cases we query directly with the object without the
+  // type erasure layer.
+  Symbol(QueryKey& value) : ptr_(&value) {  // NOLINT
+    value.symbol_type_ = QUERY_KEY;
+  }
+  const QueryKey* query_key() const {
+    return type() == QUERY_KEY ? static_cast<const QueryKey*>(ptr_) : nullptr;
+  }
+#undef DEFINE_MEMBERS
+
+  Type type() const { return static_cast<Type>(ptr_->symbol_type_); }
+  bool IsNull() const { return type() == NULL_SYMBOL; }
+  bool IsType() const { return type() == MESSAGE || type() == ENUM; }
+  bool IsAggregate() const {
+    return IsType() || IsPackage() || type() == SERVICE;
+  }
+  bool IsPackage() const {
+    return type() == FULL_PACKAGE || type() == SUB_PACKAGE;
+  }
+
+  const FileDescriptor* GetFile() const {
+    switch (type()) {
+      case MESSAGE:
+        return descriptor()->file();
+      case FIELD:
+        return field_descriptor()->file();
+      case ONEOF:
+        return oneof_descriptor()->containing_type()->file();
+      case ENUM:
+        return enum_descriptor()->file();
+      case ENUM_VALUE:
+        return enum_value_descriptor()->type()->file();
+      case SERVICE:
+        return service_descriptor()->file();
+      case METHOD:
+        return method_descriptor()->service()->file();
+      case FULL_PACKAGE:
+        return file_descriptor();
+      case SUB_PACKAGE:
+        return sub_package_file_descriptor()->file;
+      default:
+        return nullptr;
+    }
+  }
+
+  StringPiece full_name() const {
+    switch (type()) {
+      case MESSAGE:
+        return descriptor()->full_name();
+      case FIELD:
+        return field_descriptor()->full_name();
+      case ONEOF:
+        return oneof_descriptor()->full_name();
+      case ENUM:
+        return enum_descriptor()->full_name();
+      case ENUM_VALUE:
+        return enum_value_descriptor()->full_name();
+      case SERVICE:
+        return service_descriptor()->full_name();
+      case METHOD:
+        return method_descriptor()->full_name();
+      case FULL_PACKAGE:
+        return file_descriptor()->package();
+      case SUB_PACKAGE:
+        return StringPiece(sub_package_file_descriptor()->file->package())
+            .substr(0, sub_package_file_descriptor()->name_size);
+      case QUERY_KEY:
+        return query_key()->full_name();
+      default:
+        GOOGLE_CHECK(false);
+    }
+    return "";
+  }
+
+  std::pair<const void*, StringPiece> parent_name_key() const {
+    const auto or_file = [&](const void* p) { return p ? p : GetFile(); };
+    switch (type()) {
+      case MESSAGE:
+        return {or_file(descriptor()->containing_type()), descriptor()->name()};
+      case FIELD: {
+        auto* field = field_descriptor();
+        return {or_file(field->is_extension() ? field->extension_scope()
+                                              : field->containing_type()),
+                field->name()};
+      }
+      case ONEOF:
+        return {oneof_descriptor()->containing_type(),
+                oneof_descriptor()->name()};
+      case ENUM:
+        return {or_file(enum_descriptor()->containing_type()),
+                enum_descriptor()->name()};
+      case ENUM_VALUE:
+        return {or_file(enum_value_descriptor()->type()->containing_type()),
+                enum_value_descriptor()->name()};
+      case ENUM_VALUE_OTHER_PARENT:
+        return {enum_value_descriptor()->type(),
+                enum_value_descriptor()->name()};
+      case SERVICE:
+        return {GetFile(), service_descriptor()->name()};
+      case METHOD:
+        return {method_descriptor()->service(), method_descriptor()->name()};
+      case QUERY_KEY:
+        return query_key()->parent_name_key();
+      default:
+        GOOGLE_CHECK(false);
+    }
+    return {};
+  }
+
+  std::pair<const void*, int> parent_number_key() const {
+    switch (type()) {
+      case FIELD:
+        return {field_descriptor()->containing_type(),
+                field_descriptor()->number()};
+      case ENUM_VALUE:
+        return {enum_value_descriptor()->type(),
+                enum_value_descriptor()->number()};
+      case QUERY_KEY:
+        return query_key()->parent_number_key();
+      default:
+        GOOGLE_CHECK(false);
+    }
+    return {};
+  }
+
+ private:
+  const internal::SymbolBase* ptr_;
+};
+
+const FieldDescriptor::CppType
+    FieldDescriptor::kTypeToCppTypeMap[MAX_TYPE + 1] = {
+        static_cast<CppType>(0),  // 0 is reserved for errors
+
+        CPPTYPE_DOUBLE,   // TYPE_DOUBLE
+        CPPTYPE_FLOAT,    // TYPE_FLOAT
+        CPPTYPE_INT64,    // TYPE_INT64
+        CPPTYPE_UINT64,   // TYPE_UINT64
+        CPPTYPE_INT32,    // TYPE_INT32
+        CPPTYPE_UINT64,   // TYPE_FIXED64
+        CPPTYPE_UINT32,   // TYPE_FIXED32
+        CPPTYPE_BOOL,     // TYPE_BOOL
+        CPPTYPE_STRING,   // TYPE_STRING
+        CPPTYPE_MESSAGE,  // TYPE_GROUP
+        CPPTYPE_MESSAGE,  // TYPE_MESSAGE
+        CPPTYPE_STRING,   // TYPE_BYTES
+        CPPTYPE_UINT32,   // TYPE_UINT32
+        CPPTYPE_ENUM,     // TYPE_ENUM
+        CPPTYPE_INT32,    // TYPE_SFIXED32
+        CPPTYPE_INT64,    // TYPE_SFIXED64
+        CPPTYPE_INT32,    // TYPE_SINT32
+        CPPTYPE_INT64,    // TYPE_SINT64
+};
+
+const char* const FieldDescriptor::kTypeToName[MAX_TYPE + 1] = {
+    "ERROR",  // 0 is reserved for errors
+
+    "double",    // TYPE_DOUBLE
+    "float",     // TYPE_FLOAT
+    "int64",     // TYPE_INT64
+    "uint64",    // TYPE_UINT64
+    "int32",     // TYPE_INT32
+    "fixed64",   // TYPE_FIXED64
+    "fixed32",   // TYPE_FIXED32
+    "bool",      // TYPE_BOOL
+    "string",    // TYPE_STRING
+    "group",     // TYPE_GROUP
+    "message",   // TYPE_MESSAGE
+    "bytes",     // TYPE_BYTES
+    "uint32",    // TYPE_UINT32
+    "enum",      // TYPE_ENUM
+    "sfixed32",  // TYPE_SFIXED32
+    "sfixed64",  // TYPE_SFIXED64
+    "sint32",    // TYPE_SINT32
+    "sint64",    // TYPE_SINT64
+};
+
+const char* const FieldDescriptor::kCppTypeToName[MAX_CPPTYPE + 1] = {
+    "ERROR",  // 0 is reserved for errors
+
+    "int32",    // CPPTYPE_INT32
+    "int64",    // CPPTYPE_INT64
+    "uint32",   // CPPTYPE_UINT32
+    "uint64",   // CPPTYPE_UINT64
+    "double",   // CPPTYPE_DOUBLE
+    "float",    // CPPTYPE_FLOAT
+    "bool",     // CPPTYPE_BOOL
+    "enum",     // CPPTYPE_ENUM
+    "string",   // CPPTYPE_STRING
+    "message",  // CPPTYPE_MESSAGE
+};
+
+const char* const FieldDescriptor::kLabelToName[MAX_LABEL + 1] = {
+    "ERROR",  // 0 is reserved for errors
+
+    "optional",  // LABEL_OPTIONAL
+    "required",  // LABEL_REQUIRED
+    "repeated",  // LABEL_REPEATED
+};
+
+const char* FileDescriptor::SyntaxName(FileDescriptor::Syntax syntax) {
+  switch (syntax) {
+    case SYNTAX_PROTO2:
+      return "proto2";
+    case SYNTAX_PROTO3:
+      return "proto3";
+    case SYNTAX_UNKNOWN:
+      return "unknown";
+  }
+  GOOGLE_LOG(FATAL) << "can't reach here.";
+  return nullptr;
+}
+
+static const char* const kNonLinkedWeakMessageReplacementName = "google.protobuf.Empty";
+
+#if !defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)
+const int FieldDescriptor::kMaxNumber;
+const int FieldDescriptor::kFirstReservedNumber;
+const int FieldDescriptor::kLastReservedNumber;
+#endif
+
+namespace {
+
+std::string EnumValueToPascalCase(const std::string& input) {
+  bool next_upper = true;
+  std::string result;
+  result.reserve(input.size());
+
+  for (char character : input) {
+    if (character == '_') {
+      next_upper = true;
+    } else {
+      if (next_upper) {
+        result.push_back(ToUpper(character));
+      } else {
+        result.push_back(ToLower(character));
+      }
+      next_upper = false;
+    }
+  }
+
+  return result;
+}
+
+// Class to remove an enum prefix from enum values.
+class PrefixRemover {
+ public:
+  PrefixRemover(StringPiece prefix) {
+    // Strip underscores and lower-case the prefix.
+    for (char character : prefix) {
+      if (character != '_') {
+        prefix_ += ascii_tolower(character);
+      }
+    }
+  }
+
+  // Tries to remove the enum prefix from this enum value.
+  // If this is not possible, returns the input verbatim.
+  std::string MaybeRemove(StringPiece str) {
+    // We can't just lowercase and strip str and look for a prefix.
+    // We need to properly recognize the difference between:
+    //
+    //   enum Foo {
+    //     FOO_BAR_BAZ = 0;
+    //     FOO_BARBAZ = 1;
+    //   }
+    //
+    // This is acceptable (though perhaps not advisable) because even when
+    // we PascalCase, these two will still be distinct (BarBaz vs. Barbaz).
+    size_t i, j;
+
+    // Skip past prefix_ in str if we can.
+    for (i = 0, j = 0; i < str.size() && j < prefix_.size(); i++) {
+      if (str[i] == '_') {
+        continue;
+      }
+
+      if (ascii_tolower(str[i]) != prefix_[j++]) {
+        return std::string(str);
+      }
+    }
+
+    // If we didn't make it through the prefix, we've failed to strip the
+    // prefix.
+    if (j < prefix_.size()) {
+      return std::string(str);
+    }
+
+    // Skip underscores between prefix and further characters.
+    while (i < str.size() && str[i] == '_') {
+      i++;
+    }
+
+    // Enum label can't be the empty string.
+    if (i == str.size()) {
+      return std::string(str);
+    }
+
+    // We successfully stripped the prefix.
+    str.remove_prefix(i);
+    return std::string(str);
+  }
+
+ private:
+  std::string prefix_;
+};
+
+// A DescriptorPool contains a bunch of hash-maps to implement the
+// various Find*By*() methods.  Since hashtable lookups are O(1), it's
+// most efficient to construct a fixed set of large hash-maps used by
+// all objects in the pool rather than construct one or more small
+// hash-maps for each object.
+//
+// The keys to these hash-maps are (parent, name) or (parent, number) pairs.
+
+typedef std::pair<const void*, StringPiece> PointerStringPair;
+
+typedef std::pair<const Descriptor*, int> DescriptorIntPair;
+
+#define HASH_MAP std::unordered_map
+#define HASH_SET std::unordered_set
+#define HASH_FXN hash
+
+template <typename PairType>
+struct PointerIntegerPairHash {
+  size_t operator()(const PairType& p) const {
+    static const size_t prime1 = 16777499;
+    static const size_t prime2 = 16777619;
+    return reinterpret_cast<size_t>(p.first) * prime1 ^
+           static_cast<size_t>(p.second) * prime2;
+  }
+
+#ifdef _MSC_VER
+  // Used only by MSVC and platforms where hash_map is not available.
+  static const size_t bucket_size = 4;
+  static const size_t min_buckets = 8;
+#endif
+  inline bool operator()(const PairType& a, const PairType& b) const {
+    return a < b;
+  }
+};
+
+struct PointerStringPairHash {
+  size_t operator()(const PointerStringPair& p) const {
+    static const size_t prime = 16777619;
+    hash<StringPiece> string_hash;
+    return reinterpret_cast<size_t>(p.first) * prime ^
+           static_cast<size_t>(string_hash(p.second));
+  }
+
+#ifdef _MSC_VER
+  // Used only by MSVC and platforms where hash_map is not available.
+  static const size_t bucket_size = 4;
+  static const size_t min_buckets = 8;
+#endif
+  inline bool operator()(const PointerStringPair& a,
+                         const PointerStringPair& b) const {
+    return a < b;
+  }
+};
+
+
+struct SymbolByFullNameHash {
+  using is_transparent = void;
+
+  template <typename T>
+  size_t operator()(const T& s) const {
+    return HASH_FXN<StringPiece>{}(s.full_name());
+  }
+};
+struct SymbolByFullNameEq {
+  using is_transparent = void;
+
+  template <typename T, typename U>
+  bool operator()(const T& a, const U& b) const {
+    return a.full_name() == b.full_name();
+  }
+};
+using SymbolsByNameSet =
+    HASH_SET<Symbol, SymbolByFullNameHash, SymbolByFullNameEq>;
+
+struct SymbolByParentHash {
+  using is_transparent = void;
+
+  template <typename T>
+  size_t operator()(const T& s) const {
+    return PointerStringPairHash{}(s.parent_name_key());
+  }
+};
+struct SymbolByParentEq {
+  using is_transparent = void;
+
+  template <typename T, typename U>
+  bool operator()(const T& a, const U& b) const {
+    return a.parent_name_key() == b.parent_name_key();
+  }
+};
+using SymbolsByParentSet =
+    HASH_SET<Symbol, SymbolByParentHash, SymbolByParentEq>;
+
+typedef HASH_MAP<StringPiece, const FileDescriptor*,
+                 HASH_FXN<StringPiece>>
+    FilesByNameMap;
+
+typedef HASH_MAP<PointerStringPair, const FieldDescriptor*,
+                 PointerStringPairHash>
+    FieldsByNameMap;
+
+struct FieldsByNumberHash {
+  using is_transparent = void;
+
+  template <typename T>
+  size_t operator()(const T& s) const {
+    return PointerIntegerPairHash<std::pair<const void*, int>>{}(
+        s.parent_number_key());
+  }
+};
+struct FieldsByNumberEq {
+  using is_transparent = void;
+
+  template <typename T, typename U>
+  bool operator()(const T& a, const U& b) const {
+    return a.parent_number_key() == b.parent_number_key();
+  }
+};
+using FieldsByNumberSet =
+    HASH_SET<Symbol, FieldsByNumberHash, FieldsByNumberEq>;
+using EnumValuesByNumberSet = FieldsByNumberSet;
+
+// This is a map rather than a hash-map, since we use it to iterate
+// through all the extensions that extend a given Descriptor, and an
+// ordered data structure that implements lower_bound is convenient
+// for that.
+typedef std::map<DescriptorIntPair, const FieldDescriptor*>
+    ExtensionsGroupedByDescriptorMap;
+typedef HASH_MAP<std::string, const SourceCodeInfo_Location*>
+    LocationsByPathMap;
+
+std::set<std::string>* NewAllowedProto3Extendee() {
+  auto allowed_proto3_extendees = new std::set<std::string>;
+  const char* kOptionNames[] = {
+      "FileOptions",   "MessageOptions",   "FieldOptions",
+      "EnumOptions",   "EnumValueOptions", "ServiceOptions",
+      "MethodOptions", "OneofOptions",     "ExtensionRangeOptions"};
+  for (const char* option_name : kOptionNames) {
+    // descriptor.proto has a different package name in opensource. We allow
+    // both so the opensource protocol compiler can also compile internal
+    // proto3 files with custom options. See: b/27567912
+    allowed_proto3_extendees->insert(std::string("google.protobuf.") +
+                                     option_name);
+    // Split the word to trick the opensource processing scripts so they
+    // will keep the original package name.
+    allowed_proto3_extendees->insert(std::string("proto") + "2." + option_name);
+  }
+  return allowed_proto3_extendees;
+}
+
+// Checks whether the extendee type is allowed in proto3.
+// Only extensions to descriptor options are allowed. We use name comparison
+// instead of comparing the descriptor directly because the extensions may be
+// defined in a different pool.
+bool AllowedExtendeeInProto3(const std::string& name) {
+  static auto allowed_proto3_extendees =
+      internal::OnShutdownDelete(NewAllowedProto3Extendee());
+  return allowed_proto3_extendees->find(name) !=
+         allowed_proto3_extendees->end();
+}
+}  // anonymous namespace
+
+// Contains tables specific to a particular file.  These tables are not
+// modified once the file has been constructed, so they need not be
+// protected by a mutex.  This makes operations that depend only on the
+// contents of a single file -- e.g. Descriptor::FindFieldByName() --
+// lock-free.
+//
+// For historical reasons, the definitions of the methods of
+// FileDescriptorTables and DescriptorPool::Tables are interleaved below.
+// These used to be a single class.
+class FileDescriptorTables {
+ public:
+  FileDescriptorTables();
+  ~FileDescriptorTables();
+
+  // Empty table, used with placeholder files.
+  inline static const FileDescriptorTables& GetEmptyInstance();
+
+  // -----------------------------------------------------------------
+  // Finding items.
+
+  // Returns a null Symbol (symbol.IsNull() is true) if not found.
+  inline Symbol FindNestedSymbol(const void* parent,
+                                 StringPiece name) const;
+
+  // These return nullptr if not found.
+  inline const FieldDescriptor* FindFieldByNumber(const Descriptor* parent,
+                                                  int number) const;
+  inline const FieldDescriptor* FindFieldByLowercaseName(
+      const void* parent, StringPiece lowercase_name) const;
+  inline const FieldDescriptor* FindFieldByCamelcaseName(
+      const void* parent, StringPiece camelcase_name) const;
+  inline const EnumValueDescriptor* FindEnumValueByNumber(
+      const EnumDescriptor* parent, int number) const;
+  // This creates a new EnumValueDescriptor if not found, in a thread-safe way.
+  inline const EnumValueDescriptor* FindEnumValueByNumberCreatingIfUnknown(
+      const EnumDescriptor* parent, int number) const;
+
+  // -----------------------------------------------------------------
+  // Adding items.
+
+  // These add items to the corresponding tables.  They return false if
+  // the key already exists in the table.
+  bool AddAliasUnderParent(const void* parent, const std::string& name,
+                           Symbol symbol);
+  bool AddFieldByNumber(FieldDescriptor* field);
+  bool AddEnumValueByNumber(EnumValueDescriptor* value);
+
+  // Populates p->first->locations_by_path_ from p->second.
+  // Unusual signature dictated by internal::call_once.
+  static void BuildLocationsByPath(
+      std::pair<const FileDescriptorTables*, const SourceCodeInfo*>* p);
+
+  // Returns the location denoted by the specified path through info,
+  // or nullptr if not found.
+  // The value of info must be that of the corresponding FileDescriptor.
+  // (Conceptually a pure function, but stateful as an optimisation.)
+  const SourceCodeInfo_Location* GetSourceLocation(
+      const std::vector<int>& path, const SourceCodeInfo* info) const;
+
+  // Must be called after BuildFileImpl(), even if the build failed and
+  // we are going to roll back to the last checkpoint.
+  void FinalizeTables();
+
+ private:
+  const void* FindParentForFieldsByMap(const FieldDescriptor* field) const;
+  static void FieldsByLowercaseNamesLazyInitStatic(
+      const FileDescriptorTables* tables);
+  void FieldsByLowercaseNamesLazyInitInternal() const;
+  static void FieldsByCamelcaseNamesLazyInitStatic(
+      const FileDescriptorTables* tables);
+  void FieldsByCamelcaseNamesLazyInitInternal() const;
+
+  SymbolsByParentSet symbols_by_parent_;
+  mutable internal::once_flag fields_by_lowercase_name_once_;
+  mutable internal::once_flag fields_by_camelcase_name_once_;
+  // Make these fields atomic to avoid race conditions with
+  // GetEstimatedOwnedMemoryBytesSize. Once the pointer is set the map won't
+  // change anymore.
+  mutable std::atomic<const FieldsByNameMap*> fields_by_lowercase_name_{};
+  mutable std::atomic<const FieldsByNameMap*> fields_by_camelcase_name_{};
+  FieldsByNumberSet fields_by_number_;  // Not including extensions.
+  EnumValuesByNumberSet enum_values_by_number_;
+  mutable EnumValuesByNumberSet unknown_enum_values_by_number_
+      PROTOBUF_GUARDED_BY(unknown_enum_values_mu_);
+
+  // Populated on first request to save space, hence constness games.
+  mutable internal::once_flag locations_by_path_once_;
+  mutable LocationsByPathMap locations_by_path_;
+
+  // Mutex to protect the unknown-enum-value map due to dynamic
+  // EnumValueDescriptor creation on unknown values.
+  mutable internal::WrappedMutex unknown_enum_values_mu_;
+};
+
+namespace internal {
+
+// Small sequential allocator to be used within a single file.
+// Most of the memory for a single FileDescriptor and everything under it is
+// allocated in a single block of memory, with the FlatAllocator giving it out
+// in parts later.
+// The code first plans the total number of bytes needed by calling PlanArray
+// with all the allocations that will happen afterwards, then calls
+// FinalizePlanning passing the underlying allocator (the DescriptorPool::Tables
+// instance), and then proceeds to get the memory via
+// `AllocateArray`/`AllocateString` calls. The calls to PlanArray and
+// The calls have to match between planning and allocating, though not
+// necessarily in the same order.
+class FlatAllocator
+    : public decltype(ApplyTypeList<FlatAllocatorImpl>(
+          SortByAlignment<char, std::string, SourceCodeInfo,
+                          FileDescriptorTables,
+                          // Option types
+                          MessageOptions, FieldOptions, EnumOptions,
+                          EnumValueOptions, ExtensionRangeOptions, OneofOptions,
+                          ServiceOptions, MethodOptions, FileOptions>())) {};
+
+}  // namespace internal
+
+// ===================================================================
+// DescriptorPool::Tables
+
+class DescriptorPool::Tables {
+ public:
+  Tables();
+  ~Tables();
+
+  // Record the current state of the tables to the stack of checkpoints.
+  // Each call to AddCheckpoint() must be paired with exactly one call to either
+  // ClearLastCheckpoint() or RollbackToLastCheckpoint().
+  //
+  // This is used when building files, since some kinds of validation errors
+  // cannot be detected until the file's descriptors have already been added to
+  // the tables.
+  //
+  // This supports recursive checkpoints, since building a file may trigger
+  // recursive building of other files. Note that recursive checkpoints are not
+  // normally necessary; explicit dependencies are built prior to checkpointing.
+  // So although we recursively build transitive imports, there is at most one
+  // checkpoint in the stack during dependency building.
+  //
+  // Recursive checkpoints only arise during cross-linking of the descriptors.
+  // Symbol references must be resolved, via DescriptorBuilder::FindSymbol and
+  // friends. If the pending file references an unknown symbol
+  // (e.g., it is not defined in the pending file's explicit dependencies), and
+  // the pool is using a fallback database, and that database contains a file
+  // defining that symbol, and that file has not yet been built by the pool,
+  // the pool builds the file during cross-linking, leading to another
+  // checkpoint.
+  void AddCheckpoint();
+
+  // Mark the last checkpoint as having cleared successfully, removing it from
+  // the stack. If the stack is empty, all pending symbols will be committed.
+  //
+  // Note that this does not guarantee that the symbols added since the last
+  // checkpoint won't be rolled back: if a checkpoint gets rolled back,
+  // everything past that point gets rolled back, including symbols added after
+  // checkpoints that were pushed onto the stack after it and marked as cleared.
+  void ClearLastCheckpoint();
+
+  // Roll back the Tables to the state of the checkpoint at the top of the
+  // stack, removing everything that was added after that point.
+  void RollbackToLastCheckpoint();
+
+  // The stack of files which are currently being built.  Used to detect
+  // cyclic dependencies when loading files from a DescriptorDatabase.  Not
+  // used when fallback_database_ == nullptr.
+  std::vector<std::string> pending_files_;
+
+  // A set of files which we have tried to load from the fallback database
+  // and encountered errors.  We will not attempt to load them again during
+  // execution of the current public API call, but for compatibility with
+  // legacy clients, this is cleared at the beginning of each public API call.
+  // Not used when fallback_database_ == nullptr.
+  HASH_SET<std::string> known_bad_files_;
+
+  // A set of symbols which we have tried to load from the fallback database
+  // and encountered errors. We will not attempt to load them again during
+  // execution of the current public API call, but for compatibility with
+  // legacy clients, this is cleared at the beginning of each public API call.
+  HASH_SET<std::string> known_bad_symbols_;
+
+  // The set of descriptors for which we've already loaded the full
+  // set of extensions numbers from fallback_database_.
+  HASH_SET<const Descriptor*> extensions_loaded_from_db_;
+
+  // Maps type name to Descriptor::WellKnownType.  This is logically global
+  // and const, but we make it a member here to simplify its construction and
+  // destruction.  This only has 20-ish entries and is one per DescriptorPool,
+  // so the overhead is small.
+  HASH_MAP<std::string, Descriptor::WellKnownType> well_known_types_;
+
+  // -----------------------------------------------------------------
+  // Finding items.
+
+  // Find symbols.  This returns a null Symbol (symbol.IsNull() is true)
+  // if not found.
+  inline Symbol FindSymbol(StringPiece key) const;
+
+  // This implements the body of DescriptorPool::Find*ByName().  It should
+  // really be a private method of DescriptorPool, but that would require
+  // declaring Symbol in descriptor.h, which would drag all kinds of other
+  // stuff into the header.  Yay C++.
+  Symbol FindByNameHelper(const DescriptorPool* pool, StringPiece name);
+
+  // These return nullptr if not found.
+  inline const FileDescriptor* FindFile(StringPiece key) const;
+  inline const FieldDescriptor* FindExtension(const Descriptor* extendee,
+                                              int number) const;
+  inline void FindAllExtensions(const Descriptor* extendee,
+                                std::vector<const FieldDescriptor*>* out) const;
+
+  // -----------------------------------------------------------------
+  // Adding items.
+
+  // These add items to the corresponding tables.  They return false if
+  // the key already exists in the table.  For AddSymbol(), the string passed
+  // in must be one that was constructed using AllocateString(), as it will
+  // be used as a key in the symbols_by_name_ map without copying.
+  bool AddSymbol(const std::string& full_name, Symbol symbol);
+  bool AddFile(const FileDescriptor* file);
+  bool AddExtension(const FieldDescriptor* field);
+
+  // -----------------------------------------------------------------
+  // Allocating memory.
+
+  // Allocate an object which will be reclaimed when the pool is
+  // destroyed.  Note that the object's destructor will never be called,
+  // so its fields must be plain old data (primitive data types and
+  // pointers).  All of the descriptor types are such objects.
+  template <typename Type>
+  Type* Allocate();
+
+  // Allocate some bytes which will be reclaimed when the pool is
+  // destroyed. Memory is aligned to 8 bytes.
+  void* AllocateBytes(int size);
+
+  // Create a FlatAllocation for the corresponding sizes.
+  // All objects within it will be default constructed.
+  // The whole allocation, including the non-trivial objects within, will be
+  // destroyed with the pool.
+  template <typename... T>
+  internal::FlatAllocator::Allocation* CreateFlatAlloc(
+      const TypeMap<IntT, T...>& sizes);
+
+
+ private:
+  // All memory allocated in the pool.  Must be first as other objects can
+  // point into these.
+  struct MiscDeleter {
+    void operator()(int* p) const { internal::SizedDelete(p, *p + 8); }
+  };
+  // Miscellaneous allocations are length prefixed. The paylaod is 8 bytes after
+  // the `int` that contains the size. This keeps the payload aligned.
+  std::vector<std::unique_ptr<int, MiscDeleter>> misc_allocs_;
+  struct FlatAllocDeleter {
+    void operator()(internal::FlatAllocator::Allocation* p) const {
+      p->Destroy();
+    }
+  };
+  std::vector<
+      std::unique_ptr<internal::FlatAllocator::Allocation, FlatAllocDeleter>>
+      flat_allocs_;
+
+  SymbolsByNameSet symbols_by_name_;
+  FilesByNameMap files_by_name_;
+  ExtensionsGroupedByDescriptorMap extensions_;
+
+  struct CheckPoint {
+    explicit CheckPoint(const Tables* tables)
+        : flat_allocations_before_checkpoint(
+              static_cast<int>(tables->flat_allocs_.size())),
+          misc_allocations_before_checkpoint(
+              static_cast<int>(tables->misc_allocs_.size())),
+          pending_symbols_before_checkpoint(
+              tables->symbols_after_checkpoint_.size()),
+          pending_files_before_checkpoint(
+              tables->files_after_checkpoint_.size()),
+          pending_extensions_before_checkpoint(
+              tables->extensions_after_checkpoint_.size()) {}
+    int flat_allocations_before_checkpoint;
+    int misc_allocations_before_checkpoint;
+    int pending_symbols_before_checkpoint;
+    int pending_files_before_checkpoint;
+    int pending_extensions_before_checkpoint;
+  };
+  std::vector<CheckPoint> checkpoints_;
+  std::vector<Symbol> symbols_after_checkpoint_;
+  std::vector<const FileDescriptor*> files_after_checkpoint_;
+  std::vector<DescriptorIntPair> extensions_after_checkpoint_;
+};
+
+DescriptorPool::Tables::Tables() {
+  well_known_types_.insert({
+      {"google.protobuf.DoubleValue", Descriptor::WELLKNOWNTYPE_DOUBLEVALUE},
+      {"google.protobuf.FloatValue", Descriptor::WELLKNOWNTYPE_FLOATVALUE},
+      {"google.protobuf.Int64Value", Descriptor::WELLKNOWNTYPE_INT64VALUE},
+      {"google.protobuf.UInt64Value", Descriptor::WELLKNOWNTYPE_UINT64VALUE},
+      {"google.protobuf.Int32Value", Descriptor::WELLKNOWNTYPE_INT32VALUE},
+      {"google.protobuf.UInt32Value", Descriptor::WELLKNOWNTYPE_UINT32VALUE},
+      {"google.protobuf.StringValue", Descriptor::WELLKNOWNTYPE_STRINGVALUE},
+      {"google.protobuf.BytesValue", Descriptor::WELLKNOWNTYPE_BYTESVALUE},
+      {"google.protobuf.BoolValue", Descriptor::WELLKNOWNTYPE_BOOLVALUE},
+      {"google.protobuf.Any", Descriptor::WELLKNOWNTYPE_ANY},
+      {"google.protobuf.FieldMask", Descriptor::WELLKNOWNTYPE_FIELDMASK},
+      {"google.protobuf.Duration", Descriptor::WELLKNOWNTYPE_DURATION},
+      {"google.protobuf.Timestamp", Descriptor::WELLKNOWNTYPE_TIMESTAMP},
+      {"google.protobuf.Value", Descriptor::WELLKNOWNTYPE_VALUE},
+      {"google.protobuf.ListValue", Descriptor::WELLKNOWNTYPE_LISTVALUE},
+      {"google.protobuf.Struct", Descriptor::WELLKNOWNTYPE_STRUCT},
+  });
+}
+
+DescriptorPool::Tables::~Tables() { GOOGLE_DCHECK(checkpoints_.empty()); }
+
+FileDescriptorTables::FileDescriptorTables() {}
+
+FileDescriptorTables::~FileDescriptorTables() {
+  delete fields_by_lowercase_name_.load(std::memory_order_acquire);
+  delete fields_by_camelcase_name_.load(std::memory_order_acquire);
+}
+
+inline const FileDescriptorTables& FileDescriptorTables::GetEmptyInstance() {
+  static auto file_descriptor_tables =
+      internal::OnShutdownDelete(new FileDescriptorTables());
+  return *file_descriptor_tables;
+}
+
+void DescriptorPool::Tables::AddCheckpoint() {
+  checkpoints_.push_back(CheckPoint(this));
+}
+
+void DescriptorPool::Tables::ClearLastCheckpoint() {
+  GOOGLE_DCHECK(!checkpoints_.empty());
+  checkpoints_.pop_back();
+  if (checkpoints_.empty()) {
+    // All checkpoints have been cleared: we can now commit all of the pending
+    // data.
+    symbols_after_checkpoint_.clear();
+    files_after_checkpoint_.clear();
+    extensions_after_checkpoint_.clear();
+  }
+}
+
+void DescriptorPool::Tables::RollbackToLastCheckpoint() {
+  GOOGLE_DCHECK(!checkpoints_.empty());
+  const CheckPoint& checkpoint = checkpoints_.back();
+
+  for (size_t i = checkpoint.pending_symbols_before_checkpoint;
+       i < symbols_after_checkpoint_.size(); i++) {
+    symbols_by_name_.erase(symbols_after_checkpoint_[i]);
+  }
+  for (size_t i = checkpoint.pending_files_before_checkpoint;
+       i < files_after_checkpoint_.size(); i++) {
+    files_by_name_.erase(files_after_checkpoint_[i]->name());
+  }
+  for (size_t i = checkpoint.pending_extensions_before_checkpoint;
+       i < extensions_after_checkpoint_.size(); i++) {
+    extensions_.erase(extensions_after_checkpoint_[i]);
+  }
+
+  symbols_after_checkpoint_.resize(
+      checkpoint.pending_symbols_before_checkpoint);
+  files_after_checkpoint_.resize(checkpoint.pending_files_before_checkpoint);
+  extensions_after_checkpoint_.resize(
+      checkpoint.pending_extensions_before_checkpoint);
+
+  flat_allocs_.resize(checkpoint.flat_allocations_before_checkpoint);
+  misc_allocs_.resize(checkpoint.misc_allocations_before_checkpoint);
+  checkpoints_.pop_back();
+}
+
+// -------------------------------------------------------------------
+
+inline Symbol DescriptorPool::Tables::FindSymbol(StringPiece key) const {
+  Symbol::QueryKey name;
+  name.name = key;
+  auto it = symbols_by_name_.find(name);
+  return it == symbols_by_name_.end() ? Symbol() : *it;
+}
+
+inline Symbol FileDescriptorTables::FindNestedSymbol(
+    const void* parent, StringPiece name) const {
+  Symbol::QueryKey query;
+  query.name = name;
+  query.parent = parent;
+  auto it = symbols_by_parent_.find(query);
+  return it == symbols_by_parent_.end() ? Symbol() : *it;
+}
+
+Symbol DescriptorPool::Tables::FindByNameHelper(const DescriptorPool* pool,
+                                                StringPiece name) {
+  if (pool->mutex_ != nullptr) {
+    // Fast path: the Symbol is already cached.  This is just a hash lookup.
+    ReaderMutexLock lock(pool->mutex_);
+    if (known_bad_symbols_.empty() && known_bad_files_.empty()) {
+      Symbol result = FindSymbol(name);
+      if (!result.IsNull()) return result;
+    }
+  }
+  MutexLockMaybe lock(pool->mutex_);
+  if (pool->fallback_database_ != nullptr) {
+    known_bad_symbols_.clear();
+    known_bad_files_.clear();
+  }
+  Symbol result = FindSymbol(name);
+
+  if (result.IsNull() && pool->underlay_ != nullptr) {
+    // Symbol not found; check the underlay.
+    result = pool->underlay_->tables_->FindByNameHelper(pool->underlay_, name);
+  }
+
+  if (result.IsNull()) {
+    // Symbol still not found, so check fallback database.
+    if (pool->TryFindSymbolInFallbackDatabase(name)) {
+      result = FindSymbol(name);
+    }
+  }
+
+  return result;
+}
+
+inline const FileDescriptor* DescriptorPool::Tables::FindFile(
+    StringPiece key) const {
+  return FindPtrOrNull(files_by_name_, key);
+}
+
+inline const FieldDescriptor* FileDescriptorTables::FindFieldByNumber(
+    const Descriptor* parent, int number) const {
+  // If `number` is within the sequential range, just index into the parent
+  // without doing a table lookup.
+  if (parent != nullptr &&  //
+      1 <= number && number <= parent->sequential_field_limit_) {
+    return parent->field(number - 1);
+  }
+
+  Symbol::QueryKey query;
+  query.parent = parent;
+  query.field_number = number;
+
+  auto it = fields_by_number_.find(query);
+  return it == fields_by_number_.end() ? nullptr : it->field_descriptor();
+}
+
+const void* FileDescriptorTables::FindParentForFieldsByMap(
+    const FieldDescriptor* field) const {
+  if (field->is_extension()) {
+    if (field->extension_scope() == nullptr) {
+      return field->file();
+    } else {
+      return field->extension_scope();
+    }
+  } else {
+    return field->containing_type();
+  }
+}
+
+void FileDescriptorTables::FieldsByLowercaseNamesLazyInitStatic(
+    const FileDescriptorTables* tables) {
+  tables->FieldsByLowercaseNamesLazyInitInternal();
+}
+
+void FileDescriptorTables::FieldsByLowercaseNamesLazyInitInternal() const {
+  auto* map = new FieldsByNameMap;
+  for (Symbol symbol : symbols_by_parent_) {
+    const FieldDescriptor* field = symbol.field_descriptor();
+    if (!field) continue;
+    (*map)[{FindParentForFieldsByMap(field), field->lowercase_name().c_str()}] =
+        field;
+  }
+  fields_by_lowercase_name_.store(map, std::memory_order_release);
+}
+
+inline const FieldDescriptor* FileDescriptorTables::FindFieldByLowercaseName(
+    const void* parent, StringPiece lowercase_name) const {
+  internal::call_once(
+      fields_by_lowercase_name_once_,
+      &FileDescriptorTables::FieldsByLowercaseNamesLazyInitStatic, this);
+  return FindPtrOrNull(
+      *fields_by_lowercase_name_.load(std::memory_order_acquire),
+      PointerStringPair(parent, lowercase_name));
+}
+
+void FileDescriptorTables::FieldsByCamelcaseNamesLazyInitStatic(
+    const FileDescriptorTables* tables) {
+  tables->FieldsByCamelcaseNamesLazyInitInternal();
+}
+
+void FileDescriptorTables::FieldsByCamelcaseNamesLazyInitInternal() const {
+  auto* map = new FieldsByNameMap;
+  for (Symbol symbol : symbols_by_parent_) {
+    const FieldDescriptor* field = symbol.field_descriptor();
+    if (!field) continue;
+    (*map)[{FindParentForFieldsByMap(field), field->camelcase_name().c_str()}] =
+        field;
+  }
+  fields_by_camelcase_name_.store(map, std::memory_order_release);
+}
+
+inline const FieldDescriptor* FileDescriptorTables::FindFieldByCamelcaseName(
+    const void* parent, StringPiece camelcase_name) const {
+  internal::call_once(
+      fields_by_camelcase_name_once_,
+      FileDescriptorTables::FieldsByCamelcaseNamesLazyInitStatic, this);
+  return FindPtrOrNull(
+      *fields_by_camelcase_name_.load(std::memory_order_acquire),
+      PointerStringPair(parent, camelcase_name));
+}
+
+inline const EnumValueDescriptor* FileDescriptorTables::FindEnumValueByNumber(
+    const EnumDescriptor* parent, int number) const {
+  // If `number` is within the sequential range, just index into the parent
+  // without doing a table lookup.
+  const int base = parent->value(0)->number();
+  if (base <= number &&
+      number <= static_cast<int64_t>(base) + parent->sequential_value_limit_) {
+    return parent->value(number - base);
+  }
+
+  Symbol::QueryKey query;
+  query.parent = parent;
+  query.field_number = number;
+
+  auto it = enum_values_by_number_.find(query);
+  return it == enum_values_by_number_.end() ? nullptr
+                                            : it->enum_value_descriptor();
+}
+
+inline const EnumValueDescriptor*
+FileDescriptorTables::FindEnumValueByNumberCreatingIfUnknown(
+    const EnumDescriptor* parent, int number) const {
+  // First try, with map of compiled-in values.
+  {
+    const auto* value = FindEnumValueByNumber(parent, number);
+    if (value != nullptr) {
+      return value;
+    }
+  }
+
+  Symbol::QueryKey query;
+  query.parent = parent;
+  query.field_number = number;
+
+  // Second try, with reader lock held on unknown enum values: common case.
+  {
+    ReaderMutexLock l(&unknown_enum_values_mu_);
+    auto it = unknown_enum_values_by_number_.find(query);
+    if (it != unknown_enum_values_by_number_.end() &&
+        it->enum_value_descriptor() != nullptr) {
+      return it->enum_value_descriptor();
+    }
+  }
+  // If not found, try again with writer lock held, and create new descriptor if
+  // necessary.
+  {
+    WriterMutexLock l(&unknown_enum_values_mu_);
+    auto it = unknown_enum_values_by_number_.find(query);
+    if (it != unknown_enum_values_by_number_.end() &&
+        it->enum_value_descriptor() != nullptr) {
+      return it->enum_value_descriptor();
+    }
+
+    // Create an EnumValueDescriptor dynamically. We don't insert it into the
+    // EnumDescriptor (it's not a part of the enum as originally defined), but
+    // we do insert it into the table so that we can return the same pointer
+    // later.
+    std::string enum_value_name = StringPrintf(
+        "UNKNOWN_ENUM_VALUE_%s_%d", parent->name().c_str(), number);
+    auto* pool = DescriptorPool::generated_pool();
+    auto* tables = const_cast<DescriptorPool::Tables*>(pool->tables_.get());
+    internal::FlatAllocator alloc;
+    alloc.PlanArray<EnumValueDescriptor>(1);
+    alloc.PlanArray<std::string>(2);
+
+    {
+      // Must lock the pool because we will do allocations in the shared arena.
+      MutexLockMaybe l2(pool->mutex_);
+      alloc.FinalizePlanning(tables);
+    }
+    EnumValueDescriptor* result = alloc.AllocateArray<EnumValueDescriptor>(1);
+    result->all_names_ = alloc.AllocateStrings(
+        enum_value_name,
+        StrCat(parent->full_name(), ".", enum_value_name));
+    result->number_ = number;
+    result->type_ = parent;
+    result->options_ = &EnumValueOptions::default_instance();
+    unknown_enum_values_by_number_.insert(Symbol::EnumValue(result, 0));
+    return result;
+  }
+}
+
+inline const FieldDescriptor* DescriptorPool::Tables::FindExtension(
+    const Descriptor* extendee, int number) const {
+  return FindPtrOrNull(extensions_, std::make_pair(extendee, number));
+}
+
+inline void DescriptorPool::Tables::FindAllExtensions(
+    const Descriptor* extendee,
+    std::vector<const FieldDescriptor*>* out) const {
+  ExtensionsGroupedByDescriptorMap::const_iterator it =
+      extensions_.lower_bound(std::make_pair(extendee, 0));
+  for (; it != extensions_.end() && it->first.first == extendee; ++it) {
+    out->push_back(it->second);
+  }
+}
+
+// -------------------------------------------------------------------
+
+bool DescriptorPool::Tables::AddSymbol(const std::string& full_name,
+                                       Symbol symbol) {
+  GOOGLE_DCHECK_EQ(full_name, symbol.full_name());
+  if (symbols_by_name_.insert(symbol).second) {
+    symbols_after_checkpoint_.push_back(symbol);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool FileDescriptorTables::AddAliasUnderParent(const void* parent,
+                                               const std::string& name,
+                                               Symbol symbol) {
+  GOOGLE_DCHECK_EQ(name, symbol.parent_name_key().second);
+  GOOGLE_DCHECK_EQ(parent, symbol.parent_name_key().first);
+  return symbols_by_parent_.insert(symbol).second;
+}
+
+bool DescriptorPool::Tables::AddFile(const FileDescriptor* file) {
+  if (InsertIfNotPresent(&files_by_name_, file->name(), file)) {
+    files_after_checkpoint_.push_back(file);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+void FileDescriptorTables::FinalizeTables() {}
+
+bool FileDescriptorTables::AddFieldByNumber(FieldDescriptor* field) {
+  // Skip fields that are at the start of the sequence.
+  if (field->containing_type() != nullptr && field->number() >= 1 &&
+      field->number() <= field->containing_type()->sequential_field_limit_) {
+    if (field->is_extension()) {
+      // Conflicts with the field that already exists in the sequential range.
+      return false;
+    }
+    // Only return true if the field at that index matches. Otherwise it
+    // conflicts with the existing field in the sequential range.
+    return field->containing_type()->field(field->number() - 1) == field;
+  }
+
+  return fields_by_number_.insert(Symbol(field)).second;
+}
+
+bool FileDescriptorTables::AddEnumValueByNumber(EnumValueDescriptor* value) {
+  // Skip values that are at the start of the sequence.
+  const int base = value->type()->value(0)->number();
+  if (base <= value->number() &&
+      value->number() <=
+          static_cast<int64_t>(base) + value->type()->sequential_value_limit_)
+    return true;
+  return enum_values_by_number_.insert(Symbol::EnumValue(value, 0)).second;
+}
+
+bool DescriptorPool::Tables::AddExtension(const FieldDescriptor* field) {
+  DescriptorIntPair key(field->containing_type(), field->number());
+  if (InsertIfNotPresent(&extensions_, key, field)) {
+    extensions_after_checkpoint_.push_back(key);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+// -------------------------------------------------------------------
+
+template <typename Type>
+Type* DescriptorPool::Tables::Allocate() {
+  static_assert(std::is_trivially_destructible<Type>::value, "");
+  static_assert(alignof(Type) <= 8, "");
+  return ::new (AllocateBytes(sizeof(Type))) Type{};
+}
+
+void* DescriptorPool::Tables::AllocateBytes(int size) {
+  if (size == 0) return nullptr;
+  void* p = ::operator new(size + RoundUpTo<8>(sizeof(int)));
+  int* sizep = static_cast<int*>(p);
+  misc_allocs_.emplace_back(sizep);
+  *sizep = size;
+  return static_cast<char*>(p) + RoundUpTo<8>(sizeof(int));
+}
+
+template <typename... T>
+internal::FlatAllocator::Allocation* DescriptorPool::Tables::CreateFlatAlloc(
+    const TypeMap<IntT, T...>& sizes) {
+  auto ends = CalculateEnds(sizes);
+  using FlatAlloc = internal::FlatAllocator::Allocation;
+
+  int last_end = ends.template Get<
+      typename std::tuple_element<sizeof...(T) - 1, std::tuple<T...>>::type>();
+  size_t total_size =
+      last_end + RoundUpTo<FlatAlloc::kMaxAlign>(sizeof(FlatAlloc));
+  char* data = static_cast<char*>(::operator new(total_size));
+  auto* res = ::new (data) FlatAlloc(ends);
+  flat_allocs_.emplace_back(res);
+
+  return res;
+}
+
+void FileDescriptorTables::BuildLocationsByPath(
+    std::pair<const FileDescriptorTables*, const SourceCodeInfo*>* p) {
+  for (int i = 0, len = p->second->location_size(); i < len; ++i) {
+    const SourceCodeInfo_Location* loc = &p->second->location().Get(i);
+    p->first->locations_by_path_[Join(loc->path(), ",")] = loc;
+  }
+}
+
+const SourceCodeInfo_Location* FileDescriptorTables::GetSourceLocation(
+    const std::vector<int>& path, const SourceCodeInfo* info) const {
+  std::pair<const FileDescriptorTables*, const SourceCodeInfo*> p(
+      std::make_pair(this, info));
+  internal::call_once(locations_by_path_once_,
+                      FileDescriptorTables::BuildLocationsByPath, &p);
+  return FindPtrOrNull(locations_by_path_, Join(path, ","));
+}
+
+// ===================================================================
+// DescriptorPool
+
+DescriptorPool::ErrorCollector::~ErrorCollector() {}
+
+DescriptorPool::DescriptorPool()
+    : mutex_(nullptr),
+      fallback_database_(nullptr),
+      default_error_collector_(nullptr),
+      underlay_(nullptr),
+      tables_(new Tables),
+      enforce_dependencies_(true),
+      lazily_build_dependencies_(false),
+      allow_unknown_(false),
+      enforce_weak_(false),
+      disallow_enforce_utf8_(false) {}
+
+DescriptorPool::DescriptorPool(DescriptorDatabase* fallback_database,
+                               ErrorCollector* error_collector)
+    : mutex_(new internal::WrappedMutex),
+      fallback_database_(fallback_database),
+      default_error_collector_(error_collector),
+      underlay_(nullptr),
+      tables_(new Tables),
+      enforce_dependencies_(true),
+      lazily_build_dependencies_(false),
+      allow_unknown_(false),
+      enforce_weak_(false),
+      disallow_enforce_utf8_(false) {}
+
+DescriptorPool::DescriptorPool(const DescriptorPool* underlay)
+    : mutex_(nullptr),
+      fallback_database_(nullptr),
+      default_error_collector_(nullptr),
+      underlay_(underlay),
+      tables_(new Tables),
+      enforce_dependencies_(true),
+      lazily_build_dependencies_(false),
+      allow_unknown_(false),
+      enforce_weak_(false),
+      disallow_enforce_utf8_(false) {}
+
+DescriptorPool::~DescriptorPool() {
+  if (mutex_ != nullptr) delete mutex_;
+}
+
+// DescriptorPool::BuildFile() defined later.
+// DescriptorPool::BuildFileCollectingErrors() defined later.
+
+void DescriptorPool::InternalDontEnforceDependencies() {
+  enforce_dependencies_ = false;
+}
+
+void DescriptorPool::AddUnusedImportTrackFile(ConstStringParam file_name,
+                                              bool is_error) {
+  unused_import_track_files_[std::string(file_name)] = is_error;
+}
+
+void DescriptorPool::ClearUnusedImportTrackFiles() {
+  unused_import_track_files_.clear();
+}
+
+bool DescriptorPool::InternalIsFileLoaded(ConstStringParam filename) const {
+  MutexLockMaybe lock(mutex_);
+  return tables_->FindFile(filename) != nullptr;
+}
+
+// generated_pool ====================================================
+
+namespace {
+
+
+EncodedDescriptorDatabase* GeneratedDatabase() {
+  static auto generated_database =
+      internal::OnShutdownDelete(new EncodedDescriptorDatabase());
+  return generated_database;
+}
+
+DescriptorPool* NewGeneratedPool() {
+  auto generated_pool = new DescriptorPool(GeneratedDatabase());
+  generated_pool->InternalSetLazilyBuildDependencies();
+  return generated_pool;
+}
+
+}  // anonymous namespace
+
+DescriptorDatabase* DescriptorPool::internal_generated_database() {
+  return GeneratedDatabase();
+}
+
+DescriptorPool* DescriptorPool::internal_generated_pool() {
+  static DescriptorPool* generated_pool =
+      internal::OnShutdownDelete(NewGeneratedPool());
+  return generated_pool;
+}
+
+const DescriptorPool* DescriptorPool::generated_pool() {
+  const DescriptorPool* pool = internal_generated_pool();
+  // Ensure that descriptor.proto has been registered in the generated pool.
+  DescriptorProto::descriptor();
+  return pool;
+}
+
+
+void DescriptorPool::InternalAddGeneratedFile(
+    const void* encoded_file_descriptor, int size) {
+  // So, this function is called in the process of initializing the
+  // descriptors for generated proto classes.  Each generated .pb.cc file
+  // has an internal procedure called AddDescriptors() which is called at
+  // process startup, and that function calls this one in order to register
+  // the raw bytes of the FileDescriptorProto representing the file.
+  //
+  // We do not actually construct the descriptor objects right away.  We just
+  // hang on to the bytes until they are actually needed.  We actually construct
+  // the descriptor the first time one of the following things happens:
+  // * Someone calls a method like descriptor(), GetDescriptor(), or
+  //   GetReflection() on the generated types, which requires returning the
+  //   descriptor or an object based on it.
+  // * Someone looks up the descriptor in DescriptorPool::generated_pool().
+  //
+  // Once one of these happens, the DescriptorPool actually parses the
+  // FileDescriptorProto and generates a FileDescriptor (and all its children)
+  // based on it.
+  //
+  // Note that FileDescriptorProto is itself a generated protocol message.
+  // Therefore, when we parse one, we have to be very careful to avoid using
+  // any descriptor-based operations, since this might cause infinite recursion
+  // or deadlock.
+  GOOGLE_CHECK(GeneratedDatabase()->Add(encoded_file_descriptor, size));
+}
+
+
+// Find*By* methods ==================================================
+
+// TODO(kenton):  There's a lot of repeated code here, but I'm not sure if
+//   there's any good way to factor it out.  Think about this some time when
+//   there's nothing more important to do (read: never).
+
+const FileDescriptor* DescriptorPool::FindFileByName(
+    ConstStringParam name) const {
+  MutexLockMaybe lock(mutex_);
+  if (fallback_database_ != nullptr) {
+    tables_->known_bad_symbols_.clear();
+    tables_->known_bad_files_.clear();
+  }
+  const FileDescriptor* result = tables_->FindFile(name);
+  if (result != nullptr) return result;
+  if (underlay_ != nullptr) {
+    result = underlay_->FindFileByName(name);
+    if (result != nullptr) return result;
+  }
+  if (TryFindFileInFallbackDatabase(name)) {
+    result = tables_->FindFile(name);
+    if (result != nullptr) return result;
+  }
+  return nullptr;
+}
+
+const FileDescriptor* DescriptorPool::FindFileContainingSymbol(
+    ConstStringParam symbol_name) const {
+  MutexLockMaybe lock(mutex_);
+  if (fallback_database_ != nullptr) {
+    tables_->known_bad_symbols_.clear();
+    tables_->known_bad_files_.clear();
+  }
+  Symbol result = tables_->FindSymbol(symbol_name);
+  if (!result.IsNull()) return result.GetFile();
+  if (underlay_ != nullptr) {
+    const FileDescriptor* file_result =
+        underlay_->FindFileContainingSymbol(symbol_name);
+    if (file_result != nullptr) return file_result;
+  }
+  if (TryFindSymbolInFallbackDatabase(symbol_name)) {
+    result = tables_->FindSymbol(symbol_name);
+    if (!result.IsNull()) return result.GetFile();
+  }
+  return nullptr;
+}
+
+const Descriptor* DescriptorPool::FindMessageTypeByName(
+    ConstStringParam name) const {
+  return tables_->FindByNameHelper(this, name).descriptor();
+}
+
+const FieldDescriptor* DescriptorPool::FindFieldByName(
+    ConstStringParam name) const {
+  if (const FieldDescriptor* field =
+          tables_->FindByNameHelper(this, name).field_descriptor()) {
+    if (!field->is_extension()) {
+      return field;
+    }
+  }
+  return nullptr;
+}
+
+const FieldDescriptor* DescriptorPool::FindExtensionByName(
+    ConstStringParam name) const {
+  if (const FieldDescriptor* field =
+          tables_->FindByNameHelper(this, name).field_descriptor()) {
+    if (field->is_extension()) {
+      return field;
+    }
+  }
+  return nullptr;
+}
+
+const OneofDescriptor* DescriptorPool::FindOneofByName(
+    ConstStringParam name) const {
+  return tables_->FindByNameHelper(this, name).oneof_descriptor();
+}
+
+const EnumDescriptor* DescriptorPool::FindEnumTypeByName(
+    ConstStringParam name) const {
+  return tables_->FindByNameHelper(this, name).enum_descriptor();
+}
+
+const EnumValueDescriptor* DescriptorPool::FindEnumValueByName(
+    ConstStringParam name) const {
+  return tables_->FindByNameHelper(this, name).enum_value_descriptor();
+}
+
+const ServiceDescriptor* DescriptorPool::FindServiceByName(
+    ConstStringParam name) const {
+  return tables_->FindByNameHelper(this, name).service_descriptor();
+}
+
+const MethodDescriptor* DescriptorPool::FindMethodByName(
+    ConstStringParam name) const {
+  return tables_->FindByNameHelper(this, name).method_descriptor();
+}
+
+const FieldDescriptor* DescriptorPool::FindExtensionByNumber(
+    const Descriptor* extendee, int number) const {
+  if (extendee->extension_range_count() == 0) return nullptr;
+  // A faster path to reduce lock contention in finding extensions, assuming
+  // most extensions will be cache hit.
+  if (mutex_ != nullptr) {
+    ReaderMutexLock lock(mutex_);
+    const FieldDescriptor* result = tables_->FindExtension(extendee, number);
+    if (result != nullptr) {
+      return result;
+    }
+  }
+  MutexLockMaybe lock(mutex_);
+  if (fallback_database_ != nullptr) {
+    tables_->known_bad_symbols_.clear();
+    tables_->known_bad_files_.clear();
+  }
+  const FieldDescriptor* result = tables_->FindExtension(extendee, number);
+  if (result != nullptr) {
+    return result;
+  }
+  if (underlay_ != nullptr) {
+    result = underlay_->FindExtensionByNumber(extendee, number);
+    if (result != nullptr) return result;
+  }
+  if (TryFindExtensionInFallbackDatabase(extendee, number)) {
+    result = tables_->FindExtension(extendee, number);
+    if (result != nullptr) {
+      return result;
+    }
+  }
+  return nullptr;
+}
+
+const FieldDescriptor* DescriptorPool::InternalFindExtensionByNumberNoLock(
+    const Descriptor* extendee, int number) const {
+  if (extendee->extension_range_count() == 0) return nullptr;
+
+  const FieldDescriptor* result = tables_->FindExtension(extendee, number);
+  if (result != nullptr) {
+    return result;
+  }
+
+  if (underlay_ != nullptr) {
+    result = underlay_->InternalFindExtensionByNumberNoLock(extendee, number);
+    if (result != nullptr) return result;
+  }
+
+  return nullptr;
+}
+
+const FieldDescriptor* DescriptorPool::FindExtensionByPrintableName(
+    const Descriptor* extendee, ConstStringParam printable_name) const {
+  if (extendee->extension_range_count() == 0) return nullptr;
+  const FieldDescriptor* result = FindExtensionByName(printable_name);
+  if (result != nullptr && result->containing_type() == extendee) {
+    return result;
+  }
+  if (extendee->options().message_set_wire_format()) {
+    // MessageSet extensions may be identified by type name.
+    const Descriptor* type = FindMessageTypeByName(printable_name);
+    if (type != nullptr) {
+      // Look for a matching extension in the foreign type's scope.
+      const int type_extension_count = type->extension_count();
+      for (int i = 0; i < type_extension_count; i++) {
+        const FieldDescriptor* extension = type->extension(i);
+        if (extension->containing_type() == extendee &&
+            extension->type() == FieldDescriptor::TYPE_MESSAGE &&
+            extension->is_optional() && extension->message_type() == type) {
+          // Found it.
+          return extension;
+        }
+      }
+    }
+  }
+  return nullptr;
+}
+
+void DescriptorPool::FindAllExtensions(
+    const Descriptor* extendee,
+    std::vector<const FieldDescriptor*>* out) const {
+  MutexLockMaybe lock(mutex_);
+  if (fallback_database_ != nullptr) {
+    tables_->known_bad_symbols_.clear();
+    tables_->known_bad_files_.clear();
+  }
+
+  // Initialize tables_->extensions_ from the fallback database first
+  // (but do this only once per descriptor).
+  if (fallback_database_ != nullptr &&
+      tables_->extensions_loaded_from_db_.count(extendee) == 0) {
+    std::vector<int> numbers;
+    if (fallback_database_->FindAllExtensionNumbers(extendee->full_name(),
+                                                    &numbers)) {
+      for (int number : numbers) {
+        if (tables_->FindExtension(extendee, number) == nullptr) {
+          TryFindExtensionInFallbackDatabase(extendee, number);
+        }
+      }
+      tables_->extensions_loaded_from_db_.insert(extendee);
+    }
+  }
+
+  tables_->FindAllExtensions(extendee, out);
+  if (underlay_ != nullptr) {
+    underlay_->FindAllExtensions(extendee, out);
+  }
+}
+
+
+// -------------------------------------------------------------------
+
+const FieldDescriptor* Descriptor::FindFieldByNumber(int key) const {
+  const FieldDescriptor* result = file()->tables_->FindFieldByNumber(this, key);
+  if (result == nullptr || result->is_extension()) {
+    return nullptr;
+  } else {
+    return result;
+  }
+}
+
+const FieldDescriptor* Descriptor::FindFieldByLowercaseName(
+    ConstStringParam key) const {
+  const FieldDescriptor* result =
+      file()->tables_->FindFieldByLowercaseName(this, key);
+  if (result == nullptr || result->is_extension()) {
+    return nullptr;
+  } else {
+    return result;
+  }
+}
+
+const FieldDescriptor* Descriptor::FindFieldByCamelcaseName(
+    ConstStringParam key) const {
+  const FieldDescriptor* result =
+      file()->tables_->FindFieldByCamelcaseName(this, key);
+  if (result == nullptr || result->is_extension()) {
+    return nullptr;
+  } else {
+    return result;
+  }
+}
+
+const FieldDescriptor* Descriptor::FindFieldByName(ConstStringParam key) const {
+  const FieldDescriptor* field =
+      file()->tables_->FindNestedSymbol(this, key).field_descriptor();
+  return field != nullptr && !field->is_extension() ? field : nullptr;
+}
+
+const OneofDescriptor* Descriptor::FindOneofByName(ConstStringParam key) const {
+  return file()->tables_->FindNestedSymbol(this, key).oneof_descriptor();
+}
+
+const FieldDescriptor* Descriptor::FindExtensionByName(
+    ConstStringParam key) const {
+  const FieldDescriptor* field =
+      file()->tables_->FindNestedSymbol(this, key).field_descriptor();
+  return field != nullptr && field->is_extension() ? field : nullptr;
+}
+
+const FieldDescriptor* Descriptor::FindExtensionByLowercaseName(
+    ConstStringParam key) const {
+  const FieldDescriptor* result =
+      file()->tables_->FindFieldByLowercaseName(this, key);
+  if (result == nullptr || !result->is_extension()) {
+    return nullptr;
+  } else {
+    return result;
+  }
+}
+
+const FieldDescriptor* Descriptor::FindExtensionByCamelcaseName(
+    ConstStringParam key) const {
+  const FieldDescriptor* result =
+      file()->tables_->FindFieldByCamelcaseName(this, key);
+  if (result == nullptr || !result->is_extension()) {
+    return nullptr;
+  } else {
+    return result;
+  }
+}
+
+const Descriptor* Descriptor::FindNestedTypeByName(ConstStringParam key) const {
+  return file()->tables_->FindNestedSymbol(this, key).descriptor();
+}
+
+const EnumDescriptor* Descriptor::FindEnumTypeByName(
+    ConstStringParam key) const {
+  return file()->tables_->FindNestedSymbol(this, key).enum_descriptor();
+}
+
+const EnumValueDescriptor* Descriptor::FindEnumValueByName(
+    ConstStringParam key) const {
+  return file()->tables_->FindNestedSymbol(this, key).enum_value_descriptor();
+}
+
+const FieldDescriptor* Descriptor::map_key() const {
+  if (!options().map_entry()) return nullptr;
+  GOOGLE_DCHECK_EQ(field_count(), 2);
+  return field(0);
+}
+
+const FieldDescriptor* Descriptor::map_value() const {
+  if (!options().map_entry()) return nullptr;
+  GOOGLE_DCHECK_EQ(field_count(), 2);
+  return field(1);
+}
+
+const EnumValueDescriptor* EnumDescriptor::FindValueByName(
+    ConstStringParam key) const {
+  return file()->tables_->FindNestedSymbol(this, key).enum_value_descriptor();
+}
+
+const EnumValueDescriptor* EnumDescriptor::FindValueByNumber(int key) const {
+  return file()->tables_->FindEnumValueByNumber(this, key);
+}
+
+const EnumValueDescriptor* EnumDescriptor::FindValueByNumberCreatingIfUnknown(
+    int key) const {
+  return file()->tables_->FindEnumValueByNumberCreatingIfUnknown(this, key);
+}
+
+const MethodDescriptor* ServiceDescriptor::FindMethodByName(
+    ConstStringParam key) const {
+  return file()->tables_->FindNestedSymbol(this, key).method_descriptor();
+}
+
+const Descriptor* FileDescriptor::FindMessageTypeByName(
+    ConstStringParam key) const {
+  return tables_->FindNestedSymbol(this, key).descriptor();
+}
+
+const EnumDescriptor* FileDescriptor::FindEnumTypeByName(
+    ConstStringParam key) const {
+  return tables_->FindNestedSymbol(this, key).enum_descriptor();
+}
+
+const EnumValueDescriptor* FileDescriptor::FindEnumValueByName(
+    ConstStringParam key) const {
+  return tables_->FindNestedSymbol(this, key).enum_value_descriptor();
+}
+
+const ServiceDescriptor* FileDescriptor::FindServiceByName(
+    ConstStringParam key) const {
+  return tables_->FindNestedSymbol(this, key).service_descriptor();
+}
+
+const FieldDescriptor* FileDescriptor::FindExtensionByName(
+    ConstStringParam key) const {
+  const FieldDescriptor* field =
+      tables_->FindNestedSymbol(this, key).field_descriptor();
+  return field != nullptr && field->is_extension() ? field : nullptr;
+}
+
+const FieldDescriptor* FileDescriptor::FindExtensionByLowercaseName(
+    ConstStringParam key) const {
+  const FieldDescriptor* result = tables_->FindFieldByLowercaseName(this, key);
+  if (result == nullptr || !result->is_extension()) {
+    return nullptr;
+  } else {
+    return result;
+  }
+}
+
+const FieldDescriptor* FileDescriptor::FindExtensionByCamelcaseName(
+    ConstStringParam key) const {
+  const FieldDescriptor* result = tables_->FindFieldByCamelcaseName(this, key);
+  if (result == nullptr || !result->is_extension()) {
+    return nullptr;
+  } else {
+    return result;
+  }
+}
+
+void Descriptor::ExtensionRange::CopyTo(
+    DescriptorProto_ExtensionRange* proto) const {
+  proto->set_start(this->start);
+  proto->set_end(this->end);
+  if (options_ != &ExtensionRangeOptions::default_instance()) {
+    *proto->mutable_options() = *options_;
+  }
+}
+
+const Descriptor::ExtensionRange*
+Descriptor::FindExtensionRangeContainingNumber(int number) const {
+  // Linear search should be fine because we don't expect a message to have
+  // more than a couple extension ranges.
+  for (int i = 0; i < extension_range_count(); i++) {
+    if (number >= extension_range(i)->start &&
+        number < extension_range(i)->end) {
+      return extension_range(i);
+    }
+  }
+  return nullptr;
+}
+
+const Descriptor::ReservedRange* Descriptor::FindReservedRangeContainingNumber(
+    int number) const {
+  // TODO(chrisn): Consider a non-linear search.
+  for (int i = 0; i < reserved_range_count(); i++) {
+    if (number >= reserved_range(i)->start && number < reserved_range(i)->end) {
+      return reserved_range(i);
+    }
+  }
+  return nullptr;
+}
+
+const EnumDescriptor::ReservedRange*
+EnumDescriptor::FindReservedRangeContainingNumber(int number) const {
+  // TODO(chrisn): Consider a non-linear search.
+  for (int i = 0; i < reserved_range_count(); i++) {
+    if (number >= reserved_range(i)->start &&
+        number <= reserved_range(i)->end) {
+      return reserved_range(i);
+    }
+  }
+  return nullptr;
+}
+
+// -------------------------------------------------------------------
+
+bool DescriptorPool::TryFindFileInFallbackDatabase(
+    StringPiece name) const {
+  if (fallback_database_ == nullptr) return false;
+
+  auto name_string = std::string(name);
+  if (tables_->known_bad_files_.count(name_string) > 0) return false;
+
+  FileDescriptorProto file_proto;
+  if (!fallback_database_->FindFileByName(name_string, &file_proto) ||
+      BuildFileFromDatabase(file_proto) == nullptr) {
+    tables_->known_bad_files_.insert(std::move(name_string));
+    return false;
+  }
+  return true;
+}
+
+bool DescriptorPool::IsSubSymbolOfBuiltType(StringPiece name) const {
+  auto prefix = std::string(name);
+  for (;;) {
+    std::string::size_type dot_pos = prefix.find_last_of('.');
+    if (dot_pos == std::string::npos) {
+      break;
+    }
+    prefix = prefix.substr(0, dot_pos);
+    Symbol symbol = tables_->FindSymbol(prefix);
+    // If the symbol type is anything other than PACKAGE, then its complete
+    // definition is already known.
+    if (!symbol.IsNull() && !symbol.IsPackage()) {
+      return true;
+    }
+  }
+  if (underlay_ != nullptr) {
+    // Check to see if any prefix of this symbol exists in the underlay.
+    return underlay_->IsSubSymbolOfBuiltType(name);
+  }
+  return false;
+}
+
+bool DescriptorPool::TryFindSymbolInFallbackDatabase(
+    StringPiece name) const {
+  if (fallback_database_ == nullptr) return false;
+
+  auto name_string = std::string(name);
+  if (tables_->known_bad_symbols_.count(name_string) > 0) return false;
+
+  FileDescriptorProto file_proto;
+  if (  // We skip looking in the fallback database if the name is a sub-symbol
+        // of any descriptor that already exists in the descriptor pool (except
+        // for package descriptors).  This is valid because all symbols except
+        // for packages are defined in a single file, so if the symbol exists
+        // then we should already have its definition.
+        //
+        // The other reason to do this is to support "overriding" type
+        // definitions by merging two databases that define the same type. (Yes,
+        // people do this.)  The main difficulty with making this work is that
+        // FindFileContainingSymbol() is allowed to return both false positives
+        // (e.g., SimpleDescriptorDatabase, UpgradedDescriptorDatabase) and
+        // false negatives (e.g. ProtoFileParser, SourceTreeDescriptorDatabase).
+        // When two such databases are merged, looking up a non-existent
+        // sub-symbol of a type that already exists in the descriptor pool can
+        // result in an attempt to load multiple definitions of the same type.
+        // The check below avoids this.
+      IsSubSymbolOfBuiltType(name)
+
+      // Look up file containing this symbol in fallback database.
+      || !fallback_database_->FindFileContainingSymbol(name_string, &file_proto)
+
+      // Check if we've already built this file. If so, it apparently doesn't
+      // contain the symbol we're looking for.  Some DescriptorDatabases
+      // return false positives.
+      || tables_->FindFile(file_proto.name()) != nullptr
+
+      // Build the file.
+      || BuildFileFromDatabase(file_proto) == nullptr) {
+    tables_->known_bad_symbols_.insert(std::move(name_string));
+    return false;
+  }
+
+  return true;
+}
+
+bool DescriptorPool::TryFindExtensionInFallbackDatabase(
+    const Descriptor* containing_type, int field_number) const {
+  if (fallback_database_ == nullptr) return false;
+
+  FileDescriptorProto file_proto;
+  if (!fallback_database_->FindFileContainingExtension(
+          containing_type->full_name(), field_number, &file_proto)) {
+    return false;
+  }
+
+  if (tables_->FindFile(file_proto.name()) != nullptr) {
+    // We've already loaded this file, and it apparently doesn't contain the
+    // extension we're looking for.  Some DescriptorDatabases return false
+    // positives.
+    return false;
+  }
+
+  if (BuildFileFromDatabase(file_proto) == nullptr) {
+    return false;
+  }
+
+  return true;
+}
+
+// ===================================================================
+
+bool FieldDescriptor::is_map_message_type() const {
+  return type_descriptor_.message_type->options().map_entry();
+}
+
+std::string FieldDescriptor::DefaultValueAsString(
+    bool quote_string_type) const {
+  GOOGLE_CHECK(has_default_value()) << "No default value";
+  switch (cpp_type()) {
+    case CPPTYPE_INT32:
+      return StrCat(default_value_int32_t());
+    case CPPTYPE_INT64:
+      return StrCat(default_value_int64_t());
+    case CPPTYPE_UINT32:
+      return StrCat(default_value_uint32_t());
+    case CPPTYPE_UINT64:
+      return StrCat(default_value_uint64_t());
+    case CPPTYPE_FLOAT:
+      return SimpleFtoa(default_value_float());
+    case CPPTYPE_DOUBLE:
+      return SimpleDtoa(default_value_double());
+    case CPPTYPE_BOOL:
+      return default_value_bool() ? "true" : "false";
+    case CPPTYPE_STRING:
+      if (quote_string_type) {
+        return "\"" + CEscape(default_value_string()) + "\"";
+      } else {
+        if (type() == TYPE_BYTES) {
+          return CEscape(default_value_string());
+        } else {
+          return default_value_string();
+        }
+      }
+    case CPPTYPE_ENUM:
+      return default_value_enum()->name();
+    case CPPTYPE_MESSAGE:
+      GOOGLE_LOG(DFATAL) << "Messages can't have default values!";
+      break;
+  }
+  GOOGLE_LOG(FATAL) << "Can't get here: failed to get default value as string";
+  return "";
+}
+
+// CopyTo methods ====================================================
+
+void FileDescriptor::CopyTo(FileDescriptorProto* proto) const {
+  proto->set_name(name());
+  if (!package().empty()) proto->set_package(package());
+  // TODO(liujisi): Also populate when syntax="proto2".
+  if (syntax() == SYNTAX_PROTO3) proto->set_syntax(SyntaxName(syntax()));
+
+  for (int i = 0; i < dependency_count(); i++) {
+    proto->add_dependency(dependency(i)->name());
+  }
+
+  for (int i = 0; i < public_dependency_count(); i++) {
+    proto->add_public_dependency(public_dependencies_[i]);
+  }
+
+  for (int i = 0; i < weak_dependency_count(); i++) {
+    proto->add_weak_dependency(weak_dependencies_[i]);
+  }
+
+  for (int i = 0; i < message_type_count(); i++) {
+    message_type(i)->CopyTo(proto->add_message_type());
+  }
+  for (int i = 0; i < enum_type_count(); i++) {
+    enum_type(i)->CopyTo(proto->add_enum_type());
+  }
+  for (int i = 0; i < service_count(); i++) {
+    service(i)->CopyTo(proto->add_service());
+  }
+  for (int i = 0; i < extension_count(); i++) {
+    extension(i)->CopyTo(proto->add_extension());
+  }
+
+  if (&options() != &FileOptions::default_instance()) {
+    proto->mutable_options()->CopyFrom(options());
+  }
+}
+
+void FileDescriptor::CopyJsonNameTo(FileDescriptorProto* proto) const {
+  if (message_type_count() != proto->message_type_size() ||
+      extension_count() != proto->extension_size()) {
+    GOOGLE_LOG(ERROR) << "Cannot copy json_name to a proto of a different size.";
+    return;
+  }
+  for (int i = 0; i < message_type_count(); i++) {
+    message_type(i)->CopyJsonNameTo(proto->mutable_message_type(i));
+  }
+  for (int i = 0; i < extension_count(); i++) {
+    extension(i)->CopyJsonNameTo(proto->mutable_extension(i));
+  }
+}
+
+void FileDescriptor::CopySourceCodeInfoTo(FileDescriptorProto* proto) const {
+  if (source_code_info_ &&
+      source_code_info_ != &SourceCodeInfo::default_instance()) {
+    proto->mutable_source_code_info()->CopyFrom(*source_code_info_);
+  }
+}
+
+void Descriptor::CopyTo(DescriptorProto* proto) const {
+  proto->set_name(name());
+
+  for (int i = 0; i < field_count(); i++) {
+    field(i)->CopyTo(proto->add_field());
+  }
+  for (int i = 0; i < oneof_decl_count(); i++) {
+    oneof_decl(i)->CopyTo(proto->add_oneof_decl());
+  }
+  for (int i = 0; i < nested_type_count(); i++) {
+    nested_type(i)->CopyTo(proto->add_nested_type());
+  }
+  for (int i = 0; i < enum_type_count(); i++) {
+    enum_type(i)->CopyTo(proto->add_enum_type());
+  }
+  for (int i = 0; i < extension_range_count(); i++) {
+    extension_range(i)->CopyTo(proto->add_extension_range());
+  }
+  for (int i = 0; i < extension_count(); i++) {
+    extension(i)->CopyTo(proto->add_extension());
+  }
+  for (int i = 0; i < reserved_range_count(); i++) {
+    DescriptorProto::ReservedRange* range = proto->add_reserved_range();
+    range->set_start(reserved_range(i)->start);
+    range->set_end(reserved_range(i)->end);
+  }
+  for (int i = 0; i < reserved_name_count(); i++) {
+    proto->add_reserved_name(reserved_name(i));
+  }
+
+  if (&options() != &MessageOptions::default_instance()) {
+    proto->mutable_options()->CopyFrom(options());
+  }
+}
+
+void Descriptor::CopyJsonNameTo(DescriptorProto* proto) const {
+  if (field_count() != proto->field_size() ||
+      nested_type_count() != proto->nested_type_size() ||
+      extension_count() != proto->extension_size()) {
+    GOOGLE_LOG(ERROR) << "Cannot copy json_name to a proto of a different size.";
+    return;
+  }
+  for (int i = 0; i < field_count(); i++) {
+    field(i)->CopyJsonNameTo(proto->mutable_field(i));
+  }
+  for (int i = 0; i < nested_type_count(); i++) {
+    nested_type(i)->CopyJsonNameTo(proto->mutable_nested_type(i));
+  }
+  for (int i = 0; i < extension_count(); i++) {
+    extension(i)->CopyJsonNameTo(proto->mutable_extension(i));
+  }
+}
+
+void FieldDescriptor::CopyTo(FieldDescriptorProto* proto) const {
+  proto->set_name(name());
+  proto->set_number(number());
+  if (has_json_name_) {
+    proto->set_json_name(json_name());
+  }
+  if (proto3_optional_) {
+    proto->set_proto3_optional(true);
+  }
+  // Some compilers do not allow static_cast directly between two enum types,
+  // so we must cast to int first.
+  proto->set_label(static_cast<FieldDescriptorProto::Label>(
+      implicit_cast<int>(label())));
+  proto->set_type(static_cast<FieldDescriptorProto::Type>(
+      implicit_cast<int>(type())));
+
+  if (is_extension()) {
+    if (!containing_type()->is_unqualified_placeholder_) {
+      proto->set_extendee(".");
+    }
+    proto->mutable_extendee()->append(containing_type()->full_name());
+  }
+
+  if (cpp_type() == CPPTYPE_MESSAGE) {
+    if (message_type()->is_placeholder_) {
+      // We don't actually know if the type is a message type.  It could be
+      // an enum.
+      proto->clear_type();
+    }
+
+    if (!message_type()->is_unqualified_placeholder_) {
+      proto->set_type_name(".");
+    }
+    proto->mutable_type_name()->append(message_type()->full_name());
+  } else if (cpp_type() == CPPTYPE_ENUM) {
+    if (!enum_type()->is_unqualified_placeholder_) {
+      proto->set_type_name(".");
+    }
+    proto->mutable_type_name()->append(enum_type()->full_name());
+  }
+
+  if (has_default_value()) {
+    proto->set_default_value(DefaultValueAsString(false));
+  }
+
+  if (containing_oneof() != nullptr && !is_extension()) {
+    proto->set_oneof_index(containing_oneof()->index());
+  }
+
+  if (&options() != &FieldOptions::default_instance()) {
+    proto->mutable_options()->CopyFrom(options());
+  }
+}
+
+void FieldDescriptor::CopyJsonNameTo(FieldDescriptorProto* proto) const {
+  proto->set_json_name(json_name());
+}
+
+void OneofDescriptor::CopyTo(OneofDescriptorProto* proto) const {
+  proto->set_name(name());
+  if (&options() != &OneofOptions::default_instance()) {
+    proto->mutable_options()->CopyFrom(options());
+  }
+}
+
+void EnumDescriptor::CopyTo(EnumDescriptorProto* proto) const {
+  proto->set_name(name());
+
+  for (int i = 0; i < value_count(); i++) {
+    value(i)->CopyTo(proto->add_value());
+  }
+  for (int i = 0; i < reserved_range_count(); i++) {
+    EnumDescriptorProto::EnumReservedRange* range = proto->add_reserved_range();
+    range->set_start(reserved_range(i)->start);
+    range->set_end(reserved_range(i)->end);
+  }
+  for (int i = 0; i < reserved_name_count(); i++) {
+    proto->add_reserved_name(reserved_name(i));
+  }
+
+  if (&options() != &EnumOptions::default_instance()) {
+    proto->mutable_options()->CopyFrom(options());
+  }
+}
+
+void EnumValueDescriptor::CopyTo(EnumValueDescriptorProto* proto) const {
+  proto->set_name(name());
+  proto->set_number(number());
+
+  if (&options() != &EnumValueOptions::default_instance()) {
+    proto->mutable_options()->CopyFrom(options());
+  }
+}
+
+void ServiceDescriptor::CopyTo(ServiceDescriptorProto* proto) const {
+  proto->set_name(name());
+
+  for (int i = 0; i < method_count(); i++) {
+    method(i)->CopyTo(proto->add_method());
+  }
+
+  if (&options() != &ServiceOptions::default_instance()) {
+    proto->mutable_options()->CopyFrom(options());
+  }
+}
+
+void MethodDescriptor::CopyTo(MethodDescriptorProto* proto) const {
+  proto->set_name(name());
+
+  if (!input_type()->is_unqualified_placeholder_) {
+    proto->set_input_type(".");
+  }
+  proto->mutable_input_type()->append(input_type()->full_name());
+
+  if (!output_type()->is_unqualified_placeholder_) {
+    proto->set_output_type(".");
+  }
+  proto->mutable_output_type()->append(output_type()->full_name());
+
+  if (&options() != &MethodOptions::default_instance()) {
+    proto->mutable_options()->CopyFrom(options());
+  }
+
+  if (client_streaming_) {
+    proto->set_client_streaming(true);
+  }
+  if (server_streaming_) {
+    proto->set_server_streaming(true);
+  }
+}
+
+// DebugString methods ===============================================
+
+namespace {
+
+bool RetrieveOptionsAssumingRightPool(
+    int depth, const Message& options,
+    std::vector<std::string>* option_entries) {
+  option_entries->clear();
+  const Reflection* reflection = options.GetReflection();
+  std::vector<const FieldDescriptor*> fields;
+  reflection->ListFields(options, &fields);
+  for (const FieldDescriptor* field : fields) {
+    int count = 1;
+    bool repeated = false;
+    if (field->is_repeated()) {
+      count = reflection->FieldSize(options, field);
+      repeated = true;
+    }
+    for (int j = 0; j < count; j++) {
+      std::string fieldval;
+      if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+        std::string tmp;
+        TextFormat::Printer printer;
+        printer.SetExpandAny(true);
+        printer.SetInitialIndentLevel(depth + 1);
+        printer.PrintFieldValueToString(options, field, repeated ? j : -1,
+                                        &tmp);
+        fieldval.append("{\n");
+        fieldval.append(tmp);
+        fieldval.append(depth * 2, ' ');
+        fieldval.append("}");
+      } else {
+        TextFormat::PrintFieldValueToString(options, field, repeated ? j : -1,
+                                            &fieldval);
+      }
+      std::string name;
+      if (field->is_extension()) {
+        name = "(." + field->full_name() + ")";
+      } else {
+        name = field->name();
+      }
+      option_entries->push_back(name + " = " + fieldval);
+    }
+  }
+  return !option_entries->empty();
+}
+
+// Used by each of the option formatters.
+bool RetrieveOptions(int depth, const Message& options,
+                     const DescriptorPool* pool,
+                     std::vector<std::string>* option_entries) {
+  // When printing custom options for a descriptor, we must use an options
+  // message built on top of the same DescriptorPool where the descriptor
+  // is coming from. This is to ensure we are interpreting custom options
+  // against the right pool.
+  if (options.GetDescriptor()->file()->pool() == pool) {
+    return RetrieveOptionsAssumingRightPool(depth, options, option_entries);
+  } else {
+    const Descriptor* option_descriptor =
+        pool->FindMessageTypeByName(options.GetDescriptor()->full_name());
+    if (option_descriptor == nullptr) {
+      // descriptor.proto is not in the pool. This means no custom options are
+      // used so we are safe to proceed with the compiled options message type.
+      return RetrieveOptionsAssumingRightPool(depth, options, option_entries);
+    }
+    DynamicMessageFactory factory;
+    std::unique_ptr<Message> dynamic_options(
+        factory.GetPrototype(option_descriptor)->New());
+    std::string serialized = options.SerializeAsString();
+    io::CodedInputStream input(
+        reinterpret_cast<const uint8_t*>(serialized.c_str()),
+        serialized.size());
+    input.SetExtensionRegistry(pool, &factory);
+    if (dynamic_options->ParseFromCodedStream(&input)) {
+      return RetrieveOptionsAssumingRightPool(depth, *dynamic_options,
+                                              option_entries);
+    } else {
+      GOOGLE_LOG(ERROR) << "Found invalid proto option data for: "
+                 << options.GetDescriptor()->full_name();
+      return RetrieveOptionsAssumingRightPool(depth, options, option_entries);
+    }
+  }
+}
+
+// Formats options that all appear together in brackets. Does not include
+// brackets.
+bool FormatBracketedOptions(int depth, const Message& options,
+                            const DescriptorPool* pool, std::string* output) {
+  std::vector<std::string> all_options;
+  if (RetrieveOptions(depth, options, pool, &all_options)) {
+    output->append(Join(all_options, ", "));
+  }
+  return !all_options.empty();
+}
+
+// Formats options one per line
+bool FormatLineOptions(int depth, const Message& options,
+                       const DescriptorPool* pool, std::string* output) {
+  std::string prefix(depth * 2, ' ');
+  std::vector<std::string> all_options;
+  if (RetrieveOptions(depth, options, pool, &all_options)) {
+    for (const std::string& option : all_options) {
+      strings::SubstituteAndAppend(output, "$0option $1;\n", prefix, option);
+    }
+  }
+  return !all_options.empty();
+}
+
+class SourceLocationCommentPrinter {
+ public:
+  template <typename DescType>
+  SourceLocationCommentPrinter(const DescType* desc, const std::string& prefix,
+                               const DebugStringOptions& options)
+      : options_(options), prefix_(prefix) {
+    // Perform the SourceLocation lookup only if we're including user comments,
+    // because the lookup is fairly expensive.
+    have_source_loc_ =
+        options.include_comments && desc->GetSourceLocation(&source_loc_);
+  }
+  SourceLocationCommentPrinter(const FileDescriptor* file,
+                               const std::vector<int>& path,
+                               const std::string& prefix,
+                               const DebugStringOptions& options)
+      : options_(options), prefix_(prefix) {
+    // Perform the SourceLocation lookup only if we're including user comments,
+    // because the lookup is fairly expensive.
+    have_source_loc_ =
+        options.include_comments && file->GetSourceLocation(path, &source_loc_);
+  }
+  void AddPreComment(std::string* output) {
+    if (have_source_loc_) {
+      // Detached leading comments.
+      for (const std::string& leading_detached_comment :
+           source_loc_.leading_detached_comments) {
+        *output += FormatComment(leading_detached_comment);
+        *output += "\n";
+      }
+      // Attached leading comments.
+      if (!source_loc_.leading_comments.empty()) {
+        *output += FormatComment(source_loc_.leading_comments);
+      }
+    }
+  }
+  void AddPostComment(std::string* output) {
+    if (have_source_loc_ && source_loc_.trailing_comments.size() > 0) {
+      *output += FormatComment(source_loc_.trailing_comments);
+    }
+  }
+
+  // Format comment such that each line becomes a full-line C++-style comment in
+  // the DebugString() output.
+  std::string FormatComment(const std::string& comment_text) {
+    std::string stripped_comment = comment_text;
+    StripWhitespace(&stripped_comment);
+    std::vector<std::string> lines = Split(stripped_comment, "\n");
+    std::string output;
+    for (const std::string& line : lines) {
+      strings::SubstituteAndAppend(&output, "$0// $1\n", prefix_, line);
+    }
+    return output;
+  }
+
+ private:
+
+  bool have_source_loc_;
+  SourceLocation source_loc_;
+  DebugStringOptions options_;
+  std::string prefix_;
+};
+
+}  // anonymous namespace
+
+std::string FileDescriptor::DebugString() const {
+  DebugStringOptions options;  // default options
+  return DebugStringWithOptions(options);
+}
+
+std::string FileDescriptor::DebugStringWithOptions(
+    const DebugStringOptions& debug_string_options) const {
+  std::string contents;
+  {
+    std::vector<int> path;
+    path.push_back(FileDescriptorProto::kSyntaxFieldNumber);
+    SourceLocationCommentPrinter syntax_comment(this, path, "",
+                                                debug_string_options);
+    syntax_comment.AddPreComment(&contents);
+    strings::SubstituteAndAppend(&contents, "syntax = \"$0\";\n\n",
+                              SyntaxName(syntax()));
+    syntax_comment.AddPostComment(&contents);
+  }
+
+  SourceLocationCommentPrinter comment_printer(this, "", debug_string_options);
+  comment_printer.AddPreComment(&contents);
+
+  std::set<int> public_dependencies;
+  std::set<int> weak_dependencies;
+  public_dependencies.insert(public_dependencies_,
+                             public_dependencies_ + public_dependency_count_);
+  weak_dependencies.insert(weak_dependencies_,
+                           weak_dependencies_ + weak_dependency_count_);
+
+  for (int i = 0; i < dependency_count(); i++) {
+    if (public_dependencies.count(i) > 0) {
+      strings::SubstituteAndAppend(&contents, "import public \"$0\";\n",
+                                dependency(i)->name());
+    } else if (weak_dependencies.count(i) > 0) {
+      strings::SubstituteAndAppend(&contents, "import weak \"$0\";\n",
+                                dependency(i)->name());
+    } else {
+      strings::SubstituteAndAppend(&contents, "import \"$0\";\n",
+                                dependency(i)->name());
+    }
+  }
+
+  if (!package().empty()) {
+    std::vector<int> path;
+    path.push_back(FileDescriptorProto::kPackageFieldNumber);
+    SourceLocationCommentPrinter package_comment(this, path, "",
+                                                 debug_string_options);
+    package_comment.AddPreComment(&contents);
+    strings::SubstituteAndAppend(&contents, "package $0;\n\n", package());
+    package_comment.AddPostComment(&contents);
+  }
+
+  if (FormatLineOptions(0, options(), pool(), &contents)) {
+    contents.append("\n");  // add some space if we had options
+  }
+
+  for (int i = 0; i < enum_type_count(); i++) {
+    enum_type(i)->DebugString(0, &contents, debug_string_options);
+    contents.append("\n");
+  }
+
+  // Find all the 'group' type extensions; we will not output their nested
+  // definitions (those will be done with their group field descriptor).
+  std::set<const Descriptor*> groups;
+  for (int i = 0; i < extension_count(); i++) {
+    if (extension(i)->type() == FieldDescriptor::TYPE_GROUP) {
+      groups.insert(extension(i)->message_type());
+    }
+  }
+
+  for (int i = 0; i < message_type_count(); i++) {
+    if (groups.count(message_type(i)) == 0) {
+      message_type(i)->DebugString(0, &contents, debug_string_options,
+                                   /* include_opening_clause */ true);
+      contents.append("\n");
+    }
+  }
+
+  for (int i = 0; i < service_count(); i++) {
+    service(i)->DebugString(&contents, debug_string_options);
+    contents.append("\n");
+  }
+
+  const Descriptor* containing_type = nullptr;
+  for (int i = 0; i < extension_count(); i++) {
+    if (extension(i)->containing_type() != containing_type) {
+      if (i > 0) contents.append("}\n\n");
+      containing_type = extension(i)->containing_type();
+      strings::SubstituteAndAppend(&contents, "extend .$0 {\n",
+                                containing_type->full_name());
+    }
+    extension(i)->DebugString(1, &contents, debug_string_options);
+  }
+  if (extension_count() > 0) contents.append("}\n\n");
+
+  comment_printer.AddPostComment(&contents);
+
+  return contents;
+}
+
+std::string Descriptor::DebugString() const {
+  DebugStringOptions options;  // default options
+  return DebugStringWithOptions(options);
+}
+
+std::string Descriptor::DebugStringWithOptions(
+    const DebugStringOptions& options) const {
+  std::string contents;
+  DebugString(0, &contents, options, /* include_opening_clause */ true);
+  return contents;
+}
+
+void Descriptor::DebugString(int depth, std::string* contents,
+                             const DebugStringOptions& debug_string_options,
+                             bool include_opening_clause) const {
+  if (options().map_entry()) {
+    // Do not generate debug string for auto-generated map-entry type.
+    return;
+  }
+  std::string prefix(depth * 2, ' ');
+  ++depth;
+
+  SourceLocationCommentPrinter comment_printer(this, prefix,
+                                               debug_string_options);
+  comment_printer.AddPreComment(contents);
+
+  if (include_opening_clause) {
+    strings::SubstituteAndAppend(contents, "$0message $1", prefix, name());
+  }
+  contents->append(" {\n");
+
+  FormatLineOptions(depth, options(), file()->pool(), contents);
+
+  // Find all the 'group' types for fields and extensions; we will not output
+  // their nested definitions (those will be done with their group field
+  // descriptor).
+  std::set<const Descriptor*> groups;
+  for (int i = 0; i < field_count(); i++) {
+    if (field(i)->type() == FieldDescriptor::TYPE_GROUP) {
+      groups.insert(field(i)->message_type());
+    }
+  }
+  for (int i = 0; i < extension_count(); i++) {
+    if (extension(i)->type() == FieldDescriptor::TYPE_GROUP) {
+      groups.insert(extension(i)->message_type());
+    }
+  }
+
+  for (int i = 0; i < nested_type_count(); i++) {
+    if (groups.count(nested_type(i)) == 0) {
+      nested_type(i)->DebugString(depth, contents, debug_string_options,
+                                  /* include_opening_clause */ true);
+    }
+  }
+  for (int i = 0; i < enum_type_count(); i++) {
+    enum_type(i)->DebugString(depth, contents, debug_string_options);
+  }
+  for (int i = 0; i < field_count(); i++) {
+    if (field(i)->real_containing_oneof() == nullptr) {
+      field(i)->DebugString(depth, contents, debug_string_options);
+    } else if (field(i)->containing_oneof()->field(0) == field(i)) {
+      // This is the first field in this oneof, so print the whole oneof.
+      field(i)->containing_oneof()->DebugString(depth, contents,
+                                                debug_string_options);
+    }
+  }
+
+  for (int i = 0; i < extension_range_count(); i++) {
+    strings::SubstituteAndAppend(contents, "$0  extensions $1 to $2;\n", prefix,
+                              extension_range(i)->start,
+                              extension_range(i)->end - 1);
+  }
+
+  // Group extensions by what they extend, so they can be printed out together.
+  const Descriptor* containing_type = nullptr;
+  for (int i = 0; i < extension_count(); i++) {
+    if (extension(i)->containing_type() != containing_type) {
+      if (i > 0) strings::SubstituteAndAppend(contents, "$0  }\n", prefix);
+      containing_type = extension(i)->containing_type();
+      strings::SubstituteAndAppend(contents, "$0  extend .$1 {\n", prefix,
+                                containing_type->full_name());
+    }
+    extension(i)->DebugString(depth + 1, contents, debug_string_options);
+  }
+  if (extension_count() > 0)
+    strings::SubstituteAndAppend(contents, "$0  }\n", prefix);
+
+  if (reserved_range_count() > 0) {
+    strings::SubstituteAndAppend(contents, "$0  reserved ", prefix);
+    for (int i = 0; i < reserved_range_count(); i++) {
+      const Descriptor::ReservedRange* range = reserved_range(i);
+      if (range->end == range->start + 1) {
+        strings::SubstituteAndAppend(contents, "$0, ", range->start);
+      } else if (range->end > FieldDescriptor::kMaxNumber) {
+        strings::SubstituteAndAppend(contents, "$0 to max, ", range->start);
+      } else {
+        strings::SubstituteAndAppend(contents, "$0 to $1, ", range->start,
+                                  range->end - 1);
+      }
+    }
+    contents->replace(contents->size() - 2, 2, ";\n");
+  }
+
+  if (reserved_name_count() > 0) {
+    strings::SubstituteAndAppend(contents, "$0  reserved ", prefix);
+    for (int i = 0; i < reserved_name_count(); i++) {
+      strings::SubstituteAndAppend(contents, "\"$0\", ",
+                                CEscape(reserved_name(i)));
+    }
+    contents->replace(contents->size() - 2, 2, ";\n");
+  }
+
+  strings::SubstituteAndAppend(contents, "$0}\n", prefix);
+  comment_printer.AddPostComment(contents);
+}
+
+std::string FieldDescriptor::DebugString() const {
+  DebugStringOptions options;  // default options
+  return DebugStringWithOptions(options);
+}
+
+std::string FieldDescriptor::DebugStringWithOptions(
+    const DebugStringOptions& debug_string_options) const {
+  std::string contents;
+  int depth = 0;
+  if (is_extension()) {
+    strings::SubstituteAndAppend(&contents, "extend .$0 {\n",
+                              containing_type()->full_name());
+    depth = 1;
+  }
+  DebugString(depth, &contents, debug_string_options);
+  if (is_extension()) {
+    contents.append("}\n");
+  }
+  return contents;
+}
+
+// The field type string used in FieldDescriptor::DebugString()
+std::string FieldDescriptor::FieldTypeNameDebugString() const {
+  switch (type()) {
+    case TYPE_MESSAGE:
+      return "." + message_type()->full_name();
+    case TYPE_ENUM:
+      return "." + enum_type()->full_name();
+    default:
+      return kTypeToName[type()];
+  }
+}
+
+void FieldDescriptor::DebugString(
+    int depth, std::string* contents,
+    const DebugStringOptions& debug_string_options) const {
+  std::string prefix(depth * 2, ' ');
+  std::string field_type;
+
+  // Special case map fields.
+  if (is_map()) {
+    strings::SubstituteAndAppend(
+        &field_type, "map<$0, $1>",
+        message_type()->field(0)->FieldTypeNameDebugString(),
+        message_type()->field(1)->FieldTypeNameDebugString());
+  } else {
+    field_type = FieldTypeNameDebugString();
+  }
+
+  std::string label = StrCat(kLabelToName[this->label()], " ");
+
+  // Label is omitted for maps, oneof, and plain proto3 fields.
+  if (is_map() || real_containing_oneof() ||
+      (is_optional() && !has_optional_keyword())) {
+    label.clear();
+  }
+
+  SourceLocationCommentPrinter comment_printer(this, prefix,
+                                               debug_string_options);
+  comment_printer.AddPreComment(contents);
+
+  strings::SubstituteAndAppend(
+      contents, "$0$1$2 $3 = $4", prefix, label, field_type,
+      type() == TYPE_GROUP ? message_type()->name() : name(), number());
+
+  bool bracketed = false;
+  if (has_default_value()) {
+    bracketed = true;
+    strings::SubstituteAndAppend(contents, " [default = $0",
+                              DefaultValueAsString(true));
+  }
+  if (has_json_name_) {
+    if (!bracketed) {
+      bracketed = true;
+      contents->append(" [");
+    } else {
+      contents->append(", ");
+    }
+    contents->append("json_name = \"");
+    contents->append(CEscape(json_name()));
+    contents->append("\"");
+  }
+
+  std::string formatted_options;
+  if (FormatBracketedOptions(depth, options(), file()->pool(),
+                             &formatted_options)) {
+    contents->append(bracketed ? ", " : " [");
+    bracketed = true;
+    contents->append(formatted_options);
+  }
+
+  if (bracketed) {
+    contents->append("]");
+  }
+
+  if (type() == TYPE_GROUP) {
+    if (debug_string_options.elide_group_body) {
+      contents->append(" { ... };\n");
+    } else {
+      message_type()->DebugString(depth, contents, debug_string_options,
+                                  /* include_opening_clause */ false);
+    }
+  } else {
+    contents->append(";\n");
+  }
+
+  comment_printer.AddPostComment(contents);
+}
+
+std::string OneofDescriptor::DebugString() const {
+  DebugStringOptions options;  // default values
+  return DebugStringWithOptions(options);
+}
+
+std::string OneofDescriptor::DebugStringWithOptions(
+    const DebugStringOptions& options) const {
+  std::string contents;
+  DebugString(0, &contents, options);
+  return contents;
+}
+
+void OneofDescriptor::DebugString(
+    int depth, std::string* contents,
+    const DebugStringOptions& debug_string_options) const {
+  std::string prefix(depth * 2, ' ');
+  ++depth;
+  SourceLocationCommentPrinter comment_printer(this, prefix,
+                                               debug_string_options);
+  comment_printer.AddPreComment(contents);
+  strings::SubstituteAndAppend(contents, "$0oneof $1 {", prefix, name());
+
+  FormatLineOptions(depth, options(), containing_type()->file()->pool(),
+                    contents);
+
+  if (debug_string_options.elide_oneof_body) {
+    contents->append(" ... }\n");
+  } else {
+    contents->append("\n");
+    for (int i = 0; i < field_count(); i++) {
+      field(i)->DebugString(depth, contents, debug_string_options);
+    }
+    strings::SubstituteAndAppend(contents, "$0}\n", prefix);
+  }
+  comment_printer.AddPostComment(contents);
+}
+
+std::string EnumDescriptor::DebugString() const {
+  DebugStringOptions options;  // default values
+  return DebugStringWithOptions(options);
+}
+
+std::string EnumDescriptor::DebugStringWithOptions(
+    const DebugStringOptions& options) const {
+  std::string contents;
+  DebugString(0, &contents, options);
+  return contents;
+}
+
+void EnumDescriptor::DebugString(
+    int depth, std::string* contents,
+    const DebugStringOptions& debug_string_options) const {
+  std::string prefix(depth * 2, ' ');
+  ++depth;
+
+  SourceLocationCommentPrinter comment_printer(this, prefix,
+                                               debug_string_options);
+  comment_printer.AddPreComment(contents);
+
+  strings::SubstituteAndAppend(contents, "$0enum $1 {\n", prefix, name());
+
+  FormatLineOptions(depth, options(), file()->pool(), contents);
+
+  for (int i = 0; i < value_count(); i++) {
+    value(i)->DebugString(depth, contents, debug_string_options);
+  }
+
+  if (reserved_range_count() > 0) {
+    strings::SubstituteAndAppend(contents, "$0  reserved ", prefix);
+    for (int i = 0; i < reserved_range_count(); i++) {
+      const EnumDescriptor::ReservedRange* range = reserved_range(i);
+      if (range->end == range->start) {
+        strings::SubstituteAndAppend(contents, "$0, ", range->start);
+      } else if (range->end == INT_MAX) {
+        strings::SubstituteAndAppend(contents, "$0 to max, ", range->start);
+      } else {
+        strings::SubstituteAndAppend(contents, "$0 to $1, ", range->start,
+                                  range->end);
+      }
+    }
+    contents->replace(contents->size() - 2, 2, ";\n");
+  }
+
+  if (reserved_name_count() > 0) {
+    strings::SubstituteAndAppend(contents, "$0  reserved ", prefix);
+    for (int i = 0; i < reserved_name_count(); i++) {
+      strings::SubstituteAndAppend(contents, "\"$0\", ",
+                                CEscape(reserved_name(i)));
+    }
+    contents->replace(contents->size() - 2, 2, ";\n");
+  }
+
+  strings::SubstituteAndAppend(contents, "$0}\n", prefix);
+
+  comment_printer.AddPostComment(contents);
+}
+
+std::string EnumValueDescriptor::DebugString() const {
+  DebugStringOptions options;  // default values
+  return DebugStringWithOptions(options);
+}
+
+std::string EnumValueDescriptor::DebugStringWithOptions(
+    const DebugStringOptions& options) const {
+  std::string contents;
+  DebugString(0, &contents, options);
+  return contents;
+}
+
+void EnumValueDescriptor::DebugString(
+    int depth, std::string* contents,
+    const DebugStringOptions& debug_string_options) const {
+  std::string prefix(depth * 2, ' ');
+
+  SourceLocationCommentPrinter comment_printer(this, prefix,
+                                               debug_string_options);
+  comment_printer.AddPreComment(contents);
+
+  strings::SubstituteAndAppend(contents, "$0$1 = $2", prefix, name(), number());
+
+  std::string formatted_options;
+  if (FormatBracketedOptions(depth, options(), type()->file()->pool(),
+                             &formatted_options)) {
+    strings::SubstituteAndAppend(contents, " [$0]", formatted_options);
+  }
+  contents->append(";\n");
+
+  comment_printer.AddPostComment(contents);
+}
+
+std::string ServiceDescriptor::DebugString() const {
+  DebugStringOptions options;  // default values
+  return DebugStringWithOptions(options);
+}
+
+std::string ServiceDescriptor::DebugStringWithOptions(
+    const DebugStringOptions& options) const {
+  std::string contents;
+  DebugString(&contents, options);
+  return contents;
+}
+
+void ServiceDescriptor::DebugString(
+    std::string* contents,
+    const DebugStringOptions& debug_string_options) const {
+  SourceLocationCommentPrinter comment_printer(this, /* prefix */ "",
+                                               debug_string_options);
+  comment_printer.AddPreComment(contents);
+
+  strings::SubstituteAndAppend(contents, "service $0 {\n", name());
+
+  FormatLineOptions(1, options(), file()->pool(), contents);
+
+  for (int i = 0; i < method_count(); i++) {
+    method(i)->DebugString(1, contents, debug_string_options);
+  }
+
+  contents->append("}\n");
+
+  comment_printer.AddPostComment(contents);
+}
+
+std::string MethodDescriptor::DebugString() const {
+  DebugStringOptions options;  // default values
+  return DebugStringWithOptions(options);
+}
+
+std::string MethodDescriptor::DebugStringWithOptions(
+    const DebugStringOptions& options) const {
+  std::string contents;
+  DebugString(0, &contents, options);
+  return contents;
+}
+
+void MethodDescriptor::DebugString(
+    int depth, std::string* contents,
+    const DebugStringOptions& debug_string_options) const {
+  std::string prefix(depth * 2, ' ');
+  ++depth;
+
+  SourceLocationCommentPrinter comment_printer(this, prefix,
+                                               debug_string_options);
+  comment_printer.AddPreComment(contents);
+
+  strings::SubstituteAndAppend(
+      contents, "$0rpc $1($4.$2) returns ($5.$3)", prefix, name(),
+      input_type()->full_name(), output_type()->full_name(),
+      client_streaming() ? "stream " : "", server_streaming() ? "stream " : "");
+
+  std::string formatted_options;
+  if (FormatLineOptions(depth, options(), service()->file()->pool(),
+                        &formatted_options)) {
+    strings::SubstituteAndAppend(contents, " {\n$0$1}\n", formatted_options,
+                              prefix);
+  } else {
+    contents->append(";\n");
+  }
+
+  comment_printer.AddPostComment(contents);
+}
+
+// Location methods ===============================================
+
+bool FileDescriptor::GetSourceLocation(const std::vector<int>& path,
+                                       SourceLocation* out_location) const {
+  GOOGLE_CHECK(out_location != nullptr);
+  if (source_code_info_) {
+    if (const SourceCodeInfo_Location* loc =
+            tables_->GetSourceLocation(path, source_code_info_)) {
+      const RepeatedField<int32_t>& span = loc->span();
+      if (span.size() == 3 || span.size() == 4) {
+        out_location->start_line = span.Get(0);
+        out_location->start_column = span.Get(1);
+        out_location->end_line = span.Get(span.size() == 3 ? 0 : 2);
+        out_location->end_column = span.Get(span.size() - 1);
+
+        out_location->leading_comments = loc->leading_comments();
+        out_location->trailing_comments = loc->trailing_comments();
+        out_location->leading_detached_comments.assign(
+            loc->leading_detached_comments().begin(),
+            loc->leading_detached_comments().end());
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool FileDescriptor::GetSourceLocation(SourceLocation* out_location) const {
+  std::vector<int> path;  // empty path for root FileDescriptor
+  return GetSourceLocation(path, out_location);
+}
+
+bool FieldDescriptor::is_packed() const {
+  if (!is_packable()) return false;
+  if (file_->syntax() == FileDescriptor::SYNTAX_PROTO2) {
+    return (options_ != nullptr) && options_->packed();
+  } else {
+    return options_ == nullptr || !options_->has_packed() || options_->packed();
+  }
+}
+
+bool Descriptor::GetSourceLocation(SourceLocation* out_location) const {
+  std::vector<int> path;
+  GetLocationPath(&path);
+  return file()->GetSourceLocation(path, out_location);
+}
+
+bool FieldDescriptor::GetSourceLocation(SourceLocation* out_location) const {
+  std::vector<int> path;
+  GetLocationPath(&path);
+  return file()->GetSourceLocation(path, out_location);
+}
+
+bool OneofDescriptor::GetSourceLocation(SourceLocation* out_location) const {
+  std::vector<int> path;
+  GetLocationPath(&path);
+  return containing_type()->file()->GetSourceLocation(path, out_location);
+}
+
+bool EnumDescriptor::GetSourceLocation(SourceLocation* out_location) const {
+  std::vector<int> path;
+  GetLocationPath(&path);
+  return file()->GetSourceLocation(path, out_location);
+}
+
+bool MethodDescriptor::GetSourceLocation(SourceLocation* out_location) const {
+  std::vector<int> path;
+  GetLocationPath(&path);
+  return service()->file()->GetSourceLocation(path, out_location);
+}
+
+bool ServiceDescriptor::GetSourceLocation(SourceLocation* out_location) const {
+  std::vector<int> path;
+  GetLocationPath(&path);
+  return file()->GetSourceLocation(path, out_location);
+}
+
+bool EnumValueDescriptor::GetSourceLocation(
+    SourceLocation* out_location) const {
+  std::vector<int> path;
+  GetLocationPath(&path);
+  return type()->file()->GetSourceLocation(path, out_location);
+}
+
+void Descriptor::GetLocationPath(std::vector<int>* output) const {
+  if (containing_type()) {
+    containing_type()->GetLocationPath(output);
+    output->push_back(DescriptorProto::kNestedTypeFieldNumber);
+    output->push_back(index());
+  } else {
+    output->push_back(FileDescriptorProto::kMessageTypeFieldNumber);
+    output->push_back(index());
+  }
+}
+
+void FieldDescriptor::GetLocationPath(std::vector<int>* output) const {
+  if (is_extension()) {
+    if (extension_scope() == nullptr) {
+      output->push_back(FileDescriptorProto::kExtensionFieldNumber);
+      output->push_back(index());
+    } else {
+      extension_scope()->GetLocationPath(output);
+      output->push_back(DescriptorProto::kExtensionFieldNumber);
+      output->push_back(index());
+    }
+  } else {
+    containing_type()->GetLocationPath(output);
+    output->push_back(DescriptorProto::kFieldFieldNumber);
+    output->push_back(index());
+  }
+}
+
+void OneofDescriptor::GetLocationPath(std::vector<int>* output) const {
+  containing_type()->GetLocationPath(output);
+  output->push_back(DescriptorProto::kOneofDeclFieldNumber);
+  output->push_back(index());
+}
+
+void EnumDescriptor::GetLocationPath(std::vector<int>* output) const {
+  if (containing_type()) {
+    containing_type()->GetLocationPath(output);
+    output->push_back(DescriptorProto::kEnumTypeFieldNumber);
+    output->push_back(index());
+  } else {
+    output->push_back(FileDescriptorProto::kEnumTypeFieldNumber);
+    output->push_back(index());
+  }
+}
+
+void EnumValueDescriptor::GetLocationPath(std::vector<int>* output) const {
+  type()->GetLocationPath(output);
+  output->push_back(EnumDescriptorProto::kValueFieldNumber);
+  output->push_back(index());
+}
+
+void ServiceDescriptor::GetLocationPath(std::vector<int>* output) const {
+  output->push_back(FileDescriptorProto::kServiceFieldNumber);
+  output->push_back(index());
+}
+
+void MethodDescriptor::GetLocationPath(std::vector<int>* output) const {
+  service()->GetLocationPath(output);
+  output->push_back(ServiceDescriptorProto::kMethodFieldNumber);
+  output->push_back(index());
+}
+
+// ===================================================================
+
+namespace {
+
+// Represents an options message to interpret. Extension names in the option
+// name are resolved relative to name_scope. element_name and orig_opt are
+// used only for error reporting (since the parser records locations against
+// pointers in the original options, not the mutable copy). The Message must be
+// one of the Options messages in descriptor.proto.
+struct OptionsToInterpret {
+  OptionsToInterpret(const std::string& ns, const std::string& el,
+                     const std::vector<int>& path, const Message* orig_opt,
+                     Message* opt)
+      : name_scope(ns),
+        element_name(el),
+        element_path(path),
+        original_options(orig_opt),
+        options(opt) {}
+  std::string name_scope;
+  std::string element_name;
+  std::vector<int> element_path;
+  const Message* original_options;
+  Message* options;
+};
+
+}  // namespace
+
+class DescriptorBuilder {
+ public:
+  DescriptorBuilder(const DescriptorPool* pool, DescriptorPool::Tables* tables,
+                    DescriptorPool::ErrorCollector* error_collector);
+  ~DescriptorBuilder();
+
+  const FileDescriptor* BuildFile(const FileDescriptorProto& proto);
+
+ private:
+  friend class OptionInterpreter;
+
+  // Non-recursive part of BuildFile functionality.
+  FileDescriptor* BuildFileImpl(const FileDescriptorProto& proto,
+                                internal::FlatAllocator& alloc);
+
+  const DescriptorPool* pool_;
+  DescriptorPool::Tables* tables_;  // for convenience
+  DescriptorPool::ErrorCollector* error_collector_;
+
+  // As we build descriptors we store copies of the options messages in
+  // them. We put pointers to those copies in this vector, as we build, so we
+  // can later (after cross-linking) interpret those options.
+  std::vector<OptionsToInterpret> options_to_interpret_;
+
+  bool had_errors_;
+  std::string filename_;
+  FileDescriptor* file_;
+  FileDescriptorTables* file_tables_;
+  std::set<const FileDescriptor*> dependencies_;
+
+  struct MessageHints {
+    int fields_to_suggest = 0;
+    const Message* first_reason = nullptr;
+    DescriptorPool::ErrorCollector::ErrorLocation first_reason_location =
+        DescriptorPool::ErrorCollector::ErrorLocation::OTHER;
+
+    void RequestHintOnFieldNumbers(
+        const Message& reason,
+        DescriptorPool::ErrorCollector::ErrorLocation reason_location,
+        int range_start = 0, int range_end = 1) {
+      auto fit = [](int value) {
+        return std::min(std::max(value, 0), FieldDescriptor::kMaxNumber);
+      };
+      fields_to_suggest =
+          fit(fields_to_suggest + fit(fit(range_end) - fit(range_start)));
+      if (first_reason) return;
+      first_reason = &reason;
+      first_reason_location = reason_location;
+    }
+  };
+
+  std::unordered_map<const Descriptor*, MessageHints> message_hints_;
+
+  // unused_dependency_ is used to record the unused imported files.
+  // Note: public import is not considered.
+  std::set<const FileDescriptor*> unused_dependency_;
+
+  // If LookupSymbol() finds a symbol that is in a file which is not a declared
+  // dependency of this file, it will fail, but will set
+  // possible_undeclared_dependency_ to point at that file.  This is only used
+  // by AddNotDefinedError() to report a more useful error message.
+  // possible_undeclared_dependency_name_ is the name of the symbol that was
+  // actually found in possible_undeclared_dependency_, which may be a parent
+  // of the symbol actually looked for.
+  const FileDescriptor* possible_undeclared_dependency_;
+  std::string possible_undeclared_dependency_name_;
+
+  // If LookupSymbol() could resolve a symbol which is not defined,
+  // record the resolved name.  This is only used by AddNotDefinedError()
+  // to report a more useful error message.
+  std::string undefine_resolved_name_;
+
+  // Tracker for current recursion depth to implement recursion protection.
+  //
+  // Counts down to 0 when there is no depth remaining.
+  //
+  // Maximum recursion depth corresponds to 32 nested message declarations.
+  int recursion_depth_ = 32;
+
+  void AddError(const std::string& element_name, const Message& descriptor,
+                DescriptorPool::ErrorCollector::ErrorLocation location,
+                const std::string& error);
+  void AddError(const std::string& element_name, const Message& descriptor,
+                DescriptorPool::ErrorCollector::ErrorLocation location,
+                const char* error);
+  void AddRecursiveImportError(const FileDescriptorProto& proto, int from_here);
+  void AddTwiceListedError(const FileDescriptorProto& proto, int index);
+  void AddImportError(const FileDescriptorProto& proto, int index);
+
+  // Adds an error indicating that undefined_symbol was not defined.  Must
+  // only be called after LookupSymbol() fails.
+  void AddNotDefinedError(
+      const std::string& element_name, const Message& descriptor,
+      DescriptorPool::ErrorCollector::ErrorLocation location,
+      const std::string& undefined_symbol);
+
+  void AddWarning(const std::string& element_name, const Message& descriptor,
+                  DescriptorPool::ErrorCollector::ErrorLocation location,
+                  const std::string& error);
+
+  // Silly helper which determines if the given file is in the given package.
+  // I.e., either file->package() == package_name or file->package() is a
+  // nested package within package_name.
+  bool IsInPackage(const FileDescriptor* file, const std::string& package_name);
+
+  // Helper function which finds all public dependencies of the given file, and
+  // stores the them in the dependencies_ set in the builder.
+  void RecordPublicDependencies(const FileDescriptor* file);
+
+  // Like tables_->FindSymbol(), but additionally:
+  // - Search the pool's underlay if not found in tables_.
+  // - Insure that the resulting Symbol is from one of the file's declared
+  //   dependencies.
+  Symbol FindSymbol(const std::string& name, bool build_it = true);
+
+  // Like FindSymbol() but does not require that the symbol is in one of the
+  // file's declared dependencies.
+  Symbol FindSymbolNotEnforcingDeps(const std::string& name,
+                                    bool build_it = true);
+
+  // This implements the body of FindSymbolNotEnforcingDeps().
+  Symbol FindSymbolNotEnforcingDepsHelper(const DescriptorPool* pool,
+                                          const std::string& name,
+                                          bool build_it = true);
+
+  // Like FindSymbol(), but looks up the name relative to some other symbol
+  // name.  This first searches siblings of relative_to, then siblings of its
+  // parents, etc.  For example, LookupSymbol("foo.bar", "baz.moo.corge") makes
+  // the following calls, returning the first non-null result:
+  // FindSymbol("baz.moo.foo.bar"), FindSymbol("baz.foo.bar"),
+  // FindSymbol("foo.bar").  If AllowUnknownDependencies() has been called
+  // on the DescriptorPool, this will generate a placeholder type if
+  // the name is not found (unless the name itself is malformed).  The
+  // placeholder_type parameter indicates what kind of placeholder should be
+  // constructed in this case.  The resolve_mode parameter determines whether
+  // any symbol is returned, or only symbols that are types.  Note, however,
+  // that LookupSymbol may still return a non-type symbol in LOOKUP_TYPES mode,
+  // if it believes that's all it could refer to.  The caller should always
+  // check that it receives the type of symbol it was expecting.
+  enum ResolveMode { LOOKUP_ALL, LOOKUP_TYPES };
+  Symbol LookupSymbol(const std::string& name, const std::string& relative_to,
+                      DescriptorPool::PlaceholderType placeholder_type =
+                          DescriptorPool::PLACEHOLDER_MESSAGE,
+                      ResolveMode resolve_mode = LOOKUP_ALL,
+                      bool build_it = true);
+
+  // Like LookupSymbol() but will not return a placeholder even if
+  // AllowUnknownDependencies() has been used.
+  Symbol LookupSymbolNoPlaceholder(const std::string& name,
+                                   const std::string& relative_to,
+                                   ResolveMode resolve_mode = LOOKUP_ALL,
+                                   bool build_it = true);
+
+  // Calls tables_->AddSymbol() and records an error if it fails.  Returns
+  // true if successful or false if failed, though most callers can ignore
+  // the return value since an error has already been recorded.
+  bool AddSymbol(const std::string& full_name, const void* parent,
+                 const std::string& name, const Message& proto, Symbol symbol);
+
+  // Like AddSymbol(), but succeeds if the symbol is already defined as long
+  // as the existing definition is also a package (because it's OK to define
+  // the same package in two different files).  Also adds all parents of the
+  // package to the symbol table (e.g. AddPackage("foo.bar", ...) will add
+  // "foo.bar" and "foo" to the table).
+  void AddPackage(const std::string& name, const Message& proto,
+                  FileDescriptor* file);
+
+  // Checks that the symbol name contains only alphanumeric characters and
+  // underscores.  Records an error otherwise.
+  void ValidateSymbolName(const std::string& name, const std::string& full_name,
+                          const Message& proto);
+
+  // Allocates a copy of orig_options in tables_ and stores it in the
+  // descriptor. Remembers its uninterpreted options, to be interpreted
+  // later. DescriptorT must be one of the Descriptor messages from
+  // descriptor.proto.
+  template <class DescriptorT>
+  void AllocateOptions(const typename DescriptorT::OptionsType& orig_options,
+                       DescriptorT* descriptor, int options_field_tag,
+                       const std::string& option_name,
+                       internal::FlatAllocator& alloc);
+  // Specialization for FileOptions.
+  void AllocateOptions(const FileOptions& orig_options,
+                       FileDescriptor* descriptor,
+                       internal::FlatAllocator& alloc);
+
+  // Implementation for AllocateOptions(). Don't call this directly.
+  template <class DescriptorT>
+  void AllocateOptionsImpl(
+      const std::string& name_scope, const std::string& element_name,
+      const typename DescriptorT::OptionsType& orig_options,
+      DescriptorT* descriptor, const std::vector<int>& options_path,
+      const std::string& option_name, internal::FlatAllocator& alloc);
+
+  // Allocates an array of two strings, the first one is a copy of `proto_name`,
+  // and the second one is the full name.
+  // Full proto name is "scope.proto_name" if scope is non-empty and
+  // "proto_name" otherwise.
+  const std::string* AllocateNameStrings(const std::string& scope,
+                                         const std::string& proto_name,
+                                         internal::FlatAllocator& alloc);
+
+  // These methods all have the same signature for the sake of the BUILD_ARRAY
+  // macro, below.
+  void BuildMessage(const DescriptorProto& proto, const Descriptor* parent,
+                    Descriptor* result, internal::FlatAllocator& alloc);
+  void BuildFieldOrExtension(const FieldDescriptorProto& proto,
+                             Descriptor* parent, FieldDescriptor* result,
+                             bool is_extension, internal::FlatAllocator& alloc);
+  void BuildField(const FieldDescriptorProto& proto, Descriptor* parent,
+                  FieldDescriptor* result, internal::FlatAllocator& alloc) {
+    BuildFieldOrExtension(proto, parent, result, false, alloc);
+  }
+  void BuildExtension(const FieldDescriptorProto& proto, Descriptor* parent,
+                      FieldDescriptor* result, internal::FlatAllocator& alloc) {
+    BuildFieldOrExtension(proto, parent, result, true, alloc);
+  }
+  void BuildExtensionRange(const DescriptorProto::ExtensionRange& proto,
+                           const Descriptor* parent,
+                           Descriptor::ExtensionRange* result,
+                           internal::FlatAllocator& alloc);
+  void BuildReservedRange(const DescriptorProto::ReservedRange& proto,
+                          const Descriptor* parent,
+                          Descriptor::ReservedRange* result,
+                          internal::FlatAllocator& alloc);
+  void BuildReservedRange(const EnumDescriptorProto::EnumReservedRange& proto,
+                          const EnumDescriptor* parent,
+                          EnumDescriptor::ReservedRange* result,
+                          internal::FlatAllocator& alloc);
+  void BuildOneof(const OneofDescriptorProto& proto, Descriptor* parent,
+                  OneofDescriptor* result, internal::FlatAllocator& alloc);
+  void CheckEnumValueUniqueness(const EnumDescriptorProto& proto,
+                                const EnumDescriptor* result);
+  void BuildEnum(const EnumDescriptorProto& proto, const Descriptor* parent,
+                 EnumDescriptor* result, internal::FlatAllocator& alloc);
+  void BuildEnumValue(const EnumValueDescriptorProto& proto,
+                      const EnumDescriptor* parent, EnumValueDescriptor* result,
+                      internal::FlatAllocator& alloc);
+  void BuildService(const ServiceDescriptorProto& proto, const void* dummy,
+                    ServiceDescriptor* result, internal::FlatAllocator& alloc);
+  void BuildMethod(const MethodDescriptorProto& proto,
+                   const ServiceDescriptor* parent, MethodDescriptor* result,
+                   internal::FlatAllocator& alloc);
+
+  void LogUnusedDependency(const FileDescriptorProto& proto,
+                           const FileDescriptor* result);
+
+  // Must be run only after building.
+  //
+  // NOTE: Options will not be available during cross-linking, as they
+  // have not yet been interpreted. Defer any handling of options to the
+  // Validate*Options methods.
+  void CrossLinkFile(FileDescriptor* file, const FileDescriptorProto& proto);
+  void CrossLinkMessage(Descriptor* message, const DescriptorProto& proto);
+  void CrossLinkField(FieldDescriptor* field,
+                      const FieldDescriptorProto& proto);
+  void CrossLinkExtensionRange(Descriptor::ExtensionRange* range,
+                               const DescriptorProto::ExtensionRange& proto);
+  void CrossLinkEnum(EnumDescriptor* enum_type,
+                     const EnumDescriptorProto& proto);
+  void CrossLinkEnumValue(EnumValueDescriptor* enum_value,
+                          const EnumValueDescriptorProto& proto);
+  void CrossLinkService(ServiceDescriptor* service,
+                        const ServiceDescriptorProto& proto);
+  void CrossLinkMethod(MethodDescriptor* method,
+                       const MethodDescriptorProto& proto);
+  void SuggestFieldNumbers(FileDescriptor* file,
+                           const FileDescriptorProto& proto);
+
+  // Must be run only after cross-linking.
+  void InterpretOptions();
+
+  // A helper class for interpreting options.
+  class OptionInterpreter {
+   public:
+    // Creates an interpreter that operates in the context of the pool of the
+    // specified builder, which must not be nullptr. We don't take ownership of
+    // the builder.
+    explicit OptionInterpreter(DescriptorBuilder* builder);
+
+    ~OptionInterpreter();
+
+    // Interprets the uninterpreted options in the specified Options message.
+    // On error, calls AddError() on the underlying builder and returns false.
+    // Otherwise returns true.
+    bool InterpretOptions(OptionsToInterpret* options_to_interpret);
+
+    // Updates the given source code info by re-writing uninterpreted option
+    // locations to refer to the corresponding interpreted option.
+    void UpdateSourceCodeInfo(SourceCodeInfo* info);
+
+    class AggregateOptionFinder;
+
+   private:
+    // Interprets uninterpreted_option_ on the specified message, which
+    // must be the mutable copy of the original options message to which
+    // uninterpreted_option_ belongs. The given src_path is the source
+    // location path to the uninterpreted option, and options_path is the
+    // source location path to the options message. The location paths are
+    // recorded and then used in UpdateSourceCodeInfo.
+    bool InterpretSingleOption(Message* options,
+                               const std::vector<int>& src_path,
+                               const std::vector<int>& options_path);
+
+    // Adds the uninterpreted_option to the given options message verbatim.
+    // Used when AllowUnknownDependencies() is in effect and we can't find
+    // the option's definition.
+    void AddWithoutInterpreting(const UninterpretedOption& uninterpreted_option,
+                                Message* options);
+
+    // A recursive helper function that drills into the intermediate fields
+    // in unknown_fields to check if field innermost_field is set on the
+    // innermost message. Returns false and sets an error if so.
+    bool ExamineIfOptionIsSet(
+        std::vector<const FieldDescriptor*>::const_iterator
+            intermediate_fields_iter,
+        std::vector<const FieldDescriptor*>::const_iterator
+            intermediate_fields_end,
+        const FieldDescriptor* innermost_field,
+        const std::string& debug_msg_name,
+        const UnknownFieldSet& unknown_fields);
+
+    // Validates the value for the option field of the currently interpreted
+    // option and then sets it on the unknown_field.
+    bool SetOptionValue(const FieldDescriptor* option_field,
+                        UnknownFieldSet* unknown_fields);
+
+    // Parses an aggregate value for a CPPTYPE_MESSAGE option and
+    // saves it into *unknown_fields.
+    bool SetAggregateOption(const FieldDescriptor* option_field,
+                            UnknownFieldSet* unknown_fields);
+
+    // Convenience functions to set an int field the right way, depending on
+    // its wire type (a single int CppType can represent multiple wire types).
+    void SetInt32(int number, int32_t value, FieldDescriptor::Type type,
+                  UnknownFieldSet* unknown_fields);
+    void SetInt64(int number, int64_t value, FieldDescriptor::Type type,
+                  UnknownFieldSet* unknown_fields);
+    void SetUInt32(int number, uint32_t value, FieldDescriptor::Type type,
+                   UnknownFieldSet* unknown_fields);
+    void SetUInt64(int number, uint64_t value, FieldDescriptor::Type type,
+                   UnknownFieldSet* unknown_fields);
+
+    // A helper function that adds an error at the specified location of the
+    // option we're currently interpreting, and returns false.
+    bool AddOptionError(DescriptorPool::ErrorCollector::ErrorLocation location,
+                        const std::string& msg) {
+      builder_->AddError(options_to_interpret_->element_name,
+                         *uninterpreted_option_, location, msg);
+      return false;
+    }
+
+    // A helper function that adds an error at the location of the option name
+    // and returns false.
+    bool AddNameError(const std::string& msg) {
+#ifdef PROTOBUF_INTERNAL_IGNORE_FIELD_NAME_ERRORS_
+      return true;
+#else   // PROTOBUF_INTERNAL_IGNORE_FIELD_NAME_ERRORS_
+      return AddOptionError(DescriptorPool::ErrorCollector::OPTION_NAME, msg);
+#endif  // PROTOBUF_INTERNAL_IGNORE_FIELD_NAME_ERRORS_
+    }
+
+    // A helper function that adds an error at the location of the option name
+    // and returns false.
+    bool AddValueError(const std::string& msg) {
+      return AddOptionError(DescriptorPool::ErrorCollector::OPTION_VALUE, msg);
+    }
+
+    // We interpret against this builder's pool. Is never nullptr. We don't own
+    // this pointer.
+    DescriptorBuilder* builder_;
+
+    // The options we're currently interpreting, or nullptr if we're not in a
+    // call to InterpretOptions.
+    const OptionsToInterpret* options_to_interpret_;
+
+    // The option we're currently interpreting within options_to_interpret_, or
+    // nullptr if we're not in a call to InterpretOptions(). This points to a
+    // submessage of the original option, not the mutable copy. Therefore we
+    // can use it to find locations recorded by the parser.
+    const UninterpretedOption* uninterpreted_option_;
+
+    // This maps the element path of uninterpreted options to the element path
+    // of the resulting interpreted option. This is used to modify a file's
+    // source code info to account for option interpretation.
+    std::map<std::vector<int>, std::vector<int>> interpreted_paths_;
+
+    // This maps the path to a repeated option field to the known number of
+    // elements the field contains. This is used to track the compute the
+    // index portion of the element path when interpreting a single option.
+    std::map<std::vector<int>, int> repeated_option_counts_;
+
+    // Factory used to create the dynamic messages we need to parse
+    // any aggregate option values we encounter.
+    DynamicMessageFactory dynamic_factory_;
+
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OptionInterpreter);
+  };
+
+  // Work-around for broken compilers:  According to the C++ standard,
+  // OptionInterpreter should have access to the private members of any class
+  // which has declared DescriptorBuilder as a friend.  Unfortunately some old
+  // versions of GCC and other compilers do not implement this correctly.  So,
+  // we have to have these intermediate methods to provide access.  We also
+  // redundantly declare OptionInterpreter a friend just to make things extra
+  // clear for these bad compilers.
+  friend class OptionInterpreter;
+  friend class OptionInterpreter::AggregateOptionFinder;
+
+  static inline bool get_allow_unknown(const DescriptorPool* pool) {
+    return pool->allow_unknown_;
+  }
+  static inline bool get_enforce_weak(const DescriptorPool* pool) {
+    return pool->enforce_weak_;
+  }
+  static inline bool get_is_placeholder(const Descriptor* descriptor) {
+    return descriptor != nullptr && descriptor->is_placeholder_;
+  }
+  static inline void assert_mutex_held(const DescriptorPool* pool) {
+    if (pool->mutex_ != nullptr) {
+      pool->mutex_->AssertHeld();
+    }
+  }
+
+  // Must be run only after options have been interpreted.
+  //
+  // NOTE: Validation code must only reference the options in the mutable
+  // descriptors, which are the ones that have been interpreted. The const
+  // proto references are passed in only so they can be provided to calls to
+  // AddError(). Do not look at their options, which have not been interpreted.
+  void ValidateFileOptions(FileDescriptor* file,
+                           const FileDescriptorProto& proto);
+  void ValidateMessageOptions(Descriptor* message,
+                              const DescriptorProto& proto);
+  void ValidateFieldOptions(FieldDescriptor* field,
+                            const FieldDescriptorProto& proto);
+  void ValidateEnumOptions(EnumDescriptor* enm,
+                           const EnumDescriptorProto& proto);
+  void ValidateEnumValueOptions(EnumValueDescriptor* enum_value,
+                                const EnumValueDescriptorProto& proto);
+  void ValidateExtensionRangeOptions(
+      const std::string& full_name, Descriptor::ExtensionRange* extension_range,
+      const DescriptorProto_ExtensionRange& proto);
+  void ValidateServiceOptions(ServiceDescriptor* service,
+                              const ServiceDescriptorProto& proto);
+  void ValidateMethodOptions(MethodDescriptor* method,
+                             const MethodDescriptorProto& proto);
+  void ValidateProto3(FileDescriptor* file, const FileDescriptorProto& proto);
+  void ValidateProto3Message(Descriptor* message, const DescriptorProto& proto);
+  void ValidateProto3Field(FieldDescriptor* field,
+                           const FieldDescriptorProto& proto);
+  void ValidateProto3Enum(EnumDescriptor* enm,
+                          const EnumDescriptorProto& proto);
+
+  // Returns true if the map entry message is compatible with the
+  // auto-generated entry message from map fields syntax.
+  bool ValidateMapEntry(FieldDescriptor* field,
+                        const FieldDescriptorProto& proto);
+
+  // Recursively detects naming conflicts with map entry types for a
+  // better error message.
+  void DetectMapConflicts(const Descriptor* message,
+                          const DescriptorProto& proto);
+
+  void ValidateJSType(FieldDescriptor* field,
+                      const FieldDescriptorProto& proto);
+};
+
+const FileDescriptor* DescriptorPool::BuildFile(
+    const FileDescriptorProto& proto) {
+  GOOGLE_CHECK(fallback_database_ == nullptr)
+      << "Cannot call BuildFile on a DescriptorPool that uses a "
+         "DescriptorDatabase.  You must instead find a way to get your file "
+         "into the underlying database.";
+  GOOGLE_CHECK(mutex_ == nullptr);  // Implied by the above GOOGLE_CHECK.
+  tables_->known_bad_symbols_.clear();
+  tables_->known_bad_files_.clear();
+  return DescriptorBuilder(this, tables_.get(), nullptr).BuildFile(proto);
+}
+
+const FileDescriptor* DescriptorPool::BuildFileCollectingErrors(
+    const FileDescriptorProto& proto, ErrorCollector* error_collector) {
+  GOOGLE_CHECK(fallback_database_ == nullptr)
+      << "Cannot call BuildFile on a DescriptorPool that uses a "
+         "DescriptorDatabase.  You must instead find a way to get your file "
+         "into the underlying database.";
+  GOOGLE_CHECK(mutex_ == nullptr);  // Implied by the above GOOGLE_CHECK.
+  tables_->known_bad_symbols_.clear();
+  tables_->known_bad_files_.clear();
+  return DescriptorBuilder(this, tables_.get(), error_collector)
+      .BuildFile(proto);
+}
+
+const FileDescriptor* DescriptorPool::BuildFileFromDatabase(
+    const FileDescriptorProto& proto) const {
+  mutex_->AssertHeld();
+  if (tables_->known_bad_files_.count(proto.name()) > 0) {
+    return nullptr;
+  }
+  const FileDescriptor* result =
+      DescriptorBuilder(this, tables_.get(), default_error_collector_)
+          .BuildFile(proto);
+  if (result == nullptr) {
+    tables_->known_bad_files_.insert(proto.name());
+  }
+  return result;
+}
+
+DescriptorBuilder::DescriptorBuilder(
+    const DescriptorPool* pool, DescriptorPool::Tables* tables,
+    DescriptorPool::ErrorCollector* error_collector)
+    : pool_(pool),
+      tables_(tables),
+      error_collector_(error_collector),
+      had_errors_(false),
+      possible_undeclared_dependency_(nullptr),
+      undefine_resolved_name_("") {}
+
+DescriptorBuilder::~DescriptorBuilder() {}
+
+void DescriptorBuilder::AddError(
+    const std::string& element_name, const Message& descriptor,
+    DescriptorPool::ErrorCollector::ErrorLocation location,
+    const std::string& error) {
+  if (error_collector_ == nullptr) {
+    if (!had_errors_) {
+      GOOGLE_LOG(ERROR) << "Invalid proto descriptor for file \"" << filename_
+                 << "\":";
+    }
+    GOOGLE_LOG(ERROR) << "  " << element_name << ": " << error;
+  } else {
+    error_collector_->AddError(filename_, element_name, &descriptor, location,
+                               error);
+  }
+  had_errors_ = true;
+}
+
+void DescriptorBuilder::AddError(
+    const std::string& element_name, const Message& descriptor,
+    DescriptorPool::ErrorCollector::ErrorLocation location, const char* error) {
+  AddError(element_name, descriptor, location, std::string(error));
+}
+
+void DescriptorBuilder::AddNotDefinedError(
+    const std::string& element_name, const Message& descriptor,
+    DescriptorPool::ErrorCollector::ErrorLocation location,
+    const std::string& undefined_symbol) {
+  if (possible_undeclared_dependency_ == nullptr &&
+      undefine_resolved_name_.empty()) {
+    AddError(element_name, descriptor, location,
+             "\"" + undefined_symbol + "\" is not defined.");
+  } else {
+    if (possible_undeclared_dependency_ != nullptr) {
+      AddError(element_name, descriptor, location,
+               "\"" + possible_undeclared_dependency_name_ +
+                   "\" seems to be defined in \"" +
+                   possible_undeclared_dependency_->name() +
+                   "\", which is not "
+                   "imported by \"" +
+                   filename_ +
+                   "\".  To use it here, please "
+                   "add the necessary import.");
+    }
+    if (!undefine_resolved_name_.empty()) {
+      AddError(element_name, descriptor, location,
+               "\"" + undefined_symbol + "\" is resolved to \"" +
+                   undefine_resolved_name_ +
+                   "\", which is not defined. "
+                   "The innermost scope is searched first in name resolution. "
+                   "Consider using a leading '.'(i.e., \"." +
+                   undefined_symbol + "\") to start from the outermost scope.");
+    }
+  }
+}
+
+void DescriptorBuilder::AddWarning(
+    const std::string& element_name, const Message& descriptor,
+    DescriptorPool::ErrorCollector::ErrorLocation location,
+    const std::string& error) {
+  if (error_collector_ == nullptr) {
+    GOOGLE_LOG(WARNING) << filename_ << " " << element_name << ": " << error;
+  } else {
+    error_collector_->AddWarning(filename_, element_name, &descriptor, location,
+                                 error);
+  }
+}
+
+bool DescriptorBuilder::IsInPackage(const FileDescriptor* file,
+                                    const std::string& package_name) {
+  return HasPrefixString(file->package(), package_name) &&
+         (file->package().size() == package_name.size() ||
+          file->package()[package_name.size()] == '.');
+}
+
+void DescriptorBuilder::RecordPublicDependencies(const FileDescriptor* file) {
+  if (file == nullptr || !dependencies_.insert(file).second) return;
+  for (int i = 0; file != nullptr && i < file->public_dependency_count(); i++) {
+    RecordPublicDependencies(file->public_dependency(i));
+  }
+}
+
+Symbol DescriptorBuilder::FindSymbolNotEnforcingDepsHelper(
+    const DescriptorPool* pool, const std::string& name, bool build_it) {
+  // If we are looking at an underlay, we must lock its mutex_, since we are
+  // accessing the underlay's tables_ directly.
+  MutexLockMaybe lock((pool == pool_) ? nullptr : pool->mutex_);
+
+  Symbol result = pool->tables_->FindSymbol(name);
+  if (result.IsNull() && pool->underlay_ != nullptr) {
+    // Symbol not found; check the underlay.
+    result = FindSymbolNotEnforcingDepsHelper(pool->underlay_, name);
+  }
+
+  if (result.IsNull()) {
+    // With lazily_build_dependencies_, a symbol lookup at cross link time is
+    // not guaranteed to be successful. In most cases, build_it will be false,
+    // which intentionally prevents us from building an import until it's
+    // actually needed. In some cases, like registering an extension, we want
+    // to build the file containing the symbol, and build_it will be set.
+    // Also, build_it will be true when !lazily_build_dependencies_, to provide
+    // better error reporting of missing dependencies.
+    if (build_it && pool->TryFindSymbolInFallbackDatabase(name)) {
+      result = pool->tables_->FindSymbol(name);
+    }
+  }
+
+  return result;
+}
+
+Symbol DescriptorBuilder::FindSymbolNotEnforcingDeps(const std::string& name,
+                                                     bool build_it) {
+  Symbol result = FindSymbolNotEnforcingDepsHelper(pool_, name, build_it);
+  // Only find symbols which were defined in this file or one of its
+  // dependencies.
+  const FileDescriptor* file = result.GetFile();
+  if (file == file_ || dependencies_.count(file) > 0) {
+    unused_dependency_.erase(file);
+  }
+  return result;
+}
+
+Symbol DescriptorBuilder::FindSymbol(const std::string& name, bool build_it) {
+  Symbol result = FindSymbolNotEnforcingDeps(name, build_it);
+
+  if (result.IsNull()) return result;
+
+  if (!pool_->enforce_dependencies_) {
+    // Hack for CompilerUpgrader, and also used for lazily_build_dependencies_
+    return result;
+  }
+
+  // Only find symbols which were defined in this file or one of its
+  // dependencies.
+  const FileDescriptor* file = result.GetFile();
+  if (file == file_ || dependencies_.count(file) > 0) {
+    return result;
+  }
+
+  if (result.IsPackage()) {
+    // Arg, this is overcomplicated.  The symbol is a package name.  It could
+    // be that the package was defined in multiple files.  result.GetFile()
+    // returns the first file we saw that used this package.  We've determined
+    // that that file is not a direct dependency of the file we are currently
+    // building, but it could be that some other file which *is* a direct
+    // dependency also defines the same package.  We can't really rule out this
+    // symbol unless none of the dependencies define it.
+    if (IsInPackage(file_, name)) return result;
+    for (std::set<const FileDescriptor*>::const_iterator it =
+             dependencies_.begin();
+         it != dependencies_.end(); ++it) {
+      // Note:  A dependency may be nullptr if it was not found or had errors.
+      if (*it != nullptr && IsInPackage(*it, name)) return result;
+    }
+  }
+
+  possible_undeclared_dependency_ = file;
+  possible_undeclared_dependency_name_ = name;
+  return Symbol();
+}
+
+Symbol DescriptorBuilder::LookupSymbolNoPlaceholder(
+    const std::string& name, const std::string& relative_to,
+    ResolveMode resolve_mode, bool build_it) {
+  possible_undeclared_dependency_ = nullptr;
+  undefine_resolved_name_.clear();
+
+  if (!name.empty() && name[0] == '.') {
+    // Fully-qualified name.
+    return FindSymbol(name.substr(1), build_it);
+  }
+
+  // If name is something like "Foo.Bar.baz", and symbols named "Foo" are
+  // defined in multiple parent scopes, we only want to find "Bar.baz" in the
+  // innermost one.  E.g., the following should produce an error:
+  //   message Bar { message Baz {} }
+  //   message Foo {
+  //     message Bar {
+  //     }
+  //     optional Bar.Baz baz = 1;
+  //   }
+  // So, we look for just "Foo" first, then look for "Bar.baz" within it if
+  // found.
+  std::string::size_type name_dot_pos = name.find_first_of('.');
+  std::string first_part_of_name;
+  if (name_dot_pos == std::string::npos) {
+    first_part_of_name = name;
+  } else {
+    first_part_of_name = name.substr(0, name_dot_pos);
+  }
+
+  std::string scope_to_try(relative_to);
+
+  while (true) {
+    // Chop off the last component of the scope.
+    std::string::size_type dot_pos = scope_to_try.find_last_of('.');
+    if (dot_pos == std::string::npos) {
+      return FindSymbol(name, build_it);
+    } else {
+      scope_to_try.erase(dot_pos);
+    }
+
+    // Append ".first_part_of_name" and try to find.
+    std::string::size_type old_size = scope_to_try.size();
+    scope_to_try.append(1, '.');
+    scope_to_try.append(first_part_of_name);
+    Symbol result = FindSymbol(scope_to_try, build_it);
+    if (!result.IsNull()) {
+      if (first_part_of_name.size() < name.size()) {
+        // name is a compound symbol, of which we only found the first part.
+        // Now try to look up the rest of it.
+        if (result.IsAggregate()) {
+          scope_to_try.append(name, first_part_of_name.size(),
+                              name.size() - first_part_of_name.size());
+          result = FindSymbol(scope_to_try, build_it);
+          if (result.IsNull()) {
+            undefine_resolved_name_ = scope_to_try;
+          }
+          return result;
+        } else {
+          // We found a symbol but it's not an aggregate.  Continue the loop.
+        }
+      } else {
+        if (resolve_mode == LOOKUP_TYPES && !result.IsType()) {
+          // We found a symbol but it's not a type.  Continue the loop.
+        } else {
+          return result;
+        }
+      }
+    }
+
+    // Not found.  Remove the name so we can try again.
+    scope_to_try.erase(old_size);
+  }
+}
+
+Symbol DescriptorBuilder::LookupSymbol(
+    const std::string& name, const std::string& relative_to,
+    DescriptorPool::PlaceholderType placeholder_type, ResolveMode resolve_mode,
+    bool build_it) {
+  Symbol result =
+      LookupSymbolNoPlaceholder(name, relative_to, resolve_mode, build_it);
+  if (result.IsNull() && pool_->allow_unknown_) {
+    // Not found, but AllowUnknownDependencies() is enabled.  Return a
+    // placeholder instead.
+    result = pool_->NewPlaceholderWithMutexHeld(name, placeholder_type);
+  }
+  return result;
+}
+
+static bool ValidateQualifiedName(StringPiece name) {
+  bool last_was_period = false;
+
+  for (char character : name) {
+    // I don't trust isalnum() due to locales.  :(
+    if (('a' <= character && character <= 'z') ||
+        ('A' <= character && character <= 'Z') ||
+        ('0' <= character && character <= '9') || (character == '_')) {
+      last_was_period = false;
+    } else if (character == '.') {
+      if (last_was_period) return false;
+      last_was_period = true;
+    } else {
+      return false;
+    }
+  }
+
+  return !name.empty() && !last_was_period;
+}
+
+Symbol DescriptorPool::NewPlaceholder(StringPiece name,
+                                      PlaceholderType placeholder_type) const {
+  MutexLockMaybe lock(mutex_);
+  return NewPlaceholderWithMutexHeld(name, placeholder_type);
+}
+
+Symbol DescriptorPool::NewPlaceholderWithMutexHeld(
+    StringPiece name, PlaceholderType placeholder_type) const {
+  if (mutex_) {
+    mutex_->AssertHeld();
+  }
+  // Compute names.
+  StringPiece placeholder_full_name;
+  StringPiece placeholder_name;
+  const std::string* placeholder_package;
+
+  if (!ValidateQualifiedName(name)) return Symbol();
+  if (name[0] == '.') {
+    // Fully-qualified.
+    placeholder_full_name = name.substr(1);
+  } else {
+    placeholder_full_name = name;
+  }
+
+  // Create the placeholders.
+  internal::FlatAllocator alloc;
+  alloc.PlanArray<FileDescriptor>(1);
+  alloc.PlanArray<std::string>(2);
+  if (placeholder_type == PLACEHOLDER_ENUM) {
+    alloc.PlanArray<EnumDescriptor>(1);
+    alloc.PlanArray<EnumValueDescriptor>(1);
+    alloc.PlanArray<std::string>(2);  // names for the descriptor.
+    alloc.PlanArray<std::string>(2);  // names for the value.
+  } else {
+    alloc.PlanArray<Descriptor>(1);
+    alloc.PlanArray<std::string>(2);  // names for the descriptor.
+    if (placeholder_type == PLACEHOLDER_EXTENDABLE_MESSAGE) {
+      alloc.PlanArray<Descriptor::ExtensionRange>(1);
+    }
+  }
+  alloc.FinalizePlanning(tables_);
+
+  const std::string::size_type dotpos = placeholder_full_name.find_last_of('.');
+  if (dotpos != std::string::npos) {
+    placeholder_package =
+        alloc.AllocateStrings(placeholder_full_name.substr(0, dotpos));
+    placeholder_name = placeholder_full_name.substr(dotpos + 1);
+  } else {
+    placeholder_package = alloc.AllocateStrings("");
+    placeholder_name = placeholder_full_name;
+  }
+
+  FileDescriptor* placeholder_file = NewPlaceholderFileWithMutexHeld(
+      StrCat(placeholder_full_name, ".placeholder.proto"), alloc);
+  placeholder_file->package_ = placeholder_package;
+
+  if (placeholder_type == PLACEHOLDER_ENUM) {
+    placeholder_file->enum_type_count_ = 1;
+    placeholder_file->enum_types_ = alloc.AllocateArray<EnumDescriptor>(1);
+
+    EnumDescriptor* placeholder_enum = &placeholder_file->enum_types_[0];
+    memset(static_cast<void*>(placeholder_enum), 0, sizeof(*placeholder_enum));
+
+    placeholder_enum->all_names_ =
+        alloc.AllocateStrings(placeholder_name, placeholder_full_name);
+    placeholder_enum->file_ = placeholder_file;
+    placeholder_enum->options_ = &EnumOptions::default_instance();
+    placeholder_enum->is_placeholder_ = true;
+    placeholder_enum->is_unqualified_placeholder_ = (name[0] != '.');
+
+    // Enums must have at least one value.
+    placeholder_enum->value_count_ = 1;
+    placeholder_enum->values_ = alloc.AllocateArray<EnumValueDescriptor>(1);
+    // Disable fast-path lookup for this enum.
+    placeholder_enum->sequential_value_limit_ = -1;
+
+    EnumValueDescriptor* placeholder_value = &placeholder_enum->values_[0];
+    memset(static_cast<void*>(placeholder_value), 0,
+           sizeof(*placeholder_value));
+
+    // Note that enum value names are siblings of their type, not children.
+    placeholder_value->all_names_ = alloc.AllocateStrings(
+        "PLACEHOLDER_VALUE", placeholder_package->empty()
+                                 ? "PLACEHOLDER_VALUE"
+                                 : *placeholder_package + ".PLACEHOLDER_VALUE");
+
+    placeholder_value->number_ = 0;
+    placeholder_value->type_ = placeholder_enum;
+    placeholder_value->options_ = &EnumValueOptions::default_instance();
+
+    return Symbol(placeholder_enum);
+  } else {
+    placeholder_file->message_type_count_ = 1;
+    placeholder_file->message_types_ = alloc.AllocateArray<Descriptor>(1);
+
+    Descriptor* placeholder_message = &placeholder_file->message_types_[0];
+    memset(static_cast<void*>(placeholder_message), 0,
+           sizeof(*placeholder_message));
+
+    placeholder_message->all_names_ =
+        alloc.AllocateStrings(placeholder_name, placeholder_full_name);
+    placeholder_message->file_ = placeholder_file;
+    placeholder_message->options_ = &MessageOptions::default_instance();
+    placeholder_message->is_placeholder_ = true;
+    placeholder_message->is_unqualified_placeholder_ = (name[0] != '.');
+
+    if (placeholder_type == PLACEHOLDER_EXTENDABLE_MESSAGE) {
+      placeholder_message->extension_range_count_ = 1;
+      placeholder_message->extension_ranges_ =
+          alloc.AllocateArray<Descriptor::ExtensionRange>(1);
+      placeholder_message->extension_ranges_[0].start = 1;
+      // kMaxNumber + 1 because ExtensionRange::end is exclusive.
+      placeholder_message->extension_ranges_[0].end =
+          FieldDescriptor::kMaxNumber + 1;
+      placeholder_message->extension_ranges_[0].options_ = nullptr;
+    }
+
+    return Symbol(placeholder_message);
+  }
+}
+
+FileDescriptor* DescriptorPool::NewPlaceholderFile(
+    StringPiece name) const {
+  MutexLockMaybe lock(mutex_);
+  internal::FlatAllocator alloc;
+  alloc.PlanArray<FileDescriptor>(1);
+  alloc.PlanArray<std::string>(1);
+  alloc.FinalizePlanning(tables_);
+
+  return NewPlaceholderFileWithMutexHeld(name, alloc);
+}
+
+FileDescriptor* DescriptorPool::NewPlaceholderFileWithMutexHeld(
+    StringPiece name, internal::FlatAllocator& alloc) const {
+  if (mutex_) {
+    mutex_->AssertHeld();
+  }
+  FileDescriptor* placeholder = alloc.AllocateArray<FileDescriptor>(1);
+  memset(static_cast<void*>(placeholder), 0, sizeof(*placeholder));
+
+  placeholder->name_ = alloc.AllocateStrings(name);
+  placeholder->package_ = &internal::GetEmptyString();
+  placeholder->pool_ = this;
+  placeholder->options_ = &FileOptions::default_instance();
+  placeholder->tables_ = &FileDescriptorTables::GetEmptyInstance();
+  placeholder->source_code_info_ = &SourceCodeInfo::default_instance();
+  placeholder->is_placeholder_ = true;
+  placeholder->syntax_ = FileDescriptor::SYNTAX_UNKNOWN;
+  placeholder->finished_building_ = true;
+  // All other fields are zero or nullptr.
+
+  return placeholder;
+}
+
+bool DescriptorBuilder::AddSymbol(const std::string& full_name,
+                                  const void* parent, const std::string& name,
+                                  const Message& proto, Symbol symbol) {
+  // If the caller passed nullptr for the parent, the symbol is at file scope.
+  // Use its file as the parent instead.
+  if (parent == nullptr) parent = file_;
+
+  if (full_name.find('\0') != std::string::npos) {
+    AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME,
+             "\"" + full_name + "\" contains null character.");
+    return false;
+  }
+  if (tables_->AddSymbol(full_name, symbol)) {
+    if (!file_tables_->AddAliasUnderParent(parent, name, symbol)) {
+      // This is only possible if there was already an error adding something of
+      // the same name.
+      if (!had_errors_) {
+        GOOGLE_LOG(DFATAL) << "\"" << full_name
+                    << "\" not previously defined in "
+                       "symbols_by_name_, but was defined in "
+                       "symbols_by_parent_; this shouldn't be possible.";
+      }
+      return false;
+    }
+    return true;
+  } else {
+    const FileDescriptor* other_file = tables_->FindSymbol(full_name).GetFile();
+    if (other_file == file_) {
+      std::string::size_type dot_pos = full_name.find_last_of('.');
+      if (dot_pos == std::string::npos) {
+        AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME,
+                 "\"" + full_name + "\" is already defined.");
+      } else {
+        AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME,
+                 "\"" + full_name.substr(dot_pos + 1) +
+                     "\" is already defined in \"" +
+                     full_name.substr(0, dot_pos) + "\".");
+      }
+    } else {
+      // Symbol seems to have been defined in a different file.
+      AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME,
+               "\"" + full_name + "\" is already defined in file \"" +
+                   (other_file == nullptr ? "null" : other_file->name()) +
+                   "\".");
+    }
+    return false;
+  }
+}
+
+void DescriptorBuilder::AddPackage(const std::string& name,
+                                   const Message& proto, FileDescriptor* file) {
+  if (name.find('\0') != std::string::npos) {
+    AddError(name, proto, DescriptorPool::ErrorCollector::NAME,
+             "\"" + name + "\" contains null character.");
+    return;
+  }
+
+  Symbol existing_symbol = tables_->FindSymbol(name);
+  // It's OK to redefine a package.
+  if (existing_symbol.IsNull()) {
+    if (&name == &file->package()) {
+      // It is the toplevel package name, so insert the descriptor directly.
+      tables_->AddSymbol(file->package(), Symbol(file));
+    } else {
+      auto* package = tables_->Allocate<Symbol::Subpackage>();
+      // If the name is the package name, then it is already in the arena.
+      // If not, copy it there. It came from the call to AddPackage below.
+      package->name_size = static_cast<int>(name.size());
+      package->file = file;
+      tables_->AddSymbol(name, Symbol(package));
+    }
+    // Also add parent package, if any.
+    std::string::size_type dot_pos = name.find_last_of('.');
+    if (dot_pos == std::string::npos) {
+      // No parents.
+      ValidateSymbolName(name, name, proto);
+    } else {
+      // Has parent.
+      AddPackage(name.substr(0, dot_pos), proto, file);
+      ValidateSymbolName(name.substr(dot_pos + 1), name, proto);
+    }
+  } else if (!existing_symbol.IsPackage()) {
+    // Symbol seems to have been defined in a different file.
+    const FileDescriptor* other_file = existing_symbol.GetFile();
+    AddError(name, proto, DescriptorPool::ErrorCollector::NAME,
+             "\"" + name +
+                 "\" is already defined (as something other than "
+                 "a package) in file \"" +
+                 (other_file == nullptr ? "null" : other_file->name()) + "\".");
+  }
+}
+
+void DescriptorBuilder::ValidateSymbolName(const std::string& name,
+                                           const std::string& full_name,
+                                           const Message& proto) {
+  if (name.empty()) {
+    AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME,
+             "Missing name.");
+  } else {
+    for (char character : name) {
+      // I don't trust isalnum() due to locales.  :(
+      if ((character < 'a' || 'z' < character) &&
+          (character < 'A' || 'Z' < character) &&
+          (character < '0' || '9' < character) && (character != '_')) {
+        AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME,
+                 "\"" + name + "\" is not a valid identifier.");
+        return;
+      }
+    }
+  }
+}
+
+// -------------------------------------------------------------------
+
+// This generic implementation is good for all descriptors except
+// FileDescriptor.
+template <class DescriptorT>
+void DescriptorBuilder::AllocateOptions(
+    const typename DescriptorT::OptionsType& orig_options,
+    DescriptorT* descriptor, int options_field_tag,
+    const std::string& option_name, internal::FlatAllocator& alloc) {
+  std::vector<int> options_path;
+  descriptor->GetLocationPath(&options_path);
+  options_path.push_back(options_field_tag);
+  AllocateOptionsImpl(descriptor->full_name(), descriptor->full_name(),
+                      orig_options, descriptor, options_path, option_name,
+                      alloc);
+}
+
+// We specialize for FileDescriptor.
+void DescriptorBuilder::AllocateOptions(const FileOptions& orig_options,
+                                        FileDescriptor* descriptor,
+                                        internal::FlatAllocator& alloc) {
+  std::vector<int> options_path;
+  options_path.push_back(FileDescriptorProto::kOptionsFieldNumber);
+  // We add the dummy token so that LookupSymbol does the right thing.
+  AllocateOptionsImpl(descriptor->package() + ".dummy", descriptor->name(),
+                      orig_options, descriptor, options_path,
+                      "google.protobuf.FileOptions", alloc);
+}
+
+template <class DescriptorT>
+void DescriptorBuilder::AllocateOptionsImpl(
+    const std::string& name_scope, const std::string& element_name,
+    const typename DescriptorT::OptionsType& orig_options,
+    DescriptorT* descriptor, const std::vector<int>& options_path,
+    const std::string& option_name, internal::FlatAllocator& alloc) {
+  auto* options = alloc.AllocateArray<typename DescriptorT::OptionsType>(1);
+
+  if (!orig_options.IsInitialized()) {
+    AddError(name_scope + "." + element_name, orig_options,
+             DescriptorPool::ErrorCollector::OPTION_NAME,
+             "Uninterpreted option is missing name or value.");
+    return;
+  }
+
+  // Avoid using MergeFrom()/CopyFrom() in this class to make it -fno-rtti
+  // friendly. Without RTTI, MergeFrom() and CopyFrom() will fallback to the
+  // reflection based method, which requires the Descriptor. However, we are in
+  // the middle of building the descriptors, thus the deadlock.
+  options->ParseFromString(orig_options.SerializeAsString());
+  descriptor->options_ = options;
+
+  // Don't add to options_to_interpret_ unless there were uninterpreted
+  // options.  This not only avoids unnecessary work, but prevents a
+  // bootstrapping problem when building descriptors for descriptor.proto.
+  // descriptor.proto does not contain any uninterpreted options, but
+  // attempting to interpret options anyway will cause
+  // OptionsType::GetDescriptor() to be called which may then deadlock since
+  // we're still trying to build it.
+  if (options->uninterpreted_option_size() > 0) {
+    options_to_interpret_.push_back(OptionsToInterpret(
+        name_scope, element_name, options_path, &orig_options, options));
+  }
+
+  // If the custom option is in unknown fields, no need to interpret it.
+  // Remove the dependency file from unused_dependency.
+  const UnknownFieldSet& unknown_fields = orig_options.unknown_fields();
+  if (!unknown_fields.empty()) {
+    // Can not use options->GetDescriptor() which may case deadlock.
+    Symbol msg_symbol = tables_->FindSymbol(option_name);
+    if (msg_symbol.type() == Symbol::MESSAGE) {
+      for (int i = 0; i < unknown_fields.field_count(); ++i) {
+        assert_mutex_held(pool_);
+        const FieldDescriptor* field =
+            pool_->InternalFindExtensionByNumberNoLock(
+                msg_symbol.descriptor(), unknown_fields.field(i).number());
+        if (field) {
+          unused_dependency_.erase(field->file());
+        }
+      }
+    }
+  }
+}
+
+// A common pattern:  We want to convert a repeated field in the descriptor
+// to an array of values, calling some method to build each value.
+#define BUILD_ARRAY(INPUT, OUTPUT, NAME, METHOD, PARENT)               \
+  OUTPUT->NAME##_count_ = INPUT.NAME##_size();                         \
+  OUTPUT->NAME##s_ = alloc.AllocateArray<                              \
+      typename std::remove_pointer<decltype(OUTPUT->NAME##s_)>::type>( \
+      INPUT.NAME##_size());                                            \
+  for (int i = 0; i < INPUT.NAME##_size(); i++) {                      \
+    METHOD(INPUT.NAME(i), PARENT, OUTPUT->NAME##s_ + i, alloc);        \
+  }
+
+void DescriptorBuilder::AddRecursiveImportError(
+    const FileDescriptorProto& proto, int from_here) {
+  std::string error_message("File recursively imports itself: ");
+  for (size_t i = from_here; i < tables_->pending_files_.size(); i++) {
+    error_message.append(tables_->pending_files_[i]);
+    error_message.append(" -> ");
+  }
+  error_message.append(proto.name());
+
+  if (static_cast<size_t>(from_here) < tables_->pending_files_.size() - 1) {
+    AddError(tables_->pending_files_[from_here + 1], proto,
+             DescriptorPool::ErrorCollector::IMPORT, error_message);
+  } else {
+    AddError(proto.name(), proto, DescriptorPool::ErrorCollector::IMPORT,
+             error_message);
+  }
+}
+
+void DescriptorBuilder::AddTwiceListedError(const FileDescriptorProto& proto,
+                                            int index) {
+  AddError(proto.dependency(index), proto,
+           DescriptorPool::ErrorCollector::IMPORT,
+           "Import \"" + proto.dependency(index) + "\" was listed twice.");
+}
+
+void DescriptorBuilder::AddImportError(const FileDescriptorProto& proto,
+                                       int index) {
+  std::string message;
+  if (pool_->fallback_database_ == nullptr) {
+    message = "Import \"" + proto.dependency(index) + "\" has not been loaded.";
+  } else {
+    message = "Import \"" + proto.dependency(index) +
+              "\" was not found or had errors.";
+  }
+  AddError(proto.dependency(index), proto,
+           DescriptorPool::ErrorCollector::IMPORT, message);
+}
+
+static bool ExistingFileMatchesProto(const FileDescriptor* existing_file,
+                                     const FileDescriptorProto& proto) {
+  FileDescriptorProto existing_proto;
+  existing_file->CopyTo(&existing_proto);
+  // TODO(liujisi): Remove it when CopyTo supports copying syntax params when
+  // syntax="proto2".
+  if (existing_file->syntax() == FileDescriptor::SYNTAX_PROTO2 &&
+      proto.has_syntax()) {
+    existing_proto.set_syntax(
+        existing_file->SyntaxName(existing_file->syntax()));
+  }
+
+  return existing_proto.SerializeAsString() == proto.SerializeAsString();
+}
+
+// These PlanAllocationSize functions will gather into the FlatAllocator all the
+// necessary memory allocations that BuildXXX functions below will do on the
+// Tables object.
+// They *must* be kept in sync. If we miss some PlanArray call we won't have
+// enough memory and will GOOGLE_CHECK-fail.
+static void PlanAllocationSize(
+    const RepeatedPtrField<EnumValueDescriptorProto>& values,
+    internal::FlatAllocator& alloc) {
+  alloc.PlanArray<EnumValueDescriptor>(values.size());
+  alloc.PlanArray<std::string>(2 * values.size());  // name + full_name
+  for (const auto& v : values) {
+    if (v.has_options()) alloc.PlanArray<EnumValueOptions>(1);
+  }
+}
+
+static void PlanAllocationSize(
+    const RepeatedPtrField<EnumDescriptorProto>& enums,
+    internal::FlatAllocator& alloc) {
+  alloc.PlanArray<EnumDescriptor>(enums.size());
+  alloc.PlanArray<std::string>(2 * enums.size());  // name + full_name
+  for (const auto& e : enums) {
+    if (e.has_options()) alloc.PlanArray<EnumOptions>(1);
+    PlanAllocationSize(e.value(), alloc);
+    alloc.PlanArray<EnumDescriptor::ReservedRange>(e.reserved_range_size());
+    alloc.PlanArray<const std::string*>(e.reserved_name_size());
+    alloc.PlanArray<std::string>(e.reserved_name_size());
+  }
+}
+
+static void PlanAllocationSize(
+    const RepeatedPtrField<OneofDescriptorProto>& oneofs,
+    internal::FlatAllocator& alloc) {
+  alloc.PlanArray<OneofDescriptor>(oneofs.size());
+  alloc.PlanArray<std::string>(2 * oneofs.size());  // name + full_name
+  for (const auto& oneof : oneofs) {
+    if (oneof.has_options()) alloc.PlanArray<OneofOptions>(1);
+  }
+}
+
+static void PlanAllocationSize(
+    const RepeatedPtrField<FieldDescriptorProto>& fields,
+    internal::FlatAllocator& alloc) {
+  alloc.PlanArray<FieldDescriptor>(fields.size());
+  for (const auto& field : fields) {
+    if (field.has_options()) alloc.PlanArray<FieldOptions>(1);
+    alloc.PlanFieldNames(field.name(),
+                         field.has_json_name() ? &field.json_name() : nullptr);
+    if (field.has_default_value() && field.has_type() &&
+        (field.type() == FieldDescriptorProto::TYPE_STRING ||
+         field.type() == FieldDescriptorProto::TYPE_BYTES)) {
+      // For the default string value.
+      alloc.PlanArray<std::string>(1);
+    }
+  }
+}
+
+static void PlanAllocationSize(
+    const RepeatedPtrField<DescriptorProto::ExtensionRange>& ranges,
+    internal::FlatAllocator& alloc) {
+  alloc.PlanArray<Descriptor::ExtensionRange>(ranges.size());
+  for (const auto& r : ranges) {
+    if (r.has_options()) alloc.PlanArray<ExtensionRangeOptions>(1);
+  }
+}
+
+static void PlanAllocationSize(
+    const RepeatedPtrField<DescriptorProto>& messages,
+    internal::FlatAllocator& alloc) {
+  alloc.PlanArray<Descriptor>(messages.size());
+  alloc.PlanArray<std::string>(2 * messages.size());  // name + full_name
+
+  for (const auto& message : messages) {
+    if (message.has_options()) alloc.PlanArray<MessageOptions>(1);
+    PlanAllocationSize(message.nested_type(), alloc);
+    PlanAllocationSize(message.field(), alloc);
+    PlanAllocationSize(message.extension(), alloc);
+    PlanAllocationSize(message.extension_range(), alloc);
+    alloc.PlanArray<Descriptor::ReservedRange>(message.reserved_range_size());
+    alloc.PlanArray<const std::string*>(message.reserved_name_size());
+    alloc.PlanArray<std::string>(message.reserved_name_size());
+    PlanAllocationSize(message.enum_type(), alloc);
+    PlanAllocationSize(message.oneof_decl(), alloc);
+  }
+}
+
+static void PlanAllocationSize(
+    const RepeatedPtrField<MethodDescriptorProto>& methods,
+    internal::FlatAllocator& alloc) {
+  alloc.PlanArray<MethodDescriptor>(methods.size());
+  alloc.PlanArray<std::string>(2 * methods.size());  // name + full_name
+  for (const auto& m : methods) {
+    if (m.has_options()) alloc.PlanArray<MethodOptions>(1);
+  }
+}
+
+static void PlanAllocationSize(
+    const RepeatedPtrField<ServiceDescriptorProto>& services,
+    internal::FlatAllocator& alloc) {
+  alloc.PlanArray<ServiceDescriptor>(services.size());
+  alloc.PlanArray<std::string>(2 * services.size());  // name + full_name
+  for (const auto& service : services) {
+    if (service.has_options()) alloc.PlanArray<ServiceOptions>(1);
+    PlanAllocationSize(service.method(), alloc);
+  }
+}
+
+static void PlanAllocationSize(const FileDescriptorProto& proto,
+                               internal::FlatAllocator& alloc) {
+  alloc.PlanArray<FileDescriptor>(1);
+  alloc.PlanArray<FileDescriptorTables>(1);
+  alloc.PlanArray<std::string>(2);  // name + package
+  if (proto.has_options()) alloc.PlanArray<FileOptions>(1);
+  if (proto.has_source_code_info()) alloc.PlanArray<SourceCodeInfo>(1);
+
+  PlanAllocationSize(proto.service(), alloc);
+  PlanAllocationSize(proto.message_type(), alloc);
+  PlanAllocationSize(proto.enum_type(), alloc);
+  PlanAllocationSize(proto.extension(), alloc);
+
+  alloc.PlanArray<int>(proto.weak_dependency_size());
+  alloc.PlanArray<int>(proto.public_dependency_size());
+  alloc.PlanArray<const FileDescriptor*>(proto.dependency_size());
+}
+
+const FileDescriptor* DescriptorBuilder::BuildFile(
+    const FileDescriptorProto& proto) {
+  filename_ = proto.name();
+
+  // Check if the file already exists and is identical to the one being built.
+  // Note:  This only works if the input is canonical -- that is, it
+  //   fully-qualifies all type names, has no UninterpretedOptions, etc.
+  //   This is fine, because this idempotency "feature" really only exists to
+  //   accommodate one hack in the proto1->proto2 migration layer.
+  const FileDescriptor* existing_file = tables_->FindFile(filename_);
+  if (existing_file != nullptr) {
+    // File already in pool.  Compare the existing one to the input.
+    if (ExistingFileMatchesProto(existing_file, proto)) {
+      // They're identical.  Return the existing descriptor.
+      return existing_file;
+    }
+
+    // Not a match.  The error will be detected and handled later.
+  }
+
+  // Check to see if this file is already on the pending files list.
+  // TODO(kenton):  Allow recursive imports?  It may not work with some
+  //   (most?) programming languages.  E.g., in C++, a forward declaration
+  //   of a type is not sufficient to allow it to be used even in a
+  //   generated header file due to inlining.  This could perhaps be
+  //   worked around using tricks involving inserting #include statements
+  //   mid-file, but that's pretty ugly, and I'm pretty sure there are
+  //   some languages out there that do not allow recursive dependencies
+  //   at all.
+  for (size_t i = 0; i < tables_->pending_files_.size(); i++) {
+    if (tables_->pending_files_[i] == proto.name()) {
+      AddRecursiveImportError(proto, i);
+      return nullptr;
+    }
+  }
+
+  static const int kMaximumPackageLength = 511;
+  if (proto.package().size() > kMaximumPackageLength) {
+    AddError(proto.package(), proto, DescriptorPool::ErrorCollector::NAME,
+             "Package name is too long");
+    return nullptr;
+  }
+
+  // If we have a fallback_database_, and we aren't doing lazy import building,
+  // attempt to load all dependencies now, before checkpointing tables_.  This
+  // avoids confusion with recursive checkpoints.
+  if (!pool_->lazily_build_dependencies_) {
+    if (pool_->fallback_database_ != nullptr) {
+      tables_->pending_files_.push_back(proto.name());
+      for (int i = 0; i < proto.dependency_size(); i++) {
+        if (tables_->FindFile(proto.dependency(i)) == nullptr &&
+            (pool_->underlay_ == nullptr ||
+             pool_->underlay_->FindFileByName(proto.dependency(i)) ==
+                 nullptr)) {
+          // We don't care what this returns since we'll find out below anyway.
+          pool_->TryFindFileInFallbackDatabase(proto.dependency(i));
+        }
+      }
+      tables_->pending_files_.pop_back();
+    }
+  }
+
+  // Checkpoint the tables so that we can roll back if something goes wrong.
+  tables_->AddCheckpoint();
+
+  internal::FlatAllocator alloc;
+  PlanAllocationSize(proto, alloc);
+  alloc.FinalizePlanning(tables_);
+  FileDescriptor* result = BuildFileImpl(proto, alloc);
+
+  file_tables_->FinalizeTables();
+  if (result) {
+    tables_->ClearLastCheckpoint();
+    result->finished_building_ = true;
+    alloc.ExpectConsumed();
+  } else {
+    tables_->RollbackToLastCheckpoint();
+  }
+
+  return result;
+}
+
+FileDescriptor* DescriptorBuilder::BuildFileImpl(
+    const FileDescriptorProto& proto, internal::FlatAllocator& alloc) {
+  FileDescriptor* result = alloc.AllocateArray<FileDescriptor>(1);
+  file_ = result;
+
+  result->is_placeholder_ = false;
+  result->finished_building_ = false;
+  SourceCodeInfo* info = nullptr;
+  if (proto.has_source_code_info()) {
+    info = alloc.AllocateArray<SourceCodeInfo>(1);
+    info->CopyFrom(proto.source_code_info());
+    result->source_code_info_ = info;
+  } else {
+    result->source_code_info_ = &SourceCodeInfo::default_instance();
+  }
+
+  file_tables_ = alloc.AllocateArray<FileDescriptorTables>(1);
+  file_->tables_ = file_tables_;
+
+  if (!proto.has_name()) {
+    AddError("", proto, DescriptorPool::ErrorCollector::OTHER,
+             "Missing field: FileDescriptorProto.name.");
+  }
+
+  // TODO(liujisi): Report error when the syntax is empty after all the protos
+  // have added the syntax statement.
+  if (proto.syntax().empty() || proto.syntax() == "proto2") {
+    file_->syntax_ = FileDescriptor::SYNTAX_PROTO2;
+  } else if (proto.syntax() == "proto3") {
+    file_->syntax_ = FileDescriptor::SYNTAX_PROTO3;
+  } else {
+    file_->syntax_ = FileDescriptor::SYNTAX_UNKNOWN;
+    AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER,
+             "Unrecognized syntax: " + proto.syntax());
+  }
+
+  result->name_ = alloc.AllocateStrings(proto.name());
+  if (proto.has_package()) {
+    result->package_ = alloc.AllocateStrings(proto.package());
+  } else {
+    // We cannot rely on proto.package() returning a valid string if
+    // proto.has_package() is false, because we might be running at static
+    // initialization time, in which case default values have not yet been
+    // initialized.
+    result->package_ = alloc.AllocateStrings("");
+  }
+  result->pool_ = pool_;
+
+  if (result->name().find('\0') != std::string::npos) {
+    AddError(result->name(), proto, DescriptorPool::ErrorCollector::NAME,
+             "\"" + result->name() + "\" contains null character.");
+    return nullptr;
+  }
+
+  // Add to tables.
+  if (!tables_->AddFile(result)) {
+    AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER,
+             "A file with this name is already in the pool.");
+    // Bail out early so that if this is actually the exact same file, we
+    // don't end up reporting that every single symbol is already defined.
+    return nullptr;
+  }
+  if (!result->package().empty()) {
+    if (std::count(result->package().begin(), result->package().end(), '.') >
+        kPackageLimit) {
+      AddError(result->package(), proto, DescriptorPool::ErrorCollector::NAME,
+               "Exceeds Maximum Package Depth");
+      return nullptr;
+    }
+    AddPackage(result->package(), proto, result);
+  }
+
+  // Make sure all dependencies are loaded.
+  std::set<std::string> seen_dependencies;
+  result->dependency_count_ = proto.dependency_size();
+  result->dependencies_ =
+      alloc.AllocateArray<const FileDescriptor*>(proto.dependency_size());
+  result->dependencies_once_ = nullptr;
+  unused_dependency_.clear();
+  std::set<int> weak_deps;
+  for (int i = 0; i < proto.weak_dependency_size(); ++i) {
+    weak_deps.insert(proto.weak_dependency(i));
+  }
+
+  bool need_lazy_deps = false;
+  for (int i = 0; i < proto.dependency_size(); i++) {
+    if (!seen_dependencies.insert(proto.dependency(i)).second) {
+      AddTwiceListedError(proto, i);
+    }
+
+    const FileDescriptor* dependency = tables_->FindFile(proto.dependency(i));
+    if (dependency == nullptr && pool_->underlay_ != nullptr) {
+      dependency = pool_->underlay_->FindFileByName(proto.dependency(i));
+    }
+
+    if (dependency == result) {
+      // Recursive import.  dependency/result is not fully initialized, and it's
+      // dangerous to try to do anything with it.  The recursive import error
+      // will be detected and reported in DescriptorBuilder::BuildFile().
+      return nullptr;
+    }
+
+    if (dependency == nullptr) {
+      if (!pool_->lazily_build_dependencies_) {
+        if (pool_->allow_unknown_ ||
+            (!pool_->enforce_weak_ && weak_deps.find(i) != weak_deps.end())) {
+          internal::FlatAllocator lazy_dep_alloc;
+          lazy_dep_alloc.PlanArray<FileDescriptor>(1);
+          lazy_dep_alloc.PlanArray<std::string>(1);
+          lazy_dep_alloc.FinalizePlanning(tables_);
+          dependency = pool_->NewPlaceholderFileWithMutexHeld(
+              proto.dependency(i), lazy_dep_alloc);
+        } else {
+          AddImportError(proto, i);
+        }
+      }
+    } else {
+      // Add to unused_dependency_ to track unused imported files.
+      // Note: do not track unused imported files for public import.
+      if (pool_->enforce_dependencies_ &&
+          (pool_->unused_import_track_files_.find(proto.name()) !=
+           pool_->unused_import_track_files_.end()) &&
+          (dependency->public_dependency_count() == 0)) {
+        unused_dependency_.insert(dependency);
+      }
+    }
+
+    result->dependencies_[i] = dependency;
+    if (pool_->lazily_build_dependencies_ && !dependency) {
+      need_lazy_deps = true;
+    }
+  }
+  if (need_lazy_deps) {
+    int total_char_size = 0;
+    for (int i = 0; i < proto.dependency_size(); i++) {
+      if (result->dependencies_[i] == nullptr) {
+        total_char_size += static_cast<int>(proto.dependency(i).size());
+      }
+      ++total_char_size;  // For NUL char
+    }
+
+    void* data = tables_->AllocateBytes(
+        static_cast<int>(sizeof(internal::once_flag) + total_char_size));
+    result->dependencies_once_ = ::new (data) internal::once_flag{};
+    char* name_data = reinterpret_cast<char*>(result->dependencies_once_ + 1);
+
+    for (int i = 0; i < proto.dependency_size(); i++) {
+      if (result->dependencies_[i] == nullptr) {
+        memcpy(name_data, proto.dependency(i).c_str(),
+               proto.dependency(i).size());
+        name_data += proto.dependency(i).size();
+      }
+      *name_data++ = '\0';
+    }
+  }
+
+  // Check public dependencies.
+  int public_dependency_count = 0;
+  result->public_dependencies_ =
+      alloc.AllocateArray<int>(proto.public_dependency_size());
+  for (int i = 0; i < proto.public_dependency_size(); i++) {
+    // Only put valid public dependency indexes.
+    int index = proto.public_dependency(i);
+    if (index >= 0 && index < proto.dependency_size()) {
+      result->public_dependencies_[public_dependency_count++] = index;
+      // Do not track unused imported files for public import.
+      // Calling dependency(i) builds that file when doing lazy imports,
+      // need to avoid doing this. Unused dependency detection isn't done
+      // when building lazily, anyways.
+      if (!pool_->lazily_build_dependencies_) {
+        unused_dependency_.erase(result->dependency(index));
+      }
+    } else {
+      AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER,
+               "Invalid public dependency index.");
+    }
+  }
+  result->public_dependency_count_ = public_dependency_count;
+
+  // Build dependency set
+  dependencies_.clear();
+  // We don't/can't do proper dependency error checking when
+  // lazily_build_dependencies_, and calling dependency(i) will force
+  // a dependency to be built, which we don't want.
+  if (!pool_->lazily_build_dependencies_) {
+    for (int i = 0; i < result->dependency_count(); i++) {
+      RecordPublicDependencies(result->dependency(i));
+    }
+  }
+
+  // Check weak dependencies.
+  int weak_dependency_count = 0;
+  result->weak_dependencies_ =
+      alloc.AllocateArray<int>(proto.weak_dependency_size());
+  for (int i = 0; i < proto.weak_dependency_size(); i++) {
+    int index = proto.weak_dependency(i);
+    if (index >= 0 && index < proto.dependency_size()) {
+      result->weak_dependencies_[weak_dependency_count++] = index;
+    } else {
+      AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER,
+               "Invalid weak dependency index.");
+    }
+  }
+  result->weak_dependency_count_ = weak_dependency_count;
+
+  // Convert children.
+  BUILD_ARRAY(proto, result, message_type, BuildMessage, nullptr);
+  BUILD_ARRAY(proto, result, enum_type, BuildEnum, nullptr);
+  BUILD_ARRAY(proto, result, service, BuildService, nullptr);
+  BUILD_ARRAY(proto, result, extension, BuildExtension, nullptr);
+
+  // Copy options.
+  result->options_ = nullptr;  // Set to default_instance later if necessary.
+  if (proto.has_options()) {
+    AllocateOptions(proto.options(), result, alloc);
+  }
+
+  // Note that the following steps must occur in exactly the specified order.
+
+  // Cross-link.
+  CrossLinkFile(result, proto);
+
+  if (!message_hints_.empty()) {
+    SuggestFieldNumbers(result, proto);
+  }
+
+  // Interpret any remaining uninterpreted options gathered into
+  // options_to_interpret_ during descriptor building.  Cross-linking has made
+  // extension options known, so all interpretations should now succeed.
+  if (!had_errors_) {
+    OptionInterpreter option_interpreter(this);
+    for (std::vector<OptionsToInterpret>::iterator iter =
+             options_to_interpret_.begin();
+         iter != options_to_interpret_.end(); ++iter) {
+      option_interpreter.InterpretOptions(&(*iter));
+    }
+    options_to_interpret_.clear();
+    if (info != nullptr) {
+      option_interpreter.UpdateSourceCodeInfo(info);
+    }
+  }
+
+  // Validate options. See comments at InternalSetLazilyBuildDependencies about
+  // error checking and lazy import building.
+  if (!had_errors_ && !pool_->lazily_build_dependencies_) {
+    ValidateFileOptions(result, proto);
+  }
+
+  // Additional naming conflict check for map entry types. Only need to check
+  // this if there are already errors.
+  if (had_errors_) {
+    for (int i = 0; i < proto.message_type_size(); ++i) {
+      DetectMapConflicts(result->message_type(i), proto.message_type(i));
+    }
+  }
+
+
+  // Again, see comments at InternalSetLazilyBuildDependencies about error
+  // checking. Also, don't log unused dependencies if there were previous
+  // errors, since the results might be inaccurate.
+  if (!had_errors_ && !unused_dependency_.empty() &&
+      !pool_->lazily_build_dependencies_) {
+    LogUnusedDependency(proto, result);
+  }
+
+  if (had_errors_) {
+    return nullptr;
+  } else {
+    return result;
+  }
+}
+
+
+const std::string* DescriptorBuilder::AllocateNameStrings(
+    const std::string& scope, const std::string& proto_name,
+    internal::FlatAllocator& alloc) {
+  if (scope.empty()) {
+    return alloc.AllocateStrings(proto_name, proto_name);
+  } else {
+    return alloc.AllocateStrings(proto_name,
+                                 StrCat(scope, ".", proto_name));
+  }
+}
+
+namespace {
+
+// Helper for BuildMessage below.
+struct IncrementWhenDestroyed {
+  ~IncrementWhenDestroyed() { ++to_increment; }
+  int& to_increment;
+};
+
+}  // namespace
+
+void DescriptorBuilder::BuildMessage(const DescriptorProto& proto,
+                                     const Descriptor* parent,
+                                     Descriptor* result,
+                                     internal::FlatAllocator& alloc) {
+  const std::string& scope =
+      (parent == nullptr) ? file_->package() : parent->full_name();
+  result->all_names_ = AllocateNameStrings(scope, proto.name(), alloc);
+  ValidateSymbolName(proto.name(), result->full_name(), proto);
+
+  result->file_ = file_;
+  result->containing_type_ = parent;
+  result->is_placeholder_ = false;
+  result->is_unqualified_placeholder_ = false;
+  result->well_known_type_ = Descriptor::WELLKNOWNTYPE_UNSPECIFIED;
+  result->options_ = nullptr;  // Set to default_instance later if necessary.
+
+  auto it = pool_->tables_->well_known_types_.find(result->full_name());
+  if (it != pool_->tables_->well_known_types_.end()) {
+    result->well_known_type_ = it->second;
+  }
+
+  // Calculate the continuous sequence of fields.
+  // These can be fast-path'd during lookup and don't need to be added to the
+  // tables.
+  // We use uint16_t to save space for sequential_field_limit_, so stop before
+  // overflowing it. Worst case, we are not taking full advantage on huge
+  // messages, but it is unlikely.
+  result->sequential_field_limit_ = 0;
+  for (int i = 0; i < std::numeric_limits<uint16_t>::max() &&
+                  i < proto.field_size() && proto.field(i).number() == i + 1;
+       ++i) {
+    result->sequential_field_limit_ = i + 1;
+  }
+
+  // Build oneofs first so that fields and extension ranges can refer to them.
+  BUILD_ARRAY(proto, result, oneof_decl, BuildOneof, result);
+  BUILD_ARRAY(proto, result, field, BuildField, result);
+  BUILD_ARRAY(proto, result, enum_type, BuildEnum, result);
+  BUILD_ARRAY(proto, result, extension_range, BuildExtensionRange, result);
+  BUILD_ARRAY(proto, result, extension, BuildExtension, result);
+  BUILD_ARRAY(proto, result, reserved_range, BuildReservedRange, result);
+
+  // Before building submessages, check recursion limit.
+  --recursion_depth_;
+  IncrementWhenDestroyed revert{recursion_depth_};
+  if (recursion_depth_ <= 0) {
+    AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::OTHER,
+             "Reached maximum recursion limit for nested messages.");
+    result->nested_types_ = nullptr;
+    result->nested_type_count_ = 0;
+    return;
+  }
+  BUILD_ARRAY(proto, result, nested_type, BuildMessage, result);
+
+  // Copy reserved names.
+  int reserved_name_count = proto.reserved_name_size();
+  result->reserved_name_count_ = reserved_name_count;
+  result->reserved_names_ =
+      alloc.AllocateArray<const std::string*>(reserved_name_count);
+  for (int i = 0; i < reserved_name_count; ++i) {
+    result->reserved_names_[i] =
+        alloc.AllocateStrings(proto.reserved_name(i));
+  }
+
+  // Copy options.
+  if (proto.has_options()) {
+    AllocateOptions(proto.options(), result,
+                    DescriptorProto::kOptionsFieldNumber,
+                    "google.protobuf.MessageOptions", alloc);
+  }
+
+  AddSymbol(result->full_name(), parent, result->name(), proto, Symbol(result));
+
+  for (int i = 0; i < proto.reserved_range_size(); i++) {
+    const DescriptorProto_ReservedRange& range1 = proto.reserved_range(i);
+    for (int j = i + 1; j < proto.reserved_range_size(); j++) {
+      const DescriptorProto_ReservedRange& range2 = proto.reserved_range(j);
+      if (range1.end() > range2.start() && range2.end() > range1.start()) {
+        AddError(result->full_name(), proto.reserved_range(i),
+                 DescriptorPool::ErrorCollector::NUMBER,
+                 strings::Substitute("Reserved range $0 to $1 overlaps with "
+                                  "already-defined range $2 to $3.",
+                                  range2.start(), range2.end() - 1,
+                                  range1.start(), range1.end() - 1));
+      }
+    }
+  }
+
+  HASH_SET<std::string> reserved_name_set;
+  for (int i = 0; i < proto.reserved_name_size(); i++) {
+    const std::string& name = proto.reserved_name(i);
+    if (reserved_name_set.find(name) == reserved_name_set.end()) {
+      reserved_name_set.insert(name);
+    } else {
+      AddError(name, proto, DescriptorPool::ErrorCollector::NAME,
+               strings::Substitute("Field name \"$0\" is reserved multiple times.",
+                                name));
+    }
+  }
+
+
+  for (int i = 0; i < result->field_count(); i++) {
+    const FieldDescriptor* field = result->field(i);
+    for (int j = 0; j < result->extension_range_count(); j++) {
+      const Descriptor::ExtensionRange* range = result->extension_range(j);
+      if (range->start <= field->number() && field->number() < range->end) {
+        message_hints_[result].RequestHintOnFieldNumbers(
+            proto.extension_range(j), DescriptorPool::ErrorCollector::NUMBER);
+        AddError(
+            field->full_name(), proto.extension_range(j),
+            DescriptorPool::ErrorCollector::NUMBER,
+            strings::Substitute(
+                "Extension range $0 to $1 includes field \"$2\" ($3).",
+                range->start, range->end - 1, field->name(), field->number()));
+      }
+    }
+    for (int j = 0; j < result->reserved_range_count(); j++) {
+      const Descriptor::ReservedRange* range = result->reserved_range(j);
+      if (range->start <= field->number() && field->number() < range->end) {
+        message_hints_[result].RequestHintOnFieldNumbers(
+            proto.reserved_range(j), DescriptorPool::ErrorCollector::NUMBER);
+        AddError(field->full_name(), proto.reserved_range(j),
+                 DescriptorPool::ErrorCollector::NUMBER,
+                 strings::Substitute("Field \"$0\" uses reserved number $1.",
+                                  field->name(), field->number()));
+      }
+    }
+    if (reserved_name_set.find(field->name()) != reserved_name_set.end()) {
+      AddError(
+          field->full_name(), proto.field(i),
+          DescriptorPool::ErrorCollector::NAME,
+          strings::Substitute("Field name \"$0\" is reserved.", field->name()));
+    }
+
+  }
+
+  // Check that extension ranges don't overlap and don't include
+  // reserved field numbers or names.
+  for (int i = 0; i < result->extension_range_count(); i++) {
+    const Descriptor::ExtensionRange* range1 = result->extension_range(i);
+    for (int j = 0; j < result->reserved_range_count(); j++) {
+      const Descriptor::ReservedRange* range2 = result->reserved_range(j);
+      if (range1->end > range2->start && range2->end > range1->start) {
+        AddError(result->full_name(), proto.extension_range(i),
+                 DescriptorPool::ErrorCollector::NUMBER,
+                 strings::Substitute("Extension range $0 to $1 overlaps with "
+                                  "reserved range $2 to $3.",
+                                  range1->start, range1->end - 1, range2->start,
+                                  range2->end - 1));
+      }
+    }
+    for (int j = i + 1; j < result->extension_range_count(); j++) {
+      const Descriptor::ExtensionRange* range2 = result->extension_range(j);
+      if (range1->end > range2->start && range2->end > range1->start) {
+        AddError(result->full_name(), proto.extension_range(i),
+                 DescriptorPool::ErrorCollector::NUMBER,
+                 strings::Substitute("Extension range $0 to $1 overlaps with "
+                                  "already-defined range $2 to $3.",
+                                  range2->start, range2->end - 1, range1->start,
+                                  range1->end - 1));
+      }
+    }
+  }
+}
+
+void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto,
+                                              Descriptor* parent,
+                                              FieldDescriptor* result,
+                                              bool is_extension,
+                                              internal::FlatAllocator& alloc) {
+  const std::string& scope =
+      (parent == nullptr) ? file_->package() : parent->full_name();
+
+  // We allocate all names in a single array, and dedup them.
+  // We remember the indices for the potentially deduped values.
+  auto all_names = alloc.AllocateFieldNames(
+      proto.name(), scope,
+      proto.has_json_name() ? &proto.json_name() : nullptr);
+  result->all_names_ = all_names.array;
+  result->lowercase_name_index_ = all_names.lowercase_index;
+  result->camelcase_name_index_ = all_names.camelcase_index;
+  result->json_name_index_ = all_names.json_index;
+
+  ValidateSymbolName(proto.name(), result->full_name(), proto);
+
+  result->file_ = file_;
+  result->number_ = proto.number();
+  result->is_extension_ = is_extension;
+  result->is_oneof_ = false;
+  result->proto3_optional_ = proto.proto3_optional();
+
+  if (proto.proto3_optional() &&
+      file_->syntax() != FileDescriptor::SYNTAX_PROTO3) {
+    AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+             "The [proto3_optional=true] option may only be set on proto3"
+             "fields, not " +
+                 result->full_name());
+  }
+
+  result->has_json_name_ = proto.has_json_name();
+
+  // Some compilers do not allow static_cast directly between two enum types,
+  // so we must cast to int first.
+  result->type_ = static_cast<FieldDescriptor::Type>(
+      implicit_cast<int>(proto.type()));
+  result->label_ = static_cast<FieldDescriptor::Label>(
+      implicit_cast<int>(proto.label()));
+
+  if (result->label_ == FieldDescriptor::LABEL_REQUIRED) {
+    // An extension cannot have a required field (b/13365836).
+    if (result->is_extension_) {
+      AddError(result->full_name(), proto,
+               // Error location `TYPE`: we would really like to indicate
+               // `LABEL`, but the `ErrorLocation` enum has no entry for this,
+               // and we don't necessarily know about all implementations of the
+               // `ErrorCollector` interface to extend them to handle the new
+               // error location type properly.
+               DescriptorPool::ErrorCollector::TYPE,
+               "The extension " + result->full_name() + " cannot be required.");
+    }
+  }
+
+  // Some of these may be filled in when cross-linking.
+  result->containing_type_ = nullptr;
+  result->type_once_ = nullptr;
+  result->default_value_enum_ = nullptr;
+
+  result->has_default_value_ = proto.has_default_value();
+  if (proto.has_default_value() && result->is_repeated()) {
+    AddError(result->full_name(), proto,
+             DescriptorPool::ErrorCollector::DEFAULT_VALUE,
+             "Repeated fields can't have default values.");
+  }
+
+  if (proto.has_type()) {
+    if (proto.has_default_value()) {
+      char* end_pos = nullptr;
+      switch (result->cpp_type()) {
+        case FieldDescriptor::CPPTYPE_INT32:
+          result->default_value_int32_t_ =
+              strtol(proto.default_value().c_str(), &end_pos, 0);
+          break;
+        case FieldDescriptor::CPPTYPE_INT64:
+          result->default_value_int64_t_ =
+              strto64(proto.default_value().c_str(), &end_pos, 0);
+          break;
+        case FieldDescriptor::CPPTYPE_UINT32:
+          result->default_value_uint32_t_ =
+              strtoul(proto.default_value().c_str(), &end_pos, 0);
+          break;
+        case FieldDescriptor::CPPTYPE_UINT64:
+          result->default_value_uint64_t_ =
+              strtou64(proto.default_value().c_str(), &end_pos, 0);
+          break;
+        case FieldDescriptor::CPPTYPE_FLOAT:
+          if (proto.default_value() == "inf") {
+            result->default_value_float_ =
+                std::numeric_limits<float>::infinity();
+          } else if (proto.default_value() == "-inf") {
+            result->default_value_float_ =
+                -std::numeric_limits<float>::infinity();
+          } else if (proto.default_value() == "nan") {
+            result->default_value_float_ =
+                std::numeric_limits<float>::quiet_NaN();
+          } else {
+            result->default_value_float_ = io::SafeDoubleToFloat(
+                io::NoLocaleStrtod(proto.default_value().c_str(), &end_pos));
+          }
+          break;
+        case FieldDescriptor::CPPTYPE_DOUBLE:
+          if (proto.default_value() == "inf") {
+            result->default_value_double_ =
+                std::numeric_limits<double>::infinity();
+          } else if (proto.default_value() == "-inf") {
+            result->default_value_double_ =
+                -std::numeric_limits<double>::infinity();
+          } else if (proto.default_value() == "nan") {
+            result->default_value_double_ =
+                std::numeric_limits<double>::quiet_NaN();
+          } else {
+            result->default_value_double_ =
+                io::NoLocaleStrtod(proto.default_value().c_str(), &end_pos);
+          }
+          break;
+        case FieldDescriptor::CPPTYPE_BOOL:
+          if (proto.default_value() == "true") {
+            result->default_value_bool_ = true;
+          } else if (proto.default_value() == "false") {
+            result->default_value_bool_ = false;
+          } else {
+            AddError(result->full_name(), proto,
+                     DescriptorPool::ErrorCollector::DEFAULT_VALUE,
+                     "Boolean default must be true or false.");
+          }
+          break;
+        case FieldDescriptor::CPPTYPE_ENUM:
+          // This will be filled in when cross-linking.
+          result->default_value_enum_ = nullptr;
+          break;
+        case FieldDescriptor::CPPTYPE_STRING:
+          if (result->type() == FieldDescriptor::TYPE_BYTES) {
+            result->default_value_string_ = alloc.AllocateStrings(
+                UnescapeCEscapeString(proto.default_value()));
+          } else {
+            result->default_value_string_ =
+                alloc.AllocateStrings(proto.default_value());
+          }
+          break;
+        case FieldDescriptor::CPPTYPE_MESSAGE:
+          AddError(result->full_name(), proto,
+                   DescriptorPool::ErrorCollector::DEFAULT_VALUE,
+                   "Messages can't have default values.");
+          result->has_default_value_ = false;
+          result->default_generated_instance_ = nullptr;
+          break;
+      }
+
+      if (end_pos != nullptr) {
+        // end_pos is only set non-null by the parsers for numeric types,
+        // above. This checks that the default was non-empty and had no extra
+        // junk after the end of the number.
+        if (proto.default_value().empty() || *end_pos != '\0') {
+          AddError(result->full_name(), proto,
+                   DescriptorPool::ErrorCollector::DEFAULT_VALUE,
+                   "Couldn't parse default value \"" + proto.default_value() +
+                       "\".");
+        }
+      }
+    } else {
+      // No explicit default value
+      switch (result->cpp_type()) {
+        case FieldDescriptor::CPPTYPE_INT32:
+          result->default_value_int32_t_ = 0;
+          break;
+        case FieldDescriptor::CPPTYPE_INT64:
+          result->default_value_int64_t_ = 0;
+          break;
+        case FieldDescriptor::CPPTYPE_UINT32:
+          result->default_value_uint32_t_ = 0;
+          break;
+        case FieldDescriptor::CPPTYPE_UINT64:
+          result->default_value_uint64_t_ = 0;
+          break;
+        case FieldDescriptor::CPPTYPE_FLOAT:
+          result->default_value_float_ = 0.0f;
+          break;
+        case FieldDescriptor::CPPTYPE_DOUBLE:
+          result->default_value_double_ = 0.0;
+          break;
+        case FieldDescriptor::CPPTYPE_BOOL:
+          result->default_value_bool_ = false;
+          break;
+        case FieldDescriptor::CPPTYPE_ENUM:
+          // This will be filled in when cross-linking.
+          result->default_value_enum_ = nullptr;
+          break;
+        case FieldDescriptor::CPPTYPE_STRING:
+          result->default_value_string_ = &internal::GetEmptyString();
+          break;
+        case FieldDescriptor::CPPTYPE_MESSAGE:
+          result->default_generated_instance_ = nullptr;
+          break;
+      }
+    }
+  }
+
+  if (result->number() <= 0) {
+    message_hints_[parent].RequestHintOnFieldNumbers(
+        proto, DescriptorPool::ErrorCollector::NUMBER);
+    AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER,
+             "Field numbers must be positive integers.");
+  } else if (!is_extension && result->number() > FieldDescriptor::kMaxNumber) {
+    // Only validate that the number is within the valid field range if it is
+    // not an extension. Since extension numbers are validated with the
+    // extendee's valid set of extension numbers, and those are in turn
+    // validated against the max allowed number, the check is unnecessary for
+    // extension fields.
+    // This avoids cross-linking issues that arise when attempting to check if
+    // the extendee is a message_set_wire_format message, which has a higher max
+    // on extension numbers.
+    message_hints_[parent].RequestHintOnFieldNumbers(
+        proto, DescriptorPool::ErrorCollector::NUMBER);
+    AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER,
+             strings::Substitute("Field numbers cannot be greater than $0.",
+                              FieldDescriptor::kMaxNumber));
+  } else if (result->number() >= FieldDescriptor::kFirstReservedNumber &&
+             result->number() <= FieldDescriptor::kLastReservedNumber) {
+    message_hints_[parent].RequestHintOnFieldNumbers(
+        proto, DescriptorPool::ErrorCollector::NUMBER);
+    AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER,
+             strings::Substitute(
+                 "Field numbers $0 through $1 are reserved for the protocol "
+                 "buffer library implementation.",
+                 FieldDescriptor::kFirstReservedNumber,
+                 FieldDescriptor::kLastReservedNumber));
+  }
+
+  if (is_extension) {
+    if (!proto.has_extendee()) {
+      AddError(result->full_name(), proto,
+               DescriptorPool::ErrorCollector::EXTENDEE,
+               "FieldDescriptorProto.extendee not set for extension field.");
+    }
+
+    result->scope_.extension_scope = parent;
+
+    if (proto.has_oneof_index()) {
+      AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+               "FieldDescriptorProto.oneof_index should not be set for "
+               "extensions.");
+    }
+  } else {
+    if (proto.has_extendee()) {
+      AddError(result->full_name(), proto,
+               DescriptorPool::ErrorCollector::EXTENDEE,
+               "FieldDescriptorProto.extendee set for non-extension field.");
+    }
+
+    result->containing_type_ = parent;
+
+    if (proto.has_oneof_index()) {
+      if (proto.oneof_index() < 0 ||
+          proto.oneof_index() >= parent->oneof_decl_count()) {
+        AddError(result->full_name(), proto,
+                 DescriptorPool::ErrorCollector::TYPE,
+                 strings::Substitute("FieldDescriptorProto.oneof_index $0 is "
+                                  "out of range for type \"$1\".",
+                                  proto.oneof_index(), parent->name()));
+      } else {
+        result->is_oneof_ = true;
+        result->scope_.containing_oneof =
+            parent->oneof_decl(proto.oneof_index());
+      }
+    }
+  }
+
+  // Copy options.
+  result->options_ = nullptr;  // Set to default_instance later if necessary.
+  if (proto.has_options()) {
+    AllocateOptions(proto.options(), result,
+                    FieldDescriptorProto::kOptionsFieldNumber,
+                    "google.protobuf.FieldOptions", alloc);
+  }
+
+  AddSymbol(result->full_name(), parent, result->name(), proto, Symbol(result));
+}
+
+void DescriptorBuilder::BuildExtensionRange(
+    const DescriptorProto::ExtensionRange& proto, const Descriptor* parent,
+    Descriptor::ExtensionRange* result, internal::FlatAllocator& alloc) {
+  result->start = proto.start();
+  result->end = proto.end();
+  if (result->start <= 0) {
+    message_hints_[parent].RequestHintOnFieldNumbers(
+        proto, DescriptorPool::ErrorCollector::NUMBER, result->start,
+        result->end);
+    AddError(parent->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER,
+             "Extension numbers must be positive integers.");
+  }
+
+  // Checking of the upper bound of the extension range is deferred until after
+  // options interpreting. This allows messages with message_set_wire_format to
+  // have extensions beyond FieldDescriptor::kMaxNumber, since the extension
+  // numbers are actually used as int32s in the message_set_wire_format.
+
+  if (result->start >= result->end) {
+    AddError(parent->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER,
+             "Extension range end number must be greater than start number.");
+  }
+
+  result->options_ = nullptr;  // Set to default_instance later if necessary.
+  if (proto.has_options()) {
+    std::vector<int> options_path;
+    parent->GetLocationPath(&options_path);
+    options_path.push_back(DescriptorProto::kExtensionRangeFieldNumber);
+    // find index of this extension range in order to compute path
+    int index;
+    for (index = 0; parent->extension_ranges_ + index != result; index++) {
+    }
+    options_path.push_back(index);
+    options_path.push_back(DescriptorProto_ExtensionRange::kOptionsFieldNumber);
+    AllocateOptionsImpl(parent->full_name(), parent->full_name(),
+                        proto.options(), result, options_path,
+                        "google.protobuf.ExtensionRangeOptions", alloc);
+  }
+}
+
+void DescriptorBuilder::BuildReservedRange(
+    const DescriptorProto::ReservedRange& proto, const Descriptor* parent,
+    Descriptor::ReservedRange* result, internal::FlatAllocator&) {
+  result->start = proto.start();
+  result->end = proto.end();
+  if (result->start <= 0) {
+    message_hints_[parent].RequestHintOnFieldNumbers(
+        proto, DescriptorPool::ErrorCollector::NUMBER, result->start,
+        result->end);
+    AddError(parent->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER,
+             "Reserved numbers must be positive integers.");
+  }
+}
+
+void DescriptorBuilder::BuildReservedRange(
+    const EnumDescriptorProto::EnumReservedRange& proto,
+    const EnumDescriptor* parent, EnumDescriptor::ReservedRange* result,
+    internal::FlatAllocator&) {
+  result->start = proto.start();
+  result->end = proto.end();
+
+  if (result->start > result->end) {
+    AddError(parent->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER,
+             "Reserved range end number must be greater than start number.");
+  }
+}
+
+void DescriptorBuilder::BuildOneof(const OneofDescriptorProto& proto,
+                                   Descriptor* parent, OneofDescriptor* result,
+                                   internal::FlatAllocator& alloc) {
+  result->all_names_ =
+      AllocateNameStrings(parent->full_name(), proto.name(), alloc);
+  ValidateSymbolName(proto.name(), result->full_name(), proto);
+
+  result->containing_type_ = parent;
+
+  // We need to fill these in later.
+  result->field_count_ = 0;
+  result->fields_ = nullptr;
+  result->options_ = nullptr;
+
+  // Copy options.
+  if (proto.has_options()) {
+    AllocateOptions(proto.options(), result,
+                    OneofDescriptorProto::kOptionsFieldNumber,
+                    "google.protobuf.OneofOptions", alloc);
+  }
+
+  AddSymbol(result->full_name(), parent, result->name(), proto, Symbol(result));
+}
+
+void DescriptorBuilder::CheckEnumValueUniqueness(
+    const EnumDescriptorProto& proto, const EnumDescriptor* result) {
+
+  // Check that enum labels are still unique when we remove the enum prefix from
+  // values that have it.
+  //
+  // This will fail for something like:
+  //
+  //   enum MyEnum {
+  //     MY_ENUM_FOO = 0;
+  //     FOO = 1;
+  //   }
+  //
+  // By enforcing this reasonable constraint, we allow code generators to strip
+  // the prefix and/or PascalCase it without creating conflicts.  This can lead
+  // to much nicer language-specific enums like:
+  //
+  //   enum NameType {
+  //     FirstName = 1,
+  //     LastName = 2,
+  //   }
+  //
+  // Instead of:
+  //
+  //   enum NameType {
+  //     NAME_TYPE_FIRST_NAME = 1,
+  //     NAME_TYPE_LAST_NAME = 2,
+  //   }
+  PrefixRemover remover(result->name());
+  std::map<std::string, const EnumValueDescriptor*> values;
+  for (int i = 0; i < result->value_count(); i++) {
+    const EnumValueDescriptor* value = result->value(i);
+    std::string stripped =
+        EnumValueToPascalCase(remover.MaybeRemove(value->name()));
+    std::pair<std::map<std::string, const EnumValueDescriptor*>::iterator, bool>
+        insert_result = values.insert(std::make_pair(stripped, value));
+    bool inserted = insert_result.second;
+
+    // We don't throw the error if the two conflicting symbols are identical, or
+    // if they map to the same number.  In the former case, the normal symbol
+    // duplication error will fire so we don't need to (and its error message
+    // will make more sense). We allow the latter case so users can create
+    // aliases which add or remove the prefix (code generators that do prefix
+    // stripping should de-dup the labels in this case).
+    if (!inserted && insert_result.first->second->name() != value->name() &&
+        insert_result.first->second->number() != value->number()) {
+      std::string error_message =
+          "Enum name " + value->name() + " has the same name as " +
+          values[stripped]->name() +
+          " if you ignore case and strip out the enum name prefix (if any). "
+          "This is error-prone and can lead to undefined behavior. "
+          "Please avoid doing this. If you are using allow_alias, please "
+          "assign the same numeric value to both enums.";
+      // There are proto2 enums out there with conflicting names, so to preserve
+      // compatibility we issue only a warning for proto2.
+      if (result->file()->syntax() == FileDescriptor::SYNTAX_PROTO2) {
+        AddWarning(value->full_name(), proto.value(i),
+                   DescriptorPool::ErrorCollector::NAME, error_message);
+      } else {
+        AddError(value->full_name(), proto.value(i),
+                 DescriptorPool::ErrorCollector::NAME, error_message);
+      }
+    }
+  }
+}
+
+void DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto,
+                                  const Descriptor* parent,
+                                  EnumDescriptor* result,
+                                  internal::FlatAllocator& alloc) {
+  const std::string& scope =
+      (parent == nullptr) ? file_->package() : parent->full_name();
+
+  result->all_names_ = AllocateNameStrings(scope, proto.name(), alloc);
+  ValidateSymbolName(proto.name(), result->full_name(), proto);
+  result->file_ = file_;
+  result->containing_type_ = parent;
+  result->is_placeholder_ = false;
+  result->is_unqualified_placeholder_ = false;
+
+  if (proto.value_size() == 0) {
+    // We cannot allow enums with no values because this would mean there
+    // would be no valid default value for fields of this type.
+    AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NAME,
+             "Enums must contain at least one value.");
+  }
+
+  // Calculate the continuous sequence of the labels.
+  // These can be fast-path'd during lookup and don't need to be added to the
+  // tables.
+  // We use uint16_t to save space for sequential_value_limit_, so stop before
+  // overflowing it. Worst case, we are not taking full advantage on huge
+  // enums, but it is unlikely.
+  for (int i = 0;
+       i < std::numeric_limits<uint16_t>::max() && i < proto.value_size() &&
+       // We do the math in int64_t to avoid overflows.
+       proto.value(i).number() ==
+           static_cast<int64_t>(i) + proto.value(0).number();
+       ++i) {
+    result->sequential_value_limit_ = i;
+  }
+
+  BUILD_ARRAY(proto, result, value, BuildEnumValue, result);
+  BUILD_ARRAY(proto, result, reserved_range, BuildReservedRange, result);
+
+  // Copy reserved names.
+  int reserved_name_count = proto.reserved_name_size();
+  result->reserved_name_count_ = reserved_name_count;
+  result->reserved_names_ =
+      alloc.AllocateArray<const std::string*>(reserved_name_count);
+  for (int i = 0; i < reserved_name_count; ++i) {
+    result->reserved_names_[i] =
+        alloc.AllocateStrings(proto.reserved_name(i));
+  }
+
+  CheckEnumValueUniqueness(proto, result);
+
+  // Copy options.
+  result->options_ = nullptr;  // Set to default_instance later if necessary.
+  if (proto.has_options()) {
+    AllocateOptions(proto.options(), result,
+                    EnumDescriptorProto::kOptionsFieldNumber,
+                    "google.protobuf.EnumOptions", alloc);
+  }
+
+  AddSymbol(result->full_name(), parent, result->name(), proto, Symbol(result));
+
+  for (int i = 0; i < proto.reserved_range_size(); i++) {
+    const EnumDescriptorProto_EnumReservedRange& range1 =
+        proto.reserved_range(i);
+    for (int j = i + 1; j < proto.reserved_range_size(); j++) {
+      const EnumDescriptorProto_EnumReservedRange& range2 =
+          proto.reserved_range(j);
+      if (range1.end() >= range2.start() && range2.end() >= range1.start()) {
+        AddError(result->full_name(), proto.reserved_range(i),
+                 DescriptorPool::ErrorCollector::NUMBER,
+                 strings::Substitute("Reserved range $0 to $1 overlaps with "
+                                  "already-defined range $2 to $3.",
+                                  range2.start(), range2.end(), range1.start(),
+                                  range1.end()));
+      }
+    }
+  }
+
+  HASH_SET<std::string> reserved_name_set;
+  for (int i = 0; i < proto.reserved_name_size(); i++) {
+    const std::string& name = proto.reserved_name(i);
+    if (reserved_name_set.find(name) == reserved_name_set.end()) {
+      reserved_name_set.insert(name);
+    } else {
+      AddError(name, proto, DescriptorPool::ErrorCollector::NAME,
+               strings::Substitute("Enum value \"$0\" is reserved multiple times.",
+                                name));
+    }
+  }
+
+  for (int i = 0; i < result->value_count(); i++) {
+    const EnumValueDescriptor* value = result->value(i);
+    for (int j = 0; j < result->reserved_range_count(); j++) {
+      const EnumDescriptor::ReservedRange* range = result->reserved_range(j);
+      if (range->start <= value->number() && value->number() <= range->end) {
+        AddError(value->full_name(), proto.reserved_range(j),
+                 DescriptorPool::ErrorCollector::NUMBER,
+                 strings::Substitute("Enum value \"$0\" uses reserved number $1.",
+                                  value->name(), value->number()));
+      }
+    }
+    if (reserved_name_set.find(value->name()) != reserved_name_set.end()) {
+      AddError(
+          value->full_name(), proto.value(i),
+          DescriptorPool::ErrorCollector::NAME,
+          strings::Substitute("Enum value \"$0\" is reserved.", value->name()));
+    }
+  }
+}
+
+void DescriptorBuilder::BuildEnumValue(const EnumValueDescriptorProto& proto,
+                                       const EnumDescriptor* parent,
+                                       EnumValueDescriptor* result,
+                                       internal::FlatAllocator& alloc) {
+  // Note:  full_name for enum values is a sibling to the parent's name, not a
+  //   child of it.
+  std::string full_name;
+  size_t scope_len = parent->full_name().size() - parent->name().size();
+  full_name.reserve(scope_len + proto.name().size());
+  full_name.append(parent->full_name().data(), scope_len);
+  full_name.append(proto.name());
+
+  result->all_names_ =
+      alloc.AllocateStrings(proto.name(), std::move(full_name));
+  result->number_ = proto.number();
+  result->type_ = parent;
+
+  ValidateSymbolName(proto.name(), result->full_name(), proto);
+
+  // Copy options.
+  result->options_ = nullptr;  // Set to default_instance later if necessary.
+  if (proto.has_options()) {
+    AllocateOptions(proto.options(), result,
+                    EnumValueDescriptorProto::kOptionsFieldNumber,
+                    "google.protobuf.EnumValueOptions", alloc);
+  }
+
+  // Again, enum values are weird because we makes them appear as siblings
+  // of the enum type instead of children of it.  So, we use
+  // parent->containing_type() as the value's parent.
+  bool added_to_outer_scope =
+      AddSymbol(result->full_name(), parent->containing_type(), result->name(),
+                proto, Symbol::EnumValue(result, 0));
+
+  // However, we also want to be able to search for values within a single
+  // enum type, so we add it as a child of the enum type itself, too.
+  // Note:  This could fail, but if it does, the error has already been
+  //   reported by the above AddSymbol() call, so we ignore the return code.
+  bool added_to_inner_scope = file_tables_->AddAliasUnderParent(
+      parent, result->name(), Symbol::EnumValue(result, 1));
+
+  if (added_to_inner_scope && !added_to_outer_scope) {
+    // This value did not conflict with any values defined in the same enum,
+    // but it did conflict with some other symbol defined in the enum type's
+    // scope.  Let's print an additional error to explain this.
+    std::string outer_scope;
+    if (parent->containing_type() == nullptr) {
+      outer_scope = file_->package();
+    } else {
+      outer_scope = parent->containing_type()->full_name();
+    }
+
+    if (outer_scope.empty()) {
+      outer_scope = "the global scope";
+    } else {
+      outer_scope = "\"" + outer_scope + "\"";
+    }
+
+    AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NAME,
+             "Note that enum values use C++ scoping rules, meaning that "
+             "enum values are siblings of their type, not children of it.  "
+             "Therefore, \"" +
+                 result->name() + "\" must be unique within " + outer_scope +
+                 ", not just within \"" + parent->name() + "\".");
+  }
+
+  // An enum is allowed to define two numbers that refer to the same value.
+  // FindValueByNumber() should return the first such value, so we simply
+  // ignore AddEnumValueByNumber()'s return code.
+  file_tables_->AddEnumValueByNumber(result);
+}
+
+void DescriptorBuilder::BuildService(const ServiceDescriptorProto& proto,
+                                     const void* /* dummy */,
+                                     ServiceDescriptor* result,
+                                     internal::FlatAllocator& alloc) {
+  result->all_names_ =
+      AllocateNameStrings(file_->package(), proto.name(), alloc);
+  result->file_ = file_;
+  ValidateSymbolName(proto.name(), result->full_name(), proto);
+
+  BUILD_ARRAY(proto, result, method, BuildMethod, result);
+
+  // Copy options.
+  result->options_ = nullptr;  // Set to default_instance later if necessary.
+  if (proto.has_options()) {
+    AllocateOptions(proto.options(), result,
+                    ServiceDescriptorProto::kOptionsFieldNumber,
+                    "google.protobuf.ServiceOptions", alloc);
+  }
+
+  AddSymbol(result->full_name(), nullptr, result->name(), proto,
+            Symbol(result));
+}
+
+void DescriptorBuilder::BuildMethod(const MethodDescriptorProto& proto,
+                                    const ServiceDescriptor* parent,
+                                    MethodDescriptor* result,
+                                    internal::FlatAllocator& alloc) {
+  result->service_ = parent;
+  result->all_names_ =
+      AllocateNameStrings(parent->full_name(), proto.name(), alloc);
+
+  ValidateSymbolName(proto.name(), result->full_name(), proto);
+
+  // These will be filled in when cross-linking.
+  result->input_type_.Init();
+  result->output_type_.Init();
+
+  // Copy options.
+  result->options_ = nullptr;  // Set to default_instance later if necessary.
+  if (proto.has_options()) {
+    AllocateOptions(proto.options(), result,
+                    MethodDescriptorProto::kOptionsFieldNumber,
+                    "google.protobuf.MethodOptions", alloc);
+  }
+
+  result->client_streaming_ = proto.client_streaming();
+  result->server_streaming_ = proto.server_streaming();
+
+  AddSymbol(result->full_name(), parent, result->name(), proto, Symbol(result));
+}
+
+#undef BUILD_ARRAY
+
+// -------------------------------------------------------------------
+
+void DescriptorBuilder::CrossLinkFile(FileDescriptor* file,
+                                      const FileDescriptorProto& proto) {
+  if (file->options_ == nullptr) {
+    file->options_ = &FileOptions::default_instance();
+  }
+
+  for (int i = 0; i < file->message_type_count(); i++) {
+    CrossLinkMessage(&file->message_types_[i], proto.message_type(i));
+  }
+
+  for (int i = 0; i < file->extension_count(); i++) {
+    CrossLinkField(&file->extensions_[i], proto.extension(i));
+  }
+
+  for (int i = 0; i < file->enum_type_count(); i++) {
+    CrossLinkEnum(&file->enum_types_[i], proto.enum_type(i));
+  }
+
+  for (int i = 0; i < file->service_count(); i++) {
+    CrossLinkService(&file->services_[i], proto.service(i));
+  }
+}
+
+void DescriptorBuilder::CrossLinkMessage(Descriptor* message,
+                                         const DescriptorProto& proto) {
+  if (message->options_ == nullptr) {
+    message->options_ = &MessageOptions::default_instance();
+  }
+
+  for (int i = 0; i < message->nested_type_count(); i++) {
+    CrossLinkMessage(&message->nested_types_[i], proto.nested_type(i));
+  }
+
+  for (int i = 0; i < message->enum_type_count(); i++) {
+    CrossLinkEnum(&message->enum_types_[i], proto.enum_type(i));
+  }
+
+  for (int i = 0; i < message->field_count(); i++) {
+    CrossLinkField(&message->fields_[i], proto.field(i));
+  }
+
+  for (int i = 0; i < message->extension_count(); i++) {
+    CrossLinkField(&message->extensions_[i], proto.extension(i));
+  }
+
+  for (int i = 0; i < message->extension_range_count(); i++) {
+    CrossLinkExtensionRange(&message->extension_ranges_[i],
+                            proto.extension_range(i));
+  }
+
+  // Set up field array for each oneof.
+
+  // First count the number of fields per oneof.
+  for (int i = 0; i < message->field_count(); i++) {
+    const OneofDescriptor* oneof_decl = message->field(i)->containing_oneof();
+    if (oneof_decl != nullptr) {
+      // Make sure fields belonging to the same oneof are defined consecutively.
+      // This enables optimizations in codegens and reflection libraries to
+      // skip fields in the oneof group, as only one of the field can be set.
+      // Note that field_count() returns how many fields in this oneof we have
+      // seen so far. field_count() > 0 guarantees that i > 0, so field(i-1) is
+      // safe.
+      if (oneof_decl->field_count() > 0 &&
+          message->field(i - 1)->containing_oneof() != oneof_decl) {
+        AddError(message->full_name() + "." + message->field(i - 1)->name(),
+                 proto.field(i - 1), DescriptorPool::ErrorCollector::TYPE,
+                 strings::Substitute(
+                     "Fields in the same oneof must be defined consecutively. "
+                     "\"$0\" cannot be defined before the completion of the "
+                     "\"$1\" oneof definition.",
+                     message->field(i - 1)->name(), oneof_decl->name()));
+      }
+      // Must go through oneof_decls_ array to get a non-const version of the
+      // OneofDescriptor.
+      auto& out_oneof_decl = message->oneof_decls_[oneof_decl->index()];
+      if (out_oneof_decl.field_count_ == 0) {
+        out_oneof_decl.fields_ = message->field(i);
+      }
+
+      if (!had_errors_) {
+        // Verify that they are contiguous.
+        // This is assumed by OneofDescriptor::field(i).
+        // But only if there are no errors.
+        GOOGLE_CHECK_EQ(out_oneof_decl.fields_ + out_oneof_decl.field_count_,
+                 message->field(i));
+      }
+      ++out_oneof_decl.field_count_;
+    }
+  }
+
+  // Then verify the sizes.
+  for (int i = 0; i < message->oneof_decl_count(); i++) {
+    OneofDescriptor* oneof_decl = &message->oneof_decls_[i];
+
+    if (oneof_decl->field_count() == 0) {
+      AddError(message->full_name() + "." + oneof_decl->name(),
+               proto.oneof_decl(i), DescriptorPool::ErrorCollector::NAME,
+               "Oneof must have at least one field.");
+    }
+
+    if (oneof_decl->options_ == nullptr) {
+      oneof_decl->options_ = &OneofOptions::default_instance();
+    }
+  }
+
+  for (int i = 0; i < message->field_count(); i++) {
+    const FieldDescriptor* field = message->field(i);
+    if (field->proto3_optional_) {
+      if (!field->containing_oneof() ||
+          !field->containing_oneof()->is_synthetic()) {
+        AddError(message->full_name(), proto.field(i),
+                 DescriptorPool::ErrorCollector::OTHER,
+                 "Fields with proto3_optional set must be "
+                 "a member of a one-field oneof");
+      }
+    }
+  }
+
+  // Synthetic oneofs must be last.
+  int first_synthetic = -1;
+  for (int i = 0; i < message->oneof_decl_count(); i++) {
+    const OneofDescriptor* oneof = message->oneof_decl(i);
+    if (oneof->is_synthetic()) {
+      if (first_synthetic == -1) {
+        first_synthetic = i;
+      }
+    } else {
+      if (first_synthetic != -1) {
+        AddError(message->full_name(), proto.oneof_decl(i),
+                 DescriptorPool::ErrorCollector::OTHER,
+                 "Synthetic oneofs must be after all other oneofs");
+      }
+    }
+  }
+
+  if (first_synthetic == -1) {
+    message->real_oneof_decl_count_ = message->oneof_decl_count_;
+  } else {
+    message->real_oneof_decl_count_ = first_synthetic;
+  }
+}
+
+void DescriptorBuilder::CrossLinkExtensionRange(
+    Descriptor::ExtensionRange* range,
+    const DescriptorProto::ExtensionRange& /*proto*/) {
+  if (range->options_ == nullptr) {
+    range->options_ = &ExtensionRangeOptions::default_instance();
+  }
+}
+
+void DescriptorBuilder::CrossLinkField(FieldDescriptor* field,
+                                       const FieldDescriptorProto& proto) {
+  if (field->options_ == nullptr) {
+    field->options_ = &FieldOptions::default_instance();
+  }
+
+  if (proto.has_extendee()) {
+    Symbol extendee =
+        LookupSymbol(proto.extendee(), field->full_name(),
+                     DescriptorPool::PLACEHOLDER_EXTENDABLE_MESSAGE);
+    if (extendee.IsNull()) {
+      AddNotDefinedError(field->full_name(), proto,
+                         DescriptorPool::ErrorCollector::EXTENDEE,
+                         proto.extendee());
+      return;
+    } else if (extendee.type() != Symbol::MESSAGE) {
+      AddError(field->full_name(), proto,
+               DescriptorPool::ErrorCollector::EXTENDEE,
+               "\"" + proto.extendee() + "\" is not a message type.");
+      return;
+    }
+    field->containing_type_ = extendee.descriptor();
+
+    const Descriptor::ExtensionRange* extension_range =
+        field->containing_type()->FindExtensionRangeContainingNumber(
+            field->number());
+
+    if (extension_range == nullptr) {
+      // Set of valid extension numbers for MessageSet is different (< 2^32)
+      // from other extendees (< 2^29). If unknown deps are allowed, we may not
+      // have that information, and wrongly deem the extension as invalid.
+      auto skip_check = get_allow_unknown(pool_) &&
+                        proto.extendee() == "google.protobuf.bridge.MessageSet";
+      if (!skip_check) {
+        AddError(field->full_name(), proto,
+                 DescriptorPool::ErrorCollector::NUMBER,
+                 strings::Substitute("\"$0\" does not declare $1 as an "
+                                  "extension number.",
+                                  field->containing_type()->full_name(),
+                                  field->number()));
+      }
+    }
+  }
+
+  if (field->containing_oneof() != nullptr) {
+    if (field->label() != FieldDescriptor::LABEL_OPTIONAL) {
+      // Note that this error will never happen when parsing .proto files.
+      // It can only happen if you manually construct a FileDescriptorProto
+      // that is incorrect.
+      AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::NAME,
+               "Fields of oneofs must themselves have label LABEL_OPTIONAL.");
+    }
+  }
+
+  if (proto.has_type_name()) {
+    // Assume we are expecting a message type unless the proto contains some
+    // evidence that it expects an enum type.  This only makes a difference if
+    // we end up creating a placeholder.
+    bool expecting_enum = (proto.type() == FieldDescriptorProto::TYPE_ENUM) ||
+                          proto.has_default_value();
+
+    // In case of weak fields we force building the dependency. We need to know
+    // if the type exist or not. If it doesn't exist we substitute Empty which
+    // should only be done if the type can't be found in the generated pool.
+    // TODO(gerbens) Ideally we should query the database directly to check
+    // if weak fields exist or not so that we don't need to force building
+    // weak dependencies. However the name lookup rules for symbols are
+    // somewhat complicated, so I defer it too another CL.
+    bool is_weak = !pool_->enforce_weak_ && proto.options().weak();
+    bool is_lazy = pool_->lazily_build_dependencies_ && !is_weak;
+
+    Symbol type =
+        LookupSymbol(proto.type_name(), field->full_name(),
+                     expecting_enum ? DescriptorPool::PLACEHOLDER_ENUM
+                                    : DescriptorPool::PLACEHOLDER_MESSAGE,
+                     LOOKUP_TYPES, !is_lazy);
+
+    if (type.IsNull()) {
+      if (is_lazy) {
+        // Save the symbol names for later for lookup, and allocate the once
+        // object needed for the accessors.
+        const std::string& name = proto.type_name();
+
+        int name_sizes = static_cast<int>(name.size() + 1 +
+                                          proto.default_value().size() + 1);
+
+        field->type_once_ = ::new (tables_->AllocateBytes(static_cast<int>(
+            sizeof(internal::once_flag) + name_sizes))) internal::once_flag{};
+        char* names = reinterpret_cast<char*>(field->type_once_ + 1);
+
+        memcpy(names, name.c_str(), name.size() + 1);
+        memcpy(names + name.size() + 1, proto.default_value().c_str(),
+               proto.default_value().size() + 1);
+
+        // AddFieldByNumber and AddExtension are done later in this function,
+        // and can/must be done if the field type was not found. The related
+        // error checking is not necessary when in lazily_build_dependencies_
+        // mode, and can't be done without building the type's descriptor,
+        // which we don't want to do.
+        file_tables_->AddFieldByNumber(field);
+        if (field->is_extension()) {
+          tables_->AddExtension(field);
+        }
+        return;
+      } else {
+        // If the type is a weak type, we change the type to a google.protobuf.Empty
+        // field.
+        if (is_weak) {
+          type = FindSymbol(kNonLinkedWeakMessageReplacementName);
+        }
+        if (type.IsNull()) {
+          AddNotDefinedError(field->full_name(), proto,
+                             DescriptorPool::ErrorCollector::TYPE,
+                             proto.type_name());
+          return;
+        }
+      }
+    }
+
+    if (!proto.has_type()) {
+      // Choose field type based on symbol.
+      if (type.type() == Symbol::MESSAGE) {
+        field->type_ = FieldDescriptor::TYPE_MESSAGE;
+      } else if (type.type() == Symbol::ENUM) {
+        field->type_ = FieldDescriptor::TYPE_ENUM;
+      } else {
+        AddError(field->full_name(), proto,
+                 DescriptorPool::ErrorCollector::TYPE,
+                 "\"" + proto.type_name() + "\" is not a type.");
+        return;
+      }
+    }
+
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      field->type_descriptor_.message_type = type.descriptor();
+      if (field->type_descriptor_.message_type == nullptr) {
+        AddError(field->full_name(), proto,
+                 DescriptorPool::ErrorCollector::TYPE,
+                 "\"" + proto.type_name() + "\" is not a message type.");
+        return;
+      }
+
+      if (field->has_default_value()) {
+        AddError(field->full_name(), proto,
+                 DescriptorPool::ErrorCollector::DEFAULT_VALUE,
+                 "Messages can't have default values.");
+      }
+    } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+      field->type_descriptor_.enum_type = type.enum_descriptor();
+      if (field->type_descriptor_.enum_type == nullptr) {
+        AddError(field->full_name(), proto,
+                 DescriptorPool::ErrorCollector::TYPE,
+                 "\"" + proto.type_name() + "\" is not an enum type.");
+        return;
+      }
+
+      if (field->enum_type()->is_placeholder_) {
+        // We can't look up default values for placeholder types.  We'll have
+        // to just drop them.
+        field->has_default_value_ = false;
+      }
+
+      if (field->has_default_value()) {
+        // Ensure that the default value is an identifier. Parser cannot always
+        // verify this because it does not have complete type information.
+        // N.B. that this check yields better error messages but is not
+        // necessary for correctness (an enum symbol must be a valid identifier
+        // anyway), only for better errors.
+        if (!io::Tokenizer::IsIdentifier(proto.default_value())) {
+          AddError(field->full_name(), proto,
+                   DescriptorPool::ErrorCollector::DEFAULT_VALUE,
+                   "Default value for an enum field must be an identifier.");
+        } else {
+          // We can't just use field->enum_type()->FindValueByName() here
+          // because that locks the pool's mutex, which we have already locked
+          // at this point.
+          const EnumValueDescriptor* default_value =
+              LookupSymbolNoPlaceholder(proto.default_value(),
+                                        field->enum_type()->full_name())
+                  .enum_value_descriptor();
+
+          if (default_value != nullptr &&
+              default_value->type() == field->enum_type()) {
+            field->default_value_enum_ = default_value;
+          } else {
+            AddError(field->full_name(), proto,
+                     DescriptorPool::ErrorCollector::DEFAULT_VALUE,
+                     "Enum type \"" + field->enum_type()->full_name() +
+                         "\" has no value named \"" + proto.default_value() +
+                         "\".");
+          }
+        }
+      } else if (field->enum_type()->value_count() > 0) {
+        // All enums must have at least one value, or we would have reported
+        // an error elsewhere.  We use the first defined value as the default
+        // if a default is not explicitly defined.
+        field->default_value_enum_ = field->enum_type()->value(0);
+      }
+    } else {
+      AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+               "Field with primitive type has type_name.");
+    }
+  } else {
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
+        field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+      AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+               "Field with message or enum type missing type_name.");
+    }
+  }
+
+  // Add the field to the fields-by-number table.
+  // Note:  We have to do this *after* cross-linking because extensions do not
+  // know their containing type until now. If we're in
+  // lazily_build_dependencies_ mode, we're guaranteed there's no errors, so no
+  // risk to calling containing_type() or other accessors that will build
+  // dependencies.
+  if (!file_tables_->AddFieldByNumber(field)) {
+    const FieldDescriptor* conflicting_field = file_tables_->FindFieldByNumber(
+        field->containing_type(), field->number());
+    std::string containing_type_name =
+        field->containing_type() == nullptr
+            ? "unknown"
+            : field->containing_type()->full_name();
+    if (field->is_extension()) {
+      AddError(field->full_name(), proto,
+               DescriptorPool::ErrorCollector::NUMBER,
+               strings::Substitute("Extension number $0 has already been used "
+                                "in \"$1\" by extension \"$2\".",
+                                field->number(), containing_type_name,
+                                conflicting_field->full_name()));
+    } else {
+      AddError(field->full_name(), proto,
+               DescriptorPool::ErrorCollector::NUMBER,
+               strings::Substitute("Field number $0 has already been used in "
+                                "\"$1\" by field \"$2\".",
+                                field->number(), containing_type_name,
+                                conflicting_field->name()));
+    }
+  } else {
+    if (field->is_extension()) {
+      if (!tables_->AddExtension(field)) {
+        const FieldDescriptor* conflicting_field =
+            tables_->FindExtension(field->containing_type(), field->number());
+        std::string containing_type_name =
+            field->containing_type() == nullptr
+                ? "unknown"
+                : field->containing_type()->full_name();
+        std::string error_msg = strings::Substitute(
+            "Extension number $0 has already been used in \"$1\" by extension "
+            "\"$2\" defined in $3.",
+            field->number(), containing_type_name,
+            conflicting_field->full_name(), conflicting_field->file()->name());
+        // Conflicting extension numbers should be an error. However, before
+        // turning this into an error we need to fix all existing broken
+        // protos first.
+        // TODO(xiaofeng): Change this to an error.
+        AddWarning(field->full_name(), proto,
+                   DescriptorPool::ErrorCollector::NUMBER, error_msg);
+      }
+    }
+  }
+}
+
+void DescriptorBuilder::CrossLinkEnum(EnumDescriptor* enum_type,
+                                      const EnumDescriptorProto& proto) {
+  if (enum_type->options_ == nullptr) {
+    enum_type->options_ = &EnumOptions::default_instance();
+  }
+
+  for (int i = 0; i < enum_type->value_count(); i++) {
+    CrossLinkEnumValue(&enum_type->values_[i], proto.value(i));
+  }
+}
+
+void DescriptorBuilder::CrossLinkEnumValue(
+    EnumValueDescriptor* enum_value,
+    const EnumValueDescriptorProto& /* proto */) {
+  if (enum_value->options_ == nullptr) {
+    enum_value->options_ = &EnumValueOptions::default_instance();
+  }
+}
+
+void DescriptorBuilder::CrossLinkService(ServiceDescriptor* service,
+                                         const ServiceDescriptorProto& proto) {
+  if (service->options_ == nullptr) {
+    service->options_ = &ServiceOptions::default_instance();
+  }
+
+  for (int i = 0; i < service->method_count(); i++) {
+    CrossLinkMethod(&service->methods_[i], proto.method(i));
+  }
+}
+
+void DescriptorBuilder::CrossLinkMethod(MethodDescriptor* method,
+                                        const MethodDescriptorProto& proto) {
+  if (method->options_ == nullptr) {
+    method->options_ = &MethodOptions::default_instance();
+  }
+
+  Symbol input_type =
+      LookupSymbol(proto.input_type(), method->full_name(),
+                   DescriptorPool::PLACEHOLDER_MESSAGE, LOOKUP_ALL,
+                   !pool_->lazily_build_dependencies_);
+  if (input_type.IsNull()) {
+    if (!pool_->lazily_build_dependencies_) {
+      AddNotDefinedError(method->full_name(), proto,
+                         DescriptorPool::ErrorCollector::INPUT_TYPE,
+                         proto.input_type());
+    } else {
+      method->input_type_.SetLazy(proto.input_type(), file_);
+    }
+  } else if (input_type.type() != Symbol::MESSAGE) {
+    AddError(method->full_name(), proto,
+             DescriptorPool::ErrorCollector::INPUT_TYPE,
+             "\"" + proto.input_type() + "\" is not a message type.");
+  } else {
+    method->input_type_.Set(input_type.descriptor());
+  }
+
+  Symbol output_type =
+      LookupSymbol(proto.output_type(), method->full_name(),
+                   DescriptorPool::PLACEHOLDER_MESSAGE, LOOKUP_ALL,
+                   !pool_->lazily_build_dependencies_);
+  if (output_type.IsNull()) {
+    if (!pool_->lazily_build_dependencies_) {
+      AddNotDefinedError(method->full_name(), proto,
+                         DescriptorPool::ErrorCollector::OUTPUT_TYPE,
+                         proto.output_type());
+    } else {
+      method->output_type_.SetLazy(proto.output_type(), file_);
+    }
+  } else if (output_type.type() != Symbol::MESSAGE) {
+    AddError(method->full_name(), proto,
+             DescriptorPool::ErrorCollector::OUTPUT_TYPE,
+             "\"" + proto.output_type() + "\" is not a message type.");
+  } else {
+    method->output_type_.Set(output_type.descriptor());
+  }
+}
+
+void DescriptorBuilder::SuggestFieldNumbers(FileDescriptor* file,
+                                            const FileDescriptorProto& proto) {
+  for (int message_index = 0; message_index < file->message_type_count();
+       message_index++) {
+    const Descriptor* message = &file->message_types_[message_index];
+    auto* hints = FindOrNull(message_hints_, message);
+    if (!hints) continue;
+    constexpr int kMaxSuggestions = 3;
+    int fields_to_suggest = std::min(kMaxSuggestions, hints->fields_to_suggest);
+    if (fields_to_suggest <= 0) continue;
+    struct Range {
+      int from;
+      int to;
+    };
+    std::vector<Range> used_ordinals;
+    auto add_ordinal = [&](int ordinal) {
+      if (ordinal <= 0 || ordinal > FieldDescriptor::kMaxNumber) return;
+      if (!used_ordinals.empty() &&
+          ordinal == used_ordinals.back().to) {
+        used_ordinals.back().to = ordinal + 1;
+      } else {
+        used_ordinals.push_back({ordinal, ordinal + 1});
+      }
+    };
+    auto add_range = [&](int from, int to) {
+      from = std::max(0, std::min(FieldDescriptor::kMaxNumber + 1, from));
+      to = std::max(0, std::min(FieldDescriptor::kMaxNumber + 1, to));
+      if (from >= to) return;
+      used_ordinals.push_back({from, to});
+    };
+    for (int i = 0; i < message->field_count(); i++) {
+      add_ordinal(message->field(i)->number());
+    }
+    for (int i = 0; i < message->extension_count(); i++) {
+      add_ordinal(message->extension(i)->number());
+    }
+    for (int i = 0; i < message->reserved_range_count(); i++) {
+      auto range = message->reserved_range(i);
+      add_range(range->start, range->end);
+    }
+    for (int i = 0; i < message->extension_range_count(); i++) {
+      auto range = message->extension_range(i);
+      add_range(range->start, range->end);
+    }
+    used_ordinals.push_back(
+        {FieldDescriptor::kMaxNumber, FieldDescriptor::kMaxNumber + 1});
+    used_ordinals.push_back({FieldDescriptor::kFirstReservedNumber,
+                             FieldDescriptor::kLastReservedNumber});
+    std::sort(used_ordinals.begin(), used_ordinals.end(),
+              [](Range lhs, Range rhs) {
+                return std::tie(lhs.from, lhs.to) < std::tie(rhs.from, rhs.to);
+              });
+    int current_ordinal = 1;
+    std::stringstream id_list;
+    id_list << "Suggested field numbers for " << message->full_name() << ": ";
+    const char* separator = "";
+    for (auto& current_range : used_ordinals) {
+      while (current_ordinal < current_range.from && fields_to_suggest > 0) {
+        id_list << separator << current_ordinal++;
+        separator = ", ";
+        fields_to_suggest--;
+      }
+      if (fields_to_suggest == 0) break;
+      current_ordinal = std::max(current_ordinal, current_range.to);
+    }
+    if (hints->first_reason) {
+      AddError(message->full_name(), *hints->first_reason,
+               hints->first_reason_location, id_list.str());
+    }
+  }
+}
+
+// -------------------------------------------------------------------
+
+#define VALIDATE_OPTIONS_FROM_ARRAY(descriptor, array_name, type) \
+  for (int i = 0; i < descriptor->array_name##_count(); ++i) {    \
+    Validate##type##Options(descriptor->array_name##s_ + i,       \
+                            proto.array_name(i));                 \
+  }
+
+// Determine if the file uses optimize_for = LITE_RUNTIME, being careful to
+// avoid problems that exist at init time.
+static bool IsLite(const FileDescriptor* file) {
+  // TODO(kenton):  I don't even remember how many of these conditions are
+  //   actually possible.  I'm just being super-safe.
+  return file != nullptr &&
+         &file->options() != &FileOptions::default_instance() &&
+         file->options().optimize_for() == FileOptions::LITE_RUNTIME;
+}
+
+void DescriptorBuilder::ValidateFileOptions(FileDescriptor* file,
+                                            const FileDescriptorProto& proto) {
+  VALIDATE_OPTIONS_FROM_ARRAY(file, message_type, Message);
+  VALIDATE_OPTIONS_FROM_ARRAY(file, enum_type, Enum);
+  VALIDATE_OPTIONS_FROM_ARRAY(file, service, Service);
+  VALIDATE_OPTIONS_FROM_ARRAY(file, extension, Field);
+
+  // Lite files can only be imported by other Lite files.
+  if (!IsLite(file)) {
+    for (int i = 0; i < file->dependency_count(); i++) {
+      if (IsLite(file->dependency(i))) {
+        AddError(
+            file->dependency(i)->name(), proto,
+            DescriptorPool::ErrorCollector::IMPORT,
+            "Files that do not use optimize_for = LITE_RUNTIME cannot import "
+            "files which do use this option.  This file is not lite, but it "
+            "imports \"" +
+                file->dependency(i)->name() + "\" which is.");
+        break;
+      }
+    }
+  }
+  if (file->syntax() == FileDescriptor::SYNTAX_PROTO3) {
+    ValidateProto3(file, proto);
+  }
+}
+
+void DescriptorBuilder::ValidateProto3(FileDescriptor* file,
+                                       const FileDescriptorProto& proto) {
+  for (int i = 0; i < file->extension_count(); ++i) {
+    ValidateProto3Field(file->extensions_ + i, proto.extension(i));
+  }
+  for (int i = 0; i < file->message_type_count(); ++i) {
+    ValidateProto3Message(file->message_types_ + i, proto.message_type(i));
+  }
+  for (int i = 0; i < file->enum_type_count(); ++i) {
+    ValidateProto3Enum(file->enum_types_ + i, proto.enum_type(i));
+  }
+}
+
+static std::string ToLowercaseWithoutUnderscores(const std::string& name) {
+  std::string result;
+  for (char character : name) {
+    if (character != '_') {
+      if (character >= 'A' && character <= 'Z') {
+        result.push_back(character - 'A' + 'a');
+      } else {
+        result.push_back(character);
+      }
+    }
+  }
+  return result;
+}
+
+void DescriptorBuilder::ValidateProto3Message(Descriptor* message,
+                                              const DescriptorProto& proto) {
+  for (int i = 0; i < message->nested_type_count(); ++i) {
+    ValidateProto3Message(message->nested_types_ + i, proto.nested_type(i));
+  }
+  for (int i = 0; i < message->enum_type_count(); ++i) {
+    ValidateProto3Enum(message->enum_types_ + i, proto.enum_type(i));
+  }
+  for (int i = 0; i < message->field_count(); ++i) {
+    ValidateProto3Field(message->fields_ + i, proto.field(i));
+  }
+  for (int i = 0; i < message->extension_count(); ++i) {
+    ValidateProto3Field(message->extensions_ + i, proto.extension(i));
+  }
+  if (message->extension_range_count() > 0) {
+    AddError(message->full_name(), proto.extension_range(0),
+             DescriptorPool::ErrorCollector::NUMBER,
+             "Extension ranges are not allowed in proto3.");
+  }
+  if (message->options().message_set_wire_format()) {
+    // Using MessageSet doesn't make sense since we disallow extensions.
+    AddError(message->full_name(), proto, DescriptorPool::ErrorCollector::NAME,
+             "MessageSet is not supported in proto3.");
+  }
+
+  // In proto3, we reject field names if they conflict in camelCase.
+  // Note that we currently enforce a stricter rule: Field names must be
+  // unique after being converted to lowercase with underscores removed.
+  std::map<std::string, const FieldDescriptor*> name_to_field;
+  for (int i = 0; i < message->field_count(); ++i) {
+    std::string lowercase_name =
+        ToLowercaseWithoutUnderscores(message->field(i)->name());
+    if (name_to_field.find(lowercase_name) != name_to_field.end()) {
+      AddError(message->full_name(), proto.field(i),
+               DescriptorPool::ErrorCollector::NAME,
+               "The JSON camel-case name of field \"" +
+                   message->field(i)->name() + "\" conflicts with field \"" +
+                   name_to_field[lowercase_name]->name() + "\". This is not " +
+                   "allowed in proto3.");
+    } else {
+      name_to_field[lowercase_name] = message->field(i);
+    }
+  }
+}
+
+void DescriptorBuilder::ValidateProto3Field(FieldDescriptor* field,
+                                            const FieldDescriptorProto& proto) {
+  if (field->is_extension() &&
+      !AllowedExtendeeInProto3(field->containing_type()->full_name())) {
+    AddError(field->full_name(), proto,
+             DescriptorPool::ErrorCollector::EXTENDEE,
+             "Extensions in proto3 are only allowed for defining options.");
+  }
+  if (field->is_required()) {
+    AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+             "Required fields are not allowed in proto3.");
+  }
+  if (field->has_default_value()) {
+    AddError(field->full_name(), proto,
+             DescriptorPool::ErrorCollector::DEFAULT_VALUE,
+             "Explicit default values are not allowed in proto3.");
+  }
+  if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
+      field->enum_type() &&
+      field->enum_type()->file()->syntax() != FileDescriptor::SYNTAX_PROTO3 &&
+      field->enum_type()->file()->syntax() != FileDescriptor::SYNTAX_UNKNOWN) {
+    // Proto3 messages can only use Proto3 enum types; otherwise we can't
+    // guarantee that the default value is zero.
+    AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+             "Enum type \"" + field->enum_type()->full_name() +
+                 "\" is not a proto3 enum, but is used in \"" +
+                 field->containing_type()->full_name() +
+                 "\" which is a proto3 message type.");
+  }
+  if (field->type() == FieldDescriptor::TYPE_GROUP) {
+    AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+             "Groups are not supported in proto3 syntax.");
+  }
+}
+
+void DescriptorBuilder::ValidateProto3Enum(EnumDescriptor* enm,
+                                           const EnumDescriptorProto& proto) {
+  if (enm->value_count() > 0 && enm->value(0)->number() != 0) {
+    AddError(enm->full_name(), proto.value(0),
+             DescriptorPool::ErrorCollector::NUMBER,
+             "The first enum value must be zero in proto3.");
+  }
+}
+
+void DescriptorBuilder::ValidateMessageOptions(Descriptor* message,
+                                               const DescriptorProto& proto) {
+  VALIDATE_OPTIONS_FROM_ARRAY(message, field, Field);
+  VALIDATE_OPTIONS_FROM_ARRAY(message, nested_type, Message);
+  VALIDATE_OPTIONS_FROM_ARRAY(message, enum_type, Enum);
+  VALIDATE_OPTIONS_FROM_ARRAY(message, extension, Field);
+
+  const int64_t max_extension_range =
+      static_cast<int64_t>(message->options().message_set_wire_format()
+                               ? std::numeric_limits<int32_t>::max()
+                               : FieldDescriptor::kMaxNumber);
+  for (int i = 0; i < message->extension_range_count(); ++i) {
+    if (message->extension_range(i)->end > max_extension_range + 1) {
+      AddError(message->full_name(), proto.extension_range(i),
+               DescriptorPool::ErrorCollector::NUMBER,
+               strings::Substitute("Extension numbers cannot be greater than $0.",
+                                max_extension_range));
+    }
+
+    ValidateExtensionRangeOptions(message->full_name(),
+                                  message->extension_ranges_ + i,
+                                  proto.extension_range(i));
+  }
+}
+
+
+void DescriptorBuilder::ValidateFieldOptions(
+    FieldDescriptor* field, const FieldDescriptorProto& proto) {
+  if (pool_->lazily_build_dependencies_ && (!field || !field->message_type())) {
+    return;
+  }
+  // Only message type fields may be lazy.
+  if (field->options().lazy() || field->options().unverified_lazy()) {
+    if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+      AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+               "[lazy = true] can only be specified for submessage fields.");
+    }
+  }
+
+  // Only repeated primitive fields may be packed.
+  if (field->options().packed() && !field->is_packable()) {
+    AddError(
+        field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+        "[packed = true] can only be specified for repeated primitive fields.");
+  }
+
+  // Note:  Default instance may not yet be initialized here, so we have to
+  //   avoid reading from it.
+  if (field->containing_type_ != nullptr &&
+      &field->containing_type()->options() !=
+          &MessageOptions::default_instance() &&
+      field->containing_type()->options().message_set_wire_format()) {
+    if (field->is_extension()) {
+      if (!field->is_optional() ||
+          field->type() != FieldDescriptor::TYPE_MESSAGE) {
+        AddError(field->full_name(), proto,
+                 DescriptorPool::ErrorCollector::TYPE,
+                 "Extensions of MessageSets must be optional messages.");
+      }
+    } else {
+      AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::NAME,
+               "MessageSets cannot have fields, only extensions.");
+    }
+  }
+
+  // Lite extensions can only be of Lite types.
+  if (IsLite(field->file()) && field->containing_type_ != nullptr &&
+      !IsLite(field->containing_type()->file())) {
+    AddError(field->full_name(), proto,
+             DescriptorPool::ErrorCollector::EXTENDEE,
+             "Extensions to non-lite types can only be declared in non-lite "
+             "files.  Note that you cannot extend a non-lite type to contain "
+             "a lite type, but the reverse is allowed.");
+  }
+
+  // Validate map types.
+  if (field->is_map()) {
+    if (!ValidateMapEntry(field, proto)) {
+      AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+               "map_entry should not be set explicitly. Use map<KeyType, "
+               "ValueType> instead.");
+    }
+  }
+
+  ValidateJSType(field, proto);
+
+  // json_name option is not allowed on extension fields. Note that the
+  // json_name field in FieldDescriptorProto is always populated by protoc
+  // when it sends descriptor data to plugins (calculated from field name if
+  // the option is not explicitly set) so we can't rely on its presence to
+  // determine whether the json_name option is set on the field. Here we
+  // compare it against the default calculated json_name value and consider
+  // the option set if they are different. This won't catch the case when
+  // an user explicitly sets json_name to the default value, but should be
+  // good enough to catch common misuses.
+  if (field->is_extension() &&
+      (field->has_json_name() &&
+       field->json_name() != ToJsonName(field->name()))) {
+    AddError(field->full_name(), proto,
+             DescriptorPool::ErrorCollector::OPTION_NAME,
+             "option json_name is not allowed on extension fields.");
+  }
+
+}
+
+void DescriptorBuilder::ValidateEnumOptions(EnumDescriptor* enm,
+                                            const EnumDescriptorProto& proto) {
+  VALIDATE_OPTIONS_FROM_ARRAY(enm, value, EnumValue);
+  if (!enm->options().has_allow_alias() || !enm->options().allow_alias()) {
+    std::map<int, std::string> used_values;
+    for (int i = 0; i < enm->value_count(); ++i) {
+      const EnumValueDescriptor* enum_value = enm->value(i);
+      if (used_values.find(enum_value->number()) != used_values.end()) {
+        std::string error =
+            "\"" + enum_value->full_name() +
+            "\" uses the same enum value as \"" +
+            used_values[enum_value->number()] +
+            "\". If this is intended, set "
+            "'option allow_alias = true;' to the enum definition.";
+        if (!enm->options().allow_alias()) {
+          // Generate error if duplicated enum values are explicitly disallowed.
+          AddError(enm->full_name(), proto.value(i),
+                   DescriptorPool::ErrorCollector::NUMBER, error);
+        }
+      } else {
+        used_values[enum_value->number()] = enum_value->full_name();
+      }
+    }
+  }
+}
+
+void DescriptorBuilder::ValidateEnumValueOptions(
+    EnumValueDescriptor* /* enum_value */,
+    const EnumValueDescriptorProto& /* proto */) {
+  // Nothing to do so far.
+}
+
+void DescriptorBuilder::ValidateExtensionRangeOptions(
+    const std::string& full_name, Descriptor::ExtensionRange* extension_range,
+    const DescriptorProto_ExtensionRange& proto) {
+  (void)full_name;        // Parameter is used by Google-internal code.
+  (void)extension_range;  // Parameter is used by Google-internal code.
+}
+
+void DescriptorBuilder::ValidateServiceOptions(
+    ServiceDescriptor* service, const ServiceDescriptorProto& proto) {
+  if (IsLite(service->file()) &&
+      (service->file()->options().cc_generic_services() ||
+       service->file()->options().java_generic_services())) {
+    AddError(service->full_name(), proto, DescriptorPool::ErrorCollector::NAME,
+             "Files with optimize_for = LITE_RUNTIME cannot define services "
+             "unless you set both options cc_generic_services and "
+             "java_generic_services to false.");
+  }
+
+  VALIDATE_OPTIONS_FROM_ARRAY(service, method, Method);
+}
+
+void DescriptorBuilder::ValidateMethodOptions(
+    MethodDescriptor* /* method */, const MethodDescriptorProto& /* proto */) {
+  // Nothing to do so far.
+}
+
+bool DescriptorBuilder::ValidateMapEntry(FieldDescriptor* field,
+                                         const FieldDescriptorProto& proto) {
+  const Descriptor* message = field->message_type();
+  if (  // Must not contain extensions, extension range or nested message or
+        // enums
+      message->extension_count() != 0 ||
+      field->label() != FieldDescriptor::LABEL_REPEATED ||
+      message->extension_range_count() != 0 ||
+      message->nested_type_count() != 0 || message->enum_type_count() != 0 ||
+      // Must contain exactly two fields
+      message->field_count() != 2 ||
+      // Field name and message name must match
+      message->name() != ToCamelCase(field->name(), false) + "Entry" ||
+      // Entry message must be in the same containing type of the field.
+      field->containing_type() != message->containing_type()) {
+    return false;
+  }
+
+  const FieldDescriptor* key = message->map_key();
+  const FieldDescriptor* value = message->map_value();
+  if (key->label() != FieldDescriptor::LABEL_OPTIONAL || key->number() != 1 ||
+      key->name() != "key") {
+    return false;
+  }
+  if (value->label() != FieldDescriptor::LABEL_OPTIONAL ||
+      value->number() != 2 || value->name() != "value") {
+    return false;
+  }
+
+  // Check key types are legal.
+  switch (key->type()) {
+    case FieldDescriptor::TYPE_ENUM:
+      AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+               "Key in map fields cannot be enum types.");
+      break;
+    case FieldDescriptor::TYPE_FLOAT:
+    case FieldDescriptor::TYPE_DOUBLE:
+    case FieldDescriptor::TYPE_MESSAGE:
+    case FieldDescriptor::TYPE_GROUP:
+    case FieldDescriptor::TYPE_BYTES:
+      AddError(
+          field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+          "Key in map fields cannot be float/double, bytes or message types.");
+      break;
+    case FieldDescriptor::TYPE_BOOL:
+    case FieldDescriptor::TYPE_INT32:
+    case FieldDescriptor::TYPE_INT64:
+    case FieldDescriptor::TYPE_SINT32:
+    case FieldDescriptor::TYPE_SINT64:
+    case FieldDescriptor::TYPE_STRING:
+    case FieldDescriptor::TYPE_UINT32:
+    case FieldDescriptor::TYPE_UINT64:
+    case FieldDescriptor::TYPE_FIXED32:
+    case FieldDescriptor::TYPE_FIXED64:
+    case FieldDescriptor::TYPE_SFIXED32:
+    case FieldDescriptor::TYPE_SFIXED64:
+      // Legal cases
+      break;
+      // Do not add a default, so that the compiler will complain when new types
+      // are added.
+  }
+
+  if (value->type() == FieldDescriptor::TYPE_ENUM) {
+    if (value->enum_type()->value(0)->number() != 0) {
+      AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+               "Enum value in map must define 0 as the first value.");
+    }
+  }
+
+  return true;
+}
+
+void DescriptorBuilder::DetectMapConflicts(const Descriptor* message,
+                                           const DescriptorProto& proto) {
+  std::map<std::string, const Descriptor*> seen_types;
+  for (int i = 0; i < message->nested_type_count(); ++i) {
+    const Descriptor* nested = message->nested_type(i);
+    std::pair<std::map<std::string, const Descriptor*>::iterator, bool> result =
+        seen_types.insert(std::make_pair(nested->name(), nested));
+    if (!result.second) {
+      if (result.first->second->options().map_entry() ||
+          nested->options().map_entry()) {
+        AddError(message->full_name(), proto,
+                 DescriptorPool::ErrorCollector::NAME,
+                 "Expanded map entry type " + nested->name() +
+                     " conflicts with an existing nested message type.");
+        break;
+      }
+    }
+    // Recursively test on the nested types.
+    DetectMapConflicts(message->nested_type(i), proto.nested_type(i));
+  }
+  // Check for conflicted field names.
+  for (int i = 0; i < message->field_count(); ++i) {
+    const FieldDescriptor* field = message->field(i);
+    std::map<std::string, const Descriptor*>::iterator iter =
+        seen_types.find(field->name());
+    if (iter != seen_types.end() && iter->second->options().map_entry()) {
+      AddError(message->full_name(), proto,
+               DescriptorPool::ErrorCollector::NAME,
+               "Expanded map entry type " + iter->second->name() +
+                   " conflicts with an existing field.");
+    }
+  }
+  // Check for conflicted enum names.
+  for (int i = 0; i < message->enum_type_count(); ++i) {
+    const EnumDescriptor* enum_desc = message->enum_type(i);
+    std::map<std::string, const Descriptor*>::iterator iter =
+        seen_types.find(enum_desc->name());
+    if (iter != seen_types.end() && iter->second->options().map_entry()) {
+      AddError(message->full_name(), proto,
+               DescriptorPool::ErrorCollector::NAME,
+               "Expanded map entry type " + iter->second->name() +
+                   " conflicts with an existing enum type.");
+    }
+  }
+  // Check for conflicted oneof names.
+  for (int i = 0; i < message->oneof_decl_count(); ++i) {
+    const OneofDescriptor* oneof_desc = message->oneof_decl(i);
+    std::map<std::string, const Descriptor*>::iterator iter =
+        seen_types.find(oneof_desc->name());
+    if (iter != seen_types.end() && iter->second->options().map_entry()) {
+      AddError(message->full_name(), proto,
+               DescriptorPool::ErrorCollector::NAME,
+               "Expanded map entry type " + iter->second->name() +
+                   " conflicts with an existing oneof type.");
+    }
+  }
+}
+
+void DescriptorBuilder::ValidateJSType(FieldDescriptor* field,
+                                       const FieldDescriptorProto& proto) {
+  FieldOptions::JSType jstype = field->options().jstype();
+  // The default is always acceptable.
+  if (jstype == FieldOptions::JS_NORMAL) {
+    return;
+  }
+
+  switch (field->type()) {
+    // Integral 64-bit types may be represented as JavaScript numbers or
+    // strings.
+    case FieldDescriptor::TYPE_UINT64:
+    case FieldDescriptor::TYPE_INT64:
+    case FieldDescriptor::TYPE_SINT64:
+    case FieldDescriptor::TYPE_FIXED64:
+    case FieldDescriptor::TYPE_SFIXED64:
+      if (jstype == FieldOptions::JS_STRING ||
+          jstype == FieldOptions::JS_NUMBER) {
+        return;
+      }
+      AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+               "Illegal jstype for int64, uint64, sint64, fixed64 "
+               "or sfixed64 field: " +
+                   FieldOptions_JSType_descriptor()->value(jstype)->name());
+      break;
+
+    // No other types permit a jstype option.
+    default:
+      AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
+               "jstype is only allowed on int64, uint64, sint64, fixed64 "
+               "or sfixed64 fields.");
+      break;
+  }
+}
+
+#undef VALIDATE_OPTIONS_FROM_ARRAY
+
+// -------------------------------------------------------------------
+
+DescriptorBuilder::OptionInterpreter::OptionInterpreter(
+    DescriptorBuilder* builder)
+    : builder_(builder) {
+  GOOGLE_CHECK(builder_);
+}
+
+DescriptorBuilder::OptionInterpreter::~OptionInterpreter() {}
+
+bool DescriptorBuilder::OptionInterpreter::InterpretOptions(
+    OptionsToInterpret* options_to_interpret) {
+  // Note that these may be in different pools, so we can't use the same
+  // descriptor and reflection objects on both.
+  Message* options = options_to_interpret->options;
+  const Message* original_options = options_to_interpret->original_options;
+
+  bool failed = false;
+  options_to_interpret_ = options_to_interpret;
+
+  // Find the uninterpreted_option field in the mutable copy of the options
+  // and clear them, since we're about to interpret them.
+  const FieldDescriptor* uninterpreted_options_field =
+      options->GetDescriptor()->FindFieldByName("uninterpreted_option");
+  GOOGLE_CHECK(uninterpreted_options_field != nullptr)
+      << "No field named \"uninterpreted_option\" in the Options proto.";
+  options->GetReflection()->ClearField(options, uninterpreted_options_field);
+
+  std::vector<int> src_path = options_to_interpret->element_path;
+  src_path.push_back(uninterpreted_options_field->number());
+
+  // Find the uninterpreted_option field in the original options.
+  const FieldDescriptor* original_uninterpreted_options_field =
+      original_options->GetDescriptor()->FindFieldByName(
+          "uninterpreted_option");
+  GOOGLE_CHECK(original_uninterpreted_options_field != nullptr)
+      << "No field named \"uninterpreted_option\" in the Options proto.";
+
+  const int num_uninterpreted_options =
+      original_options->GetReflection()->FieldSize(
+          *original_options, original_uninterpreted_options_field);
+  for (int i = 0; i < num_uninterpreted_options; ++i) {
+    src_path.push_back(i);
+    uninterpreted_option_ = down_cast<const UninterpretedOption*>(
+        &original_options->GetReflection()->GetRepeatedMessage(
+            *original_options, original_uninterpreted_options_field, i));
+    if (!InterpretSingleOption(options, src_path,
+                               options_to_interpret->element_path)) {
+      // Error already added by InterpretSingleOption().
+      failed = true;
+      break;
+    }
+    src_path.pop_back();
+  }
+  // Reset these, so we don't have any dangling pointers.
+  uninterpreted_option_ = nullptr;
+  options_to_interpret_ = nullptr;
+
+  if (!failed) {
+    // InterpretSingleOption() added the interpreted options in the
+    // UnknownFieldSet, in case the option isn't yet known to us.  Now we
+    // serialize the options message and deserialize it back.  That way, any
+    // option fields that we do happen to know about will get moved from the
+    // UnknownFieldSet into the real fields, and thus be available right away.
+    // If they are not known, that's OK too. They will get reparsed into the
+    // UnknownFieldSet and wait there until the message is parsed by something
+    // that does know about the options.
+
+    // Keep the unparsed options around in case the reparsing fails.
+    std::unique_ptr<Message> unparsed_options(options->New());
+    options->GetReflection()->Swap(unparsed_options.get(), options);
+
+    std::string buf;
+    if (!unparsed_options->AppendToString(&buf) ||
+        !options->ParseFromString(buf)) {
+      builder_->AddError(
+          options_to_interpret->element_name, *original_options,
+          DescriptorPool::ErrorCollector::OTHER,
+          "Some options could not be correctly parsed using the proto "
+          "descriptors compiled into this binary.\n"
+          "Unparsed options: " +
+              unparsed_options->ShortDebugString() +
+              "\n"
+              "Parsing attempt:  " +
+              options->ShortDebugString());
+      // Restore the unparsed options.
+      options->GetReflection()->Swap(unparsed_options.get(), options);
+    }
+  }
+
+  return !failed;
+}
+
+bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption(
+    Message* options, const std::vector<int>& src_path,
+    const std::vector<int>& options_path) {
+  // First do some basic validation.
+  if (uninterpreted_option_->name_size() == 0) {
+    // This should never happen unless the parser has gone seriously awry or
+    // someone has manually created the uninterpreted option badly.
+    return AddNameError("Option must have a name.");
+  }
+  if (uninterpreted_option_->name(0).name_part() == "uninterpreted_option") {
+    return AddNameError(
+        "Option must not use reserved name "
+        "\"uninterpreted_option\".");
+  }
+
+  const Descriptor* options_descriptor = nullptr;
+  // Get the options message's descriptor from the builder's pool, so that we
+  // get the version that knows about any extension options declared in the file
+  // we're currently building. The descriptor should be there as long as the
+  // file we're building imported descriptor.proto.
+
+  // Note that we use DescriptorBuilder::FindSymbolNotEnforcingDeps(), not
+  // DescriptorPool::FindMessageTypeByName() because we're already holding the
+  // pool's mutex, and the latter method locks it again.  We don't use
+  // FindSymbol() because files that use custom options only need to depend on
+  // the file that defines the option, not descriptor.proto itself.
+  Symbol symbol = builder_->FindSymbolNotEnforcingDeps(
+      options->GetDescriptor()->full_name());
+  options_descriptor = symbol.descriptor();
+  if (options_descriptor == nullptr) {
+    // The options message's descriptor was not in the builder's pool, so use
+    // the standard version from the generated pool. We're not holding the
+    // generated pool's mutex, so we can search it the straightforward way.
+    options_descriptor = options->GetDescriptor();
+  }
+  GOOGLE_CHECK(options_descriptor);
+
+  // We iterate over the name parts to drill into the submessages until we find
+  // the leaf field for the option. As we drill down we remember the current
+  // submessage's descriptor in |descriptor| and the next field in that
+  // submessage in |field|. We also track the fields we're drilling down
+  // through in |intermediate_fields|. As we go, we reconstruct the full option
+  // name in |debug_msg_name|, for use in error messages.
+  const Descriptor* descriptor = options_descriptor;
+  const FieldDescriptor* field = nullptr;
+  std::vector<const FieldDescriptor*> intermediate_fields;
+  std::string debug_msg_name = "";
+
+  std::vector<int> dest_path = options_path;
+
+  for (int i = 0; i < uninterpreted_option_->name_size(); ++i) {
+    builder_->undefine_resolved_name_.clear();
+    const std::string& name_part = uninterpreted_option_->name(i).name_part();
+    if (debug_msg_name.size() > 0) {
+      debug_msg_name += ".";
+    }
+    if (uninterpreted_option_->name(i).is_extension()) {
+      debug_msg_name += "(" + name_part + ")";
+      // Search for the extension's descriptor as an extension in the builder's
+      // pool. Note that we use DescriptorBuilder::LookupSymbol(), not
+      // DescriptorPool::FindExtensionByName(), for two reasons: 1) It allows
+      // relative lookups, and 2) because we're already holding the pool's
+      // mutex, and the latter method locks it again.
+      symbol =
+          builder_->LookupSymbol(name_part, options_to_interpret_->name_scope);
+      field = symbol.field_descriptor();
+      // If we don't find the field then the field's descriptor was not in the
+      // builder's pool, but there's no point in looking in the generated
+      // pool. We require that you import the file that defines any extensions
+      // you use, so they must be present in the builder's pool.
+    } else {
+      debug_msg_name += name_part;
+      // Search for the field's descriptor as a regular field.
+      field = descriptor->FindFieldByName(name_part);
+    }
+
+    if (field == nullptr) {
+      if (get_allow_unknown(builder_->pool_)) {
+        // We can't find the option, but AllowUnknownDependencies() is enabled,
+        // so we will just leave it as uninterpreted.
+        AddWithoutInterpreting(*uninterpreted_option_, options);
+        return true;
+      } else if (!(builder_->undefine_resolved_name_).empty()) {
+        // Option is resolved to a name which is not defined.
+        return AddNameError(
+            "Option \"" + debug_msg_name + "\" is resolved to \"(" +
+            builder_->undefine_resolved_name_ +
+            ")\", which is not defined. The innermost scope is searched first "
+            "in name resolution. Consider using a leading '.'(i.e., \"(." +
+            debug_msg_name.substr(1) +
+            "\") to start from the outermost scope.");
+      } else {
+        return AddNameError(
+            "Option \"" + debug_msg_name +
+            "\" unknown. Ensure that your proto" +
+            " definition file imports the proto which defines the option.");
+      }
+    } else if (field->containing_type() != descriptor) {
+      if (get_is_placeholder(field->containing_type())) {
+        // The field is an extension of a placeholder type, so we can't
+        // reliably verify whether it is a valid extension to use here (e.g.
+        // we don't know if it is an extension of the correct *Options message,
+        // or if it has a valid field number, etc.).  Just leave it as
+        // uninterpreted instead.
+        AddWithoutInterpreting(*uninterpreted_option_, options);
+        return true;
+      } else {
+        // This can only happen if, due to some insane misconfiguration of the
+        // pools, we find the options message in one pool but the field in
+        // another. This would probably imply a hefty bug somewhere.
+        return AddNameError("Option field \"" + debug_msg_name +
+                            "\" is not a field or extension of message \"" +
+                            descriptor->name() + "\".");
+      }
+    } else {
+      // accumulate field numbers to form path to interpreted option
+      dest_path.push_back(field->number());
+
+      if (i < uninterpreted_option_->name_size() - 1) {
+        if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+          return AddNameError("Option \"" + debug_msg_name +
+                              "\" is an atomic type, not a message.");
+        } else if (field->is_repeated()) {
+          return AddNameError("Option field \"" + debug_msg_name +
+                              "\" is a repeated message. Repeated message "
+                              "options must be initialized using an "
+                              "aggregate value.");
+        } else {
+          // Drill down into the submessage.
+          intermediate_fields.push_back(field);
+          descriptor = field->message_type();
+        }
+      }
+    }
+  }
+
+  // We've found the leaf field. Now we use UnknownFieldSets to set its value
+  // on the options message. We do so because the message may not yet know
+  // about its extension fields, so we may not be able to set the fields
+  // directly. But the UnknownFieldSets will serialize to the same wire-format
+  // message, so reading that message back in once the extension fields are
+  // known will populate them correctly.
+
+  // First see if the option is already set.
+  if (!field->is_repeated() &&
+      !ExamineIfOptionIsSet(
+          intermediate_fields.begin(), intermediate_fields.end(), field,
+          debug_msg_name,
+          options->GetReflection()->GetUnknownFields(*options))) {
+    return false;  // ExamineIfOptionIsSet() already added the error.
+  }
+
+  // First set the value on the UnknownFieldSet corresponding to the
+  // innermost message.
+  std::unique_ptr<UnknownFieldSet> unknown_fields(new UnknownFieldSet());
+  if (!SetOptionValue(field, unknown_fields.get())) {
+    return false;  // SetOptionValue() already added the error.
+  }
+
+  // Now wrap the UnknownFieldSet with UnknownFieldSets corresponding to all
+  // the intermediate messages.
+  for (std::vector<const FieldDescriptor*>::reverse_iterator iter =
+           intermediate_fields.rbegin();
+       iter != intermediate_fields.rend(); ++iter) {
+    std::unique_ptr<UnknownFieldSet> parent_unknown_fields(
+        new UnknownFieldSet());
+    switch ((*iter)->type()) {
+      case FieldDescriptor::TYPE_MESSAGE: {
+        std::string* outstr =
+            parent_unknown_fields->AddLengthDelimited((*iter)->number());
+        GOOGLE_CHECK(unknown_fields->SerializeToString(outstr))
+            << "Unexpected failure while serializing option submessage "
+            << debug_msg_name << "\".";
+        break;
+      }
+
+      case FieldDescriptor::TYPE_GROUP: {
+        parent_unknown_fields->AddGroup((*iter)->number())
+            ->MergeFrom(*unknown_fields);
+        break;
+      }
+
+      default:
+        GOOGLE_LOG(FATAL) << "Invalid wire type for CPPTYPE_MESSAGE: "
+                   << (*iter)->type();
+        return false;
+    }
+    unknown_fields.reset(parent_unknown_fields.release());
+  }
+
+  // Now merge the UnknownFieldSet corresponding to the top-level message into
+  // the options message.
+  options->GetReflection()->MutableUnknownFields(options)->MergeFrom(
+      *unknown_fields);
+
+  // record the element path of the interpreted option
+  if (field->is_repeated()) {
+    int index = repeated_option_counts_[dest_path]++;
+    dest_path.push_back(index);
+  }
+  interpreted_paths_[src_path] = dest_path;
+
+  return true;
+}
+
+void DescriptorBuilder::OptionInterpreter::UpdateSourceCodeInfo(
+    SourceCodeInfo* info) {
+  if (interpreted_paths_.empty()) {
+    // nothing to do!
+    return;
+  }
+
+  // We find locations that match keys in interpreted_paths_ and
+  // 1) replace the path with the corresponding value in interpreted_paths_
+  // 2) remove any subsequent sub-locations (sub-location is one whose path
+  //    has the parent path as a prefix)
+  //
+  // To avoid quadratic behavior of removing interior rows as we go,
+  // we keep a copy. But we don't actually copy anything until we've
+  // found the first match (so if the source code info has no locations
+  // that need to be changed, there is zero copy overhead).
+
+  RepeatedPtrField<SourceCodeInfo_Location>* locs = info->mutable_location();
+  RepeatedPtrField<SourceCodeInfo_Location> new_locs;
+  bool copying = false;
+
+  std::vector<int> pathv;
+  bool matched = false;
+
+  for (RepeatedPtrField<SourceCodeInfo_Location>::iterator loc = locs->begin();
+       loc != locs->end(); loc++) {
+    if (matched) {
+      // see if this location is in the range to remove
+      bool loc_matches = true;
+      if (loc->path_size() < static_cast<int64_t>(pathv.size())) {
+        loc_matches = false;
+      } else {
+        for (size_t j = 0; j < pathv.size(); j++) {
+          if (loc->path(j) != pathv[j]) {
+            loc_matches = false;
+            break;
+          }
+        }
+      }
+
+      if (loc_matches) {
+        // don't copy this row since it is a sub-location that we're removing
+        continue;
+      }
+
+      matched = false;
+    }
+
+    pathv.clear();
+    for (int j = 0; j < loc->path_size(); j++) {
+      pathv.push_back(loc->path(j));
+    }
+
+    std::map<std::vector<int>, std::vector<int>>::iterator entry =
+        interpreted_paths_.find(pathv);
+
+    if (entry == interpreted_paths_.end()) {
+      // not a match
+      if (copying) {
+        *new_locs.Add() = *loc;
+      }
+      continue;
+    }
+
+    matched = true;
+
+    if (!copying) {
+      // initialize the copy we are building
+      copying = true;
+      new_locs.Reserve(locs->size());
+      for (RepeatedPtrField<SourceCodeInfo_Location>::iterator it =
+               locs->begin();
+           it != loc; it++) {
+        *new_locs.Add() = *it;
+      }
+    }
+
+    // add replacement and update its path
+    SourceCodeInfo_Location* replacement = new_locs.Add();
+    *replacement = *loc;
+    replacement->clear_path();
+    for (std::vector<int>::iterator rit = entry->second.begin();
+         rit != entry->second.end(); rit++) {
+      replacement->add_path(*rit);
+    }
+  }
+
+  // if we made a changed copy, put it in place
+  if (copying) {
+    *locs = new_locs;
+  }
+}
+
+void DescriptorBuilder::OptionInterpreter::AddWithoutInterpreting(
+    const UninterpretedOption& uninterpreted_option, Message* options) {
+  const FieldDescriptor* field =
+      options->GetDescriptor()->FindFieldByName("uninterpreted_option");
+  GOOGLE_CHECK(field != nullptr);
+
+  options->GetReflection()
+      ->AddMessage(options, field)
+      ->CopyFrom(uninterpreted_option);
+}
+
+bool DescriptorBuilder::OptionInterpreter::ExamineIfOptionIsSet(
+    std::vector<const FieldDescriptor*>::const_iterator
+        intermediate_fields_iter,
+    std::vector<const FieldDescriptor*>::const_iterator intermediate_fields_end,
+    const FieldDescriptor* innermost_field, const std::string& debug_msg_name,
+    const UnknownFieldSet& unknown_fields) {
+  // We do linear searches of the UnknownFieldSet and its sub-groups.  This
+  // should be fine since it's unlikely that any one options structure will
+  // contain more than a handful of options.
+
+  if (intermediate_fields_iter == intermediate_fields_end) {
+    // We're at the innermost submessage.
+    for (int i = 0; i < unknown_fields.field_count(); i++) {
+      if (unknown_fields.field(i).number() == innermost_field->number()) {
+        return AddNameError("Option \"" + debug_msg_name +
+                            "\" was already set.");
+      }
+    }
+    return true;
+  }
+
+  for (int i = 0; i < unknown_fields.field_count(); i++) {
+    if (unknown_fields.field(i).number() ==
+        (*intermediate_fields_iter)->number()) {
+      const UnknownField* unknown_field = &unknown_fields.field(i);
+      FieldDescriptor::Type type = (*intermediate_fields_iter)->type();
+      // Recurse into the next submessage.
+      switch (type) {
+        case FieldDescriptor::TYPE_MESSAGE:
+          if (unknown_field->type() == UnknownField::TYPE_LENGTH_DELIMITED) {
+            UnknownFieldSet intermediate_unknown_fields;
+            if (intermediate_unknown_fields.ParseFromString(
+                    unknown_field->length_delimited()) &&
+                !ExamineIfOptionIsSet(intermediate_fields_iter + 1,
+                                      intermediate_fields_end, innermost_field,
+                                      debug_msg_name,
+                                      intermediate_unknown_fields)) {
+              return false;  // Error already added.
+            }
+          }
+          break;
+
+        case FieldDescriptor::TYPE_GROUP:
+          if (unknown_field->type() == UnknownField::TYPE_GROUP) {
+            if (!ExamineIfOptionIsSet(intermediate_fields_iter + 1,
+                                      intermediate_fields_end, innermost_field,
+                                      debug_msg_name, unknown_field->group())) {
+              return false;  // Error already added.
+            }
+          }
+          break;
+
+        default:
+          GOOGLE_LOG(FATAL) << "Invalid wire type for CPPTYPE_MESSAGE: " << type;
+          return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool DescriptorBuilder::OptionInterpreter::SetOptionValue(
+    const FieldDescriptor* option_field, UnknownFieldSet* unknown_fields) {
+  // We switch on the CppType to validate.
+  switch (option_field->cpp_type()) {
+    case FieldDescriptor::CPPTYPE_INT32:
+      if (uninterpreted_option_->has_positive_int_value()) {
+        if (uninterpreted_option_->positive_int_value() >
+            static_cast<uint64_t>(std::numeric_limits<int32_t>::max())) {
+          return AddValueError("Value out of range for int32 option \"" +
+                               option_field->full_name() + "\".");
+        } else {
+          SetInt32(option_field->number(),
+                   uninterpreted_option_->positive_int_value(),
+                   option_field->type(), unknown_fields);
+        }
+      } else if (uninterpreted_option_->has_negative_int_value()) {
+        if (uninterpreted_option_->negative_int_value() <
+            static_cast<int64_t>(std::numeric_limits<int32_t>::min())) {
+          return AddValueError("Value out of range for int32 option \"" +
+                               option_field->full_name() + "\".");
+        } else {
+          SetInt32(option_field->number(),
+                   uninterpreted_option_->negative_int_value(),
+                   option_field->type(), unknown_fields);
+        }
+      } else {
+        return AddValueError("Value must be integer for int32 option \"" +
+                             option_field->full_name() + "\".");
+      }
+      break;
+
+    case FieldDescriptor::CPPTYPE_INT64:
+      if (uninterpreted_option_->has_positive_int_value()) {
+        if (uninterpreted_option_->positive_int_value() >
+            static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+          return AddValueError("Value out of range for int64 option \"" +
+                               option_field->full_name() + "\".");
+        } else {
+          SetInt64(option_field->number(),
+                   uninterpreted_option_->positive_int_value(),
+                   option_field->type(), unknown_fields);
+        }
+      } else if (uninterpreted_option_->has_negative_int_value()) {
+        SetInt64(option_field->number(),
+                 uninterpreted_option_->negative_int_value(),
+                 option_field->type(), unknown_fields);
+      } else {
+        return AddValueError("Value must be integer for int64 option \"" +
+                             option_field->full_name() + "\".");
+      }
+      break;
+
+    case FieldDescriptor::CPPTYPE_UINT32:
+      if (uninterpreted_option_->has_positive_int_value()) {
+        if (uninterpreted_option_->positive_int_value() >
+            std::numeric_limits<uint32_t>::max()) {
+          return AddValueError("Value out of range for uint32 option \"" +
+                               option_field->name() + "\".");
+        } else {
+          SetUInt32(option_field->number(),
+                    uninterpreted_option_->positive_int_value(),
+                    option_field->type(), unknown_fields);
+        }
+      } else {
+        return AddValueError(
+            "Value must be non-negative integer for uint32 "
+            "option \"" +
+            option_field->full_name() + "\".");
+      }
+      break;
+
+    case FieldDescriptor::CPPTYPE_UINT64:
+      if (uninterpreted_option_->has_positive_int_value()) {
+        SetUInt64(option_field->number(),
+                  uninterpreted_option_->positive_int_value(),
+                  option_field->type(), unknown_fields);
+      } else {
+        return AddValueError(
+            "Value must be non-negative integer for uint64 "
+            "option \"" +
+            option_field->full_name() + "\".");
+      }
+      break;
+
+    case FieldDescriptor::CPPTYPE_FLOAT: {
+      float value;
+      if (uninterpreted_option_->has_double_value()) {
+        value = uninterpreted_option_->double_value();
+      } else if (uninterpreted_option_->has_positive_int_value()) {
+        value = uninterpreted_option_->positive_int_value();
+      } else if (uninterpreted_option_->has_negative_int_value()) {
+        value = uninterpreted_option_->negative_int_value();
+      } else {
+        return AddValueError("Value must be number for float option \"" +
+                             option_field->full_name() + "\".");
+      }
+      unknown_fields->AddFixed32(option_field->number(),
+                                 internal::WireFormatLite::EncodeFloat(value));
+      break;
+    }
+
+    case FieldDescriptor::CPPTYPE_DOUBLE: {
+      double value;
+      if (uninterpreted_option_->has_double_value()) {
+        value = uninterpreted_option_->double_value();
+      } else if (uninterpreted_option_->has_positive_int_value()) {
+        value = uninterpreted_option_->positive_int_value();
+      } else if (uninterpreted_option_->has_negative_int_value()) {
+        value = uninterpreted_option_->negative_int_value();
+      } else {
+        return AddValueError("Value must be number for double option \"" +
+                             option_field->full_name() + "\".");
+      }
+      unknown_fields->AddFixed64(option_field->number(),
+                                 internal::WireFormatLite::EncodeDouble(value));
+      break;
+    }
+
+    case FieldDescriptor::CPPTYPE_BOOL:
+      uint64_t value;
+      if (!uninterpreted_option_->has_identifier_value()) {
+        return AddValueError(
+            "Value must be identifier for boolean option "
+            "\"" +
+            option_field->full_name() + "\".");
+      }
+      if (uninterpreted_option_->identifier_value() == "true") {
+        value = 1;
+      } else if (uninterpreted_option_->identifier_value() == "false") {
+        value = 0;
+      } else {
+        return AddValueError(
+            "Value must be \"true\" or \"false\" for boolean "
+            "option \"" +
+            option_field->full_name() + "\".");
+      }
+      unknown_fields->AddVarint(option_field->number(), value);
+      break;
+
+    case FieldDescriptor::CPPTYPE_ENUM: {
+      if (!uninterpreted_option_->has_identifier_value()) {
+        return AddValueError(
+            "Value must be identifier for enum-valued option "
+            "\"" +
+            option_field->full_name() + "\".");
+      }
+      const EnumDescriptor* enum_type = option_field->enum_type();
+      const std::string& value_name = uninterpreted_option_->identifier_value();
+      const EnumValueDescriptor* enum_value = nullptr;
+
+      if (enum_type->file()->pool() != DescriptorPool::generated_pool()) {
+        // Note that the enum value's fully-qualified name is a sibling of the
+        // enum's name, not a child of it.
+        std::string fully_qualified_name = enum_type->full_name();
+        fully_qualified_name.resize(fully_qualified_name.size() -
+                                    enum_type->name().size());
+        fully_qualified_name += value_name;
+
+        // Search for the enum value's descriptor in the builder's pool. Note
+        // that we use DescriptorBuilder::FindSymbolNotEnforcingDeps(), not
+        // DescriptorPool::FindEnumValueByName() because we're already holding
+        // the pool's mutex, and the latter method locks it again.
+        Symbol symbol =
+            builder_->FindSymbolNotEnforcingDeps(fully_qualified_name);
+        if (auto* candicate_descriptor = symbol.enum_value_descriptor()) {
+          if (candicate_descriptor->type() != enum_type) {
+            return AddValueError(
+                "Enum type \"" + enum_type->full_name() +
+                "\" has no value named \"" + value_name + "\" for option \"" +
+                option_field->full_name() +
+                "\". This appears to be a value from a sibling type.");
+          } else {
+            enum_value = candicate_descriptor;
+          }
+        }
+      } else {
+        // The enum type is in the generated pool, so we can search for the
+        // value there.
+        enum_value = enum_type->FindValueByName(value_name);
+      }
+
+      if (enum_value == nullptr) {
+        return AddValueError("Enum type \"" +
+                             option_field->enum_type()->full_name() +
+                             "\" has no value named \"" + value_name +
+                             "\" for "
+                             "option \"" +
+                             option_field->full_name() + "\".");
+      } else {
+        // Sign-extension is not a problem, since we cast directly from int32_t
+        // to uint64_t, without first going through uint32_t.
+        unknown_fields->AddVarint(
+            option_field->number(),
+            static_cast<uint64_t>(static_cast<int64_t>(enum_value->number())));
+      }
+      break;
+    }
+
+    case FieldDescriptor::CPPTYPE_STRING:
+      if (!uninterpreted_option_->has_string_value()) {
+        return AddValueError(
+            "Value must be quoted string for string option "
+            "\"" +
+            option_field->full_name() + "\".");
+      }
+      // The string has already been unquoted and unescaped by the parser.
+      unknown_fields->AddLengthDelimited(option_field->number(),
+                                         uninterpreted_option_->string_value());
+      break;
+
+    case FieldDescriptor::CPPTYPE_MESSAGE:
+      if (!SetAggregateOption(option_field, unknown_fields)) {
+        return false;
+      }
+      break;
+  }
+
+  return true;
+}
+
+class DescriptorBuilder::OptionInterpreter::AggregateOptionFinder
+    : public TextFormat::Finder {
+ public:
+  DescriptorBuilder* builder_;
+
+  const Descriptor* FindAnyType(const Message& /*message*/,
+                                const std::string& prefix,
+                                const std::string& name) const override {
+    if (prefix != internal::kTypeGoogleApisComPrefix &&
+        prefix != internal::kTypeGoogleProdComPrefix) {
+      return nullptr;
+    }
+    assert_mutex_held(builder_->pool_);
+    return builder_->FindSymbol(name).descriptor();
+  }
+
+  const FieldDescriptor* FindExtension(Message* message,
+                                       const std::string& name) const override {
+    assert_mutex_held(builder_->pool_);
+    const Descriptor* descriptor = message->GetDescriptor();
+    Symbol result =
+        builder_->LookupSymbolNoPlaceholder(name, descriptor->full_name());
+    if (auto* field = result.field_descriptor()) {
+      return field;
+    } else if (result.type() == Symbol::MESSAGE &&
+               descriptor->options().message_set_wire_format()) {
+      const Descriptor* foreign_type = result.descriptor();
+      // The text format allows MessageSet items to be specified using
+      // the type name, rather than the extension identifier. If the symbol
+      // lookup returned a Message, and the enclosing Message has
+      // message_set_wire_format = true, then return the message set
+      // extension, if one exists.
+      for (int i = 0; i < foreign_type->extension_count(); i++) {
+        const FieldDescriptor* extension = foreign_type->extension(i);
+        if (extension->containing_type() == descriptor &&
+            extension->type() == FieldDescriptor::TYPE_MESSAGE &&
+            extension->is_optional() &&
+            extension->message_type() == foreign_type) {
+          // Found it.
+          return extension;
+        }
+      }
+    }
+    return nullptr;
+  }
+};
+
+// A custom error collector to record any text-format parsing errors
+namespace {
+class AggregateErrorCollector : public io::ErrorCollector {
+ public:
+  std::string error_;
+
+  void AddError(int /* line */, int /* column */,
+                const std::string& message) override {
+    if (!error_.empty()) {
+      error_ += "; ";
+    }
+    error_ += message;
+  }
+
+  void AddWarning(int /* line */, int /* column */,
+                  const std::string& /* message */) override {
+    // Ignore warnings
+  }
+};
+}  // namespace
+
+// We construct a dynamic message of the type corresponding to
+// option_field, parse the supplied text-format string into this
+// message, and serialize the resulting message to produce the value.
+bool DescriptorBuilder::OptionInterpreter::SetAggregateOption(
+    const FieldDescriptor* option_field, UnknownFieldSet* unknown_fields) {
+  if (!uninterpreted_option_->has_aggregate_value()) {
+    return AddValueError("Option \"" + option_field->full_name() +
+                         "\" is a message. To set the entire message, use "
+                         "syntax like \"" +
+                         option_field->name() +
+                         " = { <proto text format> }\". "
+                         "To set fields within it, use "
+                         "syntax like \"" +
+                         option_field->name() + ".foo = value\".");
+  }
+
+  const Descriptor* type = option_field->message_type();
+  std::unique_ptr<Message> dynamic(dynamic_factory_.GetPrototype(type)->New());
+  GOOGLE_CHECK(dynamic.get() != nullptr)
+      << "Could not create an instance of " << option_field->DebugString();
+
+  AggregateErrorCollector collector;
+  AggregateOptionFinder finder;
+  finder.builder_ = builder_;
+  TextFormat::Parser parser;
+  parser.RecordErrorsTo(&collector);
+  parser.SetFinder(&finder);
+  if (!parser.ParseFromString(uninterpreted_option_->aggregate_value(),
+                              dynamic.get())) {
+    AddValueError("Error while parsing option value for \"" +
+                  option_field->name() + "\": " + collector.error_);
+    return false;
+  } else {
+    std::string serial;
+    dynamic->SerializeToString(&serial);  // Never fails
+    if (option_field->type() == FieldDescriptor::TYPE_MESSAGE) {
+      unknown_fields->AddLengthDelimited(option_field->number(), serial);
+    } else {
+      GOOGLE_CHECK_EQ(option_field->type(), FieldDescriptor::TYPE_GROUP);
+      UnknownFieldSet* group = unknown_fields->AddGroup(option_field->number());
+      group->ParseFromString(serial);
+    }
+    return true;
+  }
+}
+
+void DescriptorBuilder::OptionInterpreter::SetInt32(
+    int number, int32_t value, FieldDescriptor::Type type,
+    UnknownFieldSet* unknown_fields) {
+  switch (type) {
+    case FieldDescriptor::TYPE_INT32:
+      unknown_fields->AddVarint(
+          number, static_cast<uint64_t>(static_cast<int64_t>(value)));
+      break;
+
+    case FieldDescriptor::TYPE_SFIXED32:
+      unknown_fields->AddFixed32(number, static_cast<uint32_t>(value));
+      break;
+
+    case FieldDescriptor::TYPE_SINT32:
+      unknown_fields->AddVarint(
+          number, internal::WireFormatLite::ZigZagEncode32(value));
+      break;
+
+    default:
+      GOOGLE_LOG(FATAL) << "Invalid wire type for CPPTYPE_INT32: " << type;
+      break;
+  }
+}
+
+void DescriptorBuilder::OptionInterpreter::SetInt64(
+    int number, int64_t value, FieldDescriptor::Type type,
+    UnknownFieldSet* unknown_fields) {
+  switch (type) {
+    case FieldDescriptor::TYPE_INT64:
+      unknown_fields->AddVarint(number, static_cast<uint64_t>(value));
+      break;
+
+    case FieldDescriptor::TYPE_SFIXED64:
+      unknown_fields->AddFixed64(number, static_cast<uint64_t>(value));
+      break;
+
+    case FieldDescriptor::TYPE_SINT64:
+      unknown_fields->AddVarint(
+          number, internal::WireFormatLite::ZigZagEncode64(value));
+      break;
+
+    default:
+      GOOGLE_LOG(FATAL) << "Invalid wire type for CPPTYPE_INT64: " << type;
+      break;
+  }
+}
+
+void DescriptorBuilder::OptionInterpreter::SetUInt32(
+    int number, uint32_t value, FieldDescriptor::Type type,
+    UnknownFieldSet* unknown_fields) {
+  switch (type) {
+    case FieldDescriptor::TYPE_UINT32:
+      unknown_fields->AddVarint(number, static_cast<uint64_t>(value));
+      break;
+
+    case FieldDescriptor::TYPE_FIXED32:
+      unknown_fields->AddFixed32(number, static_cast<uint32_t>(value));
+      break;
+
+    default:
+      GOOGLE_LOG(FATAL) << "Invalid wire type for CPPTYPE_UINT32: " << type;
+      break;
+  }
+}
+
+void DescriptorBuilder::OptionInterpreter::SetUInt64(
+    int number, uint64_t value, FieldDescriptor::Type type,
+    UnknownFieldSet* unknown_fields) {
+  switch (type) {
+    case FieldDescriptor::TYPE_UINT64:
+      unknown_fields->AddVarint(number, value);
+      break;
+
+    case FieldDescriptor::TYPE_FIXED64:
+      unknown_fields->AddFixed64(number, value);
+      break;
+
+    default:
+      GOOGLE_LOG(FATAL) << "Invalid wire type for CPPTYPE_UINT64: " << type;
+      break;
+  }
+}
+
+void DescriptorBuilder::LogUnusedDependency(const FileDescriptorProto& proto,
+                                            const FileDescriptor* result) {
+  (void)result;  // Parameter is used by Google-internal code.
+
+  if (!unused_dependency_.empty()) {
+    auto itr = pool_->unused_import_track_files_.find(proto.name());
+    bool is_error =
+        itr != pool_->unused_import_track_files_.end() && itr->second;
+    for (std::set<const FileDescriptor*>::const_iterator it =
+             unused_dependency_.begin();
+         it != unused_dependency_.end(); ++it) {
+      std::string error_message = "Import " + (*it)->name() + " is unused.";
+      if (is_error) {
+        AddError((*it)->name(), proto, DescriptorPool::ErrorCollector::IMPORT,
+                 error_message);
+      } else {
+        AddWarning((*it)->name(), proto, DescriptorPool::ErrorCollector::IMPORT,
+                   error_message);
+      }
+    }
+  }
+}
+
+Symbol DescriptorPool::CrossLinkOnDemandHelper(StringPiece name,
+                                               bool expecting_enum) const {
+  (void)expecting_enum;  // Parameter is used by Google-internal code.
+  auto lookup_name = std::string(name);
+  if (!lookup_name.empty() && lookup_name[0] == '.') {
+    lookup_name = lookup_name.substr(1);
+  }
+  Symbol result = tables_->FindByNameHelper(this, lookup_name);
+  return result;
+}
+
+// Handle the lazy import building for a message field whose type wasn't built
+// at cross link time. If that was the case, we saved the name of the type to
+// be looked up when the accessor for the type was called. Set type_,
+// enum_type_, message_type_, and default_value_enum_ appropriately.
+void FieldDescriptor::InternalTypeOnceInit() const {
+  GOOGLE_CHECK(file()->finished_building_ == true);
+  const EnumDescriptor* enum_type = nullptr;
+  const char* lazy_type_name = reinterpret_cast<const char*>(type_once_ + 1);
+  const char* lazy_default_value_enum_name =
+      lazy_type_name + strlen(lazy_type_name) + 1;
+  Symbol result = file()->pool()->CrossLinkOnDemandHelper(
+      lazy_type_name, type_ == FieldDescriptor::TYPE_ENUM);
+  if (result.type() == Symbol::MESSAGE) {
+    type_ = FieldDescriptor::TYPE_MESSAGE;
+    type_descriptor_.message_type = result.descriptor();
+  } else if (result.type() == Symbol::ENUM) {
+    type_ = FieldDescriptor::TYPE_ENUM;
+    enum_type = type_descriptor_.enum_type = result.enum_descriptor();
+  }
+
+  if (enum_type) {
+    if (lazy_default_value_enum_name[0] != '\0') {
+      // Have to build the full name now instead of at CrossLink time,
+      // because enum_type may not be known at the time.
+      std::string name = enum_type->full_name();
+      // Enum values reside in the same scope as the enum type.
+      std::string::size_type last_dot = name.find_last_of('.');
+      if (last_dot != std::string::npos) {
+        name = name.substr(0, last_dot) + "." + lazy_default_value_enum_name;
+      } else {
+        name = lazy_default_value_enum_name;
+      }
+      Symbol result = file()->pool()->CrossLinkOnDemandHelper(name, true);
+      default_value_enum_ = result.enum_value_descriptor();
+    } else {
+      default_value_enum_ = nullptr;
+    }
+    if (!default_value_enum_) {
+      // We use the first defined value as the default
+      // if a default is not explicitly defined.
+      GOOGLE_CHECK(enum_type->value_count());
+      default_value_enum_ = enum_type->value(0);
+    }
+  }
+}
+
+void FieldDescriptor::TypeOnceInit(const FieldDescriptor* to_init) {
+  to_init->InternalTypeOnceInit();
+}
+
+// message_type(), enum_type(), default_value_enum(), and type()
+// all share the same internal::call_once init path to do lazy
+// import building and cross linking of a field of a message.
+const Descriptor* FieldDescriptor::message_type() const {
+  if (type_once_) {
+    internal::call_once(*type_once_, FieldDescriptor::TypeOnceInit, this);
+  }
+  return type_ == TYPE_MESSAGE || type_ == TYPE_GROUP
+             ? type_descriptor_.message_type
+             : nullptr;
+}
+
+const EnumDescriptor* FieldDescriptor::enum_type() const {
+  if (type_once_) {
+    internal::call_once(*type_once_, FieldDescriptor::TypeOnceInit, this);
+  }
+  return type_ == TYPE_ENUM ? type_descriptor_.enum_type : nullptr;
+}
+
+const EnumValueDescriptor* FieldDescriptor::default_value_enum() const {
+  if (type_once_) {
+    internal::call_once(*type_once_, FieldDescriptor::TypeOnceInit, this);
+  }
+  return default_value_enum_;
+}
+
+const std::string& FieldDescriptor::PrintableNameForExtension() const {
+  const bool is_message_set_extension =
+      is_extension() &&
+      containing_type()->options().message_set_wire_format() &&
+      type() == FieldDescriptor::TYPE_MESSAGE && is_optional() &&
+      extension_scope() == message_type();
+  return is_message_set_extension ? message_type()->full_name() : full_name();
+}
+
+void FileDescriptor::InternalDependenciesOnceInit() const {
+  GOOGLE_CHECK(finished_building_ == true);
+  const char* names_ptr = reinterpret_cast<const char*>(dependencies_once_ + 1);
+  for (int i = 0; i < dependency_count(); i++) {
+    const char* name = names_ptr;
+    names_ptr += strlen(name) + 1;
+    if (name[0] != '\0') {
+      dependencies_[i] = pool_->FindFileByName(name);
+    }
+  }
+}
+
+void FileDescriptor::DependenciesOnceInit(const FileDescriptor* to_init) {
+  to_init->InternalDependenciesOnceInit();
+}
+
+const FileDescriptor* FileDescriptor::dependency(int index) const {
+  if (dependencies_once_) {
+    // Do once init for all indices, as it's unlikely only a single index would
+    // be called, and saves on internal::call_once allocations.
+    internal::call_once(*dependencies_once_,
+                        FileDescriptor::DependenciesOnceInit, this);
+  }
+  return dependencies_[index];
+}
+
+const Descriptor* MethodDescriptor::input_type() const {
+  return input_type_.Get(service());
+}
+
+const Descriptor* MethodDescriptor::output_type() const {
+  return output_type_.Get(service());
+}
+
+namespace internal {
+void LazyDescriptor::Set(const Descriptor* descriptor) {
+  GOOGLE_CHECK(!once_);
+  descriptor_ = descriptor;
+}
+
+void LazyDescriptor::SetLazy(StringPiece name,
+                             const FileDescriptor* file) {
+  // verify Init() has been called and Set hasn't been called yet.
+  GOOGLE_CHECK(!descriptor_);
+  GOOGLE_CHECK(!once_);
+  GOOGLE_CHECK(file && file->pool_);
+  GOOGLE_CHECK(file->pool_->lazily_build_dependencies_);
+  GOOGLE_CHECK(!file->finished_building_);
+  once_ = ::new (file->pool_->tables_->AllocateBytes(static_cast<int>(
+      sizeof(internal::once_flag) + name.size() + 1))) internal::once_flag{};
+  char* lazy_name = reinterpret_cast<char*>(once_ + 1);
+  memcpy(lazy_name, name.data(), name.size());
+  lazy_name[name.size()] = 0;
+}
+
+void LazyDescriptor::Once(const ServiceDescriptor* service) {
+  if (once_) {
+    internal::call_once(*once_, [&] {
+      auto* file = service->file();
+      GOOGLE_CHECK(file->finished_building_);
+      const char* lazy_name = reinterpret_cast<const char*>(once_ + 1);
+      descriptor_ =
+          file->pool_->CrossLinkOnDemandHelper(lazy_name, false).descriptor();
+    });
+  }
+}
+
+}  // namespace internal
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/descriptor.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/descriptor.pb.cpp
new file mode 100644
index 0000000..d3bfb46
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/descriptor.pb.cpp
@@ -0,0 +1,11351 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/descriptor.proto
+
+#include <google/protobuf/descriptor.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR FileDescriptorSet::FileDescriptorSet(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.file_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct FileDescriptorSetDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR FileDescriptorSetDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~FileDescriptorSetDefaultTypeInternal() {}
+  union {
+    FileDescriptorSet _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FileDescriptorSetDefaultTypeInternal _FileDescriptorSet_default_instance_;
+PROTOBUF_CONSTEXPR FileDescriptorProto::FileDescriptorProto(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.dependency_)*/{}
+  , /*decltype(_impl_.message_type_)*/{}
+  , /*decltype(_impl_.enum_type_)*/{}
+  , /*decltype(_impl_.service_)*/{}
+  , /*decltype(_impl_.extension_)*/{}
+  , /*decltype(_impl_.public_dependency_)*/{}
+  , /*decltype(_impl_.weak_dependency_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.package_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.syntax_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.options_)*/nullptr
+  , /*decltype(_impl_.source_code_info_)*/nullptr} {}
+struct FileDescriptorProtoDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR FileDescriptorProtoDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~FileDescriptorProtoDefaultTypeInternal() {}
+  union {
+    FileDescriptorProto _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FileDescriptorProtoDefaultTypeInternal _FileDescriptorProto_default_instance_;
+PROTOBUF_CONSTEXPR DescriptorProto_ExtensionRange::DescriptorProto_ExtensionRange(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.options_)*/nullptr
+  , /*decltype(_impl_.start_)*/0
+  , /*decltype(_impl_.end_)*/0} {}
+struct DescriptorProto_ExtensionRangeDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR DescriptorProto_ExtensionRangeDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~DescriptorProto_ExtensionRangeDefaultTypeInternal() {}
+  union {
+    DescriptorProto_ExtensionRange _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 DescriptorProto_ExtensionRangeDefaultTypeInternal _DescriptorProto_ExtensionRange_default_instance_;
+PROTOBUF_CONSTEXPR DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.start_)*/0
+  , /*decltype(_impl_.end_)*/0} {}
+struct DescriptorProto_ReservedRangeDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR DescriptorProto_ReservedRangeDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~DescriptorProto_ReservedRangeDefaultTypeInternal() {}
+  union {
+    DescriptorProto_ReservedRange _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 DescriptorProto_ReservedRangeDefaultTypeInternal _DescriptorProto_ReservedRange_default_instance_;
+PROTOBUF_CONSTEXPR DescriptorProto::DescriptorProto(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.field_)*/{}
+  , /*decltype(_impl_.nested_type_)*/{}
+  , /*decltype(_impl_.enum_type_)*/{}
+  , /*decltype(_impl_.extension_range_)*/{}
+  , /*decltype(_impl_.extension_)*/{}
+  , /*decltype(_impl_.oneof_decl_)*/{}
+  , /*decltype(_impl_.reserved_range_)*/{}
+  , /*decltype(_impl_.reserved_name_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.options_)*/nullptr} {}
+struct DescriptorProtoDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR DescriptorProtoDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~DescriptorProtoDefaultTypeInternal() {}
+  union {
+    DescriptorProto _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 DescriptorProtoDefaultTypeInternal _DescriptorProto_default_instance_;
+PROTOBUF_CONSTEXPR ExtensionRangeOptions::ExtensionRangeOptions(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._extensions_)*/{}
+  , /*decltype(_impl_.uninterpreted_option_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct ExtensionRangeOptionsDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR ExtensionRangeOptionsDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~ExtensionRangeOptionsDefaultTypeInternal() {}
+  union {
+    ExtensionRangeOptions _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ExtensionRangeOptionsDefaultTypeInternal _ExtensionRangeOptions_default_instance_;
+PROTOBUF_CONSTEXPR FieldDescriptorProto::FieldDescriptorProto(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.extendee_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.type_name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.default_value_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.json_name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.options_)*/nullptr
+  , /*decltype(_impl_.number_)*/0
+  , /*decltype(_impl_.oneof_index_)*/0
+  , /*decltype(_impl_.proto3_optional_)*/false
+  , /*decltype(_impl_.label_)*/1
+  , /*decltype(_impl_.type_)*/1} {}
+struct FieldDescriptorProtoDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR FieldDescriptorProtoDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~FieldDescriptorProtoDefaultTypeInternal() {}
+  union {
+    FieldDescriptorProto _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FieldDescriptorProtoDefaultTypeInternal _FieldDescriptorProto_default_instance_;
+PROTOBUF_CONSTEXPR OneofDescriptorProto::OneofDescriptorProto(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.options_)*/nullptr} {}
+struct OneofDescriptorProtoDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR OneofDescriptorProtoDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~OneofDescriptorProtoDefaultTypeInternal() {}
+  union {
+    OneofDescriptorProto _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 OneofDescriptorProtoDefaultTypeInternal _OneofDescriptorProto_default_instance_;
+PROTOBUF_CONSTEXPR EnumDescriptorProto_EnumReservedRange::EnumDescriptorProto_EnumReservedRange(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.start_)*/0
+  , /*decltype(_impl_.end_)*/0} {}
+struct EnumDescriptorProto_EnumReservedRangeDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR EnumDescriptorProto_EnumReservedRangeDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~EnumDescriptorProto_EnumReservedRangeDefaultTypeInternal() {}
+  union {
+    EnumDescriptorProto_EnumReservedRange _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 EnumDescriptorProto_EnumReservedRangeDefaultTypeInternal _EnumDescriptorProto_EnumReservedRange_default_instance_;
+PROTOBUF_CONSTEXPR EnumDescriptorProto::EnumDescriptorProto(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.value_)*/{}
+  , /*decltype(_impl_.reserved_range_)*/{}
+  , /*decltype(_impl_.reserved_name_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.options_)*/nullptr} {}
+struct EnumDescriptorProtoDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR EnumDescriptorProtoDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~EnumDescriptorProtoDefaultTypeInternal() {}
+  union {
+    EnumDescriptorProto _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 EnumDescriptorProtoDefaultTypeInternal _EnumDescriptorProto_default_instance_;
+PROTOBUF_CONSTEXPR EnumValueDescriptorProto::EnumValueDescriptorProto(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.options_)*/nullptr
+  , /*decltype(_impl_.number_)*/0} {}
+struct EnumValueDescriptorProtoDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR EnumValueDescriptorProtoDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~EnumValueDescriptorProtoDefaultTypeInternal() {}
+  union {
+    EnumValueDescriptorProto _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 EnumValueDescriptorProtoDefaultTypeInternal _EnumValueDescriptorProto_default_instance_;
+PROTOBUF_CONSTEXPR ServiceDescriptorProto::ServiceDescriptorProto(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.method_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.options_)*/nullptr} {}
+struct ServiceDescriptorProtoDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR ServiceDescriptorProtoDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~ServiceDescriptorProtoDefaultTypeInternal() {}
+  union {
+    ServiceDescriptorProto _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ServiceDescriptorProtoDefaultTypeInternal _ServiceDescriptorProto_default_instance_;
+PROTOBUF_CONSTEXPR MethodDescriptorProto::MethodDescriptorProto(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.input_type_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.output_type_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.options_)*/nullptr
+  , /*decltype(_impl_.client_streaming_)*/false
+  , /*decltype(_impl_.server_streaming_)*/false} {}
+struct MethodDescriptorProtoDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR MethodDescriptorProtoDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~MethodDescriptorProtoDefaultTypeInternal() {}
+  union {
+    MethodDescriptorProto _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 MethodDescriptorProtoDefaultTypeInternal _MethodDescriptorProto_default_instance_;
+PROTOBUF_CONSTEXPR FileOptions::FileOptions(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._extensions_)*/{}
+  , /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.uninterpreted_option_)*/{}
+  , /*decltype(_impl_.java_package_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.java_outer_classname_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.go_package_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.objc_class_prefix_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.csharp_namespace_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.swift_prefix_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.php_class_prefix_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.php_namespace_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.php_metadata_namespace_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.ruby_package_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.java_multiple_files_)*/false
+  , /*decltype(_impl_.java_generate_equals_and_hash_)*/false
+  , /*decltype(_impl_.java_string_check_utf8_)*/false
+  , /*decltype(_impl_.cc_generic_services_)*/false
+  , /*decltype(_impl_.java_generic_services_)*/false
+  , /*decltype(_impl_.py_generic_services_)*/false
+  , /*decltype(_impl_.php_generic_services_)*/false
+  , /*decltype(_impl_.deprecated_)*/false
+  , /*decltype(_impl_.optimize_for_)*/1
+  , /*decltype(_impl_.cc_enable_arenas_)*/true} {}
+struct FileOptionsDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR FileOptionsDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~FileOptionsDefaultTypeInternal() {}
+  union {
+    FileOptions _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FileOptionsDefaultTypeInternal _FileOptions_default_instance_;
+PROTOBUF_CONSTEXPR MessageOptions::MessageOptions(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._extensions_)*/{}
+  , /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.uninterpreted_option_)*/{}
+  , /*decltype(_impl_.message_set_wire_format_)*/false
+  , /*decltype(_impl_.no_standard_descriptor_accessor_)*/false
+  , /*decltype(_impl_.deprecated_)*/false
+  , /*decltype(_impl_.map_entry_)*/false} {}
+struct MessageOptionsDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR MessageOptionsDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~MessageOptionsDefaultTypeInternal() {}
+  union {
+    MessageOptions _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 MessageOptionsDefaultTypeInternal _MessageOptions_default_instance_;
+PROTOBUF_CONSTEXPR FieldOptions::FieldOptions(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._extensions_)*/{}
+  , /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.uninterpreted_option_)*/{}
+  , /*decltype(_impl_.ctype_)*/0
+  , /*decltype(_impl_.jstype_)*/0
+  , /*decltype(_impl_.packed_)*/false
+  , /*decltype(_impl_.lazy_)*/false
+  , /*decltype(_impl_.unverified_lazy_)*/false
+  , /*decltype(_impl_.deprecated_)*/false
+  , /*decltype(_impl_.weak_)*/false} {}
+struct FieldOptionsDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR FieldOptionsDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~FieldOptionsDefaultTypeInternal() {}
+  union {
+    FieldOptions _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FieldOptionsDefaultTypeInternal _FieldOptions_default_instance_;
+PROTOBUF_CONSTEXPR OneofOptions::OneofOptions(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._extensions_)*/{}
+  , /*decltype(_impl_.uninterpreted_option_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct OneofOptionsDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR OneofOptionsDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~OneofOptionsDefaultTypeInternal() {}
+  union {
+    OneofOptions _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 OneofOptionsDefaultTypeInternal _OneofOptions_default_instance_;
+PROTOBUF_CONSTEXPR EnumOptions::EnumOptions(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._extensions_)*/{}
+  , /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.uninterpreted_option_)*/{}
+  , /*decltype(_impl_.allow_alias_)*/false
+  , /*decltype(_impl_.deprecated_)*/false} {}
+struct EnumOptionsDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR EnumOptionsDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~EnumOptionsDefaultTypeInternal() {}
+  union {
+    EnumOptions _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 EnumOptionsDefaultTypeInternal _EnumOptions_default_instance_;
+PROTOBUF_CONSTEXPR EnumValueOptions::EnumValueOptions(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._extensions_)*/{}
+  , /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.uninterpreted_option_)*/{}
+  , /*decltype(_impl_.deprecated_)*/false} {}
+struct EnumValueOptionsDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR EnumValueOptionsDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~EnumValueOptionsDefaultTypeInternal() {}
+  union {
+    EnumValueOptions _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 EnumValueOptionsDefaultTypeInternal _EnumValueOptions_default_instance_;
+PROTOBUF_CONSTEXPR ServiceOptions::ServiceOptions(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._extensions_)*/{}
+  , /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.uninterpreted_option_)*/{}
+  , /*decltype(_impl_.deprecated_)*/false} {}
+struct ServiceOptionsDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR ServiceOptionsDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~ServiceOptionsDefaultTypeInternal() {}
+  union {
+    ServiceOptions _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ServiceOptionsDefaultTypeInternal _ServiceOptions_default_instance_;
+PROTOBUF_CONSTEXPR MethodOptions::MethodOptions(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._extensions_)*/{}
+  , /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.uninterpreted_option_)*/{}
+  , /*decltype(_impl_.deprecated_)*/false
+  , /*decltype(_impl_.idempotency_level_)*/0} {}
+struct MethodOptionsDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR MethodOptionsDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~MethodOptionsDefaultTypeInternal() {}
+  union {
+    MethodOptions _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 MethodOptionsDefaultTypeInternal _MethodOptions_default_instance_;
+PROTOBUF_CONSTEXPR UninterpretedOption_NamePart::UninterpretedOption_NamePart(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.name_part_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.is_extension_)*/false} {}
+struct UninterpretedOption_NamePartDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR UninterpretedOption_NamePartDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~UninterpretedOption_NamePartDefaultTypeInternal() {}
+  union {
+    UninterpretedOption_NamePart _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 UninterpretedOption_NamePartDefaultTypeInternal _UninterpretedOption_NamePart_default_instance_;
+PROTOBUF_CONSTEXPR UninterpretedOption::UninterpretedOption(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.name_)*/{}
+  , /*decltype(_impl_.identifier_value_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.string_value_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.aggregate_value_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.positive_int_value_)*/uint64_t{0u}
+  , /*decltype(_impl_.negative_int_value_)*/int64_t{0}
+  , /*decltype(_impl_.double_value_)*/0} {}
+struct UninterpretedOptionDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR UninterpretedOptionDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~UninterpretedOptionDefaultTypeInternal() {}
+  union {
+    UninterpretedOption _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 UninterpretedOptionDefaultTypeInternal _UninterpretedOption_default_instance_;
+PROTOBUF_CONSTEXPR SourceCodeInfo_Location::SourceCodeInfo_Location(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.path_)*/{}
+  , /*decltype(_impl_._path_cached_byte_size_)*/{0}
+  , /*decltype(_impl_.span_)*/{}
+  , /*decltype(_impl_._span_cached_byte_size_)*/{0}
+  , /*decltype(_impl_.leading_detached_comments_)*/{}
+  , /*decltype(_impl_.leading_comments_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.trailing_comments_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}} {}
+struct SourceCodeInfo_LocationDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR SourceCodeInfo_LocationDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~SourceCodeInfo_LocationDefaultTypeInternal() {}
+  union {
+    SourceCodeInfo_Location _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SourceCodeInfo_LocationDefaultTypeInternal _SourceCodeInfo_Location_default_instance_;
+PROTOBUF_CONSTEXPR SourceCodeInfo::SourceCodeInfo(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.location_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct SourceCodeInfoDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR SourceCodeInfoDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~SourceCodeInfoDefaultTypeInternal() {}
+  union {
+    SourceCodeInfo _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SourceCodeInfoDefaultTypeInternal _SourceCodeInfo_default_instance_;
+PROTOBUF_CONSTEXPR GeneratedCodeInfo_Annotation::GeneratedCodeInfo_Annotation(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_._has_bits_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_.path_)*/{}
+  , /*decltype(_impl_._path_cached_byte_size_)*/{0}
+  , /*decltype(_impl_.source_file_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.begin_)*/0
+  , /*decltype(_impl_.end_)*/0} {}
+struct GeneratedCodeInfo_AnnotationDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR GeneratedCodeInfo_AnnotationDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~GeneratedCodeInfo_AnnotationDefaultTypeInternal() {}
+  union {
+    GeneratedCodeInfo_Annotation _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 GeneratedCodeInfo_AnnotationDefaultTypeInternal _GeneratedCodeInfo_Annotation_default_instance_;
+PROTOBUF_CONSTEXPR GeneratedCodeInfo::GeneratedCodeInfo(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.annotation_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct GeneratedCodeInfoDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR GeneratedCodeInfoDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~GeneratedCodeInfoDefaultTypeInternal() {}
+  union {
+    GeneratedCodeInfo _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 GeneratedCodeInfoDefaultTypeInternal _GeneratedCodeInfo_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[27];
+static const ::_pb::EnumDescriptor* file_level_enum_descriptors_google_2fprotobuf_2fdescriptor_2eproto[6];
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2fdescriptor_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2fdescriptor_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorSet, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorSet, _impl_.file_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.package_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.dependency_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.public_dependency_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.weak_dependency_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.message_type_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.enum_type_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.service_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.extension_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.options_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.source_code_info_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto, _impl_.syntax_),
+  0,
+  1,
+  ~0u,
+  ~0u,
+  ~0u,
+  ~0u,
+  ~0u,
+  ~0u,
+  ~0u,
+  3,
+  4,
+  2,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange, _impl_.start_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange, _impl_.end_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange, _impl_.options_),
+  1,
+  2,
+  0,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange, _impl_.start_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange, _impl_.end_),
+  0,
+  1,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_.field_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_.extension_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_.nested_type_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_.enum_type_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_.extension_range_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_.oneof_decl_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_.options_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_.reserved_range_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DescriptorProto, _impl_.reserved_name_),
+  0,
+  ~0u,
+  ~0u,
+  ~0u,
+  ~0u,
+  ~0u,
+  ~0u,
+  1,
+  ~0u,
+  ~0u,
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions, _internal_metadata_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions, _impl_._extensions_),
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions, _impl_.uninterpreted_option_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.number_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.label_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.type_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.type_name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.extendee_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.default_value_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.oneof_index_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.json_name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.options_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto, _impl_.proto3_optional_),
+  0,
+  6,
+  9,
+  10,
+  2,
+  1,
+  3,
+  7,
+  4,
+  5,
+  8,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto, _impl_.options_),
+  0,
+  1,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange, _impl_.start_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange, _impl_.end_),
+  0,
+  1,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto, _impl_.value_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto, _impl_.options_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto, _impl_.reserved_range_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto, _impl_.reserved_name_),
+  0,
+  ~0u,
+  1,
+  ~0u,
+  ~0u,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto, _impl_.number_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto, _impl_.options_),
+  0,
+  2,
+  1,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto, _impl_.method_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto, _impl_.options_),
+  0,
+  ~0u,
+  1,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto, _impl_.input_type_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto, _impl_.output_type_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto, _impl_.options_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto, _impl_.client_streaming_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto, _impl_.server_streaming_),
+  0,
+  1,
+  2,
+  3,
+  4,
+  5,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _internal_metadata_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_._extensions_),
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.java_package_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.java_outer_classname_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.java_multiple_files_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.java_generate_equals_and_hash_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.java_string_check_utf8_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.optimize_for_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.go_package_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.cc_generic_services_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.java_generic_services_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.py_generic_services_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.php_generic_services_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.deprecated_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.cc_enable_arenas_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.objc_class_prefix_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.csharp_namespace_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.swift_prefix_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.php_class_prefix_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.php_namespace_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.php_metadata_namespace_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.ruby_package_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FileOptions, _impl_.uninterpreted_option_),
+  0,
+  1,
+  10,
+  11,
+  12,
+  18,
+  2,
+  13,
+  14,
+  15,
+  16,
+  17,
+  19,
+  3,
+  4,
+  5,
+  6,
+  7,
+  8,
+  9,
+  ~0u,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MessageOptions, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MessageOptions, _internal_metadata_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MessageOptions, _impl_._extensions_),
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MessageOptions, _impl_.message_set_wire_format_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MessageOptions, _impl_.no_standard_descriptor_accessor_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MessageOptions, _impl_.deprecated_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MessageOptions, _impl_.map_entry_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MessageOptions, _impl_.uninterpreted_option_),
+  0,
+  1,
+  2,
+  3,
+  ~0u,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _internal_metadata_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _impl_._extensions_),
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _impl_.ctype_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _impl_.packed_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _impl_.jstype_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _impl_.lazy_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _impl_.unverified_lazy_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _impl_.deprecated_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _impl_.weak_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldOptions, _impl_.uninterpreted_option_),
+  0,
+  2,
+  1,
+  3,
+  4,
+  5,
+  6,
+  ~0u,
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::OneofOptions, _internal_metadata_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::OneofOptions, _impl_._extensions_),
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::OneofOptions, _impl_.uninterpreted_option_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumOptions, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumOptions, _internal_metadata_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumOptions, _impl_._extensions_),
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumOptions, _impl_.allow_alias_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumOptions, _impl_.deprecated_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumOptions, _impl_.uninterpreted_option_),
+  0,
+  1,
+  ~0u,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValueOptions, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValueOptions, _internal_metadata_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValueOptions, _impl_._extensions_),
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValueOptions, _impl_.deprecated_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValueOptions, _impl_.uninterpreted_option_),
+  0,
+  ~0u,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ServiceOptions, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ServiceOptions, _internal_metadata_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ServiceOptions, _impl_._extensions_),
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ServiceOptions, _impl_.deprecated_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ServiceOptions, _impl_.uninterpreted_option_),
+  0,
+  ~0u,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodOptions, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodOptions, _internal_metadata_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodOptions, _impl_._extensions_),
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodOptions, _impl_.deprecated_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodOptions, _impl_.idempotency_level_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::MethodOptions, _impl_.uninterpreted_option_),
+  0,
+  1,
+  ~0u,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart, _impl_.name_part_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart, _impl_.is_extension_),
+  0,
+  1,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption, _impl_.identifier_value_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption, _impl_.positive_int_value_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption, _impl_.negative_int_value_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption, _impl_.double_value_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption, _impl_.string_value_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UninterpretedOption, _impl_.aggregate_value_),
+  ~0u,
+  0,
+  3,
+  4,
+  5,
+  1,
+  2,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location, _impl_.path_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location, _impl_.span_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location, _impl_.leading_comments_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location, _impl_.trailing_comments_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location, _impl_.leading_detached_comments_),
+  ~0u,
+  ~0u,
+  0,
+  1,
+  ~0u,
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo, _impl_.location_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation, _impl_._has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation, _impl_.path_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation, _impl_.source_file_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation, _impl_.begin_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation, _impl_.end_),
+  ~0u,
+  0,
+  1,
+  2,
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo, _impl_.annotation_),
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::FileDescriptorSet)},
+  { 7, 25, -1, sizeof(::PROTOBUF_NAMESPACE_ID::FileDescriptorProto)},
+  { 37, 46, -1, sizeof(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange)},
+  { 49, 57, -1, sizeof(::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange)},
+  { 59, 75, -1, sizeof(::PROTOBUF_NAMESPACE_ID::DescriptorProto)},
+  { 85, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions)},
+  { 92, 109, -1, sizeof(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto)},
+  { 120, 128, -1, sizeof(::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto)},
+  { 130, 138, -1, sizeof(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange)},
+  { 140, 151, -1, sizeof(::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto)},
+  { 156, 165, -1, sizeof(::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto)},
+  { 168, 177, -1, sizeof(::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto)},
+  { 180, 192, -1, sizeof(::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto)},
+  { 198, 225, -1, sizeof(::PROTOBUF_NAMESPACE_ID::FileOptions)},
+  { 246, 257, -1, sizeof(::PROTOBUF_NAMESPACE_ID::MessageOptions)},
+  { 262, 276, -1, sizeof(::PROTOBUF_NAMESPACE_ID::FieldOptions)},
+  { 284, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::OneofOptions)},
+  { 291, 300, -1, sizeof(::PROTOBUF_NAMESPACE_ID::EnumOptions)},
+  { 303, 311, -1, sizeof(::PROTOBUF_NAMESPACE_ID::EnumValueOptions)},
+  { 313, 321, -1, sizeof(::PROTOBUF_NAMESPACE_ID::ServiceOptions)},
+  { 323, 332, -1, sizeof(::PROTOBUF_NAMESPACE_ID::MethodOptions)},
+  { 335, 343, -1, sizeof(::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart)},
+  { 345, 358, -1, sizeof(::PROTOBUF_NAMESPACE_ID::UninterpretedOption)},
+  { 365, 376, -1, sizeof(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location)},
+  { 381, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::SourceCodeInfo)},
+  { 388, 398, -1, sizeof(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation)},
+  { 402, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_FileDescriptorSet_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_FileDescriptorProto_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_DescriptorProto_ExtensionRange_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_DescriptorProto_ReservedRange_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_DescriptorProto_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_ExtensionRangeOptions_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_FieldDescriptorProto_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_OneofDescriptorProto_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_EnumDescriptorProto_EnumReservedRange_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_EnumDescriptorProto_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_EnumValueDescriptorProto_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_ServiceDescriptorProto_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_MethodDescriptorProto_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_FileOptions_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_MessageOptions_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_FieldOptions_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_OneofOptions_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_EnumOptions_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_EnumValueOptions_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_ServiceOptions_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_MethodOptions_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_UninterpretedOption_NamePart_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_UninterpretedOption_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_SourceCodeInfo_Location_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_SourceCodeInfo_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_GeneratedCodeInfo_Annotation_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_GeneratedCodeInfo_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2fdescriptor_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n google/protobuf/descriptor.proto\022\017goog"
+  "le.protobuf\"G\n\021FileDescriptorSet\0222\n\004file"
+  "\030\001 \003(\0132$.google.protobuf.FileDescriptorP"
+  "roto\"\333\003\n\023FileDescriptorProto\022\014\n\004name\030\001 \001"
+  "(\t\022\017\n\007package\030\002 \001(\t\022\022\n\ndependency\030\003 \003(\t\022"
+  "\031\n\021public_dependency\030\n \003(\005\022\027\n\017weak_depen"
+  "dency\030\013 \003(\005\0226\n\014message_type\030\004 \003(\0132 .goog"
+  "le.protobuf.DescriptorProto\0227\n\tenum_type"
+  "\030\005 \003(\0132$.google.protobuf.EnumDescriptorP"
+  "roto\0228\n\007service\030\006 \003(\0132\'.google.protobuf."
+  "ServiceDescriptorProto\0228\n\textension\030\007 \003("
+  "\0132%.google.protobuf.FieldDescriptorProto"
+  "\022-\n\007options\030\010 \001(\0132\034.google.protobuf.File"
+  "Options\0229\n\020source_code_info\030\t \001(\0132\037.goog"
+  "le.protobuf.SourceCodeInfo\022\016\n\006syntax\030\014 \001"
+  "(\t\"\251\005\n\017DescriptorProto\022\014\n\004name\030\001 \001(\t\0224\n\005"
+  "field\030\002 \003(\0132%.google.protobuf.FieldDescr"
+  "iptorProto\0228\n\textension\030\006 \003(\0132%.google.p"
+  "rotobuf.FieldDescriptorProto\0225\n\013nested_t"
+  "ype\030\003 \003(\0132 .google.protobuf.DescriptorPr"
+  "oto\0227\n\tenum_type\030\004 \003(\0132$.google.protobuf"
+  ".EnumDescriptorProto\022H\n\017extension_range\030"
+  "\005 \003(\0132/.google.protobuf.DescriptorProto."
+  "ExtensionRange\0229\n\noneof_decl\030\010 \003(\0132%.goo"
+  "gle.protobuf.OneofDescriptorProto\0220\n\007opt"
+  "ions\030\007 \001(\0132\037.google.protobuf.MessageOpti"
+  "ons\022F\n\016reserved_range\030\t \003(\0132..google.pro"
+  "tobuf.DescriptorProto.ReservedRange\022\025\n\rr"
+  "eserved_name\030\n \003(\t\032e\n\016ExtensionRange\022\r\n\005"
+  "start\030\001 \001(\005\022\013\n\003end\030\002 \001(\005\0227\n\007options\030\003 \001("
+  "\0132&.google.protobuf.ExtensionRangeOption"
+  "s\032+\n\rReservedRange\022\r\n\005start\030\001 \001(\005\022\013\n\003end"
+  "\030\002 \001(\005\"g\n\025ExtensionRangeOptions\022C\n\024unint"
+  "erpreted_option\030\347\007 \003(\0132$.google.protobuf"
+  ".UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"\325\005\n\024Fiel"
+  "dDescriptorProto\022\014\n\004name\030\001 \001(\t\022\016\n\006number"
+  "\030\003 \001(\005\022:\n\005label\030\004 \001(\0162+.google.protobuf."
+  "FieldDescriptorProto.Label\0228\n\004type\030\005 \001(\016"
+  "2*.google.protobuf.FieldDescriptorProto."
+  "Type\022\021\n\ttype_name\030\006 \001(\t\022\020\n\010extendee\030\002 \001("
+  "\t\022\025\n\rdefault_value\030\007 \001(\t\022\023\n\013oneof_index\030"
+  "\t \001(\005\022\021\n\tjson_name\030\n \001(\t\022.\n\007options\030\010 \001("
+  "\0132\035.google.protobuf.FieldOptions\022\027\n\017prot"
+  "o3_optional\030\021 \001(\010\"\266\002\n\004Type\022\017\n\013TYPE_DOUBL"
+  "E\020\001\022\016\n\nTYPE_FLOAT\020\002\022\016\n\nTYPE_INT64\020\003\022\017\n\013T"
+  "YPE_UINT64\020\004\022\016\n\nTYPE_INT32\020\005\022\020\n\014TYPE_FIX"
+  "ED64\020\006\022\020\n\014TYPE_FIXED32\020\007\022\r\n\tTYPE_BOOL\020\010\022"
+  "\017\n\013TYPE_STRING\020\t\022\016\n\nTYPE_GROUP\020\n\022\020\n\014TYPE"
+  "_MESSAGE\020\013\022\016\n\nTYPE_BYTES\020\014\022\017\n\013TYPE_UINT3"
+  "2\020\r\022\r\n\tTYPE_ENUM\020\016\022\021\n\rTYPE_SFIXED32\020\017\022\021\n"
+  "\rTYPE_SFIXED64\020\020\022\017\n\013TYPE_SINT32\020\021\022\017\n\013TYP"
+  "E_SINT64\020\022\"C\n\005Label\022\022\n\016LABEL_OPTIONAL\020\001\022"
+  "\022\n\016LABEL_REQUIRED\020\002\022\022\n\016LABEL_REPEATED\020\003\""
+  "T\n\024OneofDescriptorProto\022\014\n\004name\030\001 \001(\t\022.\n"
+  "\007options\030\002 \001(\0132\035.google.protobuf.OneofOp"
+  "tions\"\244\002\n\023EnumDescriptorProto\022\014\n\004name\030\001 "
+  "\001(\t\0228\n\005value\030\002 \003(\0132).google.protobuf.Enu"
+  "mValueDescriptorProto\022-\n\007options\030\003 \001(\0132\034"
+  ".google.protobuf.EnumOptions\022N\n\016reserved"
+  "_range\030\004 \003(\01326.google.protobuf.EnumDescr"
+  "iptorProto.EnumReservedRange\022\025\n\rreserved"
+  "_name\030\005 \003(\t\032/\n\021EnumReservedRange\022\r\n\005star"
+  "t\030\001 \001(\005\022\013\n\003end\030\002 \001(\005\"l\n\030EnumValueDescrip"
+  "torProto\022\014\n\004name\030\001 \001(\t\022\016\n\006number\030\002 \001(\005\0222"
+  "\n\007options\030\003 \001(\0132!.google.protobuf.EnumVa"
+  "lueOptions\"\220\001\n\026ServiceDescriptorProto\022\014\n"
+  "\004name\030\001 \001(\t\0226\n\006method\030\002 \003(\0132&.google.pro"
+  "tobuf.MethodDescriptorProto\0220\n\007options\030\003"
+  " \001(\0132\037.google.protobuf.ServiceOptions\"\301\001"
+  "\n\025MethodDescriptorProto\022\014\n\004name\030\001 \001(\t\022\022\n"
+  "\ninput_type\030\002 \001(\t\022\023\n\013output_type\030\003 \001(\t\022/"
+  "\n\007options\030\004 \001(\0132\036.google.protobuf.Method"
+  "Options\022\037\n\020client_streaming\030\005 \001(\010:\005false"
+  "\022\037\n\020server_streaming\030\006 \001(\010:\005false\"\245\006\n\013Fi"
+  "leOptions\022\024\n\014java_package\030\001 \001(\t\022\034\n\024java_"
+  "outer_classname\030\010 \001(\t\022\"\n\023java_multiple_f"
+  "iles\030\n \001(\010:\005false\022)\n\035java_generate_equal"
+  "s_and_hash\030\024 \001(\010B\002\030\001\022%\n\026java_string_chec"
+  "k_utf8\030\033 \001(\010:\005false\022F\n\014optimize_for\030\t \001("
+  "\0162).google.protobuf.FileOptions.Optimize"
+  "Mode:\005SPEED\022\022\n\ngo_package\030\013 \001(\t\022\"\n\023cc_ge"
+  "neric_services\030\020 \001(\010:\005false\022$\n\025java_gene"
+  "ric_services\030\021 \001(\010:\005false\022\"\n\023py_generic_"
+  "services\030\022 \001(\010:\005false\022#\n\024php_generic_ser"
+  "vices\030* \001(\010:\005false\022\031\n\ndeprecated\030\027 \001(\010:\005"
+  "false\022\036\n\020cc_enable_arenas\030\037 \001(\010:\004true\022\031\n"
+  "\021objc_class_prefix\030$ \001(\t\022\030\n\020csharp_names"
+  "pace\030% \001(\t\022\024\n\014swift_prefix\030\' \001(\t\022\030\n\020php_"
+  "class_prefix\030( \001(\t\022\025\n\rphp_namespace\030) \001("
+  "\t\022\036\n\026php_metadata_namespace\030, \001(\t\022\024\n\014rub"
+  "y_package\030- \001(\t\022C\n\024uninterpreted_option\030"
+  "\347\007 \003(\0132$.google.protobuf.UninterpretedOp"
+  "tion\":\n\014OptimizeMode\022\t\n\005SPEED\020\001\022\r\n\tCODE_"
+  "SIZE\020\002\022\020\n\014LITE_RUNTIME\020\003*\t\010\350\007\020\200\200\200\200\002J\004\010&\020"
+  "\'\"\204\002\n\016MessageOptions\022&\n\027message_set_wire"
+  "_format\030\001 \001(\010:\005false\022.\n\037no_standard_desc"
+  "riptor_accessor\030\002 \001(\010:\005false\022\031\n\ndeprecat"
+  "ed\030\003 \001(\010:\005false\022\021\n\tmap_entry\030\007 \001(\010\022C\n\024un"
+  "interpreted_option\030\347\007 \003(\0132$.google.proto"
+  "buf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002J\004\010\004\020\005"
+  "J\004\010\005\020\006J\004\010\006\020\007J\004\010\010\020\tJ\004\010\t\020\n\"\276\003\n\014FieldOption"
+  "s\022:\n\005ctype\030\001 \001(\0162#.google.protobuf.Field"
+  "Options.CType:\006STRING\022\016\n\006packed\030\002 \001(\010\022\?\n"
+  "\006jstype\030\006 \001(\0162$.google.protobuf.FieldOpt"
+  "ions.JSType:\tJS_NORMAL\022\023\n\004lazy\030\005 \001(\010:\005fa"
+  "lse\022\036\n\017unverified_lazy\030\017 \001(\010:\005false\022\031\n\nd"
+  "eprecated\030\003 \001(\010:\005false\022\023\n\004weak\030\n \001(\010:\005fa"
+  "lse\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.goo"
+  "gle.protobuf.UninterpretedOption\"/\n\005CTyp"
+  "e\022\n\n\006STRING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020"
+  "\002\"5\n\006JSType\022\r\n\tJS_NORMAL\020\000\022\r\n\tJS_STRING\020"
+  "\001\022\r\n\tJS_NUMBER\020\002*\t\010\350\007\020\200\200\200\200\002J\004\010\004\020\005\"^\n\014One"
+  "ofOptions\022C\n\024uninterpreted_option\030\347\007 \003(\013"
+  "2$.google.protobuf.UninterpretedOption*\t"
+  "\010\350\007\020\200\200\200\200\002\"\223\001\n\013EnumOptions\022\023\n\013allow_alias"
+  "\030\002 \001(\010\022\031\n\ndeprecated\030\003 \001(\010:\005false\022C\n\024uni"
+  "nterpreted_option\030\347\007 \003(\0132$.google.protob"
+  "uf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002J\004\010\005\020\006\""
+  "}\n\020EnumValueOptions\022\031\n\ndeprecated\030\001 \001(\010:"
+  "\005false\022C\n\024uninterpreted_option\030\347\007 \003(\0132$."
+  "google.protobuf.UninterpretedOption*\t\010\350\007"
+  "\020\200\200\200\200\002\"{\n\016ServiceOptions\022\031\n\ndeprecated\030!"
+  " \001(\010:\005false\022C\n\024uninterpreted_option\030\347\007 \003"
+  "(\0132$.google.protobuf.UninterpretedOption"
+  "*\t\010\350\007\020\200\200\200\200\002\"\255\002\n\rMethodOptions\022\031\n\ndepreca"
+  "ted\030! \001(\010:\005false\022_\n\021idempotency_level\030\" "
+  "\001(\0162/.google.protobuf.MethodOptions.Idem"
+  "potencyLevel:\023IDEMPOTENCY_UNKNOWN\022C\n\024uni"
+  "nterpreted_option\030\347\007 \003(\0132$.google.protob"
+  "uf.UninterpretedOption\"P\n\020IdempotencyLev"
+  "el\022\027\n\023IDEMPOTENCY_UNKNOWN\020\000\022\023\n\017NO_SIDE_E"
+  "FFECTS\020\001\022\016\n\nIDEMPOTENT\020\002*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023"
+  "UninterpretedOption\022;\n\004name\030\002 \003(\0132-.goog"
+  "le.protobuf.UninterpretedOption.NamePart"
+  "\022\030\n\020identifier_value\030\003 \001(\t\022\032\n\022positive_i"
+  "nt_value\030\004 \001(\004\022\032\n\022negative_int_value\030\005 \001"
+  "(\003\022\024\n\014double_value\030\006 \001(\001\022\024\n\014string_value"
+  "\030\007 \001(\014\022\027\n\017aggregate_value\030\010 \001(\t\0323\n\010NameP"
+  "art\022\021\n\tname_part\030\001 \002(\t\022\024\n\014is_extension\030\002"
+  " \002(\010\"\325\001\n\016SourceCodeInfo\022:\n\010location\030\001 \003("
+  "\0132(.google.protobuf.SourceCodeInfo.Locat"
+  "ion\032\206\001\n\010Location\022\020\n\004path\030\001 \003(\005B\002\020\001\022\020\n\004sp"
+  "an\030\002 \003(\005B\002\020\001\022\030\n\020leading_comments\030\003 \001(\t\022\031"
+  "\n\021trailing_comments\030\004 \001(\t\022!\n\031leading_det"
+  "ached_comments\030\006 \003(\t\"\247\001\n\021GeneratedCodeIn"
+  "fo\022A\n\nannotation\030\001 \003(\0132-.google.protobuf"
+  ".GeneratedCodeInfo.Annotation\032O\n\nAnnotat"
+  "ion\022\020\n\004path\030\001 \003(\005B\002\020\001\022\023\n\013source_file\030\002 \001"
+  "(\t\022\r\n\005begin\030\003 \001(\005\022\013\n\003end\030\004 \001(\005B~\n\023com.go"
+  "ogle.protobufB\020DescriptorProtosH\001Z-googl"
+  "e.golang.org/protobuf/types/descriptorpb"
+  "\370\001\001\242\002\003GPB\252\002\032Google.Protobuf.Reflection"
+  ;
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fdescriptor_2eproto = {
+    false, false, 6078, descriptor_table_protodef_google_2fprotobuf_2fdescriptor_2eproto,
+    "google/protobuf/descriptor.proto",
+    &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once, nullptr, 0, 27,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2fdescriptor_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto, file_level_enum_descriptors_google_2fprotobuf_2fdescriptor_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2fdescriptor_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2fdescriptor_2eproto(&descriptor_table_google_2fprotobuf_2fdescriptor_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* FieldDescriptorProto_Type_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2fdescriptor_2eproto);
+  return file_level_enum_descriptors_google_2fprotobuf_2fdescriptor_2eproto[0];
+}
+bool FieldDescriptorProto_Type_IsValid(int value) {
+  switch (value) {
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+    case 8:
+    case 9:
+    case 10:
+    case 11:
+    case 12:
+    case 13:
+    case 14:
+    case 15:
+    case 16:
+    case 17:
+    case 18:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_DOUBLE;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_FLOAT;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_INT64;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_UINT64;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_INT32;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_FIXED64;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_FIXED32;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_BOOL;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_STRING;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_GROUP;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_MESSAGE;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_BYTES;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_UINT32;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_ENUM;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SFIXED32;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SFIXED64;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SINT32;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SINT64;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::Type_MIN;
+constexpr FieldDescriptorProto_Type FieldDescriptorProto::Type_MAX;
+constexpr int FieldDescriptorProto::Type_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* FieldDescriptorProto_Label_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2fdescriptor_2eproto);
+  return file_level_enum_descriptors_google_2fprotobuf_2fdescriptor_2eproto[1];
+}
+bool FieldDescriptorProto_Label_IsValid(int value) {
+  switch (value) {
+    case 1:
+    case 2:
+    case 3:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr FieldDescriptorProto_Label FieldDescriptorProto::LABEL_OPTIONAL;
+constexpr FieldDescriptorProto_Label FieldDescriptorProto::LABEL_REQUIRED;
+constexpr FieldDescriptorProto_Label FieldDescriptorProto::LABEL_REPEATED;
+constexpr FieldDescriptorProto_Label FieldDescriptorProto::Label_MIN;
+constexpr FieldDescriptorProto_Label FieldDescriptorProto::Label_MAX;
+constexpr int FieldDescriptorProto::Label_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* FileOptions_OptimizeMode_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2fdescriptor_2eproto);
+  return file_level_enum_descriptors_google_2fprotobuf_2fdescriptor_2eproto[2];
+}
+bool FileOptions_OptimizeMode_IsValid(int value) {
+  switch (value) {
+    case 1:
+    case 2:
+    case 3:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr FileOptions_OptimizeMode FileOptions::SPEED;
+constexpr FileOptions_OptimizeMode FileOptions::CODE_SIZE;
+constexpr FileOptions_OptimizeMode FileOptions::LITE_RUNTIME;
+constexpr FileOptions_OptimizeMode FileOptions::OptimizeMode_MIN;
+constexpr FileOptions_OptimizeMode FileOptions::OptimizeMode_MAX;
+constexpr int FileOptions::OptimizeMode_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* FieldOptions_CType_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2fdescriptor_2eproto);
+  return file_level_enum_descriptors_google_2fprotobuf_2fdescriptor_2eproto[3];
+}
+bool FieldOptions_CType_IsValid(int value) {
+  switch (value) {
+    case 0:
+    case 1:
+    case 2:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr FieldOptions_CType FieldOptions::STRING;
+constexpr FieldOptions_CType FieldOptions::CORD;
+constexpr FieldOptions_CType FieldOptions::STRING_PIECE;
+constexpr FieldOptions_CType FieldOptions::CType_MIN;
+constexpr FieldOptions_CType FieldOptions::CType_MAX;
+constexpr int FieldOptions::CType_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* FieldOptions_JSType_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2fdescriptor_2eproto);
+  return file_level_enum_descriptors_google_2fprotobuf_2fdescriptor_2eproto[4];
+}
+bool FieldOptions_JSType_IsValid(int value) {
+  switch (value) {
+    case 0:
+    case 1:
+    case 2:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr FieldOptions_JSType FieldOptions::JS_NORMAL;
+constexpr FieldOptions_JSType FieldOptions::JS_STRING;
+constexpr FieldOptions_JSType FieldOptions::JS_NUMBER;
+constexpr FieldOptions_JSType FieldOptions::JSType_MIN;
+constexpr FieldOptions_JSType FieldOptions::JSType_MAX;
+constexpr int FieldOptions::JSType_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MethodOptions_IdempotencyLevel_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2fdescriptor_2eproto);
+  return file_level_enum_descriptors_google_2fprotobuf_2fdescriptor_2eproto[5];
+}
+bool MethodOptions_IdempotencyLevel_IsValid(int value) {
+  switch (value) {
+    case 0:
+    case 1:
+    case 2:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr MethodOptions_IdempotencyLevel MethodOptions::IDEMPOTENCY_UNKNOWN;
+constexpr MethodOptions_IdempotencyLevel MethodOptions::NO_SIDE_EFFECTS;
+constexpr MethodOptions_IdempotencyLevel MethodOptions::IDEMPOTENT;
+constexpr MethodOptions_IdempotencyLevel MethodOptions::IdempotencyLevel_MIN;
+constexpr MethodOptions_IdempotencyLevel MethodOptions::IdempotencyLevel_MAX;
+constexpr int MethodOptions::IdempotencyLevel_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+
+// ===================================================================
+
+class FileDescriptorSet::_Internal {
+ public:
+};
+
+FileDescriptorSet::FileDescriptorSet(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.FileDescriptorSet)
+}
+FileDescriptorSet::FileDescriptorSet(const FileDescriptorSet& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  FileDescriptorSet* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.file_){from._impl_.file_}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.FileDescriptorSet)
+}
+
+inline void FileDescriptorSet::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.file_){arena}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+FileDescriptorSet::~FileDescriptorSet() {
+  // @@protoc_insertion_point(destructor:google.protobuf.FileDescriptorSet)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void FileDescriptorSet::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.file_.~RepeatedPtrField();
+}
+
+void FileDescriptorSet::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void FileDescriptorSet::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.FileDescriptorSet)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.file_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* FileDescriptorSet::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // repeated .google.protobuf.FileDescriptorProto file = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_file(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* FileDescriptorSet::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.FileDescriptorSet)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.FileDescriptorProto file = 1;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_file_size()); i < n; i++) {
+    const auto& repfield = this->_internal_file(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.FileDescriptorSet)
+  return target;
+}
+
+size_t FileDescriptorSet::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FileDescriptorSet)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.FileDescriptorProto file = 1;
+  total_size += 1UL * this->_internal_file_size();
+  for (const auto& msg : this->_impl_.file_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FileDescriptorSet::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    FileDescriptorSet::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FileDescriptorSet::GetClassData() const { return &_class_data_; }
+
+
+void FileDescriptorSet::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<FileDescriptorSet*>(&to_msg);
+  auto& from = static_cast<const FileDescriptorSet&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FileDescriptorSet)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.file_.MergeFrom(from._impl_.file_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void FileDescriptorSet::CopyFrom(const FileDescriptorSet& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FileDescriptorSet)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool FileDescriptorSet::IsInitialized() const {
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.file_))
+    return false;
+  return true;
+}
+
+void FileDescriptorSet::InternalSwap(FileDescriptorSet* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.file_.InternalSwap(&other->_impl_.file_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata FileDescriptorSet::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[0]);
+}
+
+// ===================================================================
+
+class FileDescriptorProto::_Internal {
+ public:
+  using HasBits = decltype(std::declval<FileDescriptorProto>()._impl_._has_bits_);
+  static void set_has_name(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_package(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::FileOptions& options(const FileDescriptorProto* msg);
+  static void set_has_options(HasBits* has_bits) {
+    (*has_bits)[0] |= 8u;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo& source_code_info(const FileDescriptorProto* msg);
+  static void set_has_source_code_info(HasBits* has_bits) {
+    (*has_bits)[0] |= 16u;
+  }
+  static void set_has_syntax(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+};
+
+const ::PROTOBUF_NAMESPACE_ID::FileOptions&
+FileDescriptorProto::_Internal::options(const FileDescriptorProto* msg) {
+  return *msg->_impl_.options_;
+}
+const ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo&
+FileDescriptorProto::_Internal::source_code_info(const FileDescriptorProto* msg) {
+  return *msg->_impl_.source_code_info_;
+}
+FileDescriptorProto::FileDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.FileDescriptorProto)
+}
+FileDescriptorProto::FileDescriptorProto(const FileDescriptorProto& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  FileDescriptorProto* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.dependency_){from._impl_.dependency_}
+    , decltype(_impl_.message_type_){from._impl_.message_type_}
+    , decltype(_impl_.enum_type_){from._impl_.enum_type_}
+    , decltype(_impl_.service_){from._impl_.service_}
+    , decltype(_impl_.extension_){from._impl_.extension_}
+    , decltype(_impl_.public_dependency_){from._impl_.public_dependency_}
+    , decltype(_impl_.weak_dependency_){from._impl_.weak_dependency_}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.package_){}
+    , decltype(_impl_.syntax_){}
+    , decltype(_impl_.options_){nullptr}
+    , decltype(_impl_.source_code_info_){nullptr}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_name()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.package_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.package_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_package()) {
+    _this->_impl_.package_.Set(from._internal_package(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.syntax_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.syntax_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_syntax()) {
+    _this->_impl_.syntax_.Set(from._internal_syntax(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_options()) {
+    _this->_impl_.options_ = new ::PROTOBUF_NAMESPACE_ID::FileOptions(*from._impl_.options_);
+  }
+  if (from._internal_has_source_code_info()) {
+    _this->_impl_.source_code_info_ = new ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo(*from._impl_.source_code_info_);
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.FileDescriptorProto)
+}
+
+inline void FileDescriptorProto::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.dependency_){arena}
+    , decltype(_impl_.message_type_){arena}
+    , decltype(_impl_.enum_type_){arena}
+    , decltype(_impl_.service_){arena}
+    , decltype(_impl_.extension_){arena}
+    , decltype(_impl_.public_dependency_){arena}
+    , decltype(_impl_.weak_dependency_){arena}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.package_){}
+    , decltype(_impl_.syntax_){}
+    , decltype(_impl_.options_){nullptr}
+    , decltype(_impl_.source_code_info_){nullptr}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.package_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.package_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.syntax_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.syntax_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+FileDescriptorProto::~FileDescriptorProto() {
+  // @@protoc_insertion_point(destructor:google.protobuf.FileDescriptorProto)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void FileDescriptorProto::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.dependency_.~RepeatedPtrField();
+  _impl_.message_type_.~RepeatedPtrField();
+  _impl_.enum_type_.~RepeatedPtrField();
+  _impl_.service_.~RepeatedPtrField();
+  _impl_.extension_.~RepeatedPtrField();
+  _impl_.public_dependency_.~RepeatedField();
+  _impl_.weak_dependency_.~RepeatedField();
+  _impl_.name_.Destroy();
+  _impl_.package_.Destroy();
+  _impl_.syntax_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.options_;
+  if (this != internal_default_instance()) delete _impl_.source_code_info_;
+}
+
+void FileDescriptorProto::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void FileDescriptorProto::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.FileDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.dependency_.Clear();
+  _impl_.message_type_.Clear();
+  _impl_.enum_type_.Clear();
+  _impl_.service_.Clear();
+  _impl_.extension_.Clear();
+  _impl_.public_dependency_.Clear();
+  _impl_.weak_dependency_.Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000001fu) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.name_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _impl_.package_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _impl_.syntax_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000008u) {
+      GOOGLE_DCHECK(_impl_.options_ != nullptr);
+      _impl_.options_->Clear();
+    }
+    if (cached_has_bits & 0x00000010u) {
+      GOOGLE_DCHECK(_impl_.source_code_info_ != nullptr);
+      _impl_.source_code_info_->Clear();
+    }
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* FileDescriptorProto::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileDescriptorProto.name");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string package = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          auto str = _internal_mutable_package();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileDescriptorProto.package");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated string dependency = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            auto str = _internal_add_dependency();
+            ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+            CHK_(ptr);
+            #ifndef NDEBUG
+            ::_pbi::VerifyUTF8(str, "google.protobuf.FileDescriptorProto.dependency");
+            #endif  // !NDEBUG
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<26>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.DescriptorProto message_type = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_message_type(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<34>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.EnumDescriptorProto enum_type = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_enum_type(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<42>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.ServiceDescriptorProto service = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 50)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_service(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<50>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.FieldDescriptorProto extension = 7;
+      case 7:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_extension(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<58>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.FileOptions options = 8;
+      case 8:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 66)) {
+          ptr = ctx->ParseMessage(_internal_mutable_options(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.SourceCodeInfo source_code_info = 9;
+      case 9:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 74)) {
+          ptr = ctx->ParseMessage(_internal_mutable_source_code_info(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated int32 public_dependency = 10;
+      case 10:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 80)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            _internal_add_public_dependency(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr));
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<80>(ptr));
+        } else if (static_cast<uint8_t>(tag) == 82) {
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_public_dependency(), ptr, ctx);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated int32 weak_dependency = 11;
+      case 11:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 88)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            _internal_add_weak_dependency(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr));
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<88>(ptr));
+        } else if (static_cast<uint8_t>(tag) == 90) {
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_weak_dependency(), ptr, ctx);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string syntax = 12;
+      case 12:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 98)) {
+          auto str = _internal_mutable_syntax();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileDescriptorProto.syntax");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* FileDescriptorProto::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.FileDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string name = 1;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileDescriptorProto.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // optional string package = 2;
+  if (cached_has_bits & 0x00000002u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_package().data(), static_cast<int>(this->_internal_package().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileDescriptorProto.package");
+    target = stream->WriteStringMaybeAliased(
+        2, this->_internal_package(), target);
+  }
+
+  // repeated string dependency = 3;
+  for (int i = 0, n = this->_internal_dependency_size(); i < n; i++) {
+    const auto& s = this->_internal_dependency(i);
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      s.data(), static_cast<int>(s.length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileDescriptorProto.dependency");
+    target = stream->WriteString(3, s, target);
+  }
+
+  // repeated .google.protobuf.DescriptorProto message_type = 4;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_message_type_size()); i < n; i++) {
+    const auto& repfield = this->_internal_message_type(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(4, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.EnumDescriptorProto enum_type = 5;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_enum_type_size()); i < n; i++) {
+    const auto& repfield = this->_internal_enum_type(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(5, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.ServiceDescriptorProto service = 6;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_service_size()); i < n; i++) {
+    const auto& repfield = this->_internal_service(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(6, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.FieldDescriptorProto extension = 7;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_extension_size()); i < n; i++) {
+    const auto& repfield = this->_internal_extension(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(7, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // optional .google.protobuf.FileOptions options = 8;
+  if (cached_has_bits & 0x00000008u) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(8, _Internal::options(this),
+        _Internal::options(this).GetCachedSize(), target, stream);
+  }
+
+  // optional .google.protobuf.SourceCodeInfo source_code_info = 9;
+  if (cached_has_bits & 0x00000010u) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(9, _Internal::source_code_info(this),
+        _Internal::source_code_info(this).GetCachedSize(), target, stream);
+  }
+
+  // repeated int32 public_dependency = 10;
+  for (int i = 0, n = this->_internal_public_dependency_size(); i < n; i++) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(10, this->_internal_public_dependency(i), target);
+  }
+
+  // repeated int32 weak_dependency = 11;
+  for (int i = 0, n = this->_internal_weak_dependency_size(); i < n; i++) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(11, this->_internal_weak_dependency(i), target);
+  }
+
+  // optional string syntax = 12;
+  if (cached_has_bits & 0x00000004u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_syntax().data(), static_cast<int>(this->_internal_syntax().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileDescriptorProto.syntax");
+    target = stream->WriteStringMaybeAliased(
+        12, this->_internal_syntax(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.FileDescriptorProto)
+  return target;
+}
+
+size_t FileDescriptorProto::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FileDescriptorProto)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated string dependency = 3;
+  total_size += 1 *
+      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(_impl_.dependency_.size());
+  for (int i = 0, n = _impl_.dependency_.size(); i < n; i++) {
+    total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+      _impl_.dependency_.Get(i));
+  }
+
+  // repeated .google.protobuf.DescriptorProto message_type = 4;
+  total_size += 1UL * this->_internal_message_type_size();
+  for (const auto& msg : this->_impl_.message_type_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.EnumDescriptorProto enum_type = 5;
+  total_size += 1UL * this->_internal_enum_type_size();
+  for (const auto& msg : this->_impl_.enum_type_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.ServiceDescriptorProto service = 6;
+  total_size += 1UL * this->_internal_service_size();
+  for (const auto& msg : this->_impl_.service_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.FieldDescriptorProto extension = 7;
+  total_size += 1UL * this->_internal_extension_size();
+  for (const auto& msg : this->_impl_.extension_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated int32 public_dependency = 10;
+  {
+    size_t data_size = ::_pbi::WireFormatLite::
+      Int32Size(this->_impl_.public_dependency_);
+    total_size += 1 *
+                  ::_pbi::FromIntSize(this->_internal_public_dependency_size());
+    total_size += data_size;
+  }
+
+  // repeated int32 weak_dependency = 11;
+  {
+    size_t data_size = ::_pbi::WireFormatLite::
+      Int32Size(this->_impl_.weak_dependency_);
+    total_size += 1 *
+                  ::_pbi::FromIntSize(this->_internal_weak_dependency_size());
+    total_size += data_size;
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000001fu) {
+    // optional string name = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_name());
+    }
+
+    // optional string package = 2;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_package());
+    }
+
+    // optional string syntax = 12;
+    if (cached_has_bits & 0x00000004u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_syntax());
+    }
+
+    // optional .google.protobuf.FileOptions options = 8;
+    if (cached_has_bits & 0x00000008u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.options_);
+    }
+
+    // optional .google.protobuf.SourceCodeInfo source_code_info = 9;
+    if (cached_has_bits & 0x00000010u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.source_code_info_);
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FileDescriptorProto::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    FileDescriptorProto::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FileDescriptorProto::GetClassData() const { return &_class_data_; }
+
+
+void FileDescriptorProto::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<FileDescriptorProto*>(&to_msg);
+  auto& from = static_cast<const FileDescriptorProto&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FileDescriptorProto)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.dependency_.MergeFrom(from._impl_.dependency_);
+  _this->_impl_.message_type_.MergeFrom(from._impl_.message_type_);
+  _this->_impl_.enum_type_.MergeFrom(from._impl_.enum_type_);
+  _this->_impl_.service_.MergeFrom(from._impl_.service_);
+  _this->_impl_.extension_.MergeFrom(from._impl_.extension_);
+  _this->_impl_.public_dependency_.MergeFrom(from._impl_.public_dependency_);
+  _this->_impl_.weak_dependency_.MergeFrom(from._impl_.weak_dependency_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000001fu) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_name(from._internal_name());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_set_package(from._internal_package());
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _this->_internal_set_syntax(from._internal_syntax());
+    }
+    if (cached_has_bits & 0x00000008u) {
+      _this->_internal_mutable_options()->::PROTOBUF_NAMESPACE_ID::FileOptions::MergeFrom(
+          from._internal_options());
+    }
+    if (cached_has_bits & 0x00000010u) {
+      _this->_internal_mutable_source_code_info()->::PROTOBUF_NAMESPACE_ID::SourceCodeInfo::MergeFrom(
+          from._internal_source_code_info());
+    }
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void FileDescriptorProto::CopyFrom(const FileDescriptorProto& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FileDescriptorProto)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool FileDescriptorProto::IsInitialized() const {
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.message_type_))
+    return false;
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.enum_type_))
+    return false;
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.service_))
+    return false;
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.extension_))
+    return false;
+  if (_internal_has_options()) {
+    if (!_impl_.options_->IsInitialized()) return false;
+  }
+  return true;
+}
+
+void FileDescriptorProto::InternalSwap(FileDescriptorProto* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.dependency_.InternalSwap(&other->_impl_.dependency_);
+  _impl_.message_type_.InternalSwap(&other->_impl_.message_type_);
+  _impl_.enum_type_.InternalSwap(&other->_impl_.enum_type_);
+  _impl_.service_.InternalSwap(&other->_impl_.service_);
+  _impl_.extension_.InternalSwap(&other->_impl_.extension_);
+  _impl_.public_dependency_.InternalSwap(&other->_impl_.public_dependency_);
+  _impl_.weak_dependency_.InternalSwap(&other->_impl_.weak_dependency_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.package_, lhs_arena,
+      &other->_impl_.package_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.syntax_, lhs_arena,
+      &other->_impl_.syntax_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(FileDescriptorProto, _impl_.source_code_info_)
+      + sizeof(FileDescriptorProto::_impl_.source_code_info_)
+      - PROTOBUF_FIELD_OFFSET(FileDescriptorProto, _impl_.options_)>(
+          reinterpret_cast<char*>(&_impl_.options_),
+          reinterpret_cast<char*>(&other->_impl_.options_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata FileDescriptorProto::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[1]);
+}
+
+// ===================================================================
+
+class DescriptorProto_ExtensionRange::_Internal {
+ public:
+  using HasBits = decltype(std::declval<DescriptorProto_ExtensionRange>()._impl_._has_bits_);
+  static void set_has_start(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static void set_has_end(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions& options(const DescriptorProto_ExtensionRange* msg);
+  static void set_has_options(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+};
+
+const ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions&
+DescriptorProto_ExtensionRange::_Internal::options(const DescriptorProto_ExtensionRange* msg) {
+  return *msg->_impl_.options_;
+}
+DescriptorProto_ExtensionRange::DescriptorProto_ExtensionRange(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.DescriptorProto.ExtensionRange)
+}
+DescriptorProto_ExtensionRange::DescriptorProto_ExtensionRange(const DescriptorProto_ExtensionRange& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  DescriptorProto_ExtensionRange* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.options_){nullptr}
+    , decltype(_impl_.start_){}
+    , decltype(_impl_.end_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  if (from._internal_has_options()) {
+    _this->_impl_.options_ = new ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions(*from._impl_.options_);
+  }
+  ::memcpy(&_impl_.start_, &from._impl_.start_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.end_) -
+    reinterpret_cast<char*>(&_impl_.start_)) + sizeof(_impl_.end_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.DescriptorProto.ExtensionRange)
+}
+
+inline void DescriptorProto_ExtensionRange::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.options_){nullptr}
+    , decltype(_impl_.start_){0}
+    , decltype(_impl_.end_){0}
+  };
+}
+
+DescriptorProto_ExtensionRange::~DescriptorProto_ExtensionRange() {
+  // @@protoc_insertion_point(destructor:google.protobuf.DescriptorProto.ExtensionRange)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void DescriptorProto_ExtensionRange::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  if (this != internal_default_instance()) delete _impl_.options_;
+}
+
+void DescriptorProto_ExtensionRange::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void DescriptorProto_ExtensionRange::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.DescriptorProto.ExtensionRange)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000001u) {
+    GOOGLE_DCHECK(_impl_.options_ != nullptr);
+    _impl_.options_->Clear();
+  }
+  if (cached_has_bits & 0x00000006u) {
+    ::memset(&_impl_.start_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&_impl_.end_) -
+        reinterpret_cast<char*>(&_impl_.start_)) + sizeof(_impl_.end_));
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* DescriptorProto_ExtensionRange::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional int32 start = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _Internal::set_has_start(&has_bits);
+          _impl_.start_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional int32 end = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _Internal::set_has_end(&has_bits);
+          _impl_.end_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.ExtensionRangeOptions options = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          ptr = ctx->ParseMessage(_internal_mutable_options(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* DescriptorProto_ExtensionRange::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.DescriptorProto.ExtensionRange)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional int32 start = 1;
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(1, this->_internal_start(), target);
+  }
+
+  // optional int32 end = 2;
+  if (cached_has_bits & 0x00000004u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_end(), target);
+  }
+
+  // optional .google.protobuf.ExtensionRangeOptions options = 3;
+  if (cached_has_bits & 0x00000001u) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(3, _Internal::options(this),
+        _Internal::options(this).GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.DescriptorProto.ExtensionRange)
+  return target;
+}
+
+size_t DescriptorProto_ExtensionRange::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.DescriptorProto.ExtensionRange)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000007u) {
+    // optional .google.protobuf.ExtensionRangeOptions options = 3;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.options_);
+    }
+
+    // optional int32 start = 1;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_start());
+    }
+
+    // optional int32 end = 2;
+    if (cached_has_bits & 0x00000004u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_end());
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData DescriptorProto_ExtensionRange::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    DescriptorProto_ExtensionRange::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*DescriptorProto_ExtensionRange::GetClassData() const { return &_class_data_; }
+
+
+void DescriptorProto_ExtensionRange::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<DescriptorProto_ExtensionRange*>(&to_msg);
+  auto& from = static_cast<const DescriptorProto_ExtensionRange&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.DescriptorProto.ExtensionRange)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000007u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_mutable_options()->::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions::MergeFrom(
+          from._internal_options());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_impl_.start_ = from._impl_.start_;
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _this->_impl_.end_ = from._impl_.end_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void DescriptorProto_ExtensionRange::CopyFrom(const DescriptorProto_ExtensionRange& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.DescriptorProto.ExtensionRange)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool DescriptorProto_ExtensionRange::IsInitialized() const {
+  if (_internal_has_options()) {
+    if (!_impl_.options_->IsInitialized()) return false;
+  }
+  return true;
+}
+
+void DescriptorProto_ExtensionRange::InternalSwap(DescriptorProto_ExtensionRange* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(DescriptorProto_ExtensionRange, _impl_.end_)
+      + sizeof(DescriptorProto_ExtensionRange::_impl_.end_)
+      - PROTOBUF_FIELD_OFFSET(DescriptorProto_ExtensionRange, _impl_.options_)>(
+          reinterpret_cast<char*>(&_impl_.options_),
+          reinterpret_cast<char*>(&other->_impl_.options_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata DescriptorProto_ExtensionRange::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[2]);
+}
+
+// ===================================================================
+
+class DescriptorProto_ReservedRange::_Internal {
+ public:
+  using HasBits = decltype(std::declval<DescriptorProto_ReservedRange>()._impl_._has_bits_);
+  static void set_has_start(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_end(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+};
+
+DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.DescriptorProto.ReservedRange)
+}
+DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(const DescriptorProto_ReservedRange& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  DescriptorProto_ReservedRange* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.start_){}
+    , decltype(_impl_.end_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  ::memcpy(&_impl_.start_, &from._impl_.start_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.end_) -
+    reinterpret_cast<char*>(&_impl_.start_)) + sizeof(_impl_.end_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.DescriptorProto.ReservedRange)
+}
+
+inline void DescriptorProto_ReservedRange::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.start_){0}
+    , decltype(_impl_.end_){0}
+  };
+}
+
+DescriptorProto_ReservedRange::~DescriptorProto_ReservedRange() {
+  // @@protoc_insertion_point(destructor:google.protobuf.DescriptorProto.ReservedRange)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void DescriptorProto_ReservedRange::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void DescriptorProto_ReservedRange::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void DescriptorProto_ReservedRange::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.DescriptorProto.ReservedRange)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    ::memset(&_impl_.start_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&_impl_.end_) -
+        reinterpret_cast<char*>(&_impl_.start_)) + sizeof(_impl_.end_));
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* DescriptorProto_ReservedRange::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional int32 start = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _Internal::set_has_start(&has_bits);
+          _impl_.start_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional int32 end = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _Internal::set_has_end(&has_bits);
+          _impl_.end_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* DescriptorProto_ReservedRange::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.DescriptorProto.ReservedRange)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional int32 start = 1;
+  if (cached_has_bits & 0x00000001u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(1, this->_internal_start(), target);
+  }
+
+  // optional int32 end = 2;
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_end(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.DescriptorProto.ReservedRange)
+  return target;
+}
+
+size_t DescriptorProto_ReservedRange::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.DescriptorProto.ReservedRange)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    // optional int32 start = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_start());
+    }
+
+    // optional int32 end = 2;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_end());
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData DescriptorProto_ReservedRange::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    DescriptorProto_ReservedRange::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*DescriptorProto_ReservedRange::GetClassData() const { return &_class_data_; }
+
+
+void DescriptorProto_ReservedRange::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<DescriptorProto_ReservedRange*>(&to_msg);
+  auto& from = static_cast<const DescriptorProto_ReservedRange&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.DescriptorProto.ReservedRange)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_impl_.start_ = from._impl_.start_;
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_impl_.end_ = from._impl_.end_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void DescriptorProto_ReservedRange::CopyFrom(const DescriptorProto_ReservedRange& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.DescriptorProto.ReservedRange)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool DescriptorProto_ReservedRange::IsInitialized() const {
+  return true;
+}
+
+void DescriptorProto_ReservedRange::InternalSwap(DescriptorProto_ReservedRange* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(DescriptorProto_ReservedRange, _impl_.end_)
+      + sizeof(DescriptorProto_ReservedRange::_impl_.end_)
+      - PROTOBUF_FIELD_OFFSET(DescriptorProto_ReservedRange, _impl_.start_)>(
+          reinterpret_cast<char*>(&_impl_.start_),
+          reinterpret_cast<char*>(&other->_impl_.start_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata DescriptorProto_ReservedRange::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[3]);
+}
+
+// ===================================================================
+
+class DescriptorProto::_Internal {
+ public:
+  using HasBits = decltype(std::declval<DescriptorProto>()._impl_._has_bits_);
+  static void set_has_name(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::MessageOptions& options(const DescriptorProto* msg);
+  static void set_has_options(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+};
+
+const ::PROTOBUF_NAMESPACE_ID::MessageOptions&
+DescriptorProto::_Internal::options(const DescriptorProto* msg) {
+  return *msg->_impl_.options_;
+}
+DescriptorProto::DescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.DescriptorProto)
+}
+DescriptorProto::DescriptorProto(const DescriptorProto& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  DescriptorProto* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.field_){from._impl_.field_}
+    , decltype(_impl_.nested_type_){from._impl_.nested_type_}
+    , decltype(_impl_.enum_type_){from._impl_.enum_type_}
+    , decltype(_impl_.extension_range_){from._impl_.extension_range_}
+    , decltype(_impl_.extension_){from._impl_.extension_}
+    , decltype(_impl_.oneof_decl_){from._impl_.oneof_decl_}
+    , decltype(_impl_.reserved_range_){from._impl_.reserved_range_}
+    , decltype(_impl_.reserved_name_){from._impl_.reserved_name_}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.options_){nullptr}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_name()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_options()) {
+    _this->_impl_.options_ = new ::PROTOBUF_NAMESPACE_ID::MessageOptions(*from._impl_.options_);
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.DescriptorProto)
+}
+
+inline void DescriptorProto::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.field_){arena}
+    , decltype(_impl_.nested_type_){arena}
+    , decltype(_impl_.enum_type_){arena}
+    , decltype(_impl_.extension_range_){arena}
+    , decltype(_impl_.extension_){arena}
+    , decltype(_impl_.oneof_decl_){arena}
+    , decltype(_impl_.reserved_range_){arena}
+    , decltype(_impl_.reserved_name_){arena}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.options_){nullptr}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+DescriptorProto::~DescriptorProto() {
+  // @@protoc_insertion_point(destructor:google.protobuf.DescriptorProto)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void DescriptorProto::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.field_.~RepeatedPtrField();
+  _impl_.nested_type_.~RepeatedPtrField();
+  _impl_.enum_type_.~RepeatedPtrField();
+  _impl_.extension_range_.~RepeatedPtrField();
+  _impl_.extension_.~RepeatedPtrField();
+  _impl_.oneof_decl_.~RepeatedPtrField();
+  _impl_.reserved_range_.~RepeatedPtrField();
+  _impl_.reserved_name_.~RepeatedPtrField();
+  _impl_.name_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.options_;
+}
+
+void DescriptorProto::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void DescriptorProto::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.DescriptorProto)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.field_.Clear();
+  _impl_.nested_type_.Clear();
+  _impl_.enum_type_.Clear();
+  _impl_.extension_range_.Clear();
+  _impl_.extension_.Clear();
+  _impl_.oneof_decl_.Clear();
+  _impl_.reserved_range_.Clear();
+  _impl_.reserved_name_.Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.name_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      GOOGLE_DCHECK(_impl_.options_ != nullptr);
+      _impl_.options_->Clear();
+    }
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* DescriptorProto::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.DescriptorProto.name");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.FieldDescriptorProto field = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_field(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.DescriptorProto nested_type = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_nested_type(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<26>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.EnumDescriptorProto enum_type = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_enum_type(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<34>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_extension_range(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<42>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.FieldDescriptorProto extension = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 50)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_extension(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<50>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.MessageOptions options = 7;
+      case 7:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr = ctx->ParseMessage(_internal_mutable_options(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.OneofDescriptorProto oneof_decl = 8;
+      case 8:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 66)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_oneof_decl(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<66>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9;
+      case 9:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 74)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_reserved_range(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<74>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated string reserved_name = 10;
+      case 10:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 82)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            auto str = _internal_add_reserved_name();
+            ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+            CHK_(ptr);
+            #ifndef NDEBUG
+            ::_pbi::VerifyUTF8(str, "google.protobuf.DescriptorProto.reserved_name");
+            #endif  // !NDEBUG
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<82>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* DescriptorProto::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.DescriptorProto)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string name = 1;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.DescriptorProto.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // repeated .google.protobuf.FieldDescriptorProto field = 2;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_field_size()); i < n; i++) {
+    const auto& repfield = this->_internal_field(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(2, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.DescriptorProto nested_type = 3;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_nested_type_size()); i < n; i++) {
+    const auto& repfield = this->_internal_nested_type(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(3, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.EnumDescriptorProto enum_type = 4;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_enum_type_size()); i < n; i++) {
+    const auto& repfield = this->_internal_enum_type(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(4, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_extension_range_size()); i < n; i++) {
+    const auto& repfield = this->_internal_extension_range(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(5, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.FieldDescriptorProto extension = 6;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_extension_size()); i < n; i++) {
+    const auto& repfield = this->_internal_extension(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(6, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // optional .google.protobuf.MessageOptions options = 7;
+  if (cached_has_bits & 0x00000002u) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(7, _Internal::options(this),
+        _Internal::options(this).GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.OneofDescriptorProto oneof_decl = 8;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_oneof_decl_size()); i < n; i++) {
+    const auto& repfield = this->_internal_oneof_decl(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(8, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_reserved_range_size()); i < n; i++) {
+    const auto& repfield = this->_internal_reserved_range(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(9, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated string reserved_name = 10;
+  for (int i = 0, n = this->_internal_reserved_name_size(); i < n; i++) {
+    const auto& s = this->_internal_reserved_name(i);
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      s.data(), static_cast<int>(s.length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.DescriptorProto.reserved_name");
+    target = stream->WriteString(10, s, target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.DescriptorProto)
+  return target;
+}
+
+size_t DescriptorProto::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.DescriptorProto)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.FieldDescriptorProto field = 2;
+  total_size += 1UL * this->_internal_field_size();
+  for (const auto& msg : this->_impl_.field_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.DescriptorProto nested_type = 3;
+  total_size += 1UL * this->_internal_nested_type_size();
+  for (const auto& msg : this->_impl_.nested_type_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.EnumDescriptorProto enum_type = 4;
+  total_size += 1UL * this->_internal_enum_type_size();
+  for (const auto& msg : this->_impl_.enum_type_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5;
+  total_size += 1UL * this->_internal_extension_range_size();
+  for (const auto& msg : this->_impl_.extension_range_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.FieldDescriptorProto extension = 6;
+  total_size += 1UL * this->_internal_extension_size();
+  for (const auto& msg : this->_impl_.extension_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.OneofDescriptorProto oneof_decl = 8;
+  total_size += 1UL * this->_internal_oneof_decl_size();
+  for (const auto& msg : this->_impl_.oneof_decl_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9;
+  total_size += 1UL * this->_internal_reserved_range_size();
+  for (const auto& msg : this->_impl_.reserved_range_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated string reserved_name = 10;
+  total_size += 1 *
+      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(_impl_.reserved_name_.size());
+  for (int i = 0, n = _impl_.reserved_name_.size(); i < n; i++) {
+    total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+      _impl_.reserved_name_.Get(i));
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    // optional string name = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_name());
+    }
+
+    // optional .google.protobuf.MessageOptions options = 7;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.options_);
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData DescriptorProto::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    DescriptorProto::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*DescriptorProto::GetClassData() const { return &_class_data_; }
+
+
+void DescriptorProto::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<DescriptorProto*>(&to_msg);
+  auto& from = static_cast<const DescriptorProto&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.DescriptorProto)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.field_.MergeFrom(from._impl_.field_);
+  _this->_impl_.nested_type_.MergeFrom(from._impl_.nested_type_);
+  _this->_impl_.enum_type_.MergeFrom(from._impl_.enum_type_);
+  _this->_impl_.extension_range_.MergeFrom(from._impl_.extension_range_);
+  _this->_impl_.extension_.MergeFrom(from._impl_.extension_);
+  _this->_impl_.oneof_decl_.MergeFrom(from._impl_.oneof_decl_);
+  _this->_impl_.reserved_range_.MergeFrom(from._impl_.reserved_range_);
+  _this->_impl_.reserved_name_.MergeFrom(from._impl_.reserved_name_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_name(from._internal_name());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_mutable_options()->::PROTOBUF_NAMESPACE_ID::MessageOptions::MergeFrom(
+          from._internal_options());
+    }
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void DescriptorProto::CopyFrom(const DescriptorProto& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.DescriptorProto)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool DescriptorProto::IsInitialized() const {
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.field_))
+    return false;
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.nested_type_))
+    return false;
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.enum_type_))
+    return false;
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.extension_range_))
+    return false;
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.extension_))
+    return false;
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.oneof_decl_))
+    return false;
+  if (_internal_has_options()) {
+    if (!_impl_.options_->IsInitialized()) return false;
+  }
+  return true;
+}
+
+void DescriptorProto::InternalSwap(DescriptorProto* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.field_.InternalSwap(&other->_impl_.field_);
+  _impl_.nested_type_.InternalSwap(&other->_impl_.nested_type_);
+  _impl_.enum_type_.InternalSwap(&other->_impl_.enum_type_);
+  _impl_.extension_range_.InternalSwap(&other->_impl_.extension_range_);
+  _impl_.extension_.InternalSwap(&other->_impl_.extension_);
+  _impl_.oneof_decl_.InternalSwap(&other->_impl_.oneof_decl_);
+  _impl_.reserved_range_.InternalSwap(&other->_impl_.reserved_range_);
+  _impl_.reserved_name_.InternalSwap(&other->_impl_.reserved_name_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  swap(_impl_.options_, other->_impl_.options_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata DescriptorProto::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[4]);
+}
+
+// ===================================================================
+
+class ExtensionRangeOptions::_Internal {
+ public:
+};
+
+ExtensionRangeOptions::ExtensionRangeOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.ExtensionRangeOptions)
+}
+ExtensionRangeOptions::ExtensionRangeOptions(const ExtensionRangeOptions& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  ExtensionRangeOptions* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{}
+    , decltype(_impl_.uninterpreted_option_){from._impl_.uninterpreted_option_}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.ExtensionRangeOptions)
+}
+
+inline void ExtensionRangeOptions::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{::_pbi::ArenaInitialized(), arena}
+    , decltype(_impl_.uninterpreted_option_){arena}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+ExtensionRangeOptions::~ExtensionRangeOptions() {
+  // @@protoc_insertion_point(destructor:google.protobuf.ExtensionRangeOptions)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void ExtensionRangeOptions::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_._extensions_.~ExtensionSet();
+  _impl_.uninterpreted_option_.~RepeatedPtrField();
+}
+
+void ExtensionRangeOptions::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void ExtensionRangeOptions::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.ExtensionRangeOptions)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_._extensions_.Clear();
+  _impl_.uninterpreted_option_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* ExtensionRangeOptions::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+      case 999:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr -= 2;
+          do {
+            ptr += 2;
+            ptr = ctx->ParseMessage(_internal_add_uninterpreted_option(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<7994>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    if ((8000u <= tag)) {
+      ptr = _impl_._extensions_.ParseField(tag, ptr, internal_default_instance(), &_internal_metadata_, ctx);
+      CHK_(ptr != nullptr);
+      continue;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* ExtensionRangeOptions::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.ExtensionRangeOptions)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_uninterpreted_option_size()); i < n; i++) {
+    const auto& repfield = this->_internal_uninterpreted_option(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(999, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // Extension range [1000, 536870912)
+  target = _impl_._extensions_._InternalSerialize(
+  internal_default_instance(), 1000, 536870912, target, stream);
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.ExtensionRangeOptions)
+  return target;
+}
+
+size_t ExtensionRangeOptions::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.ExtensionRangeOptions)
+  size_t total_size = 0;
+
+  total_size += _impl_._extensions_.ByteSize();
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  total_size += 2UL * this->_internal_uninterpreted_option_size();
+  for (const auto& msg : this->_impl_.uninterpreted_option_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ExtensionRangeOptions::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    ExtensionRangeOptions::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ExtensionRangeOptions::GetClassData() const { return &_class_data_; }
+
+
+void ExtensionRangeOptions::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<ExtensionRangeOptions*>(&to_msg);
+  auto& from = static_cast<const ExtensionRangeOptions&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.ExtensionRangeOptions)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.uninterpreted_option_.MergeFrom(from._impl_.uninterpreted_option_);
+  _this->_impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void ExtensionRangeOptions::CopyFrom(const ExtensionRangeOptions& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.ExtensionRangeOptions)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool ExtensionRangeOptions::IsInitialized() const {
+  if (!_impl_._extensions_.IsInitialized()) {
+    return false;
+  }
+
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.uninterpreted_option_))
+    return false;
+  return true;
+}
+
+void ExtensionRangeOptions::InternalSwap(ExtensionRangeOptions* other) {
+  using std::swap;
+  _impl_._extensions_.InternalSwap(&other->_impl_._extensions_);
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.uninterpreted_option_.InternalSwap(&other->_impl_.uninterpreted_option_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata ExtensionRangeOptions::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[5]);
+}
+
+// ===================================================================
+
+class FieldDescriptorProto::_Internal {
+ public:
+  using HasBits = decltype(std::declval<FieldDescriptorProto>()._impl_._has_bits_);
+  static void set_has_name(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_number(HasBits* has_bits) {
+    (*has_bits)[0] |= 64u;
+  }
+  static void set_has_label(HasBits* has_bits) {
+    (*has_bits)[0] |= 512u;
+  }
+  static void set_has_type(HasBits* has_bits) {
+    (*has_bits)[0] |= 1024u;
+  }
+  static void set_has_type_name(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+  static void set_has_extendee(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static void set_has_default_value(HasBits* has_bits) {
+    (*has_bits)[0] |= 8u;
+  }
+  static void set_has_oneof_index(HasBits* has_bits) {
+    (*has_bits)[0] |= 128u;
+  }
+  static void set_has_json_name(HasBits* has_bits) {
+    (*has_bits)[0] |= 16u;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::FieldOptions& options(const FieldDescriptorProto* msg);
+  static void set_has_options(HasBits* has_bits) {
+    (*has_bits)[0] |= 32u;
+  }
+  static void set_has_proto3_optional(HasBits* has_bits) {
+    (*has_bits)[0] |= 256u;
+  }
+};
+
+const ::PROTOBUF_NAMESPACE_ID::FieldOptions&
+FieldDescriptorProto::_Internal::options(const FieldDescriptorProto* msg) {
+  return *msg->_impl_.options_;
+}
+FieldDescriptorProto::FieldDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.FieldDescriptorProto)
+}
+FieldDescriptorProto::FieldDescriptorProto(const FieldDescriptorProto& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  FieldDescriptorProto* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.extendee_){}
+    , decltype(_impl_.type_name_){}
+    , decltype(_impl_.default_value_){}
+    , decltype(_impl_.json_name_){}
+    , decltype(_impl_.options_){nullptr}
+    , decltype(_impl_.number_){}
+    , decltype(_impl_.oneof_index_){}
+    , decltype(_impl_.proto3_optional_){}
+    , decltype(_impl_.label_){}
+    , decltype(_impl_.type_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_name()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.extendee_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.extendee_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_extendee()) {
+    _this->_impl_.extendee_.Set(from._internal_extendee(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.type_name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.type_name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_type_name()) {
+    _this->_impl_.type_name_.Set(from._internal_type_name(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.default_value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.default_value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_default_value()) {
+    _this->_impl_.default_value_.Set(from._internal_default_value(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.json_name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.json_name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_json_name()) {
+    _this->_impl_.json_name_.Set(from._internal_json_name(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_options()) {
+    _this->_impl_.options_ = new ::PROTOBUF_NAMESPACE_ID::FieldOptions(*from._impl_.options_);
+  }
+  ::memcpy(&_impl_.number_, &from._impl_.number_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.type_) -
+    reinterpret_cast<char*>(&_impl_.number_)) + sizeof(_impl_.type_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.FieldDescriptorProto)
+}
+
+inline void FieldDescriptorProto::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.extendee_){}
+    , decltype(_impl_.type_name_){}
+    , decltype(_impl_.default_value_){}
+    , decltype(_impl_.json_name_){}
+    , decltype(_impl_.options_){nullptr}
+    , decltype(_impl_.number_){0}
+    , decltype(_impl_.oneof_index_){0}
+    , decltype(_impl_.proto3_optional_){false}
+    , decltype(_impl_.label_){1}
+    , decltype(_impl_.type_){1}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.extendee_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.extendee_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.type_name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.type_name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.default_value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.default_value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.json_name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.json_name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+FieldDescriptorProto::~FieldDescriptorProto() {
+  // @@protoc_insertion_point(destructor:google.protobuf.FieldDescriptorProto)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void FieldDescriptorProto::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.name_.Destroy();
+  _impl_.extendee_.Destroy();
+  _impl_.type_name_.Destroy();
+  _impl_.default_value_.Destroy();
+  _impl_.json_name_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.options_;
+}
+
+void FieldDescriptorProto::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void FieldDescriptorProto::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.FieldDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000003fu) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.name_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _impl_.extendee_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _impl_.type_name_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000008u) {
+      _impl_.default_value_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000010u) {
+      _impl_.json_name_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000020u) {
+      GOOGLE_DCHECK(_impl_.options_ != nullptr);
+      _impl_.options_->Clear();
+    }
+  }
+  if (cached_has_bits & 0x000000c0u) {
+    ::memset(&_impl_.number_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&_impl_.oneof_index_) -
+        reinterpret_cast<char*>(&_impl_.number_)) + sizeof(_impl_.oneof_index_));
+  }
+  if (cached_has_bits & 0x00000700u) {
+    _impl_.proto3_optional_ = false;
+    _impl_.label_ = 1;
+    _impl_.type_ = 1;
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* FieldDescriptorProto::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FieldDescriptorProto.name");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string extendee = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          auto str = _internal_mutable_extendee();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FieldDescriptorProto.extendee");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional int32 number = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) {
+          _Internal::set_has_number(&has_bits);
+          _impl_.number_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.FieldDescriptorProto.Label label = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 32)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label_IsValid(val))) {
+            _internal_set_label(static_cast<::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label>(val));
+          } else {
+            ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(4, val, mutable_unknown_fields());
+          }
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.FieldDescriptorProto.Type type = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 40)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type_IsValid(val))) {
+            _internal_set_type(static_cast<::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type>(val));
+          } else {
+            ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(5, val, mutable_unknown_fields());
+          }
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string type_name = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 50)) {
+          auto str = _internal_mutable_type_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FieldDescriptorProto.type_name");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string default_value = 7;
+      case 7:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          auto str = _internal_mutable_default_value();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FieldDescriptorProto.default_value");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.FieldOptions options = 8;
+      case 8:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 66)) {
+          ptr = ctx->ParseMessage(_internal_mutable_options(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional int32 oneof_index = 9;
+      case 9:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 72)) {
+          _Internal::set_has_oneof_index(&has_bits);
+          _impl_.oneof_index_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string json_name = 10;
+      case 10:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 82)) {
+          auto str = _internal_mutable_json_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FieldDescriptorProto.json_name");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool proto3_optional = 17;
+      case 17:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 136)) {
+          _Internal::set_has_proto3_optional(&has_bits);
+          _impl_.proto3_optional_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* FieldDescriptorProto::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.FieldDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string name = 1;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FieldDescriptorProto.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // optional string extendee = 2;
+  if (cached_has_bits & 0x00000002u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_extendee().data(), static_cast<int>(this->_internal_extendee().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FieldDescriptorProto.extendee");
+    target = stream->WriteStringMaybeAliased(
+        2, this->_internal_extendee(), target);
+  }
+
+  // optional int32 number = 3;
+  if (cached_has_bits & 0x00000040u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(3, this->_internal_number(), target);
+  }
+
+  // optional .google.protobuf.FieldDescriptorProto.Label label = 4;
+  if (cached_has_bits & 0x00000200u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      4, this->_internal_label(), target);
+  }
+
+  // optional .google.protobuf.FieldDescriptorProto.Type type = 5;
+  if (cached_has_bits & 0x00000400u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      5, this->_internal_type(), target);
+  }
+
+  // optional string type_name = 6;
+  if (cached_has_bits & 0x00000004u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_type_name().data(), static_cast<int>(this->_internal_type_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FieldDescriptorProto.type_name");
+    target = stream->WriteStringMaybeAliased(
+        6, this->_internal_type_name(), target);
+  }
+
+  // optional string default_value = 7;
+  if (cached_has_bits & 0x00000008u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_default_value().data(), static_cast<int>(this->_internal_default_value().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FieldDescriptorProto.default_value");
+    target = stream->WriteStringMaybeAliased(
+        7, this->_internal_default_value(), target);
+  }
+
+  // optional .google.protobuf.FieldOptions options = 8;
+  if (cached_has_bits & 0x00000020u) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(8, _Internal::options(this),
+        _Internal::options(this).GetCachedSize(), target, stream);
+  }
+
+  // optional int32 oneof_index = 9;
+  if (cached_has_bits & 0x00000080u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(9, this->_internal_oneof_index(), target);
+  }
+
+  // optional string json_name = 10;
+  if (cached_has_bits & 0x00000010u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_json_name().data(), static_cast<int>(this->_internal_json_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FieldDescriptorProto.json_name");
+    target = stream->WriteStringMaybeAliased(
+        10, this->_internal_json_name(), target);
+  }
+
+  // optional bool proto3_optional = 17;
+  if (cached_has_bits & 0x00000100u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(17, this->_internal_proto3_optional(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.FieldDescriptorProto)
+  return target;
+}
+
+size_t FieldDescriptorProto::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FieldDescriptorProto)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x000000ffu) {
+    // optional string name = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_name());
+    }
+
+    // optional string extendee = 2;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_extendee());
+    }
+
+    // optional string type_name = 6;
+    if (cached_has_bits & 0x00000004u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_type_name());
+    }
+
+    // optional string default_value = 7;
+    if (cached_has_bits & 0x00000008u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_default_value());
+    }
+
+    // optional string json_name = 10;
+    if (cached_has_bits & 0x00000010u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_json_name());
+    }
+
+    // optional .google.protobuf.FieldOptions options = 8;
+    if (cached_has_bits & 0x00000020u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.options_);
+    }
+
+    // optional int32 number = 3;
+    if (cached_has_bits & 0x00000040u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_number());
+    }
+
+    // optional int32 oneof_index = 9;
+    if (cached_has_bits & 0x00000080u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_oneof_index());
+    }
+
+  }
+  if (cached_has_bits & 0x00000700u) {
+    // optional bool proto3_optional = 17;
+    if (cached_has_bits & 0x00000100u) {
+      total_size += 2 + 1;
+    }
+
+    // optional .google.protobuf.FieldDescriptorProto.Label label = 4;
+    if (cached_has_bits & 0x00000200u) {
+      total_size += 1 +
+        ::_pbi::WireFormatLite::EnumSize(this->_internal_label());
+    }
+
+    // optional .google.protobuf.FieldDescriptorProto.Type type = 5;
+    if (cached_has_bits & 0x00000400u) {
+      total_size += 1 +
+        ::_pbi::WireFormatLite::EnumSize(this->_internal_type());
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FieldDescriptorProto::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    FieldDescriptorProto::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FieldDescriptorProto::GetClassData() const { return &_class_data_; }
+
+
+void FieldDescriptorProto::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<FieldDescriptorProto*>(&to_msg);
+  auto& from = static_cast<const FieldDescriptorProto&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FieldDescriptorProto)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x000000ffu) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_name(from._internal_name());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_set_extendee(from._internal_extendee());
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _this->_internal_set_type_name(from._internal_type_name());
+    }
+    if (cached_has_bits & 0x00000008u) {
+      _this->_internal_set_default_value(from._internal_default_value());
+    }
+    if (cached_has_bits & 0x00000010u) {
+      _this->_internal_set_json_name(from._internal_json_name());
+    }
+    if (cached_has_bits & 0x00000020u) {
+      _this->_internal_mutable_options()->::PROTOBUF_NAMESPACE_ID::FieldOptions::MergeFrom(
+          from._internal_options());
+    }
+    if (cached_has_bits & 0x00000040u) {
+      _this->_impl_.number_ = from._impl_.number_;
+    }
+    if (cached_has_bits & 0x00000080u) {
+      _this->_impl_.oneof_index_ = from._impl_.oneof_index_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  if (cached_has_bits & 0x00000700u) {
+    if (cached_has_bits & 0x00000100u) {
+      _this->_impl_.proto3_optional_ = from._impl_.proto3_optional_;
+    }
+    if (cached_has_bits & 0x00000200u) {
+      _this->_impl_.label_ = from._impl_.label_;
+    }
+    if (cached_has_bits & 0x00000400u) {
+      _this->_impl_.type_ = from._impl_.type_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void FieldDescriptorProto::CopyFrom(const FieldDescriptorProto& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FieldDescriptorProto)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool FieldDescriptorProto::IsInitialized() const {
+  if (_internal_has_options()) {
+    if (!_impl_.options_->IsInitialized()) return false;
+  }
+  return true;
+}
+
+void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.extendee_, lhs_arena,
+      &other->_impl_.extendee_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.type_name_, lhs_arena,
+      &other->_impl_.type_name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.default_value_, lhs_arena,
+      &other->_impl_.default_value_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.json_name_, lhs_arena,
+      &other->_impl_.json_name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(FieldDescriptorProto, _impl_.proto3_optional_)
+      + sizeof(FieldDescriptorProto::_impl_.proto3_optional_)
+      - PROTOBUF_FIELD_OFFSET(FieldDescriptorProto, _impl_.options_)>(
+          reinterpret_cast<char*>(&_impl_.options_),
+          reinterpret_cast<char*>(&other->_impl_.options_));
+  swap(_impl_.label_, other->_impl_.label_);
+  swap(_impl_.type_, other->_impl_.type_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata FieldDescriptorProto::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[6]);
+}
+
+// ===================================================================
+
+class OneofDescriptorProto::_Internal {
+ public:
+  using HasBits = decltype(std::declval<OneofDescriptorProto>()._impl_._has_bits_);
+  static void set_has_name(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::OneofOptions& options(const OneofDescriptorProto* msg);
+  static void set_has_options(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+};
+
+const ::PROTOBUF_NAMESPACE_ID::OneofOptions&
+OneofDescriptorProto::_Internal::options(const OneofDescriptorProto* msg) {
+  return *msg->_impl_.options_;
+}
+OneofDescriptorProto::OneofDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.OneofDescriptorProto)
+}
+OneofDescriptorProto::OneofDescriptorProto(const OneofDescriptorProto& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  OneofDescriptorProto* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.options_){nullptr}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_name()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_options()) {
+    _this->_impl_.options_ = new ::PROTOBUF_NAMESPACE_ID::OneofOptions(*from._impl_.options_);
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.OneofDescriptorProto)
+}
+
+inline void OneofDescriptorProto::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.options_){nullptr}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+OneofDescriptorProto::~OneofDescriptorProto() {
+  // @@protoc_insertion_point(destructor:google.protobuf.OneofDescriptorProto)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void OneofDescriptorProto::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.name_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.options_;
+}
+
+void OneofDescriptorProto::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void OneofDescriptorProto::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.OneofDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.name_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      GOOGLE_DCHECK(_impl_.options_ != nullptr);
+      _impl_.options_->Clear();
+    }
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* OneofDescriptorProto::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.OneofDescriptorProto.name");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.OneofOptions options = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          ptr = ctx->ParseMessage(_internal_mutable_options(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* OneofDescriptorProto::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.OneofDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string name = 1;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.OneofDescriptorProto.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // optional .google.protobuf.OneofOptions options = 2;
+  if (cached_has_bits & 0x00000002u) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(2, _Internal::options(this),
+        _Internal::options(this).GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.OneofDescriptorProto)
+  return target;
+}
+
+size_t OneofDescriptorProto::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.OneofDescriptorProto)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    // optional string name = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_name());
+    }
+
+    // optional .google.protobuf.OneofOptions options = 2;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.options_);
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData OneofDescriptorProto::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    OneofDescriptorProto::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*OneofDescriptorProto::GetClassData() const { return &_class_data_; }
+
+
+void OneofDescriptorProto::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<OneofDescriptorProto*>(&to_msg);
+  auto& from = static_cast<const OneofDescriptorProto&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.OneofDescriptorProto)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_name(from._internal_name());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_mutable_options()->::PROTOBUF_NAMESPACE_ID::OneofOptions::MergeFrom(
+          from._internal_options());
+    }
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void OneofDescriptorProto::CopyFrom(const OneofDescriptorProto& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.OneofDescriptorProto)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool OneofDescriptorProto::IsInitialized() const {
+  if (_internal_has_options()) {
+    if (!_impl_.options_->IsInitialized()) return false;
+  }
+  return true;
+}
+
+void OneofDescriptorProto::InternalSwap(OneofDescriptorProto* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  swap(_impl_.options_, other->_impl_.options_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata OneofDescriptorProto::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[7]);
+}
+
+// ===================================================================
+
+class EnumDescriptorProto_EnumReservedRange::_Internal {
+ public:
+  using HasBits = decltype(std::declval<EnumDescriptorProto_EnumReservedRange>()._impl_._has_bits_);
+  static void set_has_start(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_end(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+};
+
+EnumDescriptorProto_EnumReservedRange::EnumDescriptorProto_EnumReservedRange(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.EnumDescriptorProto.EnumReservedRange)
+}
+EnumDescriptorProto_EnumReservedRange::EnumDescriptorProto_EnumReservedRange(const EnumDescriptorProto_EnumReservedRange& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  EnumDescriptorProto_EnumReservedRange* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.start_){}
+    , decltype(_impl_.end_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  ::memcpy(&_impl_.start_, &from._impl_.start_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.end_) -
+    reinterpret_cast<char*>(&_impl_.start_)) + sizeof(_impl_.end_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.EnumDescriptorProto.EnumReservedRange)
+}
+
+inline void EnumDescriptorProto_EnumReservedRange::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.start_){0}
+    , decltype(_impl_.end_){0}
+  };
+}
+
+EnumDescriptorProto_EnumReservedRange::~EnumDescriptorProto_EnumReservedRange() {
+  // @@protoc_insertion_point(destructor:google.protobuf.EnumDescriptorProto.EnumReservedRange)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void EnumDescriptorProto_EnumReservedRange::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void EnumDescriptorProto_EnumReservedRange::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void EnumDescriptorProto_EnumReservedRange::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumDescriptorProto.EnumReservedRange)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    ::memset(&_impl_.start_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&_impl_.end_) -
+        reinterpret_cast<char*>(&_impl_.start_)) + sizeof(_impl_.end_));
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* EnumDescriptorProto_EnumReservedRange::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional int32 start = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _Internal::set_has_start(&has_bits);
+          _impl_.start_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional int32 end = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _Internal::set_has_end(&has_bits);
+          _impl_.end_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* EnumDescriptorProto_EnumReservedRange::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.EnumDescriptorProto.EnumReservedRange)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional int32 start = 1;
+  if (cached_has_bits & 0x00000001u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(1, this->_internal_start(), target);
+  }
+
+  // optional int32 end = 2;
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_end(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.EnumDescriptorProto.EnumReservedRange)
+  return target;
+}
+
+size_t EnumDescriptorProto_EnumReservedRange::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumDescriptorProto.EnumReservedRange)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    // optional int32 start = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_start());
+    }
+
+    // optional int32 end = 2;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_end());
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumDescriptorProto_EnumReservedRange::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    EnumDescriptorProto_EnumReservedRange::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumDescriptorProto_EnumReservedRange::GetClassData() const { return &_class_data_; }
+
+
+void EnumDescriptorProto_EnumReservedRange::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<EnumDescriptorProto_EnumReservedRange*>(&to_msg);
+  auto& from = static_cast<const EnumDescriptorProto_EnumReservedRange&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumDescriptorProto.EnumReservedRange)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_impl_.start_ = from._impl_.start_;
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_impl_.end_ = from._impl_.end_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void EnumDescriptorProto_EnumReservedRange::CopyFrom(const EnumDescriptorProto_EnumReservedRange& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumDescriptorProto.EnumReservedRange)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool EnumDescriptorProto_EnumReservedRange::IsInitialized() const {
+  return true;
+}
+
+void EnumDescriptorProto_EnumReservedRange::InternalSwap(EnumDescriptorProto_EnumReservedRange* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(EnumDescriptorProto_EnumReservedRange, _impl_.end_)
+      + sizeof(EnumDescriptorProto_EnumReservedRange::_impl_.end_)
+      - PROTOBUF_FIELD_OFFSET(EnumDescriptorProto_EnumReservedRange, _impl_.start_)>(
+          reinterpret_cast<char*>(&_impl_.start_),
+          reinterpret_cast<char*>(&other->_impl_.start_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata EnumDescriptorProto_EnumReservedRange::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[8]);
+}
+
+// ===================================================================
+
+class EnumDescriptorProto::_Internal {
+ public:
+  using HasBits = decltype(std::declval<EnumDescriptorProto>()._impl_._has_bits_);
+  static void set_has_name(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::EnumOptions& options(const EnumDescriptorProto* msg);
+  static void set_has_options(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+};
+
+const ::PROTOBUF_NAMESPACE_ID::EnumOptions&
+EnumDescriptorProto::_Internal::options(const EnumDescriptorProto* msg) {
+  return *msg->_impl_.options_;
+}
+EnumDescriptorProto::EnumDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.EnumDescriptorProto)
+}
+EnumDescriptorProto::EnumDescriptorProto(const EnumDescriptorProto& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  EnumDescriptorProto* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.value_){from._impl_.value_}
+    , decltype(_impl_.reserved_range_){from._impl_.reserved_range_}
+    , decltype(_impl_.reserved_name_){from._impl_.reserved_name_}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.options_){nullptr}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_name()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_options()) {
+    _this->_impl_.options_ = new ::PROTOBUF_NAMESPACE_ID::EnumOptions(*from._impl_.options_);
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.EnumDescriptorProto)
+}
+
+inline void EnumDescriptorProto::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.value_){arena}
+    , decltype(_impl_.reserved_range_){arena}
+    , decltype(_impl_.reserved_name_){arena}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.options_){nullptr}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+EnumDescriptorProto::~EnumDescriptorProto() {
+  // @@protoc_insertion_point(destructor:google.protobuf.EnumDescriptorProto)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void EnumDescriptorProto::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.value_.~RepeatedPtrField();
+  _impl_.reserved_range_.~RepeatedPtrField();
+  _impl_.reserved_name_.~RepeatedPtrField();
+  _impl_.name_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.options_;
+}
+
+void EnumDescriptorProto::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void EnumDescriptorProto::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.value_.Clear();
+  _impl_.reserved_range_.Clear();
+  _impl_.reserved_name_.Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.name_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      GOOGLE_DCHECK(_impl_.options_ != nullptr);
+      _impl_.options_->Clear();
+    }
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* EnumDescriptorProto::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.EnumDescriptorProto.name");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.EnumValueDescriptorProto value = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_value(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.EnumOptions options = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          ptr = ctx->ParseMessage(_internal_mutable_options(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.EnumDescriptorProto.EnumReservedRange reserved_range = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_reserved_range(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<34>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated string reserved_name = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            auto str = _internal_add_reserved_name();
+            ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+            CHK_(ptr);
+            #ifndef NDEBUG
+            ::_pbi::VerifyUTF8(str, "google.protobuf.EnumDescriptorProto.reserved_name");
+            #endif  // !NDEBUG
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<42>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* EnumDescriptorProto::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.EnumDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string name = 1;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.EnumDescriptorProto.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // repeated .google.protobuf.EnumValueDescriptorProto value = 2;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_value_size()); i < n; i++) {
+    const auto& repfield = this->_internal_value(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(2, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // optional .google.protobuf.EnumOptions options = 3;
+  if (cached_has_bits & 0x00000002u) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(3, _Internal::options(this),
+        _Internal::options(this).GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.EnumDescriptorProto.EnumReservedRange reserved_range = 4;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_reserved_range_size()); i < n; i++) {
+    const auto& repfield = this->_internal_reserved_range(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(4, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated string reserved_name = 5;
+  for (int i = 0, n = this->_internal_reserved_name_size(); i < n; i++) {
+    const auto& s = this->_internal_reserved_name(i);
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      s.data(), static_cast<int>(s.length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.EnumDescriptorProto.reserved_name");
+    target = stream->WriteString(5, s, target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.EnumDescriptorProto)
+  return target;
+}
+
+size_t EnumDescriptorProto::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumDescriptorProto)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.EnumValueDescriptorProto value = 2;
+  total_size += 1UL * this->_internal_value_size();
+  for (const auto& msg : this->_impl_.value_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.EnumDescriptorProto.EnumReservedRange reserved_range = 4;
+  total_size += 1UL * this->_internal_reserved_range_size();
+  for (const auto& msg : this->_impl_.reserved_range_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated string reserved_name = 5;
+  total_size += 1 *
+      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(_impl_.reserved_name_.size());
+  for (int i = 0, n = _impl_.reserved_name_.size(); i < n; i++) {
+    total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+      _impl_.reserved_name_.Get(i));
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    // optional string name = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_name());
+    }
+
+    // optional .google.protobuf.EnumOptions options = 3;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.options_);
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumDescriptorProto::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    EnumDescriptorProto::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumDescriptorProto::GetClassData() const { return &_class_data_; }
+
+
+void EnumDescriptorProto::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<EnumDescriptorProto*>(&to_msg);
+  auto& from = static_cast<const EnumDescriptorProto&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumDescriptorProto)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.value_.MergeFrom(from._impl_.value_);
+  _this->_impl_.reserved_range_.MergeFrom(from._impl_.reserved_range_);
+  _this->_impl_.reserved_name_.MergeFrom(from._impl_.reserved_name_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_name(from._internal_name());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_mutable_options()->::PROTOBUF_NAMESPACE_ID::EnumOptions::MergeFrom(
+          from._internal_options());
+    }
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void EnumDescriptorProto::CopyFrom(const EnumDescriptorProto& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumDescriptorProto)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool EnumDescriptorProto::IsInitialized() const {
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.value_))
+    return false;
+  if (_internal_has_options()) {
+    if (!_impl_.options_->IsInitialized()) return false;
+  }
+  return true;
+}
+
+void EnumDescriptorProto::InternalSwap(EnumDescriptorProto* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.value_.InternalSwap(&other->_impl_.value_);
+  _impl_.reserved_range_.InternalSwap(&other->_impl_.reserved_range_);
+  _impl_.reserved_name_.InternalSwap(&other->_impl_.reserved_name_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  swap(_impl_.options_, other->_impl_.options_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata EnumDescriptorProto::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[9]);
+}
+
+// ===================================================================
+
+class EnumValueDescriptorProto::_Internal {
+ public:
+  using HasBits = decltype(std::declval<EnumValueDescriptorProto>()._impl_._has_bits_);
+  static void set_has_name(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_number(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::EnumValueOptions& options(const EnumValueDescriptorProto* msg);
+  static void set_has_options(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+};
+
+const ::PROTOBUF_NAMESPACE_ID::EnumValueOptions&
+EnumValueDescriptorProto::_Internal::options(const EnumValueDescriptorProto* msg) {
+  return *msg->_impl_.options_;
+}
+EnumValueDescriptorProto::EnumValueDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.EnumValueDescriptorProto)
+}
+EnumValueDescriptorProto::EnumValueDescriptorProto(const EnumValueDescriptorProto& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  EnumValueDescriptorProto* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.options_){nullptr}
+    , decltype(_impl_.number_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_name()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_options()) {
+    _this->_impl_.options_ = new ::PROTOBUF_NAMESPACE_ID::EnumValueOptions(*from._impl_.options_);
+  }
+  _this->_impl_.number_ = from._impl_.number_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.EnumValueDescriptorProto)
+}
+
+inline void EnumValueDescriptorProto::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.options_){nullptr}
+    , decltype(_impl_.number_){0}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+EnumValueDescriptorProto::~EnumValueDescriptorProto() {
+  // @@protoc_insertion_point(destructor:google.protobuf.EnumValueDescriptorProto)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void EnumValueDescriptorProto::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.name_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.options_;
+}
+
+void EnumValueDescriptorProto::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void EnumValueDescriptorProto::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumValueDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.name_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      GOOGLE_DCHECK(_impl_.options_ != nullptr);
+      _impl_.options_->Clear();
+    }
+  }
+  _impl_.number_ = 0;
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* EnumValueDescriptorProto::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.EnumValueDescriptorProto.name");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional int32 number = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _Internal::set_has_number(&has_bits);
+          _impl_.number_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.EnumValueOptions options = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          ptr = ctx->ParseMessage(_internal_mutable_options(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* EnumValueDescriptorProto::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.EnumValueDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string name = 1;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.EnumValueDescriptorProto.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // optional int32 number = 2;
+  if (cached_has_bits & 0x00000004u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_number(), target);
+  }
+
+  // optional .google.protobuf.EnumValueOptions options = 3;
+  if (cached_has_bits & 0x00000002u) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(3, _Internal::options(this),
+        _Internal::options(this).GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.EnumValueDescriptorProto)
+  return target;
+}
+
+size_t EnumValueDescriptorProto::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumValueDescriptorProto)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000007u) {
+    // optional string name = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_name());
+    }
+
+    // optional .google.protobuf.EnumValueOptions options = 3;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.options_);
+    }
+
+    // optional int32 number = 2;
+    if (cached_has_bits & 0x00000004u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_number());
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumValueDescriptorProto::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    EnumValueDescriptorProto::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumValueDescriptorProto::GetClassData() const { return &_class_data_; }
+
+
+void EnumValueDescriptorProto::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<EnumValueDescriptorProto*>(&to_msg);
+  auto& from = static_cast<const EnumValueDescriptorProto&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumValueDescriptorProto)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000007u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_name(from._internal_name());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_mutable_options()->::PROTOBUF_NAMESPACE_ID::EnumValueOptions::MergeFrom(
+          from._internal_options());
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _this->_impl_.number_ = from._impl_.number_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void EnumValueDescriptorProto::CopyFrom(const EnumValueDescriptorProto& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumValueDescriptorProto)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool EnumValueDescriptorProto::IsInitialized() const {
+  if (_internal_has_options()) {
+    if (!_impl_.options_->IsInitialized()) return false;
+  }
+  return true;
+}
+
+void EnumValueDescriptorProto::InternalSwap(EnumValueDescriptorProto* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(EnumValueDescriptorProto, _impl_.number_)
+      + sizeof(EnumValueDescriptorProto::_impl_.number_)
+      - PROTOBUF_FIELD_OFFSET(EnumValueDescriptorProto, _impl_.options_)>(
+          reinterpret_cast<char*>(&_impl_.options_),
+          reinterpret_cast<char*>(&other->_impl_.options_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata EnumValueDescriptorProto::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[10]);
+}
+
+// ===================================================================
+
+class ServiceDescriptorProto::_Internal {
+ public:
+  using HasBits = decltype(std::declval<ServiceDescriptorProto>()._impl_._has_bits_);
+  static void set_has_name(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::ServiceOptions& options(const ServiceDescriptorProto* msg);
+  static void set_has_options(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+};
+
+const ::PROTOBUF_NAMESPACE_ID::ServiceOptions&
+ServiceDescriptorProto::_Internal::options(const ServiceDescriptorProto* msg) {
+  return *msg->_impl_.options_;
+}
+ServiceDescriptorProto::ServiceDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.ServiceDescriptorProto)
+}
+ServiceDescriptorProto::ServiceDescriptorProto(const ServiceDescriptorProto& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  ServiceDescriptorProto* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.method_){from._impl_.method_}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.options_){nullptr}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_name()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_options()) {
+    _this->_impl_.options_ = new ::PROTOBUF_NAMESPACE_ID::ServiceOptions(*from._impl_.options_);
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.ServiceDescriptorProto)
+}
+
+inline void ServiceDescriptorProto::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.method_){arena}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.options_){nullptr}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+ServiceDescriptorProto::~ServiceDescriptorProto() {
+  // @@protoc_insertion_point(destructor:google.protobuf.ServiceDescriptorProto)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void ServiceDescriptorProto::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.method_.~RepeatedPtrField();
+  _impl_.name_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.options_;
+}
+
+void ServiceDescriptorProto::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void ServiceDescriptorProto::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.ServiceDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.method_.Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.name_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      GOOGLE_DCHECK(_impl_.options_ != nullptr);
+      _impl_.options_->Clear();
+    }
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* ServiceDescriptorProto::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.ServiceDescriptorProto.name");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.MethodDescriptorProto method = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_method(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.ServiceOptions options = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          ptr = ctx->ParseMessage(_internal_mutable_options(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* ServiceDescriptorProto::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.ServiceDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string name = 1;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.ServiceDescriptorProto.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // repeated .google.protobuf.MethodDescriptorProto method = 2;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_method_size()); i < n; i++) {
+    const auto& repfield = this->_internal_method(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(2, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // optional .google.protobuf.ServiceOptions options = 3;
+  if (cached_has_bits & 0x00000002u) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(3, _Internal::options(this),
+        _Internal::options(this).GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.ServiceDescriptorProto)
+  return target;
+}
+
+size_t ServiceDescriptorProto::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.ServiceDescriptorProto)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.MethodDescriptorProto method = 2;
+  total_size += 1UL * this->_internal_method_size();
+  for (const auto& msg : this->_impl_.method_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    // optional string name = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_name());
+    }
+
+    // optional .google.protobuf.ServiceOptions options = 3;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.options_);
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ServiceDescriptorProto::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    ServiceDescriptorProto::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ServiceDescriptorProto::GetClassData() const { return &_class_data_; }
+
+
+void ServiceDescriptorProto::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<ServiceDescriptorProto*>(&to_msg);
+  auto& from = static_cast<const ServiceDescriptorProto&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.ServiceDescriptorProto)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.method_.MergeFrom(from._impl_.method_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_name(from._internal_name());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_mutable_options()->::PROTOBUF_NAMESPACE_ID::ServiceOptions::MergeFrom(
+          from._internal_options());
+    }
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void ServiceDescriptorProto::CopyFrom(const ServiceDescriptorProto& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.ServiceDescriptorProto)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool ServiceDescriptorProto::IsInitialized() const {
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.method_))
+    return false;
+  if (_internal_has_options()) {
+    if (!_impl_.options_->IsInitialized()) return false;
+  }
+  return true;
+}
+
+void ServiceDescriptorProto::InternalSwap(ServiceDescriptorProto* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.method_.InternalSwap(&other->_impl_.method_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  swap(_impl_.options_, other->_impl_.options_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata ServiceDescriptorProto::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[11]);
+}
+
+// ===================================================================
+
+class MethodDescriptorProto::_Internal {
+ public:
+  using HasBits = decltype(std::declval<MethodDescriptorProto>()._impl_._has_bits_);
+  static void set_has_name(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_input_type(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static void set_has_output_type(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::MethodOptions& options(const MethodDescriptorProto* msg);
+  static void set_has_options(HasBits* has_bits) {
+    (*has_bits)[0] |= 8u;
+  }
+  static void set_has_client_streaming(HasBits* has_bits) {
+    (*has_bits)[0] |= 16u;
+  }
+  static void set_has_server_streaming(HasBits* has_bits) {
+    (*has_bits)[0] |= 32u;
+  }
+};
+
+const ::PROTOBUF_NAMESPACE_ID::MethodOptions&
+MethodDescriptorProto::_Internal::options(const MethodDescriptorProto* msg) {
+  return *msg->_impl_.options_;
+}
+MethodDescriptorProto::MethodDescriptorProto(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.MethodDescriptorProto)
+}
+MethodDescriptorProto::MethodDescriptorProto(const MethodDescriptorProto& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  MethodDescriptorProto* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.input_type_){}
+    , decltype(_impl_.output_type_){}
+    , decltype(_impl_.options_){nullptr}
+    , decltype(_impl_.client_streaming_){}
+    , decltype(_impl_.server_streaming_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_name()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.input_type_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.input_type_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_input_type()) {
+    _this->_impl_.input_type_.Set(from._internal_input_type(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.output_type_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.output_type_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_output_type()) {
+    _this->_impl_.output_type_.Set(from._internal_output_type(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_options()) {
+    _this->_impl_.options_ = new ::PROTOBUF_NAMESPACE_ID::MethodOptions(*from._impl_.options_);
+  }
+  ::memcpy(&_impl_.client_streaming_, &from._impl_.client_streaming_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.server_streaming_) -
+    reinterpret_cast<char*>(&_impl_.client_streaming_)) + sizeof(_impl_.server_streaming_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.MethodDescriptorProto)
+}
+
+inline void MethodDescriptorProto::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.input_type_){}
+    , decltype(_impl_.output_type_){}
+    , decltype(_impl_.options_){nullptr}
+    , decltype(_impl_.client_streaming_){false}
+    , decltype(_impl_.server_streaming_){false}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.input_type_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.input_type_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.output_type_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.output_type_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+MethodDescriptorProto::~MethodDescriptorProto() {
+  // @@protoc_insertion_point(destructor:google.protobuf.MethodDescriptorProto)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void MethodDescriptorProto::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.name_.Destroy();
+  _impl_.input_type_.Destroy();
+  _impl_.output_type_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.options_;
+}
+
+void MethodDescriptorProto::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void MethodDescriptorProto::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.MethodDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000000fu) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.name_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _impl_.input_type_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _impl_.output_type_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000008u) {
+      GOOGLE_DCHECK(_impl_.options_ != nullptr);
+      _impl_.options_->Clear();
+    }
+  }
+  ::memset(&_impl_.client_streaming_, 0, static_cast<size_t>(
+      reinterpret_cast<char*>(&_impl_.server_streaming_) -
+      reinterpret_cast<char*>(&_impl_.client_streaming_)) + sizeof(_impl_.server_streaming_));
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* MethodDescriptorProto::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.MethodDescriptorProto.name");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string input_type = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          auto str = _internal_mutable_input_type();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.MethodDescriptorProto.input_type");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string output_type = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          auto str = _internal_mutable_output_type();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.MethodDescriptorProto.output_type");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.MethodOptions options = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          ptr = ctx->ParseMessage(_internal_mutable_options(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool client_streaming = 5 [default = false];
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 40)) {
+          _Internal::set_has_client_streaming(&has_bits);
+          _impl_.client_streaming_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool server_streaming = 6 [default = false];
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 48)) {
+          _Internal::set_has_server_streaming(&has_bits);
+          _impl_.server_streaming_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* MethodDescriptorProto::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.MethodDescriptorProto)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string name = 1;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.MethodDescriptorProto.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // optional string input_type = 2;
+  if (cached_has_bits & 0x00000002u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_input_type().data(), static_cast<int>(this->_internal_input_type().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.MethodDescriptorProto.input_type");
+    target = stream->WriteStringMaybeAliased(
+        2, this->_internal_input_type(), target);
+  }
+
+  // optional string output_type = 3;
+  if (cached_has_bits & 0x00000004u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_output_type().data(), static_cast<int>(this->_internal_output_type().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.MethodDescriptorProto.output_type");
+    target = stream->WriteStringMaybeAliased(
+        3, this->_internal_output_type(), target);
+  }
+
+  // optional .google.protobuf.MethodOptions options = 4;
+  if (cached_has_bits & 0x00000008u) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(4, _Internal::options(this),
+        _Internal::options(this).GetCachedSize(), target, stream);
+  }
+
+  // optional bool client_streaming = 5 [default = false];
+  if (cached_has_bits & 0x00000010u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(5, this->_internal_client_streaming(), target);
+  }
+
+  // optional bool server_streaming = 6 [default = false];
+  if (cached_has_bits & 0x00000020u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(6, this->_internal_server_streaming(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.MethodDescriptorProto)
+  return target;
+}
+
+size_t MethodDescriptorProto::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.MethodDescriptorProto)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000003fu) {
+    // optional string name = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_name());
+    }
+
+    // optional string input_type = 2;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_input_type());
+    }
+
+    // optional string output_type = 3;
+    if (cached_has_bits & 0x00000004u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_output_type());
+    }
+
+    // optional .google.protobuf.MethodOptions options = 4;
+    if (cached_has_bits & 0x00000008u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.options_);
+    }
+
+    // optional bool client_streaming = 5 [default = false];
+    if (cached_has_bits & 0x00000010u) {
+      total_size += 1 + 1;
+    }
+
+    // optional bool server_streaming = 6 [default = false];
+    if (cached_has_bits & 0x00000020u) {
+      total_size += 1 + 1;
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData MethodDescriptorProto::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    MethodDescriptorProto::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*MethodDescriptorProto::GetClassData() const { return &_class_data_; }
+
+
+void MethodDescriptorProto::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<MethodDescriptorProto*>(&to_msg);
+  auto& from = static_cast<const MethodDescriptorProto&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.MethodDescriptorProto)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000003fu) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_name(from._internal_name());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_set_input_type(from._internal_input_type());
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _this->_internal_set_output_type(from._internal_output_type());
+    }
+    if (cached_has_bits & 0x00000008u) {
+      _this->_internal_mutable_options()->::PROTOBUF_NAMESPACE_ID::MethodOptions::MergeFrom(
+          from._internal_options());
+    }
+    if (cached_has_bits & 0x00000010u) {
+      _this->_impl_.client_streaming_ = from._impl_.client_streaming_;
+    }
+    if (cached_has_bits & 0x00000020u) {
+      _this->_impl_.server_streaming_ = from._impl_.server_streaming_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void MethodDescriptorProto::CopyFrom(const MethodDescriptorProto& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.MethodDescriptorProto)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool MethodDescriptorProto::IsInitialized() const {
+  if (_internal_has_options()) {
+    if (!_impl_.options_->IsInitialized()) return false;
+  }
+  return true;
+}
+
+void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.input_type_, lhs_arena,
+      &other->_impl_.input_type_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.output_type_, lhs_arena,
+      &other->_impl_.output_type_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(MethodDescriptorProto, _impl_.server_streaming_)
+      + sizeof(MethodDescriptorProto::_impl_.server_streaming_)
+      - PROTOBUF_FIELD_OFFSET(MethodDescriptorProto, _impl_.options_)>(
+          reinterpret_cast<char*>(&_impl_.options_),
+          reinterpret_cast<char*>(&other->_impl_.options_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata MethodDescriptorProto::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[12]);
+}
+
+// ===================================================================
+
+class FileOptions::_Internal {
+ public:
+  using HasBits = decltype(std::declval<FileOptions>()._impl_._has_bits_);
+  static void set_has_java_package(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_java_outer_classname(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static void set_has_java_multiple_files(HasBits* has_bits) {
+    (*has_bits)[0] |= 1024u;
+  }
+  static void set_has_java_generate_equals_and_hash(HasBits* has_bits) {
+    (*has_bits)[0] |= 2048u;
+  }
+  static void set_has_java_string_check_utf8(HasBits* has_bits) {
+    (*has_bits)[0] |= 4096u;
+  }
+  static void set_has_optimize_for(HasBits* has_bits) {
+    (*has_bits)[0] |= 262144u;
+  }
+  static void set_has_go_package(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+  static void set_has_cc_generic_services(HasBits* has_bits) {
+    (*has_bits)[0] |= 8192u;
+  }
+  static void set_has_java_generic_services(HasBits* has_bits) {
+    (*has_bits)[0] |= 16384u;
+  }
+  static void set_has_py_generic_services(HasBits* has_bits) {
+    (*has_bits)[0] |= 32768u;
+  }
+  static void set_has_php_generic_services(HasBits* has_bits) {
+    (*has_bits)[0] |= 65536u;
+  }
+  static void set_has_deprecated(HasBits* has_bits) {
+    (*has_bits)[0] |= 131072u;
+  }
+  static void set_has_cc_enable_arenas(HasBits* has_bits) {
+    (*has_bits)[0] |= 524288u;
+  }
+  static void set_has_objc_class_prefix(HasBits* has_bits) {
+    (*has_bits)[0] |= 8u;
+  }
+  static void set_has_csharp_namespace(HasBits* has_bits) {
+    (*has_bits)[0] |= 16u;
+  }
+  static void set_has_swift_prefix(HasBits* has_bits) {
+    (*has_bits)[0] |= 32u;
+  }
+  static void set_has_php_class_prefix(HasBits* has_bits) {
+    (*has_bits)[0] |= 64u;
+  }
+  static void set_has_php_namespace(HasBits* has_bits) {
+    (*has_bits)[0] |= 128u;
+  }
+  static void set_has_php_metadata_namespace(HasBits* has_bits) {
+    (*has_bits)[0] |= 256u;
+  }
+  static void set_has_ruby_package(HasBits* has_bits) {
+    (*has_bits)[0] |= 512u;
+  }
+};
+
+FileOptions::FileOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.FileOptions)
+}
+FileOptions::FileOptions(const FileOptions& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  FileOptions* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{}
+    , decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){from._impl_.uninterpreted_option_}
+    , decltype(_impl_.java_package_){}
+    , decltype(_impl_.java_outer_classname_){}
+    , decltype(_impl_.go_package_){}
+    , decltype(_impl_.objc_class_prefix_){}
+    , decltype(_impl_.csharp_namespace_){}
+    , decltype(_impl_.swift_prefix_){}
+    , decltype(_impl_.php_class_prefix_){}
+    , decltype(_impl_.php_namespace_){}
+    , decltype(_impl_.php_metadata_namespace_){}
+    , decltype(_impl_.ruby_package_){}
+    , decltype(_impl_.java_multiple_files_){}
+    , decltype(_impl_.java_generate_equals_and_hash_){}
+    , decltype(_impl_.java_string_check_utf8_){}
+    , decltype(_impl_.cc_generic_services_){}
+    , decltype(_impl_.java_generic_services_){}
+    , decltype(_impl_.py_generic_services_){}
+    , decltype(_impl_.php_generic_services_){}
+    , decltype(_impl_.deprecated_){}
+    , decltype(_impl_.optimize_for_){}
+    , decltype(_impl_.cc_enable_arenas_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _impl_.java_package_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.java_package_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_java_package()) {
+    _this->_impl_.java_package_.Set(from._internal_java_package(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.java_outer_classname_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.java_outer_classname_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_java_outer_classname()) {
+    _this->_impl_.java_outer_classname_.Set(from._internal_java_outer_classname(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.go_package_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.go_package_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_go_package()) {
+    _this->_impl_.go_package_.Set(from._internal_go_package(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.objc_class_prefix_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.objc_class_prefix_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_objc_class_prefix()) {
+    _this->_impl_.objc_class_prefix_.Set(from._internal_objc_class_prefix(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.csharp_namespace_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.csharp_namespace_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_csharp_namespace()) {
+    _this->_impl_.csharp_namespace_.Set(from._internal_csharp_namespace(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.swift_prefix_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.swift_prefix_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_swift_prefix()) {
+    _this->_impl_.swift_prefix_.Set(from._internal_swift_prefix(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.php_class_prefix_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.php_class_prefix_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_php_class_prefix()) {
+    _this->_impl_.php_class_prefix_.Set(from._internal_php_class_prefix(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.php_namespace_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.php_namespace_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_php_namespace()) {
+    _this->_impl_.php_namespace_.Set(from._internal_php_namespace(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.php_metadata_namespace_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.php_metadata_namespace_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_php_metadata_namespace()) {
+    _this->_impl_.php_metadata_namespace_.Set(from._internal_php_metadata_namespace(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.ruby_package_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.ruby_package_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_ruby_package()) {
+    _this->_impl_.ruby_package_.Set(from._internal_ruby_package(), 
+      _this->GetArenaForAllocation());
+  }
+  ::memcpy(&_impl_.java_multiple_files_, &from._impl_.java_multiple_files_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.cc_enable_arenas_) -
+    reinterpret_cast<char*>(&_impl_.java_multiple_files_)) + sizeof(_impl_.cc_enable_arenas_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.FileOptions)
+}
+
+inline void FileOptions::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{::_pbi::ArenaInitialized(), arena}
+    , decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){arena}
+    , decltype(_impl_.java_package_){}
+    , decltype(_impl_.java_outer_classname_){}
+    , decltype(_impl_.go_package_){}
+    , decltype(_impl_.objc_class_prefix_){}
+    , decltype(_impl_.csharp_namespace_){}
+    , decltype(_impl_.swift_prefix_){}
+    , decltype(_impl_.php_class_prefix_){}
+    , decltype(_impl_.php_namespace_){}
+    , decltype(_impl_.php_metadata_namespace_){}
+    , decltype(_impl_.ruby_package_){}
+    , decltype(_impl_.java_multiple_files_){false}
+    , decltype(_impl_.java_generate_equals_and_hash_){false}
+    , decltype(_impl_.java_string_check_utf8_){false}
+    , decltype(_impl_.cc_generic_services_){false}
+    , decltype(_impl_.java_generic_services_){false}
+    , decltype(_impl_.py_generic_services_){false}
+    , decltype(_impl_.php_generic_services_){false}
+    , decltype(_impl_.deprecated_){false}
+    , decltype(_impl_.optimize_for_){1}
+    , decltype(_impl_.cc_enable_arenas_){true}
+  };
+  _impl_.java_package_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.java_package_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.java_outer_classname_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.java_outer_classname_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.go_package_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.go_package_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.objc_class_prefix_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.objc_class_prefix_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.csharp_namespace_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.csharp_namespace_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.swift_prefix_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.swift_prefix_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.php_class_prefix_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.php_class_prefix_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.php_namespace_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.php_namespace_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.php_metadata_namespace_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.php_metadata_namespace_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.ruby_package_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.ruby_package_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+FileOptions::~FileOptions() {
+  // @@protoc_insertion_point(destructor:google.protobuf.FileOptions)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void FileOptions::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_._extensions_.~ExtensionSet();
+  _impl_.uninterpreted_option_.~RepeatedPtrField();
+  _impl_.java_package_.Destroy();
+  _impl_.java_outer_classname_.Destroy();
+  _impl_.go_package_.Destroy();
+  _impl_.objc_class_prefix_.Destroy();
+  _impl_.csharp_namespace_.Destroy();
+  _impl_.swift_prefix_.Destroy();
+  _impl_.php_class_prefix_.Destroy();
+  _impl_.php_namespace_.Destroy();
+  _impl_.php_metadata_namespace_.Destroy();
+  _impl_.ruby_package_.Destroy();
+}
+
+void FileOptions::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void FileOptions::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.FileOptions)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_._extensions_.Clear();
+  _impl_.uninterpreted_option_.Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x000000ffu) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.java_package_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _impl_.java_outer_classname_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _impl_.go_package_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000008u) {
+      _impl_.objc_class_prefix_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000010u) {
+      _impl_.csharp_namespace_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000020u) {
+      _impl_.swift_prefix_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000040u) {
+      _impl_.php_class_prefix_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000080u) {
+      _impl_.php_namespace_.ClearNonDefaultToEmpty();
+    }
+  }
+  if (cached_has_bits & 0x00000300u) {
+    if (cached_has_bits & 0x00000100u) {
+      _impl_.php_metadata_namespace_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000200u) {
+      _impl_.ruby_package_.ClearNonDefaultToEmpty();
+    }
+  }
+  if (cached_has_bits & 0x0000fc00u) {
+    ::memset(&_impl_.java_multiple_files_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&_impl_.py_generic_services_) -
+        reinterpret_cast<char*>(&_impl_.java_multiple_files_)) + sizeof(_impl_.py_generic_services_));
+  }
+  if (cached_has_bits & 0x000f0000u) {
+    ::memset(&_impl_.php_generic_services_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&_impl_.deprecated_) -
+        reinterpret_cast<char*>(&_impl_.php_generic_services_)) + sizeof(_impl_.deprecated_));
+    _impl_.optimize_for_ = 1;
+    _impl_.cc_enable_arenas_ = true;
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* FileOptions::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional string java_package = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_java_package();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileOptions.java_package");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string java_outer_classname = 8;
+      case 8:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 66)) {
+          auto str = _internal_mutable_java_outer_classname();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileOptions.java_outer_classname");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED];
+      case 9:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 72)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode_IsValid(val))) {
+            _internal_set_optimize_for(static_cast<::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode>(val));
+          } else {
+            ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(9, val, mutable_unknown_fields());
+          }
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool java_multiple_files = 10 [default = false];
+      case 10:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 80)) {
+          _Internal::set_has_java_multiple_files(&has_bits);
+          _impl_.java_multiple_files_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string go_package = 11;
+      case 11:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 90)) {
+          auto str = _internal_mutable_go_package();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileOptions.go_package");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool cc_generic_services = 16 [default = false];
+      case 16:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 128)) {
+          _Internal::set_has_cc_generic_services(&has_bits);
+          _impl_.cc_generic_services_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool java_generic_services = 17 [default = false];
+      case 17:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 136)) {
+          _Internal::set_has_java_generic_services(&has_bits);
+          _impl_.java_generic_services_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool py_generic_services = 18 [default = false];
+      case 18:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 144)) {
+          _Internal::set_has_py_generic_services(&has_bits);
+          _impl_.py_generic_services_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool java_generate_equals_and_hash = 20 [deprecated = true];
+      case 20:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 160)) {
+          _Internal::set_has_java_generate_equals_and_hash(&has_bits);
+          _impl_.java_generate_equals_and_hash_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool deprecated = 23 [default = false];
+      case 23:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 184)) {
+          _Internal::set_has_deprecated(&has_bits);
+          _impl_.deprecated_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool java_string_check_utf8 = 27 [default = false];
+      case 27:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 216)) {
+          _Internal::set_has_java_string_check_utf8(&has_bits);
+          _impl_.java_string_check_utf8_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool cc_enable_arenas = 31 [default = true];
+      case 31:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 248)) {
+          _Internal::set_has_cc_enable_arenas(&has_bits);
+          _impl_.cc_enable_arenas_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string objc_class_prefix = 36;
+      case 36:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          auto str = _internal_mutable_objc_class_prefix();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileOptions.objc_class_prefix");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string csharp_namespace = 37;
+      case 37:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+          auto str = _internal_mutable_csharp_namespace();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileOptions.csharp_namespace");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string swift_prefix = 39;
+      case 39:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          auto str = _internal_mutable_swift_prefix();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileOptions.swift_prefix");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string php_class_prefix = 40;
+      case 40:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 66)) {
+          auto str = _internal_mutable_php_class_prefix();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileOptions.php_class_prefix");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string php_namespace = 41;
+      case 41:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 74)) {
+          auto str = _internal_mutable_php_namespace();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileOptions.php_namespace");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool php_generic_services = 42 [default = false];
+      case 42:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 80)) {
+          _Internal::set_has_php_generic_services(&has_bits);
+          _impl_.php_generic_services_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string php_metadata_namespace = 44;
+      case 44:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 98)) {
+          auto str = _internal_mutable_php_metadata_namespace();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileOptions.php_metadata_namespace");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string ruby_package = 45;
+      case 45:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 106)) {
+          auto str = _internal_mutable_ruby_package();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.FileOptions.ruby_package");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+      case 999:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr -= 2;
+          do {
+            ptr += 2;
+            ptr = ctx->ParseMessage(_internal_add_uninterpreted_option(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<7994>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    if ((8000u <= tag)) {
+      ptr = _impl_._extensions_.ParseField(tag, ptr, internal_default_instance(), &_internal_metadata_, ctx);
+      CHK_(ptr != nullptr);
+      continue;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* FileOptions::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.FileOptions)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string java_package = 1;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_java_package().data(), static_cast<int>(this->_internal_java_package().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileOptions.java_package");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_java_package(), target);
+  }
+
+  // optional string java_outer_classname = 8;
+  if (cached_has_bits & 0x00000002u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_java_outer_classname().data(), static_cast<int>(this->_internal_java_outer_classname().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileOptions.java_outer_classname");
+    target = stream->WriteStringMaybeAliased(
+        8, this->_internal_java_outer_classname(), target);
+  }
+
+  // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED];
+  if (cached_has_bits & 0x00040000u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      9, this->_internal_optimize_for(), target);
+  }
+
+  // optional bool java_multiple_files = 10 [default = false];
+  if (cached_has_bits & 0x00000400u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(10, this->_internal_java_multiple_files(), target);
+  }
+
+  // optional string go_package = 11;
+  if (cached_has_bits & 0x00000004u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_go_package().data(), static_cast<int>(this->_internal_go_package().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileOptions.go_package");
+    target = stream->WriteStringMaybeAliased(
+        11, this->_internal_go_package(), target);
+  }
+
+  // optional bool cc_generic_services = 16 [default = false];
+  if (cached_has_bits & 0x00002000u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(16, this->_internal_cc_generic_services(), target);
+  }
+
+  // optional bool java_generic_services = 17 [default = false];
+  if (cached_has_bits & 0x00004000u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(17, this->_internal_java_generic_services(), target);
+  }
+
+  // optional bool py_generic_services = 18 [default = false];
+  if (cached_has_bits & 0x00008000u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(18, this->_internal_py_generic_services(), target);
+  }
+
+  // optional bool java_generate_equals_and_hash = 20 [deprecated = true];
+  if (cached_has_bits & 0x00000800u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(20, this->_internal_java_generate_equals_and_hash(), target);
+  }
+
+  // optional bool deprecated = 23 [default = false];
+  if (cached_has_bits & 0x00020000u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(23, this->_internal_deprecated(), target);
+  }
+
+  // optional bool java_string_check_utf8 = 27 [default = false];
+  if (cached_has_bits & 0x00001000u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(27, this->_internal_java_string_check_utf8(), target);
+  }
+
+  // optional bool cc_enable_arenas = 31 [default = true];
+  if (cached_has_bits & 0x00080000u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(31, this->_internal_cc_enable_arenas(), target);
+  }
+
+  // optional string objc_class_prefix = 36;
+  if (cached_has_bits & 0x00000008u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_objc_class_prefix().data(), static_cast<int>(this->_internal_objc_class_prefix().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileOptions.objc_class_prefix");
+    target = stream->WriteStringMaybeAliased(
+        36, this->_internal_objc_class_prefix(), target);
+  }
+
+  // optional string csharp_namespace = 37;
+  if (cached_has_bits & 0x00000010u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_csharp_namespace().data(), static_cast<int>(this->_internal_csharp_namespace().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileOptions.csharp_namespace");
+    target = stream->WriteStringMaybeAliased(
+        37, this->_internal_csharp_namespace(), target);
+  }
+
+  // optional string swift_prefix = 39;
+  if (cached_has_bits & 0x00000020u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_swift_prefix().data(), static_cast<int>(this->_internal_swift_prefix().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileOptions.swift_prefix");
+    target = stream->WriteStringMaybeAliased(
+        39, this->_internal_swift_prefix(), target);
+  }
+
+  // optional string php_class_prefix = 40;
+  if (cached_has_bits & 0x00000040u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_php_class_prefix().data(), static_cast<int>(this->_internal_php_class_prefix().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileOptions.php_class_prefix");
+    target = stream->WriteStringMaybeAliased(
+        40, this->_internal_php_class_prefix(), target);
+  }
+
+  // optional string php_namespace = 41;
+  if (cached_has_bits & 0x00000080u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_php_namespace().data(), static_cast<int>(this->_internal_php_namespace().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileOptions.php_namespace");
+    target = stream->WriteStringMaybeAliased(
+        41, this->_internal_php_namespace(), target);
+  }
+
+  // optional bool php_generic_services = 42 [default = false];
+  if (cached_has_bits & 0x00010000u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(42, this->_internal_php_generic_services(), target);
+  }
+
+  // optional string php_metadata_namespace = 44;
+  if (cached_has_bits & 0x00000100u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_php_metadata_namespace().data(), static_cast<int>(this->_internal_php_metadata_namespace().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileOptions.php_metadata_namespace");
+    target = stream->WriteStringMaybeAliased(
+        44, this->_internal_php_metadata_namespace(), target);
+  }
+
+  // optional string ruby_package = 45;
+  if (cached_has_bits & 0x00000200u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_ruby_package().data(), static_cast<int>(this->_internal_ruby_package().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.FileOptions.ruby_package");
+    target = stream->WriteStringMaybeAliased(
+        45, this->_internal_ruby_package(), target);
+  }
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_uninterpreted_option_size()); i < n; i++) {
+    const auto& repfield = this->_internal_uninterpreted_option(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(999, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // Extension range [1000, 536870912)
+  target = _impl_._extensions_._InternalSerialize(
+  internal_default_instance(), 1000, 536870912, target, stream);
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.FileOptions)
+  return target;
+}
+
+size_t FileOptions::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FileOptions)
+  size_t total_size = 0;
+
+  total_size += _impl_._extensions_.ByteSize();
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  total_size += 2UL * this->_internal_uninterpreted_option_size();
+  for (const auto& msg : this->_impl_.uninterpreted_option_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x000000ffu) {
+    // optional string java_package = 1;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_java_package());
+    }
+
+    // optional string java_outer_classname = 8;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_java_outer_classname());
+    }
+
+    // optional string go_package = 11;
+    if (cached_has_bits & 0x00000004u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_go_package());
+    }
+
+    // optional string objc_class_prefix = 36;
+    if (cached_has_bits & 0x00000008u) {
+      total_size += 2 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_objc_class_prefix());
+    }
+
+    // optional string csharp_namespace = 37;
+    if (cached_has_bits & 0x00000010u) {
+      total_size += 2 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_csharp_namespace());
+    }
+
+    // optional string swift_prefix = 39;
+    if (cached_has_bits & 0x00000020u) {
+      total_size += 2 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_swift_prefix());
+    }
+
+    // optional string php_class_prefix = 40;
+    if (cached_has_bits & 0x00000040u) {
+      total_size += 2 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_php_class_prefix());
+    }
+
+    // optional string php_namespace = 41;
+    if (cached_has_bits & 0x00000080u) {
+      total_size += 2 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_php_namespace());
+    }
+
+  }
+  if (cached_has_bits & 0x0000ff00u) {
+    // optional string php_metadata_namespace = 44;
+    if (cached_has_bits & 0x00000100u) {
+      total_size += 2 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_php_metadata_namespace());
+    }
+
+    // optional string ruby_package = 45;
+    if (cached_has_bits & 0x00000200u) {
+      total_size += 2 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_ruby_package());
+    }
+
+    // optional bool java_multiple_files = 10 [default = false];
+    if (cached_has_bits & 0x00000400u) {
+      total_size += 1 + 1;
+    }
+
+    // optional bool java_generate_equals_and_hash = 20 [deprecated = true];
+    if (cached_has_bits & 0x00000800u) {
+      total_size += 2 + 1;
+    }
+
+    // optional bool java_string_check_utf8 = 27 [default = false];
+    if (cached_has_bits & 0x00001000u) {
+      total_size += 2 + 1;
+    }
+
+    // optional bool cc_generic_services = 16 [default = false];
+    if (cached_has_bits & 0x00002000u) {
+      total_size += 2 + 1;
+    }
+
+    // optional bool java_generic_services = 17 [default = false];
+    if (cached_has_bits & 0x00004000u) {
+      total_size += 2 + 1;
+    }
+
+    // optional bool py_generic_services = 18 [default = false];
+    if (cached_has_bits & 0x00008000u) {
+      total_size += 2 + 1;
+    }
+
+  }
+  if (cached_has_bits & 0x000f0000u) {
+    // optional bool php_generic_services = 42 [default = false];
+    if (cached_has_bits & 0x00010000u) {
+      total_size += 2 + 1;
+    }
+
+    // optional bool deprecated = 23 [default = false];
+    if (cached_has_bits & 0x00020000u) {
+      total_size += 2 + 1;
+    }
+
+    // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED];
+    if (cached_has_bits & 0x00040000u) {
+      total_size += 1 +
+        ::_pbi::WireFormatLite::EnumSize(this->_internal_optimize_for());
+    }
+
+    // optional bool cc_enable_arenas = 31 [default = true];
+    if (cached_has_bits & 0x00080000u) {
+      total_size += 2 + 1;
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FileOptions::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    FileOptions::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FileOptions::GetClassData() const { return &_class_data_; }
+
+
+void FileOptions::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<FileOptions*>(&to_msg);
+  auto& from = static_cast<const FileOptions&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FileOptions)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.uninterpreted_option_.MergeFrom(from._impl_.uninterpreted_option_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x000000ffu) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_java_package(from._internal_java_package());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_set_java_outer_classname(from._internal_java_outer_classname());
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _this->_internal_set_go_package(from._internal_go_package());
+    }
+    if (cached_has_bits & 0x00000008u) {
+      _this->_internal_set_objc_class_prefix(from._internal_objc_class_prefix());
+    }
+    if (cached_has_bits & 0x00000010u) {
+      _this->_internal_set_csharp_namespace(from._internal_csharp_namespace());
+    }
+    if (cached_has_bits & 0x00000020u) {
+      _this->_internal_set_swift_prefix(from._internal_swift_prefix());
+    }
+    if (cached_has_bits & 0x00000040u) {
+      _this->_internal_set_php_class_prefix(from._internal_php_class_prefix());
+    }
+    if (cached_has_bits & 0x00000080u) {
+      _this->_internal_set_php_namespace(from._internal_php_namespace());
+    }
+  }
+  if (cached_has_bits & 0x0000ff00u) {
+    if (cached_has_bits & 0x00000100u) {
+      _this->_internal_set_php_metadata_namespace(from._internal_php_metadata_namespace());
+    }
+    if (cached_has_bits & 0x00000200u) {
+      _this->_internal_set_ruby_package(from._internal_ruby_package());
+    }
+    if (cached_has_bits & 0x00000400u) {
+      _this->_impl_.java_multiple_files_ = from._impl_.java_multiple_files_;
+    }
+    if (cached_has_bits & 0x00000800u) {
+      _this->_impl_.java_generate_equals_and_hash_ = from._impl_.java_generate_equals_and_hash_;
+    }
+    if (cached_has_bits & 0x00001000u) {
+      _this->_impl_.java_string_check_utf8_ = from._impl_.java_string_check_utf8_;
+    }
+    if (cached_has_bits & 0x00002000u) {
+      _this->_impl_.cc_generic_services_ = from._impl_.cc_generic_services_;
+    }
+    if (cached_has_bits & 0x00004000u) {
+      _this->_impl_.java_generic_services_ = from._impl_.java_generic_services_;
+    }
+    if (cached_has_bits & 0x00008000u) {
+      _this->_impl_.py_generic_services_ = from._impl_.py_generic_services_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  if (cached_has_bits & 0x000f0000u) {
+    if (cached_has_bits & 0x00010000u) {
+      _this->_impl_.php_generic_services_ = from._impl_.php_generic_services_;
+    }
+    if (cached_has_bits & 0x00020000u) {
+      _this->_impl_.deprecated_ = from._impl_.deprecated_;
+    }
+    if (cached_has_bits & 0x00040000u) {
+      _this->_impl_.optimize_for_ = from._impl_.optimize_for_;
+    }
+    if (cached_has_bits & 0x00080000u) {
+      _this->_impl_.cc_enable_arenas_ = from._impl_.cc_enable_arenas_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void FileOptions::CopyFrom(const FileOptions& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FileOptions)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool FileOptions::IsInitialized() const {
+  if (!_impl_._extensions_.IsInitialized()) {
+    return false;
+  }
+
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.uninterpreted_option_))
+    return false;
+  return true;
+}
+
+void FileOptions::InternalSwap(FileOptions* other) {
+  using std::swap;
+  _impl_._extensions_.InternalSwap(&other->_impl_._extensions_);
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.uninterpreted_option_.InternalSwap(&other->_impl_.uninterpreted_option_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.java_package_, lhs_arena,
+      &other->_impl_.java_package_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.java_outer_classname_, lhs_arena,
+      &other->_impl_.java_outer_classname_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.go_package_, lhs_arena,
+      &other->_impl_.go_package_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.objc_class_prefix_, lhs_arena,
+      &other->_impl_.objc_class_prefix_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.csharp_namespace_, lhs_arena,
+      &other->_impl_.csharp_namespace_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.swift_prefix_, lhs_arena,
+      &other->_impl_.swift_prefix_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.php_class_prefix_, lhs_arena,
+      &other->_impl_.php_class_prefix_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.php_namespace_, lhs_arena,
+      &other->_impl_.php_namespace_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.php_metadata_namespace_, lhs_arena,
+      &other->_impl_.php_metadata_namespace_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.ruby_package_, lhs_arena,
+      &other->_impl_.ruby_package_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.deprecated_)
+      + sizeof(FileOptions::_impl_.deprecated_)
+      - PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.java_multiple_files_)>(
+          reinterpret_cast<char*>(&_impl_.java_multiple_files_),
+          reinterpret_cast<char*>(&other->_impl_.java_multiple_files_));
+  swap(_impl_.optimize_for_, other->_impl_.optimize_for_);
+  swap(_impl_.cc_enable_arenas_, other->_impl_.cc_enable_arenas_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata FileOptions::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[13]);
+}
+
+// ===================================================================
+
+class MessageOptions::_Internal {
+ public:
+  using HasBits = decltype(std::declval<MessageOptions>()._impl_._has_bits_);
+  static void set_has_message_set_wire_format(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_no_standard_descriptor_accessor(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static void set_has_deprecated(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+  static void set_has_map_entry(HasBits* has_bits) {
+    (*has_bits)[0] |= 8u;
+  }
+};
+
+MessageOptions::MessageOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.MessageOptions)
+}
+MessageOptions::MessageOptions(const MessageOptions& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  MessageOptions* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{}
+    , decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){from._impl_.uninterpreted_option_}
+    , decltype(_impl_.message_set_wire_format_){}
+    , decltype(_impl_.no_standard_descriptor_accessor_){}
+    , decltype(_impl_.deprecated_){}
+    , decltype(_impl_.map_entry_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  ::memcpy(&_impl_.message_set_wire_format_, &from._impl_.message_set_wire_format_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.map_entry_) -
+    reinterpret_cast<char*>(&_impl_.message_set_wire_format_)) + sizeof(_impl_.map_entry_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.MessageOptions)
+}
+
+inline void MessageOptions::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{::_pbi::ArenaInitialized(), arena}
+    , decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){arena}
+    , decltype(_impl_.message_set_wire_format_){false}
+    , decltype(_impl_.no_standard_descriptor_accessor_){false}
+    , decltype(_impl_.deprecated_){false}
+    , decltype(_impl_.map_entry_){false}
+  };
+}
+
+MessageOptions::~MessageOptions() {
+  // @@protoc_insertion_point(destructor:google.protobuf.MessageOptions)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void MessageOptions::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_._extensions_.~ExtensionSet();
+  _impl_.uninterpreted_option_.~RepeatedPtrField();
+}
+
+void MessageOptions::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void MessageOptions::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.MessageOptions)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_._extensions_.Clear();
+  _impl_.uninterpreted_option_.Clear();
+  ::memset(&_impl_.message_set_wire_format_, 0, static_cast<size_t>(
+      reinterpret_cast<char*>(&_impl_.map_entry_) -
+      reinterpret_cast<char*>(&_impl_.message_set_wire_format_)) + sizeof(_impl_.map_entry_));
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* MessageOptions::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional bool message_set_wire_format = 1 [default = false];
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _Internal::set_has_message_set_wire_format(&has_bits);
+          _impl_.message_set_wire_format_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool no_standard_descriptor_accessor = 2 [default = false];
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _Internal::set_has_no_standard_descriptor_accessor(&has_bits);
+          _impl_.no_standard_descriptor_accessor_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool deprecated = 3 [default = false];
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) {
+          _Internal::set_has_deprecated(&has_bits);
+          _impl_.deprecated_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool map_entry = 7;
+      case 7:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 56)) {
+          _Internal::set_has_map_entry(&has_bits);
+          _impl_.map_entry_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+      case 999:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr -= 2;
+          do {
+            ptr += 2;
+            ptr = ctx->ParseMessage(_internal_add_uninterpreted_option(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<7994>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    if ((8000u <= tag)) {
+      ptr = _impl_._extensions_.ParseField(tag, ptr, internal_default_instance(), &_internal_metadata_, ctx);
+      CHK_(ptr != nullptr);
+      continue;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* MessageOptions::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.MessageOptions)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional bool message_set_wire_format = 1 [default = false];
+  if (cached_has_bits & 0x00000001u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(1, this->_internal_message_set_wire_format(), target);
+  }
+
+  // optional bool no_standard_descriptor_accessor = 2 [default = false];
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(2, this->_internal_no_standard_descriptor_accessor(), target);
+  }
+
+  // optional bool deprecated = 3 [default = false];
+  if (cached_has_bits & 0x00000004u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(3, this->_internal_deprecated(), target);
+  }
+
+  // optional bool map_entry = 7;
+  if (cached_has_bits & 0x00000008u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(7, this->_internal_map_entry(), target);
+  }
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_uninterpreted_option_size()); i < n; i++) {
+    const auto& repfield = this->_internal_uninterpreted_option(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(999, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // Extension range [1000, 536870912)
+  target = _impl_._extensions_._InternalSerialize(
+  internal_default_instance(), 1000, 536870912, target, stream);
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.MessageOptions)
+  return target;
+}
+
+size_t MessageOptions::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.MessageOptions)
+  size_t total_size = 0;
+
+  total_size += _impl_._extensions_.ByteSize();
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  total_size += 2UL * this->_internal_uninterpreted_option_size();
+  for (const auto& msg : this->_impl_.uninterpreted_option_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000000fu) {
+    // optional bool message_set_wire_format = 1 [default = false];
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 + 1;
+    }
+
+    // optional bool no_standard_descriptor_accessor = 2 [default = false];
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 + 1;
+    }
+
+    // optional bool deprecated = 3 [default = false];
+    if (cached_has_bits & 0x00000004u) {
+      total_size += 1 + 1;
+    }
+
+    // optional bool map_entry = 7;
+    if (cached_has_bits & 0x00000008u) {
+      total_size += 1 + 1;
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData MessageOptions::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    MessageOptions::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*MessageOptions::GetClassData() const { return &_class_data_; }
+
+
+void MessageOptions::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<MessageOptions*>(&to_msg);
+  auto& from = static_cast<const MessageOptions&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.MessageOptions)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.uninterpreted_option_.MergeFrom(from._impl_.uninterpreted_option_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000000fu) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_impl_.message_set_wire_format_ = from._impl_.message_set_wire_format_;
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_impl_.no_standard_descriptor_accessor_ = from._impl_.no_standard_descriptor_accessor_;
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _this->_impl_.deprecated_ = from._impl_.deprecated_;
+    }
+    if (cached_has_bits & 0x00000008u) {
+      _this->_impl_.map_entry_ = from._impl_.map_entry_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void MessageOptions::CopyFrom(const MessageOptions& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.MessageOptions)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool MessageOptions::IsInitialized() const {
+  if (!_impl_._extensions_.IsInitialized()) {
+    return false;
+  }
+
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.uninterpreted_option_))
+    return false;
+  return true;
+}
+
+void MessageOptions::InternalSwap(MessageOptions* other) {
+  using std::swap;
+  _impl_._extensions_.InternalSwap(&other->_impl_._extensions_);
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.uninterpreted_option_.InternalSwap(&other->_impl_.uninterpreted_option_);
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(MessageOptions, _impl_.map_entry_)
+      + sizeof(MessageOptions::_impl_.map_entry_)
+      - PROTOBUF_FIELD_OFFSET(MessageOptions, _impl_.message_set_wire_format_)>(
+          reinterpret_cast<char*>(&_impl_.message_set_wire_format_),
+          reinterpret_cast<char*>(&other->_impl_.message_set_wire_format_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata MessageOptions::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[14]);
+}
+
+// ===================================================================
+
+class FieldOptions::_Internal {
+ public:
+  using HasBits = decltype(std::declval<FieldOptions>()._impl_._has_bits_);
+  static void set_has_ctype(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_packed(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+  static void set_has_jstype(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static void set_has_lazy(HasBits* has_bits) {
+    (*has_bits)[0] |= 8u;
+  }
+  static void set_has_unverified_lazy(HasBits* has_bits) {
+    (*has_bits)[0] |= 16u;
+  }
+  static void set_has_deprecated(HasBits* has_bits) {
+    (*has_bits)[0] |= 32u;
+  }
+  static void set_has_weak(HasBits* has_bits) {
+    (*has_bits)[0] |= 64u;
+  }
+};
+
+FieldOptions::FieldOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.FieldOptions)
+}
+FieldOptions::FieldOptions(const FieldOptions& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  FieldOptions* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{}
+    , decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){from._impl_.uninterpreted_option_}
+    , decltype(_impl_.ctype_){}
+    , decltype(_impl_.jstype_){}
+    , decltype(_impl_.packed_){}
+    , decltype(_impl_.lazy_){}
+    , decltype(_impl_.unverified_lazy_){}
+    , decltype(_impl_.deprecated_){}
+    , decltype(_impl_.weak_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  ::memcpy(&_impl_.ctype_, &from._impl_.ctype_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.weak_) -
+    reinterpret_cast<char*>(&_impl_.ctype_)) + sizeof(_impl_.weak_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.FieldOptions)
+}
+
+inline void FieldOptions::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{::_pbi::ArenaInitialized(), arena}
+    , decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){arena}
+    , decltype(_impl_.ctype_){0}
+    , decltype(_impl_.jstype_){0}
+    , decltype(_impl_.packed_){false}
+    , decltype(_impl_.lazy_){false}
+    , decltype(_impl_.unverified_lazy_){false}
+    , decltype(_impl_.deprecated_){false}
+    , decltype(_impl_.weak_){false}
+  };
+}
+
+FieldOptions::~FieldOptions() {
+  // @@protoc_insertion_point(destructor:google.protobuf.FieldOptions)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void FieldOptions::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_._extensions_.~ExtensionSet();
+  _impl_.uninterpreted_option_.~RepeatedPtrField();
+}
+
+void FieldOptions::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void FieldOptions::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.FieldOptions)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_._extensions_.Clear();
+  _impl_.uninterpreted_option_.Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000007fu) {
+    ::memset(&_impl_.ctype_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&_impl_.weak_) -
+        reinterpret_cast<char*>(&_impl_.ctype_)) + sizeof(_impl_.weak_));
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* FieldOptions::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING];
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FieldOptions_CType_IsValid(val))) {
+            _internal_set_ctype(static_cast<::PROTOBUF_NAMESPACE_ID::FieldOptions_CType>(val));
+          } else {
+            ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+          }
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool packed = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _Internal::set_has_packed(&has_bits);
+          _impl_.packed_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool deprecated = 3 [default = false];
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) {
+          _Internal::set_has_deprecated(&has_bits);
+          _impl_.deprecated_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool lazy = 5 [default = false];
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 40)) {
+          _Internal::set_has_lazy(&has_bits);
+          _impl_.lazy_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 48)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType_IsValid(val))) {
+            _internal_set_jstype(static_cast<::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType>(val));
+          } else {
+            ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(6, val, mutable_unknown_fields());
+          }
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool weak = 10 [default = false];
+      case 10:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 80)) {
+          _Internal::set_has_weak(&has_bits);
+          _impl_.weak_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool unverified_lazy = 15 [default = false];
+      case 15:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 120)) {
+          _Internal::set_has_unverified_lazy(&has_bits);
+          _impl_.unverified_lazy_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+      case 999:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr -= 2;
+          do {
+            ptr += 2;
+            ptr = ctx->ParseMessage(_internal_add_uninterpreted_option(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<7994>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    if ((8000u <= tag)) {
+      ptr = _impl_._extensions_.ParseField(tag, ptr, internal_default_instance(), &_internal_metadata_, ctx);
+      CHK_(ptr != nullptr);
+      continue;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* FieldOptions::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.FieldOptions)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING];
+  if (cached_has_bits & 0x00000001u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      1, this->_internal_ctype(), target);
+  }
+
+  // optional bool packed = 2;
+  if (cached_has_bits & 0x00000004u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(2, this->_internal_packed(), target);
+  }
+
+  // optional bool deprecated = 3 [default = false];
+  if (cached_has_bits & 0x00000020u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(3, this->_internal_deprecated(), target);
+  }
+
+  // optional bool lazy = 5 [default = false];
+  if (cached_has_bits & 0x00000008u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(5, this->_internal_lazy(), target);
+  }
+
+  // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      6, this->_internal_jstype(), target);
+  }
+
+  // optional bool weak = 10 [default = false];
+  if (cached_has_bits & 0x00000040u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(10, this->_internal_weak(), target);
+  }
+
+  // optional bool unverified_lazy = 15 [default = false];
+  if (cached_has_bits & 0x00000010u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(15, this->_internal_unverified_lazy(), target);
+  }
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_uninterpreted_option_size()); i < n; i++) {
+    const auto& repfield = this->_internal_uninterpreted_option(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(999, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // Extension range [1000, 536870912)
+  target = _impl_._extensions_._InternalSerialize(
+  internal_default_instance(), 1000, 536870912, target, stream);
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.FieldOptions)
+  return target;
+}
+
+size_t FieldOptions::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FieldOptions)
+  size_t total_size = 0;
+
+  total_size += _impl_._extensions_.ByteSize();
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  total_size += 2UL * this->_internal_uninterpreted_option_size();
+  for (const auto& msg : this->_impl_.uninterpreted_option_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000007fu) {
+    // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING];
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::_pbi::WireFormatLite::EnumSize(this->_internal_ctype());
+    }
+
+    // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::_pbi::WireFormatLite::EnumSize(this->_internal_jstype());
+    }
+
+    // optional bool packed = 2;
+    if (cached_has_bits & 0x00000004u) {
+      total_size += 1 + 1;
+    }
+
+    // optional bool lazy = 5 [default = false];
+    if (cached_has_bits & 0x00000008u) {
+      total_size += 1 + 1;
+    }
+
+    // optional bool unverified_lazy = 15 [default = false];
+    if (cached_has_bits & 0x00000010u) {
+      total_size += 1 + 1;
+    }
+
+    // optional bool deprecated = 3 [default = false];
+    if (cached_has_bits & 0x00000020u) {
+      total_size += 1 + 1;
+    }
+
+    // optional bool weak = 10 [default = false];
+    if (cached_has_bits & 0x00000040u) {
+      total_size += 1 + 1;
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FieldOptions::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    FieldOptions::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FieldOptions::GetClassData() const { return &_class_data_; }
+
+
+void FieldOptions::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<FieldOptions*>(&to_msg);
+  auto& from = static_cast<const FieldOptions&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FieldOptions)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.uninterpreted_option_.MergeFrom(from._impl_.uninterpreted_option_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000007fu) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_impl_.ctype_ = from._impl_.ctype_;
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_impl_.jstype_ = from._impl_.jstype_;
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _this->_impl_.packed_ = from._impl_.packed_;
+    }
+    if (cached_has_bits & 0x00000008u) {
+      _this->_impl_.lazy_ = from._impl_.lazy_;
+    }
+    if (cached_has_bits & 0x00000010u) {
+      _this->_impl_.unverified_lazy_ = from._impl_.unverified_lazy_;
+    }
+    if (cached_has_bits & 0x00000020u) {
+      _this->_impl_.deprecated_ = from._impl_.deprecated_;
+    }
+    if (cached_has_bits & 0x00000040u) {
+      _this->_impl_.weak_ = from._impl_.weak_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void FieldOptions::CopyFrom(const FieldOptions& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FieldOptions)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool FieldOptions::IsInitialized() const {
+  if (!_impl_._extensions_.IsInitialized()) {
+    return false;
+  }
+
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.uninterpreted_option_))
+    return false;
+  return true;
+}
+
+void FieldOptions::InternalSwap(FieldOptions* other) {
+  using std::swap;
+  _impl_._extensions_.InternalSwap(&other->_impl_._extensions_);
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.uninterpreted_option_.InternalSwap(&other->_impl_.uninterpreted_option_);
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(FieldOptions, _impl_.weak_)
+      + sizeof(FieldOptions::_impl_.weak_)
+      - PROTOBUF_FIELD_OFFSET(FieldOptions, _impl_.ctype_)>(
+          reinterpret_cast<char*>(&_impl_.ctype_),
+          reinterpret_cast<char*>(&other->_impl_.ctype_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata FieldOptions::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[15]);
+}
+
+// ===================================================================
+
+class OneofOptions::_Internal {
+ public:
+};
+
+OneofOptions::OneofOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.OneofOptions)
+}
+OneofOptions::OneofOptions(const OneofOptions& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  OneofOptions* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{}
+    , decltype(_impl_.uninterpreted_option_){from._impl_.uninterpreted_option_}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.OneofOptions)
+}
+
+inline void OneofOptions::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{::_pbi::ArenaInitialized(), arena}
+    , decltype(_impl_.uninterpreted_option_){arena}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+OneofOptions::~OneofOptions() {
+  // @@protoc_insertion_point(destructor:google.protobuf.OneofOptions)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void OneofOptions::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_._extensions_.~ExtensionSet();
+  _impl_.uninterpreted_option_.~RepeatedPtrField();
+}
+
+void OneofOptions::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void OneofOptions::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.OneofOptions)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_._extensions_.Clear();
+  _impl_.uninterpreted_option_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* OneofOptions::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+      case 999:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr -= 2;
+          do {
+            ptr += 2;
+            ptr = ctx->ParseMessage(_internal_add_uninterpreted_option(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<7994>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    if ((8000u <= tag)) {
+      ptr = _impl_._extensions_.ParseField(tag, ptr, internal_default_instance(), &_internal_metadata_, ctx);
+      CHK_(ptr != nullptr);
+      continue;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* OneofOptions::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.OneofOptions)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_uninterpreted_option_size()); i < n; i++) {
+    const auto& repfield = this->_internal_uninterpreted_option(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(999, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // Extension range [1000, 536870912)
+  target = _impl_._extensions_._InternalSerialize(
+  internal_default_instance(), 1000, 536870912, target, stream);
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.OneofOptions)
+  return target;
+}
+
+size_t OneofOptions::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.OneofOptions)
+  size_t total_size = 0;
+
+  total_size += _impl_._extensions_.ByteSize();
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  total_size += 2UL * this->_internal_uninterpreted_option_size();
+  for (const auto& msg : this->_impl_.uninterpreted_option_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData OneofOptions::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    OneofOptions::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*OneofOptions::GetClassData() const { return &_class_data_; }
+
+
+void OneofOptions::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<OneofOptions*>(&to_msg);
+  auto& from = static_cast<const OneofOptions&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.OneofOptions)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.uninterpreted_option_.MergeFrom(from._impl_.uninterpreted_option_);
+  _this->_impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void OneofOptions::CopyFrom(const OneofOptions& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.OneofOptions)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool OneofOptions::IsInitialized() const {
+  if (!_impl_._extensions_.IsInitialized()) {
+    return false;
+  }
+
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.uninterpreted_option_))
+    return false;
+  return true;
+}
+
+void OneofOptions::InternalSwap(OneofOptions* other) {
+  using std::swap;
+  _impl_._extensions_.InternalSwap(&other->_impl_._extensions_);
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.uninterpreted_option_.InternalSwap(&other->_impl_.uninterpreted_option_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata OneofOptions::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[16]);
+}
+
+// ===================================================================
+
+class EnumOptions::_Internal {
+ public:
+  using HasBits = decltype(std::declval<EnumOptions>()._impl_._has_bits_);
+  static void set_has_allow_alias(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_deprecated(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+};
+
+EnumOptions::EnumOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.EnumOptions)
+}
+EnumOptions::EnumOptions(const EnumOptions& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  EnumOptions* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{}
+    , decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){from._impl_.uninterpreted_option_}
+    , decltype(_impl_.allow_alias_){}
+    , decltype(_impl_.deprecated_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  ::memcpy(&_impl_.allow_alias_, &from._impl_.allow_alias_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.deprecated_) -
+    reinterpret_cast<char*>(&_impl_.allow_alias_)) + sizeof(_impl_.deprecated_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.EnumOptions)
+}
+
+inline void EnumOptions::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{::_pbi::ArenaInitialized(), arena}
+    , decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){arena}
+    , decltype(_impl_.allow_alias_){false}
+    , decltype(_impl_.deprecated_){false}
+  };
+}
+
+EnumOptions::~EnumOptions() {
+  // @@protoc_insertion_point(destructor:google.protobuf.EnumOptions)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void EnumOptions::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_._extensions_.~ExtensionSet();
+  _impl_.uninterpreted_option_.~RepeatedPtrField();
+}
+
+void EnumOptions::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void EnumOptions::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumOptions)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_._extensions_.Clear();
+  _impl_.uninterpreted_option_.Clear();
+  ::memset(&_impl_.allow_alias_, 0, static_cast<size_t>(
+      reinterpret_cast<char*>(&_impl_.deprecated_) -
+      reinterpret_cast<char*>(&_impl_.allow_alias_)) + sizeof(_impl_.deprecated_));
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* EnumOptions::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional bool allow_alias = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _Internal::set_has_allow_alias(&has_bits);
+          _impl_.allow_alias_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bool deprecated = 3 [default = false];
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) {
+          _Internal::set_has_deprecated(&has_bits);
+          _impl_.deprecated_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+      case 999:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr -= 2;
+          do {
+            ptr += 2;
+            ptr = ctx->ParseMessage(_internal_add_uninterpreted_option(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<7994>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    if ((8000u <= tag)) {
+      ptr = _impl_._extensions_.ParseField(tag, ptr, internal_default_instance(), &_internal_metadata_, ctx);
+      CHK_(ptr != nullptr);
+      continue;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* EnumOptions::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.EnumOptions)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional bool allow_alias = 2;
+  if (cached_has_bits & 0x00000001u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(2, this->_internal_allow_alias(), target);
+  }
+
+  // optional bool deprecated = 3 [default = false];
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(3, this->_internal_deprecated(), target);
+  }
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_uninterpreted_option_size()); i < n; i++) {
+    const auto& repfield = this->_internal_uninterpreted_option(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(999, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // Extension range [1000, 536870912)
+  target = _impl_._extensions_._InternalSerialize(
+  internal_default_instance(), 1000, 536870912, target, stream);
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.EnumOptions)
+  return target;
+}
+
+size_t EnumOptions::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumOptions)
+  size_t total_size = 0;
+
+  total_size += _impl_._extensions_.ByteSize();
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  total_size += 2UL * this->_internal_uninterpreted_option_size();
+  for (const auto& msg : this->_impl_.uninterpreted_option_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    // optional bool allow_alias = 2;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 + 1;
+    }
+
+    // optional bool deprecated = 3 [default = false];
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 + 1;
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumOptions::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    EnumOptions::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumOptions::GetClassData() const { return &_class_data_; }
+
+
+void EnumOptions::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<EnumOptions*>(&to_msg);
+  auto& from = static_cast<const EnumOptions&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumOptions)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.uninterpreted_option_.MergeFrom(from._impl_.uninterpreted_option_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_impl_.allow_alias_ = from._impl_.allow_alias_;
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_impl_.deprecated_ = from._impl_.deprecated_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void EnumOptions::CopyFrom(const EnumOptions& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumOptions)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool EnumOptions::IsInitialized() const {
+  if (!_impl_._extensions_.IsInitialized()) {
+    return false;
+  }
+
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.uninterpreted_option_))
+    return false;
+  return true;
+}
+
+void EnumOptions::InternalSwap(EnumOptions* other) {
+  using std::swap;
+  _impl_._extensions_.InternalSwap(&other->_impl_._extensions_);
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.uninterpreted_option_.InternalSwap(&other->_impl_.uninterpreted_option_);
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(EnumOptions, _impl_.deprecated_)
+      + sizeof(EnumOptions::_impl_.deprecated_)
+      - PROTOBUF_FIELD_OFFSET(EnumOptions, _impl_.allow_alias_)>(
+          reinterpret_cast<char*>(&_impl_.allow_alias_),
+          reinterpret_cast<char*>(&other->_impl_.allow_alias_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata EnumOptions::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[17]);
+}
+
+// ===================================================================
+
+class EnumValueOptions::_Internal {
+ public:
+  using HasBits = decltype(std::declval<EnumValueOptions>()._impl_._has_bits_);
+  static void set_has_deprecated(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+};
+
+EnumValueOptions::EnumValueOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.EnumValueOptions)
+}
+EnumValueOptions::EnumValueOptions(const EnumValueOptions& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  EnumValueOptions* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{}
+    , decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){from._impl_.uninterpreted_option_}
+    , decltype(_impl_.deprecated_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_impl_.deprecated_ = from._impl_.deprecated_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.EnumValueOptions)
+}
+
+inline void EnumValueOptions::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{::_pbi::ArenaInitialized(), arena}
+    , decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){arena}
+    , decltype(_impl_.deprecated_){false}
+  };
+}
+
+EnumValueOptions::~EnumValueOptions() {
+  // @@protoc_insertion_point(destructor:google.protobuf.EnumValueOptions)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void EnumValueOptions::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_._extensions_.~ExtensionSet();
+  _impl_.uninterpreted_option_.~RepeatedPtrField();
+}
+
+void EnumValueOptions::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void EnumValueOptions::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumValueOptions)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_._extensions_.Clear();
+  _impl_.uninterpreted_option_.Clear();
+  _impl_.deprecated_ = false;
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* EnumValueOptions::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional bool deprecated = 1 [default = false];
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _Internal::set_has_deprecated(&has_bits);
+          _impl_.deprecated_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+      case 999:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr -= 2;
+          do {
+            ptr += 2;
+            ptr = ctx->ParseMessage(_internal_add_uninterpreted_option(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<7994>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    if ((8000u <= tag)) {
+      ptr = _impl_._extensions_.ParseField(tag, ptr, internal_default_instance(), &_internal_metadata_, ctx);
+      CHK_(ptr != nullptr);
+      continue;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* EnumValueOptions::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.EnumValueOptions)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional bool deprecated = 1 [default = false];
+  if (cached_has_bits & 0x00000001u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(1, this->_internal_deprecated(), target);
+  }
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_uninterpreted_option_size()); i < n; i++) {
+    const auto& repfield = this->_internal_uninterpreted_option(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(999, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // Extension range [1000, 536870912)
+  target = _impl_._extensions_._InternalSerialize(
+  internal_default_instance(), 1000, 536870912, target, stream);
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.EnumValueOptions)
+  return target;
+}
+
+size_t EnumValueOptions::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumValueOptions)
+  size_t total_size = 0;
+
+  total_size += _impl_._extensions_.ByteSize();
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  total_size += 2UL * this->_internal_uninterpreted_option_size();
+  for (const auto& msg : this->_impl_.uninterpreted_option_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // optional bool deprecated = 1 [default = false];
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000001u) {
+    total_size += 1 + 1;
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumValueOptions::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    EnumValueOptions::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumValueOptions::GetClassData() const { return &_class_data_; }
+
+
+void EnumValueOptions::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<EnumValueOptions*>(&to_msg);
+  auto& from = static_cast<const EnumValueOptions&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumValueOptions)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.uninterpreted_option_.MergeFrom(from._impl_.uninterpreted_option_);
+  if (from._internal_has_deprecated()) {
+    _this->_internal_set_deprecated(from._internal_deprecated());
+  }
+  _this->_impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void EnumValueOptions::CopyFrom(const EnumValueOptions& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumValueOptions)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool EnumValueOptions::IsInitialized() const {
+  if (!_impl_._extensions_.IsInitialized()) {
+    return false;
+  }
+
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.uninterpreted_option_))
+    return false;
+  return true;
+}
+
+void EnumValueOptions::InternalSwap(EnumValueOptions* other) {
+  using std::swap;
+  _impl_._extensions_.InternalSwap(&other->_impl_._extensions_);
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.uninterpreted_option_.InternalSwap(&other->_impl_.uninterpreted_option_);
+  swap(_impl_.deprecated_, other->_impl_.deprecated_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata EnumValueOptions::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[18]);
+}
+
+// ===================================================================
+
+class ServiceOptions::_Internal {
+ public:
+  using HasBits = decltype(std::declval<ServiceOptions>()._impl_._has_bits_);
+  static void set_has_deprecated(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+};
+
+ServiceOptions::ServiceOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.ServiceOptions)
+}
+ServiceOptions::ServiceOptions(const ServiceOptions& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  ServiceOptions* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{}
+    , decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){from._impl_.uninterpreted_option_}
+    , decltype(_impl_.deprecated_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_impl_.deprecated_ = from._impl_.deprecated_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.ServiceOptions)
+}
+
+inline void ServiceOptions::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{::_pbi::ArenaInitialized(), arena}
+    , decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){arena}
+    , decltype(_impl_.deprecated_){false}
+  };
+}
+
+ServiceOptions::~ServiceOptions() {
+  // @@protoc_insertion_point(destructor:google.protobuf.ServiceOptions)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void ServiceOptions::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_._extensions_.~ExtensionSet();
+  _impl_.uninterpreted_option_.~RepeatedPtrField();
+}
+
+void ServiceOptions::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void ServiceOptions::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.ServiceOptions)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_._extensions_.Clear();
+  _impl_.uninterpreted_option_.Clear();
+  _impl_.deprecated_ = false;
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* ServiceOptions::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional bool deprecated = 33 [default = false];
+      case 33:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _Internal::set_has_deprecated(&has_bits);
+          _impl_.deprecated_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+      case 999:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr -= 2;
+          do {
+            ptr += 2;
+            ptr = ctx->ParseMessage(_internal_add_uninterpreted_option(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<7994>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    if ((8000u <= tag)) {
+      ptr = _impl_._extensions_.ParseField(tag, ptr, internal_default_instance(), &_internal_metadata_, ctx);
+      CHK_(ptr != nullptr);
+      continue;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* ServiceOptions::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.ServiceOptions)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional bool deprecated = 33 [default = false];
+  if (cached_has_bits & 0x00000001u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(33, this->_internal_deprecated(), target);
+  }
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_uninterpreted_option_size()); i < n; i++) {
+    const auto& repfield = this->_internal_uninterpreted_option(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(999, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // Extension range [1000, 536870912)
+  target = _impl_._extensions_._InternalSerialize(
+  internal_default_instance(), 1000, 536870912, target, stream);
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.ServiceOptions)
+  return target;
+}
+
+size_t ServiceOptions::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.ServiceOptions)
+  size_t total_size = 0;
+
+  total_size += _impl_._extensions_.ByteSize();
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  total_size += 2UL * this->_internal_uninterpreted_option_size();
+  for (const auto& msg : this->_impl_.uninterpreted_option_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // optional bool deprecated = 33 [default = false];
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000001u) {
+    total_size += 2 + 1;
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ServiceOptions::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    ServiceOptions::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ServiceOptions::GetClassData() const { return &_class_data_; }
+
+
+void ServiceOptions::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<ServiceOptions*>(&to_msg);
+  auto& from = static_cast<const ServiceOptions&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.ServiceOptions)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.uninterpreted_option_.MergeFrom(from._impl_.uninterpreted_option_);
+  if (from._internal_has_deprecated()) {
+    _this->_internal_set_deprecated(from._internal_deprecated());
+  }
+  _this->_impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void ServiceOptions::CopyFrom(const ServiceOptions& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.ServiceOptions)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool ServiceOptions::IsInitialized() const {
+  if (!_impl_._extensions_.IsInitialized()) {
+    return false;
+  }
+
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.uninterpreted_option_))
+    return false;
+  return true;
+}
+
+void ServiceOptions::InternalSwap(ServiceOptions* other) {
+  using std::swap;
+  _impl_._extensions_.InternalSwap(&other->_impl_._extensions_);
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.uninterpreted_option_.InternalSwap(&other->_impl_.uninterpreted_option_);
+  swap(_impl_.deprecated_, other->_impl_.deprecated_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata ServiceOptions::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[19]);
+}
+
+// ===================================================================
+
+class MethodOptions::_Internal {
+ public:
+  using HasBits = decltype(std::declval<MethodOptions>()._impl_._has_bits_);
+  static void set_has_deprecated(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_idempotency_level(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+};
+
+MethodOptions::MethodOptions(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.MethodOptions)
+}
+MethodOptions::MethodOptions(const MethodOptions& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  MethodOptions* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{}
+    , decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){from._impl_.uninterpreted_option_}
+    , decltype(_impl_.deprecated_){}
+    , decltype(_impl_.idempotency_level_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  ::memcpy(&_impl_.deprecated_, &from._impl_.deprecated_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.idempotency_level_) -
+    reinterpret_cast<char*>(&_impl_.deprecated_)) + sizeof(_impl_.idempotency_level_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.MethodOptions)
+}
+
+inline void MethodOptions::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_._extensions_)*/{::_pbi::ArenaInitialized(), arena}
+    , decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.uninterpreted_option_){arena}
+    , decltype(_impl_.deprecated_){false}
+    , decltype(_impl_.idempotency_level_){0}
+  };
+}
+
+MethodOptions::~MethodOptions() {
+  // @@protoc_insertion_point(destructor:google.protobuf.MethodOptions)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void MethodOptions::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_._extensions_.~ExtensionSet();
+  _impl_.uninterpreted_option_.~RepeatedPtrField();
+}
+
+void MethodOptions::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void MethodOptions::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.MethodOptions)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_._extensions_.Clear();
+  _impl_.uninterpreted_option_.Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    ::memset(&_impl_.deprecated_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&_impl_.idempotency_level_) -
+        reinterpret_cast<char*>(&_impl_.deprecated_)) + sizeof(_impl_.idempotency_level_));
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* MethodOptions::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // optional bool deprecated = 33 [default = false];
+      case 33:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _Internal::set_has_deprecated(&has_bits);
+          _impl_.deprecated_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional .google.protobuf.MethodOptions.IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN];
+      case 34:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel_IsValid(val))) {
+            _internal_set_idempotency_level(static_cast<::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel>(val));
+          } else {
+            ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(34, val, mutable_unknown_fields());
+          }
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+      case 999:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          ptr -= 2;
+          do {
+            ptr += 2;
+            ptr = ctx->ParseMessage(_internal_add_uninterpreted_option(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<7994>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    if ((8000u <= tag)) {
+      ptr = _impl_._extensions_.ParseField(tag, ptr, internal_default_instance(), &_internal_metadata_, ctx);
+      CHK_(ptr != nullptr);
+      continue;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* MethodOptions::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.MethodOptions)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional bool deprecated = 33 [default = false];
+  if (cached_has_bits & 0x00000001u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(33, this->_internal_deprecated(), target);
+  }
+
+  // optional .google.protobuf.MethodOptions.IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN];
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      34, this->_internal_idempotency_level(), target);
+  }
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_uninterpreted_option_size()); i < n; i++) {
+    const auto& repfield = this->_internal_uninterpreted_option(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(999, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // Extension range [1000, 536870912)
+  target = _impl_._extensions_._InternalSerialize(
+  internal_default_instance(), 1000, 536870912, target, stream);
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.MethodOptions)
+  return target;
+}
+
+size_t MethodOptions::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.MethodOptions)
+  size_t total_size = 0;
+
+  total_size += _impl_._extensions_.ByteSize();
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
+  total_size += 2UL * this->_internal_uninterpreted_option_size();
+  for (const auto& msg : this->_impl_.uninterpreted_option_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    // optional bool deprecated = 33 [default = false];
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 2 + 1;
+    }
+
+    // optional .google.protobuf.MethodOptions.IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN];
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 2 +
+        ::_pbi::WireFormatLite::EnumSize(this->_internal_idempotency_level());
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData MethodOptions::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    MethodOptions::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*MethodOptions::GetClassData() const { return &_class_data_; }
+
+
+void MethodOptions::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<MethodOptions*>(&to_msg);
+  auto& from = static_cast<const MethodOptions&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.MethodOptions)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.uninterpreted_option_.MergeFrom(from._impl_.uninterpreted_option_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_impl_.deprecated_ = from._impl_.deprecated_;
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_impl_.idempotency_level_ = from._impl_.idempotency_level_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_impl_._extensions_.MergeFrom(internal_default_instance(), from._impl_._extensions_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void MethodOptions::CopyFrom(const MethodOptions& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.MethodOptions)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool MethodOptions::IsInitialized() const {
+  if (!_impl_._extensions_.IsInitialized()) {
+    return false;
+  }
+
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.uninterpreted_option_))
+    return false;
+  return true;
+}
+
+void MethodOptions::InternalSwap(MethodOptions* other) {
+  using std::swap;
+  _impl_._extensions_.InternalSwap(&other->_impl_._extensions_);
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.uninterpreted_option_.InternalSwap(&other->_impl_.uninterpreted_option_);
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(MethodOptions, _impl_.idempotency_level_)
+      + sizeof(MethodOptions::_impl_.idempotency_level_)
+      - PROTOBUF_FIELD_OFFSET(MethodOptions, _impl_.deprecated_)>(
+          reinterpret_cast<char*>(&_impl_.deprecated_),
+          reinterpret_cast<char*>(&other->_impl_.deprecated_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata MethodOptions::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[20]);
+}
+
+// ===================================================================
+
+class UninterpretedOption_NamePart::_Internal {
+ public:
+  using HasBits = decltype(std::declval<UninterpretedOption_NamePart>()._impl_._has_bits_);
+  static void set_has_name_part(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_is_extension(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static bool MissingRequiredFields(const HasBits& has_bits) {
+    return ((has_bits[0] & 0x00000003) ^ 0x00000003) != 0;
+  }
+};
+
+UninterpretedOption_NamePart::UninterpretedOption_NamePart(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.UninterpretedOption.NamePart)
+}
+UninterpretedOption_NamePart::UninterpretedOption_NamePart(const UninterpretedOption_NamePart& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  UninterpretedOption_NamePart* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_part_){}
+    , decltype(_impl_.is_extension_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_part_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_part_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_name_part()) {
+    _this->_impl_.name_part_.Set(from._internal_name_part(), 
+      _this->GetArenaForAllocation());
+  }
+  _this->_impl_.is_extension_ = from._impl_.is_extension_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.UninterpretedOption.NamePart)
+}
+
+inline void UninterpretedOption_NamePart::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_part_){}
+    , decltype(_impl_.is_extension_){false}
+  };
+  _impl_.name_part_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_part_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+UninterpretedOption_NamePart::~UninterpretedOption_NamePart() {
+  // @@protoc_insertion_point(destructor:google.protobuf.UninterpretedOption.NamePart)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void UninterpretedOption_NamePart::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.name_part_.Destroy();
+}
+
+void UninterpretedOption_NamePart::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void UninterpretedOption_NamePart::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.UninterpretedOption.NamePart)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000001u) {
+    _impl_.name_part_.ClearNonDefaultToEmpty();
+  }
+  _impl_.is_extension_ = false;
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* UninterpretedOption_NamePart::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // required string name_part = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name_part();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.UninterpretedOption.NamePart.name_part");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // required bool is_extension = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _Internal::set_has_is_extension(&has_bits);
+          _impl_.is_extension_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* UninterpretedOption_NamePart::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.UninterpretedOption.NamePart)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // required string name_part = 1;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_name_part().data(), static_cast<int>(this->_internal_name_part().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.UninterpretedOption.NamePart.name_part");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name_part(), target);
+  }
+
+  // required bool is_extension = 2;
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(2, this->_internal_is_extension(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.UninterpretedOption.NamePart)
+  return target;
+}
+
+size_t UninterpretedOption_NamePart::RequiredFieldsByteSizeFallback() const {
+// @@protoc_insertion_point(required_fields_byte_size_fallback_start:google.protobuf.UninterpretedOption.NamePart)
+  size_t total_size = 0;
+
+  if (_internal_has_name_part()) {
+    // required string name_part = 1;
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name_part());
+  }
+
+  if (_internal_has_is_extension()) {
+    // required bool is_extension = 2;
+    total_size += 1 + 1;
+  }
+
+  return total_size;
+}
+size_t UninterpretedOption_NamePart::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.UninterpretedOption.NamePart)
+  size_t total_size = 0;
+
+  if (((_impl_._has_bits_[0] & 0x00000003) ^ 0x00000003) == 0) {  // All required fields are present.
+    // required string name_part = 1;
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name_part());
+
+    // required bool is_extension = 2;
+    total_size += 1 + 1;
+
+  } else {
+    total_size += RequiredFieldsByteSizeFallback();
+  }
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UninterpretedOption_NamePart::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    UninterpretedOption_NamePart::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UninterpretedOption_NamePart::GetClassData() const { return &_class_data_; }
+
+
+void UninterpretedOption_NamePart::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<UninterpretedOption_NamePart*>(&to_msg);
+  auto& from = static_cast<const UninterpretedOption_NamePart&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.UninterpretedOption.NamePart)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_name_part(from._internal_name_part());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_impl_.is_extension_ = from._impl_.is_extension_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void UninterpretedOption_NamePart::CopyFrom(const UninterpretedOption_NamePart& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.UninterpretedOption.NamePart)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool UninterpretedOption_NamePart::IsInitialized() const {
+  if (_Internal::MissingRequiredFields(_impl_._has_bits_)) return false;
+  return true;
+}
+
+void UninterpretedOption_NamePart::InternalSwap(UninterpretedOption_NamePart* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_part_, lhs_arena,
+      &other->_impl_.name_part_, rhs_arena
+  );
+  swap(_impl_.is_extension_, other->_impl_.is_extension_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata UninterpretedOption_NamePart::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[21]);
+}
+
+// ===================================================================
+
+class UninterpretedOption::_Internal {
+ public:
+  using HasBits = decltype(std::declval<UninterpretedOption>()._impl_._has_bits_);
+  static void set_has_identifier_value(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_positive_int_value(HasBits* has_bits) {
+    (*has_bits)[0] |= 8u;
+  }
+  static void set_has_negative_int_value(HasBits* has_bits) {
+    (*has_bits)[0] |= 16u;
+  }
+  static void set_has_double_value(HasBits* has_bits) {
+    (*has_bits)[0] |= 32u;
+  }
+  static void set_has_string_value(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static void set_has_aggregate_value(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+};
+
+UninterpretedOption::UninterpretedOption(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.UninterpretedOption)
+}
+UninterpretedOption::UninterpretedOption(const UninterpretedOption& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  UninterpretedOption* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_){from._impl_.name_}
+    , decltype(_impl_.identifier_value_){}
+    , decltype(_impl_.string_value_){}
+    , decltype(_impl_.aggregate_value_){}
+    , decltype(_impl_.positive_int_value_){}
+    , decltype(_impl_.negative_int_value_){}
+    , decltype(_impl_.double_value_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.identifier_value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.identifier_value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_identifier_value()) {
+    _this->_impl_.identifier_value_.Set(from._internal_identifier_value(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.string_value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.string_value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_string_value()) {
+    _this->_impl_.string_value_.Set(from._internal_string_value(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.aggregate_value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.aggregate_value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_aggregate_value()) {
+    _this->_impl_.aggregate_value_.Set(from._internal_aggregate_value(), 
+      _this->GetArenaForAllocation());
+  }
+  ::memcpy(&_impl_.positive_int_value_, &from._impl_.positive_int_value_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.double_value_) -
+    reinterpret_cast<char*>(&_impl_.positive_int_value_)) + sizeof(_impl_.double_value_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.UninterpretedOption)
+}
+
+inline void UninterpretedOption::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.name_){arena}
+    , decltype(_impl_.identifier_value_){}
+    , decltype(_impl_.string_value_){}
+    , decltype(_impl_.aggregate_value_){}
+    , decltype(_impl_.positive_int_value_){uint64_t{0u}}
+    , decltype(_impl_.negative_int_value_){int64_t{0}}
+    , decltype(_impl_.double_value_){0}
+  };
+  _impl_.identifier_value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.identifier_value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.string_value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.string_value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.aggregate_value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.aggregate_value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+UninterpretedOption::~UninterpretedOption() {
+  // @@protoc_insertion_point(destructor:google.protobuf.UninterpretedOption)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void UninterpretedOption::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.name_.~RepeatedPtrField();
+  _impl_.identifier_value_.Destroy();
+  _impl_.string_value_.Destroy();
+  _impl_.aggregate_value_.Destroy();
+}
+
+void UninterpretedOption::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void UninterpretedOption::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.UninterpretedOption)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.name_.Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000007u) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.identifier_value_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _impl_.string_value_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _impl_.aggregate_value_.ClearNonDefaultToEmpty();
+    }
+  }
+  if (cached_has_bits & 0x00000038u) {
+    ::memset(&_impl_.positive_int_value_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&_impl_.double_value_) -
+        reinterpret_cast<char*>(&_impl_.positive_int_value_)) + sizeof(_impl_.double_value_));
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* UninterpretedOption::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // repeated .google.protobuf.UninterpretedOption.NamePart name = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_name(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string identifier_value = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          auto str = _internal_mutable_identifier_value();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.UninterpretedOption.identifier_value");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional uint64 positive_int_value = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 32)) {
+          _Internal::set_has_positive_int_value(&has_bits);
+          _impl_.positive_int_value_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional int64 negative_int_value = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 40)) {
+          _Internal::set_has_negative_int_value(&has_bits);
+          _impl_.negative_int_value_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional double double_value = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 49)) {
+          _Internal::set_has_double_value(&has_bits);
+          _impl_.double_value_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<double>(ptr);
+          ptr += sizeof(double);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional bytes string_value = 7;
+      case 7:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+          auto str = _internal_mutable_string_value();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string aggregate_value = 8;
+      case 8:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 66)) {
+          auto str = _internal_mutable_aggregate_value();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.UninterpretedOption.aggregate_value");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* UninterpretedOption::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.UninterpretedOption)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption.NamePart name = 2;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_name_size()); i < n; i++) {
+    const auto& repfield = this->_internal_name(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(2, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string identifier_value = 3;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_identifier_value().data(), static_cast<int>(this->_internal_identifier_value().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.UninterpretedOption.identifier_value");
+    target = stream->WriteStringMaybeAliased(
+        3, this->_internal_identifier_value(), target);
+  }
+
+  // optional uint64 positive_int_value = 4;
+  if (cached_has_bits & 0x00000008u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteUInt64ToArray(4, this->_internal_positive_int_value(), target);
+  }
+
+  // optional int64 negative_int_value = 5;
+  if (cached_has_bits & 0x00000010u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt64ToArray(5, this->_internal_negative_int_value(), target);
+  }
+
+  // optional double double_value = 6;
+  if (cached_has_bits & 0x00000020u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteDoubleToArray(6, this->_internal_double_value(), target);
+  }
+
+  // optional bytes string_value = 7;
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->WriteBytesMaybeAliased(
+        7, this->_internal_string_value(), target);
+  }
+
+  // optional string aggregate_value = 8;
+  if (cached_has_bits & 0x00000004u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_aggregate_value().data(), static_cast<int>(this->_internal_aggregate_value().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.UninterpretedOption.aggregate_value");
+    target = stream->WriteStringMaybeAliased(
+        8, this->_internal_aggregate_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.UninterpretedOption)
+  return target;
+}
+
+size_t UninterpretedOption::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.UninterpretedOption)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.UninterpretedOption.NamePart name = 2;
+  total_size += 1UL * this->_internal_name_size();
+  for (const auto& msg : this->_impl_.name_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000003fu) {
+    // optional string identifier_value = 3;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_identifier_value());
+    }
+
+    // optional bytes string_value = 7;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+          this->_internal_string_value());
+    }
+
+    // optional string aggregate_value = 8;
+    if (cached_has_bits & 0x00000004u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_aggregate_value());
+    }
+
+    // optional uint64 positive_int_value = 4;
+    if (cached_has_bits & 0x00000008u) {
+      total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_positive_int_value());
+    }
+
+    // optional int64 negative_int_value = 5;
+    if (cached_has_bits & 0x00000010u) {
+      total_size += ::_pbi::WireFormatLite::Int64SizePlusOne(this->_internal_negative_int_value());
+    }
+
+    // optional double double_value = 6;
+    if (cached_has_bits & 0x00000020u) {
+      total_size += 1 + 8;
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UninterpretedOption::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    UninterpretedOption::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UninterpretedOption::GetClassData() const { return &_class_data_; }
+
+
+void UninterpretedOption::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<UninterpretedOption*>(&to_msg);
+  auto& from = static_cast<const UninterpretedOption&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.UninterpretedOption)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.name_.MergeFrom(from._impl_.name_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x0000003fu) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_identifier_value(from._internal_identifier_value());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_set_string_value(from._internal_string_value());
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _this->_internal_set_aggregate_value(from._internal_aggregate_value());
+    }
+    if (cached_has_bits & 0x00000008u) {
+      _this->_impl_.positive_int_value_ = from._impl_.positive_int_value_;
+    }
+    if (cached_has_bits & 0x00000010u) {
+      _this->_impl_.negative_int_value_ = from._impl_.negative_int_value_;
+    }
+    if (cached_has_bits & 0x00000020u) {
+      _this->_impl_.double_value_ = from._impl_.double_value_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void UninterpretedOption::CopyFrom(const UninterpretedOption& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.UninterpretedOption)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool UninterpretedOption::IsInitialized() const {
+  if (!::PROTOBUF_NAMESPACE_ID::internal::AllAreInitialized(_impl_.name_))
+    return false;
+  return true;
+}
+
+void UninterpretedOption::InternalSwap(UninterpretedOption* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.name_.InternalSwap(&other->_impl_.name_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.identifier_value_, lhs_arena,
+      &other->_impl_.identifier_value_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.string_value_, lhs_arena,
+      &other->_impl_.string_value_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.aggregate_value_, lhs_arena,
+      &other->_impl_.aggregate_value_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(UninterpretedOption, _impl_.double_value_)
+      + sizeof(UninterpretedOption::_impl_.double_value_)
+      - PROTOBUF_FIELD_OFFSET(UninterpretedOption, _impl_.positive_int_value_)>(
+          reinterpret_cast<char*>(&_impl_.positive_int_value_),
+          reinterpret_cast<char*>(&other->_impl_.positive_int_value_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata UninterpretedOption::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[22]);
+}
+
+// ===================================================================
+
+class SourceCodeInfo_Location::_Internal {
+ public:
+  using HasBits = decltype(std::declval<SourceCodeInfo_Location>()._impl_._has_bits_);
+  static void set_has_leading_comments(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_trailing_comments(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+};
+
+SourceCodeInfo_Location::SourceCodeInfo_Location(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.SourceCodeInfo.Location)
+}
+SourceCodeInfo_Location::SourceCodeInfo_Location(const SourceCodeInfo_Location& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  SourceCodeInfo_Location* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.path_){from._impl_.path_}
+    , /*decltype(_impl_._path_cached_byte_size_)*/{0}
+    , decltype(_impl_.span_){from._impl_.span_}
+    , /*decltype(_impl_._span_cached_byte_size_)*/{0}
+    , decltype(_impl_.leading_detached_comments_){from._impl_.leading_detached_comments_}
+    , decltype(_impl_.leading_comments_){}
+    , decltype(_impl_.trailing_comments_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.leading_comments_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.leading_comments_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_leading_comments()) {
+    _this->_impl_.leading_comments_.Set(from._internal_leading_comments(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.trailing_comments_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.trailing_comments_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_trailing_comments()) {
+    _this->_impl_.trailing_comments_.Set(from._internal_trailing_comments(), 
+      _this->GetArenaForAllocation());
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.SourceCodeInfo.Location)
+}
+
+inline void SourceCodeInfo_Location::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.path_){arena}
+    , /*decltype(_impl_._path_cached_byte_size_)*/{0}
+    , decltype(_impl_.span_){arena}
+    , /*decltype(_impl_._span_cached_byte_size_)*/{0}
+    , decltype(_impl_.leading_detached_comments_){arena}
+    , decltype(_impl_.leading_comments_){}
+    , decltype(_impl_.trailing_comments_){}
+  };
+  _impl_.leading_comments_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.leading_comments_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.trailing_comments_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.trailing_comments_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+SourceCodeInfo_Location::~SourceCodeInfo_Location() {
+  // @@protoc_insertion_point(destructor:google.protobuf.SourceCodeInfo.Location)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void SourceCodeInfo_Location::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.path_.~RepeatedField();
+  _impl_.span_.~RepeatedField();
+  _impl_.leading_detached_comments_.~RepeatedPtrField();
+  _impl_.leading_comments_.Destroy();
+  _impl_.trailing_comments_.Destroy();
+}
+
+void SourceCodeInfo_Location::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void SourceCodeInfo_Location::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.SourceCodeInfo.Location)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.path_.Clear();
+  _impl_.span_.Clear();
+  _impl_.leading_detached_comments_.Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _impl_.leading_comments_.ClearNonDefaultToEmpty();
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _impl_.trailing_comments_.ClearNonDefaultToEmpty();
+    }
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* SourceCodeInfo_Location::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // repeated int32 path = 1 [packed = true];
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_path(), ptr, ctx);
+          CHK_(ptr);
+        } else if (static_cast<uint8_t>(tag) == 8) {
+          _internal_add_path(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr));
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated int32 span = 2 [packed = true];
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_span(), ptr, ctx);
+          CHK_(ptr);
+        } else if (static_cast<uint8_t>(tag) == 16) {
+          _internal_add_span(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr));
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string leading_comments = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          auto str = _internal_mutable_leading_comments();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.SourceCodeInfo.Location.leading_comments");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string trailing_comments = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          auto str = _internal_mutable_trailing_comments();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.SourceCodeInfo.Location.trailing_comments");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated string leading_detached_comments = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 50)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            auto str = _internal_add_leading_detached_comments();
+            ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+            CHK_(ptr);
+            #ifndef NDEBUG
+            ::_pbi::VerifyUTF8(str, "google.protobuf.SourceCodeInfo.Location.leading_detached_comments");
+            #endif  // !NDEBUG
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<50>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* SourceCodeInfo_Location::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.SourceCodeInfo.Location)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated int32 path = 1 [packed = true];
+  {
+    int byte_size = _impl_._path_cached_byte_size_.load(std::memory_order_relaxed);
+    if (byte_size > 0) {
+      target = stream->WriteInt32Packed(
+          1, _internal_path(), byte_size, target);
+    }
+  }
+
+  // repeated int32 span = 2 [packed = true];
+  {
+    int byte_size = _impl_._span_cached_byte_size_.load(std::memory_order_relaxed);
+    if (byte_size > 0) {
+      target = stream->WriteInt32Packed(
+          2, _internal_span(), byte_size, target);
+    }
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string leading_comments = 3;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_leading_comments().data(), static_cast<int>(this->_internal_leading_comments().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.SourceCodeInfo.Location.leading_comments");
+    target = stream->WriteStringMaybeAliased(
+        3, this->_internal_leading_comments(), target);
+  }
+
+  // optional string trailing_comments = 4;
+  if (cached_has_bits & 0x00000002u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_trailing_comments().data(), static_cast<int>(this->_internal_trailing_comments().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.SourceCodeInfo.Location.trailing_comments");
+    target = stream->WriteStringMaybeAliased(
+        4, this->_internal_trailing_comments(), target);
+  }
+
+  // repeated string leading_detached_comments = 6;
+  for (int i = 0, n = this->_internal_leading_detached_comments_size(); i < n; i++) {
+    const auto& s = this->_internal_leading_detached_comments(i);
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      s.data(), static_cast<int>(s.length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.SourceCodeInfo.Location.leading_detached_comments");
+    target = stream->WriteString(6, s, target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.SourceCodeInfo.Location)
+  return target;
+}
+
+size_t SourceCodeInfo_Location::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.SourceCodeInfo.Location)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated int32 path = 1 [packed = true];
+  {
+    size_t data_size = ::_pbi::WireFormatLite::
+      Int32Size(this->_impl_.path_);
+    if (data_size > 0) {
+      total_size += 1 +
+        ::_pbi::WireFormatLite::Int32Size(static_cast<int32_t>(data_size));
+    }
+    int cached_size = ::_pbi::ToCachedSize(data_size);
+    _impl_._path_cached_byte_size_.store(cached_size,
+                                    std::memory_order_relaxed);
+    total_size += data_size;
+  }
+
+  // repeated int32 span = 2 [packed = true];
+  {
+    size_t data_size = ::_pbi::WireFormatLite::
+      Int32Size(this->_impl_.span_);
+    if (data_size > 0) {
+      total_size += 1 +
+        ::_pbi::WireFormatLite::Int32Size(static_cast<int32_t>(data_size));
+    }
+    int cached_size = ::_pbi::ToCachedSize(data_size);
+    _impl_._span_cached_byte_size_.store(cached_size,
+                                    std::memory_order_relaxed);
+    total_size += data_size;
+  }
+
+  // repeated string leading_detached_comments = 6;
+  total_size += 1 *
+      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(_impl_.leading_detached_comments_.size());
+  for (int i = 0, n = _impl_.leading_detached_comments_.size(); i < n; i++) {
+    total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+      _impl_.leading_detached_comments_.Get(i));
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    // optional string leading_comments = 3;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_leading_comments());
+    }
+
+    // optional string trailing_comments = 4;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_trailing_comments());
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData SourceCodeInfo_Location::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    SourceCodeInfo_Location::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*SourceCodeInfo_Location::GetClassData() const { return &_class_data_; }
+
+
+void SourceCodeInfo_Location::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<SourceCodeInfo_Location*>(&to_msg);
+  auto& from = static_cast<const SourceCodeInfo_Location&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.SourceCodeInfo.Location)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.path_.MergeFrom(from._impl_.path_);
+  _this->_impl_.span_.MergeFrom(from._impl_.span_);
+  _this->_impl_.leading_detached_comments_.MergeFrom(from._impl_.leading_detached_comments_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000003u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_leading_comments(from._internal_leading_comments());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_internal_set_trailing_comments(from._internal_trailing_comments());
+    }
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void SourceCodeInfo_Location::CopyFrom(const SourceCodeInfo_Location& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.SourceCodeInfo.Location)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool SourceCodeInfo_Location::IsInitialized() const {
+  return true;
+}
+
+void SourceCodeInfo_Location::InternalSwap(SourceCodeInfo_Location* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.path_.InternalSwap(&other->_impl_.path_);
+  _impl_.span_.InternalSwap(&other->_impl_.span_);
+  _impl_.leading_detached_comments_.InternalSwap(&other->_impl_.leading_detached_comments_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.leading_comments_, lhs_arena,
+      &other->_impl_.leading_comments_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.trailing_comments_, lhs_arena,
+      &other->_impl_.trailing_comments_, rhs_arena
+  );
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata SourceCodeInfo_Location::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[23]);
+}
+
+// ===================================================================
+
+class SourceCodeInfo::_Internal {
+ public:
+};
+
+SourceCodeInfo::SourceCodeInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.SourceCodeInfo)
+}
+SourceCodeInfo::SourceCodeInfo(const SourceCodeInfo& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  SourceCodeInfo* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.location_){from._impl_.location_}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.SourceCodeInfo)
+}
+
+inline void SourceCodeInfo::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.location_){arena}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+SourceCodeInfo::~SourceCodeInfo() {
+  // @@protoc_insertion_point(destructor:google.protobuf.SourceCodeInfo)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void SourceCodeInfo::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.location_.~RepeatedPtrField();
+}
+
+void SourceCodeInfo::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void SourceCodeInfo::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.SourceCodeInfo)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.location_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* SourceCodeInfo::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // repeated .google.protobuf.SourceCodeInfo.Location location = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_location(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* SourceCodeInfo::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.SourceCodeInfo)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.SourceCodeInfo.Location location = 1;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_location_size()); i < n; i++) {
+    const auto& repfield = this->_internal_location(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.SourceCodeInfo)
+  return target;
+}
+
+size_t SourceCodeInfo::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.SourceCodeInfo)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.SourceCodeInfo.Location location = 1;
+  total_size += 1UL * this->_internal_location_size();
+  for (const auto& msg : this->_impl_.location_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData SourceCodeInfo::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    SourceCodeInfo::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*SourceCodeInfo::GetClassData() const { return &_class_data_; }
+
+
+void SourceCodeInfo::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<SourceCodeInfo*>(&to_msg);
+  auto& from = static_cast<const SourceCodeInfo&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.SourceCodeInfo)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.location_.MergeFrom(from._impl_.location_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void SourceCodeInfo::CopyFrom(const SourceCodeInfo& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.SourceCodeInfo)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool SourceCodeInfo::IsInitialized() const {
+  return true;
+}
+
+void SourceCodeInfo::InternalSwap(SourceCodeInfo* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.location_.InternalSwap(&other->_impl_.location_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata SourceCodeInfo::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[24]);
+}
+
+// ===================================================================
+
+class GeneratedCodeInfo_Annotation::_Internal {
+ public:
+  using HasBits = decltype(std::declval<GeneratedCodeInfo_Annotation>()._impl_._has_bits_);
+  static void set_has_source_file(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+  static void set_has_begin(HasBits* has_bits) {
+    (*has_bits)[0] |= 2u;
+  }
+  static void set_has_end(HasBits* has_bits) {
+    (*has_bits)[0] |= 4u;
+  }
+};
+
+GeneratedCodeInfo_Annotation::GeneratedCodeInfo_Annotation(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.GeneratedCodeInfo.Annotation)
+}
+GeneratedCodeInfo_Annotation::GeneratedCodeInfo_Annotation(const GeneratedCodeInfo_Annotation& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  GeneratedCodeInfo_Annotation* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.path_){from._impl_.path_}
+    , /*decltype(_impl_._path_cached_byte_size_)*/{0}
+    , decltype(_impl_.source_file_){}
+    , decltype(_impl_.begin_){}
+    , decltype(_impl_.end_){}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.source_file_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.source_file_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (from._internal_has_source_file()) {
+    _this->_impl_.source_file_.Set(from._internal_source_file(), 
+      _this->GetArenaForAllocation());
+  }
+  ::memcpy(&_impl_.begin_, &from._impl_.begin_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.end_) -
+    reinterpret_cast<char*>(&_impl_.begin_)) + sizeof(_impl_.end_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.GeneratedCodeInfo.Annotation)
+}
+
+inline void GeneratedCodeInfo_Annotation::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , decltype(_impl_.path_){arena}
+    , /*decltype(_impl_._path_cached_byte_size_)*/{0}
+    , decltype(_impl_.source_file_){}
+    , decltype(_impl_.begin_){0}
+    , decltype(_impl_.end_){0}
+  };
+  _impl_.source_file_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.source_file_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+GeneratedCodeInfo_Annotation::~GeneratedCodeInfo_Annotation() {
+  // @@protoc_insertion_point(destructor:google.protobuf.GeneratedCodeInfo.Annotation)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void GeneratedCodeInfo_Annotation::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.path_.~RepeatedField();
+  _impl_.source_file_.Destroy();
+}
+
+void GeneratedCodeInfo_Annotation::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void GeneratedCodeInfo_Annotation::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.GeneratedCodeInfo.Annotation)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.path_.Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000001u) {
+    _impl_.source_file_.ClearNonDefaultToEmpty();
+  }
+  if (cached_has_bits & 0x00000006u) {
+    ::memset(&_impl_.begin_, 0, static_cast<size_t>(
+        reinterpret_cast<char*>(&_impl_.end_) -
+        reinterpret_cast<char*>(&_impl_.begin_)) + sizeof(_impl_.end_));
+  }
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* GeneratedCodeInfo_Annotation::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  _Internal::HasBits has_bits{};
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // repeated int32 path = 1 [packed = true];
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_path(), ptr, ctx);
+          CHK_(ptr);
+        } else if (static_cast<uint8_t>(tag) == 8) {
+          _internal_add_path(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr));
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional string source_file = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          auto str = _internal_mutable_source_file();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          #ifndef NDEBUG
+          ::_pbi::VerifyUTF8(str, "google.protobuf.GeneratedCodeInfo.Annotation.source_file");
+          #endif  // !NDEBUG
+        } else
+          goto handle_unusual;
+        continue;
+      // optional int32 begin = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) {
+          _Internal::set_has_begin(&has_bits);
+          _impl_.begin_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // optional int32 end = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 32)) {
+          _Internal::set_has_end(&has_bits);
+          _impl_.end_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  _impl_._has_bits_.Or(has_bits);
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* GeneratedCodeInfo_Annotation::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.GeneratedCodeInfo.Annotation)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated int32 path = 1 [packed = true];
+  {
+    int byte_size = _impl_._path_cached_byte_size_.load(std::memory_order_relaxed);
+    if (byte_size > 0) {
+      target = stream->WriteInt32Packed(
+          1, _internal_path(), byte_size, target);
+    }
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional string source_file = 2;
+  if (cached_has_bits & 0x00000001u) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->_internal_source_file().data(), static_cast<int>(this->_internal_source_file().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE,
+      "google.protobuf.GeneratedCodeInfo.Annotation.source_file");
+    target = stream->WriteStringMaybeAliased(
+        2, this->_internal_source_file(), target);
+  }
+
+  // optional int32 begin = 3;
+  if (cached_has_bits & 0x00000002u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(3, this->_internal_begin(), target);
+  }
+
+  // optional int32 end = 4;
+  if (cached_has_bits & 0x00000004u) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(4, this->_internal_end(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.GeneratedCodeInfo.Annotation)
+  return target;
+}
+
+size_t GeneratedCodeInfo_Annotation::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.GeneratedCodeInfo.Annotation)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated int32 path = 1 [packed = true];
+  {
+    size_t data_size = ::_pbi::WireFormatLite::
+      Int32Size(this->_impl_.path_);
+    if (data_size > 0) {
+      total_size += 1 +
+        ::_pbi::WireFormatLite::Int32Size(static_cast<int32_t>(data_size));
+    }
+    int cached_size = ::_pbi::ToCachedSize(data_size);
+    _impl_._path_cached_byte_size_.store(cached_size,
+                                    std::memory_order_relaxed);
+    total_size += data_size;
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000007u) {
+    // optional string source_file = 2;
+    if (cached_has_bits & 0x00000001u) {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_source_file());
+    }
+
+    // optional int32 begin = 3;
+    if (cached_has_bits & 0x00000002u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_begin());
+    }
+
+    // optional int32 end = 4;
+    if (cached_has_bits & 0x00000004u) {
+      total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_end());
+    }
+
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData GeneratedCodeInfo_Annotation::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    GeneratedCodeInfo_Annotation::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GeneratedCodeInfo_Annotation::GetClassData() const { return &_class_data_; }
+
+
+void GeneratedCodeInfo_Annotation::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<GeneratedCodeInfo_Annotation*>(&to_msg);
+  auto& from = static_cast<const GeneratedCodeInfo_Annotation&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.GeneratedCodeInfo.Annotation)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.path_.MergeFrom(from._impl_.path_);
+  cached_has_bits = from._impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000007u) {
+    if (cached_has_bits & 0x00000001u) {
+      _this->_internal_set_source_file(from._internal_source_file());
+    }
+    if (cached_has_bits & 0x00000002u) {
+      _this->_impl_.begin_ = from._impl_.begin_;
+    }
+    if (cached_has_bits & 0x00000004u) {
+      _this->_impl_.end_ = from._impl_.end_;
+    }
+    _this->_impl_._has_bits_[0] |= cached_has_bits;
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void GeneratedCodeInfo_Annotation::CopyFrom(const GeneratedCodeInfo_Annotation& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.GeneratedCodeInfo.Annotation)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool GeneratedCodeInfo_Annotation::IsInitialized() const {
+  return true;
+}
+
+void GeneratedCodeInfo_Annotation::InternalSwap(GeneratedCodeInfo_Annotation* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.path_.InternalSwap(&other->_impl_.path_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.source_file_, lhs_arena,
+      &other->_impl_.source_file_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(GeneratedCodeInfo_Annotation, _impl_.end_)
+      + sizeof(GeneratedCodeInfo_Annotation::_impl_.end_)
+      - PROTOBUF_FIELD_OFFSET(GeneratedCodeInfo_Annotation, _impl_.begin_)>(
+          reinterpret_cast<char*>(&_impl_.begin_),
+          reinterpret_cast<char*>(&other->_impl_.begin_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata GeneratedCodeInfo_Annotation::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[25]);
+}
+
+// ===================================================================
+
+class GeneratedCodeInfo::_Internal {
+ public:
+};
+
+GeneratedCodeInfo::GeneratedCodeInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.GeneratedCodeInfo)
+}
+GeneratedCodeInfo::GeneratedCodeInfo(const GeneratedCodeInfo& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  GeneratedCodeInfo* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.annotation_){from._impl_.annotation_}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.GeneratedCodeInfo)
+}
+
+inline void GeneratedCodeInfo::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.annotation_){arena}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+GeneratedCodeInfo::~GeneratedCodeInfo() {
+  // @@protoc_insertion_point(destructor:google.protobuf.GeneratedCodeInfo)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void GeneratedCodeInfo::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.annotation_.~RepeatedPtrField();
+}
+
+void GeneratedCodeInfo::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void GeneratedCodeInfo::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.GeneratedCodeInfo)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.annotation_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* GeneratedCodeInfo::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_annotation(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* GeneratedCodeInfo::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.GeneratedCodeInfo)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_annotation_size()); i < n; i++) {
+    const auto& repfield = this->_internal_annotation(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.GeneratedCodeInfo)
+  return target;
+}
+
+size_t GeneratedCodeInfo::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.GeneratedCodeInfo)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+  total_size += 1UL * this->_internal_annotation_size();
+  for (const auto& msg : this->_impl_.annotation_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData GeneratedCodeInfo::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    GeneratedCodeInfo::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GeneratedCodeInfo::GetClassData() const { return &_class_data_; }
+
+
+void GeneratedCodeInfo::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<GeneratedCodeInfo*>(&to_msg);
+  auto& from = static_cast<const GeneratedCodeInfo&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.GeneratedCodeInfo)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.annotation_.MergeFrom(from._impl_.annotation_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void GeneratedCodeInfo::CopyFrom(const GeneratedCodeInfo& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.GeneratedCodeInfo)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool GeneratedCodeInfo::IsInitialized() const {
+  return true;
+}
+
+void GeneratedCodeInfo::InternalSwap(GeneratedCodeInfo* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.annotation_.InternalSwap(&other->_impl_.annotation_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata GeneratedCodeInfo::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_getter, &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fdescriptor_2eproto[26]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::FileDescriptorSet*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::FileDescriptorSet >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::FileDescriptorSet >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::FileDescriptorProto >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ExtensionRange >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::DescriptorProto_ReservedRange >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::DescriptorProto*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::DescriptorProto >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::DescriptorProto >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::ExtensionRangeOptions >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::OneofDescriptorProto >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto_EnumReservedRange >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::EnumDescriptorProto >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::EnumValueDescriptorProto >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::ServiceDescriptorProto >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::MethodDescriptorProto >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::FileOptions*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::FileOptions >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::FileOptions >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::MessageOptions*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::MessageOptions >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::MessageOptions >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::FieldOptions*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::FieldOptions >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::FieldOptions >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::OneofOptions*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::OneofOptions >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::OneofOptions >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::EnumOptions*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::EnumOptions >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::EnumOptions >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::EnumValueOptions*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::EnumValueOptions >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::EnumValueOptions >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::ServiceOptions*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::ServiceOptions >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::ServiceOptions >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::MethodOptions*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::MethodOptions >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::MethodOptions >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption_NamePart >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::UninterpretedOption*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::UninterpretedOption >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo_Location >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::SourceCodeInfo >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/descriptor_database.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/descriptor_database.cpp
new file mode 100644
index 0000000..203000d
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/descriptor_database.cpp
@@ -0,0 +1,1048 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/descriptor_database.h>
+
+#include <algorithm>
+#include <set>
+
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+
+namespace google {
+namespace protobuf {
+
+namespace {
+void RecordMessageNames(const DescriptorProto& desc_proto,
+                        const std::string& prefix,
+                        std::set<std::string>* output) {
+  GOOGLE_CHECK(desc_proto.has_name());
+  std::string full_name = prefix.empty()
+                              ? desc_proto.name()
+                              : StrCat(prefix, ".", desc_proto.name());
+  output->insert(full_name);
+
+  for (const auto& d : desc_proto.nested_type()) {
+    RecordMessageNames(d, full_name, output);
+  }
+}
+
+void RecordMessageNames(const FileDescriptorProto& file_proto,
+                        std::set<std::string>* output) {
+  for (const auto& d : file_proto.message_type()) {
+    RecordMessageNames(d, file_proto.package(), output);
+  }
+}
+
+template <typename Fn>
+bool ForAllFileProtos(DescriptorDatabase* db, Fn callback,
+                      std::vector<std::string>* output) {
+  std::vector<std::string> file_names;
+  if (!db->FindAllFileNames(&file_names)) {
+    return false;
+  }
+  std::set<std::string> set;
+  FileDescriptorProto file_proto;
+  for (const auto& f : file_names) {
+    file_proto.Clear();
+    if (!db->FindFileByName(f, &file_proto)) {
+      GOOGLE_LOG(ERROR) << "File not found in database (unexpected): " << f;
+      return false;
+    }
+    callback(file_proto, &set);
+  }
+  output->insert(output->end(), set.begin(), set.end());
+  return true;
+}
+}  // namespace
+
+DescriptorDatabase::~DescriptorDatabase() {}
+
+bool DescriptorDatabase::FindAllPackageNames(std::vector<std::string>* output) {
+  return ForAllFileProtos(
+      this,
+      [](const FileDescriptorProto& file_proto, std::set<std::string>* set) {
+        set->insert(file_proto.package());
+      },
+      output);
+}
+
+bool DescriptorDatabase::FindAllMessageNames(std::vector<std::string>* output) {
+  return ForAllFileProtos(
+      this,
+      [](const FileDescriptorProto& file_proto, std::set<std::string>* set) {
+        RecordMessageNames(file_proto, set);
+      },
+      output);
+}
+
+// ===================================================================
+
+SimpleDescriptorDatabase::SimpleDescriptorDatabase() {}
+SimpleDescriptorDatabase::~SimpleDescriptorDatabase() {}
+
+template <typename Value>
+bool SimpleDescriptorDatabase::DescriptorIndex<Value>::AddFile(
+    const FileDescriptorProto& file, Value value) {
+  if (!InsertIfNotPresent(&by_name_, file.name(), value)) {
+    GOOGLE_LOG(ERROR) << "File already exists in database: " << file.name();
+    return false;
+  }
+
+  // We must be careful here -- calling file.package() if file.has_package() is
+  // false could access an uninitialized static-storage variable if we are being
+  // run at startup time.
+  std::string path = file.has_package() ? file.package() : std::string();
+  if (!path.empty()) path += '.';
+
+  for (int i = 0; i < file.message_type_size(); i++) {
+    if (!AddSymbol(path + file.message_type(i).name(), value)) return false;
+    if (!AddNestedExtensions(file.name(), file.message_type(i), value))
+      return false;
+  }
+  for (int i = 0; i < file.enum_type_size(); i++) {
+    if (!AddSymbol(path + file.enum_type(i).name(), value)) return false;
+  }
+  for (int i = 0; i < file.extension_size(); i++) {
+    if (!AddSymbol(path + file.extension(i).name(), value)) return false;
+    if (!AddExtension(file.name(), file.extension(i), value)) return false;
+  }
+  for (int i = 0; i < file.service_size(); i++) {
+    if (!AddSymbol(path + file.service(i).name(), value)) return false;
+  }
+
+  return true;
+}
+
+namespace {
+
+// Returns true if and only if all characters in the name are alphanumerics,
+// underscores, or periods.
+bool ValidateSymbolName(StringPiece name) {
+  for (char c : name) {
+    // I don't trust ctype.h due to locales.  :(
+    if (c != '.' && c != '_' && (c < '0' || c > '9') && (c < 'A' || c > 'Z') &&
+        (c < 'a' || c > 'z')) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// Find the last key in the container which sorts less than or equal to the
+// symbol name.  Since upper_bound() returns the *first* key that sorts
+// *greater* than the input, we want the element immediately before that.
+template <typename Container, typename Key>
+typename Container::const_iterator FindLastLessOrEqual(
+    const Container* container, const Key& key) {
+  auto iter = container->upper_bound(key);
+  if (iter != container->begin()) --iter;
+  return iter;
+}
+
+// As above, but using std::upper_bound instead.
+template <typename Container, typename Key, typename Cmp>
+typename Container::const_iterator FindLastLessOrEqual(
+    const Container* container, const Key& key, const Cmp& cmp) {
+  auto iter = std::upper_bound(container->begin(), container->end(), key, cmp);
+  if (iter != container->begin()) --iter;
+  return iter;
+}
+
+// True if either the arguments are equal or super_symbol identifies a
+// parent symbol of sub_symbol (e.g. "foo.bar" is a parent of
+// "foo.bar.baz", but not a parent of "foo.barbaz").
+bool IsSubSymbol(StringPiece sub_symbol, StringPiece super_symbol) {
+  return sub_symbol == super_symbol ||
+         (HasPrefixString(super_symbol, sub_symbol) &&
+          super_symbol[sub_symbol.size()] == '.');
+}
+
+}  // namespace
+
+template <typename Value>
+bool SimpleDescriptorDatabase::DescriptorIndex<Value>::AddSymbol(
+    const std::string& name, Value value) {
+  // We need to make sure not to violate our map invariant.
+
+  // If the symbol name is invalid it could break our lookup algorithm (which
+  // relies on the fact that '.' sorts before all other characters that are
+  // valid in symbol names).
+  if (!ValidateSymbolName(name)) {
+    GOOGLE_LOG(ERROR) << "Invalid symbol name: " << name;
+    return false;
+  }
+
+  // Try to look up the symbol to make sure a super-symbol doesn't already
+  // exist.
+  auto iter = FindLastLessOrEqual(&by_symbol_, name);
+
+  if (iter == by_symbol_.end()) {
+    // Apparently the map is currently empty.  Just insert and be done with it.
+    by_symbol_.insert(
+        typename std::map<std::string, Value>::value_type(name, value));
+    return true;
+  }
+
+  if (IsSubSymbol(iter->first, name)) {
+    GOOGLE_LOG(ERROR) << "Symbol name \"" << name
+               << "\" conflicts with the existing "
+                  "symbol \""
+               << iter->first << "\".";
+    return false;
+  }
+
+  // OK, that worked.  Now we have to make sure that no symbol in the map is
+  // a sub-symbol of the one we are inserting.  The only symbol which could
+  // be so is the first symbol that is greater than the new symbol.  Since
+  // |iter| points at the last symbol that is less than or equal, we just have
+  // to increment it.
+  ++iter;
+
+  if (iter != by_symbol_.end() && IsSubSymbol(name, iter->first)) {
+    GOOGLE_LOG(ERROR) << "Symbol name \"" << name
+               << "\" conflicts with the existing "
+                  "symbol \""
+               << iter->first << "\".";
+    return false;
+  }
+
+  // OK, no conflicts.
+
+  // Insert the new symbol using the iterator as a hint, the new entry will
+  // appear immediately before the one the iterator is pointing at.
+  by_symbol_.insert(
+      iter, typename std::map<std::string, Value>::value_type(name, value));
+
+  return true;
+}
+
+template <typename Value>
+bool SimpleDescriptorDatabase::DescriptorIndex<Value>::AddNestedExtensions(
+    const std::string& filename, const DescriptorProto& message_type,
+    Value value) {
+  for (int i = 0; i < message_type.nested_type_size(); i++) {
+    if (!AddNestedExtensions(filename, message_type.nested_type(i), value))
+      return false;
+  }
+  for (int i = 0; i < message_type.extension_size(); i++) {
+    if (!AddExtension(filename, message_type.extension(i), value)) return false;
+  }
+  return true;
+}
+
+template <typename Value>
+bool SimpleDescriptorDatabase::DescriptorIndex<Value>::AddExtension(
+    const std::string& filename, const FieldDescriptorProto& field,
+    Value value) {
+  if (!field.extendee().empty() && field.extendee()[0] == '.') {
+    // The extension is fully-qualified.  We can use it as a lookup key in
+    // the by_symbol_ table.
+    if (!InsertIfNotPresent(
+            &by_extension_,
+            std::make_pair(field.extendee().substr(1), field.number()),
+            value)) {
+      GOOGLE_LOG(ERROR) << "Extension conflicts with extension already in database: "
+                    "extend "
+                 << field.extendee() << " { " << field.name() << " = "
+                 << field.number() << " } from:" << filename;
+      return false;
+    }
+  } else {
+    // Not fully-qualified.  We can't really do anything here, unfortunately.
+    // We don't consider this an error, though, because the descriptor is
+    // valid.
+  }
+  return true;
+}
+
+template <typename Value>
+Value SimpleDescriptorDatabase::DescriptorIndex<Value>::FindFile(
+    const std::string& filename) {
+  return FindWithDefault(by_name_, filename, Value());
+}
+
+template <typename Value>
+Value SimpleDescriptorDatabase::DescriptorIndex<Value>::FindSymbol(
+    const std::string& name) {
+  auto iter = FindLastLessOrEqual(&by_symbol_, name);
+
+  return (iter != by_symbol_.end() && IsSubSymbol(iter->first, name))
+             ? iter->second
+             : Value();
+}
+
+template <typename Value>
+Value SimpleDescriptorDatabase::DescriptorIndex<Value>::FindExtension(
+    const std::string& containing_type, int field_number) {
+  return FindWithDefault(
+      by_extension_, std::make_pair(containing_type, field_number), Value());
+}
+
+template <typename Value>
+bool SimpleDescriptorDatabase::DescriptorIndex<Value>::FindAllExtensionNumbers(
+    const std::string& containing_type, std::vector<int>* output) {
+  typename std::map<std::pair<std::string, int>, Value>::const_iterator it =
+      by_extension_.lower_bound(std::make_pair(containing_type, 0));
+  bool success = false;
+
+  for (; it != by_extension_.end() && it->first.first == containing_type;
+       ++it) {
+    output->push_back(it->first.second);
+    success = true;
+  }
+
+  return success;
+}
+
+template <typename Value>
+void SimpleDescriptorDatabase::DescriptorIndex<Value>::FindAllFileNames(
+    std::vector<std::string>* output) {
+  output->resize(by_name_.size());
+  int i = 0;
+  for (const auto& kv : by_name_) {
+    (*output)[i] = kv.first;
+    i++;
+  }
+}
+
+// -------------------------------------------------------------------
+
+bool SimpleDescriptorDatabase::Add(const FileDescriptorProto& file) {
+  FileDescriptorProto* new_file = new FileDescriptorProto;
+  new_file->CopyFrom(file);
+  return AddAndOwn(new_file);
+}
+
+bool SimpleDescriptorDatabase::AddAndOwn(const FileDescriptorProto* file) {
+  files_to_delete_.emplace_back(file);
+  return index_.AddFile(*file, file);
+}
+
+bool SimpleDescriptorDatabase::FindFileByName(const std::string& filename,
+                                              FileDescriptorProto* output) {
+  return MaybeCopy(index_.FindFile(filename), output);
+}
+
+bool SimpleDescriptorDatabase::FindFileContainingSymbol(
+    const std::string& symbol_name, FileDescriptorProto* output) {
+  return MaybeCopy(index_.FindSymbol(symbol_name), output);
+}
+
+bool SimpleDescriptorDatabase::FindFileContainingExtension(
+    const std::string& containing_type, int field_number,
+    FileDescriptorProto* output) {
+  return MaybeCopy(index_.FindExtension(containing_type, field_number), output);
+}
+
+bool SimpleDescriptorDatabase::FindAllExtensionNumbers(
+    const std::string& extendee_type, std::vector<int>* output) {
+  return index_.FindAllExtensionNumbers(extendee_type, output);
+}
+
+
+bool SimpleDescriptorDatabase::FindAllFileNames(
+    std::vector<std::string>* output) {
+  index_.FindAllFileNames(output);
+  return true;
+}
+
+bool SimpleDescriptorDatabase::MaybeCopy(const FileDescriptorProto* file,
+                                         FileDescriptorProto* output) {
+  if (file == nullptr) return false;
+  output->CopyFrom(*file);
+  return true;
+}
+
+// -------------------------------------------------------------------
+
+class EncodedDescriptorDatabase::DescriptorIndex {
+ public:
+  using Value = std::pair<const void*, int>;
+  // Helpers to recursively add particular descriptors and all their contents
+  // to the index.
+  template <typename FileProto>
+  bool AddFile(const FileProto& file, Value value);
+
+  Value FindFile(StringPiece filename);
+  Value FindSymbol(StringPiece name);
+  Value FindSymbolOnlyFlat(StringPiece name) const;
+  Value FindExtension(StringPiece containing_type, int field_number);
+  bool FindAllExtensionNumbers(StringPiece containing_type,
+                               std::vector<int>* output);
+  void FindAllFileNames(std::vector<std::string>* output) const;
+
+ private:
+  friend class EncodedDescriptorDatabase;
+
+  bool AddSymbol(StringPiece symbol);
+
+  template <typename DescProto>
+  bool AddNestedExtensions(StringPiece filename,
+                           const DescProto& message_type);
+  template <typename FieldProto>
+  bool AddExtension(StringPiece filename, const FieldProto& field);
+
+  // All the maps below have two representations:
+  //  - a std::set<> where we insert initially.
+  //  - a std::vector<> where we flatten the structure on demand.
+  // The initial tree helps avoid O(N) behavior of inserting into a sorted
+  // vector, while the vector reduces the heap requirements of the data
+  // structure.
+
+  void EnsureFlat();
+
+  using String = std::string;
+
+  String EncodeString(StringPiece str) const { return String(str); }
+  StringPiece DecodeString(const String& str, int) const { return str; }
+
+  struct EncodedEntry {
+    // Do not use `Value` here to avoid the padding of that object.
+    const void* data;
+    int size;
+    // Keep the package here instead of each SymbolEntry to save space.
+    String encoded_package;
+
+    Value value() const { return {data, size}; }
+  };
+  std::vector<EncodedEntry> all_values_;
+
+  struct FileEntry {
+    int data_offset;
+    String encoded_name;
+
+    StringPiece name(const DescriptorIndex& index) const {
+      return index.DecodeString(encoded_name, data_offset);
+    }
+  };
+  struct FileCompare {
+    const DescriptorIndex& index;
+
+    bool operator()(const FileEntry& a, const FileEntry& b) const {
+      return a.name(index) < b.name(index);
+    }
+    bool operator()(const FileEntry& a, StringPiece b) const {
+      return a.name(index) < b;
+    }
+    bool operator()(StringPiece a, const FileEntry& b) const {
+      return a < b.name(index);
+    }
+  };
+  std::set<FileEntry, FileCompare> by_name_{FileCompare{*this}};
+  std::vector<FileEntry> by_name_flat_;
+
+  struct SymbolEntry {
+    int data_offset;
+    String encoded_symbol;
+
+    StringPiece package(const DescriptorIndex& index) const {
+      return index.DecodeString(index.all_values_[data_offset].encoded_package,
+                                data_offset);
+    }
+    StringPiece symbol(const DescriptorIndex& index) const {
+      return index.DecodeString(encoded_symbol, data_offset);
+    }
+
+    std::string AsString(const DescriptorIndex& index) const {
+      auto p = package(index);
+      return StrCat(p, p.empty() ? "" : ".", symbol(index));
+    }
+  };
+
+  struct SymbolCompare {
+    const DescriptorIndex& index;
+
+    std::string AsString(const SymbolEntry& entry) const {
+      return entry.AsString(index);
+    }
+    static StringPiece AsString(StringPiece str) { return str; }
+
+    std::pair<StringPiece, StringPiece> GetParts(
+        const SymbolEntry& entry) const {
+      auto package = entry.package(index);
+      if (package.empty()) return {entry.symbol(index), StringPiece{}};
+      return {package, entry.symbol(index)};
+    }
+    std::pair<StringPiece, StringPiece> GetParts(
+        StringPiece str) const {
+      return {str, {}};
+    }
+
+    template <typename T, typename U>
+    bool operator()(const T& lhs, const U& rhs) const {
+      auto lhs_parts = GetParts(lhs);
+      auto rhs_parts = GetParts(rhs);
+
+      // Fast path to avoid making the whole string for common cases.
+      if (int res =
+              lhs_parts.first.substr(0, rhs_parts.first.size())
+                  .compare(rhs_parts.first.substr(0, lhs_parts.first.size()))) {
+        // If the packages already differ, exit early.
+        return res < 0;
+      } else if (lhs_parts.first.size() == rhs_parts.first.size()) {
+        return lhs_parts.second < rhs_parts.second;
+      }
+      return AsString(lhs) < AsString(rhs);
+    }
+  };
+  std::set<SymbolEntry, SymbolCompare> by_symbol_{SymbolCompare{*this}};
+  std::vector<SymbolEntry> by_symbol_flat_;
+
+  struct ExtensionEntry {
+    int data_offset;
+    String encoded_extendee;
+    StringPiece extendee(const DescriptorIndex& index) const {
+      return index.DecodeString(encoded_extendee, data_offset).substr(1);
+    }
+    int extension_number;
+  };
+  struct ExtensionCompare {
+    const DescriptorIndex& index;
+
+    bool operator()(const ExtensionEntry& a, const ExtensionEntry& b) const {
+      return std::make_tuple(a.extendee(index), a.extension_number) <
+             std::make_tuple(b.extendee(index), b.extension_number);
+    }
+    bool operator()(const ExtensionEntry& a,
+                    std::tuple<StringPiece, int> b) const {
+      return std::make_tuple(a.extendee(index), a.extension_number) < b;
+    }
+    bool operator()(std::tuple<StringPiece, int> a,
+                    const ExtensionEntry& b) const {
+      return a < std::make_tuple(b.extendee(index), b.extension_number);
+    }
+  };
+  std::set<ExtensionEntry, ExtensionCompare> by_extension_{
+      ExtensionCompare{*this}};
+  std::vector<ExtensionEntry> by_extension_flat_;
+};
+
+bool EncodedDescriptorDatabase::Add(const void* encoded_file_descriptor,
+                                    int size) {
+  FileDescriptorProto file;
+  if (file.ParseFromArray(encoded_file_descriptor, size)) {
+    return index_->AddFile(file, std::make_pair(encoded_file_descriptor, size));
+  } else {
+    GOOGLE_LOG(ERROR) << "Invalid file descriptor data passed to "
+                  "EncodedDescriptorDatabase::Add().";
+    return false;
+  }
+}
+
+bool EncodedDescriptorDatabase::AddCopy(const void* encoded_file_descriptor,
+                                        int size) {
+  void* copy = operator new(size);
+  memcpy(copy, encoded_file_descriptor, size);
+  files_to_delete_.push_back(copy);
+  return Add(copy, size);
+}
+
+bool EncodedDescriptorDatabase::FindFileByName(const std::string& filename,
+                                               FileDescriptorProto* output) {
+  return MaybeParse(index_->FindFile(filename), output);
+}
+
+bool EncodedDescriptorDatabase::FindFileContainingSymbol(
+    const std::string& symbol_name, FileDescriptorProto* output) {
+  return MaybeParse(index_->FindSymbol(symbol_name), output);
+}
+
+bool EncodedDescriptorDatabase::FindNameOfFileContainingSymbol(
+    const std::string& symbol_name, std::string* output) {
+  auto encoded_file = index_->FindSymbol(symbol_name);
+  if (encoded_file.first == nullptr) return false;
+
+  // Optimization:  The name should be the first field in the encoded message.
+  //   Try to just read it directly.
+  io::CodedInputStream input(static_cast<const uint8_t*>(encoded_file.first),
+                             encoded_file.second);
+
+  const uint32_t kNameTag = internal::WireFormatLite::MakeTag(
+      FileDescriptorProto::kNameFieldNumber,
+      internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
+
+  if (input.ReadTagNoLastTag() == kNameTag) {
+    // Success!
+    return internal::WireFormatLite::ReadString(&input, output);
+  } else {
+    // Slow path.  Parse whole message.
+    FileDescriptorProto file_proto;
+    if (!file_proto.ParseFromArray(encoded_file.first, encoded_file.second)) {
+      return false;
+    }
+    *output = file_proto.name();
+    return true;
+  }
+}
+
+bool EncodedDescriptorDatabase::FindFileContainingExtension(
+    const std::string& containing_type, int field_number,
+    FileDescriptorProto* output) {
+  return MaybeParse(index_->FindExtension(containing_type, field_number),
+                    output);
+}
+
+bool EncodedDescriptorDatabase::FindAllExtensionNumbers(
+    const std::string& extendee_type, std::vector<int>* output) {
+  return index_->FindAllExtensionNumbers(extendee_type, output);
+}
+
+template <typename FileProto>
+bool EncodedDescriptorDatabase::DescriptorIndex::AddFile(const FileProto& file,
+                                                         Value value) {
+  // We push `value` into the array first. This is important because the AddXXX
+  // functions below will expect it to be there.
+  all_values_.push_back({value.first, value.second, {}});
+
+  if (!ValidateSymbolName(file.package())) {
+    GOOGLE_LOG(ERROR) << "Invalid package name: " << file.package();
+    return false;
+  }
+  all_values_.back().encoded_package = EncodeString(file.package());
+
+  if (!InsertIfNotPresent(
+          &by_name_, FileEntry{static_cast<int>(all_values_.size() - 1),
+                               EncodeString(file.name())}) ||
+      std::binary_search(by_name_flat_.begin(), by_name_flat_.end(),
+                         file.name(), by_name_.key_comp())) {
+    GOOGLE_LOG(ERROR) << "File already exists in database: " << file.name();
+    return false;
+  }
+
+  for (const auto& message_type : file.message_type()) {
+    if (!AddSymbol(message_type.name())) return false;
+    if (!AddNestedExtensions(file.name(), message_type)) return false;
+  }
+  for (const auto& enum_type : file.enum_type()) {
+    if (!AddSymbol(enum_type.name())) return false;
+  }
+  for (const auto& extension : file.extension()) {
+    if (!AddSymbol(extension.name())) return false;
+    if (!AddExtension(file.name(), extension)) return false;
+  }
+  for (const auto& service : file.service()) {
+    if (!AddSymbol(service.name())) return false;
+  }
+
+  return true;
+}
+
+template <typename Iter, typename Iter2, typename Index>
+static bool CheckForMutualSubsymbols(StringPiece symbol_name, Iter* iter,
+                                     Iter2 end, const Index& index) {
+  if (*iter != end) {
+    if (IsSubSymbol((*iter)->AsString(index), symbol_name)) {
+      GOOGLE_LOG(ERROR) << "Symbol name \"" << symbol_name
+                 << "\" conflicts with the existing symbol \""
+                 << (*iter)->AsString(index) << "\".";
+      return false;
+    }
+
+    // OK, that worked.  Now we have to make sure that no symbol in the map is
+    // a sub-symbol of the one we are inserting.  The only symbol which could
+    // be so is the first symbol that is greater than the new symbol.  Since
+    // |iter| points at the last symbol that is less than or equal, we just have
+    // to increment it.
+    ++*iter;
+
+    if (*iter != end && IsSubSymbol(symbol_name, (*iter)->AsString(index))) {
+      GOOGLE_LOG(ERROR) << "Symbol name \"" << symbol_name
+                 << "\" conflicts with the existing symbol \""
+                 << (*iter)->AsString(index) << "\".";
+      return false;
+    }
+  }
+  return true;
+}
+
+bool EncodedDescriptorDatabase::DescriptorIndex::AddSymbol(
+    StringPiece symbol) {
+  SymbolEntry entry = {static_cast<int>(all_values_.size() - 1),
+                       EncodeString(symbol)};
+  std::string entry_as_string = entry.AsString(*this);
+
+  // We need to make sure not to violate our map invariant.
+
+  // If the symbol name is invalid it could break our lookup algorithm (which
+  // relies on the fact that '.' sorts before all other characters that are
+  // valid in symbol names).
+  if (!ValidateSymbolName(symbol)) {
+    GOOGLE_LOG(ERROR) << "Invalid symbol name: " << entry_as_string;
+    return false;
+  }
+
+  auto iter = FindLastLessOrEqual(&by_symbol_, entry);
+  if (!CheckForMutualSubsymbols(entry_as_string, &iter, by_symbol_.end(),
+                                *this)) {
+    return false;
+  }
+
+  // Same, but on by_symbol_flat_
+  auto flat_iter =
+      FindLastLessOrEqual(&by_symbol_flat_, entry, by_symbol_.key_comp());
+  if (!CheckForMutualSubsymbols(entry_as_string, &flat_iter,
+                                by_symbol_flat_.end(), *this)) {
+    return false;
+  }
+
+  // OK, no conflicts.
+
+  // Insert the new symbol using the iterator as a hint, the new entry will
+  // appear immediately before the one the iterator is pointing at.
+  by_symbol_.insert(iter, entry);
+
+  return true;
+}
+
+template <typename DescProto>
+bool EncodedDescriptorDatabase::DescriptorIndex::AddNestedExtensions(
+    StringPiece filename, const DescProto& message_type) {
+  for (const auto& nested_type : message_type.nested_type()) {
+    if (!AddNestedExtensions(filename, nested_type)) return false;
+  }
+  for (const auto& extension : message_type.extension()) {
+    if (!AddExtension(filename, extension)) return false;
+  }
+  return true;
+}
+
+template <typename FieldProto>
+bool EncodedDescriptorDatabase::DescriptorIndex::AddExtension(
+    StringPiece filename, const FieldProto& field) {
+  if (!field.extendee().empty() && field.extendee()[0] == '.') {
+    // The extension is fully-qualified.  We can use it as a lookup key in
+    // the by_symbol_ table.
+    if (!InsertIfNotPresent(
+            &by_extension_,
+            ExtensionEntry{static_cast<int>(all_values_.size() - 1),
+                           EncodeString(field.extendee()), field.number()}) ||
+        std::binary_search(
+            by_extension_flat_.begin(), by_extension_flat_.end(),
+            std::make_pair(field.extendee().substr(1), field.number()),
+            by_extension_.key_comp())) {
+      GOOGLE_LOG(ERROR) << "Extension conflicts with extension already in database: "
+                    "extend "
+                 << field.extendee() << " { " << field.name() << " = "
+                 << field.number() << " } from:" << filename;
+      return false;
+    }
+  } else {
+    // Not fully-qualified.  We can't really do anything here, unfortunately.
+    // We don't consider this an error, though, because the descriptor is
+    // valid.
+  }
+  return true;
+}
+
+std::pair<const void*, int>
+EncodedDescriptorDatabase::DescriptorIndex::FindSymbol(StringPiece name) {
+  EnsureFlat();
+  return FindSymbolOnlyFlat(name);
+}
+
+std::pair<const void*, int>
+EncodedDescriptorDatabase::DescriptorIndex::FindSymbolOnlyFlat(
+    StringPiece name) const {
+  auto iter =
+      FindLastLessOrEqual(&by_symbol_flat_, name, by_symbol_.key_comp());
+
+  return iter != by_symbol_flat_.end() &&
+                 IsSubSymbol(iter->AsString(*this), name)
+             ? all_values_[iter->data_offset].value()
+             : Value();
+}
+
+std::pair<const void*, int>
+EncodedDescriptorDatabase::DescriptorIndex::FindExtension(
+    StringPiece containing_type, int field_number) {
+  EnsureFlat();
+
+  auto it = std::lower_bound(
+      by_extension_flat_.begin(), by_extension_flat_.end(),
+      std::make_tuple(containing_type, field_number), by_extension_.key_comp());
+  return it == by_extension_flat_.end() ||
+                 it->extendee(*this) != containing_type ||
+                 it->extension_number != field_number
+             ? std::make_pair(nullptr, 0)
+             : all_values_[it->data_offset].value();
+}
+
+template <typename T, typename Less>
+static void MergeIntoFlat(std::set<T, Less>* s, std::vector<T>* flat) {
+  if (s->empty()) return;
+  std::vector<T> new_flat(s->size() + flat->size());
+  std::merge(s->begin(), s->end(), flat->begin(), flat->end(), &new_flat[0],
+             s->key_comp());
+  *flat = std::move(new_flat);
+  s->clear();
+}
+
+void EncodedDescriptorDatabase::DescriptorIndex::EnsureFlat() {
+  all_values_.shrink_to_fit();
+  // Merge each of the sets into their flat counterpart.
+  MergeIntoFlat(&by_name_, &by_name_flat_);
+  MergeIntoFlat(&by_symbol_, &by_symbol_flat_);
+  MergeIntoFlat(&by_extension_, &by_extension_flat_);
+}
+
+bool EncodedDescriptorDatabase::DescriptorIndex::FindAllExtensionNumbers(
+    StringPiece containing_type, std::vector<int>* output) {
+  EnsureFlat();
+
+  bool success = false;
+  auto it = std::lower_bound(
+      by_extension_flat_.begin(), by_extension_flat_.end(),
+      std::make_tuple(containing_type, 0), by_extension_.key_comp());
+  for (;
+       it != by_extension_flat_.end() && it->extendee(*this) == containing_type;
+       ++it) {
+    output->push_back(it->extension_number);
+    success = true;
+  }
+
+  return success;
+}
+
+void EncodedDescriptorDatabase::DescriptorIndex::FindAllFileNames(
+    std::vector<std::string>* output) const {
+  output->resize(by_name_.size() + by_name_flat_.size());
+  int i = 0;
+  for (const auto& entry : by_name_) {
+    (*output)[i] = std::string(entry.name(*this));
+    i++;
+  }
+  for (const auto& entry : by_name_flat_) {
+    (*output)[i] = std::string(entry.name(*this));
+    i++;
+  }
+}
+
+std::pair<const void*, int>
+EncodedDescriptorDatabase::DescriptorIndex::FindFile(
+    StringPiece filename) {
+  EnsureFlat();
+
+  auto it = std::lower_bound(by_name_flat_.begin(), by_name_flat_.end(),
+                             filename, by_name_.key_comp());
+  return it == by_name_flat_.end() || it->name(*this) != filename
+             ? std::make_pair(nullptr, 0)
+             : all_values_[it->data_offset].value();
+}
+
+
+bool EncodedDescriptorDatabase::FindAllFileNames(
+    std::vector<std::string>* output) {
+  index_->FindAllFileNames(output);
+  return true;
+}
+
+bool EncodedDescriptorDatabase::MaybeParse(
+    std::pair<const void*, int> encoded_file, FileDescriptorProto* output) {
+  if (encoded_file.first == nullptr) return false;
+  return output->ParseFromArray(encoded_file.first, encoded_file.second);
+}
+
+EncodedDescriptorDatabase::EncodedDescriptorDatabase()
+    : index_(new DescriptorIndex()) {}
+
+EncodedDescriptorDatabase::~EncodedDescriptorDatabase() {
+  for (void* p : files_to_delete_) {
+    operator delete(p);
+  }
+}
+
+// ===================================================================
+
+DescriptorPoolDatabase::DescriptorPoolDatabase(const DescriptorPool& pool)
+    : pool_(pool) {}
+DescriptorPoolDatabase::~DescriptorPoolDatabase() {}
+
+bool DescriptorPoolDatabase::FindFileByName(const std::string& filename,
+                                            FileDescriptorProto* output) {
+  const FileDescriptor* file = pool_.FindFileByName(filename);
+  if (file == nullptr) return false;
+  output->Clear();
+  file->CopyTo(output);
+  return true;
+}
+
+bool DescriptorPoolDatabase::FindFileContainingSymbol(
+    const std::string& symbol_name, FileDescriptorProto* output) {
+  const FileDescriptor* file = pool_.FindFileContainingSymbol(symbol_name);
+  if (file == nullptr) return false;
+  output->Clear();
+  file->CopyTo(output);
+  return true;
+}
+
+bool DescriptorPoolDatabase::FindFileContainingExtension(
+    const std::string& containing_type, int field_number,
+    FileDescriptorProto* output) {
+  const Descriptor* extendee = pool_.FindMessageTypeByName(containing_type);
+  if (extendee == nullptr) return false;
+
+  const FieldDescriptor* extension =
+      pool_.FindExtensionByNumber(extendee, field_number);
+  if (extension == nullptr) return false;
+
+  output->Clear();
+  extension->file()->CopyTo(output);
+  return true;
+}
+
+bool DescriptorPoolDatabase::FindAllExtensionNumbers(
+    const std::string& extendee_type, std::vector<int>* output) {
+  const Descriptor* extendee = pool_.FindMessageTypeByName(extendee_type);
+  if (extendee == nullptr) return false;
+
+  std::vector<const FieldDescriptor*> extensions;
+  pool_.FindAllExtensions(extendee, &extensions);
+
+  for (const FieldDescriptor* extension : extensions) {
+    output->push_back(extension->number());
+  }
+
+  return true;
+}
+
+// ===================================================================
+
+MergedDescriptorDatabase::MergedDescriptorDatabase(
+    DescriptorDatabase* source1, DescriptorDatabase* source2) {
+  sources_.push_back(source1);
+  sources_.push_back(source2);
+}
+MergedDescriptorDatabase::MergedDescriptorDatabase(
+    const std::vector<DescriptorDatabase*>& sources)
+    : sources_(sources) {}
+MergedDescriptorDatabase::~MergedDescriptorDatabase() {}
+
+bool MergedDescriptorDatabase::FindFileByName(const std::string& filename,
+                                              FileDescriptorProto* output) {
+  for (DescriptorDatabase* source : sources_) {
+    if (source->FindFileByName(filename, output)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool MergedDescriptorDatabase::FindFileContainingSymbol(
+    const std::string& symbol_name, FileDescriptorProto* output) {
+  for (size_t i = 0; i < sources_.size(); i++) {
+    if (sources_[i]->FindFileContainingSymbol(symbol_name, output)) {
+      // The symbol was found in source i.  However, if one of the previous
+      // sources defines a file with the same name (which presumably doesn't
+      // contain the symbol, since it wasn't found in that source), then we
+      // must hide it from the caller.
+      FileDescriptorProto temp;
+      for (size_t j = 0; j < i; j++) {
+        if (sources_[j]->FindFileByName(output->name(), &temp)) {
+          // Found conflicting file in a previous source.
+          return false;
+        }
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+bool MergedDescriptorDatabase::FindFileContainingExtension(
+    const std::string& containing_type, int field_number,
+    FileDescriptorProto* output) {
+  for (size_t i = 0; i < sources_.size(); i++) {
+    if (sources_[i]->FindFileContainingExtension(containing_type, field_number,
+                                                 output)) {
+      // The symbol was found in source i.  However, if one of the previous
+      // sources defines a file with the same name (which presumably doesn't
+      // contain the symbol, since it wasn't found in that source), then we
+      // must hide it from the caller.
+      FileDescriptorProto temp;
+      for (size_t j = 0; j < i; j++) {
+        if (sources_[j]->FindFileByName(output->name(), &temp)) {
+          // Found conflicting file in a previous source.
+          return false;
+        }
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+bool MergedDescriptorDatabase::FindAllExtensionNumbers(
+    const std::string& extendee_type, std::vector<int>* output) {
+  std::set<int> merged_results;
+  std::vector<int> results;
+  bool success = false;
+
+  for (DescriptorDatabase* source : sources_) {
+    if (source->FindAllExtensionNumbers(extendee_type, &results)) {
+      std::copy(results.begin(), results.end(),
+                std::insert_iterator<std::set<int> >(merged_results,
+                                                     merged_results.begin()));
+      success = true;
+    }
+    results.clear();
+  }
+
+  std::copy(merged_results.begin(), merged_results.end(),
+            std::insert_iterator<std::vector<int> >(*output, output->end()));
+
+  return success;
+}
+
+
+bool MergedDescriptorDatabase::FindAllFileNames(
+    std::vector<std::string>* output) {
+  bool implemented = false;
+  for (DescriptorDatabase* source : sources_) {
+    std::vector<std::string> source_output;
+    if (source->FindAllFileNames(&source_output)) {
+      output->reserve(output->size() + source_output.size());
+      for (auto& source : source_output) {
+        output->push_back(std::move(source));
+      }
+      implemented = true;
+    }
+  }
+  return implemented;
+}
+
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/duration.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/duration.pb.cpp
new file mode 100644
index 0000000..72766bd
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/duration.pb.cpp
@@ -0,0 +1,307 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/duration.proto
+
+#include <google/protobuf/duration.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR Duration::Duration(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.seconds_)*/int64_t{0}
+  , /*decltype(_impl_.nanos_)*/0
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct DurationDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR DurationDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~DurationDefaultTypeInternal() {}
+  union {
+    Duration _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 DurationDefaultTypeInternal _Duration_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2fduration_2eproto[1];
+static constexpr ::_pb::EnumDescriptor const** file_level_enum_descriptors_google_2fprotobuf_2fduration_2eproto = nullptr;
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2fduration_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2fduration_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Duration, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Duration, _impl_.seconds_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Duration, _impl_.nanos_),
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Duration)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_Duration_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2fduration_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n\036google/protobuf/duration.proto\022\017google"
+  ".protobuf\"*\n\010Duration\022\017\n\007seconds\030\001 \001(\003\022\r"
+  "\n\005nanos\030\002 \001(\005B\203\001\n\023com.google.protobufB\rD"
+  "urationProtoP\001Z1google.golang.org/protob"
+  "uf/types/known/durationpb\370\001\001\242\002\003GPB\252\002\036Goo"
+  "gle.Protobuf.WellKnownTypesb\006proto3"
+  ;
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2fduration_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fduration_2eproto = {
+    false, false, 235, descriptor_table_protodef_google_2fprotobuf_2fduration_2eproto,
+    "google/protobuf/duration.proto",
+    &descriptor_table_google_2fprotobuf_2fduration_2eproto_once, nullptr, 0, 1,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2fduration_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2fduration_2eproto, file_level_enum_descriptors_google_2fprotobuf_2fduration_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2fduration_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2fduration_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2fduration_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2fduration_2eproto(&descriptor_table_google_2fprotobuf_2fduration_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class Duration::_Internal {
+ public:
+};
+
+Duration::Duration(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Duration)
+}
+Duration::Duration(const Duration& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Duration* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.seconds_){}
+    , decltype(_impl_.nanos_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  ::memcpy(&_impl_.seconds_, &from._impl_.seconds_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.nanos_) -
+    reinterpret_cast<char*>(&_impl_.seconds_)) + sizeof(_impl_.nanos_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Duration)
+}
+
+inline void Duration::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.seconds_){int64_t{0}}
+    , decltype(_impl_.nanos_){0}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+Duration::~Duration() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Duration)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Duration::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void Duration::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Duration::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Duration)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  ::memset(&_impl_.seconds_, 0, static_cast<size_t>(
+      reinterpret_cast<char*>(&_impl_.nanos_) -
+      reinterpret_cast<char*>(&_impl_.seconds_)) + sizeof(_impl_.nanos_));
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Duration::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // int64 seconds = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _impl_.seconds_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // int32 nanos = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _impl_.nanos_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Duration::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Duration)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // int64 seconds = 1;
+  if (this->_internal_seconds() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt64ToArray(1, this->_internal_seconds(), target);
+  }
+
+  // int32 nanos = 2;
+  if (this->_internal_nanos() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_nanos(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Duration)
+  return target;
+}
+
+size_t Duration::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Duration)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // int64 seconds = 1;
+  if (this->_internal_seconds() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int64SizePlusOne(this->_internal_seconds());
+  }
+
+  // int32 nanos = 2;
+  if (this->_internal_nanos() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_nanos());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Duration::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Duration::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Duration::GetClassData() const { return &_class_data_; }
+
+
+void Duration::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Duration*>(&to_msg);
+  auto& from = static_cast<const Duration&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Duration)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (from._internal_seconds() != 0) {
+    _this->_internal_set_seconds(from._internal_seconds());
+  }
+  if (from._internal_nanos() != 0) {
+    _this->_internal_set_nanos(from._internal_nanos());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Duration::CopyFrom(const Duration& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Duration)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Duration::IsInitialized() const {
+  return true;
+}
+
+void Duration::InternalSwap(Duration* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(Duration, _impl_.nanos_)
+      + sizeof(Duration::_impl_.nanos_)
+      - PROTOBUF_FIELD_OFFSET(Duration, _impl_.seconds_)>(
+          reinterpret_cast<char*>(&_impl_.seconds_),
+          reinterpret_cast<char*>(&other->_impl_.seconds_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Duration::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fduration_2eproto_getter, &descriptor_table_google_2fprotobuf_2fduration_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fduration_2eproto[0]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Duration*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Duration >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Duration >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/dynamic_message.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/dynamic_message.cpp
new file mode 100644
index 0000000..1c96ca2
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/dynamic_message.cpp
@@ -0,0 +1,826 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// DynamicMessage is implemented by constructing a data structure which
+// has roughly the same memory layout as a generated message would have.
+// Then, we use Reflection to implement our reflection interface.  All
+// the other operations we need to implement (e.g.  parsing, copying,
+// etc.) are already implemented in terms of Reflection, so the rest is
+// easy.
+//
+// The up side of this strategy is that it's very efficient.  We don't
+// need to use hash_maps or generic representations of fields.  The
+// down side is that this is a low-level memory management hack which
+// can be tricky to get right.
+//
+// As mentioned in the header, we only expose a DynamicMessageFactory
+// publicly, not the DynamicMessage class itself.  This is because
+// GenericMessageReflection wants to have a pointer to a "default"
+// copy of the class, with all fields initialized to their default
+// values.  We only want to construct one of these per message type,
+// so DynamicMessageFactory stores a cache of default messages for
+// each type it sees (each unique Descriptor pointer).  The code
+// refers to the "default" copy of the class as the "prototype".
+//
+// Note on memory allocation:  This module often calls "operator new()"
+// to allocate untyped memory, rather than calling something like
+// "new uint8_t[]".  This is because "operator new()" means "Give me some
+// space which I can use as I please." while "new uint8_t[]" means "Give
+// me an array of 8-bit integers.".  In practice, the later may return
+// a pointer that is not aligned correctly for general use.  I believe
+// Item 8 of "More Effective C++" discusses this in more detail, though
+// I don't have the book on me right now so I'm not sure.
+
+#include <google/protobuf/dynamic_message.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <new>
+#include <unordered_map>
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/stubs/hash.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/map_field_inl.h>
+#include <google/protobuf/map_type_handler.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/wire_format.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+using internal::DynamicMapField;
+using internal::ExtensionSet;
+using internal::MapField;
+
+
+using internal::ArenaStringPtr;
+
+// ===================================================================
+// Some helper tables and functions...
+
+namespace {
+
+bool IsMapFieldInApi(const FieldDescriptor* field) { return field->is_map(); }
+
+// Sync with helpers.h.
+inline bool HasHasbit(const FieldDescriptor* field) {
+  // This predicate includes proto3 message fields only if they have "optional".
+  //   Foo submsg1 = 1;           // HasHasbit() == false
+  //   optional Foo submsg2 = 2;  // HasHasbit() == true
+  // This is slightly odd, as adding "optional" to a singular proto3 field does
+  // not change the semantics or API. However whenever any field in a message
+  // has a hasbit, it forces reflection to include hasbit offsets for *all*
+  // fields, even if almost all of them are set to -1 (no hasbit). So to avoid
+  // causing a sudden size regression for ~all proto3 messages, we give proto3
+  // message fields a hasbit only if "optional" is present. If the user is
+  // explicitly writing "optional", it is likely they are writing it on
+  // primitive fields also.
+  return (field->has_optional_keyword() || field->is_required()) &&
+         !field->options().weak();
+}
+
+inline bool InRealOneof(const FieldDescriptor* field) {
+  return field->containing_oneof() &&
+         !field->containing_oneof()->is_synthetic();
+}
+
+// Compute the byte size of the in-memory representation of the field.
+int FieldSpaceUsed(const FieldDescriptor* field) {
+  typedef FieldDescriptor FD;  // avoid line wrapping
+  if (field->label() == FD::LABEL_REPEATED) {
+    switch (field->cpp_type()) {
+      case FD::CPPTYPE_INT32:
+        return sizeof(RepeatedField<int32_t>);
+      case FD::CPPTYPE_INT64:
+        return sizeof(RepeatedField<int64_t>);
+      case FD::CPPTYPE_UINT32:
+        return sizeof(RepeatedField<uint32_t>);
+      case FD::CPPTYPE_UINT64:
+        return sizeof(RepeatedField<uint64_t>);
+      case FD::CPPTYPE_DOUBLE:
+        return sizeof(RepeatedField<double>);
+      case FD::CPPTYPE_FLOAT:
+        return sizeof(RepeatedField<float>);
+      case FD::CPPTYPE_BOOL:
+        return sizeof(RepeatedField<bool>);
+      case FD::CPPTYPE_ENUM:
+        return sizeof(RepeatedField<int>);
+      case FD::CPPTYPE_MESSAGE:
+        if (IsMapFieldInApi(field)) {
+          return sizeof(DynamicMapField);
+        } else {
+          return sizeof(RepeatedPtrField<Message>);
+        }
+
+      case FD::CPPTYPE_STRING:
+        switch (field->options().ctype()) {
+          default:  // TODO(kenton):  Support other string reps.
+          case FieldOptions::STRING:
+            return sizeof(RepeatedPtrField<std::string>);
+        }
+        break;
+    }
+  } else {
+    switch (field->cpp_type()) {
+      case FD::CPPTYPE_INT32:
+        return sizeof(int32_t);
+      case FD::CPPTYPE_INT64:
+        return sizeof(int64_t);
+      case FD::CPPTYPE_UINT32:
+        return sizeof(uint32_t);
+      case FD::CPPTYPE_UINT64:
+        return sizeof(uint64_t);
+      case FD::CPPTYPE_DOUBLE:
+        return sizeof(double);
+      case FD::CPPTYPE_FLOAT:
+        return sizeof(float);
+      case FD::CPPTYPE_BOOL:
+        return sizeof(bool);
+      case FD::CPPTYPE_ENUM:
+        return sizeof(int);
+
+      case FD::CPPTYPE_MESSAGE:
+        return sizeof(Message*);
+
+      case FD::CPPTYPE_STRING:
+        switch (field->options().ctype()) {
+          default:  // TODO(kenton):  Support other string reps.
+          case FieldOptions::STRING:
+            return sizeof(ArenaStringPtr);
+        }
+        break;
+    }
+  }
+
+  GOOGLE_LOG(DFATAL) << "Can't get here.";
+  return 0;
+}
+
+inline int DivideRoundingUp(int i, int j) { return (i + (j - 1)) / j; }
+
+static const int kSafeAlignment = sizeof(uint64_t);
+static const int kMaxOneofUnionSize = sizeof(uint64_t);
+
+inline int AlignTo(int offset, int alignment) {
+  return DivideRoundingUp(offset, alignment) * alignment;
+}
+
+// Rounds the given byte offset up to the next offset aligned such that any
+// type may be stored at it.
+inline int AlignOffset(int offset) { return AlignTo(offset, kSafeAlignment); }
+
+#define bitsizeof(T) (sizeof(T) * 8)
+
+}  // namespace
+
+// ===================================================================
+
+class DynamicMessage : public Message {
+ public:
+  explicit DynamicMessage(const DynamicMessageFactory::TypeInfo* type_info);
+
+  // This should only be used by GetPrototypeNoLock() to avoid dead lock.
+  DynamicMessage(DynamicMessageFactory::TypeInfo* type_info, bool lock_factory);
+
+  ~DynamicMessage() override;
+
+  // Called on the prototype after construction to initialize message fields.
+  // Cross linking the default instances allows for fast reflection access of
+  // unset message fields. Without it we would have to go to the MessageFactory
+  // to get the prototype, which is a much more expensive operation.
+  //
+  // Generated messages do not cross-link to avoid dynamic initialization of the
+  // global instances.
+  // Instead, they keep the default instances in the FieldDescriptor objects.
+  void CrossLinkPrototypes();
+
+  // implements Message ----------------------------------------------
+
+  Message* New(Arena* arena) const override;
+
+  int GetCachedSize() const override;
+  void SetCachedSize(int size) const override;
+
+  Metadata GetMetadata() const override;
+
+#if defined(__cpp_lib_destroying_delete) && defined(__cpp_sized_deallocation)
+  static void operator delete(DynamicMessage* msg, std::destroying_delete_t);
+#else
+  // We actually allocate more memory than sizeof(*this) when this
+  // class's memory is allocated via the global operator new. Thus, we need to
+  // manually call the global operator delete. Calling the destructor is taken
+  // care of for us. This makes DynamicMessage compatible with -fsized-delete.
+  // It doesn't work for MSVC though.
+#ifndef _MSC_VER
+  static void operator delete(void* ptr) { ::operator delete(ptr); }
+#endif  // !_MSC_VER
+#endif
+
+ private:
+  DynamicMessage(const DynamicMessageFactory::TypeInfo* type_info,
+                 Arena* arena);
+
+  void SharedCtor(bool lock_factory);
+
+  // Needed to get the offset of the internal metadata member.
+  friend class DynamicMessageFactory;
+
+  bool is_prototype() const;
+
+  inline void* OffsetToPointer(int offset) {
+    return reinterpret_cast<uint8_t*>(this) + offset;
+  }
+  inline const void* OffsetToPointer(int offset) const {
+    return reinterpret_cast<const uint8_t*>(this) + offset;
+  }
+
+  void* MutableRaw(int i);
+  void* MutableExtensionsRaw();
+  void* MutableWeakFieldMapRaw();
+  void* MutableOneofCaseRaw(int i);
+  void* MutableOneofFieldRaw(const FieldDescriptor* f);
+
+  const DynamicMessageFactory::TypeInfo* type_info_;
+  mutable std::atomic<int> cached_byte_size_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DynamicMessage);
+};
+
+struct DynamicMessageFactory::TypeInfo {
+  int size;
+  int has_bits_offset;
+  int oneof_case_offset;
+  int extensions_offset;
+
+  // Not owned by the TypeInfo.
+  DynamicMessageFactory* factory;  // The factory that created this object.
+  const DescriptorPool* pool;      // The factory's DescriptorPool.
+  const Descriptor* type;          // Type of this DynamicMessage.
+
+  // Warning:  The order in which the following pointers are defined is
+  //   important (the prototype must be deleted *before* the offsets).
+  std::unique_ptr<uint32_t[]> offsets;
+  std::unique_ptr<uint32_t[]> has_bits_indices;
+  std::unique_ptr<const Reflection> reflection;
+  // Don't use a unique_ptr to hold the prototype: the destructor for
+  // DynamicMessage needs to know whether it is the prototype, and does so by
+  // looking back at this field. This would assume details about the
+  // implementation of unique_ptr.
+  const DynamicMessage* prototype;
+  int weak_field_map_offset;  // The offset for the weak_field_map;
+
+  TypeInfo() : prototype(nullptr) {}
+
+  ~TypeInfo() { delete prototype; }
+};
+
+DynamicMessage::DynamicMessage(const DynamicMessageFactory::TypeInfo* type_info)
+    : type_info_(type_info), cached_byte_size_(0) {
+  SharedCtor(true);
+}
+
+DynamicMessage::DynamicMessage(const DynamicMessageFactory::TypeInfo* type_info,
+                               Arena* arena)
+    : Message(arena), type_info_(type_info), cached_byte_size_(0) {
+  SharedCtor(true);
+}
+
+DynamicMessage::DynamicMessage(DynamicMessageFactory::TypeInfo* type_info,
+                               bool lock_factory)
+    : type_info_(type_info), cached_byte_size_(0) {
+  // The prototype in type_info has to be set before creating the prototype
+  // instance on memory. e.g., message Foo { map<int32_t, Foo> a = 1; }. When
+  // creating prototype for Foo, prototype of the map entry will also be
+  // created, which needs the address of the prototype of Foo (the value in
+  // map). To break the cyclic dependency, we have to assign the address of
+  // prototype into type_info first.
+  type_info->prototype = this;
+  SharedCtor(lock_factory);
+}
+
+inline void* DynamicMessage::MutableRaw(int i) {
+  return OffsetToPointer(type_info_->offsets[i]);
+}
+inline void* DynamicMessage::MutableExtensionsRaw() {
+  return OffsetToPointer(type_info_->extensions_offset);
+}
+inline void* DynamicMessage::MutableWeakFieldMapRaw() {
+  return OffsetToPointer(type_info_->weak_field_map_offset);
+}
+inline void* DynamicMessage::MutableOneofCaseRaw(int i) {
+  return OffsetToPointer(type_info_->oneof_case_offset + sizeof(uint32_t) * i);
+}
+inline void* DynamicMessage::MutableOneofFieldRaw(const FieldDescriptor* f) {
+  return OffsetToPointer(type_info_->offsets[type_info_->type->field_count() +
+                                             f->containing_oneof()->index()]);
+}
+
+void DynamicMessage::SharedCtor(bool lock_factory) {
+  // We need to call constructors for various fields manually and set
+  // default values where appropriate.  We use placement new to call
+  // constructors.  If you haven't heard of placement new, I suggest Googling
+  // it now.  We use placement new even for primitive types that don't have
+  // constructors for consistency.  (In theory, placement new should be used
+  // any time you are trying to convert untyped memory to typed memory, though
+  // in practice that's not strictly necessary for types that don't have a
+  // constructor.)
+
+  const Descriptor* descriptor = type_info_->type;
+  // Initialize oneof cases.
+  int oneof_count = 0;
+  for (int i = 0; i < descriptor->oneof_decl_count(); ++i) {
+    if (descriptor->oneof_decl(i)->is_synthetic()) continue;
+    new (MutableOneofCaseRaw(oneof_count++)) uint32_t{0};
+  }
+
+  if (type_info_->extensions_offset != -1) {
+    new (MutableExtensionsRaw()) ExtensionSet(GetArenaForAllocation());
+  }
+  for (int i = 0; i < descriptor->field_count(); i++) {
+    const FieldDescriptor* field = descriptor->field(i);
+    void* field_ptr = MutableRaw(i);
+    if (InRealOneof(field)) {
+      continue;
+    }
+    switch (field->cpp_type()) {
+#define HANDLE_TYPE(CPPTYPE, TYPE)                                  \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE:                          \
+    if (!field->is_repeated()) {                                    \
+      new (field_ptr) TYPE(field->default_value_##TYPE());          \
+    } else {                                                        \
+      new (field_ptr) RepeatedField<TYPE>(GetArenaForAllocation()); \
+    }                                                               \
+    break;
+
+      HANDLE_TYPE(INT32, int32_t);
+      HANDLE_TYPE(INT64, int64_t);
+      HANDLE_TYPE(UINT32, uint32_t);
+      HANDLE_TYPE(UINT64, uint64_t);
+      HANDLE_TYPE(DOUBLE, double);
+      HANDLE_TYPE(FLOAT, float);
+      HANDLE_TYPE(BOOL, bool);
+#undef HANDLE_TYPE
+
+      case FieldDescriptor::CPPTYPE_ENUM:
+        if (!field->is_repeated()) {
+          new (field_ptr) int{field->default_value_enum()->number()};
+        } else {
+          new (field_ptr) RepeatedField<int>(GetArenaForAllocation());
+        }
+        break;
+
+      case FieldDescriptor::CPPTYPE_STRING:
+        switch (field->options().ctype()) {
+          default:  // TODO(kenton):  Support other string reps.
+          case FieldOptions::STRING:
+            if (!field->is_repeated()) {
+              ArenaStringPtr* asp = new (field_ptr) ArenaStringPtr();
+              asp->InitDefault();
+            } else {
+              new (field_ptr)
+                  RepeatedPtrField<std::string>(GetArenaForAllocation());
+            }
+            break;
+        }
+        break;
+
+      case FieldDescriptor::CPPTYPE_MESSAGE: {
+        if (!field->is_repeated()) {
+          new (field_ptr) Message*(nullptr);
+        } else {
+          if (IsMapFieldInApi(field)) {
+            // We need to lock in most cases to avoid data racing. Only not lock
+            // when the constructor is called inside GetPrototype(), in which
+            // case we have already locked the factory.
+            if (lock_factory) {
+              if (GetArenaForAllocation() != nullptr) {
+                new (field_ptr) DynamicMapField(
+                    type_info_->factory->GetPrototype(field->message_type()),
+                    GetArenaForAllocation());
+                if (GetOwningArena() != nullptr) {
+                  // Needs to destroy the mutex member.
+                  GetOwningArena()->OwnDestructor(
+                      static_cast<DynamicMapField*>(field_ptr));
+                }
+              } else {
+                new (field_ptr) DynamicMapField(
+                    type_info_->factory->GetPrototype(field->message_type()));
+              }
+            } else {
+              if (GetArenaForAllocation() != nullptr) {
+                new (field_ptr)
+                    DynamicMapField(type_info_->factory->GetPrototypeNoLock(
+                                        field->message_type()),
+                                    GetArenaForAllocation());
+                if (GetOwningArena() != nullptr) {
+                  // Needs to destroy the mutex member.
+                  GetOwningArena()->OwnDestructor(
+                      static_cast<DynamicMapField*>(field_ptr));
+                }
+              } else {
+                new (field_ptr)
+                    DynamicMapField(type_info_->factory->GetPrototypeNoLock(
+                        field->message_type()));
+              }
+            }
+          } else {
+            new (field_ptr) RepeatedPtrField<Message>(GetArenaForAllocation());
+          }
+        }
+        break;
+      }
+    }
+  }
+}
+
+bool DynamicMessage::is_prototype() const {
+  return type_info_->prototype == this ||
+         // If type_info_->prototype is nullptr, then we must be constructing
+         // the prototype now, which means we must be the prototype.
+         type_info_->prototype == nullptr;
+}
+
+#if defined(__cpp_lib_destroying_delete) && defined(__cpp_sized_deallocation)
+void DynamicMessage::operator delete(DynamicMessage* msg,
+                                     std::destroying_delete_t) {
+  const size_t size = msg->type_info_->size;
+  msg->~DynamicMessage();
+  ::operator delete(msg, size);
+}
+#endif
+
+DynamicMessage::~DynamicMessage() {
+  const Descriptor* descriptor = type_info_->type;
+
+  _internal_metadata_.Delete<UnknownFieldSet>();
+
+  if (type_info_->extensions_offset != -1) {
+    reinterpret_cast<ExtensionSet*>(MutableExtensionsRaw())->~ExtensionSet();
+  }
+
+  // We need to manually run the destructors for repeated fields and strings,
+  // just as we ran their constructors in the DynamicMessage constructor.
+  // We also need to manually delete oneof fields if it is set and is string
+  // or message.
+  // Additionally, if any singular embedded messages have been allocated, we
+  // need to delete them, UNLESS we are the prototype message of this type,
+  // in which case any embedded messages are other prototypes and shouldn't
+  // be touched.
+  for (int i = 0; i < descriptor->field_count(); i++) {
+    const FieldDescriptor* field = descriptor->field(i);
+    if (InRealOneof(field)) {
+      void* field_ptr = MutableOneofCaseRaw(field->containing_oneof()->index());
+      if (*(reinterpret_cast<const int32_t*>(field_ptr)) == field->number()) {
+        field_ptr = MutableOneofFieldRaw(field);
+        if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
+          switch (field->options().ctype()) {
+            default:
+            case FieldOptions::STRING: {
+              reinterpret_cast<ArenaStringPtr*>(field_ptr)->Destroy();
+              break;
+            }
+          }
+        } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+            delete *reinterpret_cast<Message**>(field_ptr);
+        }
+      }
+      continue;
+    }
+    void* field_ptr = MutableRaw(i);
+
+    if (field->is_repeated()) {
+      switch (field->cpp_type()) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE)                  \
+  case FieldDescriptor::CPPTYPE_##UPPERCASE:               \
+    reinterpret_cast<RepeatedField<LOWERCASE>*>(field_ptr) \
+        ->~RepeatedField<LOWERCASE>();                     \
+    break
+
+        HANDLE_TYPE(INT32, int32_t);
+        HANDLE_TYPE(INT64, int64_t);
+        HANDLE_TYPE(UINT32, uint32_t);
+        HANDLE_TYPE(UINT64, uint64_t);
+        HANDLE_TYPE(DOUBLE, double);
+        HANDLE_TYPE(FLOAT, float);
+        HANDLE_TYPE(BOOL, bool);
+        HANDLE_TYPE(ENUM, int);
+#undef HANDLE_TYPE
+
+        case FieldDescriptor::CPPTYPE_STRING:
+          switch (field->options().ctype()) {
+            default:  // TODO(kenton):  Support other string reps.
+            case FieldOptions::STRING:
+              reinterpret_cast<RepeatedPtrField<std::string>*>(field_ptr)
+                  ->~RepeatedPtrField<std::string>();
+              break;
+          }
+          break;
+
+        case FieldDescriptor::CPPTYPE_MESSAGE:
+          if (IsMapFieldInApi(field)) {
+            reinterpret_cast<DynamicMapField*>(field_ptr)->~DynamicMapField();
+          } else {
+            reinterpret_cast<RepeatedPtrField<Message>*>(field_ptr)
+                ->~RepeatedPtrField<Message>();
+          }
+          break;
+      }
+
+    } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
+      switch (field->options().ctype()) {
+        default:  // TODO(kenton):  Support other string reps.
+        case FieldOptions::STRING: {
+          reinterpret_cast<ArenaStringPtr*>(field_ptr)->Destroy();
+          break;
+        }
+      }
+    } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+          if (!is_prototype()) {
+        Message* message = *reinterpret_cast<Message**>(field_ptr);
+        if (message != nullptr) {
+          delete message;
+        }
+      }
+    }
+  }
+}
+
+void DynamicMessage::CrossLinkPrototypes() {
+  // This should only be called on the prototype message.
+  GOOGLE_CHECK(is_prototype());
+
+  DynamicMessageFactory* factory = type_info_->factory;
+  const Descriptor* descriptor = type_info_->type;
+
+  // Cross-link default messages.
+  for (int i = 0; i < descriptor->field_count(); i++) {
+    const FieldDescriptor* field = descriptor->field(i);
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+        !field->options().weak() && !InRealOneof(field) &&
+        !field->is_repeated()) {
+      void* field_ptr = MutableRaw(i);
+      // For fields with message types, we need to cross-link with the
+      // prototype for the field's type.
+      // For singular fields, the field is just a pointer which should
+      // point to the prototype.
+      *reinterpret_cast<const Message**>(field_ptr) =
+          factory->GetPrototypeNoLock(field->message_type());
+    }
+  }
+}
+
+Message* DynamicMessage::New(Arena* arena) const {
+  if (arena != nullptr) {
+    void* new_base = Arena::CreateArray<char>(arena, type_info_->size);
+    memset(new_base, 0, type_info_->size);
+    return new (new_base) DynamicMessage(type_info_, arena);
+  } else {
+    void* new_base = operator new(type_info_->size);
+    memset(new_base, 0, type_info_->size);
+    return new (new_base) DynamicMessage(type_info_);
+  }
+}
+
+int DynamicMessage::GetCachedSize() const {
+  return cached_byte_size_.load(std::memory_order_relaxed);
+}
+
+void DynamicMessage::SetCachedSize(int size) const {
+  cached_byte_size_.store(size, std::memory_order_relaxed);
+}
+
+Metadata DynamicMessage::GetMetadata() const {
+  Metadata metadata;
+  metadata.descriptor = type_info_->type;
+  metadata.reflection = type_info_->reflection.get();
+  return metadata;
+}
+
+// ===================================================================
+
+DynamicMessageFactory::DynamicMessageFactory()
+    : pool_(nullptr), delegate_to_generated_factory_(false) {}
+
+DynamicMessageFactory::DynamicMessageFactory(const DescriptorPool* pool)
+    : pool_(pool), delegate_to_generated_factory_(false) {}
+
+DynamicMessageFactory::~DynamicMessageFactory() {
+  for (auto iter = prototypes_.begin(); iter != prototypes_.end(); ++iter) {
+    delete iter->second;
+  }
+}
+
+const Message* DynamicMessageFactory::GetPrototype(const Descriptor* type) {
+  MutexLock lock(&prototypes_mutex_);
+  return GetPrototypeNoLock(type);
+}
+
+const Message* DynamicMessageFactory::GetPrototypeNoLock(
+    const Descriptor* type) {
+  if (delegate_to_generated_factory_ &&
+      type->file()->pool() == DescriptorPool::generated_pool()) {
+    return MessageFactory::generated_factory()->GetPrototype(type);
+  }
+
+  const TypeInfo** target = &prototypes_[type];
+  if (*target != nullptr) {
+    // Already exists.
+    return (*target)->prototype;
+  }
+
+  TypeInfo* type_info = new TypeInfo;
+  *target = type_info;
+
+  type_info->type = type;
+  type_info->pool = (pool_ == nullptr) ? type->file()->pool() : pool_;
+  type_info->factory = this;
+
+  // We need to construct all the structures passed to Reflection's constructor.
+  // This includes:
+  // - A block of memory that contains space for all the message's fields.
+  // - An array of integers indicating the byte offset of each field within
+  //   this block.
+  // - A big bitfield containing a bit for each field indicating whether
+  //   or not that field is set.
+  int real_oneof_count = 0;
+  for (int i = 0; i < type->oneof_decl_count(); i++) {
+    if (!type->oneof_decl(i)->is_synthetic()) {
+      real_oneof_count++;
+    }
+  }
+
+  // Compute size and offsets.
+  uint32_t* offsets = new uint32_t[type->field_count() + real_oneof_count];
+  type_info->offsets.reset(offsets);
+
+  // Decide all field offsets by packing in order.
+  // We place the DynamicMessage object itself at the beginning of the allocated
+  // space.
+  int size = sizeof(DynamicMessage);
+  size = AlignOffset(size);
+
+  // Next the has_bits, which is an array of uint32s.
+  type_info->has_bits_offset = -1;
+  int max_hasbit = 0;
+  for (int i = 0; i < type->field_count(); i++) {
+    if (HasHasbit(type->field(i))) {
+      if (type_info->has_bits_offset == -1) {
+        // At least one field in the message requires a hasbit, so allocate
+        // hasbits.
+        type_info->has_bits_offset = size;
+        uint32_t* has_bits_indices = new uint32_t[type->field_count()];
+        for (int j = 0; j < type->field_count(); j++) {
+          // Initialize to -1, fields that need a hasbit will overwrite.
+          has_bits_indices[j] = static_cast<uint32_t>(-1);
+        }
+        type_info->has_bits_indices.reset(has_bits_indices);
+      }
+      type_info->has_bits_indices[i] = max_hasbit++;
+    }
+  }
+
+  if (max_hasbit > 0) {
+    int has_bits_array_size = DivideRoundingUp(max_hasbit, bitsizeof(uint32_t));
+    size += has_bits_array_size * sizeof(uint32_t);
+    size = AlignOffset(size);
+  }
+
+  // The oneof_case, if any. It is an array of uint32s.
+  if (real_oneof_count > 0) {
+    type_info->oneof_case_offset = size;
+    size += real_oneof_count * sizeof(uint32_t);
+    size = AlignOffset(size);
+  }
+
+  // The ExtensionSet, if any.
+  if (type->extension_range_count() > 0) {
+    type_info->extensions_offset = size;
+    size += sizeof(ExtensionSet);
+    size = AlignOffset(size);
+  } else {
+    // No extensions.
+    type_info->extensions_offset = -1;
+  }
+
+  // All the fields.
+  //
+  // TODO(b/31226269):  Optimize the order of fields to minimize padding.
+  for (int i = 0; i < type->field_count(); i++) {
+    // Make sure field is aligned to avoid bus errors.
+    // Oneof fields do not use any space.
+    if (!InRealOneof(type->field(i))) {
+      int field_size = FieldSpaceUsed(type->field(i));
+      size = AlignTo(size, std::min(kSafeAlignment, field_size));
+      offsets[i] = size;
+      size += field_size;
+    }
+  }
+
+  // The oneofs.
+  for (int i = 0; i < type->oneof_decl_count(); i++) {
+    if (!type->oneof_decl(i)->is_synthetic()) {
+      size = AlignTo(size, kSafeAlignment);
+      offsets[type->field_count() + i] = size;
+      size += kMaxOneofUnionSize;
+    }
+  }
+
+  type_info->weak_field_map_offset = -1;
+
+  // Align the final size to make sure no clever allocators think that
+  // alignment is not necessary.
+  type_info->size = size;
+
+  // Construct the reflection object.
+
+  // Compute the size of default oneof instance and offsets of default
+  // oneof fields.
+  for (int i = 0; i < type->oneof_decl_count(); i++) {
+    if (type->oneof_decl(i)->is_synthetic()) continue;
+    for (int j = 0; j < type->oneof_decl(i)->field_count(); j++) {
+      const FieldDescriptor* field = type->oneof_decl(i)->field(j);
+      // oneof fields are not accessed through offsets, but we still have the
+      // entry from a legacy implementation. This should be removed at some
+      // point.
+      // Mark the field to prevent unintentional access through reflection.
+      // Don't use the top bit because that is for unused fields.
+      offsets[field->index()] = internal::kInvalidFieldOffsetTag;
+    }
+  }
+
+  // Allocate the prototype fields.
+  void* base = operator new(size);
+  memset(base, 0, size);
+
+  // We have already locked the factory so we should not lock in the constructor
+  // of dynamic message to avoid dead lock.
+  DynamicMessage* prototype = new (base) DynamicMessage(type_info, false);
+
+  internal::ReflectionSchema schema = {
+      type_info->prototype,
+      type_info->offsets.get(),
+      type_info->has_bits_indices.get(),
+      type_info->has_bits_offset,
+      PROTOBUF_FIELD_OFFSET(DynamicMessage, _internal_metadata_),
+      type_info->extensions_offset,
+      type_info->oneof_case_offset,
+      type_info->size,
+      type_info->weak_field_map_offset,
+      nullptr /* inlined_string_indices_ */,
+      0 /* inlined_string_donated_offset_ */};
+
+  type_info->reflection.reset(
+      new Reflection(type_info->type, schema, type_info->pool, this));
+
+  // Cross link prototypes.
+  prototype->CrossLinkPrototypes();
+
+  return prototype;
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>  // NOLINT
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/empty.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/empty.pb.cpp
new file mode 100644
index 0000000..3a30776
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/empty.pb.cpp
@@ -0,0 +1,130 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/empty.proto
+
+#include <google/protobuf/empty.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR Empty::Empty(
+    ::_pbi::ConstantInitialized) {}
+struct EmptyDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR EmptyDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~EmptyDefaultTypeInternal() {}
+  union {
+    Empty _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 EmptyDefaultTypeInternal _Empty_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2fempty_2eproto[1];
+static constexpr ::_pb::EnumDescriptor const** file_level_enum_descriptors_google_2fprotobuf_2fempty_2eproto = nullptr;
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2fempty_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2fempty_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Empty, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Empty)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_Empty_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2fempty_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n\033google/protobuf/empty.proto\022\017google.pr"
+  "otobuf\"\007\n\005EmptyB}\n\023com.google.protobufB\n"
+  "EmptyProtoP\001Z.google.golang.org/protobuf"
+  "/types/known/emptypb\370\001\001\242\002\003GPB\252\002\036Google.P"
+  "rotobuf.WellKnownTypesb\006proto3"
+  ;
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2fempty_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fempty_2eproto = {
+    false, false, 190, descriptor_table_protodef_google_2fprotobuf_2fempty_2eproto,
+    "google/protobuf/empty.proto",
+    &descriptor_table_google_2fprotobuf_2fempty_2eproto_once, nullptr, 0, 1,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2fempty_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2fempty_2eproto, file_level_enum_descriptors_google_2fprotobuf_2fempty_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2fempty_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2fempty_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2fempty_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2fempty_2eproto(&descriptor_table_google_2fprotobuf_2fempty_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class Empty::_Internal {
+ public:
+};
+
+Empty::Empty(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase(arena, is_message_owned) {
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Empty)
+}
+Empty::Empty(const Empty& from)
+  : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase() {
+  Empty* const _this = this; (void)_this;
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Empty)
+}
+
+
+
+
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Empty::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyImpl,
+    ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeImpl,
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Empty::GetClassData() const { return &_class_data_; }
+
+
+
+
+
+
+
+::PROTOBUF_NAMESPACE_ID::Metadata Empty::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fempty_2eproto_getter, &descriptor_table_google_2fprotobuf_2fempty_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fempty_2eproto[0]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Empty*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Empty >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Empty >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/extension_set.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/extension_set.cpp
new file mode 100644
index 0000000..fada4f5
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/extension_set.cpp
@@ -0,0 +1,1967 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/extension_set.h>
+
+#include <tuple>
+#include <unordered_set>
+#include <utility>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/extension_set_inl.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/port.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/hash.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>  // must be last.
+// clang-format on
+namespace google {
+namespace protobuf {
+namespace internal {
+
+namespace {
+
+inline WireFormatLite::FieldType real_type(FieldType type) {
+  GOOGLE_DCHECK(type > 0 && type <= WireFormatLite::MAX_FIELD_TYPE);
+  return static_cast<WireFormatLite::FieldType>(type);
+}
+
+inline WireFormatLite::CppType cpp_type(FieldType type) {
+  return WireFormatLite::FieldTypeToCppType(real_type(type));
+}
+
+// Registry stuff.
+
+// Note that we cannot use hetererogeneous lookup for std containers since we
+// need to support C++11.
+struct ExtensionEq {
+  bool operator()(const ExtensionInfo& lhs, const ExtensionInfo& rhs) const {
+    return lhs.message == rhs.message && lhs.number == rhs.number;
+  }
+};
+
+struct ExtensionHasher {
+  std::size_t operator()(const ExtensionInfo& info) const {
+    return std::hash<const MessageLite*>{}(info.message) ^
+           std::hash<int>{}(info.number);
+  }
+};
+
+using ExtensionRegistry =
+    std::unordered_set<ExtensionInfo, ExtensionHasher, ExtensionEq>;
+
+static const ExtensionRegistry* global_registry = nullptr;
+
+// This function is only called at startup, so there is no need for thread-
+// safety.
+void Register(const ExtensionInfo& info) {
+  static auto local_static_registry = OnShutdownDelete(new ExtensionRegistry);
+  global_registry = local_static_registry;
+  if (!InsertIfNotPresent(local_static_registry, info)) {
+    GOOGLE_LOG(FATAL) << "Multiple extension registrations for type \""
+               << info.message->GetTypeName() << "\", field number "
+               << info.number << ".";
+  }
+}
+
+const ExtensionInfo* FindRegisteredExtension(const MessageLite* extendee,
+                                             int number) {
+  if (!global_registry) return nullptr;
+
+  ExtensionInfo info;
+  info.message = extendee;
+  info.number = number;
+
+  auto it = global_registry->find(info);
+  if (it == global_registry->end()) {
+    return nullptr;
+  } else {
+    return &*it;
+  }
+}
+
+}  // namespace
+
+bool GeneratedExtensionFinder::Find(int number, ExtensionInfo* output) {
+  const ExtensionInfo* extension = FindRegisteredExtension(extendee_, number);
+  if (extension == nullptr) {
+    return false;
+  } else {
+    *output = *extension;
+    return true;
+  }
+}
+
+void ExtensionSet::RegisterExtension(const MessageLite* extendee, int number,
+                                     FieldType type, bool is_repeated,
+                                     bool is_packed,
+                                     LazyEagerVerifyFnType verify_func) {
+  GOOGLE_CHECK_NE(type, WireFormatLite::TYPE_ENUM);
+  GOOGLE_CHECK_NE(type, WireFormatLite::TYPE_MESSAGE);
+  GOOGLE_CHECK_NE(type, WireFormatLite::TYPE_GROUP);
+  ExtensionInfo info(extendee, number, type, is_repeated, is_packed,
+                     verify_func);
+  Register(info);
+}
+
+static bool CallNoArgValidityFunc(const void* arg, int number) {
+  // Note:  Must use C-style cast here rather than reinterpret_cast because
+  //   the C++ standard at one point did not allow casts between function and
+  //   data pointers and some compilers enforce this for C++-style casts.  No
+  //   compiler enforces it for C-style casts since lots of C-style code has
+  //   relied on these kinds of casts for a long time, despite being
+  //   technically undefined.  See:
+  //     http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#195
+  // Also note:  Some compilers do not allow function pointers to be "const".
+  //   Which makes sense, I suppose, because it's meaningless.
+  return ((EnumValidityFunc*)arg)(number);
+}
+
+void ExtensionSet::RegisterEnumExtension(const MessageLite* extendee,
+                                         int number, FieldType type,
+                                         bool is_repeated, bool is_packed,
+                                         EnumValidityFunc* is_valid) {
+  GOOGLE_CHECK_EQ(type, WireFormatLite::TYPE_ENUM);
+  ExtensionInfo info(extendee, number, type, is_repeated, is_packed, nullptr);
+  info.enum_validity_check.func = CallNoArgValidityFunc;
+  // See comment in CallNoArgValidityFunc() about why we use a c-style cast.
+  info.enum_validity_check.arg = (void*)is_valid;
+  Register(info);
+}
+
+void ExtensionSet::RegisterMessageExtension(const MessageLite* extendee,
+                                            int number, FieldType type,
+                                            bool is_repeated, bool is_packed,
+                                            const MessageLite* prototype,
+                                            LazyEagerVerifyFnType verify_func) {
+  GOOGLE_CHECK(type == WireFormatLite::TYPE_MESSAGE ||
+        type == WireFormatLite::TYPE_GROUP);
+  ExtensionInfo info(extendee, number, type, is_repeated, is_packed,
+                     verify_func);
+  info.message_info = {prototype};
+  Register(info);
+}
+
+// ===================================================================
+// Constructors and basic methods.
+
+ExtensionSet::ExtensionSet(Arena* arena)
+    : arena_(arena),
+      flat_capacity_(0),
+      flat_size_(0),
+      map_{flat_capacity_ == 0
+               ? nullptr
+               : Arena::CreateArray<KeyValue>(arena_, flat_capacity_)} {}
+
+ExtensionSet::~ExtensionSet() {
+  // Deletes all allocated extensions.
+  if (arena_ == nullptr) {
+    ForEach([](int /* number */, Extension& ext) { ext.Free(); });
+    if (PROTOBUF_PREDICT_FALSE(is_large())) {
+      delete map_.large;
+    } else {
+      DeleteFlatMap(map_.flat, flat_capacity_);
+    }
+  }
+}
+
+void ExtensionSet::DeleteFlatMap(const ExtensionSet::KeyValue* flat,
+                                 uint16_t flat_capacity) {
+  // Arena::CreateArray already requires a trivially destructible type, but
+  // ensure this constraint is not violated in the future.
+  static_assert(std::is_trivially_destructible<KeyValue>::value,
+                "CreateArray requires a trivially destructible type");
+  // A const-cast is needed, but this is safe as we are about to deallocate the
+  // array.
+  internal::SizedArrayDelete(const_cast<KeyValue*>(flat),
+                             sizeof(*flat) * flat_capacity);
+}
+
+// Defined in extension_set_heavy.cc.
+// void ExtensionSet::AppendToList(const Descriptor* extendee,
+//                                 const DescriptorPool* pool,
+//                                 vector<const FieldDescriptor*>* output) const
+
+bool ExtensionSet::Has(int number) const {
+  const Extension* ext = FindOrNull(number);
+  if (ext == nullptr) return false;
+  GOOGLE_DCHECK(!ext->is_repeated);
+  return !ext->is_cleared;
+}
+
+bool ExtensionSet::HasLazy(int number) const {
+  return Has(number) && FindOrNull(number)->is_lazy;
+}
+
+int ExtensionSet::NumExtensions() const {
+  int result = 0;
+  ForEach([&result](int /* number */, const Extension& ext) {
+    if (!ext.is_cleared) {
+      ++result;
+    }
+  });
+  return result;
+}
+
+int ExtensionSet::ExtensionSize(int number) const {
+  const Extension* ext = FindOrNull(number);
+  return ext == nullptr ? 0 : ext->GetSize();
+}
+
+FieldType ExtensionSet::ExtensionType(int number) const {
+  const Extension* ext = FindOrNull(number);
+  if (ext == nullptr) {
+    GOOGLE_LOG(DFATAL) << "Don't lookup extension types if they aren't present (1). ";
+    return 0;
+  }
+  if (ext->is_cleared) {
+    GOOGLE_LOG(DFATAL) << "Don't lookup extension types if they aren't present (2). ";
+  }
+  return ext->type;
+}
+
+void ExtensionSet::ClearExtension(int number) {
+  Extension* ext = FindOrNull(number);
+  if (ext == nullptr) return;
+  ext->Clear();
+}
+
+// ===================================================================
+// Field accessors
+
+namespace {
+
+enum { REPEATED_FIELD, OPTIONAL_FIELD };
+
+}  // namespace
+
+#define GOOGLE_DCHECK_TYPE(EXTENSION, LABEL, CPPTYPE)                                 \
+  GOOGLE_DCHECK_EQ((EXTENSION).is_repeated ? REPEATED_FIELD : OPTIONAL_FIELD, LABEL); \
+  GOOGLE_DCHECK_EQ(cpp_type((EXTENSION).type), WireFormatLite::CPPTYPE_##CPPTYPE)
+
+// -------------------------------------------------------------------
+// Primitives
+
+#define PRIMITIVE_ACCESSORS(UPPERCASE, LOWERCASE, CAMELCASE)                  \
+                                                                              \
+  LOWERCASE ExtensionSet::Get##CAMELCASE(int number, LOWERCASE default_value) \
+      const {                                                                 \
+    const Extension* extension = FindOrNull(number);                          \
+    if (extension == nullptr || extension->is_cleared) {                      \
+      return default_value;                                                   \
+    } else {                                                                  \
+      GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, UPPERCASE);                     \
+      return extension->LOWERCASE##_value;                                    \
+    }                                                                         \
+  }                                                                           \
+                                                                              \
+  const LOWERCASE& ExtensionSet::GetRef##CAMELCASE(                           \
+      int number, const LOWERCASE& default_value) const {                     \
+    const Extension* extension = FindOrNull(number);                          \
+    if (extension == nullptr || extension->is_cleared) {                      \
+      return default_value;                                                   \
+    } else {                                                                  \
+      GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, UPPERCASE);                     \
+      return extension->LOWERCASE##_value;                                    \
+    }                                                                         \
+  }                                                                           \
+                                                                              \
+  void ExtensionSet::Set##CAMELCASE(int number, FieldType type,               \
+                                    LOWERCASE value,                          \
+                                    const FieldDescriptor* descriptor) {      \
+    Extension* extension;                                                     \
+    if (MaybeNewExtension(number, descriptor, &extension)) {                  \
+      extension->type = type;                                                 \
+      GOOGLE_DCHECK_EQ(cpp_type(extension->type),                                    \
+                WireFormatLite::CPPTYPE_##UPPERCASE);                         \
+      extension->is_repeated = false;                                         \
+    } else {                                                                  \
+      GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, UPPERCASE);                     \
+    }                                                                         \
+    extension->is_cleared = false;                                            \
+    extension->LOWERCASE##_value = value;                                     \
+  }                                                                           \
+                                                                              \
+  LOWERCASE ExtensionSet::GetRepeated##CAMELCASE(int number, int index)       \
+      const {                                                                 \
+    const Extension* extension = FindOrNull(number);                          \
+    GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";   \
+    GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, UPPERCASE);                       \
+    return extension->repeated_##LOWERCASE##_value->Get(index);               \
+  }                                                                           \
+                                                                              \
+  const LOWERCASE& ExtensionSet::GetRefRepeated##CAMELCASE(int number,        \
+                                                           int index) const { \
+    const Extension* extension = FindOrNull(number);                          \
+    GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";   \
+    GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, UPPERCASE);                       \
+    return extension->repeated_##LOWERCASE##_value->Get(index);               \
+  }                                                                           \
+                                                                              \
+  void ExtensionSet::SetRepeated##CAMELCASE(int number, int index,            \
+                                            LOWERCASE value) {                \
+    Extension* extension = FindOrNull(number);                                \
+    GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";   \
+    GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, UPPERCASE);                       \
+    extension->repeated_##LOWERCASE##_value->Set(index, value);               \
+  }                                                                           \
+                                                                              \
+  void ExtensionSet::Add##CAMELCASE(int number, FieldType type, bool packed,  \
+                                    LOWERCASE value,                          \
+                                    const FieldDescriptor* descriptor) {      \
+    Extension* extension;                                                     \
+    if (MaybeNewExtension(number, descriptor, &extension)) {                  \
+      extension->type = type;                                                 \
+      GOOGLE_DCHECK_EQ(cpp_type(extension->type),                                    \
+                WireFormatLite::CPPTYPE_##UPPERCASE);                         \
+      extension->is_repeated = true;                                          \
+      extension->is_packed = packed;                                          \
+      extension->repeated_##LOWERCASE##_value =                               \
+          Arena::CreateMessage<RepeatedField<LOWERCASE>>(arena_);             \
+    } else {                                                                  \
+      GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, UPPERCASE);                     \
+      GOOGLE_DCHECK_EQ(extension->is_packed, packed);                                \
+    }                                                                         \
+    extension->repeated_##LOWERCASE##_value->Add(value);                      \
+  }
+
+PRIMITIVE_ACCESSORS(INT32, int32_t, Int32)
+PRIMITIVE_ACCESSORS(INT64, int64_t, Int64)
+PRIMITIVE_ACCESSORS(UINT32, uint32_t, UInt32)
+PRIMITIVE_ACCESSORS(UINT64, uint64_t, UInt64)
+PRIMITIVE_ACCESSORS(FLOAT, float, Float)
+PRIMITIVE_ACCESSORS(DOUBLE, double, Double)
+PRIMITIVE_ACCESSORS(BOOL, bool, Bool)
+
+#undef PRIMITIVE_ACCESSORS
+
+const void* ExtensionSet::GetRawRepeatedField(int number,
+                                              const void* default_value) const {
+  const Extension* extension = FindOrNull(number);
+  if (extension == nullptr) {
+    return default_value;
+  }
+  // We assume that all the RepeatedField<>* pointers have the same
+  // size and alignment within the anonymous union in Extension.
+  return extension->repeated_int32_t_value;
+}
+
+void* ExtensionSet::MutableRawRepeatedField(int number, FieldType field_type,
+                                            bool packed,
+                                            const FieldDescriptor* desc) {
+  Extension* extension;
+
+  // We instantiate an empty Repeated{,Ptr}Field if one doesn't exist for this
+  // extension.
+  if (MaybeNewExtension(number, desc, &extension)) {
+    extension->is_repeated = true;
+    extension->type = field_type;
+    extension->is_packed = packed;
+
+    switch (WireFormatLite::FieldTypeToCppType(
+        static_cast<WireFormatLite::FieldType>(field_type))) {
+      case WireFormatLite::CPPTYPE_INT32:
+        extension->repeated_int32_t_value =
+            Arena::CreateMessage<RepeatedField<int32_t>>(arena_);
+        break;
+      case WireFormatLite::CPPTYPE_INT64:
+        extension->repeated_int64_t_value =
+            Arena::CreateMessage<RepeatedField<int64_t>>(arena_);
+        break;
+      case WireFormatLite::CPPTYPE_UINT32:
+        extension->repeated_uint32_t_value =
+            Arena::CreateMessage<RepeatedField<uint32_t>>(arena_);
+        break;
+      case WireFormatLite::CPPTYPE_UINT64:
+        extension->repeated_uint64_t_value =
+            Arena::CreateMessage<RepeatedField<uint64_t>>(arena_);
+        break;
+      case WireFormatLite::CPPTYPE_DOUBLE:
+        extension->repeated_double_value =
+            Arena::CreateMessage<RepeatedField<double>>(arena_);
+        break;
+      case WireFormatLite::CPPTYPE_FLOAT:
+        extension->repeated_float_value =
+            Arena::CreateMessage<RepeatedField<float>>(arena_);
+        break;
+      case WireFormatLite::CPPTYPE_BOOL:
+        extension->repeated_bool_value =
+            Arena::CreateMessage<RepeatedField<bool>>(arena_);
+        break;
+      case WireFormatLite::CPPTYPE_ENUM:
+        extension->repeated_enum_value =
+            Arena::CreateMessage<RepeatedField<int>>(arena_);
+        break;
+      case WireFormatLite::CPPTYPE_STRING:
+        extension->repeated_string_value =
+            Arena::CreateMessage<RepeatedPtrField<std::string>>(arena_);
+        break;
+      case WireFormatLite::CPPTYPE_MESSAGE:
+        extension->repeated_message_value =
+            Arena::CreateMessage<RepeatedPtrField<MessageLite>>(arena_);
+        break;
+    }
+  }
+
+  // We assume that all the RepeatedField<>* pointers have the same
+  // size and alignment within the anonymous union in Extension.
+  return extension->repeated_int32_t_value;
+}
+
+// Compatible version using old call signature. Does not create extensions when
+// the don't already exist; instead, just GOOGLE_CHECK-fails.
+void* ExtensionSet::MutableRawRepeatedField(int number) {
+  Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Extension not found.";
+  // We assume that all the RepeatedField<>* pointers have the same
+  // size and alignment within the anonymous union in Extension.
+  return extension->repeated_int32_t_value;
+}
+
+// -------------------------------------------------------------------
+// Enums
+
+int ExtensionSet::GetEnum(int number, int default_value) const {
+  const Extension* extension = FindOrNull(number);
+  if (extension == nullptr || extension->is_cleared) {
+    // Not present.  Return the default value.
+    return default_value;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, ENUM);
+    return extension->enum_value;
+  }
+}
+
+const int& ExtensionSet::GetRefEnum(int number,
+                                    const int& default_value) const {
+  const Extension* extension = FindOrNull(number);
+  if (extension == nullptr || extension->is_cleared) {
+    // Not present.  Return the default value.
+    return default_value;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, ENUM);
+    return extension->enum_value;
+  }
+}
+
+void ExtensionSet::SetEnum(int number, FieldType type, int value,
+                           const FieldDescriptor* descriptor) {
+  Extension* extension;
+  if (MaybeNewExtension(number, descriptor, &extension)) {
+    extension->type = type;
+    GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_ENUM);
+    extension->is_repeated = false;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, ENUM);
+  }
+  extension->is_cleared = false;
+  extension->enum_value = value;
+}
+
+int ExtensionSet::GetRepeatedEnum(int number, int index) const {
+  const Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, ENUM);
+  return extension->repeated_enum_value->Get(index);
+}
+
+const int& ExtensionSet::GetRefRepeatedEnum(int number, int index) const {
+  const Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, ENUM);
+  return extension->repeated_enum_value->Get(index);
+}
+
+void ExtensionSet::SetRepeatedEnum(int number, int index, int value) {
+  Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, ENUM);
+  extension->repeated_enum_value->Set(index, value);
+}
+
+void ExtensionSet::AddEnum(int number, FieldType type, bool packed, int value,
+                           const FieldDescriptor* descriptor) {
+  Extension* extension;
+  if (MaybeNewExtension(number, descriptor, &extension)) {
+    extension->type = type;
+    GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_ENUM);
+    extension->is_repeated = true;
+    extension->is_packed = packed;
+    extension->repeated_enum_value =
+        Arena::CreateMessage<RepeatedField<int>>(arena_);
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, ENUM);
+    GOOGLE_DCHECK_EQ(extension->is_packed, packed);
+  }
+  extension->repeated_enum_value->Add(value);
+}
+
+// -------------------------------------------------------------------
+// Strings
+
+const std::string& ExtensionSet::GetString(
+    int number, const std::string& default_value) const {
+  const Extension* extension = FindOrNull(number);
+  if (extension == nullptr || extension->is_cleared) {
+    // Not present.  Return the default value.
+    return default_value;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, STRING);
+    return *extension->string_value;
+  }
+}
+
+std::string* ExtensionSet::MutableString(int number, FieldType type,
+                                         const FieldDescriptor* descriptor) {
+  Extension* extension;
+  if (MaybeNewExtension(number, descriptor, &extension)) {
+    extension->type = type;
+    GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_STRING);
+    extension->is_repeated = false;
+    extension->string_value = Arena::Create<std::string>(arena_);
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, STRING);
+  }
+  extension->is_cleared = false;
+  return extension->string_value;
+}
+
+const std::string& ExtensionSet::GetRepeatedString(int number,
+                                                   int index) const {
+  const Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, STRING);
+  return extension->repeated_string_value->Get(index);
+}
+
+std::string* ExtensionSet::MutableRepeatedString(int number, int index) {
+  Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, STRING);
+  return extension->repeated_string_value->Mutable(index);
+}
+
+std::string* ExtensionSet::AddString(int number, FieldType type,
+                                     const FieldDescriptor* descriptor) {
+  Extension* extension;
+  if (MaybeNewExtension(number, descriptor, &extension)) {
+    extension->type = type;
+    GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_STRING);
+    extension->is_repeated = true;
+    extension->is_packed = false;
+    extension->repeated_string_value =
+        Arena::CreateMessage<RepeatedPtrField<std::string>>(arena_);
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, STRING);
+  }
+  return extension->repeated_string_value->Add();
+}
+
+// -------------------------------------------------------------------
+// Messages
+
+const MessageLite& ExtensionSet::GetMessage(
+    int number, const MessageLite& default_value) const {
+  const Extension* extension = FindOrNull(number);
+  if (extension == nullptr) {
+    // Not present.  Return the default value.
+    return default_value;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, MESSAGE);
+    if (extension->is_lazy) {
+      return extension->lazymessage_value->GetMessage(default_value, arena_);
+    } else {
+      return *extension->message_value;
+    }
+  }
+}
+
+// Defined in extension_set_heavy.cc.
+// const MessageLite& ExtensionSet::GetMessage(int number,
+//                                             const Descriptor* message_type,
+//                                             MessageFactory* factory) const
+
+MessageLite* ExtensionSet::MutableMessage(int number, FieldType type,
+                                          const MessageLite& prototype,
+                                          const FieldDescriptor* descriptor) {
+  Extension* extension;
+  if (MaybeNewExtension(number, descriptor, &extension)) {
+    extension->type = type;
+    GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE);
+    extension->is_repeated = false;
+    extension->is_lazy = false;
+    extension->message_value = prototype.New(arena_);
+    extension->is_cleared = false;
+    return extension->message_value;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, MESSAGE);
+    extension->is_cleared = false;
+    if (extension->is_lazy) {
+      return extension->lazymessage_value->MutableMessage(prototype, arena_);
+    } else {
+      return extension->message_value;
+    }
+  }
+}
+
+// Defined in extension_set_heavy.cc.
+// MessageLite* ExtensionSet::MutableMessage(int number, FieldType type,
+//                                           const Descriptor* message_type,
+//                                           MessageFactory* factory)
+
+void ExtensionSet::SetAllocatedMessage(int number, FieldType type,
+                                       const FieldDescriptor* descriptor,
+                                       MessageLite* message) {
+  if (message == nullptr) {
+    ClearExtension(number);
+    return;
+  }
+  GOOGLE_DCHECK(message->GetOwningArena() == nullptr ||
+         message->GetOwningArena() == arena_);
+  Arena* message_arena = message->GetOwningArena();
+  Extension* extension;
+  if (MaybeNewExtension(number, descriptor, &extension)) {
+    extension->type = type;
+    GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE);
+    extension->is_repeated = false;
+    extension->is_lazy = false;
+    if (message_arena == arena_) {
+      extension->message_value = message;
+    } else if (message_arena == nullptr) {
+      extension->message_value = message;
+      arena_->Own(message);  // not nullptr because not equal to message_arena
+    } else {
+      extension->message_value = message->New(arena_);
+      extension->message_value->CheckTypeAndMergeFrom(*message);
+    }
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, MESSAGE);
+    if (extension->is_lazy) {
+      extension->lazymessage_value->SetAllocatedMessage(message, arena_);
+    } else {
+      if (arena_ == nullptr) {
+        delete extension->message_value;
+      }
+      if (message_arena == arena_) {
+        extension->message_value = message;
+      } else if (message_arena == nullptr) {
+        extension->message_value = message;
+        arena_->Own(message);  // not nullptr because not equal to message_arena
+      } else {
+        extension->message_value = message->New(arena_);
+        extension->message_value->CheckTypeAndMergeFrom(*message);
+      }
+    }
+  }
+  extension->is_cleared = false;
+}
+
+void ExtensionSet::UnsafeArenaSetAllocatedMessage(
+    int number, FieldType type, const FieldDescriptor* descriptor,
+    MessageLite* message) {
+  if (message == nullptr) {
+    ClearExtension(number);
+    return;
+  }
+  Extension* extension;
+  if (MaybeNewExtension(number, descriptor, &extension)) {
+    extension->type = type;
+    GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE);
+    extension->is_repeated = false;
+    extension->is_lazy = false;
+    extension->message_value = message;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, MESSAGE);
+    if (extension->is_lazy) {
+      extension->lazymessage_value->UnsafeArenaSetAllocatedMessage(message,
+                                                                   arena_);
+    } else {
+      if (arena_ == nullptr) {
+        delete extension->message_value;
+      }
+      extension->message_value = message;
+    }
+  }
+  extension->is_cleared = false;
+}
+
+MessageLite* ExtensionSet::ReleaseMessage(int number,
+                                          const MessageLite& prototype) {
+  Extension* extension = FindOrNull(number);
+  if (extension == nullptr) {
+    // Not present.  Return nullptr.
+    return nullptr;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, MESSAGE);
+    MessageLite* ret = nullptr;
+    if (extension->is_lazy) {
+      ret = extension->lazymessage_value->ReleaseMessage(prototype, arena_);
+      if (arena_ == nullptr) {
+        delete extension->lazymessage_value;
+      }
+    } else {
+      if (arena_ == nullptr) {
+        ret = extension->message_value;
+      } else {
+        // ReleaseMessage() always returns a heap-allocated message, and we are
+        // on an arena, so we need to make a copy of this message to return.
+        ret = extension->message_value->New();
+        ret->CheckTypeAndMergeFrom(*extension->message_value);
+      }
+    }
+    Erase(number);
+    return ret;
+  }
+}
+
+MessageLite* ExtensionSet::UnsafeArenaReleaseMessage(
+    int number, const MessageLite& prototype) {
+  Extension* extension = FindOrNull(number);
+  if (extension == nullptr) {
+    // Not present.  Return nullptr.
+    return nullptr;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, MESSAGE);
+    MessageLite* ret = nullptr;
+    if (extension->is_lazy) {
+      ret = extension->lazymessage_value->UnsafeArenaReleaseMessage(prototype,
+                                                                    arena_);
+      if (arena_ == nullptr) {
+        delete extension->lazymessage_value;
+      }
+    } else {
+      ret = extension->message_value;
+    }
+    Erase(number);
+    return ret;
+  }
+}
+
+// Defined in extension_set_heavy.cc.
+// MessageLite* ExtensionSet::ReleaseMessage(const FieldDescriptor* descriptor,
+//                                           MessageFactory* factory);
+
+const MessageLite& ExtensionSet::GetRepeatedMessage(int number,
+                                                    int index) const {
+  const Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, MESSAGE);
+  return extension->repeated_message_value->Get(index);
+}
+
+MessageLite* ExtensionSet::MutableRepeatedMessage(int number, int index) {
+  Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, MESSAGE);
+  return extension->repeated_message_value->Mutable(index);
+}
+
+MessageLite* ExtensionSet::AddMessage(int number, FieldType type,
+                                      const MessageLite& prototype,
+                                      const FieldDescriptor* descriptor) {
+  Extension* extension;
+  if (MaybeNewExtension(number, descriptor, &extension)) {
+    extension->type = type;
+    GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE);
+    extension->is_repeated = true;
+    extension->repeated_message_value =
+        Arena::CreateMessage<RepeatedPtrField<MessageLite>>(arena_);
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, REPEATED_FIELD, MESSAGE);
+  }
+
+  // RepeatedPtrField<MessageLite> does not know how to Add() since it cannot
+  // allocate an abstract object, so we have to be tricky.
+  MessageLite* result = reinterpret_cast<internal::RepeatedPtrFieldBase*>(
+                            extension->repeated_message_value)
+                            ->AddFromCleared<GenericTypeHandler<MessageLite>>();
+  if (result == nullptr) {
+    result = prototype.New(arena_);
+    extension->repeated_message_value->AddAllocated(result);
+  }
+  return result;
+}
+
+// Defined in extension_set_heavy.cc.
+// MessageLite* ExtensionSet::AddMessage(int number, FieldType type,
+//                                       const Descriptor* message_type,
+//                                       MessageFactory* factory)
+
+#undef GOOGLE_DCHECK_TYPE
+
+void ExtensionSet::RemoveLast(int number) {
+  Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK(extension->is_repeated);
+
+  switch (cpp_type(extension->type)) {
+    case WireFormatLite::CPPTYPE_INT32:
+      extension->repeated_int32_t_value->RemoveLast();
+      break;
+    case WireFormatLite::CPPTYPE_INT64:
+      extension->repeated_int64_t_value->RemoveLast();
+      break;
+    case WireFormatLite::CPPTYPE_UINT32:
+      extension->repeated_uint32_t_value->RemoveLast();
+      break;
+    case WireFormatLite::CPPTYPE_UINT64:
+      extension->repeated_uint64_t_value->RemoveLast();
+      break;
+    case WireFormatLite::CPPTYPE_FLOAT:
+      extension->repeated_float_value->RemoveLast();
+      break;
+    case WireFormatLite::CPPTYPE_DOUBLE:
+      extension->repeated_double_value->RemoveLast();
+      break;
+    case WireFormatLite::CPPTYPE_BOOL:
+      extension->repeated_bool_value->RemoveLast();
+      break;
+    case WireFormatLite::CPPTYPE_ENUM:
+      extension->repeated_enum_value->RemoveLast();
+      break;
+    case WireFormatLite::CPPTYPE_STRING:
+      extension->repeated_string_value->RemoveLast();
+      break;
+    case WireFormatLite::CPPTYPE_MESSAGE:
+      extension->repeated_message_value->RemoveLast();
+      break;
+  }
+}
+
+MessageLite* ExtensionSet::ReleaseLast(int number) {
+  Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK(extension->is_repeated);
+  GOOGLE_DCHECK(cpp_type(extension->type) == WireFormatLite::CPPTYPE_MESSAGE);
+  return extension->repeated_message_value->ReleaseLast();
+}
+
+MessageLite* ExtensionSet::UnsafeArenaReleaseLast(int number) {
+  Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK(extension->is_repeated);
+  GOOGLE_DCHECK(cpp_type(extension->type) == WireFormatLite::CPPTYPE_MESSAGE);
+  return extension->repeated_message_value->UnsafeArenaReleaseLast();
+}
+
+void ExtensionSet::SwapElements(int number, int index1, int index2) {
+  Extension* extension = FindOrNull(number);
+  GOOGLE_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty).";
+  GOOGLE_DCHECK(extension->is_repeated);
+
+  switch (cpp_type(extension->type)) {
+    case WireFormatLite::CPPTYPE_INT32:
+      extension->repeated_int32_t_value->SwapElements(index1, index2);
+      break;
+    case WireFormatLite::CPPTYPE_INT64:
+      extension->repeated_int64_t_value->SwapElements(index1, index2);
+      break;
+    case WireFormatLite::CPPTYPE_UINT32:
+      extension->repeated_uint32_t_value->SwapElements(index1, index2);
+      break;
+    case WireFormatLite::CPPTYPE_UINT64:
+      extension->repeated_uint64_t_value->SwapElements(index1, index2);
+      break;
+    case WireFormatLite::CPPTYPE_FLOAT:
+      extension->repeated_float_value->SwapElements(index1, index2);
+      break;
+    case WireFormatLite::CPPTYPE_DOUBLE:
+      extension->repeated_double_value->SwapElements(index1, index2);
+      break;
+    case WireFormatLite::CPPTYPE_BOOL:
+      extension->repeated_bool_value->SwapElements(index1, index2);
+      break;
+    case WireFormatLite::CPPTYPE_ENUM:
+      extension->repeated_enum_value->SwapElements(index1, index2);
+      break;
+    case WireFormatLite::CPPTYPE_STRING:
+      extension->repeated_string_value->SwapElements(index1, index2);
+      break;
+    case WireFormatLite::CPPTYPE_MESSAGE:
+      extension->repeated_message_value->SwapElements(index1, index2);
+      break;
+  }
+}
+
+// ===================================================================
+
+void ExtensionSet::Clear() {
+  ForEach([](int /* number */, Extension& ext) { ext.Clear(); });
+}
+
+namespace {
+// Computes the size of an ExtensionSet union without actually constructing the
+// union. Note that we do not count cleared extensions from the source to be
+// part of the total, because there is no need to allocate space for those. We
+// do include cleared extensions in the destination, though, because those are
+// already allocated and will not be going away.
+template <typename ItX, typename ItY>
+size_t SizeOfUnion(ItX it_dest, ItX end_dest, ItY it_source, ItY end_source) {
+  size_t result = 0;
+  while (it_dest != end_dest && it_source != end_source) {
+    if (it_dest->first < it_source->first) {
+      ++result;
+      ++it_dest;
+    } else if (it_dest->first == it_source->first) {
+      ++result;
+      ++it_dest;
+      ++it_source;
+    } else {
+      if (!it_source->second.is_cleared) {
+        ++result;
+      }
+      ++it_source;
+    }
+  }
+  result += std::distance(it_dest, end_dest);
+  for (; it_source != end_source; ++it_source) {
+    if (!it_source->second.is_cleared) {
+      ++result;
+    }
+  }
+  return result;
+}
+}  // namespace
+
+void ExtensionSet::MergeFrom(const MessageLite* extendee,
+                             const ExtensionSet& other) {
+  if (PROTOBUF_PREDICT_TRUE(!is_large())) {
+    if (PROTOBUF_PREDICT_TRUE(!other.is_large())) {
+      GrowCapacity(SizeOfUnion(flat_begin(), flat_end(), other.flat_begin(),
+                               other.flat_end()));
+    } else {
+      GrowCapacity(SizeOfUnion(flat_begin(), flat_end(),
+                               other.map_.large->begin(),
+                               other.map_.large->end()));
+    }
+  }
+  other.ForEach([extendee, this, &other](int number, const Extension& ext) {
+    this->InternalExtensionMergeFrom(extendee, number, ext, other.arena_);
+  });
+}
+
+void ExtensionSet::InternalExtensionMergeFrom(const MessageLite* extendee,
+                                              int number,
+                                              const Extension& other_extension,
+                                              Arena* other_arena) {
+  if (other_extension.is_repeated) {
+    Extension* extension;
+    bool is_new =
+        MaybeNewExtension(number, other_extension.descriptor, &extension);
+    if (is_new) {
+      // Extension did not already exist in set.
+      extension->type = other_extension.type;
+      extension->is_packed = other_extension.is_packed;
+      extension->is_repeated = true;
+    } else {
+      GOOGLE_DCHECK_EQ(extension->type, other_extension.type);
+      GOOGLE_DCHECK_EQ(extension->is_packed, other_extension.is_packed);
+      GOOGLE_DCHECK(extension->is_repeated);
+    }
+
+    switch (cpp_type(other_extension.type)) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE, REPEATED_TYPE) \
+  case WireFormatLite::CPPTYPE_##UPPERCASE:              \
+    if (is_new) {                                        \
+      extension->repeated_##LOWERCASE##_value =          \
+          Arena::CreateMessage<REPEATED_TYPE>(arena_);   \
+    }                                                    \
+    extension->repeated_##LOWERCASE##_value->MergeFrom(  \
+        *other_extension.repeated_##LOWERCASE##_value);  \
+    break;
+
+      HANDLE_TYPE(INT32, int32_t, RepeatedField<int32_t>);
+      HANDLE_TYPE(INT64, int64_t, RepeatedField<int64_t>);
+      HANDLE_TYPE(UINT32, uint32_t, RepeatedField<uint32_t>);
+      HANDLE_TYPE(UINT64, uint64_t, RepeatedField<uint64_t>);
+      HANDLE_TYPE(FLOAT, float, RepeatedField<float>);
+      HANDLE_TYPE(DOUBLE, double, RepeatedField<double>);
+      HANDLE_TYPE(BOOL, bool, RepeatedField<bool>);
+      HANDLE_TYPE(ENUM, enum, RepeatedField<int>);
+      HANDLE_TYPE(STRING, string, RepeatedPtrField<std::string>);
+#undef HANDLE_TYPE
+
+      case WireFormatLite::CPPTYPE_MESSAGE:
+        if (is_new) {
+          extension->repeated_message_value =
+              Arena::CreateMessage<RepeatedPtrField<MessageLite>>(arena_);
+        }
+        // We can't call RepeatedPtrField<MessageLite>::MergeFrom() because
+        // it would attempt to allocate new objects.
+        RepeatedPtrField<MessageLite>* other_repeated_message =
+            other_extension.repeated_message_value;
+        for (int i = 0; i < other_repeated_message->size(); i++) {
+          const MessageLite& other_message = other_repeated_message->Get(i);
+          MessageLite* target =
+              reinterpret_cast<internal::RepeatedPtrFieldBase*>(
+                  extension->repeated_message_value)
+                  ->AddFromCleared<GenericTypeHandler<MessageLite>>();
+          if (target == nullptr) {
+            target = other_message.New(arena_);
+            extension->repeated_message_value->AddAllocated(target);
+          }
+          target->CheckTypeAndMergeFrom(other_message);
+        }
+        break;
+    }
+  } else {
+    if (!other_extension.is_cleared) {
+      switch (cpp_type(other_extension.type)) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE, CAMELCASE)  \
+  case WireFormatLite::CPPTYPE_##UPPERCASE:           \
+    Set##CAMELCASE(number, other_extension.type,      \
+                   other_extension.LOWERCASE##_value, \
+                   other_extension.descriptor);       \
+    break;
+
+        HANDLE_TYPE(INT32, int32_t, Int32);
+        HANDLE_TYPE(INT64, int64_t, Int64);
+        HANDLE_TYPE(UINT32, uint32_t, UInt32);
+        HANDLE_TYPE(UINT64, uint64_t, UInt64);
+        HANDLE_TYPE(FLOAT, float, Float);
+        HANDLE_TYPE(DOUBLE, double, Double);
+        HANDLE_TYPE(BOOL, bool, Bool);
+        HANDLE_TYPE(ENUM, enum, Enum);
+#undef HANDLE_TYPE
+        case WireFormatLite::CPPTYPE_STRING:
+          SetString(number, other_extension.type, *other_extension.string_value,
+                    other_extension.descriptor);
+          break;
+        case WireFormatLite::CPPTYPE_MESSAGE: {
+          Extension* extension;
+          bool is_new =
+              MaybeNewExtension(number, other_extension.descriptor, &extension);
+          if (is_new) {
+            extension->type = other_extension.type;
+            extension->is_packed = other_extension.is_packed;
+            extension->is_repeated = false;
+            if (other_extension.is_lazy) {
+              extension->is_lazy = true;
+              extension->lazymessage_value =
+                  other_extension.lazymessage_value->New(arena_);
+              extension->lazymessage_value->MergeFrom(
+                  GetPrototypeForLazyMessage(extendee, number),
+                  *other_extension.lazymessage_value, arena_);
+            } else {
+              extension->is_lazy = false;
+              extension->message_value =
+                  other_extension.message_value->New(arena_);
+              extension->message_value->CheckTypeAndMergeFrom(
+                  *other_extension.message_value);
+            }
+          } else {
+            GOOGLE_DCHECK_EQ(extension->type, other_extension.type);
+            GOOGLE_DCHECK_EQ(extension->is_packed, other_extension.is_packed);
+            GOOGLE_DCHECK(!extension->is_repeated);
+            if (other_extension.is_lazy) {
+              if (extension->is_lazy) {
+                extension->lazymessage_value->MergeFrom(
+                    GetPrototypeForLazyMessage(extendee, number),
+                    *other_extension.lazymessage_value, arena_);
+              } else {
+                extension->message_value->CheckTypeAndMergeFrom(
+                    other_extension.lazymessage_value->GetMessage(
+                        *extension->message_value, other_arena));
+              }
+            } else {
+              if (extension->is_lazy) {
+                extension->lazymessage_value
+                    ->MutableMessage(*other_extension.message_value, arena_)
+                    ->CheckTypeAndMergeFrom(*other_extension.message_value);
+              } else {
+                extension->message_value->CheckTypeAndMergeFrom(
+                    *other_extension.message_value);
+              }
+            }
+          }
+          extension->is_cleared = false;
+          break;
+        }
+      }
+    }
+  }
+}
+
+void ExtensionSet::Swap(const MessageLite* extendee, ExtensionSet* other) {
+#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+  if (GetArena() != nullptr && GetArena() == other->GetArena()) {
+#else   // PROTOBUF_FORCE_COPY_IN_SWAP
+  if (GetArena() == other->GetArena()) {
+#endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+    InternalSwap(other);
+  } else {
+    // TODO(cfallin, rohananil): We maybe able to optimize a case where we are
+    // swapping from heap to arena-allocated extension set, by just Own()'ing
+    // the extensions.
+    ExtensionSet extension_set;
+    extension_set.MergeFrom(extendee, *other);
+    other->Clear();
+    other->MergeFrom(extendee, *this);
+    Clear();
+    MergeFrom(extendee, extension_set);
+  }
+}
+
+void ExtensionSet::InternalSwap(ExtensionSet* other) {
+  using std::swap;
+  swap(arena_, other->arena_);
+  swap(flat_capacity_, other->flat_capacity_);
+  swap(flat_size_, other->flat_size_);
+  swap(map_, other->map_);
+}
+
+void ExtensionSet::SwapExtension(const MessageLite* extendee,
+                                 ExtensionSet* other, int number) {
+  if (this == other) return;
+
+  if (GetArena() == other->GetArena()) {
+    UnsafeShallowSwapExtension(other, number);
+    return;
+  }
+
+  Extension* this_ext = FindOrNull(number);
+  Extension* other_ext = other->FindOrNull(number);
+
+  if (this_ext == other_ext) return;
+
+  if (this_ext != nullptr && other_ext != nullptr) {
+    // TODO(cfallin, rohananil): We could further optimize these cases,
+    // especially avoid creation of ExtensionSet, and move MergeFrom logic
+    // into Extensions itself (which takes arena as an argument).
+    // We do it this way to reuse the copy-across-arenas logic already
+    // implemented in ExtensionSet's MergeFrom.
+    ExtensionSet temp;
+    temp.InternalExtensionMergeFrom(extendee, number, *other_ext,
+                                    other->GetArena());
+    Extension* temp_ext = temp.FindOrNull(number);
+
+    other_ext->Clear();
+    other->InternalExtensionMergeFrom(extendee, number, *this_ext,
+                                      this->GetArena());
+    this_ext->Clear();
+    InternalExtensionMergeFrom(extendee, number, *temp_ext, temp.GetArena());
+  } else if (this_ext == nullptr) {
+    InternalExtensionMergeFrom(extendee, number, *other_ext, other->GetArena());
+    if (other->GetArena() == nullptr) other_ext->Free();
+    other->Erase(number);
+  } else {
+    other->InternalExtensionMergeFrom(extendee, number, *this_ext,
+                                      this->GetArena());
+    if (GetArena() == nullptr) this_ext->Free();
+    Erase(number);
+  }
+}
+
+void ExtensionSet::UnsafeShallowSwapExtension(ExtensionSet* other, int number) {
+  if (this == other) return;
+
+  Extension* this_ext = FindOrNull(number);
+  Extension* other_ext = other->FindOrNull(number);
+
+  if (this_ext == other_ext) return;
+
+  GOOGLE_DCHECK_EQ(GetArena(), other->GetArena());
+
+  if (this_ext != nullptr && other_ext != nullptr) {
+    std::swap(*this_ext, *other_ext);
+  } else if (this_ext == nullptr) {
+    *Insert(number).first = *other_ext;
+    other->Erase(number);
+  } else {
+    *other->Insert(number).first = *this_ext;
+    Erase(number);
+  }
+}
+
+bool ExtensionSet::IsInitialized() const {
+  // Extensions are never required.  However, we need to check that all
+  // embedded messages are initialized.
+  if (PROTOBUF_PREDICT_FALSE(is_large())) {
+    for (const auto& kv : *map_.large) {
+      if (!kv.second.IsInitialized()) return false;
+    }
+    return true;
+  }
+  for (const KeyValue* it = flat_begin(); it != flat_end(); ++it) {
+    if (!it->second.IsInitialized()) return false;
+  }
+  return true;
+}
+
+const char* ExtensionSet::ParseField(uint64_t tag, const char* ptr,
+                                     const MessageLite* extendee,
+                                     internal::InternalMetadata* metadata,
+                                     internal::ParseContext* ctx) {
+  GeneratedExtensionFinder finder(extendee);
+  int number = tag >> 3;
+  bool was_packed_on_wire;
+  ExtensionInfo extension;
+  if (!FindExtensionInfoFromFieldNumber(tag & 7, number, &finder, &extension,
+                                        &was_packed_on_wire)) {
+    return UnknownFieldParse(
+        tag, metadata->mutable_unknown_fields<std::string>(), ptr, ctx);
+  }
+  return ParseFieldWithExtensionInfo<std::string>(
+      number, was_packed_on_wire, extension, metadata, ptr, ctx);
+}
+
+const char* ExtensionSet::ParseMessageSetItem(
+    const char* ptr, const MessageLite* extendee,
+    internal::InternalMetadata* metadata, internal::ParseContext* ctx) {
+  return ParseMessageSetItemTmpl<MessageLite, std::string>(ptr, extendee,
+                                                           metadata, ctx);
+}
+
+uint8_t* ExtensionSet::_InternalSerializeImpl(
+    const MessageLite* extendee, int start_field_number, int end_field_number,
+    uint8_t* target, io::EpsCopyOutputStream* stream) const {
+  if (PROTOBUF_PREDICT_FALSE(is_large())) {
+    const auto& end = map_.large->end();
+    for (auto it = map_.large->lower_bound(start_field_number);
+         it != end && it->first < end_field_number; ++it) {
+      target = it->second.InternalSerializeFieldWithCachedSizesToArray(
+          extendee, this, it->first, target, stream);
+    }
+    return target;
+  }
+  const KeyValue* end = flat_end();
+  for (const KeyValue* it = std::lower_bound(
+           flat_begin(), end, start_field_number, KeyValue::FirstComparator());
+       it != end && it->first < end_field_number; ++it) {
+    target = it->second.InternalSerializeFieldWithCachedSizesToArray(
+        extendee, this, it->first, target, stream);
+  }
+  return target;
+}
+
+uint8_t* ExtensionSet::InternalSerializeMessageSetWithCachedSizesToArray(
+    const MessageLite* extendee, uint8_t* target,
+    io::EpsCopyOutputStream* stream) const {
+  const ExtensionSet* extension_set = this;
+  ForEach([&target, extendee, stream, extension_set](int number,
+                                                     const Extension& ext) {
+    target = ext.InternalSerializeMessageSetItemWithCachedSizesToArray(
+        extendee, extension_set, number, target, stream);
+  });
+  return target;
+}
+
+size_t ExtensionSet::ByteSize() const {
+  size_t total_size = 0;
+  ForEach([&total_size](int number, const Extension& ext) {
+    total_size += ext.ByteSize(number);
+  });
+  return total_size;
+}
+
+// Defined in extension_set_heavy.cc.
+// int ExtensionSet::SpaceUsedExcludingSelf() const
+
+bool ExtensionSet::MaybeNewExtension(int number,
+                                     const FieldDescriptor* descriptor,
+                                     Extension** result) {
+  bool extension_is_new = false;
+  std::tie(*result, extension_is_new) = Insert(number);
+  (*result)->descriptor = descriptor;
+  return extension_is_new;
+}
+
+// ===================================================================
+// Methods of ExtensionSet::Extension
+
+void ExtensionSet::Extension::Clear() {
+  if (is_repeated) {
+    switch (cpp_type(type)) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE)   \
+  case WireFormatLite::CPPTYPE_##UPPERCASE: \
+    repeated_##LOWERCASE##_value->Clear();  \
+    break
+
+      HANDLE_TYPE(INT32, int32_t);
+      HANDLE_TYPE(INT64, int64_t);
+      HANDLE_TYPE(UINT32, uint32_t);
+      HANDLE_TYPE(UINT64, uint64_t);
+      HANDLE_TYPE(FLOAT, float);
+      HANDLE_TYPE(DOUBLE, double);
+      HANDLE_TYPE(BOOL, bool);
+      HANDLE_TYPE(ENUM, enum);
+      HANDLE_TYPE(STRING, string);
+      HANDLE_TYPE(MESSAGE, message);
+#undef HANDLE_TYPE
+    }
+  } else {
+    if (!is_cleared) {
+      switch (cpp_type(type)) {
+        case WireFormatLite::CPPTYPE_STRING:
+          string_value->clear();
+          break;
+        case WireFormatLite::CPPTYPE_MESSAGE:
+          if (is_lazy) {
+            lazymessage_value->Clear();
+          } else {
+            message_value->Clear();
+          }
+          break;
+        default:
+          // No need to do anything.  Get*() will return the default value
+          // as long as is_cleared is true and Set*() will overwrite the
+          // previous value.
+          break;
+      }
+
+      is_cleared = true;
+    }
+  }
+}
+
+size_t ExtensionSet::Extension::ByteSize(int number) const {
+  size_t result = 0;
+
+  if (is_repeated) {
+    if (is_packed) {
+      switch (real_type(type)) {
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE, LOWERCASE)                 \
+  case WireFormatLite::TYPE_##UPPERCASE:                             \
+    for (int i = 0; i < repeated_##LOWERCASE##_value->size(); i++) { \
+      result += WireFormatLite::CAMELCASE##Size(                     \
+          repeated_##LOWERCASE##_value->Get(i));                     \
+    }                                                                \
+    break
+
+        HANDLE_TYPE(INT32, Int32, int32_t);
+        HANDLE_TYPE(INT64, Int64, int64_t);
+        HANDLE_TYPE(UINT32, UInt32, uint32_t);
+        HANDLE_TYPE(UINT64, UInt64, uint64_t);
+        HANDLE_TYPE(SINT32, SInt32, int32_t);
+        HANDLE_TYPE(SINT64, SInt64, int64_t);
+        HANDLE_TYPE(ENUM, Enum, enum);
+#undef HANDLE_TYPE
+
+        // Stuff with fixed size.
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE, LOWERCASE)             \
+  case WireFormatLite::TYPE_##UPPERCASE:                         \
+    result += WireFormatLite::k##CAMELCASE##Size *               \
+              FromIntSize(repeated_##LOWERCASE##_value->size()); \
+    break
+        HANDLE_TYPE(FIXED32, Fixed32, uint32_t);
+        HANDLE_TYPE(FIXED64, Fixed64, uint64_t);
+        HANDLE_TYPE(SFIXED32, SFixed32, int32_t);
+        HANDLE_TYPE(SFIXED64, SFixed64, int64_t);
+        HANDLE_TYPE(FLOAT, Float, float);
+        HANDLE_TYPE(DOUBLE, Double, double);
+        HANDLE_TYPE(BOOL, Bool, bool);
+#undef HANDLE_TYPE
+
+        case WireFormatLite::TYPE_STRING:
+        case WireFormatLite::TYPE_BYTES:
+        case WireFormatLite::TYPE_GROUP:
+        case WireFormatLite::TYPE_MESSAGE:
+          GOOGLE_LOG(FATAL) << "Non-primitive types can't be packed.";
+          break;
+      }
+
+      cached_size = ToCachedSize(result);
+      if (result > 0) {
+        result += io::CodedOutputStream::VarintSize32(result);
+        result += io::CodedOutputStream::VarintSize32(WireFormatLite::MakeTag(
+            number, WireFormatLite::WIRETYPE_LENGTH_DELIMITED));
+      }
+    } else {
+      size_t tag_size = WireFormatLite::TagSize(number, real_type(type));
+
+      switch (real_type(type)) {
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE, LOWERCASE)                        \
+  case WireFormatLite::TYPE_##UPPERCASE:                                    \
+    result += tag_size * FromIntSize(repeated_##LOWERCASE##_value->size()); \
+    for (int i = 0; i < repeated_##LOWERCASE##_value->size(); i++) {        \
+      result += WireFormatLite::CAMELCASE##Size(                            \
+          repeated_##LOWERCASE##_value->Get(i));                            \
+    }                                                                       \
+    break
+
+        HANDLE_TYPE(INT32, Int32, int32_t);
+        HANDLE_TYPE(INT64, Int64, int64_t);
+        HANDLE_TYPE(UINT32, UInt32, uint32_t);
+        HANDLE_TYPE(UINT64, UInt64, uint64_t);
+        HANDLE_TYPE(SINT32, SInt32, int32_t);
+        HANDLE_TYPE(SINT64, SInt64, int64_t);
+        HANDLE_TYPE(STRING, String, string);
+        HANDLE_TYPE(BYTES, Bytes, string);
+        HANDLE_TYPE(ENUM, Enum, enum);
+        HANDLE_TYPE(GROUP, Group, message);
+        HANDLE_TYPE(MESSAGE, Message, message);
+#undef HANDLE_TYPE
+
+        // Stuff with fixed size.
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE, LOWERCASE)             \
+  case WireFormatLite::TYPE_##UPPERCASE:                         \
+    result += (tag_size + WireFormatLite::k##CAMELCASE##Size) *  \
+              FromIntSize(repeated_##LOWERCASE##_value->size()); \
+    break
+        HANDLE_TYPE(FIXED32, Fixed32, uint32_t);
+        HANDLE_TYPE(FIXED64, Fixed64, uint64_t);
+        HANDLE_TYPE(SFIXED32, SFixed32, int32_t);
+        HANDLE_TYPE(SFIXED64, SFixed64, int64_t);
+        HANDLE_TYPE(FLOAT, Float, float);
+        HANDLE_TYPE(DOUBLE, Double, double);
+        HANDLE_TYPE(BOOL, Bool, bool);
+#undef HANDLE_TYPE
+      }
+    }
+  } else if (!is_cleared) {
+    result += WireFormatLite::TagSize(number, real_type(type));
+    switch (real_type(type)) {
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE, LOWERCASE)      \
+  case WireFormatLite::TYPE_##UPPERCASE:                  \
+    result += WireFormatLite::CAMELCASE##Size(LOWERCASE); \
+    break
+
+      HANDLE_TYPE(INT32, Int32, int32_t_value);
+      HANDLE_TYPE(INT64, Int64, int64_t_value);
+      HANDLE_TYPE(UINT32, UInt32, uint32_t_value);
+      HANDLE_TYPE(UINT64, UInt64, uint64_t_value);
+      HANDLE_TYPE(SINT32, SInt32, int32_t_value);
+      HANDLE_TYPE(SINT64, SInt64, int64_t_value);
+      HANDLE_TYPE(STRING, String, *string_value);
+      HANDLE_TYPE(BYTES, Bytes, *string_value);
+      HANDLE_TYPE(ENUM, Enum, enum_value);
+      HANDLE_TYPE(GROUP, Group, *message_value);
+#undef HANDLE_TYPE
+      case WireFormatLite::TYPE_MESSAGE: {
+        if (is_lazy) {
+          size_t size = lazymessage_value->ByteSizeLong();
+          result += io::CodedOutputStream::VarintSize32(size) + size;
+        } else {
+          result += WireFormatLite::MessageSize(*message_value);
+        }
+        break;
+      }
+
+      // Stuff with fixed size.
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE)         \
+  case WireFormatLite::TYPE_##UPPERCASE:          \
+    result += WireFormatLite::k##CAMELCASE##Size; \
+    break
+        HANDLE_TYPE(FIXED32, Fixed32);
+        HANDLE_TYPE(FIXED64, Fixed64);
+        HANDLE_TYPE(SFIXED32, SFixed32);
+        HANDLE_TYPE(SFIXED64, SFixed64);
+        HANDLE_TYPE(FLOAT, Float);
+        HANDLE_TYPE(DOUBLE, Double);
+        HANDLE_TYPE(BOOL, Bool);
+#undef HANDLE_TYPE
+    }
+  }
+
+  return result;
+}
+
+int ExtensionSet::Extension::GetSize() const {
+  GOOGLE_DCHECK(is_repeated);
+  switch (cpp_type(type)) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE)   \
+  case WireFormatLite::CPPTYPE_##UPPERCASE: \
+    return repeated_##LOWERCASE##_value->size()
+
+    HANDLE_TYPE(INT32, int32_t);
+    HANDLE_TYPE(INT64, int64_t);
+    HANDLE_TYPE(UINT32, uint32_t);
+    HANDLE_TYPE(UINT64, uint64_t);
+    HANDLE_TYPE(FLOAT, float);
+    HANDLE_TYPE(DOUBLE, double);
+    HANDLE_TYPE(BOOL, bool);
+    HANDLE_TYPE(ENUM, enum);
+    HANDLE_TYPE(STRING, string);
+    HANDLE_TYPE(MESSAGE, message);
+#undef HANDLE_TYPE
+  }
+
+  GOOGLE_LOG(FATAL) << "Can't get here.";
+  return 0;
+}
+
+// This function deletes all allocated objects. This function should be only
+// called if the Extension was created without an arena.
+void ExtensionSet::Extension::Free() {
+  if (is_repeated) {
+    switch (cpp_type(type)) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE)   \
+  case WireFormatLite::CPPTYPE_##UPPERCASE: \
+    delete repeated_##LOWERCASE##_value;    \
+    break
+
+      HANDLE_TYPE(INT32, int32_t);
+      HANDLE_TYPE(INT64, int64_t);
+      HANDLE_TYPE(UINT32, uint32_t);
+      HANDLE_TYPE(UINT64, uint64_t);
+      HANDLE_TYPE(FLOAT, float);
+      HANDLE_TYPE(DOUBLE, double);
+      HANDLE_TYPE(BOOL, bool);
+      HANDLE_TYPE(ENUM, enum);
+      HANDLE_TYPE(STRING, string);
+      HANDLE_TYPE(MESSAGE, message);
+#undef HANDLE_TYPE
+    }
+  } else {
+    switch (cpp_type(type)) {
+      case WireFormatLite::CPPTYPE_STRING:
+        delete string_value;
+        break;
+      case WireFormatLite::CPPTYPE_MESSAGE:
+        if (is_lazy) {
+          delete lazymessage_value;
+        } else {
+          delete message_value;
+        }
+        break;
+      default:
+        break;
+    }
+  }
+}
+
+// Defined in extension_set_heavy.cc.
+// int ExtensionSet::Extension::SpaceUsedExcludingSelf() const
+
+bool ExtensionSet::Extension::IsInitialized() const {
+  if (cpp_type(type) == WireFormatLite::CPPTYPE_MESSAGE) {
+    if (is_repeated) {
+      for (int i = 0; i < repeated_message_value->size(); i++) {
+        if (!repeated_message_value->Get(i).IsInitialized()) {
+          return false;
+        }
+      }
+    } else {
+      if (!is_cleared) {
+        if (is_lazy) {
+          if (!lazymessage_value->IsInitialized()) return false;
+        } else {
+          if (!message_value->IsInitialized()) return false;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+// Dummy key method to avoid weak vtable.
+void ExtensionSet::LazyMessageExtension::UnusedKeyMethod() {}
+
+const ExtensionSet::Extension* ExtensionSet::FindOrNull(int key) const {
+  if (flat_size_ == 0) {
+    return nullptr;
+  } else if (PROTOBUF_PREDICT_TRUE(!is_large())) {
+    auto it = std::lower_bound(flat_begin(), flat_end() - 1, key,
+                               KeyValue::FirstComparator());
+    return it->first == key ? &it->second : nullptr;
+  } else {
+    return FindOrNullInLargeMap(key);
+  }
+}
+
+const ExtensionSet::Extension* ExtensionSet::FindOrNullInLargeMap(
+    int key) const {
+  assert(is_large());
+  LargeMap::const_iterator it = map_.large->find(key);
+  if (it != map_.large->end()) {
+    return &it->second;
+  }
+  return nullptr;
+}
+
+ExtensionSet::Extension* ExtensionSet::FindOrNull(int key) {
+  const auto* const_this = this;
+  return const_cast<ExtensionSet::Extension*>(const_this->FindOrNull(key));
+}
+
+ExtensionSet::Extension* ExtensionSet::FindOrNullInLargeMap(int key) {
+  const auto* const_this = this;
+  return const_cast<ExtensionSet::Extension*>(
+      const_this->FindOrNullInLargeMap(key));
+}
+
+std::pair<ExtensionSet::Extension*, bool> ExtensionSet::Insert(int key) {
+  if (PROTOBUF_PREDICT_FALSE(is_large())) {
+    auto maybe = map_.large->insert({key, Extension()});
+    return {&maybe.first->second, maybe.second};
+  }
+  KeyValue* end = flat_end();
+  KeyValue* it =
+      std::lower_bound(flat_begin(), end, key, KeyValue::FirstComparator());
+  if (it != end && it->first == key) {
+    return {&it->second, false};
+  }
+  if (flat_size_ < flat_capacity_) {
+    std::copy_backward(it, end, end + 1);
+    ++flat_size_;
+    it->first = key;
+    it->second = Extension();
+    return {&it->second, true};
+  }
+  GrowCapacity(flat_size_ + 1);
+  return Insert(key);
+}
+
+void ExtensionSet::GrowCapacity(size_t minimum_new_capacity) {
+  if (PROTOBUF_PREDICT_FALSE(is_large())) {
+    return;  // LargeMap does not have a "reserve" method.
+  }
+  if (flat_capacity_ >= minimum_new_capacity) {
+    return;
+  }
+
+  auto new_flat_capacity = flat_capacity_;
+  do {
+    new_flat_capacity = new_flat_capacity == 0 ? 1 : new_flat_capacity * 4;
+  } while (new_flat_capacity < minimum_new_capacity);
+
+  const KeyValue* begin = flat_begin();
+  const KeyValue* end = flat_end();
+  AllocatedData new_map;
+  if (new_flat_capacity > kMaximumFlatCapacity) {
+    new_map.large = Arena::Create<LargeMap>(arena_);
+    LargeMap::iterator hint = new_map.large->begin();
+    for (const KeyValue* it = begin; it != end; ++it) {
+      hint = new_map.large->insert(hint, {it->first, it->second});
+    }
+    flat_size_ = static_cast<uint16_t>(-1);
+    GOOGLE_DCHECK(is_large());
+  } else {
+    new_map.flat = Arena::CreateArray<KeyValue>(arena_, new_flat_capacity);
+    std::copy(begin, end, new_map.flat);
+  }
+
+  if (arena_ == nullptr) {
+    DeleteFlatMap(begin, flat_capacity_);
+  }
+  flat_capacity_ = new_flat_capacity;
+  map_ = new_map;
+}
+
+#if (__cplusplus < 201703) && \
+    (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+// static
+constexpr uint16_t ExtensionSet::kMaximumFlatCapacity;
+#endif  //  (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900
+        //  && _MSC_VER < 1912))
+
+void ExtensionSet::Erase(int key) {
+  if (PROTOBUF_PREDICT_FALSE(is_large())) {
+    map_.large->erase(key);
+    return;
+  }
+  KeyValue* end = flat_end();
+  KeyValue* it =
+      std::lower_bound(flat_begin(), end, key, KeyValue::FirstComparator());
+  if (it != end && it->first == key) {
+    std::copy(it + 1, end, it);
+    --flat_size_;
+  }
+}
+
+// ==================================================================
+// Default repeated field instances for iterator-compatible accessors
+
+const RepeatedPrimitiveDefaults* RepeatedPrimitiveDefaults::default_instance() {
+  static auto instance = OnShutdownDelete(new RepeatedPrimitiveDefaults);
+  return instance;
+}
+
+const RepeatedStringTypeTraits::RepeatedFieldType*
+RepeatedStringTypeTraits::GetDefaultRepeatedField() {
+  static auto instance = OnShutdownDelete(new RepeatedFieldType);
+  return instance;
+}
+
+uint8_t* ExtensionSet::Extension::InternalSerializeFieldWithCachedSizesToArray(
+    const MessageLite* extendee, const ExtensionSet* extension_set, int number,
+    uint8_t* target, io::EpsCopyOutputStream* stream) const {
+  if (is_repeated) {
+    if (is_packed) {
+      if (cached_size == 0) return target;
+
+      target = stream->EnsureSpace(target);
+      target = WireFormatLite::WriteTagToArray(
+          number, WireFormatLite::WIRETYPE_LENGTH_DELIMITED, target);
+      target = WireFormatLite::WriteInt32NoTagToArray(cached_size, target);
+
+      switch (real_type(type)) {
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE, LOWERCASE)                 \
+  case WireFormatLite::TYPE_##UPPERCASE:                             \
+    for (int i = 0; i < repeated_##LOWERCASE##_value->size(); i++) { \
+      target = stream->EnsureSpace(target);                          \
+      target = WireFormatLite::Write##CAMELCASE##NoTagToArray(       \
+          repeated_##LOWERCASE##_value->Get(i), target);             \
+    }                                                                \
+    break
+
+        HANDLE_TYPE(INT32, Int32, int32_t);
+        HANDLE_TYPE(INT64, Int64, int64_t);
+        HANDLE_TYPE(UINT32, UInt32, uint32_t);
+        HANDLE_TYPE(UINT64, UInt64, uint64_t);
+        HANDLE_TYPE(SINT32, SInt32, int32_t);
+        HANDLE_TYPE(SINT64, SInt64, int64_t);
+        HANDLE_TYPE(FIXED32, Fixed32, uint32_t);
+        HANDLE_TYPE(FIXED64, Fixed64, uint64_t);
+        HANDLE_TYPE(SFIXED32, SFixed32, int32_t);
+        HANDLE_TYPE(SFIXED64, SFixed64, int64_t);
+        HANDLE_TYPE(FLOAT, Float, float);
+        HANDLE_TYPE(DOUBLE, Double, double);
+        HANDLE_TYPE(BOOL, Bool, bool);
+        HANDLE_TYPE(ENUM, Enum, enum);
+#undef HANDLE_TYPE
+
+        case WireFormatLite::TYPE_STRING:
+        case WireFormatLite::TYPE_BYTES:
+        case WireFormatLite::TYPE_GROUP:
+        case WireFormatLite::TYPE_MESSAGE:
+          GOOGLE_LOG(FATAL) << "Non-primitive types can't be packed.";
+          break;
+      }
+    } else {
+      switch (real_type(type)) {
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE, LOWERCASE)                 \
+  case WireFormatLite::TYPE_##UPPERCASE:                             \
+    for (int i = 0; i < repeated_##LOWERCASE##_value->size(); i++) { \
+      target = stream->EnsureSpace(target);                          \
+      target = WireFormatLite::Write##CAMELCASE##ToArray(            \
+          number, repeated_##LOWERCASE##_value->Get(i), target);     \
+    }                                                                \
+    break
+
+        HANDLE_TYPE(INT32, Int32, int32_t);
+        HANDLE_TYPE(INT64, Int64, int64_t);
+        HANDLE_TYPE(UINT32, UInt32, uint32_t);
+        HANDLE_TYPE(UINT64, UInt64, uint64_t);
+        HANDLE_TYPE(SINT32, SInt32, int32_t);
+        HANDLE_TYPE(SINT64, SInt64, int64_t);
+        HANDLE_TYPE(FIXED32, Fixed32, uint32_t);
+        HANDLE_TYPE(FIXED64, Fixed64, uint64_t);
+        HANDLE_TYPE(SFIXED32, SFixed32, int32_t);
+        HANDLE_TYPE(SFIXED64, SFixed64, int64_t);
+        HANDLE_TYPE(FLOAT, Float, float);
+        HANDLE_TYPE(DOUBLE, Double, double);
+        HANDLE_TYPE(BOOL, Bool, bool);
+        HANDLE_TYPE(ENUM, Enum, enum);
+#undef HANDLE_TYPE
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE, LOWERCASE)                 \
+  case WireFormatLite::TYPE_##UPPERCASE:                             \
+    for (int i = 0; i < repeated_##LOWERCASE##_value->size(); i++) { \
+      target = stream->EnsureSpace(target);                          \
+      target = stream->WriteString(                                  \
+          number, repeated_##LOWERCASE##_value->Get(i), target);     \
+    }                                                                \
+    break
+        HANDLE_TYPE(STRING, String, string);
+        HANDLE_TYPE(BYTES, Bytes, string);
+#undef HANDLE_TYPE
+        case WireFormatLite::TYPE_GROUP:
+          for (int i = 0; i < repeated_message_value->size(); i++) {
+            target = stream->EnsureSpace(target);
+            target = WireFormatLite::InternalWriteGroup(
+                number, repeated_message_value->Get(i), target, stream);
+          }
+          break;
+        case WireFormatLite::TYPE_MESSAGE:
+          for (int i = 0; i < repeated_message_value->size(); i++) {
+            auto& msg = repeated_message_value->Get(i);
+            target = WireFormatLite::InternalWriteMessage(
+                number, msg, msg.GetCachedSize(), target, stream);
+          }
+          break;
+      }
+    }
+  } else if (!is_cleared) {
+    switch (real_type(type)) {
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE, VALUE)                               \
+  case WireFormatLite::TYPE_##UPPERCASE:                                       \
+    target = stream->EnsureSpace(target);                                      \
+    target = WireFormatLite::Write##CAMELCASE##ToArray(number, VALUE, target); \
+    break
+
+      HANDLE_TYPE(INT32, Int32, int32_t_value);
+      HANDLE_TYPE(INT64, Int64, int64_t_value);
+      HANDLE_TYPE(UINT32, UInt32, uint32_t_value);
+      HANDLE_TYPE(UINT64, UInt64, uint64_t_value);
+      HANDLE_TYPE(SINT32, SInt32, int32_t_value);
+      HANDLE_TYPE(SINT64, SInt64, int64_t_value);
+      HANDLE_TYPE(FIXED32, Fixed32, uint32_t_value);
+      HANDLE_TYPE(FIXED64, Fixed64, uint64_t_value);
+      HANDLE_TYPE(SFIXED32, SFixed32, int32_t_value);
+      HANDLE_TYPE(SFIXED64, SFixed64, int64_t_value);
+      HANDLE_TYPE(FLOAT, Float, float_value);
+      HANDLE_TYPE(DOUBLE, Double, double_value);
+      HANDLE_TYPE(BOOL, Bool, bool_value);
+      HANDLE_TYPE(ENUM, Enum, enum_value);
+#undef HANDLE_TYPE
+#define HANDLE_TYPE(UPPERCASE, CAMELCASE, VALUE)         \
+  case WireFormatLite::TYPE_##UPPERCASE:                 \
+    target = stream->EnsureSpace(target);                \
+    target = stream->WriteString(number, VALUE, target); \
+    break
+      HANDLE_TYPE(STRING, String, *string_value);
+      HANDLE_TYPE(BYTES, Bytes, *string_value);
+#undef HANDLE_TYPE
+      case WireFormatLite::TYPE_GROUP:
+        target = stream->EnsureSpace(target);
+        target = WireFormatLite::InternalWriteGroup(number, *message_value,
+                                                    target, stream);
+        break;
+      case WireFormatLite::TYPE_MESSAGE:
+        if (is_lazy) {
+          const auto* prototype =
+              extension_set->GetPrototypeForLazyMessage(extendee, number);
+          target = lazymessage_value->WriteMessageToArray(prototype, number,
+                                                          target, stream);
+        } else {
+          target = WireFormatLite::InternalWriteMessage(
+              number, *message_value, message_value->GetCachedSize(), target,
+              stream);
+        }
+        break;
+    }
+  }
+  return target;
+}
+
+const MessageLite* ExtensionSet::GetPrototypeForLazyMessage(
+    const MessageLite* extendee, int number) const {
+  GeneratedExtensionFinder finder(extendee);
+  bool was_packed_on_wire = false;
+  ExtensionInfo extension_info;
+  if (!FindExtensionInfoFromFieldNumber(
+          WireFormatLite::WireType::WIRETYPE_LENGTH_DELIMITED, number, &finder,
+          &extension_info, &was_packed_on_wire)) {
+    return nullptr;
+  }
+  return extension_info.message_info.prototype;
+}
+
+uint8_t*
+ExtensionSet::Extension::InternalSerializeMessageSetItemWithCachedSizesToArray(
+    const MessageLite* extendee, const ExtensionSet* extension_set, int number,
+    uint8_t* target, io::EpsCopyOutputStream* stream) const {
+  if (type != WireFormatLite::TYPE_MESSAGE || is_repeated) {
+    // Not a valid MessageSet extension, but serialize it the normal way.
+    GOOGLE_LOG(WARNING) << "Invalid message set extension.";
+    return InternalSerializeFieldWithCachedSizesToArray(extendee, extension_set,
+                                                        number, target, stream);
+  }
+
+  if (is_cleared) return target;
+
+  target = stream->EnsureSpace(target);
+  // Start group.
+  target = io::CodedOutputStream::WriteTagToArray(
+      WireFormatLite::kMessageSetItemStartTag, target);
+  // Write type ID.
+  target = WireFormatLite::WriteUInt32ToArray(
+      WireFormatLite::kMessageSetTypeIdNumber, number, target);
+  // Write message.
+  if (is_lazy) {
+    const auto* prototype =
+        extension_set->GetPrototypeForLazyMessage(extendee, number);
+    target = lazymessage_value->WriteMessageToArray(
+        prototype, WireFormatLite::kMessageSetMessageNumber, target, stream);
+  } else {
+    target = WireFormatLite::InternalWriteMessage(
+        WireFormatLite::kMessageSetMessageNumber, *message_value,
+        message_value->GetCachedSize(), target, stream);
+  }
+  // End group.
+  target = stream->EnsureSpace(target);
+  target = io::CodedOutputStream::WriteTagToArray(
+      WireFormatLite::kMessageSetItemEndTag, target);
+  return target;
+}
+
+size_t ExtensionSet::Extension::MessageSetItemByteSize(int number) const {
+  if (type != WireFormatLite::TYPE_MESSAGE || is_repeated) {
+    // Not a valid MessageSet extension, but compute the byte size for it the
+    // normal way.
+    return ByteSize(number);
+  }
+
+  if (is_cleared) return 0;
+
+  size_t our_size = WireFormatLite::kMessageSetItemTagsSize;
+
+  // type_id
+  our_size += io::CodedOutputStream::VarintSize32(number);
+
+  // message
+  size_t message_size = 0;
+  if (is_lazy) {
+    message_size = lazymessage_value->ByteSizeLong();
+  } else {
+    message_size = message_value->ByteSizeLong();
+  }
+
+  our_size += io::CodedOutputStream::VarintSize32(message_size);
+  our_size += message_size;
+
+  return our_size;
+}
+
+size_t ExtensionSet::MessageSetByteSize() const {
+  size_t total_size = 0;
+  ForEach([&total_size](int number, const Extension& ext) {
+    total_size += ext.MessageSetItemByteSize(number);
+  });
+  return total_size;
+}
+
+LazyEagerVerifyFnType FindExtensionLazyEagerVerifyFn(
+    const MessageLite* extendee, int number) {
+  const ExtensionInfo* registered = FindRegisteredExtension(extendee, number);
+  if (registered != nullptr) {
+    return registered->lazy_eager_verify_func;
+  }
+  return nullptr;
+}
+
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/extension_set_heavy.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/extension_set_heavy.cpp
new file mode 100644
index 0000000..a4bb948
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/extension_set_heavy.cpp
@@ -0,0 +1,440 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Contains methods defined in extension_set.h which cannot be part of the
+// lite library because they use descriptors or reflection.
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/extension_set_inl.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/wire_format_lite.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Implementation of ExtensionFinder which finds extensions in a given
+// DescriptorPool, using the given MessageFactory to construct sub-objects.
+// This class is implemented in extension_set_heavy.cc.
+class DescriptorPoolExtensionFinder {
+ public:
+  DescriptorPoolExtensionFinder(const DescriptorPool* pool,
+                                MessageFactory* factory,
+                                const Descriptor* containing_type)
+      : pool_(pool), factory_(factory), containing_type_(containing_type) {}
+
+  bool Find(int number, ExtensionInfo* output);
+
+ private:
+  const DescriptorPool* pool_;
+  MessageFactory* factory_;
+  const Descriptor* containing_type_;
+};
+
+void ExtensionSet::AppendToList(
+    const Descriptor* containing_type, const DescriptorPool* pool,
+    std::vector<const FieldDescriptor*>* output) const {
+  ForEach([containing_type, pool, &output](int number, const Extension& ext) {
+    bool has = false;
+    if (ext.is_repeated) {
+      has = ext.GetSize() > 0;
+    } else {
+      has = !ext.is_cleared;
+    }
+
+    if (has) {
+      // TODO(kenton): Looking up each field by number is somewhat unfortunate.
+      //   Is there a better way?  The problem is that descriptors are lazily-
+      //   initialized, so they might not even be constructed until
+      //   AppendToList() is called.
+
+      if (ext.descriptor == nullptr) {
+        output->push_back(pool->FindExtensionByNumber(containing_type, number));
+      } else {
+        output->push_back(ext.descriptor);
+      }
+    }
+  });
+}
+
+inline FieldDescriptor::Type real_type(FieldType type) {
+  GOOGLE_DCHECK(type > 0 && type <= FieldDescriptor::MAX_TYPE);
+  return static_cast<FieldDescriptor::Type>(type);
+}
+
+inline FieldDescriptor::CppType cpp_type(FieldType type) {
+  return FieldDescriptor::TypeToCppType(
+      static_cast<FieldDescriptor::Type>(type));
+}
+
+inline WireFormatLite::FieldType field_type(FieldType type) {
+  GOOGLE_DCHECK(type > 0 && type <= WireFormatLite::MAX_FIELD_TYPE);
+  return static_cast<WireFormatLite::FieldType>(type);
+}
+
+#define GOOGLE_DCHECK_TYPE(EXTENSION, LABEL, CPPTYPE)                         \
+  GOOGLE_DCHECK_EQ((EXTENSION).is_repeated ? FieldDescriptor::LABEL_REPEATED  \
+                                    : FieldDescriptor::LABEL_OPTIONAL, \
+            FieldDescriptor::LABEL_##LABEL);                           \
+  GOOGLE_DCHECK_EQ(cpp_type((EXTENSION).type), FieldDescriptor::CPPTYPE_##CPPTYPE)
+
+const MessageLite& ExtensionSet::GetMessage(int number,
+                                            const Descriptor* message_type,
+                                            MessageFactory* factory) const {
+  const Extension* extension = FindOrNull(number);
+  if (extension == nullptr || extension->is_cleared) {
+    // Not present.  Return the default value.
+    return *factory->GetPrototype(message_type);
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL, MESSAGE);
+    if (extension->is_lazy) {
+      return extension->lazymessage_value->GetMessage(
+          *factory->GetPrototype(message_type), arena_);
+    } else {
+      return *extension->message_value;
+    }
+  }
+}
+
+MessageLite* ExtensionSet::MutableMessage(const FieldDescriptor* descriptor,
+                                          MessageFactory* factory) {
+  Extension* extension;
+  if (MaybeNewExtension(descriptor->number(), descriptor, &extension)) {
+    extension->type = descriptor->type();
+    GOOGLE_DCHECK_EQ(cpp_type(extension->type), FieldDescriptor::CPPTYPE_MESSAGE);
+    extension->is_repeated = false;
+    extension->is_packed = false;
+    const MessageLite* prototype =
+        factory->GetPrototype(descriptor->message_type());
+    extension->is_lazy = false;
+    extension->message_value = prototype->New(arena_);
+    extension->is_cleared = false;
+    return extension->message_value;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL, MESSAGE);
+    extension->is_cleared = false;
+    if (extension->is_lazy) {
+      return extension->lazymessage_value->MutableMessage(
+          *factory->GetPrototype(descriptor->message_type()), arena_);
+    } else {
+      return extension->message_value;
+    }
+  }
+}
+
+MessageLite* ExtensionSet::ReleaseMessage(const FieldDescriptor* descriptor,
+                                          MessageFactory* factory) {
+  Extension* extension = FindOrNull(descriptor->number());
+  if (extension == nullptr) {
+    // Not present.  Return nullptr.
+    return nullptr;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL, MESSAGE);
+    MessageLite* ret = nullptr;
+    if (extension->is_lazy) {
+      ret = extension->lazymessage_value->ReleaseMessage(
+          *factory->GetPrototype(descriptor->message_type()), arena_);
+      if (arena_ == nullptr) {
+        delete extension->lazymessage_value;
+      }
+    } else {
+      if (arena_ != nullptr) {
+        ret = extension->message_value->New();
+        ret->CheckTypeAndMergeFrom(*extension->message_value);
+      } else {
+        ret = extension->message_value;
+      }
+    }
+    Erase(descriptor->number());
+    return ret;
+  }
+}
+
+MessageLite* ExtensionSet::UnsafeArenaReleaseMessage(
+    const FieldDescriptor* descriptor, MessageFactory* factory) {
+  Extension* extension = FindOrNull(descriptor->number());
+  if (extension == nullptr) {
+    // Not present.  Return nullptr.
+    return nullptr;
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL, MESSAGE);
+    MessageLite* ret = nullptr;
+    if (extension->is_lazy) {
+      ret = extension->lazymessage_value->UnsafeArenaReleaseMessage(
+          *factory->GetPrototype(descriptor->message_type()), arena_);
+      if (arena_ == nullptr) {
+        delete extension->lazymessage_value;
+      }
+    } else {
+      ret = extension->message_value;
+    }
+    Erase(descriptor->number());
+    return ret;
+  }
+}
+
+ExtensionSet::Extension* ExtensionSet::MaybeNewRepeatedExtension(
+    const FieldDescriptor* descriptor) {
+  Extension* extension;
+  if (MaybeNewExtension(descriptor->number(), descriptor, &extension)) {
+    extension->type = descriptor->type();
+    GOOGLE_DCHECK_EQ(cpp_type(extension->type), FieldDescriptor::CPPTYPE_MESSAGE);
+    extension->is_repeated = true;
+    extension->repeated_message_value =
+        Arena::CreateMessage<RepeatedPtrField<MessageLite> >(arena_);
+  } else {
+    GOOGLE_DCHECK_TYPE(*extension, REPEATED, MESSAGE);
+  }
+  return extension;
+}
+
+MessageLite* ExtensionSet::AddMessage(const FieldDescriptor* descriptor,
+                                      MessageFactory* factory) {
+  Extension* extension = MaybeNewRepeatedExtension(descriptor);
+
+  // RepeatedPtrField<Message> does not know how to Add() since it cannot
+  // allocate an abstract object, so we have to be tricky.
+  MessageLite* result =
+      reinterpret_cast<internal::RepeatedPtrFieldBase*>(
+          extension->repeated_message_value)
+          ->AddFromCleared<GenericTypeHandler<MessageLite> >();
+  if (result == nullptr) {
+    const MessageLite* prototype;
+    if (extension->repeated_message_value->empty()) {
+      prototype = factory->GetPrototype(descriptor->message_type());
+      GOOGLE_CHECK(prototype != nullptr);
+    } else {
+      prototype = &extension->repeated_message_value->Get(0);
+    }
+    result = prototype->New(arena_);
+    extension->repeated_message_value->AddAllocated(result);
+  }
+  return result;
+}
+
+void ExtensionSet::AddAllocatedMessage(const FieldDescriptor* descriptor,
+                                       MessageLite* new_entry) {
+  Extension* extension = MaybeNewRepeatedExtension(descriptor);
+
+  extension->repeated_message_value->AddAllocated(new_entry);
+}
+
+void ExtensionSet::UnsafeArenaAddAllocatedMessage(
+    const FieldDescriptor* descriptor, MessageLite* new_entry) {
+  Extension* extension = MaybeNewRepeatedExtension(descriptor);
+
+  extension->repeated_message_value->UnsafeArenaAddAllocated(new_entry);
+}
+
+static bool ValidateEnumUsingDescriptor(const void* arg, int number) {
+  return reinterpret_cast<const EnumDescriptor*>(arg)->FindValueByNumber(
+             number) != nullptr;
+}
+
+bool DescriptorPoolExtensionFinder::Find(int number, ExtensionInfo* output) {
+  const FieldDescriptor* extension =
+      pool_->FindExtensionByNumber(containing_type_, number);
+  if (extension == nullptr) {
+    return false;
+  } else {
+    output->type = extension->type();
+    output->is_repeated = extension->is_repeated();
+    output->is_packed = extension->options().packed();
+    output->descriptor = extension;
+    if (extension->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      output->message_info.prototype =
+          factory_->GetPrototype(extension->message_type());
+      GOOGLE_CHECK(output->message_info.prototype != nullptr)
+          << "Extension factory's GetPrototype() returned nullptr; extension: "
+          << extension->full_name();
+    } else if (extension->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+      output->enum_validity_check.func = ValidateEnumUsingDescriptor;
+      output->enum_validity_check.arg = extension->enum_type();
+    }
+
+    return true;
+  }
+}
+
+
+bool ExtensionSet::FindExtension(int wire_type, uint32_t field,
+                                 const Message* containing_type,
+                                 const internal::ParseContext* ctx,
+                                 ExtensionInfo* extension,
+                                 bool* was_packed_on_wire) {
+  if (ctx->data().pool == nullptr) {
+    GeneratedExtensionFinder finder(containing_type);
+    if (!FindExtensionInfoFromFieldNumber(wire_type, field, &finder, extension,
+                                          was_packed_on_wire)) {
+      return false;
+    }
+  } else {
+    DescriptorPoolExtensionFinder finder(ctx->data().pool, ctx->data().factory,
+                                         containing_type->GetDescriptor());
+    if (!FindExtensionInfoFromFieldNumber(wire_type, field, &finder, extension,
+                                          was_packed_on_wire)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+const char* ExtensionSet::ParseField(uint64_t tag, const char* ptr,
+                                     const Message* containing_type,
+                                     internal::InternalMetadata* metadata,
+                                     internal::ParseContext* ctx) {
+  int number = tag >> 3;
+  bool was_packed_on_wire;
+  ExtensionInfo extension;
+  if (!FindExtension(tag & 7, number, containing_type, ctx, &extension,
+                     &was_packed_on_wire)) {
+    return UnknownFieldParse(
+        tag, metadata->mutable_unknown_fields<UnknownFieldSet>(), ptr, ctx);
+  }
+  return ParseFieldWithExtensionInfo<UnknownFieldSet>(
+      number, was_packed_on_wire, extension, metadata, ptr, ctx);
+}
+
+const char* ExtensionSet::ParseFieldMaybeLazily(
+    uint64_t tag, const char* ptr, const Message* containing_type,
+    internal::InternalMetadata* metadata, internal::ParseContext* ctx) {
+  return ParseField(tag, ptr, containing_type, metadata, ctx);
+}
+
+const char* ExtensionSet::ParseMessageSetItem(
+    const char* ptr, const Message* containing_type,
+    internal::InternalMetadata* metadata, internal::ParseContext* ctx) {
+  return ParseMessageSetItemTmpl<Message, UnknownFieldSet>(ptr, containing_type,
+                                                           metadata, ctx);
+}
+
+int ExtensionSet::SpaceUsedExcludingSelf() const {
+  return internal::FromIntSize(SpaceUsedExcludingSelfLong());
+}
+
+size_t ExtensionSet::SpaceUsedExcludingSelfLong() const {
+  size_t total_size =
+      (is_large() ? map_.large->size() : flat_capacity_) * sizeof(KeyValue);
+  ForEach([&total_size](int /* number */, const Extension& ext) {
+    total_size += ext.SpaceUsedExcludingSelfLong();
+  });
+  return total_size;
+}
+
+inline size_t ExtensionSet::RepeatedMessage_SpaceUsedExcludingSelfLong(
+    RepeatedPtrFieldBase* field) {
+  return field->SpaceUsedExcludingSelfLong<GenericTypeHandler<Message> >();
+}
+
+size_t ExtensionSet::Extension::SpaceUsedExcludingSelfLong() const {
+  size_t total_size = 0;
+  if (is_repeated) {
+    switch (cpp_type(type)) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE)                                     \
+  case FieldDescriptor::CPPTYPE_##UPPERCASE:                                  \
+    total_size += sizeof(*repeated_##LOWERCASE##_value) +                     \
+                  repeated_##LOWERCASE##_value->SpaceUsedExcludingSelfLong(); \
+    break
+
+      HANDLE_TYPE(INT32, int32_t);
+      HANDLE_TYPE(INT64, int64_t);
+      HANDLE_TYPE(UINT32, uint32_t);
+      HANDLE_TYPE(UINT64, uint64_t);
+      HANDLE_TYPE(FLOAT, float);
+      HANDLE_TYPE(DOUBLE, double);
+      HANDLE_TYPE(BOOL, bool);
+      HANDLE_TYPE(ENUM, enum);
+      HANDLE_TYPE(STRING, string);
+#undef HANDLE_TYPE
+
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        // repeated_message_value is actually a RepeatedPtrField<MessageLite>,
+        // but MessageLite has no SpaceUsedLong(), so we must directly call
+        // RepeatedPtrFieldBase::SpaceUsedExcludingSelfLong() with a different
+        // type handler.
+        total_size += sizeof(*repeated_message_value) +
+                      RepeatedMessage_SpaceUsedExcludingSelfLong(
+                          reinterpret_cast<internal::RepeatedPtrFieldBase*>(
+                              repeated_message_value));
+        break;
+    }
+  } else {
+    switch (cpp_type(type)) {
+      case FieldDescriptor::CPPTYPE_STRING:
+        total_size += sizeof(*string_value) +
+                      StringSpaceUsedExcludingSelfLong(*string_value);
+        break;
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        if (is_lazy) {
+          total_size += lazymessage_value->SpaceUsedLong();
+        } else {
+          total_size += down_cast<Message*>(message_value)->SpaceUsedLong();
+        }
+        break;
+      default:
+        // No extra storage costs for primitive types.
+        break;
+    }
+  }
+  return total_size;
+}
+
+uint8_t* ExtensionSet::SerializeMessageSetWithCachedSizesToArray(
+    const MessageLite* extendee, uint8_t* target) const {
+  io::EpsCopyOutputStream stream(
+      target, MessageSetByteSize(),
+      io::CodedOutputStream::IsDefaultSerializationDeterministic());
+  return InternalSerializeMessageSetWithCachedSizesToArray(extendee, target,
+                                                           &stream);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/field_mask.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/field_mask.pb.cpp
new file mode 100644
index 0000000..7860bfb
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/field_mask.pb.cpp
@@ -0,0 +1,284 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/field_mask.proto
+
+#include <google/protobuf/field_mask.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR FieldMask::FieldMask(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.paths_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct FieldMaskDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR FieldMaskDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~FieldMaskDefaultTypeInternal() {}
+  union {
+    FieldMask _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FieldMaskDefaultTypeInternal _FieldMask_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2ffield_5fmask_2eproto[1];
+static constexpr ::_pb::EnumDescriptor const** file_level_enum_descriptors_google_2fprotobuf_2ffield_5fmask_2eproto = nullptr;
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2ffield_5fmask_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2ffield_5fmask_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldMask, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FieldMask, _impl_.paths_),
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::FieldMask)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_FieldMask_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2ffield_5fmask_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n google/protobuf/field_mask.proto\022\017goog"
+  "le.protobuf\"\032\n\tFieldMask\022\r\n\005paths\030\001 \003(\tB"
+  "\205\001\n\023com.google.protobufB\016FieldMaskProtoP"
+  "\001Z2google.golang.org/protobuf/types/know"
+  "n/fieldmaskpb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf"
+  ".WellKnownTypesb\006proto3"
+  ;
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2ffield_5fmask_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2ffield_5fmask_2eproto = {
+    false, false, 223, descriptor_table_protodef_google_2fprotobuf_2ffield_5fmask_2eproto,
+    "google/protobuf/field_mask.proto",
+    &descriptor_table_google_2fprotobuf_2ffield_5fmask_2eproto_once, nullptr, 0, 1,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2ffield_5fmask_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2ffield_5fmask_2eproto, file_level_enum_descriptors_google_2fprotobuf_2ffield_5fmask_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2ffield_5fmask_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2ffield_5fmask_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2ffield_5fmask_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2ffield_5fmask_2eproto(&descriptor_table_google_2fprotobuf_2ffield_5fmask_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class FieldMask::_Internal {
+ public:
+};
+
+FieldMask::FieldMask(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.FieldMask)
+}
+FieldMask::FieldMask(const FieldMask& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  FieldMask* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.paths_){from._impl_.paths_}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.FieldMask)
+}
+
+inline void FieldMask::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.paths_){arena}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+FieldMask::~FieldMask() {
+  // @@protoc_insertion_point(destructor:google.protobuf.FieldMask)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void FieldMask::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.paths_.~RepeatedPtrField();
+}
+
+void FieldMask::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void FieldMask::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.FieldMask)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.paths_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* FieldMask::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // repeated string paths = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            auto str = _internal_add_paths();
+            ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+            CHK_(ptr);
+            CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.FieldMask.paths"));
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* FieldMask::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.FieldMask)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated string paths = 1;
+  for (int i = 0, n = this->_internal_paths_size(); i < n; i++) {
+    const auto& s = this->_internal_paths(i);
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      s.data(), static_cast<int>(s.length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.FieldMask.paths");
+    target = stream->WriteString(1, s, target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.FieldMask)
+  return target;
+}
+
+size_t FieldMask::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FieldMask)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated string paths = 1;
+  total_size += 1 *
+      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(_impl_.paths_.size());
+  for (int i = 0, n = _impl_.paths_.size(); i < n; i++) {
+    total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+      _impl_.paths_.Get(i));
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FieldMask::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    FieldMask::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FieldMask::GetClassData() const { return &_class_data_; }
+
+
+void FieldMask::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<FieldMask*>(&to_msg);
+  auto& from = static_cast<const FieldMask&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FieldMask)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.paths_.MergeFrom(from._impl_.paths_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void FieldMask::CopyFrom(const FieldMask& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FieldMask)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool FieldMask::IsInitialized() const {
+  return true;
+}
+
+void FieldMask::InternalSwap(FieldMask* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.paths_.InternalSwap(&other->_impl_.paths_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata FieldMask::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2ffield_5fmask_2eproto_getter, &descriptor_table_google_2fprotobuf_2ffield_5fmask_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2ffield_5fmask_2eproto[0]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::FieldMask*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::FieldMask >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::FieldMask >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_enum_util.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_enum_util.cpp
new file mode 100644
index 0000000..df7583e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_enum_util.cpp
@@ -0,0 +1,95 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/generated_enum_util.h>
+
+#include <algorithm>
+
+#include <google/protobuf/generated_message_util.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+namespace {
+
+bool EnumCompareByName(const EnumEntry& a, const EnumEntry& b) {
+  return StringPiece(a.name) < StringPiece(b.name);
+}
+
+// Gets the numeric value of the EnumEntry at the given index, but returns a
+// special value for the index -1. This gives a way to use std::lower_bound on a
+// sorted array of indices while searching for value that we associate with -1.
+int GetValue(const EnumEntry* enums, int i, int target) {
+  if (i == -1) {
+    return target;
+  } else {
+    return enums[i].value;
+  }
+}
+
+}  // namespace
+
+bool LookUpEnumValue(const EnumEntry* enums, size_t size,
+                     StringPiece name, int* value) {
+  EnumEntry target{name, 0};
+  auto it = std::lower_bound(enums, enums + size, target, EnumCompareByName);
+  if (it != enums + size && it->name == name) {
+    *value = it->value;
+    return true;
+  }
+  return false;
+}
+
+int LookUpEnumName(const EnumEntry* enums, const int* sorted_indices,
+                   size_t size, int value) {
+  auto comparator = [enums, value](int a, int b) {
+    return GetValue(enums, a, value) < GetValue(enums, b, value);
+  };
+  auto it =
+      std::lower_bound(sorted_indices, sorted_indices + size, -1, comparator);
+  if (it != sorted_indices + size && enums[*it].value == value) {
+    return it - sorted_indices;
+  }
+  return -1;
+}
+
+bool InitializeEnumStrings(
+    const EnumEntry* enums, const int* sorted_indices, size_t size,
+    internal::ExplicitlyConstructed<std::string>* enum_strings) {
+  for (size_t i = 0; i < size; ++i) {
+    enum_strings[i].Construct(enums[sorted_indices[i]].name);
+    internal::OnShutdownDestroyString(enum_strings[i].get_mutable());
+  }
+  return true;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_bases.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_bases.cpp
new file mode 100644
index 0000000..306a38e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_bases.cpp
@@ -0,0 +1,124 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/generated_message_bases.h>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// Must be last:
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// =============================================================================
+// ZeroFieldsBase
+
+void ZeroFieldsBase::Clear() {
+  _internal_metadata_.Clear<UnknownFieldSet>();  //
+}
+
+ZeroFieldsBase::~ZeroFieldsBase() {
+  (void)_internal_metadata_.DeleteReturnArena<UnknownFieldSet>();
+}
+
+size_t ZeroFieldsBase::ByteSizeLong() const {
+  return MaybeComputeUnknownFieldsSize(0, &_cached_size_);
+}
+
+const char* ZeroFieldsBase::_InternalParse(const char* ptr,
+                                           internal::ParseContext* ctx) {
+#define CHK_(x)                       \
+  if (PROTOBUF_PREDICT_FALSE(!(x))) { \
+    goto failure;                     \
+  }
+
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = internal::ReadTag(ptr, &tag);
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag, _internal_metadata_.mutable_unknown_fields<UnknownFieldSet>(), ptr,
+        ctx);
+    CHK_(ptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+::uint8_t* ZeroFieldsBase::_InternalSerialize(
+    ::uint8_t* target, io::EpsCopyOutputStream* stream) const {
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = internal::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<UnknownFieldSet>(
+            UnknownFieldSet::default_instance),
+        target, stream);
+  }
+  return target;
+}
+
+void ZeroFieldsBase::MergeImpl(Message& to_param, const Message& from_param) {
+  auto* to = static_cast<ZeroFieldsBase*>(&to_param);
+  const auto* from = static_cast<const ZeroFieldsBase*>(&from_param);
+  GOOGLE_DCHECK_NE(from, to);
+  to->_internal_metadata_.MergeFrom<UnknownFieldSet>(from->_internal_metadata_);
+}
+
+void ZeroFieldsBase::CopyImpl(Message& to_param, const Message& from_param) {
+  auto* to = static_cast<ZeroFieldsBase*>(&to_param);
+  const auto* from = static_cast<const ZeroFieldsBase*>(&from_param);
+  if (from == to) return;
+  to->_internal_metadata_.Clear<UnknownFieldSet>();
+  to->_internal_metadata_.MergeFrom<UnknownFieldSet>(from->_internal_metadata_);
+}
+
+void ZeroFieldsBase::InternalSwap(ZeroFieldsBase* other) {
+  _internal_metadata_.Swap<UnknownFieldSet>(&other->_internal_metadata_);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_reflection.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_reflection.cpp
new file mode 100644
index 0000000..aaed219
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_reflection.cpp
@@ -0,0 +1,3174 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/generated_message_reflection.h>
+
+#include <algorithm>
+#include <set>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/mutex.h>
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/inlined_string_field.h>
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/map_field_inl.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/unknown_field_set.h>
+
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+#define GOOGLE_PROTOBUF_HAS_ONEOF
+
+using google::protobuf::internal::ArenaStringPtr;
+using google::protobuf::internal::DescriptorTable;
+using google::protobuf::internal::ExtensionSet;
+using google::protobuf::internal::GenericTypeHandler;
+using google::protobuf::internal::GetEmptyString;
+using google::protobuf::internal::InlinedStringField;
+using google::protobuf::internal::InternalMetadata;
+using google::protobuf::internal::LazyField;
+using google::protobuf::internal::MapFieldBase;
+using google::protobuf::internal::MigrationSchema;
+using google::protobuf::internal::OnShutdownDelete;
+using google::protobuf::internal::ReflectionSchema;
+using google::protobuf::internal::RepeatedPtrFieldBase;
+using google::protobuf::internal::StringSpaceUsedExcludingSelfLong;
+using google::protobuf::internal::WrappedMutex;
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4065)
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+
+namespace google {
+namespace protobuf {
+
+namespace {
+bool IsMapFieldInApi(const FieldDescriptor* field) { return field->is_map(); }
+
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+Message* MaybeForceCopy(Arena* arena, Message* msg) {
+  if (arena != nullptr || msg == nullptr) return msg;
+
+  Message* copy = msg->New();
+  copy->MergeFrom(*msg);
+  delete msg;
+  return copy;
+}
+#endif  // PROTOBUF_FORCE_COPY_IN_RELEASE
+}  // anonymous namespace
+
+namespace internal {
+
+bool ParseNamedEnum(const EnumDescriptor* descriptor, ConstStringParam name,
+                    int* value) {
+  const EnumValueDescriptor* d = descriptor->FindValueByName(name);
+  if (d == nullptr) return false;
+  *value = d->number();
+  return true;
+}
+
+const std::string& NameOfEnum(const EnumDescriptor* descriptor, int value) {
+  const EnumValueDescriptor* d = descriptor->FindValueByNumber(value);
+  return (d == nullptr ? GetEmptyString() : d->name());
+}
+
+}  // namespace internal
+
+// ===================================================================
+// Helpers for reporting usage errors (e.g. trying to use GetInt32() on
+// a string field).
+
+namespace {
+
+using internal::GetConstPointerAtOffset;
+using internal::GetConstRefAtOffset;
+using internal::GetPointerAtOffset;
+
+void ReportReflectionUsageError(const Descriptor* descriptor,
+                                const FieldDescriptor* field,
+                                const char* method, const char* description) {
+  GOOGLE_LOG(FATAL) << "Protocol Buffer reflection usage error:\n"
+                "  Method      : google::protobuf::Reflection::"
+             << method
+             << "\n"
+                "  Message type: "
+             << descriptor->full_name()
+             << "\n"
+                "  Field       : "
+             << field->full_name()
+             << "\n"
+                "  Problem     : "
+             << description;
+}
+
+const char* cpptype_names_[FieldDescriptor::MAX_CPPTYPE + 1] = {
+    "INVALID_CPPTYPE", "CPPTYPE_INT32",  "CPPTYPE_INT64",  "CPPTYPE_UINT32",
+    "CPPTYPE_UINT64",  "CPPTYPE_DOUBLE", "CPPTYPE_FLOAT",  "CPPTYPE_BOOL",
+    "CPPTYPE_ENUM",    "CPPTYPE_STRING", "CPPTYPE_MESSAGE"};
+
+static void ReportReflectionUsageTypeError(
+    const Descriptor* descriptor, const FieldDescriptor* field,
+    const char* method, FieldDescriptor::CppType expected_type) {
+  GOOGLE_LOG(FATAL)
+      << "Protocol Buffer reflection usage error:\n"
+         "  Method      : google::protobuf::Reflection::"
+      << method
+      << "\n"
+         "  Message type: "
+      << descriptor->full_name()
+      << "\n"
+         "  Field       : "
+      << field->full_name()
+      << "\n"
+         "  Problem     : Field is not the right type for this message:\n"
+         "    Expected  : "
+      << cpptype_names_[expected_type]
+      << "\n"
+         "    Field type: "
+      << cpptype_names_[field->cpp_type()];
+}
+
+static void ReportReflectionUsageEnumTypeError(
+    const Descriptor* descriptor, const FieldDescriptor* field,
+    const char* method, const EnumValueDescriptor* value) {
+  GOOGLE_LOG(FATAL) << "Protocol Buffer reflection usage error:\n"
+                "  Method      : google::protobuf::Reflection::"
+             << method
+             << "\n"
+                "  Message type: "
+             << descriptor->full_name()
+             << "\n"
+                "  Field       : "
+             << field->full_name()
+             << "\n"
+                "  Problem     : Enum value did not match field type:\n"
+                "    Expected  : "
+             << field->enum_type()->full_name()
+             << "\n"
+                "    Actual    : "
+             << value->full_name();
+}
+
+inline void CheckInvalidAccess(const internal::ReflectionSchema& schema,
+                               const FieldDescriptor* field) {
+  GOOGLE_CHECK(!schema.IsFieldStripped(field))
+      << "invalid access to a stripped field " << field->full_name();
+}
+
+#define USAGE_CHECK(CONDITION, METHOD, ERROR_DESCRIPTION) \
+  if (!(CONDITION))                                       \
+  ReportReflectionUsageError(descriptor_, field, #METHOD, ERROR_DESCRIPTION)
+#define USAGE_CHECK_EQ(A, B, METHOD, ERROR_DESCRIPTION) \
+  USAGE_CHECK((A) == (B), METHOD, ERROR_DESCRIPTION)
+#define USAGE_CHECK_NE(A, B, METHOD, ERROR_DESCRIPTION) \
+  USAGE_CHECK((A) != (B), METHOD, ERROR_DESCRIPTION)
+
+#define USAGE_CHECK_TYPE(METHOD, CPPTYPE)                      \
+  if (field->cpp_type() != FieldDescriptor::CPPTYPE_##CPPTYPE) \
+  ReportReflectionUsageTypeError(descriptor_, field, #METHOD,  \
+                                 FieldDescriptor::CPPTYPE_##CPPTYPE)
+
+#define USAGE_CHECK_ENUM_VALUE(METHOD)     \
+  if (value->type() != field->enum_type()) \
+  ReportReflectionUsageEnumTypeError(descriptor_, field, #METHOD, value)
+
+#define USAGE_CHECK_MESSAGE_TYPE(METHOD)                        \
+  USAGE_CHECK_EQ(field->containing_type(), descriptor_, METHOD, \
+                 "Field does not match message type.");
+#define USAGE_CHECK_SINGULAR(METHOD)                                      \
+  USAGE_CHECK_NE(field->label(), FieldDescriptor::LABEL_REPEATED, METHOD, \
+                 "Field is repeated; the method requires a singular field.")
+#define USAGE_CHECK_REPEATED(METHOD)                                      \
+  USAGE_CHECK_EQ(field->label(), FieldDescriptor::LABEL_REPEATED, METHOD, \
+                 "Field is singular; the method requires a repeated field.")
+
+#define USAGE_CHECK_ALL(METHOD, LABEL, CPPTYPE) \
+  USAGE_CHECK_MESSAGE_TYPE(METHOD);             \
+  USAGE_CHECK_##LABEL(METHOD);                  \
+  USAGE_CHECK_TYPE(METHOD, CPPTYPE)
+
+}  // namespace
+
+// ===================================================================
+
+Reflection::Reflection(const Descriptor* descriptor,
+                       const internal::ReflectionSchema& schema,
+                       const DescriptorPool* pool, MessageFactory* factory)
+    : descriptor_(descriptor),
+      schema_(schema),
+      descriptor_pool_(
+          (pool == nullptr) ? DescriptorPool::internal_generated_pool() : pool),
+      message_factory_(factory),
+      last_non_weak_field_index_(-1) {
+  last_non_weak_field_index_ = descriptor_->field_count() - 1;
+}
+
+const UnknownFieldSet& Reflection::GetUnknownFields(
+    const Message& message) const {
+  return GetInternalMetadata(message).unknown_fields<UnknownFieldSet>(
+      UnknownFieldSet::default_instance);
+}
+
+UnknownFieldSet* Reflection::MutableUnknownFields(Message* message) const {
+  return MutableInternalMetadata(message)
+      ->mutable_unknown_fields<UnknownFieldSet>();
+}
+
+bool Reflection::IsLazyExtension(const Message& message,
+                                 const FieldDescriptor* field) const {
+  return field->is_extension() &&
+         GetExtensionSet(message).HasLazy(field->number());
+}
+
+bool Reflection::IsLazilyVerifiedLazyField(const FieldDescriptor* field) const {
+  if (field->options().unverified_lazy()) return true;
+
+  // Message fields with [lazy=true] will be eagerly verified
+  // (go/verified-lazy).
+  return field->options().lazy() && !IsEagerlyVerifiedLazyField(field);
+}
+
+bool Reflection::IsEagerlyVerifiedLazyField(
+    const FieldDescriptor* field) const {
+  return (field->type() == FieldDescriptor::TYPE_MESSAGE &&
+          schema_.IsEagerlyVerifiedLazyField(field));
+}
+
+bool Reflection::IsInlined(const FieldDescriptor* field) const {
+  return schema_.IsFieldInlined(field);
+}
+
+size_t Reflection::SpaceUsedLong(const Message& message) const {
+  // object_size_ already includes the in-memory representation of each field
+  // in the message, so we only need to account for additional memory used by
+  // the fields.
+  size_t total_size = schema_.GetObjectSize();
+
+  total_size += GetUnknownFields(message).SpaceUsedExcludingSelfLong();
+
+  // If this message owns an arena, add any unused space that's been allocated.
+  auto* arena = Arena::InternalGetArenaForAllocation(&message);
+  if (arena != nullptr && Arena::InternalGetOwningArena(&message) == nullptr &&
+      arena->InternalIsMessageOwnedArena()) {
+    total_size += arena->SpaceAllocated() - arena->SpaceUsed();
+  }
+
+  if (schema_.HasExtensionSet()) {
+    total_size += GetExtensionSet(message).SpaceUsedExcludingSelfLong();
+  }
+  for (int i = 0; i <= last_non_weak_field_index_; i++) {
+    const FieldDescriptor* field = descriptor_->field(i);
+    if (field->is_repeated()) {
+      switch (field->cpp_type()) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE)                           \
+  case FieldDescriptor::CPPTYPE_##UPPERCASE:                        \
+    total_size += GetRaw<RepeatedField<LOWERCASE> >(message, field) \
+                      .SpaceUsedExcludingSelfLong();                \
+    break
+
+        HANDLE_TYPE(INT32, int32_t);
+        HANDLE_TYPE(INT64, int64_t);
+        HANDLE_TYPE(UINT32, uint32_t);
+        HANDLE_TYPE(UINT64, uint64_t);
+        HANDLE_TYPE(DOUBLE, double);
+        HANDLE_TYPE(FLOAT, float);
+        HANDLE_TYPE(BOOL, bool);
+        HANDLE_TYPE(ENUM, int);
+#undef HANDLE_TYPE
+
+        case FieldDescriptor::CPPTYPE_STRING:
+          switch (field->options().ctype()) {
+            default:  // TODO(kenton):  Support other string reps.
+            case FieldOptions::STRING:
+              total_size +=
+                  GetRaw<RepeatedPtrField<std::string> >(message, field)
+                      .SpaceUsedExcludingSelfLong();
+              break;
+          }
+          break;
+
+        case FieldDescriptor::CPPTYPE_MESSAGE:
+          if (IsMapFieldInApi(field)) {
+            total_size += GetRaw<internal::MapFieldBase>(message, field)
+                              .SpaceUsedExcludingSelfLong();
+          } else {
+            // We don't know which subclass of RepeatedPtrFieldBase the type is,
+            // so we use RepeatedPtrFieldBase directly.
+            total_size +=
+                GetRaw<RepeatedPtrFieldBase>(message, field)
+                    .SpaceUsedExcludingSelfLong<GenericTypeHandler<Message> >();
+          }
+
+          break;
+      }
+    } else {
+      if (schema_.InRealOneof(field) && !HasOneofField(message, field)) {
+        continue;
+      }
+      switch (field->cpp_type()) {
+        case FieldDescriptor::CPPTYPE_INT32:
+        case FieldDescriptor::CPPTYPE_INT64:
+        case FieldDescriptor::CPPTYPE_UINT32:
+        case FieldDescriptor::CPPTYPE_UINT64:
+        case FieldDescriptor::CPPTYPE_DOUBLE:
+        case FieldDescriptor::CPPTYPE_FLOAT:
+        case FieldDescriptor::CPPTYPE_BOOL:
+        case FieldDescriptor::CPPTYPE_ENUM:
+          // Field is inline, so we've already counted it.
+          break;
+
+        case FieldDescriptor::CPPTYPE_STRING: {
+          switch (field->options().ctype()) {
+            default:  // TODO(kenton):  Support other string reps.
+            case FieldOptions::STRING:
+              if (IsInlined(field)) {
+                const std::string* ptr =
+                    &GetField<InlinedStringField>(message, field).GetNoArena();
+                total_size += StringSpaceUsedExcludingSelfLong(*ptr);
+              } else {
+                // Initially, the string points to the default value stored
+                // in the prototype. Only count the string if it has been
+                // changed from the default value.
+                // Except oneof fields, those never point to a default instance,
+                // and there is no default instance to point to.
+                const auto& str = GetField<ArenaStringPtr>(message, field);
+                if (!str.IsDefault() || schema_.InRealOneof(field)) {
+                  // string fields are represented by just a pointer, so also
+                  // include sizeof(string) as well.
+                  total_size += sizeof(std::string) +
+                                StringSpaceUsedExcludingSelfLong(str.Get());
+                }
+              }
+              break;
+          }
+          break;
+        }
+
+        case FieldDescriptor::CPPTYPE_MESSAGE:
+          if (schema_.IsDefaultInstance(message)) {
+            // For singular fields, the prototype just stores a pointer to the
+            // external type's prototype, so there is no extra memory usage.
+          } else {
+            const Message* sub_message = GetRaw<const Message*>(message, field);
+            if (sub_message != nullptr) {
+              total_size += sub_message->SpaceUsedLong();
+            }
+          }
+          break;
+      }
+    }
+  }
+  return total_size;
+}
+
+namespace {
+
+template <bool unsafe_shallow_swap>
+struct OneofFieldMover {
+  template <typename FromType, typename ToType>
+  void operator()(const FieldDescriptor* field, FromType* from, ToType* to) {
+    switch (field->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_INT32:
+        to->SetInt32(from->GetInt32());
+        break;
+      case FieldDescriptor::CPPTYPE_INT64:
+        to->SetInt64(from->GetInt64());
+        break;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        to->SetUint32(from->GetUint32());
+        break;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        to->SetUint64(from->GetUint64());
+        break;
+      case FieldDescriptor::CPPTYPE_FLOAT:
+        to->SetFloat(from->GetFloat());
+        break;
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+        to->SetDouble(from->GetDouble());
+        break;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        to->SetBool(from->GetBool());
+        break;
+      case FieldDescriptor::CPPTYPE_ENUM:
+        to->SetEnum(from->GetEnum());
+        break;
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        if (!unsafe_shallow_swap) {
+          to->SetMessage(from->GetMessage());
+        } else {
+          to->UnsafeSetMessage(from->UnsafeGetMessage());
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_STRING:
+        if (!unsafe_shallow_swap) {
+          to->SetString(from->GetString());
+          break;
+        }
+        switch (field->options().ctype()) {
+          default:
+          case FieldOptions::STRING: {
+            to->SetArenaStringPtr(from->GetArenaStringPtr());
+            break;
+          }
+        }
+        break;
+      default:
+        GOOGLE_LOG(FATAL) << "unimplemented type: " << field->cpp_type();
+    }
+    if (unsafe_shallow_swap) {
+      // Not clearing oneof case after move may cause unwanted "ClearOneof"
+      // where the residual message or string value is deleted and causes
+      // use-after-free (only for unsafe swap).
+      from->ClearOneofCase();
+    }
+  }
+};
+
+}  // namespace
+
+namespace internal {
+
+class SwapFieldHelper {
+ public:
+  template <bool unsafe_shallow_swap>
+  static void SwapRepeatedStringField(const Reflection* r, Message* lhs,
+                                      Message* rhs,
+                                      const FieldDescriptor* field);
+
+  template <bool unsafe_shallow_swap>
+  static void SwapInlinedStrings(const Reflection* r, Message* lhs,
+                                 Message* rhs, const FieldDescriptor* field);
+
+  template <bool unsafe_shallow_swap>
+  static void SwapNonInlinedStrings(const Reflection* r, Message* lhs,
+                                    Message* rhs, const FieldDescriptor* field);
+
+  template <bool unsafe_shallow_swap>
+  static void SwapStringField(const Reflection* r, Message* lhs, Message* rhs,
+                              const FieldDescriptor* field);
+
+  static void SwapArenaStringPtr(ArenaStringPtr* lhs, Arena* lhs_arena,
+                                 ArenaStringPtr* rhs, Arena* rhs_arena);
+
+  template <bool unsafe_shallow_swap>
+  static void SwapRepeatedMessageField(const Reflection* r, Message* lhs,
+                                       Message* rhs,
+                                       const FieldDescriptor* field);
+
+  template <bool unsafe_shallow_swap>
+  static void SwapMessageField(const Reflection* r, Message* lhs, Message* rhs,
+                               const FieldDescriptor* field);
+
+  static void SwapMessage(const Reflection* r, Message* lhs, Arena* lhs_arena,
+                          Message* rhs, Arena* rhs_arena,
+                          const FieldDescriptor* field);
+
+  static void SwapNonMessageNonStringField(const Reflection* r, Message* lhs,
+                                           Message* rhs,
+                                           const FieldDescriptor* field);
+};
+
+template <bool unsafe_shallow_swap>
+void SwapFieldHelper::SwapRepeatedStringField(const Reflection* r, Message* lhs,
+                                              Message* rhs,
+                                              const FieldDescriptor* field) {
+  switch (field->options().ctype()) {
+    default:
+    case FieldOptions::STRING: {
+      auto* lhs_string = r->MutableRaw<RepeatedPtrFieldBase>(lhs, field);
+      auto* rhs_string = r->MutableRaw<RepeatedPtrFieldBase>(rhs, field);
+      if (unsafe_shallow_swap) {
+        lhs_string->InternalSwap(rhs_string);
+      } else {
+        lhs_string->Swap<GenericTypeHandler<std::string>>(rhs_string);
+      }
+      break;
+    }
+  }
+}
+
+template <bool unsafe_shallow_swap>
+void SwapFieldHelper::SwapInlinedStrings(const Reflection* r, Message* lhs,
+                                         Message* rhs,
+                                         const FieldDescriptor* field) {
+  // Inlined string field.
+  Arena* lhs_arena = lhs->GetArenaForAllocation();
+  Arena* rhs_arena = rhs->GetArenaForAllocation();
+  auto* lhs_string = r->MutableRaw<InlinedStringField>(lhs, field);
+  auto* rhs_string = r->MutableRaw<InlinedStringField>(rhs, field);
+  uint32_t index = r->schema_.InlinedStringIndex(field);
+  GOOGLE_DCHECK_GT(index, 0);
+  uint32_t* lhs_array = r->MutableInlinedStringDonatedArray(lhs);
+  uint32_t* rhs_array = r->MutableInlinedStringDonatedArray(rhs);
+  uint32_t* lhs_state = &lhs_array[index / 32];
+  uint32_t* rhs_state = &rhs_array[index / 32];
+  bool lhs_arena_dtor_registered = (lhs_array[0] & 0x1u) == 0;
+  bool rhs_arena_dtor_registered = (rhs_array[0] & 0x1u) == 0;
+  const uint32_t mask = ~(static_cast<uint32_t>(1) << (index % 32));
+  if (unsafe_shallow_swap || lhs_arena == rhs_arena) {
+    InlinedStringField::InternalSwap(lhs_string, lhs_arena,
+                                     lhs_arena_dtor_registered, lhs, rhs_string,
+                                     rhs_arena, rhs_arena_dtor_registered, rhs);
+  } else {
+    const std::string temp = lhs_string->Get();
+    lhs_string->Set(rhs_string->Get(), lhs_arena,
+                    r->IsInlinedStringDonated(*lhs, field), lhs_state, mask,
+                    lhs);
+    rhs_string->Set(temp, rhs_arena, r->IsInlinedStringDonated(*rhs, field),
+                    rhs_state, mask, rhs);
+  }
+}
+
+template <bool unsafe_shallow_swap>
+void SwapFieldHelper::SwapNonInlinedStrings(const Reflection* r, Message* lhs,
+                                            Message* rhs,
+                                            const FieldDescriptor* field) {
+  ArenaStringPtr* lhs_string = r->MutableRaw<ArenaStringPtr>(lhs, field);
+  ArenaStringPtr* rhs_string = r->MutableRaw<ArenaStringPtr>(rhs, field);
+  if (unsafe_shallow_swap) {
+    ArenaStringPtr::UnsafeShallowSwap(lhs_string, rhs_string);
+  } else {
+    SwapFieldHelper::SwapArenaStringPtr(
+        lhs_string, lhs->GetArenaForAllocation(),  //
+        rhs_string, rhs->GetArenaForAllocation());
+  }
+}
+
+template <bool unsafe_shallow_swap>
+void SwapFieldHelper::SwapStringField(const Reflection* r, Message* lhs,
+                                      Message* rhs,
+                                      const FieldDescriptor* field) {
+  switch (field->options().ctype()) {
+    default:
+    case FieldOptions::STRING: {
+      if (r->IsInlined(field)) {
+        SwapFieldHelper::SwapInlinedStrings<unsafe_shallow_swap>(r, lhs, rhs,
+                                                                 field);
+      } else {
+        SwapFieldHelper::SwapNonInlinedStrings<unsafe_shallow_swap>(r, lhs, rhs,
+                                                                    field);
+      }
+      break;
+    }
+  }
+}
+
+void SwapFieldHelper::SwapArenaStringPtr(ArenaStringPtr* lhs, Arena* lhs_arena,
+                                         ArenaStringPtr* rhs,
+                                         Arena* rhs_arena) {
+  if (lhs_arena == rhs_arena) {
+    ArenaStringPtr::InternalSwap(lhs, lhs_arena, rhs, rhs_arena);
+  } else if (lhs->IsDefault() && rhs->IsDefault()) {
+    // Nothing to do.
+  } else if (lhs->IsDefault()) {
+    lhs->Set(rhs->Get(), lhs_arena);
+    // rhs needs to be destroyed before overwritten.
+    rhs->Destroy();
+    rhs->InitDefault();
+  } else if (rhs->IsDefault()) {
+    rhs->Set(lhs->Get(), rhs_arena);
+    // lhs needs to be destroyed before overwritten.
+    lhs->Destroy();
+    lhs->InitDefault();
+  } else {
+    std::string temp = lhs->Get();
+    lhs->Set(rhs->Get(), lhs_arena);
+    rhs->Set(std::move(temp), rhs_arena);
+  }
+}
+
+template <bool unsafe_shallow_swap>
+void SwapFieldHelper::SwapRepeatedMessageField(const Reflection* r,
+                                               Message* lhs, Message* rhs,
+                                               const FieldDescriptor* field) {
+  if (IsMapFieldInApi(field)) {
+    auto* lhs_map = r->MutableRaw<MapFieldBase>(lhs, field);
+    auto* rhs_map = r->MutableRaw<MapFieldBase>(rhs, field);
+    if (unsafe_shallow_swap) {
+      lhs_map->UnsafeShallowSwap(rhs_map);
+    } else {
+      lhs_map->Swap(rhs_map);
+    }
+  } else {
+    auto* lhs_rm = r->MutableRaw<RepeatedPtrFieldBase>(lhs, field);
+    auto* rhs_rm = r->MutableRaw<RepeatedPtrFieldBase>(rhs, field);
+    if (unsafe_shallow_swap) {
+      lhs_rm->InternalSwap(rhs_rm);
+    } else {
+      lhs_rm->Swap<GenericTypeHandler<Message>>(rhs_rm);
+    }
+  }
+}
+
+template <bool unsafe_shallow_swap>
+void SwapFieldHelper::SwapMessageField(const Reflection* r, Message* lhs,
+                                       Message* rhs,
+                                       const FieldDescriptor* field) {
+  if (unsafe_shallow_swap) {
+    std::swap(*r->MutableRaw<Message*>(lhs, field),
+              *r->MutableRaw<Message*>(rhs, field));
+  } else {
+    SwapMessage(r, lhs, lhs->GetArenaForAllocation(), rhs,
+                rhs->GetArenaForAllocation(), field);
+  }
+}
+
+void SwapFieldHelper::SwapMessage(const Reflection* r, Message* lhs,
+                                  Arena* lhs_arena, Message* rhs,
+                                  Arena* rhs_arena,
+                                  const FieldDescriptor* field) {
+  Message** lhs_sub = r->MutableRaw<Message*>(lhs, field);
+  Message** rhs_sub = r->MutableRaw<Message*>(rhs, field);
+
+  if (*lhs_sub == *rhs_sub) return;
+
+#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+  if (lhs_arena != nullptr && lhs_arena == rhs_arena) {
+#else   // PROTOBUF_FORCE_COPY_IN_SWAP
+  if (lhs_arena == rhs_arena) {
+#endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+    std::swap(*lhs_sub, *rhs_sub);
+    return;
+  }
+
+  if (*lhs_sub != nullptr && *rhs_sub != nullptr) {
+    (*lhs_sub)->GetReflection()->Swap(*lhs_sub, *rhs_sub);
+  } else if (*lhs_sub == nullptr && r->HasBit(*rhs, field)) {
+    *lhs_sub = (*rhs_sub)->New(lhs_arena);
+    (*lhs_sub)->CopyFrom(**rhs_sub);
+    r->ClearField(rhs, field);
+    // Ensures has bit is unchanged after ClearField.
+    r->SetBit(rhs, field);
+  } else if (*rhs_sub == nullptr && r->HasBit(*lhs, field)) {
+    *rhs_sub = (*lhs_sub)->New(rhs_arena);
+    (*rhs_sub)->CopyFrom(**lhs_sub);
+    r->ClearField(lhs, field);
+    // Ensures has bit is unchanged after ClearField.
+    r->SetBit(lhs, field);
+  }
+}
+
+void SwapFieldHelper::SwapNonMessageNonStringField(
+    const Reflection* r, Message* lhs, Message* rhs,
+    const FieldDescriptor* field) {
+  switch (field->cpp_type()) {
+#define SWAP_VALUES(CPPTYPE, TYPE)               \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE:       \
+    std::swap(*r->MutableRaw<TYPE>(lhs, field),  \
+              *r->MutableRaw<TYPE>(rhs, field)); \
+    break;
+
+    SWAP_VALUES(INT32, int32_t);
+    SWAP_VALUES(INT64, int64_t);
+    SWAP_VALUES(UINT32, uint32_t);
+    SWAP_VALUES(UINT64, uint64_t);
+    SWAP_VALUES(FLOAT, float);
+    SWAP_VALUES(DOUBLE, double);
+    SWAP_VALUES(BOOL, bool);
+    SWAP_VALUES(ENUM, int);
+#undef SWAP_VALUES
+    default:
+      GOOGLE_LOG(FATAL) << "Unimplemented type: " << field->cpp_type();
+  }
+}
+
+}  // namespace internal
+
+void Reflection::SwapField(Message* message1, Message* message2,
+                           const FieldDescriptor* field) const {
+  if (field->is_repeated()) {
+    switch (field->cpp_type()) {
+#define SWAP_ARRAYS(CPPTYPE, TYPE)                                 \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE:                         \
+    MutableRaw<RepeatedField<TYPE> >(message1, field)              \
+        ->Swap(MutableRaw<RepeatedField<TYPE> >(message2, field)); \
+    break;
+
+      SWAP_ARRAYS(INT32, int32_t);
+      SWAP_ARRAYS(INT64, int64_t);
+      SWAP_ARRAYS(UINT32, uint32_t);
+      SWAP_ARRAYS(UINT64, uint64_t);
+      SWAP_ARRAYS(FLOAT, float);
+      SWAP_ARRAYS(DOUBLE, double);
+      SWAP_ARRAYS(BOOL, bool);
+      SWAP_ARRAYS(ENUM, int);
+#undef SWAP_ARRAYS
+
+      case FieldDescriptor::CPPTYPE_STRING:
+        internal::SwapFieldHelper::SwapRepeatedStringField<false>(
+            this, message1, message2, field);
+        break;
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        internal::SwapFieldHelper::SwapRepeatedMessageField<false>(
+            this, message1, message2, field);
+        break;
+
+      default:
+        GOOGLE_LOG(FATAL) << "Unimplemented type: " << field->cpp_type();
+    }
+  } else {
+    switch (field->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        internal::SwapFieldHelper::SwapMessageField<false>(this, message1,
+                                                           message2, field);
+        break;
+
+      case FieldDescriptor::CPPTYPE_STRING:
+        internal::SwapFieldHelper::SwapStringField<false>(this, message1,
+                                                          message2, field);
+        break;
+      default:
+        internal::SwapFieldHelper::SwapNonMessageNonStringField(
+            this, message1, message2, field);
+    }
+  }
+}
+
+void Reflection::UnsafeShallowSwapField(Message* message1, Message* message2,
+                                        const FieldDescriptor* field) const {
+  if (!field->is_repeated()) {
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      internal::SwapFieldHelper::SwapMessageField<true>(this, message1,
+                                                        message2, field);
+    } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
+      internal::SwapFieldHelper::SwapStringField<true>(this, message1, message2,
+                                                       field);
+    } else {
+      internal::SwapFieldHelper::SwapNonMessageNonStringField(this, message1,
+                                                              message2, field);
+    }
+    return;
+  }
+
+  switch (field->cpp_type()) {
+#define SHALLOW_SWAP_ARRAYS(CPPTYPE, TYPE)                                \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE:                                \
+    MutableRaw<RepeatedField<TYPE>>(message1, field)                      \
+        ->InternalSwap(MutableRaw<RepeatedField<TYPE>>(message2, field)); \
+    break;
+
+    SHALLOW_SWAP_ARRAYS(INT32, int32_t);
+    SHALLOW_SWAP_ARRAYS(INT64, int64_t);
+    SHALLOW_SWAP_ARRAYS(UINT32, uint32_t);
+    SHALLOW_SWAP_ARRAYS(UINT64, uint64_t);
+    SHALLOW_SWAP_ARRAYS(FLOAT, float);
+    SHALLOW_SWAP_ARRAYS(DOUBLE, double);
+    SHALLOW_SWAP_ARRAYS(BOOL, bool);
+    SHALLOW_SWAP_ARRAYS(ENUM, int);
+#undef SHALLOW_SWAP_ARRAYS
+
+    case FieldDescriptor::CPPTYPE_STRING:
+      internal::SwapFieldHelper::SwapRepeatedStringField<true>(this, message1,
+                                                               message2, field);
+      break;
+    case FieldDescriptor::CPPTYPE_MESSAGE:
+      internal::SwapFieldHelper::SwapRepeatedMessageField<true>(
+          this, message1, message2, field);
+      break;
+
+    default:
+      GOOGLE_LOG(FATAL) << "Unimplemented type: " << field->cpp_type();
+  }
+}
+
+// Swaps oneof field between lhs and rhs. If unsafe_shallow_swap is true, it
+// directly swaps oneof values; otherwise, it may involve copy/delete. Note that
+// two messages may have different oneof cases. So, it has to be done in three
+// steps (i.e. lhs -> temp, rhs -> lhs, temp -> rhs).
+template <bool unsafe_shallow_swap>
+void Reflection::SwapOneofField(Message* lhs, Message* rhs,
+                                const OneofDescriptor* oneof_descriptor) const {
+  // Wraps a local variable to temporarily store oneof value.
+  struct LocalVarWrapper {
+#define LOCAL_VAR_ACCESSOR(type, var, name)               \
+  type Get##name() const { return oneof_val.type_##var; } \
+  void Set##name(type v) { oneof_val.type_##var = v; }
+
+    LOCAL_VAR_ACCESSOR(int32_t, int32, Int32);
+    LOCAL_VAR_ACCESSOR(int64_t, int64, Int64);
+    LOCAL_VAR_ACCESSOR(uint32_t, uint32, Uint32);
+    LOCAL_VAR_ACCESSOR(uint64_t, uint64, Uint64);
+    LOCAL_VAR_ACCESSOR(float, float, Float);
+    LOCAL_VAR_ACCESSOR(double, double, Double);
+    LOCAL_VAR_ACCESSOR(bool, bool, Bool);
+    LOCAL_VAR_ACCESSOR(int, enum, Enum);
+    LOCAL_VAR_ACCESSOR(Message*, message, Message);
+    LOCAL_VAR_ACCESSOR(ArenaStringPtr, arena_string_ptr, ArenaStringPtr);
+    const std::string& GetString() const { return string_val; }
+    void SetString(const std::string& v) { string_val = v; }
+    Message* UnsafeGetMessage() const { return GetMessage(); }
+    void UnsafeSetMessage(Message* v) { SetMessage(v); }
+    void ClearOneofCase() {}
+
+    union {
+      int32_t type_int32;
+      int64_t type_int64;
+      uint32_t type_uint32;
+      uint64_t type_uint64;
+      float type_float;
+      double type_double;
+      bool type_bool;
+      int type_enum;
+      Message* type_message;
+      internal::ArenaStringPtr type_arena_string_ptr;
+    } oneof_val;
+
+    // std::string cannot be in union.
+    std::string string_val;
+  };
+
+  // Wraps a message pointer to read and write a field.
+  struct MessageWrapper {
+#define MESSAGE_FIELD_ACCESSOR(type, var, name)         \
+  type Get##name() const {                              \
+    return reflection->GetField<type>(*message, field); \
+  }                                                     \
+  void Set##name(type v) { reflection->SetField<type>(message, field, v); }
+
+    MESSAGE_FIELD_ACCESSOR(int32_t, int32, Int32);
+    MESSAGE_FIELD_ACCESSOR(int64_t, int64, Int64);
+    MESSAGE_FIELD_ACCESSOR(uint32_t, uint32, Uint32);
+    MESSAGE_FIELD_ACCESSOR(uint64_t, uint64, Uint64);
+    MESSAGE_FIELD_ACCESSOR(float, float, Float);
+    MESSAGE_FIELD_ACCESSOR(double, double, Double);
+    MESSAGE_FIELD_ACCESSOR(bool, bool, Bool);
+    MESSAGE_FIELD_ACCESSOR(int, enum, Enum);
+    MESSAGE_FIELD_ACCESSOR(ArenaStringPtr, arena_string_ptr, ArenaStringPtr);
+    std::string GetString() const {
+      return reflection->GetString(*message, field);
+    }
+    void SetString(const std::string& v) {
+      reflection->SetString(message, field, v);
+    }
+    Message* GetMessage() const {
+      return reflection->ReleaseMessage(message, field);
+    }
+    void SetMessage(Message* v) {
+      reflection->SetAllocatedMessage(message, v, field);
+    }
+    Message* UnsafeGetMessage() const {
+      return reflection->UnsafeArenaReleaseMessage(message, field);
+    }
+    void UnsafeSetMessage(Message* v) {
+      reflection->UnsafeArenaSetAllocatedMessage(message, v, field);
+    }
+    void ClearOneofCase() {
+      *reflection->MutableOneofCase(message, field->containing_oneof()) = 0;
+    }
+
+    const Reflection* reflection;
+    Message* message;
+    const FieldDescriptor* field;
+  };
+
+  GOOGLE_DCHECK(!oneof_descriptor->is_synthetic());
+  uint32_t oneof_case_lhs = GetOneofCase(*lhs, oneof_descriptor);
+  uint32_t oneof_case_rhs = GetOneofCase(*rhs, oneof_descriptor);
+
+  LocalVarWrapper temp;
+  MessageWrapper lhs_wrapper, rhs_wrapper;
+  const FieldDescriptor* field_lhs = nullptr;
+  OneofFieldMover<unsafe_shallow_swap> mover;
+  // lhs --> temp
+  if (oneof_case_lhs > 0) {
+    field_lhs = descriptor_->FindFieldByNumber(oneof_case_lhs);
+    lhs_wrapper = {this, lhs, field_lhs};
+    mover(field_lhs, &lhs_wrapper, &temp);
+  }
+  // rhs --> lhs
+  if (oneof_case_rhs > 0) {
+    const FieldDescriptor* f = descriptor_->FindFieldByNumber(oneof_case_rhs);
+    lhs_wrapper = {this, lhs, f};
+    rhs_wrapper = {this, rhs, f};
+    mover(f, &rhs_wrapper, &lhs_wrapper);
+  } else if (!unsafe_shallow_swap) {
+    ClearOneof(lhs, oneof_descriptor);
+  }
+  // temp --> rhs
+  if (oneof_case_lhs > 0) {
+    rhs_wrapper = {this, rhs, field_lhs};
+    mover(field_lhs, &temp, &rhs_wrapper);
+  } else if (!unsafe_shallow_swap) {
+    ClearOneof(rhs, oneof_descriptor);
+  }
+
+  if (unsafe_shallow_swap) {
+    *MutableOneofCase(lhs, oneof_descriptor) = oneof_case_rhs;
+    *MutableOneofCase(rhs, oneof_descriptor) = oneof_case_lhs;
+  }
+}
+
+void Reflection::Swap(Message* message1, Message* message2) const {
+  if (message1 == message2) return;
+
+  // TODO(kenton):  Other Reflection methods should probably check this too.
+  GOOGLE_CHECK_EQ(message1->GetReflection(), this)
+      << "First argument to Swap() (of type \""
+      << message1->GetDescriptor()->full_name()
+      << "\") is not compatible with this reflection object (which is for type "
+         "\""
+      << descriptor_->full_name()
+      << "\").  Note that the exact same class is required; not just the same "
+         "descriptor.";
+  GOOGLE_CHECK_EQ(message2->GetReflection(), this)
+      << "Second argument to Swap() (of type \""
+      << message2->GetDescriptor()->full_name()
+      << "\") is not compatible with this reflection object (which is for type "
+         "\""
+      << descriptor_->full_name()
+      << "\").  Note that the exact same class is required; not just the same "
+         "descriptor.";
+
+  // Check that both messages are in the same arena (or both on the heap). We
+  // need to copy all data if not, due to ownership semantics.
+#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+  if (message1->GetOwningArena() == nullptr ||
+      message1->GetOwningArena() != message2->GetOwningArena()) {
+#else   // PROTOBUF_FORCE_COPY_IN_SWAP
+  if (message1->GetOwningArena() != message2->GetOwningArena()) {
+#endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+    // One of the two is guaranteed to have an arena.  Switch things around
+    // to guarantee that message1 has an arena.
+    Arena* arena = message1->GetOwningArena();
+    if (arena == nullptr) {
+      arena = message2->GetOwningArena();
+      std::swap(message1, message2);  // Swapping names for pointers!
+    }
+
+    Message* temp = message1->New(arena);
+    temp->MergeFrom(*message2);
+    message2->CopyFrom(*message1);
+#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    message1->CopyFrom(*temp);
+    if (arena == nullptr) delete temp;
+#else   // PROTOBUF_FORCE_COPY_IN_SWAP
+    Swap(message1, temp);
+#endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+    return;
+  }
+
+  GOOGLE_DCHECK_EQ(message1->GetOwningArena(), message2->GetOwningArena());
+
+  UnsafeArenaSwap(message1, message2);
+}
+
+template <bool unsafe_shallow_swap>
+void Reflection::SwapFieldsImpl(
+    Message* message1, Message* message2,
+    const std::vector<const FieldDescriptor*>& fields) const {
+  if (message1 == message2) return;
+
+  // TODO(kenton):  Other Reflection methods should probably check this too.
+  GOOGLE_CHECK_EQ(message1->GetReflection(), this)
+      << "First argument to SwapFields() (of type \""
+      << message1->GetDescriptor()->full_name()
+      << "\") is not compatible with this reflection object (which is for type "
+         "\""
+      << descriptor_->full_name()
+      << "\").  Note that the exact same class is required; not just the same "
+         "descriptor.";
+  GOOGLE_CHECK_EQ(message2->GetReflection(), this)
+      << "Second argument to SwapFields() (of type \""
+      << message2->GetDescriptor()->full_name()
+      << "\") is not compatible with this reflection object (which is for type "
+         "\""
+      << descriptor_->full_name()
+      << "\").  Note that the exact same class is required; not just the same "
+         "descriptor.";
+
+  std::set<int> swapped_oneof;
+
+  GOOGLE_DCHECK(!unsafe_shallow_swap || message1->GetArenaForAllocation() ==
+                                     message2->GetArenaForAllocation());
+
+  const Message* prototype =
+      message_factory_->GetPrototype(message1->GetDescriptor());
+  for (const auto* field : fields) {
+    CheckInvalidAccess(schema_, field);
+    if (field->is_extension()) {
+      if (unsafe_shallow_swap) {
+        MutableExtensionSet(message1)->UnsafeShallowSwapExtension(
+            MutableExtensionSet(message2), field->number());
+      } else {
+        MutableExtensionSet(message1)->SwapExtension(
+            prototype, MutableExtensionSet(message2), field->number());
+      }
+    } else {
+      if (schema_.InRealOneof(field)) {
+        int oneof_index = field->containing_oneof()->index();
+        // Only swap the oneof field once.
+        if (swapped_oneof.find(oneof_index) != swapped_oneof.end()) {
+          continue;
+        }
+        swapped_oneof.insert(oneof_index);
+        SwapOneofField<unsafe_shallow_swap>(message1, message2,
+                                            field->containing_oneof());
+      } else {
+        // Swap field.
+        if (unsafe_shallow_swap) {
+          UnsafeShallowSwapField(message1, message2, field);
+        } else {
+          SwapField(message1, message2, field);
+        }
+        // Swap has bit for non-repeated fields.  We have already checked for
+        // oneof already. This has to be done after SwapField, because SwapField
+        // may depend on the information in has bits.
+        if (!field->is_repeated()) {
+          SwapBit(message1, message2, field);
+          if (field->options().ctype() == FieldOptions::STRING &&
+              IsInlined(field)) {
+            GOOGLE_DCHECK(!unsafe_shallow_swap ||
+                   message1->GetArenaForAllocation() ==
+                       message2->GetArenaForAllocation());
+            SwapInlinedStringDonated(message1, message2, field);
+          }
+        }
+      }
+    }
+  }
+}
+
+void Reflection::SwapFields(
+    Message* message1, Message* message2,
+    const std::vector<const FieldDescriptor*>& fields) const {
+  SwapFieldsImpl<false>(message1, message2, fields);
+}
+
+void Reflection::UnsafeShallowSwapFields(
+    Message* message1, Message* message2,
+    const std::vector<const FieldDescriptor*>& fields) const {
+  SwapFieldsImpl<true>(message1, message2, fields);
+}
+
+void Reflection::UnsafeArenaSwapFields(
+    Message* lhs, Message* rhs,
+    const std::vector<const FieldDescriptor*>& fields) const {
+  GOOGLE_DCHECK_EQ(lhs->GetArenaForAllocation(), rhs->GetArenaForAllocation());
+  UnsafeShallowSwapFields(lhs, rhs, fields);
+}
+
+// -------------------------------------------------------------------
+
+bool Reflection::HasField(const Message& message,
+                          const FieldDescriptor* field) const {
+  USAGE_CHECK_MESSAGE_TYPE(HasField);
+  USAGE_CHECK_SINGULAR(HasField);
+  CheckInvalidAccess(schema_, field);
+
+  if (field->is_extension()) {
+    return GetExtensionSet(message).Has(field->number());
+  } else {
+    if (schema_.InRealOneof(field)) {
+      return HasOneofField(message, field);
+    } else {
+      return HasBit(message, field);
+    }
+  }
+}
+
+void Reflection::UnsafeArenaSwap(Message* lhs, Message* rhs) const {
+  if (lhs == rhs) return;
+
+  MutableInternalMetadata(lhs)->InternalSwap(MutableInternalMetadata(rhs));
+
+  for (int i = 0; i <= last_non_weak_field_index_; i++) {
+    const FieldDescriptor* field = descriptor_->field(i);
+    if (schema_.InRealOneof(field)) continue;
+    if (schema_.IsFieldStripped(field)) continue;
+    UnsafeShallowSwapField(lhs, rhs, field);
+  }
+  const int oneof_decl_count = descriptor_->oneof_decl_count();
+  for (int i = 0; i < oneof_decl_count; i++) {
+    const OneofDescriptor* oneof = descriptor_->oneof_decl(i);
+    if (!oneof->is_synthetic()) {
+      SwapOneofField<true>(lhs, rhs, oneof);
+    }
+  }
+
+  // Swapping bits need to happen after swapping fields, because the latter may
+  // depend on the has bit information.
+  if (schema_.HasHasbits()) {
+    uint32_t* lhs_has_bits = MutableHasBits(lhs);
+    uint32_t* rhs_has_bits = MutableHasBits(rhs);
+
+    int fields_with_has_bits = 0;
+    for (int i = 0; i < descriptor_->field_count(); i++) {
+      const FieldDescriptor* field = descriptor_->field(i);
+      if (field->is_repeated() || schema_.InRealOneof(field)) {
+        continue;
+      }
+      fields_with_has_bits++;
+    }
+
+    int has_bits_size = (fields_with_has_bits + 31) / 32;
+
+    for (int i = 0; i < has_bits_size; i++) {
+      std::swap(lhs_has_bits[i], rhs_has_bits[i]);
+    }
+  }
+
+  if (schema_.HasInlinedString()) {
+    uint32_t* lhs_donated_array = MutableInlinedStringDonatedArray(lhs);
+    uint32_t* rhs_donated_array = MutableInlinedStringDonatedArray(rhs);
+    int inlined_string_count = 0;
+    for (int i = 0; i < descriptor_->field_count(); i++) {
+      const FieldDescriptor* field = descriptor_->field(i);
+      if (field->is_extension() || field->is_repeated() ||
+          schema_.InRealOneof(field) ||
+          field->options().ctype() != FieldOptions::STRING ||
+          !IsInlined(field)) {
+        continue;
+      }
+      inlined_string_count++;
+    }
+
+    int donated_array_size = inlined_string_count == 0
+                                 ? 0
+                                 // One extra bit for the arena dtor tracking.
+                                 : (inlined_string_count + 1 + 31) / 32;
+    GOOGLE_CHECK_EQ((lhs_donated_array[0] & 0x1u) == 0,
+             (rhs_donated_array[0] & 0x1u) == 0);
+    for (int i = 0; i < donated_array_size; i++) {
+      std::swap(lhs_donated_array[i], rhs_donated_array[i]);
+    }
+  }
+
+  if (schema_.HasExtensionSet()) {
+    MutableExtensionSet(lhs)->InternalSwap(MutableExtensionSet(rhs));
+  }
+}
+
+int Reflection::FieldSize(const Message& message,
+                          const FieldDescriptor* field) const {
+  USAGE_CHECK_MESSAGE_TYPE(FieldSize);
+  USAGE_CHECK_REPEATED(FieldSize);
+  CheckInvalidAccess(schema_, field);
+
+  if (field->is_extension()) {
+    return GetExtensionSet(message).ExtensionSize(field->number());
+  } else {
+    switch (field->cpp_type()) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE)    \
+  case FieldDescriptor::CPPTYPE_##UPPERCASE: \
+    return GetRaw<RepeatedField<LOWERCASE> >(message, field).size()
+
+      HANDLE_TYPE(INT32, int32_t);
+      HANDLE_TYPE(INT64, int64_t);
+      HANDLE_TYPE(UINT32, uint32_t);
+      HANDLE_TYPE(UINT64, uint64_t);
+      HANDLE_TYPE(DOUBLE, double);
+      HANDLE_TYPE(FLOAT, float);
+      HANDLE_TYPE(BOOL, bool);
+      HANDLE_TYPE(ENUM, int);
+#undef HANDLE_TYPE
+
+      case FieldDescriptor::CPPTYPE_STRING:
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        if (IsMapFieldInApi(field)) {
+          const internal::MapFieldBase& map =
+              GetRaw<MapFieldBase>(message, field);
+          if (map.IsRepeatedFieldValid()) {
+            return map.GetRepeatedField().size();
+          } else {
+            // No need to materialize the repeated field if it is out of sync:
+            // its size will be the same as the map's size.
+            return map.size();
+          }
+        } else {
+          return GetRaw<RepeatedPtrFieldBase>(message, field).size();
+        }
+    }
+
+    GOOGLE_LOG(FATAL) << "Can't get here.";
+    return 0;
+  }
+}
+
+void Reflection::ClearField(Message* message,
+                            const FieldDescriptor* field) const {
+  USAGE_CHECK_MESSAGE_TYPE(ClearField);
+  CheckInvalidAccess(schema_, field);
+
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->ClearExtension(field->number());
+  } else if (!field->is_repeated()) {
+    if (schema_.InRealOneof(field)) {
+      ClearOneofField(message, field);
+      return;
+    }
+    if (HasBit(*message, field)) {
+      ClearBit(message, field);
+
+      // We need to set the field back to its default value.
+      switch (field->cpp_type()) {
+#define CLEAR_TYPE(CPPTYPE, TYPE)                                      \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE:                             \
+    *MutableRaw<TYPE>(message, field) = field->default_value_##TYPE(); \
+    break;
+
+        CLEAR_TYPE(INT32, int32_t);
+        CLEAR_TYPE(INT64, int64_t);
+        CLEAR_TYPE(UINT32, uint32_t);
+        CLEAR_TYPE(UINT64, uint64_t);
+        CLEAR_TYPE(FLOAT, float);
+        CLEAR_TYPE(DOUBLE, double);
+        CLEAR_TYPE(BOOL, bool);
+#undef CLEAR_TYPE
+
+        case FieldDescriptor::CPPTYPE_ENUM:
+          *MutableRaw<int>(message, field) =
+              field->default_value_enum()->number();
+          break;
+
+        case FieldDescriptor::CPPTYPE_STRING: {
+          switch (field->options().ctype()) {
+            default:  // TODO(kenton):  Support other string reps.
+            case FieldOptions::STRING:
+              if (IsInlined(field)) {
+                // Currently, string with default value can't be inlined. So we
+                // don't have to handle default value here.
+                MutableRaw<InlinedStringField>(message, field)->ClearToEmpty();
+              } else {
+                auto* str = MutableRaw<ArenaStringPtr>(message, field);
+                str->Destroy();
+                str->InitDefault();
+              }
+              break;
+          }
+          break;
+        }
+
+        case FieldDescriptor::CPPTYPE_MESSAGE:
+          if (schema_.HasBitIndex(field) == static_cast<uint32_t>(-1)) {
+            // Proto3 does not have has-bits and we need to set a message field
+            // to nullptr in order to indicate its un-presence.
+            if (message->GetArenaForAllocation() == nullptr) {
+              delete *MutableRaw<Message*>(message, field);
+            }
+            *MutableRaw<Message*>(message, field) = nullptr;
+          } else {
+            (*MutableRaw<Message*>(message, field))->Clear();
+          }
+          break;
+      }
+    }
+  } else {
+    switch (field->cpp_type()) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE)                           \
+  case FieldDescriptor::CPPTYPE_##UPPERCASE:                        \
+    MutableRaw<RepeatedField<LOWERCASE> >(message, field)->Clear(); \
+    break
+
+      HANDLE_TYPE(INT32, int32_t);
+      HANDLE_TYPE(INT64, int64_t);
+      HANDLE_TYPE(UINT32, uint32_t);
+      HANDLE_TYPE(UINT64, uint64_t);
+      HANDLE_TYPE(DOUBLE, double);
+      HANDLE_TYPE(FLOAT, float);
+      HANDLE_TYPE(BOOL, bool);
+      HANDLE_TYPE(ENUM, int);
+#undef HANDLE_TYPE
+
+      case FieldDescriptor::CPPTYPE_STRING: {
+        switch (field->options().ctype()) {
+          default:  // TODO(kenton):  Support other string reps.
+          case FieldOptions::STRING:
+            MutableRaw<RepeatedPtrField<std::string> >(message, field)->Clear();
+            break;
+        }
+        break;
+      }
+
+      case FieldDescriptor::CPPTYPE_MESSAGE: {
+        if (IsMapFieldInApi(field)) {
+          MutableRaw<MapFieldBase>(message, field)->Clear();
+        } else {
+          // We don't know which subclass of RepeatedPtrFieldBase the type is,
+          // so we use RepeatedPtrFieldBase directly.
+          MutableRaw<RepeatedPtrFieldBase>(message, field)
+              ->Clear<GenericTypeHandler<Message> >();
+        }
+        break;
+      }
+    }
+  }
+}
+
+void Reflection::RemoveLast(Message* message,
+                            const FieldDescriptor* field) const {
+  USAGE_CHECK_MESSAGE_TYPE(RemoveLast);
+  USAGE_CHECK_REPEATED(RemoveLast);
+  CheckInvalidAccess(schema_, field);
+
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->RemoveLast(field->number());
+  } else {
+    switch (field->cpp_type()) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE)                                \
+  case FieldDescriptor::CPPTYPE_##UPPERCASE:                             \
+    MutableRaw<RepeatedField<LOWERCASE> >(message, field)->RemoveLast(); \
+    break
+
+      HANDLE_TYPE(INT32, int32_t);
+      HANDLE_TYPE(INT64, int64_t);
+      HANDLE_TYPE(UINT32, uint32_t);
+      HANDLE_TYPE(UINT64, uint64_t);
+      HANDLE_TYPE(DOUBLE, double);
+      HANDLE_TYPE(FLOAT, float);
+      HANDLE_TYPE(BOOL, bool);
+      HANDLE_TYPE(ENUM, int);
+#undef HANDLE_TYPE
+
+      case FieldDescriptor::CPPTYPE_STRING:
+        switch (field->options().ctype()) {
+          default:  // TODO(kenton):  Support other string reps.
+          case FieldOptions::STRING:
+            MutableRaw<RepeatedPtrField<std::string> >(message, field)
+                ->RemoveLast();
+            break;
+        }
+        break;
+
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        if (IsMapFieldInApi(field)) {
+          MutableRaw<MapFieldBase>(message, field)
+              ->MutableRepeatedField()
+              ->RemoveLast<GenericTypeHandler<Message> >();
+        } else {
+          MutableRaw<RepeatedPtrFieldBase>(message, field)
+              ->RemoveLast<GenericTypeHandler<Message> >();
+        }
+        break;
+    }
+  }
+}
+
+Message* Reflection::ReleaseLast(Message* message,
+                                 const FieldDescriptor* field) const {
+  USAGE_CHECK_ALL(ReleaseLast, REPEATED, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+  Message* released;
+  if (field->is_extension()) {
+    released = static_cast<Message*>(
+        MutableExtensionSet(message)->ReleaseLast(field->number()));
+  } else {
+    if (IsMapFieldInApi(field)) {
+      released = MutableRaw<MapFieldBase>(message, field)
+                     ->MutableRepeatedField()
+                     ->ReleaseLast<GenericTypeHandler<Message>>();
+    } else {
+      released = MutableRaw<RepeatedPtrFieldBase>(message, field)
+                     ->ReleaseLast<GenericTypeHandler<Message>>();
+    }
+  }
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  return MaybeForceCopy(message->GetArenaForAllocation(), released);
+#else   // PROTOBUF_FORCE_COPY_IN_RELEASE
+  return released;
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+}
+
+Message* Reflection::UnsafeArenaReleaseLast(
+    Message* message, const FieldDescriptor* field) const {
+  USAGE_CHECK_ALL(UnsafeArenaReleaseLast, REPEATED, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+  if (field->is_extension()) {
+    return static_cast<Message*>(
+        MutableExtensionSet(message)->UnsafeArenaReleaseLast(field->number()));
+  } else {
+    if (IsMapFieldInApi(field)) {
+      return MutableRaw<MapFieldBase>(message, field)
+          ->MutableRepeatedField()
+          ->UnsafeArenaReleaseLast<GenericTypeHandler<Message>>();
+    } else {
+      return MutableRaw<RepeatedPtrFieldBase>(message, field)
+          ->UnsafeArenaReleaseLast<GenericTypeHandler<Message>>();
+    }
+  }
+}
+
+void Reflection::SwapElements(Message* message, const FieldDescriptor* field,
+                              int index1, int index2) const {
+  USAGE_CHECK_MESSAGE_TYPE(Swap);
+  USAGE_CHECK_REPEATED(Swap);
+  CheckInvalidAccess(schema_, field);
+
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->SwapElements(field->number(), index1, index2);
+  } else {
+    switch (field->cpp_type()) {
+#define HANDLE_TYPE(UPPERCASE, LOWERCASE)                 \
+  case FieldDescriptor::CPPTYPE_##UPPERCASE:              \
+    MutableRaw<RepeatedField<LOWERCASE> >(message, field) \
+        ->SwapElements(index1, index2);                   \
+    break
+
+      HANDLE_TYPE(INT32, int32_t);
+      HANDLE_TYPE(INT64, int64_t);
+      HANDLE_TYPE(UINT32, uint32_t);
+      HANDLE_TYPE(UINT64, uint64_t);
+      HANDLE_TYPE(DOUBLE, double);
+      HANDLE_TYPE(FLOAT, float);
+      HANDLE_TYPE(BOOL, bool);
+      HANDLE_TYPE(ENUM, int);
+#undef HANDLE_TYPE
+
+      case FieldDescriptor::CPPTYPE_STRING:
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        if (IsMapFieldInApi(field)) {
+          MutableRaw<MapFieldBase>(message, field)
+              ->MutableRepeatedField()
+              ->SwapElements(index1, index2);
+        } else {
+          MutableRaw<RepeatedPtrFieldBase>(message, field)
+              ->SwapElements(index1, index2);
+        }
+        break;
+    }
+  }
+}
+
+namespace {
+// Comparison functor for sorting FieldDescriptors by field number.
+struct FieldNumberSorter {
+  bool operator()(const FieldDescriptor* left,
+                  const FieldDescriptor* right) const {
+    return left->number() < right->number();
+  }
+};
+
+bool IsIndexInHasBitSet(const uint32_t* has_bit_set, uint32_t has_bit_index) {
+  GOOGLE_DCHECK_NE(has_bit_index, ~0u);
+  return ((has_bit_set[has_bit_index / 32] >> (has_bit_index % 32)) &
+          static_cast<uint32_t>(1)) != 0;
+}
+
+bool CreateUnknownEnumValues(const FileDescriptor* file) {
+  return file->syntax() == FileDescriptor::SYNTAX_PROTO3;
+}
+}  // namespace
+
+namespace internal {
+bool CreateUnknownEnumValues(const FieldDescriptor* field) {
+  bool open_enum = false;
+  return field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 || open_enum;
+}
+}  // namespace internal
+using internal::CreateUnknownEnumValues;
+
+void Reflection::ListFieldsMayFailOnStripped(
+    const Message& message, bool should_fail,
+    std::vector<const FieldDescriptor*>* output) const {
+  output->clear();
+
+  // Optimization:  The default instance never has any fields set.
+  if (schema_.IsDefaultInstance(message)) return;
+
+  // Optimization: Avoid calling GetHasBits() and HasOneofField() many times
+  // within the field loop.  We allow this violation of ReflectionSchema
+  // encapsulation because this function takes a noticeable about of CPU
+  // fleetwide and properly allowing this optimization through public interfaces
+  // seems more trouble than it is worth.
+  const uint32_t* const has_bits =
+      schema_.HasHasbits() ? GetHasBits(message) : nullptr;
+  const uint32_t* const has_bits_indices = schema_.has_bit_indices_;
+  output->reserve(descriptor_->field_count());
+  const int last_non_weak_field_index = last_non_weak_field_index_;
+  for (int i = 0; i <= last_non_weak_field_index; i++) {
+    const FieldDescriptor* field = descriptor_->field(i);
+    if (!should_fail && schema_.IsFieldStripped(field)) {
+      continue;
+    }
+    if (field->is_repeated()) {
+      if (FieldSize(message, field) > 0) {
+        output->push_back(field);
+      }
+    } else {
+      const OneofDescriptor* containing_oneof = field->containing_oneof();
+      if (schema_.InRealOneof(field)) {
+        const uint32_t* const oneof_case_array =
+            GetConstPointerAtOffset<uint32_t>(&message,
+                                              schema_.oneof_case_offset_);
+        // Equivalent to: HasOneofField(message, field)
+        if (static_cast<int64_t>(oneof_case_array[containing_oneof->index()]) ==
+            field->number()) {
+          output->push_back(field);
+        }
+      } else if (has_bits && has_bits_indices[i] != static_cast<uint32_t>(-1)) {
+        CheckInvalidAccess(schema_, field);
+        // Equivalent to: HasBit(message, field)
+        if (IsIndexInHasBitSet(has_bits, has_bits_indices[i])) {
+          output->push_back(field);
+        }
+      } else if (HasBit(message, field)) {  // Fall back on proto3-style HasBit.
+        output->push_back(field);
+      }
+    }
+  }
+  if (schema_.HasExtensionSet()) {
+    GetExtensionSet(message).AppendToList(descriptor_, descriptor_pool_,
+                                          output);
+  }
+
+  // ListFields() must sort output by field number.
+  std::sort(output->begin(), output->end(), FieldNumberSorter());
+}
+
+void Reflection::ListFields(const Message& message,
+                            std::vector<const FieldDescriptor*>* output) const {
+  ListFieldsMayFailOnStripped(message, true, output);
+}
+
+void Reflection::ListFieldsOmitStripped(
+    const Message& message, std::vector<const FieldDescriptor*>* output) const {
+  ListFieldsMayFailOnStripped(message, false, output);
+}
+
+// -------------------------------------------------------------------
+
+#undef DEFINE_PRIMITIVE_ACCESSORS
+#define DEFINE_PRIMITIVE_ACCESSORS(TYPENAME, TYPE, PASSTYPE, CPPTYPE)          \
+  PASSTYPE Reflection::Get##TYPENAME(const Message& message,                   \
+                                     const FieldDescriptor* field) const {     \
+    USAGE_CHECK_ALL(Get##TYPENAME, SINGULAR, CPPTYPE);                         \
+    if (field->is_extension()) {                                               \
+      return GetExtensionSet(message).Get##TYPENAME(                           \
+          field->number(), field->default_value_##PASSTYPE());                 \
+    } else if (schema_.InRealOneof(field) && !HasOneofField(message, field)) { \
+      return field->default_value_##PASSTYPE();                                \
+    } else {                                                                   \
+      return GetField<TYPE>(message, field);                                   \
+    }                                                                          \
+  }                                                                            \
+                                                                               \
+  void Reflection::Set##TYPENAME(                                              \
+      Message* message, const FieldDescriptor* field, PASSTYPE value) const {  \
+    USAGE_CHECK_ALL(Set##TYPENAME, SINGULAR, CPPTYPE);                         \
+    if (field->is_extension()) {                                               \
+      return MutableExtensionSet(message)->Set##TYPENAME(                      \
+          field->number(), field->type(), value, field);                       \
+    } else {                                                                   \
+      SetField<TYPE>(message, field, value);                                   \
+    }                                                                          \
+  }                                                                            \
+                                                                               \
+  PASSTYPE Reflection::GetRepeated##TYPENAME(                                  \
+      const Message& message, const FieldDescriptor* field, int index) const { \
+    USAGE_CHECK_ALL(GetRepeated##TYPENAME, REPEATED, CPPTYPE);                 \
+    if (field->is_extension()) {                                               \
+      return GetExtensionSet(message).GetRepeated##TYPENAME(field->number(),   \
+                                                            index);            \
+    } else {                                                                   \
+      return GetRepeatedField<TYPE>(message, field, index);                    \
+    }                                                                          \
+  }                                                                            \
+                                                                               \
+  void Reflection::SetRepeated##TYPENAME(Message* message,                     \
+                                         const FieldDescriptor* field,         \
+                                         int index, PASSTYPE value) const {    \
+    USAGE_CHECK_ALL(SetRepeated##TYPENAME, REPEATED, CPPTYPE);                 \
+    if (field->is_extension()) {                                               \
+      MutableExtensionSet(message)->SetRepeated##TYPENAME(field->number(),     \
+                                                          index, value);       \
+    } else {                                                                   \
+      SetRepeatedField<TYPE>(message, field, index, value);                    \
+    }                                                                          \
+  }                                                                            \
+                                                                               \
+  void Reflection::Add##TYPENAME(                                              \
+      Message* message, const FieldDescriptor* field, PASSTYPE value) const {  \
+    USAGE_CHECK_ALL(Add##TYPENAME, REPEATED, CPPTYPE);                         \
+    if (field->is_extension()) {                                               \
+      MutableExtensionSet(message)->Add##TYPENAME(                             \
+          field->number(), field->type(), field->options().packed(), value,    \
+          field);                                                              \
+    } else {                                                                   \
+      AddField<TYPE>(message, field, value);                                   \
+    }                                                                          \
+  }
+
+DEFINE_PRIMITIVE_ACCESSORS(Int32, int32_t, int32_t, INT32)
+DEFINE_PRIMITIVE_ACCESSORS(Int64, int64_t, int64_t, INT64)
+DEFINE_PRIMITIVE_ACCESSORS(UInt32, uint32_t, uint32_t, UINT32)
+DEFINE_PRIMITIVE_ACCESSORS(UInt64, uint64_t, uint64_t, UINT64)
+DEFINE_PRIMITIVE_ACCESSORS(Float, float, float, FLOAT)
+DEFINE_PRIMITIVE_ACCESSORS(Double, double, double, DOUBLE)
+DEFINE_PRIMITIVE_ACCESSORS(Bool, bool, bool, BOOL)
+#undef DEFINE_PRIMITIVE_ACCESSORS
+
+// -------------------------------------------------------------------
+
+std::string Reflection::GetString(const Message& message,
+                                  const FieldDescriptor* field) const {
+  USAGE_CHECK_ALL(GetString, SINGULAR, STRING);
+  if (field->is_extension()) {
+    return GetExtensionSet(message).GetString(field->number(),
+                                              field->default_value_string());
+  } else {
+    if (schema_.InRealOneof(field) && !HasOneofField(message, field)) {
+      return field->default_value_string();
+    }
+    switch (field->options().ctype()) {
+      default:  // TODO(kenton):  Support other string reps.
+      case FieldOptions::STRING:
+        if (IsInlined(field)) {
+          return GetField<InlinedStringField>(message, field).GetNoArena();
+        } else {
+          const auto& str = GetField<ArenaStringPtr>(message, field);
+          return str.IsDefault() ? field->default_value_string() : str.Get();
+        }
+    }
+  }
+}
+
+const std::string& Reflection::GetStringReference(const Message& message,
+                                                  const FieldDescriptor* field,
+                                                  std::string* scratch) const {
+  (void)scratch;  // Parameter is used by Google-internal code.
+  USAGE_CHECK_ALL(GetStringReference, SINGULAR, STRING);
+  if (field->is_extension()) {
+    return GetExtensionSet(message).GetString(field->number(),
+                                              field->default_value_string());
+  } else {
+    if (schema_.InRealOneof(field) && !HasOneofField(message, field)) {
+      return field->default_value_string();
+    }
+    switch (field->options().ctype()) {
+      default:  // TODO(kenton):  Support other string reps.
+      case FieldOptions::STRING:
+        if (IsInlined(field)) {
+          return GetField<InlinedStringField>(message, field).GetNoArena();
+        } else {
+          const auto& str = GetField<ArenaStringPtr>(message, field);
+          return str.IsDefault() ? field->default_value_string() : str.Get();
+        }
+    }
+  }
+}
+
+
+void Reflection::SetString(Message* message, const FieldDescriptor* field,
+                           std::string value) const {
+  USAGE_CHECK_ALL(SetString, SINGULAR, STRING);
+  if (field->is_extension()) {
+    return MutableExtensionSet(message)->SetString(
+        field->number(), field->type(), std::move(value), field);
+  } else {
+    switch (field->options().ctype()) {
+      default:  // TODO(kenton):  Support other string reps.
+      case FieldOptions::STRING: {
+        if (IsInlined(field)) {
+          const uint32_t index = schema_.InlinedStringIndex(field);
+          GOOGLE_DCHECK_GT(index, 0);
+          uint32_t* states =
+              &MutableInlinedStringDonatedArray(message)[index / 32];
+          uint32_t mask = ~(static_cast<uint32_t>(1) << (index % 32));
+          MutableField<InlinedStringField>(message, field)
+              ->Set(value, message->GetArenaForAllocation(),
+                    IsInlinedStringDonated(*message, field), states, mask,
+                    message);
+          break;
+        }
+
+        // Oneof string fields are never set as a default instance.
+        // We just need to pass some arbitrary default string to make it work.
+        // This allows us to not have the real default accessible from
+        // reflection.
+        if (schema_.InRealOneof(field) && !HasOneofField(*message, field)) {
+          ClearOneof(message, field->containing_oneof());
+          MutableField<ArenaStringPtr>(message, field)->InitDefault();
+        }
+        MutableField<ArenaStringPtr>(message, field)
+            ->Set(std::move(value), message->GetArenaForAllocation());
+        break;
+      }
+    }
+  }
+}
+
+
+std::string Reflection::GetRepeatedString(const Message& message,
+                                          const FieldDescriptor* field,
+                                          int index) const {
+  USAGE_CHECK_ALL(GetRepeatedString, REPEATED, STRING);
+  if (field->is_extension()) {
+    return GetExtensionSet(message).GetRepeatedString(field->number(), index);
+  } else {
+    switch (field->options().ctype()) {
+      default:  // TODO(kenton):  Support other string reps.
+      case FieldOptions::STRING:
+        return GetRepeatedPtrField<std::string>(message, field, index);
+    }
+  }
+}
+
+const std::string& Reflection::GetRepeatedStringReference(
+    const Message& message, const FieldDescriptor* field, int index,
+    std::string* scratch) const {
+  (void)scratch;  // Parameter is used by Google-internal code.
+  USAGE_CHECK_ALL(GetRepeatedStringReference, REPEATED, STRING);
+  if (field->is_extension()) {
+    return GetExtensionSet(message).GetRepeatedString(field->number(), index);
+  } else {
+    switch (field->options().ctype()) {
+      default:  // TODO(kenton):  Support other string reps.
+      case FieldOptions::STRING:
+        return GetRepeatedPtrField<std::string>(message, field, index);
+    }
+  }
+}
+
+
+void Reflection::SetRepeatedString(Message* message,
+                                   const FieldDescriptor* field, int index,
+                                   std::string value) const {
+  USAGE_CHECK_ALL(SetRepeatedString, REPEATED, STRING);
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->SetRepeatedString(field->number(), index,
+                                                    std::move(value));
+  } else {
+    switch (field->options().ctype()) {
+      default:  // TODO(kenton):  Support other string reps.
+      case FieldOptions::STRING:
+        MutableRepeatedField<std::string>(message, field, index)
+            ->assign(std::move(value));
+        break;
+    }
+  }
+}
+
+
+void Reflection::AddString(Message* message, const FieldDescriptor* field,
+                           std::string value) const {
+  USAGE_CHECK_ALL(AddString, REPEATED, STRING);
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->AddString(field->number(), field->type(),
+                                            std::move(value), field);
+  } else {
+    switch (field->options().ctype()) {
+      default:  // TODO(kenton):  Support other string reps.
+      case FieldOptions::STRING:
+        AddField<std::string>(message, field)->assign(std::move(value));
+        break;
+    }
+  }
+}
+
+
+// -------------------------------------------------------------------
+
+const EnumValueDescriptor* Reflection::GetEnum(
+    const Message& message, const FieldDescriptor* field) const {
+  // Usage checked by GetEnumValue.
+  int value = GetEnumValue(message, field);
+  return field->enum_type()->FindValueByNumberCreatingIfUnknown(value);
+}
+
+int Reflection::GetEnumValue(const Message& message,
+                             const FieldDescriptor* field) const {
+  USAGE_CHECK_ALL(GetEnumValue, SINGULAR, ENUM);
+
+  int32_t value;
+  if (field->is_extension()) {
+    value = GetExtensionSet(message).GetEnum(
+        field->number(), field->default_value_enum()->number());
+  } else if (schema_.InRealOneof(field) && !HasOneofField(message, field)) {
+    value = field->default_value_enum()->number();
+  } else {
+    value = GetField<int>(message, field);
+  }
+  return value;
+}
+
+void Reflection::SetEnum(Message* message, const FieldDescriptor* field,
+                         const EnumValueDescriptor* value) const {
+  // Usage checked by SetEnumValue.
+  USAGE_CHECK_ENUM_VALUE(SetEnum);
+  SetEnumValueInternal(message, field, value->number());
+}
+
+void Reflection::SetEnumValue(Message* message, const FieldDescriptor* field,
+                              int value) const {
+  USAGE_CHECK_ALL(SetEnumValue, SINGULAR, ENUM);
+  if (!CreateUnknownEnumValues(field)) {
+    // Check that the value is valid if we don't support direct storage of
+    // unknown enum values.
+    const EnumValueDescriptor* value_desc =
+        field->enum_type()->FindValueByNumber(value);
+    if (value_desc == nullptr) {
+      MutableUnknownFields(message)->AddVarint(field->number(), value);
+      return;
+    }
+  }
+  SetEnumValueInternal(message, field, value);
+}
+
+void Reflection::SetEnumValueInternal(Message* message,
+                                      const FieldDescriptor* field,
+                                      int value) const {
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->SetEnum(field->number(), field->type(), value,
+                                          field);
+  } else {
+    SetField<int>(message, field, value);
+  }
+}
+
+const EnumValueDescriptor* Reflection::GetRepeatedEnum(
+    const Message& message, const FieldDescriptor* field, int index) const {
+  // Usage checked by GetRepeatedEnumValue.
+  int value = GetRepeatedEnumValue(message, field, index);
+  return field->enum_type()->FindValueByNumberCreatingIfUnknown(value);
+}
+
+int Reflection::GetRepeatedEnumValue(const Message& message,
+                                     const FieldDescriptor* field,
+                                     int index) const {
+  USAGE_CHECK_ALL(GetRepeatedEnumValue, REPEATED, ENUM);
+
+  int value;
+  if (field->is_extension()) {
+    value = GetExtensionSet(message).GetRepeatedEnum(field->number(), index);
+  } else {
+    value = GetRepeatedField<int>(message, field, index);
+  }
+  return value;
+}
+
+void Reflection::SetRepeatedEnum(Message* message, const FieldDescriptor* field,
+                                 int index,
+                                 const EnumValueDescriptor* value) const {
+  // Usage checked by SetRepeatedEnumValue.
+  USAGE_CHECK_ENUM_VALUE(SetRepeatedEnum);
+  SetRepeatedEnumValueInternal(message, field, index, value->number());
+}
+
+void Reflection::SetRepeatedEnumValue(Message* message,
+                                      const FieldDescriptor* field, int index,
+                                      int value) const {
+  USAGE_CHECK_ALL(SetRepeatedEnum, REPEATED, ENUM);
+  if (!CreateUnknownEnumValues(field)) {
+    // Check that the value is valid if we don't support direct storage of
+    // unknown enum values.
+    const EnumValueDescriptor* value_desc =
+        field->enum_type()->FindValueByNumber(value);
+    if (value_desc == nullptr) {
+      MutableUnknownFields(message)->AddVarint(field->number(), value);
+      return;
+    }
+  }
+  SetRepeatedEnumValueInternal(message, field, index, value);
+}
+
+void Reflection::SetRepeatedEnumValueInternal(Message* message,
+                                              const FieldDescriptor* field,
+                                              int index, int value) const {
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->SetRepeatedEnum(field->number(), index,
+                                                  value);
+  } else {
+    SetRepeatedField<int>(message, field, index, value);
+  }
+}
+
+void Reflection::AddEnum(Message* message, const FieldDescriptor* field,
+                         const EnumValueDescriptor* value) const {
+  // Usage checked by AddEnumValue.
+  USAGE_CHECK_ENUM_VALUE(AddEnum);
+  AddEnumValueInternal(message, field, value->number());
+}
+
+void Reflection::AddEnumValue(Message* message, const FieldDescriptor* field,
+                              int value) const {
+  USAGE_CHECK_ALL(AddEnum, REPEATED, ENUM);
+  if (!CreateUnknownEnumValues(field)) {
+    // Check that the value is valid if we don't support direct storage of
+    // unknown enum values.
+    const EnumValueDescriptor* value_desc =
+        field->enum_type()->FindValueByNumber(value);
+    if (value_desc == nullptr) {
+      MutableUnknownFields(message)->AddVarint(field->number(), value);
+      return;
+    }
+  }
+  AddEnumValueInternal(message, field, value);
+}
+
+void Reflection::AddEnumValueInternal(Message* message,
+                                      const FieldDescriptor* field,
+                                      int value) const {
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->AddEnum(field->number(), field->type(),
+                                          field->options().packed(), value,
+                                          field);
+  } else {
+    AddField<int>(message, field, value);
+  }
+}
+
+// -------------------------------------------------------------------
+
+const Message* Reflection::GetDefaultMessageInstance(
+    const FieldDescriptor* field) const {
+  // If we are using the generated factory, we cache the prototype in the field
+  // descriptor for faster access.
+  // The default instances of generated messages are not cross-linked, which
+  // means they contain null pointers on their message fields and can't be used
+  // to get the default of submessages.
+  if (message_factory_ == MessageFactory::generated_factory()) {
+    auto& ptr = field->default_generated_instance_;
+    auto* res = ptr.load(std::memory_order_acquire);
+    if (res == nullptr) {
+      // First time asking for this field's default. Load it and cache it.
+      res = message_factory_->GetPrototype(field->message_type());
+      ptr.store(res, std::memory_order_release);
+    }
+    return res;
+  }
+
+  // For other factories, we try the default's object field.
+  // In particular, the DynamicMessageFactory will cross link the default
+  // instances to allow for this. But only do this for real fields.
+  // This is an optimization to avoid going to GetPrototype() below, as that
+  // requires a lock and a map lookup.
+  if (!field->is_extension() && !field->options().weak() &&
+      !IsLazyField(field) && !schema_.InRealOneof(field)) {
+    auto* res = DefaultRaw<const Message*>(field);
+    if (res != nullptr) {
+      return res;
+    }
+  }
+  // Otherwise, just go to the factory.
+  return message_factory_->GetPrototype(field->message_type());
+}
+
+const Message& Reflection::GetMessage(const Message& message,
+                                      const FieldDescriptor* field,
+                                      MessageFactory* factory) const {
+  USAGE_CHECK_ALL(GetMessage, SINGULAR, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+  if (factory == nullptr) factory = message_factory_;
+
+  if (field->is_extension()) {
+    return static_cast<const Message&>(GetExtensionSet(message).GetMessage(
+        field->number(), field->message_type(), factory));
+  } else {
+    if (schema_.InRealOneof(field) && !HasOneofField(message, field)) {
+      return *GetDefaultMessageInstance(field);
+    }
+    const Message* result = GetRaw<const Message*>(message, field);
+    if (result == nullptr) {
+      result = GetDefaultMessageInstance(field);
+    }
+    return *result;
+  }
+}
+
+Message* Reflection::MutableMessage(Message* message,
+                                    const FieldDescriptor* field,
+                                    MessageFactory* factory) const {
+  USAGE_CHECK_ALL(MutableMessage, SINGULAR, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+  if (factory == nullptr) factory = message_factory_;
+
+  if (field->is_extension()) {
+    return static_cast<Message*>(
+        MutableExtensionSet(message)->MutableMessage(field, factory));
+  } else {
+    Message* result;
+
+    Message** result_holder = MutableRaw<Message*>(message, field);
+
+    if (schema_.InRealOneof(field)) {
+      if (!HasOneofField(*message, field)) {
+        ClearOneof(message, field->containing_oneof());
+        result_holder = MutableField<Message*>(message, field);
+        const Message* default_message = GetDefaultMessageInstance(field);
+        *result_holder = default_message->New(message->GetArenaForAllocation());
+      }
+    } else {
+      SetBit(message, field);
+    }
+
+    if (*result_holder == nullptr) {
+      const Message* default_message = GetDefaultMessageInstance(field);
+      *result_holder = default_message->New(message->GetArenaForAllocation());
+    }
+    result = *result_holder;
+    return result;
+  }
+}
+
+void Reflection::UnsafeArenaSetAllocatedMessage(
+    Message* message, Message* sub_message,
+    const FieldDescriptor* field) const {
+  USAGE_CHECK_ALL(SetAllocatedMessage, SINGULAR, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->UnsafeArenaSetAllocatedMessage(
+        field->number(), field->type(), field, sub_message);
+  } else {
+    if (schema_.InRealOneof(field)) {
+      if (sub_message == nullptr) {
+        ClearOneof(message, field->containing_oneof());
+        return;
+      }
+        ClearOneof(message, field->containing_oneof());
+        *MutableRaw<Message*>(message, field) = sub_message;
+      SetOneofCase(message, field);
+      return;
+    }
+
+    if (sub_message == nullptr) {
+      ClearBit(message, field);
+    } else {
+      SetBit(message, field);
+    }
+    Message** sub_message_holder = MutableRaw<Message*>(message, field);
+    if (message->GetArenaForAllocation() == nullptr) {
+      delete *sub_message_holder;
+    }
+    *sub_message_holder = sub_message;
+  }
+}
+
+void Reflection::SetAllocatedMessage(Message* message, Message* sub_message,
+                                     const FieldDescriptor* field) const {
+  GOOGLE_DCHECK(sub_message == nullptr || sub_message->GetOwningArena() == nullptr ||
+         sub_message->GetOwningArena() == message->GetArenaForAllocation());
+  CheckInvalidAccess(schema_, field);
+
+  // If message and sub-message are in different memory ownership domains
+  // (different arenas, or one is on heap and one is not), then we may need to
+  // do a copy.
+  if (sub_message != nullptr &&
+      sub_message->GetOwningArena() != message->GetArenaForAllocation()) {
+    if (sub_message->GetOwningArena() == nullptr &&
+        message->GetArenaForAllocation() != nullptr) {
+      // Case 1: parent is on an arena and child is heap-allocated. We can add
+      // the child to the arena's Own() list to free on arena destruction, then
+      // set our pointer.
+      message->GetArenaForAllocation()->Own(sub_message);
+      UnsafeArenaSetAllocatedMessage(message, sub_message, field);
+    } else {
+      // Case 2: all other cases. We need to make a copy. MutableMessage() will
+      // either get the existing message object, or instantiate a new one as
+      // appropriate w.r.t. our arena.
+      Message* sub_message_copy = MutableMessage(message, field);
+      sub_message_copy->CopyFrom(*sub_message);
+    }
+  } else {
+    // Same memory ownership domains.
+    UnsafeArenaSetAllocatedMessage(message, sub_message, field);
+  }
+}
+
+Message* Reflection::UnsafeArenaReleaseMessage(Message* message,
+                                               const FieldDescriptor* field,
+                                               MessageFactory* factory) const {
+  USAGE_CHECK_ALL(ReleaseMessage, SINGULAR, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+  if (factory == nullptr) factory = message_factory_;
+
+  if (field->is_extension()) {
+    return static_cast<Message*>(
+        MutableExtensionSet(message)->UnsafeArenaReleaseMessage(field,
+                                                                factory));
+  } else {
+    if (!(field->is_repeated() || schema_.InRealOneof(field))) {
+      ClearBit(message, field);
+    }
+    if (schema_.InRealOneof(field)) {
+      if (HasOneofField(*message, field)) {
+        *MutableOneofCase(message, field->containing_oneof()) = 0;
+      } else {
+        return nullptr;
+      }
+    }
+    Message** result = MutableRaw<Message*>(message, field);
+    Message* ret = *result;
+    *result = nullptr;
+    return ret;
+  }
+}
+
+Message* Reflection::ReleaseMessage(Message* message,
+                                    const FieldDescriptor* field,
+                                    MessageFactory* factory) const {
+  CheckInvalidAccess(schema_, field);
+
+  Message* released = UnsafeArenaReleaseMessage(message, field, factory);
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  released = MaybeForceCopy(message->GetArenaForAllocation(), released);
+#endif  // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (message->GetArenaForAllocation() != nullptr && released != nullptr) {
+    Message* copy_from_arena = released->New();
+    copy_from_arena->CopyFrom(*released);
+    released = copy_from_arena;
+  }
+  return released;
+}
+
+const Message& Reflection::GetRepeatedMessage(const Message& message,
+                                              const FieldDescriptor* field,
+                                              int index) const {
+  USAGE_CHECK_ALL(GetRepeatedMessage, REPEATED, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+  if (field->is_extension()) {
+    return static_cast<const Message&>(
+        GetExtensionSet(message).GetRepeatedMessage(field->number(), index));
+  } else {
+    if (IsMapFieldInApi(field)) {
+      return GetRaw<MapFieldBase>(message, field)
+          .GetRepeatedField()
+          .Get<GenericTypeHandler<Message> >(index);
+    } else {
+      return GetRaw<RepeatedPtrFieldBase>(message, field)
+          .Get<GenericTypeHandler<Message> >(index);
+    }
+  }
+}
+
+Message* Reflection::MutableRepeatedMessage(Message* message,
+                                            const FieldDescriptor* field,
+                                            int index) const {
+  USAGE_CHECK_ALL(MutableRepeatedMessage, REPEATED, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+  if (field->is_extension()) {
+    return static_cast<Message*>(
+        MutableExtensionSet(message)->MutableRepeatedMessage(field->number(),
+                                                             index));
+  } else {
+    if (IsMapFieldInApi(field)) {
+      return MutableRaw<MapFieldBase>(message, field)
+          ->MutableRepeatedField()
+          ->Mutable<GenericTypeHandler<Message> >(index);
+    } else {
+      return MutableRaw<RepeatedPtrFieldBase>(message, field)
+          ->Mutable<GenericTypeHandler<Message> >(index);
+    }
+  }
+}
+
+Message* Reflection::AddMessage(Message* message, const FieldDescriptor* field,
+                                MessageFactory* factory) const {
+  USAGE_CHECK_ALL(AddMessage, REPEATED, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+  if (factory == nullptr) factory = message_factory_;
+
+  if (field->is_extension()) {
+    return static_cast<Message*>(
+        MutableExtensionSet(message)->AddMessage(field, factory));
+  } else {
+    Message* result = nullptr;
+
+    // We can't use AddField<Message>() because RepeatedPtrFieldBase doesn't
+    // know how to allocate one.
+    RepeatedPtrFieldBase* repeated = nullptr;
+    if (IsMapFieldInApi(field)) {
+      repeated =
+          MutableRaw<MapFieldBase>(message, field)->MutableRepeatedField();
+    } else {
+      repeated = MutableRaw<RepeatedPtrFieldBase>(message, field);
+    }
+    result = repeated->AddFromCleared<GenericTypeHandler<Message> >();
+    if (result == nullptr) {
+      // We must allocate a new object.
+      const Message* prototype;
+      if (repeated->size() == 0) {
+        prototype = factory->GetPrototype(field->message_type());
+      } else {
+        prototype = &repeated->Get<GenericTypeHandler<Message> >(0);
+      }
+      result = prototype->New(message->GetArenaForAllocation());
+      // We can guarantee here that repeated and result are either both heap
+      // allocated or arena owned. So it is safe to call the unsafe version
+      // of AddAllocated.
+      repeated->UnsafeArenaAddAllocated<GenericTypeHandler<Message> >(result);
+    }
+
+    return result;
+  }
+}
+
+void Reflection::AddAllocatedMessage(Message* message,
+                                     const FieldDescriptor* field,
+                                     Message* new_entry) const {
+  USAGE_CHECK_ALL(AddAllocatedMessage, REPEATED, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->AddAllocatedMessage(field, new_entry);
+  } else {
+    RepeatedPtrFieldBase* repeated = nullptr;
+    if (IsMapFieldInApi(field)) {
+      repeated =
+          MutableRaw<MapFieldBase>(message, field)->MutableRepeatedField();
+    } else {
+      repeated = MutableRaw<RepeatedPtrFieldBase>(message, field);
+    }
+    repeated->AddAllocated<GenericTypeHandler<Message> >(new_entry);
+  }
+}
+
+void Reflection::UnsafeArenaAddAllocatedMessage(Message* message,
+                                                const FieldDescriptor* field,
+                                                Message* new_entry) const {
+  USAGE_CHECK_ALL(UnsafeArenaAddAllocatedMessage, REPEATED, MESSAGE);
+  CheckInvalidAccess(schema_, field);
+
+  if (field->is_extension()) {
+    MutableExtensionSet(message)->UnsafeArenaAddAllocatedMessage(field,
+                                                                 new_entry);
+  } else {
+    RepeatedPtrFieldBase* repeated = nullptr;
+    if (IsMapFieldInApi(field)) {
+      repeated =
+          MutableRaw<MapFieldBase>(message, field)->MutableRepeatedField();
+    } else {
+      repeated = MutableRaw<RepeatedPtrFieldBase>(message, field);
+    }
+    repeated->UnsafeArenaAddAllocated<GenericTypeHandler<Message>>(new_entry);
+  }
+}
+
+void* Reflection::MutableRawRepeatedField(Message* message,
+                                          const FieldDescriptor* field,
+                                          FieldDescriptor::CppType cpptype,
+                                          int ctype,
+                                          const Descriptor* desc) const {
+  (void)ctype;  // Parameter is used by Google-internal code.
+  USAGE_CHECK_REPEATED("MutableRawRepeatedField");
+  CheckInvalidAccess(schema_, field);
+
+  if (field->cpp_type() != cpptype &&
+      (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM ||
+       cpptype != FieldDescriptor::CPPTYPE_INT32))
+    ReportReflectionUsageTypeError(descriptor_, field,
+                                   "MutableRawRepeatedField", cpptype);
+  if (desc != nullptr)
+    GOOGLE_CHECK_EQ(field->message_type(), desc) << "wrong submessage type";
+  if (field->is_extension()) {
+    return MutableExtensionSet(message)->MutableRawRepeatedField(
+        field->number(), field->type(), field->is_packed(), field);
+  } else {
+    // Trigger transform for MapField
+    if (IsMapFieldInApi(field)) {
+      return MutableRawNonOneof<MapFieldBase>(message, field)
+          ->MutableRepeatedField();
+    }
+    return MutableRawNonOneof<void>(message, field);
+  }
+}
+
+const void* Reflection::GetRawRepeatedField(const Message& message,
+                                            const FieldDescriptor* field,
+                                            FieldDescriptor::CppType cpptype,
+                                            int ctype,
+                                            const Descriptor* desc) const {
+  USAGE_CHECK_REPEATED("GetRawRepeatedField");
+  if (field->cpp_type() != cpptype)
+    ReportReflectionUsageTypeError(descriptor_, field, "GetRawRepeatedField",
+                                   cpptype);
+  if (ctype >= 0)
+    GOOGLE_CHECK_EQ(field->options().ctype(), ctype) << "subtype mismatch";
+  if (desc != nullptr)
+    GOOGLE_CHECK_EQ(field->message_type(), desc) << "wrong submessage type";
+  if (field->is_extension()) {
+    // Should use extension_set::GetRawRepeatedField. However, the required
+    // parameter "default repeated value" is not very easy to get here.
+    // Map is not supported in extensions, it is acceptable to use
+    // extension_set::MutableRawRepeatedField which does not change the message.
+    return MutableExtensionSet(const_cast<Message*>(&message))
+        ->MutableRawRepeatedField(field->number(), field->type(),
+                                  field->is_packed(), field);
+  } else {
+    // Trigger transform for MapField
+    if (IsMapFieldInApi(field)) {
+      return &(GetRawNonOneof<MapFieldBase>(message, field).GetRepeatedField());
+    }
+    return &GetRawNonOneof<char>(message, field);
+  }
+}
+
+const FieldDescriptor* Reflection::GetOneofFieldDescriptor(
+    const Message& message, const OneofDescriptor* oneof_descriptor) const {
+  if (oneof_descriptor->is_synthetic()) {
+    const FieldDescriptor* field = oneof_descriptor->field(0);
+    return HasField(message, field) ? field : nullptr;
+  }
+  uint32_t field_number = GetOneofCase(message, oneof_descriptor);
+  if (field_number == 0) {
+    return nullptr;
+  }
+  return descriptor_->FindFieldByNumber(field_number);
+}
+
+bool Reflection::ContainsMapKey(const Message& message,
+                                const FieldDescriptor* field,
+                                const MapKey& key) const {
+  USAGE_CHECK(IsMapFieldInApi(field), "LookupMapValue",
+              "Field is not a map field.");
+  return GetRaw<MapFieldBase>(message, field).ContainsMapKey(key);
+}
+
+bool Reflection::InsertOrLookupMapValue(Message* message,
+                                        const FieldDescriptor* field,
+                                        const MapKey& key,
+                                        MapValueRef* val) const {
+  USAGE_CHECK(IsMapFieldInApi(field), "InsertOrLookupMapValue",
+              "Field is not a map field.");
+  val->SetType(field->message_type()->map_value()->cpp_type());
+  return MutableRaw<MapFieldBase>(message, field)
+      ->InsertOrLookupMapValue(key, val);
+}
+
+bool Reflection::LookupMapValue(const Message& message,
+                                const FieldDescriptor* field, const MapKey& key,
+                                MapValueConstRef* val) const {
+  USAGE_CHECK(IsMapFieldInApi(field), "LookupMapValue",
+              "Field is not a map field.");
+  val->SetType(field->message_type()->map_value()->cpp_type());
+  return GetRaw<MapFieldBase>(message, field).LookupMapValue(key, val);
+}
+
+bool Reflection::DeleteMapValue(Message* message, const FieldDescriptor* field,
+                                const MapKey& key) const {
+  USAGE_CHECK(IsMapFieldInApi(field), "DeleteMapValue",
+              "Field is not a map field.");
+  return MutableRaw<MapFieldBase>(message, field)->DeleteMapValue(key);
+}
+
+MapIterator Reflection::MapBegin(Message* message,
+                                 const FieldDescriptor* field) const {
+  USAGE_CHECK(IsMapFieldInApi(field), "MapBegin", "Field is not a map field.");
+  MapIterator iter(message, field);
+  GetRaw<MapFieldBase>(*message, field).MapBegin(&iter);
+  return iter;
+}
+
+MapIterator Reflection::MapEnd(Message* message,
+                               const FieldDescriptor* field) const {
+  USAGE_CHECK(IsMapFieldInApi(field), "MapEnd", "Field is not a map field.");
+  MapIterator iter(message, field);
+  GetRaw<MapFieldBase>(*message, field).MapEnd(&iter);
+  return iter;
+}
+
+int Reflection::MapSize(const Message& message,
+                        const FieldDescriptor* field) const {
+  USAGE_CHECK(IsMapFieldInApi(field), "MapSize", "Field is not a map field.");
+  return GetRaw<MapFieldBase>(message, field).size();
+}
+
+// -----------------------------------------------------------------------------
+
+const FieldDescriptor* Reflection::FindKnownExtensionByName(
+    const std::string& name) const {
+  if (!schema_.HasExtensionSet()) return nullptr;
+  return descriptor_pool_->FindExtensionByPrintableName(descriptor_, name);
+}
+
+const FieldDescriptor* Reflection::FindKnownExtensionByNumber(
+    int number) const {
+  if (!schema_.HasExtensionSet()) return nullptr;
+  return descriptor_pool_->FindExtensionByNumber(descriptor_, number);
+}
+
+bool Reflection::SupportsUnknownEnumValues() const {
+  return CreateUnknownEnumValues(descriptor_->file());
+}
+
+// ===================================================================
+// Some private helpers.
+
+// These simple template accessors obtain pointers (or references) to
+// the given field.
+
+template <class Type>
+const Type& Reflection::GetRawNonOneof(const Message& message,
+                                       const FieldDescriptor* field) const {
+  return GetConstRefAtOffset<Type>(message,
+                                   schema_.GetFieldOffsetNonOneof(field));
+}
+
+template <class Type>
+Type* Reflection::MutableRawNonOneof(Message* message,
+                                     const FieldDescriptor* field) const {
+  return GetPointerAtOffset<Type>(message,
+                                  schema_.GetFieldOffsetNonOneof(field));
+}
+
+template <typename Type>
+Type* Reflection::MutableRaw(Message* message,
+                             const FieldDescriptor* field) const {
+  return GetPointerAtOffset<Type>(message, schema_.GetFieldOffset(field));
+}
+
+const uint32_t* Reflection::GetHasBits(const Message& message) const {
+  GOOGLE_DCHECK(schema_.HasHasbits());
+  return &GetConstRefAtOffset<uint32_t>(message, schema_.HasBitsOffset());
+}
+
+uint32_t* Reflection::MutableHasBits(Message* message) const {
+  GOOGLE_DCHECK(schema_.HasHasbits());
+  return GetPointerAtOffset<uint32_t>(message, schema_.HasBitsOffset());
+}
+
+uint32_t* Reflection::MutableOneofCase(
+    Message* message, const OneofDescriptor* oneof_descriptor) const {
+  GOOGLE_DCHECK(!oneof_descriptor->is_synthetic());
+  return GetPointerAtOffset<uint32_t>(
+      message, schema_.GetOneofCaseOffset(oneof_descriptor));
+}
+
+const ExtensionSet& Reflection::GetExtensionSet(const Message& message) const {
+  return GetConstRefAtOffset<ExtensionSet>(message,
+                                           schema_.GetExtensionSetOffset());
+}
+
+ExtensionSet* Reflection::MutableExtensionSet(Message* message) const {
+  return GetPointerAtOffset<ExtensionSet>(message,
+                                          schema_.GetExtensionSetOffset());
+}
+
+const InternalMetadata& Reflection::GetInternalMetadata(
+    const Message& message) const {
+  return GetConstRefAtOffset<InternalMetadata>(message,
+                                               schema_.GetMetadataOffset());
+}
+
+InternalMetadata* Reflection::MutableInternalMetadata(Message* message) const {
+  return GetPointerAtOffset<InternalMetadata>(message,
+                                              schema_.GetMetadataOffset());
+}
+
+const uint32_t* Reflection::GetInlinedStringDonatedArray(
+    const Message& message) const {
+  GOOGLE_DCHECK(schema_.HasInlinedString());
+  return &GetConstRefAtOffset<uint32_t>(message,
+                                        schema_.InlinedStringDonatedOffset());
+}
+
+uint32_t* Reflection::MutableInlinedStringDonatedArray(Message* message) const {
+  GOOGLE_DCHECK(schema_.HasInlinedString());
+  return GetPointerAtOffset<uint32_t>(message,
+                                      schema_.InlinedStringDonatedOffset());
+}
+
+// Simple accessors for manipulating _inlined_string_donated_;
+bool Reflection::IsInlinedStringDonated(const Message& message,
+                                        const FieldDescriptor* field) const {
+  uint32_t index = schema_.InlinedStringIndex(field);
+  GOOGLE_DCHECK_GT(index, 0);
+  return IsIndexInHasBitSet(GetInlinedStringDonatedArray(message), index);
+}
+
+inline void SetInlinedStringDonated(uint32_t index, uint32_t* array) {
+  array[index / 32] |= (static_cast<uint32_t>(1) << (index % 32));
+}
+
+inline void ClearInlinedStringDonated(uint32_t index, uint32_t* array) {
+  array[index / 32] &= ~(static_cast<uint32_t>(1) << (index % 32));
+}
+
+void Reflection::SwapInlinedStringDonated(Message* lhs, Message* rhs,
+                                          const FieldDescriptor* field) const {
+  Arena* lhs_arena = lhs->GetArenaForAllocation();
+  Arena* rhs_arena = rhs->GetArenaForAllocation();
+  // If arenas differ, inined string fields are swapped by copying values.
+  // Donation status should not be swapped.
+  if (lhs_arena != rhs_arena) {
+    return;
+  }
+  bool lhs_donated = IsInlinedStringDonated(*lhs, field);
+  bool rhs_donated = IsInlinedStringDonated(*rhs, field);
+  if (lhs_donated == rhs_donated) {
+    return;
+  }
+  // If one is undonated, both must have already registered ArenaDtor.
+  uint32_t* lhs_array = MutableInlinedStringDonatedArray(lhs);
+  uint32_t* rhs_array = MutableInlinedStringDonatedArray(rhs);
+  GOOGLE_CHECK_EQ(lhs_array[0] & 0x1u, 0u);
+  GOOGLE_CHECK_EQ(rhs_array[0] & 0x1u, 0u);
+  // Swap donation status bit.
+  uint32_t index = schema_.InlinedStringIndex(field);
+  GOOGLE_DCHECK_GT(index, 0);
+  if (rhs_donated) {
+    SetInlinedStringDonated(index, lhs_array);
+    ClearInlinedStringDonated(index, rhs_array);
+  } else {  // lhs_donated
+    ClearInlinedStringDonated(index, lhs_array);
+    SetInlinedStringDonated(index, rhs_array);
+  }
+}
+
+// Simple accessors for manipulating has_bits_.
+bool Reflection::HasBit(const Message& message,
+                        const FieldDescriptor* field) const {
+  GOOGLE_DCHECK(!field->options().weak());
+  if (schema_.HasBitIndex(field) != static_cast<uint32_t>(-1)) {
+    return IsIndexInHasBitSet(GetHasBits(message), schema_.HasBitIndex(field));
+  }
+
+  // Intentionally check here because HasBitIndex(field) != -1 means valid.
+  CheckInvalidAccess(schema_, field);
+
+  // proto3: no has-bits. All fields present except messages, which are
+  // present only if their message-field pointer is non-null.
+  if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+    return !schema_.IsDefaultInstance(message) &&
+           GetRaw<const Message*>(message, field) != nullptr;
+  } else {
+    // Non-message field (and non-oneof, since that was handled in HasField()
+    // before calling us), and singular (again, checked in HasField). So, this
+    // field must be a scalar.
+
+    // Scalar primitive (numeric or string/bytes) fields are present if
+    // their value is non-zero (numeric) or non-empty (string/bytes). N.B.:
+    // we must use this definition here, rather than the "scalar fields
+    // always present" in the proto3 docs, because MergeFrom() semantics
+    // require presence as "present on wire", and reflection-based merge
+    // (which uses HasField()) needs to be consistent with this.
+    switch (field->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_STRING:
+        switch (field->options().ctype()) {
+          default: {
+            if (IsInlined(field)) {
+              return !GetField<InlinedStringField>(message, field)
+                          .GetNoArena()
+                          .empty();
+            }
+
+            return GetField<ArenaStringPtr>(message, field).Get().size() > 0;
+          }
+        }
+        return false;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        return GetRaw<bool>(message, field) != false;
+      case FieldDescriptor::CPPTYPE_INT32:
+        return GetRaw<int32_t>(message, field) != 0;
+      case FieldDescriptor::CPPTYPE_INT64:
+        return GetRaw<int64_t>(message, field) != 0;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        return GetRaw<uint32_t>(message, field) != 0;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        return GetRaw<uint64_t>(message, field) != 0;
+      case FieldDescriptor::CPPTYPE_FLOAT:
+        static_assert(sizeof(uint32_t) == sizeof(float),
+                      "Code assumes uint32_t and float are the same size.");
+        return GetRaw<uint32_t>(message, field) != 0;
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+        static_assert(sizeof(uint64_t) == sizeof(double),
+                      "Code assumes uint64_t and double are the same size.");
+        return GetRaw<uint64_t>(message, field) != 0;
+      case FieldDescriptor::CPPTYPE_ENUM:
+        return GetRaw<int>(message, field) != 0;
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        // handled above; avoid warning
+        break;
+    }
+    GOOGLE_LOG(FATAL) << "Reached impossible case in HasBit().";
+    return false;
+  }
+}
+
+void Reflection::SetBit(Message* message, const FieldDescriptor* field) const {
+  GOOGLE_DCHECK(!field->options().weak());
+  const uint32_t index = schema_.HasBitIndex(field);
+  if (index == static_cast<uint32_t>(-1)) return;
+  MutableHasBits(message)[index / 32] |=
+      (static_cast<uint32_t>(1) << (index % 32));
+}
+
+void Reflection::ClearBit(Message* message,
+                          const FieldDescriptor* field) const {
+  GOOGLE_DCHECK(!field->options().weak());
+  const uint32_t index = schema_.HasBitIndex(field);
+  if (index == static_cast<uint32_t>(-1)) return;
+  MutableHasBits(message)[index / 32] &=
+      ~(static_cast<uint32_t>(1) << (index % 32));
+}
+
+void Reflection::SwapBit(Message* message1, Message* message2,
+                         const FieldDescriptor* field) const {
+  GOOGLE_DCHECK(!field->options().weak());
+  if (!schema_.HasHasbits()) {
+    return;
+  }
+  bool temp_has_bit = HasBit(*message1, field);
+  if (HasBit(*message2, field)) {
+    SetBit(message1, field);
+  } else {
+    ClearBit(message1, field);
+  }
+  if (temp_has_bit) {
+    SetBit(message2, field);
+  } else {
+    ClearBit(message2, field);
+  }
+}
+
+bool Reflection::HasOneof(const Message& message,
+                          const OneofDescriptor* oneof_descriptor) const {
+  if (oneof_descriptor->is_synthetic()) {
+    return HasField(message, oneof_descriptor->field(0));
+  }
+  return (GetOneofCase(message, oneof_descriptor) > 0);
+}
+
+void Reflection::SetOneofCase(Message* message,
+                              const FieldDescriptor* field) const {
+  *MutableOneofCase(message, field->containing_oneof()) = field->number();
+}
+
+void Reflection::ClearOneofField(Message* message,
+                                 const FieldDescriptor* field) const {
+  if (HasOneofField(*message, field)) {
+    ClearOneof(message, field->containing_oneof());
+  }
+}
+
+void Reflection::ClearOneof(Message* message,
+                            const OneofDescriptor* oneof_descriptor) const {
+  if (oneof_descriptor->is_synthetic()) {
+    ClearField(message, oneof_descriptor->field(0));
+    return;
+  }
+  // TODO(jieluo): Consider to cache the unused object instead of deleting
+  // it. It will be much faster if an application switches a lot from
+  // a few oneof fields.  Time/space tradeoff
+  uint32_t oneof_case = GetOneofCase(*message, oneof_descriptor);
+  if (oneof_case > 0) {
+    const FieldDescriptor* field = descriptor_->FindFieldByNumber(oneof_case);
+    if (message->GetArenaForAllocation() == nullptr) {
+      switch (field->cpp_type()) {
+        case FieldDescriptor::CPPTYPE_STRING: {
+          switch (field->options().ctype()) {
+            default:  // TODO(kenton):  Support other string reps.
+            case FieldOptions::STRING: {
+              // Oneof string fields are never set as a default instance.
+              // We just need to pass some arbitrary default string to make it
+              // work. This allows us to not have the real default accessible
+              // from reflection.
+              MutableField<ArenaStringPtr>(message, field)->Destroy();
+              break;
+            }
+          }
+          break;
+        }
+
+        case FieldDescriptor::CPPTYPE_MESSAGE:
+          delete *MutableRaw<Message*>(message, field);
+          break;
+        default:
+          break;
+      }
+    } else {
+    }
+
+    *MutableOneofCase(message, oneof_descriptor) = 0;
+  }
+}
+
+#define HANDLE_TYPE(TYPE, CPPTYPE, CTYPE)                                  \
+  template <>                                                              \
+  const RepeatedField<TYPE>& Reflection::GetRepeatedFieldInternal<TYPE>(   \
+      const Message& message, const FieldDescriptor* field) const {        \
+    return *static_cast<RepeatedField<TYPE>*>(MutableRawRepeatedField(     \
+        const_cast<Message*>(&message), field, CPPTYPE, CTYPE, nullptr));  \
+  }                                                                        \
+                                                                           \
+  template <>                                                              \
+  RepeatedField<TYPE>* Reflection::MutableRepeatedFieldInternal<TYPE>(     \
+      Message * message, const FieldDescriptor* field) const {             \
+    return static_cast<RepeatedField<TYPE>*>(                              \
+        MutableRawRepeatedField(message, field, CPPTYPE, CTYPE, nullptr)); \
+  }
+
+HANDLE_TYPE(int32_t, FieldDescriptor::CPPTYPE_INT32, -1);
+HANDLE_TYPE(int64_t, FieldDescriptor::CPPTYPE_INT64, -1);
+HANDLE_TYPE(uint32_t, FieldDescriptor::CPPTYPE_UINT32, -1);
+HANDLE_TYPE(uint64_t, FieldDescriptor::CPPTYPE_UINT64, -1);
+HANDLE_TYPE(float, FieldDescriptor::CPPTYPE_FLOAT, -1);
+HANDLE_TYPE(double, FieldDescriptor::CPPTYPE_DOUBLE, -1);
+HANDLE_TYPE(bool, FieldDescriptor::CPPTYPE_BOOL, -1);
+
+
+#undef HANDLE_TYPE
+
+void* Reflection::MutableRawRepeatedString(Message* message,
+                                           const FieldDescriptor* field,
+                                           bool is_string) const {
+  (void)is_string;  // Parameter is used by Google-internal code.
+  return MutableRawRepeatedField(message, field,
+                                 FieldDescriptor::CPPTYPE_STRING,
+                                 FieldOptions::STRING, nullptr);
+}
+
+// Template implementations of basic accessors.  Inline because each
+// template instance is only called from one location.  These are
+// used for all types except messages.
+template <typename Type>
+const Type& Reflection::GetField(const Message& message,
+                                 const FieldDescriptor* field) const {
+  return GetRaw<Type>(message, field);
+}
+
+template <typename Type>
+void Reflection::SetField(Message* message, const FieldDescriptor* field,
+                          const Type& value) const {
+  bool real_oneof = schema_.InRealOneof(field);
+  if (real_oneof && !HasOneofField(*message, field)) {
+    ClearOneof(message, field->containing_oneof());
+  }
+  *MutableRaw<Type>(message, field) = value;
+  real_oneof ? SetOneofCase(message, field) : SetBit(message, field);
+}
+
+template <typename Type>
+Type* Reflection::MutableField(Message* message,
+                               const FieldDescriptor* field) const {
+  schema_.InRealOneof(field) ? SetOneofCase(message, field)
+                             : SetBit(message, field);
+  return MutableRaw<Type>(message, field);
+}
+
+template <typename Type>
+const Type& Reflection::GetRepeatedField(const Message& message,
+                                         const FieldDescriptor* field,
+                                         int index) const {
+  return GetRaw<RepeatedField<Type> >(message, field).Get(index);
+}
+
+template <typename Type>
+const Type& Reflection::GetRepeatedPtrField(const Message& message,
+                                            const FieldDescriptor* field,
+                                            int index) const {
+  return GetRaw<RepeatedPtrField<Type> >(message, field).Get(index);
+}
+
+template <typename Type>
+void Reflection::SetRepeatedField(Message* message,
+                                  const FieldDescriptor* field, int index,
+                                  Type value) const {
+  MutableRaw<RepeatedField<Type> >(message, field)->Set(index, value);
+}
+
+template <typename Type>
+Type* Reflection::MutableRepeatedField(Message* message,
+                                       const FieldDescriptor* field,
+                                       int index) const {
+  RepeatedPtrField<Type>* repeated =
+      MutableRaw<RepeatedPtrField<Type> >(message, field);
+  return repeated->Mutable(index);
+}
+
+template <typename Type>
+void Reflection::AddField(Message* message, const FieldDescriptor* field,
+                          const Type& value) const {
+  MutableRaw<RepeatedField<Type> >(message, field)->Add(value);
+}
+
+template <typename Type>
+Type* Reflection::AddField(Message* message,
+                           const FieldDescriptor* field) const {
+  RepeatedPtrField<Type>* repeated =
+      MutableRaw<RepeatedPtrField<Type> >(message, field);
+  return repeated->Add();
+}
+
+MessageFactory* Reflection::GetMessageFactory() const {
+  return message_factory_;
+}
+
+void* Reflection::RepeatedFieldData(Message* message,
+                                    const FieldDescriptor* field,
+                                    FieldDescriptor::CppType cpp_type,
+                                    const Descriptor* message_type) const {
+  GOOGLE_CHECK(field->is_repeated());
+  GOOGLE_CHECK(field->cpp_type() == cpp_type ||
+        (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
+         cpp_type == FieldDescriptor::CPPTYPE_INT32))
+      << "The type parameter T in RepeatedFieldRef<T> API doesn't match "
+      << "the actual field type (for enums T should be the generated enum "
+      << "type or int32_t).";
+  if (message_type != nullptr) {
+    GOOGLE_CHECK_EQ(message_type, field->message_type());
+  }
+  if (field->is_extension()) {
+    return MutableExtensionSet(message)->MutableRawRepeatedField(
+        field->number(), field->type(), field->is_packed(), field);
+  } else {
+    return MutableRawNonOneof<char>(message, field);
+  }
+}
+
+MapFieldBase* Reflection::MutableMapData(Message* message,
+                                         const FieldDescriptor* field) const {
+  USAGE_CHECK(IsMapFieldInApi(field), "GetMapData",
+              "Field is not a map field.");
+  return MutableRaw<MapFieldBase>(message, field);
+}
+
+const MapFieldBase* Reflection::GetMapData(const Message& message,
+                                           const FieldDescriptor* field) const {
+  USAGE_CHECK(IsMapFieldInApi(field), "GetMapData",
+              "Field is not a map field.");
+  return &(GetRaw<MapFieldBase>(message, field));
+}
+
+namespace {
+
+// Helper function to transform migration schema into reflection schema.
+ReflectionSchema MigrationToReflectionSchema(
+    const Message* const* default_instance, const uint32_t* offsets,
+    MigrationSchema migration_schema) {
+  ReflectionSchema result;
+  result.default_instance_ = *default_instance;
+  // First 7 offsets are offsets to the special fields. The following offsets
+  // are the proto fields.
+  result.offsets_ = offsets + migration_schema.offsets_index + 6;
+  result.has_bit_indices_ = offsets + migration_schema.has_bit_indices_index;
+  result.has_bits_offset_ = offsets[migration_schema.offsets_index + 0];
+  result.metadata_offset_ = offsets[migration_schema.offsets_index + 1];
+  result.extensions_offset_ = offsets[migration_schema.offsets_index + 2];
+  result.oneof_case_offset_ = offsets[migration_schema.offsets_index + 3];
+  result.object_size_ = migration_schema.object_size;
+  result.weak_field_map_offset_ = offsets[migration_schema.offsets_index + 4];
+  result.inlined_string_donated_offset_ =
+      offsets[migration_schema.offsets_index + 5];
+  result.inlined_string_indices_ =
+      offsets + migration_schema.inlined_string_indices_index;
+  return result;
+}
+
+}  // namespace
+
+class AssignDescriptorsHelper {
+ public:
+  AssignDescriptorsHelper(MessageFactory* factory,
+                          Metadata* file_level_metadata,
+                          const EnumDescriptor** file_level_enum_descriptors,
+                          const MigrationSchema* schemas,
+                          const Message* const* default_instance_data,
+                          const uint32_t* offsets)
+      : factory_(factory),
+        file_level_metadata_(file_level_metadata),
+        file_level_enum_descriptors_(file_level_enum_descriptors),
+        schemas_(schemas),
+        default_instance_data_(default_instance_data),
+        offsets_(offsets) {}
+
+  void AssignMessageDescriptor(const Descriptor* descriptor) {
+    for (int i = 0; i < descriptor->nested_type_count(); i++) {
+      AssignMessageDescriptor(descriptor->nested_type(i));
+    }
+
+    file_level_metadata_->descriptor = descriptor;
+
+    file_level_metadata_->reflection =
+        new Reflection(descriptor,
+                       MigrationToReflectionSchema(default_instance_data_,
+                                                   offsets_, *schemas_),
+                       DescriptorPool::internal_generated_pool(), factory_);
+    for (int i = 0; i < descriptor->enum_type_count(); i++) {
+      AssignEnumDescriptor(descriptor->enum_type(i));
+    }
+    schemas_++;
+    default_instance_data_++;
+    file_level_metadata_++;
+  }
+
+  void AssignEnumDescriptor(const EnumDescriptor* descriptor) {
+    *file_level_enum_descriptors_ = descriptor;
+    file_level_enum_descriptors_++;
+  }
+
+  const Metadata* GetCurrentMetadataPtr() const { return file_level_metadata_; }
+
+ private:
+  MessageFactory* factory_;
+  Metadata* file_level_metadata_;
+  const EnumDescriptor** file_level_enum_descriptors_;
+  const MigrationSchema* schemas_;
+  const Message* const* default_instance_data_;
+  const uint32_t* offsets_;
+};
+
+namespace {
+
+// We have the routines that assign descriptors and build reflection
+// automatically delete the allocated reflection. MetadataOwner owns
+// all the allocated reflection instances.
+struct MetadataOwner {
+  ~MetadataOwner() {
+    for (auto range : metadata_arrays_) {
+      for (const Metadata* m = range.first; m < range.second; m++) {
+        delete m->reflection;
+      }
+    }
+  }
+
+  void AddArray(const Metadata* begin, const Metadata* end) {
+    mu_.Lock();
+    metadata_arrays_.push_back(std::make_pair(begin, end));
+    mu_.Unlock();
+  }
+
+  static MetadataOwner* Instance() {
+    static MetadataOwner* res = OnShutdownDelete(new MetadataOwner);
+    return res;
+  }
+
+ private:
+  MetadataOwner() = default;  // private because singleton
+
+  WrappedMutex mu_;
+  std::vector<std::pair<const Metadata*, const Metadata*> > metadata_arrays_;
+};
+
+void AddDescriptors(const DescriptorTable* table);
+
+void AssignDescriptorsImpl(const DescriptorTable* table, bool eager) {
+  // Ensure the file descriptor is added to the pool.
+  {
+    // This only happens once per proto file. So a global mutex to serialize
+    // calls to AddDescriptors.
+    static WrappedMutex mu{GOOGLE_PROTOBUF_LINKER_INITIALIZED};
+    mu.Lock();
+    AddDescriptors(table);
+    mu.Unlock();
+  }
+  if (eager) {
+    // Normally we do not want to eagerly build descriptors of our deps.
+    // However if this proto is optimized for code size (ie using reflection)
+    // and it has a message extending a custom option of a descriptor with that
+    // message being optimized for code size as well. Building the descriptors
+    // in this file requires parsing the serialized file descriptor, which now
+    // requires parsing the message extension, which potentially requires
+    // building the descriptor of the message extending one of the options.
+    // However we are already updating descriptor pool under a lock. To prevent
+    // this the compiler statically looks for this case and we just make sure we
+    // first build the descriptors of all our dependencies, preventing the
+    // deadlock.
+    int num_deps = table->num_deps;
+    for (int i = 0; i < num_deps; i++) {
+      // In case of weak fields deps[i] could be null.
+      if (table->deps[i]) AssignDescriptors(table->deps[i], true);
+    }
+  }
+
+  // Fill the arrays with pointers to descriptors and reflection classes.
+  const FileDescriptor* file =
+      DescriptorPool::internal_generated_pool()->FindFileByName(
+          table->filename);
+  GOOGLE_CHECK(file != nullptr);
+
+  MessageFactory* factory = MessageFactory::generated_factory();
+
+  AssignDescriptorsHelper helper(
+      factory, table->file_level_metadata, table->file_level_enum_descriptors,
+      table->schemas, table->default_instances, table->offsets);
+
+  for (int i = 0; i < file->message_type_count(); i++) {
+    helper.AssignMessageDescriptor(file->message_type(i));
+  }
+
+  for (int i = 0; i < file->enum_type_count(); i++) {
+    helper.AssignEnumDescriptor(file->enum_type(i));
+  }
+  if (file->options().cc_generic_services()) {
+    for (int i = 0; i < file->service_count(); i++) {
+      table->file_level_service_descriptors[i] = file->service(i);
+    }
+  }
+  MetadataOwner::Instance()->AddArray(table->file_level_metadata,
+                                      helper.GetCurrentMetadataPtr());
+}
+
+void AddDescriptorsImpl(const DescriptorTable* table) {
+  // Reflection refers to the default fields so make sure they are initialized.
+  internal::InitProtobufDefaults();
+
+  // Ensure all dependent descriptors are registered to the generated descriptor
+  // pool and message factory.
+  int num_deps = table->num_deps;
+  for (int i = 0; i < num_deps; i++) {
+    // In case of weak fields deps[i] could be null.
+    if (table->deps[i]) AddDescriptors(table->deps[i]);
+  }
+
+  // Register the descriptor of this file.
+  DescriptorPool::InternalAddGeneratedFile(table->descriptor, table->size);
+  MessageFactory::InternalRegisterGeneratedFile(table);
+}
+
+void AddDescriptors(const DescriptorTable* table) {
+  // AddDescriptors is not thread safe. Callers need to ensure calls are
+  // properly serialized. This function is only called pre-main by global
+  // descriptors and we can assume single threaded access or it's called
+  // by AssignDescriptorImpl which uses a mutex to sequence calls.
+  if (table->is_initialized) return;
+  table->is_initialized = true;
+  AddDescriptorsImpl(table);
+}
+
+}  // namespace
+
+// Separate function because it needs to be a friend of
+// Reflection
+void RegisterAllTypesInternal(const Metadata* file_level_metadata, int size) {
+  for (int i = 0; i < size; i++) {
+    const Reflection* reflection = file_level_metadata[i].reflection;
+    MessageFactory::InternalRegisterGeneratedMessage(
+        file_level_metadata[i].descriptor,
+        reflection->schema_.default_instance_);
+  }
+}
+
+namespace internal {
+
+Metadata AssignDescriptors(const DescriptorTable* (*table)(),
+                           internal::once_flag* once,
+                           const Metadata& metadata) {
+  call_once(*once, [=] {
+    auto* t = table();
+    AssignDescriptorsImpl(t, t->is_eager);
+  });
+
+  return metadata;
+}
+
+void AssignDescriptors(const DescriptorTable* table, bool eager) {
+  if (!eager) eager = table->is_eager;
+  call_once(*table->once, AssignDescriptorsImpl, table, eager);
+}
+
+AddDescriptorsRunner::AddDescriptorsRunner(const DescriptorTable* table) {
+  AddDescriptors(table);
+}
+
+void RegisterFileLevelMetadata(const DescriptorTable* table) {
+  AssignDescriptors(table);
+  RegisterAllTypesInternal(table->file_level_metadata, table->num_messages);
+}
+
+void UnknownFieldSetSerializer(const uint8_t* base, uint32_t offset,
+                               uint32_t /*tag*/, uint32_t /*has_offset*/,
+                               io::CodedOutputStream* output) {
+  const void* ptr = base + offset;
+  const InternalMetadata* metadata = static_cast<const InternalMetadata*>(ptr);
+  if (metadata->have_unknown_fields()) {
+    metadata->unknown_fields<UnknownFieldSet>(UnknownFieldSet::default_instance)
+        .SerializeToCodedStream(output);
+  }
+}
+
+bool IsDescendant(Message& root, const Message& message) {
+  const Reflection* reflection = root.GetReflection();
+  std::vector<const FieldDescriptor*> fields;
+  reflection->ListFieldsOmitStripped(root, &fields);
+
+  for (const auto* field : fields) {
+    // Skip non-message fields.
+    if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) continue;
+
+    // Optional messages.
+    if (!field->is_repeated()) {
+      Message* sub_message = reflection->MutableMessage(&root, field);
+      if (sub_message == &message || IsDescendant(*sub_message, message)) {
+        return true;
+      }
+      continue;
+    }
+
+    // Repeated messages.
+    if (!IsMapFieldInApi(field)) {
+      int count = reflection->FieldSize(root, field);
+      for (int i = 0; i < count; i++) {
+        Message* sub_message =
+            reflection->MutableRepeatedMessage(&root, field, i);
+        if (sub_message == &message || IsDescendant(*sub_message, message)) {
+          return true;
+        }
+      }
+      continue;
+    }
+
+    // Map field: if accessed as repeated fields, messages are *copied* and
+    // matching pointer won't work. Must directly access map.
+    constexpr int kValIdx = 1;
+    const FieldDescriptor* val_field = field->message_type()->field(kValIdx);
+    // Skip map fields whose value type is not message.
+    if (val_field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) continue;
+
+    MapIterator end = reflection->MapEnd(&root, field);
+    for (auto iter = reflection->MapBegin(&root, field); iter != end; ++iter) {
+      Message* sub_message = iter.MutableValueRef()->MutableMessageValue();
+      if (sub_message == &message || IsDescendant(*sub_message, message)) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_tctable_full.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_tctable_full.cpp
new file mode 100644
index 0000000..b77bb8d
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_tctable_full.cpp
@@ -0,0 +1,53 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <cstdint>
+
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/generated_message_tctable_impl.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/unknown_field_set.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+const char* TcParser::GenericFallback(PROTOBUF_TC_PARAM_DECL) {
+  return GenericFallbackImpl<Message, UnknownFieldSet>(PROTOBUF_TC_PARAM_PASS);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_tctable_lite.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_tctable_lite.cpp
new file mode 100644
index 0000000..23557a6
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_tctable_lite.cpp
@@ -0,0 +1,1863 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <cstdint>
+#include <numeric>
+
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/generated_message_tctable_decl.h>
+#include <google/protobuf/generated_message_tctable_impl.h>
+#include <google/protobuf/inlined_string_field.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+using FieldEntry = TcParseTableBase::FieldEntry;
+
+//////////////////////////////////////////////////////////////////////////////
+// Template instantiations:
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef NDEBUG
+template void AlignFail<4>(uintptr_t);
+template void AlignFail<8>(uintptr_t);
+#endif
+
+const char* TcParser::GenericFallbackLite(PROTOBUF_TC_PARAM_DECL) {
+  return GenericFallbackImpl<MessageLite, std::string>(PROTOBUF_TC_PARAM_PASS);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Core fast parsing implementation:
+//////////////////////////////////////////////////////////////////////////////
+
+class TcParser::ScopedArenaSwap final {
+ public:
+  ScopedArenaSwap(MessageLite* msg, ParseContext* ctx)
+      : ctx_(ctx), saved_(ctx->data().arena) {
+    ctx_->data().arena = msg->GetArenaForAllocation();
+  }
+  ScopedArenaSwap(const ScopedArenaSwap&) = delete;
+  ~ScopedArenaSwap() { ctx_->data().arena = saved_; }
+
+ private:
+  ParseContext* const ctx_;
+  Arena* const saved_;
+};
+
+PROTOBUF_NOINLINE const char* TcParser::ParseLoop(
+    MessageLite* msg, const char* ptr, ParseContext* ctx,
+    const TcParseTableBase* table) {
+  ScopedArenaSwap saved(msg, ctx);
+  while (!ctx->Done(&ptr)) {
+    // Unconditionally read has bits, even if we don't have has bits.
+    // has_bits_offset will be 0 and we will just read something valid.
+    uint64_t hasbits = ReadAt<uint32_t>(msg, table->has_bits_offset);
+    ptr = TagDispatch(msg, ptr, ctx, table, hasbits, {});
+    if (ptr == nullptr) break;
+    if (ctx->LastTag() != 1) break;  // Ended on terminating tag
+  }
+  return ptr;
+}
+
+  // Dispatch to the designated parse function
+inline PROTOBUF_ALWAYS_INLINE const char* TcParser::TagDispatch(
+    PROTOBUF_TC_PARAM_DECL) {
+  const auto coded_tag = UnalignedLoad<uint16_t>(ptr);
+  const size_t idx = coded_tag & table->fast_idx_mask;
+  PROTOBUF_ASSUME((idx & 7) == 0);
+  auto* fast_entry = table->fast_entry(idx >> 3);
+  data = fast_entry->bits;
+  data.data ^= coded_tag;
+  PROTOBUF_MUSTTAIL return fast_entry->target(PROTOBUF_TC_PARAM_PASS);
+}
+
+// We can only safely call from field to next field if the call is optimized
+// to a proper tail call. Otherwise we blow through stack. Clang and gcc
+// reliably do this optimization in opt mode, but do not perform this in debug
+// mode. Luckily the structure of the algorithm is such that it's always
+// possible to just return and use the enclosing parse loop as a trampoline.
+inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ToTagDispatch(
+    PROTOBUF_TC_PARAM_DECL) {
+  constexpr bool always_return = !PROTOBUF_TAILCALL;
+  if (always_return || !ctx->DataAvailable(ptr)) {
+    PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+  }
+  PROTOBUF_MUSTTAIL return TagDispatch(PROTOBUF_TC_PARAM_PASS);
+}
+
+inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ToParseLoop(
+    PROTOBUF_TC_PARAM_DECL) {
+  (void)data;
+  (void)ctx;
+  SyncHasbits(msg, hasbits, table);
+  return ptr;
+}
+
+inline PROTOBUF_ALWAYS_INLINE const char* TcParser::Error(
+    PROTOBUF_TC_PARAM_DECL) {
+  (void)data;
+  (void)ctx;
+  (void)ptr;
+  SyncHasbits(msg, hasbits, table);
+  return nullptr;
+}
+
+// On the fast path, a (matching) 1-byte tag already has the decoded value.
+static uint32_t FastDecodeTag(uint8_t coded_tag) {
+  return coded_tag;
+}
+
+// On the fast path, a (matching) 2-byte tag always needs to be decoded.
+static uint32_t FastDecodeTag(uint16_t coded_tag) {
+  uint32_t result = coded_tag;
+  result += static_cast<int8_t>(coded_tag);
+  return result >> 1;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Core mini parsing implementation:
+//////////////////////////////////////////////////////////////////////////////
+
+// Field lookup table layout:
+//
+// Because it consists of a series of variable-length segments, the lookuup
+// table is organized within an array of uint16_t, and each element is either
+// a uint16_t or a uint32_t stored little-endian as a pair of uint16_t.
+//
+// Its fundamental building block maps 16 contiguously ascending field numbers
+// to their locations within the field entry table:
+
+struct SkipEntry16 {
+  uint16_t skipmap;
+  uint16_t field_entry_offset;
+};
+
+// The skipmap is a bitfield of which of those field numbers do NOT have a
+// field entry.  The lowest bit of the skipmap corresponds to the lowest of
+// the 16 field numbers, so if a proto had only fields 1, 2, 3, and 7, the
+// skipmap would contain 0b11111111'10111000.
+//
+// The field lookup table begins with a single 32-bit skipmap that maps the
+// field numbers 1 through 32.  This is because the majority of proto
+// messages only contain fields numbered 1 to 32.
+//
+// The rest of the lookup table is a repeated series of
+// { 32-bit field #,  #SkipEntry16s,  {SkipEntry16...} }
+// That is, the next thing is a pair of uint16_t that form the next
+// lowest field number that the lookup table handles.  If this number is -1,
+// that is the end of the table.  Then there is a uint16_t that is
+// the number of contiguous SkipEntry16 entries that follow, and then of
+// course the SkipEntry16s themselves.
+
+// Originally developed and tested at https://godbolt.org/z/vbc7enYcf
+
+// Returns the address of the field for `tag` in the table's field entries.
+// Returns nullptr if the field was not found.
+const TcParseTableBase::FieldEntry* TcParser::FindFieldEntry(
+    const TcParseTableBase* table, uint32_t field_num) {
+  const FieldEntry* const field_entries = table->field_entries_begin();
+
+  uint32_t fstart = 1;
+  uint32_t adj_fnum = field_num - fstart;
+
+  if (PROTOBUF_PREDICT_TRUE(adj_fnum < 32)) {
+    uint32_t skipmap = table->skipmap32;
+    uint32_t skipbit = 1 << adj_fnum;
+    if (PROTOBUF_PREDICT_FALSE(skipmap & skipbit)) return nullptr;
+    skipmap &= skipbit - 1;
+#if (__GNUC__ || __clang__) && __POPCNT__
+    // Note: here and below, skipmap typically has very few set bits
+    // (31 in the worst case, but usually zero) so a loop isn't that
+    // bad, and a compiler-generated popcount is typically only
+    // worthwhile if the processor itself has hardware popcount support.
+    adj_fnum -= __builtin_popcount(skipmap);
+#else
+    while (skipmap) {
+      --adj_fnum;
+      skipmap &= skipmap - 1;
+    }
+#endif
+    auto* entry = field_entries + adj_fnum;
+    PROTOBUF_ASSUME(entry != nullptr);
+    return entry;
+  }
+  const uint16_t* lookup_table = table->field_lookup_begin();
+  for (;;) {
+#ifdef PROTOBUF_LITTLE_ENDIAN
+    memcpy(&fstart, lookup_table, sizeof(fstart));
+#else
+    fstart = lookup_table[0] | (lookup_table[1] << 16);
+#endif
+    lookup_table += sizeof(fstart) / sizeof(*lookup_table);
+    uint32_t num_skip_entries = *lookup_table++;
+    if (field_num < fstart) return nullptr;
+    adj_fnum = field_num - fstart;
+    uint32_t skip_num = adj_fnum / 16;
+    if (PROTOBUF_PREDICT_TRUE(skip_num < num_skip_entries)) {
+      // for each group of 16 fields we have:
+      // a bitmap of 16 bits
+      // a 16-bit field-entry offset for the first of them.
+      auto* skip_data = lookup_table + (adj_fnum / 16) * (sizeof(SkipEntry16) /
+                                                          sizeof(uint16_t));
+      SkipEntry16 se = {skip_data[0], skip_data[1]};
+      adj_fnum &= 15;
+      uint32_t skipmap = se.skipmap;
+      uint16_t skipbit = 1 << adj_fnum;
+      if (PROTOBUF_PREDICT_FALSE(skipmap & skipbit)) return nullptr;
+      skipmap &= skipbit - 1;
+      adj_fnum += se.field_entry_offset;
+#if (__GNUC__ || __clang__) && __POPCNT__
+      adj_fnum -= __builtin_popcount(skipmap);
+#else
+      while (skipmap) {
+        --adj_fnum;
+        skipmap &= skipmap - 1;
+      }
+#endif
+      auto* entry = field_entries + adj_fnum;
+      PROTOBUF_ASSUME(entry != nullptr);
+      return entry;
+    }
+    lookup_table +=
+        num_skip_entries * (sizeof(SkipEntry16) / sizeof(*lookup_table));
+  }
+}
+
+// Field names are stored in a format of:
+//
+// 1) A table of name sizes, one byte each, from 1 to 255 per name.
+//    `entries` is the size of this first table.
+// 1a) padding bytes, so the table of name sizes is a multiple of
+//     eight bytes in length. They are zero.
+//
+// 2) All the names, concatenated, with neither separation nor termination.
+//
+// This is designed to be compact but not particularly fast to retrieve.
+// In particular, it takes O(n) to retrieve the name of the n'th field,
+// which is usually fine because most protos have fewer than 10 fields.
+static StringPiece FindName(const char* name_data, size_t entries,
+                                  size_t index) {
+  // The compiler unrolls these... if this isn't fast enough,
+  // there's an AVX version at https://godbolt.org/z/eojrjqzfr
+  // ARM-compatible version at https://godbolt.org/z/n5YT5Ee85
+
+  // The field name sizes are padded up to a multiple of 8, so we
+  // must pad them here.
+  size_t num_sizes = (entries + 7) & -8;
+  auto* uint8s = reinterpret_cast<const uint8_t*>(name_data);
+  size_t pos = std::accumulate(uint8s, uint8s + index, num_sizes);
+  size_t size = name_data[index];
+  auto* start = &name_data[pos];
+  return {start, size};
+}
+
+StringPiece TcParser::MessageName(const TcParseTableBase* table) {
+  return FindName(table->name_data(), table->num_field_entries + 1, 0);
+}
+
+StringPiece TcParser::FieldName(const TcParseTableBase* table,
+                                      const FieldEntry* field_entry) {
+  const FieldEntry* const field_entries = table->field_entries_begin();
+  auto field_index = static_cast<size_t>(field_entry - field_entries);
+  return FindName(table->name_data(), table->num_field_entries + 1,
+                  field_index + 1);
+}
+
+const char* TcParser::MiniParse(PROTOBUF_TC_PARAM_DECL) {
+  uint32_t tag;
+  ptr = ReadTagInlined(ptr, &tag);
+  if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+
+  auto* entry = FindFieldEntry(table, tag >> 3);
+  if (entry == nullptr) {
+    data.data = tag;
+    PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+  }
+
+  // The handler may need the tag and the entry to resolve fallback logic. Both
+  // of these are 32 bits, so pack them into (the 64-bit) `data`. Since we can't
+  // pack the entry pointer itself, just pack its offset from `table`.
+  uint64_t entry_offset = reinterpret_cast<const char*>(entry) -
+                          reinterpret_cast<const char*>(table);
+  data.data = entry_offset << 32 | tag;
+
+  using field_layout::FieldKind;
+  auto field_type = entry->type_card & FieldKind::kFkMask;
+  switch (field_type) {
+    case FieldKind::kFkNone:
+      PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+    case FieldKind::kFkVarint:
+      PROTOBUF_MUSTTAIL return MpVarint(PROTOBUF_TC_PARAM_PASS);
+    case FieldKind::kFkPackedVarint:
+      PROTOBUF_MUSTTAIL return MpPackedVarint(PROTOBUF_TC_PARAM_PASS);
+    case FieldKind::kFkFixed:
+      PROTOBUF_MUSTTAIL return MpFixed(PROTOBUF_TC_PARAM_PASS);
+    case FieldKind::kFkPackedFixed:
+      PROTOBUF_MUSTTAIL return MpPackedFixed(PROTOBUF_TC_PARAM_PASS);
+    case FieldKind::kFkString:
+      PROTOBUF_MUSTTAIL return MpString(PROTOBUF_TC_PARAM_PASS);
+    case FieldKind::kFkMessage:
+      PROTOBUF_MUSTTAIL return MpMessage(PROTOBUF_TC_PARAM_PASS);
+    case FieldKind::kFkMap:
+      PROTOBUF_MUSTTAIL return MpMap(PROTOBUF_TC_PARAM_PASS);
+    default:
+      return Error(PROTOBUF_TC_PARAM_PASS);
+  }
+}
+
+namespace {
+
+// Offset returns the address `offset` bytes after `base`.
+inline void* Offset(void* base, uint32_t offset) {
+  return static_cast<uint8_t*>(base) + offset;
+}
+
+// InvertPacked changes tag bits from the given wire type to length
+// delimited. This is the difference expected between packed and non-packed
+// repeated fields.
+template <WireFormatLite::WireType Wt>
+inline PROTOBUF_ALWAYS_INLINE void InvertPacked(TcFieldData& data) {
+  data.data ^= Wt ^ WireFormatLite::WIRETYPE_LENGTH_DELIMITED;
+}
+
+}  // namespace
+
+//////////////////////////////////////////////////////////////////////////////
+// Message fields
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename TagType, bool group_coding>
+inline PROTOBUF_ALWAYS_INLINE
+const char* TcParser::SingularParseMessageAuxImpl(PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  }
+  auto saved_tag = UnalignedLoad<TagType>(ptr);
+  ptr += sizeof(TagType);
+  hasbits |= (uint64_t{1} << data.hasbit_idx());
+  SyncHasbits(msg, hasbits, table);
+  auto& field = RefAt<MessageLite*>(msg, data.offset());
+  if (field == nullptr) {
+    const MessageLite* default_instance =
+        table->field_aux(data.aux_idx())->message_default;
+    field = default_instance->New(ctx->data().arena);
+  }
+  if (group_coding) {
+    return ctx->ParseGroup(field, ptr, FastDecodeTag(saved_tag));
+  }
+  return ctx->ParseMessage(field, ptr);
+}
+
+const char* TcParser::FastMS1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularParseMessageAuxImpl<uint8_t, false>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastMS2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularParseMessageAuxImpl<uint16_t, false>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastGS1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularParseMessageAuxImpl<uint8_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastGS2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularParseMessageAuxImpl<uint16_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+template <typename TagType, bool group_coding>
+inline PROTOBUF_ALWAYS_INLINE
+const char* TcParser::RepeatedParseMessageAuxImpl(PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  }
+  auto saved_tag = UnalignedLoad<TagType>(ptr);
+  ptr += sizeof(TagType);
+  SyncHasbits(msg, hasbits, table);
+  const MessageLite* default_instance =
+      table->field_aux(data.aux_idx())->message_default;
+  auto& field = RefAt<RepeatedPtrFieldBase>(msg, data.offset());
+  MessageLite* submsg =
+      field.Add<GenericTypeHandler<MessageLite>>(default_instance);
+  if (group_coding) {
+    return ctx->ParseGroup(submsg, ptr, FastDecodeTag(saved_tag));
+  }
+  return ctx->ParseMessage(submsg, ptr);
+}
+
+const char* TcParser::FastMR1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedParseMessageAuxImpl<uint8_t, false>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastMR2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedParseMessageAuxImpl<uint16_t, false>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastGR1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedParseMessageAuxImpl<uint8_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastGR2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedParseMessageAuxImpl<uint16_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Fixed fields
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename LayoutType, typename TagType>
+PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularFixed(
+    PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  }
+  ptr += sizeof(TagType);  // Consume tag
+  hasbits |= (uint64_t{1} << data.hasbit_idx());
+  RefAt<LayoutType>(msg, data.offset()) = UnalignedLoad<LayoutType>(ptr);
+  ptr += sizeof(LayoutType);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastF32S1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularFixed<uint32_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastF32S2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularFixed<uint32_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastF64S1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularFixed<uint64_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastF64S2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularFixed<uint64_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+template <typename LayoutType, typename TagType>
+PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedFixed(
+    PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    // Check if the field can be parsed as packed repeated:
+    constexpr WireFormatLite::WireType fallback_wt =
+        sizeof(LayoutType) == 4 ? WireFormatLite::WIRETYPE_FIXED32
+                                : WireFormatLite::WIRETYPE_FIXED64;
+    InvertPacked<fallback_wt>(data);
+    if (data.coded_tag<TagType>() == 0) {
+      return PackedFixed<LayoutType, TagType>(PROTOBUF_TC_PARAM_PASS);
+    } else {
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    }
+  }
+  auto& field = RefAt<RepeatedField<LayoutType>>(msg, data.offset());
+  int idx = field.size();
+  auto elem = field.Add();
+  int space = field.Capacity() - idx;
+  idx = 0;
+  auto expected_tag = UnalignedLoad<TagType>(ptr);
+  do {
+    ptr += sizeof(TagType);
+    elem[idx++] = UnalignedLoad<LayoutType>(ptr);
+    ptr += sizeof(LayoutType);
+    if (idx >= space) break;
+    if (!ctx->DataAvailable(ptr)) break;
+  } while (UnalignedLoad<TagType>(ptr) == expected_tag);
+  field.AddNAlreadyReserved(idx - 1);
+  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastF32R1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedFixed<uint32_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastF32R2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedFixed<uint32_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastF64R1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedFixed<uint64_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastF64R2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedFixed<uint64_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+// Note: some versions of GCC will fail with error "function not inlinable" if
+// corecursive functions are both marked with PROTOBUF_ALWAYS_INLINE (Clang
+// accepts this). We can still apply the attribute to one of the two functions,
+// just not both (so we do mark the Repeated variant as always inlined). This
+// also applies to PackedVarint, below.
+template <typename LayoutType, typename TagType>
+const char* TcParser::PackedFixed(PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    // Try parsing as non-packed repeated:
+    constexpr WireFormatLite::WireType fallback_wt =
+        sizeof(LayoutType) == 4 ? WireFormatLite::WIRETYPE_FIXED32
+        : WireFormatLite::WIRETYPE_FIXED64;
+    InvertPacked<fallback_wt>(data);
+    if (data.coded_tag<TagType>() == 0) {
+      return RepeatedFixed<LayoutType, TagType>(PROTOBUF_TC_PARAM_PASS);
+    } else {
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    }
+  }
+  ptr += sizeof(TagType);
+  // Since ctx->ReadPackedFixed does not use TailCall<> or Return<>, sync any
+  // pending hasbits now:
+  SyncHasbits(msg, hasbits, table);
+  auto& field = RefAt<RepeatedField<LayoutType>>(msg, data.offset());
+  int size = ReadSize(&ptr);
+  // TODO(dlj): add a tailcalling variant of ReadPackedFixed.
+  return ctx->ReadPackedFixed(ptr, size,
+                              static_cast<RepeatedField<LayoutType>*>(&field));
+}
+
+const char* TcParser::FastF32P1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedFixed<uint32_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastF32P2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedFixed<uint32_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastF64P1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedFixed<uint64_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastF64P2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedFixed<uint64_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Varint fields
+//////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+// Shift "byte" left by n * 7 bits, filling vacated bits with ones.
+template <int n>
+inline PROTOBUF_ALWAYS_INLINE uint64_t
+shift_left_fill_with_ones(uint64_t byte, uint64_t ones) {
+  return (byte << (n * 7)) | (ones >> (64 - (n * 7)));
+}
+
+// Shift "byte" left by n * 7 bits, filling vacated bits with ones, and
+// put the new value in res.  Return whether the result was negative.
+template <int n>
+inline PROTOBUF_ALWAYS_INLINE bool shift_left_fill_with_ones_was_negative(
+    uint64_t byte, uint64_t ones, int64_t& res) {
+#if defined(__GCC_ASM_FLAG_OUTPUTS__) && defined(__x86_64__)
+  // For the first two rounds (ptr[1] and ptr[2]), micro benchmarks show a
+  // substantial improvement from capturing the sign from the condition code
+  // register on x86-64.
+  bool sign_bit;
+  asm("shldq %3, %2, %1"
+      : "=@ccs"(sign_bit), "+r"(byte)
+      : "r"(ones), "i"(n * 7));
+  res = byte;
+  return sign_bit;
+#else
+  // Generic fallback:
+  res = (byte << (n * 7)) | (ones >> (64 - (n * 7)));
+  return static_cast<int64_t>(res) < 0;
+#endif
+}
+
+inline PROTOBUF_ALWAYS_INLINE std::pair<const char*, uint64_t>
+Parse64FallbackPair(const char* p, int64_t res1) {
+  auto ptr = reinterpret_cast<const int8_t*>(p);
+
+  // The algorithm relies on sign extension for each byte to set all high bits
+  // when the varint continues. It also relies on asserting all of the lower
+  // bits for each successive byte read. This allows the result to be aggregated
+  // using a bitwise AND. For example:
+  //
+  //          8       1          64     57 ... 24     17  16      9  8       1
+  // ptr[0] = 1aaa aaaa ; res1 = 1111 1111 ... 1111 1111  1111 1111  1aaa aaaa
+  // ptr[1] = 1bbb bbbb ; res2 = 1111 1111 ... 1111 1111  11bb bbbb  b111 1111
+  // ptr[2] = 1ccc cccc ; res3 = 0000 0000 ... 000c cccc  cc11 1111  1111 1111
+  //                             ---------------------------------------------
+  //        res1 & res2 & res3 = 0000 0000 ... 000c cccc  ccbb bbbb  baaa aaaa
+  //
+  // On x86-64, a shld from a single register filled with enough 1s in the high
+  // bits can accomplish all this in one instruction. It so happens that res1
+  // has 57 high bits of ones, which is enough for the largest shift done.
+  GOOGLE_DCHECK_EQ(res1 >> 7, -1);
+  uint64_t ones = res1;  // save the high 1 bits from res1 (input to SHLD)
+  int64_t res2, res3;    // accumulated result chunks
+
+  if (!shift_left_fill_with_ones_was_negative<1>(ptr[1], ones, res2))
+    goto done2;
+  if (!shift_left_fill_with_ones_was_negative<2>(ptr[2], ones, res3))
+    goto done3;
+
+  // For the remainder of the chunks, check the sign of the AND result.
+  res1 &= shift_left_fill_with_ones<3>(ptr[3], ones);
+  if (res1 >= 0) goto done4;
+  res2 &= shift_left_fill_with_ones<4>(ptr[4], ones);
+  if (res2 >= 0) goto done5;
+  res3 &= shift_left_fill_with_ones<5>(ptr[5], ones);
+  if (res3 >= 0) goto done6;
+  res1 &= shift_left_fill_with_ones<6>(ptr[6], ones);
+  if (res1 >= 0) goto done7;
+  res2 &= shift_left_fill_with_ones<7>(ptr[7], ones);
+  if (res2 >= 0) goto done8;
+  res3 &= shift_left_fill_with_ones<8>(ptr[8], ones);
+  if (res3 >= 0) goto done9;
+
+  // For valid 64bit varints, the 10th byte/ptr[9] should be exactly 1. In this
+  // case, the continuation bit of ptr[8] already set the top bit of res3
+  // correctly, so all we have to do is check that the expected case is true.
+  if (PROTOBUF_PREDICT_TRUE(ptr[9] == 1)) goto done10;
+
+  // A value of 0, however, represents an over-serialized varint. This case
+  // should not happen, but if does (say, due to a nonconforming serializer),
+  // deassert the continuation bit that came from ptr[8].
+  if (ptr[9] == 0) {
+#if defined(__GCC_ASM_FLAG_OUTPUTS__) && defined(__x86_64__)
+    // Use a small instruction since this is an uncommon code path.
+    asm("btcq $63,%0" : "+r"(res3));
+#else
+    res3 ^= static_cast<uint64_t>(1) << 63;
+#endif
+    goto done10;
+  }
+
+  // If the 10th byte/ptr[9] itself has any other value, then it is too big to
+  // fit in 64 bits. If the continue bit is set, it is an unterminated varint.
+  return {nullptr, 0};
+
+done2:
+  return {p + 2, res1 & res2};
+done3:
+  return {p + 3, res1 & res2 & res3};
+done4:
+  return {p + 4, res1 & res2 & res3};
+done5:
+  return {p + 5, res1 & res2 & res3};
+done6:
+  return {p + 6, res1 & res2 & res3};
+done7:
+  return {p + 7, res1 & res2 & res3};
+done8:
+  return {p + 8, res1 & res2 & res3};
+done9:
+  return {p + 9, res1 & res2 & res3};
+done10:
+  return {p + 10, res1 & res2 & res3};
+}
+
+inline PROTOBUF_ALWAYS_INLINE const char* ParseVarint(const char* p,
+                                                      uint64_t* value) {
+  int64_t byte = static_cast<int8_t>(*p);
+  if (PROTOBUF_PREDICT_TRUE(byte >= 0)) {
+    *value = byte;
+    return p + 1;
+  } else {
+    auto tmp = Parse64FallbackPair(p, byte);
+    if (PROTOBUF_PREDICT_TRUE(tmp.first)) *value = tmp.second;
+    return tmp.first;
+  }
+}
+
+template <typename FieldType, bool zigzag = false>
+inline FieldType ZigZagDecodeHelper(uint64_t value) {
+  return static_cast<FieldType>(value);
+}
+
+template <>
+inline int32_t ZigZagDecodeHelper<int32_t, true>(uint64_t value) {
+  return WireFormatLite::ZigZagDecode32(value);
+}
+
+template <>
+inline int64_t ZigZagDecodeHelper<int64_t, true>(uint64_t value) {
+  return WireFormatLite::ZigZagDecode64(value);
+}
+
+bool EnumIsValidAux(int32_t val, uint16_t xform_val,
+                    TcParseTableBase::FieldAux aux) {
+  if (xform_val == field_layout::kTvRange) {
+    auto lo = aux.enum_range.start;
+    return lo <= val && val < (lo + aux.enum_range.length);
+  }
+  return aux.enum_validator(val);
+}
+
+}  // namespace
+
+template <typename FieldType, typename TagType, bool zigzag>
+PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularVarint(
+    PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  }
+  ptr += sizeof(TagType);  // Consume tag
+  hasbits |= (uint64_t{1} << data.hasbit_idx());
+
+  // clang isn't smart enough to be able to only conditionally save
+  // registers to the stack, so we turn the integer-greater-than-128
+  // case into a separate routine.
+  if (PROTOBUF_PREDICT_FALSE(static_cast<int8_t>(*ptr) < 0)) {
+    PROTOBUF_MUSTTAIL return SingularVarBigint<FieldType, TagType, zigzag>(
+        PROTOBUF_TC_PARAM_PASS);
+  }
+
+  RefAt<FieldType>(msg, data.offset()) =
+      ZigZagDecodeHelper<FieldType, zigzag>(static_cast<uint8_t>(*ptr++));
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+}
+
+template <typename FieldType, typename TagType, bool zigzag>
+PROTOBUF_NOINLINE const char* TcParser::SingularVarBigint(
+    PROTOBUF_TC_PARAM_DECL) {
+  // For some reason clang wants to save 5 registers to the stack here,
+  // but we only need four for this code, so save the data we don't need
+  // to the stack.  Happily, saving them this way uses regular store
+  // instructions rather than PUSH/POP, which saves time at the cost of greater
+  // code size, but for this heavily-used piece of code, that's fine.
+  struct Spill {
+    uint64_t field_data;
+    ::google::protobuf::MessageLite* msg;
+    const ::google::protobuf::internal::TcParseTableBase* table;
+    uint64_t hasbits;
+  };
+  volatile Spill spill = {data.data, msg, table, hasbits};
+
+  uint64_t tmp = 0;
+  PROTOBUF_ASSUME(static_cast<int8_t>(*ptr) < 0);
+  ptr = ParseVarint(ptr, &tmp);
+
+  data.data = spill.field_data;
+  msg = spill.msg;
+  table = spill.table;
+  hasbits = spill.hasbits;
+
+  if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
+    return Error(PROTOBUF_TC_PARAM_PASS);
+  }
+  RefAt<FieldType>(msg, data.offset()) =
+      ZigZagDecodeHelper<FieldType, zigzag>(tmp);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastV8S1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularVarint<bool, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV8S2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularVarint<bool, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV32S1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularVarint<uint32_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV32S2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularVarint<uint32_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV64S1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularVarint<uint64_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV64S2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularVarint<uint64_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastZ32S1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularVarint<int32_t, uint8_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastZ32S2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularVarint<int32_t, uint16_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastZ64S1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularVarint<int64_t, uint8_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastZ64S2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularVarint<int64_t, uint16_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+template <typename FieldType, typename TagType, bool zigzag>
+PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedVarint(
+    PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    // Try parsing as non-packed repeated:
+    InvertPacked<WireFormatLite::WIRETYPE_VARINT>(data);
+    if (data.coded_tag<TagType>() == 0) {
+      return PackedVarint<FieldType, TagType, zigzag>(PROTOBUF_TC_PARAM_PASS);
+    } else {
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    }
+  }
+  auto& field = RefAt<RepeatedField<FieldType>>(msg, data.offset());
+  auto expected_tag = UnalignedLoad<TagType>(ptr);
+  do {
+    ptr += sizeof(TagType);
+    uint64_t tmp = 0;
+    ptr = ParseVarint(ptr, &tmp);
+    if (ptr == nullptr) {
+      return Error(PROTOBUF_TC_PARAM_PASS);
+    }
+    field.Add(ZigZagDecodeHelper<FieldType, zigzag>(tmp));
+    if (!ctx->DataAvailable(ptr)) {
+      break;
+    }
+  } while (UnalignedLoad<TagType>(ptr) == expected_tag);
+  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastV8R1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedVarint<bool, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV8R2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedVarint<bool, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV32R1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedVarint<uint32_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV32R2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedVarint<uint32_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV64R1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedVarint<uint64_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV64R2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedVarint<uint64_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastZ32R1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedVarint<int32_t, uint8_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastZ32R2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedVarint<int32_t, uint16_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastZ64R1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedVarint<int64_t, uint8_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastZ64R2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedVarint<int64_t, uint16_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+// See comment on PackedFixed for why this is not PROTOBUF_ALWAYS_INLINE.
+template <typename FieldType, typename TagType, bool zigzag>
+const char* TcParser::PackedVarint(PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    InvertPacked<WireFormatLite::WIRETYPE_VARINT>(data);
+    if (data.coded_tag<TagType>() == 0) {
+      return RepeatedVarint<FieldType, TagType, zigzag>(PROTOBUF_TC_PARAM_PASS);
+    } else {
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    }
+  }
+  ptr += sizeof(TagType);
+  // Since ctx->ReadPackedVarint does not use TailCall or Return, sync any
+  // pending hasbits now:
+  SyncHasbits(msg, hasbits, table);
+  auto* field = &RefAt<RepeatedField<FieldType>>(msg, data.offset());
+  return ctx->ReadPackedVarint(ptr, [field](uint64_t varint) {
+    FieldType val;
+    if (zigzag) {
+      if (sizeof(FieldType) == 8) {
+        val = WireFormatLite::ZigZagDecode64(varint);
+      } else {
+        val = WireFormatLite::ZigZagDecode32(varint);
+      }
+    } else {
+      val = varint;
+    }
+    field->Add(val);
+  });
+}
+
+const char* TcParser::FastV8P1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedVarint<bool, uint8_t>(PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV8P2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedVarint<bool, uint16_t>(PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV32P1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedVarint<uint32_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV32P2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedVarint<uint32_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV64P1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedVarint<uint64_t, uint8_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastV64P2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedVarint<uint64_t, uint16_t>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastZ32P1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedVarint<int32_t, uint8_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastZ32P2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedVarint<int32_t, uint16_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastZ64P1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedVarint<int64_t, uint8_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastZ64P2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return PackedVarint<int64_t, uint16_t, true>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Enum fields
+//////////////////////////////////////////////////////////////////////////////
+
+PROTOBUF_NOINLINE const char* TcParser::FastUnknownEnumFallback(
+    PROTOBUF_TC_PARAM_DECL) {
+  (void)msg;
+  (void)ctx;
+  (void)hasbits;
+
+  // If we know we want to put this field directly into the unknown field set,
+  // then we can skip the call to MiniParse and directly call table->fallback.
+  // However, we first have to update `data` to contain the decoded tag.
+  uint32_t tag;
+  ptr = ReadTag(ptr, &tag);
+  if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
+    return Error(PROTOBUF_TC_PARAM_PASS);
+  }
+  data.data = tag;
+  PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+}
+
+template <typename TagType, uint16_t xform_val>
+PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularEnum(
+    PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  }
+  const char* ptr2 = ptr;  // Save for unknown enum case
+  ptr += sizeof(TagType);  // Consume tag
+  uint64_t tmp = 0;
+  ptr = ParseVarint(ptr, &tmp);
+  if (ptr == nullptr) {
+    return Error(PROTOBUF_TC_PARAM_PASS);
+  }
+  const TcParseTableBase::FieldAux aux = *table->field_aux(data.aux_idx());
+  if (PROTOBUF_PREDICT_FALSE(
+          !EnumIsValidAux(static_cast<int32_t>(tmp), xform_val, aux))) {
+    ptr = ptr2;
+    PROTOBUF_MUSTTAIL return FastUnknownEnumFallback(PROTOBUF_TC_PARAM_PASS);
+  }
+  hasbits |= (uint64_t{1} << data.hasbit_idx());
+  RefAt<int32_t>(msg, data.offset()) = tmp;
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastErS1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularEnum<uint8_t, field_layout::kTvRange>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastErS2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularEnum<uint16_t, field_layout::kTvRange>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastEvS1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularEnum<uint8_t, field_layout::kTvEnum>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastEvS2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularEnum<uint16_t, field_layout::kTvEnum>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+template <typename TagType, uint16_t xform_val>
+PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedEnum(
+    PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    InvertPacked<WireFormatLite::WIRETYPE_VARINT>(data);
+    if (data.coded_tag<TagType>() == 0) {
+      // Packed parsing is handled by generated fallback.
+      PROTOBUF_MUSTTAIL return FastUnknownEnumFallback(PROTOBUF_TC_PARAM_PASS);
+    } else {
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    }
+  }
+  auto& field = RefAt<RepeatedField<int32_t>>(msg, data.offset());
+  auto expected_tag = UnalignedLoad<TagType>(ptr);
+  const TcParseTableBase::FieldAux aux = *table->field_aux(data.aux_idx());
+  do {
+    const char* ptr2 = ptr;  // save for unknown enum case
+    ptr += sizeof(TagType);
+    uint64_t tmp = 0;
+    ptr = ParseVarint(ptr, &tmp);
+    if (ptr == nullptr) {
+      return Error(PROTOBUF_TC_PARAM_PASS);
+    }
+    if (PROTOBUF_PREDICT_FALSE(
+            !EnumIsValidAux(static_cast<int32_t>(tmp), xform_val, aux))) {
+      // We can avoid duplicate work in MiniParse by directly calling
+      // table->fallback.
+      ptr = ptr2;
+      PROTOBUF_MUSTTAIL return FastUnknownEnumFallback(PROTOBUF_TC_PARAM_PASS);
+    }
+    field.Add(static_cast<int32_t>(tmp));
+    if (!ctx->DataAvailable(ptr)) {
+      break;
+    }
+  } while (UnalignedLoad<TagType>(ptr) == expected_tag);
+  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastErR1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedEnum<uint8_t, field_layout::kTvRange>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastErR2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedEnum<uint16_t, field_layout::kTvRange>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastEvR1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedEnum<uint8_t, field_layout::kTvEnum>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastEvR2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedEnum<uint16_t, field_layout::kTvEnum>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// String/bytes fields
+//////////////////////////////////////////////////////////////////////////////
+
+// Defined in wire_format_lite.cc
+void PrintUTF8ErrorLog(StringPiece message_name,
+                       StringPiece field_name, const char* operation_str,
+                       bool emit_stacktrace);
+
+void TcParser::ReportFastUtf8Error(uint32_t decoded_tag,
+                                   const TcParseTableBase* table) {
+  uint32_t field_num = decoded_tag >> 3;
+  const auto* entry = FindFieldEntry(table, field_num);
+  PrintUTF8ErrorLog(MessageName(table), FieldName(table, entry), "parsing",
+                    false);
+}
+
+namespace {
+
+PROTOBUF_NOINLINE
+const char* SingularStringParserFallback(ArenaStringPtr* s, const char* ptr,
+                                         EpsCopyInputStream* stream) {
+  int size = ReadSize(&ptr);
+  if (!ptr) return nullptr;
+  return stream->ReadString(ptr, size, s->MutableNoCopy(nullptr));
+}
+
+}  // namespace
+
+template <typename TagType, TcParser::Utf8Type utf8>
+PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularString(
+    PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  }
+  auto saved_tag = UnalignedLoad<TagType>(ptr);
+  ptr += sizeof(TagType);
+  hasbits |= (uint64_t{1} << data.hasbit_idx());
+  auto& field = RefAt<ArenaStringPtr>(msg, data.offset());
+  auto arena = ctx->data().arena;
+  if (arena) {
+    ptr = ctx->ReadArenaString(ptr, &field, arena);
+  } else {
+    ptr = SingularStringParserFallback(&field, ptr, ctx);
+  }
+  if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+  switch (utf8) {
+    case kNoUtf8:
+#ifdef NDEBUG
+    case kUtf8ValidateOnly:
+#endif
+      return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+    default:
+      if (PROTOBUF_PREDICT_TRUE(IsStructurallyValidUTF8(field.Get()))) {
+        return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+      }
+      ReportFastUtf8Error(FastDecodeTag(saved_tag), table);
+      return utf8 == kUtf8 ? Error(PROTOBUF_TC_PARAM_PASS)
+                           : ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+  }
+}
+
+const char* TcParser::FastBS1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularString<uint8_t, kNoUtf8>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastBS2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularString<uint16_t, kNoUtf8>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastSS1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularString<uint8_t, kUtf8ValidateOnly>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastSS2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularString<uint16_t, kUtf8ValidateOnly>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastUS1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularString<uint8_t, kUtf8>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastUS2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return SingularString<uint16_t, kUtf8>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+// Inlined string variants:
+
+const char* TcParser::FastBiS1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastBiS2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastSiS1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastSiS2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastUiS1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastUiS2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+}
+
+template <typename TagType, TcParser::Utf8Type utf8>
+PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedString(
+    PROTOBUF_TC_PARAM_DECL) {
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  }
+  auto expected_tag = UnalignedLoad<TagType>(ptr);
+  auto& field = RefAt<RepeatedPtrField<std::string>>(msg, data.offset());
+  do {
+    ptr += sizeof(TagType);
+    std::string* str = field.Add();
+    ptr = InlineGreedyStringParser(str, ptr, ctx);
+    if (ptr == nullptr) {
+      return Error(PROTOBUF_TC_PARAM_PASS);
+    }
+    switch (utf8) {
+      case kNoUtf8:
+#ifdef NDEBUG
+      case kUtf8ValidateOnly:
+#endif
+        break;
+      default:
+        if (PROTOBUF_PREDICT_TRUE(IsStructurallyValidUTF8(*str))) {
+          break;
+        }
+        ReportFastUtf8Error(FastDecodeTag(expected_tag), table);
+        if (utf8 == kUtf8) return Error(PROTOBUF_TC_PARAM_PASS);
+        break;
+    }
+    if (!ctx->DataAvailable(ptr)) break;
+  } while (UnalignedLoad<TagType>(ptr) == expected_tag);
+  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::FastBR1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedString<uint8_t, kNoUtf8>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastBR2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedString<uint16_t, kNoUtf8>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastSR1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedString<uint8_t, kUtf8ValidateOnly>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastSR2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedString<uint16_t, kUtf8ValidateOnly>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastUR1(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedString<uint8_t, kUtf8>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+const char* TcParser::FastUR2(PROTOBUF_TC_PARAM_DECL) {
+  PROTOBUF_MUSTTAIL return RepeatedString<uint16_t, kUtf8>(
+      PROTOBUF_TC_PARAM_PASS);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Mini parsing
+//////////////////////////////////////////////////////////////////////////////
+
+namespace {
+inline void SetHas(const TcParseTableBase* table, const FieldEntry& entry,
+                   MessageLite* msg, uint64_t& hasbits) {
+  int32_t has_idx = entry.has_idx;
+  if (has_idx < 32) {
+    hasbits |= uint64_t{1} << has_idx;
+  } else {
+    auto* hasblocks = &TcParser::RefAt<uint32_t>(msg, table->has_bits_offset);
+#if defined(__x86_64__) && defined(__GNUC__)
+    asm("bts %1, %0\n" : "+m"(*hasblocks) : "r"(has_idx));
+#else
+    auto& hasblock = hasblocks[has_idx / 32];
+    hasblock |= uint32_t{1} << (has_idx % 32);
+#endif
+  }
+}
+}  // namespace
+
+// Destroys any existing oneof union member (if necessary). Returns true if the
+// caller is responsible for initializing the object, or false if the field
+// already has the desired case.
+bool TcParser::ChangeOneof(const TcParseTableBase* table,
+                           const TcParseTableBase::FieldEntry& entry,
+                           uint32_t field_num, ParseContext* ctx,
+                           MessageLite* msg) {
+  // The _oneof_case_ array offset is stored in the first aux entry.
+  uint32_t oneof_case_offset = table->field_aux(0u)->offset;
+  // The _oneof_case_ array index is stored in the has-bit index.
+  uint32_t* oneof_case =
+      &TcParser::RefAt<uint32_t>(msg, oneof_case_offset) + entry.has_idx;
+  uint32_t current_case = *oneof_case;
+  *oneof_case = field_num;
+
+  if (current_case == 0) {
+    // If the member is empty, we don't have anything to clear. Caller is
+    // responsible for creating a new member object.
+    return true;
+  }
+  if (current_case == field_num) {
+    // If the member is already active, then it should be merged. We're done.
+    return false;
+  }
+  // Look up the value that is already stored, and dispose of it if necessary.
+  const FieldEntry* current_entry = FindFieldEntry(table, current_case);
+  uint16_t current_kind = current_entry->type_card & field_layout::kFkMask;
+  uint16_t current_rep = current_entry->type_card & field_layout::kRepMask;
+  if (current_kind == field_layout::kFkString) {
+    switch (current_rep) {
+      case field_layout::kRepAString: {
+        auto& field = RefAt<ArenaStringPtr>(msg, current_entry->offset);
+        field.Destroy();
+        break;
+      }
+      case field_layout::kRepSString:
+      case field_layout::kRepIString:
+      default:
+        GOOGLE_LOG(DFATAL) << "string rep not handled: "
+                    << (current_rep >> field_layout::kRepShift);
+        return true;
+    }
+  } else if (current_kind == field_layout::kFkMessage) {
+    switch (current_rep) {
+      case field_layout::kRepMessage:
+      case field_layout::kRepGroup:
+      case field_layout::kRepIWeak: {
+        auto& field = RefAt<MessageLite*>(msg, current_entry->offset);
+        if (!ctx->data().arena) {
+          delete field;
+        }
+        break;
+      }
+      default:
+        GOOGLE_LOG(DFATAL) << "message rep not handled: "
+                    << (current_rep >> field_layout::kRepShift);
+        break;
+    }
+  }
+  return true;
+}
+
+const char* TcParser::MpFixed(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  const uint16_t type_card = entry.type_card;
+  const uint16_t card = type_card & field_layout::kFcMask;
+
+  // Check for repeated parsing (wiretype fallback is handled there):
+  if (card == field_layout::kFcRepeated) {
+    PROTOBUF_MUSTTAIL return MpRepeatedFixed(PROTOBUF_TC_PARAM_PASS);
+  }
+  // Check for mismatched wiretype:
+  const uint16_t rep = type_card & field_layout::kRepMask;
+  const uint32_t decoded_wiretype = data.tag() & 7;
+  if (rep == field_layout::kRep64Bits) {
+    if (decoded_wiretype != WireFormatLite::WIRETYPE_FIXED64) {
+      PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+    }
+  } else {
+    GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep32Bits));
+    if (decoded_wiretype != WireFormatLite::WIRETYPE_FIXED32) {
+      PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+    }
+  }
+  // Set the field present:
+  if (card == field_layout::kFcOptional) {
+    SetHas(table, entry, msg, hasbits);
+  } else if (card == field_layout::kFcOneof) {
+    ChangeOneof(table, entry, data.tag() >> 3, ctx, msg);
+  }
+  // Copy the value:
+  if (rep == field_layout::kRep64Bits) {
+    RefAt<uint64_t>(msg, entry.offset) = UnalignedLoad<uint64_t>(ptr);
+    ptr += sizeof(uint64_t);
+  } else {
+    RefAt<uint32_t>(msg, entry.offset) = UnalignedLoad<uint32_t>(ptr);
+    ptr += sizeof(uint32_t);
+  }
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::MpRepeatedFixed(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  const uint32_t decoded_tag = data.tag();
+  const uint32_t decoded_wiretype = decoded_tag & 7;
+
+  // Check for packed repeated fallback:
+  if (decoded_wiretype == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+    PROTOBUF_MUSTTAIL return MpPackedFixed(PROTOBUF_TC_PARAM_PASS);
+  }
+
+  const uint16_t type_card = entry.type_card;
+  const uint16_t rep = type_card & field_layout::kRepMask;
+  if (rep == field_layout::kRep64Bits) {
+    if (decoded_wiretype != WireFormatLite::WIRETYPE_FIXED64) {
+      PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+    }
+    auto& field = RefAt<RepeatedField<uint64_t>>(msg, entry.offset);
+    constexpr auto size = sizeof(uint64_t);
+    const char* ptr2 = ptr;
+    uint32_t next_tag;
+    do {
+      ptr = ptr2;
+      *field.Add() = UnalignedLoad<uint64_t>(ptr);
+      ptr += size;
+      if (!ctx->DataAvailable(ptr)) break;
+      ptr2 = ReadTag(ptr, &next_tag);
+    } while (next_tag == decoded_tag);
+  } else {
+    GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep32Bits));
+    if (decoded_wiretype != WireFormatLite::WIRETYPE_FIXED32) {
+      PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+    }
+    auto& field = RefAt<RepeatedField<uint32_t>>(msg, entry.offset);
+    constexpr auto size = sizeof(uint32_t);
+    const char* ptr2 = ptr;
+    uint32_t next_tag;
+    do {
+      ptr = ptr2;
+      *field.Add() = UnalignedLoad<uint32_t>(ptr);
+      ptr += size;
+      if (!ctx->DataAvailable(ptr)) break;
+      ptr2 = ReadTag(ptr, &next_tag);
+    } while (next_tag == decoded_tag);
+  }
+
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::MpPackedFixed(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  const uint16_t type_card = entry.type_card;
+  const uint32_t decoded_wiretype = data.tag() & 7;
+
+  // Check for non-packed repeated fallback:
+  if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+    PROTOBUF_MUSTTAIL return MpRepeatedFixed(PROTOBUF_TC_PARAM_PASS);
+  }
+
+  // Since ctx->ReadPackedFixed does not use TailCall<> or Return<>, sync any
+  // pending hasbits now:
+  SyncHasbits(msg, hasbits, table);
+
+  int size = ReadSize(&ptr);
+  uint16_t rep = type_card & field_layout::kRepMask;
+  if (rep == field_layout::kRep64Bits) {
+    auto& field = RefAt<RepeatedField<uint64_t>>(msg, entry.offset);
+    ptr = ctx->ReadPackedFixed(ptr, size, &field);
+  } else {
+    GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep32Bits));
+    auto& field = RefAt<RepeatedField<uint32_t>>(msg, entry.offset);
+    ptr = ctx->ReadPackedFixed(ptr, size, &field);
+  }
+
+  if (ptr == nullptr) {
+    return Error(PROTOBUF_TC_PARAM_PASS);
+  }
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::MpVarint(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  const uint16_t type_card = entry.type_card;
+  const uint16_t card = type_card & field_layout::kFcMask;
+
+  // Check for repeated parsing:
+  if (card == field_layout::kFcRepeated) {
+    PROTOBUF_MUSTTAIL return MpRepeatedVarint(PROTOBUF_TC_PARAM_PASS);
+  }
+  // Check for wire type mismatch:
+  if ((data.tag() & 7) != WireFormatLite::WIRETYPE_VARINT) {
+    PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+  }
+  const uint16_t xform_val = type_card & field_layout::kTvMask;
+  const bool is_zigzag = xform_val == field_layout::kTvZigZag;
+  const bool is_validated_enum = xform_val & field_layout::kTvEnum;
+
+  // Parse the value:
+  const char* ptr2 = ptr;  // save for unknown enum case
+  uint64_t tmp = 0;
+  ptr = ParseVarint(ptr, &tmp);
+  if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+
+  // Transform and/or validate the value
+  uint16_t rep = type_card & field_layout::kRepMask;
+  if (rep == field_layout::kRep64Bits) {
+    if (is_zigzag) {
+      tmp = WireFormatLite::ZigZagDecode64(tmp);
+    }
+  } else if (rep == field_layout::kRep32Bits) {
+    if (is_validated_enum) {
+      if (!EnumIsValidAux(tmp, xform_val, *table->field_aux(&entry))) {
+        ptr = ptr2;
+        PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+      }
+    } else if (is_zigzag) {
+      tmp = WireFormatLite::ZigZagDecode32(static_cast<uint32_t>(tmp));
+    }
+  }
+
+  // Mark the field as present:
+  const bool is_oneof = card == field_layout::kFcOneof;
+  if (card == field_layout::kFcOptional) {
+    SetHas(table, entry, msg, hasbits);
+  } else if (is_oneof) {
+    ChangeOneof(table, entry, data.tag() >> 3, ctx, msg);
+  }
+
+  if (rep == field_layout::kRep64Bits) {
+    RefAt<uint64_t>(msg, entry.offset) = tmp;
+  } else if (rep == field_layout::kRep32Bits) {
+    RefAt<uint32_t>(msg, entry.offset) = static_cast<uint32_t>(tmp);
+  } else {
+    GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep8Bits));
+    RefAt<bool>(msg, entry.offset) = static_cast<bool>(tmp);
+  }
+
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::MpRepeatedVarint(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  auto type_card = entry.type_card;
+  const uint32_t decoded_tag = data.tag();
+  auto decoded_wiretype = decoded_tag & 7;
+
+  // Check for packed repeated fallback:
+  if (decoded_wiretype == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+    PROTOBUF_MUSTTAIL return MpPackedVarint(PROTOBUF_TC_PARAM_PASS);
+  }
+  // Check for wire type mismatch:
+  if (decoded_wiretype != WireFormatLite::WIRETYPE_VARINT) {
+    PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+  }
+  uint16_t xform_val = (type_card & field_layout::kTvMask);
+  const bool is_zigzag = xform_val == field_layout::kTvZigZag;
+  const bool is_validated_enum = xform_val & field_layout::kTvEnum;
+
+  uint16_t rep = type_card & field_layout::kRepMask;
+  if (rep == field_layout::kRep64Bits) {
+    auto& field = RefAt<RepeatedField<uint64_t>>(msg, entry.offset);
+    const char* ptr2 = ptr;
+    uint32_t next_tag;
+    do {
+      uint64_t tmp = 0;
+      ptr = ParseVarint(ptr2, &tmp);
+      if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+      field.Add(is_zigzag ? WireFormatLite::ZigZagDecode64(tmp) : tmp);
+      if (!ctx->DataAvailable(ptr)) break;
+      ptr2 = ReadTag(ptr, &next_tag);
+    } while (next_tag == decoded_tag);
+  } else if (rep == field_layout::kRep32Bits) {
+    auto& field = RefAt<RepeatedField<uint32_t>>(msg, entry.offset);
+    const char* ptr2 = ptr;
+    uint32_t next_tag;
+    do {
+      uint64_t tmp = 0;
+      ptr = ParseVarint(ptr2, &tmp);
+      if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+      if (is_validated_enum) {
+        if (!EnumIsValidAux(tmp, xform_val, *table->field_aux(&entry))) {
+          ptr = ptr2;
+          PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+        }
+      } else if (is_zigzag) {
+        tmp = WireFormatLite::ZigZagDecode32(tmp);
+      }
+      field.Add(tmp);
+      if (!ctx->DataAvailable(ptr)) break;
+      ptr2 = ReadTag(ptr, &next_tag);
+    } while (next_tag == decoded_tag);
+  } else {
+    GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep8Bits));
+    auto& field = RefAt<RepeatedField<bool>>(msg, entry.offset);
+    const char* ptr2 = ptr;
+    uint32_t next_tag;
+    do {
+      uint64_t tmp = 0;
+      ptr = ParseVarint(ptr2, &tmp);
+      if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+      field.Add(static_cast<bool>(tmp));
+      if (!ctx->DataAvailable(ptr)) break;
+      ptr2 = ReadTag(ptr, &next_tag);
+    } while (next_tag == decoded_tag);
+  }
+
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::MpPackedVarint(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  auto type_card = entry.type_card;
+  auto decoded_wiretype = data.tag() & 7;
+
+  // Check for non-packed repeated fallback:
+  if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+    PROTOBUF_MUSTTAIL return MpRepeatedVarint(PROTOBUF_TC_PARAM_PASS);
+  }
+  uint16_t xform_val = (type_card & field_layout::kTvMask);
+  const bool is_zigzag = xform_val == field_layout::kTvZigZag;
+  const bool is_validated_enum = xform_val & field_layout::kTvEnum;
+  if (is_validated_enum) {
+    // TODO(b/206890171): handle enums
+    PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+  }
+
+  // Since ctx->ReadPackedFixed does not use TailCall<> or Return<>, sync any
+  // pending hasbits now:
+  SyncHasbits(msg, hasbits, table);
+
+  uint16_t rep = type_card & field_layout::kRepMask;
+  if (rep == field_layout::kRep64Bits) {
+    auto* field = &RefAt<RepeatedField<uint64_t>>(msg, entry.offset);
+    return ctx->ReadPackedVarint(ptr, [field, is_zigzag](uint64_t value) {
+      field->Add(is_zigzag ? WireFormatLite::ZigZagDecode64(value) : value);
+    });
+  } else if (rep == field_layout::kRep32Bits) {
+    auto* field = &RefAt<RepeatedField<uint32_t>>(msg, entry.offset);
+    return ctx->ReadPackedVarint(ptr, [field, is_zigzag](uint64_t value) {
+      field->Add(is_zigzag ? WireFormatLite::ZigZagDecode32(
+                                 static_cast<uint32_t>(value))
+                           : value);
+    });
+  } else {
+    GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep8Bits));
+    auto* field = &RefAt<RepeatedField<bool>>(msg, entry.offset);
+    return ctx->ReadPackedVarint(
+        ptr, [field](uint64_t value) { field->Add(value); });
+  }
+
+  return Error(PROTOBUF_TC_PARAM_PASS);
+}
+
+bool TcParser::MpVerifyUtf8(StringPiece wire_bytes,
+                            const TcParseTableBase* table,
+                            const FieldEntry& entry, uint16_t xform_val) {
+  if (xform_val == field_layout::kTvUtf8) {
+    if (!IsStructurallyValidUTF8(wire_bytes)) {
+      PrintUTF8ErrorLog(MessageName(table), FieldName(table, &entry), "parsing",
+                        false);
+      return false;
+    }
+    return true;
+  }
+#ifndef NDEBUG
+  if (xform_val == field_layout::kTvUtf8Debug) {
+    if (!IsStructurallyValidUTF8(wire_bytes)) {
+      PrintUTF8ErrorLog(MessageName(table), FieldName(table, &entry), "parsing",
+                        false);
+    }
+  }
+#endif  // NDEBUG
+  return true;
+}
+
+const char* TcParser::MpString(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  const uint16_t type_card = entry.type_card;
+  const uint16_t card = type_card & field_layout::kFcMask;
+  const uint32_t decoded_wiretype = data.tag() & 7;
+
+  if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+    PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+  }
+  if (card == field_layout::kFcRepeated) {
+    PROTOBUF_MUSTTAIL return MpRepeatedString(PROTOBUF_TC_PARAM_PASS);
+  }
+  const uint16_t xform_val = type_card & field_layout::kTvMask;
+  const uint16_t rep = type_card & field_layout::kRepMask;
+  if (rep == field_layout::kRepIString) {
+    // TODO(b/198211897): support InilnedStringField.
+    PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+  }
+
+  // Mark the field as present:
+  const bool is_oneof = card == field_layout::kFcOneof;
+  bool need_init = false;
+  if (card == field_layout::kFcOptional) {
+    SetHas(table, entry, msg, hasbits);
+  } else if (is_oneof) {
+    need_init = ChangeOneof(table, entry, data.tag() >> 3, ctx, msg);
+  }
+
+  bool is_valid = false;
+  Arena* arena = ctx->data().arena;
+  switch (rep) {
+    case field_layout::kRepAString: {
+      auto& field = RefAt<ArenaStringPtr>(msg, entry.offset);
+      if (need_init) field.InitDefault();
+      if (arena) {
+        ptr = ctx->ReadArenaString(ptr, &field, arena);
+      } else {
+        std::string* str = field.MutableNoCopy(nullptr);
+        ptr = InlineGreedyStringParser(str, ptr, ctx);
+      }
+      if (!ptr) break;
+      is_valid = MpVerifyUtf8(field.Get(), table, entry, xform_val);
+      break;
+    }
+
+    case field_layout::kRepIString: {
+      break;
+    }
+  }
+
+  if (ptr == nullptr || !is_valid) {
+    return Error(PROTOBUF_TC_PARAM_PASS);
+  }
+  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::MpRepeatedString(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  const uint16_t type_card = entry.type_card;
+  const uint32_t decoded_tag = data.tag();
+  const uint32_t decoded_wiretype = decoded_tag & 7;
+
+  if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+    PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+  }
+
+  const uint16_t rep = type_card & field_layout::kRepMask;
+  const uint16_t xform_val = type_card & field_layout::kTvMask;
+  switch (rep) {
+    case field_layout::kRepSString: {
+      auto& field = RefAt<RepeatedPtrField<std::string>>(msg, entry.offset);
+      const char* ptr2 = ptr;
+      uint32_t next_tag;
+      do {
+        ptr = ptr2;
+        std::string* str = field.Add();
+        ptr = InlineGreedyStringParser(str, ptr, ctx);
+        if (PROTOBUF_PREDICT_FALSE(
+                ptr == nullptr ||
+                !MpVerifyUtf8(*str, table, entry, xform_val))) {
+          return Error(PROTOBUF_TC_PARAM_PASS);
+        }
+        if (!ctx->DataAvailable(ptr)) break;
+        ptr2 = ReadTag(ptr, &next_tag);
+      } while (next_tag == decoded_tag);
+      break;
+    }
+
+#ifndef NDEBUG
+    default:
+      GOOGLE_LOG(FATAL) << "Unsupported repeated string rep: " << rep;
+      break;
+#endif
+  }
+
+  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+}
+
+const char* TcParser::MpMessage(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  const uint16_t type_card = entry.type_card;
+  const uint16_t card = type_card & field_layout::kFcMask;
+
+  // Check for repeated parsing:
+  if (card == field_layout::kFcRepeated) {
+    PROTOBUF_MUSTTAIL return MpRepeatedMessage(PROTOBUF_TC_PARAM_PASS);
+  }
+
+  const uint32_t decoded_tag = data.tag();
+  const uint32_t decoded_wiretype = decoded_tag & 7;
+  const uint16_t rep = type_card & field_layout::kRepMask;
+  const bool is_group = rep == field_layout::kRepGroup;
+
+  // Validate wiretype:
+  switch (rep) {
+    case field_layout::kRepMessage:
+      if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+        goto fallback;
+      }
+      break;
+    case field_layout::kRepGroup:
+      if (decoded_wiretype != WireFormatLite::WIRETYPE_START_GROUP) {
+        goto fallback;
+      }
+      break;
+    default: {
+    fallback:
+      // Lazy and implicit weak fields are handled by generated code:
+      // TODO(b/210762816): support these.
+      PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+    }
+  }
+
+  const bool is_oneof = card == field_layout::kFcOneof;
+  bool need_init = false;
+  if (card == field_layout::kFcOptional) {
+    SetHas(table, entry, msg, hasbits);
+  } else if (is_oneof) {
+    need_init = ChangeOneof(table, entry, data.tag() >> 3, ctx, msg);
+  }
+  MessageLite*& field = RefAt<MessageLite*>(msg, entry.offset);
+  if (need_init || field == nullptr) {
+    const MessageLite* default_instance =
+        table->field_aux(&entry)->message_default;
+    field = default_instance->New(ctx->data().arena);
+  }
+  SyncHasbits(msg, hasbits, table);
+  if (is_group) {
+    return ctx->ParseGroup(field, ptr, decoded_tag);
+  }
+  return ctx->ParseMessage(field, ptr);
+}
+
+const char* TcParser::MpRepeatedMessage(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  const uint16_t type_card = entry.type_card;
+  GOOGLE_DCHECK_EQ(type_card & field_layout::kFcMask,
+            static_cast<uint16_t>(field_layout::kFcRepeated));
+  const uint32_t decoded_tag = data.tag();
+  const uint32_t decoded_wiretype = decoded_tag & 7;
+  const uint16_t rep = type_card & field_layout::kRepMask;
+  const bool is_group = rep == field_layout::kRepGroup;
+
+  // Validate wiretype:
+  switch (rep) {
+    case field_layout::kRepMessage:
+      if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+        goto fallback;
+      }
+      break;
+    case field_layout::kRepGroup:
+      if (decoded_wiretype != WireFormatLite::WIRETYPE_START_GROUP) {
+        goto fallback;
+      }
+      break;
+    default: {
+    fallback:
+      // Lazy and implicit weak fields are handled by generated code:
+      // TODO(b/210762816): support these.
+      PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+    }
+  }
+
+  SyncHasbits(msg, hasbits, table);
+  const MessageLite* default_instance =
+      table->field_aux(&entry)->message_default;
+  auto& field = RefAt<RepeatedPtrFieldBase>(msg, entry.offset);
+  MessageLite* value =
+      field.Add<GenericTypeHandler<MessageLite>>(default_instance);
+  if (is_group) {
+    return ctx->ParseGroup(value, ptr, decoded_tag);
+  }
+  return ctx->ParseMessage(value, ptr);
+}
+
+const char* TcParser::MpMap(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  (void)entry;
+  PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_util.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_util.cpp
new file mode 100644
index 0000000..cad12a3
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/generated_message_util.cpp
@@ -0,0 +1,409 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/generated_message_util.h>
+
+#include <atomic>
+#include <limits>
+#include <vector>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// Must be included last
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+void DestroyMessage(const void* message) {
+  static_cast<const MessageLite*>(message)->~MessageLite();
+}
+void DestroyString(const void* s) {
+  static_cast<const std::string*>(s)->~basic_string();
+}
+
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT
+    PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ExplicitlyConstructedArenaString
+        fixed_address_empty_string{};  // NOLINT
+
+
+PROTOBUF_CONSTINIT std::atomic<bool> init_protobuf_defaults_state{false};
+static bool InitProtobufDefaultsImpl() {
+  fixed_address_empty_string.DefaultConstruct();
+  OnShutdownDestroyString(fixed_address_empty_string.get_mutable());
+
+
+  init_protobuf_defaults_state.store(true, std::memory_order_release);
+  return true;
+}
+
+void InitProtobufDefaultsSlow() {
+  static bool is_inited = InitProtobufDefaultsImpl();
+  (void)is_inited;
+}
+// Force the initialization of the empty string.
+// Normally, registration would do it, but we don't have any guarantee that
+// there is any object with reflection.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 static std::true_type init_empty_string =
+    (InitProtobufDefaultsSlow(), std::true_type{});
+
+size_t StringSpaceUsedExcludingSelfLong(const std::string& str) {
+  const void* start = &str;
+  const void* end = &str + 1;
+  if (start <= str.data() && str.data() < end) {
+    // The string's data is stored inside the string object itself.
+    return 0;
+  } else {
+    return str.capacity();
+  }
+}
+
+template <typename T>
+const T& Get(const void* ptr) {
+  return *static_cast<const T*>(ptr);
+}
+
+// PrimitiveTypeHelper is a wrapper around the interface of WireFormatLite.
+// WireFormatLite has a very inconvenient interface with respect to template
+// meta-programming. This class wraps the different named functions into
+// a single Serialize / SerializeToArray interface.
+template <int type>
+struct PrimitiveTypeHelper;
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_BOOL> {
+  typedef bool Type;
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    WireFormatLite::WriteBoolNoTag(Get<bool>(ptr), output);
+  }
+  static uint8_t* SerializeToArray(const void* ptr, uint8_t* buffer) {
+    return WireFormatLite::WriteBoolNoTagToArray(Get<Type>(ptr), buffer);
+  }
+};
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_INT32> {
+  typedef int32_t Type;
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    WireFormatLite::WriteInt32NoTag(Get<int32_t>(ptr), output);
+  }
+  static uint8_t* SerializeToArray(const void* ptr, uint8_t* buffer) {
+    return WireFormatLite::WriteInt32NoTagToArray(Get<Type>(ptr), buffer);
+  }
+};
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_SINT32> {
+  typedef int32_t Type;
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    WireFormatLite::WriteSInt32NoTag(Get<int32_t>(ptr), output);
+  }
+  static uint8_t* SerializeToArray(const void* ptr, uint8_t* buffer) {
+    return WireFormatLite::WriteSInt32NoTagToArray(Get<Type>(ptr), buffer);
+  }
+};
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_UINT32> {
+  typedef uint32_t Type;
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    WireFormatLite::WriteUInt32NoTag(Get<uint32_t>(ptr), output);
+  }
+  static uint8_t* SerializeToArray(const void* ptr, uint8_t* buffer) {
+    return WireFormatLite::WriteUInt32NoTagToArray(Get<Type>(ptr), buffer);
+  }
+};
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_INT64> {
+  typedef int64_t Type;
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    WireFormatLite::WriteInt64NoTag(Get<int64_t>(ptr), output);
+  }
+  static uint8_t* SerializeToArray(const void* ptr, uint8_t* buffer) {
+    return WireFormatLite::WriteInt64NoTagToArray(Get<Type>(ptr), buffer);
+  }
+};
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_SINT64> {
+  typedef int64_t Type;
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    WireFormatLite::WriteSInt64NoTag(Get<int64_t>(ptr), output);
+  }
+  static uint8_t* SerializeToArray(const void* ptr, uint8_t* buffer) {
+    return WireFormatLite::WriteSInt64NoTagToArray(Get<Type>(ptr), buffer);
+  }
+};
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_UINT64> {
+  typedef uint64_t Type;
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    WireFormatLite::WriteUInt64NoTag(Get<uint64_t>(ptr), output);
+  }
+  static uint8_t* SerializeToArray(const void* ptr, uint8_t* buffer) {
+    return WireFormatLite::WriteUInt64NoTagToArray(Get<Type>(ptr), buffer);
+  }
+};
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_FIXED32> {
+  typedef uint32_t Type;
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    WireFormatLite::WriteFixed32NoTag(Get<uint32_t>(ptr), output);
+  }
+  static uint8_t* SerializeToArray(const void* ptr, uint8_t* buffer) {
+    return WireFormatLite::WriteFixed32NoTagToArray(Get<Type>(ptr), buffer);
+  }
+};
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_FIXED64> {
+  typedef uint64_t Type;
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    WireFormatLite::WriteFixed64NoTag(Get<uint64_t>(ptr), output);
+  }
+  static uint8_t* SerializeToArray(const void* ptr, uint8_t* buffer) {
+    return WireFormatLite::WriteFixed64NoTagToArray(Get<Type>(ptr), buffer);
+  }
+};
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_ENUM>
+    : PrimitiveTypeHelper<WireFormatLite::TYPE_INT32> {};
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_SFIXED32>
+    : PrimitiveTypeHelper<WireFormatLite::TYPE_FIXED32> {
+  typedef int32_t Type;
+};
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_SFIXED64>
+    : PrimitiveTypeHelper<WireFormatLite::TYPE_FIXED64> {
+  typedef int64_t Type;
+};
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_FLOAT>
+    : PrimitiveTypeHelper<WireFormatLite::TYPE_FIXED32> {
+  typedef float Type;
+};
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_DOUBLE>
+    : PrimitiveTypeHelper<WireFormatLite::TYPE_FIXED64> {
+  typedef double Type;
+};
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_STRING> {
+  typedef std::string Type;
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    const Type& value = *static_cast<const Type*>(ptr);
+    output->WriteVarint32(value.size());
+    output->WriteRawMaybeAliased(value.data(), value.size());
+  }
+  static uint8_t* SerializeToArray(const void* ptr, uint8_t* buffer) {
+    const Type& value = *static_cast<const Type*>(ptr);
+    return io::CodedOutputStream::WriteStringWithSizeToArray(value, buffer);
+  }
+};
+
+template <>
+struct PrimitiveTypeHelper<WireFormatLite::TYPE_BYTES>
+    : PrimitiveTypeHelper<WireFormatLite::TYPE_STRING> {};
+
+// We want to serialize to both CodedOutputStream and directly into byte arrays
+// without duplicating the code. In fact we might want extra output channels in
+// the future.
+template <typename O, int type>
+struct OutputHelper;
+
+template <int type, typename O>
+void SerializeTo(const void* ptr, O* output) {
+  OutputHelper<O, type>::Serialize(ptr, output);
+}
+
+template <typename O>
+void WriteTagTo(uint32_t tag, O* output) {
+  SerializeTo<WireFormatLite::TYPE_UINT32>(&tag, output);
+}
+
+template <typename O>
+void WriteLengthTo(uint32_t length, O* output) {
+  SerializeTo<WireFormatLite::TYPE_UINT32>(&length, output);
+}
+
+// Specialization for coded output stream
+template <int type>
+struct OutputHelper<io::CodedOutputStream, type> {
+  static void Serialize(const void* ptr, io::CodedOutputStream* output) {
+    PrimitiveTypeHelper<type>::Serialize(ptr, output);
+  }
+};
+
+// Specialization for writing into a plain array
+struct ArrayOutput {
+  uint8_t* ptr;
+  bool is_deterministic;
+};
+
+template <int type>
+struct OutputHelper<ArrayOutput, type> {
+  static void Serialize(const void* ptr, ArrayOutput* output) {
+    output->ptr = PrimitiveTypeHelper<type>::SerializeToArray(ptr, output->ptr);
+  }
+};
+
+void SerializeMessageNoTable(const MessageLite* msg,
+                             io::CodedOutputStream* output) {
+  msg->SerializeWithCachedSizes(output);
+}
+
+void SerializeMessageNoTable(const MessageLite* msg, ArrayOutput* output) {
+  io::ArrayOutputStream array_stream(output->ptr, INT_MAX);
+  io::CodedOutputStream o(&array_stream);
+  o.SetSerializationDeterministic(output->is_deterministic);
+  msg->SerializeWithCachedSizes(&o);
+  output->ptr += o.ByteCount();
+}
+
+// We need to use a helper class to get access to the private members
+class AccessorHelper {
+ public:
+  static int Size(const RepeatedPtrFieldBase& x) { return x.size(); }
+  static void const* Get(const RepeatedPtrFieldBase& x, int idx) {
+    return x.raw_data()[idx];
+  }
+};
+
+void SerializeNotImplemented(int field) {
+  GOOGLE_LOG(FATAL) << "Not implemented field number " << field;
+}
+
+// When switching to c++11 we should make these constexpr functions
+#define SERIALIZE_TABLE_OP(type, type_class) \
+  ((type - 1) + static_cast<int>(type_class) * FieldMetadata::kNumTypes)
+
+template <int type>
+bool IsNull(const void* ptr) {
+  return *static_cast<const typename PrimitiveTypeHelper<type>::Type*>(ptr) ==
+         0;
+}
+
+template <>
+bool IsNull<WireFormatLite::TYPE_STRING>(const void* ptr) {
+  return static_cast<const ArenaStringPtr*>(ptr)->Get().size() == 0;
+}
+
+template <>
+bool IsNull<WireFormatLite::TYPE_BYTES>(const void* ptr) {
+  return static_cast<const ArenaStringPtr*>(ptr)->Get().size() == 0;
+}
+
+template <>
+bool IsNull<WireFormatLite::TYPE_GROUP>(const void* ptr) {
+  return Get<const MessageLite*>(ptr) == nullptr;
+}
+
+template <>
+bool IsNull<WireFormatLite::TYPE_MESSAGE>(const void* ptr) {
+  return Get<const MessageLite*>(ptr) == nullptr;
+}
+
+void ExtensionSerializer(const MessageLite* extendee, const uint8_t* ptr,
+                         uint32_t offset, uint32_t tag, uint32_t has_offset,
+                         io::CodedOutputStream* output) {
+  reinterpret_cast<const ExtensionSet*>(ptr + offset)
+      ->SerializeWithCachedSizes(extendee, tag, has_offset, output);
+}
+
+void UnknownFieldSerializerLite(const uint8_t* ptr, uint32_t offset,
+                                uint32_t /*tag*/, uint32_t /*has_offset*/,
+                                io::CodedOutputStream* output) {
+  output->WriteString(
+      reinterpret_cast<const InternalMetadata*>(ptr + offset)
+          ->unknown_fields<std::string>(&internal::GetEmptyString));
+}
+
+MessageLite* DuplicateIfNonNullInternal(MessageLite* message) {
+  if (message) {
+    MessageLite* ret = message->New();
+    ret->CheckTypeAndMergeFrom(*message);
+    return ret;
+  } else {
+    return nullptr;
+  }
+}
+
+void GenericSwap(MessageLite* m1, MessageLite* m2) {
+  std::unique_ptr<MessageLite> tmp(m1->New());
+  tmp->CheckTypeAndMergeFrom(*m1);
+  m1->Clear();
+  m1->CheckTypeAndMergeFrom(*m2);
+  m2->Clear();
+  m2->CheckTypeAndMergeFrom(*tmp);
+}
+
+// Returns a message owned by this Arena.  This may require Own()ing or
+// duplicating the message.
+MessageLite* GetOwnedMessageInternal(Arena* message_arena,
+                                     MessageLite* submessage,
+                                     Arena* submessage_arena) {
+  GOOGLE_DCHECK(Arena::InternalGetOwningArena(submessage) == submessage_arena);
+  GOOGLE_DCHECK(message_arena != submessage_arena);
+  GOOGLE_DCHECK_EQ(submessage_arena, nullptr);
+  if (message_arena != nullptr && submessage_arena == nullptr) {
+    message_arena->Own(submessage);
+    return submessage;
+  } else {
+    MessageLite* ret = submessage->New(message_arena);
+    ret->CheckTypeAndMergeFrom(*submessage);
+    return ret;
+  }
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/implicit_weak_message.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/implicit_weak_message.cpp
new file mode 100644
index 0000000..27ed6b6
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/implicit_weak_message.cpp
@@ -0,0 +1,72 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/implicit_weak_message.h>
+
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+const char* ImplicitWeakMessage::_InternalParse(const char* ptr,
+                                                ParseContext* ctx) {
+  return ctx->AppendString(ptr, data_);
+}
+
+struct ImplicitWeakMessageDefaultType {
+  constexpr ImplicitWeakMessageDefaultType()
+      : instance(ConstantInitialized{}) {}
+  ~ImplicitWeakMessageDefaultType() {}
+  union {
+    ImplicitWeakMessage instance;
+  };
+};
+
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT ImplicitWeakMessageDefaultType
+    implicit_weak_message_default_instance;
+
+const ImplicitWeakMessage* ImplicitWeakMessage::default_instance() {
+  return reinterpret_cast<ImplicitWeakMessage*>(
+      &implicit_weak_message_default_instance);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/inlined_string_field.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/inlined_string_field.cpp
new file mode 100644
index 0000000..0c3e476
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/inlined_string_field.cpp
@@ -0,0 +1,118 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/inlined_string_field.h>
+
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/parse_context.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+
+std::string* InlinedStringField::Mutable(const LazyString& /*default_value*/,
+                                         Arena* arena, bool donated,
+                                         uint32_t* donating_states,
+                                         uint32_t mask, MessageLite* msg) {
+  if (arena == nullptr || !donated) {
+    return UnsafeMutablePointer();
+  }
+  return MutableSlow(arena, donated, donating_states, mask, msg);
+}
+
+std::string* InlinedStringField::Mutable(Arena* arena, bool donated,
+                                         uint32_t* donating_states,
+                                         uint32_t mask, MessageLite* msg) {
+  if (arena == nullptr || !donated) {
+    return UnsafeMutablePointer();
+  }
+  return MutableSlow(arena, donated, donating_states, mask, msg);
+}
+
+std::string* InlinedStringField::MutableSlow(::google::protobuf::Arena* arena,
+                                             bool donated,
+                                             uint32_t* donating_states,
+                                             uint32_t mask, MessageLite* msg) {
+  (void)mask;
+  (void)msg;
+  return UnsafeMutablePointer();
+}
+
+void InlinedStringField::SetAllocated(const std::string* default_value,
+                                      std::string* value, Arena* arena,
+                                      bool donated, uint32_t* donating_states,
+                                      uint32_t mask, MessageLite* msg) {
+  (void)mask;
+  (void)msg;
+  SetAllocatedNoArena(default_value, value);
+}
+
+void InlinedStringField::Set(std::string&& value, Arena* arena, bool donated,
+                             uint32_t* donating_states, uint32_t mask,
+                             MessageLite* msg) {
+  (void)donating_states;
+  (void)mask;
+  (void)msg;
+  SetNoArena(std::move(value));
+}
+
+std::string* InlinedStringField::Release() {
+  auto* released = new std::string(std::move(*get_mutable()));
+  get_mutable()->clear();
+  return released;
+}
+
+std::string* InlinedStringField::Release(Arena* arena, bool donated) {
+  // We can not steal donated arena strings.
+  std::string* released = (arena != nullptr && donated)
+                              ? new std::string(*get_mutable())
+                              : new std::string(std::move(*get_mutable()));
+  get_mutable()->clear();
+  return released;
+}
+
+void InlinedStringField::ClearToDefault(const LazyString& default_value,
+                                        Arena* arena, bool donated) {
+  (void)arena;
+  get_mutable()->assign(default_value.get());
+}
+
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/coded_stream.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/coded_stream.cpp
new file mode 100644
index 0000000..5399790
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/coded_stream.cpp
@@ -0,0 +1,967 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This implementation is heavily optimized to make reads and writes
+// of small values (especially varints) as fast as possible.  In
+// particular, we optimize for the common case that a read or a write
+// will not cross the end of the buffer, since we can avoid a lot
+// of branching in this case.
+
+#include <google/protobuf/io/coded_stream.h>
+
+#include <limits.h>
+
+#include <algorithm>
+#include <cstring>
+#include <utility>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+namespace {
+
+static const int kMaxVarintBytes = 10;
+static const int kMaxVarint32Bytes = 5;
+
+
+inline bool NextNonEmpty(ZeroCopyInputStream* input, const void** data,
+                         int* size) {
+  bool success;
+  do {
+    success = input->Next(data, size);
+  } while (success && *size == 0);
+  return success;
+}
+
+}  // namespace
+
+// CodedInputStream ==================================================
+
+CodedInputStream::~CodedInputStream() {
+  if (input_ != NULL) {
+    BackUpInputToCurrentPosition();
+  }
+}
+
+// Static.
+int CodedInputStream::default_recursion_limit_ = 100;
+
+
+void CodedInputStream::BackUpInputToCurrentPosition() {
+  int backup_bytes = BufferSize() + buffer_size_after_limit_ + overflow_bytes_;
+  if (backup_bytes > 0) {
+    input_->BackUp(backup_bytes);
+
+    // total_bytes_read_ doesn't include overflow_bytes_.
+    total_bytes_read_ -= BufferSize() + buffer_size_after_limit_;
+    buffer_end_ = buffer_;
+    buffer_size_after_limit_ = 0;
+    overflow_bytes_ = 0;
+  }
+}
+
+inline void CodedInputStream::RecomputeBufferLimits() {
+  buffer_end_ += buffer_size_after_limit_;
+  int closest_limit = std::min(current_limit_, total_bytes_limit_);
+  if (closest_limit < total_bytes_read_) {
+    // The limit position is in the current buffer.  We must adjust
+    // the buffer size accordingly.
+    buffer_size_after_limit_ = total_bytes_read_ - closest_limit;
+    buffer_end_ -= buffer_size_after_limit_;
+  } else {
+    buffer_size_after_limit_ = 0;
+  }
+}
+
+CodedInputStream::Limit CodedInputStream::PushLimit(int byte_limit) {
+  // Current position relative to the beginning of the stream.
+  int current_position = CurrentPosition();
+
+  Limit old_limit = current_limit_;
+
+  // security: byte_limit is possibly evil, so check for negative values
+  // and overflow. Also check that the new requested limit is before the
+  // previous limit; otherwise we continue to enforce the previous limit.
+  if (PROTOBUF_PREDICT_TRUE(byte_limit >= 0 &&
+                            byte_limit <= INT_MAX - current_position &&
+                            byte_limit < current_limit_ - current_position)) {
+    current_limit_ = current_position + byte_limit;
+    RecomputeBufferLimits();
+  }
+
+  return old_limit;
+}
+
+void CodedInputStream::PopLimit(Limit limit) {
+  // The limit passed in is actually the *old* limit, which we returned from
+  // PushLimit().
+  current_limit_ = limit;
+  RecomputeBufferLimits();
+
+  // We may no longer be at a legitimate message end.  ReadTag() needs to be
+  // called again to find out.
+  legitimate_message_end_ = false;
+}
+
+std::pair<CodedInputStream::Limit, int>
+CodedInputStream::IncrementRecursionDepthAndPushLimit(int byte_limit) {
+  return std::make_pair(PushLimit(byte_limit), --recursion_budget_);
+}
+
+CodedInputStream::Limit CodedInputStream::ReadLengthAndPushLimit() {
+  uint32_t length;
+  return PushLimit(ReadVarint32(&length) ? length : 0);
+}
+
+bool CodedInputStream::DecrementRecursionDepthAndPopLimit(Limit limit) {
+  bool result = ConsumedEntireMessage();
+  PopLimit(limit);
+  GOOGLE_DCHECK_LT(recursion_budget_, recursion_limit_);
+  ++recursion_budget_;
+  return result;
+}
+
+bool CodedInputStream::CheckEntireMessageConsumedAndPopLimit(Limit limit) {
+  bool result = ConsumedEntireMessage();
+  PopLimit(limit);
+  return result;
+}
+
+int CodedInputStream::BytesUntilLimit() const {
+  if (current_limit_ == INT_MAX) return -1;
+  int current_position = CurrentPosition();
+
+  return current_limit_ - current_position;
+}
+
+void CodedInputStream::SetTotalBytesLimit(int total_bytes_limit) {
+  // Make sure the limit isn't already past, since this could confuse other
+  // code.
+  int current_position = CurrentPosition();
+  total_bytes_limit_ = std::max(current_position, total_bytes_limit);
+  RecomputeBufferLimits();
+}
+
+int CodedInputStream::BytesUntilTotalBytesLimit() const {
+  if (total_bytes_limit_ == INT_MAX) return -1;
+  return total_bytes_limit_ - CurrentPosition();
+}
+
+void CodedInputStream::PrintTotalBytesLimitError() {
+  GOOGLE_LOG(ERROR)
+      << "A protocol message was rejected because it was too "
+         "big (more than "
+      << total_bytes_limit_
+      << " bytes).  To increase the limit (or to disable these "
+         "warnings), see CodedInputStream::SetTotalBytesLimit() "
+         "in third_party/protobuf/io/coded_stream.h.";
+}
+
+bool CodedInputStream::SkipFallback(int count, int original_buffer_size) {
+  if (buffer_size_after_limit_ > 0) {
+    // We hit a limit inside this buffer.  Advance to the limit and fail.
+    Advance(original_buffer_size);
+    return false;
+  }
+
+  count -= original_buffer_size;
+  buffer_ = NULL;
+  buffer_end_ = buffer_;
+
+  // Make sure this skip doesn't try to skip past the current limit.
+  int closest_limit = std::min(current_limit_, total_bytes_limit_);
+  int bytes_until_limit = closest_limit - total_bytes_read_;
+  if (bytes_until_limit < count) {
+    // We hit the limit.  Skip up to it then fail.
+    if (bytes_until_limit > 0) {
+      total_bytes_read_ = closest_limit;
+      input_->Skip(bytes_until_limit);
+    }
+    return false;
+  }
+
+  if (!input_->Skip(count)) {
+    total_bytes_read_ = input_->ByteCount();
+    return false;
+  }
+  total_bytes_read_ += count;
+  return true;
+}
+
+bool CodedInputStream::GetDirectBufferPointer(const void** data, int* size) {
+  if (BufferSize() == 0 && !Refresh()) return false;
+
+  *data = buffer_;
+  *size = BufferSize();
+  return true;
+}
+
+bool CodedInputStream::ReadRaw(void* buffer, int size) {
+  int current_buffer_size;
+  while ((current_buffer_size = BufferSize()) < size) {
+    // Reading past end of buffer.  Copy what we have, then refresh.
+    memcpy(buffer, buffer_, current_buffer_size);
+    buffer = reinterpret_cast<uint8_t*>(buffer) + current_buffer_size;
+    size -= current_buffer_size;
+    Advance(current_buffer_size);
+    if (!Refresh()) return false;
+  }
+
+  memcpy(buffer, buffer_, size);
+  Advance(size);
+
+  return true;
+}
+
+bool CodedInputStream::ReadString(std::string* buffer, int size) {
+  if (size < 0) return false;  // security: size is often user-supplied
+
+  if (BufferSize() >= size) {
+    STLStringResizeUninitialized(buffer, size);
+    std::pair<char*, bool> z = as_string_data(buffer);
+    if (z.second) {
+      // Oddly enough, memcpy() requires its first two args to be non-NULL even
+      // if we copy 0 bytes.  So, we have ensured that z.first is non-NULL here.
+      GOOGLE_DCHECK(z.first != NULL);
+      memcpy(z.first, buffer_, size);
+      Advance(size);
+    }
+    return true;
+  }
+
+  return ReadStringFallback(buffer, size);
+}
+
+bool CodedInputStream::ReadStringFallback(std::string* buffer, int size) {
+  if (!buffer->empty()) {
+    buffer->clear();
+  }
+
+  int closest_limit = std::min(current_limit_, total_bytes_limit_);
+  if (closest_limit != INT_MAX) {
+    int bytes_to_limit = closest_limit - CurrentPosition();
+    if (bytes_to_limit > 0 && size > 0 && size <= bytes_to_limit) {
+      buffer->reserve(size);
+    }
+  }
+
+  int current_buffer_size;
+  while ((current_buffer_size = BufferSize()) < size) {
+    // Some STL implementations "helpfully" crash on buffer->append(NULL, 0).
+    if (current_buffer_size != 0) {
+      // Note:  string1.append(string2) is O(string2.size()) (as opposed to
+      //   O(string1.size() + string2.size()), which would be bad).
+      buffer->append(reinterpret_cast<const char*>(buffer_),
+                     current_buffer_size);
+    }
+    size -= current_buffer_size;
+    Advance(current_buffer_size);
+    if (!Refresh()) return false;
+  }
+
+  buffer->append(reinterpret_cast<const char*>(buffer_), size);
+  Advance(size);
+
+  return true;
+}
+
+
+bool CodedInputStream::ReadLittleEndian32Fallback(uint32_t* value) {
+  uint8_t bytes[sizeof(*value)];
+
+  const uint8_t* ptr;
+  if (BufferSize() >= static_cast<int64_t>(sizeof(*value))) {
+    // Fast path:  Enough bytes in the buffer to read directly.
+    ptr = buffer_;
+    Advance(sizeof(*value));
+  } else {
+    // Slow path:  Had to read past the end of the buffer.
+    if (!ReadRaw(bytes, sizeof(*value))) return false;
+    ptr = bytes;
+  }
+  ReadLittleEndian32FromArray(ptr, value);
+  return true;
+}
+
+bool CodedInputStream::ReadLittleEndian64Fallback(uint64_t* value) {
+  uint8_t bytes[sizeof(*value)];
+
+  const uint8_t* ptr;
+  if (BufferSize() >= static_cast<int64_t>(sizeof(*value))) {
+    // Fast path:  Enough bytes in the buffer to read directly.
+    ptr = buffer_;
+    Advance(sizeof(*value));
+  } else {
+    // Slow path:  Had to read past the end of the buffer.
+    if (!ReadRaw(bytes, sizeof(*value))) return false;
+    ptr = bytes;
+  }
+  ReadLittleEndian64FromArray(ptr, value);
+  return true;
+}
+
+namespace {
+
+// Decodes varint64 with known size, N, and returns next pointer. Knowing N at
+// compile time, compiler can generate optimal code. For example, instead of
+// subtracting 0x80 at each iteration, it subtracts properly shifted mask once.
+template <size_t N>
+const uint8_t* DecodeVarint64KnownSize(const uint8_t* buffer, uint64_t* value) {
+  GOOGLE_DCHECK_GT(N, 0);
+  uint64_t result = static_cast<uint64_t>(buffer[N - 1]) << (7 * (N - 1));
+  for (size_t i = 0, offset = 0; i < N - 1; i++, offset += 7) {
+    result += static_cast<uint64_t>(buffer[i] - 0x80) << offset;
+  }
+  *value = result;
+  return buffer + N;
+}
+
+// Read a varint from the given buffer, write it to *value, and return a pair.
+// The first part of the pair is true iff the read was successful.  The second
+// part is buffer + (number of bytes read).  This function is always inlined,
+// so returning a pair is costless.
+PROTOBUF_ALWAYS_INLINE
+::std::pair<bool, const uint8_t*> ReadVarint32FromArray(uint32_t first_byte,
+                                                      const uint8_t* buffer,
+                                                      uint32_t* value);
+inline ::std::pair<bool, const uint8_t*> ReadVarint32FromArray(
+    uint32_t first_byte, const uint8_t* buffer, uint32_t* value) {
+  // Fast path:  We have enough bytes left in the buffer to guarantee that
+  // this read won't cross the end, so we can skip the checks.
+  GOOGLE_DCHECK_EQ(*buffer, first_byte);
+  GOOGLE_DCHECK_EQ(first_byte & 0x80, 0x80) << first_byte;
+  const uint8_t* ptr = buffer;
+  uint32_t b;
+  uint32_t result = first_byte - 0x80;
+  ++ptr;  // We just processed the first byte.  Move on to the second.
+  b = *(ptr++);
+  result += b << 7;
+  if (!(b & 0x80)) goto done;
+  result -= 0x80 << 7;
+  b = *(ptr++);
+  result += b << 14;
+  if (!(b & 0x80)) goto done;
+  result -= 0x80 << 14;
+  b = *(ptr++);
+  result += b << 21;
+  if (!(b & 0x80)) goto done;
+  result -= 0x80 << 21;
+  b = *(ptr++);
+  result += b << 28;
+  if (!(b & 0x80)) goto done;
+  // "result -= 0x80 << 28" is irrelevant.
+
+  // If the input is larger than 32 bits, we still need to read it all
+  // and discard the high-order bits.
+  for (int i = 0; i < kMaxVarintBytes - kMaxVarint32Bytes; i++) {
+    b = *(ptr++);
+    if (!(b & 0x80)) goto done;
+  }
+
+  // We have overrun the maximum size of a varint (10 bytes).  Assume
+  // the data is corrupt.
+  return std::make_pair(false, ptr);
+
+done:
+  *value = result;
+  return std::make_pair(true, ptr);
+}
+
+PROTOBUF_ALWAYS_INLINE::std::pair<bool, const uint8_t*> ReadVarint64FromArray(
+    const uint8_t* buffer, uint64_t* value);
+inline ::std::pair<bool, const uint8_t*> ReadVarint64FromArray(
+    const uint8_t* buffer, uint64_t* value) {
+  // Assumes varint64 is at least 2 bytes.
+  GOOGLE_DCHECK_GE(buffer[0], 128);
+
+  const uint8_t* next;
+  if (buffer[1] < 128) {
+    next = DecodeVarint64KnownSize<2>(buffer, value);
+  } else if (buffer[2] < 128) {
+    next = DecodeVarint64KnownSize<3>(buffer, value);
+  } else if (buffer[3] < 128) {
+    next = DecodeVarint64KnownSize<4>(buffer, value);
+  } else if (buffer[4] < 128) {
+    next = DecodeVarint64KnownSize<5>(buffer, value);
+  } else if (buffer[5] < 128) {
+    next = DecodeVarint64KnownSize<6>(buffer, value);
+  } else if (buffer[6] < 128) {
+    next = DecodeVarint64KnownSize<7>(buffer, value);
+  } else if (buffer[7] < 128) {
+    next = DecodeVarint64KnownSize<8>(buffer, value);
+  } else if (buffer[8] < 128) {
+    next = DecodeVarint64KnownSize<9>(buffer, value);
+  } else if (buffer[9] < 128) {
+    next = DecodeVarint64KnownSize<10>(buffer, value);
+  } else {
+    // We have overrun the maximum size of a varint (10 bytes). Assume
+    // the data is corrupt.
+    return std::make_pair(false, buffer + 11);
+  }
+
+  return std::make_pair(true, next);
+}
+
+}  // namespace
+
+bool CodedInputStream::ReadVarint32Slow(uint32_t* value) {
+  // Directly invoke ReadVarint64Fallback, since we already tried to optimize
+  // for one-byte varints.
+  std::pair<uint64_t, bool> p = ReadVarint64Fallback();
+  *value = static_cast<uint32_t>(p.first);
+  return p.second;
+}
+
+int64_t CodedInputStream::ReadVarint32Fallback(uint32_t first_byte_or_zero) {
+  if (BufferSize() >= kMaxVarintBytes ||
+      // Optimization:  We're also safe if the buffer is non-empty and it ends
+      // with a byte that would terminate a varint.
+      (buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) {
+    GOOGLE_DCHECK_NE(first_byte_or_zero, 0)
+        << "Caller should provide us with *buffer_ when buffer is non-empty";
+    uint32_t temp = 0;
+    ::std::pair<bool, const uint8_t*> p =
+        ReadVarint32FromArray(first_byte_or_zero, buffer_, &temp);
+    if (!p.first) return -1;
+    buffer_ = p.second;
+    return temp;
+  } else {
+    // Really slow case: we will incur the cost of an extra function call here,
+    // but moving this out of line reduces the size of this function, which
+    // improves the common case. In micro benchmarks, this is worth about 10-15%
+    uint32_t temp;
+    return ReadVarint32Slow(&temp) ? static_cast<int64_t>(temp) : -1;
+  }
+}
+
+int CodedInputStream::ReadVarintSizeAsIntSlow() {
+  // Directly invoke ReadVarint64Fallback, since we already tried to optimize
+  // for one-byte varints.
+  std::pair<uint64_t, bool> p = ReadVarint64Fallback();
+  if (!p.second || p.first > static_cast<uint64_t>(INT_MAX)) return -1;
+  return p.first;
+}
+
+int CodedInputStream::ReadVarintSizeAsIntFallback() {
+  if (BufferSize() >= kMaxVarintBytes ||
+      // Optimization:  We're also safe if the buffer is non-empty and it ends
+      // with a byte that would terminate a varint.
+      (buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) {
+    uint64_t temp;
+    ::std::pair<bool, const uint8_t*> p = ReadVarint64FromArray(buffer_, &temp);
+    if (!p.first || temp > static_cast<uint64_t>(INT_MAX)) return -1;
+    buffer_ = p.second;
+    return temp;
+  } else {
+    // Really slow case: we will incur the cost of an extra function call here,
+    // but moving this out of line reduces the size of this function, which
+    // improves the common case. In micro benchmarks, this is worth about 10-15%
+    return ReadVarintSizeAsIntSlow();
+  }
+}
+
+uint32_t CodedInputStream::ReadTagSlow() {
+  if (buffer_ == buffer_end_) {
+    // Call refresh.
+    if (!Refresh()) {
+      // Refresh failed.  Make sure that it failed due to EOF, not because
+      // we hit total_bytes_limit_, which, unlike normal limits, is not a
+      // valid place to end a message.
+      int current_position = total_bytes_read_ - buffer_size_after_limit_;
+      if (current_position >= total_bytes_limit_) {
+        // Hit total_bytes_limit_.  But if we also hit the normal limit,
+        // we're still OK.
+        legitimate_message_end_ = current_limit_ == total_bytes_limit_;
+      } else {
+        legitimate_message_end_ = true;
+      }
+      return 0;
+    }
+  }
+
+  // For the slow path, just do a 64-bit read. Try to optimize for one-byte tags
+  // again, since we have now refreshed the buffer.
+  uint64_t result = 0;
+  if (!ReadVarint64(&result)) return 0;
+  return static_cast<uint32_t>(result);
+}
+
+uint32_t CodedInputStream::ReadTagFallback(uint32_t first_byte_or_zero) {
+  const int buf_size = BufferSize();
+  if (buf_size >= kMaxVarintBytes ||
+      // Optimization:  We're also safe if the buffer is non-empty and it ends
+      // with a byte that would terminate a varint.
+      (buf_size > 0 && !(buffer_end_[-1] & 0x80))) {
+    GOOGLE_DCHECK_EQ(first_byte_or_zero, buffer_[0]);
+    if (first_byte_or_zero == 0) {
+      ++buffer_;
+      return 0;
+    }
+    uint32_t tag;
+    ::std::pair<bool, const uint8_t*> p =
+        ReadVarint32FromArray(first_byte_or_zero, buffer_, &tag);
+    if (!p.first) {
+      return 0;
+    }
+    buffer_ = p.second;
+    return tag;
+  } else {
+    // We are commonly at a limit when attempting to read tags. Try to quickly
+    // detect this case without making another function call.
+    if ((buf_size == 0) &&
+        ((buffer_size_after_limit_ > 0) ||
+         (total_bytes_read_ == current_limit_)) &&
+        // Make sure that the limit we hit is not total_bytes_limit_, since
+        // in that case we still need to call Refresh() so that it prints an
+        // error.
+        total_bytes_read_ - buffer_size_after_limit_ < total_bytes_limit_) {
+      // We hit a byte limit.
+      legitimate_message_end_ = true;
+      return 0;
+    }
+    return ReadTagSlow();
+  }
+}
+
+bool CodedInputStream::ReadVarint64Slow(uint64_t* value) {
+  // Slow path:  This read might cross the end of the buffer, so we
+  // need to check and refresh the buffer if and when it does.
+
+  uint64_t result = 0;
+  int count = 0;
+  uint32_t b;
+
+  do {
+    if (count == kMaxVarintBytes) {
+      *value = 0;
+      return false;
+    }
+    while (buffer_ == buffer_end_) {
+      if (!Refresh()) {
+        *value = 0;
+        return false;
+      }
+    }
+    b = *buffer_;
+    result |= static_cast<uint64_t>(b & 0x7F) << (7 * count);
+    Advance(1);
+    ++count;
+  } while (b & 0x80);
+
+  *value = result;
+  return true;
+}
+
+std::pair<uint64_t, bool> CodedInputStream::ReadVarint64Fallback() {
+  if (BufferSize() >= kMaxVarintBytes ||
+      // Optimization:  We're also safe if the buffer is non-empty and it ends
+      // with a byte that would terminate a varint.
+      (buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) {
+    uint64_t temp;
+    ::std::pair<bool, const uint8_t*> p = ReadVarint64FromArray(buffer_, &temp);
+    if (!p.first) {
+      return std::make_pair(0, false);
+    }
+    buffer_ = p.second;
+    return std::make_pair(temp, true);
+  } else {
+    uint64_t temp;
+    bool success = ReadVarint64Slow(&temp);
+    return std::make_pair(temp, success);
+  }
+}
+
+bool CodedInputStream::Refresh() {
+  GOOGLE_DCHECK_EQ(0, BufferSize());
+
+  if (buffer_size_after_limit_ > 0 || overflow_bytes_ > 0 ||
+      total_bytes_read_ == current_limit_) {
+    // We've hit a limit.  Stop.
+    int current_position = total_bytes_read_ - buffer_size_after_limit_;
+
+    if (current_position >= total_bytes_limit_ &&
+        total_bytes_limit_ != current_limit_) {
+      // Hit total_bytes_limit_.
+      PrintTotalBytesLimitError();
+    }
+
+    return false;
+  }
+
+  const void* void_buffer;
+  int buffer_size;
+  if (NextNonEmpty(input_, &void_buffer, &buffer_size)) {
+    buffer_ = reinterpret_cast<const uint8_t*>(void_buffer);
+    buffer_end_ = buffer_ + buffer_size;
+    GOOGLE_CHECK_GE(buffer_size, 0);
+
+    if (total_bytes_read_ <= INT_MAX - buffer_size) {
+      total_bytes_read_ += buffer_size;
+    } else {
+      // Overflow.  Reset buffer_end_ to not include the bytes beyond INT_MAX.
+      // We can't get that far anyway, because total_bytes_limit_ is guaranteed
+      // to be less than it.  We need to keep track of the number of bytes
+      // we discarded, though, so that we can call input_->BackUp() to back
+      // up over them on destruction.
+
+      // The following line is equivalent to:
+      //   overflow_bytes_ = total_bytes_read_ + buffer_size - INT_MAX;
+      // except that it avoids overflows.  Signed integer overflow has
+      // undefined results according to the C standard.
+      overflow_bytes_ = total_bytes_read_ - (INT_MAX - buffer_size);
+      buffer_end_ -= overflow_bytes_;
+      total_bytes_read_ = INT_MAX;
+    }
+
+    RecomputeBufferLimits();
+    return true;
+  } else {
+    buffer_ = NULL;
+    buffer_end_ = NULL;
+    return false;
+  }
+}
+
+// CodedOutputStream =================================================
+
+void EpsCopyOutputStream::EnableAliasing(bool enabled) {
+  aliasing_enabled_ = enabled && stream_->AllowsAliasing();
+}
+
+int64_t EpsCopyOutputStream::ByteCount(uint8_t* ptr) const {
+  // Calculate the current offset relative to the end of the stream buffer.
+  int delta = (end_ - ptr) + (buffer_end_ ? 0 : kSlopBytes);
+  return stream_->ByteCount() - delta;
+}
+
+// Flushes what's written out to the underlying ZeroCopyOutputStream buffers.
+// Returns the size remaining in the buffer and sets buffer_end_ to the start
+// of the remaining buffer, ie. [buffer_end_, buffer_end_ + return value)
+int EpsCopyOutputStream::Flush(uint8_t* ptr) {
+  while (buffer_end_ && ptr > end_) {
+    int overrun = ptr - end_;
+    GOOGLE_DCHECK(!had_error_);
+    GOOGLE_DCHECK(overrun <= kSlopBytes);  // NOLINT
+    ptr = Next() + overrun;
+    if (had_error_) return 0;
+  }
+  int s;
+  if (buffer_end_) {
+    std::memcpy(buffer_end_, buffer_, ptr - buffer_);
+    buffer_end_ += ptr - buffer_;
+    s = end_ - ptr;
+  } else {
+    // The stream is writing directly in the ZeroCopyOutputStream buffer.
+    s = end_ + kSlopBytes - ptr;
+    buffer_end_ = ptr;
+  }
+  GOOGLE_DCHECK(s >= 0);  // NOLINT
+  return s;
+}
+
+uint8_t* EpsCopyOutputStream::Trim(uint8_t* ptr) {
+  if (had_error_) return ptr;
+  int s = Flush(ptr);
+  stream_->BackUp(s);
+  // Reset to initial state (expecting new buffer)
+  buffer_end_ = end_ = buffer_;
+  return buffer_;
+}
+
+
+uint8_t* EpsCopyOutputStream::FlushAndResetBuffer(uint8_t* ptr) {
+  if (had_error_) return buffer_;
+  int s = Flush(ptr);
+  if (had_error_) return buffer_;
+  return SetInitialBuffer(buffer_end_, s);
+}
+
+bool EpsCopyOutputStream::Skip(int count, uint8_t** pp) {
+  if (count < 0) return false;
+  if (had_error_) {
+    *pp = buffer_;
+    return false;
+  }
+  int size = Flush(*pp);
+  if (had_error_) {
+    *pp = buffer_;
+    return false;
+  }
+  void* data = buffer_end_;
+  while (count > size) {
+    count -= size;
+    if (!stream_->Next(&data, &size)) {
+      *pp = Error();
+      return false;
+    }
+  }
+  *pp = SetInitialBuffer(static_cast<uint8_t*>(data) + count, size - count);
+  return true;
+}
+
+bool EpsCopyOutputStream::GetDirectBufferPointer(void** data, int* size,
+                                                 uint8_t** pp) {
+  if (had_error_) {
+    *pp = buffer_;
+    return false;
+  }
+  *size = Flush(*pp);
+  if (had_error_) {
+    *pp = buffer_;
+    return false;
+  }
+  *data = buffer_end_;
+  while (*size == 0) {
+    if (!stream_->Next(data, size)) {
+      *pp = Error();
+      return false;
+    }
+  }
+  *pp = SetInitialBuffer(*data, *size);
+  return true;
+}
+
+uint8_t* EpsCopyOutputStream::GetDirectBufferForNBytesAndAdvance(int size,
+                                                               uint8_t** pp) {
+  if (had_error_) {
+    *pp = buffer_;
+    return nullptr;
+  }
+  int s = Flush(*pp);
+  if (had_error_) {
+    *pp = buffer_;
+    return nullptr;
+  }
+  if (s >= size) {
+    auto res = buffer_end_;
+    *pp = SetInitialBuffer(buffer_end_ + size, s - size);
+    return res;
+  } else {
+    *pp = SetInitialBuffer(buffer_end_, s);
+    return nullptr;
+  }
+}
+
+uint8_t* EpsCopyOutputStream::Next() {
+  GOOGLE_DCHECK(!had_error_);  // NOLINT
+  if (PROTOBUF_PREDICT_FALSE(stream_ == nullptr)) return Error();
+  if (buffer_end_) {
+    // We're in the patch buffer and need to fill up the previous buffer.
+    std::memcpy(buffer_end_, buffer_, end_ - buffer_);
+    uint8_t* ptr;
+    int size;
+    do {
+      void* data;
+      if (PROTOBUF_PREDICT_FALSE(!stream_->Next(&data, &size))) {
+        // Stream has an error, we use the patch buffer to continue to be
+        // able to write.
+        return Error();
+      }
+      ptr = static_cast<uint8_t*>(data);
+    } while (size == 0);
+    if (PROTOBUF_PREDICT_TRUE(size > kSlopBytes)) {
+      std::memcpy(ptr, end_, kSlopBytes);
+      end_ = ptr + size - kSlopBytes;
+      buffer_end_ = nullptr;
+      return ptr;
+    } else {
+      GOOGLE_DCHECK(size > 0);  // NOLINT
+      // Buffer to small
+      std::memmove(buffer_, end_, kSlopBytes);
+      buffer_end_ = ptr;
+      end_ = buffer_ + size;
+      return buffer_;
+    }
+  } else {
+    std::memcpy(buffer_, end_, kSlopBytes);
+    buffer_end_ = end_;
+    end_ = buffer_ + kSlopBytes;
+    return buffer_;
+  }
+}
+
+uint8_t* EpsCopyOutputStream::EnsureSpaceFallback(uint8_t* ptr) {
+  do {
+    if (PROTOBUF_PREDICT_FALSE(had_error_)) return buffer_;
+    int overrun = ptr - end_;
+    GOOGLE_DCHECK(overrun >= 0);           // NOLINT
+    GOOGLE_DCHECK(overrun <= kSlopBytes);  // NOLINT
+    ptr = Next() + overrun;
+  } while (ptr >= end_);
+  GOOGLE_DCHECK(ptr < end_);  // NOLINT
+  return ptr;
+}
+
+uint8_t* EpsCopyOutputStream::WriteRawFallback(const void* data, int size,
+                                             uint8_t* ptr) {
+  int s = GetSize(ptr);
+  while (s < size) {
+    std::memcpy(ptr, data, s);
+    size -= s;
+    data = static_cast<const uint8_t*>(data) + s;
+    ptr = EnsureSpaceFallback(ptr + s);
+    s = GetSize(ptr);
+  }
+  std::memcpy(ptr, data, size);
+  return ptr + size;
+}
+
+uint8_t* EpsCopyOutputStream::WriteAliasedRaw(const void* data, int size,
+                                            uint8_t* ptr) {
+  if (size < GetSize(ptr)
+  ) {
+    return WriteRaw(data, size, ptr);
+  } else {
+    ptr = Trim(ptr);
+    if (stream_->WriteAliasedRaw(data, size)) return ptr;
+    return Error();
+  }
+}
+
+#ifndef PROTOBUF_LITTLE_ENDIAN
+uint8_t* EpsCopyOutputStream::WriteRawLittleEndian32(const void* data, int size,
+                                                   uint8_t* ptr) {
+  auto p = static_cast<const uint8_t*>(data);
+  auto end = p + size;
+  while (end - p >= kSlopBytes) {
+    ptr = EnsureSpace(ptr);
+    uint32_t buffer[4];
+    static_assert(sizeof(buffer) == kSlopBytes, "Buffer must be kSlopBytes");
+    std::memcpy(buffer, p, kSlopBytes);
+    p += kSlopBytes;
+    for (auto x : buffer)
+      ptr = CodedOutputStream::WriteLittleEndian32ToArray(x, ptr);
+  }
+  while (p < end) {
+    ptr = EnsureSpace(ptr);
+    uint32_t buffer;
+    std::memcpy(&buffer, p, 4);
+    p += 4;
+    ptr = CodedOutputStream::WriteLittleEndian32ToArray(buffer, ptr);
+  }
+  return ptr;
+}
+
+uint8_t* EpsCopyOutputStream::WriteRawLittleEndian64(const void* data, int size,
+                                                   uint8_t* ptr) {
+  auto p = static_cast<const uint8_t*>(data);
+  auto end = p + size;
+  while (end - p >= kSlopBytes) {
+    ptr = EnsureSpace(ptr);
+    uint64_t buffer[2];
+    static_assert(sizeof(buffer) == kSlopBytes, "Buffer must be kSlopBytes");
+    std::memcpy(buffer, p, kSlopBytes);
+    p += kSlopBytes;
+    for (auto x : buffer)
+      ptr = CodedOutputStream::WriteLittleEndian64ToArray(x, ptr);
+  }
+  while (p < end) {
+    ptr = EnsureSpace(ptr);
+    uint64_t buffer;
+    std::memcpy(&buffer, p, 8);
+    p += 8;
+    ptr = CodedOutputStream::WriteLittleEndian64ToArray(buffer, ptr);
+  }
+  return ptr;
+}
+#endif
+
+
+uint8_t* EpsCopyOutputStream::WriteStringMaybeAliasedOutline(uint32_t num,
+                                                           const std::string& s,
+                                                           uint8_t* ptr) {
+  ptr = EnsureSpace(ptr);
+  uint32_t size = s.size();
+  ptr = WriteLengthDelim(num, size, ptr);
+  return WriteRawMaybeAliased(s.data(), size, ptr);
+}
+
+uint8_t* EpsCopyOutputStream::WriteStringOutline(uint32_t num, const std::string& s,
+                                               uint8_t* ptr) {
+  ptr = EnsureSpace(ptr);
+  uint32_t size = s.size();
+  ptr = WriteLengthDelim(num, size, ptr);
+  return WriteRaw(s.data(), size, ptr);
+}
+
+std::atomic<bool> CodedOutputStream::default_serialization_deterministic_{
+    false};
+
+CodedOutputStream::~CodedOutputStream() { Trim(); }
+
+
+uint8_t* CodedOutputStream::WriteStringWithSizeToArray(const std::string& str,
+                                                     uint8_t* target) {
+  GOOGLE_DCHECK_LE(str.size(), std::numeric_limits<uint32_t>::max());
+  target = WriteVarint32ToArray(str.size(), target);
+  return WriteStringToArray(str, target);
+}
+
+uint8_t* CodedOutputStream::WriteVarint32ToArrayOutOfLineHelper(uint32_t value,
+                                                              uint8_t* target) {
+  GOOGLE_DCHECK_GE(value, 0x80);
+  target[0] |= static_cast<uint8_t>(0x80);
+  value >>= 7;
+  target[1] = static_cast<uint8_t>(value);
+  if (value < 0x80) {
+    return target + 2;
+  }
+  target += 2;
+  do {
+    // Turn on continuation bit in the byte we just wrote.
+    target[-1] |= static_cast<uint8_t>(0x80);
+    value >>= 7;
+    *target = static_cast<uint8_t>(value);
+    ++target;
+  } while (value >= 0x80);
+  return target;
+}
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/gzip_stream.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/gzip_stream.cpp
new file mode 100644
index 0000000..a5284b3
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/gzip_stream.cpp
@@ -0,0 +1,334 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: brianolson@google.com (Brian Olson)
+//
+// This file contains the implementation of classes GzipInputStream and
+// GzipOutputStream.
+
+
+#if HAVE_ZLIB
+#include <google/protobuf/io/gzip_stream.h>
+#include <google/protobuf/port.h>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+static const int kDefaultBufferSize = 65536;
+
+GzipInputStream::GzipInputStream(ZeroCopyInputStream* sub_stream, Format format,
+                                 int buffer_size)
+    : format_(format), sub_stream_(sub_stream), zerror_(Z_OK), byte_count_(0) {
+  zcontext_.state = Z_NULL;
+  zcontext_.zalloc = Z_NULL;
+  zcontext_.zfree = Z_NULL;
+  zcontext_.opaque = Z_NULL;
+  zcontext_.total_out = 0;
+  zcontext_.next_in = NULL;
+  zcontext_.avail_in = 0;
+  zcontext_.total_in = 0;
+  zcontext_.msg = NULL;
+  if (buffer_size == -1) {
+    output_buffer_length_ = kDefaultBufferSize;
+  } else {
+    output_buffer_length_ = buffer_size;
+  }
+  output_buffer_ = operator new(output_buffer_length_);
+  GOOGLE_CHECK(output_buffer_ != NULL);
+  zcontext_.next_out = static_cast<Bytef*>(output_buffer_);
+  zcontext_.avail_out = output_buffer_length_;
+  output_position_ = output_buffer_;
+}
+GzipInputStream::~GzipInputStream() {
+  internal::SizedDelete(output_buffer_, output_buffer_length_);
+  zerror_ = inflateEnd(&zcontext_);
+}
+
+static inline int internalInflateInit2(z_stream* zcontext,
+                                       GzipInputStream::Format format) {
+  int windowBitsFormat = 0;
+  switch (format) {
+    case GzipInputStream::GZIP:
+      windowBitsFormat = 16;
+      break;
+    case GzipInputStream::AUTO:
+      windowBitsFormat = 32;
+      break;
+    case GzipInputStream::ZLIB:
+      windowBitsFormat = 0;
+      break;
+  }
+  return inflateInit2(zcontext, /* windowBits */ 15 | windowBitsFormat);
+}
+
+int GzipInputStream::Inflate(int flush) {
+  if ((zerror_ == Z_OK) && (zcontext_.avail_out == 0)) {
+    // previous inflate filled output buffer. don't change input params yet.
+  } else if (zcontext_.avail_in == 0) {
+    const void* in;
+    int in_size;
+    bool first = zcontext_.next_in == NULL;
+    bool ok = sub_stream_->Next(&in, &in_size);
+    if (!ok) {
+      zcontext_.next_out = NULL;
+      zcontext_.avail_out = 0;
+      return Z_STREAM_END;
+    }
+    zcontext_.next_in = static_cast<Bytef*>(const_cast<void*>(in));
+    zcontext_.avail_in = in_size;
+    if (first) {
+      int error = internalInflateInit2(&zcontext_, format_);
+      if (error != Z_OK) {
+        return error;
+      }
+    }
+  }
+  zcontext_.next_out = static_cast<Bytef*>(output_buffer_);
+  zcontext_.avail_out = output_buffer_length_;
+  output_position_ = output_buffer_;
+  int error = inflate(&zcontext_, flush);
+  return error;
+}
+
+void GzipInputStream::DoNextOutput(const void** data, int* size) {
+  *data = output_position_;
+  *size = ((uintptr_t)zcontext_.next_out) - ((uintptr_t)output_position_);
+  output_position_ = zcontext_.next_out;
+}
+
+// implements ZeroCopyInputStream ----------------------------------
+bool GzipInputStream::Next(const void** data, int* size) {
+  bool ok = (zerror_ == Z_OK) || (zerror_ == Z_STREAM_END) ||
+            (zerror_ == Z_BUF_ERROR);
+  if ((!ok) || (zcontext_.next_out == NULL)) {
+    return false;
+  }
+  if (zcontext_.next_out != output_position_) {
+    DoNextOutput(data, size);
+    return true;
+  }
+  if (zerror_ == Z_STREAM_END) {
+    if (zcontext_.next_out != NULL) {
+      // sub_stream_ may have concatenated streams to follow
+      zerror_ = inflateEnd(&zcontext_);
+      byte_count_ += zcontext_.total_out;
+      if (zerror_ != Z_OK) {
+        return false;
+      }
+      zerror_ = internalInflateInit2(&zcontext_, format_);
+      if (zerror_ != Z_OK) {
+        return false;
+      }
+    } else {
+      *data = NULL;
+      *size = 0;
+      return false;
+    }
+  }
+  zerror_ = Inflate(Z_NO_FLUSH);
+  if ((zerror_ == Z_STREAM_END) && (zcontext_.next_out == NULL)) {
+    // The underlying stream's Next returned false inside Inflate.
+    return false;
+  }
+  ok = (zerror_ == Z_OK) || (zerror_ == Z_STREAM_END) ||
+       (zerror_ == Z_BUF_ERROR);
+  if (!ok) {
+    return false;
+  }
+  DoNextOutput(data, size);
+  return true;
+}
+void GzipInputStream::BackUp(int count) {
+  output_position_ = reinterpret_cast<void*>(
+      reinterpret_cast<uintptr_t>(output_position_) - count);
+}
+bool GzipInputStream::Skip(int count) {
+  const void* data;
+  int size = 0;
+  bool ok = Next(&data, &size);
+  while (ok && (size < count)) {
+    count -= size;
+    ok = Next(&data, &size);
+  }
+  if (size > count) {
+    BackUp(size - count);
+  }
+  return ok;
+}
+int64_t GzipInputStream::ByteCount() const {
+  int64_t ret = byte_count_ + zcontext_.total_out;
+  if (zcontext_.next_out != NULL && output_position_ != NULL) {
+    ret += reinterpret_cast<uintptr_t>(zcontext_.next_out) -
+           reinterpret_cast<uintptr_t>(output_position_);
+  }
+  return ret;
+}
+
+// =========================================================================
+
+GzipOutputStream::Options::Options()
+    : format(GZIP),
+      buffer_size(kDefaultBufferSize),
+      compression_level(Z_DEFAULT_COMPRESSION),
+      compression_strategy(Z_DEFAULT_STRATEGY) {}
+
+GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream) {
+  Init(sub_stream, Options());
+}
+
+GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream,
+                                   const Options& options) {
+  Init(sub_stream, options);
+}
+
+void GzipOutputStream::Init(ZeroCopyOutputStream* sub_stream,
+                            const Options& options) {
+  sub_stream_ = sub_stream;
+  sub_data_ = NULL;
+  sub_data_size_ = 0;
+
+  input_buffer_length_ = options.buffer_size;
+  input_buffer_ = operator new(input_buffer_length_);
+  GOOGLE_CHECK(input_buffer_ != NULL);
+
+  zcontext_.zalloc = Z_NULL;
+  zcontext_.zfree = Z_NULL;
+  zcontext_.opaque = Z_NULL;
+  zcontext_.next_out = NULL;
+  zcontext_.avail_out = 0;
+  zcontext_.total_out = 0;
+  zcontext_.next_in = NULL;
+  zcontext_.avail_in = 0;
+  zcontext_.total_in = 0;
+  zcontext_.msg = NULL;
+  // default to GZIP format
+  int windowBitsFormat = 16;
+  if (options.format == ZLIB) {
+    windowBitsFormat = 0;
+  }
+  zerror_ =
+      deflateInit2(&zcontext_, options.compression_level, Z_DEFLATED,
+                   /* windowBits */ 15 | windowBitsFormat,
+                   /* memLevel (default) */ 8, options.compression_strategy);
+}
+
+GzipOutputStream::~GzipOutputStream() {
+  Close();
+  internal::SizedDelete(input_buffer_, input_buffer_length_);
+}
+
+// private
+int GzipOutputStream::Deflate(int flush) {
+  int error = Z_OK;
+  do {
+    if ((sub_data_ == NULL) || (zcontext_.avail_out == 0)) {
+      bool ok = sub_stream_->Next(&sub_data_, &sub_data_size_);
+      if (!ok) {
+        sub_data_ = NULL;
+        sub_data_size_ = 0;
+        return Z_BUF_ERROR;
+      }
+      GOOGLE_CHECK_GT(sub_data_size_, 0);
+      zcontext_.next_out = static_cast<Bytef*>(sub_data_);
+      zcontext_.avail_out = sub_data_size_;
+    }
+    error = deflate(&zcontext_, flush);
+  } while (error == Z_OK && zcontext_.avail_out == 0);
+  if ((flush == Z_FULL_FLUSH) || (flush == Z_FINISH)) {
+    // Notify lower layer of data.
+    sub_stream_->BackUp(zcontext_.avail_out);
+    // We don't own the buffer anymore.
+    sub_data_ = NULL;
+    sub_data_size_ = 0;
+  }
+  return error;
+}
+
+// implements ZeroCopyOutputStream ---------------------------------
+bool GzipOutputStream::Next(void** data, int* size) {
+  if ((zerror_ != Z_OK) && (zerror_ != Z_BUF_ERROR)) {
+    return false;
+  }
+  if (zcontext_.avail_in != 0) {
+    zerror_ = Deflate(Z_NO_FLUSH);
+    if (zerror_ != Z_OK) {
+      return false;
+    }
+  }
+  if (zcontext_.avail_in == 0) {
+    // all input was consumed. reset the buffer.
+    zcontext_.next_in = static_cast<Bytef*>(input_buffer_);
+    zcontext_.avail_in = input_buffer_length_;
+    *data = input_buffer_;
+    *size = input_buffer_length_;
+  } else {
+    // The loop in Deflate should consume all avail_in
+    GOOGLE_LOG(DFATAL) << "Deflate left bytes unconsumed";
+  }
+  return true;
+}
+void GzipOutputStream::BackUp(int count) {
+  GOOGLE_CHECK_GE(zcontext_.avail_in, static_cast<uInt>(count));
+  zcontext_.avail_in -= count;
+}
+int64_t GzipOutputStream::ByteCount() const {
+  return zcontext_.total_in + zcontext_.avail_in;
+}
+
+bool GzipOutputStream::Flush() {
+  zerror_ = Deflate(Z_FULL_FLUSH);
+  // Return true if the flush succeeded or if it was a no-op.
+  return (zerror_ == Z_OK) ||
+         (zerror_ == Z_BUF_ERROR && zcontext_.avail_in == 0 &&
+          zcontext_.avail_out != 0);
+}
+
+bool GzipOutputStream::Close() {
+  if ((zerror_ != Z_OK) && (zerror_ != Z_BUF_ERROR)) {
+    return false;
+  }
+  do {
+    zerror_ = Deflate(Z_FINISH);
+  } while (zerror_ == Z_OK);
+  zerror_ = deflateEnd(&zcontext_);
+  bool ok = zerror_ == Z_OK;
+  zerror_ = Z_STREAM_END;
+  return ok;
+}
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // HAVE_ZLIB
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/io_win32.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/io_win32.cpp
new file mode 100644
index 0000000..78c07d0
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/io_win32.cpp
@@ -0,0 +1,471 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: laszlocsomor@google.com (Laszlo Csomor)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+// Implementation for long-path-aware open/mkdir/access/etc. on Windows, as well
+// as for the supporting utility functions.
+//
+// These functions convert the input path to an absolute Windows path
+// with "\\?\" prefix, then pass that to _wopen/_wmkdir/_waccess/etc.
+// (declared in <io.h>) respectively. This allows working with files/directories
+// whose paths are longer than MAX_PATH (260 chars).
+//
+// This file is only used on Windows, it's empty on other platforms.
+
+#if defined(_WIN32) && !defined(_XBOX_ONE)
+
+// Comment this out to fall back to using the ANSI versions (open, mkdir, ...)
+// instead of the Unicode ones (_wopen, _wmkdir, ...). Doing so can be useful to
+// debug failing tests if that's caused by the long path support.
+#define SUPPORT_LONGPATHS
+
+#include <google/protobuf/io/io_win32.h>
+
+#include <ctype.h>
+#include <direct.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <wctype.h>
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+
+#include <windows.h>
+
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace google {
+namespace protobuf {
+namespace io {
+namespace win32 {
+namespace {
+
+using std::string;
+using std::wstring;
+
+template <typename char_type>
+struct CharTraits {
+  static bool is_alpha(char_type ch);
+};
+
+template <>
+struct CharTraits<char> {
+  static bool is_alpha(char ch) { return isalpha(ch); }
+};
+
+template <>
+struct CharTraits<wchar_t> {
+  static bool is_alpha(wchar_t ch) { return iswalpha(ch); }
+};
+
+template <typename char_type>
+bool null_or_empty(const char_type* s) {
+  return s == nullptr || *s == 0;
+}
+
+// Returns true if the path starts with a drive letter, e.g. "c:".
+// Note that this won't check for the "\" after the drive letter, so this also
+// returns true for "c:foo" (which is "c:\${PWD}\foo").
+// This check requires that a path not have a longpath prefix ("\\?\").
+template <typename char_type>
+bool has_drive_letter(const char_type* ch) {
+  return CharTraits<char_type>::is_alpha(ch[0]) && ch[1] == ':';
+}
+
+// Returns true if the path starts with a longpath prefix ("\\?\").
+template <typename char_type>
+bool has_longpath_prefix(const char_type* path) {
+  return path[0] == '\\' && path[1] == '\\' && path[2] == '?' &&
+         path[3] == '\\';
+}
+
+template <typename char_type>
+bool is_separator(char_type c) {
+  return c == '/' || c == '\\';
+}
+
+// Returns true if the path starts with a drive specifier (e.g. "c:\").
+template <typename char_type>
+bool is_path_absolute(const char_type* path) {
+  return has_drive_letter(path) && is_separator(path[2]);
+}
+
+template <typename char_type>
+bool is_drive_relative(const char_type* path) {
+  return has_drive_letter(path) && (path[2] == 0 || !is_separator(path[2]));
+}
+
+wstring join_paths(const wstring& path1, const wstring& path2) {
+  if (path1.empty() || is_path_absolute(path2.c_str()) ||
+      has_longpath_prefix(path2.c_str())) {
+    return path2;
+  }
+  if (path2.empty()) {
+    return path1;
+  }
+
+  if (is_separator(path1[path1.size() - 1])) {
+    return is_separator(path2[0]) ? (path1 + path2.substr(1))
+                                       : (path1 + path2);
+  } else {
+    return is_separator(path2[0]) ? (path1 + path2)
+                                       : (path1 + L'\\' + path2);
+  }
+}
+
+wstring normalize(wstring path) {
+  if (has_longpath_prefix(path.c_str())) {
+    path = path.substr(4);
+  }
+
+  static const wstring dot(L".");
+  static const wstring dotdot(L"..");
+  const WCHAR* p = path.c_str();
+
+  std::vector<wstring> segments;
+  int segment_start = -1;
+  // Find the path segments in `path` (separated by "/").
+  for (int i = 0;; ++i) {
+    if (!is_separator(p[i]) && p[i] != L'\0') {
+      // The current character does not end a segment, so start one unless it's
+      // already started.
+      if (segment_start < 0) {
+        segment_start = i;
+      }
+    } else if (segment_start >= 0 && i > segment_start) {
+      // The current character is "/" or "\0", so this ends a segment.
+      // Add that to `segments` if there's anything to add; handle "." and "..".
+      wstring segment(p, segment_start, i - segment_start);
+      segment_start = -1;
+      if (segment == dotdot) {
+        if (!segments.empty() &&
+            (!has_drive_letter(segments[0].c_str()) || segments.size() > 1)) {
+          segments.pop_back();
+        }
+      } else if (segment != dot && !segment.empty()) {
+        segments.push_back(segment);
+      }
+    }
+    if (p[i] == L'\0') {
+      break;
+    }
+  }
+
+  // Handle the case when `path` is just a drive specifier (or some degenerate
+  // form of it, e.g. "c:\..").
+  if (segments.size() == 1 && segments[0].size() == 2 &&
+      has_drive_letter(segments[0].c_str())) {
+    return segments[0] + L'\\';
+  }
+
+  // Join all segments.
+  bool first = true;
+  std::wstringstream result;
+  for (size_t i = 0; i < segments.size(); ++i) {
+    if (!first) {
+      result << L'\\';
+    }
+    first = false;
+    result << segments[i];
+  }
+  // Preserve trailing separator if the input contained it.
+  if (!path.empty() && is_separator(p[path.size() - 1])) {
+    result << L'\\';
+  }
+  return result.str();
+}
+
+bool as_windows_path(const char* path, wstring* result) {
+  if (null_or_empty(path)) {
+    result->clear();
+    return true;
+  }
+  wstring wpath;
+  if (!strings::utf8_to_wcs(path, &wpath)) {
+    return false;
+  }
+  if (has_longpath_prefix(wpath.c_str())) {
+    *result = wpath;
+    return true;
+  }
+  if (is_separator(path[0]) || is_drive_relative(path)) {
+    return false;
+  }
+
+
+  if (!is_path_absolute(wpath.c_str())) {
+    int size = ::GetCurrentDirectoryW(0, nullptr);
+    if (size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+      return false;
+    }
+    std::unique_ptr<WCHAR[]> wcwd(new WCHAR[size]);
+    ::GetCurrentDirectoryW(size, wcwd.get());
+    wpath = join_paths(wcwd.get(), wpath);
+  }
+  wpath = normalize(wpath);
+  if (!has_longpath_prefix(wpath.c_str())) {
+    // Add the "\\?\" prefix unconditionally. This way we prevent the Win32 API
+    // from processing the path and "helpfully" removing trailing dots from the
+    // path, for example.
+    // See https://github.com/bazelbuild/bazel/issues/2935
+    wpath = wstring(L"\\\\?\\") + wpath;
+  }
+  *result = wpath;
+  return true;
+}
+
+}  // namespace
+
+int open(const char* path, int flags, int mode) {
+#ifdef SUPPORT_LONGPATHS
+  wstring wpath;
+  if (!as_windows_path(path, &wpath)) {
+    errno = ENOENT;
+    return -1;
+  }
+  return ::_wopen(wpath.c_str(), flags, mode);
+#else
+  return ::_open(path, flags, mode);
+#endif
+}
+
+int mkdir(const char* path, int /*_mode*/) {
+#ifdef SUPPORT_LONGPATHS
+  wstring wpath;
+  if (!as_windows_path(path, &wpath)) {
+    errno = ENOENT;
+    return -1;
+  }
+  return ::_wmkdir(wpath.c_str());
+#else   // not SUPPORT_LONGPATHS
+  return ::_mkdir(path);
+#endif  // not SUPPORT_LONGPATHS
+}
+
+int access(const char* path, int mode) {
+#ifdef SUPPORT_LONGPATHS
+  wstring wpath;
+  if (!as_windows_path(path, &wpath)) {
+    errno = ENOENT;
+    return -1;
+  }
+  return ::_waccess(wpath.c_str(), mode);
+#else
+  return ::_access(path, mode);
+#endif
+}
+
+int chdir(const char* path) {
+#ifdef SUPPORT_LONGPATHS
+  wstring wpath;
+  if (!as_windows_path(path, &wpath)) {
+    errno = ENOENT;
+    return -1;
+  }
+  return ::_wchdir(wpath.c_str());
+#else
+  return ::_chdir(path);
+#endif
+}
+
+int stat(const char* path, struct _stat* buffer) {
+#ifdef SUPPORT_LONGPATHS
+  wstring wpath;
+  if (!as_windows_path(path, &wpath)) {
+    errno = ENOENT;
+    return -1;
+  }
+  return ::_wstat(wpath.c_str(), buffer);
+#else   // not SUPPORT_LONGPATHS
+  return ::_stat(path, buffer);
+#endif  // not SUPPORT_LONGPATHS
+}
+
+FILE* fopen(const char* path, const char* mode) {
+#ifdef SUPPORT_LONGPATHS
+  if (null_or_empty(path)) {
+    errno = EINVAL;
+    return nullptr;
+  }
+  wstring wpath;
+  if (!as_windows_path(path, &wpath)) {
+    errno = ENOENT;
+    return nullptr;
+  }
+  wstring wmode;
+  if (!strings::utf8_to_wcs(mode, &wmode)) {
+    errno = EINVAL;
+    return nullptr;
+  }
+  return ::_wfopen(wpath.c_str(), wmode.c_str());
+#else
+  return ::fopen(path, mode);
+#endif
+}
+
+int close(int fd) { return ::_close(fd); }
+
+int dup(int fd) { return ::_dup(fd); }
+
+int dup2(int fd1, int fd2) { return ::_dup2(fd1, fd2); }
+
+int read(int fd, void* buffer, size_t size) {
+  return ::_read(fd, buffer, size);
+}
+
+int setmode(int fd, int mode) { return ::_setmode(fd, mode); }
+
+int write(int fd, const void* buffer, size_t size) {
+  return ::_write(fd, buffer, size);
+}
+
+wstring testonly_utf8_to_winpath(const char* path) {
+  wstring wpath;
+  return as_windows_path(path, &wpath) ? wpath : wstring();
+}
+
+ExpandWildcardsResult ExpandWildcards(
+    const string& path, std::function<void(const string&)> consume) {
+  if (path.find_first_of("*?") == string::npos) {
+    // There are no wildcards in the path, we don't need to expand it.
+    consume(path);
+    return ExpandWildcardsResult::kSuccess;
+  }
+
+  wstring wpath;
+  if (!as_windows_path(path.c_str(), &wpath)) {
+    return ExpandWildcardsResult::kErrorInputPathConversion;
+  }
+
+  static const wstring kDot = L".";
+  static const wstring kDotDot = L"..";
+  WIN32_FIND_DATAW metadata;
+  HANDLE handle = ::FindFirstFileW(wpath.c_str(), &metadata);
+  if (handle == INVALID_HANDLE_VALUE) {
+    // The pattern does not match any files (or directories).
+    return ExpandWildcardsResult::kErrorNoMatchingFile;
+  }
+
+  string::size_type pos = path.find_last_of("\\/");
+  string dirname;
+  if (pos != string::npos) {
+    dirname = path.substr(0, pos + 1);
+  }
+
+  ExpandWildcardsResult matched = ExpandWildcardsResult::kErrorNoMatchingFile;
+  do {
+    // Ignore ".", "..", and directories.
+    if ((metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 &&
+        kDot != metadata.cFileName && kDotDot != metadata.cFileName) {
+      matched = ExpandWildcardsResult::kSuccess;
+      string filename;
+      if (!strings::wcs_to_utf8(metadata.cFileName, &filename)) {
+        matched = ExpandWildcardsResult::kErrorOutputPathConversion;
+        break;
+      }
+
+      if (dirname.empty()) {
+        consume(filename);
+      } else {
+        consume(dirname + filename);
+      }
+    }
+  } while (::FindNextFileW(handle, &metadata));
+  FindClose(handle);
+  return matched;
+}
+
+namespace strings {
+
+bool wcs_to_mbs(const WCHAR* s, string* out, bool outUtf8) {
+  if (null_or_empty(s)) {
+    out->clear();
+    return true;
+  }
+  BOOL usedDefaultChar = FALSE;
+  SetLastError(0);
+  int size = WideCharToMultiByte(
+      outUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, nullptr, 0, nullptr,
+      outUtf8 ? nullptr : &usedDefaultChar);
+  if ((size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+      || usedDefaultChar) {
+    return false;
+  }
+  std::unique_ptr<CHAR[]> astr(new CHAR[size]);
+  WideCharToMultiByte(
+      outUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, astr.get(), size, nullptr, nullptr);
+  out->assign(astr.get());
+  return true;
+}
+
+bool mbs_to_wcs(const char* s, wstring* out, bool inUtf8) {
+  if (null_or_empty(s)) {
+    out->clear();
+    return true;
+  }
+
+  SetLastError(0);
+  int size =
+      MultiByteToWideChar(inUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, nullptr, 0);
+  if (size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+    return false;
+  }
+  std::unique_ptr<WCHAR[]> wstr(new WCHAR[size]);
+  MultiByteToWideChar(
+      inUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, wstr.get(), size + 1);
+  out->assign(wstr.get());
+  return true;
+}
+
+bool utf8_to_wcs(const char* input, wstring* out) {
+  return mbs_to_wcs(input, out, true);
+}
+
+bool wcs_to_utf8(const wchar_t* input, string* out) {
+  return wcs_to_mbs(input, out, true);
+}
+
+}  // namespace strings
+}  // namespace win32
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // defined(_WIN32)
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/printer.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/printer.cpp
new file mode 100644
index 0000000..47bd00b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/printer.cpp
@@ -0,0 +1,403 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/io/printer.h>
+
+#include <cctype>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter)
+    : variable_delimiter_(variable_delimiter),
+      output_(output),
+      buffer_(NULL),
+      buffer_size_(0),
+      offset_(0),
+      at_start_of_line_(true),
+      failed_(false),
+      annotation_collector_(NULL) {}
+
+Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter,
+                 AnnotationCollector* annotation_collector)
+    : variable_delimiter_(variable_delimiter),
+      output_(output),
+      buffer_(NULL),
+      buffer_size_(0),
+      offset_(0),
+      at_start_of_line_(true),
+      failed_(false),
+      annotation_collector_(annotation_collector) {}
+
+Printer::~Printer() {
+  // Only BackUp() if we invoked Next() at least once, and we have never failed.
+  // Note that we always call `Backup`, i.e. we call BackUp(0) as some output
+  // streams have buffered output, and BackUp() serves as a flush event in such
+  // implementations.
+  if (buffer_ != nullptr && !failed_) {
+    output_->BackUp(buffer_size_);
+  }
+}
+
+bool Printer::GetSubstitutionRange(const char* varname,
+                                   std::pair<size_t, size_t>* range) {
+  std::map<std::string, std::pair<size_t, size_t> >::const_iterator iter =
+      substitutions_.find(varname);
+  if (iter == substitutions_.end()) {
+    GOOGLE_LOG(DFATAL) << " Undefined variable in annotation: " << varname;
+    return false;
+  }
+  if (iter->second.first > iter->second.second) {
+    GOOGLE_LOG(DFATAL) << " Variable used for annotation used multiple times: "
+                << varname;
+    return false;
+  }
+  *range = iter->second;
+  return true;
+}
+
+void Printer::Annotate(const char* begin_varname, const char* end_varname,
+                       const std::string& file_path,
+                       const std::vector<int>& path) {
+  if (annotation_collector_ == NULL) {
+    // Can't generate signatures with this Printer.
+    return;
+  }
+  std::pair<size_t, size_t> begin, end;
+  if (!GetSubstitutionRange(begin_varname, &begin) ||
+      !GetSubstitutionRange(end_varname, &end)) {
+    return;
+  }
+  if (begin.first > end.second) {
+    GOOGLE_LOG(DFATAL) << "  Annotation has negative length from " << begin_varname
+                << " to " << end_varname;
+  } else {
+    annotation_collector_->AddAnnotation(begin.first, end.second, file_path,
+                                         path);
+  }
+}
+
+void Printer::Print(const std::map<std::string, std::string>& variables,
+                    const char* text) {
+  int size = strlen(text);
+  int pos = 0;  // The number of bytes we've written so far.
+  substitutions_.clear();
+  line_start_variables_.clear();
+
+  for (int i = 0; i < size; i++) {
+    if (text[i] == '\n') {
+      // Saw newline.  If there is more text, we may need to insert an indent
+      // here.  So, write what we have so far, including the '\n'.
+      WriteRaw(text + pos, i - pos + 1);
+      pos = i + 1;
+
+      // Setting this true will cause the next WriteRaw() to insert an indent
+      // first.
+      at_start_of_line_ = true;
+      line_start_variables_.clear();
+
+    } else if (text[i] == variable_delimiter_) {
+      // Saw the start of a variable name.
+
+      // Write what we have so far.
+      WriteRaw(text + pos, i - pos);
+      pos = i + 1;
+
+      // Find closing delimiter.
+      const char* end = strchr(text + pos, variable_delimiter_);
+      if (end == NULL) {
+        GOOGLE_LOG(DFATAL) << " Unclosed variable name.";
+        end = text + pos;
+      }
+      int endpos = end - text;
+
+      std::string varname(text + pos, endpos - pos);
+      if (varname.empty()) {
+        // Two delimiters in a row reduce to a literal delimiter character.
+        WriteRaw(&variable_delimiter_, 1);
+      } else {
+        // Replace with the variable's value.
+        std::map<std::string, std::string>::const_iterator iter =
+            variables.find(varname);
+        if (iter == variables.end()) {
+          GOOGLE_LOG(DFATAL) << " Undefined variable: " << varname;
+        } else {
+          if (at_start_of_line_ && iter->second.empty()) {
+            line_start_variables_.push_back(varname);
+          }
+          WriteRaw(iter->second.data(), iter->second.size());
+          std::pair<std::map<std::string, std::pair<size_t, size_t> >::iterator,
+                    bool>
+              inserted = substitutions_.insert(std::make_pair(
+                  varname,
+                  std::make_pair(offset_ - iter->second.size(), offset_)));
+          if (!inserted.second) {
+            // This variable was used multiple times.  Make its span have
+            // negative length so we can detect it if it gets used in an
+            // annotation.
+            inserted.first->second = std::make_pair(1, 0);
+          }
+        }
+      }
+
+      // Advance past this variable.
+      i = endpos;
+      pos = endpos + 1;
+    }
+  }
+
+  // Write the rest.
+  WriteRaw(text + pos, size - pos);
+}
+
+void Printer::Indent() { indent_ += "  "; }
+
+void Printer::Outdent() {
+  if (indent_.empty()) {
+    GOOGLE_LOG(DFATAL) << " Outdent() without matching Indent().";
+    return;
+  }
+
+  indent_.resize(indent_.size() - 2);
+}
+
+void Printer::PrintRaw(const std::string& data) {
+  WriteRaw(data.data(), data.size());
+}
+
+void Printer::PrintRaw(const char* data) {
+  if (failed_) return;
+  WriteRaw(data, strlen(data));
+}
+
+void Printer::WriteRaw(const char* data, int size) {
+  if (failed_) return;
+  if (size == 0) return;
+
+  if (at_start_of_line_ && (size > 0) && (data[0] != '\n')) {
+    // Insert an indent.
+    at_start_of_line_ = false;
+    CopyToBuffer(indent_.data(), indent_.size());
+    if (failed_) return;
+    // Fix up empty variables (e.g., "{") that should be annotated as
+    // coming after the indent.
+    for (std::vector<std::string>::iterator i = line_start_variables_.begin();
+         i != line_start_variables_.end(); ++i) {
+      substitutions_[*i].first += indent_.size();
+      substitutions_[*i].second += indent_.size();
+    }
+  }
+
+  // If we're going to write any data, clear line_start_variables_, since
+  // we've either updated them in the block above or they no longer refer to
+  // the current line.
+  line_start_variables_.clear();
+
+  CopyToBuffer(data, size);
+}
+
+bool Printer::Next() {
+  do {
+    void* void_buffer;
+    if (!output_->Next(&void_buffer, &buffer_size_)) {
+      failed_ = true;
+      return false;
+    }
+    buffer_ = reinterpret_cast<char*>(void_buffer);
+  } while (buffer_size_ == 0);
+  return true;
+}
+
+void Printer::CopyToBuffer(const char* data, int size) {
+  if (failed_) return;
+  if (size == 0) return;
+
+  while (size > buffer_size_) {
+    // Data exceeds space in the buffer.  Copy what we can and request a
+    // new buffer.
+    if (buffer_size_ > 0) {
+      memcpy(buffer_, data, buffer_size_);
+      offset_ += buffer_size_;
+      data += buffer_size_;
+      size -= buffer_size_;
+    }
+    void* void_buffer;
+    failed_ = !output_->Next(&void_buffer, &buffer_size_);
+    if (failed_) return;
+    buffer_ = reinterpret_cast<char*>(void_buffer);
+  }
+
+  // Buffer is big enough to receive the data; copy it.
+  memcpy(buffer_, data, size);
+  buffer_ += size;
+  buffer_size_ -= size;
+  offset_ += size;
+}
+
+void Printer::IndentIfAtStart() {
+  if (at_start_of_line_) {
+    CopyToBuffer(indent_.data(), indent_.size());
+    at_start_of_line_ = false;
+  }
+}
+
+void Printer::FormatInternal(const std::vector<std::string>& args,
+                             const std::map<std::string, std::string>& vars,
+                             const char* format) {
+  auto save = format;
+  int arg_index = 0;
+  std::vector<AnnotationCollector::Annotation> annotations;
+  while (*format) {
+    char c = *format++;
+    switch (c) {
+      case '$':
+        format = WriteVariable(args, vars, format, &arg_index, &annotations);
+        continue;
+      case '\n':
+        at_start_of_line_ = true;
+        line_start_variables_.clear();
+        break;
+      default:
+        IndentIfAtStart();
+        break;
+    }
+    push_back(c);
+  }
+  if (arg_index != static_cast<int>(args.size())) {
+    GOOGLE_LOG(FATAL) << " Unused arguments. " << save;
+  }
+  if (!annotations.empty()) {
+    GOOGLE_LOG(FATAL) << " Annotation range is not-closed, expect $}$. " << save;
+  }
+}
+
+const char* Printer::WriteVariable(
+    const std::vector<std::string>& args,
+    const std::map<std::string, std::string>& vars, const char* format,
+    int* arg_index, std::vector<AnnotationCollector::Annotation>* annotations) {
+  auto start = format;
+  auto end = strchr(format, '$');
+  if (!end) {
+    GOOGLE_LOG(FATAL) << " Unclosed variable name.";
+  }
+  format = end + 1;
+  if (end == start) {
+    // "$$" is an escape for just '$'
+    IndentIfAtStart();
+    push_back('$');
+    return format;
+  }
+  if (*start == '{') {
+    GOOGLE_CHECK(std::isdigit(start[1]));
+    GOOGLE_CHECK_EQ(end - start, 2);
+    int idx = start[1] - '1';
+    if (idx < 0 || static_cast<size_t>(idx) >= args.size()) {
+      GOOGLE_LOG(FATAL) << "Annotation ${" << idx + 1 << "$ is out of bounds.";
+    }
+    if (idx > *arg_index) {
+      GOOGLE_LOG(FATAL) << "Annotation arg must be in correct order as given. Expected"
+                 << " ${" << (*arg_index) + 1 << "$ got ${" << idx + 1 << "$.";
+    } else if (idx == *arg_index) {
+      (*arg_index)++;
+    }
+    IndentIfAtStart();
+    annotations->push_back({{offset_, 0}, args[idx]});
+    return format;
+  } else if (*start == '}') {
+    GOOGLE_CHECK(annotations);
+    if (annotations->empty()) {
+      GOOGLE_LOG(FATAL) << "Unexpected end of annotation found.";
+    }
+    auto& a = annotations->back();
+    a.first.second = offset_;
+    if (annotation_collector_) annotation_collector_->AddAnnotationNew(a);
+    annotations->pop_back();
+    return format;
+  }
+  auto start_var = start;
+  while (start_var < end && *start_var == ' ') start_var++;
+  if (start_var == end) {
+    GOOGLE_LOG(FATAL) << " Empty variable.";
+  }
+  auto end_var = end;
+  while (start_var < end_var && *(end_var - 1) == ' ') end_var--;
+  std::string var_name{
+      start_var, static_cast<std::string::size_type>(end_var - start_var)};
+  std::string sub;
+  if (std::isdigit(var_name[0])) {
+    GOOGLE_CHECK_EQ(var_name.size(), 1U);  // No need for multi-digits
+    int idx = var_name[0] - '1';   // Start counting at 1
+    GOOGLE_CHECK_GE(idx, 0);
+    if (static_cast<size_t>(idx) >= args.size()) {
+      GOOGLE_LOG(FATAL) << "Argument $" << idx + 1 << "$ is out of bounds.";
+    }
+    if (idx > *arg_index) {
+      GOOGLE_LOG(FATAL) << "Arguments must be used in same order as given. Expected $"
+                 << (*arg_index) + 1 << "$ got $" << idx + 1 << "$.";
+    } else if (idx == *arg_index) {
+      (*arg_index)++;
+    }
+    sub = args[idx];
+  } else {
+    auto it = vars.find(var_name);
+    if (it == vars.end()) {
+      GOOGLE_LOG(FATAL) << " Unknown variable: " << var_name << ".";
+    }
+    sub = it->second;
+  }
+
+  // By returning here in case of empty we also skip possible spaces inside
+  // the $...$, i.e. "void$ dllexpor$ f();" -> "void f();" in the empty case.
+  if (sub.empty()) return format;
+
+  // We're going to write something non-empty so we need a possible indent.
+  IndentIfAtStart();
+
+  // Write the possible spaces in front.
+  CopyToBuffer(start, start_var - start);
+  // Write a non-empty substituted variable.
+  CopyToBuffer(sub.c_str(), sub.size());
+  // Finish off with writing possible trailing spaces.
+  CopyToBuffer(end_var, end - end_var);
+  return format;
+}
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/strtod.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/strtod.cpp
new file mode 100644
index 0000000..03acb5b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/strtod.cpp
@@ -0,0 +1,82 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/io/strtod.h>
+
+#include <cstdio>
+#include <cstring>
+#include <limits>
+#include <string>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+// This approximately 0x1.ffffffp127, but we don't use 0x1.ffffffp127 because
+// it won't compile in MSVC.
+const double MAX_FLOAT_AS_DOUBLE_ROUNDED = 3.4028235677973366e+38;
+
+float SafeDoubleToFloat(double value) {
+  // static_cast<float> on a number larger than float can result in illegal
+  // instruction error, so we need to manually convert it to infinity or max.
+  if (value > std::numeric_limits<float>::max()) {
+    // Max float value is about 3.4028234664E38 when represented as a double.
+    // However, when printing float as text, it will be rounded as
+    // 3.4028235e+38. If we parse the value of 3.4028235e+38 from text and
+    // compare it to 3.4028234664E38, we may think that it is larger, but
+    // actually, any number between these two numbers could only be represented
+    // as the same max float number in float, so we should treat them the same
+    // as max float.
+    if (value <= MAX_FLOAT_AS_DOUBLE_ROUNDED) {
+      return std::numeric_limits<float>::max();
+    }
+    return std::numeric_limits<float>::infinity();
+  } else if (value < -std::numeric_limits<float>::max()) {
+    if (value >= -MAX_FLOAT_AS_DOUBLE_ROUNDED) {
+      return -std::numeric_limits<float>::max();
+    }
+    return -std::numeric_limits<float>::infinity();
+  } else {
+    return static_cast<float>(value);
+  }
+}
+
+double NoLocaleStrtod(const char* str, char** endptr) {
+  return google::protobuf::internal::NoLocaleStrtod(str, endptr);
+}
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/tokenizer.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/tokenizer.cpp
new file mode 100644
index 0000000..30d62ac
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/tokenizer.cpp
@@ -0,0 +1,1246 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// Here we have a hand-written lexer.  At first you might ask yourself,
+// "Hand-written text processing?  Is Kenton crazy?!"  Well, first of all,
+// yes I am crazy, but that's beside the point.  There are actually reasons
+// why I ended up writing this this way.
+//
+// The traditional approach to lexing is to use lex to generate a lexer for
+// you.  Unfortunately, lex's output is ridiculously ugly and difficult to
+// integrate cleanly with C++ code, especially abstract code or code meant
+// as a library.  Better parser-generators exist but would add dependencies
+// which most users won't already have, which we'd like to avoid.  (GNU flex
+// has a C++ output option, but it's still ridiculously ugly, non-abstract,
+// and not library-friendly.)
+//
+// The next approach that any good software engineer should look at is to
+// use regular expressions.  And, indeed, I did.  I have code which
+// implements this same class using regular expressions.  It's about 200
+// lines shorter.  However:
+// - Rather than error messages telling you "This string has an invalid
+//   escape sequence at line 5, column 45", you get error messages like
+//   "Parse error on line 5".  Giving more precise errors requires adding
+//   a lot of code that ends up basically as complex as the hand-coded
+//   version anyway.
+// - The regular expression to match a string literal looks like this:
+//     kString  = new RE("(\"([^\"\\\\]|"              // non-escaped
+//                       "\\\\[abfnrtv?\"'\\\\0-7]|"   // normal escape
+//                       "\\\\x[0-9a-fA-F])*\"|"       // hex escape
+//                       "\'([^\'\\\\]|"        // Also support single-quotes.
+//                       "\\\\[abfnrtv?\"'\\\\0-7]|"
+//                       "\\\\x[0-9a-fA-F])*\')");
+//   Verifying the correctness of this line noise is actually harder than
+//   verifying the correctness of ConsumeString(), defined below.  I'm not
+//   even confident that the above is correct, after staring at it for some
+//   time.
+// - PCRE is fast, but there's still more overhead involved than the code
+//   below.
+// - Sadly, regular expressions are not part of the C standard library, so
+//   using them would require depending on some other library.  For the
+//   open source release, this could be really annoying.  Nobody likes
+//   downloading one piece of software just to find that they need to
+//   download something else to make it work, and in all likelihood
+//   people downloading Protocol Buffers will already be doing so just
+//   to make something else work.  We could include a copy of PCRE with
+//   our code, but that obligates us to keep it up-to-date and just seems
+//   like a big waste just to save 200 lines of code.
+//
+// On a similar but unrelated note, I'm even scared to use ctype.h.
+// Apparently functions like isalpha() are locale-dependent.  So, if we used
+// that, then if this code is being called from some program that doesn't
+// have its locale set to "C", it would behave strangely.  We can't just set
+// the locale to "C" ourselves since we might break the calling program that
+// way, particularly if it is multi-threaded.  WTF?  Someone please let me
+// (Kenton) know if I'm missing something here...
+//
+// I'd love to hear about other alternatives, though, as this code isn't
+// exactly pretty.
+
+#include <google/protobuf/io/tokenizer.h>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/io/strtod.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace io {
+namespace {
+
+// As mentioned above, I don't trust ctype.h due to the presence of "locales".
+// So, I have written replacement functions here.  Someone please smack me if
+// this is a bad idea or if there is some way around this.
+//
+// These "character classes" are designed to be used in template methods.
+// For instance, Tokenizer::ConsumeZeroOrMore<Whitespace>() will eat
+// whitespace.
+
+// Note:  No class is allowed to contain '\0', since this is used to mark end-
+//   of-input and is handled specially.
+
+#define CHARACTER_CLASS(NAME, EXPRESSION)                     \
+  class NAME {                                                \
+   public:                                                    \
+    static inline bool InClass(char c) { return EXPRESSION; } \
+  }
+
+CHARACTER_CLASS(Whitespace, c == ' ' || c == '\n' || c == '\t' || c == '\r' ||
+                                c == '\v' || c == '\f');
+CHARACTER_CLASS(WhitespaceNoNewline,
+                c == ' ' || c == '\t' || c == '\r' || c == '\v' || c == '\f');
+
+CHARACTER_CLASS(Unprintable, c<' ' && c> '\0');
+
+CHARACTER_CLASS(Digit, '0' <= c && c <= '9');
+CHARACTER_CLASS(OctalDigit, '0' <= c && c <= '7');
+CHARACTER_CLASS(HexDigit, ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') ||
+                              ('A' <= c && c <= 'F'));
+
+CHARACTER_CLASS(Letter,
+                ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (c == '_'));
+
+CHARACTER_CLASS(Alphanumeric, ('a' <= c && c <= 'z') ||
+                                  ('A' <= c && c <= 'Z') ||
+                                  ('0' <= c && c <= '9') || (c == '_'));
+
+CHARACTER_CLASS(Escape, c == 'a' || c == 'b' || c == 'f' || c == 'n' ||
+                            c == 'r' || c == 't' || c == 'v' || c == '\\' ||
+                            c == '?' || c == '\'' || c == '\"');
+
+#undef CHARACTER_CLASS
+
+// Given a char, interpret it as a numeric digit and return its value.
+// This supports any number base up to 36.
+// Represents integer values of digits.
+// Uses 36 to indicate an invalid character since we support
+// bases up to 36.
+static const int8_t kAsciiToInt[256] = {
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // 00-0F
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // 10-1F
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // ' '-'/'
+    0,  1,  2,  3,  4,  5,  6,  7,  8,  9,                           // '0'-'9'
+    36, 36, 36, 36, 36, 36, 36,                                      // ':'-'@'
+    10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  // 'A'-'P'
+    26, 27, 28, 29, 30, 31, 32, 33, 34, 35,                          // 'Q'-'Z'
+    36, 36, 36, 36, 36, 36,                                          // '['-'`'
+    10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  // 'a'-'p'
+    26, 27, 28, 29, 30, 31, 32, 33, 34, 35,                          // 'q'-'z'
+    36, 36, 36, 36, 36,                                              // '{'-DEL
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // 80-8F
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // 90-9F
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // A0-AF
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // B0-BF
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // C0-CF
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // D0-DF
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // E0-EF
+    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // F0-FF
+};
+
+inline int DigitValue(char digit) { return kAsciiToInt[digit & 0xFF]; }
+
+// Inline because it's only used in one place.
+inline char TranslateEscape(char c) {
+  switch (c) {
+    case 'a':
+      return '\a';
+    case 'b':
+      return '\b';
+    case 'f':
+      return '\f';
+    case 'n':
+      return '\n';
+    case 'r':
+      return '\r';
+    case 't':
+      return '\t';
+    case 'v':
+      return '\v';
+    case '\\':
+      return '\\';
+    case '?':
+      return '\?';  // Trigraphs = :(
+    case '\'':
+      return '\'';
+    case '"':
+      return '\"';
+
+    // We expect escape sequences to have been validated separately.
+    default:
+      return '?';
+  }
+}
+
+}  // anonymous namespace
+
+ErrorCollector::~ErrorCollector() {}
+
+// ===================================================================
+
+Tokenizer::Tokenizer(ZeroCopyInputStream* input,
+                     ErrorCollector* error_collector)
+    : input_(input),
+      error_collector_(error_collector),
+      buffer_(NULL),
+      buffer_size_(0),
+      buffer_pos_(0),
+      read_error_(false),
+      line_(0),
+      column_(0),
+      record_target_(NULL),
+      record_start_(-1),
+      allow_f_after_float_(false),
+      comment_style_(CPP_COMMENT_STYLE),
+      require_space_after_number_(true),
+      allow_multiline_strings_(false) {
+  current_.line = 0;
+  current_.column = 0;
+  current_.end_column = 0;
+  current_.type = TYPE_START;
+
+  Refresh();
+}
+
+Tokenizer::~Tokenizer() {
+  // If we had any buffer left unread, return it to the underlying stream
+  // so that someone else can read it.
+  if (buffer_size_ > buffer_pos_) {
+    input_->BackUp(buffer_size_ - buffer_pos_);
+  }
+}
+
+bool Tokenizer::report_whitespace() const { return report_whitespace_; }
+// Note: `set_report_whitespace(false)` implies `set_report_newlines(false)`.
+void Tokenizer::set_report_whitespace(bool report) {
+  report_whitespace_ = report;
+  report_newlines_ &= report;
+}
+
+// If true, newline tokens are reported by Next().
+bool Tokenizer::report_newlines() const { return report_newlines_; }
+// Note: `set_report_newlines(true)` implies `set_report_whitespace(true)`.
+void Tokenizer::set_report_newlines(bool report) {
+  report_newlines_ = report;
+  report_whitespace_ |= report;  // enable report_whitespace if necessary
+}
+
+// -------------------------------------------------------------------
+// Internal helpers.
+
+void Tokenizer::NextChar() {
+  // Update our line and column counters based on the character being
+  // consumed.
+  if (current_char_ == '\n') {
+    ++line_;
+    column_ = 0;
+  } else if (current_char_ == '\t') {
+    column_ += kTabWidth - column_ % kTabWidth;
+  } else {
+    ++column_;
+  }
+
+  // Advance to the next character.
+  ++buffer_pos_;
+  if (buffer_pos_ < buffer_size_) {
+    current_char_ = buffer_[buffer_pos_];
+  } else {
+    Refresh();
+  }
+}
+
+void Tokenizer::Refresh() {
+  if (read_error_) {
+    current_char_ = '\0';
+    return;
+  }
+
+  // If we're in a token, append the rest of the buffer to it.
+  if (record_target_ != NULL && record_start_ < buffer_size_) {
+    record_target_->append(buffer_ + record_start_,
+                           buffer_size_ - record_start_);
+    record_start_ = 0;
+  }
+
+  const void* data = NULL;
+  buffer_ = NULL;
+  buffer_pos_ = 0;
+  do {
+    if (!input_->Next(&data, &buffer_size_)) {
+      // end of stream (or read error)
+      buffer_size_ = 0;
+      read_error_ = true;
+      current_char_ = '\0';
+      return;
+    }
+  } while (buffer_size_ == 0);
+
+  buffer_ = static_cast<const char*>(data);
+
+  current_char_ = buffer_[0];
+}
+
+inline void Tokenizer::RecordTo(std::string* target) {
+  record_target_ = target;
+  record_start_ = buffer_pos_;
+}
+
+inline void Tokenizer::StopRecording() {
+  // Note:  The if() is necessary because some STL implementations crash when
+  //   you call string::append(NULL, 0), presumably because they are trying to
+  //   be helpful by detecting the NULL pointer, even though there's nothing
+  //   wrong with reading zero bytes from NULL.
+  if (buffer_pos_ != record_start_) {
+    record_target_->append(buffer_ + record_start_,
+                           buffer_pos_ - record_start_);
+  }
+  record_target_ = NULL;
+  record_start_ = -1;
+}
+
+inline void Tokenizer::StartToken() {
+  current_.type = TYPE_START;  // Just for the sake of initializing it.
+  current_.text.clear();
+  current_.line = line_;
+  current_.column = column_;
+  RecordTo(&current_.text);
+}
+
+inline void Tokenizer::EndToken() {
+  StopRecording();
+  current_.end_column = column_;
+}
+
+// -------------------------------------------------------------------
+// Helper methods that consume characters.
+
+template <typename CharacterClass>
+inline bool Tokenizer::LookingAt() {
+  return CharacterClass::InClass(current_char_);
+}
+
+template <typename CharacterClass>
+inline bool Tokenizer::TryConsumeOne() {
+  if (CharacterClass::InClass(current_char_)) {
+    NextChar();
+    return true;
+  } else {
+    return false;
+  }
+}
+
+inline bool Tokenizer::TryConsume(char c) {
+  if (current_char_ == c) {
+    NextChar();
+    return true;
+  } else {
+    return false;
+  }
+}
+
+template <typename CharacterClass>
+inline void Tokenizer::ConsumeZeroOrMore() {
+  while (CharacterClass::InClass(current_char_)) {
+    NextChar();
+  }
+}
+
+template <typename CharacterClass>
+inline void Tokenizer::ConsumeOneOrMore(const char* error) {
+  if (!CharacterClass::InClass(current_char_)) {
+    AddError(error);
+  } else {
+    do {
+      NextChar();
+    } while (CharacterClass::InClass(current_char_));
+  }
+}
+
+// -------------------------------------------------------------------
+// Methods that read whole patterns matching certain kinds of tokens
+// or comments.
+
+void Tokenizer::ConsumeString(char delimiter) {
+  while (true) {
+    switch (current_char_) {
+      case '\0':
+        AddError("Unexpected end of string.");
+        return;
+
+      case '\n': {
+        if (!allow_multiline_strings_) {
+          AddError("String literals cannot cross line boundaries.");
+          return;
+        }
+        NextChar();
+        break;
+      }
+
+      case '\\': {
+        // An escape sequence.
+        NextChar();
+        if (TryConsumeOne<Escape>()) {
+          // Valid escape sequence.
+        } else if (TryConsumeOne<OctalDigit>()) {
+          // Possibly followed by two more octal digits, but these will
+          // just be consumed by the main loop anyway so we don't need
+          // to do so explicitly here.
+        } else if (TryConsume('x')) {
+          if (!TryConsumeOne<HexDigit>()) {
+            AddError("Expected hex digits for escape sequence.");
+          }
+          // Possibly followed by another hex digit, but again we don't care.
+        } else if (TryConsume('u')) {
+          if (!TryConsumeOne<HexDigit>() || !TryConsumeOne<HexDigit>() ||
+              !TryConsumeOne<HexDigit>() || !TryConsumeOne<HexDigit>()) {
+            AddError("Expected four hex digits for \\u escape sequence.");
+          }
+        } else if (TryConsume('U')) {
+          // We expect 8 hex digits; but only the range up to 0x10ffff is
+          // legal.
+          if (!TryConsume('0') || !TryConsume('0') ||
+              !(TryConsume('0') || TryConsume('1')) ||
+              !TryConsumeOne<HexDigit>() || !TryConsumeOne<HexDigit>() ||
+              !TryConsumeOne<HexDigit>() || !TryConsumeOne<HexDigit>() ||
+              !TryConsumeOne<HexDigit>()) {
+            AddError(
+                "Expected eight hex digits up to 10ffff for \\U escape "
+                "sequence");
+          }
+        } else {
+          AddError("Invalid escape sequence in string literal.");
+        }
+        break;
+      }
+
+      default: {
+        if (current_char_ == delimiter) {
+          NextChar();
+          return;
+        }
+        NextChar();
+        break;
+      }
+    }
+  }
+}
+
+Tokenizer::TokenType Tokenizer::ConsumeNumber(bool started_with_zero,
+                                              bool started_with_dot) {
+  bool is_float = false;
+
+  if (started_with_zero && (TryConsume('x') || TryConsume('X'))) {
+    // A hex number (started with "0x").
+    ConsumeOneOrMore<HexDigit>("\"0x\" must be followed by hex digits.");
+
+  } else if (started_with_zero && LookingAt<Digit>()) {
+    // An octal number (had a leading zero).
+    ConsumeZeroOrMore<OctalDigit>();
+    if (LookingAt<Digit>()) {
+      AddError("Numbers starting with leading zero must be in octal.");
+      ConsumeZeroOrMore<Digit>();
+    }
+
+  } else {
+    // A decimal number.
+    if (started_with_dot) {
+      is_float = true;
+      ConsumeZeroOrMore<Digit>();
+    } else {
+      ConsumeZeroOrMore<Digit>();
+
+      if (TryConsume('.')) {
+        is_float = true;
+        ConsumeZeroOrMore<Digit>();
+      }
+    }
+
+    if (TryConsume('e') || TryConsume('E')) {
+      is_float = true;
+      TryConsume('-') || TryConsume('+');
+      ConsumeOneOrMore<Digit>("\"e\" must be followed by exponent.");
+    }
+
+    if (allow_f_after_float_ && (TryConsume('f') || TryConsume('F'))) {
+      is_float = true;
+    }
+  }
+
+  if (LookingAt<Letter>() && require_space_after_number_) {
+    AddError("Need space between number and identifier.");
+  } else if (current_char_ == '.') {
+    if (is_float) {
+      AddError(
+          "Already saw decimal point or exponent; can't have another one.");
+    } else {
+      AddError("Hex and octal numbers must be integers.");
+    }
+  }
+
+  return is_float ? TYPE_FLOAT : TYPE_INTEGER;
+}
+
+void Tokenizer::ConsumeLineComment(std::string* content) {
+  if (content != NULL) RecordTo(content);
+
+  while (current_char_ != '\0' && current_char_ != '\n') {
+    NextChar();
+  }
+  TryConsume('\n');
+
+  if (content != NULL) StopRecording();
+}
+
+void Tokenizer::ConsumeBlockComment(std::string* content) {
+  int start_line = line_;
+  int start_column = column_ - 2;
+
+  if (content != NULL) RecordTo(content);
+
+  while (true) {
+    while (current_char_ != '\0' && current_char_ != '*' &&
+           current_char_ != '/' && current_char_ != '\n') {
+      NextChar();
+    }
+
+    if (TryConsume('\n')) {
+      if (content != NULL) StopRecording();
+
+      // Consume leading whitespace and asterisk;
+      ConsumeZeroOrMore<WhitespaceNoNewline>();
+      if (TryConsume('*')) {
+        if (TryConsume('/')) {
+          // End of comment.
+          break;
+        }
+      }
+
+      if (content != NULL) RecordTo(content);
+    } else if (TryConsume('*') && TryConsume('/')) {
+      // End of comment.
+      if (content != NULL) {
+        StopRecording();
+        // Strip trailing "*/".
+        content->erase(content->size() - 2);
+      }
+      break;
+    } else if (TryConsume('/') && current_char_ == '*') {
+      // Note:  We didn't consume the '*' because if there is a '/' after it
+      //   we want to interpret that as the end of the comment.
+      AddError(
+          "\"/*\" inside block comment.  Block comments cannot be nested.");
+    } else if (current_char_ == '\0') {
+      AddError("End-of-file inside block comment.");
+      error_collector_->AddError(start_line, start_column,
+                                 "  Comment started here.");
+      if (content != NULL) StopRecording();
+      break;
+    }
+  }
+}
+
+Tokenizer::NextCommentStatus Tokenizer::TryConsumeCommentStart() {
+  if (comment_style_ == CPP_COMMENT_STYLE && TryConsume('/')) {
+    if (TryConsume('/')) {
+      return LINE_COMMENT;
+    } else if (TryConsume('*')) {
+      return BLOCK_COMMENT;
+    } else {
+      // Oops, it was just a slash.  Return it.
+      current_.type = TYPE_SYMBOL;
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wrestrict"
+#endif
+      current_.text = "/";
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+      current_.line = line_;
+      current_.column = column_ - 1;
+      current_.end_column = column_;
+      return SLASH_NOT_COMMENT;
+    }
+  } else if (comment_style_ == SH_COMMENT_STYLE && TryConsume('#')) {
+    return LINE_COMMENT;
+  } else {
+    return NO_COMMENT;
+  }
+}
+
+bool Tokenizer::TryConsumeWhitespace() {
+  if (report_newlines_) {
+    if (TryConsumeOne<WhitespaceNoNewline>()) {
+      ConsumeZeroOrMore<WhitespaceNoNewline>();
+      current_.type = TYPE_WHITESPACE;
+      return true;
+    }
+    return false;
+  }
+  if (TryConsumeOne<Whitespace>()) {
+    ConsumeZeroOrMore<Whitespace>();
+    current_.type = TYPE_WHITESPACE;
+    return report_whitespace_;
+  }
+  return false;
+}
+
+bool Tokenizer::TryConsumeNewline() {
+  if (!report_whitespace_ || !report_newlines_) {
+    return false;
+  }
+  if (TryConsume('\n')) {
+    current_.type = TYPE_NEWLINE;
+    return true;
+  }
+  return false;
+}
+
+// -------------------------------------------------------------------
+
+bool Tokenizer::Next() {
+  previous_ = current_;
+
+  while (!read_error_) {
+    StartToken();
+    bool report_token = TryConsumeWhitespace() || TryConsumeNewline();
+    EndToken();
+    if (report_token) {
+      return true;
+    }
+
+    switch (TryConsumeCommentStart()) {
+      case LINE_COMMENT:
+        ConsumeLineComment(NULL);
+        continue;
+      case BLOCK_COMMENT:
+        ConsumeBlockComment(NULL);
+        continue;
+      case SLASH_NOT_COMMENT:
+        return true;
+      case NO_COMMENT:
+        break;
+    }
+
+    // Check for EOF before continuing.
+    if (read_error_) break;
+
+    if (LookingAt<Unprintable>() || current_char_ == '\0') {
+      AddError("Invalid control characters encountered in text.");
+      NextChar();
+      // Skip more unprintable characters, too.  But, remember that '\0' is
+      // also what current_char_ is set to after EOF / read error.  We have
+      // to be careful not to go into an infinite loop of trying to consume
+      // it, so make sure to check read_error_ explicitly before consuming
+      // '\0'.
+      while (TryConsumeOne<Unprintable>() ||
+             (!read_error_ && TryConsume('\0'))) {
+        // Ignore.
+      }
+
+    } else {
+      // Reading some sort of token.
+      StartToken();
+
+      if (TryConsumeOne<Letter>()) {
+        ConsumeZeroOrMore<Alphanumeric>();
+        current_.type = TYPE_IDENTIFIER;
+      } else if (TryConsume('0')) {
+        current_.type = ConsumeNumber(true, false);
+      } else if (TryConsume('.')) {
+        // This could be the beginning of a floating-point number, or it could
+        // just be a '.' symbol.
+
+        if (TryConsumeOne<Digit>()) {
+          // It's a floating-point number.
+          if (previous_.type == TYPE_IDENTIFIER &&
+              current_.line == previous_.line &&
+              current_.column == previous_.end_column) {
+            // We don't accept syntax like "blah.123".
+            error_collector_->AddError(
+                line_, column_ - 2,
+                "Need space between identifier and decimal point.");
+          }
+          current_.type = ConsumeNumber(false, true);
+        } else {
+          current_.type = TYPE_SYMBOL;
+        }
+      } else if (TryConsumeOne<Digit>()) {
+        current_.type = ConsumeNumber(false, false);
+      } else if (TryConsume('\"')) {
+        ConsumeString('\"');
+        current_.type = TYPE_STRING;
+      } else if (TryConsume('\'')) {
+        ConsumeString('\'');
+        current_.type = TYPE_STRING;
+      } else {
+        // Check if the high order bit is set.
+        if (current_char_ & 0x80) {
+          error_collector_->AddError(
+              line_, column_,
+              StringPrintf("Interpreting non ascii codepoint %d.",
+                              static_cast<unsigned char>(current_char_)));
+        }
+        NextChar();
+        current_.type = TYPE_SYMBOL;
+      }
+
+      EndToken();
+      return true;
+    }
+  }
+
+  // EOF
+  current_.type = TYPE_END;
+  current_.text.clear();
+  current_.line = line_;
+  current_.column = column_;
+  current_.end_column = column_;
+  return false;
+}
+
+namespace {
+
+// Helper class for collecting comments and putting them in the right places.
+//
+// This basically just buffers the most recent comment until it can be decided
+// exactly where that comment should be placed.  When Flush() is called, the
+// current comment goes into either prev_trailing_comments or detached_comments.
+// When the CommentCollector is destroyed, the last buffered comment goes into
+// next_leading_comments.
+class CommentCollector {
+ public:
+  CommentCollector(std::string* prev_trailing_comments,
+                   std::vector<std::string>* detached_comments,
+                   std::string* next_leading_comments)
+      : prev_trailing_comments_(prev_trailing_comments),
+        detached_comments_(detached_comments),
+        next_leading_comments_(next_leading_comments),
+        has_comment_(false),
+        is_line_comment_(false),
+        can_attach_to_prev_(true) {
+    if (prev_trailing_comments != NULL) prev_trailing_comments->clear();
+    if (detached_comments != NULL) detached_comments->clear();
+    if (next_leading_comments != NULL) next_leading_comments->clear();
+  }
+
+  ~CommentCollector() {
+    // Whatever is in the buffer is a leading comment.
+    if (next_leading_comments_ != NULL && has_comment_) {
+      comment_buffer_.swap(*next_leading_comments_);
+    }
+  }
+
+  // About to read a line comment.  Get the comment buffer pointer in order to
+  // read into it.
+  std::string* GetBufferForLineComment() {
+    // We want to combine with previous line comments, but not block comments.
+    if (has_comment_ && !is_line_comment_) {
+      Flush();
+    }
+    has_comment_ = true;
+    is_line_comment_ = true;
+    return &comment_buffer_;
+  }
+
+  // About to read a block comment.  Get the comment buffer pointer in order to
+  // read into it.
+  std::string* GetBufferForBlockComment() {
+    if (has_comment_) {
+      Flush();
+    }
+    has_comment_ = true;
+    is_line_comment_ = false;
+    return &comment_buffer_;
+  }
+
+  void ClearBuffer() {
+    comment_buffer_.clear();
+    has_comment_ = false;
+  }
+
+  // Called once we know that the comment buffer is complete and is *not*
+  // connected to the next token.
+  void Flush() {
+    if (has_comment_) {
+      if (can_attach_to_prev_) {
+        if (prev_trailing_comments_ != NULL) {
+          prev_trailing_comments_->append(comment_buffer_);
+        }
+        can_attach_to_prev_ = false;
+      } else {
+        if (detached_comments_ != NULL) {
+          detached_comments_->push_back(comment_buffer_);
+        }
+      }
+      ClearBuffer();
+    }
+  }
+
+  void DetachFromPrev() { can_attach_to_prev_ = false; }
+
+ private:
+  std::string* prev_trailing_comments_;
+  std::vector<std::string>* detached_comments_;
+  std::string* next_leading_comments_;
+
+  std::string comment_buffer_;
+
+  // True if any comments were read into comment_buffer_.  This can be true even
+  // if comment_buffer_ is empty, namely if the comment was "/**/".
+  bool has_comment_;
+
+  // Is the comment in the comment buffer a line comment?
+  bool is_line_comment_;
+
+  // Is it still possible that we could be reading a comment attached to the
+  // previous token?
+  bool can_attach_to_prev_;
+};
+
+}  // namespace
+
+bool Tokenizer::NextWithComments(std::string* prev_trailing_comments,
+                                 std::vector<std::string>* detached_comments,
+                                 std::string* next_leading_comments) {
+  CommentCollector collector(prev_trailing_comments, detached_comments,
+                             next_leading_comments);
+
+  if (current_.type == TYPE_START) {
+    // Ignore unicode byte order mark(BOM) if it appears at the file
+    // beginning. Only UTF-8 BOM (0xEF 0xBB 0xBF) is accepted.
+    if (TryConsume(static_cast<char>(0xEF))) {
+      if (!TryConsume(static_cast<char>(0xBB)) ||
+          !TryConsume(static_cast<char>(0xBF))) {
+        AddError(
+            "Proto file starts with 0xEF but not UTF-8 BOM. "
+            "Only UTF-8 is accepted for proto file.");
+        return false;
+      }
+    }
+    collector.DetachFromPrev();
+  } else {
+    // A comment appearing on the same line must be attached to the previous
+    // declaration.
+    ConsumeZeroOrMore<WhitespaceNoNewline>();
+    switch (TryConsumeCommentStart()) {
+      case LINE_COMMENT:
+        ConsumeLineComment(collector.GetBufferForLineComment());
+
+        // Don't allow comments on subsequent lines to be attached to a trailing
+        // comment.
+        collector.Flush();
+        break;
+      case BLOCK_COMMENT:
+        ConsumeBlockComment(collector.GetBufferForBlockComment());
+
+        ConsumeZeroOrMore<WhitespaceNoNewline>();
+        if (!TryConsume('\n')) {
+          // Oops, the next token is on the same line.  If we recorded a comment
+          // we really have no idea which token it should be attached to.
+          collector.ClearBuffer();
+          return Next();
+        }
+
+        // Don't allow comments on subsequent lines to be attached to a trailing
+        // comment.
+        collector.Flush();
+        break;
+      case SLASH_NOT_COMMENT:
+        return true;
+      case NO_COMMENT:
+        if (!TryConsume('\n')) {
+          // The next token is on the same line.  There are no comments.
+          return Next();
+        }
+        break;
+    }
+  }
+
+  // OK, we are now on the line *after* the previous token.
+  while (true) {
+    ConsumeZeroOrMore<WhitespaceNoNewline>();
+
+    switch (TryConsumeCommentStart()) {
+      case LINE_COMMENT:
+        ConsumeLineComment(collector.GetBufferForLineComment());
+        break;
+      case BLOCK_COMMENT:
+        ConsumeBlockComment(collector.GetBufferForBlockComment());
+
+        // Consume the rest of the line so that we don't interpret it as a
+        // blank line the next time around the loop.
+        ConsumeZeroOrMore<WhitespaceNoNewline>();
+        TryConsume('\n');
+        break;
+      case SLASH_NOT_COMMENT:
+        return true;
+      case NO_COMMENT:
+        if (TryConsume('\n')) {
+          // Completely blank line.
+          collector.Flush();
+          collector.DetachFromPrev();
+        } else {
+          bool result = Next();
+          if (!result || current_.text == "}" || current_.text == "]" ||
+              current_.text == ")") {
+            // It looks like we're at the end of a scope.  In this case it
+            // makes no sense to attach a comment to the following token.
+            collector.Flush();
+          }
+          return result;
+        }
+        break;
+    }
+  }
+}
+
+// -------------------------------------------------------------------
+// Token-parsing helpers.  Remember that these don't need to report
+// errors since any errors should already have been reported while
+// tokenizing.  Also, these can assume that whatever text they
+// are given is text that the tokenizer actually parsed as a token
+// of the given type.
+
+bool Tokenizer::ParseInteger(const std::string& text, uint64_t max_value,
+                             uint64_t* output) {
+  // We can't just use strtoull() because (a) it accepts negative numbers,
+  // (b) We want additional range checks, (c) it reports overflows via errno.
+
+#if 0
+  const char *str_begin = text.c_str();
+  if (*str_begin == '-') return false;
+  char *str_end = nullptr;
+  errno = 0;
+  *output = std::strtoull(str_begin, &str_end, 0);
+  return (errno == 0 && str_end && *str_end == '\0' && *output <= max_value);
+#endif
+
+  const char* ptr = text.c_str();
+  int base = 10;
+  uint64_t overflow_if_mul_base = (kuint64max / 10) + 1;
+  if (ptr[0] == '0') {
+    if (ptr[1] == 'x' || ptr[1] == 'X') {
+      // This is hex.
+      base = 16;
+      overflow_if_mul_base = (kuint64max / 16) + 1;
+      ptr += 2;
+    } else {
+      // This is octal.
+      base = 8;
+      overflow_if_mul_base = (kuint64max / 8) + 1;
+    }
+  }
+
+  uint64_t result = 0;
+  // For all the leading '0's, and also the first non-zero character, we
+  // don't need to multiply.
+  while (*ptr != '\0') {
+    int digit = DigitValue(*ptr++);
+    if (digit >= base) {
+      // The token provided by Tokenizer is invalid. i.e., 099 is an invalid
+      // token, but Tokenizer still think it's integer.
+      return false;
+    }
+    if (digit != 0) {
+      result = digit;
+      break;
+    }
+  }
+  for (; *ptr != '\0'; ptr++) {
+    int digit = DigitValue(*ptr);
+    if (digit < 0 || digit >= base) {
+      // The token provided by Tokenizer is invalid. i.e., 099 is an invalid
+      // token, but Tokenizer still think it's integer.
+      return false;
+    }
+    if (result >= overflow_if_mul_base) {
+      // We know the multiply we're about to do will overflow, so exit now.
+      return false;
+    }
+    // We know that result * base won't overflow, but adding digit might...
+    result = result * base + digit;
+    // C++ guarantees defined "wrap" semantics when unsigned integer
+    // operations overflow, making this a fast way to check if adding
+    // digit made result overflow, and thus, wrap around.
+    if (result < static_cast<uint64_t>(base)) return false;
+  }
+  if (result > max_value) return false;
+
+  *output = result;
+  return true;
+}
+
+double Tokenizer::ParseFloat(const std::string& text) {
+  const char* start = text.c_str();
+  char* end;
+  double result = NoLocaleStrtod(start, &end);
+
+  // "1e" is not a valid float, but if the tokenizer reads it, it will
+  // report an error but still return it as a valid token.  We need to
+  // accept anything the tokenizer could possibly return, error or not.
+  if (*end == 'e' || *end == 'E') {
+    ++end;
+    if (*end == '-' || *end == '+') ++end;
+  }
+
+  // If the Tokenizer had allow_f_after_float_ enabled, the float may be
+  // suffixed with the letter 'f'.
+  if (*end == 'f' || *end == 'F') {
+    ++end;
+  }
+
+  GOOGLE_LOG_IF(DFATAL,
+         static_cast<size_t>(end - start) != text.size() || *start == '-')
+      << " Tokenizer::ParseFloat() passed text that could not have been"
+         " tokenized as a float: "
+      << CEscape(text);
+  return result;
+}
+
+// Helper to append a Unicode code point to a string as UTF8, without bringing
+// in any external dependencies.
+static void AppendUTF8(uint32_t code_point, std::string* output) {
+  uint32_t tmp = 0;
+  int len = 0;
+  if (code_point <= 0x7f) {
+    tmp = code_point;
+    len = 1;
+  } else if (code_point <= 0x07ff) {
+    tmp = 0x0000c080 | ((code_point & 0x07c0) << 2) | (code_point & 0x003f);
+    len = 2;
+  } else if (code_point <= 0xffff) {
+    tmp = 0x00e08080 | ((code_point & 0xf000) << 4) |
+          ((code_point & 0x0fc0) << 2) | (code_point & 0x003f);
+    len = 3;
+  } else if (code_point <= 0x10ffff) {
+    tmp = 0xf0808080 | ((code_point & 0x1c0000) << 6) |
+          ((code_point & 0x03f000) << 4) | ((code_point & 0x000fc0) << 2) |
+          (code_point & 0x003f);
+    len = 4;
+  } else {
+    // Unicode code points end at 0x10FFFF, so this is out-of-range.
+    // ConsumeString permits hex values up to 0x1FFFFF, and FetchUnicodePoint
+    // doesn't perform a range check.
+    StringAppendF(output, "\\U%08x", code_point);
+    return;
+  }
+  tmp = ghtonl(tmp);
+  output->append(reinterpret_cast<const char*>(&tmp) + sizeof(tmp) - len, len);
+}
+
+// Try to read <len> hex digits from ptr, and stuff the numeric result into
+// *result. Returns true if that many digits were successfully consumed.
+static bool ReadHexDigits(const char* ptr, int len, uint32_t* result) {
+  *result = 0;
+  if (len == 0) return false;
+  for (const char* end = ptr + len; ptr < end; ++ptr) {
+    if (*ptr == '\0') return false;
+    *result = (*result << 4) + DigitValue(*ptr);
+  }
+  return true;
+}
+
+// Handling UTF-16 surrogate pairs. UTF-16 encodes code points in the range
+// 0x10000...0x10ffff as a pair of numbers, a head surrogate followed by a trail
+// surrogate. These numbers are in a reserved range of Unicode code points, so
+// if we encounter such a pair we know how to parse it and convert it into a
+// single code point.
+static const uint32_t kMinHeadSurrogate = 0xd800;
+static const uint32_t kMaxHeadSurrogate = 0xdc00;
+static const uint32_t kMinTrailSurrogate = 0xdc00;
+static const uint32_t kMaxTrailSurrogate = 0xe000;
+
+static inline bool IsHeadSurrogate(uint32_t code_point) {
+  return (code_point >= kMinHeadSurrogate) && (code_point < kMaxHeadSurrogate);
+}
+
+static inline bool IsTrailSurrogate(uint32_t code_point) {
+  return (code_point >= kMinTrailSurrogate) &&
+         (code_point < kMaxTrailSurrogate);
+}
+
+// Combine a head and trail surrogate into a single Unicode code point.
+static uint32_t AssembleUTF16(uint32_t head_surrogate,
+                              uint32_t trail_surrogate) {
+  GOOGLE_DCHECK(IsHeadSurrogate(head_surrogate));
+  GOOGLE_DCHECK(IsTrailSurrogate(trail_surrogate));
+  return 0x10000 + (((head_surrogate - kMinHeadSurrogate) << 10) |
+                    (trail_surrogate - kMinTrailSurrogate));
+}
+
+// Convert the escape sequence parameter to a number of expected hex digits.
+static inline int UnicodeLength(char key) {
+  if (key == 'u') return 4;
+  if (key == 'U') return 8;
+  return 0;
+}
+
+// Given a pointer to the 'u' or 'U' starting a Unicode escape sequence, attempt
+// to parse that sequence. On success, returns a pointer to the first char
+// beyond that sequence, and fills in *code_point. On failure, returns ptr
+// itself.
+static const char* FetchUnicodePoint(const char* ptr, uint32_t* code_point) {
+  const char* p = ptr;
+  // Fetch the code point.
+  const int len = UnicodeLength(*p++);
+  if (!ReadHexDigits(p, len, code_point)) return ptr;
+  p += len;
+
+  // Check if the code point we read is a "head surrogate." If so, then we
+  // expect it to be immediately followed by another code point which is a valid
+  // "trail surrogate," and together they form a UTF-16 pair which decodes into
+  // a single Unicode point. Trail surrogates may only use \u, not \U.
+  if (IsHeadSurrogate(*code_point) && *p == '\\' && *(p + 1) == 'u') {
+    uint32_t trail_surrogate;
+    if (ReadHexDigits(p + 2, 4, &trail_surrogate) &&
+        IsTrailSurrogate(trail_surrogate)) {
+      *code_point = AssembleUTF16(*code_point, trail_surrogate);
+      p += 6;
+    }
+    // If this failed, then we just emit the head surrogate as a code point.
+    // It's bogus, but so is the string.
+  }
+
+  return p;
+}
+
+// The text string must begin and end with single or double quote
+// characters.
+void Tokenizer::ParseStringAppend(const std::string& text,
+                                  std::string* output) {
+  // Reminder: text[0] is always a quote character.  (If text is
+  // empty, it's invalid, so we'll just return).
+  const size_t text_size = text.size();
+  if (text_size == 0) {
+    GOOGLE_LOG(DFATAL) << " Tokenizer::ParseStringAppend() passed text that could not"
+                   " have been tokenized as a string: "
+                << CEscape(text);
+    return;
+  }
+
+  // Reserve room for new string. The branch is necessary because if
+  // there is already space available the reserve() call might
+  // downsize the output.
+  const size_t new_len = text_size + output->size();
+  if (new_len > output->capacity()) {
+    output->reserve(new_len);
+  }
+
+  // Loop through the string copying characters to "output" and
+  // interpreting escape sequences.  Note that any invalid escape
+  // sequences or other errors were already reported while tokenizing.
+  // In this case we do not need to produce valid results.
+  for (const char* ptr = text.c_str() + 1; *ptr != '\0'; ptr++) {
+    if (*ptr == '\\' && ptr[1] != '\0') {
+      // An escape sequence.
+      ++ptr;
+
+      if (OctalDigit::InClass(*ptr)) {
+        // An octal escape.  May one, two, or three digits.
+        int code = DigitValue(*ptr);
+        if (OctalDigit::InClass(ptr[1])) {
+          ++ptr;
+          code = code * 8 + DigitValue(*ptr);
+        }
+        if (OctalDigit::InClass(ptr[1])) {
+          ++ptr;
+          code = code * 8 + DigitValue(*ptr);
+        }
+        output->push_back(static_cast<char>(code));
+
+      } else if (*ptr == 'x') {
+        // A hex escape.  May zero, one, or two digits.  (The zero case
+        // will have been caught as an error earlier.)
+        int code = 0;
+        if (HexDigit::InClass(ptr[1])) {
+          ++ptr;
+          code = DigitValue(*ptr);
+        }
+        if (HexDigit::InClass(ptr[1])) {
+          ++ptr;
+          code = code * 16 + DigitValue(*ptr);
+        }
+        output->push_back(static_cast<char>(code));
+
+      } else if (*ptr == 'u' || *ptr == 'U') {
+        uint32_t unicode;
+        const char* end = FetchUnicodePoint(ptr, &unicode);
+        if (end == ptr) {
+          // Failure: Just dump out what we saw, don't try to parse it.
+          output->push_back(*ptr);
+        } else {
+          AppendUTF8(unicode, output);
+          ptr = end - 1;  // Because we're about to ++ptr.
+        }
+      } else {
+        // Some other escape code.
+        output->push_back(TranslateEscape(*ptr));
+      }
+
+    } else if (*ptr == text[0] && ptr[1] == '\0') {
+      // Ignore final quote matching the starting quote.
+    } else {
+      output->push_back(*ptr);
+    }
+  }
+}
+
+template <typename CharacterClass>
+static bool AllInClass(const std::string& s) {
+  for (const char character : s) {
+    if (!CharacterClass::InClass(character)) return false;
+  }
+  return true;
+}
+
+bool Tokenizer::IsIdentifier(const std::string& text) {
+  // Mirrors IDENTIFIER definition in Tokenizer::Next() above.
+  if (text.size() == 0) return false;
+  if (!Letter::InClass(text.at(0))) return false;
+  if (!AllInClass<Alphanumeric>(text.substr(1))) return false;
+  return true;
+}
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/zero_copy_stream.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/zero_copy_stream.cpp
new file mode 100644
index 0000000..f81555e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/zero_copy_stream.cpp
@@ -0,0 +1,55 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/io/zero_copy_stream.h>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+
+bool ZeroCopyOutputStream::WriteAliasedRaw(const void* /* data */,
+                                           int /* size */) {
+  GOOGLE_LOG(FATAL) << "This ZeroCopyOutputStream doesn't support aliasing. "
+                "Reaching here usually means a ZeroCopyOutputStream "
+                "implementation bug.";
+  return false;
+}
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/zero_copy_stream_impl.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/zero_copy_stream_impl.cpp
new file mode 100644
index 0000000..c66bc86
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/zero_copy_stream_impl.cpp
@@ -0,0 +1,372 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#ifndef _MSC_VER
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+#include <algorithm>
+#include <iostream>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/io/io_win32.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+#ifdef _WIN32
+// Win32 lseek is broken:  If invoked on a non-seekable file descriptor, its
+// return value is undefined.  We re-define it to always produce an error.
+#define lseek(fd, offset, origin) ((off_t)-1)
+// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
+// them like we do below.
+using google::protobuf::io::win32::access;
+using google::protobuf::io::win32::close;
+using google::protobuf::io::win32::open;
+using google::protobuf::io::win32::read;
+using google::protobuf::io::win32::write;
+#endif
+
+namespace {
+
+// EINTR sucks.
+int close_no_eintr(int fd) {
+  int result;
+  do {
+    result = close(fd);
+  } while (result < 0 && errno == EINTR);
+  return result;
+}
+
+}  // namespace
+
+// ===================================================================
+
+FileInputStream::FileInputStream(int file_descriptor, int block_size)
+    : copying_input_(file_descriptor), impl_(&copying_input_, block_size) {}
+
+bool FileInputStream::Close() { return copying_input_.Close(); }
+
+bool FileInputStream::Next(const void** data, int* size) {
+  return impl_.Next(data, size);
+}
+
+void FileInputStream::BackUp(int count) { impl_.BackUp(count); }
+
+bool FileInputStream::Skip(int count) { return impl_.Skip(count); }
+
+int64_t FileInputStream::ByteCount() const { return impl_.ByteCount(); }
+
+FileInputStream::CopyingFileInputStream::CopyingFileInputStream(
+    int file_descriptor)
+    : file_(file_descriptor),
+      close_on_delete_(false),
+      is_closed_(false),
+      errno_(0),
+      previous_seek_failed_(false) {
+#ifndef _WIN32
+  int flags = fcntl(file_, F_GETFL);
+  flags &= ~O_NONBLOCK;
+  fcntl(file_, F_SETFL, flags);
+#endif
+}
+
+FileInputStream::CopyingFileInputStream::~CopyingFileInputStream() {
+  if (close_on_delete_) {
+    if (!Close()) {
+      GOOGLE_LOG(ERROR) << "close() failed: " << strerror(errno_);
+    }
+  }
+}
+
+bool FileInputStream::CopyingFileInputStream::Close() {
+  GOOGLE_CHECK(!is_closed_);
+
+  is_closed_ = true;
+  if (close_no_eintr(file_) != 0) {
+    // The docs on close() do not specify whether a file descriptor is still
+    // open after close() fails with EIO.  However, the glibc source code
+    // seems to indicate that it is not.
+    errno_ = errno;
+    return false;
+  }
+
+  return true;
+}
+
+int FileInputStream::CopyingFileInputStream::Read(void* buffer, int size) {
+  GOOGLE_CHECK(!is_closed_);
+
+  int result;
+  do {
+    result = read(file_, buffer, size);
+  } while (result < 0 && errno == EINTR);
+
+  if (result < 0) {
+    // Read error (not EOF).
+    errno_ = errno;
+  }
+
+  return result;
+}
+
+int FileInputStream::CopyingFileInputStream::Skip(int count) {
+  GOOGLE_CHECK(!is_closed_);
+
+  if (!previous_seek_failed_ && lseek(file_, count, SEEK_CUR) != (off_t)-1) {
+    // Seek succeeded.
+    return count;
+  } else {
+    // Failed to seek.
+
+    // Note to self:  Don't seek again.  This file descriptor doesn't
+    // support it.
+    previous_seek_failed_ = true;
+
+    // Use the default implementation.
+    return CopyingInputStream::Skip(count);
+  }
+}
+
+// ===================================================================
+
+FileOutputStream::FileOutputStream(int file_descriptor, int /*block_size*/)
+    : CopyingOutputStreamAdaptor(&copying_output_),
+      copying_output_(file_descriptor) {}
+
+bool FileOutputStream::Close() {
+  bool flush_succeeded = Flush();
+  return copying_output_.Close() && flush_succeeded;
+}
+
+FileOutputStream::CopyingFileOutputStream::CopyingFileOutputStream(
+    int file_descriptor)
+    : file_(file_descriptor),
+      close_on_delete_(false),
+      is_closed_(false),
+      errno_(0) {}
+
+FileOutputStream::~FileOutputStream() { Flush(); }
+
+FileOutputStream::CopyingFileOutputStream::~CopyingFileOutputStream() {
+  if (close_on_delete_) {
+    if (!Close()) {
+      GOOGLE_LOG(ERROR) << "close() failed: " << strerror(errno_);
+    }
+  }
+}
+
+bool FileOutputStream::CopyingFileOutputStream::Close() {
+  GOOGLE_CHECK(!is_closed_);
+
+  is_closed_ = true;
+  if (close_no_eintr(file_) != 0) {
+    // The docs on close() do not specify whether a file descriptor is still
+    // open after close() fails with EIO.  However, the glibc source code
+    // seems to indicate that it is not.
+    errno_ = errno;
+    return false;
+  }
+
+  return true;
+}
+
+bool FileOutputStream::CopyingFileOutputStream::Write(const void* buffer,
+                                                      int size) {
+  GOOGLE_CHECK(!is_closed_);
+  int total_written = 0;
+
+  const uint8_t* buffer_base = reinterpret_cast<const uint8_t*>(buffer);
+
+  while (total_written < size) {
+    int bytes;
+    do {
+      bytes = write(file_, buffer_base + total_written, size - total_written);
+    } while (bytes < 0 && errno == EINTR);
+
+    if (bytes <= 0) {
+      // Write error.
+
+      // FIXME(kenton):  According to the man page, if write() returns zero,
+      //   there was no error; write() simply did not write anything.  It's
+      //   unclear under what circumstances this might happen, but presumably
+      //   errno won't be set in this case.  I am confused as to how such an
+      //   event should be handled.  For now I'm treating it as an error, since
+      //   retrying seems like it could lead to an infinite loop.  I suspect
+      //   this never actually happens anyway.
+
+      if (bytes < 0) {
+        errno_ = errno;
+      }
+      return false;
+    }
+    total_written += bytes;
+  }
+
+  return true;
+}
+
+// ===================================================================
+
+IstreamInputStream::IstreamInputStream(std::istream* input, int block_size)
+    : copying_input_(input), impl_(&copying_input_, block_size) {}
+
+bool IstreamInputStream::Next(const void** data, int* size) {
+  return impl_.Next(data, size);
+}
+
+void IstreamInputStream::BackUp(int count) { impl_.BackUp(count); }
+
+bool IstreamInputStream::Skip(int count) { return impl_.Skip(count); }
+
+int64_t IstreamInputStream::ByteCount() const { return impl_.ByteCount(); }
+
+IstreamInputStream::CopyingIstreamInputStream::CopyingIstreamInputStream(
+    std::istream* input)
+    : input_(input) {}
+
+IstreamInputStream::CopyingIstreamInputStream::~CopyingIstreamInputStream() {}
+
+int IstreamInputStream::CopyingIstreamInputStream::Read(void* buffer,
+                                                        int size) {
+  input_->read(reinterpret_cast<char*>(buffer), size);
+  int result = input_->gcount();
+  if (result == 0 && input_->fail() && !input_->eof()) {
+    return -1;
+  }
+  return result;
+}
+
+// ===================================================================
+
+OstreamOutputStream::OstreamOutputStream(std::ostream* output, int block_size)
+    : copying_output_(output), impl_(&copying_output_, block_size) {}
+
+OstreamOutputStream::~OstreamOutputStream() { impl_.Flush(); }
+
+bool OstreamOutputStream::Next(void** data, int* size) {
+  return impl_.Next(data, size);
+}
+
+void OstreamOutputStream::BackUp(int count) { impl_.BackUp(count); }
+
+int64_t OstreamOutputStream::ByteCount() const { return impl_.ByteCount(); }
+
+OstreamOutputStream::CopyingOstreamOutputStream::CopyingOstreamOutputStream(
+    std::ostream* output)
+    : output_(output) {}
+
+OstreamOutputStream::CopyingOstreamOutputStream::~CopyingOstreamOutputStream() {
+}
+
+bool OstreamOutputStream::CopyingOstreamOutputStream::Write(const void* buffer,
+                                                            int size) {
+  output_->write(reinterpret_cast<const char*>(buffer), size);
+  return output_->good();
+}
+
+// ===================================================================
+
+ConcatenatingInputStream::ConcatenatingInputStream(
+    ZeroCopyInputStream* const streams[], int count)
+    : streams_(streams), stream_count_(count), bytes_retired_(0) {
+}
+
+bool ConcatenatingInputStream::Next(const void** data, int* size) {
+  while (stream_count_ > 0) {
+    if (streams_[0]->Next(data, size)) return true;
+
+    // That stream is done.  Advance to the next one.
+    bytes_retired_ += streams_[0]->ByteCount();
+    ++streams_;
+    --stream_count_;
+  }
+
+  // No more streams.
+  return false;
+}
+
+void ConcatenatingInputStream::BackUp(int count) {
+  if (stream_count_ > 0) {
+    streams_[0]->BackUp(count);
+  } else {
+    GOOGLE_LOG(DFATAL) << "Can't BackUp() after failed Next().";
+  }
+}
+
+bool ConcatenatingInputStream::Skip(int count) {
+  while (stream_count_ > 0) {
+    // Assume that ByteCount() can be used to find out how much we actually
+    // skipped when Skip() fails.
+    int64_t target_byte_count = streams_[0]->ByteCount() + count;
+    if (streams_[0]->Skip(count)) return true;
+
+    // Hit the end of the stream.  Figure out how many more bytes we still have
+    // to skip.
+    int64_t final_byte_count = streams_[0]->ByteCount();
+    GOOGLE_DCHECK_LT(final_byte_count, target_byte_count);
+    count = target_byte_count - final_byte_count;
+
+    // That stream is done.  Advance to the next one.
+    bytes_retired_ += final_byte_count;
+    ++streams_;
+    --stream_count_;
+  }
+
+  return false;
+}
+
+int64_t ConcatenatingInputStream::ByteCount() const {
+  if (stream_count_ == 0) {
+    return bytes_retired_;
+  } else {
+    return bytes_retired_ + streams_[0]->ByteCount();
+  }
+}
+
+
+// ===================================================================
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/zero_copy_stream_impl_lite.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/zero_copy_stream_impl_lite.cpp
new file mode 100644
index 0000000..b3dfd84
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/io/zero_copy_stream_impl_lite.cpp
@@ -0,0 +1,470 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+
+#include <algorithm>
+#include <limits>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+namespace google {
+namespace protobuf {
+namespace io {
+
+namespace {
+
+// Default block size for Copying{In,Out}putStreamAdaptor.
+static const int kDefaultBlockSize = 8192;
+
+}  // namespace
+
+// ===================================================================
+
+ArrayInputStream::ArrayInputStream(const void* data, int size, int block_size)
+    : data_(reinterpret_cast<const uint8_t*>(data)),
+      size_(size),
+      block_size_(block_size > 0 ? block_size : size),
+      position_(0),
+      last_returned_size_(0) {}
+
+bool ArrayInputStream::Next(const void** data, int* size) {
+  if (position_ < size_) {
+    last_returned_size_ = std::min(block_size_, size_ - position_);
+    *data = data_ + position_;
+    *size = last_returned_size_;
+    position_ += last_returned_size_;
+    return true;
+  } else {
+    // We're at the end of the array.
+    last_returned_size_ = 0;  // Don't let caller back up.
+    return false;
+  }
+}
+
+void ArrayInputStream::BackUp(int count) {
+  GOOGLE_CHECK_GT(last_returned_size_, 0)
+      << "BackUp() can only be called after a successful Next().";
+  GOOGLE_CHECK_LE(count, last_returned_size_);
+  GOOGLE_CHECK_GE(count, 0);
+  position_ -= count;
+  last_returned_size_ = 0;  // Don't let caller back up further.
+}
+
+bool ArrayInputStream::Skip(int count) {
+  GOOGLE_CHECK_GE(count, 0);
+  last_returned_size_ = 0;  // Don't let caller back up.
+  if (count > size_ - position_) {
+    position_ = size_;
+    return false;
+  } else {
+    position_ += count;
+    return true;
+  }
+}
+
+int64_t ArrayInputStream::ByteCount() const { return position_; }
+
+
+// ===================================================================
+
+ArrayOutputStream::ArrayOutputStream(void* data, int size, int block_size)
+    : data_(reinterpret_cast<uint8_t*>(data)),
+      size_(size),
+      block_size_(block_size > 0 ? block_size : size),
+      position_(0),
+      last_returned_size_(0) {}
+
+bool ArrayOutputStream::Next(void** data, int* size) {
+  if (position_ < size_) {
+    last_returned_size_ = std::min(block_size_, size_ - position_);
+    *data = data_ + position_;
+    *size = last_returned_size_;
+    position_ += last_returned_size_;
+    return true;
+  } else {
+    // We're at the end of the array.
+    last_returned_size_ = 0;  // Don't let caller back up.
+    return false;
+  }
+}
+
+void ArrayOutputStream::BackUp(int count) {
+  GOOGLE_CHECK_LE(count, last_returned_size_)
+      << "BackUp() can not exceed the size of the last Next() call.";
+  GOOGLE_CHECK_GE(count, 0);
+  position_ -= count;
+  last_returned_size_ -= count;
+}
+
+int64_t ArrayOutputStream::ByteCount() const { return position_; }
+
+// ===================================================================
+
+StringOutputStream::StringOutputStream(std::string* target) : target_(target) {}
+
+bool StringOutputStream::Next(void** data, int* size) {
+  GOOGLE_CHECK(target_ != NULL);
+  size_t old_size = target_->size();
+
+  // Grow the string.
+  size_t new_size;
+  if (old_size < target_->capacity()) {
+    // Resize the string to match its capacity, since we can get away
+    // without a memory allocation this way.
+    new_size = target_->capacity();
+  } else {
+    // Size has reached capacity, try to double it.
+    new_size = old_size * 2;
+  }
+  // Avoid integer overflow in returned '*size'.
+  new_size = std::min(new_size, old_size + std::numeric_limits<int>::max());
+  // Increase the size, also make sure that it is at least kMinimumSize.
+  STLStringResizeUninitialized(
+      target_,
+      std::max(new_size,
+               kMinimumSize + 0));  // "+ 0" works around GCC4 weirdness.
+
+  *data = mutable_string_data(target_) + old_size;
+  *size = target_->size() - old_size;
+  return true;
+}
+
+void StringOutputStream::BackUp(int count) {
+  GOOGLE_CHECK_GE(count, 0);
+  GOOGLE_CHECK(target_ != NULL);
+  GOOGLE_CHECK_LE(static_cast<size_t>(count), target_->size());
+  target_->resize(target_->size() - count);
+}
+
+int64_t StringOutputStream::ByteCount() const {
+  GOOGLE_CHECK(target_ != NULL);
+  return target_->size();
+}
+
+// ===================================================================
+
+int CopyingInputStream::Skip(int count) {
+  char junk[4096];
+  int skipped = 0;
+  while (skipped < count) {
+    int bytes = Read(junk, std::min(count - skipped,
+                                    implicit_cast<int>(sizeof(junk))));
+    if (bytes <= 0) {
+      // EOF or read error.
+      return skipped;
+    }
+    skipped += bytes;
+  }
+  return skipped;
+}
+
+CopyingInputStreamAdaptor::CopyingInputStreamAdaptor(
+    CopyingInputStream* copying_stream, int block_size)
+    : copying_stream_(copying_stream),
+      owns_copying_stream_(false),
+      failed_(false),
+      position_(0),
+      buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize),
+      buffer_used_(0),
+      backup_bytes_(0) {}
+
+CopyingInputStreamAdaptor::~CopyingInputStreamAdaptor() {
+  if (owns_copying_stream_) {
+    delete copying_stream_;
+  }
+}
+
+bool CopyingInputStreamAdaptor::Next(const void** data, int* size) {
+  if (failed_) {
+    // Already failed on a previous read.
+    return false;
+  }
+
+  AllocateBufferIfNeeded();
+
+  if (backup_bytes_ > 0) {
+    // We have data left over from a previous BackUp(), so just return that.
+    *data = buffer_.get() + buffer_used_ - backup_bytes_;
+    *size = backup_bytes_;
+    backup_bytes_ = 0;
+    return true;
+  }
+
+  // Read new data into the buffer.
+  buffer_used_ = copying_stream_->Read(buffer_.get(), buffer_size_);
+  if (buffer_used_ <= 0) {
+    // EOF or read error.  We don't need the buffer anymore.
+    if (buffer_used_ < 0) {
+      // Read error (not EOF).
+      failed_ = true;
+    }
+    FreeBuffer();
+    return false;
+  }
+  position_ += buffer_used_;
+
+  *size = buffer_used_;
+  *data = buffer_.get();
+  return true;
+}
+
+void CopyingInputStreamAdaptor::BackUp(int count) {
+  GOOGLE_CHECK(backup_bytes_ == 0 && buffer_.get() != NULL)
+      << " BackUp() can only be called after Next().";
+  GOOGLE_CHECK_LE(count, buffer_used_)
+      << " Can't back up over more bytes than were returned by the last call"
+         " to Next().";
+  GOOGLE_CHECK_GE(count, 0) << " Parameter to BackUp() can't be negative.";
+
+  backup_bytes_ = count;
+}
+
+bool CopyingInputStreamAdaptor::Skip(int count) {
+  GOOGLE_CHECK_GE(count, 0);
+
+  if (failed_) {
+    // Already failed on a previous read.
+    return false;
+  }
+
+  // First skip any bytes left over from a previous BackUp().
+  if (backup_bytes_ >= count) {
+    // We have more data left over than we're trying to skip.  Just chop it.
+    backup_bytes_ -= count;
+    return true;
+  }
+
+  count -= backup_bytes_;
+  backup_bytes_ = 0;
+
+  int skipped = copying_stream_->Skip(count);
+  position_ += skipped;
+  return skipped == count;
+}
+
+int64_t CopyingInputStreamAdaptor::ByteCount() const {
+  return position_ - backup_bytes_;
+}
+
+void CopyingInputStreamAdaptor::AllocateBufferIfNeeded() {
+  if (buffer_.get() == NULL) {
+    buffer_.reset(new uint8_t[buffer_size_]);
+  }
+}
+
+void CopyingInputStreamAdaptor::FreeBuffer() {
+  GOOGLE_CHECK_EQ(backup_bytes_, 0);
+  buffer_used_ = 0;
+  buffer_.reset();
+}
+
+// ===================================================================
+
+CopyingOutputStreamAdaptor::CopyingOutputStreamAdaptor(
+    CopyingOutputStream* copying_stream, int block_size)
+    : copying_stream_(copying_stream),
+      owns_copying_stream_(false),
+      failed_(false),
+      position_(0),
+      buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize),
+      buffer_used_(0) {}
+
+CopyingOutputStreamAdaptor::~CopyingOutputStreamAdaptor() {
+  WriteBuffer();
+  if (owns_copying_stream_) {
+    delete copying_stream_;
+  }
+}
+
+bool CopyingOutputStreamAdaptor::Flush() { return WriteBuffer(); }
+
+bool CopyingOutputStreamAdaptor::Next(void** data, int* size) {
+  if (buffer_used_ == buffer_size_) {
+    if (!WriteBuffer()) return false;
+  }
+
+  AllocateBufferIfNeeded();
+
+  *data = buffer_.get() + buffer_used_;
+  *size = buffer_size_ - buffer_used_;
+  buffer_used_ = buffer_size_;
+  return true;
+}
+
+void CopyingOutputStreamAdaptor::BackUp(int count) {
+  if (count == 0) {
+    Flush();
+    return;
+  }
+  GOOGLE_CHECK_GE(count, 0);
+  GOOGLE_CHECK_EQ(buffer_used_, buffer_size_)
+      << " BackUp() can only be called after Next().";
+  GOOGLE_CHECK_LE(count, buffer_used_)
+      << " Can't back up over more bytes than were returned by the last call"
+         " to Next().";
+
+  buffer_used_ -= count;
+}
+
+int64_t CopyingOutputStreamAdaptor::ByteCount() const {
+  return position_ + buffer_used_;
+}
+
+bool CopyingOutputStreamAdaptor::WriteAliasedRaw(const void* data, int size) {
+  if (size >= buffer_size_) {
+    if (!Flush() || !copying_stream_->Write(data, size)) {
+      return false;
+    }
+    GOOGLE_DCHECK_EQ(buffer_used_, 0);
+    position_ += size;
+    return true;
+  }
+
+  void* out;
+  int out_size;
+  while (true) {
+    if (!Next(&out, &out_size)) {
+      return false;
+    }
+
+    if (size <= out_size) {
+      std::memcpy(out, data, size);
+      BackUp(out_size - size);
+      return true;
+    }
+
+    std::memcpy(out, data, out_size);
+    data = static_cast<const char*>(data) + out_size;
+    size -= out_size;
+  }
+  return true;
+}
+
+
+bool CopyingOutputStreamAdaptor::WriteBuffer() {
+  if (failed_) {
+    // Already failed on a previous write.
+    return false;
+  }
+
+  if (buffer_used_ == 0) return true;
+
+  if (copying_stream_->Write(buffer_.get(), buffer_used_)) {
+    position_ += buffer_used_;
+    buffer_used_ = 0;
+    return true;
+  } else {
+    failed_ = true;
+    FreeBuffer();
+    return false;
+  }
+}
+
+void CopyingOutputStreamAdaptor::AllocateBufferIfNeeded() {
+  if (buffer_ == NULL) {
+    buffer_.reset(new uint8_t[buffer_size_]);
+  }
+}
+
+void CopyingOutputStreamAdaptor::FreeBuffer() {
+  buffer_used_ = 0;
+  buffer_.reset();
+}
+
+// ===================================================================
+
+LimitingInputStream::LimitingInputStream(ZeroCopyInputStream* input,
+                                         int64_t limit)
+    : input_(input), limit_(limit) {
+  prior_bytes_read_ = input_->ByteCount();
+}
+
+LimitingInputStream::~LimitingInputStream() {
+  // If we overshot the limit, back up.
+  if (limit_ < 0) input_->BackUp(-limit_);
+}
+
+bool LimitingInputStream::Next(const void** data, int* size) {
+  if (limit_ <= 0) return false;
+  if (!input_->Next(data, size)) return false;
+
+  limit_ -= *size;
+  if (limit_ < 0) {
+    // We overshot the limit.  Reduce *size to hide the rest of the buffer.
+    *size += limit_;
+  }
+  return true;
+}
+
+void LimitingInputStream::BackUp(int count) {
+  if (limit_ < 0) {
+    input_->BackUp(count - limit_);
+    limit_ = count;
+  } else {
+    input_->BackUp(count);
+    limit_ += count;
+  }
+}
+
+bool LimitingInputStream::Skip(int count) {
+  if (count > limit_) {
+    if (limit_ < 0) return false;
+    input_->Skip(limit_);
+    limit_ = 0;
+    return false;
+  } else {
+    if (!input_->Skip(count)) return false;
+    limit_ -= count;
+    return true;
+  }
+}
+
+int64_t LimitingInputStream::ByteCount() const {
+  if (limit_ < 0) {
+    return input_->ByteCount() + limit_ - prior_bytes_read_;
+  } else {
+    return input_->ByteCount() - prior_bytes_read_;
+  }
+}
+
+
+// ===================================================================
+
+}  // namespace io
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/map.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/map.cpp
new file mode 100644
index 0000000..d60a9a2
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/map.cpp
@@ -0,0 +1,41 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/map.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+void* const kGlobalEmptyTable[kGlobalEmptyTableSize] = {nullptr};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/map_field.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/map_field.cpp
new file mode 100644
index 0000000..ed662df
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/map_field.cpp
@@ -0,0 +1,654 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/map_field.h>
+
+#include <vector>
+
+#include <google/protobuf/map_field_inl.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+void MapFieldBase::Destruct() {
+  if (arena_ == nullptr) {
+    delete repeated_field_;
+  }
+  repeated_field_ = nullptr;
+}
+
+const RepeatedPtrFieldBase& MapFieldBase::GetRepeatedField() const {
+  ConstAccess();
+  SyncRepeatedFieldWithMap();
+  return *reinterpret_cast<RepeatedPtrFieldBase*>(repeated_field_);
+}
+
+RepeatedPtrFieldBase* MapFieldBase::MutableRepeatedField() {
+  MutableAccess();
+  SyncRepeatedFieldWithMap();
+  SetRepeatedDirty();
+  return reinterpret_cast<RepeatedPtrFieldBase*>(repeated_field_);
+}
+
+void MapFieldBase::SwapState(MapFieldBase* other) {
+  // a relaxed swap of the atomic
+  auto other_state = other->state_.load(std::memory_order_relaxed);
+  auto this_state = state_.load(std::memory_order_relaxed);
+  other->state_.store(this_state, std::memory_order_relaxed);
+  state_.store(other_state, std::memory_order_relaxed);
+}
+
+void SwapRepeatedPtrToNull(RepeatedPtrField<Message>** from,
+                           RepeatedPtrField<Message>** to, Arena* from_arena,
+                           Arena* to_arena) {
+  GOOGLE_DCHECK(*from != nullptr);
+  GOOGLE_DCHECK(*to == nullptr);
+  *to = Arena::CreateMessage<RepeatedPtrField<Message> >(to_arena);
+  **to = std::move(**from);
+  if (from_arena == nullptr) {
+    delete *from;
+  }
+  *from = nullptr;
+}
+
+void MapFieldBase::Swap(MapFieldBase* other) {
+  if (arena_ == other->arena_) {
+    InternalSwap(other);
+    return;
+  }
+  if (repeated_field_ != nullptr || other->repeated_field_ != nullptr) {
+    if (repeated_field_ == nullptr) {
+      SwapRepeatedPtrToNull(&other->repeated_field_, &repeated_field_,
+                            other->arena_, arena_);
+    } else if (other->repeated_field_ == nullptr) {
+      SwapRepeatedPtrToNull(&repeated_field_, &other->repeated_field_, arena_,
+                            other->arena_);
+    } else {
+      repeated_field_->Swap(other->repeated_field_);
+    }
+  }
+  SwapState(other);
+}
+
+void MapFieldBase::UnsafeShallowSwap(MapFieldBase* other) {
+  GOOGLE_DCHECK_EQ(arena_, other->arena_);
+  InternalSwap(other);
+}
+
+void MapFieldBase::InternalSwap(MapFieldBase* other) {
+  std::swap(arena_, other->arena_);
+  std::swap(repeated_field_, other->repeated_field_);
+  SwapState(other);
+}
+
+size_t MapFieldBase::SpaceUsedExcludingSelfLong() const {
+  ConstAccess();
+  mutex_.Lock();
+  size_t size = SpaceUsedExcludingSelfNoLock();
+  mutex_.Unlock();
+  ConstAccess();
+  return size;
+}
+
+size_t MapFieldBase::SpaceUsedExcludingSelfNoLock() const {
+  if (repeated_field_ != nullptr) {
+    return repeated_field_->SpaceUsedExcludingSelfLong();
+  } else {
+    return 0;
+  }
+}
+
+bool MapFieldBase::IsMapValid() const {
+  ConstAccess();
+  // "Acquire" insures the operation after SyncRepeatedFieldWithMap won't get
+  // executed before state_ is checked.
+  int state = state_.load(std::memory_order_acquire);
+  return state != STATE_MODIFIED_REPEATED;
+}
+
+bool MapFieldBase::IsRepeatedFieldValid() const {
+  ConstAccess();
+  int state = state_.load(std::memory_order_acquire);
+  return state != STATE_MODIFIED_MAP;
+}
+
+void MapFieldBase::SetMapDirty() {
+  MutableAccess();
+  // These are called by (non-const) mutator functions. So by our API it's the
+  // callers responsibility to have these calls properly ordered.
+  state_.store(STATE_MODIFIED_MAP, std::memory_order_relaxed);
+}
+
+void MapFieldBase::SetRepeatedDirty() {
+  MutableAccess();
+  // These are called by (non-const) mutator functions. So by our API it's the
+  // callers responsibility to have these calls properly ordered.
+  state_.store(STATE_MODIFIED_REPEATED, std::memory_order_relaxed);
+}
+
+void MapFieldBase::SyncRepeatedFieldWithMap() const {
+  ConstAccess();
+  // acquire here matches with release below to ensure that we can only see a
+  // value of CLEAN after all previous changes have been synced.
+  switch (state_.load(std::memory_order_acquire)) {
+    case STATE_MODIFIED_MAP:
+      mutex_.Lock();
+      // Double check state, because another thread may have seen the same
+      // state and done the synchronization before the current thread.
+      if (state_.load(std::memory_order_relaxed) == STATE_MODIFIED_MAP) {
+        SyncRepeatedFieldWithMapNoLock();
+        state_.store(CLEAN, std::memory_order_release);
+      }
+      mutex_.Unlock();
+      ConstAccess();
+      break;
+    case CLEAN:
+      mutex_.Lock();
+      // Double check state
+      if (state_.load(std::memory_order_relaxed) == CLEAN) {
+        if (repeated_field_ == nullptr) {
+          repeated_field_ =
+              Arena::CreateMessage<RepeatedPtrField<Message> >(arena_);
+        }
+        state_.store(CLEAN, std::memory_order_release);
+      }
+      mutex_.Unlock();
+      ConstAccess();
+      break;
+    default:
+      break;
+  }
+}
+
+void MapFieldBase::SyncRepeatedFieldWithMapNoLock() const {
+  if (repeated_field_ == nullptr) {
+    repeated_field_ = Arena::CreateMessage<RepeatedPtrField<Message> >(arena_);
+  }
+}
+
+void MapFieldBase::SyncMapWithRepeatedField() const {
+  ConstAccess();
+  // acquire here matches with release below to ensure that we can only see a
+  // value of CLEAN after all previous changes have been synced.
+  if (state_.load(std::memory_order_acquire) == STATE_MODIFIED_REPEATED) {
+    mutex_.Lock();
+    // Double check state, because another thread may have seen the same state
+    // and done the synchronization before the current thread.
+    if (state_.load(std::memory_order_relaxed) == STATE_MODIFIED_REPEATED) {
+      SyncMapWithRepeatedFieldNoLock();
+      state_.store(CLEAN, std::memory_order_release);
+    }
+    mutex_.Unlock();
+    ConstAccess();
+  }
+}
+
+// ------------------DynamicMapField------------------
+DynamicMapField::DynamicMapField(const Message* default_entry)
+    : default_entry_(default_entry) {}
+
+DynamicMapField::DynamicMapField(const Message* default_entry, Arena* arena)
+    : TypeDefinedMapFieldBase<MapKey, MapValueRef>(arena),
+      map_(arena),
+      default_entry_(default_entry) {}
+
+DynamicMapField::~DynamicMapField() {
+  if (arena_ == nullptr) {
+    // DynamicMapField owns map values. Need to delete them before clearing the
+    // map.
+    for (auto& kv : map_) {
+      kv.second.DeleteData();
+    }
+    map_.clear();
+  }
+  Destruct();
+}
+
+int DynamicMapField::size() const { return GetMap().size(); }
+
+void DynamicMapField::Clear() {
+  Map<MapKey, MapValueRef>* map = &const_cast<DynamicMapField*>(this)->map_;
+  if (MapFieldBase::arena_ == nullptr) {
+    for (Map<MapKey, MapValueRef>::iterator iter = map->begin();
+         iter != map->end(); ++iter) {
+      iter->second.DeleteData();
+    }
+  }
+
+  map->clear();
+
+  if (MapFieldBase::repeated_field_ != nullptr) {
+    MapFieldBase::repeated_field_->Clear();
+  }
+  // Data in map and repeated field are both empty, but we can't set status
+  // CLEAN which will invalidate previous reference to map.
+  MapFieldBase::SetMapDirty();
+}
+
+bool DynamicMapField::ContainsMapKey(const MapKey& map_key) const {
+  const Map<MapKey, MapValueRef>& map = GetMap();
+  Map<MapKey, MapValueRef>::const_iterator iter = map.find(map_key);
+  return iter != map.end();
+}
+
+void DynamicMapField::AllocateMapValue(MapValueRef* map_val) {
+  const FieldDescriptor* val_des = default_entry_->GetDescriptor()->map_value();
+  map_val->SetType(val_des->cpp_type());
+  // Allocate memory for the MapValueRef, and initialize to
+  // default value.
+  switch (val_des->cpp_type()) {
+#define HANDLE_TYPE(CPPTYPE, TYPE)                           \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE: {                 \
+    TYPE* value = Arena::Create<TYPE>(MapFieldBase::arena_); \
+    map_val->SetValue(value);                                \
+    break;                                                   \
+  }
+    HANDLE_TYPE(INT32, int32_t);
+    HANDLE_TYPE(INT64, int64_t);
+    HANDLE_TYPE(UINT32, uint32_t);
+    HANDLE_TYPE(UINT64, uint64_t);
+    HANDLE_TYPE(DOUBLE, double);
+    HANDLE_TYPE(FLOAT, float);
+    HANDLE_TYPE(BOOL, bool);
+    HANDLE_TYPE(STRING, std::string);
+    HANDLE_TYPE(ENUM, int32_t);
+#undef HANDLE_TYPE
+    case FieldDescriptor::CPPTYPE_MESSAGE: {
+      const Message& message =
+          default_entry_->GetReflection()->GetMessage(*default_entry_, val_des);
+      Message* value = message.New(MapFieldBase::arena_);
+      map_val->SetValue(value);
+      break;
+    }
+  }
+}
+
+bool DynamicMapField::InsertOrLookupMapValue(const MapKey& map_key,
+                                             MapValueRef* val) {
+  // Always use mutable map because users may change the map value by
+  // MapValueRef.
+  Map<MapKey, MapValueRef>* map = MutableMap();
+  Map<MapKey, MapValueRef>::iterator iter = map->find(map_key);
+  if (iter == map->end()) {
+    MapValueRef& map_val = map_[map_key];
+    AllocateMapValue(&map_val);
+    val->CopyFrom(map_val);
+    return true;
+  }
+  // map_key is already in the map. Make sure (*map)[map_key] is not called.
+  // [] may reorder the map and iterators.
+  val->CopyFrom(iter->second);
+  return false;
+}
+
+bool DynamicMapField::LookupMapValue(const MapKey& map_key,
+                                     MapValueConstRef* val) const {
+  const Map<MapKey, MapValueRef>& map = GetMap();
+  Map<MapKey, MapValueRef>::const_iterator iter = map.find(map_key);
+  if (iter == map.end()) {
+    return false;
+  }
+  // map_key is already in the map. Make sure (*map)[map_key] is not called.
+  // [] may reorder the map and iterators.
+  val->CopyFrom(iter->second);
+  return true;
+}
+
+bool DynamicMapField::DeleteMapValue(const MapKey& map_key) {
+  MapFieldBase::SyncMapWithRepeatedField();
+  Map<MapKey, MapValueRef>::iterator iter = map_.find(map_key);
+  if (iter == map_.end()) {
+    return false;
+  }
+  // Set map dirty only if the delete is successful.
+  MapFieldBase::SetMapDirty();
+  if (MapFieldBase::arena_ == nullptr) {
+    iter->second.DeleteData();
+  }
+  map_.erase(iter);
+  return true;
+}
+
+const Map<MapKey, MapValueRef>& DynamicMapField::GetMap() const {
+  MapFieldBase::SyncMapWithRepeatedField();
+  return map_;
+}
+
+Map<MapKey, MapValueRef>* DynamicMapField::MutableMap() {
+  MapFieldBase::SyncMapWithRepeatedField();
+  MapFieldBase::SetMapDirty();
+  return &map_;
+}
+
+void DynamicMapField::SetMapIteratorValue(MapIterator* map_iter) const {
+  Map<MapKey, MapValueRef>::const_iterator iter =
+      TypeDefinedMapFieldBase<MapKey, MapValueRef>::InternalGetIterator(
+          map_iter);
+  if (iter == map_.end()) return;
+  map_iter->key_.CopyFrom(iter->first);
+  map_iter->value_.CopyFrom(iter->second);
+}
+
+void DynamicMapField::MergeFrom(const MapFieldBase& other) {
+  GOOGLE_DCHECK(IsMapValid() && other.IsMapValid());
+  Map<MapKey, MapValueRef>* map = MutableMap();
+  const DynamicMapField& other_field =
+      reinterpret_cast<const DynamicMapField&>(other);
+  for (Map<MapKey, MapValueRef>::const_iterator other_it =
+           other_field.map_.begin();
+       other_it != other_field.map_.end(); ++other_it) {
+    Map<MapKey, MapValueRef>::iterator iter = map->find(other_it->first);
+    MapValueRef* map_val;
+    if (iter == map->end()) {
+      map_val = &map_[other_it->first];
+      AllocateMapValue(map_val);
+    } else {
+      map_val = &iter->second;
+    }
+
+    // Copy map value
+    const FieldDescriptor* field_descriptor =
+        default_entry_->GetDescriptor()->map_value();
+    switch (field_descriptor->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_INT32: {
+        map_val->SetInt32Value(other_it->second.GetInt32Value());
+        break;
+      }
+      case FieldDescriptor::CPPTYPE_INT64: {
+        map_val->SetInt64Value(other_it->second.GetInt64Value());
+        break;
+      }
+      case FieldDescriptor::CPPTYPE_UINT32: {
+        map_val->SetUInt32Value(other_it->second.GetUInt32Value());
+        break;
+      }
+      case FieldDescriptor::CPPTYPE_UINT64: {
+        map_val->SetUInt64Value(other_it->second.GetUInt64Value());
+        break;
+      }
+      case FieldDescriptor::CPPTYPE_FLOAT: {
+        map_val->SetFloatValue(other_it->second.GetFloatValue());
+        break;
+      }
+      case FieldDescriptor::CPPTYPE_DOUBLE: {
+        map_val->SetDoubleValue(other_it->second.GetDoubleValue());
+        break;
+      }
+      case FieldDescriptor::CPPTYPE_BOOL: {
+        map_val->SetBoolValue(other_it->second.GetBoolValue());
+        break;
+      }
+      case FieldDescriptor::CPPTYPE_STRING: {
+        map_val->SetStringValue(other_it->second.GetStringValue());
+        break;
+      }
+      case FieldDescriptor::CPPTYPE_ENUM: {
+        map_val->SetEnumValue(other_it->second.GetEnumValue());
+        break;
+      }
+      case FieldDescriptor::CPPTYPE_MESSAGE: {
+        map_val->MutableMessageValue()->CopyFrom(
+            other_it->second.GetMessageValue());
+        break;
+      }
+    }
+  }
+}
+
+void DynamicMapField::Swap(MapFieldBase* other) {
+  DynamicMapField* other_field = down_cast<DynamicMapField*>(other);
+  std::swap(this->MapFieldBase::repeated_field_, other_field->repeated_field_);
+  map_.swap(other_field->map_);
+  // a relaxed swap of the atomic
+  auto other_state = other_field->state_.load(std::memory_order_relaxed);
+  auto this_state = this->MapFieldBase::state_.load(std::memory_order_relaxed);
+  other_field->state_.store(this_state, std::memory_order_relaxed);
+  this->MapFieldBase::state_.store(other_state, std::memory_order_relaxed);
+}
+
+void DynamicMapField::SyncRepeatedFieldWithMapNoLock() const {
+  const Reflection* reflection = default_entry_->GetReflection();
+  const FieldDescriptor* key_des = default_entry_->GetDescriptor()->map_key();
+  const FieldDescriptor* val_des = default_entry_->GetDescriptor()->map_value();
+  if (MapFieldBase::repeated_field_ == nullptr) {
+    MapFieldBase::repeated_field_ =
+        Arena::CreateMessage<RepeatedPtrField<Message> >(MapFieldBase::arena_);
+  }
+
+  MapFieldBase::repeated_field_->Clear();
+
+  for (Map<MapKey, MapValueRef>::const_iterator it = map_.begin();
+       it != map_.end(); ++it) {
+    Message* new_entry = default_entry_->New(MapFieldBase::arena_);
+    MapFieldBase::repeated_field_->AddAllocated(new_entry);
+    const MapKey& map_key = it->first;
+    switch (key_des->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_STRING:
+        reflection->SetString(new_entry, key_des, map_key.GetStringValue());
+        break;
+      case FieldDescriptor::CPPTYPE_INT64:
+        reflection->SetInt64(new_entry, key_des, map_key.GetInt64Value());
+        break;
+      case FieldDescriptor::CPPTYPE_INT32:
+        reflection->SetInt32(new_entry, key_des, map_key.GetInt32Value());
+        break;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        reflection->SetUInt64(new_entry, key_des, map_key.GetUInt64Value());
+        break;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        reflection->SetUInt32(new_entry, key_des, map_key.GetUInt32Value());
+        break;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        reflection->SetBool(new_entry, key_des, map_key.GetBoolValue());
+        break;
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+      case FieldDescriptor::CPPTYPE_FLOAT:
+      case FieldDescriptor::CPPTYPE_ENUM:
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        GOOGLE_LOG(FATAL) << "Can't get here.";
+        break;
+    }
+    const MapValueRef& map_val = it->second;
+    switch (val_des->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_STRING:
+        reflection->SetString(new_entry, val_des, map_val.GetStringValue());
+        break;
+      case FieldDescriptor::CPPTYPE_INT64:
+        reflection->SetInt64(new_entry, val_des, map_val.GetInt64Value());
+        break;
+      case FieldDescriptor::CPPTYPE_INT32:
+        reflection->SetInt32(new_entry, val_des, map_val.GetInt32Value());
+        break;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        reflection->SetUInt64(new_entry, val_des, map_val.GetUInt64Value());
+        break;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        reflection->SetUInt32(new_entry, val_des, map_val.GetUInt32Value());
+        break;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        reflection->SetBool(new_entry, val_des, map_val.GetBoolValue());
+        break;
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+        reflection->SetDouble(new_entry, val_des, map_val.GetDoubleValue());
+        break;
+      case FieldDescriptor::CPPTYPE_FLOAT:
+        reflection->SetFloat(new_entry, val_des, map_val.GetFloatValue());
+        break;
+      case FieldDescriptor::CPPTYPE_ENUM:
+        reflection->SetEnumValue(new_entry, val_des, map_val.GetEnumValue());
+        break;
+      case FieldDescriptor::CPPTYPE_MESSAGE: {
+        const Message& message = map_val.GetMessageValue();
+        reflection->MutableMessage(new_entry, val_des)->CopyFrom(message);
+        break;
+      }
+    }
+  }
+}
+
+void DynamicMapField::SyncMapWithRepeatedFieldNoLock() const {
+  Map<MapKey, MapValueRef>* map = &const_cast<DynamicMapField*>(this)->map_;
+  const Reflection* reflection = default_entry_->GetReflection();
+  const FieldDescriptor* key_des = default_entry_->GetDescriptor()->map_key();
+  const FieldDescriptor* val_des = default_entry_->GetDescriptor()->map_value();
+  // DynamicMapField owns map values. Need to delete them before clearing
+  // the map.
+  if (MapFieldBase::arena_ == nullptr) {
+    for (Map<MapKey, MapValueRef>::iterator iter = map->begin();
+         iter != map->end(); ++iter) {
+      iter->second.DeleteData();
+    }
+  }
+  map->clear();
+  for (RepeatedPtrField<Message>::iterator it =
+           MapFieldBase::repeated_field_->begin();
+       it != MapFieldBase::repeated_field_->end(); ++it) {
+    // MapKey type will be set later.
+    MapKey map_key;
+    switch (key_des->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_STRING:
+        map_key.SetStringValue(reflection->GetString(*it, key_des));
+        break;
+      case FieldDescriptor::CPPTYPE_INT64:
+        map_key.SetInt64Value(reflection->GetInt64(*it, key_des));
+        break;
+      case FieldDescriptor::CPPTYPE_INT32:
+        map_key.SetInt32Value(reflection->GetInt32(*it, key_des));
+        break;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        map_key.SetUInt64Value(reflection->GetUInt64(*it, key_des));
+        break;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        map_key.SetUInt32Value(reflection->GetUInt32(*it, key_des));
+        break;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        map_key.SetBoolValue(reflection->GetBool(*it, key_des));
+        break;
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+      case FieldDescriptor::CPPTYPE_FLOAT:
+      case FieldDescriptor::CPPTYPE_ENUM:
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        GOOGLE_LOG(FATAL) << "Can't get here.";
+        break;
+    }
+
+    if (MapFieldBase::arena_ == nullptr) {
+      // Remove existing map value with same key.
+      Map<MapKey, MapValueRef>::iterator iter = map->find(map_key);
+      if (iter != map->end()) {
+        iter->second.DeleteData();
+      }
+    }
+
+    MapValueRef& map_val = (*map)[map_key];
+    map_val.SetType(val_des->cpp_type());
+    switch (val_des->cpp_type()) {
+#define HANDLE_TYPE(CPPTYPE, TYPE, METHOD)                   \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE: {                 \
+    TYPE* value = Arena::Create<TYPE>(MapFieldBase::arena_); \
+    *value = reflection->Get##METHOD(*it, val_des);          \
+    map_val.SetValue(value);                                 \
+    break;                                                   \
+  }
+      HANDLE_TYPE(INT32, int32_t, Int32);
+      HANDLE_TYPE(INT64, int64_t, Int64);
+      HANDLE_TYPE(UINT32, uint32_t, UInt32);
+      HANDLE_TYPE(UINT64, uint64_t, UInt64);
+      HANDLE_TYPE(DOUBLE, double, Double);
+      HANDLE_TYPE(FLOAT, float, Float);
+      HANDLE_TYPE(BOOL, bool, Bool);
+      HANDLE_TYPE(STRING, std::string, String);
+      HANDLE_TYPE(ENUM, int32_t, EnumValue);
+#undef HANDLE_TYPE
+      case FieldDescriptor::CPPTYPE_MESSAGE: {
+        const Message& message = reflection->GetMessage(*it, val_des);
+        Message* value = message.New(MapFieldBase::arena_);
+        value->CopyFrom(message);
+        map_val.SetValue(value);
+        break;
+      }
+    }
+  }
+}
+
+size_t DynamicMapField::SpaceUsedExcludingSelfNoLock() const {
+  size_t size = 0;
+  if (MapFieldBase::repeated_field_ != nullptr) {
+    size += MapFieldBase::repeated_field_->SpaceUsedExcludingSelfLong();
+  }
+  size += sizeof(map_);
+  size_t map_size = map_.size();
+  if (map_size) {
+    Map<MapKey, MapValueRef>::const_iterator it = map_.begin();
+    size += sizeof(it->first) * map_size;
+    size += sizeof(it->second) * map_size;
+    // If key is string, add the allocated space.
+    if (it->first.type() == FieldDescriptor::CPPTYPE_STRING) {
+      size += sizeof(std::string) * map_size;
+    }
+    // Add the allocated space in MapValueRef.
+    switch (it->second.type()) {
+#define HANDLE_TYPE(CPPTYPE, TYPE)           \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE: { \
+    size += sizeof(TYPE) * map_size;         \
+    break;                                   \
+  }
+      HANDLE_TYPE(INT32, int32_t);
+      HANDLE_TYPE(INT64, int64_t);
+      HANDLE_TYPE(UINT32, uint32_t);
+      HANDLE_TYPE(UINT64, uint64_t);
+      HANDLE_TYPE(DOUBLE, double);
+      HANDLE_TYPE(FLOAT, float);
+      HANDLE_TYPE(BOOL, bool);
+      HANDLE_TYPE(STRING, std::string);
+      HANDLE_TYPE(ENUM, int32_t);
+#undef HANDLE_TYPE
+      case FieldDescriptor::CPPTYPE_MESSAGE: {
+        while (it != map_.end()) {
+          const Message& message = it->second.GetMessageValue();
+          size += message.GetReflection()->SpaceUsedLong(message);
+          ++it;
+        }
+        break;
+      }
+    }
+  }
+  return size;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/message.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/message.cpp
new file mode 100644
index 0000000..6cdc6c8
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/message.cpp
@@ -0,0 +1,404 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/message.h>
+
+#include <iostream>
+#include <stack>
+#include <unordered_map>
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/map_field_inl.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/reflection_internal.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/stl_util.h>
+#include <google/protobuf/stubs/hash.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+namespace internal {
+
+// TODO(gerbens) make this factorized better. This should not have to hop
+// to reflection. Currently uses GeneratedMessageReflection and thus is
+// defined in generated_message_reflection.cc
+void RegisterFileLevelMetadata(const DescriptorTable* descriptor_table);
+
+}  // namespace internal
+
+using internal::ReflectionOps;
+using internal::WireFormat;
+using internal::WireFormatLite;
+
+void Message::MergeFrom(const Message& from) {
+  auto* class_to = GetClassData();
+  auto* class_from = from.GetClassData();
+  auto* merge_to_from = class_to ? class_to->merge_to_from : nullptr;
+  if (class_to == nullptr || class_to != class_from) {
+    merge_to_from = [](Message& to, const Message& from) {
+      ReflectionOps::Merge(from, &to);
+    };
+  }
+  merge_to_from(*this, from);
+}
+
+void Message::CheckTypeAndMergeFrom(const MessageLite& other) {
+  MergeFrom(*down_cast<const Message*>(&other));
+}
+
+void Message::CopyFrom(const Message& from) {
+  if (&from == this) return;
+
+  auto* class_to = GetClassData();
+  auto* class_from = from.GetClassData();
+  auto* copy_to_from = class_to ? class_to->copy_to_from : nullptr;
+
+  if (class_to == nullptr || class_to != class_from) {
+    const Descriptor* descriptor = GetDescriptor();
+    GOOGLE_CHECK_EQ(from.GetDescriptor(), descriptor)
+        << ": Tried to copy from a message with a different type. "
+           "to: "
+        << descriptor->full_name()
+        << ", "
+           "from: "
+        << from.GetDescriptor()->full_name();
+    copy_to_from = [](Message& to, const Message& from) {
+      ReflectionOps::Copy(from, &to);
+    };
+  }
+  copy_to_from(*this, from);
+}
+
+void Message::CopyWithSourceCheck(Message& to, const Message& from) {
+#ifndef NDEBUG
+  FailIfCopyFromDescendant(to, from);
+#endif
+  to.Clear();
+  to.GetClassData()->merge_to_from(to, from);
+}
+
+void Message::FailIfCopyFromDescendant(Message& to, const Message& from) {
+  auto* arena = to.GetArenaForAllocation();
+  bool same_message_owned_arena = to.GetOwningArena() == nullptr &&
+                                  arena != nullptr &&
+                                  arena == from.GetOwningArena();
+  GOOGLE_CHECK(!same_message_owned_arena && !internal::IsDescendant(to, from))
+      << "Source of CopyFrom cannot be a descendant of the target.";
+}
+
+std::string Message::GetTypeName() const {
+  return GetDescriptor()->full_name();
+}
+
+void Message::Clear() { ReflectionOps::Clear(this); }
+
+bool Message::IsInitialized() const {
+  return ReflectionOps::IsInitialized(*this);
+}
+
+void Message::FindInitializationErrors(std::vector<std::string>* errors) const {
+  return ReflectionOps::FindInitializationErrors(*this, "", errors);
+}
+
+std::string Message::InitializationErrorString() const {
+  std::vector<std::string> errors;
+  FindInitializationErrors(&errors);
+  return Join(errors, ", ");
+}
+
+void Message::CheckInitialized() const {
+  GOOGLE_CHECK(IsInitialized()) << "Message of type \"" << GetDescriptor()->full_name()
+                         << "\" is missing required fields: "
+                         << InitializationErrorString();
+}
+
+void Message::DiscardUnknownFields() {
+  return ReflectionOps::DiscardUnknownFields(this);
+}
+
+const char* Message::_InternalParse(const char* ptr,
+                                    internal::ParseContext* ctx) {
+  return WireFormat::_InternalParse(this, ptr, ctx);
+}
+
+uint8_t* Message::_InternalSerialize(uint8_t* target,
+                                     io::EpsCopyOutputStream* stream) const {
+  return WireFormat::_InternalSerialize(*this, target, stream);
+}
+
+size_t Message::ByteSizeLong() const {
+  size_t size = WireFormat::ByteSize(*this);
+  SetCachedSize(internal::ToCachedSize(size));
+  return size;
+}
+
+void Message::SetCachedSize(int /* size */) const {
+  GOOGLE_LOG(FATAL) << "Message class \"" << GetDescriptor()->full_name()
+             << "\" implements neither SetCachedSize() nor ByteSize().  "
+                "Must implement one or the other.";
+}
+
+size_t Message::ComputeUnknownFieldsSize(
+    size_t total_size, internal::CachedSize* cached_size) const {
+  total_size += WireFormat::ComputeUnknownFieldsSize(
+      _internal_metadata_.unknown_fields<UnknownFieldSet>(
+          UnknownFieldSet::default_instance));
+  cached_size->Set(internal::ToCachedSize(total_size));
+  return total_size;
+}
+
+size_t Message::MaybeComputeUnknownFieldsSize(
+    size_t total_size, internal::CachedSize* cached_size) const {
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    return ComputeUnknownFieldsSize(total_size, cached_size);
+  }
+  cached_size->Set(internal::ToCachedSize(total_size));
+  return total_size;
+}
+
+size_t Message::SpaceUsedLong() const {
+  return GetReflection()->SpaceUsedLong(*this);
+}
+
+uint64_t Message::GetInvariantPerBuild(uint64_t salt) {
+  return salt;
+}
+
+// =============================================================================
+// MessageFactory
+
+MessageFactory::~MessageFactory() {}
+
+namespace {
+
+
+#define HASH_MAP std::unordered_map
+#define STR_HASH_FXN hash<::google::protobuf::StringPiece>
+
+
+class GeneratedMessageFactory final : public MessageFactory {
+ public:
+  static GeneratedMessageFactory* singleton();
+
+  void RegisterFile(const google::protobuf::internal::DescriptorTable* table);
+  void RegisterType(const Descriptor* descriptor, const Message* prototype);
+
+  // implements MessageFactory ---------------------------------------
+  const Message* GetPrototype(const Descriptor* type) override;
+
+ private:
+  // Only written at static init time, so does not require locking.
+  HASH_MAP<StringPiece, const google::protobuf::internal::DescriptorTable*,
+           STR_HASH_FXN>
+      file_map_;
+
+  internal::WrappedMutex mutex_;
+  // Initialized lazily, so requires locking.
+  std::unordered_map<const Descriptor*, const Message*> type_map_;
+};
+
+GeneratedMessageFactory* GeneratedMessageFactory::singleton() {
+  static auto instance =
+      internal::OnShutdownDelete(new GeneratedMessageFactory);
+  return instance;
+}
+
+void GeneratedMessageFactory::RegisterFile(
+    const google::protobuf::internal::DescriptorTable* table) {
+  if (!InsertIfNotPresent(&file_map_, table->filename, table)) {
+    GOOGLE_LOG(FATAL) << "File is already registered: " << table->filename;
+  }
+}
+
+void GeneratedMessageFactory::RegisterType(const Descriptor* descriptor,
+                                           const Message* prototype) {
+  GOOGLE_DCHECK_EQ(descriptor->file()->pool(), DescriptorPool::generated_pool())
+      << "Tried to register a non-generated type with the generated "
+         "type registry.";
+
+  // This should only be called as a result of calling a file registration
+  // function during GetPrototype(), in which case we already have locked
+  // the mutex.
+  mutex_.AssertHeld();
+  if (!InsertIfNotPresent(&type_map_, descriptor, prototype)) {
+    GOOGLE_LOG(DFATAL) << "Type is already registered: " << descriptor->full_name();
+  }
+}
+
+
+const Message* GeneratedMessageFactory::GetPrototype(const Descriptor* type) {
+  {
+    ReaderMutexLock lock(&mutex_);
+    const Message* result = FindPtrOrNull(type_map_, type);
+    if (result != nullptr) return result;
+  }
+
+  // If the type is not in the generated pool, then we can't possibly handle
+  // it.
+  if (type->file()->pool() != DescriptorPool::generated_pool()) return nullptr;
+
+  // Apparently the file hasn't been registered yet.  Let's do that now.
+  const internal::DescriptorTable* registration_data =
+      FindPtrOrNull(file_map_, type->file()->name().c_str());
+  if (registration_data == nullptr) {
+    GOOGLE_LOG(DFATAL) << "File appears to be in generated pool but wasn't "
+                   "registered: "
+                << type->file()->name();
+    return nullptr;
+  }
+
+  WriterMutexLock lock(&mutex_);
+
+  // Check if another thread preempted us.
+  const Message* result = FindPtrOrNull(type_map_, type);
+  if (result == nullptr) {
+    // Nope.  OK, register everything.
+    internal::RegisterFileLevelMetadata(registration_data);
+    // Should be here now.
+    result = FindPtrOrNull(type_map_, type);
+  }
+
+  if (result == nullptr) {
+    GOOGLE_LOG(DFATAL) << "Type appears to be in generated pool but wasn't "
+                << "registered: " << type->full_name();
+  }
+
+  return result;
+}
+
+}  // namespace
+
+MessageFactory* MessageFactory::generated_factory() {
+  return GeneratedMessageFactory::singleton();
+}
+
+void MessageFactory::InternalRegisterGeneratedFile(
+    const google::protobuf::internal::DescriptorTable* table) {
+  GeneratedMessageFactory::singleton()->RegisterFile(table);
+}
+
+void MessageFactory::InternalRegisterGeneratedMessage(
+    const Descriptor* descriptor, const Message* prototype) {
+  GeneratedMessageFactory::singleton()->RegisterType(descriptor, prototype);
+}
+
+
+namespace {
+template <typename T>
+T* GetSingleton() {
+  static T singleton;
+  return &singleton;
+}
+}  // namespace
+
+const internal::RepeatedFieldAccessor* Reflection::RepeatedFieldAccessor(
+    const FieldDescriptor* field) const {
+  GOOGLE_CHECK(field->is_repeated());
+  switch (field->cpp_type()) {
+#define HANDLE_PRIMITIVE_TYPE(TYPE, type) \
+  case FieldDescriptor::CPPTYPE_##TYPE:   \
+    return GetSingleton<internal::RepeatedFieldPrimitiveAccessor<type> >();
+    HANDLE_PRIMITIVE_TYPE(INT32, int32_t)
+    HANDLE_PRIMITIVE_TYPE(UINT32, uint32_t)
+    HANDLE_PRIMITIVE_TYPE(INT64, int64_t)
+    HANDLE_PRIMITIVE_TYPE(UINT64, uint64_t)
+    HANDLE_PRIMITIVE_TYPE(FLOAT, float)
+    HANDLE_PRIMITIVE_TYPE(DOUBLE, double)
+    HANDLE_PRIMITIVE_TYPE(BOOL, bool)
+    HANDLE_PRIMITIVE_TYPE(ENUM, int32_t)
+#undef HANDLE_PRIMITIVE_TYPE
+    case FieldDescriptor::CPPTYPE_STRING:
+      switch (field->options().ctype()) {
+        default:
+        case FieldOptions::STRING:
+          return GetSingleton<internal::RepeatedPtrFieldStringAccessor>();
+      }
+      break;
+    case FieldDescriptor::CPPTYPE_MESSAGE:
+      if (field->is_map()) {
+        return GetSingleton<internal::MapFieldAccessor>();
+      } else {
+        return GetSingleton<internal::RepeatedPtrFieldMessageAccessor>();
+      }
+  }
+  GOOGLE_LOG(FATAL) << "Should not reach here.";
+  return nullptr;
+}
+
+namespace internal {
+template <>
+#if defined(_MSC_VER) && (_MSC_VER >= 1800)
+// Note: force noinline to workaround MSVC compiler bug with /Zc:inline, issue
+// #240
+PROTOBUF_NOINLINE
+#endif
+    Message*
+    GenericTypeHandler<Message>::NewFromPrototype(const Message* prototype,
+                                                  Arena* arena) {
+  return prototype->New(arena);
+}
+template <>
+#if defined(_MSC_VER) && (_MSC_VER >= 1800)
+// Note: force noinline to workaround MSVC compiler bug with /Zc:inline, issue
+// #240
+PROTOBUF_NOINLINE
+#endif
+    Arena*
+    GenericTypeHandler<Message>::GetOwningArena(Message* value) {
+  return value->GetOwningArena();
+}
+}  // namespace internal
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/message_lite.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/message_lite.cpp
new file mode 100644
index 0000000..da66c19
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/message_lite.cpp
@@ -0,0 +1,602 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Authors: wink@google.com (Wink Saville),
+//          kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/message_lite.h>
+
+#include <climits>
+#include <cstdint>
+#include <string>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/stubs/stl_util.h>
+#include <google/protobuf/stubs/mutex.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+std::string MessageLite::InitializationErrorString() const {
+  return "(cannot determine missing fields for lite message)";
+}
+
+std::string MessageLite::DebugString() const {
+  std::uintptr_t address = reinterpret_cast<std::uintptr_t>(this);
+  return StrCat("MessageLite at 0x", strings::Hex(address));
+}
+
+namespace {
+
+// When serializing, we first compute the byte size, then serialize the message.
+// If serialization produces a different number of bytes than expected, we
+// call this function, which crashes.  The problem could be due to a bug in the
+// protobuf implementation but is more likely caused by concurrent modification
+// of the message.  This function attempts to distinguish between the two and
+// provide a useful error message.
+void ByteSizeConsistencyError(size_t byte_size_before_serialization,
+                              size_t byte_size_after_serialization,
+                              size_t bytes_produced_by_serialization,
+                              const MessageLite& message) {
+  GOOGLE_CHECK_EQ(byte_size_before_serialization, byte_size_after_serialization)
+      << message.GetTypeName()
+      << " was modified concurrently during serialization.";
+  GOOGLE_CHECK_EQ(bytes_produced_by_serialization, byte_size_before_serialization)
+      << "Byte size calculation and serialization were inconsistent.  This "
+         "may indicate a bug in protocol buffers or it may be caused by "
+         "concurrent modification of "
+      << message.GetTypeName() << ".";
+  GOOGLE_LOG(FATAL) << "This shouldn't be called if all the sizes are equal.";
+}
+
+std::string InitializationErrorMessage(const char* action,
+                                       const MessageLite& message) {
+  // Note:  We want to avoid depending on strutil in the lite library, otherwise
+  //   we'd use:
+  //
+  // return strings::Substitute(
+  //   "Can't $0 message of type \"$1\" because it is missing required "
+  //   "fields: $2",
+  //   action, message.GetTypeName(),
+  //   message.InitializationErrorString());
+
+  std::string result;
+  result += "Can't ";
+  result += action;
+  result += " message of type \"";
+  result += message.GetTypeName();
+  result += "\" because it is missing required fields: ";
+  result += message.InitializationErrorString();
+  return result;
+}
+
+inline StringPiece as_string_view(const void* data, int size) {
+  return StringPiece(static_cast<const char*>(data), size);
+}
+
+// Returns true of all required fields are present / have values.
+inline bool CheckFieldPresence(const internal::ParseContext& ctx,
+                               const MessageLite& msg,
+                               MessageLite::ParseFlags parse_flags) {
+  (void)ctx;  // Parameter is used by Google-internal code.
+  if (PROTOBUF_PREDICT_FALSE((parse_flags & MessageLite::kMergePartial) != 0)) {
+    return true;
+  }
+  return msg.IsInitializedWithErrors();
+}
+
+}  // namespace
+
+void MessageLite::LogInitializationErrorMessage() const {
+  GOOGLE_LOG(ERROR) << InitializationErrorMessage("parse", *this);
+}
+
+namespace internal {
+
+template <bool aliasing>
+bool MergeFromImpl(StringPiece input, MessageLite* msg,
+                   MessageLite::ParseFlags parse_flags) {
+  const char* ptr;
+  internal::ParseContext ctx(io::CodedInputStream::GetDefaultRecursionLimit(),
+                             aliasing, &ptr, input);
+  ptr = msg->_InternalParse(ptr, &ctx);
+  // ctx has an explicit limit set (length of string_view).
+  if (PROTOBUF_PREDICT_TRUE(ptr && ctx.EndedAtLimit())) {
+    return CheckFieldPresence(ctx, *msg, parse_flags);
+  }
+  return false;
+}
+
+template <bool aliasing>
+bool MergeFromImpl(io::ZeroCopyInputStream* input, MessageLite* msg,
+                   MessageLite::ParseFlags parse_flags) {
+  const char* ptr;
+  internal::ParseContext ctx(io::CodedInputStream::GetDefaultRecursionLimit(),
+                             aliasing, &ptr, input);
+  ptr = msg->_InternalParse(ptr, &ctx);
+  // ctx has no explicit limit (hence we end on end of stream)
+  if (PROTOBUF_PREDICT_TRUE(ptr && ctx.EndedAtEndOfStream())) {
+    return CheckFieldPresence(ctx, *msg, parse_flags);
+  }
+  return false;
+}
+
+template <bool aliasing>
+bool MergeFromImpl(BoundedZCIS input, MessageLite* msg,
+                   MessageLite::ParseFlags parse_flags) {
+  const char* ptr;
+  internal::ParseContext ctx(io::CodedInputStream::GetDefaultRecursionLimit(),
+                             aliasing, &ptr, input.zcis, input.limit);
+  ptr = msg->_InternalParse(ptr, &ctx);
+  if (PROTOBUF_PREDICT_FALSE(!ptr)) return false;
+  ctx.BackUp(ptr);
+  if (PROTOBUF_PREDICT_TRUE(ctx.EndedAtLimit())) {
+    return CheckFieldPresence(ctx, *msg, parse_flags);
+  }
+  return false;
+}
+
+template bool MergeFromImpl<false>(StringPiece input, MessageLite* msg,
+                                   MessageLite::ParseFlags parse_flags);
+template bool MergeFromImpl<true>(StringPiece input, MessageLite* msg,
+                                  MessageLite::ParseFlags parse_flags);
+template bool MergeFromImpl<false>(io::ZeroCopyInputStream* input,
+                                   MessageLite* msg,
+                                   MessageLite::ParseFlags parse_flags);
+template bool MergeFromImpl<true>(io::ZeroCopyInputStream* input,
+                                  MessageLite* msg,
+                                  MessageLite::ParseFlags parse_flags);
+template bool MergeFromImpl<false>(BoundedZCIS input, MessageLite* msg,
+                                   MessageLite::ParseFlags parse_flags);
+template bool MergeFromImpl<true>(BoundedZCIS input, MessageLite* msg,
+                                  MessageLite::ParseFlags parse_flags);
+
+}  // namespace internal
+
+class ZeroCopyCodedInputStream : public io::ZeroCopyInputStream {
+ public:
+  ZeroCopyCodedInputStream(io::CodedInputStream* cis) : cis_(cis) {}
+  bool Next(const void** data, int* size) final {
+    if (!cis_->GetDirectBufferPointer(data, size)) return false;
+    cis_->Skip(*size);
+    return true;
+  }
+  void BackUp(int count) final { cis_->Advance(-count); }
+  bool Skip(int count) final { return cis_->Skip(count); }
+  int64_t ByteCount() const final { return 0; }
+
+  bool aliasing_enabled() { return cis_->aliasing_enabled_; }
+
+ private:
+  io::CodedInputStream* cis_;
+};
+
+bool MessageLite::MergeFromImpl(io::CodedInputStream* input,
+                                MessageLite::ParseFlags parse_flags) {
+  ZeroCopyCodedInputStream zcis(input);
+  const char* ptr;
+  internal::ParseContext ctx(input->RecursionBudget(), zcis.aliasing_enabled(),
+                             &ptr, &zcis);
+  // MergePartialFromCodedStream allows terminating the wireformat by 0 or
+  // end-group tag. Leaving it up to the caller to verify correct ending by
+  // calling LastTagWas on input. We need to maintain this behavior.
+  ctx.TrackCorrectEnding();
+  ctx.data().pool = input->GetExtensionPool();
+  ctx.data().factory = input->GetExtensionFactory();
+  ptr = _InternalParse(ptr, &ctx);
+  if (PROTOBUF_PREDICT_FALSE(!ptr)) return false;
+  ctx.BackUp(ptr);
+  if (!ctx.EndedAtEndOfStream()) {
+    GOOGLE_DCHECK_NE(ctx.LastTag(), 1);  // We can't end on a pushed limit.
+    if (ctx.IsExceedingLimit(ptr)) return false;
+    input->SetLastTag(ctx.LastTag());
+  } else {
+    input->SetConsumed();
+  }
+  return CheckFieldPresence(ctx, *this, parse_flags);
+}
+
+bool MessageLite::MergePartialFromCodedStream(io::CodedInputStream* input) {
+  return MergeFromImpl(input, kMergePartial);
+}
+
+bool MessageLite::MergeFromCodedStream(io::CodedInputStream* input) {
+  return MergeFromImpl(input, kMerge);
+}
+
+bool MessageLite::ParseFromCodedStream(io::CodedInputStream* input) {
+  Clear();
+  return MergeFromImpl(input, kParse);
+}
+
+bool MessageLite::ParsePartialFromCodedStream(io::CodedInputStream* input) {
+  Clear();
+  return MergeFromImpl(input, kParsePartial);
+}
+
+bool MessageLite::ParseFromZeroCopyStream(io::ZeroCopyInputStream* input) {
+  return ParseFrom<kParse>(input);
+}
+
+bool MessageLite::ParsePartialFromZeroCopyStream(
+    io::ZeroCopyInputStream* input) {
+  return ParseFrom<kParsePartial>(input);
+}
+
+bool MessageLite::ParseFromFileDescriptor(int file_descriptor) {
+  io::FileInputStream input(file_descriptor);
+  return ParseFromZeroCopyStream(&input) && input.GetErrno() == 0;
+}
+
+bool MessageLite::ParsePartialFromFileDescriptor(int file_descriptor) {
+  io::FileInputStream input(file_descriptor);
+  return ParsePartialFromZeroCopyStream(&input) && input.GetErrno() == 0;
+}
+
+bool MessageLite::ParseFromIstream(std::istream* input) {
+  io::IstreamInputStream zero_copy_input(input);
+  return ParseFromZeroCopyStream(&zero_copy_input) && input->eof();
+}
+
+bool MessageLite::ParsePartialFromIstream(std::istream* input) {
+  io::IstreamInputStream zero_copy_input(input);
+  return ParsePartialFromZeroCopyStream(&zero_copy_input) && input->eof();
+}
+
+bool MessageLite::MergePartialFromBoundedZeroCopyStream(
+    io::ZeroCopyInputStream* input, int size) {
+  return ParseFrom<kMergePartial>(internal::BoundedZCIS{input, size});
+}
+
+bool MessageLite::MergeFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input,
+                                                 int size) {
+  return ParseFrom<kMerge>(internal::BoundedZCIS{input, size});
+}
+
+bool MessageLite::ParseFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input,
+                                                 int size) {
+  return ParseFrom<kParse>(internal::BoundedZCIS{input, size});
+}
+
+bool MessageLite::ParsePartialFromBoundedZeroCopyStream(
+    io::ZeroCopyInputStream* input, int size) {
+  return ParseFrom<kParsePartial>(internal::BoundedZCIS{input, size});
+}
+
+bool MessageLite::ParseFromString(ConstStringParam data) {
+  return ParseFrom<kParse>(data);
+}
+
+bool MessageLite::ParsePartialFromString(ConstStringParam data) {
+  return ParseFrom<kParsePartial>(data);
+}
+
+bool MessageLite::ParseFromArray(const void* data, int size) {
+  return ParseFrom<kParse>(as_string_view(data, size));
+}
+
+bool MessageLite::ParsePartialFromArray(const void* data, int size) {
+  return ParseFrom<kParsePartial>(as_string_view(data, size));
+}
+
+bool MessageLite::MergeFromString(ConstStringParam data) {
+  return ParseFrom<kMerge>(data);
+}
+
+
+// ===================================================================
+
+inline uint8_t* SerializeToArrayImpl(const MessageLite& msg, uint8_t* target,
+                                     int size) {
+  constexpr bool debug = false;
+  if (debug) {
+    // Force serialization to a stream with a block size of 1, which forces
+    // all writes to the stream to cross buffers triggering all fallback paths
+    // in the unittests when serializing to string / array.
+    io::ArrayOutputStream stream(target, size, 1);
+    uint8_t* ptr;
+    io::EpsCopyOutputStream out(
+        &stream, io::CodedOutputStream::IsDefaultSerializationDeterministic(),
+        &ptr);
+    ptr = msg._InternalSerialize(ptr, &out);
+    out.Trim(ptr);
+    GOOGLE_DCHECK(!out.HadError() && stream.ByteCount() == size);
+    return target + size;
+  } else {
+    io::EpsCopyOutputStream out(
+        target, size,
+        io::CodedOutputStream::IsDefaultSerializationDeterministic());
+    auto res = msg._InternalSerialize(target, &out);
+    GOOGLE_DCHECK(target + size == res);
+    return res;
+  }
+}
+
+uint8_t* MessageLite::SerializeWithCachedSizesToArray(uint8_t* target) const {
+  // We only optimize this when using optimize_for = SPEED.  In other cases
+  // we just use the CodedOutputStream path.
+  return SerializeToArrayImpl(*this, target, GetCachedSize());
+}
+
+bool MessageLite::SerializeToCodedStream(io::CodedOutputStream* output) const {
+  GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this);
+  return SerializePartialToCodedStream(output);
+}
+
+bool MessageLite::SerializePartialToCodedStream(
+    io::CodedOutputStream* output) const {
+  const size_t size = ByteSizeLong();  // Force size to be cached.
+  if (size > INT_MAX) {
+    GOOGLE_LOG(ERROR) << GetTypeName()
+               << " exceeded maximum protobuf size of 2GB: " << size;
+    return false;
+  }
+
+  int original_byte_count = output->ByteCount();
+  SerializeWithCachedSizes(output);
+  if (output->HadError()) {
+    return false;
+  }
+  int final_byte_count = output->ByteCount();
+
+  if (final_byte_count - original_byte_count != static_cast<int64_t>(size)) {
+    ByteSizeConsistencyError(size, ByteSizeLong(),
+                             final_byte_count - original_byte_count, *this);
+  }
+
+  return true;
+}
+
+bool MessageLite::SerializeToZeroCopyStream(
+    io::ZeroCopyOutputStream* output) const {
+  GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this);
+  return SerializePartialToZeroCopyStream(output);
+}
+
+bool MessageLite::SerializePartialToZeroCopyStream(
+    io::ZeroCopyOutputStream* output) const {
+  const size_t size = ByteSizeLong();  // Force size to be cached.
+  if (size > INT_MAX) {
+    GOOGLE_LOG(ERROR) << GetTypeName()
+               << " exceeded maximum protobuf size of 2GB: " << size;
+    return false;
+  }
+
+  uint8_t* target;
+  io::EpsCopyOutputStream stream(
+      output, io::CodedOutputStream::IsDefaultSerializationDeterministic(),
+      &target);
+  target = _InternalSerialize(target, &stream);
+  stream.Trim(target);
+  if (stream.HadError()) return false;
+  return true;
+}
+
+bool MessageLite::SerializeToFileDescriptor(int file_descriptor) const {
+  io::FileOutputStream output(file_descriptor);
+  return SerializeToZeroCopyStream(&output) && output.Flush();
+}
+
+bool MessageLite::SerializePartialToFileDescriptor(int file_descriptor) const {
+  io::FileOutputStream output(file_descriptor);
+  return SerializePartialToZeroCopyStream(&output) && output.Flush();
+}
+
+bool MessageLite::SerializeToOstream(std::ostream* output) const {
+  {
+    io::OstreamOutputStream zero_copy_output(output);
+    if (!SerializeToZeroCopyStream(&zero_copy_output)) return false;
+  }
+  return output->good();
+}
+
+bool MessageLite::SerializePartialToOstream(std::ostream* output) const {
+  io::OstreamOutputStream zero_copy_output(output);
+  return SerializePartialToZeroCopyStream(&zero_copy_output);
+}
+
+bool MessageLite::AppendToString(std::string* output) const {
+  GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this);
+  return AppendPartialToString(output);
+}
+
+bool MessageLite::AppendPartialToString(std::string* output) const {
+  size_t old_size = output->size();
+  size_t byte_size = ByteSizeLong();
+  if (byte_size > INT_MAX) {
+    GOOGLE_LOG(ERROR) << GetTypeName()
+               << " exceeded maximum protobuf size of 2GB: " << byte_size;
+    return false;
+  }
+
+  STLStringResizeUninitializedAmortized(output, old_size + byte_size);
+  uint8_t* start =
+      reinterpret_cast<uint8_t*>(io::mutable_string_data(output) + old_size);
+  SerializeToArrayImpl(*this, start, byte_size);
+  return true;
+}
+
+bool MessageLite::SerializeToString(std::string* output) const {
+  output->clear();
+  return AppendToString(output);
+}
+
+bool MessageLite::SerializePartialToString(std::string* output) const {
+  output->clear();
+  return AppendPartialToString(output);
+}
+
+bool MessageLite::SerializeToArray(void* data, int size) const {
+  GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this);
+  return SerializePartialToArray(data, size);
+}
+
+bool MessageLite::SerializePartialToArray(void* data, int size) const {
+  const size_t byte_size = ByteSizeLong();
+  if (byte_size > INT_MAX) {
+    GOOGLE_LOG(ERROR) << GetTypeName()
+               << " exceeded maximum protobuf size of 2GB: " << byte_size;
+    return false;
+  }
+  if (size < static_cast<int64_t>(byte_size)) return false;
+  uint8_t* start = reinterpret_cast<uint8_t*>(data);
+  SerializeToArrayImpl(*this, start, byte_size);
+  return true;
+}
+
+std::string MessageLite::SerializeAsString() const {
+  // If the compiler implements the (Named) Return Value Optimization,
+  // the local variable 'output' will not actually reside on the stack
+  // of this function, but will be overlaid with the object that the
+  // caller supplied for the return value to be constructed in.
+  std::string output;
+  if (!AppendToString(&output)) output.clear();
+  return output;
+}
+
+std::string MessageLite::SerializePartialAsString() const {
+  std::string output;
+  if (!AppendPartialToString(&output)) output.clear();
+  return output;
+}
+
+
+namespace internal {
+
+MessageLite* NewFromPrototypeHelper(const MessageLite* prototype,
+                                    Arena* arena) {
+  return prototype->New(arena);
+}
+template <>
+void GenericTypeHandler<MessageLite>::Merge(const MessageLite& from,
+                                            MessageLite* to) {
+  to->CheckTypeAndMergeFrom(from);
+}
+template <>
+void GenericTypeHandler<std::string>::Merge(const std::string& from,
+                                            std::string* to) {
+  *to = from;
+}
+
+// Non-inline implementations of InternalMetadata destructor
+// This is moved out of the header because the GOOGLE_DCHECK produces a lot of code.
+void InternalMetadata::CheckedDestruct() {
+  if (HasMessageOwnedArenaTag()) {
+    GOOGLE_DCHECK(!HasUnknownFieldsTag());
+    delete reinterpret_cast<Arena*>(ptr_ - kMessageOwnedArenaTagMask);
+  }
+}
+
+// Non-inline variants of std::string specializations for
+// various InternalMetadata routines.
+template <>
+void InternalMetadata::DoClear<std::string>() {
+  mutable_unknown_fields<std::string>()->clear();
+}
+
+template <>
+void InternalMetadata::DoMergeFrom<std::string>(const std::string& other) {
+  mutable_unknown_fields<std::string>()->append(other);
+}
+
+template <>
+void InternalMetadata::DoSwap<std::string>(std::string* other) {
+  mutable_unknown_fields<std::string>()->swap(*other);
+}
+
+}  // namespace internal
+
+
+// ===================================================================
+// Shutdown support.
+
+namespace internal {
+
+struct ShutdownData {
+  ~ShutdownData() {
+    std::reverse(functions.begin(), functions.end());
+    for (auto pair : functions) pair.first(pair.second);
+  }
+
+  static ShutdownData* get() {
+    static auto* data = new ShutdownData;
+    return data;
+  }
+
+  std::vector<std::pair<void (*)(const void*), const void*>> functions;
+  Mutex mutex;
+};
+
+static void RunZeroArgFunc(const void* arg) {
+  void (*func)() = reinterpret_cast<void (*)()>(const_cast<void*>(arg));
+  func();
+}
+
+void OnShutdown(void (*func)()) {
+  OnShutdownRun(RunZeroArgFunc, reinterpret_cast<void*>(func));
+}
+
+void OnShutdownRun(void (*f)(const void*), const void* arg) {
+  auto shutdown_data = ShutdownData::get();
+  MutexLock lock(&shutdown_data->mutex);
+  shutdown_data->functions.push_back(std::make_pair(f, arg));
+}
+
+}  // namespace internal
+
+void ShutdownProtobufLibrary() {
+  // This function should be called only once, but accepts multiple calls.
+  static bool is_shutdown = false;
+  if (!is_shutdown) {
+    delete internal::ShutdownData::get();
+    is_shutdown = true;
+  }
+}
+
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/parse_context.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/parse_context.cpp
new file mode 100644
index 0000000..59852fd
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/parse_context.cpp
@@ -0,0 +1,548 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/parse_context.h>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/endian.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/wire_format_lite.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+namespace {
+
+// Only call if at start of tag.
+bool ParseEndsInSlopRegion(const char* begin, int overrun, int depth) {
+  constexpr int kSlopBytes = EpsCopyInputStream::kSlopBytes;
+  GOOGLE_DCHECK_GE(overrun, 0);
+  GOOGLE_DCHECK_LE(overrun, kSlopBytes);
+  auto ptr = begin + overrun;
+  auto end = begin + kSlopBytes;
+  while (ptr < end) {
+    uint32_t tag;
+    ptr = ReadTag(ptr, &tag);
+    if (ptr == nullptr || ptr > end) return false;
+    // ending on 0 tag is allowed and is the major reason for the necessity of
+    // this function.
+    if (tag == 0) return true;
+    switch (tag & 7) {
+      case 0: {  // Varint
+        uint64_t val;
+        ptr = VarintParse(ptr, &val);
+        if (ptr == nullptr) return false;
+        break;
+      }
+      case 1: {  // fixed64
+        ptr += 8;
+        break;
+      }
+      case 2: {  // len delim
+        int32_t size = ReadSize(&ptr);
+        if (ptr == nullptr || size > end - ptr) return false;
+        ptr += size;
+        break;
+      }
+      case 3: {  // start group
+        depth++;
+        break;
+      }
+      case 4: {                    // end group
+        if (--depth < 0) return true;  // We exit early
+        break;
+      }
+      case 5: {  // fixed32
+        ptr += 4;
+        break;
+      }
+      default:
+        return false;  // Unknown wireformat
+    }
+  }
+  return false;
+}
+
+}  // namespace
+
+const char* EpsCopyInputStream::NextBuffer(int overrun, int depth) {
+  if (next_chunk_ == nullptr) return nullptr;  // We've reached end of stream.
+  if (next_chunk_ != buffer_) {
+    GOOGLE_DCHECK(size_ > kSlopBytes);
+    // The chunk is large enough to be used directly
+    buffer_end_ = next_chunk_ + size_ - kSlopBytes;
+    auto res = next_chunk_;
+    next_chunk_ = buffer_;
+    if (aliasing_ == kOnPatch) aliasing_ = kNoDelta;
+    return res;
+  }
+  // Move the slop bytes of previous buffer to start of the patch buffer.
+  // Note we must use memmove because the previous buffer could be part of
+  // buffer_.
+  std::memmove(buffer_, buffer_end_, kSlopBytes);
+  if (overall_limit_ > 0 &&
+      (depth < 0 || !ParseEndsInSlopRegion(buffer_, overrun, depth))) {
+    const void* data;
+    // ZeroCopyInputStream indicates Next may return 0 size buffers. Hence
+    // we loop.
+    while (StreamNext(&data)) {
+      if (size_ > kSlopBytes) {
+        // We got a large chunk
+        std::memcpy(buffer_ + kSlopBytes, data, kSlopBytes);
+        next_chunk_ = static_cast<const char*>(data);
+        buffer_end_ = buffer_ + kSlopBytes;
+        if (aliasing_ >= kNoDelta) aliasing_ = kOnPatch;
+        return buffer_;
+      } else if (size_ > 0) {
+        std::memcpy(buffer_ + kSlopBytes, data, size_);
+        next_chunk_ = buffer_;
+        buffer_end_ = buffer_ + size_;
+        if (aliasing_ >= kNoDelta) aliasing_ = kOnPatch;
+        return buffer_;
+      }
+      GOOGLE_DCHECK(size_ == 0) << size_;
+    }
+    overall_limit_ = 0;  // Next failed, no more needs for next
+  }
+  // End of stream or array
+  if (aliasing_ == kNoDelta) {
+    // If there is no more block and aliasing is true, the previous block
+    // is still valid and we can alias. We have users relying on string_view's
+    // obtained from protos to outlive the proto, when the parse was from an
+    // array. This guarantees string_view's are always aliased if parsed from
+    // an array.
+    aliasing_ = reinterpret_cast<std::uintptr_t>(buffer_end_) -
+                reinterpret_cast<std::uintptr_t>(buffer_);
+  }
+  next_chunk_ = nullptr;
+  buffer_end_ = buffer_ + kSlopBytes;
+  size_ = 0;
+  return buffer_;
+}
+
+const char* EpsCopyInputStream::Next() {
+  GOOGLE_DCHECK(limit_ > kSlopBytes);
+  auto p = NextBuffer(0 /* immaterial */, -1);
+  if (p == nullptr) {
+    limit_end_ = buffer_end_;
+    // Distinguish ending on a pushed limit or ending on end-of-stream.
+    SetEndOfStream();
+    return nullptr;
+  }
+  limit_ -= buffer_end_ - p;  // Adjust limit_ relative to new anchor
+  limit_end_ = buffer_end_ + std::min(0, limit_);
+  return p;
+}
+
+std::pair<const char*, bool> EpsCopyInputStream::DoneFallback(int overrun,
+                                                              int depth) {
+  // Did we exceeded the limit (parse error).
+  if (PROTOBUF_PREDICT_FALSE(overrun > limit_)) return {nullptr, true};
+  GOOGLE_DCHECK(overrun != limit_);  // Guaranteed by caller.
+  GOOGLE_DCHECK(overrun < limit_);   // Follows from above
+  // TODO(gerbens) Instead of this dcheck we could just assign, and remove
+  // updating the limit_end from PopLimit, ie.
+  // limit_end_ = buffer_end_ + (std::min)(0, limit_);
+  // if (ptr < limit_end_) return {ptr, false};
+  GOOGLE_DCHECK(limit_end_ == buffer_end_ + (std::min)(0, limit_));
+  // At this point we know the following assertion holds.
+  GOOGLE_DCHECK_GT(limit_, 0);
+  GOOGLE_DCHECK(limit_end_ == buffer_end_);  // because limit_ > 0
+  const char* p;
+  do {
+    // We are past the end of buffer_end_, in the slop region.
+    GOOGLE_DCHECK_GE(overrun, 0);
+    p = NextBuffer(overrun, depth);
+    if (p == nullptr) {
+      // We are at the end of the stream
+      if (PROTOBUF_PREDICT_FALSE(overrun != 0)) return {nullptr, true};
+      GOOGLE_DCHECK_GT(limit_, 0);
+      limit_end_ = buffer_end_;
+      // Distinguish ending on a pushed limit or ending on end-of-stream.
+      SetEndOfStream();
+      return {buffer_end_, true};
+    }
+    limit_ -= buffer_end_ - p;  // Adjust limit_ relative to new anchor
+    p += overrun;
+    overrun = p - buffer_end_;
+  } while (overrun >= 0);
+  limit_end_ = buffer_end_ + std::min(0, limit_);
+  return {p, false};
+}
+
+const char* EpsCopyInputStream::SkipFallback(const char* ptr, int size) {
+  return AppendSize(ptr, size, [](const char* /*p*/, int /*s*/) {});
+}
+
+const char* EpsCopyInputStream::ReadStringFallback(const char* ptr, int size,
+                                                   std::string* str) {
+  str->clear();
+  if (PROTOBUF_PREDICT_TRUE(size <= buffer_end_ - ptr + limit_)) {
+    // Reserve the string up to a static safe size. If strings are bigger than
+    // this we proceed by growing the string as needed. This protects against
+    // malicious payloads making protobuf hold on to a lot of memory.
+    str->reserve(str->size() + std::min<int>(size, kSafeStringSize));
+  }
+  return AppendSize(ptr, size,
+                    [str](const char* p, int s) { str->append(p, s); });
+}
+
+const char* EpsCopyInputStream::AppendStringFallback(const char* ptr, int size,
+                                                     std::string* str) {
+  if (PROTOBUF_PREDICT_TRUE(size <= buffer_end_ - ptr + limit_)) {
+    // Reserve the string up to a static safe size. If strings are bigger than
+    // this we proceed by growing the string as needed. This protects against
+    // malicious payloads making protobuf hold on to a lot of memory.
+    str->reserve(str->size() + std::min<int>(size, kSafeStringSize));
+  }
+  return AppendSize(ptr, size,
+                    [str](const char* p, int s) { str->append(p, s); });
+}
+
+
+const char* EpsCopyInputStream::InitFrom(io::ZeroCopyInputStream* zcis) {
+  zcis_ = zcis;
+  const void* data;
+  int size;
+  limit_ = INT_MAX;
+  if (zcis->Next(&data, &size)) {
+    overall_limit_ -= size;
+    if (size > kSlopBytes) {
+      auto ptr = static_cast<const char*>(data);
+      limit_ -= size - kSlopBytes;
+      limit_end_ = buffer_end_ = ptr + size - kSlopBytes;
+      next_chunk_ = buffer_;
+      if (aliasing_ == kOnPatch) aliasing_ = kNoDelta;
+      return ptr;
+    } else {
+      limit_end_ = buffer_end_ = buffer_ + kSlopBytes;
+      next_chunk_ = buffer_;
+      auto ptr = buffer_ + 2 * kSlopBytes - size;
+      std::memcpy(ptr, data, size);
+      return ptr;
+    }
+  }
+  overall_limit_ = 0;
+  next_chunk_ = nullptr;
+  size_ = 0;
+  limit_end_ = buffer_end_ = buffer_;
+  return buffer_;
+}
+
+const char* ParseContext::ReadSizeAndPushLimitAndDepth(const char* ptr,
+                                                       int* old_limit) {
+  int size = ReadSize(&ptr);
+  if (PROTOBUF_PREDICT_FALSE(!ptr)) {
+    *old_limit = 0;  // Make sure this isn't uninitialized even on error return
+    return nullptr;
+  }
+  *old_limit = PushLimit(ptr, size);
+  if (--depth_ < 0) return nullptr;
+  return ptr;
+}
+
+const char* ParseContext::ParseMessage(MessageLite* msg, const char* ptr) {
+  int old;
+  ptr = ReadSizeAndPushLimitAndDepth(ptr, &old);
+  ptr = ptr ? msg->_InternalParse(ptr, this) : nullptr;
+  depth_++;
+  if (!PopLimit(old)) return nullptr;
+  return ptr;
+}
+
+inline void WriteVarint(uint64_t val, std::string* s) {
+  while (val >= 128) {
+    uint8_t c = val | 0x80;
+    s->push_back(c);
+    val >>= 7;
+  }
+  s->push_back(val);
+}
+
+void WriteVarint(uint32_t num, uint64_t val, std::string* s) {
+  WriteVarint(num << 3, s);
+  WriteVarint(val, s);
+}
+
+void WriteLengthDelimited(uint32_t num, StringPiece val, std::string* s) {
+  WriteVarint((num << 3) + 2, s);
+  WriteVarint(val.size(), s);
+  s->append(val.data(), val.size());
+}
+
+std::pair<const char*, uint32_t> VarintParseSlow32(const char* p,
+                                                   uint32_t res) {
+  for (std::uint32_t i = 2; i < 5; i++) {
+    uint32_t byte = static_cast<uint8_t>(p[i]);
+    res += (byte - 1) << (7 * i);
+    if (PROTOBUF_PREDICT_TRUE(byte < 128)) {
+      return {p + i + 1, res};
+    }
+  }
+  // Accept >5 bytes
+  for (std::uint32_t i = 5; i < 10; i++) {
+    uint32_t byte = static_cast<uint8_t>(p[i]);
+    if (PROTOBUF_PREDICT_TRUE(byte < 128)) {
+      return {p + i + 1, res};
+    }
+  }
+  return {nullptr, 0};
+}
+
+std::pair<const char*, uint64_t> VarintParseSlow64(const char* p,
+                                                   uint32_t res32) {
+  uint64_t res = res32;
+  for (std::uint32_t i = 2; i < 10; i++) {
+    uint64_t byte = static_cast<uint8_t>(p[i]);
+    res += (byte - 1) << (7 * i);
+    if (PROTOBUF_PREDICT_TRUE(byte < 128)) {
+      return {p + i + 1, res};
+    }
+  }
+  return {nullptr, 0};
+}
+
+std::pair<const char*, uint32_t> ReadTagFallback(const char* p, uint32_t res) {
+  for (std::uint32_t i = 2; i < 5; i++) {
+    uint32_t byte = static_cast<uint8_t>(p[i]);
+    res += (byte - 1) << (7 * i);
+    if (PROTOBUF_PREDICT_TRUE(byte < 128)) {
+      return {p + i + 1, res};
+    }
+  }
+  return {nullptr, 0};
+}
+
+std::pair<const char*, int32_t> ReadSizeFallback(const char* p, uint32_t res) {
+  for (std::uint32_t i = 1; i < 4; i++) {
+    uint32_t byte = static_cast<uint8_t>(p[i]);
+    res += (byte - 1) << (7 * i);
+    if (PROTOBUF_PREDICT_TRUE(byte < 128)) {
+      return {p + i + 1, res};
+    }
+  }
+  std::uint32_t byte = static_cast<uint8_t>(p[4]);
+  if (PROTOBUF_PREDICT_FALSE(byte >= 8)) return {nullptr, 0};  // size >= 2gb
+  res += (byte - 1) << 28;
+  // Protect against sign integer overflow in PushLimit. Limits are relative
+  // to buffer ends and ptr could potential be kSlopBytes beyond a buffer end.
+  // To protect against overflow we reject limits absurdly close to INT_MAX.
+  if (PROTOBUF_PREDICT_FALSE(res > INT_MAX - ParseContext::kSlopBytes)) {
+    return {nullptr, 0};
+  }
+  return {p + 5, res};
+}
+
+const char* StringParser(const char* begin, const char* end, void* object,
+                         ParseContext*) {
+  auto str = static_cast<std::string*>(object);
+  str->append(begin, end - begin);
+  return end;
+}
+
+// Defined in wire_format_lite.cc
+void PrintUTF8ErrorLog(StringPiece message_name,
+                       StringPiece field_name, const char* operation_str,
+                       bool emit_stacktrace);
+
+bool VerifyUTF8(StringPiece str, const char* field_name) {
+  if (!IsStructurallyValidUTF8(str)) {
+    PrintUTF8ErrorLog("", field_name, "parsing", false);
+    return false;
+  }
+  return true;
+}
+
+const char* InlineGreedyStringParser(std::string* s, const char* ptr,
+                                     ParseContext* ctx) {
+  int size = ReadSize(&ptr);
+  if (!ptr) return nullptr;
+  return ctx->ReadString(ptr, size, s);
+}
+
+
+template <typename T, bool sign>
+const char* VarintParser(void* object, const char* ptr, ParseContext* ctx) {
+  return ctx->ReadPackedVarint(ptr, [object](uint64_t varint) {
+    T val;
+    if (sign) {
+      if (sizeof(T) == 8) {
+        val = WireFormatLite::ZigZagDecode64(varint);
+      } else {
+        val = WireFormatLite::ZigZagDecode32(varint);
+      }
+    } else {
+      val = varint;
+    }
+    static_cast<RepeatedField<T>*>(object)->Add(val);
+  });
+}
+
+const char* PackedInt32Parser(void* object, const char* ptr,
+                              ParseContext* ctx) {
+  return VarintParser<int32_t, false>(object, ptr, ctx);
+}
+const char* PackedUInt32Parser(void* object, const char* ptr,
+                               ParseContext* ctx) {
+  return VarintParser<uint32_t, false>(object, ptr, ctx);
+}
+const char* PackedInt64Parser(void* object, const char* ptr,
+                              ParseContext* ctx) {
+  return VarintParser<int64_t, false>(object, ptr, ctx);
+}
+const char* PackedUInt64Parser(void* object, const char* ptr,
+                               ParseContext* ctx) {
+  return VarintParser<uint64_t, false>(object, ptr, ctx);
+}
+const char* PackedSInt32Parser(void* object, const char* ptr,
+                               ParseContext* ctx) {
+  return VarintParser<int32_t, true>(object, ptr, ctx);
+}
+const char* PackedSInt64Parser(void* object, const char* ptr,
+                               ParseContext* ctx) {
+  return VarintParser<int64_t, true>(object, ptr, ctx);
+}
+
+const char* PackedEnumParser(void* object, const char* ptr, ParseContext* ctx) {
+  return VarintParser<int, false>(object, ptr, ctx);
+}
+
+const char* PackedBoolParser(void* object, const char* ptr, ParseContext* ctx) {
+  return VarintParser<bool, false>(object, ptr, ctx);
+}
+
+template <typename T>
+const char* FixedParser(void* object, const char* ptr, ParseContext* ctx) {
+  int size = ReadSize(&ptr);
+  return ctx->ReadPackedFixed(ptr, size,
+                              static_cast<RepeatedField<T>*>(object));
+}
+
+const char* PackedFixed32Parser(void* object, const char* ptr,
+                                ParseContext* ctx) {
+  return FixedParser<uint32_t>(object, ptr, ctx);
+}
+const char* PackedSFixed32Parser(void* object, const char* ptr,
+                                 ParseContext* ctx) {
+  return FixedParser<int32_t>(object, ptr, ctx);
+}
+const char* PackedFixed64Parser(void* object, const char* ptr,
+                                ParseContext* ctx) {
+  return FixedParser<uint64_t>(object, ptr, ctx);
+}
+const char* PackedSFixed64Parser(void* object, const char* ptr,
+                                 ParseContext* ctx) {
+  return FixedParser<int64_t>(object, ptr, ctx);
+}
+const char* PackedFloatParser(void* object, const char* ptr,
+                              ParseContext* ctx) {
+  return FixedParser<float>(object, ptr, ctx);
+}
+const char* PackedDoubleParser(void* object, const char* ptr,
+                               ParseContext* ctx) {
+  return FixedParser<double>(object, ptr, ctx);
+}
+
+class UnknownFieldLiteParserHelper {
+ public:
+  explicit UnknownFieldLiteParserHelper(std::string* unknown)
+      : unknown_(unknown) {}
+
+  void AddVarint(uint32_t num, uint64_t value) {
+    if (unknown_ == nullptr) return;
+    WriteVarint(num * 8, unknown_);
+    WriteVarint(value, unknown_);
+  }
+  void AddFixed64(uint32_t num, uint64_t value) {
+    if (unknown_ == nullptr) return;
+    WriteVarint(num * 8 + 1, unknown_);
+    char buffer[8];
+    io::CodedOutputStream::WriteLittleEndian64ToArray(
+        value, reinterpret_cast<uint8_t*>(buffer));
+    unknown_->append(buffer, 8);
+  }
+  const char* ParseLengthDelimited(uint32_t num, const char* ptr,
+                                   ParseContext* ctx) {
+    int size = ReadSize(&ptr);
+    GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+    if (unknown_ == nullptr) return ctx->Skip(ptr, size);
+    WriteVarint(num * 8 + 2, unknown_);
+    WriteVarint(size, unknown_);
+    return ctx->AppendString(ptr, size, unknown_);
+  }
+  const char* ParseGroup(uint32_t num, const char* ptr, ParseContext* ctx) {
+    if (unknown_) WriteVarint(num * 8 + 3, unknown_);
+    ptr = ctx->ParseGroup(this, ptr, num * 8 + 3);
+    GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+    if (unknown_) WriteVarint(num * 8 + 4, unknown_);
+    return ptr;
+  }
+  void AddFixed32(uint32_t num, uint32_t value) {
+    if (unknown_ == nullptr) return;
+    WriteVarint(num * 8 + 5, unknown_);
+    char buffer[4];
+    io::CodedOutputStream::WriteLittleEndian32ToArray(
+        value, reinterpret_cast<uint8_t*>(buffer));
+    unknown_->append(buffer, 4);
+  }
+
+  const char* _InternalParse(const char* ptr, ParseContext* ctx) {
+    return WireFormatParser(*this, ptr, ctx);
+  }
+
+ private:
+  std::string* unknown_;
+};
+
+const char* UnknownGroupLiteParse(std::string* unknown, const char* ptr,
+                                  ParseContext* ctx) {
+  UnknownFieldLiteParserHelper field_parser(unknown);
+  return WireFormatParser(field_parser, ptr, ctx);
+}
+
+const char* UnknownFieldParse(uint32_t tag, std::string* unknown,
+                              const char* ptr, ParseContext* ctx) {
+  UnknownFieldLiteParserHelper field_parser(unknown);
+  return FieldParser(tag, field_parser, ptr, ctx);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/reflection_ops.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/reflection_ops.cpp
new file mode 100644
index 0000000..3a1972e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/reflection_ops.cpp
@@ -0,0 +1,459 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+#include <google/protobuf/reflection_ops.h>
+
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/map_field_inl.h>
+#include <google/protobuf/unknown_field_set.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+static const Reflection* GetReflectionOrDie(const Message& m) {
+  const Reflection* r = m.GetReflection();
+  if (r == nullptr) {
+    const Descriptor* d = m.GetDescriptor();
+    const std::string& mtype = d ? d->name() : "unknown";
+    // RawMessage is one known type for which GetReflection() returns nullptr.
+    GOOGLE_LOG(FATAL) << "Message does not support reflection (type " << mtype << ").";
+  }
+  return r;
+}
+
+void ReflectionOps::Copy(const Message& from, Message* to) {
+  if (&from == to) return;
+  Clear(to);
+  Merge(from, to);
+}
+
+void ReflectionOps::Merge(const Message& from, Message* to) {
+  GOOGLE_CHECK_NE(&from, to);
+
+  const Descriptor* descriptor = from.GetDescriptor();
+  GOOGLE_CHECK_EQ(to->GetDescriptor(), descriptor)
+      << "Tried to merge messages of different types "
+      << "(merge " << descriptor->full_name() << " to "
+      << to->GetDescriptor()->full_name() << ")";
+
+  const Reflection* from_reflection = GetReflectionOrDie(from);
+  const Reflection* to_reflection = GetReflectionOrDie(*to);
+  bool is_from_generated = (from_reflection->GetMessageFactory() ==
+                            google::protobuf::MessageFactory::generated_factory());
+  bool is_to_generated = (to_reflection->GetMessageFactory() ==
+                          google::protobuf::MessageFactory::generated_factory());
+
+  std::vector<const FieldDescriptor*> fields;
+  from_reflection->ListFieldsOmitStripped(from, &fields);
+  for (const FieldDescriptor* field : fields) {
+    if (field->is_repeated()) {
+      // Use map reflection if both are in map status and have the
+      // same map type to avoid sync with repeated field.
+      // Note: As from and to messages have the same descriptor, the
+      // map field types are the same if they are both generated
+      // messages or both dynamic messages.
+      if (is_from_generated == is_to_generated && field->is_map()) {
+        const MapFieldBase* from_field =
+            from_reflection->GetMapData(from, field);
+        MapFieldBase* to_field = to_reflection->MutableMapData(to, field);
+        if (to_field->IsMapValid() && from_field->IsMapValid()) {
+          to_field->MergeFrom(*from_field);
+          continue;
+        }
+      }
+      int count = from_reflection->FieldSize(from, field);
+      for (int j = 0; j < count; j++) {
+        switch (field->cpp_type()) {
+#define HANDLE_TYPE(CPPTYPE, METHOD)                                      \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE:                                \
+    to_reflection->Add##METHOD(                                           \
+        to, field, from_reflection->GetRepeated##METHOD(from, field, j)); \
+    break;
+
+          HANDLE_TYPE(INT32, Int32);
+          HANDLE_TYPE(INT64, Int64);
+          HANDLE_TYPE(UINT32, UInt32);
+          HANDLE_TYPE(UINT64, UInt64);
+          HANDLE_TYPE(FLOAT, Float);
+          HANDLE_TYPE(DOUBLE, Double);
+          HANDLE_TYPE(BOOL, Bool);
+          HANDLE_TYPE(STRING, String);
+          HANDLE_TYPE(ENUM, Enum);
+#undef HANDLE_TYPE
+
+          case FieldDescriptor::CPPTYPE_MESSAGE:
+            const Message& from_child =
+                from_reflection->GetRepeatedMessage(from, field, j);
+            if (from_reflection == to_reflection) {
+              to_reflection
+                  ->AddMessage(to, field,
+                               from_child.GetReflection()->GetMessageFactory())
+                  ->MergeFrom(from_child);
+            } else {
+              to_reflection->AddMessage(to, field)->MergeFrom(from_child);
+            }
+            break;
+        }
+      }
+    } else {
+      switch (field->cpp_type()) {
+#define HANDLE_TYPE(CPPTYPE, METHOD)                                       \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE:                                 \
+    to_reflection->Set##METHOD(to, field,                                  \
+                               from_reflection->Get##METHOD(from, field)); \
+    break;
+
+        HANDLE_TYPE(INT32, Int32);
+        HANDLE_TYPE(INT64, Int64);
+        HANDLE_TYPE(UINT32, UInt32);
+        HANDLE_TYPE(UINT64, UInt64);
+        HANDLE_TYPE(FLOAT, Float);
+        HANDLE_TYPE(DOUBLE, Double);
+        HANDLE_TYPE(BOOL, Bool);
+        HANDLE_TYPE(STRING, String);
+        HANDLE_TYPE(ENUM, Enum);
+#undef HANDLE_TYPE
+
+        case FieldDescriptor::CPPTYPE_MESSAGE:
+          const Message& from_child = from_reflection->GetMessage(from, field);
+          if (from_reflection == to_reflection) {
+            to_reflection
+                ->MutableMessage(
+                    to, field, from_child.GetReflection()->GetMessageFactory())
+                ->MergeFrom(from_child);
+          } else {
+            to_reflection->MutableMessage(to, field)->MergeFrom(from_child);
+          }
+          break;
+      }
+    }
+  }
+
+  if (!from_reflection->GetUnknownFields(from).empty()) {
+    to_reflection->MutableUnknownFields(to)->MergeFrom(
+        from_reflection->GetUnknownFields(from));
+  }
+}
+
+void ReflectionOps::Clear(Message* message) {
+  const Reflection* reflection = GetReflectionOrDie(*message);
+
+  std::vector<const FieldDescriptor*> fields;
+  reflection->ListFieldsOmitStripped(*message, &fields);
+  for (const FieldDescriptor* field : fields) {
+    reflection->ClearField(message, field);
+  }
+
+  if (reflection->GetInternalMetadata(*message).have_unknown_fields()) {
+    reflection->MutableUnknownFields(message)->Clear();
+  }
+}
+
+bool ReflectionOps::IsInitialized(const Message& message, bool check_fields,
+                                  bool check_descendants) {
+  const Descriptor* descriptor = message.GetDescriptor();
+  const Reflection* reflection = GetReflectionOrDie(message);
+  if (const int field_count = descriptor->field_count()) {
+    const FieldDescriptor* begin = descriptor->field(0);
+    const FieldDescriptor* end = begin + field_count;
+    GOOGLE_DCHECK_EQ(descriptor->field(field_count - 1), end - 1);
+
+    if (check_fields) {
+      // Check required fields of this message.
+      for (const FieldDescriptor* field = begin; field != end; ++field) {
+        if (field->is_required() && !reflection->HasField(message, field)) {
+          return false;
+        }
+      }
+    }
+
+    if (check_descendants) {
+      for (const FieldDescriptor* field = begin; field != end; ++field) {
+        if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+          const Descriptor* message_type = field->message_type();
+          if (PROTOBUF_PREDICT_FALSE(message_type->options().map_entry())) {
+            if (message_type->field(1)->cpp_type() ==
+                FieldDescriptor::CPPTYPE_MESSAGE) {
+              const MapFieldBase* map_field =
+                  reflection->GetMapData(message, field);
+              if (map_field->IsMapValid()) {
+                MapIterator it(const_cast<Message*>(&message), field);
+                MapIterator end_map(const_cast<Message*>(&message), field);
+                for (map_field->MapBegin(&it), map_field->MapEnd(&end_map);
+                     it != end_map; ++it) {
+                  if (!it.GetValueRef().GetMessageValue().IsInitialized()) {
+                    return false;
+                  }
+                }
+              }
+            }
+          } else if (field->is_repeated()) {
+            const int size = reflection->FieldSize(message, field);
+            for (int j = 0; j < size; j++) {
+              if (!reflection->GetRepeatedMessage(message, field, j)
+                       .IsInitialized()) {
+                return false;
+              }
+            }
+          } else if (reflection->HasField(message, field)) {
+            if (!reflection->GetMessage(message, field).IsInitialized()) {
+              return false;
+            }
+          }
+        }
+      }
+    }
+  }
+  if (check_descendants && reflection->HasExtensionSet(message) &&
+      !reflection->GetExtensionSet(message).IsInitialized()) {
+    return false;
+  }
+  return true;
+}
+
+bool ReflectionOps::IsInitialized(const Message& message) {
+  const Descriptor* descriptor = message.GetDescriptor();
+  const Reflection* reflection = GetReflectionOrDie(message);
+
+  // Check required fields of this message.
+  {
+    const int field_count = descriptor->field_count();
+    for (int i = 0; i < field_count; i++) {
+      if (descriptor->field(i)->is_required()) {
+        if (!reflection->HasField(message, descriptor->field(i))) {
+          return false;
+        }
+      }
+    }
+  }
+
+  // Check that sub-messages are initialized.
+  std::vector<const FieldDescriptor*> fields;
+  // Should be safe to skip stripped fields because required fields are not
+  // stripped.
+  reflection->ListFieldsOmitStripped(message, &fields);
+  for (const FieldDescriptor* field : fields) {
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+
+      if (field->is_map()) {
+        const FieldDescriptor* value_field = field->message_type()->field(1);
+        if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+          const MapFieldBase* map_field =
+              reflection->GetMapData(message, field);
+          if (map_field->IsMapValid()) {
+            MapIterator iter(const_cast<Message*>(&message), field);
+            MapIterator end(const_cast<Message*>(&message), field);
+            for (map_field->MapBegin(&iter), map_field->MapEnd(&end);
+                 iter != end; ++iter) {
+              if (!iter.GetValueRef().GetMessageValue().IsInitialized()) {
+                return false;
+              }
+            }
+            continue;
+          }
+        } else {
+          continue;
+        }
+      }
+
+      if (field->is_repeated()) {
+        int size = reflection->FieldSize(message, field);
+
+        for (int j = 0; j < size; j++) {
+          if (!reflection->GetRepeatedMessage(message, field, j)
+                   .IsInitialized()) {
+            return false;
+          }
+        }
+      } else {
+        if (!reflection->GetMessage(message, field).IsInitialized()) {
+          return false;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+static bool IsMapValueMessageTyped(const FieldDescriptor* map_field) {
+  return map_field->message_type()->field(1)->cpp_type() ==
+         FieldDescriptor::CPPTYPE_MESSAGE;
+}
+
+void ReflectionOps::DiscardUnknownFields(Message* message) {
+  const Reflection* reflection = GetReflectionOrDie(*message);
+
+  reflection->MutableUnknownFields(message)->Clear();
+
+  // Walk through the fields of this message and DiscardUnknownFields on any
+  // messages present.
+  std::vector<const FieldDescriptor*> fields;
+  reflection->ListFields(*message, &fields);
+  for (const FieldDescriptor* field : fields) {
+    // Skip over non-message fields.
+    if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+      continue;
+    }
+    // Discard the unknown fields in maps that contain message values.
+    if (field->is_map() && IsMapValueMessageTyped(field)) {
+      const MapFieldBase* map_field =
+          reflection->MutableMapData(message, field);
+      if (map_field->IsMapValid()) {
+        MapIterator iter(message, field);
+        MapIterator end(message, field);
+        for (map_field->MapBegin(&iter), map_field->MapEnd(&end); iter != end;
+             ++iter) {
+          iter.MutableValueRef()->MutableMessageValue()->DiscardUnknownFields();
+        }
+      }
+      // Discard every unknown field inside messages in a repeated field.
+    } else if (field->is_repeated()) {
+      int size = reflection->FieldSize(*message, field);
+      for (int j = 0; j < size; j++) {
+        reflection->MutableRepeatedMessage(message, field, j)
+            ->DiscardUnknownFields();
+      }
+      // Discard the unknown fields inside an optional message.
+    } else {
+      reflection->MutableMessage(message, field)->DiscardUnknownFields();
+    }
+  }
+}
+
+static std::string SubMessagePrefix(const std::string& prefix,
+                                    const FieldDescriptor* field, int index) {
+  std::string result(prefix);
+  if (field->is_extension()) {
+    result.append("(");
+    result.append(field->full_name());
+    result.append(")");
+  } else {
+    result.append(field->name());
+  }
+  if (index != -1) {
+    result.append("[");
+    result.append(StrCat(index));
+    result.append("]");
+  }
+  result.append(".");
+  return result;
+}
+
+void ReflectionOps::FindInitializationErrors(const Message& message,
+                                             const std::string& prefix,
+                                             std::vector<std::string>* errors) {
+  const Descriptor* descriptor = message.GetDescriptor();
+  const Reflection* reflection = GetReflectionOrDie(message);
+
+  // Check required fields of this message.
+  {
+    const int field_count = descriptor->field_count();
+    for (int i = 0; i < field_count; i++) {
+      if (descriptor->field(i)->is_required()) {
+        if (!reflection->HasField(message, descriptor->field(i))) {
+          errors->push_back(prefix + descriptor->field(i)->name());
+        }
+      }
+    }
+  }
+
+  // Check sub-messages.
+  std::vector<const FieldDescriptor*> fields;
+  reflection->ListFieldsOmitStripped(message, &fields);
+  for (const FieldDescriptor* field : fields) {
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+
+      if (field->is_repeated()) {
+        int size = reflection->FieldSize(message, field);
+
+        for (int j = 0; j < size; j++) {
+          const Message& sub_message =
+              reflection->GetRepeatedMessage(message, field, j);
+          FindInitializationErrors(sub_message,
+                                   SubMessagePrefix(prefix, field, j), errors);
+        }
+      } else {
+        const Message& sub_message = reflection->GetMessage(message, field);
+        FindInitializationErrors(sub_message,
+                                 SubMessagePrefix(prefix, field, -1), errors);
+      }
+    }
+  }
+}
+
+void GenericSwap(Message* lhs, Message* rhs) {
+#ifndef PROTOBUF_FORCE_COPY_IN_SWAP
+  GOOGLE_DCHECK(Arena::InternalGetOwningArena(lhs) !=
+         Arena::InternalGetOwningArena(rhs));
+  GOOGLE_DCHECK(Arena::InternalGetOwningArena(lhs) != nullptr ||
+         Arena::InternalGetOwningArena(rhs) != nullptr);
+#endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+  // At least one of these must have an arena, so make `rhs` point to it.
+  Arena* arena = Arena::InternalGetOwningArena(rhs);
+  if (arena == nullptr) {
+    std::swap(lhs, rhs);
+    arena = Arena::InternalGetOwningArena(rhs);
+  }
+
+  // Improve efficiency by placing the temporary on an arena so that messages
+  // are copied twice rather than three times.
+  Message* tmp = rhs->New(arena);
+  tmp->CheckTypeAndMergeFrom(*lhs);
+  lhs->Clear();
+  lhs->CheckTypeAndMergeFrom(*rhs);
+#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+  rhs->Clear();
+  rhs->CheckTypeAndMergeFrom(*tmp);
+  if (arena == nullptr) delete tmp;
+#else   // PROTOBUF_FORCE_COPY_IN_SWAP
+  rhs->GetReflection()->Swap(tmp, rhs);
+#endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/repeated_field.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/repeated_field.cpp
new file mode 100644
index 0000000..7264d0a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/repeated_field.cpp
@@ -0,0 +1,71 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/repeated_field.h>
+
+#include <algorithm>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedField<bool>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedField<int32_t>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedField<uint32_t>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedField<int64_t>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedField<uint64_t>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedField<float>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedField<double>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedPtrField<std::string>;
+
+namespace internal {
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedIterator<bool>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedIterator<int32_t>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedIterator<uint32_t>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedIterator<int64_t>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedIterator<uint64_t>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedIterator<float>;
+template class PROTOBUF_EXPORT_TEMPLATE_DEFINE RepeatedIterator<double>;
+}  // namespace internal
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/repeated_ptr_field.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/repeated_ptr_field.cpp
new file mode 100644
index 0000000..8e86727
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/repeated_ptr_field.cpp
@@ -0,0 +1,152 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <algorithm>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/implicit_weak_message.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/port.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+namespace internal {
+
+void** RepeatedPtrFieldBase::InternalExtend(int extend_amount) {
+  int new_size = current_size_ + extend_amount;
+  if (total_size_ >= new_size) {
+    // N.B.: rep_ is non-nullptr because extend_amount is always > 0, hence
+    // total_size must be non-zero since it is lower-bounded by new_size.
+    return &rep_->elements[current_size_];
+  }
+  Rep* old_rep = rep_;
+  Arena* arena = GetOwningArena();
+  new_size = internal::CalculateReserveSize<void*, kRepHeaderSize>(total_size_,
+                                                                   new_size);
+  GOOGLE_CHECK_LE(static_cast<int64_t>(new_size),
+           static_cast<int64_t>(
+               (std::numeric_limits<size_t>::max() - kRepHeaderSize) /
+               sizeof(old_rep->elements[0])))
+      << "Requested size is too large to fit into size_t.";
+  size_t bytes = kRepHeaderSize + sizeof(old_rep->elements[0]) * new_size;
+  if (arena == nullptr) {
+    rep_ = reinterpret_cast<Rep*>(::operator new(bytes));
+  } else {
+    rep_ = reinterpret_cast<Rep*>(Arena::CreateArray<char>(arena, bytes));
+  }
+  const int old_total_size = total_size_;
+  total_size_ = new_size;
+  if (old_rep) {
+    if (old_rep->allocated_size > 0) {
+      memcpy(rep_->elements, old_rep->elements,
+             old_rep->allocated_size * sizeof(rep_->elements[0]));
+    }
+    rep_->allocated_size = old_rep->allocated_size;
+
+    const size_t old_size =
+        old_total_size * sizeof(rep_->elements[0]) + kRepHeaderSize;
+    if (arena == nullptr) {
+      internal::SizedDelete(old_rep, old_size);
+    } else {
+      arena_->ReturnArrayMemory(old_rep, old_size);
+    }
+  } else {
+    rep_->allocated_size = 0;
+  }
+  return &rep_->elements[current_size_];
+}
+
+void RepeatedPtrFieldBase::Reserve(int new_size) {
+  if (new_size > current_size_) {
+    InternalExtend(new_size - current_size_);
+  }
+}
+
+void RepeatedPtrFieldBase::DestroyProtos() {
+  GOOGLE_DCHECK(rep_);
+  GOOGLE_DCHECK(arena_ == nullptr);
+  int n = rep_->allocated_size;
+  void* const* elements = rep_->elements;
+  for (int i = 0; i < n; i++) {
+    delete static_cast<MessageLite*>(elements[i]);
+  }
+  const size_t size = total_size_ * sizeof(elements[0]) + kRepHeaderSize;
+  internal::SizedDelete(rep_, size);
+  rep_ = nullptr;
+}
+
+void* RepeatedPtrFieldBase::AddOutOfLineHelper(void* obj) {
+  if (!rep_ || rep_->allocated_size == total_size_) {
+    InternalExtend(1);  // Equivalent to "Reserve(total_size_ + 1)"
+  }
+  ++rep_->allocated_size;
+  rep_->elements[current_size_++] = obj;
+  return obj;
+}
+
+void RepeatedPtrFieldBase::CloseGap(int start, int num) {
+  if (rep_ == nullptr) return;
+  // Close up a gap of "num" elements starting at offset "start".
+  for (int i = start + num; i < rep_->allocated_size; ++i)
+    rep_->elements[i - num] = rep_->elements[i];
+  current_size_ -= num;
+  rep_->allocated_size -= num;
+}
+
+MessageLite* RepeatedPtrFieldBase::AddWeak(const MessageLite* prototype) {
+  if (rep_ != nullptr && current_size_ < rep_->allocated_size) {
+    return reinterpret_cast<MessageLite*>(rep_->elements[current_size_++]);
+  }
+  if (!rep_ || rep_->allocated_size == total_size_) {
+    Reserve(total_size_ + 1);
+  }
+  ++rep_->allocated_size;
+  MessageLite* result = prototype
+                            ? prototype->New(arena_)
+                            : Arena::CreateMessage<ImplicitWeakMessage>(arena_);
+  rep_->elements[current_size_++] = result;
+  return result;
+}
+
+}  // namespace internal
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/service.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/service.cpp
new file mode 100644
index 0000000..5394568
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/service.cpp
@@ -0,0 +1,45 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/service.h>
+
+namespace google {
+namespace protobuf {
+
+Service::~Service() {}
+RpcChannel::~RpcChannel() {}
+RpcController::~RpcController() {}
+
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/source_context.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/source_context.pb.cpp
new file mode 100644
index 0000000..e7525e9
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/source_context.pb.cpp
@@ -0,0 +1,297 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/source_context.proto
+
+#include <google/protobuf/source_context.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR SourceContext::SourceContext(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.file_name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct SourceContextDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR SourceContextDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~SourceContextDefaultTypeInternal() {}
+  union {
+    SourceContext _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SourceContextDefaultTypeInternal _SourceContext_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2fsource_5fcontext_2eproto[1];
+static constexpr ::_pb::EnumDescriptor const** file_level_enum_descriptors_google_2fprotobuf_2fsource_5fcontext_2eproto = nullptr;
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2fsource_5fcontext_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2fsource_5fcontext_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceContext, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::SourceContext, _impl_.file_name_),
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::SourceContext)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_SourceContext_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2fsource_5fcontext_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n$google/protobuf/source_context.proto\022\017"
+  "google.protobuf\"\"\n\rSourceContext\022\021\n\tfile"
+  "_name\030\001 \001(\tB\212\001\n\023com.google.protobufB\022Sou"
+  "rceContextProtoP\001Z6google.golang.org/pro"
+  "tobuf/types/known/sourcecontextpb\242\002\003GPB\252"
+  "\002\036Google.Protobuf.WellKnownTypesb\006proto3"
+  ;
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto = {
+    false, false, 240, descriptor_table_protodef_google_2fprotobuf_2fsource_5fcontext_2eproto,
+    "google/protobuf/source_context.proto",
+    &descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto_once, nullptr, 0, 1,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2fsource_5fcontext_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2fsource_5fcontext_2eproto, file_level_enum_descriptors_google_2fprotobuf_2fsource_5fcontext_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2fsource_5fcontext_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2fsource_5fcontext_2eproto(&descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class SourceContext::_Internal {
+ public:
+};
+
+SourceContext::SourceContext(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.SourceContext)
+}
+SourceContext::SourceContext(const SourceContext& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  SourceContext* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.file_name_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.file_name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.file_name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_file_name().empty()) {
+    _this->_impl_.file_name_.Set(from._internal_file_name(), 
+      _this->GetArenaForAllocation());
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.SourceContext)
+}
+
+inline void SourceContext::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.file_name_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.file_name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.file_name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+SourceContext::~SourceContext() {
+  // @@protoc_insertion_point(destructor:google.protobuf.SourceContext)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void SourceContext::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.file_name_.Destroy();
+}
+
+void SourceContext::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void SourceContext::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.SourceContext)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.file_name_.ClearToEmpty();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* SourceContext::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // string file_name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_file_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.SourceContext.file_name"));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* SourceContext::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.SourceContext)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string file_name = 1;
+  if (!this->_internal_file_name().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_file_name().data(), static_cast<int>(this->_internal_file_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.SourceContext.file_name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_file_name(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.SourceContext)
+  return target;
+}
+
+size_t SourceContext::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.SourceContext)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // string file_name = 1;
+  if (!this->_internal_file_name().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_file_name());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData SourceContext::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    SourceContext::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*SourceContext::GetClassData() const { return &_class_data_; }
+
+
+void SourceContext::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<SourceContext*>(&to_msg);
+  auto& from = static_cast<const SourceContext&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.SourceContext)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (!from._internal_file_name().empty()) {
+    _this->_internal_set_file_name(from._internal_file_name());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void SourceContext::CopyFrom(const SourceContext& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.SourceContext)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool SourceContext::IsInitialized() const {
+  return true;
+}
+
+void SourceContext::InternalSwap(SourceContext* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.file_name_, lhs_arena,
+      &other->_impl_.file_name_, rhs_arena
+  );
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata SourceContext::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto_getter, &descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fsource_5fcontext_2eproto[0]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::SourceContext*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::SourceContext >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::SourceContext >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/struct.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/struct.pb.cpp
new file mode 100644
index 0000000..87c72d4
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/struct.pb.cpp
@@ -0,0 +1,1057 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/struct.proto
+
+#include <google/protobuf/struct.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR Struct_FieldsEntry_DoNotUse::Struct_FieldsEntry_DoNotUse(
+    ::_pbi::ConstantInitialized) {}
+struct Struct_FieldsEntry_DoNotUseDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR Struct_FieldsEntry_DoNotUseDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~Struct_FieldsEntry_DoNotUseDefaultTypeInternal() {}
+  union {
+    Struct_FieldsEntry_DoNotUse _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 Struct_FieldsEntry_DoNotUseDefaultTypeInternal _Struct_FieldsEntry_DoNotUse_default_instance_;
+PROTOBUF_CONSTEXPR Struct::Struct(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.fields_)*/{::_pbi::ConstantInitialized()}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct StructDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR StructDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~StructDefaultTypeInternal() {}
+  union {
+    Struct _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 StructDefaultTypeInternal _Struct_default_instance_;
+PROTOBUF_CONSTEXPR Value::Value(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.kind_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}
+  , /*decltype(_impl_._oneof_case_)*/{}} {}
+struct ValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR ValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~ValueDefaultTypeInternal() {}
+  union {
+    Value _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ValueDefaultTypeInternal _Value_default_instance_;
+PROTOBUF_CONSTEXPR ListValue::ListValue(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.values_)*/{}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct ListValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR ListValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~ListValueDefaultTypeInternal() {}
+  union {
+    ListValue _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ListValueDefaultTypeInternal _ListValue_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2fstruct_2eproto[4];
+static const ::_pb::EnumDescriptor* file_level_enum_descriptors_google_2fprotobuf_2fstruct_2eproto[1];
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2fstruct_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2fstruct_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Struct_FieldsEntry_DoNotUse, _has_bits_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Struct_FieldsEntry_DoNotUse, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Struct_FieldsEntry_DoNotUse, key_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Struct_FieldsEntry_DoNotUse, value_),
+  0,
+  1,
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Struct, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Struct, _impl_.fields_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Value, _internal_metadata_),
+  ~0u,  // no _extensions_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Value, _impl_._oneof_case_[0]),
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  ::_pbi::kInvalidFieldOffsetTag,
+  ::_pbi::kInvalidFieldOffsetTag,
+  ::_pbi::kInvalidFieldOffsetTag,
+  ::_pbi::kInvalidFieldOffsetTag,
+  ::_pbi::kInvalidFieldOffsetTag,
+  ::_pbi::kInvalidFieldOffsetTag,
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Value, _impl_.kind_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ListValue, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::ListValue, _impl_.values_),
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, 8, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Struct_FieldsEntry_DoNotUse)},
+  { 10, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Struct)},
+  { 17, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Value)},
+  { 30, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::ListValue)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_Struct_FieldsEntry_DoNotUse_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_Struct_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_Value_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_ListValue_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2fstruct_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n\034google/protobuf/struct.proto\022\017google.p"
+  "rotobuf\"\204\001\n\006Struct\0223\n\006fields\030\001 \003(\0132#.goo"
+  "gle.protobuf.Struct.FieldsEntry\032E\n\013Field"
+  "sEntry\022\013\n\003key\030\001 \001(\t\022%\n\005value\030\002 \001(\0132\026.goo"
+  "gle.protobuf.Value:\0028\001\"\352\001\n\005Value\0220\n\nnull"
+  "_value\030\001 \001(\0162\032.google.protobuf.NullValue"
+  "H\000\022\026\n\014number_value\030\002 \001(\001H\000\022\026\n\014string_val"
+  "ue\030\003 \001(\tH\000\022\024\n\nbool_value\030\004 \001(\010H\000\022/\n\014stru"
+  "ct_value\030\005 \001(\0132\027.google.protobuf.StructH"
+  "\000\0220\n\nlist_value\030\006 \001(\0132\032.google.protobuf."
+  "ListValueH\000B\006\n\004kind\"3\n\tListValue\022&\n\006valu"
+  "es\030\001 \003(\0132\026.google.protobuf.Value*\033\n\tNull"
+  "Value\022\016\n\nNULL_VALUE\020\000B\177\n\023com.google.prot"
+  "obufB\013StructProtoP\001Z/google.golang.org/p"
+  "rotobuf/types/known/structpb\370\001\001\242\002\003GPB\252\002\036"
+  "Google.Protobuf.WellKnownTypesb\006proto3"
+  ;
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2fstruct_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fstruct_2eproto = {
+    false, false, 638, descriptor_table_protodef_google_2fprotobuf_2fstruct_2eproto,
+    "google/protobuf/struct.proto",
+    &descriptor_table_google_2fprotobuf_2fstruct_2eproto_once, nullptr, 0, 4,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2fstruct_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2fstruct_2eproto, file_level_enum_descriptors_google_2fprotobuf_2fstruct_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2fstruct_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2fstruct_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2fstruct_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2fstruct_2eproto(&descriptor_table_google_2fprotobuf_2fstruct_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* NullValue_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2fstruct_2eproto);
+  return file_level_enum_descriptors_google_2fprotobuf_2fstruct_2eproto[0];
+}
+bool NullValue_IsValid(int value) {
+  switch (value) {
+    case 0:
+      return true;
+    default:
+      return false;
+  }
+}
+
+
+// ===================================================================
+
+Struct_FieldsEntry_DoNotUse::Struct_FieldsEntry_DoNotUse() {}
+Struct_FieldsEntry_DoNotUse::Struct_FieldsEntry_DoNotUse(::PROTOBUF_NAMESPACE_ID::Arena* arena)
+    : SuperType(arena) {}
+void Struct_FieldsEntry_DoNotUse::MergeFrom(const Struct_FieldsEntry_DoNotUse& other) {
+  MergeFromInternal(other);
+}
+::PROTOBUF_NAMESPACE_ID::Metadata Struct_FieldsEntry_DoNotUse::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fstruct_2eproto_getter, &descriptor_table_google_2fprotobuf_2fstruct_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fstruct_2eproto[0]);
+}
+
+// ===================================================================
+
+class Struct::_Internal {
+ public:
+};
+
+Struct::Struct(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  if (arena != nullptr && !is_message_owned) {
+    arena->OwnCustomDestructor(this, &Struct::ArenaDtor);
+  }
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Struct)
+}
+Struct::Struct(const Struct& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Struct* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_.fields_)*/{}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _this->_impl_.fields_.MergeFrom(from._impl_.fields_);
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Struct)
+}
+
+inline void Struct::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      /*decltype(_impl_.fields_)*/{::_pbi::ArenaInitialized(), arena}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+Struct::~Struct() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Struct)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    ArenaDtor(this);
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Struct::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.fields_.Destruct();
+  _impl_.fields_.~MapField();
+}
+
+void Struct::ArenaDtor(void* object) {
+  Struct* _this = reinterpret_cast< Struct* >(object);
+  _this->_impl_.fields_.Destruct();
+}
+void Struct::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Struct::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Struct)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.fields_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Struct::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // map<string, .google.protobuf.Value> fields = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(&_impl_.fields_, ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Struct::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Struct)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // map<string, .google.protobuf.Value> fields = 1;
+  if (!this->_internal_fields().empty()) {
+    using MapType = ::_pb::Map<std::string, ::PROTOBUF_NAMESPACE_ID::Value>;
+    using WireHelper = Struct_FieldsEntry_DoNotUse::Funcs;
+    const auto& map_field = this->_internal_fields();
+    auto check_utf8 = [](const MapType::value_type& entry) {
+      (void)entry;
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+        entry.first.data(), static_cast<int>(entry.first.length()),
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+        "google.protobuf.Struct.FieldsEntry.key");
+    };
+
+    if (stream->IsSerializationDeterministic() && map_field.size() > 1) {
+      for (const auto& entry : ::_pbi::MapSorterPtr<MapType>(map_field)) {
+        target = WireHelper::InternalSerialize(1, entry.first, entry.second, target, stream);
+        check_utf8(entry);
+      }
+    } else {
+      for (const auto& entry : map_field) {
+        target = WireHelper::InternalSerialize(1, entry.first, entry.second, target, stream);
+        check_utf8(entry);
+      }
+    }
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Struct)
+  return target;
+}
+
+size_t Struct::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Struct)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // map<string, .google.protobuf.Value> fields = 1;
+  total_size += 1 *
+      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->_internal_fields_size());
+  for (::PROTOBUF_NAMESPACE_ID::Map< std::string, ::PROTOBUF_NAMESPACE_ID::Value >::const_iterator
+      it = this->_internal_fields().begin();
+      it != this->_internal_fields().end(); ++it) {
+    total_size += Struct_FieldsEntry_DoNotUse::Funcs::ByteSizeLong(it->first, it->second);
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Struct::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Struct::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Struct::GetClassData() const { return &_class_data_; }
+
+
+void Struct::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Struct*>(&to_msg);
+  auto& from = static_cast<const Struct&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Struct)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.fields_.MergeFrom(from._impl_.fields_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Struct::CopyFrom(const Struct& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Struct)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Struct::IsInitialized() const {
+  return true;
+}
+
+void Struct::InternalSwap(Struct* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.fields_.InternalSwap(&other->_impl_.fields_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Struct::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fstruct_2eproto_getter, &descriptor_table_google_2fprotobuf_2fstruct_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fstruct_2eproto[1]);
+}
+
+// ===================================================================
+
+class Value::_Internal {
+ public:
+  static const ::PROTOBUF_NAMESPACE_ID::Struct& struct_value(const Value* msg);
+  static const ::PROTOBUF_NAMESPACE_ID::ListValue& list_value(const Value* msg);
+};
+
+const ::PROTOBUF_NAMESPACE_ID::Struct&
+Value::_Internal::struct_value(const Value* msg) {
+  return *msg->_impl_.kind_.struct_value_;
+}
+const ::PROTOBUF_NAMESPACE_ID::ListValue&
+Value::_Internal::list_value(const Value* msg) {
+  return *msg->_impl_.kind_.list_value_;
+}
+void Value::set_allocated_struct_value(::PROTOBUF_NAMESPACE_ID::Struct* struct_value) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  clear_kind();
+  if (struct_value) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+      ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(struct_value);
+    if (message_arena != submessage_arena) {
+      struct_value = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, struct_value, submessage_arena);
+    }
+    set_has_struct_value();
+    _impl_.kind_.struct_value_ = struct_value;
+  }
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Value.struct_value)
+}
+void Value::set_allocated_list_value(::PROTOBUF_NAMESPACE_ID::ListValue* list_value) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+  clear_kind();
+  if (list_value) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+      ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(list_value);
+    if (message_arena != submessage_arena) {
+      list_value = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, list_value, submessage_arena);
+    }
+    set_has_list_value();
+    _impl_.kind_.list_value_ = list_value;
+  }
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Value.list_value)
+}
+Value::Value(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Value)
+}
+Value::Value(const Value& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Value* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.kind_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , /*decltype(_impl_._oneof_case_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  clear_has_kind();
+  switch (from.kind_case()) {
+    case kNullValue: {
+      _this->_internal_set_null_value(from._internal_null_value());
+      break;
+    }
+    case kNumberValue: {
+      _this->_internal_set_number_value(from._internal_number_value());
+      break;
+    }
+    case kStringValue: {
+      _this->_internal_set_string_value(from._internal_string_value());
+      break;
+    }
+    case kBoolValue: {
+      _this->_internal_set_bool_value(from._internal_bool_value());
+      break;
+    }
+    case kStructValue: {
+      _this->_internal_mutable_struct_value()->::PROTOBUF_NAMESPACE_ID::Struct::MergeFrom(
+          from._internal_struct_value());
+      break;
+    }
+    case kListValue: {
+      _this->_internal_mutable_list_value()->::PROTOBUF_NAMESPACE_ID::ListValue::MergeFrom(
+          from._internal_list_value());
+      break;
+    }
+    case KIND_NOT_SET: {
+      break;
+    }
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Value)
+}
+
+inline void Value::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.kind_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+    , /*decltype(_impl_._oneof_case_)*/{}
+  };
+  clear_has_kind();
+}
+
+Value::~Value() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Value)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Value::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  if (has_kind()) {
+    clear_kind();
+  }
+}
+
+void Value::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Value::clear_kind() {
+// @@protoc_insertion_point(one_of_clear_start:google.protobuf.Value)
+  switch (kind_case()) {
+    case kNullValue: {
+      // No need to clear
+      break;
+    }
+    case kNumberValue: {
+      // No need to clear
+      break;
+    }
+    case kStringValue: {
+      _impl_.kind_.string_value_.Destroy();
+      break;
+    }
+    case kBoolValue: {
+      // No need to clear
+      break;
+    }
+    case kStructValue: {
+      if (GetArenaForAllocation() == nullptr) {
+        delete _impl_.kind_.struct_value_;
+      }
+      break;
+    }
+    case kListValue: {
+      if (GetArenaForAllocation() == nullptr) {
+        delete _impl_.kind_.list_value_;
+      }
+      break;
+    }
+    case KIND_NOT_SET: {
+      break;
+    }
+  }
+  _impl_._oneof_case_[0] = KIND_NOT_SET;
+}
+
+
+void Value::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Value)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  clear_kind();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Value::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // .google.protobuf.NullValue null_value = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          _internal_set_null_value(static_cast<::PROTOBUF_NAMESPACE_ID::NullValue>(val));
+        } else
+          goto handle_unusual;
+        continue;
+      // double number_value = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 17)) {
+          _internal_set_number_value(::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<double>(ptr));
+          ptr += sizeof(double);
+        } else
+          goto handle_unusual;
+        continue;
+      // string string_value = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          auto str = _internal_mutable_string_value();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Value.string_value"));
+        } else
+          goto handle_unusual;
+        continue;
+      // bool bool_value = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 32)) {
+          _internal_set_bool_value(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr));
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.Struct struct_value = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+          ptr = ctx->ParseMessage(_internal_mutable_struct_value(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.ListValue list_value = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 50)) {
+          ptr = ctx->ParseMessage(_internal_mutable_list_value(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Value::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Value)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // .google.protobuf.NullValue null_value = 1;
+  if (_internal_has_null_value()) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      1, this->_internal_null_value(), target);
+  }
+
+  // double number_value = 2;
+  if (_internal_has_number_value()) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteDoubleToArray(2, this->_internal_number_value(), target);
+  }
+
+  // string string_value = 3;
+  if (_internal_has_string_value()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_string_value().data(), static_cast<int>(this->_internal_string_value().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Value.string_value");
+    target = stream->WriteStringMaybeAliased(
+        3, this->_internal_string_value(), target);
+  }
+
+  // bool bool_value = 4;
+  if (_internal_has_bool_value()) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(4, this->_internal_bool_value(), target);
+  }
+
+  // .google.protobuf.Struct struct_value = 5;
+  if (_internal_has_struct_value()) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(5, _Internal::struct_value(this),
+        _Internal::struct_value(this).GetCachedSize(), target, stream);
+  }
+
+  // .google.protobuf.ListValue list_value = 6;
+  if (_internal_has_list_value()) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(6, _Internal::list_value(this),
+        _Internal::list_value(this).GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Value)
+  return target;
+}
+
+size_t Value::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Value)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  switch (kind_case()) {
+    // .google.protobuf.NullValue null_value = 1;
+    case kNullValue: {
+      total_size += 1 +
+        ::_pbi::WireFormatLite::EnumSize(this->_internal_null_value());
+      break;
+    }
+    // double number_value = 2;
+    case kNumberValue: {
+      total_size += 1 + 8;
+      break;
+    }
+    // string string_value = 3;
+    case kStringValue: {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+          this->_internal_string_value());
+      break;
+    }
+    // bool bool_value = 4;
+    case kBoolValue: {
+      total_size += 1 + 1;
+      break;
+    }
+    // .google.protobuf.Struct struct_value = 5;
+    case kStructValue: {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.kind_.struct_value_);
+      break;
+    }
+    // .google.protobuf.ListValue list_value = 6;
+    case kListValue: {
+      total_size += 1 +
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          *_impl_.kind_.list_value_);
+      break;
+    }
+    case KIND_NOT_SET: {
+      break;
+    }
+  }
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Value::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Value::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Value::GetClassData() const { return &_class_data_; }
+
+
+void Value::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Value*>(&to_msg);
+  auto& from = static_cast<const Value&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Value)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  switch (from.kind_case()) {
+    case kNullValue: {
+      _this->_internal_set_null_value(from._internal_null_value());
+      break;
+    }
+    case kNumberValue: {
+      _this->_internal_set_number_value(from._internal_number_value());
+      break;
+    }
+    case kStringValue: {
+      _this->_internal_set_string_value(from._internal_string_value());
+      break;
+    }
+    case kBoolValue: {
+      _this->_internal_set_bool_value(from._internal_bool_value());
+      break;
+    }
+    case kStructValue: {
+      _this->_internal_mutable_struct_value()->::PROTOBUF_NAMESPACE_ID::Struct::MergeFrom(
+          from._internal_struct_value());
+      break;
+    }
+    case kListValue: {
+      _this->_internal_mutable_list_value()->::PROTOBUF_NAMESPACE_ID::ListValue::MergeFrom(
+          from._internal_list_value());
+      break;
+    }
+    case KIND_NOT_SET: {
+      break;
+    }
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Value::CopyFrom(const Value& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Value)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Value::IsInitialized() const {
+  return true;
+}
+
+void Value::InternalSwap(Value* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_.kind_, other->_impl_.kind_);
+  swap(_impl_._oneof_case_[0], other->_impl_._oneof_case_[0]);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Value::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fstruct_2eproto_getter, &descriptor_table_google_2fprotobuf_2fstruct_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fstruct_2eproto[2]);
+}
+
+// ===================================================================
+
+class ListValue::_Internal {
+ public:
+};
+
+ListValue::ListValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.ListValue)
+}
+ListValue::ListValue(const ListValue& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  ListValue* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.values_){from._impl_.values_}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.ListValue)
+}
+
+inline void ListValue::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.values_){arena}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+ListValue::~ListValue() {
+  // @@protoc_insertion_point(destructor:google.protobuf.ListValue)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void ListValue::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.values_.~RepeatedPtrField();
+}
+
+void ListValue::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void ListValue::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.ListValue)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.values_.Clear();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* ListValue::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // repeated .google.protobuf.Value values = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_values(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* ListValue::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.ListValue)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.Value values = 1;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_values_size()); i < n; i++) {
+    const auto& repfield = this->_internal_values(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.ListValue)
+  return target;
+}
+
+size_t ListValue::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.ListValue)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.Value values = 1;
+  total_size += 1UL * this->_internal_values_size();
+  for (const auto& msg : this->_impl_.values_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ListValue::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    ListValue::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ListValue::GetClassData() const { return &_class_data_; }
+
+
+void ListValue::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<ListValue*>(&to_msg);
+  auto& from = static_cast<const ListValue&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.ListValue)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.values_.MergeFrom(from._impl_.values_);
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void ListValue::CopyFrom(const ListValue& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.ListValue)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool ListValue::IsInitialized() const {
+  return true;
+}
+
+void ListValue::InternalSwap(ListValue* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.values_.InternalSwap(&other->_impl_.values_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata ListValue::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fstruct_2eproto_getter, &descriptor_table_google_2fprotobuf_2fstruct_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fstruct_2eproto[3]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Struct_FieldsEntry_DoNotUse*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Struct_FieldsEntry_DoNotUse >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Struct_FieldsEntry_DoNotUse >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Struct*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Struct >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Struct >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Value*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Value >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Value >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::ListValue*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::ListValue >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::ListValue >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/bytestream.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/bytestream.cpp
new file mode 100644
index 0000000..980d6f6
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/bytestream.cpp
@@ -0,0 +1,194 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/stubs/bytestream.h>
+
+#include <string.h>
+#include <algorithm>
+
+#include <google/protobuf/stubs/logging.h>
+
+namespace google {
+namespace protobuf {
+namespace strings {
+
+void ByteSource::CopyTo(ByteSink* sink, size_t n) {
+  while (n > 0) {
+    StringPiece fragment = Peek();
+    if (fragment.empty()) {
+      GOOGLE_LOG(DFATAL) << "ByteSource::CopyTo() overran input.";
+      break;
+    }
+    std::size_t fragment_size = std::min<std::size_t>(n, fragment.size());
+    sink->Append(fragment.data(), fragment_size);
+    Skip(fragment_size);
+    n -= fragment_size;
+  }
+}
+
+void ByteSink::Flush() {}
+
+void UncheckedArrayByteSink::Append(const char* data, size_t n) {
+  if (data != dest_) {
+    // Catch cases where the pointer returned by GetAppendBuffer() was modified.
+    GOOGLE_DCHECK(!(dest_ <= data && data < (dest_ + n)))
+        << "Append() data[] overlaps with dest_[]";
+    memcpy(dest_, data, n);
+  }
+  dest_ += n;
+}
+
+CheckedArrayByteSink::CheckedArrayByteSink(char* outbuf, size_t capacity)
+    : outbuf_(outbuf), capacity_(capacity), size_(0), overflowed_(false) {
+}
+
+void CheckedArrayByteSink::Append(const char* bytes, size_t n) {
+  size_t available = capacity_ - size_;
+  if (n > available) {
+    n = available;
+    overflowed_ = true;
+  }
+  if (n > 0 && bytes != (outbuf_ + size_)) {
+    // Catch cases where the pointer returned by GetAppendBuffer() was modified.
+    GOOGLE_DCHECK(!(outbuf_ <= bytes && bytes < (outbuf_ + capacity_)))
+        << "Append() bytes[] overlaps with outbuf_[]";
+    memcpy(outbuf_ + size_, bytes, n);
+  }
+  size_ += n;
+}
+
+GrowingArrayByteSink::GrowingArrayByteSink(size_t estimated_size)
+    : capacity_(estimated_size),
+      buf_(new char[estimated_size]),
+      size_(0) {
+}
+
+GrowingArrayByteSink::~GrowingArrayByteSink() {
+  delete[] buf_;  // Just in case the user didn't call GetBuffer.
+}
+
+void GrowingArrayByteSink::Append(const char* bytes, size_t n) {
+  size_t available = capacity_ - size_;
+  if (bytes != (buf_ + size_)) {
+    // Catch cases where the pointer returned by GetAppendBuffer() was modified.
+    // We need to test for this before calling Expand() which may reallocate.
+    GOOGLE_DCHECK(!(buf_ <= bytes && bytes < (buf_ + capacity_)))
+        << "Append() bytes[] overlaps with buf_[]";
+  }
+  if (n > available) {
+    Expand(n - available);
+  }
+  if (n > 0 && bytes != (buf_ + size_)) {
+    memcpy(buf_ + size_, bytes, n);
+  }
+  size_ += n;
+}
+
+char* GrowingArrayByteSink::GetBuffer(size_t* nbytes) {
+  ShrinkToFit();
+  char* b = buf_;
+  *nbytes = size_;
+  buf_ = nullptr;
+  size_ = capacity_ = 0;
+  return b;
+}
+
+void GrowingArrayByteSink::Expand(size_t amount) {  // Expand by at least 50%.
+  size_t new_capacity = std::max(capacity_ + amount, (3 * capacity_) / 2);
+  char* bigger = new char[new_capacity];
+  memcpy(bigger, buf_, size_);
+  delete[] buf_;
+  buf_ = bigger;
+  capacity_ = new_capacity;
+}
+
+void GrowingArrayByteSink::ShrinkToFit() {
+  // Shrink only if the buffer is large and size_ is less than 3/4
+  // of capacity_.
+  if (capacity_ > 256 && size_ < (3 * capacity_) / 4) {
+    char* just_enough = new char[size_];
+    memcpy(just_enough, buf_, size_);
+    delete[] buf_;
+    buf_ = just_enough;
+    capacity_ = size_;
+  }
+}
+
+void StringByteSink::Append(const char* data, size_t n) {
+  dest_->append(data, n);
+}
+
+size_t ArrayByteSource::Available() const {
+  return input_.size();
+}
+
+StringPiece ArrayByteSource::Peek() {
+  return input_;
+}
+
+void ArrayByteSource::Skip(size_t n) {
+  GOOGLE_DCHECK_LE(n, input_.size());
+  input_.remove_prefix(n);
+}
+
+LimitByteSource::LimitByteSource(ByteSource *source, size_t limit)
+  : source_(source),
+    limit_(limit) {
+}
+
+size_t LimitByteSource::Available() const {
+  size_t available = source_->Available();
+  if (available > limit_) {
+    available = limit_;
+  }
+
+  return available;
+}
+
+StringPiece LimitByteSource::Peek() {
+  StringPiece piece = source_->Peek();
+  return StringPiece(piece.data(), std::min(piece.size(), limit_));
+}
+
+void LimitByteSource::Skip(size_t n) {
+  GOOGLE_DCHECK_LE(n, limit_);
+  source_->Skip(n);
+  limit_ -= n;
+}
+
+void LimitByteSource::CopyTo(ByteSink *sink, size_t n) {
+  GOOGLE_DCHECK_LE(n, limit_);
+  source_->CopyTo(sink, n);
+  limit_ -= n;
+}
+
+}  // namespace strings
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/common.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/common.cpp
new file mode 100644
index 0000000..1423021
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/common.cpp
@@ -0,0 +1,324 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+
+#include <google/protobuf/stubs/common.h>
+
+#include <atomic>
+#include <errno.h>
+#include <sstream>
+#include <stdio.h>
+#include <vector>
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN  // We only need minimal includes
+#endif
+#include <windows.h>
+#define snprintf _snprintf    // see comment in strutil.cc
+#endif
+#if defined(__ANDROID__)
+#include <android/log.h>
+#endif
+
+#include <google/protobuf/stubs/callback.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/int128.h>
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+namespace internal {
+
+void VerifyVersion(int headerVersion,
+                   int minLibraryVersion,
+                   const char* filename) {
+  if (GOOGLE_PROTOBUF_VERSION < minLibraryVersion) {
+    // Library is too old for headers.
+    GOOGLE_LOG(FATAL)
+      << "This program requires version " << VersionString(minLibraryVersion)
+      << " of the Protocol Buffer runtime library, but the installed version "
+         "is " << VersionString(GOOGLE_PROTOBUF_VERSION) << ".  Please update "
+         "your library.  If you compiled the program yourself, make sure that "
+         "your headers are from the same version of Protocol Buffers as your "
+         "link-time library.  (Version verification failed in \""
+      << filename << "\".)";
+  }
+  if (headerVersion < kMinHeaderVersionForLibrary) {
+    // Headers are too old for library.
+    GOOGLE_LOG(FATAL)
+      << "This program was compiled against version "
+      << VersionString(headerVersion) << " of the Protocol Buffer runtime "
+         "library, which is not compatible with the installed version ("
+      << VersionString(GOOGLE_PROTOBUF_VERSION) <<  ").  Contact the program "
+         "author for an update.  If you compiled the program yourself, make "
+         "sure that your headers are from the same version of Protocol Buffers "
+         "as your link-time library.  (Version verification failed in \""
+      << filename << "\".)";
+  }
+}
+
+std::string VersionString(int version) {
+  int major = version / 1000000;
+  int minor = (version / 1000) % 1000;
+  int micro = version % 1000;
+
+  // 128 bytes should always be enough, but we use snprintf() anyway to be
+  // safe.
+  char buffer[128];
+  snprintf(buffer, sizeof(buffer), "%d.%d.%d", major, minor, micro);
+
+  // Guard against broken MSVC snprintf().
+  buffer[sizeof(buffer)-1] = '\0';
+
+  return buffer;
+}
+
+}  // namespace internal
+
+// ===================================================================
+// emulates google3/base/logging.cc
+
+// If the minimum logging level is not set, we default to logging messages for
+// all levels.
+#ifndef GOOGLE_PROTOBUF_MIN_LOG_LEVEL
+#define GOOGLE_PROTOBUF_MIN_LOG_LEVEL LOGLEVEL_INFO
+#endif
+
+namespace internal {
+
+#if defined(__ANDROID__)
+inline void DefaultLogHandler(LogLevel level, const char* filename, int line,
+                              const std::string& message) {
+  if (level < GOOGLE_PROTOBUF_MIN_LOG_LEVEL) {
+    return;
+  }
+  static const char* level_names[] = {"INFO", "WARNING", "ERROR", "FATAL"};
+
+  static const int android_log_levels[] = {
+      ANDROID_LOG_INFO,   // LOG(INFO),
+      ANDROID_LOG_WARN,   // LOG(WARNING)
+      ANDROID_LOG_ERROR,  // LOG(ERROR)
+      ANDROID_LOG_FATAL,  // LOG(FATAL)
+  };
+
+  // Bound the logging level.
+  const int android_log_level = android_log_levels[level];
+  ::std::ostringstream ostr;
+  ostr << "[libprotobuf " << level_names[level] << " " << filename << ":"
+       << line << "] " << message.c_str();
+
+  // Output the log string the Android log at the appropriate level.
+  __android_log_write(android_log_level, "libprotobuf-native",
+                      ostr.str().c_str());
+  // Also output to std::cerr.
+  fprintf(stderr, "%s", ostr.str().c_str());
+  fflush(stderr);
+
+  // Indicate termination if needed.
+  if (android_log_level == ANDROID_LOG_FATAL) {
+    __android_log_write(ANDROID_LOG_FATAL, "libprotobuf-native",
+                        "terminating.\n");
+  }
+}
+
+#else
+void DefaultLogHandler(LogLevel level, const char* filename, int line,
+                       const std::string& message) {
+  if (level < GOOGLE_PROTOBUF_MIN_LOG_LEVEL) {
+    return;
+  }
+  static const char* level_names[] = { "INFO", "WARNING", "ERROR", "FATAL" };
+
+  // We use fprintf() instead of cerr because we want this to work at static
+  // initialization time.
+  fprintf(stderr, "[libprotobuf %s %s:%d] %s\n",
+          level_names[level], filename, line, message.c_str());
+  fflush(stderr);  // Needed on MSVC.
+}
+#endif
+
+void NullLogHandler(LogLevel /* level */, const char* /* filename */,
+                    int /* line */, const std::string& /* message */) {
+  // Nothing.
+}
+
+static LogHandler* log_handler_ = &DefaultLogHandler;
+static std::atomic<int> log_silencer_count_ = ATOMIC_VAR_INIT(0);
+
+LogMessage& LogMessage::operator<<(const std::string& value) {
+  message_ += value;
+  return *this;
+}
+
+LogMessage& LogMessage::operator<<(const char* value) {
+  message_ += value;
+  return *this;
+}
+
+LogMessage& LogMessage::operator<<(const StringPiece& value) {
+  message_ += value.ToString();
+  return *this;
+}
+
+LogMessage& LogMessage::operator<<(const util::Status& status) {
+  message_ += status.ToString();
+  return *this;
+}
+
+LogMessage& LogMessage::operator<<(const uint128& value) {
+  std::ostringstream str;
+  str << value;
+  message_ += str.str();
+  return *this;
+}
+
+LogMessage& LogMessage::operator<<(char value) {
+  return *this << StringPiece(&value, 1);
+}
+
+LogMessage& LogMessage::operator<<(void* value) {
+  StrAppend(&message_, strings::Hex(reinterpret_cast<uintptr_t>(value)));
+  return *this;
+}
+
+#undef DECLARE_STREAM_OPERATOR
+#define DECLARE_STREAM_OPERATOR(TYPE)              \
+  LogMessage& LogMessage::operator<<(TYPE value) { \
+    StrAppend(&message_, value);                   \
+    return *this;                                  \
+  }
+
+DECLARE_STREAM_OPERATOR(int)
+DECLARE_STREAM_OPERATOR(unsigned int)
+DECLARE_STREAM_OPERATOR(long)           // NOLINT(runtime/int)
+DECLARE_STREAM_OPERATOR(unsigned long)  // NOLINT(runtime/int)
+DECLARE_STREAM_OPERATOR(double)
+DECLARE_STREAM_OPERATOR(long long)           // NOLINT(runtime/int)
+DECLARE_STREAM_OPERATOR(unsigned long long)  // NOLINT(runtime/int)
+#undef DECLARE_STREAM_OPERATOR
+
+LogMessage::LogMessage(LogLevel level, const char* filename, int line)
+  : level_(level), filename_(filename), line_(line) {}
+LogMessage::~LogMessage() {}
+
+void LogMessage::Finish() {
+  bool suppress = false;
+
+  if (level_ != LOGLEVEL_FATAL) {
+    suppress = log_silencer_count_ > 0;
+  }
+
+  if (!suppress) {
+    log_handler_(level_, filename_, line_, message_);
+  }
+
+  if (level_ == LOGLEVEL_FATAL) {
+#if PROTOBUF_USE_EXCEPTIONS
+    throw FatalException(filename_, line_, message_);
+#else
+    abort();
+#endif
+  }
+}
+
+void LogFinisher::operator=(LogMessage& other) {
+  other.Finish();
+}
+
+}  // namespace internal
+
+LogHandler* SetLogHandler(LogHandler* new_func) {
+  LogHandler* old = internal::log_handler_;
+  if (old == &internal::NullLogHandler) {
+    old = nullptr;
+  }
+  if (new_func == nullptr) {
+    internal::log_handler_ = &internal::NullLogHandler;
+  } else {
+    internal::log_handler_ = new_func;
+  }
+  return old;
+}
+
+LogSilencer::LogSilencer() {
+  ++internal::log_silencer_count_;
+}
+
+LogSilencer::~LogSilencer() {
+  --internal::log_silencer_count_;
+}
+
+// ===================================================================
+// emulates google3/base/callback.cc
+
+Closure::~Closure() {}
+
+namespace internal { FunctionClosure0::~FunctionClosure0() {} }
+
+void DoNothing() {}
+
+// ===================================================================
+// emulates google3/util/endian/endian.h
+//
+// TODO(xiaofeng): PROTOBUF_LITTLE_ENDIAN is unfortunately defined in
+// google/protobuf/io/coded_stream.h and therefore can not be used here.
+// Maybe move that macro definition here in the future.
+uint32_t ghtonl(uint32_t x) {
+  union {
+    uint32_t result;
+    uint8_t result_array[4];
+  };
+  result_array[0] = static_cast<uint8_t>(x >> 24);
+  result_array[1] = static_cast<uint8_t>((x >> 16) & 0xFF);
+  result_array[2] = static_cast<uint8_t>((x >> 8) & 0xFF);
+  result_array[3] = static_cast<uint8_t>(x & 0xFF);
+  return result;
+}
+
+#if PROTOBUF_USE_EXCEPTIONS
+FatalException::~FatalException() throw() {}
+
+const char* FatalException::what() const throw() {
+  return message_.c_str();
+}
+#endif
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/int128.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/int128.cpp
new file mode 100644
index 0000000..a151cfb
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/int128.cpp
@@ -0,0 +1,193 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/stubs/int128.h>
+
+#include <iomanip>
+#include <ostream>  // NOLINT(readability/streams)
+#include <sstream>
+
+#include <google/protobuf/stubs/logging.h>
+
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+const uint128_pod kuint128max = {uint64_t{0xFFFFFFFFFFFFFFFFu},
+                                 uint64_t{0xFFFFFFFFFFFFFFFFu}};
+
+// Returns the 0-based position of the last set bit (i.e., most significant bit)
+// in the given uint64. The argument may not be 0.
+//
+// For example:
+//   Given: 5 (decimal) == 101 (binary)
+//   Returns: 2
+#define STEP(T, n, pos, sh)                   \
+  do {                                        \
+    if ((n) >= (static_cast<T>(1) << (sh))) { \
+      (n) = (n) >> (sh);                      \
+      (pos) |= (sh);                          \
+    }                                         \
+  } while (0)
+static inline int Fls64(uint64_t n) {
+  GOOGLE_DCHECK_NE(0, n);
+  int pos = 0;
+  STEP(uint64_t, n, pos, 0x20);
+  uint32_t n32 = n;
+  STEP(uint32_t, n32, pos, 0x10);
+  STEP(uint32_t, n32, pos, 0x08);
+  STEP(uint32_t, n32, pos, 0x04);
+  return pos + ((uint64_t{0x3333333322221100u} >> (n32 << 2)) & 0x3);
+}
+#undef STEP
+
+// Like Fls64() above, but returns the 0-based position of the last set bit
+// (i.e., most significant bit) in the given uint128. The argument may not be 0.
+static inline int Fls128(uint128 n) {
+  if (uint64_t hi = Uint128High64(n)) {
+    return Fls64(hi) + 64;
+  }
+  return Fls64(Uint128Low64(n));
+}
+
+void uint128::DivModImpl(uint128 dividend, uint128 divisor,
+                         uint128* quotient_ret, uint128* remainder_ret) {
+  if (divisor == 0) {
+    GOOGLE_LOG(FATAL) << "Division or mod by zero: dividend.hi=" << dividend.hi_
+                      << ", lo=" << dividend.lo_;
+  } else if (dividend < divisor) {
+    *quotient_ret = 0;
+    *remainder_ret = dividend;
+    return;
+  } else {
+    int dividend_bit_length = Fls128(dividend);
+    int divisor_bit_length = Fls128(divisor);
+    int difference = dividend_bit_length - divisor_bit_length;
+    uint128 quotient = 0;
+    while (difference >= 0) {
+      quotient <<= 1;
+      uint128 shifted_divisor = divisor << difference;
+      if (shifted_divisor <= dividend) {
+        dividend -= shifted_divisor;
+        quotient += 1;
+      }
+      difference -= 1;
+    }
+    //record the final quotient and remainder
+    *quotient_ret = quotient;
+    *remainder_ret = dividend;
+  }
+}
+
+
+uint128& uint128::operator/=(const uint128& divisor) {
+  uint128 quotient = 0;
+  uint128 remainder = 0;
+  DivModImpl(*this, divisor, &quotient, &remainder);
+  *this = quotient;
+  return *this;
+}
+uint128& uint128::operator%=(const uint128& divisor) {
+  uint128 quotient = 0;
+  uint128 remainder = 0;
+  DivModImpl(*this, divisor, &quotient, &remainder);
+  *this = remainder;
+  return *this;
+}
+
+std::ostream& operator<<(std::ostream& o, const uint128& b) {
+  std::ios_base::fmtflags flags = o.flags();
+
+  // Select a divisor which is the largest power of the base < 2^64.
+  uint128 div;
+  std::streamsize div_base_log;
+  switch (flags & std::ios::basefield) {
+    case std::ios::hex:
+      div =
+          static_cast<uint64_t>(uint64_t{0x1000000000000000u});  // 16^15
+      div_base_log = 15;
+      break;
+    case std::ios::oct:
+      div = static_cast<uint64_t>(
+          uint64_t{01000000000000000000000u});  // 8^21
+      div_base_log = 21;
+      break;
+    default:  // std::ios::dec
+      div = static_cast<uint64_t>(
+          uint64_t{10000000000000000000u});  // 10^19
+      div_base_log = 19;
+      break;
+  }
+
+  // Now piece together the uint128 representation from three chunks of
+  // the original value, each less than "div" and therefore representable
+  // as a uint64.
+  std::ostringstream os;
+  std::ios_base::fmtflags copy_mask =
+      std::ios::basefield | std::ios::showbase | std::ios::uppercase;
+  os.setf(flags & copy_mask, copy_mask);
+  uint128 high = b;
+  uint128 low;
+  uint128::DivModImpl(high, div, &high, &low);
+  uint128 mid;
+  uint128::DivModImpl(high, div, &high, &mid);
+  if (high.lo_ != 0) {
+    os << high.lo_;
+    os << std::noshowbase << std::setfill('0') << std::setw(div_base_log);
+    os << mid.lo_;
+    os << std::setw(div_base_log);
+  } else if (mid.lo_ != 0) {
+    os << mid.lo_;
+    os << std::noshowbase << std::setfill('0') << std::setw(div_base_log);
+  }
+  os << low.lo_;
+  std::string rep = os.str();
+
+  // Add the requisite padding.
+  std::streamsize width = o.width(0);
+  auto repSize = static_cast<std::streamsize>(rep.size());
+  if (width > repSize) {
+    if ((flags & std::ios::adjustfield) == std::ios::left) {
+      rep.append(width - repSize, o.fill());
+    } else {
+      rep.insert(static_cast<std::string::size_type>(0), width - repSize,
+                 o.fill());
+    }
+  }
+
+  // Stream the final representation in a single "<<" call.
+  return o << rep;
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>  // NOLINT
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/status.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/status.cpp
new file mode 100644
index 0000000..f5c0fa4
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/status.cpp
@@ -0,0 +1,262 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+#include <google/protobuf/stubs/status.h>
+
+#include <ostream>
+#include <stdio.h>
+#include <string>
+#include <utility>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace status_internal {
+namespace {
+
+inline std::string StatusCodeToString(StatusCode code) {
+  switch (code) {
+    case StatusCode::kOk:
+      return "OK";
+    case StatusCode::kCancelled:
+      return "CANCELLED";
+    case StatusCode::kUnknown:
+      return "UNKNOWN";
+    case StatusCode::kInvalidArgument:
+      return "INVALID_ARGUMENT";
+    case StatusCode::kDeadlineExceeded:
+      return "DEADLINE_EXCEEDED";
+    case StatusCode::kNotFound:
+      return "NOT_FOUND";
+    case StatusCode::kAlreadyExists:
+      return "ALREADY_EXISTS";
+    case StatusCode::kPermissionDenied:
+      return "PERMISSION_DENIED";
+    case StatusCode::kUnauthenticated:
+      return "UNAUTHENTICATED";
+    case StatusCode::kResourceExhausted:
+      return "RESOURCE_EXHAUSTED";
+    case StatusCode::kFailedPrecondition:
+      return "FAILED_PRECONDITION";
+    case StatusCode::kAborted:
+      return "ABORTED";
+    case StatusCode::kOutOfRange:
+      return "OUT_OF_RANGE";
+    case StatusCode::kUnimplemented:
+      return "UNIMPLEMENTED";
+    case StatusCode::kInternal:
+      return "INTERNAL";
+    case StatusCode::kUnavailable:
+      return "UNAVAILABLE";
+    case StatusCode::kDataLoss:
+      return "DATA_LOSS";
+  }
+
+  // No default clause, clang will abort if a code is missing from
+  // above switch.
+  return "UNKNOWN";
+}
+
+}  // namespace
+
+Status::Status() : error_code_(StatusCode::kOk) {}
+
+Status::Status(StatusCode error_code, StringPiece error_message)
+    : error_code_(error_code) {
+  if (error_code != StatusCode::kOk) {
+    error_message_ = error_message.ToString();
+  }
+}
+
+Status::Status(const Status& other)
+    : error_code_(other.error_code_), error_message_(other.error_message_) {
+}
+
+Status& Status::operator=(const Status& other) {
+  error_code_ = other.error_code_;
+  error_message_ = other.error_message_;
+  return *this;
+}
+
+bool Status::operator==(const Status& x) const {
+  return error_code_ == x.error_code_ &&
+      error_message_ == x.error_message_;
+}
+
+std::string Status::ToString() const {
+  if (error_code_ == StatusCode::kOk) {
+    return "OK";
+  } else {
+    if (error_message_.empty()) {
+      return StatusCodeToString(error_code_);
+    } else {
+      return StatusCodeToString(error_code_) + ":" + error_message_;
+    }
+  }
+}
+
+Status OkStatus() { return Status(); }
+
+std::ostream& operator<<(std::ostream& os, const Status& x) {
+  os << x.ToString();
+  return os;
+}
+
+bool IsAborted(const Status& status) {
+  return status.code() == StatusCode::kAborted;
+}
+
+bool IsAlreadyExists(const Status& status) {
+  return status.code() == StatusCode::kAlreadyExists;
+}
+
+bool IsCancelled(const Status& status) {
+  return status.code() == StatusCode::kCancelled;
+}
+
+bool IsDataLoss(const Status& status) {
+  return status.code() == StatusCode::kDataLoss;
+}
+
+bool IsDeadlineExceeded(const Status& status) {
+  return status.code() == StatusCode::kDeadlineExceeded;
+}
+
+bool IsFailedPrecondition(const Status& status) {
+  return status.code() == StatusCode::kFailedPrecondition;
+}
+
+bool IsInternal(const Status& status) {
+  return status.code() == StatusCode::kInternal;
+}
+
+bool IsInvalidArgument(const Status& status) {
+  return status.code() == StatusCode::kInvalidArgument;
+}
+
+bool IsNotFound(const Status& status) {
+  return status.code() == StatusCode::kNotFound;
+}
+
+bool IsOutOfRange(const Status& status) {
+  return status.code() == StatusCode::kOutOfRange;
+}
+
+bool IsPermissionDenied(const Status& status) {
+  return status.code() == StatusCode::kPermissionDenied;
+}
+
+bool IsResourceExhausted(const Status& status) {
+  return status.code() == StatusCode::kResourceExhausted;
+}
+
+bool IsUnauthenticated(const Status& status) {
+  return status.code() == StatusCode::kUnauthenticated;
+}
+
+bool IsUnavailable(const Status& status) {
+  return status.code() == StatusCode::kUnavailable;
+}
+
+bool IsUnimplemented(const Status& status) {
+  return status.code() == StatusCode::kUnimplemented;
+}
+
+bool IsUnknown(const Status& status) {
+  return status.code() == StatusCode::kUnknown;
+}
+
+Status AbortedError(StringPiece message) {
+  return Status(StatusCode::kAborted, message);
+}
+
+Status AlreadyExistsError(StringPiece message) {
+  return Status(StatusCode::kAlreadyExists, message);
+}
+
+Status CancelledError(StringPiece message) {
+  return Status(StatusCode::kCancelled, message);
+}
+
+Status DataLossError(StringPiece message) {
+  return Status(StatusCode::kDataLoss, message);
+}
+
+Status DeadlineExceededError(StringPiece message) {
+  return Status(StatusCode::kDeadlineExceeded, message);
+}
+
+Status FailedPreconditionError(StringPiece message) {
+  return Status(StatusCode::kFailedPrecondition, message);
+}
+
+Status InternalError(StringPiece message) {
+  return Status(StatusCode::kInternal, message);
+}
+
+Status InvalidArgumentError(StringPiece message) {
+  return Status(StatusCode::kInvalidArgument, message);
+}
+
+Status NotFoundError(StringPiece message) {
+  return Status(StatusCode::kNotFound, message);
+}
+
+Status OutOfRangeError(StringPiece message) {
+  return Status(StatusCode::kOutOfRange, message);
+}
+
+Status PermissionDeniedError(StringPiece message) {
+  return Status(StatusCode::kPermissionDenied, message);
+}
+
+Status ResourceExhaustedError(StringPiece message) {
+  return Status(StatusCode::kResourceExhausted, message);
+}
+
+Status UnauthenticatedError(StringPiece message) {
+  return Status(StatusCode::kUnauthenticated, message);
+}
+
+Status UnavailableError(StringPiece message) {
+  return Status(StatusCode::kUnavailable, message);
+}
+
+Status UnimplementedError(StringPiece message) {
+  return Status(StatusCode::kUnimplemented, message);
+}
+
+Status UnknownError(StringPiece message) {
+  return Status(StatusCode::kUnknown, message);
+}
+
+}  // namespace status_internal
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/statusor.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/statusor.cpp
new file mode 100644
index 0000000..9c0a178
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/statusor.cpp
@@ -0,0 +1,48 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/stubs/statusor.h>
+
+#include <google/protobuf/stubs/logging.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace statusor_internal {
+
+void StatusOrHelper::Crash(const Status& status) {
+  GOOGLE_LOG(FATAL) << "Attempting to fetch value instead of handling error "
+                    << status.ToString();
+}
+
+}  // namespace statusor_internal
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/stringpiece.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/stringpiece.cpp
new file mode 100644
index 0000000..7188046
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/stringpiece.cpp
@@ -0,0 +1,256 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+#include <google/protobuf/stubs/stringpiece.h>
+
+#include <string.h>
+#include <algorithm>
+#include <climits>
+#include <string>
+#include <ostream>
+
+#include <google/protobuf/stubs/logging.h>
+
+namespace google {
+namespace protobuf {
+namespace stringpiece_internal {
+
+std::ostream& operator<<(std::ostream& o, StringPiece piece) {
+  o.write(piece.data(), piece.size());
+  return o;
+}
+
+void StringPiece::LogFatalSizeTooBig(size_t size, const char* details) {
+  GOOGLE_LOG(FATAL) << "size too big: " << size << " details: " << details;
+}
+
+void StringPiece::CopyToString(std::string* target) const {
+  target->assign(ptr_, length_);
+}
+
+void StringPiece::AppendToString(std::string* target) const {
+  target->append(ptr_, length_);
+}
+
+bool StringPiece::Consume(StringPiece x) {
+  if (starts_with(x)) {
+    ptr_ += x.length_;
+    length_ -= x.length_;
+    return true;
+  }
+  return false;
+}
+
+bool StringPiece::ConsumeFromEnd(StringPiece x) {
+  if (ends_with(x)) {
+    length_ -= x.length_;
+    return true;
+  }
+  return false;
+}
+
+StringPiece::size_type StringPiece::copy(char* buf, size_type n,
+                                         size_type pos) const {
+  size_type ret = std::min(length_ - pos, n);
+  memcpy(buf, ptr_ + pos, ret);
+  return ret;
+}
+
+bool StringPiece::contains(StringPiece s) const {
+  return find(s, 0) != npos;
+}
+
+StringPiece::size_type StringPiece::find(StringPiece s, size_type pos) const {
+  if (length_ <= 0 || pos > static_cast<size_type>(length_)) {
+    if (length_ == 0 && pos == 0 && s.length_ == 0) return 0;
+    return npos;
+  }
+  const char *result = std::search(ptr_ + pos, ptr_ + length_,
+                                   s.ptr_, s.ptr_ + s.length_);
+  return result == ptr_ + length_ ? npos : result - ptr_;
+}
+
+StringPiece::size_type StringPiece::find(char c, size_type pos) const {
+  if (length_ <= 0 || pos >= static_cast<size_type>(length_)) {
+    return npos;
+  }
+  const char* result = static_cast<const char*>(
+      memchr(ptr_ + pos, c, length_ - pos));
+  return result != nullptr ? result - ptr_ : npos;
+}
+
+StringPiece::size_type StringPiece::rfind(StringPiece s, size_type pos) const {
+  if (length_ < s.length_) return npos;
+  const size_t ulen = length_;
+  if (s.length_ == 0) return std::min(ulen, pos);
+
+  const char* last = ptr_ + std::min(ulen - s.length_, pos) + s.length_;
+  const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
+  return result != last ? result - ptr_ : npos;
+}
+
+// Search range is [0..pos] inclusive.  If pos == npos, search everything.
+StringPiece::size_type StringPiece::rfind(char c, size_type pos) const {
+  // Note: memrchr() is not available on Windows.
+  if (empty()) return npos;
+  for (size_type i = std::min(pos, length_ - 1);; --i) {
+    if (ptr_[i] == c) {
+      return i;
+    }
+    if (i == 0) break;
+  }
+  return npos;
+}
+
+// For each character in characters_wanted, sets the index corresponding
+// to the ASCII code of that character to 1 in table.  This is used by
+// the find_.*_of methods below to tell whether or not a character is in
+// the lookup table in constant time.
+// The argument `table' must be an array that is large enough to hold all
+// the possible values of an unsigned char.  Thus it should be be declared
+// as follows:
+//   bool table[UCHAR_MAX + 1]
+static inline void BuildLookupTable(StringPiece characters_wanted,
+                                    bool* table) {
+  const StringPiece::size_type length = characters_wanted.length();
+  const char* const data = characters_wanted.data();
+  for (StringPiece::size_type i = 0; i < length; ++i) {
+    table[static_cast<unsigned char>(data[i])] = true;
+  }
+}
+
+StringPiece::size_type StringPiece::find_first_of(StringPiece s,
+                                                  size_type pos) const {
+  if (empty() || s.empty()) {
+    return npos;
+  }
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1) return find_first_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_type i = pos; i < length_; ++i) {
+    if (lookup[static_cast<unsigned char>(ptr_[i])]) {
+      return i;
+    }
+  }
+  return npos;
+}
+
+StringPiece::size_type StringPiece::find_first_not_of(StringPiece s,
+                                                      size_type pos) const {
+  if (empty()) return npos;
+  if (s.empty()) return 0;
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_type i = pos; i < length_; ++i) {
+    if (!lookup[static_cast<unsigned char>(ptr_[i])]) {
+      return i;
+    }
+  }
+  return npos;
+}
+
+StringPiece::size_type StringPiece::find_first_not_of(char c,
+                                                      size_type pos) const {
+  if (empty()) return npos;
+
+  for (; pos < static_cast<size_type>(length_); ++pos) {
+    if (ptr_[pos] != c) {
+      return pos;
+    }
+  }
+  return npos;
+}
+
+StringPiece::size_type StringPiece::find_last_of(StringPiece s,
+                                                 size_type pos) const {
+  if (empty() || s.empty()) return npos;
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1) return find_last_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_type i = std::min(pos, length_ - 1);; --i) {
+    if (lookup[static_cast<unsigned char>(ptr_[i])]) {
+      return i;
+    }
+    if (i == 0) break;
+  }
+  return npos;
+}
+
+StringPiece::size_type StringPiece::find_last_not_of(StringPiece s,
+                                                     size_type pos) const {
+  if (empty()) return npos;
+
+  size_type i = std::min(pos, length() - 1);
+  if (s.empty()) return i;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1) return find_last_not_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (;; --i) {
+    if (!lookup[static_cast<unsigned char>(ptr_[i])]) {
+      return i;
+    }
+    if (i == 0) break;
+  }
+  return npos;
+}
+
+StringPiece::size_type StringPiece::find_last_not_of(char c,
+                                                     size_type pos) const {
+  if (empty()) return npos;
+  size_type i = std::min(pos, length_ - 1);
+  for (;; --i) {
+    if (ptr_[i] != c) {
+      return i;
+    }
+    if (i == 0) break;
+  }
+  return npos;
+}
+
+StringPiece StringPiece::substr(size_type pos, size_type n) const {
+  if (pos > length()) pos = length();
+  if (n > length_ - pos) n = length() - pos;
+  return StringPiece(ptr_ + pos, n);
+}
+
+const StringPiece::size_type StringPiece::npos = size_type(-1);
+
+}  // namespace stringpiece_internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/stringprintf.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/stringprintf.cpp
new file mode 100644
index 0000000..8b890f4
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/stringprintf.cpp
@@ -0,0 +1,175 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2012 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// from google3/base/stringprintf.cc
+
+#include <google/protobuf/stubs/stringprintf.h>
+
+#include <errno.h>
+#include <stdarg.h> // For va_list and related operations
+#include <stdio.h> // MSVC requires this for _vsnprintf
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+
+namespace google {
+namespace protobuf {
+
+#ifdef _MSC_VER
+#ifndef va_copy
+// Define va_copy for MSVC. This is a hack, assuming va_list is simply a
+// pointer into the stack and is safe to copy.
+#define va_copy(dest, src) ((dest) = (src))
+#endif
+#endif
+
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+  // First try with a small fixed size buffer
+  static const int kSpaceLength = 1024;
+  char space[kSpaceLength];
+
+  // It's possible for methods that use a va_list to invalidate
+  // the data in it upon use.  The fix is to make a copy
+  // of the structure before using it and use that copy instead.
+  va_list backup_ap;
+  va_copy(backup_ap, ap);
+  int result = vsnprintf(space, kSpaceLength, format, backup_ap);
+  va_end(backup_ap);
+
+  if (result < kSpaceLength) {
+    if (result >= 0) {
+      // Normal case -- everything fit.
+      dst->append(space, result);
+      return;
+    }
+
+#ifdef _MSC_VER
+    {
+      // Error or MSVC running out of space.  MSVC 8.0 and higher
+      // can be asked about space needed with the special idiom below:
+      va_copy(backup_ap, ap);
+      result = vsnprintf(nullptr, 0, format, backup_ap);
+      va_end(backup_ap);
+    }
+#endif
+
+    if (result < 0) {
+      // Just an error.
+      return;
+    }
+  }
+
+  // Increase the buffer size to the size requested by vsnprintf,
+  // plus one for the closing \0.
+  int length = result+1;
+  char* buf = new char[length];
+
+  // Restore the va_list before we use it again
+  va_copy(backup_ap, ap);
+  result = vsnprintf(buf, length, format, backup_ap);
+  va_end(backup_ap);
+
+  if (result >= 0 && result < length) {
+    // It fit
+    dst->append(buf, result);
+  }
+  delete[] buf;
+}
+
+std::string StringPrintf(const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  std::string result;
+  StringAppendV(&result, format, ap);
+  va_end(ap);
+  return result;
+}
+
+const std::string& SStringPrintf(std::string* dst, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  dst->clear();
+  StringAppendV(dst, format, ap);
+  va_end(ap);
+  return *dst;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  StringAppendV(dst, format, ap);
+  va_end(ap);
+}
+
+// Max arguments supported by StringPrintVector
+const int kStringPrintfVectorMaxArgs = 32;
+
+// An empty block of zero for filler arguments.  This is const so that if
+// printf tries to write to it (via %n) then the program gets a SIGSEGV
+// and we can fix the problem or protect against an attack.
+static const char string_printf_empty_block[256] = { '\0' };
+
+std::string StringPrintfVector(const char* format,
+                               const std::vector<std::string>& v) {
+  GOOGLE_CHECK_LE(v.size(), kStringPrintfVectorMaxArgs)
+      << "StringPrintfVector currently only supports up to "
+      << kStringPrintfVectorMaxArgs << " arguments. "
+      << "Feel free to add support for more if you need it.";
+
+  // Add filler arguments so that bogus format+args have a harder time
+  // crashing the program, corrupting the program (%n),
+  // or displaying random chunks of memory to users.
+
+  const char* cstr[kStringPrintfVectorMaxArgs];
+  for (size_t i = 0; i < v.size(); ++i) {
+    cstr[i] = v[i].c_str();
+  }
+  for (size_t i = v.size(); i < GOOGLE_ARRAYSIZE(cstr); ++i) {
+    cstr[i] = &string_printf_empty_block[0];
+  }
+
+  // I do not know any way to pass kStringPrintfVectorMaxArgs arguments,
+  // or any way to build a va_list by hand, or any API for printf
+  // that accepts an array of arguments.  The best I can do is stick
+  // this COMPILE_ASSERT right next to the actual statement.
+
+  static_assert(kStringPrintfVectorMaxArgs == 32, "arg_count_mismatch");
+  return StringPrintf(format,
+                      cstr[0], cstr[1], cstr[2], cstr[3], cstr[4],
+                      cstr[5], cstr[6], cstr[7], cstr[8], cstr[9],
+                      cstr[10], cstr[11], cstr[12], cstr[13], cstr[14],
+                      cstr[15], cstr[16], cstr[17], cstr[18], cstr[19],
+                      cstr[20], cstr[21], cstr[22], cstr[23], cstr[24],
+                      cstr[25], cstr[26], cstr[27], cstr[28], cstr[29],
+                      cstr[30], cstr[31]);
+}
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/structurally_valid.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/structurally_valid.cpp
new file mode 100644
index 0000000..a535736
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/structurally_valid.cpp
@@ -0,0 +1,617 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: jrm@google.com (Jim Meehan)
+
+#include <google/protobuf/stubs/common.h>
+
+#include <google/protobuf/stubs/stringpiece.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// These four-byte entries compactly encode how many bytes 0..255 to delete
+// in making a string replacement, how many bytes to add 0..255, and the offset
+// 0..64k-1 of the replacement string in remap_string.
+struct RemapEntry {
+  uint8_t delete_bytes;
+  uint8_t add_bytes;
+  uint16_t bytes_offset;
+};
+
+// Exit type codes for state tables. All but the first get stuffed into
+// signed one-byte entries. The first is only generated by executable code.
+// To distinguish from next-state entries, these must be contiguous and
+// all <= kExitNone
+typedef enum {
+  kExitDstSpaceFull = 239,
+  kExitIllegalStructure,  // 240
+  kExitOK,                // 241
+  kExitReject,            // ...
+  kExitReplace1,
+  kExitReplace2,
+  kExitReplace3,
+  kExitReplace21,
+  kExitReplace31,
+  kExitReplace32,
+  kExitReplaceOffset1,
+  kExitReplaceOffset2,
+  kExitReplace1S0,
+  kExitSpecial,
+  kExitDoAgain,
+  kExitRejectAlt,
+  kExitNone               // 255
+} ExitReason;
+
+
+// This struct represents one entire state table. The three initialized byte
+// areas are state_table, remap_base, and remap_string. state0 and state0_size
+// give the byte offset and length within state_table of the initial state --
+// table lookups are expected to start and end in this state, but for
+// truncated UTF-8 strings, may end in a different state. These allow a quick
+// test for that condition. entry_shift is 8 for tables subscripted by a full
+// byte value and 6 for space-optimized tables subscripted by only six
+// significant bits in UTF-8 continuation bytes.
+typedef struct {
+  const uint32_t state0;
+  const uint32_t state0_size;
+  const uint32_t total_size;
+  const int max_expand;
+  const int entry_shift;
+  const int bytes_per_entry;
+  const uint32_t losub;
+  const uint32_t hiadd;
+  const uint8_t* state_table;
+  const RemapEntry* remap_base;
+  const uint8_t* remap_string;
+  const uint8_t* fast_state;
+} UTF8StateMachineObj;
+
+typedef UTF8StateMachineObj UTF8ScanObj;
+
+#define X__ (kExitIllegalStructure)
+#define RJ_ (kExitReject)
+#define S1_ (kExitReplace1)
+#define S2_ (kExitReplace2)
+#define S3_ (kExitReplace3)
+#define S21 (kExitReplace21)
+#define S31 (kExitReplace31)
+#define S32 (kExitReplace32)
+#define T1_ (kExitReplaceOffset1)
+#define T2_ (kExitReplaceOffset2)
+#define S11 (kExitReplace1S0)
+#define SP_ (kExitSpecial)
+#define D__ (kExitDoAgain)
+#define RJA (kExitRejectAlt)
+
+//  Entire table has 9 state blocks of 256 entries each
+static const unsigned int utf8acceptnonsurrogates_STATE0 = 0;     // state[0]
+static const unsigned int utf8acceptnonsurrogates_STATE0_SIZE = 256;  // =[1]
+static const unsigned int utf8acceptnonsurrogates_TOTAL_SIZE = 2304;
+static const unsigned int utf8acceptnonsurrogates_MAX_EXPAND_X4 = 0;
+static const unsigned int utf8acceptnonsurrogates_SHIFT = 8;
+static const unsigned int utf8acceptnonsurrogates_BYTES = 1;
+static const unsigned int utf8acceptnonsurrogates_LOSUB = 0x20202020;
+static const unsigned int utf8acceptnonsurrogates_HIADD = 0x00000000;
+
+static const uint8_t utf8acceptnonsurrogates[] = {
+// state[0] 0x000000 Byte 1
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__,   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,   3,   3,   3,   3,   3,   3,   3,    3,   3,   3,   3,   3,   7,   3,   3,
+  4,   5,   5,   5,   6, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+// state[1] 0x000080 Byte 2 of 2
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+// state[2] 0x000000 Byte 2 of 3
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+  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,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+// state[3] 0x001000 Byte 2 of 3
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+  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,
+  1,   1,   1,   1,   1,   1,   1,   1,    1,   1,   1,   1,   1,   1,   1,   1,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+// state[4] 0x000000 Byte 2 of 4
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+  3,   3,   3,   3,   3,   3,   3,   3,    3,   3,   3,   3,   3,   3,   3,   3,
+  3,   3,   3,   3,   3,   3,   3,   3,    3,   3,   3,   3,   3,   3,   3,   3,
+  3,   3,   3,   3,   3,   3,   3,   3,    3,   3,   3,   3,   3,   3,   3,   3,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+// state[5] 0x040000 Byte 2 of 4
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+  3,   3,   3,   3,   3,   3,   3,   3,    3,   3,   3,   3,   3,   3,   3,   3,
+  3,   3,   3,   3,   3,   3,   3,   3,    3,   3,   3,   3,   3,   3,   3,   3,
+  3,   3,   3,   3,   3,   3,   3,   3,    3,   3,   3,   3,   3,   3,   3,   3,
+  3,   3,   3,   3,   3,   3,   3,   3,    3,   3,   3,   3,   3,   3,   3,   3,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+// state[6] 0x100000 Byte 2 of 4
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+  3,   3,   3,   3,   3,   3,   3,   3,    3,   3,   3,   3,   3,   3,   3,   3,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+// state[7] 0x00d000 Byte 2 of 3
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+  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,
+  8,   8,   8,   8,   8,   8,   8,   8,    8,   8,   8,   8,   8,   8,   8,   8,
+  8,   8,   8,   8,   8,   8,   8,   8,    8,   8,   8,   8,   8,   8,   8,   8,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+// state[8] 0x00d800 Byte 3 of 3
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+
+RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,  RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,
+RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,  RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,
+RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,  RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,
+RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,  RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,
+
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+X__, X__, X__, X__, X__, X__, X__, X__,  X__, X__, X__, X__, X__, X__, X__, X__,
+};
+
+// Remap base[0] = (del, add, string_offset)
+static const RemapEntry utf8acceptnonsurrogates_remap_base[] = {
+{0, 0, 0} };
+
+// Remap string[0]
+static const unsigned char utf8acceptnonsurrogates_remap_string[] = {
+0 };
+
+static const unsigned char utf8acceptnonsurrogates_fast[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,
+
+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,
+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,
+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,
+};
+
+static const UTF8ScanObj utf8acceptnonsurrogates_obj = {
+  utf8acceptnonsurrogates_STATE0,
+  utf8acceptnonsurrogates_STATE0_SIZE,
+  utf8acceptnonsurrogates_TOTAL_SIZE,
+  utf8acceptnonsurrogates_MAX_EXPAND_X4,
+  utf8acceptnonsurrogates_SHIFT,
+  utf8acceptnonsurrogates_BYTES,
+  utf8acceptnonsurrogates_LOSUB,
+  utf8acceptnonsurrogates_HIADD,
+  utf8acceptnonsurrogates,
+  utf8acceptnonsurrogates_remap_base,
+  utf8acceptnonsurrogates_remap_string,
+  utf8acceptnonsurrogates_fast
+};
+
+
+#undef X__
+#undef RJ_
+#undef S1_
+#undef S2_
+#undef S3_
+#undef S21
+#undef S31
+#undef S32
+#undef T1_
+#undef T2_
+#undef S11
+#undef SP_
+#undef D__
+#undef RJA
+
+// Return true if current Tbl pointer is within state0 range
+// Note that unsigned compare checks both ends of range simultaneously
+static inline bool InStateZero(const UTF8ScanObj* st, const uint8_t* Tbl) {
+  const uint8_t* Tbl0 = &st->state_table[st->state0];
+  return (static_cast<uint32_t>(Tbl - Tbl0) < st->state0_size);
+}
+
+namespace {
+
+// Scan a UTF-8 string based on state table.
+// Always scan complete UTF-8 characters
+// Set number of bytes scanned. Return reason for exiting
+int UTF8GenericScan(const UTF8ScanObj* st,
+                    const char * str,
+                    int str_length,
+                    int* bytes_consumed) {
+  *bytes_consumed = 0;
+  if (str_length == 0) return kExitOK;
+
+  int eshift = st->entry_shift;
+  const uint8_t* isrc = reinterpret_cast<const uint8_t*>(str);
+  const uint8_t* src = isrc;
+  const uint8_t* srclimit = isrc + str_length;
+  const uint8_t* srclimit8 = str_length < 7 ? isrc : srclimit - 7;
+  const uint8_t* Tbl_0 = &st->state_table[st->state0];
+
+ DoAgain:
+  // Do state-table scan
+  int e = 0;
+  uint8_t c;
+  const uint8_t* Tbl2 = &st->fast_state[0];
+  const uint32_t losub = st->losub;
+  const uint32_t hiadd = st->hiadd;
+  // Check initial few bytes one at a time until 8-byte aligned
+  //----------------------------
+  while ((((uintptr_t)src & 0x07) != 0) &&
+         (src < srclimit) &&
+         Tbl2[src[0]] == 0) {
+    src++;
+  }
+  if (((uintptr_t)src & 0x07) == 0) {
+    // Do fast for groups of 8 identity bytes.
+    // This covers a lot of 7-bit ASCII ~8x faster then the 1-byte loop,
+    // including slowing slightly on cr/lf/ht
+    //----------------------------
+    while (src < srclimit8) {
+      uint32_t s0123 = (reinterpret_cast<const uint32_t *>(src))[0];
+      uint32_t s4567 = (reinterpret_cast<const uint32_t *>(src))[1];
+      src += 8;
+      // This is a fast range check for all bytes in [lowsub..0x80-hiadd)
+      uint32_t temp = (s0123 - losub) | (s0123 + hiadd) |
+                      (s4567 - losub) | (s4567 + hiadd);
+      if ((temp & 0x80808080) != 0) {
+        // We typically end up here on cr/lf/ht; src was incremented
+        int e0123 = (Tbl2[src[-8]] | Tbl2[src[-7]]) |
+                    (Tbl2[src[-6]] | Tbl2[src[-5]]);
+        if (e0123 != 0) {
+          src -= 8;
+          break;
+        }    // Exit on Non-interchange
+        e0123 = (Tbl2[src[-4]] | Tbl2[src[-3]]) |
+                (Tbl2[src[-2]] | Tbl2[src[-1]]);
+        if (e0123 != 0) {
+          src -= 4;
+          break;
+        }    // Exit on Non-interchange
+        // Else OK, go around again
+      }
+    }
+  }
+  //----------------------------
+
+  // Byte-at-a-time scan
+  //----------------------------
+  const uint8_t* Tbl = Tbl_0;
+  while (src < srclimit) {
+    c = *src;
+    e = Tbl[c];
+    src++;
+    if (e >= kExitIllegalStructure) {break;}
+    Tbl = &Tbl_0[e << eshift];
+  }
+  //----------------------------
+
+  // Exit possibilities:
+  //  Some exit code, !state0, back up over last char
+  //  Some exit code, state0, back up one byte exactly
+  //  source consumed, !state0, back up over partial char
+  //  source consumed, state0, exit OK
+  // For illegal byte in state0, avoid backup up over PREVIOUS char
+  // For truncated last char, back up to beginning of it
+
+  if (e >= kExitIllegalStructure) {
+    // Back up over exactly one byte of rejected/illegal UTF-8 character
+    src--;
+    // Back up more if needed
+    if (!InStateZero(st, Tbl)) {
+      do {
+        src--;
+      } while ((src > isrc) && ((src[0] & 0xc0) == 0x80));
+    }
+  } else if (!InStateZero(st, Tbl)) {
+    // Back up over truncated UTF-8 character
+    e = kExitIllegalStructure;
+    do {
+      src--;
+    } while ((src > isrc) && ((src[0] & 0xc0) == 0x80));
+  } else {
+    // Normal termination, source fully consumed
+    e = kExitOK;
+  }
+
+  if (e == kExitDoAgain) {
+    // Loop back up to the fast scan
+    goto DoAgain;
+  }
+
+  *bytes_consumed = src - isrc;
+  return e;
+}
+
+int UTF8GenericScanFastAscii(const UTF8ScanObj* st,
+                    const char * str,
+                    int str_length,
+                    int* bytes_consumed) {
+  *bytes_consumed = 0;
+  if (str_length == 0) return kExitOK;
+
+  const uint8_t* isrc =  reinterpret_cast<const uint8_t*>(str);
+  const uint8_t* src = isrc;
+  const uint8_t* srclimit = isrc + str_length;
+  const uint8_t* srclimit8 = str_length < 7 ? isrc : srclimit - 7;
+  int n;
+  int rest_consumed;
+  int exit_reason;
+  do {
+    // Check initial few bytes one at a time until 8-byte aligned
+    while ((((uintptr_t)src & 0x07) != 0) &&
+           (src < srclimit) && (src[0] < 0x80)) {
+      src++;
+    }
+    if (((uintptr_t)src & 0x07) == 0) {
+      while ((src < srclimit8) &&
+             (((reinterpret_cast<const uint32_t*>(src)[0] |
+                reinterpret_cast<const uint32_t*>(src)[1]) &
+               0x80808080) == 0)) {
+        src += 8;
+      }
+    }
+    while ((src < srclimit) && (src[0] < 0x80)) {
+      src++;
+    }
+    // Run state table on the rest
+    n = src - isrc;
+    exit_reason = UTF8GenericScan(st, str + n, str_length - n, &rest_consumed);
+    src += rest_consumed;
+  } while ( exit_reason == kExitDoAgain );
+
+  *bytes_consumed = src - isrc;
+  return exit_reason;
+}
+
+// Hack:  On some compilers the static tables are initialized at startup.
+//   We can't use them until they are initialized.  However, some Protocol
+//   Buffer parsing happens at static init time and may try to validate
+//   UTF-8 strings.  Since UTF-8 validation is only used for debugging
+//   anyway, we simply always return success if initialization hasn't
+//   occurred yet.
+
+bool module_initialized_ = false;
+
+struct InitDetector {
+  InitDetector() {
+    module_initialized_ = true;
+  }
+};
+InitDetector init_detector;
+
+}  // namespace
+
+bool IsStructurallyValidUTF8(const char* buf, int len) {
+  if (!module_initialized_) return true;
+
+  int bytes_consumed = 0;
+  UTF8GenericScanFastAscii(&utf8acceptnonsurrogates_obj,
+                           buf, len, &bytes_consumed);
+  return (bytes_consumed == len);
+}
+
+int UTF8SpnStructurallyValid(StringPiece str) {
+  if (!module_initialized_) return str.size();
+
+  int bytes_consumed = 0;
+  UTF8GenericScanFastAscii(&utf8acceptnonsurrogates_obj,
+                           str.data(), str.size(), &bytes_consumed);
+  return bytes_consumed;
+}
+
+// Coerce UTF-8 byte string in src_str to be
+// a structurally-valid equal-length string by selectively
+// overwriting illegal bytes with replace_char (typically blank).
+// replace_char must be legal printable 7-bit Ascii 0x20..0x7e.
+// src_str is read-only. If any overwriting is needed, a modified byte string
+// is created in idst, length isrclen.
+//
+// Returns pointer to output buffer, isrc if no changes were made,
+//  or idst if some bytes were changed.
+//
+// Fast case: all is structurally valid and no byte copying is done.
+//
+char* UTF8CoerceToStructurallyValid(StringPiece src_str, char* idst,
+                                    const char replace_char) {
+  const char* isrc = src_str.data();
+  const int len = src_str.length();
+  int n = UTF8SpnStructurallyValid(src_str);
+  if (n == len) {               // Normal case -- all is cool, return
+    return const_cast<char*>(isrc);
+  } else {                      // Unusual case -- copy w/o bad bytes
+    const char* src = isrc;
+    const char* srclimit = isrc + len;
+    char* dst = idst;
+    memmove(dst, src, n);       // Copy initial good chunk
+    src += n;
+    dst += n;
+    while (src < srclimit) {    // src points to bogus byte or is off the end
+      dst[0] = replace_char;                    // replace one bad byte
+      src++;
+      dst++;
+      StringPiece str2(src, srclimit - src);
+      n = UTF8SpnStructurallyValid(str2);       // scan the remainder
+      memmove(dst, src, n);                     // copy next good chunk
+      src += n;
+      dst += n;
+    }
+  }
+  return idst;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/strutil.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/strutil.cpp
new file mode 100644
index 0000000..58e03d0
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/strutil.cpp
@@ -0,0 +1,2487 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// from google3/strings/strutil.cc
+
+#include <google/protobuf/stubs/strutil.h>
+
+#include <errno.h>
+#include <float.h>    // FLT_DIG and DBL_DIG
+#include <limits.h>
+#include <stdio.h>
+#include <cmath>
+#include <iterator>
+#include <limits>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+#ifdef _WIN32
+// MSVC has only _snprintf, not snprintf.
+//
+// MinGW has both snprintf and _snprintf, but they appear to be different
+// functions.  The former is buggy.  When invoked like so:
+//   char buffer[32];
+//   snprintf(buffer, 32, "%.*g\n", FLT_DIG, 1.23e10f);
+// it prints "1.23000e+10".  This is plainly wrong:  %g should never print
+// trailing zeros after the decimal point.  For some reason this bug only
+// occurs with some input values, not all.  In any case, _snprintf does the
+// right thing, so we use it.
+#define snprintf _snprintf
+#endif
+
+namespace google {
+namespace protobuf {
+
+// These are defined as macros on some platforms.  #undef them so that we can
+// redefine them.
+#undef isxdigit
+#undef isprint
+
+// The definitions of these in ctype.h change based on locale.  Since our
+// string manipulation is all in relation to the protocol buffer and C++
+// languages, we always want to use the C locale.  So, we re-define these
+// exactly as we want them.
+inline bool isxdigit(char c) {
+  return ('0' <= c && c <= '9') ||
+         ('a' <= c && c <= 'f') ||
+         ('A' <= c && c <= 'F');
+}
+
+inline bool isprint(char c) {
+  return c >= 0x20 && c <= 0x7E;
+}
+
+// ----------------------------------------------------------------------
+// ReplaceCharacters
+//    Replaces any occurrence of the character 'remove' (or the characters
+//    in 'remove') with the character 'replacewith'.
+// ----------------------------------------------------------------------
+void ReplaceCharacters(std::string *s, const char *remove, char replacewith) {
+  const char *str_start = s->c_str();
+  const char *str = str_start;
+  for (str = strpbrk(str, remove);
+       str != nullptr;
+       str = strpbrk(str + 1, remove)) {
+    (*s)[str - str_start] = replacewith;
+  }
+}
+
+void StripWhitespace(std::string *str) {
+  int str_length = str->length();
+
+  // Strip off leading whitespace.
+  int first = 0;
+  while (first < str_length && ascii_isspace(str->at(first))) {
+    ++first;
+  }
+  // If entire string is white space.
+  if (first == str_length) {
+    str->clear();
+    return;
+  }
+  if (first > 0) {
+    str->erase(0, first);
+    str_length -= first;
+  }
+
+  // Strip off trailing whitespace.
+  int last = str_length - 1;
+  while (last >= 0 && ascii_isspace(str->at(last))) {
+    --last;
+  }
+  if (last != (str_length - 1) && last >= 0) {
+    str->erase(last + 1, std::string::npos);
+  }
+}
+
+// ----------------------------------------------------------------------
+// StringReplace()
+//    Replace the "old" pattern with the "new" pattern in a string,
+//    and append the result to "res".  If replace_all is false,
+//    it only replaces the first instance of "old."
+// ----------------------------------------------------------------------
+
+void StringReplace(const std::string &s, const std::string &oldsub,
+                   const std::string &newsub, bool replace_all,
+                   std::string *res) {
+  if (oldsub.empty()) {
+    res->append(s);  // if empty, append the given string.
+    return;
+  }
+
+  std::string::size_type start_pos = 0;
+  std::string::size_type pos;
+  do {
+    pos = s.find(oldsub, start_pos);
+    if (pos == std::string::npos) {
+      break;
+    }
+    res->append(s, start_pos, pos - start_pos);
+    res->append(newsub);
+    start_pos = pos + oldsub.size();  // start searching again after the "old"
+  } while (replace_all);
+  res->append(s, start_pos, s.length() - start_pos);
+}
+
+// ----------------------------------------------------------------------
+// StringReplace()
+//    Give me a string and two patterns "old" and "new", and I replace
+//    the first instance of "old" in the string with "new", if it
+//    exists.  If "global" is true; call this repeatedly until it
+//    fails.  RETURN a new string, regardless of whether the replacement
+//    happened or not.
+// ----------------------------------------------------------------------
+
+std::string StringReplace(const std::string &s, const std::string &oldsub,
+                          const std::string &newsub, bool replace_all) {
+  std::string ret;
+  StringReplace(s, oldsub, newsub, replace_all, &ret);
+  return ret;
+}
+
+// ----------------------------------------------------------------------
+// SplitStringUsing()
+//    Split a string using a character delimiter. Append the components
+//    to 'result'.
+//
+// Note: For multi-character delimiters, this routine will split on *ANY* of
+// the characters in the string, not the entire string as a single delimiter.
+// ----------------------------------------------------------------------
+template <typename ITR>
+static inline void SplitStringToIteratorUsing(StringPiece full,
+                                              const char *delim, ITR &result) {
+  // Optimize the common case where delim is a single character.
+  if (delim[0] != '\0' && delim[1] == '\0') {
+    char c = delim[0];
+    const char* p = full.data();
+    const char* end = p + full.size();
+    while (p != end) {
+      if (*p == c) {
+        ++p;
+      } else {
+        const char* start = p;
+        while (++p != end && *p != c);
+        *result++ = std::string(start, p - start);
+      }
+    }
+    return;
+  }
+
+  std::string::size_type begin_index, end_index;
+  begin_index = full.find_first_not_of(delim);
+  while (begin_index != std::string::npos) {
+    end_index = full.find_first_of(delim, begin_index);
+    if (end_index == std::string::npos) {
+      *result++ = std::string(full.substr(begin_index));
+      return;
+    }
+    *result++ =
+        std::string(full.substr(begin_index, (end_index - begin_index)));
+    begin_index = full.find_first_not_of(delim, end_index);
+  }
+}
+
+void SplitStringUsing(StringPiece full, const char *delim,
+                      std::vector<std::string> *result) {
+  std::back_insert_iterator<std::vector<std::string> > it(*result);
+  SplitStringToIteratorUsing(full, delim, it);
+}
+
+// Split a string using a character delimiter. Append the components
+// to 'result'.  If there are consecutive delimiters, this function
+// will return corresponding empty strings. The string is split into
+// at most the specified number of pieces greedily. This means that the
+// last piece may possibly be split further. To split into as many pieces
+// as possible, specify 0 as the number of pieces.
+//
+// If "full" is the empty string, yields an empty string as the only value.
+//
+// If "pieces" is negative for some reason, it returns the whole string
+// ----------------------------------------------------------------------
+template <typename ITR>
+static inline void SplitStringToIteratorAllowEmpty(StringPiece full,
+                                                   const char *delim,
+                                                   int pieces, ITR &result) {
+  std::string::size_type begin_index, end_index;
+  begin_index = 0;
+
+  for (int i = 0; (i < pieces-1) || (pieces == 0); i++) {
+    end_index = full.find_first_of(delim, begin_index);
+    if (end_index == std::string::npos) {
+      *result++ = std::string(full.substr(begin_index));
+      return;
+    }
+    *result++ =
+        std::string(full.substr(begin_index, (end_index - begin_index)));
+    begin_index = end_index + 1;
+  }
+  *result++ = std::string(full.substr(begin_index));
+}
+
+void SplitStringAllowEmpty(StringPiece full, const char *delim,
+                           std::vector<std::string> *result) {
+  std::back_insert_iterator<std::vector<std::string> > it(*result);
+  SplitStringToIteratorAllowEmpty(full, delim, 0, it);
+}
+
+// ----------------------------------------------------------------------
+// JoinStrings()
+//    This merges a vector of string components with delim inserted
+//    as separaters between components.
+//
+// ----------------------------------------------------------------------
+template <class ITERATOR>
+static void JoinStringsIterator(const ITERATOR &start, const ITERATOR &end,
+                                const char *delim, std::string *result) {
+  GOOGLE_CHECK(result != nullptr);
+  result->clear();
+  int delim_length = strlen(delim);
+
+  // Precompute resulting length so we can reserve() memory in one shot.
+  int length = 0;
+  for (ITERATOR iter = start; iter != end; ++iter) {
+    if (iter != start) {
+      length += delim_length;
+    }
+    length += iter->size();
+  }
+  result->reserve(length);
+
+  // Now combine everything.
+  for (ITERATOR iter = start; iter != end; ++iter) {
+    if (iter != start) {
+      result->append(delim, delim_length);
+    }
+    result->append(iter->data(), iter->size());
+  }
+}
+
+void JoinStrings(const std::vector<std::string> &components, const char *delim,
+                 std::string *result) {
+  JoinStringsIterator(components.begin(), components.end(), delim, result);
+}
+
+// ----------------------------------------------------------------------
+// UnescapeCEscapeSequences()
+//    This does all the unescaping that C does: \ooo, \r, \n, etc
+//    Returns length of resulting string.
+//    The implementation of \x parses any positive number of hex digits,
+//    but it is an error if the value requires more than 8 bits, and the
+//    result is truncated to 8 bits.
+//
+//    The second call stores its errors in a supplied string vector.
+//    If the string vector pointer is nullptr, it reports the errors with LOG().
+// ----------------------------------------------------------------------
+
+#define IS_OCTAL_DIGIT(c) (((c) >= '0') && ((c) <= '7'))
+
+// Protocol buffers doesn't ever care about errors, but I don't want to remove
+// the code.
+#define LOG_STRING(LEVEL, VECTOR) GOOGLE_LOG_IF(LEVEL, false)
+
+int UnescapeCEscapeSequences(const char* source, char* dest) {
+  return UnescapeCEscapeSequences(source, dest, nullptr);
+}
+
+int UnescapeCEscapeSequences(const char *source, char *dest,
+                             std::vector<std::string> *errors) {
+  GOOGLE_DCHECK(errors == nullptr) << "Error reporting not implemented.";
+
+  char* d = dest;
+  const char* p = source;
+
+  // Small optimization for case where source = dest and there's no escaping
+  while ( p == d && *p != '\0' && *p != '\\' )
+    p++, d++;
+
+  while (*p != '\0') {
+    if (*p != '\\') {
+      *d++ = *p++;
+    } else {
+      switch ( *++p ) {                    // skip past the '\\'
+        case '\0':
+          LOG_STRING(ERROR, errors) << "String cannot end with \\";
+          *d = '\0';
+          return d - dest;   // we're done with p
+        case 'a':  *d++ = '\a';  break;
+        case 'b':  *d++ = '\b';  break;
+        case 'f':  *d++ = '\f';  break;
+        case 'n':  *d++ = '\n';  break;
+        case 'r':  *d++ = '\r';  break;
+        case 't':  *d++ = '\t';  break;
+        case 'v':  *d++ = '\v';  break;
+        case '\\': *d++ = '\\';  break;
+        case '?':  *d++ = '\?';  break;    // \?  Who knew?
+        case '\'': *d++ = '\'';  break;
+        case '"':  *d++ = '\"';  break;
+        case '0': case '1': case '2': case '3':  // octal digit: 1 to 3 digits
+        case '4': case '5': case '6': case '7': {
+          char ch = *p - '0';
+          if ( IS_OCTAL_DIGIT(p[1]) )
+            ch = ch * 8 + *++p - '0';
+          if ( IS_OCTAL_DIGIT(p[1]) )      // safe (and easy) to do this twice
+            ch = ch * 8 + *++p - '0';      // now points at last digit
+          *d++ = ch;
+          break;
+        }
+        case 'x': case 'X': {
+          if (!isxdigit(p[1])) {
+            if (p[1] == '\0') {
+              LOG_STRING(ERROR, errors) << "String cannot end with \\x";
+            } else {
+              LOG_STRING(ERROR, errors) <<
+                "\\x cannot be followed by non-hex digit: \\" << *p << p[1];
+            }
+            break;
+          }
+          unsigned int ch = 0;
+          const char *hex_start = p;
+          while (isxdigit(p[1]))  // arbitrarily many hex digits
+            ch = (ch << 4) + hex_digit_to_int(*++p);
+          if (ch > 0xFF)
+            LOG_STRING(ERROR, errors)
+                << "Value of "
+                << "\\" << std::string(hex_start, p + 1 - hex_start)
+                << " exceeds 8 bits";
+          *d++ = ch;
+          break;
+        }
+#if 0  // TODO(kenton):  Support \u and \U?  Requires runetochar().
+        case 'u': {
+          // \uhhhh => convert 4 hex digits to UTF-8
+          char32 rune = 0;
+          const char *hex_start = p;
+          for (int i = 0; i < 4; ++i) {
+            if (isxdigit(p[1])) {  // Look one char ahead.
+              rune = (rune << 4) + hex_digit_to_int(*++p);  // Advance p.
+            } else {
+              LOG_STRING(ERROR, errors)
+                << "\\u must be followed by 4 hex digits: \\"
+                <<  std::string(hex_start, p+1-hex_start);
+              break;
+            }
+          }
+          d += runetochar(d, &rune);
+          break;
+        }
+        case 'U': {
+          // \Uhhhhhhhh => convert 8 hex digits to UTF-8
+          char32 rune = 0;
+          const char *hex_start = p;
+          for (int i = 0; i < 8; ++i) {
+            if (isxdigit(p[1])) {  // Look one char ahead.
+              // Don't change rune until we're sure this
+              // is within the Unicode limit, but do advance p.
+              char32 newrune = (rune << 4) + hex_digit_to_int(*++p);
+              if (newrune > 0x10FFFF) {
+                LOG_STRING(ERROR, errors)
+                  << "Value of \\"
+                  << std::string(hex_start, p + 1 - hex_start)
+                  << " exceeds Unicode limit (0x10FFFF)";
+                break;
+              } else {
+                rune = newrune;
+              }
+            } else {
+              LOG_STRING(ERROR, errors)
+                << "\\U must be followed by 8 hex digits: \\"
+                <<  std::string(hex_start, p+1-hex_start);
+              break;
+            }
+          }
+          d += runetochar(d, &rune);
+          break;
+        }
+#endif
+        default:
+          LOG_STRING(ERROR, errors) << "Unknown escape sequence: \\" << *p;
+      }
+      p++;                                 // read past letter we escaped
+    }
+  }
+  *d = '\0';
+  return d - dest;
+}
+
+// ----------------------------------------------------------------------
+// UnescapeCEscapeString()
+//    This does the same thing as UnescapeCEscapeSequences, but creates
+//    a new string. The caller does not need to worry about allocating
+//    a dest buffer. This should be used for non performance critical
+//    tasks such as printing debug messages. It is safe for src and dest
+//    to be the same.
+//
+//    The second call stores its errors in a supplied string vector.
+//    If the string vector pointer is nullptr, it reports the errors with LOG().
+//
+//    In the first and second calls, the length of dest is returned. In the
+//    the third call, the new string is returned.
+// ----------------------------------------------------------------------
+int UnescapeCEscapeString(const std::string &src, std::string *dest) {
+  return UnescapeCEscapeString(src, dest, nullptr);
+}
+
+int UnescapeCEscapeString(const std::string &src, std::string *dest,
+                          std::vector<std::string> *errors) {
+  std::unique_ptr<char[]> unescaped(new char[src.size() + 1]);
+  int len = UnescapeCEscapeSequences(src.c_str(), unescaped.get(), errors);
+  GOOGLE_CHECK(dest);
+  dest->assign(unescaped.get(), len);
+  return len;
+}
+
+std::string UnescapeCEscapeString(const std::string &src) {
+  std::unique_ptr<char[]> unescaped(new char[src.size() + 1]);
+  int len = UnescapeCEscapeSequences(src.c_str(), unescaped.get(), nullptr);
+  return std::string(unescaped.get(), len);
+}
+
+// ----------------------------------------------------------------------
+// CEscapeString()
+// CHexEscapeString()
+//    Copies 'src' to 'dest', escaping dangerous characters using
+//    C-style escape sequences. This is very useful for preparing query
+//    flags. 'src' and 'dest' should not overlap. The 'Hex' version uses
+//    hexadecimal rather than octal sequences.
+//    Returns the number of bytes written to 'dest' (not including the \0)
+//    or -1 if there was insufficient space.
+//
+//    Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped.
+// ----------------------------------------------------------------------
+int CEscapeInternal(const char* src, int src_len, char* dest,
+                    int dest_len, bool use_hex, bool utf8_safe) {
+  const char* src_end = src + src_len;
+  int used = 0;
+  bool last_hex_escape = false; // true if last output char was \xNN
+
+  for (; src < src_end; src++) {
+    if (dest_len - used < 2)   // Need space for two letter escape
+      return -1;
+
+    bool is_hex_escape = false;
+    switch (*src) {
+      case '\n': dest[used++] = '\\'; dest[used++] = 'n';  break;
+      case '\r': dest[used++] = '\\'; dest[used++] = 'r';  break;
+      case '\t': dest[used++] = '\\'; dest[used++] = 't';  break;
+      case '\"': dest[used++] = '\\'; dest[used++] = '\"'; break;
+      case '\'': dest[used++] = '\\'; dest[used++] = '\''; break;
+      case '\\': dest[used++] = '\\'; dest[used++] = '\\'; break;
+      default:
+        // Note that if we emit \xNN and the src character after that is a hex
+        // digit then that digit must be escaped too to prevent it being
+        // interpreted as part of the character code by C.
+        if ((!utf8_safe || static_cast<uint8_t>(*src) < 0x80) &&
+            (!isprint(*src) ||
+             (last_hex_escape && isxdigit(*src)))) {
+          if (dest_len - used < 4) // need space for 4 letter escape
+            return -1;
+          dest[used++] = '\\';
+          if (use_hex) {
+            constexpr char hexdigits[] = "0123456789abcdef";
+            dest[used++] = 'x';
+            dest[used++] = hexdigits[(static_cast<uint8_t>(*src) >> 4) & 0xf];
+            dest[used++] = hexdigits[static_cast<uint8_t>(*src) & 0xf];
+          } else {
+            dest[used++] = '0' + ((static_cast<uint8_t>(*src) >> 6) & 0x3);
+            dest[used++] = '0' + ((static_cast<uint8_t>(*src) >> 3) & 0x7);
+            dest[used++] = '0' + (static_cast<uint8_t>(*src) & 0x7);
+          }
+          is_hex_escape = use_hex;
+        } else {
+          dest[used++] = *src; break;
+        }
+    }
+    last_hex_escape = is_hex_escape;
+  }
+
+  if (dest_len - used < 1)   // make sure that there is room for \0
+    return -1;
+
+  dest[used] = '\0';   // doesn't count towards return value though
+  return used;
+}
+
+// Calculates the length of the C-style escaped version of 'src'.
+// Assumes that non-printable characters are escaped using octal sequences, and
+// that UTF-8 bytes are not handled specially.
+static inline size_t CEscapedLength(StringPiece src) {
+  static char c_escaped_len[256] = {
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 2, 4, 4,  // \t, \n, \r
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+    1, 1, 2, 1, 1, 1, 1, 2, 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'..'9'
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 'A'..'O'
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,  // 'P'..'Z', '\'
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 'a'..'o'
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,  // 'p'..'z', DEL
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+  };
+
+  size_t escaped_len = 0;
+  for (StringPiece::size_type i = 0; i < src.size(); ++i) {
+    unsigned char c = static_cast<unsigned char>(src[i]);
+    escaped_len += c_escaped_len[c];
+  }
+  return escaped_len;
+}
+
+// ----------------------------------------------------------------------
+// Escapes 'src' using C-style escape sequences, and appends the escaped string
+// to 'dest'. This version is faster than calling CEscapeInternal as it computes
+// the required space using a lookup table, and also does not do any special
+// handling for Hex or UTF-8 characters.
+// ----------------------------------------------------------------------
+void CEscapeAndAppend(StringPiece src, std::string *dest) {
+  size_t escaped_len = CEscapedLength(src);
+  if (escaped_len == src.size()) {
+    dest->append(src.data(), src.size());
+    return;
+  }
+
+  size_t cur_dest_len = dest->size();
+  dest->resize(cur_dest_len + escaped_len);
+  char* append_ptr = &(*dest)[cur_dest_len];
+
+  for (StringPiece::size_type i = 0; i < src.size(); ++i) {
+    unsigned char c = static_cast<unsigned char>(src[i]);
+    switch (c) {
+      case '\n': *append_ptr++ = '\\'; *append_ptr++ = 'n'; break;
+      case '\r': *append_ptr++ = '\\'; *append_ptr++ = 'r'; break;
+      case '\t': *append_ptr++ = '\\'; *append_ptr++ = 't'; break;
+      case '\"': *append_ptr++ = '\\'; *append_ptr++ = '\"'; break;
+      case '\'': *append_ptr++ = '\\'; *append_ptr++ = '\''; break;
+      case '\\': *append_ptr++ = '\\'; *append_ptr++ = '\\'; break;
+      default:
+        if (!isprint(c)) {
+          *append_ptr++ = '\\';
+          *append_ptr++ = '0' + c / 64;
+          *append_ptr++ = '0' + (c % 64) / 8;
+          *append_ptr++ = '0' + c % 8;
+        } else {
+          *append_ptr++ = c;
+        }
+        break;
+    }
+  }
+}
+
+std::string CEscape(const std::string &src) {
+  std::string dest;
+  CEscapeAndAppend(src, &dest);
+  return dest;
+}
+
+namespace strings {
+
+std::string Utf8SafeCEscape(const std::string &src) {
+  const int dest_length = src.size() * 4 + 1; // Maximum possible expansion
+  std::unique_ptr<char[]> dest(new char[dest_length]);
+  const int len = CEscapeInternal(src.data(), src.size(),
+                                  dest.get(), dest_length, false, true);
+  GOOGLE_DCHECK_GE(len, 0);
+  return std::string(dest.get(), len);
+}
+
+std::string CHexEscape(const std::string &src) {
+  const int dest_length = src.size() * 4 + 1; // Maximum possible expansion
+  std::unique_ptr<char[]> dest(new char[dest_length]);
+  const int len = CEscapeInternal(src.data(), src.size(),
+                                  dest.get(), dest_length, true, false);
+  GOOGLE_DCHECK_GE(len, 0);
+  return std::string(dest.get(), len);
+}
+
+}  // namespace strings
+
+// ----------------------------------------------------------------------
+// strto32_adaptor()
+// strtou32_adaptor()
+//    Implementation of strto[u]l replacements that have identical
+//    overflow and underflow characteristics for both ILP-32 and LP-64
+//    platforms, including errno preservation in error-free calls.
+// ----------------------------------------------------------------------
+
+int32_t strto32_adaptor(const char *nptr, char **endptr, int base) {
+  const int saved_errno = errno;
+  errno = 0;
+  const long result = strtol(nptr, endptr, base);
+  if (errno == ERANGE && result == LONG_MIN) {
+    return std::numeric_limits<int32_t>::min();
+  } else if (errno == ERANGE && result == LONG_MAX) {
+    return std::numeric_limits<int32_t>::max();
+  } else if (errno == 0 && result < std::numeric_limits<int32_t>::min()) {
+    errno = ERANGE;
+    return std::numeric_limits<int32_t>::min();
+  } else if (errno == 0 && result > std::numeric_limits<int32_t>::max()) {
+    errno = ERANGE;
+    return std::numeric_limits<int32_t>::max();
+  }
+  if (errno == 0)
+    errno = saved_errno;
+  return static_cast<int32_t>(result);
+}
+
+uint32_t strtou32_adaptor(const char *nptr, char **endptr, int base) {
+  const int saved_errno = errno;
+  errno = 0;
+  const unsigned long result = strtoul(nptr, endptr, base);
+  if (errno == ERANGE && result == ULONG_MAX) {
+    return std::numeric_limits<uint32_t>::max();
+  } else if (errno == 0 && result > std::numeric_limits<uint32_t>::max()) {
+    errno = ERANGE;
+    return std::numeric_limits<uint32_t>::max();
+  }
+  if (errno == 0)
+    errno = saved_errno;
+  return static_cast<uint32_t>(result);
+}
+
+inline bool safe_parse_sign(std::string *text /*inout*/,
+                            bool *negative_ptr /*output*/) {
+  const char* start = text->data();
+  const char* end = start + text->size();
+
+  // Consume whitespace.
+  while (start < end && (start[0] == ' ')) {
+    ++start;
+  }
+  while (start < end && (end[-1] == ' ')) {
+    --end;
+  }
+  if (start >= end) {
+    return false;
+  }
+
+  // Consume sign.
+  *negative_ptr = (start[0] == '-');
+  if (*negative_ptr || start[0] == '+') {
+    ++start;
+    if (start >= end) {
+      return false;
+    }
+  }
+  *text = text->substr(start - text->data(), end - start);
+  return true;
+}
+
+template <typename IntType>
+bool safe_parse_positive_int(std::string text, IntType *value_p) {
+  int base = 10;
+  IntType value = 0;
+  const IntType vmax = std::numeric_limits<IntType>::max();
+  assert(vmax > 0);
+  assert(static_cast<int>(vmax) >= base);
+  const IntType vmax_over_base = vmax / base;
+  const char* start = text.data();
+  const char* end = start + text.size();
+  // loop over digits
+  for (; start < end; ++start) {
+    unsigned char c = static_cast<unsigned char>(start[0]);
+    int digit = c - '0';
+    if (digit >= base || digit < 0) {
+      *value_p = value;
+      return false;
+    }
+    if (value > vmax_over_base) {
+      *value_p = vmax;
+      return false;
+    }
+    value *= base;
+    if (value > vmax - digit) {
+      *value_p = vmax;
+      return false;
+    }
+    value += digit;
+  }
+  *value_p = value;
+  return true;
+}
+
+template <typename IntType>
+bool safe_parse_negative_int(const std::string &text, IntType *value_p) {
+  int base = 10;
+  IntType value = 0;
+  const IntType vmin = std::numeric_limits<IntType>::min();
+  assert(vmin < 0);
+  assert(vmin <= 0 - base);
+  IntType vmin_over_base = vmin / base;
+  // 2003 c++ standard [expr.mul]
+  // "... the sign of the remainder is implementation-defined."
+  // Although (vmin/base)*base + vmin%base is always vmin.
+  // 2011 c++ standard tightens the spec but we cannot rely on it.
+  if (vmin % base > 0) {
+    vmin_over_base += 1;
+  }
+  const char* start = text.data();
+  const char* end = start + text.size();
+  // loop over digits
+  for (; start < end; ++start) {
+    unsigned char c = static_cast<unsigned char>(start[0]);
+    int digit = c - '0';
+    if (digit >= base || digit < 0) {
+      *value_p = value;
+      return false;
+    }
+    if (value < vmin_over_base) {
+      *value_p = vmin;
+      return false;
+    }
+    value *= base;
+    if (value < vmin + digit) {
+      *value_p = vmin;
+      return false;
+    }
+    value -= digit;
+  }
+  *value_p = value;
+  return true;
+}
+
+template <typename IntType>
+bool safe_int_internal(std::string text, IntType *value_p) {
+  *value_p = 0;
+  bool negative;
+  if (!safe_parse_sign(&text, &negative)) {
+    return false;
+  }
+  if (!negative) {
+    return safe_parse_positive_int(text, value_p);
+  } else {
+    return safe_parse_negative_int(text, value_p);
+  }
+}
+
+template <typename IntType>
+bool safe_uint_internal(std::string text, IntType *value_p) {
+  *value_p = 0;
+  bool negative;
+  if (!safe_parse_sign(&text, &negative) || negative) {
+    return false;
+  }
+  return safe_parse_positive_int(text, value_p);
+}
+
+// ----------------------------------------------------------------------
+// FastIntToBuffer()
+// FastInt64ToBuffer()
+// FastHexToBuffer()
+// FastHex64ToBuffer()
+// FastHex32ToBuffer()
+// ----------------------------------------------------------------------
+
+// Offset into buffer where FastInt64ToBuffer places the end of string
+// null character.  Also used by FastInt64ToBufferLeft.
+static const int kFastInt64ToBufferOffset = 21;
+
+char *FastInt64ToBuffer(int64_t i, char* buffer) {
+  // We could collapse the positive and negative sections, but that
+  // would be slightly slower for positive numbers...
+  // 22 bytes is enough to store -2**64, -18446744073709551616.
+  char* p = buffer + kFastInt64ToBufferOffset;
+  *p-- = '\0';
+  if (i >= 0) {
+    do {
+      *p-- = '0' + i % 10;
+      i /= 10;
+    } while (i > 0);
+    return p + 1;
+  } else {
+    // On different platforms, % and / have different behaviors for
+    // negative numbers, so we need to jump through hoops to make sure
+    // we don't divide negative numbers.
+    if (i > -10) {
+      i = -i;
+      *p-- = '0' + i;
+      *p = '-';
+      return p;
+    } else {
+      // Make sure we aren't at MIN_INT, in which case we can't say i = -i
+      i = i + 10;
+      i = -i;
+      *p-- = '0' + i % 10;
+      // Undo what we did a moment ago
+      i = i / 10 + 1;
+      do {
+        *p-- = '0' + i % 10;
+        i /= 10;
+      } while (i > 0);
+      *p = '-';
+      return p;
+    }
+  }
+}
+
+// Offset into buffer where FastInt32ToBuffer places the end of string
+// null character.  Also used by FastInt32ToBufferLeft
+static const int kFastInt32ToBufferOffset = 11;
+
+// Yes, this is a duplicate of FastInt64ToBuffer.  But, we need this for the
+// compiler to generate 32 bit arithmetic instructions.  It's much faster, at
+// least with 32 bit binaries.
+char *FastInt32ToBuffer(int32_t i, char* buffer) {
+  // We could collapse the positive and negative sections, but that
+  // would be slightly slower for positive numbers...
+  // 12 bytes is enough to store -2**32, -4294967296.
+  char* p = buffer + kFastInt32ToBufferOffset;
+  *p-- = '\0';
+  if (i >= 0) {
+    do {
+      *p-- = '0' + i % 10;
+      i /= 10;
+    } while (i > 0);
+    return p + 1;
+  } else {
+    // On different platforms, % and / have different behaviors for
+    // negative numbers, so we need to jump through hoops to make sure
+    // we don't divide negative numbers.
+    if (i > -10) {
+      i = -i;
+      *p-- = '0' + i;
+      *p = '-';
+      return p;
+    } else {
+      // Make sure we aren't at MIN_INT, in which case we can't say i = -i
+      i = i + 10;
+      i = -i;
+      *p-- = '0' + i % 10;
+      // Undo what we did a moment ago
+      i = i / 10 + 1;
+      do {
+        *p-- = '0' + i % 10;
+        i /= 10;
+      } while (i > 0);
+      *p = '-';
+      return p;
+    }
+  }
+}
+
+char *FastHexToBuffer(int i, char* buffer) {
+  GOOGLE_CHECK(i >= 0) << "FastHexToBuffer() wants non-negative integers, not " << i;
+
+  static const char *hexdigits = "0123456789abcdef";
+  char *p = buffer + 21;
+  *p-- = '\0';
+  do {
+    *p-- = hexdigits[i & 15];   // mod by 16
+    i >>= 4;                    // divide by 16
+  } while (i > 0);
+  return p + 1;
+}
+
+char *InternalFastHexToBuffer(uint64_t value, char* buffer, int num_byte) {
+  static const char *hexdigits = "0123456789abcdef";
+  buffer[num_byte] = '\0';
+  for (int i = num_byte - 1; i >= 0; i--) {
+#ifdef _M_X64
+    // MSVC x64 platform has a bug optimizing the uint32(value) in the #else
+    // block. Given that the uint32 cast was to improve performance on 32-bit
+    // platforms, we use 64-bit '&' directly.
+    buffer[i] = hexdigits[value & 0xf];
+#else
+    buffer[i] = hexdigits[uint32_t(value) & 0xf];
+#endif
+    value >>= 4;
+  }
+  return buffer;
+}
+
+char *FastHex64ToBuffer(uint64_t value, char* buffer) {
+  return InternalFastHexToBuffer(value, buffer, 16);
+}
+
+char *FastHex32ToBuffer(uint32_t value, char* buffer) {
+  return InternalFastHexToBuffer(value, buffer, 8);
+}
+
+// ----------------------------------------------------------------------
+// FastInt32ToBufferLeft()
+// FastUInt32ToBufferLeft()
+// FastInt64ToBufferLeft()
+// FastUInt64ToBufferLeft()
+//
+// Like the Fast*ToBuffer() functions above, these are intended for speed.
+// Unlike the Fast*ToBuffer() functions, however, these functions write
+// their output to the beginning of the buffer (hence the name, as the
+// output is left-aligned).  The caller is responsible for ensuring that
+// the buffer has enough space to hold the output.
+//
+// Returns a pointer to the end of the string (i.e. the null character
+// terminating the string).
+// ----------------------------------------------------------------------
+
+static const char two_ASCII_digits[100][2] = {
+  {'0','0'}, {'0','1'}, {'0','2'}, {'0','3'}, {'0','4'},
+  {'0','5'}, {'0','6'}, {'0','7'}, {'0','8'}, {'0','9'},
+  {'1','0'}, {'1','1'}, {'1','2'}, {'1','3'}, {'1','4'},
+  {'1','5'}, {'1','6'}, {'1','7'}, {'1','8'}, {'1','9'},
+  {'2','0'}, {'2','1'}, {'2','2'}, {'2','3'}, {'2','4'},
+  {'2','5'}, {'2','6'}, {'2','7'}, {'2','8'}, {'2','9'},
+  {'3','0'}, {'3','1'}, {'3','2'}, {'3','3'}, {'3','4'},
+  {'3','5'}, {'3','6'}, {'3','7'}, {'3','8'}, {'3','9'},
+  {'4','0'}, {'4','1'}, {'4','2'}, {'4','3'}, {'4','4'},
+  {'4','5'}, {'4','6'}, {'4','7'}, {'4','8'}, {'4','9'},
+  {'5','0'}, {'5','1'}, {'5','2'}, {'5','3'}, {'5','4'},
+  {'5','5'}, {'5','6'}, {'5','7'}, {'5','8'}, {'5','9'},
+  {'6','0'}, {'6','1'}, {'6','2'}, {'6','3'}, {'6','4'},
+  {'6','5'}, {'6','6'}, {'6','7'}, {'6','8'}, {'6','9'},
+  {'7','0'}, {'7','1'}, {'7','2'}, {'7','3'}, {'7','4'},
+  {'7','5'}, {'7','6'}, {'7','7'}, {'7','8'}, {'7','9'},
+  {'8','0'}, {'8','1'}, {'8','2'}, {'8','3'}, {'8','4'},
+  {'8','5'}, {'8','6'}, {'8','7'}, {'8','8'}, {'8','9'},
+  {'9','0'}, {'9','1'}, {'9','2'}, {'9','3'}, {'9','4'},
+  {'9','5'}, {'9','6'}, {'9','7'}, {'9','8'}, {'9','9'}
+};
+
+char* FastUInt32ToBufferLeft(uint32_t u, char* buffer) {
+  uint32_t digits;
+  const char *ASCII_digits = nullptr;
+  // The idea of this implementation is to trim the number of divides to as few
+  // as possible by using multiplication and subtraction rather than mod (%),
+  // and by outputting two digits at a time rather than one.
+  // The huge-number case is first, in the hopes that the compiler will output
+  // that case in one branch-free block of code, and only output conditional
+  // branches into it from below.
+  if (u >= 1000000000) {  // >= 1,000,000,000
+    digits = u / 100000000;  // 100,000,000
+    ASCII_digits = two_ASCII_digits[digits];
+    buffer[0] = ASCII_digits[0];
+    buffer[1] = ASCII_digits[1];
+    buffer += 2;
+sublt100_000_000:
+    u -= digits * 100000000;  // 100,000,000
+lt100_000_000:
+    digits = u / 1000000;  // 1,000,000
+    ASCII_digits = two_ASCII_digits[digits];
+    buffer[0] = ASCII_digits[0];
+    buffer[1] = ASCII_digits[1];
+    buffer += 2;
+sublt1_000_000:
+    u -= digits * 1000000;  // 1,000,000
+lt1_000_000:
+    digits = u / 10000;  // 10,000
+    ASCII_digits = two_ASCII_digits[digits];
+    buffer[0] = ASCII_digits[0];
+    buffer[1] = ASCII_digits[1];
+    buffer += 2;
+sublt10_000:
+    u -= digits * 10000;  // 10,000
+lt10_000:
+    digits = u / 100;
+    ASCII_digits = two_ASCII_digits[digits];
+    buffer[0] = ASCII_digits[0];
+    buffer[1] = ASCII_digits[1];
+    buffer += 2;
+sublt100:
+    u -= digits * 100;
+lt100:
+    digits = u;
+    ASCII_digits = two_ASCII_digits[digits];
+    buffer[0] = ASCII_digits[0];
+    buffer[1] = ASCII_digits[1];
+    buffer += 2;
+done:
+    *buffer = 0;
+    return buffer;
+  }
+
+  if (u < 100) {
+    digits = u;
+    if (u >= 10) goto lt100;
+    *buffer++ = '0' + digits;
+    goto done;
+  }
+  if (u  <  10000) {   // 10,000
+    if (u >= 1000) goto lt10_000;
+    digits = u / 100;
+    *buffer++ = '0' + digits;
+    goto sublt100;
+  }
+  if (u  <  1000000) {   // 1,000,000
+    if (u >= 100000) goto lt1_000_000;
+    digits = u / 10000;  //    10,000
+    *buffer++ = '0' + digits;
+    goto sublt10_000;
+  }
+  if (u  <  100000000) {   // 100,000,000
+    if (u >= 10000000) goto lt100_000_000;
+    digits = u / 1000000;  //   1,000,000
+    *buffer++ = '0' + digits;
+    goto sublt1_000_000;
+  }
+  // we already know that u < 1,000,000,000
+  digits = u / 100000000;   // 100,000,000
+  *buffer++ = '0' + digits;
+  goto sublt100_000_000;
+}
+
+char* FastInt32ToBufferLeft(int32_t i, char* buffer) {
+  uint32_t u = 0;
+  if (i < 0) {
+    *buffer++ = '-';
+    u -= i;
+  } else {
+    u = i;
+  }
+  return FastUInt32ToBufferLeft(u, buffer);
+}
+
+char* FastUInt64ToBufferLeft(uint64_t u64, char* buffer) {
+  int digits;
+  const char *ASCII_digits = nullptr;
+
+  uint32_t u = static_cast<uint32_t>(u64);
+  if (u == u64) return FastUInt32ToBufferLeft(u, buffer);
+
+  uint64_t top_11_digits = u64 / 1000000000;
+  buffer = FastUInt64ToBufferLeft(top_11_digits, buffer);
+  u = u64 - (top_11_digits * 1000000000);
+
+  digits = u / 10000000;  // 10,000,000
+  GOOGLE_DCHECK_LT(digits, 100);
+  ASCII_digits = two_ASCII_digits[digits];
+  buffer[0] = ASCII_digits[0];
+  buffer[1] = ASCII_digits[1];
+  buffer += 2;
+  u -= digits * 10000000;  // 10,000,000
+  digits = u / 100000;  // 100,000
+  ASCII_digits = two_ASCII_digits[digits];
+  buffer[0] = ASCII_digits[0];
+  buffer[1] = ASCII_digits[1];
+  buffer += 2;
+  u -= digits * 100000;  // 100,000
+  digits = u / 1000;  // 1,000
+  ASCII_digits = two_ASCII_digits[digits];
+  buffer[0] = ASCII_digits[0];
+  buffer[1] = ASCII_digits[1];
+  buffer += 2;
+  u -= digits * 1000;  // 1,000
+  digits = u / 10;
+  ASCII_digits = two_ASCII_digits[digits];
+  buffer[0] = ASCII_digits[0];
+  buffer[1] = ASCII_digits[1];
+  buffer += 2;
+  u -= digits * 10;
+  digits = u;
+  *buffer++ = '0' + digits;
+  *buffer = 0;
+  return buffer;
+}
+
+char* FastInt64ToBufferLeft(int64_t i, char* buffer) {
+  uint64_t u = 0;
+  if (i < 0) {
+    *buffer++ = '-';
+    u -= i;
+  } else {
+    u = i;
+  }
+  return FastUInt64ToBufferLeft(u, buffer);
+}
+
+// ----------------------------------------------------------------------
+// SimpleItoa()
+//    Description: converts an integer to a string.
+//
+//    Return value: string
+// ----------------------------------------------------------------------
+
+std::string SimpleItoa(int i) {
+  char buffer[kFastToBufferSize];
+  return (sizeof(i) == 4) ?
+    FastInt32ToBuffer(i, buffer) :
+    FastInt64ToBuffer(i, buffer);
+}
+
+std::string SimpleItoa(unsigned int i) {
+  char buffer[kFastToBufferSize];
+  return std::string(buffer, (sizeof(i) == 4)
+                                 ? FastUInt32ToBufferLeft(i, buffer)
+                                 : FastUInt64ToBufferLeft(i, buffer));
+}
+
+std::string SimpleItoa(long i) {
+  char buffer[kFastToBufferSize];
+  return (sizeof(i) == 4) ?
+    FastInt32ToBuffer(i, buffer) :
+    FastInt64ToBuffer(i, buffer);
+}
+
+std::string SimpleItoa(unsigned long i) {
+  char buffer[kFastToBufferSize];
+  return std::string(buffer, (sizeof(i) == 4)
+                                 ? FastUInt32ToBufferLeft(i, buffer)
+                                 : FastUInt64ToBufferLeft(i, buffer));
+}
+
+std::string SimpleItoa(long long i) {
+  char buffer[kFastToBufferSize];
+  return (sizeof(i) == 4) ?
+    FastInt32ToBuffer(i, buffer) :
+    FastInt64ToBuffer(i, buffer);
+}
+
+std::string SimpleItoa(unsigned long long i) {
+  char buffer[kFastToBufferSize];
+  return std::string(buffer, (sizeof(i) == 4)
+                                 ? FastUInt32ToBufferLeft(i, buffer)
+                                 : FastUInt64ToBufferLeft(i, buffer));
+}
+
+// ----------------------------------------------------------------------
+// SimpleDtoa()
+// SimpleFtoa()
+// DoubleToBuffer()
+// FloatToBuffer()
+//    We want to print the value without losing precision, but we also do
+//    not want to print more digits than necessary.  This turns out to be
+//    trickier than it sounds.  Numbers like 0.2 cannot be represented
+//    exactly in binary.  If we print 0.2 with a very large precision,
+//    e.g. "%.50g", we get "0.2000000000000000111022302462515654042363167".
+//    On the other hand, if we set the precision too low, we lose
+//    significant digits when printing numbers that actually need them.
+//    It turns out there is no precision value that does the right thing
+//    for all numbers.
+//
+//    Our strategy is to first try printing with a precision that is never
+//    over-precise, then parse the result with strtod() to see if it
+//    matches.  If not, we print again with a precision that will always
+//    give a precise result, but may use more digits than necessary.
+//
+//    An arguably better strategy would be to use the algorithm described
+//    in "How to Print Floating-Point Numbers Accurately" by Steele &
+//    White, e.g. as implemented by David M. Gay's dtoa().  It turns out,
+//    however, that the following implementation is about as fast as
+//    DMG's code.  Furthermore, DMG's code locks mutexes, which means it
+//    will not scale well on multi-core machines.  DMG's code is slightly
+//    more accurate (in that it will never use more digits than
+//    necessary), but this is probably irrelevant for most users.
+//
+//    Rob Pike and Ken Thompson also have an implementation of dtoa() in
+//    third_party/fmt/fltfmt.cc.  Their implementation is similar to this
+//    one in that it makes guesses and then uses strtod() to check them.
+//    Their implementation is faster because they use their own code to
+//    generate the digits in the first place rather than use snprintf(),
+//    thus avoiding format string parsing overhead.  However, this makes
+//    it considerably more complicated than the following implementation,
+//    and it is embedded in a larger library.  If speed turns out to be
+//    an issue, we could re-implement this in terms of their
+//    implementation.
+// ----------------------------------------------------------------------
+
+std::string SimpleDtoa(double value) {
+  char buffer[kDoubleToBufferSize];
+  return DoubleToBuffer(value, buffer);
+}
+
+std::string SimpleFtoa(float value) {
+  char buffer[kFloatToBufferSize];
+  return FloatToBuffer(value, buffer);
+}
+
+static inline bool IsValidFloatChar(char c) {
+  return ('0' <= c && c <= '9') ||
+         c == 'e' || c == 'E' ||
+         c == '+' || c == '-';
+}
+
+void DelocalizeRadix(char* buffer) {
+  // Fast check:  if the buffer has a normal decimal point, assume no
+  // translation is needed.
+  if (strchr(buffer, '.') != nullptr) return;
+
+  // Find the first unknown character.
+  while (IsValidFloatChar(*buffer)) ++buffer;
+
+  if (*buffer == '\0') {
+    // No radix character found.
+    return;
+  }
+
+  // We are now pointing at the locale-specific radix character.  Replace it
+  // with '.'.
+  *buffer = '.';
+  ++buffer;
+
+  if (!IsValidFloatChar(*buffer) && *buffer != '\0') {
+    // It appears the radix was a multi-byte character.  We need to remove the
+    // extra bytes.
+    char* target = buffer;
+    do { ++buffer; } while (!IsValidFloatChar(*buffer) && *buffer != '\0');
+    memmove(target, buffer, strlen(buffer) + 1);
+  }
+}
+
+char* DoubleToBuffer(double value, char* buffer) {
+  // DBL_DIG is 15 for IEEE-754 doubles, which are used on almost all
+  // platforms these days.  Just in case some system exists where DBL_DIG
+  // is significantly larger -- and risks overflowing our buffer -- we have
+  // this assert.
+  static_assert(DBL_DIG < 20, "DBL_DIG_is_too_big");
+
+  if (value == std::numeric_limits<double>::infinity()) {
+    strcpy(buffer, "inf");
+    return buffer;
+  } else if (value == -std::numeric_limits<double>::infinity()) {
+    strcpy(buffer, "-inf");
+    return buffer;
+  } else if (std::isnan(value)) {
+    strcpy(buffer, "nan");
+    return buffer;
+  }
+
+  int snprintf_result =
+    snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG, value);
+
+  // The snprintf should never overflow because the buffer is significantly
+  // larger than the precision we asked for.
+  GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
+
+  // We need to make parsed_value volatile in order to force the compiler to
+  // write it out to the stack.  Otherwise, it may keep the value in a
+  // register, and if it does that, it may keep it as a long double instead
+  // of a double.  This long double may have extra bits that make it compare
+  // unequal to "value" even though it would be exactly equal if it were
+  // truncated to a double.
+  volatile double parsed_value = internal::NoLocaleStrtod(buffer, nullptr);
+  if (parsed_value != value) {
+    snprintf_result =
+        snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG + 2, value);
+
+    // Should never overflow; see above.
+    GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
+  }
+
+  DelocalizeRadix(buffer);
+  return buffer;
+}
+
+static int memcasecmp(const char *s1, const char *s2, size_t len) {
+  const unsigned char *us1 = reinterpret_cast<const unsigned char *>(s1);
+  const unsigned char *us2 = reinterpret_cast<const unsigned char *>(s2);
+
+  for (size_t i = 0; i < len; i++) {
+    const int diff =
+      static_cast<int>(static_cast<unsigned char>(ascii_tolower(us1[i]))) -
+      static_cast<int>(static_cast<unsigned char>(ascii_tolower(us2[i])));
+    if (diff != 0) return diff;
+  }
+  return 0;
+}
+
+inline bool CaseEqual(StringPiece s1, StringPiece s2) {
+  if (s1.size() != s2.size()) return false;
+  return memcasecmp(s1.data(), s2.data(), s1.size()) == 0;
+}
+
+bool safe_strtob(StringPiece str, bool* value) {
+  GOOGLE_CHECK(value != nullptr) << "nullptr output boolean given.";
+  if (CaseEqual(str, "true") || CaseEqual(str, "t") ||
+      CaseEqual(str, "yes") || CaseEqual(str, "y") ||
+      CaseEqual(str, "1")) {
+    *value = true;
+    return true;
+  }
+  if (CaseEqual(str, "false") || CaseEqual(str, "f") ||
+      CaseEqual(str, "no") || CaseEqual(str, "n") ||
+      CaseEqual(str, "0")) {
+    *value = false;
+    return true;
+  }
+  return false;
+}
+
+bool safe_strtof(const char* str, float* value) {
+  char* endptr;
+  errno = 0;  // errno only gets set on errors
+#if defined(_WIN32) || defined (__hpux)  // has no strtof()
+  *value = internal::NoLocaleStrtod(str, &endptr);
+#else
+  *value = strtof(str, &endptr);
+#endif
+  return *str != 0 && *endptr == 0 && errno == 0;
+}
+
+bool safe_strtod(const char* str, double* value) {
+  char* endptr;
+  *value = internal::NoLocaleStrtod(str, &endptr);
+  if (endptr != str) {
+    while (ascii_isspace(*endptr)) ++endptr;
+  }
+  // Ignore range errors from strtod.  The values it
+  // returns on underflow and overflow are the right
+  // fallback in a robust setting.
+  return *str != '\0' && *endptr == '\0';
+}
+
+bool safe_strto32(const std::string &str, int32_t *value) {
+  return safe_int_internal(str, value);
+}
+
+bool safe_strtou32(const std::string &str, uint32_t *value) {
+  return safe_uint_internal(str, value);
+}
+
+bool safe_strto64(const std::string &str, int64_t *value) {
+  return safe_int_internal(str, value);
+}
+
+bool safe_strtou64(const std::string &str, uint64_t *value) {
+  return safe_uint_internal(str, value);
+}
+
+char* FloatToBuffer(float value, char* buffer) {
+  // FLT_DIG is 6 for IEEE-754 floats, which are used on almost all
+  // platforms these days.  Just in case some system exists where FLT_DIG
+  // is significantly larger -- and risks overflowing our buffer -- we have
+  // this assert.
+  static_assert(FLT_DIG < 10, "FLT_DIG_is_too_big");
+
+  if (value == std::numeric_limits<double>::infinity()) {
+    strcpy(buffer, "inf");
+    return buffer;
+  } else if (value == -std::numeric_limits<double>::infinity()) {
+    strcpy(buffer, "-inf");
+    return buffer;
+  } else if (std::isnan(value)) {
+    strcpy(buffer, "nan");
+    return buffer;
+  }
+
+  int snprintf_result =
+    snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG, value);
+
+  // The snprintf should never overflow because the buffer is significantly
+  // larger than the precision we asked for.
+  GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
+
+  float parsed_value;
+  if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) {
+    snprintf_result =
+        snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG + 3, value);
+
+    // Should never overflow; see above.
+    GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
+  }
+
+  DelocalizeRadix(buffer);
+  return buffer;
+}
+
+namespace strings {
+
+AlphaNum::AlphaNum(strings::Hex hex) {
+  char *const end = &digits[kFastToBufferSize];
+  char *writer = end;
+  uint64_t value = hex.value;
+  uint64_t width = hex.spec;
+  // We accomplish minimum width by OR'ing in 0x10000 to the user's value,
+  // where 0x10000 is the smallest hex number that is as wide as the user
+  // asked for.
+  uint64_t mask = (static_cast<uint64_t>(1) << ((width - 1) * 4)) | value;
+  static const char hexdigits[] = "0123456789abcdef";
+  do {
+    *--writer = hexdigits[value & 0xF];
+    value >>= 4;
+    mask >>= 4;
+  } while (mask != 0);
+  piece_data_ = writer;
+  piece_size_ = end - writer;
+}
+
+}  // namespace strings
+
+// ----------------------------------------------------------------------
+// StrCat()
+//    This merges the given strings or integers, with no delimiter.  This
+//    is designed to be the fastest possible way to construct a string out
+//    of a mix of raw C strings, C++ strings, and integer values.
+// ----------------------------------------------------------------------
+
+// Append is merely a version of memcpy that returns the address of the byte
+// after the area just overwritten.  It comes in multiple flavors to minimize
+// call overhead.
+static char *Append1(char *out, const AlphaNum &x) {
+  if (x.size() > 0) {
+    memcpy(out, x.data(), x.size());
+    out += x.size();
+  }
+  return out;
+}
+
+static char *Append2(char *out, const AlphaNum &x1, const AlphaNum &x2) {
+  if (x1.size() > 0) {
+    memcpy(out, x1.data(), x1.size());
+    out += x1.size();
+  }
+  if (x2.size() > 0) {
+    memcpy(out, x2.data(), x2.size());
+    out += x2.size();
+  }
+  return out;
+}
+
+static char *Append4(char *out, const AlphaNum &x1, const AlphaNum &x2,
+                     const AlphaNum &x3, const AlphaNum &x4) {
+  if (x1.size() > 0) {
+    memcpy(out, x1.data(), x1.size());
+    out += x1.size();
+  }
+  if (x2.size() > 0) {
+    memcpy(out, x2.data(), x2.size());
+    out += x2.size();
+  }
+  if (x3.size() > 0) {
+    memcpy(out, x3.data(), x3.size());
+    out += x3.size();
+  }
+  if (x4.size() > 0) {
+    memcpy(out, x4.data(), x4.size());
+    out += x4.size();
+  }
+  return out;
+}
+
+std::string StrCat(const AlphaNum &a, const AlphaNum &b) {
+  std::string result;
+  result.resize(a.size() + b.size());
+  char *const begin = &*result.begin();
+  char *out = Append2(begin, a, b);
+  GOOGLE_DCHECK_EQ(out, begin + result.size());
+  return result;
+}
+
+std::string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c) {
+  std::string result;
+  result.resize(a.size() + b.size() + c.size());
+  char *const begin = &*result.begin();
+  char *out = Append2(begin, a, b);
+  out = Append1(out, c);
+  GOOGLE_DCHECK_EQ(out, begin + result.size());
+  return result;
+}
+
+std::string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
+                   const AlphaNum &d) {
+  std::string result;
+  result.resize(a.size() + b.size() + c.size() + d.size());
+  char *const begin = &*result.begin();
+  char *out = Append4(begin, a, b, c, d);
+  GOOGLE_DCHECK_EQ(out, begin + result.size());
+  return result;
+}
+
+std::string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
+                   const AlphaNum &d, const AlphaNum &e) {
+  std::string result;
+  result.resize(a.size() + b.size() + c.size() + d.size() + e.size());
+  char *const begin = &*result.begin();
+  char *out = Append4(begin, a, b, c, d);
+  out = Append1(out, e);
+  GOOGLE_DCHECK_EQ(out, begin + result.size());
+  return result;
+}
+
+std::string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
+                   const AlphaNum &d, const AlphaNum &e, const AlphaNum &f) {
+  std::string result;
+  result.resize(a.size() + b.size() + c.size() + d.size() + e.size() +
+                f.size());
+  char *const begin = &*result.begin();
+  char *out = Append4(begin, a, b, c, d);
+  out = Append2(out, e, f);
+  GOOGLE_DCHECK_EQ(out, begin + result.size());
+  return result;
+}
+
+std::string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
+                   const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
+                   const AlphaNum &g) {
+  std::string result;
+  result.resize(a.size() + b.size() + c.size() + d.size() + e.size() +
+                f.size() + g.size());
+  char *const begin = &*result.begin();
+  char *out = Append4(begin, a, b, c, d);
+  out = Append2(out, e, f);
+  out = Append1(out, g);
+  GOOGLE_DCHECK_EQ(out, begin + result.size());
+  return result;
+}
+
+std::string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
+                   const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
+                   const AlphaNum &g, const AlphaNum &h) {
+  std::string result;
+  result.resize(a.size() + b.size() + c.size() + d.size() + e.size() +
+                f.size() + g.size() + h.size());
+  char *const begin = &*result.begin();
+  char *out = Append4(begin, a, b, c, d);
+  out = Append4(out, e, f, g, h);
+  GOOGLE_DCHECK_EQ(out, begin + result.size());
+  return result;
+}
+
+std::string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
+                   const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
+                   const AlphaNum &g, const AlphaNum &h, const AlphaNum &i) {
+  std::string result;
+  result.resize(a.size() + b.size() + c.size() + d.size() + e.size() +
+                f.size() + g.size() + h.size() + i.size());
+  char *const begin = &*result.begin();
+  char *out = Append4(begin, a, b, c, d);
+  out = Append4(out, e, f, g, h);
+  out = Append1(out, i);
+  GOOGLE_DCHECK_EQ(out, begin + result.size());
+  return result;
+}
+
+// It's possible to call StrAppend with a char * pointer that is partway into
+// the string we're appending to.  However the results of this are random.
+// Therefore, check for this in debug mode.  Use unsigned math so we only have
+// to do one comparison.
+#define GOOGLE_DCHECK_NO_OVERLAP(dest, src) \
+    GOOGLE_DCHECK_GT(uintptr_t((src).data() - (dest).data()), \
+                     uintptr_t((dest).size()))
+
+void StrAppend(std::string *result, const AlphaNum &a) {
+  GOOGLE_DCHECK_NO_OVERLAP(*result, a);
+  result->append(a.data(), a.size());
+}
+
+void StrAppend(std::string *result, const AlphaNum &a, const AlphaNum &b) {
+  GOOGLE_DCHECK_NO_OVERLAP(*result, a);
+  GOOGLE_DCHECK_NO_OVERLAP(*result, b);
+  std::string::size_type old_size = result->size();
+  result->resize(old_size + a.size() + b.size());
+  char *const begin = &*result->begin();
+  char *out = Append2(begin + old_size, a, b);
+  GOOGLE_DCHECK_EQ(out, begin + result->size());
+}
+
+void StrAppend(std::string *result, const AlphaNum &a, const AlphaNum &b,
+               const AlphaNum &c) {
+  GOOGLE_DCHECK_NO_OVERLAP(*result, a);
+  GOOGLE_DCHECK_NO_OVERLAP(*result, b);
+  GOOGLE_DCHECK_NO_OVERLAP(*result, c);
+  std::string::size_type old_size = result->size();
+  result->resize(old_size + a.size() + b.size() + c.size());
+  char *const begin = &*result->begin();
+  char *out = Append2(begin + old_size, a, b);
+  out = Append1(out, c);
+  GOOGLE_DCHECK_EQ(out, begin + result->size());
+}
+
+void StrAppend(std::string *result, const AlphaNum &a, const AlphaNum &b,
+               const AlphaNum &c, const AlphaNum &d) {
+  GOOGLE_DCHECK_NO_OVERLAP(*result, a);
+  GOOGLE_DCHECK_NO_OVERLAP(*result, b);
+  GOOGLE_DCHECK_NO_OVERLAP(*result, c);
+  GOOGLE_DCHECK_NO_OVERLAP(*result, d);
+  std::string::size_type old_size = result->size();
+  result->resize(old_size + a.size() + b.size() + c.size() + d.size());
+  char *const begin = &*result->begin();
+  char *out = Append4(begin + old_size, a, b, c, d);
+  GOOGLE_DCHECK_EQ(out, begin + result->size());
+}
+
+int GlobalReplaceSubstring(const std::string &substring,
+                           const std::string &replacement, std::string *s) {
+  GOOGLE_CHECK(s != nullptr);
+  if (s->empty() || substring.empty())
+    return 0;
+  std::string tmp;
+  int num_replacements = 0;
+  int pos = 0;
+  for (StringPiece::size_type match_pos =
+           s->find(substring.data(), pos, substring.length());
+       match_pos != std::string::npos; pos = match_pos + substring.length(),
+                              match_pos = s->find(substring.data(), pos,
+                                                  substring.length())) {
+    ++num_replacements;
+    // Append the original content before the match.
+    tmp.append(*s, pos, match_pos - pos);
+    // Append the replacement for the match.
+    tmp.append(replacement.begin(), replacement.end());
+  }
+  // Append the content after the last match. If no replacements were made, the
+  // original string is left untouched.
+  if (num_replacements > 0) {
+    tmp.append(*s, pos, s->length() - pos);
+    s->swap(tmp);
+  }
+  return num_replacements;
+}
+
+int CalculateBase64EscapedLen(int input_len, bool do_padding) {
+  // Base64 encodes three bytes of input at a time. If the input is not
+  // divisible by three, we pad as appropriate.
+  //
+  // (from http://tools.ietf.org/html/rfc3548)
+  // Special processing is performed if fewer than 24 bits are available
+  // at the end of the data being encoded.  A full encoding quantum is
+  // always completed at the end of a quantity.  When fewer than 24 input
+  // bits are available in an input group, zero bits are added (on the
+  // right) to form an integral number of 6-bit groups.  Padding at the
+  // end of the data is performed using the '=' character.  Since all base
+  // 64 input is an integral number of octets, only the following cases
+  // can arise:
+
+
+  // Base64 encodes each three bytes of input into four bytes of output.
+  int len = (input_len / 3) * 4;
+
+  if (input_len % 3 == 0) {
+    // (from http://tools.ietf.org/html/rfc3548)
+    // (1) the final quantum of encoding input is an integral multiple of 24
+    // bits; here, the final unit of encoded output will be an integral
+    // multiple of 4 characters with no "=" padding,
+  } else if (input_len % 3 == 1) {
+    // (from http://tools.ietf.org/html/rfc3548)
+    // (2) the final quantum of encoding input is exactly 8 bits; here, the
+    // final unit of encoded output will be two characters followed by two
+    // "=" padding characters, or
+    len += 2;
+    if (do_padding) {
+      len += 2;
+    }
+  } else {  // (input_len % 3 == 2)
+    // (from http://tools.ietf.org/html/rfc3548)
+    // (3) the final quantum of encoding input is exactly 16 bits; here, the
+    // final unit of encoded output will be three characters followed by one
+    // "=" padding character.
+    len += 3;
+    if (do_padding) {
+      len += 1;
+    }
+  }
+
+  assert(len >= input_len);  // make sure we didn't overflow
+  return len;
+}
+
+// Base64Escape does padding, so this calculation includes padding.
+int CalculateBase64EscapedLen(int input_len) {
+  return CalculateBase64EscapedLen(input_len, true);
+}
+
+// ----------------------------------------------------------------------
+// int Base64Unescape() - base64 decoder
+// int Base64Escape() - base64 encoder
+// int WebSafeBase64Unescape() - Google's variation of base64 decoder
+// int WebSafeBase64Escape() - Google's variation of base64 encoder
+//
+// Check out
+// http://tools.ietf.org/html/rfc2045 for formal description, but what we
+// care about is that...
+//   Take the encoded stuff in groups of 4 characters and turn each
+//   character into a code 0 to 63 thus:
+//           A-Z map to 0 to 25
+//           a-z map to 26 to 51
+//           0-9 map to 52 to 61
+//           +(- for WebSafe) maps to 62
+//           /(_ for WebSafe) maps to 63
+//   There will be four numbers, all less than 64 which can be represented
+//   by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).
+//   Arrange the 6 digit binary numbers into three bytes as such:
+//   aaaaaabb bbbbcccc ccdddddd
+//   Equals signs (one or two) are used at the end of the encoded block to
+//   indicate that the text was not an integer multiple of three bytes long.
+// ----------------------------------------------------------------------
+
+int Base64UnescapeInternal(const char *src_param, int szsrc,
+                           char *dest, int szdest,
+                           const signed char* unbase64) {
+  static const char kPad64Equals = '=';
+  static const char kPad64Dot = '.';
+
+  int decode = 0;
+  int destidx = 0;
+  int state = 0;
+  unsigned int ch = 0;
+  unsigned int temp = 0;
+
+  // If "char" is signed by default, using *src as an array index results in
+  // accessing negative array elements. Treat the input as a pointer to
+  // unsigned char to avoid this.
+  const unsigned char *src = reinterpret_cast<const unsigned char*>(src_param);
+
+  // The GET_INPUT macro gets the next input character, skipping
+  // over any whitespace, and stopping when we reach the end of the
+  // string or when we read any non-data character.  The arguments are
+  // an arbitrary identifier (used as a label for goto) and the number
+  // of data bytes that must remain in the input to avoid aborting the
+  // loop.
+#define GET_INPUT(label, remain)                 \
+  label:                                         \
+    --szsrc;                                     \
+    ch = *src++;                                 \
+    decode = unbase64[ch];                       \
+    if (decode < 0) {                            \
+      if (ascii_isspace(ch) && szsrc >= remain)  \
+        goto label;                              \
+      state = 4 - remain;                        \
+      break;                                     \
+    }
+
+  // if dest is null, we're just checking to see if it's legal input
+  // rather than producing output.  (I suspect this could just be done
+  // with a regexp...).  We duplicate the loop so this test can be
+  // outside it instead of in every iteration.
+
+  if (dest) {
+    // This loop consumes 4 input bytes and produces 3 output bytes
+    // per iteration.  We can't know at the start that there is enough
+    // data left in the string for a full iteration, so the loop may
+    // break out in the middle; if so 'state' will be set to the
+    // number of input bytes read.
+
+    while (szsrc >= 4)  {
+      // We'll start by optimistically assuming that the next four
+      // bytes of the string (src[0..3]) are four good data bytes
+      // (that is, no nulls, whitespace, padding chars, or illegal
+      // chars).  We need to test src[0..2] for nulls individually
+      // before constructing temp to preserve the property that we
+      // never read past a null in the string (no matter how long
+      // szsrc claims the string is).
+
+      if (!src[0] || !src[1] || !src[2] ||
+          (temp = ((unsigned(unbase64[src[0]]) << 18) |
+                   (unsigned(unbase64[src[1]]) << 12) |
+                   (unsigned(unbase64[src[2]]) << 6) |
+                   (unsigned(unbase64[src[3]])))) & 0x80000000) {
+        // Iff any of those four characters was bad (null, illegal,
+        // whitespace, padding), then temp's high bit will be set
+        // (because unbase64[] is -1 for all bad characters).
+        //
+        // We'll back up and resort to the slower decoder, which knows
+        // how to handle those cases.
+
+        GET_INPUT(first, 4);
+        temp = decode;
+        GET_INPUT(second, 3);
+        temp = (temp << 6) | decode;
+        GET_INPUT(third, 2);
+        temp = (temp << 6) | decode;
+        GET_INPUT(fourth, 1);
+        temp = (temp << 6) | decode;
+      } else {
+        // We really did have four good data bytes, so advance four
+        // characters in the string.
+
+        szsrc -= 4;
+        src += 4;
+        decode = -1;
+        ch = '\0';
+      }
+
+      // temp has 24 bits of input, so write that out as three bytes.
+
+      if (destidx+3 > szdest) return -1;
+      dest[destidx+2] = temp;
+      temp >>= 8;
+      dest[destidx+1] = temp;
+      temp >>= 8;
+      dest[destidx] = temp;
+      destidx += 3;
+    }
+  } else {
+    while (szsrc >= 4)  {
+      if (!src[0] || !src[1] || !src[2] ||
+          (temp = ((unsigned(unbase64[src[0]]) << 18) |
+                   (unsigned(unbase64[src[1]]) << 12) |
+                   (unsigned(unbase64[src[2]]) << 6) |
+                   (unsigned(unbase64[src[3]])))) & 0x80000000) {
+        GET_INPUT(first_no_dest, 4);
+        GET_INPUT(second_no_dest, 3);
+        GET_INPUT(third_no_dest, 2);
+        GET_INPUT(fourth_no_dest, 1);
+      } else {
+        szsrc -= 4;
+        src += 4;
+        decode = -1;
+        ch = '\0';
+      }
+      destidx += 3;
+    }
+  }
+
+#undef GET_INPUT
+
+  // if the loop terminated because we read a bad character, return
+  // now.
+  if (decode < 0 && ch != '\0' &&
+      ch != kPad64Equals && ch != kPad64Dot && !ascii_isspace(ch))
+    return -1;
+
+  if (ch == kPad64Equals || ch == kPad64Dot) {
+    // if we stopped by hitting an '=' or '.', un-read that character -- we'll
+    // look at it again when we count to check for the proper number of
+    // equals signs at the end.
+    ++szsrc;
+    --src;
+  } else {
+    // This loop consumes 1 input byte per iteration.  It's used to
+    // clean up the 0-3 input bytes remaining when the first, faster
+    // loop finishes.  'temp' contains the data from 'state' input
+    // characters read by the first loop.
+    while (szsrc > 0)  {
+      --szsrc;
+      ch = *src++;
+      decode = unbase64[ch];
+      if (decode < 0) {
+        if (ascii_isspace(ch)) {
+          continue;
+        } else if (ch == '\0') {
+          break;
+        } else if (ch == kPad64Equals || ch == kPad64Dot) {
+          // back up one character; we'll read it again when we check
+          // for the correct number of pad characters at the end.
+          ++szsrc;
+          --src;
+          break;
+        } else {
+          return -1;
+        }
+      }
+
+      // Each input character gives us six bits of output.
+      temp = (temp << 6) | decode;
+      ++state;
+      if (state == 4) {
+        // If we've accumulated 24 bits of output, write that out as
+        // three bytes.
+        if (dest) {
+          if (destidx+3 > szdest) return -1;
+          dest[destidx+2] = temp;
+          temp >>= 8;
+          dest[destidx+1] = temp;
+          temp >>= 8;
+          dest[destidx] = temp;
+        }
+        destidx += 3;
+        state = 0;
+        temp = 0;
+      }
+    }
+  }
+
+  // Process the leftover data contained in 'temp' at the end of the input.
+  int expected_equals = 0;
+  switch (state) {
+    case 0:
+      // Nothing left over; output is a multiple of 3 bytes.
+      break;
+
+    case 1:
+      // Bad input; we have 6 bits left over.
+      return -1;
+
+    case 2:
+      // Produce one more output byte from the 12 input bits we have left.
+      if (dest) {
+        if (destidx+1 > szdest) return -1;
+        temp >>= 4;
+        dest[destidx] = temp;
+      }
+      ++destidx;
+      expected_equals = 2;
+      break;
+
+    case 3:
+      // Produce two more output bytes from the 18 input bits we have left.
+      if (dest) {
+        if (destidx+2 > szdest) return -1;
+        temp >>= 2;
+        dest[destidx+1] = temp;
+        temp >>= 8;
+        dest[destidx] = temp;
+      }
+      destidx += 2;
+      expected_equals = 1;
+      break;
+
+    default:
+      // state should have no other values at this point.
+      GOOGLE_LOG(FATAL) << "This can't happen; base64 decoder state = " << state;
+  }
+
+  // The remainder of the string should be all whitespace, mixed with
+  // exactly 0 equals signs, or exactly 'expected_equals' equals
+  // signs.  (Always accepting 0 equals signs is a google extension
+  // not covered in the RFC, as is accepting dot as the pad character.)
+
+  int equals = 0;
+  while (szsrc > 0 && *src) {
+    if (*src == kPad64Equals || *src == kPad64Dot)
+      ++equals;
+    else if (!ascii_isspace(*src))
+      return -1;
+    --szsrc;
+    ++src;
+  }
+
+  return (equals == 0 || equals == expected_equals) ? destidx : -1;
+}
+
+// The arrays below were generated by the following code
+// #include <sys/time.h>
+// #include <stdlib.h>
+// #include <string.h>
+// #include <stdio.h>
+// main()
+// {
+//   static const char Base64[] =
+//     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+//   const char *pos;
+//   int idx, i, j;
+//   printf("    ");
+//   for (i = 0; i < 255; i += 8) {
+//     for (j = i; j < i + 8; j++) {
+//       pos = strchr(Base64, j);
+//       if ((pos == nullptr) || (j == 0))
+//         idx = -1;
+//       else
+//         idx = pos - Base64;
+//       if (idx == -1)
+//         printf(" %2d,     ", idx);
+//       else
+//         printf(" %2d/""*%c*""/,", idx, j);
+//     }
+//     printf("\n    ");
+//   }
+// }
+//
+// where the value of "Base64[]" was replaced by one of the base-64 conversion
+// tables from the functions below.
+static const signed char kUnBase64[] = {
+  -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,      62/*+*/, -1,      -1,      -1,      63/*/ */,
+  52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
+  60/*8*/, 61/*9*/, -1,      -1,      -1,      -1,      -1,      -1,
+  -1,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,
+   7/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
+  15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
+  23/*X*/, 24/*Y*/, 25/*Z*/, -1,      -1,      -1,      -1,      -1,
+  -1,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
+  33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
+  41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
+  49/*x*/, 50/*y*/, 51/*z*/, -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,      -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,      -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
+};
+static const signed char kUnWebSafeBase64[] = {
+  -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,      62/*-*/, -1,      -1,
+  52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
+  60/*8*/, 61/*9*/, -1,      -1,      -1,      -1,      -1,      -1,
+  -1,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,
+   7/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
+  15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
+  23/*X*/, 24/*Y*/, 25/*Z*/, -1,      -1,      -1,      -1,      63/*_*/,
+  -1,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
+  33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
+  41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
+  49/*x*/, 50/*y*/, 51/*z*/, -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,      -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,      -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
+};
+
+int WebSafeBase64Unescape(const char *src, int szsrc, char *dest, int szdest) {
+  return Base64UnescapeInternal(src, szsrc, dest, szdest, kUnWebSafeBase64);
+}
+
+static bool Base64UnescapeInternal(const char *src, int slen, std::string *dest,
+                                   const signed char *unbase64) {
+  // Determine the size of the output string.  Base64 encodes every 3 bytes into
+  // 4 characters.  any leftover chars are added directly for good measure.
+  // This is documented in the base64 RFC: http://tools.ietf.org/html/rfc3548
+  const int dest_len = 3 * (slen / 4) + (slen % 4);
+
+  dest->resize(dest_len);
+
+  // We are getting the destination buffer by getting the beginning of the
+  // string and converting it into a char *.
+  const int len = Base64UnescapeInternal(src, slen, string_as_array(dest),
+                                         dest_len, unbase64);
+  if (len < 0) {
+    dest->clear();
+    return false;
+  }
+
+  // could be shorter if there was padding
+  GOOGLE_DCHECK_LE(len, dest_len);
+  dest->erase(len);
+
+  return true;
+}
+
+bool Base64Unescape(StringPiece src, std::string *dest) {
+  return Base64UnescapeInternal(src.data(), src.size(), dest, kUnBase64);
+}
+
+bool WebSafeBase64Unescape(StringPiece src, std::string *dest) {
+  return Base64UnescapeInternal(src.data(), src.size(), dest, kUnWebSafeBase64);
+}
+
+int Base64EscapeInternal(const unsigned char *src, int szsrc,
+                         char *dest, int szdest, const char *base64,
+                         bool do_padding) {
+  static const char kPad64 = '=';
+
+  if (szsrc <= 0) return 0;
+
+  if (szsrc * 4 > szdest * 3) return 0;
+
+  char *cur_dest = dest;
+  const unsigned char *cur_src = src;
+
+  char *limit_dest = dest + szdest;
+  const unsigned char *limit_src = src + szsrc;
+
+  // Three bytes of data encodes to four characters of ciphertext.
+  // So we can pump through three-byte chunks atomically.
+  while (cur_src < limit_src - 3) {  // keep going as long as we have >= 32 bits
+    uint32_t in = BigEndian::Load32(cur_src) >> 8;
+
+    cur_dest[0] = base64[in >> 18];
+    in &= 0x3FFFF;
+    cur_dest[1] = base64[in >> 12];
+    in &= 0xFFF;
+    cur_dest[2] = base64[in >> 6];
+    in &= 0x3F;
+    cur_dest[3] = base64[in];
+
+    cur_dest += 4;
+    cur_src += 3;
+  }
+  // To save time, we didn't update szdest or szsrc in the loop.  So do it now.
+  szdest = limit_dest - cur_dest;
+  szsrc = limit_src - cur_src;
+
+  /* now deal with the tail (<=3 bytes) */
+  switch (szsrc) {
+    case 0:
+      // Nothing left; nothing more to do.
+      break;
+    case 1: {
+      // One byte left: this encodes to two characters, and (optionally)
+      // two pad characters to round out the four-character cipherblock.
+      if ((szdest -= 2) < 0) return 0;
+      uint32_t in = cur_src[0];
+      cur_dest[0] = base64[in >> 2];
+      in &= 0x3;
+      cur_dest[1] = base64[in << 4];
+      cur_dest += 2;
+      if (do_padding) {
+        if ((szdest -= 2) < 0) return 0;
+        cur_dest[0] = kPad64;
+        cur_dest[1] = kPad64;
+        cur_dest += 2;
+      }
+      break;
+    }
+    case 2: {
+      // Two bytes left: this encodes to three characters, and (optionally)
+      // one pad character to round out the four-character cipherblock.
+      if ((szdest -= 3) < 0) return 0;
+      uint32_t in = BigEndian::Load16(cur_src);
+      cur_dest[0] = base64[in >> 10];
+      in &= 0x3FF;
+      cur_dest[1] = base64[in >> 4];
+      in &= 0x00F;
+      cur_dest[2] = base64[in << 2];
+      cur_dest += 3;
+      if (do_padding) {
+        if ((szdest -= 1) < 0) return 0;
+        cur_dest[0] = kPad64;
+        cur_dest += 1;
+      }
+      break;
+    }
+    case 3: {
+      // Three bytes left: same as in the big loop above.  We can't do this in
+      // the loop because the loop above always reads 4 bytes, and the fourth
+      // byte is past the end of the input.
+      if ((szdest -= 4) < 0) return 0;
+      uint32_t in = (cur_src[0] << 16) + BigEndian::Load16(cur_src + 1);
+      cur_dest[0] = base64[in >> 18];
+      in &= 0x3FFFF;
+      cur_dest[1] = base64[in >> 12];
+      in &= 0xFFF;
+      cur_dest[2] = base64[in >> 6];
+      in &= 0x3F;
+      cur_dest[3] = base64[in];
+      cur_dest += 4;
+      break;
+    }
+    default:
+      // Should not be reached: blocks of 4 bytes are handled
+      // in the while loop before this switch statement.
+      GOOGLE_LOG(FATAL) << "Logic problem? szsrc = " << szsrc;
+      break;
+  }
+  return (cur_dest - dest);
+}
+
+static const char kBase64Chars[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static const char kWebSafeBase64Chars[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+int Base64Escape(const unsigned char *src, int szsrc, char *dest, int szdest) {
+  return Base64EscapeInternal(src, szsrc, dest, szdest, kBase64Chars, true);
+}
+int WebSafeBase64Escape(const unsigned char *src, int szsrc, char *dest,
+                        int szdest, bool do_padding) {
+  return Base64EscapeInternal(src, szsrc, dest, szdest,
+                              kWebSafeBase64Chars, do_padding);
+}
+
+void Base64EscapeInternal(const unsigned char *src, int szsrc,
+                          std::string *dest, bool do_padding,
+                          const char *base64_chars) {
+  const int calc_escaped_size =
+    CalculateBase64EscapedLen(szsrc, do_padding);
+  dest->resize(calc_escaped_size);
+  const int escaped_len = Base64EscapeInternal(src, szsrc,
+                                               string_as_array(dest),
+                                               dest->size(),
+                                               base64_chars,
+                                               do_padding);
+  GOOGLE_DCHECK_EQ(calc_escaped_size, escaped_len);
+  dest->erase(escaped_len);
+}
+
+void Base64Escape(const unsigned char *src, int szsrc, std::string *dest,
+                  bool do_padding) {
+  Base64EscapeInternal(src, szsrc, dest, do_padding, kBase64Chars);
+}
+
+void WebSafeBase64Escape(const unsigned char *src, int szsrc, std::string *dest,
+                         bool do_padding) {
+  Base64EscapeInternal(src, szsrc, dest, do_padding, kWebSafeBase64Chars);
+}
+
+void Base64Escape(StringPiece src, std::string *dest) {
+  Base64Escape(reinterpret_cast<const unsigned char*>(src.data()),
+               src.size(), dest, true);
+}
+
+void WebSafeBase64Escape(StringPiece src, std::string *dest) {
+  WebSafeBase64Escape(reinterpret_cast<const unsigned char*>(src.data()),
+                      src.size(), dest, false);
+}
+
+void WebSafeBase64EscapeWithPadding(StringPiece src, std::string *dest) {
+  WebSafeBase64Escape(reinterpret_cast<const unsigned char*>(src.data()),
+                      src.size(), dest, true);
+}
+
+// Helper to append a Unicode code point to a string as UTF8, without bringing
+// in any external dependencies.
+int EncodeAsUTF8Char(uint32_t code_point, char* output) {
+  uint32_t tmp = 0;
+  int len = 0;
+  if (code_point <= 0x7f) {
+    tmp = code_point;
+    len = 1;
+  } else if (code_point <= 0x07ff) {
+    tmp = 0x0000c080 |
+        ((code_point & 0x07c0) << 2) |
+        (code_point & 0x003f);
+    len = 2;
+  } else if (code_point <= 0xffff) {
+    tmp = 0x00e08080 |
+        ((code_point & 0xf000) << 4) |
+        ((code_point & 0x0fc0) << 2) |
+        (code_point & 0x003f);
+    len = 3;
+  } else {
+    // UTF-16 is only defined for code points up to 0x10FFFF, and UTF-8 is
+    // normally only defined up to there as well.
+    tmp = 0xf0808080 |
+        ((code_point & 0x1c0000) << 6) |
+        ((code_point & 0x03f000) << 4) |
+        ((code_point & 0x000fc0) << 2) |
+        (code_point & 0x003f);
+    len = 4;
+  }
+  tmp = ghtonl(tmp);
+  memcpy(output, reinterpret_cast<const char*>(&tmp) + sizeof(tmp) - len, len);
+  return len;
+}
+
+// Table of UTF-8 character lengths, based on first byte
+static const unsigned char kUTF8LenTbl[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, 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, 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, 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, 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, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+    3, 3, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+
+// Return length of a single UTF-8 source character
+int UTF8FirstLetterNumBytes(const char* src, int len) {
+  if (len == 0) {
+    return 0;
+  }
+  return kUTF8LenTbl[*reinterpret_cast<const uint8_t*>(src)];
+}
+
+// ----------------------------------------------------------------------
+// CleanStringLineEndings()
+//   Clean up a multi-line string to conform to Unix line endings.
+//   Reads from src and appends to dst, so usually dst should be empty.
+//
+//   If there is no line ending at the end of a non-empty string, it can
+//   be added automatically.
+//
+//   Four different types of input are correctly handled:
+//
+//     - Unix/Linux files: line ending is LF: pass through unchanged
+//
+//     - DOS/Windows files: line ending is CRLF: convert to LF
+//
+//     - Legacy Mac files: line ending is CR: convert to LF
+//
+//     - Garbled files: random line endings: convert gracefully
+//                      lonely CR, lonely LF, CRLF: convert to LF
+//
+//   @param src The multi-line string to convert
+//   @param dst The converted string is appended to this string
+//   @param auto_end_last_line Automatically terminate the last line
+//
+//   Limitations:
+//
+//     This does not do the right thing for CRCRLF files created by
+//     broken programs that do another Unix->DOS conversion on files
+//     that are already in CRLF format.  For this, a two-pass approach
+//     brute-force would be needed that
+//
+//       (1) determines the presence of LF (first one is ok)
+//       (2) if yes, removes any CR, else convert every CR to LF
+
+void CleanStringLineEndings(const std::string &src, std::string *dst,
+                            bool auto_end_last_line) {
+  if (dst->empty()) {
+    dst->append(src);
+    CleanStringLineEndings(dst, auto_end_last_line);
+  } else {
+    std::string tmp = src;
+    CleanStringLineEndings(&tmp, auto_end_last_line);
+    dst->append(tmp);
+  }
+}
+
+void CleanStringLineEndings(std::string *str, bool auto_end_last_line) {
+  ptrdiff_t output_pos = 0;
+  bool r_seen = false;
+  ptrdiff_t len = str->size();
+
+  char *p = &(*str)[0];
+
+  for (ptrdiff_t input_pos = 0; input_pos < len;) {
+    if (!r_seen && input_pos + 8 < len) {
+      uint64_t v = GOOGLE_UNALIGNED_LOAD64(p + input_pos);
+      // Loop over groups of 8 bytes at a time until we come across
+      // a word that has a byte whose value is less than or equal to
+      // '\r' (i.e. could contain a \n (0x0a) or a \r (0x0d) ).
+      //
+      // We use a has_less macro that quickly tests a whole 64-bit
+      // word to see if any of the bytes has a value < N.
+      //
+      // For more details, see:
+      //   http://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
+#define has_less(x, n) (((x) - ~0ULL / 255 * (n)) & ~(x) & ~0ULL / 255 * 128)
+      if (!has_less(v, '\r' + 1)) {
+#undef has_less
+        // No byte in this word has a value that could be a \r or a \n
+        if (output_pos != input_pos) {
+          GOOGLE_UNALIGNED_STORE64(p + output_pos, v);
+        }
+        input_pos += 8;
+        output_pos += 8;
+        continue;
+      }
+    }
+    std::string::const_reference in = p[input_pos];
+    if (in == '\r') {
+      if (r_seen) p[output_pos++] = '\n';
+      r_seen = true;
+    } else if (in == '\n') {
+      if (input_pos != output_pos)
+        p[output_pos++] = '\n';
+      else
+        output_pos++;
+      r_seen = false;
+    } else {
+      if (r_seen) p[output_pos++] = '\n';
+      r_seen = false;
+      if (input_pos != output_pos)
+        p[output_pos++] = in;
+      else
+        output_pos++;
+    }
+    input_pos++;
+  }
+  if (r_seen ||
+      (auto_end_last_line && output_pos > 0 && p[output_pos - 1] != '\n')) {
+    str->resize(output_pos + 1);
+    str->operator[](output_pos) = '\n';
+  } else if (output_pos < len) {
+    str->resize(output_pos);
+  }
+}
+
+namespace internal {
+
+// ----------------------------------------------------------------------
+// NoLocaleStrtod()
+//   This code will make you cry.
+// ----------------------------------------------------------------------
+
+namespace {
+
+// Returns a string identical to *input except that the character pointed to
+// by radix_pos (which should be '.') is replaced with the locale-specific
+// radix character.
+std::string LocalizeRadix(const char *input, const char *radix_pos) {
+  // Determine the locale-specific radix character by calling sprintf() to
+  // print the number 1.5, then stripping off the digits.  As far as I can
+  // tell, this is the only portable, thread-safe way to get the C library
+  // to divuldge the locale's radix character.  No, localeconv() is NOT
+  // thread-safe.
+  char temp[16];
+  int size = snprintf(temp, sizeof(temp), "%.1f", 1.5);
+  GOOGLE_CHECK_EQ(temp[0], '1');
+  GOOGLE_CHECK_EQ(temp[size - 1], '5');
+  GOOGLE_CHECK_LE(size, 6);
+
+  // Now replace the '.' in the input with it.
+  std::string result;
+  result.reserve(strlen(input) + size - 3);
+  result.append(input, radix_pos);
+  result.append(temp + 1, size - 2);
+  result.append(radix_pos + 1);
+  return result;
+}
+
+}  // namespace
+
+double NoLocaleStrtod(const char *str, char **endptr) {
+  // We cannot simply set the locale to "C" temporarily with setlocale()
+  // as this is not thread-safe.  Instead, we try to parse in the current
+  // locale first.  If parsing stops at a '.' character, then this is a
+  // pretty good hint that we're actually in some other locale in which
+  // '.' is not the radix character.
+
+  char *temp_endptr;
+  double result = strtod(str, &temp_endptr);
+  if (endptr != NULL) *endptr = temp_endptr;
+  if (*temp_endptr != '.') return result;
+
+  // Parsing halted on a '.'.  Perhaps we're in a different locale?  Let's
+  // try to replace the '.' with a locale-specific radix character and
+  // try again.
+  std::string localized = LocalizeRadix(str, temp_endptr);
+  const char *localized_cstr = localized.c_str();
+  char *localized_endptr;
+  result = strtod(localized_cstr, &localized_endptr);
+  if ((localized_endptr - localized_cstr) > (temp_endptr - str)) {
+    // This attempt got further, so replacing the decimal must have helped.
+    // Update endptr to point at the right location.
+    if (endptr != NULL) {
+      // size_diff is non-zero if the localized radix has multiple bytes.
+      int size_diff = localized.size() - strlen(str);
+      // const_cast is necessary to match the strtod() interface.
+      *endptr = const_cast<char *>(
+          str + (localized_endptr - localized_cstr - size_diff));
+    }
+  }
+
+  return result;
+}
+
+}  // namespace internal
+
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/substitute.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/substitute.cpp
new file mode 100644
index 0000000..8c75b25
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/substitute.cpp
@@ -0,0 +1,136 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+
+#include <google/protobuf/stubs/substitute.h>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+namespace google {
+namespace protobuf {
+namespace strings {
+
+using internal::SubstituteArg;
+
+// Returns the number of args in arg_array which were passed explicitly
+// to Substitute().
+static int CountSubstituteArgs(const SubstituteArg* const* args_array) {
+  int count = 0;
+  while (args_array[count] != nullptr && args_array[count]->size() != -1) {
+    ++count;
+  }
+  return count;
+}
+
+std::string Substitute(const std::string& format, const SubstituteArg& arg0,
+                       const SubstituteArg& arg1, const SubstituteArg& arg2,
+                       const SubstituteArg& arg3, const SubstituteArg& arg4,
+                       const SubstituteArg& arg5, const SubstituteArg& arg6,
+                       const SubstituteArg& arg7, const SubstituteArg& arg8,
+                       const SubstituteArg& arg9) {
+  std::string result;
+  SubstituteAndAppend(&result, format.c_str(), arg0, arg1, arg2, arg3, arg4,
+                      arg5, arg6, arg7, arg8, arg9);
+  return result;
+}
+
+void SubstituteAndAppend(std::string* output, const char* format,
+                         const SubstituteArg& arg0, const SubstituteArg& arg1,
+                         const SubstituteArg& arg2, const SubstituteArg& arg3,
+                         const SubstituteArg& arg4, const SubstituteArg& arg5,
+                         const SubstituteArg& arg6, const SubstituteArg& arg7,
+                         const SubstituteArg& arg8, const SubstituteArg& arg9) {
+  const SubstituteArg* const args_array[] = {
+    &arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arg7, &arg8, &arg9, nullptr
+  };
+
+  // Determine total size needed.
+  int size = 0;
+  for (int i = 0; format[i] != '\0'; i++) {
+    if (format[i] == '$') {
+      if (ascii_isdigit(format[i+1])) {
+        int index = format[i+1] - '0';
+        if (args_array[index]->size() == -1) {
+          GOOGLE_LOG(DFATAL)
+            << "strings::Substitute format string invalid: asked for \"$"
+            << index << "\", but only " << CountSubstituteArgs(args_array)
+            << " args were given.  Full format string was: \""
+            << CEscape(format) << "\".";
+          return;
+        }
+        size += args_array[index]->size();
+        ++i;  // Skip next char.
+      } else if (format[i+1] == '$') {
+        ++size;
+        ++i;  // Skip next char.
+      } else {
+        GOOGLE_LOG(DFATAL)
+          << "Invalid strings::Substitute() format string: \""
+          << CEscape(format) << "\".";
+        return;
+      }
+    } else {
+      ++size;
+    }
+  }
+
+  if (size == 0) return;
+
+  // Build the string.
+  int original_size = output->size();
+  STLStringResizeUninitialized(output, original_size + size);
+  char* target = string_as_array(output) + original_size;
+  for (int i = 0; format[i] != '\0'; i++) {
+    if (format[i] == '$') {
+      if (ascii_isdigit(format[i+1])) {
+        unsigned int index = format[i+1] - '0';
+        assert(index < 10);
+        const SubstituteArg* src = args_array[index];
+        memcpy(target, src->data(), src->size());
+        target += src->size();
+        ++i;  // Skip next char.
+      } else if (format[i+1] == '$') {
+        *target++ = '$';
+        ++i;  // Skip next char.
+      }
+    } else {
+      *target++ = format[i];
+    }
+  }
+
+  GOOGLE_DCHECK_EQ(target - output->data(), static_cast<int>(output->size()));
+}
+
+}  // namespace strings
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/time.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/time.cpp
new file mode 100644
index 0000000..692cb82
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/stubs/time.cpp
@@ -0,0 +1,365 @@
+#include <google/protobuf/stubs/time.h>
+
+#include <ctime>
+
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+namespace {
+static const int64_t kSecondsPerMinute = 60;
+static const int64_t kSecondsPerHour = 3600;
+static const int64_t kSecondsPerDay = kSecondsPerHour * 24;
+static const int64_t kSecondsPer400Years =
+    kSecondsPerDay * (400 * 365 + 400 / 4 - 3);
+// Seconds from 0001-01-01T00:00:00 to 1970-01-01T:00:00:00
+static const int64_t kSecondsFromEraToEpoch = 62135596800LL;
+// The range of timestamp values we support.
+static const int64_t kMinTime = -62135596800LL;  // 0001-01-01T00:00:00
+static const int64_t kMaxTime = 253402300799LL;  // 9999-12-31T23:59:59
+
+static const int kNanosPerMillisecond = 1000000;
+static const int kNanosPerMicrosecond = 1000;
+
+// Count the seconds from the given year (start at Jan 1, 00:00) to 100 years
+// after.
+int64_t SecondsPer100Years(int year) {
+  if (year % 400 == 0 || year % 400 > 300) {
+    return kSecondsPerDay * (100 * 365 + 100 / 4);
+  } else {
+    return kSecondsPerDay * (100 * 365 + 100 / 4 - 1);
+  }
+}
+
+// Count the seconds from the given year (start at Jan 1, 00:00) to 4 years
+// after.
+int64_t SecondsPer4Years(int year) {
+  if ((year % 100 == 0 || year % 100 > 96) &&
+      !(year % 400 == 0 || year % 400 > 396)) {
+    // No leap years.
+    return kSecondsPerDay * (4 * 365);
+  } else {
+    // One leap years.
+    return kSecondsPerDay * (4 * 365 + 1);
+  }
+}
+
+bool IsLeapYear(int year) {
+  return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
+}
+
+int64_t SecondsPerYear(int year) {
+  return kSecondsPerDay * (IsLeapYear(year) ? 366 : 365);
+}
+
+static const int kDaysInMonth[13] = {
+  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+int64_t SecondsPerMonth(int month, bool leap) {
+  if (month == 2 && leap) {
+    return kSecondsPerDay * (kDaysInMonth[month] + 1);
+  }
+  return kSecondsPerDay * kDaysInMonth[month];
+}
+
+static const int kDaysSinceJan[13] = {
+  0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+};
+
+bool ValidateDateTime(const DateTime& time) {
+  if (time.year < 1 || time.year > 9999 ||
+      time.month < 1 || time.month > 12 ||
+      time.day < 1 || time.day > 31 ||
+      time.hour < 0 || time.hour > 23 ||
+      time.minute < 0 || time.minute > 59 ||
+      time.second < 0 || time.second > 59) {
+    return false;
+  }
+  if (time.month == 2 && IsLeapYear(time.year)) {
+    return time.day <= kDaysInMonth[time.month] + 1;
+  } else {
+    return time.day <= kDaysInMonth[time.month];
+  }
+}
+
+// Count the number of seconds elapsed from 0001-01-01T00:00:00 to the given
+// time.
+int64_t SecondsSinceCommonEra(const DateTime& time) {
+  int64_t result = 0;
+  // Years should be between 1 and 9999.
+  assert(time.year >= 1 && time.year <= 9999);
+  int year = 1;
+  if ((time.year - year) >= 400) {
+    int count_400years = (time.year - year) / 400;
+    result += kSecondsPer400Years * count_400years;
+    year += count_400years * 400;
+  }
+  while ((time.year - year) >= 100) {
+    result += SecondsPer100Years(year);
+    year += 100;
+  }
+  while ((time.year - year) >= 4) {
+    result += SecondsPer4Years(year);
+    year += 4;
+  }
+  while (time.year > year) {
+    result += SecondsPerYear(year);
+    ++year;
+  }
+  // Months should be between 1 and 12.
+  assert(time.month >= 1 && time.month <= 12);
+  int month = time.month;
+  result += kSecondsPerDay * kDaysSinceJan[month];
+  if (month > 2 && IsLeapYear(year)) {
+    result += kSecondsPerDay;
+  }
+  assert(time.day >= 1 &&
+         time.day <= (month == 2 && IsLeapYear(year)
+                          ? kDaysInMonth[month] + 1
+                          : kDaysInMonth[month]));
+  result += kSecondsPerDay * (time.day - 1);
+  result += kSecondsPerHour * time.hour +
+      kSecondsPerMinute * time.minute +
+      time.second;
+  return result;
+}
+
+// Format nanoseconds with either 3, 6, or 9 digits depending on the required
+// precision to represent the exact value.
+std::string FormatNanos(int32_t nanos) {
+  if (nanos % kNanosPerMillisecond == 0) {
+    return StringPrintf("%03d", nanos / kNanosPerMillisecond);
+  } else if (nanos % kNanosPerMicrosecond == 0) {
+    return StringPrintf("%06d", nanos / kNanosPerMicrosecond);
+  } else {
+    return StringPrintf("%09d", nanos);
+  }
+}
+
+// Parses an integer from a null-terminated char sequence. The method
+// consumes at most "width" chars. Returns a pointer after the consumed
+// integer, or nullptr if the data does not start with an integer or the
+// integer value does not fall in the range of [min_value, max_value].
+const char* ParseInt(const char* data, int width, int min_value,
+                     int max_value, int* result) {
+  if (!ascii_isdigit(*data)) {
+    return nullptr;
+  }
+  int value = 0;
+  for (int i = 0; i < width; ++i, ++data) {
+    if (ascii_isdigit(*data)) {
+      value = value * 10 + (*data - '0');
+    } else {
+      break;
+    }
+  }
+  if (value >= min_value && value <= max_value) {
+    *result = value;
+    return data;
+  } else {
+    return nullptr;
+  }
+}
+
+// Consumes the fractional parts of a second into nanos. For example,
+// "010" will be parsed to 10000000 nanos.
+const char* ParseNanos(const char* data, int32_t* nanos) {
+  if (!ascii_isdigit(*data)) {
+    return nullptr;
+  }
+  int value = 0;
+  int len = 0;
+  // Consume as many digits as there are but only take the first 9 into
+  // account.
+  while (ascii_isdigit(*data)) {
+    if (len < 9) {
+      value = value * 10 + *data - '0';
+    }
+    ++len;
+    ++data;
+  }
+  while (len < 9) {
+    value = value * 10;
+    ++len;
+  }
+  *nanos = value;
+  return data;
+}
+
+const char* ParseTimezoneOffset(const char* data, int64_t* offset) {
+  // Accept format "HH:MM". E.g., "08:00"
+  int hour;
+  if ((data = ParseInt(data, 2, 0, 23, &hour)) == nullptr) {
+    return nullptr;
+  }
+  if (*data++ != ':') {
+    return nullptr;
+  }
+  int minute;
+  if ((data = ParseInt(data, 2, 0, 59, &minute)) == nullptr) {
+    return nullptr;
+  }
+  *offset = (hour * 60 + minute) * 60;
+  return data;
+}
+}  // namespace
+
+bool SecondsToDateTime(int64_t seconds, DateTime* time) {
+  if (seconds < kMinTime || seconds > kMaxTime) {
+    return false;
+  }
+  // It's easier to calculate the DateTime starting from 0001-01-01T00:00:00
+  seconds = seconds + kSecondsFromEraToEpoch;
+  int year = 1;
+  if (seconds >= kSecondsPer400Years) {
+    int count_400years = seconds / kSecondsPer400Years;
+    year += 400 * count_400years;
+    seconds %= kSecondsPer400Years;
+  }
+  while (seconds >= SecondsPer100Years(year)) {
+    seconds -= SecondsPer100Years(year);
+    year += 100;
+  }
+  while (seconds >= SecondsPer4Years(year)) {
+    seconds -= SecondsPer4Years(year);
+    year += 4;
+  }
+  while (seconds >= SecondsPerYear(year)) {
+    seconds -= SecondsPerYear(year);
+    year += 1;
+  }
+  bool leap = IsLeapYear(year);
+  int month = 1;
+  while (seconds >= SecondsPerMonth(month, leap)) {
+    seconds -= SecondsPerMonth(month, leap);
+    ++month;
+  }
+  int day = 1 + seconds / kSecondsPerDay;
+  seconds %= kSecondsPerDay;
+  int hour = seconds / kSecondsPerHour;
+  seconds %= kSecondsPerHour;
+  int minute = seconds / kSecondsPerMinute;
+  seconds %= kSecondsPerMinute;
+  time->year = year;
+  time->month = month;
+  time->day = day;
+  time->hour = hour;
+  time->minute = minute;
+  time->second = static_cast<int>(seconds);
+  return true;
+}
+
+bool DateTimeToSeconds(const DateTime& time, int64_t* seconds) {
+  if (!ValidateDateTime(time)) {
+    return false;
+  }
+  *seconds = SecondsSinceCommonEra(time) - kSecondsFromEraToEpoch;
+  return true;
+}
+
+void GetCurrentTime(int64_t* seconds, int32_t* nanos) {
+  // TODO(xiaofeng): Improve the accuracy of this implementation (or just
+  // remove this method from protobuf).
+  *seconds = time(nullptr);
+  *nanos = 0;
+}
+
+std::string FormatTime(int64_t seconds, int32_t nanos) {
+  DateTime time;
+  if (nanos < 0 || nanos > 999999999 || !SecondsToDateTime(seconds, &time)) {
+    return "InvalidTime";
+  }
+  std::string result =
+      StringPrintf("%04d-%02d-%02dT%02d:%02d:%02d", time.year, time.month,
+                   time.day, time.hour, time.minute, time.second);
+  if (nanos != 0) {
+    result += "." + FormatNanos(nanos);
+  }
+  return result + "Z";
+}
+
+bool ParseTime(const std::string& value, int64_t* seconds, int32_t* nanos) {
+  DateTime time;
+  const char* data = value.c_str();
+  // We only accept:
+  //   Z-normalized: 2015-05-20T13:29:35.120Z
+  //   With UTC offset: 2015-05-20T13:29:35.120-08:00
+
+  // Parse year
+  if ((data = ParseInt(data, 4, 1, 9999, &time.year)) == nullptr) {
+    return false;
+  }
+  // Expect '-'
+  if (*data++ != '-') return false;
+  // Parse month
+  if ((data = ParseInt(data, 2, 1, 12, &time.month)) == nullptr) {
+    return false;
+  }
+  // Expect '-'
+  if (*data++ != '-') return false;
+  // Parse day
+  if ((data = ParseInt(data, 2, 1, 31, &time.day)) == nullptr) {
+    return false;
+  }
+  // Expect 'T'
+  if (*data++ != 'T') return false;
+  // Parse hour
+  if ((data = ParseInt(data, 2, 0, 23, &time.hour)) == nullptr) {
+    return false;
+  }
+  // Expect ':'
+  if (*data++ != ':') return false;
+  // Parse minute
+  if ((data = ParseInt(data, 2, 0, 59, &time.minute)) == nullptr) {
+    return false;
+  }
+  // Expect ':'
+  if (*data++ != ':') return false;
+  // Parse second
+  if ((data = ParseInt(data, 2, 0, 59, &time.second)) == nullptr) {
+    return false;
+  }
+  if (!DateTimeToSeconds(time, seconds)) {
+    return false;
+  }
+  // Parse nanoseconds.
+  if (*data == '.') {
+    ++data;
+    // Parse nanoseconds.
+    if ((data = ParseNanos(data, nanos)) == nullptr) {
+      return false;
+    }
+  } else {
+    *nanos = 0;
+  }
+  // Parse UTC offsets.
+  if (*data == 'Z') {
+    ++data;
+  } else if (*data == '+') {
+    ++data;
+    int64_t offset;
+    if ((data = ParseTimezoneOffset(data, &offset)) == nullptr) {
+      return false;
+    }
+    *seconds -= offset;
+  } else if (*data == '-') {
+    ++data;
+    int64_t offset;
+    if ((data = ParseTimezoneOffset(data, &offset)) == nullptr) {
+      return false;
+    }
+    *seconds += offset;
+  } else {
+    return false;
+  }
+  // Done with parsing.
+  return *data == 0;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/text_format.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/text_format.cpp
new file mode 100644
index 0000000..05987d3
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/text_format.cpp
@@ -0,0 +1,2746 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: jschorr@google.com (Joseph Schorr)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/text_format.h>
+
+#include <float.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <atomic>
+#include <climits>
+#include <cmath>
+#include <limits>
+#include <utility>
+#include <vector>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/tokenizer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/any.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/io/strtod.h>
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+namespace {
+
+inline bool IsHexNumber(const std::string& str) {
+  return (str.length() >= 2 && str[0] == '0' &&
+          (str[1] == 'x' || str[1] == 'X'));
+}
+
+inline bool IsOctNumber(const std::string& str) {
+  return (str.length() >= 2 && str[0] == '0' &&
+          (str[1] >= '0' && str[1] < '8'));
+}
+
+}  // namespace
+
+namespace internal {
+const char kDebugStringSilentMarker[] = "";
+const char kDebugStringSilentMarkerForDetection[] = "\t ";
+
+// Controls insertion of kDebugStringSilentMarker.
+PROTOBUF_EXPORT std::atomic<bool> enable_debug_text_format_marker;
+}  // namespace internal
+
+std::string Message::DebugString() const {
+  std::string debug_string;
+
+  TextFormat::Printer printer;
+  printer.SetExpandAny(true);
+  printer.SetInsertSilentMarker(internal::enable_debug_text_format_marker.load(
+      std::memory_order_relaxed));
+
+  printer.PrintToString(*this, &debug_string);
+
+  return debug_string;
+}
+
+std::string Message::ShortDebugString() const {
+  std::string debug_string;
+
+  TextFormat::Printer printer;
+  printer.SetSingleLineMode(true);
+  printer.SetExpandAny(true);
+  printer.SetInsertSilentMarker(internal::enable_debug_text_format_marker.load(
+      std::memory_order_relaxed));
+
+  printer.PrintToString(*this, &debug_string);
+  // Single line mode currently might have an extra space at the end.
+  if (!debug_string.empty() && debug_string[debug_string.size() - 1] == ' ') {
+    debug_string.resize(debug_string.size() - 1);
+  }
+
+  return debug_string;
+}
+
+std::string Message::Utf8DebugString() const {
+  std::string debug_string;
+
+  TextFormat::Printer printer;
+  printer.SetUseUtf8StringEscaping(true);
+  printer.SetExpandAny(true);
+  printer.SetInsertSilentMarker(internal::enable_debug_text_format_marker.load(
+      std::memory_order_relaxed));
+
+  printer.PrintToString(*this, &debug_string);
+
+  return debug_string;
+}
+
+void Message::PrintDebugString() const { printf("%s", DebugString().c_str()); }
+
+
+// ===========================================================================
+// Implementation of the parse information tree class.
+void TextFormat::ParseInfoTree::RecordLocation(
+    const FieldDescriptor* field, TextFormat::ParseLocationRange range) {
+  locations_[field].push_back(range);
+}
+
+TextFormat::ParseInfoTree* TextFormat::ParseInfoTree::CreateNested(
+    const FieldDescriptor* field) {
+  // Owned by us in the map.
+  auto& vec = nested_[field];
+  vec.emplace_back(new TextFormat::ParseInfoTree());
+  return vec.back().get();
+}
+
+void CheckFieldIndex(const FieldDescriptor* field, int index) {
+  if (field == nullptr) {
+    return;
+  }
+
+  if (field->is_repeated() && index == -1) {
+    GOOGLE_LOG(DFATAL) << "Index must be in range of repeated field values. "
+                << "Field: " << field->name();
+  } else if (!field->is_repeated() && index != -1) {
+    GOOGLE_LOG(DFATAL) << "Index must be -1 for singular fields."
+                << "Field: " << field->name();
+  }
+}
+
+TextFormat::ParseLocationRange TextFormat::ParseInfoTree::GetLocationRange(
+    const FieldDescriptor* field, int index) const {
+  CheckFieldIndex(field, index);
+  if (index == -1) {
+    index = 0;
+  }
+
+  const std::vector<TextFormat::ParseLocationRange>* locations =
+      FindOrNull(locations_, field);
+  if (locations == nullptr ||
+      index >= static_cast<int64_t>(locations->size())) {
+    return TextFormat::ParseLocationRange();
+  }
+
+  return (*locations)[index];
+}
+
+TextFormat::ParseInfoTree* TextFormat::ParseInfoTree::GetTreeForNested(
+    const FieldDescriptor* field, int index) const {
+  CheckFieldIndex(field, index);
+  if (index == -1) {
+    index = 0;
+  }
+
+  auto it = nested_.find(field);
+  if (it == nested_.end() || index >= static_cast<int64_t>(it->second.size())) {
+    return nullptr;
+  }
+
+  return it->second[index].get();
+}
+
+namespace {
+// These functions implement the behavior of the "default" TextFormat::Finder,
+// they are defined as standalone to be called when finder_ is nullptr.
+const FieldDescriptor* DefaultFinderFindExtension(Message* message,
+                                                  const std::string& name) {
+  const Descriptor* descriptor = message->GetDescriptor();
+  return descriptor->file()->pool()->FindExtensionByPrintableName(descriptor,
+                                                                  name);
+}
+
+const FieldDescriptor* DefaultFinderFindExtensionByNumber(
+    const Descriptor* descriptor, int number) {
+  return descriptor->file()->pool()->FindExtensionByNumber(descriptor, number);
+}
+
+const Descriptor* DefaultFinderFindAnyType(const Message& message,
+                                           const std::string& prefix,
+                                           const std::string& name) {
+  if (prefix != internal::kTypeGoogleApisComPrefix &&
+      prefix != internal::kTypeGoogleProdComPrefix) {
+    return nullptr;
+  }
+  return message.GetDescriptor()->file()->pool()->FindMessageTypeByName(name);
+}
+}  // namespace
+
+// ===========================================================================
+// Internal class for parsing an ASCII representation of a Protocol Message.
+// This class makes use of the Protocol Message compiler's tokenizer found
+// in //net/proto2/io/public/tokenizer.h. Note that class's Parse
+// method is *not* thread-safe and should only be used in a single thread at
+// a time.
+
+// Makes code slightly more readable.  The meaning of "DO(foo)" is
+// "Execute foo and fail if it fails.", where failure is indicated by
+// returning false. Borrowed from parser.cc (Thanks Kenton!).
+#define DO(STATEMENT) \
+  if (STATEMENT) {    \
+  } else {            \
+    return false;     \
+  }
+
+class TextFormat::Parser::ParserImpl {
+ public:
+  // Determines if repeated values for non-repeated fields and
+  // oneofs are permitted, e.g., the string "foo: 1 foo: 2" for a
+  // required/optional field named "foo", or "baz: 1 bar: 2"
+  // where "baz" and "bar" are members of the same oneof.
+  enum SingularOverwritePolicy {
+    ALLOW_SINGULAR_OVERWRITES = 0,   // the last value is retained
+    FORBID_SINGULAR_OVERWRITES = 1,  // an error is issued
+  };
+
+  ParserImpl(const Descriptor* root_message_type,
+             io::ZeroCopyInputStream* input_stream,
+             io::ErrorCollector* error_collector,
+             const TextFormat::Finder* finder, ParseInfoTree* parse_info_tree,
+             SingularOverwritePolicy singular_overwrite_policy,
+             bool allow_case_insensitive_field, bool allow_unknown_field,
+             bool allow_unknown_extension, bool allow_unknown_enum,
+             bool allow_field_number, bool allow_relaxed_whitespace,
+             bool allow_partial, int recursion_limit)
+      : error_collector_(error_collector),
+        finder_(finder),
+        parse_info_tree_(parse_info_tree),
+        tokenizer_error_collector_(this),
+        tokenizer_(input_stream, &tokenizer_error_collector_),
+        root_message_type_(root_message_type),
+        singular_overwrite_policy_(singular_overwrite_policy),
+        allow_case_insensitive_field_(allow_case_insensitive_field),
+        allow_unknown_field_(allow_unknown_field),
+        allow_unknown_extension_(allow_unknown_extension),
+        allow_unknown_enum_(allow_unknown_enum),
+        allow_field_number_(allow_field_number),
+        allow_partial_(allow_partial),
+        initial_recursion_limit_(recursion_limit),
+        recursion_limit_(recursion_limit),
+        had_silent_marker_(false),
+        had_errors_(false) {
+    // For backwards-compatibility with proto1, we need to allow the 'f' suffix
+    // for floats.
+    tokenizer_.set_allow_f_after_float(true);
+
+    // '#' starts a comment.
+    tokenizer_.set_comment_style(io::Tokenizer::SH_COMMENT_STYLE);
+
+    if (allow_relaxed_whitespace) {
+      tokenizer_.set_require_space_after_number(false);
+      tokenizer_.set_allow_multiline_strings(true);
+    }
+
+    // Consume the starting token.
+    tokenizer_.Next();
+  }
+  ~ParserImpl() {}
+
+  // Parses the ASCII representation specified in input and saves the
+  // information into the output pointer (a Message). Returns
+  // false if an error occurs (an error will also be logged to
+  // GOOGLE_LOG(ERROR)).
+  bool Parse(Message* output) {
+    // Consume fields until we cannot do so anymore.
+    while (true) {
+      if (LookingAtType(io::Tokenizer::TYPE_END)) {
+        // Ensures recursion limit properly unwinded, but only for success
+        // cases. This implicitly avoids the check when `Parse` returns false
+        // via `DO(...)`.
+        GOOGLE_DCHECK(had_errors_ || recursion_limit_ == initial_recursion_limit_)
+            << "Recursion limit at end of parse should be "
+            << initial_recursion_limit_ << ", but was " << recursion_limit_
+            << ". Difference of " << initial_recursion_limit_ - recursion_limit_
+            << " stack frames not accounted for stack unwind.";
+
+        return !had_errors_;
+      }
+
+      DO(ConsumeField(output));
+    }
+  }
+
+  bool ParseField(const FieldDescriptor* field, Message* output) {
+    bool suc;
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      suc = ConsumeFieldMessage(output, output->GetReflection(), field);
+    } else {
+      suc = ConsumeFieldValue(output, output->GetReflection(), field);
+    }
+    return suc && LookingAtType(io::Tokenizer::TYPE_END);
+  }
+
+  void ReportError(int line, int col, const std::string& message) {
+    had_errors_ = true;
+    if (error_collector_ == nullptr) {
+      if (line >= 0) {
+        GOOGLE_LOG(ERROR) << "Error parsing text-format "
+                   << root_message_type_->full_name() << ": " << (line + 1)
+                   << ":" << (col + 1) << ": " << message;
+      } else {
+        GOOGLE_LOG(ERROR) << "Error parsing text-format "
+                   << root_message_type_->full_name() << ": " << message;
+      }
+    } else {
+      error_collector_->AddError(line, col, message);
+    }
+  }
+
+  void ReportWarning(int line, int col, const std::string& message) {
+    if (error_collector_ == nullptr) {
+      if (line >= 0) {
+        GOOGLE_LOG(WARNING) << "Warning parsing text-format "
+                     << root_message_type_->full_name() << ": " << (line + 1)
+                     << ":" << (col + 1) << ": " << message;
+      } else {
+        GOOGLE_LOG(WARNING) << "Warning parsing text-format "
+                     << root_message_type_->full_name() << ": " << message;
+      }
+    } else {
+      error_collector_->AddWarning(line, col, message);
+    }
+  }
+
+ private:
+  static constexpr int32_t kint32max = std::numeric_limits<int32_t>::max();
+  static constexpr uint32_t kuint32max = std::numeric_limits<uint32_t>::max();
+  static constexpr int64_t kint64min = std::numeric_limits<int64_t>::min();
+  static constexpr int64_t kint64max = std::numeric_limits<int64_t>::max();
+  static constexpr uint64_t kuint64max = std::numeric_limits<uint64_t>::max();
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ParserImpl);
+
+  // Reports an error with the given message with information indicating
+  // the position (as derived from the current token).
+  void ReportError(const std::string& message) {
+    ReportError(tokenizer_.current().line, tokenizer_.current().column,
+                message);
+  }
+
+  // Reports a warning with the given message with information indicating
+  // the position (as derived from the current token).
+  void ReportWarning(const std::string& message) {
+    ReportWarning(tokenizer_.current().line, tokenizer_.current().column,
+                  message);
+  }
+
+  // Consumes the specified message with the given starting delimiter.
+  // This method checks to see that the end delimiter at the conclusion of
+  // the consumption matches the starting delimiter passed in here.
+  bool ConsumeMessage(Message* message, const std::string delimiter) {
+    while (!LookingAt(">") && !LookingAt("}")) {
+      DO(ConsumeField(message));
+    }
+
+    // Confirm that we have a valid ending delimiter.
+    DO(Consume(delimiter));
+    return true;
+  }
+
+  // Consume either "<" or "{".
+  bool ConsumeMessageDelimiter(std::string* delimiter) {
+    if (TryConsume("<")) {
+      *delimiter = ">";
+    } else {
+      DO(Consume("{"));
+      *delimiter = "}";
+    }
+    return true;
+  }
+
+
+  // Consumes the current field (as returned by the tokenizer) on the
+  // passed in message.
+  bool ConsumeField(Message* message) {
+    const Reflection* reflection = message->GetReflection();
+    const Descriptor* descriptor = message->GetDescriptor();
+
+    std::string field_name;
+    bool reserved_field = false;
+    const FieldDescriptor* field = nullptr;
+    int start_line = tokenizer_.current().line;
+    int start_column = tokenizer_.current().column;
+
+    const FieldDescriptor* any_type_url_field;
+    const FieldDescriptor* any_value_field;
+    if (internal::GetAnyFieldDescriptors(*message, &any_type_url_field,
+                                         &any_value_field) &&
+        TryConsume("[")) {
+      std::string full_type_name, prefix;
+      DO(ConsumeAnyTypeUrl(&full_type_name, &prefix));
+      std::string prefix_and_full_type_name =
+          StrCat(prefix, full_type_name);
+      DO(ConsumeBeforeWhitespace("]"));
+      TryConsumeWhitespace();
+      // ':' is optional between message labels and values.
+      if (TryConsumeBeforeWhitespace(":")) {
+        TryConsumeWhitespace();
+      }
+      std::string serialized_value;
+      const Descriptor* value_descriptor =
+          finder_ ? finder_->FindAnyType(*message, prefix, full_type_name)
+                  : DefaultFinderFindAnyType(*message, prefix, full_type_name);
+      if (value_descriptor == nullptr) {
+        ReportError("Could not find type \"" + prefix_and_full_type_name +
+                    "\" stored in google.protobuf.Any.");
+        return false;
+      }
+      DO(ConsumeAnyValue(value_descriptor, &serialized_value));
+      if (singular_overwrite_policy_ == FORBID_SINGULAR_OVERWRITES) {
+        // Fail if any_type_url_field has already been specified.
+        if ((!any_type_url_field->is_repeated() &&
+             reflection->HasField(*message, any_type_url_field)) ||
+            (!any_value_field->is_repeated() &&
+             reflection->HasField(*message, any_value_field))) {
+          ReportError("Non-repeated Any specified multiple times.");
+          return false;
+        }
+      }
+      reflection->SetString(message, any_type_url_field,
+                            std::move(prefix_and_full_type_name));
+      reflection->SetString(message, any_value_field,
+                            std::move(serialized_value));
+      return true;
+    }
+    if (TryConsume("[")) {
+      // Extension.
+      DO(ConsumeFullTypeName(&field_name));
+      DO(ConsumeBeforeWhitespace("]"));
+      TryConsumeWhitespace();
+
+      field = finder_ ? finder_->FindExtension(message, field_name)
+                      : DefaultFinderFindExtension(message, field_name);
+
+      if (field == nullptr) {
+        if (!allow_unknown_field_ && !allow_unknown_extension_) {
+          ReportError("Extension \"" + field_name +
+                      "\" is not defined or "
+                      "is not an extension of \"" +
+                      descriptor->full_name() + "\".");
+          return false;
+        } else {
+          ReportWarning("Ignoring extension \"" + field_name +
+                        "\" which is not defined or is not an extension of \"" +
+                        descriptor->full_name() + "\".");
+        }
+      }
+    } else {
+      DO(ConsumeIdentifierBeforeWhitespace(&field_name));
+      TryConsumeWhitespace();
+
+      int32_t field_number;
+      if (allow_field_number_ && safe_strto32(field_name, &field_number)) {
+        if (descriptor->IsExtensionNumber(field_number)) {
+          field = finder_
+                      ? finder_->FindExtensionByNumber(descriptor, field_number)
+                      : DefaultFinderFindExtensionByNumber(descriptor,
+                                                           field_number);
+        } else if (descriptor->IsReservedNumber(field_number)) {
+          reserved_field = true;
+        } else {
+          field = descriptor->FindFieldByNumber(field_number);
+        }
+      } else {
+        field = descriptor->FindFieldByName(field_name);
+        // Group names are expected to be capitalized as they appear in the
+        // .proto file, which actually matches their type names, not their
+        // field names.
+        if (field == nullptr) {
+          std::string lower_field_name = field_name;
+          LowerString(&lower_field_name);
+          field = descriptor->FindFieldByName(lower_field_name);
+          // If the case-insensitive match worked but the field is NOT a group,
+          if (field != nullptr &&
+              field->type() != FieldDescriptor::TYPE_GROUP) {
+            field = nullptr;
+          }
+        }
+        // Again, special-case group names as described above.
+        if (field != nullptr && field->type() == FieldDescriptor::TYPE_GROUP &&
+            field->message_type()->name() != field_name) {
+          field = nullptr;
+        }
+
+        if (field == nullptr && allow_case_insensitive_field_) {
+          std::string lower_field_name = field_name;
+          LowerString(&lower_field_name);
+          field = descriptor->FindFieldByLowercaseName(lower_field_name);
+        }
+
+        if (field == nullptr) {
+          reserved_field = descriptor->IsReservedName(field_name);
+        }
+      }
+
+      if (field == nullptr && !reserved_field) {
+        if (!allow_unknown_field_) {
+          ReportError("Message type \"" + descriptor->full_name() +
+                      "\" has no field named \"" + field_name + "\".");
+          return false;
+        } else {
+          ReportWarning("Message type \"" + descriptor->full_name() +
+                        "\" has no field named \"" + field_name + "\".");
+        }
+      }
+    }
+
+    // Skips unknown or reserved fields.
+    if (field == nullptr) {
+      GOOGLE_CHECK(allow_unknown_field_ || allow_unknown_extension_ || reserved_field);
+
+      // Try to guess the type of this field.
+      // If this field is not a message, there should be a ":" between the
+      // field name and the field value and also the field value should not
+      // start with "{" or "<" which indicates the beginning of a message body.
+      // If there is no ":" or there is a "{" or "<" after ":", this field has
+      // to be a message or the input is ill-formed.
+      if (TryConsumeBeforeWhitespace(":")) {
+        TryConsumeWhitespace();
+        if (!LookingAt("{") && !LookingAt("<")) {
+          return SkipFieldValue();
+        }
+      }
+      return SkipFieldMessage();
+    }
+
+    if (singular_overwrite_policy_ == FORBID_SINGULAR_OVERWRITES) {
+      // Fail if the field is not repeated and it has already been specified.
+      if (!field->is_repeated() && reflection->HasField(*message, field)) {
+        ReportError("Non-repeated field \"" + field_name +
+                    "\" is specified multiple times.");
+        return false;
+      }
+      // Fail if the field is a member of a oneof and another member has already
+      // been specified.
+      const OneofDescriptor* oneof = field->containing_oneof();
+      if (oneof != nullptr && reflection->HasOneof(*message, oneof)) {
+        const FieldDescriptor* other_field =
+            reflection->GetOneofFieldDescriptor(*message, oneof);
+        ReportError("Field \"" + field_name +
+                    "\" is specified along with "
+                    "field \"" +
+                    other_field->name() +
+                    "\", another member "
+                    "of oneof \"" +
+                    oneof->name() + "\".");
+        return false;
+      }
+    }
+
+    // Perform special handling for embedded message types.
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      // ':' is optional here.
+      bool consumed_semicolon = TryConsumeBeforeWhitespace(":");
+      if (consumed_semicolon) {
+        TryConsumeWhitespace();
+      }
+      if (consumed_semicolon && field->options().weak() &&
+          LookingAtType(io::Tokenizer::TYPE_STRING)) {
+        // we are getting a bytes string for a weak field.
+        std::string tmp;
+        DO(ConsumeString(&tmp));
+        MessageFactory* factory =
+            finder_ ? finder_->FindExtensionFactory(field) : nullptr;
+        reflection->MutableMessage(message, field, factory)
+            ->ParseFromString(tmp);
+        goto label_skip_parsing;
+      }
+    } else {
+      // ':' is required here.
+      DO(ConsumeBeforeWhitespace(":"));
+      TryConsumeWhitespace();
+    }
+
+    if (field->is_repeated() && TryConsume("[")) {
+      // Short repeated format, e.g.  "foo: [1, 2, 3]".
+      if (!TryConsume("]")) {
+        // "foo: []" is treated as empty.
+        while (true) {
+          if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+            // Perform special handling for embedded message types.
+            DO(ConsumeFieldMessage(message, reflection, field));
+          } else {
+            DO(ConsumeFieldValue(message, reflection, field));
+          }
+          if (TryConsume("]")) {
+            break;
+          }
+          DO(Consume(","));
+        }
+      }
+    } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      DO(ConsumeFieldMessage(message, reflection, field));
+    } else {
+      DO(ConsumeFieldValue(message, reflection, field));
+    }
+  label_skip_parsing:
+    // For historical reasons, fields may optionally be separated by commas or
+    // semicolons.
+    TryConsume(";") || TryConsume(",");
+
+    if (field->options().deprecated()) {
+      ReportWarning("text format contains deprecated field \"" + field_name +
+                    "\"");
+    }
+
+    // If a parse info tree exists, add the location for the parsed
+    // field.
+    if (parse_info_tree_ != nullptr) {
+      int end_line = tokenizer_.previous().line;
+      int end_column = tokenizer_.previous().end_column;
+
+      RecordLocation(parse_info_tree_, field,
+                     ParseLocationRange(ParseLocation(start_line, start_column),
+                                        ParseLocation(end_line, end_column)));
+    }
+
+    return true;
+  }
+
+  // Skips the next field including the field's name and value.
+  bool SkipField() {
+    std::string field_name;
+    if (TryConsume("[")) {
+      // Extension name or type URL.
+      DO(ConsumeTypeUrlOrFullTypeName(&field_name));
+      DO(ConsumeBeforeWhitespace("]"));
+    } else {
+      DO(ConsumeIdentifierBeforeWhitespace(&field_name));
+    }
+    TryConsumeWhitespace();
+
+    // Try to guess the type of this field.
+    // If this field is not a message, there should be a ":" between the
+    // field name and the field value and also the field value should not
+    // start with "{" or "<" which indicates the beginning of a message body.
+    // If there is no ":" or there is a "{" or "<" after ":", this field has
+    // to be a message or the input is ill-formed.
+    if (TryConsumeBeforeWhitespace(":")) {
+      TryConsumeWhitespace();
+      if (!LookingAt("{") && !LookingAt("<")) {
+        DO(SkipFieldValue());
+      } else {
+        DO(SkipFieldMessage());
+      }
+    } else {
+      DO(SkipFieldMessage());
+    }
+    // For historical reasons, fields may optionally be separated by commas or
+    // semicolons.
+    TryConsume(";") || TryConsume(",");
+    return true;
+  }
+
+  bool ConsumeFieldMessage(Message* message, const Reflection* reflection,
+                           const FieldDescriptor* field) {
+    if (--recursion_limit_ < 0) {
+      ReportError(
+          StrCat("Message is too deep, the parser exceeded the "
+                       "configured recursion limit of ",
+                       initial_recursion_limit_, "."));
+      return false;
+    }
+    // If the parse information tree is not nullptr, create a nested one
+    // for the nested message.
+    ParseInfoTree* parent = parse_info_tree_;
+    if (parent != nullptr) {
+      parse_info_tree_ = CreateNested(parent, field);
+    }
+
+    std::string delimiter;
+    DO(ConsumeMessageDelimiter(&delimiter));
+    MessageFactory* factory =
+        finder_ ? finder_->FindExtensionFactory(field) : nullptr;
+    if (field->is_repeated()) {
+      DO(ConsumeMessage(reflection->AddMessage(message, field, factory),
+                        delimiter));
+    } else {
+      DO(ConsumeMessage(reflection->MutableMessage(message, field, factory),
+                        delimiter));
+    }
+
+    ++recursion_limit_;
+
+    // Reset the parse information tree.
+    parse_info_tree_ = parent;
+    return true;
+  }
+
+  // Skips the whole body of a message including the beginning delimiter and
+  // the ending delimiter.
+  bool SkipFieldMessage() {
+    if (--recursion_limit_ < 0) {
+      ReportError(
+          StrCat("Message is too deep, the parser exceeded the "
+                       "configured recursion limit of ",
+                       initial_recursion_limit_, "."));
+      return false;
+    }
+
+    std::string delimiter;
+    DO(ConsumeMessageDelimiter(&delimiter));
+    while (!LookingAt(">") && !LookingAt("}")) {
+      DO(SkipField());
+    }
+    DO(Consume(delimiter));
+
+    ++recursion_limit_;
+    return true;
+  }
+
+  bool ConsumeFieldValue(Message* message, const Reflection* reflection,
+                         const FieldDescriptor* field) {
+// Define an easy to use macro for setting fields. This macro checks
+// to see if the field is repeated (in which case we need to use the Add
+// methods or not (in which case we need to use the Set methods).
+#define SET_FIELD(CPPTYPE, VALUE)                    \
+  if (field->is_repeated()) {                        \
+    reflection->Add##CPPTYPE(message, field, VALUE); \
+  } else {                                           \
+    reflection->Set##CPPTYPE(message, field, VALUE); \
+  }
+
+    switch (field->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_INT32: {
+        int64_t value;
+        DO(ConsumeSignedInteger(&value, kint32max));
+        SET_FIELD(Int32, static_cast<int32_t>(value));
+        break;
+      }
+
+      case FieldDescriptor::CPPTYPE_UINT32: {
+        uint64_t value;
+        DO(ConsumeUnsignedInteger(&value, kuint32max));
+        SET_FIELD(UInt32, static_cast<uint32_t>(value));
+        break;
+      }
+
+      case FieldDescriptor::CPPTYPE_INT64: {
+        int64_t value;
+        DO(ConsumeSignedInteger(&value, kint64max));
+        SET_FIELD(Int64, value);
+        break;
+      }
+
+      case FieldDescriptor::CPPTYPE_UINT64: {
+        uint64_t value;
+        DO(ConsumeUnsignedInteger(&value, kuint64max));
+        SET_FIELD(UInt64, value);
+        break;
+      }
+
+      case FieldDescriptor::CPPTYPE_FLOAT: {
+        double value;
+        DO(ConsumeDouble(&value));
+        SET_FIELD(Float, io::SafeDoubleToFloat(value));
+        break;
+      }
+
+      case FieldDescriptor::CPPTYPE_DOUBLE: {
+        double value;
+        DO(ConsumeDouble(&value));
+        SET_FIELD(Double, value);
+        break;
+      }
+
+      case FieldDescriptor::CPPTYPE_STRING: {
+        std::string value;
+        DO(ConsumeString(&value));
+        SET_FIELD(String, std::move(value));
+        break;
+      }
+
+      case FieldDescriptor::CPPTYPE_BOOL: {
+        if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
+          uint64_t value;
+          DO(ConsumeUnsignedInteger(&value, 1));
+          SET_FIELD(Bool, value);
+        } else {
+          std::string value;
+          DO(ConsumeIdentifier(&value));
+          if (value == "true" || value == "True" || value == "t") {
+            SET_FIELD(Bool, true);
+          } else if (value == "false" || value == "False" || value == "f") {
+            SET_FIELD(Bool, false);
+          } else {
+            ReportError("Invalid value for boolean field \"" + field->name() +
+                        "\". Value: \"" + value + "\".");
+            return false;
+          }
+        }
+        break;
+      }
+
+      case FieldDescriptor::CPPTYPE_ENUM: {
+        std::string value;
+        int64_t int_value = kint64max;
+        const EnumDescriptor* enum_type = field->enum_type();
+        const EnumValueDescriptor* enum_value = nullptr;
+
+        if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
+          DO(ConsumeIdentifier(&value));
+          // Find the enumeration value.
+          enum_value = enum_type->FindValueByName(value);
+
+        } else if (LookingAt("-") ||
+                   LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
+          DO(ConsumeSignedInteger(&int_value, kint32max));
+          value = StrCat(int_value);  // for error reporting
+          enum_value = enum_type->FindValueByNumber(int_value);
+        } else {
+          ReportError("Expected integer or identifier, got: " +
+                      tokenizer_.current().text);
+          return false;
+        }
+
+        if (enum_value == nullptr) {
+          if (int_value != kint64max &&
+              reflection->SupportsUnknownEnumValues()) {
+            SET_FIELD(EnumValue, int_value);
+            return true;
+          } else if (!allow_unknown_enum_) {
+            ReportError("Unknown enumeration value of \"" + value +
+                        "\" for "
+                        "field \"" +
+                        field->name() + "\".");
+            return false;
+          } else {
+            ReportWarning("Unknown enumeration value of \"" + value +
+                          "\" for "
+                          "field \"" +
+                          field->name() + "\".");
+            return true;
+          }
+        }
+
+        SET_FIELD(Enum, enum_value);
+        break;
+      }
+
+      case FieldDescriptor::CPPTYPE_MESSAGE: {
+        // We should never get here. Put here instead of a default
+        // so that if new types are added, we get a nice compiler warning.
+        GOOGLE_LOG(FATAL) << "Reached an unintended state: CPPTYPE_MESSAGE";
+        break;
+      }
+    }
+#undef SET_FIELD
+    return true;
+  }
+
+  bool SkipFieldValue() {
+    if (--recursion_limit_ < 0) {
+      ReportError(
+          StrCat("Message is too deep, the parser exceeded the "
+                       "configured recursion limit of ",
+                       initial_recursion_limit_, "."));
+      return false;
+    }
+
+    if (LookingAtType(io::Tokenizer::TYPE_STRING)) {
+      while (LookingAtType(io::Tokenizer::TYPE_STRING)) {
+        tokenizer_.Next();
+      }
+      ++recursion_limit_;
+      return true;
+    }
+    if (TryConsume("[")) {
+      while (true) {
+        if (!LookingAt("{") && !LookingAt("<")) {
+          DO(SkipFieldValue());
+        } else {
+          DO(SkipFieldMessage());
+        }
+        if (TryConsume("]")) {
+          break;
+        }
+        DO(Consume(","));
+      }
+      ++recursion_limit_;
+      return true;
+    }
+    // Possible field values other than string:
+    //   12345        => TYPE_INTEGER
+    //   -12345       => TYPE_SYMBOL + TYPE_INTEGER
+    //   1.2345       => TYPE_FLOAT
+    //   -1.2345      => TYPE_SYMBOL + TYPE_FLOAT
+    //   inf          => TYPE_IDENTIFIER
+    //   -inf         => TYPE_SYMBOL + TYPE_IDENTIFIER
+    //   TYPE_INTEGER => TYPE_IDENTIFIER
+    // Divides them into two group, one with TYPE_SYMBOL
+    // and the other without:
+    //   Group one:
+    //     12345        => TYPE_INTEGER
+    //     1.2345       => TYPE_FLOAT
+    //     inf          => TYPE_IDENTIFIER
+    //     TYPE_INTEGER => TYPE_IDENTIFIER
+    //   Group two:
+    //     -12345       => TYPE_SYMBOL + TYPE_INTEGER
+    //     -1.2345      => TYPE_SYMBOL + TYPE_FLOAT
+    //     -inf         => TYPE_SYMBOL + TYPE_IDENTIFIER
+    // As we can see, the field value consists of an optional '-' and one of
+    // TYPE_INTEGER, TYPE_FLOAT and TYPE_IDENTIFIER.
+    bool has_minus = TryConsume("-");
+    if (!LookingAtType(io::Tokenizer::TYPE_INTEGER) &&
+        !LookingAtType(io::Tokenizer::TYPE_FLOAT) &&
+        !LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
+      std::string text = tokenizer_.current().text;
+      ReportError("Cannot skip field value, unexpected token: " + text);
+      ++recursion_limit_;
+      return false;
+    }
+    // Combination of '-' and TYPE_IDENTIFIER may result in an invalid field
+    // value while other combinations all generate valid values.
+    // We check if the value of this combination is valid here.
+    // TYPE_IDENTIFIER after a '-' should be one of the float values listed
+    // below:
+    //   inf, inff, infinity, nan
+    if (has_minus && LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
+      std::string text = tokenizer_.current().text;
+      LowerString(&text);
+      if (text != "inf" &&
+          text != "infinity" && text != "nan") {
+        ReportError("Invalid float number: " + text);
+        ++recursion_limit_;
+        return false;
+      }
+    }
+    tokenizer_.Next();
+    ++recursion_limit_;
+    return true;
+  }
+
+  // Returns true if the current token's text is equal to that specified.
+  bool LookingAt(const std::string& text) {
+    return tokenizer_.current().text == text;
+  }
+
+  // Returns true if the current token's type is equal to that specified.
+  bool LookingAtType(io::Tokenizer::TokenType token_type) {
+    return tokenizer_.current().type == token_type;
+  }
+
+  // Consumes an identifier and saves its value in the identifier parameter.
+  // Returns false if the token is not of type IDENTIFIER.
+  bool ConsumeIdentifier(std::string* identifier) {
+    if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
+      *identifier = tokenizer_.current().text;
+      tokenizer_.Next();
+      return true;
+    }
+
+    // If allow_field_numer_ or allow_unknown_field_ is true, we should able
+    // to parse integer identifiers.
+    if ((allow_field_number_ || allow_unknown_field_ ||
+         allow_unknown_extension_) &&
+        LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
+      *identifier = tokenizer_.current().text;
+      tokenizer_.Next();
+      return true;
+    }
+
+    ReportError("Expected identifier, got: " + tokenizer_.current().text);
+    return false;
+  }
+
+  // Similar to `ConsumeIdentifier`, but any following whitespace token may
+  // be reported.
+  bool ConsumeIdentifierBeforeWhitespace(std::string* identifier) {
+    tokenizer_.set_report_whitespace(true);
+    bool result = ConsumeIdentifier(identifier);
+    tokenizer_.set_report_whitespace(false);
+    return result;
+  }
+
+  // Consume a string of form "<id1>.<id2>....<idN>".
+  bool ConsumeFullTypeName(std::string* name) {
+    DO(ConsumeIdentifier(name));
+    while (TryConsume(".")) {
+      std::string part;
+      DO(ConsumeIdentifier(&part));
+      *name += ".";
+      *name += part;
+    }
+    return true;
+  }
+
+  bool ConsumeTypeUrlOrFullTypeName(std::string* name) {
+    DO(ConsumeIdentifier(name));
+    while (true) {
+      std::string connector;
+      if (TryConsume(".")) {
+        connector = ".";
+      } else if (TryConsume("/")) {
+        connector = "/";
+      } else {
+        break;
+      }
+      std::string part;
+      DO(ConsumeIdentifier(&part));
+      *name += connector;
+      *name += part;
+    }
+    return true;
+  }
+
+  // Consumes a string and saves its value in the text parameter.
+  // Returns false if the token is not of type STRING.
+  bool ConsumeString(std::string* text) {
+    if (!LookingAtType(io::Tokenizer::TYPE_STRING)) {
+      ReportError("Expected string, got: " + tokenizer_.current().text);
+      return false;
+    }
+
+    text->clear();
+    while (LookingAtType(io::Tokenizer::TYPE_STRING)) {
+      io::Tokenizer::ParseStringAppend(tokenizer_.current().text, text);
+
+      tokenizer_.Next();
+    }
+
+    return true;
+  }
+
+  // Consumes a uint64_t and saves its value in the value parameter.
+  // Returns false if the token is not of type INTEGER.
+  bool ConsumeUnsignedInteger(uint64_t* value, uint64_t max_value) {
+    if (!LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
+      ReportError("Expected integer, got: " + tokenizer_.current().text);
+      return false;
+    }
+
+    if (!io::Tokenizer::ParseInteger(tokenizer_.current().text, max_value,
+                                     value)) {
+      ReportError("Integer out of range (" + tokenizer_.current().text + ")");
+      return false;
+    }
+
+    tokenizer_.Next();
+    return true;
+  }
+
+  // Consumes an int64_t and saves its value in the value parameter.
+  // Note that since the tokenizer does not support negative numbers,
+  // we actually may consume an additional token (for the minus sign) in this
+  // method. Returns false if the token is not an integer
+  // (signed or otherwise).
+  bool ConsumeSignedInteger(int64_t* value, uint64_t max_value) {
+    bool negative = false;
+
+    if (TryConsume("-")) {
+      negative = true;
+      // Two's complement always allows one more negative integer than
+      // positive.
+      ++max_value;
+    }
+
+    uint64_t unsigned_value;
+
+    DO(ConsumeUnsignedInteger(&unsigned_value, max_value));
+
+    if (negative) {
+      if ((static_cast<uint64_t>(kint64max) + 1) == unsigned_value) {
+        *value = kint64min;
+      } else {
+        *value = -static_cast<int64_t>(unsigned_value);
+      }
+    } else {
+      *value = static_cast<int64_t>(unsigned_value);
+    }
+
+    return true;
+  }
+
+  // Consumes a double and saves its value in the value parameter.
+  // Accepts decimal numbers only, rejects hex or oct numbers.
+  bool ConsumeUnsignedDecimalAsDouble(double* value, uint64_t max_value) {
+    if (!LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
+      ReportError("Expected integer, got: " + tokenizer_.current().text);
+      return false;
+    }
+
+    const std::string& text = tokenizer_.current().text;
+    if (IsHexNumber(text) || IsOctNumber(text)) {
+      ReportError("Expect a decimal number, got: " + text);
+      return false;
+    }
+
+    uint64_t uint64_value;
+    if (io::Tokenizer::ParseInteger(text, max_value, &uint64_value)) {
+      *value = static_cast<double>(uint64_value);
+    } else {
+      // Uint64 overflow, attempt to parse as a double instead.
+      *value = io::Tokenizer::ParseFloat(text);
+    }
+
+    tokenizer_.Next();
+    return true;
+  }
+
+  // Consumes a double and saves its value in the value parameter.
+  // Note that since the tokenizer does not support negative numbers,
+  // we actually may consume an additional token (for the minus sign) in this
+  // method. Returns false if the token is not a double
+  // (signed or otherwise).
+  bool ConsumeDouble(double* value) {
+    bool negative = false;
+
+    if (TryConsume("-")) {
+      negative = true;
+    }
+
+    // A double can actually be an integer, according to the tokenizer.
+    // Therefore, we must check both cases here.
+    if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
+      // We have found an integer value for the double.
+      DO(ConsumeUnsignedDecimalAsDouble(value, kuint64max));
+    } else if (LookingAtType(io::Tokenizer::TYPE_FLOAT)) {
+      // We have found a float value for the double.
+      *value = io::Tokenizer::ParseFloat(tokenizer_.current().text);
+
+      // Mark the current token as consumed.
+      tokenizer_.Next();
+    } else if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
+      std::string text = tokenizer_.current().text;
+      LowerString(&text);
+      if (text == "inf" ||
+          text == "infinity") {
+        *value = std::numeric_limits<double>::infinity();
+        tokenizer_.Next();
+      } else if (text == "nan") {
+        *value = std::numeric_limits<double>::quiet_NaN();
+        tokenizer_.Next();
+      } else {
+        ReportError("Expected double, got: " + text);
+        return false;
+      }
+    } else {
+      ReportError("Expected double, got: " + tokenizer_.current().text);
+      return false;
+    }
+
+    if (negative) {
+      *value = -*value;
+    }
+
+    return true;
+  }
+
+  // Consumes Any::type_url value, of form "type.googleapis.com/full.type.Name"
+  // or "type.googleprod.com/full.type.Name"
+  bool ConsumeAnyTypeUrl(std::string* full_type_name, std::string* prefix) {
+    // TODO(saito) Extend Consume() to consume multiple tokens at once, so that
+    // this code can be written as just DO(Consume(kGoogleApisTypePrefix)).
+    DO(ConsumeIdentifier(prefix));
+    while (TryConsume(".")) {
+      std::string url;
+      DO(ConsumeIdentifier(&url));
+      *prefix += "." + url;
+    }
+    DO(Consume("/"));
+    *prefix += "/";
+    DO(ConsumeFullTypeName(full_type_name));
+
+    return true;
+  }
+
+  // A helper function for reconstructing Any::value. Consumes a text of
+  // full_type_name, then serializes it into serialized_value.
+  bool ConsumeAnyValue(const Descriptor* value_descriptor,
+                       std::string* serialized_value) {
+    DynamicMessageFactory factory;
+    const Message* value_prototype = factory.GetPrototype(value_descriptor);
+    if (value_prototype == nullptr) {
+      return false;
+    }
+    std::unique_ptr<Message> value(value_prototype->New());
+    std::string sub_delimiter;
+    DO(ConsumeMessageDelimiter(&sub_delimiter));
+    DO(ConsumeMessage(value.get(), sub_delimiter));
+
+    if (allow_partial_) {
+      value->AppendPartialToString(serialized_value);
+    } else {
+      if (!value->IsInitialized()) {
+        ReportError(
+            "Value of type \"" + value_descriptor->full_name() +
+            "\" stored in google.protobuf.Any has missing required fields");
+        return false;
+      }
+      value->AppendToString(serialized_value);
+    }
+    return true;
+  }
+
+  // Consumes a token and confirms that it matches that specified in the
+  // value parameter. Returns false if the token found does not match that
+  // which was specified.
+  bool Consume(const std::string& value) {
+    const std::string& current_value = tokenizer_.current().text;
+
+    if (current_value != value) {
+      ReportError("Expected \"" + value + "\", found \"" + current_value +
+                  "\".");
+      return false;
+    }
+
+    tokenizer_.Next();
+
+    return true;
+  }
+
+  // Similar to `Consume`, but the following token may be tokenized as
+  // TYPE_WHITESPACE.
+  bool ConsumeBeforeWhitespace(const std::string& value) {
+    // Report whitespace after this token, but only once.
+    tokenizer_.set_report_whitespace(true);
+    bool result = Consume(value);
+    tokenizer_.set_report_whitespace(false);
+    return result;
+  }
+
+  // Attempts to consume the supplied value. Returns false if a the
+  // token found does not match the value specified.
+  bool TryConsume(const std::string& value) {
+    if (tokenizer_.current().text == value) {
+      tokenizer_.Next();
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  // Similar to `TryConsume`, but the following token may be tokenized as
+  // TYPE_WHITESPACE.
+  bool TryConsumeBeforeWhitespace(const std::string& value) {
+    // Report whitespace after this token, but only once.
+    tokenizer_.set_report_whitespace(true);
+    bool result = TryConsume(value);
+    tokenizer_.set_report_whitespace(false);
+    return result;
+  }
+
+  bool TryConsumeWhitespace() {
+    had_silent_marker_ = false;
+    if (LookingAtType(io::Tokenizer::TYPE_WHITESPACE)) {
+      if (tokenizer_.current().text ==
+          StrCat(" ", internal::kDebugStringSilentMarkerForDetection)) {
+        had_silent_marker_ = true;
+      }
+      tokenizer_.Next();
+      return true;
+    }
+    return false;
+  }
+
+  // An internal instance of the Tokenizer's error collector, used to
+  // collect any base-level parse errors and feed them to the ParserImpl.
+  class ParserErrorCollector : public io::ErrorCollector {
+   public:
+    explicit ParserErrorCollector(TextFormat::Parser::ParserImpl* parser)
+        : parser_(parser) {}
+
+    ~ParserErrorCollector() override {}
+
+    void AddError(int line, int column, const std::string& message) override {
+      parser_->ReportError(line, column, message);
+    }
+
+    void AddWarning(int line, int column, const std::string& message) override {
+      parser_->ReportWarning(line, column, message);
+    }
+
+   private:
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ParserErrorCollector);
+    TextFormat::Parser::ParserImpl* parser_;
+  };
+
+  io::ErrorCollector* error_collector_;
+  const TextFormat::Finder* finder_;
+  ParseInfoTree* parse_info_tree_;
+  ParserErrorCollector tokenizer_error_collector_;
+  io::Tokenizer tokenizer_;
+  const Descriptor* root_message_type_;
+  SingularOverwritePolicy singular_overwrite_policy_;
+  const bool allow_case_insensitive_field_;
+  const bool allow_unknown_field_;
+  const bool allow_unknown_extension_;
+  const bool allow_unknown_enum_;
+  const bool allow_field_number_;
+  const bool allow_partial_;
+  const int initial_recursion_limit_;
+  int recursion_limit_;
+  bool had_silent_marker_;
+  bool had_errors_;
+};
+
+// ===========================================================================
+// Internal class for writing text to the io::ZeroCopyOutputStream. Adapted
+// from the Printer found in //net/proto2/io/public/printer.h
+class TextFormat::Printer::TextGenerator
+    : public TextFormat::BaseTextGenerator {
+ public:
+  explicit TextGenerator(io::ZeroCopyOutputStream* output,
+                         int initial_indent_level)
+      : output_(output),
+        buffer_(nullptr),
+        buffer_size_(0),
+        at_start_of_line_(true),
+        failed_(false),
+        insert_silent_marker_(false),
+        indent_level_(initial_indent_level),
+        initial_indent_level_(initial_indent_level) {}
+
+  explicit TextGenerator(io::ZeroCopyOutputStream* output,
+                         bool insert_silent_marker, int initial_indent_level)
+      : output_(output),
+        buffer_(nullptr),
+        buffer_size_(0),
+        at_start_of_line_(true),
+        failed_(false),
+        insert_silent_marker_(insert_silent_marker),
+        indent_level_(initial_indent_level),
+        initial_indent_level_(initial_indent_level) {}
+
+  ~TextGenerator() override {
+    // Only BackUp() if we're sure we've successfully called Next() at least
+    // once.
+    if (!failed_) {
+      output_->BackUp(buffer_size_);
+    }
+  }
+
+  // Indent text by two spaces.  After calling Indent(), two spaces will be
+  // inserted at the beginning of each line of text.  Indent() may be called
+  // multiple times to produce deeper indents.
+  void Indent() override { ++indent_level_; }
+
+  // Reduces the current indent level by two spaces, or crashes if the indent
+  // level is zero.
+  void Outdent() override {
+    if (indent_level_ == 0 || indent_level_ < initial_indent_level_) {
+      GOOGLE_LOG(DFATAL) << " Outdent() without matching Indent().";
+      return;
+    }
+
+    --indent_level_;
+  }
+
+  size_t GetCurrentIndentationSize() const override {
+    return 2 * indent_level_;
+  }
+
+  // Print text to the output stream.
+  void Print(const char* text, size_t size) override {
+    if (indent_level_ > 0) {
+      size_t pos = 0;  // The number of bytes we've written so far.
+      for (size_t i = 0; i < size; i++) {
+        if (text[i] == '\n') {
+          // Saw newline.  If there is more text, we may need to insert an
+          // indent here.  So, write what we have so far, including the '\n'.
+          Write(text + pos, i - pos + 1);
+          pos = i + 1;
+
+          // Setting this true will cause the next Write() to insert an indent
+          // first.
+          at_start_of_line_ = true;
+        }
+      }
+      // Write the rest.
+      Write(text + pos, size - pos);
+    } else {
+      Write(text, size);
+      if (size > 0 && text[size - 1] == '\n') {
+        at_start_of_line_ = true;
+      }
+    }
+  }
+
+  // True if any write to the underlying stream failed.  (We don't just
+  // crash in this case because this is an I/O failure, not a programming
+  // error.)
+  bool failed() const { return failed_; }
+
+  void PrintMaybeWithMarker(StringPiece text) {
+    Print(text.data(), text.size());
+    if (ConsumeInsertSilentMarker()) {
+      PrintLiteral(internal::kDebugStringSilentMarker);
+    }
+  }
+
+  void PrintMaybeWithMarker(StringPiece text_head,
+                            StringPiece text_tail) {
+    Print(text_head.data(), text_head.size());
+    if (ConsumeInsertSilentMarker()) {
+      PrintLiteral(internal::kDebugStringSilentMarker);
+    }
+    Print(text_tail.data(), text_tail.size());
+  }
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TextGenerator);
+
+  void Write(const char* data, size_t size) {
+    if (failed_) return;
+    if (size == 0) return;
+
+    if (at_start_of_line_) {
+      // Insert an indent.
+      at_start_of_line_ = false;
+      WriteIndent();
+      if (failed_) return;
+    }
+
+    while (static_cast<int64_t>(size) > buffer_size_) {
+      // Data exceeds space in the buffer.  Copy what we can and request a
+      // new buffer.
+      if (buffer_size_ > 0) {
+        memcpy(buffer_, data, buffer_size_);
+        data += buffer_size_;
+        size -= buffer_size_;
+      }
+      void* void_buffer = nullptr;
+      failed_ = !output_->Next(&void_buffer, &buffer_size_);
+      if (failed_) return;
+      buffer_ = reinterpret_cast<char*>(void_buffer);
+    }
+
+    // Buffer is big enough to receive the data; copy it.
+    memcpy(buffer_, data, size);
+    buffer_ += size;
+    buffer_size_ -= size;
+  }
+
+  void WriteIndent() {
+    if (indent_level_ == 0) {
+      return;
+    }
+    GOOGLE_DCHECK(!failed_);
+    int size = GetCurrentIndentationSize();
+
+    while (size > buffer_size_) {
+      // Data exceeds space in the buffer. Write what we can and request a new
+      // buffer.
+      if (buffer_size_ > 0) {
+        memset(buffer_, ' ', buffer_size_);
+      }
+      size -= buffer_size_;
+      void* void_buffer;
+      failed_ = !output_->Next(&void_buffer, &buffer_size_);
+      if (failed_) return;
+      buffer_ = reinterpret_cast<char*>(void_buffer);
+    }
+
+    // Buffer is big enough to receive the data; copy it.
+    memset(buffer_, ' ', size);
+    buffer_ += size;
+    buffer_size_ -= size;
+  }
+
+  // Return the current value of insert_silent_marker_. If it is true, set it
+  // to false as we assume that a silent marker is inserted after a call to this
+  // function.
+  bool ConsumeInsertSilentMarker() {
+    if (insert_silent_marker_) {
+      insert_silent_marker_ = false;
+      return true;
+    }
+    return false;
+  }
+
+  io::ZeroCopyOutputStream* const output_;
+  char* buffer_;
+  int buffer_size_;
+  bool at_start_of_line_;
+  bool failed_;
+  // This flag is false when inserting silent marker is disabled or a silent
+  // marker has been inserted.
+  bool insert_silent_marker_;
+
+  int indent_level_;
+  int initial_indent_level_;
+};
+
+// ===========================================================================
+//  An internal field value printer that may insert a silent marker in
+//  DebugStrings.
+class TextFormat::Printer::DebugStringFieldValuePrinter
+    : public TextFormat::FastFieldValuePrinter {
+ public:
+  void PrintMessageStart(const Message& /*message*/, int /*field_index*/,
+                         int /*field_count*/, bool single_line_mode,
+                         BaseTextGenerator* generator) const override {
+    // This is safe as only TextGenerator is used with
+    // DebugStringFieldValuePrinter.
+    TextGenerator* text_generator = static_cast<TextGenerator*>(generator);
+    if (single_line_mode) {
+      text_generator->PrintMaybeWithMarker(" ", "{ ");
+    } else {
+      text_generator->PrintMaybeWithMarker(" ", "{\n");
+    }
+  }
+};
+
+// ===========================================================================
+//  An internal field value printer that escape UTF8 strings.
+class TextFormat::Printer::FastFieldValuePrinterUtf8Escaping
+    : public TextFormat::Printer::DebugStringFieldValuePrinter {
+ public:
+  void PrintString(const std::string& val,
+                   TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintLiteral("\"");
+    generator->PrintString(strings::Utf8SafeCEscape(val));
+    generator->PrintLiteral("\"");
+  }
+  void PrintBytes(const std::string& val,
+                  TextFormat::BaseTextGenerator* generator) const override {
+    return FastFieldValuePrinter::PrintString(val, generator);
+  }
+};
+
+// ===========================================================================
+// Implementation of the default Finder for extensions.
+TextFormat::Finder::~Finder() {}
+
+const FieldDescriptor* TextFormat::Finder::FindExtension(
+    Message* message, const std::string& name) const {
+  return DefaultFinderFindExtension(message, name);
+}
+
+const FieldDescriptor* TextFormat::Finder::FindExtensionByNumber(
+    const Descriptor* descriptor, int number) const {
+  return DefaultFinderFindExtensionByNumber(descriptor, number);
+}
+
+const Descriptor* TextFormat::Finder::FindAnyType(
+    const Message& message, const std::string& prefix,
+    const std::string& name) const {
+  return DefaultFinderFindAnyType(message, prefix, name);
+}
+
+MessageFactory* TextFormat::Finder::FindExtensionFactory(
+    const FieldDescriptor* /*field*/) const {
+  return nullptr;
+}
+
+// ===========================================================================
+
+TextFormat::Parser::Parser()
+    : error_collector_(nullptr),
+      finder_(nullptr),
+      parse_info_tree_(nullptr),
+      allow_partial_(false),
+      allow_case_insensitive_field_(false),
+      allow_unknown_field_(false),
+      allow_unknown_extension_(false),
+      allow_unknown_enum_(false),
+      allow_field_number_(false),
+      allow_relaxed_whitespace_(false),
+      allow_singular_overwrites_(false),
+      recursion_limit_(std::numeric_limits<int>::max()) {}
+
+TextFormat::Parser::~Parser() {}
+
+namespace {
+
+bool CheckParseInputSize(StringPiece input,
+                         io::ErrorCollector* error_collector) {
+  if (input.size() > INT_MAX) {
+    error_collector->AddError(
+        -1, 0,
+        StrCat(
+            "Input size too large: ", static_cast<int64_t>(input.size()),
+            " bytes", " > ", INT_MAX, " bytes."));
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+bool TextFormat::Parser::Parse(io::ZeroCopyInputStream* input,
+                               Message* output) {
+  output->Clear();
+
+  ParserImpl::SingularOverwritePolicy overwrites_policy =
+      allow_singular_overwrites_ ? ParserImpl::ALLOW_SINGULAR_OVERWRITES
+                                 : ParserImpl::FORBID_SINGULAR_OVERWRITES;
+
+  ParserImpl parser(output->GetDescriptor(), input, error_collector_, finder_,
+                    parse_info_tree_, overwrites_policy,
+                    allow_case_insensitive_field_, allow_unknown_field_,
+                    allow_unknown_extension_, allow_unknown_enum_,
+                    allow_field_number_, allow_relaxed_whitespace_,
+                    allow_partial_, recursion_limit_);
+  return MergeUsingImpl(input, output, &parser);
+}
+
+bool TextFormat::Parser::ParseFromString(ConstStringParam input,
+                                         Message* output) {
+  DO(CheckParseInputSize(input, error_collector_));
+  io::ArrayInputStream input_stream(input.data(), input.size());
+  return Parse(&input_stream, output);
+}
+
+bool TextFormat::Parser::Merge(io::ZeroCopyInputStream* input,
+                               Message* output) {
+  ParserImpl parser(output->GetDescriptor(), input, error_collector_, finder_,
+                    parse_info_tree_, ParserImpl::ALLOW_SINGULAR_OVERWRITES,
+                    allow_case_insensitive_field_, allow_unknown_field_,
+                    allow_unknown_extension_, allow_unknown_enum_,
+                    allow_field_number_, allow_relaxed_whitespace_,
+                    allow_partial_, recursion_limit_);
+  return MergeUsingImpl(input, output, &parser);
+}
+
+bool TextFormat::Parser::MergeFromString(ConstStringParam input,
+                                         Message* output) {
+  DO(CheckParseInputSize(input, error_collector_));
+  io::ArrayInputStream input_stream(input.data(), input.size());
+  return Merge(&input_stream, output);
+}
+
+bool TextFormat::Parser::MergeUsingImpl(io::ZeroCopyInputStream* /* input */,
+                                        Message* output,
+                                        ParserImpl* parser_impl) {
+  if (!parser_impl->Parse(output)) return false;
+  if (!allow_partial_ && !output->IsInitialized()) {
+    std::vector<std::string> missing_fields;
+    output->FindInitializationErrors(&missing_fields);
+    parser_impl->ReportError(-1, 0,
+                             "Message missing required fields: " +
+                                 Join(missing_fields, ", "));
+    return false;
+  }
+  return true;
+}
+
+bool TextFormat::Parser::ParseFieldValueFromString(const std::string& input,
+                                                   const FieldDescriptor* field,
+                                                   Message* output) {
+  io::ArrayInputStream input_stream(input.data(), input.size());
+  ParserImpl parser(
+      output->GetDescriptor(), &input_stream, error_collector_, finder_,
+      parse_info_tree_, ParserImpl::ALLOW_SINGULAR_OVERWRITES,
+      allow_case_insensitive_field_, allow_unknown_field_,
+      allow_unknown_extension_, allow_unknown_enum_, allow_field_number_,
+      allow_relaxed_whitespace_, allow_partial_, recursion_limit_);
+  return parser.ParseField(field, output);
+}
+
+/* static */ bool TextFormat::Parse(io::ZeroCopyInputStream* input,
+                                    Message* output) {
+  return Parser().Parse(input, output);
+}
+
+/* static */ bool TextFormat::Merge(io::ZeroCopyInputStream* input,
+                                    Message* output) {
+  return Parser().Merge(input, output);
+}
+
+/* static */ bool TextFormat::ParseFromString(ConstStringParam input,
+                                              Message* output) {
+  return Parser().ParseFromString(input, output);
+}
+
+/* static */ bool TextFormat::MergeFromString(ConstStringParam input,
+                                              Message* output) {
+  return Parser().MergeFromString(input, output);
+}
+
+#undef DO
+
+// ===========================================================================
+
+TextFormat::BaseTextGenerator::~BaseTextGenerator() {}
+
+namespace {
+
+// A BaseTextGenerator that writes to a string.
+class StringBaseTextGenerator : public TextFormat::BaseTextGenerator {
+ public:
+  void Print(const char* text, size_t size) override {
+    output_.append(text, size);
+  }
+
+// Some compilers do not support ref-qualifiers even in C++11 mode.
+// Disable the optimization for now and revisit it later.
+#if 0  // LANG_CXX11
+  std::string Consume() && { return std::move(output_); }
+#else  // !LANG_CXX11
+  const std::string& Get() { return output_; }
+#endif  // LANG_CXX11
+
+ private:
+  std::string output_;
+};
+
+}  // namespace
+
+// The default implementation for FieldValuePrinter. We just delegate the
+// implementation to the default FastFieldValuePrinter to avoid duplicating the
+// logic.
+TextFormat::FieldValuePrinter::FieldValuePrinter() {}
+TextFormat::FieldValuePrinter::~FieldValuePrinter() {}
+
+#if 0  // LANG_CXX11
+#define FORWARD_IMPL(fn, ...)            \
+  StringBaseTextGenerator generator;     \
+  delegate_.fn(__VA_ARGS__, &generator); \
+  return std::move(generator).Consume()
+#else  // !LANG_CXX11
+#define FORWARD_IMPL(fn, ...)            \
+  StringBaseTextGenerator generator;     \
+  delegate_.fn(__VA_ARGS__, &generator); \
+  return generator.Get()
+#endif  // LANG_CXX11
+
+std::string TextFormat::FieldValuePrinter::PrintBool(bool val) const {
+  FORWARD_IMPL(PrintBool, val);
+}
+std::string TextFormat::FieldValuePrinter::PrintInt32(int32_t val) const {
+  FORWARD_IMPL(PrintInt32, val);
+}
+std::string TextFormat::FieldValuePrinter::PrintUInt32(uint32_t val) const {
+  FORWARD_IMPL(PrintUInt32, val);
+}
+std::string TextFormat::FieldValuePrinter::PrintInt64(int64_t val) const {
+  FORWARD_IMPL(PrintInt64, val);
+}
+std::string TextFormat::FieldValuePrinter::PrintUInt64(uint64_t val) const {
+  FORWARD_IMPL(PrintUInt64, val);
+}
+std::string TextFormat::FieldValuePrinter::PrintFloat(float val) const {
+  FORWARD_IMPL(PrintFloat, val);
+}
+std::string TextFormat::FieldValuePrinter::PrintDouble(double val) const {
+  FORWARD_IMPL(PrintDouble, val);
+}
+std::string TextFormat::FieldValuePrinter::PrintString(
+    const std::string& val) const {
+  FORWARD_IMPL(PrintString, val);
+}
+std::string TextFormat::FieldValuePrinter::PrintBytes(
+    const std::string& val) const {
+  return PrintString(val);
+}
+std::string TextFormat::FieldValuePrinter::PrintEnum(
+    int32_t val, const std::string& name) const {
+  FORWARD_IMPL(PrintEnum, val, name);
+}
+std::string TextFormat::FieldValuePrinter::PrintFieldName(
+    const Message& message, const Reflection* reflection,
+    const FieldDescriptor* field) const {
+  FORWARD_IMPL(PrintFieldName, message, reflection, field);
+}
+std::string TextFormat::FieldValuePrinter::PrintMessageStart(
+    const Message& message, int field_index, int field_count,
+    bool single_line_mode) const {
+  FORWARD_IMPL(PrintMessageStart, message, field_index, field_count,
+               single_line_mode);
+}
+std::string TextFormat::FieldValuePrinter::PrintMessageEnd(
+    const Message& message, int field_index, int field_count,
+    bool single_line_mode) const {
+  FORWARD_IMPL(PrintMessageEnd, message, field_index, field_count,
+               single_line_mode);
+}
+#undef FORWARD_IMPL
+
+TextFormat::FastFieldValuePrinter::FastFieldValuePrinter() {}
+TextFormat::FastFieldValuePrinter::~FastFieldValuePrinter() {}
+void TextFormat::FastFieldValuePrinter::PrintBool(
+    bool val, BaseTextGenerator* generator) const {
+  if (val) {
+    generator->PrintLiteral("true");
+  } else {
+    generator->PrintLiteral("false");
+  }
+}
+void TextFormat::FastFieldValuePrinter::PrintInt32(
+    int32_t val, BaseTextGenerator* generator) const {
+  generator->PrintString(StrCat(val));
+}
+void TextFormat::FastFieldValuePrinter::PrintUInt32(
+    uint32_t val, BaseTextGenerator* generator) const {
+  generator->PrintString(StrCat(val));
+}
+void TextFormat::FastFieldValuePrinter::PrintInt64(
+    int64_t val, BaseTextGenerator* generator) const {
+  generator->PrintString(StrCat(val));
+}
+void TextFormat::FastFieldValuePrinter::PrintUInt64(
+    uint64_t val, BaseTextGenerator* generator) const {
+  generator->PrintString(StrCat(val));
+}
+void TextFormat::FastFieldValuePrinter::PrintFloat(
+    float val, BaseTextGenerator* generator) const {
+  generator->PrintString(!std::isnan(val) ? SimpleFtoa(val) : "nan");
+}
+void TextFormat::FastFieldValuePrinter::PrintDouble(
+    double val, BaseTextGenerator* generator) const {
+  generator->PrintString(!std::isnan(val) ? SimpleDtoa(val) : "nan");
+}
+void TextFormat::FastFieldValuePrinter::PrintEnum(
+    int32_t /*val*/, const std::string& name,
+    BaseTextGenerator* generator) const {
+  generator->PrintString(name);
+}
+
+void TextFormat::FastFieldValuePrinter::PrintString(
+    const std::string& val, BaseTextGenerator* generator) const {
+  generator->PrintLiteral("\"");
+  generator->PrintString(CEscape(val));
+  generator->PrintLiteral("\"");
+}
+void TextFormat::FastFieldValuePrinter::PrintBytes(
+    const std::string& val, BaseTextGenerator* generator) const {
+  PrintString(val, generator);
+}
+void TextFormat::FastFieldValuePrinter::PrintFieldName(
+    const Message& message, int /*field_index*/, int /*field_count*/,
+    const Reflection* reflection, const FieldDescriptor* field,
+    BaseTextGenerator* generator) const {
+  PrintFieldName(message, reflection, field, generator);
+}
+void TextFormat::FastFieldValuePrinter::PrintFieldName(
+    const Message& /*message*/, const Reflection* /*reflection*/,
+    const FieldDescriptor* field, BaseTextGenerator* generator) const {
+  if (field->is_extension()) {
+    generator->PrintLiteral("[");
+    generator->PrintString(field->PrintableNameForExtension());
+    generator->PrintLiteral("]");
+  } else if (field->type() == FieldDescriptor::TYPE_GROUP) {
+    // Groups must be serialized with their original capitalization.
+    generator->PrintString(field->message_type()->name());
+  } else {
+    generator->PrintString(field->name());
+  }
+}
+void TextFormat::FastFieldValuePrinter::PrintMessageStart(
+    const Message& /*message*/, int /*field_index*/, int /*field_count*/,
+    bool single_line_mode, BaseTextGenerator* generator) const {
+  if (single_line_mode) {
+    generator->PrintLiteral(" { ");
+  } else {
+    generator->PrintLiteral(" {\n");
+  }
+}
+bool TextFormat::FastFieldValuePrinter::PrintMessageContent(
+    const Message& /*message*/, int /*field_index*/, int /*field_count*/,
+    bool /*single_line_mode*/, BaseTextGenerator* /*generator*/) const {
+  return false;  // Use the default printing function.
+}
+void TextFormat::FastFieldValuePrinter::PrintMessageEnd(
+    const Message& /*message*/, int /*field_index*/, int /*field_count*/,
+    bool single_line_mode, BaseTextGenerator* generator) const {
+  if (single_line_mode) {
+    generator->PrintLiteral("} ");
+  } else {
+    generator->PrintLiteral("}\n");
+  }
+}
+
+namespace {
+
+// A legacy compatibility wrapper. Takes ownership of the delegate.
+class FieldValuePrinterWrapper : public TextFormat::FastFieldValuePrinter {
+ public:
+  explicit FieldValuePrinterWrapper(
+      const TextFormat::FieldValuePrinter* delegate)
+      : delegate_(delegate) {}
+
+  void SetDelegate(const TextFormat::FieldValuePrinter* delegate) {
+    delegate_.reset(delegate);
+  }
+
+  void PrintBool(bool val,
+                 TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintBool(val));
+  }
+  void PrintInt32(int32_t val,
+                  TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintInt32(val));
+  }
+  void PrintUInt32(uint32_t val,
+                   TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintUInt32(val));
+  }
+  void PrintInt64(int64_t val,
+                  TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintInt64(val));
+  }
+  void PrintUInt64(uint64_t val,
+                   TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintUInt64(val));
+  }
+  void PrintFloat(float val,
+                  TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintFloat(val));
+  }
+  void PrintDouble(double val,
+                   TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintDouble(val));
+  }
+  void PrintString(const std::string& val,
+                   TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintString(val));
+  }
+  void PrintBytes(const std::string& val,
+                  TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintBytes(val));
+  }
+  void PrintEnum(int32_t val, const std::string& name,
+                 TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintEnum(val, name));
+  }
+  void PrintFieldName(const Message& message, int /*field_index*/,
+                      int /*field_count*/, const Reflection* reflection,
+                      const FieldDescriptor* field,
+                      TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(
+        delegate_->PrintFieldName(message, reflection, field));
+  }
+  void PrintFieldName(const Message& message, const Reflection* reflection,
+                      const FieldDescriptor* field,
+                      TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(
+        delegate_->PrintFieldName(message, reflection, field));
+  }
+  void PrintMessageStart(
+      const Message& message, int field_index, int field_count,
+      bool single_line_mode,
+      TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintMessageStart(
+        message, field_index, field_count, single_line_mode));
+  }
+  void PrintMessageEnd(
+      const Message& message, int field_index, int field_count,
+      bool single_line_mode,
+      TextFormat::BaseTextGenerator* generator) const override {
+    generator->PrintString(delegate_->PrintMessageEnd(
+        message, field_index, field_count, single_line_mode));
+  }
+
+ private:
+  std::unique_ptr<const TextFormat::FieldValuePrinter> delegate_;
+};
+
+}  // namespace
+
+const char* const TextFormat::Printer::kDoNotParse =
+    "DO NOT PARSE: fields may be stripped and missing.\n";
+
+TextFormat::Printer::Printer()
+    : initial_indent_level_(0),
+      single_line_mode_(false),
+      use_field_number_(false),
+      use_short_repeated_primitives_(false),
+      insert_silent_marker_(false),
+      hide_unknown_fields_(false),
+      print_message_fields_in_index_order_(false),
+      expand_any_(false),
+      truncate_string_field_longer_than_(0LL),
+      finder_(nullptr) {
+  SetUseUtf8StringEscaping(false);
+}
+
+void TextFormat::Printer::SetUseUtf8StringEscaping(bool as_utf8) {
+  SetDefaultFieldValuePrinter(as_utf8 ? new FastFieldValuePrinterUtf8Escaping()
+                                      : new DebugStringFieldValuePrinter());
+}
+
+void TextFormat::Printer::SetDefaultFieldValuePrinter(
+    const FieldValuePrinter* printer) {
+  default_field_value_printer_.reset(new FieldValuePrinterWrapper(printer));
+}
+
+void TextFormat::Printer::SetDefaultFieldValuePrinter(
+    const FastFieldValuePrinter* printer) {
+  default_field_value_printer_.reset(printer);
+}
+
+bool TextFormat::Printer::RegisterFieldValuePrinter(
+    const FieldDescriptor* field, const FieldValuePrinter* printer) {
+  if (field == nullptr || printer == nullptr) {
+    return false;
+  }
+  std::unique_ptr<FieldValuePrinterWrapper> wrapper(
+      new FieldValuePrinterWrapper(nullptr));
+  auto pair = custom_printers_.insert(std::make_pair(field, nullptr));
+  if (pair.second) {
+    wrapper->SetDelegate(printer);
+    pair.first->second = std::move(wrapper);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool TextFormat::Printer::RegisterFieldValuePrinter(
+    const FieldDescriptor* field, const FastFieldValuePrinter* printer) {
+  if (field == nullptr || printer == nullptr) {
+    return false;
+  }
+  auto pair = custom_printers_.insert(std::make_pair(field, nullptr));
+  if (pair.second) {
+    pair.first->second.reset(printer);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool TextFormat::Printer::RegisterMessagePrinter(
+    const Descriptor* descriptor, const MessagePrinter* printer) {
+  if (descriptor == nullptr || printer == nullptr) {
+    return false;
+  }
+  auto pair =
+      custom_message_printers_.insert(std::make_pair(descriptor, nullptr));
+  if (pair.second) {
+    pair.first->second.reset(printer);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool TextFormat::Printer::PrintToString(const Message& message,
+                                        std::string* output) const {
+  GOOGLE_DCHECK(output) << "output specified is nullptr";
+
+  output->clear();
+  io::StringOutputStream output_stream(output);
+
+  return Print(message, &output_stream);
+}
+
+bool TextFormat::Printer::PrintUnknownFieldsToString(
+    const UnknownFieldSet& unknown_fields, std::string* output) const {
+  GOOGLE_DCHECK(output) << "output specified is nullptr";
+
+  output->clear();
+  io::StringOutputStream output_stream(output);
+  return PrintUnknownFields(unknown_fields, &output_stream);
+}
+
+bool TextFormat::Printer::Print(const Message& message,
+                                io::ZeroCopyOutputStream* output) const {
+  TextGenerator generator(output, insert_silent_marker_, initial_indent_level_);
+
+  Print(message, &generator);
+
+  // Output false if the generator failed internally.
+  return !generator.failed();
+}
+
+// Maximum recursion depth for heuristically printing out length-delimited
+// unknown fields as messages.
+static constexpr int kUnknownFieldRecursionLimit = 10;
+
+bool TextFormat::Printer::PrintUnknownFields(
+    const UnknownFieldSet& unknown_fields,
+    io::ZeroCopyOutputStream* output) const {
+  TextGenerator generator(output, initial_indent_level_);
+
+  PrintUnknownFields(unknown_fields, &generator, kUnknownFieldRecursionLimit);
+
+  // Output false if the generator failed internally.
+  return !generator.failed();
+}
+
+namespace {
+// Comparison functor for sorting FieldDescriptors by field index.
+// Normal fields have higher precedence than extensions.
+struct FieldIndexSorter {
+  bool operator()(const FieldDescriptor* left,
+                  const FieldDescriptor* right) const {
+    if (left->is_extension() && right->is_extension()) {
+      return left->number() < right->number();
+    } else if (left->is_extension()) {
+      return false;
+    } else if (right->is_extension()) {
+      return true;
+    } else {
+      return left->index() < right->index();
+    }
+  }
+};
+
+}  // namespace
+
+bool TextFormat::Printer::PrintAny(const Message& message,
+                                   TextGenerator* generator) const {
+  const FieldDescriptor* type_url_field;
+  const FieldDescriptor* value_field;
+  if (!internal::GetAnyFieldDescriptors(message, &type_url_field,
+                                        &value_field)) {
+    return false;
+  }
+
+  const Reflection* reflection = message.GetReflection();
+
+  // Extract the full type name from the type_url field.
+  const std::string& type_url = reflection->GetString(message, type_url_field);
+  std::string url_prefix;
+  std::string full_type_name;
+  if (!internal::ParseAnyTypeUrl(type_url, &url_prefix, &full_type_name)) {
+    return false;
+  }
+
+  // Print the "value" in text.
+  const Descriptor* value_descriptor =
+      finder_ ? finder_->FindAnyType(message, url_prefix, full_type_name)
+              : DefaultFinderFindAnyType(message, url_prefix, full_type_name);
+  if (value_descriptor == nullptr) {
+    GOOGLE_LOG(WARNING) << "Can't print proto content: proto type " << type_url
+                 << " not found";
+    return false;
+  }
+  DynamicMessageFactory factory;
+  std::unique_ptr<Message> value_message(
+      factory.GetPrototype(value_descriptor)->New());
+  std::string serialized_value = reflection->GetString(message, value_field);
+  if (!value_message->ParseFromString(serialized_value)) {
+    GOOGLE_LOG(WARNING) << type_url << ": failed to parse contents";
+    return false;
+  }
+  generator->PrintLiteral("[");
+  generator->PrintString(type_url);
+  generator->PrintLiteral("]");
+  const FastFieldValuePrinter* printer = GetFieldPrinter(value_field);
+  printer->PrintMessageStart(message, -1, 0, single_line_mode_, generator);
+  generator->Indent();
+  Print(*value_message, generator);
+  generator->Outdent();
+  printer->PrintMessageEnd(message, -1, 0, single_line_mode_, generator);
+  return true;
+}
+
+void TextFormat::Printer::Print(const Message& message,
+                                TextGenerator* generator) const {
+  const Reflection* reflection = message.GetReflection();
+  if (!reflection) {
+    // This message does not provide any way to describe its structure.
+    // Parse it again in an UnknownFieldSet, and display this instead.
+    UnknownFieldSet unknown_fields;
+    {
+      std::string serialized = message.SerializeAsString();
+      io::ArrayInputStream input(serialized.data(), serialized.size());
+      unknown_fields.ParseFromZeroCopyStream(&input);
+    }
+    PrintUnknownFields(unknown_fields, generator, kUnknownFieldRecursionLimit);
+    return;
+  }
+  const Descriptor* descriptor = message.GetDescriptor();
+  auto itr = custom_message_printers_.find(descriptor);
+  if (itr != custom_message_printers_.end()) {
+    itr->second->Print(message, single_line_mode_, generator);
+    return;
+  }
+  if (descriptor->full_name() == internal::kAnyFullTypeName && expand_any_ &&
+      PrintAny(message, generator)) {
+    return;
+  }
+  std::vector<const FieldDescriptor*> fields;
+  if (descriptor->options().map_entry()) {
+    fields.push_back(descriptor->field(0));
+    fields.push_back(descriptor->field(1));
+  } else {
+    reflection->ListFieldsOmitStripped(message, &fields);
+    if (reflection->IsMessageStripped(message.GetDescriptor())) {
+      generator->Print(kDoNotParse, std::strlen(kDoNotParse));
+    }
+  }
+
+  if (print_message_fields_in_index_order_) {
+    std::sort(fields.begin(), fields.end(), FieldIndexSorter());
+  }
+  for (const FieldDescriptor* field : fields) {
+    PrintField(message, reflection, field, generator);
+  }
+  if (!hide_unknown_fields_) {
+    PrintUnknownFields(reflection->GetUnknownFields(message), generator,
+                       kUnknownFieldRecursionLimit);
+  }
+}
+
+void TextFormat::Printer::PrintFieldValueToString(const Message& message,
+                                                  const FieldDescriptor* field,
+                                                  int index,
+                                                  std::string* output) const {
+  GOOGLE_DCHECK(output) << "output specified is nullptr";
+
+  output->clear();
+  io::StringOutputStream output_stream(output);
+  TextGenerator generator(&output_stream, initial_indent_level_);
+
+  PrintFieldValue(message, message.GetReflection(), field, index, &generator);
+}
+
+class MapEntryMessageComparator {
+ public:
+  explicit MapEntryMessageComparator(const Descriptor* descriptor)
+      : field_(descriptor->field(0)) {}
+
+  bool operator()(const Message* a, const Message* b) {
+    const Reflection* reflection = a->GetReflection();
+    switch (field_->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_BOOL: {
+        bool first = reflection->GetBool(*a, field_);
+        bool second = reflection->GetBool(*b, field_);
+        return first < second;
+      }
+      case FieldDescriptor::CPPTYPE_INT32: {
+        int32_t first = reflection->GetInt32(*a, field_);
+        int32_t second = reflection->GetInt32(*b, field_);
+        return first < second;
+      }
+      case FieldDescriptor::CPPTYPE_INT64: {
+        int64_t first = reflection->GetInt64(*a, field_);
+        int64_t second = reflection->GetInt64(*b, field_);
+        return first < second;
+      }
+      case FieldDescriptor::CPPTYPE_UINT32: {
+        uint32_t first = reflection->GetUInt32(*a, field_);
+        uint32_t second = reflection->GetUInt32(*b, field_);
+        return first < second;
+      }
+      case FieldDescriptor::CPPTYPE_UINT64: {
+        uint64_t first = reflection->GetUInt64(*a, field_);
+        uint64_t second = reflection->GetUInt64(*b, field_);
+        return first < second;
+      }
+      case FieldDescriptor::CPPTYPE_STRING: {
+        std::string first = reflection->GetString(*a, field_);
+        std::string second = reflection->GetString(*b, field_);
+        return first < second;
+      }
+      default:
+        GOOGLE_LOG(DFATAL) << "Invalid key for map field.";
+        return true;
+    }
+  }
+
+ private:
+  const FieldDescriptor* field_;
+};
+
+namespace internal {
+class MapFieldPrinterHelper {
+ public:
+  // DynamicMapSorter::Sort cannot be used because it enforces syncing with
+  // repeated field.
+  static bool SortMap(const Message& message, const Reflection* reflection,
+                      const FieldDescriptor* field,
+                      std::vector<const Message*>* sorted_map_field);
+  static void CopyKey(const MapKey& key, Message* message,
+                      const FieldDescriptor* field_desc);
+  static void CopyValue(const MapValueRef& value, Message* message,
+                        const FieldDescriptor* field_desc);
+};
+
+// Returns true if elements contained in sorted_map_field need to be released.
+bool MapFieldPrinterHelper::SortMap(
+    const Message& message, const Reflection* reflection,
+    const FieldDescriptor* field,
+    std::vector<const Message*>* sorted_map_field) {
+  bool need_release = false;
+  const MapFieldBase& base = *reflection->GetMapData(message, field);
+
+  if (base.IsRepeatedFieldValid()) {
+    const RepeatedPtrField<Message>& map_field =
+        reflection->GetRepeatedPtrFieldInternal<Message>(message, field);
+    for (int i = 0; i < map_field.size(); ++i) {
+      sorted_map_field->push_back(
+          const_cast<RepeatedPtrField<Message>*>(&map_field)->Mutable(i));
+    }
+  } else {
+    // TODO(teboring): For performance, instead of creating map entry message
+    // for each element, just store map keys and sort them.
+    const Descriptor* map_entry_desc = field->message_type();
+    const Message* prototype =
+        reflection->GetMessageFactory()->GetPrototype(map_entry_desc);
+    for (MapIterator iter =
+             reflection->MapBegin(const_cast<Message*>(&message), field);
+         iter != reflection->MapEnd(const_cast<Message*>(&message), field);
+         ++iter) {
+      Message* map_entry_message = prototype->New();
+      CopyKey(iter.GetKey(), map_entry_message, map_entry_desc->field(0));
+      CopyValue(iter.GetValueRef(), map_entry_message,
+                map_entry_desc->field(1));
+      sorted_map_field->push_back(map_entry_message);
+    }
+    need_release = true;
+  }
+
+  MapEntryMessageComparator comparator(field->message_type());
+  std::stable_sort(sorted_map_field->begin(), sorted_map_field->end(),
+                   comparator);
+  return need_release;
+}
+
+void MapFieldPrinterHelper::CopyKey(const MapKey& key, Message* message,
+                                    const FieldDescriptor* field_desc) {
+  const Reflection* reflection = message->GetReflection();
+  switch (field_desc->cpp_type()) {
+    case FieldDescriptor::CPPTYPE_DOUBLE:
+    case FieldDescriptor::CPPTYPE_FLOAT:
+    case FieldDescriptor::CPPTYPE_ENUM:
+    case FieldDescriptor::CPPTYPE_MESSAGE:
+      GOOGLE_LOG(ERROR) << "Not supported.";
+      break;
+    case FieldDescriptor::CPPTYPE_STRING:
+      reflection->SetString(message, field_desc, key.GetStringValue());
+      return;
+    case FieldDescriptor::CPPTYPE_INT64:
+      reflection->SetInt64(message, field_desc, key.GetInt64Value());
+      return;
+    case FieldDescriptor::CPPTYPE_INT32:
+      reflection->SetInt32(message, field_desc, key.GetInt32Value());
+      return;
+    case FieldDescriptor::CPPTYPE_UINT64:
+      reflection->SetUInt64(message, field_desc, key.GetUInt64Value());
+      return;
+    case FieldDescriptor::CPPTYPE_UINT32:
+      reflection->SetUInt32(message, field_desc, key.GetUInt32Value());
+      return;
+    case FieldDescriptor::CPPTYPE_BOOL:
+      reflection->SetBool(message, field_desc, key.GetBoolValue());
+      return;
+  }
+}
+
+void MapFieldPrinterHelper::CopyValue(const MapValueRef& value,
+                                      Message* message,
+                                      const FieldDescriptor* field_desc) {
+  const Reflection* reflection = message->GetReflection();
+  switch (field_desc->cpp_type()) {
+    case FieldDescriptor::CPPTYPE_DOUBLE:
+      reflection->SetDouble(message, field_desc, value.GetDoubleValue());
+      return;
+    case FieldDescriptor::CPPTYPE_FLOAT:
+      reflection->SetFloat(message, field_desc, value.GetFloatValue());
+      return;
+    case FieldDescriptor::CPPTYPE_ENUM:
+      reflection->SetEnumValue(message, field_desc, value.GetEnumValue());
+      return;
+    case FieldDescriptor::CPPTYPE_MESSAGE: {
+      Message* sub_message = value.GetMessageValue().New();
+      sub_message->CopyFrom(value.GetMessageValue());
+      reflection->SetAllocatedMessage(message, sub_message, field_desc);
+      return;
+    }
+    case FieldDescriptor::CPPTYPE_STRING:
+      reflection->SetString(message, field_desc, value.GetStringValue());
+      return;
+    case FieldDescriptor::CPPTYPE_INT64:
+      reflection->SetInt64(message, field_desc, value.GetInt64Value());
+      return;
+    case FieldDescriptor::CPPTYPE_INT32:
+      reflection->SetInt32(message, field_desc, value.GetInt32Value());
+      return;
+    case FieldDescriptor::CPPTYPE_UINT64:
+      reflection->SetUInt64(message, field_desc, value.GetUInt64Value());
+      return;
+    case FieldDescriptor::CPPTYPE_UINT32:
+      reflection->SetUInt32(message, field_desc, value.GetUInt32Value());
+      return;
+    case FieldDescriptor::CPPTYPE_BOOL:
+      reflection->SetBool(message, field_desc, value.GetBoolValue());
+      return;
+  }
+}
+}  // namespace internal
+
+void TextFormat::Printer::PrintField(const Message& message,
+                                     const Reflection* reflection,
+                                     const FieldDescriptor* field,
+                                     TextGenerator* generator) const {
+  if (use_short_repeated_primitives_ && field->is_repeated() &&
+      field->cpp_type() != FieldDescriptor::CPPTYPE_STRING &&
+      field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+    PrintShortRepeatedField(message, reflection, field, generator);
+    return;
+  }
+
+  int count = 0;
+
+  if (field->is_repeated()) {
+    count = reflection->FieldSize(message, field);
+  } else if (reflection->HasField(message, field) ||
+             field->containing_type()->options().map_entry()) {
+    count = 1;
+  }
+
+  std::vector<const Message*> sorted_map_field;
+  bool need_release = false;
+  bool is_map = field->is_map();
+  if (is_map) {
+    need_release = internal::MapFieldPrinterHelper::SortMap(
+        message, reflection, field, &sorted_map_field);
+  }
+
+  for (int j = 0; j < count; ++j) {
+    const int field_index = field->is_repeated() ? j : -1;
+
+    PrintFieldName(message, field_index, count, reflection, field, generator);
+
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      const FastFieldValuePrinter* printer = GetFieldPrinter(field);
+      const Message& sub_message =
+          field->is_repeated()
+              ? (is_map ? *sorted_map_field[j]
+                        : reflection->GetRepeatedMessage(message, field, j))
+              : reflection->GetMessage(message, field);
+      printer->PrintMessageStart(sub_message, field_index, count,
+                                 single_line_mode_, generator);
+      generator->Indent();
+      if (!printer->PrintMessageContent(sub_message, field_index, count,
+                                        single_line_mode_, generator)) {
+        Print(sub_message, generator);
+      }
+      generator->Outdent();
+      printer->PrintMessageEnd(sub_message, field_index, count,
+                               single_line_mode_, generator);
+    } else {
+      generator->PrintMaybeWithMarker(": ");
+      // Write the field value.
+      PrintFieldValue(message, reflection, field, field_index, generator);
+      if (single_line_mode_) {
+        generator->PrintLiteral(" ");
+      } else {
+        generator->PrintLiteral("\n");
+      }
+    }
+  }
+
+  if (need_release) {
+    for (const Message* message_to_delete : sorted_map_field) {
+      delete message_to_delete;
+    }
+  }
+}
+
+void TextFormat::Printer::PrintShortRepeatedField(
+    const Message& message, const Reflection* reflection,
+    const FieldDescriptor* field, TextGenerator* generator) const {
+  // Print primitive repeated field in short form.
+  int size = reflection->FieldSize(message, field);
+  PrintFieldName(message, /*field_index=*/-1, /*field_count=*/size, reflection,
+                 field, generator);
+  generator->PrintMaybeWithMarker(": ", "[");
+  for (int i = 0; i < size; i++) {
+    if (i > 0) generator->PrintLiteral(", ");
+    PrintFieldValue(message, reflection, field, i, generator);
+  }
+  if (single_line_mode_) {
+    generator->PrintLiteral("] ");
+  } else {
+    generator->PrintLiteral("]\n");
+  }
+}
+
+void TextFormat::Printer::PrintFieldName(const Message& message,
+                                         int field_index, int field_count,
+                                         const Reflection* reflection,
+                                         const FieldDescriptor* field,
+                                         TextGenerator* generator) const {
+  // if use_field_number_ is true, prints field number instead
+  // of field name.
+  if (use_field_number_) {
+    generator->PrintString(StrCat(field->number()));
+    return;
+  }
+
+  const FastFieldValuePrinter* printer = GetFieldPrinter(field);
+  printer->PrintFieldName(message, field_index, field_count, reflection, field,
+                          generator);
+}
+
+void TextFormat::Printer::PrintFieldValue(const Message& message,
+                                          const Reflection* reflection,
+                                          const FieldDescriptor* field,
+                                          int index,
+                                          TextGenerator* generator) const {
+  GOOGLE_DCHECK(field->is_repeated() || (index == -1))
+      << "Index must be -1 for non-repeated fields";
+
+  const FastFieldValuePrinter* printer = GetFieldPrinter(field);
+
+  switch (field->cpp_type()) {
+#define OUTPUT_FIELD(CPPTYPE, METHOD)                                \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE:                           \
+    printer->Print##METHOD(                                          \
+        field->is_repeated()                                         \
+            ? reflection->GetRepeated##METHOD(message, field, index) \
+            : reflection->Get##METHOD(message, field),               \
+        generator);                                                  \
+    break
+
+    OUTPUT_FIELD(INT32, Int32);
+    OUTPUT_FIELD(INT64, Int64);
+    OUTPUT_FIELD(UINT32, UInt32);
+    OUTPUT_FIELD(UINT64, UInt64);
+    OUTPUT_FIELD(FLOAT, Float);
+    OUTPUT_FIELD(DOUBLE, Double);
+    OUTPUT_FIELD(BOOL, Bool);
+#undef OUTPUT_FIELD
+
+    case FieldDescriptor::CPPTYPE_STRING: {
+      std::string scratch;
+      const std::string& value =
+          field->is_repeated()
+              ? reflection->GetRepeatedStringReference(message, field, index,
+                                                       &scratch)
+              : reflection->GetStringReference(message, field, &scratch);
+      const std::string* value_to_print = &value;
+      std::string truncated_value;
+      if (truncate_string_field_longer_than_ > 0 &&
+          static_cast<size_t>(truncate_string_field_longer_than_) <
+              value.size()) {
+        truncated_value = value.substr(0, truncate_string_field_longer_than_) +
+                          "...<truncated>...";
+        value_to_print = &truncated_value;
+      }
+      if (field->type() == FieldDescriptor::TYPE_STRING) {
+        printer->PrintString(*value_to_print, generator);
+      } else {
+        GOOGLE_DCHECK_EQ(field->type(), FieldDescriptor::TYPE_BYTES);
+        printer->PrintBytes(*value_to_print, generator);
+      }
+      break;
+    }
+
+    case FieldDescriptor::CPPTYPE_ENUM: {
+      int enum_value =
+          field->is_repeated()
+              ? reflection->GetRepeatedEnumValue(message, field, index)
+              : reflection->GetEnumValue(message, field);
+      const EnumValueDescriptor* enum_desc =
+          field->enum_type()->FindValueByNumber(enum_value);
+      if (enum_desc != nullptr) {
+        printer->PrintEnum(enum_value, enum_desc->name(), generator);
+      } else {
+        // Ordinarily, enum_desc should not be null, because proto2 has the
+        // invariant that set enum field values must be in-range, but with the
+        // new integer-based API for enums (or the RepeatedField<int> loophole),
+        // it is possible for the user to force an unknown integer value.  So we
+        // simply use the integer value itself as the enum value name in this
+        // case.
+        printer->PrintEnum(enum_value, StrCat(enum_value), generator);
+      }
+      break;
+    }
+
+    case FieldDescriptor::CPPTYPE_MESSAGE:
+      Print(field->is_repeated()
+                ? reflection->GetRepeatedMessage(message, field, index)
+                : reflection->GetMessage(message, field),
+            generator);
+      break;
+  }
+}
+
+/* static */ bool TextFormat::Print(const Message& message,
+                                    io::ZeroCopyOutputStream* output) {
+  return Printer().Print(message, output);
+}
+
+/* static */ bool TextFormat::PrintUnknownFields(
+    const UnknownFieldSet& unknown_fields, io::ZeroCopyOutputStream* output) {
+  return Printer().PrintUnknownFields(unknown_fields, output);
+}
+
+/* static */ bool TextFormat::PrintToString(const Message& message,
+                                            std::string* output) {
+  return Printer().PrintToString(message, output);
+}
+
+/* static */ bool TextFormat::PrintUnknownFieldsToString(
+    const UnknownFieldSet& unknown_fields, std::string* output) {
+  return Printer().PrintUnknownFieldsToString(unknown_fields, output);
+}
+
+/* static */ void TextFormat::PrintFieldValueToString(
+    const Message& message, const FieldDescriptor* field, int index,
+    std::string* output) {
+  return Printer().PrintFieldValueToString(message, field, index, output);
+}
+
+/* static */ bool TextFormat::ParseFieldValueFromString(
+    const std::string& input, const FieldDescriptor* field, Message* message) {
+  return Parser().ParseFieldValueFromString(input, field, message);
+}
+
+void TextFormat::Printer::PrintUnknownFields(
+    const UnknownFieldSet& unknown_fields, TextGenerator* generator,
+    int recursion_budget) const {
+  for (int i = 0; i < unknown_fields.field_count(); i++) {
+    const UnknownField& field = unknown_fields.field(i);
+    std::string field_number = StrCat(field.number());
+
+    switch (field.type()) {
+      case UnknownField::TYPE_VARINT:
+        generator->PrintString(field_number);
+        generator->PrintMaybeWithMarker(": ");
+        generator->PrintString(StrCat(field.varint()));
+        if (single_line_mode_) {
+          generator->PrintLiteral(" ");
+        } else {
+          generator->PrintLiteral("\n");
+        }
+        break;
+      case UnknownField::TYPE_FIXED32: {
+        generator->PrintString(field_number);
+        generator->PrintMaybeWithMarker(": ", "0x");
+        generator->PrintString(
+            StrCat(strings::Hex(field.fixed32(), strings::ZERO_PAD_8)));
+        if (single_line_mode_) {
+          generator->PrintLiteral(" ");
+        } else {
+          generator->PrintLiteral("\n");
+        }
+        break;
+      }
+      case UnknownField::TYPE_FIXED64: {
+        generator->PrintString(field_number);
+        generator->PrintMaybeWithMarker(": ", "0x");
+        generator->PrintString(
+            StrCat(strings::Hex(field.fixed64(), strings::ZERO_PAD_16)));
+        if (single_line_mode_) {
+          generator->PrintLiteral(" ");
+        } else {
+          generator->PrintLiteral("\n");
+        }
+        break;
+      }
+      case UnknownField::TYPE_LENGTH_DELIMITED: {
+        generator->PrintString(field_number);
+        const std::string& value = field.length_delimited();
+        // We create a CodedInputStream so that we can adhere to our recursion
+        // budget when we attempt to parse the data. UnknownFieldSet parsing is
+        // recursive because of groups.
+        io::CodedInputStream input_stream(
+            reinterpret_cast<const uint8_t*>(value.data()), value.size());
+        input_stream.SetRecursionLimit(recursion_budget);
+        UnknownFieldSet embedded_unknown_fields;
+        if (!value.empty() && recursion_budget > 0 &&
+            embedded_unknown_fields.ParseFromCodedStream(&input_stream)) {
+          // This field is parseable as a Message.
+          // So it is probably an embedded message.
+          if (single_line_mode_) {
+            generator->PrintMaybeWithMarker(" ", "{ ");
+          } else {
+            generator->PrintMaybeWithMarker(" ", "{\n");
+            generator->Indent();
+          }
+          PrintUnknownFields(embedded_unknown_fields, generator,
+                             recursion_budget - 1);
+          if (single_line_mode_) {
+            generator->PrintLiteral("} ");
+          } else {
+            generator->Outdent();
+            generator->PrintLiteral("}\n");
+          }
+        } else {
+          // This field is not parseable as a Message (or we ran out of
+          // recursion budget). So it is probably just a plain string.
+          generator->PrintMaybeWithMarker(": ", "\"");
+          generator->PrintString(CEscape(value));
+          if (single_line_mode_) {
+            generator->PrintLiteral("\" ");
+          } else {
+            generator->PrintLiteral("\"\n");
+          }
+        }
+        break;
+      }
+      case UnknownField::TYPE_GROUP:
+        generator->PrintString(field_number);
+        if (single_line_mode_) {
+          generator->PrintMaybeWithMarker(" ", "{ ");
+        } else {
+          generator->PrintMaybeWithMarker(" ", "{\n");
+          generator->Indent();
+        }
+        // For groups, we recurse without checking the budget. This is OK,
+        // because if the groups were too deeply nested then we would have
+        // already rejected the message when we originally parsed it.
+        PrintUnknownFields(field.group(), generator, recursion_budget - 1);
+        if (single_line_mode_) {
+          generator->PrintLiteral("} ");
+        } else {
+          generator->Outdent();
+          generator->PrintLiteral("}\n");
+        }
+        break;
+    }
+  }
+}
+
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/timestamp.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/timestamp.pb.cpp
new file mode 100644
index 0000000..728a004
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/timestamp.pb.cpp
@@ -0,0 +1,307 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/timestamp.proto
+
+#include <google/protobuf/timestamp.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR Timestamp::Timestamp(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.seconds_)*/int64_t{0}
+  , /*decltype(_impl_.nanos_)*/0
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct TimestampDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR TimestampDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~TimestampDefaultTypeInternal() {}
+  union {
+    Timestamp _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 TimestampDefaultTypeInternal _Timestamp_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2ftimestamp_2eproto[1];
+static constexpr ::_pb::EnumDescriptor const** file_level_enum_descriptors_google_2fprotobuf_2ftimestamp_2eproto = nullptr;
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2ftimestamp_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2ftimestamp_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Timestamp, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Timestamp, _impl_.seconds_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Timestamp, _impl_.nanos_),
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Timestamp)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_Timestamp_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2ftimestamp_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n\037google/protobuf/timestamp.proto\022\017googl"
+  "e.protobuf\"+\n\tTimestamp\022\017\n\007seconds\030\001 \001(\003"
+  "\022\r\n\005nanos\030\002 \001(\005B\205\001\n\023com.google.protobufB"
+  "\016TimestampProtoP\001Z2google.golang.org/pro"
+  "tobuf/types/known/timestamppb\370\001\001\242\002\003GPB\252\002"
+  "\036Google.Protobuf.WellKnownTypesb\006proto3"
+  ;
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2ftimestamp_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2ftimestamp_2eproto = {
+    false, false, 239, descriptor_table_protodef_google_2fprotobuf_2ftimestamp_2eproto,
+    "google/protobuf/timestamp.proto",
+    &descriptor_table_google_2fprotobuf_2ftimestamp_2eproto_once, nullptr, 0, 1,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2ftimestamp_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2ftimestamp_2eproto, file_level_enum_descriptors_google_2fprotobuf_2ftimestamp_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2ftimestamp_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2ftimestamp_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2ftimestamp_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2ftimestamp_2eproto(&descriptor_table_google_2fprotobuf_2ftimestamp_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class Timestamp::_Internal {
+ public:
+};
+
+Timestamp::Timestamp(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Timestamp)
+}
+Timestamp::Timestamp(const Timestamp& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Timestamp* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.seconds_){}
+    , decltype(_impl_.nanos_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  ::memcpy(&_impl_.seconds_, &from._impl_.seconds_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.nanos_) -
+    reinterpret_cast<char*>(&_impl_.seconds_)) + sizeof(_impl_.nanos_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Timestamp)
+}
+
+inline void Timestamp::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.seconds_){int64_t{0}}
+    , decltype(_impl_.nanos_){0}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+Timestamp::~Timestamp() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Timestamp)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Timestamp::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void Timestamp::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Timestamp::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Timestamp)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  ::memset(&_impl_.seconds_, 0, static_cast<size_t>(
+      reinterpret_cast<char*>(&_impl_.nanos_) -
+      reinterpret_cast<char*>(&_impl_.seconds_)) + sizeof(_impl_.nanos_));
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Timestamp::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // int64 seconds = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _impl_.seconds_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // int32 nanos = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _impl_.nanos_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Timestamp::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Timestamp)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // int64 seconds = 1;
+  if (this->_internal_seconds() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt64ToArray(1, this->_internal_seconds(), target);
+  }
+
+  // int32 nanos = 2;
+  if (this->_internal_nanos() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_nanos(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Timestamp)
+  return target;
+}
+
+size_t Timestamp::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Timestamp)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // int64 seconds = 1;
+  if (this->_internal_seconds() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int64SizePlusOne(this->_internal_seconds());
+  }
+
+  // int32 nanos = 2;
+  if (this->_internal_nanos() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_nanos());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Timestamp::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Timestamp::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Timestamp::GetClassData() const { return &_class_data_; }
+
+
+void Timestamp::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Timestamp*>(&to_msg);
+  auto& from = static_cast<const Timestamp&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Timestamp)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (from._internal_seconds() != 0) {
+    _this->_internal_set_seconds(from._internal_seconds());
+  }
+  if (from._internal_nanos() != 0) {
+    _this->_internal_set_nanos(from._internal_nanos());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Timestamp::CopyFrom(const Timestamp& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Timestamp)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Timestamp::IsInitialized() const {
+  return true;
+}
+
+void Timestamp::InternalSwap(Timestamp* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(Timestamp, _impl_.nanos_)
+      + sizeof(Timestamp::_impl_.nanos_)
+      - PROTOBUF_FIELD_OFFSET(Timestamp, _impl_.seconds_)>(
+          reinterpret_cast<char*>(&_impl_.seconds_),
+          reinterpret_cast<char*>(&other->_impl_.seconds_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Timestamp::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2ftimestamp_2eproto_getter, &descriptor_table_google_2fprotobuf_2ftimestamp_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2ftimestamp_2eproto[0]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Timestamp*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Timestamp >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Timestamp >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/type.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/type.pb.cpp
new file mode 100644
index 0000000..e29bbb8
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/type.pb.cpp
@@ -0,0 +1,2157 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/type.proto
+
+#include <google/protobuf/type.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR Type::Type(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.fields_)*/{}
+  , /*decltype(_impl_.oneofs_)*/{}
+  , /*decltype(_impl_.options_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.source_context_)*/nullptr
+  , /*decltype(_impl_.syntax_)*/0
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct TypeDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR TypeDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~TypeDefaultTypeInternal() {}
+  union {
+    Type _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 TypeDefaultTypeInternal _Type_default_instance_;
+PROTOBUF_CONSTEXPR Field::Field(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.options_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.type_url_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.json_name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.default_value_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.kind_)*/0
+  , /*decltype(_impl_.cardinality_)*/0
+  , /*decltype(_impl_.number_)*/0
+  , /*decltype(_impl_.oneof_index_)*/0
+  , /*decltype(_impl_.packed_)*/false
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct FieldDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR FieldDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~FieldDefaultTypeInternal() {}
+  union {
+    Field _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FieldDefaultTypeInternal _Field_default_instance_;
+PROTOBUF_CONSTEXPR Enum::Enum(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.enumvalue_)*/{}
+  , /*decltype(_impl_.options_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.source_context_)*/nullptr
+  , /*decltype(_impl_.syntax_)*/0
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct EnumDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR EnumDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~EnumDefaultTypeInternal() {}
+  union {
+    Enum _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 EnumDefaultTypeInternal _Enum_default_instance_;
+PROTOBUF_CONSTEXPR EnumValue::EnumValue(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.options_)*/{}
+  , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.number_)*/0
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct EnumValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR EnumValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~EnumValueDefaultTypeInternal() {}
+  union {
+    EnumValue _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 EnumValueDefaultTypeInternal _EnumValue_default_instance_;
+PROTOBUF_CONSTEXPR Option::Option(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_.value_)*/nullptr
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct OptionDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR OptionDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~OptionDefaultTypeInternal() {}
+  union {
+    Option _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 OptionDefaultTypeInternal _Option_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2ftype_2eproto[5];
+static const ::_pb::EnumDescriptor* file_level_enum_descriptors_google_2fprotobuf_2ftype_2eproto[3];
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2ftype_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2ftype_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _impl_.fields_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _impl_.oneofs_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _impl_.options_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _impl_.source_context_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _impl_.syntax_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _impl_.kind_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _impl_.cardinality_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _impl_.number_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _impl_.type_url_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _impl_.oneof_index_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _impl_.packed_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _impl_.options_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _impl_.json_name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _impl_.default_value_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Enum, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Enum, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Enum, _impl_.enumvalue_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Enum, _impl_.options_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Enum, _impl_.source_context_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Enum, _impl_.syntax_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValue, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValue, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValue, _impl_.number_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValue, _impl_.options_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Option, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Option, _impl_.name_),
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Option, _impl_.value_),
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Type)},
+  { 12, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Field)},
+  { 28, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Enum)},
+  { 39, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::EnumValue)},
+  { 48, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Option)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_Type_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_Field_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_Enum_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_EnumValue_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_Option_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2ftype_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n\032google/protobuf/type.proto\022\017google.pro"
+  "tobuf\032\031google/protobuf/any.proto\032$google"
+  "/protobuf/source_context.proto\"\327\001\n\004Type\022"
+  "\014\n\004name\030\001 \001(\t\022&\n\006fields\030\002 \003(\0132\026.google.p"
+  "rotobuf.Field\022\016\n\006oneofs\030\003 \003(\t\022(\n\007options"
+  "\030\004 \003(\0132\027.google.protobuf.Option\0226\n\016sourc"
+  "e_context\030\005 \001(\0132\036.google.protobuf.Source"
+  "Context\022\'\n\006syntax\030\006 \001(\0162\027.google.protobu"
+  "f.Syntax\"\325\005\n\005Field\022)\n\004kind\030\001 \001(\0162\033.googl"
+  "e.protobuf.Field.Kind\0227\n\013cardinality\030\002 \001"
+  "(\0162\".google.protobuf.Field.Cardinality\022\016"
+  "\n\006number\030\003 \001(\005\022\014\n\004name\030\004 \001(\t\022\020\n\010type_url"
+  "\030\006 \001(\t\022\023\n\013oneof_index\030\007 \001(\005\022\016\n\006packed\030\010 "
+  "\001(\010\022(\n\007options\030\t \003(\0132\027.google.protobuf.O"
+  "ption\022\021\n\tjson_name\030\n \001(\t\022\025\n\rdefault_valu"
+  "e\030\013 \001(\t\"\310\002\n\004Kind\022\020\n\014TYPE_UNKNOWN\020\000\022\017\n\013TY"
+  "PE_DOUBLE\020\001\022\016\n\nTYPE_FLOAT\020\002\022\016\n\nTYPE_INT6"
+  "4\020\003\022\017\n\013TYPE_UINT64\020\004\022\016\n\nTYPE_INT32\020\005\022\020\n\014"
+  "TYPE_FIXED64\020\006\022\020\n\014TYPE_FIXED32\020\007\022\r\n\tTYPE"
+  "_BOOL\020\010\022\017\n\013TYPE_STRING\020\t\022\016\n\nTYPE_GROUP\020\n"
+  "\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\nTYPE_BYTES\020\014\022\017\n\013TY"
+  "PE_UINT32\020\r\022\r\n\tTYPE_ENUM\020\016\022\021\n\rTYPE_SFIXE"
+  "D32\020\017\022\021\n\rTYPE_SFIXED64\020\020\022\017\n\013TYPE_SINT32\020"
+  "\021\022\017\n\013TYPE_SINT64\020\022\"t\n\013Cardinality\022\027\n\023CAR"
+  "DINALITY_UNKNOWN\020\000\022\030\n\024CARDINALITY_OPTION"
+  "AL\020\001\022\030\n\024CARDINALITY_REQUIRED\020\002\022\030\n\024CARDIN"
+  "ALITY_REPEATED\020\003\"\316\001\n\004Enum\022\014\n\004name\030\001 \001(\t\022"
+  "-\n\tenumvalue\030\002 \003(\0132\032.google.protobuf.Enu"
+  "mValue\022(\n\007options\030\003 \003(\0132\027.google.protobu"
+  "f.Option\0226\n\016source_context\030\004 \001(\0132\036.googl"
+  "e.protobuf.SourceContext\022\'\n\006syntax\030\005 \001(\016"
+  "2\027.google.protobuf.Syntax\"S\n\tEnumValue\022\014"
+  "\n\004name\030\001 \001(\t\022\016\n\006number\030\002 \001(\005\022(\n\007options\030"
+  "\003 \003(\0132\027.google.protobuf.Option\";\n\006Option"
+  "\022\014\n\004name\030\001 \001(\t\022#\n\005value\030\002 \001(\0132\024.google.p"
+  "rotobuf.Any*.\n\006Syntax\022\021\n\rSYNTAX_PROTO2\020\000"
+  "\022\021\n\rSYNTAX_PROTO3\020\001B{\n\023com.google.protob"
+  "ufB\tTypeProtoP\001Z-google.golang.org/proto"
+  "buf/types/known/typepb\370\001\001\242\002\003GPB\252\002\036Google"
+  ".Protobuf.WellKnownTypesb\006proto3"
+  ;
+static const ::_pbi::DescriptorTable* const descriptor_table_google_2fprotobuf_2ftype_2eproto_deps[2] = {
+  &::descriptor_table_google_2fprotobuf_2fany_2eproto,
+  &::descriptor_table_google_2fprotobuf_2fsource_5fcontext_2eproto,
+};
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2ftype_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2ftype_2eproto = {
+    false, false, 1592, descriptor_table_protodef_google_2fprotobuf_2ftype_2eproto,
+    "google/protobuf/type.proto",
+    &descriptor_table_google_2fprotobuf_2ftype_2eproto_once, descriptor_table_google_2fprotobuf_2ftype_2eproto_deps, 2, 5,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2ftype_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2ftype_2eproto, file_level_enum_descriptors_google_2fprotobuf_2ftype_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2ftype_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2ftype_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2ftype_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2ftype_2eproto(&descriptor_table_google_2fprotobuf_2ftype_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Field_Kind_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2ftype_2eproto);
+  return file_level_enum_descriptors_google_2fprotobuf_2ftype_2eproto[0];
+}
+bool Field_Kind_IsValid(int value) {
+  switch (value) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+    case 8:
+    case 9:
+    case 10:
+    case 11:
+    case 12:
+    case 13:
+    case 14:
+    case 15:
+    case 16:
+    case 17:
+    case 18:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr Field_Kind Field::TYPE_UNKNOWN;
+constexpr Field_Kind Field::TYPE_DOUBLE;
+constexpr Field_Kind Field::TYPE_FLOAT;
+constexpr Field_Kind Field::TYPE_INT64;
+constexpr Field_Kind Field::TYPE_UINT64;
+constexpr Field_Kind Field::TYPE_INT32;
+constexpr Field_Kind Field::TYPE_FIXED64;
+constexpr Field_Kind Field::TYPE_FIXED32;
+constexpr Field_Kind Field::TYPE_BOOL;
+constexpr Field_Kind Field::TYPE_STRING;
+constexpr Field_Kind Field::TYPE_GROUP;
+constexpr Field_Kind Field::TYPE_MESSAGE;
+constexpr Field_Kind Field::TYPE_BYTES;
+constexpr Field_Kind Field::TYPE_UINT32;
+constexpr Field_Kind Field::TYPE_ENUM;
+constexpr Field_Kind Field::TYPE_SFIXED32;
+constexpr Field_Kind Field::TYPE_SFIXED64;
+constexpr Field_Kind Field::TYPE_SINT32;
+constexpr Field_Kind Field::TYPE_SINT64;
+constexpr Field_Kind Field::Kind_MIN;
+constexpr Field_Kind Field::Kind_MAX;
+constexpr int Field::Kind_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Field_Cardinality_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2ftype_2eproto);
+  return file_level_enum_descriptors_google_2fprotobuf_2ftype_2eproto[1];
+}
+bool Field_Cardinality_IsValid(int value) {
+  switch (value) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr Field_Cardinality Field::CARDINALITY_UNKNOWN;
+constexpr Field_Cardinality Field::CARDINALITY_OPTIONAL;
+constexpr Field_Cardinality Field::CARDINALITY_REQUIRED;
+constexpr Field_Cardinality Field::CARDINALITY_REPEATED;
+constexpr Field_Cardinality Field::Cardinality_MIN;
+constexpr Field_Cardinality Field::Cardinality_MAX;
+constexpr int Field::Cardinality_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Syntax_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2ftype_2eproto);
+  return file_level_enum_descriptors_google_2fprotobuf_2ftype_2eproto[2];
+}
+bool Syntax_IsValid(int value) {
+  switch (value) {
+    case 0:
+    case 1:
+      return true;
+    default:
+      return false;
+  }
+}
+
+
+// ===================================================================
+
+class Type::_Internal {
+ public:
+  static const ::PROTOBUF_NAMESPACE_ID::SourceContext& source_context(const Type* msg);
+};
+
+const ::PROTOBUF_NAMESPACE_ID::SourceContext&
+Type::_Internal::source_context(const Type* msg) {
+  return *msg->_impl_.source_context_;
+}
+void Type::clear_source_context() {
+  if (GetArenaForAllocation() == nullptr && _impl_.source_context_ != nullptr) {
+    delete _impl_.source_context_;
+  }
+  _impl_.source_context_ = nullptr;
+}
+Type::Type(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Type)
+}
+Type::Type(const Type& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Type* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.fields_){from._impl_.fields_}
+    , decltype(_impl_.oneofs_){from._impl_.oneofs_}
+    , decltype(_impl_.options_){from._impl_.options_}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.source_context_){nullptr}
+    , decltype(_impl_.syntax_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_name().empty()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_source_context()) {
+    _this->_impl_.source_context_ = new ::PROTOBUF_NAMESPACE_ID::SourceContext(*from._impl_.source_context_);
+  }
+  _this->_impl_.syntax_ = from._impl_.syntax_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Type)
+}
+
+inline void Type::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.fields_){arena}
+    , decltype(_impl_.oneofs_){arena}
+    , decltype(_impl_.options_){arena}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.source_context_){nullptr}
+    , decltype(_impl_.syntax_){0}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+Type::~Type() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Type)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Type::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.fields_.~RepeatedPtrField();
+  _impl_.oneofs_.~RepeatedPtrField();
+  _impl_.options_.~RepeatedPtrField();
+  _impl_.name_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.source_context_;
+}
+
+void Type::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Type::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Type)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.fields_.Clear();
+  _impl_.oneofs_.Clear();
+  _impl_.options_.Clear();
+  _impl_.name_.ClearToEmpty();
+  if (GetArenaForAllocation() == nullptr && _impl_.source_context_ != nullptr) {
+    delete _impl_.source_context_;
+  }
+  _impl_.source_context_ = nullptr;
+  _impl_.syntax_ = 0;
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Type::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Type.name"));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.Field fields = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_fields(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated string oneofs = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            auto str = _internal_add_oneofs();
+            ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+            CHK_(ptr);
+            CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Type.oneofs"));
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<26>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.Option options = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_options(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<34>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.SourceContext source_context = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+          ptr = ctx->ParseMessage(_internal_mutable_source_context(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.Syntax syntax = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 48)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          _internal_set_syntax(static_cast<::PROTOBUF_NAMESPACE_ID::Syntax>(val));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Type::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Type)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Type.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // repeated .google.protobuf.Field fields = 2;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_fields_size()); i < n; i++) {
+    const auto& repfield = this->_internal_fields(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(2, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated string oneofs = 3;
+  for (int i = 0, n = this->_internal_oneofs_size(); i < n; i++) {
+    const auto& s = this->_internal_oneofs(i);
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      s.data(), static_cast<int>(s.length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Type.oneofs");
+    target = stream->WriteString(3, s, target);
+  }
+
+  // repeated .google.protobuf.Option options = 4;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_options_size()); i < n; i++) {
+    const auto& repfield = this->_internal_options(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(4, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // .google.protobuf.SourceContext source_context = 5;
+  if (this->_internal_has_source_context()) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(5, _Internal::source_context(this),
+        _Internal::source_context(this).GetCachedSize(), target, stream);
+  }
+
+  // .google.protobuf.Syntax syntax = 6;
+  if (this->_internal_syntax() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      6, this->_internal_syntax(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Type)
+  return target;
+}
+
+size_t Type::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Type)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.Field fields = 2;
+  total_size += 1UL * this->_internal_fields_size();
+  for (const auto& msg : this->_impl_.fields_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated string oneofs = 3;
+  total_size += 1 *
+      ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(_impl_.oneofs_.size());
+  for (int i = 0, n = _impl_.oneofs_.size(); i < n; i++) {
+    total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+      _impl_.oneofs_.Get(i));
+  }
+
+  // repeated .google.protobuf.Option options = 4;
+  total_size += 1UL * this->_internal_options_size();
+  for (const auto& msg : this->_impl_.options_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name());
+  }
+
+  // .google.protobuf.SourceContext source_context = 5;
+  if (this->_internal_has_source_context()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+        *_impl_.source_context_);
+  }
+
+  // .google.protobuf.Syntax syntax = 6;
+  if (this->_internal_syntax() != 0) {
+    total_size += 1 +
+      ::_pbi::WireFormatLite::EnumSize(this->_internal_syntax());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Type::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Type::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Type::GetClassData() const { return &_class_data_; }
+
+
+void Type::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Type*>(&to_msg);
+  auto& from = static_cast<const Type&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Type)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.fields_.MergeFrom(from._impl_.fields_);
+  _this->_impl_.oneofs_.MergeFrom(from._impl_.oneofs_);
+  _this->_impl_.options_.MergeFrom(from._impl_.options_);
+  if (!from._internal_name().empty()) {
+    _this->_internal_set_name(from._internal_name());
+  }
+  if (from._internal_has_source_context()) {
+    _this->_internal_mutable_source_context()->::PROTOBUF_NAMESPACE_ID::SourceContext::MergeFrom(
+        from._internal_source_context());
+  }
+  if (from._internal_syntax() != 0) {
+    _this->_internal_set_syntax(from._internal_syntax());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Type::CopyFrom(const Type& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Type)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Type::IsInitialized() const {
+  return true;
+}
+
+void Type::InternalSwap(Type* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.fields_.InternalSwap(&other->_impl_.fields_);
+  _impl_.oneofs_.InternalSwap(&other->_impl_.oneofs_);
+  _impl_.options_.InternalSwap(&other->_impl_.options_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(Type, _impl_.syntax_)
+      + sizeof(Type::_impl_.syntax_)
+      - PROTOBUF_FIELD_OFFSET(Type, _impl_.source_context_)>(
+          reinterpret_cast<char*>(&_impl_.source_context_),
+          reinterpret_cast<char*>(&other->_impl_.source_context_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Type::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2ftype_2eproto_getter, &descriptor_table_google_2fprotobuf_2ftype_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2ftype_2eproto[0]);
+}
+
+// ===================================================================
+
+class Field::_Internal {
+ public:
+};
+
+Field::Field(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Field)
+}
+Field::Field(const Field& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Field* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.options_){from._impl_.options_}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.type_url_){}
+    , decltype(_impl_.json_name_){}
+    , decltype(_impl_.default_value_){}
+    , decltype(_impl_.kind_){}
+    , decltype(_impl_.cardinality_){}
+    , decltype(_impl_.number_){}
+    , decltype(_impl_.oneof_index_){}
+    , decltype(_impl_.packed_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_name().empty()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.type_url_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.type_url_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_type_url().empty()) {
+    _this->_impl_.type_url_.Set(from._internal_type_url(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.json_name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.json_name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_json_name().empty()) {
+    _this->_impl_.json_name_.Set(from._internal_json_name(), 
+      _this->GetArenaForAllocation());
+  }
+  _impl_.default_value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.default_value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_default_value().empty()) {
+    _this->_impl_.default_value_.Set(from._internal_default_value(), 
+      _this->GetArenaForAllocation());
+  }
+  ::memcpy(&_impl_.kind_, &from._impl_.kind_,
+    static_cast<size_t>(reinterpret_cast<char*>(&_impl_.packed_) -
+    reinterpret_cast<char*>(&_impl_.kind_)) + sizeof(_impl_.packed_));
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Field)
+}
+
+inline void Field::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.options_){arena}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.type_url_){}
+    , decltype(_impl_.json_name_){}
+    , decltype(_impl_.default_value_){}
+    , decltype(_impl_.kind_){0}
+    , decltype(_impl_.cardinality_){0}
+    , decltype(_impl_.number_){0}
+    , decltype(_impl_.oneof_index_){0}
+    , decltype(_impl_.packed_){false}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.type_url_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.type_url_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.json_name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.json_name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.default_value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.default_value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+Field::~Field() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Field)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Field::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.options_.~RepeatedPtrField();
+  _impl_.name_.Destroy();
+  _impl_.type_url_.Destroy();
+  _impl_.json_name_.Destroy();
+  _impl_.default_value_.Destroy();
+}
+
+void Field::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Field::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Field)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.options_.Clear();
+  _impl_.name_.ClearToEmpty();
+  _impl_.type_url_.ClearToEmpty();
+  _impl_.json_name_.ClearToEmpty();
+  _impl_.default_value_.ClearToEmpty();
+  ::memset(&_impl_.kind_, 0, static_cast<size_t>(
+      reinterpret_cast<char*>(&_impl_.packed_) -
+      reinterpret_cast<char*>(&_impl_.kind_)) + sizeof(_impl_.packed_));
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Field::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // .google.protobuf.Field.Kind kind = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          _internal_set_kind(static_cast<::PROTOBUF_NAMESPACE_ID::Field_Kind>(val));
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.Field.Cardinality cardinality = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          _internal_set_cardinality(static_cast<::PROTOBUF_NAMESPACE_ID::Field_Cardinality>(val));
+        } else
+          goto handle_unusual;
+        continue;
+      // int32 number = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) {
+          _impl_.number_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // string name = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Field.name"));
+        } else
+          goto handle_unusual;
+        continue;
+      // string type_url = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 50)) {
+          auto str = _internal_mutable_type_url();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Field.type_url"));
+        } else
+          goto handle_unusual;
+        continue;
+      // int32 oneof_index = 7;
+      case 7:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 56)) {
+          _impl_.oneof_index_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // bool packed = 8;
+      case 8:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 64)) {
+          _impl_.packed_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.Option options = 9;
+      case 9:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 74)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_options(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<74>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // string json_name = 10;
+      case 10:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 82)) {
+          auto str = _internal_mutable_json_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Field.json_name"));
+        } else
+          goto handle_unusual;
+        continue;
+      // string default_value = 11;
+      case 11:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 90)) {
+          auto str = _internal_mutable_default_value();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Field.default_value"));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Field::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Field)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // .google.protobuf.Field.Kind kind = 1;
+  if (this->_internal_kind() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      1, this->_internal_kind(), target);
+  }
+
+  // .google.protobuf.Field.Cardinality cardinality = 2;
+  if (this->_internal_cardinality() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      2, this->_internal_cardinality(), target);
+  }
+
+  // int32 number = 3;
+  if (this->_internal_number() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(3, this->_internal_number(), target);
+  }
+
+  // string name = 4;
+  if (!this->_internal_name().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Field.name");
+    target = stream->WriteStringMaybeAliased(
+        4, this->_internal_name(), target);
+  }
+
+  // string type_url = 6;
+  if (!this->_internal_type_url().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_type_url().data(), static_cast<int>(this->_internal_type_url().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Field.type_url");
+    target = stream->WriteStringMaybeAliased(
+        6, this->_internal_type_url(), target);
+  }
+
+  // int32 oneof_index = 7;
+  if (this->_internal_oneof_index() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(7, this->_internal_oneof_index(), target);
+  }
+
+  // bool packed = 8;
+  if (this->_internal_packed() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(8, this->_internal_packed(), target);
+  }
+
+  // repeated .google.protobuf.Option options = 9;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_options_size()); i < n; i++) {
+    const auto& repfield = this->_internal_options(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(9, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // string json_name = 10;
+  if (!this->_internal_json_name().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_json_name().data(), static_cast<int>(this->_internal_json_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Field.json_name");
+    target = stream->WriteStringMaybeAliased(
+        10, this->_internal_json_name(), target);
+  }
+
+  // string default_value = 11;
+  if (!this->_internal_default_value().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_default_value().data(), static_cast<int>(this->_internal_default_value().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Field.default_value");
+    target = stream->WriteStringMaybeAliased(
+        11, this->_internal_default_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Field)
+  return target;
+}
+
+size_t Field::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Field)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.Option options = 9;
+  total_size += 1UL * this->_internal_options_size();
+  for (const auto& msg : this->_impl_.options_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // string name = 4;
+  if (!this->_internal_name().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name());
+  }
+
+  // string type_url = 6;
+  if (!this->_internal_type_url().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_type_url());
+  }
+
+  // string json_name = 10;
+  if (!this->_internal_json_name().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_json_name());
+  }
+
+  // string default_value = 11;
+  if (!this->_internal_default_value().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_default_value());
+  }
+
+  // .google.protobuf.Field.Kind kind = 1;
+  if (this->_internal_kind() != 0) {
+    total_size += 1 +
+      ::_pbi::WireFormatLite::EnumSize(this->_internal_kind());
+  }
+
+  // .google.protobuf.Field.Cardinality cardinality = 2;
+  if (this->_internal_cardinality() != 0) {
+    total_size += 1 +
+      ::_pbi::WireFormatLite::EnumSize(this->_internal_cardinality());
+  }
+
+  // int32 number = 3;
+  if (this->_internal_number() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_number());
+  }
+
+  // int32 oneof_index = 7;
+  if (this->_internal_oneof_index() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_oneof_index());
+  }
+
+  // bool packed = 8;
+  if (this->_internal_packed() != 0) {
+    total_size += 1 + 1;
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Field::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Field::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Field::GetClassData() const { return &_class_data_; }
+
+
+void Field::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Field*>(&to_msg);
+  auto& from = static_cast<const Field&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Field)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.options_.MergeFrom(from._impl_.options_);
+  if (!from._internal_name().empty()) {
+    _this->_internal_set_name(from._internal_name());
+  }
+  if (!from._internal_type_url().empty()) {
+    _this->_internal_set_type_url(from._internal_type_url());
+  }
+  if (!from._internal_json_name().empty()) {
+    _this->_internal_set_json_name(from._internal_json_name());
+  }
+  if (!from._internal_default_value().empty()) {
+    _this->_internal_set_default_value(from._internal_default_value());
+  }
+  if (from._internal_kind() != 0) {
+    _this->_internal_set_kind(from._internal_kind());
+  }
+  if (from._internal_cardinality() != 0) {
+    _this->_internal_set_cardinality(from._internal_cardinality());
+  }
+  if (from._internal_number() != 0) {
+    _this->_internal_set_number(from._internal_number());
+  }
+  if (from._internal_oneof_index() != 0) {
+    _this->_internal_set_oneof_index(from._internal_oneof_index());
+  }
+  if (from._internal_packed() != 0) {
+    _this->_internal_set_packed(from._internal_packed());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Field::CopyFrom(const Field& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Field)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Field::IsInitialized() const {
+  return true;
+}
+
+void Field::InternalSwap(Field* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.options_.InternalSwap(&other->_impl_.options_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.type_url_, lhs_arena,
+      &other->_impl_.type_url_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.json_name_, lhs_arena,
+      &other->_impl_.json_name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.default_value_, lhs_arena,
+      &other->_impl_.default_value_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(Field, _impl_.packed_)
+      + sizeof(Field::_impl_.packed_)
+      - PROTOBUF_FIELD_OFFSET(Field, _impl_.kind_)>(
+          reinterpret_cast<char*>(&_impl_.kind_),
+          reinterpret_cast<char*>(&other->_impl_.kind_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Field::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2ftype_2eproto_getter, &descriptor_table_google_2fprotobuf_2ftype_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2ftype_2eproto[1]);
+}
+
+// ===================================================================
+
+class Enum::_Internal {
+ public:
+  static const ::PROTOBUF_NAMESPACE_ID::SourceContext& source_context(const Enum* msg);
+};
+
+const ::PROTOBUF_NAMESPACE_ID::SourceContext&
+Enum::_Internal::source_context(const Enum* msg) {
+  return *msg->_impl_.source_context_;
+}
+void Enum::clear_source_context() {
+  if (GetArenaForAllocation() == nullptr && _impl_.source_context_ != nullptr) {
+    delete _impl_.source_context_;
+  }
+  _impl_.source_context_ = nullptr;
+}
+Enum::Enum(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Enum)
+}
+Enum::Enum(const Enum& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Enum* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.enumvalue_){from._impl_.enumvalue_}
+    , decltype(_impl_.options_){from._impl_.options_}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.source_context_){nullptr}
+    , decltype(_impl_.syntax_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_name().empty()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_source_context()) {
+    _this->_impl_.source_context_ = new ::PROTOBUF_NAMESPACE_ID::SourceContext(*from._impl_.source_context_);
+  }
+  _this->_impl_.syntax_ = from._impl_.syntax_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Enum)
+}
+
+inline void Enum::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.enumvalue_){arena}
+    , decltype(_impl_.options_){arena}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.source_context_){nullptr}
+    , decltype(_impl_.syntax_){0}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+Enum::~Enum() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Enum)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Enum::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.enumvalue_.~RepeatedPtrField();
+  _impl_.options_.~RepeatedPtrField();
+  _impl_.name_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.source_context_;
+}
+
+void Enum::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Enum::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Enum)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.enumvalue_.Clear();
+  _impl_.options_.Clear();
+  _impl_.name_.ClearToEmpty();
+  if (GetArenaForAllocation() == nullptr && _impl_.source_context_ != nullptr) {
+    delete _impl_.source_context_;
+  }
+  _impl_.source_context_ = nullptr;
+  _impl_.syntax_ = 0;
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Enum::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Enum.name"));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.EnumValue enumvalue = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_enumvalue(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.Option options = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_options(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<26>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.SourceContext source_context = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+          ptr = ctx->ParseMessage(_internal_mutable_source_context(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.Syntax syntax = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 40)) {
+          uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+          _internal_set_syntax(static_cast<::PROTOBUF_NAMESPACE_ID::Syntax>(val));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Enum::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Enum)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Enum.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // repeated .google.protobuf.EnumValue enumvalue = 2;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_enumvalue_size()); i < n; i++) {
+    const auto& repfield = this->_internal_enumvalue(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(2, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // repeated .google.protobuf.Option options = 3;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_options_size()); i < n; i++) {
+    const auto& repfield = this->_internal_options(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(3, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  // .google.protobuf.SourceContext source_context = 4;
+  if (this->_internal_has_source_context()) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(4, _Internal::source_context(this),
+        _Internal::source_context(this).GetCachedSize(), target, stream);
+  }
+
+  // .google.protobuf.Syntax syntax = 5;
+  if (this->_internal_syntax() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteEnumToArray(
+      5, this->_internal_syntax(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Enum)
+  return target;
+}
+
+size_t Enum::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Enum)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.EnumValue enumvalue = 2;
+  total_size += 1UL * this->_internal_enumvalue_size();
+  for (const auto& msg : this->_impl_.enumvalue_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // repeated .google.protobuf.Option options = 3;
+  total_size += 1UL * this->_internal_options_size();
+  for (const auto& msg : this->_impl_.options_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name());
+  }
+
+  // .google.protobuf.SourceContext source_context = 4;
+  if (this->_internal_has_source_context()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+        *_impl_.source_context_);
+  }
+
+  // .google.protobuf.Syntax syntax = 5;
+  if (this->_internal_syntax() != 0) {
+    total_size += 1 +
+      ::_pbi::WireFormatLite::EnumSize(this->_internal_syntax());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Enum::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Enum::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Enum::GetClassData() const { return &_class_data_; }
+
+
+void Enum::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Enum*>(&to_msg);
+  auto& from = static_cast<const Enum&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Enum)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.enumvalue_.MergeFrom(from._impl_.enumvalue_);
+  _this->_impl_.options_.MergeFrom(from._impl_.options_);
+  if (!from._internal_name().empty()) {
+    _this->_internal_set_name(from._internal_name());
+  }
+  if (from._internal_has_source_context()) {
+    _this->_internal_mutable_source_context()->::PROTOBUF_NAMESPACE_ID::SourceContext::MergeFrom(
+        from._internal_source_context());
+  }
+  if (from._internal_syntax() != 0) {
+    _this->_internal_set_syntax(from._internal_syntax());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Enum::CopyFrom(const Enum& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Enum)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Enum::IsInitialized() const {
+  return true;
+}
+
+void Enum::InternalSwap(Enum* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.enumvalue_.InternalSwap(&other->_impl_.enumvalue_);
+  _impl_.options_.InternalSwap(&other->_impl_.options_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(Enum, _impl_.syntax_)
+      + sizeof(Enum::_impl_.syntax_)
+      - PROTOBUF_FIELD_OFFSET(Enum, _impl_.source_context_)>(
+          reinterpret_cast<char*>(&_impl_.source_context_),
+          reinterpret_cast<char*>(&other->_impl_.source_context_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Enum::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2ftype_2eproto_getter, &descriptor_table_google_2fprotobuf_2ftype_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2ftype_2eproto[2]);
+}
+
+// ===================================================================
+
+class EnumValue::_Internal {
+ public:
+};
+
+EnumValue::EnumValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.EnumValue)
+}
+EnumValue::EnumValue(const EnumValue& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  EnumValue* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.options_){from._impl_.options_}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.number_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_name().empty()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  _this->_impl_.number_ = from._impl_.number_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.EnumValue)
+}
+
+inline void EnumValue::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.options_){arena}
+    , decltype(_impl_.name_){}
+    , decltype(_impl_.number_){0}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+EnumValue::~EnumValue() {
+  // @@protoc_insertion_point(destructor:google.protobuf.EnumValue)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void EnumValue::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.options_.~RepeatedPtrField();
+  _impl_.name_.Destroy();
+}
+
+void EnumValue::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void EnumValue::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumValue)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.options_.Clear();
+  _impl_.name_.ClearToEmpty();
+  _impl_.number_ = 0;
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* EnumValue::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.EnumValue.name"));
+        } else
+          goto handle_unusual;
+        continue;
+      // int32 number = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+          _impl_.number_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      // repeated .google.protobuf.Option options = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_options(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<26>(ptr));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* EnumValue::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.EnumValue)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.EnumValue.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // int32 number = 2;
+  if (this->_internal_number() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_number(), target);
+  }
+
+  // repeated .google.protobuf.Option options = 3;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_options_size()); i < n; i++) {
+    const auto& repfield = this->_internal_options(i);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(3, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.EnumValue)
+  return target;
+}
+
+size_t EnumValue::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumValue)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .google.protobuf.Option options = 3;
+  total_size += 1UL * this->_internal_options_size();
+  for (const auto& msg : this->_impl_.options_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name());
+  }
+
+  // int32 number = 2;
+  if (this->_internal_number() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_number());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumValue::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    EnumValue::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumValue::GetClassData() const { return &_class_data_; }
+
+
+void EnumValue::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<EnumValue*>(&to_msg);
+  auto& from = static_cast<const EnumValue&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumValue)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_impl_.options_.MergeFrom(from._impl_.options_);
+  if (!from._internal_name().empty()) {
+    _this->_internal_set_name(from._internal_name());
+  }
+  if (from._internal_number() != 0) {
+    _this->_internal_set_number(from._internal_number());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void EnumValue::CopyFrom(const EnumValue& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumValue)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool EnumValue::IsInitialized() const {
+  return true;
+}
+
+void EnumValue::InternalSwap(EnumValue* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  _impl_.options_.InternalSwap(&other->_impl_.options_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  swap(_impl_.number_, other->_impl_.number_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata EnumValue::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2ftype_2eproto_getter, &descriptor_table_google_2fprotobuf_2ftype_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2ftype_2eproto[3]);
+}
+
+// ===================================================================
+
+class Option::_Internal {
+ public:
+  static const ::PROTOBUF_NAMESPACE_ID::Any& value(const Option* msg);
+};
+
+const ::PROTOBUF_NAMESPACE_ID::Any&
+Option::_Internal::value(const Option* msg) {
+  return *msg->_impl_.value_;
+}
+void Option::clear_value() {
+  if (GetArenaForAllocation() == nullptr && _impl_.value_ != nullptr) {
+    delete _impl_.value_;
+  }
+  _impl_.value_ = nullptr;
+}
+Option::Option(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Option)
+}
+Option::Option(const Option& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Option* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.name_){}
+    , decltype(_impl_.value_){nullptr}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_name().empty()) {
+    _this->_impl_.name_.Set(from._internal_name(), 
+      _this->GetArenaForAllocation());
+  }
+  if (from._internal_has_value()) {
+    _this->_impl_.value_ = new ::PROTOBUF_NAMESPACE_ID::Any(*from._impl_.value_);
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Option)
+}
+
+inline void Option::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.name_){}
+    , decltype(_impl_.value_){nullptr}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.name_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.name_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+Option::~Option() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Option)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Option::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.name_.Destroy();
+  if (this != internal_default_instance()) delete _impl_.value_;
+}
+
+void Option::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Option::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Option)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.name_.ClearToEmpty();
+  if (GetArenaForAllocation() == nullptr && _impl_.value_ != nullptr) {
+    delete _impl_.value_;
+  }
+  _impl_.value_ = nullptr;
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Option::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Option.name"));
+        } else
+          goto handle_unusual;
+        continue;
+      // .google.protobuf.Any value = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+          ptr = ctx->ParseMessage(_internal_mutable_value(), ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Option::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Option)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.Option.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // .google.protobuf.Any value = 2;
+  if (this->_internal_has_value()) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(2, _Internal::value(this),
+        _Internal::value(this).GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Option)
+  return target;
+}
+
+size_t Option::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Option)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (!this->_internal_name().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name());
+  }
+
+  // .google.protobuf.Any value = 2;
+  if (this->_internal_has_value()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+        *_impl_.value_);
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Option::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Option::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Option::GetClassData() const { return &_class_data_; }
+
+
+void Option::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Option*>(&to_msg);
+  auto& from = static_cast<const Option&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Option)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (!from._internal_name().empty()) {
+    _this->_internal_set_name(from._internal_name());
+  }
+  if (from._internal_has_value()) {
+    _this->_internal_mutable_value()->::PROTOBUF_NAMESPACE_ID::Any::MergeFrom(
+        from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Option::CopyFrom(const Option& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Option)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Option::IsInitialized() const {
+  return true;
+}
+
+void Option::InternalSwap(Option* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.name_, lhs_arena,
+      &other->_impl_.name_, rhs_arena
+  );
+  swap(_impl_.value_, other->_impl_.value_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Option::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2ftype_2eproto_getter, &descriptor_table_google_2fprotobuf_2ftype_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2ftype_2eproto[4]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Type*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Type >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Type >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Field*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Field >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Field >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Enum*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Enum >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Enum >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::EnumValue*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::EnumValue >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::EnumValue >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Option*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Option >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Option >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/unknown_field_set.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/unknown_field_set.cpp
new file mode 100644
index 0000000..74c358e
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/unknown_field_set.cpp
@@ -0,0 +1,350 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/unknown_field_set.h>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/generated_message_tctable_decl.h>
+#include <google/protobuf/generated_message_tctable_impl.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/stubs/stl_util.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+const UnknownFieldSet& UnknownFieldSet::default_instance() {
+  static auto instance = internal::OnShutdownDelete(new UnknownFieldSet());
+  return *instance;
+}
+
+void UnknownFieldSet::ClearFallback() {
+  GOOGLE_DCHECK(!fields_.empty());
+  int n = fields_.size();
+  do {
+    (fields_)[--n].Delete();
+  } while (n > 0);
+  fields_.clear();
+}
+
+void UnknownFieldSet::InternalMergeFrom(const UnknownFieldSet& other) {
+  int other_field_count = other.field_count();
+  if (other_field_count > 0) {
+    fields_.reserve(fields_.size() + other_field_count);
+    for (int i = 0; i < other_field_count; i++) {
+      fields_.push_back((other.fields_)[i]);
+      fields_.back().DeepCopy((other.fields_)[i]);
+    }
+  }
+}
+
+void UnknownFieldSet::MergeFrom(const UnknownFieldSet& other) {
+  int other_field_count = other.field_count();
+  if (other_field_count > 0) {
+    fields_.reserve(fields_.size() + other_field_count);
+    for (int i = 0; i < other_field_count; i++) {
+      fields_.push_back((other.fields_)[i]);
+      fields_.back().DeepCopy((other.fields_)[i]);
+    }
+  }
+}
+
+// A specialized MergeFrom for performance when we are merging from an UFS that
+// is temporary and can be destroyed in the process.
+void UnknownFieldSet::MergeFromAndDestroy(UnknownFieldSet* other) {
+  if (fields_.empty()) {
+    fields_ = std::move(other->fields_);
+  } else {
+    fields_.insert(fields_.end(),
+                   std::make_move_iterator(other->fields_.begin()),
+                   std::make_move_iterator(other->fields_.end()));
+  }
+  other->fields_.clear();
+}
+
+void UnknownFieldSet::MergeToInternalMetadata(
+    const UnknownFieldSet& other, internal::InternalMetadata* metadata) {
+  metadata->mutable_unknown_fields<UnknownFieldSet>()->MergeFrom(other);
+}
+
+size_t UnknownFieldSet::SpaceUsedExcludingSelfLong() const {
+  if (fields_.empty()) return 0;
+
+  size_t total_size = sizeof(UnknownField) * fields_.capacity();
+
+  for (const UnknownField& field : fields_) {
+    switch (field.type()) {
+      case UnknownField::TYPE_LENGTH_DELIMITED:
+        total_size += sizeof(*field.data_.length_delimited_.string_value) +
+                      internal::StringSpaceUsedExcludingSelfLong(
+                          *field.data_.length_delimited_.string_value);
+        break;
+      case UnknownField::TYPE_GROUP:
+        total_size += field.data_.group_->SpaceUsedLong();
+        break;
+      default:
+        break;
+    }
+  }
+  return total_size;
+}
+
+size_t UnknownFieldSet::SpaceUsedLong() const {
+  return sizeof(*this) + SpaceUsedExcludingSelf();
+}
+
+void UnknownFieldSet::AddVarint(int number, uint64_t value) {
+  UnknownField field;
+  field.number_ = number;
+  field.SetType(UnknownField::TYPE_VARINT);
+  field.data_.varint_ = value;
+  fields_.push_back(field);
+}
+
+void UnknownFieldSet::AddFixed32(int number, uint32_t value) {
+  UnknownField field;
+  field.number_ = number;
+  field.SetType(UnknownField::TYPE_FIXED32);
+  field.data_.fixed32_ = value;
+  fields_.push_back(field);
+}
+
+void UnknownFieldSet::AddFixed64(int number, uint64_t value) {
+  UnknownField field;
+  field.number_ = number;
+  field.SetType(UnknownField::TYPE_FIXED64);
+  field.data_.fixed64_ = value;
+  fields_.push_back(field);
+}
+
+std::string* UnknownFieldSet::AddLengthDelimited(int number) {
+  UnknownField field;
+  field.number_ = number;
+  field.SetType(UnknownField::TYPE_LENGTH_DELIMITED);
+  field.data_.length_delimited_.string_value = new std::string;
+  fields_.push_back(field);
+  return field.data_.length_delimited_.string_value;
+}
+
+
+UnknownFieldSet* UnknownFieldSet::AddGroup(int number) {
+  UnknownField field;
+  field.number_ = number;
+  field.SetType(UnknownField::TYPE_GROUP);
+  field.data_.group_ = new UnknownFieldSet;
+  fields_.push_back(field);
+  return field.data_.group_;
+}
+
+void UnknownFieldSet::AddField(const UnknownField& field) {
+  fields_.push_back(field);
+  fields_.back().DeepCopy(field);
+}
+
+void UnknownFieldSet::DeleteSubrange(int start, int num) {
+  // Delete the specified fields.
+  for (int i = 0; i < num; ++i) {
+    (fields_)[i + start].Delete();
+  }
+  // Slide down the remaining fields.
+  for (size_t i = start + num; i < fields_.size(); ++i) {
+    (fields_)[i - num] = (fields_)[i];
+  }
+  // Pop off the # of deleted fields.
+  for (int i = 0; i < num; ++i) {
+    fields_.pop_back();
+  }
+}
+
+void UnknownFieldSet::DeleteByNumber(int number) {
+  size_t left = 0;  // The number of fields left after deletion.
+  for (size_t i = 0; i < fields_.size(); ++i) {
+    UnknownField* field = &(fields_)[i];
+    if (field->number() == number) {
+      field->Delete();
+    } else {
+      if (i != left) {
+        (fields_)[left] = (fields_)[i];
+      }
+      ++left;
+    }
+  }
+  fields_.resize(left);
+}
+
+bool UnknownFieldSet::MergeFromCodedStream(io::CodedInputStream* input) {
+  UnknownFieldSet other;
+  if (internal::WireFormat::SkipMessage(input, &other) &&
+      input->ConsumedEntireMessage()) {
+    MergeFromAndDestroy(&other);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool UnknownFieldSet::ParseFromCodedStream(io::CodedInputStream* input) {
+  Clear();
+  return MergeFromCodedStream(input);
+}
+
+bool UnknownFieldSet::ParseFromZeroCopyStream(io::ZeroCopyInputStream* input) {
+  io::CodedInputStream coded_input(input);
+  return (ParseFromCodedStream(&coded_input) &&
+          coded_input.ConsumedEntireMessage());
+}
+
+bool UnknownFieldSet::ParseFromArray(const void* data, int size) {
+  io::ArrayInputStream input(data, size);
+  return ParseFromZeroCopyStream(&input);
+}
+
+bool UnknownFieldSet::SerializeToString(std::string* output) const {
+  const size_t size =
+      google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(*this);
+  STLStringResizeUninitializedAmortized(output, size);
+  google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
+      *this, reinterpret_cast<uint8_t*>(const_cast<char*>(output->data())));
+  return true;
+}
+
+bool UnknownFieldSet::SerializeToCodedStream(
+    io::CodedOutputStream* output) const {
+  google::protobuf::internal::WireFormat::SerializeUnknownFields(*this, output);
+  return !output->HadError();
+}
+void UnknownField::Delete() {
+  switch (type()) {
+    case UnknownField::TYPE_LENGTH_DELIMITED:
+      delete data_.length_delimited_.string_value;
+      break;
+    case UnknownField::TYPE_GROUP:
+      delete data_.group_;
+      break;
+    default:
+      break;
+  }
+}
+
+void UnknownField::DeepCopy(const UnknownField& other) {
+  (void)other;  // Parameter is used by Google-internal code.
+  switch (type()) {
+    case UnknownField::TYPE_LENGTH_DELIMITED:
+      data_.length_delimited_.string_value =
+          new std::string(*data_.length_delimited_.string_value);
+      break;
+    case UnknownField::TYPE_GROUP: {
+      UnknownFieldSet* group = new UnknownFieldSet();
+      group->InternalMergeFrom(*data_.group_);
+      data_.group_ = group;
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+
+uint8_t* UnknownField::InternalSerializeLengthDelimitedNoTag(
+    uint8_t* target, io::EpsCopyOutputStream* stream) const {
+  GOOGLE_DCHECK_EQ(TYPE_LENGTH_DELIMITED, type());
+  const std::string& data = *data_.length_delimited_.string_value;
+  target = io::CodedOutputStream::WriteVarint32ToArray(data.size(), target);
+  target = stream->WriteRaw(data.data(), data.size(), target);
+  return target;
+}
+
+namespace internal {
+
+class UnknownFieldParserHelper {
+ public:
+  explicit UnknownFieldParserHelper(UnknownFieldSet* unknown)
+      : unknown_(unknown) {}
+
+  void AddVarint(uint32_t num, uint64_t value) {
+    unknown_->AddVarint(num, value);
+  }
+  void AddFixed64(uint32_t num, uint64_t value) {
+    unknown_->AddFixed64(num, value);
+  }
+  const char* ParseLengthDelimited(uint32_t num, const char* ptr,
+                                   ParseContext* ctx) {
+    std::string* s = unknown_->AddLengthDelimited(num);
+    int size = ReadSize(&ptr);
+    GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+    return ctx->ReadString(ptr, size, s);
+  }
+  const char* ParseGroup(uint32_t num, const char* ptr, ParseContext* ctx) {
+    UnknownFieldParserHelper child(unknown_->AddGroup(num));
+    return ctx->ParseGroup(&child, ptr, num * 8 + 3);
+  }
+  void AddFixed32(uint32_t num, uint32_t value) {
+    unknown_->AddFixed32(num, value);
+  }
+
+  const char* _InternalParse(const char* ptr, ParseContext* ctx) {
+    return WireFormatParser(*this, ptr, ctx);
+  }
+
+ private:
+  UnknownFieldSet* unknown_;
+};
+
+const char* UnknownGroupParse(UnknownFieldSet* unknown, const char* ptr,
+                              ParseContext* ctx) {
+  UnknownFieldParserHelper field_parser(unknown);
+  return WireFormatParser(field_parser, ptr, ctx);
+}
+
+const char* UnknownFieldParse(uint64_t tag, UnknownFieldSet* unknown,
+                              const char* ptr, ParseContext* ctx) {
+  UnknownFieldParserHelper field_parser(unknown);
+  return FieldParser(tag, field_parser, ptr, ctx);
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/delimited_message_util.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/delimited_message_util.cpp
new file mode 100644
index 0000000..fdc633f
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/delimited_message_util.cpp
@@ -0,0 +1,128 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Adapted from the patch of kenton@google.com (Kenton Varda)
+// See https://github.com/protocolbuffers/protobuf/pull/710 for details.
+
+#include <google/protobuf/util/delimited_message_util.h>
+#include <google/protobuf/io/coded_stream.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+bool SerializeDelimitedToFileDescriptor(const MessageLite& message,
+                                        int file_descriptor) {
+  io::FileOutputStream output(file_descriptor);
+  return SerializeDelimitedToZeroCopyStream(message, &output);
+}
+
+bool SerializeDelimitedToOstream(const MessageLite& message,
+                                 std::ostream* output) {
+  {
+    io::OstreamOutputStream zero_copy_output(output);
+    if (!SerializeDelimitedToZeroCopyStream(message, &zero_copy_output))
+      return false;
+  }
+  return output->good();
+}
+
+bool ParseDelimitedFromZeroCopyStream(MessageLite* message,
+                                      io::ZeroCopyInputStream* input,
+                                      bool* clean_eof) {
+  io::CodedInputStream coded_input(input);
+  return ParseDelimitedFromCodedStream(message, &coded_input, clean_eof);
+}
+
+bool ParseDelimitedFromCodedStream(MessageLite* message,
+                                   io::CodedInputStream* input,
+                                   bool* clean_eof) {
+  if (clean_eof != nullptr) *clean_eof = false;
+  int start = input->CurrentPosition();
+
+  // Read the size.
+  uint32_t size;
+  if (!input->ReadVarint32(&size)) {
+    if (clean_eof != nullptr) *clean_eof = input->CurrentPosition() == start;
+    return false;
+  }
+
+  // Get the position after any size bytes have been read (and only the message
+  // itself remains).
+  int position_after_size = input->CurrentPosition();
+
+  // Tell the stream not to read beyond that size.
+  io::CodedInputStream::Limit limit = input->PushLimit(static_cast<int>(size));
+
+  // Parse the message.
+  if (!message->MergeFromCodedStream(input)) return false;
+  if (!input->ConsumedEntireMessage()) return false;
+  if (input->CurrentPosition() - position_after_size != static_cast<int>(size))
+    return false;
+
+  // Release the limit.
+  input->PopLimit(limit);
+
+  return true;
+}
+
+bool SerializeDelimitedToZeroCopyStream(const MessageLite& message,
+                                        io::ZeroCopyOutputStream* output) {
+  io::CodedOutputStream coded_output(output);
+  return SerializeDelimitedToCodedStream(message, &coded_output);
+}
+
+bool SerializeDelimitedToCodedStream(const MessageLite& message,
+                                     io::CodedOutputStream* output) {
+  // Write the size.
+  size_t size = message.ByteSizeLong();
+  if (size > INT_MAX) return false;
+
+  output->WriteVarint32(static_cast<uint32_t>(size));
+
+  // Write the content.
+  uint8_t* buffer =
+      output->GetDirectBufferForNBytesAndAdvance(static_cast<int>(size));
+  if (buffer != nullptr) {
+    // Optimization: The message fits in one buffer, so use the faster
+    // direct-to-array serialization path.
+    message.SerializeWithCachedSizesToArray(buffer);
+  } else {
+    // Slightly-slower path when the message is multiple buffers.
+    message.SerializeWithCachedSizes(output);
+    if (output->HadError()) return false;
+  }
+
+  return true;
+}
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/field_comparator.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/field_comparator.cpp
new file mode 100644
index 0000000..5d8e865
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/field_comparator.cpp
@@ -0,0 +1,210 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: ksroka@google.com (Krzysztof Sroka)
+
+#include <google/protobuf/util/field_comparator.h>
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/util/message_differencer.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/mathutil.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+FieldComparator::FieldComparator() {}
+FieldComparator::~FieldComparator() {}
+
+SimpleFieldComparator::SimpleFieldComparator()
+    : float_comparison_(EXACT),
+      treat_nan_as_equal_(false),
+      has_default_tolerance_(false) {}
+
+SimpleFieldComparator::~SimpleFieldComparator() {}
+
+FieldComparator::ComparisonResult SimpleFieldComparator::SimpleCompare(
+    const Message& message_1, const Message& message_2,
+    const FieldDescriptor* field, int index_1, int index_2,
+    const util::FieldContext* /*field_context*/) {
+  const Reflection* reflection_1 = message_1.GetReflection();
+  const Reflection* reflection_2 = message_2.GetReflection();
+
+  switch (field->cpp_type()) {
+#define COMPARE_FIELD(METHOD)                                                 \
+  if (field->is_repeated()) {                                                 \
+    return ResultFromBoolean(Compare##METHOD(                                 \
+        *field, reflection_1->GetRepeated##METHOD(message_1, field, index_1), \
+        reflection_2->GetRepeated##METHOD(message_2, field, index_2)));       \
+  } else {                                                                    \
+    return ResultFromBoolean(                                                 \
+        Compare##METHOD(*field, reflection_1->Get##METHOD(message_1, field),  \
+                        reflection_2->Get##METHOD(message_2, field)));        \
+  }                                                                           \
+  break;  // Make sure no fall-through is introduced.
+
+    case FieldDescriptor::CPPTYPE_BOOL:
+      COMPARE_FIELD(Bool);
+    case FieldDescriptor::CPPTYPE_DOUBLE:
+      COMPARE_FIELD(Double);
+    case FieldDescriptor::CPPTYPE_ENUM:
+      COMPARE_FIELD(Enum);
+    case FieldDescriptor::CPPTYPE_FLOAT:
+      COMPARE_FIELD(Float);
+    case FieldDescriptor::CPPTYPE_INT32:
+      COMPARE_FIELD(Int32);
+    case FieldDescriptor::CPPTYPE_INT64:
+      COMPARE_FIELD(Int64);
+    case FieldDescriptor::CPPTYPE_STRING:
+      if (field->is_repeated()) {
+        // Allocate scratch strings to store the result if a conversion is
+        // needed.
+        std::string scratch1;
+        std::string scratch2;
+        return ResultFromBoolean(
+            CompareString(*field,
+                          reflection_1->GetRepeatedStringReference(
+                              message_1, field, index_1, &scratch1),
+                          reflection_2->GetRepeatedStringReference(
+                              message_2, field, index_2, &scratch2)));
+      } else {
+        // Allocate scratch strings to store the result if a conversion is
+        // needed.
+        std::string scratch1;
+        std::string scratch2;
+        return ResultFromBoolean(CompareString(
+            *field,
+            reflection_1->GetStringReference(message_1, field, &scratch1),
+            reflection_2->GetStringReference(message_2, field, &scratch2)));
+      }
+      break;
+    case FieldDescriptor::CPPTYPE_UINT32:
+      COMPARE_FIELD(UInt32);
+    case FieldDescriptor::CPPTYPE_UINT64:
+      COMPARE_FIELD(UInt64);
+
+#undef COMPARE_FIELD
+
+    case FieldDescriptor::CPPTYPE_MESSAGE:
+      return RECURSE;
+
+    default:
+      GOOGLE_LOG(FATAL) << "No comparison code for field " << field->full_name()
+                 << " of CppType = " << field->cpp_type();
+      return DIFFERENT;
+  }
+}
+
+bool SimpleFieldComparator::CompareWithDifferencer(
+    MessageDifferencer* differencer, const Message& message1,
+    const Message& message2, const util::FieldContext* field_context) {
+  return differencer->Compare(message1, message2,
+                              field_context->parent_fields());
+}
+
+void SimpleFieldComparator::SetDefaultFractionAndMargin(double fraction,
+                                                        double margin) {
+  default_tolerance_ = Tolerance(fraction, margin);
+  has_default_tolerance_ = true;
+}
+
+void SimpleFieldComparator::SetFractionAndMargin(const FieldDescriptor* field,
+                                                 double fraction,
+                                                 double margin) {
+  GOOGLE_CHECK(FieldDescriptor::CPPTYPE_FLOAT == field->cpp_type() ||
+        FieldDescriptor::CPPTYPE_DOUBLE == field->cpp_type())
+      << "Field has to be float or double type. Field name is: "
+      << field->full_name();
+  map_tolerance_[field] = Tolerance(fraction, margin);
+}
+
+bool SimpleFieldComparator::CompareDouble(const FieldDescriptor& field,
+                                          double value_1, double value_2) {
+  return CompareDoubleOrFloat(field, value_1, value_2);
+}
+
+bool SimpleFieldComparator::CompareEnum(const FieldDescriptor& /*field*/,
+                                        const EnumValueDescriptor* value_1,
+                                        const EnumValueDescriptor* value_2) {
+  return value_1->number() == value_2->number();
+}
+
+bool SimpleFieldComparator::CompareFloat(const FieldDescriptor& field,
+                                         float value_1, float value_2) {
+  return CompareDoubleOrFloat(field, value_1, value_2);
+}
+
+template <typename T>
+bool SimpleFieldComparator::CompareDoubleOrFloat(const FieldDescriptor& field,
+                                                 T value_1, T value_2) {
+  if (value_1 == value_2) {
+    // Covers +inf and -inf (which are not within margin or fraction of
+    // themselves), and is a shortcut for finite values.
+    return true;
+  } else if (float_comparison_ == EXACT) {
+    if (treat_nan_as_equal_ && std::isnan(value_1) && std::isnan(value_2)) {
+      return true;
+    }
+    return false;
+  } else {
+    if (treat_nan_as_equal_ && std::isnan(value_1) && std::isnan(value_2)) {
+      return true;
+    }
+    // float_comparison_ == APPROXIMATE covers two use cases.
+    Tolerance* tolerance = FindOrNull(map_tolerance_, &field);
+    if (tolerance == NULL && has_default_tolerance_) {
+      tolerance = &default_tolerance_;
+    }
+    if (tolerance == NULL) {
+      return MathUtil::AlmostEquals(value_1, value_2);
+    } else {
+      // Use user-provided fraction and margin. Since they are stored as
+      // doubles, we explicitly cast them to types of values provided. This
+      // is very likely to fail if provided values are not numeric.
+      return MathUtil::WithinFractionOrMargin(
+          value_1, value_2, static_cast<T>(tolerance->fraction),
+          static_cast<T>(tolerance->margin));
+    }
+  }
+}
+
+FieldComparator::ComparisonResult SimpleFieldComparator::ResultFromBoolean(
+    bool boolean_result) const {
+  return boolean_result ? FieldComparator::SAME : FieldComparator::DIFFERENT;
+}
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/field_mask_util.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/field_mask_util.cpp
new file mode 100644
index 0000000..9a40b85
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/field_mask_util.cpp
@@ -0,0 +1,720 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/field_mask_util.h>
+
+#include <cstdint>
+
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/stubs/map_util.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+using google::protobuf::FieldMask;
+
+std::string FieldMaskUtil::ToString(const FieldMask& mask) {
+  return Join(mask.paths(), ",");
+}
+
+void FieldMaskUtil::FromString(StringPiece str, FieldMask* out) {
+  out->Clear();
+  std::vector<std::string> paths = Split(str, ",");
+  for (const std::string& path : paths) {
+    if (path.empty()) continue;
+    out->add_paths(path);
+  }
+}
+
+bool FieldMaskUtil::SnakeCaseToCamelCase(StringPiece input,
+                                         std::string* output) {
+  output->clear();
+  bool after_underscore = false;
+  for (char input_char : input) {
+    if (input_char >= 'A' && input_char <= 'Z') {
+      // The field name must not contain uppercase letters.
+      return false;
+    }
+    if (after_underscore) {
+      if (input_char >= 'a' && input_char <= 'z') {
+        output->push_back(input_char + 'A' - 'a');
+        after_underscore = false;
+      } else {
+        // The character after a "_" must be a lowercase letter.
+        return false;
+      }
+    } else if (input_char == '_') {
+      after_underscore = true;
+    } else {
+      output->push_back(input_char);
+    }
+  }
+  if (after_underscore) {
+    // Trailing "_".
+    return false;
+  }
+  return true;
+}
+
+bool FieldMaskUtil::CamelCaseToSnakeCase(StringPiece input,
+                                         std::string* output) {
+  output->clear();
+  for (const char c : input) {
+    if (c == '_') {
+      // The field name must not contain "_"s.
+      return false;
+    }
+    if (c >= 'A' && c <= 'Z') {
+      output->push_back('_');
+      output->push_back(c + 'a' - 'A');
+    } else {
+      output->push_back(c);
+    }
+  }
+  return true;
+}
+
+bool FieldMaskUtil::ToJsonString(const FieldMask& mask, std::string* out) {
+  out->clear();
+  for (int i = 0; i < mask.paths_size(); ++i) {
+    const std::string& path = mask.paths(i);
+    std::string camelcase_path;
+    if (!SnakeCaseToCamelCase(path, &camelcase_path)) {
+      return false;
+    }
+    if (i > 0) {
+      out->push_back(',');
+    }
+    out->append(camelcase_path);
+  }
+  return true;
+}
+
+bool FieldMaskUtil::FromJsonString(StringPiece str, FieldMask* out) {
+  out->Clear();
+  std::vector<std::string> paths = Split(str, ",");
+  for (const std::string& path : paths) {
+    if (path.empty()) continue;
+    std::string snakecase_path;
+    if (!CamelCaseToSnakeCase(path, &snakecase_path)) {
+      return false;
+    }
+    out->add_paths(snakecase_path);
+  }
+  return true;
+}
+
+bool FieldMaskUtil::GetFieldDescriptors(
+    const Descriptor* descriptor, StringPiece path,
+    std::vector<const FieldDescriptor*>* field_descriptors) {
+  if (field_descriptors != nullptr) {
+    field_descriptors->clear();
+  }
+  std::vector<std::string> parts = Split(path, ".");
+  for (const std::string& field_name : parts) {
+    if (descriptor == nullptr) {
+      return false;
+    }
+    const FieldDescriptor* field = descriptor->FindFieldByName(field_name);
+    if (field == nullptr) {
+      return false;
+    }
+    if (field_descriptors != nullptr) {
+      field_descriptors->push_back(field);
+    }
+    if (!field->is_repeated() &&
+        field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      descriptor = field->message_type();
+    } else {
+      descriptor = nullptr;
+    }
+  }
+  return true;
+}
+
+void FieldMaskUtil::GetFieldMaskForAllFields(const Descriptor* descriptor,
+                                             FieldMask* out) {
+  for (int i = 0; i < descriptor->field_count(); ++i) {
+    out->add_paths(descriptor->field(i)->name());
+  }
+}
+
+namespace {
+// A FieldMaskTree represents a FieldMask in a tree structure. For example,
+// given a FieldMask "foo.bar,foo.baz,bar.baz", the FieldMaskTree will be:
+//
+//   [root] -+- foo -+- bar
+//           |       |
+//           |       +- baz
+//           |
+//           +- bar --- baz
+//
+// In the tree, each leaf node represents a field path.
+class FieldMaskTree {
+ public:
+  FieldMaskTree();
+  ~FieldMaskTree();
+
+  void MergeFromFieldMask(const FieldMask& mask);
+  void MergeToFieldMask(FieldMask* mask);
+
+  // Add a field path into the tree. In a FieldMask, each field path matches
+  // the specified field and also all its sub-fields. If the field path to
+  // add is a sub-path of an existing field path in the tree (i.e., a leaf
+  // node), it means the tree already matches the given path so nothing will
+  // be added to the tree. If the path matches an existing non-leaf node in the
+  // tree, that non-leaf node will be turned into a leaf node with all its
+  // children removed because the path matches all the node's children.
+  void AddPath(const std::string& path);
+
+  // Remove a path from the tree.
+  // If the path is a sub-path of an existing field path in the tree, it means
+  // we need remove the existing field path and add all sub-paths except
+  // specified path. If the path matches an existing node in the tree, this node
+  // will be moved.
+  void RemovePath(const std::string& path, const Descriptor* descriptor);
+
+  // Calculate the intersection part of a field path with this tree and add
+  // the intersection field path into out.
+  void IntersectPath(const std::string& path, FieldMaskTree* out);
+
+  // Merge all fields specified by this tree from one message to another.
+  void MergeMessage(const Message& source,
+                    const FieldMaskUtil::MergeOptions& options,
+                    Message* destination) {
+    // Do nothing if the tree is empty.
+    if (root_.children.empty()) {
+      return;
+    }
+    MergeMessage(&root_, source, options, destination);
+  }
+
+  // Add required field path of the message to this tree based on current tree
+  // structure. If a message is present in the tree, add the path of its
+  // required field to the tree. This is to make sure that after trimming a
+  // message with required fields are set, check IsInitialized() will not fail.
+  void AddRequiredFieldPath(const Descriptor* descriptor) {
+    // Do nothing if the tree is empty.
+    if (root_.children.empty()) {
+      return;
+    }
+    AddRequiredFieldPath(&root_, descriptor);
+  }
+
+  // Trims all fields not specified by this tree from the given message.
+  // Returns true if the message is modified.
+  bool TrimMessage(Message* message) {
+    // Do nothing if the tree is empty.
+    if (root_.children.empty()) {
+      return false;
+    }
+    return TrimMessage(&root_, message);
+  }
+
+ private:
+  struct Node {
+    Node() {}
+
+    ~Node() { ClearChildren(); }
+
+    void ClearChildren() {
+      for (std::map<std::string, Node*>::iterator it = children.begin();
+           it != children.end(); ++it) {
+        delete it->second;
+      }
+      children.clear();
+    }
+
+    std::map<std::string, Node*> children;
+
+   private:
+    GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Node);
+  };
+
+  // Merge a sub-tree to mask. This method adds the field paths represented
+  // by all leaf nodes descended from "node" to mask.
+  void MergeToFieldMask(const std::string& prefix, const Node* node,
+                        FieldMask* out);
+
+  // Merge all leaf nodes of a sub-tree to another tree.
+  void MergeLeafNodesToTree(const std::string& prefix, const Node* node,
+                            FieldMaskTree* out);
+
+  // Merge all fields specified by a sub-tree from one message to another.
+  void MergeMessage(const Node* node, const Message& source,
+                    const FieldMaskUtil::MergeOptions& options,
+                    Message* destination);
+
+  // Add required field path of the message to this tree based on current tree
+  // structure. If a message is present in the tree, add the path of its
+  // required field to the tree. This is to make sure that after trimming a
+  // message with required fields are set, check IsInitialized() will not fail.
+  void AddRequiredFieldPath(Node* node, const Descriptor* descriptor);
+
+  // Trims all fields not specified by this sub-tree from the given message.
+  // Returns true if the message is actually modified
+  bool TrimMessage(const Node* node, Message* message);
+
+  Node root_;
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldMaskTree);
+};
+
+FieldMaskTree::FieldMaskTree() {}
+
+FieldMaskTree::~FieldMaskTree() {}
+
+void FieldMaskTree::MergeFromFieldMask(const FieldMask& mask) {
+  for (int i = 0; i < mask.paths_size(); ++i) {
+    AddPath(mask.paths(i));
+  }
+}
+
+void FieldMaskTree::MergeToFieldMask(FieldMask* mask) {
+  MergeToFieldMask("", &root_, mask);
+}
+
+void FieldMaskTree::MergeToFieldMask(const std::string& prefix,
+                                     const Node* node, FieldMask* out) {
+  if (node->children.empty()) {
+    if (prefix.empty()) {
+      // This is the root node.
+      return;
+    }
+    out->add_paths(prefix);
+    return;
+  }
+  for (std::map<std::string, Node*>::const_iterator it = node->children.begin();
+       it != node->children.end(); ++it) {
+    std::string current_path =
+        prefix.empty() ? it->first : prefix + "." + it->first;
+    MergeToFieldMask(current_path, it->second, out);
+  }
+}
+
+void FieldMaskTree::AddPath(const std::string& path) {
+  std::vector<std::string> parts = Split(path, ".");
+  if (parts.empty()) {
+    return;
+  }
+  bool new_branch = false;
+  Node* node = &root_;
+  for (const std::string& node_name : parts) {
+    if (!new_branch && node != &root_ && node->children.empty()) {
+      // Path matches an existing leaf node. This means the path is already
+      // covered by this tree (for example, adding "foo.bar.baz" to a tree
+      // which already contains "foo.bar").
+      return;
+    }
+    Node*& child = node->children[node_name];
+    if (child == nullptr) {
+      new_branch = true;
+      child = new Node();
+    }
+    node = child;
+  }
+  if (!node->children.empty()) {
+    node->ClearChildren();
+  }
+}
+
+void FieldMaskTree::RemovePath(const std::string& path,
+                               const Descriptor* descriptor) {
+  if (root_.children.empty()) {
+    // Nothing to be removed from an empty tree. We shortcut it here so an empty
+    // tree won't be interpreted as a field mask containing all fields by the
+    // code below.
+    return;
+  }
+  std::vector<std::string> parts = Split(path, ".");
+  if (parts.empty()) {
+    return;
+  }
+  std::vector<Node*> nodes(parts.size());
+  Node* node = &root_;
+  const Descriptor* current_descriptor = descriptor;
+  Node* new_branch_node = nullptr;
+  for (size_t i = 0; i < parts.size(); ++i) {
+    nodes[i] = node;
+    const FieldDescriptor* field_descriptor =
+        current_descriptor->FindFieldByName(parts[i]);
+    if (field_descriptor == nullptr ||
+        (field_descriptor->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE &&
+         i != parts.size() - 1)) {
+      // Invalid path.
+      if (new_branch_node != nullptr) {
+        // If add any new nodes, cleanup.
+        new_branch_node->ClearChildren();
+      }
+      return;
+    }
+
+    if (node->children.empty()) {
+      if (new_branch_node == nullptr) {
+        new_branch_node = node;
+      }
+      for (int j = 0; j < current_descriptor->field_count(); ++j) {
+        node->children[current_descriptor->field(j)->name()] = new Node();
+      }
+    }
+    if (ContainsKey(node->children, parts[i])) {
+      node = node->children[parts[i]];
+      if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+        current_descriptor = field_descriptor->message_type();
+      }
+    } else {
+      // Path does not exist.
+      return;
+    }
+  }
+  // Remove path.
+  for (int i = parts.size() - 1; i >= 0; i--) {
+    delete nodes[i]->children[parts[i]];
+    nodes[i]->children.erase(parts[i]);
+    if (!nodes[i]->children.empty()) {
+      break;
+    }
+  }
+}
+
+void FieldMaskTree::IntersectPath(const std::string& path, FieldMaskTree* out) {
+  std::vector<std::string> parts = Split(path, ".");
+  if (parts.empty()) {
+    return;
+  }
+  const Node* node = &root_;
+  for (const std::string& node_name : parts) {
+    if (node->children.empty()) {
+      if (node != &root_) {
+        out->AddPath(path);
+      }
+      return;
+    }
+    const Node* result = FindPtrOrNull(node->children, node_name);
+    if (result == nullptr) {
+      // No intersection found.
+      return;
+    }
+    node = result;
+  }
+  // Now we found a matching node with the given path. Add all leaf nodes
+  // to out.
+  MergeLeafNodesToTree(path, node, out);
+}
+
+void FieldMaskTree::MergeLeafNodesToTree(const std::string& prefix,
+                                         const Node* node, FieldMaskTree* out) {
+  if (node->children.empty()) {
+    out->AddPath(prefix);
+  }
+  for (std::map<std::string, Node*>::const_iterator it = node->children.begin();
+       it != node->children.end(); ++it) {
+    std::string current_path =
+        prefix.empty() ? it->first : prefix + "." + it->first;
+    MergeLeafNodesToTree(current_path, it->second, out);
+  }
+}
+
+void FieldMaskTree::MergeMessage(const Node* node, const Message& source,
+                                 const FieldMaskUtil::MergeOptions& options,
+                                 Message* destination) {
+  GOOGLE_DCHECK(!node->children.empty());
+  const Reflection* source_reflection = source.GetReflection();
+  const Reflection* destination_reflection = destination->GetReflection();
+  const Descriptor* descriptor = source.GetDescriptor();
+  for (std::map<std::string, Node*>::const_iterator it = node->children.begin();
+       it != node->children.end(); ++it) {
+    const std::string& field_name = it->first;
+    const Node* child = it->second;
+    const FieldDescriptor* field = descriptor->FindFieldByName(field_name);
+    if (field == nullptr) {
+      GOOGLE_LOG(ERROR) << "Cannot find field \"" << field_name << "\" in message "
+                 << descriptor->full_name();
+      continue;
+    }
+    if (!child->children.empty()) {
+      // Sub-paths are only allowed for singular message fields.
+      if (field->is_repeated() ||
+          field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+        GOOGLE_LOG(ERROR) << "Field \"" << field_name << "\" in message "
+                   << descriptor->full_name()
+                   << " is not a singular message field and cannot "
+                   << "have sub-fields.";
+        continue;
+      }
+      MergeMessage(child, source_reflection->GetMessage(source, field), options,
+                   destination_reflection->MutableMessage(destination, field));
+      continue;
+    }
+    if (!field->is_repeated()) {
+      switch (field->cpp_type()) {
+#define COPY_VALUE(TYPE, Name)                                              \
+  case FieldDescriptor::CPPTYPE_##TYPE: {                                   \
+    if (source_reflection->HasField(source, field)) {                       \
+      destination_reflection->Set##Name(                                    \
+          destination, field, source_reflection->Get##Name(source, field)); \
+    } else {                                                                \
+      destination_reflection->ClearField(destination, field);               \
+    }                                                                       \
+    break;                                                                  \
+  }
+        COPY_VALUE(BOOL, Bool)
+        COPY_VALUE(INT32, Int32)
+        COPY_VALUE(INT64, Int64)
+        COPY_VALUE(UINT32, UInt32)
+        COPY_VALUE(UINT64, UInt64)
+        COPY_VALUE(FLOAT, Float)
+        COPY_VALUE(DOUBLE, Double)
+        COPY_VALUE(ENUM, Enum)
+        COPY_VALUE(STRING, String)
+#undef COPY_VALUE
+        case FieldDescriptor::CPPTYPE_MESSAGE: {
+          if (options.replace_message_fields()) {
+            destination_reflection->ClearField(destination, field);
+          }
+          if (source_reflection->HasField(source, field)) {
+            destination_reflection->MutableMessage(destination, field)
+                ->MergeFrom(source_reflection->GetMessage(source, field));
+          }
+          break;
+        }
+      }
+    } else {
+      if (options.replace_repeated_fields()) {
+        destination_reflection->ClearField(destination, field);
+      }
+      switch (field->cpp_type()) {
+#define COPY_REPEATED_VALUE(TYPE, Name)                            \
+  case FieldDescriptor::CPPTYPE_##TYPE: {                          \
+    int size = source_reflection->FieldSize(source, field);        \
+    for (int i = 0; i < size; ++i) {                               \
+      destination_reflection->Add##Name(                           \
+          destination, field,                                      \
+          source_reflection->GetRepeated##Name(source, field, i)); \
+    }                                                              \
+    break;                                                         \
+  }
+        COPY_REPEATED_VALUE(BOOL, Bool)
+        COPY_REPEATED_VALUE(INT32, Int32)
+        COPY_REPEATED_VALUE(INT64, Int64)
+        COPY_REPEATED_VALUE(UINT32, UInt32)
+        COPY_REPEATED_VALUE(UINT64, UInt64)
+        COPY_REPEATED_VALUE(FLOAT, Float)
+        COPY_REPEATED_VALUE(DOUBLE, Double)
+        COPY_REPEATED_VALUE(ENUM, Enum)
+        COPY_REPEATED_VALUE(STRING, String)
+#undef COPY_REPEATED_VALUE
+        case FieldDescriptor::CPPTYPE_MESSAGE: {
+          int size = source_reflection->FieldSize(source, field);
+          for (int i = 0; i < size; ++i) {
+            destination_reflection->AddMessage(destination, field)
+                ->MergeFrom(
+                    source_reflection->GetRepeatedMessage(source, field, i));
+          }
+          break;
+        }
+      }
+    }
+  }
+}
+
+void FieldMaskTree::AddRequiredFieldPath(Node* node,
+                                         const Descriptor* descriptor) {
+  const int32_t field_count = descriptor->field_count();
+  for (int index = 0; index < field_count; ++index) {
+    const FieldDescriptor* field = descriptor->field(index);
+    if (field->is_required()) {
+      const std::string& node_name = field->name();
+      Node*& child = node->children[node_name];
+      if (child == nullptr) {
+        // Add required field path to the tree
+        child = new Node();
+      } else if (child->children.empty()) {
+        // If the required field is in the tree and does not have any children,
+        // do nothing.
+        continue;
+      }
+      // Add required field in the children to the tree if the field is message.
+      if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+        AddRequiredFieldPath(child, field->message_type());
+      }
+    } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      std::map<std::string, Node*>::const_iterator it =
+          node->children.find(field->name());
+      if (it != node->children.end()) {
+        // Add required fields in the children to the
+        // tree if the field is a message and present in the tree.
+        Node* child = it->second;
+        if (!child->children.empty()) {
+          AddRequiredFieldPath(child, field->message_type());
+        }
+      }
+    }
+  }
+}
+
+bool FieldMaskTree::TrimMessage(const Node* node, Message* message) {
+  GOOGLE_DCHECK(!node->children.empty());
+  const Reflection* reflection = message->GetReflection();
+  const Descriptor* descriptor = message->GetDescriptor();
+  const int32_t field_count = descriptor->field_count();
+  bool modified = false;
+  for (int index = 0; index < field_count; ++index) {
+    const FieldDescriptor* field = descriptor->field(index);
+    std::map<std::string, Node*>::const_iterator it =
+        node->children.find(field->name());
+    if (it == node->children.end()) {
+      if (field->is_repeated()) {
+        if (reflection->FieldSize(*message, field) != 0) {
+          modified = true;
+        }
+      } else {
+        if (reflection->HasField(*message, field)) {
+          modified = true;
+        }
+      }
+      reflection->ClearField(message, field);
+    } else {
+      if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+        Node* child = it->second;
+        if (!child->children.empty() && reflection->HasField(*message, field)) {
+          bool nestedMessageChanged =
+              TrimMessage(child, reflection->MutableMessage(message, field));
+          modified = nestedMessageChanged || modified;
+        }
+      }
+    }
+  }
+  return modified;
+}
+
+}  // namespace
+
+void FieldMaskUtil::ToCanonicalForm(const FieldMask& mask, FieldMask* out) {
+  FieldMaskTree tree;
+  tree.MergeFromFieldMask(mask);
+  out->Clear();
+  tree.MergeToFieldMask(out);
+}
+
+void FieldMaskUtil::Union(const FieldMask& mask1, const FieldMask& mask2,
+                          FieldMask* out) {
+  FieldMaskTree tree;
+  tree.MergeFromFieldMask(mask1);
+  tree.MergeFromFieldMask(mask2);
+  out->Clear();
+  tree.MergeToFieldMask(out);
+}
+
+void FieldMaskUtil::Intersect(const FieldMask& mask1, const FieldMask& mask2,
+                              FieldMask* out) {
+  FieldMaskTree tree, intersection;
+  tree.MergeFromFieldMask(mask1);
+  for (int i = 0; i < mask2.paths_size(); ++i) {
+    tree.IntersectPath(mask2.paths(i), &intersection);
+  }
+  out->Clear();
+  intersection.MergeToFieldMask(out);
+}
+
+void FieldMaskUtil::Subtract(const Descriptor* descriptor,
+                             const FieldMask& mask1, const FieldMask& mask2,
+                             FieldMask* out) {
+  if (mask1.paths().empty()) {
+    out->Clear();
+    return;
+  }
+  FieldMaskTree tree;
+  tree.MergeFromFieldMask(mask1);
+  for (int i = 0; i < mask2.paths_size(); ++i) {
+    tree.RemovePath(mask2.paths(i), descriptor);
+  }
+  out->Clear();
+  tree.MergeToFieldMask(out);
+}
+
+bool FieldMaskUtil::IsPathInFieldMask(StringPiece path,
+                                      const FieldMask& mask) {
+  for (int i = 0; i < mask.paths_size(); ++i) {
+    const std::string& mask_path = mask.paths(i);
+    if (path == mask_path) {
+      return true;
+    } else if (mask_path.length() < path.length()) {
+      // Also check whether mask.paths(i) is a prefix of path.
+      if (path.substr(0, mask_path.length() + 1).compare(mask_path + ".") ==
+          0) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+void FieldMaskUtil::MergeMessageTo(const Message& source, const FieldMask& mask,
+                                   const MergeOptions& options,
+                                   Message* destination) {
+  GOOGLE_CHECK(source.GetDescriptor() == destination->GetDescriptor());
+  // Build a FieldMaskTree and walk through the tree to merge all specified
+  // fields.
+  FieldMaskTree tree;
+  tree.MergeFromFieldMask(mask);
+  tree.MergeMessage(source, options, destination);
+}
+
+bool FieldMaskUtil::TrimMessage(const FieldMask& mask, Message* message) {
+  // Build a FieldMaskTree and walk through the tree to merge all specified
+  // fields.
+  FieldMaskTree tree;
+  tree.MergeFromFieldMask(mask);
+  return tree.TrimMessage(GOOGLE_CHECK_NOTNULL(message));
+}
+
+bool FieldMaskUtil::TrimMessage(const FieldMask& mask, Message* message,
+                                const TrimOptions& options) {
+  // Build a FieldMaskTree and walk through the tree to merge all specified
+  // fields.
+  FieldMaskTree tree;
+  tree.MergeFromFieldMask(mask);
+  // If keep_required_fields is true, implicitly add required fields of
+  // a message present in the tree to prevent from trimming.
+  if (options.keep_required_fields()) {
+    tree.AddRequiredFieldPath(GOOGLE_CHECK_NOTNULL(message->GetDescriptor()));
+  }
+  return tree.TrimMessage(GOOGLE_CHECK_NOTNULL(message));
+}
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/datapiece.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/datapiece.cpp
new file mode 100644
index 0000000..56f4a18
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/datapiece.cpp
@@ -0,0 +1,448 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/datapiece.h>
+
+#include <cmath>
+#include <cstdint>
+#include <limits>
+
+#include <google/protobuf/struct.pb.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/mathutil.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using util::Status;
+
+namespace {
+
+template <typename To, typename From>
+util::StatusOr<To> ValidateNumberConversion(To after, From before) {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#endif
+  if (after == before &&
+      MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) {
+    return after;
+  } else {
+    return util::InvalidArgumentError(
+        std::is_integral<From>::value       ? ValueAsString(before)
+        : std::is_same<From, double>::value ? DoubleAsString(before)
+                                            : FloatAsString(before));
+  }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+}
+
+// For general conversion between
+//     int32, int64, uint32, uint64, double and float
+// except conversion between double and float.
+template <typename To, typename From>
+util::StatusOr<To> NumberConvertAndCheck(From before) {
+  if (std::is_same<From, To>::value) return before;
+
+  To after = static_cast<To>(before);
+  return ValidateNumberConversion(after, before);
+}
+
+// For conversion to integer types (int32, int64, uint32, uint64) from floating
+// point types (double, float) only.
+template <typename To, typename From>
+util::StatusOr<To> FloatingPointToIntConvertAndCheck(From before) {
+  if (std::is_same<From, To>::value) return before;
+
+  To after = static_cast<To>(before);
+  return ValidateNumberConversion(after, before);
+}
+
+// For conversion between double and float only.
+util::StatusOr<double> FloatToDouble(float before) {
+  // Casting float to double should just work as double has more precision
+  // than float.
+  return static_cast<double>(before);
+}
+
+util::StatusOr<float> DoubleToFloat(double before) {
+  if (std::isnan(before)) {
+    return std::numeric_limits<float>::quiet_NaN();
+  } else if (!std::isfinite(before)) {
+    // Converting a double +inf/-inf to float should just work.
+    return static_cast<float>(before);
+  } else if (before > std::numeric_limits<float>::max() ||
+             before < -std::numeric_limits<float>::max()) {
+    // Some doubles are larger than the largest float, but after
+    // rounding they will be equal to the largest float.
+    // We can't just attempt the conversion because that has UB if
+    // the value really is out-of-range.
+    // Here we take advantage that 1/2-ing a large floating point
+    // will not lose precision.
+    double half_before = before * 0.5;
+    if (half_before < std::numeric_limits<float>::max() &&
+        half_before > -std::numeric_limits<float>::max()) {
+      const float half_fmax = std::numeric_limits<float>::max() * 0.5f;
+      // If after being cut in half, the value is less than the largest float,
+      // then it's safe to convert it to float.  Importantly, this conversion
+      // rounds in the same way that the original does.
+      float half_after = static_cast<float>(half_before);
+      if (half_after <= half_fmax && half_after >= -half_fmax) {
+        return half_after + half_after;
+      }
+    }
+    // Double value outside of the range of float.
+    return util::InvalidArgumentError(DoubleAsString(before));
+  } else {
+    return static_cast<float>(before);
+  }
+}
+
+}  // namespace
+
+util::StatusOr<int32_t> DataPiece::ToInt32() const {
+  if (type_ == TYPE_STRING)
+    return StringToNumber<int32_t>(safe_strto32);
+
+  if (type_ == TYPE_DOUBLE)
+    return FloatingPointToIntConvertAndCheck<int32_t, double>(double_);
+
+  if (type_ == TYPE_FLOAT)
+    return FloatingPointToIntConvertAndCheck<int32_t, float>(float_);
+
+  return GenericConvert<int32_t>();
+}
+
+util::StatusOr<uint32_t> DataPiece::ToUint32() const {
+  if (type_ == TYPE_STRING)
+    return StringToNumber<uint32_t>(safe_strtou32);
+
+  if (type_ == TYPE_DOUBLE)
+    return FloatingPointToIntConvertAndCheck<uint32_t, double>(double_);
+
+  if (type_ == TYPE_FLOAT)
+    return FloatingPointToIntConvertAndCheck<uint32_t, float>(float_);
+
+  return GenericConvert<uint32_t>();
+}
+
+util::StatusOr<int64_t> DataPiece::ToInt64() const {
+  if (type_ == TYPE_STRING)
+    return StringToNumber<int64_t>(safe_strto64);
+
+  if (type_ == TYPE_DOUBLE)
+    return FloatingPointToIntConvertAndCheck<int64_t, double>(double_);
+
+  if (type_ == TYPE_FLOAT)
+    return FloatingPointToIntConvertAndCheck<int64_t, float>(float_);
+
+  return GenericConvert<int64_t>();
+}
+
+util::StatusOr<uint64_t> DataPiece::ToUint64() const {
+  if (type_ == TYPE_STRING)
+    return StringToNumber<uint64_t>(safe_strtou64);
+
+  if (type_ == TYPE_DOUBLE)
+    return FloatingPointToIntConvertAndCheck<uint64_t, double>(double_);
+
+  if (type_ == TYPE_FLOAT)
+    return FloatingPointToIntConvertAndCheck<uint64_t, float>(float_);
+
+  return GenericConvert<uint64_t>();
+}
+
+util::StatusOr<double> DataPiece::ToDouble() const {
+  if (type_ == TYPE_FLOAT) {
+    return FloatToDouble(float_);
+  }
+  if (type_ == TYPE_STRING) {
+    if (str_ == "Infinity") return std::numeric_limits<double>::infinity();
+    if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity();
+    if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN();
+    util::StatusOr<double> value = StringToNumber<double>(safe_strtod);
+    if (value.ok() && !std::isfinite(value.value())) {
+      // safe_strtod converts out-of-range values to +inf/-inf, but we want
+      // to report them as errors.
+      return util::InvalidArgumentError(StrCat("\"", str_, "\""));
+    } else {
+      return value;
+    }
+  }
+  return GenericConvert<double>();
+}
+
+util::StatusOr<float> DataPiece::ToFloat() const {
+  if (type_ == TYPE_DOUBLE) {
+    return DoubleToFloat(double_);
+  }
+  if (type_ == TYPE_STRING) {
+    if (str_ == "Infinity") return std::numeric_limits<float>::infinity();
+    if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity();
+    if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN();
+    // SafeStrToFloat() is used instead of safe_strtof() because the later
+    // does not fail on inputs like SimpleDtoa(DBL_MAX).
+    return StringToNumber<float>(SafeStrToFloat);
+  }
+  return GenericConvert<float>();
+}
+
+util::StatusOr<bool> DataPiece::ToBool() const {
+  switch (type_) {
+    case TYPE_BOOL:
+      return bool_;
+    case TYPE_STRING:
+      return StringToNumber<bool>(safe_strtob);
+    default:
+      return util::InvalidArgumentError(
+          ValueAsStringOrDefault("Wrong type. Cannot convert to Bool."));
+  }
+}
+
+util::StatusOr<std::string> DataPiece::ToString() const {
+  switch (type_) {
+    case TYPE_STRING:
+      return std::string(str_);
+    case TYPE_BYTES: {
+      std::string base64;
+      Base64Escape(str_, &base64);
+      return base64;
+    }
+    default:
+      return util::InvalidArgumentError(
+          ValueAsStringOrDefault("Cannot convert to string."));
+  }
+}
+
+std::string DataPiece::ValueAsStringOrDefault(
+    StringPiece default_string) const {
+  switch (type_) {
+    case TYPE_INT32:
+      return StrCat(i32_);
+    case TYPE_INT64:
+      return StrCat(i64_);
+    case TYPE_UINT32:
+      return StrCat(u32_);
+    case TYPE_UINT64:
+      return StrCat(u64_);
+    case TYPE_DOUBLE:
+      return DoubleAsString(double_);
+    case TYPE_FLOAT:
+      return FloatAsString(float_);
+    case TYPE_BOOL:
+      return SimpleBtoa(bool_);
+    case TYPE_STRING:
+      return StrCat("\"", str_.ToString(), "\"");
+    case TYPE_BYTES: {
+      std::string base64;
+      WebSafeBase64Escape(str_, &base64);
+      return StrCat("\"", base64, "\"");
+    }
+    case TYPE_NULL:
+      return "null";
+    default:
+      return std::string(default_string);
+  }
+}
+
+util::StatusOr<std::string> DataPiece::ToBytes() const {
+  if (type_ == TYPE_BYTES) return str_.ToString();
+  if (type_ == TYPE_STRING) {
+    std::string decoded;
+    if (!DecodeBase64(str_, &decoded)) {
+      return util::InvalidArgumentError(
+          ValueAsStringOrDefault("Invalid data in input."));
+    }
+    return decoded;
+  } else {
+    return util::InvalidArgumentError(ValueAsStringOrDefault(
+        "Wrong type. Only String or Bytes can be converted to Bytes."));
+  }
+}
+
+util::StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type,
+                                      bool use_lower_camel_for_enums,
+                                      bool case_insensitive_enum_parsing,
+                                      bool ignore_unknown_enum_values,
+                                      bool* is_unknown_enum_value) const {
+  if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE;
+
+  if (type_ == TYPE_STRING) {
+    // First try the given value as a name.
+    std::string enum_name = std::string(str_);
+    const google::protobuf::EnumValue* value =
+        FindEnumValueByNameOrNull(enum_type, enum_name);
+    if (value != nullptr) return value->number();
+
+    // Check if int version of enum is sent as string.
+    util::StatusOr<int32_t> int_value = ToInt32();
+    if (int_value.ok()) {
+      if (const google::protobuf::EnumValue* enum_value =
+              FindEnumValueByNumberOrNull(enum_type, int_value.value())) {
+        return enum_value->number();
+      }
+    }
+
+    // Next try a normalized name.
+    bool should_normalize_enum =
+        case_insensitive_enum_parsing || use_lower_camel_for_enums;
+    if (should_normalize_enum) {
+      for (std::string::iterator it = enum_name.begin(); it != enum_name.end();
+           ++it) {
+        *it = *it == '-' ? '_' : ascii_toupper(*it);
+      }
+      value = FindEnumValueByNameOrNull(enum_type, enum_name);
+      if (value != nullptr) return value->number();
+    }
+
+    // If use_lower_camel_for_enums is true try with enum name without
+    // underscore. This will also accept camel case names as the enum_name has
+    // been normalized before.
+    if (use_lower_camel_for_enums) {
+      value = FindEnumValueByNameWithoutUnderscoreOrNull(enum_type, enum_name);
+      if (value != nullptr) return value->number();
+    }
+
+    // If ignore_unknown_enum_values is true an unknown enum value is ignored.
+    if (ignore_unknown_enum_values) {
+      *is_unknown_enum_value = true;
+      if (enum_type->enumvalue_size() > 0) {
+        return enum_type->enumvalue(0).number();
+      }
+    }
+  } else {
+    // We don't need to check whether the value is actually declared in the
+    // enum because we preserve unknown enum values as well.
+    return ToInt32();
+  }
+  return util::InvalidArgumentError(
+      ValueAsStringOrDefault("Cannot find enum with given value."));
+}
+
+template <typename To>
+util::StatusOr<To> DataPiece::GenericConvert() const {
+  switch (type_) {
+    case TYPE_INT32:
+      return NumberConvertAndCheck<To, int32_t>(i32_);
+    case TYPE_INT64:
+      return NumberConvertAndCheck<To, int64_t>(i64_);
+    case TYPE_UINT32:
+      return NumberConvertAndCheck<To, uint32_t>(u32_);
+    case TYPE_UINT64:
+      return NumberConvertAndCheck<To, uint64_t>(u64_);
+    case TYPE_DOUBLE:
+      return NumberConvertAndCheck<To, double>(double_);
+    case TYPE_FLOAT:
+      return NumberConvertAndCheck<To, float>(float_);
+    default:  // TYPE_ENUM, TYPE_STRING, TYPE_CORD, TYPE_BOOL
+      return util::InvalidArgumentError(ValueAsStringOrDefault(
+          "Wrong type. Bool, Enum, String and Cord not supported in "
+          "GenericConvert."));
+  }
+}
+
+template <typename To>
+util::StatusOr<To> DataPiece::StringToNumber(bool (*func)(StringPiece,
+                                                          To*)) const {
+  if (str_.size() > 0 && (str_[0] == ' ' || str_[str_.size() - 1] == ' ')) {
+    return util::InvalidArgumentError(StrCat("\"", str_, "\""));
+  }
+  To result;
+  if (func(str_, &result)) return result;
+  return util::InvalidArgumentError(
+      StrCat("\"", std::string(str_), "\""));
+}
+
+bool DataPiece::DecodeBase64(StringPiece src, std::string* dest) const {
+  // Try web-safe decode first, if it fails, try the non-web-safe decode.
+  if (WebSafeBase64Unescape(src, dest)) {
+    if (use_strict_base64_decoding_) {
+      // In strict mode, check if the escaped version gives us the same value as
+      // unescaped.
+      std::string encoded;
+      // WebSafeBase64Escape does no padding by default.
+      WebSafeBase64Escape(*dest, &encoded);
+      // Remove trailing padding '=' characters before comparison.
+      StringPiece src_no_padding = StringPiece(src).substr(
+          0, HasSuffixString(src, "=") ? src.find_last_not_of('=') + 1
+                                      : src.length());
+      return encoded == src_no_padding;
+    }
+    return true;
+  }
+
+  if (Base64Unescape(src, dest)) {
+    if (use_strict_base64_decoding_) {
+      std::string encoded;
+      Base64Escape(reinterpret_cast<const unsigned char*>(dest->data()),
+                         dest->length(), &encoded, false);
+      StringPiece src_no_padding = StringPiece(src).substr(
+          0, HasSuffixString(src, "=") ? src.find_last_not_of('=') + 1
+                                      : src.length());
+      return encoded == src_no_padding;
+    }
+    return true;
+  }
+
+  return false;
+}
+
+void DataPiece::InternalCopy(const DataPiece& other) {
+  type_ = other.type_;
+  use_strict_base64_decoding_ = other.use_strict_base64_decoding_;
+  switch (type_) {
+    case TYPE_INT32:
+    case TYPE_INT64:
+    case TYPE_UINT32:
+    case TYPE_UINT64:
+    case TYPE_DOUBLE:
+    case TYPE_FLOAT:
+    case TYPE_BOOL:
+    case TYPE_ENUM:
+    case TYPE_NULL:
+    case TYPE_BYTES:
+    case TYPE_STRING: {
+      str_ = other.str_;
+      break;
+    }
+  }
+}
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/default_value_objectwriter.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/default_value_objectwriter.cpp
new file mode 100644
index 0000000..a7d4ce7
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/default_value_objectwriter.cpp
@@ -0,0 +1,642 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/default_value_objectwriter.h>
+
+#include <cstdint>
+#include <unordered_map>
+
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/map_util.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+namespace {
+// Helper function to convert string value to given data type by calling the
+// passed converter function on the DataPiece created from "value" argument.
+// If value is empty or if conversion fails, the default_value is returned.
+template <typename T>
+T ConvertTo(StringPiece value,
+            util::StatusOr<T> (DataPiece::*converter_fn)() const,
+            T default_value) {
+  if (value.empty()) return default_value;
+  util::StatusOr<T> result = (DataPiece(value, true).*converter_fn)();
+  return result.ok() ? result.value() : default_value;
+}
+}  // namespace
+
+DefaultValueObjectWriter::DefaultValueObjectWriter(
+    TypeResolver* type_resolver, const google::protobuf::Type& type,
+    ObjectWriter* ow)
+    : typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
+      own_typeinfo_(true),
+      type_(type),
+      current_(nullptr),
+      root_(nullptr),
+      suppress_empty_list_(false),
+      preserve_proto_field_names_(false),
+      use_ints_for_enums_(false),
+      ow_(ow) {}
+
+DefaultValueObjectWriter::~DefaultValueObjectWriter() {
+  if (own_typeinfo_) {
+    delete typeinfo_;
+  }
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBool(
+    StringPiece name, bool value) {
+  if (current_ == nullptr) {
+    ow_->RenderBool(name, value);
+  } else {
+    RenderDataPiece(name, DataPiece(value));
+  }
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt32(
+    StringPiece name, int32_t value) {
+  if (current_ == nullptr) {
+    ow_->RenderInt32(name, value);
+  } else {
+    RenderDataPiece(name, DataPiece(value));
+  }
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint32(
+    StringPiece name, uint32_t value) {
+  if (current_ == nullptr) {
+    ow_->RenderUint32(name, value);
+  } else {
+    RenderDataPiece(name, DataPiece(value));
+  }
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt64(
+    StringPiece name, int64_t value) {
+  if (current_ == nullptr) {
+    ow_->RenderInt64(name, value);
+  } else {
+    RenderDataPiece(name, DataPiece(value));
+  }
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint64(
+    StringPiece name, uint64_t value) {
+  if (current_ == nullptr) {
+    ow_->RenderUint64(name, value);
+  } else {
+    RenderDataPiece(name, DataPiece(value));
+  }
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderDouble(
+    StringPiece name, double value) {
+  if (current_ == nullptr) {
+    ow_->RenderDouble(name, value);
+  } else {
+    RenderDataPiece(name, DataPiece(value));
+  }
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderFloat(
+    StringPiece name, float value) {
+  if (current_ == nullptr) {
+    ow_->RenderBool(name, value);
+  } else {
+    RenderDataPiece(name, DataPiece(value));
+  }
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderString(
+    StringPiece name, StringPiece value) {
+  if (current_ == nullptr) {
+    ow_->RenderString(name, value);
+  } else {
+    // Since StringPiece is essentially a pointer, takes a copy of "value" to
+    // avoid ownership issues.
+    string_values_.emplace_back(new std::string(value));
+    RenderDataPiece(name, DataPiece(*string_values_.back(), true));
+  }
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBytes(
+    StringPiece name, StringPiece value) {
+  if (current_ == nullptr) {
+    ow_->RenderBytes(name, value);
+  } else {
+    // Since StringPiece is essentially a pointer, takes a copy of "value" to
+    // avoid ownership issues.
+    string_values_.emplace_back(new std::string(value));
+    RenderDataPiece(name, DataPiece(*string_values_.back(), false, true));
+  }
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull(
+    StringPiece name) {
+  if (current_ == nullptr) {
+    ow_->RenderNull(name);
+  } else {
+    RenderDataPiece(name, DataPiece::NullData());
+  }
+  return this;
+}
+
+void DefaultValueObjectWriter::RegisterFieldScrubCallBack(
+    FieldScrubCallBack field_scrub_callback) {
+  field_scrub_callback_ = std::move(field_scrub_callback);
+}
+
+DefaultValueObjectWriter::Node* DefaultValueObjectWriter::CreateNewNode(
+    const std::string& name, const google::protobuf::Type* type, NodeKind kind,
+    const DataPiece& data, bool is_placeholder,
+    const std::vector<std::string>& path, bool suppress_empty_list,
+    bool preserve_proto_field_names, bool use_ints_for_enums,
+    FieldScrubCallBack field_scrub_callback) {
+  return new Node(name, type, kind, data, is_placeholder, path,
+                  suppress_empty_list, preserve_proto_field_names,
+                  use_ints_for_enums, std::move(field_scrub_callback));
+}
+
+DefaultValueObjectWriter::Node::Node(
+    const std::string& name, const google::protobuf::Type* type, NodeKind kind,
+    const DataPiece& data, bool is_placeholder,
+    const std::vector<std::string>& path, bool suppress_empty_list,
+    bool preserve_proto_field_names, bool use_ints_for_enums,
+    FieldScrubCallBack field_scrub_callback)
+    : name_(name),
+      type_(type),
+      kind_(kind),
+      is_any_(false),
+      data_(data),
+      is_placeholder_(is_placeholder),
+      path_(path),
+      suppress_empty_list_(suppress_empty_list),
+      preserve_proto_field_names_(preserve_proto_field_names),
+      use_ints_for_enums_(use_ints_for_enums),
+      field_scrub_callback_(std::move(field_scrub_callback)) {}
+
+DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild(
+    StringPiece name) {
+  if (name.empty() || kind_ != OBJECT) {
+    return nullptr;
+  }
+  for (Node* child : children_) {
+    if (child->name() == name) {
+      return child;
+    }
+  }
+  return nullptr;
+}
+
+void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) {
+  if (kind_ == PRIMITIVE) {
+    ObjectWriter::RenderDataPieceTo(data_, name_, ow);
+    return;
+  }
+
+  // Render maps. Empty maps are rendered as "{}".
+  if (kind_ == MAP) {
+    ow->StartObject(name_);
+    WriteChildren(ow);
+    ow->EndObject();
+    return;
+  }
+
+  // Write out lists. If we didn't have any list in response, write out empty
+  // list.
+  if (kind_ == LIST) {
+    // Suppress empty lists if requested.
+    if (suppress_empty_list_ && is_placeholder_) return;
+
+    ow->StartList(name_);
+    WriteChildren(ow);
+    ow->EndList();
+    return;
+  }
+
+  // If is_placeholder_ = true, we didn't see this node in the response, so
+  // skip output.
+  if (is_placeholder_) return;
+
+  ow->StartObject(name_);
+  WriteChildren(ow);
+  ow->EndObject();
+}
+
+void DefaultValueObjectWriter::Node::WriteChildren(ObjectWriter* ow) {
+  for (Node* child : children_) {
+    child->WriteTo(ow);
+  }
+}
+
+const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType(
+    const google::protobuf::Type& found_type, const TypeInfo* typeinfo) {
+  // If this field is a map, we should use the type of its "Value" as
+  // the type of the child node.
+  for (int i = 0; i < found_type.fields_size(); ++i) {
+    const google::protobuf::Field& sub_field = found_type.fields(i);
+    if (sub_field.number() != 2) {
+      continue;
+    }
+    if (sub_field.kind() != google::protobuf::Field::TYPE_MESSAGE) {
+      // This map's value type is not a message type. We don't need to
+      // get the field_type in this case.
+      break;
+    }
+    util::StatusOr<const google::protobuf::Type*> sub_type =
+        typeinfo->ResolveTypeUrl(sub_field.type_url());
+    if (!sub_type.ok()) {
+      GOOGLE_LOG(WARNING) << "Cannot resolve type '" << sub_field.type_url() << "'.";
+    } else {
+      return sub_type.value();
+    }
+    break;
+  }
+  return nullptr;
+}
+
+void DefaultValueObjectWriter::Node::PopulateChildren(
+    const TypeInfo* typeinfo) {
+  // Ignores well known types that don't require automatically populating their
+  // primitive children. For type "Any", we only populate its children when the
+  // "@type" field is set.
+  // TODO(tsun): remove "kStructValueType" from the list. It's being checked
+  //     now because of a bug in the tool-chain that causes the "oneof_index"
+  //     of kStructValueType to not be set correctly.
+  if (type_ == nullptr || type_->name() == kAnyType ||
+      type_->name() == kStructType || type_->name() == kTimestampType ||
+      type_->name() == kDurationType || type_->name() == kStructValueType) {
+    return;
+  }
+  std::vector<Node*> new_children;
+  std::unordered_map<std::string, int> orig_children_map;
+
+  // Creates a map of child nodes to speed up lookup.
+  for (size_t i = 0; i < children_.size(); ++i) {
+    InsertIfNotPresent(&orig_children_map, children_[i]->name_, i);
+  }
+
+  for (int i = 0; i < type_->fields_size(); ++i) {
+    const google::protobuf::Field& field = type_->fields(i);
+
+    // This code is checking if the field to be added to the tree should be
+    // scrubbed or not by calling the field_scrub_callback_ callback function.
+    std::vector<std::string> path;
+    if (!path_.empty()) {
+      path.insert(path.begin(), path_.begin(), path_.end());
+    }
+    path.push_back(field.name());
+    if (field_scrub_callback_ && field_scrub_callback_(path, &field)) {
+      continue;
+    }
+
+    std::unordered_map<std::string, int>::iterator found =
+        orig_children_map.find(field.name());
+    // If the child field has already been set, we just add it to the new list
+    // of children.
+    if (found != orig_children_map.end()) {
+      new_children.push_back(children_[found->second]);
+      children_[found->second] = nullptr;
+      continue;
+    }
+
+    const google::protobuf::Type* field_type = nullptr;
+    bool is_map = false;
+    NodeKind kind = PRIMITIVE;
+
+    if (field.kind() == google::protobuf::Field::TYPE_MESSAGE) {
+      kind = OBJECT;
+      util::StatusOr<const google::protobuf::Type*> found_result =
+          typeinfo->ResolveTypeUrl(field.type_url());
+      if (!found_result.ok()) {
+        // "field" is of an unknown type.
+        GOOGLE_LOG(WARNING) << "Cannot resolve type '" << field.type_url() << "'.";
+      } else {
+        const google::protobuf::Type* found_type = found_result.value();
+        is_map = IsMap(field, *found_type);
+
+        if (!is_map) {
+          field_type = found_type;
+        } else {
+          // If this field is a map, we should use the type of its "Value" as
+          // the type of the child node.
+          field_type = GetMapValueType(*found_type, typeinfo);
+          kind = MAP;
+        }
+      }
+    }
+
+    if (!is_map &&
+        field.cardinality() == google::protobuf::Field::CARDINALITY_REPEATED) {
+      kind = LIST;
+    }
+
+    // If oneof_index() != 0, the child field is part of a "oneof", which means
+    // the child field is optional and we shouldn't populate its default
+    // primitive value.
+    if (field.oneof_index() != 0 && kind == PRIMITIVE) continue;
+
+    // If the child field is of primitive type, sets its data to the default
+    // value of its type.
+    std::unique_ptr<Node> child(
+        new Node(preserve_proto_field_names_ ? field.name() : field.json_name(),
+                 field_type, kind,
+                 kind == PRIMITIVE ? CreateDefaultDataPieceForField(
+                                         field, typeinfo, use_ints_for_enums_)
+                                   : DataPiece::NullData(),
+                 true, path, suppress_empty_list_, preserve_proto_field_names_,
+                 use_ints_for_enums_, field_scrub_callback_));
+    new_children.push_back(child.release());
+  }
+  // Adds all leftover nodes in children_ to the beginning of new_child.
+  for (size_t i = 0; i < children_.size(); ++i) {
+    if (children_[i] == nullptr) {
+      continue;
+    }
+    new_children.insert(new_children.begin(), children_[i]);
+    children_[i] = nullptr;
+  }
+  children_.swap(new_children);
+}
+
+void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) {
+  // If this is an "Any" node with "@type" already given and no other children
+  // have been added, populates its children.
+  if (node != nullptr && node->is_any() && node->type() != nullptr &&
+      node->type()->name() != kAnyType && node->number_of_children() == 1) {
+    node->PopulateChildren(typeinfo_);
+  }
+}
+
+DataPiece DefaultValueObjectWriter::FindEnumDefault(
+    const google::protobuf::Field& field, const TypeInfo* typeinfo,
+    bool use_ints_for_enums) {
+  const google::protobuf::Enum* enum_type =
+      typeinfo->GetEnumByTypeUrl(field.type_url());
+  if (!enum_type) {
+    GOOGLE_LOG(WARNING) << "Could not find enum with type '" << field.type_url()
+                 << "'";
+    return DataPiece::NullData();
+  }
+  if (!field.default_value().empty()) {
+    if (!use_ints_for_enums) {
+      return DataPiece(field.default_value(), true);
+    } else {
+      const std::string& enum_default_value_name = field.default_value();
+      for (int enum_index = 0; enum_index < enum_type->enumvalue_size();
+           ++enum_index) {
+        auto& enum_value = enum_type->enumvalue(enum_index);
+        if (enum_value.name() == enum_default_value_name)
+          return DataPiece(enum_value.number());
+      }
+      GOOGLE_LOG(WARNING) << "Could not find enum value '" << enum_default_value_name
+                   << "' with type '" << field.type_url() << "'";
+      return DataPiece::NullData();
+    }
+  }
+  // We treat the first value as the default if none is specified.
+  return enum_type->enumvalue_size() > 0
+             ? (use_ints_for_enums
+                    ? DataPiece(enum_type->enumvalue(0).number())
+                    : DataPiece(enum_type->enumvalue(0).name(), true))
+             : DataPiece::NullData();
+}
+
+DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField(
+    const google::protobuf::Field& field, const TypeInfo* typeinfo,
+    bool use_ints_for_enums) {
+  switch (field.kind()) {
+    case google::protobuf::Field::TYPE_DOUBLE: {
+      return DataPiece(ConvertTo<double>(
+          field.default_value(), &DataPiece::ToDouble, static_cast<double>(0)));
+    }
+    case google::protobuf::Field::TYPE_FLOAT: {
+      return DataPiece(ConvertTo<float>(
+          field.default_value(), &DataPiece::ToFloat, static_cast<float>(0)));
+    }
+    case google::protobuf::Field::TYPE_INT64:
+    case google::protobuf::Field::TYPE_SINT64:
+    case google::protobuf::Field::TYPE_SFIXED64: {
+      return DataPiece(ConvertTo<int64_t>(
+          field.default_value(), &DataPiece::ToInt64, static_cast<int64_t>(0)));
+    }
+    case google::protobuf::Field::TYPE_UINT64:
+    case google::protobuf::Field::TYPE_FIXED64: {
+      return DataPiece(ConvertTo<uint64_t>(field.default_value(),
+                                           &DataPiece::ToUint64,
+                                           static_cast<uint64_t>(0)));
+    }
+    case google::protobuf::Field::TYPE_INT32:
+    case google::protobuf::Field::TYPE_SINT32:
+    case google::protobuf::Field::TYPE_SFIXED32: {
+      return DataPiece(ConvertTo<int32_t>(
+          field.default_value(), &DataPiece::ToInt32, static_cast<int32_t>(0)));
+    }
+    case google::protobuf::Field::TYPE_BOOL: {
+      return DataPiece(
+          ConvertTo<bool>(field.default_value(), &DataPiece::ToBool, false));
+    }
+    case google::protobuf::Field::TYPE_STRING: {
+      return DataPiece(field.default_value(), true);
+    }
+    case google::protobuf::Field::TYPE_BYTES: {
+      return DataPiece(field.default_value(), false, true);
+    }
+    case google::protobuf::Field::TYPE_UINT32:
+    case google::protobuf::Field::TYPE_FIXED32: {
+      return DataPiece(ConvertTo<uint32_t>(field.default_value(),
+                                           &DataPiece::ToUint32,
+                                           static_cast<uint32_t>(0)));
+    }
+    case google::protobuf::Field::TYPE_ENUM: {
+      return FindEnumDefault(field, typeinfo, use_ints_for_enums);
+    }
+    default: {
+      return DataPiece::NullData();
+    }
+  }
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject(
+    StringPiece name) {
+  if (current_ == nullptr) {
+    std::vector<std::string> path;
+    root_.reset(CreateNewNode(std::string(name), &type_, OBJECT,
+                              DataPiece::NullData(), false, path,
+                              suppress_empty_list_, preserve_proto_field_names_,
+                              use_ints_for_enums_, field_scrub_callback_));
+    root_->PopulateChildren(typeinfo_);
+    current_ = root_.get();
+    return this;
+  }
+  MaybePopulateChildrenOfAny(current_);
+  Node* child = current_->FindChild(name);
+  if (current_->kind() == LIST || current_->kind() == MAP || child == nullptr) {
+    // If current_ is a list or a map node, we should create a new child and use
+    // the type of current_ as the type of the new child.
+    std::unique_ptr<Node> node(
+        CreateNewNode(std::string(name),
+                      ((current_->kind() == LIST || current_->kind() == MAP)
+                           ? current_->type()
+                           : nullptr),
+                      OBJECT, DataPiece::NullData(), false,
+                      child == nullptr ? current_->path() : child->path(),
+                      suppress_empty_list_, preserve_proto_field_names_,
+                      use_ints_for_enums_, field_scrub_callback_));
+    child = node.get();
+    current_->AddChild(node.release());
+  }
+
+  child->set_is_placeholder(false);
+  if (child->kind() == OBJECT && child->number_of_children() == 0) {
+    child->PopulateChildren(typeinfo_);
+  }
+
+  stack_.push(current_);
+  current_ = child;
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::EndObject() {
+  if (stack_.empty()) {
+    // The root object ends here. Writes out the tree.
+    WriteRoot();
+    return this;
+  }
+  current_ = stack_.top();
+  stack_.pop();
+  return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::StartList(
+    StringPiece name) {
+  if (current_ == nullptr) {
+    std::vector<std::string> path;
+    root_.reset(CreateNewNode(std::string(name), &type_, LIST,
+                              DataPiece::NullData(), false, path,
+                              suppress_empty_list_, preserve_proto_field_names_,
+                              use_ints_for_enums_, field_scrub_callback_));
+    current_ = root_.get();
+    return this;
+  }
+  MaybePopulateChildrenOfAny(current_);
+  Node* child = current_->FindChild(name);
+  if (child == nullptr || child->kind() != LIST) {
+    std::unique_ptr<Node> node(CreateNewNode(
+        std::string(name), nullptr, LIST, DataPiece::NullData(), false,
+        child == nullptr ? current_->path() : child->path(),
+        suppress_empty_list_, preserve_proto_field_names_, use_ints_for_enums_,
+        field_scrub_callback_));
+    child = node.get();
+    current_->AddChild(node.release());
+  }
+  child->set_is_placeholder(false);
+
+  stack_.push(current_);
+  current_ = child;
+  return this;
+}
+
+void DefaultValueObjectWriter::WriteRoot() {
+  root_->WriteTo(ow_);
+  root_.reset(nullptr);
+  current_ = nullptr;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::EndList() {
+  if (stack_.empty()) {
+    WriteRoot();
+    return this;
+  }
+  current_ = stack_.top();
+  stack_.pop();
+  return this;
+}
+
+void DefaultValueObjectWriter::RenderDataPiece(StringPiece name,
+                                               const DataPiece& data) {
+  MaybePopulateChildrenOfAny(current_);
+  if (current_->type() != nullptr && current_->type()->name() == kAnyType &&
+      name == "@type") {
+    util::StatusOr<std::string> data_string = data.ToString();
+    if (data_string.ok()) {
+      const std::string& string_value = data_string.value();
+      // If the type of current_ is "Any" and its "@type" field is being set
+      // here, sets the type of current_ to be the type specified by the
+      // "@type".
+      util::StatusOr<const google::protobuf::Type*> found_type =
+          typeinfo_->ResolveTypeUrl(string_value);
+      if (!found_type.ok()) {
+        GOOGLE_LOG(WARNING) << "Failed to resolve type '" << string_value << "'.";
+      } else {
+        current_->set_type(found_type.value());
+      }
+      current_->set_is_any(true);
+      // If the "@type" field is placed after other fields, we should populate
+      // other children of primitive type now. Otherwise, we should wait until
+      // the first value field is rendered before we populate the children,
+      // because the "value" field of a Any message could be omitted.
+      if (current_->number_of_children() > 1 && current_->type() != nullptr) {
+        current_->PopulateChildren(typeinfo_);
+      }
+    }
+  }
+  Node* child = current_->FindChild(name);
+  if (child == nullptr || child->kind() != PRIMITIVE) {
+    // No children are found, creates a new child.
+    std::unique_ptr<Node> node(
+        CreateNewNode(std::string(name), nullptr, PRIMITIVE, data, false,
+                      child == nullptr ? current_->path() : child->path(),
+                      suppress_empty_list_, preserve_proto_field_names_,
+                      use_ints_for_enums_, field_scrub_callback_));
+    current_->AddChild(node.release());
+  } else {
+    child->set_data(data);
+    child->set_is_placeholder(false);
+  }
+}
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/error_listener.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/error_listener.cpp
new file mode 100644
index 0000000..538307b
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/error_listener.cpp
@@ -0,0 +1,42 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/error_listener.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/field_mask_utility.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/field_mask_utility.cpp
new file mode 100644
index 0000000..521bf48
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/field_mask_utility.cpp
@@ -0,0 +1,218 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/field_mask_utility.h>
+
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/status_macros.h>
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+namespace {
+
+// Appends a FieldMask path segment to a prefix.
+std::string AppendPathSegmentToPrefix(StringPiece prefix,
+                                      StringPiece segment) {
+  if (prefix.empty()) {
+    return std::string(segment);
+  }
+  if (segment.empty()) {
+    return std::string(prefix);
+  }
+  // If the segment is a map key, appends it to the prefix without the ".".
+  if (HasPrefixString(segment, "[\"")) {
+    return StrCat(prefix, segment);
+  }
+  return StrCat(prefix, ".", segment);
+}
+
+}  // namespace
+
+std::string ConvertFieldMaskPath(const StringPiece path,
+                                 ConverterCallback converter) {
+  std::string result;
+  result.reserve(path.size() << 1);
+
+  bool is_quoted = false;
+  bool is_escaping = false;
+  int current_segment_start = 0;
+
+  // Loops until 1 passed the end of the input to make handling the last
+  // segment easier.
+  for (size_t i = 0; i <= path.size(); ++i) {
+    // Outputs quoted string as-is.
+    if (is_quoted) {
+      if (i == path.size()) {
+        break;
+      }
+      result.push_back(path[i]);
+      if (is_escaping) {
+        is_escaping = false;
+      } else if (path[i] == '\\') {
+        is_escaping = true;
+      } else if (path[i] == '\"') {
+        current_segment_start = i + 1;
+        is_quoted = false;
+      }
+      continue;
+    }
+    if (i == path.size() || path[i] == '.' || path[i] == '(' ||
+        path[i] == ')' || path[i] == '\"') {
+      result += converter(
+          path.substr(current_segment_start, i - current_segment_start));
+      if (i < path.size()) {
+        result.push_back(path[i]);
+      }
+      current_segment_start = i + 1;
+    }
+    if (i < path.size() && path[i] == '\"') {
+      is_quoted = true;
+    }
+  }
+  return result;
+}
+
+util::Status DecodeCompactFieldMaskPaths(StringPiece paths,
+                                         PathSinkCallback path_sink) {
+  std::stack<std::string> prefix;
+  int length = paths.length();
+  int previous_position = 0;
+  bool in_map_key = false;
+  bool is_escaping = false;
+  // Loops until 1 passed the end of the input to make the handle of the last
+  // segment easier.
+  for (int i = 0; i <= length; ++i) {
+    if (i != length) {
+      // Skips everything in a map key until we hit the end of it, which is
+      // marked by an un-escaped '"' immediately followed by a ']'.
+      if (in_map_key) {
+        if (is_escaping) {
+          is_escaping = false;
+          continue;
+        }
+        if (paths[i] == '\\') {
+          is_escaping = true;
+          continue;
+        }
+        if (paths[i] != '\"') {
+          continue;
+        }
+        // Un-escaped '"' must be followed with a ']'.
+        if (i >= length - 1 || paths[i + 1] != ']') {
+          return util::InvalidArgumentError(StrCat(
+              "Invalid FieldMask '", paths,
+              "'. Map keys should be represented as [\"some_key\"]."));
+        }
+        // The end of the map key ("\"]") has been found.
+        in_map_key = false;
+        // Skips ']'.
+        i++;
+        // Checks whether the key ends at the end of a path segment.
+        if (i < length - 1 && paths[i + 1] != '.' && paths[i + 1] != ',' &&
+            paths[i + 1] != ')' && paths[i + 1] != '(') {
+          return util::InvalidArgumentError(StrCat(
+              "Invalid FieldMask '", paths,
+              "'. Map keys should be at the end of a path segment."));
+        }
+        is_escaping = false;
+        continue;
+      }
+
+      // We are not in a map key, look for the start of one.
+      if (paths[i] == '[') {
+        if (i >= length - 1 || paths[i + 1] != '\"') {
+          return util::InvalidArgumentError(StrCat(
+              "Invalid FieldMask '", paths,
+              "'. Map keys should be represented as [\"some_key\"]."));
+        }
+        // "[\"" starts a map key.
+        in_map_key = true;
+        i++;  // Skips the '\"'.
+        continue;
+      }
+      // If the current character is not a special character (',', '(' or ')'),
+      // continue to the next.
+      if (paths[i] != ',' && paths[i] != ')' && paths[i] != '(') {
+        continue;
+      }
+    }
+    // Gets the current segment - sub-string between previous position (after
+    // '(', ')', ',', or the beginning of the input) and the current position.
+    StringPiece segment =
+        paths.substr(previous_position, i - previous_position);
+    std::string current_prefix = prefix.empty() ? "" : prefix.top();
+
+    if (i < length && paths[i] == '(') {
+      // Builds a prefix and save it into the stack.
+      prefix.push(AppendPathSegmentToPrefix(current_prefix, segment));
+    } else if (!segment.empty()) {
+      // When the current character is ')', ',' or the current position has
+      // passed the end of the input, builds and outputs a new paths by
+      // concatenating the last prefix with the current segment.
+      RETURN_IF_ERROR(
+          path_sink(AppendPathSegmentToPrefix(current_prefix, segment)));
+    }
+
+    // Removes the last prefix after seeing a ')'.
+    if (i < length && paths[i] == ')') {
+      if (prefix.empty()) {
+        return util::InvalidArgumentError(
+            StrCat("Invalid FieldMask '", paths,
+                         "'. Cannot find matching '(' for all ')'."));
+      }
+      prefix.pop();
+    }
+    previous_position = i + 1;
+  }
+  if (in_map_key) {
+    return util::InvalidArgumentError(
+        StrCat("Invalid FieldMask '", paths,
+                     "'. Cannot find matching ']' for all '['."));
+  }
+  if (!prefix.empty()) {
+    return util::InvalidArgumentError(
+        StrCat("Invalid FieldMask '", paths,
+                     "'. Cannot find matching ')' for all '('."));
+  }
+  return util::Status();
+}
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/json_escaping.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/json_escaping.cpp
new file mode 100644
index 0000000..c192ddc
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/json_escaping.cpp
@@ -0,0 +1,372 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/json_escaping.h>
+
+#include <cstdint>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+namespace {
+
+// Array of hex characters for conversion to hex.
+static const char kHex[] = "0123456789abcdef";
+
+// Characters 0x00 to 0x9f are very commonly used, so we provide a special
+// table lookup.
+//
+// For unicode code point ch < 0xa0:
+// kCommonEscapes[ch] is the escaped string of ch, if escaping is needed;
+//                    or an empty string, if escaping is not needed.
+static const char kCommonEscapes[160][7] = {
+    // C0 (ASCII and derivatives) control characters
+    "\\u0000", "\\u0001", "\\u0002", "\\u0003",  // 0x00
+    "\\u0004", "\\u0005", "\\u0006", "\\u0007", "\\b", "\\t", "\\n", "\\u000b",
+    "\\f", "\\r", "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012",
+    "\\u0013",  // 0x10
+    "\\u0014", "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a",
+    "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f",
+    // Escaping of " and \ are required by www.json.org string definition.
+    // Escaping of < and > are required for HTML security.
+    "", "", "\\\"", "", "", "", "", "",                              // 0x20
+    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",  // 0x30
+    "", "", "", "", "\\u003c", "", "\\u003e", "", "", "", "", "", "", "", "",
+    "",                                                                  // 0x40
+    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",      // 0x50
+    "", "", "", "", "\\\\", "", "", "", "", "", "", "", "", "", "", "",  // 0x60
+    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",      // 0x70
+    "", "", "", "", "", "", "", "\\u007f",
+    // C1 (ISO 8859 and Unicode) extended control characters
+    "\\u0080", "\\u0081", "\\u0082", "\\u0083",  // 0x80
+    "\\u0084", "\\u0085", "\\u0086", "\\u0087", "\\u0088", "\\u0089", "\\u008a",
+    "\\u008b", "\\u008c", "\\u008d", "\\u008e", "\\u008f", "\\u0090", "\\u0091",
+    "\\u0092", "\\u0093",  // 0x90
+    "\\u0094", "\\u0095", "\\u0096", "\\u0097", "\\u0098", "\\u0099", "\\u009a",
+    "\\u009b", "\\u009c", "\\u009d", "\\u009e", "\\u009f"};
+
+// Determines if the given char value is a unicode surrogate code unit (either
+// high-surrogate or low-surrogate).
+inline bool IsSurrogate(uint32_t c) {
+  // Optimized form of:
+  // return c >= kMinHighSurrogate && c <= kMaxLowSurrogate;
+  // (Reduced from 3 ALU instructions to 2 ALU instructions)
+  return (c & 0xfffff800) == JsonEscaping::kMinHighSurrogate;
+}
+
+// Returns true if the given unicode code point cp is a valid
+// unicode code point (i.e. in the range 0 <= cp <= kMaxCodePoint).
+inline bool IsValidCodePoint(uint32_t cp) {
+  return cp <= JsonEscaping::kMaxCodePoint;
+}
+
+// Returns the low surrogate for the given unicode code point. The result is
+// meaningless if the given code point is not a supplementary character.
+inline uint16_t ToLowSurrogate(uint32_t cp) {
+  return (cp &
+          (JsonEscaping::kMaxLowSurrogate - JsonEscaping::kMinLowSurrogate)) +
+         JsonEscaping::kMinLowSurrogate;
+}
+
+// Returns the high surrogate for the given unicode code point. The result is
+// meaningless if the given code point is not a supplementary character.
+inline uint16_t ToHighSurrogate(uint32_t cp) {
+  return (cp >> 10) + (JsonEscaping::kMinHighSurrogate -
+                       (JsonEscaping::kMinSupplementaryCodePoint >> 10));
+}
+
+// Input str is encoded in UTF-8. A unicode code point could be encoded in
+// UTF-8 using anywhere from 1 to 4 characters, and it could span multiple
+// reads of the ByteSource.
+//
+// This function reads the next unicode code point from the input (str) at
+// the given position (index), taking into account any left-over partial
+// code point from the previous iteration (cp), together with the number
+// of characters left to read to complete this code point (num_left).
+//
+// This function assumes that the input (str) is valid at the given position
+// (index). In order words, at least one character could be read successfully.
+//
+// The code point read (partial or complete) is stored in (cp). Upon return,
+// (num_left) stores the number of characters that has yet to be read in
+// order to complete the current unicode code point. If the read is complete,
+// then (num_left) is 0. Also, (num_read) is the number of characters read.
+//
+// Returns false if we encounter an invalid UTF-8 string. Returns true
+// otherwise, including the case when we reach the end of the input (str)
+// before a complete unicode code point is read.
+bool ReadCodePoint(StringPiece str, int index, uint32_t* cp,
+                   int* num_left, int* num_read) {
+  if (*num_left == 0) {
+    // Last read was complete. Start reading a new unicode code point.
+    *cp = static_cast<uint8_t>(str[index++]);
+    *num_read = 1;
+    // The length of the code point is determined from reading the first byte.
+    //
+    // If the first byte is between:
+    //    0..0x7f: that's the value of the code point.
+    // 0x80..0xbf: <invalid>
+    // 0xc0..0xdf: 11-bit code point encoded in 2 bytes.
+    //                                   bit 10-6, bit 5-0
+    // 0xe0..0xef: 16-bit code point encoded in 3 bytes.
+    //                        bit 15-12, bit 11-6, bit 5-0
+    // 0xf0..0xf7: 21-bit code point encoded in 4 bytes.
+    //             bit 20-18, bit 17-12, bit 11-6, bit 5-0
+    // 0xf8..0xff: <invalid>
+    //
+    // Meaning of each bit:
+    // <msb> bit 7: 0 - single byte code point: bits 6-0 are values.
+    //              1 - multibyte code point
+    //       bit 6: 0 - subsequent bytes of multibyte code point:
+    //                  bits 5-0 are values.
+    //              1 - first byte of multibyte code point
+    //       bit 5: 0 - first byte of 2-byte code point: bits 4-0 are values.
+    //              1 - first byte of code point with >= 3 bytes.
+    //       bit 4: 0 - first byte of 3-byte code point: bits 3-0 are values.
+    //              1 - first byte of code point with >= 4 bytes.
+    //       bit 3: 0 - first byte of 4-byte code point: bits 2-0 are values.
+    //              1 - reserved for future expansion.
+    if (*cp <= 0x7f) {
+      return true;
+    } else if (*cp <= 0xbf) {
+      return false;
+    } else if (*cp <= 0xdf) {
+      *cp &= 0x1f;
+      *num_left = 1;
+    } else if (*cp <= 0xef) {
+      *cp &= 0x0f;
+      *num_left = 2;
+    } else if (*cp <= 0xf7) {
+      *cp &= 0x07;
+      *num_left = 3;
+    } else {
+      return false;
+    }
+  } else {
+    // Last read was partial. Initialize num_read to 0 and continue reading
+    // the last unicode code point.
+    *num_read = 0;
+  }
+  while (*num_left > 0 && index < static_cast<int>(str.size())) {
+    uint32_t ch = static_cast<uint8_t>(str[index++]);
+    --(*num_left);
+    ++(*num_read);
+    *cp = (*cp << 6) | (ch & 0x3f);
+    if (ch < 0x80 || ch > 0xbf) return false;
+  }
+  return *num_left > 0 || (!IsSurrogate(*cp) && IsValidCodePoint(*cp));
+}
+
+// Stores the 16-bit unicode code point as its hexadecimal digits in buffer
+// and returns a StringPiece that points to this buffer. The input buffer needs
+// to be at least 6 bytes long.
+StringPiece ToHex(uint16_t cp, char* buffer) {
+  buffer[5] = kHex[cp & 0x0f];
+  cp >>= 4;
+  buffer[4] = kHex[cp & 0x0f];
+  cp >>= 4;
+  buffer[3] = kHex[cp & 0x0f];
+  cp >>= 4;
+  buffer[2] = kHex[cp & 0x0f];
+  return StringPiece(buffer, 6);
+}
+
+// Stores the 32-bit unicode code point as its hexadecimal digits in buffer
+// and returns a StringPiece that points to this buffer. The input buffer needs
+// to be at least 12 bytes long.
+StringPiece ToSurrogateHex(uint32_t cp, char* buffer) {
+  uint16_t low = ToLowSurrogate(cp);
+  uint16_t high = ToHighSurrogate(cp);
+
+  buffer[11] = kHex[low & 0x0f];
+  low >>= 4;
+  buffer[10] = kHex[low & 0x0f];
+  low >>= 4;
+  buffer[9] = kHex[low & 0x0f];
+  low >>= 4;
+  buffer[8] = kHex[low & 0x0f];
+
+  buffer[5] = kHex[high & 0x0f];
+  high >>= 4;
+  buffer[4] = kHex[high & 0x0f];
+  high >>= 4;
+  buffer[3] = kHex[high & 0x0f];
+  high >>= 4;
+  buffer[2] = kHex[high & 0x0f];
+
+  return StringPiece(buffer, 12);
+}
+
+// If the given unicode code point needs escaping, then returns the
+// escaped form. The returned StringPiece either points to statically
+// pre-allocated char[] or to the given buffer. The input buffer needs
+// to be at least 12 bytes long.
+//
+// If the given unicode code point does not need escaping, an empty
+// StringPiece is returned.
+StringPiece EscapeCodePoint(uint32_t cp, char* buffer) {
+  if (cp < 0xa0) return kCommonEscapes[cp];
+  switch (cp) {
+    // These are not required by json spec
+    // but used to prevent security bugs in javascript.
+    case 0xfeff:  // Zero width no-break space
+    case 0xfff9:  // Interlinear annotation anchor
+    case 0xfffa:  // Interlinear annotation separator
+    case 0xfffb:  // Interlinear annotation terminator
+
+    case 0x00ad:  // Soft-hyphen
+    case 0x06dd:  // Arabic end of ayah
+    case 0x070f:  // Syriac abbreviation mark
+    case 0x17b4:  // Khmer vowel inherent Aq
+    case 0x17b5:  // Khmer vowel inherent Aa
+      return ToHex(cp, buffer);
+
+    default:
+      if ((cp >= 0x0600 && cp <= 0x0603) ||  // Arabic signs
+          (cp >= 0x200b && cp <= 0x200f) ||  // Zero width etc.
+          (cp >= 0x2028 && cp <= 0x202e) ||  // Separators etc.
+          (cp >= 0x2060 && cp <= 0x2064) ||  // Invisible etc.
+          (cp >= 0x206a && cp <= 0x206f)) {  // Shaping etc.
+        return ToHex(cp, buffer);
+      }
+
+      if (cp == 0x000e0001 ||                        // Language tag
+          (cp >= 0x0001d173 && cp <= 0x0001d17a) ||  // Music formatting
+          (cp >= 0x000e0020 && cp <= 0x000e007f)) {  // TAG symbols
+        return ToSurrogateHex(cp, buffer);
+      }
+  }
+  return StringPiece();
+}
+
+// Tries to escape the given code point first. If the given code point
+// does not need to be escaped, but force_output is true, then render
+// the given multi-byte code point in UTF8 in the buffer and returns it.
+StringPiece EscapeCodePoint(uint32_t cp, char* buffer,
+                                  bool force_output) {
+  StringPiece sp = EscapeCodePoint(cp, buffer);
+  if (force_output && sp.empty()) {
+    buffer[5] = (cp & 0x3f) | 0x80;
+    cp >>= 6;
+    if (cp <= 0x1f) {
+      buffer[4] = cp | 0xc0;
+      sp = StringPiece(buffer + 4, 2);
+      return sp;
+    }
+    buffer[4] = (cp & 0x3f) | 0x80;
+    cp >>= 6;
+    if (cp <= 0x0f) {
+      buffer[3] = cp | 0xe0;
+      sp = StringPiece(buffer + 3, 3);
+      return sp;
+    }
+    buffer[3] = (cp & 0x3f) | 0x80;
+    buffer[2] = ((cp >> 6) & 0x07) | 0xf0;
+    sp = StringPiece(buffer + 2, 4);
+  }
+  return sp;
+}
+
+}  // namespace
+
+void JsonEscaping::Escape(strings::ByteSource* input,
+                          strings::ByteSink* output) {
+  char buffer[12] = "\\udead\\ubee";
+  uint32_t cp = 0;   // Current unicode code point.
+  int num_left = 0;  // Num of chars to read to complete the code point.
+  while (input->Available() > 0) {
+    StringPiece str = input->Peek();
+    StringPiece escaped;
+    size_t i = 0;
+    int num_read;
+    bool ok;
+    bool cp_was_split = num_left > 0;
+    // Loop until we encounter either
+    //   i) a code point that needs to be escaped; or
+    //  ii) a split code point is completely read; or
+    // iii) a character that is not a valid utf8; or
+    //  iv) end of the StringPiece str is reached.
+    do {
+      ok = ReadCodePoint(str, i, &cp, &num_left, &num_read);
+      if (num_left > 0 || !ok) break;  // case iii or iv
+      escaped = EscapeCodePoint(cp, buffer, cp_was_split);
+      if (!escaped.empty()) break;  // case i or ii
+      i += num_read;
+      num_read = 0;
+    } while (i < str.length());  // case iv
+    // First copy the un-escaped prefix, if any, to the output ByteSink.
+    if (i > 0) input->CopyTo(output, i);
+    if (num_read > 0) input->Skip(num_read);
+    if (!ok) {
+      // Case iii: Report error.
+      // TODO(wpoon): Add error reporting.
+      num_left = 0;
+    } else if (num_left == 0 && !escaped.empty()) {
+      // Case i or ii: Append the escaped code point to the output ByteSink.
+      output->Append(escaped.data(), escaped.size());
+    }
+  }
+  if (num_left > 0) {
+    // Treat as case iii: report error.
+    // TODO(wpoon): Add error reporting.
+  }
+}
+
+void JsonEscaping::Escape(StringPiece input, strings::ByteSink* output) {
+  const size_t len = input.length();
+  const char* p = input.data();
+
+  bool can_skip_escaping = true;
+  for (size_t i = 0; i < len; i++) {
+    char c = p[i];
+    if (c < 0x20 || c >= 0x7F || c == '"' || c == '<' || c == '>' ||
+        c == '\\') {
+      can_skip_escaping = false;
+      break;
+    }
+  }
+
+  if (can_skip_escaping) {
+    output->Append(input.data(), input.length());
+  } else {
+    strings::ArrayByteSource source(input);
+    Escape(&source, output);
+  }
+}
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/json_objectwriter.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/json_objectwriter.cpp
new file mode 100644
index 0000000..1a86f00
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/json_objectwriter.cpp
@@ -0,0 +1,190 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/json_objectwriter.h>
+
+#include <cmath>
+#include <cstdint>
+#include <limits>
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/json_escaping.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+
+JsonObjectWriter::~JsonObjectWriter() {
+  if (element_ && !element_->is_root()) {
+    GOOGLE_LOG(WARNING) << "JsonObjectWriter was not fully closed.";
+  }
+}
+
+JsonObjectWriter* JsonObjectWriter::StartObject(StringPiece name) {
+  WritePrefix(name);
+  WriteChar('{');
+  PushObject();
+  return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::EndObject() {
+  Pop();
+  WriteChar('}');
+  if (element() && element()->is_root()) NewLine();
+  return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::StartList(StringPiece name) {
+  WritePrefix(name);
+  WriteChar('[');
+  PushArray();
+  return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::EndList() {
+  Pop();
+  WriteChar(']');
+  if (element()->is_root()) NewLine();
+  return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderBool(StringPiece name,
+                                               bool value) {
+  return RenderSimple(name, value ? "true" : "false");
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderInt32(StringPiece name,
+                                                int32_t value) {
+  return RenderSimple(name, StrCat(value));
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderUint32(StringPiece name,
+                                                 uint32_t value) {
+  return RenderSimple(name, StrCat(value));
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderInt64(StringPiece name,
+                                                int64_t value) {
+  WritePrefix(name);
+  WriteChar('"');
+  WriteRawString(StrCat(value));
+  WriteChar('"');
+  return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderUint64(StringPiece name,
+                                                 uint64_t value) {
+  WritePrefix(name);
+  WriteChar('"');
+  WriteRawString(StrCat(value));
+  WriteChar('"');
+  return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderDouble(StringPiece name,
+                                                 double value) {
+  if (std::isfinite(value)) {
+    return RenderSimple(name, SimpleDtoa(value));
+  }
+
+  // Render quoted with NaN/Infinity-aware DoubleAsString.
+  return RenderString(name, DoubleAsString(value));
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderFloat(StringPiece name,
+                                                float value) {
+  if (std::isfinite(value)) {
+    return RenderSimple(name, SimpleFtoa(value));
+  }
+
+  // Render quoted with NaN/Infinity-aware FloatAsString.
+  return RenderString(name, FloatAsString(value));
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderString(StringPiece name,
+                                                 StringPiece value) {
+  WritePrefix(name);
+  WriteChar('"');
+  JsonEscaping::Escape(value, &sink_);
+  WriteChar('"');
+  return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderBytes(StringPiece name,
+                                                StringPiece value) {
+  WritePrefix(name);
+  std::string base64;
+
+  if (use_websafe_base64_for_bytes_)
+    WebSafeBase64EscapeWithPadding(std::string(value), &base64);
+  else
+    Base64Escape(value, &base64);
+
+  WriteChar('"');
+  // TODO(wpoon): Consider a ByteSink solution that writes the base64 bytes
+  //              directly to the stream, rather than first putting them
+  //              into a string and then writing them to the stream.
+  stream_->WriteRaw(base64.data(), base64.size());
+  WriteChar('"');
+  return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderNull(StringPiece name) {
+  return RenderSimple(name, "null");
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderNullAsEmpty(StringPiece name) {
+  return RenderSimple(name, "");
+}
+
+void JsonObjectWriter::WritePrefix(StringPiece name) {
+  bool not_first = !element()->is_first();
+  if (not_first) WriteChar(',');
+  if (not_first || !element()->is_root()) NewLine();
+  if (!name.empty() || element()->is_json_object()) {
+    WriteChar('"');
+    if (!name.empty()) {
+      JsonEscaping::Escape(name, &sink_);
+    }
+    WriteRawString("\":");
+    if (!indent_string_.empty()) WriteChar(' ');
+  }
+}
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/json_stream_parser.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/json_stream_parser.cpp
new file mode 100644
index 0000000..e786781
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/json_stream_parser.cpp
@@ -0,0 +1,995 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/json_stream_parser.h>
+
+#include <algorithm>
+#include <cctype>
+#include <cmath>
+#include <memory>
+#include <stack>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/util/internal/object_writer.h>
+#include <google/protobuf/util/internal/json_escaping.h>
+
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+namespace converter {
+
+// Number of digits in an escaped UTF-16 code unit ('\\' 'u' X X X X)
+static const int kUnicodeEscapedLength = 6;
+
+static const int kDefaultMaxRecursionDepth = 100;
+
+// These cannot be constexpr for portability with VS2015.
+static const StringPiece kKeywordTrue = "true";
+static const StringPiece kKeywordFalse = "false";
+static const StringPiece kKeywordNull = "null";
+
+inline bool IsLetter(char c) {
+  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (c == '_') ||
+         (c == '$');
+}
+
+inline bool IsAlphanumeric(char c) {
+  return IsLetter(c) || ('0' <= c && c <= '9');
+}
+
+// Indicates a character may not be part of an unquoted key.
+inline bool IsKeySeparator(char c) {
+  return (ascii_isspace(c) || c == '"' || c == '\'' || c == '{' ||
+          c == '}' || c == '[' || c == ']' || c == ':' || c == ',');
+}
+
+inline void ReplaceInvalidCodePoints(StringPiece str,
+                                     const std::string& replacement,
+                                     std::string* dst) {
+  while (!str.empty()) {
+    size_t n_valid_bytes = internal::UTF8SpnStructurallyValid(str);
+    StringPiece valid_part = str.substr(0, n_valid_bytes);
+    StrAppend(dst, valid_part);
+
+    if (n_valid_bytes == str.size()) {
+      break;
+    }
+
+    // Append replacement value.
+    StrAppend(dst, replacement);
+
+    // Move past valid bytes + one invalid byte.
+    str.remove_prefix(n_valid_bytes + 1);
+  }
+}
+
+static bool ConsumeKey(StringPiece* input, StringPiece* key) {
+  if (input->empty() || !IsLetter((*input)[0])) return false;
+  size_t len = 1;
+  for (; len < input->size(); ++len) {
+    if (!IsAlphanumeric((*input)[len])) {
+      break;
+    }
+  }
+  *key = StringPiece(input->data(), len);
+  *input = StringPiece(input->data() + len, input->size() - len);
+  return true;
+}
+
+// Same as 'ConsumeKey', but allows a widened set of key characters.
+static bool ConsumeKeyPermissive(StringPiece* input,
+                                 StringPiece* key) {
+  if (input->empty() || !IsLetter((*input)[0])) return false;
+  size_t len = 1;
+  for (; len < input->size(); ++len) {
+    if (IsKeySeparator((*input)[len])) {
+      break;
+    }
+  }
+  *key = StringPiece(input->data(), len);
+  *input = StringPiece(input->data() + len, input->size() - len);
+  return true;
+}
+
+static bool MatchKey(StringPiece input) {
+  return !input.empty() && IsLetter(input[0]);
+}
+
+JsonStreamParser::JsonStreamParser(ObjectWriter* ow)
+    : ow_(ow),
+      stack_(),
+      leftover_(),
+      json_(),
+      p_(),
+      key_(),
+      key_storage_(),
+      finishing_(false),
+      seen_non_whitespace_(false),
+      allow_no_root_element_(false),
+      parsed_(),
+      parsed_storage_(),
+      string_open_(0),
+      chunk_storage_(),
+      coerce_to_utf8_(false),
+      utf8_replacement_character_(" "),
+      allow_empty_null_(false),
+      allow_permissive_key_naming_(false),
+      loose_float_number_conversion_(false),
+      recursion_depth_(0),
+      max_recursion_depth_(kDefaultMaxRecursionDepth) {
+  // Initialize the stack with a single value to be parsed.
+  stack_.push(VALUE);
+}
+
+JsonStreamParser::~JsonStreamParser() {}
+
+
+util::Status JsonStreamParser::Parse(StringPiece json) {
+  StringPiece chunk = json;
+  // If we have leftovers from a previous chunk, append the new chunk to it
+  // and create a new StringPiece pointing at the string's data. This could
+  // be large but we rely on the chunks to be small, assuming they are
+  // fragments of a Cord.
+  if (!leftover_.empty()) {
+    // Don't point chunk to leftover_ because leftover_ will be updated in
+    // ParseChunk(chunk).
+    chunk_storage_.swap(leftover_);
+    StrAppend(&chunk_storage_, json);
+    chunk = StringPiece(chunk_storage_);
+  }
+
+  // Find the structurally valid UTF8 prefix and parse only that.
+  int n = internal::UTF8SpnStructurallyValid(chunk);
+  if (n > 0) {
+    util::Status status = ParseChunk(chunk.substr(0, n));
+
+    // Any leftover characters are stashed in leftover_ for later parsing when
+    // there is more data available.
+    StrAppend(&leftover_, chunk.substr(n));
+    return status;
+  } else {
+    leftover_.assign(chunk.data(), chunk.size());
+    return util::Status();
+  }
+}
+
+util::Status JsonStreamParser::FinishParse() {
+  // If we do not expect anything and there is nothing left to parse we're all
+  // done.
+  if (stack_.empty() && leftover_.empty()) {
+    return util::Status();
+  }
+
+  // Lifetime needs to last until RunParser returns, so keep this variable
+  // outside of the coerce_to_utf8 block.
+  std::unique_ptr<std::string> scratch;
+
+  bool is_valid_utf8 = internal::IsStructurallyValidUTF8(leftover_);
+  if (coerce_to_utf8_ && !is_valid_utf8) {
+    scratch.reset(new std::string);
+    scratch->reserve(leftover_.size() * utf8_replacement_character_.size());
+    ReplaceInvalidCodePoints(leftover_, utf8_replacement_character_,
+                             scratch.get());
+    p_ = json_ = *scratch;
+  } else {
+    p_ = json_ = leftover_;
+    if (!is_valid_utf8) {
+      return ReportFailure("Encountered non UTF-8 code points.",
+                           ParseErrorType::NON_UTF_8);
+    }
+  }
+
+  // Parse the remainder in finishing mode, which reports errors for things like
+  // unterminated strings or unknown tokens that would normally be retried.
+  finishing_ = true;
+  util::Status result = RunParser();
+  if (result.ok()) {
+    SkipWhitespace();
+    if (!p_.empty()) {
+      result =
+          ReportFailure("Parsing terminated before end of input.",
+                        ParseErrorType::PARSING_TERMINATED_BEFORE_END_OF_INPUT);
+    }
+  }
+  return result;
+}
+
+util::Status JsonStreamParser::ParseChunk(StringPiece chunk) {
+  // Do not do any work if the chunk is empty.
+  if (chunk.empty()) return util::Status();
+
+  p_ = json_ = chunk;
+
+  finishing_ = false;
+  util::Status result = RunParser();
+  if (!result.ok()) return result;
+
+  SkipWhitespace();
+  if (p_.empty()) {
+    // If we parsed everything we had, clear the leftover.
+    leftover_.clear();
+  } else {
+    // If we do not expect anything i.e. stack is empty, and we have non-empty
+    // string left to parse, we report an error.
+    if (stack_.empty()) {
+      return ReportFailure(
+          "Parsing terminated before end of input.",
+          ParseErrorType::PARSING_TERMINATED_BEFORE_END_OF_INPUT);
+    }
+    // If we expect future data i.e. stack is non-empty, and we have some
+    // unparsed data left, we save it for later parse.
+    leftover_ = std::string(p_);
+  }
+  return util::Status();
+}
+
+bool JsonStreamParser::IsInputAllWhiteSpaces(TokenType type) {
+  // Conclude the whole input is full of white spaces by:
+  // - it is at the finishing stage
+  // - we have run out of the input data
+  // - haven't seen non-whitespace char so far
+  if (finishing_ && p_.empty() && type == UNKNOWN && !seen_non_whitespace_) {
+    return true;
+  }
+  return false;
+}
+
+util::Status JsonStreamParser::RunParser() {
+  while (!stack_.empty()) {
+    ParseType type = stack_.top();
+    TokenType t = (string_open_ == 0) ? GetNextTokenType() : BEGIN_STRING;
+    stack_.pop();
+    util::Status result;
+    switch (type) {
+      case VALUE:
+        if (allow_no_root_element_ && IsInputAllWhiteSpaces(t)) {
+          return util::Status();
+        }
+        result = ParseValue(t);
+        break;
+
+      case OBJ_MID:
+        result = ParseObjectMid(t);
+        break;
+
+      case ENTRY:
+        result = ParseEntry(t);
+        break;
+
+      case ENTRY_MID:
+        result = ParseEntryMid(t);
+        break;
+
+      case ARRAY_VALUE:
+        result = ParseArrayValue(t);
+        break;
+
+      case ARRAY_MID:
+        result = ParseArrayMid(t);
+        break;
+
+      default:
+        result =
+            util::InternalError(StrCat("Unknown parse type: ", type));
+        break;
+    }
+    if (!result.ok()) {
+      // If we were cancelled, save our state and try again later.
+      if (!finishing_ && util::IsCancelled(result)) {
+        stack_.push(type);
+        // If we have a key we still need to render, make sure to save off the
+        // contents in our own storage.
+        if (!key_.empty() && key_storage_.empty()) {
+          StrAppend(&key_storage_, key_);
+          key_ = StringPiece(key_storage_);
+        }
+        result = util::Status();
+      }
+      return result;
+    }
+  }
+  return util::Status();
+}
+
+util::Status JsonStreamParser::ParseValue(TokenType type) {
+  switch (type) {
+    case BEGIN_OBJECT:
+      return HandleBeginObject();
+    case BEGIN_ARRAY:
+      return HandleBeginArray();
+    case BEGIN_STRING:
+      return ParseString();
+    case BEGIN_NUMBER:
+      return ParseNumber();
+    case BEGIN_TRUE:
+      return ParseTrue();
+    case BEGIN_FALSE:
+      return ParseFalse();
+    case BEGIN_NULL:
+      return ParseNull();
+    case UNKNOWN:
+      return ReportUnknown("Expected a value.", ParseErrorType::EXPECTED_VALUE);
+    default: {
+      // Special case for having been cut off while parsing, wait for more data.
+      // This handles things like 'fals' being at the end of the string, we
+      // don't know if the next char would be e, completing it, or something
+      // else, making it invalid.
+      if (!finishing_ && p_.length() < kKeywordFalse.length()) {
+        return util::CancelledError("");
+      }
+
+      if (allow_empty_null_ && IsEmptyNullAllowed(type)) {
+        return ParseEmptyNull();
+      }
+      return ReportFailure("Unexpected token.",
+                           ParseErrorType::UNEXPECTED_TOKEN);
+    }
+  }
+}
+
+util::Status JsonStreamParser::ParseString() {
+  util::Status result = ParseStringHelper();
+  if (result.ok()) {
+    ow_->RenderString(key_, parsed_);
+    key_ = StringPiece();
+    parsed_ = StringPiece();
+    parsed_storage_.clear();
+  }
+  return result;
+}
+
+util::Status JsonStreamParser::ParseStringHelper() {
+  // If we haven't seen the start quote, grab it and remember it for later.
+  if (string_open_ == 0) {
+    string_open_ = *p_.data();
+    GOOGLE_DCHECK(string_open_ == '\"' || string_open_ == '\'');
+    Advance();
+  }
+  // Track where we last copied data from so we can minimize copying.
+  const char* last = p_.data();
+  while (!p_.empty()) {
+    const char* data = p_.data();
+    if (*data == '\\') {
+      // We're about to handle an escape, copy all bytes from last to data.
+      if (last < data) {
+        parsed_storage_.append(last, data - last);
+      }
+      // If we ran out of string after the \, cancel or report an error
+      // depending on if we expect more data later.
+      if (p_.length() == 1) {
+        if (!finishing_) {
+          return util::CancelledError("");
+        }
+        return ReportFailure("Closing quote expected in string.",
+                             ParseErrorType::EXPECTED_CLOSING_QUOTE);
+      }
+      // Parse a unicode escape if we found \u in the string.
+      if (data[1] == 'u') {
+        util::Status result = ParseUnicodeEscape();
+        if (!result.ok()) {
+          return result;
+        }
+        // Move last pointer past the unicode escape and continue.
+        last = p_.data();
+        continue;
+      }
+      // Handle the standard set of backslash-escaped characters.
+      switch (data[1]) {
+        case 'b':
+          parsed_storage_.push_back('\b');
+          break;
+        case 'f':
+          parsed_storage_.push_back('\f');
+          break;
+        case 'n':
+          parsed_storage_.push_back('\n');
+          break;
+        case 'r':
+          parsed_storage_.push_back('\r');
+          break;
+        case 't':
+          parsed_storage_.push_back('\t');
+          break;
+        case 'v':
+          parsed_storage_.push_back('\v');
+          break;
+        default:
+          parsed_storage_.push_back(data[1]);
+      }
+      // We handled two characters, so advance past them and continue.
+      p_.remove_prefix(2);
+      last = p_.data();
+      continue;
+    }
+    // If we found the closing quote note it, advance past it, and return.
+    if (*data == string_open_) {
+      // If we didn't copy anything, reuse the input buffer.
+      if (parsed_storage_.empty()) {
+        parsed_ = StringPiece(last, data - last);
+      } else {
+        if (last < data) {
+          parsed_storage_.append(last, data - last);
+        }
+        parsed_ = StringPiece(parsed_storage_);
+      }
+      // Clear the quote char so next time we try to parse a string we'll
+      // start fresh.
+      string_open_ = 0;
+      Advance();
+      return util::Status();
+    }
+    // Normal character, just advance past it.
+    Advance();
+  }
+  // If we ran out of characters, copy over what we have so far.
+  if (last < p_.data()) {
+    parsed_storage_.append(last, p_.data() - last);
+  }
+  // If we didn't find the closing quote but we expect more data, cancel for now
+  if (!finishing_) {
+    return util::CancelledError("");
+  }
+  // End of string reached without a closing quote, report an error.
+  string_open_ = 0;
+  return ReportFailure("Closing quote expected in string.",
+                       ParseErrorType::EXPECTED_CLOSING_QUOTE);
+}
+
+// Converts a unicode escaped character to a decimal value stored in a char32
+// for use in UTF8 encoding utility.  We assume that str begins with \uhhhh and
+// convert that from the hex number to a decimal value.
+//
+// There are some security exploits with UTF-8 that we should be careful of:
+//   - http://www.unicode.org/reports/tr36/#UTF-8_Exploit
+//   - http://sites/intl-eng/design-guide/core-application
+util::Status JsonStreamParser::ParseUnicodeEscape() {
+  if (p_.length() < kUnicodeEscapedLength) {
+    if (!finishing_) {
+      return util::CancelledError("");
+    }
+    return ReportFailure("Illegal hex string.",
+                         ParseErrorType::ILLEGAL_HEX_STRING);
+  }
+  GOOGLE_DCHECK_EQ('\\', p_.data()[0]);
+  GOOGLE_DCHECK_EQ('u', p_.data()[1]);
+  uint32_t code = 0;
+  for (int i = 2; i < kUnicodeEscapedLength; ++i) {
+    if (!isxdigit(p_.data()[i])) {
+      return ReportFailure("Invalid escape sequence.",
+                           ParseErrorType::INVALID_ESCAPE_SEQUENCE);
+    }
+    code = (code << 4) + hex_digit_to_int(p_.data()[i]);
+  }
+  if (code >= JsonEscaping::kMinHighSurrogate &&
+      code <= JsonEscaping::kMaxHighSurrogate) {
+    if (p_.length() < 2 * kUnicodeEscapedLength) {
+      if (!finishing_) {
+        return util::CancelledError("");
+      }
+      if (!coerce_to_utf8_) {
+        return ReportFailure("Missing low surrogate.",
+                             ParseErrorType::MISSING_LOW_SURROGATE);
+      }
+    } else if (p_.data()[kUnicodeEscapedLength] == '\\' &&
+               p_.data()[kUnicodeEscapedLength + 1] == 'u') {
+      uint32_t low_code = 0;
+      for (int i = kUnicodeEscapedLength + 2; i < 2 * kUnicodeEscapedLength;
+           ++i) {
+        if (!isxdigit(p_.data()[i])) {
+          return ReportFailure("Invalid escape sequence.",
+                               ParseErrorType::INVALID_ESCAPE_SEQUENCE);
+        }
+        low_code = (low_code << 4) + hex_digit_to_int(p_.data()[i]);
+      }
+      if (low_code >= JsonEscaping::kMinLowSurrogate &&
+          low_code <= JsonEscaping::kMaxLowSurrogate) {
+        // Convert UTF-16 surrogate pair to 21-bit Unicode codepoint.
+        code = (((code & 0x3FF) << 10) | (low_code & 0x3FF)) +
+               JsonEscaping::kMinSupplementaryCodePoint;
+        // Advance past the first code unit escape.
+        p_.remove_prefix(kUnicodeEscapedLength);
+      } else if (!coerce_to_utf8_) {
+        return ReportFailure("Invalid low surrogate.",
+                             ParseErrorType::INVALID_LOW_SURROGATE);
+      }
+    } else if (!coerce_to_utf8_) {
+      return ReportFailure("Missing low surrogate.",
+                           ParseErrorType::MISSING_LOW_SURROGATE);
+    }
+  }
+  if (!coerce_to_utf8_ && !IsValidCodePoint(code)) {
+    return ReportFailure("Invalid unicode code point.",
+                         ParseErrorType::INVALID_UNICODE);
+  }
+  char buf[UTFmax];
+  int len = EncodeAsUTF8Char(code, buf);
+  // Advance past the [final] code unit escape.
+  p_.remove_prefix(kUnicodeEscapedLength);
+  parsed_storage_.append(buf, len);
+  return util::Status();
+}
+
+util::Status JsonStreamParser::ParseNumber() {
+  NumberResult number;
+  util::Status result = ParseNumberHelper(&number);
+  if (result.ok()) {
+    switch (number.type) {
+      case NumberResult::DOUBLE:
+        ow_->RenderDouble(key_, number.double_val);
+        key_ = StringPiece();
+        break;
+
+      case NumberResult::INT:
+        ow_->RenderInt64(key_, number.int_val);
+        key_ = StringPiece();
+        break;
+
+      case NumberResult::UINT:
+        ow_->RenderUint64(key_, number.uint_val);
+        key_ = StringPiece();
+        break;
+
+      default:
+        return ReportFailure("Unable to parse number.",
+                             ParseErrorType::UNABLE_TO_PARSE_NUMBER);
+    }
+  }
+  return result;
+}
+
+util::Status JsonStreamParser::ParseDoubleHelper(const std::string& number,
+                                                 NumberResult* result) {
+  if (!safe_strtod(number, &result->double_val)) {
+    return ReportFailure("Unable to parse number.",
+                         ParseErrorType::UNABLE_TO_PARSE_NUMBER);
+  }
+  if (!loose_float_number_conversion_ && !std::isfinite(result->double_val)) {
+    return ReportFailure("Number exceeds the range of double.",
+                         ParseErrorType::NUMBER_EXCEEDS_RANGE_DOUBLE);
+  }
+  result->type = NumberResult::DOUBLE;
+  return util::Status();
+}
+
+util::Status JsonStreamParser::ParseNumberHelper(NumberResult* result) {
+  const char* data = p_.data();
+  int length = p_.length();
+
+  // Look for the first non-numeric character, or the end of the string.
+  int index = 0;
+  bool floating = false;
+  bool negative = data[index] == '-';
+  // Find the first character that cannot be part of the number. Along the way
+  // detect if the number needs to be parsed as a double.
+  // Note that this restricts numbers to the JSON specification, so for example
+  // we do not support hex or octal notations.
+  for (; index < length; ++index) {
+    char c = data[index];
+    if (isdigit(c)) continue;
+    if (c == '.' || c == 'e' || c == 'E') {
+      floating = true;
+      continue;
+    }
+    if (c == '+' || c == '-' || c == 'x') continue;
+    // Not a valid number character, break out.
+    break;
+  }
+
+  // If the entire input is a valid number, and we may have more content in the
+  // future, we abort for now and resume when we know more.
+  if (index == length && !finishing_) {
+    return util::CancelledError("");
+  }
+
+  // Create a string containing just the number, so we can use safe_strtoX
+  std::string number = std::string(p_.substr(0, index));
+
+  // Floating point number, parse as a double.
+  if (floating) {
+    util::Status status = ParseDoubleHelper(number, result);
+    if (status.ok()) {
+      p_.remove_prefix(index);
+    }
+    return status;
+  }
+
+  // Positive non-floating point number, parse as a uint64_t.
+  if (!negative) {
+    // Octal/Hex numbers are not valid JSON values.
+    if (number.length() >= 2 && number[0] == '0') {
+      return ReportFailure(
+          "Octal/hex numbers are not valid JSON values.",
+          ParseErrorType::OCTAL_OR_HEX_ARE_NOT_VALID_JSON_VALUES);
+    }
+    if (safe_strtou64(number, &result->uint_val)) {
+      result->type = NumberResult::UINT;
+      p_.remove_prefix(index);
+      return util::Status();
+    } else {
+      // If the value is too large, parse it as double.
+      util::Status status = ParseDoubleHelper(number, result);
+      if (status.ok()) {
+        p_.remove_prefix(index);
+      }
+      return status;
+    }
+  }
+
+  // Octal/Hex numbers are not valid JSON values.
+  if (number.length() >= 3 && number[1] == '0') {
+    return ReportFailure(
+        "Octal/hex numbers are not valid JSON values.",
+        ParseErrorType::OCTAL_OR_HEX_ARE_NOT_VALID_JSON_VALUES);
+  }
+  // Negative non-floating point number, parse as an int64_t.
+  if (safe_strto64(number, &result->int_val)) {
+    result->type = NumberResult::INT;
+    p_.remove_prefix(index);
+    return util::Status();
+  } else {
+    // If the value is too large, parse it as double.
+    util::Status status = ParseDoubleHelper(number, result);
+    if (status.ok()) {
+      p_.remove_prefix(index);
+    }
+    return status;
+  }
+}
+
+util::Status JsonStreamParser::HandleBeginObject() {
+  GOOGLE_DCHECK_EQ('{', *p_.data());
+  Advance();
+  ow_->StartObject(key_);
+  auto status = IncrementRecursionDepth(key_);
+  if (!status.ok()) {
+    return status;
+  }
+  key_ = StringPiece();
+  stack_.push(ENTRY);
+  return util::Status();
+}
+
+util::Status JsonStreamParser::ParseObjectMid(TokenType type) {
+  if (type == UNKNOWN) {
+    return ReportUnknown("Expected , or } after key:value pair.",
+                         ParseErrorType::EXPECTED_COMMA_OR_BRACES);
+  }
+
+  // Object is complete, advance past the comma and render the EndObject.
+  if (type == END_OBJECT) {
+    Advance();
+    ow_->EndObject();
+    --recursion_depth_;
+    return util::Status();
+  }
+  // Found a comma, advance past it and get ready for an entry.
+  if (type == VALUE_SEPARATOR) {
+    Advance();
+    stack_.push(ENTRY);
+    return util::Status();
+  }
+  // Illegal token after key:value pair.
+  return ReportFailure("Expected , or } after key:value pair.",
+                       ParseErrorType::EXPECTED_COMMA_OR_BRACES);
+}
+
+util::Status JsonStreamParser::ParseEntry(TokenType type) {
+  if (type == UNKNOWN) {
+    return ReportUnknown("Expected an object key or }.",
+                         ParseErrorType::EXPECTED_OBJECT_KEY_OR_BRACES);
+  }
+
+  // Close the object and return. This allows for trailing commas.
+  if (type == END_OBJECT) {
+    ow_->EndObject();
+    Advance();
+    --recursion_depth_;
+    return util::Status();
+  }
+
+  util::Status result;
+  if (type == BEGIN_STRING) {
+    // Key is a string (standard JSON), parse it and store the string.
+    result = ParseStringHelper();
+    if (result.ok()) {
+      key_storage_.clear();
+      if (!parsed_storage_.empty()) {
+        parsed_storage_.swap(key_storage_);
+        key_ = StringPiece(key_storage_);
+      } else {
+        key_ = parsed_;
+      }
+      parsed_ = StringPiece();
+    }
+  } else if (type == BEGIN_KEY) {
+    // Key is a bare key (back compat), create a StringPiece pointing to it.
+    result = ParseKey();
+  } else if (type == BEGIN_NULL || type == BEGIN_TRUE || type == BEGIN_FALSE) {
+    // Key may be a bare key that begins with a reserved word.
+    result = ParseKey();
+    if (result.ok() && (key_ == kKeywordNull || key_ == kKeywordTrue ||
+                        key_ == kKeywordFalse)) {
+      result = ReportFailure("Expected an object key or }.",
+                             ParseErrorType::EXPECTED_OBJECT_KEY_OR_BRACES);
+    }
+  } else {
+    // Unknown key type, report an error.
+    result = ReportFailure("Expected an object key or }.",
+                           ParseErrorType::EXPECTED_OBJECT_KEY_OR_BRACES);
+  }
+  // On success we next expect an entry mid ':' then an object mid ',' or '}'
+  if (result.ok()) {
+    stack_.push(OBJ_MID);
+    stack_.push(ENTRY_MID);
+  }
+  return result;
+}
+
+util::Status JsonStreamParser::ParseEntryMid(TokenType type) {
+  if (type == UNKNOWN) {
+    return ReportUnknown("Expected : between key:value pair.",
+                         ParseErrorType::EXPECTED_COLON);
+  }
+  if (type == ENTRY_SEPARATOR) {
+    Advance();
+    stack_.push(VALUE);
+    return util::Status();
+  }
+  return ReportFailure("Expected : between key:value pair.",
+                       ParseErrorType::EXPECTED_COLON);
+}
+
+util::Status JsonStreamParser::HandleBeginArray() {
+  GOOGLE_DCHECK_EQ('[', *p_.data());
+  Advance();
+  ow_->StartList(key_);
+  key_ = StringPiece();
+  stack_.push(ARRAY_VALUE);
+  return util::Status();
+}
+
+util::Status JsonStreamParser::ParseArrayValue(TokenType type) {
+  if (type == UNKNOWN) {
+    return ReportUnknown("Expected a value or ] within an array.",
+                         ParseErrorType::EXPECTED_VALUE_OR_BRACKET);
+  }
+
+  if (type == END_ARRAY) {
+    ow_->EndList();
+    Advance();
+    return util::Status();
+  }
+
+  // The ParseValue call may push something onto the stack so we need to make
+  // sure an ARRAY_MID is after it, so we push it on now. Also, the parsing of
+  // empty-null array value is relying on this ARRAY_MID token.
+  stack_.push(ARRAY_MID);
+  util::Status result = ParseValue(type);
+  if (util::IsCancelled(result)) {
+    // If we were cancelled, pop back off the ARRAY_MID so we don't try to
+    // push it on again when we try over.
+    stack_.pop();
+  }
+  return result;
+}
+
+util::Status JsonStreamParser::ParseArrayMid(TokenType type) {
+  if (type == UNKNOWN) {
+    return ReportUnknown("Expected , or ] after array value.",
+                         ParseErrorType::EXPECTED_COMMA_OR_BRACKET);
+  }
+
+  if (type == END_ARRAY) {
+    ow_->EndList();
+    Advance();
+    return util::Status();
+  }
+
+  // Found a comma, advance past it and expect an array value next.
+  if (type == VALUE_SEPARATOR) {
+    Advance();
+    stack_.push(ARRAY_VALUE);
+    return util::Status();
+  }
+  // Illegal token after array value.
+  return ReportFailure("Expected , or ] after array value.",
+                       ParseErrorType::EXPECTED_COMMA_OR_BRACKET);
+}
+
+util::Status JsonStreamParser::ParseTrue() {
+  ow_->RenderBool(key_, true);
+  key_ = StringPiece();
+  p_.remove_prefix(kKeywordTrue.length());
+  return util::Status();
+}
+
+util::Status JsonStreamParser::ParseFalse() {
+  ow_->RenderBool(key_, false);
+  key_ = StringPiece();
+  p_.remove_prefix(kKeywordFalse.length());
+  return util::Status();
+}
+
+util::Status JsonStreamParser::ParseNull() {
+  ow_->RenderNull(key_);
+  key_ = StringPiece();
+  p_.remove_prefix(kKeywordNull.length());
+  return util::Status();
+}
+
+util::Status JsonStreamParser::ParseEmptyNull() {
+  ow_->RenderNull(key_);
+  key_ = StringPiece();
+  return util::Status();
+}
+
+bool JsonStreamParser::IsEmptyNullAllowed(TokenType type) {
+  if (stack_.empty()) return false;
+  return (stack_.top() == ARRAY_MID && type == VALUE_SEPARATOR) ||
+         stack_.top() == OBJ_MID;
+}
+
+util::Status JsonStreamParser::ReportFailure(StringPiece message,
+                                             ParseErrorType parse_code) {
+  (void)parse_code;  // Parameter is used in Google-internal code.
+  static const int kContextLength = 20;
+  const char* p_start = p_.data();
+  const char* json_start = json_.data();
+  const char* begin = std::max(p_start - kContextLength, json_start);
+  const char* end =
+      std::min(p_start + kContextLength, json_start + json_.size());
+  StringPiece segment(begin, end - begin);
+  std::string location(p_start - begin, ' ');
+  location.push_back('^');
+  auto status = util::InvalidArgumentError(
+      StrCat(message, "\n", segment, "\n", location));
+  return status;
+}
+
+util::Status JsonStreamParser::ReportUnknown(StringPiece message,
+                                             ParseErrorType parse_code) {
+  // If we aren't finishing the parse, cancel parsing and try later.
+  if (!finishing_) {
+    return util::CancelledError("");
+  }
+  if (p_.empty()) {
+    return ReportFailure(StrCat("Unexpected end of string. ", message),
+                         parse_code);
+  }
+  return ReportFailure(message, parse_code);
+}
+
+util::Status JsonStreamParser::IncrementRecursionDepth(
+    StringPiece key) const {
+  if (++recursion_depth_ > max_recursion_depth_) {
+    return util::InvalidArgumentError(StrCat(
+        "Message too deep. Max recursion depth reached for key '", key, "'"));
+  }
+  return util::Status();
+}
+
+void JsonStreamParser::SkipWhitespace() {
+  while (!p_.empty() && ascii_isspace(*p_.data())) {
+    Advance();
+  }
+  if (!p_.empty() && !ascii_isspace(*p_.data())) {
+    seen_non_whitespace_ = true;
+  }
+}
+
+void JsonStreamParser::Advance() {
+  // Advance by moving one UTF8 character while making sure we don't go beyond
+  // the length of StringPiece.
+  p_.remove_prefix(std::min<int>(
+      p_.length(), UTF8FirstLetterNumBytes(p_.data(), p_.length())));
+}
+
+util::Status JsonStreamParser::ParseKey() {
+  StringPiece original = p_;
+
+  if (allow_permissive_key_naming_) {
+    if (!ConsumeKeyPermissive(&p_, &key_)) {
+      return ReportFailure("Invalid key or variable name.",
+                           ParseErrorType::INVALID_KEY_OR_VARIABLE_NAME);
+    }
+  } else {
+    if (!ConsumeKey(&p_, &key_)) {
+      return ReportFailure("Invalid key or variable name.",
+                           ParseErrorType::INVALID_KEY_OR_VARIABLE_NAME);
+    }
+  }
+
+  // If we consumed everything but expect more data, reset p_ and cancel since
+  // we can't know if the key was complete or not.
+  if (!finishing_ && p_.empty()) {
+    p_ = original;
+    return util::CancelledError("");
+  }
+  // Since we aren't using the key storage, clear it out.
+  key_storage_.clear();
+  return util::Status();
+}
+
+JsonStreamParser::TokenType JsonStreamParser::GetNextTokenType() {
+  SkipWhitespace();
+
+  size_t size = p_.size();
+  if (size == 0) {
+    // If we ran out of data, report unknown and we'll place the previous parse
+    // type onto the stack and try again when we have more data.
+    return UNKNOWN;
+  }
+  // TODO(sven): Split this method based on context since different contexts
+  // support different tokens. Would slightly speed up processing?
+  const char* data = p_.data();
+  StringPiece data_view = StringPiece(data, size);
+  if (*data == '\"' || *data == '\'') return BEGIN_STRING;
+  if (*data == '-' || ('0' <= *data && *data <= '9')) {
+    return BEGIN_NUMBER;
+  }
+  if (size >= kKeywordTrue.length() &&
+      HasPrefixString(data_view, kKeywordTrue)) {
+    return BEGIN_TRUE;
+  }
+  if (size >= kKeywordFalse.length() &&
+      HasPrefixString(data_view, kKeywordFalse)) {
+    return BEGIN_FALSE;
+  }
+  if (size >= kKeywordNull.length() &&
+      HasPrefixString(data_view, kKeywordNull)) {
+    return BEGIN_NULL;
+  }
+  if (*data == '{') return BEGIN_OBJECT;
+  if (*data == '}') return END_OBJECT;
+  if (*data == '[') return BEGIN_ARRAY;
+  if (*data == ']') return END_ARRAY;
+  if (*data == ':') return ENTRY_SEPARATOR;
+  if (*data == ',') return VALUE_SEPARATOR;
+  if (MatchKey(p_)) {
+    return BEGIN_KEY;
+  }
+
+  // We don't know that we necessarily have an invalid token here, just that we
+  // can't parse what we have so far. So we don't report an error and just
+  // return UNKNOWN so we can try again later when we have more data, or if we
+  // finish and we have leftovers.
+  return UNKNOWN;
+}
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/object_writer.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/object_writer.cpp
new file mode 100644
index 0000000..4dabd37
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/object_writer.cpp
@@ -0,0 +1,93 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/object_writer.h>
+
+#include <google/protobuf/util/internal/datapiece.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// static
+void ObjectWriter::RenderDataPieceTo(const DataPiece& data,
+                                     StringPiece name, ObjectWriter* ow) {
+  switch (data.type()) {
+    case DataPiece::TYPE_INT32: {
+      ow->RenderInt32(name, data.ToInt32().value());
+      break;
+    }
+    case DataPiece::TYPE_INT64: {
+      ow->RenderInt64(name, data.ToInt64().value());
+      break;
+    }
+    case DataPiece::TYPE_UINT32: {
+      ow->RenderUint32(name, data.ToUint32().value());
+      break;
+    }
+    case DataPiece::TYPE_UINT64: {
+      ow->RenderUint64(name, data.ToUint64().value());
+      break;
+    }
+    case DataPiece::TYPE_DOUBLE: {
+      ow->RenderDouble(name, data.ToDouble().value());
+      break;
+    }
+    case DataPiece::TYPE_FLOAT: {
+      ow->RenderFloat(name, data.ToFloat().value());
+      break;
+    }
+    case DataPiece::TYPE_BOOL: {
+      ow->RenderBool(name, data.ToBool().value());
+      break;
+    }
+    case DataPiece::TYPE_STRING: {
+      ow->RenderString(name, data.ToString().value());
+      break;
+    }
+    case DataPiece::TYPE_BYTES: {
+      ow->RenderBytes(name, data.ToBytes().value());
+      break;
+    }
+    case DataPiece::TYPE_NULL: {
+      ow->RenderNull(name);
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/proto_writer.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/proto_writer.cpp
new file mode 100644
index 0000000..11b6df1
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/proto_writer.cpp
@@ -0,0 +1,827 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/proto_writer.h>
+
+#include <cstdint>
+#include <functional>
+#include <stack>
+#include <unordered_set>
+
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/statusor.h>
+#include <google/protobuf/stubs/time.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/field_mask_utility.h>
+#include <google/protobuf/util/internal/object_location_tracker.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/map_util.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using io::CodedOutputStream;
+using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
+
+ProtoWriter::ProtoWriter(TypeResolver* type_resolver,
+                         const google::protobuf::Type& type,
+                         strings::ByteSink* output, ErrorListener* listener)
+    : master_type_(type),
+      typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
+      own_typeinfo_(true),
+      done_(false),
+      ignore_unknown_fields_(false),
+      ignore_unknown_enum_values_(false),
+      use_lower_camel_for_enums_(false),
+      case_insensitive_enum_parsing_(true),
+      use_json_name_in_missing_fields_(false),
+      element_(nullptr),
+      size_insert_(),
+      output_(output),
+      buffer_(),
+      adapter_(&buffer_),
+      stream_(new CodedOutputStream(&adapter_)),
+      listener_(listener),
+      invalid_depth_(0),
+      tracker_(new ObjectLocationTracker()) {}
+
+ProtoWriter::ProtoWriter(const TypeInfo* typeinfo,
+                         const google::protobuf::Type& type,
+                         strings::ByteSink* output, ErrorListener* listener)
+    : master_type_(type),
+      typeinfo_(typeinfo),
+      own_typeinfo_(false),
+      done_(false),
+      ignore_unknown_fields_(false),
+      ignore_unknown_enum_values_(false),
+      use_lower_camel_for_enums_(false),
+      case_insensitive_enum_parsing_(true),
+      use_json_name_in_missing_fields_(false),
+      element_(nullptr),
+      size_insert_(),
+      output_(output),
+      buffer_(),
+      adapter_(&buffer_),
+      stream_(new CodedOutputStream(&adapter_)),
+      listener_(listener),
+      invalid_depth_(0),
+      tracker_(new ObjectLocationTracker()) {}
+
+ProtoWriter::~ProtoWriter() {
+  if (own_typeinfo_) {
+    delete typeinfo_;
+  }
+  if (element_ == nullptr) return;
+  // Cleanup explicitly in order to avoid destructor stack overflow when input
+  // is deeply nested.
+  // Cast to BaseElement to avoid doing additional checks (like missing fields)
+  // during pop().
+  std::unique_ptr<BaseElement> element(
+      static_cast<BaseElement*>(element_.get())->pop<BaseElement>());
+  while (element != nullptr) {
+    element.reset(element->pop<BaseElement>());
+  }
+}
+
+namespace {
+
+// Writes an INT32 field, including tag to the stream.
+inline util::Status WriteInt32(int field_number, const DataPiece& data,
+                               CodedOutputStream* stream) {
+  util::StatusOr<int32_t> i32 = data.ToInt32();
+  if (i32.ok()) {
+    WireFormatLite::WriteInt32(field_number, i32.value(), stream);
+  }
+  return i32.status();
+}
+
+// writes an SFIXED32 field, including tag, to the stream.
+inline util::Status WriteSFixed32(int field_number, const DataPiece& data,
+                                  CodedOutputStream* stream) {
+  util::StatusOr<int32_t> i32 = data.ToInt32();
+  if (i32.ok()) {
+    WireFormatLite::WriteSFixed32(field_number, i32.value(), stream);
+  }
+  return i32.status();
+}
+
+// Writes an SINT32 field, including tag, to the stream.
+inline util::Status WriteSInt32(int field_number, const DataPiece& data,
+                                CodedOutputStream* stream) {
+  util::StatusOr<int32_t> i32 = data.ToInt32();
+  if (i32.ok()) {
+    WireFormatLite::WriteSInt32(field_number, i32.value(), stream);
+  }
+  return i32.status();
+}
+
+// Writes a FIXED32 field, including tag, to the stream.
+inline util::Status WriteFixed32(int field_number, const DataPiece& data,
+                                 CodedOutputStream* stream) {
+  util::StatusOr<uint32_t> u32 = data.ToUint32();
+  if (u32.ok()) {
+    WireFormatLite::WriteFixed32(field_number, u32.value(), stream);
+  }
+  return u32.status();
+}
+
+// Writes a UINT32 field, including tag, to the stream.
+inline util::Status WriteUInt32(int field_number, const DataPiece& data,
+                                CodedOutputStream* stream) {
+  util::StatusOr<uint32_t> u32 = data.ToUint32();
+  if (u32.ok()) {
+    WireFormatLite::WriteUInt32(field_number, u32.value(), stream);
+  }
+  return u32.status();
+}
+
+// Writes an INT64 field, including tag, to the stream.
+inline util::Status WriteInt64(int field_number, const DataPiece& data,
+                               CodedOutputStream* stream) {
+  util::StatusOr<int64_t> i64 = data.ToInt64();
+  if (i64.ok()) {
+    WireFormatLite::WriteInt64(field_number, i64.value(), stream);
+  }
+  return i64.status();
+}
+
+// Writes an SFIXED64 field, including tag, to the stream.
+inline util::Status WriteSFixed64(int field_number, const DataPiece& data,
+                                  CodedOutputStream* stream) {
+  util::StatusOr<int64_t> i64 = data.ToInt64();
+  if (i64.ok()) {
+    WireFormatLite::WriteSFixed64(field_number, i64.value(), stream);
+  }
+  return i64.status();
+}
+
+// Writes an SINT64 field, including tag, to the stream.
+inline util::Status WriteSInt64(int field_number, const DataPiece& data,
+                                CodedOutputStream* stream) {
+  util::StatusOr<int64_t> i64 = data.ToInt64();
+  if (i64.ok()) {
+    WireFormatLite::WriteSInt64(field_number, i64.value(), stream);
+  }
+  return i64.status();
+}
+
+// Writes a FIXED64 field, including tag, to the stream.
+inline util::Status WriteFixed64(int field_number, const DataPiece& data,
+                                 CodedOutputStream* stream) {
+  util::StatusOr<uint64_t> u64 = data.ToUint64();
+  if (u64.ok()) {
+    WireFormatLite::WriteFixed64(field_number, u64.value(), stream);
+  }
+  return u64.status();
+}
+
+// Writes a UINT64 field, including tag, to the stream.
+inline util::Status WriteUInt64(int field_number, const DataPiece& data,
+                                CodedOutputStream* stream) {
+  util::StatusOr<uint64_t> u64 = data.ToUint64();
+  if (u64.ok()) {
+    WireFormatLite::WriteUInt64(field_number, u64.value(), stream);
+  }
+  return u64.status();
+}
+
+// Writes a DOUBLE field, including tag, to the stream.
+inline util::Status WriteDouble(int field_number, const DataPiece& data,
+                                CodedOutputStream* stream) {
+  util::StatusOr<double> d = data.ToDouble();
+  if (d.ok()) {
+    WireFormatLite::WriteDouble(field_number, d.value(), stream);
+  }
+  return d.status();
+}
+
+// Writes a FLOAT field, including tag, to the stream.
+inline util::Status WriteFloat(int field_number, const DataPiece& data,
+                               CodedOutputStream* stream) {
+  util::StatusOr<float> f = data.ToFloat();
+  if (f.ok()) {
+    WireFormatLite::WriteFloat(field_number, f.value(), stream);
+  }
+  return f.status();
+}
+
+// Writes a BOOL field, including tag, to the stream.
+inline util::Status WriteBool(int field_number, const DataPiece& data,
+                              CodedOutputStream* stream) {
+  util::StatusOr<bool> b = data.ToBool();
+  if (b.ok()) {
+    WireFormatLite::WriteBool(field_number, b.value(), stream);
+  }
+  return b.status();
+}
+
+// Writes a BYTES field, including tag, to the stream.
+inline util::Status WriteBytes(int field_number, const DataPiece& data,
+                               CodedOutputStream* stream) {
+  util::StatusOr<std::string> c = data.ToBytes();
+  if (c.ok()) {
+    WireFormatLite::WriteBytes(field_number, c.value(), stream);
+  }
+  return c.status();
+}
+
+// Writes a STRING field, including tag, to the stream.
+inline util::Status WriteString(int field_number, const DataPiece& data,
+                                CodedOutputStream* stream) {
+  util::StatusOr<std::string> s = data.ToString();
+  if (s.ok()) {
+    WireFormatLite::WriteString(field_number, s.value(), stream);
+  }
+  return s.status();
+}
+
+// Given a google::protobuf::Type, returns the set of all required fields.
+std::unordered_set<const google::protobuf::Field*> GetRequiredFields(
+    const google::protobuf::Type& type) {
+  std::unordered_set<const google::protobuf::Field*> required;
+  for (int i = 0; i < type.fields_size(); i++) {
+    const google::protobuf::Field& field = type.fields(i);
+    if (field.cardinality() == google::protobuf::Field::CARDINALITY_REQUIRED) {
+      required.insert(&field);
+    }
+  }
+  return required;
+}
+
+}  // namespace
+
+ProtoWriter::ProtoElement::ProtoElement(const TypeInfo* typeinfo,
+                                        const google::protobuf::Type& type,
+                                        ProtoWriter* enclosing)
+    : BaseElement(nullptr),
+      ow_(enclosing),
+      parent_field_(nullptr),
+      typeinfo_(typeinfo),
+      proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3),
+      type_(type),
+      size_index_(-1),
+      array_index_(-1),
+      // oneof_indices_ values are 1-indexed (0 means not present).
+      oneof_indices_(type.oneofs_size() + 1) {
+  if (!proto3_) {
+    required_fields_ = GetRequiredFields(type_);
+  }
+}
+
+ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent,
+                                        const google::protobuf::Field* field,
+                                        const google::protobuf::Type& type,
+                                        bool is_list)
+    : BaseElement(parent),
+      ow_(this->parent()->ow_),
+      parent_field_(field),
+      typeinfo_(this->parent()->typeinfo_),
+      proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3),
+      type_(type),
+      size_index_(!is_list &&
+                          field->kind() == google::protobuf::Field::TYPE_MESSAGE
+                      ? ow_->size_insert_.size()
+                      : -1),
+      array_index_(is_list ? 0 : -1),
+      // oneof_indices_ values are 1-indexed (0 means not present).
+      oneof_indices_(type_.oneofs_size() + 1) {
+  if (!is_list) {
+    if (ow_->IsRepeated(*field)) {
+      // Update array_index_ if it is an explicit list.
+      if (this->parent()->array_index_ >= 0) this->parent()->array_index_++;
+    } else if (!proto3_) {
+      // For required fields tracking.
+      this->parent()->RegisterField(field);
+    }
+
+    if (field->kind() == google::protobuf::Field::TYPE_MESSAGE) {
+      if (!proto3_) {
+        required_fields_ = GetRequiredFields(type_);
+      }
+      int start_pos = ow_->stream_->ByteCount();
+      // length of serialized message is the final buffer position minus
+      // starting buffer position, plus length adjustments for size fields
+      // of any nested messages. We start with -start_pos here, so we only
+      // need to add the final buffer position to it at the end.
+      SizeInfo info = {start_pos, -start_pos};
+      ow_->size_insert_.push_back(info);
+    }
+  }
+}
+
+ProtoWriter::ProtoElement* ProtoWriter::ProtoElement::pop() {
+  if (!proto3_) {
+    // Calls the registered error listener for any required field(s) not yet
+    // seen.
+    for (std::unordered_set<const google::protobuf::Field*>::iterator it =
+             required_fields_.begin();
+         it != required_fields_.end(); ++it) {
+      ow_->MissingField(ow_->use_json_name_in_missing_fields_
+                            ? (*it)->json_name()
+                            : (*it)->name());
+    }
+  }
+  // Computes the total number of proto bytes used by a message, also adjusts
+  // the size of all parent messages by the length of this size field.
+  // If size_index_ < 0, this is not a message, so no size field is added.
+  if (size_index_ >= 0) {
+    // Add the final buffer position to compute the total length of this
+    // serialized message. The stored value (before this addition) already
+    // contains the total length of the size fields of all nested messages
+    // minus the initial buffer position.
+    ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount();
+    // Calculate the length required to serialize the size field of the
+    // message, and propagate this additional size information upward to
+    // all enclosing messages.
+    int size = ow_->size_insert_[size_index_].size;
+    int length = CodedOutputStream::VarintSize32(size);
+    for (ProtoElement* e = parent(); e != nullptr; e = e->parent()) {
+      // Only nested messages have size field, lists do not have size field.
+      if (e->size_index_ >= 0) {
+        ow_->size_insert_[e->size_index_].size += length;
+      }
+    }
+  }
+  return BaseElement::pop<ProtoElement>();
+}
+
+void ProtoWriter::ProtoElement::RegisterField(
+    const google::protobuf::Field* field) {
+  if (!required_fields_.empty() &&
+      field->cardinality() == google::protobuf::Field::CARDINALITY_REQUIRED) {
+    required_fields_.erase(field);
+  }
+}
+
+std::string ProtoWriter::ProtoElement::ToString() const {
+  std::string loc = "";
+
+  // first populate a stack with the nodes since we need to process them
+  // from root to leaf when generating the string location
+  const ProtoWriter::ProtoElement* now = this;
+  std::stack<const ProtoWriter::ProtoElement*> element_stack;
+  while (now->parent() != nullptr) {
+    element_stack.push(now);
+    now = now->parent();
+  }
+
+  // now pop each node from the stack and append to the location string
+  while (!element_stack.empty()) {
+    now = element_stack.top();
+    element_stack.pop();
+
+    if (!ow_->IsRepeated(*(now->parent_field_)) ||
+        now->parent()->parent_field_ != now->parent_field_) {
+      std::string name = now->parent_field_->name();
+      size_t i = 0;
+      while (i < name.size() &&
+             (ascii_isalnum(name[i]) || name[i] == '_'))
+        ++i;
+      if (i > 0 && i == name.size()) {  // safe field name
+        if (loc.empty()) {
+          loc = name;
+        } else {
+          StrAppend(&loc, ".", name);
+        }
+      } else {
+        StrAppend(&loc, "[\"", CEscape(name), "\"]");
+      }
+    }
+
+    int array_index_now = now->array_index_;
+    if (ow_->IsRepeated(*(now->parent_field_)) && array_index_now > 0) {
+      StrAppend(&loc, "[", array_index_now - 1, "]");
+    }
+  }
+
+  return loc;
+}
+
+bool ProtoWriter::ProtoElement::IsOneofIndexTaken(int32_t index) {
+  return oneof_indices_[index];
+}
+
+void ProtoWriter::ProtoElement::TakeOneofIndex(int32_t index) {
+  oneof_indices_[index] = true;
+}
+
+void ProtoWriter::InvalidName(StringPiece unknown_name,
+                              StringPiece message) {
+  listener_->InvalidName(location(), unknown_name, message);
+}
+
+void ProtoWriter::InvalidValue(StringPiece type_name,
+                               StringPiece value) {
+  listener_->InvalidValue(location(), type_name, value);
+}
+
+void ProtoWriter::MissingField(StringPiece missing_name) {
+  listener_->MissingField(location(), missing_name);
+}
+
+ProtoWriter* ProtoWriter::StartObject(
+    StringPiece name) {
+  // Starting the root message. Create the root ProtoElement and return.
+  if (element_ == nullptr) {
+    if (!name.empty()) {
+      InvalidName(name, "Root element should not be named.");
+    }
+    element_.reset(new ProtoElement(typeinfo_, master_type_, this));
+    return this;
+  }
+
+  const google::protobuf::Field* field = BeginNamed(name, false);
+
+  if (field == nullptr) return this;
+
+  // Check to see if this field is a oneof and that no oneof in that group has
+  // already been set.
+  if (!ValidOneof(*field, name)) {
+    ++invalid_depth_;
+    return this;
+  }
+
+  const google::protobuf::Type* type = LookupType(field);
+  if (type == nullptr) {
+    ++invalid_depth_;
+    InvalidName(name, StrCat("Missing descriptor for field: ",
+                                   field->type_url()));
+    return this;
+  }
+
+  return StartObjectField(*field, *type);
+}
+
+
+ProtoWriter* ProtoWriter::EndObject() {
+  if (invalid_depth_ > 0) {
+    --invalid_depth_;
+    return this;
+  }
+
+  if (element_ != nullptr) {
+    element_.reset(element_->pop());
+  }
+
+
+  // If ending the root element,
+  // then serialize the full message with calculated sizes.
+  if (element_ == nullptr) {
+    WriteRootMessage();
+  }
+  return this;
+}
+
+ProtoWriter* ProtoWriter::StartList(
+    StringPiece name) {
+
+  const google::protobuf::Field* field = BeginNamed(name, true);
+
+  if (field == nullptr) return this;
+
+  if (!ValidOneof(*field, name)) {
+    ++invalid_depth_;
+    return this;
+  }
+
+  const google::protobuf::Type* type = LookupType(field);
+  if (type == nullptr) {
+    ++invalid_depth_;
+    InvalidName(name, StrCat("Missing descriptor for field: ",
+                                   field->type_url()));
+    return this;
+  }
+
+  return StartListField(*field, *type);
+}
+
+
+ProtoWriter* ProtoWriter::EndList() {
+  if (invalid_depth_ > 0) {
+    --invalid_depth_;
+  } else if (element_ != nullptr) {
+    element_.reset(element_->pop());
+  }
+  return this;
+}
+
+ProtoWriter* ProtoWriter::RenderDataPiece(
+    StringPiece name, const DataPiece& data) {
+  util::Status status;
+  if (invalid_depth_ > 0) return this;
+
+  const google::protobuf::Field* field = Lookup(name);
+
+  if (field == nullptr) return this;
+
+  if (!ValidOneof(*field, name)) return this;
+
+  const google::protobuf::Type* type = LookupType(field);
+  if (type == nullptr) {
+    InvalidName(name, StrCat("Missing descriptor for field: ",
+                                   field->type_url()));
+    return this;
+  }
+
+  return RenderPrimitiveField(*field, *type, data);
+}
+
+bool ProtoWriter::ValidOneof(const google::protobuf::Field& field,
+                             StringPiece unnormalized_name) {
+  if (element_ == nullptr) return true;
+
+  if (field.oneof_index() > 0) {
+    if (element_->IsOneofIndexTaken(field.oneof_index())) {
+      InvalidValue(
+          "oneof",
+          StrCat(
+              "oneof field '", element_->type().oneofs(field.oneof_index() - 1),
+              "' is already set. Cannot set '", unnormalized_name, "'"));
+      return false;
+    }
+    element_->TakeOneofIndex(field.oneof_index());
+  }
+  return true;
+}
+
+bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) {
+  return field.cardinality() == google::protobuf::Field::CARDINALITY_REPEATED;
+}
+
+ProtoWriter* ProtoWriter::StartObjectField(const google::protobuf::Field& field,
+                                           const google::protobuf::Type& type) {
+    WriteTag(field);
+  element_.reset(new ProtoElement(element_.release(), &field, type, false));
+  return this;
+}
+
+ProtoWriter* ProtoWriter::StartListField(const google::protobuf::Field& field,
+                                         const google::protobuf::Type& type) {
+  element_.reset(new ProtoElement(element_.release(), &field, type, true));
+  return this;
+}
+
+util::Status ProtoWriter::WriteEnum(int field_number, const DataPiece& data,
+                                    const google::protobuf::Enum* enum_type,
+                                    CodedOutputStream* stream,
+                                    bool use_lower_camel_for_enums,
+                                    bool case_insensitive_enum_parsing,
+                                    bool ignore_unknown_values) {
+  bool is_unknown_enum_value = false;
+  util::StatusOr<int> e = data.ToEnum(
+      enum_type, use_lower_camel_for_enums, case_insensitive_enum_parsing,
+      ignore_unknown_values, &is_unknown_enum_value);
+  if (e.ok() && !is_unknown_enum_value) {
+    WireFormatLite::WriteEnum(field_number, e.value(), stream);
+  }
+  return e.status();
+}
+
+ProtoWriter* ProtoWriter::RenderPrimitiveField(
+    const google::protobuf::Field& field, const google::protobuf::Type& type,
+    const DataPiece& data) {
+  util::Status status;
+
+  // Pushing a ProtoElement and then pop it off at the end for 2 purposes:
+  // error location reporting and required field accounting.
+  //
+  // For proto3, since there is no required field tracking, we only need to
+  // push ProtoElement for error cases.
+  if (!element_->proto3()) {
+    element_.reset(new ProtoElement(element_.release(), &field, type, false));
+  }
+
+  switch (field.kind()) {
+    case google::protobuf::Field::TYPE_INT32: {
+      status = WriteInt32(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_SFIXED32: {
+      status = WriteSFixed32(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_SINT32: {
+      status = WriteSInt32(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_FIXED32: {
+      status = WriteFixed32(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_UINT32: {
+      status = WriteUInt32(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_INT64: {
+      status = WriteInt64(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_SFIXED64: {
+      status = WriteSFixed64(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_SINT64: {
+      status = WriteSInt64(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_FIXED64: {
+      status = WriteFixed64(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_UINT64: {
+      status = WriteUInt64(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_DOUBLE: {
+      status = WriteDouble(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_FLOAT: {
+      status = WriteFloat(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_BOOL: {
+      status = WriteBool(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_BYTES: {
+      status = WriteBytes(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_STRING: {
+      status = WriteString(field.number(), data, stream_.get());
+      break;
+    }
+    case google::protobuf::Field::TYPE_ENUM: {
+      status = WriteEnum(
+          field.number(), data, typeinfo_->GetEnumByTypeUrl(field.type_url()),
+          stream_.get(), use_lower_camel_for_enums_,
+          case_insensitive_enum_parsing_, ignore_unknown_enum_values_);
+      break;
+    }
+    default:  // TYPE_GROUP, TYPE_MESSAGE, TYPE_UNKNOWN.
+      status = util::InvalidArgumentError(data.ValueAsStringOrDefault(""));
+  }
+
+  if (!status.ok()) {
+    // Push a ProtoElement for location reporting purposes.
+    if (element_->proto3()) {
+      element_.reset(new ProtoElement(element_.release(), &field, type, false));
+    }
+    InvalidValue(field.type_url().empty()
+                     ? google::protobuf::Field_Kind_Name(field.kind())
+                     : field.type_url(),
+                 status.message());
+    element_.reset(element()->pop());
+    return this;
+  }
+
+  if (!element_->proto3()) element_.reset(element()->pop());
+
+  return this;
+}
+
+const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name,
+                                                       bool is_list) {
+  if (invalid_depth_ > 0) {
+    ++invalid_depth_;
+    return nullptr;
+  }
+  const google::protobuf::Field* field = Lookup(name);
+  if (field == nullptr) {
+    ++invalid_depth_;
+    // InvalidName() already called in Lookup().
+    return nullptr;
+  }
+  if (is_list && !IsRepeated(*field)) {
+    ++invalid_depth_;
+    InvalidName(name, "Proto field is not repeating, cannot start list.");
+    return nullptr;
+  }
+  return field;
+}
+
+const google::protobuf::Field* ProtoWriter::Lookup(
+    StringPiece unnormalized_name) {
+  ProtoElement* e = element();
+  if (e == nullptr) {
+    InvalidName(unnormalized_name, "Root element must be a message.");
+    return nullptr;
+  }
+  if (unnormalized_name.empty()) {
+    // Objects in repeated field inherit the same field descriptor.
+    if (e->parent_field() == nullptr) {
+      InvalidName(unnormalized_name, "Proto fields must have a name.");
+    } else if (!IsRepeated(*e->parent_field())) {
+      InvalidName(unnormalized_name, "Proto fields must have a name.");
+      return nullptr;
+    }
+    return e->parent_field();
+  }
+  const google::protobuf::Field* field =
+      typeinfo_->FindField(&e->type(), unnormalized_name);
+  if (field == nullptr && !ignore_unknown_fields_) {
+    InvalidName(unnormalized_name, "Cannot find field.");
+  }
+  return field;
+}
+
+const google::protobuf::Type* ProtoWriter::LookupType(
+    const google::protobuf::Field* field) {
+  return ((field->kind() == google::protobuf::Field::TYPE_MESSAGE ||
+           field->kind() == google::protobuf::Field::TYPE_GROUP)
+              ? typeinfo_->GetTypeByTypeUrl(field->type_url())
+              : &element_->type());
+}
+
+void ProtoWriter::WriteRootMessage() {
+  GOOGLE_DCHECK(!done_);
+  int curr_pos = 0;
+  // Calls the destructor of CodedOutputStream to remove any uninitialized
+  // memory from the Cord before we read it.
+  stream_.reset(nullptr);
+  const void* data;
+  int length;
+  io::ArrayInputStream input_stream(buffer_.data(), buffer_.size());
+  while (input_stream.Next(&data, &length)) {
+    if (length == 0) continue;
+    int num_bytes = length;
+    // Write up to where we need to insert the size field.
+    // The number of bytes we may write is the smaller of:
+    //   - the current fragment size
+    //   - the distance to the next position where a size field needs to be
+    //     inserted.
+    if (!size_insert_.empty() &&
+        size_insert_.front().pos - curr_pos < num_bytes) {
+      num_bytes = size_insert_.front().pos - curr_pos;
+    }
+    output_->Append(static_cast<const char*>(data), num_bytes);
+    if (num_bytes < length) {
+      input_stream.BackUp(length - num_bytes);
+    }
+    curr_pos += num_bytes;
+    // Insert the size field.
+    //   size_insert_.front():      the next <index, size> pair to be written.
+    //   size_insert_.front().pos:  position of the size field.
+    //   size_insert_.front().size: the size (integer) to be inserted.
+    if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) {
+      // Varint32 occupies at most 10 bytes.
+      uint8_t insert_buffer[10];
+      uint8_t* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray(
+          size_insert_.front().size, insert_buffer);
+      output_->Append(reinterpret_cast<const char*>(insert_buffer),
+                      insert_buffer_pos - insert_buffer);
+      size_insert_.pop_front();
+    }
+  }
+  output_->Flush();
+  stream_.reset(new CodedOutputStream(&adapter_));
+  done_ = true;
+}
+
+void ProtoWriter::WriteTag(const google::protobuf::Field& field) {
+  WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
+      static_cast<WireFormatLite::FieldType>(field.kind()));
+  stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type));
+}
+
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/protostream_objectsource.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/protostream_objectsource.cpp
new file mode 100644
index 0000000..cf6c99a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/protostream_objectsource.cpp
@@ -0,0 +1,1114 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/protostream_objectsource.h>
+
+#include <cstdint>
+#include <unordered_map>
+#include <utility>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/stubs/time.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/field_mask_utility.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/status_macros.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using ::PROTOBUF_NAMESPACE_ID::internal::WireFormat;
+using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
+
+namespace {
+
+static int kDefaultMaxRecursionDepth = 64;
+
+// Finds a field with the given number. nullptr if none found.
+const google::protobuf::Field* FindFieldByNumber(
+    const google::protobuf::Type& type, int number);
+
+// Returns true if the field is packable.
+bool IsPackable(const google::protobuf::Field& field);
+
+// Finds an enum value with the given number. nullptr if none found.
+const google::protobuf::EnumValue* FindEnumValueByNumber(
+    const google::protobuf::Enum& tech_enum, int number);
+
+// Utility function to format nanos.
+const std::string FormatNanos(uint32_t nanos, bool with_trailing_zeros);
+
+util::StatusOr<std::string> MapKeyDefaultValueAsString(
+    const google::protobuf::Field& field) {
+  switch (field.kind()) {
+    case google::protobuf::Field::TYPE_BOOL:
+      return std::string("false");
+    case google::protobuf::Field::TYPE_INT32:
+    case google::protobuf::Field::TYPE_INT64:
+    case google::protobuf::Field::TYPE_UINT32:
+    case google::protobuf::Field::TYPE_UINT64:
+    case google::protobuf::Field::TYPE_SINT32:
+    case google::protobuf::Field::TYPE_SINT64:
+    case google::protobuf::Field::TYPE_SFIXED32:
+    case google::protobuf::Field::TYPE_SFIXED64:
+    case google::protobuf::Field::TYPE_FIXED32:
+    case google::protobuf::Field::TYPE_FIXED64:
+      return std::string("0");
+    case google::protobuf::Field::TYPE_STRING:
+      return std::string();
+    default:
+      return util::InternalError("Invalid map key type.");
+  }
+}
+}  // namespace
+
+
+ProtoStreamObjectSource::ProtoStreamObjectSource(
+    io::CodedInputStream* stream, TypeResolver* type_resolver,
+    const google::protobuf::Type& type, const RenderOptions& render_options)
+    : stream_(stream),
+      typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
+      own_typeinfo_(true),
+      type_(type),
+      render_options_(render_options),
+      recursion_depth_(0),
+      max_recursion_depth_(kDefaultMaxRecursionDepth) {
+  GOOGLE_LOG_IF(DFATAL, stream == nullptr) << "Input stream is nullptr.";
+}
+
+ProtoStreamObjectSource::ProtoStreamObjectSource(
+    io::CodedInputStream* stream, const TypeInfo* typeinfo,
+    const google::protobuf::Type& type, const RenderOptions& render_options)
+    : stream_(stream),
+      typeinfo_(typeinfo),
+      own_typeinfo_(false),
+      type_(type),
+      render_options_(render_options),
+      recursion_depth_(0),
+      max_recursion_depth_(kDefaultMaxRecursionDepth) {
+  GOOGLE_LOG_IF(DFATAL, stream == nullptr) << "Input stream is nullptr.";
+}
+
+ProtoStreamObjectSource::~ProtoStreamObjectSource() {
+  if (own_typeinfo_) {
+    delete typeinfo_;
+  }
+}
+
+util::Status ProtoStreamObjectSource::NamedWriteTo(StringPiece name,
+                                                   ObjectWriter* ow) const {
+  return WriteMessage(type_, name, 0, true, ow);
+}
+
+const google::protobuf::Field* ProtoStreamObjectSource::FindAndVerifyField(
+    const google::protobuf::Type& type, uint32_t tag) const {
+  // Lookup the new field in the type by tag number.
+  const google::protobuf::Field* field = FindFieldByNumber(type, tag >> 3);
+  // Verify if the field corresponds to the wire type in tag.
+  // If there is any discrepancy, mark the field as not found.
+  if (field != nullptr) {
+    WireFormatLite::WireType expected_type =
+        WireFormatLite::WireTypeForFieldType(
+            static_cast<WireFormatLite::FieldType>(field->kind()));
+    WireFormatLite::WireType actual_type = WireFormatLite::GetTagWireType(tag);
+    if (actual_type != expected_type &&
+        (!IsPackable(*field) ||
+         actual_type != WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
+      field = nullptr;
+    }
+  }
+  return field;
+}
+
+util::Status ProtoStreamObjectSource::WriteMessage(
+    const google::protobuf::Type& type, StringPiece name,
+    const uint32_t end_tag, bool include_start_and_end,
+    ObjectWriter* ow) const {
+
+  const TypeRenderer* type_renderer = FindTypeRenderer(type.name());
+  if (type_renderer != nullptr) {
+    return (*type_renderer)(this, type, name, ow);
+  }
+
+  const google::protobuf::Field* field = nullptr;
+  std::string field_name;
+  // last_tag set to dummy value that is different from tag.
+  uint32_t tag = stream_->ReadTag(), last_tag = tag + 1;
+  UnknownFieldSet unknown_fields;
+
+
+  if (include_start_and_end) {
+    ow->StartObject(name);
+  }
+  while (tag != end_tag && tag != 0) {
+    if (tag != last_tag) {  // Update field only if tag is changed.
+      last_tag = tag;
+      field = FindAndVerifyField(type, tag);
+      if (field != nullptr) {
+        if (render_options_.preserve_proto_field_names) {
+          field_name = field->name();
+        } else {
+          field_name = field->json_name();
+        }
+      }
+    }
+    if (field == nullptr) {
+      // If we didn't find a field, skip this unknown tag.
+      // TODO(wpoon): Check return boolean value.
+      WireFormat::SkipField(
+          stream_, tag,
+                                                nullptr);
+      tag = stream_->ReadTag();
+      continue;
+    }
+
+    if (field->cardinality() == google::protobuf::Field::CARDINALITY_REPEATED) {
+      if (IsMap(*field)) {
+        ow->StartObject(field_name);
+        ASSIGN_OR_RETURN(tag, RenderMap(field, field_name, tag, ow));
+        ow->EndObject();
+      } else {
+        ASSIGN_OR_RETURN(tag, RenderList(field, field_name, tag, ow));
+      }
+    } else {
+      // Render the field.
+      RETURN_IF_ERROR(RenderField(field, field_name, ow));
+      tag = stream_->ReadTag();
+    }
+  }
+
+
+  if (include_start_and_end) {
+    ow->EndObject();
+  }
+  return util::Status();
+}
+
+util::StatusOr<uint32_t> ProtoStreamObjectSource::RenderList(
+    const google::protobuf::Field* field, StringPiece name,
+    uint32_t list_tag, ObjectWriter* ow) const {
+  uint32_t tag_to_return = 0;
+  ow->StartList(name);
+  if (IsPackable(*field) &&
+      list_tag ==
+          WireFormatLite::MakeTag(field->number(),
+                                  WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
+    RETURN_IF_ERROR(RenderPacked(field, ow));
+    // Since packed fields have a single tag, read another tag from stream to
+    // return.
+    tag_to_return = stream_->ReadTag();
+  } else {
+    do {
+      RETURN_IF_ERROR(RenderField(field, "", ow));
+    } while ((tag_to_return = stream_->ReadTag()) == list_tag);
+  }
+  ow->EndList();
+  return tag_to_return;
+}
+
+util::StatusOr<uint32_t> ProtoStreamObjectSource::RenderMap(
+    const google::protobuf::Field* field, StringPiece /* name */,
+    uint32_t list_tag, ObjectWriter* ow) const {
+  const google::protobuf::Type* field_type =
+      typeinfo_->GetTypeByTypeUrl(field->type_url());
+  uint32_t tag_to_return = 0;
+  do {
+    // Render map entry message type.
+    uint32_t buffer32;
+    stream_->ReadVarint32(&buffer32);  // message length
+    int old_limit = stream_->PushLimit(buffer32);
+    std::string map_key;
+    for (uint32_t tag = stream_->ReadTag(); tag != 0;
+         tag = stream_->ReadTag()) {
+      const google::protobuf::Field* map_entry_field =
+          FindAndVerifyField(*field_type, tag);
+      if (map_entry_field == nullptr) {
+        WireFormat::SkipField(stream_, tag, nullptr);
+        continue;
+      }
+      // Map field numbers are key = 1 and value = 2
+      if (map_entry_field->number() == 1) {
+        map_key = ReadFieldValueAsString(*map_entry_field);
+      } else if (map_entry_field->number() == 2) {
+        if (map_key.empty()) {
+          // An absent map key is treated as the default.
+          const google::protobuf::Field* key_field =
+              FindFieldByNumber(*field_type, 1);
+          if (key_field == nullptr) {
+            // The Type info for this map entry is incorrect. It should always
+            // have a field named "key" and with field number 1.
+            return util::InternalError("Invalid map entry.");
+          }
+          ASSIGN_OR_RETURN(map_key, MapKeyDefaultValueAsString(*key_field));
+        }
+        RETURN_IF_ERROR(RenderField(map_entry_field, map_key, ow));
+      } else {
+        // The Type info for this map entry is incorrect. It should contain
+        // exactly two fields with field number 1 and 2.
+        return util::InternalError("Invalid map entry.");
+      }
+    }
+    stream_->PopLimit(old_limit);
+  } while ((tag_to_return = stream_->ReadTag()) == list_tag);
+  return tag_to_return;
+}
+
+util::Status ProtoStreamObjectSource::RenderPacked(
+    const google::protobuf::Field* field, ObjectWriter* ow) const {
+  uint32_t length;
+  stream_->ReadVarint32(&length);
+  int old_limit = stream_->PushLimit(length);
+  while (stream_->BytesUntilLimit() > 0) {
+    RETURN_IF_ERROR(RenderField(field, StringPiece(), ow));
+  }
+  stream_->PopLimit(old_limit);
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderTimestamp(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+    StringPiece field_name, ObjectWriter* ow) {
+  std::pair<int64_t, int32_t> p = os->ReadSecondsAndNanos(type);
+  int64_t seconds = p.first;
+  int32_t nanos = p.second;
+  if (seconds > kTimestampMaxSeconds || seconds < kTimestampMinSeconds) {
+    return util::InternalError(StrCat(
+        "Timestamp seconds exceeds limit for field: ", field_name));
+  }
+
+  if (nanos < 0 || nanos >= kNanosPerSecond) {
+    return util::InternalError(
+        StrCat("Timestamp nanos exceeds limit for field: ", field_name));
+  }
+
+  ow->RenderString(field_name,
+                   ::google::protobuf::internal::FormatTime(seconds, nanos));
+
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderDuration(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+    StringPiece field_name, ObjectWriter* ow) {
+  std::pair<int64_t, int32_t> p = os->ReadSecondsAndNanos(type);
+  int64_t seconds = p.first;
+  int32_t nanos = p.second;
+  if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds) {
+    return util::InternalError(
+        StrCat("Duration seconds exceeds limit for field: ", field_name));
+  }
+
+  if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
+    return util::InternalError(
+        StrCat("Duration nanos exceeds limit for field: ", field_name));
+  }
+
+  std::string sign = "";
+  if (seconds < 0) {
+    if (nanos > 0) {
+      return util::InternalError(
+          StrCat("Duration nanos is non-negative, but seconds is "
+                       "negative for field: ",
+                       field_name));
+    }
+    sign = "-";
+    seconds = -seconds;
+    nanos = -nanos;
+  } else if (seconds == 0 && nanos < 0) {
+    sign = "-";
+    nanos = -nanos;
+  }
+  std::string formatted_duration = StringPrintf(
+      "%s%lld%ss", sign.c_str(), static_cast<long long>(seconds),  // NOLINT
+      FormatNanos(
+          nanos,
+          false
+          )
+          .c_str());
+  ow->RenderString(field_name, formatted_duration);
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderDouble(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
+    StringPiece field_name, ObjectWriter* ow) {
+  uint32_t tag = os->stream_->ReadTag();
+  uint64_t buffer64 = 0;  // default value of Double wrapper value
+  if (tag != 0) {
+    os->stream_->ReadLittleEndian64(&buffer64);
+    os->stream_->ReadTag();
+  }
+  ow->RenderDouble(field_name, bit_cast<double>(buffer64));
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderFloat(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
+    StringPiece field_name, ObjectWriter* ow) {
+  uint32_t tag = os->stream_->ReadTag();
+  uint32_t buffer32 = 0;  // default value of Float wrapper value
+  if (tag != 0) {
+    os->stream_->ReadLittleEndian32(&buffer32);
+    os->stream_->ReadTag();
+  }
+  ow->RenderFloat(field_name, bit_cast<float>(buffer32));
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderInt64(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
+    StringPiece field_name, ObjectWriter* ow) {
+  uint32_t tag = os->stream_->ReadTag();
+  uint64_t buffer64 = 0;  // default value of Int64 wrapper value
+  if (tag != 0) {
+    os->stream_->ReadVarint64(&buffer64);
+    os->stream_->ReadTag();
+  }
+  ow->RenderInt64(field_name, bit_cast<int64_t>(buffer64));
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderUInt64(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
+    StringPiece field_name, ObjectWriter* ow) {
+  uint32_t tag = os->stream_->ReadTag();
+  uint64_t buffer64 = 0;  // default value of UInt64 wrapper value
+  if (tag != 0) {
+    os->stream_->ReadVarint64(&buffer64);
+    os->stream_->ReadTag();
+  }
+  ow->RenderUint64(field_name, bit_cast<uint64_t>(buffer64));
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderInt32(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
+    StringPiece field_name, ObjectWriter* ow) {
+  uint32_t tag = os->stream_->ReadTag();
+  uint32_t buffer32 = 0;  // default value of Int32 wrapper value
+  if (tag != 0) {
+    os->stream_->ReadVarint32(&buffer32);
+    os->stream_->ReadTag();
+  }
+  ow->RenderInt32(field_name, bit_cast<int32_t>(buffer32));
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderUInt32(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
+    StringPiece field_name, ObjectWriter* ow) {
+  uint32_t tag = os->stream_->ReadTag();
+  uint32_t buffer32 = 0;  // default value of UInt32 wrapper value
+  if (tag != 0) {
+    os->stream_->ReadVarint32(&buffer32);
+    os->stream_->ReadTag();
+  }
+  ow->RenderUint32(field_name, bit_cast<uint32_t>(buffer32));
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderBool(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
+    StringPiece field_name, ObjectWriter* ow) {
+  uint32_t tag = os->stream_->ReadTag();
+  uint64_t buffer64 = 0;  // results in 'false' value as default, which is the
+                          // default value of Bool wrapper
+  if (tag != 0) {
+    os->stream_->ReadVarint64(&buffer64);
+    os->stream_->ReadTag();
+  }
+  ow->RenderBool(field_name, buffer64 != 0);
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderString(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
+    StringPiece field_name, ObjectWriter* ow) {
+  uint32_t tag = os->stream_->ReadTag();
+  uint32_t buffer32;
+  std::string str;  // default value of empty for String wrapper
+  if (tag != 0) {
+    os->stream_->ReadVarint32(&buffer32);  // string size.
+    os->stream_->ReadString(&str, buffer32);
+    os->stream_->ReadTag();
+  }
+  ow->RenderString(field_name, str);
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderBytes(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
+    StringPiece field_name, ObjectWriter* ow) {
+  uint32_t tag = os->stream_->ReadTag();
+  uint32_t buffer32;
+  std::string str;
+  if (tag != 0) {
+    os->stream_->ReadVarint32(&buffer32);
+    os->stream_->ReadString(&str, buffer32);
+    os->stream_->ReadTag();
+  }
+  ow->RenderBytes(field_name, str);
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderStruct(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+    StringPiece field_name, ObjectWriter* ow) {
+  const google::protobuf::Field* field = nullptr;
+  uint32_t tag = os->stream_->ReadTag();
+  ow->StartObject(field_name);
+  while (tag != 0) {
+    field = os->FindAndVerifyField(type, tag);
+    if (field == nullptr) {
+      WireFormat::SkipField(os->stream_, tag, nullptr);
+      tag = os->stream_->ReadTag();
+      continue;
+    }
+    // google.protobuf.Struct has only one field that is a map. Hence we use
+    // RenderMap to render that field.
+    if (os->IsMap(*field)) {
+      ASSIGN_OR_RETURN(tag, os->RenderMap(field, field_name, tag, ow));
+    }
+  }
+  ow->EndObject();
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderStructValue(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+    StringPiece field_name, ObjectWriter* ow) {
+  const google::protobuf::Field* field = nullptr;
+  for (uint32_t tag = os->stream_->ReadTag(); tag != 0;
+       tag = os->stream_->ReadTag()) {
+    field = os->FindAndVerifyField(type, tag);
+    if (field == nullptr) {
+      WireFormat::SkipField(os->stream_, tag, nullptr);
+      continue;
+    }
+    RETURN_IF_ERROR(os->RenderField(field, field_name, ow));
+  }
+  return util::Status();
+}
+
+// TODO(skarvaje): Avoid code duplication of for loops and SkipField logic.
+util::Status ProtoStreamObjectSource::RenderStructListValue(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+    StringPiece field_name, ObjectWriter* ow) {
+  uint32_t tag = os->stream_->ReadTag();
+
+  // Render empty list when we find empty ListValue message.
+  if (tag == 0) {
+    ow->StartList(field_name);
+    ow->EndList();
+    return util::Status();
+  }
+
+  while (tag != 0) {
+    const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
+    if (field == nullptr) {
+      WireFormat::SkipField(os->stream_, tag, nullptr);
+      tag = os->stream_->ReadTag();
+      continue;
+    }
+    ASSIGN_OR_RETURN(tag, os->RenderList(field, field_name, tag, ow));
+  }
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderAny(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+    StringPiece field_name, ObjectWriter* ow) {
+  // An Any is of the form { string type_url = 1; bytes value = 2; }
+  uint32_t tag;
+  std::string type_url;
+  std::string value;
+
+  // First read out the type_url and value from the proto stream
+  for (tag = os->stream_->ReadTag(); tag != 0; tag = os->stream_->ReadTag()) {
+    const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
+    if (field == nullptr) {
+      WireFormat::SkipField(os->stream_, tag, nullptr);
+      continue;
+    }
+    // 'type_url' has field number of 1 and 'value' has field number 2
+    // //google/protobuf/any.proto
+    if (field->number() == 1) {
+      // read type_url
+      uint32_t type_url_size;
+      os->stream_->ReadVarint32(&type_url_size);
+      os->stream_->ReadString(&type_url, type_url_size);
+    } else if (field->number() == 2) {
+      // read value
+      uint32_t value_size;
+      os->stream_->ReadVarint32(&value_size);
+      os->stream_->ReadString(&value, value_size);
+    }
+  }
+
+  // If there is no value, we don't lookup the type, we just output it (if
+  // present). If both type and value are empty we output an empty object.
+  if (value.empty()) {
+    ow->StartObject(field_name);
+    if (!type_url.empty()) {
+      ow->RenderString("@type", type_url);
+    }
+    ow->EndObject();
+    return util::Status();
+  }
+
+  // If there is a value but no type, we cannot render it, so report an error.
+  if (type_url.empty()) {
+    // TODO(sven): Add an external message once those are ready.
+    return util::InternalError("Invalid Any, the type_url is missing.");
+  }
+
+  util::StatusOr<const google::protobuf::Type*> resolved_type =
+      os->typeinfo_->ResolveTypeUrl(type_url);
+
+  if (!resolved_type.ok()) {
+    // Convert into an internal error, since this means the backend gave us
+    // an invalid response (missing or invalid type information).
+    return util::InternalError(resolved_type.status().message());
+  }
+  // nested_type cannot be null at this time.
+  const google::protobuf::Type* nested_type = resolved_type.value();
+
+  io::ArrayInputStream zero_copy_stream(value.data(), value.size());
+  io::CodedInputStream in_stream(&zero_copy_stream);
+  // We know the type so we can render it. Recursively parse the nested stream
+  // using a nested ProtoStreamObjectSource using our nested type information.
+  ProtoStreamObjectSource nested_os(&in_stream, os->typeinfo_, *nested_type,
+                                    os->render_options_);
+
+  // We manually call start and end object here so we can inject the @type.
+  ow->StartObject(field_name);
+  ow->RenderString("@type", type_url);
+  util::Status result =
+      nested_os.WriteMessage(nested_os.type_, "value", 0, false, ow);
+  ow->EndObject();
+  return result;
+}
+
+util::Status ProtoStreamObjectSource::RenderFieldMask(
+    const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+    StringPiece field_name, ObjectWriter* ow) {
+  std::string combined;
+  uint32_t buffer32;
+  uint32_t paths_field_tag = 0;
+  for (uint32_t tag = os->stream_->ReadTag(); tag != 0;
+       tag = os->stream_->ReadTag()) {
+    if (paths_field_tag == 0) {
+      const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
+      if (field != nullptr && field->number() == 1 &&
+          field->name() == "paths") {
+        paths_field_tag = tag;
+      }
+    }
+    if (paths_field_tag != tag) {
+      return util::InternalError("Invalid FieldMask, unexpected field.");
+    }
+    std::string str;
+    os->stream_->ReadVarint32(&buffer32);  // string size.
+    os->stream_->ReadString(&str, buffer32);
+    if (!combined.empty()) {
+      combined.append(",");
+    }
+    combined.append(ConvertFieldMaskPath(str, &ToCamelCase));
+  }
+  ow->RenderString(field_name, combined);
+  return util::Status();
+}
+
+
+std::unordered_map<std::string, ProtoStreamObjectSource::TypeRenderer>*
+    ProtoStreamObjectSource::renderers_ = nullptr;
+PROTOBUF_NAMESPACE_ID::internal::once_flag source_renderers_init_;
+
+
+void ProtoStreamObjectSource::InitRendererMap() {
+  renderers_ = new std::unordered_map<std::string,
+                                      ProtoStreamObjectSource::TypeRenderer>();
+  (*renderers_)["google.protobuf.Timestamp"] =
+      &ProtoStreamObjectSource::RenderTimestamp;
+  (*renderers_)["google.protobuf.Duration"] =
+      &ProtoStreamObjectSource::RenderDuration;
+  (*renderers_)["google.protobuf.DoubleValue"] =
+      &ProtoStreamObjectSource::RenderDouble;
+  (*renderers_)["google.protobuf.FloatValue"] =
+      &ProtoStreamObjectSource::RenderFloat;
+  (*renderers_)["google.protobuf.Int64Value"] =
+      &ProtoStreamObjectSource::RenderInt64;
+  (*renderers_)["google.protobuf.UInt64Value"] =
+      &ProtoStreamObjectSource::RenderUInt64;
+  (*renderers_)["google.protobuf.Int32Value"] =
+      &ProtoStreamObjectSource::RenderInt32;
+  (*renderers_)["google.protobuf.UInt32Value"] =
+      &ProtoStreamObjectSource::RenderUInt32;
+  (*renderers_)["google.protobuf.BoolValue"] =
+      &ProtoStreamObjectSource::RenderBool;
+  (*renderers_)["google.protobuf.StringValue"] =
+      &ProtoStreamObjectSource::RenderString;
+  (*renderers_)["google.protobuf.BytesValue"] =
+      &ProtoStreamObjectSource::RenderBytes;
+  (*renderers_)["google.protobuf.Any"] = &ProtoStreamObjectSource::RenderAny;
+  (*renderers_)["google.protobuf.Struct"] =
+      &ProtoStreamObjectSource::RenderStruct;
+  (*renderers_)["google.protobuf.Value"] =
+      &ProtoStreamObjectSource::RenderStructValue;
+  (*renderers_)["google.protobuf.ListValue"] =
+      &ProtoStreamObjectSource::RenderStructListValue;
+  (*renderers_)["google.protobuf.FieldMask"] =
+      &ProtoStreamObjectSource::RenderFieldMask;
+  ::google::protobuf::internal::OnShutdown(&DeleteRendererMap);
+}
+
+void ProtoStreamObjectSource::DeleteRendererMap() {
+  delete ProtoStreamObjectSource::renderers_;
+  renderers_ = nullptr;
+}
+
+// static
+ProtoStreamObjectSource::TypeRenderer*
+ProtoStreamObjectSource::FindTypeRenderer(const std::string& type_url) {
+  PROTOBUF_NAMESPACE_ID::internal::call_once(source_renderers_init_,
+                                             InitRendererMap);
+  return FindOrNull(*renderers_, type_url);
+}
+
+util::Status ProtoStreamObjectSource::RenderField(
+    const google::protobuf::Field* field, StringPiece field_name,
+    ObjectWriter* ow) const {
+  // Short-circuit message types as it tends to call WriteMessage recursively
+  // and ends up using a lot of stack space. Keep the stack usage of this
+  // message small in order to preserve stack space and not crash.
+  if (field->kind() == google::protobuf::Field::TYPE_MESSAGE) {
+    uint32_t buffer32;
+    stream_->ReadVarint32(&buffer32);  // message length
+    int old_limit = stream_->PushLimit(buffer32);
+    // Get the nested message type for this field.
+    const google::protobuf::Type* type =
+        typeinfo_->GetTypeByTypeUrl(field->type_url());
+    if (type == nullptr) {
+      return util::InternalError(
+          StrCat("Invalid configuration. Could not find the type: ",
+                       field->type_url()));
+    }
+
+    // Short-circuit any special type rendering to save call-stack space.
+    const TypeRenderer* type_renderer = FindTypeRenderer(type->name());
+
+    RETURN_IF_ERROR(IncrementRecursionDepth(type->name(), field_name));
+    if (type_renderer != nullptr) {
+      RETURN_IF_ERROR((*type_renderer)(this, *type, field_name, ow));
+    } else {
+      RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow));
+    }
+    --recursion_depth_;
+
+    if (!stream_->ConsumedEntireMessage()) {
+      return util::InvalidArgumentError(
+          "Nested protocol message not parsed in its entirety.");
+    }
+    stream_->PopLimit(old_limit);
+  } else {
+    // Render all other non-message types.
+    return RenderNonMessageField(field, field_name, ow);
+  }
+  return util::Status();
+}
+
+util::Status ProtoStreamObjectSource::RenderNonMessageField(
+    const google::protobuf::Field* field, StringPiece field_name,
+    ObjectWriter* ow) const {
+  // Temporary buffers of different types.
+  uint32_t buffer32 = 0;
+  uint64_t buffer64 = 0;
+  std::string strbuffer;
+  switch (field->kind()) {
+    case google::protobuf::Field::TYPE_BOOL: {
+      stream_->ReadVarint64(&buffer64);
+      ow->RenderBool(field_name, buffer64 != 0);
+      break;
+    }
+    case google::protobuf::Field::TYPE_INT32: {
+      stream_->ReadVarint32(&buffer32);
+      ow->RenderInt32(field_name, bit_cast<int32_t>(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_INT64: {
+      stream_->ReadVarint64(&buffer64);
+      ow->RenderInt64(field_name, bit_cast<int64_t>(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_UINT32: {
+      stream_->ReadVarint32(&buffer32);
+      ow->RenderUint32(field_name, bit_cast<uint32_t>(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_UINT64: {
+      stream_->ReadVarint64(&buffer64);
+      ow->RenderUint64(field_name, bit_cast<uint64_t>(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_SINT32: {
+      stream_->ReadVarint32(&buffer32);
+      ow->RenderInt32(field_name, WireFormatLite::ZigZagDecode32(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_SINT64: {
+      stream_->ReadVarint64(&buffer64);
+      ow->RenderInt64(field_name, WireFormatLite::ZigZagDecode64(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_SFIXED32: {
+      stream_->ReadLittleEndian32(&buffer32);
+      ow->RenderInt32(field_name, bit_cast<int32_t>(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_SFIXED64: {
+      stream_->ReadLittleEndian64(&buffer64);
+      ow->RenderInt64(field_name, bit_cast<int64_t>(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_FIXED32: {
+      stream_->ReadLittleEndian32(&buffer32);
+      ow->RenderUint32(field_name, bit_cast<uint32_t>(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_FIXED64: {
+      stream_->ReadLittleEndian64(&buffer64);
+      ow->RenderUint64(field_name, bit_cast<uint64_t>(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_FLOAT: {
+      stream_->ReadLittleEndian32(&buffer32);
+      ow->RenderFloat(field_name, bit_cast<float>(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_DOUBLE: {
+      stream_->ReadLittleEndian64(&buffer64);
+      ow->RenderDouble(field_name, bit_cast<double>(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_ENUM: {
+      stream_->ReadVarint32(&buffer32);
+
+      // If the field represents an explicit NULL value, render null.
+      if (field->type_url() == kStructNullValueTypeUrl) {
+        ow->RenderNull(field_name);
+        break;
+      }
+
+      // Get the nested enum type for this field.
+      // TODO(skarvaje): Avoid string manipulation. Find ways to speed this
+      // up.
+      const google::protobuf::Enum* en =
+          typeinfo_->GetEnumByTypeUrl(field->type_url());
+      // Lookup the name of the enum, and render that. Unknown enum values
+      // are printed as integers.
+      if (en != nullptr) {
+        const google::protobuf::EnumValue* enum_value =
+            FindEnumValueByNumber(*en, buffer32);
+        if (enum_value != nullptr) {
+          if (render_options_.use_ints_for_enums) {
+            ow->RenderInt32(field_name, buffer32);
+          } else if (render_options_.use_lower_camel_for_enums) {
+            ow->RenderString(field_name,
+                             EnumValueNameToLowerCamelCase(enum_value->name()));
+          } else {
+            ow->RenderString(field_name, enum_value->name());
+          }
+        } else {
+            ow->RenderInt32(field_name, buffer32);
+        }
+      } else {
+          ow->RenderInt32(field_name, buffer32);
+      }
+      break;
+    }
+    case google::protobuf::Field::TYPE_STRING: {
+      stream_->ReadVarint32(&buffer32);  // string size.
+      stream_->ReadString(&strbuffer, buffer32);
+      ow->RenderString(field_name, strbuffer);
+      break;
+    }
+    case google::protobuf::Field::TYPE_BYTES: {
+      stream_->ReadVarint32(&buffer32);  // bytes size.
+      stream_->ReadString(&strbuffer, buffer32);
+      ow->RenderBytes(field_name, strbuffer);
+      break;
+    }
+    default:
+      break;
+  }
+  return util::Status();
+}
+
+// TODO(skarvaje): Fix this to avoid code duplication.
+const std::string ProtoStreamObjectSource::ReadFieldValueAsString(
+    const google::protobuf::Field& field) const {
+  std::string result;
+  switch (field.kind()) {
+    case google::protobuf::Field::TYPE_BOOL: {
+      uint64_t buffer64;
+      stream_->ReadVarint64(&buffer64);
+      result = buffer64 != 0 ? "true" : "false";
+      break;
+    }
+    case google::protobuf::Field::TYPE_INT32: {
+      uint32_t buffer32;
+      stream_->ReadVarint32(&buffer32);
+      result = StrCat(bit_cast<int32_t>(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_INT64: {
+      uint64_t buffer64;
+      stream_->ReadVarint64(&buffer64);
+      result = StrCat(bit_cast<int64_t>(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_UINT32: {
+      uint32_t buffer32;
+      stream_->ReadVarint32(&buffer32);
+      result = StrCat(bit_cast<uint32_t>(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_UINT64: {
+      uint64_t buffer64;
+      stream_->ReadVarint64(&buffer64);
+      result = StrCat(bit_cast<uint64_t>(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_SINT32: {
+      uint32_t buffer32;
+      stream_->ReadVarint32(&buffer32);
+      result = StrCat(WireFormatLite::ZigZagDecode32(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_SINT64: {
+      uint64_t buffer64;
+      stream_->ReadVarint64(&buffer64);
+      result = StrCat(WireFormatLite::ZigZagDecode64(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_SFIXED32: {
+      uint32_t buffer32;
+      stream_->ReadLittleEndian32(&buffer32);
+      result = StrCat(bit_cast<int32_t>(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_SFIXED64: {
+      uint64_t buffer64;
+      stream_->ReadLittleEndian64(&buffer64);
+      result = StrCat(bit_cast<int64_t>(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_FIXED32: {
+      uint32_t buffer32;
+      stream_->ReadLittleEndian32(&buffer32);
+      result = StrCat(bit_cast<uint32_t>(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_FIXED64: {
+      uint64_t buffer64;
+      stream_->ReadLittleEndian64(&buffer64);
+      result = StrCat(bit_cast<uint64_t>(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_FLOAT: {
+      uint32_t buffer32;
+      stream_->ReadLittleEndian32(&buffer32);
+      result = SimpleFtoa(bit_cast<float>(buffer32));
+      break;
+    }
+    case google::protobuf::Field::TYPE_DOUBLE: {
+      uint64_t buffer64;
+      stream_->ReadLittleEndian64(&buffer64);
+      result = SimpleDtoa(bit_cast<double>(buffer64));
+      break;
+    }
+    case google::protobuf::Field::TYPE_ENUM: {
+      uint32_t buffer32;
+      stream_->ReadVarint32(&buffer32);
+      // Get the nested enum type for this field.
+      // TODO(skarvaje): Avoid string manipulation. Find ways to speed this
+      // up.
+      const google::protobuf::Enum* en =
+          typeinfo_->GetEnumByTypeUrl(field.type_url());
+      // Lookup the name of the enum, and render that. Skips unknown enums.
+      if (en != nullptr) {
+        const google::protobuf::EnumValue* enum_value =
+            FindEnumValueByNumber(*en, buffer32);
+        if (enum_value != nullptr) {
+          result = enum_value->name();
+        }
+      }
+      break;
+    }
+    case google::protobuf::Field::TYPE_STRING: {
+      uint32_t buffer32;
+      stream_->ReadVarint32(&buffer32);  // string size.
+      stream_->ReadString(&result, buffer32);
+      break;
+    }
+    case google::protobuf::Field::TYPE_BYTES: {
+      uint32_t buffer32;
+      stream_->ReadVarint32(&buffer32);  // bytes size.
+      stream_->ReadString(&result, buffer32);
+      break;
+    }
+    default:
+      break;
+  }
+  return result;
+}
+
+// Field is a map if it is a repeated message and it has an option "map_type".
+// TODO(skarvaje): Consider pre-computing the IsMap() into Field directly.
+bool ProtoStreamObjectSource::IsMap(
+    const google::protobuf::Field& field) const {
+  const google::protobuf::Type* field_type =
+      typeinfo_->GetTypeByTypeUrl(field.type_url());
+  return field.kind() == google::protobuf::Field::TYPE_MESSAGE &&
+         util::converter::IsMap(field, *field_type);
+}
+
+std::pair<int64_t, int32_t> ProtoStreamObjectSource::ReadSecondsAndNanos(
+    const google::protobuf::Type& type) const {
+  uint64_t seconds = 0;
+  uint32_t nanos = 0;
+  uint32_t tag = 0;
+  int64_t signed_seconds = 0;
+  int32_t signed_nanos = 0;
+
+  for (tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
+    const google::protobuf::Field* field = FindAndVerifyField(type, tag);
+    if (field == nullptr) {
+      WireFormat::SkipField(stream_, tag, nullptr);
+      continue;
+    }
+    // 'seconds' has field number of 1 and 'nanos' has field number 2
+    // //google/protobuf/timestamp.proto & duration.proto
+    if (field->number() == 1) {
+      // read seconds
+      stream_->ReadVarint64(&seconds);
+      signed_seconds = bit_cast<int64_t>(seconds);
+    } else if (field->number() == 2) {
+      // read nanos
+      stream_->ReadVarint32(&nanos);
+      signed_nanos = bit_cast<int32_t>(nanos);
+    }
+  }
+  return std::pair<int64_t, int32_t>(signed_seconds, signed_nanos);
+}
+
+util::Status ProtoStreamObjectSource::IncrementRecursionDepth(
+    StringPiece type_name, StringPiece field_name) const {
+  if (++recursion_depth_ > max_recursion_depth_) {
+    return util::InvalidArgumentError(
+        StrCat("Message too deep. Max recursion depth reached for type '",
+                     type_name, "', field '", field_name, "'"));
+  }
+  return util::Status();
+}
+
+namespace {
+// TODO(skarvaje): Speed this up by not doing a linear scan.
+const google::protobuf::Field* FindFieldByNumber(
+    const google::protobuf::Type& type, int number) {
+  for (int i = 0; i < type.fields_size(); ++i) {
+    if (type.fields(i).number() == number) {
+      return &type.fields(i);
+    }
+  }
+  return nullptr;
+}
+
+// TODO(skarvaje): Replace FieldDescriptor by implementing IsTypePackable()
+// using tech Field.
+bool IsPackable(const google::protobuf::Field& field) {
+  return field.cardinality() == google::protobuf::Field::CARDINALITY_REPEATED &&
+         FieldDescriptor::IsTypePackable(
+             static_cast<FieldDescriptor::Type>(field.kind()));
+}
+
+// TODO(skarvaje): Speed this up by not doing a linear scan.
+const google::protobuf::EnumValue* FindEnumValueByNumber(
+    const google::protobuf::Enum& tech_enum, int number) {
+  for (int i = 0; i < tech_enum.enumvalue_size(); ++i) {
+    const google::protobuf::EnumValue& ev = tech_enum.enumvalue(i);
+    if (ev.number() == number) {
+      return &ev;
+    }
+  }
+  return nullptr;
+}
+
+// TODO(skarvaje): Look into optimizing this by not doing computation on
+// double.
+const std::string FormatNanos(uint32_t nanos, bool with_trailing_zeros) {
+  if (nanos == 0) {
+    return with_trailing_zeros ? ".000" : "";
+  }
+
+  const int precision = (nanos % 1000 != 0)      ? 9
+                        : (nanos % 1000000 != 0) ? 6
+                                                 : 3;
+  std::string formatted = StringPrintf(
+      "%.*f", precision, static_cast<double>(nanos) / kNanosPerSecond);
+  // remove the leading 0 before decimal.
+  return formatted.substr(1);
+}
+}  // namespace
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/protostream_objectwriter.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/protostream_objectwriter.cpp
new file mode 100644
index 0000000..ce94cfc
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/protostream_objectwriter.cpp
@@ -0,0 +1,1401 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/protostream_objectwriter.h>
+
+#include <cstdint>
+#include <functional>
+#include <stack>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/statusor.h>
+#include <google/protobuf/stubs/time.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/field_mask_utility.h>
+#include <google/protobuf/util/internal/object_location_tracker.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/map_util.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using util::Status;
+using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
+using std::placeholders::_1;
+
+
+ProtoStreamObjectWriter::ProtoStreamObjectWriter(
+    TypeResolver* type_resolver, const google::protobuf::Type& type,
+    strings::ByteSink* output, ErrorListener* listener,
+    const ProtoStreamObjectWriter::Options& options)
+    : ProtoWriter(type_resolver, type, output, listener),
+      master_type_(type),
+      current_(nullptr),
+      options_(options) {
+  set_ignore_unknown_fields(options_.ignore_unknown_fields);
+  set_ignore_unknown_enum_values(options_.ignore_unknown_enum_values);
+  set_use_lower_camel_for_enums(options_.use_lower_camel_for_enums);
+  set_case_insensitive_enum_parsing(options_.case_insensitive_enum_parsing);
+  set_use_json_name_in_missing_fields(options.use_json_name_in_missing_fields);
+}
+
+ProtoStreamObjectWriter::ProtoStreamObjectWriter(
+    const TypeInfo* typeinfo, const google::protobuf::Type& type,
+    strings::ByteSink* output, ErrorListener* listener,
+    const ProtoStreamObjectWriter::Options& options)
+    : ProtoWriter(typeinfo, type, output, listener),
+      master_type_(type),
+      current_(nullptr),
+      options_(options) {
+  set_ignore_unknown_fields(options_.ignore_unknown_fields);
+  set_use_lower_camel_for_enums(options.use_lower_camel_for_enums);
+  set_case_insensitive_enum_parsing(options_.case_insensitive_enum_parsing);
+  set_use_json_name_in_missing_fields(options.use_json_name_in_missing_fields);
+}
+
+ProtoStreamObjectWriter::ProtoStreamObjectWriter(
+    const TypeInfo* typeinfo, const google::protobuf::Type& type,
+    strings::ByteSink* output, ErrorListener* listener)
+    : ProtoWriter(typeinfo, type, output, listener),
+      master_type_(type),
+      current_(nullptr),
+      options_(ProtoStreamObjectWriter::Options::Defaults()) {}
+
+ProtoStreamObjectWriter::~ProtoStreamObjectWriter() {
+  if (current_ == nullptr) return;
+  // Cleanup explicitly in order to avoid destructor stack overflow when input
+  // is deeply nested.
+  // Cast to BaseElement to avoid doing additional checks (like missing fields)
+  // during pop().
+  std::unique_ptr<BaseElement> element(
+      static_cast<BaseElement*>(current_.get())->pop<BaseElement>());
+  while (element != nullptr) {
+    element.reset(element->pop<BaseElement>());
+  }
+}
+
+namespace {
+// Utility method to split a string representation of Timestamp or Duration and
+// return the parts.
+void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
+                          StringPiece* nanos) {
+  size_t idx = input.rfind('.');
+  if (idx != std::string::npos) {
+    *seconds = input.substr(0, idx);
+    *nanos = input.substr(idx + 1);
+  } else {
+    *seconds = input;
+    *nanos = StringPiece();
+  }
+}
+
+Status GetNanosFromStringPiece(StringPiece s_nanos,
+                               const char* parse_failure_message,
+                               const char* exceeded_limit_message,
+                               int32_t* nanos) {
+  *nanos = 0;
+
+  // Count the number of leading 0s and consume them.
+  int num_leading_zeros = 0;
+  while (s_nanos.Consume("0")) {
+    num_leading_zeros++;
+  }
+  int32_t i_nanos = 0;
+  // 's_nanos' contains fractional seconds -- i.e. 'nanos' is equal to
+  // "0." + s_nanos.ToString() seconds. An int32_t is used for the
+  // conversion to 'nanos', rather than a double, so that there is no
+  // loss of precision.
+  if (!s_nanos.empty() && !safe_strto32(s_nanos, &i_nanos)) {
+    return util::InvalidArgumentError(parse_failure_message);
+  }
+  if (i_nanos > kNanosPerSecond || i_nanos < 0) {
+    return util::InvalidArgumentError(exceeded_limit_message);
+  }
+  // s_nanos should only have digits. No whitespace.
+  if (s_nanos.find_first_not_of("0123456789") != StringPiece::npos) {
+    return util::InvalidArgumentError(parse_failure_message);
+  }
+
+  if (i_nanos > 0) {
+    // 'scale' is the number of digits to the right of the decimal
+    // point in "0." + s_nanos.ToString()
+    int32_t scale = num_leading_zeros + s_nanos.size();
+    // 'conversion' converts i_nanos into nanoseconds.
+    // conversion = kNanosPerSecond / static_cast<int32_t>(std::pow(10, scale))
+    // For efficiency, we precompute the conversion factor.
+    int32_t conversion = 0;
+    switch (scale) {
+      case 1:
+        conversion = 100000000;
+        break;
+      case 2:
+        conversion = 10000000;
+        break;
+      case 3:
+        conversion = 1000000;
+        break;
+      case 4:
+        conversion = 100000;
+        break;
+      case 5:
+        conversion = 10000;
+        break;
+      case 6:
+        conversion = 1000;
+        break;
+      case 7:
+        conversion = 100;
+        break;
+      case 8:
+        conversion = 10;
+        break;
+      case 9:
+        conversion = 1;
+        break;
+      default:
+        return util::InvalidArgumentError(exceeded_limit_message);
+    }
+    *nanos = i_nanos * conversion;
+  }
+
+  return Status();
+}
+
+}  // namespace
+
+ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent)
+    : parent_(parent),
+      ow_(),
+      invalid_(false),
+      data_(),
+      output_(&data_),
+      depth_(0),
+      is_well_known_type_(false),
+      well_known_type_render_(nullptr) {}
+
+ProtoStreamObjectWriter::AnyWriter::~AnyWriter() {}
+
+void ProtoStreamObjectWriter::AnyWriter::StartObject(StringPiece name) {
+  ++depth_;
+  // If an object writer is absent, that means we have not called StartAny()
+  // before reaching here, which happens when we have data before the "@type"
+  // field.
+  if (ow_ == nullptr) {
+    // Save data before the "@type" field for later replay.
+    uninterpreted_events_.push_back(Event(Event::START_OBJECT, name));
+  } else if (is_well_known_type_ && depth_ == 1) {
+    // For well-known types, the only other field besides "@type" should be a
+    // "value" field.
+    if (name != "value" && !invalid_) {
+      parent_->InvalidValue("Any",
+                            "Expect a \"value\" field for well-known types.");
+      invalid_ = true;
+    }
+    ow_->StartObject("");
+  } else {
+    // Forward the call to the child writer if:
+    //   1. the type is not a well-known type.
+    //   2. or, we are in a nested Any, Struct, or Value object.
+    ow_->StartObject(name);
+  }
+}
+
+bool ProtoStreamObjectWriter::AnyWriter::EndObject() {
+  --depth_;
+  if (ow_ == nullptr) {
+    if (depth_ >= 0) {
+      // Save data before the "@type" field for later replay.
+      uninterpreted_events_.push_back(Event(Event::END_OBJECT));
+    }
+  } else if (depth_ >= 0 || !is_well_known_type_) {
+    // As long as depth_ >= 0, we know we haven't reached the end of Any.
+    // Propagate these EndObject() calls to the contained ow_. For regular
+    // message types, we propagate the end of Any as well.
+    ow_->EndObject();
+  }
+  // A negative depth_ implies that we have reached the end of Any
+  // object. Now we write out its contents.
+  if (depth_ < 0) {
+    WriteAny();
+    return false;
+  }
+  return true;
+}
+
+void ProtoStreamObjectWriter::AnyWriter::StartList(StringPiece name) {
+  ++depth_;
+  if (ow_ == nullptr) {
+    // Save data before the "@type" field for later replay.
+    uninterpreted_events_.push_back(Event(Event::START_LIST, name));
+  } else if (is_well_known_type_ && depth_ == 1) {
+    if (name != "value" && !invalid_) {
+      parent_->InvalidValue("Any",
+                            "Expect a \"value\" field for well-known types.");
+      invalid_ = true;
+    }
+    ow_->StartList("");
+  } else {
+    ow_->StartList(name);
+  }
+}
+
+void ProtoStreamObjectWriter::AnyWriter::EndList() {
+  --depth_;
+  if (depth_ < 0) {
+    GOOGLE_LOG(DFATAL) << "Mismatched EndList found, should not be possible";
+    depth_ = 0;
+  }
+  if (ow_ == nullptr) {
+    // Save data before the "@type" field for later replay.
+    uninterpreted_events_.push_back(Event(Event::END_LIST));
+  } else {
+    ow_->EndList();
+  }
+}
+
+void ProtoStreamObjectWriter::AnyWriter::RenderDataPiece(
+    StringPiece name, const DataPiece& value) {
+  // Start an Any only at depth_ 0. Other RenderDataPiece calls with "@type"
+  // should go to the contained ow_ as they indicate nested Anys.
+  if (depth_ == 0 && ow_ == nullptr && name == "@type") {
+    StartAny(value);
+  } else if (ow_ == nullptr) {
+    // Save data before the "@type" field.
+    uninterpreted_events_.push_back(Event(name, value));
+  } else if (depth_ == 0 && is_well_known_type_) {
+    if (name != "value" && !invalid_) {
+      parent_->InvalidValue("Any",
+                            "Expect a \"value\" field for well-known types.");
+      invalid_ = true;
+    }
+    if (well_known_type_render_ == nullptr) {
+      // Only Any and Struct don't have a special type render but both of
+      // them expect a JSON object (i.e., a StartObject() call).
+      if (value.type() != DataPiece::TYPE_NULL && !invalid_) {
+        parent_->InvalidValue("Any", "Expect a JSON object.");
+        invalid_ = true;
+      }
+    } else {
+      ow_->ProtoWriter::StartObject("");
+      Status status = (*well_known_type_render_)(ow_.get(), value);
+      if (!status.ok()) ow_->InvalidValue("Any", status.message());
+      ow_->ProtoWriter::EndObject();
+    }
+  } else {
+    ow_->RenderDataPiece(name, value);
+  }
+}
+
+void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
+  // Figure out the type url. This is a copy-paste from WriteString but we also
+  // need the value, so we can't just call through to that.
+  if (value.type() == DataPiece::TYPE_STRING) {
+    type_url_ = std::string(value.str());
+  } else {
+    util::StatusOr<std::string> s = value.ToString();
+    if (!s.ok()) {
+      parent_->InvalidValue("String", s.status().message());
+      invalid_ = true;
+      return;
+    }
+    type_url_ = s.value();
+  }
+  // Resolve the type url, and report an error if we failed to resolve it.
+  util::StatusOr<const google::protobuf::Type*> resolved_type =
+      parent_->typeinfo()->ResolveTypeUrl(type_url_);
+  if (!resolved_type.ok()) {
+    parent_->InvalidValue("Any", resolved_type.status().message());
+    invalid_ = true;
+    return;
+  }
+  // At this point, type is never null.
+  const google::protobuf::Type* type = resolved_type.value();
+
+  well_known_type_render_ = FindTypeRenderer(type_url_);
+  if (well_known_type_render_ != nullptr ||
+      // Explicitly list Any and Struct here because they don't have a
+      // custom renderer.
+      type->name() == kAnyType || type->name() == kStructType) {
+    is_well_known_type_ = true;
+  }
+
+  // Create our object writer and initialize it with the first StartObject
+  // call.
+  ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_,
+                                        parent_->listener(),
+                                        parent_->options_));
+
+  // Don't call StartObject() for well-known types yet. Depending on the
+  // type of actual data, we may not need to call StartObject(). For
+  // example:
+  // {
+  //   "@type": "type.googleapis.com/google.protobuf.Value",
+  //   "value": [1, 2, 3],
+  // }
+  // With the above JSON representation, we will only call StartList() on the
+  // contained ow_.
+  if (!is_well_known_type_) {
+    ow_->StartObject("");
+  }
+
+  // Now we know the proto type and can interpret all data fields we gathered
+  // before the "@type" field.
+  for (size_t i = 0; i < uninterpreted_events_.size(); ++i) {
+    uninterpreted_events_[i].Replay(this);
+  }
+}
+
+void ProtoStreamObjectWriter::AnyWriter::WriteAny() {
+  if (ow_ == nullptr) {
+    if (uninterpreted_events_.empty()) {
+      // We never got any content, so just return immediately, which is
+      // equivalent to writing an empty Any.
+      return;
+    } else {
+      // There are uninterpreted data, but we never got a "@type" field.
+      if (!invalid_) {
+        parent_->InvalidValue("Any",
+                              StrCat("Missing @type for any field in ",
+                                           parent_->master_type_.name()));
+        invalid_ = true;
+      }
+      return;
+    }
+  }
+  // Render the type_url and value fields directly to the stream.
+  // type_url has tag 1 and value has tag 2.
+  WireFormatLite::WriteString(1, type_url_, parent_->stream());
+  if (!data_.empty()) {
+    WireFormatLite::WriteBytes(2, data_, parent_->stream());
+  }
+}
+
+void ProtoStreamObjectWriter::AnyWriter::Event::Replay(
+    AnyWriter* writer) const {
+  switch (type_) {
+    case START_OBJECT:
+      writer->StartObject(name_);
+      break;
+    case END_OBJECT:
+      writer->EndObject();
+      break;
+    case START_LIST:
+      writer->StartList(name_);
+      break;
+    case END_LIST:
+      writer->EndList();
+      break;
+    case RENDER_DATA_PIECE:
+      writer->RenderDataPiece(name_, value_);
+      break;
+  }
+}
+
+void ProtoStreamObjectWriter::AnyWriter::Event::DeepCopy() {
+  // DataPiece only contains a string reference. To make sure the referenced
+  // string value stays valid, we make a copy of the string value and update
+  // DataPiece to reference our own copy.
+  if (value_.type() == DataPiece::TYPE_STRING) {
+    StrAppend(&value_storage_, value_.str());
+    value_ = DataPiece(value_storage_, value_.use_strict_base64_decoding());
+  } else if (value_.type() == DataPiece::TYPE_BYTES) {
+    value_storage_ = value_.ToBytes().value();
+    value_ =
+        DataPiece(value_storage_, true, value_.use_strict_base64_decoding());
+  }
+}
+
+ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter* enclosing,
+                                    ItemType item_type, bool is_placeholder,
+                                    bool is_list)
+    : BaseElement(nullptr),
+      ow_(enclosing),
+      any_(),
+      item_type_(item_type),
+      is_placeholder_(is_placeholder),
+      is_list_(is_list) {
+  if (item_type_ == ANY) {
+    any_.reset(new AnyWriter(ow_));
+  }
+  if (item_type == MAP) {
+    map_keys_.reset(new std::unordered_set<std::string>);
+  }
+}
+
+ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent,
+                                    ItemType item_type, bool is_placeholder,
+                                    bool is_list)
+    : BaseElement(parent),
+      ow_(this->parent()->ow_),
+      any_(),
+      item_type_(item_type),
+      is_placeholder_(is_placeholder),
+      is_list_(is_list) {
+  if (item_type == ANY) {
+    any_.reset(new AnyWriter(ow_));
+  }
+  if (item_type == MAP) {
+    map_keys_.reset(new std::unordered_set<std::string>);
+  }
+}
+
+bool ProtoStreamObjectWriter::Item::InsertMapKeyIfNotPresent(
+    StringPiece map_key) {
+  return InsertIfNotPresent(map_keys_.get(), std::string(map_key));
+}
+
+
+ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject(
+    StringPiece name) {
+  if (invalid_depth() > 0) {
+    IncrementInvalidDepth();
+    return this;
+  }
+
+  // Starting the root message. Create the root Item and return.
+  // ANY message type does not need special handling, just set the ItemType
+  // to ANY.
+  if (current_ == nullptr) {
+    ProtoWriter::StartObject(name);
+    current_.reset(new Item(
+        this, master_type_.name() == kAnyType ? Item::ANY : Item::MESSAGE,
+        false, false));
+
+    // If master type is a special type that needs extra values to be written to
+    // stream, we write those values.
+    if (master_type_.name() == kStructType) {
+      // Struct has a map<string, Value> field called "fields".
+      // https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/struct.proto
+      // "fields": [
+      Push("fields", Item::MAP, true, true);
+      return this;
+    }
+
+    if (master_type_.name() == kStructValueType) {
+      // We got a StartObject call with google.protobuf.Value field. The only
+      // object within that type is a struct type. So start a struct.
+      //
+      // The struct field in Value type is named "struct_value"
+      // https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/struct.proto
+      // Also start the map field "fields" within the struct.
+      // "struct_value": {
+      //   "fields": [
+      Push("struct_value", Item::MESSAGE, true, false);
+      Push("fields", Item::MAP, true, true);
+      return this;
+    }
+
+    if (master_type_.name() == kStructListValueType) {
+      InvalidValue(kStructListValueType,
+                   "Cannot start root message with ListValue.");
+    }
+
+    return this;
+  }
+
+  // Send all ANY events to AnyWriter.
+  if (current_->IsAny()) {
+    current_->any()->StartObject(name);
+    return this;
+  }
+
+  // If we are within a map, we render name as keys and send StartObject to the
+  // value field.
+  if (current_->IsMap()) {
+    if (!ValidMapKey(name)) {
+      IncrementInvalidDepth();
+      return this;
+    }
+
+    // Map is a repeated field of message type with a "key" and a "value" field.
+    // https://developers.google.com/protocol-buffers/docs/proto3?hl=en#maps
+    // message MapFieldEntry {
+    //   key_type key = 1;
+    //   value_type value = 2;
+    // }
+    //
+    // repeated MapFieldEntry map_field = N;
+    //
+    // That means, we render the following element within a list (hence no
+    // name):
+    // { "key": "<name>", "value": {
+    Push("", Item::MESSAGE, false, false);
+    ProtoWriter::RenderDataPiece("key",
+                                 DataPiece(name, use_strict_base64_decoding()));
+    Push("value", IsAny(*Lookup("value")) ? Item::ANY : Item::MESSAGE, true,
+         false);
+
+    // Make sure we are valid so far after starting map fields.
+    if (invalid_depth() > 0) return this;
+
+    // If top of stack is g.p.Struct type, start the struct the map field within
+    // it.
+    if (element() != nullptr && IsStruct(*element()->parent_field())) {
+      // Render "fields": [
+      Push("fields", Item::MAP, true, true);
+      return this;
+    }
+
+    // If top of stack is g.p.Value type, start the Struct within it.
+    if (element() != nullptr && IsStructValue(*element()->parent_field())) {
+      // Render
+      // "struct_value": {
+      //   "fields": [
+      Push("struct_value", Item::MESSAGE, true, false);
+      Push("fields", Item::MAP, true, true);
+    }
+    return this;
+  }
+
+  const google::protobuf::Field* field = BeginNamed(name, false);
+
+  if (field == nullptr) return this;
+
+  // Legacy JSON map is a list of key value pairs. Starts a map entry object.
+  if (options_.use_legacy_json_map_format && name.empty()) {
+    Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false);
+    return this;
+  }
+
+  if (IsMap(*field)) {
+    // Begin a map. A map is triggered by a StartObject() call if the current
+    // field has a map type.
+    // A map type is always repeated, hence set is_list to true.
+    // Render
+    // "<name>": [
+    Push(name, Item::MAP, false, true);
+    return this;
+  }
+
+  if (options_.disable_implicit_message_list) {
+    // If the incoming object is repeated, the top-level object on stack should
+    // be list. Report an error otherwise.
+    if (IsRepeated(*field) && !current_->is_list()) {
+      IncrementInvalidDepth();
+
+      if (!options_.suppress_implicit_message_list_error) {
+        InvalidValue(
+            field->name(),
+            "Starting an object in a repeated field but the parent object "
+            "is not a list");
+      }
+      return this;
+    }
+  }
+
+  if (IsStruct(*field)) {
+    // Start a struct object.
+    // Render
+    // "<name>": {
+    //   "fields": {
+    Push(name, Item::MESSAGE, false, false);
+    Push("fields", Item::MAP, true, true);
+    return this;
+  }
+
+  if (IsStructValue(*field)) {
+    // We got a StartObject call with google.protobuf.Value field.  The only
+    // object within that type is a struct type. So start a struct.
+    // Render
+    // "<name>": {
+    //   "struct_value": {
+    //     "fields": {
+    Push(name, Item::MESSAGE, false, false);
+    Push("struct_value", Item::MESSAGE, true, false);
+    Push("fields", Item::MAP, true, true);
+    return this;
+  }
+
+  if (field->kind() != google::protobuf::Field::TYPE_GROUP &&
+      field->kind() != google::protobuf::Field::TYPE_MESSAGE) {
+    IncrementInvalidDepth();
+    if (!options_.suppress_object_to_scalar_error) {
+      InvalidValue(field->name(), "Starting an object on a scalar field");
+    }
+
+    return this;
+  }
+
+  // A regular message type. Pass it directly to ProtoWriter.
+  // Render
+  // "<name>": {
+  Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false);
+  return this;
+}
+
+ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() {
+  if (invalid_depth() > 0) {
+    DecrementInvalidDepth();
+    return this;
+  }
+
+  if (current_ == nullptr) return this;
+
+  if (current_->IsAny()) {
+    if (current_->any()->EndObject()) return this;
+  }
+
+  Pop();
+
+  return this;
+}
+
+
+ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(
+    StringPiece name) {
+  if (invalid_depth() > 0) {
+    IncrementInvalidDepth();
+    return this;
+  }
+
+  // Since we cannot have a top-level repeated item in protobuf, the only way
+  // this is valid is if we start a special type google.protobuf.ListValue or
+  // google.protobuf.Value.
+  if (current_ == nullptr) {
+    if (!name.empty()) {
+      InvalidName(name, "Root element should not be named.");
+      IncrementInvalidDepth();
+      return this;
+    }
+
+    // If master type is a special type that needs extra values to be written to
+    // stream, we write those values.
+    if (master_type_.name() == kStructValueType) {
+      // We got a StartList with google.protobuf.Value master type. This means
+      // we have to start the "list_value" within google.protobuf.Value.
+      //
+      // See
+      // https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/struct.proto
+      //
+      // Render
+      // "<name>": {
+      //   "list_value": {
+      //     "values": [  // Start this list.
+      ProtoWriter::StartObject(name);
+      current_.reset(new Item(this, Item::MESSAGE, false, false));
+      Push("list_value", Item::MESSAGE, true, false);
+      Push("values", Item::MESSAGE, true, true);
+      return this;
+    }
+
+    if (master_type_.name() == kStructListValueType) {
+      // We got a StartList with google.protobuf.ListValue master type. This
+      // means we have to start the "values" within google.protobuf.ListValue.
+      //
+      // Render
+      // "<name>": {
+      //   "values": [  // Start this list.
+      ProtoWriter::StartObject(name);
+      current_.reset(new Item(this, Item::MESSAGE, false, false));
+      Push("values", Item::MESSAGE, true, true);
+      return this;
+    }
+
+    // Send the event to ProtoWriter so proper errors can be reported.
+    //
+    // Render a regular list:
+    // "<name>": [
+    ProtoWriter::StartList(name);
+    current_.reset(new Item(this, Item::MESSAGE, false, true));
+    return this;
+  }
+
+  if (current_->IsAny()) {
+    current_->any()->StartList(name);
+    return this;
+  }
+
+  // If the top of stack is a map, we are starting a list value within a map.
+  // Since map does not allow repeated values, this can only happen when the map
+  // value is of a special type that renders a list in JSON.  These can be one
+  // of 3 cases:
+  // i. We are rendering a list value within google.protobuf.Struct
+  // ii. We are rendering a list value within google.protobuf.Value
+  // iii. We are rendering a list value with type google.protobuf.ListValue.
+  if (current_->IsMap()) {
+    if (!ValidMapKey(name)) {
+      IncrementInvalidDepth();
+      return this;
+    }
+
+    // Start the repeated map entry object.
+    // Render
+    // { "key": "<name>", "value": {
+    Push("", Item::MESSAGE, false, false);
+    ProtoWriter::RenderDataPiece("key",
+                                 DataPiece(name, use_strict_base64_decoding()));
+    Push("value", Item::MESSAGE, true, false);
+
+    // Make sure we are valid after pushing all above items.
+    if (invalid_depth() > 0) return this;
+
+    // case i and ii above. Start "list_value" field within g.p.Value
+    if (element() != nullptr && element()->parent_field() != nullptr) {
+      // Render
+      // "list_value": {
+      //   "values": [  // Start this list
+      if (IsStructValue(*element()->parent_field())) {
+        Push("list_value", Item::MESSAGE, true, false);
+        Push("values", Item::MESSAGE, true, true);
+        return this;
+      }
+
+      // Render
+      // "values": [
+      if (IsStructListValue(*element()->parent_field())) {
+        // case iii above. Bind directly to g.p.ListValue
+        Push("values", Item::MESSAGE, true, true);
+        return this;
+      }
+    }
+
+    // Report an error.
+    InvalidValue("Map", StrCat("Cannot have repeated items ('", name,
+                                     "') within a map."));
+    return this;
+  }
+
+  // When name is empty and stack is not empty, we are rendering an item within
+  // a list.
+  if (name.empty()) {
+    if (element() != nullptr && element()->parent_field() != nullptr) {
+      if (IsStructValue(*element()->parent_field())) {
+        // Since it is g.p.Value, we bind directly to the list_value.
+        // Render
+        // {  // g.p.Value item within the list
+        //   "list_value": {
+        //     "values": [
+        Push("", Item::MESSAGE, false, false);
+        Push("list_value", Item::MESSAGE, true, false);
+        Push("values", Item::MESSAGE, true, true);
+        return this;
+      }
+
+      if (IsStructListValue(*element()->parent_field())) {
+        // Since it is g.p.ListValue, we bind to it directly.
+        // Render
+        // {  // g.p.ListValue item within the list
+        //   "values": [
+        Push("", Item::MESSAGE, false, false);
+        Push("values", Item::MESSAGE, true, true);
+        return this;
+      }
+    }
+
+    // Pass the event to underlying ProtoWriter.
+    Push(name, Item::MESSAGE, false, true);
+    return this;
+  }
+
+  // name is not empty
+  const google::protobuf::Field* field = Lookup(name);
+
+  if (field == nullptr) {
+    IncrementInvalidDepth();
+    return this;
+  }
+
+  if (IsStructValue(*field)) {
+    // If g.p.Value is repeated, start that list. Otherwise, start the
+    // "list_value" within it.
+    if (IsRepeated(*field)) {
+      // Render it just like a regular repeated field.
+      // "<name>": [
+      Push(name, Item::MESSAGE, false, true);
+      return this;
+    }
+
+    // Start the "list_value" field.
+    // Render
+    // "<name>": {
+    //   "list_value": {
+    //     "values": [
+    Push(name, Item::MESSAGE, false, false);
+    Push("list_value", Item::MESSAGE, true, false);
+    Push("values", Item::MESSAGE, true, true);
+    return this;
+  }
+
+  if (IsStructListValue(*field)) {
+    // If g.p.ListValue is repeated, start that list. Otherwise, start the
+    // "values" within it.
+    if (IsRepeated(*field)) {
+      // Render it just like a regular repeated field.
+      // "<name>": [
+      Push(name, Item::MESSAGE, false, true);
+      return this;
+    }
+
+    // Start the "values" field within g.p.ListValue.
+    // Render
+    // "<name>": {
+    //   "values": [
+    Push(name, Item::MESSAGE, false, false);
+    Push("values", Item::MESSAGE, true, true);
+    return this;
+  }
+
+  // If we are here, the field should be repeated. Report an error otherwise.
+  if (!IsRepeated(*field)) {
+    IncrementInvalidDepth();
+    InvalidName(name, "Proto field is not repeating, cannot start list.");
+    return this;
+  }
+
+  if (IsMap(*field)) {
+    if (options_.use_legacy_json_map_format) {
+      Push(name, Item::MESSAGE, false, true);
+      return this;
+    }
+    InvalidValue("Map", StrCat("Cannot bind a list to map for field '",
+                                     name, "'."));
+    IncrementInvalidDepth();
+    return this;
+  }
+
+  // Pass the event to ProtoWriter.
+  // Render
+  // "<name>": [
+  Push(name, Item::MESSAGE, false, true);
+  return this;
+}
+
+ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndList() {
+  if (invalid_depth() > 0) {
+    DecrementInvalidDepth();
+    return this;
+  }
+
+  if (current_ == nullptr) return this;
+
+  if (current_->IsAny()) {
+    current_->any()->EndList();
+    return this;
+  }
+
+  Pop();
+  return this;
+}
+
+Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow,
+                                                  const DataPiece& data) {
+  std::string struct_field_name;
+  switch (data.type()) {
+    case DataPiece::TYPE_INT32: {
+      if (ow->options_.struct_integers_as_strings) {
+        util::StatusOr<int32_t> int_value = data.ToInt32();
+        if (int_value.ok()) {
+          ow->ProtoWriter::RenderDataPiece(
+              "string_value",
+              DataPiece(SimpleDtoa(int_value.value()), true));
+          return Status();
+        }
+      }
+      struct_field_name = "number_value";
+      break;
+    }
+    case DataPiece::TYPE_UINT32: {
+      if (ow->options_.struct_integers_as_strings) {
+        util::StatusOr<uint32_t> int_value = data.ToUint32();
+        if (int_value.ok()) {
+          ow->ProtoWriter::RenderDataPiece(
+              "string_value",
+              DataPiece(SimpleDtoa(int_value.value()), true));
+          return Status();
+        }
+      }
+      struct_field_name = "number_value";
+      break;
+    }
+    case DataPiece::TYPE_INT64: {
+      // If the option to treat integers as strings is set, then render them as
+      // strings. Otherwise, fallback to rendering them as double.
+      if (ow->options_.struct_integers_as_strings) {
+        util::StatusOr<int64_t> int_value = data.ToInt64();
+        if (int_value.ok()) {
+          ow->ProtoWriter::RenderDataPiece(
+              "string_value", DataPiece(StrCat(int_value.value()), true));
+          return Status();
+        }
+      }
+      struct_field_name = "number_value";
+      break;
+    }
+    case DataPiece::TYPE_UINT64: {
+      // If the option to treat integers as strings is set, then render them as
+      // strings. Otherwise, fallback to rendering them as double.
+      if (ow->options_.struct_integers_as_strings) {
+        util::StatusOr<uint64_t> int_value = data.ToUint64();
+        if (int_value.ok()) {
+          ow->ProtoWriter::RenderDataPiece(
+              "string_value", DataPiece(StrCat(int_value.value()), true));
+          return Status();
+        }
+      }
+      struct_field_name = "number_value";
+      break;
+    }
+    case DataPiece::TYPE_FLOAT: {
+      if (ow->options_.struct_integers_as_strings) {
+        util::StatusOr<float> float_value = data.ToFloat();
+        if (float_value.ok()) {
+          ow->ProtoWriter::RenderDataPiece(
+              "string_value",
+              DataPiece(SimpleDtoa(float_value.value()), true));
+          return Status();
+        }
+      }
+      struct_field_name = "number_value";
+      break;
+    }
+    case DataPiece::TYPE_DOUBLE: {
+      if (ow->options_.struct_integers_as_strings) {
+        util::StatusOr<double> double_value = data.ToDouble();
+        if (double_value.ok()) {
+          ow->ProtoWriter::RenderDataPiece(
+              "string_value",
+              DataPiece(SimpleDtoa(double_value.value()), true));
+          return Status();
+        }
+      }
+      struct_field_name = "number_value";
+      break;
+    }
+    case DataPiece::TYPE_STRING: {
+      struct_field_name = "string_value";
+      break;
+    }
+    case DataPiece::TYPE_BOOL: {
+      struct_field_name = "bool_value";
+      break;
+    }
+    case DataPiece::TYPE_NULL: {
+      struct_field_name = "null_value";
+      break;
+    }
+    default: {
+      return util::InvalidArgumentError(
+          "Invalid struct data type. Only number, string, boolean or  null "
+          "values are supported.");
+    }
+  }
+  ow->ProtoWriter::RenderDataPiece(struct_field_name, data);
+  return Status();
+}
+
+Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow,
+                                                const DataPiece& data) {
+  if (data.type() == DataPiece::TYPE_NULL) return Status();
+  if (data.type() != DataPiece::TYPE_STRING) {
+    return util::InvalidArgumentError(
+        StrCat("Invalid data type for timestamp, value is ",
+                     data.ValueAsStringOrDefault("")));
+  }
+
+  StringPiece value(data.str());
+
+  int64_t seconds;
+  int32_t nanos;
+  if (!::google::protobuf::internal::ParseTime(value.ToString(), &seconds,
+                                               &nanos)) {
+    return util::InvalidArgumentError(StrCat("Invalid time format: ", value));
+  }
+
+
+  ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
+  ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
+  return Status();
+}
+
+static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow,
+                                              StringPiece path) {
+  ow->ProtoWriter::RenderDataPiece(
+      "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase), true));
+  return Status();
+}
+
+Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow,
+                                                const DataPiece& data) {
+  if (data.type() == DataPiece::TYPE_NULL) return Status();
+  if (data.type() != DataPiece::TYPE_STRING) {
+    return util::InvalidArgumentError(
+        StrCat("Invalid data type for field mask, value is ",
+                     data.ValueAsStringOrDefault("")));
+  }
+
+  // TODO(tsun): figure out how to do proto descriptor based snake case
+  // conversions as much as possible. Because ToSnakeCase sometimes returns the
+  // wrong value.
+  return DecodeCompactFieldMaskPaths(data.str(),
+                                     std::bind(&RenderOneFieldPath, ow, _1));
+}
+
+Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow,
+                                               const DataPiece& data) {
+  if (data.type() == DataPiece::TYPE_NULL) return Status();
+  if (data.type() != DataPiece::TYPE_STRING) {
+    return util::InvalidArgumentError(
+        StrCat("Invalid data type for duration, value is ",
+                     data.ValueAsStringOrDefault("")));
+  }
+
+  StringPiece value(data.str());
+
+  if (!HasSuffixString(value, "s")) {
+    return util::InvalidArgumentError(
+        "Illegal duration format; duration must end with 's'");
+  }
+  value = value.substr(0, value.size() - 1);
+  int sign = 1;
+  if (HasPrefixString(value, "-")) {
+    sign = -1;
+    value = value.substr(1);
+  }
+
+  StringPiece s_secs, s_nanos;
+  SplitSecondsAndNanos(value, &s_secs, &s_nanos);
+  uint64_t unsigned_seconds;
+  if (!safe_strtou64(s_secs, &unsigned_seconds)) {
+    return util::InvalidArgumentError(
+        "Invalid duration format, failed to parse seconds");
+  }
+
+  int32_t nanos = 0;
+  Status nanos_status = GetNanosFromStringPiece(
+      s_nanos, "Invalid duration format, failed to parse nano seconds",
+      "Duration value exceeds limits", &nanos);
+  if (!nanos_status.ok()) {
+    return nanos_status;
+  }
+  nanos = sign * nanos;
+
+  int64_t seconds = sign * unsigned_seconds;
+  if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds ||
+      nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
+    return util::InvalidArgumentError("Duration value exceeds limits");
+  }
+
+  ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
+  ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
+  return Status();
+}
+
+Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow,
+                                                  const DataPiece& data) {
+  if (data.type() == DataPiece::TYPE_NULL) return Status();
+  ow->ProtoWriter::RenderDataPiece("value", data);
+  return Status();
+}
+
+ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece(
+    StringPiece name, const DataPiece& data) {
+  Status status;
+  if (invalid_depth() > 0) return this;
+
+  if (current_ == nullptr) {
+    const TypeRenderer* type_renderer =
+        FindTypeRenderer(GetFullTypeWithUrl(master_type_.name()));
+    if (type_renderer == nullptr) {
+      InvalidName(name, "Root element must be a message.");
+      return this;
+    }
+    // Render the special type.
+    // "<name>": {
+    //   ... Render special type ...
+    // }
+    ProtoWriter::StartObject(name);
+    status = (*type_renderer)(this, data);
+    if (!status.ok()) {
+      InvalidValue(master_type_.name(),
+                   StrCat("Field '", name, "', ", status.message()));
+    }
+    ProtoWriter::EndObject();
+    return this;
+  }
+
+  if (current_->IsAny()) {
+    current_->any()->RenderDataPiece(name, data);
+    return this;
+  }
+
+  const google::protobuf::Field* field = nullptr;
+  if (current_->IsMap()) {
+    if (!ValidMapKey(name)) return this;
+
+    field = Lookup("value");
+    if (field == nullptr) {
+      GOOGLE_LOG(DFATAL) << "Map does not have a value field.";
+      return this;
+    }
+
+    if (options_.ignore_null_value_map_entry) {
+      // If we are rendering explicit null values and the backend proto field is
+      // not of the google.protobuf.NullType type, interpret null as absence.
+      if (data.type() == DataPiece::TYPE_NULL &&
+          field->type_url() != kStructNullValueTypeUrl) {
+        return this;
+      }
+    }
+
+    // Render an item in repeated map list.
+    // { "key": "<name>", "value":
+    Push("", Item::MESSAGE, false, false);
+    ProtoWriter::RenderDataPiece("key",
+                                 DataPiece(name, use_strict_base64_decoding()));
+
+    const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
+    if (type_renderer != nullptr) {
+      // Map's value type is a special type. Render it like a message:
+      // "value": {
+      //   ... Render special type ...
+      // }
+      Push("value", Item::MESSAGE, true, false);
+      status = (*type_renderer)(this, data);
+      if (!status.ok()) {
+        InvalidValue(field->type_url(),
+                     StrCat("Field '", name, "', ", status.message()));
+      }
+      Pop();
+      return this;
+    }
+
+    // If we are rendering explicit null values and the backend proto field is
+    // not of the google.protobuf.NullType type, we do nothing.
+    if (data.type() == DataPiece::TYPE_NULL &&
+        field->type_url() != kStructNullValueTypeUrl) {
+      Pop();
+      return this;
+    }
+
+    // Render the map value as a primitive type.
+    ProtoWriter::RenderDataPiece("value", data);
+    Pop();
+    return this;
+  }
+
+  field = Lookup(name);
+  if (field == nullptr) return this;
+
+  // Check if the field is of special type. Render it accordingly if so.
+  const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
+  if (type_renderer != nullptr) {
+    // Pass through null value only for google.protobuf.Value. For other
+    // types we ignore null value just like for regular field types.
+    if (data.type() != DataPiece::TYPE_NULL ||
+        field->type_url() == kStructValueTypeUrl) {
+      Push(name, Item::MESSAGE, false, false);
+      status = (*type_renderer)(this, data);
+      if (!status.ok()) {
+        InvalidValue(field->type_url(),
+                     StrCat("Field '", name, "', ", status.message()));
+      }
+      Pop();
+    }
+    return this;
+  }
+
+  // If we are rendering explicit null values and the backend proto field is
+  // not of the google.protobuf.NullType type, we do nothing.
+  if (data.type() == DataPiece::TYPE_NULL &&
+      field->type_url() != kStructNullValueTypeUrl) {
+    return this;
+  }
+
+  if (IsRepeated(*field) && !current_->is_list()) {
+    if (options_.disable_implicit_scalar_list) {
+      if (!options_.suppress_implicit_scalar_list_error) {
+        InvalidValue(
+            field->name(),
+            "Starting an primitive in a repeated field but the parent field "
+            "is not a list");
+      }
+
+      return this;
+    }
+  }
+
+  ProtoWriter::RenderDataPiece(name, data);
+  return this;
+}
+
+// Map of functions that are responsible for rendering well known type
+// represented by the key.
+std::unordered_map<std::string, ProtoStreamObjectWriter::TypeRenderer>*
+    ProtoStreamObjectWriter::renderers_ = nullptr;
+PROTOBUF_NAMESPACE_ID::internal::once_flag writer_renderers_init_;
+
+void ProtoStreamObjectWriter::InitRendererMap() {
+  renderers_ = new std::unordered_map<std::string,
+                                      ProtoStreamObjectWriter::TypeRenderer>();
+  (*renderers_)["type.googleapis.com/google.protobuf.Timestamp"] =
+      &ProtoStreamObjectWriter::RenderTimestamp;
+  (*renderers_)["type.googleapis.com/google.protobuf.Duration"] =
+      &ProtoStreamObjectWriter::RenderDuration;
+  (*renderers_)["type.googleapis.com/google.protobuf.FieldMask"] =
+      &ProtoStreamObjectWriter::RenderFieldMask;
+  (*renderers_)["type.googleapis.com/google.protobuf.Double"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.Float"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.Int64"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.UInt64"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.Int32"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.UInt32"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.Bool"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.String"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.Bytes"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.DoubleValue"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.FloatValue"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.Int64Value"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.UInt64Value"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.Int32Value"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.UInt32Value"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.BoolValue"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.StringValue"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.BytesValue"] =
+      &ProtoStreamObjectWriter::RenderWrapperType;
+  (*renderers_)["type.googleapis.com/google.protobuf.Value"] =
+      &ProtoStreamObjectWriter::RenderStructValue;
+  ::google::protobuf::internal::OnShutdown(&DeleteRendererMap);
+}
+
+void ProtoStreamObjectWriter::DeleteRendererMap() {
+  delete ProtoStreamObjectWriter::renderers_;
+  renderers_ = nullptr;
+}
+
+ProtoStreamObjectWriter::TypeRenderer*
+ProtoStreamObjectWriter::FindTypeRenderer(const std::string& type_url) {
+  PROTOBUF_NAMESPACE_ID::internal::call_once(writer_renderers_init_,
+                                             InitRendererMap);
+  return FindOrNull(*renderers_, type_url);
+}
+
+bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
+  if (current_ == nullptr) return true;
+
+  if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) {
+    listener()->InvalidName(
+        location(), unnormalized_name,
+        StrCat("Repeated map key: '", unnormalized_name,
+                     "' is already set."));
+    return false;
+  }
+
+  return true;
+}
+
+void ProtoStreamObjectWriter::Push(
+    StringPiece name, Item::ItemType item_type, bool is_placeholder,
+    bool is_list) {
+  is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name);
+
+  // invalid_depth == 0 means it is a successful StartObject or StartList.
+  if (invalid_depth() == 0)
+    current_.reset(
+        new Item(current_.release(), item_type, is_placeholder, is_list));
+}
+
+void ProtoStreamObjectWriter::Pop() {
+  // Pop all placeholder items sending StartObject or StartList events to
+  // ProtoWriter according to is_list value.
+  while (current_ != nullptr && current_->is_placeholder()) {
+    PopOneElement();
+  }
+  if (current_ != nullptr) {
+    PopOneElement();
+  }
+}
+
+void ProtoStreamObjectWriter::PopOneElement() {
+  current_->is_list() ? ProtoWriter::EndList() : ProtoWriter::EndObject();
+  current_.reset(current_->pop<Item>());
+}
+
+bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
+  if (field.type_url().empty() ||
+      field.kind() != google::protobuf::Field::TYPE_MESSAGE ||
+      field.cardinality() != google::protobuf::Field::CARDINALITY_REPEATED) {
+    return false;
+  }
+  const google::protobuf::Type* field_type =
+      typeinfo()->GetTypeByTypeUrl(field.type_url());
+
+  return converter::IsMap(field, *field_type);
+}
+
+bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) {
+  return GetTypeWithoutUrl(field.type_url()) == kAnyType;
+}
+
+bool ProtoStreamObjectWriter::IsStruct(const google::protobuf::Field& field) {
+  return GetTypeWithoutUrl(field.type_url()) == kStructType;
+}
+
+bool ProtoStreamObjectWriter::IsStructValue(
+    const google::protobuf::Field& field) {
+  return GetTypeWithoutUrl(field.type_url()) == kStructValueType;
+}
+
+bool ProtoStreamObjectWriter::IsStructListValue(
+    const google::protobuf::Field& field) {
+  return GetTypeWithoutUrl(field.type_url()) == kStructListValueType;
+}
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/type_info.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/type_info.cpp
new file mode 100644
index 0000000..b6cf536
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/type_info.cpp
@@ -0,0 +1,182 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/type_info.h>
+
+#include <map>
+#include <set>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/statusor.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/status.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+namespace {
+// A TypeInfo that looks up information provided by a TypeResolver.
+class TypeInfoForTypeResolver : public TypeInfo {
+ public:
+  explicit TypeInfoForTypeResolver(TypeResolver* type_resolver)
+      : type_resolver_(type_resolver) {}
+
+  ~TypeInfoForTypeResolver() override {
+    DeleteCachedTypes(&cached_types_);
+    DeleteCachedTypes(&cached_enums_);
+  }
+
+  util::StatusOr<const google::protobuf::Type*> ResolveTypeUrl(
+      StringPiece type_url) const override {
+    std::map<StringPiece, StatusOrType>::iterator it =
+        cached_types_.find(type_url);
+    if (it != cached_types_.end()) {
+      return it->second;
+    }
+    // Stores the string value so it can be referenced using StringPiece in the
+    // cached_types_ map.
+    const std::string& string_type_url =
+        *string_storage_.insert(std::string(type_url)).first;
+    std::unique_ptr<google::protobuf::Type> type(new google::protobuf::Type());
+    util::Status status =
+        type_resolver_->ResolveMessageType(string_type_url, type.get());
+    StatusOrType result =
+        status.ok() ? StatusOrType(type.release()) : StatusOrType(status);
+    cached_types_[string_type_url] = result;
+    return result;
+  }
+
+  const google::protobuf::Type* GetTypeByTypeUrl(
+      StringPiece type_url) const override {
+    StatusOrType result = ResolveTypeUrl(type_url);
+    return result.ok() ? result.value() : NULL;
+  }
+
+  const google::protobuf::Enum* GetEnumByTypeUrl(
+      StringPiece type_url) const override {
+    std::map<StringPiece, StatusOrEnum>::iterator it =
+        cached_enums_.find(type_url);
+    if (it != cached_enums_.end()) {
+      return it->second.ok() ? it->second.value() : NULL;
+    }
+    // Stores the string value so it can be referenced using StringPiece in the
+    // cached_enums_ map.
+    const std::string& string_type_url =
+        *string_storage_.insert(std::string(type_url)).first;
+    std::unique_ptr<google::protobuf::Enum> enum_type(
+        new google::protobuf::Enum());
+    util::Status status =
+        type_resolver_->ResolveEnumType(string_type_url, enum_type.get());
+    StatusOrEnum result =
+        status.ok() ? StatusOrEnum(enum_type.release()) : StatusOrEnum(status);
+    cached_enums_[string_type_url] = result;
+    return result.ok() ? result.value() : NULL;
+  }
+
+  const google::protobuf::Field* FindField(
+      const google::protobuf::Type* type,
+      StringPiece camel_case_name) const override {
+    std::map<const google::protobuf::Type*, CamelCaseNameTable>::const_iterator
+        it = indexed_types_.find(type);
+    const CamelCaseNameTable& camel_case_name_table =
+        (it == indexed_types_.end())
+            ? PopulateNameLookupTable(type, &indexed_types_[type])
+            : it->second;
+    StringPiece name = FindWithDefault(
+        camel_case_name_table, camel_case_name, StringPiece());
+    if (name.empty()) {
+      // Didn't find a mapping. Use whatever provided.
+      name = camel_case_name;
+    }
+    return FindFieldInTypeOrNull(type, name);
+  }
+
+ private:
+  typedef util::StatusOr<const google::protobuf::Type*> StatusOrType;
+  typedef util::StatusOr<const google::protobuf::Enum*> StatusOrEnum;
+  typedef std::map<StringPiece, StringPiece> CamelCaseNameTable;
+
+  template <typename T>
+  static void DeleteCachedTypes(std::map<StringPiece, T>* cached_types) {
+    for (typename std::map<StringPiece, T>::iterator it =
+             cached_types->begin();
+         it != cached_types->end(); ++it) {
+      if (it->second.ok()) {
+        delete it->second.value();
+      }
+    }
+  }
+
+  const CamelCaseNameTable& PopulateNameLookupTable(
+      const google::protobuf::Type* type,
+      CamelCaseNameTable* camel_case_name_table) const {
+    for (int i = 0; i < type->fields_size(); ++i) {
+      const google::protobuf::Field& field = type->fields(i);
+      StringPiece name = field.name();
+      StringPiece camel_case_name = field.json_name();
+      const StringPiece* existing = InsertOrReturnExisting(
+          camel_case_name_table, camel_case_name, name);
+      if (existing && *existing != name) {
+        GOOGLE_LOG(WARNING) << "Field '" << name << "' and '" << *existing
+                     << "' map to the same camel case name '" << camel_case_name
+                     << "'.";
+      }
+    }
+    return *camel_case_name_table;
+  }
+
+  TypeResolver* type_resolver_;
+
+  // Stores string values that will be referenced by StringPieces in
+  // cached_types_, cached_enums_.
+  mutable std::set<std::string> string_storage_;
+
+  mutable std::map<StringPiece, StatusOrType> cached_types_;
+  mutable std::map<StringPiece, StatusOrEnum> cached_enums_;
+
+  mutable std::map<const google::protobuf::Type*, CamelCaseNameTable>
+      indexed_types_;
+};
+}  // namespace
+
+TypeInfo* TypeInfo::NewTypeInfo(TypeResolver* type_resolver) {
+  return new TypeInfoForTypeResolver(type_resolver);
+}
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/utility.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/utility.cpp
new file mode 100644
index 0000000..3c4ac08
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/internal/utility.cpp
@@ -0,0 +1,416 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/internal/utility.h>
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <limits>
+
+#include <google/protobuf/stubs/callback.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/wrappers.pb.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/stubs/map_util.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+bool GetBoolOptionOrDefault(
+    const RepeatedPtrField<google::protobuf::Option>& options,
+    StringPiece option_name, bool default_value) {
+  const google::protobuf::Option* opt = FindOptionOrNull(options, option_name);
+  if (opt == nullptr) {
+    return default_value;
+  }
+  return GetBoolFromAny(opt->value());
+}
+
+int64_t GetInt64OptionOrDefault(
+    const RepeatedPtrField<google::protobuf::Option>& options,
+    StringPiece option_name, int64_t default_value) {
+  const google::protobuf::Option* opt = FindOptionOrNull(options, option_name);
+  if (opt == nullptr) {
+    return default_value;
+  }
+  return GetInt64FromAny(opt->value());
+}
+
+double GetDoubleOptionOrDefault(
+    const RepeatedPtrField<google::protobuf::Option>& options,
+    StringPiece option_name, double default_value) {
+  const google::protobuf::Option* opt = FindOptionOrNull(options, option_name);
+  if (opt == nullptr) {
+    return default_value;
+  }
+  return GetDoubleFromAny(opt->value());
+}
+
+std::string GetStringOptionOrDefault(
+    const RepeatedPtrField<google::protobuf::Option>& options,
+    StringPiece option_name, StringPiece default_value) {
+  const google::protobuf::Option* opt = FindOptionOrNull(options, option_name);
+  if (opt == nullptr) {
+    return std::string(default_value);
+  }
+  return GetStringFromAny(opt->value());
+}
+
+template <typename T>
+void ParseFromAny(const std::string& data, T* result) {
+  result->ParseFromString(data);
+}
+
+// Returns a boolean value contained in Any type.
+// TODO(skarvaje): Add type checking & error messages here.
+bool GetBoolFromAny(const google::protobuf::Any& any) {
+  google::protobuf::BoolValue b;
+  ParseFromAny(any.value(), &b);
+  return b.value();
+}
+
+int64_t GetInt64FromAny(const google::protobuf::Any& any) {
+  google::protobuf::Int64Value i;
+  ParseFromAny(any.value(), &i);
+  return i.value();
+}
+
+double GetDoubleFromAny(const google::protobuf::Any& any) {
+  google::protobuf::DoubleValue i;
+  ParseFromAny(any.value(), &i);
+  return i.value();
+}
+
+std::string GetStringFromAny(const google::protobuf::Any& any) {
+  google::protobuf::StringValue s;
+  ParseFromAny(any.value(), &s);
+  return s.value();
+}
+
+const StringPiece GetTypeWithoutUrl(StringPiece type_url) {
+  if (type_url.size() > kTypeUrlSize && type_url[kTypeUrlSize] == '/') {
+    return type_url.substr(kTypeUrlSize + 1);
+  } else {
+    size_t idx = type_url.rfind('/');
+    if (idx != type_url.npos) {
+      type_url.remove_prefix(idx + 1);
+    }
+    return type_url;
+  }
+}
+
+const std::string GetFullTypeWithUrl(StringPiece simple_type) {
+  return StrCat(kTypeServiceBaseUrl, "/", simple_type);
+}
+
+const google::protobuf::Option* FindOptionOrNull(
+    const RepeatedPtrField<google::protobuf::Option>& options,
+    StringPiece option_name) {
+  for (int i = 0; i < options.size(); ++i) {
+    const google::protobuf::Option& opt = options.Get(i);
+    if (opt.name() == option_name) {
+      return &opt;
+    }
+  }
+  return nullptr;
+}
+
+const google::protobuf::Field* FindFieldInTypeOrNull(
+    const google::protobuf::Type* type, StringPiece field_name) {
+  if (type != nullptr) {
+    for (int i = 0; i < type->fields_size(); ++i) {
+      const google::protobuf::Field& field = type->fields(i);
+      if (field.name() == field_name) {
+        return &field;
+      }
+    }
+  }
+  return nullptr;
+}
+
+const google::protobuf::Field* FindJsonFieldInTypeOrNull(
+    const google::protobuf::Type* type, StringPiece json_name) {
+  if (type != nullptr) {
+    for (int i = 0; i < type->fields_size(); ++i) {
+      const google::protobuf::Field& field = type->fields(i);
+      if (field.json_name() == json_name) {
+        return &field;
+      }
+    }
+  }
+  return nullptr;
+}
+
+const google::protobuf::Field* FindFieldInTypeByNumberOrNull(
+    const google::protobuf::Type* type, int32_t number) {
+  if (type != nullptr) {
+    for (int i = 0; i < type->fields_size(); ++i) {
+      const google::protobuf::Field& field = type->fields(i);
+      if (field.number() == number) {
+        return &field;
+      }
+    }
+  }
+  return nullptr;
+}
+
+const google::protobuf::EnumValue* FindEnumValueByNameOrNull(
+    const google::protobuf::Enum* enum_type, StringPiece enum_name) {
+  if (enum_type != nullptr) {
+    for (int i = 0; i < enum_type->enumvalue_size(); ++i) {
+      const google::protobuf::EnumValue& enum_value = enum_type->enumvalue(i);
+      if (enum_value.name() == enum_name) {
+        return &enum_value;
+      }
+    }
+  }
+  return nullptr;
+}
+
+const google::protobuf::EnumValue* FindEnumValueByNumberOrNull(
+    const google::protobuf::Enum* enum_type, int32_t value) {
+  if (enum_type != nullptr) {
+    for (int i = 0; i < enum_type->enumvalue_size(); ++i) {
+      const google::protobuf::EnumValue& enum_value = enum_type->enumvalue(i);
+      if (enum_value.number() == value) {
+        return &enum_value;
+      }
+    }
+  }
+  return nullptr;
+}
+
+const google::protobuf::EnumValue* FindEnumValueByNameWithoutUnderscoreOrNull(
+    const google::protobuf::Enum* enum_type, StringPiece enum_name) {
+  if (enum_type != nullptr) {
+    for (int i = 0; i < enum_type->enumvalue_size(); ++i) {
+      const google::protobuf::EnumValue& enum_value = enum_type->enumvalue(i);
+      std::string enum_name_without_underscore = enum_value.name();
+
+      // Remove underscore from the name.
+      enum_name_without_underscore.erase(
+          std::remove(enum_name_without_underscore.begin(),
+                      enum_name_without_underscore.end(), '_'),
+          enum_name_without_underscore.end());
+      // Make the name uppercase.
+      for (std::string::iterator it = enum_name_without_underscore.begin();
+           it != enum_name_without_underscore.end(); ++it) {
+        *it = ascii_toupper(*it);
+      }
+
+      if (enum_name_without_underscore == enum_name) {
+        return &enum_value;
+      }
+    }
+  }
+  return nullptr;
+}
+
+std::string EnumValueNameToLowerCamelCase(StringPiece input) {
+  std::string input_string(input);
+  std::transform(input_string.begin(), input_string.end(), input_string.begin(),
+                 ::tolower);
+  return ToCamelCase(input_string);
+}
+
+std::string ToCamelCase(StringPiece input) {
+  bool capitalize_next = false;
+  bool was_cap = true;
+  bool is_cap = false;
+  bool first_word = true;
+  std::string result;
+  result.reserve(input.size());
+
+  for (size_t i = 0; i < input.size(); ++i, was_cap = is_cap) {
+    is_cap = ascii_isupper(input[i]);
+    if (input[i] == '_') {
+      capitalize_next = true;
+      if (!result.empty()) first_word = false;
+      continue;
+    } else if (first_word) {
+      // Consider when the current character B is capitalized,
+      // first word ends when:
+      // 1) following a lowercase:   "...aB..."
+      // 2) followed by a lowercase: "...ABc..."
+      if (!result.empty() && is_cap &&
+          (!was_cap ||
+           (i + 1 < input.size() && ascii_islower(input[i + 1])))) {
+        first_word = false;
+        result.push_back(input[i]);
+      } else {
+        result.push_back(ascii_tolower(input[i]));
+        continue;
+      }
+    } else if (capitalize_next) {
+      capitalize_next = false;
+      if (ascii_islower(input[i])) {
+        result.push_back(ascii_toupper(input[i]));
+        continue;
+      } else {
+        result.push_back(input[i]);
+        continue;
+      }
+    } else {
+      result.push_back(ascii_tolower(input[i]));
+    }
+  }
+  return result;
+}
+
+std::string ToSnakeCase(StringPiece input) {
+  bool was_not_underscore = false;  // Initialize to false for case 1 (below)
+  bool was_not_cap = false;
+  std::string result;
+  result.reserve(input.size() << 1);
+
+  for (size_t i = 0; i < input.size(); ++i) {
+    if (ascii_isupper(input[i])) {
+      // Consider when the current character B is capitalized:
+      // 1) At beginning of input:   "B..." => "b..."
+      //    (e.g. "Biscuit" => "biscuit")
+      // 2) Following a lowercase:   "...aB..." => "...a_b..."
+      //    (e.g. "gBike" => "g_bike")
+      // 3) At the end of input:     "...AB" => "...ab"
+      //    (e.g. "GoogleLAB" => "google_lab")
+      // 4) Followed by a lowercase: "...ABc..." => "...a_bc..."
+      //    (e.g. "GBike" => "g_bike")
+      if (was_not_underscore &&                     //            case 1 out
+          (was_not_cap ||                           // case 2 in, case 3 out
+           (i + 1 < input.size() &&                 //            case 3 out
+            ascii_islower(input[i + 1])))) {  // case 4 in
+        // We add an underscore for case 2 and case 4.
+        result.push_back('_');
+      }
+      result.push_back(ascii_tolower(input[i]));
+      was_not_underscore = true;
+      was_not_cap = false;
+    } else {
+      result.push_back(input[i]);
+      was_not_underscore = input[i] != '_';
+      was_not_cap = true;
+    }
+  }
+  return result;
+}
+
+std::set<std::string>* well_known_types_ = nullptr;
+PROTOBUF_NAMESPACE_ID::internal::once_flag well_known_types_init_;
+const char* well_known_types_name_array_[] = {
+    "google.protobuf.Timestamp",   "google.protobuf.Duration",
+    "google.protobuf.DoubleValue", "google.protobuf.FloatValue",
+    "google.protobuf.Int64Value",  "google.protobuf.UInt64Value",
+    "google.protobuf.Int32Value",  "google.protobuf.UInt32Value",
+    "google.protobuf.BoolValue",   "google.protobuf.StringValue",
+    "google.protobuf.BytesValue",  "google.protobuf.FieldMask"};
+
+void DeleteWellKnownTypes() { delete well_known_types_; }
+
+void InitWellKnownTypes() {
+  well_known_types_ = new std::set<std::string>;
+  for (size_t i = 0; i < GOOGLE_ARRAYSIZE(well_known_types_name_array_); ++i) {
+    well_known_types_->insert(well_known_types_name_array_[i]);
+  }
+  google::protobuf::internal::OnShutdown(&DeleteWellKnownTypes);
+}
+
+bool IsWellKnownType(const std::string& type_name) {
+  PROTOBUF_NAMESPACE_ID::internal::call_once(well_known_types_init_,
+                                             InitWellKnownTypes);
+  return ContainsKey(*well_known_types_, type_name);
+}
+
+bool IsValidBoolString(StringPiece bool_string) {
+  return bool_string == "true" || bool_string == "false" ||
+         bool_string == "1" || bool_string == "0";
+}
+
+bool IsMap(const google::protobuf::Field& field,
+           const google::protobuf::Type& type) {
+  return field.cardinality() == google::protobuf::Field::CARDINALITY_REPEATED &&
+         (GetBoolOptionOrDefault(type.options(), "map_entry", false) ||
+          GetBoolOptionOrDefault(type.options(),
+                                 "google.protobuf.MessageOptions.map_entry",
+                                 false));
+}
+
+bool IsMessageSetWireFormat(const google::protobuf::Type& type) {
+  return GetBoolOptionOrDefault(type.options(), "message_set_wire_format",
+                                false) ||
+         GetBoolOptionOrDefault(
+             type.options(),
+             "google.protobuf.MessageOptions.message_set_wire_format", false);
+}
+
+std::string DoubleAsString(double value) {
+  if (value == std::numeric_limits<double>::infinity()) return "Infinity";
+  if (value == -std::numeric_limits<double>::infinity()) return "-Infinity";
+  if (std::isnan(value)) return "NaN";
+
+  return SimpleDtoa(value);
+}
+
+std::string FloatAsString(float value) {
+  if (std::isfinite(value)) return SimpleFtoa(value);
+  return DoubleAsString(value);
+}
+
+bool SafeStrToFloat(StringPiece str, float* value) {
+  double double_value;
+  if (!safe_strtod(str, &double_value)) {
+    return false;
+  }
+
+  if (std::isinf(double_value) || std::isnan(double_value)) return false;
+
+  // Fail if the value is not representable in float.
+  if (double_value > std::numeric_limits<float>::max() ||
+      double_value < -std::numeric_limits<float>::max()) {
+    return false;
+  }
+
+  *value = static_cast<float>(double_value);
+  return true;
+}
+
+}  // namespace converter
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/json_util.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/json_util.cpp
new file mode 100644
index 0000000..a9b1c52
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/json_util.cpp
@@ -0,0 +1,284 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/json_util.h>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/bytestream.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/util/internal/default_value_objectwriter.h>
+#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/json_objectwriter.h>
+#include <google/protobuf/util/internal/json_stream_parser.h>
+#include <google/protobuf/util/internal/protostream_objectsource.h>
+#include <google/protobuf/util/internal/protostream_objectwriter.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/util/type_resolver_util.h>
+#include <google/protobuf/stubs/status_macros.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+namespace internal {
+ZeroCopyStreamByteSink::~ZeroCopyStreamByteSink() {
+  if (buffer_size_ > 0) {
+    stream_->BackUp(buffer_size_);
+  }
+}
+
+void ZeroCopyStreamByteSink::Append(const char* bytes, size_t len) {
+  while (true) {
+    if (static_cast<int>(len) <= buffer_size_) {  // NOLINT
+      memcpy(buffer_, bytes, len);
+      buffer_ = static_cast<char*>(buffer_) + len;
+      buffer_size_ -= len;
+      return;
+    }
+    if (buffer_size_ > 0) {
+      memcpy(buffer_, bytes, buffer_size_);
+      bytes += buffer_size_;
+      len -= buffer_size_;
+    }
+    if (!stream_->Next(&buffer_, &buffer_size_)) {
+      // There isn't a way for ByteSink to report errors.
+      buffer_size_ = 0;
+      return;
+    }
+  }
+}
+}  // namespace internal
+
+util::Status BinaryToJsonStream(TypeResolver* resolver,
+                                const std::string& type_url,
+                                io::ZeroCopyInputStream* binary_input,
+                                io::ZeroCopyOutputStream* json_output,
+                                const JsonPrintOptions& options) {
+  io::CodedInputStream in_stream(binary_input);
+  google::protobuf::Type type;
+  RETURN_IF_ERROR(resolver->ResolveMessageType(type_url, &type));
+  converter::ProtoStreamObjectSource::RenderOptions render_options;
+  render_options.use_ints_for_enums = options.always_print_enums_as_ints;
+  render_options.preserve_proto_field_names =
+      options.preserve_proto_field_names;
+  converter::ProtoStreamObjectSource proto_source(&in_stream, resolver, type,
+                                                  render_options);
+  io::CodedOutputStream out_stream(json_output);
+  converter::JsonObjectWriter json_writer(options.add_whitespace ? " " : "",
+                                          &out_stream);
+  if (options.always_print_primitive_fields) {
+    converter::DefaultValueObjectWriter default_value_writer(resolver, type,
+                                                             &json_writer);
+    default_value_writer.set_preserve_proto_field_names(
+        options.preserve_proto_field_names);
+    default_value_writer.set_print_enums_as_ints(
+        options.always_print_enums_as_ints);
+    return proto_source.WriteTo(&default_value_writer);
+  } else {
+    return proto_source.WriteTo(&json_writer);
+  }
+}
+
+util::Status BinaryToJsonString(TypeResolver* resolver,
+                                const std::string& type_url,
+                                const std::string& binary_input,
+                                std::string* json_output,
+                                const JsonPrintOptions& options) {
+  io::ArrayInputStream input_stream(binary_input.data(), binary_input.size());
+  io::StringOutputStream output_stream(json_output);
+  return BinaryToJsonStream(resolver, type_url, &input_stream, &output_stream,
+                            options);
+}
+
+namespace {
+class StatusErrorListener : public converter::ErrorListener {
+ public:
+  StatusErrorListener() {}
+  ~StatusErrorListener() override {}
+
+  util::Status GetStatus() { return status_; }
+
+  void InvalidName(const converter::LocationTrackerInterface& loc,
+                   StringPiece unknown_name,
+                   StringPiece message) override {
+    std::string loc_string = GetLocString(loc);
+    if (!loc_string.empty()) {
+      loc_string.append(" ");
+    }
+    status_ = util::InvalidArgumentError(
+        StrCat(loc_string, unknown_name, ": ", message));
+  }
+
+  void InvalidValue(const converter::LocationTrackerInterface& loc,
+                    StringPiece type_name,
+                    StringPiece value) override {
+    status_ = util::InvalidArgumentError(
+        StrCat(GetLocString(loc), ": invalid value ", std::string(value),
+                     " for type ", std::string(type_name)));
+  }
+
+  void MissingField(const converter::LocationTrackerInterface& loc,
+                    StringPiece missing_name) override {
+    status_ = util::InvalidArgumentError(StrCat(
+        GetLocString(loc), ": missing field ", std::string(missing_name)));
+  }
+
+ private:
+  util::Status status_;
+
+  std::string GetLocString(const converter::LocationTrackerInterface& loc) {
+    std::string loc_string = loc.ToString();
+    StripWhitespace(&loc_string);
+    if (!loc_string.empty()) {
+      loc_string = StrCat("(", loc_string, ")");
+    }
+    return loc_string;
+  }
+
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StatusErrorListener);
+};
+}  // namespace
+
+util::Status JsonToBinaryStream(TypeResolver* resolver,
+                                const std::string& type_url,
+                                io::ZeroCopyInputStream* json_input,
+                                io::ZeroCopyOutputStream* binary_output,
+                                const JsonParseOptions& options) {
+  google::protobuf::Type type;
+  RETURN_IF_ERROR(resolver->ResolveMessageType(type_url, &type));
+  internal::ZeroCopyStreamByteSink sink(binary_output);
+  StatusErrorListener listener;
+  converter::ProtoStreamObjectWriter::Options proto_writer_options;
+  proto_writer_options.ignore_unknown_fields = options.ignore_unknown_fields;
+  proto_writer_options.ignore_unknown_enum_values =
+      options.ignore_unknown_fields;
+  proto_writer_options.case_insensitive_enum_parsing =
+      options.case_insensitive_enum_parsing;
+  converter::ProtoStreamObjectWriter proto_writer(
+      resolver, type, &sink, &listener, proto_writer_options);
+
+  converter::JsonStreamParser parser(&proto_writer);
+  const void* buffer;
+  int length;
+  while (json_input->Next(&buffer, &length)) {
+    if (length == 0) continue;
+    RETURN_IF_ERROR(parser.Parse(
+        StringPiece(static_cast<const char*>(buffer), length)));
+  }
+  RETURN_IF_ERROR(parser.FinishParse());
+
+  return listener.GetStatus();
+}
+
+util::Status JsonToBinaryString(TypeResolver* resolver,
+                                const std::string& type_url,
+                                StringPiece json_input,
+                                std::string* binary_output,
+                                const JsonParseOptions& options) {
+  io::ArrayInputStream input_stream(json_input.data(), json_input.size());
+  io::StringOutputStream output_stream(binary_output);
+  return JsonToBinaryStream(resolver, type_url, &input_stream, &output_stream,
+                            options);
+}
+
+namespace {
+const char* kTypeUrlPrefix = "type.googleapis.com";
+TypeResolver* generated_type_resolver_ = nullptr;
+PROTOBUF_NAMESPACE_ID::internal::once_flag generated_type_resolver_init_;
+
+std::string GetTypeUrl(const Message& message) {
+  return std::string(kTypeUrlPrefix) + "/" +
+         message.GetDescriptor()->full_name();
+}
+
+void DeleteGeneratedTypeResolver() {  // NOLINT
+  delete generated_type_resolver_;
+}
+
+void InitGeneratedTypeResolver() {
+  generated_type_resolver_ = NewTypeResolverForDescriptorPool(
+      kTypeUrlPrefix, DescriptorPool::generated_pool());
+  ::google::protobuf::internal::OnShutdown(&DeleteGeneratedTypeResolver);
+}
+
+TypeResolver* GetGeneratedTypeResolver() {
+  PROTOBUF_NAMESPACE_ID::internal::call_once(generated_type_resolver_init_,
+                                             InitGeneratedTypeResolver);
+  return generated_type_resolver_;
+}
+}  // namespace
+
+util::Status MessageToJsonString(const Message& message, std::string* output,
+                                 const JsonOptions& options) {
+  const DescriptorPool* pool = message.GetDescriptor()->file()->pool();
+  TypeResolver* resolver =
+      pool == DescriptorPool::generated_pool()
+          ? GetGeneratedTypeResolver()
+          : NewTypeResolverForDescriptorPool(kTypeUrlPrefix, pool);
+  util::Status result =
+      BinaryToJsonString(resolver, GetTypeUrl(message),
+                         message.SerializeAsString(), output, options);
+  if (pool != DescriptorPool::generated_pool()) {
+    delete resolver;
+  }
+  return result;
+}
+
+util::Status JsonStringToMessage(StringPiece input, Message* message,
+                                 const JsonParseOptions& options) {
+  const DescriptorPool* pool = message->GetDescriptor()->file()->pool();
+  TypeResolver* resolver =
+      pool == DescriptorPool::generated_pool()
+          ? GetGeneratedTypeResolver()
+          : NewTypeResolverForDescriptorPool(kTypeUrlPrefix, pool);
+  std::string binary;
+  util::Status result = JsonToBinaryString(resolver, GetTypeUrl(*message),
+                                           input, &binary, options);
+  if (result.ok() && !message->ParseFromString(binary)) {
+    result = util::InvalidArgumentError(
+        "JSON transcoder produced invalid protobuf output.");
+  }
+  if (pool != DescriptorPool::generated_pool()) {
+    delete resolver;
+  }
+  return result;
+}
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/message_differencer.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/message_differencer.cpp
new file mode 100644
index 0000000..30560ed
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/message_differencer.cpp
@@ -0,0 +1,2246 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: jschorr@google.com (Joseph Schorr)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/util/message_differencer.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/generated_enum_reflection.h>
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/util/field_comparator.h>
+
+// Always include as last one, otherwise it can break compilation
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+
+namespace util {
+
+namespace {
+
+std::string PrintShortTextFormat(const google::protobuf::Message& message) {
+  std::string debug_string;
+
+  google::protobuf::TextFormat::Printer printer;
+  printer.SetSingleLineMode(true);
+  printer.SetExpandAny(true);
+
+  printer.PrintToString(message, &debug_string);
+  // Single line mode currently might have an extra space at the end.
+  if (!debug_string.empty() && debug_string[debug_string.size() - 1] == ' ') {
+    debug_string.resize(debug_string.size() - 1);
+  }
+
+  return debug_string;
+}
+
+}  // namespace
+
+// A reporter to report the total number of diffs.
+// TODO(ykzhu): we can improve this to take into account the value differencers.
+class NumDiffsReporter : public google::protobuf::util::MessageDifferencer::Reporter {
+ public:
+  NumDiffsReporter() : num_diffs_(0) {}
+
+  // Returns the total number of diffs.
+  int32_t GetNumDiffs() const { return num_diffs_; }
+  void Reset() { num_diffs_ = 0; }
+
+  // Report that a field has been added into Message2.
+  void ReportAdded(
+      const google::protobuf::Message& /* message1 */,
+      const google::protobuf::Message& /* message2 */,
+      const std::vector<google::protobuf::util::MessageDifferencer::SpecificField>&
+      /*field_path*/) override {
+    ++num_diffs_;
+  }
+
+  // Report that a field has been deleted from Message1.
+  void ReportDeleted(
+      const google::protobuf::Message& /* message1 */,
+      const google::protobuf::Message& /* message2 */,
+      const std::vector<google::protobuf::util::MessageDifferencer::SpecificField>&
+      /*field_path*/) override {
+    ++num_diffs_;
+  }
+
+  // Report that the value of a field has been modified.
+  void ReportModified(
+      const google::protobuf::Message& /* message1 */,
+      const google::protobuf::Message& /* message2 */,
+      const std::vector<google::protobuf::util::MessageDifferencer::SpecificField>&
+      /*field_path*/) override {
+    ++num_diffs_;
+  }
+
+ private:
+  int32_t num_diffs_;
+};
+
+// When comparing a repeated field as map, MultipleFieldMapKeyComparator can
+// be used to specify multiple fields as key for key comparison.
+// Two elements of a repeated field will be regarded as having the same key
+// iff they have the same value for every specified key field.
+// Note that you can also specify only one field as key.
+class MessageDifferencer::MultipleFieldsMapKeyComparator
+    : public MessageDifferencer::MapKeyComparator {
+ public:
+  MultipleFieldsMapKeyComparator(
+      MessageDifferencer* message_differencer,
+      const std::vector<std::vector<const FieldDescriptor*> >& key_field_paths)
+      : message_differencer_(message_differencer),
+        key_field_paths_(key_field_paths) {
+    GOOGLE_CHECK(!key_field_paths_.empty());
+    for (const auto& path : key_field_paths_) {
+      GOOGLE_CHECK(!path.empty());
+    }
+  }
+  MultipleFieldsMapKeyComparator(MessageDifferencer* message_differencer,
+                                 const FieldDescriptor* key)
+      : message_differencer_(message_differencer) {
+    std::vector<const FieldDescriptor*> key_field_path;
+    key_field_path.push_back(key);
+    key_field_paths_.push_back(key_field_path);
+  }
+  bool IsMatch(const Message& message1, const Message& message2,
+               const std::vector<SpecificField>& parent_fields) const override {
+    for (const auto& path : key_field_paths_) {
+      if (!IsMatchInternal(message1, message2, parent_fields, path, 0)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+ private:
+  bool IsMatchInternal(
+      const Message& message1, const Message& message2,
+      const std::vector<SpecificField>& parent_fields,
+      const std::vector<const FieldDescriptor*>& key_field_path,
+      int path_index) const {
+    const FieldDescriptor* field = key_field_path[path_index];
+    std::vector<SpecificField> current_parent_fields(parent_fields);
+    if (path_index == static_cast<int64_t>(key_field_path.size() - 1)) {
+      if (field->is_map()) {
+        return message_differencer_->CompareMapField(message1, message2, field,
+                                                     &current_parent_fields);
+      } else if (field->is_repeated()) {
+        return message_differencer_->CompareRepeatedField(
+            message1, message2, field, &current_parent_fields);
+      } else {
+        return message_differencer_->CompareFieldValueUsingParentFields(
+            message1, message2, field, -1, -1, &current_parent_fields);
+      }
+    } else {
+      const Reflection* reflection1 = message1.GetReflection();
+      const Reflection* reflection2 = message2.GetReflection();
+      bool has_field1 = reflection1->HasField(message1, field);
+      bool has_field2 = reflection2->HasField(message2, field);
+      if (!has_field1 && !has_field2) {
+        return true;
+      }
+      if (has_field1 != has_field2) {
+        return false;
+      }
+      SpecificField specific_field;
+      specific_field.field = field;
+      current_parent_fields.push_back(specific_field);
+      return IsMatchInternal(reflection1->GetMessage(message1, field),
+                             reflection2->GetMessage(message2, field),
+                             current_parent_fields, key_field_path,
+                             path_index + 1);
+    }
+  }
+  MessageDifferencer* message_differencer_;
+  std::vector<std::vector<const FieldDescriptor*> > key_field_paths_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultipleFieldsMapKeyComparator);
+};
+
+// Preserve the order when treating repeated field as SMART_LIST. The current
+// implementation is to find the longest matching sequence from the first
+// element. The optimal solution requires to use //util/diff/lcs.h, which is
+// not open sourced yet. Overwrite this method if you want to have that.
+// TODO(ykzhu): change to use LCS once it is open sourced.
+void MatchIndicesPostProcessorForSmartList(std::vector<int>* match_list1,
+                                           std::vector<int>* match_list2) {
+  int last_matched_index = -1;
+  for (size_t i = 0; i < match_list1->size(); ++i) {
+    if (match_list1->at(i) < 0) {
+      continue;
+    }
+    if (last_matched_index < 0 || match_list1->at(i) > last_matched_index) {
+      last_matched_index = match_list1->at(i);
+    } else {
+      match_list2->at(match_list1->at(i)) = -1;
+      match_list1->at(i) = -1;
+    }
+  }
+}
+
+void AddSpecificIndex(
+    google::protobuf::util::MessageDifferencer::SpecificField* specific_field,
+    const Message& message, const FieldDescriptor* field, int index) {
+  if (field->is_map()) {
+    const Reflection* reflection = message.GetReflection();
+    specific_field->map_entry1 =
+        &reflection->GetRepeatedMessage(message, field, index);
+  }
+  specific_field->index = index;
+}
+
+void AddSpecificNewIndex(
+    google::protobuf::util::MessageDifferencer::SpecificField* specific_field,
+    const Message& message, const FieldDescriptor* field, int index) {
+  if (field->is_map()) {
+    const Reflection* reflection = message.GetReflection();
+    specific_field->map_entry2 =
+        &reflection->GetRepeatedMessage(message, field, index);
+  }
+  specific_field->new_index = index;
+}
+
+MessageDifferencer::MapEntryKeyComparator::MapEntryKeyComparator(
+    MessageDifferencer* message_differencer)
+    : message_differencer_(message_differencer) {}
+
+bool MessageDifferencer::MapEntryKeyComparator::IsMatch(
+    const Message& message1, const Message& message2,
+    const std::vector<SpecificField>& parent_fields) const {
+  // Map entry has its key in the field with tag 1.  See the comment for
+  // map_entry in MessageOptions.
+  const FieldDescriptor* key = message1.GetDescriptor()->FindFieldByNumber(1);
+  // If key is not present in message1 and we're doing partial comparison or if
+  // map key is explicitly ignored treat the field as set instead,
+  const bool treat_as_set =
+      (message_differencer_->scope() == PARTIAL &&
+       !message1.GetReflection()->HasField(message1, key)) ||
+      message_differencer_->IsIgnored(message1, message2, key, parent_fields);
+
+  std::vector<SpecificField> current_parent_fields(parent_fields);
+  if (treat_as_set) {
+    return message_differencer_->Compare(message1, message2,
+                                         &current_parent_fields);
+  }
+  return message_differencer_->CompareFieldValueUsingParentFields(
+      message1, message2, key, -1, -1, &current_parent_fields);
+}
+
+bool MessageDifferencer::Equals(const Message& message1,
+                                const Message& message2) {
+  MessageDifferencer differencer;
+
+  return differencer.Compare(message1, message2);
+}
+
+bool MessageDifferencer::Equivalent(const Message& message1,
+                                    const Message& message2) {
+  MessageDifferencer differencer;
+  differencer.set_message_field_comparison(MessageDifferencer::EQUIVALENT);
+
+  return differencer.Compare(message1, message2);
+}
+
+bool MessageDifferencer::ApproximatelyEquals(const Message& message1,
+                                             const Message& message2) {
+  MessageDifferencer differencer;
+  differencer.set_float_comparison(MessageDifferencer::APPROXIMATE);
+
+  return differencer.Compare(message1, message2);
+}
+
+bool MessageDifferencer::ApproximatelyEquivalent(const Message& message1,
+                                                 const Message& message2) {
+  MessageDifferencer differencer;
+  differencer.set_message_field_comparison(MessageDifferencer::EQUIVALENT);
+  differencer.set_float_comparison(MessageDifferencer::APPROXIMATE);
+
+  return differencer.Compare(message1, message2);
+}
+
+// ===========================================================================
+
+MessageDifferencer::MessageDifferencer()
+    : reporter_(NULL),
+      message_field_comparison_(EQUAL),
+      scope_(FULL),
+      repeated_field_comparison_(AS_LIST),
+      map_entry_key_comparator_(this),
+      report_matches_(false),
+      report_moves_(true),
+      report_ignores_(true),
+      output_string_(nullptr),
+      match_indices_for_smart_list_callback_(
+          MatchIndicesPostProcessorForSmartList) {}
+
+MessageDifferencer::~MessageDifferencer() {
+  for (MapKeyComparator* comparator : owned_key_comparators_) {
+    delete comparator;
+  }
+  for (IgnoreCriteria* criteria : ignore_criteria_) {
+    delete criteria;
+  }
+}
+
+void MessageDifferencer::set_field_comparator(FieldComparator* comparator) {
+  GOOGLE_CHECK(comparator) << "Field comparator can't be NULL.";
+  field_comparator_kind_ = kFCBase;
+  field_comparator_.base = comparator;
+}
+
+#ifdef PROTOBUF_FUTURE_BREAKING_CHANGES
+void MessageDifferencer::set_field_comparator(
+    DefaultFieldComparator* comparator) {
+  GOOGLE_CHECK(comparator) << "Field comparator can't be NULL.";
+  field_comparator_kind_ = kFCDefault;
+  field_comparator_.default_impl = comparator;
+}
+#endif  // PROTOBUF_FUTURE_BREAKING_CHANGES
+
+void MessageDifferencer::set_message_field_comparison(
+    MessageFieldComparison comparison) {
+  message_field_comparison_ = comparison;
+}
+
+MessageDifferencer::MessageFieldComparison
+MessageDifferencer::message_field_comparison() const {
+  return message_field_comparison_;
+}
+
+void MessageDifferencer::set_scope(Scope scope) { scope_ = scope; }
+
+MessageDifferencer::Scope MessageDifferencer::scope() const { return scope_; }
+
+void MessageDifferencer::set_float_comparison(FloatComparison comparison) {
+  default_field_comparator_.set_float_comparison(
+      comparison == EXACT ? DefaultFieldComparator::EXACT
+                          : DefaultFieldComparator::APPROXIMATE);
+}
+
+void MessageDifferencer::set_repeated_field_comparison(
+    RepeatedFieldComparison comparison) {
+  repeated_field_comparison_ = comparison;
+}
+
+MessageDifferencer::RepeatedFieldComparison
+MessageDifferencer::repeated_field_comparison() const {
+  return repeated_field_comparison_;
+}
+
+void MessageDifferencer::CheckRepeatedFieldComparisons(
+    const FieldDescriptor* field,
+    const RepeatedFieldComparison& new_comparison) {
+  GOOGLE_CHECK(field->is_repeated())
+      << "Field must be repeated: " << field->full_name();
+  const MapKeyComparator* key_comparator = GetMapKeyComparator(field);
+  GOOGLE_CHECK(key_comparator == NULL)
+      << "Cannot treat this repeated field as both MAP and " << new_comparison
+      << " for comparison.  Field name is: " << field->full_name();
+}
+
+void MessageDifferencer::TreatAsSet(const FieldDescriptor* field) {
+  CheckRepeatedFieldComparisons(field, AS_SET);
+  repeated_field_comparisons_[field] = AS_SET;
+}
+
+void MessageDifferencer::TreatAsSmartSet(const FieldDescriptor* field) {
+  CheckRepeatedFieldComparisons(field, AS_SMART_SET);
+  repeated_field_comparisons_[field] = AS_SMART_SET;
+}
+
+void MessageDifferencer::SetMatchIndicesForSmartListCallback(
+    std::function<void(std::vector<int>*, std::vector<int>*)> callback) {
+  match_indices_for_smart_list_callback_ = callback;
+}
+
+void MessageDifferencer::TreatAsList(const FieldDescriptor* field) {
+  CheckRepeatedFieldComparisons(field, AS_LIST);
+  repeated_field_comparisons_[field] = AS_LIST;
+}
+
+void MessageDifferencer::TreatAsSmartList(const FieldDescriptor* field) {
+  CheckRepeatedFieldComparisons(field, AS_SMART_LIST);
+  repeated_field_comparisons_[field] = AS_SMART_LIST;
+}
+
+void MessageDifferencer::TreatAsMap(const FieldDescriptor* field,
+                                    const FieldDescriptor* key) {
+  GOOGLE_CHECK_EQ(FieldDescriptor::CPPTYPE_MESSAGE, field->cpp_type())
+      << "Field has to be message type.  Field name is: " << field->full_name();
+  GOOGLE_CHECK(key->containing_type() == field->message_type())
+      << key->full_name()
+      << " must be a direct subfield within the repeated field "
+      << field->full_name() << ", not " << key->containing_type()->full_name();
+  GOOGLE_CHECK(repeated_field_comparisons_.find(field) ==
+        repeated_field_comparisons_.end())
+      << "Cannot treat the same field as both "
+      << repeated_field_comparisons_[field]
+      << " and MAP. Field name is: " << field->full_name();
+  MapKeyComparator* key_comparator =
+      new MultipleFieldsMapKeyComparator(this, key);
+  owned_key_comparators_.push_back(key_comparator);
+  map_field_key_comparator_[field] = key_comparator;
+}
+
+void MessageDifferencer::TreatAsMapWithMultipleFieldsAsKey(
+    const FieldDescriptor* field,
+    const std::vector<const FieldDescriptor*>& key_fields) {
+  std::vector<std::vector<const FieldDescriptor*> > key_field_paths;
+  for (const FieldDescriptor* key_filed : key_fields) {
+    std::vector<const FieldDescriptor*> key_field_path;
+    key_field_path.push_back(key_filed);
+    key_field_paths.push_back(key_field_path);
+  }
+  TreatAsMapWithMultipleFieldPathsAsKey(field, key_field_paths);
+}
+
+void MessageDifferencer::TreatAsMapWithMultipleFieldPathsAsKey(
+    const FieldDescriptor* field,
+    const std::vector<std::vector<const FieldDescriptor*> >& key_field_paths) {
+  GOOGLE_CHECK(field->is_repeated())
+      << "Field must be repeated: " << field->full_name();
+  GOOGLE_CHECK_EQ(FieldDescriptor::CPPTYPE_MESSAGE, field->cpp_type())
+      << "Field has to be message type.  Field name is: " << field->full_name();
+  for (const auto& key_field_path : key_field_paths) {
+    for (size_t j = 0; j < key_field_path.size(); ++j) {
+      const FieldDescriptor* parent_field =
+          j == 0 ? field : key_field_path[j - 1];
+      const FieldDescriptor* child_field = key_field_path[j];
+      GOOGLE_CHECK(child_field->containing_type() == parent_field->message_type())
+          << child_field->full_name()
+          << " must be a direct subfield within the field: "
+          << parent_field->full_name();
+      if (j != 0) {
+        GOOGLE_CHECK_EQ(FieldDescriptor::CPPTYPE_MESSAGE, parent_field->cpp_type())
+            << parent_field->full_name() << " has to be of type message.";
+        GOOGLE_CHECK(!parent_field->is_repeated())
+            << parent_field->full_name() << " cannot be a repeated field.";
+      }
+    }
+  }
+  GOOGLE_CHECK(repeated_field_comparisons_.find(field) ==
+        repeated_field_comparisons_.end())
+      << "Cannot treat the same field as both "
+      << repeated_field_comparisons_[field]
+      << " and MAP. Field name is: " << field->full_name();
+  MapKeyComparator* key_comparator =
+      new MultipleFieldsMapKeyComparator(this, key_field_paths);
+  owned_key_comparators_.push_back(key_comparator);
+  map_field_key_comparator_[field] = key_comparator;
+}
+
+void MessageDifferencer::TreatAsMapUsingKeyComparator(
+    const FieldDescriptor* field, const MapKeyComparator* key_comparator) {
+  GOOGLE_CHECK(field->is_repeated())
+      << "Field must be repeated: " << field->full_name();
+  GOOGLE_CHECK(repeated_field_comparisons_.find(field) ==
+        repeated_field_comparisons_.end())
+      << "Cannot treat the same field as both "
+      << repeated_field_comparisons_[field]
+      << " and MAP. Field name is: " << field->full_name();
+  map_field_key_comparator_[field] = key_comparator;
+}
+
+void MessageDifferencer::AddIgnoreCriteria(IgnoreCriteria* ignore_criteria) {
+  ignore_criteria_.push_back(ignore_criteria);
+}
+
+void MessageDifferencer::IgnoreField(const FieldDescriptor* field) {
+  ignored_fields_.insert(field);
+}
+
+void MessageDifferencer::SetFractionAndMargin(const FieldDescriptor* field,
+                                              double fraction, double margin) {
+  default_field_comparator_.SetFractionAndMargin(field, fraction, margin);
+}
+
+void MessageDifferencer::ReportDifferencesToString(std::string* output) {
+  GOOGLE_DCHECK(output) << "Specified output string was NULL";
+
+  output_string_ = output;
+  output_string_->clear();
+}
+
+void MessageDifferencer::ReportDifferencesTo(Reporter* reporter) {
+  // If an output string is set, clear it to prevent
+  // it superseding the specified reporter.
+  if (output_string_) {
+    output_string_ = NULL;
+  }
+
+  reporter_ = reporter;
+}
+
+bool MessageDifferencer::FieldBefore(const FieldDescriptor* field1,
+                                     const FieldDescriptor* field2) {
+  // Handle sentinel values (i.e. make sure NULLs are always ordered
+  // at the end of the list).
+  if (field1 == NULL) {
+    return false;
+  }
+
+  if (field2 == NULL) {
+    return true;
+  }
+
+  // Always order fields by their tag number
+  return (field1->number() < field2->number());
+}
+
+bool MessageDifferencer::Compare(const Message& message1,
+                                 const Message& message2) {
+  std::vector<SpecificField> parent_fields;
+
+  bool result = false;
+  // Setup the internal reporter if need be.
+  if (output_string_) {
+    io::StringOutputStream output_stream(output_string_);
+    StreamReporter reporter(&output_stream);
+    reporter.SetMessages(message1, message2);
+    reporter_ = &reporter;
+    result = Compare(message1, message2, &parent_fields);
+    reporter_ = NULL;
+  } else {
+    result = Compare(message1, message2, &parent_fields);
+  }
+  return result;
+}
+
+bool MessageDifferencer::CompareWithFields(
+    const Message& message1, const Message& message2,
+    const std::vector<const FieldDescriptor*>& message1_fields_arg,
+    const std::vector<const FieldDescriptor*>& message2_fields_arg) {
+  if (message1.GetDescriptor() != message2.GetDescriptor()) {
+    GOOGLE_LOG(DFATAL) << "Comparison between two messages with different "
+                << "descriptors.";
+    return false;
+  }
+
+  std::vector<SpecificField> parent_fields;
+
+  bool result = false;
+
+  FieldDescriptorArray message1_fields(message1_fields_arg.size() + 1);
+  FieldDescriptorArray message2_fields(message2_fields_arg.size() + 1);
+
+  std::copy(message1_fields_arg.cbegin(), message1_fields_arg.cend(),
+            message1_fields.begin());
+  std::copy(message2_fields_arg.cbegin(), message2_fields_arg.cend(),
+            message2_fields.begin());
+
+  // Append sentinel values.
+  message1_fields[message1_fields_arg.size()] = nullptr;
+  message2_fields[message2_fields_arg.size()] = nullptr;
+
+  std::sort(message1_fields.begin(), message1_fields.end(), FieldBefore);
+  std::sort(message2_fields.begin(), message2_fields.end(), FieldBefore);
+
+  // Setup the internal reporter if need be.
+  if (output_string_) {
+    io::StringOutputStream output_stream(output_string_);
+    StreamReporter reporter(&output_stream);
+    reporter_ = &reporter;
+    result = CompareRequestedFieldsUsingSettings(
+        message1, message2, message1_fields, message2_fields, &parent_fields);
+    reporter_ = NULL;
+  } else {
+    result = CompareRequestedFieldsUsingSettings(
+        message1, message2, message1_fields, message2_fields, &parent_fields);
+  }
+
+  return result;
+}
+
+bool MessageDifferencer::Compare(const Message& message1,
+                                 const Message& message2,
+                                 std::vector<SpecificField>* parent_fields) {
+  const Descriptor* descriptor1 = message1.GetDescriptor();
+  const Descriptor* descriptor2 = message2.GetDescriptor();
+  if (descriptor1 != descriptor2) {
+    GOOGLE_LOG(DFATAL) << "Comparison between two messages with different "
+                << "descriptors. " << descriptor1->full_name() << " vs "
+                << descriptor2->full_name();
+    return false;
+  }
+
+  // Expand google.protobuf.Any payload if possible.
+  if (descriptor1->full_name() == internal::kAnyFullTypeName) {
+    std::unique_ptr<Message> data1;
+    std::unique_ptr<Message> data2;
+    if (unpack_any_field_.UnpackAny(message1, &data1) &&
+        unpack_any_field_.UnpackAny(message2, &data2)) {
+      // Avoid DFATAL for different descriptors in google.protobuf.Any payloads.
+      if (data1->GetDescriptor() != data2->GetDescriptor()) {
+        return false;
+      }
+      return Compare(*data1, *data2, parent_fields);
+    }
+  }
+  const Reflection* reflection1 = message1.GetReflection();
+  const Reflection* reflection2 = message2.GetReflection();
+
+  bool unknown_compare_result = true;
+  // Ignore unknown fields in EQUIVALENT mode
+  if (message_field_comparison_ != EQUIVALENT) {
+    const UnknownFieldSet& unknown_field_set1 =
+        reflection1->GetUnknownFields(message1);
+    const UnknownFieldSet& unknown_field_set2 =
+        reflection2->GetUnknownFields(message2);
+    if (!CompareUnknownFields(message1, message2, unknown_field_set1,
+                              unknown_field_set2, parent_fields)) {
+      if (reporter_ == NULL) {
+        return false;
+      }
+      unknown_compare_result = false;
+    }
+  }
+
+  FieldDescriptorArray message1_fields = RetrieveFields(message1, true);
+  FieldDescriptorArray message2_fields = RetrieveFields(message2, false);
+
+  return CompareRequestedFieldsUsingSettings(message1, message2,
+                                             message1_fields, message2_fields,
+                                             parent_fields) &&
+         unknown_compare_result;
+}
+
+FieldDescriptorArray MessageDifferencer::RetrieveFields(const Message& message,
+                                                        bool base_message) {
+  const Descriptor* descriptor = message.GetDescriptor();
+
+  tmp_message_fields_.clear();
+  tmp_message_fields_.reserve(descriptor->field_count() + 1);
+
+  const Reflection* reflection = message.GetReflection();
+  if (descriptor->options().map_entry()) {
+    if (this->scope_ == PARTIAL && base_message) {
+      reflection->ListFields(message, &tmp_message_fields_);
+    } else {
+      // Map entry fields are always considered present.
+      for (int i = 0; i < descriptor->field_count(); i++) {
+        tmp_message_fields_.push_back(descriptor->field(i));
+      }
+    }
+  } else {
+    reflection->ListFields(message, &tmp_message_fields_);
+  }
+  // Add sentinel values to deal with the
+  // case where the number of the fields in
+  // each list are different.
+  tmp_message_fields_.push_back(nullptr);
+
+  FieldDescriptorArray message_fields(tmp_message_fields_.begin(),
+                                      tmp_message_fields_.end());
+
+  return message_fields;
+}
+
+bool MessageDifferencer::CompareRequestedFieldsUsingSettings(
+    const Message& message1, const Message& message2,
+    const FieldDescriptorArray& message1_fields,
+    const FieldDescriptorArray& message2_fields,
+    std::vector<SpecificField>* parent_fields) {
+  if (scope_ == FULL) {
+    if (message_field_comparison_ == EQUIVALENT) {
+      // We need to merge the field lists of both messages (i.e.
+      // we are merely checking for a difference in field values,
+      // rather than the addition or deletion of fields).
+      FieldDescriptorArray fields_union =
+          CombineFields(message1_fields, FULL, message2_fields, FULL);
+      return CompareWithFieldsInternal(message1, message2, fields_union,
+                                       fields_union, parent_fields);
+    } else {
+      // Simple equality comparison, use the unaltered field lists.
+      return CompareWithFieldsInternal(message1, message2, message1_fields,
+                                       message2_fields, parent_fields);
+    }
+  } else {
+    if (message_field_comparison_ == EQUIVALENT) {
+      // We use the list of fields for message1 for both messages when
+      // comparing.  This way, extra fields in message2 are ignored,
+      // and missing fields in message2 use their default value.
+      return CompareWithFieldsInternal(message1, message2, message1_fields,
+                                       message1_fields, parent_fields);
+    } else {
+      // We need to consider the full list of fields for message1
+      // but only the intersection for message2.  This way, any fields
+      // only present in message2 will be ignored, but any fields only
+      // present in message1 will be marked as a difference.
+      FieldDescriptorArray fields_intersection =
+          CombineFields(message1_fields, PARTIAL, message2_fields, PARTIAL);
+      return CompareWithFieldsInternal(message1, message2, message1_fields,
+                                       fields_intersection, parent_fields);
+    }
+  }
+}
+
+FieldDescriptorArray MessageDifferencer::CombineFields(
+    const FieldDescriptorArray& fields1, Scope fields1_scope,
+    const FieldDescriptorArray& fields2, Scope fields2_scope) {
+  size_t index1 = 0;
+  size_t index2 = 0;
+
+  tmp_message_fields_.clear();
+
+  while (index1 < fields1.size() && index2 < fields2.size()) {
+    const FieldDescriptor* field1 = fields1[index1];
+    const FieldDescriptor* field2 = fields2[index2];
+
+    if (FieldBefore(field1, field2)) {
+      if (fields1_scope == FULL) {
+        tmp_message_fields_.push_back(fields1[index1]);
+      }
+      ++index1;
+    } else if (FieldBefore(field2, field1)) {
+      if (fields2_scope == FULL) {
+        tmp_message_fields_.push_back(fields2[index2]);
+      }
+      ++index2;
+    } else {
+      tmp_message_fields_.push_back(fields1[index1]);
+      ++index1;
+      ++index2;
+    }
+  }
+
+  tmp_message_fields_.push_back(nullptr);
+
+  FieldDescriptorArray combined_fields(tmp_message_fields_.begin(),
+                                       tmp_message_fields_.end());
+
+  return combined_fields;
+}
+
+bool MessageDifferencer::CompareWithFieldsInternal(
+    const Message& message1, const Message& message2,
+    const FieldDescriptorArray& message1_fields,
+    const FieldDescriptorArray& message2_fields,
+    std::vector<SpecificField>* parent_fields) {
+  bool isDifferent = false;
+  int field_index1 = 0;
+  int field_index2 = 0;
+
+  const Reflection* reflection1 = message1.GetReflection();
+  const Reflection* reflection2 = message2.GetReflection();
+
+  while (true) {
+    const FieldDescriptor* field1 = message1_fields[field_index1];
+    const FieldDescriptor* field2 = message2_fields[field_index2];
+
+    // Once we have reached sentinel values, we are done the comparison.
+    if (field1 == NULL && field2 == NULL) {
+      break;
+    }
+
+    // Check for differences in the field itself.
+    if (FieldBefore(field1, field2)) {
+      // Field 1 is not in the field list for message 2.
+      if (IsIgnored(message1, message2, field1, *parent_fields)) {
+        // We are ignoring field1. Report the ignore and move on to
+        // the next field in message1_fields.
+        if (reporter_ != NULL) {
+          SpecificField specific_field;
+          specific_field.field = field1;
+          parent_fields->push_back(specific_field);
+          if (report_ignores_) {
+            reporter_->ReportIgnored(message1, message2, *parent_fields);
+          }
+          parent_fields->pop_back();
+        }
+        ++field_index1;
+        continue;
+      }
+
+      if (reporter_ != NULL) {
+        assert(field1 != NULL);
+        int count = field1->is_repeated()
+                        ? reflection1->FieldSize(message1, field1)
+                        : 1;
+
+        for (int i = 0; i < count; ++i) {
+          SpecificField specific_field;
+          specific_field.field = field1;
+          if (field1->is_repeated()) {
+            AddSpecificIndex(&specific_field, message1, field1, i);
+          } else {
+            specific_field.index = -1;
+          }
+
+          parent_fields->push_back(specific_field);
+          reporter_->ReportDeleted(message1, message2, *parent_fields);
+          parent_fields->pop_back();
+        }
+
+        isDifferent = true;
+      } else {
+        return false;
+      }
+
+      ++field_index1;
+      continue;
+    } else if (FieldBefore(field2, field1)) {
+      // Field 2 is not in the field list for message 1.
+      if (IsIgnored(message1, message2, field2, *parent_fields)) {
+        // We are ignoring field2. Report the ignore and move on to
+        // the next field in message2_fields.
+        if (reporter_ != NULL) {
+          SpecificField specific_field;
+          specific_field.field = field2;
+          parent_fields->push_back(specific_field);
+          if (report_ignores_) {
+            reporter_->ReportIgnored(message1, message2, *parent_fields);
+          }
+          parent_fields->pop_back();
+        }
+        ++field_index2;
+        continue;
+      }
+
+      if (reporter_ != NULL) {
+        int count = field2->is_repeated()
+                        ? reflection2->FieldSize(message2, field2)
+                        : 1;
+
+        for (int i = 0; i < count; ++i) {
+          SpecificField specific_field;
+          specific_field.field = field2;
+          if (field2->is_repeated()) {
+            specific_field.index = i;
+            AddSpecificNewIndex(&specific_field, message2, field2, i);
+          } else {
+            specific_field.index = -1;
+            specific_field.new_index = -1;
+          }
+
+          parent_fields->push_back(specific_field);
+          reporter_->ReportAdded(message1, message2, *parent_fields);
+          parent_fields->pop_back();
+        }
+
+        isDifferent = true;
+      } else {
+        return false;
+      }
+
+      ++field_index2;
+      continue;
+    }
+
+    // By this point, field1 and field2 are guaranteed to point to the same
+    // field, so we can now compare the values.
+    if (IsIgnored(message1, message2, field1, *parent_fields)) {
+      // Ignore this field. Report and move on.
+      if (reporter_ != NULL) {
+        SpecificField specific_field;
+        specific_field.field = field1;
+        parent_fields->push_back(specific_field);
+        if (report_ignores_) {
+          reporter_->ReportIgnored(message1, message2, *parent_fields);
+        }
+        parent_fields->pop_back();
+      }
+
+      ++field_index1;
+      ++field_index2;
+      continue;
+    }
+
+    bool fieldDifferent = false;
+    assert(field1 != NULL);
+    if (field1->is_map()) {
+      fieldDifferent =
+          !CompareMapField(message1, message2, field1, parent_fields);
+    } else if (field1->is_repeated()) {
+      fieldDifferent =
+          !CompareRepeatedField(message1, message2, field1, parent_fields);
+    } else {
+      fieldDifferent = !CompareFieldValueUsingParentFields(
+          message1, message2, field1, -1, -1, parent_fields);
+
+      if (reporter_ != nullptr) {
+        SpecificField specific_field;
+        specific_field.field = field1;
+        parent_fields->push_back(specific_field);
+        if (fieldDifferent) {
+          reporter_->ReportModified(message1, message2, *parent_fields);
+          isDifferent = true;
+        } else if (report_matches_) {
+          reporter_->ReportMatched(message1, message2, *parent_fields);
+        }
+        parent_fields->pop_back();
+      }
+    }
+    if (fieldDifferent) {
+      if (reporter_ == nullptr) return false;
+      isDifferent = true;
+    }
+    // Increment the field indices.
+    ++field_index1;
+    ++field_index2;
+  }
+
+  return !isDifferent;
+}
+
+bool MessageDifferencer::IsMatch(
+    const FieldDescriptor* repeated_field,
+    const MapKeyComparator* key_comparator, const Message* message1,
+    const Message* message2, const std::vector<SpecificField>& parent_fields,
+    Reporter* reporter, int index1, int index2) {
+  std::vector<SpecificField> current_parent_fields(parent_fields);
+  if (repeated_field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+    return CompareFieldValueUsingParentFields(*message1, *message2,
+                                              repeated_field, index1, index2,
+                                              &current_parent_fields);
+  }
+  // Back up the Reporter and output_string_.  They will be reset in the
+  // following code.
+  Reporter* backup_reporter = reporter_;
+  std::string* output_string = output_string_;
+  reporter_ = reporter;
+  output_string_ = NULL;
+  bool match;
+
+  if (key_comparator == NULL) {
+    match = CompareFieldValueUsingParentFields(*message1, *message2,
+                                               repeated_field, index1, index2,
+                                               &current_parent_fields);
+  } else {
+    const Reflection* reflection1 = message1->GetReflection();
+    const Reflection* reflection2 = message2->GetReflection();
+    const Message& m1 =
+        reflection1->GetRepeatedMessage(*message1, repeated_field, index1);
+    const Message& m2 =
+        reflection2->GetRepeatedMessage(*message2, repeated_field, index2);
+    SpecificField specific_field;
+    specific_field.field = repeated_field;
+    if (repeated_field->is_map()) {
+      specific_field.map_entry1 = &m1;
+      specific_field.map_entry2 = &m2;
+    }
+    specific_field.index = index1;
+    specific_field.new_index = index2;
+    current_parent_fields.push_back(specific_field);
+    match = key_comparator->IsMatch(m1, m2, current_parent_fields);
+  }
+
+  reporter_ = backup_reporter;
+  output_string_ = output_string;
+  return match;
+}
+
+bool MessageDifferencer::CompareMapFieldByMapReflection(
+    const Message& message1, const Message& message2,
+    const FieldDescriptor* map_field, std::vector<SpecificField>* parent_fields,
+    DefaultFieldComparator* comparator) {
+  GOOGLE_DCHECK_EQ(nullptr, reporter_);
+  GOOGLE_DCHECK(map_field->is_map());
+  GOOGLE_DCHECK(map_field_key_comparator_.find(map_field) ==
+         map_field_key_comparator_.end());
+  GOOGLE_DCHECK_EQ(repeated_field_comparison_, AS_LIST);
+  const Reflection* reflection1 = message1.GetReflection();
+  const Reflection* reflection2 = message2.GetReflection();
+  const int count1 = reflection1->MapSize(message1, map_field);
+  const int count2 = reflection2->MapSize(message2, map_field);
+  const bool treated_as_subset = IsTreatedAsSubset(map_field);
+  if (count1 != count2 && !treated_as_subset) {
+    return false;
+  }
+  if (count1 > count2) {
+    return false;
+  }
+
+  // First pass: check whether the same keys are present.
+  for (MapIterator it = reflection1->MapBegin(const_cast<Message*>(&message1),
+                                              map_field),
+                   it_end = reflection1->MapEnd(const_cast<Message*>(&message1),
+                                                map_field);
+       it != it_end; ++it) {
+    if (!reflection2->ContainsMapKey(message2, map_field, it.GetKey())) {
+      return false;
+    }
+  }
+
+  // Second pass: compare values for matching keys.
+  const FieldDescriptor* val_des = map_field->message_type()->map_value();
+  switch (val_des->cpp_type()) {
+#define HANDLE_TYPE(CPPTYPE, METHOD, COMPAREMETHOD)                           \
+  case FieldDescriptor::CPPTYPE_##CPPTYPE: {                                  \
+    for (MapIterator it = reflection1->MapBegin(                              \
+                         const_cast<Message*>(&message1), map_field),         \
+                     it_end = reflection1->MapEnd(                            \
+                         const_cast<Message*>(&message1), map_field);         \
+         it != it_end; ++it) {                                                \
+      MapValueConstRef value2;                                                \
+      reflection2->LookupMapValue(message2, map_field, it.GetKey(), &value2); \
+      if (!comparator->Compare##COMPAREMETHOD(*val_des,                       \
+                                              it.GetValueRef().Get##METHOD(), \
+                                              value2.Get##METHOD())) {        \
+        return false;                                                         \
+      }                                                                       \
+    }                                                                         \
+    break;                                                                    \
+  }
+    HANDLE_TYPE(INT32, Int32Value, Int32);
+    HANDLE_TYPE(INT64, Int64Value, Int64);
+    HANDLE_TYPE(UINT32, UInt32Value, UInt32);
+    HANDLE_TYPE(UINT64, UInt64Value, UInt64);
+    HANDLE_TYPE(DOUBLE, DoubleValue, Double);
+    HANDLE_TYPE(FLOAT, FloatValue, Float);
+    HANDLE_TYPE(BOOL, BoolValue, Bool);
+    HANDLE_TYPE(STRING, StringValue, String);
+    HANDLE_TYPE(ENUM, EnumValue, Int32);
+#undef HANDLE_TYPE
+    case FieldDescriptor::CPPTYPE_MESSAGE: {
+      for (MapIterator it = reflection1->MapBegin(
+               const_cast<Message*>(&message1), map_field);
+           it !=
+           reflection1->MapEnd(const_cast<Message*>(&message1), map_field);
+           ++it) {
+        if (!reflection2->ContainsMapKey(message2, map_field, it.GetKey())) {
+          return false;
+        }
+        bool compare_result;
+        MapValueConstRef value2;
+        reflection2->LookupMapValue(message2, map_field, it.GetKey(), &value2);
+        // Append currently compared field to the end of parent_fields.
+        SpecificField specific_value_field;
+        specific_value_field.field = val_des;
+        parent_fields->push_back(specific_value_field);
+        compare_result = Compare(it.GetValueRef().GetMessageValue(),
+                                 value2.GetMessageValue(), parent_fields);
+        parent_fields->pop_back();
+        if (!compare_result) {
+          return false;
+        }
+      }
+      break;
+    }
+  }
+  return true;
+}
+
+bool MessageDifferencer::CompareMapField(
+    const Message& message1, const Message& message2,
+    const FieldDescriptor* repeated_field,
+    std::vector<SpecificField>* parent_fields) {
+  GOOGLE_DCHECK(repeated_field->is_map());
+
+  // the input FieldDescriptor is guaranteed to be repeated field.
+  const Reflection* reflection1 = message1.GetReflection();
+  const Reflection* reflection2 = message2.GetReflection();
+
+  // When both map fields are on map, do not sync to repeated field.
+  if (reflection1->GetMapData(message1, repeated_field)->IsMapValid() &&
+      reflection2->GetMapData(message2, repeated_field)->IsMapValid() &&
+      // TODO(jieluo): Add support for reporter
+      reporter_ == nullptr &&
+      // Users didn't set custom map field key comparator
+      map_field_key_comparator_.find(repeated_field) ==
+          map_field_key_comparator_.end() &&
+      // Users didn't set repeated field comparison
+      repeated_field_comparison_ == AS_LIST &&
+      // Users didn't set their own FieldComparator implementation
+      field_comparator_kind_ == kFCDefault) {
+    const FieldDescriptor* key_des = repeated_field->message_type()->map_key();
+    const FieldDescriptor* val_des =
+        repeated_field->message_type()->map_value();
+    std::vector<SpecificField> current_parent_fields(*parent_fields);
+    SpecificField specific_field;
+    specific_field.field = repeated_field;
+    current_parent_fields.push_back(specific_field);
+    if (!IsIgnored(message1, message2, key_des, current_parent_fields) &&
+        !IsIgnored(message1, message2, val_des, current_parent_fields)) {
+      return CompareMapFieldByMapReflection(message1, message2, repeated_field,
+                                            &current_parent_fields,
+                                            field_comparator_.default_impl);
+    }
+  }
+
+  return CompareRepeatedRep(message1, message2, repeated_field, parent_fields);
+}
+
+bool MessageDifferencer::CompareRepeatedField(
+    const Message& message1, const Message& message2,
+    const FieldDescriptor* repeated_field,
+    std::vector<SpecificField>* parent_fields) {
+  GOOGLE_DCHECK(!repeated_field->is_map());
+  return CompareRepeatedRep(message1, message2, repeated_field, parent_fields);
+}
+
+bool MessageDifferencer::CompareRepeatedRep(
+    const Message& message1, const Message& message2,
+    const FieldDescriptor* repeated_field,
+    std::vector<SpecificField>* parent_fields) {
+  // the input FieldDescriptor is guaranteed to be repeated field.
+  GOOGLE_DCHECK(repeated_field->is_repeated());
+  const Reflection* reflection1 = message1.GetReflection();
+  const Reflection* reflection2 = message2.GetReflection();
+
+  const int count1 = reflection1->FieldSize(message1, repeated_field);
+  const int count2 = reflection2->FieldSize(message2, repeated_field);
+  const bool treated_as_subset = IsTreatedAsSubset(repeated_field);
+
+  // If the field is not treated as subset and no detailed reports is needed,
+  // we do a quick check on the number of the elements to avoid unnecessary
+  // comparison.
+  if (count1 != count2 && reporter_ == NULL && !treated_as_subset) {
+    return false;
+  }
+  // A match can never be found if message1 has more items than message2.
+  if (count1 > count2 && reporter_ == NULL) {
+    return false;
+  }
+
+  // These two list are used for store the index of the correspondent
+  // element in peer repeated field.
+  std::vector<int> match_list1;
+  std::vector<int> match_list2;
+
+  const MapKeyComparator* key_comparator = GetMapKeyComparator(repeated_field);
+  bool smart_list = IsTreatedAsSmartList(repeated_field);
+  bool simple_list = key_comparator == nullptr &&
+                     !IsTreatedAsSet(repeated_field) &&
+                     !IsTreatedAsSmartSet(repeated_field) && !smart_list;
+
+  // For simple lists, we avoid matching repeated field indices, saving the
+  // memory allocations that would otherwise be needed for match_list1 and
+  // match_list2.
+  if (!simple_list) {
+    // Try to match indices of the repeated fields. Return false if match fails.
+    if (!MatchRepeatedFieldIndices(message1, message2, repeated_field,
+                                   key_comparator, *parent_fields, &match_list1,
+                                   &match_list2) &&
+        reporter_ == nullptr) {
+      return false;
+    }
+  }
+
+  bool fieldDifferent = false;
+  SpecificField specific_field;
+  specific_field.field = repeated_field;
+
+  // At this point, we have already matched pairs of fields (with the reporting
+  // to be done later). Now to check if the paired elements are different.
+  int next_unmatched_index = 0;
+  for (int i = 0; i < count1; i++) {
+    if (simple_list && i >= count2) {
+      break;
+    }
+    if (!simple_list && match_list1[i] == -1) {
+      if (smart_list) {
+        if (reporter_ == nullptr) return false;
+        AddSpecificIndex(&specific_field, message1, repeated_field, i);
+        parent_fields->push_back(specific_field);
+        reporter_->ReportDeleted(message1, message2, *parent_fields);
+        parent_fields->pop_back();
+        fieldDifferent = true;
+        // Use -2 to mark this element has been reported.
+        match_list1[i] = -2;
+      }
+      continue;
+    }
+    if (smart_list) {
+      for (int j = next_unmatched_index; j < match_list1[i]; ++j) {
+        GOOGLE_CHECK_LE(0, j);
+        if (reporter_ == nullptr) return false;
+        specific_field.index = j;
+        AddSpecificNewIndex(&specific_field, message2, repeated_field, j);
+        parent_fields->push_back(specific_field);
+        reporter_->ReportAdded(message1, message2, *parent_fields);
+        parent_fields->pop_back();
+        fieldDifferent = true;
+        // Use -2 to mark this element has been reported.
+        match_list2[j] = -2;
+      }
+    }
+    AddSpecificIndex(&specific_field, message1, repeated_field, i);
+    if (simple_list) {
+      AddSpecificNewIndex(&specific_field, message2, repeated_field, i);
+    } else {
+      AddSpecificNewIndex(&specific_field, message2, repeated_field,
+                          match_list1[i]);
+      next_unmatched_index = match_list1[i] + 1;
+    }
+
+    const bool result = CompareFieldValueUsingParentFields(
+        message1, message2, repeated_field, i, specific_field.new_index,
+        parent_fields);
+
+    // If we have found differences, either report them or terminate if
+    // no reporter is present. Note that ReportModified, ReportMoved, and
+    // ReportMatched are all mutually exclusive.
+    if (!result) {
+      if (reporter_ == NULL) return false;
+      parent_fields->push_back(specific_field);
+      reporter_->ReportModified(message1, message2, *parent_fields);
+      parent_fields->pop_back();
+      fieldDifferent = true;
+    } else if (reporter_ != NULL &&
+               specific_field.index != specific_field.new_index &&
+               !specific_field.field->is_map() && report_moves_) {
+      parent_fields->push_back(specific_field);
+      reporter_->ReportMoved(message1, message2, *parent_fields);
+      parent_fields->pop_back();
+    } else if (report_matches_ && reporter_ != NULL) {
+      parent_fields->push_back(specific_field);
+      reporter_->ReportMatched(message1, message2, *parent_fields);
+      parent_fields->pop_back();
+    }
+  }
+
+  // Report any remaining additions or deletions.
+  for (int i = 0; i < count2; ++i) {
+    if (!simple_list && match_list2[i] != -1) continue;
+    if (simple_list && i < count1) continue;
+    if (!treated_as_subset) {
+      fieldDifferent = true;
+    }
+
+    if (reporter_ == NULL) continue;
+    specific_field.index = i;
+    AddSpecificNewIndex(&specific_field, message2, repeated_field, i);
+    parent_fields->push_back(specific_field);
+    reporter_->ReportAdded(message1, message2, *parent_fields);
+    parent_fields->pop_back();
+  }
+
+  for (int i = 0; i < count1; ++i) {
+    if (!simple_list && match_list1[i] != -1) continue;
+    if (simple_list && i < count2) continue;
+    assert(reporter_ != NULL);
+    AddSpecificIndex(&specific_field, message1, repeated_field, i);
+    parent_fields->push_back(specific_field);
+    reporter_->ReportDeleted(message1, message2, *parent_fields);
+    parent_fields->pop_back();
+    fieldDifferent = true;
+  }
+  return !fieldDifferent;
+}
+
+bool MessageDifferencer::CompareFieldValue(const Message& message1,
+                                           const Message& message2,
+                                           const FieldDescriptor* field,
+                                           int index1, int index2) {
+  return CompareFieldValueUsingParentFields(message1, message2, field, index1,
+                                            index2, NULL);
+}
+
+bool MessageDifferencer::CompareFieldValueUsingParentFields(
+    const Message& message1, const Message& message2,
+    const FieldDescriptor* field, int index1, int index2,
+    std::vector<SpecificField>* parent_fields) {
+  FieldContext field_context(parent_fields);
+  FieldComparator::ComparisonResult result = GetFieldComparisonResult(
+      message1, message2, field, index1, index2, &field_context);
+
+  if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+      result == FieldComparator::RECURSE) {
+    // Get the nested messages and compare them using one of the Compare
+    // methods.
+    const Reflection* reflection1 = message1.GetReflection();
+    const Reflection* reflection2 = message2.GetReflection();
+    const Message& m1 =
+        field->is_repeated()
+            ? reflection1->GetRepeatedMessage(message1, field, index1)
+            : reflection1->GetMessage(message1, field);
+    const Message& m2 =
+        field->is_repeated()
+            ? reflection2->GetRepeatedMessage(message2, field, index2)
+            : reflection2->GetMessage(message2, field);
+
+    // parent_fields is used in calls to Reporter methods.
+    if (parent_fields != NULL) {
+      // Append currently compared field to the end of parent_fields.
+      SpecificField specific_field;
+      specific_field.field = field;
+      AddSpecificIndex(&specific_field, message1, field, index1);
+      AddSpecificNewIndex(&specific_field, message2, field, index2);
+      parent_fields->push_back(specific_field);
+      const bool compare_result = Compare(m1, m2, parent_fields);
+      parent_fields->pop_back();
+      return compare_result;
+    } else {
+      // Recreates parent_fields as if m1 and m2 had no parents.
+      return Compare(m1, m2);
+    }
+  } else {
+    return (result == FieldComparator::SAME);
+  }
+}
+
+bool MessageDifferencer::CheckPathChanged(
+    const std::vector<SpecificField>& field_path) {
+  for (const SpecificField& specific_field : field_path) {
+    // Don't check indexes for map entries -- maps are unordered.
+    if (specific_field.field != nullptr && specific_field.field->is_map())
+      continue;
+    if (specific_field.index != specific_field.new_index) return true;
+  }
+  return false;
+}
+
+bool MessageDifferencer::IsTreatedAsSet(const FieldDescriptor* field) {
+  if (!field->is_repeated()) return false;
+  if (repeated_field_comparisons_.find(field) !=
+      repeated_field_comparisons_.end()) {
+    return repeated_field_comparisons_[field] == AS_SET;
+  }
+  return GetMapKeyComparator(field) == nullptr &&
+         repeated_field_comparison_ == AS_SET;
+}
+
+bool MessageDifferencer::IsTreatedAsSmartSet(const FieldDescriptor* field) {
+  if (!field->is_repeated()) return false;
+  if (repeated_field_comparisons_.find(field) !=
+      repeated_field_comparisons_.end()) {
+    return repeated_field_comparisons_[field] == AS_SMART_SET;
+  }
+  return GetMapKeyComparator(field) == nullptr &&
+         repeated_field_comparison_ == AS_SMART_SET;
+}
+
+bool MessageDifferencer::IsTreatedAsSmartList(const FieldDescriptor* field) {
+  if (!field->is_repeated()) return false;
+  if (repeated_field_comparisons_.find(field) !=
+      repeated_field_comparisons_.end()) {
+    return repeated_field_comparisons_[field] == AS_SMART_LIST;
+  }
+  return GetMapKeyComparator(field) == nullptr &&
+         repeated_field_comparison_ == AS_SMART_LIST;
+}
+
+bool MessageDifferencer::IsTreatedAsSubset(const FieldDescriptor* field) {
+  return scope_ == PARTIAL &&
+         (IsTreatedAsSet(field) || GetMapKeyComparator(field) != NULL);
+}
+
+bool MessageDifferencer::IsIgnored(
+    const Message& message1, const Message& message2,
+    const FieldDescriptor* field,
+    const std::vector<SpecificField>& parent_fields) {
+  if (ignored_fields_.find(field) != ignored_fields_.end()) {
+    return true;
+  }
+  for (IgnoreCriteria* criteria : ignore_criteria_) {
+    if (criteria->IsIgnored(message1, message2, field, parent_fields)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool MessageDifferencer::IsUnknownFieldIgnored(
+    const Message& message1, const Message& message2,
+    const SpecificField& field,
+    const std::vector<SpecificField>& parent_fields) {
+  for (IgnoreCriteria* criteria : ignore_criteria_) {
+    if (criteria->IsUnknownFieldIgnored(message1, message2, field,
+                                        parent_fields)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+const MessageDifferencer::MapKeyComparator*
+MessageDifferencer ::GetMapKeyComparator(const FieldDescriptor* field) const {
+  if (!field->is_repeated()) return NULL;
+  FieldKeyComparatorMap::const_iterator it =
+      map_field_key_comparator_.find(field);
+  if (it != map_field_key_comparator_.end()) {
+    return it->second;
+  }
+  if (field->is_map()) {
+    // field cannot already be treated as list or set since TreatAsList() and
+    // TreatAsSet() call GetMapKeyComparator() and fail if it returns non-NULL.
+    return &map_entry_key_comparator_;
+  }
+  return NULL;
+}
+
+namespace {
+
+typedef std::pair<int, const UnknownField*> IndexUnknownFieldPair;
+
+struct UnknownFieldOrdering {
+  inline bool operator()(const IndexUnknownFieldPair& a,
+                         const IndexUnknownFieldPair& b) const {
+    if (a.second->number() < b.second->number()) return true;
+    if (a.second->number() > b.second->number()) return false;
+    return a.second->type() < b.second->type();
+  }
+};
+
+}  // namespace
+
+bool MessageDifferencer::UnpackAnyField::UnpackAny(
+    const Message& any, std::unique_ptr<Message>* data) {
+  const Reflection* reflection = any.GetReflection();
+  const FieldDescriptor* type_url_field;
+  const FieldDescriptor* value_field;
+  if (!internal::GetAnyFieldDescriptors(any, &type_url_field, &value_field)) {
+    return false;
+  }
+  const std::string& type_url = reflection->GetString(any, type_url_field);
+  std::string full_type_name;
+  if (!internal::ParseAnyTypeUrl(type_url, &full_type_name)) {
+    return false;
+  }
+
+  const Descriptor* desc =
+      any.GetDescriptor()->file()->pool()->FindMessageTypeByName(
+          full_type_name);
+  if (desc == NULL) {
+    GOOGLE_LOG(INFO) << "Proto type '" << full_type_name << "' not found";
+    return false;
+  }
+
+  if (dynamic_message_factory_ == NULL) {
+    dynamic_message_factory_.reset(new DynamicMessageFactory());
+  }
+  data->reset(dynamic_message_factory_->GetPrototype(desc)->New());
+  std::string serialized_value = reflection->GetString(any, value_field);
+  if (!(*data)->ParsePartialFromString(serialized_value)) {
+    GOOGLE_DLOG(ERROR) << "Failed to parse value for " << full_type_name;
+    return false;
+  }
+  return true;
+}
+
+bool MessageDifferencer::CompareUnknownFields(
+    const Message& message1, const Message& message2,
+    const UnknownFieldSet& unknown_field_set1,
+    const UnknownFieldSet& unknown_field_set2,
+    std::vector<SpecificField>* parent_field) {
+  // Ignore unknown fields in EQUIVALENT mode.
+  if (message_field_comparison_ == EQUIVALENT) return true;
+
+  if (unknown_field_set1.empty() && unknown_field_set2.empty()) {
+    return true;
+  }
+
+  bool is_different = false;
+
+  // We first sort the unknown fields by field number and type (in other words,
+  // in tag order), making sure to preserve ordering of values with the same
+  // tag.  This allows us to report only meaningful differences between the
+  // two sets -- that is, differing values for the same tag.  We use
+  // IndexUnknownFieldPairs to keep track of the field's original index for
+  // reporting purposes.
+  std::vector<IndexUnknownFieldPair> fields1;  // unknown_field_set1, sorted
+  std::vector<IndexUnknownFieldPair> fields2;  // unknown_field_set2, sorted
+  fields1.reserve(unknown_field_set1.field_count());
+  fields2.reserve(unknown_field_set2.field_count());
+
+  for (int i = 0; i < unknown_field_set1.field_count(); i++) {
+    fields1.push_back(std::make_pair(i, &unknown_field_set1.field(i)));
+  }
+  for (int i = 0; i < unknown_field_set2.field_count(); i++) {
+    fields2.push_back(std::make_pair(i, &unknown_field_set2.field(i)));
+  }
+
+  UnknownFieldOrdering is_before;
+  std::stable_sort(fields1.begin(), fields1.end(), is_before);
+  std::stable_sort(fields2.begin(), fields2.end(), is_before);
+
+  // In order to fill in SpecificField::index, we have to keep track of how
+  // many values we've seen with the same field number and type.
+  // current_repeated points at the first field in this range, and
+  // current_repeated_start{1,2} are the indexes of the first field in the
+  // range within fields1 and fields2.
+  const UnknownField* current_repeated = NULL;
+  int current_repeated_start1 = 0;
+  int current_repeated_start2 = 0;
+
+  // Now that we have two sorted lists, we can detect fields which appear only
+  // in one list or the other by traversing them simultaneously.
+  size_t index1 = 0;
+  size_t index2 = 0;
+  while (index1 < fields1.size() || index2 < fields2.size()) {
+    enum {
+      ADDITION,
+      DELETION,
+      MODIFICATION,
+      COMPARE_GROUPS,
+      NO_CHANGE
+    } change_type;
+
+    // focus_field is the field we're currently reporting on.  (In the case
+    // of a modification, it's the field on the left side.)
+    const UnknownField* focus_field;
+    bool match = false;
+
+    if (index2 == fields2.size() ||
+        (index1 < fields1.size() &&
+         is_before(fields1[index1], fields2[index2]))) {
+      // fields1[index1] is not present in fields2.
+      change_type = DELETION;
+      focus_field = fields1[index1].second;
+    } else if (index1 == fields1.size() ||
+               is_before(fields2[index2], fields1[index1])) {
+      // fields2[index2] is not present in fields1.
+      if (scope_ == PARTIAL) {
+        // Ignore.
+        ++index2;
+        continue;
+      }
+      change_type = ADDITION;
+      focus_field = fields2[index2].second;
+    } else {
+      // Field type and number are the same.  See if the values differ.
+      change_type = MODIFICATION;
+      focus_field = fields1[index1].second;
+
+      switch (focus_field->type()) {
+        case UnknownField::TYPE_VARINT:
+          match = fields1[index1].second->varint() ==
+                  fields2[index2].second->varint();
+          break;
+        case UnknownField::TYPE_FIXED32:
+          match = fields1[index1].second->fixed32() ==
+                  fields2[index2].second->fixed32();
+          break;
+        case UnknownField::TYPE_FIXED64:
+          match = fields1[index1].second->fixed64() ==
+                  fields2[index2].second->fixed64();
+          break;
+        case UnknownField::TYPE_LENGTH_DELIMITED:
+          match = fields1[index1].second->length_delimited() ==
+                  fields2[index2].second->length_delimited();
+          break;
+        case UnknownField::TYPE_GROUP:
+          // We must deal with this later, after building the SpecificField.
+          change_type = COMPARE_GROUPS;
+          break;
+      }
+      if (match && change_type != COMPARE_GROUPS) {
+        change_type = NO_CHANGE;
+      }
+    }
+
+    if (current_repeated == NULL ||
+        focus_field->number() != current_repeated->number() ||
+        focus_field->type() != current_repeated->type()) {
+      // We've started a new repeated field.
+      current_repeated = focus_field;
+      current_repeated_start1 = index1;
+      current_repeated_start2 = index2;
+    }
+
+    if (change_type == NO_CHANGE && reporter_ == NULL) {
+      // Fields were already compared and matched and we have no reporter.
+      ++index1;
+      ++index2;
+      continue;
+    }
+
+    // Build the SpecificField.  This is slightly complicated.
+    SpecificField specific_field;
+    specific_field.unknown_field_number = focus_field->number();
+    specific_field.unknown_field_type = focus_field->type();
+
+    specific_field.unknown_field_set1 = &unknown_field_set1;
+    specific_field.unknown_field_set2 = &unknown_field_set2;
+
+    if (change_type != ADDITION) {
+      specific_field.unknown_field_index1 = fields1[index1].first;
+    }
+    if (change_type != DELETION) {
+      specific_field.unknown_field_index2 = fields2[index2].first;
+    }
+
+    // Calculate the field index.
+    if (change_type == ADDITION) {
+      specific_field.index = index2 - current_repeated_start2;
+      specific_field.new_index = index2 - current_repeated_start2;
+    } else {
+      specific_field.index = index1 - current_repeated_start1;
+      specific_field.new_index = index2 - current_repeated_start2;
+    }
+
+    if (IsUnknownFieldIgnored(message1, message2, specific_field,
+                              *parent_field)) {
+      if (report_ignores_ && reporter_ != NULL) {
+        parent_field->push_back(specific_field);
+        reporter_->ReportUnknownFieldIgnored(message1, message2, *parent_field);
+        parent_field->pop_back();
+      }
+      if (change_type != ADDITION) ++index1;
+      if (change_type != DELETION) ++index2;
+      continue;
+    }
+
+    if (change_type == ADDITION || change_type == DELETION ||
+        change_type == MODIFICATION) {
+      if (reporter_ == NULL) {
+        // We found a difference and we have no reporter.
+        return false;
+      }
+      is_different = true;
+    }
+
+    parent_field->push_back(specific_field);
+
+    switch (change_type) {
+      case ADDITION:
+        reporter_->ReportAdded(message1, message2, *parent_field);
+        ++index2;
+        break;
+      case DELETION:
+        reporter_->ReportDeleted(message1, message2, *parent_field);
+        ++index1;
+        break;
+      case MODIFICATION:
+        reporter_->ReportModified(message1, message2, *parent_field);
+        ++index1;
+        ++index2;
+        break;
+      case COMPARE_GROUPS:
+        if (!CompareUnknownFields(
+                message1, message2, fields1[index1].second->group(),
+                fields2[index2].second->group(), parent_field)) {
+          if (reporter_ == NULL) return false;
+          is_different = true;
+          reporter_->ReportModified(message1, message2, *parent_field);
+        }
+        ++index1;
+        ++index2;
+        break;
+      case NO_CHANGE:
+        ++index1;
+        ++index2;
+        if (report_matches_) {
+          reporter_->ReportMatched(message1, message2, *parent_field);
+        }
+    }
+
+    parent_field->pop_back();
+  }
+
+  return !is_different;
+}
+
+namespace {
+
+// Find maximum bipartite matching using the argumenting path algorithm.
+class MaximumMatcher {
+ public:
+  typedef std::function<bool(int, int)> NodeMatchCallback;
+  // MaximumMatcher takes ownership of the passed in callback and uses it to
+  // determine whether a node on the left side of the bipartial graph matches
+  // a node on the right side. count1 is the number of nodes on the left side
+  // of the graph and count2 to is the number of nodes on the right side.
+  // Every node is referred to using 0-based indices.
+  // If a maximum match is found, the result will be stored in match_list1 and
+  // match_list2. match_list1[i] == j means the i-th node on the left side is
+  // matched to the j-th node on the right side and match_list2[x] == y means
+  // the x-th node on the right side is matched to y-th node on the left side.
+  // match_list1[i] == -1 means the node is not matched. Same with match_list2.
+  MaximumMatcher(int count1, int count2, NodeMatchCallback callback,
+                 std::vector<int>* match_list1, std::vector<int>* match_list2);
+  // Find a maximum match and return the number of matched node pairs.
+  // If early_return is true, this method will return 0 immediately when it
+  // finds that not all nodes on the left side can be matched.
+  int FindMaximumMatch(bool early_return);
+
+ private:
+  // Determines whether the node on the left side of the bipartial graph
+  // matches the one on the right side.
+  bool Match(int left, int right);
+  // Find an argumenting path starting from the node v on the left side. If a
+  // path can be found, update match_list2_ to reflect the path and return
+  // true.
+  bool FindArgumentPathDFS(int v, std::vector<bool>* visited);
+
+  int count1_;
+  int count2_;
+  NodeMatchCallback match_callback_;
+  std::map<std::pair<int, int>, bool> cached_match_results_;
+  std::vector<int>* match_list1_;
+  std::vector<int>* match_list2_;
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MaximumMatcher);
+};
+
+MaximumMatcher::MaximumMatcher(int count1, int count2,
+                               NodeMatchCallback callback,
+                               std::vector<int>* match_list1,
+                               std::vector<int>* match_list2)
+    : count1_(count1),
+      count2_(count2),
+      match_callback_(std::move(callback)),
+      match_list1_(match_list1),
+      match_list2_(match_list2) {
+  match_list1_->assign(count1, -1);
+  match_list2_->assign(count2, -1);
+}
+
+int MaximumMatcher::FindMaximumMatch(bool early_return) {
+  int result = 0;
+  for (int i = 0; i < count1_; ++i) {
+    std::vector<bool> visited(count1_);
+    if (FindArgumentPathDFS(i, &visited)) {
+      ++result;
+    } else if (early_return) {
+      return 0;
+    }
+  }
+  // Backfill match_list1_ as we only filled match_list2_ when finding
+  // argumenting paths.
+  for (int i = 0; i < count2_; ++i) {
+    if ((*match_list2_)[i] != -1) {
+      (*match_list1_)[(*match_list2_)[i]] = i;
+    }
+  }
+  return result;
+}
+
+bool MaximumMatcher::Match(int left, int right) {
+  std::pair<int, int> p(left, right);
+  std::map<std::pair<int, int>, bool>::iterator it =
+      cached_match_results_.find(p);
+  if (it != cached_match_results_.end()) {
+    return it->second;
+  }
+  cached_match_results_[p] = match_callback_(left, right);
+  return cached_match_results_[p];
+}
+
+bool MaximumMatcher::FindArgumentPathDFS(int v, std::vector<bool>* visited) {
+  (*visited)[v] = true;
+  // We try to match those un-matched nodes on the right side first. This is
+  // the step that the naive greedy matching algorithm uses. In the best cases
+  // where the greedy algorithm can find a maximum matching, we will always
+  // find a match in this step and the performance will be identical to the
+  // greedy algorithm.
+  for (int i = 0; i < count2_; ++i) {
+    int matched = (*match_list2_)[i];
+    if (matched == -1 && Match(v, i)) {
+      (*match_list2_)[i] = v;
+      return true;
+    }
+  }
+  // Then we try those already matched nodes and see if we can find an
+  // alternative match for the node matched to them.
+  // The greedy algorithm will stop before this and fail to produce the
+  // correct result.
+  for (int i = 0; i < count2_; ++i) {
+    int matched = (*match_list2_)[i];
+    if (matched != -1 && Match(v, i)) {
+      if (!(*visited)[matched] && FindArgumentPathDFS(matched, visited)) {
+        (*match_list2_)[i] = v;
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+}  // namespace
+
+bool MessageDifferencer::MatchRepeatedFieldIndices(
+    const Message& message1, const Message& message2,
+    const FieldDescriptor* repeated_field,
+    const MapKeyComparator* key_comparator,
+    const std::vector<SpecificField>& parent_fields,
+    std::vector<int>* match_list1, std::vector<int>* match_list2) {
+  const int count1 =
+      message1.GetReflection()->FieldSize(message1, repeated_field);
+  const int count2 =
+      message2.GetReflection()->FieldSize(message2, repeated_field);
+  const bool is_treated_as_smart_set = IsTreatedAsSmartSet(repeated_field);
+
+  match_list1->assign(count1, -1);
+  match_list2->assign(count2, -1);
+  // Ensure that we don't report differences during the matching process. Since
+  // field comparators could potentially use this message differencer object to
+  // perform further comparisons, turn off reporting here and re-enable it
+  // before returning.
+  Reporter* reporter = reporter_;
+  reporter_ = NULL;
+  NumDiffsReporter num_diffs_reporter;
+  std::vector<int32_t> num_diffs_list1;
+  if (is_treated_as_smart_set) {
+    num_diffs_list1.assign(count1, std::numeric_limits<int32_t>::max());
+  }
+
+  bool success = true;
+  // Find potential match if this is a special repeated field.
+  if (scope_ == PARTIAL) {
+    // When partial matching is enabled, Compare(a, b) && Compare(a, c)
+    // doesn't necessarily imply Compare(b, c). Therefore a naive greedy
+    // algorithm will fail to find a maximum matching.
+    // Here we use the augmenting path algorithm.
+    auto callback = [&](int i1, int i2) {
+      return IsMatch(repeated_field, key_comparator, &message1, &message2,
+                     parent_fields, nullptr, i1, i2);
+    };
+    MaximumMatcher matcher(count1, count2, std::move(callback), match_list1,
+                           match_list2);
+    // If diff info is not needed, we should end the matching process as
+    // soon as possible if not all items can be matched.
+    bool early_return = (reporter == nullptr);
+    int match_count = matcher.FindMaximumMatch(early_return);
+    if (match_count != count1 && early_return) return false;
+    success = success && (match_count == count1);
+  } else {
+    int start_offset = 0;
+    // If the two repeated fields are treated as sets, optimize for the case
+    // where both start with same items stored in the same order.
+    if (IsTreatedAsSet(repeated_field) || is_treated_as_smart_set ||
+        IsTreatedAsSmartList(repeated_field)) {
+      start_offset = std::min(count1, count2);
+      for (int i = 0; i < count1 && i < count2; i++) {
+        if (IsMatch(repeated_field, key_comparator, &message1, &message2,
+                    parent_fields, nullptr, i, i)) {
+          match_list1->at(i) = i;
+          match_list2->at(i) = i;
+        } else {
+          start_offset = i;
+          break;
+        }
+      }
+    }
+    for (int i = start_offset; i < count1; ++i) {
+      // Indicates any matched elements for this repeated field.
+      bool match = false;
+      int matched_j = -1;
+
+      for (int j = start_offset; j < count2; j++) {
+        if (match_list2->at(j) != -1) {
+          if (!is_treated_as_smart_set || num_diffs_list1[i] == 0 ||
+              num_diffs_list1[match_list2->at(j)] == 0) {
+            continue;
+          }
+        }
+
+        if (is_treated_as_smart_set) {
+          num_diffs_reporter.Reset();
+          match = IsMatch(repeated_field, key_comparator, &message1, &message2,
+                          parent_fields, &num_diffs_reporter, i, j);
+        } else {
+          match = IsMatch(repeated_field, key_comparator, &message1, &message2,
+                          parent_fields, nullptr, i, j);
+        }
+
+        if (is_treated_as_smart_set) {
+          if (match) {
+            num_diffs_list1[i] = 0;
+          } else if (repeated_field->cpp_type() ==
+                     FieldDescriptor::CPPTYPE_MESSAGE) {
+            // Replace with the one with fewer diffs.
+            const int32_t num_diffs = num_diffs_reporter.GetNumDiffs();
+            if (num_diffs < num_diffs_list1[i]) {
+              // If j has been already matched to some element, ensure the
+              // current num_diffs is smaller.
+              if (match_list2->at(j) == -1 ||
+                  num_diffs < num_diffs_list1[match_list2->at(j)]) {
+                num_diffs_list1[i] = num_diffs;
+                match = true;
+              }
+            }
+          }
+        }
+
+        if (match) {
+          matched_j = j;
+          if (!is_treated_as_smart_set || num_diffs_list1[i] == 0) {
+            break;
+          }
+        }
+      }
+
+      match = (matched_j != -1);
+      if (match) {
+        if (is_treated_as_smart_set && match_list2->at(matched_j) != -1) {
+          // This is to revert the previously matched index in list2.
+          match_list1->at(match_list2->at(matched_j)) = -1;
+          match = false;
+        }
+        match_list1->at(i) = matched_j;
+        match_list2->at(matched_j) = i;
+      }
+      if (!match && reporter == nullptr) return false;
+      success = success && match;
+    }
+  }
+
+  if (IsTreatedAsSmartList(repeated_field)) {
+    match_indices_for_smart_list_callback_(match_list1, match_list2);
+  }
+
+  reporter_ = reporter;
+
+  return success;
+}
+
+FieldComparator::ComparisonResult MessageDifferencer::GetFieldComparisonResult(
+    const Message& message1, const Message& message2,
+    const FieldDescriptor* field, int index1, int index2,
+    const FieldContext* field_context) {
+  FieldComparator* comparator = field_comparator_kind_ == kFCBase
+                                    ? field_comparator_.base
+                                    : field_comparator_.default_impl;
+  return comparator->Compare(message1, message2, field, index1, index2,
+                             field_context);
+}
+
+// ===========================================================================
+
+MessageDifferencer::Reporter::Reporter() {}
+MessageDifferencer::Reporter::~Reporter() {}
+
+// ===========================================================================
+
+MessageDifferencer::MapKeyComparator::MapKeyComparator() {}
+MessageDifferencer::MapKeyComparator::~MapKeyComparator() {}
+
+// ===========================================================================
+
+MessageDifferencer::IgnoreCriteria::IgnoreCriteria() {}
+MessageDifferencer::IgnoreCriteria::~IgnoreCriteria() {}
+
+// ===========================================================================
+
+// Note that the printer's delimiter is not used, because if we are given a
+// printer, we don't know its delimiter.
+MessageDifferencer::StreamReporter::StreamReporter(
+    io::ZeroCopyOutputStream* output)
+    : printer_(new io::Printer(output, '$')),
+      delete_printer_(true),
+      report_modified_aggregates_(false),
+      message1_(nullptr),
+      message2_(nullptr) {}
+
+MessageDifferencer::StreamReporter::StreamReporter(io::Printer* printer)
+    : printer_(printer),
+      delete_printer_(false),
+      report_modified_aggregates_(false),
+      message1_(nullptr),
+      message2_(nullptr) {}
+
+MessageDifferencer::StreamReporter::~StreamReporter() {
+  if (delete_printer_) delete printer_;
+}
+
+void MessageDifferencer::StreamReporter::PrintPath(
+    const std::vector<SpecificField>& field_path, bool left_side) {
+  for (size_t i = 0; i < field_path.size(); ++i) {
+    SpecificField specific_field = field_path[i];
+
+    if (specific_field.field != nullptr &&
+        specific_field.field->name() == "value") {
+      // check to see if this the value label of a map value.  If so, skip it
+      // because it isn't meaningful
+      if (i > 0 && field_path[i - 1].field->is_map()) {
+        continue;
+      }
+    }
+    if (i > 0) {
+      printer_->Print(".");
+    }
+    if (specific_field.field != NULL) {
+      if (specific_field.field->is_extension()) {
+        printer_->Print("($name$)", "name", specific_field.field->full_name());
+      } else {
+        printer_->PrintRaw(specific_field.field->name());
+      }
+
+      if (specific_field.field->is_map()) {
+        PrintMapKey(left_side, specific_field);
+        continue;
+      }
+    } else {
+      printer_->PrintRaw(StrCat(specific_field.unknown_field_number));
+    }
+    if (left_side && specific_field.index >= 0) {
+      printer_->Print("[$name$]", "name", StrCat(specific_field.index));
+    }
+    if (!left_side && specific_field.new_index >= 0) {
+      printer_->Print("[$name$]", "name",
+                      StrCat(specific_field.new_index));
+    }
+  }
+}
+
+
+void MessageDifferencer::StreamReporter::PrintValue(
+    const Message& message, const std::vector<SpecificField>& field_path,
+    bool left_side) {
+  const SpecificField& specific_field = field_path.back();
+  const FieldDescriptor* field = specific_field.field;
+  if (field != NULL) {
+    std::string output;
+    int index = left_side ? specific_field.index : specific_field.new_index;
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      const Reflection* reflection = message.GetReflection();
+      const Message& field_message =
+          field->is_repeated()
+              ? reflection->GetRepeatedMessage(message, field, index)
+              : reflection->GetMessage(message, field);
+      const FieldDescriptor* fd = nullptr;
+
+      if (field->is_map() && message1_ != nullptr && message2_ != nullptr) {
+        fd = field_message.GetDescriptor()->field(1);
+        if (fd->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+          output = PrintShortTextFormat(
+              field_message.GetReflection()->GetMessage(field_message, fd));
+        } else {
+          TextFormat::PrintFieldValueToString(field_message, fd, -1, &output);
+        }
+      } else {
+        output = PrintShortTextFormat(field_message);
+      }
+      if (output.empty()) {
+        printer_->Print("{ }");
+      } else {
+        if ((fd != nullptr) &&
+            (fd->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE)) {
+          printer_->PrintRaw(output);
+        } else {
+          printer_->Print("{ $name$ }", "name", output);
+        }
+      }
+    } else {
+      TextFormat::PrintFieldValueToString(message, field, index, &output);
+      printer_->PrintRaw(output);
+    }
+  } else {
+    const UnknownFieldSet* unknown_fields =
+        (left_side ? specific_field.unknown_field_set1
+                   : specific_field.unknown_field_set2);
+    const UnknownField* unknown_field =
+        &unknown_fields->field(left_side ? specific_field.unknown_field_index1
+                                         : specific_field.unknown_field_index2);
+    PrintUnknownFieldValue(unknown_field);
+  }
+}
+
+void MessageDifferencer::StreamReporter::PrintUnknownFieldValue(
+    const UnknownField* unknown_field) {
+  GOOGLE_CHECK(unknown_field != NULL) << " Cannot print NULL unknown_field.";
+
+  std::string output;
+  switch (unknown_field->type()) {
+    case UnknownField::TYPE_VARINT:
+      output = StrCat(unknown_field->varint());
+      break;
+    case UnknownField::TYPE_FIXED32:
+      output = StrCat(
+          "0x", strings::Hex(unknown_field->fixed32(), strings::ZERO_PAD_8));
+      break;
+    case UnknownField::TYPE_FIXED64:
+      output = StrCat(
+          "0x", strings::Hex(unknown_field->fixed64(), strings::ZERO_PAD_16));
+      break;
+    case UnknownField::TYPE_LENGTH_DELIMITED:
+      output = StringPrintf(
+          "\"%s\"", CEscape(unknown_field->length_delimited()).c_str());
+      break;
+    case UnknownField::TYPE_GROUP:
+      // TODO(kenton):  Print the contents of the group like we do for
+      //   messages.  Requires an equivalent of ShortDebugString() for
+      //   UnknownFieldSet.
+      output = "{ ... }";
+      break;
+  }
+  printer_->PrintRaw(output);
+}
+
+void MessageDifferencer::StreamReporter::Print(const std::string& str) {
+  printer_->Print(str.c_str());
+}
+
+void MessageDifferencer::StreamReporter::PrintMapKey(
+    bool left_side, const SpecificField& specific_field) {
+  if (message1_ == nullptr || message2_ == nullptr) {
+    GOOGLE_LOG(INFO) << "PrintPath cannot log map keys; "
+                 "use SetMessages to provide the messages "
+                 "being compared prior to any processing.";
+    return;
+  }
+
+  const Message* found_message =
+      left_side ? specific_field.map_entry1 : specific_field.map_entry2;
+  std::string key_string = "";
+  if (found_message != nullptr) {
+    // NB: the map key is always the first field
+    const FieldDescriptor* fd = found_message->GetDescriptor()->field(0);
+    if (fd->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
+      // Not using PrintFieldValueToString for strings to avoid extra
+      // characters
+      key_string = found_message->GetReflection()->GetString(
+          *found_message, found_message->GetDescriptor()->field(0));
+    } else {
+      TextFormat::PrintFieldValueToString(*found_message, fd, -1, &key_string);
+    }
+    if (key_string.empty()) {
+      key_string = "''";
+    }
+    printer_->PrintRaw(StrCat("[", key_string, "]"));
+  }
+}
+
+void MessageDifferencer::StreamReporter::ReportAdded(
+    const Message& /*message1*/, const Message& message2,
+    const std::vector<SpecificField>& field_path) {
+  printer_->Print("added: ");
+  PrintPath(field_path, false);
+  printer_->Print(": ");
+  PrintValue(message2, field_path, false);
+  printer_->Print("\n");  // Print for newlines.
+}
+
+void MessageDifferencer::StreamReporter::ReportDeleted(
+    const Message& message1, const Message& /*message2*/,
+    const std::vector<SpecificField>& field_path) {
+  printer_->Print("deleted: ");
+  PrintPath(field_path, true);
+  printer_->Print(": ");
+  PrintValue(message1, field_path, true);
+  printer_->Print("\n");  // Print for newlines
+}
+
+void MessageDifferencer::StreamReporter::ReportModified(
+    const Message& message1, const Message& message2,
+    const std::vector<SpecificField>& field_path) {
+  if (!report_modified_aggregates_ && field_path.back().field == NULL) {
+    if (field_path.back().unknown_field_type == UnknownField::TYPE_GROUP) {
+      // Any changes to the subfields have already been printed.
+      return;
+    }
+  } else if (!report_modified_aggregates_) {
+    if (field_path.back().field->cpp_type() ==
+        FieldDescriptor::CPPTYPE_MESSAGE) {
+      // Any changes to the subfields have already been printed.
+      return;
+    }
+  }
+
+  printer_->Print("modified: ");
+  PrintPath(field_path, true);
+  if (CheckPathChanged(field_path)) {
+    printer_->Print(" -> ");
+    PrintPath(field_path, false);
+  }
+  printer_->Print(": ");
+  PrintValue(message1, field_path, true);
+  printer_->Print(" -> ");
+  PrintValue(message2, field_path, false);
+  printer_->Print("\n");  // Print for newlines.
+}
+
+void MessageDifferencer::StreamReporter::ReportMoved(
+    const Message& message1, const Message& /*message2*/,
+    const std::vector<SpecificField>& field_path) {
+  printer_->Print("moved: ");
+  PrintPath(field_path, true);
+  printer_->Print(" -> ");
+  PrintPath(field_path, false);
+  printer_->Print(" : ");
+  PrintValue(message1, field_path, true);
+  printer_->Print("\n");  // Print for newlines.
+}
+
+void MessageDifferencer::StreamReporter::ReportMatched(
+    const Message& message1, const Message& /*message2*/,
+    const std::vector<SpecificField>& field_path) {
+  printer_->Print("matched: ");
+  PrintPath(field_path, true);
+  if (CheckPathChanged(field_path)) {
+    printer_->Print(" -> ");
+    PrintPath(field_path, false);
+  }
+  printer_->Print(" : ");
+  PrintValue(message1, field_path, true);
+  printer_->Print("\n");  // Print for newlines.
+}
+
+void MessageDifferencer::StreamReporter::ReportIgnored(
+    const Message& /*message1*/, const Message& /*message2*/,
+    const std::vector<SpecificField>& field_path) {
+  printer_->Print("ignored: ");
+  PrintPath(field_path, true);
+  if (CheckPathChanged(field_path)) {
+    printer_->Print(" -> ");
+    PrintPath(field_path, false);
+  }
+  printer_->Print("\n");  // Print for newlines.
+}
+
+void MessageDifferencer::StreamReporter::SetMessages(const Message& message1,
+                                                     const Message& message2) {
+  message1_ = &message1;
+  message2_ = &message2;
+}
+
+void MessageDifferencer::StreamReporter::ReportUnknownFieldIgnored(
+    const Message& /*message1*/, const Message& /*message2*/,
+    const std::vector<SpecificField>& field_path) {
+  printer_->Print("ignored: ");
+  PrintPath(field_path, true);
+  if (CheckPathChanged(field_path)) {
+    printer_->Print(" -> ");
+    PrintPath(field_path, false);
+  }
+  printer_->Print("\n");  // Print for newlines.
+}
+
+MessageDifferencer::MapKeyComparator*
+MessageDifferencer::CreateMultipleFieldsMapKeyComparator(
+    const std::vector<std::vector<const FieldDescriptor*> >& key_field_paths) {
+  return new MultipleFieldsMapKeyComparator(this, key_field_paths);
+}
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/time_util.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/time_util.cpp
new file mode 100644
index 0000000..9893aa3
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/time_util.cpp
@@ -0,0 +1,514 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/time_util.h>
+
+#include <cstdint>
+
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/duration.pb.h>
+#include <google/protobuf/timestamp.pb.h>
+#include <google/protobuf/stubs/int128.h>
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/stubs/time.h>
+
+// Must go after other includes.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+using google::protobuf::Duration;
+using google::protobuf::Timestamp;
+
+namespace {
+static const int kNanosPerSecond = 1000000000;
+static const int kMicrosPerSecond = 1000000;
+static const int kMillisPerSecond = 1000;
+static const int kNanosPerMillisecond = 1000000;
+static const int kNanosPerMicrosecond = 1000;
+static const int kSecondsPerMinute = 60;  // Note that we ignore leap seconds.
+static const int kSecondsPerHour = 3600;
+
+template <typename T>
+T CreateNormalized(int64_t seconds, int64_t nanos);
+
+template <>
+Timestamp CreateNormalized(int64_t seconds, int64_t nanos) {
+  // Make sure nanos is in the range.
+  if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
+    seconds += nanos / kNanosPerSecond;
+    nanos = nanos % kNanosPerSecond;
+  }
+  // For Timestamp nanos should be in the range [0, 999999999]
+  if (nanos < 0) {
+    seconds -= 1;
+    nanos += kNanosPerSecond;
+  }
+  GOOGLE_DCHECK(seconds >= TimeUtil::kTimestampMinSeconds &&
+         seconds <= TimeUtil::kTimestampMaxSeconds);
+  Timestamp result;
+  result.set_seconds(seconds);
+  result.set_nanos(static_cast<int32_t>(nanos));
+  return result;
+}
+
+template <>
+Duration CreateNormalized(int64_t seconds, int64_t nanos) {
+  // Make sure nanos is in the range.
+  if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
+    seconds += nanos / kNanosPerSecond;
+    nanos = nanos % kNanosPerSecond;
+  }
+  // nanos should have the same sign as seconds.
+  if (seconds < 0 && nanos > 0) {
+    seconds += 1;
+    nanos -= kNanosPerSecond;
+  } else if (seconds > 0 && nanos < 0) {
+    seconds -= 1;
+    nanos += kNanosPerSecond;
+  }
+  GOOGLE_DCHECK(seconds >= TimeUtil::kDurationMinSeconds &&
+         seconds <= TimeUtil::kDurationMaxSeconds);
+  Duration result;
+  result.set_seconds(seconds);
+  result.set_nanos(static_cast<int32_t>(nanos));
+  return result;
+}
+
+// Format nanoseconds with either 3, 6, or 9 digits depending on the required
+// precision to represent the exact value.
+std::string FormatNanos(int32_t nanos) {
+  if (nanos % kNanosPerMillisecond == 0) {
+    return StringPrintf("%03d", nanos / kNanosPerMillisecond);
+  } else if (nanos % kNanosPerMicrosecond == 0) {
+    return StringPrintf("%06d", nanos / kNanosPerMicrosecond);
+  } else {
+    return StringPrintf("%09d", nanos);
+  }
+}
+
+std::string FormatTime(int64_t seconds, int32_t nanos) {
+  return ::google::protobuf::internal::FormatTime(seconds, nanos);
+}
+
+bool ParseTime(const std::string& value, int64_t* seconds, int32_t* nanos) {
+  return ::google::protobuf::internal::ParseTime(value, seconds, nanos);
+}
+
+void CurrentTime(int64_t* seconds, int32_t* nanos) {
+  return ::google::protobuf::internal::GetCurrentTime(seconds, nanos);
+}
+
+// Truncates the remainder part after division.
+int64_t RoundTowardZero(int64_t value, int64_t divider) {
+  int64_t result = value / divider;
+  int64_t remainder = value % divider;
+  // Before C++11, the sign of the remainder is implementation dependent if
+  // any of the operands is negative. Here we try to enforce C++11's "rounded
+  // toward zero" semantics. For example, for (-5) / 2 an implementation may
+  // give -3 as the result with the remainder being 1. This function ensures
+  // we always return -2 (closer to zero) regardless of the implementation.
+  if (result < 0 && remainder > 0) {
+    return result + 1;
+  } else {
+    return result;
+  }
+}
+}  // namespace
+
+// Actually define these static const integers. Required by C++ standard (but
+// some compilers don't like it).
+#ifndef _MSC_VER
+const int64_t TimeUtil::kTimestampMinSeconds;
+const int64_t TimeUtil::kTimestampMaxSeconds;
+const int64_t TimeUtil::kDurationMaxSeconds;
+const int64_t TimeUtil::kDurationMinSeconds;
+#endif  // !_MSC_VER
+
+std::string TimeUtil::ToString(const Timestamp& timestamp) {
+  return FormatTime(timestamp.seconds(), timestamp.nanos());
+}
+
+bool TimeUtil::FromString(const std::string& value, Timestamp* timestamp) {
+  int64_t seconds;
+  int32_t nanos;
+  if (!ParseTime(value, &seconds, &nanos)) {
+    return false;
+  }
+  *timestamp = CreateNormalized<Timestamp>(seconds, nanos);
+  return true;
+}
+
+Timestamp TimeUtil::GetCurrentTime() {
+  int64_t seconds;
+  int32_t nanos;
+  CurrentTime(&seconds, &nanos);
+  return CreateNormalized<Timestamp>(seconds, nanos);
+}
+
+Timestamp TimeUtil::GetEpoch() { return Timestamp(); }
+
+std::string TimeUtil::ToString(const Duration& duration) {
+  std::string result;
+  int64_t seconds = duration.seconds();
+  int32_t nanos = duration.nanos();
+  if (seconds < 0 || nanos < 0) {
+    result += "-";
+    seconds = -seconds;
+    nanos = -nanos;
+  }
+  result += StrCat(seconds);
+  if (nanos != 0) {
+    result += "." + FormatNanos(nanos);
+  }
+  result += "s";
+  return result;
+}
+
+static int64_t Pow(int64_t x, int y) {
+  int64_t result = 1;
+  for (int i = 0; i < y; ++i) {
+    result *= x;
+  }
+  return result;
+}
+
+bool TimeUtil::FromString(const std::string& value, Duration* duration) {
+  if (value.length() <= 1 || value[value.length() - 1] != 's') {
+    return false;
+  }
+  bool negative = (value[0] == '-');
+  size_t sign_length = (negative ? 1 : 0);
+  // Parse the duration value as two integers rather than a float value
+  // to avoid precision loss.
+  std::string seconds_part, nanos_part;
+  size_t pos = value.find_last_of('.');
+  if (pos == std::string::npos) {
+    seconds_part = value.substr(sign_length, value.length() - 1 - sign_length);
+    nanos_part = "0";
+  } else {
+    seconds_part = value.substr(sign_length, pos - sign_length);
+    nanos_part = value.substr(pos + 1, value.length() - pos - 2);
+  }
+  char* end;
+  int64_t seconds = strto64(seconds_part.c_str(), &end, 10);
+  if (end != seconds_part.c_str() + seconds_part.length()) {
+    return false;
+  }
+  int64_t nanos = strto64(nanos_part.c_str(), &end, 10);
+  if (end != nanos_part.c_str() + nanos_part.length()) {
+    return false;
+  }
+  nanos = nanos * Pow(10, static_cast<int>(9 - nanos_part.length()));
+  if (negative) {
+    // If a Duration is negative, both seconds and nanos should be negative.
+    seconds = -seconds;
+    nanos = -nanos;
+  }
+  duration->set_seconds(seconds);
+  duration->set_nanos(static_cast<int32_t>(nanos));
+  return true;
+}
+
+Duration TimeUtil::NanosecondsToDuration(int64_t nanos) {
+  return CreateNormalized<Duration>(nanos / kNanosPerSecond,
+                                    nanos % kNanosPerSecond);
+}
+
+Duration TimeUtil::MicrosecondsToDuration(int64_t micros) {
+  return CreateNormalized<Duration>(
+      micros / kMicrosPerSecond,
+      (micros % kMicrosPerSecond) * kNanosPerMicrosecond);
+}
+
+Duration TimeUtil::MillisecondsToDuration(int64_t millis) {
+  return CreateNormalized<Duration>(
+      millis / kMillisPerSecond,
+      (millis % kMillisPerSecond) * kNanosPerMillisecond);
+}
+
+Duration TimeUtil::SecondsToDuration(int64_t seconds) {
+  return CreateNormalized<Duration>(seconds, 0);
+}
+
+Duration TimeUtil::MinutesToDuration(int64_t minutes) {
+  return CreateNormalized<Duration>(minutes * kSecondsPerMinute, 0);
+}
+
+Duration TimeUtil::HoursToDuration(int64_t hours) {
+  return CreateNormalized<Duration>(hours * kSecondsPerHour, 0);
+}
+
+int64_t TimeUtil::DurationToNanoseconds(const Duration& duration) {
+  return duration.seconds() * kNanosPerSecond + duration.nanos();
+}
+
+int64_t TimeUtil::DurationToMicroseconds(const Duration& duration) {
+  return duration.seconds() * kMicrosPerSecond +
+         RoundTowardZero(duration.nanos(), kNanosPerMicrosecond);
+}
+
+int64_t TimeUtil::DurationToMilliseconds(const Duration& duration) {
+  return duration.seconds() * kMillisPerSecond +
+         RoundTowardZero(duration.nanos(), kNanosPerMillisecond);
+}
+
+int64_t TimeUtil::DurationToSeconds(const Duration& duration) {
+  return duration.seconds();
+}
+
+int64_t TimeUtil::DurationToMinutes(const Duration& duration) {
+  return RoundTowardZero(duration.seconds(), kSecondsPerMinute);
+}
+
+int64_t TimeUtil::DurationToHours(const Duration& duration) {
+  return RoundTowardZero(duration.seconds(), kSecondsPerHour);
+}
+
+Timestamp TimeUtil::NanosecondsToTimestamp(int64_t nanos) {
+  return CreateNormalized<Timestamp>(nanos / kNanosPerSecond,
+                                     nanos % kNanosPerSecond);
+}
+
+Timestamp TimeUtil::MicrosecondsToTimestamp(int64_t micros) {
+  return CreateNormalized<Timestamp>(
+      micros / kMicrosPerSecond,
+      micros % kMicrosPerSecond * kNanosPerMicrosecond);
+}
+
+Timestamp TimeUtil::MillisecondsToTimestamp(int64_t millis) {
+  return CreateNormalized<Timestamp>(
+      millis / kMillisPerSecond,
+      millis % kMillisPerSecond * kNanosPerMillisecond);
+}
+
+Timestamp TimeUtil::SecondsToTimestamp(int64_t seconds) {
+  return CreateNormalized<Timestamp>(seconds, 0);
+}
+
+int64_t TimeUtil::TimestampToNanoseconds(const Timestamp& timestamp) {
+  return timestamp.seconds() * kNanosPerSecond + timestamp.nanos();
+}
+
+int64_t TimeUtil::TimestampToMicroseconds(const Timestamp& timestamp) {
+  return timestamp.seconds() * kMicrosPerSecond +
+         RoundTowardZero(timestamp.nanos(), kNanosPerMicrosecond);
+}
+
+int64_t TimeUtil::TimestampToMilliseconds(const Timestamp& timestamp) {
+  return timestamp.seconds() * kMillisPerSecond +
+         RoundTowardZero(timestamp.nanos(), kNanosPerMillisecond);
+}
+
+int64_t TimeUtil::TimestampToSeconds(const Timestamp& timestamp) {
+  return timestamp.seconds();
+}
+
+Timestamp TimeUtil::TimeTToTimestamp(time_t value) {
+  return CreateNormalized<Timestamp>(static_cast<int64_t>(value), 0);
+}
+
+time_t TimeUtil::TimestampToTimeT(const Timestamp& value) {
+  return static_cast<time_t>(value.seconds());
+}
+
+Timestamp TimeUtil::TimevalToTimestamp(const timeval& value) {
+  return CreateNormalized<Timestamp>(value.tv_sec,
+                                     value.tv_usec * kNanosPerMicrosecond);
+}
+
+timeval TimeUtil::TimestampToTimeval(const Timestamp& value) {
+  timeval result;
+  result.tv_sec = value.seconds();
+  result.tv_usec = RoundTowardZero(value.nanos(), kNanosPerMicrosecond);
+  return result;
+}
+
+Duration TimeUtil::TimevalToDuration(const timeval& value) {
+  return CreateNormalized<Duration>(value.tv_sec,
+                                    value.tv_usec * kNanosPerMicrosecond);
+}
+
+timeval TimeUtil::DurationToTimeval(const Duration& value) {
+  timeval result;
+  result.tv_sec = value.seconds();
+  result.tv_usec = RoundTowardZero(value.nanos(), kNanosPerMicrosecond);
+  // timeval.tv_usec's range is [0, 1000000)
+  if (result.tv_usec < 0) {
+    result.tv_sec -= 1;
+    result.tv_usec += kMicrosPerSecond;
+  }
+  return result;
+}
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
+
+namespace google {
+namespace protobuf {
+namespace {
+using ::PROTOBUF_NAMESPACE_ID::util::CreateNormalized;
+using ::PROTOBUF_NAMESPACE_ID::util::kNanosPerSecond;
+
+// Convert a Duration to uint128.
+void ToUint128(const Duration& value, uint128* result, bool* negative) {
+  if (value.seconds() < 0 || value.nanos() < 0) {
+    *negative = true;
+    *result = static_cast<uint64_t>(-value.seconds());
+    *result = *result * kNanosPerSecond + static_cast<uint32_t>(-value.nanos());
+  } else {
+    *negative = false;
+    *result = static_cast<uint64_t>(value.seconds());
+    *result = *result * kNanosPerSecond + static_cast<uint32_t>(value.nanos());
+  }
+}
+
+void ToDuration(const uint128& value, bool negative, Duration* duration) {
+  int64_t seconds =
+      static_cast<int64_t>(Uint128Low64(value / kNanosPerSecond));
+  int32_t nanos =
+      static_cast<int32_t>(Uint128Low64(value % kNanosPerSecond));
+  if (negative) {
+    seconds = -seconds;
+    nanos = -nanos;
+  }
+  duration->set_seconds(seconds);
+  duration->set_nanos(nanos);
+}
+}  // namespace
+
+Duration& operator+=(Duration& d1, const Duration& d2) {
+  d1 = CreateNormalized<Duration>(d1.seconds() + d2.seconds(),
+                                  d1.nanos() + d2.nanos());
+  return d1;
+}
+
+Duration& operator-=(Duration& d1, const Duration& d2) {  // NOLINT
+  d1 = CreateNormalized<Duration>(d1.seconds() - d2.seconds(),
+                                  d1.nanos() - d2.nanos());
+  return d1;
+}
+
+Duration& operator*=(Duration& d, int64_t r) {  // NOLINT
+  bool negative;
+  uint128 value;
+  ToUint128(d, &value, &negative);
+  if (r > 0) {
+    value *= static_cast<uint64_t>(r);
+  } else {
+    negative = !negative;
+    value *= static_cast<uint64_t>(-r);
+  }
+  ToDuration(value, negative, &d);
+  return d;
+}
+
+Duration& operator*=(Duration& d, double r) {  // NOLINT
+  double result =
+      (static_cast<double>(d.seconds()) + d.nanos() * (1.0 / kNanosPerSecond)) *
+      r;
+  int64_t seconds = static_cast<int64_t>(result);
+  int32_t nanos = static_cast<int32_t>((result - static_cast<double>(seconds)) *
+                                       kNanosPerSecond);
+  // Note that we normalize here not just because nanos can have a different
+  // sign from seconds but also that nanos can be any arbitrary value when
+  // overflow happens (i.e., the result is a much larger value than what
+  // int64 can represent).
+  d = CreateNormalized<Duration>(seconds, nanos);
+  return d;
+}
+
+Duration& operator/=(Duration& d, int64_t r) {  // NOLINT
+  bool negative;
+  uint128 value;
+  ToUint128(d, &value, &negative);
+  if (r > 0) {
+    value /= static_cast<uint64_t>(r);
+  } else {
+    negative = !negative;
+    value /= static_cast<uint64_t>(-r);
+  }
+  ToDuration(value, negative, &d);
+  return d;
+}
+
+Duration& operator/=(Duration& d, double r) {  // NOLINT
+  return d *= 1.0 / r;
+}
+
+Duration& operator%=(Duration& d1, const Duration& d2) {  // NOLINT
+  bool negative1, negative2;
+  uint128 value1, value2;
+  ToUint128(d1, &value1, &negative1);
+  ToUint128(d2, &value2, &negative2);
+  uint128 result = value1 % value2;
+  // When negative values are involved in division, we round the division
+  // result towards zero. With this semantics, sign of the remainder is the
+  // same as the dividend. For example:
+  //     -5 / 10    = 0, -5 % 10    = -5
+  //     -5 / (-10) = 0, -5 % (-10) = -5
+  //      5 / (-10) = 0,  5 % (-10) = 5
+  ToDuration(result, negative1, &d1);
+  return d1;
+}
+
+int64_t operator/(const Duration& d1, const Duration& d2) {
+  bool negative1, negative2;
+  uint128 value1, value2;
+  ToUint128(d1, &value1, &negative1);
+  ToUint128(d2, &value2, &negative2);
+  int64_t result = Uint128Low64(value1 / value2);
+  if (negative1 != negative2) {
+    result = -result;
+  }
+  return result;
+}
+
+Timestamp& operator+=(Timestamp& t, const Duration& d) {  // NOLINT
+  t = CreateNormalized<Timestamp>(t.seconds() + d.seconds(),
+                                  t.nanos() + d.nanos());
+  return t;
+}
+
+Timestamp& operator-=(Timestamp& t, const Duration& d) {  // NOLINT
+  t = CreateNormalized<Timestamp>(t.seconds() - d.seconds(),
+                                  t.nanos() - d.nanos());
+  return t;
+}
+
+Duration operator-(const Timestamp& t1, const Timestamp& t2) {
+  return CreateNormalized<Duration>(t1.seconds() - t2.seconds(),
+                                    t1.nanos() - t2.nanos());
+}
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/type_resolver_util.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/type_resolver_util.cpp
new file mode 100644
index 0000000..8be0efb
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/util/type_resolver_util.cpp
@@ -0,0 +1,370 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+#include <google/protobuf/util/type_resolver_util.h>
+
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/wrappers.pb.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/status.h>
+
+// clang-format off
+#include <google/protobuf/port_def.inc>
+// clang-format on
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace {
+using google::protobuf::Any;
+using google::protobuf::BoolValue;
+using google::protobuf::BytesValue;
+using google::protobuf::DoubleValue;
+using google::protobuf::Enum;
+using google::protobuf::EnumValue;
+using google::protobuf::Field;
+using google::protobuf::FloatValue;
+using google::protobuf::Int32Value;
+using google::protobuf::Int64Value;
+using google::protobuf::Option;
+using google::protobuf::StringValue;
+using google::protobuf::Type;
+using google::protobuf::UInt32Value;
+using google::protobuf::UInt64Value;
+
+class DescriptorPoolTypeResolver : public TypeResolver {
+ public:
+  DescriptorPoolTypeResolver(const std::string& url_prefix,
+                             const DescriptorPool* pool)
+      : url_prefix_(url_prefix), pool_(pool) {}
+
+  util::Status ResolveMessageType(const std::string& type_url,
+                                  Type* type) override {
+    std::string type_name;
+    util::Status status = ParseTypeUrl(type_url, &type_name);
+    if (!status.ok()) {
+      return status;
+    }
+
+    const Descriptor* descriptor = pool_->FindMessageTypeByName(type_name);
+    if (descriptor == NULL) {
+      return util::NotFoundError("Invalid type URL, unknown type: " +
+                                 type_name);
+    }
+    ConvertDescriptor(descriptor, type);
+    return util::Status();
+  }
+
+  util::Status ResolveEnumType(const std::string& type_url,
+                               Enum* enum_type) override {
+    std::string type_name;
+    util::Status status = ParseTypeUrl(type_url, &type_name);
+    if (!status.ok()) {
+      return status;
+    }
+
+    const EnumDescriptor* descriptor = pool_->FindEnumTypeByName(type_name);
+    if (descriptor == NULL) {
+      return util::InvalidArgumentError("Invalid type URL, unknown type: " +
+                                        type_name);
+    }
+    ConvertEnumDescriptor(descriptor, enum_type);
+    return util::Status();
+  }
+
+ private:
+  void ConvertDescriptor(const Descriptor* descriptor, Type* type) {
+    type->Clear();
+    type->set_name(descriptor->full_name());
+    for (int i = 0; i < descriptor->field_count(); ++i) {
+      ConvertFieldDescriptor(descriptor->field(i), type->add_fields());
+    }
+    for (int i = 0; i < descriptor->oneof_decl_count(); ++i) {
+      type->add_oneofs(descriptor->oneof_decl(i)->name());
+    }
+    type->mutable_source_context()->set_file_name(descriptor->file()->name());
+    ConvertMessageOptions(descriptor->options(), type->mutable_options());
+  }
+
+  void ConvertMessageOptions(const MessageOptions& options,
+                             RepeatedPtrField<Option>* output) {
+    return ConvertOptionsInternal(options, output);
+  }
+
+  void ConvertFieldOptions(const FieldOptions& options,
+                           RepeatedPtrField<Option>* output) {
+    return ConvertOptionsInternal(options, output);
+  }
+
+  void ConvertEnumOptions(const EnumOptions& options,
+                          RepeatedPtrField<Option>* output) {
+    return ConvertOptionsInternal(options, output);
+  }
+
+  void ConvertEnumValueOptions(const EnumValueOptions& options,
+                               RepeatedPtrField<Option>* output) {
+    return ConvertOptionsInternal(options, output);
+  }
+
+  // Implementation details for Convert*Options.
+  void ConvertOptionsInternal(const Message& options,
+                              RepeatedPtrField<Option>* output) {
+    const Reflection* reflection = options.GetReflection();
+    std::vector<const FieldDescriptor*> fields;
+    reflection->ListFields(options, &fields);
+    for (const FieldDescriptor* field : fields) {
+      if (field->is_repeated()) {
+        const int size = reflection->FieldSize(options, field);
+        for (int i = 0; i < size; i++) {
+          ConvertOptionField(reflection, options, field, i, output->Add());
+        }
+      } else {
+        ConvertOptionField(reflection, options, field, -1, output->Add());
+      }
+    }
+  }
+
+  static void ConvertOptionField(const Reflection* reflection,
+                                 const Message& options,
+                                 const FieldDescriptor* field, int index,
+                                 Option* out) {
+    out->set_name(field->is_extension() ? field->full_name() : field->name());
+    Any* value = out->mutable_value();
+    switch (field->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        value->PackFrom(
+            field->is_repeated()
+                ? reflection->GetRepeatedMessage(options, field, index)
+                : reflection->GetMessage(options, field));
+        return;
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+        value->PackFrom(WrapValue<DoubleValue>(
+            field->is_repeated()
+                ? reflection->GetRepeatedDouble(options, field, index)
+                : reflection->GetDouble(options, field)));
+        return;
+      case FieldDescriptor::CPPTYPE_FLOAT:
+        value->PackFrom(WrapValue<FloatValue>(
+            field->is_repeated()
+                ? reflection->GetRepeatedFloat(options, field, index)
+                : reflection->GetFloat(options, field)));
+        return;
+      case FieldDescriptor::CPPTYPE_INT64:
+        value->PackFrom(WrapValue<Int64Value>(
+            field->is_repeated()
+                ? reflection->GetRepeatedInt64(options, field, index)
+                : reflection->GetInt64(options, field)));
+        return;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        value->PackFrom(WrapValue<UInt64Value>(
+            field->is_repeated()
+                ? reflection->GetRepeatedUInt64(options, field, index)
+                : reflection->GetUInt64(options, field)));
+        return;
+      case FieldDescriptor::CPPTYPE_INT32:
+        value->PackFrom(WrapValue<Int32Value>(
+            field->is_repeated()
+                ? reflection->GetRepeatedInt32(options, field, index)
+                : reflection->GetInt32(options, field)));
+        return;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        value->PackFrom(WrapValue<UInt32Value>(
+            field->is_repeated()
+                ? reflection->GetRepeatedUInt32(options, field, index)
+                : reflection->GetUInt32(options, field)));
+        return;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        value->PackFrom(WrapValue<BoolValue>(
+            field->is_repeated()
+                ? reflection->GetRepeatedBool(options, field, index)
+                : reflection->GetBool(options, field)));
+        return;
+      case FieldDescriptor::CPPTYPE_STRING: {
+        const std::string& val =
+            field->is_repeated()
+                ? reflection->GetRepeatedString(options, field, index)
+                : reflection->GetString(options, field);
+        if (field->type() == FieldDescriptor::TYPE_STRING) {
+          value->PackFrom(WrapValue<StringValue>(val));
+        } else {
+          value->PackFrom(WrapValue<BytesValue>(val));
+        }
+        return;
+      }
+      case FieldDescriptor::CPPTYPE_ENUM: {
+        const EnumValueDescriptor* val =
+            field->is_repeated()
+                ? reflection->GetRepeatedEnum(options, field, index)
+                : reflection->GetEnum(options, field);
+        value->PackFrom(WrapValue<Int32Value>(val->number()));
+        return;
+      }
+    }
+  }
+
+  template <typename WrapperT, typename T>
+  static WrapperT WrapValue(T value) {
+    WrapperT wrapper;
+    wrapper.set_value(value);
+    return wrapper;
+  }
+
+  void ConvertFieldDescriptor(const FieldDescriptor* descriptor, Field* field) {
+    field->set_kind(static_cast<Field::Kind>(descriptor->type()));
+    switch (descriptor->label()) {
+      case FieldDescriptor::LABEL_OPTIONAL:
+        field->set_cardinality(Field::CARDINALITY_OPTIONAL);
+        break;
+      case FieldDescriptor::LABEL_REPEATED:
+        field->set_cardinality(Field::CARDINALITY_REPEATED);
+        break;
+      case FieldDescriptor::LABEL_REQUIRED:
+        field->set_cardinality(Field::CARDINALITY_REQUIRED);
+        break;
+    }
+    field->set_number(descriptor->number());
+    field->set_name(descriptor->name());
+    field->set_json_name(descriptor->json_name());
+    if (descriptor->has_default_value()) {
+      field->set_default_value(DefaultValueAsString(descriptor));
+    }
+    if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE ||
+        descriptor->type() == FieldDescriptor::TYPE_GROUP) {
+      field->set_type_url(GetTypeUrl(descriptor->message_type()));
+    } else if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
+      field->set_type_url(GetTypeUrl(descriptor->enum_type()));
+    }
+    if (descriptor->containing_oneof() != NULL) {
+      field->set_oneof_index(descriptor->containing_oneof()->index() + 1);
+    }
+    if (descriptor->is_packed()) {
+      field->set_packed(true);
+    }
+
+    ConvertFieldOptions(descriptor->options(), field->mutable_options());
+  }
+
+  void ConvertEnumDescriptor(const EnumDescriptor* descriptor,
+                             Enum* enum_type) {
+    enum_type->Clear();
+    enum_type->set_name(descriptor->full_name());
+    enum_type->mutable_source_context()->set_file_name(
+        descriptor->file()->name());
+    for (int i = 0; i < descriptor->value_count(); ++i) {
+      const EnumValueDescriptor* value_descriptor = descriptor->value(i);
+      EnumValue* value = enum_type->mutable_enumvalue()->Add();
+      value->set_name(value_descriptor->name());
+      value->set_number(value_descriptor->number());
+
+      ConvertEnumValueOptions(value_descriptor->options(),
+                              value->mutable_options());
+    }
+
+    ConvertEnumOptions(descriptor->options(), enum_type->mutable_options());
+  }
+
+  std::string GetTypeUrl(const Descriptor* descriptor) {
+    return url_prefix_ + "/" + descriptor->full_name();
+  }
+
+  std::string GetTypeUrl(const EnumDescriptor* descriptor) {
+    return url_prefix_ + "/" + descriptor->full_name();
+  }
+
+  util::Status ParseTypeUrl(const std::string& type_url,
+                            std::string* type_name) {
+    if (type_url.substr(0, url_prefix_.size() + 1) != url_prefix_ + "/") {
+      return util::InvalidArgumentError(
+          StrCat("Invalid type URL, type URLs must be of the form '",
+                       url_prefix_, "/<typename>', got: ", type_url));
+    }
+    *type_name = type_url.substr(url_prefix_.size() + 1);
+    return util::Status();
+  }
+
+  std::string DefaultValueAsString(const FieldDescriptor* descriptor) {
+    switch (descriptor->cpp_type()) {
+      case FieldDescriptor::CPPTYPE_INT32:
+        return StrCat(descriptor->default_value_int32());
+        break;
+      case FieldDescriptor::CPPTYPE_INT64:
+        return StrCat(descriptor->default_value_int64());
+        break;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        return StrCat(descriptor->default_value_uint32());
+        break;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        return StrCat(descriptor->default_value_uint64());
+        break;
+      case FieldDescriptor::CPPTYPE_FLOAT:
+        return SimpleFtoa(descriptor->default_value_float());
+        break;
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+        return SimpleDtoa(descriptor->default_value_double());
+        break;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        return descriptor->default_value_bool() ? "true" : "false";
+        break;
+      case FieldDescriptor::CPPTYPE_STRING:
+        if (descriptor->type() == FieldDescriptor::TYPE_BYTES) {
+          return CEscape(descriptor->default_value_string());
+        } else {
+          return descriptor->default_value_string();
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_ENUM:
+        return descriptor->default_value_enum()->name();
+        break;
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        GOOGLE_LOG(DFATAL) << "Messages can't have default values!";
+        break;
+    }
+    return "";
+  }
+
+  std::string url_prefix_;
+  const DescriptorPool* pool_;
+};
+
+}  // namespace
+
+TypeResolver* NewTypeResolverForDescriptorPool(const std::string& url_prefix,
+                                               const DescriptorPool* pool) {
+  return new DescriptorPoolTypeResolver(url_prefix, pool);
+}
+
+}  // namespace util
+}  // namespace protobuf
+}  // namespace google
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/wire_format.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/wire_format.cpp
new file mode 100644
index 0000000..6fe63c8
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/wire_format.cpp
@@ -0,0 +1,1768 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/wire_format.h>
+
+#include <stack>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/map_field_inl.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/parse_context.h>
+#include <google/protobuf/unknown_field_set.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+const size_t kMapEntryTagByteSize = 2;
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Forward declare static functions
+static size_t MapValueRefDataOnlyByteSize(const FieldDescriptor* field,
+                                          const MapValueConstRef& value);
+
+// ===================================================================
+
+bool UnknownFieldSetFieldSkipper::SkipField(io::CodedInputStream* input,
+                                            uint32_t tag) {
+  return WireFormat::SkipField(input, tag, unknown_fields_);
+}
+
+bool UnknownFieldSetFieldSkipper::SkipMessage(io::CodedInputStream* input) {
+  return WireFormat::SkipMessage(input, unknown_fields_);
+}
+
+void UnknownFieldSetFieldSkipper::SkipUnknownEnum(int field_number, int value) {
+  unknown_fields_->AddVarint(field_number, value);
+}
+
+bool WireFormat::SkipField(io::CodedInputStream* input, uint32_t tag,
+                           UnknownFieldSet* unknown_fields) {
+  int number = WireFormatLite::GetTagFieldNumber(tag);
+  // Field number 0 is illegal.
+  if (number == 0) return false;
+
+  switch (WireFormatLite::GetTagWireType(tag)) {
+    case WireFormatLite::WIRETYPE_VARINT: {
+      uint64_t value;
+      if (!input->ReadVarint64(&value)) return false;
+      if (unknown_fields != nullptr) unknown_fields->AddVarint(number, value);
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_FIXED64: {
+      uint64_t value;
+      if (!input->ReadLittleEndian64(&value)) return false;
+      if (unknown_fields != nullptr) unknown_fields->AddFixed64(number, value);
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_LENGTH_DELIMITED: {
+      uint32_t length;
+      if (!input->ReadVarint32(&length)) return false;
+      if (unknown_fields == nullptr) {
+        if (!input->Skip(length)) return false;
+      } else {
+        if (!input->ReadString(unknown_fields->AddLengthDelimited(number),
+                               length)) {
+          return false;
+        }
+      }
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_START_GROUP: {
+      if (!input->IncrementRecursionDepth()) return false;
+      if (!SkipMessage(input, (unknown_fields == nullptr)
+                                  ? nullptr
+                                  : unknown_fields->AddGroup(number))) {
+        return false;
+      }
+      input->DecrementRecursionDepth();
+      // Check that the ending tag matched the starting tag.
+      if (!input->LastTagWas(
+              WireFormatLite::MakeTag(WireFormatLite::GetTagFieldNumber(tag),
+                                      WireFormatLite::WIRETYPE_END_GROUP))) {
+        return false;
+      }
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_END_GROUP: {
+      return false;
+    }
+    case WireFormatLite::WIRETYPE_FIXED32: {
+      uint32_t value;
+      if (!input->ReadLittleEndian32(&value)) return false;
+      if (unknown_fields != nullptr) unknown_fields->AddFixed32(number, value);
+      return true;
+    }
+    default: {
+      return false;
+    }
+  }
+}
+
+bool WireFormat::SkipMessage(io::CodedInputStream* input,
+                             UnknownFieldSet* unknown_fields) {
+  while (true) {
+    uint32_t tag = input->ReadTag();
+    if (tag == 0) {
+      // End of input.  This is a valid place to end, so return true.
+      return true;
+    }
+
+    WireFormatLite::WireType wire_type = WireFormatLite::GetTagWireType(tag);
+
+    if (wire_type == WireFormatLite::WIRETYPE_END_GROUP) {
+      // Must be the end of the message.
+      return true;
+    }
+
+    if (!SkipField(input, tag, unknown_fields)) return false;
+  }
+}
+
+bool WireFormat::ReadPackedEnumPreserveUnknowns(io::CodedInputStream* input,
+                                                uint32_t field_number,
+                                                bool (*is_valid)(int),
+                                                UnknownFieldSet* unknown_fields,
+                                                RepeatedField<int>* values) {
+  uint32_t length;
+  if (!input->ReadVarint32(&length)) return false;
+  io::CodedInputStream::Limit limit = input->PushLimit(length);
+  while (input->BytesUntilLimit() > 0) {
+    int value;
+    if (!WireFormatLite::ReadPrimitive<int, WireFormatLite::TYPE_ENUM>(
+            input, &value)) {
+      return false;
+    }
+    if (is_valid == nullptr || is_valid(value)) {
+      values->Add(value);
+    } else {
+      unknown_fields->AddVarint(field_number, value);
+    }
+  }
+  input->PopLimit(limit);
+  return true;
+}
+
+uint8_t* WireFormat::InternalSerializeUnknownFieldsToArray(
+    const UnknownFieldSet& unknown_fields, uint8_t* target,
+    io::EpsCopyOutputStream* stream) {
+  for (int i = 0; i < unknown_fields.field_count(); i++) {
+    const UnknownField& field = unknown_fields.field(i);
+
+    target = stream->EnsureSpace(target);
+    switch (field.type()) {
+      case UnknownField::TYPE_VARINT:
+        target = WireFormatLite::WriteUInt64ToArray(field.number(),
+                                                    field.varint(), target);
+        break;
+      case UnknownField::TYPE_FIXED32:
+        target = WireFormatLite::WriteFixed32ToArray(field.number(),
+                                                     field.fixed32(), target);
+        break;
+      case UnknownField::TYPE_FIXED64:
+        target = WireFormatLite::WriteFixed64ToArray(field.number(),
+                                                     field.fixed64(), target);
+        break;
+      case UnknownField::TYPE_LENGTH_DELIMITED:
+        target = stream->WriteString(field.number(), field.length_delimited(),
+                                     target);
+        break;
+      case UnknownField::TYPE_GROUP:
+        target = WireFormatLite::WriteTagToArray(
+            field.number(), WireFormatLite::WIRETYPE_START_GROUP, target);
+        target = InternalSerializeUnknownFieldsToArray(field.group(), target,
+                                                       stream);
+        target = stream->EnsureSpace(target);
+        target = WireFormatLite::WriteTagToArray(
+            field.number(), WireFormatLite::WIRETYPE_END_GROUP, target);
+        break;
+    }
+  }
+  return target;
+}
+
+uint8_t* WireFormat::InternalSerializeUnknownMessageSetItemsToArray(
+    const UnknownFieldSet& unknown_fields, uint8_t* target,
+    io::EpsCopyOutputStream* stream) {
+  for (int i = 0; i < unknown_fields.field_count(); i++) {
+    const UnknownField& field = unknown_fields.field(i);
+
+    // The only unknown fields that are allowed to exist in a MessageSet are
+    // messages, which are length-delimited.
+    if (field.type() == UnknownField::TYPE_LENGTH_DELIMITED) {
+      target = stream->EnsureSpace(target);
+      // Start group.
+      target = io::CodedOutputStream::WriteTagToArray(
+          WireFormatLite::kMessageSetItemStartTag, target);
+
+      // Write type ID.
+      target = io::CodedOutputStream::WriteTagToArray(
+          WireFormatLite::kMessageSetTypeIdTag, target);
+      target =
+          io::CodedOutputStream::WriteVarint32ToArray(field.number(), target);
+
+      // Write message.
+      target = io::CodedOutputStream::WriteTagToArray(
+          WireFormatLite::kMessageSetMessageTag, target);
+
+      target = field.InternalSerializeLengthDelimitedNoTag(target, stream);
+
+      target = stream->EnsureSpace(target);
+      // End group.
+      target = io::CodedOutputStream::WriteTagToArray(
+          WireFormatLite::kMessageSetItemEndTag, target);
+    }
+  }
+
+  return target;
+}
+
+size_t WireFormat::ComputeUnknownFieldsSize(
+    const UnknownFieldSet& unknown_fields) {
+  size_t size = 0;
+  for (int i = 0; i < unknown_fields.field_count(); i++) {
+    const UnknownField& field = unknown_fields.field(i);
+
+    switch (field.type()) {
+      case UnknownField::TYPE_VARINT:
+        size += io::CodedOutputStream::VarintSize32(WireFormatLite::MakeTag(
+            field.number(), WireFormatLite::WIRETYPE_VARINT));
+        size += io::CodedOutputStream::VarintSize64(field.varint());
+        break;
+      case UnknownField::TYPE_FIXED32:
+        size += io::CodedOutputStream::VarintSize32(WireFormatLite::MakeTag(
+            field.number(), WireFormatLite::WIRETYPE_FIXED32));
+        size += sizeof(int32_t);
+        break;
+      case UnknownField::TYPE_FIXED64:
+        size += io::CodedOutputStream::VarintSize32(WireFormatLite::MakeTag(
+            field.number(), WireFormatLite::WIRETYPE_FIXED64));
+        size += sizeof(int64_t);
+        break;
+      case UnknownField::TYPE_LENGTH_DELIMITED:
+        size += io::CodedOutputStream::VarintSize32(WireFormatLite::MakeTag(
+            field.number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED));
+        size += io::CodedOutputStream::VarintSize32(
+            field.length_delimited().size());
+        size += field.length_delimited().size();
+        break;
+      case UnknownField::TYPE_GROUP:
+        size += io::CodedOutputStream::VarintSize32(WireFormatLite::MakeTag(
+            field.number(), WireFormatLite::WIRETYPE_START_GROUP));
+        size += ComputeUnknownFieldsSize(field.group());
+        size += io::CodedOutputStream::VarintSize32(WireFormatLite::MakeTag(
+            field.number(), WireFormatLite::WIRETYPE_END_GROUP));
+        break;
+    }
+  }
+
+  return size;
+}
+
+size_t WireFormat::ComputeUnknownMessageSetItemsSize(
+    const UnknownFieldSet& unknown_fields) {
+  size_t size = 0;
+  for (int i = 0; i < unknown_fields.field_count(); i++) {
+    const UnknownField& field = unknown_fields.field(i);
+
+    // The only unknown fields that are allowed to exist in a MessageSet are
+    // messages, which are length-delimited.
+    if (field.type() == UnknownField::TYPE_LENGTH_DELIMITED) {
+      size += WireFormatLite::kMessageSetItemTagsSize;
+      size += io::CodedOutputStream::VarintSize32(field.number());
+
+      int field_size = field.GetLengthDelimitedSize();
+      size += io::CodedOutputStream::VarintSize32(field_size);
+      size += field_size;
+    }
+  }
+
+  return size;
+}
+
+// ===================================================================
+
+bool WireFormat::ParseAndMergePartial(io::CodedInputStream* input,
+                                      Message* message) {
+  const Descriptor* descriptor = message->GetDescriptor();
+  const Reflection* message_reflection = message->GetReflection();
+
+  while (true) {
+    uint32_t tag = input->ReadTag();
+    if (tag == 0) {
+      // End of input.  This is a valid place to end, so return true.
+      return true;
+    }
+
+    if (WireFormatLite::GetTagWireType(tag) ==
+        WireFormatLite::WIRETYPE_END_GROUP) {
+      // Must be the end of the message.
+      return true;
+    }
+
+    const FieldDescriptor* field = nullptr;
+
+    if (descriptor != nullptr) {
+      int field_number = WireFormatLite::GetTagFieldNumber(tag);
+      field = descriptor->FindFieldByNumber(field_number);
+
+      // If that failed, check if the field is an extension.
+      if (field == nullptr && descriptor->IsExtensionNumber(field_number)) {
+        if (input->GetExtensionPool() == nullptr) {
+          field = message_reflection->FindKnownExtensionByNumber(field_number);
+        } else {
+          field = input->GetExtensionPool()->FindExtensionByNumber(
+              descriptor, field_number);
+        }
+      }
+
+      // If that failed, but we're a MessageSet, and this is the tag for a
+      // MessageSet item, then parse that.
+      if (field == nullptr && descriptor->options().message_set_wire_format() &&
+          tag == WireFormatLite::kMessageSetItemStartTag) {
+        if (!ParseAndMergeMessageSetItem(input, message)) {
+          return false;
+        }
+        continue;  // Skip ParseAndMergeField(); already taken care of.
+      }
+    }
+
+    if (!ParseAndMergeField(tag, field, message, input)) {
+      return false;
+    }
+  }
+}
+
+bool WireFormat::SkipMessageSetField(io::CodedInputStream* input,
+                                     uint32_t field_number,
+                                     UnknownFieldSet* unknown_fields) {
+  uint32_t length;
+  if (!input->ReadVarint32(&length)) return false;
+  return input->ReadString(unknown_fields->AddLengthDelimited(field_number),
+                           length);
+}
+
+bool WireFormat::ParseAndMergeMessageSetField(uint32_t field_number,
+                                              const FieldDescriptor* field,
+                                              Message* message,
+                                              io::CodedInputStream* input) {
+  const Reflection* message_reflection = message->GetReflection();
+  if (field == nullptr) {
+    // We store unknown MessageSet extensions as groups.
+    return SkipMessageSetField(
+        input, field_number, message_reflection->MutableUnknownFields(message));
+  } else if (field->is_repeated() ||
+             field->type() != FieldDescriptor::TYPE_MESSAGE) {
+    // This shouldn't happen as we only allow optional message extensions to
+    // MessageSet.
+    GOOGLE_LOG(ERROR) << "Extensions of MessageSets must be optional messages.";
+    return false;
+  } else {
+    Message* sub_message = message_reflection->MutableMessage(
+        message, field, input->GetExtensionFactory());
+    return WireFormatLite::ReadMessage(input, sub_message);
+  }
+}
+
+static bool StrictUtf8Check(const FieldDescriptor* field) {
+  return field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3;
+}
+
+bool WireFormat::ParseAndMergeField(
+    uint32_t tag,
+    const FieldDescriptor* field,  // May be nullptr for unknown
+    Message* message, io::CodedInputStream* input) {
+  const Reflection* message_reflection = message->GetReflection();
+
+  enum { UNKNOWN, NORMAL_FORMAT, PACKED_FORMAT } value_format;
+
+  if (field == nullptr) {
+    value_format = UNKNOWN;
+  } else if (WireFormatLite::GetTagWireType(tag) ==
+             WireTypeForFieldType(field->type())) {
+    value_format = NORMAL_FORMAT;
+  } else if (field->is_packable() &&
+             WireFormatLite::GetTagWireType(tag) ==
+                 WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+    value_format = PACKED_FORMAT;
+  } else {
+    // We don't recognize this field. Either the field number is unknown
+    // or the wire type doesn't match. Put it in our unknown field set.
+    value_format = UNKNOWN;
+  }
+
+  if (value_format == UNKNOWN) {
+    return SkipField(input, tag,
+                     message_reflection->MutableUnknownFields(message));
+  } else if (value_format == PACKED_FORMAT) {
+    uint32_t length;
+    if (!input->ReadVarint32(&length)) return false;
+    io::CodedInputStream::Limit limit = input->PushLimit(length);
+
+    switch (field->type()) {
+#define HANDLE_PACKED_TYPE(TYPE, CPPTYPE, CPPTYPE_METHOD)                      \
+  case FieldDescriptor::TYPE_##TYPE: {                                         \
+    while (input->BytesUntilLimit() > 0) {                                     \
+      CPPTYPE value;                                                           \
+      if (!WireFormatLite::ReadPrimitive<CPPTYPE,                              \
+                                         WireFormatLite::TYPE_##TYPE>(input,   \
+                                                                      &value)) \
+        return false;                                                          \
+      message_reflection->Add##CPPTYPE_METHOD(message, field, value);          \
+    }                                                                          \
+    break;                                                                     \
+  }
+
+      HANDLE_PACKED_TYPE(INT32, int32_t, Int32)
+      HANDLE_PACKED_TYPE(INT64, int64_t, Int64)
+      HANDLE_PACKED_TYPE(SINT32, int32_t, Int32)
+      HANDLE_PACKED_TYPE(SINT64, int64_t, Int64)
+      HANDLE_PACKED_TYPE(UINT32, uint32_t, UInt32)
+      HANDLE_PACKED_TYPE(UINT64, uint64_t, UInt64)
+
+      HANDLE_PACKED_TYPE(FIXED32, uint32_t, UInt32)
+      HANDLE_PACKED_TYPE(FIXED64, uint64_t, UInt64)
+      HANDLE_PACKED_TYPE(SFIXED32, int32_t, Int32)
+      HANDLE_PACKED_TYPE(SFIXED64, int64_t, Int64)
+
+      HANDLE_PACKED_TYPE(FLOAT, float, Float)
+      HANDLE_PACKED_TYPE(DOUBLE, double, Double)
+
+      HANDLE_PACKED_TYPE(BOOL, bool, Bool)
+#undef HANDLE_PACKED_TYPE
+
+      case FieldDescriptor::TYPE_ENUM: {
+        while (input->BytesUntilLimit() > 0) {
+          int value;
+          if (!WireFormatLite::ReadPrimitive<int, WireFormatLite::TYPE_ENUM>(
+                  input, &value))
+            return false;
+          if (message->GetDescriptor()->file()->syntax() ==
+              FileDescriptor::SYNTAX_PROTO3) {
+            message_reflection->AddEnumValue(message, field, value);
+          } else {
+            const EnumValueDescriptor* enum_value =
+                field->enum_type()->FindValueByNumber(value);
+            if (enum_value != nullptr) {
+              message_reflection->AddEnum(message, field, enum_value);
+            } else {
+              // The enum value is not one of the known values.  Add it to the
+              // UnknownFieldSet.
+              int64_t sign_extended_value = static_cast<int64_t>(value);
+              message_reflection->MutableUnknownFields(message)->AddVarint(
+                  WireFormatLite::GetTagFieldNumber(tag), sign_extended_value);
+            }
+          }
+        }
+
+        break;
+      }
+
+      case FieldDescriptor::TYPE_STRING:
+      case FieldDescriptor::TYPE_GROUP:
+      case FieldDescriptor::TYPE_MESSAGE:
+      case FieldDescriptor::TYPE_BYTES:
+        // Can't have packed fields of these types: these should be caught by
+        // the protocol compiler.
+        return false;
+        break;
+    }
+
+    input->PopLimit(limit);
+  } else {
+    // Non-packed value (value_format == NORMAL_FORMAT)
+    switch (field->type()) {
+#define HANDLE_TYPE(TYPE, CPPTYPE, CPPTYPE_METHOD)                            \
+  case FieldDescriptor::TYPE_##TYPE: {                                        \
+    CPPTYPE value;                                                            \
+    if (!WireFormatLite::ReadPrimitive<CPPTYPE, WireFormatLite::TYPE_##TYPE>( \
+            input, &value))                                                   \
+      return false;                                                           \
+    if (field->is_repeated()) {                                               \
+      message_reflection->Add##CPPTYPE_METHOD(message, field, value);         \
+    } else {                                                                  \
+      message_reflection->Set##CPPTYPE_METHOD(message, field, value);         \
+    }                                                                         \
+    break;                                                                    \
+  }
+
+      HANDLE_TYPE(INT32, int32_t, Int32)
+      HANDLE_TYPE(INT64, int64_t, Int64)
+      HANDLE_TYPE(SINT32, int32_t, Int32)
+      HANDLE_TYPE(SINT64, int64_t, Int64)
+      HANDLE_TYPE(UINT32, uint32_t, UInt32)
+      HANDLE_TYPE(UINT64, uint64_t, UInt64)
+
+      HANDLE_TYPE(FIXED32, uint32_t, UInt32)
+      HANDLE_TYPE(FIXED64, uint64_t, UInt64)
+      HANDLE_TYPE(SFIXED32, int32_t, Int32)
+      HANDLE_TYPE(SFIXED64, int64_t, Int64)
+
+      HANDLE_TYPE(FLOAT, float, Float)
+      HANDLE_TYPE(DOUBLE, double, Double)
+
+      HANDLE_TYPE(BOOL, bool, Bool)
+#undef HANDLE_TYPE
+
+      case FieldDescriptor::TYPE_ENUM: {
+        int value;
+        if (!WireFormatLite::ReadPrimitive<int, WireFormatLite::TYPE_ENUM>(
+                input, &value))
+          return false;
+        if (field->is_repeated()) {
+          message_reflection->AddEnumValue(message, field, value);
+        } else {
+          message_reflection->SetEnumValue(message, field, value);
+        }
+        break;
+      }
+
+      // Handle strings separately so that we can optimize the ctype=CORD case.
+      case FieldDescriptor::TYPE_STRING: {
+        bool strict_utf8_check = StrictUtf8Check(field);
+        std::string value;
+        if (!WireFormatLite::ReadString(input, &value)) return false;
+        if (strict_utf8_check) {
+          if (!WireFormatLite::VerifyUtf8String(value.data(), value.length(),
+                                                WireFormatLite::PARSE,
+                                                field->full_name().c_str())) {
+            return false;
+          }
+        } else {
+          VerifyUTF8StringNamedField(value.data(), value.length(), PARSE,
+                                     field->full_name().c_str());
+        }
+        if (field->is_repeated()) {
+          message_reflection->AddString(message, field, value);
+        } else {
+          message_reflection->SetString(message, field, value);
+        }
+        break;
+      }
+
+      case FieldDescriptor::TYPE_BYTES: {
+        std::string value;
+        if (!WireFormatLite::ReadBytes(input, &value)) return false;
+        if (field->is_repeated()) {
+          message_reflection->AddString(message, field, value);
+        } else {
+          message_reflection->SetString(message, field, value);
+        }
+        break;
+      }
+
+      case FieldDescriptor::TYPE_GROUP: {
+        Message* sub_message;
+        if (field->is_repeated()) {
+          sub_message = message_reflection->AddMessage(
+              message, field, input->GetExtensionFactory());
+        } else {
+          sub_message = message_reflection->MutableMessage(
+              message, field, input->GetExtensionFactory());
+        }
+
+        if (!WireFormatLite::ReadGroup(WireFormatLite::GetTagFieldNumber(tag),
+                                       input, sub_message))
+          return false;
+        break;
+      }
+
+      case FieldDescriptor::TYPE_MESSAGE: {
+        Message* sub_message;
+        if (field->is_repeated()) {
+          sub_message = message_reflection->AddMessage(
+              message, field, input->GetExtensionFactory());
+        } else {
+          sub_message = message_reflection->MutableMessage(
+              message, field, input->GetExtensionFactory());
+        }
+
+        if (!WireFormatLite::ReadMessage(input, sub_message)) return false;
+        break;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool WireFormat::ParseAndMergeMessageSetItem(io::CodedInputStream* input,
+                                             Message* message) {
+  struct MSReflective {
+    bool ParseField(int type_id, io::CodedInputStream* input) {
+      const FieldDescriptor* field =
+          message_reflection->FindKnownExtensionByNumber(type_id);
+      return ParseAndMergeMessageSetField(type_id, field, message, input);
+    }
+
+    bool SkipField(uint32_t tag, io::CodedInputStream* input) {
+      return WireFormat::SkipField(input, tag, nullptr);
+    }
+
+    const Reflection* message_reflection;
+    Message* message;
+  };
+
+  return ParseMessageSetItemImpl(
+      input, MSReflective{message->GetReflection(), message});
+}
+
+struct WireFormat::MessageSetParser {
+  const char* _InternalParse(const char* ptr, internal::ParseContext* ctx) {
+    // Parse a MessageSetItem
+    auto metadata = reflection->MutableInternalMetadata(msg);
+    enum class State { kNoTag, kHasType, kHasPayload, kDone };
+    State state = State::kNoTag;
+
+    std::string payload;
+    uint32_t type_id = 0;
+    while (!ctx->Done(&ptr)) {
+      // We use 64 bit tags in order to allow typeid's that span the whole
+      // range of 32 bit numbers.
+      uint32_t tag = static_cast<uint8_t>(*ptr++);
+      if (tag == WireFormatLite::kMessageSetTypeIdTag) {
+        uint64_t tmp;
+        ptr = ParseBigVarint(ptr, &tmp);
+        GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+        if (state == State::kNoTag) {
+          type_id = tmp;
+          state = State::kHasType;
+        } else if (state == State::kHasPayload) {
+          type_id = tmp;
+          const FieldDescriptor* field;
+          if (ctx->data().pool == nullptr) {
+            field = reflection->FindKnownExtensionByNumber(type_id);
+          } else {
+            field =
+                ctx->data().pool->FindExtensionByNumber(descriptor, type_id);
+          }
+          if (field == nullptr || field->message_type() == nullptr) {
+            WriteLengthDelimited(
+                type_id, payload,
+                metadata->mutable_unknown_fields<UnknownFieldSet>());
+          } else {
+            Message* value =
+                field->is_repeated()
+                    ? reflection->AddMessage(msg, field, ctx->data().factory)
+                    : reflection->MutableMessage(msg, field,
+                                                 ctx->data().factory);
+            const char* p;
+            // We can't use regular parse from string as we have to track
+            // proper recursion depth and descriptor pools.
+            ParseContext tmp_ctx(ctx->depth(), false, &p, payload);
+            tmp_ctx.data().pool = ctx->data().pool;
+            tmp_ctx.data().factory = ctx->data().factory;
+            GOOGLE_PROTOBUF_PARSER_ASSERT(value->_InternalParse(p, &tmp_ctx) &&
+                                           tmp_ctx.EndedAtLimit());
+          }
+          state = State::kDone;
+        }
+        continue;
+      } else if (tag == WireFormatLite::kMessageSetMessageTag) {
+        if (state == State::kNoTag) {
+          int32_t size = ReadSize(&ptr);
+          GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+          ptr = ctx->ReadString(ptr, size, &payload);
+          GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+          state = State::kHasPayload;
+        } else if (state == State::kHasType) {
+          // We're now parsing the payload
+          const FieldDescriptor* field = nullptr;
+          if (descriptor->IsExtensionNumber(type_id)) {
+            if (ctx->data().pool == nullptr) {
+              field = reflection->FindKnownExtensionByNumber(type_id);
+            } else {
+              field =
+                  ctx->data().pool->FindExtensionByNumber(descriptor, type_id);
+            }
+          }
+          ptr = WireFormat::_InternalParseAndMergeField(
+              msg, ptr, ctx, static_cast<uint64_t>(type_id) * 8 + 2, reflection,
+              field);
+          state = State::kDone;
+        } else {
+          int32_t size = ReadSize(&ptr);
+          GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+          ptr = ctx->Skip(ptr, size);
+          GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+        }
+      } else {
+        // An unknown field in MessageSetItem.
+        ptr = ReadTag(ptr - 1, &tag);
+        if (tag == 0 || (tag & 7) == WireFormatLite::WIRETYPE_END_GROUP) {
+          ctx->SetLastTag(tag);
+          return ptr;
+        }
+        // Skip field.
+        ptr = internal::UnknownFieldParse(
+            tag, static_cast<std::string*>(nullptr), ptr, ctx);
+      }
+      GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+    }
+    return ptr;
+  }
+
+  const char* ParseMessageSet(const char* ptr, internal::ParseContext* ctx) {
+    while (!ctx->Done(&ptr)) {
+      uint32_t tag;
+      ptr = ReadTag(ptr, &tag);
+      if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+      if (tag == 0 || (tag & 7) == WireFormatLite::WIRETYPE_END_GROUP) {
+        ctx->SetLastTag(tag);
+        break;
+      }
+      if (tag == WireFormatLite::kMessageSetItemStartTag) {
+        // A message set item starts
+        ptr = ctx->ParseGroup(this, ptr, tag);
+      } else {
+        // Parse other fields as normal extensions.
+        int field_number = WireFormatLite::GetTagFieldNumber(tag);
+        const FieldDescriptor* field = nullptr;
+        if (descriptor->IsExtensionNumber(field_number)) {
+          if (ctx->data().pool == nullptr) {
+            field = reflection->FindKnownExtensionByNumber(field_number);
+          } else {
+            field = ctx->data().pool->FindExtensionByNumber(descriptor,
+                                                            field_number);
+          }
+        }
+        ptr = WireFormat::_InternalParseAndMergeField(msg, ptr, ctx, tag,
+                                                      reflection, field);
+      }
+      if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+    }
+    return ptr;
+  }
+
+  Message* msg;
+  const Descriptor* descriptor;
+  const Reflection* reflection;
+};
+
+const char* WireFormat::_InternalParse(Message* msg, const char* ptr,
+                                       internal::ParseContext* ctx) {
+  const Descriptor* descriptor = msg->GetDescriptor();
+  const Reflection* reflection = msg->GetReflection();
+  GOOGLE_DCHECK(descriptor);
+  GOOGLE_DCHECK(reflection);
+  if (descriptor->options().message_set_wire_format()) {
+    MessageSetParser message_set{msg, descriptor, reflection};
+    return message_set.ParseMessageSet(ptr, ctx);
+  }
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ReadTag(ptr, &tag);
+    if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+    if (tag == 0 || (tag & 7) == WireFormatLite::WIRETYPE_END_GROUP) {
+      ctx->SetLastTag(tag);
+      break;
+    }
+    const FieldDescriptor* field = nullptr;
+
+    int field_number = WireFormatLite::GetTagFieldNumber(tag);
+    field = descriptor->FindFieldByNumber(field_number);
+
+    // If that failed, check if the field is an extension.
+    if (field == nullptr && descriptor->IsExtensionNumber(field_number)) {
+      if (ctx->data().pool == nullptr) {
+        field = reflection->FindKnownExtensionByNumber(field_number);
+      } else {
+        field =
+            ctx->data().pool->FindExtensionByNumber(descriptor, field_number);
+      }
+    }
+
+    ptr = _InternalParseAndMergeField(msg, ptr, ctx, tag, reflection, field);
+    if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+  }
+  return ptr;
+}
+
+const char* WireFormat::_InternalParseAndMergeField(
+    Message* msg, const char* ptr, internal::ParseContext* ctx, uint64_t tag,
+    const Reflection* reflection, const FieldDescriptor* field) {
+  if (field == nullptr) {
+    // unknown field set parser takes 64bit tags, because message set type ids
+    // span the full 32 bit range making the tag span [0, 2^35) range.
+    return internal::UnknownFieldParse(
+        tag, reflection->MutableUnknownFields(msg), ptr, ctx);
+  }
+  if (WireFormatLite::GetTagWireType(tag) !=
+      WireTypeForFieldType(field->type())) {
+    if (field->is_packable() && WireFormatLite::GetTagWireType(tag) ==
+                                    WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+      switch (field->type()) {
+#define HANDLE_PACKED_TYPE(TYPE, CPPTYPE, CPPTYPE_METHOD)                   \
+  case FieldDescriptor::TYPE_##TYPE: {                                      \
+    ptr = internal::Packed##CPPTYPE_METHOD##Parser(                         \
+        reflection->MutableRepeatedFieldInternal<CPPTYPE>(msg, field), ptr, \
+        ctx);                                                               \
+    return ptr;                                                             \
+  }
+
+        HANDLE_PACKED_TYPE(INT32, int32_t, Int32)
+        HANDLE_PACKED_TYPE(INT64, int64_t, Int64)
+        HANDLE_PACKED_TYPE(SINT32, int32_t, SInt32)
+        HANDLE_PACKED_TYPE(SINT64, int64_t, SInt64)
+        HANDLE_PACKED_TYPE(UINT32, uint32_t, UInt32)
+        HANDLE_PACKED_TYPE(UINT64, uint64_t, UInt64)
+
+        HANDLE_PACKED_TYPE(FIXED32, uint32_t, Fixed32)
+        HANDLE_PACKED_TYPE(FIXED64, uint64_t, Fixed64)
+        HANDLE_PACKED_TYPE(SFIXED32, int32_t, SFixed32)
+        HANDLE_PACKED_TYPE(SFIXED64, int64_t, SFixed64)
+
+        HANDLE_PACKED_TYPE(FLOAT, float, Float)
+        HANDLE_PACKED_TYPE(DOUBLE, double, Double)
+
+        HANDLE_PACKED_TYPE(BOOL, bool, Bool)
+#undef HANDLE_PACKED_TYPE
+
+        case FieldDescriptor::TYPE_ENUM: {
+          auto rep_enum =
+              reflection->MutableRepeatedFieldInternal<int>(msg, field);
+          bool open_enum = false;
+          if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 ||
+              open_enum) {
+            ptr = internal::PackedEnumParser(rep_enum, ptr, ctx);
+          } else {
+            return ctx->ReadPackedVarint(
+                ptr, [rep_enum, field, reflection, msg](uint64_t val) {
+                  if (field->enum_type()->FindValueByNumber(val) != nullptr) {
+                    rep_enum->Add(val);
+                  } else {
+                    WriteVarint(field->number(), val,
+                                reflection->MutableUnknownFields(msg));
+                  }
+                });
+          }
+          return ptr;
+        }
+
+        case FieldDescriptor::TYPE_STRING:
+        case FieldDescriptor::TYPE_GROUP:
+        case FieldDescriptor::TYPE_MESSAGE:
+        case FieldDescriptor::TYPE_BYTES:
+          GOOGLE_LOG(FATAL) << "Can't reach";
+          return nullptr;
+      }
+    } else {
+      // mismatched wiretype;
+      return internal::UnknownFieldParse(
+          tag, reflection->MutableUnknownFields(msg), ptr, ctx);
+    }
+  }
+
+  // Non-packed value
+  bool utf8_check = false;
+  bool strict_utf8_check = false;
+  switch (field->type()) {
+#define HANDLE_TYPE(TYPE, CPPTYPE, CPPTYPE_METHOD)        \
+  case FieldDescriptor::TYPE_##TYPE: {                    \
+    CPPTYPE value;                                        \
+    ptr = VarintParse(ptr, &value);                       \
+    if (ptr == nullptr) return nullptr;                   \
+    if (field->is_repeated()) {                           \
+      reflection->Add##CPPTYPE_METHOD(msg, field, value); \
+    } else {                                              \
+      reflection->Set##CPPTYPE_METHOD(msg, field, value); \
+    }                                                     \
+    return ptr;                                           \
+  }
+
+    HANDLE_TYPE(BOOL, uint64_t, Bool)
+    HANDLE_TYPE(INT32, uint32_t, Int32)
+    HANDLE_TYPE(INT64, uint64_t, Int64)
+    HANDLE_TYPE(UINT32, uint32_t, UInt32)
+    HANDLE_TYPE(UINT64, uint64_t, UInt64)
+
+    case FieldDescriptor::TYPE_SINT32: {
+      int32_t value = ReadVarintZigZag32(&ptr);
+      if (ptr == nullptr) return nullptr;
+      if (field->is_repeated()) {
+        reflection->AddInt32(msg, field, value);
+      } else {
+        reflection->SetInt32(msg, field, value);
+      }
+      return ptr;
+    }
+    case FieldDescriptor::TYPE_SINT64: {
+      int64_t value = ReadVarintZigZag64(&ptr);
+      if (ptr == nullptr) return nullptr;
+      if (field->is_repeated()) {
+        reflection->AddInt64(msg, field, value);
+      } else {
+        reflection->SetInt64(msg, field, value);
+      }
+      return ptr;
+    }
+#undef HANDLE_TYPE
+#define HANDLE_TYPE(TYPE, CPPTYPE, CPPTYPE_METHOD)        \
+  case FieldDescriptor::TYPE_##TYPE: {                    \
+    CPPTYPE value;                                        \
+    value = UnalignedLoad<CPPTYPE>(ptr);                  \
+    ptr += sizeof(CPPTYPE);                               \
+    if (field->is_repeated()) {                           \
+      reflection->Add##CPPTYPE_METHOD(msg, field, value); \
+    } else {                                              \
+      reflection->Set##CPPTYPE_METHOD(msg, field, value); \
+    }                                                     \
+    return ptr;                                           \
+  }
+
+      HANDLE_TYPE(FIXED32, uint32_t, UInt32)
+      HANDLE_TYPE(FIXED64, uint64_t, UInt64)
+      HANDLE_TYPE(SFIXED32, int32_t, Int32)
+      HANDLE_TYPE(SFIXED64, int64_t, Int64)
+
+      HANDLE_TYPE(FLOAT, float, Float)
+      HANDLE_TYPE(DOUBLE, double, Double)
+
+#undef HANDLE_TYPE
+
+    case FieldDescriptor::TYPE_ENUM: {
+      uint32_t value;
+      ptr = VarintParse(ptr, &value);
+      if (ptr == nullptr) return nullptr;
+      if (field->is_repeated()) {
+        reflection->AddEnumValue(msg, field, value);
+      } else {
+        reflection->SetEnumValue(msg, field, value);
+      }
+      return ptr;
+    }
+
+    // Handle strings separately so that we can optimize the ctype=CORD case.
+    case FieldDescriptor::TYPE_STRING:
+      utf8_check = true;
+      strict_utf8_check = StrictUtf8Check(field);
+      PROTOBUF_FALLTHROUGH_INTENDED;
+    case FieldDescriptor::TYPE_BYTES: {
+      int size = ReadSize(&ptr);
+      if (ptr == nullptr) return nullptr;
+      std::string value;
+      ptr = ctx->ReadString(ptr, size, &value);
+      if (ptr == nullptr) return nullptr;
+      if (utf8_check) {
+        if (strict_utf8_check) {
+          if (!WireFormatLite::VerifyUtf8String(value.data(), value.length(),
+                                                WireFormatLite::PARSE,
+                                                field->full_name().c_str())) {
+            return nullptr;
+          }
+        } else {
+          VerifyUTF8StringNamedField(value.data(), value.length(), PARSE,
+                                     field->full_name().c_str());
+        }
+      }
+      if (field->is_repeated()) {
+        reflection->AddString(msg, field, std::move(value));
+      } else {
+        reflection->SetString(msg, field, std::move(value));
+      }
+      return ptr;
+    }
+
+    case FieldDescriptor::TYPE_GROUP: {
+      Message* sub_message;
+      if (field->is_repeated()) {
+        sub_message = reflection->AddMessage(msg, field, ctx->data().factory);
+      } else {
+        sub_message =
+            reflection->MutableMessage(msg, field, ctx->data().factory);
+      }
+
+      return ctx->ParseGroup(sub_message, ptr, tag);
+    }
+
+    case FieldDescriptor::TYPE_MESSAGE: {
+      Message* sub_message;
+      if (field->is_repeated()) {
+        sub_message = reflection->AddMessage(msg, field, ctx->data().factory);
+      } else {
+        sub_message =
+            reflection->MutableMessage(msg, field, ctx->data().factory);
+      }
+      return ctx->ParseMessage(sub_message, ptr);
+    }
+  }
+
+  // GCC 8 complains about control reaching end of non-void function here.
+  // Let's keep it happy by returning a nullptr.
+  return nullptr;
+}
+
+// ===================================================================
+
+uint8_t* WireFormat::_InternalSerialize(const Message& message, uint8_t* target,
+                                        io::EpsCopyOutputStream* stream) {
+  const Descriptor* descriptor = message.GetDescriptor();
+  const Reflection* message_reflection = message.GetReflection();
+
+  std::vector<const FieldDescriptor*> fields;
+
+  // Fields of map entry should always be serialized.
+  if (descriptor->options().map_entry()) {
+    for (int i = 0; i < descriptor->field_count(); i++) {
+      fields.push_back(descriptor->field(i));
+    }
+  } else {
+    message_reflection->ListFields(message, &fields);
+  }
+
+  for (auto field : fields) {
+    target = InternalSerializeField(field, message, target, stream);
+  }
+
+  if (descriptor->options().message_set_wire_format()) {
+    return InternalSerializeUnknownMessageSetItemsToArray(
+        message_reflection->GetUnknownFields(message), target, stream);
+  } else {
+    return InternalSerializeUnknownFieldsToArray(
+        message_reflection->GetUnknownFields(message), target, stream);
+  }
+}
+
+uint8_t* SerializeMapKeyWithCachedSizes(const FieldDescriptor* field,
+                                        const MapKey& value, uint8_t* target,
+                                        io::EpsCopyOutputStream* stream) {
+  target = stream->EnsureSpace(target);
+  switch (field->type()) {
+    case FieldDescriptor::TYPE_DOUBLE:
+    case FieldDescriptor::TYPE_FLOAT:
+    case FieldDescriptor::TYPE_GROUP:
+    case FieldDescriptor::TYPE_MESSAGE:
+    case FieldDescriptor::TYPE_BYTES:
+    case FieldDescriptor::TYPE_ENUM:
+      GOOGLE_LOG(FATAL) << "Unsupported";
+      break;
+#define CASE_TYPE(FieldType, CamelFieldType, CamelCppType)   \
+  case FieldDescriptor::TYPE_##FieldType:                    \
+    target = WireFormatLite::Write##CamelFieldType##ToArray( \
+        1, value.Get##CamelCppType##Value(), target);        \
+    break;
+      CASE_TYPE(INT64, Int64, Int64)
+      CASE_TYPE(UINT64, UInt64, UInt64)
+      CASE_TYPE(INT32, Int32, Int32)
+      CASE_TYPE(FIXED64, Fixed64, UInt64)
+      CASE_TYPE(FIXED32, Fixed32, UInt32)
+      CASE_TYPE(BOOL, Bool, Bool)
+      CASE_TYPE(UINT32, UInt32, UInt32)
+      CASE_TYPE(SFIXED32, SFixed32, Int32)
+      CASE_TYPE(SFIXED64, SFixed64, Int64)
+      CASE_TYPE(SINT32, SInt32, Int32)
+      CASE_TYPE(SINT64, SInt64, Int64)
+#undef CASE_TYPE
+    case FieldDescriptor::TYPE_STRING:
+      target = stream->WriteString(1, value.GetStringValue(), target);
+      break;
+  }
+  return target;
+}
+
+static uint8_t* SerializeMapValueRefWithCachedSizes(
+    const FieldDescriptor* field, const MapValueConstRef& value,
+    uint8_t* target, io::EpsCopyOutputStream* stream) {
+  target = stream->EnsureSpace(target);
+  switch (field->type()) {
+#define CASE_TYPE(FieldType, CamelFieldType, CamelCppType)   \
+  case FieldDescriptor::TYPE_##FieldType:                    \
+    target = WireFormatLite::Write##CamelFieldType##ToArray( \
+        2, value.Get##CamelCppType##Value(), target);        \
+    break;
+    CASE_TYPE(INT64, Int64, Int64)
+    CASE_TYPE(UINT64, UInt64, UInt64)
+    CASE_TYPE(INT32, Int32, Int32)
+    CASE_TYPE(FIXED64, Fixed64, UInt64)
+    CASE_TYPE(FIXED32, Fixed32, UInt32)
+    CASE_TYPE(BOOL, Bool, Bool)
+    CASE_TYPE(UINT32, UInt32, UInt32)
+    CASE_TYPE(SFIXED32, SFixed32, Int32)
+    CASE_TYPE(SFIXED64, SFixed64, Int64)
+    CASE_TYPE(SINT32, SInt32, Int32)
+    CASE_TYPE(SINT64, SInt64, Int64)
+    CASE_TYPE(ENUM, Enum, Enum)
+    CASE_TYPE(DOUBLE, Double, Double)
+    CASE_TYPE(FLOAT, Float, Float)
+#undef CASE_TYPE
+    case FieldDescriptor::TYPE_STRING:
+    case FieldDescriptor::TYPE_BYTES:
+      target = stream->WriteString(2, value.GetStringValue(), target);
+      break;
+    case FieldDescriptor::TYPE_MESSAGE: {
+      auto& msg = value.GetMessageValue();
+      target = WireFormatLite::InternalWriteMessage(2, msg, msg.GetCachedSize(),
+                                                    target, stream);
+    } break;
+    case FieldDescriptor::TYPE_GROUP:
+      target = WireFormatLite::InternalWriteGroup(2, value.GetMessageValue(),
+                                                  target, stream);
+      break;
+  }
+  return target;
+}
+
+class MapKeySorter {
+ public:
+  static std::vector<MapKey> SortKey(const Message& message,
+                                     const Reflection* reflection,
+                                     const FieldDescriptor* field) {
+    std::vector<MapKey> sorted_key_list;
+    for (MapIterator it =
+             reflection->MapBegin(const_cast<Message*>(&message), field);
+         it != reflection->MapEnd(const_cast<Message*>(&message), field);
+         ++it) {
+      sorted_key_list.push_back(it.GetKey());
+    }
+    MapKeyComparator comparator;
+    std::sort(sorted_key_list.begin(), sorted_key_list.end(), comparator);
+    return sorted_key_list;
+  }
+
+ private:
+  class MapKeyComparator {
+   public:
+    bool operator()(const MapKey& a, const MapKey& b) const {
+      GOOGLE_DCHECK(a.type() == b.type());
+      switch (a.type()) {
+#define CASE_TYPE(CppType, CamelCppType)                                \
+  case FieldDescriptor::CPPTYPE_##CppType: {                            \
+    return a.Get##CamelCppType##Value() < b.Get##CamelCppType##Value(); \
+  }
+        CASE_TYPE(STRING, String)
+        CASE_TYPE(INT64, Int64)
+        CASE_TYPE(INT32, Int32)
+        CASE_TYPE(UINT64, UInt64)
+        CASE_TYPE(UINT32, UInt32)
+        CASE_TYPE(BOOL, Bool)
+#undef CASE_TYPE
+
+        default:
+          GOOGLE_LOG(DFATAL) << "Invalid key for map field.";
+          return true;
+      }
+    }
+  };
+};
+
+static uint8_t* InternalSerializeMapEntry(const FieldDescriptor* field,
+                                          const MapKey& key,
+                                          const MapValueConstRef& value,
+                                          uint8_t* target,
+                                          io::EpsCopyOutputStream* stream) {
+  const FieldDescriptor* key_field = field->message_type()->field(0);
+  const FieldDescriptor* value_field = field->message_type()->field(1);
+
+  size_t size = kMapEntryTagByteSize;
+  size += MapKeyDataOnlyByteSize(key_field, key);
+  size += MapValueRefDataOnlyByteSize(value_field, value);
+  target = stream->EnsureSpace(target);
+  target = WireFormatLite::WriteTagToArray(
+      field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED, target);
+  target = io::CodedOutputStream::WriteVarint32ToArray(size, target);
+  target = SerializeMapKeyWithCachedSizes(key_field, key, target, stream);
+  target =
+      SerializeMapValueRefWithCachedSizes(value_field, value, target, stream);
+  return target;
+}
+
+uint8_t* WireFormat::InternalSerializeField(const FieldDescriptor* field,
+                                            const Message& message,
+                                            uint8_t* target,
+                                            io::EpsCopyOutputStream* stream) {
+  const Reflection* message_reflection = message.GetReflection();
+
+  if (field->is_extension() &&
+      field->containing_type()->options().message_set_wire_format() &&
+      field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+      !field->is_repeated()) {
+    return InternalSerializeMessageSetItem(field, message, target, stream);
+  }
+
+  // For map fields, we can use either repeated field reflection or map
+  // reflection.  Our choice has some subtle effects.  If we use repeated field
+  // reflection here, then the repeated field representation becomes
+  // authoritative for this field: any existing references that came from map
+  // reflection remain valid for reading, but mutations to them are lost and
+  // will be overwritten next time we call map reflection!
+  //
+  // So far this mainly affects Python, which keeps long-term references to map
+  // values around, and always uses map reflection.  See: b/35918691
+  //
+  // Here we choose to use map reflection API as long as the internal
+  // map is valid. In this way, the serialization doesn't change map field's
+  // internal state and existing references that came from map reflection remain
+  // valid for both reading and writing.
+  if (field->is_map()) {
+    const MapFieldBase* map_field =
+        message_reflection->GetMapData(message, field);
+    if (map_field->IsMapValid()) {
+      if (stream->IsSerializationDeterministic()) {
+        std::vector<MapKey> sorted_key_list =
+            MapKeySorter::SortKey(message, message_reflection, field);
+        for (std::vector<MapKey>::iterator it = sorted_key_list.begin();
+             it != sorted_key_list.end(); ++it) {
+          MapValueConstRef map_value;
+          message_reflection->LookupMapValue(message, field, *it, &map_value);
+          target =
+              InternalSerializeMapEntry(field, *it, map_value, target, stream);
+        }
+      } else {
+        for (MapIterator it = message_reflection->MapBegin(
+                 const_cast<Message*>(&message), field);
+             it !=
+             message_reflection->MapEnd(const_cast<Message*>(&message), field);
+             ++it) {
+          target = InternalSerializeMapEntry(field, it.GetKey(),
+                                             it.GetValueRef(), target, stream);
+        }
+      }
+
+      return target;
+    }
+  }
+  int count = 0;
+
+  if (field->is_repeated()) {
+    count = message_reflection->FieldSize(message, field);
+  } else if (field->containing_type()->options().map_entry()) {
+    // Map entry fields always need to be serialized.
+    count = 1;
+  } else if (message_reflection->HasField(message, field)) {
+    count = 1;
+  }
+
+  // map_entries is for maps that'll be deterministically serialized.
+  std::vector<const Message*> map_entries;
+  if (count > 1 && field->is_map() && stream->IsSerializationDeterministic()) {
+    map_entries =
+        DynamicMapSorter::Sort(message, count, message_reflection, field);
+  }
+
+  if (field->is_packed()) {
+    if (count == 0) return target;
+    target = stream->EnsureSpace(target);
+    switch (field->type()) {
+#define HANDLE_PRIMITIVE_TYPE(TYPE, CPPTYPE, TYPE_METHOD, CPPTYPE_METHOD)      \
+  case FieldDescriptor::TYPE_##TYPE: {                                         \
+    auto r =                                                                   \
+        message_reflection->GetRepeatedFieldInternal<CPPTYPE>(message, field); \
+    target = stream->Write##TYPE_METHOD##Packed(                               \
+        field->number(), r, FieldDataOnlyByteSize(field, message), target);    \
+    break;                                                                     \
+  }
+
+      HANDLE_PRIMITIVE_TYPE(INT32, int32_t, Int32, Int32)
+      HANDLE_PRIMITIVE_TYPE(INT64, int64_t, Int64, Int64)
+      HANDLE_PRIMITIVE_TYPE(SINT32, int32_t, SInt32, Int32)
+      HANDLE_PRIMITIVE_TYPE(SINT64, int64_t, SInt64, Int64)
+      HANDLE_PRIMITIVE_TYPE(UINT32, uint32_t, UInt32, UInt32)
+      HANDLE_PRIMITIVE_TYPE(UINT64, uint64_t, UInt64, UInt64)
+      HANDLE_PRIMITIVE_TYPE(ENUM, int, Enum, Enum)
+
+#undef HANDLE_PRIMITIVE_TYPE
+#define HANDLE_PRIMITIVE_TYPE(TYPE, CPPTYPE, TYPE_METHOD, CPPTYPE_METHOD)      \
+  case FieldDescriptor::TYPE_##TYPE: {                                         \
+    auto r =                                                                   \
+        message_reflection->GetRepeatedFieldInternal<CPPTYPE>(message, field); \
+    target = stream->WriteFixedPacked(field->number(), r, target);             \
+    break;                                                                     \
+  }
+
+      HANDLE_PRIMITIVE_TYPE(FIXED32, uint32_t, Fixed32, UInt32)
+      HANDLE_PRIMITIVE_TYPE(FIXED64, uint64_t, Fixed64, UInt64)
+      HANDLE_PRIMITIVE_TYPE(SFIXED32, int32_t, SFixed32, Int32)
+      HANDLE_PRIMITIVE_TYPE(SFIXED64, int64_t, SFixed64, Int64)
+
+      HANDLE_PRIMITIVE_TYPE(FLOAT, float, Float, Float)
+      HANDLE_PRIMITIVE_TYPE(DOUBLE, double, Double, Double)
+
+      HANDLE_PRIMITIVE_TYPE(BOOL, bool, Bool, Bool)
+#undef HANDLE_PRIMITIVE_TYPE
+      default:
+        GOOGLE_LOG(FATAL) << "Invalid descriptor";
+    }
+    return target;
+  }
+
+  auto get_message_from_field = [&message, &map_entries, message_reflection](
+                                    const FieldDescriptor* field, int j) {
+    if (!field->is_repeated()) {
+      return &message_reflection->GetMessage(message, field);
+    }
+    if (!map_entries.empty()) {
+      return map_entries[j];
+    }
+    return &message_reflection->GetRepeatedMessage(message, field, j);
+  };
+  for (int j = 0; j < count; j++) {
+    target = stream->EnsureSpace(target);
+    switch (field->type()) {
+#define HANDLE_PRIMITIVE_TYPE(TYPE, CPPTYPE, TYPE_METHOD, CPPTYPE_METHOD)     \
+  case FieldDescriptor::TYPE_##TYPE: {                                        \
+    const CPPTYPE value =                                                     \
+        field->is_repeated()                                                  \
+            ? message_reflection->GetRepeated##CPPTYPE_METHOD(message, field, \
+                                                              j)              \
+            : message_reflection->Get##CPPTYPE_METHOD(message, field);        \
+    target = WireFormatLite::Write##TYPE_METHOD##ToArray(field->number(),     \
+                                                         value, target);      \
+    break;                                                                    \
+  }
+
+      HANDLE_PRIMITIVE_TYPE(INT32, int32_t, Int32, Int32)
+      HANDLE_PRIMITIVE_TYPE(INT64, int64_t, Int64, Int64)
+      HANDLE_PRIMITIVE_TYPE(SINT32, int32_t, SInt32, Int32)
+      HANDLE_PRIMITIVE_TYPE(SINT64, int64_t, SInt64, Int64)
+      HANDLE_PRIMITIVE_TYPE(UINT32, uint32_t, UInt32, UInt32)
+      HANDLE_PRIMITIVE_TYPE(UINT64, uint64_t, UInt64, UInt64)
+
+      HANDLE_PRIMITIVE_TYPE(FIXED32, uint32_t, Fixed32, UInt32)
+      HANDLE_PRIMITIVE_TYPE(FIXED64, uint64_t, Fixed64, UInt64)
+      HANDLE_PRIMITIVE_TYPE(SFIXED32, int32_t, SFixed32, Int32)
+      HANDLE_PRIMITIVE_TYPE(SFIXED64, int64_t, SFixed64, Int64)
+
+      HANDLE_PRIMITIVE_TYPE(FLOAT, float, Float, Float)
+      HANDLE_PRIMITIVE_TYPE(DOUBLE, double, Double, Double)
+
+      HANDLE_PRIMITIVE_TYPE(BOOL, bool, Bool, Bool)
+#undef HANDLE_PRIMITIVE_TYPE
+
+      case FieldDescriptor::TYPE_GROUP: {
+        auto* msg = get_message_from_field(field, j);
+        target = WireFormatLite::InternalWriteGroup(field->number(), *msg,
+                                                    target, stream);
+      } break;
+
+      case FieldDescriptor::TYPE_MESSAGE: {
+        auto* msg = get_message_from_field(field, j);
+        target = WireFormatLite::InternalWriteMessage(
+            field->number(), *msg, msg->GetCachedSize(), target, stream);
+      } break;
+
+      case FieldDescriptor::TYPE_ENUM: {
+        const EnumValueDescriptor* value =
+            field->is_repeated()
+                ? message_reflection->GetRepeatedEnum(message, field, j)
+                : message_reflection->GetEnum(message, field);
+        target = WireFormatLite::WriteEnumToArray(field->number(),
+                                                  value->number(), target);
+        break;
+      }
+
+      // Handle strings separately so that we can get string references
+      // instead of copying.
+      case FieldDescriptor::TYPE_STRING: {
+        bool strict_utf8_check = StrictUtf8Check(field);
+        std::string scratch;
+        const std::string& value =
+            field->is_repeated()
+                ? message_reflection->GetRepeatedStringReference(message, field,
+                                                                 j, &scratch)
+                : message_reflection->GetStringReference(message, field,
+                                                         &scratch);
+        if (strict_utf8_check) {
+          WireFormatLite::VerifyUtf8String(value.data(), value.length(),
+                                           WireFormatLite::SERIALIZE,
+                                           field->full_name().c_str());
+        } else {
+          VerifyUTF8StringNamedField(value.data(), value.length(), SERIALIZE,
+                                     field->full_name().c_str());
+        }
+        target = stream->WriteString(field->number(), value, target);
+        break;
+      }
+
+      case FieldDescriptor::TYPE_BYTES: {
+        std::string scratch;
+        const std::string& value =
+            field->is_repeated()
+                ? message_reflection->GetRepeatedStringReference(message, field,
+                                                                 j, &scratch)
+                : message_reflection->GetStringReference(message, field,
+                                                         &scratch);
+        target = stream->WriteString(field->number(), value, target);
+        break;
+      }
+    }
+  }
+  return target;
+}
+
+uint8_t* WireFormat::InternalSerializeMessageSetItem(
+    const FieldDescriptor* field, const Message& message, uint8_t* target,
+    io::EpsCopyOutputStream* stream) {
+  const Reflection* message_reflection = message.GetReflection();
+
+  target = stream->EnsureSpace(target);
+  // Start group.
+  target = io::CodedOutputStream::WriteTagToArray(
+      WireFormatLite::kMessageSetItemStartTag, target);
+  // Write type ID.
+  target = WireFormatLite::WriteUInt32ToArray(
+      WireFormatLite::kMessageSetTypeIdNumber, field->number(), target);
+  // Write message.
+  auto& msg = message_reflection->GetMessage(message, field);
+  target = WireFormatLite::InternalWriteMessage(
+      WireFormatLite::kMessageSetMessageNumber, msg, msg.GetCachedSize(),
+      target, stream);
+  // End group.
+  target = stream->EnsureSpace(target);
+  target = io::CodedOutputStream::WriteTagToArray(
+      WireFormatLite::kMessageSetItemEndTag, target);
+  return target;
+}
+
+// ===================================================================
+
+size_t WireFormat::ByteSize(const Message& message) {
+  const Descriptor* descriptor = message.GetDescriptor();
+  const Reflection* message_reflection = message.GetReflection();
+
+  size_t our_size = 0;
+
+  std::vector<const FieldDescriptor*> fields;
+
+  // Fields of map entry should always be serialized.
+  if (descriptor->options().map_entry()) {
+    for (int i = 0; i < descriptor->field_count(); i++) {
+      fields.push_back(descriptor->field(i));
+    }
+  } else {
+    message_reflection->ListFields(message, &fields);
+  }
+
+  for (const FieldDescriptor* field : fields) {
+    our_size += FieldByteSize(field, message);
+  }
+
+  if (descriptor->options().message_set_wire_format()) {
+    our_size += ComputeUnknownMessageSetItemsSize(
+        message_reflection->GetUnknownFields(message));
+  } else {
+    our_size +=
+        ComputeUnknownFieldsSize(message_reflection->GetUnknownFields(message));
+  }
+
+  return our_size;
+}
+
+size_t WireFormat::FieldByteSize(const FieldDescriptor* field,
+                                 const Message& message) {
+  const Reflection* message_reflection = message.GetReflection();
+
+  if (field->is_extension() &&
+      field->containing_type()->options().message_set_wire_format() &&
+      field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+      !field->is_repeated()) {
+    return MessageSetItemByteSize(field, message);
+  }
+
+  size_t count = 0;
+  if (field->is_repeated()) {
+    if (field->is_map()) {
+      const MapFieldBase* map_field =
+          message_reflection->GetMapData(message, field);
+      if (map_field->IsMapValid()) {
+        count = FromIntSize(map_field->size());
+      } else {
+        count = FromIntSize(message_reflection->FieldSize(message, field));
+      }
+    } else {
+      count = FromIntSize(message_reflection->FieldSize(message, field));
+    }
+  } else if (field->containing_type()->options().map_entry()) {
+    // Map entry fields always need to be serialized.
+    count = 1;
+  } else if (message_reflection->HasField(message, field)) {
+    count = 1;
+  }
+
+  const size_t data_size = FieldDataOnlyByteSize(field, message);
+  size_t our_size = data_size;
+  if (field->is_packed()) {
+    if (data_size > 0) {
+      // Packed fields get serialized like a string, not their native type.
+      // Technically this doesn't really matter; the size only changes if it's
+      // a GROUP
+      our_size += TagSize(field->number(), FieldDescriptor::TYPE_STRING);
+      our_size += io::CodedOutputStream::VarintSize32(data_size);
+    }
+  } else {
+    our_size += count * TagSize(field->number(), field->type());
+  }
+  return our_size;
+}
+
+size_t MapKeyDataOnlyByteSize(const FieldDescriptor* field,
+                              const MapKey& value) {
+  GOOGLE_DCHECK_EQ(FieldDescriptor::TypeToCppType(field->type()), value.type());
+  switch (field->type()) {
+    case FieldDescriptor::TYPE_DOUBLE:
+    case FieldDescriptor::TYPE_FLOAT:
+    case FieldDescriptor::TYPE_GROUP:
+    case FieldDescriptor::TYPE_MESSAGE:
+    case FieldDescriptor::TYPE_BYTES:
+    case FieldDescriptor::TYPE_ENUM:
+      GOOGLE_LOG(FATAL) << "Unsupported";
+      return 0;
+#define CASE_TYPE(FieldType, CamelFieldType, CamelCppType) \
+  case FieldDescriptor::TYPE_##FieldType:                  \
+    return WireFormatLite::CamelFieldType##Size(           \
+        value.Get##CamelCppType##Value());
+
+#define FIXED_CASE_TYPE(FieldType, CamelFieldType) \
+  case FieldDescriptor::TYPE_##FieldType:          \
+    return WireFormatLite::k##CamelFieldType##Size;
+
+      CASE_TYPE(INT32, Int32, Int32);
+      CASE_TYPE(INT64, Int64, Int64);
+      CASE_TYPE(UINT32, UInt32, UInt32);
+      CASE_TYPE(UINT64, UInt64, UInt64);
+      CASE_TYPE(SINT32, SInt32, Int32);
+      CASE_TYPE(SINT64, SInt64, Int64);
+      CASE_TYPE(STRING, String, String);
+      FIXED_CASE_TYPE(FIXED32, Fixed32);
+      FIXED_CASE_TYPE(FIXED64, Fixed64);
+      FIXED_CASE_TYPE(SFIXED32, SFixed32);
+      FIXED_CASE_TYPE(SFIXED64, SFixed64);
+      FIXED_CASE_TYPE(BOOL, Bool);
+
+#undef CASE_TYPE
+#undef FIXED_CASE_TYPE
+  }
+  GOOGLE_LOG(FATAL) << "Cannot get here";
+  return 0;
+}
+
+static size_t MapValueRefDataOnlyByteSize(const FieldDescriptor* field,
+                                          const MapValueConstRef& value) {
+  switch (field->type()) {
+    case FieldDescriptor::TYPE_GROUP:
+      GOOGLE_LOG(FATAL) << "Unsupported";
+      return 0;
+#define CASE_TYPE(FieldType, CamelFieldType, CamelCppType) \
+  case FieldDescriptor::TYPE_##FieldType:                  \
+    return WireFormatLite::CamelFieldType##Size(           \
+        value.Get##CamelCppType##Value());
+
+#define FIXED_CASE_TYPE(FieldType, CamelFieldType) \
+  case FieldDescriptor::TYPE_##FieldType:          \
+    return WireFormatLite::k##CamelFieldType##Size;
+
+      CASE_TYPE(INT32, Int32, Int32);
+      CASE_TYPE(INT64, Int64, Int64);
+      CASE_TYPE(UINT32, UInt32, UInt32);
+      CASE_TYPE(UINT64, UInt64, UInt64);
+      CASE_TYPE(SINT32, SInt32, Int32);
+      CASE_TYPE(SINT64, SInt64, Int64);
+      CASE_TYPE(STRING, String, String);
+      CASE_TYPE(BYTES, Bytes, String);
+      CASE_TYPE(ENUM, Enum, Enum);
+      CASE_TYPE(MESSAGE, Message, Message);
+      FIXED_CASE_TYPE(FIXED32, Fixed32);
+      FIXED_CASE_TYPE(FIXED64, Fixed64);
+      FIXED_CASE_TYPE(SFIXED32, SFixed32);
+      FIXED_CASE_TYPE(SFIXED64, SFixed64);
+      FIXED_CASE_TYPE(DOUBLE, Double);
+      FIXED_CASE_TYPE(FLOAT, Float);
+      FIXED_CASE_TYPE(BOOL, Bool);
+
+#undef CASE_TYPE
+#undef FIXED_CASE_TYPE
+  }
+  GOOGLE_LOG(FATAL) << "Cannot get here";
+  return 0;
+}
+
+size_t WireFormat::FieldDataOnlyByteSize(const FieldDescriptor* field,
+                                         const Message& message) {
+  const Reflection* message_reflection = message.GetReflection();
+
+  size_t data_size = 0;
+
+  if (field->is_map()) {
+    const MapFieldBase* map_field =
+        message_reflection->GetMapData(message, field);
+    if (map_field->IsMapValid()) {
+      MapIterator iter(const_cast<Message*>(&message), field);
+      MapIterator end(const_cast<Message*>(&message), field);
+      const FieldDescriptor* key_field = field->message_type()->field(0);
+      const FieldDescriptor* value_field = field->message_type()->field(1);
+      for (map_field->MapBegin(&iter), map_field->MapEnd(&end); iter != end;
+           ++iter) {
+        size_t size = kMapEntryTagByteSize;
+        size += MapKeyDataOnlyByteSize(key_field, iter.GetKey());
+        size += MapValueRefDataOnlyByteSize(value_field, iter.GetValueRef());
+        data_size += WireFormatLite::LengthDelimitedSize(size);
+      }
+      return data_size;
+    }
+  }
+
+  size_t count = 0;
+  if (field->is_repeated()) {
+    count =
+        internal::FromIntSize(message_reflection->FieldSize(message, field));
+  } else if (field->containing_type()->options().map_entry()) {
+    // Map entry fields always need to be serialized.
+    count = 1;
+  } else if (message_reflection->HasField(message, field)) {
+    count = 1;
+  }
+
+  switch (field->type()) {
+#define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE_METHOD)                      \
+  case FieldDescriptor::TYPE_##TYPE:                                        \
+    if (field->is_repeated()) {                                             \
+      for (size_t j = 0; j < count; j++) {                                  \
+        data_size += WireFormatLite::TYPE_METHOD##Size(                     \
+            message_reflection->GetRepeated##CPPTYPE_METHOD(message, field, \
+                                                            j));            \
+      }                                                                     \
+    } else {                                                                \
+      data_size += WireFormatLite::TYPE_METHOD##Size(                       \
+          message_reflection->Get##CPPTYPE_METHOD(message, field));         \
+    }                                                                       \
+    break;
+
+#define HANDLE_FIXED_TYPE(TYPE, TYPE_METHOD)                   \
+  case FieldDescriptor::TYPE_##TYPE:                           \
+    data_size += count * WireFormatLite::k##TYPE_METHOD##Size; \
+    break;
+
+    HANDLE_TYPE(INT32, Int32, Int32)
+    HANDLE_TYPE(INT64, Int64, Int64)
+    HANDLE_TYPE(SINT32, SInt32, Int32)
+    HANDLE_TYPE(SINT64, SInt64, Int64)
+    HANDLE_TYPE(UINT32, UInt32, UInt32)
+    HANDLE_TYPE(UINT64, UInt64, UInt64)
+
+    HANDLE_FIXED_TYPE(FIXED32, Fixed32)
+    HANDLE_FIXED_TYPE(FIXED64, Fixed64)
+    HANDLE_FIXED_TYPE(SFIXED32, SFixed32)
+    HANDLE_FIXED_TYPE(SFIXED64, SFixed64)
+
+    HANDLE_FIXED_TYPE(FLOAT, Float)
+    HANDLE_FIXED_TYPE(DOUBLE, Double)
+
+    HANDLE_FIXED_TYPE(BOOL, Bool)
+
+    HANDLE_TYPE(GROUP, Group, Message)
+    HANDLE_TYPE(MESSAGE, Message, Message)
+#undef HANDLE_TYPE
+#undef HANDLE_FIXED_TYPE
+
+    case FieldDescriptor::TYPE_ENUM: {
+      if (field->is_repeated()) {
+        for (size_t j = 0; j < count; j++) {
+          data_size += WireFormatLite::EnumSize(
+              message_reflection->GetRepeatedEnum(message, field, j)->number());
+        }
+      } else {
+        data_size += WireFormatLite::EnumSize(
+            message_reflection->GetEnum(message, field)->number());
+      }
+      break;
+    }
+
+    // Handle strings separately so that we can get string references
+    // instead of copying.
+    case FieldDescriptor::TYPE_STRING:
+    case FieldDescriptor::TYPE_BYTES: {
+      for (size_t j = 0; j < count; j++) {
+        std::string scratch;
+        const std::string& value =
+            field->is_repeated()
+                ? message_reflection->GetRepeatedStringReference(message, field,
+                                                                 j, &scratch)
+                : message_reflection->GetStringReference(message, field,
+                                                         &scratch);
+        data_size += WireFormatLite::StringSize(value);
+      }
+      break;
+    }
+  }
+  return data_size;
+}
+
+size_t WireFormat::MessageSetItemByteSize(const FieldDescriptor* field,
+                                          const Message& message) {
+  const Reflection* message_reflection = message.GetReflection();
+
+  size_t our_size = WireFormatLite::kMessageSetItemTagsSize;
+
+  // type_id
+  our_size += io::CodedOutputStream::VarintSize32(field->number());
+
+  // message
+  const Message& sub_message = message_reflection->GetMessage(message, field);
+  size_t message_size = sub_message.ByteSizeLong();
+
+  our_size += io::CodedOutputStream::VarintSize32(message_size);
+  our_size += message_size;
+
+  return our_size;
+}
+
+// Compute the size of the UnknownFieldSet on the wire.
+size_t ComputeUnknownFieldsSize(const InternalMetadata& metadata,
+                                size_t total_size, CachedSize* cached_size) {
+  total_size += WireFormat::ComputeUnknownFieldsSize(
+      metadata.unknown_fields<UnknownFieldSet>(
+          UnknownFieldSet::default_instance));
+  cached_size->Set(ToCachedSize(total_size));
+  return total_size;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/wire_format_lite.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/wire_format_lite.cpp
new file mode 100644
index 0000000..5ab1ca1
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/wire_format_lite.cpp
@@ -0,0 +1,818 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <google/protobuf/wire_format_lite.h>
+
+#include <limits>
+#include <stack>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/stubs/stringprintf.h>
+
+
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+#if !defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)
+// Old version of MSVC doesn't like definitions of inline constants, GCC
+// requires them.
+const int WireFormatLite::kMessageSetItemStartTag;
+const int WireFormatLite::kMessageSetItemEndTag;
+const int WireFormatLite::kMessageSetTypeIdTag;
+const int WireFormatLite::kMessageSetMessageTag;
+
+#endif
+
+// IBM xlC requires prefixing constants with WireFormatLite::
+const size_t WireFormatLite::kMessageSetItemTagsSize =
+    io::CodedOutputStream::StaticVarintSize32<
+        WireFormatLite::kMessageSetItemStartTag>::value +
+    io::CodedOutputStream::StaticVarintSize32<
+        WireFormatLite::kMessageSetItemEndTag>::value +
+    io::CodedOutputStream::StaticVarintSize32<
+        WireFormatLite::kMessageSetTypeIdTag>::value +
+    io::CodedOutputStream::StaticVarintSize32<
+        WireFormatLite::kMessageSetMessageTag>::value;
+
+const WireFormatLite::CppType
+    WireFormatLite::kFieldTypeToCppTypeMap[MAX_FIELD_TYPE + 1] = {
+        static_cast<CppType>(0),  // 0 is reserved for errors
+
+        CPPTYPE_DOUBLE,   // TYPE_DOUBLE
+        CPPTYPE_FLOAT,    // TYPE_FLOAT
+        CPPTYPE_INT64,    // TYPE_INT64
+        CPPTYPE_UINT64,   // TYPE_UINT64
+        CPPTYPE_INT32,    // TYPE_INT32
+        CPPTYPE_UINT64,   // TYPE_FIXED64
+        CPPTYPE_UINT32,   // TYPE_FIXED32
+        CPPTYPE_BOOL,     // TYPE_BOOL
+        CPPTYPE_STRING,   // TYPE_STRING
+        CPPTYPE_MESSAGE,  // TYPE_GROUP
+        CPPTYPE_MESSAGE,  // TYPE_MESSAGE
+        CPPTYPE_STRING,   // TYPE_BYTES
+        CPPTYPE_UINT32,   // TYPE_UINT32
+        CPPTYPE_ENUM,     // TYPE_ENUM
+        CPPTYPE_INT32,    // TYPE_SFIXED32
+        CPPTYPE_INT64,    // TYPE_SFIXED64
+        CPPTYPE_INT32,    // TYPE_SINT32
+        CPPTYPE_INT64,    // TYPE_SINT64
+};
+
+const WireFormatLite::WireType
+    WireFormatLite::kWireTypeForFieldType[MAX_FIELD_TYPE + 1] = {
+        static_cast<WireFormatLite::WireType>(-1),  // invalid
+        WireFormatLite::WIRETYPE_FIXED64,           // TYPE_DOUBLE
+        WireFormatLite::WIRETYPE_FIXED32,           // TYPE_FLOAT
+        WireFormatLite::WIRETYPE_VARINT,            // TYPE_INT64
+        WireFormatLite::WIRETYPE_VARINT,            // TYPE_UINT64
+        WireFormatLite::WIRETYPE_VARINT,            // TYPE_INT32
+        WireFormatLite::WIRETYPE_FIXED64,           // TYPE_FIXED64
+        WireFormatLite::WIRETYPE_FIXED32,           // TYPE_FIXED32
+        WireFormatLite::WIRETYPE_VARINT,            // TYPE_BOOL
+        WireFormatLite::WIRETYPE_LENGTH_DELIMITED,  // TYPE_STRING
+        WireFormatLite::WIRETYPE_START_GROUP,       // TYPE_GROUP
+        WireFormatLite::WIRETYPE_LENGTH_DELIMITED,  // TYPE_MESSAGE
+        WireFormatLite::WIRETYPE_LENGTH_DELIMITED,  // TYPE_BYTES
+        WireFormatLite::WIRETYPE_VARINT,            // TYPE_UINT32
+        WireFormatLite::WIRETYPE_VARINT,            // TYPE_ENUM
+        WireFormatLite::WIRETYPE_FIXED32,           // TYPE_SFIXED32
+        WireFormatLite::WIRETYPE_FIXED64,           // TYPE_SFIXED64
+        WireFormatLite::WIRETYPE_VARINT,            // TYPE_SINT32
+        WireFormatLite::WIRETYPE_VARINT,            // TYPE_SINT64
+};
+
+bool WireFormatLite::SkipField(io::CodedInputStream* input, uint32_t tag) {
+  // Field number 0 is illegal.
+  if (WireFormatLite::GetTagFieldNumber(tag) == 0) return false;
+  switch (WireFormatLite::GetTagWireType(tag)) {
+    case WireFormatLite::WIRETYPE_VARINT: {
+      uint64_t value;
+      if (!input->ReadVarint64(&value)) return false;
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_FIXED64: {
+      uint64_t value;
+      if (!input->ReadLittleEndian64(&value)) return false;
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_LENGTH_DELIMITED: {
+      uint32_t length;
+      if (!input->ReadVarint32(&length)) return false;
+      if (!input->Skip(length)) return false;
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_START_GROUP: {
+      if (!input->IncrementRecursionDepth()) return false;
+      if (!SkipMessage(input)) return false;
+      input->DecrementRecursionDepth();
+      // Check that the ending tag matched the starting tag.
+      if (!input->LastTagWas(
+              WireFormatLite::MakeTag(WireFormatLite::GetTagFieldNumber(tag),
+                                      WireFormatLite::WIRETYPE_END_GROUP))) {
+        return false;
+      }
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_END_GROUP: {
+      return false;
+    }
+    case WireFormatLite::WIRETYPE_FIXED32: {
+      uint32_t value;
+      if (!input->ReadLittleEndian32(&value)) return false;
+      return true;
+    }
+    default: {
+      return false;
+    }
+  }
+}
+
+bool WireFormatLite::SkipField(io::CodedInputStream* input, uint32_t tag,
+                               io::CodedOutputStream* output) {
+  // Field number 0 is illegal.
+  if (WireFormatLite::GetTagFieldNumber(tag) == 0) return false;
+  switch (WireFormatLite::GetTagWireType(tag)) {
+    case WireFormatLite::WIRETYPE_VARINT: {
+      uint64_t value;
+      if (!input->ReadVarint64(&value)) return false;
+      output->WriteVarint32(tag);
+      output->WriteVarint64(value);
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_FIXED64: {
+      uint64_t value;
+      if (!input->ReadLittleEndian64(&value)) return false;
+      output->WriteVarint32(tag);
+      output->WriteLittleEndian64(value);
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_LENGTH_DELIMITED: {
+      uint32_t length;
+      if (!input->ReadVarint32(&length)) return false;
+      output->WriteVarint32(tag);
+      output->WriteVarint32(length);
+      // TODO(mkilavuz): Provide API to prevent extra string copying.
+      std::string temp;
+      if (!input->ReadString(&temp, length)) return false;
+      output->WriteString(temp);
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_START_GROUP: {
+      output->WriteVarint32(tag);
+      if (!input->IncrementRecursionDepth()) return false;
+      if (!SkipMessage(input, output)) return false;
+      input->DecrementRecursionDepth();
+      // Check that the ending tag matched the starting tag.
+      if (!input->LastTagWas(
+              WireFormatLite::MakeTag(WireFormatLite::GetTagFieldNumber(tag),
+                                      WireFormatLite::WIRETYPE_END_GROUP))) {
+        return false;
+      }
+      return true;
+    }
+    case WireFormatLite::WIRETYPE_END_GROUP: {
+      return false;
+    }
+    case WireFormatLite::WIRETYPE_FIXED32: {
+      uint32_t value;
+      if (!input->ReadLittleEndian32(&value)) return false;
+      output->WriteVarint32(tag);
+      output->WriteLittleEndian32(value);
+      return true;
+    }
+    default: {
+      return false;
+    }
+  }
+}
+
+bool WireFormatLite::SkipMessage(io::CodedInputStream* input) {
+  while (true) {
+    uint32_t tag = input->ReadTag();
+    if (tag == 0) {
+      // End of input.  This is a valid place to end, so return true.
+      return true;
+    }
+
+    WireFormatLite::WireType wire_type = WireFormatLite::GetTagWireType(tag);
+
+    if (wire_type == WireFormatLite::WIRETYPE_END_GROUP) {
+      // Must be the end of the message.
+      return true;
+    }
+
+    if (!SkipField(input, tag)) return false;
+  }
+}
+
+bool WireFormatLite::SkipMessage(io::CodedInputStream* input,
+                                 io::CodedOutputStream* output) {
+  while (true) {
+    uint32_t tag = input->ReadTag();
+    if (tag == 0) {
+      // End of input.  This is a valid place to end, so return true.
+      return true;
+    }
+
+    WireFormatLite::WireType wire_type = WireFormatLite::GetTagWireType(tag);
+
+    if (wire_type == WireFormatLite::WIRETYPE_END_GROUP) {
+      output->WriteVarint32(tag);
+      // Must be the end of the message.
+      return true;
+    }
+
+    if (!SkipField(input, tag, output)) return false;
+  }
+}
+
+bool FieldSkipper::SkipField(io::CodedInputStream* input, uint32_t tag) {
+  return WireFormatLite::SkipField(input, tag);
+}
+
+bool FieldSkipper::SkipMessage(io::CodedInputStream* input) {
+  return WireFormatLite::SkipMessage(input);
+}
+
+void FieldSkipper::SkipUnknownEnum(int /* field_number */, int /* value */) {
+  // Nothing.
+}
+
+bool CodedOutputStreamFieldSkipper::SkipField(io::CodedInputStream* input,
+                                              uint32_t tag) {
+  return WireFormatLite::SkipField(input, tag, unknown_fields_);
+}
+
+bool CodedOutputStreamFieldSkipper::SkipMessage(io::CodedInputStream* input) {
+  return WireFormatLite::SkipMessage(input, unknown_fields_);
+}
+
+void CodedOutputStreamFieldSkipper::SkipUnknownEnum(int field_number,
+                                                    int value) {
+  unknown_fields_->WriteVarint32(field_number);
+  unknown_fields_->WriteVarint64(value);
+}
+
+bool WireFormatLite::ReadPackedEnumPreserveUnknowns(
+    io::CodedInputStream* input, int field_number, bool (*is_valid)(int),
+    io::CodedOutputStream* unknown_fields_stream, RepeatedField<int>* values) {
+  uint32_t length;
+  if (!input->ReadVarint32(&length)) return false;
+  io::CodedInputStream::Limit limit = input->PushLimit(length);
+  while (input->BytesUntilLimit() > 0) {
+    int value;
+    if (!ReadPrimitive<int, WireFormatLite::TYPE_ENUM>(input, &value)) {
+      return false;
+    }
+    if (is_valid == nullptr || is_valid(value)) {
+      values->Add(value);
+    } else {
+      uint32_t tag = WireFormatLite::MakeTag(field_number,
+                                             WireFormatLite::WIRETYPE_VARINT);
+      unknown_fields_stream->WriteVarint32(tag);
+      unknown_fields_stream->WriteVarint32(value);
+    }
+  }
+  input->PopLimit(limit);
+  return true;
+}
+
+#if !defined(PROTOBUF_LITTLE_ENDIAN)
+
+namespace {
+void EncodeFixedSizeValue(float v, uint8_t* dest) {
+  WireFormatLite::WriteFloatNoTagToArray(v, dest);
+}
+
+void EncodeFixedSizeValue(double v, uint8_t* dest) {
+  WireFormatLite::WriteDoubleNoTagToArray(v, dest);
+}
+
+void EncodeFixedSizeValue(uint32_t v, uint8_t* dest) {
+  WireFormatLite::WriteFixed32NoTagToArray(v, dest);
+}
+
+void EncodeFixedSizeValue(uint64_t v, uint8_t* dest) {
+  WireFormatLite::WriteFixed64NoTagToArray(v, dest);
+}
+
+void EncodeFixedSizeValue(int32_t v, uint8_t* dest) {
+  WireFormatLite::WriteSFixed32NoTagToArray(v, dest);
+}
+
+void EncodeFixedSizeValue(int64_t v, uint8_t* dest) {
+  WireFormatLite::WriteSFixed64NoTagToArray(v, dest);
+}
+
+void EncodeFixedSizeValue(bool v, uint8_t* dest) {
+  WireFormatLite::WriteBoolNoTagToArray(v, dest);
+}
+}  // anonymous namespace
+
+#endif  // !defined(PROTOBUF_LITTLE_ENDIAN)
+
+template <typename CType>
+static void WriteArray(const CType* a, int n, io::CodedOutputStream* output) {
+#if defined(PROTOBUF_LITTLE_ENDIAN)
+  output->WriteRaw(reinterpret_cast<const char*>(a), n * sizeof(a[0]));
+#else
+  const int kAtATime = 128;
+  uint8_t buf[sizeof(CType) * kAtATime];
+  for (int i = 0; i < n; i += kAtATime) {
+    int to_do = std::min(kAtATime, n - i);
+    uint8_t* ptr = buf;
+    for (int j = 0; j < to_do; j++) {
+      EncodeFixedSizeValue(a[i + j], ptr);
+      ptr += sizeof(a[0]);
+    }
+    output->WriteRaw(buf, to_do * sizeof(a[0]));
+  }
+#endif
+}
+
+void WireFormatLite::WriteFloatArray(const float* a, int n,
+                                     io::CodedOutputStream* output) {
+  WriteArray<float>(a, n, output);
+}
+
+void WireFormatLite::WriteDoubleArray(const double* a, int n,
+                                      io::CodedOutputStream* output) {
+  WriteArray<double>(a, n, output);
+}
+
+void WireFormatLite::WriteFixed32Array(const uint32_t* a, int n,
+                                       io::CodedOutputStream* output) {
+  WriteArray<uint32_t>(a, n, output);
+}
+
+void WireFormatLite::WriteFixed64Array(const uint64_t* a, int n,
+                                       io::CodedOutputStream* output) {
+  WriteArray<uint64_t>(a, n, output);
+}
+
+void WireFormatLite::WriteSFixed32Array(const int32_t* a, int n,
+                                        io::CodedOutputStream* output) {
+  WriteArray<int32_t>(a, n, output);
+}
+
+void WireFormatLite::WriteSFixed64Array(const int64_t* a, int n,
+                                        io::CodedOutputStream* output) {
+  WriteArray<int64_t>(a, n, output);
+}
+
+void WireFormatLite::WriteBoolArray(const bool* a, int n,
+                                    io::CodedOutputStream* output) {
+  WriteArray<bool>(a, n, output);
+}
+
+void WireFormatLite::WriteInt32(int field_number, int32_t value,
+                                io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_VARINT, output);
+  WriteInt32NoTag(value, output);
+}
+void WireFormatLite::WriteInt64(int field_number, int64_t value,
+                                io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_VARINT, output);
+  WriteInt64NoTag(value, output);
+}
+void WireFormatLite::WriteUInt32(int field_number, uint32_t value,
+                                 io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_VARINT, output);
+  WriteUInt32NoTag(value, output);
+}
+void WireFormatLite::WriteUInt64(int field_number, uint64_t value,
+                                 io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_VARINT, output);
+  WriteUInt64NoTag(value, output);
+}
+void WireFormatLite::WriteSInt32(int field_number, int32_t value,
+                                 io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_VARINT, output);
+  WriteSInt32NoTag(value, output);
+}
+void WireFormatLite::WriteSInt64(int field_number, int64_t value,
+                                 io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_VARINT, output);
+  WriteSInt64NoTag(value, output);
+}
+void WireFormatLite::WriteFixed32(int field_number, uint32_t value,
+                                  io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_FIXED32, output);
+  WriteFixed32NoTag(value, output);
+}
+void WireFormatLite::WriteFixed64(int field_number, uint64_t value,
+                                  io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_FIXED64, output);
+  WriteFixed64NoTag(value, output);
+}
+void WireFormatLite::WriteSFixed32(int field_number, int32_t value,
+                                   io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_FIXED32, output);
+  WriteSFixed32NoTag(value, output);
+}
+void WireFormatLite::WriteSFixed64(int field_number, int64_t value,
+                                   io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_FIXED64, output);
+  WriteSFixed64NoTag(value, output);
+}
+void WireFormatLite::WriteFloat(int field_number, float value,
+                                io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_FIXED32, output);
+  WriteFloatNoTag(value, output);
+}
+void WireFormatLite::WriteDouble(int field_number, double value,
+                                 io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_FIXED64, output);
+  WriteDoubleNoTag(value, output);
+}
+void WireFormatLite::WriteBool(int field_number, bool value,
+                               io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_VARINT, output);
+  WriteBoolNoTag(value, output);
+}
+void WireFormatLite::WriteEnum(int field_number, int value,
+                               io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_VARINT, output);
+  WriteEnumNoTag(value, output);
+}
+
+constexpr size_t kInt32MaxSize = std::numeric_limits<int32_t>::max();
+
+void WireFormatLite::WriteString(int field_number, const std::string& value,
+                                 io::CodedOutputStream* output) {
+  // String is for UTF-8 text only
+  WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output);
+  GOOGLE_CHECK_LE(value.size(), kInt32MaxSize);
+  output->WriteVarint32(value.size());
+  output->WriteString(value);
+}
+void WireFormatLite::WriteStringMaybeAliased(int field_number,
+                                             const std::string& value,
+                                             io::CodedOutputStream* output) {
+  // String is for UTF-8 text only
+  WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output);
+  GOOGLE_CHECK_LE(value.size(), kInt32MaxSize);
+  output->WriteVarint32(value.size());
+  output->WriteRawMaybeAliased(value.data(), value.size());
+}
+void WireFormatLite::WriteBytes(int field_number, const std::string& value,
+                                io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output);
+  GOOGLE_CHECK_LE(value.size(), kInt32MaxSize);
+  output->WriteVarint32(value.size());
+  output->WriteString(value);
+}
+void WireFormatLite::WriteBytesMaybeAliased(int field_number,
+                                            const std::string& value,
+                                            io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output);
+  GOOGLE_CHECK_LE(value.size(), kInt32MaxSize);
+  output->WriteVarint32(value.size());
+  output->WriteRawMaybeAliased(value.data(), value.size());
+}
+
+
+void WireFormatLite::WriteGroup(int field_number, const MessageLite& value,
+                                io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_START_GROUP, output);
+  value.SerializeWithCachedSizes(output);
+  WriteTag(field_number, WIRETYPE_END_GROUP, output);
+}
+
+void WireFormatLite::WriteMessage(int field_number, const MessageLite& value,
+                                  io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output);
+  const int size = value.GetCachedSize();
+  output->WriteVarint32(size);
+  value.SerializeWithCachedSizes(output);
+}
+
+uint8_t* WireFormatLite::InternalWriteGroup(int field_number,
+                                            const MessageLite& value,
+                                            uint8_t* target,
+                                            io::EpsCopyOutputStream* stream) {
+  target = stream->EnsureSpace(target);
+  target = WriteTagToArray(field_number, WIRETYPE_START_GROUP, target);
+  target = value._InternalSerialize(target, stream);
+  target = stream->EnsureSpace(target);
+  return WriteTagToArray(field_number, WIRETYPE_END_GROUP, target);
+}
+
+uint8_t* WireFormatLite::InternalWriteMessage(int field_number,
+                                              const MessageLite& value,
+                                              int cached_size, uint8_t* target,
+                                              io::EpsCopyOutputStream* stream) {
+  target = stream->EnsureSpace(target);
+  target = WriteTagToArray(field_number, WIRETYPE_LENGTH_DELIMITED, target);
+  target = io::CodedOutputStream::WriteVarint32ToArray(
+      static_cast<uint32_t>(cached_size), target);
+  return value._InternalSerialize(target, stream);
+}
+
+void WireFormatLite::WriteSubMessageMaybeToArray(
+    int /*size*/, const MessageLite& value, io::CodedOutputStream* output) {
+  output->SetCur(value._InternalSerialize(output->Cur(), output->EpsCopy()));
+}
+
+void WireFormatLite::WriteGroupMaybeToArray(int field_number,
+                                            const MessageLite& value,
+                                            io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_START_GROUP, output);
+  const int size = value.GetCachedSize();
+  WriteSubMessageMaybeToArray(size, value, output);
+  WriteTag(field_number, WIRETYPE_END_GROUP, output);
+}
+
+void WireFormatLite::WriteMessageMaybeToArray(int field_number,
+                                              const MessageLite& value,
+                                              io::CodedOutputStream* output) {
+  WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output);
+  const int size = value.GetCachedSize();
+  output->WriteVarint32(size);
+  WriteSubMessageMaybeToArray(size, value, output);
+}
+
+PROTOBUF_NDEBUG_INLINE static bool ReadBytesToString(
+    io::CodedInputStream* input, std::string* value);
+inline static bool ReadBytesToString(io::CodedInputStream* input,
+                                     std::string* value) {
+  uint32_t length;
+  return input->ReadVarint32(&length) && input->ReadString(value, length);
+}
+
+bool WireFormatLite::ReadBytes(io::CodedInputStream* input,
+                               std::string* value) {
+  return ReadBytesToString(input, value);
+}
+
+bool WireFormatLite::ReadBytes(io::CodedInputStream* input, std::string** p) {
+  if (*p == &GetEmptyStringAlreadyInited()) {
+    *p = new std::string();
+  }
+  return ReadBytesToString(input, *p);
+}
+
+void PrintUTF8ErrorLog(StringPiece message_name,
+                       StringPiece field_name, const char* operation_str,
+                       bool emit_stacktrace) {
+  std::string stacktrace;
+  (void)emit_stacktrace;  // Parameter is used by Google-internal code.
+  std::string quoted_field_name = "";
+  if (!field_name.empty()) {
+    if (!message_name.empty()) {
+      quoted_field_name =
+          StrCat(" '", message_name, ".", field_name, "'");
+    } else {
+      quoted_field_name = StrCat(" '", field_name, "'");
+    }
+  }
+  std::string error_message =
+      StrCat("String field", quoted_field_name,
+                   " contains invalid UTF-8 data "
+                   "when ",
+                   operation_str,
+                   " a protocol buffer. Use the 'bytes' type if you intend to "
+                   "send raw bytes. ",
+                   stacktrace);
+  GOOGLE_LOG(ERROR) << error_message;
+}
+
+bool WireFormatLite::VerifyUtf8String(const char* data, int size, Operation op,
+                                      const char* field_name) {
+  if (!IsStructurallyValidUTF8(data, size)) {
+    const char* operation_str = nullptr;
+    switch (op) {
+      case PARSE:
+        operation_str = "parsing";
+        break;
+      case SERIALIZE:
+        operation_str = "serializing";
+        break;
+        // no default case: have the compiler warn if a case is not covered.
+    }
+    PrintUTF8ErrorLog("", field_name, operation_str, false);
+    return false;
+  }
+  return true;
+}
+
+// this code is deliberately written such that clang makes it into really
+// efficient SSE code.
+template <bool ZigZag, bool SignExtended, typename T>
+static size_t VarintSize(const T* data, const int n) {
+  static_assert(sizeof(T) == 4, "This routine only works for 32 bit integers");
+  // is_unsigned<T> => !ZigZag
+  static_assert(
+      (std::is_unsigned<T>::value ^ ZigZag) || std::is_signed<T>::value,
+      "Cannot ZigZag encode unsigned types");
+  // is_unsigned<T> => !SignExtended
+  static_assert(
+      (std::is_unsigned<T>::value ^ SignExtended) || std::is_signed<T>::value,
+      "Cannot SignExtended unsigned types");
+  static_assert(!(SignExtended && ZigZag),
+                "Cannot SignExtended and ZigZag on the same type");
+  uint32_t sum = n;
+  uint32_t msb_sum = 0;
+  for (int i = 0; i < n; i++) {
+    uint32_t x = data[i];
+    if (ZigZag) {
+      x = WireFormatLite::ZigZagEncode32(x);
+    } else if (SignExtended) {
+      msb_sum += x >> 31;
+    }
+    // clang is so smart that it produces optimal SSE sequence unrolling
+    // the loop 8 ints at a time. With a sequence of 4
+    // cmpres = cmpgt x, sizeclass  ( -1 or 0)
+    // sum = sum - cmpres
+    if (x > 0x7F) sum++;
+    if (x > 0x3FFF) sum++;
+    if (x > 0x1FFFFF) sum++;
+    if (x > 0xFFFFFFF) sum++;
+  }
+  if (SignExtended) sum += msb_sum * 5;
+  return sum;
+}
+
+template <bool ZigZag, typename T>
+static size_t VarintSize64(const T* data, const int n) {
+  static_assert(sizeof(T) == 8, "This routine only works for 64 bit integers");
+  // is_unsigned<T> => !ZigZag
+  static_assert(!ZigZag || !std::is_unsigned<T>::value,
+                "Cannot ZigZag encode unsigned types");
+  uint64_t sum = n;
+  for (int i = 0; i < n; i++) {
+    uint64_t x = data[i];
+    if (ZigZag) {
+      x = WireFormatLite::ZigZagEncode64(x);
+    }
+    // First step is a binary search, we can't branch in sse so we use the
+    // result of the compare to adjust sum and appropriately. This code is
+    // written to make clang recognize the vectorization.
+    uint64_t tmp = x >= (static_cast<uint64_t>(1) << 35) ? -1 : 0;
+    sum += 5 & tmp;
+    x >>= 35 & tmp;
+    if (x > 0x7F) sum++;
+    if (x > 0x3FFF) sum++;
+    if (x > 0x1FFFFF) sum++;
+    if (x > 0xFFFFFFF) sum++;
+  }
+  return sum;
+}
+
+// GCC does not recognize the vectorization opportunity
+// and other platforms are untested, in those cases using the optimized
+// varint size routine for each element is faster.
+// Hence we enable it only for clang
+#if defined(__SSE__) && defined(__clang__)
+size_t WireFormatLite::Int32Size(const RepeatedField<int32_t>& value) {
+  return VarintSize<false, true>(value.data(), value.size());
+}
+
+size_t WireFormatLite::UInt32Size(const RepeatedField<uint32_t>& value) {
+  return VarintSize<false, false>(value.data(), value.size());
+}
+
+size_t WireFormatLite::SInt32Size(const RepeatedField<int32_t>& value) {
+  return VarintSize<true, false>(value.data(), value.size());
+}
+
+size_t WireFormatLite::EnumSize(const RepeatedField<int>& value) {
+  // On ILP64, sizeof(int) == 8, which would require a different template.
+  return VarintSize<false, true>(value.data(), value.size());
+}
+
+#else  // !(defined(__SSE4_1__) && defined(__clang__))
+
+size_t WireFormatLite::Int32Size(const RepeatedField<int32_t>& value) {
+  size_t out = 0;
+  const int n = value.size();
+  for (int i = 0; i < n; i++) {
+    out += Int32Size(value.Get(i));
+  }
+  return out;
+}
+
+size_t WireFormatLite::UInt32Size(const RepeatedField<uint32_t>& value) {
+  size_t out = 0;
+  const int n = value.size();
+  for (int i = 0; i < n; i++) {
+    out += UInt32Size(value.Get(i));
+  }
+  return out;
+}
+
+size_t WireFormatLite::SInt32Size(const RepeatedField<int32_t>& value) {
+  size_t out = 0;
+  const int n = value.size();
+  for (int i = 0; i < n; i++) {
+    out += SInt32Size(value.Get(i));
+  }
+  return out;
+}
+
+size_t WireFormatLite::EnumSize(const RepeatedField<int>& value) {
+  size_t out = 0;
+  const int n = value.size();
+  for (int i = 0; i < n; i++) {
+    out += EnumSize(value.Get(i));
+  }
+  return out;
+}
+
+#endif
+
+// Micro benchmarks show that the SSE improved loop only starts beating
+// the normal loop on Haswell platforms and then only for >32 ints. We
+// disable this for now. Some specialized users might find it worthwhile to
+// enable this.
+#define USE_SSE_FOR_64_BIT_INTEGER_ARRAYS 0
+#if USE_SSE_FOR_64_BIT_INTEGER_ARRAYS
+size_t WireFormatLite::Int64Size(const RepeatedField<int64_t>& value) {
+  return VarintSize64<false>(value.data(), value.size());
+}
+
+size_t WireFormatLite::UInt64Size(const RepeatedField<uint64_t>& value) {
+  return VarintSize64<false>(value.data(), value.size());
+}
+
+size_t WireFormatLite::SInt64Size(const RepeatedField<int64_t>& value) {
+  return VarintSize64<true>(value.data(), value.size());
+}
+
+#else
+
+size_t WireFormatLite::Int64Size(const RepeatedField<int64_t>& value) {
+  size_t out = 0;
+  const int n = value.size();
+  for (int i = 0; i < n; i++) {
+    out += Int64Size(value.Get(i));
+  }
+  return out;
+}
+
+size_t WireFormatLite::UInt64Size(const RepeatedField<uint64_t>& value) {
+  size_t out = 0;
+  const int n = value.size();
+  for (int i = 0; i < n; i++) {
+    out += UInt64Size(value.Get(i));
+  }
+  return out;
+}
+
+size_t WireFormatLite::SInt64Size(const RepeatedField<int64_t>& value) {
+  size_t out = 0;
+  const int n = value.size();
+  for (int i = 0; i < n; i++) {
+    out += SInt64Size(value.Get(i));
+  }
+  return out;
+}
+
+#endif
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/wrappers.pb.cpp b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/wrappers.pb.cpp
new file mode 100644
index 0000000..40ba60a
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/main/native/thirdparty/protobuf/src/wrappers.pb.cpp
@@ -0,0 +1,1979 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/wrappers.proto
+
+#include <google/protobuf/wrappers.pb.h>
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+PROTOBUF_NAMESPACE_OPEN
+PROTOBUF_CONSTEXPR DoubleValue::DoubleValue(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.value_)*/0
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct DoubleValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR DoubleValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~DoubleValueDefaultTypeInternal() {}
+  union {
+    DoubleValue _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 DoubleValueDefaultTypeInternal _DoubleValue_default_instance_;
+PROTOBUF_CONSTEXPR FloatValue::FloatValue(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.value_)*/0
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct FloatValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR FloatValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~FloatValueDefaultTypeInternal() {}
+  union {
+    FloatValue _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FloatValueDefaultTypeInternal _FloatValue_default_instance_;
+PROTOBUF_CONSTEXPR Int64Value::Int64Value(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.value_)*/int64_t{0}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct Int64ValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR Int64ValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~Int64ValueDefaultTypeInternal() {}
+  union {
+    Int64Value _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 Int64ValueDefaultTypeInternal _Int64Value_default_instance_;
+PROTOBUF_CONSTEXPR UInt64Value::UInt64Value(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.value_)*/uint64_t{0u}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct UInt64ValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR UInt64ValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~UInt64ValueDefaultTypeInternal() {}
+  union {
+    UInt64Value _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 UInt64ValueDefaultTypeInternal _UInt64Value_default_instance_;
+PROTOBUF_CONSTEXPR Int32Value::Int32Value(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.value_)*/0
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct Int32ValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR Int32ValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~Int32ValueDefaultTypeInternal() {}
+  union {
+    Int32Value _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 Int32ValueDefaultTypeInternal _Int32Value_default_instance_;
+PROTOBUF_CONSTEXPR UInt32Value::UInt32Value(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.value_)*/0u
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct UInt32ValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR UInt32ValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~UInt32ValueDefaultTypeInternal() {}
+  union {
+    UInt32Value _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 UInt32ValueDefaultTypeInternal _UInt32Value_default_instance_;
+PROTOBUF_CONSTEXPR BoolValue::BoolValue(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.value_)*/false
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct BoolValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR BoolValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~BoolValueDefaultTypeInternal() {}
+  union {
+    BoolValue _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 BoolValueDefaultTypeInternal _BoolValue_default_instance_;
+PROTOBUF_CONSTEXPR StringValue::StringValue(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.value_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct StringValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR StringValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~StringValueDefaultTypeInternal() {}
+  union {
+    StringValue _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 StringValueDefaultTypeInternal _StringValue_default_instance_;
+PROTOBUF_CONSTEXPR BytesValue::BytesValue(
+    ::_pbi::ConstantInitialized): _impl_{
+    /*decltype(_impl_.value_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+  , /*decltype(_impl_._cached_size_)*/{}} {}
+struct BytesValueDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR BytesValueDefaultTypeInternal()
+      : _instance(::_pbi::ConstantInitialized{}) {}
+  ~BytesValueDefaultTypeInternal() {}
+  union {
+    BytesValue _instance;
+  };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 BytesValueDefaultTypeInternal _BytesValue_default_instance_;
+PROTOBUF_NAMESPACE_CLOSE
+static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2fwrappers_2eproto[9];
+static constexpr ::_pb::EnumDescriptor const** file_level_enum_descriptors_google_2fprotobuf_2fwrappers_2eproto = nullptr;
+static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2fwrappers_2eproto = nullptr;
+
+const uint32_t TableStruct_google_2fprotobuf_2fwrappers_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DoubleValue, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::DoubleValue, _impl_.value_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FloatValue, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::FloatValue, _impl_.value_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Int64Value, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Int64Value, _impl_.value_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UInt64Value, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UInt64Value, _impl_.value_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Int32Value, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Int32Value, _impl_.value_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UInt32Value, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::UInt32Value, _impl_.value_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::BoolValue, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::BoolValue, _impl_.value_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::StringValue, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::StringValue, _impl_.value_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::BytesValue, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  ~0u,  // no _inlined_string_donated_
+  PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::BytesValue, _impl_.value_),
+};
+static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::DoubleValue)},
+  { 7, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::FloatValue)},
+  { 14, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Int64Value)},
+  { 21, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::UInt64Value)},
+  { 28, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Int32Value)},
+  { 35, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::UInt32Value)},
+  { 42, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::BoolValue)},
+  { 49, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::StringValue)},
+  { 56, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::BytesValue)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+  &::PROTOBUF_NAMESPACE_ID::_DoubleValue_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_FloatValue_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_Int64Value_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_UInt64Value_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_Int32Value_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_UInt32Value_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_BoolValue_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_StringValue_default_instance_._instance,
+  &::PROTOBUF_NAMESPACE_ID::_BytesValue_default_instance_._instance,
+};
+
+const char descriptor_table_protodef_google_2fprotobuf_2fwrappers_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n\036google/protobuf/wrappers.proto\022\017google"
+  ".protobuf\"\034\n\013DoubleValue\022\r\n\005value\030\001 \001(\001\""
+  "\033\n\nFloatValue\022\r\n\005value\030\001 \001(\002\"\033\n\nInt64Val"
+  "ue\022\r\n\005value\030\001 \001(\003\"\034\n\013UInt64Value\022\r\n\005valu"
+  "e\030\001 \001(\004\"\033\n\nInt32Value\022\r\n\005value\030\001 \001(\005\"\034\n\013"
+  "UInt32Value\022\r\n\005value\030\001 \001(\r\"\032\n\tBoolValue\022"
+  "\r\n\005value\030\001 \001(\010\"\034\n\013StringValue\022\r\n\005value\030\001"
+  " \001(\t\"\033\n\nBytesValue\022\r\n\005value\030\001 \001(\014B\203\001\n\023co"
+  "m.google.protobufB\rWrappersProtoP\001Z1goog"
+  "le.golang.org/protobuf/types/known/wrapp"
+  "erspb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKno"
+  "wnTypesb\006proto3"
+  ;
+static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fwrappers_2eproto = {
+    false, false, 455, descriptor_table_protodef_google_2fprotobuf_2fwrappers_2eproto,
+    "google/protobuf/wrappers.proto",
+    &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once, nullptr, 0, 9,
+    schemas, file_default_instances, TableStruct_google_2fprotobuf_2fwrappers_2eproto::offsets,
+    file_level_metadata_google_2fprotobuf_2fwrappers_2eproto, file_level_enum_descriptors_google_2fprotobuf_2fwrappers_2eproto,
+    file_level_service_descriptors_google_2fprotobuf_2fwrappers_2eproto,
+};
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2fwrappers_2eproto_getter() {
+  return &descriptor_table_google_2fprotobuf_2fwrappers_2eproto;
+}
+
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2fwrappers_2eproto(&descriptor_table_google_2fprotobuf_2fwrappers_2eproto);
+PROTOBUF_NAMESPACE_OPEN
+
+// ===================================================================
+
+class DoubleValue::_Internal {
+ public:
+};
+
+DoubleValue::DoubleValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.DoubleValue)
+}
+DoubleValue::DoubleValue(const DoubleValue& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  DoubleValue* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _this->_impl_.value_ = from._impl_.value_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.DoubleValue)
+}
+
+inline void DoubleValue::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){0}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+DoubleValue::~DoubleValue() {
+  // @@protoc_insertion_point(destructor:google.protobuf.DoubleValue)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void DoubleValue::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void DoubleValue::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void DoubleValue::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.DoubleValue)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.value_ = 0;
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* DoubleValue::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // double value = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 9)) {
+          _impl_.value_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<double>(ptr);
+          ptr += sizeof(double);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* DoubleValue::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.DoubleValue)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // double value = 1;
+  static_assert(sizeof(uint64_t) == sizeof(double), "Code assumes uint64_t and double are the same size.");
+  double tmp_value = this->_internal_value();
+  uint64_t raw_value;
+  memcpy(&raw_value, &tmp_value, sizeof(tmp_value));
+  if (raw_value != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteDoubleToArray(1, this->_internal_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.DoubleValue)
+  return target;
+}
+
+size_t DoubleValue::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.DoubleValue)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // double value = 1;
+  static_assert(sizeof(uint64_t) == sizeof(double), "Code assumes uint64_t and double are the same size.");
+  double tmp_value = this->_internal_value();
+  uint64_t raw_value;
+  memcpy(&raw_value, &tmp_value, sizeof(tmp_value));
+  if (raw_value != 0) {
+    total_size += 1 + 8;
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData DoubleValue::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    DoubleValue::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*DoubleValue::GetClassData() const { return &_class_data_; }
+
+
+void DoubleValue::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<DoubleValue*>(&to_msg);
+  auto& from = static_cast<const DoubleValue&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.DoubleValue)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  static_assert(sizeof(uint64_t) == sizeof(double), "Code assumes uint64_t and double are the same size.");
+  double tmp_value = from._internal_value();
+  uint64_t raw_value;
+  memcpy(&raw_value, &tmp_value, sizeof(tmp_value));
+  if (raw_value != 0) {
+    _this->_internal_set_value(from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void DoubleValue::CopyFrom(const DoubleValue& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.DoubleValue)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool DoubleValue::IsInitialized() const {
+  return true;
+}
+
+void DoubleValue::InternalSwap(DoubleValue* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_.value_, other->_impl_.value_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata DoubleValue::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_getter, &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fwrappers_2eproto[0]);
+}
+
+// ===================================================================
+
+class FloatValue::_Internal {
+ public:
+};
+
+FloatValue::FloatValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.FloatValue)
+}
+FloatValue::FloatValue(const FloatValue& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  FloatValue* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _this->_impl_.value_ = from._impl_.value_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.FloatValue)
+}
+
+inline void FloatValue::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){0}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+FloatValue::~FloatValue() {
+  // @@protoc_insertion_point(destructor:google.protobuf.FloatValue)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void FloatValue::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void FloatValue::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void FloatValue::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.FloatValue)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.value_ = 0;
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* FloatValue::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // float value = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 13)) {
+          _impl_.value_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<float>(ptr);
+          ptr += sizeof(float);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* FloatValue::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.FloatValue)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // float value = 1;
+  static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size.");
+  float tmp_value = this->_internal_value();
+  uint32_t raw_value;
+  memcpy(&raw_value, &tmp_value, sizeof(tmp_value));
+  if (raw_value != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteFloatToArray(1, this->_internal_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.FloatValue)
+  return target;
+}
+
+size_t FloatValue::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FloatValue)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // float value = 1;
+  static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size.");
+  float tmp_value = this->_internal_value();
+  uint32_t raw_value;
+  memcpy(&raw_value, &tmp_value, sizeof(tmp_value));
+  if (raw_value != 0) {
+    total_size += 1 + 4;
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FloatValue::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    FloatValue::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FloatValue::GetClassData() const { return &_class_data_; }
+
+
+void FloatValue::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<FloatValue*>(&to_msg);
+  auto& from = static_cast<const FloatValue&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FloatValue)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size.");
+  float tmp_value = from._internal_value();
+  uint32_t raw_value;
+  memcpy(&raw_value, &tmp_value, sizeof(tmp_value));
+  if (raw_value != 0) {
+    _this->_internal_set_value(from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void FloatValue::CopyFrom(const FloatValue& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FloatValue)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool FloatValue::IsInitialized() const {
+  return true;
+}
+
+void FloatValue::InternalSwap(FloatValue* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_.value_, other->_impl_.value_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata FloatValue::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_getter, &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fwrappers_2eproto[1]);
+}
+
+// ===================================================================
+
+class Int64Value::_Internal {
+ public:
+};
+
+Int64Value::Int64Value(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Int64Value)
+}
+Int64Value::Int64Value(const Int64Value& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Int64Value* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _this->_impl_.value_ = from._impl_.value_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Int64Value)
+}
+
+inline void Int64Value::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){int64_t{0}}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+Int64Value::~Int64Value() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Int64Value)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Int64Value::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void Int64Value::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Int64Value::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Int64Value)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.value_ = int64_t{0};
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Int64Value::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // int64 value = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _impl_.value_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Int64Value::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Int64Value)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // int64 value = 1;
+  if (this->_internal_value() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt64ToArray(1, this->_internal_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Int64Value)
+  return target;
+}
+
+size_t Int64Value::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Int64Value)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // int64 value = 1;
+  if (this->_internal_value() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int64SizePlusOne(this->_internal_value());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Int64Value::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Int64Value::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Int64Value::GetClassData() const { return &_class_data_; }
+
+
+void Int64Value::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Int64Value*>(&to_msg);
+  auto& from = static_cast<const Int64Value&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Int64Value)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (from._internal_value() != 0) {
+    _this->_internal_set_value(from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Int64Value::CopyFrom(const Int64Value& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Int64Value)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Int64Value::IsInitialized() const {
+  return true;
+}
+
+void Int64Value::InternalSwap(Int64Value* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_.value_, other->_impl_.value_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Int64Value::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_getter, &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fwrappers_2eproto[2]);
+}
+
+// ===================================================================
+
+class UInt64Value::_Internal {
+ public:
+};
+
+UInt64Value::UInt64Value(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.UInt64Value)
+}
+UInt64Value::UInt64Value(const UInt64Value& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  UInt64Value* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _this->_impl_.value_ = from._impl_.value_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.UInt64Value)
+}
+
+inline void UInt64Value::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){uint64_t{0u}}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+UInt64Value::~UInt64Value() {
+  // @@protoc_insertion_point(destructor:google.protobuf.UInt64Value)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void UInt64Value::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void UInt64Value::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void UInt64Value::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.UInt64Value)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.value_ = uint64_t{0u};
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* UInt64Value::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // uint64 value = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _impl_.value_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* UInt64Value::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.UInt64Value)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // uint64 value = 1;
+  if (this->_internal_value() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.UInt64Value)
+  return target;
+}
+
+size_t UInt64Value::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.UInt64Value)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // uint64 value = 1;
+  if (this->_internal_value() != 0) {
+    total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_value());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UInt64Value::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    UInt64Value::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UInt64Value::GetClassData() const { return &_class_data_; }
+
+
+void UInt64Value::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<UInt64Value*>(&to_msg);
+  auto& from = static_cast<const UInt64Value&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.UInt64Value)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (from._internal_value() != 0) {
+    _this->_internal_set_value(from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void UInt64Value::CopyFrom(const UInt64Value& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.UInt64Value)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool UInt64Value::IsInitialized() const {
+  return true;
+}
+
+void UInt64Value::InternalSwap(UInt64Value* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_.value_, other->_impl_.value_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata UInt64Value::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_getter, &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fwrappers_2eproto[3]);
+}
+
+// ===================================================================
+
+class Int32Value::_Internal {
+ public:
+};
+
+Int32Value::Int32Value(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.Int32Value)
+}
+Int32Value::Int32Value(const Int32Value& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  Int32Value* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _this->_impl_.value_ = from._impl_.value_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.Int32Value)
+}
+
+inline void Int32Value::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){0}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+Int32Value::~Int32Value() {
+  // @@protoc_insertion_point(destructor:google.protobuf.Int32Value)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void Int32Value::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void Int32Value::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void Int32Value::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.Int32Value)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.value_ = 0;
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* Int32Value::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // int32 value = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _impl_.value_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* Int32Value::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Int32Value)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // int32 value = 1;
+  if (this->_internal_value() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(1, this->_internal_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Int32Value)
+  return target;
+}
+
+size_t Int32Value::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Int32Value)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // int32 value = 1;
+  if (this->_internal_value() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_value());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Int32Value::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    Int32Value::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Int32Value::GetClassData() const { return &_class_data_; }
+
+
+void Int32Value::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<Int32Value*>(&to_msg);
+  auto& from = static_cast<const Int32Value&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Int32Value)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (from._internal_value() != 0) {
+    _this->_internal_set_value(from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void Int32Value::CopyFrom(const Int32Value& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Int32Value)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Int32Value::IsInitialized() const {
+  return true;
+}
+
+void Int32Value::InternalSwap(Int32Value* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_.value_, other->_impl_.value_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Int32Value::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_getter, &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fwrappers_2eproto[4]);
+}
+
+// ===================================================================
+
+class UInt32Value::_Internal {
+ public:
+};
+
+UInt32Value::UInt32Value(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.UInt32Value)
+}
+UInt32Value::UInt32Value(const UInt32Value& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  UInt32Value* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _this->_impl_.value_ = from._impl_.value_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.UInt32Value)
+}
+
+inline void UInt32Value::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){0u}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+UInt32Value::~UInt32Value() {
+  // @@protoc_insertion_point(destructor:google.protobuf.UInt32Value)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void UInt32Value::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void UInt32Value::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void UInt32Value::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.UInt32Value)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.value_ = 0u;
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* UInt32Value::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // uint32 value = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _impl_.value_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* UInt32Value::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.UInt32Value)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // uint32 value = 1;
+  if (this->_internal_value() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteUInt32ToArray(1, this->_internal_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.UInt32Value)
+  return target;
+}
+
+size_t UInt32Value::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.UInt32Value)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // uint32 value = 1;
+  if (this->_internal_value() != 0) {
+    total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_value());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UInt32Value::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    UInt32Value::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UInt32Value::GetClassData() const { return &_class_data_; }
+
+
+void UInt32Value::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<UInt32Value*>(&to_msg);
+  auto& from = static_cast<const UInt32Value&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.UInt32Value)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (from._internal_value() != 0) {
+    _this->_internal_set_value(from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void UInt32Value::CopyFrom(const UInt32Value& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.UInt32Value)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool UInt32Value::IsInitialized() const {
+  return true;
+}
+
+void UInt32Value::InternalSwap(UInt32Value* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_.value_, other->_impl_.value_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata UInt32Value::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_getter, &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fwrappers_2eproto[5]);
+}
+
+// ===================================================================
+
+class BoolValue::_Internal {
+ public:
+};
+
+BoolValue::BoolValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.BoolValue)
+}
+BoolValue::BoolValue(const BoolValue& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  BoolValue* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _this->_impl_.value_ = from._impl_.value_;
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.BoolValue)
+}
+
+inline void BoolValue::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){false}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+}
+
+BoolValue::~BoolValue() {
+  // @@protoc_insertion_point(destructor:google.protobuf.BoolValue)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void BoolValue::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void BoolValue::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void BoolValue::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.BoolValue)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.value_ = false;
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* BoolValue::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // bool value = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+          _impl_.value_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* BoolValue::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.BoolValue)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // bool value = 1;
+  if (this->_internal_value() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteBoolToArray(1, this->_internal_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.BoolValue)
+  return target;
+}
+
+size_t BoolValue::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.BoolValue)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // bool value = 1;
+  if (this->_internal_value() != 0) {
+    total_size += 1 + 1;
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData BoolValue::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    BoolValue::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*BoolValue::GetClassData() const { return &_class_data_; }
+
+
+void BoolValue::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<BoolValue*>(&to_msg);
+  auto& from = static_cast<const BoolValue&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.BoolValue)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (from._internal_value() != 0) {
+    _this->_internal_set_value(from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void BoolValue::CopyFrom(const BoolValue& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.BoolValue)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool BoolValue::IsInitialized() const {
+  return true;
+}
+
+void BoolValue::InternalSwap(BoolValue* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_.value_, other->_impl_.value_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata BoolValue::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_getter, &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fwrappers_2eproto[6]);
+}
+
+// ===================================================================
+
+class StringValue::_Internal {
+ public:
+};
+
+StringValue::StringValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.StringValue)
+}
+StringValue::StringValue(const StringValue& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  StringValue* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_value().empty()) {
+    _this->_impl_.value_.Set(from._internal_value(), 
+      _this->GetArenaForAllocation());
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.StringValue)
+}
+
+inline void StringValue::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+StringValue::~StringValue() {
+  // @@protoc_insertion_point(destructor:google.protobuf.StringValue)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void StringValue::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.value_.Destroy();
+}
+
+void StringValue::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void StringValue::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.StringValue)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.value_.ClearToEmpty();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* StringValue::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // string value = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_value();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.StringValue.value"));
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* StringValue::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.StringValue)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string value = 1;
+  if (!this->_internal_value().empty()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_value().data(), static_cast<int>(this->_internal_value().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "google.protobuf.StringValue.value");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.StringValue)
+  return target;
+}
+
+size_t StringValue::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.StringValue)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // string value = 1;
+  if (!this->_internal_value().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_value());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData StringValue::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    StringValue::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*StringValue::GetClassData() const { return &_class_data_; }
+
+
+void StringValue::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<StringValue*>(&to_msg);
+  auto& from = static_cast<const StringValue&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.StringValue)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (!from._internal_value().empty()) {
+    _this->_internal_set_value(from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void StringValue::CopyFrom(const StringValue& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.StringValue)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool StringValue::IsInitialized() const {
+  return true;
+}
+
+void StringValue::InternalSwap(StringValue* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.value_, lhs_arena,
+      &other->_impl_.value_, rhs_arena
+  );
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata StringValue::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_getter, &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fwrappers_2eproto[7]);
+}
+
+// ===================================================================
+
+class BytesValue::_Internal {
+ public:
+};
+
+BytesValue::BytesValue(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                         bool is_message_owned)
+  : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
+  SharedCtor(arena, is_message_owned);
+  // @@protoc_insertion_point(arena_constructor:google.protobuf.BytesValue)
+}
+BytesValue::BytesValue(const BytesValue& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message() {
+  BytesValue* const _this = this; (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}};
+
+  _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+  _impl_.value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_value().empty()) {
+    _this->_impl_.value_.Set(from._internal_value(), 
+      _this->GetArenaForAllocation());
+  }
+  // @@protoc_insertion_point(copy_constructor:google.protobuf.BytesValue)
+}
+
+inline void BytesValue::SharedCtor(
+    ::_pb::Arena* arena, bool is_message_owned) {
+  (void)arena;
+  (void)is_message_owned;
+  new (&_impl_) Impl_{
+      decltype(_impl_.value_){}
+    , /*decltype(_impl_._cached_size_)*/{}
+  };
+  _impl_.value_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+    _impl_.value_.Set("", GetArenaForAllocation());
+  #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+BytesValue::~BytesValue() {
+  // @@protoc_insertion_point(destructor:google.protobuf.BytesValue)
+  if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
+  (void)arena;
+    return;
+  }
+  SharedDtor();
+}
+
+inline void BytesValue::SharedDtor() {
+  GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.value_.Destroy();
+}
+
+void BytesValue::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+void BytesValue::Clear() {
+// @@protoc_insertion_point(message_clear_start:google.protobuf.BytesValue)
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.value_.ClearToEmpty();
+  _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+}
+
+const char* BytesValue::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    uint32_t tag;
+    ptr = ::_pbi::ReadTag(ptr, &tag);
+    switch (tag >> 3) {
+      // bytes value = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+          auto str = _internal_mutable_value();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+        } else
+          goto handle_unusual;
+        continue;
+      default:
+        goto handle_unusual;
+    }  // switch
+  handle_unusual:
+    if ((tag == 0) || ((tag & 7) == 4)) {
+      CHK_(ptr);
+      ctx->SetLastTag(tag);
+      goto message_done;
+    }
+    ptr = UnknownFieldParse(
+        tag,
+        _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
+        ptr, ctx);
+    CHK_(ptr != nullptr);
+  }  // while
+message_done:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto message_done;
+#undef CHK_
+}
+
+uint8_t* BytesValue::_InternalSerialize(
+    uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.BytesValue)
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // bytes value = 1;
+  if (!this->_internal_value().empty()) {
+    target = stream->WriteBytesMaybeAliased(
+        1, this->_internal_value(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.BytesValue)
+  return target;
+}
+
+size_t BytesValue::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:google.protobuf.BytesValue)
+  size_t total_size = 0;
+
+  uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // bytes value = 1;
+  if (!this->_internal_value().empty()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+        this->_internal_value());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData BytesValue::_class_data_ = {
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
+    BytesValue::MergeImpl
+};
+const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*BytesValue::GetClassData() const { return &_class_data_; }
+
+
+void BytesValue::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
+  auto* const _this = static_cast<BytesValue*>(&to_msg);
+  auto& from = static_cast<const BytesValue&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.BytesValue)
+  GOOGLE_DCHECK_NE(&from, _this);
+  uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (!from._internal_value().empty()) {
+    _this->_internal_set_value(from._internal_value());
+  }
+  _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void BytesValue::CopyFrom(const BytesValue& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.BytesValue)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool BytesValue::IsInitialized() const {
+  return true;
+}
+
+void BytesValue::InternalSwap(BytesValue* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+      &_impl_.value_, lhs_arena,
+      &other->_impl_.value_, rhs_arena
+  );
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata BytesValue::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_getter, &descriptor_table_google_2fprotobuf_2fwrappers_2eproto_once,
+      file_level_metadata_google_2fprotobuf_2fwrappers_2eproto[8]);
+}
+
+// @@protoc_insertion_point(namespace_scope)
+PROTOBUF_NAMESPACE_CLOSE
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::DoubleValue*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::DoubleValue >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::DoubleValue >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::FloatValue*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::FloatValue >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::FloatValue >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Int64Value*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Int64Value >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Int64Value >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::UInt64Value*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::UInt64Value >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::UInt64Value >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Int32Value*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Int32Value >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Int32Value >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::UInt32Value*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::UInt32Value >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::UInt32Value >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::BoolValue*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::BoolValue >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::BoolValue >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::StringValue*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::StringValue >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::StringValue >(arena);
+}
+template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::BytesValue*
+Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::BytesValue >(Arena* arena) {
+  return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::BytesValue >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/third_party/allwpilib/wpiutil/src/main/native/windows/StackTrace.cpp b/third_party/allwpilib/wpiutil/src/main/native/windows/StackTrace.cpp
index de31b2a..5df8a6a 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/windows/StackTrace.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/windows/StackTrace.cpp
@@ -23,13 +23,7 @@
 }  // namespace
 
 void StackTraceWalker::OnOutput(LPCTSTR szText) {
-#ifdef _UNICODE
-  wpi::SmallString<128> utf8;
-  wpi::sys::windows::UTF16ToUTF8(szText, wcslen(szText), utf8);
-  m_output.append(utf8.data(), utf8.size());
-#else
   m_output.append(szText);
-#endif
 }
 
 namespace wpi {
diff --git a/third_party/allwpilib/wpiutil/src/main/native/windows/StackWalker.cpp b/third_party/allwpilib/wpiutil/src/main/native/windows/StackWalker.cpp
index 6f0fbf2..b2bcbaa 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/windows/StackWalker.cpp
+++ b/third_party/allwpilib/wpiutil/src/main/native/windows/StackWalker.cpp
@@ -1,4 +1,4 @@
-/**********************************************************************
+/**********************************************************************
  *
  * StackWalker.cpp
  * https://github.com/JochenKalmbach/StackWalker
@@ -87,76 +87,209 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <tchar.h>
+#include <windows.h>
+#include <new>
+
 #pragma comment(lib, "version.lib") // for "VerQueryValue"
 #pragma comment(lib, "Advapi32.lib") // for "GetUserName"
+
 #pragma warning(disable : 4826)
-
-#ifdef UNICODE
-  #define DBGHELP_TRANSLATE_TCHAR
-
+#if _MSC_VER >= 1900
+#pragma warning(disable : 4091)   // For fix unnamed enums from DbgHelp.h
 #endif
+
+
+// If VC7 and later, then use the shipped 'dbghelp.h'-file
 #pragma pack(push, 8)
+#if _MSC_VER >= 1300
 #include <dbghelp.h>
+#else
+// inline the important dbghelp.h-declarations...
+typedef enum
+{
+  SymNone = 0,
+  SymCoff,
+  SymCv,
+  SymPdb,
+  SymExport,
+  SymDeferred,
+  SymSym,
+  SymDia,
+  SymVirtual,
+  NumSymTypes
+} SYM_TYPE;
+typedef struct _IMAGEHLP_LINE64
+{
+  DWORD   SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
+  PVOID   Key;          // internal
+  DWORD   LineNumber;   // line number in file
+  PCHAR   FileName;     // full filename
+  DWORD64 Address;      // first instruction of line
+} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
+typedef struct _IMAGEHLP_MODULE64
+{
+  DWORD    SizeOfStruct;         // set to sizeof(IMAGEHLP_MODULE64)
+  DWORD64  BaseOfImage;          // base load address of module
+  DWORD    ImageSize;            // virtual size of the loaded module
+  DWORD    TimeDateStamp;        // date/time stamp from pe header
+  DWORD    CheckSum;             // checksum from the pe header
+  DWORD    NumSyms;              // number of symbols in the symbol table
+  SYM_TYPE SymType;              // type of symbols loaded
+  CHAR     ModuleName[32];       // module name
+  CHAR     ImageName[256];       // image name
+  CHAR     LoadedImageName[256]; // symbol file name
+} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
+typedef struct _IMAGEHLP_SYMBOL64
+{
+  DWORD   SizeOfStruct;  // set to sizeof(IMAGEHLP_SYMBOL64)
+  DWORD64 Address;       // virtual address including dll base address
+  DWORD   Size;          // estimated size of symbol, can be zero
+  DWORD   Flags;         // info about the symbols, see the SYMF defines
+  DWORD   MaxNameLength; // maximum size of symbol name in 'Name'
+  CHAR    Name[1];       // symbol name (null terminated string)
+} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
+typedef enum
+{
+  AddrMode1616,
+  AddrMode1632,
+  AddrModeReal,
+  AddrModeFlat
+} ADDRESS_MODE;
+typedef struct _tagADDRESS64
+{
+  DWORD64      Offset;
+  WORD         Segment;
+  ADDRESS_MODE Mode;
+} ADDRESS64, *LPADDRESS64;
+typedef struct _KDHELP64
+{
+  DWORD64 Thread;
+  DWORD   ThCallbackStack;
+  DWORD   ThCallbackBStore;
+  DWORD   NextCallback;
+  DWORD   FramePointer;
+  DWORD64 KiCallUserMode;
+  DWORD64 KeUserCallbackDispatcher;
+  DWORD64 SystemRangeStart;
+  DWORD64 Reserved[8];
+} KDHELP64, *PKDHELP64;
+typedef struct _tagSTACKFRAME64
+{
+  ADDRESS64 AddrPC;         // program counter
+  ADDRESS64 AddrReturn;     // return address
+  ADDRESS64 AddrFrame;      // frame pointer
+  ADDRESS64 AddrStack;      // stack pointer
+  ADDRESS64 AddrBStore;     // backing store pointer
+  PVOID     FuncTableEntry; // pointer to pdata/fpo or NULL
+  DWORD64   Params[4];      // possible arguments to the function
+  BOOL      Far;            // WOW far call
+  BOOL      Virtual;        // is this a virtual frame?
+  DWORD64   Reserved[3];
+  KDHELP64  KdHelp;
+} STACKFRAME64, *LPSTACKFRAME64;
+typedef BOOL(__stdcall* PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE  hProcess,
+                                                        DWORD64 qwBaseAddress,
+                                                        PVOID   lpBuffer,
+                                                        DWORD   nSize,
+                                                        LPDWORD lpNumberOfBytesRead);
+typedef PVOID(__stdcall* PFUNCTION_TABLE_ACCESS_ROUTINE64)(HANDLE hProcess, DWORD64 AddrBase);
+typedef DWORD64(__stdcall* PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, DWORD64 Address);
+typedef DWORD64(__stdcall* PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE      hProcess,
+                                                         HANDLE      hThread,
+                                                         LPADDRESS64 lpaddr);
+
+// clang-format off
+#define SYMOPT_CASE_INSENSITIVE         0x00000001
+#define SYMOPT_UNDNAME                  0x00000002
+#define SYMOPT_DEFERRED_LOADS           0x00000004
+#define SYMOPT_NO_CPP                   0x00000008
+#define SYMOPT_LOAD_LINES               0x00000010
+#define SYMOPT_OMAP_FIND_NEAREST        0x00000020
+#define SYMOPT_LOAD_ANYTHING            0x00000040
+#define SYMOPT_IGNORE_CVREC             0x00000080
+#define SYMOPT_NO_UNQUALIFIED_LOADS     0x00000100
+#define SYMOPT_FAIL_CRITICAL_ERRORS     0x00000200
+#define SYMOPT_EXACT_SYMBOLS            0x00000400
+#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS   0x00000800
+#define SYMOPT_IGNORE_NT_SYMPATH        0x00001000
+#define SYMOPT_INCLUDE_32BIT_MODULES    0x00002000
+#define SYMOPT_PUBLICS_ONLY             0x00004000
+#define SYMOPT_NO_PUBLICS               0x00008000
+#define SYMOPT_AUTO_PUBLICS             0x00010000
+#define SYMOPT_NO_IMAGE_SEARCH          0x00020000
+#define SYMOPT_SECURE                   0x00040000
+#define SYMOPT_DEBUG                    0x80000000
+#define UNDNAME_COMPLETE                 (0x0000) // Enable full undecoration
+#define UNDNAME_NAME_ONLY                (0x1000) // Crack only the name for primary declaration;
+// clang-format on
+
+#endif // _MSC_VER < 1300
 #pragma pack(pop)
 
+// Some missing defines (for VC5/6):
+#ifndef INVALID_FILE_ATTRIBUTES
+#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
 
-static void MyStrCpy(TCHAR* szDest, size_t nMaxDestSize, const TCHAR* szSrc)
+// secure-CRT_functions are only available starting with VC8
+#if _MSC_VER < 1400
+#define strcpy_s(dst, len, src) strcpy(dst, src)
+#define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src)
+#define strcat_s(dst, len, src) strcat(dst, src)
+#define _snprintf_s _snprintf
+#define _tcscat_s _tcscat
+#endif
+
+static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc)
 {
   if (nMaxDestSize <= 0)
     return;
-  _tcsncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE);
+  strncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE);
   // INFO: _TRUNCATE will ensure that it is null-terminated;
   // but with older compilers (<1400) it uses "strncpy" and this does not!)
   szDest[nMaxDestSize - 1] = 0;
 } // MyStrCpy
 
-#ifdef _UNICODE
-  typedef SYMBOL_INFOW   tSymbolInfo;
-  typedef IMAGEHLP_LINEW64  tImageHelperLine;
-#else
-  typedef SYMBOL_INFO   tSymbolInfo;
-  typedef IMAGEHLP_LINE64  tImageHelperLine;
-#endif
-
 // Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
 #define USED_CONTEXT_FLAGS CONTEXT_FULL
 
 class StackWalkerInternal
 {
 public:
-  StackWalkerInternal(StackWalker* parent, HANDLE hProcess)
+  StackWalkerInternal(StackWalker* parent, HANDLE hProcess, PCONTEXT ctx)
   {
     m_parent = parent;
     m_hDbhHelp = NULL;
-    symCleanup = NULL;
+    pSC = NULL;
     m_hProcess = hProcess;
-    m_szSymPath = NULL;
-    symFunctionTableAccess64 = NULL;
-    symGetLineFromAddr64 = NULL;
-    symGetModuleBase64 = NULL;
-    symGetModuleInfo64 = NULL;
-    symGetOptions = NULL;
-    symFromAddr = NULL;
-    symInitialize = NULL;
-    symLoadModuleEx = NULL;
-    symSetOptions = NULL;
-    stackWalk64 = NULL;
-    unDecorateSymbolName = NULL;
-    symGetSearchPath = NULL;
+    pSFTA = NULL;
+    pSGLFA = NULL;
+    pSGMB = NULL;
+    pSGMI = NULL;
+    pSGO = NULL;
+    pSGSFA = NULL;
+    pSI = NULL;
+    pSLM = NULL;
+    pSSO = NULL;
+    pSW = NULL;
+    pUDSN = NULL;
+    pSGSP = NULL;
+    m_ctx.ContextFlags = 0;
+    if (ctx != NULL)
+      m_ctx = *ctx;
   }
+
   ~StackWalkerInternal()
   {
-    if (symCleanup != NULL)
-      symCleanup(m_hProcess); // SymCleanup
+    if (pSC != NULL)
+      pSC(m_hProcess); // SymCleanup
     if (m_hDbhHelp != NULL)
       FreeLibrary(m_hDbhHelp);
     m_hDbhHelp = NULL;
     m_parent = NULL;
-    if (m_szSymPath != NULL)
-      free(m_szSymPath);
-    m_szSymPath = NULL;
   }
-  BOOL Init(LPCTSTR szSymPath)
+
+  BOOL Init(LPCSTR szSymPath)
   {
     if (m_parent == NULL)
       return FALSE;
@@ -229,72 +362,52 @@
       m_hDbhHelp = LoadLibrary(_T("dbghelp.dll"));
     if (m_hDbhHelp == NULL)
       return FALSE;
+    pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize");
+    pSC = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup");
 
-#ifdef _UNICODE
-    static const char strSymInitialize[] = "SymInitializeW";
-    static const char strUnDecorateSymbolName[] = "UnDecorateSymbolNameW";
-    static const char strSymGetSearchPath[] = "SymGetSearchPathW";
-    static const char strSymLoadModuleEx[] = "SymLoadModuleExW";
-    static const char strSymGetLineFromAddr64[] = "SymGetLineFromAddrW64";
-    static const char strSymGetModuleInfo64[] = "SymGetModuleInfoW64";
-    static const char strSymFromAddr[] = "SymFromAddrW";
-#else
-    static const char strSymInitialize[] = "SymInitialize";
-    static const char strUnDecorateSymbolName[] = "UnDecorateSymbolName";
-    static const char strSymGetSearchPath[] = "SymGetSearchPath";
-    static const char strSymLoadModuleEx[] = "SymLoadModuleEx";
-    static const char strSymGetLineFromAddr64[] = "SymGetLineFromAddr64";
-    static const char strSymGetModuleInfo64[] = "SymGetModuleInfo64";
-    static const char strSymFromAddr[] = "SymFromAddr";
-#endif
-    symInitialize = (tSI)GetProcAddress(m_hDbhHelp, strSymInitialize);
-    symCleanup = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup");
+    pSW = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64");
+    pSGO = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions");
+    pSSO = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions");
 
-    stackWalk64 = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64");
-    symGetOptions = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions");
-    symSetOptions = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions");
+    pSFTA = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64");
+    pSGLFA = (tSGLFA)GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64");
+    pSGMB = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64");
+    pSGMI = (tSGMI)GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64");
+    pSGSFA = (tSGSFA)GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64");
+    pUDSN = (tUDSN)GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName");
+    pSLM = (tSLM)GetProcAddress(m_hDbhHelp, "SymLoadModule64");
+    pSGSP = (tSGSP)GetProcAddress(m_hDbhHelp, "SymGetSearchPath");
 
-    symFunctionTableAccess64 = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64");
-    symGetLineFromAddr64 = (tSGLFA)GetProcAddress(m_hDbhHelp, strSymGetLineFromAddr64);
-    symGetModuleBase64 = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64");
-    symGetModuleInfo64 = (tSGMI)GetProcAddress(m_hDbhHelp, strSymGetModuleInfo64);
-    symFromAddr = (tSFA)GetProcAddress(m_hDbhHelp, strSymFromAddr);
-    unDecorateSymbolName = (tUDSN)GetProcAddress(m_hDbhHelp, strUnDecorateSymbolName);
-    symLoadModuleEx = (tSLM)GetProcAddress(m_hDbhHelp, strSymLoadModuleEx);
-    symGetSearchPath = (tSGSP)GetProcAddress(m_hDbhHelp, strSymGetSearchPath);
-
-    if (symCleanup == NULL || symFunctionTableAccess64 == NULL || symGetModuleBase64 == NULL || symGetModuleInfo64 == NULL || symGetOptions == NULL ||
-        symFromAddr == NULL || symInitialize == NULL || symSetOptions == NULL || stackWalk64 == NULL || unDecorateSymbolName == NULL ||
-        symLoadModuleEx == NULL)
+    if (pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || pSGO == NULL ||
+        pSGSFA == NULL || pSI == NULL || pSSO == NULL || pSW == NULL || pUDSN == NULL ||
+        pSLM == NULL)
     {
       FreeLibrary(m_hDbhHelp);
       m_hDbhHelp = NULL;
-      symCleanup = NULL;
+      pSC = NULL;
       return FALSE;
     }
 
     // SymInitialize
-    if (szSymPath != NULL)
-      m_szSymPath = _tcsdup(szSymPath);
-    if (this->symInitialize(m_hProcess, m_szSymPath, FALSE) == FALSE)
-      this->m_parent->OnDbgHelpErr(_T("SymInitialize"), GetLastError(), 0);
+    if (this->pSI(m_hProcess, szSymPath, FALSE) == FALSE)
+      this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
 
-    DWORD symOptions = this->symGetOptions(); // SymGetOptions
+    DWORD symOptions = this->pSGO(); // SymGetOptions
     symOptions |= SYMOPT_LOAD_LINES;
     symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
     //symOptions |= SYMOPT_NO_PROMPTS;
     // SymSetOptions
-    symOptions = this->symSetOptions(symOptions);
+    symOptions = this->pSSO(symOptions);
 
-    TCHAR buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
-    if (this->symGetSearchPath != NULL)
+    char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
+    if (this->pSGSP != NULL)
     {
-      if (this->symGetSearchPath(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE)
-        this->m_parent->OnDbgHelpErr(_T("SymGetSearchPath"), GetLastError(), 0);
+      if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE)
+        this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
     }
-    TCHAR  szUserName[1024] = {0};
+    char  szUserName[1024] = {0};
     DWORD dwSize = 1024;
-    GetUserName(szUserName, &dwSize);
+    GetUserNameA(szUserName, &dwSize);
     this->m_parent->OnSymInit(buf, symOptions, szUserName);
 
     return TRUE;
@@ -302,41 +415,12 @@
 
   StackWalker* m_parent;
 
+  CONTEXT m_ctx;
   HMODULE m_hDbhHelp;
   HANDLE  m_hProcess;
-  LPTSTR   m_szSymPath;
 
 #pragma pack(push, 8)
-  typedef struct IMAGEHLP_MODULE64_V3
-  {
-    DWORD    SizeOfStruct;         // set to sizeof(IMAGEHLP_MODULE64)
-    DWORD64  BaseOfImage;          // base load address of module
-    DWORD    ImageSize;            // virtual size of the loaded module
-    DWORD    TimeDateStamp;        // date/time stamp from pe header
-    DWORD    CheckSum;             // checksum from the pe header
-    DWORD    NumSyms;              // number of symbols in the symbol table
-    SYM_TYPE SymType;              // type of symbols loaded
-    TCHAR     ModuleName[32];       // module name
-    TCHAR     ImageName[256];       // image name
-    TCHAR     LoadedImageName[256]; // symbol file name
-    // new elements: 07-Jun-2002
-    TCHAR  LoadedPdbName[256];   // pdb file name
-    DWORD CVSig;                // Signature of the CV record in the debug directories
-    TCHAR  CVData[MAX_PATH * 3]; // Contents of the CV record
-    DWORD PdbSig;               // Signature of PDB
-    GUID  PdbSig70;             // Signature of PDB (VC 7 and up)
-    DWORD PdbAge;               // DBI age of pdb
-    BOOL  PdbUnmatched;         // loaded an unmatched pdb
-    BOOL  DbgUnmatched;         // loaded an unmatched dbg
-    BOOL  LineNumbers;          // we have line number information
-    BOOL  GlobalSymbols;        // we have internal symbol information
-    BOOL  TypeInfo;             // we have type information
-    // new elements: 17-Dec-2003
-    BOOL SourceIndexed; // pdb supports source server
-    BOOL Publics;       // contains public symbols
-  };
-
-  typedef struct IMAGEHLP_MODULE64_V2
+  typedef struct _IMAGEHLP_MODULE64_V3
   {
     DWORD    SizeOfStruct;         // set to sizeof(IMAGEHLP_MODULE64)
     DWORD64  BaseOfImage;          // base load address of module
@@ -348,64 +432,90 @@
     CHAR     ModuleName[32];       // module name
     CHAR     ImageName[256];       // image name
     CHAR     LoadedImageName[256]; // symbol file name
-  };
+    // new elements: 07-Jun-2002
+    CHAR  LoadedPdbName[256];   // pdb file name
+    DWORD CVSig;                // Signature of the CV record in the debug directories
+    CHAR  CVData[MAX_PATH * 3]; // Contents of the CV record
+    DWORD PdbSig;               // Signature of PDB
+    GUID  PdbSig70;             // Signature of PDB (VC 7 and up)
+    DWORD PdbAge;               // DBI age of pdb
+    BOOL  PdbUnmatched;         // loaded an unmatched pdb
+    BOOL  DbgUnmatched;         // loaded an unmatched dbg
+    BOOL  LineNumbers;          // we have line number information
+    BOOL  GlobalSymbols;        // we have internal symbol information
+    BOOL  TypeInfo;             // we have type information
+    // new elements: 17-Dec-2003
+    BOOL SourceIndexed; // pdb supports source server
+    BOOL Publics;       // contains public symbols
+  } IMAGEHLP_MODULE64_V3, *PIMAGEHLP_MODULE64_V3;
+
+  typedef struct _IMAGEHLP_MODULE64_V2
+  {
+    DWORD    SizeOfStruct;         // set to sizeof(IMAGEHLP_MODULE64)
+    DWORD64  BaseOfImage;          // base load address of module
+    DWORD    ImageSize;            // virtual size of the loaded module
+    DWORD    TimeDateStamp;        // date/time stamp from pe header
+    DWORD    CheckSum;             // checksum from the pe header
+    DWORD    NumSyms;              // number of symbols in the symbol table
+    SYM_TYPE SymType;              // type of symbols loaded
+    CHAR     ModuleName[32];       // module name
+    CHAR     ImageName[256];       // image name
+    CHAR     LoadedImageName[256]; // symbol file name
+  } IMAGEHLP_MODULE64_V2, *PIMAGEHLP_MODULE64_V2;
 #pragma pack(pop)
 
   // SymCleanup()
   typedef BOOL(__stdcall* tSC)(IN HANDLE hProcess);
-  tSC symCleanup;
+  tSC pSC;
 
   // SymFunctionTableAccess64()
   typedef PVOID(__stdcall* tSFTA)(HANDLE hProcess, DWORD64 AddrBase);
-  tSFTA symFunctionTableAccess64;
+  tSFTA pSFTA;
 
   // SymGetLineFromAddr64()
   typedef BOOL(__stdcall* tSGLFA)(IN HANDLE hProcess,
                                   IN DWORD64 dwAddr,
                                   OUT PDWORD pdwDisplacement,
-                                  OUT tImageHelperLine* Line);
-  tSGLFA symGetLineFromAddr64;
+                                  OUT PIMAGEHLP_LINE64 Line);
+  tSGLFA pSGLFA;
 
   // SymGetModuleBase64()
   typedef DWORD64(__stdcall* tSGMB)(IN HANDLE hProcess, IN DWORD64 dwAddr);
-  tSGMB symGetModuleBase64;
+  tSGMB pSGMB;
 
   // SymGetModuleInfo64()
   typedef BOOL(__stdcall* tSGMI)(IN HANDLE hProcess,
                                  IN DWORD64 dwAddr,
                                  OUT IMAGEHLP_MODULE64_V3* ModuleInfo);
-  tSGMI symGetModuleInfo64;
+  tSGMI pSGMI;
 
   // SymGetOptions()
   typedef DWORD(__stdcall* tSGO)(VOID);
-  tSGO symGetOptions;
-
+  tSGO pSGO;
 
   // SymGetSymFromAddr64()
-  typedef BOOL(__stdcall* tSFA)(IN HANDLE hProcess,
-                                  IN DWORD64 Address,
+  typedef BOOL(__stdcall* tSGSFA)(IN HANDLE hProcess,
+                                  IN DWORD64 dwAddr,
                                   OUT PDWORD64 pdwDisplacement,
-                                  OUT tSymbolInfo* Symbol);
-  tSFA symFromAddr;
+                                  OUT PIMAGEHLP_SYMBOL64 Symbol);
+  tSGSFA pSGSFA;
 
   // SymInitialize()
-  typedef BOOL(__stdcall* tSI)(IN HANDLE hProcess, IN PTSTR UserSearchPath, IN BOOL fInvadeProcess);
-  tSI symInitialize;
+  typedef BOOL(__stdcall* tSI)(IN HANDLE hProcess, IN LPCSTR UserSearchPath, IN BOOL fInvadeProcess);
+  tSI pSI;
 
   // SymLoadModule64()
   typedef DWORD64(__stdcall* tSLM)(IN HANDLE hProcess,
                                    IN HANDLE hFile,
-                                   IN PTSTR ImageName,
-                                   IN PTSTR ModuleName,
+                                   IN LPCSTR ImageName,
+                                   IN LPCSTR ModuleName,
                                    IN DWORD64 BaseOfDll,
-                                   IN DWORD SizeOfDll,
-                                   IN PMODLOAD_DATA Data,
-                                   IN DWORD         Flags);
-  tSLM symLoadModuleEx;
+                                   IN DWORD SizeOfDll);
+  tSLM pSLM;
 
   // SymSetOptions()
   typedef DWORD(__stdcall* tSSO)(IN DWORD SymOptions);
-  tSSO symSetOptions;
+  tSSO pSSO;
 
   // StackWalk64()
   typedef BOOL(__stdcall* tSW)(DWORD                            MachineType,
@@ -417,17 +527,17 @@
                                PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
                                PGET_MODULE_BASE_ROUTINE64       GetModuleBaseRoutine,
                                PTRANSLATE_ADDRESS_ROUTINE64     TranslateAddress);
-  tSW stackWalk64;
+  tSW pSW;
 
   // UnDecorateSymbolName()
-  typedef DWORD(__stdcall WINAPI* tUDSN)(PCTSTR DecoratedName,
-                                         PTSTR  UnDecoratedName,
+  typedef DWORD(__stdcall WINAPI* tUDSN)(PCSTR DecoratedName,
+                                         PSTR  UnDecoratedName,
                                          DWORD UndecoratedLength,
                                          DWORD Flags);
-  tUDSN unDecorateSymbolName;
+  tUDSN pUDSN;
 
-  typedef BOOL(__stdcall WINAPI* tSGSP)(HANDLE hProcess, PTSTR SearchPath, DWORD SearchPathLength);
-  tSGSP symGetSearchPath;
+  typedef BOOL(__stdcall WINAPI* tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);
+  tSGSP pSGSP;
 
 private:
 // **************************************** ToolHelp32 ************************
@@ -444,8 +554,8 @@
     BYTE*   modBaseAddr;   // Base address of module in th32ProcessID's context
     DWORD   modBaseSize;   // Size in bytes of module starting at modBaseAddr
     HMODULE hModule;       // The hModule of this module in th32ProcessID's context
-    TCHAR   szModule[MAX_MODULE_NAME32 + 1];
-    TCHAR   szExePath[MAX_PATH];
+    char    szModule[MAX_MODULE_NAME32 + 1];
+    char    szExePath[MAX_PATH];
   } MODULEENTRY32;
   typedef MODULEENTRY32* PMODULEENTRY32;
   typedef MODULEENTRY32* LPMODULEENTRY32;
@@ -463,31 +573,25 @@
     // try both dlls...
     const TCHAR* dllname[] = {_T("kernel32.dll"), _T("tlhelp32.dll")};
     HINSTANCE    hToolhelp = NULL;
-    tCT32S       createToolhelp32Snapshot = NULL;
-    tM32F        module32First = NULL;
-    tM32N        module32Next = NULL;
+    tCT32S       pCT32S = NULL;
+    tM32F        pM32F = NULL;
+    tM32N        pM32N = NULL;
 
     HANDLE        hSnap;
-    MODULEENTRY32 moduleEntry32;
-    moduleEntry32.dwSize = sizeof(moduleEntry32);
+    MODULEENTRY32 me;
+    me.dwSize = sizeof(me);
     BOOL   keepGoing;
+    size_t i;
 
-#ifdef _UNICODE
-    static const char strModule32First[] = "Module32FirstW";
-    static const char strModule32Next[] = "Module32NextW";
- #else
-    static const char strModule32First[] = "Module32First";
-    static const char strModule32Next[] = "Module32Next";
-#endif
-    for (size_t i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++)
+    for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++)
     {
       hToolhelp = LoadLibrary(dllname[i]);
       if (hToolhelp == NULL)
         continue;
-      createToolhelp32Snapshot = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
-      module32First = (tM32F)GetProcAddress(hToolhelp, strModule32First);  
-      module32Next = (tM32N)GetProcAddress(hToolhelp, strModule32Next); 
-      if ((createToolhelp32Snapshot != NULL) && (module32First != NULL) && (module32Next != NULL))
+      pCT32S = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
+      pM32F = (tM32F)GetProcAddress(hToolhelp, "Module32First");
+      pM32N = (tM32N)GetProcAddress(hToolhelp, "Module32Next");
+      if ((pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL))
         break; // found the functions!
       FreeLibrary(hToolhelp);
       hToolhelp = NULL;
@@ -496,21 +600,21 @@
     if (hToolhelp == NULL)
       return FALSE;
 
-    hSnap = createToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
+    hSnap = pCT32S(TH32CS_SNAPMODULE, pid);
     if (hSnap == (HANDLE)-1)
     {
       FreeLibrary(hToolhelp);
       return FALSE;
     }
 
-    keepGoing = !!module32First(hSnap, &moduleEntry32);
+    keepGoing = !!pM32F(hSnap, &me);
     int cnt = 0;
     while (keepGoing)
     {
-      this->LoadModule(hProcess, moduleEntry32.szExePath, moduleEntry32.szModule, (DWORD64)moduleEntry32.modBaseAddr,
-                       moduleEntry32.modBaseSize);
+      this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr,
+                       me.modBaseSize);
       cnt++;
-      keepGoing = !!module32Next(hSnap, &moduleEntry32);
+      keepGoing = !!pM32N(hSnap, &me);
     }
     CloseHandle(hSnap);
     FreeLibrary(hToolhelp);
@@ -533,41 +637,39 @@
     typedef BOOL(__stdcall * tEPM)(HANDLE hProcess, HMODULE * lphModule, DWORD cb,
                                    LPDWORD lpcbNeeded);
     // GetModuleFileNameEx()
-    typedef DWORD(__stdcall * tGMFNE)(HANDLE hProcess, HMODULE hModule, LPTSTR lpFilename,
+    typedef DWORD(__stdcall * tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename,
                                       DWORD nSize);
     // GetModuleBaseName()
-    typedef DWORD(__stdcall * tGMBN)(HANDLE hProcess, HMODULE hModule, LPTSTR lpFilename,
+    typedef DWORD(__stdcall * tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename,
                                      DWORD nSize);
     // GetModuleInformation()
     typedef BOOL(__stdcall * tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize);
 
-      //ModuleEntry e;
+    HINSTANCE hPsapi;
+    tEPM      pEPM;
+    tGMFNE    pGMFNE;
+    tGMBN     pGMBN;
+    tGMI      pGMI;
+
+    DWORD i;
+    //ModuleEntry e;
     DWORD        cbNeeded;
     MODULEINFO   mi;
-    HMODULE*     hMods = 0;
-    TCHAR*        tt = NULL;
-    TCHAR*        tt2 = NULL;
+    HMODULE*     hMods = NULL;
+    char*        tt = NULL;
+    char*        tt2 = NULL;
     const SIZE_T TTBUFLEN = 8096;
     int          cnt = 0;
 
-    HINSTANCE hPsapi = LoadLibrary(_T("psapi.dll"));
+    hPsapi = LoadLibrary(_T("psapi.dll"));
     if (hPsapi == NULL)
       return FALSE;
 
-#ifdef _UNICODE
-    static const char strGetModuleFileName[] = "GetModuleFileNameExW";
-    static const char strGetModuleBaseName[] = "GetModuleBaseNameW";
-#else
-    static const char strGetModuleFileName[] = "GetModulefileNameExA";
-    static const char strGetModuleBaseName[] = "GetModuleBaseNameA";
-#endif
-
-    tEPM   enumProcessModules = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules");
-    tGMFNE getModuleFileNameEx = (tGMFNE)GetProcAddress(hPsapi, strGetModuleFileName);
-    tGMBN  getModuleBaseName = (tGMFNE)GetProcAddress(hPsapi, strGetModuleBaseName);
-    tGMI   getModuleInformation = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation");
-    if ((enumProcessModules == NULL) || (getModuleFileNameEx == NULL) ||
-        (getModuleBaseName == NULL) || (getModuleInformation == NULL))
+    pEPM = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules");
+    pGMFNE = (tGMFNE)GetProcAddress(hPsapi, "GetModuleFileNameExA");
+    pGMBN = (tGMFNE)GetProcAddress(hPsapi, "GetModuleBaseNameA");
+    pGMI = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation");
+    if ((pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL))
     {
       // we couldn't find all functions
       FreeLibrary(hPsapi);
@@ -575,12 +677,12 @@
     }
 
     hMods = (HMODULE*)malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof(HMODULE)));
-    tt = (TCHAR*)malloc(sizeof(TCHAR) * TTBUFLEN);
-    tt2 = (TCHAR*)malloc(sizeof(TCHAR) * TTBUFLEN);
+    tt = (char*)malloc(sizeof(char) * TTBUFLEN);
+    tt2 = (char*)malloc(sizeof(char) * TTBUFLEN);
     if ((hMods == NULL) || (tt == NULL) || (tt2 == NULL))
       goto cleanup;
 
-    if (!enumProcessModules(hProcess, hMods, TTBUFLEN, &cbNeeded))
+    if (!pEPM(hProcess, hMods, TTBUFLEN, &cbNeeded))
     {
       //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
       goto cleanup;
@@ -592,20 +694,20 @@
       goto cleanup;
     }
 
-    for (DWORD i = 0; i < cbNeeded / sizeof(hMods[0]); i++)
+    for (i = 0; i < cbNeeded / sizeof(hMods[0]); i++)
     {
       // base address, size
-      getModuleInformation(hProcess, hMods[i], &mi, sizeof(mi));
+      pGMI(hProcess, hMods[i], &mi, sizeof(mi));
       // image file name
       tt[0] = 0;
-      getModuleFileNameEx(hProcess, hMods[i], tt, TTBUFLEN);
+      pGMFNE(hProcess, hMods[i], tt, TTBUFLEN);
       // module name
       tt2[0] = 0;
-      getModuleBaseName(hProcess, hMods[i], tt2, TTBUFLEN);
+      pGMBN(hProcess, hMods[i], tt2, TTBUFLEN);
 
       DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64)mi.lpBaseOfDll, mi.SizeOfImage);
       if (dwRes != ERROR_SUCCESS)
-        this->m_parent->OnDbgHelpErr(_T("LoadModule"), dwRes, 0);
+        this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);
       cnt++;
     }
 
@@ -622,16 +724,16 @@
     return cnt != 0;
   } // GetModuleListPSAPI
 
-  DWORD LoadModule(HANDLE hProcess, LPCTSTR img, LPCTSTR mod, DWORD64 baseAddr, DWORD size)
+  DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)
   {
-    TCHAR* szImg = _tcsdup(img);
-    TCHAR* szMod = _tcsdup(mod);
+    CHAR* szImg = _strdup(img);
+    CHAR* szMod = _strdup(mod);
     DWORD result = ERROR_SUCCESS;
     if ((szImg == NULL) || (szMod == NULL))
       result = ERROR_NOT_ENOUGH_MEMORY;
     else
     {
-      if (symLoadModuleEx(hProcess, 0, szImg, szMod, baseAddr, size, 0, 0) == 0)
+      if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
         result = GetLastError();
     }
     ULONGLONG fileVersion = 0;
@@ -642,13 +744,13 @@
       {
         VS_FIXEDFILEINFO* fInfo = NULL;
         DWORD             dwHandle;
-        DWORD             dwSize = GetFileVersionInfoSize(szImg, &dwHandle);
+        DWORD             dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
         if (dwSize > 0)
         {
           LPVOID vData = malloc(dwSize);
           if (vData != NULL)
           {
-            if (GetFileVersionInfo(szImg, dwHandle, dwSize, vData) != 0)
+            if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)
             {
               UINT  len;
               TCHAR szSubBlock[] = _T("\\");
@@ -667,41 +769,41 @@
 
       // Retrieve some additional-infos about the module
       IMAGEHLP_MODULE64_V3 Module;
-      const TCHAR*          szSymType = _T("-unknown-");
+      const char*          szSymType = "-unknown-";
       if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)
       {
         switch (Module.SymType)
         {
           case SymNone:
-            szSymType = _T("-nosymbols-");
+            szSymType = "-nosymbols-";
             break;
           case SymCoff: // 1
-            szSymType = _T("COFF");
+            szSymType = "COFF";
             break;
           case SymCv: // 2
-            szSymType = _T("CV");
+            szSymType = "CV";
             break;
           case SymPdb: // 3
-            szSymType = _T("PDB");
+            szSymType = "PDB";
             break;
           case SymExport: // 4
-            szSymType = _T("-exported-");
+            szSymType = "-exported-";
             break;
           case SymDeferred: // 5
-            szSymType = _T("-deferred-");
+            szSymType = "-deferred-";
             break;
           case SymSym: // 6
-            szSymType = _T("SYM");
+            szSymType = "SYM";
             break;
           case 7: // SymDia:
-            szSymType = _T("DIA");
+            szSymType = "DIA";
             break;
           case 8: //SymVirtual:
-            szSymType = _T("Virtual");
+            szSymType = "Virtual";
             break;
         }
       }
-      LPCTSTR pdbName = Module.LoadedImageName;
+      LPCSTR pdbName = Module.LoadedImageName;
       if (Module.LoadedPdbName[0] != 0)
         pdbName = Module.LoadedPdbName;
       this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, pdbName,
@@ -727,7 +829,7 @@
   BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3* pModuleInfo)
   {
     memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3));
-    if (this->symGetModuleInfo64 == NULL)
+    if (this->pSGMI == NULL)
     {
       SetLastError(ERROR_DLL_INIT_FAILED);
       return FALSE;
@@ -745,7 +847,7 @@
     static bool s_useV3Version = true;
     if (s_useV3Version)
     {
-      if (this->symGetModuleInfo64(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE)
+      if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE)
       {
         // only copy as much memory as is reserved...
         memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V3));
@@ -759,7 +861,7 @@
     // could not retrieve the bigger structure, try with the smaller one (as defined in VC7.1)...
     pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
     memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));
-    if (this->symGetModuleInfo64(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE)
+    if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE)
     {
       // only copy as much memory as is reserved...
       memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));
@@ -774,41 +876,102 @@
 };
 
 // #############################################################
-StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess)
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400 && _MSC_VER < 1900
+extern "C" void* __cdecl _getptd();
+#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1900
+extern "C" void** __cdecl __current_exception_context();
+#endif
+
+static PCONTEXT get_current_exception_context()
 {
-  this->m_options = OptionsAll;
-  this->m_modulesLoaded = FALSE;
-  this->m_hProcess = hProcess;
-  this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
-  this->m_dwProcessId = dwProcessId;
-  this->m_szSymPath = NULL;
-  this->m_MaxRecursionCount = 1000;
+  PCONTEXT * pctx = NULL;
+#if defined(_MSC_VER) && _MSC_VER >= 1400 && _MSC_VER < 1900  
+  LPSTR ptd = (LPSTR)_getptd();
+  if (ptd)
+    pctx = (PCONTEXT *)(ptd + (sizeof(void*) == 4 ? 0x8C : 0xF8));
+#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1900
+  pctx = (PCONTEXT *)__current_exception_context();
+#endif
+  return pctx ? *pctx : NULL;
 }
-StackWalker::StackWalker(int options, LPCTSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)
+
+bool StackWalker::Init(ExceptType extype, int options, LPCSTR szSymPath, DWORD dwProcessId,
+                       HANDLE hProcess, PEXCEPTION_POINTERS exp)
 {
+  PCONTEXT ctx = NULL;
+  if (extype == AfterCatch)
+    ctx = get_current_exception_context();
+  if (extype == AfterExcept && exp)
+    ctx = exp->ContextRecord;
   this->m_options = options;
   this->m_modulesLoaded = FALSE;
-  this->m_hProcess = hProcess;
-  this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
-  this->m_dwProcessId = dwProcessId;
-  if (szSymPath != NULL)
-  {
-    this->m_szSymPath = _tcsdup(szSymPath);
-    this->m_options |= SymBuildPath;
-  }
-  else
-    this->m_szSymPath = NULL;
+  this->m_szSymPath = NULL;
   this->m_MaxRecursionCount = 1000;
+  this->m_sw = NULL;
+  SetTargetProcess(dwProcessId, hProcess);
+  SetSymPath(szSymPath);
+  /* MSVC ignore std::nothrow specifier for `new` operator */
+  LPVOID buf = malloc(sizeof(StackWalkerInternal));
+  if (!buf)
+    return false;
+  memset(buf, 0, sizeof(StackWalkerInternal));
+  this->m_sw = new(buf) StackWalkerInternal(this, this->m_hProcess, ctx);  // placement new
+  return true;
+}
+
+StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess)
+{
+  Init(NonExcept, OptionsAll, NULL, dwProcessId, hProcess);
+}
+
+StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)
+{
+  Init(NonExcept, options, szSymPath, dwProcessId, hProcess);
+}
+
+StackWalker::StackWalker(ExceptType extype, int options, PEXCEPTION_POINTERS exp)
+{
+  Init(extype, options, NULL, GetCurrentProcessId(), GetCurrentProcess(), exp);
 }
 
 StackWalker::~StackWalker()
 {
-  if (m_szSymPath != NULL)
+  SetSymPath(NULL);
+  if (m_sw != NULL) {
+    m_sw->~StackWalkerInternal();  // call the object's destructor
+    free(m_sw);
+  }
+  m_sw = NULL;
+}
+
+bool StackWalker::SetSymPath(LPCSTR szSymPath)
+{
+  if (m_szSymPath)
     free(m_szSymPath);
   m_szSymPath = NULL;
-  if (this->m_sw != NULL)
-    delete this->m_sw;
-  this->m_sw = NULL;
+  if (szSymPath == NULL)
+    return true;
+  m_szSymPath = _strdup(szSymPath);
+  if (m_szSymPath)
+    m_options |= SymBuildPath;
+  return true;
+}
+
+bool StackWalker::SetTargetProcess(DWORD dwProcessId, HANDLE hProcess)
+{
+  m_dwProcessId = dwProcessId;
+  m_hProcess = hProcess;
+  if (m_sw)
+    m_sw->m_hProcess = hProcess;
+  return true;
+}
+
+PCONTEXT StackWalker::GetCurrentExceptionContext()
+{
+  return get_current_exception_context();
 }
 
 BOOL StackWalker::LoadModules()
@@ -822,11 +985,11 @@
     return TRUE;
 
   // Build the sym-path:
-  TCHAR* szSymPath = NULL;
+  char* szSymPath = NULL;
   if ((this->m_options & SymBuildPath) != 0)
   {
     const size_t nSymPathLen = 4096;
-    szSymPath = (TCHAR*)malloc(nSymPathLen * sizeof(TCHAR));
+    szSymPath = (char*)malloc(nSymPathLen);
     if (szSymPath == NULL)
     {
       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -836,27 +999,27 @@
     // Now first add the (optional) provided sympath:
     if (this->m_szSymPath != NULL)
     {
-      _tcscat_s(szSymPath, nSymPathLen, this->m_szSymPath);
-      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
+      strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);
+      strcat_s(szSymPath, nSymPathLen, ";");
     }
 
-    _tcscat_s(szSymPath, nSymPathLen, _T(".;"));
+    strcat_s(szSymPath, nSymPathLen, ".;");
 
     const size_t nTempLen = 1024;
-    TCHAR         szTemp[nTempLen];
+    char         szTemp[nTempLen];
     // Now add the current directory:
-    if (GetCurrentDirectory(nTempLen, szTemp) > 0)
+    if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
     {
       szTemp[nTempLen - 1] = 0;
-      _tcscat_s(szSymPath, nSymPathLen, szTemp);
-      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
+      strcat_s(szSymPath, nSymPathLen, szTemp);
+      strcat_s(szSymPath, nSymPathLen, ";");
     }
 
     // Now add the path for the main-module:
-    if (GetModuleFileName(NULL, szTemp, nTempLen) > 0)
+    if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)
     {
       szTemp[nTempLen - 1] = 0;
-      for (TCHAR* p = (szTemp + _tcslen(szTemp) - 1); p >= szTemp; --p)
+      for (char* p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p)
       {
         // locate the rightmost path separator
         if ((*p == '\\') || (*p == '/') || (*p == ':'))
@@ -865,48 +1028,48 @@
           break;
         }
       } // for (search for path separator...)
-      if (_tcslen(szTemp) > 0)
+      if (strlen(szTemp) > 0)
       {
-        _tcscat_s(szSymPath, nSymPathLen, szTemp);
-        _tcscat_s(szSymPath, nSymPathLen, _T(";"));
+        strcat_s(szSymPath, nSymPathLen, szTemp);
+        strcat_s(szSymPath, nSymPathLen, ";");
       }
     }
-    if (GetEnvironmentVariable(_T("_NT_SYMBOL_PATH"), szTemp, nTempLen) > 0)
+    if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
     {
       szTemp[nTempLen - 1] = 0;
-      _tcscat_s(szSymPath, nSymPathLen, szTemp);
-      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
+      strcat_s(szSymPath, nSymPathLen, szTemp);
+      strcat_s(szSymPath, nSymPathLen, ";");
     }
-    if (GetEnvironmentVariable(_T("_NT_ALTERNATE_SYMBOL_PATH"), szTemp, nTempLen) > 0)
+    if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
     {
       szTemp[nTempLen - 1] = 0;
-      _tcscat_s(szSymPath, nSymPathLen, szTemp);
-      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
+      strcat_s(szSymPath, nSymPathLen, szTemp);
+      strcat_s(szSymPath, nSymPathLen, ";");
     }
-    if (GetEnvironmentVariable(_T("SYSTEMROOT"), szTemp, nTempLen) > 0)
+    if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
     {
       szTemp[nTempLen - 1] = 0;
-      _tcscat_s(szSymPath, nSymPathLen, szTemp);
-      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
+      strcat_s(szSymPath, nSymPathLen, szTemp);
+      strcat_s(szSymPath, nSymPathLen, ";");
       // also add the "system32"-directory:
-      _tcscat_s(szTemp, nTempLen, _T("\\system32"));
-      _tcscat_s(szSymPath, nSymPathLen, szTemp);
-      _tcscat_s(szSymPath, nSymPathLen, _T(";"));
+      strcat_s(szTemp, nTempLen, "\\system32");
+      strcat_s(szSymPath, nSymPathLen, szTemp);
+      strcat_s(szSymPath, nSymPathLen, ";");
     }
 
     if ((this->m_options & SymUseSymSrv) != 0)
     {
-      if (GetEnvironmentVariable(_T("SYSTEMDRIVE"), szTemp, nTempLen) > 0)
+      if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)
       {
         szTemp[nTempLen - 1] = 0;
-        _tcscat_s(szSymPath, nSymPathLen, _T("SRV*"));
-        _tcscat_s(szSymPath, nSymPathLen, szTemp);
-        _tcscat_s(szSymPath, nSymPathLen, _T("\\websymbols"));
-        _tcscat_s(szSymPath, nSymPathLen, _T("*http://msdl.microsoft.com/download/symbols;"));
+        strcat_s(szSymPath, nSymPathLen, "SRV*");
+        strcat_s(szSymPath, nSymPathLen, szTemp);
+        strcat_s(szSymPath, nSymPathLen, "\\websymbols");
+        strcat_s(szSymPath, nSymPathLen, "*https://msdl.microsoft.com/download/symbols;");
       }
       else
-        _tcscat_s(szSymPath, nSymPathLen,
-                 _T("SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"));
+        strcat_s(szSymPath, nSymPathLen,
+                 "SRV*c:\\websymbols*https://msdl.microsoft.com/download/symbols;");
     }
   } // if SymBuildPath
 
@@ -917,7 +1080,7 @@
   szSymPath = NULL;
   if (bRet == FALSE)
   {
-    this->OnDbgHelpErr(_T("Error while initializing dbghelp.dll"), 0, 0);
+    this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
     SetLastError(ERROR_DLL_INIT_FAILED);
     return FALSE;
   }
@@ -942,10 +1105,9 @@
 {
   CONTEXT                                   c;
   CallstackEntry                            csEntry;
-
-  tSymbolInfo* pSym = NULL;
+  IMAGEHLP_SYMBOL64*                        pSym = NULL;
   StackWalkerInternal::IMAGEHLP_MODULE64_V3 Module;
-  tImageHelperLine                           Line;
+  IMAGEHLP_LINE64                           Line;
   int                                       frameNum;
   bool                                      bLastEntryCalled = true;
   int                                       curRecursionCount = 0;
@@ -973,7 +1135,10 @@
     if (GetThreadId(hThread) == GetCurrentThreadId())
 #endif
     {
-      GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, USED_CONTEXT_FLAGS);
+      if (m_sw->m_ctx.ContextFlags != 0)
+        c = m_sw->m_ctx;   // context taken at Init
+      else
+        GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, USED_CONTEXT_FLAGS);
     }
     else
     {
@@ -1026,16 +1191,24 @@
   s.AddrBStore.Mode = AddrModeFlat;
   s.AddrStack.Offset = c.IntSp;
   s.AddrStack.Mode = AddrModeFlat;
+#elif _M_ARM64
+  imageType = IMAGE_FILE_MACHINE_ARM64;
+  s.AddrPC.Offset = c.Pc;
+  s.AddrPC.Mode = AddrModeFlat;
+  s.AddrFrame.Offset = c.Fp;
+  s.AddrFrame.Mode = AddrModeFlat;
+  s.AddrStack.Offset = c.Sp;
+  s.AddrStack.Mode = AddrModeFlat;
 #else
 #error "Platform not supported!"
 #endif
 
-  pSym = (tSymbolInfo*)malloc(sizeof(tSymbolInfo) + STACKWALK_MAX_NAMELEN*sizeof(TCHAR));
+  pSym = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
   if (!pSym)
     goto cleanup; // not enough memory...
-  memset(pSym, 0, sizeof(tSymbolInfo) + STACKWALK_MAX_NAMELEN*sizeof(TCHAR));
-  pSym->SizeOfStruct = sizeof(tSymbolInfo);
-  pSym->MaxNameLen = STACKWALK_MAX_NAMELEN;
+  memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
+  pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+  pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
 
   memset(&Line, 0, sizeof(Line));
   Line.SizeOfStruct = sizeof(Line);
@@ -1050,11 +1223,11 @@
     // assume that either you are done, or that the stack is so hosed that the next
     // deeper frame could not be found.
     // CONTEXT need not to be supplied if imageTyp is IMAGE_FILE_MACHINE_I386!
-    if (!this->m_sw->stackWalk64(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem,
-                         this->m_sw->symFunctionTableAccess64, this->m_sw->symGetModuleBase64, NULL))
+    if (!this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem,
+                         this->m_sw->pSFTA, this->m_sw->pSGMB, NULL))
     {
       // INFO: "StackWalk64" does not set "GetLastError"...
-      this->OnDbgHelpErr(_T("StackWalk64"), 0, s.AddrPC.Offset);
+      this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset);
       break;
     }
 
@@ -1072,7 +1245,7 @@
     {
       if ((this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount))
       {
-        this->OnDbgHelpErr(_T("StackWalk64-Endless-Callstack!"), 0, s.AddrPC.Offset);
+        this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
         break;
       }
       curRecursionCount++;
@@ -1083,23 +1256,23 @@
     {
       // we seem to have a valid PC
       // show procedure info (SymGetSymFromAddr64())
-      if (this->m_sw->symFromAddr(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol),
+      if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol),
                              pSym) != FALSE)
       {
         MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name);
         // UnDecorateSymbolName()
-        DWORD res = this->m_sw->unDecorateSymbolName(pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY);
-        res = this->m_sw->unDecorateSymbolName(pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE);
+        this->m_sw->pUDSN(pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY);
+        this->m_sw->pUDSN(pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE);
       }
       else
       {
-        this->OnDbgHelpErr(_T("SymGetSymFromAddr64"), GetLastError(), s.AddrPC.Offset);
+        this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
       }
 
       // show line number info, NT5.0-method (SymGetLineFromAddr64())
-      if (this->m_sw->symGetLineFromAddr64 != NULL)
+      if (this->m_sw->pSGLFA != NULL)
       { // yes, we have SymGetLineFromAddr64()
-        if (this->m_sw->symGetLineFromAddr64(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine),
+        if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine),
                                &Line) != FALSE)
         {
           csEntry.lineNumber = Line.LineNumber;
@@ -1107,7 +1280,7 @@
         }
         else
         {
-          this->OnDbgHelpErr(_T("SymGetLineFromAddr64"), GetLastError(), s.AddrPC.Offset);
+          this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
         }
       } // yes, we have SymGetLineFromAddr64()
 
@@ -1157,7 +1330,7 @@
       } // got module info OK
       else
       {
-        this->OnDbgHelpErr(_T("SymGetModuleInfo64"), GetLastError(), s.AddrPC.Offset);
+        this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
       }
     } // we seem to have a valid PC
 
@@ -1203,20 +1376,22 @@
   }
 
   // SymGetSymFromAddr64() is required
-  if (this->m_sw->symFromAddr == NULL)
+  if (this->m_sw->pSGSFA == NULL)
     return FALSE;
 
   // Show object info (SymGetSymFromAddr64())
   DWORD64            dwAddress = DWORD64(pObject);
   DWORD64            dwDisplacement = 0;
-  tSymbolInfo* pSym =
-      (tSymbolInfo*)malloc(sizeof(tSymbolInfo) + STACKWALK_MAX_NAMELEN*sizeof(TCHAR));
-  memset(pSym, 0, sizeof(tSymbolInfo) + STACKWALK_MAX_NAMELEN*sizeof(TCHAR));
-  pSym->SizeOfStruct = sizeof(tSymbolInfo);
-  pSym->MaxNameLen = STACKWALK_MAX_NAMELEN;
-  if (this->m_sw->symFromAddr(this->m_hProcess, dwAddress, &dwDisplacement, pSym) == FALSE)
+  const SIZE_T       symSize = sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN;
+  IMAGEHLP_SYMBOL64* pSym = (IMAGEHLP_SYMBOL64*) malloc(symSize);
+  if (!pSym)
+    return FALSE;
+  memset(pSym, 0, symSize);
+  pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+  pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
+  if (this->m_sw->pSGSFA(this->m_hProcess, dwAddress, &dwDisplacement, pSym) == FALSE)
   {
-    this->OnDbgHelpErr(_T("SymGetSymFromAddr64"), GetLastError(), dwAddress);
+    this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), dwAddress);
     return FALSE;
   }
   // Object name output
@@ -1247,22 +1422,22 @@
   }
 }
 
-void StackWalker::OnLoadModule(LPCTSTR    img,
-                               LPCTSTR    mod,
+void StackWalker::OnLoadModule(LPCSTR    img,
+                               LPCSTR    mod,
                                DWORD64   baseAddr,
                                DWORD     size,
                                DWORD     result,
-                               LPCTSTR    symType,
-                               LPCTSTR    pdbName,
+                               LPCSTR    symType,
+                               LPCSTR    pdbName,
                                ULONGLONG fileVersion)
 {
-  TCHAR   buffer[STACKWALK_MAX_NAMELEN];
+  CHAR   buffer[STACKWALK_MAX_NAMELEN];
   size_t maxLen = STACKWALK_MAX_NAMELEN;
 #if _MSC_VER >= 1400
   maxLen = _TRUNCATE;
 #endif
   if (fileVersion == 0)
-    _sntprintf_s(buffer, maxLen, _T("%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n"),
+    _snprintf_s(buffer, maxLen, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n",
                 img, mod, (LPVOID)baseAddr, size, result, symType, pdbName);
   else
   {
@@ -1270,9 +1445,9 @@
     DWORD v3 = (DWORD)((fileVersion >> 16) & 0xFFFF);
     DWORD v2 = (DWORD)((fileVersion >> 32) & 0xFFFF);
     DWORD v1 = (DWORD)((fileVersion >> 48) & 0xFFFF);
-    _sntprintf_s(
+    _snprintf_s(
         buffer, maxLen,
-        _T("%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n"),
+        "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n",
         img, mod, (LPVOID)baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);
   }
   buffer[STACKWALK_MAX_NAMELEN - 1] = 0; // be sure it is NULL terminated
@@ -1281,7 +1456,7 @@
 
 void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry)
 {
-  TCHAR   buffer[STACKWALK_MAX_NAMELEN];
+  CHAR   buffer[STACKWALK_MAX_NAMELEN];
   size_t maxLen = STACKWALK_MAX_NAMELEN;
 #if _MSC_VER >= 1400
   maxLen = _TRUNCATE;
@@ -1289,48 +1464,48 @@
   if ((eType != lastEntry) && (entry.offset != 0))
   {
     if (entry.name[0] == 0)
-      MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, _T("(function-name not available)"));
+      MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)");
     if (entry.undName[0] != 0)
       MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName);
     if (entry.undFullName[0] != 0)
       MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName);
     if (entry.lineFileName[0] == 0)
     {
-      MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, _T("(filename not available)"));
+      MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)");
       if (entry.moduleName[0] == 0)
-        MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, _T("(module-name not available)"));
-      _sntprintf_s(buffer, maxLen, _T("%p (%s): %s: %s\n"), (LPVOID)entry.offset, entry.moduleName,
+        MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)");
+      _snprintf_s(buffer, maxLen, "%p (%s): %s: %s\n", (LPVOID)entry.offset, entry.moduleName,
                   entry.lineFileName, entry.name);
     }
     else
-      _sntprintf_s(buffer, maxLen, _T("%s (%d): %s\n"), entry.lineFileName, entry.lineNumber,
+      _snprintf_s(buffer, maxLen, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber,
                   entry.name);
     buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
     OnOutput(buffer);
   }
 }
 
-void StackWalker::OnDbgHelpErr(LPCTSTR szFuncName, DWORD gle, DWORD64 addr)
+void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
 {
-  TCHAR   buffer[STACKWALK_MAX_NAMELEN];
+  CHAR   buffer[STACKWALK_MAX_NAMELEN];
   size_t maxLen = STACKWALK_MAX_NAMELEN;
 #if _MSC_VER >= 1400
   maxLen = _TRUNCATE;
 #endif
-  _sntprintf_s(buffer, maxLen, _T("ERROR: %s, GetLastError: %d (Address: %p)\n"), szFuncName, gle,
+  _snprintf_s(buffer, maxLen, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle,
               (LPVOID)addr);
   buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
   OnOutput(buffer);
 }
 
-void StackWalker::OnSymInit(LPCTSTR szSearchPath, DWORD symOptions, LPCTSTR szUserName)
+void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
 {
-  TCHAR   buffer[STACKWALK_MAX_NAMELEN];
+  CHAR   buffer[STACKWALK_MAX_NAMELEN];
   size_t maxLen = STACKWALK_MAX_NAMELEN;
 #if _MSC_VER >= 1400
   maxLen = _TRUNCATE;
 #endif
-  _sntprintf_s(buffer, maxLen, _T("SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n"),
+  _snprintf_s(buffer, maxLen, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n",
               szSearchPath, symOptions, szUserName);
   buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
   OnOutput(buffer);
@@ -1347,16 +1522,16 @@
     OnOutput(buffer);
   }
 #else
-  OSVERSIONINFOEX ver;
-  ZeroMemory(&ver, sizeof(OSVERSIONINFOEX));
+  OSVERSIONINFOEXA ver;
+  ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));
   ver.dwOSVersionInfoSize = sizeof(ver);
 #if _MSC_VER >= 1900
 #pragma warning(push)
 #pragma warning(disable : 4996)
 #endif
-  if (GetVersionEx((OSVERSIONINFO*)&ver) != FALSE)
+  if (GetVersionExA((OSVERSIONINFOA*)&ver) != FALSE)
   {
-    _sntprintf_s(buffer, maxLen, _T("OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n"), ver.dwMajorVersion,
+    _snprintf_s(buffer, maxLen, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", ver.dwMajorVersion,
                 ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion, ver.wSuiteMask,
                 ver.wProductType);
     buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
@@ -1368,7 +1543,7 @@
 #endif
 }
 
-void StackWalker::OnOutput(LPCTSTR buffer)
+void StackWalker::OnOutput(LPCSTR buffer)
 {
-  OutputDebugString(buffer);
+  OutputDebugStringA(buffer);
 }
diff --git a/third_party/allwpilib/wpiutil/src/main/native/windows/StackWalker.h b/third_party/allwpilib/wpiutil/src/main/native/windows/StackWalker.h
index 89be951..5ab241e 100644
--- a/third_party/allwpilib/wpiutil/src/main/native/windows/StackWalker.h
+++ b/third_party/allwpilib/wpiutil/src/main/native/windows/StackWalker.h
@@ -43,14 +43,27 @@
 
 #include <windows.h>
 
-#if _MSC_VER >= 1900
-#pragma warning(disable : 4091)
+// special defines for VC5/6 (if no actual PSDK is installed):
+#if _MSC_VER < 1300
+typedef unsigned __int64 DWORD64, *PDWORD64;
+#if defined(_WIN64)
+typedef unsigned __int64 SIZE_T, *PSIZE_T;
+#else
+typedef unsigned long SIZE_T, *PSIZE_T;
 #endif
+#endif // _MSC_VER < 1300
 
 class StackWalkerInternal; // forward
 class StackWalker
 {
 public:
+  typedef enum ExceptType
+  {
+    NonExcept   = 0,     // RtlCaptureContext
+    AfterExcept = 1,
+    AfterCatch  = 2,     // get_current_exception_context
+  } ExceptType;
+
   typedef enum StackWalkOptions
   {
     // No addition info will be retrieved
@@ -85,13 +98,28 @@
     OptionsAll = 0x3F
   } StackWalkOptions;
 
+  StackWalker(ExceptType extype, int options = OptionsAll, PEXCEPTION_POINTERS exp = NULL);
+
   StackWalker(int    options = OptionsAll, // 'int' is by design, to combine the enum-flags
-              LPCTSTR szSymPath = NULL,
+              LPCSTR szSymPath = NULL,
               DWORD  dwProcessId = GetCurrentProcessId(),
               HANDLE hProcess = GetCurrentProcess());
+
   StackWalker(DWORD dwProcessId, HANDLE hProcess);
+
   virtual ~StackWalker();
 
+  bool SetSymPath(LPCSTR szSymPath);
+
+  bool SetTargetProcess(DWORD dwProcessId, HANDLE hProcess);
+
+  PCONTEXT GetCurrentExceptionContext();
+
+private:
+  bool Init(ExceptType extype, int options, LPCSTR szSymPath, DWORD dwProcessId,
+            HANDLE hProcess, PEXCEPTION_POINTERS exp = NULL);
+
+public:
   typedef BOOL(__stdcall* PReadProcessMemoryRoutine)(
       HANDLE  hProcess,
       DWORD64 qwBaseAddress,
@@ -127,18 +155,18 @@
   typedef struct CallstackEntry
   {
     DWORD64 offset; // if 0, we have no valid entry
-    TCHAR    name[STACKWALK_MAX_NAMELEN];
-    TCHAR    undName[STACKWALK_MAX_NAMELEN];
-    TCHAR    undFullName[STACKWALK_MAX_NAMELEN];
+    CHAR    name[STACKWALK_MAX_NAMELEN];
+    CHAR    undName[STACKWALK_MAX_NAMELEN];
+    CHAR    undFullName[STACKWALK_MAX_NAMELEN];
     DWORD64 offsetFromSmybol;
     DWORD   offsetFromLine;
     DWORD   lineNumber;
-    TCHAR    lineFileName[STACKWALK_MAX_NAMELEN];
+    CHAR    lineFileName[STACKWALK_MAX_NAMELEN];
     DWORD   symType;
     LPCSTR  symTypeString;
-    TCHAR    moduleName[STACKWALK_MAX_NAMELEN];
+    CHAR    moduleName[STACKWALK_MAX_NAMELEN];
     DWORD64 baseOfImage;
-    TCHAR    loadedImageName[STACKWALK_MAX_NAMELEN];
+    CHAR    loadedImageName[STACKWALK_MAX_NAMELEN];
   } CallstackEntry;
 
   typedef enum CallstackEntryType
@@ -148,24 +176,24 @@
     lastEntry
   } CallstackEntryType;
 
-  virtual void OnSymInit(LPCTSTR szSearchPath, DWORD symOptions, LPCTSTR szUserName);
-  virtual void OnLoadModule(LPCTSTR    img,
-                            LPCTSTR    mod,
+  virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
+  virtual void OnLoadModule(LPCSTR    img,
+                            LPCSTR    mod,
                             DWORD64   baseAddr,
                             DWORD     size,
                             DWORD     result,
-                            LPCTSTR    symType,
-                            LPCTSTR    pdbName,
+                            LPCSTR    symType,
+                            LPCSTR    pdbName,
                             ULONGLONG fileVersion);
   virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry);
-  virtual void OnDbgHelpErr(LPCTSTR szFuncName, DWORD gle, DWORD64 addr);
-  virtual void OnOutput(LPCTSTR szText);
+  virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
+  virtual void OnOutput(LPCSTR szText);
 
   StackWalkerInternal* m_sw;
   HANDLE               m_hProcess;
   DWORD                m_dwProcessId;
   BOOL                 m_modulesLoaded;
-  LPTSTR               m_szSymPath;
+  LPSTR                m_szSymPath;
 
   int m_options;
   int m_MaxRecursionCount;
@@ -179,6 +207,57 @@
   friend StackWalkerInternal;
 }; // class StackWalker
 
+// The "ugly" assembler-implementation is needed for systems before XP
+// If you have a new PSDK and you only compile for XP and later, then you can use
+// the "RtlCaptureContext"
+// Currently there is no define which determines the PSDK-Version...
+// So we just use the compiler-version (and assumes that the PSDK is
+// the one which was installed by the VS-IDE)
+
+// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
+//       But I currently use it in x64/IA64 environments...
+//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
+
+#if defined(_M_IX86)
+#ifdef CURRENT_THREAD_VIA_EXCEPTION
+// TODO: The following is not a "good" implementation,
+// because the callstack is only valid in the "__except" block...
+#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags)               \
+  do                                                                            \
+  {                                                                             \
+    memset(&c, 0, sizeof(CONTEXT));                                             \
+    EXCEPTION_POINTERS* pExp = NULL;                                            \
+    __try                                                                       \
+    {                                                                           \
+      throw 0;                                                                  \
+    }                                                                           \
+    __except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER   \
+                                                  : EXCEPTION_EXECUTE_HANDLER)) \
+    {                                                                           \
+    }                                                                           \
+    if (pExp != NULL)                                                           \
+      memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT));                         \
+    c.ContextFlags = contextFlags;                                              \
+  } while (0);
+#else
+// clang-format off
+// The following should be enough for walking the callstack...
+#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
+  do                                                              \
+  {                                                               \
+    memset(&c, 0, sizeof(CONTEXT));                               \
+    c.ContextFlags = contextFlags;                                \
+    __asm    call x                                               \
+    __asm x: pop eax                                              \
+    __asm    mov c.Eip, eax                                       \
+    __asm    mov c.Ebp, ebp                                       \
+    __asm    mov c.Esp, esp                                       \
+  } while (0)
+// clang-format on
+#endif
+
+#else
+
 // The following is defined for x86 (XP and higher), x64 and IA64:
 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
   do                                                              \
@@ -187,6 +266,7 @@
     c.ContextFlags = contextFlags;                                \
     RtlCaptureContext(&c);                                        \
   } while (0);
+#endif
 
 #endif //defined(_MSC_VER)
 
diff --git a/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/InterpolatingTreeMapTest.java b/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/InterpolatingTreeMapTest.java
index ea23dc5..2203ca8 100644
--- a/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/InterpolatingTreeMapTest.java
+++ b/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/InterpolatingTreeMapTest.java
@@ -8,6 +8,7 @@
 
 import org.junit.jupiter.api.Test;
 
+@SuppressWarnings("removal")
 class InterpolatingTreeMapTest {
   @Test
   void testInterpolationDouble() {
diff --git a/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/cleanup/CleanupPoolTest.java b/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/cleanup/CleanupPoolTest.java
new file mode 100644
index 0000000..a953a85
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/cleanup/CleanupPoolTest.java
@@ -0,0 +1,154 @@
+// 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.
+
+package edu.wpi.first.util.cleanup;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class CleanupPoolTest {
+  static class AutoCloseableObject implements AutoCloseable {
+    public boolean m_closed;
+
+    @Override
+    public void close() {
+      m_closed = true;
+    }
+  }
+
+  static class AutoCloseableObjectWithCallback implements AutoCloseable {
+    private final Runnable m_cb;
+
+    AutoCloseableObjectWithCallback(Runnable cb) {
+      m_cb = cb;
+    }
+
+    @Override
+    public void close() {
+      m_cb.run();
+    }
+  }
+
+  static class FailingAutoCloseableObject implements AutoCloseable {
+    public static final String message = "This is an expected failure";
+
+    @Override
+    public void close() {
+      throw new RuntimeException(message);
+    }
+  }
+
+  @Test
+  void cleanupStackWorks() {
+    List<AutoCloseableObject> objects = new ArrayList<>();
+    objects.add(new AutoCloseableObject());
+    objects.add(new AutoCloseableObject());
+    objects.add(new AutoCloseableObject());
+
+    try (CleanupPool pool = new CleanupPool()) {
+      for (AutoCloseableObject autoCloseableObject : objects) {
+        pool.register(autoCloseableObject);
+      }
+    }
+
+    for (AutoCloseableObject autoCloseableObject : objects) {
+      assertTrue(autoCloseableObject.m_closed);
+    }
+  }
+
+  @Test
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  void cleanupStackWithExceptionNotInCloseWorks() {
+    List<AutoCloseableObject> objects = new ArrayList<>();
+    objects.add(new AutoCloseableObject());
+    objects.add(new AutoCloseableObject());
+    objects.add(new AutoCloseableObject());
+
+    String message = "This is a known failure";
+
+    try (CleanupPool pool = new CleanupPool()) {
+      for (AutoCloseableObject autoCloseableObject : objects) {
+        pool.register(autoCloseableObject);
+      }
+      throw new Exception(message);
+    } catch (Exception e) {
+      assertEquals(message, e.getMessage());
+    }
+
+    for (AutoCloseableObject autoCloseableObject : objects) {
+      assertTrue(autoCloseableObject.m_closed);
+    }
+  }
+
+  @Test
+  @SuppressWarnings("PMD.AvoidCatchingGenericException")
+  void cleanupStackWithExceptionInCloseWorks() {
+    List<AutoCloseableObject> objects = new ArrayList<>();
+    objects.add(new AutoCloseableObject());
+    objects.add(new AutoCloseableObject());
+    objects.add(new AutoCloseableObject());
+
+    try (CleanupPool pool = new CleanupPool()) {
+      for (AutoCloseableObject autoCloseableObject : objects) {
+        pool.register(new FailingAutoCloseableObject());
+        pool.register(autoCloseableObject);
+      }
+    }
+
+    for (AutoCloseableObject autoCloseableObject : objects) {
+      assertTrue(autoCloseableObject.m_closed);
+    }
+  }
+
+  @Test
+  void cleanupStackRemovalWorks() {
+    List<AutoCloseableObject> objects = new ArrayList<>();
+    objects.add(new AutoCloseableObject());
+    objects.add(new AutoCloseableObject());
+    objects.add(new AutoCloseableObject());
+
+    try (CleanupPool pool = new CleanupPool()) {
+      for (AutoCloseableObject autoCloseableObject : objects) {
+        pool.register(autoCloseableObject);
+      }
+
+      pool.remove(objects.get(0));
+    }
+
+    int idx = 0;
+    for (AutoCloseableObject autoCloseableObject : objects) {
+      if (idx == 0) {
+        assertFalse(autoCloseableObject.m_closed);
+      } else {
+        assertTrue(autoCloseableObject.m_closed);
+      }
+      idx++;
+    }
+  }
+
+  @Test
+  void cleanupStackIsLifo() {
+    List<AutoCloseableObjectWithCallback> objects = new ArrayList<>();
+    List<Integer> order = new ArrayList<>();
+    objects.add(new AutoCloseableObjectWithCallback(() -> order.add(0)));
+    objects.add(new AutoCloseableObjectWithCallback(() -> order.add(1)));
+    objects.add(new AutoCloseableObjectWithCallback(() -> order.add(2)));
+
+    try (CleanupPool pool = new CleanupPool()) {
+      for (AutoCloseable autoCloseableObject : objects) {
+        pool.register(autoCloseableObject);
+      }
+    }
+
+    assertEquals(order.size(), 3);
+    assertEquals(order.get(0), 2);
+    assertEquals(order.get(1), 1);
+    assertEquals(order.get(2), 0);
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/cleanup/ReflectionCleanupTest.java b/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/cleanup/ReflectionCleanupTest.java
new file mode 100644
index 0000000..7bf3d64
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/cleanup/ReflectionCleanupTest.java
@@ -0,0 +1,62 @@
+// 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.
+
+package edu.wpi.first.util.cleanup;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class ReflectionCleanupTest {
+  static class CleanupClass implements AutoCloseable {
+    public boolean m_closed;
+
+    @Override
+    public void close() {
+      m_closed = true;
+    }
+  }
+
+  @SuppressWarnings("PMD.TestClassWithoutTestCases")
+  static class CleanupTest implements ReflectionCleanup {
+    public CleanupClass m_class1 = new CleanupClass();
+    public CleanupClass m_class2 = new CleanupClass();
+    public Object m_nonCleanupObject = new Object();
+    public Object m_nullCleanupObject;
+
+    @Override
+    public void close() {
+      reflectionCleanup(CleanupTest.class);
+    }
+  }
+
+  static class CleanupTest2 extends CleanupTest {
+    @SkipCleanup public CleanupClass m_class3 = new CleanupClass();
+    public CleanupClass m_class4 = new CleanupClass();
+
+    @Override
+    public void close() {
+      reflectionCleanup(CleanupTest2.class);
+    }
+  }
+
+  @Test
+  void cleanupClosesAllFields() {
+    CleanupTest test = new CleanupTest();
+    test.close();
+    assertTrue(test.m_class1.m_closed);
+    assertTrue(test.m_class2.m_closed);
+  }
+
+  @Test
+  void cleanupOnlyClosesExplicitClassAndSkipWorks() {
+    CleanupTest2 test = new CleanupTest2();
+    test.close();
+    assertFalse(test.m_class1.m_closed);
+    assertFalse(test.m_class2.m_closed);
+    assertFalse(test.m_class3.m_closed);
+    assertTrue(test.m_class4.m_closed);
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/struct/DynamicStructTest.java b/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/struct/DynamicStructTest.java
new file mode 100644
index 0000000..9e77c80
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/struct/DynamicStructTest.java
@@ -0,0 +1,390 @@
+// 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.
+
+package edu.wpi.first.util.struct;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.stream.Stream;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class DynamicStructTest {
+  @SuppressWarnings("MemberName")
+  private StructDescriptorDatabase db;
+
+  @BeforeEach
+  public void init() {
+    db = new StructDescriptorDatabase();
+  }
+
+  @Test
+  void testEmpty() {
+    var desc = assertDoesNotThrow(() -> db.add("test", ""));
+    assertEquals(desc.getName(), "test");
+    assertEquals(desc.getSchema(), "");
+    assertTrue(desc.getFields().isEmpty());
+    assertTrue(desc.isValid());
+    assertEquals(desc.getSize(), 0);
+  }
+
+  @Test
+  void testNestedStruct() {
+    var desc = assertDoesNotThrow(() -> db.add("test", "int32 a"));
+    assertTrue(desc.isValid());
+    var desc2 = assertDoesNotThrow(() -> db.add("test2", "test a"));
+    assertTrue(desc2.isValid());
+    assertEquals(desc2.getSize(), 4);
+  }
+
+  @Test
+  void testDelayedValid() {
+    var desc = assertDoesNotThrow(() -> db.add("test", "foo a"));
+    assertFalse(desc.isValid());
+    var desc2 = assertDoesNotThrow(() -> db.add("test2", "foo a[2]"));
+    assertFalse(desc2.isValid());
+    var desc3 = assertDoesNotThrow(() -> db.add("foo", "int32 a"));
+    assertTrue(desc3.isValid());
+    assertTrue(desc.isValid());
+    assertEquals(desc.getSize(), 4);
+    assertTrue(desc2.isValid());
+    assertEquals(desc2.getSize(), 8);
+  }
+
+  @Test
+  void testInvalidBitfield() {
+    assertThrows(
+        BadSchemaException.class,
+        () -> db.add("test", "float a:1"),
+        "field a: type float cannot be bitfield");
+    assertThrows(
+        BadSchemaException.class,
+        () -> db.add("test", "double a:1"),
+        "field a: type double cannot be bitfield");
+    assertThrows(
+        BadSchemaException.class,
+        () -> db.add("test", "foo a:1"),
+        "field a: type foo cannot be bitfield");
+  }
+
+  @Test
+  void testCircularStructReference() {
+    assertThrows(
+        BadSchemaException.class,
+        () -> db.add("test", "test a"),
+        "field a: recursive struct reference");
+  }
+
+  @Test
+  void testNestedCircularStructRef() {
+    assertDoesNotThrow(() -> db.add("test", "foo a"));
+    assertDoesNotThrow(() -> db.add("foo", "bar a"));
+    assertThrows(
+        BadSchemaException.class,
+        () -> db.add("bar", "test a"),
+        "circular struct reference: bar <- foo <- test");
+
+    // ok
+    var desc = assertDoesNotThrow(() -> db.add("baz", "bar a"));
+    assertFalse(desc.isValid());
+  }
+
+  @Test
+  void testNestedCircularStructRef2() {
+    assertDoesNotThrow(() -> db.add("test", "foo a"));
+    assertDoesNotThrow(() -> db.add("bar", "test a"));
+    assertThrows(
+        BadSchemaException.class,
+        () -> db.add("foo", "bar a"),
+        "circular struct reference: foo <- test <- bar");
+  }
+
+  @Test
+  void testBitfieldBasic() {
+    var desc = assertDoesNotThrow(() -> db.add("test", "int32 a:2; uint32 b:30"));
+    assertEquals(desc.getSize(), 4);
+    var fields = desc.getFields();
+    assertEquals(fields.size(), 2);
+    var field = fields.get(0);
+    assertEquals(field.getBitWidth(), 2);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getBitMask(), 0x3);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 4);
+    field = fields.get(1);
+    assertEquals(field.getBitWidth(), 30);
+    assertEquals(field.getBitShift(), 2);
+    assertEquals(field.getBitMask(), 0x3fffffff);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 4);
+  }
+
+  @Test
+  void testBitfieldDiffType() {
+    var desc = assertDoesNotThrow(() -> db.add("test", "int32 a:2; int16 b:2"));
+    assertEquals(desc.getSize(), 6);
+    var fields = desc.getFields();
+    assertEquals(fields.size(), 2);
+    var field = fields.get(0);
+    assertEquals(field.getBitWidth(), 2);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getBitMask(), 0x3);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 4);
+    field = fields.get(1);
+    assertEquals(field.getBitWidth(), 2);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getBitMask(), 0x3);
+    assertEquals(field.getOffset(), 4);
+    assertEquals(field.getSize(), 2);
+  }
+
+  @Test
+  void testBitfieldOverflow() {
+    var desc = assertDoesNotThrow(() -> db.add("test", "int8 a:4; int8 b:5"));
+    assertEquals(desc.getSize(), 2);
+    var fields = desc.getFields();
+    assertEquals(fields.size(), 2);
+    var field = fields.get(0);
+    assertEquals(field.getBitWidth(), 4);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getBitMask(), 0xf);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 1);
+    field = fields.get(1);
+    assertEquals(field.getBitWidth(), 5);
+    assertEquals(field.getBitMask(), 0x1f);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getOffset(), 1);
+    assertEquals(field.getSize(), 1);
+  }
+
+  @Test
+  void testBitfieldBoolBegin8() {
+    var desc = assertDoesNotThrow(() -> db.add("test", "bool a:1; int8 b:5"));
+    assertEquals(desc.getSize(), 1);
+    var fields = desc.getFields();
+    assertEquals(fields.size(), 2);
+    var field = fields.get(0);
+    assertEquals(field.getBitWidth(), 1);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getBitMask(), 0x1);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 1);
+    field = fields.get(1);
+    assertEquals(field.getBitWidth(), 5);
+    assertEquals(field.getBitMask(), 0x1f);
+    assertEquals(field.getBitShift(), 1);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 1);
+  }
+
+  @Test
+  void testBitfieldBoolBegin16() {
+    var desc = assertDoesNotThrow(() -> db.add("test", "bool a:1; int16 b:5"));
+    assertEquals(desc.getSize(), 3);
+    var fields = desc.getFields();
+    assertEquals(fields.size(), 2);
+    var field = fields.get(0);
+    assertEquals(field.getBitWidth(), 1);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getBitMask(), 0x1);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 1);
+    field = fields.get(1);
+    assertEquals(field.getBitWidth(), 5);
+    assertEquals(field.getBitMask(), 0x1f);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getOffset(), 1);
+    assertEquals(field.getSize(), 2);
+  }
+
+  @Test
+  void testBitfieldBoolMid() {
+    var desc =
+        assertDoesNotThrow(() -> db.add("test", "int16 a:2; bool b:1; bool c:1; uint16 d:5"));
+    assertEquals(desc.getSize(), 2);
+    var fields = desc.getFields();
+    assertEquals(fields.size(), 4);
+    var field = fields.get(0);
+    assertEquals(field.getBitWidth(), 2);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getBitMask(), 0x3);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 2);
+    field = fields.get(1);
+    assertEquals(field.getBitWidth(), 1);
+    assertEquals(field.getBitMask(), 0x1);
+    assertEquals(field.getBitShift(), 2);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 2);
+    field = fields.get(2);
+    assertEquals(field.getBitWidth(), 1);
+    assertEquals(field.getBitMask(), 0x1);
+    assertEquals(field.getBitShift(), 3);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 2);
+    field = fields.get(3);
+    assertEquals(field.getBitWidth(), 5);
+    assertEquals(field.getBitMask(), 0x1f);
+    assertEquals(field.getBitShift(), 4);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 2);
+  }
+
+  @Test
+  void testBitfieldBoolEnd() {
+    var desc = assertDoesNotThrow(() -> db.add("test", "int16 a:15; bool b:1"));
+    assertEquals(desc.getSize(), 2);
+    var fields = desc.getFields();
+    assertEquals(fields.size(), 2);
+    var field = fields.get(0);
+    assertEquals(field.getBitWidth(), 15);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getBitMask(), 0x7fff);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 2);
+    field = fields.get(1);
+    assertEquals(field.getBitWidth(), 1);
+    assertEquals(field.getBitMask(), 0x1);
+    assertEquals(field.getBitShift(), 15);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 2);
+  }
+
+  @Test
+  void testBitfieldBoolEnd2() {
+    var desc = assertDoesNotThrow(() -> db.add("test", "int16 a:16; bool b:1"));
+    assertEquals(desc.getSize(), 3);
+    var fields = desc.getFields();
+    assertEquals(fields.size(), 2);
+    var field = fields.get(0);
+    assertEquals(field.getBitWidth(), 16);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getBitMask(), 0xffff);
+    assertEquals(field.getOffset(), 0);
+    assertEquals(field.getSize(), 2);
+    field = fields.get(1);
+    assertEquals(field.getBitWidth(), 1);
+    assertEquals(field.getBitMask(), 0x1);
+    assertEquals(field.getBitShift(), 0);
+    assertEquals(field.getOffset(), 2);
+    assertEquals(field.getSize(), 1);
+  }
+
+  @Test
+  void testBitfieldBoolWrongSize() {
+    assertThrows(
+        BadSchemaException.class,
+        () -> db.add("test", "bool a:2"),
+        "field a: bit width must be 1 for bool type");
+  }
+
+  @Test
+  void testBitfieldTooBig() {
+    assertThrows(
+        BadSchemaException.class,
+        () -> db.add("test", "int16 a:17"),
+        "field a: bit width 17 exceeds type size");
+  }
+
+  @Test
+  void testDuplicateFieldName() {
+    assertThrows(
+        BadSchemaException.class,
+        () -> db.add("test", "int16 a; int8 a"),
+        "field a: duplicate field name");
+  }
+
+  private static Stream<Arguments> provideSimpleTestParams() {
+    return Stream.of(
+        Arguments.of("bool a", 1, StructFieldType.kBool, false, false, 8, 0xff),
+        Arguments.of("char a", 1, StructFieldType.kChar, false, false, 8, 0xff),
+        Arguments.of("int8 a", 1, StructFieldType.kInt8, true, false, 8, 0xff),
+        Arguments.of("int16 a", 2, StructFieldType.kInt16, true, false, 16, 0xffff),
+        Arguments.of("int32 a", 4, StructFieldType.kInt32, true, false, 32, 0xffffffffL),
+        Arguments.of("int64 a", 8, StructFieldType.kInt64, true, false, 64, -1),
+        Arguments.of("uint8 a", 1, StructFieldType.kUint8, false, true, 8, 0xff),
+        Arguments.of("uint16 a", 2, StructFieldType.kUint16, false, true, 16, 0xffff),
+        Arguments.of("uint32 a", 4, StructFieldType.kUint32, false, true, 32, 0xffffffffL),
+        Arguments.of("uint64 a", 8, StructFieldType.kUint64, false, true, 64, -1),
+        Arguments.of("float a", 4, StructFieldType.kFloat, false, false, 32, 0xffffffffL),
+        Arguments.of("float32 a", 4, StructFieldType.kFloat, false, false, 32, 0xffffffffL),
+        Arguments.of("double a", 8, StructFieldType.kDouble, false, false, 64, -1),
+        Arguments.of("float64 a", 8, StructFieldType.kDouble, false, false, 64, -1),
+        Arguments.of("foo a", 0, StructFieldType.kStruct, false, false, 0, 0));
+  }
+
+  @ParameterizedTest
+  @MethodSource("provideSimpleTestParams")
+  void testStandardCheck(
+      String schema,
+      int size,
+      StructFieldType type,
+      boolean isInt,
+      boolean isUint,
+      int bitWidth,
+      long bitMask) {
+    var desc = assertDoesNotThrow(() -> db.add("test", schema));
+    assertEquals(desc.getName(), "test");
+    assertEquals(desc.getSchema(), schema);
+    var fields = desc.getFields();
+    assertEquals(fields.size(), 1);
+    var field = fields.get(0);
+    assertEquals(field.getParent(), desc);
+    assertEquals(field.getName(), "a");
+    assertEquals(field.isInt(), isInt);
+    assertEquals(field.isUint(), isUint);
+    assertFalse(field.isArray());
+    if (type != StructFieldType.kStruct) {
+      assertTrue(desc.isValid());
+      assertEquals(desc.getSize(), size);
+      assertEquals(field.getSize(), size);
+      assertEquals(field.getBitWidth(), bitWidth);
+      assertEquals(field.getBitMask(), bitMask);
+    } else {
+      assertFalse(desc.isValid());
+      assertNotNull(field.getStruct());
+    }
+  }
+
+  @ParameterizedTest
+  @MethodSource("provideSimpleTestParams")
+  void testStandardArray(
+      String schema,
+      int size,
+      StructFieldType type,
+      boolean isInt,
+      boolean isUint,
+      int bitWidth,
+      long bitMask) {
+    var desc = assertDoesNotThrow(() -> db.add("test", schema + "[2]"));
+    assertEquals(desc.getName(), "test");
+    assertEquals(desc.getSchema(), schema + "[2]");
+    var fields = desc.getFields();
+    assertEquals(fields.size(), 1);
+    var field = fields.get(0);
+    assertEquals(field.getParent(), desc);
+    assertEquals(field.getName(), "a");
+    assertEquals(field.isInt(), isInt);
+    assertEquals(field.isUint(), isUint);
+    assertTrue(field.isArray());
+    assertEquals(field.getArraySize(), 2);
+    if (type != StructFieldType.kStruct) {
+      assertTrue(desc.isValid());
+      assertEquals(desc.getSize(), size * 2);
+    } else {
+      assertFalse(desc.isValid());
+      assertNotNull(field.getStruct());
+    }
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/struct/parser/ParserTest.java b/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/struct/parser/ParserTest.java
new file mode 100644
index 0000000..52ba8da
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/java/edu/wpi/first/util/struct/parser/ParserTest.java
@@ -0,0 +1,212 @@
+// 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.
+
+package edu.wpi.first.util.struct.parser;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class ParserTest {
+  @Test
+  void testEmpty() {
+    Parser p = new Parser("");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertTrue(schema.declarations.isEmpty());
+  }
+
+  @Test
+  void testEmptySemicolon() {
+    Parser p = new Parser(";");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertTrue(schema.declarations.isEmpty());
+  }
+
+  @Test
+  void testSimple() {
+    Parser p = new Parser("int32 a");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+    var decl = schema.declarations.get(0);
+    assertEquals(decl.typeString, "int32");
+    assertEquals(decl.name, "a");
+    assertEquals(decl.arraySize, 1);
+  }
+
+  @Test
+  void testSimpleTrailingSemi() {
+    Parser p = new Parser("int32 a;");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+  }
+
+  @Test
+  void testArray() {
+    Parser p = new Parser("int32 a[2]");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+    var decl = schema.declarations.get(0);
+    assertEquals(decl.typeString, "int32");
+    assertEquals(decl.name, "a");
+    assertEquals(decl.arraySize, 2);
+  }
+
+  @Test
+  void testArrayTrailingSemi() {
+    Parser p = new Parser("int32 a[2];");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+  }
+
+  @Test
+  void testBitfield() {
+    Parser p = new Parser("int32 a:2");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+    var decl = schema.declarations.get(0);
+    assertEquals(decl.typeString, "int32");
+    assertEquals(decl.name, "a");
+    assertEquals(decl.bitWidth, 2);
+  }
+
+  @Test
+  void testBitfieldTrailingSemi() {
+    Parser p = new Parser("int32 a:2;");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+  }
+
+  @Test
+  void testEnumKeyword() {
+    Parser p = new Parser("enum {x=1} int32 a;");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+    var decl = schema.declarations.get(0);
+    assertEquals(decl.typeString, "int32");
+    assertEquals(decl.name, "a");
+    assertEquals(decl.enumValues.size(), 1);
+    assertEquals(decl.enumValues.get("x"), 1);
+  }
+
+  @Test
+  void testEnumNoKeyword() {
+    Parser p = new Parser("{x=1} int32 a;");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+    var decl = schema.declarations.get(0);
+    assertEquals(decl.typeString, "int32");
+    assertEquals(decl.name, "a");
+    assertEquals(decl.enumValues.size(), 1);
+    assertEquals(decl.enumValues.get("x"), 1);
+  }
+
+  @Test
+  void testEnumNoValues() {
+    Parser p = new Parser("{} int32 a;");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+    var decl = schema.declarations.get(0);
+    assertEquals(decl.typeString, "int32");
+    assertEquals(decl.name, "a");
+    assertTrue(decl.enumValues.isEmpty());
+  }
+
+  @Test
+  void testEnumMultipleValues() {
+    Parser p = new Parser("{x=1,y=-2} int32 a;");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+    var decl = schema.declarations.get(0);
+    assertEquals(decl.typeString, "int32");
+    assertEquals(decl.name, "a");
+    assertEquals(decl.enumValues.size(), 2);
+    assertEquals(decl.enumValues.get("x"), 1);
+    assertEquals(decl.enumValues.get("y"), -2);
+  }
+
+  @Test
+  void testEnumTrailingComma() {
+    Parser p = new Parser("{x=1,y=2,} int32 a;");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 1);
+    var decl = schema.declarations.get(0);
+    assertEquals(decl.typeString, "int32");
+    assertEquals(decl.name, "a");
+    assertEquals(decl.enumValues.size(), 2);
+    assertEquals(decl.enumValues.get("x"), 1);
+    assertEquals(decl.enumValues.get("y"), 2);
+  }
+
+  @Test
+  void testMultipleNoTrailingSemi() {
+    Parser p = new Parser("int32 a; int16 b");
+    ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+    assertEquals(schema.declarations.size(), 2);
+    assertEquals(schema.declarations.get(0).typeString, "int32");
+    assertEquals(schema.declarations.get(0).name, "a");
+    assertEquals(schema.declarations.get(1).typeString, "int16");
+    assertEquals(schema.declarations.get(1).name, "b");
+  }
+
+  @Test
+  void testErrBitfieldArray() {
+    Parser p = new Parser("int32 a[1]:2");
+    assertThrows(ParseException.class, () -> p.parse(), "10: expected ';', got ':'");
+  }
+
+  @Test
+  void testErrNoArrayValue() {
+    Parser p = new Parser("int32 a[]");
+    assertThrows(ParseException.class, () -> p.parse(), "8: expected integer, got ']'");
+  }
+
+  @Test
+  void testErrNoBitfieldValue() {
+    Parser p = new Parser("int32 a:");
+    assertThrows(ParseException.class, () -> p.parse(), "8: expected integer, got ''");
+  }
+
+  @Test
+  void testErrNoNameArray() {
+    Parser p = new Parser("int32 [2]");
+    assertThrows(ParseException.class, () -> p.parse(), "6: expected identifier, got '['");
+  }
+
+  @Test
+  void testErrNoNameBitField() {
+    Parser p = new Parser("int32 :2");
+    assertThrows(ParseException.class, () -> p.parse(), "6: expected identifier, got ':'");
+  }
+
+  @Test
+  void testNegativeBitField() {
+    Parser p = new Parser("int32 a:-1");
+    assertThrows(
+        ParseException.class, () -> p.parse(), "8: bitfield width '-1' is not a positive integer");
+  }
+
+  @Test
+  void testNegativeArraySize() {
+    Parser p = new Parser("int32 a[-1]");
+    assertThrows(
+        ParseException.class, () -> p.parse(), "8: array size '-1' is not a positive integer");
+  }
+
+  @Test
+  void testZeroBitField() {
+    Parser p = new Parser("int32 a:0");
+    assertThrows(
+        ParseException.class, () -> p.parse(), "8: bitfield width '0' is not a positive integer");
+  }
+
+  @Test
+  void testZeroArraySize() {
+    Parser p = new Parser("int32 a[0]");
+    assertThrows(
+        ParseException.class, () -> p.parse(), "8: array size '0' is not a positive integer");
+  }
+}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/ArrayTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/ArrayTest.cpp
index 1f28157..f70b201 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/ArrayTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/ArrayTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "wpi/array.h"
 
 namespace {
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/Base64Test.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/Base64Test.cpp
index 542858d..514bb55 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/Base64Test.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/Base64Test.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "wpi/Base64.h"
 #include "wpi/SmallString.h"
 
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/CircularBufferTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/CircularBufferTest.cpp
index ad2285e..a56e2e5 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/CircularBufferTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/CircularBufferTest.cpp
@@ -6,7 +6,7 @@
 
 #include <array>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 static const std::array<double, 10> values = {
     {751.848, 766.366, 342.657, 234.252, 716.126, 132.344, 445.697, 22.727,
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/ScopeExitTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/ScopeExitTest.cpp
index 9cecb1c..d6f99ca 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/ScopeExitTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/ScopeExitTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "wpi/scope"
 
 TEST(ScopeExitTest, ScopeExit) {
@@ -23,6 +24,7 @@
   {
     wpi::scope_exit exit1{[&] { ++exitCount; }};
     wpi::scope_exit exit2 = std::move(exit1);
+    // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move)
     wpi::scope_exit exit3 = std::move(exit1);
     EXPECT_EQ(0, exitCount);
   }
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/StaticCircularBufferTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/StaticCircularBufferTest.cpp
index f95f9cb..338bd8e 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/StaticCircularBufferTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/StaticCircularBufferTest.cpp
@@ -6,7 +6,7 @@
 
 #include <array>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 static const std::array<double, 10> values = {
     {751.848, 766.366, 342.657, 234.252, 716.126, 132.344, 445.697, 22.727,
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/SynchronizationTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/SynchronizationTest.cpp
index a349c54..70d6d13 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/SynchronizationTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/SynchronizationTest.cpp
@@ -6,7 +6,7 @@
 
 #include <thread>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 TEST(EventTest, AutoReset) {
   auto event = wpi::CreateEvent(false, false);
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/UidVectorTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/UidVectorTest.cpp
index f9e1624..dda7d79 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/UidVectorTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/UidVectorTest.cpp
@@ -4,7 +4,7 @@
 
 #include "wpi/UidVector.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 namespace wpi {
 
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/UnescapeCStringTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/UnescapeCStringTest.cpp
index 7f0ca5f..8b35b97 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/UnescapeCStringTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/UnescapeCStringTest.cpp
@@ -2,7 +2,8 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "wpi/SmallString.h"
 #include "wpi/StringExtras.h"
 
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/ct_string_test.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/ct_string_test.cpp
new file mode 100644
index 0000000..6c3c7d0
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/ct_string_test.cpp
@@ -0,0 +1,38 @@
+// 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/ct_string.h"  // NOLINT(build/include_order)
+
+#include <string>
+#include <string_view>
+
+#include <gtest/gtest.h>
+
+TEST(CtStringTest, Concat) {
+  using namespace wpi::literals;
+  constexpr std::string_view astring = "name";
+  constexpr int arrsize = 5;
+  constexpr auto str = Concat(
+      wpi::ct_string<char, std::char_traits<char>, astring.size()>{astring},
+      "["_ct_string, wpi::NumToCtString<arrsize>(), "]"_ct_string);
+  static_assert(str.size() == 7);
+  ASSERT_EQ(std::string{str}, "name[5]");
+}
+
+TEST(CtStringTest, OperatorPlus) {
+  using namespace wpi::literals;
+  constexpr std::string_view astring = "name";
+  constexpr auto str =
+      wpi::ct_string<char, std::char_traits<char>, astring.size()>{astring} +
+      "[]"_ct_string;
+  static_assert(str.size() == 6);
+  ASSERT_EQ(std::string{str}, "name[]");
+}
+
+TEST(CtStringTest, StringViewConversion) {
+  using namespace wpi::literals;
+  constexpr auto str = "[]"_ct_string;
+  std::string_view sv = str;
+  ASSERT_EQ(sv, "[]");
+}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/future_test.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/future_test.cpp
index 8817f2d..97e5e60 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/future_test.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/future_test.cpp
@@ -4,10 +4,10 @@
 
 #include "wpi/future.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
-
 #include <thread>
 
+#include <gtest/gtest.h>
+
 namespace wpi {
 
 TEST(FutureTest, Then) {
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-algorithms.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-algorithms.cpp
deleted file mode 100644
index f1a8745..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-algorithms.cpp
+++ /dev/null
@@ -1,310 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-#include <algorithm>
-
-class JsonAlgorithmsTest : public ::testing::Test {
- protected:
-    json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"};
-    json j_object = {{"one", 1}, {"two", 2}};
-};
-
-// non-modifying sequence operations
-TEST_F(JsonAlgorithmsTest, AllOf)
-{
-    EXPECT_TRUE(std::all_of(j_array.begin(), j_array.end(), [](const json & value)
-    {
-        return value.size() > 0;
-    }));
-    EXPECT_TRUE(std::all_of(j_object.begin(), j_object.end(), [](const json & value)
-    {
-        return value.type() == json::value_t::number_integer;
-    }));
-}
-
-TEST_F(JsonAlgorithmsTest, AnyOf)
-{
-    EXPECT_TRUE(std::any_of(j_array.begin(), j_array.end(), [](const json & value)
-    {
-        return value.is_string() && value.get<std::string>() == "foo";
-    }));
-    EXPECT_TRUE(std::any_of(j_object.begin(), j_object.end(), [](const json & value)
-    {
-        return value.get<int>() > 1;
-    }));
-}
-
-TEST_F(JsonAlgorithmsTest, NoneOf)
-{
-    EXPECT_TRUE(std::none_of(j_array.begin(), j_array.end(), [](const json & value)
-    {
-        return value.size() == 0;
-    }));
-    EXPECT_TRUE(std::none_of(j_object.begin(), j_object.end(), [](const json & value)
-    {
-        return value.get<int>() <= 0;
-    }));
-}
-
-TEST_F(JsonAlgorithmsTest, ForEachReading)
-{
-    int sum = 0;
-
-    std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value)
-    {
-        if (value.is_number())
-        {
-            sum += static_cast<int>(value);
-        }
-    });
-
-    EXPECT_EQ(sum, 45);
-}
-
-TEST_F(JsonAlgorithmsTest, ForEachWriting)
-{
-    auto add17 = [](json & value)
-    {
-        if (value.is_array())
-        {
-            value.push_back(17);
-        }
-    };
-
-    std::for_each(j_array.begin(), j_array.end(), add17);
-
-    EXPECT_EQ(j_array[6], json({1, 2, 3, 17}));
-}
-
-TEST_F(JsonAlgorithmsTest, Count)
-{
-    EXPECT_EQ(std::count(j_array.begin(), j_array.end(), json(true)), 1);
-}
-
-TEST_F(JsonAlgorithmsTest, CountIf)
-{
-    auto count1 = std::count_if(j_array.begin(), j_array.end(), [](const json & value)
-    {
-        return (value.is_number());
-    });
-    EXPECT_EQ(count1, 3);
-    auto count2 = std::count_if(j_array.begin(), j_array.end(), [](const json&)
-    {
-        return true;
-    });
-    EXPECT_EQ(count2, 9);
-}
-
-TEST_F(JsonAlgorithmsTest, Mismatch)
-{
-    json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"};
-    auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin());
-    EXPECT_EQ(*res.first, json({{"one", 1}, {"two", 2}}));
-    EXPECT_EQ(*res.second, json({{"one", 1}, {"two", 2}, {"three", 3}}));
-}
-
-TEST_F(JsonAlgorithmsTest, EqualOperatorEquals)
-{
-    EXPECT_TRUE(std::equal(j_array.begin(), j_array.end(), j_array.begin()));
-    EXPECT_TRUE(std::equal(j_object.begin(), j_object.end(), j_object.begin()));
-    EXPECT_FALSE(std::equal(j_array.begin(), j_array.end(), j_object.begin()));
-}
-
-TEST_F(JsonAlgorithmsTest, EqualUserComparison)
-{
-    // compare objects only by size of its elements
-    json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"};
-    EXPECT_FALSE(std::equal(j_array.begin(), j_array.end(), j_array2.begin()));
-    EXPECT_TRUE(std::equal(j_array.begin(), j_array.end(), j_array2.begin(),
-                     [](const json & a, const json & b)
-    {
-        return (a.size() == b.size());
-    }));
-}
-
-TEST_F(JsonAlgorithmsTest, Find)
-{
-    auto it = std::find(j_array.begin(), j_array.end(), json(false));
-    EXPECT_EQ(std::distance(j_array.begin(), it), 5);
-}
-
-TEST_F(JsonAlgorithmsTest, FindIf)
-{
-    auto it = std::find_if(j_array.begin(), j_array.end(),
-                           [](const json & value)
-    {
-        return value.is_boolean();
-    });
-    EXPECT_EQ(std::distance(j_array.begin(), it), 4);
-}
-
-TEST_F(JsonAlgorithmsTest, FindIfNot)
-{
-    auto it = std::find_if_not(j_array.begin(), j_array.end(),
-                               [](const json & value)
-    {
-        return value.is_number();
-    });
-    EXPECT_EQ(std::distance(j_array.begin(), it), 3);
-}
-
-TEST_F(JsonAlgorithmsTest, AdjacentFind)
-{
-    EXPECT_EQ(std::adjacent_find(j_array.begin(), j_array.end()), j_array.end());
-    auto it = std::adjacent_find(j_array.begin(), j_array.end(),
-                             [](const json & v1, const json & v2)
-    {
-        return v1.type() == v2.type();
-    });
-    EXPECT_EQ(it, j_array.begin());
-}
-
-// modifying sequence operations
-TEST_F(JsonAlgorithmsTest, Reverse)
-{
-    std::reverse(j_array.begin(), j_array.end());
-    EXPECT_EQ(j_array, json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13}));
-}
-
-TEST_F(JsonAlgorithmsTest, Rotate)
-{
-    std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end());
-    EXPECT_EQ(j_array, json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13}));
-}
-
-TEST_F(JsonAlgorithmsTest, Partition)
-{
-    auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v)
-    {
-        return v.is_string();
-    });
-    EXPECT_EQ(std::distance(j_array.begin(), it), 2);
-    EXPECT_FALSE(it[2].is_string());
-}
-
-// sorting operations
-TEST_F(JsonAlgorithmsTest, SortOperatorEquals)
-{
-    json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr};
-    std::sort(j.begin(), j.end());
-    EXPECT_EQ(j, json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"}));
-}
-
-TEST_F(JsonAlgorithmsTest, SortUserComparison)
-{
-    json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr};
-    std::sort(j.begin(), j.end(), [](const json & a, const json & b)
-    {
-        return a.size() < b.size();
-    });
-    EXPECT_EQ(j, json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}}));
-}
-
-TEST_F(JsonAlgorithmsTest, SortObject)
-{
-    json j({{"one", 1}, {"two", 2}});
-    EXPECT_THROW_MSG(std::sort(j.begin(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-}
-
-TEST_F(JsonAlgorithmsTest, PartialSort)
-{
-    json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr};
-    std::partial_sort(j.begin(), j.begin() + 4, j.end());
-    EXPECT_EQ(j, json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13}));
-}
-
-// set operations
-TEST_F(JsonAlgorithmsTest, Merge)
-{
-    json j1 = {2, 4, 6, 8};
-    json j2 = {1, 2, 3, 5, 7};
-    json j3;
-
-    std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
-    EXPECT_EQ(j3, json({1, 2, 2, 3, 4, 5, 6, 7, 8}));
-}
-
-TEST_F(JsonAlgorithmsTest, SetDifference)
-{
-    json j1 = {1, 2, 3, 4, 5, 6, 7, 8};
-    json j2 = {1, 2, 3, 5, 7};
-    json j3;
-
-    std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
-    EXPECT_EQ(j3, json({4, 6, 8}));
-}
-
-TEST_F(JsonAlgorithmsTest, SetIntersection)
-{
-    json j1 = {1, 2, 3, 4, 5, 6, 7, 8};
-    json j2 = {1, 2, 3, 5, 7};
-    json j3;
-
-    std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
-    EXPECT_EQ(j3, json({1, 2, 3, 5, 7}));
-}
-
-TEST_F(JsonAlgorithmsTest, SetUnion)
-{
-    json j1 = {2, 4, 6, 8};
-    json j2 = {1, 2, 3, 5, 7};
-    json j3;
-
-    std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
-    EXPECT_EQ(j3, json({1, 2, 3, 4, 5, 6, 7, 8}));
-}
-
-TEST_F(JsonAlgorithmsTest, SetSymmetricDifference)
-{
-    json j1 = {2, 4, 6, 8};
-    json j2 = {1, 2, 3, 5, 7};
-    json j3;
-
-    std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
-    EXPECT_EQ(j3, json({1, 3, 4, 5, 6, 7, 8}));
-}
-
-TEST_F(JsonAlgorithmsTest, HeapOperations)
-{
-    std::make_heap(j_array.begin(), j_array.end());
-    EXPECT_TRUE(std::is_heap(j_array.begin(), j_array.end()));
-    std::sort_heap(j_array.begin(), j_array.end());
-    EXPECT_EQ(j_array, json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"}));
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-capacity.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-capacity.cpp
deleted file mode 100644
index 8f5433e..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-capacity.cpp
+++ /dev/null
@@ -1,528 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-TEST(JsonEmptyTest, Boolean)
-{
-    json j = true;
-    json j_const(j);
-
-    // result of empty
-    {
-        EXPECT_FALSE(j.empty());
-        EXPECT_FALSE(j_const.empty());
-    }
-
-    // definition of empty
-    {
-        EXPECT_EQ(j.empty(), (j.begin() == j.end()));
-        EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
-    }
-}
-
-TEST(JsonEmptyTest, String)
-{
-    json j = "hello world";
-    json j_const(j);
-
-    // result of empty
-    {
-        EXPECT_FALSE(j.empty());
-        EXPECT_FALSE(j_const.empty());
-    }
-
-    // definition of empty
-    {
-        EXPECT_EQ(j.empty(), (j.begin() == j.end()));
-        EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
-    }
-}
-
-TEST(JsonEmptyTest, ArrayEmpty)
-{
-    json j = json::array();
-    json j_const(j);
-
-    // result of empty
-    {
-        EXPECT_TRUE(j.empty());
-        EXPECT_TRUE(j_const.empty());
-    }
-
-    // definition of empty
-    {
-        EXPECT_EQ(j.empty(), (j.begin() == j.end()));
-        EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
-    }
-}
-
-TEST(JsonEmptyTest, ArrayFilled)
-{
-    json j = {1, 2, 3};
-    json j_const(j);
-
-    // result of empty
-    {
-        EXPECT_FALSE(j.empty());
-        EXPECT_FALSE(j_const.empty());
-    }
-
-    // definition of empty
-    {
-        EXPECT_EQ(j.empty(), (j.begin() == j.end()));
-        EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
-    }
-}
-
-TEST(JsonEmptyTest, ObjectEmpty)
-{
-    json j = json::object();
-    json j_const(j);
-
-    // result of empty
-    {
-        EXPECT_TRUE(j.empty());
-        EXPECT_TRUE(j_const.empty());
-    }
-
-    // definition of empty
-    {
-        EXPECT_EQ(j.empty(), (j.begin() == j.end()));
-        EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
-    }
-}
-
-TEST(JsonEmptyTest, ObjectFilled)
-{
-    json j = {{"one", 1}, {"two", 2}, {"three", 3}};
-    json j_const(j);
-
-    // result of empty
-    {
-        EXPECT_FALSE(j.empty());
-        EXPECT_FALSE(j_const.empty());
-    }
-
-    // definition of empty
-    {
-        EXPECT_EQ(j.empty(), (j.begin() == j.end()));
-        EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
-    }
-}
-
-TEST(JsonEmptyTest, NumberInteger)
-{
-    json j = 23;
-    json j_const(j);
-
-    // result of empty
-    {
-        EXPECT_FALSE(j.empty());
-        EXPECT_FALSE(j_const.empty());
-    }
-
-    // definition of empty
-    {
-        EXPECT_EQ(j.empty(), (j.begin() == j.end()));
-        EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
-    }
-}
-
-TEST(JsonEmptyTest, NumberUnsigned)
-{
-    json j = 23u;
-    json j_const(j);
-
-    // result of empty
-    {
-        EXPECT_FALSE(j.empty());
-        EXPECT_FALSE(j_const.empty());
-    }
-
-    // definition of empty
-    {
-        EXPECT_EQ(j.empty(), (j.begin() == j.end()));
-        EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
-    }
-}
-
-TEST(JsonEmptyTest, NumberFloat)
-{
-    json j = 23.42;
-    json j_const(j);
-
-    // result of empty
-    {
-        EXPECT_FALSE(j.empty());
-        EXPECT_FALSE(j_const.empty());
-    }
-
-    // definition of empty
-    {
-        EXPECT_EQ(j.empty(), (j.begin() == j.end()));
-        EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
-    }
-}
-
-TEST(JsonEmptyTest, Null)
-{
-    json j = nullptr;
-    json j_const(j);
-
-    // result of empty
-    {
-        EXPECT_TRUE(j.empty());
-        EXPECT_TRUE(j_const.empty());
-    }
-
-    // definition of empty
-    {
-        EXPECT_EQ(j.empty(), (j.begin() == j.end()));
-        EXPECT_EQ(j_const.empty(), (j_const.begin() == j_const.end()));
-    }
-}
-
-TEST(JsonSizeTest, Boolean)
-{
-    json j = true;
-    json j_const(j);
-
-    // result of size
-    {
-        EXPECT_EQ(j.size(), 1u);
-        EXPECT_EQ(j_const.size(), 1u);
-    }
-
-    // definition of size
-    {
-        EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
-        EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
-                  static_cast<int>(j_const.size()));
-    }
-}
-
-TEST(JsonSizeTest, String)
-{
-    json j = "hello world";
-    json j_const(j);
-
-    // result of size
-    {
-        EXPECT_EQ(j.size(), 1u);
-        EXPECT_EQ(j_const.size(), 1u);
-    }
-
-    // definition of size
-    {
-        EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
-        EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
-                  static_cast<int>(j_const.size()));
-    }
-}
-
-TEST(JsonSizeTest, ArrayEmpty)
-{
-    json j = json::array();
-    json j_const(j);
-
-    // result of size
-    {
-        EXPECT_EQ(j.size(), 0u);
-        EXPECT_EQ(j_const.size(), 0u);
-    }
-
-    // definition of size
-    {
-        EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
-        EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
-                  static_cast<int>(j_const.size()));
-    }
-}
-
-TEST(JsonSizeTest, ArrayFilled)
-{
-    json j = {1, 2, 3};
-    json j_const(j);
-
-    // result of size
-    {
-        EXPECT_EQ(j.size(), 3u);
-        EXPECT_EQ(j_const.size(), 3u);
-    }
-
-    // definition of size
-    {
-        EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
-        EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
-                  static_cast<int>(j_const.size()));
-    }
-}
-
-TEST(JsonSizeTest, ObjectEmpty)
-{
-    json j = json::object();
-    json j_const(j);
-
-    // result of size
-    {
-        EXPECT_EQ(j.size(), 0u);
-        EXPECT_EQ(j_const.size(), 0u);
-    }
-
-    // definition of size
-    {
-        EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
-        EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
-                  static_cast<int>(j_const.size()));
-    }
-}
-
-TEST(JsonSizeTest, ObjectFilled)
-{
-    json j = {{"one", 1}, {"two", 2}, {"three", 3}};
-    json j_const(j);
-
-    // result of size
-    {
-        EXPECT_EQ(j.size(), 3u);
-        EXPECT_EQ(j_const.size(), 3u);
-    }
-
-    // definition of size
-    {
-        EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
-        EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
-                  static_cast<int>(j_const.size()));
-    }
-}
-
-TEST(JsonSizeTest, NumberInteger)
-{
-    json j = 23;
-    json j_const(j);
-
-    // result of size
-    {
-        EXPECT_EQ(j.size(), 1u);
-        EXPECT_EQ(j_const.size(), 1u);
-    }
-
-    // definition of size
-    {
-        EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
-        EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
-                  static_cast<int>(j_const.size()));
-    }
-}
-
-TEST(JsonSizeTest, NumberUnsigned)
-{
-    json j = 23u;
-    json j_const(j);
-
-    // result of size
-    {
-        EXPECT_EQ(j.size(), 1u);
-        EXPECT_EQ(j_const.size(), 1u);
-    }
-
-    // definition of size
-    {
-        EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
-        EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
-                  static_cast<int>(j_const.size()));
-    }
-}
-
-TEST(JsonSizeTest, NumberFloat)
-{
-    json j = 23.42;
-    json j_const(j);
-
-    // result of size
-    {
-        EXPECT_EQ(j.size(), 1u);
-        EXPECT_EQ(j_const.size(), 1u);
-    }
-
-    // definition of size
-    {
-        EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
-        EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
-                  static_cast<int>(j_const.size()));
-    }
-}
-
-TEST(JsonSizeTest, Null)
-{
-    json j = nullptr;
-    json j_const(j);
-
-    // result of size
-    {
-        EXPECT_EQ(j.size(), 0u);
-        EXPECT_EQ(j_const.size(), 0u);
-    }
-
-    // definition of size
-    {
-        EXPECT_EQ(std::distance(j.begin(), j.end()), static_cast<int>(j.size()));
-        EXPECT_EQ(std::distance(j_const.begin(), j_const.end()),
-                  static_cast<int>(j_const.size()));
-    }
-}
-
-TEST(JsonMaxSizeTest, Boolean)
-{
-    json j = true;
-    json j_const(j);
-
-    // result of max_size
-    {
-        EXPECT_EQ(j.max_size(), 1u);
-        EXPECT_EQ(j_const.max_size(), 1u);
-    }
-}
-
-TEST(JsonMaxSizeTest, String)
-{
-    json j = "hello world";
-    json j_const(j);
-
-    // result of max_size
-    {
-        EXPECT_EQ(j.max_size(), 1u);
-        EXPECT_EQ(j_const.max_size(), 1u);
-    }
-}
-
-TEST(JsonMaxSizeTest, ArrayEmpty)
-{
-    json j = json::array();
-    json j_const(j);
-
-    // result of max_size
-    {
-        EXPECT_GE(j.max_size(), j.size());
-        EXPECT_GE(j_const.max_size(), j_const.size());
-    }
-}
-
-TEST(JsonMaxSizeTest, ArrayFilled)
-{
-    json j = {1, 2, 3};
-    json j_const(j);
-
-    // result of max_size
-    {
-        EXPECT_GE(j.max_size(), j.size());
-        EXPECT_GE(j_const.max_size(), j_const.size());
-    }
-}
-
-TEST(JsonMaxSizeTest, ObjectEmpty)
-{
-    json j = json::object();
-    json j_const(j);
-
-    // result of max_size
-    {
-        EXPECT_GE(j.max_size(), j.size());
-        EXPECT_GE(j_const.max_size(), j_const.size());
-    }
-}
-
-TEST(JsonMaxSizeTest, ObjectFilled)
-{
-    json j = {{"one", 1}, {"two", 2}, {"three", 3}};
-    json j_const(j);
-
-    // result of max_size
-    {
-        EXPECT_GE(j.max_size(), j.size());
-        EXPECT_GE(j_const.max_size(), j_const.size());
-    }
-}
-
-TEST(JsonMaxSizeTest, NumberInteger)
-{
-    json j = 23;
-    json j_const(j);
-
-    // result of max_size
-    {
-        EXPECT_EQ(j.max_size(), 1u);
-        EXPECT_EQ(j_const.max_size(), 1u);
-    }
-}
-
-TEST(JsonMaxSizeTest, NumberUnsigned)
-{
-    json j = 23u;
-    json j_const(j);
-
-    // result of max_size
-    {
-        EXPECT_EQ(j.max_size(), 1u);
-        EXPECT_EQ(j_const.max_size(), 1u);
-    }
-}
-
-TEST(JsonMaxSizeTest, NumberFloat)
-{
-    json j = 23.42;
-    json j_const(j);
-
-    // result of max_size
-    {
-        EXPECT_EQ(j.max_size(), 1u);
-        EXPECT_EQ(j_const.max_size(), 1u);
-    }
-}
-
-TEST(JsonMaxSizeTest, Null)
-{
-    json j = nullptr;
-    json j_const(j);
-
-    // result of max_size
-    {
-        EXPECT_EQ(j.max_size(), 0u);
-        EXPECT_EQ(j_const.max_size(), 0u);
-    }
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-cbor.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-cbor.cpp
deleted file mode 100644
index dfb3b60..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-cbor.cpp
+++ /dev/null
@@ -1,1704 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-
-#include <cmath>
-
-using wpi::json;
-
-#include <fstream>
-
-TEST(CborDiscardedTest, Case)
-{
-    // discarded values are not serialized
-    json j = json::value_t::discarded;
-    const auto result = json::to_cbor(j);
-    ASSERT_TRUE(result.empty());
-}
-
-TEST(CborNullTest, Case)
-{
-    json j = nullptr;
-    std::vector<uint8_t> expected = {0xf6};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-TEST(CborBooleanTest, True)
-{
-    json j = true;
-    std::vector<uint8_t> expected = {0xf5};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-TEST(CborBooleanTest, False)
-{
-    json j = false;
-    std::vector<uint8_t> expected = {0xf4};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// -9223372036854775808..-4294967297
-class CborSignedNeg8Test : public ::testing::TestWithParam<int64_t> {};
-TEST_P(CborSignedNeg8Test, Case)
-{
-    // create JSON value with integer number
-    json j = GetParam();
-
-    // check type
-    ASSERT_TRUE(j.is_number_integer());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(static_cast<uint8_t>(0x3b));
-    uint64_t positive = static_cast<uint64_t>(-1 - GetParam());
-    expected.push_back(static_cast<uint8_t>((positive >> 56) & 0xff));
-    expected.push_back(static_cast<uint8_t>((positive >> 48) & 0xff));
-    expected.push_back(static_cast<uint8_t>((positive >> 40) & 0xff));
-    expected.push_back(static_cast<uint8_t>((positive >> 32) & 0xff));
-    expected.push_back(static_cast<uint8_t>((positive >> 24) & 0xff));
-    expected.push_back(static_cast<uint8_t>((positive >> 16) & 0xff));
-    expected.push_back(static_cast<uint8_t>((positive >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(positive & 0xff));
-
-    // compare result + size
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-    ASSERT_EQ(result.size(), 9u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], 0x3b);
-    uint64_t restored = (static_cast<uint64_t>(static_cast<uint8_t>(result[1])) << 070) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[2])) << 060) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[3])) << 050) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[4])) << 040) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[5])) << 030) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[6])) << 020) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[7])) << 010) +
-                        static_cast<uint64_t>(static_cast<uint8_t>(result[8]));
-    EXPECT_EQ(restored, positive);
-    EXPECT_EQ(-1 - static_cast<int64_t>(restored), GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-static const int64_t neg8_numbers[] = {
-    INT64_MIN,
-    -1000000000000000000,
-    -100000000000000000,
-    -10000000000000000,
-    -1000000000000000,
-    -100000000000000,
-    -10000000000000,
-    -1000000000000,
-    -100000000000,
-    -10000000000,
-    -4294967297,
-};
-
-INSTANTIATE_TEST_SUITE_P(CborSignedNeg8Tests, CborSignedNeg8Test,
-                        ::testing::ValuesIn(neg8_numbers));
-
-// -4294967296..-65537
-class CborSignedNeg4Test : public ::testing::TestWithParam<int64_t> {};
-TEST_P(CborSignedNeg4Test, Case)
-{
-    // create JSON value with integer number
-    json j = GetParam();
-
-    // check type
-    ASSERT_TRUE(j.is_number_integer());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(static_cast<uint8_t>(0x3a));
-    uint32_t positive = static_cast<uint32_t>(static_cast<uint64_t>(-1 - GetParam()) & 0x00000000ffffffff);
-    expected.push_back(static_cast<uint8_t>((positive >> 24) & 0xff));
-    expected.push_back(static_cast<uint8_t>((positive >> 16) & 0xff));
-    expected.push_back(static_cast<uint8_t>((positive >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(positive & 0xff));
-
-    // compare result + size
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-    ASSERT_EQ(result.size(), 5u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], 0x3a);
-    uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
-                        static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
-    EXPECT_EQ(restored, positive);
-    EXPECT_EQ(-1ll - restored, GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-static const int64_t neg4_numbers[] = {
-    -65537,
-    -100000,
-    -1000000,
-    -10000000,
-    -100000000,
-    -1000000000,
-    -4294967296,
-};
-
-INSTANTIATE_TEST_SUITE_P(CborSignedNeg4Tests, CborSignedNeg4Test,
-                        ::testing::ValuesIn(neg4_numbers));
-
-// -65536..-257
-TEST(CborSignedTest, Neg2)
-{
-    for (int32_t i = -65536; i <= -257; ++i) {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = i;
-
-        // check type
-        ASSERT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0x39));
-        uint16_t positive = static_cast<uint16_t>(-1 - i);
-        expected.push_back(static_cast<uint8_t>((positive >> 8) & 0xff));
-        expected.push_back(static_cast<uint8_t>(positive & 0xff));
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), 3u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], 0x39);
-        uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
-        EXPECT_EQ(restored, positive);
-        EXPECT_EQ(-1 - restored, i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// -9263 (int 16)
-TEST(CborSignedTest, NegInt16)
-{
-    json j = -9263;
-    std::vector<uint8_t> expected = {0x39,0x24,0x2e};
-
-    const auto result = json::to_cbor(j);
-    ASSERT_EQ(result, expected);
-
-    int16_t restored = static_cast<int16_t>(-1 - ((result[1] << 8) + result[2]));
-    EXPECT_EQ(restored, -9263);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// -256..-24
-TEST(CborSignedTest, Neg1)
-{
-    for (auto i = -256; i < -24; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = i;
-
-        // check type
-        ASSERT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0x38));
-        expected.push_back(static_cast<uint8_t>(-1 - i));
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), 2u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], 0x38);
-        EXPECT_EQ(static_cast<int16_t>(-1 - static_cast<uint8_t>(result[1])), i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// -24..-1
-TEST(CborSignedTest, Neg0)
-{
-    for (auto i = -24; i <= -1; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = i;
-
-        // check type
-        ASSERT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0x20 - 1 - static_cast<uint8_t>(i)));
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), 1u);
-
-        // check individual bytes
-        EXPECT_EQ(static_cast<int8_t>(0x20 - 1 - result[0]), i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// 0..23
-TEST(CborSignedTest, Pos0)
-{
-    for (size_t i = 0; i <= 23; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = -1;
-        j.get_ref<int64_t&>() = static_cast<int64_t>(i);
-
-        // check type
-        ASSERT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(i));
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), 1u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], static_cast<uint8_t>(i));
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// 24..255
-TEST(CborSignedTest, Pos1)
-{
-    for (size_t i = 24; i <= 255; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = -1;
-        j.get_ref<int64_t&>() = static_cast<int64_t>(i);
-
-        // check type
-        ASSERT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0x18));
-        expected.push_back(static_cast<uint8_t>(i));
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), 2u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], 0x18);
-        EXPECT_EQ(result[1], static_cast<uint8_t>(i));
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// 256..65535
-TEST(CborSignedTest, Pos2)
-{
-    for (size_t i = 256; i <= 65535; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = -1;
-        j.get_ref<int64_t&>() = static_cast<int64_t>(i);
-
-        // check type
-        ASSERT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0x19));
-        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
-        expected.push_back(static_cast<uint8_t>(i & 0xff));
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), 3u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], 0x19);
-        uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
-        EXPECT_EQ(restored, i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// 65536..4294967295
-class CborSignedPos4Test : public ::testing::TestWithParam<uint32_t> {};
-TEST_P(CborSignedPos4Test, Case)
-{
-    // create JSON value with integer number
-    json j = -1;
-    j.get_ref<int64_t&>() =
-        static_cast<int64_t>(GetParam());
-
-    // check type
-    ASSERT_TRUE(j.is_number_integer());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(0x1a);
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-
-    // compare result + size
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-    ASSERT_EQ(result.size(), 5u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], 0x1a);
-    uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
-                        static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
-    EXPECT_EQ(restored, GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-static const uint32_t pos4_numbers[] = {
-    65536u,
-    77777u,
-    1048576u,
-};
-
-INSTANTIATE_TEST_SUITE_P(CborSignedPos4Tests, CborSignedPos4Test,
-                        ::testing::ValuesIn(pos4_numbers));
-
-// 4294967296..4611686018427387903
-class CborSignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
-TEST_P(CborSignedPos8Test, Case)
-{
-    // create JSON value with integer number
-    json j = -1;
-    j.get_ref<int64_t&>() =
-        static_cast<int64_t>(GetParam());
-
-    // check type
-    ASSERT_TRUE(j.is_number_integer());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(0x1b);
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 070) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 060) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 050) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 040) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 030) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 020) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 010) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-
-    // compare result + size
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-    ASSERT_EQ(result.size(), 9u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], 0x1b);
-    uint64_t restored = (static_cast<uint64_t>(static_cast<uint8_t>(result[1])) << 070) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[2])) << 060) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[3])) << 050) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[4])) << 040) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[5])) << 030) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[6])) << 020) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[7])) << 010) +
-                        static_cast<uint64_t>(static_cast<uint8_t>(result[8]));
-    EXPECT_EQ(restored, GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-static const uint64_t pos8_numbers[] = {
-    4294967296ul,
-    4611686018427387903ul
-};
-
-INSTANTIATE_TEST_SUITE_P(CborSignedPos8Tests, CborSignedPos8Test,
-                        ::testing::ValuesIn(pos8_numbers));
-
-/*
-// -32768..-129 (int 16)
-{
-    for (int16_t i = -32768; i <= -129; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = i;
-
-        // check type
-        ASSERT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(0xd1);
-        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
-        expected.push_back(static_cast<uint8_t>(i & 0xff));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result == expected);
-        ASSERT_EQ(result.size(), 3u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], 0xd1);
-        int16_t restored = (result[1] << 8) + result[2];
-        EXPECT_EQ(restored, i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-*/
-
-// 0..23 (Integer)
-TEST(CborUnsignedTest, Pos0)
-{
-    for (size_t i = 0; i <= 23; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with unsigned integer number
-        json j = i;
-
-        // check type
-        ASSERT_TRUE(j.is_number_unsigned());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(i));
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), 1u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], static_cast<uint8_t>(i));
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// 24..255 (one-byte uint8_t)
-TEST(CborUnsignedTest, Pos1)
-{
-    for (size_t i = 24; i <= 255; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with unsigned integer number
-        json j = i;
-
-        // check type
-        ASSERT_TRUE(j.is_number_unsigned());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(0x18);
-        expected.push_back(static_cast<uint8_t>(i));
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), 2u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], 0x18);
-        uint8_t restored = static_cast<uint8_t>(result[1]);
-        EXPECT_EQ(restored, i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// 256..65535 (two-byte uint16_t)
-TEST(CborUnsignedTest, Pos2)
-{
-    for (size_t i = 256; i <= 65535; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with unsigned integer number
-        json j = i;
-
-        // check type
-        ASSERT_TRUE(j.is_number_unsigned());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(0x19);
-        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
-        expected.push_back(static_cast<uint8_t>(i & 0xff));
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), 3u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], 0x19);
-        uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
-        EXPECT_EQ(restored, i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// 65536..4294967295 (four-byte uint32_t)
-class CborUnsignedPos4Test : public ::testing::TestWithParam<uint32_t> {};
-TEST_P(CborUnsignedPos4Test, Case)
-{
-    // create JSON value with unsigned integer number
-    json j = GetParam();
-
-    // check type
-    ASSERT_TRUE(j.is_number_unsigned());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(0x1a);
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-
-    // compare result + size
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-    ASSERT_EQ(result.size(), 5u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], 0x1a);
-    uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
-                        static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
-    EXPECT_EQ(restored, GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-INSTANTIATE_TEST_SUITE_P(CborUnsignedPos4Tests, CborUnsignedPos4Test,
-                        ::testing::ValuesIn(pos4_numbers));
-
-// 4294967296..4611686018427387903 (eight-byte uint64_t)
-class CborUnsignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
-TEST_P(CborUnsignedPos8Test, Case)
-{
-    // create JSON value with integer number
-    json j = GetParam();
-
-    // check type
-    ASSERT_TRUE(j.is_number_unsigned());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(0x1b);
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 070) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 060) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 050) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 040) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 030) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 020) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 010) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-
-    // compare result + size
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-    ASSERT_EQ(result.size(), 9u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], 0x1b);
-    uint64_t restored = (static_cast<uint64_t>(static_cast<uint8_t>(result[1])) << 070) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[2])) << 060) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[3])) << 050) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[4])) << 040) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[5])) << 030) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[6])) << 020) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[7])) << 010) +
-                        static_cast<uint64_t>(static_cast<uint8_t>(result[8]));
-    EXPECT_EQ(restored, GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-INSTANTIATE_TEST_SUITE_P(CborUnsignedPos8Tests, CborUnsignedPos8Test,
-                        ::testing::ValuesIn(pos8_numbers));
-
-// 3.1415925
-TEST(CborFloatTest, Number)
-{
-    double v = 3.1415925;
-    json j = v;
-    std::vector<uint8_t> expected = {0xfb,0x40,0x09,0x21,0xfb,0x3f,0xa6,0xde,0xfc};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-    EXPECT_EQ(json::from_cbor(result), v);
-}
-
-TEST(CborFloatTest, HalfInfinity)
-{
-    json j = json::from_cbor(std::vector<uint8_t>({0xf9,0x7c,0x00}));
-    double d = j;
-    EXPECT_FALSE(std::isfinite(d));
-    EXPECT_EQ(j.dump(), "null");
-}
-
-TEST(CborFloatTest, HalfNaN)
-{
-    json j = json::from_cbor(std::vector<uint8_t>({0xf9,0x7c,0x01}));
-    double d = j;
-    EXPECT_TRUE(std::isnan(d));
-    EXPECT_EQ(j.dump(), "null");
-}
-
-// N = 0..23
-TEST(CborStringTest, String1)
-{
-    for (size_t N = 0; N <= 0x17; ++N)
-    {
-        SCOPED_TRACE(N);
-
-        // create JSON value with string containing of N * 'x'
-        const auto s = std::string(N, 'x');
-        json j = s;
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0x60 + N));
-        for (size_t i = 0; i < N; ++i)
-        {
-            expected.push_back('x');
-        }
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), N + 1);
-        // check that no null byte is appended
-        if (N > 0)
-        {
-            EXPECT_NE(result.back(), '\x00');
-        }
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// N = 24..255
-TEST(CborStringTest, String2)
-{
-    for (size_t N = 24; N <= 255; ++N)
-    {
-        SCOPED_TRACE(N);
-
-        // create JSON value with string containing of N * 'x'
-        const auto s = std::string(N, 'x');
-        json j = s;
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0x78));
-        expected.push_back(static_cast<uint8_t>(N));
-        for (size_t i = 0; i < N; ++i)
-        {
-            expected.push_back('x');
-        }
-
-        // compare result + size
-        const auto result = json::to_cbor(j);
-        EXPECT_EQ(result, expected);
-        ASSERT_EQ(result.size(), N + 2);
-        // check that no null byte is appended
-        EXPECT_NE(result.back(), '\x00');
-
-        // roundtrip
-        EXPECT_EQ(json::from_cbor(result), j);
-    }
-}
-
-// N = 256..65535
-class CborString3Test : public ::testing::TestWithParam<size_t> {};
-TEST_P(CborString3Test, Case)
-{
-    // create JSON value with string containing of N * 'x'
-    const auto s = std::string(GetParam(), 'x');
-    json j = s;
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(0x79);
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-    for (size_t i = 0; i < GetParam(); ++i)
-    {
-        expected.push_back('x');
-    }
-
-    // compare result + size
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-    ASSERT_EQ(result.size(), GetParam() + 3);
-    // check that no null byte is appended
-    EXPECT_NE(result.back(), '\x00');
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-static size_t string3_lens[] = {
-    256u,
-    999u,
-    1025u,
-    3333u,
-    2048u,
-    65535u
-};
-
-INSTANTIATE_TEST_SUITE_P(CborString3Tests, CborString3Test,
-                        ::testing::ValuesIn(string3_lens));
-
-// N = 65536..4294967295
-class CborString5Test : public ::testing::TestWithParam<size_t> {};
-TEST_P(CborString5Test, Case)
-{
-    // create JSON value with string containing of N * 'x'
-    const auto s = std::string(GetParam(), 'x');
-    json j = s;
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(0x7a);
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-    for (size_t i = 0; i < GetParam(); ++i)
-    {
-        expected.push_back('x');
-    }
-
-    // compare result + size
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-    ASSERT_EQ(result.size(), GetParam() + 5);
-    // check that no null byte is appended
-    EXPECT_NE(result.back(), '\x00');
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-static size_t string5_lens[] = {
-    65536u,
-    77777u,
-    1048576u
-};
-
-INSTANTIATE_TEST_SUITE_P(CborString5Tests, CborString5Test,
-                        ::testing::ValuesIn(string5_lens));
-
-TEST(CborArrayTest, Empty)
-{
-    json j = json::array();
-    std::vector<uint8_t> expected = {0x80};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// [null]
-TEST(CborArrayTest, Null)
-{
-    json j = {nullptr};
-    std::vector<uint8_t> expected = {0x81,0xf6};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// [1,2,3,4,5]
-TEST(CborArrayTest, Simple)
-{
-    json j = json::parse("[1,2,3,4,5]");
-    std::vector<uint8_t> expected = {0x85,0x01,0x02,0x03,0x04,0x05};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// [[[[]]]]
-TEST(CborArrayTest, NestEmpty)
-{
-    json j = json::parse("[[[[]]]]");
-    std::vector<uint8_t> expected = {0x81,0x81,0x81,0x80};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// array with uint16_t elements
-TEST(CborArrayTest, UInt16)
-{
-    json j(257, nullptr);
-    std::vector<uint8_t> expected(j.size() + 3, 0xf6); // all null
-    expected[0] = static_cast<uint8_t>(0x99); // array 16 bit
-    expected[1] = 0x01; // size (0x0101), byte 0
-    expected[2] = 0x01; // size (0x0101), byte 1
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// array with uint32_t elements
-TEST(CborArrayTest, UInt32)
-{
-    json j(65793, nullptr);
-    std::vector<uint8_t> expected(j.size() + 5, 0xf6); // all null
-    expected[0] = static_cast<uint8_t>(0x9a); // array 32 bit
-    expected[1] = 0x00; // size (0x00010101), byte 0
-    expected[2] = 0x01; // size (0x00010101), byte 1
-    expected[3] = 0x01; // size (0x00010101), byte 2
-    expected[4] = 0x01; // size (0x00010101), byte 3
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-/*
-// array with uint64_t elements
-TEST(CborArrayTest, UInt64)
-{
-    json j(4294967296, nullptr);
-    std::vector<uint8_t> expected(j.size() + 9, 0xf6); // all null
-    expected[0] = 0x9b; // array 64 bit
-    expected[1] = 0x00; // size (0x0000000100000000), byte 0
-    expected[2] = 0x00; // size (0x0000000100000000), byte 1
-    expected[3] = 0x00; // size (0x0000000100000000), byte 2
-    expected[4] = 0x01; // size (0x0000000100000000), byte 3
-    expected[5] = 0x00; // size (0x0000000100000000), byte 4
-    expected[6] = 0x00; // size (0x0000000100000000), byte 5
-    expected[7] = 0x00; // size (0x0000000100000000), byte 6
-    expected[8] = 0x00; // size (0x0000000100000000), byte 7
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-*/
-
-TEST(CborObjectTest, Empty)
-{
-    json j = json::object();
-    std::vector<uint8_t> expected = {0xa0};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// {"":null}
-TEST(CborObjectTest, EmptyKey)
-{
-    json j = {{"", nullptr}};
-    std::vector<uint8_t> expected = {0xa1,0x60,0xf6};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// {"a": {"b": {"c": {}}}}
-TEST(CborObjectTest, NestedEmpty)
-{
-    json j = json::parse("{\"a\": {\"b\": {\"c\": {}}}}");
-    std::vector<uint8_t> expected = {0xa1,0x61,0x61,0xa1,0x61,0x62,0xa1,0x61,0x63,0xa0};
-    const auto result = json::to_cbor(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// object with uint8_t elements
-TEST(CborObjectTest, UInt8)
-{
-    json j;
-    for (auto i = 0; i < 255; ++i)
-    {
-        // format i to a fixed width of 5
-        // each entry will need 7 bytes: 6 for string, 1 for null
-        std::stringstream ss;
-        ss << std::setw(5) << std::setfill('0') << i;
-        j.emplace(ss.str(), nullptr);
-    }
-
-    const auto result = json::to_cbor(j);
-
-    // Checking against an expected vector byte by byte is
-    // difficult, because no assumption on the order of key/value
-    // pairs are made. We therefore only check the prefix (type and
-    // size and the overall size. The rest is then handled in the
-    // roundtrip check.
-    ASSERT_EQ(result.size(), 1787u); // 1 type, 1 size, 255*7 content
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xb8)); // map 8 bit
-    EXPECT_EQ(result[1], static_cast<uint8_t>(0xff)); // size byte (0xff)
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// object with uint16_t elements
-TEST(CborObjectTest, UInt16)
-{
-    json j;
-    for (auto i = 0; i < 256; ++i)
-    {
-        // format i to a fixed width of 5
-        // each entry will need 7 bytes: 6 for string, 1 for null
-        std::stringstream ss;
-        ss << std::setw(5) << std::setfill('0') << i;
-        j.emplace(ss.str(), nullptr);
-    }
-
-    const auto result = json::to_cbor(j);
-
-    // Checking against an expected vector byte by byte is
-    // difficult, because no assumption on the order of key/value
-    // pairs are made. We therefore only check the prefix (type and
-    // size and the overall size. The rest is then handled in the
-    // roundtrip check.
-    ASSERT_EQ(result.size(), 1795u); // 1 type, 2 size, 256*7 content
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xb9)); // map 16 bit
-    EXPECT_EQ(result[1], 0x01); // byte 0 of size (0x0100)
-    EXPECT_EQ(result[2], 0x00); // byte 1 of size (0x0100)
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// object with uint32_t elements
-TEST(CborObjectTest, UInt32)
-{
-    json j;
-    for (auto i = 0; i < 65536; ++i)
-    {
-        // format i to a fixed width of 5
-        // each entry will need 7 bytes: 6 for string, 1 for null
-        std::stringstream ss;
-        ss << std::setw(5) << std::setfill('0') << i;
-        j.emplace(ss.str(), nullptr);
-    }
-
-    const auto result = json::to_cbor(j);
-
-    // Checking against an expected vector byte by byte is
-    // difficult, because no assumption on the order of key/value
-    // pairs are made. We therefore only check the prefix (type and
-    // size and the overall size. The rest is then handled in the
-    // roundtrip check.
-    ASSERT_EQ(result.size(), 458757u); // 1 type, 4 size, 65536*7 content
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xba)); // map 32 bit
-    EXPECT_EQ(result[1], 0x00); // byte 0 of size (0x00010000)
-    EXPECT_EQ(result[2], 0x01); // byte 1 of size (0x00010000)
-    EXPECT_EQ(result[3], 0x00); // byte 2 of size (0x00010000)
-    EXPECT_EQ(result[4], 0x00); // byte 3 of size (0x00010000)
-
-    // roundtrip
-    EXPECT_EQ(json::from_cbor(result), j);
-}
-
-// 0x7b (string)
-TEST(CborAdditionalDeserializationTest, Case7b)
-{
-    std::vector<uint8_t> given{0x7b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x61};
-    json j = json::from_cbor(given);
-    EXPECT_EQ(j, "a");
-}
-
-// 0x9b (array)
-TEST(CborAdditionalDeserializationTest, Case9b)
-{
-    std::vector<uint8_t> given{0x9b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xf4};
-    json j = json::from_cbor(given);
-    EXPECT_EQ(j, json::parse("[false]"));
-}
-
-// 0xbb (map)
-TEST(CborAdditionalDeserializationTest, Casebb)
-{
-    std::vector<uint8_t> given{0xbb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x60,0xf4};
-    json j = json::from_cbor(given);
-    EXPECT_EQ(j, json::parse("{\"\": false}"));
-}
-
-TEST(CborErrorTest, TooShortByteVector)
-{
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x18})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x19})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x19,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1a})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1a,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1a,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1a,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 5: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 5: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 6: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 7: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 8: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1b,0x00,0x00,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 9: unexpected end of input");
-}
-
-TEST(CborErrorTest, UnsupportedBytesConcrete)
-{
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0x1c})), json::parse_error,
-                     "[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0x1c");
-    EXPECT_THROW_MSG(json::from_cbor(std::vector<uint8_t>({0xf8})), json::parse_error,
-                     "[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0xf8");
-}
-
-class CborUnsupportedBytesTest : public ::testing::TestWithParam<uint8_t> {
-};
-TEST_P(CborUnsupportedBytesTest, Case)
-{
-    EXPECT_THROW(json::from_cbor(std::vector<uint8_t>({GetParam()})), json::parse_error);
-}
-
-static const uint8_t unsupported_bytes_cases[] = {
-    // ?
-    0x1c, 0x1d, 0x1e, 0x1f,
-    // ?
-    0x3c, 0x3d, 0x3e, 0x3f,
-    // byte strings
-    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
-    // byte strings
-    0x58, 0x59, 0x5a, 0x5b,
-    // ?
-    0x5c, 0x5d, 0x5e,
-    // byte string
-    0x5f,
-    // ?
-    0x7c, 0x7d, 0x7e,
-    // ?
-    0x9c, 0x9d, 0x9e,
-    // ?
-    0xbc, 0xbd, 0xbe,
-    // date/time
-    0xc0, 0xc1,
-    // bignum
-    0xc2, 0xc3,
-    // fraction
-    0xc4,
-    // bigfloat
-    0xc5,
-    // tagged item
-    0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4,
-    // expected conversion
-    0xd5, 0xd6, 0xd7,
-    // more tagged items
-    0xd8, 0xd9, 0xda, 0xdb,
-    // ?
-    0xdc, 0xdd, 0xde, 0xdf,
-    // (simple value)
-    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3,
-    // undefined
-    0xf7,
-    // simple value
-    0xf8,
-};
-
-INSTANTIATE_TEST_SUITE_P(CborUnsupportedBytesTests, CborUnsupportedBytesTest,
-                        ::testing::ValuesIn(unsupported_bytes_cases));
-#if 0
-// use this testcase outside [hide] to run it with Valgrind
-TEST(CborRoundtripTest, Sample)
-{
-    std::string filename = "test/data/json_testsuite/sample.json";
-
-    // parse JSON file
-    std::ifstream f_json(filename);
-    json j1 = json::parse(f_json);
-
-    // parse CBOR file
-    std::ifstream f_cbor(filename + ".cbor", std::ios::binary);
-    std::vector<uint8_t> packed((std::istreambuf_iterator<char>(f_cbor)),
-                                std::istreambuf_iterator<char>());
-    json j2;
-    j2 = json::from_cbor(packed);
-
-    // compare parsed JSON values
-    EXPECT_EQ(j1, j2);
-
-    // check with different start index
-    packed.insert(packed.begin(), 5, 0xff);
-    EXPECT_EQ(j1, json::from_cbor(packed, 5));
-}
-
-/*
-The following test cases were found during a two-day session with
-AFL-Fuzz. As a result, empty byte vectors and excessive lengths are
-detected.
-*/
-class CborRegressionFuzzTest : public ::testing::TestWithParam<const char*> {};
-TEST_P(CborRegressionFuzzTest, Case)
-{
-    try
-    {
-        // parse CBOR file
-        std::ifstream f_cbor(GetParam(), std::ios::binary);
-        std::vector<uint8_t> vec1(
-            (std::istreambuf_iterator<char>(f_cbor)),
-            std::istreambuf_iterator<char>());
-        json j1 = json::from_cbor(vec1);
-
-        try
-        {
-            // step 2: round trip
-            std::string vec2 = json::to_cbor(j1);
-
-            // parse serialization
-            json j2 = json::from_cbor(vec2);
-
-            // deserializations must match
-            EXPECT_EQ(j1, j2);
-        }
-        catch (const json::parse_error&)
-        {
-            // parsing a CBOR serialization must not fail
-            FAIL();
-        }
-    }
-    catch (const json::parse_error&)
-    {
-        // parse errors are ok, because input may be random bytes
-    }
-}
-
-static const char* fuzz_test_cases[] = {
-    "test/data/cbor_regression/test01",
-    "test/data/cbor_regression/test02",
-    "test/data/cbor_regression/test03",
-    "test/data/cbor_regression/test04",
-    "test/data/cbor_regression/test05",
-    "test/data/cbor_regression/test06",
-    "test/data/cbor_regression/test07",
-    "test/data/cbor_regression/test08",
-    "test/data/cbor_regression/test09",
-    "test/data/cbor_regression/test10",
-    "test/data/cbor_regression/test11",
-    "test/data/cbor_regression/test12",
-    "test/data/cbor_regression/test13",
-    "test/data/cbor_regression/test14",
-    "test/data/cbor_regression/test15",
-    "test/data/cbor_regression/test16",
-    "test/data/cbor_regression/test17",
-    "test/data/cbor_regression/test18",
-    "test/data/cbor_regression/test19",
-    "test/data/cbor_regression/test20",
-    "test/data/cbor_regression/test21",
-};
-
-INSTANTIATE_TEST_SUITE_P(CborRegressionFuzzTests, CborRegressionFuzzTest,
-                        ::testing::ValuesIn(fuzz_test_cases));
-
-class CborRegressionFlynnTest : public ::testing::TestWithParam<const char*> {};
-TEST_F(CborRegressionFlynnTest, Case)
-{
-    // parse JSON file
-    std::ifstream f_json(GetParam());
-    json j1 = json::parse(f_json);
-
-    // parse CBOR file
-    std::ifstream f_cbor(filename + ".cbor", std::ios::binary);
-    std::vector<uint8_t> packed(
-        (std::istreambuf_iterator<char>(f_cbor)),
-        std::istreambuf_iterator<char>());
-    json j2;
-    j2 = json::from_cbor(packed);
-
-    // compare parsed JSON values
-    EXPECT_EQ(j1, j2);
-}
-
-static const char* flynn_test_cases[] = {
-    "test/data/json_nlohmann_tests/all_unicode.json",
-    "test/data/json.org/1.json",
-    "test/data/json.org/2.json",
-    "test/data/json.org/3.json",
-    "test/data/json.org/4.json",
-    "test/data/json.org/5.json",
-    "test/data/json_roundtrip/roundtrip01.json",
-    "test/data/json_roundtrip/roundtrip02.json",
-    "test/data/json_roundtrip/roundtrip03.json",
-    "test/data/json_roundtrip/roundtrip04.json",
-    "test/data/json_roundtrip/roundtrip05.json",
-    "test/data/json_roundtrip/roundtrip06.json",
-    "test/data/json_roundtrip/roundtrip07.json",
-    "test/data/json_roundtrip/roundtrip08.json",
-    "test/data/json_roundtrip/roundtrip09.json",
-    "test/data/json_roundtrip/roundtrip10.json",
-    "test/data/json_roundtrip/roundtrip11.json",
-    "test/data/json_roundtrip/roundtrip12.json",
-    "test/data/json_roundtrip/roundtrip13.json",
-    "test/data/json_roundtrip/roundtrip14.json",
-    "test/data/json_roundtrip/roundtrip15.json",
-    "test/data/json_roundtrip/roundtrip16.json",
-    "test/data/json_roundtrip/roundtrip17.json",
-    "test/data/json_roundtrip/roundtrip18.json",
-    "test/data/json_roundtrip/roundtrip19.json",
-    "test/data/json_roundtrip/roundtrip20.json",
-    "test/data/json_roundtrip/roundtrip21.json",
-    "test/data/json_roundtrip/roundtrip22.json",
-    "test/data/json_roundtrip/roundtrip23.json",
-    "test/data/json_roundtrip/roundtrip24.json",
-    "test/data/json_roundtrip/roundtrip25.json",
-    "test/data/json_roundtrip/roundtrip26.json",
-    "test/data/json_roundtrip/roundtrip27.json",
-    "test/data/json_roundtrip/roundtrip28.json",
-    "test/data/json_roundtrip/roundtrip29.json",
-    "test/data/json_roundtrip/roundtrip30.json",
-    "test/data/json_roundtrip/roundtrip31.json",
-    "test/data/json_roundtrip/roundtrip32.json",
-    "test/data/json_testsuite/sample.json", // kills AppVeyor
-    "test/data/json_tests/pass1.json",
-    "test/data/json_tests/pass2.json",
-    "test/data/json_tests/pass3.json",
-    "test/data/regression/floats.json",
-    "test/data/regression/signed_ints.json",
-    "test/data/regression/unsigned_ints.json",
-    "test/data/regression/working_file.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_arraysWithSpaces.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_empty-string.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_empty.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_ending_with_newline.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_false.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_heterogeneous.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_null.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_with_1_and_newline.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_with_leading_space.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_with_several_null.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_with_trailing_space.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_0e+1.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_0e1.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_after_space.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_double_close_to_zero.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_double_huge_neg_exp.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_int_with_exp.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_minus_zero.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_negative_int.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_negative_one.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_negative_zero.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e_neg_exp.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e_pos_exp.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_exponent.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_fraction_exponent.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_exp.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_exponent.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_underflow.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_simple_int.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_simple_real.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_too_big_neg_int.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_too_big_pos_int.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_very_big_negative_int.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_basic.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_duplicated_key.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_duplicated_key_and_value.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_empty.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_empty_key.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_escaped_null_in_key.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_extreme_numbers.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_long_strings.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_simple.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_string_unicode.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_with_newlines.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_UTF-16_Surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_accepted_surrogate_pair.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_accepted_surrogate_pairs.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_allowed_escapes.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_backslash_and_u_escaped_zero.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_backslash_doublequotes.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_comments.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_double_escape_a.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_double_escape_n.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_escaped_control_character.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_escaped_noncharacter.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_in_array.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_in_array_with_leading_space.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_last_surrogates_1_and_2.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_newline_uescaped.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+1FFFF.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_null_escape.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_one-byte-utf-8.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_pi.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_simple_ascii.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_space.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_three-byte-utf-8.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_two-byte-utf-8.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_u+2028_line_sep.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_u+2029_par_sep.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_uEscape.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unescaped_char_delete.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicode.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicodeEscapedBackslash.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_2.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_U+2064_invisible_plus.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_escaped_double_quote.json",
-    // "test/data/nst_json_testsuite/test_parsing/y_string_utf16.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_utf8.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_with_del_character.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_false.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_int.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_negative_real.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_null.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_string.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_true.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_string_empty.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_trailing_newline.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_true_in_array.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_whitespace_array.json",
-};
-
-INSTANTIATE_TEST_SUITE_P(CborRegressionFlynnTests, CborRegressionFlynnTest,
-                        ::testing::ValuesIn(flynn_test_cases));
-
-#endif
-TEST(CborFirstBytesTest, Unsupported)
-{
-    // these bytes will fail immediately with exception parse_error.112
-    std::set<uint8_t> unsupported =
-    {
-        //// types not supported by this library
-
-        // byte strings
-        0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
-        0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
-        0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
-        // byte strings
-        0x58, 0x59, 0x5a, 0x5b, 0x5f,
-        // date/time
-        0xc0, 0xc1,
-        // bignum
-        0xc2, 0xc3,
-        // decimal fracion
-        0xc4,
-        // bigfloat
-        0xc5,
-        // tagged item
-        0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
-        0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd8,
-        0xd9, 0xda, 0xdb,
-        // expected conversion
-        0xd5, 0xd6, 0xd7,
-        // simple value
-        0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
-        0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf0,
-        0xf1, 0xf2, 0xf3,
-        0xf8,
-        // undefined
-        0xf7,
-
-        //// bytes not specified by CBOR
-
-        0x1c, 0x1d, 0x1e, 0x1f,
-        0x3c, 0x3d, 0x3e, 0x3f,
-        0x5c, 0x5d, 0x5e,
-        0x7c, 0x7d, 0x7e,
-        0x9c, 0x9d, 0x9e,
-        0xbc, 0xbd, 0xbe,
-        0xdc, 0xdd, 0xde, 0xdf,
-        0xee,
-        0xfc, 0xfe, 0xfd,
-
-        /// break cannot be the first byte
-
-        0xff
-    };
-
-    for (auto i = 0; i < 256; ++i)
-    {
-        const auto byte = static_cast<uint8_t>(i);
-
-        try
-        {
-            json::from_cbor(std::vector<uint8_t>(1, byte));
-        }
-        catch (const json::parse_error& e)
-        {
-            // check that parse_error.112 is only thrown if the
-            // first byte is in the unsupported set
-            SCOPED_TRACE(e.what());
-            if (std::find(unsupported.begin(), unsupported.end(),
-                          static_cast<uint8_t>(byte)) != unsupported.end())
-            {
-                EXPECT_EQ(e.id, 112);
-            }
-            else
-            {
-                EXPECT_NE(e.id, 112);
-            }
-        }
-    }
-}
-
-// examples from RFC 7049 Appendix A
-namespace internal {
-struct CborRoundtripTestParam {
-  const char* plain;
-  std::vector<uint8_t> encoded;
-  bool test_encode;
-};
-}  // namespace internal
-
-class CborRoundtripTest
-    : public ::testing::TestWithParam<internal::CborRoundtripTestParam> {
-};
-TEST_P(CborRoundtripTest, Case)
-{
-    if (GetParam().test_encode)
-    {
-        EXPECT_EQ(json::to_cbor(json::parse(GetParam().plain)), GetParam().encoded);
-    }
-    EXPECT_EQ(json::parse(GetParam().plain), json::from_cbor(GetParam().encoded));
-}
-
-static const internal::CborRoundtripTestParam rfc7049_appendix_a_numbers[] = {
-    {"0", {0x00}, true},
-    {"1", {0x01}, true},
-    {"10", {0x0a}, true},
-    {"23", {0x17}, true},
-    {"24", {0x18,0x18}, true},
-    {"25", {0x18,0x19}, true},
-    {"100", {0x18,0x64}, true},
-    {"1000", {0x19,0x03,0xe8}, true},
-    {"1000000", {0x1a,0x00,0x0f,0x42,0x40}, true},
-    {"1000000000000", {0x1b,0x00,0x00,0x00,0xe8,0xd4,0xa5,0x10,0x00}, true},
-    {"18446744073709551615", {0x1b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, true},
-    // positive bignum is not supported
-    //{"18446744073709551616", {0xc2,0x49,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00", 11), true},
-    //{"-18446744073709551616", {0x3b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, true},
-    // negative bignum is not supported
-    //{"-18446744073709551617", {0xc3,0x49,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, true},
-    {"-1", {0x20}, true},
-    {"-10", {0x29}, true},
-    {"-100", {0x38,0x63}, true},
-    {"-1000", {0x39,0x03,0xe7}, true},
-    // half-precision float
-    {"0.0", {0xf9,0x00,0x00}, false},
-    // half-precision float
-    {"-0.0", {0xf9,0x80,0x00}, false},
-    // half-precision float
-    {"1.0", {0xf9,0x3c,0x00}, false},
-    {"1.1", {0xfb,0x3f,0xf1,0x99,0x99,0x99,0x99,0x99,0x9a}, true},
-    // half-precision float
-    {"1.5", {0xf9,0x3e,0x00}, false},
-    // half-precision float
-    {"65504.0", {0xf9,0x7b,0xff}, false},
-    {"100000.0", {0xfa,0x47,0xc3,0x50,0x00}, false},
-    {"3.4028234663852886e+38", {0xfa,0x7f,0x7f,0xff,0xff}, false},
-    {"1.0e+300", {0xfb,0x7e,0x37,0xe4,0x3c,0x88,0x00,0x75,0x9c}, true},
-    // half-precision float
-    {"5.960464477539063e-8", {0xf9,0x00,0x01}, false},
-    // half-precision float
-    {"0.00006103515625", {0xf9,0x04,0x00}, false},
-    // half-precision float
-    {"-4.0", {0xf9,0xc4,0x00}, false},
-    {"-4.1", {0xfb,0xc0,0x10,0x66,0x66,0x66,0x66,0x66,0x66}, true},
-};
-
-INSTANTIATE_TEST_SUITE_P(CborRfc7049AppendixANumberTests, CborRoundtripTest,
-                        ::testing::ValuesIn(rfc7049_appendix_a_numbers));
-
-static const internal::CborRoundtripTestParam rfc7049_appendix_a_simple_values[] = {
-    {"false", {0xf4}, true},
-    {"true", {0xf5}, true},
-};
-
-INSTANTIATE_TEST_SUITE_P(CborRfc7049AppendixASimpleValueTests, CborRoundtripTest,
-                        ::testing::ValuesIn(rfc7049_appendix_a_simple_values));
-
-static const internal::CborRoundtripTestParam rfc7049_appendix_a_strings[] = {
-    {"\"\"", {0x60}, true},
-    {"\"a\"", {0x61,0x61}, true},
-    {"\"IETF\"", {0x64,0x49,0x45,0x54,0x46}, true},
-    {"\"\\u00fc\"", {0x62,0xc3,0xbc}, true},
-    {"\"\\u6c34\"", {0x63,0xe6,0xb0,0xb4}, true},
-    {"\"\\ud800\\udd51\"", {0x64,0xf0,0x90,0x85,0x91}, true},
-    // indefinite length strings
-    {"\"streaming\"", {0x7f,0x65,0x73,0x74,0x72,0x65,0x61,0x64,0x6d,0x69,0x6e,0x67,0xff}, false},
-};
-
-INSTANTIATE_TEST_SUITE_P(CborRfc7049AppendixAStringTests, CborRoundtripTest,
-                        ::testing::ValuesIn(rfc7049_appendix_a_strings));
-
-static const internal::CborRoundtripTestParam rfc7049_appendix_a_arrays[] = {
-    {"[]", {0x80}, true},
-    {"[1, 2, 3]", {0x83,0x01,0x02,0x03}, true},
-    {"[1, [2, 3], [4, 5]]", {0x83,0x01,0x82,0x02,0x03,0x82,0x04,0x05}, true},
-    {"[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]", {0x98,0x19,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x18,0x18,0x19}, true},
-    // indefinite length arrays
-    {"[]", {0x9f,0xff}, false},
-    {"[1, [2, 3], [4, 5]] ", {0x9f,0x01,0x82,0x02,0x03,0x9f,0x04,0x05,0xff,0xff}, false},
-    {"[1, [2, 3], [4, 5]]", {0x9f,0x01,0x82,0x02,0x03,0x82,0x04,0x05,0xff}, false},
-    {"[1, [2, 3], [4, 5]]", {0x83,0x01,0x82,0x02,0x03,0x9f,0x04,0x05,0xff}, false},
-    {"[1, [2, 3], [4, 5]]", {0x83,0x01,0x9f,0x02,0x03,0xff,0x82,0x04,0x05}, false},
-    {"[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]", {0x9f,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x18,0x18,0x19,0xff}, false},
-};
-
-INSTANTIATE_TEST_SUITE_P(CborRfc7049AppendixAArrayTests, CborRoundtripTest,
-                        ::testing::ValuesIn(rfc7049_appendix_a_arrays));
-
-static const internal::CborRoundtripTestParam rfc7049_appendix_a_objects[] = {
-    {"{}", {0xa0}, true},
-    {"{\"a\": 1, \"b\": [2, 3]}", {0xa2,0x61,0x61,0x01,0x61,0x62,0x82,0x02,0x03}, true},
-    {"[\"a\", {\"b\": \"c\"}]", {0x82,0x61,0x61,0xa1,0x61,0x62,0x61,0x63}, true},
-    {"{\"a\": \"A\", \"b\": \"B\", \"c\": \"C\", \"d\": \"D\", \"e\": \"E\"}", {0xa5,0x61,0x61,0x61,0x41,0x61,0x62,0x61,0x42,0x61,0x63,0x61,0x43,0x61,0x64,0x61,0x44,0x61,0x65,0x61,0x45}, true},
-    // indefinite length objects
-    {"{\"a\": 1, \"b\": [2, 3]}", {0xbf,0x61,0x61,0x01,0x61,0x62,0x9f,0x02,0x03,0xff,0xff}, false},
-    {"[\"a\", {\"b\": \"c\"}]", {0x82,0x61,0x61,0xbf,0x61,0x62,0x61,0x63,0xff}, false},
-    {"{\"Fun\": true, \"Amt\": -2}", {0xbf,0x63,0x46,0x75,0x6e,0xf5,0x63,0x41,0x6d,0x74,0x21,0xff}, false},
-};
-
-INSTANTIATE_TEST_SUITE_P(CborRfc7049AppendixAObjectTests, CborRoundtripTest,
-                        ::testing::ValuesIn(rfc7049_appendix_a_objects));
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-comparison.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-comparison.cpp
deleted file mode 100644
index 18934a2..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-comparison.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-class JsonComparisonTypesTest : public ::testing::Test {
- protected:
-    std::vector<json::value_t> j_types =
-    {
-        json::value_t::null,
-        json::value_t::boolean,
-        json::value_t::number_integer,
-        json::value_t::number_unsigned,
-        json::value_t::number_float,
-        json::value_t::object,
-        json::value_t::array,
-        json::value_t::string
-    };
-};
-
-TEST_F(JsonComparisonTypesTest, Less)
-{
-    static const std::vector<std::vector<bool>> expected =
-    {
-        {false, true, true, true, true, true, true, true},
-        {false, false, true, true, true, true, true, true},
-        {false, false, false, false, false, true, true, true},
-        {false, false, false, false, false, true, true, true},
-        {false, false, false, false, false, true, true, true},
-        {false, false, false, false, false, false, true, true},
-        {false, false, false, false, false, false, false, true},
-        {false, false, false, false, false, false, false, false}
-    };
-
-    for (size_t i = 0; i < j_types.size(); ++i)
-    {
-        SCOPED_TRACE(i);
-        for (size_t j = 0; j < j_types.size(); ++j)
-        {
-            SCOPED_TRACE(j);
-            // check precomputed values
-            EXPECT_EQ(operator<(j_types[i], j_types[j]), expected[i][j]);
-        }
-    }
-}
-
-class JsonComparisonValuesTest : public ::testing::Test {
- protected:
-    json j_values =
-    {
-        nullptr, nullptr,
-        17, 42,
-        8u, 13u,
-        3.14159, 23.42,
-        "foo", "bar",
-        true, false,
-        {1, 2, 3}, {"one", "two", "three"},
-        {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}
-    };
-};
-
-TEST_F(JsonComparisonValuesTest, Equal)
-{
-    static const std::vector<std::vector<bool>> expected =
-    {
-        {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-        {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-        {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false},
-        {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false},
-        {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false},
-        {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false},
-        {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false},
-        {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false},
-        {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false},
-        {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false},
-        {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false},
-        {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false},
-        {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false},
-        {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false},
-        {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
-        {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true}
-    };
-
-    for (size_t i = 0; i < j_values.size(); ++i)
-    {
-        SCOPED_TRACE(i);
-        for (size_t j = 0; j < j_values.size(); ++j)
-        {
-            SCOPED_TRACE(j);
-            // check precomputed values
-            EXPECT_EQ(j_values[i] == j_values[j], expected[i][j]);
-        }
-    }
-
-    // comparison with discarded elements
-    json j_discarded(json::value_t::discarded);
-    for (size_t i = 0; i < j_values.size(); ++i)
-    {
-        SCOPED_TRACE(i);
-        EXPECT_FALSE(j_values[i] == j_discarded);
-        EXPECT_FALSE(j_discarded == j_values[i]);
-        EXPECT_FALSE(j_discarded == j_discarded);
-    }
-
-    // compare with null pointer
-    json j_null;
-    EXPECT_TRUE(j_null == nullptr);
-    EXPECT_TRUE(nullptr == j_null);
-}
-
-TEST_F(JsonComparisonValuesTest, NotEqual)
-{
-    for (size_t i = 0; i < j_values.size(); ++i)
-    {
-        SCOPED_TRACE(i);
-        for (size_t j = 0; j < j_values.size(); ++j)
-        {
-            SCOPED_TRACE(j);
-            // check definition
-            EXPECT_EQ(j_values[i] != j_values[j], !(j_values[i] == j_values[j]));
-        }
-    }
-
-    // compare with null pointer
-    json j_null;
-    EXPECT_FALSE(j_null != nullptr);
-    EXPECT_FALSE(nullptr != j_null);
-    EXPECT_EQ(j_null != nullptr, !(j_null == nullptr));
-    EXPECT_EQ(nullptr != j_null, !(nullptr == j_null));
-}
-
-TEST_F(JsonComparisonValuesTest, Less)
-{
-    static const std::vector<std::vector<bool>> expected =
-    {
-        {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
-        {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
-        {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true},
-        {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true},
-        {false, false, true, true, false, true, false, true, true, true, false, false, true, true, true, true},
-        {false, false, true, true, false, false, false, true, true, true, false, false, true, true, true, true},
-        {false, false, true, true, true, true, false, true, true, true, false, false, true, true, true, true},
-        {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true},
-        {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-        {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false},
-        {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true},
-        {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true},
-        {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false},
-        {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false},
-        {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false},
-        {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false}
-    };
-
-    for (size_t i = 0; i < j_values.size(); ++i)
-    {
-        SCOPED_TRACE(i);
-        for (size_t j = 0; j < j_values.size(); ++j)
-        {
-            SCOPED_TRACE(j);
-            // check precomputed values
-            EXPECT_EQ(j_values[i] < j_values[j], expected[i][j]);
-        }
-    }
-
-    // comparison with discarded elements
-    json j_discarded(json::value_t::discarded);
-    for (size_t i = 0; i < j_values.size(); ++i)
-    {
-        SCOPED_TRACE(i);
-        EXPECT_FALSE(j_values[i] < j_discarded);
-        EXPECT_FALSE(j_discarded < j_values[i]);
-        EXPECT_FALSE(j_discarded < j_discarded);
-    }
-}
-
-TEST_F(JsonComparisonValuesTest, LessEqual)
-{
-    for (size_t i = 0; i < j_values.size(); ++i)
-    {
-        SCOPED_TRACE(i);
-        for (size_t j = 0; j < j_values.size(); ++j)
-        {
-            SCOPED_TRACE(j);
-            // check definition
-            EXPECT_EQ(j_values[i] <= j_values[j], !(j_values[j] < j_values[i]));
-        }
-    }
-}
-
-TEST_F(JsonComparisonValuesTest, Greater)
-{
-    for (size_t i = 0; i < j_values.size(); ++i)
-    {
-        SCOPED_TRACE(i);
-        for (size_t j = 0; j < j_values.size(); ++j)
-        {
-            SCOPED_TRACE(j);
-            // check definition
-            EXPECT_EQ(j_values[i] > j_values[j], j_values[j] < j_values[i]);
-        }
-    }
-}
-
-TEST_F(JsonComparisonValuesTest, GreaterEqual)
-{
-    for (size_t i = 0; i < j_values.size(); ++i)
-    {
-        SCOPED_TRACE(i);
-        for (size_t j = 0; j < j_values.size(); ++j)
-        {
-            SCOPED_TRACE(j);
-            // check definition
-            EXPECT_EQ(j_values[i] >= j_values[j], !(j_values[i] < j_values[j]));
-        }
-    }
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-concepts.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-concepts.cpp
deleted file mode 100644
index 4ce5a5b..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-concepts.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-TEST(JsonConceptsTest, ContainerRequirements)
-{
-    // X: container class: json
-    // T: type of objects: json
-    // a, b: values of type X: json
-
-    // TABLE 96 - Container Requirements
-
-    // X::value_type must return T
-    EXPECT_TRUE((std::is_same<json::value_type, json>::value));
-
-    // X::reference must return lvalue of T
-    EXPECT_TRUE((std::is_same<json::reference, json&>::value));
-
-    // X::const_reference must return const lvalue of T
-    EXPECT_TRUE((std::is_same<json::const_reference, const json&>::value));
-
-    // X::iterator must return iterator whose value_type is T
-    EXPECT_TRUE((std::is_same<json::iterator::value_type, json>::value));
-    // X::iterator must meet the forward iterator requirements
-    EXPECT_TRUE((std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<json::iterator>::iterator_category>::value));
-    // X::iterator must be convertible to X::const_iterator
-    EXPECT_TRUE((std::is_convertible<json::iterator, json::const_iterator>::value));
-
-    // X::const_iterator must return iterator whose value_type is T
-    EXPECT_TRUE((std::is_same<json::const_iterator::value_type, json>::value));
-    // X::const_iterator must meet the forward iterator requirements
-    EXPECT_TRUE((std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<json::const_iterator>::iterator_category>::value));
-
-    // X::difference_type must return a signed integer
-    EXPECT_TRUE((std::is_signed<json::difference_type>::value));
-    // X::difference_type must be identical to X::iterator::difference_type
-    EXPECT_TRUE((std::is_same<json::difference_type, json::iterator::difference_type>::value));
-    // X::difference_type must be identical to X::const_iterator::difference_type
-    EXPECT_TRUE((std::is_same<json::difference_type, json::const_iterator::difference_type>::value));
-
-    // X::size_type must return an unsigned integer
-    EXPECT_TRUE((std::is_unsigned<json::size_type>::value));
-    // X::size_type can represent any non-negative value of X::difference_type
-    EXPECT_TRUE(static_cast<json::size_type>(std::numeric_limits<json::difference_type>::max()) <=
-          std::numeric_limits<json::size_type>::max());
-
-    // the expression "X u" has the post-condition "u.empty()"
-    {
-        json u;
-        EXPECT_TRUE(u.empty());
-    }
-
-    // the expression "X()" has the post-condition "X().empty()"
-    EXPECT_TRUE(json().empty());
-}
-
-TEST(JsonConceptsTest, DefaultConstructible)
-{
-    EXPECT_TRUE(std::is_nothrow_default_constructible<json>::value);
-}
-
-TEST(JsonConceptsTest, MoveConstructible)
-{
-    EXPECT_TRUE(std::is_nothrow_move_constructible<json>::value);
-}
-
-TEST(JsonConceptsTest, CopyConstructible)
-{
-    EXPECT_TRUE(std::is_copy_constructible<json>::value);
-}
-
-TEST(JsonConceptsTest, MoveAssignable)
-{
-    EXPECT_TRUE(std::is_nothrow_move_assignable<json>::value);
-}
-
-TEST(JsonConceptsTest, CopyAssignable)
-{
-    EXPECT_TRUE(std::is_copy_assignable<json>::value);
-}
-
-TEST(JsonConceptsTest, Destructible)
-{
-    EXPECT_TRUE(std::is_nothrow_destructible<json>::value);
-}
-
-TEST(JsonConceptsTest, StandardLayoutType)
-{
-    EXPECT_TRUE(std::is_standard_layout<json>::value);
-}
-
-TEST(JsonIteratorConceptsTest, CopyConstructible)
-{
-    EXPECT_TRUE(std::is_nothrow_copy_constructible<json::iterator>::value);
-    EXPECT_TRUE(std::is_nothrow_copy_constructible<json::const_iterator>::value);
-}
-
-TEST(JsonIteratorConceptsTest, CopyAssignable)
-{
-    // STL iterators used by json::iterator don't pass this test in Debug mode
-#if !defined(_MSC_VER) || (_ITERATOR_DEBUG_LEVEL == 0)
-    EXPECT_TRUE(std::is_nothrow_copy_assignable<json::iterator>::value);
-    EXPECT_TRUE(std::is_nothrow_copy_assignable<json::const_iterator>::value);
-#endif
-}
-
-TEST(JsonIteratorConceptsTest, Destructible)
-{
-    EXPECT_TRUE(std::is_nothrow_destructible<json::iterator>::value);
-    EXPECT_TRUE(std::is_nothrow_destructible<json::const_iterator>::value);
-}
-
-TEST(JsonIteratorConceptsTest, Swappable)
-{
-    json j {1, 2, 3};
-    json::iterator it1 = j.begin();
-    json::iterator it2 = j.end();
-    std::swap(it1, it2);
-    EXPECT_EQ(it1, j.end());
-    EXPECT_EQ(it2, j.begin());
-}
-
-TEST(JsonIteratorConceptsTest, SwappableConst)
-{
-    json j {1, 2, 3};
-    json::const_iterator it1 = j.cbegin();
-    json::const_iterator it2 = j.cend();
-    std::swap(it1, it2);
-    EXPECT_EQ(it1, j.end());
-    EXPECT_EQ(it2, j.begin());
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp
deleted file mode 100644
index 273bfb6..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp
+++ /dev/null
@@ -1,1069 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include <array>
-#include <cmath>
-#include <deque>
-#include <forward_list>
-#include <list>
-#include <map>
-#include <set>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "unit-json.h"
-using wpi::json;
-using wpi::JsonTest;
-
-class JsonConstructTypeTest : public ::testing::TestWithParam<json::value_t> {};
-TEST_P(JsonConstructTypeTest, Case)
-{
-    auto t = GetParam();
-    json j(t);
-    EXPECT_EQ(j.type(), t);
-}
-
-static const json::value_t construct_type_cases[] = {
-    json::value_t::null,
-    json::value_t::discarded,
-    json::value_t::object,
-    json::value_t::array,
-    json::value_t::boolean,
-    json::value_t::string,
-    json::value_t::number_integer,
-    json::value_t::number_unsigned,
-    json::value_t::number_float,
-};
-
-INSTANTIATE_TEST_SUITE_P(JsonConstructTypeTests, JsonConstructTypeTest,
-                        ::testing::ValuesIn(construct_type_cases));
-
-
-TEST(JsonConstructNullTest, NoParameter)
-{
-    json j{};
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonConstructNullTest, Parameter)
-{
-    json j(nullptr);
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonConstructObjectExplicitTest, Empty)
-{
-    json::object_t o;
-    json j(o);
-    EXPECT_EQ(j.type(), json::value_t::object);
-}
-
-TEST(JsonConstructObjectExplicitTest, Filled)
-{
-    json::object_t o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
-    json j(o);
-    EXPECT_EQ(j.type(), json::value_t::object);
-}
-
-class JsonConstructObjectImplicitTest : public ::testing::Test {
- public:
-    JsonConstructObjectImplicitTest() : j_reference(o_reference) {}
-
- protected:
-    json::object_t o_reference {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
-    json j_reference;
-};
-
-// std::map<std::string, json>
-TEST_F(JsonConstructObjectImplicitTest, StdMapStringJson)
-{
-    std::map<std::string, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
-    json j(o);
-    EXPECT_EQ(j.type(), json::value_t::object);
-    EXPECT_EQ(j, j_reference);
-}
-
-// std::pair<CompatibleString, T>
-TEST_F(JsonConstructObjectImplicitTest, StdPairStringT)
-{
-    std::pair<std::string, std::string> p{"first", "second"};
-    json j(p);
-
-    EXPECT_EQ(j.get<decltype(p)>(), p);
-
-    std::pair<std::string, int> p2{"first", 1};
-    // use char const*
-    json j2(std::make_pair("first", 1));
-
-    EXPECT_EQ(j2.get<decltype(p2)>(), p2);
-}
-
-// std::map<std::string, std::string>
-TEST_F(JsonConstructObjectImplicitTest, StdMapStringString)
-{
-    std::map<std::string, std::string> m;
-    m["a"] = "b";
-    m["c"] = "d";
-    m["e"] = "f";
-
-    json j(m);
-    EXPECT_EQ(j.get<decltype(m)>(), m);
-}
-
-// std::map<const char*, json>
-TEST_F(JsonConstructObjectImplicitTest, StdMapCharPointerJson)
-{
-    std::map<const char*, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
-    json j(o);
-    EXPECT_EQ(j.type(), json::value_t::object);
-    EXPECT_EQ(j, j_reference);
-}
-
-// std::multimap<std::string, json>
-TEST_F(JsonConstructObjectImplicitTest, StdMultiMapStringJson)
-{
-    std::multimap<std::string, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
-    json j(o);
-    EXPECT_EQ(j.type(), json::value_t::object);
-    EXPECT_EQ(j, j_reference);
-}
-
-// std::unordered_map<std::string, json>
-TEST_F(JsonConstructObjectImplicitTest, StdUnorderedMapStringJson)
-{
-    std::unordered_map<std::string, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
-    json j(o);
-    EXPECT_EQ(j.type(), json::value_t::object);
-    EXPECT_EQ(j, j_reference);
-}
-
-// std::unordered_multimap<std::string, json>
-TEST_F(JsonConstructObjectImplicitTest, StdUnorderedMultiMapStringJson)
-{
-    std::unordered_multimap<std::string, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
-    json j(o);
-    EXPECT_EQ(j.type(), json::value_t::object);
-    EXPECT_EQ(j, j_reference);
-}
-
-// associative container literal
-TEST_F(JsonConstructObjectImplicitTest, AssociativeContainerLiteral)
-{
-    json j({{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}});
-    EXPECT_EQ(j.type(), json::value_t::object);
-    EXPECT_EQ(j, j_reference);
-}
-
-TEST(JsonConstructArrayExplicitTest, Empty)
-{
-    json::array_t a;
-    json j(a);
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructArrayExplicitTest, Filled)
-{
-    json::array_t a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
-    json j(a);
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-template <typename T>
-class JsonConstructArrayTest : public ::testing::Test {
- public:
-    JsonConstructArrayTest() : j_reference(a_reference) {}
-
- protected:
-    json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
-    json j_reference;
-};
-
-typedef ::testing::Types<std::list<json>, std::forward_list<json>,
-                         std::array<json, 6>, std::vector<json>,
-                         std::deque<json>>
-    JsonConstructArrayTestTypes;
-TYPED_TEST_SUITE(JsonConstructArrayTest, JsonConstructArrayTestTypes, );
-
-// clang warns on missing braces on the TypeParam initializer line below.
-// Suppress this warning.
-#if defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmissing-braces"
-#endif
-TYPED_TEST(JsonConstructArrayTest, Implicit)
-{
-    TypeParam a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
-    json j(a);
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, this->j_reference);
-}
-#if defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-
-// std::set<json>
-TEST(JsonConstructArraySetTest, StdSet)
-{
-    std::set<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
-    json j(a);
-    EXPECT_EQ(j.type(), json::value_t::array);
-    // we cannot really check for equality here
-}
-
-// std::unordered_set<json>
-TEST(JsonConstructArraySetTest, StdUnorderedSet)
-{
-    std::unordered_set<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
-    json j(a);
-    EXPECT_EQ(j.type(), json::value_t::array);
-    // we cannot really check for equality here
-}
-
-// sequence container literal
-TEST(JsonConstructArrayContainerTest, Case)
-{
-    json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
-    json j_reference(a_reference);
-
-    json j({json(1), json(1u), json(2.2), json(false), json("string"), json()});
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, j_reference);
-}
-
-TEST(JsonConstructStringExplicitTest, Empty)
-{
-    std::string s;
-    json j(s);
-    EXPECT_EQ(j.type(), json::value_t::string);
-}
-
-TEST(JsonConstructStringExplicitTest, Filled)
-{
-    std::string s {"Hello world"};
-    json j(s);
-    EXPECT_EQ(j.type(), json::value_t::string);
-}
-
-class JsonConstructStringTest : public ::testing::Test {
- public:
-    JsonConstructStringTest() : j_reference(s_reference) {}
-
- protected:
-    std::string s_reference {"Hello world"};
-    json j_reference;
-};
-
-// std::string
-TEST_F(JsonConstructStringTest, StdString)
-{
-    std::string s {"Hello world"};
-    json j(s);
-    EXPECT_EQ(j.type(), json::value_t::string);
-    EXPECT_EQ(j, j_reference);
-}
-
-// char[]
-TEST_F(JsonConstructStringTest, CharArray)
-{
-    char s[] {"Hello world"};
-    json j(s);
-    EXPECT_EQ(j.type(), json::value_t::string);
-    EXPECT_EQ(j, j_reference);
-}
-
-// const char*
-TEST_F(JsonConstructStringTest, ConstCharPointer)
-{
-    const char* s {"Hello world"};
-    json j(s);
-    EXPECT_EQ(j.type(), json::value_t::string);
-    EXPECT_EQ(j, j_reference);
-}
-
-// string literal
-TEST_F(JsonConstructStringTest, StringLiteral)
-{
-    json j("Hello world");
-    EXPECT_EQ(j.type(), json::value_t::string);
-    EXPECT_EQ(j, j_reference);
-}
-
-TEST(JsonConstructBooleanExplicitTest, Empty)
-{
-    bool b{};
-    json j(b);
-    EXPECT_EQ(j.type(), json::value_t::boolean);
-}
-
-TEST(JsonConstructBooleanExplicitTest, True)
-{
-    json j(true);
-    EXPECT_EQ(j.type(), json::value_t::boolean);
-}
-
-TEST(JsonConstructBooleanExplicitTest, False)
-{
-    json j(false);
-    EXPECT_EQ(j.type(), json::value_t::boolean);
-}
-
-TEST(JsonConstructIntegerExplicitTest, Uninitialized)
-{
-    int64_t n{};
-    json j(n);
-    EXPECT_EQ(j.type(), json::value_t::number_integer);
-}
-
-TEST(JsonConstructIntegerExplicitTest, Initialized)
-{
-    int64_t n(42);
-    json j(n);
-    EXPECT_EQ(j.type(), json::value_t::number_integer);
-}
-
-template <typename T>
-class JsonConstructIntegerTest : public ::testing::Test {
- public:
-    JsonConstructIntegerTest()
-        : j_reference(n_reference), j_unsigned_reference(n_unsigned_reference) {}
-
- protected:
-    int64_t n_reference = 42;
-    json j_reference;
-    uint64_t n_unsigned_reference = 42u;
-    json j_unsigned_reference;
-};
-
-typedef ::testing::Types<
-      short
-    , unsigned short
-    , int
-    , unsigned int
-    , long
-    , unsigned long
-    , long long
-    , unsigned long long
-    , int8_t
-    , int16_t
-    , int32_t
-    , int64_t
-#if 0
-    , int8_fast_t
-    , int16_fast_t
-    , int32_fast_t
-    , int64_fast_t
-    , int8_least_t
-    , int16_least_t
-    , int32_least_t
-    , int64_least_t
-#endif
-    , uint8_t
-    , uint16_t
-    , uint32_t
-    , uint64_t
-#if 0
-    , uint8_fast_t
-    , uint16_fast_t
-    , uint32_fast_t
-    , uint64_fast_t
-    , uint8_least_t
-    , uint16_least_t
-    , uint32_least_t
-    , uint64_least_t
-#endif
-    > JsonConstructIntegerTestTypes;
-
-TYPED_TEST_SUITE(JsonConstructIntegerTest, JsonConstructIntegerTestTypes, );
-
-TYPED_TEST(JsonConstructIntegerTest, Implicit)
-{
-    TypeParam n = 42;
-    json j(n);
-    if (std::is_unsigned<TypeParam>::value)
-    {
-        EXPECT_EQ(j.type(), json::value_t::number_unsigned);
-        EXPECT_EQ(j, this->j_unsigned_reference);
-    }
-    else
-    {
-        EXPECT_EQ(j.type(), json::value_t::number_integer);
-        EXPECT_EQ(j, this->j_reference);
-    }
-}
-
-class JsonConstructIntegerLiteralTest : public ::testing::Test {
- public:
-    JsonConstructIntegerLiteralTest()
-        : j_reference(n_reference), j_unsigned_reference(n_unsigned_reference) {}
-
- protected:
-    int64_t n_reference = 42;
-    json j_reference;
-    uint64_t n_unsigned_reference = 42u;
-    json j_unsigned_reference;
-};
-
-TEST_F(JsonConstructIntegerLiteralTest, None)
-{
-    json j(42);
-    EXPECT_EQ(j.type(), json::value_t::number_integer);
-    EXPECT_EQ(j, j_reference);
-}
-
-TEST_F(JsonConstructIntegerLiteralTest, U)
-{
-    json j(42u);
-    EXPECT_EQ(j.type(), json::value_t::number_unsigned);
-    EXPECT_EQ(j, j_unsigned_reference);
-}
-
-TEST_F(JsonConstructIntegerLiteralTest, L)
-{
-    json j(42l);
-    EXPECT_EQ(j.type(), json::value_t::number_integer);
-    EXPECT_EQ(j, j_reference);
-}
-
-TEST_F(JsonConstructIntegerLiteralTest, UL)
-{
-    json j(42ul);
-    EXPECT_EQ(j.type(), json::value_t::number_unsigned);
-    EXPECT_EQ(j, j_unsigned_reference);
-}
-
-TEST_F(JsonConstructIntegerLiteralTest, LL)
-{
-    json j(42ll);
-    EXPECT_EQ(j.type(), json::value_t::number_integer);
-    EXPECT_EQ(j, j_reference);
-}
-
-TEST_F(JsonConstructIntegerLiteralTest, ULL)
-{
-    json j(42ull);
-    EXPECT_EQ(j.type(), json::value_t::number_unsigned);
-    EXPECT_EQ(j, j_unsigned_reference);
-}
-
-TEST(JsonConstructFloatExplicitTest, Uninitialized)
-{
-    double n{};
-    json j(n);
-    EXPECT_EQ(j.type(), json::value_t::number_float);
-}
-
-TEST(JsonConstructFloatExplicitTest, Initialized)
-{
-    double n(42.23);
-    json j(n);
-    EXPECT_EQ(j.type(), json::value_t::number_float);
-}
-
-TEST(JsonConstructFloatExplicitTest, Infinity)
-{
-    // infinity is stored properly, but serialized to null
-    double n(std::numeric_limits<double>::infinity());
-    json j(n);
-    EXPECT_EQ(j.type(), json::value_t::number_float);
-
-    // check round trip of infinity
-    double d = j;
-    EXPECT_EQ(d, n);
-
-    // check that inf is serialized to null
-    EXPECT_EQ(j.dump(), "null");
-}
-
-template <typename T>
-class JsonConstructFloatTest : public ::testing::Test {
- public:
-    JsonConstructFloatTest() : j_reference(n_reference) {}
-
- protected:
-    double n_reference {42.23};
-    json j_reference;
-};
-
-typedef ::testing::Types<float, double
-#if 0
-                         , long double
-#endif
-                         >
-    JsonConstructFloatTestTypes;
-
-TYPED_TEST_SUITE(JsonConstructFloatTest, JsonConstructFloatTestTypes, );
-
-TYPED_TEST(JsonConstructFloatTest, Implicit)
-{
-    TypeParam n = 42.23f;
-    json j(n);
-    EXPECT_EQ(j.type(), json::value_t::number_float);
-    EXPECT_LT(std::fabs(JsonTest::GetValue(j).number_float -
-                        JsonTest::GetValue(this->j_reference).number_float),
-              0.001);
-}
-
-class JsonConstructFloatLiteralTest : public ::testing::Test {
- public:
-    JsonConstructFloatLiteralTest() : j_reference(n_reference) {}
-
- protected:
-    double n_reference {42.23};
-    json j_reference;
-};
-
-TEST_F(JsonConstructFloatLiteralTest, None)
-{
-    json j(42.23);
-    EXPECT_EQ(j.type(), json::value_t::number_float);
-    EXPECT_LT(std::fabs(JsonTest::GetValue(j).number_float -
-                        JsonTest::GetValue(this->j_reference).number_float),
-              0.001);
-}
-
-TEST_F(JsonConstructFloatLiteralTest, F)
-{
-    json j(42.23f);
-    EXPECT_EQ(j.type(), json::value_t::number_float);
-    EXPECT_LT(std::fabs(JsonTest::GetValue(j).number_float -
-                        JsonTest::GetValue(this->j_reference).number_float),
-              0.001);
-}
-
-#if 0
-TEST_F(JsonConstructFloatLiteralTest, L)
-{
-    json j(42.23l);
-    EXPECT_EQ(j.type(), json::value_t::number_float);
-    EXPECT_LT(std::fabs(JsonTest::GetValue(j).number_float -
-                        JsonTest::GetValue(this->j_reference).number_float),
-              0.001);
-}
-#endif
-
-TEST(JsonConstructInitializerEmptyTest, Explicit)
-{
-    json j(json::initializer_list_t{});
-    EXPECT_EQ(j.type(), json::value_t::object);
-}
-
-TEST(JsonConstructInitializerEmptyTest, Implicit)
-{
-    json j {};
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonConstructInitializerOneTest, ExplicitArray)
-{
-    std::initializer_list<json> l = {json(json::array_t())};
-    json j(l);
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ImplicitArray)
-{
-    json j {json::array_t()};
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ExplicitObject)
-{
-    std::initializer_list<json> l = {json(json::object_t())};
-    json j(l);
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ImplicitObject)
-{
-    json j {json::object_t()};
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ExplicitString)
-{
-    std::initializer_list<json> l = {json("Hello world")};
-    json j(l);
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ImplicitString)
-{
-    json j {"Hello world"};
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ExplicitBoolean)
-{
-    std::initializer_list<json> l = {json(true)};
-    json j(l);
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ImplicitBoolean)
-{
-    json j {true};
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ExplicitInteger)
-{
-    std::initializer_list<json> l = {json(1)};
-    json j(l);
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ImplicitInteger)
-{
-    json j {1};
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ExplicitUnsigned)
-{
-    std::initializer_list<json> l = {json(1u)};
-    json j(l);
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ImplicitUnsigned)
-{
-    json j {1u};
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ExplicitFloat)
-{
-    std::initializer_list<json> l = {json(42.23)};
-    json j(l);
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerOneTest, ImplicitFloat)
-{
-    json j {42.23};
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerManyTest, Explicit)
-{
-    std::initializer_list<json> l = {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()};
-    json j(l);
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerManyTest, Implicit)
-{
-    json j {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()};
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerImplicitTest, Object)
-{
-    json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} };
-    EXPECT_EQ(j.type(), json::value_t::object);
-}
-
-TEST(JsonConstructInitializerImplicitTest, Array)
-{
-    json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 };
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerExplicitTest, EmptyObject)
-{
-    json j = json::object();
-    EXPECT_EQ(j.type(), json::value_t::object);
-}
-
-TEST(JsonConstructInitializerExplicitTest, Object)
-{
-    json j = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} });
-    EXPECT_EQ(j.type(), json::value_t::object);
-}
-
-TEST(JsonConstructInitializerExplicitTest, ObjectError)
-{
-    EXPECT_THROW_MSG(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }),
-    json::type_error,
-    "[json.exception.type_error.301] cannot create object from initializer list");
-}
-
-// std::pair<CompatibleString, T> with error
-TEST(JsonConstructInitializerPairErrorTest, WrongFieldNumber)
-{
-    json j{{"too", "much"}, {"string", "fields"}};
-    EXPECT_THROW_MSG((j.get<std::pair<std::string, std::string>>()), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with object");
-}
-
-TEST(JsonConstructInitializerPairErrorTest, WrongJsonType)
-{
-    json j(42);
-    EXPECT_THROW_MSG((j.get<std::pair<std::string, std::string>>()), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-}
-
-TEST(JsonConstructInitializerTest, EmptyArray)
-{
-    json j = json::array();
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConstructInitializerTest, Array)
-{
-    json j = json::array({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} });
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-// create an array of n copies of a given value
-TEST(JsonConstructArrayCopyTest, Case)
-{
-    json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2u}}};
-    json arr(3, v);
-    EXPECT_EQ(arr.size(), 3u);
-    for (auto& x : arr)
-    {
-        EXPECT_EQ(x, v);
-    }
-}
-
-// create a JSON container from an iterator range
-TEST(JsonConstructIteratorTest, ObjectBeginEnd)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-#if 0
-    json j_new(jobject.begin(), jobject.end());
-    EXPECT_EQ(j_new, jobject);
-#else
-    EXPECT_THROW(json(jobject.begin(), jobject.end()), json::invalid_iterator);
-#endif
-}
-
-TEST(JsonConstructIteratorTest, ObjectBeginEndConst)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-#if 0
-    json j_new(jobject.cbegin(), jobject.cend());
-    EXPECT_EQ(j_new, jobject);
-#else
-    EXPECT_THROW(json(jobject.cbegin(), jobject.cend()), json::invalid_iterator);
-#endif
-}
-
-TEST(JsonConstructIteratorTest, ObjectBeginBegin)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-#if 0
-    json j_new(jobject.begin(), jobject.begin());
-    EXPECT_EQ(j_new, json::object());
-#else
-    EXPECT_THROW(json(jobject.begin(), jobject.end()), json::invalid_iterator);
-#endif
-}
-
-TEST(JsonConstructIteratorTest, ObjectBeginBeginConst)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-#if 0
-    json j_new(jobject.cbegin(), jobject.cbegin());
-    EXPECT_EQ(j_new, json::object());
-#else
-    EXPECT_THROW(json(jobject.cbegin(), jobject.cend()), json::invalid_iterator);
-#endif
-}
-#if 0
-TEST(JsonConstructIteratorTest, ObjectSubrange)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
-    json j_new(jobject.find("b"), jobject.find("e"));
-    EXPECT_EQ(j_new, json({{"b", 1}, {"c", 17u}, {"d", false}}));
-}
-#endif
-TEST(JsonConstructIteratorTest, ObjectIncompatibleIterators)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
-    json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-    EXPECT_THROW_MSG(json(jobject.begin(), jobject2.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.201] iterators are not compatible");
-    EXPECT_THROW_MSG(json(jobject2.begin(), jobject.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.201] iterators are not compatible");
-}
-
-TEST(JsonConstructIteratorTest, ObjectIncompatibleIteratorsConst)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
-    json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-    EXPECT_THROW_MSG(json(jobject.cbegin(), jobject2.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.201] iterators are not compatible");
-    EXPECT_THROW_MSG(json(jobject2.cbegin(), jobject.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.201] iterators are not compatible");
-}
-
-TEST(JsonConstructIteratorTest, ArrayBeginEnd)
-{
-    json jarray = {1, 2, 3, 4, 5};
-    json j_new(jarray.begin(), jarray.end());
-    EXPECT_EQ(j_new, jarray);
-}
-
-TEST(JsonConstructIteratorTest, ArrayBeginEndConst)
-{
-    json jarray = {1, 2, 3, 4, 5};
-    json j_new(jarray.cbegin(), jarray.cend());
-    EXPECT_EQ(j_new, jarray);
-}
-
-TEST(JsonConstructIteratorTest, ArrayBeginBegin)
-{
-    json jarray = {1, 2, 3, 4, 5};
-    json j_new(jarray.begin(), jarray.begin());
-    EXPECT_EQ(j_new, json::array());
-}
-
-TEST(JsonConstructIteratorTest, ArrayBeginBeginConst)
-{
-    json jarray = {1, 2, 3, 4, 5};
-    json j_new(jarray.cbegin(), jarray.cbegin());
-    EXPECT_EQ(j_new, json::array());
-}
-
-TEST(JsonConstructIteratorTest, ArraySubrange)
-{
-    json jarray = {1, 2, 3, 4, 5};
-    json j_new(jarray.begin() + 1, jarray.begin() + 3);
-    EXPECT_EQ(j_new, json({2, 3}));
-}
-
-TEST(JsonConstructIteratorTest, ArraySubrangeConst)
-{
-    json jarray = {1, 2, 3, 4, 5};
-    json j_new(jarray.cbegin() + 1, jarray.cbegin() + 3);
-    EXPECT_EQ(j_new, json({2, 3}));
-}
-
-TEST(JsonConstructIteratorTest, ArrayIncompatibleIterators)
-{
-    json jarray = {1, 2, 3, 4};
-    json jarray2 = {2, 3, 4, 5};
-    EXPECT_THROW_MSG(json(jarray.begin(), jarray2.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.201] iterators are not compatible");
-    EXPECT_THROW_MSG(json(jarray2.begin(), jarray.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.201] iterators are not compatible");
-}
-
-TEST(JsonConstructIteratorTest, ArrayIncompatibleIteratorsConst)
-{
-    json jarray = {1, 2, 3, 4};
-    json jarray2 = {2, 3, 4, 5};
-    EXPECT_THROW_MSG(json(jarray.cbegin(), jarray2.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.201] iterators are not compatible");
-    EXPECT_THROW_MSG(json(jarray2.cbegin(), jarray.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.201] iterators are not compatible");
-}
-
-TEST(JsonConstructTwoValidIteratorTest, Null)
-{
-    json j;
-    EXPECT_THROW_MSG(json(j.begin(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.206] cannot construct with iterators from null");
-}
-
-TEST(JsonConstructTwoValidIteratorTest, NullConst)
-{
-    json j;
-    EXPECT_THROW_MSG(json(j.cbegin(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.206] cannot construct with iterators from null");
-}
-
-TEST(JsonConstructTwoValidIteratorTest, String)
-{
-    json j = "foo";
-    json j_new(j.begin(), j.end());
-    EXPECT_EQ(j, j_new);
-}
-
-TEST(JsonConstructTwoValidIteratorTest, StringConst)
-{
-    json j = "bar";
-    json j_new(j.cbegin(), j.cend());
-    EXPECT_EQ(j, j_new);
-}
-
-TEST(JsonConstructTwoValidIteratorTest, Boolean)
-{
-    json j = false;
-    json j_new(j.begin(), j.end());
-    EXPECT_EQ(j, j_new);
-}
-
-TEST(JsonConstructTwoValidIteratorTest, BooleanConst)
-{
-    json j = true;
-    json j_new(j.cbegin(), j.cend());
-    EXPECT_EQ(j, j_new);
-}
-
-TEST(JsonConstructTwoValidIteratorTest, Integer)
-{
-    json j = 17;
-    json j_new(j.begin(), j.end());
-    EXPECT_EQ(j, j_new);
-}
-
-TEST(JsonConstructTwoValidIteratorTest, IntegerConst)
-{
-    json j = 17;
-    json j_new(j.cbegin(), j.cend());
-    EXPECT_EQ(j, j_new);
-}
-
-TEST(JsonConstructTwoValidIteratorTest, Unsigned)
-{
-    json j = 17u;
-    json j_new(j.begin(), j.end());
-    EXPECT_EQ(j, j_new);
-}
-
-TEST(JsonConstructTwoValidIteratorTest, UnsignedConst)
-{
-    json j = 17u;
-    json j_new(j.cbegin(), j.cend());
-    EXPECT_EQ(j, j_new);
-}
-
-TEST(JsonConstructTwoValidIteratorTest, Float)
-{
-    json j = 23.42;
-    json j_new(j.begin(), j.end());
-    EXPECT_EQ(j, j_new);
-}
-
-TEST(JsonConstructTwoValidIteratorTest, FloatConst)
-{
-    json j = 23.42;
-    json j_new(j.cbegin(), j.cend());
-    EXPECT_EQ(j, j_new);
-}
-
-TEST(JsonConstructTwoInvalidIteratorTest, String)
-{
-    json j = "foo";
-    EXPECT_THROW_MSG(json(j.end(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(json(j.begin(), j.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonConstructTwoInvalidIteratorTest, StringConst)
-{
-    json j = "bar";
-    EXPECT_THROW_MSG(json(j.cend(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(json(j.cbegin(), j.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonConstructTwoInvalidIteratorTest, Boolean)
-{
-    json j = false;
-    EXPECT_THROW_MSG(json(j.end(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(json(j.begin(), j.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonConstructTwoInvalidIteratorTest, BooleanConst)
-{
-    json j = true;
-    EXPECT_THROW_MSG(json(j.cend(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(json(j.cbegin(), j.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonConstructTwoInvalidIteratorTest, Integer)
-{
-    json j = 17;
-    EXPECT_THROW_MSG(json(j.end(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(json(j.begin(), j.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonConstructTwoInvalidIteratorTest, IntegerConst)
-{
-    json j = 17;
-    EXPECT_THROW_MSG(json(j.cend(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(json(j.cbegin(), j.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonConstructTwoInvalidIteratorTest, Unsigned)
-{
-    json j = 17u;
-    EXPECT_THROW_MSG(json(j.end(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(json(j.begin(), j.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonConstructTwoInvalidIteratorTest, UnsignedConst)
-{
-    json j = 17u;
-    EXPECT_THROW_MSG(json(j.cend(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(json(j.cbegin(), j.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonConstructTwoInvalidIteratorTest, Float)
-{
-    json j = 23.42;
-    EXPECT_THROW_MSG(json(j.end(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(json(j.begin(), j.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonConstructTwoInvalidIteratorTest, FloatConst)
-{
-    json j = 23.42;
-    EXPECT_THROW_MSG(json(j.cend(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(json(j.cbegin(), j.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-constructor2.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-constructor2.cpp
deleted file mode 100644
index 39f1301..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-constructor2.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-TEST(JsonCopyConstructorTest, Object)
-{
-    json j {{"foo", 1}, {"bar", false}};
-    json k(j);
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyConstructorTest, Array)
-{
-    json j {"foo", 1, 42.23, false};
-    json k(j);
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyConstructorTest, Null)
-{
-    json j(nullptr);
-    json k(j);
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyConstructorTest, Boolean)
-{
-    json j(true);
-    json k(j);
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyConstructorTest, String)
-{
-    json j("Hello world");
-    json k(j);
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyConstructorTest, Integer)
-{
-    json j(42);
-    json k(j);
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyConstructorTest, Unsigned)
-{
-    json j(42u);
-    json k(j);
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyConstructorTest, Float)
-{
-    json j(42.23);
-    json k(j);
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonMoveConstructorTest, Case)
-{
-    json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}};
-    EXPECT_EQ(j.type(), json::value_t::object);
-    json k(std::move(j));
-    EXPECT_EQ(k.type(), json::value_t::object);
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonCopyAssignmentTest, Object)
-{
-    json j {{"foo", 1}, {"bar", false}};
-    json k;
-    k = j;
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyAssignmentTest, Array)
-{
-    json j {"foo", 1, 42.23, false};
-    json k;
-    k = j;
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyAssignmentTest, Null)
-{
-    json j(nullptr);
-    json k;
-    k = j;
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyAssignmentTest, Boolean)
-{
-    json j(true);
-    json k;
-    k = j;
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyAssignmentTest, String)
-{
-    json j("Hello world");
-    json k;
-    k = j;
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyAssignmentTest, Integer)
-{
-    json j(42);
-    json k;
-    k = j;
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyAssignmentTest, Unsigned)
-{
-    json j(42u);
-    json k;
-    k = j;
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonCopyAssignmentTest, Float)
-{
-    json j(42.23);
-    json k;
-    k = j;
-    EXPECT_EQ(j, k);
-}
-
-TEST(JsonDestructorTest, Object)
-{
-    auto j = new json {{"foo", 1}, {"bar", false}};
-    delete j;
-}
-
-TEST(JsonDestructorTest, Array)
-{
-    auto j = new json {"foo", 1, 1u, false, 23.42};
-    delete j;
-}
-
-TEST(JsonDestructorTest, String)
-{
-    auto j = new json("Hello world");
-    delete j;
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-conversions.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-conversions.cpp
deleted file mode 100644
index 601055c..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-conversions.cpp
+++ /dev/null
@@ -1,561 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-using wpi::JsonTest;
-
-#include <cmath>
-#include <deque>
-//#include <forward_list>
-#include <list>
-#include <map>
-#include <unordered_map>
-#include <unordered_set>
-
-template <typename T>
-class JsonGetObjectTest : public ::testing::Test {
- public:
-    JsonGetObjectTest() : j(o_reference) {}
-
- protected:
-    json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
-    json j;
-};
-
-typedef ::testing::Types<
-      json::object_t
-    , std::map<std::string, json>
-    , std::multimap<std::string, json>
-    , std::unordered_map<std::string, json>
-    , std::unordered_multimap<std::string, json>
-    > JsonGetObjectTestTypes;
-TYPED_TEST_SUITE(JsonGetObjectTest, JsonGetObjectTestTypes, );
-
-TYPED_TEST(JsonGetObjectTest, Explicit)
-{
-    TypeParam o = (this->j).template get<TypeParam>();
-    EXPECT_EQ(json(o), this->j);
-}
-
-TYPED_TEST(JsonGetObjectTest, Implicit)
-{
-    TypeParam o = this->j;
-    EXPECT_EQ(json(o), this->j);
-}
-
-// exception in case of a non-object type
-TEST(JsonGetObjectExceptionTest, TypeError)
-{
-    EXPECT_THROW_MSG(json(json::value_t::null).get<json::object_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be object, but is null");
-    EXPECT_THROW_MSG(json(json::value_t::array).get<json::object_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be object, but is array");
-    EXPECT_THROW_MSG(json(json::value_t::string).get<json::object_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be object, but is string");
-    EXPECT_THROW_MSG(json(json::value_t::boolean).get<json::object_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be object, but is boolean");
-    EXPECT_THROW_MSG(json(json::value_t::number_integer).get<json::object_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be object, but is number");
-    EXPECT_THROW_MSG(json(json::value_t::number_unsigned).get<json::object_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be object, but is number");
-    EXPECT_THROW_MSG(json(json::value_t::number_float).get<json::object_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be object, but is number");
-}
-
-template <typename T>
-class JsonGetArrayTest : public ::testing::Test {
- public:
-    JsonGetArrayTest() : j(a_reference) {}
-
- protected:
-    json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
-    json j;
-};
-
-typedef ::testing::Types<json::array_t, std::list<json>,
-                         /*std::forward_list<json>,*/ std::vector<json>,
-                         std::deque<json>>
-    JsonGetArrayTestTypes;
-TYPED_TEST_SUITE(JsonGetArrayTest, JsonGetArrayTestTypes, );
-
-TYPED_TEST(JsonGetArrayTest, Explicit)
-{
-    TypeParam a = (this->j).template get<TypeParam>();
-    EXPECT_EQ(json(a), this->j);
-}
-
-TYPED_TEST(JsonGetArrayTest, Implicit)
-{
-    TypeParam a = this->j;
-    EXPECT_EQ(json(a), this->j);
-}
-
-#if !defined(JSON_NOEXCEPTION)
-// reserve is called on containers that supports it
-TEST(JsonGetArrayAdditionalTest, ExplicitStdVectorReserve)
-{
-    json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
-    json j(a_reference);
-
-    // making the call to from_json throw in order to check capacity
-    std::vector<float> v;
-    EXPECT_THROW(wpi::from_json(j, v), json::type_error);
-    EXPECT_EQ(v.capacity(), j.size());
-
-    // make sure all values are properly copied
-    std::vector<int> v2 = json({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
-    EXPECT_EQ(v2.size(), 10u);
-}
-#endif
-
-// built-in arrays
-TEST(JsonGetArrayAdditionalTest, ExplicitBuiltinArray)
-{
-    const char str[] = "a string";
-    const int nbs[] = {0, 1, 2};
-
-    json j2 = nbs;
-    json j3 = str;
-
-    auto v = j2.get<std::vector<int>>();
-    auto s = j3.get<std::string>();
-    EXPECT_TRUE(std::equal(v.begin(), v.end(), std::begin(nbs)));
-    EXPECT_EQ(s, str);
-}
-#if 0
-TEST(JsonGetArrayExceptionTest, ForwardList)
-{
-    EXPECT_THROW_MSG(json(json::value_t::null).get<std::forward_list<json>>(), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is null");
-}
-#endif
-TEST(JsonGetArrayExceptionTest, StdVector)
-{
-    EXPECT_THROW_MSG(json(json::value_t::null).get<std::vector<json>>(), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is null");
-}
-
-// exception in case of a non-array type
-TEST(JsonGetArrayExceptionTest, TypeError)
-{
-    EXPECT_THROW_MSG(json(json::value_t::object).get<std::vector<int>>(), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is object");
-    EXPECT_THROW_MSG(json(json::value_t::null).get<json::array_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is null");
-    EXPECT_THROW_MSG(json(json::value_t::object).get<json::array_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is object");
-    EXPECT_THROW_MSG(json(json::value_t::string).get<json::array_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is string");
-    EXPECT_THROW_MSG(json(json::value_t::boolean).get<json::array_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is boolean");
-    EXPECT_THROW_MSG(json(json::value_t::number_integer).get<json::array_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is number");
-    EXPECT_THROW_MSG(json(json::value_t::number_unsigned).get<json::array_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is number");
-    EXPECT_THROW_MSG(json(json::value_t::number_float).get<json::array_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is number");
-}
-
-template <typename T>
-class JsonGetStringTest : public ::testing::Test {
- public:
-    JsonGetStringTest() : j(s_reference) {}
-
- protected:
-    std::string s_reference {"Hello world"};
-    json j;
-};
-
-typedef ::testing::Types<std::string, std::string> JsonGetStringTestTypes;
-TYPED_TEST_SUITE(JsonGetStringTest, JsonGetStringTestTypes, );
-
-TYPED_TEST(JsonGetStringTest, Explicit)
-{
-    TypeParam s = (this->j).template get<TypeParam>();
-    EXPECT_EQ(json(s), this->j);
-}
-
-TYPED_TEST(JsonGetStringTest, Implicit)
-{
-    TypeParam s = this->j;
-    EXPECT_EQ(json(s), this->j);
-}
-
-// exception in case of a non-string type
-TEST(JsonGetStringExceptionTest, TypeError)
-{
-    EXPECT_THROW_MSG(json(json::value_t::null).get<std::string>(), json::type_error,
-                     "[json.exception.type_error.302] type must be string, but is null");
-    EXPECT_THROW_MSG(json(json::value_t::object).get<std::string>(), json::type_error,
-                     "[json.exception.type_error.302] type must be string, but is object");
-    EXPECT_THROW_MSG(json(json::value_t::array).get<std::string>(), json::type_error,
-                     "[json.exception.type_error.302] type must be string, but is array");
-    EXPECT_THROW_MSG(json(json::value_t::boolean).get<std::string>(), json::type_error,
-                     "[json.exception.type_error.302] type must be string, but is boolean");
-    EXPECT_THROW_MSG(json(json::value_t::number_integer).get<std::string>(), json::type_error,
-                     "[json.exception.type_error.302] type must be string, but is number");
-    EXPECT_THROW_MSG(json(json::value_t::number_unsigned).get<std::string>(), json::type_error,
-                     "[json.exception.type_error.302] type must be string, but is number");
-    EXPECT_THROW_MSG(json(json::value_t::number_float).get<std::string>(), json::type_error,
-                     "[json.exception.type_error.302] type must be string, but is number");
-}
-
-template <typename T>
-class JsonGetBooleanTest : public ::testing::Test {
- public:
-    JsonGetBooleanTest() : j(b_reference) {}
-
- protected:
-    bool b_reference {true};
-    json j;
-};
-
-typedef ::testing::Types<bool, bool> JsonGetBooleanTestTypes;
-TYPED_TEST_SUITE(JsonGetBooleanTest, JsonGetBooleanTestTypes, );
-
-TYPED_TEST(JsonGetBooleanTest, Explicit)
-{
-    TypeParam b = (this->j).template get<TypeParam>();
-    EXPECT_EQ(json(b), this->j);
-}
-
-TYPED_TEST(JsonGetBooleanTest, Implicit)
-{
-    TypeParam b = this->j;
-    EXPECT_EQ(json(b), this->j);
-}
-
-// exception in case of a non-string type
-TEST(JsonGetBooleanExceptionTest, TypeError)
-{
-    EXPECT_THROW_MSG(json(json::value_t::null).get<bool>(), json::type_error,
-                     "[json.exception.type_error.302] type must be boolean, but is null");
-    EXPECT_THROW_MSG(json(json::value_t::object).get<bool>(), json::type_error,
-                     "[json.exception.type_error.302] type must be boolean, but is object");
-    EXPECT_THROW_MSG(json(json::value_t::array).get<bool>(), json::type_error,
-                     "[json.exception.type_error.302] type must be boolean, but is array");
-    EXPECT_THROW_MSG(json(json::value_t::string).get<bool>(), json::type_error,
-                     "[json.exception.type_error.302] type must be boolean, but is string");
-    EXPECT_THROW_MSG(json(json::value_t::number_integer).get<bool>(), json::type_error,
-                     "[json.exception.type_error.302] type must be boolean, but is number");
-    EXPECT_THROW_MSG(json(json::value_t::number_unsigned).get<bool>(), json::type_error,
-                     "[json.exception.type_error.302] type must be boolean, but is number");
-    EXPECT_THROW_MSG(json(json::value_t::number_float).get<bool>(), json::type_error,
-                     "[json.exception.type_error.302] type must be boolean, but is number");
-}
-
-template <typename T>
-class JsonGetIntegerTest : public ::testing::Test {
- public:
-    JsonGetIntegerTest() : j(n_reference), j_unsigned(n_unsigned_reference) {}
-
- protected:
-    int64_t n_reference {42};
-    json j;
-    uint64_t n_unsigned_reference {42u};
-    json j_unsigned;
-};
-
-typedef ::testing::Types<
-      short
-    , unsigned short
-    , int
-    , unsigned int
-    , long
-    , unsigned long
-    , long long
-    , unsigned long long
-    , int8_t
-    , int16_t
-    , int32_t
-    , int64_t
-#if 0
-    , int8_fast_t
-    , int16_fast_t
-    , int32_fast_t
-    , int64_fast_t
-    , int8_least_t
-    , int16_least_t
-    , int32_least_t
-    , int64_least_t
-#endif
-    , uint8_t
-    , uint16_t
-    , uint32_t
-    , uint64_t
-#if 0
-    , uint8_fast_t
-    , uint16_fast_t
-    , uint32_fast_t
-    , uint64_fast_t
-    , uint8_least_t
-    , uint16_least_t
-    , uint32_least_t
-    , uint64_least_t
-#endif
-    > JsonGetIntegerTestTypes;
-
-TYPED_TEST_SUITE(JsonGetIntegerTest, JsonGetIntegerTestTypes, );
-
-TYPED_TEST(JsonGetIntegerTest, Explicit)
-{
-    TypeParam n = (this->j).template get<TypeParam>();
-    EXPECT_EQ(json(n), this->j);
-}
-
-TYPED_TEST(JsonGetIntegerTest, Implicit)
-{
-    if (std::is_unsigned<TypeParam>::value)
-    {
-        TypeParam n = this->j_unsigned;
-        EXPECT_EQ(json(n), this->j_unsigned);
-    }
-    else
-    {
-        TypeParam n = this->j;
-        EXPECT_EQ(json(n), this->j);
-    }
-}
-
-// exception in case of a non-number type
-TEST(JsonGetIntegerExceptionTest, TypeError)
-{
-    EXPECT_THROW_MSG(json(json::value_t::null).get<int64_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be number, but is null");
-    EXPECT_THROW_MSG(json(json::value_t::object).get<int64_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be number, but is object");
-    EXPECT_THROW_MSG(json(json::value_t::array).get<int64_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be number, but is array");
-    EXPECT_THROW_MSG(json(json::value_t::string).get<int64_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be number, but is string");
-    EXPECT_THROW_MSG(json(json::value_t::boolean).get<int64_t>(), json::type_error,
-                     "[json.exception.type_error.302] type must be number, but is boolean");
-
-    EXPECT_NO_THROW(json(json::value_t::number_float).get<int64_t>());
-    EXPECT_NO_THROW(json(json::value_t::number_float).get<uint64_t>());
-}
-
-template <typename T>
-class JsonGetFloatTest : public ::testing::Test {
- public:
-    JsonGetFloatTest() : j(n_reference) {}
-
- protected:
-    double n_reference {42.23};
-    json j;
-};
-
-typedef ::testing::Types<double, float, double>
-    JsonGetFloatTestTypes;
-
-TYPED_TEST_SUITE(JsonGetFloatTest, JsonGetFloatTestTypes, );
-
-TYPED_TEST(JsonGetFloatTest, Explicit)
-{
-    TypeParam n = (this->j).template get<TypeParam>();
-    EXPECT_LT(std::fabs(JsonTest::GetValue(json(n)).number_float -
-                        JsonTest::GetValue(this->j).number_float), 0.001);
-}
-
-TYPED_TEST(JsonGetFloatTest, Implicit)
-{
-    TypeParam n = this->j;
-    EXPECT_LT(std::fabs(JsonTest::GetValue(json(n)).number_float -
-                        JsonTest::GetValue(this->j).number_float), 0.001);
-}
-
-// exception in case of a non-string type
-TEST(JsonGetFloatExceptionTest, TypeError)
-{
-    EXPECT_THROW_MSG(json(json::value_t::null).get<double>(), json::type_error,
-                     "[json.exception.type_error.302] type must be number, but is null");
-    EXPECT_THROW_MSG(json(json::value_t::object).get<double>(), json::type_error,
-                     "[json.exception.type_error.302] type must be number, but is object");
-    EXPECT_THROW_MSG(json(json::value_t::array).get<double>(), json::type_error,
-                     "[json.exception.type_error.302] type must be number, but is array");
-    EXPECT_THROW_MSG(json(json::value_t::string).get<double>(), json::type_error,
-                     "[json.exception.type_error.302] type must be number, but is string");
-    EXPECT_THROW_MSG(json(json::value_t::boolean).get<double>(), json::type_error,
-                     "[json.exception.type_error.302] type must be number, but is boolean");
-
-    EXPECT_NO_THROW(json(json::value_t::number_integer).get<double>());
-    EXPECT_NO_THROW(json(json::value_t::number_unsigned).get<double>());
-}
-
-TEST(JsonGetEnumTest, Case)
-{
-    enum c_enum { value_1, value_2 };
-    enum class cpp_enum { value_1, value_2 };
-
-    EXPECT_EQ(json(value_1).get<c_enum>(), value_1);
-    EXPECT_EQ(json(cpp_enum::value_1).get<cpp_enum>(), cpp_enum::value_1);
-}
-
-class JsonObjectConversionTest : public ::testing::Test {
- protected:
-    json j1 = {{"one", 1}, {"two", 2}, {"three", 3}};
-    json j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}};
-    json j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}};
-    json j4 = {{"one", true}, {"two", false}, {"three", true}};
-    json j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}};
-};
-
-TEST_F(JsonObjectConversionTest, StdMap)
-{
-    auto m1 = j1.get<std::map<std::string, int>>();
-    auto m2 = j2.get<std::map<std::string, unsigned int>>();
-    auto m3 = j3.get<std::map<std::string, double>>();
-    auto m4 = j4.get<std::map<std::string, bool>>();
-    //auto m5 = j5.get<std::map<std::string, std::string>>();
-}
-
-TEST_F(JsonObjectConversionTest, StdUnorderedMap)
-{
-    auto m1 = j1.get<std::unordered_map<std::string, int>>();
-    auto m2 = j2.get<std::unordered_map<std::string, unsigned int>>();
-    auto m3 = j3.get<std::unordered_map<std::string, double>>();
-    auto m4 = j4.get<std::unordered_map<std::string, bool>>();
-    //auto m5 = j5.get<std::unordered_map<std::string, std::string>>();
-    //CHECK(m5["one"] == "eins");
-}
-
-TEST_F(JsonObjectConversionTest, StdMultiMap)
-{
-    auto m1 = j1.get<std::multimap<std::string, int>>();
-    auto m2 = j2.get<std::multimap<std::string, unsigned int>>();
-    auto m3 = j3.get<std::multimap<std::string, double>>();
-    auto m4 = j4.get<std::multimap<std::string, bool>>();
-    //auto m5 = j5.get<std::multimap<std::string, std::string>>();
-    //CHECK(m5["one"] == "eins");
-}
-
-TEST_F(JsonObjectConversionTest, StdUnorderedMultiMap)
-{
-    auto m1 = j1.get<std::unordered_multimap<std::string, int>>();
-    auto m2 = j2.get<std::unordered_multimap<std::string, unsigned int>>();
-    auto m3 = j3.get<std::unordered_multimap<std::string, double>>();
-    auto m4 = j4.get<std::unordered_multimap<std::string, bool>>();
-    //auto m5 = j5.get<std::unordered_multimap<std::string, std::string>>();
-    //CHECK(m5["one"] == "eins");
-}
-
-// exception in case of a non-object type
-TEST_F(JsonObjectConversionTest, Exception)
-{
-    EXPECT_THROW_MSG((json().get<std::map<std::string, int>>()), json::type_error,
-                     "[json.exception.type_error.302] type must be object, but is null");
-}
-
-class JsonArrayConversionTest : public ::testing::Test {
- protected:
-    json j1 = {1, 2, 3, 4};
-    json j2 = {1u, 2u, 3u, 4u};
-    json j3 = {1.2, 2.3, 3.4, 4.5};
-    json j4 = {true, false, true};
-    json j5 = {"one", "two", "three"};
-};
-
-TEST_F(JsonArrayConversionTest, StdList)
-{
-    auto m1 = j1.get<std::list<int>>();
-    auto m2 = j2.get<std::list<unsigned int>>();
-    auto m3 = j3.get<std::list<double>>();
-    auto m4 = j4.get<std::list<bool>>();
-    auto m5 = j5.get<std::list<std::string>>();
-}
-
-#if 0
-TEST_F(JsonArrayConversionTest, StdForwardList)
-{
-    auto m1 = j1.get<std::forward_list<int>>();
-    auto m2 = j2.get<std::forward_list<unsigned int>>();
-    auto m3 = j3.get<std::forward_list<double>>();
-    auto m4 = j4.get<std::forward_list<bool>>();
-    auto m5 = j5.get<std::forward_list<std::string>>();
-}
-#endif
-
-TEST_F(JsonArrayConversionTest, StdVector)
-{
-    auto m1 = j1.get<std::vector<int>>();
-    auto m2 = j2.get<std::vector<unsigned int>>();
-    auto m3 = j3.get<std::vector<double>>();
-    auto m4 = j4.get<std::vector<bool>>();
-    auto m5 = j5.get<std::vector<std::string>>();
-}
-
-TEST_F(JsonArrayConversionTest, StdDeque)
-{
-    auto m1 = j1.get<std::deque<int>>();
-    auto m2 = j2.get<std::deque<unsigned int>>();
-    auto m3 = j2.get<std::deque<double>>();
-    auto m4 = j4.get<std::deque<bool>>();
-    auto m5 = j5.get<std::deque<std::string>>();
-}
-
-TEST_F(JsonArrayConversionTest, StdSet)
-{
-    auto m1 = j1.get<std::set<int>>();
-    auto m2 = j2.get<std::set<unsigned int>>();
-    auto m3 = j3.get<std::set<double>>();
-    auto m4 = j4.get<std::set<bool>>();
-    auto m5 = j5.get<std::set<std::string>>();
-}
-
-TEST_F(JsonArrayConversionTest, StdUnorderedSet)
-{
-    auto m1 = j1.get<std::unordered_set<int>>();
-    auto m2 = j2.get<std::unordered_set<unsigned int>>();
-    auto m3 = j3.get<std::unordered_set<double>>();
-    auto m4 = j4.get<std::unordered_set<bool>>();
-    auto m5 = j5.get<std::unordered_set<std::string>>();
-}
-
-// exception in case of a non-object type
-TEST_F(JsonArrayConversionTest, Exception)
-{
-    EXPECT_THROW_MSG((json().get<std::list<int>>()), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is null");
-    EXPECT_THROW_MSG((json().get<std::vector<int>>()), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is null");
-    EXPECT_THROW_MSG((json().get<std::vector<json>>()), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is null");
-    EXPECT_THROW_MSG((json().get<std::list<json>>()), json::type_error,
-                     "[json.exception.type_error.302] type must be array, but is null");
-    // does type really must be an array? or it rather must not be null?
-    // that's what I thought when other test like this one broke
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-deserialization.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-deserialization.cpp
deleted file mode 100644
index 2505ef5..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-deserialization.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-#include "wpi/raw_istream.h"
-using wpi::json;
-
-#include <valarray>
-
-TEST(JsonDeserializationTest, SuccessfulStream)
-{
-    std::string s = "[\"foo\",1,2,3,false,{\"one\":1}]";
-    wpi::raw_mem_istream ss(s.data(), s.size());
-    json j = json::parse(ss);
-    ASSERT_EQ(j, json({"foo", 1, 2, 3, false, {{"one", 1}}}));
-}
-
-TEST(JsonDeserializationTest, SuccessfulStringLiteral)
-{
-    auto s = "[\"foo\",1,2,3,false,{\"one\":1}]";
-    json j = json::parse(s);
-    ASSERT_EQ(j, json({"foo", 1, 2, 3, false, {{"one", 1}}}));
-}
-
-TEST(JsonDeserializationTest, SuccessfulStdString)
-{
-    std::string s = "[\"foo\",1,2,3,false,{\"one\":1}]";
-    json j = json::parse(s);
-    ASSERT_EQ(j, json({"foo", 1, 2, 3, false, {{"one", 1}}}));
-}
-
-TEST(JsonDeserializationTest, SuccessfulStreamOperator)
-{
-    std::string s = "[\"foo\",1,2,3,false,{\"one\":1}]";
-    wpi::raw_mem_istream ss(s.data(), s.size());
-    json j;
-    ss >> j;
-    ASSERT_EQ(j, json({"foo", 1, 2, 3, false, {{"one", 1}}}));
-}
-
-TEST(JsonDeserializationTest, SuccessfulUserStringLiteral)
-{
-    ASSERT_EQ("[\"foo\",1,2,3,false,{\"one\":1}]"_json, json({"foo", 1, 2, 3, false, {{"one", 1}}}));
-}
-
-TEST(JsonDeserializationTest, UnsuccessfulStream)
-{
-    std::string s = "[\"foo\",1,2,3,false,{\"one\":1}";
-    wpi::raw_mem_istream ss(s.data(), s.size());
-    ASSERT_THROW_MSG(json::parse(ss), json::parse_error,
-                     "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
-}
-
-TEST(JsonDeserializationTest, UnsuccessfulStdString)
-{
-    std::string s = "[\"foo\",1,2,3,false,{\"one\":1}";
-    ASSERT_THROW_MSG(json::parse(s), json::parse_error,
-                     "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
-}
-
-TEST(JsonDeserializationTest, UnsuccessfulStreamOperator)
-{
-    std::string s = "[\"foo\",1,2,3,false,{\"one\":1}";
-    wpi::raw_mem_istream ss(s.data(), s.size());
-    json j;
-    ASSERT_THROW_MSG(ss >> j, json::parse_error,
-                     "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
-}
-
-TEST(JsonDeserializationTest, UnsuccessfulUserStringLiteral)
-{
-    ASSERT_THROW_MSG("[\"foo\",1,2,3,false,{\"one\":1}"_json, json::parse_error,
-                     "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
-}
-
-// these cases are required for 100% line coverage
-class JsonDeserializationErrorTest
-    : public ::testing::TestWithParam<const char*> {};
-
-TEST_P(JsonDeserializationErrorTest, ErrorCase)
-{
-    ASSERT_THROW(json::parse(GetParam()), json::parse_error);
-}
-
-static const char* error_cases[] = {
-    "\"aaaaaa\\u",
-    "\"aaaaaa\\u1",
-    "\"aaaaaa\\u11111111",
-    "\"aaaaaau11111111\\",
-    "\"\x7F\xC1",
-    "\"\x7F\xDF\x7F",
-    "\"\x7F\xDF\xC0",
-    "\"\x7F\xE0\x9F",
-    "\"\x7F\xEF\xC0",
-    "\"\x7F\xED\x7F",
-    "\"\x7F\xF0\x8F",
-    "\"\x7F\xF0\xC0",
-    "\"\x7F\xF3\x7F",
-    "\"\x7F\xF3\xC0",
-    "\"\x7F\xF4\x7F",
-};
-
-INSTANTIATE_TEST_SUITE_P(JsonDeserializationErrorTests,
-                        JsonDeserializationErrorTest,
-                        ::testing::ValuesIn(error_cases));
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-element_access1.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-element_access1.cpp
deleted file mode 100644
index a97d6b6..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-element_access1.cpp
+++ /dev/null
@@ -1,873 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-class JsonElementArrayAccessTestBase {
- public:
-    JsonElementArrayAccessTestBase() : j_const(j) {}
-
- protected:
-    json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
-    const json j_const;
-};
-
-class JsonElementArrayAccessTest : public ::testing::Test,
-                                   public JsonElementArrayAccessTestBase {};
-
-TEST_F(JsonElementArrayAccessTest, AtWithinBounds)
-{
-    EXPECT_EQ(j.at(0), json(1));
-    EXPECT_EQ(j.at(1), json(1u));
-    EXPECT_EQ(j.at(2), json(true));
-    EXPECT_EQ(j.at(3), json(nullptr));
-    EXPECT_EQ(j.at(4), json("string"));
-    EXPECT_EQ(j.at(5), json(42.23));
-    EXPECT_EQ(j.at(6), json::object());
-    EXPECT_EQ(j.at(7), json({1, 2, 3}));
-
-    EXPECT_EQ(j_const.at(0), json(1));
-    EXPECT_EQ(j_const.at(1), json(1u));
-    EXPECT_EQ(j_const.at(2), json(true));
-    EXPECT_EQ(j_const.at(3), json(nullptr));
-    EXPECT_EQ(j_const.at(4), json("string"));
-    EXPECT_EQ(j_const.at(5), json(42.23));
-    EXPECT_EQ(j_const.at(6), json::object());
-    EXPECT_EQ(j_const.at(7), json({1, 2, 3}));
-}
-
-TEST_F(JsonElementArrayAccessTest, AtOutsideBounds)
-{
-    EXPECT_THROW_MSG(j.at(8), json::out_of_range,
-                     "[json.exception.out_of_range.401] array index 8 is out of range");
-    EXPECT_THROW_MSG(j_const.at(8), json::out_of_range,
-                     "[json.exception.out_of_range.401] array index 8 is out of range");
-}
-
-TEST(JsonElementNonArrayAtAccessTest, Null)
-{
-    json j_nonarray(json::value_t::null);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with null");
-    EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with null");
-}
-
-TEST(JsonElementNonArrayAtAccessTest, Boolean)
-{
-    json j_nonarray(json::value_t::boolean);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with boolean");
-    EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with boolean");
-}
-
-TEST(JsonElementNonArrayAtAccessTest, String)
-{
-    json j_nonarray(json::value_t::string);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with string");
-    EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with string");
-}
-
-TEST(JsonElementNonArrayAtAccessTest, Object)
-{
-    json j_nonarray(json::value_t::object);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with object");
-    EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with object");
-}
-
-TEST(JsonElementNonArrayAtAccessTest, Integer)
-{
-    json j_nonarray(json::value_t::number_integer);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-    EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-}
-
-TEST(JsonElementNonArrayAtAccessTest, Unsigned)
-{
-    json j_nonarray(json::value_t::number_unsigned);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-    EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-}
-
-TEST(JsonElementNonArrayAtAccessTest, Float)
-{
-    json j_nonarray(json::value_t::number_float);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-    EXPECT_THROW_MSG(j_nonarray_const.at(0), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-}
-
-TEST_F(JsonElementArrayAccessTest, FrontAndBack)
-{
-    EXPECT_EQ(j.front(), json(1));
-    EXPECT_EQ(j_const.front(), json(1));
-    EXPECT_EQ(j.back(), json({1, 2, 3}));
-    EXPECT_EQ(j_const.back(), json({1, 2, 3}));
-}
-
-TEST_F(JsonElementArrayAccessTest, OperatorWithinBounds)
-{
-    EXPECT_EQ(j[0], json(1));
-    EXPECT_EQ(j[1], json(1u));
-    EXPECT_EQ(j[2], json(true));
-    EXPECT_EQ(j[3], json(nullptr));
-    EXPECT_EQ(j[4], json("string"));
-    EXPECT_EQ(j[5], json(42.23));
-    EXPECT_EQ(j[6], json::object());
-    EXPECT_EQ(j[7], json({1, 2, 3}));
-
-    EXPECT_EQ(j_const[0], json(1));
-    EXPECT_EQ(j_const[1], json(1u));
-    EXPECT_EQ(j_const[2], json(true));
-    EXPECT_EQ(j_const[3], json(nullptr));
-    EXPECT_EQ(j_const[4], json("string"));
-    EXPECT_EQ(j_const[5], json(42.23));
-    EXPECT_EQ(j_const[6], json::object());
-    EXPECT_EQ(j_const[7], json({1, 2, 3}));
-}
-
-TEST(JsonElementNonArrayOperatorAccessTest, NullStandard)
-{
-    json j_nonarray(json::value_t::null);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_NO_THROW(j_nonarray[0]);
-    EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with null");
-}
-
-// implicit transformation to properly filled array
-TEST(JsonElementNonArrayOperatorAccessTest, NullImplicitFilled)
-{
-    json j_nonarray;
-    j_nonarray[3] = 42;
-    EXPECT_EQ(j_nonarray, json({nullptr, nullptr, nullptr, 42}));
-}
-
-TEST(JsonElementNonArrayOperatorAccessTest, Boolean)
-{
-    json j_nonarray(json::value_t::boolean);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with boolean");
-    EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with boolean");
-}
-
-TEST(JsonElementNonArrayOperatorAccessTest, String)
-{
-    json j_nonarray(json::value_t::string);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with string");
-    EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with string");
-}
-
-TEST(JsonElementNonArrayOperatorAccessTest, Object)
-{
-    json j_nonarray(json::value_t::object);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with object");
-    EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with object");
-}
-
-TEST(JsonElementNonArrayOperatorAccessTest, Integer)
-{
-    json j_nonarray(json::value_t::number_integer);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-}
-
-TEST(JsonElementNonArrayOperatorAccessTest, Unsigned)
-{
-    json j_nonarray(json::value_t::number_unsigned);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-}
-
-TEST(JsonElementNonArrayOperatorAccessTest, Float)
-{
-    json j_nonarray(json::value_t::number_float);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_THROW_MSG(j_nonarray[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_nonarray_const[0], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-}
-
-class JsonElementArrayRemoveTest : public ::testing::Test,
-                                   public JsonElementArrayAccessTestBase {};
-
-
-// remove element by index
-TEST_F(JsonElementArrayRemoveTest, Index0)
-{
-    j.erase(0);
-    EXPECT_EQ(j, json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, Index1)
-{
-    j.erase(1);
-    EXPECT_EQ(j, json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, Index2)
-{
-    j.erase(2);
-    EXPECT_EQ(j, json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, Index3)
-{
-    j.erase(3);
-    EXPECT_EQ(j, json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, Index4)
-{
-    j.erase(4);
-    EXPECT_EQ(j, json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, Index5)
-{
-    j.erase(5);
-    EXPECT_EQ(j, json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, Index6)
-{
-    j.erase(6);
-    EXPECT_EQ(j, json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, Index7)
-{
-    j.erase(7);
-    EXPECT_EQ(j, json({1, 1u, true, nullptr, "string", 42.23, json::object()}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, Index8)
-{
-    EXPECT_THROW_MSG(j.erase(8), json::out_of_range,
-                     "[json.exception.out_of_range.401] array index 8 is out of range");
-}
-
-// erase(begin())
-TEST_F(JsonElementArrayRemoveTest, Begin)
-{
-    j.erase(j.begin());
-    EXPECT_EQ(j, json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, BeginConst)
-{
-    j.erase(j.cbegin());
-    EXPECT_EQ(j, json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
-}
-
-// erase(begin(), end())
-TEST_F(JsonElementArrayRemoveTest, BeginEnd)
-{
-    j.erase(j.begin(), j.end());
-    EXPECT_EQ(j, json::array());
-}
-TEST_F(JsonElementArrayRemoveTest, BeginEndConst)
-{
-    j.erase(j.cbegin(), j.cend());
-    EXPECT_EQ(j, json::array());
-}
-
-// erase(begin(), begin())
-TEST_F(JsonElementArrayRemoveTest, BeginBegin)
-{
-    j.erase(j.begin(), j.begin());
-    EXPECT_EQ(j, json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, BeginBeginConst)
-{
-    j.erase(j.cbegin(), j.cbegin());
-    EXPECT_EQ(j, json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
-}
-
-// erase at offset
-TEST_F(JsonElementArrayRemoveTest, Offset)
-{
-    json::iterator it = j.begin() + 4;
-    j.erase(it);
-    EXPECT_EQ(j, json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, OffsetConst)
-{
-    json::const_iterator it = j.cbegin() + 4;
-    j.erase(it);
-    EXPECT_EQ(j, json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
-}
-
-// erase subrange
-TEST_F(JsonElementArrayRemoveTest, Subrange)
-{
-    j.erase(j.begin() + 3, j.begin() + 6);
-    EXPECT_EQ(j, json({1, 1u, true, json::object(), {1, 2, 3}}));
-}
-
-TEST_F(JsonElementArrayRemoveTest, SubrangeConst)
-{
-    j.erase(j.cbegin() + 3, j.cbegin() + 6);
-    EXPECT_EQ(j, json({1, 1u, true, json::object(), {1, 2, 3}}));
-}
-
-// different arrays
-TEST_F(JsonElementArrayRemoveTest, Different)
-{
-    json j2 = {"foo", "bar"};
-    EXPECT_THROW_MSG(j.erase(j2.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.202] iterator does not fit current value");
-    EXPECT_THROW_MSG(j.erase(j.begin(), j2.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-    EXPECT_THROW_MSG(j.erase(j2.begin(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-    EXPECT_THROW_MSG(j.erase(j2.begin(), j2.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-}
-
-TEST_F(JsonElementArrayRemoveTest, DifferentConst)
-{
-    json j2 = {"foo", "bar"};
-    EXPECT_THROW_MSG(j.erase(j2.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.202] iterator does not fit current value");
-    EXPECT_THROW_MSG(j.erase(j.cbegin(), j2.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-    EXPECT_THROW_MSG(j.erase(j2.cbegin(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-    EXPECT_THROW_MSG(j.erase(j2.cbegin(), j2.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-}
-
-// remove element by index in non-array type
-TEST(JsonElementNonArrayIndexRemoveTest, Null)
-{
-    json j_nonobject(json::value_t::null);
-    EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with null");
-}
-
-TEST(JsonElementNonArrayIndexRemoveTest, Boolean)
-{
-    json j_nonobject(json::value_t::boolean);
-    EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with boolean");
-}
-
-TEST(JsonElementNonArrayIndexRemoveTest, String)
-{
-    json j_nonobject(json::value_t::string);
-    EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with string");
-}
-
-TEST(JsonElementNonArrayIndexRemoveTest, Object)
-{
-    json j_nonobject(json::value_t::object);
-    EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with object");
-}
-
-TEST(JsonElementNonArrayIndexRemoveTest, Integer)
-{
-    json j_nonobject(json::value_t::number_integer);
-    EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with number");
-}
-
-TEST(JsonElementNonArrayIndexRemoveTest, Unsigned)
-{
-    json j_nonobject(json::value_t::number_unsigned);
-    EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with number");
-}
-
-TEST(JsonElementNonArrayIndexRemoveTest, Float)
-{
-    json j_nonobject(json::value_t::number_float);
-    EXPECT_THROW_MSG(j_nonobject.erase(0), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with number");
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, Null)
-{
-    json j;
-    EXPECT_THROW_MSG(j.front(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(j.back(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, NullConst)
-{
-    const json j{};
-    EXPECT_THROW_MSG(j.front(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(j.back(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, String)
-{
-    json j = "foo";
-    EXPECT_EQ(j.front(), j);
-    EXPECT_EQ(j.back(), j);
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, StringConst)
-{
-    const json j = "bar";
-    EXPECT_EQ(j.front(), j);
-    EXPECT_EQ(j.back(), j);
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, Boolean)
-{
-    json j = false;
-    EXPECT_EQ(j.front(), j);
-    EXPECT_EQ(j.back(), j);
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, BooleanConst)
-{
-    const json j = true;
-    EXPECT_EQ(j.front(), j);
-    EXPECT_EQ(j.back(), j);
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, Integer)
-{
-    json j = 17;
-    EXPECT_EQ(j.front(), j);
-    EXPECT_EQ(j.back(), j);
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, IntegerConst)
-{
-    const json j = 17;
-    EXPECT_EQ(j.front(), j);
-    EXPECT_EQ(j.back(), j);
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, Unsigned)
-{
-    json j = 17u;
-    EXPECT_EQ(j.front(), j);
-    EXPECT_EQ(j.back(), j);
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, UnsignedConst)
-{
-    const json j = 17u;
-    EXPECT_EQ(j.front(), j);
-    EXPECT_EQ(j.back(), j);
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, Float)
-{
-    json j = 23.42;
-    EXPECT_EQ(j.front(), j);
-    EXPECT_EQ(j.back(), j);
-}
-
-TEST(JsonElementNonArrayFrontBackAccessTest, FloatConst)
-{
-    const json j = 23.42;
-    EXPECT_EQ(j.front(), j);
-    EXPECT_EQ(j.back(), j);
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, Null)
-{
-    json j;
-    EXPECT_THROW_MSG(j.erase(j.begin()), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with null");
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, NullConst)
-{
-    json j;
-    EXPECT_THROW_MSG(j.erase(j.cbegin()), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with null");
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, String)
-{
-    json j = "foo";
-    j.erase(j.begin());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, StringConst)
-{
-    json j = "bar";
-    j.erase(j.cbegin());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, Boolean)
-{
-    json j = false;
-    j.erase(j.begin());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, BooleanConst)
-{
-    json j = true;
-    j.erase(j.cbegin());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, Integer)
-{
-    json j = 17;
-    j.erase(j.begin());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, IntegerConst)
-{
-    json j = 17;
-    j.erase(j.cbegin());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, Unsigned)
-{
-    json j = 17u;
-    j.erase(j.begin());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, UnsignedConst)
-{
-    json j = 17u;
-    j.erase(j.cbegin());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, Float)
-{
-    json j = 23.42;
-    j.erase(j.begin());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayOneValidIteratorRemoveTest, FloatConst)
-{
-    json j = 23.42;
-    j.erase(j.cbegin());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, String)
-{
-    json j = "foo";
-    EXPECT_THROW_MSG(j.erase(j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.205] iterator out of range");
-}
-
-TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, StringConst)
-{
-    json j = "bar";
-    EXPECT_THROW_MSG(j.erase(j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.205] iterator out of range");
-}
-
-TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, Boolean)
-{
-    json j = false;
-    EXPECT_THROW_MSG(j.erase(j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.205] iterator out of range");
-}
-
-TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, BooleanConst)
-{
-    json j = true;
-    EXPECT_THROW_MSG(j.erase(j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.205] iterator out of range");
-}
-
-TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, Integer)
-{
-    json j = 17;
-    EXPECT_THROW_MSG(j.erase(j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.205] iterator out of range");
-}
-
-TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, IntegerConst)
-{
-    json j = 17;
-    EXPECT_THROW_MSG(j.erase(j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.205] iterator out of range");
-}
-
-TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, Unsigned)
-{
-    json j = 17u;
-    EXPECT_THROW_MSG(j.erase(j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.205] iterator out of range");
-}
-
-TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, UnsignedConst)
-{
-    json j = 17u;
-    EXPECT_THROW_MSG(j.erase(j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.205] iterator out of range");
-}
-
-TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, Float)
-{
-    json j = 23.42;
-    EXPECT_THROW_MSG(j.erase(j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.205] iterator out of range");
-}
-
-TEST(JsonElementNonArrayOneInvalidIteratorRemoveTest, FloatConst)
-{
-    json j = 23.42;
-    EXPECT_THROW_MSG(j.erase(j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.205] iterator out of range");
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, Null)
-{
-    json j;
-    EXPECT_THROW_MSG(j.erase(j.begin(), j.end()), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with null");
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, NullConst)
-{
-    json j;
-    EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cend()), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with null");
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, String)
-{
-    json j = "foo";
-    j.erase(j.begin(), j.end());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, StringConst)
-{
-    json j = "bar";
-    j.erase(j.cbegin(), j.cend());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, Boolean)
-{
-    json j = false;
-    j.erase(j.begin(), j.end());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, BooleanConst)
-{
-    json j = true;
-    j.erase(j.cbegin(), j.cend());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, Integer)
-{
-    json j = 17;
-    j.erase(j.begin(), j.end());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, IntegerConst)
-{
-    json j = 17;
-    j.erase(j.cbegin(), j.cend());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, Unsigned)
-{
-    json j = 17u;
-    j.erase(j.begin(), j.end());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, UnsignedConst)
-{
-    json j = 17u;
-    j.erase(j.cbegin(), j.cend());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, Float)
-{
-    json j = 23.42;
-    j.erase(j.begin(), j.end());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayTwoValidIteratorRemoveTest, FloatConst)
-{
-    json j = 23.42;
-    j.erase(j.cbegin(), j.cend());
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, String)
-{
-    json j = "foo";
-    EXPECT_THROW_MSG(j.erase(j.end(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(j.erase(j.begin(), j.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, StringConst)
-{
-    json j = "bar";
-    EXPECT_THROW_MSG(j.erase(j.cend(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, Boolean)
-{
-    json j = false;
-    EXPECT_THROW_MSG(j.erase(j.end(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(j.erase(j.begin(), j.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, BooleanConst)
-{
-    json j = true;
-    EXPECT_THROW_MSG(j.erase(j.cend(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, Integer)
-{
-    json j = 17;
-    EXPECT_THROW_MSG(j.erase(j.end(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(j.erase(j.begin(), j.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, IntegerConst)
-{
-    json j = 17;
-    EXPECT_THROW_MSG(j.erase(j.cend(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, Unsigned)
-{
-    json j = 17u;
-    EXPECT_THROW_MSG(j.erase(j.end(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(j.erase(j.begin(), j.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, UnsignedConst)
-{
-    json j = 17u;
-    EXPECT_THROW_MSG(j.erase(j.cend(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, Float)
-{
-    json j = 23.42;
-    EXPECT_THROW_MSG(j.erase(j.end(), j.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(j.erase(j.begin(), j.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
-
-TEST(JsonElementNonArrayTwoInvalidIteratorRemoveTest, FloatConst)
-{
-    json j = 23.42;
-    EXPECT_THROW_MSG(j.erase(j.cend(), j.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-    EXPECT_THROW_MSG(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.204] iterators out of range");
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-element_access2.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-element_access2.cpp
deleted file mode 100644
index ab953bd..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-element_access2.cpp
+++ /dev/null
@@ -1,926 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-
-#include <cmath>
-
-using wpi::json;
-
-class JsonElementObjectAccessTestBase {
- public:
-    JsonElementObjectAccessTestBase() : j_const(j) {}
-
- protected:
-    json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}};
-    const json j_const;
-};
-
-class JsonElementObjectAccessTest : public ::testing::Test,
-                                    public JsonElementObjectAccessTestBase {};
-
-TEST_F(JsonElementObjectAccessTest, AtWithinBounds)
-{
-    EXPECT_EQ(j.at("integer"), json(1));
-    EXPECT_EQ(j.at("unsigned"), json(1u));
-    EXPECT_EQ(j.at("boolean"), json(true));
-    EXPECT_EQ(j.at("null"), json(nullptr));
-    EXPECT_EQ(j.at("string"), json("hello world"));
-    EXPECT_EQ(j.at("floating"), json(42.23));
-    EXPECT_EQ(j.at("object"), json::object());
-    EXPECT_EQ(j.at("array"), json({1, 2, 3}));
-
-    EXPECT_EQ(j_const.at("integer"), json(1));
-    EXPECT_EQ(j_const.at("unsigned"), json(1u));
-    EXPECT_EQ(j_const.at("boolean"), json(true));
-    EXPECT_EQ(j_const.at("null"), json(nullptr));
-    EXPECT_EQ(j_const.at("string"), json("hello world"));
-    EXPECT_EQ(j_const.at("floating"), json(42.23));
-    EXPECT_EQ(j_const.at("object"), json::object());
-    EXPECT_EQ(j_const.at("array"), json({1, 2, 3}));
-}
-
-TEST_F(JsonElementObjectAccessTest, AtOutsideBounds)
-{
-    EXPECT_THROW_MSG(j.at("foo"), json::out_of_range,
-                     "[json.exception.out_of_range.403] key 'foo' not found");
-    EXPECT_THROW_MSG(j_const.at("foo"), json::out_of_range,
-                     "[json.exception.out_of_range.403] key 'foo' not found");
-}
-
-TEST(JsonElementNonObjectAtAccessTest, Null)
-{
-    json j_nonobject(json::value_t::null);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with null");
-    EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with null");
-}
-
-TEST(JsonElementNonObjectAtAccessTest, Boolean)
-{
-    json j_nonobject(json::value_t::boolean);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with boolean");
-    EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with boolean");
-}
-
-TEST(JsonElementNonObjectAtAccessTest, String)
-{
-    json j_nonobject(json::value_t::string);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with string");
-    EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with string");
-}
-
-TEST(JsonElementNonObjectAtAccessTest, Array)
-{
-    json j_nonobject(json::value_t::array);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with array");
-    EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with array");
-}
-
-TEST(JsonElementNonObjectAtAccessTest, Integer)
-{
-    json j_nonobject(json::value_t::number_integer);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-    EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-}
-
-TEST(JsonElementNonObjectAtAccessTest, Unsigned)
-{
-    json j_nonobject(json::value_t::number_unsigned);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-    EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-}
-
-TEST(JsonElementNonObjectAtAccessTest, Float)
-{
-    json j_nonobject(json::value_t::number_float);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-    EXPECT_THROW_MSG(j_nonobject_const.at("foo"), json::type_error,
-                     "[json.exception.type_error.304] cannot use at() with number");
-}
-
-TEST_F(JsonElementObjectAccessTest, KeyValueExist)
-{
-    EXPECT_EQ(j.value("integer", 2), 1);
-    EXPECT_LT(std::fabs(j.value("integer", 1.0) - 1), 0.001);
-    EXPECT_EQ(j.value("unsigned", 2), 1);
-    EXPECT_LT(std::fabs(j.value("unsigned", 1.0) - 1), 0.001);
-    EXPECT_EQ(j.value("null", json(1)), json());
-    EXPECT_EQ(j.value("boolean", false), true);
-    EXPECT_EQ(j.value("string", "bar"), "hello world");
-    EXPECT_EQ(j.value("string", std::string("bar")), "hello world");
-    EXPECT_LT(std::fabs(j.value("floating", 12.34) - 42.23), 0.001);
-    EXPECT_EQ(j.value("floating", 12), 42);
-    EXPECT_EQ(j.value("object", json({{"foo", "bar"}})), json::object());
-    EXPECT_EQ(j.value("array", json({10, 100})), json({1, 2, 3}));
-
-    EXPECT_EQ(j_const.value("integer", 2), 1);
-    EXPECT_LT(std::fabs(j_const.value("integer", 1.0) - 1), 0.001);
-    EXPECT_EQ(j_const.value("unsigned", 2), 1);
-    EXPECT_LT(std::fabs(j_const.value("unsigned", 1.0) - 1), 0.001);
-    EXPECT_EQ(j_const.value("boolean", false), true);
-    EXPECT_EQ(j_const.value("string", "bar"), "hello world");
-    EXPECT_EQ(j_const.value("string", std::string("bar")), "hello world");
-    EXPECT_LT(std::fabs(j_const.value("floating", 12.34) - 42.23), 0.001);
-    EXPECT_EQ(j_const.value("floating", 12), 42);
-    EXPECT_EQ(j_const.value("object", json({{"foo", "bar"}})), json::object());
-    EXPECT_EQ(j_const.value("array", json({10, 100})), json({1, 2, 3}));
-}
-
-TEST_F(JsonElementObjectAccessTest, KeyValueNotExist)
-{
-    EXPECT_EQ(j.value("_", 2), 2);
-    EXPECT_EQ(j.value("_", 2u), 2u);
-    EXPECT_EQ(j.value("_", false), false);
-    EXPECT_EQ(j.value("_", "bar"), "bar");
-    EXPECT_LT(std::fabs(j.value("_", 12.34) - 12.34), 0.001);
-    EXPECT_EQ(j.value("_", json({{"foo", "bar"}})), json({{"foo", "bar"}}));
-    EXPECT_EQ(j.value("_", json({10, 100})), json({10, 100}));
-
-    EXPECT_EQ(j_const.value("_", 2), 2);
-    EXPECT_EQ(j_const.value("_", 2u), 2u);
-    EXPECT_EQ(j_const.value("_", false), false);
-    EXPECT_EQ(j_const.value("_", "bar"), "bar");
-    EXPECT_LT(std::fabs(j_const.value("_", 12.34) - 12.34), 0.001);
-    EXPECT_EQ(j_const.value("_", json({{"foo", "bar"}})), json({{"foo", "bar"}}));
-    EXPECT_EQ(j_const.value("_", json({10, 100})), json({10, 100}));
-}
-
-TEST(JsonElementNonObjectKeyValueAccessTest, Null)
-{
-    json j_nonobject(json::value_t::null);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with null");
-    EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with null");
-}
-
-TEST(JsonElementNonObjectKeyValueAccessTest, Boolean)
-{
-    json j_nonobject(json::value_t::boolean);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with boolean");
-    EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with boolean");
-}
-
-TEST(JsonElementNonObjectKeyValueAccessTest, String)
-{
-    json j_nonobject(json::value_t::string);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with string");
-    EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with string");
-}
-
-TEST(JsonElementNonObjectKeyValueAccessTest, Array)
-{
-    json j_nonobject(json::value_t::array);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with array");
-    EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with array");
-}
-
-TEST(JsonElementNonObjectKeyValueAccessTest, Integer)
-{
-    json j_nonobject(json::value_t::number_integer);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-    EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-}
-
-TEST(JsonElementNonObjectKeyValueAccessTest, Unsigned)
-{
-    json j_nonobject(json::value_t::number_unsigned);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-    EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-}
-
-TEST(JsonElementNonObjectKeyValueAccessTest, Float)
-{
-    json j_nonobject(json::value_t::number_float);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-    EXPECT_THROW_MSG(j_nonobject_const.value("foo", 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-}
-
-TEST_F(JsonElementObjectAccessTest, PointerValueExist)
-{
-    EXPECT_EQ(j.value("/integer"_json_pointer, 2), 1);
-    EXPECT_LT(std::fabs(j.value("/integer"_json_pointer, 1.0) - 1), 0.001);
-    EXPECT_EQ(j.value("/unsigned"_json_pointer, 2), 1);
-    EXPECT_LT(std::fabs(j.value("/unsigned"_json_pointer, 1.0) - 1), 0.001);
-    EXPECT_EQ(j.value("/null"_json_pointer, json(1)), json());
-    EXPECT_EQ(j.value("/boolean"_json_pointer, false), true);
-    EXPECT_EQ(j.value("/string"_json_pointer, "bar"), "hello world");
-    EXPECT_EQ(j.value("/string"_json_pointer, std::string("bar")), "hello world");
-    EXPECT_LT(std::fabs(j.value("/floating"_json_pointer, 12.34) - 42.23), 0.001);
-    EXPECT_EQ(j.value("/floating"_json_pointer, 12), 42);
-    EXPECT_EQ(j.value("/object"_json_pointer, json({{"foo", "bar"}})), json::object());
-    EXPECT_EQ(j.value("/array"_json_pointer, json({10, 100})), json({1, 2, 3}));
-
-    EXPECT_EQ(j_const.value("/integer"_json_pointer, 2), 1);
-    EXPECT_LT(std::fabs(j_const.value("/integer"_json_pointer, 1.0) - 1), 0.001);
-    EXPECT_EQ(j_const.value("/unsigned"_json_pointer, 2), 1);
-    EXPECT_LT(std::fabs(j_const.value("/unsigned"_json_pointer, 1.0) - 1), 0.001);
-    EXPECT_EQ(j_const.value("/boolean"_json_pointer, false), true);
-    EXPECT_EQ(j_const.value("/string"_json_pointer, "bar"), "hello world");
-    EXPECT_EQ(j_const.value("/string"_json_pointer, std::string("bar")), "hello world");
-    EXPECT_LT(std::fabs(j_const.value("/floating"_json_pointer, 12.34) - 42.23), 0.001);
-    EXPECT_EQ(j_const.value("/floating"_json_pointer, 12), 42);
-    EXPECT_EQ(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})), json::object());
-    EXPECT_EQ(j_const.value("/array"_json_pointer, json({10, 100})), json({1, 2, 3}));
-}
-
-TEST(JsonElementNonObjectPointerValueAccessTest, Null)
-{
-    json j_nonobject(json::value_t::null);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with null");
-    EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with null");
-}
-
-TEST(JsonElementNonObjectPointerValueAccessTest, Boolean)
-{
-    json j_nonobject(json::value_t::boolean);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with boolean");
-    EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with boolean");
-}
-
-TEST(JsonElementNonObjectPointerValueAccessTest, String)
-{
-    json j_nonobject(json::value_t::string);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with string");
-    EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with string");
-}
-
-TEST(JsonElementNonObjectPointerValueAccessTest, Array)
-{
-    json j_nonobject(json::value_t::array);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with array");
-    EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with array");
-}
-
-TEST(JsonElementNonObjectPointerValueAccessTest, Integer)
-{
-    json j_nonobject(json::value_t::number_integer);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-    EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-}
-
-TEST(JsonElementNonObjectPointerValueAccessTest, Unsigned)
-{
-    json j_nonobject(json::value_t::number_unsigned);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-    EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-}
-
-TEST(JsonElementNonObjectPointerValueAccessTest, Float)
-{
-    json j_nonobject(json::value_t::number_float);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-    EXPECT_THROW_MSG(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error,
-                     "[json.exception.type_error.306] cannot use value() with number");
-}
-#if 0
-TEST_F(JsonElementObjectAccessTest, FrontAndBack)
-{
-    // "array" is the smallest key
-    EXPECT_EQ(j.front(), json({1, 2, 3}));
-    EXPECT_EQ(j_const.front(), json({1, 2, 3}));
-    // "unsigned" is the largest key
-    EXPECT_EQ(j.back(), json(1u));
-    EXPECT_EQ(j_const.back(), json(1u));
-}
-#endif
-TEST_F(JsonElementObjectAccessTest, OperatorWithinBounds)
-{
-    EXPECT_EQ(j["integer"], json(1));
-    EXPECT_EQ(j[json::object_t::key_type("integer")], j["integer"]);
-
-    EXPECT_EQ(j["unsigned"], json(1u));
-    EXPECT_EQ(j[json::object_t::key_type("unsigned")], j["unsigned"]);
-
-    EXPECT_EQ(j["boolean"], json(true));
-    EXPECT_EQ(j[json::object_t::key_type("boolean")], j["boolean"]);
-
-    EXPECT_EQ(j["null"], json(nullptr));
-    EXPECT_EQ(j[json::object_t::key_type("null")], j["null"]);
-
-    EXPECT_EQ(j["string"], json("hello world"));
-    EXPECT_EQ(j[json::object_t::key_type("string")], j["string"]);
-
-    EXPECT_EQ(j["floating"], json(42.23));
-    EXPECT_EQ(j[json::object_t::key_type("floating")], j["floating"]);
-
-    EXPECT_EQ(j["object"], json::object());
-    EXPECT_EQ(j[json::object_t::key_type("object")], j["object"]);
-
-    EXPECT_EQ(j["array"], json({1, 2, 3}));
-    EXPECT_EQ(j[json::object_t::key_type("array")], j["array"]);
-
-    EXPECT_EQ(j_const["integer"], json(1));
-    EXPECT_EQ(j_const[json::object_t::key_type("integer")], j["integer"]);
-
-    EXPECT_EQ(j_const["boolean"], json(true));
-    EXPECT_EQ(j_const[json::object_t::key_type("boolean")], j["boolean"]);
-
-    EXPECT_EQ(j_const["null"], json(nullptr));
-    EXPECT_EQ(j_const[json::object_t::key_type("null")], j["null"]);
-
-    EXPECT_EQ(j_const["string"], json("hello world"));
-    EXPECT_EQ(j_const[json::object_t::key_type("string")], j["string"]);
-
-    EXPECT_EQ(j_const["floating"], json(42.23));
-    EXPECT_EQ(j_const[json::object_t::key_type("floating")], j["floating"]);
-
-    EXPECT_EQ(j_const["object"], json::object());
-    EXPECT_EQ(j_const[json::object_t::key_type("object")], j["object"]);
-
-    EXPECT_EQ(j_const["array"], json({1, 2, 3}));
-    EXPECT_EQ(j_const[json::object_t::key_type("array")], j["array"]);
-}
-
-TEST(JsonElementNonObjectOperatorAccessTest, Null)
-{
-    json j_nonobject(json::value_t::null);
-    json j_nonobject2(json::value_t::null);
-    const json j_const_nonobject(j_nonobject);
-    EXPECT_NO_THROW(j_nonobject["foo"]);
-    EXPECT_NO_THROW(j_nonobject2[json::object_t::key_type("foo")]);
-    EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with null");
-    EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with null");
-}
-
-TEST(JsonElementNonObjectOperatorAccessTest, Boolean)
-{
-    json j_nonobject(json::value_t::boolean);
-    const json j_const_nonobject(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with boolean");
-    EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with boolean");
-    EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with boolean");
-    EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with boolean");
-}
-
-TEST(JsonElementNonObjectOperatorAccessTest, String)
-{
-    json j_nonobject(json::value_t::string);
-    const json j_const_nonobject(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with string");
-    EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with string");
-    EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with string");
-    EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with string");
-}
-
-TEST(JsonElementNonObjectOperatorAccessTest, Array)
-{
-    json j_nonobject(json::value_t::array);
-    const json j_const_nonobject(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with array");
-    EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with array");
-    EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with array");
-    EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with array");
-}
-
-TEST(JsonElementNonObjectOperatorAccessTest, Integer)
-{
-    json j_nonobject(json::value_t::number_integer);
-    const json j_const_nonobject(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-}
-
-TEST(JsonElementNonObjectOperatorAccessTest, Unsigned)
-{
-    json j_nonobject(json::value_t::number_unsigned);
-    const json j_const_nonobject(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-}
-
-TEST(JsonElementNonObjectOperatorAccessTest, Float)
-{
-    json j_nonobject(json::value_t::number_float);
-    const json j_const_nonobject(j_nonobject);
-    EXPECT_THROW_MSG(j_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_const_nonobject["foo"], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-    EXPECT_THROW_MSG(j_const_nonobject[json::object_t::key_type("foo")], json::type_error,
-                     "[json.exception.type_error.305] cannot use operator[] with number");
-}
-
-class JsonElementObjectRemoveTest : public ::testing::Test,
-                                    public JsonElementObjectAccessTestBase {};
-
-TEST_F(JsonElementObjectRemoveTest, Key)
-{
-    EXPECT_NE(j.find("integer"), j.end());
-    EXPECT_EQ(j.erase("integer"), 1u);
-    EXPECT_EQ(j.find("integer"), j.end());
-    EXPECT_EQ(j.erase("integer"), 0u);
-
-    EXPECT_NE(j.find("unsigned"), j.end());
-    EXPECT_EQ(j.erase("unsigned"), 1u);
-    EXPECT_EQ(j.find("unsigned"), j.end());
-    EXPECT_EQ(j.erase("unsigned"), 0u);
-
-    EXPECT_NE(j.find("boolean"), j.end());
-    EXPECT_EQ(j.erase("boolean"), 1u);
-    EXPECT_EQ(j.find("boolean"), j.end());
-    EXPECT_EQ(j.erase("boolean"), 0u);
-
-    EXPECT_NE(j.find("null"), j.end());
-    EXPECT_EQ(j.erase("null"), 1u);
-    EXPECT_EQ(j.find("null"), j.end());
-    EXPECT_EQ(j.erase("null"), 0u);
-
-    EXPECT_NE(j.find("string"), j.end());
-    EXPECT_EQ(j.erase("string"), 1u);
-    EXPECT_EQ(j.find("string"), j.end());
-    EXPECT_EQ(j.erase("string"), 0u);
-
-    EXPECT_NE(j.find("floating"), j.end());
-    EXPECT_EQ(j.erase("floating"), 1u);
-    EXPECT_EQ(j.find("floating"), j.end());
-    EXPECT_EQ(j.erase("floating"), 0u);
-
-    EXPECT_NE(j.find("object"), j.end());
-    EXPECT_EQ(j.erase("object"), 1u);
-    EXPECT_EQ(j.find("object"), j.end());
-    EXPECT_EQ(j.erase("object"), 0u);
-
-    EXPECT_NE(j.find("array"), j.end());
-    EXPECT_EQ(j.erase("array"), 1u);
-    EXPECT_EQ(j.find("array"), j.end());
-    EXPECT_EQ(j.erase("array"), 0u);
-}
-
-// erase(begin())
-TEST_F(JsonElementObjectRemoveTest, Begin)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-    jobject.erase(jobject.begin());
-    EXPECT_EQ(jobject, json({{"b", 1}, {"c", 17u}}));
-}
-
-TEST_F(JsonElementObjectRemoveTest, BeginConst)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-    jobject.erase(jobject.cbegin());
-    EXPECT_EQ(jobject, json({{"b", 1}, {"c", 17u}}));
-}
-
-// erase(begin(), end())
-TEST_F(JsonElementObjectRemoveTest, BeginEnd)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-#if 0
-    json::iterator it2 = jobject.erase(jobject.begin(), jobject.end());
-    EXPECT_EQ(jobject, json::object());
-    EXPECT_EQ(it2, jobject.end());
-#else
-    EXPECT_THROW(jobject.erase(jobject.begin(), jobject.end()), json::type_error);
-#endif
-}
-
-TEST_F(JsonElementObjectRemoveTest, BeginEndConst)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-#if 0
-    json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend());
-    EXPECT_EQ(jobject, json::object());
-    EXPECT_EQ(it2, jobject.cend());
-#else
-    EXPECT_THROW(jobject.erase(jobject.cbegin(), jobject.cend()), json::type_error);
-#endif
-}
-
-TEST_F(JsonElementObjectRemoveTest, BeginBegin)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-#if 0
-    json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin());
-    EXPECT_EQ(jobject, json({{"a", "a"}, {"b", 1}, {"c", 17u}}));
-    EXPECT_EQ(*it2, json("a"));
-#else
-    EXPECT_THROW(jobject.erase(jobject.begin(), jobject.end()), json::type_error);
-#endif
-}
-
-TEST_F(JsonElementObjectRemoveTest, BeginBeginConst)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-#if 0
-    json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin());
-    EXPECT_EQ(jobject, json({{"a", "a"}, {"b", 1}, {"c", 17u}}));
-    EXPECT_EQ(*it2, json("a"));
-#else
-    EXPECT_THROW(jobject.erase(jobject.cbegin(), jobject.cbegin()), json::type_error);
-#endif
-}
-
-TEST_F(JsonElementObjectRemoveTest, Offset)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-    json::iterator it = jobject.find("b");
-    jobject.erase(it);
-    EXPECT_EQ(jobject, json({{"a", "a"}, {"c", 17u}}));
-}
-
-TEST_F(JsonElementObjectRemoveTest, OffsetConst)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-    json::const_iterator it = jobject.find("b");
-    jobject.erase(it);
-    EXPECT_EQ(jobject, json({{"a", "a"}, {"c", 17u}}));
-}
-
-TEST_F(JsonElementObjectRemoveTest, Subrange)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
-#if 0
-    json::iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e"));
-    EXPECT_EQ(jobject, json({{"a", "a"}, {"e", true}}));
-    EXPECT_EQ(*it2, json(true));
-#else
-    EXPECT_THROW(jobject.erase(jobject.find("b"), jobject.find("e")), json::type_error);
-#endif
-}
-
-TEST_F(JsonElementObjectRemoveTest, SubrangeConst)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
-#if 0
-    json::const_iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e"));
-    EXPECT_EQ(jobject, json({{"a", "a"}, {"e", true}}));
-    EXPECT_EQ(*it2, json(true));
-#else
-    EXPECT_THROW(jobject.erase(jobject.find("b"), jobject.find("e")), json::type_error);
-#endif
-}
-
-TEST_F(JsonElementObjectRemoveTest, Different)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
-    json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-    EXPECT_THROW_MSG(jobject.erase(jobject2.begin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.202] iterator does not fit current value");
-    EXPECT_THROW_MSG(jobject.erase(jobject.begin(), jobject2.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-    EXPECT_THROW_MSG(jobject.erase(jobject2.begin(), jobject.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-    EXPECT_THROW_MSG(jobject.erase(jobject2.begin(), jobject2.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-}
-
-TEST_F(JsonElementObjectRemoveTest, DifferentConst)
-{
-    json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
-    json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-    EXPECT_THROW_MSG(jobject.erase(jobject2.cbegin()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.202] iterator does not fit current value");
-    EXPECT_THROW_MSG(jobject.erase(jobject.cbegin(), jobject2.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-    EXPECT_THROW_MSG(jobject.erase(jobject2.cbegin(), jobject.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-    EXPECT_THROW_MSG(jobject.erase(jobject2.cbegin(), jobject2.cend()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.203] iterators do not fit current value");
-}
-
-// remove element by key in non-object type
-TEST(JsonElementNonObjectKeyRemoveTest, Null)
-{
-    json j_nonobject(json::value_t::null);
-    EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with null");
-}
-
-TEST(JsonElementNonObjectKeyRemoveTest, Boolean)
-{
-    json j_nonobject(json::value_t::boolean);
-    EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with boolean");
-}
-
-TEST(JsonElementNonObjectKeyRemoveTest, String)
-{
-    json j_nonobject(json::value_t::string);
-    EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with string");
-}
-
-TEST(JsonElementNonObjectKeyRemoveTest, Array)
-{
-    json j_nonobject(json::value_t::array);
-    EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with array");
-}
-
-TEST(JsonElementNonObjectKeyRemoveTest, Integer)
-{
-    json j_nonobject(json::value_t::number_integer);
-    EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with number");
-}
-
-TEST(JsonElementNonObjectKeyRemoveTest, Float)
-{
-    json j_nonobject(json::value_t::number_float);
-    EXPECT_THROW_MSG(j_nonobject.erase("foo"), json::type_error,
-                     "[json.exception.type_error.307] cannot use erase() with number");
-}
-
-TEST_F(JsonElementObjectAccessTest, FindExist)
-{
-    for (auto key :
-            {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array"
-            })
-    {
-        EXPECT_NE(j.find(key), j.end());
-        EXPECT_EQ(*j.find(key), j.at(key));
-        EXPECT_NE(j_const.find(key), j_const.end());
-        EXPECT_EQ(*j_const.find(key), j_const.at(key));
-    }
-}
-
-TEST_F(JsonElementObjectAccessTest, FindNotExist)
-{
-    EXPECT_EQ(j.find("foo"), j.end());
-    EXPECT_EQ(j_const.find("foo"), j_const.end());
-}
-
-TEST(JsonElementNonObjectFindAccessTest, Null)
-{
-    json j_nonarray(json::value_t::null);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
-    EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
-}
-
-TEST(JsonElementNonObjectFindAccessTest, String)
-{
-    json j_nonarray(json::value_t::string);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
-    EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
-}
-
-TEST(JsonElementNonObjectFindAccessTest, Object)
-{
-    json j_nonarray(json::value_t::object);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
-    EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
-}
-
-TEST(JsonElementNonObjectFindAccessTest, Array)
-{
-    json j_nonarray(json::value_t::array);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
-    EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
-}
-
-TEST(JsonElementNonObjectFindAccessTest, Boolean)
-{
-    json j_nonarray(json::value_t::boolean);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
-    EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
-}
-
-TEST(JsonElementNonObjectFindAccessTest, Integer)
-{
-    json j_nonarray(json::value_t::number_integer);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
-    EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
-}
-
-TEST(JsonElementNonObjectFindAccessTest, Unsigned)
-{
-    json j_nonarray(json::value_t::number_unsigned);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
-    EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
-}
-
-TEST(JsonElementNonObjectFindAccessTest, Float)
-{
-    json j_nonarray(json::value_t::number_float);
-    const json j_nonarray_const(j_nonarray);
-    EXPECT_EQ(j_nonarray.find("foo"), j_nonarray.end());
-    EXPECT_EQ(j_nonarray_const.find("foo"), j_nonarray_const.end());
-}
-
-TEST_F(JsonElementObjectAccessTest, CountExist)
-{
-    for (auto key :
-            {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array"
-            })
-    {
-        EXPECT_EQ(j.count(key), 1u);
-        EXPECT_EQ(j_const.count(key), 1u);
-    }
-}
-
-TEST_F(JsonElementObjectAccessTest, CountNotExist)
-{
-    EXPECT_EQ(j.count("foo"), 0u);
-    EXPECT_EQ(j_const.count("foo"), 0u);
-}
-
-TEST(JsonElementNonObjectCountAccessTest, Null)
-{
-    json j_nonobject(json::value_t::null);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_EQ(j_nonobject.count("foo"), 0u);
-    EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
-}
-
-TEST(JsonElementNonObjectCountAccessTest, String)
-{
-    json j_nonobject(json::value_t::string);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_EQ(j_nonobject.count("foo"), 0u);
-    EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
-}
-
-TEST(JsonElementNonObjectCountAccessTest, Object)
-{
-    json j_nonobject(json::value_t::object);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_EQ(j_nonobject.count("foo"), 0u);
-    EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
-}
-
-TEST(JsonElementNonObjectCountAccessTest, Array)
-{
-    json j_nonobject(json::value_t::array);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_EQ(j_nonobject.count("foo"), 0u);
-    EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
-}
-
-TEST(JsonElementNonObjectCountAccessTest, Boolean)
-{
-    json j_nonobject(json::value_t::boolean);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_EQ(j_nonobject.count("foo"), 0u);
-    EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
-}
-
-TEST(JsonElementNonObjectCountAccessTest, Integer)
-{
-    json j_nonobject(json::value_t::number_integer);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_EQ(j_nonobject.count("foo"), 0u);
-    EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
-}
-
-TEST(JsonElementNonObjectCountAccessTest, Unsigned)
-{
-    json j_nonobject(json::value_t::number_unsigned);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_EQ(j_nonobject.count("foo"), 0u);
-    EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
-}
-
-TEST(JsonElementNonObjectCountAccessTest, Float)
-{
-    json j_nonobject(json::value_t::number_float);
-    const json j_nonobject_const(j_nonobject);
-    EXPECT_EQ(j_nonobject.count("foo"), 0u);
-    EXPECT_EQ(j_nonobject_const.count("foo"), 0u);
-}
-
-TEST_F(JsonElementObjectAccessTest, PointerValueNotExist)
-{
-    EXPECT_EQ(j.value("/not/existing"_json_pointer, 2), 2);
-    EXPECT_EQ(j.value("/not/existing"_json_pointer, 2u), 2u);
-    EXPECT_EQ(j.value("/not/existing"_json_pointer, false), false);
-    EXPECT_EQ(j.value("/not/existing"_json_pointer, "bar"), "bar");
-    EXPECT_LT(std::fabs(j.value("/not/existing"_json_pointer, 12.34) - 12.34), 0.001);
-    EXPECT_EQ(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})), json({{"foo", "bar"}}));
-    EXPECT_EQ(j.value("/not/existing"_json_pointer, json({10, 100})), json({10, 100}));
-
-    EXPECT_EQ(j_const.value("/not/existing"_json_pointer, 2), 2);
-    EXPECT_EQ(j_const.value("/not/existing"_json_pointer, 2u), 2u);
-    EXPECT_EQ(j_const.value("/not/existing"_json_pointer, false), false);
-    EXPECT_EQ(j_const.value("/not/existing"_json_pointer, "bar"), "bar");
-    EXPECT_LT(std::fabs(j_const.value("/not/existing"_json_pointer, 12.34) - 12.34), 0.001);
-    EXPECT_EQ(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})), json({{"foo", "bar"}}));
-    EXPECT_EQ(j_const.value("/not/existing"_json_pointer, json({10, 100})), json({10, 100}));
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-inspection.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-inspection.cpp
deleted file mode 100644
index 79c63f0..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-inspection.cpp
+++ /dev/null
@@ -1,385 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-TEST(JsonConvTypeCheckTest, Object)
-{
-    json j {{"foo", 1}, {"bar", false}};
-    EXPECT_FALSE(j.is_null());
-    EXPECT_FALSE(j.is_boolean());
-    EXPECT_FALSE(j.is_number());
-    EXPECT_FALSE(j.is_number_integer());
-    EXPECT_FALSE(j.is_number_unsigned());
-    EXPECT_FALSE(j.is_number_float());
-    EXPECT_TRUE(j.is_object());
-    EXPECT_FALSE(j.is_array());
-    EXPECT_FALSE(j.is_string());
-    EXPECT_FALSE(j.is_discarded());
-    EXPECT_FALSE(j.is_primitive());
-    EXPECT_TRUE(j.is_structured());
-}
-
-TEST(JsonConvTypeCheckTest, Array)
-{
-    json j {"foo", 1, 1u, 42.23, false};
-    EXPECT_FALSE(j.is_null());
-    EXPECT_FALSE(j.is_boolean());
-    EXPECT_FALSE(j.is_number());
-    EXPECT_FALSE(j.is_number_integer());
-    EXPECT_FALSE(j.is_number_unsigned());
-    EXPECT_FALSE(j.is_number_float());
-    EXPECT_FALSE(j.is_object());
-    EXPECT_TRUE(j.is_array());
-    EXPECT_FALSE(j.is_string());
-    EXPECT_FALSE(j.is_discarded());
-    EXPECT_FALSE(j.is_primitive());
-    EXPECT_TRUE(j.is_structured());
-}
-
-TEST(JsonConvTypeCheckTest, Null)
-{
-    json j(nullptr);
-    EXPECT_TRUE(j.is_null());
-    EXPECT_FALSE(j.is_boolean());
-    EXPECT_FALSE(j.is_number());
-    EXPECT_FALSE(j.is_number_integer());
-    EXPECT_FALSE(j.is_number_unsigned());
-    EXPECT_FALSE(j.is_number_float());
-    EXPECT_FALSE(j.is_object());
-    EXPECT_FALSE(j.is_array());
-    EXPECT_FALSE(j.is_string());
-    EXPECT_FALSE(j.is_discarded());
-    EXPECT_TRUE(j.is_primitive());
-    EXPECT_FALSE(j.is_structured());
-}
-
-TEST(JsonConvTypeCheckTest, Boolean)
-{
-    json j(true);
-    EXPECT_FALSE(j.is_null());
-    EXPECT_TRUE(j.is_boolean());
-    EXPECT_FALSE(j.is_number());
-    EXPECT_FALSE(j.is_number_integer());
-    EXPECT_FALSE(j.is_number_unsigned());
-    EXPECT_FALSE(j.is_number_float());
-    EXPECT_FALSE(j.is_object());
-    EXPECT_FALSE(j.is_array());
-    EXPECT_FALSE(j.is_string());
-    EXPECT_FALSE(j.is_discarded());
-    EXPECT_TRUE(j.is_primitive());
-    EXPECT_FALSE(j.is_structured());
-}
-
-TEST(JsonConvTypeCheckTest, String)
-{
-    json j("Hello world");
-    EXPECT_FALSE(j.is_null());
-    EXPECT_FALSE(j.is_boolean());
-    EXPECT_FALSE(j.is_number());
-    EXPECT_FALSE(j.is_number_integer());
-    EXPECT_FALSE(j.is_number_unsigned());
-    EXPECT_FALSE(j.is_number_float());
-    EXPECT_FALSE(j.is_object());
-    EXPECT_FALSE(j.is_array());
-    EXPECT_TRUE(j.is_string());
-    EXPECT_FALSE(j.is_discarded());
-    EXPECT_TRUE(j.is_primitive());
-    EXPECT_FALSE(j.is_structured());
-}
-
-TEST(JsonConvTypeCheckTest, Integer)
-{
-    json j(42);
-    EXPECT_FALSE(j.is_null());
-    EXPECT_FALSE(j.is_boolean());
-    EXPECT_TRUE(j.is_number());
-    EXPECT_TRUE(j.is_number_integer());
-    EXPECT_FALSE(j.is_number_unsigned());
-    EXPECT_FALSE(j.is_number_float());
-    EXPECT_FALSE(j.is_object());
-    EXPECT_FALSE(j.is_array());
-    EXPECT_FALSE(j.is_string());
-    EXPECT_FALSE(j.is_discarded());
-    EXPECT_TRUE(j.is_primitive());
-    EXPECT_FALSE(j.is_structured());
-}
-
-TEST(JsonConvTypeCheckTest, Unsigned)
-{
-    json j(42u);
-    EXPECT_FALSE(j.is_null());
-    EXPECT_FALSE(j.is_boolean());
-    EXPECT_TRUE(j.is_number());
-    EXPECT_TRUE(j.is_number_integer());
-    EXPECT_TRUE(j.is_number_unsigned());
-    EXPECT_FALSE(j.is_number_float());
-    EXPECT_FALSE(j.is_object());
-    EXPECT_FALSE(j.is_array());
-    EXPECT_FALSE(j.is_string());
-    EXPECT_FALSE(j.is_discarded());
-    EXPECT_TRUE(j.is_primitive());
-    EXPECT_FALSE(j.is_structured());
-}
-
-TEST(JsonConvTypeCheckTest, Float)
-{
-    json j(42.23);
-    EXPECT_FALSE(j.is_null());
-    EXPECT_FALSE(j.is_boolean());
-    EXPECT_TRUE(j.is_number());
-    EXPECT_FALSE(j.is_number_integer());
-    EXPECT_FALSE(j.is_number_unsigned());
-    EXPECT_TRUE(j.is_number_float());
-    EXPECT_FALSE(j.is_object());
-    EXPECT_FALSE(j.is_array());
-    EXPECT_FALSE(j.is_string());
-    EXPECT_FALSE(j.is_discarded());
-    EXPECT_TRUE(j.is_primitive());
-    EXPECT_FALSE(j.is_structured());
-}
-
-TEST(JsonConvTypeCheckTest, Discarded)
-{
-    json j(json::value_t::discarded);
-    EXPECT_FALSE(j.is_null());
-    EXPECT_FALSE(j.is_boolean());
-    EXPECT_FALSE(j.is_number());
-    EXPECT_FALSE(j.is_number_integer());
-    EXPECT_FALSE(j.is_number_unsigned());
-    EXPECT_FALSE(j.is_number_float());
-    EXPECT_FALSE(j.is_object());
-    EXPECT_FALSE(j.is_array());
-    EXPECT_FALSE(j.is_string());
-    EXPECT_TRUE(j.is_discarded());
-    EXPECT_FALSE(j.is_primitive());
-    EXPECT_FALSE(j.is_structured());
-}
-
-class JsonConvSerializationTest : public ::testing::Test {
- protected:
-    json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
-};
-#if 0
-// no indent / indent=-1
-TEST_F(JsonConvSerializationTest, NoIndent)
-{
-    EXPECT_EQ(j.dump(),
-          "{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}");
-
-    EXPECT_EQ(j.dump(), j.dump(-1));
-}
-
-// indent=0
-TEST_F(JsonConvSerializationTest, Indent0)
-{
-    EXPECT_EQ(j.dump(0),
-          "{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}");
-}
-
-// indent=1, space='\t'
-TEST_F(JsonConvSerializationTest, Indent1)
-{
-    EXPECT_EQ(j.dump(1, '\t'),
-          "{\n\t\"array\": [\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4\n\t],\n\t\"boolean\": false,\n\t\"null\": null,\n\t\"number\": 42,\n\t\"object\": {},\n\t\"string\": \"Hello world\"\n}");
-}
-
-// indent=4
-TEST_F(JsonConvSerializationTest, Indent4)
-{
-    EXPECT_EQ(j.dump(4),
-          "{\n    \"array\": [\n        1,\n        2,\n        3,\n        4\n    ],\n    \"boolean\": false,\n    \"null\": null,\n    \"number\": 42,\n    \"object\": {},\n    \"string\": \"Hello world\"\n}");
-}
-#endif
-// indent=x
-TEST_F(JsonConvSerializationTest, IndentX)
-{
-    EXPECT_EQ(j.dump().size(), 94u);
-    EXPECT_EQ(j.dump(1).size(), 127u);
-    EXPECT_EQ(j.dump(2).size(), 142u);
-    EXPECT_EQ(j.dump(512).size(), 7792u);
-}
-
-// dump and floating-point numbers
-TEST_F(JsonConvSerializationTest, Float)
-{
-    auto s = json(42.23).dump();
-    EXPECT_NE(s.find("42.23"), std::string::npos);
-}
-
-// dump and small floating-point numbers
-TEST_F(JsonConvSerializationTest, SmallFloat)
-{
-    auto s = json(1.23456e-78).dump();
-    EXPECT_NE(s.find("1.23456e-78"), std::string::npos);
-}
-
-// dump and non-ASCII characters
-TEST_F(JsonConvSerializationTest, NonAscii)
-{
-    EXPECT_EQ(json("ä").dump(), "\"ä\"");
-    EXPECT_EQ(json("Ö").dump(), "\"Ö\"");
-    EXPECT_EQ(json("❤️").dump(), "\"❤️\"");
-}
-
-// serialization of discarded element
-TEST_F(JsonConvSerializationTest, Discarded)
-{
-    json j_discarded(json::value_t::discarded);
-    EXPECT_EQ(j_discarded.dump(), "<discarded>");
-}
-
-TEST(JsonConvRoundTripTest, Case)
-{
-    for (const auto& s :
-{"3.141592653589793", "1000000000000000010E5"
-})
-    {
-        SCOPED_TRACE(s);
-        json j1 = json::parse(s);
-        std::string s1 = j1.dump();
-        json j2 = json::parse(s1);
-        std::string s2 = j2.dump();
-        EXPECT_EQ(s1, s2);
-    }
-}
-
-// return the type of the object (explicit)
-TEST(JsonConvTypeExplicitTest, Null)
-{
-    json j = nullptr;
-    EXPECT_EQ(j.type(), json::value_t::null);
-}
-
-TEST(JsonConvTypeExplicitTest, Object)
-{
-    json j = {{"foo", "bar"}};
-    EXPECT_EQ(j.type(), json::value_t::object);
-}
-
-TEST(JsonConvTypeExplicitTest, Array)
-{
-    json j = {1, 2, 3, 4};
-    EXPECT_EQ(j.type(), json::value_t::array);
-}
-
-TEST(JsonConvTypeExplicitTest, Boolean)
-{
-    json j = true;
-    EXPECT_EQ(j.type(), json::value_t::boolean);
-}
-
-TEST(JsonConvTypeExplicitTest, String)
-{
-    json j = "Hello world";
-    EXPECT_EQ(j.type(), json::value_t::string);
-}
-
-TEST(JsonConvTypeExplicitTest, Integer)
-{
-    json j = 23;
-    EXPECT_EQ(j.type(), json::value_t::number_integer);
-}
-
-TEST(JsonConvTypeExplicitTest, Unsigned)
-{
-    json j = 23u;
-    EXPECT_EQ(j.type(), json::value_t::number_unsigned);
-}
-
-TEST(JsonConvTypeExplicitTest, Float)
-{
-    json j = 42.23;
-    EXPECT_EQ(j.type(), json::value_t::number_float);
-}
-
-// return the type of the object (implicit)
-TEST(JsonConvTypeImplicitTest, Null)
-{
-    json j = nullptr;
-    json::value_t t = j;
-    EXPECT_EQ(t, j.type());
-}
-
-TEST(JsonConvTypeImplicitTest, Object)
-{
-    json j = {{"foo", "bar"}};
-    json::value_t t = j;
-    EXPECT_EQ(t, j.type());
-}
-
-TEST(JsonConvTypeImplicitTest, Array)
-{
-    json j = {1, 2, 3, 4};
-    json::value_t t = j;
-    EXPECT_EQ(t, j.type());
-}
-
-TEST(JsonConvTypeImplicitTest, Boolean)
-{
-    json j = true;
-    json::value_t t = j;
-    EXPECT_EQ(t, j.type());
-}
-
-TEST(JsonConvTypeImplicitTest, String)
-{
-    json j = "Hello world";
-    json::value_t t = j;
-    EXPECT_EQ(t, j.type());
-}
-
-TEST(JsonConvTypeImplicitTest, Integer)
-{
-    json j = 23;
-    json::value_t t = j;
-    EXPECT_EQ(t, j.type());
-}
-
-TEST(JsonConvTypeImplicitTest, Unsigned)
-{
-    json j = 23u;
-    json::value_t t = j;
-    EXPECT_EQ(t, j.type());
-}
-
-TEST(JsonConvTypeImplicitTest, Float)
-{
-    json j = 42.23;
-    json::value_t t = j;
-    EXPECT_EQ(t, j.type());
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-iterators1.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-iterators1.cpp
deleted file mode 100644
index bae3862..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-iterators1.cpp
+++ /dev/null
@@ -1,1617 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-using wpi::JsonTest;
-
-TEST(JsonIteratorBasicTest, Uninitialized)
-{
-    json::iterator it;
-    EXPECT_EQ(JsonTest::GetObject(it), nullptr);
-
-    json::const_iterator cit;
-    EXPECT_EQ(JsonTest::GetObject(cit), nullptr);
-}
-
-class JsonIteratorBooleanTest : public ::testing::Test {
- public:
-    JsonIteratorBooleanTest() : j_const(j) {}
-
- protected:
-    json j = true;
-    json j_const;
-};
-
-TEST_F(JsonIteratorBooleanTest, BeginEnd)
-{
-    json::iterator it = j.begin();
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.begin());
-    EXPECT_EQ(it, j.end());
-
-    it--;
-    EXPECT_EQ(it, j.begin());
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.begin());
-    EXPECT_EQ(it, j.end());
-
-    --it;
-    EXPECT_EQ(it, j.begin());
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorBooleanTest, ConstBeginEnd)
-{
-    json::const_iterator it = j_const.begin();
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.begin());
-    EXPECT_EQ(it, j_const.end());
-
-    it--;
-    EXPECT_EQ(it, j_const.begin());
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.begin());
-    EXPECT_EQ(it, j_const.end());
-
-    --it;
-    EXPECT_EQ(it, j_const.begin());
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-}
-
-TEST_F(JsonIteratorBooleanTest, CBeginEnd)
-{
-    json::const_iterator it = j.cbegin();
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.cbegin());
-    EXPECT_EQ(it, j.cend());
-
-    it--;
-    EXPECT_EQ(it, j.cbegin());
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.cbegin());
-    EXPECT_EQ(it, j.cend());
-
-    --it;
-    EXPECT_EQ(it, j.cbegin());
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorBooleanTest, ConstCBeginEnd)
-{
-    json::const_iterator it = j_const.cbegin();
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.cbegin());
-    EXPECT_EQ(it, j_const.cend());
-
-    it--;
-    EXPECT_EQ(it, j_const.cbegin());
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.cbegin());
-    EXPECT_EQ(it, j_const.cend());
-
-    --it;
-    EXPECT_EQ(it, j_const.cbegin());
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-}
-#if 0
-TEST_F(JsonIteratorBooleanTest, RBeginEnd)
-{
-    json::reverse_iterator it = j.rbegin();
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.rbegin());
-    EXPECT_EQ(it, j.rend());
-
-    it--;
-    EXPECT_EQ(it, j.rbegin());
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.rbegin());
-    EXPECT_EQ(it, j.rend());
-
-    --it;
-    EXPECT_EQ(it, j.rbegin());
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorBooleanTest, CRBeginEnd)
-{
-    json::const_reverse_iterator it = j.crbegin();
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.crbegin());
-    EXPECT_EQ(it, j.crend());
-
-    it--;
-    EXPECT_EQ(it, j.crbegin());
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.crbegin());
-    EXPECT_EQ(it, j.crend());
-
-    --it;
-    EXPECT_EQ(it, j.crbegin());
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorBooleanTest, ConstCRBeginEnd)
-{
-    json::const_reverse_iterator it = j_const.crbegin();
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.crbegin());
-    EXPECT_EQ(it, j_const.crend());
-
-    it--;
-    EXPECT_EQ(it, j_const.crbegin());
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.crbegin());
-    EXPECT_EQ(it, j_const.crend());
-
-    --it;
-    EXPECT_EQ(it, j_const.crbegin());
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-}
-#endif
-TEST_F(JsonIteratorBooleanTest, KeyValue)
-{
-    auto it = j.begin();
-    auto cit = j_const.cbegin();
-    EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(it.value(), json(true));
-    EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(cit.value(), json(true));
-#if 0
-    auto rit = j.rend();
-    auto crit = j.crend();
-    EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-#endif
-}
-
-class JsonIteratorStringTest : public ::testing::Test {
- public:
-    JsonIteratorStringTest() : j_const(j) {}
-
- protected:
-    json j = "hello world";
-    json j_const;
-};
-
-TEST_F(JsonIteratorStringTest, BeginEnd)
-{
-    json::iterator it = j.begin();
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.begin());
-    EXPECT_EQ(it, j.end());
-
-    it--;
-    EXPECT_EQ(it, j.begin());
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.begin());
-    EXPECT_EQ(it, j.end());
-
-    --it;
-    EXPECT_EQ(it, j.begin());
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorStringTest, ConstBeginEnd)
-{
-    json::const_iterator it = j_const.begin();
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.begin());
-    EXPECT_EQ(it, j_const.end());
-
-    it--;
-    EXPECT_EQ(it, j_const.begin());
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.begin());
-    EXPECT_EQ(it, j_const.end());
-
-    --it;
-    EXPECT_EQ(it, j_const.begin());
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-}
-
-TEST_F(JsonIteratorStringTest, CBeginEnd)
-{
-    json::const_iterator it = j.cbegin();
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.cbegin());
-    EXPECT_EQ(it, j.cend());
-
-    it--;
-    EXPECT_EQ(it, j.cbegin());
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.cbegin());
-    EXPECT_EQ(it, j.cend());
-
-    --it;
-    EXPECT_EQ(it, j.cbegin());
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorStringTest, ConstCBeginEnd)
-{
-    json::const_iterator it = j_const.cbegin();
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.cbegin());
-    EXPECT_EQ(it, j_const.cend());
-
-    it--;
-    EXPECT_EQ(it, j_const.cbegin());
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.cbegin());
-    EXPECT_EQ(it, j_const.cend());
-
-    --it;
-    EXPECT_EQ(it, j_const.cbegin());
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-}
-#if 0
-TEST_F(JsonIteratorStringTest, RBeginEnd)
-{
-    json::reverse_iterator it = j.rbegin();
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.rbegin());
-    EXPECT_EQ(it, j.rend());
-
-    it--;
-    EXPECT_EQ(it, j.rbegin());
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.rbegin());
-    EXPECT_EQ(it, j.rend());
-
-    --it;
-    EXPECT_EQ(it, j.rbegin());
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorStringTest, CRBeginEnd)
-{
-    json::const_reverse_iterator it = j.crbegin();
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.crbegin());
-    EXPECT_EQ(it, j.crend());
-
-    it--;
-    EXPECT_EQ(it, j.crbegin());
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.crbegin());
-    EXPECT_EQ(it, j.crend());
-
-    --it;
-    EXPECT_EQ(it, j.crbegin());
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorStringTest, ConstCRBeginEnd)
-{
-    json::const_reverse_iterator it = j_const.crbegin();
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.crbegin());
-    EXPECT_EQ(it, j_const.crend());
-
-    it--;
-    EXPECT_EQ(it, j_const.crbegin());
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.crbegin());
-    EXPECT_EQ(it, j_const.crend());
-
-    --it;
-    EXPECT_EQ(it, j_const.crbegin());
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-}
-#endif
-TEST_F(JsonIteratorStringTest, KeyValue)
-{
-    auto it = j.begin();
-    auto cit = j_const.cbegin();
-    EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(it.value(), json("hello world"));
-    EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(cit.value(), json("hello world"));
-#if 0
-    auto rit = j.rend();
-    auto crit = j.crend();
-    EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-#endif
-}
-
-class JsonIteratorArrayTest : public ::testing::Test {
- public:
-    JsonIteratorArrayTest() : j_const(j) {}
-
- protected:
-    json j = {1, 2, 3};
-    json j_const;
-};
-
-TEST_F(JsonIteratorArrayTest, BeginEnd)
-{
-    json::iterator it_begin = j.begin();
-    json::iterator it_end = j.end();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[0]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[1]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[2]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-
-TEST_F(JsonIteratorArrayTest, ConstBeginEnd)
-{
-    json::const_iterator it_begin = j_const.begin();
-    json::const_iterator it_end = j_const.end();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j_const[0]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j_const[1]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j_const[2]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-
-TEST_F(JsonIteratorArrayTest, CBeginEnd)
-{
-    json::const_iterator it_begin = j.cbegin();
-    json::const_iterator it_end = j.cend();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[0]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[1]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[2]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-
-TEST_F(JsonIteratorArrayTest, ConstCBeginEnd)
-{
-    json::const_iterator it_begin = j_const.cbegin();
-    json::const_iterator it_end = j_const.cend();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[0]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[1]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[2]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-#if 0
-TEST_F(JsonIteratorArrayTest, RBeginEnd)
-{
-    json::reverse_iterator it_begin = j.rbegin();
-    json::reverse_iterator it_end = j.rend();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[2]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[1]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[0]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-
-TEST_F(JsonIteratorArrayTest, CRBeginEnd)
-{
-    json::const_reverse_iterator it_begin = j.crbegin();
-    json::const_reverse_iterator it_end = j.crend();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[2]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[1]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[0]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-
-TEST_F(JsonIteratorArrayTest, ConstCRBeginEnd)
-{
-    json::const_reverse_iterator it_begin = j_const.crbegin();
-    json::const_reverse_iterator it_end = j_const.crend();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[2]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[1]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j[0]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-#endif
-TEST_F(JsonIteratorArrayTest, KeyValue)
-{
-    auto it = j.begin();
-    auto cit = j_const.cbegin();
-    EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(it.value(), json(1));
-    EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(cit.value(), json(1));
-}
-
-class JsonIteratorObjectTest : public ::testing::Test {
- public:
-    JsonIteratorObjectTest() : j_const(j) {}
-
- protected:
-    json j = {{"A", 1}, {"B", 2}, {"C", 3}};
-    json j_const;
-};
-
-TEST_F(JsonIteratorObjectTest, BeginEnd)
-{
-    json::iterator it_begin = j.begin();
-    json::iterator it_end = j.end();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["A"]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["B"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["C"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-
-TEST_F(JsonIteratorObjectTest, ConstBeginEnd)
-{
-    json::const_iterator it_begin = j_const.begin();
-    json::const_iterator it_end = j_const.end();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j_const["A"]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j_const["B"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j_const["C"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-
-TEST_F(JsonIteratorObjectTest, CBeginEnd)
-{
-    json::const_iterator it_begin = j.cbegin();
-    json::const_iterator it_end = j.cend();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["A"]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["B"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["C"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-
-TEST_F(JsonIteratorObjectTest, ConstCBeginEnd)
-{
-    json::const_iterator it_begin = j_const.cbegin();
-    json::const_iterator it_end = j_const.cend();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j_const["A"]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j_const["B"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j_const["C"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-#if 0
-TEST_F(JsonIteratorObjectTest, RBeginEnd)
-{
-    json::reverse_iterator it_begin = j.rbegin();
-    json::reverse_iterator it_end = j.rend();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["C"]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["B"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["A"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-
-TEST_F(JsonIteratorObjectTest, CRBeginEnd)
-{
-    json::const_reverse_iterator it_begin = j.crbegin();
-    json::const_reverse_iterator it_end = j.crend();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["C"]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["B"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["A"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-
-TEST_F(JsonIteratorObjectTest, ConstCRBeginEnd)
-{
-    json::const_reverse_iterator it_begin = j_const.crbegin();
-    json::const_reverse_iterator it_end = j_const.crend();
-
-    auto it = it_begin;
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["C"]);
-
-    it++;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["B"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_NE(it, it_end);
-    EXPECT_EQ(*it, j["A"]);
-
-    ++it;
-    EXPECT_NE(it, it_begin);
-    EXPECT_EQ(it, it_end);
-}
-#endif
-
-TEST_F(JsonIteratorObjectTest, KeyValue)
-{
-    auto it = j.begin();
-    auto cit = j_const.cbegin();
-    EXPECT_EQ(it.key(), "A");
-    EXPECT_EQ(it.value(), json(1));
-    EXPECT_EQ(cit.key(), "A");
-    EXPECT_EQ(cit.value(), json(1));
-}
-
-class JsonIteratorIntegerTest : public ::testing::Test {
- public:
-    JsonIteratorIntegerTest() : j_const(j) {}
-
- protected:
-    json j = 23;
-    json j_const;
-};
-
-TEST_F(JsonIteratorIntegerTest, BeginEnd)
-{
-    json::iterator it = j.begin();
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.begin());
-    EXPECT_EQ(it, j.end());
-
-    it--;
-    EXPECT_EQ(it, j.begin());
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.begin());
-    EXPECT_EQ(it, j.end());
-
-    --it;
-    EXPECT_EQ(it, j.begin());
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorIntegerTest, ConstBeginEnd)
-{
-    json::const_iterator it = j_const.begin();
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.begin());
-    EXPECT_EQ(it, j_const.end());
-
-    it--;
-    EXPECT_EQ(it, j_const.begin());
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.begin());
-    EXPECT_EQ(it, j_const.end());
-
-    --it;
-    EXPECT_EQ(it, j_const.begin());
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-}
-
-TEST_F(JsonIteratorIntegerTest, CBeginEnd)
-{
-    json::const_iterator it = j.cbegin();
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.cbegin());
-    EXPECT_EQ(it, j.cend());
-
-    it--;
-    EXPECT_EQ(it, j.cbegin());
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.cbegin());
-    EXPECT_EQ(it, j.cend());
-
-    --it;
-    EXPECT_EQ(it, j.cbegin());
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorIntegerTest, ConstCBeginEnd)
-{
-    json::const_iterator it = j_const.cbegin();
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.cbegin());
-    EXPECT_EQ(it, j_const.cend());
-
-    it--;
-    EXPECT_EQ(it, j_const.cbegin());
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.cbegin());
-    EXPECT_EQ(it, j_const.cend());
-
-    --it;
-    EXPECT_EQ(it, j_const.cbegin());
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-}
-#if 0
-TEST_F(JsonIteratorIntegerTest, RBeginEnd)
-{
-    json::reverse_iterator it = j.rbegin();
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.rbegin());
-    EXPECT_EQ(it, j.rend());
-
-    it--;
-    EXPECT_EQ(it, j.rbegin());
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.rbegin());
-    EXPECT_EQ(it, j.rend());
-
-    --it;
-    EXPECT_EQ(it, j.rbegin());
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorIntegerTest, CRBeginEnd)
-{
-    json::const_reverse_iterator it = j.crbegin();
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.crbegin());
-    EXPECT_EQ(it, j.crend());
-
-    it--;
-    EXPECT_EQ(it, j.crbegin());
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.crbegin());
-    EXPECT_EQ(it, j.crend());
-
-    --it;
-    EXPECT_EQ(it, j.crbegin());
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorIntegerTest, ConstCRBeginEnd)
-{
-    json::const_reverse_iterator it = j_const.crbegin();
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.crbegin());
-    EXPECT_EQ(it, j_const.crend());
-
-    it--;
-    EXPECT_EQ(it, j_const.crbegin());
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.crbegin());
-    EXPECT_EQ(it, j_const.crend());
-
-    --it;
-    EXPECT_EQ(it, j_const.crbegin());
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-}
-#endif
-TEST_F(JsonIteratorIntegerTest, KeyValue)
-{
-    auto it = j.begin();
-    auto cit = j_const.cbegin();
-    EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(it.value(), json(23));
-    EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(cit.value(), json(23));
-#if 0
-    auto rit = j.rend();
-    auto crit = j.crend();
-    EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-#endif
-}
-
-class JsonIteratorUnsignedTest : public ::testing::Test {
- public:
-    JsonIteratorUnsignedTest() : j_const(j) {}
-
- protected:
-    json j = 23u;
-    json j_const;
-};
-
-TEST_F(JsonIteratorUnsignedTest, BeginEnd)
-{
-    json::iterator it = j.begin();
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.begin());
-    EXPECT_EQ(it, j.end());
-
-    it--;
-    EXPECT_EQ(it, j.begin());
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.begin());
-    EXPECT_EQ(it, j.end());
-
-    --it;
-    EXPECT_EQ(it, j.begin());
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorUnsignedTest, ConstBeginEnd)
-{
-    json::const_iterator it = j_const.begin();
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.begin());
-    EXPECT_EQ(it, j_const.end());
-
-    it--;
-    EXPECT_EQ(it, j_const.begin());
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.begin());
-    EXPECT_EQ(it, j_const.end());
-
-    --it;
-    EXPECT_EQ(it, j_const.begin());
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-}
-
-TEST_F(JsonIteratorUnsignedTest, CBeginEnd)
-{
-    json::const_iterator it = j.cbegin();
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.cbegin());
-    EXPECT_EQ(it, j.cend());
-
-    it--;
-    EXPECT_EQ(it, j.cbegin());
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.cbegin());
-    EXPECT_EQ(it, j.cend());
-
-    --it;
-    EXPECT_EQ(it, j.cbegin());
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorUnsignedTest, ConstCBeginEnd)
-{
-    json::const_iterator it = j_const.cbegin();
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.cbegin());
-    EXPECT_EQ(it, j_const.cend());
-
-    it--;
-    EXPECT_EQ(it, j_const.cbegin());
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.cbegin());
-    EXPECT_EQ(it, j_const.cend());
-
-    --it;
-    EXPECT_EQ(it, j_const.cbegin());
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-}
-#if 0
-TEST_F(JsonIteratorUnsignedTest, RBeginEnd)
-{
-    json::reverse_iterator it = j.rbegin();
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.rbegin());
-    EXPECT_EQ(it, j.rend());
-
-    it--;
-    EXPECT_EQ(it, j.rbegin());
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.rbegin());
-    EXPECT_EQ(it, j.rend());
-
-    --it;
-    EXPECT_EQ(it, j.rbegin());
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorUnsignedTest, CRBeginEnd)
-{
-    json::const_reverse_iterator it = j.crbegin();
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.crbegin());
-    EXPECT_EQ(it, j.crend());
-
-    it--;
-    EXPECT_EQ(it, j.crbegin());
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.crbegin());
-    EXPECT_EQ(it, j.crend());
-
-    --it;
-    EXPECT_EQ(it, j.crbegin());
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorUnsignedTest, ConstCRBeginEnd)
-{
-    json::const_reverse_iterator it = j_const.crbegin();
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.crbegin());
-    EXPECT_EQ(it, j_const.crend());
-
-    it--;
-    EXPECT_EQ(it, j_const.crbegin());
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.crbegin());
-    EXPECT_EQ(it, j_const.crend());
-
-    --it;
-    EXPECT_EQ(it, j_const.crbegin());
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-}
-#endif
-TEST_F(JsonIteratorUnsignedTest, KeyValue)
-{
-    auto it = j.begin();
-    auto cit = j_const.cbegin();
-    EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(it.value(), json(23));
-    EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(cit.value(), json(23));
-#if 0
-    auto rit = j.rend();
-    auto crit = j.crend();
-    EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-#endif
-}
-
-class JsonIteratorFloatTest : public ::testing::Test {
- public:
-    JsonIteratorFloatTest() : j_const(j) {}
-
- protected:
-    json j = 23.42;
-    json j_const;
-};
-
-TEST_F(JsonIteratorFloatTest, BeginEnd)
-{
-    json::iterator it = j.begin();
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.begin());
-    EXPECT_EQ(it, j.end());
-
-    it--;
-    EXPECT_EQ(it, j.begin());
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.begin());
-    EXPECT_EQ(it, j.end());
-
-    --it;
-    EXPECT_EQ(it, j.begin());
-    EXPECT_NE(it, j.end());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorFloatTest, ConstBeginEnd)
-{
-    json::const_iterator it = j_const.begin();
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.begin());
-    EXPECT_EQ(it, j_const.end());
-
-    it--;
-    EXPECT_EQ(it, j_const.begin());
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.begin());
-    EXPECT_EQ(it, j_const.end());
-
-    --it;
-    EXPECT_EQ(it, j_const.begin());
-    EXPECT_NE(it, j_const.end());
-    EXPECT_EQ(*it, j_const);
-}
-
-TEST_F(JsonIteratorFloatTest, CBeginEnd)
-{
-    json::const_iterator it = j.cbegin();
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.cbegin());
-    EXPECT_EQ(it, j.cend());
-
-    it--;
-    EXPECT_EQ(it, j.cbegin());
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.cbegin());
-    EXPECT_EQ(it, j.cend());
-
-    --it;
-    EXPECT_EQ(it, j.cbegin());
-    EXPECT_NE(it, j.cend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorFloatTest, ConstCBeginEnd)
-{
-    json::const_iterator it = j_const.cbegin();
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.cbegin());
-    EXPECT_EQ(it, j_const.cend());
-
-    it--;
-    EXPECT_EQ(it, j_const.cbegin());
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.cbegin());
-    EXPECT_EQ(it, j_const.cend());
-
-    --it;
-    EXPECT_EQ(it, j_const.cbegin());
-    EXPECT_NE(it, j_const.cend());
-    EXPECT_EQ(*it, j_const);
-}
-#if 0
-TEST_F(JsonIteratorFloatTest, RBeginEnd)
-{
-    json::reverse_iterator it = j.rbegin();
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.rbegin());
-    EXPECT_EQ(it, j.rend());
-
-    it--;
-    EXPECT_EQ(it, j.rbegin());
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.rbegin());
-    EXPECT_EQ(it, j.rend());
-
-    --it;
-    EXPECT_EQ(it, j.rbegin());
-    EXPECT_NE(it, j.rend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorFloatTest, CRBeginEnd)
-{
-    json::const_reverse_iterator it = j.crbegin();
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-
-    it++;
-    EXPECT_NE(it, j.crbegin());
-    EXPECT_EQ(it, j.crend());
-
-    it--;
-    EXPECT_EQ(it, j.crbegin());
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-
-    ++it;
-    EXPECT_NE(it, j.crbegin());
-    EXPECT_EQ(it, j.crend());
-
-    --it;
-    EXPECT_EQ(it, j.crbegin());
-    EXPECT_NE(it, j.crend());
-    EXPECT_EQ(*it, j);
-}
-
-TEST_F(JsonIteratorFloatTest, ConstCRBeginEnd)
-{
-    json::const_reverse_iterator it = j_const.crbegin();
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-
-    it++;
-    EXPECT_NE(it, j_const.crbegin());
-    EXPECT_EQ(it, j_const.crend());
-
-    it--;
-    EXPECT_EQ(it, j_const.crbegin());
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-
-    ++it;
-    EXPECT_NE(it, j_const.crbegin());
-    EXPECT_EQ(it, j_const.crend());
-
-    --it;
-    EXPECT_EQ(it, j_const.crbegin());
-    EXPECT_NE(it, j_const.crend());
-    EXPECT_EQ(*it, j_const);
-}
-#endif
-TEST_F(JsonIteratorFloatTest, KeyValue)
-{
-    auto it = j.begin();
-    auto cit = j_const.cbegin();
-    EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(it.value(), json(23.42));
-    EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_EQ(cit.value(), json(23.42));
-#if 0
-    auto rit = j.rend();
-    auto crit = j.crend();
-    EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-#endif
-}
-
-class JsonIteratorNullTest : public ::testing::Test {
- public:
-    JsonIteratorNullTest() : j_const(j) {}
-
- protected:
-    json j = nullptr;
-    json j_const;
-};
-
-TEST_F(JsonIteratorNullTest, BeginEnd)
-{
-    json::iterator it = j.begin();
-    EXPECT_EQ(it, j.end());
-}
-
-TEST_F(JsonIteratorNullTest, ConstBeginEnd)
-{
-    json::const_iterator it_begin = j_const.begin();
-    json::const_iterator it_end = j_const.end();
-    EXPECT_EQ(it_begin, it_end);
-}
-
-TEST_F(JsonIteratorNullTest, CBeginEnd)
-{
-    json::const_iterator it_begin = j.cbegin();
-    json::const_iterator it_end = j.cend();
-    EXPECT_EQ(it_begin, it_end);
-}
-
-TEST_F(JsonIteratorNullTest, ConstCBeginEnd)
-{
-    json::const_iterator it_begin = j_const.cbegin();
-    json::const_iterator it_end = j_const.cend();
-    EXPECT_EQ(it_begin, it_end);
-}
-#if 0
-TEST_F(JsonIteratorNullTest, RBeginEnd)
-{
-    json::reverse_iterator it = j.rbegin();
-    EXPECT_EQ(it, j.rend());
-}
-
-TEST_F(JsonIteratorNullTest, CRBeginEnd)
-{
-    json::const_reverse_iterator it = j.crbegin();
-    EXPECT_EQ(it, j.crend());
-}
-
-TEST_F(JsonIteratorNullTest, ConstCRBeginEnd)
-{
-    json::const_reverse_iterator it = j_const.crbegin();
-    EXPECT_EQ(it, j_const.crend());
-}
-#endif
-TEST_F(JsonIteratorNullTest, KeyValue)
-{
-    auto it = j.begin();
-    auto cit = j_const.cbegin();
-    EXPECT_THROW_MSG(it.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(it.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(cit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(cit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-#if 0
-    auto rit = j.rend();
-    auto crit = j.crend();
-    EXPECT_THROW_MSG(rit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(rit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(crit.key(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
-    EXPECT_THROW_MSG(crit.value(), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-#endif
-}
-
-TEST(JsonIteratorConstConversionTest, Boolean)
-{
-    json j = true;
-    json::const_iterator it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-    it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-}
-
-TEST(JsonIteratorConstConversionTest, String)
-{
-    json j = "hello world";
-    json::const_iterator it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-    it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-}
-
-TEST(JsonIteratorConstConversionTest, Array)
-{
-    json j = {1, 2, 3};
-    json::const_iterator it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-    it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-}
-
-TEST(JsonIteratorConstConversionTest, Object)
-{
-    json j = {{"A", 1}, {"B", 2}, {"C", 3}};
-    json::const_iterator it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-    it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-}
-
-TEST(JsonIteratorConstConversionTest, Integer)
-{
-    json j = 23;
-    json::const_iterator it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-    it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-}
-
-TEST(JsonIteratorConstConversionTest, Unsigned)
-{
-    json j = 23u;
-    json::const_iterator it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-    it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-}
-
-TEST(JsonIteratorConstConversionTest, Float)
-{
-    json j = 23.42;
-    json::const_iterator it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-    it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-}
-
-TEST(JsonIteratorConstConversionTest, Null)
-{
-    json j = nullptr;
-    json::const_iterator it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-    it = j.begin();
-    EXPECT_EQ(it, j.cbegin());
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-iterators2.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-iterators2.cpp
deleted file mode 100644
index 69a4dac..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-iterators2.cpp
+++ /dev/null
@@ -1,899 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-TEST(JsonIteratorTest, Comparisons)
-{
-    json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"};
-
-    for (json& j : j_values)
-    {
-        SCOPED_TRACE(j.dump());
-        auto it1 = j.begin();
-        auto it2 = j.begin();
-        auto it3 = j.begin();
-        ++it2;
-        ++it3;
-        ++it3;
-        auto it1_c = j.cbegin();
-        auto it2_c = j.cbegin();
-        auto it3_c = j.cbegin();
-        ++it2_c;
-        ++it3_c;
-        ++it3_c;
-
-        // comparison: equal
-        {
-            EXPECT_TRUE(it1 == it1);
-            EXPECT_FALSE(it1 == it2);
-            EXPECT_FALSE(it1 == it3);
-            EXPECT_FALSE(it2 == it3);
-            EXPECT_TRUE(it1_c == it1_c);
-            EXPECT_FALSE(it1_c == it2_c);
-            EXPECT_FALSE(it1_c == it3_c);
-            EXPECT_FALSE(it2_c == it3_c);
-        }
-
-        // comparison: not equal
-        {
-            // check definition
-            EXPECT_EQ( (it1 != it1), !(it1 == it1) );
-            EXPECT_EQ( (it1 != it2), !(it1 == it2) );
-            EXPECT_EQ( (it1 != it3), !(it1 == it3) );
-            EXPECT_EQ( (it2 != it3), !(it2 == it3) );
-            EXPECT_EQ( (it1_c != it1_c), !(it1_c == it1_c) );
-            EXPECT_EQ( (it1_c != it2_c), !(it1_c == it2_c) );
-            EXPECT_EQ( (it1_c != it3_c), !(it1_c == it3_c) );
-            EXPECT_EQ( (it2_c != it3_c), !(it2_c == it3_c) );
-        }
-
-        // comparison: smaller
-        {
-            if (j.type() == json::value_t::object)
-            {
-                EXPECT_THROW_MSG(it1 < it1, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 < it2, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2 < it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 < it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c < it1_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c < it2_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2_c < it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c < it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-            }
-            else
-            {
-                EXPECT_FALSE(it1 < it1);
-                EXPECT_TRUE(it1 < it2);
-                EXPECT_TRUE(it1 < it3);
-                EXPECT_TRUE(it2 < it3);
-                EXPECT_FALSE(it1_c < it1_c);
-                EXPECT_TRUE(it1_c < it2_c);
-                EXPECT_TRUE(it1_c < it3_c);
-                EXPECT_TRUE(it2_c < it3_c);
-            }
-        }
-
-        // comparison: less than or equal
-        {
-            if (j.type() == json::value_t::object)
-            {
-                EXPECT_THROW_MSG(it1 <= it1, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 <= it2, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2 <= it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 <= it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c <= it1_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c <= it2_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2_c <= it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c <= it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-            }
-            else
-            {
-                // check definition
-                EXPECT_EQ( (it1 <= it1), !(it1 < it1) );
-                EXPECT_EQ( (it1 <= it2), !(it2 < it1) );
-                EXPECT_EQ( (it1 <= it3), !(it3 < it1) );
-                EXPECT_EQ( (it2 <= it3), !(it3 < it2) );
-                EXPECT_EQ( (it1_c <= it1_c), !(it1_c < it1_c) );
-                EXPECT_EQ( (it1_c <= it2_c), !(it2_c < it1_c) );
-                EXPECT_EQ( (it1_c <= it3_c), !(it3_c < it1_c) );
-                EXPECT_EQ( (it2_c <= it3_c), !(it3_c < it2_c) );
-            }
-        }
-
-        // comparison: greater than
-        {
-            if (j.type() == json::value_t::object)
-            {
-                EXPECT_THROW_MSG(it1 > it1, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 > it2, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2 > it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 > it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c > it1_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c > it2_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2_c > it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c > it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-            }
-            else
-            {
-                // check definition
-                EXPECT_EQ( (it1 > it1), (it1 < it1) );
-                EXPECT_EQ( (it1 > it2), (it2 < it1) );
-                EXPECT_EQ( (it1 > it3), (it3 < it1) );
-                EXPECT_EQ( (it2 > it3), (it3 < it2) );
-                EXPECT_EQ( (it1_c > it1_c), (it1_c < it1_c) );
-                EXPECT_EQ( (it1_c > it2_c), (it2_c < it1_c) );
-                EXPECT_EQ( (it1_c > it3_c), (it3_c < it1_c) );
-                EXPECT_EQ( (it2_c > it3_c), (it3_c < it2_c) );
-            }
-        }
-
-        // comparison: greater than or equal
-        {
-            if (j.type() == json::value_t::object)
-            {
-                EXPECT_THROW_MSG(it1 >= it1, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 >= it2, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2 >= it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 >= it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c >= it1_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c >= it2_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2_c >= it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c >= it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-            }
-            else
-            {
-                // check definition
-                EXPECT_EQ( (it1 >= it1), !(it1 < it1) );
-                EXPECT_EQ( (it1 >= it2), !(it1 < it2) );
-                EXPECT_EQ( (it1 >= it3), !(it1 < it3) );
-                EXPECT_EQ( (it2 >= it3), !(it2 < it3) );
-                EXPECT_EQ( (it1_c >= it1_c), !(it1_c < it1_c) );
-                EXPECT_EQ( (it1_c >= it2_c), !(it1_c < it2_c) );
-                EXPECT_EQ( (it1_c >= it3_c), !(it1_c < it3_c) );
-                EXPECT_EQ( (it2_c >= it3_c), !(it2_c < it3_c) );
-            }
-        }
-    }
-
-    // check exceptions if different objects are compared
-    for (auto j : j_values)
-    {
-        for (auto k : j_values)
-        {
-            if (j != k)
-            {
-                EXPECT_THROW_MSG(j.begin() == k.begin(), json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
-                EXPECT_THROW_MSG(j.cbegin() == k.cbegin(), json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
-
-                EXPECT_THROW_MSG(j.begin() < k.begin(), json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
-                EXPECT_THROW_MSG(j.cbegin() < k.cbegin(), json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
-            }
-        }
-    }
-}
-
-class JsonIteratorArithmeticTest : public ::testing::Test {
- protected:
-    json j_object = {{"one", 1}, {"two", 2}, {"three", 3}};
-    json j_array = {1, 2, 3, 4, 5, 6};
-    json j_null = nullptr;
-    json j_value = 42;
-};
-
-TEST_F(JsonIteratorArithmeticTest, AddSubObject)
-{
-    {
-        auto it = j_object.begin();
-        EXPECT_THROW_MSG(it += 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.cbegin();
-        EXPECT_THROW_MSG(it += 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.begin();
-        EXPECT_THROW_MSG(it + 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.cbegin();
-        EXPECT_THROW_MSG(it + 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.begin();
-        EXPECT_THROW_MSG(1 + it, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.cbegin();
-        EXPECT_THROW_MSG(1 + it, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.begin();
-        EXPECT_THROW_MSG(it -= 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.cbegin();
-        EXPECT_THROW_MSG(it -= 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.begin();
-        EXPECT_THROW_MSG(it - 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.cbegin();
-        EXPECT_THROW_MSG(it - 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.begin();
-        EXPECT_THROW_MSG(it - it, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.cbegin();
-        EXPECT_THROW_MSG(it - it, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-}
-
-TEST_F(JsonIteratorArithmeticTest, AddSubArray)
-{
-    auto it = j_array.begin();
-    it += 3;
-    EXPECT_EQ((j_array.begin() + 3), it);
-    EXPECT_EQ(json::iterator(3 + j_array.begin()), it);
-    EXPECT_EQ((it - 3), j_array.begin());
-    EXPECT_EQ((it - j_array.begin()), 3);
-    EXPECT_EQ(*it, json(4));
-    it -= 2;
-    EXPECT_EQ(*it, json(2));
-}
-
-TEST_F(JsonIteratorArithmeticTest, AddSubArrayConst)
-{
-    auto it = j_array.cbegin();
-    it += 3;
-    EXPECT_EQ((j_array.cbegin() + 3), it);
-    EXPECT_EQ(json::const_iterator(3 + j_array.cbegin()), it);
-    EXPECT_EQ((it - 3), j_array.cbegin());
-    EXPECT_EQ((it - j_array.cbegin()), 3);
-    EXPECT_EQ(*it, json(4));
-    it -= 2;
-    EXPECT_EQ(*it, json(2));
-}
-
-TEST_F(JsonIteratorArithmeticTest, AddSubNull)
-{
-    auto it = j_null.begin();
-    it += 3;
-    EXPECT_EQ((j_null.begin() + 3), it);
-    EXPECT_EQ(json::iterator(3 + j_null.begin()), it);
-    EXPECT_EQ((it - 3), j_null.begin());
-    EXPECT_EQ((it - j_null.begin()), 3);
-    EXPECT_NE(it, j_null.end());
-    it -= 3;
-    EXPECT_EQ(it, j_null.end());
-}
-
-TEST_F(JsonIteratorArithmeticTest, AddSubNullConst)
-{
-    auto it = j_null.cbegin();
-    it += 3;
-    EXPECT_EQ((j_null.cbegin() + 3), it);
-    EXPECT_EQ(json::const_iterator(3 + j_null.cbegin()), it);
-    EXPECT_EQ((it - 3), j_null.cbegin());
-    EXPECT_EQ((it - j_null.cbegin()), 3);
-    EXPECT_NE(it, j_null.cend());
-    it -= 3;
-    EXPECT_EQ(it, j_null.cend());
-}
-
-TEST_F(JsonIteratorArithmeticTest, AddSubValue)
-{
-    auto it = j_value.begin();
-    it += 3;
-    EXPECT_EQ((j_value.begin() + 3), it);
-    EXPECT_EQ(json::iterator(3 + j_value.begin()), it);
-    EXPECT_EQ((it - 3), j_value.begin());
-    EXPECT_EQ((it - j_value.begin()), 3);
-    EXPECT_NE(it, j_value.end());
-    it -= 3;
-    EXPECT_EQ(*it, json(42));
-}
-
-TEST_F(JsonIteratorArithmeticTest, AddSubValueConst)
-{
-    auto it = j_value.cbegin();
-    it += 3;
-    EXPECT_EQ((j_value.cbegin() + 3), it);
-    EXPECT_EQ(json::const_iterator(3 + j_value.cbegin()), it);
-    EXPECT_EQ((it - 3), j_value.cbegin());
-    EXPECT_EQ((it - j_value.cbegin()), 3);
-    EXPECT_NE(it, j_value.cend());
-    it -= 3;
-    EXPECT_EQ(*it, json(42));
-}
-
-TEST_F(JsonIteratorArithmeticTest, SubscriptObject)
-{
-    auto it = j_object.begin();
-    EXPECT_THROW_MSG(it[0], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators");
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators");
-}
-
-TEST_F(JsonIteratorArithmeticTest, SubscriptObjectConst)
-{
-    auto it = j_object.cbegin();
-    EXPECT_THROW_MSG(it[0], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators");
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators");
-}
-
-TEST_F(JsonIteratorArithmeticTest, SubscriptArray)
-{
-    auto it = j_array.begin();
-    EXPECT_EQ(it[0], json(1));
-    EXPECT_EQ(it[1], json(2));
-    EXPECT_EQ(it[2], json(3));
-    EXPECT_EQ(it[3], json(4));
-    EXPECT_EQ(it[4], json(5));
-    EXPECT_EQ(it[5], json(6));
-}
-
-TEST_F(JsonIteratorArithmeticTest, SubscriptArrayConst)
-{
-    auto it = j_array.cbegin();
-    EXPECT_EQ(it[0], json(1));
-    EXPECT_EQ(it[1], json(2));
-    EXPECT_EQ(it[2], json(3));
-    EXPECT_EQ(it[3], json(4));
-    EXPECT_EQ(it[4], json(5));
-    EXPECT_EQ(it[5], json(6));
-}
-
-TEST_F(JsonIteratorArithmeticTest, SubscriptNull)
-{
-    auto it = j_null.begin();
-    EXPECT_THROW_MSG(it[0], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-}
-
-TEST_F(JsonIteratorArithmeticTest, SubscriptNullConst)
-{
-    auto it = j_null.cbegin();
-    EXPECT_THROW_MSG(it[0], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-}
-
-TEST_F(JsonIteratorArithmeticTest, SubscriptValue)
-{
-    auto it = j_value.begin();
-    EXPECT_EQ(it[0], json(42));
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-}
-
-TEST_F(JsonIteratorArithmeticTest, SubscriptValueConst)
-{
-    auto it = j_value.cbegin();
-    EXPECT_EQ(it[0], json(42));
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-}
-#if 0
-TEST(JsonReverseIteratorTest, Comparisons)
-{
-    json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"};
-
-    for (json& j : j_values)
-    {
-        SCOPED_TRACE(j.dump());
-        auto it1 = j.rbegin();
-        auto it2 = j.rbegin();
-        auto it3 = j.rbegin();
-        ++it2;
-        ++it3;
-        ++it3;
-        auto it1_c = j.crbegin();
-        auto it2_c = j.crbegin();
-        auto it3_c = j.crbegin();
-        ++it2_c;
-        ++it3_c;
-        ++it3_c;
-
-        // comparison: equal
-        {
-            EXPECT_TRUE(it1 == it1);
-            EXPECT_FALSE(it1 == it2);
-            EXPECT_FALSE(it1 == it3);
-            EXPECT_FALSE(it2 == it3);
-            EXPECT_TRUE(it1_c == it1_c);
-            EXPECT_FALSE(it1_c == it2_c);
-            EXPECT_FALSE(it1_c == it3_c);
-            EXPECT_FALSE(it2_c == it3_c);
-        }
-
-        // comparison: not equal
-        {
-            // check definition
-            EXPECT_EQ( (it1 != it1), !(it1 == it1) );
-            EXPECT_EQ( (it1 != it2), !(it1 == it2) );
-            EXPECT_EQ( (it1 != it3), !(it1 == it3) );
-            EXPECT_EQ( (it2 != it3), !(it2 == it3) );
-            EXPECT_EQ( (it1_c != it1_c), !(it1_c == it1_c) );
-            EXPECT_EQ( (it1_c != it2_c), !(it1_c == it2_c) );
-            EXPECT_EQ( (it1_c != it3_c), !(it1_c == it3_c) );
-            EXPECT_EQ( (it2_c != it3_c), !(it2_c == it3_c) );
-        }
-
-        // comparison: smaller
-        {
-            if (j.type() == json::value_t::object)
-            {
-                EXPECT_THROW_MSG(it1 < it1, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 < it2, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2 < it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 < it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c < it1_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c < it2_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2_c < it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c < it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-            }
-            else
-            {
-                EXPECT_FALSE(it1 < it1);
-                EXPECT_TRUE(it1 < it2);
-                EXPECT_TRUE(it1 < it3);
-                EXPECT_TRUE(it2 < it3);
-                EXPECT_FALSE(it1_c < it1_c);
-                EXPECT_TRUE(it1_c < it2_c);
-                EXPECT_TRUE(it1_c < it3_c);
-                EXPECT_TRUE(it2_c < it3_c);
-            }
-        }
-
-        // comparison: less than or equal
-        {
-            if (j.type() == json::value_t::object)
-            {
-                EXPECT_THROW_MSG(it1 <= it1, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 <= it2, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2 <= it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 <= it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c <= it1_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c <= it2_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2_c <= it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c <= it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-            }
-            else
-            {
-                // check definition
-                EXPECT_EQ( (it1 <= it1), !(it1 < it1) );
-                EXPECT_EQ( (it1 <= it2), !(it2 < it1) );
-                EXPECT_EQ( (it1 <= it3), !(it3 < it1) );
-                EXPECT_EQ( (it2 <= it3), !(it3 < it2) );
-                EXPECT_EQ( (it1_c <= it1_c), !(it1_c < it1_c) );
-                EXPECT_EQ( (it1_c <= it2_c), !(it2_c < it1_c) );
-                EXPECT_EQ( (it1_c <= it3_c), !(it3_c < it1_c) );
-                EXPECT_EQ( (it2_c <= it3_c), !(it3_c < it2_c) );
-            }
-        }
-
-        // comparison: greater than
-        {
-            if (j.type() == json::value_t::object)
-            {
-                EXPECT_THROW_MSG(it1 > it1, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 > it2, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2 > it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 > it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c > it1_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c > it2_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2_c > it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c > it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-            }
-            else
-            {
-                // check definition
-                EXPECT_EQ( (it1 > it1), (it1 < it1) );
-                EXPECT_EQ( (it1 > it2), (it2 < it1) );
-                EXPECT_EQ( (it1 > it3), (it3 < it1) );
-                EXPECT_EQ( (it2 > it3), (it3 < it2) );
-                EXPECT_EQ( (it1_c > it1_c), (it1_c < it1_c) );
-                EXPECT_EQ( (it1_c > it2_c), (it2_c < it1_c) );
-                EXPECT_EQ( (it1_c > it3_c), (it3_c < it1_c) );
-                EXPECT_EQ( (it2_c > it3_c), (it3_c < it2_c) );
-            }
-        }
-
-        // comparison: greater than or equal
-        {
-            if (j.type() == json::value_t::object)
-            {
-                EXPECT_THROW_MSG(it1 >= it1, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 >= it2, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2 >= it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1 >= it3, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c >= it1_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c >= it2_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it2_c >= it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-                EXPECT_THROW_MSG(it1_c >= it3_c, json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
-            }
-            else
-            {
-                // check definition
-                EXPECT_EQ( (it1 >= it1), !(it1 < it1) );
-                EXPECT_EQ( (it1 >= it2), !(it1 < it2) );
-                EXPECT_EQ( (it1 >= it3), !(it1 < it3) );
-                EXPECT_EQ( (it2 >= it3), !(it2 < it3) );
-                EXPECT_EQ( (it1_c >= it1_c), !(it1_c < it1_c) );
-                EXPECT_EQ( (it1_c >= it2_c), !(it1_c < it2_c) );
-                EXPECT_EQ( (it1_c >= it3_c), !(it1_c < it3_c) );
-                EXPECT_EQ( (it2_c >= it3_c), !(it2_c < it3_c) );
-            }
-        }
-    }
-
-    // check exceptions if different objects are compared
-    for (auto j : j_values)
-    {
-        for (auto k : j_values)
-        {
-            if (j != k)
-            {
-                EXPECT_THROW_MSG(j.rbegin() == k.rbegin(), json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
-                EXPECT_THROW_MSG(j.crbegin() == k.crbegin(), json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
-
-                EXPECT_THROW_MSG(j.rbegin() < k.rbegin(), json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
-                EXPECT_THROW_MSG(j.crbegin() < k.crbegin(), json::invalid_iterator,
-                                 "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
-            }
-        }
-    }
-}
-
-class JsonReverseIteratorArithmeticTest : public ::testing::Test {
- protected:
-    json j_object = {{"one", 1}, {"two", 2}, {"three", 3}};
-    json j_array = {1, 2, 3, 4, 5, 6};
-    json j_null = nullptr;
-    json j_value = 42;
-};
-
-TEST_F(JsonReverseIteratorArithmeticTest, AddSubObject)
-{
-    {
-        auto it = j_object.rbegin();
-        EXPECT_THROW_MSG(it += 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.crbegin();
-        EXPECT_THROW_MSG(it += 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.rbegin();
-        EXPECT_THROW_MSG(it + 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.crbegin();
-        EXPECT_THROW_MSG(it + 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.rbegin();
-        EXPECT_THROW_MSG(1 + it, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.crbegin();
-        EXPECT_THROW_MSG(1 + it, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.rbegin();
-        EXPECT_THROW_MSG(it -= 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.crbegin();
-        EXPECT_THROW_MSG(it -= 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.rbegin();
-        EXPECT_THROW_MSG(it - 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.crbegin();
-        EXPECT_THROW_MSG(it - 1, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.rbegin();
-        EXPECT_THROW_MSG(it - it, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-    {
-        auto it = j_object.crbegin();
-        EXPECT_THROW_MSG(it - it, json::invalid_iterator,
-                         "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    }
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, AddSubArray)
-{
-    auto it = j_array.rbegin();
-    it += 3;
-    EXPECT_EQ((j_array.rbegin() + 3), it);
-    EXPECT_EQ(json::reverse_iterator(3 + j_array.rbegin()), it);
-    EXPECT_EQ((it - 3), j_array.rbegin());
-    EXPECT_EQ((it - j_array.rbegin()), 3);
-    EXPECT_EQ(*it, json(3));
-    it -= 2;
-    EXPECT_EQ(*it, json(5));
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, AddSubArrayConst)
-{
-    auto it = j_array.crbegin();
-    it += 3;
-    EXPECT_EQ((j_array.crbegin() + 3), it);
-    EXPECT_EQ(json::const_reverse_iterator(3 + j_array.crbegin()), it);
-    EXPECT_EQ((it - 3), j_array.crbegin());
-    EXPECT_EQ((it - j_array.crbegin()), 3);
-    EXPECT_EQ(*it, json(3));
-    it -= 2;
-    EXPECT_EQ(*it, json(5));
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, AddSubNull)
-{
-    auto it = j_null.rbegin();
-    it += 3;
-    EXPECT_EQ((j_null.rbegin() + 3), it);
-    EXPECT_EQ(json::reverse_iterator(3 + j_null.rbegin()), it);
-    EXPECT_EQ((it - 3), j_null.rbegin());
-    EXPECT_EQ((it - j_null.rbegin()), 3);
-    EXPECT_NE(it, j_null.rend());
-    it -= 3;
-    EXPECT_EQ(it, j_null.rend());
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, AddSubNullConst)
-{
-    auto it = j_null.crbegin();
-    it += 3;
-    EXPECT_EQ((j_null.crbegin() + 3), it);
-    EXPECT_EQ(json::const_reverse_iterator(3 + j_null.crbegin()), it);
-    EXPECT_EQ((it - 3), j_null.crbegin());
-    EXPECT_EQ((it - j_null.crbegin()), 3);
-    EXPECT_NE(it, j_null.crend());
-    it -= 3;
-    EXPECT_EQ(it, j_null.crend());
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, AddSubValue)
-{
-    auto it = j_value.rbegin();
-    it += 3;
-    EXPECT_EQ((j_value.rbegin() + 3), it);
-    EXPECT_EQ(json::reverse_iterator(3 + j_value.rbegin()), it);
-    EXPECT_EQ((it - 3), j_value.rbegin());
-    EXPECT_EQ((it - j_value.rbegin()), 3);
-    EXPECT_NE(it, j_value.rend());
-    it -= 3;
-    EXPECT_EQ(*it, json(42));
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, AddSubValueConst)
-{
-    auto it = j_value.crbegin();
-    it += 3;
-    EXPECT_EQ((j_value.crbegin() + 3), it);
-    EXPECT_EQ(json::const_reverse_iterator(3 + j_value.crbegin()), it);
-    EXPECT_EQ((it - 3), j_value.crbegin());
-    EXPECT_EQ((it - j_value.crbegin()), 3);
-    EXPECT_NE(it, j_value.crend());
-    it -= 3;
-    EXPECT_EQ(*it, json(42));
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, SubscriptObject)
-{
-    auto it = j_object.rbegin();
-    EXPECT_THROW_MSG(it[0], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, SubscriptObjectConst)
-{
-    auto it = j_object.crbegin();
-    EXPECT_THROW_MSG(it[0], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, SubscriptArray)
-{
-    auto it = j_array.rbegin();
-    EXPECT_EQ(it[0], json(6));
-    EXPECT_EQ(it[1], json(5));
-    EXPECT_EQ(it[2], json(4));
-    EXPECT_EQ(it[3], json(3));
-    EXPECT_EQ(it[4], json(2));
-    EXPECT_EQ(it[5], json(1));
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, SubscriptArrayConst)
-{
-    auto it = j_array.crbegin();
-    EXPECT_EQ(it[0], json(6));
-    EXPECT_EQ(it[1], json(5));
-    EXPECT_EQ(it[2], json(4));
-    EXPECT_EQ(it[3], json(3));
-    EXPECT_EQ(it[4], json(2));
-    EXPECT_EQ(it[5], json(1));
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, SubscriptNull)
-{
-    auto it = j_null.rbegin();
-    EXPECT_THROW_MSG(it[0], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, SubscriptNullConst)
-{
-    auto it = j_null.crbegin();
-    EXPECT_THROW_MSG(it[0], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, SubscriptValue)
-{
-    auto it = j_value.rbegin();
-    EXPECT_EQ(it[0], json(42));
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-}
-
-TEST_F(JsonReverseIteratorArithmeticTest, SubscriptValueConst)
-{
-    auto it = j_value.crbegin();
-    EXPECT_EQ(it[0], json(42));
-    EXPECT_THROW_MSG(it[1], json::invalid_iterator,
-                     "[json.exception.invalid_iterator.214] cannot get value");
-}
-#endif
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-json.h b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-json.h
deleted file mode 100644
index 5a764b7..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-json.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) FIRST 2017. 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.                                                               */
-/*----------------------------------------------------------------------------*/#ifndef UNIT_JSON_H_
-#define UNIT_JSON_H_
-
-#include <ostream>
-
-#include "wpi/json.h"
-
-namespace wpi {
-
-inline
-void PrintTo(const json& j, std::ostream* os) {
-  *os << j.dump();
-}
-
-class JsonTest {
- public:
-  static const json::json_value& GetValue(const json& j) {
-    return j.m_value;
-  }
-  static json::pointer GetObject(json::iterator it) {
-    return it.m_object;
-  }
-  static json::const_pointer GetObject(json::const_iterator it) {
-    return it.m_object;
-  }
-  static std::string pop_back(json::json_pointer& p) {
-    return p.pop_back();
-  }
-  static json::json_pointer top(const json::json_pointer& p) {
-    return p.top();
-  }
-};
-
-}  // namespace wpi
-
-// clang warns on TEST_THROW_MSG(x == y, ...) saying the result is unused.
-// suppress this warning.
-#if defined(__clang__)
-#pragma GCC diagnostic ignored "-Wunused-comparison"
-#endif
-
-// variant of GTEST_TEST_THROW_ that also checks the exception's message.
-#define TEST_THROW_MSG(statement, expected_exception, expected_msg, fail) \
-  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
-  if (::testing::internal::ConstCharPtr gtest_msg = "") { \
-    bool gtest_caught_expected = false; \
-    try { \
-      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
-    } \
-    catch (expected_exception const& gtest_ex) { \
-      gtest_caught_expected = true; \
-      if (::std::string(gtest_ex.what()) != expected_msg) { \
-        ::testing::AssertionResult gtest_ar = ::testing::AssertionFailure(); \
-        gtest_ar \
-            << "Expected: " #statement " throws an exception with message \"" \
-            << expected_msg "\".\n  Actual: it throws message \"" \
-            << gtest_ex.what() << "\"."; \
-        fail(gtest_ar.failure_message()); \
-      } \
-    } \
-    catch (...) { \
-      gtest_msg.value = \
-          "Expected: " #statement " throws an exception of type " \
-          #expected_exception ".\n  Actual: it throws a different type."; \
-      goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
-    } \
-    if (!gtest_caught_expected) { \
-      gtest_msg.value = \
-          "Expected: " #statement " throws an exception of type " \
-          #expected_exception ".\n  Actual: it throws nothing."; \
-      goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
-    } \
-  } else \
-    GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
-      fail(gtest_msg.value)
-
-#define EXPECT_THROW_MSG(statement, expected_exception, expected_msg) \
-  TEST_THROW_MSG(statement, expected_exception, expected_msg, GTEST_NONFATAL_FAILURE_)
-
-#define ASSERT_THROW_MSG(statement, expected_exception, expected_msg) \
-  TEST_THROW_MSG(statement, expected_exception, expected_msg, GTEST_FATAL_FAILURE_)
-
-#endif
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-json_pointer.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-json_pointer.cpp
deleted file mode 100644
index d54fd6a..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-json_pointer.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-using wpi::JsonTest;
-
-TEST(JsonPointerTest, Errors)
-{
-    EXPECT_THROW_MSG(json::json_pointer("foo"), json::parse_error,
-                     "[json.exception.parse_error.107] parse error at 1: JSON pointer must be empty or begin with '/' - was: 'foo'");
-
-    EXPECT_THROW_MSG(json::json_pointer("/~~"), json::parse_error,
-                     "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'");
-
-    EXPECT_THROW_MSG(json::json_pointer("/~"), json::parse_error,
-                     "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'");
-
-    json::json_pointer p;
-    EXPECT_THROW_MSG(JsonTest::top(p), json::out_of_range,
-                     "[json.exception.out_of_range.405] JSON pointer has no parent");
-    EXPECT_THROW_MSG(JsonTest::pop_back(p), json::out_of_range,
-                     "[json.exception.out_of_range.405] JSON pointer has no parent");
-}
-
-// examples from RFC 6901
-TEST(JsonPointerTest, AccessNonConst)
-{
-    json j = R"(
-    {
-        "foo": ["bar", "baz"],
-        "": 0,
-        "a/b": 1,
-        "c%d": 2,
-        "e^f": 3,
-        "g|h": 4,
-        "i\\j": 5,
-        "k\"l": 6,
-        " ": 7,
-        "m~n": 8
-    }
-    )"_json;
-
-    // the whole document
-    EXPECT_EQ(j[json::json_pointer()], j);
-    EXPECT_EQ(j[json::json_pointer("")], j);
-
-    // array access
-    EXPECT_EQ(j[json::json_pointer("/foo")], j["foo"]);
-    EXPECT_EQ(j[json::json_pointer("/foo/0")], j["foo"][0]);
-    EXPECT_EQ(j[json::json_pointer("/foo/1")], j["foo"][1]);
-    EXPECT_EQ(j["/foo/1"_json_pointer], j["foo"][1]);
-
-    // checked array access
-    EXPECT_EQ(j.at(json::json_pointer("/foo/0")), j["foo"][0]);
-    EXPECT_EQ(j.at(json::json_pointer("/foo/1")), j["foo"][1]);
-
-    // empty string access
-    EXPECT_EQ(j[json::json_pointer("/")], j[""]);
-
-    // other cases
-    EXPECT_EQ(j[json::json_pointer("/ ")], j[" "]);
-    EXPECT_EQ(j[json::json_pointer("/c%d")], j["c%d"]);
-    EXPECT_EQ(j[json::json_pointer("/e^f")], j["e^f"]);
-    EXPECT_EQ(j[json::json_pointer("/g|h")], j["g|h"]);
-    EXPECT_EQ(j[json::json_pointer("/i\\j")], j["i\\j"]);
-    EXPECT_EQ(j[json::json_pointer("/k\"l")], j["k\"l"]);
-
-    // checked access
-    EXPECT_EQ(j.at(json::json_pointer("/ ")), j[" "]);
-    EXPECT_EQ(j.at(json::json_pointer("/c%d")), j["c%d"]);
-    EXPECT_EQ(j.at(json::json_pointer("/e^f")), j["e^f"]);
-    EXPECT_EQ(j.at(json::json_pointer("/g|h")), j["g|h"]);
-    EXPECT_EQ(j.at(json::json_pointer("/i\\j")), j["i\\j"]);
-    EXPECT_EQ(j.at(json::json_pointer("/k\"l")), j["k\"l"]);
-
-    // escaped access
-    EXPECT_EQ(j[json::json_pointer("/a~1b")], j["a/b"]);
-    EXPECT_EQ(j[json::json_pointer("/m~0n")], j["m~n"]);
-
-    // unescaped access
-    // access to nonexisting values yield object creation
-    EXPECT_NO_THROW(j[json::json_pointer("/a/b")] = 42);
-    EXPECT_EQ(j["a"]["b"], json(42));
-    EXPECT_NO_THROW(j[json::json_pointer("/a/c/1")] = 42);
-    EXPECT_EQ(j["a"]["c"], json({nullptr, 42}));
-    EXPECT_NO_THROW(j[json::json_pointer("/a/d/-")] = 42);
-    EXPECT_EQ(j["a"]["d"], json::array({42}));
-    // "/a/b" works for JSON {"a": {"b": 42}}
-    EXPECT_EQ(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")], json(42));
-
-    // unresolved access
-    json j_primitive = 1;
-    EXPECT_THROW_MSG(j_primitive["/foo"_json_pointer], json::out_of_range,
-                     "[json.exception.out_of_range.404] unresolved reference token 'foo'");
-    EXPECT_THROW_MSG(j_primitive.at("/foo"_json_pointer), json::out_of_range,
-                     "[json.exception.out_of_range.404] unresolved reference token 'foo'");
-}
-
-TEST(JsonPointerTest, AccessConst)
-{
-    const json j = R"(
-    {
-        "foo": ["bar", "baz"],
-        "": 0,
-        "a/b": 1,
-        "c%d": 2,
-        "e^f": 3,
-        "g|h": 4,
-        "i\\j": 5,
-        "k\"l": 6,
-        " ": 7,
-        "m~n": 8
-    }
-    )"_json;
-
-    // the whole document
-    EXPECT_EQ(j[json::json_pointer()], j);
-    EXPECT_EQ(j[json::json_pointer("")], j);
-
-    // array access
-    EXPECT_EQ(j[json::json_pointer("/foo")], j["foo"]);
-    EXPECT_EQ(j[json::json_pointer("/foo/0")], j["foo"][0]);
-    EXPECT_EQ(j[json::json_pointer("/foo/1")], j["foo"][1]);
-    EXPECT_EQ(j["/foo/1"_json_pointer], j["foo"][1]);
-
-    // checked array access
-    EXPECT_EQ(j.at(json::json_pointer("/foo/0")), j["foo"][0]);
-    EXPECT_EQ(j.at(json::json_pointer("/foo/1")), j["foo"][1]);
-
-    // empty string access
-    EXPECT_EQ(j[json::json_pointer("/")], j[""]);
-
-    // other cases
-    EXPECT_EQ(j[json::json_pointer("/ ")], j[" "]);
-    EXPECT_EQ(j[json::json_pointer("/c%d")], j["c%d"]);
-    EXPECT_EQ(j[json::json_pointer("/e^f")], j["e^f"]);
-    EXPECT_EQ(j[json::json_pointer("/g|h")], j["g|h"]);
-    EXPECT_EQ(j[json::json_pointer("/i\\j")], j["i\\j"]);
-    EXPECT_EQ(j[json::json_pointer("/k\"l")], j["k\"l"]);
-
-    // checked access
-    EXPECT_EQ(j.at(json::json_pointer("/ ")), j[" "]);
-    EXPECT_EQ(j.at(json::json_pointer("/c%d")), j["c%d"]);
-    EXPECT_EQ(j.at(json::json_pointer("/e^f")), j["e^f"]);
-    EXPECT_EQ(j.at(json::json_pointer("/g|h")), j["g|h"]);
-    EXPECT_EQ(j.at(json::json_pointer("/i\\j")), j["i\\j"]);
-    EXPECT_EQ(j.at(json::json_pointer("/k\"l")), j["k\"l"]);
-
-    // escaped access
-    EXPECT_EQ(j[json::json_pointer("/a~1b")], j["a/b"]);
-    EXPECT_EQ(j[json::json_pointer("/m~0n")], j["m~n"]);
-
-    // unescaped access
-    EXPECT_THROW_MSG(j.at(json::json_pointer("/a/b")), json::out_of_range,
-                     "[json.exception.out_of_range.403] key 'a' not found");
-
-    // unresolved access
-    const json j_primitive = 1;
-    EXPECT_THROW_MSG(j_primitive["/foo"_json_pointer], json::out_of_range,
-                     "[json.exception.out_of_range.404] unresolved reference token 'foo'");
-    EXPECT_THROW_MSG(j_primitive.at("/foo"_json_pointer), json::out_of_range,
-                     "[json.exception.out_of_range.404] unresolved reference token 'foo'");
-}
-
-TEST(JsonPointerTest, UserStringLiteral)
-{
-    json j = R"(
-    {
-        "foo": ["bar", "baz"],
-        "": 0,
-        "a/b": 1,
-        "c%d": 2,
-        "e^f": 3,
-        "g|h": 4,
-        "i\\j": 5,
-        "k\"l": 6,
-        " ": 7,
-        "m~n": 8
-    }
-    )"_json;
-
-    // the whole document
-    EXPECT_EQ(j[""_json_pointer], j);
-
-    // array access
-    EXPECT_EQ(j["/foo"_json_pointer], j["foo"]);
-    EXPECT_EQ(j["/foo/0"_json_pointer], j["foo"][0]);
-    EXPECT_EQ(j["/foo/1"_json_pointer], j["foo"][1]);
-}
-
-TEST(JsonPointerTest, ArrayNonConst)
-{
-    json j = {1, 2, 3};
-    const json j_const = j;
-
-    // check reading access
-    EXPECT_EQ(j["/0"_json_pointer], j[0]);
-    EXPECT_EQ(j["/1"_json_pointer], j[1]);
-    EXPECT_EQ(j["/2"_json_pointer], j[2]);
-
-    // assign to existing index
-    j["/1"_json_pointer] = 13;
-    EXPECT_EQ(j[1], json(13));
-
-    // assign to nonexisting index
-    j["/3"_json_pointer] = 33;
-    EXPECT_EQ(j[3], json(33));
-
-    // assign to nonexisting index (with gap)
-    j["/5"_json_pointer] = 55;
-    EXPECT_EQ(j, json({1, 13, 3, 33, nullptr, 55}));
-
-    // error with leading 0
-    EXPECT_THROW_MSG(j["/01"_json_pointer], json::parse_error,
-                     "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
-    EXPECT_THROW_MSG(j_const["/01"_json_pointer], json::parse_error,
-                     "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
-    EXPECT_THROW_MSG(j.at("/01"_json_pointer), json::parse_error,
-                     "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
-    EXPECT_THROW_MSG(j_const.at("/01"_json_pointer), json::parse_error,
-                     "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
-
-    // error with incorrect numbers
-    EXPECT_THROW_MSG(j["/one"_json_pointer] = 1, json::parse_error,
-                     "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
-    EXPECT_THROW_MSG(j_const["/one"_json_pointer] == 1, json::parse_error,
-                     "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
-
-    EXPECT_THROW_MSG(j.at("/one"_json_pointer) = 1, json::parse_error,
-                     "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
-    EXPECT_THROW_MSG(j_const.at("/one"_json_pointer) == 1, json::parse_error,
-                     "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
-
-    EXPECT_THROW_MSG(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(), json::parse_error,
-                     "[json.exception.parse_error.109] parse error: array index 'three' is not a number");
-
-    // assign to "-"
-    j["/-"_json_pointer] = 99;
-    EXPECT_EQ(j, json({1, 13, 3, 33, nullptr, 55, 99}));
-
-    // error when using "-" in const object
-    EXPECT_THROW_MSG(j_const["/-"_json_pointer], json::out_of_range,
-                     "[json.exception.out_of_range.402] array index '-' (3) is out of range");
-
-    // error when using "-" with at
-    EXPECT_THROW_MSG(j.at("/-"_json_pointer), json::out_of_range,
-                     "[json.exception.out_of_range.402] array index '-' (7) is out of range");
-    EXPECT_THROW_MSG(j_const.at("/-"_json_pointer), json::out_of_range,
-                     "[json.exception.out_of_range.402] array index '-' (3) is out of range");
-}
-
-TEST(JsonPointerTest, ArrayConst)
-{
-    const json j = {1, 2, 3};
-
-    // check reading access
-    EXPECT_EQ(j["/0"_json_pointer], j[0]);
-    EXPECT_EQ(j["/1"_json_pointer], j[1]);
-    EXPECT_EQ(j["/2"_json_pointer], j[2]);
-
-    // assign to nonexisting index
-    EXPECT_THROW_MSG(j.at("/3"_json_pointer), json::out_of_range,
-                     "[json.exception.out_of_range.401] array index 3 is out of range");
-
-    // assign to nonexisting index (with gap)
-    EXPECT_THROW_MSG(j.at("/5"_json_pointer), json::out_of_range,
-                     "[json.exception.out_of_range.401] array index 5 is out of range");
-
-    // assign to "-"
-    EXPECT_THROW_MSG(j["/-"_json_pointer], json::out_of_range,
-                     "[json.exception.out_of_range.402] array index '-' (3) is out of range");
-    EXPECT_THROW_MSG(j.at("/-"_json_pointer), json::out_of_range,
-                     "[json.exception.out_of_range.402] array index '-' (3) is out of range");
-}
-
-TEST(JsonPointerTest, Flatten)
-{
-    json j =
-    {
-        {"pi", 3.141},
-        {"happy", true},
-        {"name", "Niels"},
-        {"nothing", nullptr},
-        {
-            "answer", {
-                {"everything", 42}
-            }
-        },
-        {"list", {1, 0, 2}},
-        {
-            "object", {
-                {"currency", "USD"},
-                {"value", 42.99},
-                {"", "empty string"},
-                {"/", "slash"},
-                {"~", "tilde"},
-                {"~1", "tilde1"}
-            }
-        }
-    };
-
-    json j_flatten =
-    {
-        {"/pi", 3.141},
-        {"/happy", true},
-        {"/name", "Niels"},
-        {"/nothing", nullptr},
-        {"/answer/everything", 42},
-        {"/list/0", 1},
-        {"/list/1", 0},
-        {"/list/2", 2},
-        {"/object/currency", "USD"},
-        {"/object/value", 42.99},
-        {"/object/", "empty string"},
-        {"/object/~1", "slash"},
-        {"/object/~0", "tilde"},
-        {"/object/~01", "tilde1"}
-    };
-
-    // check if flattened result is as expected
-    EXPECT_EQ(j.flatten(), j_flatten);
-
-    // check if unflattened result is as expected
-    EXPECT_EQ(j_flatten.unflatten(), j);
-
-    // error for nonobjects
-    EXPECT_THROW_MSG(json(1).unflatten(), json::type_error,
-                     "[json.exception.type_error.314] only objects can be unflattened");
-
-    // error for nonprimitve values
-    EXPECT_THROW_MSG(json({{"/1", {1, 2, 3}}}).unflatten(), json::type_error,
-                     "[json.exception.type_error.315] values in object must be primitive");
-
-    // error for conflicting values
-    json j_error = {{"", 42}, {"/foo", 17}};
-    EXPECT_THROW_MSG(j_error.unflatten(), json::type_error,
-                     "[json.exception.type_error.313] invalid value to unflatten");
-
-    // explicit roundtrip check
-    EXPECT_EQ(j.flatten().unflatten(), j);
-
-    // roundtrip for primitive values
-    json j_null;
-    EXPECT_EQ(j_null.flatten().unflatten(), j_null);
-    json j_number = 42;
-    EXPECT_EQ(j_number.flatten().unflatten(), j_number);
-    json j_boolean = false;
-    EXPECT_EQ(j_boolean.flatten().unflatten(), j_boolean);
-    json j_string = "foo";
-    EXPECT_EQ(j_string.flatten().unflatten(), j_string);
-
-    // roundtrip for empty structured values (will be unflattened to null)
-    json j_array(json::value_t::array);
-    EXPECT_EQ(j_array.flatten().unflatten(), json());
-    json j_object(json::value_t::object);
-    EXPECT_EQ(j_object.flatten().unflatten(), json());
-}
-
-TEST(JsonPointerTest, StringRepresentation)
-{
-    for (auto ptr :
-            {"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n"
-            })
-    {
-        SCOPED_TRACE(ptr);
-        EXPECT_EQ(json::json_pointer(ptr).to_string(), ptr);
-    }
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-meta.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-meta.cpp
deleted file mode 100644
index 45daf8f..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-meta.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2016 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-TEST(JsonVersionTest, Meta)
-{
-    json j = json::meta();
-
-    EXPECT_EQ(j["name"], "WPI version of JSON for Modern C++");
-    EXPECT_EQ(j["copyright"], "(C) 2013-2017 Niels Lohmann, (C) 2017-2018 FIRST");
-    EXPECT_EQ(j["url"], "https://github.com/wpilibsuite/allwpilib");
-    EXPECT_EQ(j["version"], json(
-    {
-        {"string", "3.1.2"},
-        {"major", 3},
-        {"minor", 1},
-        {"patch", 2}
-    }));
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-modifiers.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-modifiers.cpp
deleted file mode 100644
index 4125858..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-modifiers.cpp
+++ /dev/null
@@ -1,739 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-TEST(JsonClearTest, Boolean)
-{
-    json j = true;
-
-    j.clear();
-    EXPECT_EQ(j, json(json::value_t::boolean));
-}
-
-TEST(JsonClearTest, String)
-{
-    json j = "hello world";
-
-    j.clear();
-    EXPECT_EQ(j, json(json::value_t::string));
-}
-
-TEST(JsonClearTest, ArrayEmpty)
-{
-    json j = json::array();
-
-    j.clear();
-    EXPECT_TRUE(j.empty());
-    EXPECT_EQ(j, json(json::value_t::array));
-}
-
-TEST(JsonClearTest, ArrayFilled)
-{
-    json j = {1, 2, 3};
-
-    j.clear();
-    EXPECT_TRUE(j.empty());
-    EXPECT_EQ(j, json(json::value_t::array));
-}
-
-TEST(JsonClearTest, ObjectEmpty)
-{
-    json j = json::object();
-
-    j.clear();
-    EXPECT_TRUE(j.empty());
-    EXPECT_EQ(j, json(json::value_t::object));
-}
-
-TEST(JsonClearTest, ObjectFilled)
-{
-    json j = {{"one", 1}, {"two", 2}, {"three", 3}};
-
-    j.clear();
-    EXPECT_TRUE(j.empty());
-    EXPECT_EQ(j, json(json::value_t::object));
-}
-
-TEST(JsonClearTest, Integer)
-{
-    json j = 23;
-
-    j.clear();
-    EXPECT_EQ(j, json(json::value_t::number_integer));
-}
-
-TEST(JsonClearTest, Unsigned)
-{
-    json j = 23u;
-
-    j.clear();
-    EXPECT_EQ(j, json(json::value_t::number_integer));
-}
-
-TEST(JsonClearTest, Float)
-{
-    json j = 23.42;
-
-    j.clear();
-    EXPECT_EQ(j, json(json::value_t::number_float));
-}
-
-TEST(JsonClearTest, Null)
-{
-    json j = nullptr;
-
-    j.clear();
-    EXPECT_EQ(j, json(json::value_t::null));
-}
-
-TEST(JsonPushBackArrayTest, RRefNull)
-{
-    json j;
-    j.push_back(1);
-    j.push_back(2);
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({1, 2}));
-}
-
-TEST(JsonPushBackArrayTest, RRefArray)
-{
-    json j = {1, 2, 3};
-    j.push_back("Hello");
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({1, 2, 3, "Hello"}));
-}
-
-TEST(JsonPushBackArrayTest, RRefOther)
-{
-    json j = 1;
-    EXPECT_THROW_MSG(j.push_back("Hello"), json::type_error,
-                     "[json.exception.type_error.308] cannot use push_back() with number");
-}
-
-TEST(JsonPushBackArrayTest, LRefNull)
-{
-    json j;
-    json k(1);
-    j.push_back(k);
-    j.push_back(k);
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({1, 1}));
-}
-
-TEST(JsonPushBackArrayTest, LRefArray)
-{
-    json j = {1, 2, 3};
-    json k("Hello");
-    j.push_back(k);
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({1, 2, 3, "Hello"}));
-}
-
-TEST(JsonPushBackArrayTest, LRefOther)
-{
-    json j = 1;
-    json k("Hello");
-    EXPECT_THROW_MSG(j.push_back(k), json::type_error,
-                     "[json.exception.type_error.308] cannot use push_back() with number");
-}
-#if 0
-TEST(JsonPushBackObjectTest, Null)
-{
-    json j;
-    j.push_back(json::object_t::value_type({"one", 1}));
-    j.push_back(json::object_t::value_type({"two", 2}));
-    EXPECT_EQ(j.type(), json::value_t::object);
-    EXPECT_EQ(j.size(), 2u);
-    EXPECT_EQ(j["one"], json(1));
-    EXPECT_EQ(j["two"], json(2));
-}
-
-TEST(JsonPushBackObjectTest, Object)
-{
-    json j(json::value_t::object);
-    j.push_back(json::object_t::value_type({"one", 1}));
-    j.push_back(json::object_t::value_type({"two", 2}));
-    EXPECT_EQ(j.size(), 2u);
-    EXPECT_EQ(j["one"], json(1));
-    EXPECT_EQ(j["two"], json(2));
-}
-
-TEST(JsonPushBackObjectTest, Other)
-{
-    json j = 1;
-    json k("Hello");
-    EXPECT_THROW_MSG(j.push_back(json::object_t::value_type({"one", 1})), json::type_error,
-                     "[json.exception.type_error.308] cannot use push_back() with number");
-}
-#endif
-TEST(JsonPushBackInitListTest, Null)
-{
-    json j;
-    j.push_back({"foo", "bar"});
-    EXPECT_EQ(j, json::array({{"foo", "bar"}}));
-
-    json k;
-    k.push_back({1, 2, 3});
-    EXPECT_EQ(k, json::array({{1, 2, 3}}));
-}
-
-TEST(JsonPushBackInitListTest, Array)
-{
-    json j = {1, 2, 3};
-    j.push_back({"foo", "bar"});
-    EXPECT_EQ(j, json({1, 2, 3, {"foo", "bar"}}));
-
-    json k = {1, 2, 3};
-    k.push_back({1, 2, 3});
-    EXPECT_EQ(k, json({1, 2, 3, {1, 2, 3}}));
-}
-
-TEST(JsonPushBackInitListTest, Object)
-{
-    json j = {{"key1", 1}};
-    j.push_back({"key2", "bar"});
-    EXPECT_EQ(j, json({{"key1", 1}, {"key2", "bar"}}));
-
-    json k = {{"key1", 1}};
-    EXPECT_THROW_MSG(k.push_back({1, 2, 3, 4}), json::type_error,
-                     "[json.exception.type_error.308] cannot use push_back() with object");
-}
-
-TEST(JsonEmplaceBackArrayTest, Null)
-{
-    json j;
-    j.emplace_back(1);
-    j.emplace_back(2);
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({1, 2}));
-}
-
-TEST(JsonEmplaceBackArrayTest, Array)
-{
-    json j = {1, 2, 3};
-    j.emplace_back("Hello");
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({1, 2, 3, "Hello"}));
-}
-
-TEST(JsonEmplaceBackArrayTest, MultipleValues)
-{
-    json j;
-    j.emplace_back(3, "foo");
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({{"foo", "foo", "foo"}}));
-}
-
-TEST(JsonEmplaceBackArrayTest, Other)
-{
-    json j = 1;
-    EXPECT_THROW_MSG(j.emplace_back("Hello"), json::type_error,
-                     "[json.exception.type_error.311] cannot use emplace_back() with number");
-}
-
-TEST(JsonEmplaceObjectTest, Null)
-{
-    // start with a null value
-    json j;
-
-    // add a new key
-    auto res1 = j.emplace("foo", "bar");
-    EXPECT_EQ(res1.second, true);
-    EXPECT_EQ(*res1.first, "bar");
-
-    // the null value is changed to an object
-    EXPECT_EQ(j.type(), json::value_t::object);
-
-    // add a new key
-    auto res2 = j.emplace("baz", "bam");
-    EXPECT_EQ(res2.second, true);
-    EXPECT_EQ(*res2.first, "bam");
-
-    // we try to insert at given key - no change
-    auto res3 = j.emplace("baz", "bad");
-    EXPECT_EQ(res3.second, false);
-    EXPECT_EQ(*res3.first, "bam");
-
-    // the final object
-    EXPECT_EQ(j, json({{"baz", "bam"}, {"foo", "bar"}}));
-}
-
-TEST(JsonEmplaceObjectTest, Object)
-{
-    // start with an object
-    json j = {{"foo", "bar"}};
-
-    // add a new key
-    auto res1 = j.emplace("baz", "bam");
-    EXPECT_EQ(res1.second, true);
-    EXPECT_EQ(*res1.first, "bam");
-
-    // add an existing key
-    auto res2 = j.emplace("foo", "bad");
-    EXPECT_EQ(res2.second, false);
-    EXPECT_EQ(*res2.first, "bar");
-
-    // check final object
-    EXPECT_EQ(j, json({{"baz", "bam"}, {"foo", "bar"}}));
-}
-
-TEST(JsonEmplaceObjectTest, Other)
-{
-    json j = 1;
-    EXPECT_THROW_MSG(j.emplace("foo", "bar"), json::type_error,
-                     "[json.exception.type_error.311] cannot use emplace() with number");
-}
-
-TEST(JsonPlusEqualArrayTest, RRefNull)
-{
-    json j;
-    j += 1;
-    j += 2;
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({1, 2}));
-}
-
-TEST(JsonPlusEqualArrayTest, RRefArray)
-{
-    json j = {1, 2, 3};
-    j += "Hello";
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({1, 2, 3, "Hello"}));
-}
-
-TEST(JsonPlusEqualArrayTest, RRefOther)
-{
-    json j = 1;
-    EXPECT_THROW_MSG(j += "Hello", json::type_error,
-                     "[json.exception.type_error.308] cannot use push_back() with number");
-}
-
-TEST(JsonPlusEqualArrayTest, LRefNull)
-{
-    json j;
-    json k(1);
-    j += k;
-    j += k;
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({1, 1}));
-}
-
-TEST(JsonPlusEqualArrayTest, LRefArray)
-{
-    json j = {1, 2, 3};
-    json k("Hello");
-    j += k;
-    EXPECT_EQ(j.type(), json::value_t::array);
-    EXPECT_EQ(j, json({1, 2, 3, "Hello"}));
-}
-
-TEST(JsonPlusEqualArrayTest, LRefOther)
-{
-    json j = 1;
-    json k("Hello");
-    EXPECT_THROW_MSG(j += k, json::type_error,
-                     "[json.exception.type_error.308] cannot use push_back() with number");
-}
-#if 0
-TEST(JsonPlusEqualObjectTest, Null)
-{
-    json j;
-    j += json::object_t::value_type({"one", 1});
-    j += json::object_t::value_type({"two", 2});
-    EXPECT_EQ(j.type(), json::value_t::object);
-    EXPECT_EQ(j.size(), 2u);
-    EXPECT_EQ(j["one"], json(1));
-    EXPECT_EQ(j["two"], json(2));
-}
-
-TEST(JsonPlusEqualObjectTest, Object)
-{
-    json j(json::value_t::object);
-    j += json::object_t::value_type({"one", 1});
-    j += json::object_t::value_type({"two", 2});
-    EXPECT_EQ(j.size(), 2u);
-    EXPECT_EQ(j["one"], json(1));
-    EXPECT_EQ(j["two"], json(2));
-}
-
-TEST(JsonPlusEqualObjectTest, Other)
-{
-    json j = 1;
-    json k("Hello");
-    EXPECT_THROW_MSG(j += json::object_t::value_type({"one", 1}), json::type_error,
-                     "[json.exception.type_error.308] cannot use push_back() with number");
-}
-#endif
-TEST(JsonPlusEqualInitListTest, Null)
-{
-    json j;
-    j += {"foo", "bar"};
-    EXPECT_EQ(j, json::array({{"foo", "bar"}}));
-
-    json k;
-    k += {1, 2, 3};
-    EXPECT_EQ(k, json::array({{1, 2, 3}}));
-}
-
-TEST(JsonPlusEqualInitListTest, Array)
-{
-    json j = {1, 2, 3};
-    j += {"foo", "bar"};
-    EXPECT_EQ(j, json({1, 2, 3, {"foo", "bar"}}));
-
-    json k = {1, 2, 3};
-    k += {1, 2, 3};
-    EXPECT_EQ(k, json({1, 2, 3, {1, 2, 3}}));
-}
-
-TEST(JsonPlusEqualInitListTest, Object)
-{
-    json j = {{"key1", 1}};
-    j += {"key2", "bar"};
-    EXPECT_EQ(j, json({{"key1", 1}, {"key2", "bar"}}));
-
-    json k = {{"key1", 1}};
-    EXPECT_THROW_MSG((k += {1, 2, 3, 4}), json::type_error,
-                     "[json.exception.type_error.308] cannot use push_back() with object");
-}
-
-class JsonInsertTest : public ::testing::Test {
- protected:
-    json j_array = {1, 2, 3, 4};
-    json j_value = 5;
-    json j_other_array = {"first", "second"};
-    json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
-    json j_object2 = {{"eleven", "elf"}, {"seventeen", "siebzehn"}};
-};
-
-TEST_F(JsonInsertTest, ValueBegin)
-{
-    auto it = j_array.insert(j_array.begin(), j_value);
-    EXPECT_EQ(j_array.size(), 5u);
-    EXPECT_EQ(*it, j_value);
-    EXPECT_EQ(j_array.begin(), it);
-    EXPECT_EQ(j_array, json({5, 1, 2, 3, 4}));
-}
-
-TEST_F(JsonInsertTest, ValueMiddle)
-{
-    auto it = j_array.insert(j_array.begin() + 2, j_value);
-    EXPECT_EQ(j_array.size(), 5u);
-    EXPECT_EQ(*it, j_value);
-    EXPECT_EQ((it - j_array.begin()), 2);
-    EXPECT_EQ(j_array, json({1, 2, 5, 3, 4}));
-}
-
-TEST_F(JsonInsertTest, ValueEnd)
-{
-    auto it = j_array.insert(j_array.end(), j_value);
-    EXPECT_EQ(j_array.size(), 5u);
-    EXPECT_EQ(*it, j_value);
-    EXPECT_EQ((j_array.end() - it), 1);
-    EXPECT_EQ(j_array, json({1, 2, 3, 4, 5}));
-}
-
-TEST_F(JsonInsertTest, RvalueBegin)
-{
-    auto it = j_array.insert(j_array.begin(), 5);
-    EXPECT_EQ(j_array.size(), 5u);
-    EXPECT_EQ(*it, j_value);
-    EXPECT_EQ(j_array.begin(), it);
-    EXPECT_EQ(j_array, json({5, 1, 2, 3, 4}));
-}
-
-TEST_F(JsonInsertTest, RvalueMiddle)
-{
-    auto it = j_array.insert(j_array.begin() + 2, 5);
-    EXPECT_EQ(j_array.size(), 5u);
-    EXPECT_EQ(*it, j_value);
-    EXPECT_EQ((it - j_array.begin()), 2);
-    EXPECT_EQ(j_array, json({1, 2, 5, 3, 4}));
-}
-
-TEST_F(JsonInsertTest, RvalueEnd)
-{
-    auto it = j_array.insert(j_array.end(), 5);
-    EXPECT_EQ(j_array.size(), 5u);
-    EXPECT_EQ(*it, j_value);
-    EXPECT_EQ((j_array.end() - it), 1);
-    EXPECT_EQ(j_array, json({1, 2, 3, 4, 5}));
-}
-
-TEST_F(JsonInsertTest, CopyBegin)
-{
-    auto it = j_array.insert(j_array.begin(), 3, 5);
-    EXPECT_EQ(j_array.size(), 7u);
-    EXPECT_EQ(*it, j_value);
-    EXPECT_EQ(j_array.begin(), it);
-    EXPECT_EQ(j_array, json({5, 5, 5, 1, 2, 3, 4}));
-}
-
-TEST_F(JsonInsertTest, CopyMiddle)
-{
-    auto it = j_array.insert(j_array.begin() + 2, 3, 5);
-    EXPECT_EQ(j_array.size(), 7u);
-    EXPECT_EQ(*it, j_value);
-    EXPECT_EQ((it - j_array.begin()), 2);
-    EXPECT_EQ(j_array, json({1, 2, 5, 5, 5, 3, 4}));
-}
-
-TEST_F(JsonInsertTest, CopyEnd)
-{
-    auto it = j_array.insert(j_array.end(), 3, 5);
-    EXPECT_EQ(j_array.size(), 7u);
-    EXPECT_EQ(*it, j_value);
-    EXPECT_EQ((j_array.end() - it), 3);
-    EXPECT_EQ(j_array, json({1, 2, 3, 4, 5, 5, 5}));
-}
-
-TEST_F(JsonInsertTest, CopyNothing)
-{
-    auto it = j_array.insert(j_array.end(), 0, 5);
-    EXPECT_EQ(j_array.size(), 4u);
-    // the returned iterator points to the first inserted element;
-    // there were 4 elements, so it should point to the 5th
-    EXPECT_EQ(it, j_array.begin() + 4);
-    EXPECT_EQ(j_array, json({1, 2, 3, 4}));
-}
-
-TEST_F(JsonInsertTest, RangeForArrayProper)
-{
-    auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end());
-    EXPECT_EQ(j_array.size(), 6u);
-    EXPECT_EQ(*it, *j_other_array.begin());
-    EXPECT_EQ((j_array.end() - it), 2);
-    EXPECT_EQ(j_array, json({1, 2, 3, 4, "first", "second"}));
-}
-
-TEST_F(JsonInsertTest, RangeForArrayEmpty)
-{
-    auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin());
-    EXPECT_EQ(j_array.size(), 4u);
-    EXPECT_EQ(it, j_array.end());
-    EXPECT_EQ(j_array, json({1, 2, 3, 4}));
-}
-
-TEST_F(JsonInsertTest, RangeForArrayInvalid)
-{
-    json j_other_array2 = {"first", "second"};
-
-    EXPECT_THROW_MSG(j_array.insert(j_array.end(), j_array.begin(), j_array.end()),
-                    json::invalid_iterator,
-                    "[json.exception.invalid_iterator.211] passed iterators may not belong to container");
-    EXPECT_THROW_MSG(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()),
-                    json::invalid_iterator,
-                    "[json.exception.invalid_iterator.210] iterators do not fit");
-}
-
-TEST_F(JsonInsertTest, RangeForObjectProper)
-{
-    j_object1.insert(j_object2.begin(), j_object2.end());
-    EXPECT_EQ(j_object1.size(), 4u);
-}
-
-TEST_F(JsonInsertTest, RangeForObjectEmpty)
-{
-    j_object1.insert(j_object2.begin(), j_object2.begin());
-    EXPECT_EQ(j_object1.size(), 2u);
-}
-
-TEST_F(JsonInsertTest, RangeForObjectInvalid)
-{
-    json j_other_array2 = {"first", "second"};
-
-    EXPECT_THROW_MSG(j_array.insert(j_object2.begin(), j_object2.end()), json::type_error,
-                     "[json.exception.type_error.309] cannot use insert() with array");
-    EXPECT_THROW_MSG(j_object1.insert(j_object1.begin(), j_object2.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.210] iterators do not fit");
-    EXPECT_THROW_MSG(j_object1.insert(j_array.begin(), j_array.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.202] iterators first and last must point to objects");
-}
-
-TEST_F(JsonInsertTest, InitListBegin)
-{
-    auto it = j_array.insert(j_array.begin(), {7, 8, 9});
-    EXPECT_EQ(j_array.size(), 7u);
-    EXPECT_EQ(*it, json(7));
-    EXPECT_EQ(j_array.begin(), it);
-    EXPECT_EQ(j_array, json({7, 8, 9, 1, 2, 3, 4}));
-}
-
-TEST_F(JsonInsertTest, InitListMiddle)
-{
-    auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9});
-    EXPECT_EQ(j_array.size(), 7u);
-    EXPECT_EQ(*it, json(7));
-    EXPECT_EQ((it - j_array.begin()), 2);
-    EXPECT_EQ(j_array, json({1, 2, 7, 8, 9, 3, 4}));
-}
-
-TEST_F(JsonInsertTest, InitListEnd)
-{
-    auto it = j_array.insert(j_array.end(), {7, 8, 9});
-    EXPECT_EQ(j_array.size(), 7u);
-    EXPECT_EQ(*it, json(7));
-    EXPECT_EQ((j_array.end() - it), 3);
-    EXPECT_EQ(j_array, json({1, 2, 3, 4, 7, 8, 9}));
-}
-
-TEST_F(JsonInsertTest, InvalidIterator)
-{
-    // pass iterator to a different array
-    json j_another_array = {1, 2};
-    json j_yet_another_array = {"first", "second"};
-    EXPECT_THROW_MSG(j_array.insert(j_another_array.end(), 10), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.202] iterator does not fit current value");
-    EXPECT_THROW_MSG(j_array.insert(j_another_array.end(), j_value), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.202] iterator does not fit current value");
-    EXPECT_THROW_MSG(j_array.insert(j_another_array.end(), 10, 11), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.202] iterator does not fit current value");
-    EXPECT_THROW_MSG(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.202] iterator does not fit current value");
-    EXPECT_THROW_MSG(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), json::invalid_iterator,
-                     "[json.exception.invalid_iterator.202] iterator does not fit current value");
-}
-
-TEST_F(JsonInsertTest, NonArray)
-{
-    // call insert on a non-array type
-    json j_nonarray = 3;
-    json j_yet_another_array = {"first", "second"};
-    EXPECT_THROW_MSG(j_nonarray.insert(j_nonarray.end(), 10), json::type_error,
-                     "[json.exception.type_error.309] cannot use insert() with number");
-    EXPECT_THROW_MSG(j_nonarray.insert(j_nonarray.end(), j_value), json::type_error,
-                     "[json.exception.type_error.309] cannot use insert() with number");
-    EXPECT_THROW_MSG(j_nonarray.insert(j_nonarray.end(), 10, 11), json::type_error,
-                     "[json.exception.type_error.309] cannot use insert() with number");
-    EXPECT_THROW_MSG(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(),
-                                       j_yet_another_array.end()), json::type_error,
-                     "[json.exception.type_error.309] cannot use insert() with number");
-    EXPECT_THROW_MSG(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), json::type_error,
-                     "[json.exception.type_error.309] cannot use insert() with number");
-}
-
-TEST(JsonSwapTest, JsonMember)
-{
-    json j("hello world");
-    json k(42.23);
-
-    j.swap(k);
-
-    EXPECT_EQ(j, json(42.23));
-    EXPECT_EQ(k, json("hello world"));
-}
-
-TEST(JsonSwapTest, JsonNonMember)
-{
-    json j("hello world");
-    json k(42.23);
-
-    std::swap(j, k);
-
-    EXPECT_EQ(j, json(42.23));
-    EXPECT_EQ(k, json("hello world"));
-}
-
-TEST(JsonSwapTest, ArrayT)
-{
-    json j = {1, 2, 3, 4};
-    json::array_t a = {"foo", "bar", "baz"};
-
-    j.swap(a);
-
-    EXPECT_EQ(j, json({"foo", "bar", "baz"}));
-
-    j.swap(a);
-
-    EXPECT_EQ(j, json({1, 2, 3, 4}));
-}
-
-TEST(JsonSwapTest, NonArrayT)
-{
-    json j = 17;
-    json::array_t a = {"foo", "bar", "baz"};
-
-    EXPECT_THROW_MSG(j.swap(a), json::type_error,
-                     "[json.exception.type_error.310] cannot use swap() with number");
-}
-
-TEST(JsonSwapTest, ObjectT)
-{
-    json j = {{"one", 1}, {"two", 2}};
-    json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};
-
-    j.swap(o);
-
-    EXPECT_EQ(j, json({{"cow", "Kuh"}, {"chicken", "Huhn"}}));
-
-    j.swap(o);
-
-    EXPECT_EQ(j, json({{"one", 1}, {"two", 2}}));
-}
-
-TEST(JsonSwapTest, NonObjectT)
-{
-    json j = 17;
-    json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};
-
-    EXPECT_THROW_MSG(j.swap(o), json::type_error,
-                     "[json.exception.type_error.310] cannot use swap() with number");
-}
-
-TEST(JsonSwapTest, StringT)
-{
-    json j = "Hello world";
-    std::string s = "Hallo Welt";
-
-    j.swap(s);
-
-    EXPECT_EQ(j, json("Hallo Welt"));
-
-    j.swap(s);
-
-    EXPECT_EQ(j, json("Hello world"));
-}
-
-TEST(JsonSwapTest, NonStringT)
-{
-    json j = 17;
-    std::string s = "Hallo Welt";
-
-    EXPECT_THROW_MSG(j.swap(s), json::type_error,
-                     "[json.exception.type_error.310] cannot use swap() with number");
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp
deleted file mode 100644
index 73665ea..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp
+++ /dev/null
@@ -1,1270 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-#include <cmath>
-#include <fstream>
-
-TEST(MessagePackDiscardedTest, Case)
-{
-    // discarded values are not serialized
-    json j = json::value_t::discarded;
-    const auto result = json::to_msgpack(j);
-    EXPECT_TRUE(result.empty());
-}
-
-TEST(MessagePackNullTest, Case)
-{
-    json j = nullptr;
-    std::vector<uint8_t> expected = {0xc0};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-TEST(MessagePackBooleanTest, True)
-{
-    json j = true;
-    std::vector<uint8_t> expected = {0xc3};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-TEST(MessagePackBooleanTest, False)
-{
-    json j = false;
-    std::vector<uint8_t> expected = {0xc2};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// -32..-1 (negative fixnum)
-TEST(MessagePackSignedTest, Neg0)
-{
-    for (auto i = -32; i <= -1; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = i;
-
-        // check type
-        EXPECT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(i));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), 1u);
-
-        // check individual bytes
-        EXPECT_EQ(static_cast<int8_t>(result[0]), i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// 0..127 (positive fixnum)
-TEST(MessagePackSignedTest, Pos0)
-{
-    for (size_t i = 0; i <= 127; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = -1;
-        j.get_ref<int64_t&>() = static_cast<int64_t>(i);
-
-        // check type
-        EXPECT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(i));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), 1u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], static_cast<uint8_t>(i));
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// 128..255 (int 8)
-TEST(MessagePackSignedTest, Pos1)
-{
-    for (size_t i = 128; i <= 255; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = -1;
-        j.get_ref<int64_t&>() = static_cast<int64_t>(i);
-
-        // check type
-        EXPECT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0xcc));
-        expected.push_back(static_cast<uint8_t>(i));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), 2u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], static_cast<uint8_t>(0xcc));
-        uint8_t restored = static_cast<uint8_t>(result[1]);
-        EXPECT_EQ(restored, i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// 256..65535 (int 16)
-TEST(MessagePackSignedTest, Pos2)
-{
-    for (size_t i = 256; i <= 65535; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = -1;
-        j.get_ref<int64_t&>() = static_cast<int64_t>(i);
-
-        // check type
-        EXPECT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0xcd));
-        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
-        expected.push_back(static_cast<uint8_t>(i & 0xff));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), 3u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], static_cast<uint8_t>(0xcd));
-        uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
-        EXPECT_EQ(restored, i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// 65536..4294967295 (int 32)
-class MessagePackSignedPos4Test : public ::testing::TestWithParam<uint32_t> {};
-TEST_P(MessagePackSignedPos4Test, Case)
-{
-    // create JSON value with integer number
-    json j = -1;
-    j.get_ref<int64_t&>() = static_cast<int64_t>(GetParam());
-
-    // check type
-    EXPECT_TRUE(j.is_number_integer());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(static_cast<uint8_t>(0xce));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-
-    // compare result + size
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-    EXPECT_EQ(result.size(), 5u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xce));
-    uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
-                        static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
-    EXPECT_EQ(restored, GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-static const uint32_t pos4_numbers[] = {
-    65536u,
-    77777u,
-    1048576u,
-    4294967295u,
-};
-
-INSTANTIATE_TEST_SUITE_P(MessagePackSignedPos4Tests, MessagePackSignedPos4Test,
-                        ::testing::ValuesIn(pos4_numbers));
-
-// 4294967296..9223372036854775807 (int 64)
-class MessagePackSignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
-TEST_P(MessagePackSignedPos8Test, Case)
-{
-    // create JSON value with integer number
-    json j = -1;
-    j.get_ref<int64_t&>() =
-        static_cast<int64_t>(GetParam());
-
-    // check type
-    EXPECT_TRUE(j.is_number_integer());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(static_cast<uint8_t>(0xcf));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 070) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 060) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 050) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 040) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 030) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 020) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 010) & 0xff));
-    expected.push_back(static_cast<char>(GetParam() & 0xff));
-
-    // compare result + size
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-    EXPECT_EQ(result.size(), 9u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xcf));
-    uint64_t restored = (static_cast<uint64_t>(static_cast<uint8_t>(result[1])) << 070) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[2])) << 060) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[3])) << 050) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[4])) << 040) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[5])) << 030) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[6])) << 020) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[7])) << 010) +
-                        static_cast<uint64_t>(static_cast<uint8_t>(result[8]));
-    EXPECT_EQ(restored, GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-static const uint64_t pos8_numbers[] = {
-    4294967296lu,
-    9223372036854775807lu,
-};
-
-INSTANTIATE_TEST_SUITE_P(MessagePackSignedPos8Tests, MessagePackSignedPos8Test,
-                        ::testing::ValuesIn(pos8_numbers));
-
-// -128..-33 (int 8)
-TEST(MessagePackSignedTest, Neg1)
-{
-    for (auto i = -128; i <= -33; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = i;
-
-        // check type
-        EXPECT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0xd0));
-        expected.push_back(static_cast<uint8_t>(i));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), 2u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], static_cast<uint8_t>(0xd0));
-        EXPECT_EQ(static_cast<int8_t>(result[1]), i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// -32768..-129 (int 16)
-TEST(MessagePackSignedTest, Neg2)
-{
-    for (int16_t i = -32768; i <= -129; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with integer number
-        json j = i;
-
-        // check type
-        EXPECT_TRUE(j.is_number_integer());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0xd1));
-        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
-        expected.push_back(static_cast<uint8_t>(i & 0xff));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), 3u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], static_cast<uint8_t>(0xd1));
-        int16_t restored = static_cast<int16_t>((static_cast<uint8_t>(result[1]) << 8) +
-                                                static_cast<uint8_t>(result[2]));
-        EXPECT_EQ(restored, i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// -32769..-2147483648
-class MessagePackSignedNeg4Test : public ::testing::TestWithParam<int32_t> {};
-TEST_P(MessagePackSignedNeg4Test, Case)
-{
-    // create JSON value with integer number
-    json j = GetParam();
-
-    // check type
-    EXPECT_TRUE(j.is_number_integer());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(static_cast<uint8_t>(0xd2));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-
-    // compare result + size
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-    EXPECT_EQ(result.size(), 5u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xd2));
-    uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
-                        static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
-    EXPECT_EQ(static_cast<int32_t>(restored), GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-static const int32_t neg4_numbers[] = {
-    -32769,
-    -65536,
-    -77777,
-    -1048576,
-    -2147483648ll,
-};
-
-INSTANTIATE_TEST_SUITE_P(MessagePackSignedNeg4Tests, MessagePackSignedNeg4Test,
-                        ::testing::ValuesIn(neg4_numbers));
-
-// -9223372036854775808..-2147483649 (int 64)
-class MessagePackSignedNeg8Test : public ::testing::TestWithParam<int64_t> {};
-TEST_P(MessagePackSignedNeg8Test, Case)
-{
-    // create JSON value with unsigned integer number
-    json j = GetParam();
-
-    // check type
-    EXPECT_TRUE(j.is_number_integer());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(static_cast<uint8_t>(0xd3));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 070) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 060) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 050) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 040) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 030) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 020) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 010) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-
-    // compare result + size
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-    EXPECT_EQ(result.size(), 9u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xd3));
-    int64_t restored = (static_cast<int64_t>(static_cast<uint8_t>(result[1])) << 070) +
-                       (static_cast<int64_t>(static_cast<uint8_t>(result[2])) << 060) +
-                       (static_cast<int64_t>(static_cast<uint8_t>(result[3])) << 050) +
-                       (static_cast<int64_t>(static_cast<uint8_t>(result[4])) << 040) +
-                       (static_cast<int64_t>(static_cast<uint8_t>(result[5])) << 030) +
-                       (static_cast<int64_t>(static_cast<uint8_t>(result[6])) << 020) +
-                       (static_cast<int64_t>(static_cast<uint8_t>(result[7])) << 010) +
-                       static_cast<int64_t>(static_cast<uint8_t>(result[8]));
-    EXPECT_EQ(restored, GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-static const int64_t neg8_numbers[] = {
-    INT64_MIN,
-    -2147483649ll,
-};
-
-INSTANTIATE_TEST_SUITE_P(MessagePackSignedNeg8Tests, MessagePackSignedNeg8Test,
-                        ::testing::ValuesIn(neg8_numbers));
-
-// 0..127 (positive fixnum)
-TEST(MessagePackUnsignedTest, Pos0)
-{
-    for (size_t i = 0; i <= 127; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with unsigned integer number
-        json j = i;
-
-        // check type
-        EXPECT_TRUE(j.is_number_unsigned());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(i));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), 1u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], static_cast<uint8_t>(i));
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// 128..255 (uint 8)
-TEST(MessagePackUnsignedTest, Pos1)
-{
-    for (size_t i = 128; i <= 255; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with unsigned integer number
-        json j = i;
-
-        // check type
-        EXPECT_TRUE(j.is_number_unsigned());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0xcc));
-        expected.push_back(static_cast<uint8_t>(i));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), 2u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], static_cast<uint8_t>(0xcc));
-        uint8_t restored = static_cast<uint8_t>(result[1]);
-        EXPECT_EQ(restored, i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// 256..65535 (uint 16)
-TEST(MessagePackUnsignedTest, Pos2)
-{
-    for (size_t i = 256; i <= 65535; ++i)
-    {
-        SCOPED_TRACE(i);
-
-        // create JSON value with unsigned integer number
-        json j = i;
-
-        // check type
-        EXPECT_TRUE(j.is_number_unsigned());
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0xcd));
-        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
-        expected.push_back(static_cast<uint8_t>(i & 0xff));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), 3u);
-
-        // check individual bytes
-        EXPECT_EQ(result[0], static_cast<uint8_t>(0xcd));
-        uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
-        EXPECT_EQ(restored, i);
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// 65536..4294967295 (uint 32)
-class MessagePackUnsignedPos4Test : public ::testing::TestWithParam<uint32_t> {};
-TEST_P(MessagePackUnsignedPos4Test, Case)
-{
-    // create JSON value with unsigned integer number
-    json j = GetParam();
-
-    // check type
-    EXPECT_TRUE(j.is_number_unsigned());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(static_cast<uint8_t>(0xce));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-
-    // compare result + size
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-    EXPECT_EQ(result.size(), 5u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xce));
-    uint32_t restored = (static_cast<uint32_t>(static_cast<uint8_t>(result[1])) << 030) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[2])) << 020) +
-                        (static_cast<uint32_t>(static_cast<uint8_t>(result[3])) << 010) +
-                        static_cast<uint32_t>(static_cast<uint8_t>(result[4]));
-    EXPECT_EQ(restored, GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-INSTANTIATE_TEST_SUITE_P(MessagePackUnsignedPos4Tests,
-                        MessagePackUnsignedPos4Test,
-                        ::testing::ValuesIn(pos4_numbers));
-
-// 4294967296..18446744073709551615 (uint 64)
-class MessagePackUnsignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
-TEST_P(MessagePackUnsignedPos8Test, Case)
-{
-    // create JSON value with unsigned integer number
-    json j = GetParam();
-
-    // check type
-    EXPECT_TRUE(j.is_number_unsigned());
-
-    // create expected byte vector
-    std::vector<uint8_t> expected;
-    expected.push_back(static_cast<uint8_t>(0xcf));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 070) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 060) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 050) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 040) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 030) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 020) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 010) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-
-    // compare result + size
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-    EXPECT_EQ(result.size(), 9u);
-
-    // check individual bytes
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xcf));
-    uint64_t restored = (static_cast<uint64_t>(static_cast<uint8_t>(result[1])) << 070) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[2])) << 060) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[3])) << 050) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[4])) << 040) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[5])) << 030) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[6])) << 020) +
-                        (static_cast<uint64_t>(static_cast<uint8_t>(result[7])) << 010) +
-                        static_cast<uint64_t>(static_cast<uint8_t>(result[8]));
-    EXPECT_EQ(restored, GetParam());
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-INSTANTIATE_TEST_SUITE_P(MessagePackUnsignedPos8Tests,
-                        MessagePackUnsignedPos8Test,
-                        ::testing::ValuesIn(pos8_numbers));
-
-// 3.1415925
-TEST(MessagePackFloatTest, Number)
-{
-    double v = 3.1415925;
-    json j = v;
-    std::vector<uint8_t> expected = {0xcb,0x40,0x09,0x21,0xfb,0x3f,0xa6,0xde,0xfc};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-    EXPECT_EQ(json::from_msgpack(result), v);
-}
-
-// N = 0..31
-TEST(MessagePackStringTest, String1)
-{
-    // explicitly enumerate the first byte for all 32 strings
-    const std::vector<uint8_t> first_bytes =
-    {
-        0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,
-        0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1,
-        0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
-        0xbb, 0xbc, 0xbd, 0xbe, 0xbf
-    };
-
-    for (size_t N = 0; N < first_bytes.size(); ++N)
-    {
-        SCOPED_TRACE(N);
-
-        // create JSON value with string containing of N * 'x'
-        const auto s = std::string(N, 'x');
-        json j = s;
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(first_bytes[N]));
-        for (size_t i = 0; i < N; ++i)
-        {
-            expected.push_back('x');
-        }
-
-        // check first byte
-        EXPECT_EQ((first_bytes[N] & 0x1f), static_cast<uint8_t>(N));
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), N + 1);
-        // check that no null byte is appended
-        if (N > 0)
-        {
-            EXPECT_NE(result.back(), '\x00');
-        }
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// N = 32..255
-TEST(MessagePackStringTest, String2)
-{
-    for (size_t N = 32; N <= 255; ++N)
-    {
-        SCOPED_TRACE(N);
-
-        // create JSON value with string containing of N * 'x'
-        const auto s = std::string(N, 'x');
-        json j = s;
-
-        // create expected byte vector
-        std::vector<uint8_t> expected;
-        expected.push_back(static_cast<uint8_t>(0xd9));
-        expected.push_back(static_cast<uint8_t>(N));
-        for (size_t i = 0; i < N; ++i)
-        {
-            expected.push_back('x');
-        }
-
-        // compare result + size
-        const auto result = json::to_msgpack(j);
-        EXPECT_EQ(result, expected);
-        EXPECT_EQ(result.size(), N + 2);
-        // check that no null byte is appended
-        EXPECT_NE(result.back(), '\x00');
-
-        // roundtrip
-        EXPECT_EQ(json::from_msgpack(result), j);
-    }
-}
-
-// N = 256..65535
-class MessagePackString3Test : public ::testing::TestWithParam<size_t> {};
-TEST_P(MessagePackString3Test, Case)
-{
-    // create JSON value with string containing of N * 'x'
-    const auto s = std::string(GetParam(), 'x');
-    json j = s;
-
-    // create expected byte vector (hack: create string first)
-    std::vector<uint8_t> expected;
-    expected.push_back(static_cast<uint8_t>(0xda));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-    for (size_t i = 0; i < GetParam(); ++i)
-    {
-        expected.push_back('x');
-    }
-
-    // compare result + size
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-    EXPECT_EQ(result.size(), GetParam() + 3);
-    // check that no null byte is appended
-    EXPECT_NE(result.back(), '\x00');
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-static size_t string3_lens[] = {
-    256u,
-    999u,
-    1025u,
-    3333u,
-    2048u,
-    65535u
-};
-
-INSTANTIATE_TEST_SUITE_P(MessagePackString3Tests, MessagePackString3Test,
-                        ::testing::ValuesIn(string3_lens));
-
-
-// N = 65536..4294967295
-class MessagePackString5Test : public ::testing::TestWithParam<size_t> {};
-TEST_P(MessagePackString5Test, Case)
-{
-    // create JSON value with string containing of N * 'x'
-    const auto s = std::string(GetParam(), 'x');
-    json j = s;
-
-    // create expected byte vector (hack: create string first)
-    std::vector<uint8_t> expected;
-    expected.push_back(static_cast<uint8_t>(0xdb));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 24) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 16) & 0xff));
-    expected.push_back(static_cast<uint8_t>((GetParam() >> 8) & 0xff));
-    expected.push_back(static_cast<uint8_t>(GetParam() & 0xff));
-    for (size_t i = 0; i < GetParam(); ++i)
-    {
-        expected.push_back('x');
-    }
-
-    // compare result + size
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-    EXPECT_EQ(result.size(), GetParam() + 5);
-    // check that no null byte is appended
-    EXPECT_NE(result.back(), '\x00');
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-static size_t string5_lens[] = {
-    65536u,
-    77777u,
-    1048576u
-};
-
-INSTANTIATE_TEST_SUITE_P(MessagePackString5Tests, MessagePackString5Test,
-                        ::testing::ValuesIn(string5_lens));
-
-TEST(MessagePackArrayTest, Empty)
-{
-    json j = json::array();
-    std::vector<uint8_t> expected = {0x90};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// [null]
-TEST(MessagePackArrayTest, Null)
-{
-    json j = {nullptr};
-    std::vector<uint8_t> expected = {0x91,0xc0};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// [1,2,3,4,5]
-TEST(MessagePackArrayTest, Simple)
-{
-    json j = json::parse("[1,2,3,4,5]");
-    std::vector<uint8_t> expected = {0x95,0x01,0x02,0x03,0x04,0x05};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// [[[[]]]]
-TEST(MessagePackArrayTest, NestEmpty)
-{
-    json j = json::parse("[[[[]]]]");
-    std::vector<uint8_t> expected = {0x91,0x91,0x91,0x90};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// array 16
-TEST(MessagePackArrayTest, UInt16)
-{
-    json j(16, nullptr);
-    std::vector<uint8_t> expected(j.size() + 3, static_cast<uint8_t>(0xc0)); // all null
-    expected[0] = static_cast<uint8_t>(0xdc); // array 16
-    expected[1] = 0x00; // size (0x0010), byte 0
-    expected[2] = 0x10; // size (0x0010), byte 1
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// array 32
-TEST(MessagePackArrayTest, UInt32)
-{
-    json j(65536, nullptr);
-    std::vector<uint8_t> expected(j.size() + 5, static_cast<uint8_t>(0xc0)); // all null
-    expected[0] = static_cast<uint8_t>(0xdd); // array 32
-    expected[1] = 0x00; // size (0x00100000), byte 0
-    expected[2] = 0x01; // size (0x00100000), byte 1
-    expected[3] = 0x00; // size (0x00100000), byte 2
-    expected[4] = 0x00; // size (0x00100000), byte 3
-    const auto result = json::to_msgpack(j);
-    //EXPECT_EQ(result, expected);
-
-    EXPECT_EQ(result.size(), expected.size());
-    for (size_t i = 0; i < expected.size(); ++i)
-    {
-        SCOPED_TRACE(i);
-        EXPECT_EQ(result[i], expected[i]);
-    }
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-TEST(MessagePackObjectTest, Empty)
-{
-    json j = json::object();
-    std::vector<uint8_t> expected = {0x80};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// {"":null}
-TEST(MessagePackObjectTest, EmptyKey)
-{
-    json j = {{"", nullptr}};
-    std::vector<uint8_t> expected = {0x81,0xa0,0xc0};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// {"a": {"b": {"c": {}}}}
-TEST(MessagePackObjectTest, NestedEmpty)
-{
-    json j = json::parse("{\"a\": {\"b\": {\"c\": {}}}}");
-    std::vector<uint8_t> expected = {0x81,0xa1,0x61,0x81,0xa1,0x62,0x81,0xa1,0x63,0x80};
-    const auto result = json::to_msgpack(j);
-    EXPECT_EQ(result, expected);
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// map 16
-TEST(MessagePackObjectTest, UInt16)
-{
-    json j = R"({"00": null, "01": null, "02": null, "03": null,
-                 "04": null, "05": null, "06": null, "07": null,
-                 "08": null, "09": null, "10": null, "11": null,
-                 "12": null, "13": null, "14": null, "15": null})"_json;
-
-    const auto result = json::to_msgpack(j);
-
-    // Checking against an expected vector byte by byte is
-    // difficult, because no assumption on the order of key/value
-    // pairs are made. We therefore only check the prefix (type and
-    // size and the overall size. The rest is then handled in the
-    // roundtrip check.
-    EXPECT_EQ(result.size(), 67u); // 1 type, 2 size, 16*4 content
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xde)); // map 16
-    EXPECT_EQ(result[1], 0x00); // byte 0 of size (0x0010)
-    EXPECT_EQ(result[2], 0x10); // byte 1 of size (0x0010)
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// map 32
-TEST(MessagePackObjectTest, UInt32)
-{
-    json j;
-    for (auto i = 0; i < 65536; ++i)
-    {
-        // format i to a fixed width of 5
-        // each entry will need 7 bytes: 6 for fixstr, 1 for null
-        std::stringstream ss;
-        ss << std::setw(5) << std::setfill('0') << i;
-        j.emplace(ss.str(), nullptr);
-    }
-
-    const auto result = json::to_msgpack(j);
-
-    // Checking against an expected vector byte by byte is
-    // difficult, because no assumption on the order of key/value
-    // pairs are made. We therefore only check the prefix (type and
-    // size and the overall size. The rest is then handled in the
-    // roundtrip check.
-    EXPECT_EQ(result.size(), 458757u); // 1 type, 4 size, 65536*7 content
-    EXPECT_EQ(result[0], static_cast<uint8_t>(0xdf)); // map 32
-    EXPECT_EQ(result[1], 0x00); // byte 0 of size (0x00010000)
-    EXPECT_EQ(result[2], 0x01); // byte 1 of size (0x00010000)
-    EXPECT_EQ(result[3], 0x00); // byte 2 of size (0x00010000)
-    EXPECT_EQ(result[4], 0x00); // byte 3 of size (0x00010000)
-
-    // roundtrip
-    EXPECT_EQ(json::from_msgpack(result), j);
-}
-
-// from float32
-TEST(MessagePackFloat32Test, Case)
-{
-    auto given = std::vector<uint8_t>({0xca,0x41,0xc8,0x00,0x01});
-    json j = json::from_msgpack(given);
-    EXPECT_LT(std::fabs(j.get<double>() - 25), 0.001);
-}
-
-TEST(MessagePackErrorTest, TooShortByteVector)
-{
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcc})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcd})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcd,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xce})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xce,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xce,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xce,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 5: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 5: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 6: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 7: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 8: unexpected end of input");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xcf,0x00,0x00,0x00,0x00,0x00,0x00,0x00})), json::parse_error,
-                     "[json.exception.parse_error.110] parse error at 9: unexpected end of input");
-}
-
-TEST(MessagePackErrorTest, UnsupportedBytesConcrete)
-{
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xc1})), json::parse_error,
-                     "[json.exception.parse_error.112] parse error at 1: error reading MessagePack; last byte: 0xc1");
-    EXPECT_THROW_MSG(json::from_msgpack(std::vector<uint8_t>({0xc6})), json::parse_error,
-                     "[json.exception.parse_error.112] parse error at 1: error reading MessagePack; last byte: 0xc6");
-}
-
-TEST(MessagePackErrorTest, UnsupportedBytesAll)
-{
-    for (auto byte :
-            {
-                // never used
-                0xc1,
-                // bin
-                0xc4, 0xc5, 0xc6,
-                // ext
-                0xc7, 0xc8, 0xc9,
-                // fixext
-                0xd4, 0xd5, 0xd6, 0xd7, 0xd8
-            })
-    {
-        EXPECT_THROW(json::from_msgpack(std::vector<uint8_t>({static_cast<uint8_t>(byte)})), json::parse_error);
-    }
-}
-#if 0
-// use this testcase outside [hide] to run it with Valgrind
-TEST(MessagePackRoundtripTest, Sample)
-{
-    std::string filename = "test/data/json_testsuite/sample.json";
-
-    // parse JSON file
-    std::ifstream f_json(filename);
-    json j1 = json::parse(f_json);
-
-    // parse MessagePack file
-    std::ifstream f_msgpack(filename + ".msgpack", std::ios::binary);
-    std::vector<uint8_t> packed((std::istreambuf_iterator<char>(f_msgpack)),
-                                std::istreambuf_iterator<char>());
-    json j2;
-    EXPECT_NO_THROW(j2 = json::from_msgpack(packed));
-
-    // compare parsed JSON values
-    EXPECT_EQ(j1, j2);
-
-    // check with different start index
-    packed.insert(packed.begin(), 5, 0xff);
-    EXPECT_EQ(j1, json::from_msgpack(packed, 5));
-}
-
-class MessagePackRegressionTest : public ::testing::TestWithParam<const char*> {};
-TEST_P(MessagePackRegressionTest, Case)
-{
-    // parse JSON file
-    std::ifstream f_json(GetParam());
-    json j1 = json::parse(f_json);
-
-    // parse MessagePack file
-    std::ifstream f_msgpack(filename + ".msgpack", std::ios::binary);
-    std::vector<uint8_t> packed((std::istreambuf_iterator<char>(f_msgpack)),
-                                std::istreambuf_iterator<char>());
-    json j2;
-    EXPECT_NO_THROW(j2 = json::from_msgpack(packed));
-
-    // compare parsed JSON values
-    EXPECT_EQ(j1, j2);
-}
-
-static const char* regression_test_cases[] = {
-    "test/data/json_nlohmann_tests/all_unicode.json",
-    "test/data/json.org/1.json",
-    "test/data/json.org/2.json",
-    "test/data/json.org/3.json",
-    "test/data/json.org/4.json",
-    "test/data/json.org/5.json",
-    "test/data/json_roundtrip/roundtrip01.json",
-    "test/data/json_roundtrip/roundtrip02.json",
-    "test/data/json_roundtrip/roundtrip03.json",
-    "test/data/json_roundtrip/roundtrip04.json",
-    "test/data/json_roundtrip/roundtrip05.json",
-    "test/data/json_roundtrip/roundtrip06.json",
-    "test/data/json_roundtrip/roundtrip07.json",
-    "test/data/json_roundtrip/roundtrip08.json",
-    "test/data/json_roundtrip/roundtrip09.json",
-    "test/data/json_roundtrip/roundtrip10.json",
-    "test/data/json_roundtrip/roundtrip11.json",
-    "test/data/json_roundtrip/roundtrip12.json",
-    "test/data/json_roundtrip/roundtrip13.json",
-    "test/data/json_roundtrip/roundtrip14.json",
-    "test/data/json_roundtrip/roundtrip15.json",
-    "test/data/json_roundtrip/roundtrip16.json",
-    "test/data/json_roundtrip/roundtrip17.json",
-    "test/data/json_roundtrip/roundtrip18.json",
-    "test/data/json_roundtrip/roundtrip19.json",
-    "test/data/json_roundtrip/roundtrip20.json",
-    "test/data/json_roundtrip/roundtrip21.json",
-    "test/data/json_roundtrip/roundtrip22.json",
-    "test/data/json_roundtrip/roundtrip23.json",
-    "test/data/json_roundtrip/roundtrip24.json",
-    "test/data/json_roundtrip/roundtrip25.json",
-    "test/data/json_roundtrip/roundtrip26.json",
-    "test/data/json_roundtrip/roundtrip27.json",
-    "test/data/json_roundtrip/roundtrip28.json",
-    "test/data/json_roundtrip/roundtrip29.json",
-    "test/data/json_roundtrip/roundtrip30.json",
-    "test/data/json_roundtrip/roundtrip31.json",
-    "test/data/json_roundtrip/roundtrip32.json",
-    "test/data/json_testsuite/sample.json", // kills AppVeyor
-    "test/data/json_tests/pass1.json",
-    "test/data/json_tests/pass2.json",
-    "test/data/json_tests/pass3.json",
-    "test/data/regression/floats.json",
-    "test/data/regression/signed_ints.json",
-    "test/data/regression/unsigned_ints.json",
-    "test/data/regression/working_file.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_arraysWithSpaces.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_empty-string.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_empty.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_ending_with_newline.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_false.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_heterogeneous.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_null.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_with_1_and_newline.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_with_leading_space.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_with_several_null.json",
-    "test/data/nst_json_testsuite/test_parsing/y_array_with_trailing_space.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_0e+1.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_0e1.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_after_space.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_double_close_to_zero.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_double_huge_neg_exp.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_int_with_exp.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_minus_zero.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_negative_int.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_negative_one.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_negative_zero.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e_neg_exp.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e_pos_exp.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_exponent.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_fraction_exponent.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_exp.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_exponent.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_real_underflow.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_simple_int.json",
-    "test/data/nst_json_testsuite/test_parsing/y_number_simple_real.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_too_big_neg_int.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_too_big_pos_int.json",
-    //"test/data/nst_json_testsuite/test_parsing/y_number_very_big_negative_int.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_basic.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_duplicated_key.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_duplicated_key_and_value.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_empty.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_empty_key.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_escaped_null_in_key.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_extreme_numbers.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_long_strings.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_simple.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_string_unicode.json",
-    "test/data/nst_json_testsuite/test_parsing/y_object_with_newlines.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_UTF-16_Surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_accepted_surrogate_pair.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_accepted_surrogate_pairs.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_allowed_escapes.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_backslash_and_u_escaped_zero.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_backslash_doublequotes.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_comments.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_double_escape_a.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_double_escape_n.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_escaped_control_character.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_escaped_noncharacter.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_in_array.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_in_array_with_leading_space.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_last_surrogates_1_and_2.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_newline_uescaped.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+1FFFF.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_null_escape.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_one-byte-utf-8.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_pi.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_simple_ascii.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_space.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_three-byte-utf-8.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_two-byte-utf-8.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_u+2028_line_sep.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_u+2029_par_sep.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_uEscape.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unescaped_char_delete.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicode.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicodeEscapedBackslash.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_2.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_U+2064_invisible_plus.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_escaped_double_quote.json",
-    // "test/data/nst_json_testsuite/test_parsing/y_string_utf16.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_utf8.json",
-    "test/data/nst_json_testsuite/test_parsing/y_string_with_del_character.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_false.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_int.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_negative_real.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_null.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_string.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_true.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_string_empty.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_trailing_newline.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_true_in_array.json",
-    "test/data/nst_json_testsuite/test_parsing/y_structure_whitespace_array.json",
-};
-
-INSTANTIATE_TEST_SUITE_P(MessagePackRegressionTests, MessagePackRegressionTest,
-                        ::testing::ValuesIn(regression_test_cases));
-#endif
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-pointer_access.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-pointer_access.cpp
deleted file mode 100644
index 3224b05..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-pointer_access.cpp
+++ /dev/null
@@ -1,466 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-
-#include <cmath>
-
-using wpi::json;
-
-TEST(JsonPointerTest, TypesCreate)
-{
-    // create a JSON value with different types
-    json json_types =
-    {
-        {"boolean", true},
-        {
-            "number", {
-                {"integer", 42},
-                {"unsigned", 42u},
-                {"floating-point", 17.23}
-            }
-        },
-        {"string", "Hello, world!"},
-        {"array", {1, 2, 3, 4, 5}},
-        {"null", nullptr}
-    };
-}
-
-// pointer access to object_t
-TEST(JsonPointerTest, ObjectT)
-{
-    using test_type = json::object_t;
-    json value = {{"one", 1}, {"two", 2}};
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_NE(value.get_ptr<json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<double*>(), nullptr);
-}
-
-// pointer access to const object_t
-TEST(JsonPointerTest, ConstObjectT)
-{
-    using test_type = const json::object_t;
-    const json value = {{"one", 1}, {"two", 2}};
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_NE(value.get_ptr<const json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
-}
-
-// pointer access to array_t
-TEST(JsonPointerTest, ArrayT)
-{
-    using test_type = json::array_t;
-    json value = {1, 2, 3, 4};
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
-    EXPECT_NE(value.get_ptr<json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<double*>(), nullptr);
-}
-
-// pointer access to const array_t
-TEST(JsonPointerTest, ConstArrayT)
-{
-    using test_type = const json::array_t;
-    const json value = {1, 2, 3, 4};
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
-    EXPECT_NE(value.get_ptr<const json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
-}
-
-// pointer access to string_t
-TEST(JsonPointerTest, StringT)
-{
-    using test_type = std::string;
-    json value = "hello";
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
-    EXPECT_NE(value.get_ptr<std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<double*>(), nullptr);
-}
-
-// pointer access to const string_t
-TEST(JsonPointerTest, ConstStringT)
-{
-    using test_type = const std::string;
-    const json value = "hello";
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
-    EXPECT_NE(value.get_ptr<const std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
-}
-
-// pointer access to boolean_t
-TEST(JsonPointerTest, BooleanT)
-{
-    using test_type = bool;
-    json value = false;
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
-    EXPECT_NE(value.get_ptr<bool*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<double*>(), nullptr);
-}
-
-// pointer access to const boolean_t
-TEST(JsonPointerTest, ConstBooleanT)
-{
-    using test_type = const bool;
-    const json value = false;
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    //EXPECT_EQ(*p1, value.get<test_type>());
-
-    //const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    //EXPECT_EQ(*p2, value.get<test_type>());
-
-    //const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    //EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
-    EXPECT_NE(value.get_ptr<const bool*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
-}
-
-// pointer access to number_integer_t
-TEST(JsonPointerTest, IntegerT)
-{
-    using test_type = int64_t;
-    json value = 23;
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
-    EXPECT_NE(value.get_ptr<int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<double*>(), nullptr);
-}
-
-// pointer access to const number_integer_t
-TEST(JsonPointerTest, ConstIntegerT)
-{
-    using test_type = const int64_t;
-    const json value = 23;
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
-    EXPECT_NE(value.get_ptr<const int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
-}
-
-// pointer access to number_unsigned_t
-TEST(JsonPointerTest, UnsignedT)
-{
-    using test_type = uint64_t;
-    json value = 23u;
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
-    EXPECT_NE(value.get_ptr<int64_t*>(), nullptr);
-    EXPECT_NE(value.get_ptr<uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<double*>(), nullptr);
-}
-
-// pointer access to const number_unsigned_t
-TEST(JsonPointerTest, ConstUnsignedT)
-{
-    using test_type = const uint64_t;
-    const json value = 23u;
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(*p1, value.get<test_type>());
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_EQ(*p2, value.get<test_type>());
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_EQ(*p3, value.get<test_type>());
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
-    EXPECT_NE(value.get_ptr<const int64_t*>(), nullptr);
-    EXPECT_NE(value.get_ptr<const uint64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const double*>(), nullptr);
-}
-
-// pointer access to number_float_t
-TEST(JsonPointerTest, FloatT)
-{
-    using test_type = double;
-    json value = 42.23;
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_LT(std::fabs(*p1 - value.get<test_type>()), 0.001);
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_LT(std::fabs(*p2 - value.get<test_type>()), 0.001);
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_LT(std::fabs(*p3 - value.get<test_type>()), 0.001);
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<bool*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<uint64_t*>(), nullptr);
-    EXPECT_NE(value.get_ptr<double*>(), nullptr);
-}
-
-// pointer access to const number_float_t
-TEST(JsonPointerTest, ConstFloatT)
-{
-    using test_type = const double;
-    const json value = 42.23;
-
-    // check if pointers are returned correctly
-    test_type* p1 = value.get_ptr<test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<test_type*>());
-    EXPECT_LT(std::fabs(*p1 - value.get<test_type>()), 0.001);
-
-    const test_type* p2 = value.get_ptr<const test_type*>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type*>());
-    EXPECT_LT(std::fabs(*p2 - value.get<test_type>()), 0.001);
-
-    const test_type* const p3 = value.get_ptr<const test_type* const>();
-    EXPECT_EQ(p1, value.get_ptr<const test_type* const>());
-    EXPECT_LT(std::fabs(*p3 - value.get<test_type>()), 0.001);
-
-    // check if null pointers are returned correctly
-    EXPECT_EQ(value.get_ptr<const json::object_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const json::array_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const std::string*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const bool*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const int64_t*>(), nullptr);
-    EXPECT_EQ(value.get_ptr<const uint64_t*>(), nullptr);
-    EXPECT_NE(value.get_ptr<const double*>(), nullptr);
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-readme.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-readme.cpp
deleted file mode 100644
index d9bb9e5..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-readme.cpp
+++ /dev/null
@@ -1,327 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-#include <array>
-#include <deque>
-#include <forward_list>
-#include <list>
-#include <map>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "wpi/raw_ostream.h"
-
-TEST(JsonReadmeTest, Basic)
-{
-    // create an empty structure (null)
-    json j;
-
-    // add a number that is stored as double (note the implicit conversion of j to an object)
-    j["pi"] = 3.141;
-
-    // add a Boolean that is stored as bool
-    j["happy"] = true;
-
-    // add a string that is stored as std::string
-    j["name"] = "Niels";
-
-    // add another null object by passing nullptr
-    j["nothing"] = nullptr;
-
-    // add an object inside the object
-    j["answer"]["everything"] = 42;
-
-    // add an array that is stored as std::vector (using an initializer list)
-    j["list"] = { 1, 0, 2 };
-
-    // add another object (using an initializer list of pairs)
-    j["object"] = { {"currency", "USD"}, {"value", 42.99} };
-
-    // instead, you could also write (which looks very similar to the JSON above)
-    json j2 =
-    {
-        {"pi", 3.141},
-        {"happy", true},
-        {"name", "Niels"},
-        {"nothing", nullptr},
-        {
-            "answer", {
-                {"everything", 42}
-            }
-        },
-        {"list", {1, 0, 2}},
-        {
-            "object", {
-                {"currency", "USD"},
-                {"value", 42.99}
-            }
-        }
-    };
-}
-
-TEST(JsonReadmeTest, Other)
-{
-    // ways to express the empty array []
-    json empty_array_implicit = {{}};
-    json empty_array_explicit = json::array();
-
-    // a way to express the empty object {}
-    json empty_object_explicit = json::object();
-
-    // a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
-    json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) };
-}
-
-TEST(JsonReadmeTest, FromToString)
-{
-    // create object from string literal
-    json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
-
-    // or even nicer with a raw string literal
-    auto j2 = R"(
-  {
-    "happy": true,
-    "pi": 3.141
-  }
-)"_json;
-
-    // or explicitly
-    auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");
-
-    // explicit conversion to string
-    std::string s;
-    wpi::raw_string_ostream os(s);
-    j.dump(os);    // {\"happy\":true,\"pi\":3.141}
-    EXPECT_EQ(os.str(), "{\"happy\":true,\"pi\":3.141}");
-
-    // serialization with pretty printing
-    // pass in the amount of spaces to indent
-    std::string s2;
-    wpi::raw_string_ostream os2(s2);
-    j2.dump(os2, 4);
-    EXPECT_EQ(os2.str(), "{\n    \"happy\": true,\n    \"pi\": 3.141\n}");
-    // {
-    //     "happy": true,
-    //     "pi": 3.141
-    // }
-}
-
-TEST(JsonReadmeTest, Basic2)
-{
-    // create an array using push_back
-    json j;
-    j.push_back("foo");
-    j.push_back(1);
-    j.push_back(true);
-
-    std::string s;
-    wpi::raw_string_ostream os(s);
-
-    // iterate the array
-    for (json::iterator it = j.begin(); it != j.end(); ++it)
-    {
-        os << *it << '\n';
-    }
-
-    // range-based for
-    for (auto element : j)
-    {
-        os << element << '\n';
-    }
-
-    // comparison
-    bool x = (j == "[\"foo\", 1, true]"_json);  // true
-    EXPECT_EQ(x, true);
-
-    // getter/setter
-    const std::string tmp = j[0];
-    j[1] = 42;
-    bool foo = j.at(2);
-    EXPECT_EQ(foo, true);
-
-    // other stuff
-    EXPECT_EQ(j.size(), 3u);        // 3 entries
-    EXPECT_EQ(j.empty(), false);
-    EXPECT_EQ(j.type(), json::value_t::array);
-    j.clear();    // the array is empty again
-    EXPECT_EQ(j.size(), 0u);
-    EXPECT_EQ(j.empty(), true);
-
-    // create an object
-    json o;
-    o["foo"] = 23;
-    o["bar"] = false;
-    o["baz"] = 3.141;
-
-    // find an entry
-    if (o.find("foo") != o.end())
-    {
-        // there is an entry with key "foo"
-    }
-}
-
-TEST(JsonReadmeTest, OtherContainer)
-{
-    std::vector<int> c_vector {1, 2, 3, 4};
-    json j_vec(c_vector);
-    json j_vec2(std::span<const int>(c_vector.data(), c_vector.size()));
-    // [1, 2, 3, 4]
-
-    std::deque<float> c_deque {1.2f, 2.3f, 3.4f, 5.6f};
-    json j_deque(c_deque);
-    // [1.2, 2.3, 3.4, 5.6]
-
-    std::list<bool> c_list {true, true, false, true};
-    json j_list(c_list);
-    // [true, true, false, true]
-
-    std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
-    json j_flist(c_flist);
-    // [12345678909876, 23456789098765, 34567890987654, 45678909876543]
-
-    std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
-    json j_array(c_array);
-    // [1, 2, 3, 4]
-
-    std::set<std::string> c_set {"one", "two", "three", "four", "one"};
-    json j_set(c_set); // only one entry for "one" is used
-    // ["four", "one", "three", "two"]
-
-    std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
-    json j_uset(c_uset); // only one entry for "one" is used
-    // maybe ["two", "three", "four", "one"]
-
-    std::multiset<std::string> c_mset {"one", "two", "one", "four"};
-    json j_mset(c_mset); // both entries for "one" are used
-    // maybe ["one", "two", "one", "four"]
-
-    std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
-    json j_umset(c_umset); // both entries for "one" are used
-    // maybe ["one", "two", "one", "four"]
-}
-
-TEST(JsonReadmeTest, MapContainer)
-{
-    std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
-    json j_map(c_map);
-    // {"one": 1, "two": 2, "three": 3}
-
-#if 0
-    std::unordered_map<const char*, float> c_umap { {"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f} };
-    json j_umap(c_umap);
-    // {"one": 1.2, "two": 2.3, "three": 3.4}
-#endif
-
-    std::multimap<std::string, bool> c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
-    json j_mmap(c_mmap); // only one entry for key "three" is used
-    // maybe {"one": true, "two": true, "three": true}
-
-    std::unordered_multimap<std::string, bool> c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
-    json j_ummap(c_ummap); // only one entry for key "three" is used
-    // maybe {"one": true, "two": true, "three": true}
-}
-
-TEST(JsonReadmeTest, Values)
-{
-    // strings
-    std::string s1 = "Hello, world!";
-    json js = s1;
-    std::string s2 = js;
-    EXPECT_EQ(s1, s2);
-
-    // Booleans
-    bool b1 = true;
-    json jb = b1;
-    bool b2 = jb;
-    EXPECT_EQ(b1, b2);
-
-    // numbers
-    int i = 42;
-    json jn = i;
-    double f = jn;
-    EXPECT_EQ(i, f);
-
-    // etc.
-
-    std::string vs = js.get<std::string>();
-    bool vb = jb.get<bool>();
-    int vi = jn.get<int>();
-    EXPECT_EQ(s1, vs);
-    EXPECT_EQ(b1, vb);
-    EXPECT_EQ(i, vi);
-
-    // etc.
-}
-
-#if 0
-TEST(JsonReadmeTest, DiffPatch)
-{
-    // a JSON value
-    json j_original = R"({
-  "baz": ["one", "two", "three"],
-  "foo": "bar"
-})"_json;
-
-    // access members with a JSON pointer (RFC 6901)
-    j_original["/baz/1"_json_pointer];
-    // "two"
-
-    // a JSON patch (RFC 6902)
-    json j_patch = R"([
-  { "op": "replace", "path": "/baz", "value": "boo" },
-  { "op": "add", "path": "/hello", "value": ["world"] },
-  { "op": "remove", "path": "/foo"}
-])"_json;
-
-    // apply the patch
-    json j_result = j_original.patch(j_patch);
-    // {
-    //    "baz": "boo",
-    //    "hello": ["world"]
-    // }
-
-    // calculate a JSON patch from two JSON values
-    json::diff(j_result, j_original);
-    // [
-    //   { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
-    //   { "op":"remove","path":"/hello" },
-    //   { "op":"add","path":"/foo","value":"bar" }
-    // ]
-}
-#endif
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-reference_access.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-reference_access.cpp
deleted file mode 100644
index 7c3cfb1..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-reference_access.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-// reference access to object_t
-TEST(JsonReferenceTest, ObjectT)
-{
-    using test_type = json::object_t;
-    json value = {{"one", 1}, {"two", 2}};
-
-    // check if references are returned correctly
-    test_type& p1 = value.get_ref<test_type&>();
-    EXPECT_EQ(&p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(p1, value.get<test_type>());
-
-    const test_type& p2 = value.get_ref<const test_type&>();
-    EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
-    EXPECT_EQ(p2, value.get<test_type>());
-
-    // check if mismatching references throw correctly
-    EXPECT_NO_THROW(value.get_ref<json::object_t&>());
-    EXPECT_ANY_THROW(value.get_ref<json::array_t&>());
-    EXPECT_ANY_THROW(value.get_ref<std::string&>());
-    EXPECT_ANY_THROW(value.get_ref<bool&>());
-    EXPECT_ANY_THROW(value.get_ref<int64_t&>());
-    EXPECT_ANY_THROW(value.get_ref<double&>());
-}
-
-// const reference access to const object_t
-TEST(JsonReferenceTest, ConstObjectT)
-{
-    using test_type = json::object_t;
-    const json value = {{"one", 1}, {"two", 2}};
-
-    // this should not compile
-    // test_type& p1 = value.get_ref<test_type&>();
-
-    // check if references are returned correctly
-    const test_type& p2 = value.get_ref<const test_type&>();
-    EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
-    EXPECT_EQ(p2, value.get<test_type>());
-}
-
-// reference access to array_t
-TEST(JsonReferenceTest, ArrayT)
-{
-    using test_type = json::array_t;
-    json value = {1, 2, 3, 4};
-
-    // check if references are returned correctly
-    test_type& p1 = value.get_ref<test_type&>();
-    EXPECT_EQ(&p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(p1, value.get<test_type>());
-
-    const test_type& p2 = value.get_ref<const test_type&>();
-    EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
-    EXPECT_EQ(p2, value.get<test_type>());
-
-    // check if mismatching references throw correctly
-    EXPECT_ANY_THROW(value.get_ref<json::object_t&>());
-    EXPECT_NO_THROW(value.get_ref<json::array_t&>());
-    EXPECT_ANY_THROW(value.get_ref<std::string&>());
-    EXPECT_ANY_THROW(value.get_ref<bool&>());
-    EXPECT_ANY_THROW(value.get_ref<int64_t&>());
-    EXPECT_ANY_THROW(value.get_ref<double&>());
-}
-
-// reference access to string_t
-TEST(JsonReferenceTest, StringT)
-{
-    using test_type = std::string;
-    json value = "hello";
-
-    // check if references are returned correctly
-    test_type& p1 = value.get_ref<test_type&>();
-    EXPECT_EQ(&p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(p1, value.get<test_type>());
-
-    const test_type& p2 = value.get_ref<const test_type&>();
-    EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
-    EXPECT_EQ(p2, value.get<test_type>());
-
-    // check if mismatching references throw correctly
-    EXPECT_ANY_THROW(value.get_ref<json::object_t&>());
-    EXPECT_ANY_THROW(value.get_ref<json::array_t&>());
-    EXPECT_NO_THROW(value.get_ref<std::string&>());
-    EXPECT_ANY_THROW(value.get_ref<bool&>());
-    EXPECT_ANY_THROW(value.get_ref<int64_t&>());
-    EXPECT_ANY_THROW(value.get_ref<double&>());
-}
-
-// reference access to boolean_t
-TEST(JsonReferenceTest, BooleanT)
-{
-    using test_type = bool;
-    json value = false;
-
-    // check if references are returned correctly
-    test_type& p1 = value.get_ref<test_type&>();
-    EXPECT_EQ(&p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(p1, value.get<test_type>());
-
-    const test_type& p2 = value.get_ref<const test_type&>();
-    EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
-    EXPECT_EQ(p2, value.get<test_type>());
-
-    // check if mismatching references throw correctly
-    EXPECT_ANY_THROW(value.get_ref<json::object_t&>());
-    EXPECT_ANY_THROW(value.get_ref<json::array_t&>());
-    EXPECT_ANY_THROW(value.get_ref<std::string&>());
-    EXPECT_NO_THROW(value.get_ref<bool&>());
-    EXPECT_ANY_THROW(value.get_ref<int64_t&>());
-    EXPECT_ANY_THROW(value.get_ref<double&>());
-}
-
-// reference access to number_integer_t
-TEST(JsonReferenceTest, IntegerT)
-{
-    using test_type = int64_t;
-    json value = 23;
-
-    // check if references are returned correctly
-    test_type& p1 = value.get_ref<test_type&>();
-    EXPECT_EQ(&p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(p1, value.get<test_type>());
-
-    const test_type& p2 = value.get_ref<const test_type&>();
-    EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
-    EXPECT_EQ(p2, value.get<test_type>());
-
-    // check if mismatching references throw correctly
-    EXPECT_ANY_THROW(value.get_ref<json::object_t&>());
-    EXPECT_ANY_THROW(value.get_ref<json::array_t&>());
-    EXPECT_ANY_THROW(value.get_ref<std::string&>());
-    EXPECT_ANY_THROW(value.get_ref<bool&>());
-    EXPECT_NO_THROW(value.get_ref<int64_t&>());
-    EXPECT_ANY_THROW(value.get_ref<double&>());
-}
-
-// reference access to number_float_t
-TEST(JsonReferenceTest, FloatT)
-{
-    using test_type = double;
-    json value = 42.23;
-
-    // check if references are returned correctly
-    test_type& p1 = value.get_ref<test_type&>();
-    EXPECT_EQ(&p1, value.get_ptr<test_type*>());
-    EXPECT_EQ(p1, value.get<test_type>());
-
-    const test_type& p2 = value.get_ref<const test_type&>();
-    EXPECT_EQ(&p2, value.get_ptr<const test_type*>());
-    EXPECT_EQ(p2, value.get<test_type>());
-
-    // check if mismatching references throw correctly
-    EXPECT_ANY_THROW(value.get_ref<json::object_t&>());
-    EXPECT_ANY_THROW(value.get_ref<json::array_t&>());
-    EXPECT_ANY_THROW(value.get_ref<std::string&>());
-    EXPECT_ANY_THROW(value.get_ref<bool&>());
-    EXPECT_ANY_THROW(value.get_ref<int64_t&>());
-    EXPECT_NO_THROW(value.get_ref<double&>());
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-unicode.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-unicode.cpp
deleted file mode 100644
index 6755fe2..0000000
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/json/unit-unicode.cpp
+++ /dev/null
@@ -1,1089 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) FIRST 2017. 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++ (test suite)
-|  |  |__   |  |  | | | |  version 2.1.1
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 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 "gtest/gtest.h"
-
-#include "unit-json.h"
-using wpi::json;
-
-#include "fmt/format.h"
-#include "wpi/StringExtras.h"
-#include "wpi/raw_ostream.h"
-
-#include <fstream>
-
-// create and check a JSON string with up to four UTF-8 bytes
-::testing::AssertionResult check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
-{
-    std::string json_string = "\"";
-
-    json_string += std::string(1, static_cast<char>(byte1));
-
-    if (byte2 != -1)
-    {
-        json_string += std::string(1, static_cast<char>(byte2));
-    }
-
-    if (byte3 != -1)
-    {
-        json_string += std::string(1, static_cast<char>(byte3));
-    }
-
-    if (byte4 != -1)
-    {
-        json_string += std::string(1, static_cast<char>(byte4));
-    }
-
-    json_string += "\"";
-
-    const char* basemsg = "";
-
-    try {
-        json::parse(json_string);
-    } catch (json::parse_error&) {
-        if (success_expected)
-        {
-            basemsg = "parse_error";
-            goto error;
-        }
-        return ::testing::AssertionSuccess();
-    } catch (...) {
-        basemsg = "other exception";
-        goto error;
-    }
-
-    if (success_expected)
-    {
-        return ::testing::AssertionSuccess();
-    }
-    basemsg = "expected failure";
-
-error:
-    auto result = ::testing::AssertionFailure();
-    result << basemsg << " with {" << wpi::utohexstr(byte1);
-    if (byte2 != -1)
-    {
-        result << ',' << wpi::utohexstr(byte2);
-    }
-    if (byte3 != -1)
-    {
-        result << ',' << wpi::utohexstr(byte3);
-    }
-    if (byte4 != -1)
-    {
-        result << ',' << wpi::utohexstr(byte4);
-    }
-    result << '}';
-    return result;
-}
-
-/*
-RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
-follows:
-
-    A UTF-8 string is a sequence of octets representing a sequence of UCS
-    characters.  An octet sequence is valid UTF-8 only if it matches the
-    following syntax, which is derived from the rules for encoding UTF-8
-    and is expressed in the ABNF of [RFC2234].
-
-    UTF8-octets = *( UTF8-char )
-    UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
-    UTF8-1      = %x00-7F
-    UTF8-2      = %xC2-DF UTF8-tail
-    UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
-                  %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
-    UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
-                  %xF4 %x80-8F 2( UTF8-tail )
-    UTF8-tail   = %x80-BF
-*/
-
-// ill-formed first byte
-TEST(JsonUnicodeRfc3629Test, IllFormedFirstByte)
-{
-    for (int byte1 = 0x80; byte1 <= 0xC1; ++byte1)
-    {
-        EXPECT_TRUE(check_utf8string(false, byte1));
-    }
-
-    for (int byte1 = 0xF5; byte1 <= 0xFF; ++byte1)
-    {
-        EXPECT_TRUE(check_utf8string(false, byte1));
-    }
-}
-
-// UTF8-1 (x00-x7F), well-formed
-TEST(JsonUnicodeRfc3629Test, Utf8_1WellFormed)
-{
-    for (int byte1 = 0x00; byte1 <= 0x7F; ++byte1)
-    {
-        // unescaped control characters are parse errors in JSON
-        if (0x00 <= byte1 && byte1 <= 0x1F)
-        {
-            EXPECT_TRUE(check_utf8string(false, byte1));
-            continue;
-        }
-
-        // a single quote is a parse error in JSON
-        if (byte1 == 0x22)
-        {
-            EXPECT_TRUE(check_utf8string(false, byte1));
-            continue;
-        }
-
-        // a single backslash is a parse error in JSON
-        if (byte1 == 0x5C)
-        {
-            EXPECT_TRUE(check_utf8string(false, byte1));
-            continue;
-        }
-
-        // all other characters are OK
-        EXPECT_TRUE(check_utf8string(true, byte1));
-    }
-}
-
-// UTF8-2 (xC2-xDF UTF8-tail)
-// well-formed
-TEST(JsonUnicodeRfc3629Test, Utf8_2WellFormed)
-{
-    for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            EXPECT_TRUE(check_utf8string(true, byte1, byte2));
-        }
-    }
-}
-
-// ill-formed: missing second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_2Missing2)
-{
-    for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
-    {
-        EXPECT_TRUE(check_utf8string(false, byte1));
-    }
-}
-
-// ill-formed: wrong second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_2Wrong2)
-{
-    for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
-    {
-        for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
-        {
-            // skip correct second byte
-            if (0x80 <= byte2 && byte2 <= 0xBF)
-            {
-                continue;
-            }
-
-            EXPECT_TRUE(check_utf8string(false, byte1, byte2));
-        }
-    }
-}
-
-// UTF8-3 (xE0 xA0-BF UTF8-tail)
-// well-formed
-TEST(JsonUnicodeRfc3629Test, Utf8_3AWellFormed)
-{
-    for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
-    {
-        for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: missing second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3AMissing2)
-{
-    for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
-    {
-        EXPECT_TRUE(check_utf8string(false, byte1));
-    }
-}
-
-// ill-formed: missing third byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3AMissing3)
-{
-    for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
-    {
-        for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
-        {
-            EXPECT_TRUE(check_utf8string(false, byte1, byte2));
-        }
-    }
-}
-
-// ill-formed: wrong second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3AWrong2)
-{
-    for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
-    {
-        for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
-        {
-            // skip correct second byte
-            if (0xA0 <= byte2 && byte2 <= 0xBF)
-            {
-                continue;
-            }
-
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: wrong third byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3AWrong3)
-{
-    for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
-    {
-        for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
-            {
-                // skip correct third byte
-                if (0x80 <= byte3 && byte3 <= 0xBF)
-                {
-                    continue;
-                }
-
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// UTF8-3 (xE1-xEC UTF8-tail UTF8-tail)
-// well-formed
-TEST(JsonUnicodeRfc3629Test, Utf8_3BWellFormed)
-{
-    for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: missing second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3BMissing2)
-{
-    for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
-    {
-        EXPECT_TRUE(check_utf8string(false, byte1));
-    }
-}
-
-// ill-formed: missing third byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3BMissing3)
-{
-    for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            EXPECT_TRUE(check_utf8string(false, byte1, byte2));
-        }
-    }
-}
-
-// ill-formed: wrong second byte
-TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_3BWrong2)
-{
-    for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
-    {
-        for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
-        {
-            // skip correct second byte
-            if (0x80 <= byte2 && byte2 <= 0xBF)
-            {
-                continue;
-            }
-
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: wrong third byte
-TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_3BWrong3)
-{
-    for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
-            {
-                // skip correct third byte
-                if (0x80 <= byte3 && byte3 <= 0xBF)
-                {
-                    continue;
-                }
-
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// UTF8-3 (xED x80-9F UTF8-tail)
-// well-formed
-TEST(JsonUnicodeRfc3629Test, Utf8_3CWellFormed)
-{
-    for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: missing second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3CMissing2)
-{
-    for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
-    {
-        EXPECT_TRUE(check_utf8string(false, byte1));
-    }
-}
-
-// ill-formed: missing third byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3CMissing3)
-{
-    for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
-        {
-            EXPECT_TRUE(check_utf8string(false, byte1, byte2));
-        }
-    }
-}
-
-// ill-formed: wrong second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3CWrong2)
-{
-    for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
-    {
-        for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
-        {
-            // skip correct second byte
-            if (0x80 <= byte2 && byte2 <= 0x9F)
-            {
-                continue;
-            }
-
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: wrong third byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3CWrong3)
-{
-    for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
-        {
-            for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
-            {
-                // skip correct third byte
-                if (0x80 <= byte3 && byte3 <= 0xBF)
-                {
-                    continue;
-                }
-
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// UTF8-3 (xEE-xEF UTF8-tail UTF8-tail)
-// well-formed
-TEST(JsonUnicodeRfc3629Test, Utf8_3DWellFormed)
-{
-    for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: missing second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3DMissing2)
-{
-    for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
-    {
-        EXPECT_TRUE(check_utf8string(false, byte1));
-    }
-}
-
-// ill-formed: missing third byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3DMissing3)
-{
-    for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            EXPECT_TRUE(check_utf8string(false, byte1, byte2));
-        }
-    }
-}
-
-// ill-formed: wrong second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3DWrong2)
-{
-    for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
-    {
-        for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
-        {
-            // skip correct second byte
-            if (0x80 <= byte2 && byte2 <= 0xBF)
-            {
-                continue;
-            }
-
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: wrong third byte
-TEST(JsonUnicodeRfc3629Test, Utf8_3DWrong3)
-{
-    for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
-            {
-                // skip correct third byte
-                if (0x80 <= byte3 && byte3 <= 0xBF)
-                {
-                    continue;
-                }
-
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// UTF8-4 (xF0 x90-BF UTF8-tail UTF8-tail)
-// well-formed
-TEST(JsonUnicodeRfc3629Test, Utf8_4AWellFormed)
-{
-    for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
-    {
-        for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
-                {
-                    EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// ill-formed: missing second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4AMissing2)
-{
-    for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
-    {
-        EXPECT_TRUE(check_utf8string(false, byte1));
-    }
-}
-
-// ill-formed: missing third byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4AMissing3)
-{
-    for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
-    {
-        for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
-        {
-            EXPECT_TRUE(check_utf8string(false, byte1, byte2));
-        }
-    }
-}
-
-// ill-formed: missing fourth byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4AMissing4)
-{
-    for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
-    {
-        for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: wrong second byte
-TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4AWrong2)
-{
-    for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
-    {
-        for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
-        {
-            // skip correct second byte
-            if (0x90 <= byte2 && byte2 <= 0xBF)
-            {
-                continue;
-            }
-
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
-                {
-                    EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// ill-formed: wrong third byte
-TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4AWrong3)
-{
-    for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
-    {
-        for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
-            {
-                // skip correct third byte
-                if (0x80 <= byte3 && byte3 <= 0xBF)
-                {
-                    continue;
-                }
-
-                for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
-                {
-                    EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// ill-formed: wrong fourth byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4AWrong4)
-{
-    for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
-    {
-        for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
-                {
-                    // skip fourth second byte
-                    if (0x80 <= byte3 && byte3 <= 0xBF)
-                    {
-                        continue;
-                    }
-
-                    EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// UTF8-4 (xF1-F3 UTF8-tail UTF8-tail UTF8-tail)
-// well-formed
-TEST(JsonUnicodeRfc3629Test, Utf8_4BWellFormed)
-{
-    for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
-                {
-                    EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// ill-formed: missing second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4BMissing2)
-{
-    for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
-    {
-        EXPECT_TRUE(check_utf8string(false, byte1));
-    }
-}
-
-// ill-formed: missing third byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4BMissing3)
-{
-    for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            EXPECT_TRUE(check_utf8string(false, byte1, byte2));
-        }
-    }
-}
-
-// ill-formed: missing fourth byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4BMissing4)
-{
-    for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: wrong second byte
-TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4BWrong2)
-{
-    for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
-    {
-        for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
-        {
-            // skip correct second byte
-            if (0x80 <= byte2 && byte2 <= 0xBF)
-            {
-                continue;
-            }
-
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
-                {
-                    EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// ill-formed: wrong third byte
-TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4BWrong3)
-{
-    for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
-            {
-                // skip correct third byte
-                if (0x80 <= byte3 && byte3 <= 0xBF)
-                {
-                    continue;
-                }
-
-                for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
-                {
-                    EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// ill-formed: wrong fourth byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4BWrong4)
-{
-    for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
-                {
-                    // skip correct fourth byte
-                    if (0x80 <= byte3 && byte3 <= 0xBF)
-                    {
-                        continue;
-                    }
-
-                    EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// UTF8-4 (xF4 x80-8F UTF8-tail UTF8-tail)
-// well-formed
-TEST(JsonUnicodeRfc3629Test, Utf8_4CWellFormed)
-{
-    for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
-                {
-                    EXPECT_TRUE(check_utf8string(true, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// ill-formed: missing second byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4CMissing2)
-{
-    for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
-    {
-        EXPECT_TRUE(check_utf8string(false, byte1));
-    }
-}
-
-// ill-formed: missing third byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4CMissing3)
-{
-    for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
-        {
-            EXPECT_TRUE(check_utf8string(false, byte1, byte2));
-        }
-    }
-}
-
-// ill-formed: missing fourth byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4CMissing4)
-{
-    for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3));
-            }
-        }
-    }
-}
-
-// ill-formed: wrong second byte
-TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4CWrong2)
-{
-    for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
-    {
-        for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
-        {
-            // skip correct second byte
-            if (0x80 <= byte2 && byte2 <= 0x8F)
-            {
-                continue;
-            }
-
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
-                {
-                    EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// ill-formed: wrong third byte
-TEST(JsonUnicodeRfc3629Test, DISABLED_Utf8_4CWrong3)
-{
-    for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
-        {
-            for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
-            {
-                // skip correct third byte
-                if (0x80 <= byte3 && byte3 <= 0xBF)
-                {
-                    continue;
-                }
-
-                for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
-                {
-                    EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// ill-formed: wrong fourth byte
-TEST(JsonUnicodeRfc3629Test, Utf8_4CWrong4)
-{
-    for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
-    {
-        for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
-        {
-            for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
-            {
-                for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
-                {
-                    // skip correct fourth byte
-                    if (0x80 <= byte3 && byte3 <= 0xBF)
-                    {
-                        continue;
-                    }
-
-                    EXPECT_TRUE(check_utf8string(false, byte1, byte2, byte3, byte4));
-                }
-            }
-        }
-    }
-}
-
-// \\uxxxx sequences
-
-// create an escaped string from a code point
-static std::string codepoint_to_unicode(std::size_t cp)
-{
-    // code points are represented as a six-character sequence: a
-    // reverse solidus, followed by the lowercase letter u, followed
-    // by four hexadecimal digits that encode the character's code
-    // point
-    return fmt::format("\\u{:04x}", cp);
-}
-
-// correct sequences
-TEST(JsonUnicodeCodepointTest, DISABLED_Correct)
-{
-    // generate all UTF-8 code points; in total, 1112064 code points are
-    // generated: 0x1FFFFF code points - 2048 invalid values between
-    // 0xD800 and 0xDFFF.
-    for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp)
-    {
-        // string to store the code point as in \uxxxx format
-        std::string json_text = "\"";
-
-        // decide whether to use one or two \uxxxx sequences
-        if (cp < 0x10000u)
-        {
-            // The Unicode standard permanently reserves these code point
-            // values for UTF-16 encoding of the high and low surrogates, and
-            // they will never be assigned a character, so there should be no
-            // reason to encode them. The official Unicode standard says that
-            // no UTF forms, including UTF-16, can encode these code points.
-            if (cp >= 0xD800u && cp <= 0xDFFFu)
-            {
-                // if we would not skip these code points, we would get a
-                // "missing low surrogate" exception
-                continue;
-            }
-
-            // code points in the Basic Multilingual Plane can be
-            // represented with one \uxxxx sequence
-            json_text += codepoint_to_unicode(cp);
-        }
-        else
-        {
-            // To escape an extended character that is not in the Basic
-            // Multilingual Plane, the character is represented as a
-            // 12-character sequence, encoding the UTF-16 surrogate pair
-            const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu);
-            const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
-            json_text += codepoint_to_unicode(codepoint1) + codepoint_to_unicode(codepoint2);
-        }
-
-        json_text += "\"";
-        SCOPED_TRACE(json_text);
-        EXPECT_NO_THROW(json::parse(json_text));
-    }
-}
-
-#if 0
-// incorrect sequences
-// high surrogate without low surrogate
-TEST(JsonUnicodeCodepointTest, IncorrectHighMissingLow)
-{
-    // D800..DBFF are high surrogates and must be followed by low
-    // surrogates DC00..DFFF; here, nothing follows
-    for (std::size_t cp = 0xD800u; cp <= 0xDBFFu; ++cp)
-    {
-        std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
-        SCOPED_TRACE(json_text);
-        EXPECT_THROW(json::parse(json_text), json::parse_error);
-    }
-}
-
-// high surrogate with wrong low surrogate
-TEST(JsonUnicodeCodepointTest, IncorrectHighWrongLow)
-{
-    // D800..DBFF are high surrogates and must be followed by low
-    // surrogates DC00..DFFF; here a different sequence follows
-    for (std::size_t cp1 = 0xD800u; cp1 <= 0xDBFFu; ++cp1)
-    {
-        for (std::size_t cp2 = 0x0000u; cp2 <= 0xFFFFu; ++cp2)
-        {
-            if (0xDC00u <= cp2 && cp2 <= 0xDFFFu)
-            {
-                continue;
-            }
-
-            std::string json_text = "\"" + codepoint_to_unicode(cp1) + codepoint_to_unicode(cp2) + "\"";
-            SCOPED_TRACE(json_text);
-            EXPECT_THROW(json::parse(json_text), json::parse_error);
-        }
-    }
-}
-
-// low surrogate without high surrogate
-TEST(JsonUnicodeCodepointTest, IncorrectLowMissingHigh)
-{
-    // low surrogates DC00..DFFF must follow high surrogates; here,
-    // they occur alone
-    for (std::size_t cp = 0xDC00u; cp <= 0xDFFFu; ++cp)
-    {
-        std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
-        SCOPED_TRACE(json_text);
-        EXPECT_THROW(json::parse(json_text), json::parse_error);
-    }
-}
-#endif
-
-#if 0
-// read all unicode characters
-TEST(JsonUnicodeTest, ReadAllUnicode)
-{
-    // read a file with all unicode characters stored as single-character
-    // strings in a JSON array
-    std::ifstream f("test/data/json_nlohmann_tests/all_unicode.json");
-    json j;
-    CHECK_NOTHROW(f >> j);
-
-    // the array has 1112064 + 1 elemnts (a terminating "null" value)
-    // Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between
-    // 0xD800 and 0xDFFF.
-    CHECK(j.size() == 1112065);
-
-    SECTION("check JSON Pointers")
-    {
-        for (auto s : j)
-        {
-            // skip non-string JSON values
-            if (not s.is_string())
-            {
-                continue;
-            }
-
-            std::string ptr = s;
-
-            // tilde must be followed by 0 or 1
-            if (ptr == "~")
-            {
-                ptr += "0";
-            }
-
-            // JSON Pointers must begin with "/"
-            ptr = "/" + ptr;
-
-            CHECK_NOTHROW(json::json_pointer("/" + ptr));
-
-            // check escape/unescape roundtrip
-            auto escaped = json::json_pointer::escape(ptr);
-            json::json_pointer::unescape(escaped);
-            CHECK(escaped == ptr);
-        }
-    }
-}
-
-// ignore byte-order-mark
-// in a stream
-TEST(JsonUnicodeTest, IgnoreBOMStream)
-{
-    // read a file with a UTF-8 BOM
-    std::ifstream f("test/data/json_nlohmann_tests/bom.json");
-    json j;
-    EXPECT_NO_THROW(f >> j);
-}
-
-// with an iterator
-TEST(JsonUnicodeTest, IgnoreBOMIterator)
-{
-    std::string i = "\xef\xbb\xbf{\n   \"foo\": true\n}";
-    EXPECT_NO_THROW(json::parse(i.begin(), i.end()));
-}
-#endif
-// error for incomplete/wrong BOM
-TEST(JsonUnicodeTest, WrongBOM)
-{
-    EXPECT_THROW(json::parse("\xef\xbb"), json::parse_error);
-    EXPECT_THROW(json::parse("\xef\xbb\xbb"), json::parse_error);
-}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/leb128Test.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/leb128Test.cpp
index cc537ae..6c78b51 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/leb128Test.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/leb128Test.cpp
@@ -16,7 +16,8 @@
 #include <string>
 #include <string_view>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "wpi/SmallString.h"
 #include "wpi/leb128.h"
 #include "wpi/raw_istream.h"
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/ConvertUTFTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/ConvertUTFTest.cpp
index 762682e..3ea7ab0 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/ConvertUTFTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/ConvertUTFTest.cpp
@@ -26,6 +26,18 @@
   EXPECT_EQ(Expected, std::string{Result});
 }
 
+TEST(ConvertUTFTest, ConvertUTF32LittleEndianToUTF8String) {
+  // Src is the look of disapproval.
+  alignas(UTF32) static const char Src[] =
+      "\xFF\xFE\x00\x00\xA0\x0C\x00\x00\x5F\x00\x00\x00\xA0\x0C\x00\x00";
+  std::span<const char> Ref(Src, sizeof(Src) - 1);
+  std::string Result;
+  bool Success = convertUTF32ToUTF8String(Ref, Result);
+  EXPECT_TRUE(Success);
+  std::string Expected("\xE0\xB2\xA0_\xE0\xB2\xA0");
+  EXPECT_EQ(Expected, Result);
+}
+
 TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) {
   // Src is the look of disapproval.
   alignas(UTF16) static const char Src[] = "\xfe\xff\x0c\xa0\x00_\x0c\xa0";
@@ -37,6 +49,18 @@
   EXPECT_EQ(Expected, std::string{Result});
 }
 
+TEST(ConvertUTFTest, ConvertUTF32BigEndianToUTF8String) {
+  // Src is the look of disapproval.
+  alignas(UTF32) static const char Src[] =
+      "\x00\x00\xFE\xFF\x00\x00\x0C\xA0\x00\x00\x00\x5F\x00\x00\x0C\xA0";
+  std::span<const char> Ref(Src, sizeof(Src) - 1);
+  std::string Result;
+  bool Success = convertUTF32ToUTF8String(Ref, Result);
+  EXPECT_TRUE(Success);
+  std::string Expected("\xE0\xB2\xA0_\xE0\xB2\xA0");
+  EXPECT_EQ(Expected, Result);
+}
+
 TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) {
   // Src is the look of disapproval.
   static const char Src[] = "\xe0\xb2\xa0_\xe0\xb2\xa0";
@@ -58,7 +82,8 @@
 
 TEST(ConvertUTFTest, Empty) {
   SmallString<20> Result;
-  bool Success = convertUTF16ToUTF8String(std::span<const char>(), Result);
+  bool Success =
+      convertUTF16ToUTF8String(std::span<const char>(), Result);
   EXPECT_TRUE(Success);
   EXPECT_TRUE(std::string{Result}.empty());
 }
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/DenseMapTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/DenseMapTest.cpp
index 68c37c0..22e535e 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/DenseMapTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/DenseMapTest.cpp
@@ -11,9 +11,15 @@
 #endif
 
 #include "wpi/DenseMap.h"
+#include "wpi/DenseMapInfo.h"
+#include "wpi/DenseMapInfoVariant.h"
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <map>
 #include <set>
+#include <string_view>
+#include <utility>
+#include <variant>
 
 using namespace wpi;
 
@@ -122,6 +128,7 @@
 
   // Lookup tests
   EXPECT_FALSE(this->Map.count(this->getKey()));
+  EXPECT_FALSE(this->Map.contains(this->getKey()));
   EXPECT_TRUE(this->Map.find(this->getKey()) == this->Map.end());
   EXPECT_EQ(typename TypeParam::mapped_type(),
             this->Map.lookup(this->getKey()));
@@ -153,11 +160,21 @@
 
   // Lookup tests
   EXPECT_TRUE(this->Map.count(this->getKey()));
+  EXPECT_TRUE(this->Map.contains(this->getKey()));
   EXPECT_TRUE(this->Map.find(this->getKey()) == this->Map.begin());
   EXPECT_EQ(this->getValue(), this->Map.lookup(this->getKey()));
   EXPECT_EQ(this->getValue(), this->Map[this->getKey()]);
 }
 
+TYPED_TEST(DenseMapTest, AtTest) {
+  this->Map[this->getKey(0)] = this->getValue(0);
+  this->Map[this->getKey(1)] = this->getValue(1);
+  this->Map[this->getKey(2)] = this->getValue(2);
+  EXPECT_EQ(this->getValue(0), this->Map.at(this->getKey(0)));
+  EXPECT_EQ(this->getValue(1), this->Map.at(this->getKey(1)));
+  EXPECT_EQ(this->getValue(2), this->Map.at(this->getKey(2)));
+}
+
 // Test clear() method
 TYPED_TEST(DenseMapTest, ClearTest) {
   this->Map[this->getKey()] = this->getValue();
@@ -446,6 +463,7 @@
   std::vector<std::pair<int, CountCopyAndMove>> Values;
   // The size is a random value greater than 64 (hardcoded DenseMap min init)
   const int Count = 65;
+  Values.reserve(Count);
   for (int i = 0; i < Count; i++)
     Values.emplace_back(i, CountCopyAndMove());
 
@@ -586,6 +604,15 @@
   EXPECT_TRUE(map.find(0) == map.end());
 }
 
+TEST(DenseMapCustomTest, SmallDenseMapWithNumBucketsNonPowerOf2) {
+  // Is not power of 2.
+  const unsigned NumInitBuckets = 33;
+  // Power of 2 less then NumInitBuckets.
+  constexpr unsigned InlineBuckets = 4;
+  // Constructor should not trigger assert.
+  SmallDenseMap<int, int, InlineBuckets> map(NumInitBuckets);
+}
+
 TEST(DenseMapCustomTest, TryEmplaceTest) {
   DenseMap<int, std::unique_ptr<int>> Map;
   std::unique_ptr<int> P(new int(2));
@@ -644,11 +671,15 @@
 struct B : public A {
   using A::A;
 };
+
+struct AlwaysEqType {
+  bool operator==(const AlwaysEqType &RHS) const { return true; }
+};
 } // namespace
 
 namespace wpi {
 template <typename T>
-struct DenseMapInfo<T, std::enable_if_t<std::is_base_of<A, T>::value>> {
+struct DenseMapInfo<T, std::enable_if_t<std::is_base_of_v<A, T>>> {
   static inline T getEmptyKey() { return {static_cast<int>(~0)}; }
   static inline T getTombstoneKey() { return {static_cast<int>(~0U - 1)}; }
   static unsigned getHashValue(const T &Val) { return Val.value; }
@@ -656,6 +687,16 @@
     return LHS.value == RHS.value;
   }
 };
+
+template <> struct DenseMapInfo<AlwaysEqType> {
+  using T = AlwaysEqType;
+  static inline T getEmptyKey() { return {}; }
+  static inline T getTombstoneKey() { return {}; }
+  static unsigned getHashValue(const T &Val) { return 0; }
+  static bool isEqual(const T &LHS, const T &RHS) {
+    return false;
+  }
+};
 } // namespace wpi
 
 namespace {
@@ -677,4 +718,23 @@
   EXPECT_EQ(Map.find(Keys[1]), Map.end());
   EXPECT_EQ(Map.find(Keys[2]), Map.end());
 }
+
+TEST(DenseMapCustomTest, VariantSupport) {
+  using variant = std::variant<int, int, AlwaysEqType>;
+  DenseMap<variant, int> Map;
+  variant Keys[] = {
+      variant(std::in_place_index<0>, 1),
+      variant(std::in_place_index<1>, 1),
+      variant(std::in_place_index<2>),
+  };
+  Map.try_emplace(Keys[0], 0);
+  Map.try_emplace(Keys[1], 1);
+  EXPECT_THAT(Map, testing::SizeIs(2));
+  EXPECT_NE(DenseMapInfo<variant>::getHashValue(Keys[0]),
+            DenseMapInfo<variant>::getHashValue(Keys[1]));
+  // Check that isEqual dispatches to isEqual of underlying type, and not to
+  // operator==.
+  EXPECT_FALSE(DenseMapInfo<variant>::isEqual(Keys[2], Keys[2]));
+}
+
 } // namespace
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MapVectorTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MapVectorTest.cpp
index 99bda8a..4bfa6b2 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MapVectorTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MapVectorTest.cpp
@@ -94,8 +94,10 @@
   MV.insert(std::make_pair(5, 6));
   ASSERT_EQ(MV.size(), 3u);
 
+  ASSERT_TRUE(MV.contains(1));
   MV.erase(MV.find(1));
   ASSERT_EQ(MV.size(), 2u);
+  ASSERT_FALSE(MV.contains(1));
   ASSERT_EQ(MV.find(1), MV.end());
   ASSERT_EQ(MV[3], 4);
   ASSERT_EQ(MV[5], 6);
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MathExtrasTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MathExtrasTest.cpp
index f392887..eab7ba1 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MathExtrasTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MathExtrasTest.cpp
@@ -13,58 +13,6 @@
 
 namespace {
 
-TEST(MathExtras, countTrailingZeros) {
-  uint8_t Z8 = 0;
-  uint16_t Z16 = 0;
-  uint32_t Z32 = 0;
-  uint64_t Z64 = 0;
-  EXPECT_EQ(8u, countTrailingZeros(Z8));
-  EXPECT_EQ(16u, countTrailingZeros(Z16));
-  EXPECT_EQ(32u, countTrailingZeros(Z32));
-  EXPECT_EQ(64u, countTrailingZeros(Z64));
-
-  uint8_t NZ8 = 42;
-  uint16_t NZ16 = 42;
-  uint32_t NZ32 = 42;
-  uint64_t NZ64 = 42;
-  EXPECT_EQ(1u, countTrailingZeros(NZ8));
-  EXPECT_EQ(1u, countTrailingZeros(NZ16));
-  EXPECT_EQ(1u, countTrailingZeros(NZ32));
-  EXPECT_EQ(1u, countTrailingZeros(NZ64));
-}
-
-TEST(MathExtras, countLeadingZeros) {
-  uint8_t Z8 = 0;
-  uint16_t Z16 = 0;
-  uint32_t Z32 = 0;
-  uint64_t Z64 = 0;
-  EXPECT_EQ(8u, countLeadingZeros(Z8));
-  EXPECT_EQ(16u, countLeadingZeros(Z16));
-  EXPECT_EQ(32u, countLeadingZeros(Z32));
-  EXPECT_EQ(64u, countLeadingZeros(Z64));
-
-  uint8_t NZ8 = 42;
-  uint16_t NZ16 = 42;
-  uint32_t NZ32 = 42;
-  uint64_t NZ64 = 42;
-  EXPECT_EQ(2u, countLeadingZeros(NZ8));
-  EXPECT_EQ(10u, countLeadingZeros(NZ16));
-  EXPECT_EQ(26u, countLeadingZeros(NZ32));
-  EXPECT_EQ(58u, countLeadingZeros(NZ64));
-
-  EXPECT_EQ(8u, countLeadingZeros(0x00F000FFu));
-  EXPECT_EQ(8u, countLeadingZeros(0x00F12345u));
-  for (unsigned i = 0; i <= 30; ++i) {
-    EXPECT_EQ(31 - i, countLeadingZeros(1u << i));
-  }
-
-  EXPECT_EQ(8u, countLeadingZeros(0x00F1234500F12345ULL));
-  EXPECT_EQ(1u, countLeadingZeros(1ULL << 62));
-  for (unsigned i = 0; i <= 62; ++i) {
-    EXPECT_EQ(63 - i, countLeadingZeros(1ULL << i));
-  }
-}
-
 TEST(MathExtras, onesMask) {
   EXPECT_EQ(0U, maskLeadingOnes<uint8_t>(0));
   EXPECT_EQ(0U, maskTrailingOnes<uint8_t>(0));
@@ -90,46 +38,6 @@
   EXPECT_EQ(0xFFFFFFFFFFFF0000ULL, maskLeadingOnes<uint64_t>(48U));
 }
 
-TEST(MathExtras, findFirstSet) {
-  uint8_t Z8 = 0;
-  uint16_t Z16 = 0;
-  uint32_t Z32 = 0;
-  uint64_t Z64 = 0;
-  EXPECT_EQ(0xFFULL, findFirstSet(Z8));
-  EXPECT_EQ(0xFFFFULL, findFirstSet(Z16));
-  EXPECT_EQ(0xFFFFFFFFULL, findFirstSet(Z32));
-  EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, findFirstSet(Z64));
-
-  uint8_t NZ8 = 42;
-  uint16_t NZ16 = 42;
-  uint32_t NZ32 = 42;
-  uint64_t NZ64 = 42;
-  EXPECT_EQ(1u, findFirstSet(NZ8));
-  EXPECT_EQ(1u, findFirstSet(NZ16));
-  EXPECT_EQ(1u, findFirstSet(NZ32));
-  EXPECT_EQ(1u, findFirstSet(NZ64));
-}
-
-TEST(MathExtras, findLastSet) {
-  uint8_t Z8 = 0;
-  uint16_t Z16 = 0;
-  uint32_t Z32 = 0;
-  uint64_t Z64 = 0;
-  EXPECT_EQ(0xFFULL, findLastSet(Z8));
-  EXPECT_EQ(0xFFFFULL, findLastSet(Z16));
-  EXPECT_EQ(0xFFFFFFFFULL, findLastSet(Z32));
-  EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, findLastSet(Z64));
-
-  uint8_t NZ8 = 42;
-  uint16_t NZ16 = 42;
-  uint32_t NZ32 = 42;
-  uint64_t NZ64 = 42;
-  EXPECT_EQ(5u, findLastSet(NZ8));
-  EXPECT_EQ(5u, findLastSet(NZ16));
-  EXPECT_EQ(5u, findLastSet(NZ32));
-  EXPECT_EQ(5u, findLastSet(NZ64));
-}
-
 TEST(MathExtras, isIntN) {
   EXPECT_TRUE(isIntN(16, 32767));
   EXPECT_FALSE(isIntN(16, 32768));
@@ -175,6 +83,44 @@
   EXPECT_EQ(0x5400000000000000ULL, reverseBits(NZ64));
 }
 
+TEST(MathExtras, isShiftedMask_32) {
+  EXPECT_FALSE(isShiftedMask_32(0x01010101));
+  EXPECT_TRUE(isShiftedMask_32(0xf0000000));
+  EXPECT_TRUE(isShiftedMask_32(0xffff0000));
+  EXPECT_TRUE(isShiftedMask_32(0xff << 1));
+
+  unsigned MaskIdx, MaskLen;
+  EXPECT_FALSE(isShiftedMask_32(0x01010101, MaskIdx, MaskLen));
+  EXPECT_TRUE(isShiftedMask_32(0xf0000000, MaskIdx, MaskLen));
+  EXPECT_EQ(28, (int)MaskIdx);
+  EXPECT_EQ(4, (int)MaskLen);
+  EXPECT_TRUE(isShiftedMask_32(0xffff0000, MaskIdx, MaskLen));
+  EXPECT_EQ(16, (int)MaskIdx);
+  EXPECT_EQ(16, (int)MaskLen);
+  EXPECT_TRUE(isShiftedMask_32(0xff << 1, MaskIdx, MaskLen));
+  EXPECT_EQ(1, (int)MaskIdx);
+  EXPECT_EQ(8, (int)MaskLen);
+}
+
+TEST(MathExtras, isShiftedMask_64) {
+  EXPECT_FALSE(isShiftedMask_64(0x0101010101010101ull));
+  EXPECT_TRUE(isShiftedMask_64(0xf000000000000000ull));
+  EXPECT_TRUE(isShiftedMask_64(0xffff000000000000ull));
+  EXPECT_TRUE(isShiftedMask_64(0xffull << 55));
+
+  unsigned MaskIdx, MaskLen;
+  EXPECT_FALSE(isShiftedMask_64(0x0101010101010101ull, MaskIdx, MaskLen));
+  EXPECT_TRUE(isShiftedMask_64(0xf000000000000000ull, MaskIdx, MaskLen));
+  EXPECT_EQ(60, (int)MaskIdx);
+  EXPECT_EQ(4, (int)MaskLen);
+  EXPECT_TRUE(isShiftedMask_64(0xffff000000000000ull, MaskIdx, MaskLen));
+  EXPECT_EQ(48, (int)MaskIdx);
+  EXPECT_EQ(16, (int)MaskLen);
+  EXPECT_TRUE(isShiftedMask_64(0xffull << 55, MaskIdx, MaskLen));
+  EXPECT_EQ(55, (int)MaskIdx);
+  EXPECT_EQ(8, (int)MaskLen);
+}
+
 TEST(MathExtras, isPowerOf2_32) {
   EXPECT_FALSE(isPowerOf2_32(0));
   EXPECT_TRUE(isPowerOf2_32(1 << 6));
@@ -197,12 +143,6 @@
   EXPECT_EQ(8U, PowerOf2Ceil(7U));
 }
 
-TEST(MathExtras, PowerOf2Floor) {
-  EXPECT_EQ(0U, PowerOf2Floor(0U));
-  EXPECT_EQ(8U, PowerOf2Floor(8U));
-  EXPECT_EQ(4U, PowerOf2Floor(7U));
-}
-
 TEST(MathExtras, CTLog2) {
   EXPECT_EQ(CTLog2<1ULL << 0>(), 0U);
   EXPECT_EQ(CTLog2<1ULL << 1>(), 1U);
@@ -222,31 +162,6 @@
   EXPECT_EQ(CTLog2<1ULL << 15>(), 15U);
 }
 
-TEST(MathExtras, countLeadingOnes) {
-  for (int i = 30; i >= 0; --i) {
-    // Start with all ones and unset some bit.
-    EXPECT_EQ(31u - i, countLeadingOnes(0xFFFFFFFF ^ (1 << i)));
-  }
-  for (int i = 62; i >= 0; --i) {
-    // Start with all ones and unset some bit.
-    EXPECT_EQ(63u - i, countLeadingOnes(0xFFFFFFFFFFFFFFFFULL ^ (1LL << i)));
-  }
-  for (int i = 30; i >= 0; --i) {
-    // Start with all ones and unset some bit.
-    EXPECT_EQ(31u - i, countLeadingOnes(0xFFFFFFFF ^ (1 << i)));
-  }
-}
-
-TEST(MathExtras, FloatBits) {
-  static const float kValue = 5632.34f;
-  EXPECT_FLOAT_EQ(kValue, BitsToFloat(FloatToBits(kValue)));
-}
-
-TEST(MathExtras, DoubleBits) {
-  static const double kValue = 87987234.983498;
-  EXPECT_DOUBLE_EQ(kValue, BitsToDouble(DoubleToBits(kValue)));
-}
-
 TEST(MathExtras, MinAlign) {
   EXPECT_EQ(1u, MinAlign(2, 3));
   EXPECT_EQ(2u, MinAlign(2, 4));
@@ -271,9 +186,7 @@
   EXPECT_EQ(552u, alignTo(321, 255, 42));
 }
 
-template<typename T>
-void SaturatingAddTestHelper()
-{
+template <typename T> void SaturatingAddTestHelper() {
   const T Max = std::numeric_limits<T>::max();
   bool ResultOverflowed;
 
@@ -296,6 +209,42 @@
   EXPECT_EQ(Max, SaturatingAdd(Max, Max));
   EXPECT_EQ(Max, SaturatingAdd(Max, Max, &ResultOverflowed));
   EXPECT_TRUE(ResultOverflowed);
+
+  EXPECT_EQ(T(6), SaturatingAdd(T(1), T(2), T(3)));
+  EXPECT_EQ(T(6), SaturatingAdd(T(1), T(2), T(3), &ResultOverflowed));
+  EXPECT_FALSE(ResultOverflowed);
+
+  EXPECT_EQ(T(10), SaturatingAdd(T(1), T(2), T(3), T(4)));
+  EXPECT_EQ(T(10), SaturatingAdd(T(1), T(2), T(3), T(4), &ResultOverflowed));
+  EXPECT_FALSE(ResultOverflowed);
+
+  EXPECT_EQ(Max, SaturatingAdd(Max, T(0), T(0)));
+  EXPECT_EQ(Max, SaturatingAdd(Max, T(0), T(0), &ResultOverflowed));
+  EXPECT_FALSE(ResultOverflowed);
+
+  EXPECT_EQ(Max, SaturatingAdd(T(0), T(0), Max));
+  EXPECT_EQ(Max, SaturatingAdd(T(0), T(0), Max, &ResultOverflowed));
+  EXPECT_FALSE(ResultOverflowed);
+
+  EXPECT_EQ(Max, SaturatingAdd(Max, T(0), T(1)));
+  EXPECT_EQ(Max, SaturatingAdd(Max, T(0), T(1), &ResultOverflowed));
+  EXPECT_TRUE(ResultOverflowed);
+
+  EXPECT_EQ(Max, SaturatingAdd(T(0), T(1), Max));
+  EXPECT_EQ(Max, SaturatingAdd(T(0), T(1), Max, &ResultOverflowed));
+  EXPECT_TRUE(ResultOverflowed);
+
+  EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 2), T(1)));
+  EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 2), T(1), &ResultOverflowed));
+  EXPECT_FALSE(ResultOverflowed);
+
+  EXPECT_EQ(Max, SaturatingAdd(T(1), T(1), T(Max - 2)));
+  EXPECT_EQ(Max, SaturatingAdd(T(1), T(1), T(Max - 2), &ResultOverflowed));
+  EXPECT_FALSE(ResultOverflowed);
+
+  EXPECT_EQ(Max, SaturatingAdd(Max, Max, Max));
+  EXPECT_EQ(Max, SaturatingAdd(Max, Max, Max, &ResultOverflowed));
+  EXPECT_TRUE(ResultOverflowed);
 }
 
 TEST(MathExtras, SaturatingAdd) {
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MoveOnly.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MoveOnly.cpp
new file mode 100644
index 0000000..efbf244
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MoveOnly.cpp
@@ -0,0 +1,15 @@
+//===- llvm/unittest/ADT/MoveOnly.cpp - Optional unit tests ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MoveOnly.h"
+
+using namespace wpi;
+
+unsigned MoveOnly::MoveConstructions = 0;
+unsigned MoveOnly::Destructions = 0;
+unsigned MoveOnly::MoveAssignments = 0;
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MoveOnly.h b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MoveOnly.h
new file mode 100644
index 0000000..b992335
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/MoveOnly.h
@@ -0,0 +1,42 @@
+//===- llvm/unittest/ADT/MoveOnly.h - Optional unit tests -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UNITTESTS_ADT_MOVEONLY_H
+#define LLVM_UNITTESTS_ADT_MOVEONLY_H
+
+namespace wpi {
+
+struct MoveOnly {
+  static unsigned MoveConstructions;
+  static unsigned Destructions;
+  static unsigned MoveAssignments;
+  int val;
+  explicit MoveOnly(int val) : val(val) {
+  }
+  MoveOnly(MoveOnly&& other) {
+    val = other.val;
+    ++MoveConstructions;
+  }
+  MoveOnly &operator=(MoveOnly&& other) {
+    val = other.val;
+    ++MoveAssignments;
+    return *this;
+  }
+  ~MoveOnly() {
+    ++Destructions;
+  }
+  static void ResetCounts() {
+    MoveConstructions = 0;
+    Destructions = 0;
+    MoveAssignments = 0;
+  }
+};
+
+}  // end namespace wpi
+
+#endif // LLVM_UNITTESTS_ADT_MOVEONLY_H
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/PointerIntPairTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/PointerIntPairTest.cpp
index fcca5b6..03e22e1 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/PointerIntPairTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/PointerIntPairTest.cpp
@@ -62,7 +62,11 @@
   EXPECT_EQ(&s, Pair2.getPointer());
   EXPECT_EQ(E::Case3, Pair2.getInt());
 
-  static_assert(std::is_trivially_copyable<PointerIntPair<S *, 2, E>>::value,
+  auto [Pointer2, Int2] = Pair2;
+  EXPECT_EQ(Pair2.getPointer(), Pointer2);
+  EXPECT_EQ(Pair2.getInt(), Int2);
+
+  static_assert(std::is_trivially_copyable_v<PointerIntPair<S *, 2, E>>,
                 "trivially copyable");
 }
 
@@ -100,10 +104,27 @@
   EXPECT_EQ(FixnumPointerTraits::NumLowBitsAvailable - 1,
             (int)PointerLikeTypeTraits<decltype(pair)>::NumLowBitsAvailable);
 
-  static_assert(
-      std::is_trivially_copyable<
-          PointerIntPair<Fixnum31, 1, bool, FixnumPointerTraits>>::value,
-      "trivially copyable");
+  static_assert(std::is_trivially_copyable_v<
+                    PointerIntPair<Fixnum31, 1, bool, FixnumPointerTraits>>,
+                "trivially copyable");
+}
+
+TEST(PointerIntPairTest, TypePunning) {
+  int I = 0;
+  int *IntPtr = &I;
+
+  int **IntPtrBegin = &IntPtr;
+  int **IntPtrEnd = IntPtrBegin + 1;
+
+  PointerIntPair<int *, 1> Pair;
+  int **PairAddr = Pair.getAddrOfPointer();
+
+  while (IntPtrBegin != IntPtrEnd) {
+    *PairAddr = *IntPtrBegin;
+    ++PairAddr;
+    ++IntPtrBegin;
+  }
+  EXPECT_EQ(Pair.getPointer(), IntPtr);
 }
 
 } // end anonymous namespace
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/PointerUnionTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/PointerUnionTest.cpp
index 5a94a45..1bc2033 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/PointerUnionTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/PointerUnionTest.cpp
@@ -89,29 +89,29 @@
 }
 
 TEST_F(PointerUnionTest, Is) {
-  EXPECT_FALSE(a.is<int *>());
-  EXPECT_TRUE(a.is<float *>());
-  EXPECT_TRUE(b.is<int *>());
-  EXPECT_FALSE(b.is<float *>());
-  EXPECT_TRUE(n.is<int *>());
-  EXPECT_FALSE(n.is<float *>());
-  EXPECT_TRUE(i3.is<int *>());
-  EXPECT_TRUE(f3.is<float *>());
-  EXPECT_TRUE(l3.is<long long *>());
-  EXPECT_TRUE(i4.is<int *>());
-  EXPECT_TRUE(f4.is<float *>());
-  EXPECT_TRUE(l4.is<long long *>());
-  EXPECT_TRUE(d4.is<double *>());
-  EXPECT_TRUE(i4null.is<int *>());
-  EXPECT_TRUE(f4null.is<float *>());
-  EXPECT_TRUE(l4null.is<long long *>());
-  EXPECT_TRUE(d4null.is<double *>());
+  EXPECT_FALSE(isa<int *>(a));
+  EXPECT_TRUE(isa<float *>(a));
+  EXPECT_TRUE(isa<int *>(b));
+  EXPECT_FALSE(isa<float *>(b));
+  EXPECT_TRUE(isa<int *>(n));
+  EXPECT_FALSE(isa<float *>(n));
+  EXPECT_TRUE(isa<int *>(i3));
+  EXPECT_TRUE(isa<float *>(f3));
+  EXPECT_TRUE(isa<long long *>(l3));
+  EXPECT_TRUE(isa<int *>(i4));
+  EXPECT_TRUE(isa<float *>(f4));
+  EXPECT_TRUE(isa<long long *>(l4));
+  EXPECT_TRUE(isa<double *>(d4));
+  EXPECT_TRUE(isa<int *>(i4null));
+  EXPECT_TRUE(isa<float *>(f4null));
+  EXPECT_TRUE(isa<long long *>(l4null));
+  EXPECT_TRUE(isa<double *>(d4null));
 }
 
 TEST_F(PointerUnionTest, Get) {
-  EXPECT_EQ(a.get<float *>(), &f);
-  EXPECT_EQ(b.get<int *>(), &i);
-  EXPECT_EQ(n.get<int *>(), (int *)nullptr);
+  EXPECT_EQ(cast<float *>(a), &f);
+  EXPECT_EQ(cast<int *>(b), &i);
+  EXPECT_EQ(cast<int *>(n), (int *)nullptr);
 }
 
 template<int I> struct alignas(8) Aligned {};
@@ -125,27 +125,27 @@
   Aligned<7> a7;
 
   PU8 a = &a0;
-  EXPECT_TRUE(a.is<Aligned<0>*>());
-  EXPECT_FALSE(a.is<Aligned<1>*>());
-  EXPECT_FALSE(a.is<Aligned<2>*>());
-  EXPECT_FALSE(a.is<Aligned<3>*>());
-  EXPECT_FALSE(a.is<Aligned<4>*>());
-  EXPECT_FALSE(a.is<Aligned<5>*>());
-  EXPECT_FALSE(a.is<Aligned<6>*>());
-  EXPECT_FALSE(a.is<Aligned<7>*>());
-  EXPECT_EQ(a.dyn_cast<Aligned<0>*>(), &a0);
+  EXPECT_TRUE(isa<Aligned<0> *>(a));
+  EXPECT_FALSE(isa<Aligned<1> *>(a));
+  EXPECT_FALSE(isa<Aligned<2> *>(a));
+  EXPECT_FALSE(isa<Aligned<3> *>(a));
+  EXPECT_FALSE(isa<Aligned<4> *>(a));
+  EXPECT_FALSE(isa<Aligned<5> *>(a));
+  EXPECT_FALSE(isa<Aligned<6> *>(a));
+  EXPECT_FALSE(isa<Aligned<7> *>(a));
+  EXPECT_EQ(dyn_cast_if_present<Aligned<0> *>(a), &a0);
   EXPECT_EQ(*a.getAddrOfPtr1(), &a0);
 
   a = &a7;
-  EXPECT_FALSE(a.is<Aligned<0>*>());
-  EXPECT_FALSE(a.is<Aligned<1>*>());
-  EXPECT_FALSE(a.is<Aligned<2>*>());
-  EXPECT_FALSE(a.is<Aligned<3>*>());
-  EXPECT_FALSE(a.is<Aligned<4>*>());
-  EXPECT_FALSE(a.is<Aligned<5>*>());
-  EXPECT_FALSE(a.is<Aligned<6>*>());
-  EXPECT_TRUE(a.is<Aligned<7>*>());
-  EXPECT_EQ(a.dyn_cast<Aligned<7>*>(), &a7);
+  EXPECT_FALSE(isa<Aligned<0> *>(a));
+  EXPECT_FALSE(isa<Aligned<1> *>(a));
+  EXPECT_FALSE(isa<Aligned<2> *>(a));
+  EXPECT_FALSE(isa<Aligned<3> *>(a));
+  EXPECT_FALSE(isa<Aligned<4> *>(a));
+  EXPECT_FALSE(isa<Aligned<5> *>(a));
+  EXPECT_FALSE(isa<Aligned<6> *>(a));
+  EXPECT_TRUE(isa<Aligned<7> *>(a));
+  EXPECT_EQ(dyn_cast_if_present<Aligned<7> *>(a), &a7);
 
   EXPECT_TRUE(a == PU8(&a7));
   EXPECT_TRUE(a != PU8(&a0));
@@ -156,4 +156,137 @@
   EXPECT_TRUE((void *)n.getAddrOfPtr1() == (void *)&n);
 }
 
+TEST_F(PointerUnionTest, NewCastInfra) {
+  // test isa<>
+  EXPECT_TRUE(isa<float *>(a));
+  EXPECT_TRUE(isa<int *>(b));
+  EXPECT_TRUE(isa<int *>(c));
+  EXPECT_TRUE(isa<int *>(n));
+  EXPECT_TRUE(isa<int *>(i3));
+  EXPECT_TRUE(isa<float *>(f3));
+  EXPECT_TRUE(isa<long long *>(l3));
+  EXPECT_TRUE(isa<int *>(i4));
+  EXPECT_TRUE(isa<float *>(f4));
+  EXPECT_TRUE(isa<long long *>(l4));
+  EXPECT_TRUE(isa<double *>(d4));
+  EXPECT_TRUE(isa<int *>(i4null));
+  EXPECT_TRUE(isa<float *>(f4null));
+  EXPECT_TRUE(isa<long long *>(l4null));
+  EXPECT_TRUE(isa<double *>(d4null));
+  EXPECT_FALSE(isa<int *>(a));
+  EXPECT_FALSE(isa<float *>(b));
+  EXPECT_FALSE(isa<float *>(c));
+  EXPECT_FALSE(isa<float *>(n));
+  EXPECT_FALSE(isa<float *>(i3));
+  EXPECT_FALSE(isa<long long *>(i3));
+  EXPECT_FALSE(isa<int *>(f3));
+  EXPECT_FALSE(isa<long long *>(f3));
+  EXPECT_FALSE(isa<int *>(l3));
+  EXPECT_FALSE(isa<float *>(l3));
+  EXPECT_FALSE(isa<float *>(i4));
+  EXPECT_FALSE(isa<long long *>(i4));
+  EXPECT_FALSE(isa<double *>(i4));
+  EXPECT_FALSE(isa<int *>(f4));
+  EXPECT_FALSE(isa<long long *>(f4));
+  EXPECT_FALSE(isa<double *>(f4));
+  EXPECT_FALSE(isa<int *>(l4));
+  EXPECT_FALSE(isa<float *>(l4));
+  EXPECT_FALSE(isa<double *>(l4));
+  EXPECT_FALSE(isa<int *>(d4));
+  EXPECT_FALSE(isa<float *>(d4));
+  EXPECT_FALSE(isa<long long *>(d4));
+  EXPECT_FALSE(isa<float *>(i4null));
+  EXPECT_FALSE(isa<long long *>(i4null));
+  EXPECT_FALSE(isa<double *>(i4null));
+  EXPECT_FALSE(isa<int *>(f4null));
+  EXPECT_FALSE(isa<long long *>(f4null));
+  EXPECT_FALSE(isa<double *>(f4null));
+  EXPECT_FALSE(isa<int *>(l4null));
+  EXPECT_FALSE(isa<float *>(l4null));
+  EXPECT_FALSE(isa<double *>(l4null));
+  EXPECT_FALSE(isa<int *>(d4null));
+  EXPECT_FALSE(isa<float *>(d4null));
+  EXPECT_FALSE(isa<long long *>(d4null));
+
+  // test cast<>
+  EXPECT_EQ(cast<float *>(a), &f);
+  EXPECT_EQ(cast<int *>(b), &i);
+  EXPECT_EQ(cast<int *>(c), &i);
+  EXPECT_EQ(cast<int *>(i3), &i);
+  EXPECT_EQ(cast<float *>(f3), &f);
+  EXPECT_EQ(cast<long long *>(l3), &l);
+  EXPECT_EQ(cast<int *>(i4), &i);
+  EXPECT_EQ(cast<float *>(f4), &f);
+  EXPECT_EQ(cast<long long *>(l4), &l);
+  EXPECT_EQ(cast<double *>(d4), &d);
+
+  // test dyn_cast
+  EXPECT_EQ(dyn_cast<int *>(a), nullptr);
+  EXPECT_EQ(dyn_cast<float *>(a), &f);
+  EXPECT_EQ(dyn_cast<int *>(b), &i);
+  EXPECT_EQ(dyn_cast<float *>(b), nullptr);
+  EXPECT_EQ(dyn_cast<int *>(c), &i);
+  EXPECT_EQ(dyn_cast<float *>(c), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<int *>(n), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<float *>(n), nullptr);
+  EXPECT_EQ(dyn_cast<int *>(i3), &i);
+  EXPECT_EQ(dyn_cast<float *>(i3), nullptr);
+  EXPECT_EQ(dyn_cast<long long *>(i3), nullptr);
+  EXPECT_EQ(dyn_cast<int *>(f3), nullptr);
+  EXPECT_EQ(dyn_cast<float *>(f3), &f);
+  EXPECT_EQ(dyn_cast<long long *>(f3), nullptr);
+  EXPECT_EQ(dyn_cast<int *>(l3), nullptr);
+  EXPECT_EQ(dyn_cast<float *>(l3), nullptr);
+  EXPECT_EQ(dyn_cast<long long *>(l3), &l);
+  EXPECT_EQ(dyn_cast<int *>(i4), &i);
+  EXPECT_EQ(dyn_cast<float *>(i4), nullptr);
+  EXPECT_EQ(dyn_cast<long long *>(i4), nullptr);
+  EXPECT_EQ(dyn_cast<double *>(i4), nullptr);
+  EXPECT_EQ(dyn_cast<int *>(f4), nullptr);
+  EXPECT_EQ(dyn_cast<float *>(f4), &f);
+  EXPECT_EQ(dyn_cast<long long *>(f4), nullptr);
+  EXPECT_EQ(dyn_cast<double *>(f4), nullptr);
+  EXPECT_EQ(dyn_cast<int *>(l4), nullptr);
+  EXPECT_EQ(dyn_cast<float *>(l4), nullptr);
+  EXPECT_EQ(dyn_cast<long long *>(l4), &l);
+  EXPECT_EQ(dyn_cast<double *>(l4), nullptr);
+  EXPECT_EQ(dyn_cast<int *>(d4), nullptr);
+  EXPECT_EQ(dyn_cast<float *>(d4), nullptr);
+  EXPECT_EQ(dyn_cast<long long *>(d4), nullptr);
+  EXPECT_EQ(dyn_cast<double *>(d4), &d);
+  EXPECT_EQ(dyn_cast_if_present<int *>(i4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<float *>(i4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<long long *>(i4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<double *>(i4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<int *>(f4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<float *>(f4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<long long *>(f4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<double *>(f4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<int *>(l4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<float *>(l4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<long long *>(l4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<double *>(l4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<int *>(d4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<float *>(d4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<long long *>(d4null), nullptr);
+  EXPECT_EQ(dyn_cast_if_present<double *>(d4null), nullptr);
+
+  // test for const
+  const PU4 constd4(&d);
+  EXPECT_TRUE(isa<double *>(constd4));
+  EXPECT_FALSE(isa<int *>(constd4));
+  EXPECT_EQ(cast<double *>(constd4), &d);
+  EXPECT_EQ(dyn_cast<long long *>(constd4), nullptr);
+
+  auto *result1 = cast<double *>(constd4);
+  static_assert(std::is_same_v<double *, decltype(result1)>,
+                "type mismatch for cast with PointerUnion");
+
+  PointerUnion<int *, const double *> constd2(&d);
+  auto *result2 = cast<const double *>(constd2);
+  EXPECT_EQ(result2, &d);
+  static_assert(std::is_same_v<const double *, decltype(result2)>,
+                "type mismatch for cast with PointerUnion");
+}
+
 } // end anonymous namespace
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/STLForwardCompatTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/STLForwardCompatTest.cpp
index 333359e..153305b 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/STLForwardCompatTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/STLForwardCompatTest.cpp
@@ -7,41 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "wpi/STLForwardCompat.h"
+#include "MoveOnly.h"
 #include "gtest/gtest.h"
 
 namespace {
 
-TEST(STLForwardCompatTest, NegationTest) {
-  EXPECT_TRUE((wpi::negation<std::false_type>::value));
-  EXPECT_FALSE((wpi::negation<std::true_type>::value));
-}
-
-struct incomplete_type;
-
-TEST(STLForwardCompatTest, ConjunctionTest) {
-  EXPECT_TRUE((wpi::conjunction<>::value));
-  EXPECT_FALSE((wpi::conjunction<std::false_type>::value));
-  EXPECT_TRUE((wpi::conjunction<std::true_type>::value));
-  EXPECT_FALSE((wpi::conjunction<std::false_type, incomplete_type>::value));
-  EXPECT_FALSE((wpi::conjunction<std::false_type, std::true_type>::value));
-  EXPECT_FALSE((wpi::conjunction<std::true_type, std::false_type>::value));
-  EXPECT_TRUE((wpi::conjunction<std::true_type, std::true_type>::value));
-  EXPECT_TRUE((wpi::conjunction<std::true_type, std::true_type,
-                                 std::true_type>::value));
-}
-
-TEST(STLForwardCompatTest, DisjunctionTest) {
-  EXPECT_FALSE((wpi::disjunction<>::value));
-  EXPECT_FALSE((wpi::disjunction<std::false_type>::value));
-  EXPECT_TRUE((wpi::disjunction<std::true_type>::value));
-  EXPECT_TRUE((wpi::disjunction<std::true_type, incomplete_type>::value));
-  EXPECT_TRUE((wpi::disjunction<std::false_type, std::true_type>::value));
-  EXPECT_TRUE((wpi::disjunction<std::true_type, std::false_type>::value));
-  EXPECT_TRUE((wpi::disjunction<std::true_type, std::true_type>::value));
-  EXPECT_TRUE((wpi::disjunction<std::true_type, std::true_type,
-                                 std::true_type>::value));
-}
-
 template <typename T>
 class STLForwardCompatRemoveCVRefTest : public ::testing::Test {};
 
@@ -75,4 +45,78 @@
                             wpi::remove_cvref_t<From>>::value));
 }
 
+TEST(TransformTest, TransformStd) {
+  std::optional<int> A;
+
+  std::optional<int> B = wpi::transformOptional(A, [&](int N) { return N + 1; });
+  EXPECT_FALSE(B.has_value());
+
+  A = 3;
+  std::optional<int> C = wpi::transformOptional(A, [&](int N) { return N + 1; });
+  EXPECT_TRUE(C.has_value());
+  EXPECT_EQ(4, *C);
+}
+
+TEST(TransformTest, MoveTransformStd) {
+  using wpi::MoveOnly;
+
+  std::optional<MoveOnly> A;
+
+  MoveOnly::ResetCounts();
+  std::optional<int> B = wpi::transformOptional(
+      std::move(A), [&](const MoveOnly &M) { return M.val + 2; });
+  EXPECT_FALSE(B.has_value());
+  EXPECT_EQ(0u, MoveOnly::MoveConstructions);
+  EXPECT_EQ(0u, MoveOnly::MoveAssignments);
+  EXPECT_EQ(0u, MoveOnly::Destructions);
+
+  A = MoveOnly(5);
+  MoveOnly::ResetCounts();
+  std::optional<int> C = wpi::transformOptional(
+      std::move(A), [&](const MoveOnly &M) { return M.val + 2; });
+  EXPECT_TRUE(C.has_value());
+  EXPECT_EQ(7, *C);
+  EXPECT_EQ(0u, MoveOnly::MoveConstructions);
+  EXPECT_EQ(0u, MoveOnly::MoveAssignments);
+  EXPECT_EQ(0u, MoveOnly::Destructions);
+}
+
+TEST(TransformTest, TransformLlvm) {
+  std::optional<int> A;
+
+  std::optional<int> B =
+      wpi::transformOptional(A, [&](int N) { return N + 1; });
+  EXPECT_FALSE(B.has_value());
+
+  A = 3;
+  std::optional<int> C =
+      wpi::transformOptional(A, [&](int N) { return N + 1; });
+  EXPECT_TRUE(C.has_value());
+  EXPECT_EQ(4, *C);
+}
+
+TEST(TransformTest, MoveTransformLlvm) {
+  using wpi::MoveOnly;
+
+  std::optional<MoveOnly> A;
+
+  MoveOnly::ResetCounts();
+  std::optional<int> B = wpi::transformOptional(
+      std::move(A), [&](const MoveOnly &M) { return M.val + 2; });
+  EXPECT_FALSE(B.has_value());
+  EXPECT_EQ(0u, MoveOnly::MoveConstructions);
+  EXPECT_EQ(0u, MoveOnly::MoveAssignments);
+  EXPECT_EQ(0u, MoveOnly::Destructions);
+
+  A = MoveOnly(5);
+  MoveOnly::ResetCounts();
+  std::optional<int> C = wpi::transformOptional(
+      std::move(A), [&](const MoveOnly &M) { return M.val + 2; });
+  EXPECT_TRUE(C.has_value());
+  EXPECT_EQ(7, *C);
+  EXPECT_EQ(0u, MoveOnly::MoveConstructions);
+  EXPECT_EQ(0u, MoveOnly::MoveAssignments);
+  EXPECT_EQ(0u, MoveOnly::Destructions);
+}
+
 } // namespace
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SmallSetTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SmallSetTest.cpp
index e245aa4..6f35907 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SmallSetTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SmallSetTest.cpp
@@ -12,6 +12,7 @@
 
 #include "wpi/SmallSet.h"
 #include "gtest/gtest.h"
+#include <algorithm>
 #include <string>
 
 using namespace wpi;
@@ -20,11 +21,17 @@
 
   SmallSet<int, 4> s1;
 
-  for (int i = 0; i < 4; i++)
-    s1.insert(i);
+  for (int i = 0; i < 4; i++) {
+    auto InsertResult = s1.insert(i);
+    EXPECT_EQ(*InsertResult.first, i);
+    EXPECT_EQ(InsertResult.second, true);
+  }
 
-  for (int i = 0; i < 4; i++)
-    s1.insert(i);
+  for (int i = 0; i < 4; i++) {
+    auto InsertResult = s1.insert(i);
+    EXPECT_EQ(*InsertResult.first, i);
+    EXPECT_EQ(InsertResult.second, false);
+  }
 
   EXPECT_EQ(4u, s1.size());
 
@@ -37,8 +44,17 @@
 TEST(SmallSetTest, Grow) {
   SmallSet<int, 4> s1;
 
-  for (int i = 0; i < 8; i++)
-    s1.insert(i);
+  for (int i = 0; i < 8; i++) {
+    auto InsertResult = s1.insert(i);
+    EXPECT_EQ(*InsertResult.first, i);
+    EXPECT_EQ(InsertResult.second, true);
+  }
+
+  for (int i = 0; i < 8; i++) {
+    auto InsertResult = s1.insert(i);
+    EXPECT_EQ(*InsertResult.first, i);
+    EXPECT_EQ(InsertResult.second, false);
+  }
 
   EXPECT_EQ(8u, s1.size());
 
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SmallVectorTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SmallVectorTest.cpp
index a60e683..53a941c 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SmallVectorTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SmallVectorTest.cpp
@@ -11,10 +11,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "wpi/SmallVector.h"
-#include <span>
 #include "wpi/Compiler.h"
 #include "gtest/gtest.h"
+#include <array>
 #include <list>
+#include <span>
 #include <stdarg.h>
 
 #if defined(__GNUC__)
@@ -127,14 +128,30 @@
     return numCopyAssignmentCalls;
   }
 
-  friend bool operator==(const Constructable & c0, const Constructable & c1) {
+  friend bool operator==(const Constructable &c0, const Constructable &c1) {
     return c0.getValue() == c1.getValue();
   }
 
-  friend bool LLVM_ATTRIBUTE_UNUSED
-  operator!=(const Constructable & c0, const Constructable & c1) {
+  friend bool LLVM_ATTRIBUTE_UNUSED operator!=(const Constructable &c0,
+                                               const Constructable &c1) {
     return c0.getValue() != c1.getValue();
   }
+
+  friend bool operator<(const Constructable &c0, const Constructable &c1) {
+    return c0.getValue() < c1.getValue();
+  }
+  friend bool LLVM_ATTRIBUTE_UNUSED operator<=(const Constructable &c0,
+                                               const Constructable &c1) {
+    return c0.getValue() <= c1.getValue();
+  }
+  friend bool LLVM_ATTRIBUTE_UNUSED operator>(const Constructable &c0,
+                                              const Constructable &c1) {
+    return c0.getValue() > c1.getValue();
+  }
+  friend bool LLVM_ATTRIBUTE_UNUSED operator>=(const Constructable &c0,
+                                               const Constructable &c1) {
+    return c0.getValue() >= c1.getValue();
+  }
 };
 
 int Constructable::numConstructorCalls;
@@ -159,42 +176,50 @@
   V.resize(42);
 }
 
+TEST(SmallVectorTest, ConstructNonCopyableTest) {
+  SmallVector<NonCopyable, 0> V(42);
+  EXPECT_EQ(V.size(), (size_t)42);
+}
+
+// Assert that v contains the specified values, in order.
+template <typename VectorT>
+void assertValuesInOrder(VectorT &v, size_t size, ...) {
+  EXPECT_EQ(size, v.size());
+
+  va_list ap;
+  va_start(ap, size);
+  for (size_t i = 0; i < size; ++i) {
+    int value = va_arg(ap, int);
+    EXPECT_EQ(value, v[i].getValue());
+  }
+
+  va_end(ap);
+}
+
+template <typename VectorT> void assertEmpty(VectorT &v) {
+  // Size tests
+  EXPECT_EQ(0u, v.size());
+  EXPECT_TRUE(v.empty());
+
+  // Iterator tests
+  EXPECT_TRUE(v.begin() == v.end());
+}
+
+// Generate a sequence of values to initialize the vector.
+template <typename VectorT> void makeSequence(VectorT &v, int start, int end) {
+  for (int i = start; i <= end; ++i) {
+    v.push_back(Constructable(i));
+  }
+}
+
+template <typename T, unsigned N>
+constexpr static unsigned NumBuiltinElts(const SmallVector<T, N> &) {
+  return N;
+}
+
 class SmallVectorTestBase : public testing::Test {
 protected:
   void SetUp() override { Constructable::reset(); }
-
-  template <typename VectorT>
-  void assertEmpty(VectorT & v) {
-    // Size tests
-    EXPECT_EQ(0u, v.size());
-    EXPECT_TRUE(v.empty());
-
-    // Iterator tests
-    EXPECT_TRUE(v.begin() == v.end());
-  }
-
-  // Assert that v contains the specified values, in order.
-  template <typename VectorT>
-  void assertValuesInOrder(VectorT & v, size_t size, ...) {
-    EXPECT_EQ(size, v.size());
-
-    va_list ap;
-    va_start(ap, size);
-    for (size_t i = 0; i < size; ++i) {
-      int value = va_arg(ap, int);
-      EXPECT_EQ(value, v[i].getValue());
-    }
-
-    va_end(ap);
-  }
-
-  // Generate a sequence of values to initialize the vector.
-  template <typename VectorT>
-  void makeSequence(VectorT & v, int start, int end) {
-    for (int i = start; i <= end; ++i) {
-      v.push_back(Constructable(i));
-    }
-  }
 };
 
 // Test fixture class
@@ -217,24 +242,38 @@
 // Constructor test.
 TYPED_TEST(SmallVectorTest, ConstructorNonIterTest) {
   SCOPED_TRACE("ConstructorTest");
-  this->theVector = SmallVector<Constructable, 2>(2, 2);
-  this->assertValuesInOrder(this->theVector, 2u, 2, 2);
+  auto &V = this->theVector;
+  V = SmallVector<Constructable, 2>(2, 2);
+  assertValuesInOrder(V, 2u, 2, 2);
 }
 
 // Constructor test.
 TYPED_TEST(SmallVectorTest, ConstructorIterTest) {
   SCOPED_TRACE("ConstructorTest");
   int arr[] = {1, 2, 3};
-  this->theVector =
-      SmallVector<Constructable, 4>(std::begin(arr), std::end(arr));
-  this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3);
+  auto &V = this->theVector;
+  V = SmallVector<Constructable, 4>(std::begin(arr), std::end(arr));
+  assertValuesInOrder(V, 3u, 1, 2, 3);
+}
+
+// Constructor test.
+TYPED_TEST(SmallVectorTest, ConstructorFromSpanSimpleTest) {
+  SCOPED_TRACE("ConstructorFromSpanSimpleTest");
+  std::array<Constructable, 3> StdArray = {Constructable(1), Constructable(2),
+                                           Constructable(3)};
+  std::span<const Constructable> Array = StdArray;
+  auto &V = this->theVector;
+  V = SmallVector<Constructable, 4>(Array);
+  assertValuesInOrder(V, 3u, 1, 2, 3);
+  ASSERT_EQ(NumBuiltinElts(TypeParam{}), NumBuiltinElts(V));
 }
 
 // New vector test.
 TYPED_TEST(SmallVectorTest, EmptyVectorTest) {
   SCOPED_TRACE("EmptyVectorTest");
-  this->assertEmpty(this->theVector);
-  EXPECT_TRUE(this->theVector.rbegin() == this->theVector.rend());
+  auto &V = this->theVector;
+  assertEmpty(V);
+  EXPECT_TRUE(V.rbegin() == V.rend());
   EXPECT_EQ(0, Constructable::getNumConstructorCalls());
   EXPECT_EQ(0, Constructable::getNumDestructorCalls());
 }
@@ -242,35 +281,35 @@
 // Simple insertions and deletions.
 TYPED_TEST(SmallVectorTest, PushPopTest) {
   SCOPED_TRACE("PushPopTest");
-
+  auto &V = this->theVector;
   // Track whether the vector will potentially have to grow.
-  bool RequiresGrowth = this->theVector.capacity() < 3;
+  bool RequiresGrowth = V.capacity() < 3;
 
   // Push an element
-  this->theVector.push_back(Constructable(1));
+  V.push_back(Constructable(1));
 
   // Size tests
-  this->assertValuesInOrder(this->theVector, 1u, 1);
-  EXPECT_FALSE(this->theVector.begin() == this->theVector.end());
-  EXPECT_FALSE(this->theVector.empty());
+  assertValuesInOrder(V, 1u, 1);
+  EXPECT_FALSE(V.begin() == V.end());
+  EXPECT_FALSE(V.empty());
 
   // Push another element
-  this->theVector.push_back(Constructable(2));
-  this->assertValuesInOrder(this->theVector, 2u, 1, 2);
+  V.push_back(Constructable(2));
+  assertValuesInOrder(V, 2u, 1, 2);
 
   // Insert at beginning. Reserve space to avoid reference invalidation from
-  // this->theVector[1].
-  this->theVector.reserve(this->theVector.size() + 1);
-  this->theVector.insert(this->theVector.begin(), this->theVector[1]);
-  this->assertValuesInOrder(this->theVector, 3u, 2, 1, 2);
+  // V[1].
+  V.reserve(V.size() + 1);
+  V.insert(V.begin(), V[1]);
+  assertValuesInOrder(V, 3u, 2, 1, 2);
 
   // Pop one element
-  this->theVector.pop_back();
-  this->assertValuesInOrder(this->theVector, 2u, 2, 1);
+  V.pop_back();
+  assertValuesInOrder(V, 2u, 2, 1);
 
   // Pop remaining elements
-  this->theVector.pop_back_n(2);
-  this->assertEmpty(this->theVector);
+  V.pop_back_n(2);
+  assertEmpty(V);
 
   // Check number of constructor calls. Should be 2 for each list element,
   // one for the argument to push_back, one for the argument to insert,
@@ -290,12 +329,12 @@
 // Clear test.
 TYPED_TEST(SmallVectorTest, ClearTest) {
   SCOPED_TRACE("ClearTest");
+  auto &V = this->theVector;
+  V.reserve(2);
+  makeSequence(V, 1, 2);
+  V.clear();
 
-  this->theVector.reserve(2);
-  this->makeSequence(this->theVector, 1, 2);
-  this->theVector.clear();
-
-  this->assertEmpty(this->theVector);
+  assertEmpty(V);
   EXPECT_EQ(4, Constructable::getNumConstructorCalls());
   EXPECT_EQ(4, Constructable::getNumDestructorCalls());
 }
@@ -303,12 +342,12 @@
 // Resize smaller test.
 TYPED_TEST(SmallVectorTest, ResizeShrinkTest) {
   SCOPED_TRACE("ResizeShrinkTest");
+  auto &V = this->theVector;
+  V.reserve(3);
+  makeSequence(V, 1, 3);
+  V.resize(1);
 
-  this->theVector.reserve(3);
-  this->makeSequence(this->theVector, 1, 3);
-  this->theVector.resize(1);
-
-  this->assertValuesInOrder(this->theVector, 1u, 1);
+  assertValuesInOrder(V, 1u, 1);
   EXPECT_EQ(6, Constructable::getNumConstructorCalls());
   EXPECT_EQ(5, Constructable::getNumDestructorCalls());
 }
@@ -316,25 +355,25 @@
 // Truncate test.
 TYPED_TEST(SmallVectorTest, TruncateTest) {
   SCOPED_TRACE("TruncateTest");
+  auto &V = this->theVector;
+  V.reserve(3);
+  makeSequence(V, 1, 3);
+  V.truncate(1);
 
-  this->theVector.reserve(3);
-  this->makeSequence(this->theVector, 1, 3);
-  this->theVector.truncate(1);
-
-  this->assertValuesInOrder(this->theVector, 1u, 1);
+  assertValuesInOrder(V, 1u, 1);
   EXPECT_EQ(6, Constructable::getNumConstructorCalls());
   EXPECT_EQ(5, Constructable::getNumDestructorCalls());
 
 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
-  EXPECT_DEATH(this->theVector.truncate(2), "Cannot increase size");
+  EXPECT_DEATH(V.truncate(2), "Cannot increase size");
 #endif
-  this->theVector.truncate(1);
-  this->assertValuesInOrder(this->theVector, 1u, 1);
+  V.truncate(1);
+  assertValuesInOrder(V, 1u, 1);
   EXPECT_EQ(6, Constructable::getNumConstructorCalls());
   EXPECT_EQ(5, Constructable::getNumDestructorCalls());
 
-  this->theVector.truncate(0);
-  this->assertEmpty(this->theVector);
+  V.truncate(0);
+  assertEmpty(V);
   EXPECT_EQ(6, Constructable::getNumConstructorCalls());
   EXPECT_EQ(6, Constructable::getNumDestructorCalls());
 }
@@ -342,20 +381,21 @@
 // Resize bigger test.
 TYPED_TEST(SmallVectorTest, ResizeGrowTest) {
   SCOPED_TRACE("ResizeGrowTest");
-
-  this->theVector.resize(2);
+  auto &V = this->theVector;
+  V.resize(2);
 
   EXPECT_EQ(2, Constructable::getNumConstructorCalls());
   EXPECT_EQ(0, Constructable::getNumDestructorCalls());
-  EXPECT_EQ(2u, this->theVector.size());
+  EXPECT_EQ(2u, V.size());
 }
 
 TYPED_TEST(SmallVectorTest, ResizeWithElementsTest) {
-  this->theVector.resize(2);
+  auto &V = this->theVector;
+  V.resize(2);
 
   Constructable::reset();
 
-  this->theVector.resize(4);
+  V.resize(4);
 
   size_t Ctors = Constructable::getNumConstructorCalls();
   EXPECT_TRUE(Ctors == 2 || Ctors == 4);
@@ -368,9 +408,9 @@
 // Resize with fill value.
 TYPED_TEST(SmallVectorTest, ResizeFillTest) {
   SCOPED_TRACE("ResizeFillTest");
-
-  this->theVector.resize(3, Constructable(77));
-  this->assertValuesInOrder(this->theVector, 3u, 77, 77, 77);
+  auto &V = this->theVector;
+  V.resize(3, Constructable(77));
+  assertValuesInOrder(V, 3u, 77, 77, 77);
 }
 
 TEST(SmallVectorTest, ResizeForOverwrite) {
@@ -401,100 +441,103 @@
 // Overflow past fixed size.
 TYPED_TEST(SmallVectorTest, OverflowTest) {
   SCOPED_TRACE("OverflowTest");
-
+  auto &V = this->theVector;
   // Push more elements than the fixed size.
-  this->makeSequence(this->theVector, 1, 10);
+  makeSequence(V, 1, 10);
 
   // Test size and values.
-  EXPECT_EQ(10u, this->theVector.size());
+  EXPECT_EQ(10u, V.size());
   for (int i = 0; i < 10; ++i) {
-    EXPECT_EQ(i+1, this->theVector[i].getValue());
+    EXPECT_EQ(i + 1, V[i].getValue());
   }
 
   // Now resize back to fixed size.
-  this->theVector.resize(1);
+  V.resize(1);
 
-  this->assertValuesInOrder(this->theVector, 1u, 1);
+  assertValuesInOrder(V, 1u, 1);
 }
 
 // Iteration tests.
 TYPED_TEST(SmallVectorTest, IterationTest) {
-  this->makeSequence(this->theVector, 1, 2);
+  auto &V = this->theVector;
+  makeSequence(V, 1, 2);
 
   // Forward Iteration
-  typename TypeParam::iterator it = this->theVector.begin();
-  EXPECT_TRUE(*it == this->theVector.front());
-  EXPECT_TRUE(*it == this->theVector[0]);
+  typename TypeParam::iterator it = V.begin();
+  EXPECT_TRUE(*it == V.front());
+  EXPECT_TRUE(*it == V[0]);
   EXPECT_EQ(1, it->getValue());
   ++it;
-  EXPECT_TRUE(*it == this->theVector[1]);
-  EXPECT_TRUE(*it == this->theVector.back());
+  EXPECT_TRUE(*it == V[1]);
+  EXPECT_TRUE(*it == V.back());
   EXPECT_EQ(2, it->getValue());
   ++it;
-  EXPECT_TRUE(it == this->theVector.end());
+  EXPECT_TRUE(it == V.end());
   --it;
-  EXPECT_TRUE(*it == this->theVector[1]);
+  EXPECT_TRUE(*it == V[1]);
   EXPECT_EQ(2, it->getValue());
   --it;
-  EXPECT_TRUE(*it == this->theVector[0]);
+  EXPECT_TRUE(*it == V[0]);
   EXPECT_EQ(1, it->getValue());
 
   // Reverse Iteration
-  typename TypeParam::reverse_iterator rit = this->theVector.rbegin();
-  EXPECT_TRUE(*rit == this->theVector[1]);
+  typename TypeParam::reverse_iterator rit = V.rbegin();
+  EXPECT_TRUE(*rit == V[1]);
   EXPECT_EQ(2, rit->getValue());
   ++rit;
-  EXPECT_TRUE(*rit == this->theVector[0]);
+  EXPECT_TRUE(*rit == V[0]);
   EXPECT_EQ(1, rit->getValue());
   ++rit;
-  EXPECT_TRUE(rit == this->theVector.rend());
+  EXPECT_TRUE(rit == V.rend());
   --rit;
-  EXPECT_TRUE(*rit == this->theVector[0]);
+  EXPECT_TRUE(*rit == V[0]);
   EXPECT_EQ(1, rit->getValue());
   --rit;
-  EXPECT_TRUE(*rit == this->theVector[1]);
+  EXPECT_TRUE(*rit == V[1]);
   EXPECT_EQ(2, rit->getValue());
 }
 
 // Swap test.
 TYPED_TEST(SmallVectorTest, SwapTest) {
   SCOPED_TRACE("SwapTest");
+  auto &V = this->theVector;
+  auto &U = this->otherVector;
+  makeSequence(V, 1, 2);
+  std::swap(V, U);
 
-  this->makeSequence(this->theVector, 1, 2);
-  std::swap(this->theVector, this->otherVector);
-
-  this->assertEmpty(this->theVector);
-  this->assertValuesInOrder(this->otherVector, 2u, 1, 2);
+  assertEmpty(V);
+  assertValuesInOrder(U, 2u, 1, 2);
 }
 
 // Append test
 TYPED_TEST(SmallVectorTest, AppendTest) {
   SCOPED_TRACE("AppendTest");
+  auto &V = this->theVector;
+  auto &U = this->otherVector;
+  makeSequence(U, 2, 3);
 
-  this->makeSequence(this->otherVector, 2, 3);
+  V.push_back(Constructable(1));
+  V.append(U.begin(), U.end());
 
-  this->theVector.push_back(Constructable(1));
-  this->theVector.append(this->otherVector.begin(), this->otherVector.end());
-
-  this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3);
+  assertValuesInOrder(V, 3u, 1, 2, 3);
 }
 
 // Append repeated test
 TYPED_TEST(SmallVectorTest, AppendRepeatedTest) {
   SCOPED_TRACE("AppendRepeatedTest");
-
-  this->theVector.push_back(Constructable(1));
-  this->theVector.append(2, Constructable(77));
-  this->assertValuesInOrder(this->theVector, 3u, 1, 77, 77);
+  auto &V = this->theVector;
+  V.push_back(Constructable(1));
+  V.append(2, Constructable(77));
+  assertValuesInOrder(V, 3u, 1, 77, 77);
 }
 
 // Append test
 TYPED_TEST(SmallVectorTest, AppendNonIterTest) {
   SCOPED_TRACE("AppendRepeatedTest");
-
-  this->theVector.push_back(Constructable(1));
-  this->theVector.append(2, 7);
-  this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7);
+  auto &V = this->theVector;
+  V.push_back(Constructable(1));
+  V.append(2, 7);
+  assertValuesInOrder(V, 3u, 1, 7, 7);
 }
 
 struct output_iterator {
@@ -509,84 +552,85 @@
 
 TYPED_TEST(SmallVectorTest, AppendRepeatedNonForwardIterator) {
   SCOPED_TRACE("AppendRepeatedTest");
-
-  this->theVector.push_back(Constructable(1));
-  this->theVector.append(output_iterator(), output_iterator());
-  this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7);
+  auto &V = this->theVector;
+  V.push_back(Constructable(1));
+  V.append(output_iterator(), output_iterator());
+  assertValuesInOrder(V, 3u, 1, 7, 7);
 }
 
 TYPED_TEST(SmallVectorTest, AppendSmallVector) {
   SCOPED_TRACE("AppendSmallVector");
-
+  auto &V = this->theVector;
   SmallVector<Constructable, 3> otherVector = {7, 7};
-  this->theVector.push_back(Constructable(1));
-  this->theVector.append(otherVector);
-  this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7);
+  V.push_back(Constructable(1));
+  V.append(otherVector);
+  assertValuesInOrder(V, 3u, 1, 7, 7);
 }
 
 // Assign test
 TYPED_TEST(SmallVectorTest, AssignTest) {
   SCOPED_TRACE("AssignTest");
-
-  this->theVector.push_back(Constructable(1));
-  this->theVector.assign(2, Constructable(77));
-  this->assertValuesInOrder(this->theVector, 2u, 77, 77);
+  auto &V = this->theVector;
+  V.push_back(Constructable(1));
+  V.assign(2, Constructable(77));
+  assertValuesInOrder(V, 2u, 77, 77);
 }
 
 // Assign test
 TYPED_TEST(SmallVectorTest, AssignRangeTest) {
   SCOPED_TRACE("AssignTest");
-
-  this->theVector.push_back(Constructable(1));
+  auto &V = this->theVector;
+  V.push_back(Constructable(1));
   int arr[] = {1, 2, 3};
-  this->theVector.assign(std::begin(arr), std::end(arr));
-  this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3);
+  V.assign(std::begin(arr), std::end(arr));
+  assertValuesInOrder(V, 3u, 1, 2, 3);
 }
 
 // Assign test
 TYPED_TEST(SmallVectorTest, AssignNonIterTest) {
   SCOPED_TRACE("AssignTest");
-
-  this->theVector.push_back(Constructable(1));
-  this->theVector.assign(2, 7);
-  this->assertValuesInOrder(this->theVector, 2u, 7, 7);
+  auto &V = this->theVector;
+  V.push_back(Constructable(1));
+  V.assign(2, 7);
+  assertValuesInOrder(V, 2u, 7, 7);
 }
 
 TYPED_TEST(SmallVectorTest, AssignSmallVector) {
   SCOPED_TRACE("AssignSmallVector");
-
+  auto &V = this->theVector;
   SmallVector<Constructable, 3> otherVector = {7, 7};
-  this->theVector.push_back(Constructable(1));
-  this->theVector.assign(otherVector);
-  this->assertValuesInOrder(this->theVector, 2u, 7, 7);
+  V.push_back(Constructable(1));
+  V.assign(otherVector);
+  assertValuesInOrder(V, 2u, 7, 7);
 }
 
 // Move-assign test
 TYPED_TEST(SmallVectorTest, MoveAssignTest) {
   SCOPED_TRACE("MoveAssignTest");
-
+  auto &V = this->theVector;
+  auto &U = this->otherVector;
   // Set up our vector with a single element, but enough capacity for 4.
-  this->theVector.reserve(4);
-  this->theVector.push_back(Constructable(1));
-  
+  V.reserve(4);
+  V.push_back(Constructable(1));
+
   // Set up the other vector with 2 elements.
-  this->otherVector.push_back(Constructable(2));
-  this->otherVector.push_back(Constructable(3));
+  U.push_back(Constructable(2));
+  U.push_back(Constructable(3));
 
   // Move-assign from the other vector.
-  this->theVector = std::move(this->otherVector);
+  V = std::move(U);
 
   // Make sure we have the right result.
-  this->assertValuesInOrder(this->theVector, 2u, 2, 3);
+  assertValuesInOrder(V, 2u, 2, 3);
 
   // Make sure the # of constructor/destructor calls line up. There
   // are two live objects after clearing the other vector.
-  this->otherVector.clear();
+  U.clear();
   EXPECT_EQ(Constructable::getNumConstructorCalls()-2, 
             Constructable::getNumDestructorCalls());
 
   // There shouldn't be any live objects any more.
-  this->theVector.clear();
+  V.clear();
   EXPECT_EQ(Constructable::getNumConstructorCalls(), 
             Constructable::getNumDestructorCalls());
 }
@@ -594,54 +638,51 @@
 // Erase a single element
 TYPED_TEST(SmallVectorTest, EraseTest) {
   SCOPED_TRACE("EraseTest");
-
-  this->makeSequence(this->theVector, 1, 3);
-  const auto &theConstVector = this->theVector;
-  this->theVector.erase(theConstVector.begin());
-  this->assertValuesInOrder(this->theVector, 2u, 2, 3);
+  auto &V = this->theVector;
+  makeSequence(V, 1, 3);
+  const auto &theConstVector = V;
+  V.erase(theConstVector.begin());
+  assertValuesInOrder(V, 2u, 2, 3);
 }
 
 // Erase a range of elements
 TYPED_TEST(SmallVectorTest, EraseRangeTest) {
   SCOPED_TRACE("EraseRangeTest");
-
-  this->makeSequence(this->theVector, 1, 3);
-  const auto &theConstVector = this->theVector;
-  this->theVector.erase(theConstVector.begin(), theConstVector.begin() + 2);
-  this->assertValuesInOrder(this->theVector, 1u, 3);
+  auto &V = this->theVector;
+  makeSequence(V, 1, 3);
+  const auto &theConstVector = V;
+  V.erase(theConstVector.begin(), theConstVector.begin() + 2);
+  assertValuesInOrder(V, 1u, 3);
 }
 
 // Insert a single element.
 TYPED_TEST(SmallVectorTest, InsertTest) {
   SCOPED_TRACE("InsertTest");
-
-  this->makeSequence(this->theVector, 1, 3);
-  typename TypeParam::iterator I =
-    this->theVector.insert(this->theVector.begin() + 1, Constructable(77));
-  EXPECT_EQ(this->theVector.begin() + 1, I);
-  this->assertValuesInOrder(this->theVector, 4u, 1, 77, 2, 3);
+  auto &V = this->theVector;
+  makeSequence(V, 1, 3);
+  typename TypeParam::iterator I = V.insert(V.begin() + 1, Constructable(77));
+  EXPECT_EQ(V.begin() + 1, I);
+  assertValuesInOrder(V, 4u, 1, 77, 2, 3);
 }
 
 // Insert a copy of a single element.
 TYPED_TEST(SmallVectorTest, InsertCopy) {
   SCOPED_TRACE("InsertTest");
-
-  this->makeSequence(this->theVector, 1, 3);
+  auto &V = this->theVector;
+  makeSequence(V, 1, 3);
   Constructable C(77);
-  typename TypeParam::iterator I =
-      this->theVector.insert(this->theVector.begin() + 1, C);
-  EXPECT_EQ(this->theVector.begin() + 1, I);
-  this->assertValuesInOrder(this->theVector, 4u, 1, 77, 2, 3);
+  typename TypeParam::iterator I = V.insert(V.begin() + 1, C);
+  EXPECT_EQ(V.begin() + 1, I);
+  assertValuesInOrder(V, 4u, 1, 77, 2, 3);
 }
 
 // Insert repeated elements.
 TYPED_TEST(SmallVectorTest, InsertRepeatedTest) {
   SCOPED_TRACE("InsertRepeatedTest");
-
-  this->makeSequence(this->theVector, 1, 4);
+  auto &V = this->theVector;
+  makeSequence(V, 1, 4);
   Constructable::reset();
-  auto I =
-      this->theVector.insert(this->theVector.begin() + 1, 2, Constructable(16));
+  auto I = V.insert(V.begin() + 1, 2, Constructable(16));
   // Move construct the top element into newly allocated space, and optionally
   // reallocate the whole buffer, move constructing into it.
   // FIXME: This is inefficient, we shouldn't move things into newly allocated
@@ -655,26 +696,26 @@
   EXPECT_EQ(2, Constructable::getNumCopyAssignmentCalls());
   // All without any copy construction.
   EXPECT_EQ(0, Constructable::getNumCopyConstructorCalls());
-  EXPECT_EQ(this->theVector.begin() + 1, I);
-  this->assertValuesInOrder(this->theVector, 6u, 1, 16, 16, 2, 3, 4);
+  EXPECT_EQ(V.begin() + 1, I);
+  assertValuesInOrder(V, 6u, 1, 16, 16, 2, 3, 4);
 }
 
 TYPED_TEST(SmallVectorTest, InsertRepeatedNonIterTest) {
   SCOPED_TRACE("InsertRepeatedTest");
-
-  this->makeSequence(this->theVector, 1, 4);
+  auto &V = this->theVector;
+  makeSequence(V, 1, 4);
   Constructable::reset();
-  auto I = this->theVector.insert(this->theVector.begin() + 1, 2, 7);
-  EXPECT_EQ(this->theVector.begin() + 1, I);
-  this->assertValuesInOrder(this->theVector, 6u, 1, 7, 7, 2, 3, 4);
+  auto I = V.insert(V.begin() + 1, 2, 7);
+  EXPECT_EQ(V.begin() + 1, I);
+  assertValuesInOrder(V, 6u, 1, 7, 7, 2, 3, 4);
 }
 
 TYPED_TEST(SmallVectorTest, InsertRepeatedAtEndTest) {
   SCOPED_TRACE("InsertRepeatedTest");
-
-  this->makeSequence(this->theVector, 1, 4);
+  auto &V = this->theVector;
+  makeSequence(V, 1, 4);
   Constructable::reset();
-  auto I = this->theVector.insert(this->theVector.end(), 2, Constructable(16));
+  auto I = V.insert(V.end(), 2, Constructable(16));
   // Just copy construct them into newly allocated space
   EXPECT_EQ(2, Constructable::getNumCopyConstructorCalls());
   // Move everything across if reallocation is needed.
@@ -684,34 +725,30 @@
   EXPECT_EQ(0, Constructable::getNumCopyAssignmentCalls());
   EXPECT_EQ(0, Constructable::getNumMoveAssignmentCalls());
 
-  EXPECT_EQ(this->theVector.begin() + 4, I);
-  this->assertValuesInOrder(this->theVector, 6u, 1, 2, 3, 4, 16, 16);
+  EXPECT_EQ(V.begin() + 4, I);
+  assertValuesInOrder(V, 6u, 1, 2, 3, 4, 16, 16);
 }
 
 TYPED_TEST(SmallVectorTest, InsertRepeatedEmptyTest) {
   SCOPED_TRACE("InsertRepeatedTest");
-
-  this->makeSequence(this->theVector, 10, 15);
+  auto &V = this->theVector;
+  makeSequence(V, 10, 15);
 
   // Empty insert.
-  EXPECT_EQ(this->theVector.end(),
-            this->theVector.insert(this->theVector.end(),
-                                   0, Constructable(42)));
-  EXPECT_EQ(this->theVector.begin() + 1,
-            this->theVector.insert(this->theVector.begin() + 1,
-                                   0, Constructable(42)));
+  EXPECT_EQ(V.end(), V.insert(V.end(), 0, Constructable(42)));
+  EXPECT_EQ(V.begin() + 1, V.insert(V.begin() + 1, 0, Constructable(42)));
 }
 
 // Insert range.
 TYPED_TEST(SmallVectorTest, InsertRangeTest) {
   SCOPED_TRACE("InsertRangeTest");
-
+  auto &V = this->theVector;
   Constructable Arr[3] =
     { Constructable(77), Constructable(77), Constructable(77) };
 
-  this->makeSequence(this->theVector, 1, 3);
+  makeSequence(V, 1, 3);
   Constructable::reset();
-  auto I = this->theVector.insert(this->theVector.begin() + 1, Arr, Arr + 3);
+  auto I = V.insert(V.begin() + 1, Arr, Arr + 3);
   // Move construct the top 3 elements into newly allocated space.
   // Possibly move the whole sequence into new space first.
   // FIXME: This is inefficient, we shouldn't move things into newly allocated
@@ -723,22 +760,22 @@
   EXPECT_EQ(2, Constructable::getNumCopyAssignmentCalls());
   // Copy construct the third element into newly allocated space.
   EXPECT_EQ(1, Constructable::getNumCopyConstructorCalls());
-  EXPECT_EQ(this->theVector.begin() + 1, I);
-  this->assertValuesInOrder(this->theVector, 6u, 1, 77, 77, 77, 2, 3);
+  EXPECT_EQ(V.begin() + 1, I);
+  assertValuesInOrder(V, 6u, 1, 77, 77, 77, 2, 3);
 }
 
 
 TYPED_TEST(SmallVectorTest, InsertRangeAtEndTest) {
   SCOPED_TRACE("InsertRangeTest");
-
+  auto &V = this->theVector;
   Constructable Arr[3] =
     { Constructable(77), Constructable(77), Constructable(77) };
 
-  this->makeSequence(this->theVector, 1, 3);
+  makeSequence(V, 1, 3);
 
   // Insert at end.
   Constructable::reset();
-  auto I = this->theVector.insert(this->theVector.end(), Arr, Arr+3);
+  auto I = V.insert(V.end(), Arr, Arr + 3);
   // Copy construct the 3 elements into new space at the top.
   EXPECT_EQ(3, Constructable::getNumCopyConstructorCalls());
   // Don't copy/move anything else.
@@ -748,42 +785,67 @@
   EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 0 ||
               Constructable::getNumMoveConstructorCalls() == 3);
   EXPECT_EQ(0, Constructable::getNumMoveAssignmentCalls());
-  EXPECT_EQ(this->theVector.begin() + 3, I);
-  this->assertValuesInOrder(this->theVector, 6u,
-                            1, 2, 3, 77, 77, 77);
+  EXPECT_EQ(V.begin() + 3, I);
+  assertValuesInOrder(V, 6u, 1, 2, 3, 77, 77, 77);
 }
 
 TYPED_TEST(SmallVectorTest, InsertEmptyRangeTest) {
   SCOPED_TRACE("InsertRangeTest");
-
-  this->makeSequence(this->theVector, 1, 3);
+  auto &V = this->theVector;
+  makeSequence(V, 1, 3);
 
   // Empty insert.
-  EXPECT_EQ(this->theVector.end(),
-            this->theVector.insert(this->theVector.end(),
-                                   this->theVector.begin(),
-                                   this->theVector.begin()));
-  EXPECT_EQ(this->theVector.begin() + 1,
-            this->theVector.insert(this->theVector.begin() + 1,
-                                   this->theVector.begin(),
-                                   this->theVector.begin()));
+  EXPECT_EQ(V.end(), V.insert(V.end(), V.begin(), V.begin()));
+  EXPECT_EQ(V.begin() + 1, V.insert(V.begin() + 1, V.begin(), V.begin()));
 }
 
 // Comparison tests.
-TYPED_TEST(SmallVectorTest, ComparisonTest) {
-  SCOPED_TRACE("ComparisonTest");
+TYPED_TEST(SmallVectorTest, ComparisonEqualityTest) {
+  SCOPED_TRACE("ComparisonEqualityTest");
+  auto &V = this->theVector;
+  auto &U = this->otherVector;
+  makeSequence(V, 1, 3);
+  makeSequence(U, 1, 3);
 
-  this->makeSequence(this->theVector, 1, 3);
-  this->makeSequence(this->otherVector, 1, 3);
+  EXPECT_TRUE(V == U);
+  EXPECT_FALSE(V != U);
 
-  EXPECT_TRUE(this->theVector == this->otherVector);
-  EXPECT_FALSE(this->theVector != this->otherVector);
+  U.clear();
+  makeSequence(U, 2, 4);
 
-  this->otherVector.clear();
-  this->makeSequence(this->otherVector, 2, 4);
+  EXPECT_FALSE(V == U);
+  EXPECT_TRUE(V != U);
+}
 
-  EXPECT_FALSE(this->theVector == this->otherVector);
-  EXPECT_TRUE(this->theVector != this->otherVector);
+// Comparison tests.
+TYPED_TEST(SmallVectorTest, ComparisonLessThanTest) {
+  SCOPED_TRACE("ComparisonLessThanTest");
+  auto &V = this->theVector;
+  auto &U = this->otherVector;
+  V = {1, 2, 4};
+  U = {1, 4};
+
+  EXPECT_TRUE(V < U);
+  EXPECT_TRUE(V <= U);
+  EXPECT_FALSE(V > U);
+  EXPECT_FALSE(V >= U);
+
+  EXPECT_FALSE(U < V);
+  EXPECT_FALSE(U <= V);
+  EXPECT_TRUE(U > V);
+  EXPECT_TRUE(U >= V);
+
+  U = {1, 2, 4};
+
+  EXPECT_FALSE(V < U);
+  EXPECT_TRUE(V <= U);
+  EXPECT_FALSE(V > U);
+  EXPECT_TRUE(V >= U);
+
+  EXPECT_FALSE(U < V);
+  EXPECT_TRUE(U <= V);
+  EXPECT_FALSE(U > V);
+  EXPECT_TRUE(U >= V);
 }
 
 // Constant vector tests.
@@ -797,25 +859,27 @@
 
 // Direct array access.
 TYPED_TEST(SmallVectorTest, DirectVectorTest) {
-  EXPECT_EQ(0u, this->theVector.size());
-  this->theVector.reserve(4);
-  EXPECT_LE(4u, this->theVector.capacity());
+  auto &V = this->theVector;
+  EXPECT_EQ(0u, V.size());
+  V.reserve(4);
+  EXPECT_LE(4u, V.capacity());
   EXPECT_EQ(0, Constructable::getNumConstructorCalls());
-  this->theVector.push_back(1);
-  this->theVector.push_back(2);
-  this->theVector.push_back(3);
-  this->theVector.push_back(4);
-  EXPECT_EQ(4u, this->theVector.size());
+  V.push_back(1);
+  V.push_back(2);
+  V.push_back(3);
+  V.push_back(4);
+  EXPECT_EQ(4u, V.size());
   EXPECT_EQ(8, Constructable::getNumConstructorCalls());
-  EXPECT_EQ(1, this->theVector[0].getValue());
-  EXPECT_EQ(2, this->theVector[1].getValue());
-  EXPECT_EQ(3, this->theVector[2].getValue());
-  EXPECT_EQ(4, this->theVector[3].getValue());
+  EXPECT_EQ(1, V[0].getValue());
+  EXPECT_EQ(2, V[1].getValue());
+  EXPECT_EQ(3, V[2].getValue());
+  EXPECT_EQ(4, V[3].getValue());
 }
 
 TYPED_TEST(SmallVectorTest, IteratorTest) {
+  auto &V = this->theVector;
   std::list<int> L;
-  this->theVector.insert(this->theVector.end(), L.begin(), L.end());
+  V.insert(V.end(), L.begin(), L.end());
 }
 
 template <typename InvalidType> class DualSmallVectorsTest;
@@ -825,9 +889,6 @@
 protected:
   VectorT1 theVector;
   VectorT2 otherVector;
-
-  template <typename T, unsigned N>
-  static unsigned NumBuiltinElts(const SmallVector<T, N>&) { return N; }
 };
 
 typedef ::testing::Types<
@@ -845,33 +906,32 @@
 
 TYPED_TEST(DualSmallVectorsTest, MoveAssignment) {
   SCOPED_TRACE("MoveAssignTest-DualVectorTypes");
-
+  auto &V = this->theVector;
+  auto &U = this->otherVector;
   // Set up our vector with four elements.
   for (unsigned I = 0; I < 4; ++I)
-    this->otherVector.push_back(Constructable(I));
+    U.push_back(Constructable(I));
 
-  const Constructable *OrigDataPtr = this->otherVector.data();
+  const Constructable *OrigDataPtr = U.data();
 
   // Move-assign from the other vector.
-  this->theVector =
-    std::move(static_cast<SmallVectorImpl<Constructable>&>(this->otherVector));
+  V = std::move(static_cast<SmallVectorImpl<Constructable> &>(U));
 
   // Make sure we have the right result.
-  this->assertValuesInOrder(this->theVector, 4u, 0, 1, 2, 3);
+  assertValuesInOrder(V, 4u, 0, 1, 2, 3);
 
   // Make sure the # of constructor/destructor calls line up. There
   // are two live objects after clearing the other vector.
-  this->otherVector.clear();
+  U.clear();
   EXPECT_EQ(Constructable::getNumConstructorCalls()-4,
             Constructable::getNumDestructorCalls());
 
   // If the source vector (otherVector) was in small-mode, assert that we just
   // moved the data pointer over.
-  EXPECT_TRUE(this->NumBuiltinElts(this->otherVector) == 4 ||
-              this->theVector.data() == OrigDataPtr);
+  EXPECT_TRUE(NumBuiltinElts(U) == 4 || V.data() == OrigDataPtr);
 
   // There shouldn't be any live objects any more.
-  this->theVector.clear();
+  V.clear();
   EXPECT_EQ(Constructable::getNumConstructorCalls(),
             Constructable::getNumDestructorCalls());
 
@@ -1074,6 +1134,83 @@
   EXPECT_EQ(NestedV[0][0][0], 42);
 }
 
+TEST(SmallVectorTest, ToVector) {
+  {
+    std::vector<char> v = {'a', 'b', 'c'};
+    auto Vector = to_vector<4>(v);
+    static_assert(NumBuiltinElts(Vector) == 4u);
+    ASSERT_EQ(3u, Vector.size());
+    for (size_t I = 0; I < v.size(); ++I)
+      EXPECT_EQ(v[I], Vector[I]);
+  }
+  {
+    std::vector<char> v = {'a', 'b', 'c'};
+    auto Vector = to_vector(v);
+    static_assert(NumBuiltinElts(Vector) != 4u);
+    ASSERT_EQ(3u, Vector.size());
+    for (size_t I = 0; I < v.size(); ++I)
+      EXPECT_EQ(v[I], Vector[I]);
+  }
+}
+
+struct To {
+  int Content;
+  friend bool operator==(const To &LHS, const To &RHS) {
+    return LHS.Content == RHS.Content;
+  }
+};
+
+class From {
+public:
+  From() = default;
+  From(To M) { T = M; }
+  operator To() const { return T; }
+
+private:
+  To T;
+};
+
+TEST(SmallVectorTest, ConstructFromSpanOfConvertibleType) {
+  To to1{1}, to2{2}, to3{3};
+  std::vector<From> StdVector = {From(to1), From(to2), From(to3)};
+  std::span<const From> Array = StdVector;
+  {
+    wpi::SmallVector<To> Vector(Array);
+
+    ASSERT_EQ(Array.size(), Vector.size());
+    for (size_t I = 0; I < Array.size(); ++I)
+      EXPECT_EQ(Array[I], Vector[I]);
+  }
+  {
+    wpi::SmallVector<To, 4> Vector(Array);
+
+    ASSERT_EQ(Array.size(), Vector.size());
+    ASSERT_EQ(4u, NumBuiltinElts(Vector));
+    for (size_t I = 0; I < Array.size(); ++I)
+      EXPECT_EQ(Array[I], Vector[I]);
+  }
+}
+
+TEST(SmallVectorTest, ToVectorOf) {
+  To to1{1}, to2{2}, to3{3};
+  std::vector<From> StdVector = {From(to1), From(to2), From(to3)};
+  {
+    wpi::SmallVector<To> Vector = wpi::to_vector_of<To>(StdVector);
+
+    ASSERT_EQ(StdVector.size(), Vector.size());
+    for (size_t I = 0; I < StdVector.size(); ++I)
+      EXPECT_EQ(StdVector[I], Vector[I]);
+  }
+  {
+    auto Vector = wpi::to_vector_of<To, 4>(StdVector);
+
+    ASSERT_EQ(StdVector.size(), Vector.size());
+    static_assert(NumBuiltinElts(Vector) == 4u);
+    for (size_t I = 0; I < StdVector.size(); ++I)
+      EXPECT_EQ(StdVector[I], Vector[I]);
+  }
+}
+
 template <class VectorT>
 class SmallVectorReferenceInvalidationTest : public SmallVectorTestBase {
 protected:
@@ -1083,13 +1220,8 @@
 
   VectorT V;
 
-  template <typename T, unsigned N>
-  static unsigned NumBuiltinElts(const SmallVector<T, N> &) {
-    return N;
-  }
-
   template <class T> static bool isValueType() {
-    return std::is_same<T, typename VectorT::value_type>::value;
+    return std::is_same_v<T, typename VectorT::value_type>;
   }
 
   void SetUp() override {
@@ -1113,7 +1245,7 @@
 TYPED_TEST(SmallVectorReferenceInvalidationTest, PushBack) {
   // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.
   auto &V = this->V;
-  int N = this->NumBuiltinElts(V);
+  int N = NumBuiltinElts(V);
 
   // Push back a reference to last element when growing from small storage.
   V.push_back(V.back());
@@ -1135,7 +1267,7 @@
 TYPED_TEST(SmallVectorReferenceInvalidationTest, PushBackMoved) {
   // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.
   auto &V = this->V;
-  int N = this->NumBuiltinElts(V);
+  int N = NumBuiltinElts(V);
 
   // Push back a reference to last element when growing from small storage.
   V.push_back(std::move(V.back()));
@@ -1164,7 +1296,7 @@
 TYPED_TEST(SmallVectorReferenceInvalidationTest, Resize) {
   auto &V = this->V;
   (void)V;
-  int N = this->NumBuiltinElts(V);
+  int N = NumBuiltinElts(V);
   V.resize(N + 1, V.back());
   EXPECT_EQ(N, V.back());
 
@@ -1179,7 +1311,7 @@
   auto &V = this->V;
   (void)V;
   V.append(1, V.back());
-  int N = this->NumBuiltinElts(V);
+  int N = NumBuiltinElts(V);
   EXPECT_EQ(N, V[N - 1]);
 
   // Append enough more elements that V will grow again. This tests growing
@@ -1197,7 +1329,7 @@
 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
   EXPECT_DEATH(V.append(V.begin(), V.begin() + 1), this->AssertionMessage);
 
-  ASSERT_EQ(3u, this->NumBuiltinElts(V));
+  ASSERT_EQ(3u, NumBuiltinElts(V));
   ASSERT_EQ(3u, V.size());
   V.pop_back();
   ASSERT_EQ(2u, V.size());
@@ -1212,7 +1344,7 @@
   // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.
   auto &V = this->V;
   (void)V;
-  int N = this->NumBuiltinElts(V);
+  int N = NumBuiltinElts(V);
   ASSERT_EQ(unsigned(N), V.size());
   ASSERT_EQ(unsigned(N), V.capacity());
 
@@ -1312,7 +1444,7 @@
 
   // Cover NumToInsert <= this->end() - I.
   V.insert(V.begin() + 1, 1, V.back());
-  int N = this->NumBuiltinElts(V);
+  int N = NumBuiltinElts(V);
   EXPECT_EQ(N, V[1]);
 
   // Cover NumToInsert > this->end() - I, inserting enough elements that V will
@@ -1332,7 +1464,7 @@
   EXPECT_DEATH(V.insert(V.begin(), V.begin(), V.begin() + 1),
                this->AssertionMessage);
 
-  ASSERT_EQ(3u, this->NumBuiltinElts(V));
+  ASSERT_EQ(3u, NumBuiltinElts(V));
   ASSERT_EQ(3u, V.size());
   V.pop_back();
   ASSERT_EQ(2u, V.size());
@@ -1346,7 +1478,7 @@
 TYPED_TEST(SmallVectorReferenceInvalidationTest, EmplaceBack) {
   // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.
   auto &V = this->V;
-  int N = this->NumBuiltinElts(V);
+  int N = NumBuiltinElts(V);
 
   // Push back a reference to last element when growing from small storage.
   V.emplace_back(V.back());
@@ -1375,11 +1507,6 @@
 
   VectorT V;
 
-  template <typename T, unsigned N>
-  static unsigned NumBuiltinElts(const SmallVector<T, N> &) {
-    return N;
-  }
-
   void SetUp() override {
     SmallVectorTestBase::SetUp();
 
@@ -1400,7 +1527,7 @@
 TYPED_TEST(SmallVectorInternalReferenceInvalidationTest, EmplaceBack) {
   // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.
   auto &V = this->V;
-  int N = this->NumBuiltinElts(V);
+  int N = NumBuiltinElts(V);
 
   // Push back a reference to last element when growing from small storage.
   V.emplace_back(V.back().first, V.back().second);
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/StringMapTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/StringMapTest.cpp
index 71baa20..7607498 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/StringMapTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/StringMapTest.cpp
@@ -15,6 +15,10 @@
 
 namespace {
 
+static_assert(sizeof(StringMap<uint32_t>) <
+                  sizeof(StringMap<uint32_t, MallocAllocator &>),
+              "Ensure empty base optimization happens with default allocator");
+
 // Test fixture
 class StringMapTest : public testing::Test {
 protected:
@@ -22,7 +26,7 @@
 
   static const char testKey[];
   static const uint32_t testValue;
-  static const char* testKeyFirst;
+  static const char *testKeyFirst;
   static size_t testKeyLength;
   static const std::string testKeyStr;
 
@@ -35,11 +39,12 @@
     EXPECT_TRUE(testMap.begin() == testMap.end());
 
     // Lookup tests
+    EXPECT_FALSE(testMap.contains(testKey));
     EXPECT_EQ(0u, testMap.count(testKey));
     EXPECT_EQ(0u, testMap.count(std::string_view(testKeyFirst, testKeyLength)));
     EXPECT_EQ(0u, testMap.count(testKeyStr));
     EXPECT_TRUE(testMap.find(testKey) == testMap.end());
-    EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) == 
+    EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
                 testMap.end());
     EXPECT_TRUE(testMap.find(testKeyStr) == testMap.end());
   }
@@ -58,11 +63,12 @@
     EXPECT_TRUE(it == testMap.end());
 
     // Lookup tests
+    EXPECT_TRUE(testMap.contains(testKey));
     EXPECT_EQ(1u, testMap.count(testKey));
     EXPECT_EQ(1u, testMap.count(std::string_view(testKeyFirst, testKeyLength)));
     EXPECT_EQ(1u, testMap.count(testKeyStr));
     EXPECT_TRUE(testMap.find(testKey) == testMap.begin());
-    EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) == 
+    EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
                 testMap.begin());
     EXPECT_TRUE(testMap.find(testKeyStr) == testMap.begin());
   }
@@ -70,7 +76,7 @@
 
 const char StringMapTest::testKey[] = "key";
 const uint32_t StringMapTest::testValue = 1u;
-const char* StringMapTest::testKeyFirst = testKey;
+const char *StringMapTest::testKeyFirst = testKey;
 size_t StringMapTest::testKeyLength = sizeof(testKey) - 1;
 const std::string StringMapTest::testKeyStr(testKey);
 
@@ -85,13 +91,11 @@
 };
 
 // Empty map tests.
-TEST_F(StringMapTest, EmptyMapTest) {
-  assertEmptyMap();
-}
+TEST_F(StringMapTest, EmptyMapTest) { assertEmptyMap(); }
 
 // Constant map tests.
 TEST_F(StringMapTest, ConstEmptyMapTest) {
-  const StringMap<uint32_t>& constTestMap = testMap;
+  const StringMap<uint32_t> &constTestMap = testMap;
 
   // Size tests
   EXPECT_EQ(0u, constTestMap.size());
@@ -201,6 +205,18 @@
   EXPECT_EQ(5, Map2.lookup("funf"));
 }
 
+TEST_F(StringMapTest, AtTest) {
+  wpi::StringMap<int> Map;
+
+  // keys both found and not found on non-empty map
+  Map["a"] = 1;
+  Map["b"] = 2;
+  Map["c"] = 3;
+  EXPECT_EQ(1, Map.at("a"));
+  EXPECT_EQ(2, Map.at("b"));
+  EXPECT_EQ(3, Map.at("c"));
+}
+
 // A more complex iteration test.
 TEST_F(StringMapTest, IterationTest) {
   bool visited[100];
@@ -214,8 +230,8 @@
   }
 
   // Iterate over all numbers and mark each one found.
-  for (StringMap<uint32_t>::iterator it = testMap.begin();
-      it != testMap.end(); ++it) {
+  for (StringMap<uint32_t>::iterator it = testMap.begin(); it != testMap.end();
+       ++it) {
     std::stringstream ss;
     ss << "key_" << it->second;
     ASSERT_STREQ(ss.str().c_str(), it->first().data());
@@ -232,7 +248,7 @@
 TEST_F(StringMapTest, StringMapEntryTest) {
   MallocAllocator Allocator;
   StringMap<uint32_t>::value_type *entry =
-      StringMap<uint32_t>::value_type::Create(
+      StringMap<uint32_t>::value_type::create(
           std::string_view(testKeyFirst, testKeyLength), Allocator, 1u);
   EXPECT_STREQ(testKey, entry->first().data());
   EXPECT_EQ(1u, entry->second);
@@ -242,10 +258,8 @@
 // Test insert() method.
 TEST_F(StringMapTest, InsertTest) {
   SCOPED_TRACE("InsertTest");
-  testMap.insert(
-      StringMap<uint32_t>::value_type::Create(
-          std::string_view(testKeyFirst, testKeyLength),
-          testMap.getAllocator(), 1u));
+  testMap.insert(StringMap<uint32_t>::value_type::create(
+      std::string_view(testKeyFirst, testKeyLength), testMap.getAllocator(), 1u));
   assertSingleItemMap();
 }
 
@@ -280,7 +294,7 @@
   EXPECT_EQ(0u, t.getNumBuckets());
 
   StringMap<uint32_t>::iterator It =
-    t.insert(std::make_pair("abcdef", 42)).first;
+      t.insert(std::make_pair("abcdef", 42)).first;
   EXPECT_EQ(16u, t.getNumBuckets());
   EXPECT_EQ("abcdef", It->first());
   EXPECT_EQ(42u, It->second);
@@ -338,13 +352,13 @@
 
 struct Immovable {
   Immovable() {}
-  Immovable(Immovable&&) = delete; // will disable the other special members
+  Immovable(Immovable &&) = delete; // will disable the other special members
 };
 
 struct MoveOnly {
   int i;
   MoveOnly(int i) : i(i) {}
-  MoveOnly(const Immovable&) : i(0) {}
+  MoveOnly(const Immovable &) : i(0) {}
   MoveOnly(MoveOnly &&RHS) : i(RHS.i) {}
   MoveOnly &operator=(MoveOnly &&RHS) {
     i = RHS.i;
@@ -360,14 +374,14 @@
   StringMap<MoveOnly> t;
   t.insert(std::make_pair("Test", MoveOnly(42)));
   std::string_view Key = "Test";
-  StringMapEntry<MoveOnly>::Create(Key, t.getAllocator(), MoveOnly(42))
+  StringMapEntry<MoveOnly>::create(Key, t.getAllocator(), MoveOnly(42))
       ->Destroy(t.getAllocator());
 }
 
 TEST_F(StringMapTest, CtorArg) {
   std::string_view Key = "Test";
   MallocAllocator Allocator;
-  StringMapEntry<MoveOnly>::Create(Key, Allocator, Immovable())
+  StringMapEntry<MoveOnly>::create(Key, Allocator, Immovable())
       ->Destroy(Allocator);
 }
 
@@ -501,6 +515,16 @@
   ASSERT_TRUE(B.empty());
 }
 
+TEST_F(StringMapTest, StructuredBindings) {
+  StringMap<int> A;
+  A["a"] = 42;
+
+  for (auto &[Key, Value] : A) {
+    EXPECT_EQ("a", Key);
+    EXPECT_EQ(42, Value);
+  }
+}
+
 namespace {
 // Simple class that counts how many moves and copy happens when growing a map
 struct CountCtorCopyAndMove {
@@ -570,7 +594,7 @@
   NonMoveableNonCopyableType(const NonMoveableNonCopyableType &) = delete;
   NonMoveableNonCopyableType(NonMoveableNonCopyableType &&) = delete;
 };
-}
+} // namespace
 
 // Test that we can "emplace" an element in the map without involving map/move
 TEST(StringMapCustomTest, EmplaceTest) {
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SwapByteOrderTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SwapByteOrderTest.cpp
index abacef7..23ad2ab 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SwapByteOrderTest.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/SwapByteOrderTest.cpp
@@ -16,16 +16,6 @@
 
 namespace {
 
-TEST(ByteSwap, Swap_32) {
-  EXPECT_EQ(0x44332211u, ByteSwap_32(0x11223344));
-  EXPECT_EQ(0xDDCCBBAAu, ByteSwap_32(0xAABBCCDD));
-}
-
-TEST(ByteSwap, Swap_64) {
-  EXPECT_EQ(0x8877665544332211ULL, ByteSwap_64(0x1122334455667788LL));
-  EXPECT_EQ(0x1100FFEEDDCCBBAAULL, ByteSwap_64(0xAABBCCDDEEFF0011LL));
-}
-
 // In these first two tests all of the original_uintx values are truncated
 // except for 64. We could avoid this, but there's really no point.
 
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/xxhashTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/xxhashTest.cpp
new file mode 100644
index 0000000..133c542
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/llvm/xxhashTest.cpp
@@ -0,0 +1,63 @@
+//===- llvm/unittest/Support/xxhashTest.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/xxhash.h"
+#include "gtest/gtest.h"
+
+using namespace wpi;
+
+TEST(xxhashTest, Basic) {
+  EXPECT_EQ(0xef46db3751d8e999U, xxHash64(std::string_view()));
+  EXPECT_EQ(0x33bf00a859c4ba3fU, xxHash64("foo"));
+  EXPECT_EQ(0x48a37c90ad27a659U, xxHash64("bar"));
+  EXPECT_EQ(0x69196c1b3af0bff9U,
+            xxHash64("0123456789abcdefghijklmnopqrstuvwxyz"));
+}
+
+TEST(xxhashTest, xxh3) {
+  constexpr size_t size = 2243;
+  uint8_t a[size];
+  uint64_t x = 1;
+  for (size_t i = 0; i < size; ++i) {
+    x ^= x << 13;
+    x ^= x >> 7;
+    x ^= x << 17;
+    a[i] = uint8_t(x);
+  }
+
+#define F(len, expected)                                                       \
+  EXPECT_EQ(uint64_t(expected), xxh3_64bits(std::span(a, size_t(len))))
+  F(0, 0x2d06800538d394c2);
+  F(1, 0xd0d496e05c553485);
+  F(2, 0x84d625edb7055eac);
+  F(3, 0x6ea2d59aca5c3778);
+  F(4, 0xbf65290914e80242);
+  F(5, 0xc01fd099ad4fc8e4);
+  F(6, 0x9e3ea8187399caa5);
+  F(7, 0x9da8b60540644f5a);
+  F(8, 0xabc1413da6cd0209);
+  F(9, 0x8bc89400bfed51f6);
+  F(16, 0x7e46916754d7c9b8);
+  F(17, 0xed4be912ba5f836d);
+  F(32, 0xf59b59b58c304fd1);
+  F(33, 0x9013fb74ca603e0c);
+  F(64, 0xfa5271fcce0db1c3);
+  F(65, 0x79c42431727f1012);
+  F(96, 0x591ee0ddf9c9ccd1);
+  F(97, 0x8ffc6a3111fe19da);
+  F(128, 0x06a146ee9a2da378);
+  F(129, 0xbc7138129bf065da);
+  F(403, 0xcefeb3ffa532ad8c);
+  F(512, 0xcdfa6b6268e3650f);
+  F(513, 0x4bb5d42742f9765f);
+  F(2048, 0x330ce110cbb79eae);
+  F(2049, 0x3ba6afa0249fef9a);
+  F(2240, 0xd61d4d2a94e926a8);
+  F(2243, 0x0979f786a24edde7);
+#undef F
+}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/main.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/main.cpp
index 09072ee..e993c1f 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/main.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/main.cpp
@@ -2,7 +2,7 @@
 // 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 "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/priority_mutex_test.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
index 832e8f3..8e2ec3e 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
@@ -8,7 +8,7 @@
 #include <condition_variable>
 #include <thread>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 namespace wpi {
 
@@ -157,7 +157,7 @@
 //
 // To run the test, we need 3 threads, and then 1 thread to kick the test off.
 // The threads must all run on the same core, otherwise they wouldn't starve
-// eachother. The threads and their roles are as follows:
+// each other. The threads and their roles are as follows:
 //
 // Low priority thread:
 //   Holds a lock that the high priority thread needs, and releases it upon
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/sha1Test.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/sha1Test.cpp
index 0257431..814e47e 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/sha1Test.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/sha1Test.cpp
@@ -21,7 +21,8 @@
 
 #include <string>
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
+
 #include "wpi/sha1.h"
 
 namespace wpi {
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp
index f31e296..9901488 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp
@@ -33,10 +33,10 @@
 
 #include "wpi/Signal.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
-
 #include <type_traits>
 
+#include <gtest/gtest.h>
+
 using namespace wpi::sig::trait;
 
 namespace {
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/recursive.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/recursive.cpp
index 82613f2..6d35f78 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/recursive.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/recursive.cpp
@@ -33,7 +33,7 @@
 
 #include "wpi/Signal.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 namespace {
 
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp
index a0dbdfc..e863ca6 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp
@@ -33,7 +33,7 @@
 
 #include "wpi/Signal.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 
 using namespace wpi::sig;
 
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp
index 00001a8..15dc3a8 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp
@@ -33,12 +33,12 @@
 
 #include "wpi/Signal.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
-
 #include <array>
 #include <atomic>
 #include <thread>
 
+#include <gtest/gtest.h>
+
 using namespace wpi::sig;
 
 namespace {
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp
index d05ee1a..b77f5a4 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp
@@ -33,12 +33,12 @@
 
 #include "wpi/Signal.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
-
 #include <cmath>
 #include <sstream>
 #include <string>
 
+#include <gtest/gtest.h>
+
 using namespace wpi::sig;
 
 namespace {
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal.cpp
index cc2ff80..34d149e 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/sigslot/signal.cpp
@@ -33,12 +33,12 @@
 
 #include "wpi/Signal.h"  // NOLINT(build/include_order)
 
-#include "gtest/gtest.h"  // NOLINT(build/include_order)
-
 #include <cmath>
 #include <sstream>
 #include <string>
 
+#include <gtest/gtest.h>
+
 using namespace wpi::sig;
 
 namespace {
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/spinlock_bench.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/spinlock_bench.cpp
index 9534837..c8af3d2 100644
--- a/third_party/allwpilib/wpiutil/src/test/native/cpp/spinlock_bench.cpp
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/spinlock_bench.cpp
@@ -8,8 +8,9 @@
 #include <mutex>
 #include <thread>
 
-#include "fmt/core.h"
-#include "gtest/gtest.h"
+#include <fmt/core.h>
+#include <gtest/gtest.h>
+
 #include "wpi/mutex.h"
 
 static std::mutex std_mutex;
@@ -28,7 +29,7 @@
 
   // warmup
   std::thread thr([]() {
-    int value = 0;
+    [[maybe_unused]] int value = 0;
 
     auto start = high_resolution_clock::now();
     for (int i = 0; i < 10000000; i++) {
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/struct/DynamicStructTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/struct/DynamicStructTest.cpp
new file mode 100644
index 0000000..d5d21d1
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/struct/DynamicStructTest.cpp
@@ -0,0 +1,358 @@
+// 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/struct/DynamicStruct.h"  // NOLINT(build/include_order)
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+using namespace wpi;
+
+class DynamicStructTest : public ::testing::Test {
+ protected:
+  StructDescriptorDatabase db;
+  std::string err;
+};
+
+TEST_F(DynamicStructTest, Empty) {
+  auto desc = db.Add("test", "", &err);
+  ASSERT_TRUE(desc);
+  ASSERT_EQ(desc->GetName(), "test");
+  ASSERT_EQ(desc->GetSchema(), "");
+  ASSERT_TRUE(desc->GetFields().empty());
+  ASSERT_TRUE(desc->IsValid());
+  ASSERT_EQ(desc->GetSize(), 0u);
+}
+
+TEST_F(DynamicStructTest, NestedStruct) {
+  auto desc = db.Add("test", "int32 a", &err);
+  ASSERT_TRUE(desc);
+  ASSERT_TRUE(desc->IsValid());
+  auto desc2 = db.Add("test2", "test a", &err);
+  ASSERT_TRUE(desc2);
+  ASSERT_TRUE(desc2->IsValid());
+  ASSERT_EQ(desc2->GetSize(), 4u);
+}
+
+TEST_F(DynamicStructTest, DelayedValid) {
+  auto desc = db.Add("test", "foo a", &err);
+  ASSERT_TRUE(desc);
+  ASSERT_FALSE(desc->IsValid());
+  auto desc2 = db.Add("test2", "foo a[2]", &err);
+  ASSERT_TRUE(desc2);
+  ASSERT_FALSE(desc2->IsValid());
+  auto desc3 = db.Add("foo", "int32 a", &err);
+  ASSERT_TRUE(desc3);
+  ASSERT_TRUE(desc3->IsValid());
+  ASSERT_TRUE(desc->IsValid());
+  ASSERT_EQ(desc->GetSize(), 4u);
+  ASSERT_TRUE(desc2->IsValid());
+  ASSERT_EQ(desc2->GetSize(), 8u);
+}
+
+TEST_F(DynamicStructTest, InvalidBitfield) {
+  auto desc = db.Add("test", "float a:1", &err);
+  EXPECT_FALSE(desc);
+  EXPECT_EQ(err, "field a: type float cannot be bitfield");
+
+  desc = db.Add("test", "double a:1", &err);
+  EXPECT_FALSE(desc);
+  EXPECT_EQ(err, "field a: type double cannot be bitfield");
+
+  desc = db.Add("test", "foo a:1", &err);
+  EXPECT_FALSE(desc);
+  EXPECT_EQ(err, "field a: type foo cannot be bitfield");
+}
+
+TEST_F(DynamicStructTest, CircularStructReference) {
+  auto desc = db.Add("test", "test a", &err);
+  ASSERT_FALSE(desc);
+  ASSERT_EQ(err, "field a: recursive struct reference");
+}
+
+TEST_F(DynamicStructTest, NestedCircularStructRef) {
+  auto desc = db.Add("test", "foo a", &err);
+  ASSERT_TRUE(desc);
+  auto desc2 = db.Add("foo", "bar a", &err);
+  ASSERT_TRUE(desc2);
+  auto desc3 = db.Add("bar", "test a", &err);
+  ASSERT_FALSE(desc3);
+  ASSERT_EQ(err, "circular struct reference: bar <- foo <- test");
+
+  // ok
+  auto desc4 = db.Add("baz", "bar a", &err);
+  ASSERT_TRUE(desc4);
+  ASSERT_FALSE(desc4->IsValid());
+}
+
+TEST_F(DynamicStructTest, NestedCircularStructRef2) {
+  auto desc = db.Add("test", "foo a", &err);
+  ASSERT_TRUE(desc);
+  auto desc2 = db.Add("bar", "test a", &err);
+  ASSERT_TRUE(desc2);
+  auto desc3 = db.Add("foo", "bar a", &err);
+  ASSERT_FALSE(desc3);
+  ASSERT_EQ(err, "circular struct reference: foo <- test <- bar");
+}
+
+TEST_F(DynamicStructTest, BitfieldBasic) {
+  auto desc = db.Add("test", "int32 a:2; uint32 b:30", &err);
+  ASSERT_TRUE(desc);
+  EXPECT_EQ(desc->GetSize(), 4u);
+  auto& fields = desc->GetFields();
+  ASSERT_EQ(fields.size(), 2u);
+  EXPECT_EQ(fields[0].GetBitWidth(), 2u);
+  EXPECT_EQ(fields[0].GetBitShift(), 0u);
+  EXPECT_EQ(fields[0].GetBitMask(), 0x3u);
+  EXPECT_EQ(fields[0].GetOffset(), 0u);
+  EXPECT_EQ(fields[0].GetSize(), 4u);
+  EXPECT_EQ(fields[1].GetBitWidth(), 30u);
+  EXPECT_EQ(fields[1].GetBitShift(), 2u);
+  EXPECT_EQ(fields[1].GetBitMask(), 0x3fffffffu);
+  EXPECT_EQ(fields[1].GetOffset(), 0u);
+  EXPECT_EQ(fields[1].GetSize(), 4u);
+}
+
+TEST_F(DynamicStructTest, BitfieldDiffType) {
+  auto desc = db.Add("test", "int32 a:2; int16 b:2", &err);
+  ASSERT_TRUE(desc);
+  EXPECT_EQ(desc->GetSize(), 6u);
+  auto& fields = desc->GetFields();
+  ASSERT_EQ(fields.size(), 2u);
+  EXPECT_EQ(fields[0].GetBitWidth(), 2u);
+  EXPECT_EQ(fields[0].GetBitShift(), 0u);
+  EXPECT_EQ(fields[0].GetBitMask(), 0x3u);
+  EXPECT_EQ(fields[0].GetOffset(), 0u);
+  EXPECT_EQ(fields[0].GetSize(), 4u);
+  EXPECT_EQ(fields[1].GetBitWidth(), 2u);
+  EXPECT_EQ(fields[1].GetBitShift(), 0u);
+  EXPECT_EQ(fields[1].GetBitMask(), 0x3u);
+  EXPECT_EQ(fields[1].GetOffset(), 4u);
+  EXPECT_EQ(fields[1].GetSize(), 2u);
+}
+
+TEST_F(DynamicStructTest, BitfieldOverflow) {
+  auto desc = db.Add("test", "int8 a:4; int8 b:5", &err);
+  ASSERT_TRUE(desc);
+  EXPECT_EQ(desc->GetSize(), 2u);
+  auto& fields = desc->GetFields();
+  ASSERT_EQ(fields.size(), 2u);
+  EXPECT_EQ(fields[0].GetBitWidth(), 4u);
+  EXPECT_EQ(fields[0].GetBitShift(), 0u);
+  EXPECT_EQ(fields[0].GetBitMask(), 0xfu);
+  EXPECT_EQ(fields[0].GetOffset(), 0u);
+  EXPECT_EQ(fields[0].GetSize(), 1u);
+  EXPECT_EQ(fields[1].GetBitWidth(), 5u);
+  EXPECT_EQ(fields[1].GetBitMask(), 0x1fu);
+  EXPECT_EQ(fields[1].GetBitShift(), 0u);
+  EXPECT_EQ(fields[1].GetOffset(), 1u);
+  EXPECT_EQ(fields[1].GetSize(), 1u);
+}
+
+TEST_F(DynamicStructTest, BitfieldBoolBegin8) {
+  auto desc = db.Add("test", "bool a:1; int8 b:5", &err);
+  ASSERT_TRUE(desc);
+  EXPECT_EQ(desc->GetSize(), 1u);
+  auto& fields = desc->GetFields();
+  ASSERT_EQ(fields.size(), 2u);
+  EXPECT_EQ(fields[0].GetBitWidth(), 1u);
+  EXPECT_EQ(fields[0].GetBitShift(), 0u);
+  EXPECT_EQ(fields[0].GetBitMask(), 0x1u);
+  EXPECT_EQ(fields[0].GetOffset(), 0u);
+  EXPECT_EQ(fields[0].GetSize(), 1u);
+  EXPECT_EQ(fields[1].GetBitWidth(), 5u);
+  EXPECT_EQ(fields[1].GetBitMask(), 0x1fu);
+  EXPECT_EQ(fields[1].GetBitShift(), 1u);
+  EXPECT_EQ(fields[1].GetOffset(), 0u);
+  EXPECT_EQ(fields[1].GetSize(), 1u);
+}
+
+TEST_F(DynamicStructTest, BitfieldBoolBegin16) {
+  auto desc = db.Add("test", "bool a:1; int16 b:5", &err);
+  ASSERT_TRUE(desc);
+  EXPECT_EQ(desc->GetSize(), 3u);
+  auto& fields = desc->GetFields();
+  ASSERT_EQ(fields.size(), 2u);
+  EXPECT_EQ(fields[0].GetBitWidth(), 1u);
+  EXPECT_EQ(fields[0].GetBitShift(), 0u);
+  EXPECT_EQ(fields[0].GetBitMask(), 0x1u);
+  EXPECT_EQ(fields[0].GetOffset(), 0u);
+  EXPECT_EQ(fields[0].GetSize(), 1u);
+  EXPECT_EQ(fields[1].GetBitWidth(), 5u);
+  EXPECT_EQ(fields[1].GetBitMask(), 0x1fu);
+  EXPECT_EQ(fields[1].GetBitShift(), 0u);
+  EXPECT_EQ(fields[1].GetOffset(), 1u);
+  EXPECT_EQ(fields[1].GetSize(), 2u);
+}
+
+TEST_F(DynamicStructTest, BitfieldBoolMid) {
+  auto desc = db.Add("test", "int16 a:2; bool b:1; bool c:1; uint16 d:5", &err);
+  ASSERT_TRUE(desc);
+  EXPECT_EQ(desc->GetSize(), 2u);
+  auto& fields = desc->GetFields();
+  ASSERT_EQ(fields.size(), 4u);
+  EXPECT_EQ(fields[0].GetBitWidth(), 2u);
+  EXPECT_EQ(fields[0].GetBitShift(), 0u);
+  EXPECT_EQ(fields[0].GetBitMask(), 0x3u);
+  EXPECT_EQ(fields[0].GetOffset(), 0u);
+  EXPECT_EQ(fields[0].GetSize(), 2u);
+  EXPECT_EQ(fields[1].GetBitWidth(), 1u);
+  EXPECT_EQ(fields[1].GetBitMask(), 0x1u);
+  EXPECT_EQ(fields[1].GetBitShift(), 2u);
+  EXPECT_EQ(fields[1].GetOffset(), 0u);
+  EXPECT_EQ(fields[1].GetSize(), 2u);
+  EXPECT_EQ(fields[2].GetBitWidth(), 1u);
+  EXPECT_EQ(fields[2].GetBitMask(), 0x1u);
+  EXPECT_EQ(fields[2].GetBitShift(), 3u);
+  EXPECT_EQ(fields[2].GetOffset(), 0u);
+  EXPECT_EQ(fields[2].GetSize(), 2u);
+  EXPECT_EQ(fields[3].GetBitWidth(), 5u);
+  EXPECT_EQ(fields[3].GetBitMask(), 0x1fu);
+  EXPECT_EQ(fields[3].GetBitShift(), 4u);
+  EXPECT_EQ(fields[3].GetOffset(), 0u);
+  EXPECT_EQ(fields[3].GetSize(), 2u);
+}
+
+TEST_F(DynamicStructTest, BitfieldBoolEnd) {
+  auto desc = db.Add("test", "int16 a:15; bool b:1", &err);
+  ASSERT_TRUE(desc);
+  EXPECT_EQ(desc->GetSize(), 2u);
+  auto& fields = desc->GetFields();
+  ASSERT_EQ(fields.size(), 2u);
+  EXPECT_EQ(fields[0].GetBitWidth(), 15u);
+  EXPECT_EQ(fields[0].GetBitShift(), 0u);
+  EXPECT_EQ(fields[0].GetBitMask(), 0x7fffu);
+  EXPECT_EQ(fields[0].GetOffset(), 0u);
+  EXPECT_EQ(fields[0].GetSize(), 2u);
+  EXPECT_EQ(fields[1].GetBitWidth(), 1u);
+  EXPECT_EQ(fields[1].GetBitMask(), 0x1u);
+  EXPECT_EQ(fields[1].GetBitShift(), 15u);
+  EXPECT_EQ(fields[1].GetOffset(), 0u);
+  EXPECT_EQ(fields[1].GetSize(), 2u);
+}
+
+TEST_F(DynamicStructTest, BitfieldBoolEnd2) {
+  auto desc = db.Add("test", "int16 a:16; bool b:1", &err);
+  ASSERT_TRUE(desc);
+  EXPECT_EQ(desc->GetSize(), 3u);
+  auto& fields = desc->GetFields();
+  ASSERT_EQ(fields.size(), 2u);
+  EXPECT_EQ(fields[0].GetBitWidth(), 16u);
+  EXPECT_EQ(fields[0].GetBitShift(), 0u);
+  EXPECT_EQ(fields[0].GetBitMask(), 0xffffu);
+  EXPECT_EQ(fields[0].GetOffset(), 0u);
+  EXPECT_EQ(fields[0].GetSize(), 2u);
+  EXPECT_EQ(fields[1].GetBitWidth(), 1u);
+  EXPECT_EQ(fields[1].GetBitMask(), 0x1u);
+  EXPECT_EQ(fields[1].GetBitShift(), 0u);
+  EXPECT_EQ(fields[1].GetOffset(), 2u);
+  EXPECT_EQ(fields[1].GetSize(), 1u);
+}
+
+TEST_F(DynamicStructTest, BitfieldBoolWrongSize) {
+  auto desc = db.Add("test", "bool a:2", &err);
+  ASSERT_FALSE(desc);
+  ASSERT_EQ(err, "field a: bit width must be 1 for bool type");
+}
+
+TEST_F(DynamicStructTest, BitfieldTooBig) {
+  auto desc = db.Add("test", "int16 a:17", &err);
+  ASSERT_FALSE(desc);
+  ASSERT_EQ(err, "field a: bit width 17 exceeds type size");
+}
+
+TEST_F(DynamicStructTest, DuplicateFieldName) {
+  auto desc = db.Add("test", "int16 a; int8 a", &err);
+  ASSERT_FALSE(desc);
+  ASSERT_EQ(err, "duplicate field a");
+}
+
+struct SimpleTestParam {
+  const char* schema;
+  size_t size;
+  StructFieldType type;
+  bool isInt;
+  bool isUint;
+  unsigned int bitWidth;
+  uint64_t bitMask;
+};
+
+std::ostream& operator<<(std::ostream& os, const SimpleTestParam& param) {
+  return os << "SimpleTestParam(Schema: \"" << param.schema << "\")";
+}
+
+class DynamicSimpleStructTest
+    : public ::testing::TestWithParam<SimpleTestParam> {
+ protected:
+  StructDescriptorDatabase db;
+  std::string err;
+};
+
+TEST_P(DynamicSimpleStructTest, Check) {
+  auto desc = db.Add("test", GetParam().schema, &err);
+  ASSERT_TRUE(desc);
+  ASSERT_EQ(desc->GetName(), "test");
+  ASSERT_EQ(desc->GetSchema(), GetParam().schema);
+  auto& fields = desc->GetFields();
+  ASSERT_EQ(fields.size(), 1u);
+  EXPECT_EQ(fields[0].GetParent(), desc);
+  EXPECT_EQ(fields[0].GetName(), "a");
+  EXPECT_EQ(fields[0].IsInt(), GetParam().isInt);
+  EXPECT_EQ(fields[0].IsUint(), GetParam().isUint);
+  EXPECT_FALSE(fields[0].IsArray());
+  if (GetParam().type != StructFieldType::kStruct) {
+    ASSERT_TRUE(desc->IsValid());
+    ASSERT_EQ(desc->GetSize(), GetParam().size);
+    ASSERT_EQ(fields[0].GetSize(), GetParam().size);
+    ASSERT_EQ(fields[0].GetBitWidth(), GetParam().bitWidth);
+    ASSERT_EQ(fields[0].GetBitMask(), GetParam().bitMask);
+  } else {
+    ASSERT_FALSE(desc->IsValid());
+    ASSERT_TRUE(fields[0].GetStruct());
+  }
+}
+
+TEST_P(DynamicSimpleStructTest, Array) {
+  auto desc = db.Add("test", GetParam().schema + std::string{"[2]"}, &err);
+  ASSERT_TRUE(desc);
+  ASSERT_EQ(desc->GetName(), "test");
+  ASSERT_EQ(desc->GetSchema(), GetParam().schema + std::string{"[2]"});
+  auto& fields = desc->GetFields();
+  ASSERT_EQ(fields.size(), 1u);
+  EXPECT_EQ(fields[0].GetParent(), desc);
+  EXPECT_EQ(fields[0].GetName(), "a");
+  EXPECT_EQ(fields[0].IsInt(), GetParam().isInt);
+  EXPECT_EQ(fields[0].IsUint(), GetParam().isUint);
+  EXPECT_TRUE(fields[0].IsArray());
+  EXPECT_EQ(fields[0].GetArraySize(), 2u);
+  if (GetParam().type != StructFieldType::kStruct) {
+    ASSERT_TRUE(desc->IsValid());
+    ASSERT_EQ(desc->GetSize(), GetParam().size * 2u);
+  } else {
+    ASSERT_FALSE(desc->IsValid());
+    ASSERT_TRUE(fields[0].GetStruct());
+  }
+}
+
+static SimpleTestParam simpleTests[] = {
+    {"bool a", 1, StructFieldType::kBool, false, false, 8, UINT8_MAX},
+    {"char a", 1, StructFieldType::kChar, false, false, 8, UINT8_MAX},
+    {"int8 a", 1, StructFieldType::kInt8, true, false, 8, UINT8_MAX},
+    {"int16 a", 2, StructFieldType::kInt16, true, false, 16, UINT16_MAX},
+    {"int32 a", 4, StructFieldType::kInt32, true, false, 32, UINT32_MAX},
+    {"int64 a", 8, StructFieldType::kInt64, true, false, 64, UINT64_MAX},
+    {"uint8 a", 1, StructFieldType::kUint8, false, true, 8, UINT8_MAX},
+    {"uint16 a", 2, StructFieldType::kUint16, false, true, 16, UINT16_MAX},
+    {"uint32 a", 4, StructFieldType::kUint32, false, true, 32, UINT32_MAX},
+    {"uint64 a", 8, StructFieldType::kUint64, false, true, 64, UINT64_MAX},
+    {"float a", 4, StructFieldType::kFloat, false, false, 32, UINT32_MAX},
+    {"float32 a", 4, StructFieldType::kFloat, false, false, 32, UINT32_MAX},
+    {"double a", 8, StructFieldType::kDouble, false, false, 64, UINT64_MAX},
+    {"float64 a", 8, StructFieldType::kDouble, false, false, 64, UINT64_MAX},
+    {"foo a", 0, StructFieldType::kStruct, false, false, 0, 0},
+};
+
+INSTANTIATE_TEST_SUITE_P(DynamicSimpleStructTests, DynamicSimpleStructTest,
+                         ::testing::ValuesIn(simpleTests));
diff --git a/third_party/allwpilib/wpiutil/src/test/native/cpp/struct/SchemaParserTest.cpp b/third_party/allwpilib/wpiutil/src/test/native/cpp/struct/SchemaParserTest.cpp
new file mode 100644
index 0000000..bf24141
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/native/cpp/struct/SchemaParserTest.cpp
@@ -0,0 +1,218 @@
+// 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/struct/SchemaParser.h"  // NOLINT(build/include_order)
+
+#include <gtest/gtest.h>
+
+using namespace wpi::structparser;
+
+TEST(StructParserTest, Empty) {
+  Parser p{""};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_TRUE(schema.declarations.empty());
+}
+
+TEST(StructParserTest, EmptySemicolon) {
+  Parser p{";"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_TRUE(schema.declarations.empty());
+}
+
+TEST(StructParserTest, Simple) {
+  Parser p{"int32 a"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+  auto& decl = schema.declarations[0];
+  EXPECT_EQ(decl.typeString, "int32");
+  EXPECT_EQ(decl.name, "a");
+  EXPECT_EQ(decl.arraySize, 1u);
+}
+
+TEST(StructParserTest, SimpleTrailingSemi) {
+  Parser p{"int32 a;"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+}
+
+TEST(StructParserTest, Array) {
+  Parser p{"int32 a[2]"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+  auto& decl = schema.declarations[0];
+  EXPECT_EQ(decl.typeString, "int32");
+  EXPECT_EQ(decl.name, "a");
+  EXPECT_EQ(decl.arraySize, 2u);
+}
+
+TEST(StructParserTest, ArrayTrailingSemi) {
+  Parser p{"int32 a[2];"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+}
+
+TEST(StructParserTest, Bitfield) {
+  Parser p{"int32 a:2"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+  auto& decl = schema.declarations[0];
+  EXPECT_EQ(decl.typeString, "int32");
+  EXPECT_EQ(decl.name, "a");
+  EXPECT_EQ(decl.bitWidth, 2u);
+}
+
+TEST(StructParserTest, BitfieldTrailingSemi) {
+  Parser p{"int32 a:2;"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+}
+
+TEST(StructParserTest, EnumKeyword) {
+  Parser p{"enum {x=1} int32 a;"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+  auto& decl = schema.declarations[0];
+  EXPECT_EQ(decl.typeString, "int32");
+  EXPECT_EQ(decl.name, "a");
+  ASSERT_EQ(decl.enumValues.size(), 1u);
+  EXPECT_EQ(decl.enumValues[0].first, "x");
+  EXPECT_EQ(decl.enumValues[0].second, 1);
+}
+
+TEST(StructParserTest, EnumNoKeyword) {
+  Parser p{"{x=1} int32 a;"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+  auto& decl = schema.declarations[0];
+  EXPECT_EQ(decl.typeString, "int32");
+  EXPECT_EQ(decl.name, "a");
+  ASSERT_EQ(decl.enumValues.size(), 1u);
+  EXPECT_EQ(decl.enumValues[0].first, "x");
+  EXPECT_EQ(decl.enumValues[0].second, 1);
+}
+
+TEST(StructParserTest, EnumNoValues) {
+  Parser p{"{} int32 a;"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+  auto& decl = schema.declarations[0];
+  EXPECT_EQ(decl.typeString, "int32");
+  EXPECT_EQ(decl.name, "a");
+  ASSERT_TRUE(decl.enumValues.empty());
+}
+
+TEST(StructParserTest, EnumMultipleValues) {
+  Parser p{"{x=1,y=-2} int32 a;"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+  auto& decl = schema.declarations[0];
+  EXPECT_EQ(decl.typeString, "int32");
+  EXPECT_EQ(decl.name, "a");
+  ASSERT_EQ(decl.enumValues.size(), 2u);
+  EXPECT_EQ(decl.enumValues[0].first, "x");
+  EXPECT_EQ(decl.enumValues[0].second, 1);
+  EXPECT_EQ(decl.enumValues[1].first, "y");
+  EXPECT_EQ(decl.enumValues[1].second, -2);
+}
+
+TEST(StructParserTest, EnumTrailingComma) {
+  Parser p{"{x=1,y=2,} int32 a;"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 1u);
+  auto& decl = schema.declarations[0];
+  EXPECT_EQ(decl.typeString, "int32");
+  EXPECT_EQ(decl.name, "a");
+  ASSERT_EQ(decl.enumValues.size(), 2u);
+  EXPECT_EQ(decl.enumValues[0].first, "x");
+  EXPECT_EQ(decl.enumValues[0].second, 1);
+  EXPECT_EQ(decl.enumValues[1].first, "y");
+  EXPECT_EQ(decl.enumValues[1].second, 2);
+}
+
+TEST(StructParserTest, MultipleNoTrailingSemi) {
+  Parser p{"int32 a; int16 b"};
+  ParsedSchema schema;
+  ASSERT_TRUE(p.Parse(&schema));
+  ASSERT_EQ(schema.declarations.size(), 2u);
+  EXPECT_EQ(schema.declarations[0].typeString, "int32");
+  EXPECT_EQ(schema.declarations[0].name, "a");
+  EXPECT_EQ(schema.declarations[1].typeString, "int16");
+  EXPECT_EQ(schema.declarations[1].name, "b");
+}
+
+TEST(StructParserTest, ErrBitfieldArray) {
+  Parser p{"int32 a[1]:2"};
+  ParsedSchema schema;
+  ASSERT_FALSE(p.Parse(&schema));
+  ASSERT_EQ(p.GetError(), "10: expected ';', got ':'");
+}
+
+TEST(StructParserTest, ErrNoArrayValue) {
+  Parser p{"int32 a[]"};
+  ParsedSchema schema;
+  ASSERT_FALSE(p.Parse(&schema));
+  ASSERT_EQ(p.GetError(), "8: expected integer, got ']'");
+}
+
+TEST(StructParserTest, ErrNoBitfieldValue) {
+  Parser p{"int32 a:"};
+  ParsedSchema schema;
+  ASSERT_FALSE(p.Parse(&schema));
+  ASSERT_EQ(p.GetError(), "8: expected integer, got ''");
+}
+
+TEST(StructParserTest, ErrNoNameArray) {
+  Parser p{"int32 [2]"};
+  ParsedSchema schema;
+  ASSERT_FALSE(p.Parse(&schema));
+  ASSERT_EQ(p.GetError(), "6: expected identifier, got '['");
+}
+
+TEST(StructParserTest, ErrNoNameBitField) {
+  Parser p{"int32 :2"};
+  ParsedSchema schema;
+  ASSERT_FALSE(p.Parse(&schema));
+  ASSERT_EQ(p.GetError(), "6: expected identifier, got ':'");
+}
+
+TEST(StructParserTest, NegativeBitField) {
+  Parser p{"int32 a:-1"};
+  ParsedSchema schema;
+  ASSERT_FALSE(p.Parse(&schema));
+  ASSERT_EQ(p.GetError(), "8: bitfield width '-1' is not a positive integer");
+}
+
+TEST(StructParserTest, NegativeArraySize) {
+  Parser p{"int32 a[-1]"};
+  ParsedSchema schema;
+  ASSERT_FALSE(p.Parse(&schema));
+  ASSERT_EQ(p.GetError(), "8: array size '-1' is not a positive integer");
+}
+
+TEST(StructParserTest, ZeroBitField) {
+  Parser p{"int32 a:0"};
+  ParsedSchema schema;
+  ASSERT_FALSE(p.Parse(&schema));
+  ASSERT_EQ(p.GetError(), "8: bitfield width '0' is not a positive integer");
+}
+
+TEST(StructParserTest, ZeroArraySize) {
+  Parser p{"int32 a[0]"};
+  ParsedSchema schema;
+  ASSERT_FALSE(p.Parse(&schema));
+  ASSERT_EQ(p.GetError(), "8: array size '0' is not a positive integer");
+}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/include/wpi/SpanMatcher.h b/third_party/allwpilib/wpiutil/src/test/native/include/wpi/SpanMatcher.h
new file mode 100644
index 0000000..247c142
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/native/include/wpi/SpanMatcher.h
@@ -0,0 +1,75 @@
+// 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 <algorithm>
+#include <initializer_list>
+#include <memory>
+#include <ostream>
+#include <span>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include "wpi/TestPrinters.h"
+
+namespace wpi {
+
+template <typename T>
+class SpanMatcher : public ::testing::MatcherInterface<std::span<T>> {
+ public:
+  explicit SpanMatcher(std::span<T> good_) : good{good_.begin(), good_.end()} {}
+
+  bool MatchAndExplain(std::span<T> val,
+                       ::testing::MatchResultListener* listener) const override;
+  void DescribeTo(::std::ostream* os) const override;
+  void DescribeNegationTo(::std::ostream* os) const override;
+
+ private:
+  std::vector<std::remove_cv_t<T>> good;
+};
+
+template <typename T>
+inline ::testing::Matcher<std::span<const typename T::value_type>> SpanEq(
+    const T& good) {
+  return ::testing::MakeMatcher(
+      new SpanMatcher(std::span<const typename T::value_type>(good)));
+}
+
+template <typename T>
+inline ::testing::Matcher<std::span<const T>> SpanEq(
+    std::initializer_list<const T> good) {
+  return ::testing::MakeMatcher(
+      new SpanMatcher<const T>({good.begin(), good.end()}));
+}
+
+template <typename T>
+bool SpanMatcher<T>::MatchAndExplain(
+    std::span<T> val, ::testing::MatchResultListener* listener) const {
+  if (val.size() != good.size() ||
+      !std::equal(val.begin(), val.end(), good.begin())) {
+    return false;
+  }
+  return true;
+}
+
+template <typename T>
+void SpanMatcher<T>::DescribeTo(::std::ostream* os) const {
+  PrintTo(std::span<T>{good}, os);
+}
+
+template <typename T>
+void SpanMatcher<T>::DescribeNegationTo(::std::ostream* os) const {
+  *os << "is not equal to ";
+  PrintTo(std::span<T>{good}, os);
+}
+
+}  // namespace wpi
+
+inline std::span<const uint8_t> operator"" _us(const char* str, size_t len) {
+  return {reinterpret_cast<const uint8_t*>(str), len};
+}
diff --git a/third_party/allwpilib/wpiutil/src/test/native/include/wpi/TestPrinters.h b/third_party/allwpilib/wpiutil/src/test/native/include/wpi/TestPrinters.h
new file mode 100644
index 0000000..72d7da3
--- /dev/null
+++ b/third_party/allwpilib/wpiutil/src/test/native/include/wpi/TestPrinters.h
@@ -0,0 +1,41 @@
+// 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 <ostream>
+#include <span>
+#include <string>
+#include <string_view>
+
+#include <gtest/gtest.h>
+
+#include "wpi/json.h"
+
+namespace wpi {
+
+inline void PrintTo(std::string_view str, ::std::ostream* os) {
+  ::testing::internal::PrintStringTo(std::string{str}, os);
+}
+
+template <typename T>
+void PrintTo(std::span<T> val, ::std::ostream* os) {
+  *os << '{';
+  bool first = true;
+  for (auto v : val) {
+    if (first) {
+      first = false;
+    } else {
+      *os << ", ";
+    }
+    *os << ::testing::PrintToString(v);
+  }
+  *os << '}';
+}
+
+inline void PrintTo(const json& val, ::std::ostream* os) {
+  *os << val.dump();
+}
+
+}  // namespace wpi
diff --git a/third_party/allwpilib/wpiutil/wpiutil-config.cmake.in b/third_party/allwpilib/wpiutil/wpiutil-config.cmake.in
index fde839e..3f696c8 100644
--- a/third_party/allwpilib/wpiutil/wpiutil-config.cmake.in
+++ b/third_party/allwpilib/wpiutil/wpiutil-config.cmake.in
@@ -4,5 +4,9 @@
 find_dependency(Threads)
 @FMTLIB_SYSTEM_REPLACE@
 
+if(@USE_SYSTEM_FMTLIB@)
+    find_dependency(fmt)
+endif()
+
 @FILENAME_DEP_REPLACE@
 include(${SELF_DIR}/wpiutil.cmake)
diff --git a/third_party/allwpilib/xrpVendordep/CMakeLists.txt b/third_party/allwpilib/xrpVendordep/CMakeLists.txt
new file mode 100644
index 0000000..40d40a7
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/CMakeLists.txt
@@ -0,0 +1,71 @@
+project(xrpVendordep)
+
+include(SubDirList)
+include(CompileWarnings)
+include(AddTest)
+
+if (WITH_JAVA)
+  find_package(Java REQUIRED)
+  include(UseJava)
+  set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
+
+  file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
+  add_jar(xrpVendordep_jar ${JAVA_SOURCES} INCLUDE_JARS hal_jar ntcore_jar cscore_jar cameraserver_jar wpimath_jar wpiutil_jar wpilibj_jar OUTPUT_NAME xrpVendordep)
+
+  get_property(xrpVendordep_JAR_FILE TARGET xrpVendordep_jar PROPERTY JAR_FILE)
+  install(FILES ${xrpVendordep_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+  set_property(TARGET xrpVendordep_jar PROPERTY FOLDER "java")
+
+  if (WITH_FLAT_INSTALL)
+      set (xrpVendordep_config_dir ${wpilib_dest})
+  else()
+      set (xrpVendordep_config_dir share/xrpVendordep)
+  endif()
+endif()
+
+if (WITH_JAVA_SOURCE)
+  find_package(Java REQUIRED)
+  include(UseJava)
+  file(GLOB XRPVENDORDEP_SOURCES src/main/java/edu/wpi/first/wpilibj/xrp/*.java)
+  add_jar(xrpVendordep_src_jar
+  RESOURCES NAMESPACE "edu/wpi/first/wpilibj/xrp" ${XRPVENDORDEP_SOURCES}
+  OUTPUT_NAME xrpVendordep-sources)
+
+  get_property(xrpVendordep_src_JAR_FILE TARGET xrpVendordep_src_jar PROPERTY JAR_FILE)
+  install(FILES ${xrpVendordep_src_JAR_FILE} DESTINATION "${java_lib_dest}")
+
+  set_property(TARGET xrpVendordep_src_jar PROPERTY FOLDER "java")
+endif()
+
+file(GLOB_RECURSE xrpVendordep_native_src src/main/native/cpp/*.cpp)
+add_library(xrpVendordep ${xrpVendordep_native_src})
+set_target_properties(xrpVendordep PROPERTIES DEBUG_POSTFIX "d")
+set_property(TARGET xrpVendordep PROPERTY FOLDER "libraries")
+
+target_compile_features(xrpVendordep PUBLIC cxx_std_20)
+wpilib_target_warnings(xrpVendordep)
+target_link_libraries(xrpVendordep wpilibc)
+
+target_include_directories(xrpVendordep PUBLIC
+                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
+                            $<INSTALL_INTERFACE:${include_dest}/xrpVendordep>)
+
+install(TARGETS xrpVendordep EXPORT xrpVendordep DESTINATION "${main_lib_dest}")
+install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/xrpVendordep")
+
+if (FLAT_INSTALL_WPILIB)
+     set(xrpVendordep_config_dir ${wpilib_dest})
+ else()
+     set(xrpVendordep_config_dir share/xrpVendordep)
+ endif()
+
+ configure_file(xrpVendordep-config.cmake.in ${WPILIB_BINARY_DIR}/xrpVendordep-config.cmake)
+ install(FILES ${WPILIB_BINARY_DIR}/xrpVendordep-config.cmake DESTINATION ${xrpVendordep_config_dir})
+ install(EXPORT xrpVendordep DESTINATION ${xrpVendordep_config_dir})
+
+ if (WITH_TESTS)
+     wpilib_add_test(xrpVendordep src/test/native/cpp)
+     target_include_directories(xrpVendordep_test PRIVATE src/test/native/include)
+     target_link_libraries(xrpVendordep_test xrpVendordep gmock_main)
+ endif()
diff --git a/third_party/allwpilib/xrpVendordep/README.md b/third_party/allwpilib/xrpVendordep/README.md
new file mode 100644
index 0000000..0b3688c
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/README.md
@@ -0,0 +1,2 @@
+# XRP-vendordep
+WPILib XRP Vendordep
diff --git a/third_party/allwpilib/xrpVendordep/XRPVendordep.json b/third_party/allwpilib/xrpVendordep/XRPVendordep.json
new file mode 100644
index 0000000..3c7f1a1
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/XRPVendordep.json
@@ -0,0 +1,37 @@
+{
+  "fileName": "XRPVendordep.json",
+  "name": "XRP-Vendordep",
+  "version": "1.0.0",
+  "uuid": "1571a1a5-ed3f-4f07-b7eb-b2beb17394e0",
+  "frcYear": "2024",
+  "mavenUrls": [],
+  "jsonUrl": "",
+  "javaDependencies": [
+    {
+      "groupId": "edu.wpi.first.xrpVendordep",
+      "artifactId": "xrpVendordep-java",
+      "version": "wpilib"
+    }
+  ],
+  "jniDependencies": [],
+  "cppDependencies": [
+    {
+      "groupId": "edu.wpi.first.xrpVendordep",
+      "artifactId": "xrpVendordep-cpp",
+      "version": "wpilib",
+      "libName": "xrpVendordep",
+      "headerClassifier": "headers",
+      "sourcesClassifier": "sources",
+      "sharedLibrary": true,
+      "skipInvalidPlatforms": true,
+      "binaryPlatforms": [
+        "linuxarm32",
+        "linuxarm64",
+        "windowsx86-64",
+        "windowsx86",
+        "linuxx86-64",
+        "osxuniversal"
+      ]
+    }
+  ]
+}
diff --git a/third_party/allwpilib/xrpVendordep/build.gradle b/third_party/allwpilib/xrpVendordep/build.gradle
new file mode 100644
index 0000000..3ee9d0a
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/build.gradle
@@ -0,0 +1,98 @@
+ext {
+    nativeName = 'xrpVendordep'
+    devMain = 'edu.wpi.first.wpilibj.xrp.DevMain'
+}
+
+evaluationDependsOn(":ntcore")
+evaluationDependsOn(":cscore")
+evaluationDependsOn(":hal")
+evaluationDependsOn(":wpimath")
+evaluationDependsOn(":wpilibc")
+evaluationDependsOn(":cameraserver")
+evaluationDependsOn(":wpilibj")
+
+apply from: "${rootDir}/shared/javacpp/setupBuild.gradle"
+
+dependencies {
+    implementation project(":wpiutil")
+    implementation project(":wpinet")
+    implementation project(":ntcore")
+    implementation project(":cscore")
+    implementation project(":hal")
+    implementation project(":wpimath")
+    implementation project(":wpilibj")
+}
+
+nativeUtils.exportsConfigs {
+    // Main library is just default empty. This will export everything
+    xrpVendordep {
+        x64ExcludeSymbols = [
+            '_CT??_R0?AV_System_error',
+            '_CT??_R0?AVexception',
+            '_CT??_R0?AVfailure',
+            '_CT??_R0?AVruntime_error',
+            '_CT??_R0?AVsystem_error',
+            '_CTA5?AVfailure',
+            '_TI5?AVfailure',
+            '_CT??_R0?AVout_of_range',
+            '_CTA3?AVout_of_range',
+            '_TI3?AVout_of_range',
+            '_CT??_R0?AVbad_cast'
+        ]
+    }
+}
+
+model {
+    components {}
+    binaries {
+        all {
+            if (!it.buildable || !(it instanceof NativeBinarySpec)) {
+                return
+            }
+            lib project: ":wpilibc", library: "wpilibc", linkage: "shared"
+            project(":ntcore").addNtcoreDependency(it, "shared")
+            project(":hal").addHalDependency(it, "shared")
+            lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+            lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
+            lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
+
+            if (it.component.name == "${nativeName}Dev") {
+                project(":ntcore").addNtcoreJniDependency(it)
+                lib project: ":wpinet", library: "wpinetJNIShared", linkage: "shared"
+                lib project: ":wpiutil", library: "wpiutilJNIShared", linkage: "shared"
+                lib project: ":wpimath", library: "wpimathJNIShared", linkage: "shared"
+                project(":hal").addHalJniDependency(it)
+            }
+
+            if (it instanceof GoogleTestTestSuiteBinarySpec) {
+                nativeUtils.useRequiredLibrary(it, "opencv_shared")
+                lib project: ":cscore", library: "cscore", linkage: "shared"
+            }
+        }
+    }
+    tasks {
+        def c = $.components
+        def found = false
+        def systemArch = getCurrentArch()
+        c.each {
+            if (it in NativeExecutableSpec && it.name == "${nativeName}Dev") {
+                it.binaries.each {
+                    if (!found) {
+                        def arch = it.targetPlatform.name
+                        if (arch == systemArch) {
+                            def filePath = it.tasks.install.installDirectory.get().toString() + File.separatorChar + "lib"
+                            found = true
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+test {
+    testLogging {
+        outputs.upToDateWhen {false}
+        showStandardStreams = true
+    }
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/dev/java/edu/wpi/first/wpilibj/xrp/DevMain.java b/third_party/allwpilib/xrpVendordep/src/dev/java/edu/wpi/first/wpilibj/xrp/DevMain.java
new file mode 100644
index 0000000..9b981c5
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/dev/java/edu/wpi/first/wpilibj/xrp/DevMain.java
@@ -0,0 +1,21 @@
+// 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.
+
+package edu.wpi.first.wpilibj.xrp;
+
+import edu.wpi.first.hal.HALUtil;
+import edu.wpi.first.networktables.NetworkTablesJNI;
+import edu.wpi.first.util.RuntimeDetector;
+
+public final class DevMain {
+  /** Main entry point. */
+  public static void main(String[] args) {
+    System.out.println("Hello World!");
+    System.out.println(RuntimeDetector.getPlatformPath());
+    System.out.println(NetworkTablesJNI.now());
+    System.out.println(HALUtil.getHALRuntimeType());
+  }
+
+  private DevMain() {}
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/dev/native/cpp/main.cpp b/third_party/allwpilib/xrpVendordep/src/dev/native/cpp/main.cpp
new file mode 100644
index 0000000..a3e363e
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/dev/native/cpp/main.cpp
@@ -0,0 +1,5 @@
+// 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.
+
+int main() {}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPGyro.java b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPGyro.java
new file mode 100644
index 0000000..16d70d4
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPGyro.java
@@ -0,0 +1,147 @@
+// 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.
+
+package edu.wpi.first.wpilibj.xrp;
+
+import edu.wpi.first.hal.SimDevice;
+import edu.wpi.first.hal.SimDevice.Direction;
+import edu.wpi.first.hal.SimDouble;
+
+public class XRPGyro {
+  private final SimDevice m_simDevice;
+  private final SimDouble m_simRateX;
+  private final SimDouble m_simRateY;
+  private final SimDouble m_simRateZ;
+  private final SimDouble m_simAngleX;
+  private final SimDouble m_simAngleY;
+  private final SimDouble m_simAngleZ;
+
+  private double m_angleXOffset;
+  private double m_angleYOffset;
+  private double m_angleZOffset;
+
+  /** Create a new XRPGyro. */
+  public XRPGyro() {
+    m_simDevice = SimDevice.create("Gyro:XRPGyro");
+    if (m_simDevice != null) {
+      m_simDevice.createBoolean("init", Direction.kOutput, true);
+      m_simRateX = m_simDevice.createDouble("rate_x", Direction.kInput, 0.0);
+      m_simRateY = m_simDevice.createDouble("rate_y", Direction.kInput, 0.0);
+      m_simRateZ = m_simDevice.createDouble("rate_z", Direction.kInput, 0.0);
+
+      m_simAngleX = m_simDevice.createDouble("angle_x", Direction.kInput, 0.0);
+      m_simAngleY = m_simDevice.createDouble("angle_y", Direction.kInput, 0.0);
+      m_simAngleZ = m_simDevice.createDouble("angle_z", Direction.kInput, 0.0);
+    } else {
+      m_simRateX = null;
+      m_simRateY = null;
+      m_simRateZ = null;
+      m_simAngleX = null;
+      m_simAngleY = null;
+      m_simAngleZ = null;
+    }
+  }
+
+  /**
+   * Get the rate of turn in degrees-per-second around the X-axis.
+   *
+   * @return rate of turn in degrees-per-second
+   */
+  public double getRateX() {
+    if (m_simRateX != null) {
+      return m_simRateX.get();
+    }
+
+    return 0.0;
+  }
+
+  /**
+   * Get the rate of turn in degrees-per-second around the Y-axis.
+   *
+   * @return rate of turn in degrees-per-second
+   */
+  public double getRateY() {
+    if (m_simRateY != null) {
+      return m_simRateY.get();
+    }
+
+    return 0.0;
+  }
+
+  /**
+   * Get the rate of turn in degrees-per-second around the Z-axis.
+   *
+   * @return rate of turn in degrees-per-second
+   */
+  public double getRateZ() {
+    if (m_simRateZ != null) {
+      return m_simRateZ.get();
+    }
+
+    return 0.0;
+  }
+
+  /**
+   * Get the currently reported angle around the X-axis.
+   *
+   * @return current angle around X-axis in degrees
+   */
+  public double getAngleX() {
+    if (m_simAngleX != null) {
+      return m_simAngleX.get() - m_angleXOffset;
+    }
+
+    return 0.0;
+  }
+
+  /**
+   * Get the currently reported angle around the X-axis.
+   *
+   * @return current angle around Y-axis in degrees
+   */
+  public double getAngleY() {
+    if (m_simAngleY != null) {
+      return m_simAngleY.get() - m_angleYOffset;
+    }
+
+    return 0.0;
+  }
+
+  /**
+   * Get the currently reported angle around the Z-axis.
+   *
+   * @return current angle around Z-axis in degrees
+   */
+  public double getAngleZ() {
+    if (m_simAngleZ != null) {
+      return m_simAngleZ.get() - m_angleZOffset;
+    }
+
+    return 0.0;
+  }
+
+  /** Reset the gyro angles to 0. */
+  public void reset() {
+    if (m_simAngleX != null) {
+      m_angleXOffset = m_simAngleX.get();
+      m_angleYOffset = m_simAngleY.get();
+      m_angleZOffset = m_simAngleZ.get();
+    }
+  }
+
+  public double getAngle() {
+    return getAngleZ();
+  }
+
+  public double getRate() {
+    return getRateZ();
+  }
+
+  /** Close out the SimDevice. */
+  public void close() {
+    if (m_simDevice != null) {
+      m_simDevice.close();
+    }
+  }
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPMotor.java b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPMotor.java
new file mode 100644
index 0000000..957f9e7
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPMotor.java
@@ -0,0 +1,109 @@
+// 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.
+
+package edu.wpi.first.wpilibj.xrp;
+
+import edu.wpi.first.hal.SimBoolean;
+import edu.wpi.first.hal.SimDevice;
+import edu.wpi.first.hal.SimDevice.Direction;
+import edu.wpi.first.hal.SimDouble;
+import edu.wpi.first.wpilibj.motorcontrol.MotorController;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * XRPMotor.
+ *
+ * <p>A SimDevice based motor controller representing the motors on an XRP robot
+ */
+public class XRPMotor implements MotorController {
+  private static HashMap<Integer, String> s_simDeviceNameMap = new HashMap<>();
+  private static HashSet<Integer> s_registeredDevices = new HashSet<>();
+
+  private static void checkDeviceAllocation(int deviceNum) {
+    if (!s_simDeviceNameMap.containsKey(deviceNum)) {
+      throw new IllegalArgumentException("Invalid XRPMotor device number. Should be 0-3");
+    }
+
+    if (s_registeredDevices.contains(deviceNum)) {
+      throw new IllegalArgumentException("XRPMotor " + deviceNum + " already allocated");
+    }
+
+    s_registeredDevices.add(deviceNum);
+  }
+
+  static {
+    s_simDeviceNameMap.put(0, "motorL");
+    s_simDeviceNameMap.put(1, "motorR");
+    s_simDeviceNameMap.put(2, "motor3");
+    s_simDeviceNameMap.put(3, "motor4");
+  }
+
+  private final SimDouble m_simSpeed;
+  private final SimBoolean m_simInverted;
+
+  /** XRPMotor. */
+  public XRPMotor(int deviceNum) {
+    checkDeviceAllocation(deviceNum);
+
+    // We want this to appear on the WS messages as type: "XRPMotor", device: <motor name>
+    String simDeviceName = "XRPMotor:" + s_simDeviceNameMap.get(deviceNum);
+    SimDevice xrpMotorSimDevice = SimDevice.create(simDeviceName);
+
+    if (xrpMotorSimDevice != null) {
+      xrpMotorSimDevice.createBoolean("init", Direction.kOutput, true);
+      m_simInverted = xrpMotorSimDevice.createBoolean("inverted", Direction.kInput, false);
+      m_simSpeed = xrpMotorSimDevice.createDouble("speed", Direction.kOutput, 0.0);
+    } else {
+      m_simInverted = null;
+      m_simSpeed = null;
+    }
+  }
+
+  @Override
+  public void set(double speed) {
+    if (m_simSpeed != null) {
+      boolean invert = false;
+      if (m_simInverted != null) {
+        invert = m_simInverted.get();
+      }
+
+      m_simSpeed.set(invert ? -speed : speed);
+    }
+  }
+
+  @Override
+  public double get() {
+    if (m_simSpeed != null) {
+      return m_simSpeed.get();
+    }
+
+    return 0.0;
+  }
+
+  @Override
+  public void setInverted(boolean isInverted) {
+    if (m_simInverted != null) {
+      m_simInverted.set(isInverted);
+    }
+  }
+
+  @Override
+  public boolean getInverted() {
+    if (m_simInverted != null) {
+      return m_simInverted.get();
+    }
+    return false;
+  }
+
+  @Override
+  public void disable() {
+    set(0.0);
+  }
+
+  @Override
+  public void stopMotor() {
+    set(0.0);
+  }
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPOnBoardIO.java b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPOnBoardIO.java
new file mode 100644
index 0000000..a37d417
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPOnBoardIO.java
@@ -0,0 +1,41 @@
+// 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.
+
+package edu.wpi.first.wpilibj.xrp;
+
+import edu.wpi.first.wpilibj.DigitalInput;
+import edu.wpi.first.wpilibj.DigitalOutput;
+
+/**
+ * This class represents the onboard IO of the XRP Reference Robot. This includes the USER
+ * pushbutton and LED
+ */
+public class XRPOnBoardIO {
+  private final DigitalInput m_button = new DigitalInput(0);
+  private final DigitalOutput m_led = new DigitalOutput(1);
+
+  /** Constructor. */
+  public XRPOnBoardIO() {
+    // No need to do anything else. Unlike the Romi, there are no other configurable
+    // I/O ports
+  }
+
+  /**
+   * Gets if the USER button is pressed.
+   *
+   * @return True if the USER button is currently pressed.
+   */
+  public boolean getUserButtonPressed() {
+    return m_button.get();
+  }
+
+  /**
+   * Sets the onboard LED.
+   *
+   * @param value True to activate LED, false otherwise.
+   */
+  public void setLed(boolean value) {
+    m_led.set(value);
+  }
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPRangefinder.java b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPRangefinder.java
new file mode 100644
index 0000000..ac2d72c
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPRangefinder.java
@@ -0,0 +1,31 @@
+// 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.
+
+package edu.wpi.first.wpilibj.xrp;
+
+import edu.wpi.first.math.util.Units;
+import edu.wpi.first.wpilibj.AnalogInput;
+
+/** This class represents the ultrasonic rangefinder on an XRP robot. */
+public class XRPRangefinder {
+  private final AnalogInput m_rangefinder = new AnalogInput(2);
+
+  /**
+   * Get the measured distance in meters. Distance further than 4m will be reported as 4m.
+   *
+   * @return distance in meters
+   */
+  public double getDistanceMeters() {
+    return (m_rangefinder.getVoltage() / 5.0) * 4.0;
+  }
+
+  /**
+   * Get the measured distance in inches.
+   *
+   * @return distance in inches
+   */
+  public double getDistanceInches() {
+    return Units.metersToInches(getDistanceMeters());
+  }
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPReflectanceSensor.java b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPReflectanceSensor.java
new file mode 100644
index 0000000..ebd5368
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPReflectanceSensor.java
@@ -0,0 +1,31 @@
+// 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.
+
+package edu.wpi.first.wpilibj.xrp;
+
+import edu.wpi.first.wpilibj.AnalogInput;
+
+/** This class represents the reflectance sensor pair on an XRP robot. */
+public class XRPReflectanceSensor {
+  private final AnalogInput m_leftSensor = new AnalogInput(0);
+  private final AnalogInput m_rightSensor = new AnalogInput(1);
+
+  /**
+   * Returns the reflectance value of the left sensor.
+   *
+   * @return value between 0.0 (white) and 1.0 (black).
+   */
+  public double getLeftReflectanceValue() {
+    return m_leftSensor.getVoltage() / 5.0;
+  }
+
+  /**
+   * Returns the reflectance value of the right sensor.
+   *
+   * @return value between 0.0 (white) and 1.0 (black).
+   */
+  public double getRightReflectanceValue() {
+    return m_rightSensor.getVoltage() / 5.0;
+  }
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPServo.java b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPServo.java
new file mode 100644
index 0000000..ac7231f
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPServo.java
@@ -0,0 +1,123 @@
+// 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.
+
+package edu.wpi.first.wpilibj.xrp;
+
+import edu.wpi.first.hal.SimDevice;
+import edu.wpi.first.hal.SimDevice.Direction;
+import edu.wpi.first.hal.SimDouble;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * XRPServo.
+ *
+ * <p>A SimDevice based servo
+ */
+public class XRPServo {
+  private static HashMap<Integer, String> s_simDeviceNameMap = new HashMap<>();
+  private static HashSet<Integer> s_registeredDevices = new HashSet<>();
+
+  private static void checkDeviceAllocation(int deviceNum) {
+    if (!s_simDeviceNameMap.containsKey(deviceNum)) {
+      throw new IllegalArgumentException("Invalid XRPServo device number. Should be 4-5");
+    }
+
+    if (s_registeredDevices.contains(deviceNum)) {
+      throw new IllegalArgumentException("XRPServo " + deviceNum + " already allocated");
+    }
+
+    s_registeredDevices.add(deviceNum);
+  }
+
+  static {
+    s_simDeviceNameMap.put(4, "servo1");
+    s_simDeviceNameMap.put(5, "servo2");
+  }
+
+  private final SimDouble m_simPosition;
+
+  /** XRPServo. */
+  public XRPServo(int deviceNum) {
+    checkDeviceAllocation(deviceNum);
+
+    // We want this to appear on WS as type: "XRPServo", device: <servo name>
+    String simDeviceName = "XRPServo:" + s_simDeviceNameMap.get(deviceNum);
+    SimDevice xrpServoSimDevice = SimDevice.create(simDeviceName);
+
+    if (xrpServoSimDevice != null) {
+      xrpServoSimDevice.createBoolean("init", Direction.kOutput, true);
+      // This should mimic PWM position [0.0, 1.0]
+      m_simPosition = xrpServoSimDevice.createDouble("position", Direction.kOutput, 0.5);
+    } else {
+      m_simPosition = null;
+    }
+  }
+
+  /**
+   * Set the servo angle.
+   *
+   * @param angle Desired angle in degrees
+   */
+  public void setAngle(double angle) {
+    if (angle < 0.0) {
+      angle = 0.0;
+    }
+
+    if (angle > 180.0) {
+      angle = 180.0;
+    }
+
+    double pos = angle / 180.0;
+
+    if (m_simPosition != null) {
+      m_simPosition.set(pos);
+    }
+  }
+
+  /**
+   * Get the servo angle.
+   *
+   * @return Current servo angle
+   */
+  public double getAngle() {
+    if (m_simPosition != null) {
+      return m_simPosition.get() * 180.0;
+    }
+
+    return 90.0;
+  }
+
+  /**
+   * Set the servo position.
+   *
+   * @param pos Desired position (Between 0.0 and 1.0)
+   */
+  public void setPosition(double pos) {
+    if (pos < 0.0) {
+      pos = 0.0;
+    }
+
+    if (pos > 1.0) {
+      pos = 1.0;
+    }
+
+    if (m_simPosition != null) {
+      m_simPosition.set(pos);
+    }
+  }
+
+  /**
+   * Get the servo position.
+   *
+   * @return Current servo position
+   */
+  public double getPosition() {
+    if (m_simPosition != null) {
+      return m_simPosition.get();
+    }
+
+    return 0.5;
+  }
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPGyro.cpp b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPGyro.cpp
new file mode 100644
index 0000000..46bf618
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPGyro.cpp
@@ -0,0 +1,89 @@
+// 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 "frc/xrp/XRPGyro.h"
+
+using namespace frc;
+
+XRPGyro::XRPGyro() : m_simDevice("Gyro:XRPGyro") {
+  if (m_simDevice) {
+    m_simDevice.CreateBoolean("init", hal::SimDevice::kOutput, true);
+    m_simRateX =
+        m_simDevice.CreateDouble("rate_x", hal::SimDevice::kInput, 0.0);
+    m_simRateY =
+        m_simDevice.CreateDouble("rate_y", hal::SimDevice::kInput, 0.0);
+    m_simRateZ =
+        m_simDevice.CreateDouble("rate_z", hal::SimDevice::kInput, 0.0);
+    m_simAngleX =
+        m_simDevice.CreateDouble("angle_x", hal::SimDevice::kInput, 0.0);
+    m_simAngleY =
+        m_simDevice.CreateDouble("angle_y", hal::SimDevice::kInput, 0.0);
+    m_simAngleZ =
+        m_simDevice.CreateDouble("angle_z", hal::SimDevice::kInput, 0.0);
+  }
+}
+
+double XRPGyro::GetAngle() const {
+  return GetAngleZ();
+}
+
+double XRPGyro::GetRate() const {
+  return GetRateZ();
+}
+
+double XRPGyro::GetRateX() const {
+  if (m_simRateX) {
+    return m_simRateX.Get();
+  }
+
+  return 0.0;
+}
+
+double XRPGyro::GetRateY() const {
+  if (m_simRateY) {
+    return m_simRateY.Get();
+  }
+
+  return 0.0;
+}
+
+double XRPGyro::GetRateZ() const {
+  if (m_simRateZ) {
+    return m_simRateZ.Get();
+  }
+
+  return 0.0;
+}
+
+double XRPGyro::GetAngleX() const {
+  if (m_simAngleX) {
+    return m_simAngleX.Get() - m_angleXOffset;
+  }
+
+  return 0.0;
+}
+
+double XRPGyro::GetAngleY() const {
+  if (m_simAngleY) {
+    return m_simAngleY.Get() - m_angleYOffset;
+  }
+
+  return 0.0;
+}
+
+double XRPGyro::GetAngleZ() const {
+  if (m_simAngleZ) {
+    return m_simAngleZ.Get() - m_angleZOffset;
+  }
+
+  return 0.0;
+}
+
+void XRPGyro::Reset() {
+  if (m_simAngleX) {
+    m_angleXOffset = m_simAngleX.Get();
+    m_angleYOffset = m_simAngleY.Get();
+    m_angleZOffset = m_simAngleZ.Get();
+  }
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPMotor.cpp b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPMotor.cpp
new file mode 100644
index 0000000..02d1d58
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPMotor.cpp
@@ -0,0 +1,88 @@
+// 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 "frc/xrp/XRPMotor.h"
+
+#include <frc/Errors.h>
+
+using namespace frc;
+
+std::map<int, std::string> XRPMotor::s_simDeviceMap = {
+    {0, "motorL"}, {1, "motorR"}, {2, "motor3"}, {3, "motor4"}};
+
+std::set<int> XRPMotor::s_registeredDevices = {};
+
+void XRPMotor::CheckDeviceAllocation(int deviceNum) {
+  if (s_simDeviceMap.count(deviceNum) == 0) {
+    throw FRC_MakeError(frc::err::ChannelIndexOutOfRange, "Channel {}",
+                        deviceNum);
+  }
+
+  if (s_registeredDevices.count(deviceNum) > 0) {
+    throw FRC_MakeError(frc::err::ResourceAlreadyAllocated, "Channel {}",
+                        deviceNum);
+  }
+
+  s_registeredDevices.insert(deviceNum);
+}
+
+XRPMotor::XRPMotor(int deviceNum) {
+  CheckDeviceAllocation(deviceNum);
+
+  m_deviceName = "XRPMotor:" + s_simDeviceMap[deviceNum];
+  m_simDevice = hal::SimDevice(m_deviceName.c_str());
+
+  if (m_simDevice) {
+    m_simDevice.CreateBoolean("init", hal::SimDevice::kOutput, true);
+    m_simInverted =
+        m_simDevice.CreateBoolean("inverted", hal::SimDevice::kInput, false);
+    m_simSpeed =
+        m_simDevice.CreateDouble("speed", hal::SimDevice::kOutput, 0.0);
+  }
+}
+
+void XRPMotor::Set(double speed) {
+  if (m_simSpeed) {
+    bool invert = false;
+    if (m_simInverted) {
+      invert = m_simInverted.Get();
+    }
+
+    m_simSpeed.Set(invert ? -speed : speed);
+  }
+}
+
+double XRPMotor::Get() const {
+  if (m_simSpeed) {
+    return m_simSpeed.Get();
+  }
+
+  return 0.0;
+}
+
+void XRPMotor::SetInverted(bool isInverted) {
+  if (m_simInverted) {
+    m_simInverted.Set(isInverted);
+  }
+}
+
+bool XRPMotor::GetInverted() const {
+  if (m_simInverted) {
+    return m_simInverted.Get();
+  }
+
+  return false;
+}
+
+void XRPMotor::Disable() {
+  Set(0.0);
+}
+
+void XRPMotor::StopMotor() {
+  Set(0.0);
+}
+
+std::string XRPMotor::GetDescription() const {
+  return m_deviceName;
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPOnBoardIO.cpp b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPOnBoardIO.cpp
new file mode 100644
index 0000000..eb1ab61
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPOnBoardIO.cpp
@@ -0,0 +1,20 @@
+// 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 "frc/xrp/XRPOnBoardIO.h"
+
+#include <frc/DigitalInput.h>
+#include <frc/DigitalOutput.h>
+#include <frc/Errors.h>
+#include <frc/Timer.h>
+
+using namespace frc;
+
+bool XRPOnBoardIO::GetUserButtonPressed() {
+  return m_userButton.Get();
+}
+
+void XRPOnBoardIO::SetLed(bool value) {
+  m_led.Set(value);
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPRangefinder.cpp b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPRangefinder.cpp
new file mode 100644
index 0000000..c1222bd
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPRangefinder.cpp
@@ -0,0 +1,11 @@
+// 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 "frc/xrp/XRPRangefinder.h"
+
+using namespace frc;
+
+units::meter_t XRPRangefinder::GetDistance() const {
+  return units::meter_t{m_rangefinder.GetVoltage() / 5.0 * 4.0};
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPReflectanceSensor.cpp b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPReflectanceSensor.cpp
new file mode 100644
index 0000000..5f81774
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPReflectanceSensor.cpp
@@ -0,0 +1,15 @@
+// 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 "frc/xrp/XRPReflectanceSensor.h"
+
+using namespace frc;
+
+double XRPReflectanceSensor::GetLeftReflectanceValue() const {
+  return m_leftSensor.GetVoltage() / 5.0;
+}
+
+double XRPReflectanceSensor::GetRightReflectanceValue() const {
+  return m_rightSensor.GetVoltage() / 5.0;
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPServo.cpp b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPServo.cpp
new file mode 100644
index 0000000..01635cd
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/cpp/xrp/XRPServo.cpp
@@ -0,0 +1,87 @@
+// 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 "frc/xrp/XRPServo.h"
+
+#include <frc/Errors.h>
+
+using namespace frc;
+
+std::map<int, std::string> XRPServo::s_simDeviceMap = {{4, "servo1"},
+                                                       {5, "servo2"}};
+
+std::set<int> XRPServo::s_registeredDevices = {};
+
+void XRPServo::CheckDeviceAllocation(int deviceNum) {
+  if (s_simDeviceMap.count(deviceNum) == 0) {
+    throw FRC_MakeError(frc::err::ChannelIndexOutOfRange, "Channel {}",
+                        deviceNum);
+  }
+
+  if (s_registeredDevices.count(deviceNum) > 0) {
+    throw FRC_MakeError(frc::err::ResourceAlreadyAllocated, "Channel {}",
+                        deviceNum);
+  }
+
+  s_registeredDevices.insert(deviceNum);
+}
+
+XRPServo::XRPServo(int deviceNum) {
+  CheckDeviceAllocation(deviceNum);
+
+  m_deviceName = "XRPServo:" + s_simDeviceMap[deviceNum];
+  m_simDevice = hal::SimDevice(m_deviceName.c_str());
+
+  if (m_simDevice) {
+    m_simDevice.CreateBoolean("init", hal::SimDevice::kOutput, true);
+    m_simPosition =
+        m_simDevice.CreateDouble("position", hal::SimDevice::kOutput, 0.5);
+  }
+}
+
+void XRPServo::SetAngle(double angleDegrees) {
+  if (angleDegrees < 0.0) {
+    angleDegrees = 0.0;
+  }
+
+  if (angleDegrees > 180.0) {
+    angleDegrees = 180.0;
+  }
+
+  double pos = (angleDegrees / 180.0);
+
+  if (m_simPosition) {
+    m_simPosition.Set(pos);
+  }
+}
+
+double XRPServo::GetAngle() const {
+  if (m_simPosition) {
+    return m_simPosition.Get() * 180.0;
+  }
+
+  return 90.0;
+}
+
+void XRPServo::SetPosition(double pos) {
+  if (pos < 0.0) {
+    pos = 0.0;
+  }
+
+  if (pos > 1.0) {
+    pos = 1.0;
+  }
+
+  if (m_simPosition) {
+    m_simPosition.Set(pos);
+  }
+}
+
+double XRPServo::GetPosition() const {
+  if (m_simPosition) {
+    return m_simPosition.Get();
+  }
+
+  return 0.5;
+}
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPGyro.h b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPGyro.h
new file mode 100644
index 0000000..81b1610
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPGyro.h
@@ -0,0 +1,91 @@
+// 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 <hal/SimDevice.h>
+
+namespace frc {
+
+/**
+ * Use a rate gyro to return the robots heading relative to a starting position.
+ *
+ * This class is for the XRP onboard gyro, and will only work in
+ * simulation/XRP mode. Only one instance of a XRPGyro is supported.
+ */
+class XRPGyro {
+ public:
+  XRPGyro();
+
+  /**
+   * Return the actual angle in degrees that the robot is currently facing.
+   *
+   * The angle is based on integration of the returned rate form the gyro.
+   * The angle is continuous, that is, it will continue from 360->361 degrees.
+   * This allows algorithms that wouldn't want to see a discontinuity in the
+   * gyro output as it sweeps from 360 to 0 on the second time around.
+   *
+   * @return the current heading of the robot in degrees.
+   */
+  double GetAngle() const;
+
+  /**
+   * Return the rate of rotation of the gyro
+   *
+   * The rate is based on the most recent reading of the gyro.
+   *
+   * @return the current rate in degrees per second
+   */
+  double GetRate() const;
+
+  /**
+   * Gets the rate of turn in degrees-per-second around the X-axis
+   */
+  double GetRateX() const;
+
+  /**
+   * Gets the rate of turn in degrees-per-second around the Y-axis
+   */
+  double GetRateY() const;
+
+  /**
+   * Gets the rate of turn in degrees-per-second around the Z-axis
+   */
+  double GetRateZ() const;
+
+  /**
+   * Gets the currently reported angle around the X-axis
+   */
+  double GetAngleX() const;
+
+  /**
+   * Gets the currently reported angle around the X-axis
+   */
+  double GetAngleY() const;
+
+  /**
+   * Gets the currently reported angle around the X-axis
+   */
+  double GetAngleZ() const;
+
+  /**
+   * Resets the gyro
+   */
+  void Reset();
+
+ private:
+  hal::SimDevice m_simDevice;
+  hal::SimDouble m_simRateX;
+  hal::SimDouble m_simRateY;
+  hal::SimDouble m_simRateZ;
+  hal::SimDouble m_simAngleX;
+  hal::SimDouble m_simAngleY;
+  hal::SimDouble m_simAngleZ;
+
+  double m_angleXOffset = 0;
+  double m_angleYOffset = 0;
+  double m_angleZOffset = 0;
+};
+
+}  // namespace frc
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPMotor.h b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPMotor.h
new file mode 100644
index 0000000..c174cbf
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPMotor.h
@@ -0,0 +1,46 @@
+// 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 <frc/MotorSafety.h>
+#include <frc/motorcontrol/MotorController.h>
+
+#include <map>
+#include <set>
+#include <string>
+
+#include <hal/SimDevice.h>
+
+namespace frc {
+
+class XRPMotor : public frc::MotorController, public frc::MotorSafety {
+ public:
+  explicit XRPMotor(int deviceNum);
+
+  void Set(double value) override;
+  double Get() const override;
+
+  void SetInverted(bool isInverted) override;
+  bool GetInverted() const override;
+
+  void Disable() override;
+
+  void StopMotor() override;
+  std::string GetDescription() const override;
+
+ private:
+  hal::SimDevice m_simDevice;
+  hal::SimDouble m_simSpeed;
+  hal::SimBoolean m_simInverted;
+
+  std::string m_deviceName;
+
+  static std::map<int, std::string> s_simDeviceMap;
+  static std::set<int> s_registeredDevices;
+
+  static void CheckDeviceAllocation(int deviceNum);
+};
+
+}  // namespace frc
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPOnBoardIO.h b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPOnBoardIO.h
new file mode 100644
index 0000000..4b97870
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPOnBoardIO.h
@@ -0,0 +1,46 @@
+// 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 <frc/DigitalInput.h>
+#include <frc/DigitalOutput.h>
+
+#include <memory>
+
+#include <units/time.h>
+
+namespace frc {
+
+/**
+ * This class represents the onboard IO of the XRP
+ * reference robot. This the USER push button and
+ * LED.
+ *
+ * <p>DIO 0 - USER Button (input only)
+ * DIO 1 - LED (output only)
+ */
+class XRPOnBoardIO {
+ public:
+  XRPOnBoardIO() {}  // No need to do anything. No configurable IO
+
+  static constexpr auto kMessageInterval = 1_s;
+  units::second_t m_nextMessageTime = 0_s;
+
+  /**
+   * Gets if the USER button is pressed.
+   */
+  bool GetUserButtonPressed();
+
+  /**
+   * Sets the yellow LED.
+   */
+  void SetLed(bool value);
+
+ private:
+  frc::DigitalInput m_userButton{0};
+  frc::DigitalOutput m_led{1};
+};
+
+}  // namespace frc
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPRangefinder.h b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPRangefinder.h
new file mode 100644
index 0000000..5be78e6
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPRangefinder.h
@@ -0,0 +1,29 @@
+// 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 <frc/AnalogInput.h>
+
+#include <units/length.h>
+
+namespace frc {
+
+/**
+ * This class represents the reflectance sensor pair
+ * on the XRP robot.
+ */
+class XRPRangefinder {
+ public:
+  /**
+   * Return the measured distance in meters. Distances further than 4 meters
+   * will be reported as 4 meters.
+   */
+  units::meter_t GetDistance() const;
+
+ private:
+  frc::AnalogInput m_rangefinder{2};
+};
+
+}  // namespace frc
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPReflectanceSensor.h b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPReflectanceSensor.h
new file mode 100644
index 0000000..8b85cd2
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPReflectanceSensor.h
@@ -0,0 +1,34 @@
+// 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 <frc/AnalogInput.h>
+
+namespace frc {
+
+/**
+ * This class represents the reflectance sensor pair
+ * on the XRP robot.
+ */
+class XRPReflectanceSensor {
+ public:
+  /**
+   * Return the reflectance value of the left sensor.
+   * Value ranges from 0.0 (white) to 1.0 (black)
+   */
+  double GetLeftReflectanceValue() const;
+
+  /**
+   * Return the reflectance value of the right sensor.
+   * Value ranges from 0.0 (white) to 1.0 (black)
+   */
+  double GetRightReflectanceValue() const;
+
+ private:
+  frc::AnalogInput m_leftSensor{0};
+  frc::AnalogInput m_rightSensor{1};
+};
+
+}  // namespace frc
diff --git a/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPServo.h b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPServo.h
new file mode 100644
index 0000000..f452a5f
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/main/native/include/frc/xrp/XRPServo.h
@@ -0,0 +1,37 @@
+// 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 <map>
+#include <set>
+#include <string>
+
+#include <hal/SimDevice.h>
+
+namespace frc {
+
+class XRPServo {
+ public:
+  explicit XRPServo(int deviceNum);
+
+  void SetAngle(double angleDegrees);
+  double GetAngle() const;
+
+  void SetPosition(double position);
+  double GetPosition() const;
+
+ private:
+  hal::SimDevice m_simDevice;
+  hal::SimDouble m_simPosition;
+
+  std::string m_deviceName;
+
+  static std::map<int, std::string> s_simDeviceMap;
+  static std::set<int> s_registeredDevices;
+
+  static void CheckDeviceAllocation(int deviceNum);
+};
+
+}  // namespace frc
diff --git a/third_party/allwpilib/xrpVendordep/src/test/native/cpp/main.cpp b/third_party/allwpilib/xrpVendordep/src/test/native/cpp/main.cpp
new file mode 100644
index 0000000..2d710be
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/src/test/native/cpp/main.cpp
@@ -0,0 +1,10 @@
+// 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 <gtest/gtest.h>

+

+int main(int argc, char** argv) {

+  ::testing::InitGoogleTest(&argc, argv);

+  return RUN_ALL_TESTS();

+}

diff --git a/third_party/allwpilib/xrpVendordep/xrpVendordep-config.cmake.in b/third_party/allwpilib/xrpVendordep/xrpVendordep-config.cmake.in
new file mode 100644
index 0000000..ad06e4c
--- /dev/null
+++ b/third_party/allwpilib/xrpVendordep/xrpVendordep-config.cmake.in
@@ -0,0 +1,11 @@
+include(CMakeFindDependencyMacro)
+ @WPIUTIL_DEP_REPLACE@
+ @NTCORE_DEP_REPLACE@
+ @CSCORE_DEP_REPLACE@
+ @CAMERASERVER_DEP_REPLACE@
+ @HAL_DEP_REPLACE@
+ @WPILIBC_DEP_REPLACE@
+ @WPIMATH_DEP_REPLACE@
+
+ @FILENAME_DEP_REPLACE@
+ include(${SELF_DIR}/xrpVendordep.cmake)